(module cgi-unit (lib "a-unit.ss") (require (lib "etc.ss") "cgi-sig.ss") (import) (export cgi^) ;; type bindings = list ((symbol . string)) ;; -------------------------------------------------------------------- ;; Exceptions: (define-struct cgi-error ()) ;; chars : list (char) ;; -- gives the suffix which is invalid, not including the `%' (define-struct (incomplete-%-suffix cgi-error) (chars)) ;; char : char ;; -- an invalid character in a hex string (define-struct (invalid-%-suffix cgi-error) (char)) ;; -------------------------------------------------------------------- ;; query-chars->string : list (char) -> string ;; -- The input is the characters post-processed as per Web specs, which ;; is as follows: ;; spaces are turned into "+"es and lots of things are turned into %XX, where ;; XX are hex digits, eg, %E7 for ~. The output is a regular Scheme string ;; with all the characters converted back. (define (query-chars->string chars) (list->string (let loop ([chars chars]) (if (null? chars) null (let ([first (car chars)] [rest (cdr chars)]) (let-values ([(this rest) (cond [(char=? first #\+) (values #\space rest)] [(char=? first #\%) (if (and (pair? rest) (pair? (cdr rest))) (values (integer->char (or (string->number (string (car rest) (cadr rest)) 16) (raise (make-invalid-%-suffix (if (string->number (string (car rest)) 16) (cadr rest) (car rest)))))) (cddr rest)) (raise (make-incomplete-%-suffix rest)))] [else (values first rest)])]) (cons this (loop rest)))))))) ;; string->html : string -> string ;; -- the input is raw text, the output is HTML appropriately quoted (define (string->html s) (apply string-append (map (lambda (c) (case c [(#\<) "<"] [(#\>) ">"] [(#\&) "&"] [else (string c)])) (string->list s)))) (define default-text-color "#000000") (define default-bg-color "#ffffff") (define default-link-color "#cc2200") (define default-vlink-color "#882200") (define default-alink-color "#444444") ;; generate-html-output : ;; html-string x list (html-string) x ... -> () (define generate-html-output (opt-lambda (title body-lines [text-color default-text-color] [bg-color default-bg-color] [link-color default-link-color] [vlink-color default-vlink-color] [alink-color default-alink-color]) (let ([sa string-append]) (for-each (lambda (l) (display l) (newline)) `("Content-type: text/html" "" "" "" "
" ,(sa ""
,@(map (lambda (bind)
(string-append (symbol->string (car bind))
" --> "
(cdr bind)
"
"))
bindings)
"
"))
;; extract-bindings : (string + symbol) x bindings -> list (string)
;; -- Extracts the bindings associated with a given name. The semantics of
;; forms states that a CHECKBOX may use the same NAME field multiple times.
;; Hence, a list of strings is returned. Note that the result may be the
;; empty list.
(define (extract-bindings field-name bindings)
(let ([field-name (if (symbol? field-name)
field-name (string->symbol field-name))])
(let loop ([found null] [bindings bindings])
(if (null? bindings)
found
(if (equal? field-name (caar bindings))
(loop (cons (cdar bindings) found) (cdr bindings))
(loop found (cdr bindings)))))))
;; extract-binding/single : (string + symbol) x bindings -> string
;; -- used in cases where only one binding is supposed to occur
(define (extract-binding/single field-name bindings)
(let* ([field-name (if (symbol? field-name)
field-name (string->symbol field-name))]
[result (extract-bindings field-name bindings)])
(cond
[(null? result)
(generate-error-output
(cons (format "No binding for field `~a':