;;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; Base: 10; Package: modlisp -*- ;;;; ************************************************************************* ;;;; FILE IDENTIFICATION ;;;; ;;;; Name: utils.lisp ;;;; Purpose: Utility functions for modlisp package ;;;; Programmer: Kevin M. Rosenberg ;;;; Date Started: Dec 2002 ;;;; ;;;; $Id: utils.lisp,v 1.8 2003/07/19 20:32:47 kevin Exp $ ;;;; ************************************************************************* (in-package #:modlisp) (defun format-string (fmt) (case fmt (:html "text/html") (:xml "text/xml") (:text "text/plain") (otherwise fmt))) (defmacro with-ml-page ((&key (format :html) (precompute t) headers) &body body) (let ((fmt (gensym "FMT-")) (precomp (gensym "PRE-")) (result (gensym "RES-")) (outstr (gensym "STR-")) (stream (gensym "STRM-")) (hdr (gensym "HDR-"))) `(let ((,fmt ,format) (,precomp ,precompute) ,result ,outstr ,stream) (declare (ignorable ,stream)) (write-header-line "Status" "200 OK") (write-header-line "Content-Type" (format-string ,fmt)) (dolist (,hdr ,headers) (write-header-line (car ,hdr) (cdr ,hdr))) (unless ,precomp (write-string "end" *modlisp-socket*) (write-char #\NewLine *modlisp-socket*)) (setq ,outstr (with-output-to-string (,stream) (let ((*modlisp-socket* (if ,precomp ,stream *modlisp-socket*))) (setq ,result (progn ,@body))))) (cond (,precomp (write-header-line "Content-Length" (write-to-string (length ,outstr))) (write-header-line "Keep-Socket" "1") (write-header-line "Connection" "Keep-Alive") (write-string "end" *modlisp-socket*) (write-char #\NewLine *modlisp-socket*) (write-string ,outstr *modlisp-socket*) (finish-output *modlisp-socket*) (setq *close-modlisp-socket* nil)) (t (finish-output *modlisp-socket*) (setq *close-modlisp-socket* t))) ,result))) (defun redirect-to-location (url) (write-header-line "Status" "301 Moved Permanently") (write-header-line "Location" url) ;;(write-header-line "Keep-Socket" "1") ;;(write-header-line "Connection" "Keep-Alive") (write-string "end" *modlisp-socket*) (write-char #\NewLine *modlisp-socket*) (force-output *modlisp-socket*) (setq *close-modlisp-socket* t)) (defun output-ml-page (format html) (write-header-line "Status" "200 OK") (write-header-line "Content-Type" (format-string format)) (write-header-line "Content-Length" (format nil "~d" (length html))) (write-header-line "Keep-Socket" "1") (write-header-line "Connection" "Keep-Alive") (write-string "end" *modlisp-socket*) (write-char #\NewLine *modlisp-socket*) (write-string html *modlisp-socket*) (force-output *modlisp-socket*) (setq *close-modlisp-socket* nil)) (defun output-html-page (str) (output-ml-page :html str)) (defun output-xml-page (str) (output-ml-page :xml str)) ;; Utility functions for library users (defun query-to-alist (posted-string) "Converts a posted string to an assoc list of keyword names and values, \"a=1&bc=demo\" => ((:a . \"1\") (:bc . \"demo\"))" (when posted-string (let ((alist '())) (dolist (name=val (kmrcl:delimited-string-to-list posted-string #\&) (nreverse alist)) (let ((name-val-list (kmrcl:delimited-string-to-list name=val #\=))) (if (= 2 (length name-val-list)) (destructuring-bind (name val) name-val-list (push (cons (kmrcl:ensure-keyword name) (kmrcl:decode-uri-query-string val)) alist)) (cmsg-c :debug "Invalid number of #\= in ~S" name-val-list)))))))