(:<encoding> :replacement <character>) as an external format which will
automatically substitute <character> on encoding or decoding errors for
streams and for STRING-TO-OCTETS and its inverse.
+ ** improvement: the file streams underlying the standard streams (such as
+ *STANDARD-INPUT*, *TERMINAL-IO*) are opened with an external format
+ which uses the replacement mechanism to handle encoding errors,
+ preventing various infinite error chains and unrecoverable I/O
+ confusion.
** minor incompatible change: the utf-8 external format now correctly
refuses to encode Lisp characters in the surrogate range (char-codes
between #xd800 and #xdfff).
;; All the names that can refer to this external format. The first
;; one is the canonical name.
(names (missing-arg) :type list :read-only t)
+ (default-replacement-character (missing-arg) :type character)
(read-n-chars-fun (missing-arg) :type function)
(read-char-fun (missing-arg) :type function)
(write-n-bytes-fun (missing-arg) :type function)
(canonical-name (&rest other-names)
out-form in-form octets-to-string-symbol string-to-octets-symbol)
`(define-external-format/variable-width (,canonical-name ,@other-names)
- t 1
+ t #\? 1
,out-form
1
,in-form
,string-to-octets-symbol))
(defmacro define-external-format/variable-width
- (external-format output-restart out-size-expr
- out-expr in-size-expr in-expr
+ (external-format output-restart replacement-character
+ out-size-expr out-expr in-size-expr in-expr
octets-to-string-sym string-to-octets-sym)
(let* ((name (first external-format))
(out-function (symbolicate "OUTPUT-BYTES/" name))
(let ((entry (%make-external-format
:names ',external-format
+ :default-replacement-character ,replacement-character
:read-n-chars-fun #',in-function
:read-char-fun #',in-char-function
:write-n-bytes-fun #',out-function
(without-package-locks
(makunbound '*available-buffers*))))
+(defun stdstream-external-format (outputp)
+ (declare (ignorable outputp))
+ (let* ((keyword #!+win32 (if outputp (sb!win32::console-output-codepage) (sb!win32::console-input-codepage))
+ #!-win32 (default-external-format))
+ (ef (get-external-format keyword))
+ (replacement (ef-default-replacement-character ef)))
+ `(,keyword :replacement ,replacement)))
+
;;; This is called whenever a saved core is restarted.
(defun stream-reinit (&optional init-buffers-p)
(when init-buffers-p
(with-output-to-string (*error-output*)
(setf *stdin*
(make-fd-stream 0 :name "standard input" :input t :buffering :line
- #!+win32 :external-format #!+win32 (sb!win32::console-input-codepage)))
+ :external-format (stdstream-external-format nil)))
(setf *stdout*
(make-fd-stream 1 :name "standard output" :output t :buffering :line
- #!+win32 :external-format #!+win32 (sb!win32::console-output-codepage)))
+ :external-format (stdstream-external-format t)))
(setf *stderr*
(make-fd-stream 2 :name "standard error" :output t :buffering :line
- #!+win32 :external-format #!+win32 (sb!win32::console-output-codepage)))
+ :external-format (stdstream-external-format t)))
(let* ((ttyname #.(coerce "/dev/tty" 'simple-base-string))
(tty (sb!unix:unix-open ttyname sb!unix:o_rdwr #o666)))
(if tty
(setf *tty*
- (make-fd-stream tty
- :name "the terminal"
- :input t
- :output t
- :buffering :line
+ (make-fd-stream tty :name "the terminal"
+ :input t :output t :buffering :line
+ :external-format (stdstream-external-format t)
:auto-close t))
(setf *tty* (make-two-way-stream *stdin* *stdout*))))
(princ (get-output-stream-string *error-output*) *stderr*))