blob: efc27e28a5073e4dbc26d348f8a2316cef03d2b4 [file] [log] [blame]
Tor Norbye3a2425a2013-11-04 10:16:08 -08001# Module 'ntpath' -- common operations on WinNT/Win95 pathnames
2"""Common pathname manipulations, WindowsNT/95 version.
3
4Instead of importing this module directly, import os and refer to this
5module as os.path.
6"""
7
8import os
9import stat
10import sys
11
12__all__ = ["normcase","isabs","join","splitdrive","split","splitext",
13 "basename","dirname","commonprefix","getsize","getmtime",
14 "getatime","getctime", "islink","exists","lexists","isdir","isfile",
15 "ismount","walk","expanduser","expandvars","normpath","abspath",
16 "splitunc","curdir","pardir","sep","pathsep","defpath","altsep",
17 "extsep","devnull","realpath","supports_unicode_filenames"]
18
19# strings representing various path-related bits and pieces
20curdir = '.'
21pardir = '..'
22extsep = '.'
23sep = '\\'
24pathsep = ';'
25altsep = '/'
26defpath = '.;C:\\bin'
27if 'ce' in sys.builtin_module_names:
28 defpath = '\\Windows'
29elif 'os2' in sys.builtin_module_names:
30 # OS/2 w/ VACPP
31 altsep = '/'
32devnull = 'nul'
33
34# Normalize the case of a pathname and map slashes to backslashes.
35# Other normalizations (such as optimizing '../' away) are not done
36# (this is done by normpath).
37
38def normcase(s):
39 """Normalize case of pathname.
40
41 Makes all characters lowercase and all slashes into backslashes."""
42 return s.replace("/", "\\").lower()
43
44
45# Return whether a path is absolute.
46# Trivial in Posix, harder on the Mac or MS-DOS.
47# For DOS it is absolute if it starts with a slash or backslash (current
48# volume), or if a pathname after the volume letter and colon / UNC resource
49# starts with a slash or backslash.
50
51def isabs(s):
52 """Test whether a path is absolute"""
53 s = splitdrive(s)[1]
54 return s != '' and s[:1] in '/\\'
55
56
57# Join two (or more) paths.
58
59def join(a, *p):
60 """Join two or more pathname components, inserting "\\" as needed"""
61 path = a
62 for b in p:
63 b_wins = 0 # set to 1 iff b makes path irrelevant
64 if path == "":
65 b_wins = 1
66
67 elif isabs(b):
68 # This probably wipes out path so far. However, it's more
69 # complicated if path begins with a drive letter:
70 # 1. join('c:', '/a') == 'c:/a'
71 # 2. join('c:/', '/a') == 'c:/a'
72 # But
73 # 3. join('c:/a', '/b') == '/b'
74 # 4. join('c:', 'd:/') = 'd:/'
75 # 5. join('c:/', 'd:/') = 'd:/'
76 if path[1:2] != ":" or b[1:2] == ":":
77 # Path doesn't start with a drive letter, or cases 4 and 5.
78 b_wins = 1
79
80 # Else path has a drive letter, and b doesn't but is absolute.
81 elif len(path) > 3 or (len(path) == 3 and
82 path[-1] not in "/\\"):
83 # case 3
84 b_wins = 1
85
86 if b_wins:
87 path = b
88 else:
89 # Join, and ensure there's a separator.
90 assert len(path) > 0
91 if path[-1] in "/\\":
92 if b and b[0] in "/\\":
93 path += b[1:]
94 else:
95 path += b
96 elif path[-1] == ":":
97 path += b
98 elif b:
99 if b[0] in "/\\":
100 path += b
101 else:
102 path += "\\" + b
103 else:
104 # path is not empty and does not end with a backslash,
105 # but b is empty; since, e.g., split('a/') produces
106 # ('a', ''), it's best if join() adds a backslash in
107 # this case.
108 path += '\\'
109
110 return path
111
112
113# Split a path in a drive specification (a drive letter followed by a
114# colon) and the path specification.
115# It is always true that drivespec + pathspec == p
116def splitdrive(p):
117 """Split a pathname into drive and path specifiers. Returns a 2-tuple
118"(drive,path)"; either part may be empty"""
119 if p[1:2] == ':':
120 return p[0:2], p[2:]
121 return '', p
122
123
124# Parse UNC paths
125def splitunc(p):
126 """Split a pathname into UNC mount point and relative path specifiers.
127
128 Return a 2-tuple (unc, rest); either part may be empty.
129 If unc is not empty, it has the form '//host/mount' (or similar
130 using backslashes). unc+rest is always the input path.
131 Paths containing drive letters never have an UNC part.
132 """
133 if p[1:2] == ':':
134 return '', p # Drive letter present
135 firstTwo = p[0:2]
136 if firstTwo == '//' or firstTwo == '\\\\':
137 # is a UNC path:
138 # vvvvvvvvvvvvvvvvvvvv equivalent to drive letter
139 # \\machine\mountpoint\directories...
140 # directory ^^^^^^^^^^^^^^^
141 normp = normcase(p)
142 index = normp.find('\\', 2)
143 if index == -1:
144 ##raise RuntimeError, 'illegal UNC path: "' + p + '"'
145 return ("", p)
146 index = normp.find('\\', index + 1)
147 if index == -1:
148 index = len(p)
149 return p[:index], p[index:]
150 return '', p
151
152
153# Split a path in head (everything up to the last '/') and tail (the
154# rest). After the trailing '/' is stripped, the invariant
155# join(head, tail) == p holds.
156# The resulting head won't end in '/' unless it is the root.
157
158def split(p):
159 """Split a pathname.
160
161 Return tuple (head, tail) where tail is everything after the final slash.
162 Either part may be empty."""
163
164 d, p = splitdrive(p)
165 # set i to index beyond p's last slash
166 i = len(p)
167 while i and p[i-1] not in '/\\':
168 i = i - 1
169 head, tail = p[:i], p[i:] # now tail has no slashes
170 # remove trailing slashes from head, unless it's all slashes
171 head2 = head
172 while head2 and head2[-1] in '/\\':
173 head2 = head2[:-1]
174 head = head2 or head
175 return d + head, tail
176
177
178# Split a path in root and extension.
179# The extension is everything starting at the last dot in the last
180# pathname component; the root is everything before that.
181# It is always true that root + ext == p.
182
183def splitext(p):
184 """Split the extension from a pathname.
185
186 Extension is everything from the last dot to the end.
187 Return (root, ext), either part may be empty."""
188
189 i = p.rfind('.')
190 if i<=max(p.rfind('/'), p.rfind('\\')):
191 return p, ''
192 else:
193 return p[:i], p[i:]
194
195
196# Return the tail (basename) part of a path.
197
198def basename(p):
199 """Returns the final component of a pathname"""
200 return split(p)[1]
201
202
203# Return the head (dirname) part of a path.
204
205def dirname(p):
206 """Returns the directory component of a pathname"""
207 return split(p)[0]
208
209
210# Return the longest prefix of all list elements.
211
212def commonprefix(m):
213 "Given a list of pathnames, returns the longest common leading component"
214 if not m: return ''
215 s1 = min(m)
216 s2 = max(m)
217 n = min(len(s1), len(s2))
218 for i in xrange(n):
219 if s1[i] != s2[i]:
220 return s1[:i]
221 return s1[:n]
222
223
224# Get size, mtime, atime of files.
225
226def getsize(filename):
227 """Return the size of a file, reported by os.stat()"""
228 return os.stat(filename).st_size
229
230def getmtime(filename):
231 """Return the last modification time of a file, reported by os.stat()"""
232 return os.stat(filename).st_mtime
233
234def getatime(filename):
235 """Return the last access time of a file, reported by os.stat()"""
236 return os.stat(filename).st_atime
237
238def getctime(filename):
239 """Return the creation time of a file, reported by os.stat()."""
240 return os.stat(filename).st_ctime
241
242# Is a path a symbolic link?
243# This will always return false on systems where posix.lstat doesn't exist.
244
245def islink(path):
246 """Test for symbolic link. On WindowsNT/95 always returns false"""
247 return False
248
249
250# Does a path exist?
251
252def exists(path):
253 """Test whether a path exists"""
254 try:
255 st = os.stat(path)
256 except os.error:
257 return False
258 return True
259
260lexists = exists
261
262
263# Is a path a dos directory?
264# This follows symbolic links, so both islink() and isdir() can be true
265# for the same path.
266
267def isdir(path):
268 """Test whether a path is a directory"""
269 try:
270 st = os.stat(path)
271 except os.error:
272 return False
273 return stat.S_ISDIR(st.st_mode)
274
275
276# Is a path a regular file?
277# This follows symbolic links, so both islink() and isdir() can be true
278# for the same path.
279
280def isfile(path):
281 """Test whether a path is a regular file"""
282 try:
283 st = os.stat(path)
284 except os.error:
285 return False
286 return stat.S_ISREG(st.st_mode)
287
288
289# Is a path a mount point? Either a root (with or without drive letter)
290# or an UNC path with at most a / or \ after the mount point.
291
292def ismount(path):
293 """Test whether a path is a mount point (defined as root of drive)"""
294 unc, rest = splitunc(path)
295 if unc:
296 return rest in ("", "/", "\\")
297 p = splitdrive(path)[1]
298 return len(p) == 1 and p[0] in '/\\'
299
300
301# Directory tree walk.
302# For each directory under top (including top itself, but excluding
303# '.' and '..'), func(arg, dirname, filenames) is called, where
304# dirname is the name of the directory and filenames is the list
305# of files (and subdirectories etc.) in the directory.
306# The func may modify the filenames list, to implement a filter,
307# or to impose a different order of visiting.
308
309def walk(top, func, arg):
310 """Directory tree walk with callback function.
311
312 For each directory in the directory tree rooted at top (including top
313 itself, but excluding '.' and '..'), call func(arg, dirname, fnames).
314 dirname is the name of the directory, and fnames a list of the names of
315 the files and subdirectories in dirname (excluding '.' and '..'). func
316 may modify the fnames list in-place (e.g. via del or slice assignment),
317 and walk will only recurse into the subdirectories whose names remain in
318 fnames; this can be used to implement a filter, or to impose a specific
319 order of visiting. No semantics are defined for, or required of, arg,
320 beyond that arg is always passed to func. It can be used, e.g., to pass
321 a filename pattern, or a mutable object designed to accumulate
322 statistics. Passing None for arg is common."""
323
324 try:
325 names = os.listdir(top)
326 except os.error:
327 return
328 func(arg, top, names)
329 exceptions = ('.', '..')
330 for name in names:
331 if name not in exceptions:
332 name = join(top, name)
333 if isdir(name):
334 walk(name, func, arg)
335
336
337# Expand paths beginning with '~' or '~user'.
338# '~' means $HOME; '~user' means that user's home directory.
339# If the path doesn't begin with '~', or if the user or $HOME is unknown,
340# the path is returned unchanged (leaving error reporting to whatever
341# function is called with the expanded path as argument).
342# See also module 'glob' for expansion of *, ? and [...] in pathnames.
343# (A function should also be defined to do full *sh-style environment
344# variable expansion.)
345
346def expanduser(path):
347 """Expand ~ and ~user constructs.
348
349 If user or $HOME is unknown, do nothing."""
350 if path[:1] != '~':
351 return path
352 i, n = 1, len(path)
353 while i < n and path[i] not in '/\\':
354 i = i + 1
355 if i == 1:
356 if 'HOME' in os.environ:
357 userhome = os.environ['HOME']
358 elif not 'HOMEPATH' in os.environ:
359 return path
360 else:
361 try:
362 drive = os.environ['HOMEDRIVE']
363 except KeyError:
364 drive = ''
365 userhome = join(drive, os.environ['HOMEPATH'])
366 else:
367 return path
368 return userhome + path[i:]
369
370
371# Expand paths containing shell variable substitutions.
372# The following rules apply:
373# - no expansion within single quotes
374# - no escape character, except for '$$' which is translated into '$'
375# - ${varname} is accepted.
376# - varnames can be made out of letters, digits and the character '_'
377# XXX With COMMAND.COM you can use any characters in a variable name,
378# XXX except '^|<>='.
379
380def expandvars(path):
381 """Expand shell variables of form $var and ${var}.
382
383 Unknown variables are left unchanged."""
384 if '$' not in path:
385 return path
386 import string
387 varchars = string.ascii_letters + string.digits + '_-'
388 res = ''
389 index = 0
390 pathlen = len(path)
391 while index < pathlen:
392 c = path[index]
393 if c == '\'': # no expansion within single quotes
394 path = path[index + 1:]
395 pathlen = len(path)
396 try:
397 index = path.index('\'')
398 res = res + '\'' + path[:index + 1]
399 except ValueError:
400 res = res + path
401 index = pathlen - 1
402 elif c == '$': # variable or '$$'
403 if path[index + 1:index + 2] == '$':
404 res = res + c
405 index = index + 1
406 elif path[index + 1:index + 2] == '{':
407 path = path[index+2:]
408 pathlen = len(path)
409 try:
410 index = path.index('}')
411 var = path[:index]
412 if var in os.environ:
413 res = res + os.environ[var]
414 except ValueError:
415 res = res + path
416 index = pathlen - 1
417 else:
418 var = ''
419 index = index + 1
420 c = path[index:index + 1]
421 while c != '' and c in varchars:
422 var = var + c
423 index = index + 1
424 c = path[index:index + 1]
425 if var in os.environ:
426 res = res + os.environ[var]
427 if c != '':
428 res = res + c
429 else:
430 res = res + c
431 index = index + 1
432 return res
433
434
435# Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A\B.
436# Previously, this function also truncated pathnames to 8+3 format,
437# but as this module is called "ntpath", that's obviously wrong!
438
439def normpath(path):
440 """Normalize path, eliminating double slashes, etc."""
441 path = path.replace("/", "\\")
442 prefix, path = splitdrive(path)
443 # We need to be careful here. If the prefix is empty, and the path starts
444 # with a backslash, it could either be an absolute path on the current
445 # drive (\dir1\dir2\file) or a UNC filename (\\server\mount\dir1\file). It
446 # is therefore imperative NOT to collapse multiple backslashes blindly in
447 # that case.
448 # The code below preserves multiple backslashes when there is no drive
449 # letter. This means that the invalid filename \\\a\b is preserved
450 # unchanged, where a\\\b is normalised to a\b. It's not clear that there
451 # is any better behaviour for such edge cases.
452 if prefix == '':
453 # No drive letter - preserve initial backslashes
454 while path[:1] == "\\":
455 prefix = prefix + "\\"
456 path = path[1:]
457 else:
458 # We have a drive letter - collapse initial backslashes
459 if path.startswith("\\"):
460 prefix = prefix + "\\"
461 path = path.lstrip("\\")
462 comps = path.split("\\")
463 i = 0
464 while i < len(comps):
465 if comps[i] in ('.', ''):
466 del comps[i]
467 elif comps[i] == '..':
468 if i > 0 and comps[i-1] != '..':
469 del comps[i-1:i+1]
470 i -= 1
471 elif i == 0 and prefix.endswith("\\"):
472 del comps[i]
473 else:
474 i += 1
475 else:
476 i += 1
477 # If the path is now empty, substitute '.'
478 if not prefix and not comps:
479 comps.append('.')
480 return prefix + "\\".join(comps)
481
482
483# Return an absolute path.
484try:
485 from nt import _getfullpathname
486
487except ImportError: # not running on Windows - mock up something sensible
488 import java.io.File
489 from org.python.core.Py import newString
490
491 def abspath(path):
492 """Return the absolute version of a path."""
493 if not isabs(path):
494 path = join(os.getcwd(), path)
495 if not splitunc(path)[0] and not splitdrive(path)[0]:
496 # cwd lacks a UNC mount point, so it should have a drive
497 # letter (but lacks one): determine it
498 canon_path = newString(java.io.File(path).getCanonicalPath())
499 drive = splitdrive(canon_path)[0]
500 path = join(drive, path)
501 return normpath(path)
502
503else: # use native Windows method on Windows
504 def abspath(path):
505 """Return the absolute version of a path."""
506
507 if path: # Empty path must return current working directory.
508 try:
509 path = _getfullpathname(path)
510 except WindowsError:
511 pass # Bad path - return unchanged.
512 else:
513 path = os.getcwd()
514 return normpath(path)
515
516# realpath is a no-op on systems without islink support
517realpath = abspath
518# Win9x family and earlier have no Unicode filename support.
519supports_unicode_filenames = (hasattr(sys, "getwindowsversion") and
520 sys.getwindowsversion()[3] >= 2)