/* # Credits:
# Original C code: Frans van Hoesel (hoesel@chem.rug.nl)
# Original port to Perl: Dan Rich (drich@corp.sgi.com)
# Modifications for cgi: Michael Nelson (m.l.nelson@larc.nasa.gov)
# Modifications for NSF: Mike Morse (mmorse@nsf.gov)
# Modified from NSF version: Spencer Thomas (spencer.thomas@med.umich.edu)
# Modified for flexible use: Stefan Powell (spowell@netaccess.on.ca)
# Modified to ease installation and included instructions:
# Jeff Squyres (squyres@nd.edu)
* Converted back to C for use by VMS DECthreads server: David Jones.
*
* Usage:
* In your html document place an image with a src reference
* of /htbin/access_odometer/name, e.g.
*
*
*
* The path-info (name) is used to uniquely name a record in the
* file www_root:[000000]accesses.dat. Version number 0 is always used.
*
* After compiling, link with access_db.obj, cgilib.obj, and scriptlib.obj.
*
* For CERN server:
* Compile with NOCGILIB macro defined (/define=NOCGILIB)
* Link with access_db.obj only (plus C RTL is necessary).
*/
#include
#include
#include
#include
#include "access_db.h"
#define IMAGE_MIME_TYPE "image/x-xbm"
#define ACCESS_DB "www_root:[000000]accesses.dat"
/*
* Handle both DECthreads and CERN CGI environments via macros.
*/
#ifdef NOCGILIB
#define cgi_init(a,b) 1
#define cgi_info(a) getenv(strcpy(cgi_info_buf[4], a)-4)
static char cgi_info_buf[64] = {'W', 'W', 'W', '_' };
#define cgi_printf printf
#define net_link_set_mode(a) 1;
#else
#include "cgilib.h"
#include "scriptlib.h"
#endif
static int send_http_header ( char *stsline, char *content );
static char *access_file_fdl = "\
FILE; ORGANIZATION indexed; PROTECTION (system:RWED,owner:RWED,group,world);\
RECORD; CARRIAGE_CONTROL carriage_return; FORMAT fixed; SIZE 120;\
KEY 0; CHANGES no; PROLOG 3; SEG0_LENGTH 100; SEG0_POSITION 0; TYPE string;\
";
static int xbm_int ( int number, /* number to represent as bitmap */
int places, /* Number of digits */
int inverse, /* If true, invert foreground/background */
int margin, /* If true, put 4-bit margin around image */
char *outbuf,
int *outlen )
{
static char *invdigit_map[10] = {
"c3 99 99 99 99 99 99 99 99 c3", "cf c7 cf cf cf cf cf cf cf c7",
"c3 99 9f 9f cf e7 f3 f9 f9 81", "c3 99 9f 9f c7 9f 9f 9f 99 c3",
"cf cf c7 c7 cb cb cd 81 cf 87", "81 f9 f9 f9 c1 9f 9f 9f 99 c3",
"c7 f3 f9 f9 c1 99 99 99 99 c3", "81 99 9f 9f cf cf e7 e7 f3 f3",
"c3 99 99 99 c3 99 99 99 99 c3", "c3 99 99 99 99 83 9f 9f cf e3"};
static char *digit_map[10] = {
"3c 66 66 66 66 66 66 66 66 3c", "30 38 30 30 30 30 30 30 30 30",
"3c 66 60 60 30 18 0c 06 06 7e", "3c 66 60 60 38 60 60 60 66 3c",
"30 30 38 38 34 34 32 7e 30 78", "7e 06 06 06 3e 60 60 60 66 3c",
"38 0c 06 06 3e 66 66 66 66 3c", "7e 66 60 60 30 30 18 18 0c 0c",
"3c 66 66 66 3c 66 66 66 66 3c", "3c 66 66 66 66 7c 60 60 30 1c"};
char **dmap, *column[20];
int i, digit, row, off, length;
/*
* Convert number to digits.
*/
if ( number < 0 ) number = 0;
dmap = inverse ? invdigit_map : digit_map;
for ( i = 0; i < places; i++ ) column[i] = dmap[0];
for ( i = 0; (number > 0) && (i < places); i++ ) {
column[i] = dmap[number%10];
number = number / 10;
}
/*
* Put prolog in outbuf.
*/
sprintf ( outbuf, "#define count_width %d\n#define count_height %d\n",
places*8, margin ? 16 : 10 );
length = strlen ( outbuf );
strcpy ( &outbuf[length], "static char count_bits[] = {\n" );
length += 29;
/*
* Produce output rows.
*/
if ( margin ) {
for ( row = 0; row < 3; row++ ) {
for ( i = 0; i < places; i++ ) {
strcpy ( &outbuf[length], inverse ? "0xff," : "0x00," );
length += 5;
}
outbuf[length++] = '\n';
}
}
for ( row = 0; row < 10; row++ ) {
off = row * 3;
for ( i = places-1; i >= 0; --i ) {
outbuf[length++] = '0'; outbuf[length++] = 'x';
outbuf[length++] = column[i][off];
outbuf[length++] = column[i][off+1];
if ( (i == 0) && (row == 9) && !margin ) {
outbuf[length++] = '}'; outbuf[length++] = ';';
} else outbuf[length++] = ',';
}
outbuf[length++] = '\n';
}
if ( margin ) {
for ( row = 0; row < 3; row++ ) {
for ( i = places-1; i >= 0; --i ) {
strcpy ( &outbuf[length], inverse ? "0xff" : "0x00" );
length += 4;
if ( (i == 0) && ( row == 3 ) ) {
outbuf[length++] = '}'; outbuf[length++] = ';';
}
else outbuf[length++] = ',';
}
outbuf[length++] = '\n';
}
}
*outlen = length;
outbuf[length] = '\0';
return length;
}
/*****************************************************************************/
/* Return accesses count for indicated path, updating count by 1 on first call
* A version number less than or equal to 0 means ignore the version number.
*
* The path string is prefixed with "//odometer/" to avoid confusion with
* the pre-processor counters. No checks for access permissions are made.
*/
static int get_access_count ( char *path, int version ) {
IFILE *fp;
static int accesses_known = 0, accesses, rec_version;
char accessesRecord[128];
char accessesPath[104];
char accessesStr[20];
int status, i, new_rec;
size_t length;
/*
* Reset accesses_known if version changes.
*/
if ( accesses_known ) if ( (version > 0) && (version != rec_version) ) {
accesses_known = 0;
}
if ( !accesses_known ) {
/*
* Get/update access count/version in file. Try to open file.
*/
fp = ifopen ( ACCESS_DB, "r+" );
if ( !fp ) {
/*
* Attempt to create indexed file and retry open.
*/
status = ifdlcreate
( access_file_fdl, "accesses.dat", "www_root:[000000]" );
if ( status & 1 )
fp = ifopen ( "www_root:[000000]accesses.dat","r+" );
}
if ( !fp ) {
send_http_header ( "500 Failed to access file", "text/plain" );
cgi_printf ( "Could not open accesses.dat file.");
return -1;
}
/*
* File open, do indexed read to get record for specified path.
*/
strcpy ( accessesPath, "//odometer/" );
strncpy ( &accessesPath[11], path, sizeof(accessesPath)-12 );
accessesPath[sizeof(accessesPath)-1] = '\0';
for ( i = 0; (i < 100) && accessesPath[i]; i++ )
accessesPath[i] = _toupper ( accessesPath[i] );
while ( i < 100 ) accessesPath[i++] = ' ';
accessesPath[i] = '\0';
status = ifread_rec(accessesRecord,120,&length,fp,0,accessesPath,100);
if ( status & 1 ) {
sscanf(accessesRecord+100,"%10d%10d",&accesses, &rec_version);
new_rec = 0;
} else {
/*
* Read error on record, assume non-existent and make initial.
*/
accesses = 0;
rec_version = 0;
new_rec = 1;
}
/*
* Reset or update access count by 1 and update record in file.
*/
if ( version <= 0 ) version = rec_version;
else if ( version != rec_version ) accesses = 0;
accesses++;
sprintf(accessesRecord,"%-100.100s%10.10d%10.10d",accessesPath,accesses,
version);
if ( new_rec ) {
iferror(fp);
status = ifwrite_rec(accessesRecord,120,fp);
} else {
status = ifupdate_rec(accessesRecord,120,fp);
}
if ( (status&1) == 0 ) iferror(fp);
ifclose(fp);
accesses_known = 1;
}
return accesses;
}
/**************************************************************************/
/* Prepare to send back response. Build standard response header.
*/
static int send_http_header ( char *stsline, char *content )
{
int status;
/*
*/
cgi_printf ( "Content-type: %s\n", content );
cgi_printf ( "status: %s\n\n", stsline );
return 1;
}
/****************************************************************************/
/* Main routine.
*/
int main ( int argc, char **argv )
{
int number, length, status;
char *path, buffer[2024];
/*
* Initialize CGI library.
*/
status = cgi_init ( argc, argv );
if ( (status&1) == 0 ) exit ( status );
/*
* Get path info and and use for access path.
*/
path = cgi_info ( "PATH_INFO" );
if ( !path ) {
send_http_header ( "500 bad path info", "text/plain" );
return 20;
}
number = get_access_count ( path, 0 );
if ( number < 1 ) exit ( 1 ); /* Response already sent */
/*
* Generate xbm odometer image.
*/
xbm_int ( number,
7, /* 7 digits */
0, /* Normal (not inverted) */
0, /* No margin */
buffer, &length ); /* Recieves output image */
cgi_printf ( "Content-type: %s\n", IMAGE_MIME_TYPE );
cgi_printf ( "Content-transfer-encoding: 7bit\n\n" );
net_link_set_mode ( 1 ); /* put into binary mode */
cgi_printf ( "%s", buffer );
return 1;
}