summary refs log blame commit diff stats
path: root/lib/pure/os.nim
blob: f7d22ca06d31f3da5255c0a78b23af8a486d0b0f (plain) (tree)
1
2
3
4
5
6
7
8
9


                                     
                                         




                                                   


                                                                    
                    
 
                      
 

                        


                 








                                                             


                                                            









                                                                         

                                                                         






                                                                        















                                                                               

                                                               
                                        

                   
                                                                        

                          







                                    

                   














                                                                               
                                                                         


                                                                         
             






                                            

                      






                                         

                   






                                     

                   







                                   

                   





                                                                          
                                                                    

















                                                                       
                                                  




                                                 

                                               



                                                          





                        
                   





























                                                                  
                           

              
                                                                   




                                                        
       

                                                                
 
                                                             

                                                                           
                        


                                                        
       

                                                           
 
                                                                             











                                                             
                                                                       











                                                              
                                                                      










                                                            
 
                                                              



                                                                       
                                                        
                                                  
                                        
                             



                                                 
       
                                      
                                      

               
 
                                                
                                                                  





                                                       

                                             



                                      
                           



                             
                           



                                                   












                                             

                                                                

                                                                  



                                       



                                                          
















                                                                             

                                                           


                                                  
               
    





                                                           






                                     

                                         
       

                      
 

                                        



                                                                    

                                                                 













                                           








                                                                      
                                                                  
             
                                  

                      
           


                                   

                                                               






                                                       




                                                                 









                                                                             
                                                   







                                                                     


                                                               

                                                                             
 

                                              

                                                                  


                                                           
                                 
 
                                                                         











                                                                         
 
                                                                     
                               




                                                                           

                                                                             
                                     






                                             
                                                                             

                                                                   


                                                                        
 
                                                                               

                                                                   


                                                                              

                                                     






                                                                
                                     


                                                           

                                                  
                                                        




                                                                
                                     
                                                 





                                                                  
 

                                            











                                                            
 
                                                                     

                                                                      
                                                                    

                        


                                       
                                 
                                           
         
                                                





                                          
                                                        

                                                                       
                                                            
 
                                                                            
                                                                          
                    



                              


                                     
















                                            

          
 
                                                               


                                                          
                                                         

                                                               

                                                    


                                     
               
                            
               







                                                         
                                    
                

            
 
                                                               
                                                                          
                                              
 
                                                         
                                                        
                                      
 



                                                                    
                                                                   
                                     




                                                                      
                                                               
            
                           
 


                                                                        

                           
                                



                                                                  

                                                   
 
                      

                       
                                      



                                            
                            


                                                 
                                          

     



                                                   







                                                                         
                                            


                                                           
 
                      

                                                            
                           
                                  
               









                                   
                                                 

           
                                   
                                                                      



                                                                   
                         
            
                                                            
       



                            


                                                                


                                     
                                
                                                                            
                                                    
 

                                                                
                                                                         






                                                                     





                                                  
 









                                                                               


                                                                            




                                                                   
                                              

                                                                            




                                                         
                       
              
                                    

                 
                                 
                                                                      
                                                


                          
              




                                        



                                    

                     



                                                                     





                                                                          
 



                                                                            
                                        










                                           
                                                                










                                        
                                                                        
                     












                                                        
                                       
                        
                                          

                                            
                         
 








                                                                           
                                                          
                                                    
                                                                 









                                                                          
                                 
                        
                                                
       
                                     
 
                                                       
                                                                       


                                                            
                                                          
                             
                   
 



                                                      
       

                                                                         
 
                                                       
                                        







                                                                              
 

                                             
                                                     
                                                                              
























                                                                                

                                                                  

              
            
             
                


                                                             
                            







                                    
                                                























                                        
         












                                             

                  











                                                                         

                                                                   
                                                                              

























                                                                             

                                                                                
























                                                                              
                                                     


                                            

                                                               






                                                                             

                                                               





                                                                             
                                                     



                                                            
                                                       


                                                                       
 
                      




                                                                           
     
                        
 
                                                    
                                                                       
                   
                                                                 
                          
 
                                                           
                                                                   



                                                         
                                                                 
                     
 

                                                                          
     
                                          

                                                
                                  


                                                      
                                             
 
















                                                                          
                                                         



































                                                                               












                                                                             
                                                           
                                          
 
                                                    








                                         
                                                                      













                                                         












                                                                           
       
 
#
#
#            Nimrod's Runtime Library
#        (c) Copyright 2010 Andreas Rumpf
#
#    See the file "copying.txt", included in this
#    distribution, for details about the copyright.
#

## This module contains basic operating system facilities like
## retrieving environment variables, reading command line arguments,
## working with directories, running shell commands, etc.
{.deadCodeElim: on.}

{.push debugger: off.}

include "system/inclrtl"

import
  strutils, times

when defined(windows):
  import winlean
elif defined(posix): 
  import posix
else:
  {.error: "OS module not ported to your operating system!".}

include "system/ansi_c"

const
  doslike = defined(windows) or defined(OS2) or defined(DOS)
    # DOS-like filesystem

when defined(Nimdoc): # only for proper documentation:
  const
    CurDir* = '.'
      ## The constant string used by the operating system to refer to the
      ## current directory.
      ##
      ## For example: '.' for POSIX or ':' for the classic Macintosh.

    ParDir* = ".."
      ## The constant string used by the operating system to refer to the
      ## parent directory.
      ##
      ## For example: ".." for POSIX or "::" for the classic Macintosh.

    DirSep* = '/'
      ## The character used by the operating system to separate pathname
      ## components, for example, '/' for POSIX or ':' for the classic
      ## Macintosh.

    AltSep* = '/'
      ## An alternative character used by the operating system to separate
      ## pathname components, or the same as `DirSep` if only one separator
      ## character exists. This is set to '/' on Windows systems where `DirSep`
      ## is a backslash.

    PathSep* = ':'
      ## The character conventionally used by the operating system to separate
      ## search patch components (as in PATH), such as ':' for POSIX or ';' for
      ## Windows.

    FileSystemCaseSensitive* = True
      ## True if the file system is case sensitive, false otherwise. Used by
      ## `cmpPaths` to compare filenames properly.

    ExeExt* = ""
      ## The file extension of native executables. For example:
      ## "" for POSIX, "exe" on Windows.

    ScriptExt* = ""
      ## The file extension of a script file. For example: "" for POSIX,
      ## "bat" on Windows.

elif defined(macos):
  const
    curdir* = ':'
    pardir* = "::"
    dirsep* = ':'
    altsep* = dirsep
    pathsep* = ','
    FileSystemCaseSensitive* = false
    ExeExt* = ""
    ScriptExt* = ""

  #  MacOS paths
  #  ===========
  #  MacOS directory separator is a colon ":" which is the only character not
  #  allowed in filenames.
  #
  #  A path containing no colon or which begins with a colon is a partial path.
  #  E.g. ":kalle:petter" ":kalle" "kalle"
  #
  #  All other paths are full (absolute) paths. E.g. "HD:kalle:" "HD:"
  #  When generating paths, one is safe if one ensures that all partial paths
  #  begin with a colon, and all full paths end with a colon.
  #  In full paths the first name (e g HD above) is the name of a mounted
  #  volume.
  #  These names are not unique, because, for instance, two diskettes with the
  #  same names could be inserted. This means that paths on MacOS are not
  #  waterproof. In case of equal names the first volume found will do.
  #  Two colons "::" are the relative path to the parent. Three is to the
  #  grandparent etc.
elif doslike:
  const
    curdir* = '.'
    pardir* = ".."
    dirsep* = '\\' # seperator within paths
    altsep* = '/'
    pathSep* = ';' # seperator between paths
    FileSystemCaseSensitive* = false
    ExeExt* = "exe"
    ScriptExt* = "bat"
elif defined(PalmOS) or defined(MorphOS):
  const
    dirsep* = '/'
    altsep* = dirsep
    PathSep* = ';'
    pardir* = ".."
    FileSystemCaseSensitive* = false
    ExeExt* = ""
    ScriptExt* = ""
elif defined(RISCOS):
  const
    dirsep* = '.'
    altsep* = '.'
    pardir* = ".." # is this correct?
    pathSep* = ','
    FileSystemCaseSensitive* = true
    ExeExt* = ""
    ScriptExt* = ""
else: # UNIX-like operating system
  const
    curdir* = '.'
    pardir* = ".."
    dirsep* = '/'
    altsep* = dirsep
    pathSep* = ':'
    FileSystemCaseSensitive* = true
    ExeExt* = ""
    ScriptExt* = ""

const
  ExtSep* = '.'
    ## The character which separates the base filename from the extension;
    ## for example, the '.' in ``os.nim``.

proc OSError*(msg: string = "") {.noinline, rtl, extern: "nos$1".} =
  ## 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
  ## ``GetLastError`` is checked before ``errno``.
  ## If no error flag is set, the message ``unknown OS error`` is used.
  if len(msg) == 0:
    when defined(Windows):
      var err = GetLastError()
      if err != 0'i32:
        # sigh, why is this is so difficult?
        var msgbuf: cstring
        if FormatMessageA(0x00000100 or 0x00001000 or 0x00000200,
                          nil, err, 0, addr(msgbuf), 0, nil) != 0'i32:
          var m = $msgbuf
          if msgbuf != nil:
            LocalFree(msgbuf)
          raise newException(EOS, m)
    if errno != 0'i32:
      raise newException(EOS, $os.strerror(errno))
    else:
      raise newException(EOS, "unknown OS error")
  else:
    raise newException(EOS, msg)

proc UnixToNativePath*(path: string): 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.
  when defined(unix):
    result = path
  else:
    var start: int
    if path[0] == '/':
      # an absolute path
      when doslike:
        result = r"C:\"
      elif defined(macos):
        result = "" # must not start with ':'
      else:
        result = $dirSep
      start = 1
    elif path[0] == '.' and path[1] == '/':
      # current directory
      result = $curdir
      start = 2
    else:
      result = ""
      start = 0

    var i = start
    while i < len(path): # ../../../ --> ::::
      if path[i] == '.' and path[i+1] == '.' and path[i+2] == '/':
        # parent directory
        when defined(macos):
          if result[high(result)] == ':':
            add result, ':'
          else:
            add result, pardir
        else:
          add result, pardir & dirSep
        inc(i, 3)
      elif path[i] == '/':
        add result, dirSep
        inc(i)
      else:
        add result, path[i]
        inc(i)

proc existsFile*(filename: string): bool {.rtl, extern: "nos$1".} =
  ## Returns true if the file exists, false otherwise.
  when defined(windows):
    var a = GetFileAttributesA(filename)
    if a != -1'i32:
      result = (a and FILE_ATTRIBUTE_DIRECTORY) == 0'i32
  else:
    var res: TStat
    return stat(filename, res) >= 0'i32 and S_ISREG(res.st_mode)

proc existsDir*(dir: string): bool {.rtl, extern: "nos$1".} =
  ## Returns true iff the directory `dir` exists. If `dir` is a file, false
  ## is returned.
  when defined(windows):
    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 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()
    return res.st_mtime
  else:
    var f: TWIN32_Find_Data
    var h = findfirstFileA(file, f)
    if h == -1'i32: OSError()
    result = winTimeToUnixTime(rdFileTime(f.ftLastWriteTime))
    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()
    return res.st_atime
  else:
    var f: TWIN32_Find_Data
    var h = findfirstFileA(file, f)
    if h == -1'i32: OSError()
    result = winTimeToUnixTime(rdFileTime(f.ftLastAccessTime))
    findclose(h)

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()
    return res.st_ctime
  else:
    var f: TWIN32_Find_Data
    var h = findfirstFileA(file, f)
    if h == -1'i32: OSError()
    result = winTimeToUnixTime(rdFileTime(f.ftCreationTime))
    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
  ## modification time is later than `b`'s.
  result = getLastModificationTime(a) - getLastModificationTime(b) > 0

proc getCurrentDir*(): string {.rtl, extern: "nos$1".} =
  ## Returns the `current working directory`:idx:.
  const bufsize = 512 # should be enough
  result = newString(bufsize)
  when defined(windows):
    var L = GetCurrentDirectoryA(bufsize, result)
    if L == 0'i32: OSError()
    setLen(result, L)
  else:
    if getcwd(result, bufsize) != nil:
      setlen(result, c_strlen(result))
    else:
      OSError()

proc setCurrentDir*(newDir: string) {.inline.} =
  ## Sets the `current working directory`:idx:; `EOS` is raised if
  ## `newDir` cannot been set.
  when defined(Windows):
    if SetCurrentDirectoryA(newDir) == 0'i32: OSError()
  else:
    if chdir(newDir) != 0'i32: OSError()

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")
  ##
  ## results in:
  ##
  ## .. code-block:: nimrod
  ##   "usr/lib"
  ##
  ## If head is the empty string, tail is returned.
  ## If tail is the empty string, head is returned.
  if len(head) == 0:
    result = tail
  elif head[len(head)-1] in {DirSep, AltSep}:
    if tail[0] in {DirSep, AltSep}:
      result = head & copy(tail, 1)
    else:
      result = head & tail
  else:
    if tail[0] in {DirSep, AltSep}:
      result = head & tail
    else:
      result = head & DirSep & tail

proc JoinPath*(parts: openarray[string]): string {.noSideEffect,
  rtl, extern: "nos$1OpenArray".} =
  ## The same as `JoinPath(head, tail)`, but works with any number
  ## of directory parts.
  result = parts[0]
  for i in 1..high(parts):
    result = JoinPath(result, parts[i])

proc `/` * (head, tail: string): string {.noSideEffect.} =
  ## The same as ``joinPath(head, tail)``
  return joinPath(head, tail)

proc SplitPath*(path: string, head, tail: var string) {.noSideEffect,
                                                        deprecated.} =
  ## **Deprecated since version 0.8.2**: use the version that returns a tuple
  ## instead
  var
    sepPos = -1
  for i in countdown(len(path)-1, 0):
    if path[i] in {dirsep, altsep}:
      sepPos = i
      break
  if sepPos >= 0:
    head = copy(path, 0, sepPos-1)
    tail = copy(path, sepPos+1)
  else:
    head = ""
    tail = path # make a string copy here

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``.
  ##
  ## 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("") -> ("", "")
  var
    sepPos = -1
  for i in countdown(len(path)-1, 0):
    if path[i] in {dirsep, altsep}:
      sepPos = i
      break
  if sepPos >= 0:
    result.head = copy(path, 0, sepPos-1)
    result.tail = copy(path, sepPos+1)
  else:
    result.head = ""
    result.tail = path

proc parentDir*(path: string): string {.
  noSideEffect, rtl, extern: "nos$1".} =
  ## Returns the parent directory of `path`.
  ##
  ## This is often the same as the ``head`` result of ``splitPath``.
  ## If there is no parent, ``path`` is returned.
  ## | Example: ``parentDir("/usr/local/bin") == "/usr/local"``.
  ## | Example: ``parentDir("/usr/local/bin/") == "/usr/local"``.
  var
    sepPos = -1
    q = 1
  if path[len(path)-1] in {dirsep, altsep}:
    q = 2
  for i in countdown(len(path)-q, 0):
    if path[i] in {dirsep, altsep}:
      sepPos = i
      break
  if sepPos >= 0:
    result = copy(path, 0, sepPos-1)
  else:
    result = path

proc `/../` * (head, tail: string): string {.noSideEffect.} =
  ## The same as ``parentDir(head) / tail``
  return parentDir(head) / tail

proc normExt(ext: string): string =
  if ext == "" or ext[0] == extSep: result = ext # no copy needed here
  else: result = extSep & ext

proc searchExtPos(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:
      result = i
      break
    elif s[i] in {dirsep, altsep}:
      break # do not skip over path

proc splitFile*(path: string): tuple[dir, name, ext: string] {.
  noSideEffect, rtl, extern: "nos$1".} =
  ## Splits a filename into (dir, filename, extension).
  ## `dir` does not end in `DirSep`.
  ## `extension` includes the leading dot.
  ##
  ## Example:
  ##
  ## .. code-block:: nimrod
  ##   var (dir, name, ext) = splitFile("usr/local/nimrodc.html")
  ##   assert dir == "usr/local"
  ##   assert name == "nimrodc"
  ##   assert ext == ".html"
  ##
  ## If `path` has no extension, `ext` is the empty string.
  ## If `path` has no directory component, `dir` is the empty string.
  ## If `path` has no filename component, `name` and `ext` are empty strings.
  if path.len == 0 or path[path.len-1] in {dirSep, altSep}:
    result = (path, "", "")
  else:
    var sepPos = -1
    var dotPos = path.len
    for i in countdown(len(path)-1, 0):
      if path[i] == ExtSep:
        if dotPos == path.len and i > 0: dotPos = i
      elif path[i] in {dirsep, altsep}:
        sepPos = i
        break
    result.dir = copy(path, 0, sepPos-1)
    result.name = copy(path, sepPos+1, dotPos-1)
    result.ext = copy(path, dotPos)

proc extractDir*(path: string): string {.noSideEffect, deprecated.} =
  ## Extracts the directory of a given path. This is almost the
  ## same as the `head` result of `splitPath`, except that
  ## ``extractDir("/usr/lib/") == "/usr/lib/"``.
  ## **Deprecated since version 0.8.2**: Use ``splitFile(path).dir`` instead.
  result = splitFile(path).dir

proc extractFilename*(path: string): string {.
  noSideEffect, rtl, extern: "nos$1".} =
  ## Extracts the filename of a given `path`. This is the same as 
  ## ``name & ext`` from ``splitFile(path)``.
  if path.len == 0 or path[path.len-1] in {dirSep, altSep}:
    result = ""
  else:
    result = splitPath(path).tail

proc expandFilename*(filename: string): string {.rtl, extern: "nos$1".} =
  ## Returns the full path of `filename`, raises EOS in case of an error.
  when defined(windows):
    var unused: cstring
    result = newString(3072)
    var L = GetFullPathNameA(filename, 3072'i32, result, unused)
    if L <= 0'i32 or L >= 3072'i32: OSError()
    setLen(result, L)
  else:
    var res = realpath(filename, nil)
    if res == nil: OSError()
    result = $res
    c_free(res)

proc SplitFilename*(filename: string, name, extension: var string) {.
  noSideEffect, deprecated.} = 
  ## Splits a filename into (name, extension), so that
  ## ``name & extension == filename``.
  ##
  ## Example: After ``SplitFilename("usr/local/nimrodc.html", name, ext)``,
  ## `name` is "usr/local/nimrodc" and `ext` is ".html".
  ## If the file has no extension, extension is the empty string.
  ## **Deprecated since version 0.8.2**: Use ``splitFile(filename)`` instead.
  var extPos = searchExtPos(filename)
  if extPos >= 0:
    name = copy(filename, 0, extPos-1)
    extension = copy(filename, extPos)
  else:
    name = filename # make a string copy here
    extension = ""

proc extractFileExt*(filename: string): string {.noSideEffect, deprecated.} =
  ## Extracts the file extension of a given `filename`. This is the
  ## same as the `extension` result of `splitFilename`.
  ## **Deprecated since version 0.8.2**: Use ``splitFile(filename).ext``
  ## instead.
  result = splitFile(filename).ext

proc extractFileTrunk*(filename: string): string {.noSideEffect, deprecated.} =
  ## Extracts the file name of a given `filename`. This removes any
  ## directory information and the file extension.
  ## **Deprecated since version 0.8.2**: Use ``splitFile(path).name`` instead.
  result = splitFile(filename).name
  
proc ChangeFileExt*(filename, ext: string): string {.
  noSideEffect, rtl, extern: "nos$1".} =
  ## Changes the file extension to `ext`.
  ##
  ## If the `filename` has no extension, `ext` will be added.
  ## If `ext` == "" then any extension is removed.
  ## `Ext` should be given without the leading '.', because some
  ## filesystems may use a different character. (Although I know
  ## of none such beast.)
  var extPos = searchExtPos(filename)
  if extPos < 0: result = filename & normExt(ext)
  else: result = copy(filename, 0, extPos-1) & normExt(ext)

proc addFileExt*(filename, ext: string): string {.
  noSideEffect, rtl, extern: "nos$1".} =
  ## Adds the file extension `ext` to `filename`, unless
  ## `filename` already has an extension.
  ##
  ## `Ext` should be given without the leading '.', because some
  ## filesystems may use a different character.
  ## (Although I know of none such beast.)
  var extPos = searchExtPos(filename)
  if extPos < 0: result = filename & normExt(ext)
  else: result = filename

proc AppendFileExt*(filename, ext: string): string {.
  noSideEffect, deprecated.} =
  ## **Deprecated since version 0.8.2**: Use `addFileExt` instead.
  result = addFileExt(filename, ext)

proc cmpPaths*(pathA, pathB: string): int {.
  noSideEffect, rtl, extern: "nos$1".} =
  ## Compares two paths.
  ##
  ## On a case-sensitive filesystem this is done
  ## case-sensitively otherwise case-insensitively. Returns:
  ##
  ## | 0 iff pathA == pathB
  ## | < 0 iff pathA < pathB
  ## | > 0 iff pathA > pathB
  if FileSystemCaseSensitive:
    result = cmp(pathA, pathB)
  else:
    result = cmpIgnoreCase(pathA, pathB)

proc sameFile*(path1, path2: string): bool {.rtl, extern: "nos$1".} =
  ## Returns True if both pathname arguments refer to the same file or
  ## directory (as indicated by device number and i-node number).
  ## Raises an exception if an stat() call on either pathname fails.
  when defined(Windows):
    var
      a, b: TWin32FindData
    var resA = findfirstFileA(path1, a)
    var resB = findfirstFileA(path2, b)
    if resA != -1 and resB != -1:
      result = $a.cFileName == $b.cFileName
    else:
      # work around some ``findfirstFileA`` bugs
      result = cmpPaths(path1, path2) == 0
    if resA != -1: findclose(resA)
    if resB != -1: findclose(resB)
  else:
    var
      a, b: TStat
    if stat(path1, a) < 0'i32 or stat(path2, b) < 0'i32:
      result = cmpPaths(path1, path2) == 0 # be consistent with Windows
    else:
      result = a.st_dev == b.st_dev and a.st_ino == b.st_ino

proc sameFileContent*(path1, path2: string): bool {.rtl, extern: "nos$1".} =
  ## Returns True if both pathname arguments refer to files with identical
  ## binary content.
  const
    bufSize = 8192 # 8K buffer
  var
    a, b: TFile
  if not open(a, path1): return false
  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)
    if readA != readB:
      result = false
      break
    if readA == 0:
      result = true
      break
    result = equalMem(bufA, bufB, readA)
    if not result: break
    if readA != bufSize: break # end of file
  dealloc(bufA)
  dealloc(bufB)
  close(a)
  close(b)

proc copyFile*(source, dest: string) {.rtl, extern: "nos$1".} =
  ## Copies a file from `source` to `dest`. If this fails,
  ## `EOS` is raised.
  when defined(Windows):
    if CopyFileA(source, dest, 0'i32) == 0'i32: OSError()
  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()
    if not open(d, dest, fmWrite):
      close(s)
      OSError()
    var buf = alloc(bufsize)
    while True:
      var bytesread = readBuffer(s, buf, bufsize)
      if bytesread > 0:
        var byteswritten = writeBuffer(d, buf, bytesread)
        if bytesread != bytesWritten:
          dealloc(buf)
          close(s)
          close(d)
          OSError()
      if bytesread != bufSize: break
    dealloc(buf)
    close(s)
    close(d)

proc moveFile*(source, dest: string) {.rtl, extern: "nos$1".} =
  ## Moves a file from `source` to `dest`. If this fails, `EOS` is raised.
  if crename(source, dest) != 0'i32: OSError()

proc removeFile*(file: string) {.rtl, extern: "nos$1".} =
  ## Removes the `file`. If this fails, `EOS` is raised.
  if cremove(file) != 0'i32: OSError()

proc executeShellCommand*(command: string): int {.deprecated.} =
  ## **Deprecated since version 0.8.2**: Use `execShellCmd` instead.
  result = csystem(command)

proc execShellCmd*(command: string): int {.rtl, extern: "nos$1".} =
  ## Executes a `shell command`:idx:.
  ##
  ## Command has the form 'program args' where args are the command
  ## line arguments given to program. The proc returns the error code
  ## of the shell when it has finished. The proc does not return until
  ## the process has finished. To execute a program without having a
  ## shell involved, use the `execProcess` proc of the `osproc`
  ## module.
  result = csystem(command)

# Environment handling cannot be put into RTL, because the ``envPairs`` 
# iterator depends on ``environment``.

var
  envComputed: bool = false
  environment: seq[string] = @[]

when defined(windows):
  # because we support Windows GUI applications, things get really
  # messy here...
  proc strEnd(cstr: CString, c = 0'i32): CString {.
    importc: "strchr", header: "<string.h>".}

  proc getEnvVarsC() =
    if not envComputed:
      var
        env = getEnvironmentStringsA()
        e = env
      if e == nil: return # an error occured
      while True:
        var eend = strEnd(e)
        add(environment, $e)
        e = cast[CString](cast[TAddress](eend)+1)
        if eend[1] == '\0': break
      envComputed = true
      discard FreeEnvironmentStringsA(env)

else:
  const
    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, 
    # 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
    # environ is needed, the _NSGetEnviron() routine, defined in
    # <crt_externs.h>, can be used to retrieve the address of environ
    # at runtime.
    proc NSGetEnviron(): ptr cstringArray {.
      importc: "_NSGetEnviron", header: "<crt_externs.h>".}
  else:
    var gEnv {.importc: "environ".}: cstringArray

  proc getEnvVarsC() =
    # retrieves the variables of char** env of C's main proc
    if not envComputed:
      when useNSGetEnviron:
        var gEnv = NSGetEnviron()^
      var i = 0
      while True:
        if gEnv[i] == nil: break
        add environment, $gEnv[i]
        inc(i)
      envComputed = true

proc findEnvVar(key: string): int =
  getEnvVarsC()
  var temp = key & '='
  for i in 0..high(environment):
    if startsWith(environment[i], temp): return i
  return -1

proc getEnv*(key: string): string =
  ## Returns the value of the `environment variable`:idx: named `key`.
  ##
  ## If the variable does not exist, "" is returned. To distinguish
  ## whether a variable exists or it's value is just "", call
  ## `existsEnv(key)`.
  var i = findEnvVar(key)
  if i >= 0:
    return copy(environment[i], find(environment[i], '=')+1)
  else:
    var env = cgetenv(key)
    if env == nil: return ""
    result = $env

proc existsEnv*(key: string): bool =
  ## Checks whether the environment variable named `key` exists.
  ## Returns true if it exists, false otherwise.
  if cgetenv(key) != nil: return true
  else: return findEnvVar(key) >= 0

proc putEnv*(key, val: string) =
  ## Sets the value of the `environment variable`:idx: named `key` to `val`.
  ## If an error occurs, `EInvalidEnvVar` is raised.

  # Note: by storing the string in the environment sequence,
  # we gurantee that we don't free the memory before the program
  # ends (this is needed for POSIX compliance). It is also needed so that
  # the process itself may access its modified environment variables!
  var indx = findEnvVar(key)
  if indx >= 0:
    environment[indx] = key & '=' & val
  else:
    add environment, (key & '=' & val)
    indx = high(environment)
  when defined(unix):
    if cputenv(environment[indx]) != 0'i32:
      OSError()
  else:
    if SetEnvironmentVariableA(key, val) == 0'i32:
      OSError()

iterator iterOverEnvironment*(): tuple[key, value: string] {.deprecated.} =
  ## Iterate over all environments variables. In the first component of the
  ## tuple is the name of the current variable stored, in the second its value.
  ## **Deprecated since version 0.8.2**: Use `envPairs` instead.
  getEnvVarsC()
  for i in 0..high(environment):
    var p = find(environment[i], '=')
    yield (copy(environment[i], 0, p-1), copy(environment[i], p+1))

iterator envPairs*(): tuple[key, value: string] =
  ## 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 (copy(environment[i], 0, p-1), copy(environment[i], p+1))

iterator walkFiles*(pattern: string): string =
  ## Iterate over all the files that match the `pattern`. On POSIX this uses
  ## the `glob`:idx: call.
  ##
  ## `pattern` is OS dependant, but at least the "\*.ext"
  ## notation is supported.
  when defined(windows):
    var
      f: TWin32FindData
      res: int
    res = findfirstFileA(pattern, f)
    if res != -1:
      while true:
        if f.cFileName[0] != '.':
          yield splitFile(pattern).dir / extractFilename($f.cFileName)
        if findnextFileA(res, f) == 0'i32: break
      findclose(res)
  else: # here we use glob
    var
      f: TGlob
      res: int
    f.gl_offs = 0
    f.gl_pathc = 0
    f.gl_pathv = nil
    res = glob(pattern, 0, nil, addr(f))
    if res == 0:
      for i in 0.. f.gl_pathc - 1:
        assert(f.gl_pathv[i] != nil)
        yield $f.gl_pathv[i]
    globfree(addr(f))

type
  TPathComponent* = enum  ## Enumeration specifying a path component.
    pcFile,               ## path refers to a file
    pcLinkToFile,         ## path refers to a symbolic link to a file
    pcDir,                ## path refers to a directory
    pcLinkToDir           ## path refers to a symbolic link to a directory

const
  pcDirectory* {.deprecated.} = pcDir ## deprecated alias 
  pcLinkToDirectory* {.deprecated.} = pcLinkToDir ## deprecated alias

iterator walkDir*(dir: string): tuple[kind: TPathComponent, path: string] =
  ## walks over the directory `dir` and yields for each directory or file in
  ## `dir`. The component type and full path for each item is returned.
  ## Walking is not recursive.
  ## Example: This directory structure::
  ##   dirA / dirB / fileB1.txt
  ##        / dirC
  ##        / fileA1.txt
  ##        / fileA2.txt
  ##
  ## and this code:
  ##
  ## .. code-block:: Nimrod
  ##     for kind, path in walkDir("dirA"):
  ##       echo(path)
  ##
  ## produces this output (but not necessarily in this order!)::
  ##   dirA/dirB
  ##   dirA/dirC
  ##   dirA/fileA1.txt
  ##   dirA/fileA2.txt
  when defined(windows):
    var f: TWIN32_Find_Data
    var h = findfirstFileA(dir / "*", f)
    if h != -1:
      while true:
        var k = pcFile
        if f.cFilename[0] != '.':
          if (f.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) != 0'i32:
            k = pcDir
          yield (k, dir / extractFilename($f.cFilename))
        if findnextFileA(h, f) == 0'i32: break
      findclose(h)
  else:
    var d = openDir(dir)
    if d != nil:
      while true:
        var x = readDir(d)
        if x == nil: break
        var y = $x.d_name
        if y != "." and y != "..":
          var s: TStat
          y = dir / y
          if lstat(y, s) < 0'i32: break
          var k = pcFile
          if S_ISDIR(s.st_mode): k = pcDir
          if S_ISLNK(s.st_mode): k = succ(k)
          yield (k, y)
      discard closeDir(d)

iterator walkDirRec*(dir: string, filter={pcFile, pcDir}): string =
  ## 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:
  ##
  ## ---------------------   ---------------------------------------------
  ## filter                  meaning
  ## ---------------------   ---------------------------------------------
  ## ``pcFile``              yield real files
  ## ``pcLinkToFile``        yield symbolic links to files
  ## ``pcDir``               follow real directories
  ## ``pcLinkToDir``         follow symbolic links to directories
  ## ---------------------   ---------------------------------------------
  ## 
  var stack = @[dir]
  while stack.len > 0:
    for k,p in walkDir(stack.pop()):
      if k in filter:
        case k
        of pcFile, pcLinkToFile: yield p
        of pcDir, pcLinkToDir: stack.add(p)

proc rawRemoveDir(dir: string) = 
  when defined(windows):
    if RemoveDirectoryA(dir) == 0'i32: OSError()
  else:
    if rmdir(dir) != 0'i32: OSError()

proc removeDir*(dir: string) {.rtl, extern: "nos$1".} =
  ## Removes the directory `dir` including all subdirectories and files
  ## in `dir` (recursively). If this fails, `EOS` is raised.
  for kind, path in walkDir(dir): 
    case kind
    of pcFile, pcLinkToFile, pcLinkToDir: removeFile(path)
    of pcDir: removeDir(path)
  rawRemoveDir(dir)

proc rawCreateDir(dir: string) =
  when defined(unix):
    if mkdir(dir, 0o711) != 0'i32 and errno != EEXIST:
      OSError()
  else:
    if CreateDirectoryA(dir, nil) == 0'i32 and GetLastError() != 183'i32:
      OSError()

proc createDir*(dir: string) {.rtl, extern: "nos$1".} =
  ## 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 
  ## indicate an error.
  for i in 1.. dir.len-1:
    if dir[i] in {dirsep, altsep}: rawCreateDir(copy(dir, 0, i-1))
  rawCreateDir(dir)

proc parseCmdLine*(c: string): seq[string] {.
  noSideEffect, rtl, extern: "nos$1".} =
  ## 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
  ## (see http://msdn.microsoft.com/en-us/library/17w5ykft.aspx):
  ##
  ## * Arguments are delimited by white space, which is either a space or a tab.
  ## * The caret character (^) is not recognized as an escape character or
  ##   delimiter. The character is handled completely by the command-line parser
  ##   in the operating system before being passed to the argv array in the
  ##   program.
  ## * A string surrounded by double quotation marks ("string") is interpreted
  ##   as a single argument, regardless of white space contained within. A
  ##   quoted string can be embedded in an argument.
  ## * A double quotation mark preceded by a backslash (\") is interpreted as a
  ##   literal double quotation mark character (").
  ## * Backslashes are interpreted literally, unless they immediately precede
  ##   a double quotation mark.
  ## * If an even number of backslashes is followed by a double quotation mark,
  ##   one backslash is placed in the argv array for every pair of backslashes,
  ##   and the double quotation mark is interpreted as a string delimiter.
  ## * If an odd number of backslashes is followed by a double quotation mark,
  ##   one backslash is placed in the argv array for every pair of backslashes,
  ##   and the double quotation mark is "escaped" by the remaining backslash,
  ##   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 
  ## occurs within ``"`` or ``'`` quotes.
  result = @[]
  var i = 0
  var a = ""
  while true:
    setLen(a, 0)
    while c[i] == ' ' or c[i] == '\t': inc(i)
    when defined(windows):
      # parse a single argument according to the above rules:
      if c[i] == '\0': break
      var inQuote = false
      while true:
        case c[i]        
        of '\0': break
        of '\\':
          var j = i
          while c[j] == '\\': inc(j)
          if c[j] == '"': 
            for k in 1..(j-i) div 2: a.add('\\')
            if (j-i) mod 2 == 0: 
              i = j
            else: 
              a.add('"')
              i = j+1
          else: 
            a.add(c[i])
            inc(i)
        of '"':
          inc(i)
          if not inQuote: inQuote = true
          elif c[i] == '"': 
            a.add(c[i])
            inc(i)
          else:
            inQuote = false
            break
        of ' ', '\t': 
          if not inQuote: break
          a.add(c[i])
          inc(i)
        else:
          a.add(c[i])
          inc(i)
    else:
      case c[i]
      of '\'', '\"':
        var delim = c[i]
        inc(i) # skip ' or "
        while c[i] != '\0' and c[i] != delim:
          add a, c[i]
          inc(i)
        if c[i] != '\0': inc(i)
      of '\0': break
      else:
        while c[i] > ' ':
          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".} =
  ## 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()
    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:
    var res = GetFileAttributesA(filename)
    if res == -1'i32: OSError()
    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".} =
  ## 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()
  else:
    var res = GetFileAttributesA(filename)
    if res == -1'i32: OSError()
    if fpUserWrite in permissions: 
      res = res and not FILE_ATTRIBUTE_READONLY
    else:
      res = res or FILE_ATTRIBUTE_READONLY
    if SetFileAttributesA(filename, res) == - 1'i32: 
      OSError()
  
proc inclFilePermissions*(filename: string, 
                          permissions: set[TFilePermission]) {.
  rtl, extern: "nos$1".} =
  ## a convenience procedure for: 
  ##
  ## .. code-block:: nimrod
  ##   setFilePermissions(filename, getFilePermissions(filename)+permissions)
  setFilePermissions(filename, getFilePermissions(filename)+permissions)

proc exclFilePermissions*(filename: string, 
                          permissions: set[TFilePermission]) {.
  rtl, extern: "nos$1".} =
  ## a convenience procedure for: 
  ##
  ## .. code-block:: nimrod
  ##   setFilePermissions(filename, getFilePermissions(filename)-permissions)
  setFilePermissions(filename, getFilePermissions(filename)-permissions)

proc getHomeDir*(): string {.rtl, extern: "nos$1".} =
  ## Returns the home directory of the current user.
  when defined(windows): return getEnv("USERPROFILE") & "\\"
  else: return getEnv("HOME") & "/"

proc getConfigDir*(): string {.rtl, extern: "nos$1".} =
  ## Returns the config directory of the current user for applications.
  when defined(windows): return getEnv("APPDATA") & "\\"
  else: return getEnv("HOME") & "/.config/"

when defined(windows):
  # Since we support GUI applications with Nimrod, we sometimes generate
  # a WinMain entry proc. But a WinMain proc has no access to the parsed
  # command line arguments. The way to get them differs. Thus we parse them
  # ourselves. This has the additional benefit that the program's behaviour
  # is always the same -- independent of the used C compiler.
  var
    ownArgv: seq[string]

  proc paramCount*(): int {.rtl, extern: "nos$1".} =
    ## Returns the number of `command line arguments`:idx: given to the
    ## application.
    if isNil(ownArgv): ownArgv = parseCmdLine($getCommandLineA())
    result = ownArgv.len-1

  proc paramStr*(i: int): string {.rtl, extern: "nos$1".} =
    ## Returns the `i`-th `command line argument`:idx: given to the
    ## application.
    ##
    ## `i` should be in the range `1..paramCount()`, else
    ## the `EOutOfIndex` exception is raised.
    if isNil(ownArgv): ownArgv = parseCmdLine($getCommandLineA())
    return ownArgv[i]

elif not defined(createNimRtl):
  # On Posix, there is no portable way to get the command line from a DLL.
  var
    cmdCount {.importc: "cmdCount".}: cint
    cmdLine {.importc: "cmdLine".}: cstringArray

  proc paramStr*(i: int): string =
    if i < cmdCount and i >= 0: return $cmdLine[i]
    raise newException(EInvalidIndex, "invalid index")

  proc paramCount*(): int = return cmdCount-1

when defined(linux) or defined(solaris) or defined(bsd) or defined(aix):
  proc getApplAux(procPath: string): string =
    result = newString(256)
    var len = readlink(procPath, result, 256)
    if len > 256:
      result = newString(len+1)
      len = readlink(procPath, result, len)
    setlen(result, len)

when defined(macosx):
  # 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 int32) {.
    importc: "_NSGetExecutablePath", header: "<sys/param.h>".}
  proc getExecPath2(c: cstring, size: var int32): bool {.
    importc: "_NSGetExecutablePath", header: "<mach-o/dyld.h>".}

proc getAppFilename*(): string {.rtl, extern: "nos$1".} =
  ## Returns the filename of the application's executable.

  # Linux: /proc/<pid>/exe
  # Solaris:
  # /proc/<pid>/object/a.out (filename only)
  # /proc/<pid>/path/a.out (complete pathname)
  # *BSD (and maybe Darwin too):
  # /proc/<pid>/file
  when defined(windows):
    result = newString(256)
    var len = getModuleFileNameA(0, result, 256)
    setlen(result, int(len))
  elif defined(linux) or defined(aix):
    result = getApplAux("/proc/self/exe")
  elif defined(solaris):
    result = getApplAux("/proc/" & $getpid() & "/path/a.out")
  elif defined(bsd):
    result = getApplAux("/proc/" & $getpid() & "/file")
  elif defined(macosx):
    var size: int32
    getExecPath1(nil, size)
    result = newString(int(size))
    if getExecPath2(result, size):
      result = "" # error!
  else:
    # little heuristic that may work on other POSIX-like systems:
    result = getEnv("_")
    if len(result) == 0:
      result = 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(getEnv("PATH"), {PathSep}):
          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`` 
  ## 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`` 
  ## instead.
  result = splitFile(getAppFilename()).dir

proc getAppDir*(): string {.rtl, extern: "nos$1".} =
  ## Returns the directory of the application's executable.
  result = splitFile(getAppFilename()).dir

proc sleep*(milsecs: int) {.rtl, extern: "nos$1".} =
  ## sleeps `milsecs` milliseconds.
  when defined(windows):
    winlean.sleep(int32(milsecs))
  else:
    var a, b: Ttimespec
    a.tv_sec = TTime(milsecs div 1000)
    a.tv_nsec = (milsecs mod 1000) * 1000
    discard posix.nanosleep(a, b)

proc getFileSize*(file: string): biggestInt {.rtl, extern: "nos$1".} =
  ## returns the file size of `file`. Can raise ``EOS``. 
  when defined(windows):
    var a: TWin32FindData
    var resA = findfirstFileA(file, a)
    if resA == -1: OSError()
    result = rdFileSize(a)
    findclose(resA)
  else:
    var f: TFile
    if open(f, file): 
      result = getFileSize(f)
      close(f)
    else: OSError()

proc findExe*(exe: string): string = 
  ## 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` 
  ## is added an ``.exe`` file extension if it has no extension.
  result = addFileExt(exe, os.exeExt)
  if ExistsFile(result): return
  var path = os.getEnv("PATH")
  for candidate in split(path, pathSep): 
    var x = candidate / result
    if ExistsFile(x): return x
  result = ""

{.pop.}