changeset 9238:40196d036a71

util: add realpath() for getting the 'true' path. The function is implemented for Mac OS X using the F_GETPATH fcntl, and a basic implementation for Windows is provided as well. On other POSIX systems, vanilla os.path.realpath() is used.
author Dan Villiom Podlaski Christiansen <danchr@gmail.com>
date Sun, 26 Jul 2009 17:25:08 +0200
parents abc198bca7c1
children 26fa0e31011d
files mercurial/posix.py mercurial/windows.py
diffstat 2 files changed, 48 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/mercurial/posix.py	Fri Jul 24 00:40:00 2009 +0200
+++ b/mercurial/posix.py	Sun Jul 26 17:25:08 2009 +0200
@@ -7,7 +7,7 @@
 
 from i18n import _
 import osutil
-import os, sys, errno, stat, getpass, pwd, grp
+import os, sys, errno, stat, getpass, pwd, grp, fcntl
 
 posixfile = open
 nulldev = '/dev/null'
@@ -104,6 +104,44 @@
 def localpath(path):
     return path
 
+if sys.platform == 'darwin':
+    def realpath(path):
+        '''
+        Returns the true, canonical file system path equivalent to the given
+        path.
+
+        Equivalent means, in this case, resulting in the same, unique
+        file system link to the path. Every file system entry, whether a file,
+        directory, hard link or symbolic link or special, will have a single
+        path preferred by the system, but may allow multiple, differing path
+        lookups to point to it.
+
+        Most regular UNIX file systems only allow a file system entry to be
+        looked up by its distinct path. Obviously, this does not apply to case
+        insensitive file systems, whether case preserving or not. The most
+        complex issue to deal with is file systems transparently reencoding the
+        path, such as the non-standard Unicode normalisation required for HFS+
+        and HFSX.
+        '''
+        # Constants copied from /usr/include/sys/fcntl.h
+        F_GETPATH = 50
+        O_SYMLINK = 0x200000
+
+        try:
+            fd = os.open(path, O_SYMLINK)
+        except OSError, err:
+            if err.errno is errno.ENOENT:
+                return path
+            raise
+
+        try:
+            return fcntl.fcntl(fd, F_GETPATH, '\0' * 1024).rstrip('\0')
+        finally:
+            os.close(fd)
+else:
+    # Fallback to the likely inadequate Python builtin function.
+    realpath = os.path.realpath
+
 def shellquote(s):
     if os.sys.platform == 'OpenVMS':
         return '"%s"' % s
--- a/mercurial/windows.py	Fri Jul 24 00:40:00 2009 +0200
+++ b/mercurial/windows.py	Sun Jul 26 17:25:08 2009 +0200
@@ -126,6 +126,15 @@
 def normpath(path):
     return pconvert(os.path.normpath(path))
 
+def realpath(path):
+    '''
+    Returns the true, canonical file system path equivalent to the given
+    path.
+    '''
+    # TODO: There may be a more clever way to do this that also handles other,
+    # less common file systems.
+    return os.path.normpath(os.path.normcase(os.path.realpath(path)))
+
 def samestat(s1, s2):
     return False