deprecated ospaths (#9665)
This commit is contained in:
parent
8507e506ce
commit
8d850f7a69
13 changed files with 838 additions and 811 deletions
|
|
@ -72,6 +72,10 @@ proc enumToString*(enums: openArray[enum]): string =
|
||||||
|
|
||||||
- Complex type is now generic and not a tuple anymore.
|
- Complex type is now generic and not a tuple anymore.
|
||||||
|
|
||||||
|
- The `ospaths` module is now deprecated, use `os` instead. Note that
|
||||||
|
`os` is available in a NimScript environment but unsupported
|
||||||
|
operations produce a compile-time error.
|
||||||
|
|
||||||
- The `parseopt` module now supports a new flag `allowWhitespaceAfterColon`
|
- The `parseopt` module now supports a new flag `allowWhitespaceAfterColon`
|
||||||
(default value: true) that can be set to `false` for better Posix
|
(default value: true) that can be set to `false` for better Posix
|
||||||
interoperability. (Bug #9619.)
|
interoperability. (Bug #9619.)
|
||||||
|
|
|
||||||
|
|
@ -85,6 +85,7 @@ proc initDefines*(symbols: StringTableRef) =
|
||||||
defineSymbol("nimHasUserErrors")
|
defineSymbol("nimHasUserErrors")
|
||||||
defineSymbol("nimUncheckedArrayTyp")
|
defineSymbol("nimUncheckedArrayTyp")
|
||||||
defineSymbol("nimHasTypeof")
|
defineSymbol("nimHasTypeof")
|
||||||
|
defineSymbol("nimErrorProcCanHaveBody")
|
||||||
|
|
||||||
defineSymbol("nimHasNilSeqs")
|
defineSymbol("nimHasNilSeqs")
|
||||||
for f in low(Feature)..high(Feature):
|
for f in low(Feature)..high(Feature):
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
|
|
||||||
import
|
import
|
||||||
intsets, ast, idents, algorithm, renderer, parser, ospaths, strutils,
|
intsets, ast, idents, algorithm, renderer, parser, os, strutils,
|
||||||
sequtils, msgs, modulegraphs, syntaxes, options, modulepaths, tables,
|
sequtils, msgs, modulegraphs, syntaxes, options, modulepaths, tables,
|
||||||
lineinfos
|
lineinfos
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1670,7 +1670,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
|
||||||
localError(c.config, n.info, "the overloaded " & s.name.s &
|
localError(c.config, n.info, "the overloaded " & s.name.s &
|
||||||
" operator has to be enabled with {.experimental: \"callOperator\".}")
|
" operator has to be enabled with {.experimental: \"callOperator\".}")
|
||||||
|
|
||||||
if n.sons[bodyPos].kind != nkEmpty:
|
if n.sons[bodyPos].kind != nkEmpty and sfError notin s.flags:
|
||||||
# for DLL generation it is annoying to check for sfImportc!
|
# for DLL generation it is annoying to check for sfImportc!
|
||||||
if sfBorrow in s.flags:
|
if sfBorrow in s.flags:
|
||||||
localError(c.config, n.sons[bodyPos].info, errImplOfXNotAllowed % s.name.s)
|
localError(c.config, n.sons[bodyPos].info, errImplOfXNotAllowed % s.name.s)
|
||||||
|
|
|
||||||
|
|
@ -112,9 +112,9 @@ proc registerAdditionalOps*(c: PCtx) =
|
||||||
registerCallback(c, "stdlib.math.mod", `mod Wrapper`)
|
registerCallback(c, "stdlib.math.mod", `mod Wrapper`)
|
||||||
|
|
||||||
when defined(nimcore):
|
when defined(nimcore):
|
||||||
wrap2s(getEnv, ospathsop)
|
wrap2s(getEnv, osop)
|
||||||
wrap1s(existsEnv, ospathsop)
|
wrap1s(existsEnv, osop)
|
||||||
wrap2svoid(putEnv, ospathsop)
|
wrap2svoid(putEnv, osop)
|
||||||
wrap1s(dirExists, osop)
|
wrap1s(dirExists, osop)
|
||||||
wrap1s(fileExists, osop)
|
wrap1s(fileExists, osop)
|
||||||
wrap2svoid(writeFile, systemop)
|
wrap2svoid(writeFile, systemop)
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
## Include file that implements 'getEnv' and friends. Do not import it!
|
## Include file that implements 'getEnv' and friends. Do not import it!
|
||||||
|
|
||||||
when not declared(ospaths):
|
when not declared(os):
|
||||||
{.error: "This is an include file for ospaths.nim!".}
|
{.error: "This is an include file for os.nim!".}
|
||||||
|
|
||||||
proc c_getenv(env: cstring): cstring {.
|
proc c_getenv(env: cstring): cstring {.
|
||||||
importc: "getenv", header: "<stdlib.h>".}
|
importc: "getenv", header: "<stdlib.h>".}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
## Include file that implements 'osErrorMsg' and friends. Do not import it!
|
## Include file that implements 'osErrorMsg' and friends. Do not import it!
|
||||||
|
|
||||||
when not declared(ospaths):
|
when not declared(os):
|
||||||
{.error: "This is an include file for ospaths.nim!".}
|
{.error: "This is an include file for os.nim!".}
|
||||||
|
|
||||||
when not defined(nimscript):
|
when not defined(nimscript):
|
||||||
var errno {.importc, header: "<errno.h>".}: cint
|
var errno {.importc, header: "<errno.h>".}: cint
|
||||||
|
|
|
||||||
875
lib/pure/os.nim
875
lib/pure/os.nim
File diff suppressed because it is too large
Load diff
|
|
@ -7,707 +7,15 @@
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
#
|
#
|
||||||
|
|
||||||
# Forwarded by the ``os`` module but a module in its own right for NimScript
|
## This module is deprecated, ``import os`` instead.
|
||||||
# support.
|
{.deprecated: "import os.nim instead".}
|
||||||
|
|
||||||
include "system/inclrtl"
|
import os
|
||||||
|
export ReadEnvEffect, WriteEnvEffect, ReadDirEffect, WriteDirEffect, OSErrorCode,
|
||||||
import strutils
|
doslikeFileSystem, CurDir, ParDir, DirSep, AltSep, PathSep, FileSystemCaseSensitive,
|
||||||
|
ExeExt, ScriptExt, DynlibFormat, ExtSep, joinPath, `/`, splitPath, parentDir,
|
||||||
type
|
tailDir, isRootDir, parentDirs, `/../`, searchExtPos, splitFile, extractFilename,
|
||||||
ReadEnvEffect* = object of ReadIOEffect ## effect that denotes a read
|
lastPathPart, changeFileExt, addFileExt, cmpPaths, isAbsolute, unixToNativePath,
|
||||||
## from an environment variable
|
`==`, `$`, osErrorMsg, raiseOSError, osLastError, getEnv, existsEnv, putEnv,
|
||||||
WriteEnvEffect* = object of WriteIOEffect ## effect that denotes a write
|
getHomeDir, getConfigDir, getTempDir, expandTilde, quoteShellWindows,
|
||||||
## to an environment variable
|
quoteShellPosix, quoteShell, quoteShellCommand
|
||||||
|
|
||||||
ReadDirEffect* = object of ReadIOEffect ## effect that denotes a read
|
|
||||||
## operation from the directory
|
|
||||||
## structure
|
|
||||||
WriteDirEffect* = object of WriteIOEffect ## effect that denotes a write
|
|
||||||
## operation to
|
|
||||||
## the directory structure
|
|
||||||
|
|
||||||
OSErrorCode* = distinct int32 ## Specifies an OS Error Code.
|
|
||||||
|
|
||||||
const
|
|
||||||
doslikeFileSystem* = defined(windows) or defined(OS2) or defined(DOS)
|
|
||||||
|
|
||||||
when defined(Nimdoc): # only for proper documentation:
|
|
||||||
const
|
|
||||||
CurDir* = '.'
|
|
||||||
## The constant string used by the operating system to refer to the
|
|
||||||
## current directory.
|
|
||||||
##
|
|
||||||
## For example: '.' for POSIX or ':' for the classic Macintosh.
|
|
||||||
|
|
||||||
ParDir* = ".."
|
|
||||||
## The constant string used by the operating system to refer to the
|
|
||||||
## parent directory.
|
|
||||||
##
|
|
||||||
## For example: ".." for POSIX or "::" for the classic Macintosh.
|
|
||||||
|
|
||||||
DirSep* = '/'
|
|
||||||
## The character used by the operating system to separate pathname
|
|
||||||
## components, for example, '/' for POSIX or ':' for the classic
|
|
||||||
## Macintosh.
|
|
||||||
|
|
||||||
AltSep* = '/'
|
|
||||||
## An alternative character used by the operating system to separate
|
|
||||||
## pathname components, or the same as `DirSep` if only one separator
|
|
||||||
## character exists. This is set to '/' on Windows systems
|
|
||||||
## where `DirSep` is a backslash.
|
|
||||||
|
|
||||||
PathSep* = ':'
|
|
||||||
## The character conventionally used by the operating system to separate
|
|
||||||
## search patch components (as in PATH), such as ':' for POSIX
|
|
||||||
## or ';' for Windows.
|
|
||||||
|
|
||||||
FileSystemCaseSensitive* = true
|
|
||||||
## true if the file system is case sensitive, false otherwise. Used by
|
|
||||||
## `cmpPaths` to compare filenames properly.
|
|
||||||
|
|
||||||
ExeExt* = ""
|
|
||||||
## The file extension of native executables. For example:
|
|
||||||
## "" for POSIX, "exe" on Windows.
|
|
||||||
|
|
||||||
ScriptExt* = ""
|
|
||||||
## The file extension of a script file. For example: "" for POSIX,
|
|
||||||
## "bat" on Windows.
|
|
||||||
|
|
||||||
DynlibFormat* = "lib$1.so"
|
|
||||||
## The format string to turn a filename into a `DLL`:idx: file (also
|
|
||||||
## called `shared object`:idx: on some operating systems).
|
|
||||||
|
|
||||||
elif defined(macos):
|
|
||||||
const
|
|
||||||
CurDir* = ':'
|
|
||||||
ParDir* = "::"
|
|
||||||
DirSep* = ':'
|
|
||||||
AltSep* = Dirsep
|
|
||||||
PathSep* = ','
|
|
||||||
FileSystemCaseSensitive* = false
|
|
||||||
ExeExt* = ""
|
|
||||||
ScriptExt* = ""
|
|
||||||
DynlibFormat* = "$1.dylib"
|
|
||||||
|
|
||||||
# MacOS paths
|
|
||||||
# ===========
|
|
||||||
# MacOS directory separator is a colon ":" which is the only character not
|
|
||||||
# allowed in filenames.
|
|
||||||
#
|
|
||||||
# A path containing no colon or which begins with a colon is a partial
|
|
||||||
# path.
|
|
||||||
# E.g. ":kalle:petter" ":kalle" "kalle"
|
|
||||||
#
|
|
||||||
# All other paths are full (absolute) paths. E.g. "HD:kalle:" "HD:"
|
|
||||||
# When generating paths, one is safe if one ensures that all partial paths
|
|
||||||
# begin with a colon, and all full paths end with a colon.
|
|
||||||
# In full paths the first name (e g HD above) is the name of a mounted
|
|
||||||
# volume.
|
|
||||||
# These names are not unique, because, for instance, two diskettes with the
|
|
||||||
# same names could be inserted. This means that paths on MacOS are not
|
|
||||||
# waterproof. In case of equal names the first volume found will do.
|
|
||||||
# Two colons "::" are the relative path to the parent. Three is to the
|
|
||||||
# grandparent etc.
|
|
||||||
elif doslikeFileSystem:
|
|
||||||
const
|
|
||||||
CurDir* = '.'
|
|
||||||
ParDir* = ".."
|
|
||||||
DirSep* = '\\' # seperator within paths
|
|
||||||
AltSep* = '/'
|
|
||||||
PathSep* = ';' # seperator between paths
|
|
||||||
FileSystemCaseSensitive* = false
|
|
||||||
ExeExt* = "exe"
|
|
||||||
ScriptExt* = "bat"
|
|
||||||
DynlibFormat* = "$1.dll"
|
|
||||||
elif defined(PalmOS) or defined(MorphOS):
|
|
||||||
const
|
|
||||||
DirSep* = '/'
|
|
||||||
AltSep* = Dirsep
|
|
||||||
PathSep* = ';'
|
|
||||||
ParDir* = ".."
|
|
||||||
FileSystemCaseSensitive* = false
|
|
||||||
ExeExt* = ""
|
|
||||||
ScriptExt* = ""
|
|
||||||
DynlibFormat* = "$1.prc"
|
|
||||||
elif defined(RISCOS):
|
|
||||||
const
|
|
||||||
DirSep* = '.'
|
|
||||||
AltSep* = '.'
|
|
||||||
ParDir* = ".." # is this correct?
|
|
||||||
PathSep* = ','
|
|
||||||
FileSystemCaseSensitive* = true
|
|
||||||
ExeExt* = ""
|
|
||||||
ScriptExt* = ""
|
|
||||||
DynlibFormat* = "lib$1.so"
|
|
||||||
else: # UNIX-like operating system
|
|
||||||
const
|
|
||||||
CurDir* = '.'
|
|
||||||
ParDir* = ".."
|
|
||||||
DirSep* = '/'
|
|
||||||
AltSep* = DirSep
|
|
||||||
PathSep* = ':'
|
|
||||||
FileSystemCaseSensitive* = when defined(macosx): false else: true
|
|
||||||
ExeExt* = ""
|
|
||||||
ScriptExt* = ""
|
|
||||||
DynlibFormat* = when defined(macosx): "lib$1.dylib" else: "lib$1.so"
|
|
||||||
|
|
||||||
const
|
|
||||||
ExtSep* = '.'
|
|
||||||
## The character which separates the base filename from the extension;
|
|
||||||
## for example, the '.' in ``os.nim``.
|
|
||||||
|
|
||||||
proc normalizePathEnd(path: var string, trailingSep = false) =
|
|
||||||
## ensures ``path`` has exactly 0 or 1 trailing `DirSep`, depending on
|
|
||||||
## ``trailingSep``, and taking care of edge cases: it preservers whether
|
|
||||||
## a path is absolute or relative, and makes sure trailing sep is `DirSep`,
|
|
||||||
## not `AltSep`.
|
|
||||||
if path.len == 0: return
|
|
||||||
var i = path.len
|
|
||||||
while i >= 1 and path[i-1] in {DirSep, AltSep}: dec(i)
|
|
||||||
if trailingSep:
|
|
||||||
# foo// => foo
|
|
||||||
path.setLen(i)
|
|
||||||
# foo => foo/
|
|
||||||
path.add DirSep
|
|
||||||
elif i>0:
|
|
||||||
# foo// => foo
|
|
||||||
path.setLen(i)
|
|
||||||
else:
|
|
||||||
# // => / (empty case was already taken care of)
|
|
||||||
path = $DirSep
|
|
||||||
|
|
||||||
proc normalizePathEnd(path: string, trailingSep = false): string =
|
|
||||||
result = path
|
|
||||||
result.normalizePathEnd(trailingSep)
|
|
||||||
|
|
||||||
proc joinPath*(head, tail: string): string {.
|
|
||||||
noSideEffect, rtl, extern: "nos$1".} =
|
|
||||||
## Joins two directory names to one.
|
|
||||||
##
|
|
||||||
## For example on Unix:
|
|
||||||
##
|
|
||||||
## .. code-block:: nim
|
|
||||||
## joinPath("usr", "lib")
|
|
||||||
##
|
|
||||||
## results in:
|
|
||||||
##
|
|
||||||
## .. code-block:: nim
|
|
||||||
## "usr/lib"
|
|
||||||
##
|
|
||||||
## If head is the empty string, tail is returned. If tail is the empty
|
|
||||||
## string, head is returned with a trailing path separator. If tail starts
|
|
||||||
## with a path separator it will be removed when concatenated to head. Other
|
|
||||||
## path separators not located on boundaries won't be modified. More
|
|
||||||
## examples on Unix:
|
|
||||||
##
|
|
||||||
## .. code-block:: nim
|
|
||||||
## assert joinPath("usr", "") == "usr/"
|
|
||||||
## assert joinPath("", "lib") == "lib"
|
|
||||||
## assert joinPath("", "/lib") == "/lib"
|
|
||||||
## assert joinPath("usr/", "/lib") == "usr/lib"
|
|
||||||
if len(head) == 0:
|
|
||||||
result = tail
|
|
||||||
elif head[len(head)-1] in {DirSep, AltSep}:
|
|
||||||
if tail.len > 0 and tail[0] in {DirSep, AltSep}:
|
|
||||||
result = head & substr(tail, 1)
|
|
||||||
else:
|
|
||||||
result = head & tail
|
|
||||||
else:
|
|
||||||
if tail.len > 0 and tail[0] in {DirSep, AltSep}:
|
|
||||||
result = head & tail
|
|
||||||
else:
|
|
||||||
result = head & DirSep & tail
|
|
||||||
|
|
||||||
proc joinPath*(parts: varargs[string]): string {.noSideEffect,
|
|
||||||
rtl, extern: "nos$1OpenArray".} =
|
|
||||||
## The same as `joinPath(head, tail)`, but works with any number of
|
|
||||||
## directory parts. You need to pass at least one element or the proc
|
|
||||||
## will assert in debug builds and crash on release builds.
|
|
||||||
result = parts[0]
|
|
||||||
for i in 1..high(parts):
|
|
||||||
result = joinPath(result, parts[i])
|
|
||||||
|
|
||||||
proc `/` * (head, tail: string): string {.noSideEffect.} =
|
|
||||||
## The same as ``joinPath(head, tail)``
|
|
||||||
##
|
|
||||||
## Here are some examples for Unix:
|
|
||||||
##
|
|
||||||
## .. code-block:: nim
|
|
||||||
## assert "usr" / "" == "usr/"
|
|
||||||
## assert "" / "lib" == "lib"
|
|
||||||
## assert "" / "/lib" == "/lib"
|
|
||||||
## assert "usr/" / "/lib" == "usr/lib"
|
|
||||||
return joinPath(head, tail)
|
|
||||||
|
|
||||||
proc splitPath*(path: string): tuple[head, tail: string] {.
|
|
||||||
noSideEffect, rtl, extern: "nos$1".} =
|
|
||||||
## Splits a directory into (head, tail), so that
|
|
||||||
## ``head / tail == path`` (except for edge cases like "/usr").
|
|
||||||
##
|
|
||||||
## Examples:
|
|
||||||
##
|
|
||||||
## .. code-block:: nim
|
|
||||||
## splitPath("usr/local/bin") -> ("usr/local", "bin")
|
|
||||||
## splitPath("usr/local/bin/") -> ("usr/local/bin", "")
|
|
||||||
## splitPath("bin") -> ("", "bin")
|
|
||||||
## splitPath("/bin") -> ("", "bin")
|
|
||||||
## splitPath("") -> ("", "")
|
|
||||||
var sepPos = -1
|
|
||||||
for i in countdown(len(path)-1, 0):
|
|
||||||
if path[i] in {DirSep, AltSep}:
|
|
||||||
sepPos = i
|
|
||||||
break
|
|
||||||
if sepPos >= 0:
|
|
||||||
result.head = substr(path, 0, sepPos-1)
|
|
||||||
result.tail = substr(path, sepPos+1)
|
|
||||||
else:
|
|
||||||
result.head = ""
|
|
||||||
result.tail = path
|
|
||||||
|
|
||||||
proc parentDirPos(path: string): int =
|
|
||||||
var q = 1
|
|
||||||
if len(path) >= 1 and path[len(path)-1] in {DirSep, AltSep}: q = 2
|
|
||||||
for i in countdown(len(path)-q, 0):
|
|
||||||
if path[i] in {DirSep, AltSep}: return i
|
|
||||||
result = -1
|
|
||||||
|
|
||||||
proc parentDir*(path: string): string {.
|
|
||||||
noSideEffect, rtl, extern: "nos$1".} =
|
|
||||||
## Returns the parent directory of `path`.
|
|
||||||
##
|
|
||||||
## This is the same as ``splitPath(path).head`` when ``path`` doesn't end
|
|
||||||
## in a dir separator.
|
|
||||||
## The remainder can be obtained with ``lastPathPart(path)``
|
|
||||||
runnableExamples:
|
|
||||||
doAssert parentDir("") == ""
|
|
||||||
when defined(posix):
|
|
||||||
doAssert parentDir("/usr/local/bin") == "/usr/local"
|
|
||||||
doAssert parentDir("foo/bar/") == "foo"
|
|
||||||
|
|
||||||
let sepPos = parentDirPos(path)
|
|
||||||
if sepPos >= 0:
|
|
||||||
result = substr(path, 0, sepPos-1)
|
|
||||||
else:
|
|
||||||
result = ""
|
|
||||||
|
|
||||||
proc tailDir*(path: string): string {.
|
|
||||||
noSideEffect, rtl, extern: "nos$1".} =
|
|
||||||
## Returns the tail part of `path`..
|
|
||||||
##
|
|
||||||
## | Example: ``tailDir("/usr/local/bin") == "local/bin"``.
|
|
||||||
## | Example: ``tailDir("usr/local/bin/") == "local/bin"``.
|
|
||||||
## | Example: ``tailDir("bin") == ""``.
|
|
||||||
var q = 1
|
|
||||||
if len(path) >= 1 and path[len(path)-1] in {DirSep, AltSep}: q = 2
|
|
||||||
for i in 0..len(path)-q:
|
|
||||||
if path[i] in {DirSep, AltSep}:
|
|
||||||
return substr(path, i+1)
|
|
||||||
result = ""
|
|
||||||
|
|
||||||
proc isRootDir*(path: string): bool {.
|
|
||||||
noSideEffect, rtl, extern: "nos$1".} =
|
|
||||||
## Checks whether a given `path` is a root directory
|
|
||||||
result = parentDirPos(path) < 0
|
|
||||||
|
|
||||||
iterator parentDirs*(path: string, fromRoot=false, inclusive=true): string =
|
|
||||||
## Walks over all parent directories of a given `path`
|
|
||||||
##
|
|
||||||
## If `fromRoot` is set, the traversal will start from the file system root
|
|
||||||
## diretory. If `inclusive` is set, the original argument will be included
|
|
||||||
## in the traversal.
|
|
||||||
##
|
|
||||||
## Relative paths won't be expanded by this proc. Instead, it will traverse
|
|
||||||
## only the directories appearing in the relative path.
|
|
||||||
if not fromRoot:
|
|
||||||
var current = path
|
|
||||||
if inclusive: yield path
|
|
||||||
while true:
|
|
||||||
if current.isRootDir: break
|
|
||||||
current = current.parentDir
|
|
||||||
yield current
|
|
||||||
else:
|
|
||||||
for i in countup(0, path.len - 2): # ignore the last /
|
|
||||||
# deal with non-normalized paths such as /foo//bar//baz
|
|
||||||
if path[i] in {DirSep, AltSep} and
|
|
||||||
(i == 0 or path[i-1] notin {DirSep, AltSep}):
|
|
||||||
yield path.substr(0, i)
|
|
||||||
|
|
||||||
if inclusive: yield path
|
|
||||||
|
|
||||||
proc `/../`*(head, tail: string): string {.noSideEffect.} =
|
|
||||||
## The same as ``parentDir(head) / tail`` unless there is no parent
|
|
||||||
## directory. Then ``head / tail`` is performed instead.
|
|
||||||
let sepPos = parentDirPos(head)
|
|
||||||
if sepPos >= 0:
|
|
||||||
result = substr(head, 0, sepPos-1) / tail
|
|
||||||
else:
|
|
||||||
result = head / tail
|
|
||||||
|
|
||||||
proc normExt(ext: string): string =
|
|
||||||
if ext == "" or ext[0] == ExtSep: result = ext # no copy needed here
|
|
||||||
else: result = ExtSep & ext
|
|
||||||
|
|
||||||
proc searchExtPos*(path: string): int =
|
|
||||||
## Returns index of the '.' char in `path` if it signifies the beginning
|
|
||||||
## of extension. Returns -1 otherwise.
|
|
||||||
# BUGFIX: do not search until 0! .DS_Store is no file extension!
|
|
||||||
result = -1
|
|
||||||
for i in countdown(len(path)-1, 1):
|
|
||||||
if path[i] == ExtSep:
|
|
||||||
result = i
|
|
||||||
break
|
|
||||||
elif path[i] in {DirSep, AltSep}:
|
|
||||||
break # do not skip over path
|
|
||||||
|
|
||||||
proc splitFile*(path: string): tuple[dir, name, ext: string] {.
|
|
||||||
noSideEffect, rtl, extern: "nos$1".} =
|
|
||||||
## Splits a filename into (dir, filename, extension).
|
|
||||||
## `dir` does not end in `DirSep`.
|
|
||||||
## `extension` includes the leading dot.
|
|
||||||
##
|
|
||||||
## Example:
|
|
||||||
##
|
|
||||||
## .. code-block:: nim
|
|
||||||
## var (dir, name, ext) = splitFile("usr/local/nimc.html")
|
|
||||||
## assert dir == "usr/local"
|
|
||||||
## assert name == "nimc"
|
|
||||||
## assert ext == ".html"
|
|
||||||
##
|
|
||||||
## If `path` has no extension, `ext` is the empty string.
|
|
||||||
## If `path` has no directory component, `dir` is the empty string.
|
|
||||||
## If `path` has no filename component, `name` and `ext` are empty strings.
|
|
||||||
if path.len == 0 or path[path.len-1] in {DirSep, AltSep}:
|
|
||||||
result = (path, "", "")
|
|
||||||
else:
|
|
||||||
var sepPos = -1
|
|
||||||
var dotPos = path.len
|
|
||||||
for i in countdown(len(path)-1, 0):
|
|
||||||
if path[i] == ExtSep:
|
|
||||||
if dotPos == path.len and i > 0 and
|
|
||||||
path[i-1] notin {DirSep, AltSep}: dotPos = i
|
|
||||||
elif path[i] in {DirSep, AltSep}:
|
|
||||||
sepPos = i
|
|
||||||
break
|
|
||||||
result.dir = substr(path, 0, sepPos-1)
|
|
||||||
result.name = substr(path, sepPos+1, dotPos-1)
|
|
||||||
result.ext = substr(path, dotPos)
|
|
||||||
|
|
||||||
proc extractFilename*(path: string): string {.
|
|
||||||
noSideEffect, rtl, extern: "nos$1".} =
|
|
||||||
## Extracts the filename of a given `path`. This is the same as
|
|
||||||
## ``name & ext`` from ``splitFile(path)``. See also ``lastPathPart``.
|
|
||||||
runnableExamples:
|
|
||||||
when defined(posix):
|
|
||||||
doAssert extractFilename("foo/bar/") == ""
|
|
||||||
doAssert extractFilename("foo/bar") == "bar"
|
|
||||||
if path.len == 0 or path[path.len-1] in {DirSep, AltSep}:
|
|
||||||
result = ""
|
|
||||||
else:
|
|
||||||
result = splitPath(path).tail
|
|
||||||
|
|
||||||
proc lastPathPart*(path: string): string {.
|
|
||||||
noSideEffect, rtl, extern: "nos$1".} =
|
|
||||||
## like ``extractFilename``, but ignores trailing dir separator; aka: `baseName`:idx:
|
|
||||||
## in some other languages.
|
|
||||||
runnableExamples:
|
|
||||||
when defined(posix):
|
|
||||||
doAssert lastPathPart("foo/bar/") == "bar"
|
|
||||||
let path = path.normalizePathEnd(trailingSep = false)
|
|
||||||
result = extractFilename(path)
|
|
||||||
|
|
||||||
proc changeFileExt*(filename, ext: string): string {.
|
|
||||||
noSideEffect, rtl, extern: "nos$1".} =
|
|
||||||
## Changes the file extension to `ext`.
|
|
||||||
##
|
|
||||||
## If the `filename` has no extension, `ext` will be added.
|
|
||||||
## If `ext` == "" then any extension is removed.
|
|
||||||
## `Ext` should be given without the leading '.', because some
|
|
||||||
## filesystems may use a different character. (Although I know
|
|
||||||
## of none such beast.)
|
|
||||||
var extPos = searchExtPos(filename)
|
|
||||||
if extPos < 0: result = filename & normExt(ext)
|
|
||||||
else: result = substr(filename, 0, extPos-1) & normExt(ext)
|
|
||||||
|
|
||||||
proc addFileExt*(filename, ext: string): string {.
|
|
||||||
noSideEffect, rtl, extern: "nos$1".} =
|
|
||||||
## Adds the file extension `ext` to `filename`, unless
|
|
||||||
## `filename` already has an extension.
|
|
||||||
##
|
|
||||||
## `Ext` should be given without the leading '.', because some
|
|
||||||
## filesystems may use a different character.
|
|
||||||
## (Although I know of none such beast.)
|
|
||||||
var extPos = searchExtPos(filename)
|
|
||||||
if extPos < 0: result = filename & normExt(ext)
|
|
||||||
else: result = filename
|
|
||||||
|
|
||||||
proc cmpPaths*(pathA, pathB: string): int {.
|
|
||||||
noSideEffect, rtl, extern: "nos$1".} =
|
|
||||||
## Compares two paths.
|
|
||||||
##
|
|
||||||
## On a case-sensitive filesystem this is done
|
|
||||||
## case-sensitively otherwise case-insensitively. Returns:
|
|
||||||
##
|
|
||||||
## | 0 iff pathA == pathB
|
|
||||||
## | < 0 iff pathA < pathB
|
|
||||||
## | > 0 iff pathA > pathB
|
|
||||||
runnableExamples:
|
|
||||||
when defined(macosx):
|
|
||||||
doAssert cmpPaths("foo", "Foo") == 0
|
|
||||||
elif defined(posix):
|
|
||||||
doAssert cmpPaths("foo", "Foo") > 0
|
|
||||||
if FileSystemCaseSensitive:
|
|
||||||
result = cmp(pathA, pathB)
|
|
||||||
else:
|
|
||||||
when defined(nimscript):
|
|
||||||
result = cmpic(pathA, pathB)
|
|
||||||
elif defined(nimdoc): discard
|
|
||||||
else:
|
|
||||||
result = cmpIgnoreCase(pathA, pathB)
|
|
||||||
|
|
||||||
proc isAbsolute*(path: string): bool {.rtl, noSideEffect, extern: "nos$1".} =
|
|
||||||
## Checks whether a given `path` is absolute.
|
|
||||||
##
|
|
||||||
## On Windows, network paths are considered absolute too.
|
|
||||||
runnableExamples:
|
|
||||||
doAssert(not "".isAbsolute)
|
|
||||||
doAssert(not ".".isAbsolute)
|
|
||||||
when defined(posix):
|
|
||||||
doAssert "/".isAbsolute
|
|
||||||
doAssert(not "a/".isAbsolute)
|
|
||||||
|
|
||||||
if len(path) == 0: return false
|
|
||||||
|
|
||||||
when doslikeFileSystem:
|
|
||||||
var len = len(path)
|
|
||||||
result = (path[0] in {'/', '\\'}) or
|
|
||||||
(len > 1 and path[0] in {'a'..'z', 'A'..'Z'} and path[1] == ':')
|
|
||||||
elif defined(macos):
|
|
||||||
# according to https://perldoc.perl.org/File/Spec/Mac.html `:a` is a relative path
|
|
||||||
result = path[0] != ':'
|
|
||||||
elif defined(RISCOS):
|
|
||||||
result = path[0] == '$'
|
|
||||||
elif defined(posix):
|
|
||||||
result = path[0] == '/'
|
|
||||||
|
|
||||||
proc unixToNativePath*(path: string, drive=""): string {.
|
|
||||||
noSideEffect, rtl, extern: "nos$1".} =
|
|
||||||
## Converts an UNIX-like path to a native one.
|
|
||||||
##
|
|
||||||
## On an UNIX system this does nothing. Else it converts
|
|
||||||
## '/', '.', '..' to the appropriate things.
|
|
||||||
##
|
|
||||||
## On systems with a concept of "drives", `drive` is used to determine
|
|
||||||
## which drive label to use during absolute path conversion.
|
|
||||||
## `drive` defaults to the drive of the current working directory, and is
|
|
||||||
## ignored on systems that do not have a concept of "drives".
|
|
||||||
|
|
||||||
when defined(unix):
|
|
||||||
result = path
|
|
||||||
else:
|
|
||||||
if path.len == 0:
|
|
||||||
return ""
|
|
||||||
|
|
||||||
var start: int
|
|
||||||
if path[0] == '/':
|
|
||||||
# an absolute path
|
|
||||||
when doslikeFileSystem:
|
|
||||||
if drive != "":
|
|
||||||
result = drive & ":" & DirSep
|
|
||||||
else:
|
|
||||||
result = $DirSep
|
|
||||||
elif defined(macos):
|
|
||||||
result = "" # must not start with ':'
|
|
||||||
else:
|
|
||||||
result = $DirSep
|
|
||||||
start = 1
|
|
||||||
elif path[0] == '.' and (path.len == 1 or path[1] == '/'):
|
|
||||||
# current directory
|
|
||||||
result = $CurDir
|
|
||||||
start = when doslikeFileSystem: 1 else: 2
|
|
||||||
else:
|
|
||||||
result = ""
|
|
||||||
start = 0
|
|
||||||
|
|
||||||
var i = start
|
|
||||||
while i < len(path): # ../../../ --> ::::
|
|
||||||
if i+2 < path.len and path[i] == '.' and path[i+1] == '.' and path[i+2] == '/':
|
|
||||||
# parent directory
|
|
||||||
when defined(macos):
|
|
||||||
if result[high(result)] == ':':
|
|
||||||
add result, ':'
|
|
||||||
else:
|
|
||||||
add result, ParDir
|
|
||||||
else:
|
|
||||||
add result, ParDir & DirSep
|
|
||||||
inc(i, 3)
|
|
||||||
elif path[i] == '/':
|
|
||||||
add result, DirSep
|
|
||||||
inc(i)
|
|
||||||
else:
|
|
||||||
add result, path[i]
|
|
||||||
inc(i)
|
|
||||||
|
|
||||||
include "includes/oserr"
|
|
||||||
when not defined(nimscript):
|
|
||||||
include "includes/osenv"
|
|
||||||
|
|
||||||
proc getHomeDir*(): string {.rtl, extern: "nos$1",
|
|
||||||
tags: [ReadEnvEffect, ReadIOEffect].} =
|
|
||||||
## Returns the home directory of the current user.
|
|
||||||
##
|
|
||||||
## This proc is wrapped by the expandTilde proc for the convenience of
|
|
||||||
## processing paths coming from user configuration files.
|
|
||||||
when defined(windows): return string(getEnv("USERPROFILE")) & "\\"
|
|
||||||
else: return string(getEnv("HOME")) & "/"
|
|
||||||
|
|
||||||
proc getConfigDir*(): string {.rtl, extern: "nos$1",
|
|
||||||
tags: [ReadEnvEffect, ReadIOEffect].} =
|
|
||||||
## Returns the config directory of the current user for applications.
|
|
||||||
##
|
|
||||||
## On non-Windows OSs, this proc conforms to the XDG Base Directory
|
|
||||||
## spec. Thus, this proc returns the value of the XDG_CONFIG_HOME environment
|
|
||||||
## variable if it is set, and returns the default configuration directory,
|
|
||||||
## "~/.config/", otherwise.
|
|
||||||
##
|
|
||||||
## An OS-dependent trailing slash is always present at the end of the
|
|
||||||
## returned string; `\` on Windows and `/` on all other OSs.
|
|
||||||
when defined(windows):
|
|
||||||
result = getEnv("APPDATA").string
|
|
||||||
else:
|
|
||||||
result = getEnv("XDG_CONFIG_HOME", getEnv("HOME").string / ".config").string
|
|
||||||
result.normalizePathEnd(trailingSep = true)
|
|
||||||
|
|
||||||
proc getTempDir*(): string {.rtl, extern: "nos$1",
|
|
||||||
tags: [ReadEnvEffect, ReadIOEffect].} =
|
|
||||||
## Returns the temporary directory of the current user for applications to
|
|
||||||
## save temporary files in.
|
|
||||||
##
|
|
||||||
## **Please do not use this**: On Android, it currently
|
|
||||||
## returns ``getHomeDir()``, and on other Unix based systems it can cause
|
|
||||||
## security problems too. That said, you can override this implementation
|
|
||||||
## by adding ``-d:tempDir=mytempname`` to your compiler invokation.
|
|
||||||
when defined(tempDir):
|
|
||||||
const tempDir {.strdefine.}: string = nil
|
|
||||||
return tempDir
|
|
||||||
elif defined(windows): return string(getEnv("TEMP")) & "\\"
|
|
||||||
elif defined(android): return getHomeDir()
|
|
||||||
else: return "/tmp/"
|
|
||||||
|
|
||||||
proc expandTilde*(path: string): string {.
|
|
||||||
tags: [ReadEnvEffect, ReadIOEffect].} =
|
|
||||||
## Expands ``~`` or a path starting with ``~/`` to a full path, replacing
|
|
||||||
## ``~`` with ``getHomeDir()`` (otherwise returns ``path`` unmodified).
|
|
||||||
##
|
|
||||||
## Windows: this is still supported despite Windows platform not having this
|
|
||||||
## convention; also, both ``~/`` and ``~\`` are handled.
|
|
||||||
runnableExamples:
|
|
||||||
doAssert expandTilde("~" / "appname.cfg") == getHomeDir() / "appname.cfg"
|
|
||||||
if len(path) == 0 or path[0] != '~':
|
|
||||||
result = path
|
|
||||||
elif len(path) == 1:
|
|
||||||
result = getHomeDir()
|
|
||||||
elif (path[1] in {DirSep, AltSep}):
|
|
||||||
result = getHomeDir() / path.substr(2)
|
|
||||||
else:
|
|
||||||
# TODO: handle `~bob` and `~bob/` which means home of bob
|
|
||||||
result = path
|
|
||||||
|
|
||||||
# TODO: consider whether quoteShellPosix, quoteShellWindows, quoteShell, quoteShellCommand
|
|
||||||
# belong in `strutils` instead; they are not specific to paths
|
|
||||||
proc quoteShellWindows*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".} =
|
|
||||||
## Quote s, so it can be safely passed to Windows API.
|
|
||||||
## Based on Python's subprocess.list2cmdline
|
|
||||||
## See http://msdn.microsoft.com/en-us/library/17w5ykft.aspx
|
|
||||||
let needQuote = {' ', '\t'} in s or s.len == 0
|
|
||||||
|
|
||||||
result = ""
|
|
||||||
var backslashBuff = ""
|
|
||||||
if needQuote:
|
|
||||||
result.add("\"")
|
|
||||||
|
|
||||||
for c in s:
|
|
||||||
if c == '\\':
|
|
||||||
backslashBuff.add(c)
|
|
||||||
elif c == '\"':
|
|
||||||
result.add(backslashBuff)
|
|
||||||
result.add(backslashBuff)
|
|
||||||
backslashBuff.setLen(0)
|
|
||||||
result.add("\\\"")
|
|
||||||
else:
|
|
||||||
if backslashBuff.len != 0:
|
|
||||||
result.add(backslashBuff)
|
|
||||||
backslashBuff.setLen(0)
|
|
||||||
result.add(c)
|
|
||||||
|
|
||||||
if needQuote:
|
|
||||||
result.add("\"")
|
|
||||||
|
|
||||||
proc quoteShellPosix*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".} =
|
|
||||||
## Quote ``s``, so it can be safely passed to POSIX shell.
|
|
||||||
## Based on Python's pipes.quote
|
|
||||||
const safeUnixChars = {'%', '+', '-', '.', '/', '_', ':', '=', '@',
|
|
||||||
'0'..'9', 'A'..'Z', 'a'..'z'}
|
|
||||||
if s.len == 0:
|
|
||||||
return "''"
|
|
||||||
|
|
||||||
let safe = s.allCharsInSet(safeUnixChars)
|
|
||||||
|
|
||||||
if safe:
|
|
||||||
return s
|
|
||||||
else:
|
|
||||||
return "'" & s.replace("'", "'\"'\"'") & "'"
|
|
||||||
|
|
||||||
when defined(windows) or defined(posix) or defined(nintendoswitch):
|
|
||||||
proc quoteShell*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".} =
|
|
||||||
## Quote ``s``, so it can be safely passed to shell.
|
|
||||||
when defined(windows):
|
|
||||||
return quoteShellWindows(s)
|
|
||||||
else:
|
|
||||||
return quoteShellPosix(s)
|
|
||||||
|
|
||||||
proc quoteShellCommand*(args: openArray[string]): string =
|
|
||||||
## Concatenates and quotes shell arguments `args`
|
|
||||||
runnableExamples:
|
|
||||||
when defined(posix):
|
|
||||||
assert quoteShellCommand(["aaa", "", "c d"]) == "aaa '' 'c d'"
|
|
||||||
when defined(windows):
|
|
||||||
assert quoteShellCommand(["aaa", "", "c d"]) == "aaa \"\" \"c d\""
|
|
||||||
# can't use `map` pending https://github.com/nim-lang/Nim/issues/8303
|
|
||||||
for i in 0..<args.len:
|
|
||||||
if i > 0: result.add " "
|
|
||||||
result.add quoteShell(args[i])
|
|
||||||
|
|
||||||
when isMainModule:
|
|
||||||
assert quoteShellWindows("aaa") == "aaa"
|
|
||||||
assert quoteShellWindows("aaa\"") == "aaa\\\""
|
|
||||||
assert quoteShellWindows("") == "\"\""
|
|
||||||
|
|
||||||
assert quoteShellPosix("aaa") == "aaa"
|
|
||||||
assert quoteShellPosix("aaa a") == "'aaa a'"
|
|
||||||
assert quoteShellPosix("") == "''"
|
|
||||||
assert quoteShellPosix("a'a") == "'a'\"'\"'a'"
|
|
||||||
|
|
||||||
when defined(posix):
|
|
||||||
assert quoteShell("") == "''"
|
|
||||||
|
|
||||||
block normalizePathEndTest:
|
|
||||||
# handle edge cases correctly: shouldn't affect whether path is
|
|
||||||
# absolute/relative
|
|
||||||
doAssert "".normalizePathEnd(true) == ""
|
|
||||||
doAssert "".normalizePathEnd(false) == ""
|
|
||||||
doAssert "/".normalizePathEnd(true) == $DirSep
|
|
||||||
doAssert "/".normalizePathEnd(false) == $DirSep
|
|
||||||
|
|
||||||
when defined(posix):
|
|
||||||
doAssert "//".normalizePathEnd(false) == "/"
|
|
||||||
doAssert "foo.bar//".normalizePathEnd == "foo.bar"
|
|
||||||
doAssert "bar//".normalizePathEnd(trailingSep = true) == "bar/"
|
|
||||||
when defined(Windows):
|
|
||||||
doAssert r"C:\foo\\".normalizePathEnd == r"C:\foo"
|
|
||||||
doAssert r"C:\foo".normalizePathEnd(trailingSep = true) == r"C:\foo\"
|
|
||||||
# this one is controversial: we could argue for returning `D:\` instead,
|
|
||||||
# but this is simplest.
|
|
||||||
doAssert r"D:\".normalizePathEnd == r"D:"
|
|
||||||
doAssert r"E:/".normalizePathEnd(trailingSep = true) == r"E:\"
|
|
||||||
doAssert "/".normalizePathEnd == r"\"
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ include "system/inclrtl"
|
||||||
import
|
import
|
||||||
strutils, os, strtabs, streams, cpuinfo
|
strutils, os, strtabs, streams, cpuinfo
|
||||||
|
|
||||||
from ospaths import quoteShell, quoteShellWindows, quoteShellPosix
|
#from ospaths import quoteShell, quoteShellWindows, quoteShellPosix
|
||||||
export quoteShell, quoteShellWindows, quoteShellPosix
|
export quoteShell, quoteShellWindows, quoteShellPosix
|
||||||
|
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,8 @@ when defined(windows):
|
||||||
SIGTERM = cint(15)
|
SIGTERM = cint(15)
|
||||||
elif defined(macosx) or defined(linux) or defined(freebsd) or
|
elif defined(macosx) or defined(linux) or defined(freebsd) or
|
||||||
defined(openbsd) or defined(netbsd) or defined(solaris) or
|
defined(openbsd) or defined(netbsd) or defined(solaris) or
|
||||||
defined(dragonfly) or defined(nintendoswitch) or defined(genode):
|
defined(dragonfly) or defined(nintendoswitch) or defined(genode) or
|
||||||
|
defined(aix):
|
||||||
const
|
const
|
||||||
SIGABRT = cint(6)
|
SIGABRT = cint(6)
|
||||||
SIGFPE = cint(8)
|
SIGFPE = cint(8)
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ proc listFiles*(dir: string): seq[string] =
|
||||||
## Lists all the files (non-recursively) in the directory `dir`.
|
## Lists all the files (non-recursively) in the directory `dir`.
|
||||||
builtin
|
builtin
|
||||||
|
|
||||||
proc removeDir(dir: string){.
|
proc removeDir(dir: string) {.
|
||||||
tags: [ReadIOEffect, WriteIOEffect], raises: [OSError].} = builtin
|
tags: [ReadIOEffect, WriteIOEffect], raises: [OSError].} = builtin
|
||||||
proc removeFile(dir: string) {.
|
proc removeFile(dir: string) {.
|
||||||
tags: [ReadIOEffect, WriteIOEffect], raises: [OSError].} = builtin
|
tags: [ReadIOEffect, WriteIOEffect], raises: [OSError].} = builtin
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import lists
|
||||||
import math
|
import math
|
||||||
# import marshal
|
# import marshal
|
||||||
import options
|
import options
|
||||||
import ospaths
|
import os
|
||||||
# import parsecfg
|
# import parsecfg
|
||||||
# import parseopt
|
# import parseopt
|
||||||
import parseutils
|
import parseutils
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue