1 ;;;; machine/filesystem-independent pathname functions
3 ;;;; This software is part of the SBCL system. See the README file for
6 ;;;; This software is derived from the CMU CL system, which was
7 ;;;; written at Carnegie Mellon University and released into the
8 ;;;; public domain. The software is in the public domain and is
9 ;;;; provided with absolutely no warranty. See the COPYING and CREDITS
10 ;;;; files for more information.
12 (in-package "SB!IMPL")
14 #!-sb-fluid
(declaim (freeze-type logical-pathname logical-host
))
18 (def!struct
(unix-host
19 (:make-load-form-fun make-unix-host-load-form
)
21 (parse #'parse-unix-namestring
)
22 (unparse #'unparse-unix-namestring
)
23 (unparse-host #'unparse-unix-host
)
24 (unparse-directory #'unparse-unix-directory
)
25 (unparse-file #'unparse-unix-file
)
26 (unparse-enough #'unparse-unix-enough
)
27 (customary-case :lower
))))
29 (defvar *unix-host
* (make-unix-host))
31 (defun make-unix-host-load-form (host)
32 (declare (ignore host
))
35 ;;; Return a value suitable, e.g., for preinitializing
36 ;;; *DEFAULT-PATHNAME-DEFAULTS* before *DEFAULT-PATHNAME-DEFAULTS* is
37 ;;; initialized (at which time we can't safely call e.g. #'PATHNAME).
38 (defun make-trivial-default-pathname ()
39 (%make-pathname
*unix-host
* nil nil nil nil
:newest
))
43 (def!method print-object
((pathname pathname
) stream
)
44 (let ((namestring (handler-case (namestring pathname
)
47 (format stream
"#P~S" namestring
)
48 (print-unreadable-object (pathname stream
:type t
)
50 "~@<(with no namestring) ~_:HOST ~S ~_:DEVICE ~S ~_:DIRECTORY ~S ~
51 ~_:NAME ~S ~_:TYPE ~S ~_:VERSION ~S~:>"
52 (%pathname-host pathname
)
53 (%pathname-device pathname
)
54 (%pathname-directory pathname
)
55 (%pathname-name pathname
)
56 (%pathname-type pathname
)
57 (%pathname-version pathname
))))))
59 (def!method make-load-form
((pathname pathname
) &optional environment
)
60 (make-load-form-saving-slots pathname
:environment environment
))
62 ;;; The potential conflict with search lists requires isolating the
63 ;;; printed representation to use the i/o macro #.(logical-pathname
64 ;;; <path-designator>).
66 ;;; FIXME: We don't use search lists any more, so that comment is
68 (def!method print-object
((pathname logical-pathname
) stream
)
69 (let ((namestring (handler-case (namestring pathname
)
72 (format stream
"#.(CL:LOGICAL-PATHNAME ~S)" namestring
)
73 (print-unreadable-object (pathname stream
:type t
)
76 "~_:HOST ~S ~_:DIRECTORY ~S ~_:FILE ~S ~_:NAME ~S ~_:VERSION ~S"
77 (%pathname-host pathname
)
78 (%pathname-directory pathname
)
79 (%pathname-name pathname
)
80 (%pathname-type pathname
)
81 (%pathname-version pathname
))))))
83 ;;; A pathname is logical if the host component is a logical host.
84 ;;; This constructor is used to make an instance of the correct type
85 ;;; from parsed arguments.
86 (defun %make-maybe-logical-pathname
(host device directory name type version
)
87 ;; We canonicalize logical pathname components to uppercase. ANSI
88 ;; doesn't strictly require this, leaving it up to the implementor;
89 ;; but the arguments given in the X3J13 cleanup issue
90 ;; PATHNAME-LOGICAL:ADD seem compelling: we should canonicalize the
91 ;; case, and uppercase is the ordinary way to do that.
92 (flet ((upcase-maybe (x) (typecase x
(string (logical-word-or-lose x
)) (t x
))))
93 (if (typep host
'logical-host
)
94 (%make-logical-pathname host
96 (mapcar #'upcase-maybe directory
)
101 (aver (eq host
*unix-host
*))
102 (%make-pathname host device directory name type version
)))))
104 ;;; Hash table searching maps a logical pathname's host to its
105 ;;; physical pathname translation.
106 (defvar *logical-hosts
* (make-hash-table :test
'equal
))
110 (def!method make-load-form
((pattern pattern
) &optional environment
)
111 (make-load-form-saving-slots pattern
:environment environment
))
113 (def!method print-object
((pattern pattern
) stream
)
114 (print-unreadable-object (pattern stream
:type t
)
116 (let ((*print-escape
* t
))
117 (pprint-fill stream
(pattern-pieces pattern
) nil
))
118 (prin1 (pattern-pieces pattern
) stream
))))
120 (defun pattern= (pattern1 pattern2
)
121 (declare (type pattern pattern1 pattern2
))
122 (let ((pieces1 (pattern-pieces pattern1
))
123 (pieces2 (pattern-pieces pattern2
)))
124 (and (= (length pieces1
) (length pieces2
))
125 (every (lambda (piece1 piece2
)
128 (and (simple-string-p piece2
)
129 (string= piece1 piece2
)))
132 (eq (car piece1
) (car piece2
))
133 (string= (cdr piece1
) (cdr piece2
))))
135 (eq piece1 piece2
))))
139 ;;; If the string matches the pattern returns the multiple values T
140 ;;; and a list of the matched strings.
141 (defun pattern-matches (pattern string
)
142 (declare (type pattern pattern
)
143 (type simple-string string
))
144 (let ((len (length string
)))
145 (labels ((maybe-prepend (subs cur-sub chars
)
147 (let* ((len (length chars
))
148 (new (make-string len
))
151 (setf (schar new
(decf index
)) char
))
154 (matches (pieces start subs cur-sub chars
)
157 (values t
(maybe-prepend subs cur-sub chars
))
159 (let ((piece (car pieces
)))
162 (let ((end (+ start
(length piece
))))
164 (string= piece string
165 :start2 start
:end2 end
)
166 (matches (cdr pieces
) end
167 (maybe-prepend subs cur-sub chars
)
173 (let ((char (schar string start
)))
174 (if (find char
(cdr piece
) :test
#'char
=)
175 (matches (cdr pieces
) (1+ start
) subs t
176 (cons char chars
))))))))
177 ((member :single-char-wild
)
179 (matches (cdr pieces
) (1+ start
) subs t
180 (cons (schar string start
) chars
))))
181 ((member :multi-char-wild
)
182 (multiple-value-bind (won new-subs
)
183 (matches (cdr pieces
) start subs t chars
)
187 (matches pieces
(1+ start
) subs t
188 (cons (schar string start
)
190 (multiple-value-bind (won subs
)
191 (matches (pattern-pieces pattern
) 0 nil nil nil
)
192 (values won
(reverse subs
))))))
194 ;;; PATHNAME-MATCH-P for directory components
195 (defun directory-components-match (thing wild
)
198 ;; If THING has a null directory, assume that it matches
199 ;; (:ABSOLUTE :WILD-INFERIORS) or (:RELATIVE :WILD-INFERIORS).
202 (member (first wild
) '(:absolute
:relative
))
203 (eq (second wild
) :wild-inferiors
))
205 (let ((wild1 (first wild
)))
206 (if (eq wild1
:wild-inferiors
)
207 (let ((wild-subdirs (rest wild
)))
208 (or (null wild-subdirs
)
210 (when (directory-components-match thing wild-subdirs
)
213 (unless thing
(return nil
)))))
215 (components-match (first thing
) wild1
)
216 (directory-components-match (rest thing
)
219 ;;; Return true if pathname component THING is matched by WILD. (not
221 (defun components-match (thing wild
)
222 (declare (type (or pattern symbol simple-string integer
) thing wild
))
227 ;; String is matched by itself, a matching pattern or :WILD.
230 (values (pattern-matches wild thing
)))
232 (string= thing wild
))))
234 ;; A pattern is only matched by an identical pattern.
235 (and (pattern-p wild
) (pattern= thing wild
)))
237 ;; An integer (version number) is matched by :WILD or the
238 ;; same integer. This branch will actually always be NIL as
239 ;; long as the version is a fixnum.
242 ;;; a predicate for comparing two pathname slot component sub-entries
243 (defun compare-component (this that
)
247 (and (simple-string-p that
)
248 (string= this that
)))
250 (and (pattern-p that
)
251 (pattern= this that
)))
254 (compare-component (car this
) (car that
))
255 (compare-component (cdr this
) (cdr that
)))))))
257 ;;;; pathname functions
259 (defun pathname= (pathname1 pathname2
)
260 (declare (type pathname pathname1
)
261 (type pathname pathname2
))
262 (and (eq (%pathname-host pathname1
)
263 (%pathname-host pathname2
))
264 (compare-component (%pathname-device pathname1
)
265 (%pathname-device pathname2
))
266 (compare-component (%pathname-directory pathname1
)
267 (%pathname-directory pathname2
))
268 (compare-component (%pathname-name pathname1
)
269 (%pathname-name pathname2
))
270 (compare-component (%pathname-type pathname1
)
271 (%pathname-type pathname2
))
272 (or (eq (%pathname-host pathname1
) *unix-host
*)
273 (compare-component (%pathname-version pathname1
)
274 (%pathname-version pathname2
)))))
276 ;;; Convert PATHNAME-DESIGNATOR (a pathname, or string, or
277 ;;; stream), into a pathname in pathname.
279 ;;; FIXME: was rewritten, should be tested (or rewritten again, this
280 ;;; time using ONCE-ONLY, *then* tested)
281 ;;; FIXME: become SB!XC:DEFMACRO inside EVAL-WHEN (COMPILE EVAL)?
282 (defmacro with-pathname
((pathname pathname-designator
) &body body
)
283 (let ((pd0 (gensym)))
284 `(let* ((,pd0
,pathname-designator
)
285 (,pathname
(etypecase ,pd0
287 (string (parse-namestring ,pd0
))
288 (file-stream (file-name ,pd0
)))))
291 ;;; Convert the var, a host or string name for a host, into a
292 ;;; LOGICAL-HOST structure or nil if not defined.
294 ;;; pw notes 1/12/97 this potentially useful macro is not used anywhere
295 ;;; and 'find-host' is not defined. 'find-logical-host' seems to be needed.
297 (defmacro with-host
((var expr
) &body body
)
298 `(let ((,var
(let ((,var
,expr
))
301 (string (find-logical-host ,var nil
))
306 (defun pathname (thing)
308 "Convert thing (a pathname, string or stream) into a pathname."
309 (declare (type pathname-designator thing
))
310 (with-pathname (pathname thing
)
313 ;;; Change the case of thing if DIDDLE-P.
314 (defun maybe-diddle-case (thing diddle-p
)
315 (if (and diddle-p
(not (or (symbolp thing
) (integerp thing
))))
316 (labels ((check-for (pred in
)
319 (dolist (piece (pattern-pieces in
))
320 (when (typecase piece
322 (check-for pred piece
))
326 (check-for pred
(cdr piece
))))))
330 (when (check-for pred x
)
333 (dotimes (i (length in
))
334 (when (funcall pred
(schar in i
))
337 (diddle-with (fun thing
)
341 (mapcar (lambda (piece)
349 (funcall fun
(cdr piece
))))
354 (pattern-pieces thing
))))
361 (let ((any-uppers (check-for #'upper-case-p thing
))
362 (any-lowers (check-for #'lower-case-p thing
)))
363 (cond ((and any-uppers any-lowers
)
364 ;; mixed case, stays the same
367 ;; all uppercase, becomes all lower case
368 (diddle-with (lambda (x) (if (stringp x
)
372 ;; all lowercase, becomes all upper case
373 (diddle-with (lambda (x) (if (stringp x
)
377 ;; no letters? I guess just leave it.
381 (defun merge-directories (dir1 dir2 diddle-case
)
382 (if (or (eq (car dir1
) :absolute
)
387 (if (and (eq dir
:back
)
389 (not (member (car results
)
390 '(:back
:wild-inferiors
))))
392 (push dir results
))))
393 (dolist (dir (maybe-diddle-case dir2 diddle-case
))
395 (dolist (dir (cdr dir1
))
399 (defun merge-pathnames (pathname
401 (defaults *default-pathname-defaults
*)
402 (default-version :newest
))
404 "Construct a filled in pathname by completing the unspecified components
406 (declare (type pathname-designator pathname
)
407 (type pathname-designator defaults
)
409 (with-pathname (defaults defaults
)
410 (let ((pathname (let ((*default-pathname-defaults
* defaults
))
411 (pathname pathname
))))
412 (let* ((default-host (%pathname-host defaults
))
413 (pathname-host (%pathname-host pathname
))
415 (and default-host pathname-host
416 (not (eq (host-customary-case default-host
)
417 (host-customary-case pathname-host
))))))
418 (%make-maybe-logical-pathname
419 (or pathname-host default-host
)
420 (or (%pathname-device pathname
)
421 (maybe-diddle-case (%pathname-device defaults
)
423 (merge-directories (%pathname-directory pathname
)
424 (%pathname-directory defaults
)
426 (or (%pathname-name pathname
)
427 (maybe-diddle-case (%pathname-name defaults
)
429 (or (%pathname-type pathname
)
430 (maybe-diddle-case (%pathname-type defaults
)
432 (or (%pathname-version pathname
)
433 (and (not (%pathname-name pathname
)) (%pathname-version defaults
))
434 default-version
))))))
436 (defun import-directory (directory diddle-case
)
439 ((member :wild
) '(:absolute
:wild-inferiors
))
440 ((member :unspecific
) '(:relative
))
443 (results (pop directory
))
444 (dolist (piece directory
)
445 (cond ((member piece
'(:wild
:wild-inferiors
:up
:back
))
447 ((or (simple-string-p piece
) (pattern-p piece
))
448 (results (maybe-diddle-case piece diddle-case
)))
450 (results (maybe-diddle-case (coerce piece
'simple-string
)
453 (error "~S is not allowed as a directory component." piece
))))
457 ,(maybe-diddle-case directory diddle-case
)))
460 ,(maybe-diddle-case (coerce directory
'simple-string
)
463 (defun make-pathname (&key host
468 (version nil versionp
)
472 "Makes a new pathname from the component arguments. Note that host is
473 a host-structure or string."
474 (declare (type (or string host pathname-component-tokens
) host
)
475 (type (or string pathname-component-tokens
) device
)
476 (type (or list string pattern pathname-component-tokens
) directory
)
477 (type (or string pattern pathname-component-tokens
) name type
)
478 (type (or integer pathname-component-tokens
(member :newest
))
480 (type (or pathname-designator null
) defaults
)
481 (type (member :common
:local
) case
))
482 (let* ((defaults (when defaults
483 (with-pathname (defaults defaults
) defaults
)))
484 (default-host (if defaults
485 (%pathname-host defaults
)
486 (pathname-host *default-pathname-defaults
*)))
487 ;; Raymond Toy writes: CLHS says make-pathname can take a
488 ;; string (as a logical-host) for the host part. We map that
489 ;; string into the corresponding logical host structure.
491 ;; Paul Werkowski writes:
492 ;; HyperSpec says for the arg to MAKE-PATHNAME;
493 ;; "host---a valid physical pathname host. ..."
494 ;; where it probably means -- a valid pathname host.
495 ;; "valid pathname host n. a valid physical pathname host or
496 ;; a valid logical pathname host."
498 ;; "valid physical pathname host n. any of a string,
499 ;; a list of strings, or the symbol :unspecific,
500 ;; that is recognized by the implementation as the name of a host."
501 ;; "valid logical pathname host n. a string that has been defined
502 ;; as the name of a logical host. ..."
503 ;; HS is silent on what happens if the :HOST arg is NOT one of these.
504 ;; It seems an error message is appropriate.
506 (host host
) ; A valid host, use it.
507 ((string 0) *unix-host
*) ; "" cannot be a logical host
508 (string (find-logical-host host t
)) ; logical-host or lose.
509 (t default-host
))) ; unix-host
510 (diddle-args (and (eq (host-customary-case host
) :lower
)
513 (not (eq (host-customary-case host
)
514 (host-customary-case default-host
))))
515 (dev (if devp device
(if defaults
(%pathname-device defaults
))))
516 (dir (import-directory directory diddle-args
))
519 (defaults (%pathname-version defaults
))
521 (when (and defaults
(not dirp
))
523 (merge-directories dir
524 (%pathname-directory defaults
)
527 (macrolet ((pick (var varp field
)
528 `(cond ((or (simple-string-p ,var
)
530 (maybe-diddle-case ,var diddle-args
))
532 (maybe-diddle-case (coerce ,var
'simple-string
)
535 (maybe-diddle-case ,var diddle-args
))
537 (maybe-diddle-case (,field defaults
)
541 (%make-maybe-logical-pathname host
542 dev
; forced to :UNSPECIFIC when logical
544 (pick name namep %pathname-name
)
545 (pick type typep %pathname-type
)
548 (defun pathname-host (pathname &key
(case :local
))
550 "Return PATHNAME's host."
551 (declare (type pathname-designator pathname
)
552 (type (member :local
:common
) case
)
555 (with-pathname (pathname pathname
)
556 (%pathname-host pathname
)))
558 (defun pathname-device (pathname &key
(case :local
))
560 "Return PATHNAME's device."
561 (declare (type pathname-designator pathname
)
562 (type (member :local
:common
) case
))
563 (with-pathname (pathname pathname
)
564 (maybe-diddle-case (%pathname-device pathname
)
565 (and (eq case
:common
)
566 (eq (host-customary-case
567 (%pathname-host pathname
))
570 (defun pathname-directory (pathname &key
(case :local
))
572 "Return PATHNAME's directory."
573 (declare (type pathname-designator pathname
)
574 (type (member :local
:common
) case
))
575 (with-pathname (pathname pathname
)
576 (maybe-diddle-case (%pathname-directory pathname
)
577 (and (eq case
:common
)
578 (eq (host-customary-case
579 (%pathname-host pathname
))
581 (defun pathname-name (pathname &key
(case :local
))
583 "Return PATHNAME's name."
584 (declare (type pathname-designator pathname
)
585 (type (member :local
:common
) case
))
586 (with-pathname (pathname pathname
)
587 (maybe-diddle-case (%pathname-name pathname
)
588 (and (eq case
:common
)
589 (eq (host-customary-case
590 (%pathname-host pathname
))
593 (defun pathname-type (pathname &key
(case :local
))
595 "Return PATHNAME's type."
596 (declare (type pathname-designator pathname
)
597 (type (member :local
:common
) case
))
598 (with-pathname (pathname pathname
)
599 (maybe-diddle-case (%pathname-type pathname
)
600 (and (eq case
:common
)
601 (eq (host-customary-case
602 (%pathname-host pathname
))
605 (defun pathname-version (pathname)
607 "Return PATHNAME's version."
608 (declare (type pathname-designator pathname
))
609 (with-pathname (pathname pathname
)
610 (%pathname-version pathname
)))
614 ;;; Handle the case for PARSE-NAMESTRING parsing a potentially
615 ;;; syntactically valid logical namestring with an explicit host.
617 ;;; This then isn't fully general -- we are relying on the fact that
618 ;;; we will only pass to parse-namestring namestring with an explicit
619 ;;; logical host, so that we can pass the host return from
620 ;;; parse-logical-namestring through to %PARSE-NAMESTRING as a truth
621 ;;; value. Yeah, this is probably a KLUDGE - CSR, 2002-04-18
622 (defun parseable-logical-namestring-p (namestr start end
)
625 ((namestring-parse-error (lambda (c)
628 (let ((colon (position #\
: namestr
:start start
:end end
)))
630 (let ((potential-host
631 (logical-word-or-lose (subseq namestr start colon
))))
632 ;; depending on the outcome of CSR comp.lang.lisp post
633 ;; "can PARSE-NAMESTRING create logical hosts", we may need
634 ;; to do things with potential-host (create it
635 ;; temporarily, parse the namestring and unintern the
636 ;; logical host potential-host on failure.
637 (declare (ignore potential-host
))
640 ((simple-type-error (lambda (c)
643 (parse-logical-namestring namestr start end
))))
644 ;; if we got this far, we should have an explicit host
645 ;; (first return value of parse-logical-namestring)
649 ;;; Handle the case where PARSE-NAMESTRING is actually parsing a
650 ;;; namestring. We pick off the :JUNK-ALLOWED case then find a host to
651 ;;; use for parsing, call the parser, then check whether the host matches.
652 (defun %parse-namestring
(namestr host defaults start end junk-allowed
)
653 (declare (type (or host null
) host
)
654 (type string namestr
)
656 (type (or index null
) end
))
660 (%parse-namestring namestr host defaults start end nil
)
661 (namestring-parse-error (condition)
662 (values nil
(namestring-parse-error-offset condition
)))))
664 (let* ((end (%check-vector-sequence-bounds namestr start end
)))
665 (multiple-value-bind (new-host device directory file type version
)
666 ;; Comments below are quotes from the HyperSpec
667 ;; PARSE-NAMESTRING entry, reproduced here to demonstrate
668 ;; that we actually have to do things this way rather than
669 ;; some possibly more logical way. - CSR, 2002-04-18
671 ;; "If host is a logical host then thing is parsed as a
672 ;; logical pathname namestring on the host."
673 (host (funcall (host-parse host
) namestr start end
))
674 ;; "If host is nil and thing is a syntactically valid
675 ;; logical pathname namestring containing an explicit
676 ;; host, then it is parsed as a logical pathname
678 ((parseable-logical-namestring-p namestr start end
)
679 (parse-logical-namestring namestr start end
))
680 ;; "If host is nil, default-pathname is a logical
681 ;; pathname, and thing is a syntactically valid logical
682 ;; pathname namestring without an explicit host, then it
683 ;; is parsed as a logical pathname namestring on the
684 ;; host that is the host component of default-pathname."
686 ;; "Otherwise, the parsing of thing is
687 ;; implementation-defined."
689 ;; Both clauses are handled here, as the default
690 ;; *DEFAULT-PATHNAME-DEFAULTS has a SB-IMPL::UNIX-HOST
692 ((pathname-host defaults
)
693 (funcall (host-parse (pathname-host defaults
))
697 ;; I don't think we should ever get here, as the default
698 ;; host will always have a non-null HOST, given that we
699 ;; can't create a new pathname without going through
700 ;; *DEFAULT-PATHNAME-DEFAULTS*, which has a non-null
702 (t (bug "Fallen through COND in %PARSE-NAMESTRING")))
703 (when (and host new-host
(not (eq new-host host
)))
704 (error 'simple-type-error
706 ;; Note: ANSI requires that this be a TYPE-ERROR,
707 ;; but there seems to be no completely correct
708 ;; value to use for TYPE-ERROR-EXPECTED-TYPE.
709 ;; Instead, we return a sort of "type error allowed
710 ;; type", trying to say "it would be OK if you
711 ;; passed NIL as the host value" but not mentioning
712 ;; that a matching string would be OK too.
715 "The host in the namestring, ~S,~@
716 does not match the explicit HOST argument, ~S."
717 :format-arguments
(list new-host host
)))
718 (let ((pn-host (or new-host host
(pathname-host defaults
))))
719 (values (%make-maybe-logical-pathname
720 pn-host device directory file type version
)
723 ;;; If NAMESTR begins with a colon-terminated, defined, logical host,
724 ;;; then return that host, otherwise return NIL.
725 (defun extract-logical-host-prefix (namestr start end
)
726 (declare (type simple-base-string namestr
)
727 (type index start end
)
728 (values (or logical-host null
)))
729 (let ((colon-pos (position #\
: namestr
:start start
:end end
)))
731 (values (gethash (nstring-upcase (subseq namestr start colon-pos
))
735 (defun parse-namestring (thing
738 (defaults *default-pathname-defaults
*)
739 &key
(start 0) end junk-allowed
)
740 (declare (type pathname-designator thing
)
741 (type (or list host string
(member :unspecific
)) host
)
742 (type pathname defaults
)
744 (type (or index null
) end
)
745 (type (or t null
) junk-allowed
)
746 (values (or null pathname
) (or null index
)))
747 ;; Generally, redundant specification of information in software,
748 ;; whether in code or in comments, is bad. However, the ANSI spec
749 ;; for this is messy enough that it's hard to hold in short-term
750 ;; memory, so I've recorded these redundant notes on the
751 ;; implications of the ANSI spec.
753 ;; According to the ANSI spec, HOST can be a valid pathname host, or
754 ;; a logical host, or NIL.
756 ;; A valid pathname host can be a valid physical pathname host or a
757 ;; valid logical pathname host.
759 ;; A valid physical pathname host is "any of a string, a list of
760 ;; strings, or the symbol :UNSPECIFIC, that is recognized by the
761 ;; implementation as the name of a host". In SBCL as of 0.6.9.8,
762 ;; that means :UNSPECIFIC: though someday we might want to
763 ;; generalize it to allow strings like "RTFM.MIT.EDU" or lists like
764 ;; '("RTFM" "MIT" "EDU"), that's not supported now.
766 ;; A valid logical pathname host is a string which has been defined as
767 ;; the name of a logical host, as with LOAD-LOGICAL-PATHNAME-TRANSLATIONS.
769 ;; A logical host is an object of implementation-dependent nature. In
770 ;; SBCL, it's a member of the HOST class (a subclass of STRUCTURE-OBJECT).
771 (let ((found-host (etypecase host
773 ;; This is a special host. It's not valid as a
774 ;; logical host, so it is a sensible thing to
775 ;; designate the physical Unix host object. So
779 ;; In general ANSI-compliant Common Lisps, a
780 ;; string might also be a physical pathname host,
781 ;; but ANSI leaves this up to the implementor,
782 ;; and in SBCL we don't do it, so it must be a
784 (find-logical-host host
))
785 ((or null
(member :unspecific
))
786 ;; CLHS says that HOST=:UNSPECIFIC has
787 ;; implementation-defined behavior. We
788 ;; just turn it into NIL.
791 ;; ANSI also allows LISTs to designate hosts,
792 ;; but leaves its interpretation
793 ;; implementation-defined. Our interpretation
794 ;; is that it's unsupported.:-|
795 (error "A LIST representing a pathname host is not ~
796 supported in this implementation:~% ~S"
800 (declare (type (or null host
) found-host
))
803 (%parse-namestring thing found-host defaults start end junk-allowed
))
805 (%parse-namestring
(coerce thing
'simple-string
)
806 found-host defaults start end junk-allowed
))
808 (let ((defaulted-host (or found-host
(%pathname-host defaults
))))
809 (declare (type host defaulted-host
))
810 (unless (eq defaulted-host
(%pathname-host thing
))
811 (error "The HOST argument doesn't match the pathname host:~% ~
813 defaulted-host
(%pathname-host thing
))))
814 (values thing start
))
816 (let ((name (file-name thing
)))
818 (error "can't figure out the file associated with stream:~% ~S"
820 (values name nil
))))))
822 (defun namestring (pathname)
824 "Construct the full (name)string form of the pathname."
825 (declare (type pathname-designator pathname
))
826 (with-pathname (pathname pathname
)
828 (let ((host (%pathname-host pathname
)))
830 (error "can't determine the namestring for pathnames with no ~
831 host:~% ~S" pathname
))
832 (funcall (host-unparse host
) pathname
)))))
834 (defun host-namestring (pathname)
836 "Return a string representation of the name of the host in the pathname."
837 (declare (type pathname-designator pathname
))
838 (with-pathname (pathname pathname
)
839 (let ((host (%pathname-host pathname
)))
841 (funcall (host-unparse-host host
) pathname
)
843 "can't determine the namestring for pathnames with no host:~% ~S"
846 (defun directory-namestring (pathname)
848 "Return a string representation of the directories used in the pathname."
849 (declare (type pathname-designator pathname
))
850 (with-pathname (pathname pathname
)
851 (let ((host (%pathname-host pathname
)))
853 (funcall (host-unparse-directory host
) pathname
)
855 "can't determine the namestring for pathnames with no host:~% ~S"
858 (defun file-namestring (pathname)
860 "Return a string representation of the name used in the pathname."
861 (declare (type pathname-designator pathname
))
862 (with-pathname (pathname pathname
)
863 (let ((host (%pathname-host pathname
)))
865 (funcall (host-unparse-file host
) pathname
)
867 "can't determine the namestring for pathnames with no host:~% ~S"
870 (defun enough-namestring (pathname
872 (defaults *default-pathname-defaults
*))
874 "Return an abbreviated pathname sufficent to identify the pathname relative
876 (declare (type pathname-designator pathname
))
877 (with-pathname (pathname pathname
)
878 (let ((host (%pathname-host pathname
)))
880 (with-pathname (defaults defaults
)
881 (funcall (host-unparse-enough host
) pathname defaults
))
883 "can't determine the namestring for pathnames with no host:~% ~S"
888 (defun wild-pathname-p (pathname &optional field-key
)
890 "Predicate for determining whether pathname contains any wildcards."
891 (declare (type pathname-designator pathname
)
892 (type (member nil
:host
:device
:directory
:name
:type
:version
)
894 (with-pathname (pathname pathname
)
896 (or (pattern-p x
) (member x
'(:wild
:wild-inferiors
)))))
899 (or (wild-pathname-p pathname
:host
)
900 (wild-pathname-p pathname
:device
)
901 (wild-pathname-p pathname
:directory
)
902 (wild-pathname-p pathname
:name
)
903 (wild-pathname-p pathname
:type
)
904 (wild-pathname-p pathname
:version
)))
905 (:host
(frob (%pathname-host pathname
)))
906 (:device
(frob (%pathname-host pathname
)))
907 (:directory
(some #'frob
(%pathname-directory pathname
)))
908 (:name
(frob (%pathname-name pathname
)))
909 (:type
(frob (%pathname-type pathname
)))
910 (:version
(frob (%pathname-version pathname
)))))))
912 (defun pathname-match-p (in-pathname in-wildname
)
914 "Pathname matches the wildname template?"
915 (declare (type pathname-designator in-pathname
))
916 (with-pathname (pathname in-pathname
)
917 (with-pathname (wildname in-wildname
)
918 (macrolet ((frob (field &optional
(op 'components-match
))
919 `(or (null (,field wildname
))
920 (,op
(,field pathname
) (,field wildname
)))))
921 (and (or (null (%pathname-host wildname
))
922 (eq (%pathname-host wildname
) (%pathname-host pathname
)))
923 (frob %pathname-device
)
924 (frob %pathname-directory directory-components-match
)
925 (frob %pathname-name
)
926 (frob %pathname-type
)
927 (or (eq (%pathname-host wildname
) *unix-host
*)
928 (frob %pathname-version
)))))))
930 ;;; Place the substitutions into the pattern and return the string or pattern
931 ;;; that results. If DIDDLE-CASE is true, we diddle the result case as well,
932 ;;; in case we are translating between hosts with difference conventional case.
933 ;;; The second value is the tail of subs with all of the values that we used up
934 ;;; stripped off. Note that PATTERN-MATCHES matches all consecutive wildcards
935 ;;; as a single string, so we ignore subsequent contiguous wildcards.
936 (defun substitute-into (pattern subs diddle-case
)
937 (declare (type pattern pattern
)
939 (values (or simple-base-string pattern
) list
))
940 (let ((in-wildcard nil
)
943 (dolist (piece (pattern-pieces pattern
))
944 (cond ((simple-string-p piece
)
946 (setf in-wildcard nil
))
951 (error "not enough wildcards in FROM pattern to match ~
954 (let ((sub (pop subs
)))
958 (push (apply #'concatenate
'simple-string
961 (dolist (piece (pattern-pieces sub
))
962 (push piece pieces
)))
966 (error "can't substitute this into the middle of a word:~
971 (push (apply #'concatenate
'simple-string
(nreverse strings
))
975 (if (and pieces
(simple-string-p (car pieces
)) (null (cdr pieces
)))
977 (make-pattern (nreverse pieces
)))
981 ;;; Called when we can't see how source and from matched.
982 (defun didnt-match-error (source from
)
983 (error "Pathname components from SOURCE and FROM args to TRANSLATE-PATHNAME~@
984 did not match:~% ~S ~S"
987 ;;; Do TRANSLATE-COMPONENT for all components except host, directory
989 (defun translate-component (source from to diddle-case
)
996 (if (pattern= from source
)
998 (didnt-match-error source from
)))
1000 (multiple-value-bind (won subs
) (pattern-matches from source
)
1002 (values (substitute-into to subs diddle-case
))
1003 (didnt-match-error source from
))))
1005 (maybe-diddle-case source diddle-case
))))
1007 (values (substitute-into to
(list source
) diddle-case
)))
1009 (if (components-match source from
)
1010 (maybe-diddle-case source diddle-case
)
1011 (didnt-match-error source from
)))))
1013 (maybe-diddle-case source diddle-case
))
1015 (if (components-match source from
)
1017 (didnt-match-error source from
)))))
1019 ;;; Return a list of all the things that we want to substitute into the TO
1020 ;;; pattern (the things matched by from on source.) When From contains
1021 ;;; :WILD-INFERIORS, the result contains a sublist of the matched source
1023 (defun compute-directory-substitutions (orig-source orig-from
)
1024 (let ((source orig-source
)
1029 (unless (every (lambda (x) (eq x
:wild-inferiors
)) from
)
1030 (didnt-match-error orig-source orig-from
))
1033 (unless from
(didnt-match-error orig-source orig-from
))
1034 (let ((from-part (pop from
))
1035 (source-part (pop source
)))
1038 (typecase source-part
1040 (if (pattern= from-part source-part
)
1042 (didnt-match-error orig-source orig-from
)))
1044 (multiple-value-bind (won new-subs
)
1045 (pattern-matches from-part source-part
)
1047 (dolist (sub new-subs
)
1049 (didnt-match-error orig-source orig-from
))))
1051 (didnt-match-error orig-source orig-from
))))
1054 ((member :wild-inferiors
)
1055 (let ((remaining-source (cons source-part source
)))
1058 (when (directory-components-match remaining-source from
)
1060 (unless remaining-source
1061 (didnt-match-error orig-source orig-from
))
1062 (res (pop remaining-source
)))
1064 (setq source remaining-source
))))
1066 (unless (and (simple-string-p source-part
)
1067 (string= from-part source-part
))
1068 (didnt-match-error orig-source orig-from
)))
1070 (didnt-match-error orig-source orig-from
)))))
1073 ;;; This is called by TRANSLATE-PATHNAME on the directory components
1074 ;;; of its argument pathnames to produce the result directory
1075 ;;; component. If this leaves the directory NIL, we return the source
1076 ;;; directory. The :RELATIVE or :ABSOLUTE is taken from the source
1077 ;;; directory, except if TO is :ABSOLUTE, in which case the result
1078 ;;; will be :ABSOLUTE.
1079 (defun translate-directories (source from to diddle-case
)
1080 (if (not (and source to from
))
1081 (or (and to
(null source
) (remove :wild-inferiors to
))
1082 (mapcar (lambda (x) (maybe-diddle-case x diddle-case
)) source
))
1084 ;; If TO is :ABSOLUTE, the result should still be :ABSOLUTE.
1085 (res (if (eq (first to
) :absolute
)
1088 (let ((subs-left (compute-directory-substitutions (rest source
)
1090 (dolist (to-part (rest to
))
1094 (let ((match (pop subs-left
)))
1096 (error ":WILD-INFERIORS is not paired in from and to ~
1097 patterns:~% ~S ~S" from to
))
1098 (res (maybe-diddle-case match diddle-case
))))
1099 ((member :wild-inferiors
)
1101 (let ((match (pop subs-left
)))
1102 (unless (listp match
)
1103 (error ":WILD-INFERIORS not paired in from and to ~
1104 patterns:~% ~S ~S" from to
))
1106 (res (maybe-diddle-case x diddle-case
)))))
1108 (multiple-value-bind
1110 (substitute-into to-part subs-left diddle-case
)
1111 (setf subs-left new-subs-left
)
1113 (t (res to-part
)))))
1116 (defun translate-pathname (source from-wildname to-wildname
&key
)
1118 "Use the source pathname to translate the from-wildname's wild and
1119 unspecified elements into a completed to-pathname based on the to-wildname."
1120 (declare (type pathname-designator source from-wildname to-wildname
))
1121 (with-pathname (source source
)
1122 (with-pathname (from from-wildname
)
1123 (with-pathname (to to-wildname
)
1124 (let* ((source-host (%pathname-host source
))
1125 (from-host (%pathname-host from
))
1126 (to-host (%pathname-host to
))
1128 (and source-host to-host
1129 (not (eq (host-customary-case source-host
)
1130 (host-customary-case to-host
))))))
1131 (macrolet ((frob (field &optional
(op 'translate-component
))
1132 `(let ((result (,op
(,field source
)
1136 (if (eq result
:error
)
1137 (error "~S doesn't match ~S." source from
)
1139 (%make-maybe-logical-pathname
1140 (or to-host source-host
)
1141 (frob %pathname-device
)
1142 (frob %pathname-directory translate-directories
)
1143 (frob %pathname-name
)
1144 (frob %pathname-type
)
1145 (if (eq from-host
*unix-host
*)
1146 (if (eq (%pathname-version to
) :wild
)
1147 (%pathname-version from
)
1148 (%pathname-version to
))
1149 (frob %pathname-version
)))))))))
1151 ;;;; logical pathname support. ANSI 92-102 specification.
1153 ;;;; As logical-pathname translations are loaded they are
1154 ;;;; canonicalized as patterns to enable rapid efficient translation
1155 ;;;; into physical pathnames.
1159 ;;; Canonicalize a logical pathname word by uppercasing it checking that it
1160 ;;; contains only legal characters.
1161 (defun logical-word-or-lose (word)
1162 (declare (string word
))
1163 (when (string= word
"")
1164 (error 'namestring-parse-error
1165 :complaint
"Attempted to treat invalid logical hostname ~
1166 as a logical host:~% ~S"
1168 :namestring word
:offset
0))
1169 (let ((word (string-upcase word
)))
1170 (dotimes (i (length word
))
1171 (let ((ch (schar word i
)))
1172 (unless (or (alpha-char-p ch
) (digit-char-p ch
) (char= ch
#\-
))
1173 (error 'namestring-parse-error
1174 :complaint
"logical namestring character which ~
1175 is not alphanumeric or hyphen:~% ~S"
1177 :namestring word
:offset i
))))
1180 ;;; Given a logical host or string, return a logical host. If ERROR-P
1181 ;;; is NIL, then return NIL when no such host exists.
1182 (defun find-logical-host (thing &optional
(errorp t
))
1185 (let ((found (gethash (logical-word-or-lose thing
)
1187 (if (or found
(not errorp
))
1189 ;; This is the error signalled from e.g.
1190 ;; LOGICAL-PATHNAME-TRANSLATIONS when host is not a defined
1191 ;; host, and ANSI specifies that that's a TYPE-ERROR.
1192 (error 'simple-type-error
1194 ;; God only knows what ANSI expects us to use for
1195 ;; the EXPECTED-TYPE here. Maybe this will be OK..
1197 '(and string
(satisfies logical-pathname-translations
))
1198 :format-control
"logical host not yet defined: ~S"
1199 :format-arguments
(list thing
)))))
1200 (logical-host thing
)))
1202 ;;; Given a logical host name or host, return a logical host, creating
1203 ;;; a new one if necessary.
1204 (defun intern-logical-host (thing)
1205 (declare (values logical-host
))
1206 (or (find-logical-host thing nil
)
1207 (let* ((name (logical-word-or-lose thing
))
1208 (new (make-logical-host :name name
)))
1209 (setf (gethash name
*logical-hosts
*) new
)
1212 ;;;; logical pathname parsing
1214 ;;; Deal with multi-char wildcards in a logical pathname token.
1215 (defun maybe-make-logical-pattern (namestring chunks
)
1216 (let ((chunk (caar chunks
)))
1217 (collect ((pattern))
1219 (len (length chunk
)))
1220 (declare (fixnum last-pos
))
1222 (when (= last-pos len
) (return))
1223 (let ((pos (or (position #\
* chunk
:start last-pos
) len
)))
1224 (if (= pos last-pos
)
1226 (error 'namestring-parse-error
1227 :complaint
"double asterisk inside of logical ~
1230 :namestring namestring
1231 :offset
(+ (cdar chunks
) pos
)))
1232 (pattern (subseq chunk last-pos pos
)))
1235 (pattern :multi-char-wild
))
1236 (setq last-pos
(1+ pos
)))))
1239 (make-pattern (pattern))
1240 (let ((x (car (pattern))))
1241 (if (eq x
:multi-char-wild
)
1245 ;;; Return a list of conses where the CDR is the start position and
1246 ;;; the CAR is a string (token) or character (punctuation.)
1247 (defun logical-chunkify (namestr start end
)
1249 (do ((i start
(1+ i
))
1253 (chunks (cons (nstring-upcase (subseq namestr prev end
)) prev
))))
1254 (let ((ch (schar namestr i
)))
1255 (unless (or (alpha-char-p ch
) (digit-char-p ch
)
1256 (member ch
'(#\-
#\
*)))
1258 (chunks (cons (nstring-upcase (subseq namestr prev i
)) prev
)))
1260 (unless (member ch
'(#\
; #\: #\.))
1261 (error 'namestring-parse-error
1262 :complaint
"illegal character for logical pathname:~% ~S"
1266 (chunks (cons ch i
)))))
1269 ;;; Break up a logical-namestring, always a string, into its
1270 ;;; constituent parts.
1271 (defun parse-logical-namestring (namestr start end
)
1272 (declare (type simple-base-string namestr
)
1273 (type index start end
))
1274 (collect ((directory))
1279 (labels ((expecting (what chunks
)
1280 (unless (and chunks
(simple-string-p (caar chunks
)))
1281 (error 'namestring-parse-error
1282 :complaint
"expecting ~A, got ~:[nothing~;~S~]."
1283 :args
(list what
(caar chunks
) (caar chunks
))
1285 :offset
(if chunks
(cdar chunks
) end
)))
1287 (parse-host (chunks)
1288 (case (caadr chunks
)
1291 (find-logical-host (expecting "a host name" chunks
)))
1292 (parse-relative (cddr chunks
)))
1294 (parse-relative chunks
))))
1295 (parse-relative (chunks)
1298 (directory :relative
)
1299 (parse-directory (cdr chunks
)))
1301 (directory :absolute
) ; Assumption! Maybe revoked later.
1302 (parse-directory chunks
))))
1303 (parse-directory (chunks)
1304 (case (caadr chunks
)
1307 (let ((res (expecting "a directory name" chunks
)))
1308 (cond ((string= res
"..") :up
)
1309 ((string= res
"**") :wild-inferiors
)
1311 (maybe-make-logical-pattern namestr chunks
)))))
1312 (parse-directory (cddr chunks
)))
1314 (parse-name chunks
))))
1315 (parse-name (chunks)
1317 (expecting "a file name" chunks
)
1318 (setq name
(maybe-make-logical-pattern namestr chunks
))
1319 (expecting-dot (cdr chunks
))))
1320 (expecting-dot (chunks)
1322 (unless (eql (caar chunks
) #\.
)
1323 (error 'namestring-parse-error
1324 :complaint
"expecting a dot, got ~S."
1325 :args
(list (caar chunks
))
1327 :offset
(cdar chunks
)))
1329 (parse-version (cdr chunks
))
1330 (parse-type (cdr chunks
)))))
1331 (parse-type (chunks)
1332 (expecting "a file type" chunks
)
1333 (setq type
(maybe-make-logical-pattern namestr chunks
))
1334 (expecting-dot (cdr chunks
)))
1335 (parse-version (chunks)
1336 (let ((str (expecting "a positive integer, * or NEWEST"
1339 ((string= str
"*") (setq version
:wild
))
1340 ((string= str
"NEWEST") (setq version
:newest
))
1342 (multiple-value-bind (res pos
)
1343 (parse-integer str
:junk-allowed t
)
1344 (unless (and res
(plusp res
))
1345 (error 'namestring-parse-error
1346 :complaint
"expected a positive integer, ~
1350 :offset
(+ pos
(cdar chunks
))))
1351 (setq version res
)))))
1353 (error 'namestring-parse-error
1354 :complaint
"extra stuff after end of file name"
1356 :offset
(cdadr chunks
)))))
1357 (parse-host (logical-chunkify namestr start end
)))
1358 (values host
:unspecific
(directory) name type version
))))
1360 ;;; We can't initialize this yet because not all host methods are
1362 (defvar *logical-pathname-defaults
*)
1364 (defun logical-pathname (pathspec)
1366 "Converts the pathspec argument to a logical-pathname and returns it."
1367 (declare (type (or logical-pathname string stream
) pathspec
)
1368 (values logical-pathname
))
1369 (if (typep pathspec
'logical-pathname
)
1371 (let ((res (parse-namestring pathspec nil
*logical-pathname-defaults
*)))
1372 (when (eq (%pathname-host res
)
1373 (%pathname-host
*logical-pathname-defaults
*))
1374 (error "This logical namestring does not specify a host:~% ~S"
1378 ;;;; logical pathname unparsing
1380 (defun unparse-logical-directory (pathname)
1381 (declare (type pathname pathname
))
1383 (let ((directory (%pathname-directory pathname
)))
1385 (ecase (pop directory
)
1386 (:absolute
) ; nothing special
1387 (:relative
(pieces ";")))
1388 (dolist (dir directory
)
1389 (cond ((or (stringp dir
) (pattern-p dir
))
1390 (pieces (unparse-logical-piece dir
))
1394 ((eq dir
:wild-inferiors
)
1397 (error "invalid directory component: ~S" dir
))))))
1398 (apply #'concatenate
'simple-string
(pieces))))
1400 (defun unparse-logical-piece (thing)
1402 (simple-string thing
)
1404 (collect ((strings))
1405 (dolist (piece (pattern-pieces thing
))
1407 (simple-string (strings piece
))
1409 (cond ((eq piece
:wild-inferiors
)
1411 ((eq piece
:multi-char-wild
)
1413 (t (error "invalid keyword: ~S" piece
))))))
1414 (apply #'concatenate
'simple-string
(strings))))))
1416 (defun unparse-logical-file (pathname)
1417 (declare (type pathname pathname
))
1418 (collect ((strings))
1419 (let* ((name (%pathname-name pathname
))
1420 (type (%pathname-type pathname
))
1421 (version (%pathname-version pathname
))
1422 (type-supplied (not (or (null type
) (eq type
:unspecific
))))
1423 (version-supplied (not (or (null version
)
1424 (eq version
:unspecific
)))))
1426 (when (and (null type
) (position #\. name
:start
1))
1427 (error "too many dots in the name: ~S" pathname
))
1428 (strings (unparse-logical-piece name
)))
1431 (error "cannot specify the type without a file: ~S" pathname
))
1432 (when (typep type
'simple-base-string
)
1433 (when (position #\. type
)
1434 (error "type component can't have a #\. inside: ~S" pathname
)))
1436 (strings (unparse-logical-piece type
)))
1437 (when version-supplied
1438 (unless type-supplied
1439 (error "cannot specify the version without a type: ~S" pathname
))
1441 ((member :newest
) (strings ".NEWEST"))
1442 ((member :wild
) (strings ".*"))
1443 (fixnum (strings ".") (strings (format nil
"~D" version
))))))
1444 (apply #'concatenate
'simple-string
(strings))))
1446 ;;; Unparse a logical pathname string.
1447 (defun unparse-enough-namestring (pathname defaults
)
1448 (let* ((path-directory (pathname-directory pathname
))
1449 (def-directory (pathname-directory defaults
))
1451 ;; Go down the directory lists to see what matches. What's
1452 ;; left is what we want, more or less.
1453 (cond ((and (eq (first path-directory
) (first def-directory
))
1454 (eq (first path-directory
) :absolute
))
1455 ;; Both paths are :ABSOLUTE, so find where the
1456 ;; common parts end and return what's left
1457 (do* ((p (rest path-directory
) (rest p
))
1458 (d (rest def-directory
) (rest d
)))
1459 ((or (endp p
) (endp d
)
1460 (not (equal (first p
) (first d
))))
1463 ;; At least one path is :RELATIVE, so just return the
1464 ;; original path. If the original path is :RELATIVE,
1465 ;; then that's the right one. If PATH-DIRECTORY is
1466 ;; :ABSOLUTE, we want to return that except when
1467 ;; DEF-DIRECTORY is :ABSOLUTE, as handled above. so return
1468 ;; the original directory.
1470 (unparse-logical-namestring
1471 (make-pathname :host
(pathname-host pathname
)
1472 :directory enough-directory
1473 :name
(pathname-name pathname
)
1474 :type
(pathname-type pathname
)
1475 :version
(pathname-version pathname
)))))
1477 (defun unparse-logical-namestring (pathname)
1478 (declare (type logical-pathname pathname
))
1479 (concatenate 'simple-string
1480 (logical-host-name (%pathname-host pathname
)) ":"
1481 (unparse-logical-directory pathname
)
1482 (unparse-logical-file pathname
)))
1484 ;;;; logical pathname translations
1486 ;;; Verify that the list of translations consists of lists and prepare
1487 ;;; canonical translations. (Parse pathnames and expand out wildcards
1489 (defun canonicalize-logical-pathname-translations (translation-list host
)
1490 (declare (type list translation-list
) (type host host
)
1492 (mapcar (lambda (translation)
1493 (destructuring-bind (from to
) translation
1494 (list (if (typep from
'logical-pathname
)
1496 (parse-namestring from host
))
1500 (defun logical-pathname-translations (host)
1502 "Return the (logical) host object argument's list of translations."
1503 (declare (type (or string logical-host
) host
)
1505 (logical-host-translations (find-logical-host host
)))
1507 (defun (setf logical-pathname-translations
) (translations host
)
1509 "Set the translations list for the logical host argument."
1510 (declare (type (or string logical-host
) host
)
1511 (type list translations
)
1513 (let ((host (intern-logical-host host
)))
1514 (setf (logical-host-canon-transls host
)
1515 (canonicalize-logical-pathname-translations translations host
))
1516 (setf (logical-host-translations host
) translations
)))
1518 (defun translate-logical-pathname (pathname &key
)
1520 "Translate PATHNAME to a physical pathname, which is returned."
1521 (declare (type pathname-designator pathname
)
1522 (values (or null pathname
)))
1525 (dolist (x (logical-host-canon-transls (%pathname-host pathname
))
1526 (error 'simple-file-error
1528 :format-control
"no translation for ~S"
1529 :format-arguments
(list pathname
)))
1530 (destructuring-bind (from to
) x
1531 (when (pathname-match-p pathname from
)
1532 (return (translate-logical-pathname
1533 (translate-pathname pathname from to
)))))))
1535 (t (translate-logical-pathname (pathname pathname
)))))
1537 (defvar *logical-pathname-defaults
*
1538 (%make-logical-pathname
(make-logical-host :name
"BOGUS")
1545 (defun load-logical-pathname-translations (host)
1547 (declare (type string host
)
1548 (values (member t nil
)))
1549 (if (find-logical-host host nil
)
1550 ;; This host is already defined, all is well and good.
1552 ;; ANSI: "The specific nature of the search is
1553 ;; implementation-defined." SBCL: doesn't search at all
1555 ;; FIXME: now that we have a SYS host that the system uses, it
1556 ;; might be cute to search in "SYS:TRANSLATIONS;<name>.LISP"
1557 (error "logical host ~S not found" host
)))