This example can be found in the examples/c/counter
directory.
/*- * * This code implements an odometer-like page counter extension to the * AOLserver. The code is based on the popular "odometer" CGI * by Chris Stephens, stephenc@pcmail.cbil.vcu.edu, and * Fred Christiansen, fredch@fc.hp.com (see below). * * Why use this module instead of the CGI? By loading the code directly * into the AOLserver, you avoid all the painful overhead of CGI. * The result is this module is more than 10 times as fast as the CGI! * Plus, it compiles and run on all platforms, including Windows NT! * * To use the counter, load the module and insert an image * link of the form <IMG SRC="/NS/Counter/my/page.htm">. * The NsCounter() function will return a small odometer image of the * number of hits to the corresponding page (i.e., /my/page.htm). The * page count is kept in a file prefixed by .odo (i.e., /my/.odo.page.htm). * * To load this module, add the following to the [ns\server\<vs>\modules] * section of your nsd.ini file where <vs> is the name of the virtual * server you want to add the counter too: * * counter=counter.dll ; or counter.so on Unix platforms * * This prefix(s) of the count URL can be set with the "UrlPrefix" * key in the module's nsd.ini configuration file section (i.e., * [ns\server\<vs>\module\counter]). The value is a comma separated * list of URL prefixes. For example, if the list includes the * "/cnt,/count" prefixes, then any URL which begins with /cnt or * /count will be processed by the NsCounter() function. * * Note: the setup interface in AOLserver 2.0 is open-ended, so you can * use it to view/edit these parameters by using the "List AOLserver * Configuration Sections" available from the "Setup Home" page in expert * mode * */ #include "ns.h" #include <errno.h> #include <assert.h> #include <string.h> #ifdef WIN32 #include <io.h> #include <fcntl.h> #else #include <unistd.h> #endif #define CONFIG_PREFIX "UrlPrefix" #define DEFAULT_PREFIX "/cgi-bin/counter,/NS/Counter,/ns/counter" #define COUNT_PREFIX ".odo" static Ns_OpProc NsCounter; DllExport int Ns_ModuleVersion = 1; DllExport int Ns_ModuleInit(char *hServer, char *hModule) { char *configPath; char *urlPrefix; char *next; Ns_DString dsBuf; Ns_DString dsPath; /* * Fetch the UrlPrefix from the config file. */ configPath = Ns_ConfigGetPath(hServer, hModule, NULL); urlPrefix = Ns_ConfigGetValue(configPath, CONFIG_PREFIX); if (urlPrefix == NULL) { urlPrefix = DEFAULT_PREFIX; } /* * Register NsCounter at each of the URL's specified * in the comma seperated UrlPrefix list. */ Ns_DStringInit(&dsBuf); Ns_DStringInit(&dsPath); Ns_DStringAppend(&dsBuf, urlPrefix); next = dsBuf.string; do { urlPrefix = next; next = strchr(urlPrefix, `,'); if (next != NULL) { *next++ = `\0'; } if (*urlPrefix != `/') { Ns_Log(Warning, "%s(%s): Invalid URL prefix skipped: %s", hModule, hServer, urlPrefix); } else { Ns_NormalizePath(&dsPath, urlPrefix); urlPrefix = Ns_DStringExport(&dsPath); Ns_RegisterRequest(hServer, "GET", urlPrefix, NsCounter, ns_free, urlPrefix, 0); } } while (next != NULL); Ns_DStringFree(&dsBuf); Ns_DStringFree(&dsPath); return NS_OK; } /*- ** Originally count.c, by Chris Stephens, stephenc@pcmail.cbil.vcu.edu, (c)1995. ** Code cleaned up and enhanced by Fred Christiansen, fredch@fc.hp.com, ** as odometer.c; supports: ** - 8 digits instead of 7 (and by changing ODO_DIGITS, up to 10 (number ** of digits in an unsigned long)) */ #define NL `\n' /* new line char */ #define ODO_DIGITS 8 /* # digits displayed */ #define BUFSIZE (ODO_DIGITS+2) /* 8 digits + NL + nul */ #define BM_HT 16 /* bitmap height */ #define BM_WD 8 /* bitmap width */ static char *bitmap[] = { "0xff", "0xff", "0xff", "0xc3", "0x99", "0x99", "0x99", "0x99", /* rows 1-8 of 0 */ "0x99", "0x99", "0x99", "0x99", "0xc3", "0xff", "0xff", "0xff", /* rows 9-16 of 0 */ "0xff", "0xff", "0xff", "0xcf", "0xc7", "0xcf", "0xcf", "0xcf", /* rows 1-8 of 1 */ "0xcf", "0xcf", "0xcf", "0xcf", "0xcf", "0xff", "0xff", "0xff", /* rows 9-16 of 1 */ "0xff", "0xff", "0xff", "0xc3", "0x99", "0x9f", "0x9f", "0xcf", /* rows 1-8 of 2 */ "0xe7", "0xf3", "0xf9", "0xf9", "0x81", "0xff", "0xff", "0xff", /* rows 9-16 of 2 */ "0xff", "0xff", "0xff", "0xc3", "0x99", "0x9f", "0x9f", "0xc7", /* rows 1-8 of 3 */ "0x9f", "0x9f", "0x9f", "0x99", "0xc3", "0xff", "0xff", "0xff", /* rows 9-16 of 3 */ "0xff", "0xff", "0xff", "0xcf", "0xcf", "0xc7", "0xc7", "0xcb", /* rows 1-8 of 4 */ "0xcb", "0xcd", "0x81", "0xcf", "0x87", "0xff", "0xff", "0xff", /* rows 9-16 of 4 */ "0xff", "0xff", "0xff", "0x81", "0xf9", "0xf9", "0xf9", "0xc1", /* rows 1-8 of 5 */ "0x9f", "0x9f", "0x9f", "0x99", "0xc3", "0xff", "0xff", "0xff", /* rows 9-16 of 5 */ "0xff", "0xff", "0xff", "0xc7", "0xf3", "0xf9", "0xf9", "0xc1", /* rows 1-8 of 6 */ "0x99", "0x99", "0x99", "0x99", "0xc3", "0xff", "0xff", "0xff", /* rows 9-16 of 6 */ "0xff", "0xff", "0xff", "0x81", "0x99", "0x9f", "0x9f", "0xcf", /* rows 1-8 of 7 */ "0xcf", "0xe7", "0xe7", "0xf3", "0xf3", "0xff", "0xff", "0xff", /* rows 9-16 of 7 */ "0xff", "0xff", "0xff", "0xc3", "0x99", "0x99", "0x99", "0xc3", /* rows 1-8 of 8 */ "0x99", "0x99", "0x99", "0x99", "0xc3", "0xff", "0xff", "0xff", /* rows 9-16 of 8 */ "0xff", "0xff", "0xff", "0xc3", "0x99", "0x99", "0x99", "0x99", /* rows 1-8 of 9 */ "0x83", "0x9f", "0x9f", "0xcf", "0xe3", "0xff", "0xff", "0xff" /* rows 9-16 of 9 */ }; /* * NsCounter - * * Handle a request of the form "/NS/Counter/my/page.htm" to create an * odometer-like count of visits to the /my/page.htm page. * */ static int NsCounter(void *context, Ns_Conn *conn) { char buf[BUFSIZE], dial[ODO_DIGITS]; unsigned long cnt; int status, i, l; char *p; Ns_DString ds; int fd; char *uri; char *hServer; char *urlPrefix; int nPrefix; hServer = Ns_ConnServer(conn); urlPrefix = (char *) context; /* * Figure out how many parts are in the * prefix, i.e., /NS/Counter has 2 parts. */ nPrefix = 0; p = urlPrefix; while ((p = strchr(p, `/')) != NULL) { ++p; ++nPrefix; } if (conn->request->urlc > nPrefix) { uri = Ns_SkipUrl(conn->request, nPrefix); } else { return Ns_ConnReturnBadRequest(conn, "Missing URI"); } /* * Convert the URI to the file, sneaking in counter prefix before the last * path element. */ Ns_DStringInit(&ds); p = strrchr(uri, `/'); *p = `\0'; Ns_UrlToFile(&ds, hServer, uri); Ns_DStringAppend(&ds, "/"); Ns_DStringAppend(&ds, COUNT_PREFIX); *p++ = `/'; Ns_DStringAppend(&ds, "."); Ns_DStringAppend(&ds, p); /* * Open (or create) the counter file and increment the page count. */ fd = open(ds.string, O_RDWR | O_CREAT, 0660); if (fd < 0) { Ns_Log(Error, "Could not open counter file `%s' for URI `%s': %s", ds.string, uri, strerror(errno)); Ns_DStringFree(&ds); return Ns_ConnReturnInternalError(conn); } Ns_DStringFree(&ds); i = read(fd, buf, BUFSIZE); if (i > 0) { buf[i] = `\0'; cnt = strtoul(buf, (char **) NULL, 10); } else { cnt = 0; } lseek(fd, 0, SEEK_SET); sprintf(buf, "%u\n", ++cnt); write(fd, buf, strlen(buf)); close(fd); /* Calc length (ignoring NL) and copy right-to-left into dial */ for (l = 0, p = buf; *p != `\0' && *p != NL; l++, p++); for (i = 0, p = buf; i < l; i++, p++) dial[ODO_DIGITS - l + i] = *p; for (i = 0; i < (ODO_DIGITS - l); i++) /* backfill with zeros */ dial[i] = `0'; /* * Build up the odometer xbm. */ Ns_DStringPrintf(&ds, "#define odo_width %d\n", (BM_WD * ODO_DIGITS)); Ns_DStringPrintf(&ds, "#define odo_height %d\n", BM_HT); Ns_DStringAppend(&ds, "static char odo_bits[] = {\n"); for (i = 0; i < BM_HT; i++) { for (l = 0; l < ODO_DIGITS; l++) { Ns_DStringAppend(&ds, bitmap[(((dial[l] - `0') * BM_HT) + i)]); Ns_DStringAppend(&ds, ", "); if (l == 7) Ns_DStringAppend(&ds, "\n"); } if (i == 15) { Ns_DStringAppend(&ds, "};"); } } Ns_DStringAppend(&ds, "\n"); /* * Setup the HTTP headers. */ Ns_ConnSetRequiredHeaders(conn, "image/x-xbitmap", ds.length); status = Ns_ConnFlushHeaders(conn, 200); if (status == NS_OK) { /* * Send the bitmap if the headers were sent o.k. * * Note that the underlying communications driver may send * less than the number of bytes requested so we need to * call Ns_ConnWrite() in a while loop to ensure all the * bytes are sent. If the connection is shut down while * attempting to send, the communication driver returns * -1 and we set status to NS_ERROR and immediately break * out of the loop. Whenever a status other than NS_OK * is returned from a request function, the AOLserver does * not run the registered trace functions (trace functions * are normally used for access logging and you shouldn't * normally log aborted connections). * * Actually, in this trivial case, you could simply use: * * status = Ns_ConnPuts(conn, ds.string) * * which pretty much implements the loop below. */ p = ds.string; i = ds.length; while (i > 0) { cnt = Ns_ConnWrite(conn, p, i); if (cnt < 0) { status = NS_ERROR; break; } i -= cnt; p += cnt; } } Ns_DStringFree(&ds); return status; }