Previous Page TOC Index Next Page See File


13

Debugging CGI Programs

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 lucky—debugging 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.

Examining the Debugging Process

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.

Determining which Program Has a Problem

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 process—for 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:

Determining whether the Program Is Being Executed

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.

Checking the Program's Syntax

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.

Checking Syntax at the Command Line

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.

Interpreting Perl Error Messages

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.

Looking At the Causes of Common Syntax Errors

Some syntax errors are very easy to spot—for 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.

Punctuation Problems

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.

Assignment and Equality Operators

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 constantly—usually 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 code—in 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:

String and Numeric Equality Operators

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 strings—combinations of letters—instead 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.

Variable Syntax Errors

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 array—this is how Perl can tell which type of variable you are referring to.

Viewing HTML Source of Output

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.

Using MIME 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 anything—text, 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.

Examining Problems in the HTML Output

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.

Displaying the Output as Text

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>...
Using the Direct Method: Testing with TELNET

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 server—the 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

Viewing the CGI Program's Environment

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.

Displaying the "Raw" Environment

The easiest way to determine the environment going into the program is to display it. This means using a different program temporarily—one 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:

Displaying Name/Value Pairs

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

Debugging at the Command Line

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.

Testing without the HTTP Server

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 parameters—in 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.

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.

Using Perl's Debug Mode

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

Print

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>

Reading the Server Error Log

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.

Debugging with the Print Command

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 codes—such as <BR> for a line break—in the text of your print statements.

Looking At Useful Code for Debugging

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.

Show Environment

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;
}

Show GET Values

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;
}

Show Post Values

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 program—for 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;
}

Display Debugging Data

The Display Debugging Data program the simplest program in this section, but you may find it—or your own modified version—very 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";

A Final Word about Debugging

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!

Summary

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&A

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 servers—particularly brand new versions—might 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.

Previous Page TOC Index Next Page See File