e-mail on cmucl-help@cons.org on 2001-01-16 and 2001-01-17 from WHN
   and Pierre Mai.)
 
+79:
+  as pointed out by Dan Barlow on sbcl-devel 2000-07-02:
+  The PICK-TEMPORARY-FILE-NAME utility used by LOAD-FOREIGN uses
+  an easily guessable temporary filename in a way which might open
+  applications using LOAD-FOREIGN to hijacking by malicious users
+  on the same machine. Incantations for doing this safely are
+  floating around the net in various "how to write secure programs
+  despite Unix" documents, and it would be good to (1) fix this in 
+  LOAD-FOREIGN, and (2) hunt for any other code which uses temporary
+  files and make it share the same new safe logic.
+
 
 KNOWN BUGS RELATED TO THE IR1 INTERPRETER
 
 
  #s(sb-cold:package-data
     :name "SB!IMPL"
     :doc "private: a grab bag of implementation details"
-    :use ("CL" "SB!EXT" "SB!INT" "SB!SYS" "SB!DEBUG" "SB!KERNEL" "SB!BIGNUM"
-          "SB!GRAY"))
+    :use ("CL" "SB!ALIEN" "SB!BIGNUM" "SB!C-CALL" "SB!DEBUG" "SB!EXT"
+          "SB!GRAY" "SB!INT" "SB!KERNEL" "SB!SYS"))
 
  ;; FIXME: It seems to me that this could go away, with its contents moved
  ;; into SB!KERNEL, like the implementation of the rest of the class system.
     :reexport ("LOAD-FOREIGN" "LOAD-1-FOREIGN" "WEAK-POINTER-P")
     :export (;; Information about how the program was invoked is
              ;; nonstandard but very useful.
-             "*POSIX-ARGV*" "POSIX-GETENV"
+             "*POSIX-ARGV*" "POSIX-GETENV" "POSIX-ENVIRON"
 
              ;; People have various good reasons to mess with the GC.
              "*AFTER-GC-HOOKS*" "*BEFORE-GC-HOOKS*"
              "DEFINE-HASH-TABLE-TEST"
              "*GC-INHIBIT-HOOK*"
 
+             ;; compatibility hacks for old-style CMU CL data formats
+             "UNIX-ENVIRONMENT-CMUCL-FROM-SBCL"
+             "UNIX-ENVIRONMENT-SBCL-FROM-CMUCL"
+
              ;; not used any more, I think -- WHN 19991206
             #+nil
              ("SERVE-BUTTON-PRESS"
 This package only tries to implement what happens to be
 needed by the current implementation of SBCL, and makes
 no guarantees of interface stability."
-    :use ("CL" "SB!ALIEN" "SB!C-CALL" "SB!SYS" "SB!EXT" "SB!INT")
+    :use ("CL" "SB!ALIEN" "SB!C-CALL" "SB!EXT" "SB!INT" "SB!SYS")
     :export ("CADDR-T" "D-INO" "D-NAME" "D-NAMLEN" "D-OFF" "D-RECLEN"
              "DADDR-T" "DEV-T" "DIRECT" "EXECGRP" "EXECOTH" "EXECOWN" "F-DUPFD"
              "F-GETFD" "F-GETFL" "F-GETOWN" "F-SETFD" "F-SETFL" "F-SETOWN"
 
 ;;;; provided with absolutely no warranty. See the COPYING and CREDITS
 ;;;; files for more information.
 
-(in-package "SB!EXT")
+(in-package "SB!IMPL")
 
 ;;; Lots of code wants to get to the KEYWORD package or the
 ;;; COMMON-LISP package without a lot of fuss, so we cache them in
 
 ;;;; provided with absolutely no warranty. See the COPYING and CREDITS
 ;;;; files for more information.
 
-(in-package "SB!EXT")
+(in-package "SB!IMPL")
 
 ;;; something not EQ to anything we might legitimately READ
 (defparameter *eof-object* (make-symbol "EOF-OBJECT"))
 
 ;;;; provided with absolutely no warranty. See the COPYING and CREDITS
 ;;;; files for more information.
 
-(in-package "SB!EXT")
+(in-package "SB!IMPL")
 
 (defvar *objects-pending-finalization* nil)
 
 
 
 (in-package "SB-SYS")
 
-;;; not needed until we implement full-blown LOAD-FOREIGN
-#|
 (defun pick-temporary-file-name (&optional
                                 ;; KLUDGE: There are various security
                                 ;; nastyisms associated with easily
                 (return nil))
                (t
                 (incf code))))))))
-|#
+
 
 ;;; On any OS where we don't support foreign object file loading, any
 ;;; query of a foreign symbol value is answered with "no definition
 (push (lambda () (setq *tables-from-dlopen* nil))
       sb-int:*after-save-initializations*)
 
-;;; not needed until we implement full-blown LOAD-FOREIGN
-#|
 (defvar *dso-linker* "/usr/bin/ld")
 (defvar *dso-linker-options* '("-G" "-o"))
-|#
+
 
 (sb-alien:def-alien-routine dlopen system-area-pointer
   (file sb-c-call:c-string) (mode sb-c-call:int))
       (error "can't open global symbol table: ~S" (dlerror)))))
 
 (defun load-1-foreign (file)
-  "a primitive way to load a foreign object file. (LOAD-FOREIGN is
-  probably preferred, but as of SBCL 0.6.7 is not implemented..)
+  "the primitive upon which the more general LOAD-FOREIGN is built: load
+  a single foreign object file
 
   To use LOAD-1-FOREIGN, at the Unix command line do this:
     echo 'int summish(int x, int y) { return 1 + x + y; }' > /tmp/ffi-test.c
       (unless (zerop possible-result)
        (return possible-result)))))
 
-;;; code partially ported from CMU CL to SBCL, but needs RUN-PROGRAM
-#|
-(defun load-foreign (files &key
-                          (libraries '("-lc"))
-                          (base-file nil)
-                          ;; Note: Since SBCL has no *ENVIRONMENT-LIST*
-                          ;; variable, if this code is ever restored,
-                          ;; the default should be taken from the alien
-                          ;; "environ" variable.
-                          ,, ; do it!
-                          (env sb-ext:*environment-list*))
+(defun load-foreign (files
+                    &key
+                    (libraries '("-lc"))
+                    ;; FIXME: The old documentation said
+                    ;;   The BASE-FILE argument is used to specify a
+                    ;;   file to use as the starting place for
+                    ;;   defined symbols. The default is the C start
+                    ;;   up code for Lisp.
+                    ;; But the code ignored the BASE-FILE argument.
+                    ;; The comment above
+                    ;;   (DECLARE (IGNORE BASE-FILE))
+                    ;; said
+                    ;;   dlopen() remembers the name of an object,
+                    ;;   when dlopen()ing the same name twice, the
+                    ;;   old object is reused.
+                    ;; So I deleted all reference to BASE-FILE,
+                    ;; including the now-bogus reference to the
+                    ;; BASE-FILE argument in the documentation. But
+                    ;; are there any other subtleties of the new code
+                    ;; which need to be documented in its place?
+                    (env nil env-p)
+                    (environment (if env-p
+                                     (unix-environment-sbcl-from-cmu env)
+                                     (posix-environ))
+                                 environment-p))
   #+sb-doc
   "LOAD-FOREIGN loads a list of C object files into a running Lisp. The FILES
   argument should be a single file or a list of files. The files may be
   specified as namestrings or as pathnames. The libraries argument should be a
   list of library files as would be specified to ld. They will be searched in
   the order given. The default is just \"-lc\", i.e., the C library. The
-  base-file argument is used to specify a file to use as the starting place for
-  defined symbols. The default is the C start up code for Lisp. The ENV
-  argument is the Unix environment variable definitions for the invocation of
-  the linker. The default is the environment passed to Lisp."
-  ;; Note: dlopen() remembers the name of an object, when dlopen()ing
-  ;; the same name twice, the old object is reused.
-  (declare (ignore base-file))
+  ENVIRONMENT argument is a list of SIMPLE-STRINGs corresponding to the Unix
+  environment (\"man environ\") definitions for the invocation of the linker.
+  The default is the environment that Lisp is itself running in. Instead of
+  using the ENVIRONMENT argument, it is also possible to use the ENV argument,
+  using the alternate, lossy representation used by CMU CL."
+  (when (and env-p environment-p)
+    (error "can't specify :ENV and :ENVIRONMENT simultaneously"))
   (let ((output-file (pick-temporary-file-name
                      (concatenate 'string "/tmp/~D~C" (string (gensym)))))
        (error-output (make-string-output-stream)))
                                                 (list files)
                                               files))
                                     libraries))
-                    :env env
+                    :environment environment
                     :input nil
                     :output error-output
                     :error :output)))
          (load-1-foreign output-file))
       #-sb-show (sb-unix:unix-unlink output-file)
       #+sb-show (/show "not unlinking" output-file)))) ; so we can look at it
-|#
 
 ) ; PROGN
 
 ;;;; provided with absolutely no warranty. See the COPYING and CREDITS
 ;;;; files for more information.
 
-(in-package "SB!EXT")
+(in-package "SB!IMPL")
 
 (defparameter *abbrev-weekday-table*
   #("Mon" "Tue" "Wed" "Thu" "Fri" "Sat" "Sun"))
 
 ;;;; provided with absolutely no warranty. See the COPYING and CREDITS
 ;;;; files for more information.
 
-(in-package "SB!EXT")
+(in-package "SB!IMPL")
 
 (defun featurep (x)
   #!+sb-doc
 
 ;;;; provided with absolutely no warranty. See the COPYING and CREDITS
 ;;;; files for more information.
 
-(in-package "SB-EXT")
+(in-package "SB-IMPL")
+\f
+;;;; hacking the Unix environment
+;;;;
+;;;; In the original CMU CL code that LOAD-FOREIGN is derived from, the
+;;;; Unix environment (as in "man environ") was represented as an
+;;;; alist from keywords to strings, so that e.g. the Unix environment
+;;;;   "SHELL=/bin/bash" "HOME=/root" "PAGER=less"
+;;;; was represented as
+;;;;   ((:SHELL . "/bin/bash") (:HOME . "/root") (:PAGER "less"))
+;;;; This had a few problems in principle: the mapping into
+;;;; keyword symbols smashed the case of environment
+;;;; variables, and the whole mapping depended on the presence of
+;;;; #\= characters in the environment strings. In practice these
+;;;; problems weren't hugely important, since conventionally environment
+;;;; variables are uppercase strings followed by #\= followed by
+;;;; arbitrary data. However, since it's so manifestly not The Right
+;;;; Thing to make code which breaks unnecessarily on input which
+;;;; doesn't follow what is, after all, only a tradition, we've switched
+;;;; formats in SBCL, so that the fundamental environment list
+;;;; is just a list of strings, with a one-to-one-correspondence
+;;;; to the C-level representation. I.e., in the example above,
+;;;; the SBCL representation is
+;;;;   '("SHELL=/bin/bash" "HOME=/root" "PAGER=less")
+;;;; CMU CL's implementation is currently supported to help with porting.
+;;;;
+;;;; It's not obvious that this code belongs here (instead of e.g. in
+;;;; unix.lisp), since it has only a weak logical connection with
+;;;; RUN-PROGRAM. However, physically it's convenient to put it here.
+;;;; It's not needed at cold init, so we *can* put it in this
+;;;; warm-loaded file. And by putting it in this warm-loaded file, we
+;;;; make it easy for it to get to the C-level 'environ' variable.
+;;;; which (at least in sbcl-0.6.10 on Red Hat Linux 6.2) is not
+;;;; visible at GENESIS time.
+
+(def-alien-variable "environ" (* c-string))
+
+(defun posix-environ ()
+  "Return the Unix environment (\"man environ\") as a list of SIMPLE-STRINGs."
+  (let ((reversed-result nil))
+    (dotimes (i most-positive-fixnum (error "can't happen"))
+      (declare (type index i))
+      (let ((env-item (deref environ i)))
+       (if env-item
+            (push env-item reversed-result)
+           (return (nreverse reversed-result)))))))
+
+;;; Convert as best we can from a SBCL representation of a Unix
+;;; environment to a CMU CL representation.
+;;;
+;;; * (UNIX-ENVIRONMENT-CMUCL-FROM-SBCL '("Bletch=fub" "Noggin" "YES=No!"))
+;;; WARNING:
+;;;   smashing case of "Bletch=fub" in conversion to CMU-CL-style
+;;;     environment alist
+;;; WARNING:
+;;;   no #\= in "Noggin", eliding it in CMU-CL-style environment alist
+;;; ((:BLETCH . "fub") (:YES . "No!"))
+(defun unix-environment-cmucl-from-sbcl (sbcl)
+  (mapcan
+   (lambda (string)
+     (declare (type simple-string string))
+     (let ((=-pos (position #\= string :test #'equal)))
+       (if =-pos
+          (list
+           (let* ((key-as-string (subseq string 0 =-pos))
+                  (key-as-upcase-string (string-upcase key-as-string))
+                  (key (keywordicate key-as-upcase-string))
+                  (val (subseq string (1+ =-pos))))
+             (unless (string= key-as-string key-as-upcase-string)
+               (warn "smashing case of ~S in conversion to CMU-CL-style ~
+                      environment alist"
+                     string))
+             (cons key val)))
+          (warn "no #\\= in ~S, eliding it in CMU-CL-style environment alist"
+                string))))
+   sbcl))
+
+;;; Convert from a CMU CL representation of a Unix environment to a
+;;; SBCL representation.
+(defun unix-environment-sbcl-from-cmucl (cmucl)
+  (mapcar
+   (lambda (cons)
+     (destructuring-bind (key . val) cons
+       (declare (type keyword key) (type simple-string val))
+       (concatenate 'simple-string (symbol-name key) "=" val)))
+   cmucl))
 \f
 ;;;; Import wait3(2) from Unix.
 
   (stdout sb-c-call:int)
   (stderr sb-c-call:int))
 
+;;; FIXME: There shouldn't be two semiredundant versions of the
+;;; documentation. Since this is a public extension function, the
+;;; documentation should be in the doc string. So all information from
+;;; this comment should be merged into the doc string, and then this
+;;; comment can go away.
+;;;
 ;;; RUN-PROGRAM uses fork() and execve() to run a different program.
 ;;; Strange stuff happens to keep the Unix state of the world
 ;;; coherent.
 ;;; RUN-PROGRAM returns a PROCESS structure for the process if
 ;;; the fork worked, and NIL if it did not.
 (defun run-program (program args
-                   &key env (wait t) pty input
-                   if-input-does-not-exist output (if-output-exists :error)
-                   (error :output) (if-error-exists :error) status-hook)
-  "RUN-PROGRAM creates a new process and runs the unix progam in the
-   file specified by the simple-string program.  Args are the standard
-   arguments that can be passed to a Unix program, for no arguments
-   use NIL (which means just the name of the program is passed as arg 0).
+                   &key
+                   (env nil env-p)
+                   (environment (if env-p
+                                    (unix-environment-sbcl-from-cmucl env)
+                                    (posix-environ))
+                                environment-p)
+                   (wait t)
+                   pty
+                   input
+                   if-input-does-not-exist
+                   output
+                   (if-output-exists :error)
+                   (error :output)
+                   (if-error-exists :error)
+                   status-hook)
+  "RUN-PROGRAM creates a new Unix process running the Unix program found in
+   the file specified by the PROGRAM argument.  ARGS are the standard
+   arguments that can be passed to a Unix program. For no arguments, use NIL
+   (which means that just the name of the program is passed as arg 0).
 
    RUN-PROGRAM will either return NIL or a PROCESS structure.  See the CMU
    Common Lisp Users Manual for details about the PROCESS structure.
 
+   notes about Unix environments (as in the :ENVIRONMENT and :ENV args):
+     1. The SBCL implementation of RUN-PROGRAM, like Perl and many other
+        programs, but unlike the original CMU CL implementation, copies
+        the Unix environment by default.
+     2. Running Unix programs from a setuid process, or in any other
+        situation where the Unix environment is under the control of someone
+        else, is a mother lode of security problems. If you are contemplating
+        doing this, read about it first. (The Perl community has a lot of good
+        documentation about this and other security issues in script-like
+        programs.)
+
    The keyword arguments have the following meanings:
+     :ENVIRONMENT
+        a list of SIMPLE-STRINGs describing the new Unix environment (as
+        in \"man environ\"). The default is to copy the environment of
+        the current process.
      :ENV
-        An A-LIST mapping keyword environment variables to simple-string
-       values.
+        an alternative lossy representation of the new Unix environment,
+        for compatibility with CMU CL
      :WAIT
         If non-NIL (default), wait until the created process finishes.  If
         NIL, continue running Lisp until the program finishes.
         This is a function the system calls whenever the status of the
         process changes.  The function takes the process as an argument."
 
+  (when (and env-p environment-p)
+    (error "can't specify :ENV and :ENVIRONMENT simultaneously"))
   ;; Make sure that the interrupt handler is installed.
   (sb-sys:enable-interrupt sb-unix:sigchld #'sigchld-handler)
   ;; Make sure that all the args are okay.
     (error "All arguments to program must be simple strings: ~S" args))
   ;; Prepend the program to the argument list.
   (push (namestring program) args)
-  ;; Clear various specials used by GET-DESCRIPTOR-FOR to communicate
-  ;; cleanup info.  Also, establish proc at this level so we can
-  ;; return it.
-  (let (*close-on-error* *close-in-parent* *handlers-installed* proc)
+  (let (;; Clear various specials used by GET-DESCRIPTOR-FOR to
+       ;; communicate cleanup info.
+       *close-on-error*
+       *close-in-parent*
+       *handlers-installed*
+       ;; Establish PROC at this level so that we can return it.
+       proc)
     (unwind-protect
-        (let ((pfile (unix-namestring (merge-pathnames program "path:") t t))
+        (let (;; FIXME: The old code here used to do
+              ;;   (MERGE-PATHNAMES PROGRAM "path:"),
+              ;; which is the right idea (searching through the Unix
+              ;; PATH). Unfortunately, there is no logical pathname
+              ;; "path:" defined in sbcl-0.6.10. It would probably be 
+              ;; reasonable to restore Unix PATH searching in SBCL, e.g.
+              ;; with a function FIND-EXECUTABLE-FILE-IN-POSIX-PATH.
+              ;; (I don't want to do it with search lists the way
+              ;; that CMU CL did, because those are a non-ANSI
+              ;; extension which I'd like to get rid of. -- WHN)
+              (pfile (unix-namestring program t t))
               (cookie (list 0)))
           (unless pfile
             (error "no such program: ~S" program))
-          (multiple-value-bind
-                (stdin input-stream)
+          (multiple-value-bind (stdin input-stream)
               (get-descriptor-for input cookie :direction :input
                                   :if-does-not-exist if-input-does-not-exist)
-            (multiple-value-bind
-                  (stdout output-stream)
+            (multiple-value-bind (stdout output-stream)
                 (get-descriptor-for output cookie :direction :output
                                     :if-exists if-output-exists)
-              (multiple-value-bind
-                    (stderr error-stream)
+              (multiple-value-bind (stderr error-stream)
                   (if (eq error :output)
                       (values stdout output-stream)
                       (get-descriptor-for error cookie :direction :output
                   ;; death before we have installed the PROCESS
                   ;; structure in *ACTIVE-PROCESSES*.
                   (sb-sys:without-interrupts
-                   (with-c-strvec (argv args)
-                     (with-c-strvec
-                         (envp (mapcar #'(lambda (entry)
-                                           (concatenate
-                                            'string
-                                            (symbol-name (car entry))
-                                            "="
-                                            (cdr entry)))
-                                       env))
+                   (with-c-strvec (args-vec args)
+                     (with-c-strvec (environment-vec environment)
                        (let ((child-pid
                               (without-gcing
-                               (spawn pfile argv envp pty-name
+                               (spawn pfile args-vec environment-vec pty-name
                                       stdin stdout stderr))))
                          (when (< child-pid 0)
                            (error "could not fork child process: ~S"
 
 ;;;; provided with absolutely no warranty. See the COPYING and CREDITS
 ;;;; files for more information.
 
-(in-package "SB!EXT")
+(in-package "SB!IMPL")
+\f
+;;;; miscellaneous I/O
 
 ;;; INDENTING-FURTHER is a user-level macro which may be used to locally
 ;;; increment the indentation of a stream.
 
-;;;; 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
         (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)))
 (defmacro int-syscall ((name &rest arg-types) &rest args)
   `(syscall (,name ,@arg-types) (values result 0) ,@args))
 \f
-;;; from stdio.h
+;;;; hacking the Unix environment
 
-(/show0 "unix.lisp 124")
+(/show0 "unix.lisp 122")
+
+(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
 
 (defun unix-rename (name1 name2)
   #!+sb-doc
   (declare (type unix-pathname name1 name2))
   (void-syscall ("rename" c-string c-string) name1 name2))
 \f
-;;; from stdlib.h
-
-(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 sys/types.h and gnu/types.h
 
-(/show0 "unix.lisp 144")
+(/show0 "unix.lisp 220")
 
 (defconstant +max-s-long+ 2147483647)
 (defconstant +max-u-long+ 4294967295)
 (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
 (def-alien-type nlink-t
   #!+linux unsigned-int
   #!+bsd   unsigned-short)
-(/show0 "unix.lisp 190")
+(/show0 "unix.lisp 263")
 
 ;;; FIXME: We shouldn't hand-copy types from header files into Lisp like this
 ;;; unless we have extreme provocation. Reading directories is not extreme
   #!+linux long
   #!+bsd   quad-t)
 
-(/show0 "unix.lisp 195")
 (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")
 \f
 ;;;; direntry.h
 
     (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")
+(/show0 "unix.lisp 289")
 \f
 ;;;; dirent.h
 
 (defstruct directory
   name
   (dir-struct (required-argument) :type system-area-pointer))
-(/show0 "unix.lisp 258")
+(/show0 "unix.lisp 304")
 
 (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 "")
        (values nil enoent))
       (t
        (values nil enotdir)))))
-(/show0 "unix.lisp 286")
 
 (defun read-dir (dir)
   (declare (type directory dir))
          (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)
+\f
+;;;; fcntl.h
+;;;;
+;;;; POSIX Standard: 6.5 File Control Operations       <fcntl.h>
 
-;;; dlfcn.h -> in foreign.lisp
-
-;;; fcntl.h
-;;;
-;;; POSIX Standard: 6.5 File Control Operations        <fcntl.h>
-
-(/show0 "unix.lisp 318")
+(/show0 "unix.lisp 356")
 (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")
 
-(/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
+;;;; fcntlbits.h
 
 (/show0 "unix.lisp 337")
 (defconstant o_rdonly  0) ; read-only flag
   (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-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
 \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
 
-;; 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)
 
 ;;;; provided with absolutely no warranty. See the COPYING and CREDITS
 ;;;; files for more information.
 
-(in-package "SB!EXT")
+(in-package "SB!IMPL")
 
 (defun make-weak-pointer (object)
   #!+sb-doc
-  "Allocates and returns a weak pointer which points to OBJECT."
+  "Allocate and return a weak pointer which points to OBJECT."
   (declare (values weak-pointer))
   (make-weak-pointer object))
 
 #!-sb-fluid (declaim (inline weak-pointer-value))
 (defun weak-pointer-value (weak-pointer)
   #!+sb-doc
-  "If WEAK-POINTER is valid, returns the value of WEAK-POINTER and T.
+  "If WEAK-POINTER is valid, return the value of WEAK-POINTER and T.
    If the referent of WEAK-POINTER has been garbage collected, returns
    the values NIL and NIL."
   (declare (type weak-pointer weak-pointer)
           (values t (member t nil)))
-  ;; We don't need to wrap this with a without-gcing, because once we have
-  ;; extracted the value, our reference to it will keep the weak pointer
-  ;; from becoming broken. We just have to make sure the compiler won't
-  ;; reorder these primitives.
+  ;; We don't need to wrap this with a WITHOUT-GCING, because once we
+  ;; have extracted the value, our reference to it will keep the weak
+  ;; pointer from becoming broken. We just have to make sure the
+  ;; compiler won't reorder these primitives.
+  ;;
+  ;; FIXME: Might it be a good idea to tweak the DEFKNOWNs for
+  ;; %WEAK-POINTER-VALUE and %WEAK-POINTER-BROKEN, so that the
+  ;; compiler will never try to reorder them even in code where we
+  ;; neglect to frame them in a LET?
   (let ((value (sb!c::%weak-pointer-value weak-pointer))
        (broken (sb!c::%weak-pointer-broken weak-pointer)))
     (values value (not broken))))
 
         .text
 
 #define LDSO_STUBIFY(fct) \
-       .align 16 ;\
-.globl ldso_stub__ ## fct ;\
-       .type    ldso_stub__ ## fct,@function ;\
-ldso_stub__ ## fct: ;\
-       jmp fct ;\
-.L ## fct ## e1: ;\
-       .size    ldso_stub__ ## fct,.L ## fct ## e1-ldso_stub__ ## fct  ;
+       .align 16 ; \
+.globl ldso_stub__ ## fct ; \
+       .type    ldso_stub__ ## fct,@function ; \
+ldso_stub__ ## fct: ; \
+       jmp fct ; \
+.L ## fct ## e1: ; \
+       .size    ldso_stub__ ## fct,.L ## fct ## e1-ldso_stub__ ## fct ;
 
  LDSO_STUBIFY(accept)
  LDSO_STUBIFY(access)
 /*  LDSO_STUBIFY(endspent) */
 /*  LDSO_STUBIFY(endusershell) */
 /*  LDSO_STUBIFY(endutent) */
-/*  LDSO_STUBIFY(environ) */
 /*  LDSO_STUBIFY(erand48) */
 /*  LDSO_STUBIFY(erf) */
 /*  LDSO_STUBIFY(erfc) */
 
--- /dev/null
+#!/bin/sh
+
+# tests related to foreign function interface and LOAD-FOREIGN
+
+# This software is part of the SBCL system. See the README file for
+# more information.
+#
+# While most of SBCL is derived from the CMU CL system, the test
+# files (like this one) were written from scratch after the fork
+# from CMU CL.
+# 
+# This software is in the public domain and is provided with
+# absolutely no warranty. See the COPYING and CREDITS files for
+# more information.
+
+testfilestem=$TMPDIR/sbcl-foreign-test-$$
+
+echo 'int summish(int x, int y) { return 1 + x + y; }' > $testfilestem.c
+make $testfilestem.o
+ld -shared -o $testfilestem.so $testfilestem.o
+
+sbcl --noinform --noprint --sysinit /dev/null --userinit /dev/null <<EOF
+  (load-foreign '("$testfilestem.so"))
+  (def-alien-routine summish int (x int) (y int))
+  (assert (= (summish 10 20) 31))
+  (sb-ext:quit :unix-status 52) ; success convention for Lisp program
+EOF
+if [ $? != 52 ]; then
+    echo test failed: $?
+    exit 1
+fi
+
+# FIXME: I rewrote the handling of ENV/ENVIRONMENT arguments for
+# LOAD-FOREIGN, but I can't think of a nice way to test it. (Kent Beck
+# would cry. If he didn't keel over on the spot and then commence
+# rolling over in his grave.:-) It would be good to make a test case
+# for it..
+
+# success convention for script
+exit 104
 
 # absolutely no warranty. See the COPYING and CREDITS files for
 # more information.
 
+# Make sure that there's at least something in the environment (for
+# one of the tests below).
+export SOMETHING_IN_THE_ENVIRONMENT='yes there is'
+
 sbcl --noinform --noprint --sysinit /dev/null --userinit /dev/null <<EOF
   (let ((string (with-output-to-string (stream)
                   (sb-ext:run-program "/bin/echo"
                                       '("foo" "bar")
                                       :output stream))))
-    (when (string= string "foo bar
-")
-      (sb-ext:quit :unix-status 52)))
+    (assert (string= string "foo bar
+")))
+  ;; Unix environment strings are ordinarily passed with SBCL convention
+  ;; (instead of CMU CL alist-of-keywords convention).
+  (let ((string (with-output-to-string (stream)
+                  (sb-ext:run-program "/usr/bin/env" ()
+                                     :output stream
+                                     :environment '("FEEFIE=foefum")))))
+    (assert (string= string "FEEFIE=foefum
+")))
+  ;; The default Unix environment for the subprocess is the same as
+  ;; for the parent process. (I.e., we behave like perl and lots of
+  ;; other programs, but not like CMU CL.)
+  (let ((string (with-output-to-string (stream)
+                  (sb-ext:run-program "/usr/bin/env" ()
+                                     :output stream)))
+       (expected (apply #'concatenate
+                        'string
+                        (mapcar (lambda (environ-string)
+                                  (concatenate 'string
+                                               environ-string
+                                               (string #\newline)))
+                                 (sb-ext:posix-environ)))))
+    (assert (string= string expected)))
+  ;; That's not just because POSIX-ENVIRON is having a bad hair
+  ;; day and returning NIL, is it?
+  (assert (plusp (length (sb-ext:posix-environ))))
+  ;; success convention for this Lisp program run as part of a larger script
+  (sb-ext:quit :unix-status 52)))
 EOF
 if [ $? != 52 ]; then
     echo test failed: $?
 
 ;;; versions, and a string like "0.6.5.12" is used for versions which
 ;;; aren't released but correspond only to CVS tags or snapshots.
 
-"0.6.10.1"
+"0.6.10.2"