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
10 # backslashes and maps invalid consecutive characters to a single '_'.
11 # Other normalizations (such as optimizing '../' away) are not allowed
12 # (this is done by normpath).
17 res
, s
= splitdrive(s
)
21 elif c
== '.' and res
[-1:] == os
.sep
:
22 res
= res
+ mapchar
+ c
23 elif ord(c
) < 32 or c
in ' "*+,:;<=>?[]|':
24 if res
[-1:] != mapchar
:
28 return string
.lower(res
)
31 # Return wheter a path is absolute.
32 # Trivial in Posix, harder on the Mac or MS-DOS.
33 # For DOS it is absolute if it starts with a slash or backslash (current
34 # volume), or if a pathname after the volume letter and colon starts with
35 # a slash or backslash.
39 return s
!= '' and s
[:1] in '/\\'
43 # Ignore the first part if the second part is absolute.
44 # Insert a '/' unless the first part is empty or already ends in '/'.
48 if a
== '' or a
[-1:] in '/\\': return a
+ b
49 # Note: join('x', '') returns 'x/'; is this what we want?
53 # Split a path in a drive specification (a drive letter followed by a
54 # colon) and the path specification.
55 # It is always true that drivespec + pathspec == p
62 # Split a path in head (everything up to the last '/') and tail (the
63 # rest). If the original path ends in '/' but is not the root, this
64 # '/' is stripped. After the trailing '/' is stripped, the invariant
65 # join(head, tail) == p holds.
66 # The resulting head won't end in '/' unless it is the root.
71 while p
and p
[-1:] in '/\\':
72 slashes
= slashes
+ p
[-1]
80 head
, tail
= head
+ tail
, ''
82 while head
and head
[-1:] in '/\\':
83 slashes
= slashes
+ head
[-1]
90 # Split a path in root and extension.
91 # The extension is everything starting at the first dot in the last
92 # pathname component; the root is everything before that.
93 # It is always true that root + ext == p.
99 root
, ext
= root
+ ext
+ c
, ''
100 elif c
== '.' or ext
:
107 # Return the tail (basename) part of a path.
113 # Return the head (dirname) part of a path.
119 # Return the longest prefix of all list elements.
125 for i
in range(len(prefix
)):
126 if prefix
[:i
+1] <> item
[:i
+1]:
133 # Is a path a symbolic link?
134 # This will always return false on systems where posix.lstat doesn't exist.
141 # This is false for dangling symbolic links.
151 # Is a path a dos directory?
152 # This follows symbolic links, so both islink() and isdir() can be true
160 return stat
.S_ISDIR(st
[stat
.ST_MODE
])
163 # Is a path a regular file?
164 # This follows symbolic links, so both islink() and isdir() can be true
172 return stat
.S_ISREG(st
[stat
.ST_MODE
])
175 # Are two filenames really pointing to the same file?
177 def samefile(f1
, f2
):
180 return samestat(s1
, s2
)
183 # Are two open files really referencing the same file?
184 # (Not necessarily the same file descriptor!)
185 # XXX THIS IS BROKEN UNDER DOS! ST_INO seems to indicate number of reads?
187 def sameopenfile(fp1
, fp2
):
188 s1
= os
.fstat(fp1
.fileno())
189 s2
= os
.fstat(fp2
.fileno())
190 return samestat(s1
, s2
)
193 # Are two stat buffers (obtained from stat, fstat or lstat)
194 # describing the same file?
196 def samestat(s1
, s2
):
197 return s1
[stat
.ST_INO
] == s2
[stat
.ST_INO
] and \
198 s1
[stat
.ST_DEV
] == s2
[stat
.ST_DEV
]
201 # Is a path a mount point?
202 # XXX This degenerates in: 'is this the root?' on DOS
205 return isabs(splitdrive(path
)[1])
208 # Directory tree walk.
209 # For each directory under top (including top itself, but excluding
210 # '.' and '..'), func(arg, dirname, filenames) is called, where
211 # dirname is the name of the directory and filenames is the list
212 # files files (and subdirectories etc.) in the directory.
213 # The func may modify the filenames list, to implement a filter,
214 # or to impose a different order of visiting.
216 def walk(top
, func
, arg
):
218 names
= os
.listdir(top
)
221 func(arg
, top
, names
)
222 exceptions
= ('.', '..')
224 if name
not in exceptions
:
225 name
= join(top
, name
)
227 walk(name
, func
, arg
)
230 # Expand paths beginning with '~' or '~user'.
231 # '~' means $HOME; '~user' means that user's home directory.
232 # If the path doesn't begin with '~', or if the user or $HOME is unknown,
233 # the path is returned unchanged (leaving error reporting to whatever
234 # function is called with the expanded path as argument).
235 # See also module 'glob' for expansion of *, ? and [...] in pathnames.
236 # (A function should also be defined to do full *sh-style environment
237 # variable expansion.)
239 def expanduser(path
):
243 while i
< n
and path
[i
] not in '/\\':
246 if not os
.environ
.has_key('HOME'):
248 userhome
= os
.environ
['HOME']
251 return userhome
+ path
[i
:]
254 # Expand paths containing shell variable substitutions.
255 # The following rules apply:
256 # - no expansion within single quotes
257 # - no escape character, except for '$$' which is translated into '$'
258 # - ${varname} is accepted.
259 # - varnames can be made out of letters, digits and the character '_'
260 # XXX With COMMAND.COM you can use any characters in a variable name,
261 # XXX except '^|<>='.
263 varchars
= string
.letters
+ string
.digits
+ '_-'
265 def expandvars(path
):
271 while index
< pathlen
:
273 if c
== '\'': # no expansion within single quotes
274 path
= path
[index
+ 1:]
277 index
= string
.index(path
, '\'')
278 res
= res
+ '\'' + path
[:index
+ 1]
279 except string
.index_error
:
282 elif c
== '$': # variable or '$$'
283 if path
[index
+ 1:index
+ 2] == '$':
286 elif path
[index
+ 1:index
+ 2] == '{':
287 path
= path
[index
+2:]
290 index
= string
.index(path
, '}')
292 if os
.environ
.has_key(var
):
293 res
= res
+ os
.environ
[var
]
294 except string
.index_error
:
300 c
= path
[index
:index
+ 1]
301 while c
!= '' and c
in varchars
:
304 c
= path
[index
:index
+ 1]
305 if os
.environ
.has_key(var
):
306 res
= res
+ os
.environ
[var
]
315 # Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A/B.
316 # Also, components of the path are silently truncated to 8+3 notation.
319 path
= normcase(path
)
320 prefix
, path
= splitdrive(path
)
321 while path
[:1] == os
.sep
:
322 prefix
= prefix
+ os
.sep
324 comps
= string
.splitfields(path
, os
.sep
)
326 while i
< len(comps
):
329 elif comps
[i
] == '..' and i
> 0 and \
330 comps
[i
-1] not in ('', '..'):
333 elif comps
[i
] == '' and i
> 0 and comps
[i
-1] <> '':
335 elif '.' in comps
[i
]:
336 comp
= string
.splitfields(comps
[i
], '.')
337 comps
[i
] = comp
[0][:8] + '.' + comp
[1][:3]
339 elif len(comps
[i
]) > 8:
340 comps
[i
] = comps
[i
][:8]
344 # If the path is now empty, substitute '.'
345 if not prefix
and not comps
:
347 return prefix
+ string
.joinfields(comps
, os
.sep
)