;; -*- Mode: Lisp; Package: Moby-File-System; Base: 8 -*- ;;; Pathname parsing support for LM systems. (DEFUN MLM-PARSE-NAMESTRING (STRING &OPTIONAL (START 0) END &AUX CHAR STATE TEM TEM1 FIELD-START (DEVICE "DSK") DIRECTORY NAME TYPE VERSION) ;; STATE can be T, DOTTED, VERSION, DIRECTORY or NIL. ;; NIL is the initial state, and means anything is allowed and nothing is in progress. ;; T means that we are in the middle of a name, but nothing else special. ;; DOTTED means we have encountered a single period. TEM is what preceded it. ;; DOUBLE-DOTTED means we have encountered "name.name." ;; TEM is the first name and TEM1 is the second. ;; DIRECTORY means we have encountered "name . name . name" ;; which can only be the beginning of a directory name, ;; or else that we have encountered a "<". ;; VERSION means reading a version number (following a #). (OR END (SETQ END (ARRAY-ACTIVE-LENGTH STRING))) (DO ((INDEX START (1+ INDEX))) (NIL) (IF ( INDEX END) (SETQ CHAR 'DONE) (SETQ CHAR (AREF STRING INDEX))) (COND ((AND (NOT (MEMQ CHAR '(#/SP #/TAB #/. #/: #/; #/# DONE))) (OR (NOT (MEMQ CHAR '(#/< #/>))) (MEMQ STATE '(VERSION DOUBLE-DOTTED)))) (AND (%STORE-CONDITIONAL (LOCF STATE) NIL T) (SETQ FIELD-START INDEX)) (COND ((OR (EQ CHAR #//) (EQ CHAR #/)) (SETQ INDEX (1+ INDEX)) (OR (< INDEX END) (MLM-CHAR-ERROR STRING 'DONE)) (SETQ CHAR (AREF STRING INDEX)) (AND ( CHAR #o200) ( CHAR #/TAB) (MLM-CHAR-ERROR STRING CHAR))))) ((EQ CHAR #/<) (COND ((NULL STATE)) ;Extraneous whitespace. ((EQ STATE T) (SETQ NAME (MLM-FIELD STRING FIELD-START INDEX))) ((EQ STATE 'DOTTED) (AND TEM (SETQ NAME TEM)) (SETQ TYPE (MLM-FIELD STRING FIELD-START INDEX))) ((EQ STATE 'DOUBLE-DOTTED) (AND TEM (SETQ NAME TEM)) (AND TEM1 (SETQ TYPE TEM1)) (SETQ VERSION (MLM-FIELD STRING FIELD-START INDEX T))) (T (MLM-CHAR-ERROR STRING CHAR))) (SETQ STATE 'DIRECTORY DIRECTORY NIL) (GO NEW-FIELD)) ((MEMQ CHAR '(#/SP #/TAB DONE)) (COND ((NULL STATE)) ;Extraneous whitespace. ((EQ STATE T) (SETQ NAME (MLM-FIELD STRING FIELD-START INDEX) STATE NIL)) ((EQ STATE 'DOTTED) (AND TEM (SETQ NAME TEM)) (SETQ TYPE (MLM-FIELD STRING FIELD-START INDEX) STATE NIL)) ((EQ STATE 'DOUBLE-DOTTED) (AND TEM (SETQ NAME TEM)) (AND TEM1 (SETQ TYPE TEM1)) (SETQ VERSION (MLM-FIELD STRING FIELD-START INDEX T) STATE NIL) (COND ((EQ VERSION 0) (SETQ VERSION :NEWEST)) ((EQ VERSION -2) (SETQ VERSION :OLDEST)))) ((EQ STATE 'VERSION) (SETQ VERSION (MLM-FIELD STRING FIELD-START INDEX T) STATE NIL)) (T (MLM-CHAR-ERROR STRING CHAR)))) ((EQ CHAR #/.) (COND ((NULL STATE) ;Could only be :UNSPECIFIC name (SETQ TEM NIL STATE 'DOTTED)) ((EQ STATE T) ;Could either be directory or name (SETQ STATE 'DOTTED TEM (MLM-FIELD STRING FIELD-START INDEX))) ((EQ STATE 'DOTTED) (OR TEM (MLM-CHAR-ERROR STRING #/.)) (SETQ TEM1 (MLM-FIELD STRING FIELD-START INDEX) STATE 'DOUBLE-DOTTED)) ((EQ STATE 'DOUBLE-DOTTED) (OR TEM (MLM-CHAR-ERROR STRING #/.)) (SETQ STATE 'DIRECTORY DIRECTORY (LIST* TEM TEM1 (MLM-FIELD STRING FIELD-START INDEX) NIL))) ((EQ STATE 'DIRECTORY) (SETQ DIRECTORY (NCONC DIRECTORY (NCONS (MLM-FIELD STRING FIELD-START INDEX))))) ((EQ STATE 'VERSION) (SETQ VERSION (MLM-FIELD STRING FIELD-START INDEX T) STATE 'DOTTED)) (T (MLM-CHAR-ERROR STRING CHAR))) (GO NEW-FIELD)) ((EQ CHAR #/#) (COND ((NULL STATE) (SETQ STATE 'VERSION)) ((EQ STATE T) (SETQ NAME (MLM-FIELD STRING FIELD-START INDEX) STATE 'VERSION)) ((EQ STATE 'DOTTED) (AND TEM (SETQ NAME TEM)) (SETQ TYPE (MLM-FIELD STRING FIELD-START INDEX) STATE 'VERSION)) (T (MLM-CHAR-ERROR STRING CHAR))) (GO NEW-FIELD)) ((OR (EQ CHAR #/;) (EQ CHAR #/>)) (COND ((EQ STATE T) (SETQ DIRECTORY (MLM-FIELD STRING FIELD-START INDEX)) (IF (STRING-EQUAL DIRECTORY "~") (SETQ DIRECTORY :ROOT))) ((EQ STATE 'DOTTED) (OR TEM (MLM-CHAR-ERROR STRING CHAR)) (SETQ DIRECTORY (LIST TEM (MLM-FIELD STRING FIELD-START INDEX)))) ((EQ STATE 'DOUBLE-DOTTED) (OR (AND TEM TEM1) (MLM-CHAR-ERROR STRING CHAR)) (SETQ DIRECTORY (LIST TEM TEM1 (MLM-FIELD STRING FIELD-START INDEX)))) ((EQ STATE 'DIRECTORY) (LET ((FIELD (MLM-FIELD STRING FIELD-START INDEX))) (IF (AND (NULL DIRECTORY) (EQ FIELD :UNSPECIFIC)) (SETQ DIRECTORY :ROOT) (SETQ DIRECTORY (NCONC DIRECTORY (LIST FIELD)))))) (T (MLM-CHAR-ERROR STRING CHAR))) (SETQ STATE NIL)) ((EQ STATE T) (SETQ DEVICE (SEND SELF :PARSE-DEVICE-SPEC (MLM-FIELD STRING FIELD-START INDEX)) STATE NIL)) (T (MLM-CHAR-ERROR STRING CHAR))) (GO SKIP) NEW-FIELD (SETQ FIELD-START (1+ INDEX)) SKIP (AND (EQ CHAR 'DONE) (RETURN (values DEVICE DIRECTORY NAME TYPE VERSION))))) (DEFPROP MLM-CHAR-ERROR T :ERROR-REPORTER) (DEFUN MLM-CHAR-ERROR (STRING CHAR) (IF (EQ CHAR 'DONE) (FERROR :PATHNAME-PARSE-ERROR "Unexpected end of string while parsing ~S." STRING) (FERROR :PATHNAME-PARSE-ERROR "Unexpected character (~:C) while parsing ~S." CHAR STRING))) (DEFUN MLM-FIELD (STRING &OPTIONAL (START 0) END VERSION-P DEVICE-P &AUX SIZE ARR CHAR) DEVICE-P (OR END (SETQ END (ARRAY-ACTIVE-LENGTH STRING))) (SETQ SIZE (- END START)) (IF (ZEROP SIZE) :UNSPECIFIC (SETQ ARR (MAKE-ARRAY SIZE :TYPE 'ART-STRING)) (DO ((I START (1+ I)) (SI 0)) (( I END) (OR (= SI SIZE) (SETQ ARR (ADJUST-ARRAY-SIZE ARR SI)))) (COND ((NOT (MEMQ (SETQ CHAR (AREF STRING I)) '(#// #/))) (AND ( CHAR 200) ( CHAR #/TAB) (MLM-CHAR-ERROR STRING CHAR)) (ASET (CHAR-UPCASE CHAR) ARR SI) (SETQ SI (1+ SI))))) (COND ((STRING-EQUAL ARR "*") :WILD) ((NOT VERSION-P) ARR) ((fs:NUMERIC-P ARR NIL T)) ((CDR (ASSOC ARR '((">" . :NEWEST) ("<" . :OLDEST))))) (T (FERROR :PATHNAME-PARSE-ERROR "Invalid version spec ~S in ~S" ARR STRING))))) ;;; Like MLM-FIELD, but doesn't "unquotify", or know about versions. (DEFUN MLM-SPEC (SPEC &AUX LENGTH UPCASE-FLAG CHAR) (COND ((STRINGP SPEC) (DOTIMES (I (SETQ LENGTH (ARRAY-ACTIVE-LENGTH SPEC))) (AND (> (SETQ CHAR (AREF SPEC I)) 177) ( CHAR #/TAB) (MLM-CHAR-ERROR SPEC CHAR)) (AND ( CHAR #/a) ( CHAR #/z) (SETQ UPCASE-FLAG T))) (COND ((ZEROP LENGTH) :UNSPECIFIC) ((STRING-EQUAL SPEC "*") :WILD) (UPCASE-FLAG (STRING-UPCASE SPEC)) (T SPEC))) (T SPEC))) (DEFVAR *MBFS-USE-TWENEX-SYNTAX* NIL "When non-NIL, print out LMFS pathnames in Twenex syntax.") (DEFUN MLM-NAMESTRING (HOST DEVICE DIRECTORY NAME TYPE VERSION) (WITH-OUTPUT-TO-STRING (S) (MLM-PRINT-NAME NAME TYPE VERSION S (MLM-PRINT-DIRECTORY DEVICE DIRECTORY S (MLM-PRINT-HOST HOST S))))) (DEFUN MLM-NAMESTRING-FOR-DIRECTORY (DEVICE DIRECTORY) (WITH-OUTPUT-TO-STRING (S) (MLM-PRINT-DIRECTORY DEVICE DIRECTORY S NIL))) (DEFUN MLM-NAMESTRING-FOR-EDITOR (HOST DEVICE DIRECTORY NAME TYPE VERSION) (WITH-OUTPUT-TO-STRING (S) (MLM-PRINT-NAME NAME TYPE VERSION S NIL) (SEND S :TYO #/SP) (MLM-PRINT-DIRECTORY DEVICE DIRECTORY S NIL) (SEND S :TYO #/SP) (MLM-PRINT-HOST HOST S))) (DEFUN MLM-NAMESTRING-FOR-DIRED (NAME TYPE VERSION) (WITH-OUTPUT-TO-STRING (S) (UNLESS (MEMQ NAME '(NIL :UNSPECIFIC)) (MLM-PRINT-COMPONENT NAME S)) (WHEN TYPE (SEND S :TYO #/.) (OR (EQ TYPE :UNSPECIFIC) (MLM-PRINT-COMPONENT TYPE S))) (IF (NOT *MBFS-USE-TWENEX-SYNTAX*) (FORMAT S "~15T")) (WHEN VERSION (SEND S :TYO (IF *MBFS-USE-TWENEX-SYNTAX* #/. #/#)) (UNLESS (EQ VERSION :UNSPECIFIC) (MLM-PRINT-COMPONENT VERSION S T))))) (DEFUN MLM-PRINT-HOST (HOST S) (COND ((NOT (NULL HOST)) (MLM-PRINT-COMPONENT (SEND HOST :NAME-AS-FILE-COMPUTER) S) (SEND S :TYO #/:) T) (T NIL))) (DEFUN MLM-PRINT-DIRECTORY (DEVICE DIRECTORY S SPACE) (COND ((NOT (EQ DIRECTORY :UNSPECIFIC)) (IF *MBFS-USE-TWENEX-SYNTAX* (SEND S :TYO #/<) (IF (NULL SPACE) (SETQ SPACE T) (SEND S :TYO #/SP))) (COND ((MEMQ DIRECTORY '(NIL :ROOT)) (IF (NOT *MBFS-USE-TWENEX-SYNTAX*) (SEND S :TYO #/~))) ((LISTP DIRECTORY) (DO ((D DIRECTORY (CDR D))) ((NULL D)) (MLM-PRINT-COMPONENT (CAR D) S) (IF (NOT (NULL (CDR D))) (SEND S :TYO #/.)))) (T (MLM-PRINT-COMPONENT DIRECTORY S))) (SEND S :TYO (IF *MBFS-USE-TWENEX-SYNTAX* #/> #/;)))) (COND ((AND (NOT (MEMQ DEVICE '(NIL :UNSPECIFIC))) (NOT (EQUAL DEVICE "DSK"))) (IF (NOT *MBFS-USE-TWENEX-SYNTAX*) (IF (NULL SPACE) (SETQ SPACE T) (SEND S :TYO #/SP))) (MLM-PRINT-COMPONENT DEVICE S) (SEND S :TYO #/:))) SPACE) (DEFUN MLM-PRINT-NAME (NAME TYPE VERSION S SPACE) (UNLESS (MEMQ NAME '(NIL :UNSPECIFIC)) (WHEN SPACE (IF (NOT *MBFS-USE-TWENEX-SYNTAX*) (SEND S :TYO #/SP)) (SETQ SPACE NIL)) (MLM-PRINT-COMPONENT NAME S)) (UNLESS (NULL TYPE) (WHEN SPACE (IF (NOT *MBFS-USE-TWENEX-SYNTAX*) (SEND S :TYO #/SP)) (SETQ SPACE NIL)) (SEND S :TYO #/.) (IF (NOT (EQ TYPE :UNSPECIFIC)) (MLM-PRINT-COMPONENT TYPE S))) (IF *MBFS-USE-TWENEX-SYNTAX* (UNLESS (MEMQ VERSION '(NIL :NEWEST)) (SEND S :TYO #/.) (IF (NOT (EQ VERSION :UNSPECIFIC)) (MLM-PRINT-COMPONENT VERSION S T)))) (UNLESS (NULL VERSION) (WHEN SPACE (SEND S :TYO #/SP) (SETQ SPACE NIL)) (SEND S :TYO #/#) (IF (NOT (EQ VERSION :UNSPECIFIC)) (MLM-PRINT-COMPONENT VERSION S T))) SPACE) (DEFUN MLM-PRINT-COMPONENT (SPEC STREAM &OPTIONAL VERSION-P &AUX TEM) (COND ((EQ SPEC :WILD) (SEND STREAM :TYO #/*)) ((NUMBERP SPEC) (LET ((*PRINT-BASE* 10.) (*NOPOINT T)) (SI:PRINT-FIXNUM SPEC STREAM))) (VERSION-P (COND ((SETQ TEM (CDR (ASSQ SPEC '((:NEWEST . #/>) (:OLDEST . #/<))))) (SEND STREAM :TYO TEM)) (T (FERROR "Attempt to print ~S, which is not a valid version." SPEC)))) ((STRINGP SPEC) (DOTIMES (I (ARRAY-ACTIVE-LENGTH SPEC)) (AND (MEMQ (SETQ TEM (AREF SPEC I)) '(#/SP #/TAB #/. #/: #/; #/# #// #/> #/<)) (SEND STREAM :TYO #//)) (SEND STREAM :TYO TEM))) (T (FERROR NIL "Attempt to print ~S, which is not a valid component." SPEC)))) (DEFFLAVOR MLM-PARSING-MIXIN () () (:REQUIRED-FLAVORS fs:HOST-PATHNAME)) (DEFMETHOD (MLM-PARSING-MIXIN :PARSE-NAMESTRING) (HOST-SPECIFIED NAMESTRING &OPTIONAL (START 0) END) HOST-SPECIFIED (MLM-PARSE-NAMESTRING NAMESTRING START END)) (DEFMETHOD (MLM-PARSING-MIXIN :STRING-FOR-PRINTING) () (MLM-NAMESTRING fs:HOST fs:DEVICE fs:DIRECTORY fs:NAME fs:TYPE fs:VERSION)) (DEFMETHOD (MLM-PARSING-MIXIN :STRING-FOR-HOST) () (MLM-NAMESTRING NIL fs:DEVICE fs:DIRECTORY fs:NAME fs:TYPE fs:VERSION)) (DEFMETHOD (MLM-PARSING-MIXIN :STRING-FOR-DIRED) () (MLM-NAMESTRING-FOR-DIRED fs:NAME fs:TYPE fs:VERSION)) (DEFMETHOD (MLM-PARSING-MIXIN :STRING-FOR-EDITOR) () (MLM-NAMESTRING-FOR-EDITOR fs:HOST fs:DEVICE fs:DIRECTORY fs:NAME fs:TYPE fs:VERSION)) (DEFMETHOD (MLM-PARSING-MIXIN :STRING-FOR-DIRECTORY) () (MLM-NAMESTRING-FOR-DIRECTORY fs:DEVICE fs:DIRECTORY)) (DEFMETHOD (MLM-PARSING-MIXIN :PARSE-COMPONENT-SPEC) (SPEC) (COND ((STRINGP SPEC) (MLM-SPEC SPEC)) ((MEMQ SPEC '(NIL :UNSPECIFIC :WILD)) SPEC) (T "FOO"))) (DEFMETHOD (MLM-PARSING-MIXIN :PARSE-DIRECTORY-SPEC) (SPEC) (COND ((STRINGP SPEC) (SEND SELF :PARSE-COMPONENT-SPEC SPEC)) ;; Canonicalize list of length 1 into a single string. ((AND (CONSP SPEC) (STRINGP (CAR SPEC)) (NULL (CDR SPEC))) (SEND SELF :PARSE-COMPONENT-SPEC (CAR SPEC))) ;; A list of strings is also a structured directory. ((AND (CONSP SPEC) (LOOP FOR ELT IN SPEC ALWAYS (OR (STRINGP ELT) (MEMQ ELT '(NIL :ROOT :UNSPECIFIC :WILD))))) (MAPCAR SELF (CIRCULAR-LIST :PARSE-COMPONENT-SPEC) SPEC)) ((MEMQ SPEC '(NIL :ROOT :UNSPECIFIC :WILD)) SPEC) (T (fs:PATHNAME-DIRECTORY (fs:QUIET-USER-HOMEDIR fs:HOST))))) (DEFMETHOD (MLM-PARSING-MIXIN :PARSE-VERSION-SPEC) (SPEC) (IF (OR (FIXNUMP SPEC) (MEMQ SPEC '(NIL :UNSPECIFIC :WILD :NEWEST :OLDEST))) SPEC :NEWEST)) (DEFMETHOD (MLM-PARSING-MIXIN :UNDELETABLE-P) () T) (DEFMETHOD (MLM-PARSING-MIXIN :PRIMARY-DEVICE) () "DSK") (DEFMETHOD (MLM-PARSING-MIXIN :DIRECTORY-FILE-TYPE) () "DIRECTORY") (DEFMETHOD (MLM-PARSING-MIXIN :DIRECTORY-PATHNAME-AS-FILE) DEFAULT-DIRECTORY-PATHNAME-AS-FILE) (DEFFLAVOR MLM-PATHNAME () (MLM-PARSING-MIXIN fs:HOST-PATHNAME)) (DEFPROP :MOBY-LISPM MLM-PATHNAME LISPM-PATHNAME-FLAVOR) (COMPILE-FLAVOR-METHODS MLM-PATHNAME)