openfile(): Go back to opening the files in text mode. This undoes
[python/dscho.git] / Lib / plat-riscos / riscospath.py
blob30c0c9fe5160881be54c9eadc9dedb3564a70481
1 # Module 'riscospath' -- common operations on RISC OS pathnames.
3 # contributed by Andrew Clover ( andrew@oaktree.co.uk )
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).
10 """
11 Instead of importing this module directly, import os and refer to this module
12 as os.path.
13 """
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)
19 import os, stat, string
21 try:
22 import swi
23 except ImportError:
24 class _swi:
25 def swi(*a):
26 raise AttributeError, 'This function only available under RISC OS'
27 block= swi
28 swi= _swi()
30 [_false, _true]= range(2)
32 _roots= ['$', '&', '%', '@', '\\']
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.
42 _allowMOSFSNames= _false
45 ## Path manipulation, RISC OS stylee.
47 def _split(p):
48 """
49 split filing system name (including special field) and drive specifier from rest
50 of path. This is needed by many riscospath functions.
51 """
52 dash= _allowMOSFSNames and p[:1]=='-'
53 if dash:
54 q= string.find(p, '-', 1)+1
55 else:
56 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]:
65 if c not in string.ascii_letters:
66 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:])
76 def normcase(p):
77 """
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.
81 """
82 return string.lower(p)
85 def isabs(p):
86 """
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.
93 """
94 (fs, drive, path)= _split(p)
95 return drive!='' or path[:1] in _roots
98 def join(a, *p):
99 """
100 Join path elements with the directory separator, replacing the entire path when
101 an absolute or FS-changing path part is found.
103 j= a
104 for b in p:
105 (fs, drive, path)= _split(b)
106 if j=='' or fs!='' or drive!='' or path[:1] in _roots:
107 j= b
108 elif j[-1]==':':
109 j= j+b
110 else:
111 j= j+'.'+b
112 return j
115 def split(p):
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 '.'.
120 (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)
127 def splitext(p):
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.
132 (tail, head)= split(p)
133 if '/' in head:
134 q= len(head)-string.rfind(head, '/')
135 return (p[:-q], p[-q:])
136 return (p, '')
139 def splitdrive(p):
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.
145 (fs, drive, path)= _split(p)
146 return (fs+drive, p)
149 def basename(p):
151 Return the tail (basename) part of a path.
153 return split(p)[1]
156 def dirname(p):
158 Return the head (dirname) part of a path.
160 return split(p)[0]
163 def commonprefix(ps):
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?
168 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
182 ## File access functions. Why are we in os.path?
184 def getsize(p):
186 Return the size of a file, reported by os.stat().
188 st= os.stat(p)
189 return st[stat.ST_SIZE]
192 def getmtime(p):
194 Return the last modification time of a file, reported by os.stat().
196 st = os.stat(p)
197 return st[stat.ST_MTIME]
199 getatime= getmtime
202 # RISC OS-specific file access functions
204 def exists(p):
206 Test whether a path exists.
208 try:
209 return swi.swi('OS_File', '5s;i', p)!=0
210 except swi.error:
211 return 0
214 def isdir(p):
216 Is a path a directory? Includes image files.
218 try:
219 return swi.swi('OS_File', '5s;i', p) in [2, 3]
220 except swi.error:
221 return 0
224 def isfile(p):
226 Test whether a path is a file, including image files.
228 try:
229 return swi.swi('OS_File', '5s;i', p) in [1, 3]
230 except swi.error:
231 return 0
234 def islink(p):
236 RISC OS has no links or mounts.
238 return _false
240 ismount= islink
243 # Same-file testing.
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).
250 def samefile(fa, fb):
252 Test whether two pathnames reference the same actual file.
254 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
263 def sameopenfile(a, b):
265 Test whether two open file objects reference the same file.
267 return os.fstat(a)[stat.ST_INO]==os.fstat(b)[stat.ST_INO]
270 ## Path canonicalisation
272 # 'user directory' is taken as meaning the User Root Directory, which is in
273 # practice never used, for anything.
275 def expanduser(p):
276 (fs, drive, path)= _split(p)
277 l= 512
278 b= swi.block(l)
280 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()
286 else:
287 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)
293 if x<l:
294 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:]
303 # Environment variables are in angle brackets.
305 def expandvars(p):
307 Expand environment variables using OS_GSTrans.
309 l= 512
310 b= swi.block(l)
311 return b.tostring(0, swi.swi('OS_GSTrans', 'sbi;..i', p, b, l))
314 # Return an absolute path. RISC OS' osfscontrol_canonicalise_path does this among others
315 abspath = os.expand
318 # realpath is a no-op on systems without islink support
319 realpath = abspath
322 # Normalize a path. Only special path element under RISC OS is "^" for "..".
324 def normpath(p):
326 Normalize path, eliminating up-directory ^s.
328 (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
335 else:
336 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
349 # Directory tree walk.
350 # Independent of host system. Why am I in os.path?
352 def walk(top, func, arg):
353 """Directory tree walk with callback function.
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."""
367 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)