Skip to content
Snippets Groups Projects
fd-stream.lisp 75.3 KiB
Newer Older
ram's avatar
ram committed
;;; -*- Log: code.log; Package: LISP -*-
ram's avatar
ram committed
;;; **********************************************************************
;;; This code was written as part of the CMU Common Lisp project at
;;; Carnegie Mellon University, and has been placed in the public domain.
;;;
(ext:file-comment
rtoy's avatar
rtoy committed
  "$Header: /Volumes/share2/src/cmucl/cvs2git/cvsroot/src/code/fd-stream.lisp,v 1.88 2009/06/25 01:48:59 rtoy Exp $")
ram's avatar
ram committed
;;; **********************************************************************
;;;
;;; Streams for UNIX file descriptors.
;;;
;;; Written by William Lott, July 1989 - January 1990.
;;; Some tuning by Rob MacLachlan.
ram's avatar
ram committed
;;; 
;;; **********************************************************************


(in-package "SYSTEM")

(export '(fd-stream fd-stream-p fd-stream-fd make-fd-stream
          io-timeout beep *beep-function* output-raw-bytes
	  *tty* *stdin* *stdout* *stderr*
	  binary-text-stream))
ram's avatar
ram committed


(in-package "EXTENSIONS")

(export '(*backup-extension*))


(in-package "LISP")

(export '(file-stream file-string-length))
ram's avatar
ram committed

;;;; Buffer manipulation routines.

(defvar *available-buffers* ()
  "List of available buffers.  Each buffer is an sap pointing to
  bytes-per-buffer of memory.")
ram's avatar
ram committed

(defconstant bytes-per-buffer (* 4 1024)
  "Number of bytes per buffer.")

;; This limit is rather arbitrary
(defconstant max-stream-element-size 1024
  "The maximum supported byte size for a stream element-type.")

ram's avatar
ram committed
;;; NEXT-AVAILABLE-BUFFER -- Internal.
;;;
;;; Returns the next available buffer, creating one if necessary.
ram's avatar
ram committed
;;;
pw's avatar
pw committed
(declaim (inline next-available-buffer))
ram's avatar
ram committed
;;;
(defun next-available-buffer ()
  (if *available-buffers*
      (pop *available-buffers*)
      (allocate-system-memory bytes-per-buffer)))
ram's avatar
ram committed

(declaim (inline buffer-sap bref (setf bref) buffer-copy))

(defun buffer-sap (thing &optional offset)
  (declare (type simple-stream-buffer thing) (type (or fixnum null) offset)
           (optimize (speed 3) (space 2) (debug 0) (safety 0)
                     ;; Suppress the note about having to box up the return:
                     (ext:inhibit-warnings 3)))
  (let ((sap (if (vectorp thing) (sys:vector-sap thing) thing)))
    (if offset (sys:sap+ sap offset) sap)))

(defun bref (buffer index)
  (declare (type simple-stream-buffer buffer)
           (type (integer 0 #.most-positive-fixnum) index))
  (sys:sap-ref-8 (buffer-sap buffer) index))

(defun (setf bref) (octet buffer index)
  (declare (type (unsigned-byte 8) octet)
           (type simple-stream-buffer buffer)
           (type (integer 0 #.most-positive-fixnum) index))
  (setf (sys:sap-ref-8 (buffer-sap buffer) index) octet))

(defun buffer-copy (src soff dst doff length)
  (declare (type simple-stream-buffer src dst)
           (type fixnum soff doff length))
  (sys:without-gcing ;; is this necessary??
   (kernel:system-area-copy (buffer-sap src) (* soff 8)
                            (buffer-sap dst) (* doff 8)
                            (* length 8))))

#-(or big-endian little-endian)
(eval-when (:compile-toplevel)
  (push (c::backend-byte-order c::*target-backend*) *features*))

(defun vector-elt-width (vector)
  ;; Return octet-width of vector elements
  (etypecase vector
    ;; (simple-array fixnum (*)) not supported
    ;; (simple-array base-char (*)) treated specially; don't call this
    ((simple-array bit (*)) 1/8)
    ((simple-array (unsigned-byte 2) (*)) 1/4)
    ((simple-array (unsigned-byte 4) (*)) 1/2)
    ((simple-array (signed-byte 8) (*)) 1)
    ((simple-array (unsigned-byte 8) (*)) 1)
    ((simple-array (signed-byte 16) (*)) 2)
    ((simple-array (unsigned-byte 16) (*)) 2)
    ((simple-array (signed-byte 32) (*)) 4)
    ((simple-array (unsigned-byte 32) (*)) 4)
    ((simple-array single-float (*)) 4)
    ((simple-array double-float (*)) 8)
    ((simple-array (complex single-float) (*)) 8)
    ((simple-array (complex double-float) (*)) 16)
    #+long-float
    ((simple-array long-float (*)) 10)
    #+long-float
    ((simple-array (complex long-float) (*)) 20)
    #+double-double
    ((simple-array double-double-float (*)) 16)
    #+double-double
    ((simple-array (complex double-double-float) (*)) 32)))

(defun endian-swap-value (vector endian-swap)
  (case endian-swap
    (:network-order
     #+big-endian 0
     ;; This is needed because the little-endian (x86) architectures
     ;; store the lowest indexed element in the least significant part
     ;; of a byte.  On a big-endian machine (sparc, ppc), the lowest
     ;; indexed element is at the most significant part of a byte.
     #+little-endian
     (typecase vector
       ((array (unsigned-byte 4) (*))
	-1)
       ((array (unsigned-byte 2) (*))
	-2)
       ((array (unsigned-byte 1) (*))
	-8)
       (t
	(1- (vector-elt-width vector)))))
    (:byte-8 0)
    (:byte-16 1)
    (:byte-32 3)
    (:byte-64 7)
    (:byte-128 15)
    ;; additions by Lynn Quam
    (:machine-endian 0)
    (:big-endian
     #+big-endian 0
     #+little-endian
     (typecase vector
       ((array (unsigned-byte 4) (*))
	-1)
       ((array (unsigned-byte 2) (*))
	-2)
       ((array (unsigned-byte 1) (*))
	-8)
       (t
	(1- (vector-elt-width vector)))))
    (:little-endian
     #+big-endian
     (typecase vector
       ((array (unsigned-byte 4) (*))
	-1)
       ((array (unsigned-byte 2) (*))
	-2)
       ((array (unsigned-byte 1) (*))
	-8)
       (t
	(1- (vector-elt-width vector))))
     #+little-endian 0)
ram's avatar
ram committed

;;;; The FD-STREAM structure.

moore's avatar
 
moore committed
;;;; Superclass defined by the ANSI Spec
(defstruct (file-stream
	     (:include lisp-stream)
	     (:constructor nil)
	     (:copier nil)))

ram's avatar
ram committed
(defstruct (fd-stream
	    (:print-function %print-fd-stream)
	    (:constructor %make-fd-stream)
moore's avatar
 
moore committed
	    (:include file-stream
ram's avatar
ram committed
		      (misc #'fd-stream-misc-routine)))

  (name nil)		      ; The name of this stream
  (file nil)		      ; The file this stream is for
  ;;
  ;; The backup file namestring for the old file, for :if-exists :rename or
  ;; :rename-and-delete.
  (original nil :type (or simple-string null))
ram's avatar
ram committed
  (delete-original nil)	      ; for :if-exists :rename-and-delete
  ;;
  ;;; Number of bytes per element.
  (element-size 1 :type index)
  (element-type 'base-char)   ; The type of element being transfered.
ram's avatar
ram committed
  (fd -1 :type fixnum)	      ; The file descriptor
  ;; Controls when the output buffer is flushed.
  (buffering :full :type (member :full :line :none))
  ;;
  ;; Character position if known.
  (char-pos nil :type (or index null))
  ;;
  ;; T if input is waiting on FD.  :EOF if we hit EOF.
  (listen nil :type (member nil t :eof))
  ;;
ram's avatar
ram committed
  ;; The input buffer.
  (unread nil)
  (ibuf-sap nil :type (or system-area-pointer null))
  (ibuf-length nil :type (or index null))
  (ibuf-head 0 :type index)
  (ibuf-tail 0 :type index)
ram's avatar
ram committed

  ;; The output buffer.
  (obuf-sap nil :type (or system-area-pointer null))
  (obuf-length nil :type (or index null))
  (obuf-tail 0 :type index)
ram's avatar
ram committed

  ;; Output flushed, but not written due to non-blocking io.
  (output-later nil)
  (handler nil)
  ;;
  ;; Timeout specified for this stream, or NIL if none.
  (timeout nil :type (or index null))
  ;;
  ;; Pathname of the file this stream is opened to (returned by PATHNAME.)
  (pathname nil :type (or pathname null))
  ;;
  ;; External format support
  ;;
  ;; @@ I want to use :default here, but keyword pkg isn't set up yet at boot
  ;; so initialize to NIL and fix it in SET-ROUTINES
  #+unicode
  (external-format nil :type (or null keyword cons))
  #+unicode
  (oc-state nil)
  #+unicode
  (co-state nil)
  #+unicode
  (last-char-read-size 0 :type index))
ram's avatar
ram committed

(defun %print-fd-stream (fd-stream stream depth)
  (declare (ignore depth) (stream stream))
ram's avatar
ram committed
  (format stream "#<Stream for ~A>"
	  (fd-stream-name fd-stream)))

;; CMUCL extension.  This is a FD-STREAM, but it allows reading and
;; writing of 8-bit characters and unsigned bytes from the stream.
(defstruct (binary-text-stream
	     (:print-function %print-binary-text-stream)
	     (:constructor %make-binary-text-stream)
	     (:include fd-stream)))

(defun %print-binary-text-stream (fd-stream stream depth)
  (declare (ignore depth) (stream stream))
  (format stream "#<Binary-text Stream for ~A>"
	  (fd-stream-name fd-stream)))
ram's avatar
ram committed

(define-condition io-timeout (stream-error)
  ((direction :reader io-timeout-direction :initarg :direction))
     (declare (stream stream))
     (format stream "Timeout ~(~A~)ing ~S."
	     (io-timeout-direction condition)
	     (stream-error-stream condition)))))

ram's avatar
ram committed

;;;; Output routines and related noise.

(defvar *output-routines* ()
  "List of all available output routines. Each element is a list of the
  element-type output, the kind of buffering, the function name, and the number
  of bytes per element.")

;;; DO-OUTPUT-LATER -- internal
;;;
;;;   Called by the server when we can write to the given file descriptor.
;;; Attempt to write the data again. If it worked, remove the data from the
ram's avatar
ram committed
;;; output-later list. If it didn't work, something is wrong.
;;;
(defun do-output-later (stream)
  (let* ((stuff (pop (fd-stream-output-later stream)))
	 (base (car stuff))
	 (start (cadr stuff))
	 (end (caddr stuff))
	 (reuse-sap (cadddr stuff))
ram's avatar
ram committed
	 (length (- end start)))
    (declare (type index start end length))
ram's avatar
ram committed
    (multiple-value-bind
	(count errno)
	(unix:unix-write (fd-stream-fd stream)
ram's avatar
ram committed
			 base
			 start
			 length)
      (cond ((not count)
	     (if (= errno unix:ewouldblock)
		 (error "Write would have blocked, but SERVER told us to go.")
		 (error "While writing ~S: ~A"
			stream (unix:get-unix-error-msg errno))))
	    ((eql count length) ; Hot damn, it worked.
	     (when reuse-sap
	       (push base *available-buffers*)))
ram's avatar
ram committed
	    ((not (null count)) ; Sorta worked.
	     (push (list base
			 (the index (+ start count))
		   (fd-stream-output-later stream))))))
ram's avatar
ram committed
  (unless (fd-stream-output-later stream)
    (system:remove-fd-handler (fd-stream-handler stream))
    (setf (fd-stream-handler stream) nil)))

;;; OUTPUT-LATER -- internal
;;;
;;;   Arrange to output the string when we can write on the file descriptor.
ram's avatar
ram committed
;;;
(defun output-later (stream base start end reuse-sap)
ram's avatar
ram committed
  (cond ((null (fd-stream-output-later stream))
	 (setf (fd-stream-output-later stream)
	       (list (list base start end reuse-sap)))
ram's avatar
ram committed
	 (setf (fd-stream-handler stream)
	       (system:add-fd-handler (fd-stream-fd stream)
				      :output
				      #'(lambda (fd)
					  (declare (ignore fd))
					  (do-output-later stream)))))
	(t
	 (nconc (fd-stream-output-later stream)
		(list (list base start end reuse-sap)))))
  (when reuse-sap
ram's avatar
ram committed
    (let ((new-buffer (next-available-buffer)))
      (setf (fd-stream-obuf-sap stream) new-buffer)
      (setf (fd-stream-obuf-length stream) bytes-per-buffer)))) 
ram's avatar
ram committed

;;; DO-OUTPUT -- internal
;;;
Loading
Loading full blame...