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.
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:
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.
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.
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"
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>
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.
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
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.
The e-mail form.
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.
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.
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 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> \<BODY TEXT=\"\#$text_red$text_green$text_blue\" BGCOLOR=\"\#$bgcolor\" \> <P> <HR> </BODY> </HTML> formpage
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.
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.
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.
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:
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 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); }
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.
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.
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.
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); } } }
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.
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.
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.
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.
A simple guestbook form.
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.
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.
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.
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.
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.
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.
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 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.
A sample redo page for the guestbook.
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.
The thank you page for the guestbook.
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.
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.
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.
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"
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.
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.
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.
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.
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.
The HTML page generated by the
makesure subroutine.
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.
The guestbook's new response page.
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.
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.
![]() ![]() |