summary refs log tree commit diff stats
path: root/lib
diff options
context:
space:
mode:
authorZahary Karadjov <zahary@gmail.com>2011-12-07 00:53:27 +0200
committerZahary Karadjov <zahary@gmail.com>2011-12-07 00:53:27 +0200
commit0e609d2101535b4ca2640b68890b2c575ed3678d (patch)
tree0015005ea5610c3034447afa6b07d60b8db1dabf /lib
parent446b0421888cf32a58d22d4152dfbf98d6c12a10 (diff)
downloadNim-0e609d2101535b4ca2640b68890b2c575ed3678d.tar.gz
New implementation for os.sameFile on Windows
Hard-links on Windows are now treated just as they are on POSIX.
The new implementation is faster than the previous, but still it's quite
slower than fstat (use with caution).
Diffstat (limited to 'lib')
-rwxr-xr-xlib/pure/os.nim49
-rwxr-xr-xlib/pure/times.nim2
-rwxr-xr-xlib/windows/winlean.nim40
3 files changed, 66 insertions, 25 deletions
diff --git a/lib/pure/os.nim b/lib/pure/os.nim
index a8fafd5de..f22a53dd8 100755
--- a/lib/pure/os.nim
+++ b/lib/pure/os.nim
@@ -567,26 +567,45 @@ proc isAbsolute*(path: string): bool {.rtl, noSideEffect, extern: "nos$1".} =
     result = path[0] == '/'
 
 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.
+  ## 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):
-    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)
+    var success = true
+    
+    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)
+
+    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:
+        result = fi1.dwVolumeSerialNumber == fi2.dwVolumeSerialNumber and
+                 fi1.nFileIndexHigh == fi2.nFileIndexHigh and
+                 fi1.nFileIndexLow == fi2.nFileIndexLow
+      else: success = false
+    else: success = false
+
+    discard CloseHandle(f1)
+    discard CloseHandle(f2)
+
+    if not success:
+      OSError()
+
   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
+      OSError()
     else:
       result = a.st_dev == b.st_dev and a.st_ino == b.st_ino
 
diff --git a/lib/pure/times.nim b/lib/pure/times.nim
index a4ac59673..f73a48bea 100755
--- a/lib/pure/times.nim
+++ b/lib/pure/times.nim
@@ -302,7 +302,7 @@ when not defined(ECMAScript):
         posix_gettimeofday(a)
         result = toFloat(a.tv_sec) + toFloat(a.tv_usec)*0.00_0001
       elif defined(windows):
-        var f: winlean.Filetime
+        var f: winlean.TFiletime
         GetSystemTimeAsFileTime(f)
         var i64 = rdFileTime(f) - epochDiff
         var secs = i64 div rateDiff
diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim
index 18e287bfa..acc28e58b 100755
--- a/lib/windows/winlean.nim
+++ b/lib/windows/winlean.nim
@@ -47,6 +47,22 @@ type
     dwProcessId*: int32
     dwThreadId*: int32
 
+  TFILETIME* {.final, pure.} = object ## CANNOT BE int64 BECAUSE OF ALIGNMENT
+    dwLowDateTime*: DWORD
+    dwHighDateTime*: DWORD
+  
+  TBY_HANDLE_FILE_INFORMATION* {.final, pure.} = object
+    dwFileAttributes*: DWORD
+    ftCreationTime*: TFILETIME
+    ftLastAccessTime*: TFILETIME
+    ftLastWriteTime*: TFILETIME
+    dwVolumeSerialNumber*: DWORD
+    nFileSizeHigh*: DWORD
+    nFileSizeLow*: DWORD
+    nNumberOfLinks*: DWORD
+    nFileIndexHigh*: DWORD
+    nFileIndexLow*: DWORD
+
 const
   STARTF_USESHOWWINDOW* = 1'i32
   STARTF_USESTDHANDLES* = 256'i32
@@ -149,14 +165,11 @@ const
 
   MAX_PATH* = 260
 type
-  FILETIME* {.final, pure.} = object ## CANNOT BE int64 BECAUSE OF ALIGNMENT
-    dwLowDateTime*: int32
-    dwHighDateTime*: int32
   TWIN32_FIND_DATA* {.pure.} = object
     dwFileAttributes*: int32
-    ftCreationTime*: FILETIME
-    ftLastAccessTime*: FILETIME
-    ftLastWriteTime*: FILETIME
+    ftCreationTime*: TFILETIME
+    ftLastAccessTime*: TFILETIME
+    ftLastWriteTime*: TFILETIME
     nFileSizeHigh*: int32
     nFileSizeLow*: int32
     dwReserved0: int32
@@ -192,13 +205,13 @@ proc FreeEnvironmentStringsA*(para1: cstring): int32 {.
 
 proc GetCommandLineA*(): CString {.importc, stdcall, dynlib: "kernel32".}
 
-proc rdFileTime*(f: FILETIME): int64 = 
+proc rdFileTime*(f: TFILETIME): int64 = 
   result = ze64(f.dwLowDateTime) or (ze64(f.dwHighDateTime) shl 32)
 
 proc rdFileSize*(f: TWin32FindData): int64 = 
   result = ze64(f.nFileSizeLow) or (ze64(f.nFileSizeHigh) shl 32)
 
-proc GetSystemTimeAsFileTime*(lpSystemTimeAsFileTime: var FileTime) {.
+proc GetSystemTimeAsFileTime*(lpSystemTimeAsFileTime: var TFILETIME) {.
   importc: "GetSystemTimeAsFileTime", dynlib: "kernel32", stdcall.}
 
 proc Sleep*(dwMilliseconds: int32){.stdcall, dynlib: "kernel32",
@@ -209,12 +222,16 @@ proc ShellExecute*(HWND: THandle, lpOperation, lpFile,
                    nShowCmd: int32): THandle{.
     stdcall, dynlib: "shell32.dll", importc: "ShellExecuteA".}
 
+proc GetFileInformationByHandle*(hFile: THandle,
+  lpFileInformation: ptr TBY_HANDLE_FILE_INFORMATION): WINBOOL{.
+    stdcall, dynlib: "kernel32", importc: "GetFileInformationByHandle".}
+
 const
   WSADESCRIPTION_LEN* = 256
   WSASYS_STATUS_LEN* = 128
   FD_SETSIZE* = 64
   MSG_PEEK* = 2
-  
+ 
   INADDR_ANY* = 0
   INADDR_LOOPBACK* = 0x7F000001
   INADDR_BROADCAST* = -1
@@ -410,6 +427,9 @@ const
   GENERIC_READ* = 0x80000000'i32
   GENERIC_ALL* = 0x10000000'i32
   FILE_SHARE_READ* = 1'i32
+  FILE_SHARE_DELETE* = 4'i32
+  FILE_SHARE_WRITE* = 2'i32
+ 
   CREATE_ALWAYS* = 2'i32
   OPEN_EXISTING* = 3'i32
   FILE_BEGIN* = 0'i32
@@ -421,6 +441,8 @@ const
   FILE_MAP_WRITE* = 2'i32
   INVALID_FILE_SIZE* = -1'i32
 
+  FILE_FLAG_BACKUP_SEMANTICS* = 33554432'i32
+
 proc CreateFileA*(lpFileName: cstring, dwDesiredAccess, dwShareMode: DWORD,
                   lpSecurityAttributes: pointer,
                   dwCreationDisposition, dwFlagsAndAttributes: DWORD,