summary refs log tree commit diff stats
path: root/lib/pure
diff options
context:
space:
mode:
authorAraq <rumpf_a@web.de>2013-12-13 15:58:03 +0100
committerAraq <rumpf_a@web.de>2013-12-13 15:58:03 +0100
commit8c553fa8a2e9476c67c6348bf64c02ea0f19a679 (patch)
treece32cf3cd815f4dbb7e5f0f00942f43e1a7871c0 /lib/pure
parent328f1932925889d5bb7f91c68fb1504b9b26ba8c (diff)
parent01661daf76daa713d25d06d202f83c842a6f1fe3 (diff)
downloadNim-8c553fa8a2e9476c67c6348bf64c02ea0f19a679.tar.gz
Merge branch 'master' into vm2
Diffstat (limited to 'lib/pure')
-rw-r--r--lib/pure/os.nim300
-rw-r--r--lib/pure/parseopt.nim5
-rw-r--r--lib/pure/parseopt2.nim148
-rw-r--r--lib/pure/unittest.nim14
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 =