cgi.tcl - A Reference Manual (Draft)
by Don Libes
This document contains technical notes on using cgi.tcl. This
document is a draft and has not been officially reviewed.
This document assumes that you have read the Tcl '96 paper "CGI
Scripting in Tcl". That document will give you the feel for what this
code is all about. In contrast, this document provides the details.
This document assumes you know HTML. I'm not going to explain what
particular tags do or how to use them effectively, except as necessary
to understand the document.
Some of the commands may not work with all browsers. For example, the
cgi_center command is generally understood only by some Netscape
browsers because it produces
tags which are not
commonly supported. You'll have to use your judgement. Remember:
Just because a command exists to produce the HTML doesn't mean your
browser will do anything meaningful with it. In that sense, using
this code is no different than handcoding HTML.
**************************************************
A NOTE ABOUT PROCEDURE NAMES
**************************************************
All procedures are named cgi_XXX. You can also call them without the
cgi_ prefix. Using the cgi_XXX form is no big deal for rarely used
procedures, but the aliases are particularly convenient for things
like hr and br. Aliases are suppressed if procedures exist by those
names already. (Thus, cgi_eval cannot be invoked as "eval"!)
Similarly, you can overwrite the aliases with impunity. Internally,
references are only made to the cgi_ names.
I'm still thinking about this. If you have strong feelings about it,
let me know.
**************************************************
SECURITY
**************************************************
I frequently see statements saying that Tcl (and other interpretive
languages, for that matter) are insecure and should not be used for
writing CGI.
I disagree. It is possible to use Tcl securely and it really isn't
very hard. The key, of course, is to not blindly evaluate user input.
There really isn't much reason to. For instance, this library itself
is pretty big and does lots of things, but nothing in it evaluates
user input. (It does do a lot of evaluation of *programmer* input,
but that's quite acceptable.)
The two classes of commands you should pay close attention to are
commands that do eval operations and commands that invoke external
programs.
**************************************************
GENERAL NOTES ABOUT PARAMETERS
**************************************************
** There are several basic styles of parameter passing.
-- Container commands (e.g., , , etc.)
The last argument is typically a block of code. Other arguments
become attributes. For example:
cgi_body bgcolor=red background=white {
h4 "hello"
}
produces:
hello
-- Commands with required arguments
Some commands have required arguments. Required arguments which are
relatively long, are passed *after* all others. For example:
cgi_h4 align=left "Foo's Bar & Grill"
Another example:
cgi_body bgcolor=red background=white {
cgi_h4 "Foo's Bar & Grill"
}
Commands with relatively short arguments have the short arguments
passed *before* all others. This avoids many redundant keywords. In
the following example, the "img=" is omitted because it is already
implied by the command. The "alt=" is not optional (although the
entire argument is).
cgi_img foo.gif "alt=Foo's Bar & Grill"
Note the quotes around the alt argument. This is only necessary if
the argument has whitespace in it - a consequence of Tcl's normal
scanning rules. The resulting HTML automatically includes quotes
around the value attribute. (See Case-sensitivity.)
** Case-sensitivity and Quotes and Whitespace
Attribute names are case sensitive. Use lowercase names for "normal"
handling. For example, values for attributes such as "url" and
"value" are quoted and encoded. Use uppercase names to suppress the
usual processing. (For example, you might want to test how a browser
responds to incorrect HTML.)
Consider:
cgi_body bgcolor=#123456 {
p [cgi_img foo.gif "alt=Foo's Bar & Grill"]
}
This is translated to
Notice how the ampersand in the alt value has been encoded. Also
notice how quotes have been added to the values of bgcolor, url, and
alt. Thus you no longer have to add quotes all over the place (or
remember when you need to).
Embedded whitespace is protected by quoting in the usual Tcl fashion
rather than typical HTML fashion.
So instead of:
img foo.gif alt="foo bar"
do this:
img foo.gif "alt=foo bar"
which will return HTML that is properly quoted:
-- Name-value commands
Many commands produce tags that use "name" and "value" attributes.
Because these attributes are almost always used in such commands, the
first argument is always of the form name=value so the literal "name"
and "value" can be omitted. For example:
cgi_text color=Red size=5
produces:
Reasonable defaults exist. For example, if you don't need the value
of a submit button (but the user still needs to see it appear as the
label), omit the name:
cgi_submit_button =Execute
If no "=" is present, the string is assumed to be the "name"
attribute. For example:
cgi_checkbox Vegies
produces a checkbox associated with the variable named "Vegies".
(With no specified value, it will be set to "on" if checked.)
Most of the commands have reasonable defaults. For example, to
quickly script a submit button, the following suffices:
cgi_submit_button
Certain constructions make no sense and are therefore disallowed. For
example, a submit button with a name but no value makes no sense, so
cgi_submit_button doesn't allow it. (In other words, if you provide
an argument, it has to have "=" in it.)
**************************************************
JAVASCRIPT ARGUMENTS
**************************************************
JavaScript event attributes such as onClick are handled just like any
other attributes in terms of quoting and case sensitivity. Because
there are so many attributes on so many tags, they are not documented
explicitly in this manual. However, they are all supported. Here is
an example:
cgi_text age=18 onChange=MinimumAge(this.form.age)
**************************************************
PROCEDURES TO ASSIST IN DEBUGGING
**************************************************
** User-id differences
You can interactively run and debug CGI scripts by simply running them
from the shell, or a Tcl or C debugger. (For convenience, I use
Expect instead of tclsh just so that I get the debugger.) In fact,
I've never had to resort to the C debugger. However, simply watching
the raw output from a shell is very handy. This catches the really
basic errors such as incorrect protections, incorrect #! line, etc.
Once your script is actually executing, you can rely on cgi_eval (see
below).
cgi_uid_check user
Typically, a CGI script is intended to run from a particular uid, such
as "nobody". If you run such scripts interactively, you can end up
with conflicts. For example, if the script creates files, files can
accidentally end up being owned by you.
cgi_uid_check is a convenient mechanism to warn against this problem.
Simply call cgi_uid_check in your script. The argument is the uid
under which the script should be running. If the given uid does
not match the actual uid, cgi_uid_check will generate an error.
** Trapping error messages
cgi_eval
cgi_eval is the primary error catching/reporting mechanism. Execute
commands from cgi_eval so that errors can be caught and reported in a
nice way. By default, errors are emailed back to the administrator.
"cgi_debug -on" makes errors be immediately viewable in the browser.
If an error is caught when debugging is enabled, the diagnostic is
anchored with #cgierror. This is useful in scripts that produce
voluminous output.
cgi_error_occurred
cgi_error_occurred returns 1 if an error occurred and was caught by
cgi_eval. This separate function simplifies exit handling - for
example, rather than always checking the return value of cgi_eval, an
app can just test this from a redefined exit.
cgi_admin_mail_addr addr
cgi_admin_mail_addr sets the administrator's email address.
Diagnostics are sent via email if a problem is encountered with the
script and debugging is not enabled.
cgi_name name
cgi_name defines a name for the service. If called with no arguments,
the name is returned. The name is currently used in the following
places:
If email is sent, it comes from [cgi_name].
If errors are emailed, the subject is "[cgi_name]: CGI problem"
** Generating debugging output and other commands
cgi_debug args
cgi_debug provides rudimentary support for generating debugging
messages. Here are some example calls:
cgi_debug cmd
If debugging is on, the command is evaluated. For example:
cgi_debug {
h2 "completed initialization"
}
or
cgi_debug {h2 "completed initialization"}
Note this is more than simply calling h2. Context is rewound
or forwarded to get to a place where this is safe. (And
conversely, this call can suppress things such as header
elements that haven't been processed yet.) The flag
"-noprint" suppresses this manipulation - this is useful for
early code that causes no printing.
The -- flag causes the next argument to treated as a command
even if it looks like another flag.
cgi_debug -on
Enables debugging messages. This includes debugging messages
generated by explicit calls to cgi_debug as well as implicit
diagnostics, for example, that report on form input.
cgi_debug -temp text
Enable debugging for this one line.
cgi_debug -off
Disable debugging.
cgi_debug always returns the old setting ("-on" or "-off"). The
initial value is -off.
** Printing arrays
cgi_parray arrayname
cgi_parray prints out the elements of a Tcl array. cgi_parray is just
like Tcl's parray except that its output is appropriately formatted
for a browser.
**************************************************
BASIC STRUCTURE OF A CGI SCRIPT
**************************************************
Typically, the basic structure of most CGI scripts is:
package require cgi
cgi_eval {
cgi_http_head {cmds}
cgi_html {
cgi_head {cmds}
cgi_body {cmds}
}
}
Much of this can be omitted, however. In fact, a typical script looks
more like this:
package require cgi
cgi_eval {
cgi_title "title"
cgi_body {
cmds
}
}
(If you're not using the Tcl package support, replace the 'package
require' command with a 'source' command of the specific file
containing the cgi.tcl source.)
(The "...." in the examples above should be replaced by the true path
to the cgi.tcl file.)
I'll now go through each of these in more detail as well as some other
possibilities for the overall structure.
**************************************************
HTTP HEADERS
**************************************************
cgi_http_head cmds
CGI scripts must produce various headers to explain how the remainder
of the output is to be interpreted. No other output may preceed this!
With no argument, an HTML content type is produced if the script is
running in the CGI environment. This means that most people need not
bother calling cgi_http_head. However, if you want to see the HTTP
headers and you are not running in the CGI environment, you should
call cgi_http_head explicitly. (Alternatively, you can make it appear
that you are in the CGI environment by defining the environment
variable REQUEST_METHOD.)
The remaining commands in this section may be used in cgi_http_head.
Most should be intuitively obvious and thus need no explanation.
cgi_content_type type
Generates a "Content-type:" header.
With no argument, cgi_content_type generates a declaration for HTML.
If specified, the 'type' argument should be the full MIME-style
type/subtype declaration. Any MIME-style parameters should be
included in the type argument.
cgi_redirect location
Generates a redirect header (Status:/Location:/URI:)
cgi_target
Generates a "Window-target:" header.
cgi_refresh seconds url
Generates a "Refresh:" header. The url argument is optional.
cgi_pragma pragma
Generates a "Pragma:" header.
cgi_status number string
Generates a "Status:" header.
** Cookies
cgi_cookie_set name=val args
Define a cookie with given name, value, and other arguments.
Cookie values are automatically encoded to protect odd characters.
A couple expirations are predefined (with intuitive meaning):
expires=never
expires=now
expires=...actual date...
Here are some examples:
cgi_cookie_set user=don domain=nist.gov expires=never
cgi_cookie_set user=don expires=now secure
cgi_export_cookie name args
Export the named Tcl variable as a cookie. Other arguments are
processed as with cgi_cookie_set.
Cookies may be read only after calling cgi_input. The following
routines read cookie names or specific cookies.
cgi_cookie_list
Returns the list of all cookies supplied by the server.
cgi_cookie_get name
Returns the value of the named cookie. If multiple values exists
the most specific path mapping is used.
If the "-all" flag is used (before the cookie name), a list is
returned containing all cookie values for the given name. The
list is ordered most-specific (path mapping) to least. I.e.,
the first value on the list is the same one returned by
calling cgi_cookie get without a flag.
cgi_import_cookie name
Define a Tcl variable with the value of the cookie of the same
name. For example, the following command retrieves the cookie
named "Password" and stores it in the Tcl variable "Password".
cgi_import_cookie Password
**************************************************
GENERATING HTML
**************************************************
cgi_html
tags can be generated using cgi_html. An argument to
cgi_html is evaluated to produce the actual HTML code.
In practice, it is not necessary to use cgi_html. CGI.tcl will
automatically generate the tags when appropriate. (Oddly, modern HTML
specs don't require it and most if not all browsers never cared
anyway.)
cgi_doctype
cgi_doctype is a user-defined procedure that produces a SGML DOCTYPE
declaration. If it exists, cgi_doctype is automatically invoked at
the beginning of cgi_html. (This library does not automatically create
DOCTYPE declarations since the library is not restricted to generating
SGML for any single DTD. Realistically, DOCTYPEs are pointless for
HTML generation since web browsers don't require DOCTYPE declarations.
However, if you are creating pages for some other purpose that
requires such a declaration, use cgi_doctype.)
cgi_head
tags can be generated using cgi_head. An argument to
cgi_head is evaluated to produce the actual headers.
In practice, it is not necessary to use cgi_head. CGI.tcl will
automatically generate the tags when appropriate. (Oddly, modern HTML
specs don't require it and most if not all browsers never cared
anyway. So for example:
cgi_head {
cgi_title "my page"
}
is equivalent to:
cgi_title "my page"
Note that cgi_title will be called automatically if you omit
cgi_title, cgi_head, or call cgi_head with no arguments.
cgi_title title
cgi_title defines the title of a page. It is called from within a
pair. If not called from within cgi_head, it implicitly
forces it to occur.
cgi_title always returns the title. With no argument, cgi_title
returns the old title without changing it.
cgi_http_equiv
cgi_http_equiv is equivalent to cgi_http_head but from within
cgi_head. This procedure is defined for completeness - there is no
reason to use it. In fact, it doesn't allow all cgi_http_head
declarations, so it should be avoided.
cgi_meta
cgi_meta generates a tag. You can do whatever you want with
these. (Read some an HTML document for more info.) For example:
meta name=author {content="Don Libes"}
cgi_script cmd
cgi_script evaluates its arguments inside of tags.
This is appropriate for putting in client-side scripting. Optional
arguments are passed as attributes to the