blob: bd5e560ceb99263f653191ed08543036fe491b64 [file] [log] [blame]
Guido van Rossum3ed23cc1994-02-15 15:57:15 +00001# Module 'dospath' -- common operations on DOS pathnames
2
3import os
4import stat
5import string
6
7
8# Normalize the case of a pathname.
9# On MS-DOS it maps the pathname to lowercase, turns slashes into
Guido van Rossumfda5c1a1995-08-10 19:27:42 +000010# backslashes.
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000011# Other normalizations (such as optimizing '../' away) are not allowed
12# (this is done by normpath).
Guido van Rossumfda5c1a1995-08-10 19:27:42 +000013# Previously, this version mapped invalid consecutive characters to a
14# single '_', but this has been removed. This functionality should
15# possibly be added as a new function.
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000016
17def normcase(s):
Guido van Rossum0d530ce1998-02-19 21:08:36 +000018 return string.lower(string.replace(s, "/", "\\"))
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000019
20
21# Return wheter a path is absolute.
22# Trivial in Posix, harder on the Mac or MS-DOS.
23# For DOS it is absolute if it starts with a slash or backslash (current
24# volume), or if a pathname after the volume letter and colon starts with
25# a slash or backslash.
26
27def isabs(s):
28 s = splitdrive(s)[1]
29 return s != '' and s[:1] in '/\\'
30
31
Guido van Rossumae590db1997-10-07 14:48:23 +000032# Join two (or more) paths.
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000033
Guido van Rossumae590db1997-10-07 14:48:23 +000034def join(a, *p):
35 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
45
46# Split a path in a drive specification (a drive letter followed by a
47# colon) and the path specification.
48# It is always true that drivespec + pathspec == p
Guido van Rossumfda5c1a1995-08-10 19:27:42 +000049
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000050def splitdrive(p):
51 if p[1:2] == ':':
52 return p[0:2], p[2:]
53 return '', p
54
55
56# Split a path in head (everything up to the last '/') and tail (the
Guido van Rossum8f0fa9e1999-03-19 21:05:12 +000057# rest). After the trailing '/' is stripped, the invariant
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000058# join(head, tail) == p holds.
59# The resulting head won't end in '/' unless it is the root.
60
61def split(p):
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
76# Split a path in root and extension.
77# The extension is everything starting at the first dot in the last
78# pathname component; the root is everything before that.
79# It is always true that root + ext == p.
80
81def splitext(p):
82 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
91
92
93# Return the tail (basename) part of a path.
94
95def basename(p):
96 return split(p)[1]
97
98
99# Return the head (dirname) part of a path.
100
101def dirname(p):
102 return split(p)[0]
103
104
105# Return the longest prefix of all list elements.
106
107def commonprefix(m):
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
117
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 +0000137# Is a path a symbolic link?
138# This will always return false on systems where posix.lstat doesn't exist.
139
140def islink(path):
Guido van Rossumbfa9f131997-11-04 18:40:53 +0000141 return 0
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000142
143
144# Does a path exist?
145# This is false for dangling symbolic links.
146
147def exists(path):
148 try:
149 st = os.stat(path)
150 except os.error:
151 return 0
152 return 1
153
154
155# Is a path a dos directory?
156# This follows symbolic links, so both islink() and isdir() can be true
157# for the same path.
158
159def isdir(path):
160 try:
161 st = os.stat(path)
162 except os.error:
163 return 0
164 return stat.S_ISDIR(st[stat.ST_MODE])
165
166
167# Is a path a regular file?
168# This follows symbolic links, so both islink() and isdir() can be true
169# for the same path.
170
171def isfile(path):
172 try:
173 st = os.stat(path)
174 except os.error:
175 return 0
176 return stat.S_ISREG(st[stat.ST_MODE])
177
178
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000179# Is a path a mount point?
180# XXX This degenerates in: 'is this the root?' on DOS
181
182def ismount(path):
183 return isabs(splitdrive(path)[1])
184
185
186# 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.
193
194def walk(top, func, arg):
195 try:
196 names = os.listdir(top)
197 except os.error:
198 return
199 func(arg, top, names)
200 exceptions = ('.', '..')
201 for name in names:
202 if name not in exceptions:
203 name = join(top, name)
204 if isdir(name):
205 walk(name, func, arg)
206
207
208# 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.)
216
217def expanduser(path):
218 if path[:1] <> '~':
219 return path
220 i, n = 1, len(path)
221 while i < n and path[i] not in '/\\':
222 i = i+1
223 if i == 1:
224 if not os.environ.has_key('HOME'):
225 return path
226 userhome = os.environ['HOME']
227 else:
228 return path
229 return userhome + path[i:]
230
231
232# 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 '^|<>='.
240
241varchars = string.letters + string.digits + '_-'
242
243def expandvars(path):
244 if '$' not in path:
245 return path
246 res = ''
247 index = 0
248 pathlen = len(path)
249 while index < pathlen:
250 c = path[index]
251 if c == '\'': # no expansion within single quotes
252 path = path[index + 1:]
253 pathlen = len(path)
254 try:
255 index = string.index(path, '\'')
256 res = res + '\'' + path[:index + 1]
257 except string.index_error:
258 res = res + path
259 index = pathlen -1
260 elif c == '$': # variable or '$$'
261 if path[index + 1:index + 2] == '$':
262 res = res + c
263 index = index + 1
264 elif path[index + 1:index + 2] == '{':
265 path = path[index+2:]
266 pathlen = len(path)
267 try:
268 index = string.index(path, '}')
269 var = path[:index]
270 if os.environ.has_key(var):
271 res = res + os.environ[var]
272 except string.index_error:
273 res = res + path
274 index = pathlen - 1
275 else:
276 var = ''
277 index = index + 1
278 c = path[index:index + 1]
279 while c != '' and c in varchars:
280 var = var + c
281 index = index + 1
282 c = path[index:index + 1]
283 if os.environ.has_key(var):
284 res = res + os.environ[var]
285 if c != '':
286 res = res + c
287 else:
288 res = res + c
289 index = index + 1
290 return res
291
292
293# 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
296def normpath(path):
Guido van Rossum0d530ce1998-02-19 21:08:36 +0000297 path = string.replace(path, "/", "\\")
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000298 prefix, path = splitdrive(path)
299 while path[:1] == os.sep:
300 prefix = prefix + os.sep
301 path = path[1:]
302 comps = string.splitfields(path, os.sep)
303 i = 0
304 while i < len(comps):
305 if comps[i] == '.':
306 del comps[i]
307 elif comps[i] == '..' and i > 0 and \
308 comps[i-1] not in ('', '..'):
309 del comps[i-1:i+1]
310 i = i-1
311 elif comps[i] == '' and i > 0 and comps[i-1] <> '':
312 del comps[i]
313 elif '.' in comps[i]:
314 comp = string.splitfields(comps[i], '.')
315 comps[i] = comp[0][:8] + '.' + comp[1][:3]
316 i = i+1
317 elif len(comps[i]) > 8:
318 comps[i] = comps[i][:8]
319 i = i+1
320 else:
321 i = i+1
322 # If the path is now empty, substitute '.'
323 if not prefix and not comps:
324 comps.append('.')
325 return prefix + string.joinfields(comps, os.sep)
326
Guido van Rossume294cf61999-01-29 18:05:18 +0000327
328
329# Return an absolute path.
330def abspath(path):
331 if not isabs(path):
332 path = join(os.getcwd(), path)
333 return normpath(path)