1 /* Usage: cat /path/to/file.log | ./irssistats \#channel maintainer language theme [nickfile] > /path/to/file.html */
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <time.h>
6 #include <string.h>
7 #include <regex.h>
9 /* Options */
10 #define REFRESH_TIME 3600
11 #define W3C_LINK
13 /* Config */
14 #define MAXUSERS 5000
15 #define MAXNICKLENGTH 50
16 #define MAXLINELENGTH 2000
17 #define MAXQUOTELENGTH 100
18 #define NBUSERS 50
19 #define NBUSERSTIME 10
20 #define NBURLS 5
21 #define NBTOPICS 5
22 #define NBWORDS 20
23 #define MINWORDLENGTH 5
25 /* irssistats */
26 #define VERSION "0.42"
27 #define URL "http://royale.zerezo.com/programmation/irssistats/"
29 /* Counters */
30 #define D_SMILE 0
31 #define D_FROWN 1
32 #define D_EXCLAM 2
33 #define D_QUESTION 3
34 #define D_ME 4
35 #define D_TOPIC 5
36 #define D_MODE 6
37 #define D_KICK 7
38 #define D_KICKED 8
39 #define D_URL 9
40 #define D_JOIN 10
41 #define D_NICK 11
42 #define D_MONOLOGUE 12
43 #define NBCOUNTERS 13
44 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"};
46 /* Languages */
47 #define NBLANGUAGES 5
48 #define NBKEYS 38
49 char *keys[NBLANGUAGES][NBKEYS+1][2]= /* first key used for language name and abbreviation */
50 {
51 { /* English language */
52 { "English", "en" },
53 { "HEADER", "Statistics for %s by %s" },
54 { "LEGEND", "Legend" },
55 { "LASTDAYS", "Lastdays statistics" },
56 { "TOPHOURS", "Hourly statistics" },
57 { "TOPUSERS", "Most active people" },
58 { "OTHERS", "There are %d left not ranked..." },
59 { "NBLINES", "lines" },
60 { "NICK", "nick" },
61 { "AVGLETTERS", "letters/lines" },
62 { "HOURS", "hours" },
63 { "QUOTE", "random message" },
64 { "TOPUSERSTIME", "Most active people by time of day" },
65 { "RANDTOPICS", "Some topics" },
66 { "CHANGEDBY", "changed by" },
67 { "NEWTOPIC", "new topic" },
68 { "RANDURLS", "Some URLs" },
69 { "POSTEDBY", "posted by" },
70 { "POSTEDURL", "URL" },
71 { "TOPWORDS", "Most used words" },
72 { "WORD", "word" },
73 { "OCCURRENCES", "occurrences" },
74 { "BIGNUMBERS", "Some big numbers..." },
75 { "NUMBERS", "numbers" },
76 { "TIME", "%d lines (%d days) parsed in %d seconds" },
77 { "FOOTER", "Statistics generated by" },
78 { "C_SMILE", "is often happy :)" },
79 { "C_FROWN", "is often sad :(" },
80 { "C_EXCLAM", "yells a lot !" },
81 { "C_QUESTION", "asks a lot of questions ?" },
82 { "C_ME", "likes /me command" },
83 { "C_TOPIC", "often changes the topic" },
84 { "C_MODE", "often changes the modes" },
85 { "C_KICK", "likes to /kick" },
86 { "C_KICKED", "is often kicked" },
87 { "C_URL", "posts many URLs" },
88 { "C_JOIN", "doesn't know wether to stay or quit" },
89 { "C_NICK", "often changes his nick" },
90 { "C_MONOLOGUE", "speaks a lot of monologues" }
91 },
92 { /* French language */
93 { "Français", "fr" },
94 { "HEADER", "Statistiques de %s par %s" },
95 { "LEGEND", "Légende" },
96 { "LASTDAYS", "Statistiques des derniers jours" },
97 { "TOPHOURS", "Statistiques horaires" },
98 { "TOPUSERS", "Personnes les plus actives" },
99 { "OTHERS", "Il reste %d personnes non classées..." },
100 { "NBLINES", "lignes" },
101 { "NICK", "nick" },
102 { "AVGLETTERS", "lettres/lignes" },
103 { "HOURS", "heures" },
104 { "QUOTE", "message aléatoire" },
105 { "TOPUSERSTIME", "Personnes les plus actives par période de la journée" },
106 { "RANDTOPICS", "Quelques topics" },
107 { "CHANGEDBY", "changé par" },
108 { "NEWTOPIC", "nouveau topic" },
109 { "RANDURLS", "Quelques URLs" },
110 { "POSTEDBY", "postée par" },
111 { "POSTEDURL", "URL" },
112 { "TOPWORDS", "Mots les plus utilisés" },
113 { "WORD", "mot" },
114 { "OCCURRENCES", "occurrences" },
115 { "BIGNUMBERS", "Quelques grands nombres..." },
116 { "NUMBERS", "nombres" },
117 { "TIME", "%d lignes (%d jours) traitées en %d secondes" },
118 { "FOOTER", "Statistiques générées par" },
119 { "C_SMILE", "est souvent heureux :)" },
120 { "C_FROWN", "est souvent triste :(" },
121 { "C_EXCLAM", "hurle beaucoup !" },
122 { "C_QUESTION", "pose beaucoup de questions ?" },
123 { "C_ME", "aime la commande /me" },
124 { "C_TOPIC", "change souvent le topic" },
125 { "C_MODE", "change souvent les modes" },
126 { "C_KICK", "aime la commande /kick" },
127 { "C_KICKED", "est souvent kické" },
128 { "C_URL", "poste beaucoup d'URLs" },
129 { "C_JOIN", "ne sait pas s'il doit rester ou partir" },
130 { "C_NICK", "change souvent de nick" },
131 { "C_MONOLOGUE", "parle beaucoup de monologues" }
132 },
133 { /* German language */
134 /* contributed by Valentin Gelhorn <valentin.gelhorn@web.de> */
135 { "German", "de" },
136 { "HEADER", "Statistiken für %s von %s" },
137 { "LEGEND", "Legende" },
138 { "LASTDAYS", "Statistik der letzten Tage" },
139 { "TOPHOURS", "Stündliche Statistik" },
140 { "TOPUSERS", "Die aktivsten Personen" },
141 { "OTHERS", "Es bleiben noch %d uneingetragene" },
142 { "NBLINES", "Zeilen" },
143 { "NICK", "Nick" },
144 { "AVGLETTERS", "Buchstaben/Zeile" },
145 { "HOURS", "Stunden" },
146 { "QUOTE", "Zufällig ausgewaehlte Zitate" },
147 { "TOPUSERSTIME", "Die aktivsten Personen zur bestimmten Tageszeit" },
148 { "RANDTOPICS", "Ein paar Topics" },
149 { "CHANGEDBY", "Gesetzt von" },
150 { "NEWTOPIC", "Neues topic" },
151 { "RANDURLS", "Ein paar URLs" },
152 { "POSTEDBY", "Geschrieben von" },
153 { "POSTEDURL", "URL" },
154 { "TOPWORDS", "Am häufigsten benutze Wörter" },
155 { "WORD", "Wort" },
156 { "OCCURRENCES", "Vorkommen" },
157 { "BIGNUMBERS", "Ein paar grosse Zahlen" },
158 { "NUMBERS", "Zahlen" },
159 { "TIME", "%d Zeilen (%d Tage) analysiert in %d Sekunden" },
160 { "FOOTER", "Statistiken wurden erstellt von" },
161 { "C_SMILE", "ist oft glüklich :)" },
162 { "C_FROWN", "ist oft traurig :(" },
163 { "C_EXCLAM", "schreit oft !" },
164 { "C_QUESTION", "stellt viele Fragen ?" },
165 { "C_ME", "mag /me'en" },
166 { "C_TOPIC", "aendert oft das Topico" },
167 { "C_MODE", "aendert oft die Modes" },
168 { "C_KICK", "mag /kick'en" },
169 { "C_KICKED", "wird oft gekickt"},
170 { "C_URL", "schreibt viele URLs"},
171 { "C_JOIN", "kann sich nicht entscheiden ob er bleiben oder gehen soll" },
172 { "C_NICK", "ändert oft seinen Nick" },
173 { "C_MONOLOGUE", "spricht oft Monologe" }
174 },
175 { /* Spanish language */
176 /* contributed by Alex <ainaker@gmx.net> */
177 { "Spanish", "es" },
178 { "HEADER", "Estadísticas de %s por %s" },
179 { "LEGEND", "Leyenda" },
180 { "LASTDAYS", "Estadísticas de los últimos días" },
181 { "TOPHOURS", "Estadísticas por horas" },
182 { "TOPUSERS", "Los que más escriben" },
183 { "OTHERS", "Hay %d más que no llegaron..." },
184 { "NBLINES", "líneas" },
185 { "NICK", "nick" },
186 { "AVGLETTERS", "letras por línea" },
187 { "HOURS", "horas" },
188 { "QUOTE", "Frase aleatoria" },
189 { "TOPUSERSTIME", "Los que más escriben según la hora" },
190 { "RANDTOPICS", "Algunos topics" },
191 { "CHANGEDBY", "Puestos por" },
192 { "NEWTOPIC", "topic" },
193 { "RANDURLS", "Algunas URLs" },
194 { "POSTEDBY", "puestas por" },
195 { "POSTEDURL", "URL" },
196 { "TOPWORDS", "Palabras más usadas" },
197 { "WORD", "Palabra" },
198 { "OCCURRENCES", "Frecuencia" },
199 { "BIGNUMBERS", "Algunos datos..." },
200 { "NUMBERS", "Número de veces" },
201 { "TIME", "%d lineas (%d días) procesadas en %d segundos" },
202 { "FOOTER", "Estadísticas generadas por" },
203 { "C_SMILE", "Suele estar felíz :)" },
204 { "C_FROWN", "Suele estar triste :(" },
205 { "C_EXCLAM", "Grita mucho !" },
206 { "C_QUESTION", "Hace muchas preguntas ?" },
207 { "C_ME", "Abusa del comando /me" },
208 { "C_TOPIC", "Suele cambiar el topic" },
209 { "C_MODE", "Cambia a veces los modos del canal" },
210 { "C_KICK", "Le gusta patear" },
211 { "C_KICKED", "Es pateado con frecuencia" },
212 { "C_URL", "Pone muchas URLs" },
213 { "C_JOIN", "No sabe si irse o quedarse" },
214 { "C_NICK", "Cambia mucho de nick" },
215 { "C_MONOLOGUE", "Habla solo" }
216 },
217 { /* Polish language */
218 /* contributed by Piotr Jarmuz <coreupper@yahoo.com> */
219 { "Polish", "pl" },
220 { "HEADER", "Statystyki dla %s przez %s" },
221 { "LEGEND", "Legenda" },
222 { "LASTDAYS", "Statystyki z ostatnich dni" },
223 { "TOPHOURS", "Statystyki godzinne" },
224 { "TOPUSERS", "Najaktywniejsi ludzie" },
225 { "OTHERS", "Zostalo jeszcze %d nie sklasyfikowanych..." },
226 { "NBLINES", "linie" },
227 { "NICK", "nick" },
228 { "AVGLETTERS", "litery/linie" },
229 { "HOURS", "godziny" },
230 { "QUOTE", "przypadkowa wiadomosc" },
231 { "TOPUSERSTIME", "Najaktywniejsi ludzie wedlug czasu dnia" },
232 { "RANDTOPICS", "Pare tematow" },
233 { "CHANGEDBY", "zmienione przez" },
234 { "NEWTOPIC", "nowy temat" },
235 { "RANDURLS", "Pare URL-i" },
236 { "POSTEDBY", "wyslane przez" },
237 { "POSTEDURL", "URL" },
238 { "TOPWORDS", "Najczestsze slowa" },
239 { "WORD", "slowo" },
240 { "OCCURRENCES", "wystapienia" },
241 { "BIGNUMBERS", "Pare wielkich liczb..." },
242 { "NUMBERS", "liczby" },
243 { "TIME", "%d linii (%d dni) sparsowanych w %d sekund" },
244 { "FOOTER", "Statystyki wygenerowane przez" },
245 { "C_SMILE", "jest czesto szczesliwy :)" },
246 { "C_FROWN", "jest czesto smutny :(" },
247 { "C_EXCLAM", "duzo krzyczy !" },
248 { "C_QUESTION", "zadaje duzo pytan ?" },
249 { "C_ME", "lubi /mnie polecenie" },
250 { "C_TOPIC", "czesto zmienia temat" },
251 { "C_MODE", "czesto zmienia tryb" },
252 { "C_KICK", "lubi /kopac" },
253 { "C_KICKED", "czesto go wykopuja" },
254 { "C_URL", "wysyla duzo URL-i" },
255 { "C_JOIN", "nie wie czy zostac czy wyjsc" },
256 { "C_NICK", "czesto zmienia swojego nicka" },
257 { "C_MONOLOGUE", "czesto mowi monologiem" }
258 }
259 };
261 int language;
263 char *L(char *key)
264 {
265 int i;
266 for (i=1;i<=NBKEYS;i++) if (strcmp(key,keys[language][i][0])==0) return(keys[language][i][1]);
267 fprintf(stderr,"unknown language key: %s\n",key);
268 return("");
269 }
271 /* Themes */
272 #define NBTHEMES 5
273 #define NBCOLORS 9
274 char *colors[NBTHEMES][NBCOLORS+1][2]= /* first key used for theme name/description and abbreviation */
275 {
276 { /* Default theme */
277 { "White background", "default" },
278 { "BGCOLOR", "#FFFFFF" },
279 { "TEXT", "#000000" },
280 { "LINK", "#0000EE" },
281 { "VLINK", "#551A8B" },
282 { "ALINK", "#FF0000" },
283 { "TITLE1", "#000000" },
284 { "TITLE2", "#000000" },
285 { "BGTABLE", "#EEEEEE" },
286 { "BGTITLE", "#FFEEEE" }
287 },
288 { /* Dark theme */
289 { "Black background", "dark" },
290 { "BGCOLOR", "#000000" },
291 { "TEXT", "#FFFFFF" },
292 { "LINK", "#AAAAFF" },
293 { "VLINK", "#CCCCDD" },
294 { "ALINK", "#FFAAAA" },
295 { "TITLE1", "#AAAAFF" },
296 { "TITLE2", "#FFAAAA" },
297 { "BGTABLE", "#225522" },
298 { "BGTITLE", "#552222" }
299 },
300 { /* zeRezo theme */
301 { "Green theme...", "zerezo" },
302 { "BGCOLOR", "#000000" },
303 { "TEXT", "#FFFFFF" },
304 { "LINK", "#14F024" },
305 { "VLINK", "#0CBA18" },
306 { "ALINK", "#FFFFFF" },
307 { "TITLE1", "#0CBA18" },
308 { "TITLE2", "#84DB8C" },
309 { "BGTABLE", "#085D10" },
310 { "BGTITLE", "#0B810B" }
311 },
312 { /* tit-namour theme */
313 { "Purple and Pink", "namour" },
314 { "BGCOLOR", "#9933CC" },
315 { "TEXT", "#DDAAFF" },
316 { "LINK", "#CC99FF" },
317 { "VLINK", "#999999" },
318 { "ALINK", "#FFC8C8" },
319 { "TITLE1", "#FFC8C8" },
320 { "TITLE2", "#FFC8C8" },
321 { "BGTABLE", "#7711AA" },
322 { "BGTITLE", "#550088" }
323 },
324 { /* zeDuel theme */
325 { "Orange theme...", "zeduel" },
326 { "BGCOLOR", "#FFFFFF" },
327 { "TEXT", "#000000" },
328 { "LINK", "#FF7700" },
329 { "VLINK", "#C05A00" },
330 { "ALINK", "#FF9A41" },
331 { "TITLE1", "#C05A00" },
332 { "TITLE2", "#FF7700" },
333 { "BGTABLE", "#FFEEEE" },
334 { "BGTITLE", "#FF7700" }
335 }
336 };
338 int theme;
340 char *T(char *color)
341 {
342 int i;
343 for (i=1;i<=NBCOLORS;i++) if (strcmp(color,colors[theme][i][0])==0) return(colors[theme][i][1]);
344 fprintf(stderr,"unknown theme color: %s\n",color);
345 return("");
346 }
348 /* Variables */
350 char *channel;
351 char *maintainer;
353 struct
354 {
355 char nick[MAXNICKLENGTH];
356 int lines;
357 int letters;
358 int hours[4];
359 char quote[MAXQUOTELENGTH+1];
360 int counters[NBCOUNTERS];
361 int temp;
362 } users[MAXUSERS];
363 int nbusers=0;
365 struct
366 {
367 char nick[MAXNICKLENGTH];
368 char url[MAXLINELENGTH];
369 char shorturl[MAXQUOTELENGTH+1];
370 } urls[5];
371 int nburls=0;
373 struct
374 {
375 char nick[MAXNICKLENGTH];
376 char topic[MAXQUOTELENGTH+1];
377 } topics[5];
378 int nbtopics=0;
380 struct
381 {
382 int lines;
383 int hours[4];
384 } lastdays[31];
385 int days=0;
387 int hours[24];
388 int lines=0;
390 struct letter
391 {
392 int nb;
393 struct letter *next[26];
394 } words;
396 struct
397 {
398 int nb;
399 char word[MAXLINELENGTH];
400 } topwords[NBWORDS];
402 #define isletter(c) (((c>='a')&&(c<='z'))||((c>='A')&&(c<='Z')))
403 #define lowercase(c) (((c>='A')&&(c<='Z'))?c-'A'+'a':c)
404 void findwords(char *message)
405 {
406 int i,c;
407 struct letter *pos,*tmp;
408 for (;;)
409 {
410 while (!isletter(*message)) if (*message=='\0') return; else message++;
411 pos=&words;
412 while (isletter(*message))
413 {
414 c=lowercase(*message)-'a';
415 if (pos->next[(int)c]==NULL)
416 {
417 tmp=malloc(sizeof(struct letter));
418 tmp->nb=0;
419 for (i=0;i<26;i++) tmp->next[i]=NULL;
420 pos->next[(int)c]=tmp;
421 }
422 pos=pos->next[(int)c];
423 message++;
424 }
425 pos->nb++;
426 }
427 return;
428 }
430 char tempword[MAXLINELENGTH];
431 void bestwords(struct letter pos,int cur)
432 {
433 int i,j;
434 if ((cur>=MINWORDLENGTH)&&(pos.nb>topwords[NBWORDS-1].nb))
435 {
436 for (i=0;pos.nb<topwords[i].nb;i++);
437 for (j=NBWORDS-1;j>i;j--)
438 {
439 topwords[j].nb=topwords[j-1].nb;
440 strcpy(topwords[j].word,topwords[j-1].word);
441 }
442 topwords[i].nb=pos.nb;
443 strcpy(topwords[i].word,tempword);
444 }
445 for (i=0;i<26;i++) if (pos.next[i]!=NULL)
446 {
447 tempword[cur]='a'+i;
448 bestwords(*(pos.next[i]),cur+1);
449 }
450 tempword[cur]='\0';
451 }
453 void printhtml(char *string) /* replace < and > by < and > */
454 {
455 while (*string!='\0')
456 {
457 switch (*string)
458 {
459 case '<':printf("<"); break;
460 case '>':printf(">"); break;
461 case '&':printf("&"); break;
462 default:printf("%c",*string); break;
463 }
464 string++;
465 }
466 return;
467 }
469 int dichotomic(char *nick)
470 {
471 int i,j,start=0,end=nbusers-1,middle;
472 while (start<=end)
473 {
474 middle=(start+end)/2;
475 if (strcmp(nick,users[middle].nick)>0) start=middle+1; else end=middle-1;
476 }
477 if (strcmp(nick,users[start].nick)!=0)
478 {
479 nbusers++;
480 if (nbusers>=MAXUSERS) { fprintf(stderr,"too many users\n"); exit(1); }
481 for (i=nbusers-1;i>start;i--)
482 {
483 strcpy(users[i].nick,users[i-1].nick);
484 users[i].lines=users[i-1].lines;
485 users[i].letters=users[i-1].letters;
486 for (j=0;j<4;j++) users[i].hours[j]=users[i-1].hours[j];
487 strcpy(users[i].quote,users[i-1].quote);
488 for (j=0;j<NBCOUNTERS;j++) users[i].counters[j]=users[i-1].counters[j];
489 users[i].temp=users[i-1].temp;
490 }
491 strcpy(users[start].nick,nick);
492 users[start].lines=0;
493 users[start].letters=0;
494 for (j=0;j<4;j++) users[start].hours[j]=0;
495 users[start].quote[0]='\0';
496 for (j=0;j<NBCOUNTERS;j++) users[start].counters[j]=0;
497 users[start].temp=0;
498 }
499 return(start);
500 }
502 int main(int argc,char *argv[])
503 {
504 int i,j,k;
505 int max,user,temp,hour;
506 int mononick,monolines;
507 time_t debut;
508 int totallines=0;
509 int pos=0;
510 char c;
511 char *nick,*message;
512 char line[MAXLINELENGTH];
513 FILE *fic;
514 regex_t preg;
516 /*** INIT ***/
518 if ((argc<5) || (argc>6))
519 {
520 fprintf(stderr,"Usage: cat /path/to/file.log | ./irssistats \\#channel maintainer language theme [nickfile] > /path/to/file.html\n\n");
521 fprintf(stderr,"Version :\nirssistats %s\n\n",VERSION);
522 fprintf(stderr,"Supported languages :\n");
523 for (i=0;i<NBLANGUAGES;i++) fprintf(stderr,"%s = %s\n",keys[i][0][1],keys[i][0][0]);
524 fprintf(stderr,"\nSupported themes :\n");
525 for (i=0;i<NBTHEMES;i++) fprintf(stderr,"%s = %s\n",colors[i][0][1],colors[i][0][0]);
526 return(1);
527 }
528 channel=argv[1];
529 maintainer=argv[2];
530 for (i=0;i<NBLANGUAGES;i++) if (strcmp(argv[3],keys[i][0][1])==0) { language=i; break; }
531 if (i==NBLANGUAGES)
532 {
533 fprintf(stderr,"Invalid language : %s\n",argv[3]);
534 return(1);
535 }
536 for (i=0;i<NBTHEMES;i++) if (strcmp(argv[4],colors[i][0][1])==0) { theme=i; break; }
537 if (i==NBTHEMES)
538 {
539 fprintf(stderr,"Invalid theme : %s\n",argv[4]);
540 return(1);
541 }
543 /*** LOG ***/
545 srand(debut=time(NULL));
546 fprintf(stderr,"working:");
547 while (!feof(stdin))
548 {
549 c=getchar();
550 line[pos++]=c;
551 if (pos>=MAXLINELENGTH) { fprintf(stderr,"line too long\n"); exit(1); }
552 if (c=='\n')
553 {
554 line[pos-1]='\0';
555 totallines++;
556 if (totallines%10000==0) { fprintf(stderr,"."); fflush(stdout); }
557 if (strncmp("--- Day changed",line,15)==0) /* --- Day changed Wed May 01 2002 */
558 {
559 for (i=30;i>0;i--)
560 {
561 lastdays[i].lines=lastdays[i-1].lines;
562 for (j=0;j<4;j++) lastdays[i].hours[j]=lastdays[i-1].hours[j];
563 }
564 lastdays[0].lines=0;
565 for (j=0;j<4;j++) lastdays[0].hours[j]=0;
566 days++;
567 }
568 else if (strncmp("-!- mode/",&line[6],9)==0) /* 00:00 -!- mode/#channel [...] by (Nick, Nick2, )Nick3 */
569 {
570 for (i=strlen(line);line[i]!=' ';i--);
571 nick=&line[i+1];
572 users[dichotomic(nick)].counters[D_MODE]++;
573 }
574 else if (strncmp("-!-",&line[6],3)==0) /* 00:00 -!- Nick something... */
575 {
576 for (i=10;line[i]!=' ';i++);
577 line[i]='\0';
578 nick=&line[10];
579 message=&line[i+1];
580 if (strncmp("changed the topic of",message,20)==0) /* 00:00 -!- Nick changed the topic of #channel to: new topic */
581 {
582 users[dichotomic(nick)].counters[D_TOPIC]++;
583 for (i=21;message[i]!=':';i++);
584 message=&message[i+2];
585 nbtopics++;
586 if ((nbtopics<=NBTOPICS) || (rand()%(nbtopics/NBTOPICS)==0))
587 {
588 temp=nbtopics<=NBTOPICS?nbtopics-1:rand()%NBTOPICS;
589 strcpy(topics[temp].nick,nick);
590 strncpy(topics[temp].topic,message,MAXQUOTELENGTH);
591 }
592 }
593 else if (strncmp("was kicked from",message,15)==0) /* 00:00 -!- Nick was kicked from #channel by Nick [Reason] */
594 {
595 users[dichotomic(nick)].counters[D_KICKED]++;
596 for (i=16;message[i]!=' ';i++);
597 message=&message[i+4];
598 for (i=0;message[i]!=' ';i++);
599 message[i]='\0';
600 users[dichotomic(message)].counters[D_KICK]++;
601 }
602 else if (strncmp("is now known as",message,15)==0) /* 00:00 -!- Nick is now known as Nick */
603 users[dichotomic(nick)].counters[D_NICK]++;
604 else if (message[0]=='[') /* 00:00 -!- Nick [user@host] something... */
605 {
606 for (i=0;message[i]!=']';i++);
607 message=&message[i+2];
608 if (strncmp("has joined",message,10)==0) /* 00:00 -!- Nick [user@host] has joined #channel */
609 users[dichotomic(nick)].counters[D_JOIN]++;
610 else if (strncmp("has quit",message,8)==0); /* 00:00 -!- Nick [user@host] has quit [Reason] */
611 else if (strncmp("has left",message,8)==0); /* 00:00 -!- Nick [user@host] has left #channel [Reason] */
612 else;
613 }
614 }
615 else if ((line[6]=='<') || (line[7]=='*'))
616 {
617 line[2]='\0';
618 hour=atoi(line);
619 if (line[7]=='*') /* 00:00 * Nick the message */
620 {
621 for (i=9;line[i]!=' ';i++);
622 nick=&line[9];
623 message=&line[i+1];
624 }
625 else if (line[7]=='>') /* 00:00 <>>>?Nick<<<> the personal message */
626 /* 00:00 <>>?Nick<<> the personal message */
627 {
628 for (i=10;line[i]!='<';i++);
629 nick=&line[10];
630 if (line[9]=='>') nick++;
631 message=&line[i+5];
632 }
633 else /* 00:00 <?Nick> the message */
634 {
635 for (i=8;line[i]!='>';i++);
636 nick=&line[8];
637 message=&line[i+2];
638 }
639 line[i]='\0';
640 i=dichotomic(nick);
641 if (line[7]=='*') users[i].counters[D_ME]++;
642 if (i==mononick)
643 {
644 monolines++;
645 if (monolines==5) users[i].counters[D_MONOLOGUE]++;
646 }
647 else
648 {
649 mononick=i;
650 monolines=1;
651 }
652 j=strlen(message);
653 users[i].lines++;
654 users[i].letters+=j;
655 users[i].hours[hour/6]++;
656 lastdays[0].lines++;
657 lastdays[0].hours[hour/6]++;
658 lines++;
659 hours[hour]++;
660 if (message[j-1]=='?') users[i].counters[D_QUESTION]++;
661 else if (message[j-1]=='!') users[i].counters[D_EXCLAM]++;
662 else if ((message[j-3]==' ')&&(message[j-2]==':'))
663 {
664 if (message[j-1]==')') users[i].counters[D_SMILE]++;
665 else if (message[j-1]=='(') users[i].counters[D_FROWN]++;
666 }
667 if (rand()%users[i].lines==0) strncpy(users[i].quote,message,MAXQUOTELENGTH);
668 if (strncmp("http://",message,7)==0)
669 {
670 users[i].counters[D_URL]++;
671 for (i=0;(message[i]!=' ') && (i<strlen(message));i++);
672 message[i]='\0';
673 nburls++;
674 if ((nburls<=NBURLS) || (rand()%(nburls/NBURLS)==0))
675 {
676 temp=nburls<=NBURLS?nburls-1:rand()%NBURLS;
677 strcpy(urls[temp].nick,nick);
678 strcpy(urls[temp].url,message);
679 strncpy(urls[temp].shorturl,message,MAXQUOTELENGTH);
680 }
681 }
682 findwords(message);
683 }
684 pos=0;
685 }
686 }
687 fprintf(stderr,"done\n");
689 bestwords(words,0);
691 /*** ALIAS ***/
693 if (argc==6)
694 {
695 for (i=0;i<nbusers;i++) users[i].temp=users[i].lines;
696 if ((fic=fopen(argv[5],"rt"))==NULL) { fprintf(stderr,"can't open nick file\n"); exit(1); }
697 while (fscanf(fic,"%s",line)==1)
698 {
699 user=dichotomic(line);
700 fscanf(fic,"%s",line);
701 if (regcomp(&preg,line,0)!=0) { fprintf(stderr,"error in nick file"); exit(1); }
702 for (i=0;i<nbusers;i++) if ((i!=user) && (regexec(&preg,users[i].nick,0,0,0)==0) && (users[i].lines>=0))
703 {
704 if (users[i].temp>users[user].temp) /* for nick alias, keep the random quote of the most used nick */
705 {
706 strcpy(users[user].quote,users[i].quote);
707 users[user].temp=users[i].temp;
708 }
709 users[user].lines+=users[i].lines;
710 users[user].letters+=users[i].letters;
711 for (j=0;j<4;j++) users[user].hours[j]+=users[i].hours[j];
712 for (j=0;j<NBCOUNTERS;j++) users[user].counters[j]+=users[i].counters[j];
713 /* "remove" old user */
714 users[i].lines=-1;
715 users[i].letters=-1;
716 for (j=0;j<4;j++) users[i].hours[j]=-1;
717 for (j=0;j<NBCOUNTERS;j++) users[i].counters[j]=-1;
718 }
719 regfree(&preg);
720 }
721 fclose(fic);
722 /* "remove" the ignored nicks */
723 i=dichotomic("<NULL>");
724 users[i].lines=-1;
725 users[i].letters=-1;
726 for (j=0;j<4;j++) users[i].hours[j]=-1;
727 for (j=0;j<NBCOUNTERS;j++) users[i].counters[j]=-1;
728 }
730 /*** HTML ***/
732 /* header */
733 printf("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n\n");
734 printf("<!-- Generated by irssistats %s : %s -->\n\n",VERSION,URL);
735 printf("<html>\n\n<head>\n<title>");
736 printf(L("HEADER"),channel,maintainer);
737 printf("</title>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=iso-8859-1\">\n");
738 #ifdef REFRESH_TIME
739 printf("<meta http-equiv=\"Refresh\" content=\"%d\">\n",REFRESH_TIME);
740 #endif
741 printf("<base target=\"_blank\">\n</head>\n\n");
742 printf("<body bgcolor=\"%s\" text=\"%s\" link=\"%s\" vlink=\"%s\" alink=\"%s\">\n\n<center>\n\n<font color=\"%s\" size=\"+3\"><b>",T("BGCOLOR"),T("TEXT"),T("LINK"),T("VLINK"),T("ALINK"),T("TITLE1"));
743 printf(L("HEADER"),channel,maintainer);
744 printf("</b></font><br><br>\n%s<br>\n<br><br>\n\n",ctime(&debut));
746 /* legend */
747 printf("<font color=\"%s\" size=\"+1\"><b>%s</b></font><br><br>\n<table bgcolor=\"%s\">\n<tr>\n",T("TITLE2"),L("LEGEND"),T("BGTABLE"));
748 for (i=0;i<4;i++) printf("<td><img src=\"h%d.png\" width=\"40\" height=\"15\" alt=\"\"></td><td> : %s %d-%d</td><td width=\"10\"></td>\n",i+1,L("HOURS"),i*6,i*6+5);
749 printf("</tr>\n</table>\n<br><br>\n\n");
751 /* last days */
752 printf("<font color=\"%s\" size=\"+1\"><b>%s</b></font><br><br>\n<table>\n<tr>\n",T("TITLE2"),L("LASTDAYS"));
753 max=-1;
754 for (i=30;i>=0;i--) if (lastdays[i].lines>max) max=lastdays[i].lines;
755 for (i=30;i>=0;i--)
756 {
757 printf("<td width=\"15\" align=\"center\" valign=\"bottom\"><font size=\"-2\">%d</font><br>",lastdays[i].lines);
758 for (j=0;j<4;j++) if (lastdays[i].hours[j]!=0) printf("<img src=\"v%d.png\" width=\"15\" height=\"%d\" alt=\"\"><br>",j+1,150*lastdays[i].hours[j]/max);
759 printf("</td>\n");
760 }
761 printf("</tr>\n<tr>\n");
762 for (i=30;i>=0;i--)
763 printf("<td align=\"center\" valign=\"middle\" bgcolor=\"%s\">%d</td>\n",T("BGTABLE"),i);
764 printf("</tr>\n</table>\n<br><br>\n\n");
766 /* top hours */
767 printf("<font color=\"%s\" size=\"+1\"><b>%s</b></font><br><br>\n<table>\n<tr>\n",T("TITLE2"),L("TOPHOURS"));
768 max=-1;
769 for (i=0;i<24;i++) if (hours[i]>max) max=hours[i];
770 for (i=0;i<24;i++)
771 {
772 printf("<td width=\"15\" align=\"center\" valign=\"bottom\"><font size=\"-2\">%.1f%%</font><br>",lines!=0?(float)100*hours[i]/lines:0);
773 if (hours[i]!=0) printf("<img src=\"v%d.png\" width=\"15\" height=\"%d\" alt=\"\"><br>",i/6+1,150*hours[i]/max);
774 printf("</td>\n");
775 }
776 printf("</tr>\n<tr>\n");
777 for (i=0;i<24;i++)
778 printf("<td align=\"center\" valign=\"middle\" bgcolor=\"%s\">%d</td>\n",T("BGTABLE"),i);
779 printf("</tr>\n</table>\n<br><br>\n\n");
781 /* top users */
782 printf("<font color=\"%s\" size=\"+1\"><b>%s</b></font><br><br>\n",T("TITLE2"),L("TOPUSERS"));
783 printf("<table>\n<tr><td></td><td bgcolor=\"%s\">%s</td><td bgcolor=\"%s\">%s</td><td bgcolor=\"%s\">%s</td><td bgcolor=\"%s\" colspan=\"2\">%s</td><td bgcolor=\"%s\">%s</td>\n",T("BGTITLE"),L("NICK"),T("BGTITLE"),L("NBLINES"),T("BGTITLE"),L("HOURS"),T("BGTITLE"),L("AVGLETTERS"),T("BGTITLE"),L("QUOTE"));
784 for (i=1;i<=NBUSERS;i++)
785 {
786 user=-1;
787 max=0;
788 for (j=0;j<nbusers;j++) if (users[j].lines>max) max=users[user=j].lines;
789 if (user!=-1)
790 {
791 printf("<tr><td bgcolor=\"%s\" align=\"right\">%d</td><td bgcolor=\"%s\">%s</td><td bgcolor=\"%s\">%d</td><td bgcolor=\"%s\">",T("BGTABLE"),i,T("BGTABLE"),users[user].nick,T("BGTABLE"),users[user].lines,T("BGTABLE"));
792 for (j=0;j<4;j++) if (users[user].hours[j]!=0) printf("<img src=\"h%d.png\" width=\"%d\" height=\"15\" alt=\"\">",j+1,100*users[user].hours[j]/users[user].lines);
793 printf("</td><td bgcolor=\"%s\">%d</td><td bgcolor=\"%s\"><img src=\"hm.png\" width=\"%d\" height=\"15\" alt=\"\"></td><td bgcolor=\"%s\">\"",T("BGTABLE"),users[user].lines!=0?users[user].letters/users[user].lines:0,T("BGTABLE"),users[user].lines!=0?users[user].letters/users[user].lines:0,T("BGTABLE"));
794 printhtml(users[user].quote);
795 printf("\"</td></tr>\n");
796 users[user].lines=-1;
797 }
798 }
799 printf("</table>\n");
800 temp=0;
801 for (i=0;i<=nbusers;i++) if (users[i].lines>=0) temp++;
802 if (temp>0)
803 {
804 printf("<br>");
805 printf(L("OTHERS"),temp);
806 printf("<br>\n");
807 }
808 printf("<br><br>\n\n");
810 /* top users by time */
811 printf("<font color=\"%s\" size=\"+1\"><b>%s</b></font><br><br>\n",T("TITLE2"),L("TOPUSERSTIME"));
812 printf("<table>\n<tr><td></td>");
813 for (i=0;i<4;i++) printf("<td bgcolor=\"%s\" colspan=\"2\">%s %d-%d</td>",T("BGTITLE"),L("HOURS"),i*6,i*6+5);
814 printf("</tr>\n");
815 for (i=1;i<=NBUSERSTIME;i++)
816 {
817 printf("<tr><td bgcolor=\"%s\" align=\"right\">%d</td>",T("BGTABLE"),i);
818 for (j=0;j<4;j++)
819 {
820 user=-1;
821 max=0;
822 for (k=0;k<nbusers;k++) if (users[k].hours[j]>max) max=users[user=k].hours[j];
823 if (user!=-1)
824 {
825 printf("<td bgcolor=\"%s\">%s</td><td bgcolor=\"%s\">%d</td>",T("BGTABLE"),users[user].nick,T("BGTABLE"),users[user].hours[j]);
826 users[user].hours[j]=-1;
827 }
828 else printf("<td></td><td></td>");
829 }
830 printf("</tr>\n");
831 }
832 printf("</table>\n<br><br>\n\n");
834 /* random topics */
835 printf("<font color=\"%s\" size=\"+1\"><b>%s</b></font><br><br>\n",T("TITLE2"),L("RANDTOPICS"));
836 printf("<table>\n<tr><td bgcolor=\"%s\">%s</td><td bgcolor=\"%s\">%s</td></tr>\n",T("BGTITLE"),L("CHANGEDBY"),T("BGTITLE"),L("NEWTOPIC"));
837 for (i=nbtopics<NBTOPICS?nbtopics-1:NBTOPICS-1;i>=0;i--)
838 {
839 printf("<tr><td bgcolor=\"%s\">%s</td><td bgcolor=\"%s\">\"",T("BGTABLE"),topics[i].nick,T("BGTABLE"));
840 printhtml(topics[i].topic);
841 printf("\"</td></tr>\n");
842 }
843 printf("</table>\n<br><br>\n\n");
845 /* random urls */
846 printf("<font color=\"%s\" size=\"+1\"><b>%s</b></font><br><br>\n",T("TITLE2"),L("RANDURLS"));
847 printf("<table>\n<tr><td bgcolor=\"%s\">%s</td><td bgcolor=\"%s\">%s</td></tr>\n",T("BGTITLE"),L("POSTEDBY"),T("BGTITLE"),L("POSTEDURL"));
848 for (i=nburls<NBURLS?nburls-1:NBURLS-1;i>=0;i--)
849 {
850 printf("<tr><td bgcolor=\"%s\">%s</td><td bgcolor=\"%s\">\"<a href=\"",T("BGTABLE"),urls[i].nick,T("BGTABLE"));
851 printhtml(urls[i].url);
852 printf("\">");
853 printhtml(urls[i].shorturl);
854 printf("</a>\"</td></tr>\n");
855 }
856 printf("</table>\n<br><br>\n\n");
858 /* top words */
859 printf("<font color=\"%s\" size=\"+1\"><b>%s</b></font><br><br>\n",T("TITLE2"),L("TOPWORDS"));
860 printf("<table>\n<tr><td></td><td bgcolor=\"%s\">%s</td><td bgcolor=\"%s\">%s</td></tr>\n",T("BGTITLE"),L("WORD"),T("BGTITLE"),L("OCCURRENCES"));
861 for (i=0;i<NBWORDS;i++)
862 if (topwords[i].nb!=0) printf("<tr><td bgcolor=\"%s\" align=\"right\">%d</td><td bgcolor=\"%s\">\"%s\"</td><td bgcolor=\"%s\">%d</td></tr>\n",T("BGTABLE"),i+1,T("BGTABLE"),topwords[i].word,T("BGTABLE"),topwords[i].nb);
863 printf("</table>\n<br><br>\n\n");
865 /* big numbers */
866 printf("<font color=\"%s\" size=\"+1\"><b>%s</b></font><br><br>\n",T("TITLE2"),L("BIGNUMBERS"));
867 printf("<table>\n<tr><td bgcolor=\"%s\">%s</td><td bgcolor=\"%s\">%s</td><td bgcolor=\"%s\">%s</td></tr>\n",T("BGTITLE"),L("NICK"),T("BGTITLE"),L("NUMBERS"),T("BGTITLE"),L("NBLINES"));
868 for (i=0;i<NBCOUNTERS;i++)
869 {
870 user=-1;
871 max=0;
872 for (j=0;j<nbusers;j++) if (users[j].counters[i]>max) max=users[user=j].counters[i];
873 if (user!=-1) printf("<tr><td bgcolor=\"%s\">%s</td><td bgcolor=\"%s\">%s</td><td bgcolor=\"%s\">%d</td></tr>",T("BGTABLE"),users[user].nick,T("BGTABLE"),L(counters[i]),T("BGTABLE"),users[user].counters[i]);
874 }
875 printf("</table>\n<br><br>\n\n");
877 /* footer */
878 printf(L("TIME"),totallines,days,(int)(time(NULL)-debut));
879 printf("<br>\n%s <a href=\"%s\">irssistats %s</a>",L("FOOTER"),URL,VERSION);
880 #ifdef W3C_LINK
881 printf("<br><br>\n\n<a href=\"http://validator.w3.org/check/referer\"><img src=\"valid-html401.gif\" border=\"0\" height=\"31\" width=\"88\"alt=\"Valid HTML 4.01!\"></a>");
882 #endif
883 printf("\n\n</center>\n\n</body>\n\n</html>\n");
885 return(0);
886 }