(reported by Christophe Rhodes and Martin Atzmueller sbcl-devel
   2002-05-15)
 
-160:
-  USER-HOMEDIR-PATHNAME returns a pathname that SBCL can't do anything
-  with.  Probably we should return an absolute physical pathname
-  instead.  (Reported by Peter van Eynde sbcl-devel 2002-03-29)
-
-161:
-  Typep on certain SATISFIES types doesn't take account of the fact
-  that the function could cause an error; e.g. (TYPEP #\! '(SATISFIES
-  FBOUNDP)) raises an error when it should return NIL.
-
 162:
   (reported by Robert E. Brown 2002-04-16) 
   When a function is called with too few arguments, causing the
   isn't too surprising since there are many differences in stack
   implementation and GC conservatism between the X86 and other ports.)
 
+163:
+  HOST-NAMESTRING on a Unix pathname returns "Unix", which isn't
+  treated as a valid host by anything else in the system. (Reported by
+  Erik Naggum on comp.lang.lisp 2002-04-18)
+
 DEFUNCT CATEGORIES OF BUGS
   IR1-#:
     These labels were used for bugs related to the old IR1 interpreter.
 
     sbcl-0.7.0, available as a patch against sbcl-0.7.0 at
       <http://designix.com.au/brian/SBCL/sbcl-0.7.0-unicode.p0.gz>.
   * Bugfix to GET-DISPATCH-MACRO-CHAR, now returning NIL for undefined
-    dispatch macro character combinations (thanks to Alexey Dejenka)
+    dispatch macro character combinations. (thanks to Alexey Dejenka)
+  * Bugfix in PARSE-NAMESTRING: we now correctly parse unix namestrings 
+    that superficially look like logical namestrings correctly.
+  * USER-HOMEDIR-PATHNAME now returns a (physical) pathname that SBCL
+    can deal with.
 
 planned incompatible changes in 0.7.x:
 * When the profiling interface settles down, maybe in 0.7.x, maybe
 
 no guarantees of interface stability."
     :use ("CL" "SB!ALIEN" "SB!EXT" "SB!INT" "SB!SYS")
     :export (;; wrappers around Unix stuff to give just what Lisp needs
-            "UID-USERNAME"
+            "UID-USERNAME" "UID-HOMEDIR"
 
             ;; stuff with a one-to-one mapping to Unix constructs
             "D-INO" "D-NAME" "D-NAMLEN" "D-OFF" "D-RECLEN"
 
          (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)))
-
-       (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)))))
+    (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))
+               (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")
 
     (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
         ))
   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
 
 \f
 ;;;; namestrings
 
+;;; Handle the case for PARSE-NAMESTRING parsing a potentially
+;;; syntactically valid logical namestring with an explicit host.
+;;;
+;;; This then isn't fully general -- we are relying on the fact that
+;;; we will only pass to parse-namestring namestring with an explicit
+;;; logical host, so that we can pass the host return from
+;;; parse-logical-namestring through to %PARSE-NAMESTRING as a truth
+;;; value. Yeah, this is probably a KLUDGE - CSR, 2002-04-18
+(defun parseable-logical-namestring-p (namestr start end)
+  (catch 'exit
+    (handler-bind
+       ((namestring-parse-error (lambda (c)
+                                  (declare (ignore c))
+                                  (throw 'exit nil))))
+      (let ((colon (position #\: namestr :start start :end end)))
+       (when colon
+         (let ((potential-host
+                (logical-word-or-lose (subseq namestr start colon))))
+           ;; depending on the outcome of CSR comp.lang.lisp post
+           ;; "can PARSE-NAMESTRING create logical hosts, we may need
+           ;; to do things with potential-host (create it
+           ;; temporarily, parse the namestring and unintern the
+           ;; logical host potential-host on failure.
+           (declare (ignore potential-host))
+           (let ((result
+                  (handler-bind
+                      ((simple-type-error (lambda (c)
+                                            (declare (ignore c))
+                                            (throw 'exit nil))))
+                    (parse-logical-namestring namestr start end))))
+             ;; if we got this far, we should have an explicit host
+             ;; (first return value of parse-logical-namestring)
+             (aver result)
+             result)))))))
+
 ;;; Handle the case where PARSE-NAMESTRING is actually parsing a
 ;;; namestring. We pick off the :JUNK-ALLOWED case then find a host to
 ;;; use for parsing, call the parser, then check whether the host matches.
          (%parse-namestring namestr host defaults start end nil)
        (namestring-parse-error (condition)
          (values nil (namestring-parse-error-offset condition))))
-      (let* ((end (or end (length namestr)))
-            (parse-host (or host
-                            (extract-logical-host-prefix namestr start end)
-                            (pathname-host defaults))))
-       (unless parse-host
-         (error "When no HOST argument is supplied, the DEFAULTS argument ~
-                 must have a non-null PATHNAME-HOST."))
-
+      (let* ((end (or end (length namestr))))
        (multiple-value-bind (new-host device directory file type version)
-           (funcall (host-parse parse-host) namestr start end)
+           ;; Comments below are quotes from the HyperSpec
+           ;; PARSE-NAMESTRING entry, reproduced here to demonstrate
+           ;; that we actually have to do things this way rather than
+           ;; some possibly more logical way. - CSR, 2002-04-18
+           (cond
+             ;; "If host is a logical host then thing is parsed as a
+             ;; logical pathname namestring on the host."
+             (host (funcall (host-parse host) namestr start end))
+             ;; "If host is nil and thing is a syntactically valid
+             ;; logical pathname namestring containing an explicit
+             ;; host, then it is parsed as a logical pathname
+             ;; namestring."
+             ((parseable-logical-namestring-p namestr start end)
+              (parse-logical-namestring namestr start end))
+             ;; "If host is nil, default-pathname is a logical
+             ;; pathname, and thing is a syntactically valid logical
+             ;; pathname namestring without an explicit host, then it
+             ;; is parsed as a logical pathname namestring on the
+             ;; host that is the host component of default-pathname."
+             ;;
+             ;; "Otherwise, the parsing of thing is
+             ;; implementation-defined."
+             ;;
+             ;; Both clauses are handled here, as the default
+             ;; *DEFAULT-PATHNAME-DEFAULTS has a SB-IMPL::UNIX-HOST
+             ;; for a host.
+             ((pathname-host defaults)
+              (funcall (host-parse (pathname-host defaults)) namestr start end))
+             ;; I don't think we should ever get here, as the default
+             ;; host will always have a non-null HOST, given that we
+             ;; can't create a new pathname without going through
+             ;; *DEFAULT-PATHNAME-DEFAULTS*, which has a non-null
+             ;; host...
+             (t (bug "Fallen through COND in %PARSE-NAMESTRING")))
          (when (and host new-host (not (eq new-host host)))
            (error 'simple-type-error
                   :datum new-host
                   "The host in the namestring, ~S,~@
                    does not match the explicit HOST argument, ~S."
                   :format-arguments (list new-host host)))
-         (let ((pn-host (or new-host parse-host)))
+         (let ((pn-host (or new-host host (pathname-host defaults))))
            (values (%make-maybe-logical-pathname
                     pn-host device directory file type version)
                    end))))))
 
                                         uid))
       (error "found no match for Unix uid=~S" uid)))
 
+;;; Return the namestring of the home directory, being careful to
+;;; include a trailing #\/
+(defun uid-homedir (uid)
+  (or (newcharstar-string (alien-funcall (extern-alien "uid_homedir"
+                                                      (function (* char) int))
+                                        uid))
+      (error "failed to resolve home directory for Unix uid=~S" uid)))
+
 ;;; Invoke readlink(2) on the file name specified by PATH. Return
 ;;; (VALUES LINKSTRING NIL) on success, or (VALUES NIL ERRNO) on
 ;;; failure.
 
 #include <string.h>
 #include <unistd.h>
 #include <pwd.h>
+#include <stdio.h>
 
 #include "runtime.h"
 #include "sbcl.h"
        return 0;
     }
 }
+
+char *
+uid_homedir(uid_t uid)
+{
+    struct passwd *p = getpwuid(uid);
+    if(p) {
+       /* Let's be careful about this, shall we? */
+       size_t len = strlen(p->pw_dir);
+       if (p->pw_dir[len-1] == '/') {
+           return strdup(p->pw_dir);
+       } else {
+           char *result = malloc(len + 2);
+           if (result) {
+               int nchars = sprintf(result,"%s/",p->pw_dir);
+               if (nchars == len + 1) {
+                   return result;
+               } else {
+                   return 0;
+               }
+           } else {
+               return 0;
+           }
+       }
+    } else {
+       return 0;
+    }
+}
 \f
 /*
  * functions to get miscellaneous C-level variables
 
 
 ;;; turning one logical pathname into another:
 (setf (logical-pathname-translations "foo")
-       '(("tohome;*.*.*" "home:*.*.*")))
-(assert (equal (namestring (translate-logical-pathname "foo:tohome;x.y"))
-               "home:x.y"))    
+       '(("todemo;*.*.*" "demo0:*.*.*")))
+(assert (equal (namestring (translate-logical-pathname "foo:todemo;x.y"))
+               (namestring (translate-logical-pathname "demo0:x.y"))))
 
 ;;; ANSI, in its wisdom, specifies that it's an error (specifically a
 ;;; TYPE-ERROR) to query the system about the translations of a string
 (let ((cond (grab-condition (logical-pathname-translations "unregistered-host"))))
   (assert (typep cond 'type-error)))
 
+(assert (not (string-equal (host-namestring (parse-namestring "OTHER-HOST:ILLEGAL/LPN")) "OTHER-HOST")))
+(assert (string-equal (pathname-name (parse-namestring "OTHER-HOST:ILLEGAL/LPN")) "LPN"))
+
 ;;; FIXME: A comment on this section up to sbcl-0.6.11.30 or so said
 ;;;   examples from CLHS: Section 19.4, LOGICAL-PATHNAME-TRANSLATIONS
 ;;;   (sometimes converted to the Un*x way of things)
 
 ;;; for internal versions, especially for internal versions off the
 ;;; main CVS branch, it gets hairier, e.g. "0.pre7.14.flaky4.13".)
 
-"0.7.2.15"
+"0.7.2.16"