0.8.8.30:
[sbcl.git] / src / code / filesys.lisp
index 05809d3..1d243d8 100644 (file)
@@ -54,7 +54,7 @@
    checked for whatever they may have protected."
   (declare (type simple-base-string namestr)
           (type index start end))
    checked for whatever they may have protected."
   (declare (type simple-base-string namestr)
           (type index start end))
-  (let* ((result (make-string (- end start)))
+  (let* ((result (make-string (- end start) :element-type 'base-char))
         (dst 0)
         (quoted nil))
     (do ((src start (1+ src)))
         (dst 0)
         (quoted nil))
     (do ((src start (1+ src)))
   (declare (type simple-base-string namestr)
           (type index start end))
   (let* ((last-dot (position #\. namestr :start (1+ start) :end end
   (declare (type simple-base-string namestr)
           (type index start end))
   (let* ((last-dot (position #\. namestr :start (1+ start) :end end
-                            :from-end t))
-        (second-to-last-dot (and last-dot
-                                 (position #\. namestr :start (1+ start)
-                                           :end last-dot :from-end t)))
-        (version :newest))
-    ;; If there is a second-to-last dot, check to see whether there is
-    ;; a valid version after the last dot.
-    (when second-to-last-dot
-      (cond ((and (= (+ last-dot 2) end)
-                 (char= (schar namestr (1+ last-dot)) #\*))
-            (setf version :wild))
-           ((and (< (1+ last-dot) end)
-                 (do ((index (1+ last-dot) (1+ index)))
-                     ((= index end) t)
-                   (unless (char<= #\0 (schar namestr index) #\9)
-                     (return nil))))
-            (setf version
-                  (parse-integer namestr :start (1+ last-dot) :end end)))
-           (t
-            (setf second-to-last-dot nil))))
-    (cond (second-to-last-dot
-          (values (maybe-make-pattern namestr start second-to-last-dot)
-                  (maybe-make-pattern namestr
-                                      (1+ second-to-last-dot)
-                                      last-dot)
-                  version))
-         (last-dot
-          (values (maybe-make-pattern namestr start last-dot)
-                  (maybe-make-pattern namestr (1+ last-dot) end)
-                  version))
-         (t
-          (values (maybe-make-pattern namestr start end)
-                  nil
-                  version)))))
+                            :from-end t)))
+    (cond 
+      (last-dot
+       (values (maybe-make-pattern namestr start last-dot)
+              (maybe-make-pattern namestr (1+ last-dot) end)
+              :newest))
+      (t
+       (values (maybe-make-pattern namestr start end)
+              nil
+              :newest)))))
 
 (/show0 "filesys.lisp 200")
 
 
 (/show0 "filesys.lisp 200")
 
 (defun unparse-unix-host (pathname)
   (declare (type pathname pathname)
           (ignore pathname))
 (defun unparse-unix-host (pathname)
   (declare (type pathname pathname)
           (ignore pathname))
-  "Unix")
+  ;; this host designator needs to be recognized as a physical host in
+  ;; PARSE-NAMESTRING. Until sbcl-0.7.3.x, we had "Unix" here, but
+  ;; that's a valid Logical Hostname, so that's a bad choice. -- CSR,
+  ;; 2002-05-09
+  "")
 
 (defun unparse-unix-piece (thing)
   (etypecase thing
 
 (defun unparse-unix-piece (thing)
   (etypecase thing
       ;; translating logical pathnames to a filesystem without
       ;; versions (like Unix).
       (when name
       ;; translating logical pathnames to a filesystem without
       ;; versions (like Unix).
       (when name
+       (when (and (null type)
+                  (typep name 'string)
+                  (> (length name) 0)
+                  (position #\. name :start 1))
+         (error "too many dots in the name: ~S" pathname))
+       (when (and (typep name 'string)
+                  (string= name ""))
+         (error "name is of length 0: ~S" pathname))
        (strings (unparse-unix-piece name)))
       (when type-supplied
        (unless name
          (error "cannot specify the type without a file: ~S" pathname))
        (strings (unparse-unix-piece name)))
       (when type-supplied
        (unless name
          (error "cannot specify the type without a file: ~S" pathname))
+       (when (typep type 'simple-base-string)
+         (when (position #\. type)
+           (error "type component can't have a #\. inside: ~S" pathname)))
        (strings ".")
        (strings (unparse-unix-piece type))))
     (apply #'concatenate 'simple-string (strings))))
        (strings ".")
        (strings (unparse-unix-piece type))))
     (apply #'concatenate 'simple-string (strings))))
                     ;; We are a relative directory. So we lose.
                     (lose)))))
        (strings (unparse-unix-directory-list result-directory)))
                     ;; We are a relative directory. So we lose.
                     (lose)))))
        (strings (unparse-unix-directory-list result-directory)))
-      (let* ((pathname-version (%pathname-version pathname))
-            (version-needed (and pathname-version
-                                 (not (eq pathname-version :newest))))
-            (pathname-type (%pathname-type pathname))
-            (type-needed (or version-needed
-                             (and pathname-type
-                                  (not (eq pathname-type :unspecific)))))
+      (let* ((pathname-type (%pathname-type pathname))
+            (type-needed (and pathname-type
+                              (not (eq pathname-type :unspecific))))
             (pathname-name (%pathname-name pathname))
             (name-needed (or type-needed
                              (and pathname-name
             (pathname-name (%pathname-name pathname))
             (name-needed (or type-needed
                              (and pathname-name
                                                            defaults)))))))
        (when name-needed
          (unless pathname-name (lose))
                                                            defaults)))))))
        (when name-needed
          (unless pathname-name (lose))
+         (when (and (null pathname-type)
+                    (position #\. pathname-name :start 1))
+           (error "too many dots in the name: ~S" pathname))
          (strings (unparse-unix-piece pathname-name)))
        (when type-needed
          (when (or (null pathname-type) (eq pathname-type :unspecific))
            (lose))
          (strings (unparse-unix-piece pathname-name)))
        (when type-needed
          (when (or (null pathname-type) (eq pathname-type :unspecific))
            (lose))
+         (when (typep pathname-type 'simple-base-string)
+           (when (position #\. pathname-type)
+             (error "type component can't have a #\. inside: ~S" pathname)))
          (strings ".")
          (strings ".")
-         (strings (unparse-unix-piece pathname-type)))
-       (when version-needed
-         (typecase pathname-version
-           ((member :wild)
-            (strings ".*"))
-           (integer
-            (strings (format nil ".~D" pathname-version)))
-           (t
-            (lose)))))
+         (strings (unparse-unix-piece pathname-type))))
       (apply #'concatenate 'simple-string (strings)))))
 \f
 ;;;; wildcard matching stuff
       (apply #'concatenate 'simple-string (strings)))))
 \f
 ;;;; wildcard matching stuff
        (let ((piece (car tail)))
          (etypecase piece
            (simple-string
        (let ((piece (car tail)))
          (etypecase piece
            (simple-string
-            (let ((head (concatenate 'string head piece)))
+            (let ((head (concatenate 'base-string head piece)))
               (with-directory-node-noted (head)
               (with-directory-node-noted (head)
-                (%enumerate-directories (concatenate 'string head "/")
+                (%enumerate-directories (concatenate 'base-string head "/")
                                         (cdr tail) pathname
                                         verify-existence follow-links
                                         nodes function))))
            ((member :wild-inferiors)
                                         (cdr tail) pathname
                                         verify-existence follow-links
                                         nodes function))))
            ((member :wild-inferiors)
+            ;; now with extra error case handling from CLHS
+            ;; 19.2.2.4.3 -- CSR, 2004-01-24
+            (when (member (cadr tail) '(:up :back))
+              (error 'simple-file-error
+                     :pathname pathname
+                     :format-control "~@<invalid use of ~S after :WILD-INFERIORS~@:>."
+                     :format-arguments (list (cadr tail))))
             (%enumerate-directories head (rest tail) pathname
                                     verify-existence follow-links
                                     nodes function)
             (dolist (name (ignore-errors (directory-lispy-filenames head)))
             (%enumerate-directories head (rest tail) pathname
                                     verify-existence follow-links
                                     nodes function)
             (dolist (name (ignore-errors (directory-lispy-filenames head)))
-              (let ((subdir (concatenate 'string head name)))
+              (let ((subdir (concatenate 'base-string head name)))
                 (multiple-value-bind (res dev ino mode)
                     (unix-xstat subdir)
                   (declare (type (or fixnum null) mode))
                 (multiple-value-bind (res dev ino mode)
                     (unix-xstat subdir)
                   (declare (type (or fixnum null) mode))
                                          (eql (cdr dir) ino))
                                 (return t)))
                       (let ((nodes (cons (cons dev ino) nodes))
                                          (eql (cdr dir) ino))
                                 (return t)))
                       (let ((nodes (cons (cons dev ino) nodes))
-                            (subdir (concatenate 'string subdir "/")))
+                            (subdir (concatenate 'base-string subdir "/")))
                         (%enumerate-directories subdir tail pathname
                                                 verify-existence follow-links
                                                 nodes function))))))))
            ((or pattern (member :wild))
             (dolist (name (directory-lispy-filenames head))
               (when (or (eq piece :wild) (pattern-matches piece name))
                         (%enumerate-directories subdir tail pathname
                                                 verify-existence follow-links
                                                 nodes function))))))))
            ((or pattern (member :wild))
             (dolist (name (directory-lispy-filenames head))
               (when (or (eq piece :wild) (pattern-matches piece name))
-                (let ((subdir (concatenate 'string head name)))
+                (let ((subdir (concatenate 'base-string head name)))
                   (multiple-value-bind (res dev ino mode)
                       (unix-xstat subdir)
                     (declare (type (or fixnum null) mode))
                   (multiple-value-bind (res dev ino mode)
                       (unix-xstat subdir)
                     (declare (type (or fixnum null) mode))
                                (eql (logand mode sb!unix:s-ifmt)
                                     sb!unix:s-ifdir))
                       (let ((nodes (cons (cons dev ino) nodes))
                                (eql (logand mode sb!unix:s-ifmt)
                                     sb!unix:s-ifdir))
                       (let ((nodes (cons (cons dev ino) nodes))
-                            (subdir (concatenate 'string subdir "/")))
+                            (subdir (concatenate 'base-string subdir "/")))
                         (%enumerate-directories subdir (rest tail) pathname
                                                 verify-existence follow-links
                                                 nodes function))))))))
          ((member :up)
                         (%enumerate-directories subdir (rest tail) pathname
                                                 verify-existence follow-links
                                                 nodes function))))))))
          ((member :up)
-            (with-directory-node-removed (head)
-            (let ((head (concatenate 'string head "..")))
+          (when (string= head "/")
+            (error 'simple-file-error
+                   :pathname pathname
+                   :format-control "~@<invalid use of :UP after :ABSOLUTE.~@:>"))
+          (with-directory-node-removed (head)
+            (let ((head (concatenate 'base-string head "..")))
               (with-directory-node-noted (head)
               (with-directory-node-noted (head)
-                (%enumerate-directories (concatenate 'string head "/")
+                (%enumerate-directories (concatenate 'base-string head "/")
                                         (rest tail) pathname
                                         verify-existence follow-links
                                         (rest tail) pathname
                                         verify-existence follow-links
-                                        nodes function)))))))
+                                        nodes function)))))
+         ((member :back)
+          ;; :WILD-INFERIORS is handled above, so the only case here
+          ;; should be (:ABSOLUTE :BACK)
+          (aver (string= head "/"))
+          (error 'simple-file-error
+                 :pathname pathname
+                 :format-control "~@<invalid use of :BACK after :ABSOLUTE.~@:>"))))
        (%enumerate-files head pathname verify-existence function))))
 
 ;;; Call FUNCTION on files.
        (%enumerate-files head pathname verify-existence function))))
 
 ;;; Call FUNCTION on files.
                          (components-match file-type type)
                          (components-match file-version version))
                 (funcall function
                          (components-match file-type type)
                          (components-match file-version version))
                 (funcall function
-                         (concatenate 'string
+                         (concatenate 'base-string
                                       directory
                                       complete-filename))))))
          (t
           (/noshow0 "default case")
                                       directory
                                       complete-filename))))))
          (t
           (/noshow0 "default case")
-          (let ((file (concatenate 'string directory name)))
+          (let ((file (concatenate 'base-string directory name)))
             (/noshow "computed basic FILE")
             (unless (or (null type) (eq type :unspecific))
               (/noshow0 "tweaking FILE for more-or-less-:UNSPECIFIC case")
             (/noshow "computed basic FILE")
             (unless (or (null type) (eq type :unspecific))
               (/noshow0 "tweaking FILE for more-or-less-:UNSPECIFIC case")
-              (setf file (concatenate 'string file "." type)))
-            (unless (member version '(nil :newest :wild))
+              (setf file (concatenate 'base-string file "." type)))
+            (unless (member version '(nil :newest :wild :unspecific))
               (/noshow0 "tweaking FILE for more-or-less-:WILD case")
               (/noshow0 "tweaking FILE for more-or-less-:WILD case")
-              (setf file (concatenate 'string file "."
+              (setf file (concatenate 'base-string file "."
                                       (quick-integer-to-string version))))
             (/noshow0 "finished possibly tweaking FILE")
             (when (or (not verify-existence)
                                       (quick-integer-to-string version))))
             (/noshow0 "finished possibly tweaking FILE")
             (when (or (not verify-existence)
        ((zerop n) "0")
        ((eql n 1) "1")
        ((minusp n)
        ((zerop n) "0")
        ((eql n 1) "1")
        ((minusp n)
-        (concatenate 'simple-string "-"
-                     (the simple-string (quick-integer-to-string (- n)))))
+        (concatenate 'simple-base-string "-"
+                     (the simple-base-string (quick-integer-to-string (- n)))))
        (t
         (do* ((len (1+ (truncate (integer-length n) 3)))
        (t
         (do* ((len (1+ (truncate (integer-length n) 3)))
-              (res (make-string len))
+              (res (make-string len :element-type 'base-char))
               (i (1- len) (1- i))
               (q n)
               (r 0))
               (i (1- len) (1- i))
               (q n)
               (r 0))
 
 ;;; Convert PATHNAME into a string that can be used with UNIX system
 ;;; calls, or return NIL if no match is found. Wild-cards are expanded.
 
 ;;; Convert PATHNAME into a string that can be used with UNIX system
 ;;; calls, or return NIL if no match is found. Wild-cards are expanded.
-;;; FIXME this should signal file-error if the pathname is wild, whether
-;;; or not it turns out to have only one match.  Fix post 0.7.2
 (defun unix-namestring (pathname-spec &optional (for-input t))
   (let* ((namestring (physicalize-pathname (merge-pathnames pathname-spec)))
         (matches nil)) ; an accumulator for actual matches
 (defun unix-namestring (pathname-spec &optional (for-input t))
   (let* ((namestring (physicalize-pathname (merge-pathnames pathname-spec)))
         (matches nil)) ; an accumulator for actual matches
+    (when (wild-pathname-p namestring)
+      (error 'simple-file-error
+            :pathname namestring
+            :format-control "bad place for a wild pathname"))
     (!enumerate-matches (match namestring nil :verify-existence for-input)
                        (push match matches))
     (case (length matches)
       (0 nil)
       (1 (first matches))
     (!enumerate-matches (match namestring nil :verify-existence for-input)
                        (push match matches))
     (case (length matches)
       (0 nil)
       (1 (first matches))
-      (t (error 'simple-file-error
-               :format-control "~S is ambiguous:~{~%  ~A~}"
-               :format-arguments (list pathname-spec matches))))))
+      (t (bug "!ENUMERATE-MATCHES returned more than one match on a non-wild pathname")))))
 \f
 ;;;; TRUENAME and PROBE-FILE
 
 \f
 ;;;; TRUENAME and PROBE-FILE
 
 
   Under Unix, the TRUENAME of a broken symlink is considered to be
   the name of the broken symlink itself."
 
   Under Unix, the TRUENAME of a broken symlink is considered to be
   the name of the broken symlink itself."
-  (if (wild-pathname-p pathname)
+  (let ((result (probe-file pathname)))
+    (unless result
       (error 'simple-file-error
       (error 'simple-file-error
-            :format-control "can't use a wild pathname here"
-            :pathname pathname)
-      (let ((result (probe-file pathname)))
-       (unless result
-         (error 'simple-file-error
-                :pathname pathname
-                :format-control "The file ~S does not exist."
-                :format-arguments (list (namestring pathname))))
-       result)))
-
-;;; If PATHNAME exists, return its truename, otherwise NIL.
+            :pathname pathname
+            :format-control "The file ~S does not exist."
+            :format-arguments (list (namestring pathname))))
+    result))
+
 (defun probe-file (pathname)
   #!+sb-doc
   "Return a pathname which is the truename of the file if it exists, or NIL
   otherwise. An error of type FILE-ERROR is signaled if pathname is wild."
 (defun probe-file (pathname)
   #!+sb-doc
   "Return a pathname which is the truename of the file if it exists, or NIL
   otherwise. An error of type FILE-ERROR is signaled if pathname is wild."
-  (when (wild-pathname-p pathname)
-    (error 'simple-file-error
-          :pathname pathname
-          :format-control "can't use a wild pathname here"))
   (let* ((defaulted-pathname (merge-pathnames
                              pathname
                              (sane-default-pathname-defaults)))
   (let* ((defaulted-pathname (merge-pathnames
                              pathname
                              (sane-default-pathname-defaults)))
     (when (and namestring (sb!unix:unix-file-kind namestring t))
       (let ((trueishname (sb!unix:unix-resolve-links namestring)))
        (when trueishname
     (when (and namestring (sb!unix:unix-file-kind namestring t))
       (let ((trueishname (sb!unix:unix-resolve-links namestring)))
        (when trueishname
-         (let ((*ignore-wildcards* t))
-           (pathname (sb!unix:unix-simplify-pathname trueishname))))))))
+         (let* ((*ignore-wildcards* t)
+                (name (sb!unix:unix-simplify-pathname trueishname))) 
+           (if (eq (sb!unix:unix-file-kind name) :directory)
+               (pathname (concatenate 'string name "/"))
+               (pathname name))))))))
 \f
 ;;;; miscellaneous other operations
 
 \f
 ;;;; miscellaneous other operations
 
                                 ~I~_~A~:>"
               :format-arguments (list original new-name (strerror error))))
       (when (streamp file)
                                 ~I~_~A~:>"
               :format-arguments (list original new-name (strerror error))))
       (when (streamp file)
-       (file-name file new-namestring))
+       (file-name file new-name))
       (values new-name original (truename new-name)))))
 
 (defun delete-file (file)
       (values new-name original (truename new-name)))))
 
 (defun delete-file (file)
   #!+sb-doc
   "Return file's creation date, or NIL if it doesn't exist.
  An error of type file-error is signaled if file is a wild pathname"
   #!+sb-doc
   "Return file's creation date, or NIL if it doesn't exist.
  An error of type file-error is signaled if file is a wild pathname"
-  (if (wild-pathname-p file)
-      ;; FIXME: This idiom appears many times in this file. Perhaps it
-      ;; should turn into (CANNOT-BE-WILD-PATHNAME FILE). (C-B-W-P
-      ;; should be a macro, not a function, so that the error message
-      ;; is reported as coming from e.g. FILE-WRITE-DATE instead of
-      ;; from CANNOT-BE-WILD-PATHNAME itself.)
-      (error 'simple-file-error
-            :pathname file
-            :format-control "bad place for a wild pathname")
-      (let ((name (unix-namestring file t)))
-       (when name
-         (multiple-value-bind
-             (res dev ino mode nlink uid gid rdev size atime mtime)
-             (sb!unix:unix-stat name)
-           (declare (ignore dev ino mode nlink uid gid rdev size atime))
-           (when res
-             (+ unix-to-universal-time mtime)))))))
+  (let ((name (unix-namestring file t)))
+    (when name
+      (multiple-value-bind
+           (res dev ino mode nlink uid gid rdev size atime mtime)
+         (sb!unix:unix-stat name)
+       (declare (ignore dev ino mode nlink uid gid rdev size atime))
+       (when res
+         (+ unix-to-universal-time mtime))))))
 
 (defun file-author (file)
   #!+sb-doc
   "Return the file author as a string, or NIL if the author cannot be
  determined. Signal an error of type FILE-ERROR if FILE doesn't exist,
  or FILE is a wild pathname."
 
 (defun file-author (file)
   #!+sb-doc
   "Return the file author as a string, or NIL if the author cannot be
  determined. Signal an error of type FILE-ERROR if FILE doesn't exist,
  or FILE is a wild pathname."
-  (if (wild-pathname-p file)
+  (let ((name (unix-namestring (pathname file) t)))
+    (unless name
       (error 'simple-file-error
             :pathname file
       (error 'simple-file-error
             :pathname file
-            "bad place for a wild pathname")
-      (let ((name (unix-namestring (pathname file) t)))
-       (unless name
-         (error 'simple-file-error
-                :pathname file
-                :format-control "~S doesn't exist."
-                :format-arguments (list file)))
-       (multiple-value-bind (winp dev ino mode nlink uid)
-           (sb!unix:unix-stat name)
-         (declare (ignore dev ino mode nlink))
-         (and winp (sb!unix:uid-username uid))))))
+            :format-control "~S doesn't exist."
+            :format-arguments (list file)))
+    (multiple-value-bind (winp dev ino mode nlink uid)
+       (sb!unix:unix-stat name)
+      (declare (ignore dev ino mode nlink))
+      (and winp (sb!unix:uid-username uid)))))
 \f
 ;;;; DIRECTORY
 
 (/show0 "filesys.lisp 800")
 
 \f
 ;;;; DIRECTORY
 
 (/show0 "filesys.lisp 800")
 
+;;; NOTE: There is a fair amount of hair below that is probably not
+;;; strictly necessary.
+;;;
+;;; The issue is the following: what does (DIRECTORY "SYS:*;") mean?
+;;; Until 2004-01, SBCL's behaviour was unquestionably wrong, as it
+;;; did not translate the logical pathname at all, but instead treated
+;;; it as a physical one.  Other Lisps seem to to treat this call as
+;;; equivalent to (DIRECTORY (TRANSLATE-LOGICAL-PATHNAME "SYS:*;")),
+;;; which is fine as far as it goes, but not very interesting, and
+;;; arguably counterintuitive.  (PATHNAME-MATCH-P "SYS:SRC;" "SYS:*;")
+;;; is true, so why should "SYS:SRC;" not show up in the call to
+;;; DIRECTORY?  (assuming the physical pathname corresponding to it
+;;; exists, of course).
+;;;
+;;; So, the interpretation that I am pushing is for all pathnames
+;;; matching the input pathname to be queried.  This means that we
+;;; need to compute the intersection of the input pathname and the
+;;; logical host FROM translations, and then translate the resulting
+;;; pathname using the host to the TO translation; this treatment is
+;;; recursively invoked until we get a physical pathname, whereupon
+;;; our physical DIRECTORY implementation takes over.
+
+;;; FIXME: this is an incomplete implementation.  It only works when
+;;; both are logical pathnames (which is OK, because that's the only
+;;; case when we call it), but there are other pitfalls as well: see
+;;; the DIRECTORY-HELPER below for some, but others include a lack of
+;;; pattern handling.
+(defun pathname-intersections (one two)
+  (aver (logical-pathname-p one))
+  (aver (logical-pathname-p two))
+  (labels
+      ((intersect-version (one two)
+        (aver (typep one '(or null (member :newest :wild :unspecific)
+                           integer)))
+        (aver (typep two '(or null (member :newest :wild :unspecific)
+                           integer)))
+        (cond
+          ((eq one :wild) two)
+          ((eq two :wild) one)
+          ((or (null one) (eq one :unspecific)) two)
+          ((or (null two) (eq two :unspecific)) one)
+          ((eql one two) one)
+          (t nil)))
+       (intersect-name/type (one two)
+        (aver (typep one '(or null (member :wild :unspecific) string)))
+        (aver (typep two '(or null (member :wild :unspecific) string)))
+        (cond
+          ((eq one :wild) two)
+          ((eq two :wild) one)
+          ((or (null one) (eq one :unspecific)) two)
+          ((or (null two) (eq two :unspecific)) one)
+          ((string= one two) one)
+          (t nil)))
+       (intersect-directory (one two)
+        (aver (typep one '(or null (member :wild :unspecific) list)))
+        (aver (typep two '(or null (member :wild :unspecific) list)))
+        (cond
+          ((eq one :wild) two)
+          ((eq two :wild) one)
+          ((or (null one) (eq one :unspecific)) two)
+          ((or (null two) (eq two :unspecific)) one)
+          (t (aver (eq (car one) (car two)))
+             (mapcar
+              (lambda (x) (cons (car one) x))
+              (intersect-directory-helper (cdr one) (cdr two)))))))
+    (let ((version (intersect-version
+                   (pathname-version one) (pathname-version two)))
+         (name (intersect-name/type
+                (pathname-name one) (pathname-name two)))
+         (type (intersect-name/type
+                (pathname-type one) (pathname-type two)))
+         (host (pathname-host one)))
+      (mapcar (lambda (d)
+               (make-pathname :host host :name name :type type
+                              :version version :directory d))
+             (intersect-directory
+              (pathname-directory one) (pathname-directory two))))))
+
+;;; FIXME: written as its own function because I (CSR) don't
+;;; understand it, so helping both debuggability and modularity.  In
+;;; case anyone is motivated to rewrite it, it returns a list of
+;;; sublists representing the intersection of the two input directory
+;;; paths (excluding the initial :ABSOLUTE or :RELATIVE).
+;;;
+;;; FIXME: Does not work with :UP or :BACK
+;;; FIXME: Does not work with patterns
+;;;
+;;; FIXME: PFD suggests replacing this implementation with a DFA
+;;; conversion of a NDFA.  Find out (a) what this means and (b) if it
+;;; turns out to be worth it.
+(defun intersect-directory-helper (one two)
+  (flet ((simple-intersection (cone ctwo)
+          (cond
+            ((eq cone :wild) ctwo)
+            ((eq ctwo :wild) cone)
+            (t (aver (typep cone 'string))
+               (aver (typep ctwo 'string))
+               (if (string= cone ctwo) cone nil)))))
+    (macrolet
+       ((loop-possible-wild-inferiors-matches
+            (lower-bound bounding-sequence order)
+          (let ((index (gensym)) (g2 (gensym)) (g3 (gensym)) (l (gensym)))
+            `(let ((,l (length ,bounding-sequence)))
+              (loop for ,index from ,lower-bound to ,l
+               append (mapcar (lambda (,g2)
+                                (append
+                                 (butlast ,bounding-sequence (- ,l ,index))
+                                 ,g2))
+                       (mapcar
+                        (lambda (,g3)
+                          (append
+                           (if (eq (car (nthcdr ,index ,bounding-sequence))
+                                   :wild-inferiors)
+                               '(:wild-inferiors)
+                               nil) ,g3))
+                        (intersect-directory-helper
+                         ,@(if order
+                               `((nthcdr ,index one) (cdr two))
+                               `((cdr one) (nthcdr ,index two)))))))))))
+      (cond
+       ((and (eq (car one) :wild-inferiors)
+             (eq (car two) :wild-inferiors))
+        (delete-duplicates
+         (append (mapcar (lambda (x) (cons :wild-inferiors x))
+                         (intersect-directory-helper (cdr one) (cdr two)))
+                 (loop-possible-wild-inferiors-matches 2 one t)
+                 (loop-possible-wild-inferiors-matches 2 two nil))
+         :test 'equal))
+       ((eq (car one) :wild-inferiors)
+        (delete-duplicates (loop-possible-wild-inferiors-matches 0 two nil)
+                           :test 'equal))
+       ((eq (car two) :wild-inferiors)
+        (delete-duplicates (loop-possible-wild-inferiors-matches 0 one t)
+                           :test 'equal))
+       ((and (null one) (null two)) (list nil))
+       ((null one) nil)
+       ((null two) nil)
+       (t (and (simple-intersection (car one) (car two))
+               (mapcar (lambda (x) (cons (simple-intersection
+                                          (car one) (car two)) x))
+                       (intersect-directory-helper (cdr one) (cdr two)))))))))
+
 (defun directory (pathname &key)
   #!+sb-doc
   "Return a list of PATHNAMEs, each the TRUENAME of a file that matched the
 (defun directory (pathname &key)
   #!+sb-doc
   "Return a list of PATHNAMEs, each the TRUENAME of a file that matched the
        ;; (which can arise when e.g. multiple symlinks map to the
        ;; same truename).
        (truenames (make-hash-table :test #'equal))
        ;; (which can arise when e.g. multiple symlinks map to the
        ;; same truename).
        (truenames (make-hash-table :test #'equal))
-        (merged-pathname (merge-pathnames pathname
-                                         *default-pathname-defaults*)))
-    (!enumerate-matches (match merged-pathname)
-      (let ((*ignore-wildcards* t)
-            (truename (truename (if (eq (sb!unix:unix-file-kind match)
-                                       :directory)
-                                    (concatenate 'string match "/")
-                                    match))))
-        (setf (gethash (namestring truename) truenames)
-             truename)))
+       ;; FIXME: Possibly this MERGE-PATHNAMES call should only
+       ;; happen once we get a physical pathname.
+        (merged-pathname (merge-pathnames pathname)))
+    (labels ((do-physical-directory (pathname)
+              (aver (not (logical-pathname-p pathname)))
+              (!enumerate-matches (match pathname)
+                (let* ((*ignore-wildcards* t)
+                       ;; FIXME: Why not TRUENAME?  As reported by
+                       ;; Milan Zamazal sbcl-devel 2003-10-05, using
+                       ;; TRUENAME causes a race condition whereby
+                       ;; removal of a file during the directory
+                       ;; operation causes an error.  It's not clear
+                       ;; what the right thing to do is, though.  --
+                       ;; CSR, 2003-10-13
+                       (truename (probe-file match)))
+                  (when truename
+                    (setf (gethash (namestring truename) truenames)
+                          truename)))))
+            (do-directory (pathname)
+              (if (logical-pathname-p pathname)
+                  (let ((host (intern-logical-host (pathname-host pathname))))
+                    (dolist (x (logical-host-canon-transls host))
+                      (destructuring-bind (from to) x
+                        (let ((intersections
+                               (pathname-intersections pathname from)))
+                          (dolist (p intersections)
+                            (do-directory (translate-pathname p from to)))))))
+                  (do-physical-directory pathname))))
+      (do-directory merged-pathname))
     (mapcar #'cdr
            ;; Sorting isn't required by the ANSI spec, but sorting
            ;; into some canonical order seems good just on the
     (mapcar #'cdr
            ;; Sorting isn't required by the ANSI spec, but sorting
            ;; into some canonical order seems good just on the