/* * 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; }