Antoine Pitrou avatar Antoine Pitrou committed 4c0c41c

Split extended NT paths correctly

Comments (0)

Files changed (2)

         {'LPT%d' % i for i in range(1, 10)}
         )
 
+    # Interesting findings about extended paths:
+    # - '\\?\c:\a', '//?/c:\a' and '//?/c:/a' are all supported
+    #   but '\\?\c:/a' is not
+    # - extended paths are always absolute; "relative" extended paths will
+    #   fail.
+
     def splitroot(self, part, sep=sep):
         first = part[0:1]
         second = part[1:2]
+        if (second == sep and first == sep):
+            # XXX extended paths should also disable the collapsing of "."
+            # components (according to MSDN docs).
+            prefix, part = self._split_extended_path(part)
+            first = part[0:1]
+            second = part[1:2]
+        else:
+            prefix = ''
         third = part[2:3]
         if (second == sep and first == sep and third != sep):
             # is a UNC path:
                 if index2 != index + 1:
                     if index2 == -1:
                         index2 = len(part)
-                    return part[:index2], sep, part[index2+1:]
+                    if prefix:
+                        return prefix + part[1:index2], sep, part[index2+1:]
+                    else:
+                        return part[:index2], sep, part[index2+1:]
         drv = root = ''
         if second == ':' and first in self.drive_letters:
             drv = part[:2]
         if first == sep:
             root = first
             part = part.lstrip(sep)
-        return drv, root, part
+        return prefix + drv, root, part
 
     def casefold(self, s):
         return s.lower()
         # Means fallback on absolute
         return None
 
+    def _split_extended_path(self, s, ext_prefix=ext_namespace_prefix):
+        prefix = ''
+        if s.startswith(ext_prefix):
+            prefix = s[:4]
+            s = s[4:]
+            if s.startswith('UNC\\'):
+                prefix += s[:3]
+                s = '\\' + s[3:]
+        return prefix, s
+
     def _ext_to_normal(self, s):
         # Turn back an extended path into a normal DOS-like path
-        if s.startswith(self.ext_namespace_prefix):
-            s = s[4:]
-            if s.startswith('UNC\\'):
-                s = '\\' + s[3:]
-        return s
+        return self._split_extended_path(s)[1]
 
     def is_reserved(self, parts):
         # NOTE: the rules for reserved names seem somewhat complicated
         check(['a', 'Z:\\\\b\\\\c\\', 'd\\'], ('Z:', '\\', ['Z:\\', 'b', 'c', 'd']))
         # UNC paths
         check(['a', '\\\\b\\c\\\\', 'd'], ('\\\\b\\c', '\\', ['\\\\b\\c\\', 'd']))
+        # Extended paths
+        check(['\\\\?\\c:\\'],          ('\\\\?\\c:', '\\', ['\\\\?\\c:\\']))
+        check(['\\\\?\\c:\\a'],         ('\\\\?\\c:', '\\', ['\\\\?\\c:\\', 'a']))
+        # Extended UNC paths (format is "\\?\UNC\server\share")
+        check(['\\\\?\\UNC\\b\\c'],     ('\\\\?\\UNC\\b\\c', '\\', ['\\\\?\\UNC\\b\\c\\']))
+        check(['\\\\?\\UNC\\b\\c\\d'],  ('\\\\?\\UNC\\b\\c', '\\', ['\\\\?\\UNC\\b\\c\\', 'd']))
 
     def test_splitroot(self):
         f = self.flavour.splitroot
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.