blob: 5a813d0e077f244d8579dd20463e24dd8ccdf307 [file] [log] [blame]
Guido van Rossum4b8c6ea2000-02-04 15:39:30 +00001"""Common operations on DOS pathnames."""
Guido van Rossum3ed23cc1994-02-15 15:57:15 +00002
3import os
4import stat
5import string
6
7
Guido van Rossum3ed23cc1994-02-15 15:57:15 +00008def normcase(s):
Guido van Rossum54f22ed2000-02-04 15:10:34 +00009 """Normalize the case of a pathname.
10 On MS-DOS it maps the pathname to lowercase, turns slashes into
11 backslashes.
12 Other normalizations (such as optimizing '../' away) are not allowed
13 (this is done by normpath).
14 Previously, this version mapped invalid consecutive characters to a
15 single '_', but this has been removed. This functionality should
16 possibly be added as a new function."""
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000017
Guido van Rossum54f22ed2000-02-04 15:10:34 +000018 return string.lower(string.replace(s, "/", "\\"))
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000019
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000020
21def isabs(s):
Guido van Rossum54f22ed2000-02-04 15:10:34 +000022 """Return whether a path is absolute.
23 Trivial in Posix, harder on the Mac or MS-DOS.
24 For DOS it is absolute if it starts with a slash or backslash (current
25 volume), or if a pathname after the volume letter and colon starts with
26 a slash or backslash."""
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000027
Guido van Rossum54f22ed2000-02-04 15:10:34 +000028 s = splitdrive(s)[1]
29 return s != '' and s[:1] in '/\\'
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000030
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000031
Guido van Rossumae590db1997-10-07 14:48:23 +000032def join(a, *p):
Guido van Rossum54f22ed2000-02-04 15:10:34 +000033 """Join two (or more) paths."""
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000034
Guido van Rossum54f22ed2000-02-04 15:10:34 +000035 path = a
36 for b in p:
37 if isabs(b):
38 path = b
39 elif path == '' or path[-1:] in '/\\':
40 path = path + b
41 else:
42 path = path + os.sep + b
43 return path
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000044
Guido van Rossumfda5c1a1995-08-10 19:27:42 +000045
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000046def splitdrive(p):
Guido van Rossum54f22ed2000-02-04 15:10:34 +000047 """Split a path into a drive specification (a drive letter followed
48 by a colon) and path specification.
49 It is always true that drivespec + pathspec == p."""
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000050
Guido van Rossum54f22ed2000-02-04 15:10:34 +000051 if p[1:2] == ':':
52 return p[0:2], p[2:]
53 return '', p
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000054
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000055
56def split(p):
Guido van Rossum54f22ed2000-02-04 15:10:34 +000057 """Split a path into head (everything up to the last '/') and tail
58 (the rest). After the trailing '/' is stripped, the invariant
59 join(head, tail) == p holds.
60 The resulting head won't end in '/' unless it is the root."""
61
Guido van Rossum8f0fa9e1999-03-19 21:05:12 +000062 d, p = splitdrive(p)
63 # set i to index beyond p's last slash
64 i = len(p)
65 while i and p[i-1] not in '/\\':
66 i = i - 1
67 head, tail = p[:i], p[i:] # now tail has no slashes
68 # remove trailing slashes from head, unless it's all slashes
69 head2 = head
70 while head2 and head2[-1] in '/\\':
71 head2 = head2[:-1]
72 head = head2 or head
73 return d + head, tail
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000074
75
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000076def splitext(p):
Guido van Rossum54f22ed2000-02-04 15:10:34 +000077 """Split a path into root and extension.
78 The extension is everything starting at the first dot in the last
79 pathname component; the root is everything before that.
80 It is always true that root + ext == p."""
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000081
Guido van Rossum54f22ed2000-02-04 15:10:34 +000082 root, ext = '', ''
83 for c in p:
84 if c in '/\\':
85 root, ext = root + ext + c, ''
86 elif c == '.' or ext:
87 ext = ext + c
88 else:
89 root = root + c
90 return root, ext
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000091
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000092
93def basename(p):
Guido van Rossum54f22ed2000-02-04 15:10:34 +000094 """Return the tail (basename) part of a path."""
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000095
Guido van Rossum54f22ed2000-02-04 15:10:34 +000096 return split(p)[1]
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000097
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000098
99def dirname(p):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000100 """Return the head (dirname) part of a path."""
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000101
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000102 return split(p)[0]
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000103
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000104
Skip Montanaro97bc98a2000-07-12 16:55:57 +0000105# Return the longest prefix of all list elements.
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000106
Skip Montanaro97bc98a2000-07-12 16:55:57 +0000107def commonprefix(m):
108 "Given a list of pathnames, returns the longest common leading component"
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000109 if not m: return ''
Skip Montanaro97bc98a2000-07-12 16:55:57 +0000110 n = m[:]
111 for i in range(len(n)):
112 n[i] = n[i].split(os.sep)
113 # if os.sep didn't have any effect, try os.altsep
114 if os.altsep and len(n[i]) == 1:
115 n[i] = n[i].split(os.altsep)
116
117 prefix = n[0]
118 for item in n:
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000119 for i in range(len(prefix)):
120 if prefix[:i+1] <> item[:i+1]:
121 prefix = prefix[:i]
122 if i == 0: return ''
123 break
Skip Montanaro97bc98a2000-07-12 16:55:57 +0000124 return os.sep.join(prefix)
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000125
126
Guido van Rossum2bc1f8f1998-07-24 20:49:26 +0000127# Get size, mtime, atime of files.
128
129def getsize(filename):
130 """Return the size of a file, reported by os.stat()."""
131 st = os.stat(filename)
132 return st[stat.ST_SIZE]
133
134def getmtime(filename):
135 """Return the last modification time of a file, reported by os.stat()."""
136 st = os.stat(filename)
137 return st[stat.ST_MTIME]
138
139def getatime(filename):
140 """Return the last access time of a file, reported by os.stat()."""
141 st = os.stat(filename)
Guido van Rossum46d56512000-07-01 10:52:49 +0000142 return st[stat.ST_ATIME]
Guido van Rossum2bc1f8f1998-07-24 20:49:26 +0000143
144
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000145def islink(path):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000146 """Is a path a symbolic link?
147 This will always return false on systems where posix.lstat doesn't exist."""
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000148
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000149 return 0
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000150
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000151
152def exists(path):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000153 """Does a path exist?
154 This is false for dangling symbolic links."""
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000155
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000156 try:
157 st = os.stat(path)
158 except os.error:
159 return 0
160 return 1
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000161
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000162
163def isdir(path):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000164 """Is a path a dos directory?"""
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000165
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000166 try:
167 st = os.stat(path)
168 except os.error:
169 return 0
170 return stat.S_ISDIR(st[stat.ST_MODE])
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000171
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000172
173def isfile(path):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000174 """Is a path a regular file?"""
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000175
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000176 try:
177 st = os.stat(path)
178 except os.error:
179 return 0
180 return stat.S_ISREG(st[stat.ST_MODE])
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000181
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000182
183def ismount(path):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000184 """Is a path a mount point?"""
185 # XXX This degenerates in: 'is this the root?' on DOS
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000186
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000187 return isabs(splitdrive(path)[1])
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000188
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000189
190def walk(top, func, arg):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000191 """Directory tree walk.
192 For each directory under top (including top itself, but excluding
193 '.' and '..'), func(arg, dirname, filenames) is called, where
194 dirname is the name of the directory and filenames is the list
195 files files (and subdirectories etc.) in the directory.
196 The func may modify the filenames list, to implement a filter,
197 or to impose a different order of visiting."""
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000198
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000199 try:
200 names = os.listdir(top)
201 except os.error:
202 return
203 func(arg, top, names)
204 exceptions = ('.', '..')
205 for name in names:
206 if name not in exceptions:
207 name = join(top, name)
208 if isdir(name):
209 walk(name, func, arg)
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000210
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000211
212def expanduser(path):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000213 """Expand paths beginning with '~' or '~user'.
214 '~' means $HOME; '~user' means that user's home directory.
215 If the path doesn't begin with '~', or if the user or $HOME is unknown,
216 the path is returned unchanged (leaving error reporting to whatever
217 function is called with the expanded path as argument).
218 See also module 'glob' for expansion of *, ? and [...] in pathnames.
219 (A function should also be defined to do full *sh-style environment
220 variable expansion.)"""
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000221
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000222 if path[:1] <> '~':
223 return path
224 i, n = 1, len(path)
225 while i < n and path[i] not in '/\\':
226 i = i+1
227 if i == 1:
228 if not os.environ.has_key('HOME'):
229 return path
230 userhome = os.environ['HOME']
231 else:
232 return path
233 return userhome + path[i:]
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000234
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000235
236varchars = string.letters + string.digits + '_-'
237
238def expandvars(path):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000239 """Expand paths containing shell variable substitutions.
240 The following rules apply:
241 - no expansion within single quotes
242 - no escape character, except for '$$' which is translated into '$'
243 - ${varname} is accepted.
244 - varnames can be made out of letters, digits and the character '_'"""
245 # XXX With COMMAND.COM you can use any characters in a variable name,
246 # XXX except '^|<>='.
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000247
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000248 if '$' not in path:
249 return path
250 res = ''
251 index = 0
252 pathlen = len(path)
253 while index < pathlen:
254 c = path[index]
255 if c == '\'': # no expansion within single quotes
256 path = path[index + 1:]
257 pathlen = len(path)
258 try:
259 index = string.index(path, '\'')
260 res = res + '\'' + path[:index + 1]
261 except string.index_error:
262 res = res + path
263 index = pathlen -1
264 elif c == '$': # variable or '$$'
265 if path[index + 1:index + 2] == '$':
266 res = res + c
267 index = index + 1
268 elif path[index + 1:index + 2] == '{':
269 path = path[index+2:]
270 pathlen = len(path)
271 try:
272 index = string.index(path, '}')
273 var = path[:index]
274 if os.environ.has_key(var):
275 res = res + os.environ[var]
276 except string.index_error:
277 res = res + path
278 index = pathlen - 1
279 else:
280 var = ''
281 index = index + 1
282 c = path[index:index + 1]
283 while c != '' and c in varchars:
284 var = var + c
285 index = index + 1
286 c = path[index:index + 1]
287 if os.environ.has_key(var):
288 res = res + os.environ[var]
289 if c != '':
290 res = res + c
291 else:
292 res = res + c
293 index = index + 1
294 return res
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000295
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000296
297def normpath(path):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000298 """Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A/B.
299 Also, components of the path are silently truncated to 8+3 notation."""
300
301 path = string.replace(path, "/", "\\")
302 prefix, path = splitdrive(path)
303 while path[:1] == os.sep:
304 prefix = prefix + os.sep
305 path = path[1:]
306 comps = string.splitfields(path, os.sep)
307 i = 0
308 while i < len(comps):
309 if comps[i] == '.':
310 del comps[i]
311 elif comps[i] == '..' and i > 0 and \
312 comps[i-1] not in ('', '..'):
313 del comps[i-1:i+1]
314 i = i-1
315 elif comps[i] == '' and i > 0 and comps[i-1] <> '':
316 del comps[i]
317 elif '.' in comps[i]:
318 comp = string.splitfields(comps[i], '.')
319 comps[i] = comp[0][:8] + '.' + comp[1][:3]
320 i = i+1
321 elif len(comps[i]) > 8:
322 comps[i] = comps[i][:8]
323 i = i+1
324 else:
325 i = i+1
326 # If the path is now empty, substitute '.'
327 if not prefix and not comps:
328 comps.append('.')
329 return prefix + string.joinfields(comps, os.sep)
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000330
Guido van Rossume294cf61999-01-29 18:05:18 +0000331
332
Guido van Rossume294cf61999-01-29 18:05:18 +0000333def abspath(path):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000334 """Return an absolute path."""
Guido van Rossume294cf61999-01-29 18:05:18 +0000335 if not isabs(path):
336 path = join(os.getcwd(), path)
337 return normpath(path)