diff options
author | Araq <rumpf_a@web.de> | 2013-12-13 15:58:03 +0100 |
---|---|---|
committer | Araq <rumpf_a@web.de> | 2013-12-13 15:58:03 +0100 |
commit | 8c553fa8a2e9476c67c6348bf64c02ea0f19a679 (patch) | |
tree | ce32cf3cd815f4dbb7e5f0f00942f43e1a7871c0 /lib/pure | |
parent | 328f1932925889d5bb7f91c68fb1504b9b26ba8c (diff) | |
parent | 01661daf76daa713d25d06d202f83c842a6f1fe3 (diff) | |
download | Nim-8c553fa8a2e9476c67c6348bf64c02ea0f19a679.tar.gz |
Merge branch 'master' into vm2
Diffstat (limited to 'lib/pure')
-rw-r--r-- | lib/pure/os.nim | 300 | ||||
-rw-r--r-- | lib/pure/parseopt.nim | 5 | ||||
-rw-r--r-- | lib/pure/parseopt2.nim | 148 | ||||
-rw-r--r-- | lib/pure/unittest.nim | 14 |
4 files changed, 323 insertions, 144 deletions
diff --git a/lib/pure/os.nim b/lib/pure/os.nim index a39ca7b83..71639d821 100644 --- a/lib/pure/os.nim +++ b/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.} - diff --git a/lib/pure/parseopt.nim b/lib/pure/parseopt.nim index 6d9d16bc9..fa704bbce 100644 --- a/lib/pure/parseopt.nim +++ b/lib/pure/parseopt.nim @@ -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" diff --git a/lib/pure/parseopt2.nim b/lib/pure/parseopt2.nim new file mode 100644 index 000000000..5e79d8a18 --- /dev/null +++ b/lib/pure/parseopt2.nim @@ -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.} diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim index 71f4d498b..f847d24f4 100644 --- a/lib/pure/unittest.nim +++ b/lib/pure/unittest.nim @@ -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 = |