r11859: Canonicalize whitespace
[cl-modlisp.git] / utils.lisp
1 ;;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; Base: 10; Package: modlisp -*-
2 ;;;; *************************************************************************
3 ;;;; FILE IDENTIFICATION
4 ;;;;
5 ;;;; Name:          utils.lisp
6 ;;;; Purpose:       Utility functions for modlisp package
7 ;;;; Programmer:    Kevin M. Rosenberg
8 ;;;; Date Started:  Dec 2002
9 ;;;;
10 ;;;; $Id$
11 ;;;; *************************************************************************
12
13 (in-package #:modlisp)
14
15 (defun format->string (fmt)
16   (case fmt
17     (:html "text/html")
18     (:xml "text/xml")
19     (:text "text/plain")
20     (otherwise fmt)))
21
22 (defun format-string (fmt headers)
23  `(("Content-Type" .
24     ,(format->string fmt))
25    . ,headers))
26
27 (defmacro write-response ((&key headers len (status "200 OK")) &body body)
28   (let ((result (gensym "RES-")))
29    `(progn
30       (write-header-line "Status" ,status)
31       (dolist (hdr ,headers)
32         (write-header-line (car hdr) (cdr hdr)))
33     ,@(and len
34         `((write-header-line "Content-Length" ,len)
35           (write-header-line "Keep-Socket" "1")
36           (write-header-line "Connection" "Keep-Alive")))
37       (write-string "end" *modlisp-socket*)
38       (write-char #\NewLine *modlisp-socket*)
39       (let ((,result (progn ,@body)))
40         (,(if len 'force-output 'finish-output)  *modlisp-socket*)
41         (setq *close-modlisp-socket* ,(not len))
42         ,result))))
43
44 (defmacro with-ml-page ((&key (format :html) (precompute t) headers)
45                         &body body)
46   (if precompute
47     `(output-ml-page ,format (with-output-to-string (*modlisp-socket*) ,@body) :headers ,headers)
48     `(write-response (:headers (format-string ,format ,headers)) ,@body)))
49
50 (defun redirect-to-location (url)
51   (write-response (:status "307 Temporary Redirect" :headers `(("Location" . ,url)))))
52
53 (defmacro output-ml-page (format html &key headers)
54   (let ((str (gensym "STR-")))
55    `(let ((,str ,html))
56       (write-response (:len (format nil "~d" (length ,str))
57                        :headers (format-string ,format ,headers))
58         (write-string ,str *modlisp-socket*)))))
59
60 (defun output-html-page (str &key headers)
61   (output-ml-page :html str :headers headers))
62
63 (defun output-xml-page (str &key headers)
64   (output-ml-page :xml str :headers headers))
65
66 ;; Utility functions for library users
67
68 (defun query-to-alist (posted-string &key (keyword t))
69   "Converts a posted string to an assoc list of keyword names and values,
70 \"a=1&bc=demo\" => ((:a . \"1\") (:bc . \"demo\"))"
71   (when posted-string
72     (let ((alist '()))
73       (dolist (name=val (kmrcl:delimited-string-to-list posted-string #\&)
74                (nreverse alist))
75         (let ((name-val-list (kmrcl:delimited-string-to-list name=val #\=)))
76           (if (= 2 (length name-val-list))
77             (destructuring-bind (name val) name-val-list
78               (push (cons (if keyword
79                               (kmrcl:ensure-keyword name)
80                             name)
81                           (kmrcl:decode-uri-query-string val))
82                     alist))
83             (cmsg-c :debug "Invalid number of #\= in ~S" name-val-list)))))))