summary refs log tree commit diff stats
path: root/lib/pure/os.nim
diff options
context:
space:
mode:
Diffstat (limited to 'lib/pure/os.nim')
-rw-r--r--lib/pure/os.nim819
1 files changed, 466 insertions, 353 deletions
diff --git a/lib/pure/os.nim b/lib/pure/os.nim
index a39ca7b83..bb70f28b6 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
@@ -93,11 +93,11 @@ when defined(Nimdoc): # only for proper documentation:
 
 elif defined(macos):
   const
-    curdir* = ':'
-    pardir* = "::"
-    dirsep* = ':'
-    altsep* = dirsep
-    pathsep* = ','
+    CurDir* = ':'
+    ParDir* = "::"
+    DirSep* = ':'
+    AltSep* = Dirsep
+    PathSep* = ','
     FileSystemCaseSensitive* = false
     ExeExt* = ""
     ScriptExt* = ""
@@ -123,84 +123,88 @@ elif defined(macos):
   #  grandparent etc.
 elif doslike:
   const
-    curdir* = '.'
-    pardir* = ".."
-    dirsep* = '\\' # seperator within paths
-    altsep* = '/'
-    pathSep* = ';' # seperator between paths
+    CurDir* = '.'
+    ParDir* = ".."
+    DirSep* = '\\' # seperator within paths
+    AltSep* = '/'
+    PathSep* = ';' # seperator between paths
     FileSystemCaseSensitive* = false
     ExeExt* = "exe"
     ScriptExt* = "bat"
     DynlibFormat* = "$1.dll"
 elif defined(PalmOS) or defined(MorphOS):
   const
-    dirsep* = '/'
-    altsep* = dirsep
+    DirSep* = '/'
+    AltSep* = Dirsep
     PathSep* = ';'
-    pardir* = ".."
+    ParDir* = ".."
     FileSystemCaseSensitive* = false
     ExeExt* = ""
     ScriptExt* = ""
     DynlibFormat* = "$1.prc"
 elif defined(RISCOS):
   const
-    dirsep* = '.'
-    altsep* = '.'
-    pardir* = ".." # is this correct?
-    pathSep* = ','
+    DirSep* = '.'
+    AltSep* = '.'
+    ParDir* = ".." # is this correct?
+    PathSep* = ','
     FileSystemCaseSensitive* = true
     ExeExt* = ""
     ScriptExt* = ""
     DynlibFormat* = "lib$1.so"
 else: # UNIX-like operating system
   const
-    curdir* = '.'
-    pardir* = ".."
-    dirsep* = '/'
-    altsep* = dirsep
-    pathSep* = ':'
+    CurDir* = '.'
+    ParDir* = ".."
+    DirSep* = '/'
+    AltSep* = DirSep
+    PathSep* = ':'
     FileSystemCaseSensitive* = true
     ExeExt* = ""
     ScriptExt* = ""
     DynlibFormat* = when defined(macosx): "lib$1.dylib" else: "lib$1.so"
 
 when defined(posix):
-  var
-    pathMax {.importc: "PATH_MAX", header: "<stdlib.h>".}: cint
+  when NoFakeVars:
+    const pathMax = 5000 # doesn't matter really. The concept of PATH_MAX
+                         # doesn't work anymore on modern OSes.
+  else:
+    var
+      pathMax {.importc: "PATH_MAX", header: "<stdlib.h>".}: cint
 
 const
   ExtSep* = '.'
     ## The character which separates the base filename from the extension;
     ## for example, the '.' in ``os.nim``.
 
-proc OSErrorMsg*(): string {.rtl, extern: "nos$1", deprecated.} =
+proc osErrorMsg*(): string {.rtl, extern: "nos$1", deprecated.} =
   ## Retrieves the operating system's error flag, ``errno``.
   ## On Windows ``GetLastError`` is checked before ``errno``.
   ## Returns "" if no error occured.
   ##
   ## **Deprecated since version 0.9.4**: use the other ``OSErrorMsg`` proc.
-  
+
   result = ""
   when defined(Windows):
-    var err = GetLastError()
+    var err = getLastError()
     if err != 0'i32:
       when useWinUnicode:
-        var msgbuf: widecstring
-        if FormatMessageW(0x00000100 or 0x00001000 or 0x00000200,
+        var msgbuf: WideCString
+        if formatMessageW(0x00000100 or 0x00001000 or 0x00000200,
                           nil, err, 0, addr(msgbuf), 0, nil) != 0'i32:
           result = $msgbuf
-          if msgbuf != nil: LocalFree(cast[pointer](msgbuf))
+          if msgbuf != nil: localFree(cast[pointer](msgbuf))
       else:
         var msgbuf: cstring
-        if FormatMessageA(0x00000100 or 0x00001000 or 0x00000200,
+        if formatMessageA(0x00000100 or 0x00001000 or 0x00000200,
                           nil, err, 0, addr(msgbuf), 0, nil) != 0'i32:
           result = $msgbuf
-          if msgbuf != nil: LocalFree(msgbuf)
+          if msgbuf != nil: localFree(msgbuf)
   if errno != 0'i32:
     result = $os.strerror(errno)
 
 {.push warning[deprecated]: off.}
-proc OSError*(msg: string = "") {.noinline, rtl, extern: "nos$1", deprecated.} =
+proc osError*(msg: string = "") {.noinline, rtl, extern: "nos$1", deprecated.} =
   ## raises an EOS exception with the given message ``msg``.
   ## If ``msg == ""``, the operating system's error flag
   ## (``errno``) is converted to a readable error message. On Windows
@@ -209,7 +213,7 @@ proc OSError*(msg: string = "") {.noinline, rtl, extern: "nos$1", deprecated.} =
   ##
   ## **Deprecated since version 0.9.4**: use the other ``OSError`` proc.
   if len(msg) == 0:
-    var m = OSErrorMsg()
+    var m = osErrorMsg()
     raise newException(EOS, if m.len > 0: m else: "unknown OS error")
   else:
     raise newException(EOS, msg)
@@ -218,7 +222,7 @@ proc OSError*(msg: string = "") {.noinline, rtl, extern: "nos$1", deprecated.} =
 proc `==`*(err1, err2: TOSErrorCode): bool {.borrow.}
 proc `$`*(err: TOSErrorCode): string {.borrow.}
 
-proc OSErrorMsg*(errorCode: TOSErrorCode): string =
+proc osErrorMsg*(errorCode: TOSErrorCode): string =
   ## Converts an OS error code into a human readable string.
   ##
   ## The error code can be retrieved using the ``OSLastError`` proc.
@@ -233,22 +237,22 @@ proc OSErrorMsg*(errorCode: TOSErrorCode): string =
   when defined(Windows):
     if errorCode != TOSErrorCode(0'i32):
       when useWinUnicode:
-        var msgbuf: widecstring
-        if FormatMessageW(0x00000100 or 0x00001000 or 0x00000200,
+        var msgbuf: WideCString
+        if formatMessageW(0x00000100 or 0x00001000 or 0x00000200,
                         nil, errorCode.int32, 0, addr(msgbuf), 0, nil) != 0'i32:
           result = $msgbuf
-          if msgbuf != nil: LocalFree(cast[pointer](msgbuf))
+          if msgbuf != nil: localFree(cast[pointer](msgbuf))
       else:
         var msgbuf: cstring
-        if FormatMessageA(0x00000100 or 0x00001000 or 0x00000200,
+        if formatMessageA(0x00000100 or 0x00001000 or 0x00000200,
                         nil, errorCode.int32, 0, addr(msgbuf), 0, nil) != 0'i32:
           result = $msgbuf
-          if msgbuf != nil: LocalFree(msgbuf)
+          if msgbuf != nil: localFree(msgbuf)
   else:
     if errorCode != TOSErrorCode(0'i32):
       result = $os.strerror(errorCode.int32)
 
-proc OSError*(errorCode: TOSErrorCode) =
+proc osError*(errorCode: TOSErrorCode) =
   ## Raises an ``EOS`` exception. The ``errorCode`` will determine the
   ## message, ``OSErrorMsg`` will be used to get this message.
   ##
@@ -256,14 +260,14 @@ proc OSError*(errorCode: TOSErrorCode) =
   ##
   ## If the error code is ``0`` or an error message could not be retrieved,
   ## the message ``unknown OS error`` will be used.
-  let msg = OSErrorMsg(errorCode)
+  let msg = osErrorMsg(errorCode)
   if msg == "":
     raise newException(EOS, "unknown OS error")
   else:
     raise newException(EOS, msg)
 
 {.push stackTrace:off.}
-proc OSLastError*(): TOSErrorCode =
+proc osLastError*(): TOSErrorCode =
   ## Retrieves the last operating system error code.
   ##
   ## This procedure is useful in the event when an OS call fails. In that case
@@ -276,19 +280,25 @@ 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())
+    result = TOSErrorCode(getLastError())
   else:
     result = TOSErrorCode(errno)
 {.pop.}
 
-proc UnixToNativePath*(path: string): string {.
+proc unixToNativePath*(path: string, drive=""): string {.
   noSideEffect, rtl, extern: "nos$1".} =
   ## Converts an UNIX-like path to a native one.
   ##
   ## On an UNIX system this does nothing. Else it converts
   ## '/', '.', '..' to the appropriate things.
+  ##
+  ## On systems with a concept of "drives", `drive` is used to determine
+  ## which drive label to use during absolute path conversion.
+  ## `drive` defaults to the drive of the current working directory, and is
+  ## ignored on systems that do not have a concept of "drives".
+
   when defined(unix):
     result = path
   else:
@@ -296,15 +306,18 @@ proc UnixToNativePath*(path: string): string {.
     if path[0] == '/':
       # an absolute path
       when doslike:
-        result = r"C:\"
+        if drive != "":
+          result = drive & ":" & DirSep
+        else:
+          result = $DirSep
       elif defined(macos):
         result = "" # must not start with ':'
       else:
-        result = $dirSep
+        result = $DirSep
       start = 1
     elif path[0] == '.' and path[1] == '/':
       # current directory
-      result = $curdir
+      result = $CurDir
       start = 2
     else:
       result = ""
@@ -318,12 +331,12 @@ proc UnixToNativePath*(path: string): string {.
           if result[high(result)] == ':':
             add result, ':'
           else:
-            add result, pardir
+            add result, ParDir
         else:
-          add result, pardir & dirSep
+          add result, ParDir & DirSep
         inc(i, 3)
       elif path[i] == '/':
-        add result, dirSep
+        add result, DirSep
         inc(i)
       else:
         add result, path[i]
@@ -336,34 +349,33 @@ when defined(windows):
 
     template wrapBinary(varname, winApiProc, arg, arg2: expr) {.immediate.} =
       var varname = winApiProc(newWideCString(arg), arg2)
-    proc FindFirstFile(a: string, b: var TWIN32_FIND_DATA): THandle =
-      result = FindFirstFileW(newWideCString(a), b)
-    template FindNextFile(a, b: expr): expr = FindNextFileW(a, b)
+    proc findFirstFile(a: string, b: var TWIN32_FIND_DATA): THandle =
+      result = findFirstFileW(newWideCString(a), b)
+    template findNextFile(a, b: expr): expr = findNextFileW(a, b)
     template getCommandLine(): expr = getCommandLineW()
 
-    proc skipFindData(f: TWIN32_FIND_DATA): bool {.inline.} =
-      result = f.cFilename[0].int == ord('.')
-
     template getFilename(f: expr): expr =
       $cast[WideCString](addr(f.cFilename[0]))
   else:
-    template FindFirstFile(a, b: expr): expr = FindFirstFileA(a, b)
-    template FindNextFile(a, b: expr): expr = FindNextFileA(a, b)
+    template findFirstFile(a, b: expr): expr = findFirstFileA(a, b)
+    template findNextFile(a, b: expr): expr = findNextFileA(a, b)
     template getCommandLine(): expr = getCommandLineA()
 
-    proc skipFindData(f: TWIN32_FIND_DATA): bool {.inline.} =
-      result = f.cFilename[0] == '.'
-
     template getFilename(f: expr): expr = $f.cFilename
-    
-proc existsFile*(filename: string): bool {.rtl, extern: "nos$1", 
+
+  proc skipFindData(f: TWIN32_FIND_DATA): bool {.inline.} =
+    const dot = ord('.')
+    result = f.cFileName[0].int == dot and(f.cFileName[1].int == 0 or
+             f.cFileName[1].int == dot and f.cFileName[2].int == 0)
+
+proc existsFile*(filename: string): bool {.rtl, extern: "nos$1",
                                           tags: [FReadDir].} =
   ## Returns true if the file exists, false otherwise.
   when defined(windows):
     when useWinUnicode:
-      wrapUnary(a, GetFileAttributesW, filename)
+      wrapUnary(a, getFileAttributesW, filename)
     else:
-      var a = GetFileAttributesA(filename)
+      var a = getFileAttributesA(filename)
     if a != -1'i32:
       result = (a and FILE_ATTRIBUTE_DIRECTORY) == 0'i32
   else:
@@ -375,53 +387,76 @@ proc existsDir*(dir: string): bool {.rtl, extern: "nos$1", tags: [FReadDir].} =
   ## is returned.
   when defined(windows):
     when useWinUnicode:
-      wrapUnary(a, GetFileAttributesW, dir)
+      wrapUnary(a, getFileAttributesW, dir)
     else:
-      var a = GetFileAttributesA(dir)
+      var a = getFileAttributesA(dir)
     if a != -1'i32:
       result = (a and FILE_ATTRIBUTE_DIRECTORY) != 0'i32
   else:
     var res: TStat
     return stat(dir, res) >= 0'i32 and S_ISDIR(res.st_mode)
 
+proc symlinkExists*(link: string): bool {.rtl, extern: "nos$1",
+                                          tags: [FReadDir].} =
+  ## Returns true iff the symlink `link` exists. Will return true
+  ## regardless of whether the link points to a directory or file.
+  when defined(windows):
+    when useWinUnicode:
+      wrapUnary(a, getFileAttributesW, link)
+    else:
+      var a = getFileAttributesA(link)
+    if a != -1'i32:
+      result = (a and FILE_ATTRIBUTE_REPARSE_POINT) != 0'i32
+  else:
+    var res: TStat
+    return lstat(link, res) >= 0'i32 and S_ISLNK(res.st_mode)
+
+proc fileExists*(filename: string): bool {.inline.} =
+  ## Synonym for existsFile
+  existsFile(filename)
+
+proc dirExists*(dir: string): bool {.inline.} =
+  ## Synonym for existsDir
+  existsDir(dir)
+
 proc getLastModificationTime*(file: string): TTime {.rtl, extern: "nos$1".} =
   ## Returns the `file`'s last modification time.
   when defined(posix):
     var res: TStat
-    if stat(file, res) < 0'i32: OSError(OSLastError())
+    if stat(file, res) < 0'i32: osError(osLastError())
     return res.st_mtime
   else:
-    var f: TWIN32_Find_Data
-    var h = findfirstFile(file, f)
-    if h == -1'i32: OSError(OSLastError())
+    var f: TWIN32_FIND_DATA
+    var h = findFirstFile(file, f)
+    if h == -1'i32: osError(osLastError())
     result = winTimeToUnixTime(rdFileTime(f.ftLastWriteTime))
-    findclose(h)
+    findClose(h)
 
 proc getLastAccessTime*(file: string): TTime {.rtl, extern: "nos$1".} =
   ## Returns the `file`'s last read or write access time.
   when defined(posix):
     var res: TStat
-    if stat(file, res) < 0'i32: OSError(OSLastError())
+    if stat(file, res) < 0'i32: osError(osLastError())
     return res.st_atime
   else:
-    var f: TWIN32_Find_Data
-    var h = findfirstFile(file, f)
-    if h == -1'i32: OSError(OSLastError())
+    var f: TWIN32_FIND_DATA
+    var h = findFirstFile(file, f)
+    if h == -1'i32: osError(osLastError())
     result = winTimeToUnixTime(rdFileTime(f.ftLastAccessTime))
-    findclose(h)
+    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
-    if stat(file, res) < 0'i32: OSError(OSLastError())
+    if stat(file, res) < 0'i32: osError(osLastError())
     return res.st_ctime
   else:
-    var f: TWIN32_Find_Data
-    var h = findfirstFile(file, f)
-    if h == -1'i32: OSError(OSLastError())
+    var f: TWIN32_FIND_DATA
+    var h = findFirstFile(file, f)
+    if h == -1'i32: osError(osLastError())
     result = winTimeToUnixTime(rdFileTime(f.ftCreationTime))
-    findclose(h)
+    findClose(h)
 
 proc fileNewer*(a, b: string): bool {.rtl, extern: "nos$1".} =
   ## Returns true if the file `a` is newer than file `b`, i.e. if `a`'s
@@ -434,40 +469,41 @@ proc getCurrentDir*(): string {.rtl, extern: "nos$1", tags: [].} =
   when defined(windows):
     when useWinUnicode:
       var res = newWideCString("", bufsize)
-      var L = GetCurrentDirectoryW(bufsize, res)
-      if L == 0'i32: OSError(OSLastError())
+      var L = getCurrentDirectoryW(bufsize, res)
+      if L == 0'i32: osError(osLastError())
       result = res$L
     else:
       result = newString(bufsize)
-      var L = GetCurrentDirectoryA(bufsize, result)
-      if L == 0'i32: OSError(OSLastError())
+      var L = getCurrentDirectoryA(bufsize, result)
+      if L == 0'i32: osError(osLastError())
       setLen(result, L)
   else:
     result = newString(bufsize)
     if getcwd(result, bufsize) != nil:
-      setlen(result, c_strlen(result))
+      setLen(result, c_strlen(result))
     else:
-      OSError(OSLastError())
+      osError(osLastError())
 
 proc setCurrentDir*(newDir: string) {.inline, tags: [].} =
   ## Sets the `current working directory`:idx:; `EOS` is raised if
   ## `newDir` cannot been set.
   when defined(Windows):
     when useWinUnicode:
-      if SetCurrentDirectoryW(newWideCString(newDir)) == 0'i32: OSError(OSLastError())
+      if setCurrentDirectoryW(newWideCString(newDir)) == 0'i32:
+        osError(osLastError())
     else:
-      if SetCurrentDirectoryA(newDir) == 0'i32: OSError(OSLastError())
+      if setCurrentDirectoryA(newDir) == 0'i32: osError(osLastError())
   else:
-    if chdir(newDir) != 0'i32: OSError(OSLastError())
+    if chdir(newDir) != 0'i32: osError(osLastError())
 
-proc JoinPath*(head, tail: string): string {.
+proc joinPath*(head, tail: string): string {.
   noSideEffect, rtl, extern: "nos$1".} =
   ## Joins two directory names to one.
   ##
   ## For example on Unix:
   ##
   ## .. code-block:: nimrod
-  ##   JoinPath("usr", "lib")
+  ##   joinPath("usr", "lib")
   ##
   ## results in:
   ##
@@ -481,10 +517,10 @@ proc JoinPath*(head, tail: string): string {.
   ## examples on Unix:
   ##
   ## .. code-block:: nimrod
-  ##   assert JoinPath("usr", "") == "usr/"
-  ##   assert JoinPath("", "lib") == "lib"
-  ##   assert JoinPath("", "/lib") == "/lib"
-  ##   assert JoinPath("usr/", "/lib") == "usr/lib"
+  ##   assert joinPath("usr", "") == "usr/"
+  ##   assert joinPath("", "lib") == "lib"
+  ##   assert joinPath("", "/lib") == "/lib"
+  ##   assert joinPath("usr/", "/lib") == "usr/lib"
   if len(head) == 0:
     result = tail
   elif head[len(head)-1] in {DirSep, AltSep}:
@@ -498,17 +534,17 @@ proc JoinPath*(head, tail: string): string {.
     else:
       result = head & DirSep & tail
 
-proc JoinPath*(parts: varargs[string]): string {.noSideEffect,
+proc joinPath*(parts: varargs[string]): string {.noSideEffect,
   rtl, extern: "nos$1OpenArray".} =
-  ## The same as `JoinPath(head, tail)`, but works with any number of directory
+  ## The same as `joinPath(head, tail)`, but works with any number of directory
   ## parts. You need to pass at least one element or the proc will assert in
   ## debug builds and crash on release builds.
   result = parts[0]
   for i in 1..high(parts):
-    result = JoinPath(result, parts[i])
+    result = joinPath(result, parts[i])
 
 proc `/` * (head, tail: string): string {.noSideEffect.} =
-  ## The same as ``JoinPath(head, tail)``
+  ## The same as ``joinPath(head, tail)``
   ##
   ## Here are some examples for Unix:
   ##
@@ -517,24 +553,24 @@ proc `/` * (head, tail: string): string {.noSideEffect.} =
   ##   assert "" / "lib" == "lib"
   ##   assert "" / "/lib" == "/lib"
   ##   assert "usr/" / "/lib" == "usr/lib"
-  return JoinPath(head, tail)
+  return joinPath(head, tail)
 
-proc SplitPath*(path: string): tuple[head, tail: string] {.
+proc splitPath*(path: string): tuple[head, tail: string] {.
   noSideEffect, rtl, extern: "nos$1".} =
   ## Splits a directory into (head, tail), so that
-  ## ``JoinPath(head, tail) == path``.
+  ## ``joinPath(head, tail) == path``.
   ##
-  ## Examples: 
+  ## Examples:
   ##
   ## .. code-block:: nimrod
-  ##   SplitPath("usr/local/bin") -> ("usr/local", "bin")
-  ##   SplitPath("usr/local/bin/") -> ("usr/local/bin", "")
-  ##   SplitPath("bin") -> ("", "bin")
-  ##   SplitPath("/bin") -> ("", "bin")
-  ##   SplitPath("") -> ("", "")
+  ##   splitPath("usr/local/bin") -> ("usr/local", "bin")
+  ##   splitPath("usr/local/bin/") -> ("usr/local/bin", "")
+  ##   splitPath("bin") -> ("", "bin")
+  ##   splitPath("/bin") -> ("", "bin")
+  ##   splitPath("") -> ("", "")
   var sepPos = -1
   for i in countdown(len(path)-1, 0):
-    if path[i] in {dirsep, altsep}:
+    if path[i] in {DirSep, AltSep}:
       sepPos = i
       break
   if sepPos >= 0:
@@ -546,9 +582,9 @@ proc SplitPath*(path: string): tuple[head, tail: string] {.
 
 proc parentDirPos(path: string): int =
   var q = 1
-  if path[len(path)-1] in {dirsep, altsep}: q = 2
+  if path[len(path)-1] in {DirSep, AltSep}: q = 2
   for i in countdown(len(path)-q, 0):
-    if path[i] in {dirsep, altsep}: return i
+    if path[i] in {DirSep, AltSep}: return i
   result = -1
 
 proc parentDir*(path: string): string {.
@@ -567,7 +603,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,8 +625,8 @@ 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 
-          (i == 0 or path[i-1] notin {dirsep, altsep}):
+      if path[i] in {DirSep, AltSep} and
+          (i == 0 or path[i-1] notin {DirSep, AltSep}):
         yield path.substr(0, i)
 
     if inclusive: yield path
@@ -605,17 +641,17 @@ proc `/../` * (head, tail: string): string {.noSideEffect.} =
     result = head / tail
 
 proc normExt(ext: string): string =
-  if ext == "" or ext[0] == extSep: result = ext # no copy needed here
-  else: result = extSep & ext
+  if ext == "" or ext[0] == ExtSep: result = ext # no copy needed here
+  else: result = ExtSep & ext
 
 proc searchExtPos(s: string): int =
   # BUGFIX: do not search until 0! .DS_Store is no file extension!
   result = -1
   for i in countdown(len(s)-1, 1):
-    if s[i] == extsep:
+    if s[i] == ExtSep:
       result = i
       break
-    elif s[i] in {dirsep, altsep}:
+    elif s[i] in {DirSep, AltSep}:
       break # do not skip over path
 
 proc splitFile*(path: string): tuple[dir, name, ext: string] {.
@@ -635,16 +671,16 @@ proc splitFile*(path: string): tuple[dir, name, ext: string] {.
   ## If `path` has no extension, `ext` is the empty string.
   ## If `path` has no directory component, `dir` is the empty string.
   ## If `path` has no filename component, `name` and `ext` are empty strings.
-  if path.len == 0 or path[path.len-1] in {dirSep, altSep}:
+  if path.len == 0 or path[path.len-1] in {DirSep, AltSep}:
     result = (path, "", "")
   else:
     var sepPos = -1
     var dotPos = path.len
     for i in countdown(len(path)-1, 0):
       if path[i] == ExtSep:
-        if dotPos == path.len and i > 0 and 
-            path[i-1] notin {dirsep, altsep}: dotPos = i
-      elif path[i] in {dirsep, altsep}:
+        if dotPos == path.len and i > 0 and
+            path[i-1] notin {DirSep, AltSep}: dotPos = i
+      elif path[i] in {DirSep, AltSep}:
         sepPos = i
         break
     result.dir = substr(path, 0, sepPos-1)
@@ -653,9 +689,9 @@ 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}:
+  if path.len == 0 or path[path.len-1] in {DirSep, AltSep}:
     result = ""
   else:
     result = splitPath(path).tail
@@ -666,26 +702,26 @@ proc expandFilename*(filename: string): string {.rtl, extern: "nos$1",
   when defined(windows):
     const bufsize = 3072'i32
     when useWinUnicode:
-      var unused: widecstring
+      var unused: WideCString
       var res = newWideCString("", bufsize div 2)
-      var L = GetFullPathNameW(newWideCString(filename), bufsize, res, unused)
-      if L <= 0'i32 or L >= bufsize: 
-        OSError(OSLastError())
+      var L = getFullPathNameW(newWideCString(filename), bufsize, res, unused)
+      if L <= 0'i32 or L >= bufsize:
+        osError(osLastError())
       result = res$L
     else:
       var unused: cstring
       result = newString(bufsize)
-      var L = GetFullPathNameA(filename, bufsize, result, unused)
-      if L <= 0'i32 or L >= bufsize: OSError(OSLastError())
+      var L = getFullPathNameA(filename, bufsize, result, unused)
+      if L <= 0'i32 or L >= bufsize: osError(osLastError())
       setLen(result, L)
   else:
     # careful, realpath needs to take an allocated buffer according to Posix:
     result = newString(pathMax)
     var r = realpath(filename, result)
-    if r.isNil: OSError(OSLastError())
-    setlen(result, c_strlen(result))
- 
-proc ChangeFileExt*(filename, ext: string): string {.
+    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 +776,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):
@@ -754,47 +790,47 @@ proc sameFile*(path1, path2: string): bool {.rtl, extern: "nos$1",
     when useWinUnicode:
       var p1 = newWideCString(path1)
       var p2 = newWideCString(path2)
-      template OpenHandle(path: expr): expr =
-        CreateFileW(path, 0'i32, FILE_SHARE_DELETE or FILE_SHARE_READ or
+      template openHandle(path: expr): expr =
+        createFileW(path, 0'i32, FILE_SHARE_DELETE or FILE_SHARE_READ or
           FILE_SHARE_WRITE, nil, OPEN_EXISTING,
           FILE_FLAG_BACKUP_SEMANTICS or FILE_ATTRIBUTE_NORMAL, 0)
 
-      var f1 = OpenHandle(p1)
-      var f2 = OpenHandle(p2)
-      
+      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
+      template openHandle(path: expr): expr =
+        createFileA(path, 0'i32, FILE_SHARE_DELETE or FILE_SHARE_READ or
           FILE_SHARE_WRITE, nil, OPEN_EXISTING,
           FILE_FLAG_BACKUP_SEMANTICS or FILE_ATTRIBUTE_NORMAL, 0)
 
-      var f1 = OpenHandle(path1)
-      var f2 = OpenHandle(path2)
+      var f1 = openHandle(path1)
+      var f2 = openHandle(path2)
 
     var lastErr: TOSErrorCode
     if f1 != INVALID_HANDLE_VALUE and f2 != INVALID_HANDLE_VALUE:
       var fi1, fi2: TBY_HANDLE_FILE_INFORMATION
 
-      if GetFileInformationByHandle(f1, addr(fi1)) != 0 and
-         GetFileInformationByHandle(f2, addr(fi2)) != 0:
+      if getFileInformationByHandle(f1, addr(fi1)) != 0 and
+         getFileInformationByHandle(f2, addr(fi2)) != 0:
         result = fi1.dwVolumeSerialNumber == fi2.dwVolumeSerialNumber and
                  fi1.nFileIndexHigh == fi2.nFileIndexHigh and
                  fi1.nFileIndexLow == fi2.nFileIndexLow
       else:
-        lastErr = OSLastError()
+        lastErr = osLastError()
         success = false
     else:
-      lastErr = OSLastError()
+      lastErr = osLastError()
       success = false
 
-    discard CloseHandle(f1)
-    discard CloseHandle(f2)
+    discard closeHandle(f1)
+    discard closeHandle(f2)
 
-    if not success: OSError(lastErr)
+    if not success: osError(lastErr)
   else:
     var a, b: TStat
     if stat(path1, a) < 0'i32 or stat(path2, b) < 0'i32:
-      OSError(OSLastError())
+      osError(osLastError())
     else:
       result = a.st_dev == b.st_dev and a.st_ino == b.st_ino
 
@@ -810,11 +846,11 @@ proc sameFileContent*(path1, path2: string): bool {.rtl, extern: "nos$1",
   if not open(b, path2):
     close(a)
     return false
-  var bufA = alloc(bufsize)
-  var bufB = alloc(bufsize)
-  while True:
-    var readA = readBuffer(a, bufA, bufsize)
-    var readB = readBuffer(b, bufB, bufsize)
+  var bufA = alloc(bufSize)
+  var bufB = alloc(bufSize)
+  while true:
+    var readA = readBuffer(a, bufA, bufSize)
+    var readB = readBuffer(b, bufB, bufSize)
     if readA != readB:
       result = false
       break
@@ -829,7 +865,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`.
   ##
@@ -844,48 +960,74 @@ proc copyFile*(source, dest: string) {.rtl, extern: "nos$1",
     when useWinUnicode:
       let s = newWideCString(source)
       let d = newWideCString(dest)
-      if CopyFileW(s, d, 0'i32) == 0'i32: OSError(OSLastError())
+      if copyFileW(s, d, 0'i32) == 0'i32: osError(osLastError())
     else:
-      if CopyFileA(source, dest, 0'i32) == 0'i32: OSError(OSLastError())
+      if copyFileA(source, dest, 0'i32) == 0'i32: osError(osLastError())
   else:
     # generic version of copyFile which works for any platform:
     const bufSize = 8000 # better for memory manager
     var d, s: TFile
-    if not open(s, source): OSError(OSLastError())
+    if not open(s, source): osError(osLastError())
     if not open(d, dest, fmWrite):
       close(s)
-      OSError(OSLastError())
-    var buf = alloc(bufsize)
-    while True:
-      var bytesread = readBuffer(s, buf, bufsize)
+      osError(osLastError())
+    var buf = alloc(bufSize)
+    while true:
+      var bytesread = readBuffer(s, buf, bufSize)
       if bytesread > 0:
         var byteswritten = writeBuffer(d, buf, bytesread)
-        if bytesread != bytesWritten:
+        if bytesread != byteswritten:
           dealloc(buf)
           close(s)
           close(d)
-          OSError(OSLastError())
+          osError(osLastError())
       if bytesread != bufSize: break
     dealloc(buf)
     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:
+  if c_rename(source, dest) != 0'i32:
     raise newException(EOS, $strerror(errno))
 
-when not defined(ENOENT):
-  var ENOENT {.importc, header: "<errno.h>".}: cint
+when not defined(ENOENT) and not defined(Windows):
+  when NoFakeVars:
+    const ENOENT = cint(2) # 2 on most systems including Solaris
+  else:
+    var ENOENT {.importc, header: "<errno.h>".}: cint
+
+when defined(Windows):
+  when useWinUnicode:
+    template deleteFile(file: expr): expr {.immediate.} = deleteFileW(file)
+    template setFileAttributes(file, attrs: expr): expr {.immediate.} = 
+      setFileAttributesW(file, attrs)
+  else:
+    template deleteFile(file: expr): expr {.immediate.} = deleteFileA(file)
+    template setFileAttributes(file, attrs: expr): expr {.immediate.} = 
+      setFileAttributesA(file, attrs)
 
 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.
-  if cremove(file) != 0'i32 and errno != ENOENT:
-    raise newException(EOS, $strerror(errno))
+  ## On Windows, ignores the read-only attribute.
+  when defined(Windows):
+    when useWinUnicode:
+      let f = newWideCString(file)
+    else:
+      let f = file
+    if deleteFile(f) == 0:
+      if getLastError() == ERROR_ACCESS_DENIED: 
+        if setFileAttributes(f, FILE_ATTRIBUTE_NORMAL) == 0:
+          osError(osLastError())
+        if deleteFile(f) == 0:
+          osError(osLastError())
+  else:
+    if c_remove(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:.
   ##
@@ -895,9 +1037,9 @@ proc execShellCmd*(command: string): int {.rtl, extern: "nos$1",
   ## the process has finished. To execute a program without having a
   ## shell involved, use the `execProcess` proc of the `osproc`
   ## module.
-  result = csystem(command)
+  result = c_system(command) shr 8
 
-# 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
@@ -908,10 +1050,10 @@ when defined(windows):
   # because we support Windows GUI applications, things get really
   # messy here...
   when useWinUnicode:
-    proc strEnd(cstr: wideCString, c = 0'i32): wideCString {.
+    proc strEnd(cstr: WideCString, c = 0'i32): WideCString {.
       importc: "wcschr", header: "<string.h>".}
   else:
-    proc strEnd(cstr: CString, c = 0'i32): CString {.
+    proc strEnd(cstr: cstring, c = 0'i32): cstring {.
       importc: "strchr", header: "<string.h>".}
 
   proc getEnvVarsC() =
@@ -922,32 +1064,32 @@ when defined(windows):
           env = getEnvironmentStringsW()
           e = env
         if e == nil: return # an error occured
-        while True:
+        while true:
           var eend = strEnd(e)
           add(environment, $e)
-          e = cast[wideCString](cast[TAddress](eend)+2)
+          e = cast[WideCString](cast[TAddress](eend)+2)
           if eend[1].int == 0: break
-        discard FreeEnvironmentStringsW(env)
+        discard freeEnvironmentStringsW(env)
       else:
         var
           env = getEnvironmentStringsA()
           e = env
         if e == nil: return # an error occured
-        while True:
+        while true:
           var eend = strEnd(e)
           add(environment, $e)
-          e = cast[CString](cast[TAddress](eend)+1)
+          e = cast[cstring](cast[TAddress](eend)+1)
           if eend[1] == '\0': break
-        discard FreeEnvironmentStringsA(env)
+        discard freeEnvironmentStringsA(env)
       envComputed = true
 
 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
@@ -966,7 +1108,7 @@ else:
       when useNSGetEnviron:
         var gEnv = NSGetEnviron()[]
       var i = 0
-      while True:
+      while true:
         if gEnv[i] == nil: break
         add environment, $gEnv[i]
         inc(i)
@@ -989,14 +1131,14 @@ proc getEnv*(key: string): TaintedString {.tags: [FReadEnv].} =
   if i >= 0:
     return TaintedString(substr(environment[i], find(environment[i], '=')+1))
   else:
-    var env = cgetenv(key)
+    var env = c_getenv(key)
     if env == nil: return TaintedString("")
     result = TaintedString($env)
 
 proc existsEnv*(key: string): bool {.tags: [FReadEnv].} =
   ## Checks whether the environment variable named `key` exists.
   ## Returns true if it exists, false otherwise.
-  if cgetenv(key) != nil: return true
+  if c_getenv(key) != nil: return true
   else: return findEnvVar(key) >= 0
 
 proc putEnv*(key, val: string) {.tags: [FWriteEnv].} =
@@ -1014,24 +1156,24 @@ proc putEnv*(key, val: string) {.tags: [FWriteEnv].} =
     add environment, (key & '=' & val)
     indx = high(environment)
   when defined(unix):
-    if cputenv(environment[indx]) != 0'i32:
-      OSError(OSLastError())
+    if c_putenv(environment[indx]) != 0'i32:
+      osError(osLastError())
   else:
     when useWinUnicode:
       var k = newWideCString(key)
       var v = newWideCString(val)
-      if SetEnvironmentVariableW(k, v) == 0'i32: OSError(OSLastError())
+      if setEnvironmentVariableW(k, v) == 0'i32: osError(osLastError())
     else:
-      if SetEnvironmentVariableA(key, val) == 0'i32: OSError(OSLastError())
+      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].} =
@@ -1042,15 +1184,15 @@ iterator walkFiles*(pattern: string): string {.tags: [FReadDir].} =
   ## notation is supported.
   when defined(windows):
     var
-      f: TWin32FindData
+      f: TWIN32_FIND_DATA
       res: int
-    res = findfirstFile(pattern, f)
+    res = findFirstFile(pattern, f)
     if res != -1:
       while true:
         if not skipFindData(f):
           yield splitFile(pattern).dir / extractFilename(getFilename(f))
-        if findnextFile(res, f) == 0'i32: break
-      findclose(res)
+        if findNextFile(res, f) == 0'i32: break
+      findClose(res)
   else: # here we use glob
     var
       f: TGlob
@@ -1095,22 +1237,24 @@ iterator walkDir*(dir: string): tuple[kind: TPathComponent, path: string] {.
   ##   dirA/fileA1.txt
   ##   dirA/fileA2.txt
   when defined(windows):
-    var f: TWIN32_Find_Data
-    var h = findfirstFile(dir / "*", f)
+    var f: TWIN32_FIND_DATA
+    var h = findFirstFile(dir / "*", f)
     if h != -1:
       while true:
         var k = pcFile
         if not skipFindData(f):
           if (f.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) != 0'i32:
             k = pcDir
+          if (f.dwFileAttributes and FILE_ATTRIBUTE_REPARSE_POINT) != 0'i32:
+            k = succ(k)
           yield (k, dir / extractFilename(getFilename(f)))
-        if findnextFile(h, f) == 0'i32: break
-      findclose(h)
+        if findNextFile(h, f) == 0'i32: break
+      findClose(h)
   else:
-    var d = openDir(dir)
+    var d = opendir(dir)
     if d != nil:
       while true:
-        var x = readDir(d)
+        var x = readdir(d)
         if x == nil: break
         var y = $x.d_name
         if y != "." and y != "..":
@@ -1121,12 +1265,16 @@ iterator walkDir*(dir: string): tuple[kind: TPathComponent, path: string] {.
           if S_ISDIR(s.st_mode): k = pcDir
           if S_ISLNK(s.st_mode): k = succ(k)
           yield (k, y)
-      discard closeDir(d)
+      discard closedir(d)
 
 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.
+  ## **Warning**:
+  ## Modifying the directory structure while the iterator 
+  ## is traversing may result in undefined behavior! 
+  ## 
   ## Walking is recursive. `filter` controls the behaviour of the iterator:
   ##
   ## ---------------------   ---------------------------------------------
@@ -1137,7 +1285,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,18 +1294,18 @@ 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)
+      wrapUnary(res, removeDirectoryW, dir)
     else:
-      var res = RemoveDirectoryA(dir)
-    let lastError = OSLastError()
-    if res == 0'i32 and lastError.int32 != 3'i32 and 
+      var res = removeDirectoryA(dir)
+    let lastError = osLastError()
+    if res == 0'i32 and lastError.int32 != 3'i32 and
         lastError.int32 != 18'i32 and lastError.int32 != 2'i32:
-      OSError(lastError)
+      osError(lastError)
   else:
-    if rmdir(dir) != 0'i32 and errno != ENOENT: OSError(OSLastError())
+    if rmdir(dir) != 0'i32 and errno != ENOENT: osError(osLastError())
 
 proc removeDir*(dir: string) {.rtl, extern: "nos$1", tags: [
   FWriteDir, FReadDir].} =
@@ -1166,7 +1314,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)
@@ -1175,37 +1323,37 @@ proc removeDir*(dir: string) {.rtl, extern: "nos$1", tags: [
 proc rawCreateDir(dir: string) =
   when defined(solaris):
     if mkdir(dir, 0o711) != 0'i32 and errno != EEXIST and errno != ENOSYS:
-      OSError(OSLastError())
+      osError(osLastError())
   elif defined(unix):
     if mkdir(dir, 0o711) != 0'i32 and errno != EEXIST:
-      OSError(OSLastError())
+      osError(osLastError())
   else:
     when useWinUnicode:
-      wrapUnary(res, CreateDirectoryW, dir)
+      wrapUnary(res, createDirectoryW, dir)
     else:
-      var res = CreateDirectoryA(dir)
-    if res == 0'i32 and GetLastError() != 183'i32:
-      OSError(OSLastError())
+      var res = createDirectoryA(dir)
+    if res == 0'i32 and getLastError() != 183'i32:
+      osError(osLastError())
 
 proc createDir*(dir: string) {.rtl, extern: "nos$1", tags: [FWriteDir].} =
   ## Creates the `directory`:idx: `dir`.
   ##
   ## 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):
     omitNext = isAbsolute(dir)
   for i in 1.. dir.len-1:
-    if dir[i] in {dirsep, altsep}:
+    if dir[i] in {DirSep, AltSep}:
       if omitNext:
         omitNext = false
       else:
         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)
@@ -1216,11 +1364,51 @@ proc copyDir*(source, dest: string) {.rtl, extern: "nos$1",
       copyFile(path, dest / noSource)
     of pcDir:
       copyDir(path, dest / noSource)
-    else: nil
+    else: discard
+
+proc createSymlink*(src, dest: string) =
+  ## Create a symbolic link at `dest` which points to the item specified
+  ## by `src`. On most operating systems, will fail if a lonk
+  ##
+  ## **Warning**:
+  ## Some OS's (such as Microsoft Windows) restrict the creation 
+  ## of symlinks to root users (administrators).
+  when defined(Windows):
+    let flag = dirExists(src).int32
+    when useWinUnicode:
+      var wSrc = newWideCString(src)
+      var wDst = newWideCString(dest)
+      if createSymbolicLinkW(wDst, wSrc, flag) == 0 or getLastError() != 0:
+        osError(osLastError())
+    else:
+      if createSymbolicLinkA(dest, src, flag) == 0 or getLastError() != 0:
+        osError(osLastError())
+  else:
+    if symlink(src, dest) != 0:
+      osError(osLastError())
+
+proc createHardlink*(src, dest: string) =
+  ## Create a hard link at `dest` which points to the item specified
+  ## by `src`.
+  ##
+  ## **Warning**: Most OS's restrict the creation of hard links to 
+  ## root users (administrators) .
+  when defined(Windows):
+    when useWinUnicode:
+      var wSrc = newWideCString(src)
+      var wDst = newWideCString(dest)
+      if createHardLinkW(wDst, wSrc, nil) == 0:
+        osError(osLastError())
+    else:
+      if createHardLinkA(dest, src, nil) == 0:
+        osError(osLastError())
+  else:
+    if link(src, dest) != 0:
+      osError(osLastError())
 
 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 +1435,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 +1448,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 +1494,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 +1515,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 +1567,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 +1589,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)
@@ -1488,13 +1602,13 @@ when defined(linux) or defined(solaris) or defined(bsd) or defined(aix):
     if len > 256:
       result = newString(len+1)
       len = readlink(procPath, result, len)
-    setlen(result, len)
+    setLen(result, len)
 
 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) {.
@@ -1517,7 +1631,7 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [FReadIO].} =
   # /proc/<pid>/file
   when defined(windows):
     when useWinUnicode:
-      var buf = cast[wideCString](alloc(256*2))
+      var buf = cast[WideCString](alloc(256*2))
       var len = getModuleFileNameW(0, buf, 256)
       result = buf$len
     else:
@@ -1542,24 +1656,24 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [FReadIO].} =
     # little heuristic that may work on other POSIX-like systems:
     result = string(getEnv("_"))
     if len(result) == 0:
-      result = string(ParamStr(0))
+      result = string(paramStr(0))
       # POSIX guaranties that this contains the executable
       # as it has been executed by the calling process
       if len(result) > 0 and result[0] != DirSep: # not an absolute path?
         # iterate over any path in the $PATH environment variable
         for p in split(string(getEnv("PATH")), {PathSep}):
-          var x = JoinPath(p, result)
-          if ExistsFile(x): return x
+          var x = joinPath(p, result)
+          if existsFile(x): return x
 
 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
 
@@ -1578,33 +1692,33 @@ proc sleep*(milsecs: int) {.rtl, extern: "nos$1", tags: [FTime].} =
     a.tv_nsec = (milsecs mod 1000) * 1000 * 1000
     discard posix.nanosleep(a, b)
 
-proc getFileSize*(file: string): biggestInt {.rtl, extern: "nos$1",
+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)
-    if resA == -1: OSError(OSLastError())
+    var a: TWIN32_FIND_DATA
+    var resA = findFirstFile(file, a)
+    if resA == -1: osError(osLastError())
     result = rdFileSize(a)
-    findclose(resA)
+    findClose(resA)
   else:
     var f: TFile
-    if open(f, file): 
+    if open(f, file):
       result = getFileSize(f)
       close(f)
-    else: OSError(OSLastError())
+    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
+  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
+    if existsFile(x): return x
   result = ""
 
 proc expandTilde*(path: string): string =
@@ -1629,4 +1743,3 @@ proc expandTilde*(path: string): string =
     result = path
 
 {.pop.}
-