comment j'ai fait mon compteur


Cette page vous explique comment j'ai fait un compteur. Evidement, c'est plus simple d'en utiliser un tout fait (je vous conseille fastcounter.linkexchange.com, rapide et gratuit) mais je suis curieux de nature. En résumé, dans ma page, j'ai inséré une image GIF, par contre sur mon serveur j'ai remplacé cette image par un programme cgi qui me crée cette image. Ceci vous permettra de voir : Cette page est écrite par P. Trau

Vous pouvez voir un autre exemple de page créée par CGI avec du graphisme


dans la page HTML de départ

Dans la page HTML de départ, j'ai inséré la commande :

<IMG SRC="/cgi-bin/compteur?nom=pat/ma-page" Align=middle>

J'ai donc inséré une image, en donnant l'adresse absolue du programme exécutable "compteur" (attention, le chemin des CGI, ici cgi-bin, est un chemin "calculé" par le serveur, il correspond à un chemin d'habitude différent de celui des fichiers HTML normaux, c'est pour cela que j'ai mis l'adresse absolue et non relative).

J'ai ensuite donné les arguments transmis à ce programme (par ?nom1=val1&nom2=val2&nom3=val3...), ici une seule seule variable (nom), avec une valeur définissant le nom de ma page (pas nécessairement le nom du fichier).

J'ai de plus centré l'image verticalement dans la ligne (align=middle).

dans le répertoire des CGI, sur ma station

J'ai écrit un programme en C, utilisant la librairie cgic disponible chez www.boutell.com, et je remercie chaleureusement Monsieur Boutell pour cet utilitaire.

Dans ce programme, la fonction int cgiMain()est la fonction principale (la vraie fonction main est dans la librairie cgic).

Dans cette fonction, j'envoie l'entête du document (le code qui permet à mon navigateur de savoir le type de document qu'il recoit, ici une image GIF) par l'instruction :

cgiHeaderContentType("image/gif");/* "text/html" habituellement */
Puis je récupère les arguments envoyés par ma page HTML par :
cgiFormStringNoNewlines("nom",i.nom,sizeof(tnom));
Ici une seule variable m'intéresse, elle s'apelle "nom".

Puis je recherche dans un fichier local le nombre précédent d'appels avec ce même nom par la fonction :

incrementeinfo(&i);
Cette fonction n'utilise que du C classique : j'ouvre le fichier COMPTEUR.DAT, je recherche si le nom existe déjà, si oui je lis le nombre actuellement dans le fichier et je l'incrémente, si non je rajoute le nom en fin de fichier avec un nombre valant 0.

Puis je "dessine" ce nombre sous forme d'un GIF par la fonction :

creeimage(i.nb);
Cette fonction utilise la bibliothèque graphique GD qui crée un fichier GIF disponible chez www.boutell.com, et je remercie chaleureusement Monsieur Boutell pour cet utilitaire.
La seule particularité est que je sauve cette image sous cgiOut (le flux de sortie de mon programme CGI) au lieu d'un fichier classique.

Et Voilà !


Ci dessous le listing complet de mon fichier "compteur.c"
#include <stdio.h>
/* les 3 bibliothèques ci-dessous sont récupérées sous "http://www.boutell.com" */
/* que Mr Boutell en soit chaleureusement remercié */
#include "cgic.h" /* pour faire du cgi en C */
#include "gd.h"   /* pour créer un GIF en C */
#include "gdfontl.h" /* pour écrire en "large" */

#define nomfic "compteur.dat"
#define nbmax 1000 /* cette limite n'est là que pour empêcher des bouclages infinis */

typedef char tnom[80];
typedef struct {
  tnom nom;
  int nb;
 }info;

int incrementeinfo(info *x)
/* retourne l'index du nom dans le fichier (-1 si erreur), et met dans
   le champ x->nb la valeur trouvée dans le fic. S'il n'existe pas encore,
   il le créée (avec nb=0) */
 {
  FILE *fic;
  size_t err;
  info actu;
  int index;
  
  if((fic=fopen(nomfic,"r+"))==NULL) /* ouverture en lecture+écriture, le fichier doit exister, même vide */
       {perror("ERREUR OUVERTURE");return(-1);}
  for(index=0;index<nbmax;index++)
   {
    err=fread(&actu,sizeof(info),1,fic); /* lecture d'une info dans le fichier */
/*    printf("LECTURE DE %s (NB=%d) ERR=%ld\n",actu.nom,actu.nb,err); */
    if (err>0L)
     {
      if(!strncmp(actu.nom,x->nom,nbmax)) /*ils sont egaux*/
       {   
	x->nb=actu.nb+1;
	fclose(fic);
	return(index);
       }
      /* sinon tester le suivant */
     }
    else if(!err) /* || eof(fic) normalement le deuxieme test est inutile */
     { /* on est à la fin du fichier: il on le rajoute */
      x->nb=0;
      if(fwrite(x,sizeof(info),1,fic)<=0) /* si negatif plus de place sur le disque */
       {printf("erreur d'ecriture sur fic !!!");return(-1);}
      fclose(fic);
      return(index);
     }
   }
  printf("attention, on a depasse %d enregistrements\n",nbmax);
  return(-1);
 }

int creeimage(int i)
/* crée une image GIF dans laquelle on met la valeur de i */
 {
	char texte[7]; /* la var string qui contiendra le nb i */
	/* Declare l'image */
	gdImagePtr im;
	/* Declare an output file , ici inutile car je ne stocke pas le résultat sur fichier */
	/* FILE *out; */
	/* Declare color indexes */
	int c_fond;
	int c_text;
	int c_cadr;
	sprintf(texte,"%06d",i); /* traduction de i en chaîne ASCII */
	/* Allocate the image: 80 pixels across by 30 pixels tall */
	im = gdImageCreate(80, 30);

	/* Allocate the color black (red, green and blue all minimum).
		Since this is the first color in a new image, it will
		be the background color. */
	c_fond = gdImageColorAllocate(im, 0, 0, 0);      

	/* Allocate the color white (red, green and blue all maximum). */
	c_text = gdImageColorAllocate(im, 255, 255, 255);  
	/* cadre presque rouge */   
	c_cadr = gdImageColorAllocate(im, 255, 25, 25);    
		
	/* dessin d'un cadre. j'utilise gdImageSX qui me donne la taille en X de l'image (ici 80), idem en Y */
	gdImageRectangle(im, 2, 2, gdImageSX(im)-3, gdImageSY(im)-3, c_cadr);
	gdImageString(im, gdFontLarge,
	     gdImageSX(im) / 2 - (strlen(texte) * gdFontLarge->w / 2), /* débuter au milieu de l'image moins la taille de la moitié des caractères à afficher */
	     gdImageSY(im) / 2 - gdFontLarge->h / 2,
	     texte, c_text); /* écrire le texte AU MILIEU !!!! */

	/* ouvrir le fic résultat, inutile ici car sortie directe */
	/* out = fopen("compteur.gif", "w+");*/

	/* Output the image to the disk file. */
	gdImageGif(im, cgiOut); /* ou uniquement out */   

	/* Close the file. */
	/* fclose(out);*/

	/* Destroy the image in memory. */
	gdImageDestroy(im);
	return(0);
}

int cgiMain()
/* le main pour Cgic, le "vrai" main étant dans cgic.h */
 {
  info i;
  /* Important: we must indicate the type of document */
  cgiHeaderContentType("image/gif");/* "text/html" habituellement */
  
  /* récupérer la valeur de la variable "nom" envoyée par le demandeur */      
  cgiFormStringNoNewlines("nom",i.nom,sizeof(tnom));
  /* rechercher le nb d'accès précédents et incrémentation */
  incrementeinfo(&i);
  /* "dessiner" ce nombre sous forme d'un GIF */
  creeimage(i.nb);
  /*printf("il y etait %d fois (IL Y EST UNE FOIS DE PLUS)\n\n",i.nb);*/
 }


Je vous donne aussi un makefile sous UNIX : (à copier dans votre répertoire, puis taper la commande make)
#----makefile pour compteur.c ---------- P.TRAU (testé sous Solaris 2.3)
#mon compilateur est gcc, modifiez la ligne si vous en avez un autre
CC=gcc 
AR=ar
CFLAGS=-O
LIBS=-L./ -lgd -lcgic -lm

all: libgd.a libcgic.a compteur

compteur: compteur.o libgd.a gd.h gdfonts.h gdfontl.h
	$(CC) compteur.o -o compteur    $(LIBS)

libgd.a: gd.o gdfontt.o gdfonts.o gdfontmb.o gdfontl.o gdfontg.o \
	gd.h gdfontt.h gdfonts.h gdfontmb.h gdfontl.h gdfontg.h
	rm -f libgd.a
	$(AR) rc libgd.a gd.o gdfontt.o gdfonts.o gdfontmb.o \
		gdfontl.o gdfontg.o
libcgic.a: cgic.o cgic.h
	rm -f libcgic.a
	$(AR) rc libcgic.a cgic.o

clean:
	rm -f *.o *.a compteur 

Cette page a été écrite le 2/2/97 par P. Trau