Building-blocks for CGI Scripts in Perl

Introduction

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 Common Gateway Interface (CGI) scripts for use with the World Wide Web (Web).

This tutorial provides a kind of erector set for building CGI programs with Perl. It is not an introduction to the language (or at least not a good introduction), but rather a collection of program fragments that can be combined, sometimes with slight modifications, to perform common CGI functions.

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

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://kuhttp.cc.ukans.edu/info/forms/forms-intro.html
for an introduction to HTML forms and CGI scripts.

How to use this tutorial

You should already have a directory in which you keep your HTML documents. Usually this directory is public_html in your login directory. The Perl scripts you create should be in a subdirectory of public_html called cgi-bin. Programs in this directory can be run as CGI scripts on any system operated by Academic Computing Services (ACS) at The University of Kansas (e.g., FALCON, LARK, RAVEN, etc.).

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 World Wide 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.ukans.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.ukans.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.ukans.edu, the URL for the script would look like this:

http://falcon.cc.ukans.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 and that the location of the interpreter is 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 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, of course, the minimum HTTP required as a header if a script is to return an HTML document. 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 an easy task.

For more info about running scripts on systems operated by Academic Computing Services see CGI Scripting at the University of Kansas. For a good, short introduction to the security issues in CGI scripting see A Tour of HTML Forms and CGI Scripts. For a more detailed discussion of these issues see the CGI Security Tutorial by Michael Van Biesbrouck.

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.

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 only be seen 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 the child scripts that you start change the values of environment variables defined in the parent script, however, the parent script will not see the changes. That means 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

There are several techniques for sending 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.ukans.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 like :

#!/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";

or, 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.ukans.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. So, to build a counter you must first create a count file. All this file contains is 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 an 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 first 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 that 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, in the Perl code above.

This problem applies to other Perl building blocks to be presented later, and to scripts written in any language that simultaneously write to, or simultaneously write to and read from the same file. 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.

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://ukanaix.cc.ukans.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://ukanaix.cc.ukans.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 (commonly known as falcon.cc.ukans.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.ukans.edu/cgiwrap/username/script_file">

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 fieldnames specified within the HTML form in a special way within the Perl script. For example, within the following script, $in{'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 whenever the fieldname "name" is used in this fashion:

#!/usr/local/bin/perl require ("/usr/local/lib/perl5/cgi-lib.pl"); &ReadParse; 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, $in{'name'}, for filling out my form! I will mail information to $in{'address'} right away. </html> END_OF_MESSAGE

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

require ("/usr/local/lib/perl5/cgi-lib.pl"); &ReadParse; Those lines make sure that the Perl interpreter finds the Perl CGI library stored in /usr/local/lib/perl5/cgi-lib.pl and starts a Perl subroutine called ReadParse which processes the information coming to the script from the user. Without these lines $in{'name'} will not work properly, because ReadParse creates the Perl "associative array" called $in, that contains the values assigned to every form variable when the user filled out the form.

The cgi-lib.pl package was written by Steven Brenner of Cambridge. It includes a number of subroutines that perform various functions for CGI script programmers, but only ReadParse is used in this tutorial.

The cgi-lib.pl package has been installed for public access on KU systems operated by Academic Computing Services from:

/usr/local/lib/perl5/cgi-lib.pl but you can get it via the link above, if it's not on the system you are using.

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. The Perl commands to do this take the following form: open (MAIL, "| /usr/lib/sendmail -oi -n -t" ); print MAIL <<MAIL_MESSAGE; To:username\@ukanaix.cc.ukans.edu From:username\@ukanaix.cc.ukans.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. A Subject: clause can also be included.

After these lines, you may add any text you want, including fieldnames embedded in $in{...} strings. You might construct the following fragment to mail information to the requestor:

require ("/usr/local/lib/perl5/cgi-lib.pl"); &ReadParse; open (MAIL, "| /usr/lib/sendmail -oi -n -t" ); print MAIL <<MESSAGE_TO_USER; To:$in{'address"} From:smith\@ukanaix.cc.ukans.edu Dear $in{'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 contained 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 and/or strings instructing Perl to insert information collected on a form. For example, the HTML may include a string like $in{'name'} indicating the name entered by the user 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 require ("/usr/local/lib/perl5/cgi-lib.pl"); &ReadParse; open (DATAFILE, ">> /home/smith/public_html/guestbook.html"); flock (DATAFILE, 2); print DATAFILE <<RECORD_ITEM; Name: $in{'name'} Address: $in{'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@ukans.edu Name: Megen Duffy Address: meg@ukans.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 World Wide 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 require ("/usr/local/lib/perl5/cgi-lib.pl"); &ReadParse; open (DATAFILE, ">> /home/smith/list-of-recipients.dbf"); flock (DATAFILE, 2); print DATAFILE <<LIST_ITEM; \"$in{'name'}\",\"$in{'address'}\" LIST_ITEM flock (DATAFILE, 8); close DATAFILE;

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

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

Typically, a file used to collect data must grant read and write permissions (600) only to its owner since Perl scripts started by CGI-WRAP 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 will be able to read the file; it will no longer be 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>

    Everytime 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 ReadParse subroutine from the cgi-lib.pl library written by Steven Brenner 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://ukanaix.cc.ukans.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 send 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. # # Send the html document MIME type. # print "Content-type:text/html\n\n"; # # parse the input information and retrieve arguments from the form. # require ("/usr/local/lib/perl5/cgi-lib.pl"); &ReadParse; #get the form arguments. # # 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 $in{'name'} at $in{'address'}. </html> WEB_PAGE # # Mail the information 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 <<MESSAGE_TO_USER; To:$in{'address'} From:grobe\@ukanaix.cc.ukans.edu Dear $in{'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; \"$in{'name'}\",\"$in{'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\@ukanaix.cc.ukans.edu From:grobe\@ukanaix.cc.ukans.edu You have just sent info about vegetarianism to $in{'name'} at $in{'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.

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 ReadParse receives the values, it concatenates them into a single string of characters, separating adjacent values with a null character (\0). Perl programs may need to extract individual checkbox values from this single string for specific purposes.

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

<html> <FORM METHOD=POST ACTION="http://www.ukans.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 sent to www.cc.ukans.edu by the client using the HTTP protocol would look 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@ukanaix.cc.ukans.edu Content-type: application/x-www-form-urlencoded Content-length: 150 org=Academic%20Computing%20Services &browsers=Lynx &browsers=Mosaic &browsers=Netscape &address=grobe@ukans.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". ReadParse will return all of the values for the variable "browsers" in a single string that can be referenced as $in{'browsers'}. The entries will be separated by null characters (\0).

#!/usr/local/bin/perl require ("/usr/local/lib/perl5/cgi-lib.pl"); &ReadParse; print "Content-type:text/html\n\n"; @BROWSERS = split( "\0", $in{'browsers'} ); print "<html>"; print "<h1>Thanks for filling out the survey</h1>"; print "We will record your information as follows:<p>"; print "$in{'org'} uses the following browsers:"; print "<ol>"; foreach $BROWSER (@BROWSERS) { print "<li>$BROWSER \n"; } print "</ol>"; print "The contact address is:<br>$in{'address'}"; print "</html>"; exit;

This script uses the split function to separate 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 ($in{'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 ( $in{'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" would fail, 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:

$_ = $in{'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 other ways to perform these same searches, and many other functions and operators available within Perl. See the Perl tutorials for more information.

Michael Grobe
Megen Duffy
Academic Computing Services
The University of Kansas
July 3, 1996