source: trunk/essentials/dev-lang/python/Lib/os2knixpath.py

Last change on this file was 3426, checked in by bird, 19 years ago

os2knixpath.

File size: 16.7 KB
Line 
1"""Common operations on OS/2 kNIX pathnames.
2
3Instead of importing this module directly, import os and refer to
4this module as os.path. The "os.path" name is an alias for this
5module on OS/2 kNIX Posix; on other systems (e.g. Mac, Windows),
6os.path provides the same operations in a manner specific to that
7platform, and is an alias to another module (e.g. macpath, ntpath).
8
9This is slightly different from both OS/2 EMX and POSIX, so in order
10to avoid messing up those sources, the kNIX code has got a separate
11file that drags from both EMX and POSIX.
12"""
13
14import os
15import stat
16
17__all__ = ["normcase","isabs","join","splitdrive","split","splitext",
18 "basename","dirname","commonprefix","getsize","getmtime",
19 "getatime","getctime","islink","exists","lexists","isdir","isfile",
20 "ismount","walk","expanduser","expandvars","normpath","abspath",
21 "samefile","sameopenfile","samestat",
22 "curdir","pardir","sep","pathsep","defpath","altsep","extsep",
23 "devnull","realpath","supports_unicode_filenames",
24 "splitunc"]
25
26# strings representing various path-related bits and pieces
27curdir = '.'
28pardir = '..'
29extsep = '.'
30sep = '/'
31altsep = '\\'
32pathsep = ';'
33defpath = '.;/@unixroot/bin;/@unixroot/usr/bin;/@unixroot/usr/local/bin'
34devnull = 'nul'
35
36# Normalize the case of a pathname. Trivial in Posix, string.lower on Mac.
37# On MS-DOS this may also turn slashes into backslashes; however, other
38# normalizations (such as optimizing '../' away) are not allowed
39# (another function should be defined to do that).
40
41def normcase(s):
42 """Normalize case of pathname.
43
44 XXX: This needs to call some kLIBC function to do case corrections, but
45 I'm not up do that right now.
46 """
47 return s.replace('\\', '/')
48
49
50# Return whether a path is absolute.
51# Trivial in Posix, harder on the Mac or MS-DOS.
52# For DOS it is absolute if it starts with a slash or backslash (current
53# volume), or if a pathname after the volume letter and colon / UNC resource
54# starts with a slash or backslash.
55
56def isabs(s):
57 """Test whether a path is absolute"""
58 s = splitdrive(s)[1]
59 return s != '' and s[:1] in '/\\'
60
61
62# Join pathnames.
63# Ignore the previous parts if a part is absolute.
64# Insert a '/' unless the first part is empty or already ends in '/'.
65
66def join(a, *p):
67 """Join two or more pathname components, inserting '/' as needed"""
68 path = a
69 for b in p:
70 if isabs(b):
71 path = b
72 elif path == '' or path[-1:] in '/\\:':
73 path = path + b
74 else:
75 path = path + '/' + b
76 return path
77
78
79# Split a path in a drive specification (a drive letter followed by a
80# colon) and the path specification.
81# It is always true that drivespec + pathspec == p
82def splitdrive(p):
83 """Split a pathname into drive and path specifiers. Returns a 2-tuple
84"(drive,path)"; either part may be empty"""
85 if p[1:2] == ':':
86 return p[0:2], p[2:]
87 return '', p
88
89
90# Parse UNC paths
91def splitunc(p):
92 """Split a pathname into UNC mount point and relative path specifiers.
93
94 Return a 2-tuple (unc, rest); either part may be empty.
95 If unc is not empty, it has the form '//host/mount' (or similar
96 using backslashes). unc+rest is always the input path.
97 Paths containing drive letters never have an UNC part.
98 """
99 if p[1:2] == ':':
100 return '', p # Drive letter present
101 firstTwo = p[0:2]
102 if firstTwo == '/' * 2 or firstTwo == '\\' * 2:
103 # is a UNC path:
104 # vvvvvvvvvvvvvvvvvvvv equivalent to drive letter
105 # \\machine\mountpoint\directories...
106 # directory ^^^^^^^^^^^^^^^
107 normp = normcase(p)
108 index = normp.find('/', 2)
109 if index == -1:
110 ##raise RuntimeError, 'illegal UNC path: "' + p + '"'
111 return ("", p)
112 index = normp.find('/', index + 1)
113 if index == -1:
114 index = len(p)
115 return p[:index], p[index:]
116 return '', p
117
118
119# Split a path in head (everything up to the last '/') and tail (the
120# rest). If the path ends in '/', tail will be empty. If there is no
121# '/' in the path, head will be empty.
122# Trailing '/'es are stripped from head unless it is the root.
123
124def split(p):
125 """Split a pathname. Returns tuple "(head, tail)" where "tail" is
126 everything after the final slash. Either part may be empty."""
127
128 d, p = splitdrive(p)
129 # set i to index beyond p's last slash
130 i = len(p)
131 while i and p[i-1] not in '/\\':
132 i = i - 1
133 head, tail = p[:i], p[i:] # now tail has no slashes
134 # remove trailing slashes from head, unless it's all slashes
135 head2 = head
136 while head2 and head2[-1] in '/\\':
137 head2 = head2[:-1]
138 head = head2 or head
139 return d + head, tail
140
141
142# Split a path in root and extension.
143# The extension is everything starting at the last dot in the last
144# pathname component; the root is everything before that.
145# It is always true that root + ext == p.
146
147def splitext(p):
148 """Split the extension from a pathname.
149
150 Extension is everything from the last dot to the end.
151 Return (root, ext), either part may be empty."""
152 root, ext = '', ''
153 for c in p:
154 if c in ['/','\\']:
155 root, ext = root + ext + c, ''
156 elif c == '.':
157 if ext:
158 root, ext = root + ext, c
159 else:
160 ext = c
161 elif ext:
162 ext = ext + c
163 else:
164 root = root + c
165 return root, ext
166
167
168# Return the tail (basename) part of a path.
169
170def basename(p):
171 """Returns the final component of a pathname"""
172 return split(p)[1]
173
174
175# Return the head (dirname) part of a path.
176
177def dirname(p):
178 """Returns the directory component of a pathname"""
179 return split(p)[0]
180
181
182# Return the longest prefix of all list elements.
183
184def commonprefix(m):
185 "Given a list of pathnames, returns the longest common leading component"
186 if not m: return ''
187 s1 = min(m)
188 s2 = max(m)
189 n = min(len(s1), len(s2))
190 for i in xrange(n):
191 if s1[i] != s2[i]:
192 return s1[:i]
193 return s1[:n]
194
195# Get size, mtime, atime of files.
196
197def getsize(filename):
198 """Return the size of a file, reported by os.stat()."""
199 return os.stat(filename).st_size
200
201def getmtime(filename):
202 """Return the last modification time of a file, reported by os.stat()."""
203 return os.stat(filename).st_mtime
204
205def getatime(filename):
206 """Return the last access time of a file, reported by os.stat()."""
207 return os.stat(filename).st_atime
208
209def getctime(filename):
210 """Return the metadata change time of a file, reported by os.stat()."""
211 return os.stat(filename).st_ctime
212
213# Is a path a symbolic link?
214# This will always return false on systems where os.lstat doesn't exist.
215
216def islink(path):
217 """Test whether a path is a symbolic link"""
218 try:
219 st = os.lstat(path)
220 except (os.error, AttributeError):
221 return False
222 return stat.S_ISLNK(st.st_mode)
223
224
225# Does a path exist?
226# This is false for dangling symbolic links.
227
228def exists(path):
229 """Test whether a path exists. Returns False for broken symbolic links"""
230 try:
231 st = os.stat(path)
232 except os.error:
233 return False
234 return True
235
236
237# Being true for dangling symbolic links is also useful.
238
239def lexists(path):
240 """Test whether a path exists. Returns True for broken symbolic links"""
241 try:
242 st = os.lstat(path)
243 except os.error:
244 return False
245 return True
246
247
248# Is a path a directory?
249# This follows symbolic links, so both islink() and isdir() can be true
250# for the same path.
251
252def isdir(path):
253 """Test whether a path is a directory"""
254 try:
255 st = os.stat(path)
256 except os.error:
257 return False
258 return stat.S_ISDIR(st.st_mode)
259
260
261# Is a path a regular file?
262# This follows symbolic links, so both islink() and isfile() can be true
263# for the same path.
264
265def isfile(path):
266 """Test whether a path is a regular file"""
267 try:
268 st = os.stat(path)
269 except os.error:
270 return False
271 return stat.S_ISREG(st.st_mode)
272
273
274# Are two filenames really pointing to the same file?
275
276def samefile(f1, f2):
277 """Test whether two pathnames reference the same actual file"""
278 s1 = os.stat(f1)
279 s2 = os.stat(f2)
280 return samestat(s1, s2)
281
282
283# Are two open files really referencing the same file?
284# (Not necessarily the same file descriptor!)
285
286def sameopenfile(fp1, fp2):
287 """Test whether two open file objects reference the same file"""
288 s1 = os.fstat(fp1)
289 s2 = os.fstat(fp2)
290 return samestat(s1, s2)
291
292
293# Are two stat buffers (obtained from stat, fstat or lstat)
294# describing the same file?
295
296def samestat(s1, s2):
297 """Test whether two stat buffers reference the same file"""
298 return s1.st_ino == s2.st_ino and \
299 s1.st_dev == s2.st_dev
300
301
302# Is a path a mount point? Either a root (with or without drive letter)
303# or an UNC path with at most a / or \ after the mount point.
304
305def ismount(path):
306 """Test whether a path is a mount point (defined as root of drive)
307 XXX: this isn't correct for if path is a symlink!
308 """
309 unc, rest = splitunc(path)
310 if unc:
311 return rest in ("", "/", "\\")
312 p = splitdrive(path)[1]
313 return len(p) == 1 and p[0] in '/\\'
314
315
316# Directory tree walk.
317# For each directory under top (including top itself, but excluding
318# '.' and '..'), func(arg, dirname, filenames) is called, where
319# dirname is the name of the directory and filenames is the list
320# of files (and subdirectories etc.) in the directory.
321# The func may modify the filenames list, to implement a filter,
322# or to impose a different order of visiting.
323
324def walk(top, func, arg):
325 """Directory tree walk with callback function.
326
327 For each directory in the directory tree rooted at top (including top
328 itself, but excluding '.' and '..'), call func(arg, dirname, fnames).
329 dirname is the name of the directory, and fnames a list of the names of
330 the files and subdirectories in dirname (excluding '.' and '..'). func
331 may modify the fnames list in-place (e.g. via del or slice assignment),
332 and walk will only recurse into the subdirectories whose names remain in
333 fnames; this can be used to implement a filter, or to impose a specific
334 order of visiting. No semantics are defined for, or required of, arg,
335 beyond that arg is always passed to func. It can be used, e.g., to pass
336 a filename pattern, or a mutable object designed to accumulate
337 statistics. Passing None for arg is common."""
338 try:
339 names = os.listdir(top)
340 except os.error:
341 return
342 func(arg, top, names)
343 exceptions = ('.', '..')
344 for name in names:
345 if name not in exceptions:
346 name = join(top, name)
347 if isdir(name):
348 walk(name, func, arg)
349
350
351# Expand paths beginning with '~' or '~user'.
352# '~' means $HOME; '~user' means that user's home directory.
353# If the path doesn't begin with '~', or if the user or $HOME is unknown,
354# the path is returned unchanged (leaving error reporting to whatever
355# function is called with the expanded path as argument).
356# See also module 'glob' for expansion of *, ? and [...] in pathnames.
357# (A function should also be defined to do full *sh-style environment
358# variable expansion.)
359
360def expanduser(path):
361 """Expand ~ and ~user constructs.
362
363 If user or $HOME is unknown, do nothing."""
364 if path[:1] != '~':
365 return path
366 i, n = 1, len(path)
367 while i < n and path[i] not in '/\\':
368 i = i + 1
369 if i == 1:
370 if 'HOME' in os.environ:
371 userhome = os.environ['HOME']
372 elif 'HOMEPATH' in os.environ:
373 try:
374 drive = os.environ['HOMEDRIVE']
375 except KeyError:
376 drive = ''
377 userhome = join(drive, os.environ['HOMEPATH'])
378 else:
379 import pwd
380 userhome = pwd.getpwuid(os.getuid()).pw_dir
381 else:
382 import pwd
383 try:
384 pwent = pwd.getpwnam(path[1:i])
385 except KeyError:
386 return path
387 userhome = pwent.pw_dir
388
389 if userhome.endswith('/') or userhome.endswith('\\'):
390 i += 1
391 return userhome + path[i:]
392
393
394# Expand paths containing shell variable substitutions.
395# The following rules apply:
396# - no expansion within single quotes
397# - no escape character, except for '$$' which is translated into '$'
398# - ${varname} is accepted.
399# - varnames can be made out of letters, digits and the character '_'
400# XXX With COMMAND.COM you can use any characters in a variable name,
401# XXX except '^|<>='.
402
403def expandvars(path):
404 """Expand shell variables of form $var and ${var}.
405
406 Unknown variables are left unchanged."""
407 if '$' not in path:
408 return path
409 import string
410 varchars = string.letters + string.digits + '_-'
411 res = ''
412 index = 0
413 pathlen = len(path)
414 while index < pathlen:
415 c = path[index]
416 if c == '\'': # no expansion within single quotes
417 path = path[index + 1:]
418 pathlen = len(path)
419 try:
420 index = path.index('\'')
421 res = res + '\'' + path[:index + 1]
422 except ValueError:
423 res = res + path
424 index = pathlen - 1
425 elif c == '$': # variable or '$$'
426 if path[index + 1:index + 2] == '$':
427 res = res + c
428 index = index + 1
429 elif path[index + 1:index + 2] == '{':
430 path = path[index+2:]
431 pathlen = len(path)
432 try:
433 index = path.index('}')
434 var = path[:index]
435 if var in os.environ:
436 res = res + os.environ[var]
437 except ValueError:
438 res = res + path
439 index = pathlen - 1
440 else:
441 var = ''
442 index = index + 1
443 c = path[index:index + 1]
444 while c != '' and c in varchars:
445 var = var + c
446 index = index + 1
447 c = path[index:index + 1]
448 if var in os.environ:
449 res = res + os.environ[var]
450 if c != '':
451 res = res + c
452 else:
453 res = res + c
454 index = index + 1
455 return res
456
457
458# Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A/B.
459
460def normpath(path):
461 """Normalize path, eliminating double slashes, etc."""
462 path = path.replace('\\', '/')
463 prefix, path = splitdrive(path)
464 while path[:1] == '/':
465 prefix = prefix + '/'
466 path = path[1:]
467 comps = path.split('/')
468 i = 0
469 while i < len(comps):
470 if comps[i] == '.':
471 del comps[i]
472 elif comps[i] == '..' and i > 0 and comps[i-1] not in ('', '..'):
473 del comps[i-1:i+1]
474 i = i - 1
475 elif comps[i] == '' and i > 0 and comps[i-1] != '':
476 del comps[i]
477 else:
478 i = i + 1
479 # If the path is now empty, substitute '.'
480 if not prefix and not comps:
481 comps.append('.')
482 return prefix + '/'.join(comps)
483
484
485# Return an absolute path.
486def abspath(path):
487 """Return the absolute version of a path"""
488 if not isabs(path):
489 path = join(os.getcwd(), path)
490 return normpath(path)
491
492
493# Return a canonical path (i.e. the absolute location of a file on the
494# filesystem).
495
496def realpath(filename):
497 """Return the canonical path of the specified filename, eliminating any
498symbolic links encountered in the path."""
499 filename.replace('\\', '/') # simpler this way..
500 if isabs(filename):
501 bits = ['/'] + filename.split('/')[1:]
502 else:
503 bits = [''] + filename.split('/')
504
505 for i in range(2, len(bits)+1):
506 component = join(*bits[0:i])
507 # Resolve symbolic links.
508 if islink(component):
509 resolved = _resolve_link(component)
510 if resolved is None:
511 # Infinite loop -- return original component + rest of the path
512 return abspath(join(*([component] + bits[i:])))
513 else:
514 newpath = join(*([resolved] + bits[i:]))
515 return realpath(newpath)
516
517 return abspath(filename)
518
519
520def _resolve_link(path):
521 """Internal helper function. Takes a path and follows symlinks
522 until we either arrive at something that isn't a symlink, or
523 encounter a path we've seen before (meaning that there's a loop).
524 """
525 paths_seen = []
526 while islink(path):
527 if path in paths_seen:
528 # Already seen this path, so we must have a symlink loop
529 return None
530 paths_seen.append(path)
531 # Resolve where the link points to
532 resolved = os.readlink(path)
533 if not isabs(resolved):
534 dir = dirname(path)
535 path = normpath(join(dir, resolved))
536 else:
537 path = normpath(resolved)
538 return path
539
540supports_unicode_filenames = False
541
Note: See TracBrowser for help on using the repository browser.