blob: ed35d5970b63a56eba7820577e56e04621e5e00a [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
Guido van Rossum3ed23cc1994-02-15 15:57:15 +00005
Skip Montanaroeccd02a2001-01-20 23:34:12 +00006__all__ = ["normcase","isabs","join","splitdrive","split","splitext",
7 "basename","dirname","commonprefix","getsize","getmtime",
8 "getatime","islink","exists","isdir","isfile","ismount",
9 "walk","expanduser","expandvars","normpath","abspath"]
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000010
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000011def normcase(s):
Guido van Rossum54f22ed2000-02-04 15:10:34 +000012 """Normalize the case of a pathname.
13 On MS-DOS it maps the pathname to lowercase, turns slashes into
14 backslashes.
15 Other normalizations (such as optimizing '../' away) are not allowed
16 (this is done by normpath).
Tim Peters88869f92001-01-14 23:36:06 +000017 Previously, this version mapped invalid consecutive characters to a
18 single '_', but this has been removed. This functionality should
Guido van Rossum54f22ed2000-02-04 15:10:34 +000019 possibly be added as a new function."""
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000020
Fred Drakeb4e460a2000-09-28 16:25:20 +000021 return s.replace("/", "\\").lower()
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000022
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000023
24def isabs(s):
Guido van Rossum54f22ed2000-02-04 15:10:34 +000025 """Return whether a path is absolute.
26 Trivial in Posix, harder on the Mac or MS-DOS.
27 For DOS it is absolute if it starts with a slash or backslash (current
28 volume), or if a pathname after the volume letter and colon starts with
29 a slash or backslash."""
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000030
Guido van Rossum54f22ed2000-02-04 15:10:34 +000031 s = splitdrive(s)[1]
32 return s != '' and s[:1] in '/\\'
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000033
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000034
Guido van Rossumae590db1997-10-07 14:48:23 +000035def join(a, *p):
Guido van Rossum54f22ed2000-02-04 15:10:34 +000036 """Join two (or more) paths."""
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000037
Guido van Rossum54f22ed2000-02-04 15:10:34 +000038 path = a
39 for b in p:
40 if isabs(b):
41 path = b
Tim Petersceeda0e2000-09-19 23:46:56 +000042 elif path == '' or path[-1:] in '/\\:':
Guido van Rossum54f22ed2000-02-04 15:10:34 +000043 path = path + b
44 else:
Fred Drakeb4e460a2000-09-28 16:25:20 +000045 path = path + "\\" + b
Guido van Rossum54f22ed2000-02-04 15:10:34 +000046 return path
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000047
Guido van Rossumfda5c1a1995-08-10 19:27:42 +000048
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000049def splitdrive(p):
Guido van Rossum54f22ed2000-02-04 15:10:34 +000050 """Split a path into a drive specification (a drive letter followed
51 by a colon) and path specification.
52 It is always true that drivespec + pathspec == p."""
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000053
Guido van Rossum54f22ed2000-02-04 15:10:34 +000054 if p[1:2] == ':':
55 return p[0:2], p[2:]
56 return '', p
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000057
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000058
59def split(p):
Guido van Rossum54f22ed2000-02-04 15:10:34 +000060 """Split a path into head (everything up to the last '/') and tail
61 (the rest). After the trailing '/' is stripped, the invariant
62 join(head, tail) == p holds.
63 The resulting head won't end in '/' unless it is the root."""
64
Guido van Rossum8f0fa9e1999-03-19 21:05:12 +000065 d, p = splitdrive(p)
66 # set i to index beyond p's last slash
67 i = len(p)
68 while i and p[i-1] not in '/\\':
69 i = i - 1
70 head, tail = p[:i], p[i:] # now tail has no slashes
71 # remove trailing slashes from head, unless it's all slashes
72 head2 = head
73 while head2 and head2[-1] in '/\\':
74 head2 = head2[:-1]
75 head = head2 or head
76 return d + head, tail
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000077
78
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000079def splitext(p):
Guido van Rossum54f22ed2000-02-04 15:10:34 +000080 """Split a path into root and extension.
81 The extension is everything starting at the first dot in the last
82 pathname component; the root is everything before that.
83 It is always true that root + ext == p."""
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000084
Guido van Rossum54f22ed2000-02-04 15:10:34 +000085 root, ext = '', ''
86 for c in p:
87 if c in '/\\':
88 root, ext = root + ext + c, ''
89 elif c == '.' or ext:
90 ext = ext + c
91 else:
92 root = root + c
93 return root, ext
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000094
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000095
96def basename(p):
Guido van Rossum54f22ed2000-02-04 15:10:34 +000097 """Return the tail (basename) part of a path."""
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000098
Guido van Rossum54f22ed2000-02-04 15:10:34 +000099 return split(p)[1]
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000100
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000101
102def dirname(p):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000103 """Return the head (dirname) part of a path."""
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000104
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000105 return split(p)[0]
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000106
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000107
Skip Montanaro97bc98a2000-07-12 16:55:57 +0000108def commonprefix(m):
Skip Montanaro62358312000-08-22 13:01:53 +0000109 """Return the longest prefix of all list elements."""
110
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000111 if not m: return ''
Skip Montanaro62358312000-08-22 13:01:53 +0000112 prefix = m[0]
113 for item in m:
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000114 for i in range(len(prefix)):
Fred Drake8152d322000-12-12 23:20:45 +0000115 if prefix[:i+1] != item[:i+1]:
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000116 prefix = prefix[:i]
117 if i == 0: return ''
118 break
Skip Montanaro62358312000-08-22 13:01:53 +0000119 return prefix
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000120
121
Guido van Rossum2bc1f8f1998-07-24 20:49:26 +0000122# Get size, mtime, atime of files.
123
124def getsize(filename):
125 """Return the size of a file, reported by os.stat()."""
126 st = os.stat(filename)
127 return st[stat.ST_SIZE]
128
129def getmtime(filename):
130 """Return the last modification time of a file, reported by os.stat()."""
131 st = os.stat(filename)
132 return st[stat.ST_MTIME]
133
134def getatime(filename):
135 """Return the last access time of a file, reported by os.stat()."""
136 st = os.stat(filename)
Guido van Rossum46d56512000-07-01 10:52:49 +0000137 return st[stat.ST_ATIME]
Guido van Rossum2bc1f8f1998-07-24 20:49:26 +0000138
139
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000140def islink(path):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000141 """Is a path a symbolic link?
142 This will always return false on systems where posix.lstat doesn't exist."""
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000143
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000144 return 0
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000145
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000146
147def exists(path):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000148 """Does a path exist?
149 This is false for dangling symbolic links."""
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000150
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000151 try:
152 st = os.stat(path)
153 except os.error:
154 return 0
155 return 1
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000156
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000157
158def isdir(path):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000159 """Is a path a dos directory?"""
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000160
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000161 try:
162 st = os.stat(path)
163 except os.error:
164 return 0
165 return stat.S_ISDIR(st[stat.ST_MODE])
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000166
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000167
168def isfile(path):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000169 """Is a path a regular file?"""
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000170
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000171 try:
172 st = os.stat(path)
173 except os.error:
174 return 0
175 return stat.S_ISREG(st[stat.ST_MODE])
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000176
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000177
178def ismount(path):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000179 """Is a path a mount point?"""
180 # XXX This degenerates in: 'is this the root?' on DOS
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000181
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000182 return isabs(splitdrive(path)[1])
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000183
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000184
185def walk(top, func, arg):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000186 """Directory tree walk.
187 For each directory under top (including top itself, but excluding
188 '.' and '..'), func(arg, dirname, filenames) is called, where
189 dirname is the name of the directory and filenames is the list
190 files files (and subdirectories etc.) in the directory.
191 The func may modify the filenames list, to implement a filter,
192 or to impose a different order of visiting."""
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000193
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000194 try:
195 names = os.listdir(top)
196 except os.error:
197 return
198 func(arg, top, names)
199 exceptions = ('.', '..')
200 for name in names:
201 if name not in exceptions:
202 name = join(top, name)
203 if isdir(name):
204 walk(name, func, arg)
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000205
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000206
207def expanduser(path):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000208 """Expand paths beginning with '~' or '~user'.
209 '~' means $HOME; '~user' means that user's home directory.
210 If the path doesn't begin with '~', or if the user or $HOME is unknown,
211 the path is returned unchanged (leaving error reporting to whatever
212 function is called with the expanded path as argument).
213 See also module 'glob' for expansion of *, ? and [...] in pathnames.
214 (A function should also be defined to do full *sh-style environment
215 variable expansion.)"""
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000216
Fred Drake8152d322000-12-12 23:20:45 +0000217 if path[:1] != '~':
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000218 return path
219 i, n = 1, len(path)
220 while i < n and path[i] not in '/\\':
221 i = i+1
222 if i == 1:
223 if not os.environ.has_key('HOME'):
224 return path
225 userhome = os.environ['HOME']
226 else:
227 return path
228 return userhome + path[i:]
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000229
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000230
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000231def expandvars(path):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000232 """Expand paths containing shell variable substitutions.
233 The following rules apply:
234 - no expansion within single quotes
235 - no escape character, except for '$$' which is translated into '$'
236 - ${varname} is accepted.
237 - varnames can be made out of letters, digits and the character '_'"""
238 # XXX With COMMAND.COM you can use any characters in a variable name,
239 # XXX except '^|<>='.
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000240
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000241 if '$' not in path:
242 return path
Fred Drakeb4e460a2000-09-28 16:25:20 +0000243 import string
244 varchars = string.letters + string.digits + '_-'
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000245 res = ''
246 index = 0
247 pathlen = len(path)
248 while index < pathlen:
249 c = path[index]
250 if c == '\'': # no expansion within single quotes
251 path = path[index + 1:]
252 pathlen = len(path)
253 try:
Fred Drakeb4e460a2000-09-28 16:25:20 +0000254 index = path.index('\'')
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000255 res = res + '\'' + path[:index + 1]
Fred Drakeb4e460a2000-09-28 16:25:20 +0000256 except ValueError:
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000257 res = res + path
258 index = pathlen -1
259 elif c == '$': # variable or '$$'
260 if path[index + 1:index + 2] == '$':
261 res = res + c
262 index = index + 1
263 elif path[index + 1:index + 2] == '{':
264 path = path[index+2:]
265 pathlen = len(path)
266 try:
Fred Drakeb4e460a2000-09-28 16:25:20 +0000267 index = path.index('}')
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000268 var = path[:index]
269 if os.environ.has_key(var):
270 res = res + os.environ[var]
Fred Drakeb4e460a2000-09-28 16:25:20 +0000271 except ValueError:
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000272 res = res + path
273 index = pathlen - 1
274 else:
275 var = ''
276 index = index + 1
277 c = path[index:index + 1]
278 while c != '' and c in varchars:
279 var = var + c
280 index = index + 1
281 c = path[index:index + 1]
282 if os.environ.has_key(var):
283 res = res + os.environ[var]
284 if c != '':
285 res = res + c
286 else:
287 res = res + c
288 index = index + 1
289 return res
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000290
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000291
292def normpath(path):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000293 """Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A/B.
294 Also, components of the path are silently truncated to 8+3 notation."""
295
Fred Drakeb4e460a2000-09-28 16:25:20 +0000296 path = path.replace("/", "\\")
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000297 prefix, path = splitdrive(path)
Fred Drakeb4e460a2000-09-28 16:25:20 +0000298 while path[:1] == "\\":
299 prefix = prefix + "\\"
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000300 path = path[1:]
Fred Drakeb4e460a2000-09-28 16:25:20 +0000301 comps = path.split("\\")
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000302 i = 0
303 while i < len(comps):
304 if comps[i] == '.':
305 del comps[i]
306 elif comps[i] == '..' and i > 0 and \
307 comps[i-1] not in ('', '..'):
308 del comps[i-1:i+1]
Fred Drakeb4e460a2000-09-28 16:25:20 +0000309 i = i - 1
Fred Drake8152d322000-12-12 23:20:45 +0000310 elif comps[i] == '' and i > 0 and comps[i-1] != '':
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000311 del comps[i]
312 elif '.' in comps[i]:
Fred Drakeb4e460a2000-09-28 16:25:20 +0000313 comp = comps[i].split('.')
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000314 comps[i] = comp[0][:8] + '.' + comp[1][:3]
Fred Drakeb4e460a2000-09-28 16:25:20 +0000315 i = i + 1
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000316 elif len(comps[i]) > 8:
317 comps[i] = comps[i][:8]
Fred Drakeb4e460a2000-09-28 16:25:20 +0000318 i = i + 1
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000319 else:
Fred Drakeb4e460a2000-09-28 16:25:20 +0000320 i = i + 1
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000321 # If the path is now empty, substitute '.'
322 if not prefix and not comps:
323 comps.append('.')
Fred Drakeb4e460a2000-09-28 16:25:20 +0000324 return prefix + "\\".join(comps)
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000325
Guido van Rossume294cf61999-01-29 18:05:18 +0000326
327
Guido van Rossume294cf61999-01-29 18:05:18 +0000328def abspath(path):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000329 """Return an absolute path."""
Guido van Rossume294cf61999-01-29 18:05:18 +0000330 if not isabs(path):
331 path = join(os.getcwd(), path)
332 return normpath(path)