by Michael Moncur
There's nothing better than writing four or five pages of Perl code, putting it on-line, and watching it work flawlessly the first time. Unfortunately, you won't always be this luckydebugging is an important part of any programming project.
In this chapter, you'll examine the process you can use to test and debug your CGI programs. You also will learn about some common errors and their causes, as well as several tools that can help make the debugging process less painful.
Even if you've debugged programs before, you will find the process of debugging CGI programs a different kind of challenge. You should not be surprised if CGI programs are harder to debug than anything else you've encountered. Nevertheless, it can be done.
CGI programs often are hard to debug because you don't have as many clues as you might expect. If you receive an error message when submitting a form, it might mean that your program has a syntax error, that it is not creating the output, or that it simply doesn't exist.
Several basic steps exist in the debugging process. The following list is a suggested method of finding the problem by process of elimination; as you develop and debug a few programs of your own, you'll grow to recognize certain kinds of problems and will be able to skip many of these steps:
First, you'll look at the basic steps of this process in the following sections.
In a large CGI project, you may have several programs interacting with each other. It is important to determine which of them is executing when the problem occurs. This may be a simple processfor example, if your project uses only one program, or if the output stops halfway through a certain program's text.
Some situations can be more difficult. Imagine an HTML document that includes several Server Side Includes commands, for example, or a combination of programs that access a database. If I enter a record (using one program), and then cannot successfully recall it (using another program), it may be one of two things: The record isn't being written or it isn't being read.
In order to pinpoint the incriminating program, you might want to try the following tips:
Here we run into one of the idiosyncrasies of the CGI environment. In a typical programming language, it's usually obvious that the program is running. With a CGI program, however, you can't take this for granted. There are many factors that can cause your program not to run at all, and unfortunately, the error message you get is usually the same one as you'll get if your program runs into a problem.
The error message you'll usually see when your program is not executing is the one shown in Figure 13.1.
|
This server has encountered an internal error which prevents it from fulfilling your request. The most likely cause is a misconfiguration. Please ask the administrator to look for messages in the server's error log. |
Figure 13.1. The error message usually displayed if a CGI program is not executed.
The most likely cause of this error, unfortunately, is not a misconfiguration; it's a CGI problem. The next step is to determine whether your program is executing at all. The following are some situations that could prevent your program from executing. It's best to quickly check each of these first when you encounter a problem.
Note: With some HTTP servers, a second error message is possible, specifying that the file was not found. This is a sure indication of one of the first two conditions that follow.
Note: You'll look at the server's error log later in this chapter, in "Reading the Server Error Log." It can be an invaluable resource if you happen to be the System Administrator or have the time to contact her.
The first step in debugging a Perl program is to check its syntax. Perl is very picky about syntax errors and very sensitive to them. A simple misspelling or a misplaced punctuation character can cause you hours of frustration if you aren't careful. In this section, you'll learn how to check your program's syntax and how to spot (and avoid) some of the most common syntax errors.
Note: Technically, a syntax error is an error in the language, formatting, or punctuation used to write the program. These errors often are typographical errors.
With Perl, it's quite easy to check your program's syntax. You should do this as part of the editing process. Personally, I check the syntax each time I make a change. Type the following command:
perl -c programname
This checks the syntax of the program without executing any of the commands. Alternatively, you simply can execute the program by typing its name. Perl checks the syntax before executing it and displays any errors it finds.
A typical error message produced when you check syntax looks like the one shown in Figure 13.2.
|
syntax error at test.cgi line 29, near "while" |
|
syntax error at test.cgi line 129, near "}" |
|
test.cgi had compilation errors. |
|
Exit -1 |
Figure 13.2. A typical Perl syntax error message.
As you can see, Perl doesn't exactly spell out the exact cause and location of the error. However, it does give you two important clues: The line number where the error occurred, and a bit of text near it. These are not exact; the line number often is incorrect, and the quoted code often is unrelated to (but next to) the code with the problem. It's best to consider this a starting point for your debugging process.
As Figure 13.2 illustrates, Perl often displays more than one error message. A good general rule is to ignore all but the first message in the list. Why? Often, an error at one point in the program causes a later section to appear wrong, creating a second error. Fixing the first error often eliminates the second, so it's best to fix one error at a time and then to check the syntax again to see whether you receive a different message.
Some syntax errors are very easy to spotfor example, if you misspell the word print. However, Perl has some tricky syntax, and some errors are much harder to detect.
You will now look at some of the most common errors you can make when creating a Perl program and the error messages or other symptoms they are likely to produce.
Note: Not all syntax errors produce an error message. If a section of your program doesn't work or behaves in an unexpected manner, watch out for one of the errors described in this section.
One of the most basic syntax errors is incorrect punctuation. Because these errors can be created by a simple missed key on the keyboard, they are quite common. Perl uses certain characters to indicate sections of the program or parts of a command. Table 13.1 shows some errors to watch out for.
Symbol |
Name |
Description |
|
; |
semicolon |
Each command in your Perl program must end with a semicolon. Unfortunately, the error message you get doesn't give you any hints. The error message in Figure 13.2 was caused by this very error. The line listed in the error message is usually the line after the line missing the semicolon. |
|
{} |
braces |
Used to delimit sections of the program. The most common problem is leaving off a closing brace to correspond with an opening brace. Fortunately, the error message is right on target: Missing right bracket. Remember, you need to use braces after each if, while, or sub statement. |
|
() |
parentheses |
Most of the commands in Perl do not require parentheses. However, an if statement must use parentheses around the condition. |
|
"" |
quotation marks |
Perl allows quoted strings to include multiple lines. This means that if you leave off a closing quote, the rest of your entire program might be considered part of the string. |
Operators are used to form a relationship between two words in the program. The most common operator syntax error is also the hardest to notice. Remember that Perl uses two kinds of equal sign:
If you're like me, you'll run into this error constantlyusually a simple typing mistake. What makes it so complicated is that the incorrect operator often does not cause a syntax error; instead, it just works differently than you are expecting. Consider the following example code:
if ($result = 5) {
print "The result is 5.";
}
This looks like a correct section of codein fact, in some languages it would be perfectly acceptable. However, note that the assignment operator (=) has been used in the if statement when the equality operator (==) should have been used.
What does this mean to the program? Well, instead of comparing the $result variable to the constant 5, it is being assigned the value 5. Worse, Perl allows the assignment to be used as a condition. The success of the assignment determines whether the condition is True; in other words, instead of saying "if the result is 5," you're saying "if you can successfully make the result 5."
Needless to say, this creates a problem. First of all, your condition always will be considered True, because the $result = 5 statement never fails. Second, and worse, your $result variable will be assigned the value 5, losing its previous value.
Based on this scenario, you can remember the following clues, which might let you know that you have mistakenly used the wrong type of equal sign:
Before you consider that if statement to be good, there's one more thing to check. Perl, unlike some languages, uses separate operators to refer to strings and numbers. The equality operator, ==, which was referred to earlier, is strictly for numbers.
The operators are easy to remember, because the string operators use stringscombinations of lettersinstead of the normal punctuation. Table 13.2 gives a summary of the different operators for strings and numbers.
Condition |
Numeric Operator |
String Operator |
|
Is Equal To |
== |
eq |
|
Does Not Equal |
!= |
ne |
|
Is Greater Than |
> |
gt |
|
Greater Than or Equal |
>= |
ge |
|
Is Less Than |
< |
lt |
|
Less Than or Equal |
<= |
le |
Tip: The assignment operator, =, is the same for both numbers and strings.
Another common syntax problem is in variable names. All variables in Perl start with a character that indicates the type of variable. You often can refer to a variable in more than one way. Table 13.3 lists the characters used with the three types of variables.
Table 13.3. The syntax used for different Perl variable types.
Variable Type |
Character |
Example |
|
Scalar |
$ |
$result |
|
Array (entire array) |
@ |
@data |
|
Array (one element) |
$ |
$data[4] |
|
Associative array (entire array) |
% |
%value |
|
Associative array (one element) |
$ |
$value{"key"} |
The simplest variable syntax error is to leave the character off the beginning of the variable, like this:
result = 1
Again, if you're used to another language, you will run into this problem frequently. A more complicated issue involves using the right character to refer to either an entire array or a single element. A good rule of thumb is that the dollar sign ($) should be used any time you are referring to one element. However, you must include either brackets [] for an array or braces {} for an associative arraythis is how Perl can tell which type of variable you are referring to.
There are many CGI problems that cause you to receive no output at all, or simply an error message. The most common error message was shown in Figure 13.1. The text of that figure is repeated here:
This server has encountered an internal error which prevents it from fulfilling your request. The most likely cause is a misconfiguration. Please ask the administrator to look for messages in the server's error log.
As mentioned earlier, this error message can be caused by your program failing to execute at all, and you should check for that first. Even if your program does execute, however, it can produce this error if it does not output correct HTML and headers.
As you learned earlier in this book, the first output your CGI program should produce is a MIME header to indicate the type of output. This usually is HTML, but your program can output anythingtext, a downloadable file, or even a graphic. Most of your CGI scripts use a header like the following (the beginning of the actual HTML is included for clarity):
Content-type: text/html <HTML>
Note the blank line after the Content-type header and before the HTML document begins. This is mandatory. If the blank line is not included, you receive the error message shown in Figure 13.1.
Alternatively, your program might return a reference to an existing URL. The output should look something like this:
Content-type: text/html Location: URL of referenced document <HTML>
Note that you still include the beginning of an HTML document. It's best to include a small HTML document with the reference. The reason? First of all, if it is mistakenly interpreted as actual HTML, you'll have some hint as to what's going on. Second, some browsers won't accept the headers, including the all-important Location, unless they're followed by at least one line of text. The blank line after the headers still is required.
If your program is outputting the correct headers, you still might not receive any output. The most likely cause is incorrect HTML in the output after the header. Some browsers are forgiving and will display incorrect HTML; others will ignore it completely or display it incorrectly. If your browser allows you to view HTML source, you can quickly pinpoint the problem. Here are some common HTML mistakes you should check for:
If you still have problems or are using a browser that doesn't allow you to view source, there are two tricks that might be helpful, described in the next sections.
As you learned in the previous section, the MIME header your program outputs tells the browser what sort of content to expect and how to display it. You can take advantage of this and force the browser to display the output as text. This makes it easy to determine whether an HTML element is causing the problem. Change your header to the following:
Content-type: text/ascii <HTML>...
Are you still stuck trying to view your program's output without interference from the browser? If you have access to the TELNET command, you can view the output without using a browser at all. This makes it easy to narrow down the problem.
First, use the following command to open a session with the HTTP server:
telnet sitename.com 80
The 80 specifies the port under which the HTTP server is running. This is typically 80, but may be different on your serverthe administrator may have chosen a different port number for security, or for a special purpose. After you've established a connection, type a Get request like the following:
GET /cgi-bin/directory/scriptname HTTP/1.0
This is not a complete URL; instead, it is the location to find the document. Use the exact directory that your script is in; this is equivalent to the URL you use to access it from a browser, but does not include the http: identifier or the site name.
After your GET request (note that the capital letters are required) your program will execute and the output will appear as HTML source. It should be easy to find the error. There are two considerations to note:
As a final example, here is the captured output of executing a CGI script from a successful GET request through the TELNET command:
Trying 198.60.22.4 ... Connected to www.xmission.com. Escape character is '^]'. GET /cgi-bin/users/mgm/randquote <HTML> This is a simple test document. </HTML> Connection closed by foreign host. Exit 1
The next step in determining the cause of a problem with your CGI program is to view the input going into the program. This is usually the data entered in a form after a GET or POST query, or a QUERY_STRING that is appended directly to the URL.
The easiest way to determine the environment going into the program is to display it. This means using a different program temporarilyone that is intended simply to display the environment. Listing 13.1 shows a Perl program that simply displays environmental variables available to the program as an HTML file.
#!/usr/bin/perl
MAIN: {
print "Content-type: text/html\n\n";
print "<HTML><HEAD><TITLE>Environment Display</TITLE>";
print "</HEAD><BODY>";
while (($key,$value) = each %ENV) {
print "$key=$value<BR>\n";
}
print "</BODY></HTML>";
exit 0;
}
Listing 13.2 shows the typical output of this program. In this case, the CGI program was accessed directly; no form was used.
SERVER_SOFTWARE=Apache/0.8.13 GATEWAY_INTERFACE=CGI/1.1 DOCUMENT_ROOT=/usr/local/lib/httpd/htdocs REMOTE_ADDR=204.228.136.119 SERVER_PROTOCOL=HTTP/1.0 REQUEST_METHOD=GET REMOTE_HOST=slc119.xmission.com QUERY_STRING= HTTP_USER_AGENT=Mozilla/1.22 (Windows; I; 16bit) PATH=/usr/local/bin:/usr/sbin:/usr/local/sbin/:s/. HTTP_ACCEPT=*/*, image/gif, image/x-xbitmap, image/jpeg SCRIPT_FILENAME=/usr/local/lib/httpd/cgi-bin/users SCRIPT_NAME=/cgi-bin/users/mgm/test.cgi HTTP_PRAGMA=no-cache SERVER_NAME=www.xmission.com PATH_INFO= SERVER_PORT=8000 PATH_TRANSLATED=/usr/local/lib/httpd/htdocs/mgm/test.cgi SERVER_ADMIN=www@xmission.com
As you can see, this gives you quite a bit of information. Here are some of the problems this can help you detect:
A more useful debugging script displays the name and value pairs that were submitted. You easily can make such a script. Use the same code you usually do to split the name/value pairs, and use a section of code like this to display them:
while (($key,$value) = each %entries) {
print "$key=$value<BR>\n";
}
In this example, the name/value pairs are contained in the associative array %entries. The each keyword allows you to display each element in the array without knowing its key. To use this script to debug a form, simply point the ACTION to this script instead of your normal script.
Here is an example of the output of this script, using a form with the POST method and several text fields:
Name = John Smith Address = 221b Baker Street Phone = 801-555-1245 Interests = Computers, Hiking, Bad Poetry
If you are allowed access to the Unix command line or shell, there are some additional debugging features you can access. These include testing the program without involving the HTTP server and using Perl's powerful debug mode to find bugs in your program.
Although your CGI program is intended to work with an HTTP server across the Internet, there are some advantages to testing it without involving the HTTP server at all. These include the following:
If your program is a simple Server Side Include file, it's easy to test at the command line. Simply type the name of the program at the command line. If the current directory is not in your PATH environment variable, you may need to include a directory name in your command. The following command executes a program called test.cgi in the current directory:
./test.cgi
The period in this example is interpreted by Unix to mean the current directory. You also could type the entire path to the program file.
This method also works if your program does not accept any parametersin other words, if it is intended to give information that is not based on input from a form or from the URL. If your program does expect input, you'll need to do something a bit more tricky: simulating a GET request.
If you are using the POST method with your script, there is no easy way to test it at the command line. However, the GET method is easy to simulate. You can change the method to GET temporarily in order to use this technique.
In a GET request, the following environment variables are set:
REQUEST_METHOD = GET QUERY_STRING = data
You can set these manually to fool your program into working at the command line. For the variables in the QUERY_STRING, you need to use the & character between variable/value pairs and the = character between variables and their values. Suppose that you want to send the following data to the script:
Name: John Smith Address: 321 Elm Street City: Metropolis
You would use the following variable settings:
REQUEST_METHOD = GET QUERY_STRING = Name=John Smith&Address=321 Elm Street&City=Metropolis
In actuality, things are a bit more difficult, because the & characters are interpreted as special characters by the shell. Here are the actual commands to use to set these variables:
setenv REQUEST_METHOD GET setenv QUERY_STRING "Name=John Smith\&Address=321 Elm Street\&City=Metropolis"
Note that you use a backslash \ character before each & character. This is an escape code that indicates to the shell to use the character, rather than its meaning. Also, the quotation marks in the string are required in order for the spaces to be treated as spaces. Otherwise, the command would end with the first space.
After typing the earlier commands, verify your settings by typing the setenv command by itself. This displays the entire environment; the last two entries should be the ones you added. Make sure that the data is listed correctly.
This method is particularly useful when it's necessary to debug the program without placing it on-line, such as in situations where the server's administrator must place scripts online manually. It is also handy because once you've set the variables as listed earlier, you can test the program repeatedly without having to retype the data.
Another advantage of debugging a CGI program at the command line is that you can use the debug mode available with Perl. This allows you much greater control over the execution of the program. You can step through each command individually, examine variable values along the way, and narrow down the source of an error or incorrect result.
Before you begin, set the environment variables to simulate a GET request if your program needs it, as described in the previous section. Then type the following command to start the program in debug mode:
perl -d programname
After you type this command, the first statement in your program is executed. Perl then stops and asks you for a command. You can enter Perl commands here and they are executed. More important, you can enter special debug commands. Table 13.4 lists the most useful commands.
Command |
Mnemonic |
Explanation |
|
/text |
Search |
Searches for the text in the program |
|
?text |
Search back |
Searches backward for the text |
|
b |
break |
Sets a breakpoint; uses the current line or specifies a line |
|
b sub |
break sub |
Sets a breakpoint at the start of a subroutine |
|
c |
Continue |
Continues to the next breakpoint |
|
<CR> |
Next |
Repeats the last "next" or "step" command |
|
d line |
delete break |
Deletes a breakpoint at line or the current line |
|
D |
Delete all |
Deletes all breakpoints |
|
f |
finish |
Executes statements until the end of the current routine |
|
h |
help |
Displays a list of debug commands |
|
l number |
List |
Lists number lines of the program |
|
l sub |
List sub |
Lists a named subroutine |
|
n |
next |
Advances to the next statement, ignoring subroutines |
|
p |
|
Displays a variable or expression's value |
|
q |
Quit |
Exits the debugger and quits the program |
|
s |
step |
Executes a single statement (a single step) |
|
S |
Subroutines |
Lists the names of all subroutines |
|
t |
trace |
Displays commands as they execute |
|
V |
Variables |
Lists all variables |
As a quick introduction to the debugger, here are the actions you will perform in a typical debugging session:
As a final bit of explanation, Listing 13.3 shows the output of the beginning of a typical debug session. The first statement in this program sets a variable called $sendmail. The prompt is the DB<1> at the end of the output. This is where you type debug commands.
perl -d jobqry.cgi Loading DB routines from $RCSfile: perl5db.pl,v $$Revision: 4.1 $$Date: 92/08/07 18:24:07 $ Emacs support available. Enter h for help. main::(jobqry.cgi:10): $sendmail = "/usr/lib/sendmail"; DB<1>
One of the tools that you might have available is the HTTP server's error log. This is a text file that lists all the errors that have occurred. Each time your CGI script produces an error, a message is added to this log.
Unfortunately, you often will not have access to the error log. However, you can ask your Administrator to view it or give you access. Of course, if you have your own server, you will have no problem. Listing 13.4 shows a sample of part of an error log. This is from a particularly busy server; all these errors happened within about two hours.
[20/Apr/1995:17:50:17 +0500] [OK] [host: dsouza.interlog.com referer: http://webcrawler.cs.washington.edu/cgi-bin/WebQuery] Connection interrupted [SIGPIPE], req: GET /89-94.refs.html HTTP/1.0 [20/Apr/1995:18:15:29 +0500] [OK] [host: cleta.chinalake.navy.mil referer: http://webcrawler.cs.washington.edu/cgi-bin/WebQuery] Connection interrupted [SIGPIPE], req: GET /89-94.refs.html HTTP/1.0 [20/Apr/1995:20:55:17 +0500] [OK] [host: mac1223.botany.iastate.edu referer: http://webcrawler.cs.washington.edu/cgi-bin/WebQuery] Connection interrupted [SIGPIPE], req: GET /89-94.refs.html HTTP/1.0 [20/Apr/1995:21:09:26 +0500] [OK] [host: slip16.docker.com referer: http://webcrawler.cs.washington.edu/cgi-bin/WebQuery] Connection interrupted [SIGPIPE], req: GET /89-94.refs.html HTTP/1.0 [20/Apr/1995:21:14:46 +0500] [OK] [host: ip-pdx8-30.teleport.com referer: http://webcrawler.cs.washington.edu/cgi-bin/WebQuery] Connection interrupted [SIGPIPE], req: GET /89-94.refs.html HTTP/1.0 [20/Apr/1995:22:45:38 +0500] [OK] [host: alpha10.scs.carleton.ca] Connection interrupted [SIGPIPE], req: GET /89-94.refs.html HTTP/1.0 [20/Apr/1995:23:04:53 +0500] [MULTI FAILED] [host: opentext.uunet.ca] /robots.txt [20/Apr/1995:23:36:54 +0500] [OK] [host: macsf47.med.nyu.edu referer: http://charlotte.med.nyu.edu/getstats] Connection interrupted [SIGPIPE], req: GET /getstats/statform HTTP/1.0 [20/Apr/1995:23:42:15 +0500] [OK] [host: macsf47.med.nyu.edu referer: http://charlotte.med.nyu.edu/getstats/statform.html] Bad script request -- none of '/opt/cern_httpd_3.0/cgi-bin/getstats' and '/opt/cern_httpd_3.pp.pp' is executable (500) "POST /cgi-bin/getstats HTTP/1.0" [20/Apr/1995:23:54:39 +0500] [OK] [host: macsf47.med.nyu.edu referer: http://charlotte.med.nyu.edu/getstats/statform.html] Bad script request -- none of '/opt/cern_httpd_3.0/cgi-bin/getstats' and '/opt/cern_httpd_3.pp.pp' is executable (500) "POST /cgi-bin/getstats HTTP/1.0" [21/Apr/1995:00:28:39 +0500] [OK] [host: charlotte.med.nyu.edu] Invalid request "" (unknown method)
Tip: If you are the Administrator, you should keep an eye on the size of the error log. It can quickly run you out of disk space if you aren't careful.
The error log typically is found in a directory under the httpd directory. In a typical server setup, the directory might be
/usr/local/lib/httpd/logs
You need to ask your Administrator to tell you the exact location of the log file and to give you access to it. As you can see, several items are logged for each error message:
The exact messages listed in the error log depend on the type of HTTP server you are running. The earlier example was produced by the CERN HTTP server. You should browse the log after experiencing various errors to get an idea of what events they cause. In Listing 13.4, the message bad script request is a particularly useful message, which indicates that the script file was either not found or is not executable.
If you don't have access to the error log and don't find it convenient (or possible) to test your script at the command line, you might try debugging "the hard way" with simple print commands. In fact, this method is often the easiest to use and can quickly narrow down the source of a problem.
Note: Some Internet providers give you access to your own directory to run CGI scripts, but don't allow access to the command line. This is a difficult situation; the print command method is one of the debugging methods that still is available to you in this circumstance.
As an example, Listing 3.5 shows a section of a script used to search for jobs matching a certain criteria. To be completely realistic, I've even included a bug in the code. Can you find it?
# State must match if entered
if ($rqpairs{"State"} gt " ") {
if ($rqpairs{"State"} ne $data{"ST"}) {
$match = 0;
}
}
# Zip code must match if entered
if ($rqpairs{"Zip_Code"} gt " ") {
if ($rqpairs{"Zip_Code"} ne $data{"Z"}) {
$match = 0;
}
}
# Country must match if entered
if ($rqpairs{"Country"} gt " ") {
if ($rqpairs{"Country"} != $data{"C"}) {
$match = 0;
}
}
As you can see, this code is comparing several values entered in a form, stored in the associative array %rqpairs, with values in a database, stored in the associative array %data. The $match variable is used to indicate whether the record matches the criteria. The $match variable defaults to 1 and is changed to 0 if any of the criteria do not match.
The symptoms: When the code in Listing 13.5 is executed, $match always ends up being 0. The search is never successful, even if the exact values for State, Zip Code, and Country are entered.
To debug this problem with the debugger, you simply could step through each if statement block and display the value of the $match variable after each one. You can do the same thing with print statements. Listing 13.6 shows the same section of code shown in Listing 13.5 with print statements inserted. I left the print statements non-indented to make them easy to see.
Note: You will need to be sure your program outputs a correct MIME header before the output so that you will be able to view the results of the print statements on your browser. If your program already outputs HTML, you probably won't need to add anything.
# State must match if entered
if ($rqpairs{"State"} gt " ") {
if ($rqpairs{"State"} ne $data{"ST"}) {
$match = 0;
}
}
print "After State: match=$match";
# Zip code must match if entered
if ($rqpairs{"Zip_Code"} gt " ") {
if ($rqpairs{"Zip_Code"} ne $data{"Z"}) {
$match = 0;
}
}
print "After Zip: match=$match";
# Country must match if entered
if ($rqpairs{"Country"} gt " ") {
if ($rqpairs{"Country"} != $data{"C"}) {
$match = 0;
}
}
print "After Country: match=$match";
As you can see, I displayed the $match variable after each criterion is checked. The text in the print statement lets you know which of the print statements is being executed. Here is the output the print statements would produce:
After State: match=1 After Zip: match=1 After Country: match=0
Aha! It looks like the check for the Country field is always resulting in a match value of 0. If you're very observant, you've probably found the error already. Look at the following line again:
if ($rqpairs{"Country"} != $data{"C"}) {
Here, I accidentally used the numeric inequality operator (!=) when I should have used the string inequality operator (ne). It's a common typographical mistake.
You can follow this same method and use as many print statements as you need to diagnose the problem. After you're finished debugging, you'll need to remove every one of them. In the final section of this chapter, you'll learn about an alternate print routine called bugprint that you can use for this purpose, and then easily turn off.
Note: Because the output of our CGI program is being interpreted as HTML, it helps to include HTML codessuch as <BR> for a line breakin the text of your print statements.
In this section, you'll learn about some handy Perl programs you can use to assist in your debugging. They are short and easy to type in and use, and they can save you hours of time. Each program is explained and presented here.
Note: These programs have been tested under Perl 5.0 on a Unix system. You need to specify the correct location for the Perl interpreter on the first line of the program, and you may need to modify it slightly for your system.
The program shown in Listing 13.7 displays the environment available when a CGI program executes. A shortened version of this was presented in the section "Displaying the CGI Program's Environment," earlier in this chapter. This version is a bit longer, but displays more readable HTML.
#!/usr/bin/perl
MAIN: {
print "Content-type: text/html\n\n";
print "<HTML><HEAD><TITLE>Environment Display</TITLE>";
print "</HEAD><BODY>";
print "<H1>Environment Variables</H1>";
print "The following variables are present in the current environment:";
print "<UL>"
while (($key,$value) = each %ENV) {
print "<LI>$key = $value\n";
}
print "</UL>";
print "End of environment.";
print "</BODY></HTML>";
exit 0;
}
Listing 13.8 shows a simple script that displays all the variables from a form using the Get method. To use it, simply set the ACTION of the form to this program instead of your normal program, for example:
<FORM METHOD="GET" ACTION="/cgi-bin/show_get">
#!/usr/bin/perl
MAIN: {
print "Content-type: text/html\n\n";
print "<HTML><HEAD><TITLE>GET Variables</TITLE>";
print "</HEAD><BODY>";
print "<H1>GET Method Variables</H1>";
print "The following variables were sent:";
print "<UL>"
$request = $ENV{'QUERY_STRING'};
# Split request into name/value pairs
%rqpairs = split(/[&=]/, $request));
# Convert URL syntax to ASCII
foreach (%rqpairs) {
tr/+/ /;
s/%(..)/pack("c",hex($1))/ge;
}
# Display each value
while (($key,$value) = each %rqpairs) {
print "<LI>$key = $value\n";
}
print "</UL>";
print "End of variables.";
print "</BODY></HTML>";
exit 0;
}
The program shown in Listing 13.9 is similar to Listing 13.8, but it displays values for a Post query. This is a bit more complicated. Again, simply point the ACTION of your form to the location of this programfor example,
<FORM METHOD="POST" ACTION="/cgi-bin/show_post">
#!/usr/bin/perl
MAIN: {
print "Content-type: text/html\n\n";
print "<HTML><HEAD><TITLE>GET Variables</TITLE>";
print "</HEAD><BODY>";
print "<H1>GET Method Variables</H1>";
print "The following variables were sent:";
print "<UL>"
# Read POST data from standard input.
# The CONTENT_LENGTH variable tells us how
# many bytes to read.
read(STDIN, $request, $ENV{'CONTENT_LENGTH'});
# Split request into name/value pairs
%rqpairs = split(/[&=]/, $request));
# Convert URL syntax to ASCII
foreach (%rqpairs) {
tr/+/ /;
s/%(..)/pack("c",hex($1))/ge;
}
# Display each value
while (($key,$value) = each %rqpairs) {
print "<LI>$key = $value\n";
}
print "</UL>";
print "End of variables.";
print "</BODY></HTML>";
exit 0;
}
The Display Debugging Data program the simplest program in this section, but you may find itor your own modified versionvery useful. In the "Debugging with the Print Command" section, you learned about using print statements to display variables during sections of the program. You can use the bugprint subroutine shown in Listing 13.10 instead. It offers a simple advantage: You can turn it off.
The bugprint routine prints, but only if the variable $debug is set to 1. This means that you can quickly remove all the debugging from your program simply by setting $debug to 0. In addition, because it uses a different keyword than print, you quickly can search through the program to remove the debug commands when you're finished. Finally, it adds the <BR> HTML tag automatically to separate lines of output.
Listing 13.10 shows the code for the bugprint routine. It could really fit on a single line, but I've stretched it out to make its meaning clear.
sub bugprint {
if ($debug ==1) {
print "Debug:"
eval "print @_";
print "<BR>\n";
}
}
To use this subroutine, simply insert the code in Listing 13.10 at the end of your program. Then add the following command to the start of your program to turn on debugging:
$debug = 1
After you're through debugging, you can change the $debug value to 0 to deactivate all the debugging output. This makes it easy to quickly switch between the debug output and the normal output.
Remember that, because bugprint is a subroutine, you must refer to it with the & character or the do keyword. You can use variables in the statement, just as you would with print. Here are two examples:
do bugprint "The current value is:$result"; &bugprint "Key: $key Value: $value";
And now, a final word about debugging. Three words, to be exact: Don't Give Up. Debugging can be a long, time-consuming process with little reward. You can spend hours staring at code and testing it over and over before finally noticing one tiny typing mistake. Here are a few tips for the human side of debugging:
If you don't give up, you'll get through it and the program will work beautifully. Good luck and happy debugging!
In this chapter, you were introduced to the not-so-glamorous world of debugging CGI programs in Perl. You learned about many of the common mistakes that you can make in a Perl program, and many methods that you can use to pinpoint the part of your program that is causing an error.
You also looked at several techniques that can make it easier to narrow down an error. These include the HTTP error log, the source of the HTML output, the environment provided to the CGI program, and the good old-fashioned print statement.
Finally, you learned about several code segments and complete programs that can be helpful in debugging your own CGI programs or HTML forms.
Q: My program worked when I tested it, but doesn't work now that it's in use. What could the problem be?
A: This is common for two reasons:
Q: Are there any new syntax errors possible with Perl 5?
A: Yes, but not too many. Certain errors have been eliminated; for example, parentheses are usually not required with an if statement. The main cause for errors is the @ character. Perl 5 interprets @ as a variable reference, even in a quoted string. This means that if you include this character in a string (such as an e-mail address) you must be sure to escape it with a backslash: \@. Previous versions of Perl allowed this.
Q: You mentioned Python as an alternative to Perl for CGI programs. Are there any other alternative languages?
A: Yes. Many CGI programmers prefer to use C or C++, which can generate faster code than Perl or Python. Unix shell languages, such as sh and csh, also are common and easy to use for simple scripts. Any language can be used, as long as the server supports it.
Q: Will future versions of HTML, such as HTML 3.0, affect my CGI scripts?
A: The only affect will be how the browser interprets the output of your program. The HTML 3.0 standard allows most valid HTML 2.0 tags, so there is little chance that your program will become completely unusable; however, you might want to modify it to take advantages of new HTML tags.
Q: The data from a Post form doesn't seem to reach my CGI program at all. What's wrong?
A: This may be a browser problem or a misconfigured HTTP server. In addition, if the URL you are using to access your program is forwarded to another URL, the Post data might not be forwarded properly. Try using the other URL in the ACTION of your form.
Q: What are the most common HTTP servers?
A: You shouldn't have to worry, because the CGI standard is supported by most servers; however, some serversparticularly brand new versionsmight have trouble with your CGI program. The most common Unix-based servers at this writing are from CERN and NCSA. Netscape Corporation's server, NetSite, is becoming more popular; entirely new servers, such as Apache and Spinner, offer a wide array of new features.
Q: My script works at the command line, but can't read or write to a file when I run it on-line. What can cause this?
A: Remember that most servers run CGI scripts as the user nobody. A file that you can access is not necessarily accessible to that user. Be sure to allow the Read right, and Write if necessary, to all users; this is the only way to be sure the file can be used from the CGI script.