+ (delete-file p)))))
+
+\f
+;;;; KOI8-R external format
+(with-open-file (s *test-path* :direction :output
+ :if-exists :supersede :external-format :koi8-r)
+ (write-char (code-char #xB0) s)
+ (assert (eq
+ (handler-case
+ (progn
+ (write-char (code-char #xBAAD) s)
+ :bad)
+ (sb-int:character-encoding-error ()
+ :good))
+ :good)))
+(with-open-file (s *test-path* :direction :input
+ :element-type '(unsigned-byte 8))
+ (let ((byte (read-byte s)))
+ (assert (= (eval byte) #x9C))))
+(with-open-file (s *test-path* :direction :input
+ :external-format :koi8-r)
+ (let ((char (read-char s)))
+ (assert (= (char-code (eval char)) #xB0))))
+(delete-file *test-path*)
+
+(let* ((koi8-r-codes (coerce '(240 210 201 215 197 212 33) '(vector (unsigned-byte 8))))
+ (uni-codes #(1055 1088 1080 1074 1077 1090 33))
+
+ (string (octets-to-string koi8-r-codes :external-format :koi8-r))
+ (uni-decoded (map 'vector #'char-code string)))
+ (assert (equalp (map 'vector #'char-code (octets-to-string koi8-r-codes :external-format :koi8-r))
+ uni-codes))
+ (assert (equalp (string-to-octets (map 'string #'code-char uni-codes) :external-format :koi8-r)
+ koi8-r-codes)))
+\f
+;;; tests of FILE-STRING-LENGTH
+(let ((standard-characters "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!$\"'(),_-./:;?+<=>#%&*@[\\]{|}`^~"))
+ (do-external-formats (xf)
+ (with-open-file (s *test-path* :direction :output
+ :external-format xf)
+ (loop for x across standard-characters
+ for position = (file-position s)
+ for char-length = (file-string-length s x)
+ do (write-char x s)
+ do (assert (= (file-position s) (+ position char-length))))
+ (let ((position (file-position s))
+ (string-length (file-string-length s standard-characters)))
+ (write-string standard-characters s)
+ (assert (= (file-position s) (+ position string-length)))))
+ (delete-file *test-path*)))
+
+(let ((char-codes '(0 1 255 256 511 512 1023 1024 2047 2048 4095 4096
+ 8191 8192 16383 16384 32767 32768 65535 65536 131071
+ 131072 262143 262144)))
+ (with-open-file (s *test-path* :direction :output
+ :external-format :utf-8)
+ (dolist (code char-codes)
+ (let* ((char (code-char code))
+ (position (file-position s))
+ (char-length (file-string-length s char)))
+ (write-char char s)
+ (assert (= (file-position s) (+ position char-length)))))
+ (let* ((string (map 'string #'code-char char-codes))
+ (position (file-position s))
+ (string-length (file-string-length s string)))
+ (write-string string s)
+ (assert (= (file-position s) (+ position string-length))))))
+\f
+
+;;; See sbcl-devel "Subject: Bug in FILE-POSITION on UTF-8-encoded files"
+;;; by Lutz Euler on 2006-03-05 for more details.
+(with-test (:name (:file-position :utf-8))
+ (let ((path *test-path*))
+ (with-open-file (s path
+ :direction :output
+ :if-exists :supersede
+ :element-type '(unsigned-byte 8))
+ ;; Write #\*, encoded in UTF-8, to the file.
+ (write-byte 42 s)
+ ;; Append #\adiaeresis, encoded in UTF-8, to the file.
+ (write-sequence '(195 164) s))
+ (with-open-file (s path :external-format :utf-8)
+ (read-char s)
+ (let ((pos (file-position s))
+ (char (read-char s)))
+ (format t "read character with code ~a successfully from file position ~a~%"
+ (char-code char) pos)
+ (file-position s pos)
+ (format t "set file position back to ~a, trying to read-char again~%" pos)
+ (let ((new-char (read-char s)))
+ (assert (char= char new-char)))))
+ (values)))
+(delete-file *test-path*)
+
+;;; We used to call STREAM-EXTERNAL-FORMAT on the stream in the error
+;;; when printing a coding error, but that didn't work if the stream
+;;; was closed by the time the error was printed. See sbcl-devel
+;;; "Subject: Printing coding errors for closed streams" by Zach Beane
+;;; on 2008-10-16 for more info.
+(with-test (:name (:character-coding-error-stream-external-format))
+ (flet ((first-file-character ()
+ (with-open-file (stream *test-path* :external-format :utf-8)
+ (read-char stream))))
+ (with-open-file (stream *test-path*
+ :direction :output
+ :if-exists :supersede
+ :element-type '(unsigned-byte 8))
+ (write-byte 192 stream))
+ (princ-to-string (nth-value 1 (ignore-errors (first-file-character))))))
+(delete-file *test-path*)
+
+;;; External format support in SB-ALIEN
+
+(with-test (:name (:sb-alien :vanilla))
+ (define-alien-routine strdup c-string (str c-string))
+ (assert (equal "foo" (strdup "foo"))))
+
+(with-test (:name (:sb-alien :utf-8 :utf-8))
+ (define-alien-routine strdup (c-string :external-format :utf-8)
+ (str (c-string :external-format :utf-8)))
+ (assert (equal "foo" (strdup "foo"))))
+
+(with-test (:name (:sb-alien :latin-1 :utf-8))
+ (define-alien-routine strdup (c-string :external-format :latin-1)
+ (str (c-string :external-format :utf-8)))
+ (assert (= (length (strdup (string (code-char 246))))
+ 2)))
+
+(with-test (:name (:sb-alien :utf-8 :latin-1))
+ (define-alien-routine strdup (c-string :external-format :utf-8)
+ (str (c-string :external-format :latin-1)))
+ (assert (equal (string (code-char 228))
+ (strdup (concatenate 'string
+ (list (code-char 195))
+ (list (code-char 164)))))))
+
+(with-test (:name (:sb-alien :ebcdic :ebcdic))
+ (define-alien-routine strdup (c-string :external-format :ebcdic-us)
+ (str (c-string :external-format :ebcdic-us)))
+ (assert (equal "foo" (strdup "foo"))))
+
+(with-test (:name (:sb-alien :latin-1 :ebcdic))
+ (define-alien-routine strdup (c-string :external-format :latin-1)
+ (str (c-string :external-format :ebcdic-us)))
+ (assert (not (equal "foo" (strdup "foo")))))