]> git.lizzy.rs Git - plan9front.git/blob - sys/lib/python/ntpath.py
make python subprocess module work with ape/sh
[plan9front.git] / sys / lib / python / ntpath.py
1 # Module 'ntpath' -- common operations on WinNT/Win95 pathnames
2 """Common pathname manipulations, WindowsNT/95 version.
3
4 Instead of importing this module directly, import os and refer to this
5 module as os.path.
6 """
7
8 import os
9 import stat
10 import 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
20 curdir = '.'
21 pardir = '..'
22 extsep = '.'
23 sep = '\\'
24 pathsep = ';'
25 altsep = '/'
26 defpath = '.;C:\\bin'
27 if 'ce' in sys.builtin_module_names:
28     defpath = '\\Windows'
29 elif 'os2' in sys.builtin_module_names:
30     # OS/2 w/ VACPP
31     altsep = '/'
32 devnull = '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
38 def 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
51 def 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
59 def 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
116 def 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
125 def 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
158 def 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
183 def 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
198 def 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
205 def 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
212 def 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
226 def getsize(filename):
227     """Return the size of a file, reported by os.stat()"""
228     return os.stat(filename).st_size
229
230 def getmtime(filename):
231     """Return the last modification time of a file, reported by os.stat()"""
232     return os.stat(filename).st_mtime
233
234 def getatime(filename):
235     """Return the last access time of a file, reported by os.stat()"""
236     return os.stat(filename).st_atime
237
238 def 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
245 def islink(path):
246     """Test for symbolic link.  On WindowsNT/95 always returns false"""
247     return False
248
249
250 # Does a path exist?
251
252 def 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
260 lexists = 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
267 def 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
280 def 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
292 def 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
309 def 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
346 def 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
380 def 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
439 def 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.
484 try:
485     from nt import _getfullpathname
486
487 except ImportError: # not running on Windows - mock up something sensible
488     def abspath(path):
489         """Return the absolute version of a path."""
490         if not isabs(path):
491             path = join(os.getcwd(), path)
492         return normpath(path)
493
494 else:  # use native Windows method on Windows
495     def abspath(path):
496         """Return the absolute version of a path."""
497
498         if path: # Empty path must return current working directory.
499             try:
500                 path = _getfullpathname(path)
501             except WindowsError:
502                 pass # Bad path - return unchanged.
503         else:
504             path = os.getcwd()
505         return normpath(path)
506
507 # realpath is a no-op on systems without islink support
508 realpath = abspath
509 # Win9x family and earlier have no Unicode filename support.
510 supports_unicode_filenames = (hasattr(sys, "getwindowsversion") and
511                               sys.getwindowsversion()[3] >= 2)