0.9.2.43:
[sbcl.git] / src / code / print.lisp
index 205afbe..9da5da1 100644 (file)
@@ -21,7 +21,7 @@
   "If true, all objects will printed readably. If readable printing is
   impossible, an error will be signalled. This overrides the value of
   *PRINT-ESCAPE*.")
-(defvar *print-escape* T
+(defvar *print-escape* t
   #!+sb-doc
   "Should we print in a reasonably machine-readable way? (possibly
   overridden by *PRINT-READABLY*)")
    is less than this, then print using ``miser-style'' output. Miser
    style conditional newlines are turned on, and all indentations are
    turned off. If NIL, never use miser mode.")
-(defvar *print-pprint-dispatch* nil
-  #!+sb-doc
-  "the pprint-dispatch-table that controls how to pretty-print objects")
+(defvar *print-pprint-dispatch*)
+#!+sb-doc
+(setf (fdocumentation '*print-pprint-dispatch* 'variable)
+      "the pprint-dispatch-table that controls how to pretty-print objects")
 
 (defmacro with-standard-io-syntax (&body body)
   #!+sb-doc
   "Bind the reader and printer control variables to values that enable READ
    to reliably read the results of PRINT. These values are:
-       *PACKAGE*                       the COMMON-LISP-USER package
-       *PRINT-ARRAY*                   T
-       *PRINT-BASE*                    10
-       *PRINT-CASE*                    :UPCASE
-       *PRINT-CIRCLE*                  NIL
-       *PRINT-ESCAPE*                  T
-       *PRINT-GENSYM*                  T
-       *PRINT-LENGTH*                  NIL
-       *PRINT-LEVEL*                   NIL
-       *PRINT-LINES*                   NIL
-       *PRINT-MISER-WIDTH*             NIL
-       *PRINT-PRETTY*                  NIL
-       *PRINT-RADIX*                   NIL
-       *PRINT-READABLY*                        T
-       *PRINT-RIGHT-MARGIN*            NIL
-       *READ-BASE*                     10
-       *READ-DEFAULT-FLOAT-FORMAT*     SINGLE-FLOAT
-       *READ-EVAL*                     T
-       *READ-SUPPRESS*                 NIL
-       *READTABLE*                     the standard readtable"
-  `(%with-standard-io-syntax #'(lambda () ,@body)))
+       *PACKAGE*                        the COMMON-LISP-USER package
+       *PRINT-ARRAY*                    T
+       *PRINT-BASE*                     10
+       *PRINT-CASE*                     :UPCASE
+       *PRINT-CIRCLE*                   NIL
+       *PRINT-ESCAPE*                   T
+       *PRINT-GENSYM*                   T
+       *PRINT-LENGTH*                   NIL
+       *PRINT-LEVEL*                    NIL
+       *PRINT-LINES*                    NIL
+       *PRINT-MISER-WIDTH*              NIL
+       *PRINT-PRETTY*                   NIL
+       *PRINT-RADIX*                    NIL
+       *PRINT-READABLY*                 T
+       *PRINT-RIGHT-MARGIN*             NIL
+       *READ-BASE*                      10
+       *READ-DEFAULT-FLOAT-FORMAT*      SINGLE-FLOAT
+       *READ-EVAL*                      T
+       *READ-SUPPRESS*                  NIL
+       *READTABLE*                      the standard readtable"
+  `(%with-standard-io-syntax (lambda () ,@body)))
 
 (defun %with-standard-io-syntax (function)
+  (declare (type function function))
   (let ((*package* (find-package "COMMON-LISP-USER"))
-       (*print-array* t)
-       (*print-base* 10)
-       (*print-case* :upcase)
-       (*print-circle* nil)
-       (*print-escape* t)
-       (*print-gensym* t)
-       (*print-length* nil)
-       (*print-level* nil)
-       (*print-lines* nil)
-       (*print-miser-width* nil)
-       (*print-pretty* nil)
-       (*print-radix* nil)
-       (*print-readably* t)
-       (*print-right-margin* nil)
-       (*read-base* 10)
-       (*read-default-float-format* 'single-float)
-       (*read-eval* t)
-       (*read-suppress* nil)
-       ;; FIXME: It doesn't seem like a good idea to expose our
-       ;; disaster-recovery *STANDARD-READTABLE* here. What if some
-       ;; enterprising user corrupts the disaster-recovery readtable
-       ;; by doing destructive readtable operations within
-       ;; WITH-STANDARD-IO-SYNTAX? Perhaps we should do a
-       ;; COPY-READTABLE? The consing would be unfortunate, though.
-       (*readtable* *standard-readtable*))
+        (*print-array* t)
+        (*print-base* 10)
+        (*print-case* :upcase)
+        (*print-circle* nil)
+        (*print-escape* t)
+        (*print-gensym* t)
+        (*print-length* nil)
+        (*print-level* nil)
+        (*print-lines* nil)
+        (*print-miser-width* nil)
+        (*print-pretty* nil)
+        (*print-radix* nil)
+        (*print-readably* t)
+        (*print-right-margin* nil)
+        (*read-base* 10)
+        (*read-default-float-format* 'single-float)
+        (*read-eval* t)
+        (*read-suppress* nil)
+        ;; FIXME: It doesn't seem like a good idea to expose our
+        ;; disaster-recovery *STANDARD-READTABLE* here. What if some
+        ;; enterprising user corrupts the disaster-recovery readtable
+        ;; by doing destructive readtable operations within
+        ;; WITH-STANDARD-IO-SYNTAX? Perhaps we should do a
+        ;; COPY-READTABLE? The consing would be unfortunate, though.
+        (*readtable* *standard-readtable*))
     (funcall function)))
 \f
 ;;;; routines to print objects
 
 (defun write (object &key
-                    ((:stream stream) *standard-output*)
-                    ((:escape *print-escape*) *print-escape*)
-                    ((:radix *print-radix*) *print-radix*)
-                    ((:base *print-base*) *print-base*)
-                    ((:circle *print-circle*) *print-circle*)
-                    ((:pretty *print-pretty*) *print-pretty*)
-                    ((:level *print-level*) *print-level*)
-                    ((:length *print-length*) *print-length*)
-                    ((:case *print-case*) *print-case*)
-                    ((:array *print-array*) *print-array*)
-                    ((:gensym *print-gensym*) *print-gensym*)
-                    ((:readably *print-readably*) *print-readably*)
-                    ((:right-margin *print-right-margin*)
-                     *print-right-margin*)
-                    ((:miser-width *print-miser-width*)
-                     *print-miser-width*)
-                    ((:lines *print-lines*) *print-lines*)
-                    ((:pprint-dispatch *print-pprint-dispatch*)
-                     *print-pprint-dispatch*))
+                     ((:stream stream) *standard-output*)
+                     ((:escape *print-escape*) *print-escape*)
+                     ((:radix *print-radix*) *print-radix*)
+                     ((:base *print-base*) *print-base*)
+                     ((:circle *print-circle*) *print-circle*)
+                     ((:pretty *print-pretty*) *print-pretty*)
+                     ((:level *print-level*) *print-level*)
+                     ((:length *print-length*) *print-length*)
+                     ((:case *print-case*) *print-case*)
+                     ((:array *print-array*) *print-array*)
+                     ((:gensym *print-gensym*) *print-gensym*)
+                     ((:readably *print-readably*) *print-readably*)
+                     ((:right-margin *print-right-margin*)
+                      *print-right-margin*)
+                     ((:miser-width *print-miser-width*)
+                      *print-miser-width*)
+                     ((:lines *print-lines*) *print-lines*)
+                     ((:pprint-dispatch *print-pprint-dispatch*)
+                      *print-pprint-dispatch*))
   #!+sb-doc
   "Output OBJECT to the specified stream, defaulting to *STANDARD-OUTPUT*"
   (output-object object (out-synonym-of stream))
   #!+sb-doc
   "Output a mostly READable printed representation of OBJECT on the specified
   STREAM."
-  (let ((*print-escape* T))
+  (let ((*print-escape* t))
     (output-object object (out-synonym-of stream)))
   object)
 
   #!+sb-doc
   "Output an aesthetic but not necessarily READable printed representation
   of OBJECT on the specified STREAM."
-  (let ((*print-escape* NIL)
-       (*print-readably* NIL))
+  (let ((*print-escape* nil)
+        (*print-readably* nil))
     (output-object object (out-synonym-of stream)))
   object)
 
   #!+sb-doc
   "Prettily output OBJECT preceded by a newline."
   (let ((*print-pretty* t)
-       (*print-escape* t)
-       (stream (out-synonym-of stream)))
+        (*print-escape* t)
+        (stream (out-synonym-of stream)))
     (terpri stream)
     (output-object object stream))
   (values))
 
 (defun write-to-string
        (object &key
-              ((:escape *print-escape*) *print-escape*)
-              ((:radix *print-radix*) *print-radix*)
-              ((:base *print-base*) *print-base*)
-              ((:circle *print-circle*) *print-circle*)
-              ((:pretty *print-pretty*) *print-pretty*)
-              ((:level *print-level*) *print-level*)
-              ((:length *print-length*) *print-length*)
-              ((:case *print-case*) *print-case*)
-              ((:array *print-array*) *print-array*)
-              ((:gensym *print-gensym*) *print-gensym*)
-              ((:readably *print-readably*) *print-readably*)
-              ((:right-margin *print-right-margin*) *print-right-margin*)
-              ((:miser-width *print-miser-width*) *print-miser-width*)
-              ((:lines *print-lines*) *print-lines*)
-              ((:pprint-dispatch *print-pprint-dispatch*)
-               *print-pprint-dispatch*))
+               ((:escape *print-escape*) *print-escape*)
+               ((:radix *print-radix*) *print-radix*)
+               ((:base *print-base*) *print-base*)
+               ((:circle *print-circle*) *print-circle*)
+               ((:pretty *print-pretty*) *print-pretty*)
+               ((:level *print-level*) *print-level*)
+               ((:length *print-length*) *print-length*)
+               ((:case *print-case*) *print-case*)
+               ((:array *print-array*) *print-array*)
+               ((:gensym *print-gensym*) *print-gensym*)
+               ((:readably *print-readably*) *print-readably*)
+               ((:right-margin *print-right-margin*) *print-right-margin*)
+               ((:miser-width *print-miser-width*) *print-miser-width*)
+               ((:lines *print-lines*) *print-lines*)
+               ((:pprint-dispatch *print-pprint-dispatch*)
+                *print-pprint-dispatch*))
   #!+sb-doc
   "Return the printed representation of OBJECT as a string."
   (stringify-object object))
   #!+sb-doc
   "Return the printed representation of OBJECT as a string with
    slashification on."
-  (stringify-object object t))
+  (let ((*print-escape* t))
+    (stringify-object object)))
 
 (defun princ-to-string (object)
   #!+sb-doc
   "Return the printed representation of OBJECT as a string with
   slashification off."
-  (stringify-object object nil))
+  (let ((*print-escape* nil)
+        (*print-readably* nil))
+    (stringify-object object)))
 
 ;;; This produces the printed representation of an object as a string.
 ;;; The few ...-TO-STRING functions above call this.
-(defvar *string-output-streams* ())
-(defun stringify-object (object &optional (*print-escape* *print-escape*))
-  (let ((stream (if *string-output-streams*
-                   (pop *string-output-streams*)
-                   (make-string-output-stream))))
+(defun stringify-object (object)
+  (let ((stream (make-string-output-stream)))
     (setup-printer-state)
     (output-object object stream)
-    (prog1
-       (get-output-stream-string stream)
-      (push stream *string-output-streams*))))
+    (get-output-stream-string stream)))
 \f
 ;;;; support for the PRINT-UNREADABLE-OBJECT macro
 
 ;;; guts of PRINT-UNREADABLE-OBJECT
 (defun %print-unreadable-object (object stream type identity body)
+  (declare (type (or null function) body))
   (when *print-readably*
     (error 'print-not-readable :object object))
   (flet ((print-description ()
-          (when type
-            (write (type-of object) :stream stream :circle nil
-                   :level nil :length nil)
-            (when (or body identity)
-              (write-char #\space stream)
-              (pprint-newline :fill stream)))
-          (when body
-            (funcall body))
-          (when identity
-            (when body
-              (write-char #\space stream)
-              (pprint-newline :fill stream))
-            (write-char #\{ stream)
-            (write (get-lisp-obj-address object) :stream stream
-                   :radix nil :base 16)
-            (write-char #\} stream))))
+           (when type
+             (write (type-of object) :stream stream :circle nil
+                    :level nil :length nil)
+             (write-char #\space stream))
+           (when body
+             (funcall body))
+           (when identity
+             (when (or body (not type))
+               (write-char #\space stream))
+             (write-char #\{ stream)
+             (write (get-lisp-obj-address object) :stream stream
+                    :radix nil :base 16)
+             (write-char #\} stream))))
     (cond ((print-pretty-on-stream-p stream)
-          ;; Since we're printing prettily on STREAM, format the
-          ;; object within a logical block. PPRINT-LOGICAL-BLOCK does
-          ;; not rebind the stream when it is already a pretty stream,
-          ;; so output from the body will go to the same stream.
-          (pprint-logical-block (stream nil :prefix "#<" :suffix ">")
-            (print-description)))
-         (t
+           ;; Since we're printing prettily on STREAM, format the
+           ;; object within a logical block. PPRINT-LOGICAL-BLOCK does
+           ;; not rebind the stream when it is already a pretty stream,
+           ;; so output from the body will go to the same stream.
+           (pprint-logical-block (stream nil :prefix "#<" :suffix ">")
+             (print-description)))
+          (t
             (write-string "#<" stream)
             (print-description)
             (write-char #\> stream))))
   nil)
 \f
-;;;; circularity detection stuff
-
-;;; When *PRINT-CIRCLE* is T, this gets bound to a hash table that
-;;; (eventually) ends up with entries for every object printed. When
-;;; we are initially looking for circularities, we enter a T when we
-;;; find an object for the first time, and a 0 when we encounter an
-;;; object a second time around. When we are actually printing, the 0
-;;; entries get changed to the actual marker value when they are first
-;;; printed.
-(defvar *circularity-hash-table* nil)
-
-;;; When NIL, we are just looking for circularities. After we have
-;;; found them all, this gets bound to 0. Then whenever we need a new
-;;; marker, it is incremented.
-(defvar *circularity-counter* nil)
-
-;;; Check to see whether OBJECT is a circular reference, and return
-;;; something non-NIL if it is. If ASSIGN is T, then the number to use
-;;; in the #n= and #n# noise is assigned at this time.
-;;;
-;;; Note: CHECK-FOR-CIRCULARITY must be called *exactly* once with
-;;; ASSIGN true, or the circularity detection noise will get confused
-;;; about when to use #n= and when to use #n#. If this returns non-NIL
-;;; when ASSIGN is true, then you must call HANDLE-CIRCULARITY on it.
-;;; If you are not using this inside a WITH-CIRCULARITY-DETECTION,
-;;; then you have to be prepared to handle a return value of :INITIATE
-;;; which means it needs to initiate the circularity detection noise.
-(defun check-for-circularity (object &optional assign)
-  (cond ((null *print-circle*)
-        ;; Don't bother, nobody cares.
-        nil)
-       ((null *circularity-hash-table*)
-        :initiate)
-       ((null *circularity-counter*)
-        (ecase (gethash object *circularity-hash-table*)
-          ((nil)
-           ;; first encounter
-           (setf (gethash object *circularity-hash-table*) t)
-           ;; We need to keep looking.
-           nil)
-          ((t)
-           ;; second encounter
-           (setf (gethash object *circularity-hash-table*) 0)
-           ;; It's a circular reference.
-           t)
-          (0
-           ;; It's a circular reference.
-           t)))
-       (t
-        (let ((value (gethash object *circularity-hash-table*)))
-          (case value
-            ((nil t)
-             ;; If NIL, we found an object that wasn't there the
-             ;; first time around. If T, this object appears exactly
-             ;; once. Either way, just print the thing without any
-             ;; special processing. Note: you might argue that
-             ;; finding a new object means that something is broken,
-             ;; but this can happen. If someone uses the ~@<...~:>
-             ;; format directive, it conses a new list each time
-             ;; though format (i.e. the &REST list), so we will have
-             ;; different cdrs.
-             nil)
-            (0
-             (if assign
-                 (let ((value (incf *circularity-counter*)))
-                   ;; first occurrence of this object: Set the counter.
-                   (setf (gethash object *circularity-hash-table*) value)
-                   value)
-                 t))
-            (t
-             ;; second or later occurrence
-             (- value)))))))
-
-;;; Handle the results of CHECK-FOR-CIRCULARITY. If this returns T then
-;;; you should go ahead and print the object. If it returns NIL, then
-;;; you should blow it off.
-(defun handle-circularity (marker stream)
-  (case marker
-    (:initiate
-     ;; Someone forgot to initiate circularity detection.
-     (let ((*print-circle* nil))
-       (error "trying to use CHECK-FOR-CIRCULARITY when ~
-              circularity checking isn't initiated")))
-    ((t)
-     ;; It's a second (or later) reference to the object while we are
-     ;; just looking. So don't bother groveling it again.
-     nil)
-    (t
-     (write-char #\# stream)
-     (let ((*print-base* 10) (*print-radix* nil))
-       (cond ((minusp marker)
-             (output-integer (- marker) stream)
-             (write-char #\# stream)
-             nil)
-            (t
-             (output-integer marker stream)
-             (write-char #\= stream)
-             t))))))
-\f
 ;;;; OUTPUT-OBJECT -- the main entry point
 
-;;; the current pretty printer. This should be either a function that
-;;; takes two arguments (the object and the stream) or NIL to indicate
-;;; that there is no pretty printer installed.
-(defvar *pretty-printer* nil)
-
 ;;; Objects whose print representation identifies them EQLly don't
 ;;; need to be checked for circularity.
 (defun uniquely-identified-by-print-p (x)
   (or (numberp x)
       (characterp x)
       (and (symbolp x)
-          (symbol-package x))))
+           (symbol-package x))))
 
 ;;; Output OBJECT to STREAM observing all printer control variables.
 (defun output-object (object stream)
   (labels ((print-it (stream)
-            (if *print-pretty*
-                (if *pretty-printer*
-                    (funcall *pretty-printer* object stream)
-                    (let ((*print-pretty* nil))
-                      (output-ugly-object object stream)))
-                (output-ugly-object object stream)))
-          (check-it (stream)
-            (let ((marker (check-for-circularity object t)))
-              (case marker
-                (:initiate
-                 (let ((*circularity-hash-table*
-                        (make-hash-table :test 'eq)))
-                   (check-it (make-broadcast-stream))
-                   (let ((*circularity-counter* 0))
-                     (check-it stream))))
-                ((nil)
-                 (print-it stream))
-                (t
-                 (when (handle-circularity marker stream)
-                   (print-it stream)))))))
+             (if *print-pretty*
+                 (sb!pretty:output-pretty-object object stream)
+                 (output-ugly-object object stream)))
+           (check-it (stream)
+             (multiple-value-bind (marker initiate)
+                 (check-for-circularity object t)
+               (if (eq initiate :initiate)
+                   (let ((*circularity-hash-table*
+                          (make-hash-table :test 'eq)))
+                     (check-it (make-broadcast-stream))
+                     (let ((*circularity-counter* 0))
+                       (check-it stream)))
+                   ;; otherwise
+                   (if marker
+                       (when (handle-circularity marker stream)
+                         (print-it stream))
+                       (print-it stream))))))
     (cond (;; Maybe we don't need to bother with circularity detection.
-          (or (not *print-circle*)
-              (uniquely-identified-by-print-p object))
-          (print-it stream))
-         (;; If we have already started circularity detection, this
-          ;; object might be a shared reference. If we have not, then
-          ;; if it is a compound object it might contain a circular
-          ;; reference to itself or multiple shared references.
-          (or *circularity-hash-table*
-              (compound-object-p object))
-          (check-it stream))
-         (t
-          (print-it stream)))))
+           (or (not *print-circle*)
+               (uniquely-identified-by-print-p object))
+           (print-it stream))
+          (;; If we have already started circularity detection, this
+           ;; object might be a shared reference. If we have not, then
+           ;; if it is a compound object it might contain a circular
+           ;; reference to itself or multiple shared references.
+           (or *circularity-hash-table*
+               (compound-object-p object))
+           (check-it stream))
+          (t
+           (print-it stream)))))
+
+;;; a hack to work around recurring gotchas with printing while
+;;; DEFGENERIC PRINT-OBJECT is being built
+;;;
+;;; (hopefully will go away naturally when CLOS moves into cold init)
+(defvar *print-object-is-disabled-p*)
 
 ;;; Output OBJECT to STREAM observing all printer control variables
 ;;; except for *PRINT-PRETTY*. Note: if *PRINT-PRETTY* is non-NIL,
     ;;       a method on an external symbol in the CL package which is
     ;;       applicable to arg lists containing only direct instances of
     ;;       standardized classes.
-    ;; Thus, in order for the user to detect our sleaziness, he has to do
-    ;; something relatively obscure like
+    ;; Thus, in order for the user to detect our sleaziness in conforming
+    ;; code, he has to do something relatively obscure like
     ;;   (1) actually use tools like FIND-METHOD to look for PRINT-OBJECT
     ;;       methods, or
     ;;   (2) define a PRINT-OBJECT method which is specialized on the stream
     ;;       value (e.g. a Gray stream object).
     ;; As long as no one comes up with a non-obscure way of detecting this
     ;; sleaziness, fixing this nonconformity will probably have a low
-    ;; priority. -- WHN 20000121
-    (fixnum
-     (output-integer object stream))
+    ;; priority. -- WHN 2001-11-25
     (list
      (if (null object)
-        (output-symbol object stream)
-        (output-list object stream)))
+         (output-symbol object stream)
+         (output-list object stream)))
     (instance
-     (print-object object stream))
+     (cond ((not (and (boundp '*print-object-is-disabled-p*)
+                      *print-object-is-disabled-p*))
+            (print-object object stream))
+           ((typep object 'structure-object)
+            (default-structure-print object stream *current-level-in-print*))
+           (t
+            (write-string "#<INSTANCE but not STRUCTURE-OBJECT>" stream))))
     (function
      (unless (and (funcallable-instance-p object)
-                 (printed-as-funcallable-standard-class object stream))
-       (output-function object stream)))
+                  (printed-as-funcallable-standard-class object stream))
+       (output-fun object stream)))
     (symbol
      (output-symbol object stream))
     (number
      (etypecase object
        (integer
-       (output-integer object stream))
+        (output-integer object stream))
        (float
-       (output-float object stream))
+        (output-float object stream))
        (ratio
-       (output-ratio object stream))
+        (output-ratio object stream))
        (ratio
-       (output-ratio object stream))
+        (output-ratio object stream))
        (complex
-       (output-complex object stream))))
+        (output-complex object stream))))
     (character
      (output-character object stream))
     (vector
 \f
 ;;;; symbols
 
-;;; Values of *PRINT-CASE* and (READTABLE-CASE *READTABLE*) the last
-;;; time the printer was called.
+;;; values of *PRINT-CASE* and (READTABLE-CASE *READTABLE*) the last
+;;; time the printer was called
 (defvar *previous-case* nil)
 (defvar *previous-readtable-case* nil)
 
 ;;; This variable contains the current definition of one of three
 ;;; symbol printers. SETUP-PRINTER-STATE sets this variable.
-(defvar *internal-symbol-output-function* nil)
+(defvar *internal-symbol-output-fun* nil)
 
 ;;; This function sets the internal global symbol
-;;; *INTERNAL-SYMBOL-OUTPUT-FUNCTION* to the right function depending
-;;; on the value of *PRINT-CASE*. See the manual for details. The
-;;; print buffer stream is also reset.
+;;; *INTERNAL-SYMBOL-OUTPUT-FUN* to the right function depending on
+;;; the value of *PRINT-CASE*. See the manual for details. The print
+;;; buffer stream is also reset.
 (defun setup-printer-state ()
   (unless (and (eq *print-case* *previous-case*)
-              (eq (readtable-case *readtable*) *previous-readtable-case*))
+               (eq (readtable-case *readtable*) *previous-readtable-case*))
     (setq *previous-case* *print-case*)
     (setq *previous-readtable-case* (readtable-case *readtable*))
     (unless (member *print-case* '(:upcase :downcase :capitalize))
       (setq *print-case* :upcase)
       (error "invalid *PRINT-CASE* value: ~S" *previous-case*))
     (unless (member *previous-readtable-case*
-                   '(:upcase :downcase :invert :preserve))
+                    '(:upcase :downcase :invert :preserve))
       (setf (readtable-case *readtable*) :upcase)
       (error "invalid READTABLE-CASE value: ~S" *previous-readtable-case*))
 
-    (setq *internal-symbol-output-function*
-         (case *previous-readtable-case*
-           (:upcase
-            (case *print-case*
-              (:upcase #'output-preserve-symbol)
-              (:downcase #'output-lowercase-symbol)
-              (:capitalize #'output-capitalize-symbol)))
-           (:downcase
-            (case *print-case*
-              (:upcase #'output-uppercase-symbol)
-              (:downcase #'output-preserve-symbol)
-              (:capitalize #'output-capitalize-symbol)))
-           (:preserve #'output-preserve-symbol)
-           (:invert #'output-invert-symbol)))))
+    (setq *internal-symbol-output-fun*
+          (case *previous-readtable-case*
+            (:upcase
+             (case *print-case*
+               (:upcase #'output-preserve-symbol)
+               (:downcase #'output-lowercase-symbol)
+               (:capitalize #'output-capitalize-symbol)))
+            (:downcase
+             (case *print-case*
+               (:upcase #'output-uppercase-symbol)
+               (:downcase #'output-preserve-symbol)
+               (:capitalize #'output-capitalize-symbol)))
+            (:preserve #'output-preserve-symbol)
+            (:invert #'output-invert-symbol)))))
 
 ;;; Output PNAME (a symbol-name or package-name) surrounded with |'s,
 ;;; and with any embedded |'s or \'s escaped.
   (dotimes (index (length pname))
     (let ((char (schar pname index)))
       (when (or (char= char #\\) (char= char #\|))
-       (write-char #\\ stream))
+        (write-char #\\ stream))
       (write-char char stream)))
   (write-char #\| stream))
 
 (defun output-symbol (object stream)
   (if (or *print-escape* *print-readably*)
       (let ((package (symbol-package object))
-           (name (symbol-name object)))
-       (cond
-        ;; The ANSI spec "22.1.3.3.1 Package Prefixes for Symbols"
-        ;; requires that keywords be printed with preceding colons
-        ;; always, regardless of the value of *PACKAGE*.
-        ((eq package *keyword-package*)
-         (write-char #\: stream))
-        ;; Otherwise, if the symbol's home package is the current
-        ;; one, then a prefix is never necessary.
-        ((eq package (sane-package)))
-        ;; Uninterned symbols print with a leading #:.
-        ((null package)
-         (when (or *print-gensym* *print-readably*)
-           (write-string "#:" stream)))
-        (t
-         (multiple-value-bind (symbol accessible)
-             (find-symbol name (sane-package))
-           ;; If we can find the symbol by looking it up, it need not
-           ;; be qualified. This can happen if the symbol has been
-           ;; inherited from a package other than its home package.
-           (unless (and accessible (eq symbol object))
-             (output-symbol-name (package-name package) stream)
-             (multiple-value-bind (symbol externalp)
-                 (find-external-symbol name package)
-               (declare (ignore symbol))
-               (if externalp
-                   (write-char #\: stream)
-                   (write-string "::" stream)))))))
-       (output-symbol-name name stream))
+            (name (symbol-name object)))
+        (cond
+         ;; The ANSI spec "22.1.3.3.1 Package Prefixes for Symbols"
+         ;; requires that keywords be printed with preceding colons
+         ;; always, regardless of the value of *PACKAGE*.
+         ((eq package *keyword-package*)
+          (write-char #\: stream))
+         ;; Otherwise, if the symbol's home package is the current
+         ;; one, then a prefix is never necessary.
+         ((eq package (sane-package)))
+         ;; Uninterned symbols print with a leading #:.
+         ((null package)
+          (when (or *print-gensym* *print-readably*)
+            (write-string "#:" stream)))
+         (t
+          (multiple-value-bind (symbol accessible)
+              (find-symbol name (sane-package))
+            ;; If we can find the symbol by looking it up, it need not
+            ;; be qualified. This can happen if the symbol has been
+            ;; inherited from a package other than its home package.
+            (unless (and accessible (eq symbol object))
+              (output-symbol-name (package-name package) stream)
+              (multiple-value-bind (symbol externalp)
+                  (find-external-symbol name package)
+                (declare (ignore symbol))
+                (if externalp
+                    (write-char #\: stream)
+                    (write-string "::" stream)))))))
+        (output-symbol-name name stream))
       (output-symbol-name (symbol-name object) stream nil)))
 
 ;;; Output the string NAME as if it were a symbol name. In other
 ;;; words, diddle its case according to *PRINT-CASE* and
 ;;; READTABLE-CASE.
 (defun output-symbol-name (name stream &optional (maybe-quote t))
-  (declare (type simple-base-string name))
-  (setup-printer-state)
-  (if (and maybe-quote (symbol-quotep name))
-      (output-quoted-symbol-name name stream)
-      (funcall *internal-symbol-output-function* name stream)))
+  (declare (type simple-string name))
+  (let ((*readtable* (if *print-readably* *standard-readtable* *readtable*)))
+    (setup-printer-state)
+    (if (and maybe-quote (symbol-quotep name))
+        (output-quoted-symbol-name name stream)
+        (funcall *internal-symbol-output-fun* name stream))))
 \f
 ;;;; escaping symbols
 
 ;;; character has. At characters have at least one bit set, so we can
 ;;; search for any character with a positive test.
 (defvar *character-attributes*
-  (make-array char-code-limit
-             :element-type '(unsigned-byte 16)
-             :initial-element 0))
-(declaim (type (simple-array (unsigned-byte 16) (#.char-code-limit))
-              *character-attributes*))
+  (make-array 160 ; FIXME
+              :element-type '(unsigned-byte 16)
+              :initial-element 0))
+(declaim (type (simple-array (unsigned-byte 16) (#.160)) ; FIXME
+               *character-attributes*))
 
 ;;; constants which are a bit-mask for each interesting character attribute
-(defconstant other-attribute           (ash 1 0)) ; Anything else legal.
-(defconstant number-attribute          (ash 1 1)) ; A numeric digit.
-(defconstant uppercase-attribute       (ash 1 2)) ; An uppercase letter.
-(defconstant lowercase-attribute       (ash 1 3)) ; A lowercase letter.
-(defconstant sign-attribute            (ash 1 4)) ; +-
-(defconstant extension-attribute       (ash 1 5)) ; ^_
-(defconstant dot-attribute             (ash 1 6)) ; .
-(defconstant slash-attribute           (ash 1 7)) ; /
-(defconstant funny-attribute           (ash 1 8)) ; Anything illegal.
+(defconstant other-attribute            (ash 1 0)) ; Anything else legal.
+(defconstant number-attribute           (ash 1 1)) ; A numeric digit.
+(defconstant uppercase-attribute        (ash 1 2)) ; An uppercase letter.
+(defconstant lowercase-attribute        (ash 1 3)) ; A lowercase letter.
+(defconstant sign-attribute             (ash 1 4)) ; +-
+(defconstant extension-attribute        (ash 1 5)) ; ^_
+(defconstant dot-attribute              (ash 1 6)) ; .
+(defconstant slash-attribute            (ash 1 7)) ; /
+(defconstant funny-attribute            (ash 1 8)) ; Anything illegal.
 
 (eval-when (:compile-toplevel :load-toplevel :execute)
 
 ) ; EVAL-WHEN
 
 (flet ((set-bit (char bit)
-        (let ((code (char-code char)))
-          (setf (aref *character-attributes* code)
-                (logior bit (aref *character-attributes* code))))))
+         (let ((code (char-code char)))
+           (setf (aref *character-attributes* code)
+                 (logior bit (aref *character-attributes* code))))))
 
   (dolist (char '(#\! #\@ #\$ #\% #\& #\* #\= #\~ #\[ #\] #\{ #\}
-                 #\? #\< #\>))
+                  #\? #\< #\>))
     (set-bit char other-attribute))
 
   (dotimes (i 10)
   (set-bit #\/ slash-attribute)
 
   ;; Mark anything not explicitly allowed as funny.
-  (dotimes (i char-code-limit)
+  (dotimes (i 160) ; FIXME
     (when (zerop (aref *character-attributes* i))
       (setf (aref *character-attributes* i) funny-attribute))))
 
 ;;; For each character, the value of the corresponding element is the
 ;;; lowest base in which that character is a digit.
 (defvar *digit-bases*
-  (make-array char-code-limit
-             :element-type '(unsigned-byte 8)
-             :initial-element 36))
-(declaim (type (simple-array (unsigned-byte 8) (#.char-code-limit))
-              *digit-bases*))
-
+  (make-array 128 ; FIXME
+              :element-type '(unsigned-byte 8)
+              :initial-element 36))
+(declaim (type (simple-array (unsigned-byte 8) (#.128)) ; FIXME
+               *digit-bases*))
 (dotimes (i 36)
   (let ((char (digit-char i 36)))
     (setf (aref *digit-bases* (char-code char)) i)))
 (defun symbol-quotep (name)
   (declare (simple-string name))
   (macrolet ((advance (tag &optional (at-end t))
-              `(progn
-                (when (= index len)
-                  ,(if at-end '(go TEST-SIGN) '(return nil)))
-                (setq current (schar name index)
-                      code (char-code current)
-                      bits (aref attributes code))
-                (incf index)
-                (go ,tag)))
-            (test (&rest attributes)
-               `(not (zerop
-                      (the fixnum
-                           (logand
-                            (logior ,@(mapcar
-                                       (lambda (x)
-                                         (or (cdr (assoc x
-                                                         *attribute-names*))
-                                             (error "Blast!")))
-                                       attributes))
-                            bits)))))
-            (digitp ()
-              `(< (the fixnum (aref bases code)) base)))
+               `(progn
+                 (when (= index len)
+                   ,(if at-end '(go TEST-SIGN) '(return nil)))
+                 (setq current (schar name index)
+                       code (char-code current)
+                       bits (cond ; FIXME
+                              ((< code 160) (aref attributes code))
+                              ((upper-case-p current) uppercase-attribute)
+                              ((lower-case-p current) lowercase-attribute)
+                              (t other-attribute)))
+                 (incf index)
+                 (go ,tag)))
+             (test (&rest attributes)
+                `(not (zerop
+                       (the fixnum
+                            (logand
+                             (logior ,@(mapcar
+                                        (lambda (x)
+                                          (or (cdr (assoc x
+                                                          *attribute-names*))
+                                              (error "Blast!")))
+                                        attributes))
+                             bits)))))
+             (digitp ()
+               `(and (< code 128) ; FIXME
+                     (< (the fixnum (aref bases code)) base))))
 
     (prog ((len (length name))
-          (attributes *character-attributes*)
-          (bases *digit-bases*)
-          (base *print-base*)
-          (letter-attribute
-           (case (readtable-case *readtable*)
-             (:upcase uppercase-attribute)
-             (:downcase lowercase-attribute)
-             (t (logior lowercase-attribute uppercase-attribute))))
-          (index 0)
-          (bits 0)
-          (code 0)
-          current)
+           (attributes *character-attributes*)
+           (bases *digit-bases*)
+           (base *print-base*)
+           (letter-attribute
+            (case (readtable-case *readtable*)
+              (:upcase uppercase-attribute)
+              (:downcase lowercase-attribute)
+              (t (logior lowercase-attribute uppercase-attribute))))
+           (index 0)
+           (bits 0)
+           (code 0)
+           current)
       (declare (fixnum len base index bits code))
       (advance START t)
 
 
      OTHER ; not potential number, see whether funny chars...
       (let ((mask (logxor (logior lowercase-attribute uppercase-attribute
-                                 funny-attribute)
-                         letter-attribute)))
-       (do ((i (1- index) (1+ i)))
-           ((= i len) (return-from symbol-quotep nil))
-         (unless (zerop (logand (aref attributes (char-code (schar name i)))
-                                mask))
-           (return-from symbol-quotep t))))
+                                  funny-attribute)
+                          letter-attribute)))
+        (do ((i (1- index) (1+ i)))
+            ((= i len) (return-from symbol-quotep nil))
+          (unless (zerop (logand (let* ((char (schar name i))
+                                        (code (char-code char)))
+                                   (cond
+                                     ((< code 160) (aref attributes code))
+                                     ((upper-case-p char) uppercase-attribute)
+                                     ((lower-case-p char) lowercase-attribute)
+                                     (t other-attribute)))
+                                 mask))
+            (return-from symbol-quotep t))))
 
      START
       (when (digitp)
-       (if (test letter)
-           (advance LAST-DIGIT-ALPHA)
-           (advance DIGIT)))
+        (if (test letter)
+            (advance LAST-DIGIT-ALPHA)
+            (advance DIGIT)))
       (when (test letter number other slash) (advance OTHER nil))
       (when (char= current #\.) (advance DOT-FOUND))
       (when (test sign extension) (advance START-STUFF nil))
 
      START-STUFF ; leading stuff before any dot or digit
       (when (digitp)
-       (if (test letter)
-           (advance LAST-DIGIT-ALPHA)
-           (advance DIGIT)))
+        (if (test letter)
+            (advance LAST-DIGIT-ALPHA)
+            (advance DIGIT)))
       (when (test number other) (advance OTHER nil))
       (when (test letter) (advance START-MARKER nil))
       (when (char= current #\.) (advance START-DOT-STUFF nil))
 
      LAST-DIGIT-ALPHA ; previous char is a letter digit...
       (when (or (digitp) (test sign slash))
-       (advance ALPHA-DIGIT))
+        (advance ALPHA-DIGIT))
       (when (test letter number other dot) (advance OTHER nil))
       (return t)
 
      ALPHA-DIGIT ; seen a digit which is a letter...
       (when (or (digitp) (test sign slash))
-       (if (test letter)
-           (advance LAST-DIGIT-ALPHA)
-           (advance ALPHA-DIGIT)))
+        (if (test letter)
+            (advance LAST-DIGIT-ALPHA)
+            (advance ALPHA-DIGIT)))
       (when (test letter) (advance ALPHA-MARKER))
       (when (test number other dot) (advance OTHER nil))
       (return t)
 
      DIGIT ; seen only ordinary (non-alphabetic) numeric digits...
       (when (digitp)
-       (if (test letter)
-           (advance ALPHA-DIGIT)
-           (advance DIGIT)))
+        (if (test letter)
+            (advance ALPHA-DIGIT)
+            (advance DIGIT)))
       (when (test number other) (advance OTHER nil))
       (when (test letter) (advance MARKER))
       (when (test extension slash sign) (advance DIGIT))
       (return t)
 
      MARKER ; number marker in a numeric number...
+      ;; ("What," you may ask, "is a 'number marker'?" It's something
+      ;; that a conforming implementation might use in number syntax.
+      ;; See ANSI 2.3.1.1 "Potential Numbers as Tokens".)
       (when (test letter) (advance OTHER nil))
       (go DIGIT))))
 \f
-;;;; *INTERNAL-SYMBOL-OUTPUT-FUNCTION*
+;;;; *INTERNAL-SYMBOL-OUTPUT-FUN*
 ;;;;
-;;;; Case hackery. These functions are stored in
-;;;; *INTERNAL-SYMBOL-OUTPUT-FUNCTION* according to the values of
+;;;; case hackery: These functions are stored in
+;;;; *INTERNAL-SYMBOL-OUTPUT-FUN* according to the values of
 ;;;; *PRINT-CASE* and READTABLE-CASE.
 
 ;;; called when:
-;;; READTABLE-CASE     *PRINT-CASE*
-;;; :UPCASE            :UPCASE
-;;; :DOWNCASE          :DOWNCASE
-;;; :PRESERVE          any
+;;; READTABLE-CASE      *PRINT-CASE*
+;;; :UPCASE             :UPCASE
+;;; :DOWNCASE           :DOWNCASE
+;;; :PRESERVE           any
 (defun output-preserve-symbol (pname stream)
   (declare (simple-string pname))
   (write-string pname stream))
 
 ;;; called when:
-;;; READTABLE-CASE     *PRINT-CASE*
-;;; :UPCASE            :DOWNCASE
+;;; READTABLE-CASE      *PRINT-CASE*
+;;; :UPCASE             :DOWNCASE
 (defun output-lowercase-symbol (pname stream)
   (declare (simple-string pname))
   (dotimes (index (length pname))
       (write-char (char-downcase char) stream))))
 
 ;;; called when:
-;;; READTABLE-CASE     *PRINT-CASE*
-;;; :DOWNCASE          :UPCASE
+;;; READTABLE-CASE      *PRINT-CASE*
+;;; :DOWNCASE           :UPCASE
 (defun output-uppercase-symbol (pname stream)
   (declare (simple-string pname))
   (dotimes (index (length pname))
       (write-char (char-upcase char) stream))))
 
 ;;; called when:
-;;; READTABLE-CASE     *PRINT-CASE*
-;;; :UPCASE            :CAPITALIZE
-;;; :DOWNCASE          :CAPITALIZE
+;;; READTABLE-CASE      *PRINT-CASE*
+;;; :UPCASE             :CAPITALIZE
+;;; :DOWNCASE           :CAPITALIZE
 (defun output-capitalize-symbol (pname stream)
   (declare (simple-string pname))
-  (let ((prev-not-alpha t)
-       (up (eq (readtable-case *readtable*) :upcase)))
+  (let ((prev-not-alphanum t)
+        (up (eq (readtable-case *readtable*) :upcase)))
     (dotimes (i (length pname))
       (let ((char (char pname i)))
-       (write-char (if up
-                       (if (or prev-not-alpha (lower-case-p char))
-                           char
-                           (char-downcase char))
-                       (if prev-not-alpha
-                           (char-upcase char)
-                           char))
-                   stream)
-       (setq prev-not-alpha (not (alpha-char-p char)))))))
+        (write-char (if up
+                        (if (or prev-not-alphanum (lower-case-p char))
+                            char
+                            (char-downcase char))
+                        (if prev-not-alphanum
+                            (char-upcase char)
+                            char))
+                    stream)
+        (setq prev-not-alphanum (not (alphanumericp char)))))))
 
 ;;; called when:
-;;; READTABLE-CASE     *PRINT-CASE*
-;;; :INVERT            any
+;;; READTABLE-CASE      *PRINT-CASE*
+;;; :INVERT             any
 (defun output-invert-symbol (pname stream)
   (declare (simple-string pname))
   (let ((all-upper t)
-       (all-lower t))
+        (all-lower t))
     (dotimes (i (length pname))
       (let ((ch (schar pname i)))
-       (when (both-case-p ch)
-         (if (upper-case-p ch)
-             (setq all-lower nil)
-             (setq all-upper nil)))))
+        (when (both-case-p ch)
+          (if (upper-case-p ch)
+              (setq all-lower nil)
+              (setq all-upper nil)))))
     (cond (all-upper (output-lowercase-symbol pname stream))
-         (all-lower (output-uppercase-symbol pname stream))
-         (t
-          (write-string pname stream)))))
+          (all-lower (output-uppercase-symbol pname stream))
+          (t
+           (write-string pname stream)))))
 
 #|
 (defun test1 ()
   (let ((*readtable* (copy-readtable nil)))
     (format t "READTABLE-CASE  Input   Symbol-name~@
-              ----------------------------------~%")
+               ----------------------------------~%")
     (dolist (readtable-case '(:upcase :downcase :preserve :invert))
       (setf (readtable-case *readtable*) readtable-case)
       (dolist (input '("ZEBRA" "Zebra" "zebra"))
-       (format t "~&:~A~16T~A~24T~A"
-               (string-upcase readtable-case)
-               input
-               (symbol-name (read-from-string input)))))))
+        (format t "~&:~A~16T~A~24T~A"
+                (string-upcase readtable-case)
+                input
+                (symbol-name (read-from-string input)))))))
 
 (defun test2 ()
   (let ((*readtable* (copy-readtable nil)))
     (format t "READTABLE-CASE  *PRINT-CASE*  Symbol-name  Output  Princ~@
-              --------------------------------------------------------~%")
+               --------------------------------------------------------~%")
     (dolist (readtable-case '(:upcase :downcase :preserve :invert))
       (setf (readtable-case *readtable*) readtable-case)
       (dolist (*print-case* '(:upcase :downcase :capitalize))
-       (dolist (symbol '(|ZEBRA| |Zebra| |zebra|))
-         (format t "~&:~A~15T:~A~29T~A~42T~A~50T~A"
-                 (string-upcase readtable-case)
-                 (string-upcase *print-case*)
-                 (symbol-name symbol)
-                 (prin1-to-string symbol)
-                 (princ-to-string symbol)))))))
+        (dolist (symbol '(|ZEBRA| |Zebra| |zebra|))
+          (format t "~&:~A~15T:~A~29T~A~42T~A~50T~A"
+                  (string-upcase readtable-case)
+                  (string-upcase *print-case*)
+                  (symbol-name symbol)
+                  (prin1-to-string symbol)
+                  (princ-to-string symbol)))))))
 |#
 \f
 ;;;; recursive objects
   (descend-into (stream)
     (write-char #\( stream)
     (let ((length 0)
-         (list list))
+          (list list))
       (loop
-       (punt-print-if-too-long length stream)
-       (output-object (pop list) stream)
-       (unless list
-         (return))
-       (when (or (atom list) (check-for-circularity list))
-         (write-string " . " stream)
-         (output-object list stream)
-         (return))
-       (write-char #\space stream)
-       (incf length)))
+        (punt-print-if-too-long length stream)
+        (output-object (pop list) stream)
+        (unless list
+          (return))
+        (when (or (atom list)
+                  (check-for-circularity list))
+          (write-string " . " stream)
+          (output-object list stream)
+          (return))
+        (write-char #\space stream)
+        (incf length)))
     (write-char #\) stream)))
 
 (defun output-vector (vector stream)
   (declare (vector vector))
   (cond ((stringp vector)
-        (cond ((or *print-escape* *print-readably*)
-               (write-char #\" stream)
-               (quote-string vector stream)
-               (write-char #\" stream))
-              (t
-               (write-string vector stream))))
-       ((not (or *print-array* *print-readably*))
-        (output-terse-array vector stream))
-       ((bit-vector-p vector)
-        (write-string "#*" stream)
-        (dovector (bit vector)
-          ;; (Don't use OUTPUT-OBJECT here, since this code
-          ;; has to work for all possible *PRINT-BASE* values.)
-          (write-char (if (zerop bit) #\0 #\1) stream)))
-       (t
-        (when (and *print-readably*
-                   (not (eq (array-element-type vector) t)))
-          (error 'print-not-readable :object vector))
-        (descend-into (stream)
-                      (write-string "#(" stream)
-                      (dotimes (i (length vector))
-                        (unless (zerop i)
-                          (write-char #\space stream))
-                        (punt-print-if-too-long i stream)
-                        (output-object (aref vector i) stream))
-                      (write-string ")" stream)))))
+         (cond ((and *print-readably*
+                     (not (eq (array-element-type vector)
+                              (load-time-value
+                               (array-element-type
+                                (make-array 0 :element-type 'character))))))
+                (error 'print-not-readable :object vector))
+               ((or *print-escape* *print-readably*)
+                (write-char #\" stream)
+                (quote-string vector stream)
+                (write-char #\" stream))
+               (t
+                (write-string vector stream))))
+        ((not (or *print-array* *print-readably*))
+         (output-terse-array vector stream))
+        ((bit-vector-p vector)
+         (write-string "#*" stream)
+         (dovector (bit vector)
+           ;; (Don't use OUTPUT-OBJECT here, since this code
+           ;; has to work for all possible *PRINT-BASE* values.)
+           (write-char (if (zerop bit) #\0 #\1) stream)))
+        (t
+         (when (and *print-readably*
+                    (not (array-readably-printable-p vector)))
+           (error 'print-not-readable :object vector))
+         (descend-into (stream)
+                       (write-string "#(" stream)
+                       (dotimes (i (length vector))
+                         (unless (zerop i)
+                           (write-char #\space stream))
+                         (punt-print-if-too-long i stream)
+                         (output-object (aref vector i) stream))
+                       (write-string ")" stream)))))
 
 ;;; This function outputs a string quoting characters sufficiently
 ;;; so that someone can read it in again. Basically, put a slash in
 ;;; front of an character satisfying NEEDS-SLASH-P.
 (defun quote-string (string stream)
   (macrolet ((needs-slash-p (char)
-              ;; KLUDGE: We probably should look at the readtable, but just do
-              ;; this for now. [noted by anonymous long ago] -- WHN 19991130
-              `(or (char= ,char #\\)
+               ;; KLUDGE: We probably should look at the readtable, but just do
+               ;; this for now. [noted by anonymous long ago] -- WHN 19991130
+               `(or (char= ,char #\\)
                  (char= ,char #\"))))
     (with-array-data ((data string) (start) (end (length string)))
       (do ((index start (1+ index)))
-         ((>= index end))
-       (let ((char (schar data index)))
-         (when (needs-slash-p char) (write-char #\\ stream))
-         (write-char char stream))))))
+          ((>= index end))
+        (let ((char (schar data index)))
+          (when (needs-slash-p char) (write-char #\\ stream))
+          (write-char char stream))))))
+
+(defun array-readably-printable-p (array)
+  (and (eq (array-element-type array) t)
+       (let ((zero (position 0 (array-dimensions array)))
+             (number (position 0 (array-dimensions array)
+                               :test (complement #'eql)
+                               :from-end t)))
+         (or (null zero) (null number) (> zero number)))))
 
 ;;; Output the printed representation of any array in either the #< or #A
 ;;; form.
 ;;; Output the abbreviated #< form of an array.
 (defun output-terse-array (array stream)
   (let ((*print-level* nil)
-       (*print-length* nil))
+        (*print-length* nil))
     (print-unreadable-object (array stream :type t :identity t))))
 
 ;;; Output the readable #A form of an array.
 (defun output-array-guts (array stream)
   (when (and *print-readably*
-            (not (eq (array-element-type array) t)))
+             (not (array-readably-printable-p array)))
     (error 'print-not-readable :object array))
   (write-char #\# stream)
-  (let ((*print-base* 10))
+  (let ((*print-base* 10)
+        (*print-radix* nil))
     (output-integer (array-rank array) stream))
   (write-char #\A stream)
   (with-array-data ((data array) (start) (end))
 (defun sub-output-array-guts (array dimensions stream index)
   (declare (type (simple-array * (*)) array) (fixnum index))
   (cond ((null dimensions)
-        (output-object (aref array index) stream))
-       (t
-        (descend-into (stream)
-          (write-char #\( stream)
-          (let* ((dimension (car dimensions))
-                 (dimensions (cdr dimensions))
-                 (count (reduce #'* dimensions)))
-            (dotimes (i dimension)
-              (unless (zerop i)
-                (write-char #\space stream))
-              (punt-print-if-too-long i stream)
-              (sub-output-array-guts array dimensions stream index)
-              (incf index count)))
-          (write-char #\) stream)))))
+         (output-object (aref array index) stream))
+        (t
+         (descend-into (stream)
+           (write-char #\( stream)
+           (let* ((dimension (car dimensions))
+                  (dimensions (cdr dimensions))
+                  (count (reduce #'* dimensions)))
+             (dotimes (i dimension)
+               (unless (zerop i)
+                 (write-char #\space stream))
+               (punt-print-if-too-long i stream)
+               (sub-output-array-guts array dimensions stream index)
+               (incf index count)))
+           (write-char #\) stream)))))
 
 ;;; a trivial non-generic-function placeholder for PRINT-OBJECT, for
 ;;; use until CLOS is set up (at which time it will be replaced with
 ;;; the real generic function implementation)
 (defun print-object (instance stream)
-  (default-structure-print instance stream *current-level*))
+  (default-structure-print instance stream *current-level-in-print*))
 \f
 ;;;; integer, ratio, and complex printing (i.e. everything but floats)
 
+(defun %output-radix (base stream)
+  (write-char #\# stream)
+  (write-char (case base
+                (2 #\b)
+                (8 #\o)
+                (16 #\x)
+                (t (%output-fixnum-in-base base 10 stream)
+                   #\r))
+              stream))
+
+(defun %output-fixnum-in-base (n base stream)
+  (multiple-value-bind (q r)
+      (truncate n base)
+    ;; Recurse until you have all the digits pushed on
+    ;; the stack.
+    (unless (zerop q)
+      (%output-fixnum-in-base q base stream))
+    ;; Then as each recursive call unwinds, turn the
+    ;; digit (in remainder) into a character and output
+    ;; the character.
+    (write-char
+     (schar "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" r)
+     stream)))
+
+;; Algorithm by Harald Hanche-Olsen, sbcl-devel 2005-02-05
+(defun %output-bignum-in-base (n base stream)
+  (declare (type bignum n) (type fixnum base))
+  (let ((power (make-array 10 :adjustable t :fill-pointer 0)))
+    ;; Here there be the bottleneck for big bignums, in the (* p p).
+    ;; A special purpose SQUARE-BIGNUM might help a bit. See eg: Dan
+    ;; Zuras, "On Squaring and Multiplying Large Integers", ARITH-11:
+    ;; IEEE Symposium on Computer Arithmetic, 1993, pp. 260 to 271.
+    ;; Reprinted as "More on Multiplying and Squaring Large Integers",
+    ;; IEEE Transactions on Computers, volume 43, number 8, August
+    ;; 1994, pp. 899-908.
+    (do ((p base (* p p)))
+        ((> p n))
+      (vector-push-extend p power))
+    ;; (aref power k) == (expt base (expt 2 k))
+    (labels ((bisect (n k exactp)
+               (declare (fixnum k))
+               ;; N is the number to bisect
+               ;; K on initial entry BASE^(2^K) > N
+               ;; EXACTP is true if 2^K is the exact number of digits
+               (cond ((zerop n)
+                      (when exactp
+                        (loop repeat (ash 1 k) do (write-char #\0 stream))))
+                     ((zerop k)
+                      (write-char
+                       (schar "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" n)
+                       stream))
+                     (t
+                      (setf k (1- k))
+                      (multiple-value-bind (q r) (truncate n (aref power k))
+                        ;; EXACTP is NIL only at the head of the
+                        ;; initial number, as we don't know the number
+                        ;; of digits there, but we do know that it
+                        ;; doesn't get any leading zeros.
+                        (bisect q k exactp)
+                        (bisect r k (or exactp (plusp q))))))))
+      (bisect n (fill-pointer power) nil))))
+
+(defun %output-integer-in-base (integer base stream)
+  (when (minusp integer)
+    (write-char #\- stream)
+    (setf integer (- integer)))
+  (if (fixnump integer)
+      (%output-fixnum-in-base integer base stream)
+      (%output-bignum-in-base integer base stream)))
+
 (defun output-integer (integer stream)
-  ;; FIXME: This UNLESS form should be pulled out into something like
-  ;; (SANE-PRINT-BASE), along the lines of (SANE-PACKAGE) for the
-  ;; *PACKAGE* variable.
-  (unless (and (fixnump *print-base*)
-              (< 1 *print-base* 37))
-    (let ((obase *print-base*))
-      (setq *print-base* 10.)
-      (error "~A is not a reasonable value for *PRINT-BASE*." obase)))
-  (when (and (not (= *print-base* 10.))
-            *print-radix*)
-    ;; First print leading base information, if any.
-    (write-char #\# stream)
-    (write-char (case *print-base*
-                 (2. #\b)
-                 (8. #\o)
-                 (16. #\x)
-                 (T (let ((fixbase *print-base*)
-                          (*print-base* 10.)
-                          (*print-radix* ()))
-                      (sub-output-integer fixbase stream))
-                    #\r))
-               stream))
-  ;; Then output a minus sign if the number is negative, then output
-  ;; the absolute value of the number.
-  (cond ((bignump integer) (print-bignum integer stream))
-       ((< integer 0)
-        (write-char #\- stream)
-        (sub-output-integer (- integer) stream))
-       (t
-        (sub-output-integer integer stream)))
-  ;; Print any trailing base information, if any.
-  (if (and (= *print-base* 10.) *print-radix*)
-      (write-char #\. stream)))
-
-(defun sub-output-integer (integer stream)
-  (let ((quotient ())
-       (remainder ()))
-    ;; Recurse until you have all the digits pushed on the stack.
-    (if (not (zerop (multiple-value-setq (quotient remainder)
-                     (truncate integer *print-base*))))
-       (sub-output-integer quotient stream))
-    ;; Then as each recursive call unwinds, turn the digit (in remainder)
-    ;; into a character and output the character.
-    (write-char (code-char (if (and (> remainder 9.)
-                                   (> *print-base* 10.))
-                              (+ (char-code #\A) (- remainder 10.))
-                              (+ (char-code #\0) remainder)))
-               stream)))
-\f
-;;;; bignum printing
-
-;;; *BASE-POWER* holds the number that we keep dividing into the
-;;; bignum for each *print-base*. We want this number as close to
-;;; *most-positive-fixnum* as possible, i.e. (floor (log
-;;; most-positive-fixnum *print-base*)).
-(defparameter *base-power* (make-array 37 :initial-element nil))
-
-;;; *FIXNUM-POWER--1* holds the number of digits for each *PRINT-BASE*
-;;; that fit in the corresponding *base-power*.
-(defparameter *fixnum-power--1* (make-array 37 :initial-element nil))
-
-;;; Print the bignum to the stream. We first generate the correct
-;;; value for *base-power* and *fixnum-power--1* if we have not
-;;; already. Then we call bignum-print-aux to do the printing.
-(defun print-bignum (big stream)
-  (unless (aref *base-power* *print-base*)
-    (do ((power-1 -1 (1+ power-1))
-        (new-divisor *print-base* (* new-divisor *print-base*))
-        (divisor 1 new-divisor))
-       ((not (fixnump new-divisor))
-        (setf (aref *base-power* *print-base*) divisor)
-        (setf (aref *fixnum-power--1* *print-base*) power-1))))
-  (bignum-print-aux (cond ((minusp big)
-                          (write-char #\- stream)
-                          (- big))
-                         (t big))
-                   (aref *base-power* *print-base*)
-                   (aref *fixnum-power--1* *print-base*)
-                   stream)
-  big)
-
-(defun bignum-print-aux (big divisor power-1 stream)
-  (multiple-value-bind (newbig fix) (truncate big divisor)
-    (if (fixnump newbig)
-       (sub-output-integer newbig stream)
-       (bignum-print-aux newbig divisor power-1 stream))
-    (do ((zeros power-1 (1- zeros))
-        (base-power *print-base* (* base-power *print-base*)))
-       ((> base-power fix)
-        (dotimes (i zeros) (write-char #\0 stream))
-        (sub-output-integer fix stream)))))
+  (let ((base *print-base*))
+    (when (and (/= base 10) *print-radix*)
+      (%output-radix base stream))
+    (%output-integer-in-base integer base stream)
+    (when (and *print-radix* (= base 10))
+      (write-char #\. stream))))
 
 (defun output-ratio (ratio stream)
-  (when *print-radix*
-    (write-char #\# stream)
-    (case *print-base*
-      (2 (write-char #\b stream))
-      (8 (write-char #\o stream))
-      (16 (write-char #\x stream))
-      (t (write *print-base* :stream stream :radix nil :base 10)))
-    (write-char #\r stream))
-  (let ((*print-radix* nil))
-    (output-integer (numerator ratio) stream)
+  (let ((base *print-base*))
+    (when *print-radix*
+      (%output-radix base stream))
+    (%output-integer-in-base (numerator ratio) base stream)
     (write-char #\/ stream)
-    (output-integer (denominator ratio) stream)))
+    (%output-integer-in-base (denominator ratio) base stream)))
 
 (defun output-complex (complex stream)
   (write-string "#C(" stream)
+  ;; FIXME: Could this just be OUTPUT-NUMBER?
   (output-object (realpart complex) stream)
   (write-char #\space stream)
   (output-object (imagpart complex) stream)
 ;;;; float printing
 
 ;;; FLONUM-TO-STRING (and its subsidiary function FLOAT-STRING) does
-;;; most of the work for all printing of floating point numbers in the
-;;; printer and in FORMAT. It converts a floating point number to a
-;;; string in a free or fixed format with no exponent. The
-;;; interpretation of the arguments is as follows:
+;;; most of the work for all printing of floating point numbers in
+;;; FORMAT.  It converts a floating point number to a string in a free
+;;; or fixed format with no exponent. The interpretation of the
+;;; arguments is as follows:
 ;;;
-;;;     X      - The floating point number to convert, which must not be
-;;;            negative.
+;;;     X       - The floating point number to convert, which must not be
+;;;             negative.
 ;;;     WIDTH    - The preferred field width, used to determine the number
-;;;            of fraction digits to produce if the FDIGITS parameter
-;;;            is unspecified or NIL. If the non-fraction digits and the
-;;;            decimal point alone exceed this width, no fraction digits
-;;;            will be produced unless a non-NIL value of FDIGITS has been
-;;;            specified. Field overflow is not considerd an error at this
-;;;            level.
+;;;             of fraction digits to produce if the FDIGITS parameter
+;;;             is unspecified or NIL. If the non-fraction digits and the
+;;;             decimal point alone exceed this width, no fraction digits
+;;;             will be produced unless a non-NIL value of FDIGITS has been
+;;;             specified. Field overflow is not considerd an error at this
+;;;             level.
 ;;;     FDIGITS  - The number of fractional digits to produce. Insignificant
-;;;            trailing zeroes may be introduced as needed. May be
-;;;            unspecified or NIL, in which case as many digits as possible
-;;;            are generated, subject to the constraint that there are no
-;;;            trailing zeroes.
+;;;             trailing zeroes may be introduced as needed. May be
+;;;             unspecified or NIL, in which case as many digits as possible
+;;;             are generated, subject to the constraint that there are no
+;;;             trailing zeroes.
 ;;;     SCALE    - If this parameter is specified or non-NIL, then the number
-;;;            printed is (* x (expt 10 scale)). This scaling is exact,
-;;;            and cannot lose precision.
+;;;             printed is (* x (expt 10 scale)). This scaling is exact,
+;;;             and cannot lose precision.
 ;;;     FMIN     - This parameter, if specified or non-NIL, is the minimum
-;;;            number of fraction digits which will be produced, regardless
-;;;            of the value of WIDTH or FDIGITS. This feature is used by
-;;;            the ~E format directive to prevent complete loss of
-;;;            significance in the printed value due to a bogus choice of
-;;;            scale factor.
-;;;
-;;; Most of the optional arguments are for the benefit for FORMAT and are not
-;;; used by the printer.
+;;;             number of fraction digits which will be produced, regardless
+;;;             of the value of WIDTH or FDIGITS. This feature is used by
+;;;             the ~E format directive to prevent complete loss of
+;;;             significance in the printed value due to a bogus choice of
+;;;             scale factor.
 ;;;
 ;;; Returns:
 ;;; (VALUES DIGIT-STRING DIGIT-LENGTH LEADING-POINT TRAILING-POINT DECPNT)
 ;;;     DIGIT-STRING    - The decimal representation of X, with decimal point.
 ;;;     DIGIT-LENGTH    - The length of the string DIGIT-STRING.
 ;;;     LEADING-POINT   - True if the first character of DIGIT-STRING is the
-;;;                   decimal point.
+;;;                    decimal point.
 ;;;     TRAILING-POINT  - True if the last character of DIGIT-STRING is the
-;;;                   decimal point.
+;;;                    decimal point.
 ;;;     POINT-POS       - The position of the digit preceding the decimal
-;;;                   point. Zero indicates point before first digit.
+;;;                    point. Zero indicates point before first digit.
 ;;;
 ;;; NOTE: FLONUM-TO-STRING goes to a lot of trouble to guarantee
 ;;; accuracy. Specifically, the decimal number printed is the closest
 ;;; representation. Furthermore, only as many digits as necessary to
 ;;; satisfy this condition will be printed.
 ;;;
-;;; FLOAT-STRING actually generates the digits for positive numbers.
-;;; The algorithm is essentially that of algorithm Dragon4 in "How to
-;;; Print Floating-Point Numbers Accurately" by Steele and White. The
-;;; current (draft) version of this paper may be found in
-;;; [CMUC]<steele>tradix.press. DO NOT EVEN THINK OF ATTEMPTING TO
-;;; UNDERSTAND THIS CODE WITHOUT READING THE PAPER!
-
-(defvar *digits* "0123456789")
+;;; FLOAT-DIGITS actually generates the digits for positive numbers;
+;;; see below for comments.
 
 (defun flonum-to-string (x &optional width fdigits scale fmin)
+  (declare (type float x))
+  ;; FIXME: I think only FORMAT-DOLLARS calls FLONUM-TO-STRING with
+  ;; possibly-negative X.
+  (setf x (abs x))
   (cond ((zerop x)
-        ;; Zero is a special case which FLOAT-STRING cannot handle.
-        (if fdigits
-            (let ((s (make-string (1+ fdigits) :initial-element #\0)))
-              (setf (schar s 0) #\.)
-              (values s (length s) t (zerop fdigits) 0))
-            (values "." 1 t t 0)))
-       (t
-        (multiple-value-bind (sig exp) (integer-decode-float x)
-          (let* ((precision (float-precision x))
-                 (digits (float-digits x))
-                 (fudge (- digits precision))
-                 (width (if width (max width 1) nil)))
-          (float-string (ash sig (- fudge)) (+ exp fudge) precision width
-                        fdigits scale fmin))))))
-
-(defun float-string (fraction exponent precision width fdigits scale fmin)
-  (let ((r fraction) (s 1) (m- 1) (m+ 1) (k 0)
-       (digits 0) (decpnt 0) (cutoff nil) (roundup nil) u low high
-       (digit-string (make-array 50
-                                 :element-type 'base-char
-                                 :fill-pointer 0
-                                 :adjustable t)))
-    ;; Represent fraction as r/s, error bounds as m+/s and m-/s.
-    ;; Rational arithmetic avoids loss of precision in subsequent
-    ;; calculations.
-    (cond ((> exponent 0)
-          (setq r (ash fraction exponent))
-          (setq m- (ash 1 exponent))
-          (setq m+ m-))
-         ((< exponent 0)
-          (setq s (ash 1 (- exponent)))))
-    ;; Adjust the error bounds m+ and m- for unequal gaps.
-    (when (= fraction (ash 1 precision))
-      (setq m+ (ash m+ 1))
-      (setq r (ash r 1))
-      (setq s (ash s 1)))
-    ;; Scale value by requested amount, and update error bounds.
-    (when scale
-      (if (minusp scale)
-         (let ((scale-factor (expt 10 (- scale))))
-           (setq s (* s scale-factor)))
-         (let ((scale-factor (expt 10 scale)))
-           (setq r (* r scale-factor))
-           (setq m+ (* m+ scale-factor))
-           (setq m- (* m- scale-factor)))))
-    ;; Scale r and s and compute initial k, the base 10 logarithm of r.
-    (do ()
-       ((>= r (ceiling s 10)))
-      (decf k)
-      (setq r (* r 10))
-      (setq m- (* m- 10))
-      (setq m+ (* m+ 10)))
-    (do ()(nil)
-      (do ()
-         ((< (+ (ash r 1) m+) (ash s 1)))
-       (setq s (* s 10))
-       (incf k))
-      ;; Determine number of fraction digits to generate.
-      (cond (fdigits
-            ;; Use specified number of fraction digits.
-            (setq cutoff (- fdigits))
-            ;;don't allow less than fmin fraction digits
-            (if (and fmin (> cutoff (- fmin))) (setq cutoff (- fmin))))
-           (width
-            ;; Use as many fraction digits as width will permit but
-            ;; force at least fmin digits even if width will be
-            ;; exceeded.
-            (if (< k 0)
-                (setq cutoff (- 1 width))
-                (setq cutoff (1+ (- k width))))
-            (if (and fmin (> cutoff (- fmin))) (setq cutoff (- fmin)))))
-      ;; If we decided to cut off digit generation before precision
-      ;; has been exhausted, rounding the last digit may cause a carry
-      ;; propagation. We can prevent this, preserving left-to-right
-      ;; digit generation, with a few magical adjustments to m- and
-      ;; m+. Of course, correct rounding is also preserved.
-      (when (or fdigits width)
-       (let ((a (- cutoff k))
-             (y s))
-         (if (>= a 0)
-             (dotimes (i a) (setq y (* y 10)))
-             (dotimes (i (- a)) (setq y (ceiling y 10))))
-         (setq m- (max y m-))
-         (setq m+ (max y m+))
-         (when (= m+ y) (setq roundup t))))
-      (when (< (+ (ash r 1) m+) (ash s 1)) (return)))
-    ;; Zero-fill before fraction if no integer part.
-    (when (< k 0)
-      (setq decpnt digits)
-      (vector-push-extend #\. digit-string)
-      (dotimes (i (- k))
-       (incf digits) (vector-push-extend #\0 digit-string)))
-    ;; Generate the significant digits.
-    (do ()(nil)
-      (decf k)
-      (when (= k -1)
-       (vector-push-extend #\. digit-string)
-       (setq decpnt digits))
-      (multiple-value-setq (u r) (truncate (* r 10) s))
-      (setq m- (* m- 10))
-      (setq m+ (* m+ 10))
-      (setq low (< (ash r 1) m-))
-      (if roundup
-         (setq high (>= (ash r 1) (- (ash s 1) m+)))
-         (setq high (> (ash r 1) (- (ash s 1) m+))))
-      ;; Stop when either precision is exhausted or we have printed as
-      ;; many fraction digits as permitted.
-      (when (or low high (and cutoff (<= k cutoff))) (return))
-      (vector-push-extend (char *digits* u) digit-string)
-      (incf digits))
-    ;; If cutoff occurred before first digit, then no digits are
-    ;; generated at all.
-    (when (or (not cutoff) (>= k cutoff))
-      ;; Last digit may need rounding
-      (vector-push-extend (char *digits*
-                               (cond ((and low (not high)) u)
-                                     ((and high (not low)) (1+ u))
-                                     (t (if (<= (ash r 1) s) u (1+ u)))))
-                         digit-string)
-      (incf digits))
-    ;; Zero-fill after integer part if no fraction.
-    (when (>= k 0)
-      (dotimes (i k) (incf digits) (vector-push-extend #\0 digit-string))
-      (vector-push-extend #\. digit-string)
-      (setq decpnt digits))
-    ;; Add trailing zeroes to pad fraction if fdigits specified.
-    (when fdigits
-      (dotimes (i (- fdigits (- digits decpnt)))
-       (incf digits)
-       (vector-push-extend #\0 digit-string)))
-    ;; all done
-    (values digit-string (1+ digits) (= decpnt 0) (= decpnt digits) decpnt)))
-
+         ;; Zero is a special case which FLOAT-STRING cannot handle.
+         (if fdigits
+             (let ((s (make-string (1+ fdigits) :initial-element #\0)))
+               (setf (schar s 0) #\.)
+               (values s (length s) t (zerop fdigits) 0))
+             (values "." 1 t t 0)))
+        (t
+         (multiple-value-bind (e string)
+             (if fdigits
+                 (flonum-to-digits x (min (- fdigits) (- (or fmin 0))))
+                 (if (and width (> width 1))
+                     (let ((w (multiple-value-list (flonum-to-digits x (1- width) t)))
+                           (f (multiple-value-list (flonum-to-digits x (- (or fmin 0))))))
+                       (cond
+                         ((>= (length (cadr w)) (length (cadr f)))
+                          (values-list w))
+                         (t (values-list f))))
+                     (flonum-to-digits x)))
+           (let ((e (+ e (or scale 0)))
+                 (stream (make-string-output-stream)))
+             (if (plusp e)
+                 (progn
+                   (write-string string stream :end (min (length string) e))
+                   (dotimes (i (- e (length string)))
+                     (write-char #\0 stream))
+                   (write-char #\. stream)
+                   (write-string string stream :start (min (length string) e))
+                   (when fdigits
+                     (dotimes (i (- fdigits
+                                    (- (length string)
+                                       (min (length string) e))))
+                       (write-char #\0 stream))))
+                 (progn
+                   (write-string "." stream)
+                   (dotimes (i (- e))
+                     (write-char #\0 stream))
+                   (write-string string stream)
+                   (when fdigits
+                     (dotimes (i (+ fdigits e (- (length string))))
+                       (write-char #\0 stream)))))
+             (let ((string (get-output-stream-string stream)))
+               (values string (length string)
+                       (char= (char string 0) #\.)
+                       (char= (char string (1- (length string))) #\.)
+                       (position #\. string))))))))
+
+;;; implementation of figure 1 from Burger and Dybvig, 1996.  As the
+;;; implementation of the Dragon from Classic CMUCL (and previously in
+;;; SBCL above FLONUM-TO-STRING) says: "DO NOT EVEN THINK OF
+;;; ATTEMPTING TO UNDERSTAND THIS CODE WITHOUT READING THE PAPER!",
+;;; and in this case we have to add that even reading the paper might
+;;; not bring immediate illumination as CSR has attempted to turn
+;;; idiomatic Scheme into idiomatic Lisp.
+;;;
+;;; FIXME: figure 1 from Burger and Dybvig is the unoptimized
+;;; algorithm, noticeably slow at finding the exponent.  Figure 2 has
+;;; an improved algorithm, but CSR ran out of energy.
+;;;
+;;; possible extension for the enthusiastic: printing floats in bases
+;;; other than base 10.
+(defconstant single-float-min-e
+  (nth-value 1 (decode-float least-positive-single-float)))
+(defconstant double-float-min-e
+  (nth-value 1 (decode-float least-positive-double-float)))
+#!+long-float
+(defconstant long-float-min-e
+  (nth-value 1 (decode-float least-positive-long-float)))
+
+(defun flonum-to-digits (v &optional position relativep)
+  (let ((print-base 10) ; B
+        (float-radix 2) ; b
+        (float-digits (float-digits v)) ; p
+        (digit-characters "0123456789")
+        (min-e
+         (etypecase v
+           (single-float single-float-min-e)
+           (double-float double-float-min-e)
+           #!+long-float
+           (long-float long-float-min-e))))
+    (multiple-value-bind (f e)
+        (integer-decode-float v)
+      (let (;; FIXME: these even tests assume normal IEEE rounding
+            ;; mode.  I wonder if we should cater for non-normal?
+            (high-ok (evenp f))
+            (low-ok (evenp f))
+            (result (make-array 50 :element-type 'base-char
+                                :fill-pointer 0 :adjustable t)))
+        (labels ((scale (r s m+ m-)
+                   (do ((k 0 (1+ k))
+                        (s s (* s print-base)))
+                       ((not (or (> (+ r m+) s)
+                                 (and high-ok (= (+ r m+) s))))
+                        (do ((k k (1- k))
+                             (r r (* r print-base))
+                             (m+ m+ (* m+ print-base))
+                             (m- m- (* m- print-base)))
+                            ((not (or (< (* (+ r m+) print-base) s)
+                                      (and (not high-ok)
+                                           (= (* (+ r m+) print-base) s))))
+                             (values k (generate r s m+ m-)))))))
+                 (generate (r s m+ m-)
+                   (let (d tc1 tc2)
+                     (tagbody
+                      loop
+                        (setf (values d r) (truncate (* r print-base) s))
+                        (setf m+ (* m+ print-base))
+                        (setf m- (* m- print-base))
+                        (setf tc1 (or (< r m-) (and low-ok (= r m-))))
+                        (setf tc2 (or (> (+ r m+) s)
+                                      (and high-ok (= (+ r m+) s))))
+                        (when (or tc1 tc2)
+                          (go end))
+                        (vector-push-extend (char digit-characters d) result)
+                        (go loop)
+                      end
+                        (let ((d (cond
+                                   ((and (not tc1) tc2) (1+ d))
+                                   ((and tc1 (not tc2)) d)
+                                   (t ; (and tc1 tc2)
+                                    (if (< (* r 2) s) d (1+ d))))))
+                          (vector-push-extend (char digit-characters d) result)
+                          (return-from generate result)))))
+                 (initialize ()
+                   (let (r s m+ m-)
+                     (if (>= e 0)
+                         (let* ((be (expt float-radix e))
+                                (be1 (* be float-radix)))
+                           (if (/= f (expt float-radix (1- float-digits)))
+                               (setf r (* f be 2)
+                                     s 2
+                                     m+ be
+                                     m- be)
+                               (setf r (* f be1 2)
+                                     s (* float-radix 2)
+                                     m+ be1
+                                     m- be)))
+                         (if (or (= e min-e)
+                                 (/= f (expt float-radix (1- float-digits))))
+                             (setf r (* f 2)
+                                   s (* (expt float-radix (- e)) 2)
+                                   m+ 1
+                                   m- 1)
+                             (setf r (* f float-radix 2)
+                                   s (* (expt float-radix (- 1 e)) 2)
+                                   m+ float-radix
+                                   m- 1)))
+                     (when position
+                       (when relativep
+                         (aver (> position 0))
+                         (do ((k 0 (1+ k))
+                              ;; running out of letters here
+                              (l 1 (* l print-base)))
+                             ((>= (* s l) (+ r m+))
+                              ;; k is now \hat{k}
+                              (if (< (+ r (* s (/ (expt print-base (- k position)) 2)))
+                                     (* s (expt print-base k)))
+                                  (setf position (- k position))
+                                  (setf position (- k position 1))))))
+                       (let ((low (max m- (/ (* s (expt print-base position)) 2)))
+                             (high (max m+ (/ (* s (expt print-base position)) 2))))
+                         (when (<= m- low)
+                           (setf m- low)
+                           (setf low-ok t))
+                         (when (<= m+ high)
+                           (setf m+ high)
+                           (setf high-ok t))))
+                     (values r s m+ m-))))
+          (multiple-value-bind (r s m+ m-) (initialize)
+            (scale r s m+ m-)))))))
+\f
 ;;; Given a non-negative floating point number, SCALE-EXPONENT returns
 ;;; a new floating point number Z in the range (0.1, 1.0] and an
 ;;; exponent E such that Z * 10^E is (approximately) equal to the
 ;;; part of the computation to avoid over/under flow. When
 ;;; denormalized, we must pull out a large factor, since there is more
 ;;; negative exponent range than positive range.
+
+(eval-when (:compile-toplevel :execute)
+  (setf *read-default-float-format*
+        #!+long-float 'long-float #!-long-float 'double-float))
 (defun scale-exponent (original-x)
   (let* ((x (coerce original-x 'long-float)))
     (multiple-value-bind (sig exponent) (decode-float x)
       (declare (ignore sig))
-      (if (= x 0.0l0)
-         (values (float 0.0l0 original-x) 1)
-         (let* ((ex (round (* exponent (log 2l0 10))))
-                (x (if (minusp ex)
-                       (if (float-denormalized-p x)
-                           #!-long-float
-                           (* x 1.0l16 (expt 10.0l0 (- (- ex) 16)))
-                           #!+long-float
-                           (* x 1.0l18 (expt 10.0l0 (- (- ex) 18)))
-                           (* x 10.0l0 (expt 10.0l0 (- (- ex) 1))))
-                       (/ x 10.0l0 (expt 10.0l0 (1- ex))))))
-           (do ((d 10.0l0 (* d 10.0l0))
-                (y x (/ x d))
-                (ex ex (1+ ex)))
-               ((< y 1.0l0)
-                (do ((m 10.0l0 (* m 10.0l0))
-                     (z y (* y m))
-                     (ex ex (1- ex)))
-                    ((>= z 0.1l0)
-                     (values (float z original-x) ex))))))))))
+      (if (= x 0.0e0)
+          (values (float 0.0e0 original-x) 1)
+          (let* ((ex (locally (declare (optimize (safety 0)))
+                       (the fixnum
+                         (round (* exponent (log 2e0 10))))))
+                 (x (if (minusp ex)
+                        (if (float-denormalized-p x)
+                            #!-long-float
+                            (* x 1.0e16 (expt 10.0e0 (- (- ex) 16)))
+                            #!+long-float
+                            (* x 1.0e18 (expt 10.0e0 (- (- ex) 18)))
+                            (* x 10.0e0 (expt 10.0e0 (- (- ex) 1))))
+                        (/ x 10.0e0 (expt 10.0e0 (1- ex))))))
+            (do ((d 10.0e0 (* d 10.0e0))
+                 (y x (/ x d))
+                 (ex ex (1+ ex)))
+                ((< y 1.0e0)
+                 (do ((m 10.0e0 (* m 10.0e0))
+                      (z y (* y m))
+                      (ex ex (1- ex)))
+                     ((>= z 0.1e0)
+                      (values (float z original-x) ex))
+                   (declare (long-float m) (integer ex))))
+              (declare (long-float d))))))))
+(eval-when (:compile-toplevel :execute)
+  (setf *read-default-float-format* 'single-float))
 \f
 ;;;; entry point for the float printer
 
 ;;; attractive to handle exponential notation with the same accuracy
 ;;; as non-exponential notation, using the method described in the
 ;;; Steele and White paper.
+;;;
+;;; NOTE II: this has been bypassed slightly by implementing Burger
+;;; and Dybvig, 1996.  When someone has time (KLUDGE) they can
+;;; probably (a) implement the optimizations suggested by Burger and
+;;; Dyvbig, and (b) remove all vestiges of Dragon4, including from
+;;; fixed-format printing.
 
 ;;; Print the appropriate exponent marker for X and the specified exponent.
 (defun print-float-exponent (x exp stream)
   (declare (type float x) (type integer exp) (type stream stream))
   (let ((*print-radix* nil)
-       (plusp (plusp exp)))
+        (plusp (plusp exp)))
     (if (typep x *read-default-float-format*)
-       (unless (eql exp 0)
-         (format stream "e~:[~;+~]~D" plusp exp))
-       (format stream "~C~:[~;+~]~D"
-               (etypecase x
-                 (single-float #\f)
-                 (double-float #\d)
-                 (short-float #\s)
-                 (long-float #\L))
-               plusp exp))))
+        (unless (eql exp 0)
+          (format stream "e~:[~;+~]~D" plusp exp))
+        (format stream "~C~:[~;+~]~D"
+                (etypecase x
+                  (single-float #\f)
+                  (double-float #\d)
+                  (short-float #\s)
+                  (long-float #\L))
+                plusp exp))))
 
 (defun output-float-infinity (x stream)
   (declare (float x) (stream stream))
     (output-float-nan x stream))
    (t
     (let ((x (cond ((minusp (float-sign x))
-                   (write-char #\- stream)
-                   (- x))
-                  (t
-                   x))))
+                    (write-char #\- stream)
+                    (- x))
+                   (t
+                    x))))
       (cond
        ((zerop x)
-       (write-string "0.0" stream)
-       (print-float-exponent x 0 stream))
+        (write-string "0.0" stream)
+        (print-float-exponent x 0 stream))
        (t
-       (output-float-aux x stream (float 1/1000 x) (float 10000000 x))))))))
+        (output-float-aux x stream -3 8)))))))
 (defun output-float-aux (x stream e-min e-max)
-  (if (and (>= x e-min) (< x e-max))
-      ;; free format
-      (multiple-value-bind (str len lpoint tpoint) (flonum-to-string x)
-       (declare (ignore len))
-       (when lpoint (write-char #\0 stream))
-       (write-string str stream)
-       (when tpoint (write-char #\0 stream))
-       (print-float-exponent x 0 stream))
-      ;; exponential format
-      (multiple-value-bind (f ex) (scale-exponent x)
-       (multiple-value-bind (str len lpoint tpoint)
-           (flonum-to-string f nil nil 1)
-         (declare (ignore len))
-         (when lpoint (write-char #\0 stream))
-         (write-string str stream)
-         (when tpoint (write-char #\0 stream))
-         ;; Subtract out scale factor of 1 passed to FLONUM-TO-STRING.
-         (print-float-exponent x (1- ex) stream)))))
+  (multiple-value-bind (e string)
+      (flonum-to-digits x)
+    (cond
+      ((< e-min e e-max)
+       (if (plusp e)
+           (progn
+             (write-string string stream :end (min (length string) e))
+             (dotimes (i (- e (length string)))
+               (write-char #\0 stream))
+             (write-char #\. stream)
+             (write-string string stream :start (min (length string) e))
+             (when (<= (length string) e)
+               (write-char #\0 stream))
+             (print-float-exponent x 0 stream))
+           (progn
+             (write-string "0." stream)
+             (dotimes (i (- e))
+               (write-char #\0 stream))
+             (write-string string stream)
+             (print-float-exponent x 0 stream))))
+      (t (write-string string stream :end 1)
+         (write-char #\. stream)
+         (write-string string stream :start 1)
+         (when (= (length string) 1)
+           (write-char #\0 stream))
+         (print-float-exponent x (1- e) stream)))))
 \f
 ;;;; other leaf objects
 
 ;;; the character name or the character in the #\char format.
 (defun output-character (char stream)
   (if (or *print-escape* *print-readably*)
-      (let ((name (char-name char)))
-       (write-string "#\\" stream)
-       (if name
-           (quote-string name stream)
-           (write-char char stream)))
+      (let ((graphicp (graphic-char-p char))
+            (name (char-name char)))
+        (write-string "#\\" stream)
+        (if (and name (not graphicp))
+            (quote-string name stream)
+            (write-char char stream)))
       (write-char char stream)))
 
 (defun output-sap (sap stream)
   (declare (type system-area-pointer sap))
   (cond (*read-eval*
-        (format stream "#.(~S #X~8,'0X)" 'int-sap (sap-int sap)))
-       (t
-        (print-unreadable-object (sap stream)
-          (format stream "system area pointer: #X~8,'0X" (sap-int sap))))))
+         (format stream "#.(~S #X~8,'0X)" 'int-sap (sap-int sap)))
+        (t
+         (print-unreadable-object (sap stream)
+           (format stream "system area pointer: #X~8,'0X" (sap-int sap))))))
 
 (defun output-weak-pointer (weak-pointer stream)
   (declare (type weak-pointer weak-pointer))
   (print-unreadable-object (weak-pointer stream)
     (multiple-value-bind (value validp) (weak-pointer-value weak-pointer)
       (cond (validp
-            (write-string "weak pointer: " stream)
-            (write value :stream stream))
-           (t
-            (write-string "broken weak pointer" stream))))))
+             (write-string "weak pointer: " stream)
+             (write value :stream stream))
+            (t
+             (write-string "broken weak pointer" stream))))))
 
 (defun output-code-component (component stream)
   (print-unreadable-object (component stream :identity t)
     (let ((dinfo (%code-debug-info component)))
       (cond ((eq dinfo :bogus-lra)
-            (write-string "bogus code object" stream))
-           (t
-            (write-string "code object" stream)
-            (when dinfo
-              (write-char #\space stream)
-              (output-object (sb!c::debug-info-name dinfo) stream)))))))
+             (write-string "bogus code object" stream))
+            (t
+             (write-string "code object" stream)
+             (when dinfo
+               (write-char #\space stream)
+               (output-object (sb!c::debug-info-name dinfo) stream)))))))
 
 (defun output-lra (lra stream)
   (print-unreadable-object (lra stream :identity t)
   (declare (ignore object stream))
   nil)
 
-(defun output-function (object stream)
-  (let* ((*print-length* 3) ; in case we have to..
-        (*print-level* 3)  ; ..print an interpreted function definition
-        ;; FIXME: This find-the-function-name idiom ought to be
-        ;; encapsulated in a function somewhere.
-        (name (case (function-subtype object)
-                (#.sb!vm:closure-header-widetag "CLOSURE")
-                (#.sb!vm:simple-fun-header-widetag (%simple-fun-name object))
-                (t 'no-name-available)))
-        (identified-by-name-p (and (symbolp name)
-                                   (fboundp name)
-                                   (eq (fdefinition name) object))))
-      (print-unreadable-object (object
-                               stream
-                               :identity (not identified-by-name-p))
-       (prin1 'function stream)
-       (unless (eq name 'no-name-available)
-         (format stream " ~S" name)))))
+(defun output-fun (object stream)
+    (let* ((*print-length* 3)  ; in case we have to..
+           (*print-level* 3)  ; ..print an interpreted function definition
+           (name (%fun-name object))
+           (proper-name-p (and (legal-fun-name-p name) (fboundp name)
+                               (eq (fdefinition name) object))))
+      (print-unreadable-object (object stream :identity (not proper-name-p))
+        (format stream "~:[FUNCTION~;CLOSURE~]~@[ ~S~]"
+                (closurep object)
+                name))))
 \f
 ;;;; catch-all for unknown things
 
   (print-unreadable-object (object stream :identity t)
     (let ((lowtag (lowtag-of object)))
       (case lowtag
-       (#.sb!vm:other-pointer-lowtag
-         (let ((widetag (widetag-of object)))
-           (case widetag
-             (#.sb!vm:value-cell-header-widetag
-              (write-string "value cell " stream)
-              (output-object (value-cell-ref object) stream))
-             (t
-              (write-string "unknown pointer object, widetag=" stream)
-              (let ((*print-base* 16) (*print-radix* t))
-                (output-integer widetag stream))))))
-       ((#.sb!vm:fun-pointer-lowtag
-         #.sb!vm:instance-pointer-lowtag
-         #.sb!vm:list-pointer-lowtag)
-        (write-string "unknown pointer object, lowtag=" stream)
-        (let ((*print-base* 16) (*print-radix* t))
-          (output-integer lowtag stream)))
-       (t
-        (case (widetag-of object)
-          (#.sb!vm:unbound-marker-widetag
-           (write-string "unbound marker" stream))
-          (t
-           (write-string "unknown immediate object, lowtag=" stream)
-           (let ((*print-base* 2) (*print-radix* t))
-             (output-integer lowtag stream))
-           (write-string ", widetag=" stream)
-           (let ((*print-base* 16) (*print-radix* t))
-             (output-integer (widetag-of object) stream)))))))))
+        (#.sb!vm:other-pointer-lowtag
+          (let ((widetag (widetag-of object)))
+            (case widetag
+              (#.sb!vm:value-cell-header-widetag
+               (write-string "value cell " stream)
+               (output-object (value-cell-ref object) stream))
+              (t
+               (write-string "unknown pointer object, widetag=" stream)
+               (let ((*print-base* 16) (*print-radix* t))
+                 (output-integer widetag stream))))))
+        ((#.sb!vm:fun-pointer-lowtag
+          #.sb!vm:instance-pointer-lowtag
+          #.sb!vm:list-pointer-lowtag)
+         (write-string "unknown pointer object, lowtag=" stream)
+         (let ((*print-base* 16) (*print-radix* t))
+           (output-integer lowtag stream)))
+        (t
+         (case (widetag-of object)
+           (#.sb!vm:unbound-marker-widetag
+            (write-string "unbound marker" stream))
+           (t
+            (write-string "unknown immediate object, lowtag=" stream)
+            (let ((*print-base* 2) (*print-radix* t))
+              (output-integer lowtag stream))
+            (write-string ", widetag=" stream)
+            (let ((*print-base* 16) (*print-radix* t))
+              (output-integer (widetag-of object) stream)))))))))