- Got rid of newmodule.c
[python/dscho.git] / Lib / dospath.py
blobf613a633f7d249ed6648e4f735ec75af4a3c2f4e
1 """Common operations on DOS pathnames."""
3 import os
4 import stat
6 __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"]
11 def normcase(s):
12 """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).
17 Previously, this version mapped invalid consecutive characters to a
18 single '_', but this has been removed. This functionality should
19 possibly be added as a new function."""
21 return s.replace("/", "\\").lower()
24 def isabs(s):
25 """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."""
31 s = splitdrive(s)[1]
32 return s != '' and s[:1] in '/\\'
35 def join(a, *p):
36 """Join two (or more) paths."""
38 path = a
39 for b in p:
40 if isabs(b):
41 path = b
42 elif path == '' or path[-1:] in '/\\:':
43 path = path + b
44 else:
45 path = path + "\\" + b
46 return path
49 def splitdrive(p):
50 """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."""
54 if p[1:2] == ':':
55 return p[0:2], p[2:]
56 return '', p
59 def split(p):
60 """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."""
65 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
79 def splitext(p):
80 """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."""
85 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
96 def basename(p):
97 """Return the tail (basename) part of a path."""
99 return split(p)[1]
102 def dirname(p):
103 """Return the head (dirname) part of a path."""
105 return split(p)[0]
108 def commonprefix(m):
109 """Return the longest prefix of all list elements."""
111 if not m: return ''
112 prefix = m[0]
113 for item in m:
114 for i in range(len(prefix)):
115 if prefix[:i+1] != item[:i+1]:
116 prefix = prefix[:i]
117 if i == 0: return ''
118 break
119 return prefix
122 # Get size, mtime, atime of files.
124 def getsize(filename):
125 """Return the size of a file, reported by os.stat()."""
126 return os.stat(filename).st_size
128 def getmtime(filename):
129 """Return the last modification time of a file, reported by os.stat()."""
130 return os.stat(filename).st_mtime
132 def getatime(filename):
133 """Return the last access time of a file, reported by os.stat()."""
135 return os.stat(filename).st_atime
137 def islink(path):
138 """Is a path a symbolic link?
139 This will always return false on systems where posix.lstat doesn't exist."""
141 return False
144 def exists(path):
145 """Does a path exist?
146 This is false for dangling symbolic links."""
148 try:
149 st = os.stat(path)
150 except os.error:
151 return False
152 return True
155 def isdir(path):
156 """Is a path a dos directory?"""
158 try:
159 st = os.stat(path)
160 except os.error:
161 return False
162 return stat.S_ISDIR(st.st_mode)
165 def isfile(path):
166 """Is a path a regular file?"""
168 try:
169 st = os.stat(path)
170 except os.error:
171 return False
172 return stat.S_ISREG(st.st_mode)
175 def ismount(path):
176 """Is a path a mount point?"""
177 # XXX This degenerates in: 'is this the root?' on DOS
179 return isabs(splitdrive(path)[1])
182 def walk(top, func, arg):
183 """Directory tree walk with callback function.
185 For each directory in the directory tree rooted at top (including top
186 itself, but excluding '.' and '..'), call func(arg, dirname, fnames).
187 dirname is the name of the directory, and fnames a list of the names of
188 the files and subdirectories in dirname (excluding '.' and '..'). func
189 may modify the fnames list in-place (e.g. via del or slice assignment),
190 and walk will only recurse into the subdirectories whose names remain in
191 fnames; this can be used to implement a filter, or to impose a specific
192 order of visiting. No semantics are defined for, or required of, arg,
193 beyond that arg is always passed to func. It can be used, e.g., to pass
194 a filename pattern, or a mutable object designed to accumulate
195 statistics. Passing None for arg is common."""
197 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)
210 def expanduser(path):
211 """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.)"""
220 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 'HOME' in os.environ:
227 return path
228 userhome = os.environ['HOME']
229 else:
230 return path
231 return userhome + path[i:]
234 def expandvars(path):
235 """Expand paths containing shell variable substitutions.
236 The following rules apply:
237 - no expansion within single quotes
238 - no escape character, except for '$$' which is translated into '$'
239 - ${varname} is accepted.
240 - varnames can be made out of letters, digits and the character '_'"""
241 # XXX With COMMAND.COM you can use any characters in a variable name,
242 # XXX except '^|<>='.
244 if '$' not in path:
245 return path
246 import string
247 varchars = string.ascii_letters + string.digits + "_-"
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 = path.index('\'')
258 res = res + '\'' + path[:index + 1]
259 except ValueError:
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 = path.index('}')
271 var = path[:index]
272 if var in os.environ:
273 res = res + os.environ[var]
274 except ValueError:
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 var in os.environ:
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
295 def normpath(path):
296 """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."""
299 path = path.replace("/", "\\")
300 prefix, path = splitdrive(path)
301 while path[:1] == "\\":
302 prefix = prefix + "\\"
303 path = path[1:]
304 comps = path.split("\\")
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 = comps[i].split('.')
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 + "\\".join(comps)
331 def abspath(path):
332 """Return an absolute path."""
333 if not isabs(path):
334 path = join(os.getcwd(), path)
335 return normpath(path)
337 # realpath is a no-op on systems without islink support
338 realpath = abspath