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!method print-object ((host host) stream)
19 (print-unreadable-object (host stream :type t :identity t)))
23 (def!method print-object ((pathname pathname) stream)
24 (let ((namestring (handler-case (namestring pathname)
27 (format stream "#P~S" namestring)
28 ;; FIXME: This code was rewritten and should be tested. (How does
29 ;; control get to this case anyhow? Perhaps we could just punt it?)
30 (print-unreadable-object (pathname stream :type t)
32 "(with no namestring) :HOST ~S :DEVICE ~S :DIRECTORY ~S ~
33 :NAME ~S :TYPE ~S :VERSION ~S"
34 (%pathname-host pathname)
35 (%pathname-device pathname)
36 (%pathname-directory pathname)
37 (%pathname-name pathname)
38 (%pathname-type pathname)
39 (%pathname-version pathname))))))
41 (def!method make-load-form ((pathname pathname) &optional environment)
42 (make-load-form-saving-slots pathname :environment environment))
44 ;;; The potential conflict with search lists requires isolating the
45 ;;; printed representation to use the i/o macro #.(logical-pathname
46 ;;; <path-designator>).
48 ;;; FIXME: We don't use search lists any more, so that comment is
50 (def!method print-object ((pathname logical-pathname) stream)
51 (let ((namestring (handler-case (namestring pathname)
54 (format stream "#.(logical-pathname ~S)" namestring)
55 (print-unreadable-object (pathname stream :type t)
57 ":HOST ~S :DIRECTORY ~S :FILE ~S :NAME=~S :VERSION ~S"
58 (%pathname-host pathname)
59 (%pathname-directory pathname)
60 (%pathname-name pathname)
61 (%pathname-type pathname)
62 (%pathname-version pathname))))))
64 ;;; A pathname is logical if the host component is a logical host.
65 ;;; This constructor is used to make an instance of the correct type
66 ;;; from parsed arguments.
67 (defun %make-maybe-logical-pathname (host device directory name type version)
68 ;; We canonicalize logical pathname components to uppercase. ANSI
69 ;; doesn't strictly require this, leaving it up to the implementor;
70 ;; but the arguments given in the X3J13 cleanup issue
71 ;; PATHNAME-LOGICAL:ADD seem compelling: we should canonicalize the
72 ;; case, and uppercase is the ordinary way to do that.
73 (flet ((upcase-maybe (x) (typecase x (string (string-upcase x)) (t x))))
74 (if (typep host 'logical-host)
75 (%make-logical-pathname host
77 (mapcar #'upcase-maybe directory)
81 (%make-pathname host device directory name type version))))
83 ;;; Hash table searching maps a logical pathname's host to its
84 ;;; physical pathname translation.
85 (defvar *logical-hosts* (make-hash-table :test 'equal))
89 (def!method make-load-form ((pattern pattern) &optional environment)
90 (make-load-form-saving-slots pattern :environment environment))
92 (def!method print-object ((pattern pattern) stream)
93 (print-unreadable-object (pattern stream :type t)
95 (let ((*print-escape* t))
96 (pprint-fill stream (pattern-pieces pattern) nil))
97 (prin1 (pattern-pieces pattern) stream))))
99 (defun pattern= (pattern1 pattern2)
100 (declare (type pattern pattern1 pattern2))
101 (let ((pieces1 (pattern-pieces pattern1))
102 (pieces2 (pattern-pieces pattern2)))
103 (and (= (length pieces1) (length pieces2))
104 (every #'(lambda (piece1 piece2)
107 (and (simple-string-p piece2)
108 (string= piece1 piece2)))
111 (eq (car piece1) (car piece2))
112 (string= (cdr piece1) (cdr piece2))))
114 (eq piece1 piece2))))
118 ;;; If the string matches the pattern returns the multiple values T and a
119 ;;; list of the matched strings.
120 (defun pattern-matches (pattern string)
121 (declare (type pattern pattern)
122 (type simple-string string))
123 (let ((len (length string)))
124 (labels ((maybe-prepend (subs cur-sub chars)
126 (let* ((len (length chars))
127 (new (make-string len))
130 (setf (schar new (decf index)) char))
133 (matches (pieces start subs cur-sub chars)
136 (values t (maybe-prepend subs cur-sub chars))
138 (let ((piece (car pieces)))
141 (let ((end (+ start (length piece))))
143 (string= piece string
144 :start2 start :end2 end)
145 (matches (cdr pieces) end
146 (maybe-prepend subs cur-sub chars)
152 (let ((char (schar string start)))
153 (if (find char (cdr piece) :test #'char=)
154 (matches (cdr pieces) (1+ start) subs t
155 (cons char chars))))))))
156 ((member :single-char-wild)
158 (matches (cdr pieces) (1+ start) subs t
159 (cons (schar string start) chars))))
160 ((member :multi-char-wild)
161 (multiple-value-bind (won new-subs)
162 (matches (cdr pieces) start subs t chars)
166 (matches pieces (1+ start) subs t
167 (cons (schar string start)
169 (multiple-value-bind (won subs)
170 (matches (pattern-pieces pattern) 0 nil nil nil)
171 (values won (reverse subs))))))
173 ;;; PATHNAME-MATCH-P for directory components
174 (defun directory-components-match (thing wild)
177 ;; If THING has a null directory, assume that it matches
178 ;; (:ABSOLUTE :WILD-INFERIORS) or (:RELATIVE :WILD-INFERIORS).
181 (member (first wild) '(:absolute :relative))
182 (eq (second wild) :wild-inferiors))
184 (let ((wild1 (first wild)))
185 (if (eq wild1 :wild-inferiors)
186 (let ((wild-subdirs (rest wild)))
187 (or (null wild-subdirs)
189 (when (directory-components-match thing wild-subdirs)
192 (unless thing (return nil)))))
194 (components-match (first thing) wild1)
195 (directory-components-match (rest thing)
198 ;;; Return true if pathname component THING is matched by WILD. (not
200 (defun components-match (thing wild)
201 (declare (type (or pattern symbol simple-string integer) thing wild))
206 ;; String is matched by itself, a matching pattern or :WILD.
209 (values (pattern-matches wild thing)))
211 (string= thing wild))))
213 ;; A pattern is only matched by an identical pattern.
214 (and (pattern-p wild) (pattern= thing wild)))
216 ;; An integer (version number) is matched by :WILD or the
217 ;; same integer. This branch will actually always be NIL as
218 ;; long as the version is a fixnum.
221 ;;; a predicate for comparing two pathname slot component sub-entries
222 (defun compare-component (this that)
226 (and (simple-string-p that)
227 (string= this that)))
229 (and (pattern-p that)
230 (pattern= this that)))
233 (compare-component (car this) (car that))
234 (compare-component (cdr this) (cdr that)))))))
236 ;;;; pathname functions
238 (defun pathname= (pathname1 pathname2)
239 (declare (type pathname pathname1)
240 (type pathname pathname2))
241 (and (eq (%pathname-host pathname1)
242 (%pathname-host pathname2))
243 (compare-component (%pathname-device pathname1)
244 (%pathname-device pathname2))
245 (compare-component (%pathname-directory pathname1)
246 (%pathname-directory pathname2))
247 (compare-component (%pathname-name pathname1)
248 (%pathname-name pathname2))
249 (compare-component (%pathname-type pathname1)
250 (%pathname-type pathname2))
251 (compare-component (%pathname-version pathname1)
252 (%pathname-version pathname2))))
254 ;;; Convert PATHNAME-DESIGNATOR (a pathname, or string, or
255 ;;; stream), into a pathname in pathname.
257 ;;; FIXME: was rewritten, should be tested (or rewritten again, this
258 ;;; time using ONCE-ONLY, *then* tested)
259 ;;; FIXME: become SB!XC:DEFMACRO inside EVAL-WHEN (COMPILE EVAL)?
260 (defmacro with-pathname ((pathname pathname-designator) &body body)
261 (let ((pd0 (gensym)))
262 `(let* ((,pd0 ,pathname-designator)
263 (,pathname (etypecase ,pd0
265 (string (parse-namestring ,pd0))
266 (stream (file-name ,pd0)))))
269 ;;; Convert the var, a host or string name for a host, into a
270 ;;; LOGICAL-HOST structure or nil if not defined.
272 ;;; pw notes 1/12/97 this potentially useful macro is not used anywhere
273 ;;; and 'find-host' is not defined. 'find-logical-host' seems to be needed.
275 (defmacro with-host ((var expr) &body body)
276 `(let ((,var (let ((,var ,expr))
279 (string (find-logical-host ,var nil))
284 (defun pathname (thing)
286 "Convert thing (a pathname, string or stream) into a pathname."
287 (declare (type pathname-designator thing))
288 (with-pathname (pathname thing)
291 ;;; Change the case of thing if DIDDLE-P.
292 (defun maybe-diddle-case (thing diddle-p)
293 (if (and diddle-p (not (or (symbolp thing) (integerp thing))))
294 (labels ((check-for (pred in)
297 (dolist (piece (pattern-pieces in))
298 (when (typecase piece
300 (check-for pred piece))
304 (check-for pred (cdr in))))))
308 (when (check-for pred x)
311 (dotimes (i (length in))
312 (when (funcall pred (schar in i))
315 (diddle-with (fun thing)
319 (mapcar #'(lambda (piece)
327 (funcall fun (cdr piece))))
332 (pattern-pieces thing))))
339 (let ((any-uppers (check-for #'upper-case-p thing))
340 (any-lowers (check-for #'lower-case-p thing)))
341 (cond ((and any-uppers any-lowers)
342 ;; Mixed case, stays the same.
345 ;; All uppercase, becomes all lower case.
346 (diddle-with #'(lambda (x) (if (stringp x)
350 ;; All lowercase, becomes all upper case.
351 (diddle-with #'(lambda (x) (if (stringp x)
355 ;; No letters? I guess just leave it.
359 (defun merge-directories (dir1 dir2 diddle-case)
360 (if (or (eq (car dir1) :absolute)
365 (if (and (eq dir :back)
367 (not (eq (car results) :back)))
369 (push dir results))))
370 (dolist (dir (maybe-diddle-case dir2 diddle-case))
372 (dolist (dir (cdr dir1))
376 (defun merge-pathnames (pathname
378 (defaults *default-pathname-defaults*)
379 (default-version :newest))
381 "Construct a filled in pathname by completing the unspecified components
383 (declare (type pathname-designator pathname)
384 (type pathname-designator defaults)
386 (with-pathname (defaults defaults)
387 (let ((pathname (let ((*default-pathname-defaults* defaults))
388 (pathname pathname))))
389 (let* ((default-host (%pathname-host defaults))
390 (pathname-host (%pathname-host pathname))
392 (and default-host pathname-host
393 (not (eq (host-customary-case default-host)
394 (host-customary-case pathname-host))))))
395 (%make-maybe-logical-pathname
396 (or pathname-host default-host)
397 (or (%pathname-device pathname)
398 (maybe-diddle-case (%pathname-device defaults)
400 (merge-directories (%pathname-directory pathname)
401 (%pathname-directory defaults)
403 (or (%pathname-name pathname)
404 (maybe-diddle-case (%pathname-name defaults)
406 (or (%pathname-type pathname)
407 (maybe-diddle-case (%pathname-type defaults)
409 (or (%pathname-version pathname)
410 default-version))))))
412 (defun import-directory (directory diddle-case)
415 ((member :wild) '(:absolute :wild-inferiors))
416 ((member :unspecific) '(:relative))
419 (ecase (pop directory)
422 (when (search-list-p (car directory))
423 (results (pop directory))))
425 (results :relative)))
426 (dolist (piece directory)
427 (cond ((member piece '(:wild :wild-inferiors :up :back))
429 ((or (simple-string-p piece) (pattern-p piece))
430 (results (maybe-diddle-case piece diddle-case)))
432 (results (maybe-diddle-case (coerce piece 'simple-string)
435 (error "~S is not allowed as a directory component." piece))))
439 ,(maybe-diddle-case directory diddle-case)))
442 ,(maybe-diddle-case (coerce directory 'simple-string)
445 (defun make-pathname (&key host
450 (version nil versionp)
454 "Makes a new pathname from the component arguments. Note that host is
455 a host-structure or string."
456 (declare (type (or string host component-tokens) host)
457 (type (or string component-tokens) device)
458 (type (or list string pattern component-tokens) directory)
459 (type (or string pattern component-tokens) name type)
460 (type (or integer component-tokens (member :newest)) version)
461 (type (or pathname-designator null) defaults)
462 (type (member :common :local) case))
463 (let* ((defaults (when defaults
464 (with-pathname (defaults defaults) defaults)))
465 (default-host (if defaults
466 (%pathname-host defaults)
467 (pathname-host *default-pathname-defaults*)))
468 ;; toy@rtp.ericsson.se: CLHS says make-pathname can take a
469 ;; string (as a logical-host) for the host part. We map that
470 ;; string into the corresponding logical host structure.
473 ;; HyperSpec says for the arg to MAKE-PATHNAME;
474 ;; "host---a valid physical pathname host. ..."
475 ;; where it probably means -- a valid pathname host.
476 ;; "valid pathname host n. a valid physical pathname host or
477 ;; a valid logical pathname host."
479 ;; "valid physical pathname host n. any of a string,
480 ;; a list of strings, or the symbol :unspecific,
481 ;; that is recognized by the implementation as the name of a host."
482 ;; "valid logical pathname host n. a string that has been defined
483 ;; as the name of a logical host. ..."
484 ;; HS is silent on what happens if the :HOST arg is NOT one of these.
485 ;; It seems an error message is appropriate.
487 (host host) ; A valid host, use it.
488 (string (find-logical-host host t)) ; logical-host or lose.
489 (t default-host))) ; unix-host
490 (diddle-args (and (eq (host-customary-case host) :lower)
493 (not (eq (host-customary-case host)
494 (host-customary-case default-host))))
495 (dev (if devp device (if defaults (%pathname-device defaults))))
496 (dir (import-directory directory diddle-args))
499 (defaults (%pathname-version defaults))
501 (when (and defaults (not dirp))
503 (merge-directories dir
504 (%pathname-directory defaults)
507 (macrolet ((pick (var varp field)
508 `(cond ((or (simple-string-p ,var)
510 (maybe-diddle-case ,var diddle-args))
512 (maybe-diddle-case (coerce ,var 'simple-string)
515 (maybe-diddle-case ,var diddle-args))
517 (maybe-diddle-case (,field defaults)
521 (%make-maybe-logical-pathname host
522 dev ; forced to :UNSPECIFIC when logical
524 (pick name namep %pathname-name)
525 (pick type typep %pathname-type)
528 (defun pathname-host (pathname &key (case :local))
530 "Accessor for the pathname's host."
531 (declare (type pathname-designator pathname)
532 (type (member :local :common) case)
535 (with-pathname (pathname pathname)
536 (%pathname-host pathname)))
538 (defun pathname-device (pathname &key (case :local))
540 "Accessor for pathname's device."
541 (declare (type pathname-designator pathname)
542 (type (member :local :common) case))
543 (with-pathname (pathname pathname)
544 (maybe-diddle-case (%pathname-device pathname)
545 (and (eq case :common)
546 (eq (host-customary-case
547 (%pathname-host pathname))
550 (defun pathname-directory (pathname &key (case :local))
552 "Accessor for the pathname's directory list."
553 (declare (type pathname-designator pathname)
554 (type (member :local :common) case))
555 (with-pathname (pathname pathname)
556 (maybe-diddle-case (%pathname-directory pathname)
557 (and (eq case :common)
558 (eq (host-customary-case
559 (%pathname-host pathname))
561 (defun pathname-name (pathname &key (case :local))
563 "Accessor for the pathname's name."
564 (declare (type pathname-designator pathname)
565 (type (member :local :common) case))
566 (with-pathname (pathname pathname)
567 (maybe-diddle-case (%pathname-name pathname)
568 (and (eq case :common)
569 (eq (host-customary-case
570 (%pathname-host pathname))
574 (defun pathname-type (pathname &key (case :local))
576 "Accessor for the pathname's name."
577 (declare (type pathname-designator pathname)
578 (type (member :local :common) case))
579 (with-pathname (pathname pathname)
580 (maybe-diddle-case (%pathname-type pathname)
581 (and (eq case :common)
582 (eq (host-customary-case
583 (%pathname-host pathname))
587 (defun pathname-version (pathname)
589 "Accessor for the pathname's version."
590 (declare (type pathname-designator pathname))
591 (with-pathname (pathname pathname)
592 (%pathname-version pathname)))
596 ;;; Handle the case where PARSE-NAMESTRING is actually parsing a
597 ;;; namestring. We pick off the :JUNK-ALLOWED case then find a host to
598 ;;; use for parsing, call the parser, then check whether the host matches.
599 (defun %parse-namestring (namestr host defaults start end junk-allowed)
600 (declare (type (or host null) host)
601 (type string namestr)
603 (type (or index null) end))
606 (%parse-namestring namestr host defaults start end nil)
607 (namestring-parse-error (condition)
608 (values nil (namestring-parse-error-offset condition))))
609 (let* ((end (or end (length namestr)))
611 (extract-logical-host-prefix namestr start end)
612 (pathname-host defaults))))
614 (error "When no HOST argument is supplied, the DEFAULTS argument ~
615 must have a non-null PATHNAME-HOST."))
617 (multiple-value-bind (new-host device directory file type version)
618 (funcall (host-parse parse-host) namestr start end)
619 (when (and host new-host (not (eq new-host host)))
620 (error 'simple-type-error
622 ;; Note: ANSI requires that this be a TYPE-ERROR,
623 ;; but there seems to be no completely correct
624 ;; value to use for TYPE-ERROR-EXPECTED-TYPE.
625 ;; Instead, we return a sort of "type error allowed
626 ;; type", trying to say "it would be OK if you
627 ;; passed NIL as the host value" but not mentioning
628 ;; that a matching string would be OK too.
631 "The host in the namestring, ~S,~@
632 does not match the explicit HOST argument, ~S."
633 :format-arguments (list new-host host)))
634 (let ((pn-host (or new-host parse-host)))
635 (values (%make-maybe-logical-pathname
636 pn-host device directory file type version)
639 ;;; If NAMESTR begins with a colon-terminated, defined, logical host,
640 ;;; then return that host, otherwise return NIL.
641 (defun extract-logical-host-prefix (namestr start end)
642 (declare (type simple-base-string namestr)
643 (type index start end)
644 (values (or logical-host null)))
645 (let ((colon-pos (position #\: namestr :start start :end end)))
647 (values (gethash (nstring-upcase (subseq namestr start colon-pos))
651 (defun parse-namestring (thing
654 (defaults *default-pathname-defaults*)
655 &key (start 0) end junk-allowed)
656 (declare (type pathname-designator thing)
657 (type (or list host string (member :unspecific)) host)
658 (type pathname defaults)
660 (type (or index null) end)
661 (type (or t null) junk-allowed)
662 (values (or null pathname) (or null index)))
663 ;; Generally, redundant specification of information in software,
664 ;; whether in code or in comments, is bad. However, the ANSI spec
665 ;; for this is messy enough that it's hard to hold in short-term
666 ;; memory, so I've recorded these redundant notes on the
667 ;; implications of the ANSI spec.
669 ;; According to the ANSI spec, HOST can be a valid pathname host, or
670 ;; a logical host, or NIL.
672 ;; A valid pathname host can be a valid physical pathname host or a
673 ;; valid logical pathname host.
675 ;; A valid physical pathname host is "any of a string, a list of
676 ;; strings, or the symbol :UNSPECIFIC, that is recognized by the
677 ;; implementation as the name of a host". In SBCL as of 0.6.9.8,
678 ;; that means :UNSPECIFIC: though someday we might want to
679 ;; generalize it to allow strings like "RTFM.MIT.EDU" or lists like
680 ;; '("RTFM" "MIT" "EDU"), that's not supported now.
682 ;; A valid logical pathname host is a string which has been defined as
683 ;; the name of a logical host, as with LOAD-LOGICAL-PATHNAME-TRANSLATIONS.
685 ;; A logical host is an object of implementation-dependent nature. In
686 ;; SBCL, it's a member of the HOST class (a subclass of STRUCTURE-OBJECT).
687 (let ((found-host (etypecase host
689 ;; In general ANSI-compliant Common Lisps, a
690 ;; string might also be a physical pathname host,
691 ;; but ANSI leaves this up to the implementor,
692 ;; and in SBCL we don't do it, so it must be a
694 (find-logical-host host))
695 ((or null (member :unspecific))
696 ;; CLHS says that HOST=:UNSPECIFIC has
697 ;; implementation-defined behavior. We
698 ;; just turn it into NIL.
701 ;; ANSI also allows LISTs to designate hosts,
702 ;; but leaves its interpretation
703 ;; implementation-defined. Our interpretation
704 ;; is that it's unsupported.:-|
705 (error "A LIST representing a pathname host is not ~
706 supported in this implementation:~% ~S"
710 (declare (type (or null host) found-host))
713 (%parse-namestring thing found-host defaults start end junk-allowed))
715 (%parse-namestring (coerce thing 'simple-string)
716 found-host defaults start end junk-allowed))
718 (let ((defaulted-host (or found-host (%pathname-host defaults))))
719 (declare (type host defaulted-host))
720 (unless (eq defaulted-host (%pathname-host thing))
721 (error "The HOST argument doesn't match the pathname host:~% ~
723 defaulted-host (%pathname-host thing))))
724 (values thing start))
726 (let ((name (file-name thing)))
728 (error "can't figure out the file associated with stream:~% ~S"
730 (values name nil))))))
732 (defun namestring (pathname)
734 "Construct the full (name)string form of the pathname."
735 (declare (type pathname-designator pathname)
736 (values (or null simple-base-string)))
737 (with-pathname (pathname pathname)
739 (let ((host (%pathname-host pathname)))
741 (error "can't determine the namestring for pathnames with no ~
742 host:~% ~S" pathname))
743 (funcall (host-unparse host) pathname)))))
745 (defun host-namestring (pathname)
747 "Returns a string representation of the name of the host in the pathname."
748 (declare (type pathname-designator pathname)
749 (values (or null simple-base-string)))
750 (with-pathname (pathname pathname)
751 (let ((host (%pathname-host pathname)))
753 (funcall (host-unparse-host host) pathname)
755 "can't determine the namestring for pathnames with no host:~% ~S"
758 (defun directory-namestring (pathname)
760 "Returns a string representation of the directories used in the pathname."
761 (declare (type pathname-designator pathname)
762 (values (or null simple-base-string)))
763 (with-pathname (pathname pathname)
764 (let ((host (%pathname-host pathname)))
766 (funcall (host-unparse-directory host) pathname)
768 "can't determine the namestring for pathnames with no host:~% ~S"
771 (defun file-namestring (pathname)
773 "Returns a string representation of the name used in the pathname."
774 (declare (type pathname-designator pathname)
775 (values (or null simple-base-string)))
776 (with-pathname (pathname pathname)
777 (let ((host (%pathname-host pathname)))
779 (funcall (host-unparse-file host) pathname)
781 "can't determine the namestring for pathnames with no host:~% ~S"
784 (defun enough-namestring (pathname
786 (defaults *default-pathname-defaults*))
788 "Returns an abbreviated pathname sufficent to identify the pathname relative
790 (declare (type pathname-designator pathname))
791 (with-pathname (pathname pathname)
792 (let ((host (%pathname-host pathname)))
794 (with-pathname (defaults defaults)
795 (funcall (host-unparse-enough host) pathname defaults))
797 "can't determine the namestring for pathnames with no host:~% ~S"
802 (defun wild-pathname-p (pathname &optional field-key)
804 "Predicate for determining whether pathname contains any wildcards."
805 (declare (type pathname-designator pathname)
806 (type (member nil :host :device :directory :name :type :version)
808 (with-pathname (pathname pathname)
810 (or (pattern-p x) (member x '(:wild :wild-inferiors)))))
813 (or (wild-pathname-p pathname :host)
814 (wild-pathname-p pathname :device)
815 (wild-pathname-p pathname :directory)
816 (wild-pathname-p pathname :name)
817 (wild-pathname-p pathname :type)
818 (wild-pathname-p pathname :version)))
819 (:host (frob (%pathname-host pathname)))
820 (:device (frob (%pathname-host pathname)))
821 (:directory (some #'frob (%pathname-directory pathname)))
822 (:name (frob (%pathname-name pathname)))
823 (:type (frob (%pathname-type pathname)))
824 (:version (frob (%pathname-version pathname)))))))
826 (defun pathname-match-p (in-pathname in-wildname)
828 "Pathname matches the wildname template?"
829 (declare (type pathname-designator in-pathname))
830 (with-pathname (pathname in-pathname)
831 (with-pathname (wildname in-wildname)
832 (macrolet ((frob (field &optional (op 'components-match ))
833 `(or (null (,field wildname))
834 (,op (,field pathname) (,field wildname)))))
835 (and (or (null (%pathname-host wildname))
836 (eq (%pathname-host wildname) (%pathname-host pathname)))
837 (frob %pathname-device)
838 (frob %pathname-directory directory-components-match)
839 (frob %pathname-name)
840 (frob %pathname-type)
841 (frob %pathname-version))))))
843 ;;; Place the substitutions into the pattern and return the string or pattern
844 ;;; that results. If DIDDLE-CASE is true, we diddle the result case as well,
845 ;;; in case we are translating between hosts with difference conventional case.
846 ;;; The second value is the tail of subs with all of the values that we used up
847 ;;; stripped off. Note that PATTERN-MATCHES matches all consecutive wildcards
848 ;;; as a single string, so we ignore subsequent contiguous wildcards.
849 (defun substitute-into (pattern subs diddle-case)
850 (declare (type pattern pattern)
852 (values (or simple-base-string pattern)))
853 (let ((in-wildcard nil)
856 (dolist (piece (pattern-pieces pattern))
857 (cond ((simple-string-p piece)
859 (setf in-wildcard nil))
864 (error "not enough wildcards in FROM pattern to match ~
867 (let ((sub (pop subs)))
871 (push (apply #'concatenate 'simple-string
874 (dolist (piece (pattern-pieces sub))
875 (push piece pieces)))
879 (error "can't substitute this into the middle of a word:~
884 (push (apply #'concatenate 'simple-string (nreverse strings))
888 (if (and pieces (simple-string-p (car pieces)) (null (cdr pieces)))
890 (make-pattern (nreverse pieces)))
894 ;;; Called when we can't see how source and from matched.
895 (defun didnt-match-error (source from)
896 (error "Pathname components from SOURCE and FROM args to TRANSLATE-PATHNAME~@
897 did not match:~% ~S ~S"
900 ;;; Do TRANSLATE-COMPONENT for all components except host and directory.
901 (defun translate-component (source from to diddle-case)
908 (if (pattern= from source)
910 (didnt-match-error source from)))
912 (multiple-value-bind (won subs) (pattern-matches from source)
914 (values (substitute-into to subs diddle-case))
915 (didnt-match-error source from))))
917 (maybe-diddle-case source diddle-case))))
919 (values (substitute-into to (list source) diddle-case)))
921 (if (components-match source from)
922 (maybe-diddle-case source diddle-case)
923 (didnt-match-error source from)))))
925 (maybe-diddle-case source diddle-case))
927 (if (components-match source from)
929 (didnt-match-error source from)))))
931 ;;; Return a list of all the things that we want to substitute into the TO
932 ;;; pattern (the things matched by from on source.) When From contains
933 ;;; :WILD-INFERIORS, the result contains a sublist of the matched source
935 (defun compute-directory-substitutions (orig-source orig-from)
936 (let ((source orig-source)
941 (unless (every #'(lambda (x) (eq x :wild-inferiors)) from)
942 (didnt-match-error orig-source orig-from))
945 (unless from (didnt-match-error orig-source orig-from))
946 (let ((from-part (pop from))
947 (source-part (pop source)))
950 (typecase source-part
952 (if (pattern= from-part source-part)
954 (didnt-match-error orig-source orig-from)))
956 (multiple-value-bind (won new-subs)
957 (pattern-matches from-part source-part)
959 (dolist (sub new-subs)
961 (didnt-match-error orig-source orig-from))))
963 (didnt-match-error orig-source orig-from))))
966 ((member :wild-inferiors)
967 (let ((remaining-source (cons source-part source)))
970 (when (directory-components-match remaining-source from)
972 (unless remaining-source
973 (didnt-match-error orig-source orig-from))
974 (res (pop remaining-source)))
976 (setq source remaining-source))))
978 (unless (and (simple-string-p source-part)
979 (string= from-part source-part))
980 (didnt-match-error orig-source orig-from)))
982 (didnt-match-error orig-source orig-from)))))
985 ;;; This is called by TRANSLATE-PATHNAME on the directory components
986 ;;; of its argument pathnames to produce the result directory
987 ;;; component. If this leaves the directory NIL, we return the source
988 ;;; directory. The :RELATIVE or :ABSOLUTE is taken from the source
989 ;;; directory, except if TO is :ABSOLUTE, in which case the result
990 ;;; will be :ABSOLUTE.
991 (defun translate-directories (source from to diddle-case)
992 (if (not (and source to from))
993 (or (and to (null source) (remove :wild-inferiors to))
994 (mapcar (lambda (x) (maybe-diddle-case x diddle-case)) source))
996 ;; If TO is :ABSOLUTE, the result should still be :ABSOLUTE.
997 (res (if (eq (first to) :absolute)
1000 (let ((subs-left (compute-directory-substitutions (rest source)
1002 (dolist (to-part (rest to))
1006 (let ((match (pop subs-left)))
1008 (error ":WILD-INFERIORS is not paired in from and to ~
1009 patterns:~% ~S ~S" from to))
1010 (res (maybe-diddle-case match diddle-case))))
1011 ((member :wild-inferiors)
1013 (let ((match (pop subs-left)))
1014 (unless (listp match)
1015 (error ":WILD-INFERIORS not paired in from and to ~
1016 patterns:~% ~S ~S" from to))
1018 (res (maybe-diddle-case x diddle-case)))))
1020 (multiple-value-bind
1022 (substitute-into to-part subs-left diddle-case)
1023 (setf subs-left new-subs-left)
1025 (t (res to-part)))))
1028 (defun translate-pathname (source from-wildname to-wildname &key)
1030 "Use the source pathname to translate the from-wildname's wild and
1031 unspecified elements into a completed to-pathname based on the to-wildname."
1032 (declare (type pathname-designator source from-wildname to-wildname))
1033 (with-pathname (source source)
1034 (with-pathname (from from-wildname)
1035 (with-pathname (to to-wildname)
1036 (let* ((source-host (%pathname-host source))
1037 (to-host (%pathname-host to))
1039 (and source-host to-host
1040 (not (eq (host-customary-case source-host)
1041 (host-customary-case to-host))))))
1042 (macrolet ((frob (field &optional (op 'translate-component))
1043 `(let ((result (,op (,field source)
1047 (if (eq result :error)
1048 (error "~S doesn't match ~S." source from)
1050 (%make-maybe-logical-pathname
1051 (or to-host source-host)
1052 (frob %pathname-device)
1053 (frob %pathname-directory translate-directories)
1054 (frob %pathname-name)
1055 (frob %pathname-type)
1056 (frob %pathname-version))))))))
1060 (def!struct (search-list (:make-load-form-fun
1062 (values `(intern-search-list
1063 ',(search-list-name s))
1065 ;; The name of this search-list. Always stored in lowercase.
1066 (name (required-argument) :type simple-string)
1067 ;; T if this search-list has been defined. Otherwise NIL.
1068 (defined nil :type (member t nil))
1069 ;; the list of expansions for this search-list. Each expansion is
1070 ;; the list of directory components to use in place of this
1072 (expansions nil :type list))
1073 (def!method print-object ((sl search-list) stream)
1074 (print-unreadable-object (sl stream :type t)
1075 (write-string (search-list-name sl) stream)))
1077 ;;; a hash table mapping search-list names to search-list structures
1078 (defvar *search-lists* (make-hash-table :test 'equal))
1080 ;;; When search-lists are encountered in namestrings, they are
1081 ;;; converted to search-list structures right then, instead of waiting
1082 ;;; until the search list used. This allows us to verify ahead of time
1083 ;;; that there are no circularities and makes expansion much quicker.
1084 (defun intern-search-list (name)
1085 (let ((name (string-downcase name)))
1086 (or (gethash name *search-lists*)
1087 (let ((new (make-search-list :name name)))
1088 (setf (gethash name *search-lists*) new)
1091 ;;; Clear the definition. Note: we can't remove it from the hash-table
1092 ;;; because there may be pathnames still refering to it. So we just
1093 ;;; clear out the expansions and ste defined to NIL.
1094 (defun clear-search-list (name)
1096 "Clear the current definition for the search-list NAME. Returns T if such
1097 a definition existed, and NIL if not."
1098 (let* ((name (string-downcase name))
1099 (search-list (gethash name *search-lists*)))
1100 (when (and search-list (search-list-defined search-list))
1101 (setf (search-list-defined search-list) nil)
1102 (setf (search-list-expansions search-list) nil)
1105 ;;; As in CLEAR-SEARCH-LIST, we can't actually remove the entries from
1106 ;;; the hash-table, so we just mark them as being undefined.
1107 (defun clear-all-search-lists ()
1109 "Clear the definition for all search-lists. Only use this if you know
1110 what you are doing."
1111 (maphash #'(lambda (name search-list)
1112 (declare (ignore name))
1113 (setf (search-list-defined search-list) nil)
1114 (setf (search-list-expansions search-list) nil))
1118 ;;; Extract the search-list from PATHNAME and return it. If PATHNAME
1119 ;;; doesn't start with a search-list, then either error (if
1120 ;;; FLAME-IF-NONE is true) or return NIL (if FLAME-IF-NONE is false).
1121 (defun extract-search-list (pathname flame-if-none)
1122 (with-pathname (pathname pathname)
1123 (let* ((directory (%pathname-directory pathname))
1124 (search-list (cadr directory)))
1125 (cond ((search-list-p search-list)
1128 (error "~S doesn't start with a search-list." pathname))
1132 ;;; We have to convert the internal form of the search-list back into
1133 ;;; a bunch of pathnames.
1134 (defun search-list (pathname)
1136 "Return the expansions for the search-list starting PATHNAME. If PATHNAME
1137 does not start with a search-list, then an error is signaled. If
1138 the search-list has not been defined yet, then an error is signaled.
1139 The expansion for a search-list can be set with SETF."
1140 (with-pathname (pathname pathname)
1141 (let ((search-list (extract-search-list pathname t))
1142 (host (pathname-host pathname)))
1143 (if (search-list-defined search-list)
1144 (mapcar #'(lambda (directory)
1145 (make-pathname :host host
1146 :directory (cons :absolute directory)))
1147 (search-list-expansions search-list))
1148 (error "Search list ~S has not been defined yet." pathname)))))
1150 (defun search-list-defined-p (pathname)
1152 "Returns T if the search-list starting PATHNAME is currently defined, and
1153 NIL otherwise. An error is signaled if PATHNAME does not start with a
1155 (with-pathname (pathname pathname)
1156 (search-list-defined (extract-search-list pathname t))))
1158 ;;; Set the expansion for the search list in PATHNAME. If this would
1159 ;;; result in any circularities, we flame out. If anything goes wrong,
1160 ;;; we leave the old definition intact.
1161 (defun %set-search-list (pathname values)
1162 (let ((search-list (extract-search-list pathname t)))
1164 ((check (target-list path)
1165 (when (eq search-list target-list)
1166 (error "That would result in a circularity:~% ~
1168 (search-list-name search-list)
1170 (search-list-name target-list)))
1171 (when (search-list-p target-list)
1172 (push (search-list-name target-list) path)
1173 (dolist (expansion (search-list-expansions target-list))
1174 (check (car expansion) path))))
1176 (with-pathname (pathname pathname)
1177 (when (or (pathname-name pathname)
1178 (pathname-type pathname)
1179 (pathname-version pathname))
1180 (error "Search-lists cannot expand into pathnames that have ~
1181 a name, type, or ~%version specified:~% ~S"
1183 (let ((directory (pathname-directory pathname)))
1186 (ecase (car directory)
1187 (:absolute (cdr directory))
1188 (:relative (cons (intern-search-list "default")
1190 (list (intern-search-list "default")))))
1191 (check (car expansion) nil)
1193 (setf (search-list-expansions search-list)
1195 (mapcar #'convert values)
1196 (list (convert values)))))
1197 (setf (search-list-defined search-list) t))
1200 (defun %enumerate-search-list (pathname function)
1201 (/show0 "entering %ENUMERATE-SEARCH-LIST")
1202 (let* ((pathname (if (typep pathname 'logical-pathname)
1203 (translate-logical-pathname pathname)
1205 (search-list (extract-search-list pathname nil)))
1206 (/show0 "PATHNAME and SEARCH-LIST computed")
1209 (/show0 "no search list")
1210 (funcall function pathname))
1211 ((not (search-list-defined search-list))
1212 (/show0 "undefined search list")
1213 (error "undefined search list: ~A"
1214 (search-list-name search-list)))
1216 (/show0 "general case")
1217 (let ((tail (cddr (pathname-directory pathname))))
1218 (/show0 "TAIL computed")
1220 (search-list-expansions search-list))
1221 (/show0 "tail recursing in %ENUMERATE-SEARCH-LIST")
1222 (%enumerate-search-list (make-pathname :defaults pathname
1229 ;;;; logical pathname support. ANSI 92-102 specification.
1231 ;;;; As logical-pathname translations are loaded they are
1232 ;;;; canonicalized as patterns to enable rapid efficent translation
1233 ;;;; into physical pathnames.
1237 ;;; Canonicalize a logical pathanme word by uppercasing it checking that it
1238 ;;; contains only legal characters.
1239 (defun logical-word-or-lose (word)
1240 (declare (string word))
1241 (let ((word (string-upcase word)))
1242 (dotimes (i (length word))
1243 (let ((ch (schar word i)))
1244 (unless (or (alpha-char-p ch) (digit-char-p ch) (char= ch #\-))
1245 (error 'namestring-parse-error
1246 :complaint "logical namestring character which ~
1247 is not alphanumeric or hyphen:~% ~S"
1248 :arguments (list ch)
1249 :namestring word :offset i))))
1252 ;;; Given a logical host or string, return a logical host. If ERROR-P
1253 ;;; is NIL, then return NIL when no such host exists.
1254 (defun find-logical-host (thing &optional (errorp t))
1257 (let ((found (gethash (logical-word-or-lose thing)
1259 (if (or found (not errorp))
1261 ;; This is the error signalled from e.g.
1262 ;; LOGICAL-PATHNAME-TRANSLATIONS when host is not a defined
1263 ;; host, and ANSI specifies that that's a TYPE-ERROR.
1264 (error 'simple-type-error
1266 ;; God only knows what ANSI expects us to use for
1267 ;; the EXPECTED-TYPE here. Maybe this will be OK..
1269 '(and string (satisfies logical-pathname-translations))
1270 :format-control "logical host not yet defined: ~S"
1271 :format-arguments (list thing)))))
1272 (logical-host thing)))
1274 ;;; Given a logical host name or host, return a logical host, creating
1275 ;;; a new one if necessary.
1276 (defun intern-logical-host (thing)
1277 (declare (values logical-host))
1278 (or (find-logical-host thing nil)
1279 (let* ((name (logical-word-or-lose thing))
1280 (new (make-logical-host :name name)))
1281 (setf (gethash name *logical-hosts*) new)
1284 ;;;; logical pathname parsing
1286 ;;; Deal with multi-char wildcards in a logical pathname token.
1287 (defun maybe-make-logical-pattern (namestring chunks)
1288 (let ((chunk (caar chunks)))
1289 (collect ((pattern))
1291 (len (length chunk)))
1292 (declare (fixnum last-pos))
1294 (when (= last-pos len) (return))
1295 (let ((pos (or (position #\* chunk :start last-pos) len)))
1296 (if (= pos last-pos)
1298 (error 'namestring-parse-error
1299 :complaint "double asterisk inside of logical ~
1301 :arguments (list chunk)
1302 :namestring namestring
1303 :offset (+ (cdar chunks) pos)))
1304 (pattern (subseq chunk last-pos pos)))
1307 (pattern :multi-char-wild))
1308 (setq last-pos (1+ pos)))))
1311 (make-pattern (pattern))
1312 (let ((x (car (pattern))))
1313 (if (eq x :multi-char-wild)
1317 ;;; Return a list of conses where the CDR is the start position and
1318 ;;; the CAR is a string (token) or character (punctuation.)
1319 (defun logical-chunkify (namestr start end)
1321 (do ((i start (1+ i))
1325 (chunks (cons (nstring-upcase (subseq namestr prev end)) prev))))
1326 (let ((ch (schar namestr i)))
1327 (unless (or (alpha-char-p ch) (digit-char-p ch)
1328 (member ch '(#\- #\*)))
1330 (chunks (cons (nstring-upcase (subseq namestr prev i)) prev)))
1332 (unless (member ch '(#\; #\: #\.))
1333 (error 'namestring-parse-error
1334 :complaint "illegal character for logical pathname:~% ~S"
1335 :arguments (list ch)
1338 (chunks (cons ch i)))))
1341 ;;; Break up a logical-namestring, always a string, into its
1342 ;;; constituent parts.
1343 (defun parse-logical-namestring (namestr start end)
1344 (declare (type simple-base-string namestr)
1345 (type index start end))
1346 (collect ((directory))
1351 (labels ((expecting (what chunks)
1352 (unless (and chunks (simple-string-p (caar chunks)))
1353 (error 'namestring-parse-error
1354 :complaint "expecting ~A, got ~:[nothing~;~S~]."
1355 :arguments (list what (caar chunks) (caar chunks))
1357 :offset (if chunks (cdar chunks) end)))
1359 (parse-host (chunks)
1360 (case (caadr chunks)
1363 (find-logical-host (expecting "a host name" chunks)))
1364 (parse-relative (cddr chunks)))
1366 (parse-relative chunks))))
1367 (parse-relative (chunks)
1370 (directory :relative)
1371 (parse-directory (cdr chunks)))
1373 (directory :absolute) ; Assumption! Maybe revoked later.
1374 (parse-directory chunks))))
1375 (parse-directory (chunks)
1376 (case (caadr chunks)
1379 (let ((res (expecting "a directory name" chunks)))
1380 (cond ((string= res "..") :up)
1381 ((string= res "**") :wild-inferiors)
1383 (maybe-make-logical-pattern namestr chunks)))))
1384 (parse-directory (cddr chunks)))
1386 (parse-name chunks))))
1387 (parse-name (chunks)
1389 (expecting "a file name" chunks)
1390 (setq name (maybe-make-logical-pattern namestr chunks))
1391 (expecting-dot (cdr chunks))))
1392 (expecting-dot (chunks)
1394 (unless (eql (caar chunks) #\.)
1395 (error 'namestring-parse-error
1396 :complaint "expecting a dot, got ~S."
1397 :arguments (list (caar chunks))
1399 :offset (cdar chunks)))
1401 (parse-version (cdr chunks))
1402 (parse-type (cdr chunks)))))
1403 (parse-type (chunks)
1404 (expecting "a file type" chunks)
1405 (setq type (maybe-make-logical-pattern namestr chunks))
1406 (expecting-dot (cdr chunks)))
1407 (parse-version (chunks)
1408 (let ((str (expecting "a positive integer, * or NEWEST"
1411 ((string= str "*") (setq version :wild))
1412 ((string= str "NEWEST") (setq version :newest))
1414 (multiple-value-bind (res pos)
1415 (parse-integer str :junk-allowed t)
1416 (unless (and res (plusp res))
1417 (error 'namestring-parse-error
1418 :complaint "expected a positive integer, ~
1420 :arguments (list str)
1422 :offset (+ pos (cdar chunks))))
1423 (setq version res)))))
1425 (error 'namestring-parse-error
1426 :complaint "extra stuff after end of file name"
1428 :offset (cdadr chunks)))))
1429 (parse-host (logical-chunkify namestr start end)))
1430 (values host :unspecific
1431 (and (not (equal (directory)'(:absolute)))(directory))
1432 name type version))))
1434 ;;; We can't initialize this yet because not all host methods are loaded yet.
1435 (defvar *logical-pathname-defaults*)
1437 (defun logical-pathname (pathspec)
1439 "Converts the pathspec argument to a logical-pathname and returns it."
1440 (declare (type (or logical-pathname string stream) pathspec)
1441 (values logical-pathname))
1442 (if (typep pathspec 'logical-pathname)
1444 (let ((res (parse-namestring pathspec nil *logical-pathname-defaults*)))
1445 (when (eq (%pathname-host res)
1446 (%pathname-host *logical-pathname-defaults*))
1447 (error "This logical namestring does not specify a host:~% ~S"
1451 ;;;; logical pathname unparsing
1453 (defun unparse-logical-directory (pathname)
1454 (declare (type pathname pathname))
1456 (let ((directory (%pathname-directory pathname)))
1458 (ecase (pop directory)
1459 (:absolute) ; nothing special
1460 (:relative (pieces ";")))
1461 (dolist (dir directory)
1462 (cond ((or (stringp dir) (pattern-p dir))
1463 (pieces (unparse-logical-piece dir))
1467 ((eq dir :wild-inferiors)
1470 (error "invalid directory component: ~S" dir))))))
1471 (apply #'concatenate 'simple-string (pieces))))
1473 (defun unparse-logical-piece (thing)
1475 (simple-string thing)
1477 (collect ((strings))
1478 (dolist (piece (pattern-pieces thing))
1480 (simple-string (strings piece))
1482 (cond ((eq piece :wild-inferiors)
1484 ((eq piece :multi-char-wild)
1486 (t (error "invalid keyword: ~S" piece))))))
1487 (apply #'concatenate 'simple-string (strings))))))
1489 ;;; Unparse a logical pathname string.
1490 (defun unparse-enough-namestring (pathname defaults)
1491 (let* ((path-dir (pathname-directory pathname))
1492 (def-dir (pathname-directory defaults))
1494 ;; Go down the directory lists to see what matches. What's
1495 ;; left is what we want, more or less.
1496 (cond ((and (eq (first path-dir) (first def-dir))
1497 (eq (first path-dir) :absolute))
1498 ;; Both paths are :ABSOLUTE, so find where the
1499 ;; common parts end and return what's left
1500 (do* ((p (rest path-dir) (rest p))
1501 (d (rest def-dir) (rest d)))
1502 ((or (endp p) (endp d)
1503 (not (equal (first p) (first d))))
1506 ;; At least one path is :RELATIVE, so just return the
1507 ;; original path. If the original path is :RELATIVE,
1508 ;; then that's the right one. If PATH-DIR is
1509 ;; :ABSOLUTE, we want to return that except when
1510 ;; DEF-DIR is :ABSOLUTE, as handled above. so return
1511 ;; the original directory.
1513 (make-pathname :host (pathname-host pathname)
1514 :directory enough-dir
1515 :name (pathname-name pathname)
1516 :type (pathname-type pathname)
1517 :version (pathname-version pathname))))
1519 (defun unparse-logical-namestring (pathname)
1520 (declare (type logical-pathname pathname))
1521 (concatenate 'simple-string
1522 (logical-host-name (%pathname-host pathname)) ":"
1523 (unparse-logical-directory pathname)
1524 (unparse-unix-file pathname)))
1526 ;;;; logical pathname translations
1528 ;;; Verify that the list of translations consists of lists and prepare
1529 ;;; canonical translations. (Parse pathnames and expand out wildcards
1531 (defun canonicalize-logical-pathname-translations (translation-list host)
1532 (declare (type list translation-list) (type host host)
1534 (mapcar (lambda (translation)
1535 (destructuring-bind (from to) translation
1536 (list (if (typep from 'logical-pathname)
1538 (parse-namestring from host))
1542 (defun logical-pathname-translations (host)
1544 "Return the (logical) host object argument's list of translations."
1545 (declare (type (or string logical-host) host)
1547 (logical-host-translations (find-logical-host host)))
1549 (defun (setf logical-pathname-translations) (translations host)
1551 "Set the translations list for the logical host argument.
1552 Return translations."
1553 (declare (type (or string logical-host) host)
1554 (type list translations)
1556 (let ((host (intern-logical-host host)))
1557 (setf (logical-host-canon-transls host)
1558 (canonicalize-logical-pathname-translations translations host))
1559 (setf (logical-host-translations host) translations)))
1561 (defun translate-logical-pathname (pathname &key)
1563 "Translates pathname to a physical pathname, which is returned."
1564 (declare (type pathname-designator pathname)
1565 (values (or null pathname)))
1568 (dolist (x (logical-host-canon-transls (%pathname-host pathname))
1569 (error 'simple-file-error
1571 :format-control "no translation for ~S"
1572 :format-arguments (list pathname)))
1573 (destructuring-bind (from to) x
1574 (when (pathname-match-p pathname from)
1575 (return (translate-logical-pathname
1576 (translate-pathname pathname from to)))))))
1578 (stream (translate-logical-pathname (pathname pathname)))
1579 (t (translate-logical-pathname (logical-pathname pathname)))))
1581 (defvar *logical-pathname-defaults*
1582 (%make-logical-pathname (make-logical-host :name "BOGUS")
1589 (defun load-logical-pathname-translations (host)
1591 (declare (type string host)
1592 (values (member t nil)))
1593 (if (find-logical-host host nil)
1594 ;; This host is already defined, all is well and good.
1596 ;; ANSI: "The specific nature of the search is
1597 ;; implementation-defined." SBCL: doesn't search at all
1598 (error "logical host ~S not found" host)))