Coverage report: /home/ellis/comp/core/lib/obj/uri/parse.lisp
Kind | Covered | All | % |
expression | 129 | 197 | 65.5 |
branch | 31 | 52 | 59.6 |
Key
Not instrumented
Conditionalized out
Executed
Not executed
Both branches taken
One branch taken
Neither branch taken
1
;;; obj/uri/parse.lisp --- URI Parsers
8
(eval-when (:compile-toplevel :execute :load-toplevel)
9
;; Generate the parser for URI or IRI. The only difference is the name
10
;; of the parser and for IRIs the binding of %iri-mode to T.
11
(defmacro gen-xri-parser (name irip)
13
&aux ,@(when irip '((%iri-mode t)))
16
real-host ipv6 zone-id)
17
(declare (optimize (safety 0))
20
(check-xri-string string)
22
(multiple-value-bind (i scheme userinfo host port path query fragment)
23
(state-absolute-uri string 0 end)
25
(if* (and host (consp host))
26
then (setq real-host (first host))
27
(setq ipv6 (second host))
28
(setq zone-id (third host))
29
else (setq real-host host))
31
(setq port (xval string port))
32
(setq port (parse-integer port :radix 10)))
34
(values (xval string scheme)
35
(xval string real-host)
36
(xval string userinfo)
40
;; This is only non-nil for URNs
41
(xval string fragment)
44
(xval string zone-id)))))
46
(multiple-value-bind (i scheme userinfo host port path query fragment)
47
(state-uri-reference string 0 end)
49
(if* (and host (consp host))
50
then (setq real-host (first host))
51
(setq ipv6 (second host))
52
(setq zone-id (third host))
53
else (setq real-host host))
55
(setq port (xval string port))
56
(setq port (parse-integer port :radix 10)))
58
(values (xval string scheme)
59
(xval string real-host)
60
(xval string userinfo)
64
(xval string fragment)
67
(xval string zone-id)))))
68
(uri-parse-error string "Couldn't parse uri: ~s." string))))
70
(defun uri-parse-error (string format-string &rest format-arguments)
71
(error 'uri-parse-error
73
:format-control format-string
74
:format-arguments format-arguments))
76
(gen-xri-parser parse-uri-string-rfc3986 nil)
77
(gen-xri-parser parse-iri-string-rfc3987 :iri-mode)
79
;; TODO fix string escapes
80
(defun parse-uri (thing &key (class 'uri) (escape t))
81
;; Parse THING into a URI object, an instance of CLASS.
83
;; If ESCAPE is non-nil, then decode percent-encoded characters in places
84
;; where they can legally appear, into the raw characters. The exception
85
;; to this is when those characters are reserved for the component in
86
;; which they appear, and in this case the percent-encoded character
89
(when (uri-p thing) (return-from parse-uri thing))
91
(multiple-value-bind (scheme host userinfo port path query fragment
92
pct-encoded ipv6 zone-id)
93
(parse-uri-string-rfc3986 thing)
97
;; Ordered from most common to least, and the set of known schemes
98
;; hardwired for efficiency.
99
((string-equal scheme "https") :https)
100
((string-equal scheme "http") :http)
101
((string-equal scheme "ssh") :ssh)
102
((string-equal scheme "ftp") :ftp)
103
((string-equal scheme "file") :file)
104
((string-equal scheme "urn") :urn)
105
((string-equal scheme "telnet") :telnet)
114
(load-time-value (find-package :keyword)))))))
116
(when (and scheme (eq :urn scheme))
117
(return-from parse-uri
118
(make-instance 'urn :scheme scheme :nid host :nss path
119
:query query :fragment fragment
120
:r-component userinfo)))
122
(when (and escape host)
123
(setq host (percent-decode-string host *reg-name-bitvector*)))
124
(when (and escape userinfo)
125
(setq userinfo (percent-decode-string userinfo *userinfo-bitvector*)))
127
(when (not (numberp port)) (error "port is not a number: ~s." port))
128
(when (not (plusp port))
129
(error "port is not a positive integer: ~d." port))
130
;; Use `eql' instead of `=' so that scheme's other than the small set
131
;; below are possible.
132
(when (eql port (case scheme
138
(when (= 0 (length path))
140
(when (and escape path)
141
(setq path (percent-decode-string path *pchar-bitvector*)))
142
(when (and escape query)
144
(percent-decode-string query
146
then *decode-query-bitvector-strict*
147
else *decode-query-bitvector-non-strict*))))
148
(when (and escape fragment)
150
(percent-decode-string fragment
152
then *fragment-bitvector-strict*
153
else *fragment-bitvector-non-strict*))))
155
then ;; allow the compiler to optimize the make-instance call:
166
:escaped (when escape pct-encoded))
167
else ;; do it the slow way:
176
:escaped (when escape pct-encoded)))))
178
(defmacro gen-string-to-xri (name parser class)
179
`(defun ,name (string)
180
;; Parse STRING as a xRI and either signal an error if it cannot be
181
;; parsed or return the xRI object. This function differs from
182
;; parse-uri in that the query is not decoded. The knowledge of how
183
;; to properly decode the query is outside the bounds of RFC 3986/7.
184
(multiple-value-bind (scheme host userinfo port path query fragment
185
pct-encoded ;; non-nil if any %xx in any slot
192
;; Ordered from most common to least, and the set of known schemes
193
;; hardwired for efficiency.
194
((string-equal scheme "https") :https)
195
((string-equal scheme "http") :http)
196
((string-equal scheme "ftp") :ftp)
197
((string-equal scheme "file") :file)
198
((string-equal scheme "urn") :urn)
199
((string-equal scheme "telnet") :telnet)
208
(load-time-value (find-package :keyword)))))))
210
(when (and scheme (eq :urn scheme))
212
;; NOTE: for now, we treat URNs like parse-uri, and do no
214
(make-instance 'urn :scheme scheme :nid host :nss path
215
:query query :fragment fragment
216
:r-component userinfo)))
218
(when (and pct-encoded host)
219
(setq host (percent-decode-string host *reg-name-bitvector*)))
221
(when (and pct-encoded userinfo)
222
(setq userinfo (percent-decode-string userinfo *userinfo-bitvector*)))
225
(when (not (numberp port)) (error "port is not a number: ~s." port))
226
(when (not (plusp port))
227
(error "port is not a positive integer: ~d." port))
228
;; Use `eql' instead of `=' so that scheme's other than the small set
229
;; below are possible.
230
(when (eql port (case scheme
237
(when (= 0 (length path))
239
(when (and pct-encoded path)
240
(setq path (percent-decode-string path *pchar-bitvector*)))
242
;; query is left alone
244
(when (and pct-encoded fragment)
246
(percent-decode-string fragment
248
then *fragment-bitvector-strict*
249
else *fragment-bitvector-non-strict*))))
251
(make-instance ,class
261
:escaped pct-encoded))))
263
(gen-string-to-xri string-to-uri parse-uri-string-rfc3986 'uri)
264
(gen-string-to-xri string-to-iri parse-iri-string-rfc3987 'iri)
266
(defun parse-path (path-string escape)
267
(do* ((xpath-list (uiop:split-string path-string :separator '(#\/)))
269
(let (#+mswindows temp #+mswindows c)
270
(cond ((string= "" (car xpath-list))
271
(setf (car xpath-list) :absolute))
272
(t (push :relative xpath-list)))
274
(pl (cdr path-list) (cdr pl))
276
((null pl) path-list)
278
(if* (symbolp (car pl))
279
then ;; Only happens on Windows when we see a path with a drive
280
;; letter. The lack of #+mswindows doesn't matter here.
282
elseif (cdr (setq segments
283
(if* (string= "" (car pl))
285
else (uiop:split-string (car pl) :separator '(#\:)))))
286
then ;; there is a param
288
(mapcar #'(lambda (s)
290
then (percent-decode-string s nil)
296
then (percent-decode-string (car segments) nil)
297
else (car segments))))))