version 0.5
[irssistats] / irssistats.c
1 /* Usage: irssistats [/path/to/file.conf] */
2
3 #include <sys/types.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <time.h>
7 #include <string.h>
8 #include <regex.h>
9
10 /* Config */
11 #define MAXUSERS 10000
12 #define MAXNICKLENGTH 50
13 #define MAXLINELENGTH 2000
14 #define MAXQUOTELENGTH 100
15 #define NBUSERS 50
16 #define NBUSERSTIME 10
17 #define NBURLS 5
18 #define NBTOPICS 5
19 #define NBWORDS 20
20 #define MINWORDLENGTH 5
21
22 /* irssistats */
23 #define VERSION "0.5"
24 #define URL "http://royale.zerezo.com/irssistats/"
25
26 /* Counters */
27 #define D_SMILE     0
28 #define D_FROWN     1
29 #define D_EXCLAM    2
30 #define D_QUESTION  3
31 #define D_ME        4
32 #define D_TOPIC     5
33 #define D_MODE      6
34 #define D_KICK      7
35 #define D_KICKED    8
36 #define D_URL       9
37 #define D_JOIN      10
38 #define D_NICK      11
39 #define D_MONOLOGUE 12
40 #define NBCOUNTERS  13
41 char *counters[NBCOUNTERS]={"C_SMILE","C_FROWN","C_EXCLAM","C_QUESTION","C_ME","C_TOPIC","C_MODE","C_KICK","C_KICKED","C_URL","C_JOIN","C_NICK","C_MONOLOGUE"};
42
43 /* Languages */
44 #define NBLANGUAGES 8
45 #define NBKEYS 38
46 char *keys[NBLANGUAGES][NBKEYS+1][2]= /* first key used for language name and abbreviation */
47 {
48   { /* English language */
49     { "English",      "en" },
50     { "HEADER",       "Statistics for %s by %s" },
51     { "LEGEND",       "Legend" },
52     { "LASTDAYS",     "Lastdays statistics" },
53     { "TOPHOURS",     "Hourly statistics" },
54     { "TOPUSERS",     "Most active people" },
55     { "OTHERS",       "There are %d left not ranked..." },
56     { "NBLINES",      "lines" },
57     { "NICK",         "nick" },
58     { "AVGLETTERS",   "letters/lines" },
59     { "HOURS",        "hours" },
60     { "QUOTE",        "random message" },
61     { "TOPUSERSTIME", "Most active people by time of day" },
62     { "RANDTOPICS",   "Some topics" },
63     { "CHANGEDBY",    "changed by" },
64     { "NEWTOPIC",     "new topic" },
65     { "RANDURLS",     "Some URLs" },
66     { "POSTEDBY",     "posted by" },
67     { "POSTEDURL",    "URL" },
68     { "TOPWORDS",     "Most used words" },
69     { "WORD",         "word" },
70     { "OCCURRENCES",  "occurrences" },
71     { "BIGNUMBERS",   "Some big numbers..." },
72     { "NUMBERS",      "numbers" },
73     { "TIME",         "%d lines (%d days) parsed in %d seconds" },
74     { "FOOTER",       "Statistics generated by" },
75     { "C_SMILE",      "is often happy :)" },
76     { "C_FROWN",      "is often sad :(" },
77     { "C_EXCLAM",     "yells a lot !" },
78     { "C_QUESTION",   "asks a lot of questions ?" },
79     { "C_ME",         "likes /me command" },
80     { "C_TOPIC",      "often changes the topic" },
81     { "C_MODE",       "often changes the modes" },
82     { "C_KICK",       "likes to /kick" },
83     { "C_KICKED",     "is often kicked" },
84     { "C_URL",        "posts many URLs" },
85     { "C_JOIN",       "doesn't know wether to stay or quit" },
86     { "C_NICK",       "often changes his nick" },
87     { "C_MONOLOGUE",  "speaks a lot of monologues" }
88   },
89   { /* French language */
90     { "French",       "fr" },
91     { "HEADER",       "Statistiques de %s par %s" },
92     { "LEGEND",       "L&eacute;gende" },
93     { "LASTDAYS",     "Statistiques des derniers jours" },
94     { "TOPHOURS",     "Statistiques horaires" },
95     { "TOPUSERS",     "Personnes les plus actives" },
96     { "OTHERS",       "Il reste %d personnes non class&eacute;es..." },
97     { "NBLINES",      "lignes" },
98     { "NICK",         "nick" },
99     { "AVGLETTERS",   "lettres/lignes" },
100     { "HOURS",        "heures" },
101     { "QUOTE",        "message al&eacute;atoire" },
102     { "TOPUSERSTIME", "Personnes les plus actives par p&eacute;riode de la journ&eacute;e" },
103     { "RANDTOPICS",   "Quelques topics" },
104     { "CHANGEDBY",    "chang&eacute; par" },
105     { "NEWTOPIC",     "nouveau topic" },
106     { "RANDURLS",     "Quelques URLs" },
107     { "POSTEDBY",     "post&eacute;e par" },
108     { "POSTEDURL",    "URL" },
109     { "TOPWORDS",     "Mots les plus utilis&eacute;s" },
110     { "WORD",         "mot" },
111     { "OCCURRENCES",  "occurrences" },
112     { "BIGNUMBERS",   "Quelques grands nombres..." },
113     { "NUMBERS",      "nombres" },
114     { "TIME",         "%d lignes (%d jours) trait&eacute;es en %d secondes" },
115     { "FOOTER",       "Statistiques g&eacute;n&eacute;r&eacute;es par" },
116     { "C_SMILE",      "est souvent heureux :)" },
117     { "C_FROWN",      "est souvent triste :(" },
118     { "C_EXCLAM",     "hurle beaucoup !" },
119     { "C_QUESTION",   "pose beaucoup de questions ?" },
120     { "C_ME",         "aime la commande /me" },
121     { "C_TOPIC",      "change souvent le topic" },
122     { "C_MODE",       "change souvent les modes" },
123     { "C_KICK",       "aime la commande /kick" },
124     { "C_KICKED",     "est souvent kick&eacute;" },
125     { "C_URL",        "poste beaucoup d'URLs" },
126     { "C_JOIN",       "ne sait pas s'il doit rester ou partir" },
127     { "C_NICK",       "change souvent de nick" },
128     { "C_MONOLOGUE",  "parle beaucoup de monologues" }
129   },
130   { /* German language */
131     /* contributed by Valentin Gelhorn <valentin.gelhorn@web.de> */
132     { "German",       "de" },
133     { "HEADER",       "Statistiken f&uuml;r %s von %s" },
134     { "LEGEND",       "Legende" },
135     { "LASTDAYS",     "Statistik der letzten Tage" },
136     { "TOPHOURS",     "St&uuml;ndliche Statistik" },
137     { "TOPUSERS",     "Die aktivsten Personen" },
138     { "OTHERS",       "Es bleiben noch %d uneingetragene" },
139     { "NBLINES",      "Zeilen" },
140     { "NICK",         "Nick" },
141     { "AVGLETTERS",   "Buchstaben/Zeile" },
142     { "HOURS",        "Stunden" },
143     { "QUOTE",        "Zuf&auml;llig ausgewaehlte Zitate" },
144     { "TOPUSERSTIME", "Die aktivsten Personen zur bestimmten Tageszeit" },
145     { "RANDTOPICS",   "Ein paar Topics" },
146     { "CHANGEDBY",    "Gesetzt von" },
147     { "NEWTOPIC",     "Neues topic" },
148     { "RANDURLS",     "Ein paar URLs" },
149     { "POSTEDBY",     "Geschrieben von" },
150     { "POSTEDURL",    "URL" },
151     { "TOPWORDS",     "Am h&auml;ufigsten benutze W&ouml;rter" },
152     { "WORD",         "Wort" },
153     { "OCCURRENCES",  "Vorkommen" },
154     { "BIGNUMBERS",   "Ein paar grosse Zahlen" },
155     { "NUMBERS",      "Zahlen" },
156     { "TIME",         "%d Zeilen (%d Tage) analysiert in %d Sekunden" },
157     { "FOOTER",       "Statistiken wurden erstellt von" },
158     { "C_SMILE",      "ist oft gl&uuml;klich :)" },
159     { "C_FROWN",      "ist oft traurig :(" },
160     { "C_EXCLAM",     "schreit oft !" },
161     { "C_QUESTION",   "stellt viele Fragen ?" },
162     { "C_ME",         "mag /me'en" },
163     { "C_TOPIC",      "aendert oft das Topico" },
164     { "C_MODE",       "aendert oft die Modes" },
165     { "C_KICK",       "mag /kick'en" },
166     { "C_KICKED",     "wird oft gekickt"},
167     { "C_URL",        "schreibt viele URLs"},
168     { "C_JOIN",       "kann sich nicht entscheiden ob er bleiben oder gehen soll" },
169     { "C_NICK",       "&auml;ndert oft seinen Nick" },
170     { "C_MONOLOGUE",  "spricht oft Monologe" }
171   },
172   { /* Spanish language */
173     /* contributed by Alex <ainaker@gmx.net> */
174     { "Spanish",      "es" },
175     { "HEADER",       "Estad&iacute;sticas de %s por %s" },
176     { "LEGEND",       "Leyenda" },
177     { "LASTDAYS",     "Estad&iacute;sticas de los &uacute;ltimos d&iacute;as" },
178     { "TOPHOURS",     "Estad&iacute;sticas por horas" },
179     { "TOPUSERS",     "Los que m&aacute;s escriben" },
180     { "OTHERS",       "Hay %d m&aacute;s que no llegaron..." },
181     { "NBLINES",      "l&iacute;neas" },
182     { "NICK",         "nick" },
183     { "AVGLETTERS",   "letras por l&iacute;nea" },
184     { "HOURS",        "horas" },
185     { "QUOTE",        "Frase aleatoria" },
186     { "TOPUSERSTIME", "Los que m&aacute;s escriben seg&uacute;n la hora" },
187     { "RANDTOPICS",   "Algunos topics" },
188     { "CHANGEDBY",    "Puestos por" },
189     { "NEWTOPIC",     "topic" },
190     { "RANDURLS",     "Algunas URLs" },
191     { "POSTEDBY",     "puestas por" },
192     { "POSTEDURL",    "URL" },
193     { "TOPWORDS",     "Palabras m&aacute;s usadas" },
194     { "WORD",         "Palabra" },
195     { "OCCURRENCES",  "Frecuencia" },
196     { "BIGNUMBERS",   "Algunos datos..." },
197     { "NUMBERS",      "N&uacute;mero de veces" },
198     { "TIME",         "%d lineas (%d d&iacute;as) procesadas en %d segundos" },
199     { "FOOTER",       "Estad&iacute;sticas generadas por" },
200     { "C_SMILE",      "Suele estar fel&iacute;z :)" },
201     { "C_FROWN",      "Suele estar triste :(" },
202     { "C_EXCLAM",     "Grita mucho !" },
203     { "C_QUESTION",   "Hace muchas preguntas ?" },
204     { "C_ME",         "Abusa del comando /me" },
205     { "C_TOPIC",      "Suele cambiar el topic" },
206     { "C_MODE",       "Cambia a veces los modos del canal" },
207     { "C_KICK",       "Le gusta patear" },
208     { "C_KICKED",     "Es pateado con frecuencia" },
209     { "C_URL",        "Pone muchas URLs" },
210     { "C_JOIN",       "No sabe si irse o quedarse" },
211     { "C_NICK",       "Cambia mucho de nick" },
212     { "C_MONOLOGUE",  "Habla solo" }
213   },
214   { /* Polish language */
215     /* contributed by Piotr Jarmuz <coreupper@yahoo.com> */
216     { "Polish",       "pl" },
217     { "HEADER",       "Statystyki dla %s przez %s" },
218     { "LEGEND",       "Legenda" },
219     { "LASTDAYS",     "Statystyki z ostatnich dni" },
220     { "TOPHOURS",     "Statystyki godzinne" },
221     { "TOPUSERS",     "Najaktywniejsi ludzie" },
222     { "OTHERS",       "Zostalo jeszcze %d nie sklasyfikowanych..." },
223     { "NBLINES",      "linie" },
224     { "NICK",         "nick" },
225     { "AVGLETTERS",   "litery/linie" },
226     { "HOURS",        "godziny" },
227     { "QUOTE",        "przypadkowa wiadomosc" },
228     { "TOPUSERSTIME", "Najaktywniejsi ludzie wedlug czasu dnia" },
229     { "RANDTOPICS",   "Pare tematow" },
230     { "CHANGEDBY",    "zmienione przez" },
231     { "NEWTOPIC",     "nowy temat" },
232     { "RANDURLS",     "Pare URL-i" },
233     { "POSTEDBY",     "wyslane przez" },
234     { "POSTEDURL",    "URL" },
235     { "TOPWORDS",     "Najczestsze slowa" },
236     { "WORD",         "slowo" },
237     { "OCCURRENCES",  "wystapienia" },
238     { "BIGNUMBERS",   "Pare wielkich liczb..." },
239     { "NUMBERS",      "liczby" },
240     { "TIME",         "%d linii (%d dni) sparsowanych w %d sekund" },
241     { "FOOTER",       "Statystyki wygenerowane przez" },
242     { "C_SMILE",      "jest czesto szczesliwy :)" },
243     { "C_FROWN",      "jest czesto smutny :(" },
244     { "C_EXCLAM",     "duzo krzyczy !" },
245     { "C_QUESTION",   "zadaje duzo pytan ?" },
246     { "C_ME",         "lubi /mnie polecenie" },
247     { "C_TOPIC",      "czesto zmienia temat" },
248     { "C_MODE",       "czesto zmienia tryb" },
249     { "C_KICK",       "lubi /kopac" },
250     { "C_KICKED",     "czesto go wykopuja" },
251     { "C_URL",        "wysyla duzo URL-i" },
252     { "C_JOIN",       "nie wie czy zostac czy wyjsc" },
253     { "C_NICK",       "czesto zmienia swojego nicka" },
254     { "C_MONOLOGUE",  "czesto mowi monologiem" }
255   },
256   { /* Finnish language */
257     /* contributed by Antti Huopana <ahuopana@ratol.fi> */
258     { "Finnish",      "fi" },
259     { "HEADER",       "Kanavan %s tilastot - %s" },
260     { "LEGEND",       "Merkkien selitykset" },
261     { "LASTDAYS",     "Viime päivien tilastot" },
262     { "TOPHOURS",     "Tilastot tunneittain" },
263     { "TOPUSERS",     "Aktiivisimmat ihmiset" },
264     { "OTHERS",       "Jäljelle jäi %d joita ei listattu..." },
265     { "NBLINES",      "rivit" },
266     { "NICK",         "nikki" },
267     { "AVGLETTERS",   "kirjainta/rivi" },
268     { "HOURS",        "tunnit" },
269     { "QUOTE",        "satunnainen viesti" },
270     { "TOPUSERSTIME", "Vuorokauden ajan mukaan aktiivisimmat" },
271     { "RANDTOPICS",   "Joitakin aiheita" },
272     { "CHANGEDBY",    "vaihtaja" },
273     { "NEWTOPIC",     "aihe" },
274     { "RANDURLS",     "Joitakin URLeja" },
275     { "POSTEDBY",     "lähettäjä" },
276     { "POSTEDURL",    "URL" },
277     { "TOPWORDS",     "Eniten käytettyjä sanoja" },
278     { "WORD",         "sana" },
279     { "OCCURRENCES",  "käytetty" },
280     { "BIGNUMBERS",   "Joitakin isoja lukuja..." },
281     { "NUMBERS",      "luvut" },
282     { "TIME",         "%d riviä (%d päivää) parsittu %d sekunnissa" },
283     { "FOOTER",       "Tilastot on generoinut" },
284     { "C_SMILE",      "on usein iloinen :)" },
285     { "C_FROWN",      "on usein surullinen :(" },
286     { "C_EXCLAM",     "möykkää paljon !" },
287     { "C_QUESTION",   "kyselee liikaa ?" },
288     { "C_ME",         "pitää itsestään" },
289     { "C_TOPIC",      "vaihtaa usein aihetta" },
290     { "C_MODE",       "haluaa elää muuttuvassa maailmassa" },
291     { "C_KICK",       "pitää potkimisesta" },
292     { "C_KICKED",     "tykkää tulla potkituksi" },
293     { "C_URL",        "surffailee liikaa" },
294     { "C_JOIN",       "ei tiedä ollakko vai eikö olla" },
295     { "C_NICK",       "kärsii identiteettiongelmista" },
296     { "C_MONOLOGUE",  "höpöttää paljon itsekseen" }
297   },
298   { /* Italian language */
299     /* contributed by Coviello Giuseppe <giuseppecoviello@tin.it> <http://coviello.altervista.org> */
300     { "Italian",      "it" },
301     { "HEADER",       "Statistiche per il canale %s di %s" },
302     { "LEGEND",       "Legenda" },
303     { "LASTDAYS",     "Statistiche degli ultimi giorni" },
304     { "TOPHOURS",     "Statistiche in ore" },
305     { "TOPUSERS",     "Utenti pi&ugrave; attivi" },
306     { "OTHERS",       "Ci sono %d utenti non classificati..." },
307     { "NBLINES",      "righe" },
308     { "NICK",         "nick" },
309     { "AVGLETTERS",   "lettere/righe" },
310     { "HOURS",        "ore" },
311     { "QUOTE",        "messaggio casuale" },
312     { "TOPUSERSTIME", "Utenti più attivi del giorno (divisi per fasce orarie)" },
313     { "RANDTOPICS",   "Alcuni topic" },
314     { "CHANGEDBY",    "cambiato da" },
315     { "NEWTOPIC",     "nuovo topic" },
316     { "RANDURLS",     "ALcuni URL" },
317     { "POSTEDBY",     "postato da" },
318     { "POSTEDURL",    "URL" },
319     { "TOPWORDS",     "Le parole più usate" },
320     { "WORD",         "parola" },
321     { "OCCURRENCES",  "usata" },
322     { "BIGNUMBERS",   "Alcuni numeri ..." },
323     { "NUMBERS",      "numeri" },
324     { "TIME",         "%d righe (%d giorni) esaminate in %d secondi" },
325     { "FOOTER",       "Statistiche generate da" },
326     { "C_SMILE",      "è spesso felice :)" },
327     { "C_FROWN",      "è spesso triste :(" },
328     { "C_EXCLAM",     "esclama molto !" },
329     { "C_QUESTION",   "fa molte domande ?" },
330     { "C_ME",         "ama il comando /me" },
331     { "C_TOPIC",      "cambia spesso il topic" },
332     { "C_MODE",       "cambia spesso i mode" },
333     { "C_KICK",       "ama /kick(are)" },
334     { "C_KICKED",     "è kickato spesso" },
335     { "C_URL",        "posta molti URL" },
336     { "C_JOIN",       "non sa se è ora di andare o restare" },
337     { "C_NICK",       "cambia spesso il nick" },
338     { "C_MONOLOGUE",  "fa molti monologhi" }
339   },
340   { /* Dutch language */
341     /* contributed by Jeroen Ubbink <crasp@blackbyte.nl> */
342     { "Dutch",        "nl" },
343     { "HEADER",       "Statistieken voor %s door %s" },
344     { "LEGEND",       "Legenda" },
345     { "LASTDAYS",     "Statistieken van de laatste dagen" },
346     { "TOPHOURS",     "Statistieken per uur" },
347     { "TOPUSERS",     "Meest actieve mensen" },
348     { "OTHERS",       "Er zijn nog %d niet in de top..." },
349     { "NBLINES",      "regels" },
350     { "NICK",         "nick" },
351     { "AVGLETTERS",   "letters/lijn" },
352     { "HOURS",        "uren" },
353     { "QUOTE",        "Willekeurige regel" },
354     { "TOPUSERSTIME", "Meest actieve mensen per tijdstip per dag" },
355     { "RANDTOPICS",   "Enkele topics" },
356     { "CHANGEDBY",    "gewijzigd door" },
357     { "NEWTOPIC",     "nieuwe topic" },
358     { "RANDURLS",     "Enkele URLs" },
359     { "POSTEDBY",     "Geplaatst door" },
360     { "POSTEDURL",    "URL" },
361     { "TOPWORDS",     "Meest gebruikte woorden" },
362     { "WORD",         "woord" },
363     { "OCCURRENCES",  "aantal" },
364     { "BIGNUMBERS",   "Enkele grote aantallen..." },
365     { "NUMBERS",      "numbers" },
366     { "TIME",         "%d regels (%d dagen) verwerkt in %d seconden" },
367     { "FOOTER",       "Statistieken gegenereert door" },
368     { "C_SMILE",      "is vaak vrolijk :)" },
369     { "C_FROWN",      "is vaak droevig :(" },
370     { "C_EXCLAM",     "schreeuwt veel !" },
371     { "C_QUESTION",   "stelt veel vragen ?" },
372     { "C_ME",         "vindt /me een leuk commando" },
373     { "C_TOPIC",      "verandert vaak de topic" },
374     { "C_MODE",       "verandert vaak de modes" },
375     { "C_KICK",       "vindt /kick erg leuk" },
376     { "C_KICKED",     "wordt vaak gekickt" },
377     { "C_URL",        "plaatst veel URLs" },
378     { "C_JOIN",       "twijfelt tussen blijven of gaan" },
379     { "C_NICK",       "verandert vaak van nick" },
380     { "C_MONOLOGUE",  "spreekt veel monologen" }
381   }  
382 };
383
384 int language=0; /* default to english */
385
386 char *L(char *key)
387 {
388   int i;
389   for (i=1;i<=NBKEYS;i++) if (strcmp(key,keys[language][i][0])==0) return(keys[language][i][1]);
390   fprintf(stderr,"unknown language key: %s\n",key);
391   return("");
392 }
393
394 /* Variables */
395
396 int debug=1; /* 0 = none ; 1 = normal ; 2 = verbose */
397 char channel[MAXLINELENGTH]="set_channel_in_config_file";
398 char maintainer[MAXLINELENGTH]="set_maintainer_in_config_file";
399 char theme[MAXLINELENGTH]="default";
400 int refresh_time=0; /* 0 = disabled */
401 int w3c_link=1; /* 0 = disabled */
402 char header[MAXLINELENGTH]="none";
403 char footer[MAXLINELENGTH]="none";
404 int totallines=0;
405 time_t debut;
406
407 struct
408 {
409   char nick[MAXNICKLENGTH];
410   int lines;
411   int letters;
412   int hours[4];
413   char quote[MAXQUOTELENGTH+1];
414   int counters[NBCOUNTERS];
415   int temp;
416 } users[MAXUSERS];
417 int nbusers=0;
418
419 struct
420 {
421   char nick[MAXNICKLENGTH];
422   char url[MAXLINELENGTH];
423   char shorturl[MAXQUOTELENGTH+1];
424 } urls[5];
425 int nburls=0;
426
427 struct
428
429   char nick[MAXNICKLENGTH];
430   char topic[MAXQUOTELENGTH+1];
431 } topics[5];
432 int nbtopics=0;
433
434 struct
435 {
436   int lines;
437   int hours[4];
438 } lastdays[31];
439 int days=0;
440
441 int hours[24];
442 int lines=0;
443
444 struct letter
445 {
446   int nb;
447   struct letter *next[26];
448 } words;
449
450 struct
451 {
452   int nb;
453   char word[MAXLINELENGTH];
454 } topwords[NBWORDS];
455
456 #define isletter(c) (((c>='a')&&(c<='z'))||((c>='A')&&(c<='Z')))
457 #define lowercase(c) (((c>='A')&&(c<='Z'))?c-'A'+'a':c)
458 void findwords(char *message)
459 {
460   int i,c;
461   struct letter *pos,*tmp;
462   for (;;)
463   {
464     while (!isletter(*message)) if (*message=='\0') return; else message++;
465     pos=&words;
466     while (isletter(*message))
467     {
468       c=lowercase(*message)-'a';
469       if (pos->next[(int)c]==NULL)
470       {
471         tmp=malloc(sizeof(struct letter));
472         tmp->nb=0;
473         for (i=0;i<26;i++) tmp->next[i]=NULL;
474         pos->next[(int)c]=tmp;
475       }
476       pos=pos->next[(int)c];
477       message++; 
478     }
479     pos->nb++;
480   }
481   return;
482 }
483
484 char tempword[MAXLINELENGTH];
485 void bestwords(struct letter pos,int cur)
486 {
487   int i,j;
488   if ((cur>=MINWORDLENGTH)&&(pos.nb>topwords[NBWORDS-1].nb))
489   {
490     for (i=0;pos.nb<topwords[i].nb;i++);
491     for (j=NBWORDS-1;j>i;j--)
492     {
493       topwords[j].nb=topwords[j-1].nb;
494       strcpy(topwords[j].word,topwords[j-1].word);
495     }
496     topwords[i].nb=pos.nb;
497     strcpy(topwords[i].word,tempword);
498   }
499   for (i=0;i<26;i++) if (pos.next[i]!=NULL)
500   {
501     tempword[cur]='a'+i;
502     bestwords(*(pos.next[i]),cur+1);
503   }
504   tempword[cur]='\0';
505 }
506
507 void freewords(struct letter *pos)
508 {
509   int i;
510   for (i=0;i<26;i++) if (pos->next[i]!=NULL)
511   {
512     freewords(pos->next[i]);
513     free(pos->next[i]);
514     (*pos).next[i]=NULL;
515   }
516 }
517
518 void printhtml(FILE *fic,char *string) /* replace < and > by &lt; and &gt; */
519 {
520   while (*string!='\0')
521   {
522     switch (*string)
523     {
524       case '<':fprintf(fic,"&lt;"); break;
525       case '>':fprintf(fic,"&gt;"); break;
526       case '&':fprintf(fic,"&amp;"); break;
527       default:fprintf(fic,"%c",*string); break;
528     }
529     string++;
530   }
531   return;
532 }
533
534 int dichotomic(char *nick)
535 {
536   int i,j,start=0,end=nbusers-1,middle;
537   while (start<=end)
538   {
539     middle=(start+end)/2;
540     if (strcmp(nick,users[middle].nick)>0) start=middle+1; else end=middle-1;
541   }
542   if (strcmp(nick,users[start].nick)!=0)
543   {
544     nbusers++;
545     if (nbusers>=MAXUSERS) { fprintf(stderr,"too many users\n"); exit(1); }
546     for (i=nbusers-1;i>start;i--)
547     {
548       strcpy(users[i].nick,users[i-1].nick);
549       users[i].lines=users[i-1].lines;
550       users[i].letters=users[i-1].letters;
551       for (j=0;j<4;j++) users[i].hours[j]=users[i-1].hours[j];
552       strcpy(users[i].quote,users[i-1].quote);
553       for (j=0;j<NBCOUNTERS;j++) users[i].counters[j]=users[i-1].counters[j];
554       users[i].temp=users[i-1].temp;
555     }
556     strcpy(users[start].nick,nick);
557     users[start].lines=0;
558     users[start].letters=0;
559     for (j=0;j<4;j++) users[start].hours[j]=0;
560     users[start].quote[0]='\0';
561     for (j=0;j<NBCOUNTERS;j++) users[start].counters[j]=0;
562     users[start].temp=0;
563   }
564   return(start);
565 }
566
567 void parse_log(char *logfile)
568 {
569   FILE *fic;
570   char line[MAXLINELENGTH];
571   int pos;
572   int i,j;
573   char *nick,*message;
574   int nickstart;
575   int mononick,monolines;
576   int temp,hour;
577
578   if ((fic=fopen(logfile,"rt"))==NULL) { fprintf(stderr,"can't open log file \"%s\"\n",logfile); exit(1); }
579   if (debug) printf("working on %s : ",channel);
580   while (fgets(line,MAXLINELENGTH,fic)!=NULL)
581   {
582     /* remove \n */
583     for (i=0;line[i]!=0;i++);
584     if (i>=MAXLINELENGTH-1) { fprintf(stderr,"line %d is too long\n",totallines); exit(1); }
585     line[i-1]='\0';
586     pos=0;
587     totallines++;
588     if (totallines%10000==0 && debug) { printf("."); fflush(stdout); }
589     if (strncmp("--- Day changed",line,15)==0) /* --- Day changed Wed May 01 2002 */
590     {
591       for (i=30;i>0;i--)
592       {
593         lastdays[i].lines=lastdays[i-1].lines;
594         for (j=0;j<4;j++) lastdays[i].hours[j]=lastdays[i-1].hours[j];
595       }
596       lastdays[0].lines=0;
597       for (j=0;j<4;j++) lastdays[0].hours[j]=0;
598       days++;
599     }
600     else if (strncmp("-!- mode/",&line[6],9)==0) /* 00:00 -!- mode/#channel [...] by (Nick, Nick2, )Nick3 */
601     {
602       for (i=strlen(line);line[i]!=' ';i--);
603       nick=&line[i+1];
604       users[dichotomic(nick)].counters[D_MODE]++;
605     }
606     else if (strncmp("-!-",&line[6],3)==0) /* 00:00 -!- Nick something... */
607     {
608       for (i=10;line[i]!=' ';i++);
609       line[i]='\0';
610       nick=&line[10];
611       message=&line[i+1];
612       if (strncmp("changed the topic of",message,20)==0) /* 00:00 -!- Nick changed the topic of #channel to: new topic */
613       {
614         users[dichotomic(nick)].counters[D_TOPIC]++;
615         for (i=21;message[i]!=':';i++);
616         message=&message[i+2];
617         nbtopics++;
618         if ((nbtopics<=NBTOPICS) || (rand()%(nbtopics/NBTOPICS)==0))
619         {
620           temp=nbtopics<=NBTOPICS?nbtopics-1:rand()%NBTOPICS;
621           strcpy(topics[temp].nick,nick);
622           strncpy(topics[temp].topic,message,MAXQUOTELENGTH);
623         }
624       }
625       else if (strncmp("was kicked from",message,15)==0) /* 00:00 -!- Nick was kicked from #channel by Nick [Reason] */
626       {
627         users[dichotomic(nick)].counters[D_KICKED]++;
628         for (i=16;message[i]!=' ';i++);
629         message=&message[i+4];
630         for (i=0;message[i]!=' ';i++);
631         message[i]='\0';
632         users[dichotomic(message)].counters[D_KICK]++;
633       }
634       else if (strncmp("is now known as",message,15)==0) /* 00:00 -!- Nick is now known as Nick */
635         users[dichotomic(nick)].counters[D_NICK]++;
636       else if (message[0]=='[') /* 00:00 -!- Nick [user@host] something... */
637       {
638         for (i=0;message[i]!=']';i++);
639         message=&message[i+2];
640         if (strncmp("has joined",message,10)==0) /* 00:00 -!- Nick [user@host] has joined #channel */
641           users[dichotomic(nick)].counters[D_JOIN]++;
642         else if (strncmp("has quit",message,8)==0); /* 00:00 -!- Nick [user@host] has quit [Reason] */
643         else if (strncmp("has left",message,8)==0); /* 00:00 -!- Nick [user@host] has left #channel [Reason] */
644         else;
645       }
646     }
647     else if ((line[6]=='<') || (line[7]=='*'))
648     {
649       line[2]='\0';
650       hour=atoi(line);
651       if (line[7]=='*') /* 00:00  * Nick the message */
652       {
653         for (i=9;line[i]!=' ';i++);
654         nick=&line[9];
655         message=&line[i+1];
656       }
657       else if (line[7]=='>') /* 00:00 <>>>?Nick<<<> the personal message */
658                              /* 00:00 <>>?Nick<<> the personal message */
659       {
660         for (i=10;line[i]!='<';i++);
661         nick=&line[10];
662         if (line[9]=='>') nick++;
663         message=&line[i+5];
664       }
665       else /* 00:00 <?Nick> the message */
666       {
667
668         /* 
669          * Irssi doesn't log channel mode with show_nickmode = OFF    
670          * the following covers op, half-op, voice and show_nickmode_empty                 
671          */
672         if (line[7]=='@' || line[7]=='%' || line[7]=='+' || line[7]==' ') {
673             nickstart = 8;
674         } else {
675             nickstart = 7;
676         }
677           
678         for (i=nickstart;line[i]!='>';i++);
679         nick=&line[nickstart];
680         message=&line[i+2];
681       }
682       line[i]='\0';
683       i=dichotomic(nick);
684       if (line[7]=='*') users[i].counters[D_ME]++;
685       if (i==mononick)
686       {
687         monolines++;
688         if (monolines==5) users[i].counters[D_MONOLOGUE]++;
689       }
690       else
691       {
692         mononick=i;
693         monolines=1;
694       }
695       j=strlen(message);
696       users[i].lines++;
697       users[i].letters+=j;
698       users[i].hours[hour/6]++;
699       lastdays[0].lines++;
700       lastdays[0].hours[hour/6]++;
701       lines++;
702       hours[hour]++;
703       if (message[j-1]=='?') users[i].counters[D_QUESTION]++;
704       else if (message[j-1]=='!') users[i].counters[D_EXCLAM]++;
705       else if ((message[j-3]==' ')&&(message[j-2]==':'))
706       {
707         if (message[j-1]==')') users[i].counters[D_SMILE]++;
708         else if (message[j-1]=='(') users[i].counters[D_FROWN]++;
709       }
710       if (rand()%users[i].lines==0) strncpy(users[i].quote,message,MAXQUOTELENGTH);
711       if (strncmp("http://",message,7)==0)
712       {
713         users[i].counters[D_URL]++;
714         for (i=0;(message[i]!=' ') && (i<strlen(message));i++);
715         message[i]='\0';
716         nburls++;
717         if ((nburls<=NBURLS) || (rand()%(nburls/NBURLS)==0))
718         {
719           temp=nburls<=NBURLS?nburls-1:rand()%NBURLS;
720           strcpy(urls[temp].nick,nick);
721           strcpy(urls[temp].url,message);
722           strncpy(urls[temp].shorturl,message,MAXQUOTELENGTH);
723         }
724       }
725       findwords(message);
726     }
727     pos=0;
728   }
729   fclose(fic);
730   if (debug) printf(" done\n");
731 }
732
733 void parse_nick(char *nickfile)
734 {
735   FILE *fic;
736   int i,j;
737   regex_t preg;
738   char line[MAXLINELENGTH];
739   int user;
740   
741   for (i=0;i<nbusers;i++) users[i].temp=users[i].lines;
742   if ((fic=fopen(nickfile,"rt"))==NULL) { fprintf(stderr,"can't open nick file \"%s\"\n",nickfile); exit(1); }
743   while (fscanf(fic,"%s",line)==1)
744   {
745     user=dichotomic(line);
746     fscanf(fic,"%s",line);
747     if (regcomp(&preg,line,0)!=0) { fprintf(stderr,"error in nick file"); exit(1); }
748     for (i=0;i<nbusers;i++) if ((i!=user) && (regexec(&preg,users[i].nick,0,0,0)==0) && (users[i].lines>=0))
749     {
750       if (users[i].temp>users[user].temp) /* for nick alias, keep the random quote of the most used nick */
751       {
752         strcpy(users[user].quote,users[i].quote);
753         users[user].temp=users[i].temp;
754       }
755       users[user].lines+=users[i].lines;
756       users[user].letters+=users[i].letters;
757       for (j=0;j<4;j++) users[user].hours[j]+=users[i].hours[j];
758       for (j=0;j<NBCOUNTERS;j++) users[user].counters[j]+=users[i].counters[j];
759       /* "remove" old user */
760       users[i].lines=-1;
761       users[i].letters=-1;
762       for (j=0;j<4;j++) users[i].hours[j]=-1;
763       for (j=0;j<NBCOUNTERS;j++) users[i].counters[j]=-1;
764     }
765     regfree(&preg);
766   }
767   fclose(fic);
768   /* "remove" the ignored nicks */
769   i=dichotomic("<NULL>");
770   users[i].lines=-1;
771   users[i].letters=-1;
772   for (j=0;j<4;j++) users[i].hours[j]=-1;
773   for (j=0;j<NBCOUNTERS;j++) users[i].counters[j]=-1;
774 }
775
776 void gen_xhtml(char *xhtmlfile)
777 {
778   FILE *fic;
779   FILE *sfic;
780   int i,j,k;
781   int user,max,temp;
782   char line[MAXLINELENGTH];
783   
784   if ((fic=fopen(xhtmlfile,"wt"))==NULL) { fprintf(stderr,"can't open xhtml file \"%s\"\n",xhtmlfile); exit(1); }
785   
786   /* header */
787   if (strcmp("none",header)==0)
788   {
789     fprintf(fic,"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n\n");
790     fprintf(fic,"<!-- Generated by irssistats %s : %s -->\n\n",VERSION,URL);
791     fprintf(fic,"<html>\n\n<head>\n<title>");
792     fprintf(fic,L("HEADER"),channel,maintainer);
793     fprintf(fic,"</title>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=iso-8859-1\" />\n");
794     if (refresh_time)
795       fprintf(fic,"<meta http-equiv=\"Refresh\" content=\"%d\" />\n",refresh_time);
796     fprintf(fic,"<link rel=\"stylesheet\" type=\"text/css\" href=\"%s.css\" />\n",theme);
797     fprintf(fic,"</head>\n\n");
798     fprintf(fic,"<body>\n\n");
799   }
800   else
801   {
802     if ((sfic=fopen(header,"rt"))==NULL) { fprintf(stderr,"can't open header file \"%s\"\n",header); exit(1); }
803     while ((temp=fread(line,1,MAXLINELENGTH,sfic))) fwrite(line,temp,1,fic);
804     fclose(sfic);
805   }
806   fprintf(fic,"<div id=\"irssistats\">\n\n<div id=\"irssistats_header\">\n<h1>");
807   fprintf(fic,L("HEADER"),channel,maintainer);
808   fprintf(fic,"</h1>\n<p>\n%s</p>\n</div>\n\n",ctime(&debut));
809
810   /* legend */
811   fprintf(fic,"<div id=\"irssistats_legend\">\n<h2>%s</h2>\n<table>\n<tr>\n",L("LEGEND"));
812   for (i=0;i<4;i++) fprintf(fic,"<td><div class=\"h%d\" style=\"width: 40px\"></div></td><td>%s %d-%d</td>\n",i+1,L("HOURS"),i*6,i*6+5);
813   fprintf(fic,"</tr>\n</table>\n</div>\n\n");
814   
815   /* last days */
816   fprintf(fic,"<div id=\"irssistats_lastdays\">\n<h2>%s</h2>\n<table>\n<tr>\n",L("LASTDAYS"));
817   max=-1;
818   for (i=30;i>=0;i--) if (lastdays[i].lines>max) max=lastdays[i].lines;
819   for (i=30;i>=0;i--)
820   {
821     fprintf(fic,"<td align=\"center\" valign=\"bottom\"><small>%d</small>",lastdays[i].lines); /* width=\"15\" */
822     for (j=0;j<4;j++) if (lastdays[i].hours[j]!=0) fprintf(fic,"<div class=\"v%d\" style=\"height:%dpx\"></div>",j+1,150*lastdays[i].hours[j]/max);
823     fprintf(fic,"</td>\n");
824   }
825   fprintf(fic,"</tr>\n<tr>\n");
826   for (i=30;i>=0;i--)
827     fprintf(fic,"<th>%d</th>\n",i);
828   fprintf(fic,"</tr>\n</table>\n</div>\n\n");
829   
830   /* top hours */
831   fprintf(fic,"<div id=\"irssistats_tophours\">\n<h2>%s</h2>\n<table>\n<tr>\n",L("TOPHOURS"));
832   max=-1;
833   for (i=0;i<24;i++) if (hours[i]>max) max=hours[i];
834   for (i=0;i<24;i++)
835   {
836     fprintf(fic,"<td align=\"center\" valign=\"bottom\"><small>%.1f%%</small>",lines!=0?(float)100*hours[i]/lines:0); /* width=\"15\" */
837     if (hours[i]!=0) fprintf(fic,"<div class=\"v%d\" style=\"height:%dpx\"></div>",i/6+1,150*hours[i]/max);
838     fprintf(fic,"</td>\n");
839   }
840   fprintf(fic,"</tr>\n<tr>\n");
841   for (i=0;i<24;i++)
842     fprintf(fic,"<th>%d</th>\n",i);
843   fprintf(fic,"</tr>\n</table>\n</div>\n\n");
844   
845   /* top users */
846   fprintf(fic,"<div id=\"irssistats_topusers\">\n<h2>%s</h2>\n",L("TOPUSERS"));
847   fprintf(fic,"<table>\n<tr><th></th><th>%s</th><th>%s</th><th>%s</th><th colspan=\"2\">%s</th><th>%s</th></tr>\n",L("NICK"),L("NBLINES"),L("HOURS"),L("AVGLETTERS"),L("QUOTE"));
848   for (i=1;i<=NBUSERS;i++)
849   {
850     user=-1;
851     max=0;
852     for (j=0;j<nbusers;j++) if (users[j].lines>max) max=users[user=j].lines;
853     if (user!=-1)
854     {
855       fprintf(fic,"<tr><td>%d</td><td>%s</td><td>%d</td><td class=\"oneline\">",i,users[user].nick,users[user].lines);
856       for (j=0;j<4;j++) if (users[user].hours[j]!=0) fprintf(fic,"<div class=\"h%d\" style=\"width:%dpx\"></div>",j+1,100*users[user].hours[j]/users[user].lines);
857       fprintf(fic,"</td><td>%d</td><td><div class=\"hm\" style=\"width:%dpx\"></div></td><td>\"",users[user].lines!=0?users[user].letters/users[user].lines:0,users[user].lines!=0?users[user].letters/users[user].lines:0);
858       printhtml(fic,users[user].quote);
859       fprintf(fic,"\"</td></tr>\n");
860       users[user].lines=-1;
861     }    
862   }
863   fprintf(fic,"</table>\n");
864   temp=0;
865   for (i=0;i<=nbusers;i++) if (users[i].lines>=0) temp++;
866   if (temp>0)
867   {
868     fprintf(fic,"<p>");
869     fprintf(fic,L("OTHERS"),temp);
870     fprintf(fic,"</p>\n");
871   }
872   fprintf(fic,"</div>\n\n");
873   
874   /* top users by time */
875   fprintf(fic,"<div id=\"irssistats_topuserstime\">\n<h2>%s</h2>\n",L("TOPUSERSTIME"));
876   fprintf(fic,"<table>\n<tr><th></th>");
877   for (i=0;i<4;i++) fprintf(fic,"<th colspan=\"2\">%s %d-%d</th>",L("HOURS"),i*6,i*6+5);
878   fprintf(fic,"</tr>\n");
879   for (i=1;i<=NBUSERSTIME;i++)
880   {
881     fprintf(fic,"<tr><td>%d</td>",i);
882     for (j=0;j<4;j++)
883     {
884       user=-1;
885       max=0;
886       for (k=0;k<nbusers;k++) if (users[k].hours[j]>max) max=users[user=k].hours[j];
887       if (user!=-1)
888       {
889         fprintf(fic,"<td>%s</td><td>%d</td>",users[user].nick,users[user].hours[j]);
890         users[user].hours[j]=-1;
891       }
892       else fprintf(fic,"<td></td><td></td>");
893     }
894     fprintf(fic,"</tr>\n");
895   }
896   fprintf(fic,"</table>\n</div>\n\n");
897
898   /* random topics */
899   fprintf(fic,"<div id=\"irssistats_randtopics\">\n<h2>%s</h2>\n",L("RANDTOPICS"));
900   fprintf(fic,"<table>\n<tr><th>%s</th><th>%s</th></tr>\n",L("CHANGEDBY"),L("NEWTOPIC"));
901   for (i=nbtopics<NBTOPICS?nbtopics-1:NBTOPICS-1;i>=0;i--)
902   {
903     fprintf(fic,"<tr><td>%s</td><td>\"",topics[i].nick);
904     printhtml(fic,topics[i].topic);
905     fprintf(fic,"\"</td></tr>\n");
906   }
907   fprintf(fic,"</table>\n</div>\n\n");
908   
909   /* random urls */
910   fprintf(fic,"<div id=\"irssistats_randurls\">\n<h2>%s</h2>\n",L("RANDURLS"));
911   fprintf(fic,"<table>\n<tr><th>%s</th><th>%s</th></tr>\n",L("POSTEDBY"),L("POSTEDURL"));
912   for (i=nburls<NBURLS?nburls-1:NBURLS-1;i>=0;i--)
913   {
914     fprintf(fic,"<tr><td>%s</td><td>\"<a href=\"",urls[i].nick);
915     printhtml(fic,urls[i].url);
916     fprintf(fic,"\">");
917     printhtml(fic,urls[i].shorturl);
918     fprintf(fic,"</a>\"</td></tr>\n");
919   }
920   fprintf(fic,"</table>\n</div>\n\n");
921   
922   /* top words */
923   fprintf(fic,"<div id=\"irssistats_topwords\">\n<h2>%s</h2>\n",L("TOPWORDS"));
924   fprintf(fic,"<table>\n<tr><th></th><th>%s</th><th>%s</th></tr>\n",L("WORD"),L("OCCURRENCES"));
925   for (i=0;i<NBWORDS;i++)
926     if (topwords[i].nb!=0) fprintf(fic,"<tr><td>%d</td><td>\"%s\"</td><td>%d</td></tr>\n",i+1,topwords[i].word,topwords[i].nb);
927   fprintf(fic,"</table>\n</div>\n\n");
928
929   /* big numbers */
930   fprintf(fic,"<div id=\"irssistats_bignumbers\">\n<h2>%s</h2>\n",L("BIGNUMBERS"));
931   fprintf(fic,"<table>\n<tr><th>%s</th><th>%s</th><th>%s</th></tr>\n",L("NICK"),L("NUMBERS"),L("NBLINES"));
932   for (i=0;i<NBCOUNTERS;i++)
933   {
934     user=-1;
935     max=0;
936     for (j=0;j<nbusers;j++) if (users[j].counters[i]>max) max=users[user=j].counters[i];
937     if (user!=-1) fprintf(fic,"<tr><td>%s</td><td>%s</td><td>%d</td></tr>",users[user].nick,L(counters[i]),users[user].counters[i]);
938   }
939   fprintf(fic,"</table>\n</div>\n\n");
940   
941   /* footer */
942   fprintf(fic,"<div id=\"irssistats_footer\">\n<p>");
943   fprintf(fic,L("TIME"),totallines,days,(int)(time(NULL)-debut));
944   fprintf(fic,"</p>\n<p>%s <a href=\"%s\">irssistats %s</a></p>\n",L("FOOTER"),URL,VERSION);
945   if (w3c_link)
946   {
947     fprintf(fic,"<p>\n<a href=\"http://validator.w3.org/check/referer\"><img src=\"valid-xhtml10.png\" height=\"31\" width=\"88\" alt=\"Valid XHTML 1.0!\" /></a>\n");
948     fprintf(fic,"<a href=\"http://jigsaw.w3.org/css-validator/check/referer\"><img src=\"valid-css.png\" height=\"31\" width=\"88\" alt=\"Valid CSS!\" /></a>\n</p>\n");
949   }
950   fprintf(fic,"</div>\n\n</div>");
951   if (strcmp("none",header)==0)
952   {
953     fprintf(fic,"\n\n</body>\n\n</html>\n");
954   }
955   else
956   {
957     if ((sfic=fopen(footer,"rt"))==NULL) { fprintf(stderr,"can't open footer file \"%s\"\n",footer); exit(1); }
958     while ((temp=fread(line,1,MAXLINELENGTH,sfic))) fwrite(line,temp,1,fic);
959     fclose(sfic);
960   }
961   
962   fclose(fic);
963 }
964
965 void parse_config(char *configfile)
966 {
967   FILE *fic;
968   char line[MAXLINELENGTH];
969   char keyword[MAXLINELENGTH];
970   char value[MAXLINELENGTH];
971   int configlines=0;
972   int i;
973   
974   if (configfile!=NULL)
975   {
976     if ((fic=fopen(configfile,"rt"))==NULL)
977     {
978       fprintf(stderr,"can't open config file : \"%s\"\n",configfile);
979       exit(1);
980     }
981   }
982   else
983   {
984     sprintf(line,"%s/.irssistats",getenv("HOME"));
985     if ((fic=fopen(line,"rt"))==NULL)
986       if ((fic=fopen("/etc/irssistats.conf","rt"))==NULL)
987       {
988         fprintf(stderr,"can't find config file : \"%s\" nor \"/etc/irssistats.conf\"\n",line);
989         fprintf(stderr,"please give the path to the config file in argument\n");
990         exit(1);
991       }
992   }
993
994   while (fgets(line,MAXLINELENGTH,fic))
995   {
996     configlines++;
997     if (*line!=';' && *line!='#' && *line!='/' && *line!='-' && *line!='\n')
998     {
999       if ((sscanf(line,"%s : %s\n",(char *)&keyword,(char *)&value))!=2) { fprintf(stderr,"error in config file : each line must have the format \"keyword : value\" (line %d)\n",configlines); exit(1); }
1000       
1001       if (strcmp("debug",keyword)==0)
1002       {
1003         if (strcmp("none",value)==0) debug=0;
1004         else
1005         if (strcmp("normal",value)==0) debug=1;
1006         else
1007         if (strcmp("verbose",value)==0) { debug=2; fprintf(stderr,"switching to verbose output\n"); }
1008         else { fprintf(stderr,"unknown value for \"debug\" option, must be \"normal\", \"verbose\" or \"none\"\n"); exit(1); }
1009       } else
1010       
1011       if (strcmp("channel",keyword)==0)
1012       {
1013         if (debug==2) fprintf(stderr,"setting channel name to \"%s\"\n",value);
1014         strcpy(channel,value);
1015       }
1016       else
1017       
1018       if (strcmp("maintainer",keyword)==0)
1019       {
1020         if (debug==2) fprintf(stderr,"setting maintainer to \"%s\"\n",value);
1021         strcpy(maintainer,value);
1022       }
1023       else
1024       
1025       if (strcmp("language",keyword)==0)
1026       {
1027         if (debug==2) fprintf(stderr,"setting language to \"%s\"\n",value);
1028         for (i=0;i<NBLANGUAGES;i++) if (strcmp(value,keys[i][0][1])==0) { language=i; break; }
1029         if (i==NBLANGUAGES)
1030         {
1031           fprintf(stderr,"Invalid language : %s\n",value);
1032           fprintf(stderr,"Supported languages :\n");
1033           for (i=0;i<NBLANGUAGES;i++) fprintf(stderr,"%s = %s\n",keys[i][0][1],keys[i][0][0]);
1034           exit(1);
1035         }
1036       }
1037       else
1038       
1039       if (strcmp("theme",keyword)==0)
1040       {
1041         if (debug==2) fprintf(stderr,"setting theme to \"%s\"\n",value);
1042         strcpy(theme,value);
1043       }
1044       else
1045       
1046       if (strcmp("refresh_time",keyword)==0)
1047       {
1048         refresh_time=atoi(value);
1049         if (debug==2) fprintf(stderr,"setting refresh_time to \"%d\"\n",refresh_time);
1050       }
1051       else
1052       
1053       if (strcmp("w3c_link",keyword)==0)
1054       {
1055         if (debug==2) fprintf(stderr,"setting w3c_link to \"%s\"\n",value);
1056         if (strcmp("no",value)==0) w3c_link=0;
1057         else if (strcmp("yes",value)==0) w3c_link=1;
1058         else { fprintf(stderr,"unknown value for \"w3c_link\" option, must be \"yes\" or \"no\"\n"); exit(1); }
1059       }
1060       else
1061       
1062       if (strcmp("header",keyword)==0)
1063       {
1064         if (debug==2) fprintf(stderr,"setting header to \"%s\"\n",value);
1065         strcpy(header,value);
1066       }
1067       else
1068       
1069       if (strcmp("footer",keyword)==0)
1070       {
1071         if (debug==2) fprintf(stderr,"setting footer to \"%s\"\n",value);
1072         strcpy(footer,value);
1073       }
1074       else
1075       
1076       if (strcmp("input",keyword)==0)
1077       {
1078         if (debug==2) fprintf(stderr,"parsing log file \"%s\"\n",value);
1079         parse_log(value);
1080       }
1081       else
1082       
1083       if (strcmp("nickfile",keyword)==0)
1084       {
1085         if (debug==2) fprintf(stderr,"nick alias using file \"%s\"\n",value);
1086         parse_nick(value);
1087       }
1088       else
1089       
1090       if (strcmp("output",keyword)==0)
1091       {
1092         if (debug==2) fprintf(stderr,"generating xhtml file \"%s\"\n",value);
1093         bestwords(words,0);
1094         gen_xhtml(value);
1095         
1096         /* reset variables */
1097         nbusers=0;
1098         nburls=0;
1099         nbtopics=0;
1100         days=0;
1101         for (i=0;i<24;i++) hours[i]=0;
1102         lines=0;
1103         freewords(&words);
1104         for (i=0;i<NBWORDS;i++) topwords[i].nb=0;
1105         totallines=0;
1106         debut=time(NULL);
1107       }
1108       
1109       else { fprintf(stderr,"error in config file : \"%s\" is an unknown keyword (line %d)\n",keyword,configlines); exit(1); }        
1110     }
1111   }
1112   fclose(fic);
1113 }
1114
1115 int main(int argc,char *argv[])
1116 {
1117   srand(debut=time(NULL));
1118   if (argc==1) parse_config(NULL);
1119   else if (argc==2) parse_config(argv[1]);
1120   else
1121   {
1122     fprintf(stderr,"Usage : %s [/path/to/file.conf]\n",argv[0]);
1123     fprintf(stderr,"Version : irssistats %s\n",VERSION);
1124     exit(1);
1125   }
1126   return(0);
1127 }