1"""Pathname and path-related operations for the Macintosh."""
2
3import os
4import warnings
5from stat import *
6import genericpath
7from genericpath import *
8
9__all__ = ["normcase","isabs","join","splitdrive","split","splitext",
10           "basename","dirname","commonprefix","getsize","getmtime",
11           "getatime","getctime", "islink","exists","lexists","isdir","isfile",
12           "walk","expanduser","expandvars","normpath","abspath",
13           "curdir","pardir","sep","pathsep","defpath","altsep","extsep",
14           "devnull","realpath","supports_unicode_filenames"]
15
16# strings representing various path-related bits and pieces
17curdir = ':'
18pardir = '::'
19extsep = '.'
20sep = ':'
21pathsep = '\n'
22defpath = ':'
23altsep = None
24devnull = 'Dev:Null'
25
26# Normalize the case of a pathname.  Dummy in Posix, but <s>.lower() here.
27
28def normcase(path):
29    return path.lower()
30
31
32def isabs(s):
33    """Return true if a path is absolute.
34    On the Mac, relative paths begin with a colon,
35    but as a special case, paths with no colons at all are also relative.
36    Anything else is absolute (the string up to the first colon is the
37    volume name)."""
38
39    return ':' in s and s[0] != ':'
40
41
42def join(s, *p):
43    path = s
44    for t in p:
45        if (not s) or isabs(t):
46            path = t
47            continue
48        if t[:1] == ':':
49            t = t[1:]
50        if ':' not in path:
51            path = ':' + path
52        if path[-1:] != ':':
53            path = path + ':'
54        path = path + t
55    return path
56
57
58def split(s):
59    """Split a pathname into two parts: the directory leading up to the final
60    bit, and the basename (the filename, without colons, in that directory).
61    The result (s, t) is such that join(s, t) yields the original argument."""
62
63    if ':' not in s: return '', s
64    colon = 0
65    for i in range(len(s)):
66        if s[i] == ':': colon = i + 1
67    path, file = s[:colon-1], s[colon:]
68    if path and not ':' in path:
69        path = path + ':'
70    return path, file
71
72
73def splitext(p):
74    return genericpath._splitext(p, sep, altsep, extsep)
75splitext.__doc__ = genericpath._splitext.__doc__
76
77def splitdrive(p):
78    """Split a pathname into a drive specification and the rest of the
79    path.  Useful on DOS/Windows/NT; on the Mac, the drive is always
80    empty (don't use the volume name -- it doesn't have the same
81    syntactic and semantic oddities as DOS drive letters, such as there
82    being a separate current directory per drive)."""
83
84    return '', p
85
86
87# Short interfaces to split()
88
89def dirname(s): return split(s)[0]
90def basename(s): return split(s)[1]
91
92def ismount(s):
93    if not isabs(s):
94        return False
95    components = split(s)
96    return len(components) == 2 and components[1] == ''
97
98def islink(s):
99    """Return true if the pathname refers to a symbolic link."""
100
101    try:
102        import Carbon.File
103        return Carbon.File.ResolveAliasFile(s, 0)[2]
104    except:
105        return False
106
107# Is `stat`/`lstat` a meaningful difference on the Mac?  This is safe in any
108# case.
109
110def lexists(path):
111    """Test whether a path exists.  Returns True for broken symbolic links"""
112
113    try:
114        st = os.lstat(path)
115    except os.error:
116        return False
117    return True
118
119def expandvars(path):
120    """Dummy to retain interface-compatibility with other operating systems."""
121    return path
122
123
124def expanduser(path):
125    """Dummy to retain interface-compatibility with other operating systems."""
126    return path
127
128class norm_error(Exception):
129    """Path cannot be normalized"""
130
131def normpath(s):
132    """Normalize a pathname.  Will return the same result for
133    equivalent paths."""
134
135    if ":" not in s:
136        return ":"+s
137
138    comps = s.split(":")
139    i = 1
140    while i < len(comps)-1:
141        if comps[i] == "" and comps[i-1] != "":
142            if i > 1:
143                del comps[i-1:i+1]
144                i = i - 1
145            else:
146                # best way to handle this is to raise an exception
147                raise norm_error, 'Cannot use :: immediately after volume name'
148        else:
149            i = i + 1
150
151    s = ":".join(comps)
152
153    # remove trailing ":" except for ":" and "Volume:"
154    if s[-1] == ":" and len(comps) > 2 and s != ":"*len(s):
155        s = s[:-1]
156    return s
157
158
159def walk(top, func, arg):
160    """Directory tree walk with callback function.
161
162    For each directory in the directory tree rooted at top (including top
163    itself, but excluding '.' and '..'), call func(arg, dirname, fnames).
164    dirname is the name of the directory, and fnames a list of the names of
165    the files and subdirectories in dirname (excluding '.' and '..').  func
166    may modify the fnames list in-place (e.g. via del or slice assignment),
167    and walk will only recurse into the subdirectories whose names remain in
168    fnames; this can be used to implement a filter, or to impose a specific
169    order of visiting.  No semantics are defined for, or required of, arg,
170    beyond that arg is always passed to func.  It can be used, e.g., to pass
171    a filename pattern, or a mutable object designed to accumulate
172    statistics.  Passing None for arg is common."""
173    warnings.warnpy3k("In 3.x, os.path.walk is removed in favor of os.walk.",
174                      stacklevel=2)
175    try:
176        names = os.listdir(top)
177    except os.error:
178        return
179    func(arg, top, names)
180    for name in names:
181        name = join(top, name)
182        if isdir(name) and not islink(name):
183            walk(name, func, arg)
184
185
186def abspath(path):
187    """Return an absolute path."""
188    if not isabs(path):
189        if isinstance(path, unicode):
190            cwd = os.getcwdu()
191        else:
192            cwd = os.getcwd()
193        path = join(cwd, path)
194    return normpath(path)
195
196# realpath is a no-op on systems without islink support
197def realpath(path):
198    path = abspath(path)
199    try:
200        import Carbon.File
201    except ImportError:
202        return path
203    if not path:
204        return path
205    components = path.split(':')
206    path = components[0] + ':'
207    for c in components[1:]:
208        path = join(path, c)
209        try:
210            path = Carbon.File.FSResolveAliasFile(path, 1)[0].as_pathname()
211        except Carbon.File.Error:
212            pass
213    return path
214
215supports_unicode_filenames = True
216