blob: 8eda834d564894838e5d901bac80d13e1c5f2703 [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)
106 if fs!='' or drive!='' or path[:1] in _roots:
107 j= b
108 else:
109 j= j+'.'+b
110 return j
Guido van Rossum228d8072001-03-02 05:58:11 +0000111
112
113def split(p):
Tim Peters683ecc72001-07-02 04:59:35 +0000114 """
115 Split a path in head (everything up to the last '.') and tail (the rest). FS
116 name must still be dealt with separately since special field may contain '.'.
Guido van Rossum228d8072001-03-02 05:58:11 +0000117 """
Tim Peters683ecc72001-07-02 04:59:35 +0000118 (fs, drive, path)= _split(p)
119 q= string.rfind(path, '.')
120 if q!=-1:
121 return (fs+drive+path[:q], path[q+1:])
122 return ('', p)
Guido van Rossum228d8072001-03-02 05:58:11 +0000123
124
125def splitext(p):
Tim Peters683ecc72001-07-02 04:59:35 +0000126 """
127 Split a path in root and extension. This assumes the 'using slash for dot and
128 dot for slash with foreign files' convention common in RISC OS is in force.
Guido van Rossum228d8072001-03-02 05:58:11 +0000129 """
Tim Peters683ecc72001-07-02 04:59:35 +0000130 (tail, head)= split(p)
131 if '/' in head:
132 q= len(head)-string.rfind(head, '/')
133 return (p[:-q], p[-q:])
134 return (p, '')
Guido van Rossum228d8072001-03-02 05:58:11 +0000135
136
137def splitdrive(p):
Tim Peters683ecc72001-07-02 04:59:35 +0000138 """
139 Split a pathname into a drive specification (including FS name) and the rest of
140 the path. The terminating dot of the drive name is included in the drive
141 specification.
Guido van Rossum228d8072001-03-02 05:58:11 +0000142 """
Tim Peters683ecc72001-07-02 04:59:35 +0000143 (fs, drive, path)= _split(p)
144 return (fs+drive, p)
Guido van Rossum228d8072001-03-02 05:58:11 +0000145
146
147def basename(p):
Tim Peters683ecc72001-07-02 04:59:35 +0000148 """
149 Return the tail (basename) part of a path.
Guido van Rossum228d8072001-03-02 05:58:11 +0000150 """
Tim Peters683ecc72001-07-02 04:59:35 +0000151 return split(p)[1]
Guido van Rossum228d8072001-03-02 05:58:11 +0000152
153
154def dirname(p):
Tim Peters683ecc72001-07-02 04:59:35 +0000155 """
156 Return the head (dirname) part of a path.
Guido van Rossum228d8072001-03-02 05:58:11 +0000157 """
Tim Peters683ecc72001-07-02 04:59:35 +0000158 return split(p)[0]
Guido van Rossum228d8072001-03-02 05:58:11 +0000159
160
161def commonprefix(ps):
Tim Peters683ecc72001-07-02 04:59:35 +0000162 """
163 Return the longest prefix of all list elements. Purely string-based; does not
164 separate any path parts. Why am I in os.path?
Guido van Rossum228d8072001-03-02 05:58:11 +0000165 """
Tim Peters683ecc72001-07-02 04:59:35 +0000166 if len(ps)==0:
167 return ''
168 prefix= ps[0]
169 for p in ps[1:]:
170 prefix= prefix[:len(p)]
171 for i in range(len(prefix)):
172 if prefix[i] <> p[i]:
173 prefix= prefix[:i]
174 if i==0:
175 return ''
176 break
177 return prefix
Guido van Rossum228d8072001-03-02 05:58:11 +0000178
179
180## File access functions. Why are we in os.path?
181
182def getsize(p):
Tim Peters683ecc72001-07-02 04:59:35 +0000183 """
184 Return the size of a file, reported by os.stat().
Guido van Rossum228d8072001-03-02 05:58:11 +0000185 """
Tim Peters683ecc72001-07-02 04:59:35 +0000186 st= os.stat(p)
187 return st[stat.ST_SIZE]
Guido van Rossum228d8072001-03-02 05:58:11 +0000188
189
190def getmtime(p):
Tim Peters683ecc72001-07-02 04:59:35 +0000191 """
192 Return the last modification time of a file, reported by os.stat().
Guido van Rossum228d8072001-03-02 05:58:11 +0000193 """
Tim Peters683ecc72001-07-02 04:59:35 +0000194 st = os.stat(p)
195 return st[stat.ST_MTIME]
Guido van Rossum228d8072001-03-02 05:58:11 +0000196
197getatime= getmtime
198
199
200# RISC OS-specific file access functions
201
202def exists(p):
Tim Peters683ecc72001-07-02 04:59:35 +0000203 """
204 Test whether a path exists.
Guido van Rossum228d8072001-03-02 05:58:11 +0000205 """
Tim Peters683ecc72001-07-02 04:59:35 +0000206 try:
207 return swi.swi('OS_File', '5s;i', p)!=0
208 except swi.error:
209 return 0
Guido van Rossum228d8072001-03-02 05:58:11 +0000210
211
212def isdir(p):
Tim Peters683ecc72001-07-02 04:59:35 +0000213 """
214 Is a path a directory? Includes image files.
Guido van Rossum228d8072001-03-02 05:58:11 +0000215 """
Tim Peters683ecc72001-07-02 04:59:35 +0000216 try:
217 return swi.swi('OS_File', '5s;i', p) in [2, 3]
218 except swi.error:
219 return 0
Guido van Rossum228d8072001-03-02 05:58:11 +0000220
221
222def isfile(p):
Tim Peters683ecc72001-07-02 04:59:35 +0000223 """
224 Test whether a path is a file, including image files.
Guido van Rossum228d8072001-03-02 05:58:11 +0000225 """
Tim Peters683ecc72001-07-02 04:59:35 +0000226 try:
227 return swi.swi('OS_File', '5s;i', p) in [1, 3]
228 except swi.error:
229 return 0
Guido van Rossum228d8072001-03-02 05:58:11 +0000230
231
232def islink(p):
Tim Peters683ecc72001-07-02 04:59:35 +0000233 """
234 RISC OS has no links or mounts.
Guido van Rossum228d8072001-03-02 05:58:11 +0000235 """
Tim Peters683ecc72001-07-02 04:59:35 +0000236 return _false
Guido van Rossum228d8072001-03-02 05:58:11 +0000237
238ismount= islink
239
240
241# Same-file testing.
242
243# samefile works on filename comparison since there is no ST_DEV and ST_INO is
244# not reliably unique (esp. directories). First it has to normalise the
245# pathnames, which it can do 'properly' using OS_FSControl since samefile can
246# assume it's running on RISC OS (unlike normpath).
247
248def samefile(fa, fb):
Tim Peters683ecc72001-07-02 04:59:35 +0000249 """
250 Test whether two pathnames reference the same actual file.
Guido van Rossum228d8072001-03-02 05:58:11 +0000251 """
Tim Peters683ecc72001-07-02 04:59:35 +0000252 l= 512
253 b= swi.block(l)
254 swi.swi('OS_FSControl', 'isb..i', 37, fa, b, l)
255 fa= b.ctrlstring()
256 swi.swi('OS_FSControl', 'isb..i', 37, fb, b, l)
257 fb= b.ctrlstring()
258 return fa==fb
Guido van Rossum228d8072001-03-02 05:58:11 +0000259
260
261def sameopenfile(a, b):
Tim Peters683ecc72001-07-02 04:59:35 +0000262 """
263 Test whether two open file objects reference the same file.
Guido van Rossum228d8072001-03-02 05:58:11 +0000264 """
Tim Peters683ecc72001-07-02 04:59:35 +0000265 return os.fstat(a)[stat.ST_INO]==os.fstat(b)[stat.ST_INO]
Guido van Rossum228d8072001-03-02 05:58:11 +0000266
267
268## Path canonicalisation
269
270# 'user directory' is taken as meaning the User Root Directory, which is in
271# practice never used, for anything.
272
273def expanduser(p):
Tim Peters683ecc72001-07-02 04:59:35 +0000274 (fs, drive, path)= _split(p)
275 l= 512
276 b= swi.block(l)
Guido van Rossum228d8072001-03-02 05:58:11 +0000277
Tim Peters683ecc72001-07-02 04:59:35 +0000278 if path[:1]!='@':
279 return p
280 if fs=='':
281 fsno= swi.swi('OS_Args', '00;i')
282 swi.swi('OS_FSControl', 'iibi', 33, fsno, b, l)
283 fsname= b.ctrlstring()
Guido van Rossum228d8072001-03-02 05:58:11 +0000284 else:
Tim Peters683ecc72001-07-02 04:59:35 +0000285 if fs[:1]=='-':
286 fsname= fs[1:-1]
287 else:
288 fsname= fs[:-1]
289 fsname= string.split(fsname, '#', 1)[0] # remove special field from fs
290 x= swi.swi('OS_FSControl', 'ib2s.i;.....i', 54, b, fsname, l)
Guido van Rossum228d8072001-03-02 05:58:11 +0000291 if x<l:
Tim Peters683ecc72001-07-02 04:59:35 +0000292 urd= b.tostring(0, l-x-1)
293 else: # no URD! try CSD
294 x= swi.swi('OS_FSControl', 'ib0s.i;.....i', 54, b, fsname, l)
295 if x<l:
296 urd= b.tostring(0, l-x-1)
297 else: # no CSD! use root
298 urd= '$'
299 return fsname+':'+urd+path[1:]
Guido van Rossum228d8072001-03-02 05:58:11 +0000300
301# Environment variables are in angle brackets.
302
303def expandvars(p):
Tim Peters683ecc72001-07-02 04:59:35 +0000304 """
305 Expand environment variables using OS_GSTrans.
Guido van Rossum228d8072001-03-02 05:58:11 +0000306 """
Tim Peters683ecc72001-07-02 04:59:35 +0000307 l= 512
308 b= swi.block(l)
309 return b.tostring(0, swi.swi('OS_GSTrans', 'sbi;..i', p, b, l))
Guido van Rossum228d8072001-03-02 05:58:11 +0000310
311
312# Return an absolute path.
313
314def abspath(p):
Tim Peters683ecc72001-07-02 04:59:35 +0000315 return normpath(join(os.getcwd(), p))
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 Peters683ecc72001-07-02 04:59:35 +0000353 """
354 walk(top,func,args) calls func(arg, d, files) for each directory "d" in the tree
355 rooted at "top" (including "top" itself). "files" is a list of all the files and
356 subdirs in directory "d".
Guido van Rossum228d8072001-03-02 05:58:11 +0000357 """
Tim Peters683ecc72001-07-02 04:59:35 +0000358 try:
359 names= os.listdir(top)
360 except os.error:
361 return
362 func(arg, top, names)
363 for name in names:
364 name= join(top, name)
365 if isdir(name) and not islink(name):
366 walk(name, func, arg)