Appendix E

cgihtml Reference Guide


CONTENTS


The cgihtml library is a collection of routines written in C for parsing Common Gateway Interface input and for outputting HyperText Markup Language. These tasks, which normally require several lines of C, can be reduced to just a few lines. Using cgihtml enables you to focus on the main algorithms of your code rather than on laborious parsing and output routines.

Getting Started

The cgihtml routines were written for UNIX machines. The library has been successfully ported to Windows NT and Windows 3.1, and probably could be ported to other operating systems fairly easily.

The cgihtml library includes the following files:

READMEA file you should read first
CHANGESVersion revision information
TODOThings or changes I want to implement eventually
docs/Documentation directory
debug-cgi.shShell script to help debug CGI code
MakefileA makefile to compile and install cgihtml
cgi-lib.cSource code
html-lib.cSource code
llist.cSource code
string-lib.cSource code
cgi-lib.hHeader file for routines
html-lib.h Header file for routines
llist.h Header file for routines
string-lib.h Header file for routines
query-results.cExample program
mail.cgi.c Example program
ignore.cgi.c Example program
index-sample.cgi.c Example program
test.cgi.c Example program

Availability

The latest version of cgihtml is always available at

<URL:ftp://hcs.harvard.edu/pub/web/tools/cgihtml.tar.gz>

The cgihtml home page, which contains links to documentation and other cgihtml resources, is available at

<URL:http://hcs.harvard.edu/~eekim/web/cgihtml/>

You can subscribe to the cgihtml mailing list for the latest cgihtml information and announcements. To subscribe, send e-mail to [email protected] with the following body:

subscribe cgihtml

Installing cgihtml

The cgihtml archive comes in one of three different compressed formats: gzip (.gz), compress (.Z), and pkzip (.zip). To unpack the gzipped or UNIX-compressed archive, try the following commands:

% gzip -c cgihtml.tar.gz | tar xvof -

or

% zcat cgihtml.tar.Z | tar xvof -

To unpack the pkzipped archive, use the following command:

C:\> pkunzip -a cgihtml.zip

After you unpack the distribution, you need to compile it. First, look over the makefile, which by default looks like this:

# Makefile for cgihtml.a
# $Id: Makefile,v 1.4 1996/02/21 13:42:21 eekim Exp eekim $

CC= gcc
RANLIB = ranlib
CFLAGS= -g -Wall -DUNIX
CGI-BIN= /home/eekim/Web/cgi-bin

all: cgihtml.a query-results mail.cgi index-sample.cgi ignore.cgi test.cgi

cgihtml.a: cgi-lib.o llist.o html-lib.o string-lib.o
   ar cr cgihtml.a cgi-lib.o llist.o html-lib.o string-lib.o
   $(RANLIB) cgihtml.a

query-results: query-results.o cgihtml.a
   $(CC) query-results.o cgihtml.a -o query-results

mail.cgi: mail.cgi.o cgihtml.a
   $(CC) mail.cgi.o cgihtml.a -o mail.cgi

index-sample.cgi: index-sample.cgi.o cgihtml.a
   $(CC) index-sample.cgi.o cgihtml.a -o index-sample.cgi

ignore.cgi: ignore.cgi.o cgihtml.a
   $(CC) ignore.cgi.o cgihtml.a -o ignore.cgi

test.cgi: test.cgi.o cgihtml.a
   $(CC) test.cgi.o cgihtml.a -o test.cgi

install: all
   chmod a+x query-results mail.cgi index-sample.cgi ignore.cgi test.cgi
   strip query-results mail.cgi index-sample.cgi gnore.cgi test.cgi
   mv -f query-results mail.cgi index-sample.cgi ignore.cgi test.cgi $(CGI-BIN)

clean:
    rm -f *.o cgihtml.a
    rm -f query-results mail.cgi index-sample.cgi ignore.cgi test.cgi

You might want to change a few options. If you are compiling this package on any non-UNIX platform, especially Windows NT, comment out the -DUNIX reference, as follows:

CFLAGS= -g -Wall #-DUNIX

You can also change -g to -O2 if you don't want debugging information compiled into the library. If you do not have the Gnu C Compiler, change the CC variable to the name of your C compiler. It must be ANSI-compliant. Finally, change the value of CGI-BIN to the value of your cgi-bin directory.

To compile the library, type the following:

% make cgihtml.a

This command builds the library cgihtml.a. To compile the library as well as all the example programs, type the following:

% make all

While you compile the libraries on various UNIX machines, you might have trouble with the ranlib command. If your system doesn't seem to have this command, you most likely don't need it. Set the RANLIB variable in the makefile to True.

After you successfully build cgihtml.a, copy it and all the header files to your CGI source code directory.

Using cgihtml

When you use cgihtml, consider what header files you must include and what variables you need to initialize. The possible header files are

cgi-lib.h
html-lib.h
llist.h
string-lib.h

You do not have to include llist.h if you include cgi-lib.h because cgi-lib.h implicitly includes llist.h.

If you are parsing CGI input, you must take the following steps:

  1. Declare a linked list.
  2. Parse the data into the linked list.
  3. Manipulate the data.
  4. Clear the linked list.

The best way to understand how to use cgihtml properly is by example. The following is a basic template for all CGI applications:

/* template using cgihtml.a library */

#include <stdio.h>    /* standard io functions */
#include "cgi-lib.h"  /* CGI-related routines */
#include "html-lib.h" /* HTML-related routines */

int main()
{
  llist entries;  /* define a linked list; this is where the entries */
                  /* are stored. */

  read_cgi_input(&entries);  /* parse the form data and add it to the list */

  /* The data is now in a very usable form. To search the list entries
     by name, call the function:
        cgi_val(entries, "nameofentry")
     which returns a pointer to the value associated with "nameofentry". */

  html_header();           /* print HTML MIME header */
  html_begin("Output");    /* send appropriate HTML headers with title */
                           /* Output */

  /* display whatever data you wish, probably with printf() */

  html_end();             /* send appropriate HTML end footers ( ) */
  list_clear(&entries);   /* free up the pointers in the linked list */
  return 0;               send exit code of 0 -- successful */
}

Most of the C code throughout this book uses cgihtml, so look through the other chapters for more examples.

Compiling Your Program

To compile your program with the library, include the file cgihtml.a when you link your object files. For example, if your main object file is program.cgi.o, the following should work successfully:

% gcc -o program.cgi program.cgi.o cgihtml.a

Routines

This section contains listings and explanations for the routines contained in the cgihtml library.

cgi-lib.h

Variables

cgi-lib.h defines constants for the standard CGI environment variables. The value of the environment variable QUERY_STRING, for example, is stored in the constant QUERY_STRING in cgi-lib.h. Here is a list of the constants:

SERVER_SOFTWARE
SERVER_NAME
GATEWAY_INTERFACE
SERVER_PROTOCOL
SERVER_PORT
REQUEST_METHOD
PATH_INFO
PATH_TRANSLATED
SCRIPT_NAME
QUERY_STRING
REMOTE_HOST
REMOTE_ADDR
AUTH_TYPE
REMOTE_USER
REMOTE_IDENT
CONTENT_TYPE
CONTENT_LENGTH

Functions

The following is a listing of the functions. The following sections are the global definitions needed by the functions.

void die();
short accept_image()
void unescape_url()
int read_cgi_input(llist *entries);
char* cgi_val(llist l, char *name);
char **cgi_val_multi(llist l, char *name);
void print_cgi_env();
void print_entries(llist l);
char* escape_input(char *str);
short is_form_empty(llist l);
void die();

You should use die() in conjunction with UNIX's signal handling libraries. To prevent runaway processes, you should send an alarm signal after a certain amount of time and call die() upon receiving this signal.

At this stage, die() is somewhat primitive. It displays a simple error message and kills the program gracefully.

If you are not using UNIX, then die() is unavailable.

short accept_image();
The function accept_image() determines whether the browser accepts pictures. It does so by checking the HTTP_ACCEPT environment variable for an image MIME type. It returns a 1 if the browser accepts graphics, a 0 otherwise.
void unescape_url();
The function unescape_url() converts escaped URI values into their character form. read_cgi_input() calls this function. You rarely, if ever, need to access this function directly, but it is made available in case you do.
int read_cgi_input(llist *entries);
This routine parses the raw CGI data passed from the browser to the server and adds each associated name and value to the linked list entries. It parses information transmitted using both the GET and POST method. If it receives no information, it returns a 0; otherwise, it returns a 1.
Remember that receiving no information is not the same as receiving an empty form. An empty form means that the values are empty, but read_cgi_input() still reads the names of each field.
char* cgi_val(llist l, char *name);
The routine cgi_val() searches the linked list for the value of the entry named name and returns the value if it finds it. If it cannot find an entry with name name, it returns a null string.
char** cgi_val_multi(llist l, char *name);
This routine is the same as cgi_val() except that it returns multiple values with the same name to an array of strings. It returns a null string if it cannot find an entry with name name.
void print_cgi_env();
This routine prints the environment variables defined in cgi-lib.h. It prints (null) if the variables are empty.
void print_entries(llist l);
This generic routine iterates through the linked list and prints each name and associated value in HTML form. It uses the <dl> list format to display the list.
char* escape_input(char *str);
The routine escape_input() "escapes" shell metacharacters in the string. Because I could not find any authoritative documentation on what characters are considered metacharacters, escape_input() escapes all nonalphanumeric characters.
C routines including system() and popen() open up a Bourne shell process before running. If you do not escape shell metacharacters in the input (prefix metacharacters with a backslash), then malicious users might be able to take advantage of your system.
short is_form_empty(llist l);
This routine returns a 1 if an empty form is submitted; it returns 0 otherwise.

html-lib.h

The following explains the contents of the html-lib.h header file.

Functions

The following are listings the functions. The following sections are the global definitions needed by the functions.

void html_header();
void mime_header(char *mime);
void nph_header(char *status);
void show_html_page(char *loc)
void status(char *status);
void pragma(char *msg);
void html_begin(char *title);
void html_end();
void h1(char *str); ... void h6(char *str);
void hidden(char *name, char *value);
void html_header();
The routine html_header() prints a MIME-compliant header that should precede the output of any HTML document from a CGI script. It simply prints the following line and a blank line to stdout:
Content-Type: text/html
void mime_header(char *mime);
This routine enables you to print any MIME header. If you are about to send a GIF image to the standard output from your C CGI program, for example, precede your program with the following:
mime_header("image/gif");
/* now you can send your GIF file to stdout */
The mime_header() routine simply prints Content-Type: followed by your specified MIME header and a blank line.
void nph_header(char *status);
This routine sends a standard HTTP header for direct communication with the client using no parse header. status is the status code followed by the status message. To send a No Content header, for example, you can use the following:
nph_header("204 No Content");
html_header();
These lines send the following:
HTTP/1.0 204 No Content
Server: CGI using cgihtml
Content-Type: text/html
The nph_header() function does not send a blank line after printing the headers, so you must follow it with either another header or a blank line. Also, scripts using this function must have nph- preceding their filenames.
void show_html_page(char *loc);
This routine sends a Location: header. loc is the location of the HTML file you want sent to the browser. If you want to send the root index file from the CGI program, for example, use the following line:
show_html_page("/index.html");
void status(char *status);
This routine sends an HTTP Status header. status is a status code followed by a status message. To send a status code of 302 (temporary redirection) followed by a location header, for example, use the following:
status("302 Temporarily Moved");
show_html_page("http://hcs.harvard.edu/");
The status() function does not print a blank line following the header, so you must follow it with either another function that does output a blank line or an explicit printf("\r\n");.
void pragma(char *msg);
This routine sends an HTTP pragma header. It is most commonly used to tell the browser not to cache the document. Here's an example:
pragma("No-cache");
html_header();
As with status(), pragma() does not print a blank line following the header.
void html_begin(char *title);
The html_begin() function sends somewhat standard HTML tags that should generally be at the top of every HTML file. It sends the following:
<html> <head>
<title>title</title>
</head>
<body>
void html_end();
The html_end() function is the complement to html_begin(), sending the following HTML:
</body> </html>
Note that neither html_begin() nor html_end() are necessary for your CGI scripts to output HTML, but they are good style, and I encourage use of these routines.
void h1(char *str);
This routine surrounds str with the headline 1 tags: <h1>. Routines h2(), h3(), h4(), h5(), and h6() also do the same.
void hidden(char *name, char *value);
This routine prints an <input type=hidden> tag with name *name and value *value. It is useful for maintaining state.

llist.h

For most scripts, you will most likely never have to use any of the link list routines available, with the exception of list_clear(), because cgi-lib.h handles most common linked list manipulation almost transparently. You might sometimes want to manipulate the information directly, however, or perform special functions on each entry, in which case these routines can be useful.

Variables

The following are listings of the functions. The following sections are the global definitions needed by the functions.

typedef struct {
  char *name;
  char *value;
} entrytype;

typedef struct _node {
  entrytype entry;
  struct _node* next;
} node;

typedef struct {
  node* head;
} llist;

Functions

The following are listings of the functions. The following sections are the global definitions needed by the functions.

void list_create(llist *l);
node* list_next(node* w);
short on_list(llist *l, node* w);
short on_list_debug(llist *l, node* w);
void list_traverse(llist *l, void (*visit)(entrytype item));
node* list_insafter(llist* l, node* w, entrytype item);
void list_clear(llist* l);
void list_create(llist *l);
The routine list_create() creates and initializes the list, and it should be called at the beginning of every CGI script using this library to parse input.
node* list_next(node* w);
The routine list_next() returns the next node on the list.
short on_list(llist *l, node* w);
The routine on_list() returns a 1 if the node w is on the linked list l; otherwise, it returns a 0.
short on_list_debug(llist *l, node* w);
The previous routine makes the assumption that your linked list routines are bug-free, a possibly bad assumption. If you are using linked list routines and on_list() doesn't return the correct value, try using on_list_debug(), which makes no assumptions, is almost definitely reliable, but is a little slower than the other routine.
void list_traverse(llist *l,void (*visit)(entrytype item));
The routine list_traverse() lets you pass a pointer to a function that manipulates each entry on the list.
To use this routine, you must create a function that takes as its argument a variable of type entrytype. If you want to write your own print_entries() function, for example, you could do the following:
void print_element(entrytype item);
{
  printf("%s = %s\n",item.name,item.value);
}

void print_entries(llist entries);
{
  list_traverse(&stuff, print_element);
}
node* list_insafter(llist* l, node* w, entrytype item);
The routine list_insafter() adds the entry item after the node w and returns the pointer to the newly created node. I didn't write a function to insert before a node because my CGI functions don't need one.
void list_clear(llist* l);
This routine frees up the memory used by the linked list after you are finished with it. It is imperative that you call this function at the end of every program that calls read_cgi_input().

string-lib.h

This section lists and describes the contents of string-lib.h.

Functions

The following are listings of the functions. The following sections are the global definitions needed by the functions.

char* newstr(char *str);
char *substr(char *str, int offset, int len);
char *replace_ltgt(char *str);
char* newstr(char *str);
The function newstr() allocates memory and returns a copy of str. Use this function to allocate memory correctly and copy strings.
char *substr(char *str, int offset, int len);
This routine is equivalent to the Perl function with the same name. It returns a substring of str of length len starting from offset away from either the beginning or end of the string (if it's positive or negative, respectively).
char *replace_ltgt(char *str);
This routine replaces less-than (<) and greater-than (>) signs with their HTML-escaped equivalents (&lt; and &gt;, respectively).

Source Code

Listings E.1 through E.8 provide the complete source code for cgihtml.


Listing E.1. llist.h.
/* llist.h - Header file for llist.c
   Eugene Kim, [email protected]
   $Id: llist.h,v 1.2 1995/08/13 21:30:53 eekim Exp $

   Copyright (C) 1995 Eugene Eric Kim
   All Rights Reserved
*/

typedef struct {
  char *name;
  char *value;
} entrytype;

typedef struct _node {
  entrytype entry;
  struct _node* next;
} node;

typedef struct {
  node* head;
} llist;

void list_create(llist *l);
node* list_next(node* w);
short on_list(llist *l, node* w);
short on_list_debug(llist *l, node* w);
void list_traverse(llist *l, void (*visit)(entrytype item));
node* list_insafter(llist* l, node* w, entrytype item);
void list_clear(llist* l);


Listing E.2. llist.c.
/* llist.c - Minimal linked list library for revised CGI C library
   Eugene Kim, [email protected]
   $Id: llist.c,v 1.2 1995/08/13 21:30:53 eekim Exp $

   Copyright (C) 1995 Eugene Eric Kim
   All Rights Reserved
*/

#include <stdlib.h>
#include <string.h>
#include "llist.h"
#include "string-lib.h"

void list_create(llist *l)
{
  (*l).head = 0;
}

node* list_next(node* w)
{
  return (*w).next;
}

short on_list(llist *l, node* w)
{
  return (w != 0);
}

short on_list_debug(llist *l, node* w)
{
  node* current;

  if (w == 0)
    return 0;
  else {
    current = (*l).head;
    while ( (current != w) && (current != 0) )
      current = (*current).next;
    if (current == w)
      return 1;
    else
      return 0;
  }
}

void list_traverse(llist *l, void (*visit)(entrytype item))
{
  node* current;

  current = (*l).head;
  while (current != 0) {
    (*visit)((*current).entry);
    current = (*current).next;
  }
}

node* list_insafter(llist* l, node* w, entrytype item)
{
  node* newnode = malloc(sizeof(node));

  (*newnode).entry.name = newstr(item.name);
  (*newnode).entry.value = newstr(item.value);
  if ( (*l).head == 0) {
    (*newnode).next = 0;
    (*l).head = newnode;
  }
  else if (!on_list(l,w))
    /* ERROR: can't insert item after w since w is not on l */
    exit(1);
  else {
    /* insert node after */
    if (newnode == 0) /* can assume that w != NULL */
      /* ERROR: nothing to insert after */
      exit(1);
    else {
      (*newnode).next = (*w).next;
      (*w).next = newnode;
    }
  }
  return newnode;
}

void list_clear(llist* l)
{
  node* lastnode;
  node* nexttolast;
  node* current;

  while ((*l).head != 0) {
    current = (*l).head;
    if ((*current).next == 0) {
      free(current);
      current = 0;
      (*l).head = current;
    }
    else {
      while ( (*current).next != 0 ) {
    nexttolast = current;
    lastnode = (*current).next;
    current = lastnode;
      }
      free(lastnode);
      (*nexttolast).next = 0;
    }
  }
}


Listing E.3. string-lib.h.
/* string-lib.h - headers for string-lib.c
   $Id: string-lib.h,v 1.1 1995/08/13 21:30:53 eekim Exp $
*/

char *newstr(char *str);
char *substr(char *str, int offset, int len);
char *replace_ltgt(char *str);


Listing E.4. string-lib.c.
/* string-lib.c - generic string processing routines
   $Id: string-lib.c,v 1.2 1996/02/18 22:33:27 eekim Exp eekim $
   Copyright © 1996 Eugene Eric Kim
   All Rights Reserved.
*/

#include <malloc.h>
#include <stdio.h>
#include <string.h>
#include "string-lib.h"

char *newstr(char *str)
{
  return strcpy((char *)malloc(sizeof(char) * strlen(str)+1),str);
}

char *substr(char *str, int offset, int len)
{
  int slen, start, i;
  char *nstr;

  if (str == NULL)
    return NULL;
  else
    slen = strlen(str);
  nstr = malloc(sizeof(char) * slen + 1);
  if (offset >= 0)
    start = offset;
  else
    start = slen + offset - 1;
  if ( (start < 0) || (start > slen) ) /* invalid offset */
    return NULL;
  for (i = start; i < slen; i++)
    nstr[i - start] = str[i];
  nstr[slen] = '\0';
  return nstr;
}

char *replace_ltgt(char *str)
{
  int i,j = 0;
  char *new = malloc(sizeof(char) * (strlen(str) * 4 + 1));
  for (i = 0; i < strlen(str); i++) {
    if (str[i] == '<') {
      new[j] = '&';
      new[j+1] = 'l';
      new[j+2] = 't';
      new[j+3] = ';';
      j += 3;
    }
    else if (str[i] == '>') {
      new[j] = '&';
      new[j+1] = 'g';
      new[j+2] = 't';
      new[j+3] = ';';
      j += 3;
    }
    else
      new[j] = str[i];
    j++;
  }
  new[j] = '\0';
  return new;
}


Listing E.5. cgi-lib.h.
/* cgi-lib.h - header file for cgi-lib.c
   Eugene Kim, [email protected]
   $Id: cgi-lib.h,v 1.4 1996/02/21 13:40:41 eekim Exp eekim $

   Copyright (C) 1996 Eugene Eric Kim
   All Rights Reserved
*/

#include <stdlib.h>
#include "llist.h"

/* CGI Environment Variables */
#define SERVER_SOFTWARE getenv("SERVER_SOFTWARE")
#define SERVER_NAME getenv("SERVER_NAME")
#define GATEWAY_INTERFACE getenv("GATEWAY_INTERFACE")

#define SERVER_PROTOCOL getenv("SERVER_PROTOCOL")
#define SERVER_PORT getenv("SERVER_PORT")
#define REQUEST_METHOD getenv("REQUEST_METHOD")
#define PATH_INFO getenv("PATH_INFO")
#define PATH_TRANSLATED getenv("PATH_TRANSLATED")
#define SCRIPT_NAME getenv("SCRIPT_NAME")
#define QUERY_STRING getenv("QUERY_STRING")
#define REMOTE_HOST getenv("REMOTE_HOST")
#define REMOTE_ADDR getenv("REMOTE_ADDR")
#define AUTH_TYPE getenv("AUTH_TYPE")
#define REMOTE_USER getenv("REMOTE_USER")
#define REMOTE_IDENT getenv("REMOTE_IDENT")
#define CONTENT_TYPE getenv("CONTENT_TYPE")
#define CONTENT_LENGTH getenv("CONTENT_LENGTH")

#ifdef UNIX
void die();
#endif
short accept_image();

/* form processing routines */
void unescape_url(char *url);
int read_cgi_input(llist* entries);
char *cgi_val(llist l,char *name);
char **cgi_val_multi(llist l, char *name);

/* miscellaneous CGI routines */
void print_cgi_env();
void print_entries(llist l);
char *escape_input(char *str);
short is_form_empty(llist l);


Listing E.6. cgi-lib.c.
/* cgi-lib.c - C routines that make writing CGI scripts in C a breeze
   Eugene Kim, <[email protected]>
   $Id: cgi-lib.c,v 1.7 1996/02/21 13:40:27 eekim Exp eekim $
   Motivation: Perl is a much more convenient language to use when
     writing CGI scripts. Unfortunately, it is also a larger drain on
     the system. Hopefully, these routines will make writing CGI
     scripts just as easy in C.

   Copyright (C) 1996 Eugene Eric Kim
   All Rights Reserved
*/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "cgi-lib.h"
#include "html-lib.h"
#include "string-lib.h"

#ifdef UNIX
void die()
{
/* this routine needs some beefing up. I hope to eventually add:
     o more detailed information
     o error logging
     o perhaps sending a header which signifies an internal error
*/
  html_header();
  html_begin("CGI Error");
  printf("<h1>CGI Error</h1>\r\n");
  printf("An internal error has occurred. Please contact your web\r\n");
  printf("administrator. Thanks.\r\n");
  html_end();
  exit(1);
}
#endif

short accept_image()
{
  char *httpaccept = getenv("HTTP_ACCEPT");

  if (strstr(httpaccept,"image") == NULL)
    return 0;
  else
    return 1;
}

/* x2c() and unescape_url() stolen from NCSA code */
char x2c(char *what)
{
  register char digit;

  digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A')+10 : (what[0] - '0'));
  digit *= 16;
  digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A')+10 : (what[1] - '0'));
  return(digit);
}

void unescape_url(char *url)
{
  register int x,y;

  for (x=0,y=0; url[y]; ++x,++y) {
    if((url[x] = url[y]) == '%') {
      url[x] = x2c(&url[y+1]);
      y+=2;
    }
  }
  url[x] = '\0';
}

int read_cgi_input(llist* entries)
{
  int i,j,content_length;
  short NM = 1;
  char *input;
  entrytype entry;
  node* window;

  list_create(entries);
  window = (*entries).head;

  /* get the input */
  if (REQUEST_METHOD == NULL) {
    /* perhaps add an HTML error message here for robustness sake;
       don't know whether CGI is running from command line or from
       web server. In fact, maybe a general CGI error routine might
       be nice, sort of a generalization of die(). */
    fprintf(stderr,"caught by cgihtml: REQUEST_METHOD is null\n");
    exit(1);
  }
  if (!strcmp(REQUEST_METHOD,"POST")) {
    if (CONTENT_LENGTH != NULL) {
      content_length = atoi(CONTENT_LENGTH);
      input = malloc(sizeof(char) * content_length + 1);
      if (fread(input,sizeof(char),content_length,stdin) != content_length) {
    /* consistency error. */
    fprintf(stderr,"caught by cgihtml: input length < CONTENT_LENGTH\n");
    exit(1);
      }
    }
    else { /* null content length */
      /* again, perhaps more detailed, robust error message here */
      fprintf(stderr,"caught by cgihtml: CONTENT_LENGTH is null\n");
      exit(1);
    }
  }
  else if (!strcmp(REQUEST_METHOD,"GET")) {
    if (QUERY_STRING == NULL) {
      fprintf(stderr,"caught by cgihtml: QUERY_STRING is null\n");
      exit(1);
    }
    input = newstr(QUERY_STRING);
    content_length = strlen(input);
  }
  else { /* error: invalid request method */
    fprintf(stderr,"caught by cgihtml: REQUEST_METHOD invalid\n");
    exit(1);
  }
  /* parsing starts here */
  if (content_length == 0)
    return 0;
  else {
    j = 0;
    entry.name = malloc(sizeof(char) * content_length + 1);
    entry.value = malloc(sizeof(char) * content_length + 1);
    for (i = 0; i < content_length; i++) {
      if (input[i] == '=') {
    entry.name[j] = '\0';
    unescape_url(entry.name);
    if (i == content_length - 1) {
      strcpy(entry.value,"");
      window = list_insafter(entries,window,entry);
    }
    j = 0;
    NM = 0;
      }
      else if ( (input[i] == '&') || (i == content_length - 1) ) {
    if (i == content_length - 1) {
      entry.value[j] = input[i];
      j++;
    }
    entry.value[j] = '\0';
    unescape_url(entry.value);
    window = list_insafter(entries,window,entry);
    j = 0;
    NM = 1;
      }
      else if (NM) {
    if (input[i] == '+')
      entry.name[j] = ' ';
    else
      entry.name[j] = input[i];
    j++;
      }
      else if (!NM) {
    if (input[i] == '+')
      entry.value[j] = ' ';
    else
      entry.value[j] = input[i];
    j++;
      }
    }
    return 1;
  }
}

char *cgi_val(llist l, char *name)
{
  short FOUND = 0;
  node* window;

  window = l.head;
  while ( (window != 0) && (!FOUND) )
    if (!strcmp((*window).entry.name,name))
      FOUND = 1;
    else
      window = (*window).next;
  if (FOUND)
    return (*window).entry.value;
  else
    return NULL;
}

/* cgi_val_multi - contributed by Mitch Garnaat <[email protected]>;
   modified by me */

char **cgi_val_multi(llist l, char *name)
{
  short FOUND = 0;
  node* window;
  char **ret_val = 0;
  int num_vals = 0, i;

  window = l.head;
  while (window != 0) {
    if (!strcmp((*window).entry.name,name)) {
      FOUND = 1;
      num_vals++;
    }
    window = (*window).next;
  }
  if (FOUND) {
    /* copy the value pointers into the returned array */
    ret_val = (char**) malloc(sizeof(char*) * (num_vals + 1));
    window = l.head;
    i = 0;
    while (window != NULL) {
      if (!strcmp((*window).entry.name,name)) {
    ret_val[i] = (*window).entry.value;
    i++;
      }
      window = (*window).next;
    }
    /* NULL terminate the array */
    ret_val[i] = 0;
    return ret_val;
  }
  else
    return NULL;
}

/* miscellaneous useful CGI routines */

void print_cgi_env()
{
  printf("<p>SERVER_SOFTWARE = %s<br>\n",SERVER_SOFTWARE);
  printf("SERVER_NAME = %s<br>\n",SERVER_NAME);
  printf("GATEWAY_INTERFACE = %s<br>\n",GATEWAY_INTERFACE);

  printf("SERVER_PROTOCOL = %s<br>\n",SERVER_PROTOCOL);
  printf("SERVER_PORT = %s<br>\n",SERVER_PORT);
  printf("REQUEST_METHOD = %s<br>\n",REQUEST_METHOD);
  printf("PATH_INFO = %s<br>\n",PATH_INFO);
  printf("PATH_TRANSLATED = %s<br>\n",PATH_TRANSLATED);
  printf("SCRIPT_NAME = %s<br>\n",SCRIPT_NAME);
  printf("QUERY_STRING = %s<br>\n",QUERY_STRING);
  printf("REMOTE_HOST = %s<br>\n",REMOTE_HOST);
  printf("REMOTE_ADDR = %s<br>\n",REMOTE_ADDR);
  printf("AUTH_TYPE = %s<br>\n",AUTH_TYPE);
  printf("REMOTE_USER = %s<br>\n",REMOTE_USER);
  printf("REMOTE_IDENT = %s<br>\n",REMOTE_IDENT);
  printf("CONTENT_TYPE = %s<br>\n",CONTENT_TYPE);
  printf("CONTENT_LENGTH = %s<br></p>\n",CONTENT_LENGTH);
}

void print_entries(llist l)
{
  node* window;

  window = l.head;
  printf("<dl>\r\n");
  while (window != NULL) {
    printf(" <dt> <b>%s</b>\r\n",(*window).entry.name);
    printf(" <dd> %s\r\n",replace_ltgt((*window).entry.value));
    window = (*window).next;
  }
  printf("</dl>\r\n");
}

char *escape_input(char *str)
/* takes string and escapes all metacharacters. should be used before
   including string in system() or similar call. */
{
  int i,j = 0;
  char *new = malloc(sizeof(char) * (strlen(str) * 2 + 1));

  for (i = 0; i < strlen(str); i++) {
    if (!( ((str[i] >= 'A') && (str[i] <= 'Z')) ||
       ((str[i] >= 'a') && (str[i] <= 'z')) ||
       ((str[i] >= '0') && (str[i] <= '9')) )) {
      new[j] = '\\';
      j++;
    }
    new[j] = str[i];
    j++;
  }
  new[j] = '\0';
  return new;
}

short is_form_empty(llist l)
{
  node* window;
  short EMPTY = 1;

  window = l.head;
  while ( (window != NULL) && (EMPTY == 1) ) {
    if (strcmp((*window).entry.value,""))
      EMPTY = 0;
    window = (*window).next;
  }
  return EMPTY;
}


Listing E.7. html-lib.h.
/* html-lib.h - header file for html-lib.c
   Eugene Kim, [email protected]
   $Id: html-lib.h,v 1.3 1996/02/18 22:33:27 eekim Exp eekim $

   Copyright (C) 1996 Eugene Eric Kim
   All Rights Reserved
*/

void html_header();
void mime_header(char *mime);
void nph_header(char *status);
void show_html_page(char *loc);
void status(char *status);
void pragma(char *msg);
void html_begin(char *title);
void html_end();

/* better to do printf inside of function, or return string? */
void h1(char *str);
void h2(char *str);
void h3(char *str);
void h4(char *str);
void h5(char *str);
void h6(char *str);
void hidden(char *name, char *value);


Listing E.8. html-lib.c.
/* html-lib.c - C routines that output various HTML constructs
   Eugene Kim, [email protected]
   $Id: html-lib.c,v 1.3 1996/02/18 22:33:27 eekim Exp eekim $

   Copyright (C) 1996 Eugene Eric Kim
   All Rights Reserved
*/

#include <stdio.h>
#include "html-lib.h"

/* HTTP headers */

void html_header()
{
  printf("Content-type: text/html\r\n\r\n");
}

void mime_header(char *mime)
/* char *mime = valid mime type */
{
  printf("Content-type: %s\r\n\r\n",mime);
}

void nph_header(char *status)
{
  printf("HTTP/1.0 %s\r\n",status);
  printf("Server: CGI using cgihtml\r\n");
}

void show_html_page(char *loc)
{
  printf("Location: %s\r\n\r\n",loc);
}

void status(char *status)
{
  printf("Status: %s\r\n",status);
}

void pragma(char *msg)
{
  printf("Pragma: %s\r\n",msg);
}

/* HTML shortcuts */

void html_begin(char *title)
{
  printf("<html> <head>\n");
  printf("<title>%s</title>\n",title);
  printf("</head>\n\n");
  printf("<body>\n");
}

void html_end()
{
  printf("</body> </html>\n");
}

void h1(char *str)
{
  printf("<h1>%s</h1>\n",str);
}

void h2(char *str)
{
  printf("<h2>%s</h2>\n",str);
}

void h3(char *str)
{
  printf("<h3>%s</h3>\n",str);
}

void h4(char *str)
{
  printf("<h4>%s</h4>\n",str);
}

void h5(char *str)
{
  printf("<h5>%s</h5>\n",str);
}

void h6(char *str)
{
  printf("<h6>%s</h6>\n",str);
}

void hidden(char *name, char *value)
{
  printf("<input type=hidden name=\"%s\" value=\"%s\">\n",name,value);
}