;;; -*- Mode: Lisp; Package: File-System; Base: 8 -*- ;;; Dump stuff (which will be obsolete as of February 1984) moved to SYS:FILE;ODUMP ;;; ;;; Entry functions which deal with dribble files as well as magtape... ;;; LM-FULL-DUMP -- do a full dump of the file system. ;;; LM-INCREMENTAL-DUMP -- do an incremental dump of the file system. ;;; LM-FULL-DUMP-DIRECTORIES -- do a full dump of specific directories ;;; LM-DUMP -- do a dump of specific files. ;;; DUMP-RESTORE -- look up and restore file(s) which has been DUMPed. ;;; DUMP-LIST-FILES -- given a tape name, list the files on it. ;;; RESTORE-FILES -- given a number of pathnames, restore them selectively ;;; RESTORE -- more general form of RESTORE-FILES ;;; ;;; Info on dumps is kept in a file in the FS: MAGTAPE; directory. ;;; Name is DUMP-.TEXT.0 ;;; Format is (pardon notation): ;;; entry ::= ;;; plist ::= ... ;;; So the end of an entry is signified by two CRs. ;;; Defined properties are: ;;; :NSC -- The file has not been source compared. ;;; Modification history ;;; ;;; dumping stuff ;;; started by DLA sometime ago ;;; hacked by ALR summer of 1982? so that dumps would work somewhat reasonably ;;; improved by HGA starting 11/18/82 (DEFVAR DUMP-HOST "LM") (DEFVAR DUMP-VERSION 1) ;;; In core, the format is: (DEFSTRUCT (TAPE-FILE :LIST* (:DEFAULT-POINTER TAPE-FILE)) TAPE-NAME ;A string TAPE-FILE-NAME ;A pathname TAPE-FILE-PROPERTIES ;File properties of the file TAPE-FILE-TAIL-POINTER ;For efficiently adding entries TAPE-FILE-ENTRIES) ;A list or NIL. (DEFSTRUCT (TAPE-FILE-ENTRY :LIST) ENTRY-TRUENAME ;A pathname ENTRY-CREATION-DATE ;A universal time ENTRY-AUTHOR ;A string ENTRY-PLIST) ;A list or NIL. (DEFVAR TAPE-FILE) (defun setup-tape-file (tape-name &OPTIONAL tape-file-name new-p &AUX entries properties) (or tape-file-name (setq tape-file-name (tape-file-name-from-tape-name tape-name))) (or new-p (multiple-value (entries properties) (read-tape-file tape-file-name))) (setq tape-file (make-tape-file tape-name tape-name tape-file-name tape-file-name tape-file-properties properties tape-file-entries entries)) (setf (tape-file-tail-pointer tape-file) (cond ((last (tape-file-entries tape-file))) (t (locf (tape-file-entries tape-file)))))) (defun read-tape-file (tape-file) (with-open-file (s tape-file '(:READ :NOERROR)) (cond ((errorp s) NIL) (T (values (LOOP FOR entry = (read-tape-entry s) UNTIL (null entry) COLLECTING entry) NIL;(funcall s ':FILE-PLIST) ))))) (defun read-tape-entry (stream &AUX line eof pathname creation-date author plist prop val tem (package si:pkg-user-package) (ibase 10.)) (prog NIL (multiple-value (line eof) (SEND stream ':LINE-IN T)) (and eof (return nil)) (setq pathname (merge-pathname-defaults line)) (setq line (SEND stream ':LINE-IN T)) (setq creation-date (time:parse-universal-time creation-date 0 NIL NIL)) (setq author (SEND stream ':LINE-IN T)) l (setq line (SEND stream ':LINE-IN T)) (and (equal line "") (return (make-tape-file-entry entry-truename pathname entry-creation-date creation-date entry-author author entry-plist plist))) (setq tem (string-search-char #\SP line)) (setq prop (intern (substring line 0 tem) si:pkg-user-package)) (setq val (read-from-string line 'SI:NO-EOF-OPTION tem)) (setq plist (nconc plist (list* prop val nil))) (go l))) (defun dump-list-files (tape-name &AUX tape-file) (setup-tape-file tape-name) (format T "~%Tape ~A~:[ not found.~;~%~]" (tape-name) (tape-file-entries)) (mapc 'LIST-ONE-ENTRY (tape-file-entries)) T) (defun tape-name-from-tape-file-name (file-name) (substring (SEND file-name ':NAME) 5)) (defun list-one-entry (entry) (format T "~% ~A~40T" (entry-truename entry)) (time:print-universal-time (entry-creation-date entry)) (format T "~60T~A" (entry-author entry))) (defun write-tape-file () (with-open-file (s (tape-file-name) '(:WRITE)) (dolist (e (tape-file-entries)) (write-tape-file-entry e s)) ;(tv:doplist ((tape-file-properties) prop ind) ;(funcall s ':PROPERTY-LIST prop ind)))) )) (defun write-tape-file-entry (entry stream &AUX (base 10.) (*Nopoint T) (package si:pkg-user-package)) (SEND stream ':LINE-OUT (SEND (entry-truename entry) ':STRING-FOR-PRINTING)) (time:print-universal-time (entry-creation-date entry) stream) (SEND stream ':TYO #\CR) (SEND stream ':LINE-OUT (entry-author entry)) (LOOP FOR p ON (entry-plist entry) BY #'CDDR DO (format stream "~A ~S~%" (first p) (second p))) (SEND stream ':TYO #\CR)) (defun tape-file-name-from-tape-name (tape-name) (merge-pathname-defaults (make-pathname ':HOST "FS" ':DIRECTORY "Magtape" ':NAME (format NIL "DUMP-~A" tape-name) ':TYPE "TEXT" ':VERSION dump-version))) (DEFVAR DUMP-FILE-LIST) (DEFVAR DUMP-TYPE) (DEFVAR DUMP-GET-MORE-FILES) (defun file-to-pathname (file) (merge-pathname-defaults (make-pathname ':Host "fs" ':DIRECTORY (directory-name (file-directory file)) ':Name (file-name file) ':TYPE (file-type file) ':VERSION (file-version file)))) (DEFUN LM-FULL-DUMP (&optional (HOST DUMP-HOST)) (DUMP-1 HOST "Full" (dump-get-all-lm-files))) (defun lm-incremental-dump (&OPTIONAL (host DUMP-HOST)) (dump-1 host "Incremental" (dump-get-undumped-lm-files))) (defun lm-full-dump-directories (dirs &OPTIONAL (host DUMP-HOST)) (dump-1 host "Random directories" (dump-get-directories dirs host))) (DEFUN LM-DUMP (&OPTIONAL (host DUMP-HOST)) (LET ((FILES (DUMP-PROMPT-AND-READ-FILES host))) (AND FILES (DUMP-1 (SEND (CAR FILES) ':HOST) "Random files" FILES)))) ;;; this doesn't quite work yet for local file systems because ;;; because read-directory-files wants raw directories (defun dump-get-directories (dirs host &AUX result tem) (cond ((equal host DUMP-HOST) (ferror nil "Sorry, you can't dump just a few of the local file system's files yet")) (T (dolist (dir dirs) (setq dir (make-pathname ':host host ':device ':wild ':directory dir ':name ':wild ':type ':wild ':version ':wild)) (setq tem (cdr (SEND dir ':directory-list '(:fast)))) (setq result (nconc result (mapcar 'car tem)))) result))) (defun dump-get-all-lm-files (&OPTIONAL dir continue &AUX dirs result) (cond (dir (setq dirs (LOOP FOR d ON (DIRECTORY-FILES (dc-ROOT-directorY)) WHEN (equalp dir (directory-name (first d))) DO (return (if continue d (ncons (first d)))) FINALLY (return NIL)))) (T (setq dirs (dc-ROOT-directorY)))) (dolist (dir dirs) (setq result (nconc result (copylist* (read-directory-files dir))))) result) (defun mark-all-undumped-files-dumped (&OPTIONAL dir continue) (LOOP FOR file IN (dump-get-all-lm-files dir continue) UNLESS (file-attribute file ':DUMPED) DO (format T "~%Marking file ~A dumped." file) (change-file-properties (file-truename file) NIL ':NOT-BACKED-UP NIL))) (defun dump-get-undumped-lm-files (&AUX (result nil)) (LOOP FOR dir IN (DIRECTORY-FILES (dc-ROOT-directorY)) DO (LOOP FOR file IN (read-directory-files dir) UNLESS (file-attribute file ':DUMPED) DO (push file result))) (nreverse result)) ;;; this is broken for lists of remote dirs. individual files work. - hga (DEFUN DUMP-PROMPT-AND-READ-FILES (HOST &AUX TEM LINES RESULT) (DO () (NIL) (FORMAT T "~% FILE= ") (SETQ TEM (READLINE)) (AND (EQUAL TEM "") (RETURN NIL)) (PUSH TEM LINES)) (DOLIST (LINE LINES) (SETQ LINE (MERGE-PATHNAME-DEFAULTS (PARSE-PATHNAME LINE HOST *DEFAULT-PATHNAME-DEFAULTS*))) (SETQ TEM (SEND LINE ':DIRECTORY-LIST '(:FAST))) (SETQ RESULT (NCONC RESULT (MAPCAR 'CAR TEM)))) (SI:ELIMINATE-DUPLICATES RESULT)) (defun dump-1 (host dump-type dump-file-list &AUX tape-file tape-name tape-file-name probe) (*catch 'DUMP-1 (format T "~%Enter Tape Name: ") (setq tape-name (readline)) (setq tape-file-name (tape-file-name-from-tape-name tape-name)) (setq probe (open tape-file-name '(:PROBE))) (setup-tape-file tape-name tape-file-name T)) (format T "~&~A dump ~:[starting on~;appending to~] tape ~A, " dump-type (tape-file-entries) (tape-name)) (time:print-current-time) (dump-2 host) (write-tape-file) (format T "~&~A dump completed on tape ~A." dump-type (tape-name))) (defun dump-2 (host &AUX current-directory this-host current-directory-start (current-tape-start dump-file-list) (current-file-list dump-file-list) #|compare-end|#) (SETQ THIS-HOST (EQ (fs:get-pathname-HOST host) si:local-host)) (*Catch 'DUMP-2 (do () (NIL) (condition-case () (do () (NIL) (if (null current-file-list) (*THROW 'DUMP-2 T)) (cond ((not (equal current-directory (if this-host (file-directory (first current-file-list)) (SEND (first current-file-list) ':directory)))) (setq current-directory (if this-host (file-directory (first current-file-list)) (SEND (first current-file-list) ':directory))) (setq current-directory-start current-file-list) (format T "~% Dumping ~A" current-directory))) (dump-one-file (pop current-file-list) this-host)) (end-of-tape nil)) ;; Here reached EOT. Space back over files until beginning of directory, and reset. (mt-space-rev-to-bof 0 (find-position-in-list (first current-file-list) current-directory-start)) (mt-write-eof) (mt-rewind) ;(setq compare-end (dump-compare-files current-tape-start NIL)) ;(cond ((neq compare-end current-directory-start) ;(ferror NIL "Number of files on tape does not match. (Bug in DUMP)"))) ;(mt-rewind) (mt-offline) (dump-get-new-tape T) (setq current-file-list current-directory-start current-tape-start current-directory-start))) ;; Here we've reached the end of the dump... (mt-write-eof) (mt-rewind) ; (setq compare-end (dump-compare-files current-tape-start NIL)) ; (cond (compare-end ; (ferror NIL "All files evidently didn't get dumped. (Bug in DUMP)"))) (when this-host ; Mark as dumped, locally. (dolist (file dump-file-list) (set-file-attribute file t ':dumped)) (save-directory-tree)) (mt-offline)) ;; Cuts a lot of unneccessary crap from COPY-FILE, losing generality. (defun dump-one-file (file this-host) (let* ((truename (if this-host (file-truename file) (SEND file ':truename))) (plist (if this-host (lm-construct-plist file) (cdr (file-properties file)))) (author (second (memq ':AUTHOR plist))) (creation-date (second (memq ':CREATION-DATE plist)))) (copy-file truename "MT:" ':default-byte-size (second (memq ':byte-size plist))) ;; If it's not a local dump, change the property now. (if (not this-host) (change-file-properties truename NIL ':NOT-BACKED-UP NIL)) (append-tape-file-entry truename creation-date author '(:NSC T)))) ;; Not Source Compared (DEFUN REVERSE-PLIST (PLIST &AUX ANS) (TV:DOPLIST (PLIST VALUE IND) (SETQ ANS (CONS IND (CONS VALUE ANS)))) ANS) ;build list in reverse order to eliminate gross paging every time. (defun append-tape-file-entry (name creation-date author plist) (LET ((FOO (ncons (make-tape-file-entry entry-truename name entry-creation-date creation-date entry-author author entry-plist (copylist plist))))) (RPLACD (TAPE-FILE-TAIL-POINTER) FOO) (SETF (TAPE-FILE-TAIL-POINTER) FOO))) ; save until we're sure the above works ;(defun append-tape-file-entry (name creation-date author plist) ; (setf (tape-file-entries) ; (nconc (tape-file-entries) ; (ncons (make-tape-file-entry entry-truename name ; entry-creation-date creation-date ; entry-author author ; entry-plist (copylist plist)))))) ;this guy isn't used by anyone (defun find-file (filename) (let* ((pathname (fs:parse-pathname filename)) (directory (SEND pathname ':DIRECTORY)) (name (SEND pathname ':NAME)) (type (SEND pathname ':TYPE)) (version (SEND pathname ':VERSION))) (LOOP FOR dir IN (dc-ROOT-directorY) AS directory-name = (directory-name dir) AS printed-flag = NIL WHEN (or (not (stringp directory)) (string-search directory directory-name)) DO (LOOP FOR file IN (read-directory-files dir) WHEN (or (not (stringp name)) (string-search name (file-name file))) WHEN (or (not (stringp type)) (string-search type (file-type file))) WHEN (or (not (numberp version)) (= version (file-version file))) DO (if (null printed-flag) (progn (setq printed-flag T) (format T "~2%~A;" directory-name))) (format T "~%~A.~A#~A" (file-name file)(file-type file)(file-version file)))))) (defun find-multiple-files () (LOOP FOR dir IN (dc-ROOT-directorY) AS directory-name = (directory-name dir) AS printed-flag = NIL DO (print-directory-multiple-versions dir))) (defun find-multiple-files-in-dir (directory) (if (not (typep directory 'FS:DIRECTORY)) (setq directory (lookup-directory directory))) (LOOP FOR file IN (read-directory-files directory) WITH directory-results = NIL AS file-entry = (assoc (file-name file) directory-results) AS type-entry = (assoc (file-type file) (cdr file-entry)) DO (cond ((and (null file-entry) (null type-entry)) (push (list (file-name file) (list (file-type file) file)) directory-results)) ((null type-entry) (push (list (file-type file) file) (cdr file-entry))) (T (push file (cdr type-entry)))) FINALLY (return directory-results))) (defvar type-versions '(("LISP" . 2) ("TEXT" . 2) ("INIT" . 2) ("RMAIL" . 2) ("QFASL" . 1) ("DRAW" . 2))) (defun print-directory-multiple-versions (directory) (format T "~&~%Directory ~A~2%" (if (typep directory ':STRING) directory (directory-name directory))) (LOOP FOR (filename . types) IN (nreverse (find-multiple-files-in-dir directory)) DO (LOOP FOR (type . files) IN types AS versions = (cdr (assoc type type-versions)) WHEN (or (and versions (> (length files) versions)) (and (null versions) (> (length files) 1))) DO (LOOP FOR f IN files DO (format T "~%~A;~A.~A#~A" (directory-name (file-directory f)) (file-name f) (file-type f) (file-version f)))))) (defun find-directory-files (directory-name) (let* ((directory-pathname (fs:parse-pathname (format NIL "~A;*.*" directory-name))) (directory-file-list (SEND directory-pathname ':DIRECTORY-LIST '(:SORTED)))) (LOOP FOR file-entry IN (cdr directory-file-list) WITH result = NIL AS pathname = (first file-entry) AS name = (SEND pathname ':NAME) AS type = (SEND pathname ':TYPE) AS string = (fs:make-pathname ':DIRECTORY directory-name ':NAME name ':TYPE type) AS search = (assoc string result) UNLESS search DO (push (list string file-entry) result) ELSE DO (setf (second search) file-entry) FINALLY (return result)))) (defun find-all-single-files (&OPTIONAL (dirs *)) (LOOP FOR dir IN dirs APPEND (find-directory-files dir))) ;;;??? :PLISTs will disagree some, I fear. (DEFUN DUMP-COMPARE-FILES (FILE-LIST ;FILES-NOT-DUMPED &AUX PATH COMPARE-END) (*CATCH 'DUMP-COMPARE-FILES (DO ((F FILE-LIST (CDR F)) (E (TAPE-FILE-ENTRIES) (CDR E))) ((NULL F)) (WITH-OPEN-FILE (FILE-STREAM (CAR F) ':CHARACTERS ':DEFAULT ':BYTE-SIZE ':DEFAULT ':PDP10-FORMAT ':DEFAULT) (SETQ PATH (SEND (CAR F) ':TRUENAME)) (WITH-OPEN-FILE (MT-STREAM "MT: *" ':CHARACTERS (SEND FILE-STREAM ':CHARACTERS) ':BYTE-SIZE (SEND FILE-STREAM ':BYTE-SIZE)) (IF (ERRORP MT-STREAM) (PROGN (SETQ COMPARE-END F) (*THROW 'DUMP-COMPARE-FILES NIL)) (LET ((CD-CORRECT (EQUAL (SEND MT-STREAM ':CREATION-DATE) (SEND FILE-STREAM ':CREATION-DATE))) (AUTHOR-CORRECT (EQUAL (SEND MT-STREAM ':AUTHOR) (SEND FILE-STREAM ':AUTHOR))) (PLIST-CORRECT (EQUAL (SEND MT-STREAM ':PLIST) (SEND FILE-STREAM ':PLIST)))) (COND ((AND CD-CORRECT AUTHOR-CORRECT PLIST-CORRECT (DUMP-SOURCE-COMPARE-STREAMS MT-STREAM FILE-STREAM)) (OR (EQUAL PATH (ENTRY-TRUENAME (CAR E))) (FERROR NIL "Shouldn't happen.")) (REMPROP (LOCF (ENTRY-PLIST (CAR E))) ':NOT-SOURCE-COMPARED) (SEND (CAR F) ':PUTPROP ':DUMPED T)) (T (FORMAT T "~% Compare error ~A: ~ ~:[Creation date ~]~:[Author ~]~:[PLIST~]" PATH CD-CORRECT AUTHOR-CORRECT PLIST-CORRECT) (COND ((NULL PLIST-CORRECT) (FORMAT T "~% Magtape PLIST ~S, Disk PLIST ~S" (SEND MT-STREAM ':PLIST) (SEND FILE-STREAM ':PLIST)))))))))))) COMPARE-END) (DEFUN DUMP-SOURCE-COMPARE-STREAMS (S1 S2 &AUX (ALPHABETIC-CASE-AFFECTS-STRING-COMPARISON T)) (PROG (BUF1 IDX1 CNT1 BUF2 IDX2 CNT2 CC (BUF-START 0)) L (MULTIPLE-VALUE (BUF1 IDX1 CNT1) (SEND S1 ':GET-INPUT-BUFFER)) (MULTIPLE-VALUE (BUF2 IDX2 CNT2) (SEND S2 ':GET-INPUT-BUFFER)) (COND ((NOT (AND BUF1 BUF2)) (IF BUF1 (FORMAT T "~%Stream 1 longer")) (IF BUF2 (FORMAT T "~%Stream 2 longer")) (RETURN (NOT (OR BUF1 BUF2))))) (SETQ CC (MIN CNT1 CNT2)) (COND ((NULL (%STRING-EQUAL BUF1 IDX1 BUF2 IDX2 CC)) (FORMAT T "~%Compare fails in buffer starting at ~D" BUF-START) (RETURN NIL))) (SEND S1 ':ADVANCE-INPUT-BUFFER (+ IDX1 CC)) (SEND S2 ':ADVANCE-INPUT-BUFFER (+ IDX2 CC)) (SETQ BUF-START (+ BUF-START CC)) (GO L))) (DEFUNP DUMP-GET-NEW-TAPE (&OPTIONAL OLD-FULL-P &AUX TAPE-NAME TAPE-FILE-NAME PROBE) (PUTPROP (LOCF (TAPE-FILE-PROPERTIES)) (NOT OLD-FULL-P) 'TAPE-MAY-APPEND) (WRITE-TAPE-FILE) (BEEP) (FORMAT T "~&>>> Time to mount a new tape <<<") FOO (FORMAT T "~%Continue ~A dump on tape: " DUMP-TYPE) (SETQ TAPE-NAME (READLINE)) (SETQ TAPE-FILE-NAME (TAPE-FILE-NAME-FROM-TAPE-NAME (SEND (TAPE-FILE-NAME) ':HOST) TAPE-NAME)) (SETQ PROBE (OPEN TAPE-FILE-NAME '(:PROBE))) (COND ((NOT (ERRORP PROBE)) (FORMAT T "~&Tape ~A has been used. Are you *SURE* you want to overwrite it? ") (OR (Y-OR-N-P) (GO FOO)))) (MT-REWIND) (SETUP-TAPE-FILE TAPE-NAME TAPE-FILE-NAME T) (FORMAT T "~&Continuing ~A dump on tape ~A ..." DUMP-TYPE TAPE-NAME)) ;; RESTORE (DEFVAR RESTORE-PATHNAMES) (DEFVAR RESTORE-PATHNAME-TAPENAME-ALIST) (DEFUN RESTORE-FILES (&REST PATHNAMES) (LET ((RESTORE-PATHNAMES (MAPCAR 'MERGE-PATHNAME-DEFAULTS PATHNAMES))) (RESTORE-1))) (DEFUN RESTORE () (LET ((RESTORE-PATHNAMES (LOOP FOR PATH = (FORMAT T "~& FILE= ") (READLINE) WHEN (EQUAL PATH "") RETURN PATHLIST COLLECT (MERGE-PATHNAME-DEFAULTS PATH) INTO PATHLIST))) (RESTORE-1))) (DEFUN RESTORE-1 (&AUX MAGTAPE-DIRECTORY RESTORE-PATHNAME-TAPENAME-ALIST TAPE-NAME TAPE-FILE TAPE-SHOWN TEM HOST) (SETQ HOST (SEND (CAR RESTORE-PATHNAMES) ':HOST)) (FORMAT T "~%(Type Abort to stop searching for files)") (SETQ MAGTAPE-DIRECTORY (MAPCAR 'CAR (SORT (DIRECTORY-LIST (SEND (DEFAULT-PATHNAME NIL HOST) ':NEW-PATHNAME ':DIRECTORY "MAGTAPE" ':NAME ':WILD ':TYPE ':WILD)) #'(LAMBDA (X Y) (NOT (< (GET X ':CREATION-DATE) (GET Y ':CREATION-DATE))))))) (ERROR-RESTART-LOOP ((SYS:ABORT ERROR) "Continue restoring files to ~A" HOST) (DOLIST (TAPE-FILE-NAME MAGTAPE-DIRECTORY) (SETQ TAPE-NAME (TAPE-NAME-FROM-TAPE-FILE-NAME TAPE-FILE-NAME)) (SETUP-TAPE-FILE TAPE-NAME) (SETQ TAPE-SHOWN NIL) (DOLIST (ENTRY (TAPE-FILE-ENTRIES)) (COND ((RESTORE-PATHNAMES-MATCH (ENTRY-TRUENAME ENTRY)) (COND ((NULL TAPE-SHOWN) (FORMAT T "~%~%Tape ~A:" (TAPE-NAME)) (SETQ TAPE-SHOWN (LIST* (TAPE-NAME) TAPE-FILE NIL)))) (LIST-ONE-ENTRY ENTRY) (RPLACD (LAST TAPE-SHOWN) (NCONS ENTRY))))) (AND TAPE-SHOWN (PUSH TAPE-SHOWN RESTORE-PATHNAME-TAPENAME-ALIST)) ;; Allow abort to throw now. (AND (SETQ TEM (SEND STANDARD-INPUT ':TYI-NO-HANG)) (SEND STANDARD-INPUT ':UNTYI TEM)))) (RESTORE-2)) (DEFUN RESTORE-PATHNAMES-MATCH (PATHNAME) (DOLIST (P RESTORE-PATHNAMES) (RESTORE-PATHNAME-MATCH PATHNAME P))) ;; P2 shouldn't contain wildcards... (DEFUN RESTORE-PATHNAME-MATCH (P1 P2 &AUX T1 T2) (AND (OR (EQUAL (SETQ T1 (PATHNAME-DIRECTORY P1)) (SETQ T2 (PATHNAME-DIRECTORY P2))) (AND (STRINGP T1) (STRINGP T2) (STRING-SEARCH-CHAR #\* T1) (WILD-STRING-MATCH T1 T2))) (OR (EQUAL (SETQ T1 (PATHNAME-NAME P1)) (SETQ T2 (PATHNAME-NAME P2))) (EQ T1 ':WILD) (AND (STRINGP T1) (STRINGP T2) (STRING-SEARCH-CHAR #\* T1) (WILD-STRING-MATCH T1 T2))) (OR (EQUAL (SETQ T1 (PATHNAME-TYPE P1)) (SETQ T2 (PATHNAME-TYPE P2))) (EQ T1 ':WILD) (AND (STRINGP T1) (STRINGP T2) (STRING-SEARCH-CHAR #\* T1) (WILD-STRING-MATCH T1 T2))) (OR (SYMBOLP (SETQ T1 (PATHNAME-VERSION P1))) (EQUAL T1 (PATHNAME-VERSION P2))))) (DEFUN RESTORE-2 (&AUX TAPE-NAME TEM) (DO () (NIL) TOP (FORMAT T "~% Enter Tape name to restore or [Return]: ") (AND (EQUAL (SETQ TAPE-NAME (READLINE)) "") (RETURN NIL)) (COND ((SETQ TEM (ASSOC TAPE-NAME RESTORE-PATHNAME-TAPENAME-ALIST)) (SETQ TAPE-FILE (SECOND TEM)) (FORMAT T "~% Files to be restored on ~A:") (MAPC 'LIST-ONE-ENTRY (CDDR TEM)) (SELECTQ (RESTORE-TAPE-OPTION) (ALL (MAPC 'RESTORE-TAPE-ENTRIES (CDDR TEM))) (SELECTIVE (MAPC 'RESTORE-TAPE-ENTRIES (SELECT-FILES-TO-RESTORE (CDDR TEM)))))) (T (SEND T "~% No files were found on ~A."))))) (DEFUN RESTORE-TAPE-OPTION () ;; Returns NIL if user types abort. (CONDITION-CASE () (FQUERY '(:CHOICES (((ALL "All.") #/A) ((SELECTIVE "Selective.") #/S))) "Restore all these or be selective? ") (SYS:ABORT ()))) (DEFUN RESTORE-TAPE-ENTRIES (ENTRIES &AUX FILE-POS SKIP) (MT-REWIND) (DO ((E ENTRIES (CDR E)) (POS 0)) ((NULL E)) (OR (SETQ FILE-POS (FIND-POSITION-IN-LIST (CAR E) (TAPE-FILE-ENTRIES))) (FERROR NIL "Tape entry not found in tape file on second pass.")) (AND (MINUSP (SETQ SKIP (- POS FILE-POS))) (FERROR NIL "Tape entries out of order on second pass.")) (MT-SPACE-TO-EOF 0 SKIP) (RESTORE-FILE-FROM-TAPE (CAR E)) (SETQ POS (1+ FILE-POS)))) (DEFUN RESTORE-FILE-FROM-TAPE (ENTRY &AUX PROBE TRUENAME AUTHOR CREATION-DATE FLAVOR BYTE-SIZE CHARACTERS HOST) (SETQ PROBE (OPEN "MT: *" '(:PROBE))) (SETQ TRUENAME (SEND PROBE ':TRUENAME) AUTHOR (SEND PROBE ':AUTHOR) CREATION-DATE (SEND PROBE ':CREATION-DATE) CHARACTERS (SEND PROBE ':CHARACTERS) BYTE-SIZE (SEND PROBE ':BYTE-SIZE) FLAVOR (SEND PROBE ':GET ':FLAVOR)) (OR (AND (EQ TRUENAME (ENTRY-TRUENAME ENTRY)) (EQUAL AUTHOR (ENTRY-AUTHOR ENTRY)) (EQUAL CREATION-DATE (ENTRY-CREATION-DATE ENTRY))) (FERROR NIL "Incorrect file found on tape.")) (WITH-OPEN-FILE (I "MT: *#0" ':CHARACTERS CHARACTERS ':BYTE-SIZE BYTE-SIZE) (WITH-OPEN-FILE (O (SEND TRUENAME ':NEW-PATHNAME ':HOST HOST ':DEVICE "DSK") ':CHARACTERS CHARACTERS ':BYTE-SIZE BYTE-SIZE ':DIRECTION ':OUTPUT ':FLAVOR FLAVOR) (STREAM-COPY-UNTIL-EOF I O NIL) (SEND O ':SET-AUTHOR AUTHOR) (SEND O ':SET-CREATION-DATE CREATION-DATE) (LEXPR-SEND O ':CHANGE-PROPERTIES NIL (SEND I ':PLIST)) (SEND O ':CHANGE-PROPERTIES NIL ':DUMPED (NOT (GET (LOCF (ENTRY-PLIST ENTRY)) ':NOT-SOURCE-COMPARED)))))) (DEFUN SELECT-FILES-TO-RESTORE (ENTRIES) ; (get (locf hdr) 'truename) (LOOP FOR ENTRY IN ENTRIES DOING (LIST-ONE-ENTRY ENTRY) (FORMAT T " Restore? ") WHEN (Y-OR-N-P) COLLECT ENTRY))