Copyright ©1996, Que Corporation. All rights reserved. No part of this book may be used or reproduced in any form or by any means, or stored in a database or @retrieval system without prior written permission of the publisher except in the case of brief quotations embodied in critical articles and reviews. Making copies of any part of this book for any purpose other than your own personal use is a violation of @United States copyright laws. For information, address Que Corporation, 201 West 103rd Street, Indianapolis, IN 46290 or at support@mcp.com.

Notice: This material is excerpted from Special Edition Using HTML, 2nd Edition, ISBN: 0-7897-0758-6. This material has not yet been through the final proof reading stage that it will pass through before being published in printed form. Some errors may exist here that will be corrected before the book is published. This material is provided "as is" without any warranty of any kind.

Chapter 24 Sample Code and CGI Scripts

by Ken Murphy

Nestled snugly out of sight on most Web sites are CGI scripts busily giving the site its dynamic look and feel. Scripts allow you to include, for example, @on-line forms, animations, search engines, and customized @Web pages on your @Web site.

By far, form processing is the most common use of @CGI scripts on the Web today. Whether you have your own business and would like an @on-line order form or you just want to give people stopping by a cool way to leave you a message, you need forms on your Web site. Luckily, processing forms with CGI scripts is fairly easy.

In this chapter, you do the following:

Form Output

Every time you send information via a form on a Web site, you must first package that information into a special standardized format for CGI scripts to process. In this section, you learn how the form output is formatted and where you can get a simple PERL script to take this formatted information and change it into a form that is easy to work with.

The Output Format

Each time you fill out a form and send the information, the @CGI script specified in the form's @ACTION attribute is executed to process the information.

When the browser sends this information to the @Web server for processing by a @CGI script, it is sent in a standard format. This format is basically a long string of name/value pairs. The @NAME portion of each pair comes from the @NAME attribute within the form field designations, and the @VALUE half of the pair is the information that is entered into the form for that field. The browser then uses an = (equal sign) to separate these two halves of the pair. Any blank spaces between words in the form are replaced with + (plus signs). Finally, an & (ampersand symbol) is placed between each set of name/value pairs.

When a special, @non-7-bit ASCII character is entered into a form, the character is sent as a percent sign followed by its @hexadecimal equivalent. The same occurs for such common symbols as +, =, %, and & if they appear as form input.

For example, if you visit my guestbook at http://www.missouri.edu/~bchemkm/guestbook.htm and take a peek at the source code, you see a line similar to the following:

<FORM METHOD="POST" ACTION="http://www.missouri.edu/bchemkm-bin/framebook.pl">

As mentioned previously, the @ACTION attribute indicates the location of the @CGI script that is executed by the server and given the information in the form. If you enter your name and a comment in the form and click on the submit button, the PERL CGI script framebook.@pl is executed by the @Web server. At this time, your browser passes your name and the comment to the @CGI script in the following format:

?name=your+name&comments=your+comment+goes+here

Luckily, you normally don't need to remember how this works. As you learn later in this chapter, a freely available PERL program can decode the form's information for you.

Using cgi-lib.pl for Form Processing

Thanks to the standard format that forms use to send information to CGI scripts, you can easily write a CGI script to handle this information. In fact, this task is so simple that you don't even have to write a script. Just go to Steven Brenner's Web site at http://www.bio.cam.ac.uk/cgi-lib/, and ftp a copy of cgi-lib.pl. This library is a collection of several short subroutines that you can use to make processing forms easier. The most important subroutine in this library is called ReadParse. This subroutine actually processes the form's information into a format that you can work with by turning the name/value pairs into the keys and values of an associative array.

Once you have cgi-lib.pl downloaded, remember to place it in a directory that is set up for CGI scripts. After doing so, make sure that the file permissions have been set correctly. To do so, make sure that the Web server has permission to read and execute the file. If you are using a UNIX server, one method is to go to the script's directory and type the following command at the @UNIX prompt:

$ chmod 755 cgi-lib.pl

This command sets the file's permissions to the following:

rwxr-xr-x

The permission value used indicates that all users have permission to read and execute this file, but only the owner can write to it.

See "Can You Write CGI Scripts"

Sample E-Mail Form

The first form that you learn about in this chapter is a simple e-mail form called mail.htm that your visitors can use to send you e-mail (see listing 24.1) if you are using a server that has the sendmail utility installed. The simple @CGI script used to process this form contains many features that carry over into the more complex scripts that are discussed later in this chapter.

Listing 24.1 Sample e-mail form.

<HTML>
<HEAD>
<TITLE>E-Mail Form</TITLE>
</HEAD>
<BODY TEXT="#000000" BGCOLOR="#FFFFFF">
<CENTER>
<!--The action attribute below must be set to the correct URL-->
<!--for the place you put the email.pl file on your server.-->
<FORM METHOD="POST" ACTION="http://www.missouri.edu/bchemkm-bin/email.pl">
<TABLE BORDER=10 CELLSPACING=10 CELLPADDING=2>
<TR>
<TH COLSPAN=2>E-Mail Form<BR></TH>
</TR><TR>
<TD ALIGN=RIGHT><B>Name of Sender:</B></TD>
<TD><INPUT SIZE=40 TYPE="text" NAME="from"><BR></TD>
</TR><TR>
<TD ALIGN=RIGHT><B>Subject:</B></TD>
<TD><INPUT SIZE=40 NAME="subject"><BR></TD>
</TR><TR>
<TD COLSPAN=2 ALIGN=CENTER><B>Body of Mail:</B><BR>
<TEXTAREA WRAP=VIRTUAL NAME="message" COLS=50 ROWS=10></textarea><BR></TD>
</TR><TR>
<TD ALIGN=CENTER><INPUT TYPE="submit" VALUE="Send Mail"></TD>
<TD ALIGN=CENTER><INPUT TYPE="reset" VALUE="Clear"><BR></TD>
</TR>
</TABLE>
</FORM>
</CENTER>
</BODY>
</HTML>

How the Script Works

The first thing you should notice when looking at this CGI script in listing 24.1 is that, in PERL, all variables begin with a $ (dollar symbol), making them easy to spot. @PERL also uses a @ symbol to indicate a standard array and a % symbol to indicate an associative array. Likewise, each subroutine that is called is indicated by an & before its name. This script uses the ReadParse subroutine from the cgi-lib.@pl library, which was discussed in the preceding section. This subroutine takes the form's output and puts it into an associative array that allows the @CGI script to handle it easily.

One common practice when writing @PERL CGI scripts is to initialize the script's variables near the beginning. This way, it is easier to go back in later and make minor changes to the values of these variables. The assignment of the major variables is usually followed by the body of the program. The body of this script is designed to send the information from the @e-mail form to you as a formatted e-mail message letting you know someone has dropped by your site, and to send a brief thank you message to the visitor letting him or her know that everything went smoothly.

For this script to function properly on your system, you must be using a @UNIX server with the @sendmail utility installed and then properly set several variable's values within this script. The $myname variable should contain your name as you want it to appear on the To: line of your e-mail. Likewise, the $myemail should contain your full e-mail address. Finally, you need to set the path for the $sendmail variable to indicate the location of this program on your server.

If you are using a different type of server or your server doesn't have the @sendmail utility installed, you will need to determine what e-mail utility is provided by your system. Once you have that information, you will then need to look at that utility's documentation to determine how to send an e-mail from a file via the comand line and subsitute that information for the call to the sendmail utility in this program.

If you are using a UNIX server, you can find a complete listing of all the sendmail command-line flags by typing man sendmail at a UNIX prompt.

You should understand two main features in the body of the script. First, the script opens a @filehandle called MAIL to the @sendmail program on your server. Filehandles allow the @PERL program to interact with something other than a browser, which is considered the standard output device for the server during @Web surfing. After the @filehandle to the @sendmail program is opened, the script prints the @e-mail to the @sendmail program using the @filehandle to redirect the information away from your visitor's browser. Second, after the e-mail portion of the script is finished, the script proceeds to send a brief HTML document to their browser to let them know that everything went smoothly after he or she submitted the form. The easiest way for the script to generate this @HTML document is to include normal @HTML code as the body of a concatenated print statement.

Remember that when using the sendmail utility, the To: line must be the first line in the mail message. Also, you must left-justify the line so that the T is the first character in this line. If you tab it over or put any blank spaces before the To:, the @sendmail program will not send the @e-mail.

Consider this friendly advice when you're writing e-mail subroutines for your CGI scripts. First, avoid writing a CGI script that allows users to input the To: address for an e-mail subroutine. If you do so, anyone can send hate or threatening e-mail using your server, leaving you partly responsible. Also, note that sending unsolicited @e-mail is considered a breach of @Internet etiquette.

The HTML E-mail Form's CGI Script

By carefull examination of the email.pl CGI scirpt in listing 24.2 you will soon find that e-mail scripts are quite easy to write. Once you become more familiar with PERL scripting, you should be able to take this small script, modify it, and use it as a subroutine in other scripts to give them similiar e-mail capabilities.

Listing 24.2 E-mail form's CGI script.

#!/usr/local/bin/perl
require "cgi-lib.pl";
&ReadParse;
print "Content-type: text/html\n\n";
# Sendmail options
#  -n no aliasing
#  -t read message for "To:"
#  -oi don't terminate message on line containing '.' alone
#  -f this is to be used when you need to specify who is sending it for
#     security reasons or for a listsrv
$sendmail = "/usr/lib/sendmail -t -oi -n";
$from = $in{'from'};
$subject = $in{'subject'};
$message = $in{'message'};
$myemail = 'bchemkm@showme.missouri.edu';
$myname = "Kenneth E. Murphy";
open (MAIL,"|$sendmail") || die "<HTML><BODY>Failed in opening sendmail</BODY></HTML>\n";
print MAIL <<SOM;
To: $myname <$myemail>
From: $from
Subject: $subject

$message
SOM
print <<BROWSER;
<HTML>
<HEAD><TITLE>Mail Results</TITLE></HEAD>
<BODY BGCOLOR="#FFFFFF">
Thank You!<P>
I will answer your letter ASAP.
</BODY>
</HTML>
BROWSER

Input/Output Look

If you get the e-mail form mail.htm up and running, it should look like figure 24.1. Each time someone uses this form at your site, you will get an e-mail that will look like figure 24.2 if viewed using Netscape 2.0's e-mail utility. After you have everything working correctly, you can modify the form's look to match the style for the rest of your Web site.

Fig. 24.1

The e-mail form.

Fig. 24.2

Here is what the e-mail looks like viewed with Netscape Mail.

As a general rule, you must use complete URLs in hyperlinks that are on pages loaded to the browser by one of your CGI scripts. When the server tries to follow one of these hyperlinks, it does so from your script's directory, not your HTML page directory as it usually does. So, if you use a relative URL, the server will not be able to find the desired file.

A Color Conversion CGI Script

Hopefully, now you know how to place a working e-mail form on your Web site. Next, you learn how a different type of CGI form processing script works. The script in listing 24.3 allows you to input color values in either @hexadecimal or decimal and then displays the resulting conversion between the two types of units in a table. So that this process is a little more fun, the script uses the entered color codes as the background and text colors for the page that it generates. Each time the script runs, it generates a new form using the color settings from the form. Thus, you can change the colors and see what they look like as many times as you want.

How the Script Works

The beginning of the script in listing 24.3 looks a lot like the @e-mail form's script because this script also reads in data from a form. First, the script stores the values from three of the form fields into variables with the same name. Then the script defines the background and text colors that are used in the event that the script is run for the first time. Because this script generates its own form, the first time it is executed, it needs to display a background color and text color even though you have not had a chance to enter their values into the form yet.

The hex() operator used with some variables takes a @hexadecimal value and returns the corresponding decimal value, which is stored in the variable for later use. The sprintf("%lx, $variable_name) portion of some statements takes the specified variable containing a decimal number and converts it into @hexadecimal.

This script checks to see if the information is entered in @hexadecimal format, which is the standard for @HTML color attributes. If so, the values are in the correct format for use in the @HTML form the script generates. Thus, the script only needs to convert these values to their decimal equivalents for display in the @conversion table that appears under the form. However, if the background and text colors are entered in decimal format, they need to be converted into @hexadecimal for the @HTML tags and for the @hexadecimal portion of the conversion table.

When you define a variable in PERL, you can use the . (period) to concatenate two string arguments into a single string.

After all the color variables are set properly for use, the script creates the form and sends it to the browser. Most of this form is just standard HTML. However, variables are needed to indicate the background and text colors correctly. Also, remember to change the file path in the ACTION attribute of the form tag to the complete file path of this script. This script also uses table tags to format the form fields. The input field's @VALUE attributes are specified using the appropriately defined variable.

Also the script needs to set a flag within the form to let the script know if it is receiving input from the form, or if the script is being executed for the first time and the form has not been sent yet. If the script is being executed for the first time, the script needs to use its own values for the background and text colors.

After the new form is generated, a second table is created; this table displays the inputted hexadecimal or decimal color codes and displays their values. As an extra feature, the script generates a <BODY> tag line that is viewable on the screen. The attributes indicate the values used to generate the current version of the form and can be pasted into other @HTML documents to give them the same color settings that are on the form.

The CGI Script

The colorex.@pl script may seem a little intimidating at first glance, but with a closer look at listing 24.3 you should notice that the bulk of this script is very uniform and highly repetative. Thus, the length of a script is not a good indicator of it's overall complexity.

Listing 24.3 Color conversion CGI script.

#!/usr/local/bin/perl
require "cgi-lib.pl";
&ReadParse(*in);

print "Content-type: text/html\n\n";
#########################################################
$dec          =     "$in{'dec'}";
$hex          =     "$in{'hex'}";
$hidden          =     "$in{'hidden'}";
#########################################################
if ($hidden ne "1") {
$bgcolor_red = "EE";
$bgcolor_green = "EE";
$bgcolor_blue = "EE";
$text_red = "00";
$text_green = "00";
$text_blue = "AA";
$dec_bgcolor_red = hex($bgcolor_red);
$dec_bgcolor_green = hex($bgcolor_green);
$dec_bgcolor_blue = hex($bgcolor_blue);
$dec_text_red = hex($text_red);
$dec_text_green = hex($text_green);
$dec_text_blue = hex($text_blue);
}
if ($hex eq "hex") {
$bgcolor_red = $in{'bgcolor_red'};
$bgcolor_green = $in{'bgcolor_green'};
$bgcolor_blue = $in{'bgcolor_blue'};
$text_red = $in{'text_red'};
$text_green = $in{'text_green'};
$text_blue = $in{'text_blue'};
$dec_bgcolor_red = hex($bgcolor_red);
$dec_bgcolor_green = hex($bgcolor_green);
$dec_bgcolor_blue = hex($bgcolor_blue);
$dec_text_red = hex($text_red);
$dec_text_green = hex($text_green);
$dec_text_blue = hex($text_blue);
}
if ($dec eq "dec") {
$dec_bgcolor_red = $in{'bgcolor_red'};
$dec_bgcolor_green = $in{'bgcolor_green'};
$dec_bgcolor_blue = $in{'bgcolor_blue'};
$dec_text_red = $in{'text_red'};
$dec_text_green = $in{'text_green'};
$dec_text_blue = $in{'text_blue'};
$bgcolor_red = sprintf("%lx", $dec_bgcolor_red);
$bgcolor_green = sprintf("%lx", $dec_bgcolor_green);
$bgcolor_blue = sprintf("%lx", $dec_bgcolor_blue);
$text_red = sprintf("%lx", $dec_text_red);
$text_green = sprintf("%lx", $dec_text_green);
$text_blue = sprintf("%lx", $dec_text_blue);
}
$bgcolor = "$bgcolor_red" . "$bgcolor_green" . "$bgcolor_blue";
print <<formpage;
<HTML>
<HEAD>
<TITLE>The Color Converter</TITLE>
</HEAD>
<BODY TEXT="#$text_red$text_green$text_blue"
BGCOLOR="#$bgcolor">
<CENTER>
<H2>The Color Converter</H2>
<FORM METHOD="POST" ACTION="http://www.missouri.edu/bchemkm-bin/colorex.pl">
<TABLE BORDER=5 CELLSPACING=10>
<TR>
<TH COLSPAN=4>COLOR TEST SUITE<BR></TH>
</TR><TR>
<TD WIDTH=130 ALIGN=CENTER></TD>
<TH WIDTH=50 ALIGN="CENTER">RED</TH>
<TH WIDTH=50 ALIGN=CENTER>GREEN</TH>
<TH WIDTH=50 ALIGN=CENTER>BLUE<BR></TH>
</TR><TR>
<TD WIDTH=130 ALIGN=CENTER><B>BGCOLOR: </B></TD>
<TD WIDTH=50 ALIGN=CENTER>
<INPUT TYPE=TEXT SIZE=5 MAXLENGTH=3 
NAME="bgcolor_red" VALUE="$bgcolor_red"></TD>
<TD WIDTH=50 ALIGN=CENTER>
<INPUT TYPE=TEXT SIZE=5 MAXLENGTH=3 
NAME="bgcolor_green" VALUE="$bgcolor_green"></TD>
<TD WIDTH=50 ALIGN=CENTER>
<INPUT TYPE=TEXT SIZE=5 MAXLENGTH=3 
NAME="bgcolor_blue" VALUE="$bgcolor_blue"><BR></TD>
</TR><TR>
<TD WIDTH=130 ALIGN=CENTER><B>TEXT: </B></TD>
<TD WIDTH=50 ALIGN=CENTER>
<INPUT TYPE=TEXT SIZE=5 MAXLENGTH=3 
NAME="text_red" VALUE="$text_red"></TD>
<TD WIDTH=50 ALIGN=CENTER>
<INPUT TYPE=TEXT SIZE=5 MAXLENGTH=3 
NAME="text_green" VALUE="$text_green"></TD>
<TD WIDTH=50 ALIGN=CENTER>
<INPUT TYPE=TEXT SIZE=5 MAXLENGTH=3 
NAME="text_blue" VALUE="$text_blue"><BR></TD>
</TR><TR>
<TD WIDTH=130 ALIGN=CENTER><B>HEX:</B></TD>
<TD WIDTH=50 ALIGN=CENTER>
<INPUT TYPE=CHECKBOX NAME="hex" VALUE="hex"></TD>
<TD WIDTH=50 ALIGN=CENTER><B>DEC:</B></TD>
<TD WIDTH=50 ALIGN=CENTER>
<INPUT TYPE=CHECKBOX NAME="dec" VALUE="dec"><BR></TD>
</TR><TR>
<TD COLSPAN=2><INPUT TYPE=SUBMIT VALUE="SUBMIT COLORS"></TD>
<TD COLSPAN=2><INPUT TYPE=RESET VALUE="RESET COLORS"><BR></TD>
</TR>
</TABLE>
<INPUT TYPE=HIDDEN NAME="hidden" VALUE="1">
</FORM>
</CENTER>
<CENTER>
<TABLE BORDER=5 CELLSPACING=5>
<TR>
<TH COLSPAN=7>Your Color Information</TH>
</TR><TR>
<TH></TH>
<TH COLSPAN=3>HEX</TH>
<TH COLSPAN=3>DEC<BR></TH>
</TR><TR>
<TH></TH>
<TH WIDTH=60>RED</TH>
<TH WIDTH=60>GREEN</TH>
<TH WIDTH=60>BLUE</TH>
<TH WIDTH=60>RED</TH>
<TH WIDTH=60>GREEN</TH>
<TH WIDTH=60>BLUE<BR></TH>
</TR><TR>
<TH>BGCOLOR:</TH>
<TD>$bgcolor_red</TD>
<TD>$bgcolor_green</TD>
<TD>$bgcolor_blue</TD>
<TD>$dec_bgcolor_red</TD>
<TD>$dec_bgcolor_green</TD>
<TD>$dec_bgcolor_blue<BR></TD>
</TR><TR>
<TH>TEXT:</TH>
<TD>$text_red</TD>
<TD>$text_green</TD>
<TD>$text_blue</TD>
<TD>$dec_text_red</TD>
<TD>$dec_text_green</TD>
<TD>$dec_text_blue<BR></TD>
</TR>
</TABLE>
</CENTER>
<P>
\&lt;BODY TEXT=\&quot;\&#35;$text_red$text_green$text_blue\&quot;
 BGCOLOR=\&quot;\&#35;$bgcolor\&quot;
\&gt;
<P>
<HR>
</BODY>
</HTML>
formpage

Input/Output Look

If everything has gone smoothly, you should see a form similar to figure 24.3 appear on your screen when you run the colorex.pl script using your Web browser. If you want, you should be able to expand the script to include the rest of the link types without much trouble.

Fig. 24.3

The form generated by the color @conversion script colorex.pl.

Every time you write a script, you should try to think of ways to reuse code from CGI scripts you already have. This way, you can save a lot of time that you would otherwise spend rewriting existing code.

To give you a better idea of how to do this, the color conversion script you just saw is a good example of a script that generates its own form. So, when you need a second script that also generates its own form, you should refer to this one to see how it works.

CGI Animation Script

Although most of the CGI scripts you use are probably going to be designed to process forms or send mail messages, sometimes you may want to do something a little more exciting on your Web site. One easy script you can write is an @animation script that works with @Netscape's 2.0 browser if your server has been configured to understand the @multipart/x-mixed-replace MIME type.

How the Script Works

The @CGI script in listing 24.4 uses the @MIME Content-type multipart/x-mixed-replace to create a boundary that is used to keep track of a portion of the document that can be replaced with new information. In this case, the script replaces an image with a new one, repeating the process several times. If you create your images properly, the process of replacing images creates a simple animation.

The main body of this script contains two subroutines. The first subroutine consists of all the variables that need to be defined. As mentioned previously, placing all the variables together in one place is a good habit to practice. By doing so, you can keep track of your variables more efficiently.


Initialize all your variables at the beginning of each script. Doing so makes editing variables' file paths a lot easier if you ever have to move your scripts.

The $animateDir variable indicates the name of the directory that contains the images for the animation. This directory should be a subdirectory created off the directory where you place the animate.pl script. The remaining variables that are defined are used to properly indicate the @MIME types for the various sections of the output created by the Animate subroutine. Also, the subroutine defines the AnimateImages array, which contains the list of images to be animated.

The second subroutine for this script does the actual animation. Basically, the animation occurs as follows:

  1. The @MIME Content-type is sent to the browser.
  2. The upper boundary is sent.
  3. An image file is opened and sent to the browser.
  4. Steps 2 and 3 are repeated until all the images are sent.
  5. Finally, the lower boundary is sent.

After you have the script set up correctly, you need to place a reference to it in your HTML document where you want the animation to appear. The easiest way to do this is to place the following code in the image tag:

<IMG SRC="URL for the script">

If you view the source file for the example, you should see a line on the page as follows:

<IMG SRC="http://www.missouri.edu/bchemkm-bin/animate.pl">

When you begin to write new scripts, you should do so in a safe environment off your Web site. This way, you can protect yourself from the security risks involved with giving outside visitors access to scripts that you are still developing.

The CGI Script

The animate.pl CGI script, as you can see in listing 24.4 is quite short. This is a good example of a short script that is more complex than many longer @CGI scripts. Learning how this script works should help you develop a solid foundation in understanding MIME types.

See "Using a Content-type Output Header"

Listing 24.4 CGI animation script.

#!/usr/bin/perl
# Animation CGI script.
# Written by Paul Saab 1-12-96
&Init;
&Animate;
sub Init {
$AnimateDir = "animation";
$MainContent = "Content-Type: multipart/x-mixed-replace;boundary=BOUNDRY\n";
$Boundry = "\n--BOUNDRY\n";
$EndBoundry = "\n--BOUNDRY--\n";
$ImageType = "Content-type: image/gif\n\n";
@AnimateImages = ("1.gif","2.gif","3.gif","4.gif",
                      "5.gif","6.gif","7.gif","8.gif");
}
sub Animate {
printf("%s",$MainContent);
foreach $file (@AnimateImages) {
open(IMAGE,"$AnimateDir/$file");
printf("%s",$Boundry);
printf("%s",$ImageType);
print <IMAGE>;
close(IMAGE);
}
printf("%s",$EndBoundry);
}

Input/Output Look

You can find an example of this animation script on the Web at http://www.missouri.edu/~bchemkm/guestbook.htm . Because this type of animation runs as fast as the browser can download the images, you will notice a change in speed based on the amount of your local bandwidth. One way to make the animation last longer for viewers on a @LAN is to repeat the images multiple times in the @@AnimateImages array. Or you can make the animation loop again by repeating the whole list in the same order.

A Utility Script

If you already have your own Web site, then you are well aware of the amount of time it takes to make minor corrections repeatedly across your entire site. For example, changing every instance of a blue dot graphic used for bullet lists to green ones may take a few hours. Or you may find that you need to go into every HTML file and change part of the footer, the date on the page, or part of a URL. Listing 24.5 is a little PERL script-not a CGI script-that you can run on the command line of a UNIX machine to automate this kind of task. This script performs find-and-replace string searches on multiple @HTML documents in a given directory.

How the Script Works

The find-and-replace utility script works by opening every .htm or .html file in your working directory and then looking for the search string. If the script finds the string, it gives you the option of replacing the search string with the replacement string. However, before the script makes any changes in a file, it saves the file as file_name.save in case you need to undo the changes later. This script's main limitation is that it can find and replace strings only within a single line of the document. Thus, you can't find and replace large multi-line segments within a document. However, this script should save you hours of changing dates and hyperlink URLs on your site.

The Script

Once you feel comfortable with how find-replace.pl in listing 24.5 works, you can expand it to deal with other types of documents or to work with more than one line at a time. You can use utility scripts like this one to help speed up many types of routine file maintainence.

Listing 24.5 Find-and-replace utility script.

#!/usr/local/bin/perl
print "What do you want to find?\n";
$findthis = <STDIN>;
chop($findthis);
print "What do you want to replace it with?\n";
$replacewith = <STDIN>;
chop($replacewith);
while($filename = <*.ht*>) {
print "Do you want to check in $filename? (y)\n";
$response = <STDIN>;
chop($response);
$check = $response;
&testyes;
if ($check eq "y") {
open(HTMLDOC, $filename);
open(HTMLDOCSAVE,">$filename.save");
while($line = <HTMLDOC>) {
chop($line);
print HTMLDOCSAVE $line,"\n" if $line ne "";
}
print "$filename was saved to $filename.save\n";
close(HTMLDOCSAVE);
close(HTMLDOC);
open(HTMLDOCSAVE,"$filename.save");
open(HTMLDOC,">$filename");
$changes = 0;
while($line = <HTMLDOCSAVE>) {
chop($line);
$_ = $line;
s#$findthis#$replacewith#g;
$newline = $_;
if ($line ne $newline){
print "Do you want\n$line\nreplaced with\n$newline? (y)\n";
$validate = <STDIN>;
chop($validate);
$check = $validate;
&testyes;
if ($check eq "y") {
print HTMLDOC $newline,"\n" if $line ne "";
print "The replacement was made.\n";
$changes = ++$changes;
}
else {
print HTMLDOC $line,"\n" if $line ne "";
print "The replacement was not made.\n";
}
}
else {
print HTMLDOC $newline,"\n" if $line ne "";
}
}
print "$filename was written with $changes changes.\n";
print "\n";
close(HTMLDOC);
close(HTMLDOCSAVE);
}
else {
print "$filename was not inspected for a match.\n";
}
}
sub testyes {
$num = 0;
while ($num == 0) {
if ($check eq "y") {
$num = 1;
}
elsif ($check eq "n") {
$num = 1;
}
else {
print "Please enter y or n.\n";
$check = <STDIN>;
chop($check);
}
}
}

Input/Output Look

In hopes of giving you a clearer idea of how to use the find-replace.pl script, figure 24.4 shows the output produced by this script while I was using it with an example file. Hopefully, you will save many hours of making routine changes by using this script.

Fig. 24.4

An example session using find-replace.pl.

As you begin your adventure in writing CGI scripts, you should always be security conscious while you're planning what to write. You can greatly reduce the security risk a script imposes by not allowing command-line operators to be passed to the script via a form or other means. Someone may take advantage of this @security hole and send other types of commands to the server through the script.

Creating a Guestbook

In this section, you learn how to write a CGI script for your very own guestbook. Basically, a @guestbook script is a @CGI script that takes the information visitors enter into a form and adds it to a second @HTML page that everyone can read.

The Guestbook's Form

To get started, you need to have a form that your visitors can use to let you know how much they like your site. Listing 24.6 is a basic @guestbook form called @gbform.html that you can use to get started. If you want, you can change the background color to one of your favorite colors. Also, you need to change the path in the ACTION attribute of the <FORM> tag to the location of your CGI scripts.

Finally, so that you can keep track of where your visitors are coming from, your visitors can select a choice from a pop-up menu letting you know how they found out about your Web site.. If you think of a choice that is more appropriate for your site, you should add it by modifying the option attributes of the <SELECT> tag in the form to meet your needs.

See "HTML Tables 101"

Listing 24.6 Guestbook form.

<HTML>
<HEAD>
<TITLE>Bare Guest Book Page</TITLE>
</HEAD>
<BODY>
<BODY BGCOLOR="#FFFFFF">
<FORM ACTION="http://your_path/gbookbare.pl" method="POST">
<H2>GuestBook</H2><P>
<B>Name:</B>
<input name="name" size=30 maxlength=60><BR>
<B>Email:</B>
<input name="email" size=30 maxlength=60><BR>
<B>Homepage:</B>
<input name="homepage" value="http://" size=30 maxlength=60><BR>
<B>Your Homepage's Title:</B>
<input name="hometitle" size=30 maxlength=60><BR>
<B>How did you find me?:</B>
<SELECT NAME="reference">
<OPTION>ListServe
<OPTION>Student
<OPTION>Search Engine
<OPTION>NewsGroup
<OPTION>Word of Mouth
<OPTION>Advertisment/Brochure
<OPTION>Personal Friend
<OPTION selected>Just Surfed On In!</SELECT><BR>
<B>COMMENTS:</B><BR>
<TEXTAREA WRAP=PHYSICAL NAME="comments" COLS=50 ROWS=8></textarea><P>
<input type="submit" value="Sign GuestBook">
<input type="reset" value="Clear">
</form>
<BR>
<A HREF="gbookbare.html">| View GuestBook |</A> 
</BODY>
</HTML>

You should feel free to customize the look of your guestbook form to match your site's look and feel. For example, you can add your own header, footer, and other graphics without affecting the CGI script that processes this form.

However, you need to remember that the script uses the values in the NAME attribute of the <INPUT> and <TEXTAREA> tags to keep track of the information that is entered into the form. If you change these values without changing them in the script, they won't match and the script will not process the form correctly.

After you have set up the form correctly, it should look like figure 24.5. Later in this chapter, you learn how to use table formatting to make your guestbook form look even better.

Fig. 24.5

A simple guestbook form.

CGI Subroutines

At this point, you should have your guestbook form up and running on your Web server. Also, you should have the subroutine library cgi-lib.@pl copied into your cgi-bin directory with the read and execute permissions turned on. This may require you to check the permissions for the file, the directory it is in or both depending on the type of server you are using. Here, I am assuming that you are using a UNIX server. After you have these tasks completed, you are ready to start writing the @CGI script that processes the @guestbook form's output and adds it to the response page.

Most larger scripts contain subroutines that are called from the main body of the script or even from within the subroutines themselves. By placing commonly repeated code into subroutines, you don't have to repeat the code each time it is used. Another common use for subroutines is for breaking a script down into its logical functions or tasks. If you write your scripts this way, you can easily copy subroutines, modify them slightly, and place then into different scripts.

The @guestbook CGI script uses several subroutines to make the process easier to follow. Each of these subroutines is discussed later, followed by an overall description of the guestbook script to wrap up.

A standard programming practice is to put all your subroutines either at the top or bottom of the script. This way, you make the logical flow of the main body of the program easier to follow. I prefer to put the subroutines at the bottom of the script, below the main body of the program.

If you find yourself repeating a subroutine in several scripts, turn it into a library by placing that subroutine in a separate file and including it in each script with the require function.

Performing Error Correction

Every CGI script that you write to process a form should contain some level of error checking. By using some simple error-checking methods in your scripts, you can reduce the number of improperly filled out forms that you receive. The first subroutine used in the script in listing 24.7 is called redo; it represents a simple level of error checking. The subroutine's function is to make sure that a value is entered into the form for both the name and comments fields of the @guestbook form. If either of these fields is left blank, the if condition in the subroutine will evaluate true and its contents will be executed. This causes an HTML page to be sent to the visitor's browser letting him or her know that the form was not filled out properly. Some of your visitors may be unable to fill out the remaining fields on the form, so the script allows those fields to be empty.

If your guest has entered something into both the name and comments fields on the form, then the form's information is acceptable. In this case, the if statement is skipped, the subroutine is exited, and the @interpreter moves on to the @addbook and @writefile subroutines, which are discussed next. The first one loads an @HTML page to the visitor's browser, letting him or her know that his or her information has been added to the guestbook. This page also provides the visitor with a link to the guestbook responses. The second subroutine actually adds the information from the form to the @HTML response page.

Outputting a Static Document

The second subroutine in the script in listing 24.7 is called addbook. As mentioned previously, it creates a link to the HTML page specified in the $thankyoupage variable, which is initialized near the beginning of the script. The link is called a @filehandle, and is named THANKYOUPAGE in this instance. You use the open function to create this filehandle to the HTML page that contains information showing the visitor the location of the response page after he or she has signed the guestbook.

You use a while function to create a loop that repeats everything inside it as long as its test condition is true. In this case, it checks the value of <THANKYOUPAGE>. The <> part is called the diamond operator, and it acts on the filehandle THANKYOUPAGE. The @diamond operator reads one line of the file, which is linked to the @filehandle, each time it is called. The while function evaluates true until the @filehandle reads in the last line of the file. The information in the current line of the file is automatically assigned to the $_ variable. Thus, as long as another line in the file remains to be read, the while loop continues to read one line at a time from the file, and it immediately prints that line to the browser. When the last line of the file is read and printed, the loop exits.

The if statement block of the redo subroutine does exactly the same thing as the @addbook subroutine, except that it prints a different HTML file to the browser.

Creating a Response Page

The @writefile subroutine is the last one needed for the script in listing 24.7, and it does most of the work. The first part of this subroutine takes the already-existing @HTML response page and stores a copy of it in a second file specified in the $outputstore variable defined earlier in the script. Then the original file specified in the $outputfile variable is re-created, so the script can construct the new response page.

To construct the new response page, the script uses a concatenated print command to print out an @HTML header for the new response page. With this type of print function, the script prints out each line until it comes to the parameter specified with the print function. In this case, the script prints each line until it comes to the word stuff. Thus, if you want to print out a paragraph, you can do start with the following line of code.

print <<STUFF;

When the script gets to this line of code, it will print out each new line of code that it comes to until it sees the word STUFF on a line by itself. So, the following code will print a three sentence paragraph.

Print <<STUFF;
sentence one
sentence two
sentence three
STUFF 

By using this type of print structure, you can avoid using a print statement for every line of output.

Next, the script adds the guest's information to the top of the response list. Everything being printed is standard HTML except that each time the PERL interpreter prints out a line, it substitutes each variable's value in place of that variable's name.

So far, the script has rebuilt the header of the response page and has added the new visitor's information below. Next, it takes the old responses from the stored copy of the response page, appends them to the bottom of the new response page, and deletes the stored version. This process is straightforward, except for one detail. The stored copy of the response page contains the original page's header. Therefore, if the script appends this file-as is-to the bottom of the new response page, it will have two headers. The solution to this problem is to count the number of lines in the header and skip that many lines of the old response page before the script starts appending to the bottom of the new version. In this way, the script can chop off the old header and only add the responses and footer from the previous version of the response page to the new version.

How the Script Works

If you are using a UNIX server, you may remember from the preceding chapter that the first line of a @PERL CGI script always contains the special comment line that lets the server know where to locate the PERL interpreter.

See "Using a Content-Type Output Header"

When writing the script, you use the require command to tell the PERL interpreter that it's going to be using subroutines from the cgi-lib.pl library. Without this line, the interpreter will be unable to find the ReadParse subroutine when looking for it. Also at some point, the server must know that the output from this script will be an @HTML file. This is done by printing out a MIME Content-type line.

Commonly, when you use a script from an archive on the Web, you have to make several changes to the variables within the script. Most of these changes require you to enter the correct directory paths for the various input and output files that the script uses. If you forget to make these changes, the script will be unable to locate the information that it needs to execute correctly. If you are lucky, all these variables will have been gathered into an initialization section at the beginning of the script making them easier to find.


More on the ReadParse subroutine.

When the @ReadParse subroutine receives the information that is entered into the @guestbook form, it digests it and places the resulting information into a special type of list called an associative array. Each field name from the form can then be used as a key to access the value that was entered into that field from the list. The name of this array is -called in-.

To retrieve this information, you need to indicate the name of the array and the key for which you want to retrieve the value. Commonly, this information is then assigned to a variable with the same name as the key for the array.

For example, if the form you are processing contains a field named comments, then you can assign the information that is placed in the comments field on the form to a variable named $comments as follows:

$comments = $in{'comments'};

Then you can refer to the $comments variable throughout the script to retrieve the information from the comments field.


Most of the variables initialized near the beginning of the script in listing 24.7 contain the information that is entered into the @guestbook form. You should not modify them unless you have changed the corresponding NAME attribute within the guestbook form. However, you do need to modify four variables in this script for the script to execute properly on your site. These four variables contain the location and file names for the four @HTML pages that are used with this @CGI script. You need to set each variable to a specific value based on your site. To do so, you have to replace the complete/file/path portion of each variable's value with the correct path to the directory in which you store your HTML pages.

If you are using a @UNIX server and don't know the complete path name for your @HTML file's directory, try typing pwd (print working directory) on the UNIX command line while in that directory.

After the script finishes initializing the variables at the beginning of the script, the interpreter executes the three subroutines discussed previously. The first subroutine makes sure that the form is filled out correctly. If the form is correct, the second subroutine sends a thank you page to the visitor's browser, and the final subroutine takes the form's information and creates the new response page.

Guestbook Script with Subroutines

As you examine the code for the gbbare.pl script in listing 24.7, you will soon see the logic behind each section of code. Once you become more familiar with how all the pieces fit together, you will be well on your way to writing CGI scripts to process any form you may wish to create.

Listing 24.7 Guestbook's CGI script.

#!/usr/local/bin/perl
require "cgi-lib.pl";
&ReadParse(*in);
print "Content-type: text/html\n\n";
$thismonth = (January, February, March, 
April, May, June, July, August, 
September, October, November, December)[(localtime)[4]];
$thisday = (localtime)[3];
$thisyear = (localtime)[5];
$name = $in{'name'};
$email = $in{'email'};
$homepage = $in{'homepage'};
$hometitle = $in{'hometitle'};
$reference = $in{'reference'};
$comments = $in{'comments'};
$outputfile = "/complete/file/path/gbookbare.html";
$outputstore = "/complete/file/path/gbooksave.html";
$redopage = "/complete/file/path/redopage.html";
$thankyoupage = "/complete/file/path/thankyoupage.html";
&redo;
&addbook;
&writefile;
sub redo {
if ($name eq "" || $comments eq "") {
open(REDOPAGE,$redopage);
while (<REDOPAGE>) {
print $_;
}
close(REDOPAGE);
exit;           
}
}
sub addbook {
open(THANKYOUPAGE,$thankyoupage);
while (<THANKYOUPAGE>) {
print $_;
}
close(THANKYOUPAGE);
}
sub writefile {
open(STOREFILE,">$outputstore");
open(OLDFILE,$outputfile);
while (<OLDFILE>) {
print STOREFILE $_;
}
close(OLDFILE);
close(STOREFILE);
open(NEWFILE,">$outputfile");
print NEWFILE<<stuff;
<HTML>
<HEAD>
<TITLE>Ken's GuestBook</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF">
<H1>My Guestbook!</H1>
<P>
<HR>
<B>Name: </B>$name<BR>
<B>Homepage: </B><A HREF=\"$homepage\">$hometitle</A><BR>
<B>E-mail: </B><A HREF=\"mailto:$email\">$email</A><BR>
<B>Referred By: </B>$reference<BR>
<B>Submitted: </B><I>$thismonth $thisday, 19$thisyear</I><BR>
<B>Comments: </B>$comments
stuff
open(SAVEFILE,$outputstore);
$num = 0;
while (<SAVEFILE>) {
if ($num >= 7) {
print NEWFILE $_;
}
++$num;
}
close(SAVEFILE);
close(NEWFILE);
unlink($outputstore);
}

PERL names are case sensitive. If you get into the habit of using uppercase names for your variables, arrays, subroutines, and filehandles, it will prevent you from accidentally using a PERL reserved word. In PERL all reserved words are lowercase.

Input/Output Look

After you have set up this script on your server, the hard part is over. Now, you need to wrap up by creating several HTML pages that the script needs. Because they are standard HTML pages, you can modify them to suit your needs without fear of damaging the script. For example, you can add your own customized header graphic and footer to each page.

The @Guestbook's Redo Page

The first page the script needs is the redo page that the script sends to the visitor if he or she leaves the name or comments field in the form blank. Listing 24.8 of redopage.@html provides this page.

Listing 24.8 Sample redo page for @guestbook.

<HTML>
<HEAD><TITLE>Guestbook Form's Redo Page</TITLE></HEAD>
<BODY>
<BODY BGCOLOR"#FFFFFF">
<CENTER><H2>Please  Note!</H2>
You have left some important information 
out of the Guestbook's form.<BR>
Please use the back button on your browser to go back<BR>
and make sure you have entered at least your name and a comment.
<P>
<H2>Thank You!</H2>
</CENTER>
<HR>
</BODY>
</HTML>

The result is shown in figure 24.6.

Fig. 24.6

A sample redo page for the @guestbook.

The Guestbook's Thank You Page

Likewise, the script sends a different @HTML page called @thankyou.html to the guest if the proper fields in the form have been filled in (see listing 24.9). This page thanks the guest for filling out the form and gives him or her a link to the newly updated response page.

You must include the full URL to the response page, not the relative URL, in the hyperlink when using the HTML page shown in listing 24.9. When the server goes for this page, it does so from your script's directory, not your HTML page directory as it usually does. So, if you use a relative URL, the server will not be able to find the desired file.

As a general rule, you must use complete URLs in HTML pages that are loaded to the browser by one of your CGI scripts.

Listing 24.9 Thank you page for guestbook.

<HTML>
<HEAD><TITLE>Thank you for signing my Guestbook</TITLE></HEAD>
<BODY>
<BODY BGCOLOR="#FFFFFF">
<CENTER><H2>Thank You For Signing!</H2>
|<A HREF="http://complete/url/gbookfancy.html">View GuestBook</A>|
</CENTER>
<HR>
</BODY>
</HTML>

The thank you page is shown in figure 24.7.

Fig. 24.7

The thank you page for the guestbook.

The Guestbook's Response Page

Finally, the last HTML page that this script needs is an empty response page. You should modify listing 24.10 of gbbare.html to meet your needs. However, you need to make sure that the number of lines in the header matches the number of lines that the CGI script skips when it appends the old response page to the bottom of the newly created version. Currently, the script is set to skip the first seven lines of this response page, which is everything up to, but not including, the <HR> line.

Listing 24.10 Response page for guestbook.

<HTML>
<HEAD>
<TITLE>Ken's GuestBook</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF">
<H1>My Guestbook!</H1>
<P>
<HR>
</BODY>
</HTML>

The result of running listing 24.10 is shown in figure 24.8.

Fig. 24.8

The guestbook's response page.

After you have set up the response page, it looks something like figure 24.9 when it is signed for the first time.

Fig. 24.9

The guestbook's response page with a signature.

If everything goes well, you should now have the guestbook up and running on your Web site. If you're having problems, you should hang in there and check out the troubleshooting section in this chapter for some help with common problems.


Troubleshooting

When I submit the guestbook information using the form, the information is not added to the response page.

You should make sure that the script has permission to write the information to the file. For the script to do so, the write permissions must be on for both the file and the directory in which it is located. You can set these permissions correctly by going to the UNIX command line and entering chmod 766 filename. This command gives everyone read and write privileges for that file or directory. Because it is not a good idea to give everyone write permissions in your main document directory, you might want to create a subdirectory that contains only those files that need to be written to by a script.


Improving the Guestbook's Form and CGI Script

Because form processing is the most common use of @CGI scripts on the Web at this time, you should learn how to format your form so your visitors can easily follow it and thus fill it out. By far, the easiest way to do this is by placing the entire form into a well-designed table. That way, your form looks like the forms you fill out on paper.

See "Form Layout and Design"

Table-Formatting the Guestbook Form

Using the HTML form gbform2.html in listing 24.11, you can see what your guestbook form can look like if you use a table to format it. Here is the HTML source for this new version of the form. As a reminder, don't forget to modify the action attribute of the <FORM> tag to include the location of the file gbfancy.pl and to include the correct URL for the response page in the anchor for viewing the guestbook responses.

Listing 24.11 Table formatted @guestbook form.

<HTML>
<HEAD>
<TITLE>Fancy Guest Book Page</TITLE>
</HEAD>
<BODY>
<BODY BGCOLOR="#FFFFFF">
<CENTER>
<form action="http://complete/file/path/gbookfancy.pl" method="POST">
<TABLE BORDER=10 CELLSPACING=10 CELLPADDING=2>
<TR>
<TH COLSPAN=2 ALIGN=CENTER>GuestBook<BR></TH>
</TR><TR>
<TD><B>Name:</B></TD>
<TD><input name="name" size=30 maxlength=60><BR></TD>
</TR><TR>
<TD><B>Email:</B></TD>
<TD><input name="email" size=30 maxlength=60><BR></TD>
</TR><TR>
<TD><B>Homepage:</B></TD>
<TD><input name="homepage" value="http://" size=30 maxlength=60><BR></TD>
</TR><TR>
<TD><B>Your Homepage's Title:</B></TD>
<TD><input name="hometitle" size=30 maxlength=60><BR></TD>
</TR><TR>
<TD><B>How did you find me?:</B></TD>
<TD> <SELECT NAME="reference">
<OPTION>ListServe
<OPTION>Student
<OPTION>Search Engine
<OPTION>NewsGroup
<OPTION>Word of Mouth
<OPTION>Advertisment/Brochure
<OPTION>Personal Friend
<OPTION selected>Just Surfed On In!</SELECT><BR></TD>
</TR><TR>
<TD COLSPAN=2>
<B>COMMENTS:</B><BR>
<TEXTAREA WRAP=VIRTUAL NAME="comments" COLS=50 ROWS=8></textarea><BR></TD>
</TR><TR>
<TD ALIGN=CENTER>
<input type="submit" value="Sign GuestBook"></TD>
<TD ALIGN=CENTER>
<input type="reset" value="Clear"></TD>
</TR>
</TABLE>
</form>
<BR>
<A HREF="gbookfancy.html">| View GuestBook |</A>
</CENTER>
</BODY>
</HTML>

You can see from figure 24.10 that this version of the form is easier to read and looks nicer than the previous version shown in figure 24.5.

Fig. 24.10

A table formatted @guestbook form.

Some browsers that are currently in use don't support the tags for tables. One way to help make your tables easier to read for these users is to place a <BR> tag at the end of each row of the table, right before the row's <TD> tag.

Next, you learn how to add an extra subroutine to your guestbook script to improve its error-checking properties.

Creating a Better Error-Checking Subroutine

The @makesure subroutine provides an additional level of error checking that is very useful in a wide variety of forms. After you add it to your script, visitors can see what their responses look like before the responses are added to the response page. This way, they have a second chance to make sure all the information is correct, and even test the link to their homepage to make sure it works.

To accomplish this, the subroutine generates a hidden form that contains the values for the fields that are in the original form's input fields. This way, the information is not lost. In addition, the script sets the $test variable to act as a flag so that the script can tell the difference between the information from this form and the original one. The page that the subroutine creates, as you can see later in figure 24.11, includes a standard submit button for the visitors to click after they have double-checked their information and want it added to the response page.


Troubleshooting

When I submit the guestbook information using the form, I receive a file contains no data error.

The most common cause for this type of problem is that the script is accessing a variable that contains the wrong file path for the desired output page. You should go into the script and make sure that all the variables for the output pages contain the correct file path information. Another possible cause for the error is that the MIME Content-type line has an error in it, or the necessary blank line is not being sent to the server with this information.


How the Script Works

In addition to adding the makesure subroutine, you need to make some minor changes in the main body of the script to call the subroutine at the right time. Also, because you gave the form a face-lift using table formatting, you can improve the output's look while you're at it. Because most of this @guestbook script works the same way as the previous one, I point out the changes only.

First, this script needs a new variable-called $test-for the makesure error-checking subroutine. Just as in the previous version, this script checks to see if the visitor has entered his or her name and a comment into the form. If he or she didn't, the visitor is shown a page letting him or her know what to add. This is the first level of error checking in this script. Now, assuming that the visitor has correctly entered information into the form, the script shows what the response is going to look like and gives him or her a second chance to make sure that everything is okay. If it is, the visitor can click on the new submit button to send the information for addition. The new page that is generated by the script is considered the second level of error checking because it takes effect only if the first level is passed.

Unfortunately, the script has no way of knowing if the information that it is receiving is from the original posting of the form or from the next page showing what the output will look like. This happens because the information that the visitor has filled out has not changed between submissions. So, to solve this problem, the script uses the $test variable.

The $test variable acts as a flag that indicates that the information the script is receiving is from the second submission of the form. If the flag is set, the script knows that the information in the form has been checked by the visitor and that the script can add it to the current response list and thank him or her for visiting your site. If the flag is not set, the script executes the makesure subroutine. This way, the visitor gets a chance to double-check and make sure the information he or she submits is correct.

Just as a reminder, the redo and addbook subroutines don't require modification from the previous version of the guestbook.

Two changes have been made to the @writefile subroutine from the previous version. First, a background graphic is used to create a vertical gray stripe down the left side of the screen. Second, table formatting is used to force the response information to appear just to the right of this stripe, making for a nice-looking response page.

Advanced Guestbook CGI Script

If you examine gbfancy.pl in listing 24.12 closely, you will find that most of the changes to this version from listing 24.7 are contained neatly in their own subroutines. By using subroutines in this way, it is easy to add new features into existing scripts.

Listing 24.12 Advanced guestbook's CGI script.

#!/usr/local/bin/perl
require "cgi-lib.pl";
&ReadParse(*in);
Print "Content-type: text/html\n\n";

$thismonth = (January, February, March, 
April, May, June, July, August, September, 
October, November, December)[(localtime)[4]];
$thisday = (localtime)[3];
$thisyear = (localtime)[5];
$test = $in{'test'};
$name = $in{'name'};
$email = $in{'email'};
$homepage = $in{'homepage'};
$hometitle = $in{'hometitle'};
$reference = $in{'reference'};
$comments = $in{'comments'};
$outputfile = "/showme/bchemkm/www/gbookfancy.html";
$outputstore = "/showme/bchemkm/www/gbooksave.html";
$redopage = "/showme/bchemkm/www/redopage.html";
$thankyoupage = "/showme/bchemkm/www/thankyoupage.html";
&redo;
if ($test eq "makesure") {
&addbook;
&writefile;
exit;
}
&makesure;
sub redo {
if ($name eq "" || $comments eq "") {
open(REDOPAGE,$redopage);
while (<REDOPAGE>) {
print $_;
}
close(REDOPAGE);
exit;
}
}
sub addbook {
open(THANKYOUPAGE,$thankyoupage);
while (<THANKYOUPAGE>) {
print $_;
}
close(THANKYOUPAGE);
}
sub makesure {
print <<OUTPUT;
<HTML>
<HEAD>
<TITLE>Guest Book Checking Page</TITLE>
</HEAD>
<BODY>
<BODY BGCOLOR="#AAAAAA">
<CENTER>
<H2>Hello $name !</H2>
Please make sure the following is correct.<P>
If you find you need to make a change,<BR>
use the back feature on your browser to return to the original form.<P>
</CENTER>
<HR>
<TABLE BORDER=0>
<TR>
<TD WIDTH=80 NOWRAP><BR></TD>
<TD><B>Name: </B>$name<BR>
<B>Homepage: </B><A HREF=\"$homepage\">$hometitle</A><BR>
<B>E-mail: </B><A HREF=\"mailto:$email\">$email</A><BR>
<B>Referred By: </B>$reference<BR>
<B>Submitted: </B><I>$thismonth $thisday, 19$thisyear</I><BR>
<B>Comments: </B>$comments
</TD>
</TR>
</TABLE>
<CENTER>
<form action="http://www.missouri.edu/bchemkm-bin/gbookfancy.pl" method="POST">
<input type="hidden" name="test" value="makesure">
<input type="hidden" name="name" value="$name">
<input type="hidden" name="email" value="$email">
<input type="hidden" name="homepage" value="$homepage">
<input type="hidden" name="hometitle" value="$hometitle">
<input type="hidden" NAME="reference" value="$reference">
<input type="hidden" NAME="comments" value="$comments">
<HR>
<TABLE CELLSPACING=10 CELLPADDING=2 BORDER=5>
<TR>
<TD><input type="submit" value="Sign GuestBook"></TD>
</TR>
</TABLE>
</FORM>
</CENTER>
<HR>
</BODY>
</HTML>
OUTPUT
}
sub writefile {
open(STOREFILE,">$outputstore");
open(OLDFILE,$outputfile);
while (<OLDFILE>) {
print STOREFILE $_;
}
close(OLDFILE);
close(STOREFILE);
open(NEWFILE,">$outputfile");
print NEWFILE<<stuff;
<HTML>
<HEAD>
<TITLE>The Fancy Guestbook</TITLE>
</HEAD>
<BODY BACKGROUND="Http://www.missouri.edu/~bchemkm/greybar.gif">
<CENTER><H2>Guestbook</H2>
</CENTER>
<HR>
<TABLE BORDER=0>
<TR>
<TD WIDTH=80 NOWRAP><BR></TD>
<TD><B>Name: </B>$name<BR>
<B>Homepage: </B><A HREF=\"$homepage\">$hometitle</A><BR>
<B>E-mail:</B><A HREF=\"mailto:$email\">$email</A><BR>
<B>Referred By: </B>$reference<BR>
<B>Submitted: </B><I>$thismonth $thisday, 19$thisyear</I><BR>
<B>Comments: </B>$comments </TD>
</TR>
</TABLE>
stuff
open(SAVEFILE,$outputstore);
$num = 0;
while (<SAVEFILE>) {
if ($num >= 7) {
print NEWFILE $_;
}
++$num;
}
close(SAVEFILE);
close(NEWFILE);
unlink($outputstore);
}


Troubleshooting

When I submit the guestbook information using the form, the hyperlinks don't work properly.

This problem is caused by using a relative URL on a page that is loaded to the browser by a script. When a script loads a Web document, the server uses the script's directory as its starting place for finding the new document. Because the hyperlink is usually relative to the HTML document's directory and not the script's directory, the link does not work. The best way to avoid this problem is to use Absolute URLs in those files that are loaded to the browser by a script.


Figure 24.11 shows the response page generated by the subroutine in listing 24.12.

Fig. 24.11

The @HTML page generated by the @makesure subroutine.

An Improved Guestbook Response Page

The HTML page gbfancy.@html found in listing 24.13 is the copy of the @HTML response page that you should use with this new version of your guestbook.

Listing 24.13 New guestbook response page.

<HTML>
<HEAD>
<TITLE>The Fancy Guestbook</TITLE>
</HEAD>
<BODY BACKGROUND="Http://www.missouri.edu/~bchemkm/greybar.gif">
<CENTER><H2>Guestbook</H2>
</CENTER>
</TR>
</TABLE>
<HR>
</BODY>
</HTML>

The resulting page is shown in figure 24.12, and figure 24.13 shows the @guestbook response page containing a signature.

Fig. 24.12

The guestbook's new response page.

Fig. 24.13

The guestbook's new response page with a signature.

So far, you have seen how a standard guestbook script takes the guest's form input and appends it to a response page. Now, imagine that you are the guest and that you have just signed the guestbook. The next thing you will probably do is go and look at your response on the page. If other people were also looking at the same @guestbook, and they signed it too, then when you reload the page, you see their responses on the page also. With a little modification of the guestbook's format, these responses easily become live chat. Each guest simply refreshes the response page, reads the current reply, and then sends in his or her own comments to be added to the page.

Getting More Out of a Script

After you get the hang of how your guestbook CGI script works, you can adapt it to handle almost any other form processing need. Just with this example, you have seen how to perform two different types of error checking-both making sure that the appropriate fields have been filled in and giving your visitors a chance to double-check that they have entered the correct information. Also, don't overlook that this script also demonstrates loading static documents to the browser and file reads and writes, along with the more complex process of appending output to the middle of an existing document. You can recombine or repeat all these techniques as needed to process many other types of forms.


Internet & New Technologies Home Page - Que Home Page
For technical support for our books and software contact support@mcp.com
© 1996, Que Corporation