;;; -*- Mode:lisp; Package:USER; Base:10; readtable:zl -*-
;;;
;;; Minor support for Symbolics' CARRY-TAPE format
;;;
;;; -dg 5/13/85
;;;

(defun keyword-read (stream)
  "Read symbols into the package keyword"
  (let ((package (find-package "")))
    (read stream)))

(defun make-carry-tape-stream (&rest ignored)
  #+TI
  (mt:make-mt-stream :characters t :byte-size 8 :error nil :density 0 :direction :input
		     :record-size 1024)
  #+LMI
  (fs:make-mt-stream :characters t :byte-size 8 :error nil :density 0 :direction :input
		     :record-size 1024))

(defresource random-string-resource (size)
  "A resource for copying data from a raw quart-stream to another stream."
  :constructor (make-string size))

(defun read-carry-tape-header ()
  (with-open-stream (stream (make-carry-tape-stream))
    (do ((plist (list (intern (string-trim '(#/space) (readline stream))))))
	(())
      (let ((prop (keyword-read stream)))
	(when (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))
	  (return 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)))))

(defun describe-carry-tape-header (h)
  (print h))

(defun preservable-properties (plist)
  (let ((return-list (list "Preservable Properties")))
    (cond-every ((get plist :creation-date)
		 (putprop return-list
			  (typecase (get plist :creation-date)
			    (:string (time:parse-universal-time
				       (get plist :creation-date)))
			    (:number (get plist :creation-date))
			    (t (time:get-universal-time)))
			  :creation-date))
		((get plist :author)
		 (putprop return-list
			  (string (get plist :author))
			  :author)))
    (cdr return-list)))

(defun read-carry-record-plist (instream &aux (plist (list 'carry-tape-file-plist)))
  (do ((prop (keyword-read instream) (keyword-read instream)))
      ((eq prop :end) plist)
    (putprop plist
	     (case prop
	       ((:creation-date :author :type :raw-type :name
				:raw-name :pathname :directory :host :dump-group)
		(readline instream))
	       ((:version :characters :system-type :canonical-type :tape-system-version
			  :record-type :byte-size)
		(read instream)))
	     prop)))

(defun copy-carry-tape-record (host &key
			       (create-directory t)
			       overwrite
			       query
			       from-stream)
  (setq host (si:parse-host host))
  (with-open-stream (instream (if from-stream
				  (open from-stream)
				  (make-carry-tape-stream)))
    (let ((plist (read-carry-record-plist instream))
	  (new-pathname)
	  (preservable-properties))
      (or (setq new-pathname (parse-pathname-on-carry-tape (get plist :pathname) host))
	  (return-from copy-carry-tape-record nil))
      (condition-case ()
	  (probef new-pathname)
	(fs:directory-not-found
	 (when (and create-directory (or (not query)
					 (y-or-n-p "~&Create directory ~A?" new-pathname)))
	   (format t "~&Creating directory for ~A~%" new-pathname)
	   (fs:create-directory new-pathname)
	   (setq overwrite t)))
	(:no-error (if (probef new-pathname)
		       (when (eq overwrite :ask)
			 (setq overwrite (yes-or-no-p "~&Overwrite file [~A]? ")))
		       (setq overwrite t))))
      (cond ((not overwrite)
	     (format t "~&File Already exists - ~A - Not Overwriting~%" new-pathname))
	    ((and query (not (y-or-n-p "~&Restore ~A?" new-pathname)))
	     ())
	    ('else
	     (format t "~&Copying file ~A to ~A - " (get plist :pathname) new-pathname)
	     (fs:create-directory new-pathname)
	     (setq preservable-properties (preservable-properties plist))
	     (with-open-stream (outstream
				 (if overwrite
				     (open new-pathname
					   :direction :output
					   :characters (get plist :characters)
					   :byte-size (or (get plist :byte-size)
							  (if (get plist :characters) 8. 16.)))
				     'si:null-stream))
	       (when (and preservable-properties (neq outstream 'si:null-stream))
		 (lexpr-send outstream :change-properties t preservable-properties))
	       (let ((block-string (make-string 12)))
		 (do ((eof (progn (send instream :string-in t block-string 0 4)
				  (string-equal block-string "EOF " :end1 4))
			   (progn (send instream :string-in t block-string 0 4)
				  (string-equal block-string "EOF " :end1 4))))
		     (eof)
		   (send instream :string-in t block-string 0)
		   (let ((*read-base* 10.))
		     (using-resource (string random-string-resource (read-from-string block-string))
		       (send instream :string-in t string)
		       (send outstream :string-out string))))))
	     (when overwrite (format t "done.~%")))))))

(defun restore-carry-tape (&key
			   (host si:local-host)
			   (overwrite nil)
			   (query nil))
  (setq host (si:parse-host host))
  (fs:mt-rewind)
  (describe-carry-tape-header (read-carry-tape-header))
  (do-forever 
    (condition-case ()
	(copy-carry-tape-record host :overwrite overwrite :query query)
      (fs:read-end-of-file))))

(defconst *dummy-lmfs-host*
	  (progn (si:define-host "dummy"
				 :host-names (list "dummy")
				 :machine-type :lispm
				 :system-type :lispm
				 :chaos '(#o01))
		 (fs:add-file-computer '("dummy" :lmfs))
		 (si:parse-host "dummy")))

(defun parse-pathname-on-carry-tape (string host)
  (let ((spn (fs:parse-pathname string *dummy-lmfs-host*)))
    (fs:merge-pathnames (fs:make-pathname :host host) spn)))

