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