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 <center></center> 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., <body></body>, <div></div>, 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:

  <body bgcolor="red" background="white">
     <h4>hello</h4>
  </body>

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 &amp 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

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 {
     cgi_img foo.gif "alt=Foo's Bar & Grill"
  }

This is translated to

  <body bgcolor="#123456">
     <img url="foo.gif" alt="Foo's Bar & Grill">
  </body>

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).

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:

  <input name="color" value="Red" size=5>

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.

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:

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

cgi_import_filename flag name

Uploaded files are saved on the CGI server. To avoid collisions with other file upload instances, files are not stored in their original names. The name of the file as it is stored on the CGI server is retrieved using the "-server" flag. The original name of the file as it was stored on the user's host is retrieved using the "-client" flag.

Uploaded files are the responsibility of the CGI programmer. In particular, if you do not delete them, they will remain until /tmp is cleaned up in some other way.

If the user does not enter a filename, an empty file will be delivered with a null remote filename.

For example, the following code echos the contents of a file that = was uploaded using the variable "file":

  cgi_input
  cgi_body {
    set server [cgi_import_filename -server $v]
    set client [cgi_import_filename -client $v]
    if [string length $client] {
      h4 "Uploaded: $client, contents:"
      cgi_preformatted {puts [exec cat $server]}
    }
    exec /bin/rm -f $server
  }

The present implementation supports binary upload only if you are using the Expect extension. If you are using Expect but want to suppress binary loading, create the global variable _cgi(no_binary_upload). (The reason you might want to suppress binary loading is that it is noticably slower.)

GENERATING HTML

cgi_html

<html></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

<head></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 <head></head> 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 <meta> 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 <script></script>

tags. This is appropriate for putting in client-side scripting. Optional arguments are passed as attributes to the <script> tag.

Note that the cmd argument is a Tcl command, not a command in the other scripting language. So if all you want to do is print out some script, use cgi_puts:

  cgi_script {
    cgi_puts {
      some scripting stuff
        in whatever weird funky
        language you want
    }
}

cgi_javascript cmd

cgi_javascript is a version of cgi_script

specialized for javascript. At present, all it does is add the comment hacks so that the javascript can't be seen by old browsers.

cgi_noscript cmd

cgi_noscript evaluates its argument to generate code for browsers that do not understand cgi_script or its variants.

cgi_body

<body></body> tags are generated using cgi_body. An argument to cgi_body is evaluated to produce the actual body.

Executing "return" from within cgi_body causes cgi_body to return. This is useful if more code follows the cgi_body within the cgi_eval. Compare to cgi_exit (see elsewhere).

cgi_body_args

Arguments to cgi_body_args are made available to cgi_body

as if they had been specified in the call to cgi_body. This provides a convenient way for using the same colors, backgrounds, etc, in a set of pages.

cgi_exit

cgi_exit provides a fast way of cleanly exiting a CGI script without having to manually unwind procedures. In particular, cgi_exit forces closure of all open tags. It then calls exit. This is useful if you want to exit from a CGI script at any point and still have the HTML be correct.

Frames

cgi_frameset cmd

Instead of cgi_body, you can call cgi_frameset to create framed documents. This produces <frameset></frameset> tags with the content filled by evaluation of cmd. Optional arguments are passed on as attributes.

cgi_frame name=url

cgi_frame defines a frame with the given name and url. The argument handling is the same as for other name-value commands (even though the value here is a url). The url is automatically = double-quoted. Other optional arguments are passed on as attributes.

cgi_noframes cmd

cgi_noframes produces = <noframes></noframes>

tags with the content filled evaluation of cmd. Optional arguments are passed on as attributes.

CONTAINER SUPPORT

cgi_division

cgi_division evaluates its last argument, grouping it together. This is useful for acting on a group of paragraphs, such = as for alignment purposes.

cgi_center

cgi_center is similar to "cgi_division = align=center".

SINGLE PARAGRAPH SUPPORT

Everything in this section generates a single paragraph or line = break. Most of these take a string as the last argument which is appropriately formatted. Any other arguments are used as tag attributes.

cgi_p
cgi_address
cgi_blockquote
cgi_h1 through h7

Most of these procedures should be intuitive. They all take a string and display it in the appropriate way. For example, a level 2 heading:

  h2 "Paragraph Support"

Here's a paragraph with some formatting (see next section for more info):

  p "I [bold love] Tcl but hate [blink "blinking text"]"

Note that some of these generate tags that are not supported by all browsers. See the format-tour.cgi script to see these in use.

cgi_br

cgi_br causes a paragraph break to be printed. Additional arguments are passed on as attributes.

To embed a paragraph break (rather than printing it), use cgi_nl. In the following example, it is much more convenient to call cgi_br than cgi_nl:

  radio_button "version=1"
  br
  radio_button "version=2"

See cgi_nl for more info.

TEXT SUPPORT

The following procedures take a string and return an appropriately formatted version. The string is always the last argument. Any other arguments are used as tag attributes.

cgi_bold
cgi_italic
cgi_underline
cgi_strikeout
cgi_subscript
cgi_superscript
cgi_typewriter
cgi_blink
cgi_emphasis
cgi_strong
cgi_cite
cgi_sample
cgi_keyboard
cgi_variable
cgi_definition
cgi_big
cgi_small
cgi_font
 
  p "I [bold love] Tcl but hate [blink "blinking text"]"

Note that some of these generate tags that are not supported by all browsers. See the format-tour.cgi script to see these in use.

cgi_basefont

cgi_basefont defines the base font.

SPECIAL CHARACTERS OR CHARACTER SEQUENCES

The following procedures produce characters such that when = interpreted by a browser returns the indicated character.

code                    Returns            
cgi_lt                  &lt;
cgi_gt                  &gt;
cgi_amp                 &
cgi_quote               " 
cgi_enspace             en space
cgi_emspace             em space
cgi_nbspace             nonbreaking space
cgi_tm                  registered trademark
cgi_copyright           copyright
cgi_isochar n           ISO character #n

cgi_nl

cgi_nl returns a paragraph break string suitable for embedding in a string just as you would embed a newline in a Tcl string via \n.

To print a paragraph break rather than returning it, use cgi_br. In the following example, it is much more convenient to call cgi_nl than than cgi_br:

  h2 "This appears[nl]on two lines."

See cgi_br for more info.

cgi_breakable

cgi_breakable indicates a place in a word at which the browser can break a string across two lines.

cgi_unbreakable cmd

cgi_unbreakable evaluates a cmd in such a way that the output will not be broken across lines by the browser just because the screen width is exceeded. Instead a horizontal scrollbar will appear so that the browser can be manually scrolled to see the long line.

cgi_unbreakable_string string

cgi_unbreakable_string returns its arguments so that it will not be broken across lines by the browser just because the screen width is exceeded. Instead a horizontal scrollbar will appear so that the browser can be manually scrolled to see the long line.

Notes:

FORMS

cgi_form action args cmd

cgi_form defines a form. The form is populated by executing the command (last argument of cgi_form). action defines the url to process the form. Any other arguments are passed as attributes. A typical call looks like this:

   cgi_form response {
     .... 
  }

Here "response" names the URL to process the form. If the URL does not begin with a protocol name (such as "http:"), a common root is prepended and ".cgi" is appended. This can be changed by redefining the procedure cgi_cgi.

cgi_root

cgi_root defines the common root used by cgi_form

(see above). For example:

   cgi_root "http://www.nist.gov/cgi-bin/cgi.tcl-examples"

With one argument, cgi_root returns the new root. With no arguments, cgi_root returns the old root.

cgi_suffix

cgi_suffix defines the common suffix used by = cgi_cgi and anything that uses it such as cgi_form. The default suffix is ".cgi".

cgi_cgi
cgi_cgi_set

cgi_cgi controls exactly how cgi_form creates URLs from its action argument. By default, cgi_cgi takes an argument, prepends [cgi_root] and appends [cgi_suffix]. The suffix can be overridden by using the -suffix flag and an argument to be used instead of [cgi_suffix].

Any additional arguments are joined together in the style required for a GET style request. These arguments should be preformatted using cgi_cgi_set to guarantee proper encoding. For example:

  cgi_cgi myscript \
     [cgi_cgi_set owner "Don"] \
     [cgi_cgi_set color "black & white"]

generates:

  ....?owner=Don&color=black+%26+white

cgi_isindex

cgi_isindex generates an <isindex> tag. Optional arguments are passed on as attributes. In the processing CGI script, the value of the isindex query is found in the "anonymous" variable.

cgi_relationship rel url

cgi_relationship expresses a relationship between this document and another. For example, the following says that the url named by homepage is the home document of the current document.

    cgi_relationship home $homepage

Optional arguments are passed on as additional attributes. Here's an example that references an external style sheet that is a CSS type (cascading style sheet).

    cgi_relationship stylesheet basic.css type=text/css

INPUT

cgi_input

CGI input means "cookies, files, and get/post data". cgi_input reads in all input, decodes it, and makes it available to a variety of other routines.

For debugging, cgi_input can be given arguments to fake input. This allows you to run your cgi_script interactively or under a debugger (either Tcl or C debugger). Provide GET/POST data as the first argument. Provide cookie data as the second argument. The arguments should be encoded (see cgi_cgi_set). For example:

    cgi_input =
"name=libes&old=foo&new1=bar&new2=hello"

This is convenient because when you run into a misbehaving CGI script, the first thing it does is tell you the input in exactly this format. Now simply cut and paste it into your program and you can then interactively debug it without using the real form or the CGI server.

If cgi_input is invoked from the CGI environment, the fake inputs are ignored. (If you want to force fake inputs in the CGI environment, unset env(REQUEST_METHOD)).

Forms encoded as multipart/form-data are usually used to handle file input. Since file data usually implies a large amount of data, the data is saved to /tmp/CGIdbg.[pid] if debugging is enabled. This file can be fed back to cgi_input by providing the filename as the first argument of cgi_input. In addition, env(CONTENT_TYPE) must be set to the appropriate content type. This can found in env(CONTENT_TYPE). Removal of the debugging file is the responsibility of the script or the script programmer. (Typically, I examine the file after my CGI script is over and then delete them by hand.)

Execs before cgi_input reads POST data should have standard input redirected ("< /dev/null" for instance) so that the exec'd process doesn't inherit the CGI script's standard input.

Form elements that generate lists

Variable names should only end with "List" if they correspond to form elements which generate multiple values (some but not all uses of select, checkbox, etc). List variables will be given Tcl-style list values.

cgi_input enforces this restriction by issuing diagnostics when it can detect violations.

This is done in order to distinguish whether a value such as "foo bar" is a single element "foo bar" or two elements "foo" and "bar".

Here are examples:

  # pull-down menu
  cgi_select Foo {
    cgi_option "a"
    cgi_option "a b"
  }
 
  # scrolled list, allow multiple selections
  cgi_select FooList multiple {
    cgi_option "a"
    cgi_option "a b"
  }

Getting at the input

Input is made available in two ways: variables and cookies.

Variables

cgi_import_list

cgi_import_list returns a list of variable names supplied as input to the script.

cgi_cookie_list

cgi_cookie_list returns a list of cookie names supplied to the script.

cgi_import name

cgi_import retrieves the value of the named variable and places it in a Tcl variable of the same name. The value is also returned as the return value.

cgi_import_as name tclvar

cgi_import_as is similar to cgi_import but the value is assigned to the Tcl variable named by the second argument.

cgi_import_cookie name

cgi_import is similar to cgi_import, however if the cgi variable does not exist, the value is fetched from the cookie by that name. (This allows the user to override a cookie if the form allows it.)

cgi_import_cookie_as name tclvar

cgi_import_cookie_as is similar to = cgi_import_cookie

but the value is assigned to the Tcl variable named by the second = argument.

cgi_cookie_get name

cgi_cookie_get returns the value of the named cookie.

EXPORT

Form elements automatically export their values. See FORM ELEMENTS for more information.

cgi_export name=value

cgi_export makes the named variable available with the = given value. The "=value" is optional. If not present, the value = of the Tcl variable by the same name is used.

cgi_export is implemented with variables of = type=hidden.

cgi_export is implemented as an all-or-nothing operation. = In particular, no HTML is emitted if the variable does not exist. That = means it is not necessary to test for existence in situations where you would like to export a variable IF it exists. Rather, it is sufficient to = embed cgi_export within a catch. For example, the following generates nothing if xyz doesn't exist and it generates the appropriate HTML if xyz does exist.

   catch {cgi_export xyz}

Cookies

cgi_export_cookie name

cgi_export_cookie is similar to cgi_export except that the value is made available as a cookie. Additional arguments are handled as with cgi_cookie_set (see below).

cgi_cookie_set name=val

cgi_cookie_set sets the named cookie. All optional = arguments are handled specially. All arguments are encoded appropriately. The = expires keyword is handled specially to simplify common cases. In particular, = the values "now" and "never" produce appropriate GMT values.

Here are some example of cgi_cookie_set:

  cgi_cookie_set user=don domain=nist.gov expires=never
  cgi_cookie_set user=don domain=nist.gov expires=now
  cgi_cookie_set user=don domain=nist.gov expires=...actual =
date...

Note that cookie setting must be done during http head = generation.

URL/IMG DICTIONARY SUPPORT

cgi_link tag
cgi_link tag display url

cgi_link provides a convenient mechanism for maintaining and referencing from a set of URLs.

cgi_link returns the string <A>...</A> corresponding to the given tag. A tag is defined by calling cgi_link with the tag, the clickable text that the use should see, and the url.

For example, suppose you want to produce the following (where xyz indicates xyz is a hyperlink):

I am married to Don Libes who works in the Manufacturing Collaboration Technologies Group at NIST.

Using cgi_link with appropriate link definitions, the scripting to produce this is:

  p "I am married to [link Libes] who works in the [link MCTG] at =
[link NIST]."

This expands to:

 I am married to <A HREF="http://elib.cme.nist.gov/msid/staff
 /libes/ libes.don.html">Don Libes</A> who works in the <A =
HREF="http://
 elib.cme.nist.gov/msid/groups/mctg.htm"> Manufacturing Collaboration
 Technologies Group</A> at <A HREF="http:// =
www.nist.gov">NIST</A>

The links themselves are defined thusly:

link Libes "Don Libes" http://www.cme.nist.gov/msid/staff/libes
link MCTG "$MCT Group" http://www.cme.nist.gov/msid/mctg link NIST =
"NIST"
http://www.nist.gov

Now if my home page ever changes, rather than updating every = occurrence, I just have to edit the one definition.

Tcl variables can further simplify updates. For instance, URLs for = Libes and MCTG are in a common directory. It makes sense to store that in a = single variable. Rewritten this appears:

    set MSID http://www.cme.nist.gov/msid
 
    link Libes "Don Libes"    $MSID/staff/libes
    link MCTG  "$MCT Group"   $MSID/mctg
    link NIST  "NIST"         http://www.nist.gov

Then if the MSID directory ever moves, only one line need be updated. This may seem like no big deal here, but if you have many links and many uses of them, this pays off handsomely.

Optional attributes can be provided as additional arguments (see IMG example below).

An existing link can be given a different "display" temporarily by calling cgi_link with the different display and omitting the url.

cgi_imglink

imglink works similar to cgi_link (see that documentation for more info) except that no display argument is used and the second argument is assumed to be the image source. Example:

   imglink taj tajmahal.gif

Other attributes can be provided as additional arguments.

   imglink taj tajmahal.gif alt="The Taj Mahal"

cgi_url display href args

By using cgi_url, URLs can be generated immediately (without using cgi_link first). This is convenient when you need a URL that will only appear once - so that there is no point in storing it in a dictionary. For example:

cgi_li "[cgi_url "Plume" http://pastime.anu.edu.au/Plume]
 is a Tcl-based WWW browser written by Steve Ball, Australian
 National University. Among its interesting features is the
 ability to execute Tcl applets and the ability to dynamically
 extend the browser at runtime."

cgi_img href args

cgi_img returns a formatted <img> tag. It is useful for one-time tags. Tags that are used multiple times should use cgi_imglink. Example:

    cgi_img foo.gif "alt=Foo's Bar & Grill"

cgi_anchor_name name

cgi_anchor_name returns an anchor that can be used in an HTML body that it can be linked to using the #name syntax. For example, to make a heading that you want to be able to link to:

    h2 "[cgi_anchor_name future]Future Improvements"

Then to reference the "future" tag:

    p "Look for [cgi_url "improvements" future] in the =
future."

cgi_base args

cgi_base defines a base or window target for urls.

QUOTING

cgi_unquote_input string

cgi_unquote_input undoes "url-encoding" and returns the result. This is normally applied automatically to input sources including URLs and cookies. So you shouldn't have to call this manually.

cgi_quote_html string

cgi_quote_html returns the string but with any html-special characters escaped. For example, "<" is replaced by "\<". This is useful for displaying a literal "<" in the browser.

cgi_dquote_html string

cgi_dquote_html does the same thing as cgi_quote_html

but also adds on double quotes. cgi_quote_html is called automatically for implicit value attributes.

cgi_quote_url string

cgi_quote_url quotes strings appropriately to appear in a url, cookie, etc. This is useful if you want to publish a url by hand (and must do the conversion manually that the client normally does for you).

If you are generating cgi-style URLs for forms, use cgi_cgi_set.

cgi_preformatted cmd

cgi_preformatted evaluates its last argument to produce fixed-width preformatted output. Optional arguments are passed as attributes to the tags.

cgi_preformatted allows a subset of tags to be interpreted by the browser. For example, the <a> tag is interpreted but font change tags are not. To prevent all interpretation, use cgi_quote_html. For example, the following prints a file that might contain HTML but without any risk to throwing off formatting.

 cgi_preformatted {
    puts [cgi_quote_html [read $fid]]
 }

LIST SUPPORT

List elements

cgi_li string

cgi_li prints its string as a list element. Optional arguments are passed through as attributes. cgi_li does not = have to appear in a list container, but it can.

cgi_term text
cgi_term_definition text

cgi_term and cgi_term_definition are usually paired up (although they need not be) to creates terms and defintions. They do not have to appear in a list container, but usually appear in a cgi_definition_list.

List containers

cgi_number_list cmd
cgi_bullet_list cmd

cgi_number_list and cgi_bullet_list take their cmd argument and evaluate it in a list container context. (I don't know about you but I could never remember <ol>, <ul>, and all the other ones. This names seem much easier to remember.)

cgi_li is a typical command to call inside of a list container, but you can use regular paragraphs (or anything else) as well.

cgi_definition_list

cgi_definition_list is the usual list container for cgi_term and cgi_term_definition. It may contain other things as well.

cgi_menu_list
cgi_directory_list

cgi_menu_list and cgi_directory are more list containers with the obvious semantics. Previous remarks about other list containers apply.

TABLE

cgi_table cmd

cgi_table produces

  <table></table>

tags with the content filled by evaluation of cmd. Optional arguments are passed on as attributes.

cgi_caption cmd

cgi_caption produces

<caption></caption> tags with the content filled by evaluation of cmd. Optional arguments are passed on as attributes.

cgi_table_row cmd
cgi_table_head cmd
cgi_table_data cmd

These functions all produce the appropriate tags with the content filled by evaluation of cmd. Optional arguments are passed on as attributes.

cgi_tr table_data
cgi_td table_data
cgi_th table_data

cgi_tr, cgi_td, and cgi_th are shortcuts for relatively simple rows.

cgi_td outputs a table element. Unlike = cgi_table_data, the argument is not evalled. This allows more terse specification of = simple rows. The following example produces a table with three elements, the = last of which is prevented from wrapping:

    table_row {td Don;td Steve;td nowrap "Really
Long Name"}

As the example suggests, optional arguments are passed on as data-specific attributes.

cgi_th is identical to cgi_td except that it produces table heading elements.

cgi_tr outputs a row of elements without having to call cgi_td or cgi_table_data. As with td, eval is not called. Data-specific attributes cannot be provided. All the elements are passed as a single argument. For example:

  tr {Don Steve {Really Long Name}}
               or
  tr [list Don Steve $reallylongname]

Optional arguments are passed on as row-specific attributes.

BUTTON

cgi_submit_button name=value
cgi_radio_button name=value
cgi_image_button name=value

These procedure create buttons. The first argument indicates the variable name and value. (See notes on "Name-value" commands earlier to understand behavior of omitted names/values.) Unless otherwise mentioned below, additional arguments are passed on as attributes.

  cgi_submit_button "=Submit Form"
  cgi_submit_button "Action=Pay Raise"
 
  cgi_radio_button "version=1"
  cgi_radio_button "version=2" checked
  cgi_image_button "=http://www.cme.nist.gov/images/title.gif"
  cgi_image_button =
"Map=http://www.cme.nist.gov/images/msid3.gif"

Groups of radio buttons must share the same variable name. To address the obvious question: No, there is no single command to produce a group of radio buttons because you might very well want to do arbitrarily-complex calculations in between them. And with a long-enough line of buttons, the obvious behavior (laying them all out in a line like CGI.pm does) makes it hard to tell at a glance if the buttons associate with the label to the left or the right of them. Anyway, you'll almost certainly want to write another procedure to call cgi_radio_button and that can control the variable name.

The radio button argument "checked_if_equal=xxx" = indicates that the current name should be shown selected if it is equal to = xxx. This is handy if you are creating radio buttons by iterating over a = list.

cgi_file_button name

cgi_file_button provides a filename entry box. When the form is submitted, the file is "uploaded" (sent from the client to the server). The argument enctype=multipart/form-data must be given to = the cgi_form command when using cgi_file_button.

After uploading, the file is the responsibility of the CGI programmer. In particular, if you do not delete it, it will remain until /tmp is cleaned up in some other way.

For example, to upload a single file, the form might look like this:

  cgi_form upload height=3 enctype=multipart/form-data {
    cgi_file_button file
    cgi_submit_button =Upload
  }

Uploaded files are automatically made available to the CGI script using

cgi_import_filename. (See elsewhere for more information.)

cgi_reset_button value

cgi_reset_button creates a a reset button. An argument overrides the default label. For example:

cgi_reset_button
cgi_reset_button "Not the Default Reset Button"

cgi_map name cmd
cgi_area args

These procedures are used to specify client-side image maps. The first argument of cgi_map is the map name. The last argument is evaluated to fill the contents of <map></map> tags. cgi_area's arguments are embedded as arguments in an <area> tag.

Warning: These two commands will likely be redefined as I get more familiar with how they are typically used.

CHECKBOX

cgi_checkbox name=value

cgi_checkbox is similar to cgi_radio_button

(see above) except that multiple values can be checked at the same time. As explained earlier, the variable name must end with "List" in order for it to group all the values as a list in the resulting CGI script. Other arguments are passed on as attributes except for the argument "checked_if_equal=xxx" which indicates that the current name should be shown selected if it is equal to xxx. This is handy if you are creating checkboxes by iterating over a list.

TEXTENTRY AND TEXTAREA

cgi_text name=value

cgi_text provides a one-line text entry box. It works similarly to other form elements. (Read "Name-value commands" elsewhere.) Additional arguments are passed on as attributes. Examples:

  cgi_text Foo
  cgi_text Foo=
  cgi_text Foo=value2
  cgi_text Foo=value2 size=5
  cgi_text Foo=value2 size=5 maxlength=10
  cgi_text Foo=value2 size=10 maxlength=5
  cgi_text Foo=value2 maxlength=5

cgi_textarea name=value

cgi_text provides a multiline text entry box. It works similarly to other form elements. (Read "Name-value commands" elsewhere.) Additional arguments are passed on as attributes.

  set value "A really long line so that we can compare the\
                effect of wrap options."
 
  cgi_textarea Foo
  cgi_textarea Foo=
  cgi_textarea Foo=$value
  cgi_textarea Foo=$value rows=3
  cgi_textarea Foo=$value rows=3 cols=7
  cgi_textarea Foo=$value rows=3 cols=7 wrap=virtual

SELECT

cgi_select name cmd

cgi_select can be used to produce pull-down menus and scrolled lists. (And depending upon the future of HTML, I may provide better-named commands to indicate this.) Its behavior is controlled by additional arguments which are simply the appropriate attributes.

cgi_select evaluates cmd which typically contains multiple calls to cgi_option.

cgi_option string

cgi_option adds options to cgi_select. By default, the string is displayed and sent back as the value of the cgi_select variable. The value can be overridden by an explicit "value=" argument. Additional options are passed on as attributes, except for "selected_if_equal".

"selected_if_equal=xxx" indicates that the current option should be shown selected if the value is equal to xxx. This is useful if you are generating cgi_options in a loop rather than manually.

Here are examples:

  # pull-down menu
  cgi_select Foo {
    cgi_option one selected
    cgi_option two
    cgi_option many value=hello
  }
 
  # scrolled list, allow multiple selections, show all elements
  cgi_select FooList multiple {
    cgi_option one selected
    cgi_option two selected
    cgi_option many
  }
 
  # scrolled list, allow multiple selections, show 2 elements max
  cgi_select FooList multiple size=2 {
    cgi_option one selected
    cgi_option two selected
    cgi_option many
  }
 
  # choose "selected" dynamically
  # example: list all Tcl command and select "exit" automatically
  cgi_select FooList multiple size=5 {
    foreach o [info comm] {
      cgi_option $o selected_if_equal=exit
    }
  }

Note: If both value= and selected_if_equal= appear, the test for selection is made against the last value string (implicit or explicit) that appears before the selected_if_equal argument. In other words, if you want selected_if_equal= to be tested against the explicit

value= argument, put the selected_if_equal= after the value=.

APPLET

cgi_applet parameters cmd

cgi_applet produces <applet></applet> tags such as for Java.

cgi_param name=value

cgi_param produces <param> tags for passing parameters to applets.

For example:

  cgi_applet codebase="../" code="some.class" width=640 =
height=480 {
     cgi_param parm=value
  }

PLUG-IN

cgi_embed src widthxheight

cgi_embed creates an <embed> tag. The first argument is the source. The second argument is the width and height in the style "WxH". Optional arguments are passed on to the tag. For example:

    cgi_embed myavi.avi 320x200 autostart=true

produces:

    <embed src="myavi.avi" width="320" height="200"
autostart=true>

Notice the autostart value is unquoted because autostart is not specifically defined by the spec. The argument "-quote" causes all remaining attributes to be url encoded and quoted. For example:

    cgi_embed myavi.avi 320x200 a=b -quote c=d e=f

produces:

    <embed src="myavi.avi" width="320" height="200"
a=b c="d" e="f">

MISC

cgi_hr

cgi_hr produces horizontal rules. Optional arguments are passed on as attributes.

COMMENTS

cgi_comment stuff

cgi_comment can comment out anything including blocks of code.

cgi_html_comment stuff

cgi_html_comment comments out things in such a way that the comment appears in the final html itself.

OUTPUT

cgi_put string

cgi_put prints the string with no new terminating newline. This is simply a shorthand for puts -nonewline.

cgi_puts

Many routines in this library send output to the standard output by default. This is convenient for CGI scripts. However, if you want to generate multiple files, it is useful to be able to redirect output dynamically. Output can be redirected by redefining cgi_puts. The default definition of cgi_puts is:

  proc cgi_puts {args} {
    eval puts $args
  }

cgi_puts must allow for an optional -nonewline argument. For example, here is a definition that writes to a file identified by the global "fd".

  proc cgi_puts {args} {
    global fd

    puts -nonewline $fd [lindex $args end]
    if {[llength $args] > 1} {
      puts $fd ""
    }
  }

cgi_buffer cmd

cgi_buffer evaluates its argument in such a way that output (through explicit or implicit calls to cgi_puts) is not produced. Instead, the output is returned.

For example, the following cmd generates a link with the hyperlinked portion being a header and two paragraphs.

  link tag [cgi_buffer {
    h3 "Level 3 header"
    p "New paragraph"
    p "Another paragraph"
  }] $URL

cgi_buffer can be called recursively.

cgi_buffer_nl string

By default, cgi_buffer generates newlines at the end of every line in the style of puts. It is occasionally useful to be able to disable this - for example, when calling it inside cgi_preformatted. The newline definition can be changed via cgi_buffer_nl. (There is no point to redefining cgi_puts since cgi_buffer doesn't use it.) A null argument suppresses any newline. For example:

    cgi_buffer_nl ""

cgi_buffer_nl returns the previous definition.

MAIL

Rudimentary email support is provided, in part, because it is useful for trapping errors - if debugging is disabled (i.e., during actual use) and an error is encountered, errors are emailed to the service admin.

The current implementation only permits one email transaction at a time.

cgi_mail_addr addr

cgi_mail_addr defines the email address that mail comes from. Your email system must allow this, of course. (If your system doesn't allow it, the request is usually ignored.)

cgi_mail_start addr

cgi_mail_start creates a mail message to be delivered the given addr. cgi_mail_add should be used to provide a subject and body.

cgi_mail_add string

cgi_mail_add adds strings to the current mail message. No argument causes a blank line to be added.

cgi_mail_end

cgi_mail_end queues the the current mail message for delivery.

  cgi_mail_start libes@nist.gov
  cgi_mail_add "Subject: [cgi_name] request succeeded
  cgi_mail_add
  cgi_mail_add "Your request has been processed."
  cgi_mail_add "Thanks for using [cgi_name].
  cgi_mail_end

cgi_mail_relay host

cgi_mail_relay identifies a host to be used for mail relay services. (This is currently only used when sendmail support is not available.) If a relay is not defined, email is sent directly to the recipient.