blob: 30c0c9fe5160881be54c9eadc9dedb3564a70481 [file] [log] [blame]
Guido van Rossum228d8072001-03-02 05:58:11 +00001# Module 'riscospath' -- common operations on RISC OS pathnames.
2
3# contributed by Andrew Clover ( andrew@oaktree.co.uk )
4
5# The "os.path" name is an alias for this module on RISC OS systems;
6# on other systems (e.g. Mac, Windows), os.path provides the same
7# operations in a manner specific to that platform, and is an alias
8# to another module (e.g. macpath, ntpath).
9
10"""
11Instead of importing this module directly, import os and refer to this module
12as os.path.
13"""
14
15
16# Imports - make an error-generating swi object if the swi module is not
17# available (ie. we are not running on RISC OS Python)
18
19import os, stat, string
20
21try:
Tim Peters683ecc72001-07-02 04:59:35 +000022 import swi
Guido van Rossum228d8072001-03-02 05:58:11 +000023except ImportError:
Tim Peters683ecc72001-07-02 04:59:35 +000024 class _swi:
25 def swi(*a):
26 raise AttributeError, 'This function only available under RISC OS'
27 block= swi
28 swi= _swi()
Guido van Rossum228d8072001-03-02 05:58:11 +000029
30[_false, _true]= range(2)
31
32_roots= ['$', '&', '%', '@', '\\']
33
34
35# _allowMOSFSNames
36# After importing riscospath, set _allowMOSFSNames true if you want the module
37# to understand the "-SomeFS-" notation left over from the old BBC Master MOS,
38# as well as the standard "SomeFS:" notation. Set this to be fully backwards
39# compatible but remember that "-SomeFS-" can also be a perfectly valid file
40# name so care must be taken when splitting and joining paths.
41
42_allowMOSFSNames= _false
43
44
45## Path manipulation, RISC OS stylee.
46
47def _split(p):
Tim Peters683ecc72001-07-02 04:59:35 +000048 """
49 split filing system name (including special field) and drive specifier from rest
50 of path. This is needed by many riscospath functions.
Guido van Rossum228d8072001-03-02 05:58:11 +000051 """
Tim Peters683ecc72001-07-02 04:59:35 +000052 dash= _allowMOSFSNames and p[:1]=='-'
53 if dash:
54 q= string.find(p, '-', 1)+1
Guido van Rossum228d8072001-03-02 05:58:11 +000055 else:
Tim Peters683ecc72001-07-02 04:59:35 +000056 if p[:1]==':':
57 q= 0
58 else:
59 q= string.find(p, ':')+1 # q= index of start of non-FS portion of path
60 s= string.find(p, '#')
61 if s==-1 or s>q:
62 s= q # find end of main FS name, not including special field
63 else:
64 for c in p[dash:s]:
Fred Drake79e75e12001-07-20 19:05:50 +000065 if c not in string.ascii_letters:
Tim Peters683ecc72001-07-02 04:59:35 +000066 q= 0
67 break # disallow invalid non-special-field characters in FS name
68 r= q
69 if p[q:q+1]==':':
70 r= string.find(p, '.', q+1)+1
71 if r==0:
72 r= len(p) # find end of drive name (if any) following FS name (if any)
73 return (p[:q], p[q:r], p[r:])
Guido van Rossum228d8072001-03-02 05:58:11 +000074
75
76def normcase(p):
Tim Peters683ecc72001-07-02 04:59:35 +000077 """
78 Normalize the case of a pathname. This converts to lowercase as the native RISC
79 OS filesystems are case-insensitive. However, not all filesystems have to be,
80 and there's no simple way to find out what type an FS is argh.
Guido van Rossum228d8072001-03-02 05:58:11 +000081 """
Tim Peters683ecc72001-07-02 04:59:35 +000082 return string.lower(p)
Guido van Rossum228d8072001-03-02 05:58:11 +000083
84
85def isabs(p):
Tim Peters683ecc72001-07-02 04:59:35 +000086 """
87 Return whether a path is absolute. Under RISC OS, a file system specifier does
88 not make a path absolute, but a drive name or number does, and so does using the
89 symbol for root, URD, library, CSD or PSD. This means it is perfectly possible
90 to have an "absolute" URL dependent on the current working directory, and
91 equally you can have a "relative" URL that's on a completely different device to
92 the current one argh.
Guido van Rossum228d8072001-03-02 05:58:11 +000093 """
Tim Peters683ecc72001-07-02 04:59:35 +000094 (fs, drive, path)= _split(p)
95 return drive!='' or path[:1] in _roots
Guido van Rossum228d8072001-03-02 05:58:11 +000096
97
98def join(a, *p):
Tim Peters683ecc72001-07-02 04:59:35 +000099 """
100 Join path elements with the directory separator, replacing the entire path when
101 an absolute or FS-changing path part is found.
Guido van Rossum228d8072001-03-02 05:58:11 +0000102 """
Tim Peters683ecc72001-07-02 04:59:35 +0000103 j= a
104 for b in p:
105 (fs, drive, path)= _split(b)
Guido van Rossume2ae77b2001-10-24 20:42:55 +0000106 if j=='' or fs!='' or drive!='' or path[:1] in _roots:
Tim Peters683ecc72001-07-02 04:59:35 +0000107 j= b
Guido van Rossume2ae77b2001-10-24 20:42:55 +0000108 elif j[-1]==':':
109 j= j+b
Tim Peters683ecc72001-07-02 04:59:35 +0000110 else:
111 j= j+'.'+b
112 return j
Guido van Rossum228d8072001-03-02 05:58:11 +0000113
114
115def split(p):
Tim Peters683ecc72001-07-02 04:59:35 +0000116 """
117 Split a path in head (everything up to the last '.') and tail (the rest). FS
118 name must still be dealt with separately since special field may contain '.'.
Guido van Rossum228d8072001-03-02 05:58:11 +0000119 """
Tim Peters683ecc72001-07-02 04:59:35 +0000120 (fs, drive, path)= _split(p)
121 q= string.rfind(path, '.')
122 if q!=-1:
123 return (fs+drive+path[:q], path[q+1:])
124 return ('', p)
Guido van Rossum228d8072001-03-02 05:58:11 +0000125
126
127def splitext(p):
Tim Peters683ecc72001-07-02 04:59:35 +0000128 """
129 Split a path in root and extension. This assumes the 'using slash for dot and
130 dot for slash with foreign files' convention common in RISC OS is in force.
Guido van Rossum228d8072001-03-02 05:58:11 +0000131 """
Tim Peters683ecc72001-07-02 04:59:35 +0000132 (tail, head)= split(p)
133 if '/' in head:
134 q= len(head)-string.rfind(head, '/')
135 return (p[:-q], p[-q:])
136 return (p, '')
Guido van Rossum228d8072001-03-02 05:58:11 +0000137
138
139def splitdrive(p):
Tim Peters683ecc72001-07-02 04:59:35 +0000140 """
141 Split a pathname into a drive specification (including FS name) and the rest of
142 the path. The terminating dot of the drive name is included in the drive
143 specification.
Guido van Rossum228d8072001-03-02 05:58:11 +0000144 """
Tim Peters683ecc72001-07-02 04:59:35 +0000145 (fs, drive, path)= _split(p)
146 return (fs+drive, p)
Guido van Rossum228d8072001-03-02 05:58:11 +0000147
148
149def basename(p):
Tim Peters683ecc72001-07-02 04:59:35 +0000150 """
151 Return the tail (basename) part of a path.
Guido van Rossum228d8072001-03-02 05:58:11 +0000152 """
Tim Peters683ecc72001-07-02 04:59:35 +0000153 return split(p)[1]
Guido van Rossum228d8072001-03-02 05:58:11 +0000154
155
156def dirname(p):
Tim Peters683ecc72001-07-02 04:59:35 +0000157 """
158 Return the head (dirname) part of a path.
Guido van Rossum228d8072001-03-02 05:58:11 +0000159 """
Tim Peters683ecc72001-07-02 04:59:35 +0000160 return split(p)[0]
Guido van Rossum228d8072001-03-02 05:58:11 +0000161
162
163def commonprefix(ps):
Tim Peters683ecc72001-07-02 04:59:35 +0000164 """
165 Return the longest prefix of all list elements. Purely string-based; does not
166 separate any path parts. Why am I in os.path?
Guido van Rossum228d8072001-03-02 05:58:11 +0000167 """
Tim Peters683ecc72001-07-02 04:59:35 +0000168 if len(ps)==0:
169 return ''
170 prefix= ps[0]
171 for p in ps[1:]:
172 prefix= prefix[:len(p)]
173 for i in range(len(prefix)):
174 if prefix[i] <> p[i]:
175 prefix= prefix[:i]
176 if i==0:
177 return ''
178 break
179 return prefix
Guido van Rossum228d8072001-03-02 05:58:11 +0000180
181
182## File access functions. Why are we in os.path?
183
184def getsize(p):
Tim Peters683ecc72001-07-02 04:59:35 +0000185 """
186 Return the size of a file, reported by os.stat().
Guido van Rossum228d8072001-03-02 05:58:11 +0000187 """
Tim Peters683ecc72001-07-02 04:59:35 +0000188 st= os.stat(p)
189 return st[stat.ST_SIZE]
Guido van Rossum228d8072001-03-02 05:58:11 +0000190
191
192def getmtime(p):
Tim Peters683ecc72001-07-02 04:59:35 +0000193 """
194 Return the last modification time of a file, reported by os.stat().
Guido van Rossum228d8072001-03-02 05:58:11 +0000195 """
Tim Peters683ecc72001-07-02 04:59:35 +0000196 st = os.stat(p)
197 return st[stat.ST_MTIME]
Guido van Rossum228d8072001-03-02 05:58:11 +0000198
199getatime= getmtime
200
201
202# RISC OS-specific file access functions
203
204def exists(p):
Tim Peters683ecc72001-07-02 04:59:35 +0000205 """
206 Test whether a path exists.
Guido van Rossum228d8072001-03-02 05:58:11 +0000207 """
Tim Peters683ecc72001-07-02 04:59:35 +0000208 try:
209 return swi.swi('OS_File', '5s;i', p)!=0
210 except swi.error:
211 return 0
Guido van Rossum228d8072001-03-02 05:58:11 +0000212
213
214def isdir(p):
Tim Peters683ecc72001-07-02 04:59:35 +0000215 """
216 Is a path a directory? Includes image files.
Guido van Rossum228d8072001-03-02 05:58:11 +0000217 """
Tim Peters683ecc72001-07-02 04:59:35 +0000218 try:
219 return swi.swi('OS_File', '5s;i', p) in [2, 3]
220 except swi.error:
221 return 0
Guido van Rossum228d8072001-03-02 05:58:11 +0000222
223
224def isfile(p):
Tim Peters683ecc72001-07-02 04:59:35 +0000225 """
226 Test whether a path is a file, including image files.
Guido van Rossum228d8072001-03-02 05:58:11 +0000227 """
Tim Peters683ecc72001-07-02 04:59:35 +0000228 try:
229 return swi.swi('OS_File', '5s;i', p) in [1, 3]
230 except swi.error:
231 return 0
Guido van Rossum228d8072001-03-02 05:58:11 +0000232
233
234def islink(p):
Tim Peters683ecc72001-07-02 04:59:35 +0000235 """
236 RISC OS has no links or mounts.
Guido van Rossum228d8072001-03-02 05:58:11 +0000237 """
Tim Peters683ecc72001-07-02 04:59:35 +0000238 return _false
Guido van Rossum228d8072001-03-02 05:58:11 +0000239
240ismount= islink
241
242
243# Same-file testing.
244
245# samefile works on filename comparison since there is no ST_DEV and ST_INO is
246# not reliably unique (esp. directories). First it has to normalise the
247# pathnames, which it can do 'properly' using OS_FSControl since samefile can
248# assume it's running on RISC OS (unlike normpath).
249
250def samefile(fa, fb):
Tim Peters683ecc72001-07-02 04:59:35 +0000251 """
252 Test whether two pathnames reference the same actual file.
Guido van Rossum228d8072001-03-02 05:58:11 +0000253 """
Tim Peters683ecc72001-07-02 04:59:35 +0000254 l= 512
255 b= swi.block(l)
256 swi.swi('OS_FSControl', 'isb..i', 37, fa, b, l)
257 fa= b.ctrlstring()
258 swi.swi('OS_FSControl', 'isb..i', 37, fb, b, l)
259 fb= b.ctrlstring()
260 return fa==fb
Guido van Rossum228d8072001-03-02 05:58:11 +0000261
262
263def sameopenfile(a, b):
Tim Peters683ecc72001-07-02 04:59:35 +0000264 """
265 Test whether two open file objects reference the same file.
Guido van Rossum228d8072001-03-02 05:58:11 +0000266 """
Tim Peters683ecc72001-07-02 04:59:35 +0000267 return os.fstat(a)[stat.ST_INO]==os.fstat(b)[stat.ST_INO]
Guido van Rossum228d8072001-03-02 05:58:11 +0000268
269
270## Path canonicalisation
271
272# 'user directory' is taken as meaning the User Root Directory, which is in
273# practice never used, for anything.
274
275def expanduser(p):
Tim Peters683ecc72001-07-02 04:59:35 +0000276 (fs, drive, path)= _split(p)
277 l= 512
278 b= swi.block(l)
Guido van Rossum228d8072001-03-02 05:58:11 +0000279
Tim Peters683ecc72001-07-02 04:59:35 +0000280 if path[:1]!='@':
281 return p
282 if fs=='':
283 fsno= swi.swi('OS_Args', '00;i')
284 swi.swi('OS_FSControl', 'iibi', 33, fsno, b, l)
285 fsname= b.ctrlstring()
Guido van Rossum228d8072001-03-02 05:58:11 +0000286 else:
Tim Peters683ecc72001-07-02 04:59:35 +0000287 if fs[:1]=='-':
288 fsname= fs[1:-1]
289 else:
290 fsname= fs[:-1]
291 fsname= string.split(fsname, '#', 1)[0] # remove special field from fs
292 x= swi.swi('OS_FSControl', 'ib2s.i;.....i', 54, b, fsname, l)
Guido van Rossum228d8072001-03-02 05:58:11 +0000293 if x<l:
Tim Peters683ecc72001-07-02 04:59:35 +0000294 urd= b.tostring(0, l-x-1)
295 else: # no URD! try CSD
296 x= swi.swi('OS_FSControl', 'ib0s.i;.....i', 54, b, fsname, l)
297 if x<l:
298 urd= b.tostring(0, l-x-1)
299 else: # no CSD! use root
300 urd= '$'
301 return fsname+':'+urd+path[1:]
Guido van Rossum228d8072001-03-02 05:58:11 +0000302
303# Environment variables are in angle brackets.
304
305def expandvars(p):
Tim Peters683ecc72001-07-02 04:59:35 +0000306 """
307 Expand environment variables using OS_GSTrans.
Guido van Rossum228d8072001-03-02 05:58:11 +0000308 """
Tim Peters683ecc72001-07-02 04:59:35 +0000309 l= 512
310 b= swi.block(l)
311 return b.tostring(0, swi.swi('OS_GSTrans', 'sbi;..i', p, b, l))
Guido van Rossum228d8072001-03-02 05:58:11 +0000312
313
Tim Peters1422e9d2001-12-15 22:12:47 +0000314# Return an absolute path. RISC OS' osfscontrol_canonicalise_path does this among others
315abspath = os.expand
Guido van Rossum228d8072001-03-02 05:58:11 +0000316
317
Guido van Rossum83eeef42001-09-17 15:16:09 +0000318# realpath is a no-op on systems without islink support
319realpath = abspath
320
321
Guido van Rossum228d8072001-03-02 05:58:11 +0000322# Normalize a path. Only special path element under RISC OS is "^" for "..".
323
324def normpath(p):
Tim Peters683ecc72001-07-02 04:59:35 +0000325 """
326 Normalize path, eliminating up-directory ^s.
Guido van Rossum228d8072001-03-02 05:58:11 +0000327 """
Tim Peters683ecc72001-07-02 04:59:35 +0000328 (fs, drive, path)= _split(p)
329 rhs= ''
330 ups= 0
331 while path!='':
332 (path, el)= split(path)
333 if el=='^':
334 ups= ups+1
Guido van Rossum228d8072001-03-02 05:58:11 +0000335 else:
Tim Peters683ecc72001-07-02 04:59:35 +0000336 if ups>0:
337 ups= ups-1
338 else:
339 if rhs=='':
340 rhs= el
341 else:
342 rhs= el+'.'+rhs
343 while ups>0:
344 ups= ups-1
345 rhs= '^.'+rhs
346 return fs+drive+rhs
Guido van Rossum228d8072001-03-02 05:58:11 +0000347
348
349# Directory tree walk.
350# Independent of host system. Why am I in os.path?
351
352def walk(top, func, arg):
Tim Peterscf5e6a42001-10-10 04:16:20 +0000353 """Directory tree walk with callback function.
354
355 For each directory in the directory tree rooted at top (including top
356 itself, but excluding '.' and '..'), call func(arg, dirname, fnames).
357 dirname is the name of the directory, and fnames a list of the names of
358 the files and subdirectories in dirname (excluding '.' and '..'). func
359 may modify the fnames list in-place (e.g. via del or slice assignment),
360 and walk will only recurse into the subdirectories whose names remain in
361 fnames; this can be used to implement a filter, or to impose a specific
362 order of visiting. No semantics are defined for, or required of, arg,
363 beyond that arg is always passed to func. It can be used, e.g., to pass
364 a filename pattern, or a mutable object designed to accumulate
365 statistics. Passing None for arg is common."""
366
Tim Peters683ecc72001-07-02 04:59:35 +0000367 try:
368 names= os.listdir(top)
369 except os.error:
370 return
371 func(arg, top, names)
372 for name in names:
373 name= join(top, name)
374 if isdir(name) and not islink(name):
375 walk(name, func, arg)