Building blocks for CGI scripts in Perl

This tutorial provides and explains a kind of erector set for building Common Gateway Interface (CGI) programs with Perl. It is not an introduction to the language (at least, not a good introduction), but describes a collection of program fragments that can be combined, sometimes with slight modifications, to perform common CGI functions. It is intended for users who understand HTML and are willing to explore Perl and the CGI environment together.

Table of Contents

Recommended background

Perl is a computer language developed by Larry Wall for writing programs in a Unix environment and later adopted in many other operating environments. Perl is commonly used for writing CGI scripts for use with the World Wide Web (Web).

For more information about Perl, you might want to review the "Introduction to Perl" by Larry Wall, et al. Marshall Brain of Interface Technologies has written "A Quick Introduction to PERL," which is a succinct introduction for users who already understand programming. For a more general approach to using Perl to write CGI scripts, see A Tour of HTML Forms and CGI Scripts, by Sanford Morton. Morton's document may make a good companion for the document you are now reading; it covers much of the same material from a different point of view.

Some knowledge of HTML and HTML forms processing is necessary before learning to write CGI scripts; for background information about these topics, see

http://www.ncsa.uiuc.edu/General/Internet/WWW/HTMLPrimer.html
for an introduction to HTML and
http://condor.cc.ku.edu/~grobe/docs/forms-intro.shtml
for an introduction to HTML forms and CGI scripts.

This tutorial is somewhat specific to writing scripts within the environment provided by the Unix operating system, in general, and on systems operated by Academic Computing Services (ACS) at The University of Kansas (KU), in particular. The general principles will hold for any environment providing Perl, but the specifics may change.

Users of ACS systems usually store their HTML documents in a directory called public_html in their login directories. The Perl scripts they create are stored in a subdirectory of public_html called cgi-bin. Programs in this directory can be run as CGI scripts on most Unix systems operated by Academic Computing Services (ACS) at the University of Kansas (e.g., FALCON, EAGLE, LARK, RAVEN, etc.).

Writing Perl CGI scripts without forms

As mentioned earlier, this tutorial presents an erector set of Perl program fragments for performing tasks commonly used with CGI scripts. These building blocks allow you to

Note that these building blocks are useful for building scripts that work both with and without forms. Since scripts that work without forms are simpler, we start with those and then enhance those scripts to work with data supplied by forms.

Most readers will find it useful to review the entire tutorial to get the big picture and then concentrate on those building blocks which they want to use in their own CGI scripts.

Sending an HTML document to the user

Most scripts will return a page of information (usually an HTML document) to the Web browser screen after a user submits a request for the script file. Such a page may do as little as thank the person for filling out the form; it may list the data that the user entered; or it may even contain another form that points to another script file.

Perl may be used to return an HTML document by using a program like

#!/usr/local/bin/perl print "Content-type:text/html\n\n"; print <<WEB_PAGE; <html> . . . </html> WEB_PAGE

In this example, the Web document would be placed between the <html> and </html> tags. The first line of the script tells where to find the Perl interpreter, and the second line informs the Web client that it is about to receive an HTML document.

A document returned in this manner may be as simple or as complex as you like. At its most basic, the document is just a simple message to the user. Suppose you want to build a script that simply returns the following document every time it is called:

<html> <title>My Thank You Page</title> <h1>Thank you for reading this document.</h1> See <a href="http://falcon.cc.ku.edu/~username/"> my homepage</a> for more information.

Such a script could be constructed as follows:

#!/usr/local/bin/perl print "Content-type:text/html\n\n"; print <<WEB_PAGE; <html> <title>My Thank You Page</title> <h1>Thank you for reading this document.</h1> See <a href="http://falcon.cc.ku.edu/~username/"> my homepage</a> for more information. </html> WEB_PAGE

If this script is stored in a file called thanks.pl within the cgi-bin directory within the username home directory on falcon.cc.ku.edu, the URL for the script would look like this:

http://falcon.cc.ku.edu/cgiwrap/username/thanks.pl

The script would be started by the FALCON Web server whenever a Web browser requests this URL. Note that a Perl script can be used only on systems that have a Perl interpreter installed. The location of the interpreter must be specified on the first line of the script following a pound sign (#) and an exclamation point (!).

This script makes use of a Perl facility called a here document by Perl programmers. The here document is everything between the lines

print <<WEB_PAGE; and WEB_PAGE Here documents are useful for laying out HTML documents to be returned by scripts.

As you work through the examples below, remember that the first line of each Perl script must specify the location of the Perl interpreter, and on systems operated by ACS that line will be #!/usr/local/bin/perl. Note also that any script that returns an HTML document to the Web browser must print the string Content-type:text/html\n\n to standard out. This is the minimum HTTP (Hypertext Transfer Protocol) required as a header if a script is to return an HTML document. (In fact, if you want your script to provide more header information, you must use a different version of CGIwrap: nph-cgiwrap.) This short header prints the document content type and then two newlines, which cause a blank line to be sent to the WWW client, indicating the end of the header information and the beginning of the document to be sent. For more information about the HTTP header, see the references above.

CGI scripts present several security risks. To protect the user community, Academic Computing Services has installed special software called CGIWrap that must be used to start CGI scripts stored in user home directories. When the script starts, it runs as if you had started it while logged in to your account. That is, it will have potential access to the same files you can access when you run a command. As a result, you must be careful to build scripts that do exactly and only what you expect them to do, which is not always easy.

For a good, short introduction to the security issues in CGI scripting see A Tour of HTML Forms and CGI Scripts.

You can name the script file anything you want, but you might find it useful to use names that end with .pl to remind you that they are Perl scripts. You can use any text editor to create your script file. Note, however, that if you create your script file on your desktop system and upload it to one of the ACS systems, you must save it in "text only" mode and upload it in ASCII mode (using FTP or equivalent).

IMPORTANT: Your script files should probably give access permissions only to the owner. You can assign these permissions by entering

chmod 700 script_filename

Putting comments in your Perl scripts

Perl provides a way to put informational comments in your Perl scripts to help readers understand how the script works. These comments will be seen only by people reading your script file. Users who execute your scripts will not see them. Whenever Perl encounters a pound sign (#), it ignores the rest of the line, so you can make your comments without interferring with the execution of the script.

Running other scripts from yours

Perl allows you to call a script from within a Perl script. In general, you can run a script and send the output of that script to the user with a Perl print command like print `script_filename`; where script_filename is the name of the file that holds the script to be executed by the Perl script. This facility can be used to run file-resident shell commands as well as other Perl or shell scripts. For example, you can use this approach to include the current date in a document: #!/usr/local/bin/perl print "Content-type: text/html\n\n"; print "The current date is "; print `/bin/date`;

Any information written (to standard out) by the script will become part of the HTML document returned to the user. In this example, the date will appear in the HTML document as

The current date is Wed May 8 18:09:14 CDT 1996

This capability allows Perl users to utilize already existing commands and scripts supplied by the operating system or by other users.

An alternative approach is to assign the output of the called script to a Perl variable and then print the variable, as in:

#!/usr/local/bin/perl print "Content-type:text/html\n\n"; $DATE_INFO = `/bin/date`; #assign the date string to $DATE_INFO print <<END_OF_MESSAGE; The current date is $DATE_INFO END_OF_MESSAGE

This approach is more convenient when you wish to embed the date information within a larger document.

Environment variables

Environment variables are variables defined within the operating system or shell at any given time. They typically hold information that may be useful to commands or programs running within the operating environment.

You may use any UNIX environment variable within a Perl script. To use an environment variable, use a string of characters like $ENV{'variable'} where you replace the word variable with the name of the environment variable of interest. For example, to use the REMOTE_HOST environment variable within an Perl script, use the string $ENV{'REMOTE_HOST'}. The following fragment prints the value of the Present Working Directory environment variable, PWD:

#!/usr/local/bin/perl print "Content-type:text/html\n\n"; print <<EOM; The current working directory is $ENV{'PWD'} EOM

The following environment variables are established by the Web server, the Perl interpreter, and/or CGIWrap and may be used in Perl scripts:

DOCUMENT_ROOT
GATEWAY_INTERFACE
HTTP_ACCEPT
HTTP_PRAGMA
HTTP_USER_AGENT
PATH
PATH_INFO
PATH_TRANSLATED
QUERY_STRING
REMOTE_ADDR
REMOTE_HOST
REQUEST_METHOD
SCRIPT_NAME
SERVER_NAME
SERVER_PORT
SERVER_PROTOCOL
SERVER_SOFTWARE
TZ

If you define an environment variable within your (parent) Perl script, (child) scripts started by your (parent) script may use it. If you start child scripts that change the values of environment variables defined in the parent script, however, the parent script will not see the changes: the child script cannot send information back to the parent via environment variables. Environment variable names are case sensitive.

Sending the contents of files to the user

Several techniques send the contents of a file to the Web client, and the easiest are based on the fact that you can send the output from shell commands to the user, as demonstrated in the previous section. For example, you can print a file using the UNIX shell command

cat filename

where filename is the name of the file you want to print, and you can copy the contents of filename to the user with a Perl print command of the following form:

print `cat filename`;

This command will cause the contents of filename to be sent along with whatever else you are sending to the user. For example, suppose you have a signature file named

/home/username/public_html/signature.file

that contains a signature like

<p> See <a href="http://falcon.cc.ku.edu/~username/"> my homepage</a> for more information.

and you want to use this signature with a page you send to the user from a script. You could do that with a sequence of print commands:

#!/usr/local/bin/perl print "Content-type:text/html\n\n"; print <<WEB_PAGE; <html> <title>My Thank You Page</title> <h1>Thank you for reading this document.</h1> WEB_PAGE print `cat /home/username/public_html/signature.file`; print "</html>\n";

Alternatively, you might first use the UNIX cat command to copy the signature file into a Perl variable and then print that variable within a Perl here document:

#!/usr/local/bin/perl print "Content-type:text/html\n\n"; $SIG = `cat /home/username/public_html/signature.file`; print <<WEB_PAGE; <html> <title>My Thank You Page</title> <h1>Thank you for reading this document.</h1> $SIG </html> WEB_PAGE

This approach assigns the contents of the signature file to the variable $SIG and then uses that variable within the document returned to the user. With either approach, the following document will be sent:

<html> <title>My Thank You Page</title> <h1>Thank you for reading this document.</h1> <p> See <a href="http://falcon.cc.ku.edu/~username/"> my homepage</a> for more information. </html>

Including the contents of files in Web pages allows you to make changes to information that will be returned to a user by a script without changing the script itself. This can be very economical if many Perl scripts refer to the same file. For example, you could change your signature information for all documents returned by your scripts by modifying just your signature file instead of all your Perl scripts.

If another script or command has access to the signature file you are trying to copy to the user, your process may not be allowed to copy information from the file. For more information about this kind of problem, see the section titled "Sharing files among running scripts" below.

Building and using counters

Counters are widely used on the World Wide Web to see how many people examine HTML documents. A counter is usually implemented as an ordinary file that is used to hold a single value--the number of accesses to a particular document. To build a counter, you must first create a count file. This file contains only the (ASCII) number at which you would like the counter to start. You can build a count file using any editor on your system. Assuming this file is called counter.file, a Perl script to use it would look like

#!/usr/local/bin/perl # open(COUNTER, "+< /home/smith/counter.file"); # open the counter file with read and write access. $COUNT= <COUNTER>; #read the current value. $COUNT++; #increment it by one. seek(COUNTER, 0 , 0); #rewind the file. print COUNTER $COUNT; #write the new value to the file. close COUNTER;

The line $COUNT++; adds one to the value of the variable $COUNT.

To include the number from the count file in a document, use the print command as before. For example, you might build a Perl script like the following:

#!/usr/local/bin/perl print "Content-type:text/html\n\n"; # #First, increment the counter: # open(COUNTER, "+< /home/smith/counter.file"); # open the counter file with read and write access. $COUNT= <COUNTER>; #read the current value. $COUNT++; #increment it by one. seek(COUNTER, 0 , 0); #rewind the file. print COUNTER $COUNT; #write the new value to the file. close COUNTER; # #Then use the counter in the returned HMTL. # print <<END_OF_PAGE; <html> <title>My first return page</title> <h1>Thank you for selecting this document.</h1> You are visitor number $COUNT </html> END_OF_PAGE

This script reads the counter file into a variable called COUNT, increments the counter variable by one, stores the new value in the counter file, and then returns an HTML document that uses the counter variable.

Sharing files among scripts

If multiple users run this script at the same time, strange things may occur. It is possible, for example, that the two running scripts would read the current count at the same time, increment it by one at the same time, and store the new value in the file, reflecting access by just one user instead of both. The chances of experiencing difficulties due to such concurrent access vary according to the type and frequency of access. In many cases, the frequency of document access is small, so the chances of simultaneous access are small enough to ignore.

However, Perl does include facilities for synchronizing file accesses. Among others, the flock function allows users to lock a file for private use. You could use the line

flock (COUNTER, 2);

to lock the file immediately after opening the file and the line

flock (COUNTER, 8);

to unlock the file immediately before closing it.

This problem applies to other Perl building blocks and to scripts that simultaneously write to, or simultaneously write to and read from the same file, written in any language . In general, the problem can be controlled by giving any writing script private access. That is, if a script is writing, no other writers or readers will be given access until that script is finished. The Perl flock function grants such privacy.

On the other hand, any solution will have drawbacks. For example, it may be inconvenient to lock the file for a long period of time, in which case other approaches may be needed. For more detail on this problem, see the section on file locking in A Tour of HTML Forms and CGI Scripts.

Using Perl with forms

As you know from introductory reading, scripts can be started from HTML forms using the Common Gateway Interface supported by Web HTTP servers. If you are going to start a script from a form, create the form, carefully noting the names of your "fields" (the names of the places you leave for users to fill in data, such as "name" and "address" below); these will be important when you write your script. When you've created the form and you have some idea of what you want your script to do, then you're ready to start.

A simple example form

Suppose you want to build a form that collects e-mail address from users interested in vegetarianism. The following is an example.

<form method="post" action="http://www.cc.ku.edu/cgiwrap/grobe/send-veggi-info.pl"> <P> If you would like more information about vegetarianism, please enter your name and e-mail address below.<P> Please enter your name:<br> <input type="text" name="name" size="35"><br> Please enter your e-mail address:<br> <input type="text" name="address" size="35"><p> <input type="submit" value="send address"> <input type="reset" value="start over"> </form>

This form would be rendered as follows by your Web browser:


If you would like more information about vegetarianism, please enter your name and e-mail address below.

Please enter your name:

Please enter your e-mail address:


You must identify a CGI script for each form that you create. Data from the form above will be processed by the script located at

http://www.cc.ku.edu/cgiwrap/grobe/send-veggi-info.pl

The location of the script that processes information supplied by the form must be provided in the ACTION attribute of the <form> tag. For example, if you have an account on FALCON (falcon.cc.ku.edu), your account is called username, and you have a Perl script in the file script_file, then your form tag would look like this:

<form method="post" action="http://falcon.cc.ku.edu/cgiwrap/username/script_file">

A script to work with the simple example form

Once you've created your form, specified your script file, and given it the correct permissions, you're ready to start composing a Perl script to process your form data. You might use a very simple script to respond to this form:

#!/usr/local/bin/perl print "Content-type:text/html\n\n"; print <<END_OF_MESSAGE; <html> <title>Thank You Page</title> <h1>Thank you for filling out my form!</h1> </html> END_OF_MESSAGE

However, this form does nothing with the data entered by the user. To actually use the data, you must use the HTML form field names in a special way within the Perl script. For example, within the following script, $q->param('name') refers to the contents of the "name" field in the example form above. The result page returned to the user will contain the actual name entered when the fieldname "name" is used in this fashion:

#!/usr/local/bin/perl use CGI; $q = new CGI; $name = $q->param('name'); $address = $q->param('address'); print "Content-type:text/html\n\n"; print <<END_OF_MESSAGE; <html> <title>Thank You Page</title> <h1>Thank you for filling out my form!</h1> Thank you, $name, for filling out my form! I will mail information to $address right away. </html> END_OF_MESSAGE

The alert reader will have noticed the two new lines inserted in the preceding script:

use CGI; $q = new CGI; These lines make sure that the Perl interpreter uses the Perl CGI module and processes the information coming to the script from the user. Without these lines, $q->param('name') will not work properly because new CGI creates the Perl object called $q, which contains the values assigned to every form variable when the user fills out the form.

The Perl CGI module comes with a standard installation of Perl and contains many useful subroutines that perform various functions for CGI script programmers that are not used in this tutorial.

Mailing information to users from a script

You can use a Perl script to e-mail information to you whenever users activate the "Submit form" button on your form. On Unix systems operated by Academic Computing Services at The University of Kansas, the Perl commands to do this take the following form. (Note: if you are not using a Unix system, the command "/usr/lib/sendmail" will probably not work at all; see your local system administrator for assistance.) open (MAIL, "| /usr/lib/sendmail -oi -n -t" ); print MAIL <<MAIL_MESSAGE; To:username\@www.cc.ku.edu From:username\@www.cc.ku.edu You have just sent info about vegetarianism to another interested person. MAIL_MESSAGE close MAIL;

The information in the To: and From: clauses can be any e-mail address. Reply-to: and Return: clauses can be appended to identify to whom any return message should be sent. These are not always the same as the From: address. You can also include a Subject: clause.

After these lines, you may add any text you want, including variables that you have previously set in your program. You might construct the following fragment to mail information to the requestor:

use CGI; $q = new CGI; $address = $q->param('address'); $name = $q->param('name'); open (MAIL, "| /usr/lib/sendmail -oi -n -t" ); print MAIL <<MESSAGE_TO_USER; To:$address From:smith\@raven.cc.ku.edu Dear $name: Here are some books that talk about vegetarianism: Diet for a New America How to Avoid Beef MESSAGE_TO_USER close MAIL;

In this example, "name" and "address" are names of fields specified in the example form, and they would be replaced by the user's name and e-mail address in the actual e-mail message sent to her. In this example, both the mail header information and the enclosed mail message contain references to fieldnames.

Recording information in a file

To record data in a file, you can use a Perl fragment like the following: open (DATAFILE, ">> /home/smith/filename.txt"); flock (DATAFILE, 2); print DATAFILE <<LIST_ITEM; . . . LIST_ITEM flock (DATAFILE, 8); close DATAFILE; where the information you want to record appears between the print DATAFILE <<LIST_ITEM and LIST_ITEM commands. In this example, information is written to the file /home/smith/filename.txt, which you would replace with the name of the file to which your information should be written.

The information written may contain HTML mixed with strings instructing Perl to insert information collected on a form. For example, a a string like $name appearing in the HTML indicates the name variable set previously in your program should be included in the HTML written to the file. This approach might be used in a typical guestbook application as

#!/usr/local/bin/perl use CGI; $q = new CGI; $name = $q->param('name'); $address = $q->param('address'); open (DATAFILE, ">> /home/smith/public_html/guestbook.html"); flock (DATAFILE, 2); print DATAFILE <<RECORD_ITEM; Name: $name Address: $address RECORD_ITEM flock (DATAFILE, 8); close DATAFILE;

The above fragment would append each user's name and e-mail address to a file called guestbook.html in the public_html directory. The information written to the file might look like this:

Name: Michael Grobe Address: grobe@ku.edu Name: Megen Duffy Address: meg@ku.edu

If you are the only one who will look at the file and you are going to use a text editor rather than a Web browser to view the file, you may call it anything you want. If you plan to use the data in a database, you may want to use a standard comma-delimited format and add a .dbf extension to your filename as in

#!/usr/local/bin/perl use CGI; $q = new CGI; $name = $q->param('name'); $address = $q->param('address'); open (DATAFILE, ">> /home/smith/list-of-recipients.dbf"); flock (DATAFILE, 2); print DATAFILE <<LIST_ITEM; \"$name\",\"$address\" LIST_ITEM flock (DATAFILE, 8); close DATAFILE;

In this case, the information written to the file would resemble

"Michael Grobe","grobe@ku.edu" "Megen Duffy","meg@ku.edu"

Typically, a file used to collect data must grant read and write permissions (600) only to its owner since Perl scripts started by CGIWrap run as if the owner of the file containing the scripts has started them manually while logged in. If permissions are assigned in this fashion, only the scripts owned by the file owner can read or record information in the file. The data remains relatively confidential.

If you would like a data file to be used as an HTML document, you must take special steps:

  1. Create the data file and give the owner read and write permissions, and give the group owner and the all other users read permission:

    touch data.html chmod 644 data.html

    When you do this, any user can read the file; it is no longer confidential.

  2. Use any text editor to begin the file with HTML header information (any tags within the <head>...</head> section of the document, plus any section headers). The header information should appear only once in an HTML document, so your script should not put that information on the file. For example, you could initialize the guestbook.html file used in the example above with the following HTML:

    <title>My personal guestbook</title> <h1>Here is a list of the guests who have visited my homepage:</h1> <p> <pre>

    Every time the script runs, it will append a name and address entry to the HTML in this file, so the guestbook list will continue to expand. Note that the <pre> tag is included to make sure whatever browser is used will recognize the line feeds on each line of the new HTML file when it is viewed as a document.

Once you have performed these steps, the data file may be used like any other HTML document.

A complete example

Now that you know the basics of scripting with Perl, you can begin to combine the various building blocks to design more complicated scripts. Here is an example script that collects and processes information from the example vegetarianism form presented above. It uses several of the building blocks that have been presented to perform a variety of functions.

The script collects the name and address information submitted by the user, records that information in a data file, returns a summary of the record to the user who entered the information, and sends a mail message to the form creator to let her know more data has arrived. This script uses the CGI module to collect the form information submitted by the user and relayed to the script by the Web server.

This example would be referenced with the URL

http://raven.cc.ku.edu/cgiwrap/grobe/send-veggi-info.pl

if it were to be run from the public_html/cgi-bin directory within the home directory for the account grobe.

#!/usr/local/bin/perl # # This script sends out information about Vegetarianism, to users # who fill out a form asking for their names and e-mail addresses. # # The script mails information to the user and mails a message # indicating it has mailed information to the user to the script owner. # # The script also records information about the request in a database # file for subsequent use by other programs. # # Written by Michael Grobe...5-8-96. # Updated by Jeff Long...9-15-2000. # # Send the html document MIME type. # print "Content-type: text/html\n\n"; # # parse the input information and retrieve arguments from the form. # use CGI; $q = new CGI; # get the form arguments. $name = $q->param('name'); $address = $q->param('address'); # # respond with an html file to the user. # print <<WEB_PAGE; <html> <h1>Vegetarians unite!</h1> A list of books about vegetarianism will be sent to $name at $address. </html> WEB_PAGE # # Mail the information to the user using the Unix sendmail command. # sendmail -n ignores alias file. # sendmail -t examines stdin for To: list of addressees # sendmail -oi does not stop with a line containing only a period # open (MAIL, "| /usr/lib/sendmail -oi -n -t" ); print MAIL <<MESSAGE_TO_USER; To:$address From:grobe\@raven.cc.ku.edu Dear $name: Here are some books that talk about vegetarianism: Diet for a New America How to Avoid Beef MESSAGE_TO_USER close MAIL; # # Record the request in a datafile in comma delimited format. # open (DATAFILE, ">> /homea/grobe/list-of-recipients.txt"); flock (DATAFILE, 2); print DATAFILE <<LIST_ITEM; \"$name\",\"$address\" LIST_ITEM flock (DATAFILE, 8); close DATAFILE; # # send a message to the script owner that info has been sent to the user. # # sendmail -n ignores alias file. # sendmail -t examines stdin for To: list of addressees # sendmail -oi does not stop with a line containing only a period # open (MAIL, "| /usr/lib/sendmail -oi -n -t" ); print MAIL <<MAIL_MESSAGE; To:grobe\@raven.cc.ku.edu From:grobe\@raven.cc.ku.edu You have just sent info about vegetarianism to $name at $address. The requestor's name and address have been recorded in /homea/grobe/list-of-recipients.txt. MAIL_MESSAGE close MAIL; exit; # end of user script.

In a later example, you will see one way to use the information collected by this script.

Handling checkbox and multiple select variables

Checkbox variables are handled somewhat differently from other form variable types. Typically, checkbox fields in an HTML form all have the same name, and the values collected from each field are sent separately by the Web client. When the CGI module receives the values, it places them into an array of values. Perl programs may need to extract individual checkbox values from this array for specific purposes.

Suppose you want to find out what browsers your Web community uses. You could set up a survey form to collect and record information provided by a form containing a checkbox variable:

<html> <FORM METHOD=POST ACTION="http://www.ku.edu/cgiwrap/grobe/test-checkbox.pl"> Please help us to improve KUfacts by filling in the following questionnaire: <P> Your organization? <INPUT NAME="org" TYPE=text SIZE="48"> <P>Which browsers do you use? <OL> <LI>Lynx <INPUT NAME="browsers" TYPE=checkbox VALUE="Lynx"> <LI>Mosaic <INPUT NAME="browsers" TYPE=checkbox VALUE="Mosaic"> <LI>Netscape <INPUT NAME="browsers" TYPE=checkbox VALUE="Netscape"> <LI>Internet Explorer <INPUT NAME="browsers" TYPE=checkbox VALUE="Explorer"> <LI>Others <INPUT NAME="browsers" TYPE=checkbox VALUE="Others"> </OL> Your e-mail address: <INPUT NAME="address" SIZE="42"> <P>Thanks for your input. <P><INPUT TYPE=submit value="Submit survey"> <INPUT TYPE=reset> </FORM> </body> </html>

On your browser this form would appear as


Please help us to improve KUfacts by filling in the following questionnaire:

Your organization?

Which browsers do you use?

  1. Lynx
  2. Mosaic
  3. Netscape
  4. Internet Explorer
  5. Others
Your e-mail address:

Thanks for your input.


When the form is submitted, the information the client sends to www.cc.ku.edu looks something like this:

POST cgiwrap/grobe/test-checkbox.pl HTTP/1.0 Accept: www/source Accept: text/html Accept: video/mpeg Accept: image/jpeg Accept: image/x-tiff Accept: image/x-rgb Accept: image/x-xbm Accept: image/gif Accept: application/postscript User-Agent: Lynx/2.2 libwww/2.14 From: grobe@www.cc.ku.edu Content-type: application/x-www-form-urlencoded Content-length: 150 org=Academic%20Computing%20Services &browsers=Lynx &browsers=Mosaic &browsers=Netscape &address=grobe@ku.edu

Note that multiple values are sent for the checkbox variable "browsers".

The following script could be used to return a summary of the respondent's reply. This script collects the form information and then prints a list of each browser that was checked. The CGI module will return all of the values for the variable browsers in a single array that can be obtained by @BROWSERS = $q->param('browsers').

#!/usr/local/bin/perl use CGI; $q = new CGI; print "Content-type:text/html\n\n"; @BROWSERS = $q->param('browsers'); $org = $q->param('org'); $address = $q->param('address'); print "<html>"; print "<h1>Thanks for filling out the survey</h1>"; print "We will record your information as follows:<p>"; print "$org uses the following browsers:"; print "<ol>"; foreach $BROWSER (@BROWSERS) { print "<li>$BROWSER \n"; } print "</ol>"; print "The contact address is:<br>$address"; print "</html>"; exit;

This script places the browser names into separate entries in an array called BROWSERS. It then uses a foreach loop to print each value along with an <li> tag, so the values will appear as entries in an ordered list. The Perl variable $BROWSER holds each value from the BROWSERS array in turn as the loop repeats.

This script could be modified to record the reply on a data file or do other things with the data submitted.

The checkbox information in this example could have been collected by using a "multiple select" of the form:

<select multiple name="browsers"> <option> Lynx <option> Mosaic <option> Netscape <option value="Explorer">Internet Explorer <option> Others </select>

In either case the information submitted for the variable browsers will be handled the same.

Doing things selectively

The Perl if statement allows you to compose more complex scripts which do different things based on what the user submits in the HTML form. You could use the if structure to display special messages only to people with a certain name or domain name, for example. The if structure takes the following form:

if ( some logical expression ) { some Perl commands }

For example, suppose you would like to greet anyone whose name is the same as yours with a special message in your result page. You could enclose the following if structure inside your result page:

if ($q->param('name') eq "Megen") { print "Hi Megen!"; }

Each time a user submitts a form which contains "Megen" in the "name" field, the user will be greeted with a return page which includes "Hi Megen!"

Perl also supplies an accompanying else clause which allows even more latitude in constructing scripts. The else structure looks like:

if ( some logical expression ) { some Perl commands } else { some other Perl commands }

You could use the else clause to present a different message to any users who weren't named "Megen":

if ( $q->param('name') eq "Megen" ) { print "Hi Megen!"; } else { print "Hi Dude!"; }

The else clause returns, "Hi Dude!" whenever someone who is not named Megen submits the form. If the else clause were omitted, no greeting at all would be returned to such users.

Of course, the user might enter her name as "Megen Smith", in which case the comparison with "Megen" fails even though the user's name really is Megen. One way around this is to search for the string "Megen" within the field value submitted instead of testing for equality. You can use a Perl regular expression search to do just that as follows:

$_ = $q->param('name'); # first store the field value in # a special variable called $_. if ( /Megen/ ) # then look for the string "Megen" in $_ { print "Hi Megen!"; }

If the user enters a string that contains the substring "Megen" anywhere within it, she will be greeted as "Megen".

However, you really can't be sure whether Megen will type her name with a leading capital "M". If she types "megen" or "MEGEN SMITH", the if statement above will not respond with "Hi Megen!" To make sure case sensitivity doesn't confuse things, you could simply add the letter "i" after the search string, making it: /Megen/i.

There are many other functions and operators available within Perl and other ways to perform these same searches. See the Perl tutorials for more information.

Combodocs

If a Perl script is started with no form data, the method param() returns "false", so the calling script can tell whether data were submitted. If there is no form data, the script can return a form to collect some data. If there is data, the script can processes it. Here is a code outline, showing one way to structure such a script:

#!/usr/local/bin/perl use CGI; $q = new CGI; print "Content-type: text/html\n\n"; if (! $q->param()) #send the form { print <<A_FORM; ... A_FORM } else #process the form data { ... } exit;

Basically, when the script is called, it executes the first part of the if statement which generates the form. Then, once the form has been sent to the user and the user has filled it out and returned it, the script executes the else part of the if statement to process the data. Think of it as a "Buy One, Get One Free" concept. The script does two things for you: generates a form and then processes it.

One of the reasons for using a combodoc is to help you synchronize the form with the script. If you keep a form together with the program that processes it, it is harder to modify one without attending to the other.

An example "combodoc"

Here is an example script that creates the form whose data it processes. This program would reside in http://falcon.cc.ku.edu/cgiwrap/classX/veg-search.pl

#!/usr/local/bin/perl
use CGI;
$q = new CGI;
print "Content-type: text/html\n\n";

if (! $q->param())    #send the form
{
   print <<FORM;
<title> Search the Vegetarian Interest Database </title>
<form method="post" 
action="http://falcon.cc.ku.edu/cgiwrap/classX/veg-search.pl">
<p>
<h1>Search the Vegetarian Database</h1>
To get the e-mail address for someone who has filled out the 
Vegetarian Interest Form enter the name of the person below.
<p>
Name to search for:<p>
<input type="text" name="name" size="35">
<p>
<input type="submit" value="send address">
<input type="reset" value="start over">
</form>
FORM
} 
else         #process the form data
{
   open (DATAFILE, "</homef/classX/list-of-recipients.dbf");
   flock (DATAFILE, 2);
   $name = $q->param('name');
   
   print "Here are the e-mail addresses for $name:<p>\n";

   while ($RECORD=<DATAFILE>)
   {
     ($NAME,$ADDRESS)=split("\0",$RECORD);
     if ($NAME eq $name)
     {
         print "<br>$ADDRESS\n";
     }
   } #endwhile

   flock (DATAFILE, 8);
   close DATAFILE;

} #endif

exit;
This script sends a form to the user who wants to search for a name in the vegetarian database. When a name is submitted, the script searches the list of users who have asked for information about vegetarian issues, and returns information connected with the name submitted.

Michael Grobe
Megen Duffy
Hasan Naseer
Jeff Long
Academic Computing Services
The University of Kansas
First version: Sometime in 1995
Current version: September 15, 2000