;;; -*- Mode:LISP; Package:FILE-SYSTEM; Base:10 -*- ;;; ;;; Extremely raw quarter inch tape drive support ;;; ;;; -dg 5/16/85 ;;; (defmacro maybe-with-real-time (condition &body body) `(let ((old-sb-state (si:sb-on))) (unwind-protect (progn (when ,condition (si:sb-on '(:keyboard))) . ,body) (si:sb-on old-sb-state)))) (defconst *use-real-time* t) (defun raw-quart-read-array (ary &optional (n-tape-blocks (ceiling (array-length ary) 512.))) (assure-quarter-inch-tape-still-allocated) (cond (quart-file-mark-detected-on-last-read (setq quart-file-mark-detected-on-last-read nil) 0) (t ; (when (or (not (quart-ready-p)) ; (not (quart-online-p))) ; (ferror 'drive-offline "The quarter inch tape drive is not Online.")) (when (not (= *last-quart-cmd* quart-cmd-read)) (quart-send-cmd quart-cmd-read nil)) (let ((blocks-transfered (with-nubus-timeouts-disabled (maybe-with-real-time *use-real-time* (si:page-in-array ary) (cond (quart-use-ucode (compiler:%quart-transfer 0 ary n-tape-blocks)) (t (do ((block-number 0 (1+ block-number)) (block-adr 0 (+ block-adr 512.))) ((progn (quart-wait-for-ready t nil) (or (quart-exception) (>= block-number n-tape-blocks))) block-number) (dotimes (i 512.) (aset (read-quart-data) ary (+ block-adr i))))))))) bytes-transfered) (cond ((cond ((eq (handle-quart-exception t) 'file-mark-detected) (cond ((not (zerop blocks-transfered)) (setq quart-file-mark-detected-on-last-read t) 0))) (t (handle-quart-exception)))) (t (setq quart-file-mark-detected-on-last-read nil))) (or bytes-transfered (* 512. blocks-transfered)))))) (defun decode-block-mark (thing) (if (not (string-equal "BLOK" thing :end2 4)) (ferror nil "Bad block marker - ~a" thing) (do ((num 0) (word (string thing)) (count 15 (sub1 count))) ((= count 3) num) (setq num (+ num (* (- (aref word count) 48) (^ 10. (- 15 count)))))))) (defun read-block-mark (instream) (let* ((string (make-string 16)) (mark (progn (send instream :string-in t string 0 4) (if (string-equal string "EOF " :end1 4) (progn (quart-read-file-mark) (ferror 'fs:end-of-file :format-string "End of file detected on carry tape.")) (send instream :string-in t string 4) string)))) (if (eq mark :eof) :eof (decode-block-mark mark)))) (defsubst keyword-read (stream) (intern (read stream) pkg-keyword-package)) (defmacro with-open-carry-tape ((var . options) &body body) `(let ((,var (lexpr-funcall #'make-instance 'carry-tape-input-stream ,options))) (unwind-protect (progn ,@body) (close ,var)))) (defun read-carry-file-plist (instream) (do ((plist (list "file plist")) (prop (keyword-read instream) (keyword-read instream))) ((eq prop :end) plist) (If (eq prop :carry-tape) (read-carry-tape-header instream) (putprop plist (case prop ((:record-type :creation-date :author :type :raw-type :name :raw-name :pathname :directory :host :dump-group) (readline instream)) ((:version :characters :system-type :canonical-type) (read instream))) prop)))) (defun read-carry-tape-header (stream) (do ((plist (list :CARRY-TAPE)) (prop (keyword-read stream) (keyword-read stream))) ((eq prop :end) (when (get plist :dump-list-follows) (putprop plist (if (and (string-equal (readline stream) "RECORD-TYPE DUMP-LIST") (string-equal (readline stream) "END")) (do ((dump-list) (line (readline stream) (readline stream))) ((zerop (length line)) dump-list) (setq dump-list (append dump-list (list line)))) (format t "~&Error in header: Bad dump list.~%")) :dump-list)) (send stream :next-file) plist) (putprop plist (case prop ((:version :tape-system-version :dump-list-follows) ;symbols or numbers (read stream)) ((:time :machine :user-id :tape-host :tape-drive) ;strings (readline stream)) (t (ferror nil "unhandled prop - ~A" prop))) prop))) ;------------------------------ ;;; raw-carry-tape-stream (defflavor carry-tape-input-mixin () (qt-mixin si:buffered-input-stream) (:default-init-plist :chunk-size 1024.)) (defmethod (carry-tape-input-mixin :next-input-buffer) (&rest ignore) (cond ((eq status :eof) (ferror 'fs:file-mark-detected "End of file on carry tape detected.")) ((eq status ':closed) (ferror 'ferror :format-string "Attempt to get input from ~s, which is closed." :format-args self))) (if (null array) (setq array (allocate-resource 'quart-array chunk-size))) (let ((bytes-transfered (raw-quart-read-array array))) (when (< bytes-transfered chunk-size) (setq status :eof)) (cond ((= byte-size 8) (values (quart-array-string array) 0 bytes-transfered)) ((= byte-size 16.) (values (quart-array-art-16b array) 0 (ceiling bytes-transfered 2))) (t (ferror 'quart-error :format-args "illegal byte-size"))))) (defmethod (carry-tape-input-mixin :next-file) () (cond ((eq status ':eof) (setq status ':open)) ((not (eq status ':open)) (ferror 'quart-error :format-string "bad state in :next-file")) (t (cond ((null quart-file-mark-detected-on-last-read) (quart-read-file-mark))) (setq quart-file-mark-detected-on-last-read nil) (send self ':clear-input) (setq status :open)))) ;;;skip the rest of the blocks in this file, make the status EOF, and ;;;make the next block be the first block in the next file (defmethod (carry-tape-input-mixin :advance-file) () (cond ((not (memq status '(:eof :closed))) (send self ':clear-input) (cond ((null quart-file-mark-detected-on-last-read) (quart-read-file-mark))) (setq quart-file-mark-detected-on-last-read nil) (setq status ':eof)))) (defmethod (carry-tape-input-mixin :discard-input-buffer) (&rest ignore) nil) (defflavor carry-tape-input-stream () (carry-tape-input-mixin)) ;------------------------------ ; carry-tape-file-streams (defflavor carry-tape-file-input-stream ((raw-stream (make-instance 'carry-tape-input-stream)) file-plist) (si:buffered-input-stream) :gettable-instance-variables :settable-instance-variables :initable-instance-variables :special-instance-variables ) (defmethod (carry-tape-file-input-stream :discard-input-buffer) (&rest ignore) nil) (defmethod (carry-tape-file-input-stream :next-input-buffer) (&rest ignore) (condition-case () (let* ((next-block-size (read-block-mark raw-stream)) (string (or si:stream-input-buffer (make-string 4096.)))) (send raw-stream :string-in nil string 0 next-block-size) (setq si:stream-input-buffer string si:stream-input-index 0 si:stream-input-limit next-block-size) (values si:stream-input-buffer si:stream-input-index si:stream-input-limit)) (file-mark-detected (values nil 0 nil)))) (defmethod (carry-tape-file-input-stream :init) (&rest ignore) (setq file-plist (read-carry-file-plist raw-stream)) (send self :next-input-buffer)) ;-------------------- ; ; random support functions (defun make-carry-tape-file-stream (&optional (direction :input)) (case direction (:input (make-instance 'carry-tape-file-input-stream)) (:output (tv:beep)(format t "~&CARRY-TAPE output streams are not yet suppoerted.~%"))))