Changeset 314


Ignore:
Timestamp:
Jan 17, 2004, 7:46:12 PM (21 years ago)
Author:
Gary Byers
Message:

Quote dots in namestring components. Catch a few bogus cases, like
(:ABSOLUTE :UP) in directory.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/ccl/level-1/l1-files.lisp

    r299 r314  
    181181
    182182
    183 #|
    184 (defstruct (pattern
    185             (:constructor make-pattern (pieces)))
    186   (pieces nil :type list))
    187 
    188 (defmethod make-load-form ((p pattern))
    189   (make-load-form-saving-slots p))
    190 
    191 (defmethod print-object ((p pattern) stream)
    192   (print-unreadable-object (p stream :type t)
    193     (let* ((pieces (pattern-pieces p)))
    194       (if *print-pretty*
    195         (let ((*print-escape* t))
    196           (pprint-fill stream pieces nil))
    197         (prin1 pieces stream)))))
    198 
    199 (defvar *ignore-wildcards* nil)
    200 
    201 
    202 (defun remove-backslashes (namestr start end)
    203   "Remove and occurences of \\ from the string because we've already
    204    checked for whatever they may have been backslashed."
    205   (declare (type simple-base-string namestr)
    206            (type index start end))
    207   (let* ((result (make-string (- end start) :element-type 'base-char))
    208          (dst 0)
    209          (quoted nil))
    210     (do ((src start (1+ src)))
    211         ((= src end))
    212       (cond (quoted
    213              (setf (schar result dst) (schar namestr src))
    214              (setf quoted nil)
    215              (incf dst))
    216             (t
    217              (let ((char (schar namestr src)))
    218                (cond ((char= char #\\)
    219                       (setq quoted t))
    220                      (t
    221                       (setf (schar result dst) char)
    222                       (incf dst)))))))
    223     (when quoted
    224       (error 'namestring-parse-error
    225              :complaint "Backslash in bad place."
    226              :namestring namestr
    227              :offset (1- end)))
    228     (shrink-vector result dst)))
    229 
    230 (defun maybe-make-pattern (namestr start end)
    231   (declare (type simple-base-string namestr)
    232            (type index start end))
    233   (if *ignore-wildcards*
    234       (subseq namestr start end)
    235       (let* ((pattern ()))
    236         (let* ((quoted nil)
    237               (any-quotes nil)
    238               (last-regular-char nil)
    239               (index start))
    240           (flet ((flush-pending-regulars ()
    241                    (when last-regular-char
    242                      (push (if any-quotes
    243                                (remove-backslashes namestr
    244                                                    last-regular-char
    245                                                    index)
    246                                (subseq namestr last-regular-char index))
    247                            pattern)
    248                      (setf any-quotes nil)
    249                      (setf last-regular-char nil))))
    250             (loop
    251               (when (>= index end)
    252                 (return))
    253               (let ((char (schar namestr index)))
    254                 (cond (quoted
    255                        (incf index)
    256                        (setf quoted nil))
    257                       ((char= char #\\)
    258                        (setf quoted t)
    259                        (setf any-quotes t)
    260                        (unless last-regular-char
    261                          (setf last-regular-char index))
    262                        (incf index))
    263                       ((char= char #\?)
    264                        (flush-pending-regulars)
    265                        (push :single-char-wild pattern)
    266                        (incf index))
    267                       ((char= char #\*)
    268                        (flush-pending-regulars)
    269                        (push :multi-char-wild pattern)
    270                        (incf index))
    271                       ((char= char #\[)
    272                        (flush-pending-regulars)
    273                        (let ((close-bracket
    274                               (position #\] namestr :start index :end end)))
    275                          (unless close-bracket
    276                            (error 'namestring-parse-error
    277                                   :complaint "``['' with no corresponding ``]''"
    278                                   :namestring namestr
    279                                   :offset index))
    280                          (push (list :character-set
    281                                         (subseq namestr
    282                                                 (1+ index)
    283                                                 close-bracket))
    284                           pattern)
    285                          (setf index (1+ close-bracket))))
    286                       (t
    287                        (unless last-regular-char
    288                          (setf last-regular-char index))
    289                        (incf index)))))
    290             (flush-pending-regulars)))
    291         (cond ((null (setq pattern (nreverse pattern)))
    292                "")
    293               ((null (cdr pattern))
    294                (let ((piece (first pattern)))
    295                  (typecase piece
    296                    ((member :multi-char-wild) :wild)
    297                    (simple-string piece)
    298                    (t
    299                     (make-pattern pattern)))))
    300               (t
    301                (make-pattern pattern))))))
    302 
    303 (defun extract-name-type-and-version (namestr start end)
    304   (declare (type simple-base-string namestr)
    305            (type index start end))
    306   (let* ((last-dot (position #\. namestr :start (1+ start) :end end
    307                              :from-end t))
    308          (second-to-last-dot (and last-dot
    309                                   (position #\. namestr :start (1+ start)
    310                                             :end last-dot :from-end t)))
    311          (version nil))
    312     ;; If there is a second-to-last dot, check to see if there is a valid
    313     ;; version after the last dot.
    314     (when second-to-last-dot
    315       (cond ((and (= (+ last-dot 2) end)
    316                   (char= (schar namestr (1+ last-dot)) #\*))
    317              (setf version :wild))
    318             ((and (< (1+ last-dot) end)
    319                   (do ((index (1+ last-dot) (1+ index)))
    320                       ((= index end) t)
    321                     (unless (char<= #\0 (schar namestr index) #\9)
    322                       (return nil))))
    323              (setf version
    324                    (parse-integer namestr :start (1+ last-dot) :end end)))
    325             (t
    326              (setf second-to-last-dot nil))))
    327     (cond (second-to-last-dot
    328            (values (maybe-make-pattern namestr start second-to-last-dot)
    329                    (maybe-make-pattern namestr
    330                                        (1+ second-to-last-dot)
    331                                        last-dot)
    332                    version))
    333           (last-dot
    334            (values (maybe-make-pattern namestr start last-dot)
    335                    (maybe-make-pattern namestr (1+ last-dot) end)
    336                    version))
    337           (t
    338            (values (maybe-make-pattern namestr start end)
    339                    nil
    340                    version)))))
    341 
    342 ;;; Take a string and return a list of cons cells that mark the char
    343 ;;; separated subseq. The first value t if absolute directories location.
    344 ;;;
    345 (defun split-at-slashes (namestr start end)
    346   (declare (type simple-base-string namestr)
    347            (type index start end))
    348   (let ((absolute (and (/= start end)
    349                        (char= (schar namestr start) #\/))))
    350     (when absolute
    351       (incf start))
    352     ;; Next, split the remainder into slash seperated chunks.
    353     (let* ((pieces ()))
    354       (loop
    355         (let ((slash (position #\/ namestr :start start :end end)))
    356           (push (cons start (or slash end)) pieces)
    357           (unless slash
    358             (return))
    359           (setf start (1+ slash))))
    360       (values absolute (nreverse pieces)))))
    361 
    362 (defun parse-unix-namestring (namestr start end)
    363   (declare (type simple-base-string namestr)
    364            (type index start end))
    365   (multiple-value-bind
    366       (absolute pieces)
    367       (split-at-slashes namestr start end)
    368     (multiple-value-bind
    369           (name type)
    370         (let* ((tail (car (last pieces)))
    371                (tail-start (car tail))
    372                (tail-end (cdr tail)))
    373           (unless (= tail-start tail-end)
    374             (setf pieces (butlast pieces))
    375             (extract-name-type-and-version namestr tail-start tail-end)))
    376       ;; Now we have everything we want.  So return it.
    377       (values nil ; no host for unix namestrings.
    378               (let* ((dirs ()))
    379                 (dolist (piece pieces)
    380                   (let* ((piece-start (car piece))
    381                          (piece-end (cdr piece)))
    382                     (unless (= piece-start piece-end)
    383                       (cond ((string= namestr ".." :start1 piece-start
    384                                       :end1 piece-end)
    385                              (push :up dirs))
    386                             ((string= namestr "**" :start1 piece-start
    387                                       :end1 piece-end)
    388                              (push :wild-inferiors dirs))
    389                             (t
    390                              (push (maybe-make-pattern namestr
    391                                                        piece-start
    392                                                        piece-end)
    393                               dirs))))))
    394                 (setq dirs (nreverse dirs))
    395                 (cond (absolute
    396                        (cons :absolute dirs))
    397                       (dirs
    398                        (cons :relative dirs))
    399                       (t
    400                        nil)))
    401               name
    402               type
    403               nil))))
    404 
    405 (defun unparse-unix-piece (thing)
    406   (etypecase thing
    407     ((member :wild) "*")
    408     (simple-string
    409      (let* ((srclen (length thing))
    410             (dstlen srclen))
    411        (dotimes (i srclen)
    412          (case (schar thing i)
    413            ((#\* #\? #\[)
    414             (incf dstlen))))
    415        (let ((result (make-string dstlen :element-type 'base-char))
    416              (dst 0))
    417          (dotimes (src srclen)
    418            (let ((char (schar thing src)))
    419              (case char
    420                ((#\* #\? #\[)
    421                 (setf (schar result dst) #\\)
    422                 (incf dst)))
    423              (setf (schar result dst) char)
    424              (incf dst)))
    425          result)))
    426     (pattern
    427      (let* ((strings ()))
    428        (dolist (piece (pattern-pieces thing))
    429          (etypecase piece
    430            (simple-string
    431             (push piece strings))
    432            (symbol
    433             (ecase piece
    434               (:multi-char-wild
    435                (push "*" strings))
    436               (:single-char-wild
    437                (push "?" strings))))
    438            (cons
    439             (case (car piece)
    440               (:character-set
    441                (push "[" strings)
    442                (push (cdr piece) strings)
    443                (push "]" strings))
    444               (t
    445                (error "Invalid pattern piece: ~S" piece))))))
    446        (apply #'concatenate
    447               'simple-string
    448               (nreverse strings))))))
    449 
    450 (defun unparse-unix-directory-list (directory)
    451   (declare (type list directory))
    452   (let* ((pieces ()))
    453     (when directory
    454       (ecase (pop directory)
    455         (:absolute
    456          (push "/" pieces))
    457         (:relative
    458          ;; Nothing special.
    459          ))
    460       (dolist (dir directory)
    461         (typecase dir
    462           ((member :up)
    463            (push "../" pieces))
    464           ((member :back)
    465            (error ":BACK cannot be represented in namestrings."))
    466           ((member :wild-inferiors)
    467            (push "**/" pieces))
    468           ((or simple-string pattern)
    469            (push (unparse-unix-piece dir) pieces)
    470            (push "/" pieces))
    471           (t
    472            (error "Invalid directory component: ~S" dir)))))
    473     (apply #'concatenate 'simple-string (nreverse pieces))))
    474 
    475 (defun %pathname-host (pathname)
    476   (if (logical-pathname-p pathname)
    477       (%logical-pathname-host pathname)
    478       :unspecific))
    479 
    480 (defun %pathname-version (pathname)
    481   (if (logical-pathname-p pathname)
    482       (%logical-pathname-version pathname)
    483       :newest))
    484 
    485 (defun unparse-unix-file (pathname)
    486   (declare (type pathname pathname))
    487   (let* ((strings ()))
    488     (let* ((name (%pathname-name pathname))
    489            (type (%pathname-type pathname))
    490            (type-supplied (not (or (null type) (eq type :unspecific))))
    491            (version (%pathname-version pathname))
    492            (version-supplied (not (or (null version) (eq version :newest)))))
    493       (when name
    494         (push (unparse-unix-piece name) strings))
    495       (when type-supplied
    496         (push "." strings)
    497         (push (unparse-unix-piece type) strings))
    498       (when version-supplied
    499         (unless type-supplied
    500           (error "Cannot specify the version without a type: ~S" pathname))
    501         (push (if (eq version :wild)
    502              ".*"
    503              (format nil ".~D" version))
    504          strings)))
    505     (apply #'concatenate 'simple-string (nreverse strings))))
    506 
    507 (defun unparse-unix-namestring (pathname)
    508   (declare (type pathname pathname))
    509   (concatenate 'simple-string
    510                (unparse-unix-directory-list (%pathname-directory pathname))
    511                (unparse-unix-file pathname)))
    512 
    513 (defun unparse-logical-piece (thing)
    514   (etypecase thing
    515     (simple-string thing)
    516     (pattern
    517      (let* ((strings ()))
    518        (dolist (piece (pattern-pieces thing))
    519          (etypecase piece
    520            (simple-string (push piece strings))
    521            (keyword
    522             (cond ((eq piece :wild-inferiors)
    523                    (push "**" strings))
    524                   ((eq piece :multi-char-wild)
    525                    (push "*" strings))
    526                   (t (error "Invalid keyword: ~S" piece))))))
    527        (apply #'concatenate 'simple-string (nreverse strings))))))
    528 
    529 (defun unparse-logical-directory (pathname)
    530   (declare (type pathname pathname))
    531   (let* ((pieces ()))
    532     (let ((directory (%pathname-directory pathname)))
    533       (when directory
    534         (ecase (pop directory)
    535           (:absolute)    ;; Nothing special.
    536           (:relative (push ";" pieces)))
    537         (dolist (dir directory)
    538           (cond ((or (stringp dir) (pattern-p dir))
    539                  (push (unparse-logical-piece dir) pieces)
    540                  (push ";" pieces))
    541                 ((eq dir :wild)
    542                  (push "*;" pieces))
    543                 ((eq dir :wild-inferiors)
    544                  (push "**;" pieces))
    545                 (t
    546                  (error "Invalid directory component: ~S" dir))))))
    547     (apply #'concatenate 'simple-string (nreverse pieces))))
    548 
    549 (defun unparse-logical-namestring (pathname)
    550   (declare (type logical-pathname pathname))
    551   (concatenate 'simple-string
    552                (%pathname-host pathname) ":"
    553                (unparse-logical-directory pathname)
    554                (unparse-unix-file pathname)))
    555 
    556 (defun maybe-make-logical-pattern (namestring chunks)
    557   (let* ((chunk (caar chunks)))
    558     (let* ((pattern ()))
    559       (let* ((last-pos 0)
    560              (len (length chunk)))
    561         (declare (fixnum last-pos))
    562         (loop
    563           (when (= last-pos len) (return))
    564           (let ((pos (or (position #\* chunk :start last-pos) len)))
    565             (if (= pos last-pos)
    566                 (when pattern
    567                   (error 'namestring-parse-error
    568                          :complaint "Double asterisk inside of logical ~
    569                                      word: ~S"
    570                          :arguments (list chunk)
    571                          :namestring namestring
    572                          :offset (+ (cdar chunks) pos)))
    573                 (push (subseq chunk last-pos pos) pattern))
    574             (if (= pos len)
    575                 (return)
    576                 (push :multi-char-wild pattern))
    577             (setq last-pos (1+ pos)))))
    578       (setq pattern (nreverse pattern))
    579         (if (cdr pattern)
    580             (make-pattern pattern)
    581             (let ((x (car pattern)))
    582               (if (eq x :multi-char-wild)
    583                   :wild
    584                   x))))))
    585 
    586 (defun logical-chunkify (namestr start end)
    587   (let* ((chunks nil))
    588     (do ((i start (1+ i))
    589          (prev 0))
    590         ((= i end)
    591          (when (> end prev)
    592             (push (cons (subseq namestr prev end) prev) chunks)))
    593       (let ((ch (schar namestr i)))
    594         (unless (or (alpha-char-p ch) (digit-char-p ch)
    595                     (member ch '(#\- #\*)))
    596           (when (> i prev)
    597             (push (cons (subseq namestr prev i) prev) chunks))
    598           (setq prev (1+ i))
    599           (unless (member ch '(#\; #\: #\.))
    600             (error 'namestring-parse-error
    601                    :complaint "Illegal character for logical pathname:~%  ~S"
    602                    :arguments (list ch)
    603                    :namestring namestr
    604                    :offset i))
    605           (push (cons ch i) chunks))))
    606     (nreverse chunks)))
    607 
    608 (defun find-logical-host (hostname &optional (errorp t))
    609   (let* ((found (%str-assoc hostname %logical-host-translations%)))
    610     (unless (or found (not errorp))
    611       (error "Logical host not yet defined: ~S" hostname))
    612     (car found)))
    613 
    614 (defun parse-logical-namestring (namestr start end)
    615   (declare (type simple-base-string namestr)
    616            (type index start end))
    617   (let* ((directory ()))
    618     (let* ((host nil)
    619            (name nil)
    620            (type nil)
    621            (version nil))
    622       (labels ((expecting (what chunks)
    623                  (unless (and chunks (simple-string-p (caar chunks)))
    624                    (error 'namestring-parse-error
    625                           :complaint "Expecting ~A, got ~:[nothing~;~S~] ."
    626                           :arguments (list what (caar chunks) (caar chunks))
    627                           :namestring namestr
    628                           :offset (if chunks (cdar chunks) end)))
    629                  (caar chunks))
    630                (parse-host (chunks)
    631                  (case (caadr chunks)
    632                    (#\:
    633                     (setq host
    634                           (find-logical-host (expecting "a host name" chunks)))
    635                     (parse-relative (cddr chunks)))
    636                    (t
    637                     (parse-relative chunks))))
    638                (parse-relative (chunks)
    639                  (case (caar chunks)
    640                    (#\;
    641                     (push :relative directory)
    642                     (parse-directory (cdr chunks)))
    643                    (t
    644                     (push :absolute directory) ; Assumption! Maybe revoked later.
    645                     (parse-directory chunks))))
    646                (parse-directory (chunks)
    647                  (case (caadr chunks)
    648                    (#\;
    649                     (push
    650                      (let ((res (expecting "a directory name" chunks)))
    651                        (cond ((string= res "..") :up)
    652                              ((string= res "**") :wild-inferiors)
    653                              (t
    654                               (maybe-make-logical-pattern namestr chunks))))
    655                      directory)
    656                     (parse-directory (cddr chunks)))
    657                    (t
    658                     (parse-name chunks))))
    659                (parse-name (chunks)
    660                  (when chunks
    661                    (expecting "a file name" chunks)
    662                    (setq name (maybe-make-logical-pattern namestr chunks))
    663                    (expecting-dot (cdr chunks))))
    664                (expecting-dot (chunks)
    665                  (when chunks
    666                    (unless (eql (caar chunks) #\.)
    667                      (error 'namestring-parse-error
    668                             :complaint "Expecting a dot, got ~S."
    669                             :arguments (list (caar chunks))
    670                             :namestring namestr
    671                             :offset (cdar chunks)))
    672                    (if type
    673                      (parse-version (cdr chunks))
    674                      (parse-type (cdr chunks)))))
    675                (parse-type (chunks)
    676                  (expecting "a file type" chunks)
    677                  (setq type (maybe-make-logical-pattern namestr chunks))
    678                  (expecting-dot (cdr chunks)))
    679                (parse-version (chunks)
    680                  (let ((str (expecting "a positive integer, * or NEWEST"
    681                                        chunks)))
    682                    (cond
    683                      ((string= str "*") (setq version :wild))
    684                      ((string= str "NEWEST") (setq version :newest))
    685                      (t
    686                       (multiple-value-bind
    687                           (res pos)
    688                           (parse-integer str :junk-allowed t)
    689                         (unless (and res (plusp res))
    690                           (error 'namestring-parse-error
    691                                  :complaint "Expected a positive integer, ~
    692     got ~S"
    693                                  :arguments (list str)
    694                                  :namestring namestr
    695                                  :offset (+ pos (cdar chunks))))
    696                         (setq version res)))))
    697                  (when (cdr chunks)
    698                    (error 'namestring-parse-error
    699                           :complaint "Extra stuff after end of file name."
    700                           :namestring namestr
    701                           :offset (cdadr chunks)))))
    702         (parse-host (logical-chunkify namestr start end)))
    703       (setq directory (nreverse directory))
    704       (values host
    705               (and (not (equal directory '(:absolute)))directory)
    706               name type version))))
    707 
    708 (defun extract-logical-host-prefix (namestr start end)
    709   (declare (type simple-base-string namestr)
    710            (type index start end)
    711            (values (or logical-host null)))
    712   (let ((colon-pos (position #\: namestr :start start :end end)))
    713     (if colon-pos
    714         (%str-assoc (subseq namestr start colon-pos)
    715                     %logical-host-translations%)
    716         nil)))
    717 
    718 (defun %parse-namestring (namestr host defaults start end junk-allowed)
    719   (declare  (type string namestr)
    720            (type index start) (type (or index null) end))
    721   (if junk-allowed
    722       (handler-case
    723           (%parse-namestring namestr host defaults start end nil)
    724         (namestring-parse-error (condition)
    725           (values nil (namestring-parse-error-offset condition))))
    726       (let* ((end (or end (length namestr)))
    727              (logical-host (car (extract-logical-host-prefix namestr start end)))
    728              (parse-host (or host logical-host (pathname-host defaults))))
    729         (unless parse-host
    730           (error "When Host arg is not supplied, Defaults arg must ~
    731                   have a non-null PATHNAME-HOST."))
    732         (if (eq host :unspecific)
    733             (setq host nil))
    734         (setq host (or host logical-host))
    735         (multiple-value-bind
    736             (new-host directory name type version)
    737             (if host
    738                 (parse-logical-namestring namestr start end)
    739                 (parse-unix-namestring namestr start end))
    740           (when (and host new-host (not (equal new-host host)))
    741             (error "Host in namestring: ~S~@
    742                     does not match explicit host argument: ~S"
    743                    (subseq namestr start end)
    744                    host))
    745           (cons-pathname directory name type host version)))))
    746 
    747 
    748 (defun parse-namestring (thing
    749                          &optional host (defaults *default-pathname-defaults*)
    750                          &key (start 0) end junk-allowed)
    751   (declare (type pathname defaults)
    752            (type index start)
    753            (type (or index null) end)
    754            (type (or t null) junk-allowed)
    755            (values (or null pathname) (or null index)))
    756     (typecase thing
    757       (simple-string
    758        (%parse-namestring thing host defaults start end junk-allowed))
    759       (string
    760        (%parse-namestring (coerce thing 'simple-string)
    761                           host defaults start end junk-allowed))
    762       (pathname
    763        (let ((host (if host host (%pathname-host defaults))))
    764          (unless (equal host (%pathname-host thing))
    765            (error "Hosts do not match: ~S and ~S."
    766                   host (%pathname-host thing))))
    767        (values thing start))
    768       (stream
    769        (let ((name (stream-pathname thing)))
    770          (unless name
    771            (error "Can't figure out the file associated with stream:~%  ~S"
    772                   thing))
    773          name))))
    774 
    775 (defun namestring (path &key)
    776   (typecase path
    777     (logical-pathname (unparse-logical-namestring path))
    778     (pathname (unparse-unix-namestring path))
    779     (t (namestring (pathname path)))))
    780 
    781 |#
    782 
    783183;; The following assumptions are deeply embedded in all our pathname code:
    784184;; (1) Non-logical pathname host is always :unspecific.
     
    842242  dirlist)
    843243
    844 ; ? is (:absolute "a" :up "b") = (:absolute "b") - seems reasonable
    845244; destructively mungs dir
    846245(defun remove-up (dir)
     
    938337              ((nil :unspecific) "")
    939338              (:wild "*")
    940               (t name))
     339              (t (%path-std-quotes name nil ".")))
    941340            (if (or type version)
    942341              (%str-cat (case type
    943342                          ((nil) ".")
    944343                          (:wild ".*")
    945                           (t (%str-cat "." type)))
     344                          (t (%str-cat "." (%path-std-quotes type nil "."))))
    946345                        (case version
    947346                          ((nil) "")
     
    1098497               (unless directory-p '(:absolute)))
    1099498           name type host version)))
     499  (when (and (eq (car directory) :absolute)
     500             (member (cadr directory) '(:up :back)))
     501    (error 'simple-file-error :pathname path :error-type "Second element of absolute directory component in ~s is ~s" :format-arguments (list (cadr directory))))
     502  (let* ((after-wif (cadr (member :wild-inferiors directory))))
     503    (when (member after-wif '(:up :back))
     504          (error 'simple-file-error :pathname path :error-type "Directory component in ~s contains :WILD-INFERIORS followed by ~s" :format-arguments (list after-wif))))
     505         
    1100506  (when (and case (neq case :local))
    1101507    (setf (%pathname-directory path) (%reverse-component-case (%pathname-directory path) case)
Note: See TracChangeset for help on using the changeset viewer.