Correctly disassemble method functions.
[sbcl.git] / src / code / target-pathname.lisp
blob4b0b82979d6921d9dad1204f3e229715c8f8293b
1 ;;;; machine/filesystem-independent pathname functions
3 ;;;; This software is part of the SBCL system. See the README file for
4 ;;;; more information.
5 ;;;;
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 (defstruct (logical-host
15 (:copier nil)
16 (:print-object
17 (lambda (logical-host stream)
18 (print-unreadable-object (logical-host stream :type t)
19 (prin1 (logical-host-name logical-host) stream))))
20 (:include host
21 (parse #'parse-logical-namestring)
22 (parse-native
23 (lambda (&rest x)
24 (error "called PARSE-NATIVE-NAMESTRING using a ~
25 logical host: ~S" (first x))))
26 (unparse #'unparse-logical-namestring)
27 (unparse-native
28 (lambda (&rest x)
29 (error "called NATIVE-NAMESTRING using a ~
30 logical host: ~S" (first x))))
31 (unparse-host
32 (lambda (x)
33 (logical-host-name (%pathname-host x))))
34 (unparse-directory #'unparse-logical-directory)
35 (unparse-file #'unparse-logical-file)
36 (unparse-enough #'unparse-enough-namestring)
37 (unparse-directory-separator ";")
38 (simplify-namestring #'identity)
39 (customary-case :upper)))
40 (name-hash 0 :type fixnum)
41 (name "" :type simple-string :read-only t)
42 (translations nil :type list)
43 (canon-transls nil :type list))
45 ;;; Logical pathnames have the following format:
46 ;;;
47 ;;; logical-namestring ::=
48 ;;; [host ":"] [";"] {directory ";"}* [name] ["." type ["." version]]
49 ;;;
50 ;;; host ::= word
51 ;;; directory ::= word | wildcard-word | **
52 ;;; name ::= word | wildcard-word
53 ;;; type ::= word | wildcard-word
54 ;;; version ::= pos-int | newest | NEWEST | *
55 ;;; word ::= {uppercase-letter | digit | -}+
56 ;;; wildcard-word ::= [word] '* {word '*}* [word]
57 ;;; pos-int ::= integer > 0
58 ;;;
59 ;;; Physical pathnames include all these slots and a device slot.
61 ;;; We can't freeze HOST because later on we define either UNIX-HOST or WIN32-HOST.
62 (declaim (freeze-type logical-host))
64 ;;; Utility functions
66 (deftype absent-pathname-component ()
67 '(member nil :unspecific))
69 (defun make-pattern (pieces)
70 ;; Ensure that the hash will meet the SXASH persistence requirement:
71 ;; "2. For any two objects, x and y, both of which are ... pathnames ... and which are similar,
72 ;; (sxhash x) and (sxhash y) yield the same mathematical value even if x and y exist in
73 ;; different Lisp images of the same implementation."
74 ;; Specifically, hashes that depend on object identity (address) are impermissible.
75 (dolist (piece pieces)
76 (aver (typep piece '(or string symbol (cons (eql :character-set) string)))))
77 (%make-pattern (pathname-sxhash pieces) pieces))
79 (declaim (inline pathname-component-present-p))
80 (defun pathname-component-present-p (component)
81 (not (typep component 'absent-pathname-component)))
83 ;;; The following functions are used both for Unix and Windows: while
84 ;;; we accept both \ and / as directory separators on Windows, we
85 ;;; print our own always with /, which is much less confusing what
86 ;;; with being \ needing to be escaped.
87 (defun unparse-physical-directory (pathname escape-char)
88 (declare (pathname pathname))
89 (unparse-physical-directory-list (%pathname-directory pathname) escape-char))
91 (defun unparse-physical-directory-list (directory escape-char)
92 (declare (list directory))
93 (collect ((pieces))
94 (when directory
95 (ecase (pop directory)
96 (:absolute
97 (let ((next (pop directory)))
98 (cond ((eq :home next)
99 (pieces "~"))
100 ((and (consp next) (eq :home (car next)))
101 (pieces "~")
102 (pieces (second next)))
103 ((and (stringp next)
104 (plusp (length next))
105 (char= #\~ (char next 0)))
106 ;; The only place we need to escape the tilde.
107 (pieces "\\")
108 (pieces next))
109 (next
110 (push next directory)))
111 (pieces "/")))
112 (:relative))
113 (dolist (dir directory)
114 (typecase dir
115 ((member :up)
116 (pieces "../"))
117 ((member :back)
118 (error ":BACK cannot be represented in namestrings."))
119 ((member :wild-inferiors)
120 (pieces "**/"))
121 ((or simple-string pattern (member :wild))
122 (pieces (unparse-physical-piece dir escape-char))
123 (pieces "/"))
125 (error "invalid directory component: ~S" dir)))))
126 (apply #'concatenate 'simple-string (pieces))))
128 (defun unparse-physical-file (pathname escape-char)
129 (declare (type pathname pathname))
130 (let ((name (%pathname-name pathname))
131 (type (%pathname-type pathname)))
132 (collect ((fragments))
133 ;; Note: by ANSI 19.3.1.1.5, we ignore the version slot when
134 ;; translating logical pathnames to a filesystem without
135 ;; versions (like Unix and Win32).
136 (when name
137 (when (and (typep name 'string)
138 (string= name ""))
139 (no-namestring-error
140 pathname "the ~S component ~S is of length 0" :name name))
141 (fragments (unparse-physical-piece
142 name escape-char
143 :escape-dot (when (null type) :unless-at-start))))
144 (when (pathname-component-present-p type)
145 (unless name
146 (no-namestring-error
147 pathname
148 "there is a ~S component but no ~S component" :type :name))
149 (fragments ".")
150 (fragments (unparse-physical-piece
151 type escape-char :escape-dot t)))
152 (apply #'concatenate 'simple-string (fragments)))))
154 (defun unparse-native-physical-file (pathname)
155 (let ((name (pathname-name pathname))
156 (type (pathname-type pathname)))
157 (collect ((fragments))
158 (cond
159 ((pathname-component-present-p name)
160 (unless (stringp name) ; some kind of wild field
161 (no-native-namestring-error
162 pathname "of the ~S component ~S." :name name))
163 (fragments name)
164 (when (pathname-component-present-p type)
165 (unless (stringp type) ; some kind of wild field
166 (no-native-namestring-error
167 pathname "of the ~S component ~S" :type type))
168 (fragments ".")
169 (fragments type)))
170 ((pathname-component-present-p type) ; type without a name
171 (no-native-namestring-error
172 pathname
173 "there is a ~S component but no ~S component" :type :name)))
174 (apply #'concatenate 'simple-string (fragments)))))
176 (defun unparse-physical-enough (pathname defaults escape-char)
177 (declare (type pathname pathname defaults))
178 (flet ((lose ()
179 (error "~S cannot be represented relative to ~S."
180 pathname defaults)))
181 (collect ((strings))
182 (let* ((pathname-directory (%pathname-directory pathname))
183 (defaults-directory (%pathname-directory defaults))
184 (prefix-len (length defaults-directory))
185 (result-directory
186 (cond ((null pathname-directory) '(:relative))
187 ((eq (car pathname-directory) :relative)
188 pathname-directory)
189 ((and (> prefix-len 0)
190 (>= (length pathname-directory) prefix-len)
191 (compare-component (subseq pathname-directory
192 0 prefix-len)
193 defaults-directory))
194 ;; Pathname starts with a prefix of default. So
195 ;; just use a relative directory from then on out.
196 (cons :relative (nthcdr prefix-len pathname-directory)))
197 ((eq (car pathname-directory) :absolute)
198 ;; We are an absolute pathname, so we can just use it.
199 pathname-directory)
201 (bug "Bad fallthrough in ~S" 'unparse-physical-enough)))))
202 (strings (unparse-physical-directory-list result-directory escape-char)))
203 (let* ((pathname-type (%pathname-type pathname))
204 (type-needed (pathname-component-present-p pathname-type))
205 (pathname-name (%pathname-name pathname))
206 (name-needed (or type-needed
207 (and pathname-name
208 (not (compare-component pathname-name
209 (%pathname-name
210 defaults)))))))
211 (when name-needed
212 (unless pathname-name (lose))
213 (strings (unparse-physical-piece
214 pathname-name escape-char
215 :escape-dot (when (not pathname-type) :unless-at-start))))
216 (when type-needed
217 (unless (pathname-component-present-p pathname-type)
218 (lose))
219 (strings ".")
220 (strings (unparse-physical-piece pathname-type
221 escape-char :escape-dot t))))
222 (apply #'concatenate 'simple-string (strings)))))
225 ;;; To be initialized in unix/win32-pathname.lisp
226 (define-load-time-global *physical-host* nil)
228 ;;; Return a value suitable, e.g., for preinitializing
229 ;;; *DEFAULT-PATHNAME-DEFAULTS* before *DEFAULT-PATHNAME-DEFAULTS* is
230 ;;; initialized (at which time we can't safely call e.g. #'PATHNAME).
231 (defun make-trivial-default-pathname ()
232 (intern-pathname *physical-host* nil nil nil nil :newest))
234 ;;; pathname methods
236 (defmethod print-object ((pathname pathname) stream)
237 (let ((namestring (handler-case (namestring pathname)
238 (error nil))))
239 (if namestring
240 (format stream
241 (if (or *print-readably* *print-escape*)
242 "#P~S"
243 "~A")
244 (coerce namestring '(simple-array character (*))))
245 (print-unreadable-object (pathname stream :type t)
246 (format stream
247 "~@<(with no namestring) ~_:HOST ~S ~_:DEVICE ~S ~_:DIRECTORY ~S ~
248 ~_:NAME ~S ~_:TYPE ~S ~_:VERSION ~S~:>"
249 (%pathname-host pathname)
250 (%pathname-device pathname)
251 (%pathname-directory pathname)
252 (%pathname-name pathname)
253 (%pathname-type pathname)
254 (%pathname-version pathname))))))
256 ;;; Pathnames are stored in an open-addressing weak hash-set.
257 ;;; Ideally there would be only one internal representation of any pathname,
258 ;;; so that EQUAL on pathnames could reduce to EQ.
259 ;;; I'm not sure that's possible. For the time being, we use a comparator
260 ;;; that is stricter (I think) than EQUAL.
262 ;;; The spec is actually extremely underspecified in regard to the meaning of
263 ;;; "pathnames that are equal should be functionally equivalent."
264 ;;; The simple test:
265 ;;; (equal (make-pathname :name "a" :version nil) (make-pathname :name "a" :version :newest))
266 ;;; shows that EQUAL is inconsistent in terms of what "functionally equivalent" means:
267 ;;; SBCL, ABCL, and CCL => T
268 ;;; CLISP and ECL => NIL
269 ;;; Also, on case-sensitive-case-preserving filesystems it's not possible
270 ;;; to know which pathnames are equivalent without asking the filesystem.
272 ;;: TODO: consider similarly interning the DEVICE and TYPE parts
273 (define-load-time-global *pn-dir-table* nil)
274 (define-load-time-global *pn-table* nil)
275 (declaim (type robinhood-hashset *pn-dir-table* *pn-table*))
277 (defmacro compare-pathname-host/dev/dir/name/type (a b)
278 `(and (eq (%pathname-host ,a) (%pathname-host ,b)) ; Interned
279 ;; dir+hash are EQ-comparable thanks to INTERN-PATHNAME
280 (eq (%pathname-dir+hash ,a) (%pathname-dir+hash ,b))
281 ;; the pathname pieces which are strings aren't interned
282 (compare-component (%pathname-device ,a) (%pathname-device ,b))
283 (compare-component (%pathname-name ,a) (%pathname-name ,b))
284 (compare-component (%pathname-type ,a) (%pathname-type ,b))))
286 (defun pn-table-dir= (entry key)
287 (or (eq (car entry) (car key)) ; quick win if lists are EQ
288 (and (eq (cdr entry) (cdr key)) ; hashes match
289 (compare-component (car entry) (car key)))))
290 (defun pn-table-hash (pathname)
291 ;; The pathname table makes distinctions between pathnames that EQUAL does not.
292 (mix (sxhash (%pathname-version pathname))
293 (pathname-sxhash pathname)))
294 (defun pn-table-pn= (entry key)
295 (and (compare-pathname-host/dev/dir/name/type entry key)
296 (eql (%pathname-version entry) (%pathname-version key))))
298 (defun !pathname-cold-init ()
299 (setq *pn-dir-table* (make-hashset 32 #'pn-table-dir= #'cdr
300 :synchronized t :weakness t)
301 *pn-table* (make-hashset 32 #'pn-table-pn= #'pn-table-hash
302 :synchronized t :weakness t)))
304 ;;; A pathname is logical if the host component is a logical host.
305 ;;; This constructor is used to make an instance of the correct type
306 ;;; from parsed arguments.
307 (defun intern-pathname (host device directory name type version)
308 ;; We canonicalize logical pathname components to uppercase. ANSI
309 ;; doesn't strictly require this, leaving it up to the implementor;
310 ;; but the arguments given in the X3J13 cleanup issue
311 ;; PATHNAME-LOGICAL:ADD seem compelling: we should canonicalize the
312 ;; case, and uppercase is the ordinary way to do that.
313 (declare (sb-c::tlab :system))
314 (flet ((upcase-maybe (x) (typecase x (string (logical-word-or-lose x)) (t x))))
315 (when (typep host 'logical-host)
316 (setq device :unspecific
317 directory (mapcar #'upcase-maybe directory)
318 name (upcase-maybe name)
319 type (upcase-maybe type))))
320 (dx-let ((dir-key (cons directory (pathname-sxhash directory))))
321 (declare (inline !allocate-pathname)) ; for DX-allocation
322 (flet ((ensure-heap-string (part) ; return any non-string as-is
323 ;; FIXME: what about pattern pieces and (:HOME "user") ?
324 (cond ((or (not (stringp part)) (read-only-space-obj-p part)) part)
325 ;; Altering a string that is any piece of any arg to INTERN-PATHNAME
326 ;; is a surefire way to corrupt the hashset, so *always* copy the input.
327 ;; Users might not have reason to believe that once a string is passed
328 ;; to any pathname function, it is immutable. We can only hope that
329 ;; they don't mutate strings returned by pathname accessors.
330 (t (let ((l (length part)))
331 (logically-readonlyize
332 (replace (typecase part
333 (base-string (make-string l :element-type 'base-char))
334 (t (make-string l)))
335 part)))))))
336 (let* ((dir+hash
337 (if directory ; find the interned dir-key
338 (hashset-insert-if-absent
339 *pn-dir-table* dir-key
340 (lambda (dir)
341 (cons (mapcar #'ensure-heap-string (car dir)) (cdr dir))))))
342 (pn-key (!allocate-pathname host device dir+hash name type version)))
343 (declare (dynamic-extent pn-key))
344 (hashset-insert-if-absent
345 *pn-table* pn-key
346 (lambda (tmp &aux (host (%pathname-host tmp)))
347 (let ((new (!allocate-pathname
348 host (%pathname-device tmp)
349 (%pathname-dir+hash tmp)
350 (ensure-heap-string (%pathname-name tmp))
351 (ensure-heap-string (%pathname-type tmp))
352 (%pathname-version tmp))))
353 (when (typep host 'logical-host)
354 (setf (%instance-layout new) #.(find-layout 'logical-pathname)))
355 new)))))))
357 ;;; Weak vectors don't work at all once rendered pseudo-static.
358 ;;; so in order to weaken the pathname cache, the vectors are copied on restart.
359 ;;; It may not achieve anything for saved pathnames, since the vector elements
360 ;;; are themselves pseudo-static, but at least newly made ones aren't immortal.
361 (defun rebuild-pathname-cache ()
362 (hashset-rehash *pn-dir-table* nil)
363 (hashset-rehash *pn-table* nil))
365 (defun show-pn-cache (&aux (*print-pretty* nil) (*package* (find-package "CL-USER")))
366 (dolist (symbol '(*pn-dir-table* *pn-table*))
367 (let* ((hashset (symbol-value symbol))
368 (v (hss-cells (hashset-storage hashset)))
369 (n (hs-cells-capacity v)))
370 (multiple-value-bind (live tombstones unused) (hs-cells-occupancy v n)
371 (declare (ignore live))
372 (format t "~&~S: size=~D tombstones=~D unused=~D~%" symbol n tombstones unused))
373 (dotimes (i n)
374 (let ((entry (hs-cell-ref v i)))
375 (unless (member entry '(nil 0))
376 (format t "~4d ~3d ~x " i (generation-of entry) (get-lisp-obj-address entry))
377 (if (eq symbol '*pn-dir-table*)
378 (format t "~16x ~s~%" (cdr entry) (car entry))
379 (flet ((index-of (pn-dir)
380 (let ((dirs (hss-cells (hashset-storage *pn-dir-table*))))
381 (dotimes (i (weak-vector-len dirs))
382 (when (eq pn-dir (weak-vector-ref dirs i))
383 (return i))))))
384 (format t
385 "~16x [~A ~S ~A ~S ~S ~S]~%"
386 (pathname-sxhash entry)
387 (let ((host (%pathname-host entry)))
388 (cond ((logical-host-p host)
389 ;; display with string quotes around name
390 (prin1-to-string (logical-host-name host)))
391 ((eq host *physical-host*) "phys")
392 (t host)))
393 (%pathname-device entry)
394 (acond ((%pathname-dir+hash entry) (format nil "@~D" (index-of it)))
395 (t "-"))
396 (%pathname-name entry)
397 (%pathname-type entry)
398 (%pathname-version entry))))))))))
400 ;;; Vector of logical host objects, each of which contains its translations.
401 ;;; The vector is never mutated- always a new vector is created when adding
402 ;;; translations for a new host. So nothing needs locking.
403 ;;; And the fact that hosts are never deleted keeps things really simple.
404 (define-load-time-global *logical-hosts* #())
405 (declaim (simple-vector *logical-hosts*))
407 ;;;; patterns
409 (defmethod print-object ((pattern pattern) stream)
410 (print-unreadable-object (pattern stream :type t)
411 (if *print-pretty*
412 (let ((*print-escape* t))
413 (pprint-fill stream (pattern-pieces pattern) nil))
414 (prin1 (pattern-pieces pattern) stream))))
416 (defun pattern= (pattern1 pattern2)
417 (declare (type pattern pattern1 pattern2))
418 (let ((pieces1 (pattern-pieces pattern1))
419 (pieces2 (pattern-pieces pattern2)))
420 (and (= (length pieces1) (length pieces2))
421 (every (lambda (piece1 piece2)
422 (typecase piece1
423 (simple-string
424 (and (simple-string-p piece2)
425 (string= piece1 piece2)))
426 (cons
427 (and (consp piece2)
428 (eq (car piece1) (car piece2))
429 (string= (cdr piece1) (cdr piece2))))
431 (eq piece1 piece2))))
432 pieces1
433 pieces2))))
435 ;;; If the string matches the pattern returns the multiple values T
436 ;;; and a list of the matched strings.
437 (defun pattern-matches (pattern string)
438 (declare (type pattern pattern)
439 (type simple-string string))
440 (let ((len (length string)))
441 (labels ((maybe-prepend (subs cur-sub chars)
442 (if cur-sub
443 (let* ((len (length chars))
444 (new (make-string len))
445 (index len))
446 (dolist (char chars)
447 (setf (schar new (decf index)) char))
448 (cons new subs))
449 subs))
450 (matches (pieces start subs cur-sub chars)
451 (if (null pieces)
452 (if (= start len)
453 (values t (maybe-prepend subs cur-sub chars))
454 (values nil nil))
455 (let ((piece (car pieces)))
456 (etypecase piece
457 (simple-string
458 (let ((end (+ start (length piece))))
459 (and (<= end len)
460 (string= piece string
461 :start2 start :end2 end)
462 (matches (cdr pieces) end
463 (maybe-prepend subs cur-sub chars)
464 nil nil))))
465 (list
466 (ecase (car piece)
467 (:character-set
468 (and (< start len)
469 (let ((char (schar string start)))
470 (if (find char (cdr piece) :test #'char=)
471 (matches (cdr pieces) (1+ start) subs t
472 (cons char chars))))))))
473 ((member :single-char-wild)
474 (and (< start len)
475 (matches (cdr pieces) (1+ start) subs t
476 (cons (schar string start) chars))))
477 ((member :multi-char-wild)
478 (multiple-value-bind (won new-subs)
479 (matches (cdr pieces) start subs t chars)
480 (if won
481 (values t new-subs)
482 (and (< start len)
483 (matches pieces (1+ start) subs t
484 (cons (schar string start)
485 chars)))))))))))
486 (multiple-value-bind (won subs)
487 (matches (pattern-pieces pattern) 0 nil nil nil)
488 (values won (reverse subs))))))
490 ;;; PATHNAME-MATCH-P for directory components
491 (defun directory-components-match (thing wild)
492 (or (eq thing wild)
493 (eq wild :wild)
494 ;; If THING has a null directory, assume that it matches
495 ;; (:ABSOLUTE :WILD-INFERIORS) or (:RELATIVE :WILD-INFERIORS).
496 (and (consp wild)
497 (null thing)
498 (member (first wild) '(:absolute :relative))
499 (eq (second wild) :wild-inferiors))
500 (and (consp wild)
501 (let ((wild1 (first wild)))
502 (if (eq wild1 :wild-inferiors)
503 (let ((wild-subdirs (rest wild)))
504 (or (null wild-subdirs)
505 (loop
506 (when (directory-components-match thing wild-subdirs)
507 (return t))
508 (pop thing)
509 (unless thing (return nil)))))
510 (and (consp thing)
511 (components-match (first thing) wild1)
512 (directory-components-match (rest thing)
513 (rest wild))))))))
515 ;;; Return true if pathname component THING is matched by WILD. (not
516 ;;; commutative)
517 (defun components-match (thing wild)
518 (declare (type (or pattern symbol simple-string integer) thing wild))
519 (or (eq thing wild)
520 (eq wild :wild)
521 (typecase thing
522 (simple-string
523 ;; String is matched by itself, a matching pattern or :WILD.
524 (typecase wild
525 (pattern
526 (values (pattern-matches wild thing)))
527 (simple-string
528 (string= thing wild))))
529 (pattern
530 ;; A pattern is only matched by an identical pattern.
531 (and (pattern-p wild) (pattern= thing wild)))
532 (bignum
533 ;; An integer (version number) is matched by :WILD or the
534 ;; same integer. This branch will actually always be NIL as
535 ;; long as the version is a fixnum.
536 (eql thing wild)))))
538 ;;; a predicate for comparing two pathname slot component sub-entries
539 (defun compare-component (this that)
540 (or (eq this that)
541 (typecase this
542 (simple-string
543 (and (simple-string-p that)
544 (string= this that)))
545 (pattern
546 ;; PATTERN instances should probably become interned objects
547 ;; so that we can use EQ on them.
548 (and (pattern-p that)
549 (pattern= this that)))
550 (cons
551 ;; Even though directory parts are now reliably interned -
552 ;; and so you might be inclined to think that the "full" comparison
553 ;; could be confined to just the interning operation, that's not so,
554 ;; because we also use COMPARE-COMPONENT in ENOUGH-NAMESTRING.
555 (and (consp that)
556 (compare-component (car this) (car that))
557 (compare-component (cdr this) (cdr that))))
558 (bignum
559 (eql this that)))))
561 ;;;; pathname functions
563 (defun pathname= (a b)
564 (declare (type pathname a b))
565 (or (eq a b)
566 (and (compare-pathname-host/dev/dir/name/type a b)
567 (or (eq (%pathname-host a) *physical-host*)
568 (compare-component (pathname-version a)
569 (pathname-version b))))))
571 (sb-kernel::assign-equalp-impl 'pathname #'pathname=)
572 (sb-kernel::assign-equalp-impl 'logical-pathname #'pathname=)
574 ;;; Hash a PATHNAME or a PATHNAME-DIRECTORY or pieces of a PATTERN.
575 ;;; This is called by both SXHASH and by the interning of pathnames, which uses a
576 ;;; multi-step approaching to coalescing shared subparts.
577 ;;; If an EQUAL directory was used before, we share that.
578 ;;; Since a directory is stored with its hash precomputed, hashing a PATHNAME as a
579 ;;; whole entails at most 4 more MIX operations. So using pathnames as keys in
580 ;;; a hash-table pays a small up-front price for later speed improvement.
581 (defun pathname-sxhash (x)
582 (labels
583 ((hash-piece (piece)
584 (etypecase piece
585 (string
586 (let ((res (length piece)))
587 (if (<= res 6) ; hash it more thoroughly than (SXHASH string)
588 (dovector (ch piece res)
589 (setf res (mix (murmur-hash-word/+fixnum (char-code ch)) res)))
590 (sxhash piece))))
591 (symbol (symbol-name-hash piece))
592 (pattern (pattern-hash piece))
593 ;; next case is only for MAKE-PATTERN
594 ((cons (eql :character-set)) (hash-piece (the string (cdr piece))))
595 ((cons (eql :home) (cons string null))
596 ;; :HOME has two representations- one is just '(:absolute :home ...)
597 ;; and the other '(:absolute (:home "user") ...)
598 (sxhash (second piece))))))
599 (etypecase x
600 (pathname
601 (let* ((host (%pathname-host x))
602 ;; NAME-HASH is based on SXHASH of a string
603 (hash (if (typep host 'logical-host) (logical-host-name-hash host) 0)))
604 (mixf hash (hash-piece (%pathname-device x))) ; surely stringlike, right?
605 (awhen (%pathname-dir+hash x) (mixf hash (cdr it)))
606 (mixf hash (hash-piece (%pathname-name x)))
607 (mixf hash (hash-piece (%pathname-type x)))
608 ;; The requirement NOT to mix the version into the resulting hash is mandated
609 ;; by bullet point 1 in the SXHASH specification:
610 ;; (equal x y) implies (= (sxhash x) (sxhash y))
611 ;; and the observation that in this implementation of Lisp:
612 ;; (equal (make-pathname :version 1) (make-pathname :version 15)) => T
613 hash))
614 (list ;; a directory, or the PIECES argument to MAKE-PATTERN
615 (let ((hash 0))
616 (dolist (piece x hash)
617 (mixf hash (hash-piece piece))))))))
619 ;;; Convert PATHNAME-DESIGNATOR (a pathname, or string, or
620 ;;; stream), into a pathname in PATHNAME.
621 (defmacro with-pathname ((pathname pathname-designator) &body body)
622 (once-only ((pathname-designator pathname-designator))
623 `(let ((,pathname (etypecase ,pathname-designator
624 (pathname ,pathname-designator)
625 (string (parse-namestring ,pathname-designator))
626 ((or file-stream synonym-stream)
627 (stream-file-name-or-lose ,pathname-designator)))))
628 ,@body)))
630 (defmacro with-native-pathname ((pathname pathname-designator) &body body)
631 (once-only ((pathname-designator pathname-designator))
632 `(let ((,pathname (etypecase ,pathname-designator
633 (pathname ,pathname-designator)
634 (string (parse-native-namestring ,pathname-designator))
635 ;; FIXME
636 #+nil
637 (file-stream (file-name ,pathname-designator)))))
638 ,@body)))
640 (defmacro with-host ((host host-designator) &body body)
641 ;; Generally, redundant specification of information in software,
642 ;; whether in code or in comments, is bad. However, the ANSI spec
643 ;; for this is messy enough that it's hard to hold in short-term
644 ;; memory, so I've recorded these redundant notes on the
645 ;; implications of the ANSI spec.
647 ;; According to the ANSI spec, HOST can be a valid pathname host, or
648 ;; a logical host, or NIL.
650 ;; A valid pathname host can be a valid physical pathname host or a
651 ;; valid logical pathname host.
653 ;; A valid physical pathname host is "any of a string, a list of
654 ;; strings, or the symbol :UNSPECIFIC, that is recognized by the
655 ;; implementation as the name of a host". In SBCL as of 0.6.9.8,
656 ;; that means :UNSPECIFIC: though someday we might want to
657 ;; generalize it to allow strings like "RTFM.MIT.EDU" or lists like
658 ;; '("RTFM" "MIT" "EDU"), that's not supported now.
660 ;; A valid logical pathname host is a string which has been defined as
661 ;; the name of a logical host, as with LOAD-LOGICAL-PATHNAME-TRANSLATIONS.
663 ;; A logical host is an object of implementation-dependent nature. In
664 ;; SBCL, it's a member of the HOST class (a subclass of STRUCTURE-OBJECT).
665 (once-only ((host-designator host-designator))
666 `(let ((,host (etypecase ,host-designator
667 ((string 0)
668 ;; This is a special host. It's not valid as a
669 ;; logical host, so it is a sensible thing to
670 ;; designate the physical host object. So we do
671 ;; that.
672 *physical-host*)
673 (string
674 ;; In general ANSI-compliant Common Lisps, a
675 ;; string might also be a physical pathname
676 ;; host, but ANSI leaves this up to the
677 ;; implementor, and in SBCL we don't do it, so
678 ;; it must be a logical host.
679 (find-logical-host ,host-designator))
680 (absent-pathname-component
681 ;; CLHS says that HOST=:UNSPECIFIC has
682 ;; implementation-defined behavior. We
683 ;; just turn it into NIL.
684 nil)
685 (list
686 ;; ANSI also allows LISTs to designate hosts,
687 ;; but leaves its interpretation
688 ;; implementation-defined. Our interpretation
689 ;; is that it's unsupported.:-|
690 (error "A LIST representing a pathname host is not ~
691 supported in this implementation:~% ~S"
692 ,host-designator))
693 (host ,host-designator))))
694 ,@body)))
696 (defun find-host (host-designator &optional (errorp t))
697 (with-host (host host-designator)
698 (when (and errorp (not host))
699 (error "Couldn't find host: ~S" host-designator))
700 host))
702 (defun pathname (pathspec)
703 "Convert PATHSPEC (a pathname designator) into a pathname."
704 (declare (type pathname-designator pathspec))
705 (with-pathname (pathname pathspec)
706 pathname))
708 (defun native-pathname (pathspec)
709 "Convert PATHSPEC (a pathname designator) into a pathname, assuming
710 the operating system native pathname conventions."
711 (with-native-pathname (pathname pathspec)
712 pathname))
714 ;;; Recursively (e.g. for the directory component) change the case of
715 ;;; the pathname component THING.
716 (declaim (ftype (sfunction ((or symbol integer string pattern list))
717 (or symbol integer string pattern list))
718 diddle-case))
719 (defun diddle-case (thing)
720 (labels ((check-for (pred in)
721 (typecase in
722 (pattern
723 (some (lambda (piece)
724 (typecase piece
725 (simple-string
726 (check-for pred piece))
727 ((cons (eql :character-set))
728 (check-for pred (cdr piece)))))
729 (pattern-pieces in)))
730 (simple-string
731 (some pred in))))
732 (diddle-with (fun thing)
733 (typecase thing
734 (pattern
735 (make-pattern
736 (mapcar (lambda (piece)
737 (typecase piece
738 (simple-string
739 (funcall fun piece))
740 ((cons (eql :character-set))
741 (funcall fun (cdr piece)))
743 piece)))
744 (pattern-pieces thing))))
745 (simple-string
746 (funcall fun thing))
748 thing)))
749 (maybe-diddle-part (thing)
750 (if (listp thing)
751 (mapcar #'maybe-diddle-part thing)
752 (let ((any-uppers (check-for #'upper-case-p thing))
753 (any-lowers (check-for #'lower-case-p thing)))
754 (cond ((and any-uppers any-lowers) ; mixed case, stays the same
755 thing)
756 (any-uppers ; all uppercase, becomes all lower case
757 (diddle-with 'string-downcase thing))
758 (any-lowers ; all lowercase, becomes all upper case
759 (diddle-with 'string-upcase thing))
760 (t ; no letters? I guess just leave it.
761 thing))))))
762 (if (not (or (symbolp thing) (integerp thing)))
763 (maybe-diddle-part thing)
764 thing)))
766 (declaim (inline maybe-diddle-case))
767 (defun maybe-diddle-case (thing diddle-p)
768 (if diddle-p
769 (diddle-case thing)
770 thing))
772 (defun merge-directories (dir1 dir2 diddle-case)
773 (if (or (eq (car dir1) :absolute)
774 (null dir2))
775 dir1
776 (let ((results nil))
777 (flet ((add (dir)
778 (if (and (eq dir :back)
779 results
780 (typep (car results) '(or string pattern
781 (member :wild :wild-inferiors))))
782 (pop results)
783 (push dir results))))
784 (dolist (dir (maybe-diddle-case dir2 diddle-case))
785 (add dir))
786 (dolist (dir (cdr dir1))
787 (add dir)))
788 (reverse results))))
790 (defun merge-pathnames (pathname
791 &optional
792 (defaults *default-pathname-defaults*)
793 (default-version :newest))
794 "Construct a filled in pathname by completing the unspecified components
795 from the defaults."
796 (declare (type pathname-designator pathname)
797 (type pathname-designator defaults)
798 (values pathname))
799 (with-pathname (defaults defaults)
800 (let* ((pathname (let ((*default-pathname-defaults* defaults))
801 (pathname pathname)))
802 (default-host (%pathname-host defaults))
803 (pathname-host (%pathname-host pathname))
804 (diddle-case
805 (and default-host pathname-host
806 (not (eq (host-customary-case default-host)
807 (host-customary-case pathname-host)))))
808 (directory (merge-directories (%pathname-directory pathname)
809 (%pathname-directory defaults)
810 diddle-case)))
811 (macrolet ((merged-component (component)
812 `(or (,component pathname)
813 (let ((default (,component defaults)))
814 (if diddle-case
815 (diddle-case default)
816 default)))))
817 (intern-pathname
818 (or pathname-host default-host)
819 ;; The device of ~/ shouldn't be merged, because the
820 ;; expansion may have a different device
821 (unless (typep directory '(cons (eql :absolute) (cons (eql :home))))
822 (merged-component %pathname-device))
823 directory
824 (merged-component %pathname-name)
825 (merged-component %pathname-type)
826 (or (%pathname-version pathname)
827 (and (not (%pathname-name pathname)) (%pathname-version defaults))
828 default-version))))))
830 (defun import-directory (directory diddle-case)
831 (etypecase directory
832 (null nil)
833 ((member :wild) '(:absolute :wild-inferiors))
834 ((member :unspecific) '(:relative))
835 (list
836 (let ((root (pop directory))
837 results)
838 (if (member root '(:relative :absolute))
839 (push root results)
840 (error "List of directory components must start with ~S or ~S."
841 :absolute :relative))
842 (when directory
843 (let ((next (car directory)))
844 (when (or (eq :home next)
845 (typep next '(cons (eql :home) (cons string null))))
846 (push (pop directory) results)))
847 (dolist (piece directory)
848 (typecase piece
849 ((member :wild :wild-inferiors :up)
850 (push piece results))
851 ((member :back)
852 (if (typep (car results) '(or string pattern
853 (member :wild :wild-inferiors)))
854 (pop results)
855 (push piece results)))
856 ((or string pattern)
857 (when (typep piece '(and string (not simple-array)))
858 (setq piece (coerce piece 'simple-string)))
859 ;; Unix namestrings allow embedded "//" within them. Consecutive
860 ;; slashes are treated as one, which is weird but often convenient.
861 ;; However, preserving empty directory components:
862 ;; - is unaesthetic
863 ;; - makes (NAMESTRING (MAKE-PATHNAME :DIRECTORY '(:RELATIVE "" "d")))
864 ;; visually indistinguishable from the absolute pathname "/d/"
865 ;; - can causes a pathname equality test to return NIL
866 ;; on semantically equivalent pathnames. This can happen for
867 ;; other reasons, but fewer false negatives is better.
868 (unless (and (stringp piece) (zerop (length piece)))
869 (push (maybe-diddle-case piece diddle-case) results)))
871 (error "~S is not allowed as a directory component." piece)))))
872 (nreverse results)))
873 (string
874 (cond ((zerop (length directory)) `(:absolute))
876 (when (typep directory '(not simple-array))
877 (setq directory (coerce directory 'simple-string)))
878 `(:absolute ,(maybe-diddle-case directory diddle-case)))))))
880 (defun make-pathname (&key host
881 (device nil devp)
882 (directory nil dirp)
883 (name nil namep)
884 (type nil typep)
885 (version nil versionp)
886 defaults
887 (case :local))
888 "Makes a new pathname from the component arguments. Note that host is
889 a host-structure or string."
890 (declare (type (or string host pathname-component-tokens) host)
891 (type (or string pathname-component-tokens) device)
892 (type (or list string pattern pathname-component-tokens) directory)
893 (type (or string pattern pathname-component-tokens) name type)
894 (type (or integer pathname-component-tokens (member :newest))
895 version)
896 (type (or pathname-designator null) defaults)
897 (type pathname-component-case case))
898 (let* ((defaults (when defaults
899 (with-pathname (defaults defaults) defaults)))
900 (default-host (if defaults
901 (%pathname-host defaults)
902 (pathname-host *default-pathname-defaults*)))
903 ;; Raymond Toy writes: CLHS says make-pathname can take a
904 ;; string (as a logical-host) for the host part. We map that
905 ;; string into the corresponding logical host structure.
907 ;; Paul Werkowski writes:
908 ;; HyperSpec says for the arg to MAKE-PATHNAME;
909 ;; "host---a valid physical pathname host. ..."
910 ;; where it probably means -- a valid pathname host.
911 ;; "valid pathname host n. a valid physical pathname host or
912 ;; a valid logical pathname host."
913 ;; and defines
914 ;; "valid physical pathname host n. any of a string,
915 ;; a list of strings, or the symbol :unspecific,
916 ;; that is recognized by the implementation as the name of a host."
917 ;; "valid logical pathname host n. a string that has been defined
918 ;; as the name of a logical host. ..."
919 ;; HS is silent on what happens if the :HOST arg is NOT one of these.
920 ;; It seems an error message is appropriate.
921 (host (or (find-host host nil) default-host))
922 (diddle-args (and (eq (host-customary-case host) :lower)
923 (eq case :common)))
924 (diddle-defaults
925 (not (eq (host-customary-case host)
926 (host-customary-case default-host))))
927 (dir (import-directory directory diddle-args))
928 (ver (cond
929 (versionp version)
930 (defaults (%pathname-version defaults))
931 (t nil))))
932 (when (and defaults (not dirp))
933 (setf dir
934 (merge-directories dir
935 (%pathname-directory defaults)
936 diddle-defaults)))
938 (macrolet ((pick (var varp field)
939 `(cond ((or (simple-string-p ,var)
940 (pattern-p ,var))
941 (maybe-diddle-case ,var diddle-args))
942 ((stringp ,var)
943 (maybe-diddle-case (coerce ,var 'simple-string)
944 diddle-args))
945 (,varp
946 (maybe-diddle-case ,var diddle-args))
947 (defaults
948 (maybe-diddle-case (,field defaults)
949 diddle-defaults))
951 nil))))
952 (intern-pathname
953 host
954 (pick device devp %pathname-device) ; forced to :UNSPECIFIC when logical
956 (pick name namep %pathname-name)
957 (pick type typep %pathname-type)
958 ver))))
960 (defun pathname-host (pathname &key (case :local))
961 "Return PATHNAME's host."
962 (declare (ignore case))
963 (with-pathname (pathname pathname)
964 (%pathname-host pathname)))
966 (macrolet ((frob (name component docstring)
967 `(defun ,name (pathname &key (case :local))
968 ,docstring
969 (with-pathname (pathname pathname)
970 (let ((effective-case (and (eq case :common)
971 (eq (host-customary-case
972 (%pathname-host pathname))
973 :lower))))
974 (maybe-diddle-case (,component pathname) effective-case))))))
976 (frob pathname-device %pathname-device "Return PATHNAME's device.")
977 (frob pathname-directory %pathname-directory "Return PATHNAME's directory.")
978 (frob pathname-name %pathname-name "Return PATHNAME's name.")
979 (frob pathname-type %pathname-type "Return PATHNAME's type."))
981 (defun pathname-version (pathname)
982 "Return PATHNAME's version."
983 (with-pathname (pathname pathname)
984 (%pathname-version pathname)))
986 ;;;; namestrings
988 ;;; Handle the case for PARSE-NAMESTRING parsing a potentially
989 ;;; syntactically valid logical namestring with an explicit host.
991 ;;; This then isn't fully general -- we are relying on the fact that
992 ;;; we will only pass to parse-namestring namestring with an explicit
993 ;;; logical host, so that we can pass the host return from
994 ;;; parse-logical-namestring through to %PARSE-NAMESTRING as a truth
995 ;;; value. Yeah, this is probably a KLUDGE - CSR, 2002-04-18
996 (defun parseable-logical-namestring-p (namestr start end)
997 (and (parse-potential-logical-host namestr start end)
998 (handler-case
999 (let ((result (parse-logical-namestring namestr start end)))
1000 ;; if we got this far, we should have an explicit host
1001 ;; (first return value of parse-logical-namestring)
1002 (aver result)
1003 result)
1004 ((or simple-type-error namestring-parse-error) ()
1005 nil))))
1007 (defun parse-potential-logical-host (namestr &optional (start 0) end)
1008 (handler-case
1009 (let ((colon (position #\: namestr :start start :end end)))
1010 (when colon
1011 (let ((potential-host
1012 (logical-word-or-lose (subseq namestr start colon))))
1013 (values potential-host colon))))
1014 (namestring-parse-error () nil)))
1016 ;;; Handle the case where PARSE-NAMESTRING is actually parsing a
1017 ;;; namestring. We pick off the :JUNK-ALLOWED case then find a host to
1018 ;;; use for parsing, call the parser, then check whether the host matches.
1019 (defun %parse-namestring (namestr host defaults start end junk-allowed)
1020 (declare (type (or host null) host)
1021 (type string namestr)
1022 (type index start)
1023 (type (or index null) end))
1024 (cond
1025 (junk-allowed
1026 (handler-case
1027 (%parse-namestring namestr host defaults start end nil)
1028 (namestring-parse-error (condition)
1029 (values nil (namestring-parse-error-offset condition)))))
1031 (let ((end (%check-vector-sequence-bounds namestr start end)))
1032 (multiple-value-bind (new-host device directory file type version)
1033 ;; Comments below are quotes from the HyperSpec
1034 ;; PARSE-NAMESTRING entry, reproduced here to demonstrate
1035 ;; that we actually have to do things this way rather than
1036 ;; some possibly more logical way. - CSR, 2002-04-18
1037 (cond
1038 ;; "If host is a logical host then thing is parsed as a
1039 ;; logical pathname namestring on the host."
1040 (host (funcall (host-parse host) namestr start end))
1041 ;; "If host is nil and thing is a syntactically valid
1042 ;; logical pathname namestring containing an explicit
1043 ;; host, then it is parsed as a logical pathname
1044 ;; namestring."
1045 ((parseable-logical-namestring-p namestr start end)
1046 (parse-logical-namestring namestr start end))
1047 ;; "If host is nil, default-pathname is a logical
1048 ;; pathname, and thing is a syntactically valid logical
1049 ;; pathname namestring without an explicit host, then it
1050 ;; is parsed as a logical pathname namestring on the
1051 ;; host that is the host component of default-pathname."
1053 ;; "Otherwise, the parsing of thing is
1054 ;; implementation-defined."
1056 ;; Both clauses are handled here, as the default
1057 ;; *DEFAULT-PATHNAME-DEFAULTS* has a SB-IMPL::UNIX-HOST
1058 ;; for a host.
1059 ((pathname-host defaults)
1060 (funcall (host-parse (pathname-host defaults))
1061 namestr
1062 start
1063 end))
1064 ;; I don't think we should ever get here, as the default
1065 ;; host will always have a non-null HOST, given that we
1066 ;; can't create a new pathname without going through
1067 ;; *DEFAULT-PATHNAME-DEFAULTS*, which has a non-null
1068 ;; host...
1069 (t (bug "Fallen through COND in %PARSE-NAMESTRING")))
1070 (when (and host new-host (not (eq new-host host)))
1071 (error 'simple-type-error
1072 :datum new-host
1073 ;; Note: ANSI requires that this be a TYPE-ERROR,
1074 ;; but there seems to be no completely correct
1075 ;; value to use for TYPE-ERROR-EXPECTED-TYPE.
1076 ;; Instead, we return a sort of "type error allowed
1077 ;; type", trying to say "it would be OK if you
1078 ;; passed NIL as the host value" but not mentioning
1079 ;; that a matching string would be OK too.
1080 :expected-type 'null
1081 :format-control
1082 "The host in the namestring, ~S,~@
1083 does not match the explicit HOST argument, ~S."
1084 :format-arguments (list new-host host)))
1085 (let ((pn-host (or new-host host (pathname-host defaults))))
1086 (values (intern-pathname pn-host device directory file type version)
1087 end)))))))
1089 (defun parse-namestring (thing
1090 &optional
1091 host
1092 (defaults *default-pathname-defaults*)
1093 &key (start 0) end junk-allowed)
1094 (declare (ftype (function * (values (or null pathname) (or null index)))
1095 %parse-namestring))
1096 (with-host (found-host host)
1097 (let (;; According to ANSI defaults may be any valid pathname designator
1098 (defaults (etypecase defaults
1099 (pathname
1100 defaults)
1101 (string
1102 (aver (pathnamep *default-pathname-defaults*))
1103 (parse-namestring defaults))
1104 (stream
1105 (truename defaults)))))
1106 (declare (type pathname defaults))
1107 (etypecase thing
1108 (string
1109 (with-array-data ((thing thing) (start start) (end end)
1110 :check-fill-pointer t)
1111 (multiple-value-bind (pathname position)
1112 (%parse-namestring thing found-host defaults start end junk-allowed)
1113 (values pathname (- position start)))))
1114 (pathname
1115 (let ((defaulted-host (or found-host (%pathname-host defaults))))
1116 (declare (type host defaulted-host))
1117 (unless (eq defaulted-host (%pathname-host thing))
1118 (error "The HOST argument doesn't match the pathname host:~% ~
1119 ~S and ~S."
1120 defaulted-host (%pathname-host thing))))
1121 (values thing start))
1122 ((or file-stream synonym-stream)
1123 (values (stream-file-name-or-lose thing) nil))))))
1125 (defun %parse-native-namestring (namestr host defaults start end junk-allowed
1126 as-directory)
1127 (declare (type (or host null) host)
1128 (type string namestr)
1129 (type index start)
1130 (type (or index null) end))
1131 (cond
1132 (junk-allowed
1133 (handler-case
1134 (%parse-native-namestring namestr host defaults start end nil as-directory)
1135 (namestring-parse-error (condition)
1136 (values nil (namestring-parse-error-offset condition)))))
1138 (let* ((end (%check-vector-sequence-bounds namestr start end)))
1139 (multiple-value-bind (new-host device directory file type version)
1140 (cond
1141 (host
1142 (funcall (host-parse-native host) namestr start end as-directory))
1143 ((pathname-host defaults)
1144 (funcall (host-parse-native (pathname-host defaults))
1145 namestr
1146 start
1148 as-directory))
1149 ;; I don't think we should ever get here, as the default
1150 ;; host will always have a non-null HOST, given that we
1151 ;; can't create a new pathname without going through
1152 ;; *DEFAULT-PATHNAME-DEFAULTS*, which has a non-null
1153 ;; host...
1154 (t (bug "Fallen through COND in %PARSE-NAMESTRING")))
1155 (when (and host new-host (not (eq new-host host)))
1156 (error 'simple-type-error
1157 :datum new-host
1158 :expected-type `(or null (eql ,host))
1159 :format-control
1160 "The host in the namestring, ~S,~@
1161 does not match the explicit HOST argument, ~S."
1162 :format-arguments (list new-host host)))
1163 (let ((pn-host (or new-host host (pathname-host defaults))))
1164 (values (intern-pathname pn-host device directory file type version)
1165 end)))))))
1167 (defun parse-native-namestring (thing
1168 &optional
1169 host
1170 (defaults *default-pathname-defaults*)
1171 &key (start 0) end junk-allowed
1172 as-directory)
1173 "Convert THING into a pathname, using the native conventions
1174 appropriate for the pathname host HOST, or if not specified the
1175 host of DEFAULTS. If THING is a string, the parse is bounded by
1176 START and END, and error behaviour is controlled by JUNK-ALLOWED,
1177 as with PARSE-NAMESTRING. For file systems whose native
1178 conventions allow directories to be indicated as files, if
1179 AS-DIRECTORY is true, return a pathname denoting THING as a
1180 directory."
1181 (declare (type pathname-designator thing defaults)
1182 (type (or list host string (member :unspecific)) host)
1183 (type index start)
1184 (type (or index null) end)
1185 (type (or t null) junk-allowed)
1186 (values (or null pathname) (or null index)))
1187 (declare (ftype (function * (values (or null pathname) (or null index)))
1188 %parse-native-namestring))
1189 (with-host (found-host host)
1190 (let ((defaults (etypecase defaults
1191 (pathname
1192 defaults)
1193 (string
1194 (aver (pathnamep *default-pathname-defaults*))
1195 (parse-native-namestring defaults))
1196 (stream
1197 (truename defaults)))))
1198 (declare (type pathname defaults))
1199 (etypecase thing
1200 (string
1201 (with-array-data ((thing thing) (start start) (end end)
1202 :check-fill-pointer t)
1203 (multiple-value-bind (pathname position)
1204 (%parse-native-namestring thing
1205 found-host defaults start end junk-allowed
1206 as-directory)
1207 (values pathname (- position start)))))
1208 (pathname
1209 (let ((defaulted-host (or found-host (%pathname-host defaults))))
1210 (declare (type host defaulted-host))
1211 (unless (eq defaulted-host (%pathname-host thing))
1212 (error "The HOST argument doesn't match the pathname host:~% ~
1213 ~S and ~S."
1214 defaulted-host (%pathname-host thing))))
1215 (values thing start))
1216 ((or file-stream synonym-stream)
1217 (values (stream-file-name-or-lose thing) nil))))))
1219 (defun native-namestring (pathname &key as-file)
1220 "Construct the full native (name)string form of PATHNAME. For
1221 file systems whose native conventions allow directories to be
1222 indicated as files, if AS-FILE is true and the name, type, and
1223 version components of PATHNAME are all NIL or :UNSPECIFIC,
1224 construct a string that names the directory according to the file
1225 system's syntax for files."
1226 (declare (type pathname-designator pathname))
1227 (with-native-pathname (pathname pathname)
1228 (when pathname
1229 (let ((host (or (%pathname-host pathname)
1230 (no-native-namestring-error
1231 pathname "there is no ~S component." :host))))
1232 (funcall (host-unparse-native host) pathname as-file)))))
1234 (flet ((pathname-host-or-no-namestring (pathname)
1235 (or (%pathname-host pathname)
1236 (no-namestring-error
1237 pathname "there is no ~S component." :host))))
1239 (defun namestring (pathname)
1240 "Construct the full (name)string form PATHNAME."
1241 (with-pathname (pathname pathname)
1242 (when pathname
1243 (or (%pathname-namestring pathname)
1244 (let ((host (pathname-host-or-no-namestring pathname)))
1245 (setf (%pathname-namestring pathname)
1246 (logically-readonlyize
1247 (possibly-base-stringize-to-heap
1248 (funcall (host-unparse host) pathname)))))))))
1250 (defun host-namestring (pathname)
1251 "Return a string representation of the name of the host in PATHNAME."
1252 (with-pathname (pathname pathname)
1253 (let ((host (pathname-host-or-no-namestring pathname)))
1254 (funcall (host-unparse-host host) pathname))))
1256 (defun directory-namestring (pathname)
1257 "Return a string representation of the directory in PATHNAME."
1258 (with-pathname (pathname pathname)
1259 (let ((host (pathname-host-or-no-namestring pathname)))
1260 (funcall (host-unparse-directory host) pathname))))
1262 (defun file-namestring (pathname)
1263 "Return a string representation of the name in PATHNAME."
1264 (with-pathname (pathname pathname)
1265 (let ((host (pathname-host-or-no-namestring pathname)))
1266 (funcall (host-unparse-file host) pathname))))
1268 (defun enough-namestring (pathname
1269 &optional
1270 (defaults *default-pathname-defaults*))
1271 "Return an abbreviated pathname sufficient to identify PATHNAME
1272 relative to DEFAULTS."
1273 (with-pathname (pathname pathname)
1274 (let ((host (pathname-host-or-no-namestring pathname)))
1275 (with-pathname (defaults defaults)
1276 (funcall (host-unparse-enough host) pathname defaults))))))
1278 ;;;; wild pathnames
1280 (defun wild-pathname-p (pathname &optional field-key)
1281 "Predicate for determining whether pathname contains any wildcards."
1282 (declare (type pathname-designator pathname)
1283 (type (member nil :host :device :directory :name :type :version)
1284 field-key))
1285 (with-pathname (pathname pathname)
1286 (labels ((wildp (x)
1287 (or (pattern-p x) (if (member x '(:wild :wild-inferiors)) t nil)))
1288 (test (field)
1289 (wildp
1290 (case field
1291 (:host (%pathname-host pathname)) ; always NIL
1292 (:device (%pathname-device pathname))
1293 (:directory
1294 (return-from test (some #'wildp (%pathname-directory pathname))))
1295 (:name (%pathname-name pathname))
1296 (:type (%pathname-type pathname))
1297 (:version (%pathname-version pathname))))))
1298 (if (not field-key)
1299 ;; SBCL does not allow :WILD in the host
1300 (or (test :device) (test :directory) (test :name) (test :type) (test :version))
1301 (test field-key)))))
1303 (defun pathname-match-p (in-pathname in-wildname)
1304 "Pathname matches the wildname template?"
1305 (declare (type pathname-designator in-pathname))
1306 (with-pathname (pathname in-pathname)
1307 (with-pathname (wildname in-wildname)
1308 (macrolet ((frob (field &optional (op 'components-match))
1309 `(or (null (,field wildname))
1310 (,op (,field pathname) (,field wildname)))))
1311 (and (or (null (%pathname-host wildname))
1312 (eq (%pathname-host wildname) (%pathname-host pathname)))
1313 (frob %pathname-device)
1314 (frob %pathname-directory directory-components-match)
1315 (frob %pathname-name)
1316 (frob %pathname-type)
1317 (or (eq (%pathname-host wildname) *physical-host*)
1318 (frob %pathname-version)))))))
1320 ;;; Place the substitutions into the pattern and return the string or pattern
1321 ;;; that results. If DIDDLE-CASE is true, we diddle the result case as well,
1322 ;;; in case we are translating between hosts with difference conventional case.
1323 ;;; The second value is the tail of subs with all of the values that we used up
1324 ;;; stripped off. Note that PATTERN-MATCHES matches all consecutive wildcards
1325 ;;; as a single string, so we ignore subsequent contiguous wildcards.
1326 (defun substitute-into (pattern subs diddle-case)
1327 (declare (type pattern pattern)
1328 (type list subs)
1329 (values (or simple-string pattern) list))
1330 (let ((in-wildcard nil)
1331 (pieces nil)
1332 (strings nil))
1333 (dolist (piece (pattern-pieces pattern))
1334 (cond ((simple-string-p piece)
1335 (push piece strings)
1336 (setf in-wildcard nil))
1337 (in-wildcard)
1339 (setf in-wildcard t)
1340 (unless subs
1341 (error "not enough wildcards in FROM pattern to match ~
1342 TO pattern:~% ~S"
1343 pattern))
1344 (let ((sub (pop subs)))
1345 (typecase sub
1346 (pattern
1347 (when strings
1348 (push (apply #'concatenate 'simple-string
1349 (nreverse strings))
1350 pieces))
1351 (dolist (piece (pattern-pieces sub))
1352 (push piece pieces)))
1353 (simple-string
1354 (push sub strings))
1356 (error "can't substitute this into the middle of a word:~
1357 ~% ~S"
1358 sub)))))))
1360 (when strings
1361 (push (apply #'concatenate 'simple-string (nreverse strings))
1362 pieces))
1363 (values
1364 (maybe-diddle-case
1365 (if (and pieces (simple-string-p (car pieces)) (null (cdr pieces)))
1366 (car pieces)
1367 (make-pattern (nreverse pieces)))
1368 diddle-case)
1369 subs)))
1371 ;;; Called when we can't see how source and from matched.
1372 (defun didnt-match-error (source from)
1373 (error "Pathname components from SOURCE and FROM args to TRANSLATE-PATHNAME~@
1374 did not match:~% ~S ~S"
1375 source from))
1377 ;;; Do TRANSLATE-COMPONENT for all components except host, directory
1378 ;;; and version.
1379 (defun translate-component (source from to diddle-case)
1380 (typecase to
1381 (pattern
1382 (typecase from
1383 (pattern
1384 (typecase source
1385 (pattern
1386 (if (pattern= from source)
1387 source
1388 (didnt-match-error source from)))
1389 (simple-string
1390 (multiple-value-bind (won subs) (pattern-matches from source)
1391 (if won
1392 (values (substitute-into to subs diddle-case))
1393 (didnt-match-error source from))))
1395 (maybe-diddle-case source diddle-case))))
1396 ((member :wild)
1397 (values (substitute-into to (list source) diddle-case)))
1399 (if (components-match source from)
1400 (maybe-diddle-case source diddle-case)
1401 (didnt-match-error source from)))))
1402 ((member nil :wild)
1403 (maybe-diddle-case source diddle-case))
1405 (if (components-match source from)
1407 (didnt-match-error source from)))))
1409 ;;; Return a list of all the things that we want to substitute into the TO
1410 ;;; pattern (the things matched by from on source.) When From contains
1411 ;;; :WILD-INFERIORS, the result contains a sublist of the matched source
1412 ;;; subdirectories.
1413 (defun compute-directory-substitutions (orig-source orig-from)
1414 (let ((source orig-source)
1415 (from orig-from))
1416 (collect ((subs))
1417 (loop
1418 (unless source
1419 (unless (every (lambda (x) (eq x :wild-inferiors)) from)
1420 (didnt-match-error orig-source orig-from))
1421 (subs ())
1422 (return))
1423 (unless from (didnt-match-error orig-source orig-from))
1424 (let ((from-part (pop from))
1425 (source-part (pop source)))
1426 (typecase from-part
1427 (pattern
1428 (typecase source-part
1429 (pattern
1430 (if (pattern= from-part source-part)
1431 (subs source-part)
1432 (didnt-match-error orig-source orig-from)))
1433 (simple-string
1434 (multiple-value-bind (won new-subs)
1435 (pattern-matches from-part source-part)
1436 (if won
1437 (dolist (sub new-subs)
1438 (subs sub))
1439 (didnt-match-error orig-source orig-from))))
1441 (didnt-match-error orig-source orig-from))))
1442 ((member :wild)
1443 (subs source-part))
1444 ((member :wild-inferiors)
1445 (let ((remaining-source (cons source-part source)))
1446 (collect ((res))
1447 (loop
1448 (when (directory-components-match remaining-source from)
1449 (return))
1450 (unless remaining-source
1451 (didnt-match-error orig-source orig-from))
1452 (res (pop remaining-source)))
1453 (subs (res))
1454 (setq source remaining-source))))
1455 (simple-string
1456 (unless (and (simple-string-p source-part)
1457 (string= from-part source-part))
1458 (didnt-match-error orig-source orig-from)))
1460 (didnt-match-error orig-source orig-from)))))
1461 (subs))))
1463 ;;; This is called by TRANSLATE-PATHNAME on the directory components
1464 ;;; of its argument pathnames to produce the result directory
1465 ;;; component. If this leaves the directory NIL, we return the source
1466 ;;; directory. The :RELATIVE or :ABSOLUTE is taken from the source
1467 ;;; directory, except if TO is :ABSOLUTE, in which case the result
1468 ;;; will be :ABSOLUTE.
1469 (defun translate-directories (source from to diddle-case)
1470 (if (not (and source to from))
1471 (or (and to (null source) (remove :wild-inferiors to))
1472 (mapcar (lambda (x) (maybe-diddle-case x diddle-case)) source))
1473 (collect ((res))
1474 ;; If TO is :ABSOLUTE, the result should still be :ABSOLUTE.
1475 (res (if (eq (first to) :absolute)
1476 :absolute
1477 (first source)))
1478 (let ((subs-left (compute-directory-substitutions (rest source)
1479 (rest from))))
1480 (dolist (to-part (rest to))
1481 (typecase to-part
1482 ((member :wild)
1483 (aver subs-left)
1484 (let ((match (pop subs-left)))
1485 (when (listp match)
1486 (error ":WILD-INFERIORS is not paired in from and to ~
1487 patterns:~% ~S ~S" from to))
1488 (res (maybe-diddle-case match diddle-case))))
1489 ((member :wild-inferiors)
1490 (aver subs-left)
1491 (let ((match (pop subs-left)))
1492 (unless (listp match)
1493 (error ":WILD-INFERIORS not paired in from and to ~
1494 patterns:~% ~S ~S" from to))
1495 (dolist (x match)
1496 (res (maybe-diddle-case x diddle-case)))))
1497 (pattern
1498 (multiple-value-bind
1499 (new new-subs-left)
1500 (substitute-into to-part subs-left diddle-case)
1501 (setf subs-left new-subs-left)
1502 (res new)))
1503 (t (res to-part)))))
1504 (res))))
1506 (defun translate-pathname (source from-wildname to-wildname &key)
1507 "Use the source pathname to translate the from-wildname's wild and
1508 unspecified elements into a completed to-pathname based on the to-wildname."
1509 (declare (type pathname-designator source from-wildname to-wildname))
1510 (with-pathname (source source)
1511 (with-pathname (from from-wildname)
1512 (with-pathname (to to-wildname)
1513 (let* ((source-host (%pathname-host source))
1514 (from-host (%pathname-host from))
1515 (to-host (%pathname-host to))
1516 (diddle-case
1517 (and source-host to-host
1518 (not (eq (host-customary-case source-host)
1519 (host-customary-case to-host))))))
1520 (macrolet ((frob (field &optional (op 'translate-component))
1521 `(let ((result (,op (,field source)
1522 (,field from)
1523 (,field to)
1524 diddle-case)))
1525 (if (eq result :error)
1526 (error "~S doesn't match ~S." source from)
1527 result))))
1528 (intern-pathname
1529 (or to-host source-host)
1530 (frob %pathname-device)
1531 (frob %pathname-directory translate-directories)
1532 (frob %pathname-name)
1533 (frob %pathname-type)
1534 (if (eq from-host *physical-host*)
1535 (if (or (eq (%pathname-version to) :wild)
1536 (eq (%pathname-version to) nil))
1537 (%pathname-version source)
1538 (%pathname-version to))
1539 (frob %pathname-version)))))))))
1541 ;;;; logical pathname support. ANSI 92-102 specification.
1542 ;;;;
1543 ;;;; As logical-pathname translations are loaded they are
1544 ;;;; canonicalized as patterns to enable rapid efficient translation
1545 ;;;; into physical pathnames.
1547 ;;;; utilities
1549 ;;; Access *DEFAULT-PATHNAME-DEFAULTS*, issuing a warning if its value
1550 ;;; is silly. (Unlike the vaguely-analogous SANE-PACKAGE, we don't
1551 ;;; actually need to reset the variable when it's silly, since even
1552 ;;; crazy values of *DEFAULT-PATHNAME-DEFAULTS* don't leave the system
1553 ;;; in a state where it's hard to recover interactively.)
1554 (defun sane-default-pathname-defaults ()
1555 (let* ((dfd *default-pathname-defaults*)
1556 (dfd-dir (pathname-directory dfd)))
1557 ;; It's generally not good to use a relative pathname for
1558 ;; *DEFAULT-PATHNAME-DEFAULTS*, since relative pathnames
1559 ;; are defined by merging into a default pathname (which is,
1560 ;; by default, *DEFAULT-PATHNAME-DEFAULTS*).
1561 (when (and (consp dfd-dir)
1562 (eql (first dfd-dir) :relative))
1563 (warn
1564 "~@<~S is a relative pathname. (But we'll try using it anyway.)~@:>"
1565 '*default-pathname-defaults*))
1566 dfd))
1568 (defun simplify-namestring (namestring &optional host)
1569 (funcall (host-simplify-namestring
1570 (or host
1571 (pathname-host (sane-default-pathname-defaults))))
1572 namestring))
1574 (defun lpn-word-char-p (char)
1575 ;; This predicate is just {alpha|digit|dash} but by expressing it as
1576 ;; range comparison we can - I hope - avoid cross-compiling many of
1577 ;; the Unicode tables and particularly MISC-INDEX. Taking them out of
1578 ;; make-host-2 removes some hassle around dumping specialized vectors.
1579 (and (typep (truly-the character char) 'base-char)
1580 (let ((code (char-code char)))
1581 (or (<= (char-code #\a) code (char-code #\z))
1582 (<= (char-code #\A) code (char-code #\Z))
1583 (<= (char-code #\0) code (char-code #\9))
1584 (= code (char-code #\-))))))
1586 ;;; Canonicalize a logical pathname word by uppercasing it checking that it
1587 ;;; contains only legal characters.
1588 (defun logical-word-or-lose (word)
1589 (declare (string word))
1590 ;; Maybe this function used to be called only on the HOST part of a namestring,
1591 ;; and so the error message about an empty string made sense in that it mentioned
1592 ;; "logical host", but this is also called by UPCASE-MAYBE via INTERN-PATHNAME
1593 ;; on every part - name, type, and directory.
1594 ;; Maybe INTERN-PATHNAME is the one that's wrong?
1595 (when (string= word "")
1596 ;; https://www.lispworks.com/documentation/HyperSpec/Body/19_cbb.htm
1597 (error 'namestring-parse-error
1598 :complaint "A string of length 0 is not a valid value for any ~
1599 component of a logical pathname"
1600 :args (list word)
1601 :namestring word :offset 0))
1602 (dotimes (i (length word) (string-upcase word))
1603 ;; um, how do we know it's SIMPLE-STRING when the decl at the top
1604 ;; only says STRING?
1605 (let ((ch (schar word i)))
1606 (unless (lpn-word-char-p ch)
1607 (error 'namestring-parse-error
1608 :complaint "logical namestring character which ~
1609 is not alphanumeric or hyphen:~% ~S"
1610 :args (list ch)
1611 :namestring word :offset i)))))
1613 ;;; Given a logical host or string, return a logical host. If ERROR-P
1614 ;;; is NIL, then return NIL when no such host exists.
1615 (defun find-logical-host (thing &optional (errorp t))
1616 (etypecase thing
1617 (string
1618 (let* ((name (logical-word-or-lose thing))
1619 (hash (sxhash name))
1620 ;; Can do better here: binary search, since we maintain sorted order.
1621 (found (dovector (x *logical-hosts*)
1622 (when (and (eq (logical-host-name-hash x) hash)
1623 (string= (logical-host-name x) name))
1624 (return x)))))
1625 (if (or found (not errorp))
1626 found
1627 ;; This is the error signalled from e.g.
1628 ;; LOGICAL-PATHNAME-TRANSLATIONS when host is not a defined
1629 ;; host, and ANSI specifies that that's a TYPE-ERROR.
1630 (error 'simple-type-error
1631 :datum thing
1632 ;; God only knows what ANSI expects us to use for
1633 ;; the EXPECTED-TYPE here. Maybe this will be OK..
1634 :expected-type
1635 '(and string (satisfies logical-pathname-translations))
1636 :format-control "logical host not yet defined: ~S"
1637 :format-arguments (list thing)))))
1638 (logical-host thing)))
1640 ;;; Given a logical host name or host, return a logical host, creating
1641 ;;; a new one if necessary.
1642 (defun intern-logical-host (thing &aux name host)
1643 (loop
1644 (awhen (find-logical-host thing nil) (return it))
1645 (unless name
1646 (setq name (logical-word-or-lose thing)
1647 host (make-logical-host :name name :name-hash (sxhash name))))
1648 (let* ((old *logical-hosts*)
1649 (new (merge 'vector old (list host) #'string< :key #'logical-host-name)))
1650 (when (eq (cas *logical-hosts* old new) old)
1651 (return host)))))
1653 ;;;; logical pathname parsing
1655 ;;; Deal with multi-char wildcards in a logical pathname token.
1656 (defun maybe-make-logical-pattern (namestring chunks)
1657 (let ((chunk (caar chunks)))
1658 (collect ((pattern))
1659 (let ((last-pos 0)
1660 (len (length chunk)))
1661 (declare (fixnum last-pos))
1662 (loop
1663 (when (= last-pos len) (return))
1664 (let ((pos (or (position #\* chunk :start last-pos) len)))
1665 (if (= pos last-pos)
1666 (when (pattern)
1667 (error 'namestring-parse-error
1668 :complaint "double asterisk inside of logical ~
1669 word: ~S"
1670 :args (list chunk)
1671 :namestring namestring
1672 :offset (+ (cdar chunks) pos)))
1673 (pattern (subseq chunk last-pos pos)))
1674 (if (= pos len)
1675 (return)
1676 (pattern :multi-char-wild))
1677 (setq last-pos (1+ pos)))))
1678 (aver (pattern))
1679 (if (cdr (pattern))
1680 (make-pattern (pattern))
1681 (let ((x (car (pattern))))
1682 (if (eq x :multi-char-wild)
1683 :wild
1684 x))))))
1686 ;;; Return a list of conses where the CDR is the start position and
1687 ;;; the CAR is a string (token) or character (punctuation.)
1688 (defun logical-chunkify (namestr start end)
1689 (collect ((chunks))
1690 (do ((i start (1+ i))
1691 (prev start))
1692 ((= i end)
1693 (when (> end prev)
1694 (chunks (cons (nstring-upcase (subseq namestr prev end)) prev))))
1695 (let ((ch (schar namestr i)))
1696 (unless (or (lpn-word-char-p ch) (char= ch #\*))
1697 (when (> i prev)
1698 (chunks (cons (nstring-upcase (subseq namestr prev i)) prev)))
1699 (setq prev (1+ i))
1700 (unless (member ch '(#\; #\: #\.))
1701 (error 'namestring-parse-error
1702 :complaint "illegal character for logical pathname:~% ~S"
1703 :args (list ch)
1704 :namestring namestr
1705 :offset i))
1706 (chunks (cons ch i)))))
1707 (chunks)))
1709 ;;; Break up a logical-namestring, always a string, into its
1710 ;;; constituent parts.
1711 (defun parse-logical-namestring (namestr start end)
1712 (declare (type simple-string namestr)
1713 (type index start end))
1714 (collect ((directory))
1715 (let ((host nil)
1716 (name nil)
1717 (type nil)
1718 (version nil))
1719 (labels ((expecting (what chunks)
1720 (unless (and chunks (simple-string-p (caar chunks)))
1721 (error 'namestring-parse-error
1722 :complaint "expecting ~A, got ~:[nothing~;~S~]."
1723 :args (list what (caar chunks) (caar chunks))
1724 :namestring namestr
1725 :offset (if chunks (cdar chunks) end)))
1726 (caar chunks))
1727 (parse-host (chunks)
1728 (case (caadr chunks)
1729 (#\:
1730 (setq host
1731 (find-logical-host (expecting "a host name" chunks)))
1732 (parse-relative (cddr chunks)))
1734 (parse-relative chunks))))
1735 (parse-relative (chunks)
1736 (case (caar chunks)
1737 (#\;
1738 (directory :relative)
1739 (parse-directory (cdr chunks)))
1741 (directory :absolute) ; Assumption! Maybe revoked later.
1742 (parse-directory chunks))))
1743 (parse-directory (chunks)
1744 (case (caadr chunks)
1745 (#\;
1746 (directory
1747 (let ((res (expecting "a directory name" chunks)))
1748 (cond ((string= res "..") :up)
1749 ((string= res "**") :wild-inferiors)
1751 (maybe-make-logical-pattern namestr chunks)))))
1752 (parse-directory (cddr chunks)))
1754 (parse-name chunks))))
1755 (parse-name (chunks)
1756 (when chunks
1757 (expecting "a file name" chunks)
1758 (setq name (maybe-make-logical-pattern namestr chunks))
1759 (expecting-dot (cdr chunks))))
1760 (expecting-dot (chunks)
1761 (when chunks
1762 (unless (eql (caar chunks) #\.)
1763 (error 'namestring-parse-error
1764 :complaint "expecting a dot, got ~S."
1765 :args (list (caar chunks))
1766 :namestring namestr
1767 :offset (cdar chunks)))
1768 (if type
1769 (parse-version (cdr chunks))
1770 (parse-type (cdr chunks)))))
1771 (parse-type (chunks)
1772 (expecting "a file type" chunks)
1773 (setq type (maybe-make-logical-pattern namestr chunks))
1774 (expecting-dot (cdr chunks)))
1775 (parse-version (chunks)
1776 (let ((str (expecting "a positive integer, * or NEWEST"
1777 chunks)))
1778 (cond
1779 ((string= str "*") (setq version :wild))
1780 ((string= str "NEWEST") (setq version :newest))
1782 (multiple-value-bind (res pos)
1783 (parse-integer str :junk-allowed t)
1784 (unless (and res (plusp res))
1785 (error 'namestring-parse-error
1786 :complaint "expected a positive integer, ~
1787 got ~S"
1788 :args (list str)
1789 :namestring namestr
1790 :offset (+ pos (cdar chunks))))
1791 (setq version res)))))
1792 (when (cdr chunks)
1793 (error 'namestring-parse-error
1794 :complaint "extra stuff after end of file name"
1795 :namestring namestr
1796 :offset (cdadr chunks)))))
1797 (parse-host (logical-chunkify namestr start end)))
1798 (values host :unspecific (directory) name type version))))
1800 ;;; Return a value suitable, e.g., for preinitializing
1801 ;;; *DEFAULT-PATHNAME-DEFAULTS* before *DEFAULT-PATHNAME-DEFAULTS* is
1802 ;;; initialized (at which time we can't safely call e.g. #'PATHNAME).
1803 (defun make-trivial-default-logical-pathname ()
1804 (intern-pathname (load-time-value (make-logical-host :name "") t)
1805 :unspecific nil nil nil nil))
1807 (defun logical-namestring-p (x)
1808 (and (stringp x)
1809 (ignore-errors
1810 (typep (pathname x) 'logical-pathname))))
1812 (deftype logical-namestring ()
1813 `(satisfies logical-namestring-p))
1815 (defun logical-pathname (pathspec)
1816 "Converts the pathspec argument to a logical-pathname and returns it."
1817 (declare (type (or logical-pathname string stream) pathspec)
1818 (values logical-pathname))
1819 (if (typep pathspec 'logical-pathname)
1820 pathspec
1821 (flet ((oops (problem)
1822 (error 'simple-type-error
1823 :datum pathspec
1824 :expected-type 'logical-namestring
1825 :format-control "~S is not a valid logical namestring:~% ~A"
1826 :format-arguments (list pathspec problem))))
1827 (handler-case
1828 (if (streamp pathspec)
1829 (pathname pathspec)
1830 (let ((potential-host (parse-potential-logical-host pathspec)))
1831 (if potential-host
1832 (values (parse-namestring
1833 pathspec (find-logical-host potential-host)))
1834 (error "no host specified"))))
1835 (error (e) (oops e))))))
1838 ;;;; logical pathname unparsing
1840 (defun unparse-logical-directory (pathname)
1841 (declare (type pathname pathname))
1842 (collect ((pieces))
1843 (let ((directory (%pathname-directory pathname)))
1844 (when directory
1845 (ecase (pop directory)
1846 (:absolute) ; nothing special
1847 (:relative (pieces ";")))
1848 (dolist (dir directory)
1849 (cond ((or (stringp dir) (pattern-p dir))
1850 (pieces (unparse-logical-piece dir))
1851 (pieces ";"))
1852 ((eq dir :wild)
1853 (pieces "*;"))
1854 ((eq dir :wild-inferiors)
1855 (pieces "**;"))
1857 (error "invalid directory component: ~S" dir))))))
1858 (apply #'concatenate 'simple-string (pieces))))
1860 (defun unparse-logical-piece (thing)
1861 (etypecase thing
1862 ((member :wild) "*")
1863 (simple-string thing)
1864 (pattern
1865 (collect ((strings))
1866 (dolist (piece (pattern-pieces thing))
1867 (etypecase piece
1868 (simple-string (strings piece))
1869 (keyword
1870 (cond ((eq piece :wild-inferiors)
1871 (strings "**"))
1872 ((eq piece :multi-char-wild)
1873 (strings "*"))
1874 (t (error "invalid keyword: ~S" piece))))))
1875 (apply #'concatenate 'simple-string (strings))))))
1877 (defun unparse-logical-file (pathname)
1878 (declare (type pathname pathname))
1879 (collect ((strings))
1880 (let* ((name (%pathname-name pathname))
1881 (type (%pathname-type pathname))
1882 (version (%pathname-version pathname))
1883 (type-supplied (pathname-component-present-p type))
1884 (version-supplied (pathname-component-present-p version)))
1885 (when name
1886 (when (and (null type)
1887 (typep name 'string)
1888 (position #\. name :start 1))
1889 (error "too many dots in the name: ~S" pathname))
1890 (strings (unparse-logical-piece name)))
1891 (when type-supplied
1892 (unless name
1893 (error "cannot specify the type without a file: ~S" pathname))
1894 (when (typep type 'string)
1895 (when (position #\. type)
1896 (error "type component can't have a #\. inside: ~S" pathname)))
1897 (strings ".")
1898 (strings (unparse-logical-piece type)))
1899 (when version-supplied
1900 (unless type-supplied
1901 (error "cannot specify the version without a type: ~S" pathname))
1902 (etypecase version
1903 ((member :newest) (strings ".NEWEST")) ; really? not in LPNIFY-NAMESTRING
1904 ((member :wild) (strings ".*"))
1905 (fixnum (strings ".") (strings (format nil "~D" version))))))
1906 (apply #'concatenate 'simple-string (strings))))
1908 ;;; Unparse a logical pathname string.
1909 (defun unparse-enough-namestring (pathname defaults)
1910 (let* ((path-directory (pathname-directory pathname))
1911 (def-directory (pathname-directory defaults))
1912 (enough-directory
1913 ;; Go down the directory lists to see what matches. What's
1914 ;; left is what we want, more or less.
1915 (cond ((and (eq (first path-directory) (first def-directory))
1916 (eq (first path-directory) :absolute))
1917 ;; Both paths are :ABSOLUTE, so find where the
1918 ;; common parts end and return what's left
1919 (do* ((p (rest path-directory) (rest p))
1920 (d (rest def-directory) (rest d)))
1921 ((or (endp p) (endp d)
1922 (not (equal (first p) (first d))))
1923 `(:relative ,@p))))
1925 ;; At least one path is :RELATIVE, so just return the
1926 ;; original path. If the original path is :RELATIVE,
1927 ;; then that's the right one. If PATH-DIRECTORY is
1928 ;; :ABSOLUTE, we want to return that except when
1929 ;; DEF-DIRECTORY is :ABSOLUTE, as handled above. so return
1930 ;; the original directory.
1931 path-directory))))
1932 (unparse-logical-namestring
1933 (make-pathname :host (pathname-host pathname)
1934 :directory enough-directory
1935 :name (pathname-name pathname)
1936 :type (pathname-type pathname)
1937 :version (pathname-version pathname)))))
1939 (defun unparse-logical-namestring (pathname)
1940 (declare (type logical-pathname pathname))
1941 (concatenate 'simple-string
1942 (logical-host-name (%pathname-host pathname)) ":"
1943 (unparse-logical-directory pathname)
1944 (unparse-logical-file pathname)))
1946 ;;;; logical pathname translations
1948 ;;; Verify that the list of translations consists of lists and prepare
1949 ;;; canonical translations. (Parse pathnames and expand out wildcards
1950 ;;; into patterns.)
1951 (defun canonicalize-logical-pathname-translations (translation-list host)
1952 (declare (type list translation-list) (type host host)
1953 (values list))
1954 (mapcar (lambda (translation)
1955 (destructuring-bind (from to) translation
1956 (list (if (typep from 'logical-pathname)
1957 from
1958 (parse-namestring from host))
1959 (pathname to))))
1960 translation-list))
1962 (defun logical-pathname-translations (host)
1963 "Return the (logical) host object argument's list of translations."
1964 (declare (type (or string logical-host) host)
1965 (values list))
1966 (logical-host-translations (find-logical-host host)))
1968 (defun (setf logical-pathname-translations) (translations host)
1969 "Set the translations list for the logical host argument."
1970 (declare (type (or string logical-host) host)
1971 (type list translations)
1972 (values list))
1973 (let ((host (intern-logical-host host)))
1974 (setf (logical-host-canon-transls host)
1975 (canonicalize-logical-pathname-translations translations host))
1976 (setf (logical-host-translations host) translations)))
1978 (defun translate-logical-pathname (pathname &key)
1979 "Translate PATHNAME to a physical pathname, which is returned."
1980 (declare (type pathname-designator pathname)
1981 (values (or null pathname)))
1982 (typecase pathname
1983 (logical-pathname
1984 (dolist (x (logical-host-canon-transls (%pathname-host pathname))
1985 (error 'simple-file-error
1986 :pathname pathname
1987 :format-control "no translation for ~S"
1988 :format-arguments (list pathname)))
1989 (destructuring-bind (from to) x
1990 (when (pathname-match-p pathname from)
1991 (return (translate-logical-pathname
1992 (translate-pathname pathname from to)))))))
1993 (pathname pathname)
1994 (t (translate-logical-pathname (pathname pathname)))))
1996 ;;; Given a pathname, return a corresponding physical pathname.
1997 (defun physicalize-pathname (possibly-logical-pathname)
1998 (if (typep possibly-logical-pathname 'logical-pathname)
1999 (translate-logical-pathname possibly-logical-pathname)
2000 possibly-logical-pathname))
2002 (defun load-logical-pathname-translations (host)
2003 "Reads logical pathname translations from SYS:SITE;HOST.TRANSLATIONS.NEWEST,
2004 with HOST replaced by the supplied parameter. Returns T on success.
2006 If HOST is already defined as logical pathname host, no file is loaded and NIL
2007 is returned.
2009 The file should contain a single form, suitable for use with
2010 \(SETF LOGICAL-PATHNAME-TRANSLATIONS).
2012 Note: behaviour of this function is highly implementation dependent, and
2013 historically it used to be a no-op in SBCL -- the current approach is somewhat
2014 experimental and subject to change."
2015 (declare (type string host)
2016 (values (member t nil)))
2017 (if (find-logical-host host nil)
2018 ;; This host is already defined, all is well and good.
2020 ;; ANSI: "The specific nature of the search is
2021 ;; implementation-defined."
2022 (prog1 t
2023 (setf (logical-pathname-translations host)
2024 (with-open-file (lpt (make-pathname :host "SYS"
2025 :directory '(:absolute "SITE")
2026 :name host
2027 :type "TRANSLATIONS"
2028 :version :newest))
2029 (read lpt))))))
2031 (defun !lpn-cold-init ()
2032 (let* ((sys *default-pathname-defaults*)
2033 (src
2034 (merge-pathnames
2035 (make-pathname :directory '(:relative "src" :wild-inferiors)
2036 :name :wild :type :wild)
2037 sys))
2038 (contrib
2039 (merge-pathnames
2040 (make-pathname :directory '(:relative "contrib" :wild-inferiors)
2041 :name :wild :type :wild)
2042 sys))
2043 (output
2044 (merge-pathnames
2045 (make-pathname :directory '(:relative "output" :wild-inferiors)
2046 :name :wild :type :wild)
2047 sys)))
2048 (setf (logical-pathname-translations "SYS")
2049 `(("SYS:SRC;**;*.*.*" ,src)
2050 ("SYS:CONTRIB;**;*.*.*" ,contrib)
2051 ("SYS:OUTPUT;**;*.*.*" ,output)))))
2053 (defun set-sbcl-source-location (pathname)
2054 "Initialize the SYS logical host based on PATHNAME, which should be
2055 the top-level directory of the SBCL sources. This will replace any
2056 existing translations for \"SYS:SRC;\", \"SYS:CONTRIB;\", and
2057 \"SYS:OUTPUT;\". Other \"SYS:\" translations are preserved."
2058 (let ((truename (truename pathname))
2059 (current-translations
2060 (remove-if (lambda (translation)
2061 (or (pathname-match-p "SYS:SRC;" translation)
2062 (pathname-match-p "SYS:CONTRIB;" translation)
2063 (pathname-match-p "SYS:OUTPUT;" translation)))
2064 (logical-pathname-translations "SYS")
2065 :key #'first)))
2066 (flet ((physical-target (component)
2067 (merge-pathnames
2068 (make-pathname :directory (list :relative component
2069 :wild-inferiors)
2070 :name :wild
2071 :type :wild)
2072 truename)))
2073 (setf (logical-pathname-translations "SYS")
2074 `(("SYS:SRC;**;*.*.*" ,(physical-target "src"))
2075 ("SYS:CONTRIB;**;*.*.*" ,(physical-target "contrib"))
2076 ("SYS:OUTPUT;**;*.*.*" ,(physical-target "output"))
2077 ,@current-translations)))))
2079 (defmethod make-load-form ((pn pathname) &optional env)
2080 (declare (ignore env))
2081 (labels ((reconstruct (component)
2082 (cond ((pattern-p component) (patternify component))
2083 ((and (listp component) (some #'pattern-p component))
2084 (cons 'list (mapcar #'patternify component)))
2085 (t `',component)))
2086 (patternify (subcomponent)
2087 (if (pattern-p subcomponent)
2088 `(make-pattern ',(pattern-pieces subcomponent))
2089 `',subcomponent)))
2090 (values `(intern-pathname
2091 ,(if (typep pn 'logical-pathname)
2092 `(find-logical-host ',(logical-host-name (%pathname-host pn)))
2093 '*physical-host*)
2094 ,(reconstruct (%pathname-device pn))
2095 ,(reconstruct (%pathname-directory pn))
2096 ,(reconstruct (%pathname-name pn))
2097 ,(reconstruct (%pathname-type pn))
2098 ,(reconstruct (%pathname-version pn))))))