blob: 4e4be5651889b626a7891726f708a575d70ee4c7 [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
105def commonprefix(m):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000106 """Return the longest prefix of all list elements."""
107
108 if not m: return ''
109 prefix = m[0]
110 for item in m:
111 for i in range(len(prefix)):
112 if prefix[:i+1] <> item[:i+1]:
113 prefix = prefix[:i]
114 if i == 0: return ''
115 break
116 return prefix
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000117
118
Guido van Rossum2bc1f8f1998-07-24 20:49:26 +0000119# Get size, mtime, atime of files.
120
121def getsize(filename):
122 """Return the size of a file, reported by os.stat()."""
123 st = os.stat(filename)
124 return st[stat.ST_SIZE]
125
126def getmtime(filename):
127 """Return the last modification time of a file, reported by os.stat()."""
128 st = os.stat(filename)
129 return st[stat.ST_MTIME]
130
131def getatime(filename):
132 """Return the last access time of a file, reported by os.stat()."""
133 st = os.stat(filename)
134 return st[stat.ST_MTIME]
135
136
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000137def islink(path):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000138 """Is a path a symbolic link?
139 This will always return false on systems where posix.lstat doesn't exist."""
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000140
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000141 return 0
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000142
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000143
144def exists(path):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000145 """Does a path exist?
146 This is false for dangling symbolic links."""
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000147
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000148 try:
149 st = os.stat(path)
150 except os.error:
151 return 0
152 return 1
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000153
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000154
155def isdir(path):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000156 """Is a path a dos directory?"""
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000157
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000158 try:
159 st = os.stat(path)
160 except os.error:
161 return 0
162 return stat.S_ISDIR(st[stat.ST_MODE])
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000163
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000164
165def isfile(path):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000166 """Is a path a regular file?"""
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000167
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000168 try:
169 st = os.stat(path)
170 except os.error:
171 return 0
172 return stat.S_ISREG(st[stat.ST_MODE])
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000173
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000174
175def ismount(path):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000176 """Is a path a mount point?"""
177 # XXX This degenerates in: 'is this the root?' on DOS
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000178
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000179 return isabs(splitdrive(path)[1])
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000180
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000181
182def walk(top, func, arg):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000183 """Directory tree walk.
184 For each directory under top (including top itself, but excluding
185 '.' and '..'), func(arg, dirname, filenames) is called, where
186 dirname is the name of the directory and filenames is the list
187 files files (and subdirectories etc.) in the directory.
188 The func may modify the filenames list, to implement a filter,
189 or to impose a different order of visiting."""
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000190
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000191 try:
192 names = os.listdir(top)
193 except os.error:
194 return
195 func(arg, top, names)
196 exceptions = ('.', '..')
197 for name in names:
198 if name not in exceptions:
199 name = join(top, name)
200 if isdir(name):
201 walk(name, func, arg)
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000202
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000203
204def expanduser(path):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000205 """Expand paths beginning with '~' or '~user'.
206 '~' means $HOME; '~user' means that user's home directory.
207 If the path doesn't begin with '~', or if the user or $HOME is unknown,
208 the path is returned unchanged (leaving error reporting to whatever
209 function is called with the expanded path as argument).
210 See also module 'glob' for expansion of *, ? and [...] in pathnames.
211 (A function should also be defined to do full *sh-style environment
212 variable expansion.)"""
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000213
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000214 if path[:1] <> '~':
215 return path
216 i, n = 1, len(path)
217 while i < n and path[i] not in '/\\':
218 i = i+1
219 if i == 1:
220 if not os.environ.has_key('HOME'):
221 return path
222 userhome = os.environ['HOME']
223 else:
224 return path
225 return userhome + path[i:]
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000226
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000227
228varchars = string.letters + string.digits + '_-'
229
230def expandvars(path):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000231 """Expand paths containing shell variable substitutions.
232 The following rules apply:
233 - no expansion within single quotes
234 - no escape character, except for '$$' which is translated into '$'
235 - ${varname} is accepted.
236 - varnames can be made out of letters, digits and the character '_'"""
237 # XXX With COMMAND.COM you can use any characters in a variable name,
238 # XXX except '^|<>='.
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000239
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000240 if '$' not in path:
241 return path
242 res = ''
243 index = 0
244 pathlen = len(path)
245 while index < pathlen:
246 c = path[index]
247 if c == '\'': # no expansion within single quotes
248 path = path[index + 1:]
249 pathlen = len(path)
250 try:
251 index = string.index(path, '\'')
252 res = res + '\'' + path[:index + 1]
253 except string.index_error:
254 res = res + path
255 index = pathlen -1
256 elif c == '$': # variable or '$$'
257 if path[index + 1:index + 2] == '$':
258 res = res + c
259 index = index + 1
260 elif path[index + 1:index + 2] == '{':
261 path = path[index+2:]
262 pathlen = len(path)
263 try:
264 index = string.index(path, '}')
265 var = path[:index]
266 if os.environ.has_key(var):
267 res = res + os.environ[var]
268 except string.index_error:
269 res = res + path
270 index = pathlen - 1
271 else:
272 var = ''
273 index = index + 1
274 c = path[index:index + 1]
275 while c != '' and c in varchars:
276 var = var + c
277 index = index + 1
278 c = path[index:index + 1]
279 if os.environ.has_key(var):
280 res = res + os.environ[var]
281 if c != '':
282 res = res + c
283 else:
284 res = res + c
285 index = index + 1
286 return res
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000287
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000288
289def normpath(path):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000290 """Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A/B.
291 Also, components of the path are silently truncated to 8+3 notation."""
292
293 path = string.replace(path, "/", "\\")
294 prefix, path = splitdrive(path)
295 while path[:1] == os.sep:
296 prefix = prefix + os.sep
297 path = path[1:]
298 comps = string.splitfields(path, os.sep)
299 i = 0
300 while i < len(comps):
301 if comps[i] == '.':
302 del comps[i]
303 elif comps[i] == '..' and i > 0 and \
304 comps[i-1] not in ('', '..'):
305 del comps[i-1:i+1]
306 i = i-1
307 elif comps[i] == '' and i > 0 and comps[i-1] <> '':
308 del comps[i]
309 elif '.' in comps[i]:
310 comp = string.splitfields(comps[i], '.')
311 comps[i] = comp[0][:8] + '.' + comp[1][:3]
312 i = i+1
313 elif len(comps[i]) > 8:
314 comps[i] = comps[i][:8]
315 i = i+1
316 else:
317 i = i+1
318 # If the path is now empty, substitute '.'
319 if not prefix and not comps:
320 comps.append('.')
321 return prefix + string.joinfields(comps, os.sep)
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000322
Guido van Rossume294cf61999-01-29 18:05:18 +0000323
324
Guido van Rossume294cf61999-01-29 18:05:18 +0000325def abspath(path):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000326 """Return an absolute path."""
Guido van Rossume294cf61999-01-29 18:05:18 +0000327 if not isabs(path):
328 path = join(os.getcwd(), path)
329 return normpath(path)