1 # Module 'dospath' -- common operations on DOS pathnames
8 # Normalize the case of a pathname.
9 # On MS-DOS it maps the pathname to lowercase, turns slashes into
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.
18 res
, s
= splitdrive(s
)
24 return string
.lower(res
)
27 # Return wheter a path is absolute.
28 # Trivial in Posix, harder on the Mac or MS-DOS.
29 # For DOS it is absolute if it starts with a slash or backslash (current
30 # volume), or if a pathname after the volume letter and colon starts with
31 # a slash or backslash.
35 return s
!= '' and s
[:1] in '/\\'
39 # Ignore the first part if the second part is absolute.
40 # Insert a '/' unless the first part is empty or already ends in '/'.
44 if a
== '' or a
[-1:] in '/\\': return a
+ b
45 # Note: join('x', '') returns 'x/'; is this what we want?
49 # Split a path in a drive specification (a drive letter followed by a
50 # colon) and the path specification.
51 # It is always true that drivespec + pathspec == p
59 # Split a path in head (everything up to the last '/') and tail (the
60 # rest). If the original path ends in '/' but is not the root, this
61 # '/' is stripped. 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.
68 while p
and p
[-1:] in '/\\':
69 slashes
= slashes
+ p
[-1]
77 head
, tail
= head
+ tail
, ''
79 while head
and head
[-1:] in '/\\':
80 slashes
= slashes
+ head
[-1]
87 # Split a path in root and extension.
88 # The extension is everything starting at the first dot in the last
89 # pathname component; the root is everything before that.
90 # It is always true that root + ext == p.
96 root
, ext
= root
+ ext
+ c
, ''
104 # Return the tail (basename) part of a path.
110 # Return the head (dirname) part of a path.
116 # Return the longest prefix of all list elements.
122 for i
in range(len(prefix
)):
123 if prefix
[:i
+1] <> item
[:i
+1]:
130 # Is a path a symbolic link?
131 # This will always return false on systems where posix.lstat doesn't exist.
138 # This is false for dangling symbolic links.
148 # Is a path a dos directory?
149 # This follows symbolic links, so both islink() and isdir() can be true
157 return stat
.S_ISDIR(st
[stat
.ST_MODE
])
160 # Is a path a regular file?
161 # This follows symbolic links, so both islink() and isdir() can be true
169 return stat
.S_ISREG(st
[stat
.ST_MODE
])
172 # Are two filenames really pointing to the same file?
174 def samefile(f1
, f2
):
177 return samestat(s1
, s2
)
180 # Are two open files really referencing the same file?
181 # (Not necessarily the same file descriptor!)
182 # XXX THIS IS BROKEN UNDER DOS! ST_INO seems to indicate number of reads?
184 def sameopenfile(fp1
, fp2
):
185 s1
= os
.fstat(fp1
.fileno())
186 s2
= os
.fstat(fp2
.fileno())
187 return samestat(s1
, s2
)
190 # Are two stat buffers (obtained from stat, fstat or lstat)
191 # describing the same file?
193 def samestat(s1
, s2
):
194 return s1
[stat
.ST_INO
] == s2
[stat
.ST_INO
] and \
195 s1
[stat
.ST_DEV
] == s2
[stat
.ST_DEV
]
198 # Is a path a mount point?
199 # XXX This degenerates in: 'is this the root?' on DOS
202 return isabs(splitdrive(path
)[1])
205 # Directory tree walk.
206 # For each directory under top (including top itself, but excluding
207 # '.' and '..'), func(arg, dirname, filenames) is called, where
208 # dirname is the name of the directory and filenames is the list
209 # files files (and subdirectories etc.) in the directory.
210 # The func may modify the filenames list, to implement a filter,
211 # or to impose a different order of visiting.
213 def walk(top
, func
, arg
):
215 names
= os
.listdir(top
)
218 func(arg
, top
, names
)
219 exceptions
= ('.', '..')
221 if name
not in exceptions
:
222 name
= join(top
, name
)
224 walk(name
, func
, arg
)
227 # Expand paths beginning with '~' or '~user'.
228 # '~' means $HOME; '~user' means that user's home directory.
229 # If the path doesn't begin with '~', or if the user or $HOME is unknown,
230 # the path is returned unchanged (leaving error reporting to whatever
231 # function is called with the expanded path as argument).
232 # See also module 'glob' for expansion of *, ? and [...] in pathnames.
233 # (A function should also be defined to do full *sh-style environment
234 # variable expansion.)
236 def expanduser(path
):
240 while i
< n
and path
[i
] not in '/\\':
243 if not os
.environ
.has_key('HOME'):
245 userhome
= os
.environ
['HOME']
248 return userhome
+ path
[i
:]
251 # Expand paths containing shell variable substitutions.
252 # The following rules apply:
253 # - no expansion within single quotes
254 # - no escape character, except for '$$' which is translated into '$'
255 # - ${varname} is accepted.
256 # - varnames can be made out of letters, digits and the character '_'
257 # XXX With COMMAND.COM you can use any characters in a variable name,
258 # XXX except '^|<>='.
260 varchars
= string
.letters
+ string
.digits
+ '_-'
262 def expandvars(path
):
268 while index
< pathlen
:
270 if c
== '\'': # no expansion within single quotes
271 path
= path
[index
+ 1:]
274 index
= string
.index(path
, '\'')
275 res
= res
+ '\'' + path
[:index
+ 1]
276 except string
.index_error
:
279 elif c
== '$': # variable or '$$'
280 if path
[index
+ 1:index
+ 2] == '$':
283 elif path
[index
+ 1:index
+ 2] == '{':
284 path
= path
[index
+2:]
287 index
= string
.index(path
, '}')
289 if os
.environ
.has_key(var
):
290 res
= res
+ os
.environ
[var
]
291 except string
.index_error
:
297 c
= path
[index
:index
+ 1]
298 while c
!= '' and c
in varchars
:
301 c
= path
[index
:index
+ 1]
302 if os
.environ
.has_key(var
):
303 res
= res
+ os
.environ
[var
]
312 # Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A/B.
313 # Also, components of the path are silently truncated to 8+3 notation.
316 path
= normcase(path
)
317 prefix
, path
= splitdrive(path
)
318 while path
[:1] == os
.sep
:
319 prefix
= prefix
+ os
.sep
321 comps
= string
.splitfields(path
, os
.sep
)
323 while i
< len(comps
):
326 elif comps
[i
] == '..' and i
> 0 and \
327 comps
[i
-1] not in ('', '..'):
330 elif comps
[i
] == '' and i
> 0 and comps
[i
-1] <> '':
332 elif '.' in comps
[i
]:
333 comp
= string
.splitfields(comps
[i
], '.')
334 comps
[i
] = comp
[0][:8] + '.' + comp
[1][:3]
336 elif len(comps
[i
]) > 8:
337 comps
[i
] = comps
[i
][:8]
341 # If the path is now empty, substitute '.'
342 if not prefix
and not comps
:
344 return prefix
+ string
.joinfields(comps
, os
.sep
)