blob: 55123a1bf3a5f334a116f39848c0f588cbdaa362 [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
Skip Montanaro802bc5d2000-07-17 03:06:26 +00006import re
Guido van Rossum3ed23cc1994-02-15 15:57:15 +00007
8
Guido van Rossum3ed23cc1994-02-15 15:57:15 +00009def normcase(s):
Guido van Rossum54f22ed2000-02-04 15:10:34 +000010 """Normalize the case of a pathname.
11 On MS-DOS it maps the pathname to lowercase, turns slashes into
12 backslashes.
13 Other normalizations (such as optimizing '../' away) are not allowed
14 (this is done by normpath).
15 Previously, this version mapped invalid consecutive characters to a
16 single '_', but this has been removed. This functionality should
17 possibly be added as a new function."""
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000018
Guido van Rossum54f22ed2000-02-04 15:10:34 +000019 return string.lower(string.replace(s, "/", "\\"))
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000020
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000021
22def isabs(s):
Guido van Rossum54f22ed2000-02-04 15:10:34 +000023 """Return whether a path is absolute.
24 Trivial in Posix, harder on the Mac or MS-DOS.
25 For DOS it is absolute if it starts with a slash or backslash (current
26 volume), or if a pathname after the volume letter and colon starts with
27 a slash or backslash."""
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000028
Guido van Rossum54f22ed2000-02-04 15:10:34 +000029 s = splitdrive(s)[1]
30 return s != '' and s[:1] in '/\\'
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000031
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000032
Guido van Rossumae590db1997-10-07 14:48:23 +000033def join(a, *p):
Guido van Rossum54f22ed2000-02-04 15:10:34 +000034 """Join two (or more) paths."""
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000035
Guido van Rossum54f22ed2000-02-04 15:10:34 +000036 path = a
37 for b in p:
38 if isabs(b):
39 path = b
40 elif path == '' or path[-1:] in '/\\':
41 path = path + b
42 else:
43 path = path + os.sep + b
44 return path
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000045
Guido van Rossumfda5c1a1995-08-10 19:27:42 +000046
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000047def splitdrive(p):
Guido van Rossum54f22ed2000-02-04 15:10:34 +000048 """Split a path into a drive specification (a drive letter followed
49 by a colon) and path specification.
50 It is always true that drivespec + pathspec == p."""
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000051
Guido van Rossum54f22ed2000-02-04 15:10:34 +000052 if p[1:2] == ':':
53 return p[0:2], p[2:]
54 return '', p
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000055
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000056
57def split(p):
Guido van Rossum54f22ed2000-02-04 15:10:34 +000058 """Split a path into head (everything up to the last '/') and tail
59 (the rest). After the trailing '/' is stripped, the invariant
60 join(head, tail) == p holds.
61 The resulting head won't end in '/' unless it is the root."""
62
Guido van Rossum8f0fa9e1999-03-19 21:05:12 +000063 d, p = splitdrive(p)
64 # set i to index beyond p's last slash
65 i = len(p)
66 while i and p[i-1] not in '/\\':
67 i = i - 1
68 head, tail = p[:i], p[i:] # now tail has no slashes
69 # remove trailing slashes from head, unless it's all slashes
70 head2 = head
71 while head2 and head2[-1] in '/\\':
72 head2 = head2[:-1]
73 head = head2 or head
74 return d + head, tail
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000075
76
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000077def splitext(p):
Guido van Rossum54f22ed2000-02-04 15:10:34 +000078 """Split a path into root and extension.
79 The extension is everything starting at the first dot in the last
80 pathname component; the root is everything before that.
81 It is always true that root + ext == p."""
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000082
Guido van Rossum54f22ed2000-02-04 15:10:34 +000083 root, ext = '', ''
84 for c in p:
85 if c in '/\\':
86 root, ext = root + ext + c, ''
87 elif c == '.' or ext:
88 ext = ext + c
89 else:
90 root = root + c
91 return root, ext
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000092
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000093
94def basename(p):
Guido van Rossum54f22ed2000-02-04 15:10:34 +000095 """Return the tail (basename) part of a path."""
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000096
Guido van Rossum54f22ed2000-02-04 15:10:34 +000097 return split(p)[1]
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000098
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000099
100def dirname(p):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000101 """Return the head (dirname) part of a path."""
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000102
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000103 return split(p)[0]
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000104
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000105
Skip Montanaro97bc98a2000-07-12 16:55:57 +0000106# Return the longest prefix of all list elements.
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000107
Skip Montanaro97bc98a2000-07-12 16:55:57 +0000108def commonprefix(m):
109 "Given a list of pathnames, returns the longest common leading component"
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000110 if not m: return ''
Skip Montanaro6222c052000-07-17 03:04:19 +0000111 n = map(string.lower, m)
Skip Montanaro97bc98a2000-07-12 16:55:57 +0000112 for i in range(len(n)):
Skip Montanaro6222c052000-07-17 03:04:19 +0000113 n[i] = re.split(r"[/\\]", n[i])
Skip Montanaro97bc98a2000-07-12 16:55:57 +0000114
115 prefix = n[0]
116 for item in n:
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000117 for i in range(len(prefix)):
118 if prefix[:i+1] <> item[:i+1]:
119 prefix = prefix[:i]
120 if i == 0: return ''
121 break
Skip Montanaro6222c052000-07-17 03:04:19 +0000122 return "\\".join(prefix)
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000123
124
Guido van Rossum2bc1f8f1998-07-24 20:49:26 +0000125# Get size, mtime, atime of files.
126
127def getsize(filename):
128 """Return the size of a file, reported by os.stat()."""
129 st = os.stat(filename)
130 return st[stat.ST_SIZE]
131
132def getmtime(filename):
133 """Return the last modification time of a file, reported by os.stat()."""
134 st = os.stat(filename)
135 return st[stat.ST_MTIME]
136
137def getatime(filename):
138 """Return the last access time of a file, reported by os.stat()."""
139 st = os.stat(filename)
Guido van Rossum46d56512000-07-01 10:52:49 +0000140 return st[stat.ST_ATIME]
Guido van Rossum2bc1f8f1998-07-24 20:49:26 +0000141
142
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000143def islink(path):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000144 """Is a path a symbolic link?
145 This will always return false on systems where posix.lstat doesn't exist."""
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000146
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000147 return 0
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000148
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000149
150def exists(path):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000151 """Does a path exist?
152 This is false for dangling symbolic links."""
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000153
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000154 try:
155 st = os.stat(path)
156 except os.error:
157 return 0
158 return 1
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000159
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000160
161def isdir(path):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000162 """Is a path a dos directory?"""
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000163
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000164 try:
165 st = os.stat(path)
166 except os.error:
167 return 0
168 return stat.S_ISDIR(st[stat.ST_MODE])
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000169
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000170
171def isfile(path):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000172 """Is a path a regular file?"""
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000173
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000174 try:
175 st = os.stat(path)
176 except os.error:
177 return 0
178 return stat.S_ISREG(st[stat.ST_MODE])
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000179
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000180
181def ismount(path):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000182 """Is a path a mount point?"""
183 # XXX This degenerates in: 'is this the root?' on DOS
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000184
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000185 return isabs(splitdrive(path)[1])
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000186
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000187
188def walk(top, func, arg):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000189 """Directory tree walk.
190 For each directory under top (including top itself, but excluding
191 '.' and '..'), func(arg, dirname, filenames) is called, where
192 dirname is the name of the directory and filenames is the list
193 files files (and subdirectories etc.) in the directory.
194 The func may modify the filenames list, to implement a filter,
195 or to impose a different order of visiting."""
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000196
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000197 try:
198 names = os.listdir(top)
199 except os.error:
200 return
201 func(arg, top, names)
202 exceptions = ('.', '..')
203 for name in names:
204 if name not in exceptions:
205 name = join(top, name)
206 if isdir(name):
207 walk(name, func, arg)
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000208
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000209
210def expanduser(path):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000211 """Expand paths beginning with '~' or '~user'.
212 '~' means $HOME; '~user' means that user's home directory.
213 If the path doesn't begin with '~', or if the user or $HOME is unknown,
214 the path is returned unchanged (leaving error reporting to whatever
215 function is called with the expanded path as argument).
216 See also module 'glob' for expansion of *, ? and [...] in pathnames.
217 (A function should also be defined to do full *sh-style environment
218 variable expansion.)"""
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000219
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000220 if path[:1] <> '~':
221 return path
222 i, n = 1, len(path)
223 while i < n and path[i] not in '/\\':
224 i = i+1
225 if i == 1:
226 if not os.environ.has_key('HOME'):
227 return path
228 userhome = os.environ['HOME']
229 else:
230 return path
231 return userhome + path[i:]
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000232
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000233
234varchars = string.letters + string.digits + '_-'
235
236def expandvars(path):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000237 """Expand paths containing shell variable substitutions.
238 The following rules apply:
239 - no expansion within single quotes
240 - no escape character, except for '$$' which is translated into '$'
241 - ${varname} is accepted.
242 - varnames can be made out of letters, digits and the character '_'"""
243 # XXX With COMMAND.COM you can use any characters in a variable name,
244 # XXX except '^|<>='.
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000245
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000246 if '$' not in path:
247 return path
248 res = ''
249 index = 0
250 pathlen = len(path)
251 while index < pathlen:
252 c = path[index]
253 if c == '\'': # no expansion within single quotes
254 path = path[index + 1:]
255 pathlen = len(path)
256 try:
257 index = string.index(path, '\'')
258 res = res + '\'' + path[:index + 1]
259 except string.index_error:
260 res = res + path
261 index = pathlen -1
262 elif c == '$': # variable or '$$'
263 if path[index + 1:index + 2] == '$':
264 res = res + c
265 index = index + 1
266 elif path[index + 1:index + 2] == '{':
267 path = path[index+2:]
268 pathlen = len(path)
269 try:
270 index = string.index(path, '}')
271 var = path[:index]
272 if os.environ.has_key(var):
273 res = res + os.environ[var]
274 except string.index_error:
275 res = res + path
276 index = pathlen - 1
277 else:
278 var = ''
279 index = index + 1
280 c = path[index:index + 1]
281 while c != '' and c in varchars:
282 var = var + c
283 index = index + 1
284 c = path[index:index + 1]
285 if os.environ.has_key(var):
286 res = res + os.environ[var]
287 if c != '':
288 res = res + c
289 else:
290 res = res + c
291 index = index + 1
292 return res
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000293
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000294
295def normpath(path):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000296 """Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A/B.
297 Also, components of the path are silently truncated to 8+3 notation."""
298
299 path = string.replace(path, "/", "\\")
300 prefix, path = splitdrive(path)
301 while path[:1] == os.sep:
302 prefix = prefix + os.sep
303 path = path[1:]
304 comps = string.splitfields(path, os.sep)
305 i = 0
306 while i < len(comps):
307 if comps[i] == '.':
308 del comps[i]
309 elif comps[i] == '..' and i > 0 and \
310 comps[i-1] not in ('', '..'):
311 del comps[i-1:i+1]
312 i = i-1
313 elif comps[i] == '' and i > 0 and comps[i-1] <> '':
314 del comps[i]
315 elif '.' in comps[i]:
316 comp = string.splitfields(comps[i], '.')
317 comps[i] = comp[0][:8] + '.' + comp[1][:3]
318 i = i+1
319 elif len(comps[i]) > 8:
320 comps[i] = comps[i][:8]
321 i = i+1
322 else:
323 i = i+1
324 # If the path is now empty, substitute '.'
325 if not prefix and not comps:
326 comps.append('.')
327 return prefix + string.joinfields(comps, os.sep)
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000328
Guido van Rossume294cf61999-01-29 18:05:18 +0000329
330
Guido van Rossume294cf61999-01-29 18:05:18 +0000331def abspath(path):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000332 """Return an absolute path."""
Guido van Rossume294cf61999-01-29 18:05:18 +0000333 if not isabs(path):
334 path = join(os.getcwd(), path)
335 return normpath(path)