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
Free Software
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
and
sursis-html
. 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 .fas
extension used by
CLISP
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:
#!/usr/local/bin/clisp
or the appropriate for the Lisp system being used, and are assumed to have the appropriate executable permissions.
sursis
usageThe 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 nil
.
The next most useful function exported is probably format-query
.
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
usageThe 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 <p>
,
<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
contents. Ex:
(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
PackageThe 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.
sursis:get-query
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.
Normally, 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.
Sample usage:
(cgi:get-query) => (("message" . "Hello World!") ("tone" . "emphatic"))
sursis:format-query
format-query al
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.
Sample usage:
(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!"
sursis:get-environment
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-nil
,
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 ("REQUEST_METHOD" "QUERY_STRING"
"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
was desired.
A note on the a-list returned by get-environment
:
cdr
of its entry in the
a-list will be its value: ( variable-name . value )
.
cdr
of its entry will be nil
:
( variable-name )
.
cdr
of its entry will be ""
:
( variable-name . "" )
.
Sample usage:
(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 nil
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
PackageThe 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.
sursis-html
OverviewNearly every exported function is based on one of the two generic tag
functions: sa-tag
and tag
. These functions are primitives
for creating a stand-alone tag, and a normal tag (with a <name>
and a </name>
portion), respectively. All tag functions except
those for purely stand-alone tags (i.e., tags like <br>
, as
opposed to <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.
Although the 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: <name>body</name>
. The
reader should note that some of these functions insert line breaks for
readability.
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 dt
which 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.
anchor name
anchor
tag 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 a
function.
(anchor "para1") => "<a name=\"para1\"></a>
br
br
produces a <br>
tag, with line breaks before and after
the tag.
hr &optional atrib
hr
works 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-tag
generates 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-html
are built on
tag
. Whereas sa-tag
could be reasonable to use in one's
code for tags not provided by sursis-html
, if one is going to use
tag
to produce a given tag more than once, it is probably
worthwhile to define a new (inline) function to do this. tag
is
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 tag
very useful
from functions with a &rest argument, but more difficult to use
directly. As an example, to create a <b>
tag using tag
directly, one could use:
(p "Here's " (tag "b" nil '("some bold " "text")) ".") => "<p>Here's <b>some bold text</b>.</p>"The
defun
for b
:
(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 nil
.