Mercurial > core / lisp/lib/obj/uri/path.lisp
changeset 698: |
96958d3eb5b0 |
parent: |
78ef6145e272
|
author: |
Richard Westhaver <ellis@rwest.io> |
date: |
Fri, 04 Oct 2024 22:04:59 -0400 |
permissions: |
-rw-r--r-- |
description: |
fixes |
1 ;;; obj/uri/path.lisp --- URI Path merging functions 7 ;; merging and unmerging 8 (defmethod merge-uris ((uri string) (base string) &optional place) 9 (merge-uris (parse-uri uri) (parse-uri base) place)) 11 (defmethod merge-uris ((uri uri) (base string) &optional place) 12 (merge-uris uri (parse-uri base) place)) 14 (defmethod merge-uris ((uri string) (base uri) &optional place) 15 (merge-uris (parse-uri uri) base place)) 17 (defmethod merge-uris ((uri uri) (base uri) &optional place) 18 ;; When PLACE is nil, this function returns a new URI. 19 ;; When PLACE is non-nil, it is return. 21 (when (and (null (uri-path uri)) 22 (null (uri-scheme uri)) 24 (null (uri-userinfo uri)) 26 (null (uri-query uri))) 27 (return-from merge-uris 28 (let ((new (copy-uri base :place place))) 30 (setf (uri-query new) (uri-query uri))) 31 (when (uri-fragment uri) 32 (setf (uri-fragment new) (uri-fragment uri))) 35 (setq uri (copy-uri uri :place place)) 37 (when (uri-scheme uri) (go :done)) 39 (setf (uri-scheme uri) (uri-scheme base)) 41 ;; if URI has a host, we're done 42 (when (uri-host uri) (go :done)) 48 (setf (uri-userinfo uri) (uri-userinfo base)) 49 (setf (uri-port uri) (uri-port base)) 51 (let ((p (uri-parsed-path uri))) 53 (setf (uri-path uri) (uri-path base)) 56 (when (and p (eq :absolute (car p))) 57 (if* (equal '(:absolute "") p) 58 then ;; Canonicalize the way parsing does: 59 (setf (uri-path uri) nil) 60 elseif (eq :absolute (first p)) 61 then ;; this also sets uri-path 62 (multiple-value-bind (new changed) 63 (canonicalize-path-list p) 65 (setf (uri-parsed-path uri) new)))) 69 (or (uri-parsed-path base) 70 ;; needed because we canonicalize away a path of just `/': 72 (path (uri-parsed-path uri)) 74 (when (not (eq :absolute (car base-path))) 75 (error "Cannot merge ~a and ~a, since the latter is not absolute." 79 (append (butlast base-path) 80 (if* path then (cdr path) else '("")))) 82 (let ((last (last new-path-list))) 83 (if* (atom (car last)) 84 then (when (string= "." (car last)) 86 else (when (string= "." (caar last)) 87 (setf (caar last) "")))) 89 (delete "." new-path-list :test #'(lambda (a b) 94 (let ((npl (cdr new-path-list)) 97 (string= ".." (let ((l (car (last npl)))) 104 :test #'(lambda (a b) 109 (when (null index) (return)) 112 then ;; rfe11852: RFC 3986, in section 5.4.2 (Abnormal 113 ;; Examples) says parsers; must be careful in handling 114 ;; cases where there are more ".." segments in a 115 ;; relative-path reference than there are in the base 116 ;; URI's path. The examples, between the two RFC's were 117 ;; changed to show the additional, leading ..'s to be 118 ;; removed. So, we'll do that now. 121 then (setq npl (cddr npl)) 123 (dotimes (x (- index 2)) (setq tmp (cdr tmp))) 124 (setf (cdr tmp) (cdddr tmp)))) 125 (setf (cdr new-path-list) npl) 126 (when fix-tail (setq new-path-list (nconc new-path-list '(""))))) 128 (when (eq :absolute (first new-path-list)) 129 (multiple-value-bind (new changed) 130 (canonicalize-path-list new-path-list) 131 (when changed (setq new-path-list new)))) 133 ;; Also sets uri-path: 134 (setf (uri-parsed-path uri) new-path-list)) 137 (return-from merge-uris uri))) 139 (defun canonicalize-path-list (path-list &aux changed) 140 ;; Return two values: new version of PATH-LIST and an indicator if it was 141 ;; changed. We are only called when (car path-list) is :absolute. 142 (loop while (or (equal "." (second path-list)) 143 (equal ".." (second path-list))) 144 do (setf (cdr path-list) (cddr path-list)) 146 (values path-list changed)) 148 (defmethod merge-uris ((urn urn) (base urn) &optional place) 150 then (setf (urn-nid place) (urn-nid urn)) 151 (setf (urn-nss place) (urn-nss urn)) 155 (defmethod merge-uris ((urn urn) (base uri) &optional place) 157 then (setf (urn-nid place) (urn-nid urn)) 158 (setf (urn-nss place) (urn-nss urn)) 162 (defmethod merge-uris ((uri uri) (base urn) &optional place) 163 (copy-uri uri :place place)) 165 (defmethod enough-uri ((uri string) (base string) &optional place) 166 (enough-uri (parse-uri uri) (parse-uri base) place)) 168 (defmethod enough-uri ((uri uri) (base string) &optional place) 169 (enough-uri uri (parse-uri base) place)) 171 (defmethod enough-uri ((uri string) (base uri) &optional place) 172 (enough-uri (parse-uri uri) base place)) 174 (defmethod enough-uri ((uri uri) (base uri) &optional place) 175 ;; Like ENOUGH-PATHNAME, but for URIs. 176 (let ((new-scheme nil) 182 (new-parsed-path nil)) 184 ;; If the scheme and authority are not the same, then return URI. 185 (when (or (and (uri-scheme uri) 186 (not (equalp (uri-scheme uri) (uri-scheme base)))) 187 ;; We don't use uri-authority, because it conses a lot. 189 (not (equalp (uri-host uri) (uri-host base)))) 190 (not (equalp (uri-userinfo uri) (uri-userinfo base))) 191 (not (equalp (uri-port uri) (uri-port base)))) 192 (return-from enough-uri uri)) 194 ;; For this group, if the slot is nil in URI, then the return value is 195 ;; copied from from BASE: 196 (when (null (uri-scheme uri)) (setq new-scheme (uri-scheme base))) 197 (when (null (uri-host uri)) 198 ;; These are copied as a unit: 199 (setq new-host (%uri-host base)) 200 (setq new-ipv6 (%uri-ipv6 base)) 201 (setq new-zone-id (%uri-zone-id base))) 202 (when (null (uri-userinfo uri)) (setq new-userinfo (uri-userinfo base))) 203 (when (null (uri-port uri)) (setq new-port (uri-port base))) 205 ;; Now, for the hard one, path. 206 ;; We essentially do here what enough-namestring does. 207 (do* ((base-path (uri-parsed-path base)) 208 (path (uri-parsed-path uri)) 209 (bp base-path (cdr bp)) 211 ((or (null bp) (null p)) 212 ;; If p is nil, that means we have something like 213 ;; (enough-uri "/foo/bar" "/foo/bar/baz.htm"), so 214 ;; new-parsed-path will be nil. 216 (setq new-parsed-path (copy-list p)) 217 (when (not (symbolp (car new-parsed-path))) 218 (push :relative new-parsed-path)))) 219 (if* (equal (car bp) (car p)) 221 else (setq new-parsed-path (copy-list p)) 222 (when (not (symbolp (car new-parsed-path))) 223 (push :relative new-parsed-path)) 227 (or (when new-parsed-path 228 (render-parsed-path new-parsed-path 229 ;; don't know, so have to assume: 231 ;; can't have a completely empty uri! 233 (copy-uri nil :class (class-of uri) :place place 234 ;;; these come from base if the original slot was nil 239 :userinfo new-userinfo 242 :parsed-path new-parsed-path 243 ;;; never from base... why? is this documented? 244 :query (uri-query uri) 245 :fragment (uri-fragment uri) 246 :plist (copy-list (uri-plist uri)))))) 248 (defmethod enough-uri ((urn urn) (base urn) &optional place) 250 then (setf (urn-nid place) (urn-nid urn)) 251 (setf (urn-nss place) (urn-nss urn)) 255 (defmethod enough-uri ((urn urn) (base uri) &optional place) 256 (declare (ignore place)) 257 (error "enough-uri of a URN (~a) and URI (~a)." urn base)) 259 (defmethod enough-uri ((uri uri) (base urn) &optional place) 260 (declare (ignore place)) 261 (error "enough-uri of a URI (~a) and URN (~a)." uri base)) 263 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 265 (defun uri-to-pathname (uri) 266 ;; On Windows, turn file:///d:/foo/bar.cl into #p"d:/foo/bar.cl" 267 ;; On UNIX, turn file:///foo/bar.cl into #p"/foo/bar.cl" 268 (when (not (eq :file (uri-scheme uri))) 269 (error "Only file: URIs can be converted to pathnames: ~s." uri)) 270 (when (null (uri-path uri)) (error "URI has no path: ~s." uri)) 272 (percent-decode-string 276 (defun pathname-to-uri (pathname) 277 (when (not (uiop:absolute-pathname-p pathname t)) 278 (error "A relative pathname cannot be converted to a URI: ~s." pathname)) 280 (let ((s (percent-encode-string 281 #+mswindows (substitute #\/ #\\ (namestring pathname)) 282 #-mswindows (namestring pathname) 283 *pchar/-bitvector*))) 284 #-mswindows (format nil "file://~a" s) 285 #+mswindows (if* (pathname-device pathname) 286 then (format nil "file:///~a" s) 287 else (format nil "file://~a" s)))))