1 ;;; -*- Mode: Lisp; Syntax: Common-Lisp; Package: NETLIB; -*-
2 ;;; ---------------------------------------------------------------------------
3 ;;; Title: Common Parsing Routines to Parse off Header Fields et al
4 ;;; Created: 2001-05-16
5 ;;; Author: Gilbert Baumann <gilbert@base-engineering.com>
6 ;;; License: LGPL (See file COPYING for details).
7 ;;; ---------------------------------------------------------------------------
8 ;;; (c) copyright 2001 by Gilbert Baumann
10 ;;; Permission is hereby granted, free of charge, to any person obtaining
11 ;;; a copy of this software and associated documentation files (the
12 ;;; "Software"), to deal in the Software without restriction, including
13 ;;; without limitation the rights to use, copy, modify, merge, publish,
14 ;;; distribute, sublicense, and/or sell copies of the Software, and to
15 ;;; permit persons to whom the Software is furnished to do so, subject to
16 ;;; the following conditions:
18 ;;; The above copyright notice and this permission notice shall be
19 ;;; included in all copies or substantial portions of the Software.
21 ;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 ;;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 ;;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
24 ;;; IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
25 ;;; CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
26 ;;; TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
27 ;;; SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 (defun p/if
(predicate string start end
)
35 (funcall predicate
(char string start
))
38 (defun p/expect
(c string start end
)
39 (p/if
(lambda (d) (char= c d
)) string start end
))
41 (defun p/string
(match string start end
)
42 (let ((p2 (+ start
(length match
))))
44 (string= match string
:start2 start
:end2 p2
)
47 (defun p/skip-opt-white-space
(string start end
)
50 (not (white-space-p (char string start
))))
56 (defun p/concat
(funs string start end
)
59 ;; fall thru return value
60 (values start
(reverse res
)))
61 (setq start
(p/skip-opt-white-space string start end
))
62 (multiple-value-bind (new-start semantic
) (funcall f string start end
)
66 (setq start new-start
) ))))
68 (defun p/star
(fun string start end
)
71 (setq start
(p/skip-opt-white-space string start end
))
72 (multiple-value-bind (new-start semantic
) (funcall fun string start end
)
75 (setf start new-start
))
82 (defun p/mungle
(mungler subfun string start end
)
83 (multiple-value-bind (p v
) (funcall subfun string start end
)
85 (values p
(funcall mungler v
)))))
87 (defun p/or
(funs string start end
)
89 (multiple-value-bind (new-start sem
) (funcall f string start end
)
91 (return (values new-start sem
))))))
93 (defun p/whole
(subfun string start end
)
94 (multiple-value-bind (start sem
) (funcall subfun string start end
)
96 (setf start
(p/skip-opt-white-space string start end
))
98 (values start sem
)))))
102 (defun p/separated-list
(separator sub-parser string start end
)
103 (setf start
(p/skip-opt-white-space string start end
))
104 (multiple-value-bind (p2 semantic
) (funcall sub-parser string start end
)
106 (setf p2
(p/skip-opt-white-space string p2 end
))
107 (cond ((and (< p2 end
) (char= (char string p2
) separator
))
108 (multiple-value-bind (p3 rest
) (p/separated-list separator sub-parser string
(+ p2
1) end
)
110 (values p3
(cons semantic rest
))
113 (values p2
(list semantic
)))))
117 (defun p/generic-token
(initial-predicate more-predicate string start end
)
119 (funcall initial-predicate
(char string start
))
120 (let ((j (or (position-if-not more-predicate
121 string
:start
(+ start
1) :end end
)
125 (subseq string start j
)))))
127 ;;; More special parsers in the context of HTTP
129 (defvar +token-chars
+
130 "!#$%&'*+-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz|~")
133 (concatenate 'string
"()<>@,;:\\\"/[]?={}" (string #\space
) (string #\tab
)))
135 (defun http-alpha-char-p (c)
136 (find c
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"))
138 (defun http-digit-char-p (c)
139 (find c
"0123456789"))
141 (defun http-token-char-p (c)
142 (find c
+token-chars
+))
144 (defun p/token
(string start end
)
145 (p/generic-token
#'http-token-char-p
#'http-token-char-p string start end
))
147 (defun p/quoted-string
(string start end
)
150 (char= (char string p
) #\")
154 (with-output-to-string (bag)
157 (return-from p
/quoted-string nil
))
158 (let ((c (char string p
)))
165 (write-char (char string p
) bag
)
166 (return-from p
/quoted-string nil
)))
168 (write-char c bag
))))
172 ;;;; HTTP "Link:" header fields
174 (defun p/http-link-extension
(string start end
)
175 ;; link-extension = token [ "=" ( token | quoted-string ) ]
176 (multiple-value-bind (p2 sem
) (p/concat
(list #'p
/token
177 (curry #'p
/expect
#\
=)
178 (curry #'p
/or
(list #'p
/token
181 (and p2
(values p2
(list (first sem
) (third sem
))))))
183 (defun p/URI-char-p
(c)
184 (find c
"!#$%&'()*+,-./0123456789:;=?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"))
186 (defun p/URI-string
(string start end
)
187 (p/generic-token
#'p
/URI-char-p
#'p
/URI-char-p string start end
))
189 (defun parse-http-link-field (string &optional
(start 0) (end (length string
)))
190 (multiple-value-bind (start links
)
192 (curry #'p
/separated-list
#\
,
195 (cons (second x
) (fourth x
)))
196 (curry #'p
/concat
(list (curry #'p
/expect
#\
<)
198 (curry #'p
/expect
#\
>)
203 (list (curry #'p
/expect
#\
;)
204 #'p
/http-link-extension
))))))))
207 (mapcar (lambda (link)
208 ;; Since link header fields work like <LINK> elements,
209 ;; we build psuedo elements
210 (destructuring-bind (href &rest attributes
) link
212 ,@(mapcan (lambda (attribute)
213 (destructuring-bind (name value
) attribute
214 (list (intern (string-upcase name
) :keyword
)
221 (defun apply-p (pfun string
&rest args
)
222 (apply pfun
(append args
(list string
0 (length string
)))))