;;;; Dribble (in-package "EXT") (export '(make-dribble-stream dribble-stream-p dribble-stream dribble-stream-source dribble-stream-target dribble-toggle)) (in-package "SYSTEM") (defun make-dribble-stream (source target) (make-two-way-stream (make-echo-stream source target) (make-broadcast-stream source target))) (defun dribble-stream (stream) (and (sys::two-way-stream-p stream) (let ((in (two-way-stream-input-stream stream)) (out (two-way-stream-output-stream stream))) (and (sys::echo-stream-p in) (sys::broadcast-stream-p out) (let ((source (echo-stream-input-stream in)) (target (echo-stream-output-stream in)) (broadcast-list (broadcast-stream-streams out))) (when (and (eq source (pop broadcast-list)) (eq target (pop broadcast-list))) (values source target))))))) (defun dribble-stream-p (obj) (not (null (dribble-stream obj)))) ;; should this be integrated into CLOS and the rest of CLISP? ;; right now DRIBBLE-STREAM is not a recognizable subtype of TWO-WAY-STREAM. ;; should it be? should it be printed specially? (deftype dribble-stream () '(satisfies dribble-stream-p)) (defun check-dribble-stream (obj caller) (loop (multiple-value-bind (so ta) (dribble-stream obj) (when so (return-from check-dribble-stream (values so ta)))) (setq obj (check-value nil (make-condition 'simple-type-error :format-control (TEXT "~S: ~S should be a ~S") :format-arguments (list caller obj 'dribble-stream) :datum obj :expected-type 'dribble-stream))))) (defun dribble-stream-source (ds) (check-dribble-stream ds 'dribble-stream-source)) (defun dribble-stream-target (ds) (nth-value 1 (check-dribble-stream ds 'dribble-stream-target))) (defun dribble-toggle (stream &optional file) (multiple-value-bind (source target) (dribble-stream stream) (if source (if file ; already dribbling (warn (TEXT "Already dribbling ~S to ~S") source target) (progn (format target (TEXT "~&;; Dribble of ~S finished on ~A.~%") source (date-string)) (values source target))) (if file ; not dribbling (let ((target (if (and (streamp file) (open-stream-p file) (output-stream-p file)) file (open file :direction :output :external-format (stream-external-format stream) :if-exists :append :if-does-not-exist :create)))) (format target (TEXT ";; Dribble of ~S started on ~A.~%") stream (date-string)) (values (make-dribble-stream stream target) target)) (warn (TEXT "Currently not dribbling from ~S.") stream))))) (defun dribble (&optional file) (multiple-value-bind (source target) (dribble-toggle *terminal-io* file) (when (streamp source) ; no warning (unless file (close target)) ; dribble off (setq *terminal-io* source)) target))