Merge branch 'master' into vm2
This commit is contained in:
commit
8c553fa8a2
33 changed files with 28859 additions and 234 deletions
300
lib/pure/os.nim
300
lib/pure/os.nim
|
|
@ -21,7 +21,7 @@ import
|
|||
|
||||
when defined(windows):
|
||||
import winlean
|
||||
elif defined(posix):
|
||||
elif defined(posix):
|
||||
import posix
|
||||
else:
|
||||
{.error: "OS module not ported to your operating system!".}
|
||||
|
|
@ -33,7 +33,7 @@ type
|
|||
## from an environment variable
|
||||
FWriteEnv* = object of FWriteIO ## effect that denotes a write
|
||||
## to an environment variable
|
||||
|
||||
|
||||
FReadDir* = object of FReadIO ## effect that denotes a write operation to
|
||||
## the directory structure
|
||||
FWriteDir* = object of FWriteIO ## effect that denotes a write operation to
|
||||
|
|
@ -179,7 +179,7 @@ proc OSErrorMsg*(): string {.rtl, extern: "nos$1", deprecated.} =
|
|||
## Returns "" if no error occured.
|
||||
##
|
||||
## **Deprecated since version 0.9.4**: use the other ``OSErrorMsg`` proc.
|
||||
|
||||
|
||||
result = ""
|
||||
when defined(Windows):
|
||||
var err = GetLastError()
|
||||
|
|
@ -276,7 +276,7 @@ proc OSLastError*(): TOSErrorCode =
|
|||
## On Windows some OS calls can reset the error code to ``0`` causing this
|
||||
## procedure to return ``0``. It is therefore advised to call this procedure
|
||||
## immediately after an OS call fails. On POSIX systems this is not a problem.
|
||||
|
||||
|
||||
when defined(windows):
|
||||
result = TOSErrorCode(GetLastError())
|
||||
else:
|
||||
|
|
@ -342,7 +342,14 @@ when defined(windows):
|
|||
template getCommandLine(): expr = getCommandLineW()
|
||||
|
||||
proc skipFindData(f: TWIN32_FIND_DATA): bool {.inline.} =
|
||||
result = f.cFilename[0].int == ord('.')
|
||||
let
|
||||
nul = 0
|
||||
dot = ord('.')
|
||||
result = (f.cFilename[0].int == dot)
|
||||
if result:
|
||||
result = (f.cFilename[1].int in {dot, nul})
|
||||
if result:
|
||||
result = (f.cFilename[2].int == nul)
|
||||
|
||||
template getFilename(f: expr): expr =
|
||||
$cast[WideCString](addr(f.cFilename[0]))
|
||||
|
|
@ -352,11 +359,18 @@ when defined(windows):
|
|||
template getCommandLine(): expr = getCommandLineA()
|
||||
|
||||
proc skipFindData(f: TWIN32_FIND_DATA): bool {.inline.} =
|
||||
result = f.cFilename[0] == '.'
|
||||
let
|
||||
nul = '\0'
|
||||
dot = '.'
|
||||
result = (f.cFilename[0] == dot)
|
||||
if result:
|
||||
result = (f.cFilename[1] in {dot, nul})
|
||||
if result:
|
||||
result = (f.cFilename[2] == nul)
|
||||
|
||||
template getFilename(f: expr): expr = $f.cFilename
|
||||
|
||||
proc existsFile*(filename: string): bool {.rtl, extern: "nos$1",
|
||||
|
||||
proc existsFile*(filename: string): bool {.rtl, extern: "nos$1",
|
||||
tags: [FReadDir].} =
|
||||
## Returns true if the file exists, false otherwise.
|
||||
when defined(windows):
|
||||
|
|
@ -410,7 +424,7 @@ proc getLastAccessTime*(file: string): TTime {.rtl, extern: "nos$1".} =
|
|||
result = winTimeToUnixTime(rdFileTime(f.ftLastAccessTime))
|
||||
findclose(h)
|
||||
|
||||
proc getCreationTime*(file: string): TTime {.rtl, extern: "nos$1".} =
|
||||
proc getCreationTime*(file: string): TTime {.rtl, extern: "nos$1".} =
|
||||
## Returns the `file`'s creation time.
|
||||
when defined(posix):
|
||||
var res: TStat
|
||||
|
|
@ -524,7 +538,7 @@ proc SplitPath*(path: string): tuple[head, tail: string] {.
|
|||
## Splits a directory into (head, tail), so that
|
||||
## ``JoinPath(head, tail) == path``.
|
||||
##
|
||||
## Examples:
|
||||
## Examples:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## SplitPath("usr/local/bin") -> ("usr/local", "bin")
|
||||
|
|
@ -567,7 +581,7 @@ proc parentDir*(path: string): string {.
|
|||
|
||||
proc isRootDir*(path: string): bool {.
|
||||
noSideEffect, rtl, extern: "nos$1".} =
|
||||
## Checks whether a given `path` is a root directory
|
||||
## Checks whether a given `path` is a root directory
|
||||
result = parentDirPos(path) < 0
|
||||
|
||||
iterator parentDirs*(path: string, fromRoot=false, inclusive=true): string =
|
||||
|
|
@ -589,7 +603,7 @@ iterator parentDirs*(path: string, fromRoot=false, inclusive=true): string =
|
|||
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
|
||||
if path[i] in {dirsep, altsep} and
|
||||
(i == 0 or path[i-1] notin {dirsep, altsep}):
|
||||
yield path.substr(0, i)
|
||||
|
||||
|
|
@ -642,7 +656,7 @@ proc splitFile*(path: string): tuple[dir, name, ext: string] {.
|
|||
var dotPos = path.len
|
||||
for i in countdown(len(path)-1, 0):
|
||||
if path[i] == ExtSep:
|
||||
if dotPos == path.len and i > 0 and
|
||||
if dotPos == path.len and i > 0 and
|
||||
path[i-1] notin {dirsep, altsep}: dotPos = i
|
||||
elif path[i] in {dirsep, altsep}:
|
||||
sepPos = i
|
||||
|
|
@ -653,7 +667,7 @@ proc splitFile*(path: string): tuple[dir, name, ext: string] {.
|
|||
|
||||
proc extractFilename*(path: string): string {.
|
||||
noSideEffect, rtl, extern: "nos$1".} =
|
||||
## Extracts the filename of a given `path`. This is the same as
|
||||
## Extracts the filename of a given `path`. This is the same as
|
||||
## ``name & ext`` from ``splitFile(path)``.
|
||||
if path.len == 0 or path[path.len-1] in {dirSep, altSep}:
|
||||
result = ""
|
||||
|
|
@ -669,7 +683,7 @@ proc expandFilename*(filename: string): string {.rtl, extern: "nos$1",
|
|||
var unused: widecstring
|
||||
var res = newWideCString("", bufsize div 2)
|
||||
var L = GetFullPathNameW(newWideCString(filename), bufsize, res, unused)
|
||||
if L <= 0'i32 or L >= bufsize:
|
||||
if L <= 0'i32 or L >= bufsize:
|
||||
OSError(OSLastError())
|
||||
result = res$L
|
||||
else:
|
||||
|
|
@ -684,7 +698,7 @@ proc expandFilename*(filename: string): string {.rtl, extern: "nos$1",
|
|||
var r = realpath(filename, result)
|
||||
if r.isNil: OSError(OSLastError())
|
||||
setlen(result, c_strlen(result))
|
||||
|
||||
|
||||
proc ChangeFileExt*(filename, ext: string): string {.
|
||||
noSideEffect, rtl, extern: "nos$1".} =
|
||||
## Changes the file extension to `ext`.
|
||||
|
|
@ -740,12 +754,12 @@ proc isAbsolute*(path: string): bool {.rtl, noSideEffect, extern: "nos$1".} =
|
|||
elif defined(posix):
|
||||
result = path[0] == '/'
|
||||
|
||||
proc sameFile*(path1, path2: string): bool {.rtl, extern: "nos$1",
|
||||
proc sameFile*(path1, path2: string): bool {.rtl, extern: "nos$1",
|
||||
tags: [FReadDir].} =
|
||||
## Returns True if both pathname arguments refer to the same physical
|
||||
## Returns True if both pathname arguments refer to the same physical
|
||||
## file or directory. Raises an exception if any of the files does not
|
||||
## exist or information about it can not be obtained.
|
||||
##
|
||||
##
|
||||
## This proc will return true if given two alternative hard-linked or
|
||||
## sym-linked paths to the same file or directory.
|
||||
when defined(Windows):
|
||||
|
|
@ -761,7 +775,7 @@ proc sameFile*(path1, path2: string): bool {.rtl, extern: "nos$1",
|
|||
|
||||
var f1 = OpenHandle(p1)
|
||||
var f2 = OpenHandle(p2)
|
||||
|
||||
|
||||
else:
|
||||
template OpenHandle(path: expr): expr =
|
||||
CreateFileA(path, 0'i32, FILE_SHARE_DELETE or FILE_SHARE_READ or
|
||||
|
|
@ -829,7 +843,87 @@ proc sameFileContent*(path1, path2: string): bool {.rtl, extern: "nos$1",
|
|||
close(a)
|
||||
close(b)
|
||||
|
||||
proc copyFile*(source, dest: string) {.rtl, extern: "nos$1",
|
||||
type
|
||||
TFilePermission* = enum ## file access permission; modelled after UNIX
|
||||
fpUserExec, ## execute access for the file owner
|
||||
fpUserWrite, ## write access for the file owner
|
||||
fpUserRead, ## read access for the file owner
|
||||
fpGroupExec, ## execute access for the group
|
||||
fpGroupWrite, ## write access for the group
|
||||
fpGroupRead, ## read access for the group
|
||||
fpOthersExec, ## execute access for others
|
||||
fpOthersWrite, ## write access for others
|
||||
fpOthersRead ## read access for others
|
||||
|
||||
proc getFilePermissions*(filename: string): set[TFilePermission] {.
|
||||
rtl, extern: "nos$1", tags: [FReadDir].} =
|
||||
## retrieves file permissions for `filename`. `OSError` is raised in case of
|
||||
## an error. On Windows, only the ``readonly`` flag is checked, every other
|
||||
## permission is available in any case.
|
||||
when defined(posix):
|
||||
var a: TStat
|
||||
if stat(filename, a) < 0'i32: OSError(OSLastError())
|
||||
result = {}
|
||||
if (a.st_mode and S_IRUSR) != 0'i32: result.incl(fpUserRead)
|
||||
if (a.st_mode and S_IWUSR) != 0'i32: result.incl(fpUserWrite)
|
||||
if (a.st_mode and S_IXUSR) != 0'i32: result.incl(fpUserExec)
|
||||
|
||||
if (a.st_mode and S_IRGRP) != 0'i32: result.incl(fpGroupRead)
|
||||
if (a.st_mode and S_IWGRP) != 0'i32: result.incl(fpGroupWrite)
|
||||
if (a.st_mode and S_IXGRP) != 0'i32: result.incl(fpGroupExec)
|
||||
|
||||
if (a.st_mode and S_IROTH) != 0'i32: result.incl(fpOthersRead)
|
||||
if (a.st_mode and S_IWOTH) != 0'i32: result.incl(fpOthersWrite)
|
||||
if (a.st_mode and S_IXOTH) != 0'i32: result.incl(fpOthersExec)
|
||||
else:
|
||||
when useWinUnicode:
|
||||
wrapUnary(res, GetFileAttributesW, filename)
|
||||
else:
|
||||
var res = GetFileAttributesA(filename)
|
||||
if res == -1'i32: OSError(OSLastError())
|
||||
if (res and FILE_ATTRIBUTE_READONLY) != 0'i32:
|
||||
result = {fpUserExec, fpUserRead, fpGroupExec, fpGroupRead,
|
||||
fpOthersExec, fpOthersRead}
|
||||
else:
|
||||
result = {fpUserExec..fpOthersRead}
|
||||
|
||||
proc setFilePermissions*(filename: string, permissions: set[TFilePermission]) {.
|
||||
rtl, extern: "nos$1", tags: [FWriteDir].} =
|
||||
## sets the file permissions for `filename`. `OSError` is raised in case of
|
||||
## an error. On Windows, only the ``readonly`` flag is changed, depending on
|
||||
## ``fpUserWrite``.
|
||||
when defined(posix):
|
||||
var p = 0'i32
|
||||
if fpUserRead in permissions: p = p or S_IRUSR
|
||||
if fpUserWrite in permissions: p = p or S_IWUSR
|
||||
if fpUserExec in permissions: p = p or S_IXUSR
|
||||
|
||||
if fpGroupRead in permissions: p = p or S_IRGRP
|
||||
if fpGroupWrite in permissions: p = p or S_IWGRP
|
||||
if fpGroupExec in permissions: p = p or S_IXGRP
|
||||
|
||||
if fpOthersRead in permissions: p = p or S_IROTH
|
||||
if fpOthersWrite in permissions: p = p or S_IWOTH
|
||||
if fpOthersExec in permissions: p = p or S_IXOTH
|
||||
|
||||
if chmod(filename, p) != 0: OSError(OSLastError())
|
||||
else:
|
||||
when useWinUnicode:
|
||||
wrapUnary(res, GetFileAttributesW, filename)
|
||||
else:
|
||||
var res = GetFileAttributesA(filename)
|
||||
if res == -1'i32: OSError(OSLastError())
|
||||
if fpUserWrite in permissions:
|
||||
res = res and not FILE_ATTRIBUTE_READONLY
|
||||
else:
|
||||
res = res or FILE_ATTRIBUTE_READONLY
|
||||
when useWinUnicode:
|
||||
wrapBinary(res2, SetFileAttributesW, filename, res)
|
||||
else:
|
||||
var res2 = SetFileAttributesA(filename, res)
|
||||
if res2 == - 1'i32: OSError(OSLastError())
|
||||
|
||||
proc copyFile*(source, dest: string) {.rtl, extern: "nos$1",
|
||||
tags: [FReadIO, FWriteIO].} =
|
||||
## Copies a file from `source` to `dest`.
|
||||
##
|
||||
|
|
@ -870,7 +964,7 @@ proc copyFile*(source, dest: string) {.rtl, extern: "nos$1",
|
|||
close(s)
|
||||
close(d)
|
||||
|
||||
proc moveFile*(source, dest: string) {.rtl, extern: "nos$1",
|
||||
proc moveFile*(source, dest: string) {.rtl, extern: "nos$1",
|
||||
tags: [FReadIO, FWriteIO].} =
|
||||
## Moves a file from `source` to `dest`. If this fails, `EOS` is raised.
|
||||
if crename(source, dest) != 0'i32:
|
||||
|
|
@ -882,10 +976,13 @@ when not defined(ENOENT):
|
|||
proc removeFile*(file: string) {.rtl, extern: "nos$1", tags: [FWriteDir].} =
|
||||
## Removes the `file`. If this fails, `EOS` is raised. This does not fail
|
||||
## if the file never existed in the first place.
|
||||
## On Windows, ignores the read-only attribute.
|
||||
when defined(Windows):
|
||||
setFilePermissions(file, {fpUserWrite})
|
||||
if cremove(file) != 0'i32 and errno != ENOENT:
|
||||
raise newException(EOS, $strerror(errno))
|
||||
|
||||
proc execShellCmd*(command: string): int {.rtl, extern: "nos$1",
|
||||
proc execShellCmd*(command: string): int {.rtl, extern: "nos$1",
|
||||
tags: [FExecIO].} =
|
||||
## Executes a `shell command`:idx:.
|
||||
##
|
||||
|
|
@ -897,7 +994,7 @@ proc execShellCmd*(command: string): int {.rtl, extern: "nos$1",
|
|||
## module.
|
||||
result = csystem(command)
|
||||
|
||||
# Environment handling cannot be put into RTL, because the ``envPairs``
|
||||
# Environment handling cannot be put into RTL, because the ``envPairs``
|
||||
# iterator depends on ``environment``.
|
||||
|
||||
var
|
||||
|
|
@ -943,11 +1040,11 @@ when defined(windows):
|
|||
|
||||
else:
|
||||
const
|
||||
useNSGetEnviron = defined(macosx) and
|
||||
useNSGetEnviron = defined(macosx) and
|
||||
(defined(createNimRtl) or defined(useNimRtl))
|
||||
when useNSGetEnviron:
|
||||
# From the manual:
|
||||
# Shared libraries and bundles don't have direct access to environ,
|
||||
# Shared libraries and bundles don't have direct access to environ,
|
||||
# which is only available to the loader ld(1) when a complete program
|
||||
# is being linked.
|
||||
# The environment routines can still be used, but if direct access to
|
||||
|
|
@ -1025,13 +1122,13 @@ proc putEnv*(key, val: string) {.tags: [FWriteEnv].} =
|
|||
if SetEnvironmentVariableA(key, val) == 0'i32: OSError(OSLastError())
|
||||
|
||||
iterator envPairs*(): tuple[key, value: TaintedString] {.tags: [FReadEnv].} =
|
||||
## Iterate over all `environments variables`:idx:. In the first component
|
||||
## Iterate over all `environments variables`:idx:. In the first component
|
||||
## of the tuple is the name of the current variable stored, in the second
|
||||
## its value.
|
||||
getEnvVarsC()
|
||||
for i in 0..high(environment):
|
||||
var p = find(environment[i], '=')
|
||||
yield (TaintedString(substr(environment[i], 0, p-1)),
|
||||
yield (TaintedString(substr(environment[i], 0, p-1)),
|
||||
TaintedString(substr(environment[i], p+1)))
|
||||
|
||||
iterator walkFiles*(pattern: string): string {.tags: [FReadDir].} =
|
||||
|
|
@ -1125,7 +1222,7 @@ iterator walkDir*(dir: string): tuple[kind: TPathComponent, path: string] {.
|
|||
|
||||
iterator walkDirRec*(dir: string, filter={pcFile, pcDir}): string {.
|
||||
tags: [FReadDir].} =
|
||||
## walks over the directory `dir` and yields for each file in `dir`. The
|
||||
## walks over the directory `dir` and yields for each file in `dir`. The
|
||||
## full path for each file is returned.
|
||||
## Walking is recursive. `filter` controls the behaviour of the iterator:
|
||||
##
|
||||
|
|
@ -1137,7 +1234,7 @@ iterator walkDirRec*(dir: string, filter={pcFile, pcDir}): string {.
|
|||
## ``pcDir`` follow real directories
|
||||
## ``pcLinkToDir`` follow symbolic links to directories
|
||||
## --------------------- ---------------------------------------------
|
||||
##
|
||||
##
|
||||
var stack = @[dir]
|
||||
while stack.len > 0:
|
||||
for k,p in walkDir(stack.pop()):
|
||||
|
|
@ -1146,14 +1243,14 @@ iterator walkDirRec*(dir: string, filter={pcFile, pcDir}): string {.
|
|||
of pcFile, pcLinkToFile: yield p
|
||||
of pcDir, pcLinkToDir: stack.add(p)
|
||||
|
||||
proc rawRemoveDir(dir: string) =
|
||||
proc rawRemoveDir(dir: string) =
|
||||
when defined(windows):
|
||||
when useWinUnicode:
|
||||
wrapUnary(res, RemoveDirectoryW, dir)
|
||||
else:
|
||||
var res = RemoveDirectoryA(dir)
|
||||
let lastError = OSLastError()
|
||||
if res == 0'i32 and lastError.int32 != 3'i32 and
|
||||
if res == 0'i32 and lastError.int32 != 3'i32 and
|
||||
lastError.int32 != 18'i32 and lastError.int32 != 2'i32:
|
||||
OSError(lastError)
|
||||
else:
|
||||
|
|
@ -1166,7 +1263,7 @@ proc removeDir*(dir: string) {.rtl, extern: "nos$1", tags: [
|
|||
##
|
||||
## If this fails, `EOS` is raised. This does not fail if the directory never
|
||||
## existed in the first place.
|
||||
for kind, path in walkDir(dir):
|
||||
for kind, path in walkDir(dir):
|
||||
case kind
|
||||
of pcFile, pcLinkToFile, pcLinkToDir: removeFile(path)
|
||||
of pcDir: removeDir(path)
|
||||
|
|
@ -1192,7 +1289,7 @@ proc createDir*(dir: string) {.rtl, extern: "nos$1", tags: [FWriteDir].} =
|
|||
##
|
||||
## The directory may contain several subdirectories that do not exist yet.
|
||||
## The full path is created. If this fails, `EOS` is raised. It does **not**
|
||||
## fail if the path already exists because for most usages this does not
|
||||
## fail if the path already exists because for most usages this does not
|
||||
## indicate an error.
|
||||
var omitNext = false
|
||||
when defined(doslike):
|
||||
|
|
@ -1205,7 +1302,7 @@ proc createDir*(dir: string) {.rtl, extern: "nos$1", tags: [FWriteDir].} =
|
|||
rawCreateDir(substr(dir, 0, i-1))
|
||||
rawCreateDir(dir)
|
||||
|
||||
proc copyDir*(source, dest: string) {.rtl, extern: "nos$1",
|
||||
proc copyDir*(source, dest: string) {.rtl, extern: "nos$1",
|
||||
tags: [FWriteIO, FReadIO].} =
|
||||
## Copies a directory from `source` to `dest`. If this fails, `EOS` is raised.
|
||||
createDir(dest)
|
||||
|
|
@ -1220,7 +1317,7 @@ proc copyDir*(source, dest: string) {.rtl, extern: "nos$1",
|
|||
|
||||
proc parseCmdLine*(c: string): seq[string] {.
|
||||
noSideEffect, rtl, extern: "nos$1".} =
|
||||
## Splits a command line into several components;
|
||||
## Splits a command line into several components;
|
||||
## This proc is only occassionally useful, better use the `parseopt` module.
|
||||
##
|
||||
## On Windows, it uses the following parsing rules
|
||||
|
|
@ -1247,7 +1344,7 @@ proc parseCmdLine*(c: string): seq[string] {.
|
|||
## causing a literal double quotation mark (") to be placed in argv.
|
||||
##
|
||||
## On Posix systems, it uses the following parsing rules:
|
||||
## Components are separated by whitespace unless the whitespace
|
||||
## Components are separated by whitespace unless the whitespace
|
||||
## occurs within ``"`` or ``'`` quotes.
|
||||
result = @[]
|
||||
var i = 0
|
||||
|
|
@ -1260,31 +1357,31 @@ proc parseCmdLine*(c: string): seq[string] {.
|
|||
if c[i] == '\0': break
|
||||
var inQuote = false
|
||||
while true:
|
||||
case c[i]
|
||||
case c[i]
|
||||
of '\0': break
|
||||
of '\\':
|
||||
var j = i
|
||||
while c[j] == '\\': inc(j)
|
||||
if c[j] == '"':
|
||||
if c[j] == '"':
|
||||
for k in 1..(j-i) div 2: a.add('\\')
|
||||
if (j-i) mod 2 == 0:
|
||||
if (j-i) mod 2 == 0:
|
||||
i = j
|
||||
else:
|
||||
else:
|
||||
a.add('"')
|
||||
i = j+1
|
||||
else:
|
||||
else:
|
||||
a.add(c[i])
|
||||
inc(i)
|
||||
of '"':
|
||||
inc(i)
|
||||
if not inQuote: inQuote = true
|
||||
elif c[i] == '"':
|
||||
elif c[i] == '"':
|
||||
a.add(c[i])
|
||||
inc(i)
|
||||
else:
|
||||
inQuote = false
|
||||
break
|
||||
of ' ', '\t':
|
||||
of ' ', '\t':
|
||||
if not inQuote: break
|
||||
a.add(c[i])
|
||||
inc(i)
|
||||
|
|
@ -1306,86 +1403,6 @@ proc parseCmdLine*(c: string): seq[string] {.
|
|||
add(a, c[i])
|
||||
inc(i)
|
||||
add(result, a)
|
||||
|
||||
type
|
||||
TFilePermission* = enum ## file access permission; modelled after UNIX
|
||||
fpUserExec, ## execute access for the file owner
|
||||
fpUserWrite, ## write access for the file owner
|
||||
fpUserRead, ## read access for the file owner
|
||||
fpGroupExec, ## execute access for the group
|
||||
fpGroupWrite, ## write access for the group
|
||||
fpGroupRead, ## read access for the group
|
||||
fpOthersExec, ## execute access for others
|
||||
fpOthersWrite, ## write access for others
|
||||
fpOthersRead ## read access for others
|
||||
|
||||
proc getFilePermissions*(filename: string): set[TFilePermission] {.
|
||||
rtl, extern: "nos$1", tags: [FReadDir].} =
|
||||
## retrieves file permissions for `filename`. `OSError` is raised in case of
|
||||
## an error. On Windows, only the ``readonly`` flag is checked, every other
|
||||
## permission is available in any case.
|
||||
when defined(posix):
|
||||
var a: TStat
|
||||
if stat(filename, a) < 0'i32: OSError(OSLastError())
|
||||
result = {}
|
||||
if (a.st_mode and S_IRUSR) != 0'i32: result.incl(fpUserRead)
|
||||
if (a.st_mode and S_IWUSR) != 0'i32: result.incl(fpUserWrite)
|
||||
if (a.st_mode and S_IXUSR) != 0'i32: result.incl(fpUserExec)
|
||||
|
||||
if (a.st_mode and S_IRGRP) != 0'i32: result.incl(fpGroupRead)
|
||||
if (a.st_mode and S_IWGRP) != 0'i32: result.incl(fpGroupWrite)
|
||||
if (a.st_mode and S_IXGRP) != 0'i32: result.incl(fpGroupExec)
|
||||
|
||||
if (a.st_mode and S_IROTH) != 0'i32: result.incl(fpOthersRead)
|
||||
if (a.st_mode and S_IWOTH) != 0'i32: result.incl(fpOthersWrite)
|
||||
if (a.st_mode and S_IXOTH) != 0'i32: result.incl(fpOthersExec)
|
||||
else:
|
||||
when useWinUnicode:
|
||||
wrapUnary(res, GetFileAttributesW, filename)
|
||||
else:
|
||||
var res = GetFileAttributesA(filename)
|
||||
if res == -1'i32: OSError(OSLastError())
|
||||
if (res and FILE_ATTRIBUTE_READONLY) != 0'i32:
|
||||
result = {fpUserExec, fpUserRead, fpGroupExec, fpGroupRead,
|
||||
fpOthersExec, fpOthersRead}
|
||||
else:
|
||||
result = {fpUserExec..fpOthersRead}
|
||||
|
||||
proc setFilePermissions*(filename: string, permissions: set[TFilePermission]) {.
|
||||
rtl, extern: "nos$1", tags: [FWriteDir].} =
|
||||
## sets the file permissions for `filename`. `OSError` is raised in case of
|
||||
## an error. On Windows, only the ``readonly`` flag is changed, depending on
|
||||
## ``fpUserWrite``.
|
||||
when defined(posix):
|
||||
var p = 0'i32
|
||||
if fpUserRead in permissions: p = p or S_IRUSR
|
||||
if fpUserWrite in permissions: p = p or S_IWUSR
|
||||
if fpUserExec in permissions: p = p or S_IXUSR
|
||||
|
||||
if fpGroupRead in permissions: p = p or S_IRGRP
|
||||
if fpGroupWrite in permissions: p = p or S_IWGRP
|
||||
if fpGroupExec in permissions: p = p or S_IXGRP
|
||||
|
||||
if fpOthersRead in permissions: p = p or S_IROTH
|
||||
if fpOthersWrite in permissions: p = p or S_IWOTH
|
||||
if fpOthersExec in permissions: p = p or S_IXOTH
|
||||
|
||||
if chmod(filename, p) != 0: OSError(OSLastError())
|
||||
else:
|
||||
when useWinUnicode:
|
||||
wrapUnary(res, GetFileAttributesW, filename)
|
||||
else:
|
||||
var res = GetFileAttributesA(filename)
|
||||
if res == -1'i32: OSError(OSLastError())
|
||||
if fpUserWrite in permissions:
|
||||
res = res and not FILE_ATTRIBUTE_READONLY
|
||||
else:
|
||||
res = res or FILE_ATTRIBUTE_READONLY
|
||||
when useWinUnicode:
|
||||
wrapBinary(res2, SetFileAttributesW, filename, res)
|
||||
else:
|
||||
var res2 = SetFileAttributesA(filename, res)
|
||||
if res2 == - 1'i32: OSError(OSLastError())
|
||||
|
||||
proc copyFileWithPermissions*(source, dest: string,
|
||||
ignorePermissionErrors = true) =
|
||||
|
|
@ -1407,19 +1424,19 @@ proc copyFileWithPermissions*(source, dest: string,
|
|||
if not ignorePermissionErrors:
|
||||
raise
|
||||
|
||||
proc inclFilePermissions*(filename: string,
|
||||
proc inclFilePermissions*(filename: string,
|
||||
permissions: set[TFilePermission]) {.
|
||||
rtl, extern: "nos$1", tags: [FReadDir, FWriteDir].} =
|
||||
## a convenience procedure for:
|
||||
## a convenience procedure for:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## setFilePermissions(filename, getFilePermissions(filename)+permissions)
|
||||
setFilePermissions(filename, getFilePermissions(filename)+permissions)
|
||||
|
||||
proc exclFilePermissions*(filename: string,
|
||||
proc exclFilePermissions*(filename: string,
|
||||
permissions: set[TFilePermission]) {.
|
||||
rtl, extern: "nos$1", tags: [FReadDir, FWriteDir].} =
|
||||
## a convenience procedure for:
|
||||
## a convenience procedure for:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## setFilePermissions(filename, getFilePermissions(filename)-permissions)
|
||||
|
|
@ -1459,7 +1476,7 @@ when defined(windows):
|
|||
if isNil(ownArgv): ownArgv = parseCmdLine($getCommandLine())
|
||||
result = ownArgv.len-1
|
||||
|
||||
proc paramStr*(i: int): TaintedString {.rtl, extern: "nos$1",
|
||||
proc paramStr*(i: int): TaintedString {.rtl, extern: "nos$1",
|
||||
tags: [FReadIO].} =
|
||||
## Returns the `i`-th `command line argument`:idx: given to the
|
||||
## application.
|
||||
|
|
@ -1481,6 +1498,12 @@ elif not defined(createNimRtl):
|
|||
|
||||
proc paramCount*(): int {.tags: [FReadIO].} = return cmdCount-1
|
||||
|
||||
when defined(paramCount):
|
||||
proc commandLineParams*(): seq[TaintedString] =
|
||||
result = @[]
|
||||
for i in 1..paramCount():
|
||||
result.add(paramStr(i))
|
||||
|
||||
when defined(linux) or defined(solaris) or defined(bsd) or defined(aix):
|
||||
proc getApplAux(procPath: string): string =
|
||||
result = newString(256)
|
||||
|
|
@ -1494,7 +1517,7 @@ when defined(macosx):
|
|||
type
|
||||
cuint32* {.importc: "unsigned int", nodecl.} = int
|
||||
## This is the same as the type ``uint32_t`` in *C*.
|
||||
|
||||
|
||||
# a really hacky solution: since we like to include 2 headers we have to
|
||||
# define two procs which in reality are the same
|
||||
proc getExecPath1(c: cstring, size: var cuint32) {.
|
||||
|
|
@ -1553,13 +1576,13 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [FReadIO].} =
|
|||
|
||||
proc getApplicationFilename*(): string {.rtl, extern: "nos$1", deprecated.} =
|
||||
## Returns the filename of the application's executable.
|
||||
## **Deprecated since version 0.8.12**: use ``getAppFilename``
|
||||
## **Deprecated since version 0.8.12**: use ``getAppFilename``
|
||||
## instead.
|
||||
result = getAppFilename()
|
||||
|
||||
proc getApplicationDir*(): string {.rtl, extern: "nos$1", deprecated.} =
|
||||
## Returns the directory of the application's executable.
|
||||
## **Deprecated since version 0.8.12**: use ``getAppDir``
|
||||
## **Deprecated since version 0.8.12**: use ``getAppDir``
|
||||
## instead.
|
||||
result = splitFile(getAppFilename()).dir
|
||||
|
||||
|
|
@ -1580,7 +1603,7 @@ proc sleep*(milsecs: int) {.rtl, extern: "nos$1", tags: [FTime].} =
|
|||
|
||||
proc getFileSize*(file: string): biggestInt {.rtl, extern: "nos$1",
|
||||
tags: [FReadIO].} =
|
||||
## returns the file size of `file`. Can raise ``EOS``.
|
||||
## returns the file size of `file`. Can raise ``EOS``.
|
||||
when defined(windows):
|
||||
var a: TWin32FindData
|
||||
var resA = findfirstFile(file, a)
|
||||
|
|
@ -1589,20 +1612,20 @@ proc getFileSize*(file: string): biggestInt {.rtl, extern: "nos$1",
|
|||
findclose(resA)
|
||||
else:
|
||||
var f: TFile
|
||||
if open(f, file):
|
||||
if open(f, file):
|
||||
result = getFileSize(f)
|
||||
close(f)
|
||||
else: OSError(OSLastError())
|
||||
|
||||
proc findExe*(exe: string): string {.tags: [FReadDir, FReadEnv].} =
|
||||
proc findExe*(exe: string): string {.tags: [FReadDir, FReadEnv].} =
|
||||
## Searches for `exe` in the current working directory and then
|
||||
## in directories listed in the ``PATH`` environment variable.
|
||||
## Returns "" if the `exe` cannot be found. On DOS-like platforms, `exe`
|
||||
## in directories listed in the ``PATH`` environment variable.
|
||||
## Returns "" if the `exe` cannot be found. On DOS-like platforms, `exe`
|
||||
## is added an ``.exe`` file extension if it has no extension.
|
||||
result = addFileExt(exe, os.exeExt)
|
||||
if ExistsFile(result): return
|
||||
var path = string(os.getEnv("PATH"))
|
||||
for candidate in split(path, pathSep):
|
||||
for candidate in split(path, pathSep):
|
||||
var x = candidate / result
|
||||
if ExistsFile(x): return x
|
||||
result = ""
|
||||
|
|
@ -1629,4 +1652,3 @@ proc expandTilde*(path: string): string =
|
|||
result = path
|
||||
|
||||
{.pop.}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,10 @@
|
|||
## This module provides the standard Nimrod command line parser.
|
||||
## It supports one convenience iterator over all command line options and some
|
||||
## lower-level features.
|
||||
|
||||
##
|
||||
## DEPRECATED. Use parseopt2 instead as this version has issues with spaces
|
||||
## in arguments.
|
||||
{.deprecated.}
|
||||
{.push debugger: off.}
|
||||
|
||||
include "system/inclrtl"
|
||||
|
|
|
|||
148
lib/pure/parseopt2.nim
Normal file
148
lib/pure/parseopt2.nim
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# (c) Copyright 2012 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## This module provides the standard Nimrod command line parser.
|
||||
## It supports one convenience iterator over all command line options and some
|
||||
## lower-level features.
|
||||
##
|
||||
## Supported syntax:
|
||||
##
|
||||
## 1. short options - ``-abcd``, where a, b, c, d are names
|
||||
## 2. long option - ``--foo:bar``, ``--foo=bar`` or ``--foo``
|
||||
## 3. argument - everything else
|
||||
|
||||
{.push debugger: off.}
|
||||
|
||||
include "system/inclrtl"
|
||||
|
||||
import
|
||||
os, strutils
|
||||
|
||||
type
|
||||
TCmdLineKind* = enum ## the detected command line token
|
||||
cmdEnd, ## end of command line reached
|
||||
cmdArgument, ## argument detected
|
||||
cmdLongOption, ## a long option ``--option`` detected
|
||||
cmdShortOption ## a short option ``-c`` detected
|
||||
TOptParser* =
|
||||
object of TObject ## this object implements the command line parser
|
||||
cmd: seq[string]
|
||||
pos: int
|
||||
remainingShortOptions: string
|
||||
kind*: TCmdLineKind ## the dected command line token
|
||||
key*, val*: TaintedString ## key and value pair; ``key`` is the option
|
||||
## or the argument, ``value`` is not "" if
|
||||
## the option was given a value
|
||||
|
||||
proc initOptParser*(cmdline: seq[string]): TOptParser {.rtl.} =
|
||||
## Initalizes option parses with cmdline. cmdline should not contain
|
||||
## argument 0 - program name.
|
||||
## If cmdline == nil default to current command line arguments.
|
||||
result.remainingShortOptions = ""
|
||||
when not defined(createNimRtl):
|
||||
if cmdline == nil:
|
||||
result.cmd = commandLineParams()
|
||||
return
|
||||
else:
|
||||
assert cmdline != nil, "Cannot determine command line arguments."
|
||||
|
||||
result.cmd = @cmdline
|
||||
|
||||
proc initOptParser*(cmdline: string): TOptParser {.rtl, deprecated.} =
|
||||
## Initalizes option parses with cmdline. Splits cmdline in on spaces
|
||||
## and calls initOptParser(openarray[string])
|
||||
## Do not use.
|
||||
if cmdline == "": # backward compatibilty
|
||||
return initOptParser(seq[string](nil))
|
||||
else:
|
||||
return initOptParser(cmdline.split)
|
||||
|
||||
when not defined(createNimRtl):
|
||||
proc initOptParser*(): TOptParser =
|
||||
## Initializes option parser from current command line arguments.
|
||||
return initOptParser(commandLineParams())
|
||||
|
||||
proc next*(p: var TOptParser) {.rtl, extern: "npo$1".}
|
||||
|
||||
proc nextOption(p: var TOptParser, token: string, allowEmpty: bool) =
|
||||
for splitchar in [':', '=']:
|
||||
if splitchar in token:
|
||||
let pos = token.find(splitchar)
|
||||
p.key = token[0..pos-1]
|
||||
p.val = token[pos+1..token.len-1]
|
||||
return
|
||||
|
||||
p.key = token
|
||||
if allowEmpty:
|
||||
p.val = ""
|
||||
else:
|
||||
p.remainingShortOptions = token[0..token.len-1]
|
||||
p.next()
|
||||
|
||||
proc next(p: var TOptParser) =
|
||||
if p.remainingShortOptions.len != 0:
|
||||
p.kind = cmdShortOption
|
||||
p.key = TaintedString(p.remainingShortOptions[0..0])
|
||||
p.val = ""
|
||||
p.remainingShortOptions = p.remainingShortOptions[1..p.remainingShortOptions.len-1]
|
||||
return
|
||||
|
||||
if p.pos >= p.cmd.len:
|
||||
p.kind = cmdEnd
|
||||
return
|
||||
|
||||
let token = p.cmd[p.pos]
|
||||
p.pos += 1
|
||||
|
||||
if token.startswith("--"):
|
||||
p.kind = cmdLongOption
|
||||
nextOption(p, token[2..token.len-1], allowEmpty=true)
|
||||
elif token.startswith("-"):
|
||||
p.kind = cmdShortOption
|
||||
nextOption(p, token[1..token.len-1], allowEmpty=true)
|
||||
else:
|
||||
p.kind = cmdArgument
|
||||
p.key = token
|
||||
p.val = ""
|
||||
|
||||
proc cmdLineRest*(p: TOptParser): TaintedString {.rtl, extern: "npo$1", deprecated.} =
|
||||
## Returns part of command line string that has not been parsed yet.
|
||||
## Do not use - does not correctly handle whitespace.
|
||||
return p.cmd[p.pos..p.cmd.len-1].join(" ")
|
||||
|
||||
type
|
||||
TGetoptResult* = tuple[kind: TCmdLineKind, key, val: TaintedString]
|
||||
|
||||
when defined(paramCount):
|
||||
iterator getopt*(): TGetoptResult =
|
||||
## This is an convenience iterator for iterating over the command line.
|
||||
## This uses the TOptParser object. Example:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## var
|
||||
## filename = ""
|
||||
## for kind, key, val in getopt():
|
||||
## case kind
|
||||
## of cmdArgument:
|
||||
## filename = key
|
||||
## of cmdLongOption, cmdShortOption:
|
||||
## case key
|
||||
## of "help", "h": writeHelp()
|
||||
## of "version", "v": writeVersion()
|
||||
## of cmdEnd: assert(false) # cannot happen
|
||||
## if filename == "":
|
||||
## # no filename has been given, so we show the help:
|
||||
## writeHelp()
|
||||
var p = initOptParser()
|
||||
while true:
|
||||
next(p)
|
||||
if p.kind == cmdEnd: break
|
||||
yield (p.kind, p.key, p.val)
|
||||
|
||||
{.pop.}
|
||||
|
|
@ -98,8 +98,12 @@ template fail* =
|
|||
|
||||
when not defined(ECMAScript):
|
||||
if AbortOnError: quit(1)
|
||||
|
||||
TestStatusIMPL = FAILED
|
||||
|
||||
when defined(TestStatusIMPL):
|
||||
TestStatusIMPL = FAILED
|
||||
else:
|
||||
program_result += 1
|
||||
|
||||
checkpoints = @[]
|
||||
|
||||
macro check*(conditions: stmt): stmt {.immediate.} =
|
||||
|
|
@ -111,7 +115,8 @@ macro check*(conditions: stmt): stmt {.immediate.} =
|
|||
counter = 0
|
||||
|
||||
template asgn(a, value: expr): stmt =
|
||||
let a = value
|
||||
var a = value # XXX: we need "var: var" here in order to
|
||||
# preserve the semantics of var params
|
||||
|
||||
template print(name, value: expr): stmt =
|
||||
when compiles(string($value)):
|
||||
|
|
@ -146,7 +151,8 @@ macro check*(conditions: stmt): stmt {.immediate.} =
|
|||
of nnkStmtList:
|
||||
result = newNimNode(nnkStmtList)
|
||||
for i in countup(0, checked.len - 1):
|
||||
result.add(newCall(!"check", checked[i]))
|
||||
if checked[i].kind != nnkCommentStmt:
|
||||
result.add(newCall(!"check", checked[i]))
|
||||
|
||||
else:
|
||||
template rewrite(Exp, lineInfoLit: expr, expLit: string): stmt =
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue