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)))
(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")
(setf start (1+ slash))))
(values absolute (pieces)))))
-;;; the thing before a colon in a logical path
-(def!struct (logical-hostname (:make-load-form-fun
- (lambda (x)
- (values `(make-logical-hostname
- ,(logical-hostname-name x))
- nil)))
- (:copier nil)
- (:constructor make-logical-hostname (name)))
- (name (missing-arg) :type simple-string))
-
-(defun maybe-extract-logical-hostname (namestr start end)
- (declare (type simple-base-string namestr)
- (type index start end))
- (let ((quoted nil))
- (do ((index start (1+ index)))
- ((= index end)
- (values nil start))
- (if quoted
- (setf quoted nil)
- (case (schar namestr index)
- (#\\
- (setf quoted t))
- (#\:
- (return (values (make-logical-hostname
- (remove-backslashes namestr start index))
- (1+ index)))))))))
-
(defun parse-unix-namestring (namestr start end)
(declare (type simple-base-string namestr)
(type index start end))
(multiple-value-bind (absolute pieces) (split-at-slashes namestr start end)
- (let ((logical-hostname
- (if absolute
- nil
- (let ((first (car pieces)))
- (multiple-value-bind (logical-hostname new-start)
- (maybe-extract-logical-hostname namestr
- (car first)
- (cdr first))
- (when logical-hostname
- (setf absolute t)
- (setf (car first) new-start))
- logical-hostname)))))
- (declare (type (or null logical-hostname) logical-hostname))
- (multiple-value-bind (name type version)
- (let* ((tail (car (last pieces)))
- (tail-start (car tail))
- (tail-end (cdr tail)))
- (unless (= tail-start tail-end)
- (setf pieces (butlast pieces))
- (extract-name-type-and-version namestr tail-start tail-end)))
+ (multiple-value-bind (name type version)
+ (let* ((tail (car (last pieces)))
+ (tail-start (car tail))
+ (tail-end (cdr tail)))
+ (unless (= tail-start tail-end)
+ (setf pieces (butlast pieces))
+ (extract-name-type-and-version namestr tail-start tail-end)))
- (when (stringp name)
- (let ((position (position-if (lambda (char)
- (or (char= char (code-char 0))
- (char= char #\/)))
- name)))
- (when position
- (error 'namestring-parse-error
- :complaint "can't embed #\\Nul or #\\/ in Unix namestring"
- :namestring namestr
- :offset position))))
-
- ;; Now we have everything we want. So return it.
- (values nil ; no host for Unix namestrings
- nil ; no device for Unix namestrings
- (collect ((dirs))
- (when logical-hostname
- (dirs logical-hostname))
- (dolist (piece pieces)
- (let ((piece-start (car piece))
- (piece-end (cdr piece)))
- (unless (= piece-start piece-end)
- (cond ((string= namestr ".."
- :start1 piece-start
- :end1 piece-end)
- (dirs :up))
- ((string= namestr "**"
- :start1 piece-start
- :end1 piece-end)
- (dirs :wild-inferiors))
- (t
- (dirs (maybe-make-pattern namestr
- piece-start
- piece-end)))))))
- (cond (absolute
- (cons :absolute (dirs)))
- ((dirs)
- (cons :relative (dirs)))
- (t
- nil)))
- name
- type
- version)))))
+ (when (stringp name)
+ (let ((position (position-if (lambda (char)
+ (or (char= char (code-char 0))
+ (char= char #\/)))
+ name)))
+ (when position
+ (error 'namestring-parse-error
+ :complaint "can't embed #\\Nul or #\\/ in Unix namestring"
+ :namestring namestr
+ :offset position))))
+ ;; Now we have everything we want. So return it.
+ (values nil ; no host for Unix namestrings
+ nil ; no device for Unix namestrings
+ (collect ((dirs))
+ (dolist (piece pieces)
+ (let ((piece-start (car piece))
+ (piece-end (cdr piece)))
+ (unless (= piece-start piece-end)
+ (cond ((string= namestr ".."
+ :start1 piece-start
+ :end1 piece-end)
+ (dirs :up))
+ ((string= namestr "**"
+ :start1 piece-start
+ :end1 piece-end)
+ (dirs :wild-inferiors))
+ (t
+ (dirs (maybe-make-pattern namestr
+ piece-start
+ piece-end)))))))
+ (cond (absolute
+ (cons :absolute (dirs)))
+ ((dirs)
+ (cons :relative (dirs)))
+ (t
+ nil)))
+ name
+ type
+ version))))
(/show0 "filesys.lisp 300")
(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
(when directory
(ecase (pop directory)
(:absolute
- (cond ((logical-hostname-p (car directory))
- ;; FIXME: The old CMU CL "search list" extension is
- ;; gone, but the old machinery is still being used
- ;; clumsily here and elsewhere, to represent anything
- ;; which belongs before a colon prefix in the ANSI
- ;; pathname machinery. This should be cleaned up,
- ;; using simpler machinery with more mnemonic names.
- (pieces (logical-hostname-name (pop directory)))
- (pieces ":"))
- (t
- (pieces "/"))))
+ (pieces "/"))
(:relative
;; nothing special
))
;; 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))
+ (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))))
;; 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
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))
+ (when (typep pathname-type 'simple-base-string)
+ (when (position #\. pathname-type)
+ (error "type component can't have a #\. inside: ~S" pathname)))
(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
(let ((directory (pathname-directory pathname)))
(/noshow0 "computed DIRECTORY")
(if directory
- (ecase (car directory)
+ (ecase (first 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")
- (%enumerate-directories "" (cdr directory) pathname
+ (%enumerate-directories "" (rest directory) pathname
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)))
+ ,@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)))
(etypecase piece
(simple-string
- (let ((head (concatenate 'string head piece)))
+ (let ((head (concatenate 'base-string head piece)))
(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)
+ ;; 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)))
- (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))
(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))
- (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))
(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)
- (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)
- (%enumerate-directories (concatenate 'string head "/")
+ (%enumerate-directories (concatenate 'base-string head "/")
(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.
(components-match file-type type)
(components-match file-version version))
(funcall function
- (concatenate 'string
+ (concatenate 'base-string
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")
- (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")
- (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)
((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)))
- (res (make-string len))
+ (res (make-string len :element-type 'base-char))
(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.
(defun unix-namestring (pathname-spec &optional (for-input t))
- ;; The ordinary rules of converting Lispy paths to Unix paths break
- ;; down for the current working directory, which Lisp thinks of as
- ;; "" (more or less, and modulo ANSI's *DEFAULT-PATHNAME-DEFAULTS*,
- ;; which unfortunately SBCL, as of sbcl-0.6.12.8, basically ignores)
- ;; and Unix thinks of as ".". Since we're at the interface between
- ;; Unix system calls and things like ENSURE-DIRECTORIES-EXIST which
- ;; think the Lisp way, we perform the conversion.
- ;;
- ;; (FIXME: The *right* way to deal with this special case is to
- ;; merge PATHNAME-SPEC with *DEFAULT-PATHNAME-DEFAULTS* here, after
- ;; which it's not a relative pathname any more so the special case
- ;; is no longer an issue. But until *DEFAULT-PATHNAME-DEFAULTS*
- ;; works, we use this hack.)
- (if (empty-relative-pathname-spec-p pathname-spec)
- "."
- ;; 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)
- (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)))))))
+ (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))
+ (t (bug "!ENUMERATE-MATCHES returned more than one match on a non-wild pathname")))))
\f
;;;; TRUENAME and PROBE-FILE
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
- :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)))
+ :pathname pathname
+ :format-control "The file ~S does not exist."
+ :format-arguments (list (namestring pathname))))
+ result))
-;;; If PATHNAME exists, return its truename, otherwise NIL.
(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)))
(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
~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)
t)
\f
;;; (This is an ANSI Common Lisp function.)
-;;;
-;;; This is obtained from the logical name \"home:\", which is set
-;;; up for us at initialization time.
(defun user-homedir-pathname (&optional host)
"Return the home directory of the user as a pathname."
(declare (ignore host))
- ;; Note: CMU CL did #P"home:" here instead of using a call to
- ;; PATHNAME. Delaying construction of the pathname until we're
- ;; running in a target Lisp lets us avoid figuring out how to dump
- ;; cross-compilation host Lisp PATHNAME objects into a target Lisp
- ;; object file. It also might have a small positive effect on
- ;; efficiency, in that we don't allocate a PATHNAME we don't need,
- ;; but it it could also have a larger negative effect. Hopefully
- ;; it'll be OK. -- WHN 19990714
- (pathname "home:"))
+ (pathname (sb!unix:uid-homedir (sb!unix:unix-getuid))))
(defun file-write-date (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"
- (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
+ "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
- "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))
- (if winp (lookup-login-name 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")
+;;; 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
means this function can sometimes return files which don't have the same
directory as PATHNAME."
(let (;; We create one entry in this hash table for each truename,
- ;; as an asymptotically fast way of removing duplicates (which
- ;; can arise when e.g. multiple symlinks map to the same
- ;; 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
- (make-pathname :name :wild
- :type :wild
- :version :wild))))
- (!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
#'string<
:key #'car))))
\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.
-;;;
-;;; The result is a SIMPLE-STRING or NIL.
-;;;
-;;; 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)
- (declare (type (member :group :user) group-or-user))
- (declare (type index id))
- (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))))))))))))
-\f
(/show0 "filesys.lisp 899")
;;; predicate to order pathnames by; goes by name