dospath.py 9.43 KB
Newer Older
1
"""Common operations on DOS pathnames."""
2 3 4 5

import os
import stat

Skip Montanaro's avatar
Skip Montanaro committed
6 7 8 9
__all__ = ["normcase","isabs","join","splitdrive","split","splitext",
           "basename","dirname","commonprefix","getsize","getmtime",
           "getatime","islink","exists","isdir","isfile","ismount",
           "walk","expanduser","expandvars","normpath","abspath"]
10 11

def normcase(s):
12 13 14 15 16
    """Normalize the case of a pathname.
    On MS-DOS it maps the pathname to lowercase, turns slashes into
    backslashes.
    Other normalizations (such as optimizing '../' away) are not allowed
    (this is done by normpath).
Tim Peters's avatar
Tim Peters committed
17 18
    Previously, this version mapped invalid consecutive characters to a
    single '_', but this has been removed.  This functionality should
19
    possibly be added as a new function."""
20

21
    return s.replace("/", "\\").lower()
22 23 24


def isabs(s):
25 26 27 28 29
    """Return whether a path is absolute.
    Trivial in Posix, harder on the Mac or MS-DOS.
    For DOS it is absolute if it starts with a slash or backslash (current
    volume), or if a pathname after the volume letter and colon starts with
    a slash or backslash."""
30

31 32
    s = splitdrive(s)[1]
    return s != '' and s[:1] in '/\\'
33

34 35

def join(a, *p):
36
    """Join two (or more) paths."""
37

38 39 40 41
    path = a
    for b in p:
        if isabs(b):
            path = b
42
        elif path == '' or path[-1:] in '/\\:':
43 44
            path = path + b
        else:
45
            path = path + "\\" + b
46
    return path
47

Guido van Rossum's avatar
Guido van Rossum committed
48

49
def splitdrive(p):
50 51 52
    """Split a path into a drive specification (a drive letter followed
    by a colon) and path specification.
    It is always true that drivespec + pathspec == p."""
53

54 55 56
    if p[1:2] == ':':
        return p[0:2], p[2:]
    return '', p
57 58 59


def split(p):
60 61 62 63 64
    """Split a path into head (everything up to the last '/') and tail
    (the rest).  After the trailing '/' is stripped, the invariant
    join(head, tail) == p holds.
    The resulting head won't end in '/' unless it is the root."""

65 66 67 68 69 70 71 72 73 74 75 76
    d, p = splitdrive(p)
    # set i to index beyond p's last slash
    i = len(p)
    while i and p[i-1] not in '/\\':
        i = i - 1
    head, tail = p[:i], p[i:]  # now tail has no slashes
    # remove trailing slashes from head, unless it's all slashes
    head2 = head
    while head2 and head2[-1] in '/\\':
        head2 = head2[:-1]
    head = head2 or head
    return d + head, tail
77 78 79


def splitext(p):
80 81 82 83 84 85 86 87 88 89 90 91 92 93
    """Split a path into root and extension.
    The extension is everything starting at the first dot in the last
    pathname component; the root is everything before that.
    It is always true that root + ext == p."""

    root, ext = '', ''
    for c in p:
        if c in '/\\':
            root, ext = root + ext + c, ''
        elif c == '.' or ext:
            ext = ext + c
        else:
            root = root + c
    return root, ext
94 95 96


def basename(p):
97
    """Return the tail (basename) part of a path."""
98

99
    return split(p)[1]
100 101 102


def dirname(p):
103
    """Return the head (dirname) part of a path."""
104

105
    return split(p)[0]
106 107


108
def commonprefix(m):
109 110
    """Return the longest prefix of all list elements."""

111
    if not m: return ''
112 113
    prefix = m[0]
    for item in m:
114
        for i in range(len(prefix)):
115
            if prefix[:i+1] != item[:i+1]:
116 117 118
                prefix = prefix[:i]
                if i == 0: return ''
                break
119
    return prefix
120 121


122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
# Get size, mtime, atime of files.

def getsize(filename):
    """Return the size of a file, reported by os.stat()."""
    st = os.stat(filename)
    return st[stat.ST_SIZE]

def getmtime(filename):
    """Return the last modification time of a file, reported by os.stat()."""
    st = os.stat(filename)
    return st[stat.ST_MTIME]

def getatime(filename):
    """Return the last access time of a file, reported by os.stat()."""
    st = os.stat(filename)
137
    return st[stat.ST_ATIME]
138 139


140
def islink(path):
141 142
    """Is a path a symbolic link?
    This will always return false on systems where posix.lstat doesn't exist."""
143

144
    return 0
145 146 147


def exists(path):
148 149
    """Does a path exist?
    This is false for dangling symbolic links."""
150

151 152 153 154 155
    try:
        st = os.stat(path)
    except os.error:
        return 0
    return 1
156 157 158


def isdir(path):
159
    """Is a path a dos directory?"""
160

161 162 163 164 165
    try:
        st = os.stat(path)
    except os.error:
        return 0
    return stat.S_ISDIR(st[stat.ST_MODE])
166 167 168


def isfile(path):
169
    """Is a path a regular file?"""
170

171 172 173 174 175
    try:
        st = os.stat(path)
    except os.error:
        return 0
    return stat.S_ISREG(st[stat.ST_MODE])
176 177 178


def ismount(path):
179 180
    """Is a path a mount point?"""
    # XXX This degenerates in: 'is this the root?' on DOS
181

182
    return isabs(splitdrive(path)[1])
183 184 185


def walk(top, func, arg):
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
    """Directory tree walk.
    For each directory under top (including top itself, but excluding
    '.' and '..'), func(arg, dirname, filenames) is called, where
    dirname is the name of the directory and filenames is the list
    files files (and subdirectories etc.) in the directory.
    The func may modify the filenames list, to implement a filter,
    or to impose a different order of visiting."""

    try:
        names = os.listdir(top)
    except os.error:
        return
    func(arg, top, names)
    exceptions = ('.', '..')
    for name in names:
        if name not in exceptions:
            name = join(top, name)
            if isdir(name):
                walk(name, func, arg)

206 207

def expanduser(path):
208 209 210 211 212 213 214 215 216
    """Expand paths beginning with '~' or '~user'.
    '~' means $HOME; '~user' means that user's home directory.
    If the path doesn't begin with '~', or if the user or $HOME is unknown,
    the path is returned unchanged (leaving error reporting to whatever
    function is called with the expanded path as argument).
    See also module 'glob' for expansion of *, ? and [...] in pathnames.
    (A function should also be defined to do full *sh-style environment
    variable expansion.)"""

217
    if path[:1] != '~':
218 219 220 221 222 223 224 225 226 227 228 229
        return path
    i, n = 1, len(path)
    while i < n and path[i] not in '/\\':
        i = i+1
    if i == 1:
        if not os.environ.has_key('HOME'):
            return path
        userhome = os.environ['HOME']
    else:
        return path
    return userhome + path[i:]

230 231

def expandvars(path):
232 233 234 235 236 237 238 239 240 241 242
    """Expand paths containing shell variable substitutions.
    The following rules apply:
        - no expansion within single quotes
        - no escape character, except for '$$' which is translated into '$'
        - ${varname} is accepted.
        - varnames can be made out of letters, digits and the character '_'"""
    # XXX With COMMAND.COM you can use any characters in a variable name,
    # XXX except '^|<>='.

    if '$' not in path:
        return path
243
    import string
244
    varchars = string.ascii_letters + string.digits + "_-"
245 246 247 248 249 250 251 252 253
    res = ''
    index = 0
    pathlen = len(path)
    while index < pathlen:
        c = path[index]
        if c == '\'':   # no expansion within single quotes
            path = path[index + 1:]
            pathlen = len(path)
            try:
254
                index = path.index('\'')
255
                res = res + '\'' + path[:index + 1]
256
            except ValueError:
257 258 259 260 261 262 263 264 265 266
                res = res + path
                index = pathlen -1
        elif c == '$':  # variable or '$$'
            if path[index + 1:index + 2] == '$':
                res = res + c
                index = index + 1
            elif path[index + 1:index + 2] == '{':
                path = path[index+2:]
                pathlen = len(path)
                try:
267
                    index = path.index('}')
268 269 270
                    var = path[:index]
                    if os.environ.has_key(var):
                        res = res + os.environ[var]
271
                except ValueError:
272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290
                    res = res + path
                    index = pathlen - 1
            else:
                var = ''
                index = index + 1
                c = path[index:index + 1]
                while c != '' and c in varchars:
                    var = var + c
                    index = index + 1
                    c = path[index:index + 1]
                if os.environ.has_key(var):
                    res = res + os.environ[var]
                if c != '':
                    res = res + c
        else:
            res = res + c
        index = index + 1
    return res

291 292

def normpath(path):
293 294 295
    """Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A/B.
    Also, components of the path are silently truncated to 8+3 notation."""

296
    path = path.replace("/", "\\")
297
    prefix, path = splitdrive(path)
298 299
    while path[:1] == "\\":
        prefix = prefix + "\\"
300
        path = path[1:]
301
    comps = path.split("\\")
302 303 304 305 306 307 308
    i = 0
    while i < len(comps):
        if comps[i] == '.':
            del comps[i]
        elif comps[i] == '..' and i > 0 and \
                      comps[i-1] not in ('', '..'):
            del comps[i-1:i+1]
309
            i = i - 1
310
        elif comps[i] == '' and i > 0 and comps[i-1] != '':
311 312
            del comps[i]
        elif '.' in comps[i]:
313
            comp = comps[i].split('.')
314
            comps[i] = comp[0][:8] + '.' + comp[1][:3]
315
            i = i + 1
316 317
        elif len(comps[i]) > 8:
            comps[i] = comps[i][:8]
318
            i = i + 1
319
        else:
320
            i = i + 1
321 322 323
    # If the path is now empty, substitute '.'
    if not prefix and not comps:
        comps.append('.')
324
    return prefix + "\\".join(comps)
325 326 327



Guido van Rossum's avatar
Guido van Rossum committed
328
def abspath(path):
329
    """Return an absolute path."""
Guido van Rossum's avatar
Guido van Rossum committed
330 331 332
    if not isabs(path):
        path = join(os.getcwd(), path)
    return normpath(path)
333 334 335

# realpath is a no-op on systems without islink support
realpath = abspath