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).
11 Instead of importing this module directly, import os and refer to this module
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
26 raise AttributeError, 'This function only available under RISC OS'
30 [_false
, _true
]= range(2)
32 _roots
= ['$', '&', '%', '@', '\\']
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.
49 split filing system name (including special field) and drive specifier from rest
50 of path. This is needed by many riscospath functions.
52 dash
= _allowMOSFSNames
and p
[:1]=='-'
54 q
= string
.find(p
, '-', 1)+1
59 q
= string
.find(p
, ':')+1 # q= index of start of non-FS portion of path
60 s
= string
.find(p
, '#')
62 s
= q
# find end of main FS name, not including special field
65 if c
not in string
.ascii_letters
:
67 break # disallow invalid non-special-field characters in FS name
70 r
= string
.find(p
, '.', q
+1)+1
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
:])
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.
82 return string
.lower(p
)
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
94 (fs
, drive
, path
)= _split(p
)
95 return drive
!='' or path
[:1] in _roots
100 Join path elements with the directory separator, replacing the entire path when
101 an absolute or FS-changing path part is found.
105 (fs
, drive
, path
)= _split(b
)
106 if j
=='' or fs
!='' or drive
!='' or path
[:1] in _roots
:
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
, '.')
123 return (fs
+drive
+path
[:q
], path
[q
+1:])
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
)
134 q
= len(head
)-string
.rfind(head
, '/')
135 return (p
[:-q
], p
[-q
:])
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
145 (fs
, drive
, path
)= _split(p
)
151 Return the tail (basename) part of a path.
158 Return the head (dirname) part of a path.
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?
172 prefix
= prefix
[:len(p
)]
173 for i
in range(len(prefix
)):
174 if prefix
[i
] <> p
[i
]:
182 ## File access functions. Why are we in os.path?
186 Return the size of a file, reported by os.stat().
189 return st
[stat
.ST_SIZE
]
194 Return the last modification time of a file, reported by os.stat().
197 return st
[stat
.ST_MTIME
]
202 # RISC OS-specific file access functions
206 Test whether a path exists.
209 return swi
.swi('OS_File', '5s;i', p
)!=0
216 Is a path a directory? Includes image files.
219 return swi
.swi('OS_File', '5s;i', p
) in [2, 3]
226 Test whether a path is a file, including image files.
229 return swi
.swi('OS_File', '5s;i', p
) in [1, 3]
236 RISC OS has no links or mounts.
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.
256 swi
.swi('OS_FSControl', 'isb..i', 37, fa
, b
, l
)
258 swi
.swi('OS_FSControl', 'isb..i', 37, fb
, b
, l
)
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.
276 (fs
, drive
, path
)= _split(p
)
283 fsno
= swi
.swi('OS_Args', '00;i')
284 swi
.swi('OS_FSControl', 'iibi', 33, fsno
, b
, l
)
285 fsname
= b
.ctrlstring()
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
)
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
)
298 urd
= b
.tostring(0, l
-x
-1)
299 else: # no CSD! use root
301 return fsname
+':'+urd
+path
[1:]
303 # Environment variables are in angle brackets.
307 Expand environment variables using OS_GSTrans.
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
318 # realpath is a no-op on systems without islink support
322 # Normalize a path. Only special path element under RISC OS is "^" for "..".
326 Normalize path, eliminating up-directory ^s.
328 (fs
, drive
, path
)= _split(p
)
332 (path
, el
)= split(path
)
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."""
368 names
= os
.listdir(top
)
371 func(arg
, top
, names
)
373 name
= join(top
, name
)
374 if isdir(name
) and not islink(name
):
375 walk(name
, func
, arg
)