Hypertriton, Inc. Hypertriton, Inc.
( Francais )
HOME | DOWNLOAD | PORTABILITY | AUTHOR | BUGZILLA

Csoft-CGI is a small and unobstrusive framework for writing multi-lingual web applications in C. Traditional CGI and FastCGI modes are supported. This document demonstrates some of the basic functions implemented by the library.

Required includes
All source code should include <libcgi/cgi.h>, even if it does not use libcgi calls directly, in FastCGI mode it provides necessary stdio wrappers. Csoft-cgi provides the following includes:
#include <libcgi/cgi.h>         /* Required */
#include <libcgi/mime.h>        /* MIME-related functions */
#include <libcgi/html.h>        /* HTML output related functions */
#include <libcgi/table.h>       /* General-purpose tables */
Initializing the library

Before using any libcgi function, you must invoke the cgi_init() function, providing a pointer to a preinitialized struct cgi. This structure contains some information about the application, a list of available languages, optiopn flags and a pointer to an optional function to call when the application exits.

Instead of using exit(), you should use cgi_destroy(). The first argument is the return code. The second argument is an optional error message.


#include <libcgi/cgi.h>
struct cgi hello_cgi = {
	"Hello, world application",   /* Application title */
	"",                           /* Copyright */
	{ "en", "fr", "no", NULL },   /* Available languages */
	CGI_HTML_ERRORS,              /* Option flags */
	foo_destroy                   /* Exit function */
};
int
main(int argc, char *argv)
{
	cgi_init(&hello_cgi);
	cgi_destroy(EX_OK, NULL);
	return (0);
}

The list of languages defines the languages which the application speaks. When processing queries, the application checks the client's list of accepted languages and switches to the first language appearing on this list. Multilanguage support is provided by both an internal gettext implementation (which substitutes marked strings such as _("foo") in the code), and the html subsystem which uses an XSL transform to treat <ml lang="xx"> tags in the html source and templates.

Possible option flags include CGI_HTML_ERRORS (generate HTML error in addition to logging), and CGI_PERSISTENT (require FastCGI mode; application must run persistently).

Processing requests

A typical csoft-cgi application will loop processing requests every time the cgi_accept() function return a struct cgi_query pointer. The cgi_begin() function outputs the HTTP headers, such as Content-Type. Between those two calls, the application can set cookies or return specific HTTP headers.


#include <libcgi/cgi.h>
struct cgi hello_cgi = {
	"Hello, world application",   /* Application title */
	"",                           /* Copyright */
	{ "en", "fr", "no", NULL },   /* Available languages */
	CGI_HTML_ERRORS,              /* Option flags */
	foo_destroy                   /* Exit function */
};
int
main(int argc, char *argv)
{
	struct cgi_query *q;
	cgi_init(&hello_cgi);
	while ((q = cgi_accept()) != NULL) {
		cgi_begin(q, "text/html");
		cgi_log(LOG_INFO, "Processing request");
		cgi_printf(q, "Hello, world!");
	}
	cgi_destroy(EX_OK, NULL);
	return (0);
}

The cgi_printf() function, as you might have guessed, outputs text to the client. The cgi_log() function inserts an entry in the application's logfile (ie. "hello.fcgi.log").

Using GET/POST arguments

The cgi_get_arg() and cgi_lget_arg() functions are used to retrieve GET and POST arguments. Each argument has two strings, a key and a value. The key length is always limited to CGI_ARG_KEY_MAX characters. The length of values is not limited (at least not at the application level) unless you retrieve it using cgi_lget_arg().

Note that characters in values of all GET arguments are substituted per RFC1738, so you do not need to worry about URL escaping.


#include <libcgi/cgi.h>
struct cgi hello_cgi = {
	"Hello, world application",   /* Application title */
	"",                           /* Copyright */
	{ "en", "fr", "no", NULL },   /* Available languages */
	CGI_HTML_ERRORS,              /* Option flags */
	foo_destroy                   /* Exit function */
};
int
main(int argc, char *argv)
{
	struct cgi_query *q;
	cgi_init(&hello_cgi);
	while ((q = cgi_accept()) != NULL) {
		const char *foo, *bar;
	
		cgi_begin(q, "text/html");
		foo = cgi_get_arg(q, "foo");
		bar = cgi_lget_arg(q, "foo", 20);
		cgi_printf(q, "Foo = %s", foo);
		cgi_printf(q, "Bar = %s", bar);
	}
	cgi_destroy(EX_OK, NULL);
	return (0);
}

To test the arguments, invoke hello.fcgi?foo=fooval&bar=barval. In this example, if the length of the "bar" argument exceeds 20 characters, its value will be truncated.

Using the HTML subsystem

Csoft-cgi's build system uses a set of M4 templates and an XSLT processor (xsltproc) to process a set of HTML source files (which must have the *.htm extension) into pre-processed HTML documents in different languages and character sets. For example, if the application is available in both French and English, you would run make to process foo.htm into foo.html.en, foo.html.en.utf8, foo.html.fr, foo.html.fr.utf8 and foo.html.fr.iso8859-1.

At each request, csoft-cgi will switch to the proper language based on HTTP/1.1 language negotiation. It is possible to override language negotiation by providing a lang argument.

The html_output() function is used to output the contents of a preprocessed HTML document to the client. Various substitutions are made. For instance, if you use var_set("foo", "bar") in the program, instances of "$foo" in the document (either the *.htm or any of the *.m4 templates will be substituted for "bar".


#include <libcgi/cgi.h>
struct cgi hello_cgi = {
	"Hello, world application",   /* Application title */
	"",                           /* Copyright */
	{ "en", "fr", "no", NULL },   /* Available languages */
	CGI_HTML_ERRORS,              /* Option flags */
	foo_destroy                   /* Exit function */
};
int
main(int argc, char *argv)
{
	struct cgi_query *q;
	cgi_init(&hello_cgi);
	while ((q = cgi_accept()) != NULL) {
		const char *foo, *bar;
	
		cgi_begin(q, "text/html");
		var_set("foo", "bar");
		html_output(q, "mypage");
	}
	cgi_destroy(EX_OK, NULL);
	return (0);
}

Now you must add "mypage.htm" to the ${HTML} list in your application's Makefile, and create the file. Two special macros are required, CONTENT() and TITLE(). You can use m4 builtins, such as define() and include().

The default quotes are "[" and "]". If you need to use those literal characters in the document, you might need to use the m4 changequote() function. The XSL processor will escape some characters for you. Literal quotes that are not part of an HTML tag, for instance, are automatically substituted to "&quot;".


TITLE([
 <ml lang="en">My page</ml>
 <ml lang="fr">Ma page</ml>
])
CONTENT([
 <ml lang="en">The value of foo is "$foo".</ml>
 <ml lang="fr">La valeur de foo est «$foo».</ml>
])
Using HTTP cookies
The cgi_set_cookie() and cgi_get_cookie() function allows you to set and retrieve HTTP cookies.
#include <libcgi/cgi.h>
struct cgi hello_cgi = {
	"Hello, world application",   /* Application title */
	"",                           /* Copyright */
	{ "en", "fr", "no", NULL },   /* Available languages */
	CGI_HTML_ERRORS,              /* Option flags */
	foo_destroy                   /* Exit function */
};
int
main(int argc, char *argv)
{
	struct cgi_query *q;
	cgi_init(&hello_cgi);
	while ((q = cgi_accept()) != NULL) {
		const char *foo, *bar;
		struct cookie *ticket;
	
		ticket = cgi_set_cookie("ticket", "12345678");
		ticket->expire = "12/31/2009 00:00:00";
		ticket->secure = 1;
		cgi_begin(q, "text/html");
		cgi_printf(q, "Ticket = %s", cgi_get_cookie(q, "ticket"));
	}
	cgi_destroy(EX_OK, NULL);
	return (0);
}

(More to come...)