1 ;;; -*- Mode: LISP; Syntax: ANSI-Common-Lisp; Package: STREAM -*-
3 ;;; **********************************************************************
4 ;;; This code was written by Paul Foley and has been placed in the public
8 ;;; Sbcl port by Rudi Schlatte.
10 (in-package "SB-SIMPLE-STREAMS")
13 ;;; **********************************************************************
15 ;;; Various functions needed by simple-streams
16 (declaim (inline buffer-sap bref
(setf bref
) buffer-copy
17 allocate-buffer free-buffer
))
19 (defun buffer-sap (thing &optional offset
)
20 (declare (type simple-stream-buffer thing
) (type (or fixnum null
) offset
)
21 (optimize (speed 3) (space 2) (debug 0) (safety 0)
22 ;; Suppress the note about having to box up the return:
23 (sb-ext:inhibit-warnings
3)))
24 (let ((sap (if (vectorp thing
) (sb-sys:vector-sap thing
) thing
)))
25 (if offset
(sb-sys:sap
+ sap offset
) sap
)))
27 (defun bref (buffer index
)
28 (declare (type simple-stream-buffer buffer
)
29 (type (integer 0 #.most-positive-fixnum
) index
))
31 (sb-sys:with-pinned-objects
(buffer)
32 (sb-sys:sap-ref-8
(sb-sys:vector-sap buffer
) index
))
33 (sb-sys:sap-ref-8 buffer index
)))
35 (defun (setf bref
) (octet buffer index
)
36 (declare (type (unsigned-byte 8) octet
)
37 (type simple-stream-buffer buffer
)
38 (type (integer 0 #.most-positive-fixnum
) index
))
40 (sb-sys:with-pinned-objects
(buffer)
41 (setf (sb-sys:sap-ref-8
(sb-sys:vector-sap buffer
) index
) octet
))
42 (setf (sb-sys:sap-ref-8 buffer index
) octet
)))
44 (defun buffer-copy (src soff dst doff length
)
45 (declare (type simple-stream-buffer src dst
)
46 (type fixnum soff doff length
))
47 ;; FIXME: Should probably be with-pinned-objects
49 (sb-kernel:system-area-ub8-copy
(buffer-sap src
) soff
53 (defun allocate-buffer (size)
54 (make-array size
:element-type
'(unsigned-byte 8)))
56 (defun free-buffer (buffer)
57 (sb-int:aver
(typep buffer
'(simple-array (unsigned-byte 8) (*))))
60 (defun make-control-table (&rest inits
)
61 (let ((table (make-array 32 :initial-element nil
)))
62 (do* ((char (pop inits
) (pop inits
))
63 (func (pop inits
) (pop inits
)))
65 (when (< (char-code char
) 32)
66 (setf (aref table
(char-code char
)) func
)))
69 (defun std-newline-out-handler (stream character
)
70 (declare (ignore character
))
71 (with-stream-class (simple-stream stream
)
72 (setf (sm charpos stream
) -
1)
75 (defun std-tab-out-handler (stream character
)
76 (declare (ignore character
))
77 (with-stream-class (simple-stream stream
)
78 (let ((col (sm charpos stream
)))
80 (setf (sm charpos stream
) (1- (* 8 (1+ (floor col
8)))))))
83 (defun std-dc-newline-in-handler (stream character
)
84 (with-stream-class (dual-channel-simple-stream stream
)
85 ;; FIXME: Currently, -1 is wrong, since callers of CHARPOS expect
86 ;; a result in (or null (and fixnum unsigned-byte)), so they must
87 ;; never see this temporary value. Note that in
88 ;; STD-NEWLINE-OUT-HANDLER it is correct to use -1, since CHARPOS
89 ;; is incremented to zero before WRITE-CHAR returns. Perhaps the
90 ;; same should happen for input?
91 (setf (sm charpos stream
) 0) ; was -1
94 (defvar *std-control-out-table
*
95 (make-control-table #\Newline
#'std-newline-out-handler
96 #\Tab
#'std-tab-out-handler
))
98 (defvar *default-external-format
* :iso8859-1
)
100 (defvar *external-formats
* (make-hash-table))
101 (defvar *external-format-aliases
* (make-hash-table))
103 (defstruct (external-format
105 (:print-function %print-external-format
)
106 (:constructor make-external-format
(name octets-to-char
108 (name (sb-int:missing-arg
) :type keyword
:read-only t
)
109 (octets-to-char (sb-int:missing-arg
) :type function
:read-only t
)
110 (char-to-octets (sb-int:missing-arg
) :type function
:read-only t
))
112 (defun %print-external-format
(ef stream depth
)
113 (declare (ignore depth
))
114 (print-unreadable-object (ef stream
:type t
:identity t
)
115 (princ (ef-name ef
) stream
)))
117 (defmacro define-external-format
(name octets-to-char char-to-octets
)
118 `(macrolet ((octets-to-char ((state input unput
) &body body
)
119 `(lambda (,state
,input
,unput
)
120 (declare (type (function () (unsigned-byte 8)) ,input
)
121 (type (function (sb-int:index
) t
) ,unput
)
122 (ignorable ,state
,input
,unput
)
123 (values character sb-int
:index t
))
125 (char-to-octets ((char state output
) &body body
)
126 `(lambda (,char
,state
,output
)
127 (declare (type character
,char
)
128 (type (function ((unsigned-byte 8)) t
) ,output
)
129 (ignorable state
,output
)
132 (setf (gethash ,name
*external-formats
*)
133 (make-external-format ,name
,octets-to-char
,char-to-octets
))))
135 ;;; TODO: make this work
136 (defun load-external-format-aliases ()
137 (let ((*package
* (find-package "KEYWORD")))
138 (with-open-file (stm "ef:aliases" :if-does-not-exist nil
)
140 (do ((alias (read stm nil stm
) (read stm nil stm
))
141 (value (read stm nil stm
) (read stm nil stm
)))
142 ((or (eq alias stm
) (eq value stm
))
143 (unless (eq alias stm
)
144 (warn "External-format aliases file ends early.")))
145 (if (and (keywordp alias
) (keywordp value
))
146 (setf (gethash alias
*external-format-aliases
*) value
)
147 (warn "Bad entry in external-format aliases file: ~S => ~S."
150 (defun find-external-format (name &optional
(error-p t
))
151 (when (external-format-p name
)
152 (return-from find-external-format name
))
154 (when (eq name
:default
)
155 (setq name
*default-external-format
*))
157 ;; TODO: make this work
159 (unless (ext:search-list-defined-p
"ef:")
160 (setf (ext:search-list
"ef:") '("library:ef/")))
162 (when (zerop (hash-table-count *external-format-aliases
*))
163 (setf (gethash :latin1
*external-format-aliases
*) :iso8859-1
)
164 (setf (gethash :latin-1
*external-format-aliases
*) :iso8859-1
)
165 (setf (gethash :iso-8859-1
*external-format-aliases
*) :iso8859-1
)
166 (load-external-format-aliases))
168 (do ((tmp (gethash name
*external-format-aliases
*)
169 (gethash tmp
*external-format-aliases
*))
171 ((or (null tmp
) (= cnt
50))
173 (error "External-format aliasing depth exceeded.")))
176 (or (gethash name
*external-formats
*)
177 (and (let ((*package
* (find-package "SB-SIMPLE-STREAMS")))
178 (load (format nil
"ef:~(~A~)" name
) :if-does-not-exist nil
))
179 (gethash name
*external-formats
*))
180 (if error-p
(error "External format ~S not found." name
) nil
)))
182 (define-condition void-external-format
(error)
185 (lambda (condition stream
)
186 (declare (ignore condition
))
187 (format stream
"Attempting I/O through void external-format."))))
189 (define-external-format :void
190 (octets-to-char (state input unput
)
191 (declare (ignore state input unput
))
192 (error 'void-external-format
))
193 (char-to-octets (char state output
)
194 (declare (ignore char state output
))
195 (error 'void-external-format
)))
197 (define-external-format :iso8859-1
198 (octets-to-char (state input unput
)
199 (declare (optimize (speed 3) (space 0) (safety 0) (debug 0)))
200 (values (code-char (funcall input
)) 1 state
))
201 (char-to-octets (char state output
)
202 (declare (optimize (speed 3) (space 0) (safety 0) (debug 0)))
203 (let ((code (char-code char
)))
205 (funcall output code
)
208 (funcall output code
)
209 (funcall output
(char-code #\?))))
212 (defmacro octets-to-char
(external-format state count input unput
)
213 (let ((tmp1 (gensym)) (tmp2 (gensym)) (tmp3 (gensym)))
214 `(multiple-value-bind (,tmp1
,tmp2
,tmp3
)
215 (funcall (ef-octets-to-char ,external-format
) ,state
,input
,unput
)
216 (setf ,state
,tmp3
,count
,tmp2
)
219 (defmacro char-to-octets
(external-format char state output
)
221 (setf ,state
(funcall (ef-char-to-octets ,external-format
)
222 ,char
,state
,output
))
225 (defun string-to-octets (string &key
(start 0) end
(external-format :default
))
226 (declare (type string string
)
227 (type sb-int
:index start
)
228 (type (or null sb-int
:index
) end
))
229 (let ((ef (find-external-format external-format
))
230 (buffer (make-array (length string
) :element-type
'(unsigned-byte 8)))
234 (setf (aref buffer ptr
) b
)
235 (when (= (incf ptr
) (length buffer
))
236 (setq buffer
(adjust-array buffer
(* 2 ptr
))))))
237 (dotimes (i (- (or end
(length string
)) start
))
238 (declare (type sb-int
:index i
))
239 (char-to-octets ef
(char string
(+ start i
)) state
#'out
))
240 (sb-kernel:shrink-vector buffer ptr
))))
242 (defun octets-to-string (octets &key
(start 0) end
(external-format :default
))
243 (declare (type vector octets
)
244 (type sb-int
:index start
)
245 (type (or null sb-int
:index
) end
))
246 (let ((ef (find-external-format external-format
))
247 (end (1- (or end
(length octets
))))
248 (string (make-string (length octets
)))
254 (aref octets
(incf ptr
)))
257 (loop until
(>= ptr end
)
258 do
(setf (schar string
(incf pos
))
259 (octets-to-char ef state count
#'input
#'unput
))))
260 (sb-kernel:shrink-vector string
(1+ pos
))))
262 (defun vector-elt-width (vector)
263 ;; Return octet-width of vector elements
265 ;; (simple-array fixnum (*)) not supported
266 ;; (simple-array base-char (*)) treated specially; don't call this
267 ((simple-array bit
(*)) 1)
268 ((simple-array (unsigned-byte 2) (*)) 1)
269 ((simple-array (unsigned-byte 4) (*)) 1)
270 ((simple-array (signed-byte 8) (*)) 1)
271 ((simple-array (unsigned-byte 8) (*)) 1)
272 ((simple-array (signed-byte 16) (*)) 2)
273 ((simple-array (unsigned-byte 16) (*)) 2)
274 ((simple-array (signed-byte 32) (*)) 4)
275 ((simple-array (unsigned-byte 32) (*)) 4)
276 ((simple-array single-float
(*)) 4)
277 ((simple-array double-float
(*)) 8)
278 ((simple-array (complex single-float
) (*)) 8)
279 ((simple-array (complex double-float
) (*)) 16)))
281 #-
(or big-endian little-endian
)
282 (eval-when (:compile-toplevel
)
283 (push sb-c
::*backend-byte-order
* *features
*))
285 (defun endian-swap-value (vector endian-swap
)
286 #+big-endian
(declare (ignore vector
))
288 (:network-order
#+big-endian
0
289 #+little-endian
(1- (vector-elt-width vector
)))
295 (otherwise endian-swap
)))
298 (defun %read-vector
(vector stream start end endian-swap blocking
)
299 (declare (type (kernel:simple-unboxed-array
(*)) vector
)
300 (type stream stream
))
301 ;; move code from read-vector
305 (defun %write-vector
(... blocking
)
309 (defun read-octets (stream buffer start end blocking
)
310 (declare (type simple-stream stream
)
311 (type (or null simple-stream-buffer
) buffer
)
313 (type (or null fixnum
) end
)
314 (type blocking blocking
)
315 (optimize (speed 3) (space 2) (safety 0) (debug 0)))
316 (with-stream-class (simple-stream stream
)
317 (let ((fd (sm input-handle stream
))
318 (end (or end
(sm buf-len stream
)))
319 (buffer (or buffer
(sm buffer stream
))))
320 (declare (fixnum end
))
323 (let ((flag (sb-sys:wait-until-fd-usable fd
:input
324 (if blocking nil
0))))
326 ((and (not blocking
) (= start end
)) (if flag -
3 0))
327 ((and (not blocking
) (not flag
)) 0)
330 (declare (type fixnum count
))
333 ;; Avoid CMUCL gengc write barrier
334 (do ((i start
(+ i
#.
(sb-posix:getpagesize
))))
336 (declare (type fixnum i
))
337 (setf (bref buffer i
) 0))
338 (setf (bref buffer
(1- end
)) 0)
339 (multiple-value-bind (bytes errno
)
340 (sb-sys:with-pinned-objects
(buffer)
341 (sb-unix:unix-read fd
(buffer-sap buffer start
)
342 (the fixnum
(- end start
))))
343 (declare (type (or null fixnum
) bytes
)
344 (type (integer 0 100) errno
))
349 (format *debug-io
* "~&;; UNIX-READ: errno=~D~%" errno
)
350 (cond ((= errno sb-unix
:eintr
) (go again
))
352 (or (= errno
;;sb-unix:eagain
362 (sb-sys:wait-until-fd-usable fd
:input nil
)
364 (t (return (- -
10 errno
)))))
365 ((zerop count
) (return -
1))
366 (t (return count
)))))))))))
367 (t (%read-vector buffer fd start end
:byte-8
368 (if blocking
:bnb nil
)))))))
370 (defun write-octets (stream buffer start end blocking
)
371 (declare (type simple-stream stream
)
372 (type simple-stream-buffer buffer
)
374 (type (or null fixnum
) end
))
375 (with-stream-class (simple-stream stream
)
376 (when (sm handler stream
)
378 ((null (sm pending stream
)))
379 (sb-sys:serve-all-events
)))
381 (let ((fd (sm output-handle stream
))
382 (end (or end
(length buffer
))))
385 (let ((flag (sb-sys:wait-until-fd-usable fd
:output
386 (if blocking nil
0))))
388 ((and (not blocking
) (= start end
)) (if flag -
3 0))
389 ((and (not blocking
) (not flag
)) 0)
394 (multiple-value-bind (bytes errno
)
395 (sb-sys:with-pinned-objects
(buffer)
396 (sb-unix:unix-write fd
(buffer-sap buffer
) start
402 (format *debug-io
* "~&;; UNIX-WRITE: errno=~D~%" errno
)
403 (cond ((= errno sb-unix
:eintr
) (go again
))
404 ;; don't block for subsequent chars
405 (t (return (- -
10 errno
)))))
406 (t (return count
)))))))))))
407 (t (error "implement me"))))))
409 (defun do-some-output (stream)
410 ;; Do some pending output; return T if completed, NIL if more to do
411 (with-stream-class (simple-stream stream
)
412 (let ((fd (sm output-handle stream
)))
414 (let ((list (pop (sm pending stream
))))
416 (sb-sys:remove-fd-handler
(sm handler stream
))
417 (setf (sm handler stream
) nil
)
419 (let* ((buffer (first list
))
420 (start (second list
))
423 (declare (type simple-stream-buffer buffer
)
424 (type sb-int
:index start end len
))
426 (multiple-value-bind (bytes errno
)
427 (sb-sys:with-pinned-objects
(buffer)
428 (sb-unix:unix-write fd
(buffer-sap buffer
) start len
))
430 (if (= errno sb-unix
:eintr
)
432 (progn (push list
(sm pending stream
))
435 (setf (second list
) (+ start bytes
))
436 (push list
(sm pending stream
))
439 (free-buffer buffer
)))))))))))
441 (defun queue-write (stream buffer start end
)
442 ;; Queue a write; return T if buffer needs changing, NIL otherwise
443 (declare (type simple-stream stream
)
444 (type simple-stream-buffer buffer
)
445 (type sb-int
:index start end
))
446 (with-stream-class (simple-stream stream
)
447 (when (sm handler stream
)
448 (unless (do-some-output stream
)
449 (let ((last (last (sm pending stream
))))
450 (setf (cdr last
) (list (list buffer start end
)))
451 (return-from queue-write t
))))
452 (let ((bytes (write-octets stream buffer start end nil
)))
453 (unless (or (= bytes
(- end start
)) ; completed
454 (= bytes -
3)) ; empty buffer; shouldn't happen
455 (setf (sm pending stream
) (list (list buffer start end
)))
456 (setf (sm handler stream
)
457 (sb-sys:add-fd-handler
(sm output-handle stream
) :output
459 (declare (ignore fd
))
460 (do-some-output stream
))))
466 (defun %fd-open
(pathname direction if-exists if-exists-given
467 if-does-not-exist if-does-not-exist-given
)
468 (declare (type pathname pathname
)
469 (type (member :input
:output
:io
:probe
) direction
)
470 (type (member :error
:new-version
:rename
:rename-and-delete
471 :overwrite
:append
:supersede nil
) if-exists
)
472 (type (member :error
:create nil
) if-does-not-exist
))
473 (multiple-value-bind (input output mask
)
475 (:input
(values t nil sb-unix
:o_rdonly
))
476 (:output
(values nil t sb-unix
:o_wronly
))
477 (:io
(values t t sb-unix
:o_rdwr
))
478 (:probe
(values t nil sb-unix
:o_rdonly
)))
479 (declare (type sb-int
:index mask
))
480 (let* ((phys (sb-int:physicalize-pathname
(merge-pathnames pathname
)))
481 (true (probe-file phys
))
483 (sb-ext:native-namestring true
:as-file t
))
485 (and input
(eq if-does-not-exist
:create
))
486 (and (eq direction
:io
) (not if-does-not-exist-given
)))
487 (sb-ext:native-namestring phys
:as-file t
)))))
488 ;; Process if-exists argument if we are doing any output.
490 (unless if-exists-given
492 (if (eq (pathname-version pathname
) :newest
)
496 ((:error nil
:new-version
)
497 (setf mask
(logior mask sb-unix
:o_excl
)))
498 ((:rename
:rename-and-delete
)
499 (setf mask
(logior mask sb-unix
:o_creat
)))
501 (setf mask
(logior mask sb-unix
:o_trunc
)))))
503 (setf if-exists nil
))) ; :ignore-this-arg
504 (unless if-does-not-exist-given
505 (setf if-does-not-exist
506 (cond ((eq direction
:input
) :error
)
508 (member if-exists
'(:overwrite
:append
)))
510 ((eq direction
:probe
)
514 (if (eq if-does-not-exist
:create
)
515 (setf mask
(logior mask sb-unix
:o_creat
)))
517 (let ((original (if (member if-exists
518 '(:rename
:rename-and-delete
))
519 (sb-impl::pick-backup-name name
)
521 (delete-original (eq if-exists
:rename-and-delete
))
524 ;; We are doing a :rename or :rename-and-delete.
525 ;; Determine if the file already exists, make sure the original
526 ;; file is not a directory and keep the mode
530 (okay err
/dev inode orig-mode
)
531 (sb-unix:unix-stat name
)
532 (declare (ignore inode
)
533 (type (or sb-int
:index null
) orig-mode
))
536 (when (and output
(= (logand orig-mode
#o170000
)
538 (error 'sb-int
:simple-file-error
541 "Cannot open ~S for output: Is a directory."
542 :format-arguments
(list name
)))
543 (setf mode
(logand orig-mode
#o777
))
545 ((eql err
/dev sb-unix
:enoent
)
548 (error 'sb-int
:simple-file-error
550 :format-control
"Cannot find ~S: ~A"
553 (sb-int:strerror err
/dev
)))))))))
555 (rename-file name original
))
557 (setf delete-original nil
)
558 ;; In order to use SUPERSEDE instead, we have
559 ;; to make sure unix:o_creat corresponds to
560 ;; if-does-not-exist. unix:o_creat was set
561 ;; before because of if-exists being :rename.
562 (unless (eq if-does-not-exist
:create
)
563 (setf mask
(logior (logandc2 mask sb-unix
:o_creat
)
565 (setf if-exists
:supersede
))))
567 ;; Okay, now we can try the actual open.
569 (multiple-value-bind (fd errno
)
571 (sb-unix:unix-open name mask mode
)
572 (values nil
#-win32 sb-unix
:enoent
573 #+win32 sb-win32
::error_file_not_found
))
575 (when (eql if-exists
:append
)
576 (sb-unix:unix-lseek fd
0 sb-unix
:l_xtnd
))
577 (return (values fd name original delete-original
)))
578 ((eql errno
#-win32 sb-unix
:enoent
579 #+win32 sb-win32
::error_file_not_found
)
580 (case if-does-not-exist
582 (cerror "Return NIL."
583 'sb-int
:simple-file-error
585 :format-control
"Error opening ~S, ~A."
588 (sb-int:strerror errno
))))
590 (cerror "Return NIL."
591 'sb-int
:simple-file-error
594 "Error creating ~S, path does not exist."
595 :format-arguments
(list pathname
))))
597 ((eql errno
#-win32 sb-unix
:eexist
598 #+win32 sb-win32
::error_file_not_found
)
599 (unless (eq nil if-exists
)
600 (cerror "Return NIL."
601 'sb-int
:simple-file-error
603 :format-control
"Error opening ~S, ~A."
606 (sb-int:strerror errno
))))
608 #+nil
; FIXME: reinstate this; error reporting is nice.
609 ((eql errno sb-unix
:eacces
)
611 'sb-int
:simple-file-error
613 :format-control
"Error opening ~S, ~A."
616 (sb-int:strerror errno
))))
618 (cerror "Return NIL."
619 'sb-int
:simple-file-error
621 :format-control
"Error opening ~S, ~A."
624 (sb-int:strerror errno
)))
627 (defun open-fd-stream (pathname &key
(class 'sb-sys
:fd-stream
)
629 (element-type 'base-char
)
630 (if-exists nil if-exists-given
)
631 (if-does-not-exist nil if-does-not-exist-given
)
632 (external-format :default
))
633 (declare (type (or pathname string stream
) pathname
)
634 (type (member :input
:output
:io
:probe
) direction
)
635 (type (member :error
:new-version
:rename
:rename-and-delete
636 :overwrite
:append
:supersede nil
) if-exists
)
637 (type (member :error
:create nil
) if-does-not-exist
))
638 (let ((filespec (merge-pathnames pathname
)))
639 (multiple-value-bind (fd namestring original delete-original
)
640 (%fd-open filespec direction if-exists if-exists-given
641 if-does-not-exist if-does-not-exist-given
)
644 ((:input
:output
:io
)
645 (sb-sys:make-fd-stream fd
647 :input
(member direction
'(:input
:io
))
648 :output
(member direction
'(:output
:io
))
649 :element-type element-type
652 :delete-original delete-original
657 :external-format external-format
))
659 (let ((stream (sb-impl::%make-fd-stream
:name namestring
:fd fd
661 :element-type element-type
)))
666 ;; Experimental "filespec" stuff
668 ;; sat: Hooks to parse URIs etc apparently go here
670 (defstruct (filespec-parser
671 (:constructor make-filespec-parser
(name priority function
)))
676 (defvar *filespec-parsers
* ())
678 (defun add-filespec (name priority function
)
679 (let ((filespec (make-filespec-parser name priority function
)))
680 (setf *filespec-parsers
*
681 (stable-sort (cons filespec
(delete name
*filespec-parsers
*
682 :key
#'filespec-parser-name
))
684 :key
#'filespec-parser-priority
)))
687 (defmacro define-filespec
(name lambda-list
&body body
)
688 (let ((truename (if (consp name
) (first name
) name
))
689 (priority (if (consp name
) (second name
) 0)))
690 `(add-filespec ',truename
,priority
(lambda ,lambda-list
694 (defun parse-filespec (string &optional
(errorp t
))
695 (dolist (i *filespec-parsers
* (when errorp
696 (error "~S not recognised." string
)))
697 (let ((result (ignore-errors
698 (funcall (filespec-parser-function i
) string
))))
699 (when result
(return result
)))))
701 (define-filespec pathname
(string)