;;; -*- Mode:LISP; Package:USER; Readtable:CL; Base:10 -*- ;;;FILE-MATCHER.LISP ;;;FILE-MATCHER makes a closure that will be a function acceptable to ;;;tape functions as a file matcher. This function will let the user of ;;;the tape function (e.g. RESTORE-FILES) specify once only for each ;;;top-level directory encountered whether the file "matches" and should ;;;be processed. ;;; ;;;Keyword options provide more control, over the timeout in seconds, ;;;the default answer per directory (whether it "matches"), and ;;;verbosity mode. (defvar *file-matcher-list* nil) (defvar *file-matcher-default* nil) (defvar *file-matcher-timeout* (* 15 60.)) (defvar *file-matcher-verbose* nil) (defun matcher (flist) (let* ((file (first flist)) (dir (pathname-directory file)) (top (string (if (atom dir) dir (first dir))))) (if (rest (or (find top *file-matcher-list* :key #'first :test #'string-equal) (first (push (cons top (y-or-n-p-with-timeout (* *file-matcher-timeout* 60.) *file-matcher-default* "Process ~a directory?" top)) *file-matcher-list*)))) file ;;else (if *file-matcher-verbose* (format t "~&Skipping ~a" file))))) (defun file-matcher(&key (matches nil) (default *file-matcher-default*) (timeout *file-matcher-timeout*) (verbose *file-matcher-verbose*)) "Returns a function that will maintain a list of top-level directories and a status indication of whether they should be processed or not. \ This function is suitable for use as a file-matching function within the tape software (a match function that can be used as the value for a :MATCH keyword). The file matcher function accepts one argument, which must be a (disembodied) tape file property list, where the CAR of the list is the pathname. The file matcher does not inspect the CDR of the tape file property list. \ A typical use is with TAPE:RESTORE-FILES, in cases where you want to restore selected top level directories. As each top level directory is encountered, the file matcher function asks the user whether it should be processed. On subsequent calls, when passed a top level directory that was previously encountered, the matcher will not ask, but will return non-NIL on files that should be processed." (let((*file-matcher-list* matches) (*file-matcher-default* default) (*file-matcher-timeout* timeout) (*file-matcher-verbose* verbose)) (closure '(*file-matcher-list* *file-matcher-default* *file-matcher-timeout* *file-matcher-verbose*) 'matcher))) #| (defun test() (let((fcn (file-matcher)) (fls '("joe;a" "joe;b" "joe;c" "keith;foo" "keith;stuff" "keith;more" "zozz;zozzle" "zozz.zizz;zozzle" "keith;morestuff"))) (dolist (file fls) (setq file (fs:parse-pathname file si:local-host)) (format t "~%File ~s - ~:[skip it~;process it~]" file (funcall fcn (ncons file)))))) |#