r11492: add pretty-data-ut
[kmrcl.git] / web-utils.lisp
1 ;;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; Base: 10 -*-
2 ;;;; *************************************************************************
3 ;;;; FILE IDENTIFICATION
4 ;;;;
5 ;;;; Name:          web-utils.lisp
6 ;;;; Purpose:       Basic web utility functions
7 ;;;; Programmer:    Kevin M. Rosenberg
8 ;;;; Date Started:  Apr 2000
9 ;;;;
10 ;;;; $Id$
11 ;;;;
12 ;;;; This file, part of KMRCL, is Copyright (c) 2002 by Kevin M. Rosenberg
13 ;;;;
14 ;;;; KMRCL users are granted the rights to distribute and use this software
15 ;;;; as governed by the terms of the Lisp Lesser GNU Public License
16 ;;;; (http://opensource.franz.com/preamble.html), also known as the LLGPL.
17 ;;;; *************************************************************************
18
19 (in-package #:kmrcl)
20
21
22 ;;; HTML/XML constants
23
24 (defvar *standard-xml-header* 
25   #.(format nil "<?xml version=\"1.0\" encoding=\"iso-8859-1\" standalone=\"yes\"?>~%"))
26
27 (defvar *standard-html-header* "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML//EN\">")
28
29 (defvar *standard-xhtml-header*
30   #.(format nil "<?xml version=\"1.0\" encoding=\"iso-8859-1\" standalone=\"yes\"?>~%<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">"))
31
32
33 ;;; User agent functions
34
35 (defun user-agent-ie-p (agent)
36   "Takes a user-agent string and returns T for Internet Explorer."
37   (or (string-starts-with "Microsoft" agent)
38       (string-starts-with "Internet Explore" agent)
39       (search "Safari" agent)
40       (search "MSIE" agent)))
41
42 ;;; URL Functions
43
44 (defvar *base-url* "")
45 (defun base-url! (url)
46   (setq *base-url* url))
47
48 (defun make-url (page-name &key (base-dir *base-url*) (format :html) vars anchor)
49   (let ((amp (case format
50                (:html
51                 "&")
52                ((:xml :ie-xml)
53                 "&amp;"))))
54     (concatenate 'string 
55       base-dir page-name
56       (if vars
57           (let ((first-var (first vars)))
58             (concatenate 'string 
59               "?"  (car first-var) "=" (cdr first-var)
60               (mapcar-append-string 
61                #'(lambda (var) 
62                    (when (and (car var) (cdr var))
63                      (concatenate 'string 
64                        amp (string-downcase (car var)) "=" (cdr var))))
65                (rest vars))))
66         "")
67       (if anchor
68           (concatenate 'string "#" anchor)
69         ""))))
70
71 (defun decode-uri-query-string (s)
72   "Decode a URI query string field"
73   (declare (simple-string s)
74            (optimize (speed 3) (safety 0) (space 0)))
75   (do* ((old-len (length s))
76         (new-len (- old-len (* 2 (the fixnum (count-string-char s #\%)))))
77         (new (make-string new-len))
78         (p-old 0)
79         (p-new 0 (1+ p-new)))
80        ((= p-new new-len) new)
81     (declare (simple-string new)
82              (fixnum p-old p-new old-len new-len))
83          (let ((c (schar s p-old)))
84            (when (char= c #\+)
85              (setq c #\space))
86            (case c
87              (#\%
88               (unless (>= old-len (+ p-old 3))
89                 (error "#\% not followed by enough characters"))
90               (setf (schar new p-new)
91                     (code-char
92                      (parse-integer (subseq s (1+ p-old) (+ p-old 3))
93                                     :radix 16)))
94               (incf p-old 3))
95              (t
96               (setf (schar new p-new) c)
97               (incf p-old))))))
98
99 (defun split-uri-query-string (s)
100   (mapcar
101    (lambda (pair)
102      (let ((pos (position #\= pair)))
103        (when pos
104          (cons (subseq pair 0 pos)
105                (when (> (length pair) pos)
106                  (decode-uri-query-string (subseq pair (1+ pos))))))))
107    (delimited-string-to-list s #\&)))