0.pre7.122:
[sbcl.git] / src / code / filesys.lisp
index e596852..ca9a19f 100644 (file)
                                         nil)))
                              (:copier nil)
                              (:constructor make-logical-hostname (name)))
                                         nil)))
                              (:copier nil)
                              (:constructor make-logical-hostname (name)))
-  (name (required-argument) :type simple-string))
+  (name (missing-arg) :type simple-string))
 
 (defun maybe-extract-logical-hostname (namestr start end)
   (declare (type simple-base-string namestr)
 
 (defun maybe-extract-logical-hostname (namestr start end)
   (declare (type simple-base-string namestr)
            (t
             (lose)))))
       (apply #'concatenate 'simple-string (strings)))))
            (t
             (lose)))))
       (apply #'concatenate 'simple-string (strings)))))
-
-(/show0 "filesys.lisp 471")
-
-(def!struct (unix-host
-            (:make-load-form-fun make-unix-host-load-form)
-            (:include host
-                      (parse #'parse-unix-namestring)
-                      (unparse #'unparse-unix-namestring)
-                      (unparse-host #'unparse-unix-host)
-                      (unparse-directory #'unparse-unix-directory)
-                      (unparse-file #'unparse-unix-file)
-                      (unparse-enough #'unparse-unix-enough)
-                      (customary-case :lower))))
-
-(/show0 "filesys.lisp 486")
-
-(defvar *unix-host* (make-unix-host))
-
-(/show0 "filesys.lisp 488")
-
-(defun make-unix-host-load-form (host)
-  (declare (ignore host))
-  '*unix-host*)
 \f
 ;;;; wildcard matching stuff
 
 \f
 ;;;; wildcard matching stuff
 
 
 (/show0 "filesys.lisp 498")
 
 
 (/show0 "filesys.lisp 498")
 
-;;; FIXME: could maybe be EVAL-WHEN (COMPILE EVAL)
-
-(defmacro enumerate-matches ((var pathname &optional result
-                                 &key (verify-existence t)
-                                  (follow-links t))
-                            &body body)
-  (let ((body-name (gensym "ENUMERATE-MATCHES-BODY-FUN-")))
-    `(block nil
-       (flet ((,body-name (,var)
-               ,@body))
-         (declare (dynamic-extent ,body-name))
-        (%enumerate-matches (pathname ,pathname)
-                            ,verify-existence
-                             ,follow-links
-                            #',body-name)
-        ,result))))
+(defmacro !enumerate-matches ((var pathname &optional result
+                                  &key (verify-existence t)
+                                  (follow-links t))
+                             &body body)
+  `(block nil
+     (%enumerate-matches (pathname ,pathname)
+                        ,verify-existence
+                        ,follow-links
+                        (lambda (,var) ,@body))
+     ,result))
 
 (/show0 "filesys.lisp 500")
 
 
 (/show0 "filesys.lisp 500")
 
   (let ((directory (pathname-directory pathname)))
     (/noshow0 "computed DIRECTORY")
     (if directory
   (let ((directory (pathname-directory pathname)))
     (/noshow0 "computed DIRECTORY")
     (if directory
-       (ecase (car directory)
+       (ecase (first directory)
          (:absolute
           (/noshow0 "absolute directory")
          (:absolute
           (/noshow0 "absolute directory")
-          (%enumerate-directories "/" (cdr directory) pathname
+          (%enumerate-directories "/" (rest directory) pathname
                                   verify-existence follow-links
                                   nil function))
          (:relative
           (/noshow0 "relative directory")
                                   verify-existence follow-links
                                   nil function))
          (:relative
           (/noshow0 "relative directory")
-          (%enumerate-directories "" (cdr directory) pathname
+          (%enumerate-directories "" (rest directory) pathname
                                   verify-existence follow-links
                                   nil function)))
        (%enumerate-files "" pathname verify-existence function))))
                                   verify-existence follow-links
                                   nil function)))
        (%enumerate-files "" pathname verify-existence function))))
                  (when (and res (eql (logand mode sb!unix:s-ifmt)
                                      sb!unix:s-ifdir))
                    (let ((nodes (cons (cons dev ino) nodes)))
                  (when (and res (eql (logand mode sb!unix:s-ifmt)
                                      sb!unix:s-ifdir))
                    (let ((nodes (cons (cons dev ino) nodes)))
+                     ,@body))))
+            (with-directory-node-removed ((head) &body body)
+              `(multiple-value-bind (res dev ino mode)
+                   (unix-xstat ,head)
+                 (when (and res (eql (logand mode sb!unix:s-ifmt)
+                                     sb!unix:s-ifdir))
+                   (let ((nodes (remove (cons dev ino) nodes :test #'equal)))
                      ,@body)))))
     (if tail
        (let ((piece (car tail)))
                      ,@body)))))
     (if tail
        (let ((piece (car tail)))
                                                 verify-existence follow-links
                                                 nodes function))))))))
          ((member :up)
                                                 verify-existence follow-links
                                                 nodes function))))))))
          ((member :up)
+            (with-directory-node-removed (head)
             (let ((head (concatenate 'string head "..")))
               (with-directory-node-noted (head)
                 (%enumerate-directories (concatenate 'string head "/")
                                         (rest tail) pathname
                                         verify-existence follow-links
             (let ((head (concatenate 'string head "..")))
               (with-directory-node-noted (head)
                 (%enumerate-directories (concatenate 'string head "/")
                                         (rest tail) pathname
                                         verify-existence follow-links
-                                        nodes function))))))
+                                        nodes function)))))))
        (%enumerate-files head pathname verify-existence function))))
 
 ;;; Call FUNCTION on files.
        (%enumerate-files head pathname verify-existence function))))
 
 ;;; Call FUNCTION on files.
          (t
           (/noshow0 "default case")
           (let ((file (concatenate 'string directory name)))
          (t
           (/noshow0 "default case")
           (let ((file (concatenate 'string directory name)))
-            (/noshow0 "computed basic FILE=..")
-            (/primitive-print file)
+            (/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 (or (null type) (eq type :unspecific))
               (/noshow0 "tweaking FILE for more-or-less-:UNSPECIFIC case")
               (setf file (concatenate 'string file "." type)))
               (/noshow0 "tweaking FILE for more-or-less-:WILD case")
               (setf file (concatenate 'string file "."
                                       (quick-integer-to-string version))))
               (/noshow0 "tweaking FILE for more-or-less-:WILD case")
               (setf file (concatenate 'string file "."
                                       (quick-integer-to-string version))))
-            (/noshow0 "finished possibly tweaking FILE=..")
-            (/primitive-print file)
+            (/noshow0 "finished possibly tweaking FILE")
             (when (or (not verify-existence)
                       (sb!unix:unix-file-kind file t))
               (/noshow0 "calling FUNCTION on FILE")
             (when (or (not verify-existence)
                       (sb!unix:unix-file-kind file t))
               (/noshow0 "calling FUNCTION on FILE")
       ;; Otherwise, the ordinary rules apply.
       (let* ((namestring (physicalize-pathname (pathname pathname-spec)))
             (matches nil)) ; an accumulator for actual matches
       ;; Otherwise, the ordinary rules apply.
       (let* ((namestring (physicalize-pathname (pathname pathname-spec)))
             (matches nil)) ; an accumulator for actual matches
-       (enumerate-matches (match namestring nil :verify-existence for-input)
+       (!enumerate-matches (match namestring nil :verify-existence for-input)
           (push match matches))
        (case (length matches)
          (0 nil)
           (push match matches))
        (case (length matches)
          (0 nil)
 
 (defun file-author (file)
   #!+sb-doc
 
 (defun file-author (file)
   #!+sb-doc
-  "Return the file author as a string, or nil if the author cannot be
+  "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)
  determined. Signal an error of type FILE-ERROR if FILE doesn't exist,
  or FILE is a wild pathname."
   (if (wild-pathname-p file)
        (multiple-value-bind (winp dev ino mode nlink uid)
            (sb!unix:unix-stat name)
          (declare (ignore dev ino mode nlink))
        (multiple-value-bind (winp dev ino mode nlink uid)
            (sb!unix:unix-stat name)
          (declare (ignore dev ino mode nlink))
-         (if winp (lookup-login-name uid))))))
+         (and winp (sb!unix:uid-username uid))))))
 \f
 ;;;; DIRECTORY
 
 \f
 ;;;; DIRECTORY
 
    TRUENAMEing and the semantics of the Unix filesystem (symbolic links..)
    means this function can sometimes return files which don't have the same
    directory as PATHNAME."
    TRUENAMEing and the semantics of the Unix filesystem (symbolic links..)
    means this function can sometimes return files which don't have the same
    directory as PATHNAME."
-  (let ((truenames nil)
-       (merged-pathname (merge-pathnames pathname
-                                         (make-pathname :name :wild
-                                                        :type :wild
-                                                        :version :wild))))
-    (enumerate-matches (match merged-pathname)
-      (let ((*ignore-wildcards* t))
-       (push (truename (if (eq (sb!unix:unix-file-kind match) :directory)
-                           (concatenate 'string match "/")
-                           match))
-             truenames)))
-    ;; FIXME: The DELETE-DUPLICATES here requires quadratic time,
-    ;; which is unnecessarily slow. That might not be an issue,
-    ;; though, since the time constant for doing TRUENAME on every
-    ;; directory entry is likely to be (much) larger, and the cost of
-    ;; all those TRUENAMEs on a huge directory might even be quadratic
-    ;; in the directory size. Someone who cares about enormous
-    ;; directories might want to check this. -- WHN 2001-06-19
-    (sort (delete-duplicates truenames :test #'string= :key #'pathname-name)
-         #'string< :key #'pathname-name)))
-\f
-;;;; translating Unix uid's
-;;;;
-;;;; FIXME: should probably move into unix.lisp
-
-(defvar *uid-hash-table* (make-hash-table)
-  #!+sb-doc
-  "hash table for keeping track of uid's and login names")
-
-(/show0 "filesys.lisp 844")
-
-;;; LOOKUP-LOGIN-NAME translates a user id into a login name. Previous
-;;; lookups are cached in a hash table since groveling the passwd(s)
-;;; files is somewhat expensive. The table may hold NIL for id's that
-;;; cannot be looked up since this keeps the files from having to be
-;;; searched in their entirety each time this id is translated.
-(defun lookup-login-name (uid)
-  (multiple-value-bind (login-name foundp) (gethash uid *uid-hash-table*)
-    (if foundp
-       login-name
-       (setf (gethash uid *uid-hash-table*)
-             (get-group-or-user-name :user uid)))))
-
-;;; GET-GROUP-OR-USER-NAME first tries "/etc/passwd" ("/etc/group")
-;;; since it is a much smaller file, contains all the local id's, and
-;;; most uses probably involve id's on machines one would login into.
-;;; Then if necessary, we look in "/etc/passwds" ("/etc/groups") which
-;;; is really long and has to be fetched over the net.
-;;;
-;;; FIXME: Now that we no longer have lookup-group-name, we no longer need
-;;; the GROUP-OR-USER argument.
-(defun get-group-or-user-name (group-or-user id)
-  #!+sb-doc
-  "Returns the simple-string user or group name of the user whose uid or gid
-   is id, or NIL if no such user or group exists. Group-or-user is either
-   :group or :user."
-  (let ((id-string (let ((*print-base* 10)) (prin1-to-string id))))
-    (declare (simple-string id-string))
-    (multiple-value-bind (file1 file2)
-       (ecase group-or-user
-         (:group (values "/etc/group" "/etc/groups"))
-         (:user (values "/etc/passwd" "/etc/passwd")))
-      (or (get-group-or-user-name-aux id-string file1)
-         (get-group-or-user-name-aux id-string file2)))))
-
-;;; FIXME: Isn't there now a POSIX routine to parse the passwd file?
-;;; getpwent? getpwuid?
-(defun get-group-or-user-name-aux (id-string passwd-file)
-  (with-open-file (stream passwd-file)
-    (loop
-      (let ((entry (read-line stream nil)))
-       (unless entry (return nil))
-       (let ((name-end (position #\: (the simple-string entry)
-                                 :test #'char=)))
-         (when name-end
-           (let ((id-start (position #\: (the simple-string entry)
-                                     :start (1+ name-end) :test #'char=)))
-             (when id-start
-               (incf id-start)
-               (let ((id-end (position #\: (the simple-string entry)
-                                       :start id-start :test #'char=)))
-                 (when (and id-end
-                            (string= id-string entry
-                                     :start2 id-start :end2 id-end))
-                   (return (subseq entry 0 name-end))))))))))))
+  (let (;; We create one entry in this hash table for each truename,
+       ;; as an asymptotically efficient way of removing duplicates
+       ;; (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)))
+    (mapcar #'cdr
+           ;; Sorting isn't required by the ANSI spec, but sorting
+           ;; into some canonical order seems good just on the
+           ;; grounds that the implementation should have repeatable
+           ;; behavior when possible.
+            (sort (loop for name being each hash-key in truenames
+                       using (hash-value truename)
+                        collect (cons name truename))
+                  #'string<
+                 :key #'car))))
 \f
 (/show0 "filesys.lisp 899")
 
 \f
 (/show0 "filesys.lisp 899")