Home -> C Callable CGI Library
Overview

CGI

Error reporting

Comma Separated Values

Singly Linked List
Maintained by suitti@uitti.net, Stephen Uitti

Table Of Contents

Synopsis

     #include 
     #include "cgi.h"

     Sun:
     NET=-lsocket -lnsl
     cc file.c -lccgi -lerr -lsll -lgdbm ${NET} [Oracle libraries]

or, without Oracle support (login validation & dynamic SQL)

     #include 
     #include "cgi.h"

     cc file.c -lnodbccgi -lerr -lsll -lgdbm ${NET}

or, with Postgres

     cc file.c -lccgi -lerr -lsll -lgdbm ${NET} -lpq

or, with MSQL

     cc file.c -lccgi -lerr -lsll -lgdbm ${NET} -lmsql

Description

The libccgi.a library provides functions supporting web server based applications written in the C language using a CGI interface. Most routines are intended to be widely usable across such applications. The primary goals of the library are to isolate the CGI interface to share construction an maintenance of the code, to provide superior functionality, to provide superior performance, and to ease the maintenance of the calling applications by reducing their maintenance.

The gdbm database library is only needed if the application uses this library's login authentication system. This includes calls to cgi_emit_login or cgi_validate_user.

The socket and nsl libraries are only needed for applications which require connections to other servers. This includes calls to cgi_wdeliver, cgi_wget_close, cgi_wget_open, and cgi_wget_openh. These libraries are only required on Solaris installations. For example, they are not required (or available) on Linux systems.

The library may be compiled without Oracle support, in which case, only gdbm login is supported. The Makefile supports a target:
make installnodb

Function Names

Date Functions

cgi_add_bdays, cgi_canonical_date, cgi_canonical_date_time, cgi_cnvdate, cgi_date_fmt_mmddccyy, cgi_fmt_ansi_date, cgi_fmt_ansi_date_time, cgi_get_ansi_date, cgi_get_ansi_date_time, cgi_fmt_date, cgi_fmt_date_time, cgi_get_date, cgi_get_date_time, check_error_date, dbsql_isbday

Debug Functions

cgi_osql_print, cgi_print_cookies, cgi_print_form_info, cgi_printenv

Form and Path Data Functions

cgi_form_add, cgi_form_datum, cgi_form_info, cgi_free_path_info, cgi_path_info, cgi_rm_fi

HTML Functions

cgi_err, cgi_form, cgi_fts, cgi_header, cgi_icheckbox, cgi_ihidden, cgi_ipassword, cgi_iradio, cgi_mk_select, cgi_mks_select, cgi_iselect, cgi_iselects, cgi_itext, cgi_itextr, cgi_itext_size, cgi_itextr_size, cgi_itextarea, cgi_itextarea_spell, cgi_mk_base_url, cgi_mk_help_url, cgi_mk_url, cgi_prompt, cgi_quote_sql, cgi_rtable, cgi_rtablei, cgi_rtablei_sub, cgi_search_form, cgi_search_sql, cgi_select_sql, cgi_setup_url, cgi_table, cgi_tablel, cgi_text, cgi_trailer

Navigation Functions

cgi_page_children, cgi_page_content, cgi_page_header, cgi_page_lookup, cgi_page_nav

Mini SQL Database Functions

cgi_mclose, cgi_mlogin, cgi_msql, cgi_inserts_msql

Oracle Database Functions

cgi_get_odbname, cgi_oclose, cgi_ologin, cgi_osql

PostgreSQL Database Functions

cgi_plogin, cgi_psql, cgi_pclose

Remote Access Functions

cgi_cclose, cgi_ccon, cgi_wdeliver, cgi_wget_close, cgi_wget_open, cgi_wget_openh

String Functions

cgi_add_commas, cgi_canonical_newlines, cgi_crypt, cgi_fmt_account, cgi_fraction, cgi_fraction_check, cgi_mklower, cgi_mkupper, cgi_replace, cgi_spell, cgi_spell_check, cgi_strip, cgi_strip_commas, cgi_strip_dashes, cgi_strip_dollar, cgi_tbltocsv, cgi_validate_account, cgi_valid_dollar, cgi_valid_ndollar, cgi_valid_number, cgi_valid_nnumber, convert_all_newlines, strip_all_newlines, striplws, striptws, striptz, strnistr stristr

Web Login Functions

cgi_create_cookie, cgi_emit_login, cgi_getcookie, cgi_invalidate_cookies, cgi_validate_user

Function Details

time_t cgi_add_bdays(time_t i_date, int bdays);

Add (subtract) business days to a date. Knows weekends. Knows holidays.

char *cgi_canonical_date(char *in_date);

Convert any date string into an oracle 4 digit year date string. For example, '23-OCT-2000'. Returns a pointer to a malloc'ed buffer, which should be free'd by caller. Returns NULL if error.

char *cgi_add_commas(char *str);

Add commas to a string. For example, "123456789" becomes "123,456,789". If there is a decimal point, it starts there. For example, "1234567.12" becomes "1,234,567.12". Otherwise, it starts at the end of the string. Return a copy in static space, that is overwritten on each call. The string may not expand to longer than 100 characters (not enforced).

char *cgi_canonical_newlines(char *str)

Newlines are replaced with Unix style newlines. DOS style newlines, ^M^J, Macintosh style newlines, ^M and Unix style newlines, ^J, are recognized. They are all replaced with Unix style newlines. The string is modified in place.

For example, embedded newlines collected using textarea form widgets are stored as carriage returns (^M). Developer 2000 uses line feed characters (^J) to represent newlines. So, web forms should convert text data before it is stored in the database.

void cgi_cclose(int stream);

Close a client stream opened with ccon.

int cgi_ccon(char *service, char *host);

Connect to a remote server. AF_INET stream sockets are always used. service - the name or number of the port that the server serves. host - the name or internet number of the host where the server runs. The file descriptor of the socket is returned. If there is an error, -1 is returned, errno may be of value.

time_t cgi_cnvdate(char *date_str);

Convert any date string into an Unix time_t. Returns -1 for error, as the mktime(3C) manual page indicates.

char *cgi_create_cookie(char *user, char *passwd);

Create a cookie. Return the generated cookie name=value string. A file called "master" has the last number used. Each cookie has a file, named by the cookie name. Return NULL for error (unlikely).

void cgi_crypt(char *str);

Simple reversible substitution cypher. A child can break it. The goal is so that when the administrator looks at a cookie file, they do not have the password staring in their face. You only need one routine for encrypt or decrypt. The string is encrypted or decrypted in place.

char *cgi_canonical_date_time(char *in_date);

Convert any date/time string into an oracle 4 digit year date/time string. For example, '23-OCT-2000 15:23:37'. Returns a pointer to a malloc'ed buffer, which should be free'd by caller. Returns NULL if error.

char *cgi_date_fmt_ccyymmdd(char *when);

Format a date string into "ccyymmddhhmmss". For example, a returned string might by "19990816142115". Returns a pointer to a static buffer, which is overwritten by each call. Useful in filenames which sort chronologically in listings.

char *cgi_date_fmt_mmddccyy(char *when);

Format a date string into "mm/dd/ccyy". Returns a pointer to a static buffer, which is overwritten by each call.

const char *cgi_get_odbname(void);

Return the Oracle database name. Returns NULL if cgi_ologin has not been called.

void cgi_emit_login(const char *url, const char *error);

Emit the login screen form. If URL is NULL, omit it from the hidden variable URL. If errro is not NULL, it is printed in bold red before the login screen. Typically, it is a error from a previous login.

void cgi_err(char *format, ...);

Emit an error for a cgi program. Operates like printf(3). Uses bold red text. In many ways, routines from the liberr library are superior, and should be considered.

char *cgi_fmt_date(time_t when);

Format a time_t into "DD-MON-YYYY". Returns a pointer to a static buffer, which is overwritten by each call.

char *cgi_fmt_date_time(time_t when);

Format a time_t into "DD-MON-YYYY HH24:MI:SS". Returns a pointer to a static buffer, which is overwritten by each call.

char *cgi_fmt_ansi_date(time_t when);

Format a time_t into "YYYY-MM-DD". Returns a pointer to a static buffer, which is overwritten by each call.

char *cgi_fmt_ansi_date_time(time_t when);

Format a time_t into "YYYY-MM-DD HH:MM:SS". Returns a pointer to a static buffer, which is overwritten by each call.

void cgi_form(const char *url);

Emit <form> start. Use cgi_mk_url to generate references to the same application. There is no call to end a form. Generally it is sufficient to use:

puts("</form>");


struct sll *cgi_form_add(const char *key, const char *dat);

Add a key/datum pair to the internal cgi_fi form info list. Adds to the beginning, so that searches find the new one. Creates copies of key and datum. Returns NULL if error - out of RAM. All data may be removed from the internal list with cgi_rm_fi.

char *cgi_form_datum(const char *key);

Get datum for key. The key/datum pairs are entered either from form data - parsed by cgi_form_info, or explicitly added with cgi_form_add. All data may be removed from the internal list with cgi_rm_fi.

struct sll *cgi_form_info(void);

Parse POST style form data, and return an array with the decoded data. Return the form list as an sll if there is any data. Returns NULL for no data or any error.

char *cgi_fmt_account(char *account);

Format an account number. Basically, add a dash. Given an account number "12345678", turn it into "123-45678". Returns a pointer to a static buffer which is overwritten on subsequent calls. Return NULL if passed account is not long enough or NULL. See cgi_validate_account and cgi_strip_dashes.

void cgi_free_path_info(char **path_info);

Free a path_info, as returned by cgi_path_info.

char *cgi_fts(char *str);

Format a table cell string.

char *cgi_get_date(void);

Get the system date in "DD-MON-YYYY" format from the web server (local time(2) call).

char *cgi_get_date_time(void);

Get the system date in "DD-MON-YYYY HH24 MI SS" format from the web server (local time(2) call).

char *cgi_get_ansi_date(void);

Get the system date in "YYYY-MM-DD" format from the web server (local time(2) call).

char *cgi_get_ansi_date_time(void);

Get the system date in "YYYY-MM-DD HH:MM:SS" format from the web server (local time(2) call).

char *cgi_getcookie(const char *key);

Get the value of a cookie. Returns NULL if key not found. The first found cookie is returned, in the case that there are more than one. The list is reversed, so this amounts to the last cookie value. This is important for last_update.

Two values in every cookie are user and password. Passwords are encrypted. Use cgi_crypt to decrypt a password before comparing it to user input.

void cgi_invalidate_cookies(void);

Invalidate all cookies sent by the browser. In effect, this is a logout from the current app.

void cgi_header(const char *title);

Emit html header.

void cgi_icheckbox(const char *name);

Emit a "checkbox" input. name - the 'name' of the "checkbox" input field. The name is fed to cgi_form_datum.

void cgi_ihidden(const char *name);

Emit hidden "input" field. Name is fed to cgi_form_datum.

void cgi_ipassword(const char *name);

Emit a password input. name - the name of the password input field. Name is not fed to cgi_form_datum.

void cgi_iradio(const char *name, char *value, int checked);

Emit a "radio" input. name - the 'name' of the "checkbox" input field. This name is common to the group of radio buttons. The name is fed to cgi_form_datum, and overrides the checked flag if non-NULL. value is the value for this button - eg: "yes", "no" or "maybe". checked is this the default button. The caller should use the values TRUE and FALSE, defined by the header. A TRUE value indicates that this radio button is the current value for the radio group. A FALSE value indicates that this radio button isn't the current value for the radio group. Only one radio button in the group may be checked. There does not have to be any radio button in a group checked. The browser standard says that in this case, the browser will use the value of the first radio button as the value for the group. That would generally be the one the upper left of the group.

struct sll *cgi_mks_iselect(char *entries[]);

Using a NULL terminated array of entries, create a list usable with cgi_iselect. Returns NULL if out of RAM (unlikely). Entries are malloc'ed, and the list can be deleted with sll_rmlist. Each entry in the list becomes both key and datum. Therefore the string that the user sees is the string that the receiving program reads. For example:

char *internet[] = {
    "Alta Vista",
    "CNNfn",
    "Excite",
    "Women.com",
    NULL
};
    ...
    cgi_iselect("Internet", cgi_mks_iselect(internet)));
    ...

This produces:




struct sll *cgi_mk_iselect(struct cgi_select_entries *entries[]);

Create a select list from an entry list. Returns an sll list for use with cgi_iselect. Returns NULL if out of RAM. Entries are malloc'ed, and the list can be deleted with sll_rmlist. For example:

struct cgi_select_entries *Heroes[] = {
    { "Bruce", "Batman" },
    { "Peter", "Spider-Man" },
    { "Clark", "Superman" },
    { "Bruce", "The Hulk" },
    { NULL, NULL }
}
    ...
    cgi_iselect("Internet", cgi_mk_iselect(Heroes)));
    ...

This uses the structure from cgi.h which looks like this:

struct cgi_select_entries {
    char *key;                  /* Key - program visible. */
    char *datum;                /* Datum - user visible. */
};

This produces:




void cgi_iselect(const char *name, struct sll *opts);

Emit <select>...</select>. Name is fed to cgi_form_datum. If the selected option is in the list, it is used as the default value.

The datum part of each node of the linked list is the value that is displayed, while the key is used as the value sent to the receiving CGI program. The key value is never displayed.

One may use cgi_mk_iselect or cgi_mks_iselect to construct the list.

A short example follows. The cgi_form_add call shows how to have a selected value for the pull down list. Without such a form variable, a blank entry is added to the top of the list, and is selected.

#include <stdio.h>
#include <string.h>
#include "sll.h"
#include "cgi.h"

int main(void);

int
main()
{
    struct sll *opts = NULL;

    sll_add_item(&opts, strdup("Wilma"), strdup("Fred"));
    sll_add_item(&opts, strdup("Betty"), strdup("Barney"));
    sll_add_item(&opts, strdup("Pebbles"), strdup("Bam Bam"));

    cgi_header("Rocks");
    cgi_form("envform.cgi");
    cgi_form_add("Flintstones", "Wilma");
    cgi_iselect("Flintstones", opts);
    puts("<br><input type=\"submit\">");
    puts("</form>");
    cgi_trailer();
    return 0;
}

When this is run, a form is emitted which looks like this:




void cgi_iselects(const char *name, struct sll *opts);

Emit <select>...</select>. Name is fed to cgi_form_datum. If there is a Name form variable, then the value of that variable is used as a default value for the select list. (This presumes that the value matches an entry in the list).

The list format is the same as is returned by cgi_msql, cgi_osql and cgi_psql. The first column of each row is used as the non-displayed value that is sent to the server, and the second column is used as the displayed value that is not sent.

This version differs from the older cgi_iselect only in the format of the linked list. It should be noted that cgi_iselect historically predates cgi_msql, cgi_osql and cgi_psql, as well as the libsll.a 2 dimensional list support routines.

Neither version is deprecated, though it is exepected that cgi_iselects will often be more convenient when used with RDBMS databases.

A short example follows. The cgi_form_add call shows how to have a selected value for the pull down list. Without such a form variable, a blank entry is added to the top of the list, and is selected.

#include <stdio.h>
#include <string.h>
#include "sll.h"
#include "cgi.h"

int main(void);

int
main()
{
    struct sll *opts = NULL;

    cgi_header("State");

    cgi_plogin(NULL, NULL, NULL);
    opts = cgi_psql("select state_code, state from states;");

    cgi_form("envform.cgi");
    cgi_form_add("state", "MI");
    cgi_iselects("state", opts);
    puts("<br><input type=\"submit\">");
    puts("</form>");
    cgi_trailer();
    return 0;
}

This produces a form such as:




void cgi_itextarea(const char *name, const int rows, const int cols);

Emit textarea input field. Name is fed to cgi_form_datum. Use rows for the height of the box in lines. Use cols for the width of the box in characters. In a break with tradition, both start and end for <textarea> are emitted.

void cgi_itextarea_spell(const char *name, const int rows, const int cols);

Emit textarea input field. Name is fed to cgi_form_datum. Use rows for the height of the box in lines. Use cols for the width of the box in characters. In a break with tradition, both start and end for <textarea> are emitted. After the textarea, emit any misspelled words. See cgi_spell_check. Requires local spell dictionary - app specific. The file spell.dict must exist in same directory as cgi program.

void cgi_itext(const char *name);

Emit text input field. Name is fed to cgi_form_datum.

void cgi_itextr(const char *name);

Emit text input field. Name is fed to cgi_form_datum. Right justify the text in the text box - for numerics.

void cgi_itext_size(const char *name, int size);

Emit text input field. Name is fed to cgi_form_datum. The size is the width of the text field.

void cgi_itextr_size(const char *name, int size);

Emit text input field. Name is fed to cgi_form_datum. The size is the width of the text field. Right justify the text in the text box - for numerics.

The library may be compiled without Oracle support, in which case, only gdbm login is supported.

char *cgi_mk_base_url(const char *ref, ...);

Generate a URL relative to the app's base directory. ref can be NULL or "", "@", which means the same thing as NULL, or start with a '/'. If the ref starts with a '/', then the path to the cgi program is not omitted, and an absolute URL relative to the document root is created. The C compiler will probably emit warnings if you pass this function NULL or the null string, "", so "@" is preferred. If the CGI program is running on a server on port 80, the port number is omitted in the URL. If the CGI program is running on a secure site (https), or on port 443, then URLs are generated with "https". The same binary can emit correct URLs for secure and insecure servers. Returns pointer to static data. ref can be a printf(3) like format string, with arguments to follow.

char *cgi_mk_help_url(void);

Generate a URL to point to the local help. If the CGI program is running on a server on port 80, the port number is omitted in the URL. If the CGI program is running on a secure site (https), or on port 443, then URLS are generated with "https". The same binary can emit correct URLS for secure and insecure servers. Returns pointer to static data.

char *cgi_mklower(char *str);

cgi_mklower - Convert a string to lower case in place.

char *cgi_mkupper(char *str);

Convert a string to UPPER CASE in place.

char *cgi_replace(char *str, int old, int new);

Replace all instances of old characters with new character in given string.

struct sll *cgi_spell(char *str);

Return a list of unrecognized words. Words are in the key fields of the list. Words end with a null ('\0'). See cgi_spell_check. Requires local spell dictionary - app specific. The file spell.dict must exist in same directory as cgi program.

void cgi_spell_check(char *str);

Emit to stdout the unrecognized words. See cgi_spell. Requires local spell dictionary - app specific. The file spell.dict must exist in same directory as cgi program.

char *cgi_mk_url(const char *ref, ...);

Generate a URL to point back here. ref can be NULL, or the null string, "", but should not start with a '/', eg: "edit/1". Returns pointer to static data. ref can be a printf(3) like format string, with arguments to follow. ref can be NULL or "", but should not start with a '/', eg: "images/f.gif". ref can also be "@", which means the same thing as NULL. The C compiler will probably emit warnings if you pass this function NULL or the null string, "", so "@" is preferred. If the CGI program is running on a server on port 80, the port number is omitted in the URL. If the CGI program is running on a secure site (https), or on port 443, then URLS are generated with "https". The same binary can emit correct URLs for secure and insecure servers.

void cgi_prompt(char *prompt);

Emit a prompt, creating a table cell. This uses the white on blue color scheme, with a right justified prompt.

void cgi_oclose(void);

Close an Oracle database connection. Databases are opened with cgi_ologin.

int cgi_ologin(const char *dbname, const char *user, const char *passwd);

Open an Oracle database. Return TRUE if connected. Return FALSE if not connected.

struct sll *cgi_osql(char *sql);

Execute a dynamic Oracle SQL statement. This may be a select, update, insert, or delete. Assumes you are already connected, via cgi_ologin. The SQL statement may be UPPER or lower case. Terminate SQL statements with a semicolon. Terminate PL/SQL blocks (which can contain embedded semicolons) with a slash (/).

Return NULL on error. Errors are reported by using calls in liberr.

For select statements, data is returned in a list of lists, using libsll. This amounts to a dynamic two dimensional array. Each returned row is a list. The outer list contains only lists. The lists are pointed to using the key field of the outer list. The first row is a list of headers, or column titles. The rest of the rows are data from the query.

For update, delete, or insert statements, the returned list has one entry, whose key field is a number encoded as a string. This number reflects the number of rows that were processed by the statement. For example, if a delete statement removes ten rows from a table, the number would be ten.

The Oracle SQL syntax for changing a column name in a select is to follow the column name with the alias. The alias may be in double quotes. For example:
select chipsoz \"Chips (OZ)\" from chips;
This is handy when using cgi_table.

If the database transaction is successful, a commit is issued.

void cgi_osql_print(struct sll *list);

Print osql output to stdout, formatted for a terminal. This is likely for debugging. Usage is to call cgi_msql, cgi_osql or cgi_psql with a select statement, then call this function with the list.

int cgi_mlogin(char *pghost, char *dbName);

Log into the Mini SQL database. Host and database name may be NULL, and may default to the uittinet.accountsupportmysql.com (using Unix domain sockets), and web user database. Returns 0 for success, -1 for error.

struct sll *cgi_msql(char *sql);

Perform the sql string. Allows a terminating semicolon, even though MSQL generally does not. Only one statement per call is allowed. Return NULL for error. This routine is intended to be compatible with cgi_osql and cgi_psql, in the sense that the same SQL commands are supported, and yield the same list results. However, Mini SQL does not support PL/SQL. Further, version two does not support aggregates, such as sum, count, min, max. Use single quotes for strings. To get the effect of min and max, you can use order by as follows:
select NAME from place order by NAME desc limit 1;


void cgi_mclose(void);

Close the connection to the database and cleanup.

void cgi_inserts_msql(struct sll *sp);

From the output of cgi_osql or cgi_psql, print msql insert statements, suitable for the msql command line client. This has been used to move data from an Access database to an MSQL database using a CSV file. It was painful, and the tool requires that the resulting file be edited a little before being run. Still, the bulk of the work was OK.

int cgi_plogin(char *pghost, char *pgport, char *dbName);

Log into the PostgreSQL database. Host, port and database name may be NULL, and may default to the uittinet.accountsupportmysql.com, correct local PostgreSQL port, and web user database. Returns 0 for success, -1 for error.

struct sll *cgi_psql(char *sql);

Perform the sql string. Return NULL for error. This routine is intended to be compatible with cgi_osql, in the sense that the same SQL commands are supported, and yield the same list results. However, PostgreSQL does not support PL/SQL.

The PostgreSQL SQL syntax for changing a column name in a select is to follow the column name with the keyword "as" followed by the alias. The alias may be in double quotes. For example:
select chipsoz as \"Chips (OZ)\" from chips;
This is handy when using cgi_table.

void cgi_pclose(void);

Close the connection to the database and cleanup.

struct sll *cgi_page_children(struct cgi_page tbl[], char *page);

Return all the children of a page. The page names are in the key fields of the list. The page aliases are in the datum fields of the list. See cgi_page_nav for details.

void cgi_page_content(struct cgi_page tbl[], char *page);

Call the function for the current page. The functions are registered int the cgi_page array. See cgi_page_nav for details.

int cgi_page_header(struct cgi_page tbl[], char *page);

Emit the header for the page. See cgi_page_nav for details. Returns TRUE if the page can't be found. This might indicate an internal error.

void cgi_page_index(struct cgi_page tbl[]);

Emit an index for the app. The index, like an index in a book, is alphabetized. However, the description is also emitted. If the description is NULL in the navigation table, that page is omitted. This is useful if, for example, it is desired to have automatic navigation to a page used for submission, but where it makes no sense having the user navigate there without a form. See cgi_page_nav for details.

struct cgi_page *cgi_page_lookup(struct cgi_page tbl[], char *page);

Find the cgi_page entry for a page. See cgi_page_nav for details.

void cgi_page_nav(struct cgi_page tbl[], char *page);

Emit the navigation bar for the current page. tbl is an array of struct cgi_page. It contains application structure info. page is the name of the current page. This name is used to identify the page to the application. Names are used in href's to the application as the first path information element.
The cgi_page structure:

struct cgi_page {
    char *parent;       /* Internal name of parent page "main" */
    char *page;         /* Internal name of the page "check_bal" */
    char *page_alias;   /* May have spaces in it "Check Balance" */
    char *help;         /* Help name for page "check_bal.shtml" */
    void (*func)(void); /* page handler routine. */
    char *title;        /* Title for page. */
    char *desc;         /* Description.  NULL for don't display. */
};

Note that in the example below, the Checking Account Balance and Visa Account Balance do not appear in the top level menu, as generated by cgi_page_index. It is generally desireable to omit pages that take form data from a previous page. These pages will not have such data when the navigation is a simple href.

struct cgi_page tbl[] = {
    { "top", "top", "Top", "top_help.shtml",
      top, "Top", "Top Page" },
    { "top", "check", "Check", "check_help.shtml",
      check, "Check", "Checking Account" },
    { "check", "checkbal", "Checking Acocunt Balance", "checkbal_help.shtml",
      checkbal, "Checking Blance", NULL },
    { "top", "visa", "Visa", "visa_help.shtml",
      visa, "Visa", "Visa Account" },
    { "visa", "visabal", "Visa Account Balance", "visabal_help.shtml",
      visabal, "Visa Balance", NULL },
    { NULL, NULL, NULL, NULL, NULL, NULL, NULL }
};
cgi_page_nav(tbl, "checkbal"); Emits something like this (with real hrefs):

Help : Main > Check > Balance

Careful: it is trivial to create an infinite loop. There must be a root entry in the table whose parent is itself.

void cgi_page_toc(struct cgi_page tbl[]);

Emit a Table Of Contents for the app. The name of the page and the description of the page are emitted. Nested unnumbered lists are used to show the hierarchy. The root is emitted first, and each page's children are displayed. If the description is NULL in the navigation table, that page is omitted. This is useful if, for example, it is desired to have automatic navigation to a page used for submission, but where it makes no sense having the user navigate there without a form.

struct cgi_page *cgi_page_root(struct cgi_page tbl[]);

Return the root node of a navigation list.

char **cgi_path_info(void);

Parse path info.

void cgi_print_form_info(void);

Print form info as a table. Debug. Call cgi_form_info first.

void cgi_printenv(void);

Emit the environment variables as a table. For debug use.

void cgi_print_cookies(void);

Print all cookies as a table. For debug use. You need to call cgi_validate_user first. This requirement is not a good match for an app that just wants to know who it talked to recently, without bothering the user with a login.

void cgi_setup_url(const char *path, const char *name, const char *help_name);

Set the path, CGI executable name & help name for creation of URL's.

void cgi_rtable(struct sll *sp);

From the output of cgi_osql or cgi_psql, print a rotated table. Each row is title, data. A blank row is inserted between records.

void cgi_table(struct sll *sp);

From the output of cgi_msql, cgi_osql or cgi_psql, print a table.

void cgi_rtablei(struct cgi_tbl *tbl, int transaction, char *dest, struct sll *sp);

Emit an entry form. tbl describes the table. transaction - insert, update and/or delete. Use the constants TRANS_INSERT, TRANS_UPDATE, and TRANS_DELETE. They may be or'ed together. This controls which submit buttons will be generated. You may not use insert with either update or delete, since insertion indicates data for a record that does not already exist. dest is a string used in building a destination url. For example, use a reference to a table entry used by cgi_page_nav. sp is a list for data if Update or Delete. Only the first row is shown. The format is as returned by cgi_msql, cgi_psql or cgi_osql, as required for listing - that is, the first column is a unique non-shown entry.

The cgi_tbl structure is defined in cgi.h as follows:

struct cgi_tbl {
    char *name;			/* Column name. */
    char *desc;			/* Description for prompt */
    int type;			/* Use TBL_FLOAT, ... */
    int size;			/* Width for input, -1 no edit, 0 default. */
    int rows;			/* Rows, for TBL_AREA. */
    struct sll *(*func)(void);	/* Get list for TBL_SELECT. */
    int order;                  /* Order for order by.  zero is ignored. */
    int searchable;		/* TRUE for searchable */
};

Note that the type field may have any of the values TBL_FLOAT, TBL_INT, TBL_TIME, TBL_STRING, TBL_AREA, or TBL_SELECT.

The size field governs the width of the text box in which a field is entered. This is for types TBL_FLOAT, TBL_INT, TBL_TIME, TBL_STRING and TBL_AREA, but not TBL_STRING fields. If the size is zero, TBL_AREA uses a width of 50, but other types, TBL_FLOAT, TBL_INT, TBL_TIME, and TBL_STRING do not use a size, and default to the browser's default. A size of -1 indicates that the field is not editable. This is for types TBL_FLOAT, TBL_INT, TBL_TIME, TBL_STRING and TBL_AREA. These fields are formatted in simple text without a widget. For type TBL_SELECT, a value may still be selected. However, cgi_rtablei_sub will not modify any field whose size is -1.

For TBL_AREA, rows indicates the number of visible rows for the textarea. The func function pointer is used by TBL_SELECT to return the data used for the select widget. Note that the select list format is that used by cgi_iselect. Therefore, one may use cgi_mk_iselect or cgi_mks_iselect to construct the list.

The searchable field is used in building and responding to search forms using cgi_search_form, cgi_search_sql and cgi_select_sql.

Note that the first row of the table specifies the column that is used as the unique key. The cgi_rtablei function does not display the unique key field (the first row). It does use the value in the CGI path for use by cgi_rtablei_sub.

int cgi_rtablei_sub(struct cgi_tbl *tbl, char *table_name, char *keyval);

Respond to a form, as generated by cgi_rtablei(). tbl describes the table. table_name supplies the database table name. keyval specifies the unique key value for database update or delete. It is not required for insert, and may be NULL. Typically, it is obtained from path_info[1].

This function reads form data, as produced by cgi_rtablei(). The form variable submit must be set to one of Insert, Update or Delete. The cgi_tbl structure should match the one used to generate the form. As such, this function can not be used directly in a struct cgi_page table for application navigation. A stub function must be used to pass the table definition structure.

Up to one row will be affected - insert, update or delete.

Returns success or failure: TRUE or FALSE. Any errors are emited using liberr.

The compiled libccgi library will support only Oracle or PostgreSQL, but not both at once. That database is updated. The gdbm interface is not supported.

void cgi_tablel(struct sll *sp, char *dest);

Emit a table list page. From the output of cgi_msql, cgi_osql or cgi_psql, print a table. The first column is not displayed, but used as a unique argument to the CGI program, using the second argument the page address.

void cgi_search_form(struct cgi_tbl *tbl, char *dest)

Emit an entry form for database table searching. tbl - describes the table. dest - for building destination url.

The handler for the generated form can use cgi_search_sql to generate the query, then cgi_msql, cgi_osql or cgi_psql to execute the query, then cgi_rtablei, cgi_rtable, or cgi_table for display and/or editiing.

char *cgi_quote_sql(char *sql);

Returns a string where especially single quotes are quoted so that they are literally inserted. The rules differ for Oralce, Postgres, and msql, but this routine is compiled for each library. Returns a pointer to a static buffer that is overwritten by each call.

char *cgi_search_sql(struct cgi_tbl *tbl, char *tbl_name, int display);

Create an SQL query from a search form generated by cgi_search_form. Return NULL if nothing to search. tbl - describes the table. tbl_name - name of the database table. display - TRUE if for display. If display, then don't get columns with NULL desc. Returns pointer to static buffer, which may be overwritten by subsequent calls.

The resulting string can be passed to cgi_msql, cgi_osql or cgi_psql for a list of results. Those can then be passed to cgi_rtablei, cgi_rtable, or cgi_table for display and/or editiing.

char * cgi_select_sql(struct cgi_tbl *tbl, char *tbl_name, int display, char *where)

Create an SQL query. tbl - describes the table. tbl_name - name of the database table. display - TRUE if for display. If display, then don't get columns with NULL desc. where - optional where clause. If NULL, get everything. Returns pointer to static buffer, which may be overwritten by subsequent calls.

The resulting string can be passed to cgi_msql, cgi_osql or cgi_psql for a list of results. Those can then be passed to cgi_rtablei, cgi_rtable, or cgi_table for display and/or editiing.

void cgi_text(const char *name);

Emit text from cgi_form_datum.

void cgi_rm_fi(void);

Free parsed copy of CGI form information.

char *cgi_strip(char *str, int chr);

Strip all instances of a character from a string, in place.

char *cgi_strip_commas(char *str);

Strip commas from a string, in place.

char *cgi_strip_dollar(char *str);

Strip dollar signs from a string, in place. See cgi_valid_dollar.

char *cgi_strip_dashes(char *account);

Remove any dashes from a string, in place. Account numbers are usually 123-45678, but stored without dashes. Returns the original string pointer. See cgi_validate_account and cgi_fmt_account.

struct sll *cgi_tbltocsv(char *file);

Parse an html file, and convert any table data to a 2-d libsll list. Returns NULL for error. Use sll_rmlist2 to free the list when finished with it.

void cgi_trailer(void);

Emit html trailer.

char *cgi_validate_account(char *account);

Validate an account number. This does NOT look the account number up in the database. Return error string for first error detected. Return NULL for good. Should call cgi_strip_dashes first. Also see cgi_fmt_account. Errors include:
Account is a required field.
Account must be exactly 8 alpha-numeric characters.
Account must have only alpha-numeric characters.

int cgi_valid_dollar(char *num);

Return TRUE if the number (dollar amount) is valid. Checks: [$][-][0-9][[.[0-9]]. That is, it starts with an optional '$', then has an optional sign ('-'). then has optional digits, then optional decimal point ('.'), then optional digits, and finally, the end of the string. At least one digit must be present.

To convert a validated string, check to see if the first character is a dollar sign ($), then use atof(3) from the C library to convert to binary form. For example:

char *str;
double value;
value = atof((*str == '$') ? str + 1 : str);

Alternately, use cgi_strip_dollar and then use cgi_valid_number and the C library atof(3) functions.

int cgi_valid_ndollar(char *num);

Return TRUE if the number (dollar amount) is valid or missing. Checks: [$][-][0-9][[.[0-9]]. That is, it starts with an optional '$', then has an optional sign ('-'). then has optional digits, then optional decimal point ('.'), then optional digits, and finally, the end of the string.

To convert a validated string, check to see if the first character is a dollar sign ($), then use atof(3) from the C library to convert to binary form. For example:

char *str;
double value;
value = atof((*str == '$') ? str + 1 : str);


int cgi_valid_number(char *num);

Return TRUE if the number is valid. Checks: [-][0-9][[.[0-9]]. That is, it starts with an optional '-', then has optional digits, then optional decimal point ('.'), then optional digits, and finally, the end of the string. At least one digit must be present.

Use atof(3) from the C library to convert a validated number to binary form.

int cgi_valid_nnumber(char *num);

Return TRUE if the number is valid or missing. Checks: [-][0-9][[.[0-9]]. That is, it starts with an optional '-', then has optional digits, then optional decimal point ('.'), then optional digits, and finally, the end of the string.

Use atof(3) from the C library to convert a validated number to binary form.

int cgi_validate_user(const char *databasename, const char *url, const char *app);

Is the user a valid user? If the user's browser has sent a cookie, validate it. If app is not NULL, validate the user against the database of application roles. If there is no cookie, look to see if we're coming from a login screen. If the user is valid, then, if app is not NULL, validate the user against the database of application roles. Application roles are supported for Oracle or PostgreSQL databases. If the user passes these tests, create a cookie, and issue it to the browser. If the user is not valid, emit a login screen. A new last_update is appended to the cookie. Return TRUE for good, FALSE for failure.

GDBM database tables should start with "dbm". The databasename for PostgreSQL should end with "psql". It should start with the PostgreSQL database name in use - for example, "www/psql". The databasename for MSQL should end with "msql". It should start with the MSQL database name in use - for example, "www/msql".

An example call follows:

#include <stdio.h>
#include "cgi.h"
#include "hello.h"

int main(void)
{
    cgi_setup_url("hello", "hello.cgi", "hello_help.shtml");
    if (!cgi_validate_user("www/msql", cgi_mk_url("@"), "HELLO")) {
	exit(0);		/* A login screen is in progress. */
    }
    cgi_header("Hello, World.");
    printf("<h1>Hello, World</h1>\n");
    cgi_trailer();
    cgi_oclose();
    return 0;
}
For an Oracle database, you'd need an account on the database. For Oracle, Postgres or msql (Mini SQL) you need an entry for HELLO and the user in the application_roles table.

int cgi_wdeliver(char *host, char *port, char *path);

Get a web page from a web server, and spit it to stdout, as a web page. Return TRUE for success, FALSE for error.

void cgi_wget_close(int filedes);

Close a wget stream opened with cgi_wget_open or cgi_wget_openh.

int cgi_wget_open(char *host, char *port, char *path);

Get a web page from a web server. Use cgi_wget_close to close the connection.

int cgi_wget_openh(char *host, char *port, char *path);

Get a web page from a web server. Use cgi_wget_close to close the connection. The caller will see http headers:

HTTP/1.1 200 OK
Date: Thu, 18 Feb 1999 20:36:54 GMT
Server: Apache/1.3.3 (Unix)
Connection: close
Content-Type: text/html


int cgi_valid_number(char *num);

Return TRUE if the number is valid. Checks: [-][0-9][[.[0-9]]. That is, it starts with an optional '-', then has optional digits, then optional decimal point ('.'), then optional digits, and finally, the end of the string. At least one digit must be present.

char *check_error_date(char *date_str);

Check error date, return Oracle date, or NULL.

void convert_all_newlines(char *str);

Convert newlines to Oracle developer 2000 convention. This appears to be the Mac convention (^M). The string is converted in place. The result is always no longer than the original - and often shorter.

int dbsql_isbday(char *date);

Look up a date. Return TRUE if it is a business day, else FALSE. Input date is in DD-MON-YYYY format.

void strip_all_newlines(char *str);

Strip newlines from the string, replacing them with a single space. Newlines may be ^M, ^J or ^M^J. Count spaces, when the end of the string or a non-space or non-newline character is seen, then, if there were any newline characters, emit one space. If there were no newline characters, emit the spaces. The string is stripped in place. The idea is that when you store textarea data, the newlines should be removed so that when you emit text as <p>, it wraps to the width of the screen. However, this trip is unnecessary. <p> will effectively remove newlines anyway. It's probably better to store the formatting.

void striplws(char *str);

Strip leading white space in place.

void striptws(char *str);

Strip trailing white space in place.

char *striptz(char *str);

Strip trailing zeroes in place. Also, remove a trailing '.' if the fractional part is all zeroes. Does not check to see if the string is a number, or even that there is a '.' in the string. Use cgi_valid_number() and check for a '.' with strchr().

char *strnistr(char *haystack, char *needle, int n);

Find an instance of the string needle in the string haystack. Matches are performed in a case-insensitive manner. Unlike stristr(), this function stops looking after n bytes in haystack.

char *stristr(char *haystack, char *needle);

Find an instance of the string needle in the string haystack. Matches are performed in a case-insensitive manner. This function is similar to strstr(3) in the C library. There should be a strcasestr(3) in the C library.

double cgi_fraction(char *number);

Convert a fraction, such as "3 3/8", or "-7 7/8" or "+5/16" to a number.

int cgi_fraction_check(char *str);

Check a fraction for validity. The intent is to determine if cgi_fraction will properly convert a string to a number. Any denominator must be a power of 2 from 2 to 256. Return FALSE for invalid, else TRUE.

Example

#include "cgi.h"

int main(void);

int
main()
{
    cgi_header("Environmental Concerns");
    cgi_printenv();
    cgi_trailer();
    return 0;
}

Diagnostics

Most routines return NULL if an error is encountered. A design goal of the library is that no routines should crash due to poor data.

Files

/usr/local/lib/libccgi.a
/usr/local/include/cgi.h

See Also

gdbm(3), strings(3), libsll, liberr

Author

Stephen Uitti, 1998

Bugs

There are numerous limitations to this early version. No attempt has yet been made to support any particular version of HTML. However, the philosophy is to support HTML that is likely available on most browsers. Think "lynx" with tables.

The author considers frames misdesigned, so there is no explicit support. However, several applications have been written that work fine in a frames environment.

Some routines modify passed data in place. Some routines return pointers to library-managed malloc'ed space. Some routines return pointers to caller-managed malloc'ed space. Some routines return pointers to static buffers that are overwritten on each call. This latter makes these calls unusable with multi-threaded applications.

The FastCGI interface is not explicitly supported (or unsupported). This library ought to be usable with and without FastCGI.

There are numerous small memory leaks. Mostly calls to strdup(3), with no free(). This shouldn't hurt anything until, for example, FastCGI is used more extensively, and with long-lived applications.

cgi_form_info does not parse GET style form information. This may be fixed without altering the interface.

The naming convention for functions has evolved in a less than sane manner.

cgi_path_info returns a list of strings. It should return a struct sll pointer. Then, the indexing functions, such as sll_inds could be used to extract a given element with less hassle. These functions are a good deal younger than cgi_path_info.