/*
* count.xbm
*
* Originally written by Chris Stephens
* However, his code was so badly mangled that the result barely looks like
* his anymore. Nevertheless, give credit where credit is due.
*
* Done by Dave Ratner:
* code style and multiple bugs fixed
* file locking added
* added support for multiple counts on different pages
*
* This script is Shareware.
* Copyright Dave Ratner 1995. All rights reserved.
*
* This script reads a number from a datafile, increments the number and
* then writes the new number back to the datafile. The script then creates
* an X-bitmap image of the number, similar to the odometer on your car, and
* returns that to an . Therefore, you don't have to have
* server-side includes enabled or make modifications to the server
* configuration files in order to use this script.
* You only need to be able to run CGIs.
*
* TO USE:
*
* Step 1:
* Change the definition of COUNT_FILE and ERROR_LOG if required,
* though this is not strictly necessary, just whatever you like best.
*
* Step 2:
* Create the file COUNT_FILE and add all entries and counts
* in the form:
* name1 count1
* name2 count2
*
* The names must all be unique, single-string character strings.
* The file should be writable by whatever user the web server runs as.
*
* IMPORTANT: in order to do in-line replacements of the count field,
* the numbers must have leading 0's. Use enough leading
* 0's to make the number of size MAXNUMSIZE.
*
* Step 3:
* Compile this file with your favorite C compiler.
* (or your least favorite; I really don't care).
* The executable cgi script should have the extension ".xbm"
* for x-bitmap.
*
* Step 4:
* In the html page that you want the counter to appear, place the
* following html command:
* .
*
* For example, if the database file looked like:
* ratner-homepage 00001234
* john-homepage 00000008
* ratner-cheerspage 00086511
* and the script lives in /cgi-bin,
* and the database lives in /cgi-bin, then the directory argument
* is not necessary, and the html command for ratner's homepage would be:
*
*
* If the database lived in /home/ratner/counter, then the html command is
*
*
*/
#include
#include
#include
#include
#include
#include
#include
char *digits[] =
{"0xff","0xff","0xff","0xc3","0x99","0x99","0x99","0x99",
"0x99","0x99","0x99","0x99","0xc3","0xff","0xff","0xff",
"0xff","0xff","0xff","0xcf","0xc7","0xcf","0xcf","0xcf",
"0xcf","0xcf","0xcf","0xcf","0xcf","0xff","0xff","0xff",
"0xff","0xff","0xff","0xc3","0x99","0x9f","0x9f","0xcf",
"0xe7","0xf3","0xf9","0xf9","0x81","0xff","0xff","0xff",
"0xff","0xff","0xff","0xc3","0x99","0x9f","0x9f","0xc7",
"0x9f","0x9f","0x9f","0x99","0xc3","0xff","0xff","0xff",
"0xff","0xff","0xff","0xcf","0xcf","0xc7","0xc7","0xcb",
"0xcb","0xcd","0x81","0xcf","0x87","0xff","0xff","0xff",
"0xff","0xff","0xff","0x81","0xf9","0xf9","0xf9","0xc1",
"0x9f","0x9f","0x9f","0x99","0xc3","0xff","0xff","0xff",
"0xff","0xff","0xff","0xc7","0xf3","0xf9","0xf9","0xc1",
"0x99","0x99","0x99","0x99","0xc3","0xff","0xff","0xff",
"0xff","0xff","0xff","0x81","0x99","0x9f","0x9f","0xcf",
"0xcf","0xe7","0xe7","0xf3","0xf3","0xff","0xff","0xff",
"0xff","0xff","0xff","0xc3","0x99","0x99","0x99","0xc3",
"0x99","0x99","0x99","0x99","0xc3","0xff","0xff","0xff",
"0xff","0xff","0xff","0xc3","0x99","0x99","0x99","0x99",
"0x83","0x9f","0x9f","0xcf","0xe3","0xff","0xff","0xff"
};
/*
* COUNT_FILE = name of the database file with counts
* LOCK_FILE = global lock file used to enforce exclusive access to the
* database count file
* ERROR_LOG = where to write errors to
* MAXNUMSIZE = number of digits
* MAXLINESIZE = maximum line length in the database file
* MAXWAITTIME = maximum # of times we loop waiting for the database file
* to unlock. This must be greater than 0.
*/
#define COUNT_FILE "count.txt"
#define LOCK_FILE ".count_lock"
#define ERROR_LOG "error_log"
#define MAXNUMSIZE 8
#define MAXLINESIZE 256
#define MAXWAITTIME 50
/*
* Take the number in num and
* convert it into an 8-digit string with leading zeros.
* Assumes there is enough space in str.
*/
void
makeIntoString(num, str)
int num;
char *str;
{
int pos;
/*
* NEEDSWORK: this depends on MAXNUMSIZE
*/
strcpy(str, "00000000");
pos = MAXNUMSIZE-1;
while (num > 0) {
str[pos--] = '0' + (num % 10);
num = num / 10;
}
}
/*
* Write an error to the error log
*/
void
writeError(errorStr)
char *errorStr;
{
FILE *fp;
if ((fp = fopen(ERROR_LOG,"a")) != NULL) {
fprintf(fp, "%s\n", errorStr);
fclose(fp);
}
}
/*
* Read the file that holds the numeric counter value
* Get the counter, bump it, and write it back.
*/
int
readAndWriteVal(name)
char *name; /* string we are looking for in the database file */
{
FILE *fp;
char line[MAXLINESIZE];
char field[MAXLINESIZE];
char numberString[MAXNUMSIZE];
int num;
int retval = 0;
int waitTime = 0;
/*
* Open and lock file
*/
while (waitTime++ < MAXWAITTIME && open(LOCK_FILE, O_CREAT | O_EXCL) == -1)
;
if (waitTime >= MAXWAITTIME) {
writeError("timed out waiting for lock file to disappear");
return 0;
}
if ((fp = fopen(COUNT_FILE,"r+")) == NULL) {
unlink(LOCK_FILE);
writeError("could not open count file");
return 0;
}
/*
* Scan the databse file, looking for a match on "name"
*/
while (fgets(line, MAXLINESIZE, fp) != NULL) {
sscanf(line, "%s %d", &field, &num);
if (!strcmp(field, name)) {
/*
* We found the right entry
* Bump the count and rewrite the database file
*/
retval = num;
fseek(fp, -strlen(line), SEEK_CUR);
makeIntoString(++num, numberString);
fprintf(fp, "%s %s", name, numberString);
break;
}
}
/*
* Unlock and close file
*/
fclose(fp);
unlink(LOCK_FILE);
return retval;
}
/*
* Mainline
*
* command-line: count [directory] counter-name
*/
int
main(argc, argv)
int argc;
char **argv;
{
int num;
char numberString[MAXNUMSIZE];
int row, col; /* row and column for creating bitmap */
/*
* Check usage
* Usage: script-name [optional directory]
*/
switch (argc) {
case 3:
/*
* change directory to argv[1]
*/
if (chdir(*++argv) != 0) {
chdir("/tmp");
writeError("couldn't chdir to requested directory");
}
/* NOTE: explicit fall-through */
case 2:
/*
* Get, bump, and write back new value
*/
num = readAndWriteVal(*++argv);
break;
default:
num = 0;
writeError("incorrect number of arguments");
}
/*
* Put the number into an 8 digit string.
*/
makeIntoString(num, numberString);
/*
* Print out the bitmap
*/
printf ("Content-type: image/x-xbitmap\n\n");
printf ("#define count_width 64\n");
printf ("#define count_height 16\n");
printf ("static char count_bits[] = {\n");
for (row = 0; row < 16; row++)
{
for (col = 0; col < 8; col++)
{
num = (int)(numberString[col] - '0');
printf(digits[((num*16) + row)]);
if (col < 7) {
printf(", ");
}
}
printf((row == 15 ? "};" : ",\n"));
}
printf("\n");
return 0;
}