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