0.6.12.7.flaky1:
[sbcl.git] / src / code / unix.lisp
index ce25a36..424b2b8 100644 (file)
@@ -1,8 +1,9 @@
-;;;; This file contains Unix support that SBCL needs to implement itself. It's
-;;;; derived from Peter Van Eynde's unix-glibc2.lisp for CMU CL, which was
-;;;; derived from CMU CL unix.lisp 1.56. But those files aspired to be complete
-;;;; Unix interfaces exported to the end user, while this file aims to be as
-;;;; simple as possible and is not intended for the end user.
+;;;; This file contains Unix support that SBCL needs to implement
+;;;; itself. It's derived from Peter Van Eynde's unix-glibc2.lisp for
+;;;; CMU CL, which was derived from CMU CL unix.lisp 1.56. But those
+;;;; files aspired to be complete Unix interfaces exported to the end
+;;;; user, while this file aims to be as simple as possible and is not
+;;;; intended for the end user.
 ;;;;
 ;;;; FIXME: The old CMU CL unix.lisp code was implemented as hand
 ;;;; transcriptions from Unix headers into Lisp. It appears that this was as
 
 (in-package "SB!UNIX")
 
-(file-comment
-  "$Header$")
-
 (/show0 "unix.lisp 21")
 
-;;;; common machine-independent structures
-
-(eval-when (:compile-toplevel :execute)
-
-(defparameter *compiler-unix-errors* nil)
-
-(/show0 "unix.lisp 29")
-
-(sb!xc:defmacro def-unix-error (name number description)
-  `(progn
-     (eval-when (:compile-toplevel :execute)
-       (push (cons ,number ,description) *compiler-unix-errors*))
-     (eval-when (:compile-toplevel :load-toplevel :execute)
-       (defconstant ,name ,number ,description))))
-
-(sb!xc:defmacro emit-unix-errors ()
-  (let* ((max (apply #'max (mapcar #'car *compiler-unix-errors*)))
-        (array (make-array (1+ max) :initial-element nil)))
-    (dolist (error *compiler-unix-errors*)
-      (setf (svref array (car error)) (cdr error)))
-    `(progn
-       (defvar *unix-errors* ',array)
-       (proclaim '(simple-vector *unix-errors*)))))
-
-) ; EVAL-WHEN
-
-(defvar *unix-errors*)
-
-(/show0 "unix.lisp 52")
-
 (defmacro def-enum (inc cur &rest names)
   (flet ((defform (name)
           (prog1 (when name `(defconstant ,name ,cur))
             (setf cur (funcall inc cur 1)))))
     `(progn ,@(mapcar #'defform names))))
+
+;;; Given a C-level zero-terminated array of C strings, return a
+;;; corresponding Lisp-level list of SIMPLE-STRINGs.
+(defun c-strings->string-list (c-strings)
+  (declare (type (alien (* c-string)) c-strings))
+  (let ((reversed-result nil))
+    (dotimes (i most-positive-fixnum (error "argh! can't happen"))
+      (declare (type index i))
+      (let ((c-string (deref c-strings i)))
+       (if c-string
+            (push c-string reversed-result)
+           (return (nreverse reversed-result)))))))
 \f
 ;;;; Lisp types used by syscalls
 
 \f
 ;;;; system calls
 
-(def-alien-routine ("os_get_errno" get-errno) integer
-  "Return the value of the C library pseudo-variable named \"errno\".")
-
 (/show0 "unix.lisp 74")
 
-(defun get-unix-error-msg (&optional (error-number (get-errno)))
-  #!+sb-doc
-  "Returns a string describing the error number which was returned by a
-  UNIX system call."
-  (declare (type integer error-number))
-  (if (array-in-bounds-p *unix-errors* error-number)
-      (svref *unix-errors* error-number)
-      (format nil "unknown error [~D]" error-number)))
-
 ;;; FIXME: The various FOO-SYSCALL-BAR macros, and perhaps some other
 ;;; macros in this file, are only used in this file, and could be
 ;;; implemented using SB!XC:DEFMACRO wrapped in EVAL-WHEN.
         (values nil (get-errno))
         ,success-form)))
 
-;;; Like SYSCALL, but if it fails, signal an error instead of returning error
-;;; codes. Should only be used for syscalls that will never really get an
-;;; error.
+;;; This is like SYSCALL, but if it fails, signal an error instead of
+;;; returning error codes. Should only be used for syscalls that will
+;;; never really get an error.
 (defmacro syscall* ((name &rest arg-types) success-form &rest args)
   `(let ((result (alien-funcall (extern-alien ,name (function int ,@arg-types))
                                ,@args)))
      (if (minusp result)
-        (error "Syscall ~A failed: ~A" ,name (get-unix-error-msg))
+        (error "Syscall ~A failed: ~A" ,name (strerror))
         ,success-form)))
 
 (/show0 "unix.lisp 109")
 (defmacro int-syscall ((name &rest arg-types) &rest args)
   `(syscall (,name ,@arg-types) (values result 0) ,@args))
 \f
-;;; from stdio.h
-
-(/show0 "unix.lisp 124")
-
-(defun unix-rename (name1 name2)
-  #!+sb-doc
-  "Unix-rename renames the file with string name1 to the string
-   name2. NIL and an error code is returned if an error occurs."
-  (declare (type unix-pathname name1 name2))
-  (void-syscall ("rename" c-string c-string) name1 name2))
-\f
-;;; from stdlib.h
+;;;; hacking the Unix environment
 
 (def-alien-routine ("getenv" posix-getenv) c-string
   "Return the environment string \"name=value\" which corresponds to NAME, or
    NIL if there is none."
   (name c-string))
 \f
+;;; from stdio.h
+
+;;; Rename the file with string NAME1 to the string NAME2. NIL and an
+;;; error code is returned if an error occurs.
+(defun unix-rename (name1 name2)
+  (declare (type unix-pathname name1 name2))
+  (void-syscall ("rename" c-string c-string) name1 name2))
+\f
 ;;; from sys/types.h and gnu/types.h
 
-(/show0 "unix.lisp 144")
+(/show0 "unix.lisp 220")
 
+;;; FIXME: Isn't there some way to use a C wrapper to avoid this hand-copying?
 (defconstant +max-s-long+ 2147483647)
 (defconstant +max-u-long+ 4294967295)
-
-;;; FIXME: Isn't there some way to use a C wrapper to avoid this hand-copying?
 (def-alien-type quad-t #+nil long-long #-nil (array long 2))
 (def-alien-type uquad-t #+nil unsigned-long-long
                #-nil (array unsigned-long 2))
 (def-alien-type caddr-t (* char))
 (def-alien-type swblk-t long)
 (def-alien-type size-t unsigned-int)
-(def-alien-type time-t long)
-(def-alien-type clock-t
-  #!+linux long
-  #!+bsd   unsigned-long)
-(def-alien-type uid-t unsigned-int)
 (def-alien-type ssize-t int)
 
-(/show0 "unix.lisp 163")
-
 ;;; FIXME: We shouldn't hand-copy types from header files into Lisp like this
 ;;; unless we have extreme provocation. Reading directories is not extreme
 ;;; enough, since it doesn't need to be blindingly fast: we can just implement
 ;;; those functions in C as a wrapper layer.
 (def-alien-type fd-mask unsigned-long)
-(/show0 "unix.lisp 171")
-
-;;; FIXME: Isn't there some way to use a C wrapper to avoid this hand-copying?
-(def-alien-type dev-t
-  #!+linux uquad-t
-  #!+bsd   unsigned-int)
-(def-alien-type uid-t unsigned-int)
-(def-alien-type gid-t unsigned-int)
-(def-alien-type ino-t
-  #!+linux unsigned-long
-  #!+bsd   unsigned-int)
-(def-alien-type mode-t
-  #!+linux unsigned-int
-  #!+bsd   unsigned-short)
-(def-alien-type nlink-t
-  #!+linux unsigned-int
-  #!+bsd   unsigned-short)
-(/show0 "unix.lisp 190")
-
-;;; FIXME: We shouldn't hand-copy types from header files into Lisp like this
-;;; unless we have extreme provocation. Reading directories is not extreme
-;;; enough, since it doesn't need to be blindingly fast: we can just implement
-;;; those functions in C as a wrapper layer.
-
-(def-alien-type off-t
-  #!+linux long
-  #!+bsd   quad-t)
 
 (eval-when (:compile-toplevel :load-toplevel :execute)
-  (/show0 "unix.lisp 215")
   (defconstant fd-setsize 1024))
-(/show0 "unix.lisp 217")
 
 (def-alien-type nil
   (struct fd-set
          (fds-bits (array fd-mask #.(/ fd-setsize 32)))))
 
-(/show0 "unix.lisp 223")
+(/show0 "unix.lisp 304")
 \f
-;;;; direntry.h
-
-(def-alien-type nil
-  (struct direct
-    (d-ino long); inode number of entry
-    (d-off off-t)                      ; offset of next disk directory entry
-    (d-reclen unsigned-short)          ; length of this record
-    (d_type unsigned-char)
-    (d-name (array char 256))))                ; name must be no longer than this
-(/show0 "unix.lisp 241")
 \f
-;;;; dirent.h
-
-;;; operations on Unix directories
-
-;;;; FIXME: It might be really nice to implement these in C, so that
-;;;; we don't need to do horrible things like hand-copying the
-;;;; direntry struct slot types into an alien struct.
-
-;;; FIXME: DIRECTORY is an external symbol of package CL, so we should use some
-;;; other name for this low-level implementation type.
-(defstruct directory
-  name
-  (dir-struct (required-argument) :type system-area-pointer))
-(/show0 "unix.lisp 258")
-
-(def!method print-object ((dir directory) stream)
-  (print-unreadable-object (dir stream :type t)
-    (prin1 (directory-name dir) stream)))
-
-(/show0 "unix.lisp 264")
-(defun open-dir (pathname)
-  (declare (type unix-pathname pathname))
-  (when (string= pathname "")
-    (setf pathname "."))
-  (let ((kind (unix-file-kind pathname)))
-    (case kind
-      (:directory
-       (let ((dir-struct
-             (alien-funcall (extern-alien "opendir"
-                                          (function system-area-pointer
-                                                    c-string))
-                            pathname)))
-        (if (zerop (sap-int dir-struct))
-            (values nil (get-errno))
-            (make-directory :name pathname :dir-struct dir-struct))))
-      ((nil)
-       (values nil enoent))
-      (t
-       (values nil enotdir)))))
-(/show0 "unix.lisp 286")
-
-(defun read-dir (dir)
-  (declare (type directory dir))
-  (let ((daddr (alien-funcall (extern-alien "readdir"
-                                           (function system-area-pointer
-                                                     system-area-pointer))
-                             (directory-dir-struct dir))))
-    (declare (type system-area-pointer daddr))
-    (if (zerop (sap-int daddr))
-       nil
-       (with-alien ((direct (* (struct direct)) daddr))
-         (values (cast (slot direct 'd-name) c-string)
-                 (slot direct 'd-ino))))))
-
-(/show0 "unix.lisp 301")
-(defun close-dir (dir)
-  (declare (type directory dir))
-  (alien-funcall (extern-alien "closedir"
-                              (function void system-area-pointer))
-                (directory-dir-struct dir))
-  nil)
-
-;;; dlfcn.h -> in foreign.lisp
-
-;;; fcntl.h
-;;;
-;;; POSIX Standard: 6.5 File Control Operations        <fcntl.h>
-
-(/show0 "unix.lisp 318")
-(defconstant r_ok 4 #!+sb-doc "Test for read permission")
-(defconstant w_ok 2 #!+sb-doc "Test for write permission")
-(defconstant x_ok 1 #!+sb-doc "Test for execute permission")
-(defconstant f_ok 0 #!+sb-doc "Test for presence of file")
+;;;; fcntl.h
+;;;;
+;;;; POSIX Standard: 6.5 File Control Operations       <fcntl.h>
 
-(/show0 "unix.lisp 352")
+;;; Open the file whose pathname is specified by PATH for reading
+;;; and/or writing as specified by the FLAGS argument. Various FLAGS
+;;; masks (O_RDONLY etc.) are defined in fcntlbits.h.
+;;;
+;;; If the O_CREAT flag is specified, then the file is created with a
+;;; permission of argument MODE if the file doesn't exist. An integer
+;;; file descriptor is returned by UNIX-OPEN.
 (defun unix-open (path flags mode)
-  #!+sb-doc
-  "Unix-open opens the file whose pathname is specified by path
-   for reading and/or writing as specified by the flags argument.
-   The flags argument can be:
-
-     o_rdonly  Read-only flag.
-     o_wronly  Write-only flag.
-     o_rdwr      Read-and-write flag.
-     o_append  Append flag.
-     o_creat    Create-if-nonexistent flag.
-     o_trunc    Truncate-to-size-0 flag.
-     o_excl      Error if the file allready exists
-     o_noctty  Don't assign controlling tty
-     o_ndelay  Non-blocking I/O
-     o_sync      Synchronous I/O
-     o_async    Asynchronous I/O
-
-   If the o_creat flag is specified, then the file is created with
-   a permission of argument mode if the file doesn't exist. An
-   integer file descriptor is returned by unix-open."
   (declare (type unix-pathname path)
           (type fixnum flags)
           (type unix-file-mode mode))
 ;;; associated with it.
 (/show0 "unix.lisp 391")
 (defun unix-close (fd)
-  #!+sb-doc
-  "Unix-close takes an integer file descriptor as an argument and
-   closes the file associated with it. T is returned upon successful
-   completion, otherwise NIL and an error number."
   (declare (type unix-fd fd))
   (void-syscall ("close" int) fd))
 \f
-;;; fcntlbits.h
-(eval-when (:compile-toplevel :load-toplevel :execute)
-
-(/show0 "unix.lisp 337")
-(defconstant o_rdonly  0) ; read-only flag
-(defconstant o_wronly  1) ; write-only flag
-(defconstant o_rdwr    2) ; read/write flag
-(defconstant o_accmode 3) ; access mode mask
-(defconstant o_creat ; create-if-nonexistent flag (not fcntl)
-  #!+linux #o100
-  #!+bsd   #x0200)
-(/show0 "unix.lisp 345")
-(defconstant o_excl ; error if already exists (not fcntl)
-  #!+linux #o200
-  #!+bsd   #x0800)
-(defconstant o_noctty ; Don't assign controlling tty. (not fcntl)
-  #!+linux #o400
-  #!+bsd   #x8000)
-(defconstant o_trunc ; truncation flag (not fcntl)
-  #!+linux #o1000
-  #!+bsd   #x0400)
-(defconstant o_append ; append flag
-  #!+linux #o2000
-  #!+bsd   #x0008)
-(/show0 "unix.lisp 361")
-) ; EVAL-WHEN
-\f
 ;;;; timebits.h
 
 ;; A time value that is accurate to the nearest
 \f
 ;;;; resourcebits.h
 
-(defconstant rusage_self 0 #!+sb-doc "The calling process.")
-(defconstant rusage_children -1 #!+sb-doc "Terminated child processes.")
+(defconstant rusage_self 0) ; the calling process
+(defconstant rusage_children -1) ; terminated child processes
 (defconstant rusage_both -2)
 
 (def-alien-type nil
   (struct rusage
-    (ru-utime (struct timeval))                ; user time used
-    (ru-stime (struct timeval))                ; system time used.
-    (ru-maxrss long)               ; Maximum resident set size (in kilobytes)
-    (ru-ixrss long)                    ; integral shared memory size
-    (ru-idrss long)                    ; integral unshared data size
-    (ru-isrss long)                    ; integral unshared stack size
-    (ru-minflt long)                   ; page reclaims
-    (ru-majflt long)                   ; page faults
-    (ru-nswap long)                    ; swaps
-    (ru-inblock long)                  ; block input operations
-    (ru-oublock long)                  ; block output operations
-    (ru-msgsnd long)                   ; messages sent
-    (ru-msgrcv long)                   ; messages received
-    (ru-nsignals long)                 ; signals received
-    (ru-nvcsw long)                    ; voluntary context switches
-    (ru-nivcsw long)))                 ; involuntary context switches
-\f
-;;;; statbuf.h
-
-;;; FIXME: This should go into C code so that we don't need to hand-copy
-;;; it from header files.
-#!+Linux
-(def-alien-type nil
-  (struct stat
-    (st-dev dev-t)
-    (st-pad1 unsigned-short)
-    (st-ino ino-t)
-    (st-mode mode-t)
-    (st-nlink  nlink-t)
-    (st-uid  uid-t)
-    (st-gid  gid-t)
-    (st-rdev dev-t)
-    (st-pad2  unsigned-short)
-    (st-size off-t)
-    (st-blksize unsigned-long)
-    (st-blocks unsigned-long)
-    (st-atime time-t)
-    (unused-1 unsigned-long)
-    (st-mtime time-t)
-    (unused-2 unsigned-long)
-    (st-ctime time-t)
-    (unused-3 unsigned-long)
-    (unused-4 unsigned-long)
-    (unused-5 unsigned-long)))
-
-#!+bsd
-(def-alien-type nil
-  (struct timespec-t
-    (tv-sec long)
-    (tv-nsec long)))
-
-#!+bsd
-(def-alien-type nil
-  (struct stat
-    (st-dev dev-t)
-    (st-ino ino-t)
-    (st-mode mode-t)
-    (st-nlink nlink-t)
-    (st-uid uid-t)
-    (st-gid gid-t)
-    (st-rdev dev-t)
-    (st-atime (struct timespec-t))
-    (st-mtime (struct timespec-t))
-    (st-ctime (struct timespec-t))
-    (st-size    unsigned-long)         ; really quad
-    (st-sizeh   unsigned-long)         ;
-    (st-blocks  unsigned-long)         ; really quad
-    (st-blocksh unsigned-long)
-    (st-blksize unsigned-long)
-    (st-flags   unsigned-long)
-    (st-gen     unsigned-long)
-    (st-lspare  long)
-    (st-qspare (array long 4))
-    ))
-
-;; encoding of the file mode
-
-(defconstant s-ifmt   #o0170000 #!+sb-doc "These bits determine file type.")
-
-;; file types
-(defconstant s-ififo  #o0010000 #!+sb-doc "FIFO")
-(defconstant s-ifchr  #o0020000 #!+sb-doc "Character device")
-(defconstant s-ifdir  #o0040000 #!+sb-doc "Directory")
-(defconstant s-ifblk  #o0060000 #!+sb-doc "Block device")
-(defconstant s-ifreg  #o0100000 #!+sb-doc "Regular file")
-
-;; These don't actually exist on System V, but having them doesn't hurt.
-(defconstant s-iflnk  #o0120000 #!+sb-doc "Symbolic link.")
-(defconstant s-ifsock #o0140000 #!+sb-doc "Socket.")
+    (ru-utime (struct timeval))            ; user time used
+    (ru-stime (struct timeval))            ; system time used.
+    (ru-maxrss long)               ; maximum resident set size (in kilobytes)
+    (ru-ixrss long)                ; integral shared memory size
+    (ru-idrss long)                ; integral unshared data size
+    (ru-isrss long)                ; integral unshared stack size
+    (ru-minflt long)               ; page reclaims
+    (ru-majflt long)               ; page faults
+    (ru-nswap long)                ; swaps
+    (ru-inblock long)              ; block input operations
+    (ru-oublock long)              ; block output operations
+    (ru-msgsnd long)               ; messages sent
+    (ru-msgrcv long)               ; messages received
+    (ru-nsignals long)             ; signals received
+    (ru-nvcsw long)                ; voluntary context switches
+    (ru-nivcsw long)))             ; involuntary context switches
 \f
 ;;;; unistd.h
 
-;;; values for the second argument to access
+;;; Given a file path (a string) and one of four constant modes,
+;;; return T if the file is accessible with that mode and NIL if not.
+;;; When NIL, also return an errno value with NIL which tells why the
+;;; file was not accessible.
+;;; 
+;;; The access modes are:
+;;;   r_ok     Read permission.
+;;;   w_ok     Write permission.
+;;;   x_ok     Execute permission.
+;;;   f_ok     Presence of file.
 (defun unix-access (path mode)
-  #!+sb-doc
-  "Given a file path (a string) and one of four constant modes,
-   UNIX-ACCESS returns T if the file is accessible with that
-   mode and NIL if not. It also returns an errno value with
-   NIL which determines why the file was not accessible.
-
-   The access modes are:
-       r_ok     Read permission.
-       w_ok     Write permission.
-       x_ok     Execute permission.
-       f_ok     Presence of file."
   (declare (type unix-pathname path)
           (type (mod 8) mode))
   (void-syscall ("access" c-string int) path mode))
 
-(defconstant l_set 0 #!+sb-doc "set the file pointer")
-(defconstant l_incr 1 #!+sb-doc "increment the file pointer")
-(defconstant l_xtnd 2 #!+sb-doc "extend the file size")
+;;; values for the second argument to UNIX-LSEEK
+(defconstant l_set 0) ; to set the file pointer
+(defconstant l_incr 1) ; to increment the file pointer
+(defconstant l_xtnd 2) ; to extend the file size
 
+;;; Accept a file descriptor and move the file pointer ahead
+;;; a certain offset for that file. WHENCE can be any of the following:
+;;;  L_SET     Set the file pointer.
+;;;  L_INCR    Increment the file pointer.
+;;;  L_XTND    Extend the file size.
 (defun unix-lseek (fd offset whence)
-  #!+sb-doc
-  "Unix-lseek accepts a file descriptor and moves the file pointer ahead
-   a certain offset for that file. Whence can be any of the following:
-
-   l_set       Set the file pointer.
-   l_incr       Increment the file pointer.
-   l_xtnd       Extend the file size.
-  "
   (declare (type unix-fd fd)
           (type (unsigned-byte 32) offset)
           (type (integer 0 2) whence))
 ;;; and store them into the buffer. It returns the actual number of
 ;;; bytes read.
 (defun unix-read (fd buf len)
-  #!+sb-doc
-  "Unix-read attempts to read from the file described by fd into
-   the buffer buf until it is full. Len is the length of the buffer.
-   The number of bytes actually read is returned or NIL and an error
-   number if an error occurred."
   (declare (type unix-fd fd)
           (type (unsigned-byte 32) len))
 
 ;;; associated with fd from the the buffer starting at offset. It returns
 ;;; the actual number of bytes written.
 (defun unix-write (fd buf offset len)
-  #!+sb-doc
-  "Unix-write attempts to write a character buffer (buf) of length
-   len to the file described by the file descriptor fd. NIL and an
-   error is returned if the call is unsuccessful."
   (declare (type unix-fd fd)
           (type (unsigned-byte 32) offset len))
   (int-syscall ("write" int (* char) int)
                 (addr (deref ptr offset)))
               len))
 
+;;; Set up a unix-piping mechanism consisting of an input pipe and an
+;;; output pipe. Return two values: if no error occurred the first
+;;; value is the pipe to be read from and the second is can be written
+;;; to. If an error occurred the first value is NIL and the second the
+;;; unix error code.
+(defun unix-pipe ()
+  (with-alien ((fds (array int 2)))
+    (syscall ("pipe" (* int))
+            (values (deref fds 0) (deref fds 1))
+            (cast fds (* int)))))
+
 ;;; UNIX-CHDIR accepts a directory name and makes that the
 ;;; current working directory.
 (defun unix-chdir (path)
-  #!+sb-doc
-  "Given a file path string, unix-chdir changes the current working
-   directory to the one specified."
   (declare (type unix-pathname path))
   (void-syscall ("chdir" c-string) path))
 
+(defun unix-mkdir (name mode)
+  (declare (type unix-pathname name)
+          (type unix-file-mode mode))
+  (void-syscall ("mkdir" c-string int) name mode))
+
+;;; Return the current directory as a SIMPLE-STRING.
 (defun unix-current-directory ()
-  #!+sb-doc
-  "Return the current directory as a SIMPLE-STRING."
   ;; FIXME: Gcc justifiably complains that getwd is dangerous and should
   ;; not be used; especially with a hardwired 1024 buffer size, yecch.
   ;; This should be rewritten to use getcwd(3), perhaps by writing
                                       (cast buf (* char)))))
            (cast buf c-string))))
 
-;;; UNIX-EXIT terminates a program.
+;;; Duplicate an existing file descriptor (given as the argument) and
+;;; return it. If FD is not a valid file descriptor, NIL and an error
+;;; number are returned.
+(defun unix-dup (fd)
+  (declare (type unix-fd fd))
+  (int-syscall ("dup" int) fd))
+
+;;; Terminate the current process with an optional error code. If
+;;; successful, the call doesn't return. If unsuccessful, the call
+;;; returns NIL and an error number.
 (defun unix-exit (&optional (code 0))
-  #!+sb-doc
-  "Unix-exit terminates the current process with an optional
-   error code. If successful, the call doesn't return. If
-   unsuccessful, the call returns NIL and an error number."
   (declare (type (signed-byte 32) code))
   (void-syscall ("exit" int) code))
 
-(def-alien-routine ("getpid" unix-getpid) int
-  #!+sb-doc
-  "Unix-getpid returns the process-id of the current process.")
+;;; Return the process id of the current process.
+(def-alien-routine ("getpid" unix-getpid) int)
 
-(def-alien-routine ("getuid" unix-getuid) int
-  #!+sb-doc
-  "Unix-getuid returns the real user-id associated with the
-   current process.")
+;;; Return the real user-id associated with the current process.
+(def-alien-routine ("getuid" unix-getuid) int)
 
+;;; Invoke readlink(2) on the file name specified by the simple string
+;;; PATH. Return up to two values: the contents of the symbolic link
+;;; if the call is successful, or NIL and the Unix error number.
 (defun unix-readlink (path)
-  #!+sb-doc
-  "Unix-readlink invokes the readlink system call on the file name
-  specified by the simple string path. It returns up to two values:
-  the contents of the symbolic link if the call is successful, or
-  NIL and the Unix error number."
   (declare (type unix-pathname path))
   (with-alien ((buf (array char 1024)))
     (syscall ("readlink" c-string (* char) int)
             path (cast buf (* char)) 1024)))
 
 ;;; UNIX-UNLINK accepts a name and deletes the directory entry for that
-;;; name and the file if this is the last link.
+;;; name and the file if this is the last link. 
 (defun unix-unlink (name)
-  #!+sb-doc
-  "Unix-unlink removes the directory entry for the named file.
-   NIL and an error code is returned if the call fails."
   (declare (type unix-pathname name))
   (void-syscall ("unlink" c-string) name))
 
+;;; Set the tty-process-group for the unix file-descriptor FD to PGRP.
+;;; If not supplied, FD defaults to "/dev/tty".
 (defun %set-tty-process-group (pgrp &optional fd)
-  #!+sb-doc
-  "Set the tty-process-group for the unix file-descriptor FD to PGRP. If not
-  supplied, FD defaults to /dev/tty."
   (let ((old-sigs (unix-sigblock (sigmask :sigttou
                                          :sigttin
                                          :sigtstp
                     (values nil errno)))))
       (unix-sigsetmask old-sigs))))
 
+;;; Return the name of the host machine as a string.
 (defun unix-gethostname ()
-  #!+sb-doc
-  "Unix-gethostname returns the name of the host machine as a string."
   (with-alien ((buf (array char 256)))
     (syscall ("gethostname" (* char) int)
             (cast buf c-string)
             (cast buf (* char)) 256)))
 
-;;; Unix-fsync writes the core-image of the file described by "fd" to
-;;; permanent storage (i.e. disk).
-
+;;; Write the core image of the file described by FD to disk.
 (defun unix-fsync (fd)
-  #!+sb-doc
-  "Unix-fsync writes the core image of the file described by
-   fd to disk."
   (declare (type unix-fd fd))
   (void-syscall ("fsync" int) fd))
 \f
+;;;; sys/ioctl.h
+
+;;; UNIX-IOCTL performs a variety of operations on open i/o
+;;; descriptors. See the UNIX Programmer's Manual for more
+;;; information.
+(defun unix-ioctl (fd cmd arg)
+  (declare (type unix-fd fd)
+          (type (unsigned-byte 32) cmd))
+  (void-syscall ("ioctl" int unsigned-int (* char)) fd cmd arg))
+\f
 ;;;; sys/resource.h
 
 ;;; FIXME: All we seem to need is the RUSAGE_SELF version of this.
+;;;
+;;; Like getrusage(2), but return only the system and user time,
+;;; and return the seconds and microseconds as separate values.
 #!-sb-fluid (declaim (inline unix-fast-getrusage))
 (defun unix-fast-getrusage (who)
-  #!+sb-doc
-  "Like call getrusage, but return only the system and user time, and returns
-   the seconds and microseconds as separate values."
   (declare (values (member t)
                   (unsigned-byte 31) (mod 1000000)
                   (unsigned-byte 31) (mod 1000000)))
                      (slot (slot usage 'ru-stime) 'tv-usec))
              who (addr usage))))
 
+;;; Return information about the resource usage of the process
+;;; specified by WHO. WHO can be either the current process
+;;; (rusage_self) or all of the terminated child processes
+;;; (rusage_children). NIL and an error number is returned if the call
+;;; fails.
 (defun unix-getrusage (who)
-  #!+sb-doc
-  "Unix-getrusage returns information about the resource usage
-   of the process specified by who. Who can be either the
-   current process (rusage_self) or all of the terminated
-   child processes (rusage_children). NIL and an error number
-   is returned if the call fails."
   (with-alien ((usage (struct rusage)))
     (syscall ("getrusage" int (* (struct rusage)))
              (values t
                      (slot usage 'ru-nvcsw)
                      (slot usage 'ru-nivcsw))
              who (addr usage))))
-
 \f
 ;;;; sys/select.h
 
                    ,num-descriptors ,read-fds ,write-fds ,exception-fds
                    (if timeout-secs (alien-sap (addr tv)) (int-sap 0))))))
 
-;;; Unix-select accepts sets of file descriptors and waits for an event
+;;; UNIX-SELECT accepts sets of file descriptors and waits for an event
 ;;; to happen on one of them or to time out.
-
 (defmacro num-to-fd-set (fdset num)
   `(if (fixnump ,num)
        (progn
              collect `(ash (deref (slot ,fdset 'fds-bits) ,index)
                            ,(* index 32))))))
 
+;;; Examine the sets of descriptors passed as arguments to see whether
+;;; they are ready for reading and writing. See the UNIX Programmer's
+;;; Manual for more information.
 (defun unix-select (nfds rdfds wrfds xpfds to-secs &optional (to-usecs 0))
-  #!+sb-doc
-  "Unix-select examines the sets of descriptors passed as arguments
-   to see whether they are ready for reading and writing. See the UNIX
-   Programmers Manual for more information."
   (declare (type (integer 0 #.FD-SETSIZE) nfds)
           (type unsigned-byte rdfds wrfds xpfds)
           (type (or (unsigned-byte 31) null) to-secs)
               (xpf (struct fd-set)))
     (when to-secs
       (setf (slot tv 'tv-sec) to-secs)
-      (setf (slot tv 'tv-usec) to-usecs))
+     (setf (slot tv 'tv-usec) to-usecs))
     (num-to-fd-set rdf rdfds)
     (num-to-fd-set wrf wrfds)
     (num-to-fd-set xpf xpfds)
 \f
 ;;;; sys/stat.h
 
-;;; FIXME: This is only used in this file, and needn't be in target Lisp
-;;; runtime. It's also unclear why it needs to be a macro instead of a
-;;; function. Perhaps it should become a FLET.
-(defmacro extract-stat-results (buf)
-  `(values T
-          #!+bsd
-          (slot ,buf 'st-dev)
-          #!+linux
-          (+ (deref (slot ,buf 'st-dev) 0)
-             (* (+ +max-u-long+  1)
-                (deref (slot ,buf 'st-dev) 1)))   ;;; let's hope this works..
-          (slot ,buf 'st-ino)
-          (slot ,buf 'st-mode)
-          (slot ,buf 'st-nlink)
-          (slot ,buf 'st-uid)
-          (slot ,buf 'st-gid)
-          #!+bsd
-          (slot ,buf 'st-rdev)
-          #!+linux
-          (+ (deref (slot ,buf 'st-rdev) 0)
-             (* (+ +max-u-long+  1)
-                (deref (slot ,buf 'st-rdev) 1)))   ;;; let's hope this works..
-          #!+linux (slot ,buf 'st-size)
-          #!+bsd
-          (+ (slot ,buf 'st-size)
-             (* (+ +max-u-long+ 1)
-                (slot ,buf 'st-sizeh)))
-          #!+linux (slot ,buf 'st-atime)
-          #!+bsd   (slot (slot ,buf 'st-atime) 'tv-sec)
-          #!+linux (slot ,buf 'st-mtime)
-          #!+bsd   (slot (slot ,buf 'st-mtime) 'tv-sec)
-          #!+linux (slot ,buf 'st-ctime)
-          #!+bsd   (slot (slot ,buf 'st-ctime) 'tv-sec)
-          (slot ,buf 'st-blksize)
-          #!+linux (slot ,buf 'st-blocks)
-          #!+bsd
-          (+ (slot ,buf 'st-blocks)
-             (* (+ +max-u-long+ 1)
-                (slot ,buf 'st-blocksh)))
-          ))
-
+;;; This is a structure defined in src/runtime/wrap.c, to look
+;;; basically like "struct stat" according to stat(2). It may not
+;;; actually correspond to the real in-memory stat structure that the
+;;; syscall uses, and that's OK. Linux in particular is packed full of
+;;; stat macros, and trying to keep Lisp code in correspondence with
+;;; it is more pain than it's worth, so we just let our C runtime
+;;; synthesize a nice consistent structure for us.
+;;;
+;;; Note that st-dev is a long, not a dev-t. This is because dev-t on
+;;; linux 32 bit archs is a 64 bit quantity, but alien doesn's support
+;;; those. We don't actually access that field anywhere, though, so
+;;; until we can get 64 bit alien support it'll do.
+(def-alien-type nil
+  (struct wrapped_stat
+    (st-dev unsigned-long)              ;would be dev-t in a real stat
+    (st-ino ino-t)
+    (st-mode mode-t)
+    (st-nlink  nlink-t)
+    (st-uid  uid-t)
+    (st-gid  gid-t)
+    (st-rdev unsigned-long)             ;ditto
+    (st-size off-t)
+    (st-blksize unsigned-long)
+    (st-blocks unsigned-long)
+    (st-atime time-t)
+    (st-mtime time-t)
+    (st-ctime time-t)))
+
+;;; shared C-struct-to-multiple-VALUES conversion for the stat(2)
+;;; family of Unix system calls
+(defun %extract-stat-results (wrapped-stat)
+  (declare (type (alien (* (struct wrapped_stat)))))
+  (values t
+         (slot wrapped-stat 'st-dev)
+         (slot wrapped-stat 'st-ino)
+         (slot wrapped-stat 'st-mode)
+         (slot wrapped-stat 'st-nlink)
+         (slot wrapped-stat 'st-uid)
+         (slot wrapped-stat 'st-gid)
+         (slot wrapped-stat 'st-rdev)
+         ;; FIXME: OpenBSD has a 64-bit st_size slot, which is
+         ;; basically a good thing, except that it is too
+         ;; 21st-century for sbcl-0.6.12.8's FFI to handle. As a
+         ;; quick kludgy workaround, we return a 0 placeholder from
+         ;; this function, and downstream we stub out the FILE-LENGTH
+         ;; operation (which is the only place that SBCL actually
+         ;; uses the SIZE value returned from any UNIX-STAT-ish call).
+         #!+openbsd 0
+         #!-openbsd (slot wrapped-stat 'st-size)
+         (slot wrapped-stat 'st-atime)
+         (slot wrapped-stat 'st-mtime)
+         (slot wrapped-stat 'st-ctime)
+         (slot wrapped-stat 'st-blksize)
+         (slot wrapped-stat 'st-blocks)))
+
+;;; The stat(2) family of Unix system calls are implemented as calls
+;;; to C-level wrapper functions which copies all the raw "struct
+;;; stat" slots into a system-independent format, so that we don't
+;;; need to mess around with tweaking the Lisp code to correspond to
+;;; different OS/CPU combinations.
+;;;    stat(2) <->  stat_wrapper()
+;;;   fstat(2) <-> fstat_wrapper()
+;;;   lstat(2) <-> lstat_wrapper()
+;;; Then this function is used to convert all the stat slots into
+;;; multiple return values.
 (defun unix-stat (name)
-  #!+sb-doc
-  "Unix-stat retrieves information about the specified
-   file returning them in the form of multiple values.
-   See the UNIX Programmer's Manual for a description
-   of the values returned. If the call fails, then NIL
-   and an error number is returned instead."
   (declare (type unix-pathname name))
-  (when (string= name "")
-    (setf name "."))
-  (with-alien ((buf (struct stat)))
-    (syscall ("stat" c-string (* (struct stat)))
-            (extract-stat-results buf)
+  (with-alien ((buf (struct wrapped_stat)))
+    (syscall ("stat_wrapper" c-string (* (struct wrapped_stat)))
+            (%extract-stat-results buf)
             name (addr buf))))
-
-(defun unix-fstat (fd)
-  #!+sb-doc
-  "Unix-fstat is similar to unix-stat except the file is specified
-   by the file descriptor fd."
-  (declare (type unix-fd fd))
-  (with-alien ((buf (struct stat)))
-    (syscall ("fstat" int (* (struct stat)))
-            (extract-stat-results buf)
-            fd (addr buf))))
-
 (defun unix-lstat (name)
-  #!+sb-doc
-  "Unix-lstat is similar to unix-stat except the specified
-   file must be a symbolic link."
   (declare (type unix-pathname name))
-  (with-alien ((buf (struct stat)))
-    (syscall ("lstat" c-string (* (struct stat)))
-            (extract-stat-results buf)
+  (with-alien ((buf (struct wrapped_stat)))
+    (syscall ("lstat_wrapper" c-string (* (struct wrapped_stat)))
+            (%extract-stat-results buf)
             name (addr buf))))
-
-;;; UNIX-MKDIR accepts a name and a mode and attempts to create the
-;;; corresponding directory with mode mode.
-(defun unix-mkdir (name mode)
-  #!+sb-doc
-  "Unix-mkdir creates a new directory with the specified name and mode.
-   (Same as those for unix-fchmod.)  It returns T upon success, otherwise
-   NIL and an error number."
-  (declare (type unix-pathname name)
-          (type unix-file-mode mode))
-  (void-syscall ("mkdir" c-string int) name mode))
+(defun unix-fstat (fd)
+  (declare (type unix-fd fd))
+  (with-alien ((buf (struct wrapped_stat)))
+    (syscall ("fstat_wrapper" int (* (struct wrapped_stat)))
+            (%extract-stat-results buf)
+            fd (addr buf))))
 \f
 ;;;; time.h
 
-;; POSIX.4 structure for a time value. This is like a `struct timeval' but
-;; has nanoseconds instead of microseconds.
-
+;; the POSIX.4 structure for a time value. This is like a `struct
+;; timeval' but has nanoseconds instead of microseconds.
 (def-alien-type nil
     (struct timespec
-           (tv-sec long)   ;Seconds
-           (tv-nsec long))) ;Nanoseconds
+           (tv-sec long)   ; seconds
+           (tv-nsec long))) ; nanoseconds
 
-;; Used by other time functions.
+;; used by other time functions
 (def-alien-type nil
     (struct tm
            (tm-sec int)   ; Seconds.   [0-60] (1 leap second)
     (tz-minuteswest int)               ; minutes west of Greenwich
     (tz-dsttime        int)))                  ; type of dst correction
 
+;;; If it works, UNIX-GETTIMEOFDAY returns 5 values: T, the seconds
+;;; and microseconds of the current time of day, the timezone (in
+;;; minutes west of Greenwich), and a daylight-savings flag. If it
+;;; doesn't work, it returns NIL and the errno.
 #!-sb-fluid (declaim (inline unix-gettimeofday))
 (defun unix-gettimeofday ()
-  #!+sb-doc
-  "If it works, unix-gettimeofday returns 5 values: T, the seconds and
-   microseconds of the current time of day, the timezone (in minutes west
-   of Greenwich), and a daylight-savings flag. If it doesn't work, it
-   returns NIL and the errno."
   (with-alien ((tv (struct timeval))
               (tz (struct timezone)))
     (syscall* ("gettimeofday" (* (struct timeval))
              (addr tv)
              (addr tz))))
 \f
-;;;; asm/errno.h
-
-#|
-(def-unix-error ESUCCESS 0 "Successful")
-(def-unix-error EPERM 1 "Operation not permitted")
-|#
-(def-unix-error ENOENT 2 "No such file or directory")
-#|
-(def-unix-error ESRCH 3 "No such process")
-|#
-(def-unix-error EINTR 4 "Interrupted system call")
-#|
-(def-unix-error EIO 5 "I/O error")
-(def-unix-error ENXIO 6 "No such device or address")
-(def-unix-error E2BIG 7 "Arg list too long")
-(def-unix-error ENOEXEC 8 "Exec format error")
-(def-unix-error EBADF 9 "Bad file number")
-(def-unix-error ECHILD 10 "No children")
-(def-unix-error EAGAIN 11 "Try again")
-(def-unix-error ENOMEM 12 "Out of memory")
-|#
-(def-unix-error EACCES 13 "Permission denied")
-#|
-(def-unix-error EFAULT 14 "Bad address")
-(def-unix-error ENOTBLK 15 "Block device required")
-(def-unix-error EBUSY 16 "Device or resource busy")
-|#
-(def-unix-error EEXIST 17 "File exists")
-#|
-(def-unix-error EXDEV 18 "Cross-device link")
-(def-unix-error ENODEV 19 "No such device")
-|#
-(def-unix-error ENOTDIR 20 "Not a directory")
-#|
-(def-unix-error EISDIR 21 "Is a directory")
-(def-unix-error EINVAL 22 "Invalid argument")
-(def-unix-error ENFILE 23 "File table overflow")
-(def-unix-error EMFILE 24 "Too many open files")
-(def-unix-error ENOTTY 25 "Not a typewriter")
-(def-unix-error ETXTBSY 26 "Text file busy")
-(def-unix-error EFBIG 27 "File too large")
-(def-unix-error ENOSPC 28 "No space left on device")
-|#
-(def-unix-error ESPIPE 29 "Illegal seek")
-#|
-(def-unix-error EROFS 30 "Read-only file system")
-(def-unix-error EMLINK 31 "Too many links")
-(def-unix-error EPIPE 32 "Broken pipe")
-|#
-
-#|
-;;; Math
-(def-unix-error EDOM 33 "Math argument out of domain")
-(def-unix-error ERANGE 34 "Math result not representable")
-(def-unix-error  EDEADLK        35     "Resource deadlock would occur")
-(def-unix-error  ENAMETOOLONG    36     "File name too long")
-(def-unix-error  ENOLCK          37     "No record locks available")
-(def-unix-error  ENOSYS          38     "Function not implemented")
-(def-unix-error  ENOTEMPTY       39     "Directory not empty")
-(def-unix-error  ELOOP    40     "Too many symbolic links encountered")
-|#
-(def-unix-error  EWOULDBLOCK     11     "Operation would block")
-(/show0 "unix.lisp 3192")
-#|
-(def-unix-error  ENOMSG          42     "No message of desired type")
-(def-unix-error  EIDRM    43     "Identifier removed")
-(def-unix-error  ECHRNG          44     "Channel number out of range")
-(def-unix-error  EL2NSYNC      45     "Level 2 not synchronized")
-(def-unix-error  EL3HLT          46     "Level 3 halted")
-(def-unix-error  EL3RST          47     "Level 3 reset")
-(def-unix-error  ELNRNG          48     "Link number out of range")
-(def-unix-error  EUNATCH        49     "Protocol driver not attached")
-(def-unix-error  ENOCSI          50     "No CSI structure available")
-(def-unix-error  EL2HLT          51     "Level 2 halted")
-(def-unix-error  EBADE    52     "Invalid exchange")
-(def-unix-error  EBADR    53     "Invalid request descriptor")
-(def-unix-error  EXFULL          54     "Exchange full")
-(def-unix-error  ENOANO          55     "No anode")
-(def-unix-error  EBADRQC        56     "Invalid request code")
-(def-unix-error  EBADSLT        57     "Invalid slot")
-(def-unix-error  EDEADLOCK       EDEADLK     "File locking deadlock error")
-(def-unix-error  EBFONT          59     "Bad font file format")
-(def-unix-error  ENOSTR          60     "Device not a stream")
-(def-unix-error  ENODATA        61     "No data available")
-(def-unix-error  ETIME    62     "Timer expired")
-(def-unix-error  ENOSR    63     "Out of streams resources")
-(def-unix-error  ENONET          64     "Machine is not on the network")
-(def-unix-error  ENOPKG          65     "Package not installed")
-(def-unix-error  EREMOTE        66     "Object is remote")
-(def-unix-error  ENOLINK        67     "Link has been severed")
-(def-unix-error  EADV      68     "Advertise error")
-(def-unix-error  ESRMNT          69     "Srmount error")
-(def-unix-error  ECOMM    70     "Communication error on send")
-(def-unix-error  EPROTO          71     "Protocol error")
-(def-unix-error  EMULTIHOP       72     "Multihop attempted")
-(def-unix-error  EDOTDOT        73     "RFS specific error")
-(def-unix-error  EBADMSG        74     "Not a data message")
-(def-unix-error  EOVERFLOW       75     "Value too large for defined data type")
-(def-unix-error  ENOTUNIQ      76     "Name not unique on network")
-(def-unix-error  EBADFD          77     "File descriptor in bad state")
-(def-unix-error  EREMCHG        78     "Remote address changed")
-(def-unix-error  ELIBACC        79     "Can not access a needed shared library")
-(def-unix-error  ELIBBAD        80     "Accessing a corrupted shared library")
-(def-unix-error  ELIBSCN        81     ".lib section in a.out corrupted")
-(def-unix-error  ELIBMAX        82     "Attempting to link in too many shared libraries")
-(def-unix-error  ELIBEXEC      83     "Cannot exec a shared library directly")
-(def-unix-error  EILSEQ          84     "Illegal byte sequence")
-(def-unix-error  ERESTART      85     "Interrupted system call should be restarted ")
-(def-unix-error  ESTRPIPE      86     "Streams pipe error")
-(def-unix-error  EUSERS          87     "Too many users")
-(def-unix-error  ENOTSOCK      88     "Socket operation on non-socket")
-(def-unix-error  EDESTADDRREQ    89     "Destination address required")
-(def-unix-error  EMSGSIZE      90     "Message too long")
-(def-unix-error  EPROTOTYPE      91     "Protocol wrong type for socket")
-(def-unix-error  ENOPROTOOPT     92     "Protocol not available")
-(def-unix-error  EPROTONOSUPPORT 93     "Protocol not supported")
-(def-unix-error  ESOCKTNOSUPPORT 94     "Socket type not supported")
-(def-unix-error  EOPNOTSUPP      95     "Operation not supported on transport endpoint")
-(def-unix-error  EPFNOSUPPORT    96     "Protocol family not supported")
-(def-unix-error  EAFNOSUPPORT    97     "Address family not supported by protocol")
-(def-unix-error  EADDRINUSE      98     "Address already in use")
-(def-unix-error  EADDRNOTAVAIL   99     "Cannot assign requested address")
-(def-unix-error  ENETDOWN      100    "Network is down")
-(def-unix-error  ENETUNREACH     101    "Network is unreachable")
-(def-unix-error  ENETRESET       102    "Network dropped connection because of reset")
-(def-unix-error  ECONNABORTED    103    "Software caused connection abort")
-(def-unix-error  ECONNRESET      104    "Connection reset by peer")
-(def-unix-error  ENOBUFS        105    "No buffer space available")
-(def-unix-error  EISCONN        106    "Transport endpoint is already connected")
-(def-unix-error  ENOTCONN      107    "Transport endpoint is not connected")
-(def-unix-error  ESHUTDOWN       108    "Cannot send after transport endpoint shutdown")
-(def-unix-error  ETOOMANYREFS    109    "Too many references: cannot splice")
-(def-unix-error  ETIMEDOUT       110    "Connection timed out")
-(def-unix-error  ECONNREFUSED    111    "Connection refused")
-(def-unix-error  EHOSTDOWN       112    "Host is down")
-(def-unix-error  EHOSTUNREACH    113    "No route to host")
-(def-unix-error  EALREADY      114    "Operation already in progress")
-(def-unix-error  EINPROGRESS     115    "Operation now in progress")
-(def-unix-error  ESTALE          116    "Stale NFS file handle")
-(def-unix-error  EUCLEAN        117    "Structure needs cleaning")
-(def-unix-error  ENOTNAM        118    "Not a XENIX named type file")
-(def-unix-error  ENAVAIL        119    "No XENIX semaphores available")
-(def-unix-error  EISNAM          120    "Is a named type file")
-(def-unix-error  EREMOTEIO       121    "Remote I/O error")
-(def-unix-error  EDQUOT          122    "Quota exceeded")
-|#
-
-;;; And now for something completely different ...
-(emit-unix-errors)
+
+(defconstant ENOENT 2) ; Unix error code, "No such file or directory"
+(defconstant EINTR 4) ; Unix error code, "Interrupted system call"
+(defconstant EIO 5) ; Unix error code, "I/O error"
+(defconstant EEXIST 17) ; Unix error code, "File exists"
+(defconstant ESPIPE 29) ; Unix error code, "Illegal seek"
+(defconstant EWOULDBLOCK 11) ; Unix error code, "Operation would block"
+;;; FIXME: Many Unix error code definitions were deleted from the old
+;;; CMU CL source code here, but not in the exports of SB-UNIX. I
+;;; (WHN) hope that someday I'll figure out an automatic way to detect
+;;; unused symbols in package exports, but if I don't, there are
+;;; enough of them all in one place here that they should probably be
+;;; removed by hand.
+\f
 \f
-;;;; support routines for dealing with unix pathnames
+;;;; support routines for dealing with Unix pathnames
 
 (defun unix-file-kind (name &optional check-for-links)
   #!+sb-doc
            (concatenate 'simple-string dir "/" name)
            name))))
 
+;;; Return the pathname with all symbolic links resolved.
+;;;
+;;; FIXME: Could we just use Unix readlink(2) instead?
 (defun unix-resolve-links (pathname)
-  #!+sb-doc
-  "Returns the pathname with all symbolic links resolved."
   (declare (simple-string pathname))
   (let ((len (length pathname))
        (pending pathname))
                (cond ((eq kind :link)
                       (multiple-value-bind (link err) (unix-readlink result)
                         (unless link
-                          (error "error reading link ~S: ~S"
-                                 (subseq result 0 fill-ptr)
-                                 (get-unix-error-msg err)))
+                          (error 'simple-file-error
+                                 :pathname pathname
+                                 :format-control
+                                 "~@<error reading link ~S: ~2I~_~A~:>"
+                                 :format-arguments (list (subseq
+                                                          result 0 fill-ptr)
+                                                         (strerror err))))
                         (cond ((or (zerop (length link))
                                    (char/= (schar link 0) #\/))
                                ;; It's a relative link.
      ,@(loop for index upfrom 0 below (/ fd-setsize 32)
         collect `(setf (deref (slot ,fd-set 'fds-bits) ,index) 0))))
 
-(/show0 "unix.lisp 3555")
+