/*
 * 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))
 *    - acceptance of an argument to create a distinct counter file name; this
 *      permits refs like <img src="cgi-bin/odometer.cgi?argument"> when
 *      used in combination with Ksh script odometer.cgi:
 *             exec ./odometer $QUERY_STRING
 *
 * Code cleaned up and enhanced a little more by Francois Planque, 
 * fplanque@planete.net in March 1996; supports: 
 *    - sends out plain text error messages for debugging. These work when
 *      you reference the counter directly from your browser, i-e when
 *      you don't reference it in an <IMG SRC="..."> tag.
 */

#include <stdio.h>
#include <unistd.h>
#include <sys/file.h>

/*
 * Definition des images des chiffres:
 */
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 */
};

/*
 * Chemin d'acces au fichier qui garde la valeur courante du compteur: 
 * Etant donne le id sous lequel les scripts CGI s'executent,
 * ls permissions de ce fichier doivent etre 666:
 */
#define ODO_FILE        "/usr/home/fplanque/public_html/.odo%s" /* prime w/ 1 */

#define UPDATE          "r+"
#define NL              '\n'            /* new line char */
#define ODO_DIGITS      8               /* Nombre de chiffres a afficher */
#define BUFSIZE         (ODO_DIGITS+2)  /* 8 digits + NL + nul */
#define BM_HT           16              /* bitmap height */
#define BM_WD           8               /* bitmap width */

main(int argc, char *argv[])
{
    FILE *fp; 
    char path[64], buf[BUFSIZE], dial[ODO_DIGITS];
    unsigned long cnt;
    int i, l;
    char *p;

    /*
     * Verification des arguments:
     */
    if (argc != 2)
    {   /*
	 * Nombre d'arguments incorrect:
         * Affiche msg d'erreur et quitte:
         */
        printf( "Content-type: text/plain\n\n" );
	printf( "Odometer:\n" );
        printf( " Je n'ai pas recu de nom de compteur en argument.\n" );
	printf( " Essayez de m'appeler de la maniere suivante: http://.../cgi-bin/odometer?nom_compteur\n" );
        exit(0);
    }

    /*
     * Creation du nom de fichier du compteur en fonction du nom passe en argument:
     */
    sprintf(path, ODO_FILE, argv[1]);


    /*
     * ---------------------------------
     * Manipulation du fichier compteur:
     * ---------------------------------
     * Ouverture du fichier compteur:
     */
    if ((fp = fopen(path, UPDATE)) == NULL)
    {
        printf( "Content-type: text/plain\n\n" );
	printf( "Odometer:\n" );
	printf( " Impossible d'ouvrir le fichier compteur: %s\n", path );
	printf( " Verifiez que ce fichier existe, sinon creez le en y inscrivant " 
                "le chiffre 0 et un retour chariot.\n" );
	printf( " S'il existe, verifiez que les permissions sont 666. " 
                "Au besoin faites un    chmod 666 .odo*\n" );
	
	exit(0);
    }

    /* 
     * File-LOCK:
     * S'assure que personne d'autre n'est en train d'ecrire dans ce fichier,
     * sinon: attend que l'autre ait termine:
     */
    flock(fileno(fp), LOCK_EX);

    /*
     * Lecture du compteur actuel:
     */
    fgets(buf, BUFSIZE, fp);
    
    /*
     * Conversion de la chaine de caracteres lue en entier:
     */
    cnt = strtoul(buf, (char **)NULL, 10);

    /*
     * Incrementation:
     */
    cnt++;

    /*
     * Retourne au debut du fichier en vue d'y ecrire la nouvelle valeur du compteur:
     */
    rewind(fp);

    /*
     * Ecriture de la nouvelle valeur dans le fichier:
     */
    fprintf(fp, "%u\n", cnt);
    
    /*
     * Fermeture du fichier:
     */
    fclose(fp);

    /*
     * -----------------------------------------
     * Creation et envoi de l'image du compteur:
     * -----------------------------------------
     * Calcul de la longueur et copie de droite a gauche dans dial[]:
     */
    for(l = 0, p = buf; *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';

    /*
     * Envoi du header HTTP et de la definition XBM:
     */
    printf("Content-type: image/x-xbitmap\n\n");
    printf("#define odo_width  %d\n", (BM_WD*ODO_DIGITS));
    printf("#define odo_height %d\n", BM_HT);
    printf("static char odo_bits[] = {\n");

    for (i = 0; i < BM_HT; i++)
    {
	for (l = 0; l < ODO_DIGITS; l++)
	{
	    printf("%s, ", bitmap[(((dial[l]-'0')*BM_HT)+i)]);
	    if (l == 7)
		printf("\n");
	}
	if (i == 15)
	    printf("};");
    }
    printf("\n");
    exit(0);
}
