[ Team LiB ] Previous Section Next Section

Defining Forms and Processing Form Data

The guestbook.cgi script only generates output. The other half of CGI deals with input from the user. Input is more complex for two reasons. First, we have to define another HTML page that has a form for the user to fill out. Second, the data from the form is organized and encoded in a standard form that must be decoded by the script. Example 3-8 on page 43 defines a very simple form, and the procedure that decodes the form data is shown in Example 11-6 on page 165.

The guestbook page contains a link to newguest.html. This page contains a form that lets a user register his or her name, home page URL, and some additional HTML markup. The form has a submit button. When a user clicks that button in her browser, the information from the form is passed to the newguest.cgi script. This script updates the database and computes another page for the user that acknowledges the user's contribution.

The newguest.html Form

An HTML form contains tags that define data entry fields, buttons, checkboxes, and other elements that let the user specify values. For example, a one-line entry field that is used to enter the home page URL is defined like this:

<INPUT TYPE=text NAME=url>

The INPUT tag is used to define several kinds of input elements, and its type parameter indicates what kind. In this case, TYPE=text creates a one-line text entry field. The submit button is defined with an INPUT tag that has TYPE=submit, and the VALUE parameter becomes the text that appears on the submit button:

<INPUT TYPE=submit NAME=submit VALUE=Register>

A general type-in window is defined with the TEXTAREA tag. This creates a multiline, scrolling text field that is useful for specifying lots of information, such as a free-form comment. In our case, we will let guests type in HTML that will appear with their guestbook entry. The text between the open and close TEXT-AREA tags is inserted into the type-in window when the page is first displayed.

<TEXTAREA NAME=markup ROWS=10 COLS=50>Hello.</TEXTAREA>

A common parameter to the form tags is NAME=something. This name identifies the data that will come back from the form. The tags also have parameters that affect their display, such as the label on the submit button and the size of the text area. Those details are not important for our example. The complete form is shown in Example 3-8:

Example 3-8 The newguest.html form
<HTML>
<HEAD>
<TITLE>Register in my Guestbook</TITLE>
</HEAD>
<BODY BGCOLOR=white TEXT=black>

<FORM ACTION="newguest.cgi" METHOD="POST">

<H1>Register in my Guestbook</H1>
<UL>
<LI>Name <INPUT TYPE="text" NAME="name" SIZE="40">
<LI>URL  <INPUT TYPE="text" NAME="url" SIZE="40">
<P>
If you don't have a home page, you can use an email URL like "mailto:welch@acm.org"
<LI>Additional HTML to include after your link:
<BR>

<TEXTAREA NAME="html" COLS="60" ROWS="15">
</TEXTAREA>
<LI><INPUT TYPE="submit" NAME="new" VALUE="Add me to your guestbook">
<LI><INPUT TYPE="submit" NAME="update" VALUE="Update my guestbook entry">
</UL>
</FORM>

</BODY>
</HTML>

The ncgi and cgi.tcl Packages

The newguest.cgi script uses the ncgi package to process form data. This is one of many packages available in the Standard Tcl Library, commonly known as "tcllib". If you don't have tcllib installed, you can find it on the CD-ROM, on SourceForge at www.sf.net/projects/tcllib, or via the main www.tcl.tk Web site. If your Tcl installation includes tcllib, then you use the package command to load the package.

package require ncgi

The procedures in the ncgi package are in the ncgi namespace. Tcl namespaces are described in detail in Chapter 14. Procedures in a namespace are qualified with the name of the namespace and :: syntax. For example, the standard setup procedure for a CGI script is ncgi::parse.

The "n" in ncgi is for "new". Don Libes wrote the original package for CGI scripts known as cgi.tcl. There is also the cgilib.tcl package that contains Cgi_Header and some other procedures described in earlier editions of this book. The ncgi and html packages of tcllib provide most of the features in both cgi.tcl and cgilib.tcl, but follow the standard namespace conventions use by the packages in tcllib. You can still find cgi.tcl on the Web at

http://expect.nist.gov/cgi.tcl/

The newguest.cgi Script

When the user clicks the Submit button in her browser, the data from the form is passed to the program identified by the ACTION parameter of the form tag. That program takes the data, does something useful with it, and then returns a new page for the browser to display. In our case, the FORM tag names newguest.cgi as the program to handle the data:

<FORM ACTION=newguest.cgi METHOD=POST>

The CGI specification defines how the data from the form is passed to the program. The data is encoded and organized so that the program can figure out the values the user specified for each form element. The encoding is handled rather nicely with some regular expression tricks that are done in ncgi::parse. ncgi::parse saves the form data, and ncgi::value gets a form value in the script. These procedures are described in Example 11-6 on page 165. Example 3-9 starts out by calling ncgi::parse:

Example 3-9 The newguest.cgi script
#!/bin/sh
# \
exec tclsh "$0" ${1+"$@"}

# Use the ncgi package from tcllib to process form data

package require ncgi
ncgi::parse

# Load our data file and supporting procedures

set dir [file dirname [info script]]
set datafile [file join $dir guestbook.data]
source [file join $dir cgihacks.tcl]

# Open the datafile in append mode

if {[catch {open $datafile a} out]} {
   Cgi_Header "Guestbook Registration Error" \
      {BGCOLOR=black TEXT=red}
   puts "<P>Cannot open the data file<P>"
   puts $out;# the error message
   exit 0
}

# Append a Tcl set command that defines the guest's entry

puts $out ""
puts $out [list set Guestbook([ncgi::value name]) \
   [list [ncgi::value url] [ncgi::value html]]]
close $out

# Return a page to the browser

Cgi_Header "Guestbook Registration Confirmed" \
   {BGCOLOR=white TEXT=black}

puts "
<TABLE BORDER=1>
<TR><TD>Name</TD>
<TD>[ncgi::value name]</TD></TR>
<TR><TD>URL</TD>
<TD><A HREF='[ncgi::value url]'>[ncgi::value url]</A></TD></TR>
<TR><TD>Extra HTML</TD>
<TD>[ncgi::value html]</TD></TR>
</TABLE>
"

puts </BODY></HTML>

Using Tcl Scripts to Store Data

The main idea of the newguest.cgi script is that it saves the data to a file as a Tcl command that defines an element of the Guestbook array. This lets the guestbook.cgi script simply load the data by using the Tcl source command. This trick of storing data as a Tcl script saves us from the chore of defining a new file format and writing code to parse it. Instead, we can rely on the well-tuned Tcl implementation to do the hard work for us efficiently.

The script opens the datafile in append mode so that it can add a new record to the end. Opening files is described in detail on page 116. The script uses a catch command to guard against errors. If an error occurs, a page explaining the error is returned to the user. Working with files is one of the most common sources of errors (permission denied, disk full, file-not-found, and so on), so I always open the file inside a catch statement:

if {[catch {open $datafile a} out]} {
    # an error occurred
} else {
    # open was ok
}

In this command, the variable out gets the result of the open command, which is either a file descriptor or an error message. This style of using catch is described in detail in Example 6-14 on page 83.

graphics/common_icon.gif

Use list to generate Tcl commands.


The script writes the data as a Tcl set command. The list command is used to format the data properly:

puts $out [list set Guestbook([ncgi::value name]) \
     [list [ncgi::value url] [ncgi::value html]]]

There are two lists. First, the url and html values are formatted into one list. This list will be the value of the array element. Then the whole Tcl command is formed as a list. In simplified form, the command is generated from this:

list set variable value

Using the list command ensures that the result will always be a valid Tcl command that sets the variable to the given value. This is a very important technique. If you want to generate Tcl commands, the best way to do it is to generate lists using list manipulation commands. The list command is described in more detail on page 65.

    [ Team LiB ] Previous Section Next Section