blob: cb98576febb667f6b33ece27e9aeabfd4e46c377 [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 Montanaro6222c052000-07-17 03:04:19 +0000110 n = map(string.lower, m)
Skip Montanaro97bc98a2000-07-12 16:55:57 +0000111 for i in range(len(n)):
Skip Montanaro6222c052000-07-17 03:04:19 +0000112 n[i] = re.split(r"[/\\]", n[i])
Skip Montanaro97bc98a2000-07-12 16:55:57 +0000113
114 prefix = n[0]
115 for item in n:
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000116 for i in range(len(prefix)):
117 if prefix[:i+1] <> item[:i+1]:
118 prefix = prefix[:i]
119 if i == 0: return ''
120 break
Skip Montanaro6222c052000-07-17 03:04:19 +0000121 return "\\".join(prefix)
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000122
123
Guido van Rossum2bc1f8f1998-07-24 20:49:26 +0000124# Get size, mtime, atime of files.
125
126def getsize(filename):
127 """Return the size of a file, reported by os.stat()."""
128 st = os.stat(filename)
129 return st[stat.ST_SIZE]
130
131def getmtime(filename):
132 """Return the last modification time of a file, reported by os.stat()."""
133 st = os.stat(filename)
134 return st[stat.ST_MTIME]
135
136def getatime(filename):
137 """Return the last access time of a file, reported by os.stat()."""
138 st = os.stat(filename)
Guido van Rossum46d56512000-07-01 10:52:49 +0000139 return st[stat.ST_ATIME]
Guido van Rossum2bc1f8f1998-07-24 20:49:26 +0000140
141
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000142def islink(path):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000143 """Is a path a symbolic link?
144 This will always return false on systems where posix.lstat doesn't exist."""
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000145
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000146 return 0
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000147
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000148
149def exists(path):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000150 """Does a path exist?
151 This is false for dangling symbolic links."""
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000152
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000153 try:
154 st = os.stat(path)
155 except os.error:
156 return 0
157 return 1
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000158
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000159
160def isdir(path):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000161 """Is a path a dos directory?"""
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000162
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000163 try:
164 st = os.stat(path)
165 except os.error:
166 return 0
167 return stat.S_ISDIR(st[stat.ST_MODE])
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000168
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000169
170def isfile(path):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000171 """Is a path a regular file?"""
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000172
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000173 try:
174 st = os.stat(path)
175 except os.error:
176 return 0
177 return stat.S_ISREG(st[stat.ST_MODE])
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000178
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000179
180def ismount(path):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000181 """Is a path a mount point?"""
182 # XXX This degenerates in: 'is this the root?' on DOS
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000183
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000184 return isabs(splitdrive(path)[1])
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000185
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000186
187def walk(top, func, arg):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000188 """Directory tree walk.
189 For each directory under top (including top itself, but excluding
190 '.' and '..'), func(arg, dirname, filenames) is called, where
191 dirname is the name of the directory and filenames is the list
192 files files (and subdirectories etc.) in the directory.
193 The func may modify the filenames list, to implement a filter,
194 or to impose a different order of visiting."""
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000195
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000196 try:
197 names = os.listdir(top)
198 except os.error:
199 return
200 func(arg, top, names)
201 exceptions = ('.', '..')
202 for name in names:
203 if name not in exceptions:
204 name = join(top, name)
205 if isdir(name):
206 walk(name, func, arg)
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000207
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000208
209def expanduser(path):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000210 """Expand paths beginning with '~' or '~user'.
211 '~' means $HOME; '~user' means that user's home directory.
212 If the path doesn't begin with '~', or if the user or $HOME is unknown,
213 the path is returned unchanged (leaving error reporting to whatever
214 function is called with the expanded path as argument).
215 See also module 'glob' for expansion of *, ? and [...] in pathnames.
216 (A function should also be defined to do full *sh-style environment
217 variable expansion.)"""
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000218
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000219 if path[:1] <> '~':
220 return path
221 i, n = 1, len(path)
222 while i < n and path[i] not in '/\\':
223 i = i+1
224 if i == 1:
225 if not os.environ.has_key('HOME'):
226 return path
227 userhome = os.environ['HOME']
228 else:
229 return path
230 return userhome + path[i:]
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000231
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000232
233varchars = string.letters + string.digits + '_-'
234
235def expandvars(path):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000236 """Expand paths containing shell variable substitutions.
237 The following rules apply:
238 - no expansion within single quotes
239 - no escape character, except for '$$' which is translated into '$'
240 - ${varname} is accepted.
241 - varnames can be made out of letters, digits and the character '_'"""
242 # XXX With COMMAND.COM you can use any characters in a variable name,
243 # XXX except '^|<>='.
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000244
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000245 if '$' not in path:
246 return path
247 res = ''
248 index = 0
249 pathlen = len(path)
250 while index < pathlen:
251 c = path[index]
252 if c == '\'': # no expansion within single quotes
253 path = path[index + 1:]
254 pathlen = len(path)
255 try:
256 index = string.index(path, '\'')
257 res = res + '\'' + path[:index + 1]
258 except string.index_error:
259 res = res + path
260 index = pathlen -1
261 elif c == '$': # variable or '$$'
262 if path[index + 1:index + 2] == '$':
263 res = res + c
264 index = index + 1
265 elif path[index + 1:index + 2] == '{':
266 path = path[index+2:]
267 pathlen = len(path)
268 try:
269 index = string.index(path, '}')
270 var = path[:index]
271 if os.environ.has_key(var):
272 res = res + os.environ[var]
273 except string.index_error:
274 res = res + path
275 index = pathlen - 1
276 else:
277 var = ''
278 index = index + 1
279 c = path[index:index + 1]
280 while c != '' and c in varchars:
281 var = var + c
282 index = index + 1
283 c = path[index:index + 1]
284 if os.environ.has_key(var):
285 res = res + os.environ[var]
286 if c != '':
287 res = res + c
288 else:
289 res = res + c
290 index = index + 1
291 return res
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000292
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000293
294def normpath(path):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000295 """Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A/B.
296 Also, components of the path are silently truncated to 8+3 notation."""
297
298 path = string.replace(path, "/", "\\")
299 prefix, path = splitdrive(path)
300 while path[:1] == os.sep:
301 prefix = prefix + os.sep
302 path = path[1:]
303 comps = string.splitfields(path, os.sep)
304 i = 0
305 while i < len(comps):
306 if comps[i] == '.':
307 del comps[i]
308 elif comps[i] == '..' and i > 0 and \
309 comps[i-1] not in ('', '..'):
310 del comps[i-1:i+1]
311 i = i-1
312 elif comps[i] == '' and i > 0 and comps[i-1] <> '':
313 del comps[i]
314 elif '.' in comps[i]:
315 comp = string.splitfields(comps[i], '.')
316 comps[i] = comp[0][:8] + '.' + comp[1][:3]
317 i = i+1
318 elif len(comps[i]) > 8:
319 comps[i] = comps[i][:8]
320 i = i+1
321 else:
322 i = i+1
323 # If the path is now empty, substitute '.'
324 if not prefix and not comps:
325 comps.append('.')
326 return prefix + string.joinfields(comps, os.sep)
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000327
Guido van Rossume294cf61999-01-29 18:05:18 +0000328
329
Guido van Rossume294cf61999-01-29 18:05:18 +0000330def abspath(path):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000331 """Return an absolute path."""
Guido van Rossume294cf61999-01-29 18:05:18 +0000332 if not isabs(path):
333 path = join(os.getcwd(), path)
334 return normpath(path)