Common Lisp the Language, 2nd Edition
Next: Logical Pathnames
Up: File Names
Previous: Structured
Directories
Some file systems provide more complex conventions for wildcards than
simple component-wise wildcards representable by :wild
. For
example, the namestring "F*O"
might mean a normal
three-character name; a three-character name with the middle character
wild; a name with at least two characters, beginning with F
and ending with O
; or perhaps a wild match spanning
multiple directories. Similarly, the namestring
">foo>**>bar>"
might imply that the middle
directory is named "**"
; the middle directory is
:wild
; there are zero or more middle directories that are
:wild
; or perhaps that the middle directory name matches
any two-letter name. Some file systems support even more complex
wildcards, such as regular expressions.
X3J13 voted in June 1989 (PATHNAME-WILD) to provide some facilities for dealing with more general wildcard pathnames in a fairly portable manner.
[Function]
wild-pathname-p
pathname
&optional
field-key
Tests a pathname for the presence of wildcard components. If the
first argument is not a pathname, string, or file stream, an error of
type type-error
is signaled.
If no field-key is provided, or the field-key is
nil
, the result is true if and only if pathname
has any wildcard components.
If a non-null field-key is provided, it must be one of
:host
, :device
, :directory
,
:name
, :type
, or :version
. In
this case, the result is true if and only if the indicated component of
pathname is a wildcard.
Note that X3J13 voted in June 1989 (PATHNAME-COMPONENT-VALUE) to
specify that an implementation need not support wildcards in all fields;
the only requirement is that the name, type, or version may be
:wild
. However, portable programs should be prepared to
encounter either :wild
or implementation-dependent
wildcards in any pathname component. The function
wild-pathname-p
provides a portable way for testing the
presence of wildcards.
[Function]
pathname-match-p
pathname
wildname
This predicate is true if and only if the pathname matches
the wildname. The matching rules are implementation-defined but
should be consistent with the behavior of the directory
function. Missing components of wildname default to
:wild
.
If either argument is not a pathname, string, or file stream, an
error of type type-error
is signaled. It is valid for
pathname to be a wild pathname; a wildcard field in
pathname will match only a wildcard field in wildname;
that is, pathname-match-p
is not commutative. It is valid
for wildname to be a non-wild pathname; I believe that in this
case pathname-match-p
will have the same behavior as
equal
, though the X3J13 specification did not say so.
[Function]
translate-pathname
source
from-wildname
to-wildname
&key
Translates the pathname source, which must match from-wildname, into a corresponding pathname (call it result), which is constructed so as to match to-wildname, and returns result.
The pathname result is a copy of to-wildname with
each missing or wildcard field replaced by a portion of source;
for this purpose a wildcard field is a pathname component with a value
of :wild
, a :wild
element of a list-valued
directory component, or an implementation-defined portion of a
component, such as the *
in the complex wildcard string
"foo*bar"
that some implementations support. An
implementation that adds other wildcard features, such as regular
expressions, must define how translate-pathname
extends to
those features. A missing field is a pathname component that is
nil
.
The portion of source that is copied into result is
implementation-defined. Typically it is determined by the user interface
conventions of the file systems involved. Usually it is the portion of
source that matches a wildcard field of from-wildname
that is in the same position as the missing or wildcard field of
to-wildname. If there is no wildcard field in
from-wildname at that position, then usually it is the entire
corresponding pathname component of source or, in the case of a
list-valued directory component, the entire corresponding list element.
For example, if the name components of source,
from-wildname, and to-wildname are
"gazonk"
, "gaz*"
, and "h*"
respectively, then in most file systems the wildcard fields of the name
component of from-wildname and to-wildname are each
"*"
, the matching portion of source is
"onk"
, and the name component of result is
"honk"
; however, the exact behavior of
translate-pathname
is not dictated by the Common Lisp
language and may vary according to the user interface conventions of the
file systems involved.
During the copying of a portion of source into result, additional implementation-defined translations of alphabetic case or file naming conventions may occur, especially when from-wildname and to-wildname are for different hosts.
If any of the first three arguments is not a pathname, string, or
file stream, an error of type type-error
is signaled. It is
valid for source to be a wild pathname; in general this will
produce a wild result pathname. It is valid for
from-wildname or to-wildname or both to be non-wild.
An error is signaled if the source pathname does not match the
from-wildname, that is, if
(pathname-match-p
source
from-wildname
)
would not be true.
There are no specified keyword arguments for
translate-pathname
, but implementations are permitted to
extend it by adding keyword arguments. There is one specified return
value from translate-pathname
; implementations are
permitted to extend it by returning additional values.
Here is an implementation suggestion. One file system performs this
operation by examining corresponding pieces of the three pathnames in
turn, where a piece is a pathname component or a list element of a
structured component such as a hierarchical directory. Hierarchical
directory elements in from-wildname and to-wildname
are matched by whether they are wildcards, not by depth in the directory
hierarchy. If the piece in to-wildname is present and not wild,
it is copied into the result. If the piece in to-wildname is
:wild
or nil
, the corresponding piece in
source is copied into the result. Otherwise, the piece in
to-wildname might be a complex wildcard such as
"foo*bar"
; the portion of the piece in source that
matches the wildcard portion of the corresponding piece in
from-wildname (or the entire source piece, if the
from-wildname piece is not wild and therefore equals the
source piece) replaces the wildcard portion of the piece in
to-wildname and the value produced is used in the result.
X3J13 voted in June 1989 (PATHNAME-COMPONENT-CASE) to require
translate-pathname
to map customary case in argument
pathnames to the customary case in returned pathnames (see section 23.1.2).
Here are some examples of the use of the new wildcard pathname facilities. These examples are not portable. They are written to run with particular file systems and particular wildcard conventions and are intended to be illustrative, not prescriptive. Other implementations may behave differently.
(wild-pathname-p (make-pathname :name :wild)) => t
(wild-pathname-p (make-pathname :name :wild) :name) => t
(wild-pathname-p (make-pathname :name :wild) :type) => nil
(wild-pathname-p (pathname "S:>foo>**>")) => t ;Maybe
(wild-pathname-p (make-pathname :name "F*O")) => t ;Probably
One cannot rely on rename-file
to handle wild pathnames
in a predictable manner. However, one can use
translate-pathname
explicitly to control the process.
(defun rename-files (from to)
"Rename all files that match the first argument by
translating their names to the form of the second
argument. Both arguments may be wild pathnames."
(dolist (file (directory from))
;; DIRECTORY produces only pathnames that match from-wildname.
(rename-file file (translate-pathname file from to))))
Assuming one particular set of popular wildcard conventions, this function might exhibit the following behavior. Not all file systems will run this example exactly as written.
(rename-files "/usr/me/*.lisp" "/dev/her/*.l")
renames /usr/me/init.lisp
to /dev/her/init.l
(rename-files "/usr/me/pcl*/*" "/sys/pcl/*/")
renames /usr/me/pcl-5-may/low.lisp
to /sys/pcl/pcl-5-may/low.lisp
(in some file systems the result might be /sys/pcl/5-may/low.lisp)
(rename-files "/usr/me/pcl*/*" "/sys/library/*/")
renames /usr/me/pcl-5-may/low.lisp
to /sys/library/pcl-5-may/low.lisp
(in some file systems the result might be /sys/library/5-may/low.lisp)
(rename-files "/usr/me/foo.bar" "/usr/me2/")
renames /usr/me/foo.bar
to /usr/me2/foo.bar
(rename-files "/usr/joe/*-recipes.text"
"/usr/jim/personal/cookbook/joe's-*-rec.text")
renames /usr/joe/lamb-recipes.text
to /usr/jim/personal/cookbook/joe's-lamb-rec.text
renames /usr/joe/veg-recipes.text
to /usr/jim/personal/cookbook/joe's-veg-rec.text
renames /usr/joe/cajun-recipes.text
to /usr/jim/personal/cookbook/joe's-cajun-rec.text
renames /usr/joe/szechuan-recipes.text
to /usr/jim/personal/cookbook/joe's-szechuan-rec.text
The following examples use UNIX syntax and the wildcard conventions of one particular version of UNIX.
(namestring
(translate-pathname "/usr/dmr/hacks/frob.l"
"/usr/d*/hacks/*.l"
"/usr/d*/backup/hacks/backup-*.*"))
=> "/usr/dmr/backup/hacks/backup-frob.l"
(namestring
(translate-pathname "/usr/dmr/hacks/frob.l"
"/usr/d*/hacks/fr*.l"
"/usr/d*/backup/hacks/backup-*.*"))
=> "/usr/dmr/backup/hacks/backup-ob.l"
The following examples are similar to the preceding examples but use
two different hosts; host U
supports a UNIX file system and
host V
supports a VAX/VMS file system. Note the translation
of file type (from l
to LSP
) and the change of
alphabetic case conventions.
(namestring
(translate-pathname "U:/usr/dmr/hacks/frob.l"
"U:/usr/d*/hacks/*.l"
"V:SYS$DISK:[D*.BACKUP.HACKS]BACKUP-*.*"))
=> "V:SYS$DISK:[DMR.BACKUP.HACKS]BACKUP-FROB.LSP"
(namestring
(translate-pathname "U:/usr/dmr/hacks/frob.l"
"U:/usr/d*/hacks/fr*.l"
"V:SYS$DISK:[D*.BACKUP.HACKS]BACKUP-*.*"))
=> "V:SYS$DISK:[DMR.BACKUP.HACKS]BACKUP-OB.LSP"
The next example is a version of the function
translate-logical-pathname
(simplified a bit) for a logical
host named FOO
. The points of interest are the use of
pathname-match-p
as a :test
argument for
assoc
and the use of translate-pathname
as a
substrate for translate-logical-pathname
.
(define-condition logical-translation-error (file-error))
(defun my-translate-logical-pathname (pathname &key rules)
(let ((rule (assoc pathname rules :test #'pathname-match-p)))
(unless rule
(error 'logical-translation-error :pathname pathname))
(translate-pathname pathname (first rule) (second rule))))
(my-translate-logical-pathname
"FOO:CODE;BASIC.LISP"
:rules '(("FOO:DOCUMENTATION;" "U:/doc/foo/")
("FOO:CODE;" "U:/lib/foo/")
("FOO:PATCHES;*;" "U:/lib/foo/patch/*/")))
=> #P"U:/lib/foo/basic.l"
Next: Logical Pathnames
Up: File Names
Previous: Structured
Directories
AI.Repository@cs.cmu.edu