分类:
2008-08-24 11:32:17
This is an introduction to writing CGI programs in the . The reader is assumed to know the basics of C as well how to write simple forms in and to be able to install CGI scripts on a Web server. The principles are illustrated with very simple examples.
As my document How to write HTML forms briefly explains, you need a server side-script in order to use HTML forms reliably. Typically, there are simple server-side scripts available for simple, common ways of processing form submissions, such as sending the data in text format by E-mail to a specified address.
However, for more advanced processing, such as collecting data into a file or database, or retrieving information and sending it back, or doing some calculations with the submitted data, you will probably need to write a server-side script of your own.
CGI is simply an interface between HTML forms and server-side scripts. It is not the only possibility—see the excellent tutorial by for both an introduction to the concepts of CGI and notes on other possibilities.
If someone suggests using JavaScript as an alternative to CGI, ask him to read my . Briefly, JavaScript is inherently unreliable at least if not “backed up” with server-side scripting.
The above-mentioned is a great tutorial. The following introduction of mine is just another attempt to present the basics; please consult other sources if you get confused or need more information.
It will look like the following on your current browser:
You can try it if you like. Just in case the server used isn’t running and accessible when you try it, here’s what you would get as the result:
Multiplication results
The product of 4 and 9 is 36.
We will now analyze how the example above works.
Assume that you type 4 into one input field and
9 into another and then invoke submission—. The browser pick up this server name
from the value of
ACTION
attribute where it
occurs as the host name part of a URL.
(Quite often, the ACTION
attribute refers, often using
a relative URL, to a script on the same server as the document
resides on, but this is not necessary, as this example shows.)
When sending the request, the browser provides additional information,
specifying a relative URL, in this case
/cgi-bin/run/~jkorpela/mult.cgi?m=4&n=9
This was constructed from that part of the ACTION
value
that follows the host name, by appending a question mark
“?
” and the form data in
.
The server to which the request was sent
(in this case, )
will then process
it according to its own rules. Typically, the server’s configuration
defines how the relative URLs are mapped to file names and which
directories/folders are interpreted as containing CGI scripts.
As you may guess, the part
cgi-bin/
in the URL causes
such interpretation in this case. This means that instead of just
picking up and
sending back (to the browser that sent the request) an HTML document
or some other file, the server invokes a script or a program
specified in the URL (mult.cgi
in this case) and passes
some data to it (the data m=4&n=9
in this case).
It depends on the server how this really happens.
In this particular case, the server actually runs the (executable)
program in the file mult.cgi
in the subdirectory
cgi-bin
of user
jkorpela
’s home directory. It could be something
quite different, depending on server configuration.
The often-mystified abbreviation CGI, for Common Gateway Interface, refers just to a convention on how the invocation and parameter passing takes place in detail.
Invocation means different things in different cases. For a script, the server would invoke a Perl interpreter and make it execute the script in an interpretive manner. For an executable program, which has typically been produced by a compiler and a loader from a source program in a language like C, it would just be started as a separate process.
Although the word script typically suggests that the code is interpreted, the term CGI script refers both to such scripts and to executable programs. See the in by Nick Kew.
In order to set up a C program as a CGI script, it needs to be turned into a binary executable program. This is often problematic, since people largely work on Windows whereas servers often run some version of UNIX or Linux. The system where you develop your program and the server where it should be installed as a CGI script may have quite different architectures, so that the same executable does not run on both of them.
This may create an unsolvable problem. If you are not allowed to log on the server and you cannot use a binary-compatible system (or a cross-compiler) either, you are out of luck. Many servers, however, allow you log on and use the server in interactive mode, as a “shell user,” and contain a C compiler.
your C program on the server (or, in principle, on a system with the same architecture, so that binaries produced for it are executable on the server too).
Normally, you would proceed as follows:
GET
method,
the input is to be read from the environment variable.
QUERY_STRING
.
(The program may also read data from files—but these must then
reside on the server.)
It should generate output on the standard output stream
(stdout
) so that it starts with suitable HTTP
headers. Often, the output is in HTML format.
QUERY_STRING
so that it contains the test data as it will be sent as form
data. E.g.,
if you intend
to use a form where a field named foo
contains the input data,
you can give the commandYou need to put the executable into
a suitable directory and name it according to server-specific conventions.
Even the compilation commands needed here might differ from what
you are used to on your workstation.
For example, if the server runs some flavor of Unix and has the
Gnu C compiler available, you would typically use a compilation
command like gcc
and
then move (mv
) mult.c
to a directory with
a name like cgi-bin
.
Instead of gcc
, you might need to use cc
.
You really need to check local
instructions for such issues.
has no fixed meaning in general. However,
there can be server-dependent
(and operating system dependent) rules for naming executable files.
Typical extensions for executables are .cgi
and .exe
.
As usual when starting work with some new programming technology, you should probably first make a trivial program work. This avoids fighting with many potential problems at a time and concentrating first on the issues specific to the environment, here CGI.
You could use the following program that just prints Hello world but preceded by HTTP headers as required by the CGI interface. Here the header specifies that the data is plain ASCII text.
#include
int main(void) {
printf("Content-Type: text/plain;charset=us-ascii\n\n");
printf("Hello world\n\n");
return 0;
}
After compiling, loading, and uploading, you should be able
to test the script simply by entering the URL in the browser’s
address bar. You could also make it the destination of a normal
link in an HTML document. The URL of course depends on how you set
things up; the URL for my installed Hello world
script is the following:
For forms that use METHOD="GET"
(as our
above uses, since
this is the default),
CGI specifications say that the data is passed to
the script or program in an environment variable called
QUERY_STRING
.
It depends on the scripting or programming
language used how a program can access the value of an environment
variable. In the C language, you would use the
library function getenv
(defined in the
standard library stdlib
) to access the value as
a string. You might then use various techniques to pick up data
from the string, convert parts of it to numeric values, etc.
The output from the script or program to “primary output
stream” (such as stdin
in the C language) is handled
in a special way. Effectively, it is directed so that it gets sent
back to the browser. Thus, by writing a C program that it writes
an HTML document onto its standard output, you will make that document
appear on user’s screen as a response to the form submission.
In this case, the source program in C is the following:
#include
#include
int main(void)
{
char *data;
long m,n;
printf("%s%c%c\n",
"Content-Type:text/html;charset=iso-8859-1",13,10);
printf("Multiplication results \n");
printf("Multiplication results
\n");
data = getenv("QUERY_STRING");
if(data == NULL)
printf("Error! Error in passing data from form to script.");
else if(sscanf(data,"m=%ld&n=%ld",&m,&n)!=2)
printf("
Error! Invalid data. Data must be numeric.");
else
printf("
The product of %ld and %ld is %ld.",m,n,m*n);
return 0;
}
As a disciplined programmer, you have probably noticed that the program makes no check against integer overflow, so it will return bogus results for very large operands. In real life, such checks would be needed, but such considerations would take us too far from our topic.
Note: The first printf
function call prints out
data that will be sent by the server as an HTTP header.
This is required for several reasons, including the fact that
a CGI script can send any data (such as an image or a plain text file)
to the browser, not just HTML documents.
For HTML documents, you can just use the printf
function
call above as such; however, if your
is different from ISO 8859-1 (ISO Latin 1),
which is the most common on the Web, you need to replace
iso-8859-1
by the
.
I have compiled this program and saved the executable program
under the name mult.cgi
in my directory for CGI scripts
at .
This implies that any
form with
action=
will, when submitted, be processed by that program.
Consequently, anyone could write a form
of his own with the same ACTION
attribute and pass
whatever data he likes to my program. Therefore, the program
needs to be able to handle any data.
Generally, you need to check the data before starting to process it.
METHOD="POST"
METHOD="POST"
Let us consider next a different processing for form data. Assume that we wish to write a form that takes a line of text as input so that the form data is sent to a CGI script that appends the data to a text file on the server. (That text file could be readable by the author of the form and the script only, or it could be made readable to the world through another script.)
It might seem that the problem is similar to the
; one would just need
a different form and a different script (program).
In fact, there is a difference. The example above can be regarded
as a “pure query” that does not change the
“state of the world.”
In particular, it is “idempotent,” i.e. the same form data could
be submitted as many times as you like without causing any problems
(except minor waste of resources). However, our current task needs
to cause such changes—a change in the content of a file that is
intended to be more or less permanent. Therefore, one should use
METHOD="POST"
. This is explained in more detail in
the document
Here we will take it for granted that
METHOD="POST"
needs to be used and we will consider the
technical implications.
For forms that use METHOD="POST"
,
CGI specifications say that the data is passed to
the script or program
in the standard input stream (stdin
), and the
length (in bytes, i.e. characters) of the data is passed
in an environment variable called
CONTENT_LENGTH
.
Reading from standard input sounds probably simpler than
reading from an environment variable, but there are complications.
The server is not required to pass the data so that
when the CGI script tries to read more data than there is, it would
get an end of file indication! That is, if you read e.g. using
the getchar
function in a C program, it is undefined
what happens after reading all the data characters; it is not guaranteed
that the function will return EOF
.
When reading the input, the program must not try to read more
than CONTENT_LENGTH
characters.
A relatively simple
C program
for accepting input via CGI and
METHOD="POST"
is the following:
#include
#include
#define MAXLEN 80
#define EXTRA 5
/* 4 for field name "data", 1 for "=" */
#define MAXINPUT MAXLEN+EXTRA+2
/* 1 for added line break, 1 for trailing NUL */
#define DATAFILE "../data/data.txt"
void unencode(char *src, char *last, char *dest)
{
for(; src != last; src++, dest++)
if(*src == '+')
*dest = ' ';
else if(*src == '%') {
int code;
if(sscanf(src+1, "%2x", &code) != 1) code = '?';
*dest = code;
src +=2; }
else
*dest = *src;
*dest = '\n';
*++dest = '\0';
}
int main(void)
{
char *lenstr;
char input[MAXINPUT], data[MAXINPUT];
long len;
printf("%s%c%c\n",
"Content-Type:text/html;charset=iso-8859-1",13,10);
printf("Response \n");
lenstr = getenv("CONTENT_LENGTH");
if(lenstr == NULL || sscanf(lenstr,"%ld",&len)!=1 || len > MAXLEN)
printf("Error in invocation - wrong FORM probably.");
else {
FILE *f;
fgets(input, len+1, stdin);
unencode(input+EXTRA, input+len, data);
f = fopen(DATAFILE, "a");
if(f == NULL)
printf("
Sorry, cannot store your data.");
else
fputs(data, f);
fclose(f);
printf("
Thank you! Your contribution has been stored.");
}
return 0;
}
Essentially, the program retrieves the
information about the number of characters in the input
from value of the
CONTENT_LENGTH
environment variable.
Then it unencodes (decodes) the data, since the data arrives in the
that was already mentioned.
The program has been written for a form where the text input field
has the name data
(actually, just the length of the name
matters here). For example, if the user types
Hello there!
then the data will be passed to the program encoded as
data=Hello+there%21
(with space encoded as +
and exclamation mark encoded
as %21
). The unencode
routine in the program
converts this back to the original format. After that,
the data is appended to a file (with a fixed file name),
as well as echoed back to the user.
Having compiled the program I have saved it as collect.cgi
into the directory for CGI scripts. Now a form like the following
can be used for data submissions:
Finally, we can write ; it only needs to copy the content of a given text file onto standard output:
#include
#include
#define DATAFILE "../data/data.txt"
int main(void)
{
FILE *f = fopen(DATAFILE,"r");
int ch;
if(f == NULL) {
printf("%s%c%c\n",
"Content-Type:text/html;charset=iso-8859-1",13,10);
printf("Failure \n");
printf("Unable to open data file, sorry!"); }
else {
printf("%s%c%c\n",
"Content-Type:text/plain;charset=iso-8859-1",13,10);
while((ch=getc(f)) != EOF)
putchar(ch);
fclose(f); }
return 0;
}
Notice that this program prints (when successful) the data
as plain text, preceded by a header that says this, i.e.
has text/plain
instead of text/html
.
A form that invokes that program can be very simple, since no input data is needed:
Finally, here’s what the two forms look like. You can now test them:
Please notice that anything you submit here will become visible to the world:
The content of the text file to which the submissions are stored will be displayed as plain text.
Even though the output is declared to be plain text, Internet Explorer
may interpret it partly as containing HTML markup. Thus, if someone enters
data that contains such markup, strange things would happen.
The program takes this into account
by writing the NUL character ('\0'
) after each occurrence
of the greater-than character lt;
, so that it will not
be taken (even by IE) as starting a tag.
You may now wish to read , which tells you all the basic details about CGI. The next step is probably to see what the contains. Beware that it is relatively old.
There is a lot of material, including introductions and tutorials, in the . Notice in particular the section , which contains libraries that can make it easier to process form data. It can be instructive to parse simple data format by using code of your own, as was done in the simple examples above, but in practical application a library routine might be better.
The C language was originally designed for an environment where
only ASCII characters were used. Nowadays, it can be
used—with caution—for processing