blob: e4d63c62be36eca2f7ddbd9fd46ce185a19acb4f [file] [log] [blame]
Andrew MacIntyre5cef5712002-02-24 05:32:32 +00001# Module 'os2emxpath' -- common operations on OS/2 pathnames
2"""Common pathname manipulations, OS/2 EMX version.
3
4Instead of importing this module directly, import os and refer to this
5module as os.path.
6"""
7
8import os
9import stat
10
11__all__ = ["normcase","isabs","join","splitdrive","split","splitext",
12 "basename","dirname","commonprefix","getsize","getmtime",
13 "getatime","islink","exists","isdir","isfile","ismount",
14 "walk","expanduser","expandvars","normpath","abspath","splitunc"]
15
16# Normalize the case of a pathname and map slashes to backslashes.
17# Other normalizations (such as optimizing '../' away) are not done
18# (this is done by normpath).
19
20def normcase(s):
21 """Normalize case of pathname.
22
23 Makes all characters lowercase and all altseps into seps."""
24 return s.replace('\\', '/').lower()
25
26
27# Return whether a path is absolute.
28# Trivial in Posix, harder on the Mac or MS-DOS.
29# For DOS it is absolute if it starts with a slash or backslash (current
30# volume), or if a pathname after the volume letter and colon / UNC resource
31# starts with a slash or backslash.
32
33def isabs(s):
34 """Test whether a path is absolute"""
35 s = splitdrive(s)[1]
36 return s != '' and s[:1] in '/\\'
37
38
39# Join two (or more) paths.
40
41def join(a, *p):
42 """Join two or more pathname components, inserting sep as needed"""
43 path = a
44 for b in p:
45 if isabs(b):
46 path = b
47 elif path == '' or path[-1:] in '/\\:':
48 path = path + b
49 else:
50 path = path + '/' + b
51 return path
52
53
54# Split a path in a drive specification (a drive letter followed by a
55# colon) and the path specification.
56# It is always true that drivespec + pathspec == p
57def splitdrive(p):
58 """Split a pathname into drive and path specifiers. Returns a 2-tuple
59"(drive,path)"; either part may be empty"""
60 if p[1:2] == ':':
61 return p[0:2], p[2:]
62 return '', p
63
64
65# Parse UNC paths
66def splitunc(p):
67 """Split a pathname into UNC mount point and relative path specifiers.
68
69 Return a 2-tuple (unc, rest); either part may be empty.
70 If unc is not empty, it has the form '//host/mount' (or similar
71 using backslashes). unc+rest is always the input path.
72 Paths containing drive letters never have an UNC part.
73 """
74 if p[1:2] == ':':
75 return '', p # Drive letter present
76 firstTwo = p[0:2]
77 if firstTwo == '/' * 2 or firstTwo == '\\' * 2:
78 # is a UNC path:
79 # vvvvvvvvvvvvvvvvvvvv equivalent to drive letter
80 # \\machine\mountpoint\directories...
81 # directory ^^^^^^^^^^^^^^^
82 normp = normcase(p)
83 index = normp.find('/', 2)
84 if index == -1:
85 ##raise RuntimeError, 'illegal UNC path: "' + p + '"'
86 return ("", p)
87 index = normp.find('/', index + 1)
88 if index == -1:
89 index = len(p)
90 return p[:index], p[index:]
91 return '', p
92
93
94# Split a path in head (everything up to the last '/') and tail (the
95# rest). After the trailing '/' is stripped, the invariant
96# join(head, tail) == p holds.
97# The resulting head won't end in '/' unless it is the root.
98
99def split(p):
100 """Split a pathname.
101
102 Return tuple (head, tail) where tail is everything after the final slash.
103 Either part may be empty."""
104
105 d, p = splitdrive(p)
106 # set i to index beyond p's last slash
107 i = len(p)
108 while i and p[i-1] not in '/\\':
109 i = i - 1
110 head, tail = p[:i], p[i:] # now tail has no slashes
111 # remove trailing slashes from head, unless it's all slashes
112 head2 = head
113 while head2 and head2[-1] in '/\\':
114 head2 = head2[:-1]
115 head = head2 or head
116 return d + head, tail
117
118
119# Split a path in root and extension.
120# The extension is everything starting at the last dot in the last
121# pathname component; the root is everything before that.
122# It is always true that root + ext == p.
123
124def splitext(p):
125 """Split the extension from a pathname.
126
127 Extension is everything from the last dot to the end.
128 Return (root, ext), either part may be empty."""
129 root, ext = '', ''
130 for c in p:
131 if c in ['/','\\']:
132 root, ext = root + ext + c, ''
133 elif c == '.':
134 if ext:
135 root, ext = root + ext, c
136 else:
137 ext = c
138 elif ext:
139 ext = ext + c
140 else:
141 root = root + c
142 return root, ext
143
144
145# Return the tail (basename) part of a path.
146
147def basename(p):
148 """Returns the final component of a pathname"""
149 return split(p)[1]
150
151
152# Return the head (dirname) part of a path.
153
154def dirname(p):
155 """Returns the directory component of a pathname"""
156 return split(p)[0]
157
158
159# Return the longest prefix of all list elements.
160
161def commonprefix(m):
162 "Given a list of pathnames, returns the longest common leading component"
163 if not m: return ''
164 prefix = m[0]
165 for item in m:
166 for i in range(len(prefix)):
167 if prefix[:i+1] != item[:i+1]:
168 prefix = prefix[:i]
169 if i == 0: return ''
170 break
171 return prefix
172
173
174# Get size, mtime, atime of files.
175
176def getsize(filename):
177 """Return the size of a file, reported by os.stat()"""
178 st = os.stat(filename)
179 return st[stat.ST_SIZE]
180
181def getmtime(filename):
182 """Return the last modification time of a file, reported by os.stat()"""
183 st = os.stat(filename)
184 return st[stat.ST_MTIME]
185
186def getatime(filename):
187 """Return the last access time of a file, reported by os.stat()"""
188 st = os.stat(filename)
189 return st[stat.ST_ATIME]
190
191
192# Is a path a symbolic link?
193# This will always return false on systems where posix.lstat doesn't exist.
194
195def islink(path):
196 """Test for symbolic link. On OS/2 always returns false"""
197 return 0
198
199
200# Does a path exist?
201# This is false for dangling symbolic links.
202
203def exists(path):
204 """Test whether a path exists"""
205 try:
206 st = os.stat(path)
207 except os.error:
208 return 0
209 return 1
210
211
212# Is a path a directory?
213
214def isdir(path):
215 """Test whether a path is a directory"""
216 try:
217 st = os.stat(path)
218 except os.error:
219 return 0
220 return stat.S_ISDIR(st[stat.ST_MODE])
221
222
223# Is a path a regular file?
224# This follows symbolic links, so both islink() and isdir() can be true
225# for the same path.
226
227def isfile(path):
228 """Test whether a path is a regular file"""
229 try:
230 st = os.stat(path)
231 except os.error:
232 return 0
233 return stat.S_ISREG(st[stat.ST_MODE])
234
235
236# Is a path a mount point? Either a root (with or without drive letter)
237# or an UNC path with at most a / or \ after the mount point.
238
239def ismount(path):
240 """Test whether a path is a mount point (defined as root of drive)"""
241 unc, rest = splitunc(path)
242 if unc:
243 return rest in ("", "/", "\\")
244 p = splitdrive(path)[1]
245 return len(p) == 1 and p[0] in '/\\'
246
247
248# Directory tree walk.
249# For each directory under top (including top itself, but excluding
250# '.' and '..'), func(arg, dirname, filenames) is called, where
251# dirname is the name of the directory and filenames is the list
252# files files (and subdirectories etc.) in the directory.
253# The func may modify the filenames list, to implement a filter,
254# or to impose a different order of visiting.
255
256def walk(top, func, arg):
257 """Directory tree walk whth callback function.
258
259 walk(top, func, arg) calls func(arg, d, files) for each directory d
260 in the tree rooted at top (including top itself); files is a list
261 of all the files and subdirs in directory d."""
262 try:
263 names = os.listdir(top)
264 except os.error:
265 return
266 func(arg, top, names)
267 exceptions = ('.', '..')
268 for name in names:
269 if name not in exceptions:
270 name = join(top, name)
271 if isdir(name):
272 walk(name, func, arg)
273
274
275# Expand paths beginning with '~' or '~user'.
276# '~' means $HOME; '~user' means that user's home directory.
277# If the path doesn't begin with '~', or if the user or $HOME is unknown,
278# the path is returned unchanged (leaving error reporting to whatever
279# function is called with the expanded path as argument).
280# See also module 'glob' for expansion of *, ? and [...] in pathnames.
281# (A function should also be defined to do full *sh-style environment
282# variable expansion.)
283
284def expanduser(path):
285 """Expand ~ and ~user constructs.
286
287 If user or $HOME is unknown, do nothing."""
288 if path[:1] != '~':
289 return path
290 i, n = 1, len(path)
291 while i < n and path[i] not in '/\\':
292 i = i + 1
293 if i == 1:
294 if os.environ.has_key('HOME'):
295 userhome = os.environ['HOME']
296 elif not os.environ.has_key('HOMEPATH'):
297 return path
298 else:
299 try:
300 drive = os.environ['HOMEDRIVE']
301 except KeyError:
302 drive = ''
303 userhome = join(drive, os.environ['HOMEPATH'])
304 else:
305 return path
306 return userhome + path[i:]
307
308
309# Expand paths containing shell variable substitutions.
310# The following rules apply:
311# - no expansion within single quotes
312# - no escape character, except for '$$' which is translated into '$'
313# - ${varname} is accepted.
314# - varnames can be made out of letters, digits and the character '_'
315# XXX With COMMAND.COM you can use any characters in a variable name,
316# XXX except '^|<>='.
317
318def expandvars(path):
319 """Expand shell variables of form $var and ${var}.
320
321 Unknown variables are left unchanged."""
322 if '$' not in path:
323 return path
324 import string
325 varchars = string.letters + string.digits + '_-'
326 res = ''
327 index = 0
328 pathlen = len(path)
329 while index < pathlen:
330 c = path[index]
331 if c == '\'': # no expansion within single quotes
332 path = path[index + 1:]
333 pathlen = len(path)
334 try:
335 index = path.index('\'')
336 res = res + '\'' + path[:index + 1]
337 except ValueError:
338 res = res + path
339 index = pathlen - 1
340 elif c == '$': # variable or '$$'
341 if path[index + 1:index + 2] == '$':
342 res = res + c
343 index = index + 1
344 elif path[index + 1:index + 2] == '{':
345 path = path[index+2:]
346 pathlen = len(path)
347 try:
348 index = path.index('}')
349 var = path[:index]
350 if os.environ.has_key(var):
351 res = res + os.environ[var]
352 except ValueError:
353 res = res + path
354 index = pathlen - 1
355 else:
356 var = ''
357 index = index + 1
358 c = path[index:index + 1]
359 while c != '' and c in varchars:
360 var = var + c
361 index = index + 1
362 c = path[index:index + 1]
363 if os.environ.has_key(var):
364 res = res + os.environ[var]
365 if c != '':
366 res = res + c
367 else:
368 res = res + c
369 index = index + 1
370 return res
371
372
373# Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A/B.
374
375def normpath(path):
376 """Normalize path, eliminating double slashes, etc."""
377 path = path.replace('\\', '/')
378 prefix, path = splitdrive(path)
379 while path[:1] == '/':
380 prefix = prefix + '/'
381 path = path[1:]
382 comps = path.split('/')
383 i = 0
384 while i < len(comps):
385 if comps[i] == '.':
386 del comps[i]
387 elif comps[i] == '..' and i > 0 and comps[i-1] not in ('', '..'):
388 del comps[i-1:i+1]
389 i = i - 1
390 elif comps[i] == '' and i > 0 and comps[i-1] != '':
391 del comps[i]
392 else:
393 i = i + 1
394 # If the path is now empty, substitute '.'
395 if not prefix and not comps:
396 comps.append('.')
397 return prefix + '/'.join(comps)
398
399
400# Return an absolute path.
401def abspath(path):
402 """Return the absolute version of a path"""
403 if not isabs(path):
404 path = join(os.getcwd(), path)
405 return normpath(path)