2 # Module 'ntpath' -- common operations on WinNT/Win95 and UEFI pathnames.
4 # Copyright (c) 2015, Daryl McDaniel. All rights reserved.<BR>
5 # Copyright (c) 2011 - 2012, Intel Corporation. All rights reserved.<BR>
6 # This program and the accompanying materials are licensed and made available under
7 # the terms and conditions of the BSD License that accompanies this distribution.
8 # The full text of the license may be found at
9 # http://opensource.org/licenses/bsd-license.
11 # THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
15 """Common pathname manipulations, WindowsNT/95 and UEFI version.
17 Instead of importing this module directly, import os and refer to this
27 from genericpath
import *
28 from genericpath
import _unicode
30 __all__
= ["normcase","isabs","join","splitdrive","split","splitext",
31 "basename","dirname","commonprefix","getsize","getmtime",
32 "getatime","getctime", "islink","exists","lexists","isdir","isfile",
33 "ismount","walk","expanduser","expandvars","normpath","abspath",
34 "splitunc","curdir","pardir","sep","pathsep","defpath","altsep",
35 "extsep","devnull","realpath","supports_unicode_filenames","relpath"]
37 # strings representing various path-related bits and pieces
45 if 'ce' in sys
.builtin_module_names
:
47 elif 'os2' in sys
.builtin_module_names
:
52 # Normalize the case of a pathname and map slashes to backslashes.
53 # Other normalizations (such as optimizing '../' away) are not done
54 # (this is done by normpath).
57 """Normalize case of pathname.
59 Makes all characters lowercase and all slashes into backslashes."""
60 return s
.replace("/", "\\").lower()
63 # Return whether a path is absolute.
64 # Trivial in Posix, harder on the Mac or MS-DOS.
65 # For DOS it is absolute if it starts with a slash or backslash (current
66 # volume), or if a pathname after the volume letter and colon / UNC resource
67 # starts with a slash or backslash.
70 """Test whether a path is absolute"""
72 return s
!= '' and s
[:1] in '/\\'
75 # Join two (or more) paths.
76 def join(path
, *paths
):
77 """Join two or more pathname components, inserting "\\" as needed."""
78 result_drive
, result_path
= splitdrive(path
)
80 p_drive
, p_path
= splitdrive(p
)
81 if p_path
and p_path
[0] in '\\/':
82 # Second path is absolute
83 if p_drive
or not result_drive
:
84 result_drive
= p_drive
87 elif p_drive
and p_drive
!= result_drive
:
88 if p_drive
.lower() != result_drive
.lower():
89 # Different drives => ignore the first path entirely
90 result_drive
= p_drive
93 # Same drive in different case
94 result_drive
= p_drive
95 # Second path is relative to the first
96 if result_path
and result_path
[-1] not in '\\/':
97 result_path
= result_path
+ '\\'
98 result_path
= result_path
+ p_path
99 ## add separator between UNC and non-absolute path
100 if (result_path
and result_path
[0] not in '\\/' and
101 result_drive
and result_drive
[-1:] != ':'):
102 return result_drive
+ sep
+ result_path
103 return result_drive
+ result_path
106 # Split a path in a drive specification (a drive letter followed by a
107 # colon) and the path specification.
108 # It is always true that drivespec + pathspec == p
109 # NOTE: for UEFI (and even Windows) you can have multiple characters to the left
110 # of the ':' for the device or drive spec. This is reflected in the modifications
111 # to splitdrive() and splitunc().
113 """Split a pathname into drive/UNC sharepoint and relative path specifiers.
114 Returns a 2-tuple (drive_or_unc, path); either part may be empty.
117 result = splitdrive(p)
118 It is always true that:
119 result[0] + result[1] == p
121 If the path contained a drive letter, drive_or_unc will contain everything
122 up to and including the colon. e.g. splitdrive("c:/dir") returns ("c:", "/dir")
124 If the path contained a UNC path, the drive_or_unc will contain the host name
125 and share up to but not including the fourth directory separator character.
126 e.g. splitdrive("//host/computer/dir") returns ("//host/computer", "/dir")
128 Paths cannot contain both a drive letter and a UNC path.
132 normp
= p
.replace(altsep
, sep
)
133 if (normp
[0:2] == sep
*2) and (normp
[2:3] != sep
):
135 # vvvvvvvvvvvvvvvvvvvv drive letter or UNC path
136 # \\machine\mountpoint\directory\etc\...
137 # directory ^^^^^^^^^^^^^^^
138 index
= normp
.find(sep
, 2)
141 index2
= normp
.find(sep
, index
+ 1)
142 # a UNC path can't have two slashes in a row
143 # (after the initial two)
144 if index2
== index
+ 1:
148 return p
[:index2
], p
[index2
:]
152 return p
[:index
], p
[index
:]
157 """Split a pathname into UNC mount point and relative path specifiers.
159 Return a 2-tuple (unc, rest); either part may be empty.
160 If unc is not empty, it has the form '//host/mount' (or similar
161 using backslashes). unc+rest is always the input path.
162 Paths containing drive letters never have an UNC part.
165 return '', p
# Drive letter or device name present
167 if firstTwo
== '//' or firstTwo
== '\\\\':
169 # vvvvvvvvvvvvvvvvvvvv equivalent to drive letter
170 # \\machine\mountpoint\directories...
171 # directory ^^^^^^^^^^^^^^^
172 normp
= p
.replace('\\', '/')
173 index
= normp
.find('/', 2)
176 index2
= normp
.find('/', index
+ 1)
177 # a UNC path can't have two slashes in a row
178 # (after the initial two)
179 if index2
== index
+ 1:
183 return p
[:index2
], p
[index2
:]
187 # Split a path in head (everything up to the last '/') and tail (the
188 # rest). After the trailing '/' is stripped, the invariant
189 # join(head, tail) == p holds.
190 # The resulting head won't end in '/' unless it is the root.
195 Return tuple (head, tail) where tail is everything after the final slash.
196 Either part may be empty."""
199 # set i to index beyond p's last slash
201 while i
and p
[i
-1] not in '/\\':
203 head
, tail
= p
[:i
], p
[i
:] # now tail has no slashes
204 # remove trailing slashes from head, unless it's all slashes
206 while head2
and head2
[-1] in '/\\':
209 return d
+ head
, tail
212 # Split a path in root and extension.
213 # The extension is everything starting at the last dot in the last
214 # pathname component; the root is everything before that.
215 # It is always true that root + ext == p.
218 return genericpath
._splitext
(p
, sep
, altsep
, extsep
)
219 splitext
.__doc
__ = genericpath
._splitext
.__doc
__
222 # Return the tail (basename) part of a path.
225 """Returns the final component of a pathname"""
229 # Return the head (dirname) part of a path.
232 """Returns the directory component of a pathname"""
235 # Is a path a symbolic link?
236 # This will always return false on systems where posix.lstat doesn't exist.
239 """Test for symbolic link.
240 On WindowsNT/95 and OS/2 always returns false
244 # alias exists to lexists
247 # Is a path a mount point? Either a root (with or without drive letter)
248 # or an UNC path with at most a / or \ after the mount point.
251 """Test whether a path is a mount point (defined as root of drive)"""
252 unc
, rest
= splitunc(path
)
254 return rest
in ("", "/", "\\")
255 p
= splitdrive(path
)[1]
256 return len(p
) == 1 and p
[0] in '/\\'
259 # Directory tree walk.
260 # For each directory under top (including top itself, but excluding
261 # '.' and '..'), func(arg, dirname, filenames) is called, where
262 # dirname is the name of the directory and filenames is the list
263 # of files (and subdirectories etc.) in the directory.
264 # The func may modify the filenames list, to implement a filter,
265 # or to impose a different order of visiting.
267 def walk(top
, func
, arg
):
268 """Directory tree walk with callback function.
270 For each directory in the directory tree rooted at top (including top
271 itself, but excluding '.' and '..'), call func(arg, dirname, fnames).
272 dirname is the name of the directory, and fnames a list of the names of
273 the files and subdirectories in dirname (excluding '.' and '..'). func
274 may modify the fnames list in-place (e.g. via del or slice assignment),
275 and walk will only recurse into the subdirectories whose names remain in
276 fnames; this can be used to implement a filter, or to impose a specific
277 order of visiting. No semantics are defined for, or required of, arg,
278 beyond that arg is always passed to func. It can be used, e.g., to pass
279 a filename pattern, or a mutable object designed to accumulate
280 statistics. Passing None for arg is common."""
281 warnings
.warnpy3k("In 3.x, os.path.walk is removed in favor of os.walk.",
284 names
= os
.listdir(top
)
287 func(arg
, top
, names
)
289 name
= join(top
, name
)
291 walk(name
, func
, arg
)
294 # Expand paths beginning with '~' or '~user'.
295 # '~' means $HOME; '~user' means that user's home directory.
296 # If the path doesn't begin with '~', or if the user or $HOME is unknown,
297 # the path is returned unchanged (leaving error reporting to whatever
298 # function is called with the expanded path as argument).
299 # See also module 'glob' for expansion of *, ? and [...] in pathnames.
300 # (A function should also be defined to do full *sh-style environment
301 # variable expansion.)
303 def expanduser(path
):
304 """Expand ~ and ~user constructs.
306 If user or $HOME is unknown, do nothing."""
310 while i
< n
and path
[i
] not in '/\\':
313 if 'HOME' in os
.environ
:
314 userhome
= os
.environ
['HOME']
315 elif 'USERPROFILE' in os
.environ
:
316 userhome
= os
.environ
['USERPROFILE']
317 elif not 'HOMEPATH' in os
.environ
:
321 drive
= os
.environ
['HOMEDRIVE']
324 userhome
= join(drive
, os
.environ
['HOMEPATH'])
327 userhome
= join(dirname(userhome
), path
[1:i
])
329 return userhome
+ path
[i
:]
332 # Expand paths containing shell variable substitutions.
333 # The following rules apply:
334 # - no expansion within single quotes
335 # - '$$' is translated into '$'
336 # - '%%' is translated into '%' if '%%' are not seen in %var1%%var2%
337 # - ${varname} is accepted.
338 # - $varname is accepted.
339 # - %varname% is accepted.
340 # - varnames can be made out of letters, digits and the characters '_-'
341 # (though is not verified in the ${varname} and %varname% cases)
342 # XXX With COMMAND.COM you can use any characters in a variable name,
343 # XXX except '^|<>='.
345 def expandvars(path
):
346 """Expand shell variables of the forms $var, ${var} and %var%.
348 Unknown variables are left unchanged."""
349 if '$' not in path
and '%' not in path
:
352 varchars
= string
.ascii_letters
+ string
.digits
+ '_-'
353 if isinstance(path
, _unicode
):
354 encoding
= sys
.getfilesystemencoding()
356 return os
.environ
[var
.encode(encoding
)].decode(encoding
)
359 return os
.environ
[var
]
363 while index
< pathlen
:
365 if c
== '\'': # no expansion within single quotes
366 path
= path
[index
+ 1:]
369 index
= path
.index('\'')
370 res
= res
+ '\'' + path
[:index
+ 1]
374 elif c
== '%': # variable or '%'
375 if path
[index
+ 1:index
+ 2] == '%':
379 path
= path
[index
+1:]
382 index
= path
.index('%')
384 res
= res
+ '%' + path
389 res
= res
+ getenv(var
)
391 res
= res
+ '%' + var
+ '%'
392 elif c
== '$': # variable or '$$'
393 if path
[index
+ 1:index
+ 2] == '$':
396 elif path
[index
+ 1:index
+ 2] == '{':
397 path
= path
[index
+2:]
400 index
= path
.index('}')
403 res
= res
+ getenv(var
)
405 res
= res
+ '${' + var
+ '}'
407 res
= res
+ '${' + path
412 c
= path
[index
:index
+ 1]
413 while c
!= '' and c
in varchars
:
416 c
= path
[index
:index
+ 1]
418 res
= res
+ getenv(var
)
420 res
= res
+ '$' + var
429 # Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A\B.
430 # Previously, this function also truncated pathnames to 8+3 format,
431 # but as this module is called "ntpath", that's obviously wrong!
434 """Normalize path, eliminating double slashes, etc."""
435 # Preserve unicode (if path is unicode)
436 backslash
, dot
= (u
'\\', u
'.') if isinstance(path
, _unicode
) else ('\\', '.')
437 if path
.startswith(('\\\\.\\', '\\\\?\\')):
438 # in the case of paths with these prefixes:
439 # \\.\ -> device names
440 # \\?\ -> literal paths
441 # do not do any normalization, but return the path unchanged
443 path
= path
.replace("/", "\\")
444 prefix
, path
= splitdrive(path
)
445 # We need to be careful here. If the prefix is empty, and the path starts
446 # with a backslash, it could either be an absolute path on the current
447 # drive (\dir1\dir2\file) or a UNC filename (\\server\mount\dir1\file). It
448 # is therefore imperative NOT to collapse multiple backslashes blindly in
450 # The code below preserves multiple backslashes when there is no drive
451 # letter. This means that the invalid filename \\\a\b is preserved
452 # unchanged, where a\\\b is normalised to a\b. It's not clear that there
453 # is any better behaviour for such edge cases.
455 # No drive letter - preserve initial backslashes
456 while path
[:1] == "\\":
457 prefix
= prefix
+ backslash
460 # We have a drive letter - collapse initial backslashes
461 if path
.startswith("\\"):
462 prefix
= prefix
+ backslash
463 path
= path
.lstrip("\\")
464 comps
= path
.split("\\")
466 while i
< len(comps
):
467 if comps
[i
] in ('.', ''):
469 elif comps
[i
] == '..':
470 if i
> 0 and comps
[i
-1] != '..':
473 elif i
== 0 and prefix
.endswith("\\"):
479 # If the path is now empty, substitute '.'
480 if not prefix
and not comps
:
482 return prefix
+ backslash
.join(comps
)
485 # Return an absolute path.
487 from nt
import _getfullpathname
489 except ImportError: # not running on Windows - mock up something sensible
491 """Return the absolute version of a path."""
493 if isinstance(path
, _unicode
):
497 path
= join(cwd
, path
)
498 return normpath(path
)
500 else: # use native Windows method on Windows
502 """Return the absolute version of a path."""
504 if path
: # Empty path must return current working directory.
506 path
= _getfullpathname(path
)
508 pass # Bad path - return unchanged.
509 elif isinstance(path
, _unicode
):
513 return normpath(path
)
515 # realpath is a no-op on systems without islink support
517 # Win9x family and earlier have no Unicode filename support.
518 supports_unicode_filenames
= (hasattr(sys
, "getwindowsversion") and
519 sys
.getwindowsversion()[3] >= 2)
521 def _abspath_split(path
):
522 abs = abspath(normpath(path
))
523 prefix
, rest
= splitunc(abs)
524 is_unc
= bool(prefix
)
526 prefix
, rest
= splitdrive(abs)
527 return is_unc
, prefix
, [x
for x
in rest
.split(sep
) if x
]
529 def relpath(path
, start
=curdir
):
530 """Return a relative version of a path"""
533 raise ValueError("no path specified")
535 start_is_unc
, start_prefix
, start_list
= _abspath_split(start
)
536 path_is_unc
, path_prefix
, path_list
= _abspath_split(path
)
538 if path_is_unc ^ start_is_unc
:
539 raise ValueError("Cannot mix UNC and non-UNC paths (%s and %s)"
541 if path_prefix
.lower() != start_prefix
.lower():
543 raise ValueError("path is on UNC root %s, start on UNC root %s"
544 % (path_prefix
, start_prefix
))
546 raise ValueError("path is on drive %s, start on drive %s"
547 % (path_prefix
, start_prefix
))
548 # Work out how much of the filepath is shared by start and path.
550 for e1
, e2
in zip(start_list
, path_list
):
551 if e1
.lower() != e2
.lower():
555 rel_list
= [pardir
] * (len(start_list
)-i
) + path_list
[i
:]
558 return join(*rel_list
)
561 # The genericpath.isdir implementation uses os.stat and checks the mode
562 # attribute to tell whether or not the path is a directory.
563 # This is overkill on Windows - just pass the path to GetFileAttributes
564 # and check the attribute from there.
565 from nt
import _isdir
as isdir
567 # Use genericpath.isdir as imported above.