struct.pack has become picky about h (short) and H (unsigned short).
[python/dscho.git] / Lib / dospath.py
blobf44d499e53f9bd11f79d3e0f6bb2052ffe754ad0
1 """Common operations on DOS pathnames."""
3 import os
4 import stat
7 def normcase(s):
8 """Normalize the case of a pathname.
9 On MS-DOS it maps the pathname to lowercase, turns slashes into
10 backslashes.
11 Other normalizations (such as optimizing '../' away) are not allowed
12 (this is done by normpath).
13 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."""
17 return s.replace("/", "\\").lower()
20 def isabs(s):
21 """Return whether 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."""
27 s = splitdrive(s)[1]
28 return s != '' and s[:1] in '/\\'
31 def join(a, *p):
32 """Join two (or more) paths."""
34 path = a
35 for b in p:
36 if isabs(b):
37 path = b
38 elif path == '' or path[-1:] in '/\\:':
39 path = path + b
40 else:
41 path = path + "\\" + b
42 return path
45 def splitdrive(p):
46 """Split a path into a drive specification (a drive letter followed
47 by a colon) and path specification.
48 It is always true that drivespec + pathspec == p."""
50 if p[1:2] == ':':
51 return p[0:2], p[2:]
52 return '', p
55 def split(p):
56 """Split a path into head (everything up to the last '/') and tail
57 (the rest). After the trailing '/' is stripped, the invariant
58 join(head, tail) == p holds.
59 The resulting head won't end in '/' unless it is the root."""
61 d, p = splitdrive(p)
62 # set i to index beyond p's last slash
63 i = len(p)
64 while i and p[i-1] not in '/\\':
65 i = i - 1
66 head, tail = p[:i], p[i:] # now tail has no slashes
67 # remove trailing slashes from head, unless it's all slashes
68 head2 = head
69 while head2 and head2[-1] in '/\\':
70 head2 = head2[:-1]
71 head = head2 or head
72 return d + head, tail
75 def splitext(p):
76 """Split a path into 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."""
81 root, ext = '', ''
82 for c in p:
83 if c in '/\\':
84 root, ext = root + ext + c, ''
85 elif c == '.' or ext:
86 ext = ext + c
87 else:
88 root = root + c
89 return root, ext
92 def basename(p):
93 """Return the tail (basename) part of a path."""
95 return split(p)[1]
98 def dirname(p):
99 """Return the head (dirname) part of a path."""
101 return split(p)[0]
104 def commonprefix(m):
105 """Return the longest prefix of all list elements."""
107 if not m: return ''
108 prefix = m[0]
109 for item in m:
110 for i in range(len(prefix)):
111 if prefix[:i+1] <> item[:i+1]:
112 prefix = prefix[:i]
113 if i == 0: return ''
114 break
115 return prefix
118 # Get size, mtime, atime of files.
120 def getsize(filename):
121 """Return the size of a file, reported by os.stat()."""
122 st = os.stat(filename)
123 return st[stat.ST_SIZE]
125 def getmtime(filename):
126 """Return the last modification time of a file, reported by os.stat()."""
127 st = os.stat(filename)
128 return st[stat.ST_MTIME]
130 def getatime(filename):
131 """Return the last access time of a file, reported by os.stat()."""
132 st = os.stat(filename)
133 return st[stat.ST_ATIME]
136 def islink(path):
137 """Is a path a symbolic link?
138 This will always return false on systems where posix.lstat doesn't exist."""
140 return 0
143 def exists(path):
144 """Does a path exist?
145 This is false for dangling symbolic links."""
147 try:
148 st = os.stat(path)
149 except os.error:
150 return 0
151 return 1
154 def isdir(path):
155 """Is a path a dos directory?"""
157 try:
158 st = os.stat(path)
159 except os.error:
160 return 0
161 return stat.S_ISDIR(st[stat.ST_MODE])
164 def isfile(path):
165 """Is a path a regular file?"""
167 try:
168 st = os.stat(path)
169 except os.error:
170 return 0
171 return stat.S_ISREG(st[stat.ST_MODE])
174 def ismount(path):
175 """Is a path a mount point?"""
176 # XXX This degenerates in: 'is this the root?' on DOS
178 return isabs(splitdrive(path)[1])
181 def walk(top, func, arg):
182 """Directory tree walk.
183 For each directory under top (including top itself, but excluding
184 '.' and '..'), func(arg, dirname, filenames) is called, where
185 dirname is the name of the directory and filenames is the list
186 files files (and subdirectories etc.) in the directory.
187 The func may modify the filenames list, to implement a filter,
188 or to impose a different order of visiting."""
190 try:
191 names = os.listdir(top)
192 except os.error:
193 return
194 func(arg, top, names)
195 exceptions = ('.', '..')
196 for name in names:
197 if name not in exceptions:
198 name = join(top, name)
199 if isdir(name):
200 walk(name, func, arg)
203 def expanduser(path):
204 """Expand paths beginning with '~' or '~user'.
205 '~' means $HOME; '~user' means that user's home directory.
206 If the path doesn't begin with '~', or if the user or $HOME is unknown,
207 the path is returned unchanged (leaving error reporting to whatever
208 function is called with the expanded path as argument).
209 See also module 'glob' for expansion of *, ? and [...] in pathnames.
210 (A function should also be defined to do full *sh-style environment
211 variable expansion.)"""
213 if path[:1] <> '~':
214 return path
215 i, n = 1, len(path)
216 while i < n and path[i] not in '/\\':
217 i = i+1
218 if i == 1:
219 if not os.environ.has_key('HOME'):
220 return path
221 userhome = os.environ['HOME']
222 else:
223 return path
224 return userhome + path[i:]
227 def expandvars(path):
228 """Expand paths containing shell variable substitutions.
229 The following rules apply:
230 - no expansion within single quotes
231 - no escape character, except for '$$' which is translated into '$'
232 - ${varname} is accepted.
233 - varnames can be made out of letters, digits and the character '_'"""
234 # XXX With COMMAND.COM you can use any characters in a variable name,
235 # XXX except '^|<>='.
237 if '$' not in path:
238 return path
239 import string
240 varchars = string.letters + string.digits + '_-'
241 res = ''
242 index = 0
243 pathlen = len(path)
244 while index < pathlen:
245 c = path[index]
246 if c == '\'': # no expansion within single quotes
247 path = path[index + 1:]
248 pathlen = len(path)
249 try:
250 index = path.index('\'')
251 res = res + '\'' + path[:index + 1]
252 except ValueError:
253 res = res + path
254 index = pathlen -1
255 elif c == '$': # variable or '$$'
256 if path[index + 1:index + 2] == '$':
257 res = res + c
258 index = index + 1
259 elif path[index + 1:index + 2] == '{':
260 path = path[index+2:]
261 pathlen = len(path)
262 try:
263 index = path.index('}')
264 var = path[:index]
265 if os.environ.has_key(var):
266 res = res + os.environ[var]
267 except ValueError:
268 res = res + path
269 index = pathlen - 1
270 else:
271 var = ''
272 index = index + 1
273 c = path[index:index + 1]
274 while c != '' and c in varchars:
275 var = var + c
276 index = index + 1
277 c = path[index:index + 1]
278 if os.environ.has_key(var):
279 res = res + os.environ[var]
280 if c != '':
281 res = res + c
282 else:
283 res = res + c
284 index = index + 1
285 return res
288 def normpath(path):
289 """Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A/B.
290 Also, components of the path are silently truncated to 8+3 notation."""
292 path = path.replace("/", "\\")
293 prefix, path = splitdrive(path)
294 while path[:1] == "\\":
295 prefix = prefix + "\\"
296 path = path[1:]
297 comps = path.split("\\")
298 i = 0
299 while i < len(comps):
300 if comps[i] == '.':
301 del comps[i]
302 elif comps[i] == '..' and i > 0 and \
303 comps[i-1] not in ('', '..'):
304 del comps[i-1:i+1]
305 i = i - 1
306 elif comps[i] == '' and i > 0 and comps[i-1] <> '':
307 del comps[i]
308 elif '.' in comps[i]:
309 comp = comps[i].split('.')
310 comps[i] = comp[0][:8] + '.' + comp[1][:3]
311 i = i + 1
312 elif len(comps[i]) > 8:
313 comps[i] = comps[i][:8]
314 i = i + 1
315 else:
316 i = i + 1
317 # If the path is now empty, substitute '.'
318 if not prefix and not comps:
319 comps.append('.')
320 return prefix + "\\".join(comps)
324 def abspath(path):
325 """Return an absolute path."""
326 if not isabs(path):
327 path = join(os.getcwd(), path)
328 return normpath(path)