Le Sursis is a system to aid in writing CGI scripts in Common Lisp. This manual describes its use.
This is Version 0.1.1 of Le Sursis by Tom Burdick.
The latest version of Le Sursis can always be found on the Sursis web site .
Le Sursis is
licensed to you, the user, under the terms of the
GNU General Public License
, a copy of which should be in the
COPYING file included in the Le
Sursis distribution. Please read it as it is designed to protect your
rights as the user of this software; it also places certain restrictions
on the use of this software so as to protect the rights of all users.
Le Sursis is an attempt to provide a good environment to write CGI
scripts in Common Lisp. It consists of two packages:
sursis contains the functions for retrieving
CGI queries, and
sursis-html contains a set of utility functions
to make it easier to compose HTML. Although they are meant to be
complimentary, each may be used independently of the other.
Le Sursis nominally stands for "Sane and Usable Response to Scripting Internet Sites." However, "le sursis" is also French for "the reprieve," meant here in the sense of "a reprieve from Perl." And, alas, while Le Sursis is indeed a reprieve from Perl, it is not a solution. If one has to work on a CGI script with others, it will almost certainly be necessary to use a language other than Lisp. But, when you can use it, what a reprieve!
Also, Le Sursis is a novel by Jean-Paul Sartre, the second in the three-part series Les Chemins de la LibertÚ ("The Paths to Freedom"). It is a great book, M. Sartre was an amazing author and philosopher, and I would encourage everyone to read his work. The remainder of this manual will consider only the software.
All examples given here use the
.lsp extension for lisp files.
In most situations, you would want to compile the lisp files from this
distribution. Any references to compiled files will use the
extension used by
which is the Common Lisp implementation used by the author.
All lisp files intended to be used as CGI scripts are assumed to begin with:
or the appropriate for the Lisp system being used, and are assumed to have the appropriate executable permissions.
sursis package provides Le Sursis' interface to CGI queries.
For the simplest scripts, the following should suffice:
(load "sursis.lsp") (cgi:get-query) => (("name1" . "value1") ... ("nameN" . "valueN"))
get-query's value is an association list; or, if no input was
given to the script, it's value is
The next most useful function exported is probably
format-query takes an a-list as an argument and its value is a
matching query string. For example:
(cgi:get-query) => (("greeting" . "Hello world!") ("tone" . "emphatic")) (cgi:format-query *) => "?greeting=Hello+world!&tone=emphatic"
This makes it easy to have a cgi script recur on itself. In fact, it was this behavior of the majority of my cgi scripts that lead me to write this package.
For more detail, see The sursis Package.
sursis-html package provides utility functions to make it
easier to write HTML responses. It exports functions with names
corresponding to most common HTML tags. A simple example of its use is:
(load "sursis-html.lsp") (use-package 's-html) (http-response (html (head (title "Test page")) (body nil (h1 "Section 1") (p "paragraph 1") (h2 "Subsection 1.1") (p (i "and so on..."))))) => "Content-type: text/html <html> <!-- Generated with help from Le Sursis <http://sursis.sourceforge.net/> --> <head><title>Test page</title></head> <body> <h1>Section 1</h1> <p>paragraph 1</p> <h2>Subsection 1.1</h2> <p><i>and so on...</i></p> </body> </html>"
The resulting html varies in readability, but the assumption is that the
user prefers easily readable lisp. All the tag functions take multiple
arguments for their bodies and concatenate these to form the text
between the two tags (or after the tag in the case of
<li>, and some others). A typical use could be as follows:
(format t (http-response (html (head (title "Thank You!")) (body (h1 "Thank You!") "Thank you for your response."))))
Some tags, for example the
<img> tag, take attribute values. The
functions for these take a required a-list argument before any tag
(img '(("src" . "le-sursis.png") ("alt" . "The Le Sursis banner"))) => "<img src=\"le-sursis.png\" alt=\"The Le Sursis banner\">" (table '(("border" . "on")) "...") => "<table border=\"on\">...</table>"
Also note that some tag functions, like
img, do not accept any
arguments for their body, as they don't have one.
For more detail, see The sursis-html Package.
sursis package (which is also available via the nickname
cgi) is in
sursis.lsp. It exports functions for
retrieving cgi query data and for forming queries. It currently
supports only CLISP, but it should be portable to any Common Lisp system
with a one or two line change (CL doesn't provide any mechanism for
accessing environmental variables).
Currently (as of Version 0.1) the
sursis package only supports
GET queries. Version 0.2 will add support for POST queries. Also, the
functions in this package have not been optimized to be tail-recursive.
Version 0.3--or possibly sooner--should see the re-writing of some
functions to be tail-recursive where efficiency or any potential stack
overflows could be a concern.
get-query &optional reget
get-query's value is an association list containing the key-value
pairs given to the cgi script, or
nil if none were given.
get-query reads the query data in once and returns the
same list every time it is called. This improves efficiency and is
necessary for POST queries. If a non-
nil value is given for the
optional argument reget,
get-query will behave as if it has
not yet been run. This behavior is almost never desired, except under
the most pathological circumstances.
(cgi:get-query) => (("message" . "Hello World!") ("tone" . "emphatic"))
format-query takes an association list al and returns an
appropriately encoded string to be used as a GET query. Note that it
ignores repeats of keys, so it is safe to
acons pairs onto the
head of the list returned by
get-query. Unless you care about
order, in which case you'll have to use a more awkward idiom.
(cgi:get-query) => (("message" . "Hello World!") ("tone" . "emphatic")) (acons "tone" "blasÚ" *) => (("tone" . "blasÚ") ("message" . "Hello World!") ("tone" . "emphatic")) (cgi:format-query *) => "?tone=blas%e9&message=Hello+World!" (format nil "http://path/to/self~A" *) => "http://path/to/self?tone=blas%e9&message=Hello+World!"
get-environment &optional reget toget
get-environment's value is an association list of environmental
variables describing the CGI environment. Under normal circumstances,
it reads from the environment once and returns the same list every time
it is called. If the optional argument reget is non-
it will return a newly-constructed list. The optional variable
toget, which should be a list of strings, specifies the
environmental variables to include in the list. As of this writing,
toget defaults to
"CONTENT_LENGTH"). This function will eventually inspect all
environmental variables in the CGI specification; the user may wish to
inspect the definition of
get-environment before passing it a
custom list, in case the desired variable has been added to the
defaults. When specifying a custom list in toget, reget
should be set to non-
nil, or the behavior may be other than what
A note on the a-list returned by
cdrof its entry in the a-list will be its value:
( variable-name . value ).
cdrof its entry will be
( variable-name ).
cdrof its entry will be
( variable-name . "" ).
(cgi:get-environment) => (("REQUEST_METHOD" . "GET") ("QUERY_STRING" . "message=Hello+World!&tone=emphatic") ("CONTENT_LENGTH")) (cgi:get-environment nil '("SCI" "WWW_HOME" "USERNAME")) => (("REQUEST_METHOD" . "GET") ("QUERY_STRING" . "message=Hello+World!&tone=emphatic") ("CONTENT_LENGTH")) (cgi:get-environment t '("SCI" "WWW_HOME" "USERNAME")) => (("SCI" . "/usr/lib/scilab-2.4.1/") ("WWW_HOME" . "/home/noc/lynx_bookmarks.html") ("USERNAME" . ""))
It is worth noting that
sursis contains a definition for
getenv, which is a thin wrapper around the current Lisp system's
function to retrieve a single environmental variable (this is not a part
of Common Lisp, so the function name varies from system to system). It
takes a single string as an argument and returns a string, or
if the variable is not set. It is not exported, but it may be useful to
call to retrieve a single variable from the environment.
sursis-html package (which is also available via the nickname
s-html) is in
sursis-html.lsp. It exports a large number of
utility functions to make it easier to form HTML responses to CGI
queries. It is written entirely in Common Lisp, using no extensions.
Unless one wishes to only use the
sursis-html functions a few
times in a given script, one should import the functions provided by
sursis-html. So long as one avoids the names of common html
tags, this name-space pollution should not be a problem, and the
increased readability of the Lisp code should more than make up for it.
This package is optimized to be tail-recursive.
Nearly every exported function is based on one of the two generic tag
tag. These functions are primitives
for creating a stand-alone tag, and a normal tag (with a
</name> portion), respectively. All tag functions except
those for purely stand-alone tags (i.e., tags like
<p>, which may be viewed as having contents, even if
it need not be closed) have a
&rest argument for the contents of
the tag. The tag functions take the
~D representations from
format for all of their
&rest arguments and concatenate
them together for the body of the tag:
(html "The" " value of x is" 12) => "<html>The value of x is 12</html>"
This allows nesting of the tag functions, as in:
(http-response (html (head (title "Example")) (body nil "Hi."))) => "Content-type: text/html <html> <!-- Generated with help from Le Sursis <http://sursis.sourceforge.net/> --> <head><title>Example</title></head> <body> Hi. </body> </html>"
The readability of the Lisp code given above is the point of this package.
http-response function used in the example above
does not produce an HTML tag, it is provided to be consistent with the
philosophy of the package.
Note that generation of the recognition comment above is controlled by a
constant set at the top of
sursis-html.lsp, and may be disabled.
All these functions are of the form:
name &rest sl
And produce output of the form:
reader should note that some of these functions insert line breaks for
All these functions are of the form:
name &rest slAnd produce output of the form:
<name>body. This is always followed with a newline, except for
dtwhich omits the newline to increase readability:
(dt "term" (dd "definition of term")) => "<dt>term<dd>definition of term "
None of the functions in this section accept arguments for a body. They do not have a general form, so they will be addressed one at a time.
anchortag in HTML. This function is meant for establishing anchors within a document, and is intended to be more readable than trying to do the same with the
(anchor "para1") => "<a name=\"para1\"></a>
<br>tag, with line breaks before and after the tag.
hr &optional atrib
hrworks the same as
br, except that it accepts an optional list of attributes.
img &optional atrib
<img>tag exactly how you would expect:
(img '(("src" . "pic") ("alt" . "a pic"))) => "<img src=\"pic\" alt=\"a pic\">"
These functions are of the form:
name atrib &rest slAnd produce output of the form:
<name a1="v1" ...>body</name>. As with the simple tag functions, some insert line breaks for readability.
No functions are currently in this section.
These functions do not have a general form, so they will be addressed one at a time.
comment &rest sl
<name>body</name>, it produces
<!-- body -->.
http-response &rest sl
"Content-type: text/html"and two newlines to its body.
sa-tag name atrib
sa-taggenerates an unclosed (stand-alone) tag from the string given as name, and the a-list given as atrib:
(sa-tag "name" nil) => "<name>" (sa-tag "name" '(("a1" . "v1") ("a2" . "v2"))) => "<name a2=\"v2\" a1=\"v1\">"
tag name atrib contents &optional pretty1 pretty2
sursis-htmlare built on
sa-tagcould be reasonable to use in one's code for tags not provided by
sursis-html, if one is going to use
tagto produce a given tag more than once, it is probably worthwhile to define a new (inline) function to do this.
tagis build around
sa-tag. name and atrib are used as in
sa-tag, and contents provides the body. However, note that contents is a list, which makes
tagvery useful from functions with a &rest argument, but more difficult to use directly. As an example, to create a
tagdirectly, one could use:
(p "Here's " (tag "b" nil '("some bold " "text")) ".") => "<p>Here's <b>some bold text</b>.</p>"The
(defun b (&rest sl) (tag "b" nil sl))makes this much less awkward:
(p "Here's " (b "some bold " "text") ".")If pretty1 or pretty2 are non-
nil, they insert a newline after the
<name>tag and before the
</name>tag, respectively. Both are optional, and both default to