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