14adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao"""Pathname and path-related operations for the Macintosh."""
24adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao
34adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaoimport os
44adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaoimport warnings
54adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaofrom stat import *
64adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaoimport genericpath
74adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaofrom genericpath import *
84adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao
94adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao__all__ = ["normcase","isabs","join","splitdrive","split","splitext",
104adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao           "basename","dirname","commonprefix","getsize","getmtime",
114adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao           "getatime","getctime", "islink","exists","lexists","isdir","isfile",
124adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao           "walk","expanduser","expandvars","normpath","abspath",
134adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao           "curdir","pardir","sep","pathsep","defpath","altsep","extsep",
144adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao           "devnull","realpath","supports_unicode_filenames"]
154adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao
164adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao# strings representing various path-related bits and pieces
174adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaocurdir = ':'
184adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaopardir = '::'
194adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaoextsep = '.'
204adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaosep = ':'
214adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaopathsep = '\n'
224adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaodefpath = ':'
234adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaoaltsep = None
244adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaodevnull = 'Dev:Null'
254adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao
264adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao# Normalize the case of a pathname.  Dummy in Posix, but <s>.lower() here.
274adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao
284adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaodef normcase(path):
294adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    return path.lower()
304adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao
314adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao
324adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaodef isabs(s):
334adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    """Return true if a path is absolute.
344adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    On the Mac, relative paths begin with a colon,
354adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    but as a special case, paths with no colons at all are also relative.
364adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    Anything else is absolute (the string up to the first colon is the
374adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    volume name)."""
384adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao
394adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    return ':' in s and s[0] != ':'
404adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao
414adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao
424adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaodef join(s, *p):
434adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    path = s
444adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    for t in p:
454adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao        if (not s) or isabs(t):
464adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao            path = t
474adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao            continue
484adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao        if t[:1] == ':':
494adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao            t = t[1:]
504adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao        if ':' not in path:
514adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao            path = ':' + path
524adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao        if path[-1:] != ':':
534adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao            path = path + ':'
544adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao        path = path + t
554adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    return path
564adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao
574adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao
584adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaodef split(s):
594adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    """Split a pathname into two parts: the directory leading up to the final
604adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    bit, and the basename (the filename, without colons, in that directory).
614adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    The result (s, t) is such that join(s, t) yields the original argument."""
624adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao
634adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    if ':' not in s: return '', s
644adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    colon = 0
654adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    for i in range(len(s)):
664adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao        if s[i] == ':': colon = i + 1
674adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    path, file = s[:colon-1], s[colon:]
684adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    if path and not ':' in path:
694adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao        path = path + ':'
704adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    return path, file
714adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao
724adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao
734adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaodef splitext(p):
744adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    return genericpath._splitext(p, sep, altsep, extsep)
754adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaosplitext.__doc__ = genericpath._splitext.__doc__
764adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao
774adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaodef splitdrive(p):
784adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    """Split a pathname into a drive specification and the rest of the
794adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    path.  Useful on DOS/Windows/NT; on the Mac, the drive is always
804adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    empty (don't use the volume name -- it doesn't have the same
814adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    syntactic and semantic oddities as DOS drive letters, such as there
824adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    being a separate current directory per drive)."""
834adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao
844adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    return '', p
854adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao
864adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao
874adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao# Short interfaces to split()
884adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao
894adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaodef dirname(s): return split(s)[0]
904adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaodef basename(s): return split(s)[1]
914adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao
924adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaodef ismount(s):
934adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    if not isabs(s):
944adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao        return False
954adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    components = split(s)
964adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    return len(components) == 2 and components[1] == ''
974adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao
984adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaodef islink(s):
994adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    """Return true if the pathname refers to a symbolic link."""
1004adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao
1014adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    try:
1024adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao        import Carbon.File
1034adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao        return Carbon.File.ResolveAliasFile(s, 0)[2]
1044adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    except:
1054adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao        return False
1064adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao
1074adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao# Is `stat`/`lstat` a meaningful difference on the Mac?  This is safe in any
1084adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao# case.
1094adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao
1104adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaodef lexists(path):
1114adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    """Test whether a path exists.  Returns True for broken symbolic links"""
1124adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao
1134adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    try:
1144adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao        st = os.lstat(path)
1154adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    except os.error:
1164adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao        return False
1174adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    return True
1184adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao
1194adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaodef expandvars(path):
1204adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    """Dummy to retain interface-compatibility with other operating systems."""
1214adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    return path
1224adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao
1234adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao
1244adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaodef expanduser(path):
1254adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    """Dummy to retain interface-compatibility with other operating systems."""
1264adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    return path
1274adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao
1284adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaoclass norm_error(Exception):
1294adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    """Path cannot be normalized"""
1304adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao
1314adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaodef normpath(s):
1324adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    """Normalize a pathname.  Will return the same result for
1334adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    equivalent paths."""
1344adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao
1354adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    if ":" not in s:
1364adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao        return ":"+s
1374adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao
1384adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    comps = s.split(":")
1394adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    i = 1
1404adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    while i < len(comps)-1:
1414adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao        if comps[i] == "" and comps[i-1] != "":
1424adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao            if i > 1:
1434adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao                del comps[i-1:i+1]
1444adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao                i = i - 1
1454adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao            else:
1464adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao                # best way to handle this is to raise an exception
1474adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao                raise norm_error, 'Cannot use :: immediately after volume name'
1484adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao        else:
1494adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao            i = i + 1
1504adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao
1514adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    s = ":".join(comps)
1524adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao
1534adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    # remove trailing ":" except for ":" and "Volume:"
1544adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    if s[-1] == ":" and len(comps) > 2 and s != ":"*len(s):
1554adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao        s = s[:-1]
1564adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    return s
1574adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao
1584adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao
1594adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaodef walk(top, func, arg):
1604adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    """Directory tree walk with callback function.
1614adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao
1624adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    For each directory in the directory tree rooted at top (including top
1634adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    itself, but excluding '.' and '..'), call func(arg, dirname, fnames).
1644adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    dirname is the name of the directory, and fnames a list of the names of
1654adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    the files and subdirectories in dirname (excluding '.' and '..').  func
1664adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    may modify the fnames list in-place (e.g. via del or slice assignment),
1674adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    and walk will only recurse into the subdirectories whose names remain in
1684adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    fnames; this can be used to implement a filter, or to impose a specific
1694adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    order of visiting.  No semantics are defined for, or required of, arg,
1704adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    beyond that arg is always passed to func.  It can be used, e.g., to pass
1714adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    a filename pattern, or a mutable object designed to accumulate
1724adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    statistics.  Passing None for arg is common."""
1734adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    warnings.warnpy3k("In 3.x, os.path.walk is removed in favor of os.walk.",
1744adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao                      stacklevel=2)
1754adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    try:
1764adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao        names = os.listdir(top)
1774adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    except os.error:
1784adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao        return
1794adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    func(arg, top, names)
1804adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    for name in names:
1814adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao        name = join(top, name)
1824adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao        if isdir(name) and not islink(name):
1834adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao            walk(name, func, arg)
1844adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao
1854adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao
1864adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaodef abspath(path):
1874adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    """Return an absolute path."""
1884adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    if not isabs(path):
1894adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao        if isinstance(path, unicode):
1904adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao            cwd = os.getcwdu()
1914adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao        else:
1924adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao            cwd = os.getcwd()
1934adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao        path = join(cwd, path)
1944adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    return normpath(path)
1954adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao
1964adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao# realpath is a no-op on systems without islink support
1974adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaodef realpath(path):
1984adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    path = abspath(path)
1994adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    try:
2004adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao        import Carbon.File
2014adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    except ImportError:
2024adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao        return path
2034adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    if not path:
2044adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao        return path
2054adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    components = path.split(':')
2064adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    path = components[0] + ':'
2074adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    for c in components[1:]:
2084adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao        path = join(path, c)
2094adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao        try:
2104adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao            path = Carbon.File.FSResolveAliasFile(path, 1)[0].as_pathname()
2114adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao        except Carbon.File.Error:
2124adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao            pass
2134adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao    return path
2144adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao
2154adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaosupports_unicode_filenames = True
216