2 * irssistats version 0.72
4 * This tool generates IRC stats based on irssi logs.
5 * Usage: irssistats [/path/to/file.conf]
7 * Copyright (C) 2002-2004 Antoine Jacquet <royale@zerezo.com>
8 * http://royale.zerezo.com/irssistats/
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 #include <sys/types.h>
33 #define GLOBALCONF "irssistats.conf"
35 #define GLOBALCONF "/etc/irssistats.conf"
40 #define BASEUSERS 1000
41 #define MAXNICKLENGTH 50
42 #define MAXLINELENGTH 2000
43 #define MAXQUOTELENGTH 100
45 #define NBUSERSTIME 10
49 #define MINWORDLENGTH 5
52 #define VERSION "0.72"
53 #define URL "http://royale.zerezo.com/irssistats/"
68 #define D_MONOLOGUE 12
70 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"};
73 #define NBLANGUAGES 11
75 char *keys[NBLANGUAGES][NBKEYS+1][2]= /* first key used for language name and abbreviation */
77 { /* English language */
79 { "CHARSET", "ISO-8859-1" },
80 { "HEADER", "Statistics for %s by %s" },
81 { "LEGEND", "Legend" },
82 { "LASTDAYS", "Lastdays statistics" },
83 { "TOPHOURS", "Hourly statistics" },
84 { "TOPUSERS", "Most active people" },
85 { "OTHERS", "There are %d left not ranked..." },
86 { "NBLINES", "lines" },
88 { "AVGLETTERS", "letters/lines" },
90 { "QUOTE", "random message" },
91 { "TOPUSERSTIME", "Most active people by time of day" },
92 { "RANDTOPICS", "Some topics" },
93 { "CHANGEDBY", "changed by" },
94 { "NEWTOPIC", "new topic" },
95 { "RANDURLS", "Some URLs" },
96 { "POSTEDBY", "posted by" },
97 { "POSTEDURL", "URL" },
98 { "TOPWORDS", "Most used words" },
100 { "OCCURRENCES", "occurrences" },
101 { "BIGNUMBERS", "Some big numbers..." },
102 { "NUMBERS", "numbers" },
103 { "TIME", "%d lines (%d days) parsed in %d seconds" },
104 { "FOOTER", "Statistics generated by" },
105 { "C_SMILE", "is often happy :)" },
106 { "C_FROWN", "is often sad :(" },
107 { "C_EXCLAM", "yells a lot !" },
108 { "C_QUESTION", "asks a lot of questions ?" },
109 { "C_ME", "likes /me command" },
110 { "C_TOPIC", "often changes the topic" },
111 { "C_MODE", "often changes the modes" },
112 { "C_KICK", "likes to /kick" },
113 { "C_KICKED", "is often kicked" },
114 { "C_URL", "posts many URLs" },
115 { "C_JOIN", "doesn't know wether to stay or quit" },
116 { "C_NICK", "often changes his nick" },
117 { "C_MONOLOGUE", "speaks a lot of monologues" }
119 { /* French language */
121 { "CHARSET", "ISO-8859-1" },
122 { "HEADER", "Statistiques de %s par %s" },
123 { "LEGEND", "Légende" },
124 { "LASTDAYS", "Statistiques des derniers jours" },
125 { "TOPHOURS", "Statistiques horaires" },
126 { "TOPUSERS", "Personnes les plus actives" },
127 { "OTHERS", "Il reste %d personnes non classées..." },
128 { "NBLINES", "lignes" },
130 { "AVGLETTERS", "lettres/lignes" },
131 { "HOURS", "heures" },
132 { "QUOTE", "message aléatoire" },
133 { "TOPUSERSTIME", "Personnes les plus actives par période de la journée" },
134 { "RANDTOPICS", "Quelques topics" },
135 { "CHANGEDBY", "changé par" },
136 { "NEWTOPIC", "nouveau topic" },
137 { "RANDURLS", "Quelques URLs" },
138 { "POSTEDBY", "postée par" },
139 { "POSTEDURL", "URL" },
140 { "TOPWORDS", "Mots les plus utilisés" },
142 { "OCCURRENCES", "occurrences" },
143 { "BIGNUMBERS", "Quelques grands nombres..." },
144 { "NUMBERS", "nombres" },
145 { "TIME", "%d lignes (%d jours) traitées en %d secondes" },
146 { "FOOTER", "Statistiques générées par" },
147 { "C_SMILE", "est souvent heureux :)" },
148 { "C_FROWN", "est souvent triste :(" },
149 { "C_EXCLAM", "hurle beaucoup !" },
150 { "C_QUESTION", "pose beaucoup de questions ?" },
151 { "C_ME", "aime la commande /me" },
152 { "C_TOPIC", "change souvent le topic" },
153 { "C_MODE", "change souvent les modes" },
154 { "C_KICK", "aime la commande /kick" },
155 { "C_KICKED", "est souvent kické" },
156 { "C_URL", "poste beaucoup d'URLs" },
157 { "C_JOIN", "ne sait pas s'il doit rester ou partir" },
158 { "C_NICK", "change souvent de nick" },
159 { "C_MONOLOGUE", "parle beaucoup de monologues" }
161 { /* German language */
162 /* contributed by Valentin Gelhorn <valentin.gelhorn@web.de> */
164 { "CHARSET", "ISO-8859-1" },
165 { "HEADER", "Statistiken für %s von %s" },
166 { "LEGEND", "Legende" },
167 { "LASTDAYS", "Statistik der letzten Tage" },
168 { "TOPHOURS", "Stündliche Statistik" },
169 { "TOPUSERS", "Die aktivsten Personen" },
170 { "OTHERS", "Es bleiben noch %d uneingetragene" },
171 { "NBLINES", "Zeilen" },
173 { "AVGLETTERS", "Buchstaben/Zeile" },
174 { "HOURS", "Stunden" },
175 { "QUOTE", "Zufällig ausgewaehlte Zitate" },
176 { "TOPUSERSTIME", "Die aktivsten Personen zur bestimmten Tageszeit" },
177 { "RANDTOPICS", "Ein paar Topics" },
178 { "CHANGEDBY", "Gesetzt von" },
179 { "NEWTOPIC", "Neues topic" },
180 { "RANDURLS", "Ein paar URLs" },
181 { "POSTEDBY", "Geschrieben von" },
182 { "POSTEDURL", "URL" },
183 { "TOPWORDS", "Am häufigsten benutze Wörter" },
185 { "OCCURRENCES", "Vorkommen" },
186 { "BIGNUMBERS", "Ein paar grosse Zahlen" },
187 { "NUMBERS", "Zahlen" },
188 { "TIME", "%d Zeilen (%d Tage) analysiert in %d Sekunden" },
189 { "FOOTER", "Statistiken wurden erstellt von" },
190 { "C_SMILE", "ist oft glüklich :)" },
191 { "C_FROWN", "ist oft traurig :(" },
192 { "C_EXCLAM", "schreit oft !" },
193 { "C_QUESTION", "stellt viele Fragen ?" },
194 { "C_ME", "mag /me'en" },
195 { "C_TOPIC", "aendert oft das Topico" },
196 { "C_MODE", "aendert oft die Modes" },
197 { "C_KICK", "mag /kick'en" },
198 { "C_KICKED", "wird oft gekickt"},
199 { "C_URL", "schreibt viele URLs"},
200 { "C_JOIN", "kann sich nicht entscheiden ob er bleiben oder gehen soll" },
201 { "C_NICK", "ändert oft seinen Nick" },
202 { "C_MONOLOGUE", "spricht oft Monologe" }
204 { /* Spanish language */
205 /* contributed by Alex <ainaker@gmx.net> */
207 { "CHARSET", "ISO-8859-1" },
208 { "HEADER", "Estadísticas de %s por %s" },
209 { "LEGEND", "Leyenda" },
210 { "LASTDAYS", "Estadísticas de los últimos días" },
211 { "TOPHOURS", "Estadísticas por horas" },
212 { "TOPUSERS", "Los que más escriben" },
213 { "OTHERS", "Hay %d más que no llegaron..." },
214 { "NBLINES", "líneas" },
216 { "AVGLETTERS", "letras por línea" },
217 { "HOURS", "horas" },
218 { "QUOTE", "Frase aleatoria" },
219 { "TOPUSERSTIME", "Los que más escriben según la hora" },
220 { "RANDTOPICS", "Algunos topics" },
221 { "CHANGEDBY", "Puestos por" },
222 { "NEWTOPIC", "topic" },
223 { "RANDURLS", "Algunas URLs" },
224 { "POSTEDBY", "puestas por" },
225 { "POSTEDURL", "URL" },
226 { "TOPWORDS", "Palabras más usadas" },
227 { "WORD", "Palabra" },
228 { "OCCURRENCES", "Frecuencia" },
229 { "BIGNUMBERS", "Algunos datos..." },
230 { "NUMBERS", "Número de veces" },
231 { "TIME", "%d lineas (%d días) procesadas en %d segundos" },
232 { "FOOTER", "Estadísticas generadas por" },
233 { "C_SMILE", "Suele estar felíz :)" },
234 { "C_FROWN", "Suele estar triste :(" },
235 { "C_EXCLAM", "Grita mucho !" },
236 { "C_QUESTION", "Hace muchas preguntas ?" },
237 { "C_ME", "Abusa del comando /me" },
238 { "C_TOPIC", "Suele cambiar el topic" },
239 { "C_MODE", "Cambia a veces los modos del canal" },
240 { "C_KICK", "Le gusta patear" },
241 { "C_KICKED", "Es pateado con frecuencia" },
242 { "C_URL", "Pone muchas URLs" },
243 { "C_JOIN", "No sabe si irse o quedarse" },
244 { "C_NICK", "Cambia mucho de nick" },
245 { "C_MONOLOGUE", "Habla solo" }
247 { /* Polish language */
248 /* contributed by Jakub Jankowski <shasta@atn.pl> */
250 { "CHARSET", "ISO-8859-2" },
251 { "HEADER", "Statystyki dla %s zebrane przez %s" },
252 { "LEGEND", "Legenda" },
253 { "LASTDAYS", "Statystyki z ostatnich dni" },
254 { "TOPHOURS", "Statystyki godzinowe" },
255 { "TOPUSERS", "Najaktywniejsi" },
256 { "OTHERS", "Jest jeszcze %d nie sklasyfikowanych..." },
257 { "NBLINES", "linii" },
259 { "AVGLETTERS", "liter/liniê" },
260 { "HOURS", "godziny" },
261 { "QUOTE", "losowa wypowied¼" },
262 { "TOPUSERSTIME", "Najaktywniejsi wed³ug pory dnia" },
263 { "RANDTOPICS", "Kilka topików" },
264 { "CHANGEDBY", "ustawiony przez" },
265 { "NEWTOPIC", "topik" },
266 { "RANDURLS", "Kilka URL-i" },
267 { "POSTEDBY", "poda³(a)" },
268 { "POSTEDURL", "URL" },
269 { "TOPWORDS", "Najczê¶ciej wystêpuj±ce s³owa" },
271 { "OCCURRENCES", "wyst±pieñ" },
272 { "BIGNUMBERS", "Kilka wielkopomnych liczb..." },
273 { "NUMBERS", "kategorie" },
274 { "TIME", "Dokonano analizy %d linii (obejmuj±cych %d dni) w czasie %d sekund" },
275 { "FOOTER", "Statystyki wygenerowane przez" },
276 { "C_SMILE", "jest czêsto szczesliwy(a) :)" },
277 { "C_FROWN", "jest czêsto smutny(a) :(" },
278 { "C_EXCLAM", "czêsto KRZYCZY!" },
279 { "C_QUESTION", "zadaje du¿o pytañ?" },
280 { "C_ME", "lubi u¿ywaæ /me" },
281 { "C_TOPIC", "czêsto zmienia topik" },
282 { "C_MODE", "czêsto zmienia tryby kana³u" },
283 { "C_KICK", "lubi kopaæ" },
284 { "C_KICKED", "czêsto wykopywany(a)" },
285 { "C_URL", "podaje du¿o URL-i" },
286 { "C_JOIN", "nie wie czy zostaæ, czy wyj¶æ" },
287 { "C_NICK", "czêsto zmienia nick" },
288 { "C_MONOLOGUE", "uwielbia monologi" }
290 { /* Polish language */
291 /* contributed by Piotr Jarmuz <coreupper@yahoo.com> */
292 { "Polish", "pl-old" },
293 { "CHARSET", "ISO-8859-1" },
294 { "HEADER", "Statystyki dla %s przez %s" },
295 { "LEGEND", "Legenda" },
296 { "LASTDAYS", "Statystyki z ostatnich dni" },
297 { "TOPHOURS", "Statystyki godzinne" },
298 { "TOPUSERS", "Najaktywniejsi ludzie" },
299 { "OTHERS", "Zostalo jeszcze %d nie sklasyfikowanych..." },
300 { "NBLINES", "linie" },
302 { "AVGLETTERS", "litery/linie" },
303 { "HOURS", "godziny" },
304 { "QUOTE", "przypadkowa wiadomosc" },
305 { "TOPUSERSTIME", "Najaktywniejsi ludzie wedlug czasu dnia" },
306 { "RANDTOPICS", "Pare tematow" },
307 { "CHANGEDBY", "zmienione przez" },
308 { "NEWTOPIC", "nowy temat" },
309 { "RANDURLS", "Pare URL-i" },
310 { "POSTEDBY", "wyslane przez" },
311 { "POSTEDURL", "URL" },
312 { "TOPWORDS", "Najczestsze slowa" },
314 { "OCCURRENCES", "wystapienia" },
315 { "BIGNUMBERS", "Pare wielkich liczb..." },
316 { "NUMBERS", "liczby" },
317 { "TIME", "%d linii (%d dni) sparsowanych w %d sekund" },
318 { "FOOTER", "Statystyki wygenerowane przez" },
319 { "C_SMILE", "jest czesto szczesliwy :)" },
320 { "C_FROWN", "jest czesto smutny :(" },
321 { "C_EXCLAM", "duzo krzyczy !" },
322 { "C_QUESTION", "zadaje duzo pytan ?" },
323 { "C_ME", "lubi /mnie polecenie" },
324 { "C_TOPIC", "czesto zmienia temat" },
325 { "C_MODE", "czesto zmienia tryb" },
326 { "C_KICK", "lubi /kopac" },
327 { "C_KICKED", "czesto go wykopuja" },
328 { "C_URL", "wysyla duzo URL-i" },
329 { "C_JOIN", "nie wie czy zostac czy wyjsc" },
330 { "C_NICK", "czesto zmienia swojego nicka" },
331 { "C_MONOLOGUE", "czesto mowi monologiem" }
333 { /* Finnish language */
334 /* contributed by Antti Huopana <ahuopana@ratol.fi> */
336 { "CHARSET", "ISO-8859-1" },
337 { "HEADER", "Kanavan %s tilastot - %s" },
338 { "LEGEND", "Merkkien selitykset" },
339 { "LASTDAYS", "Viime päivien tilastot" },
340 { "TOPHOURS", "Tilastot tunneittain" },
341 { "TOPUSERS", "Aktiivisimmat ihmiset" },
342 { "OTHERS", "Jäljelle jäi %d joita ei listattu..." },
343 { "NBLINES", "rivit" },
345 { "AVGLETTERS", "kirjainta/rivi" },
346 { "HOURS", "tunnit" },
347 { "QUOTE", "satunnainen viesti" },
348 { "TOPUSERSTIME", "Vuorokauden ajan mukaan aktiivisimmat" },
349 { "RANDTOPICS", "Joitakin aiheita" },
350 { "CHANGEDBY", "vaihtaja" },
351 { "NEWTOPIC", "aihe" },
352 { "RANDURLS", "Joitakin URLeja" },
353 { "POSTEDBY", "lähettäjä" },
354 { "POSTEDURL", "URL" },
355 { "TOPWORDS", "Eniten käytettyjä sanoja" },
357 { "OCCURRENCES", "käytetty" },
358 { "BIGNUMBERS", "Joitakin isoja lukuja..." },
359 { "NUMBERS", "luvut" },
360 { "TIME", "%d riviä (%d päivää) parsittu %d sekunnissa" },
361 { "FOOTER", "Tilastot on generoinut" },
362 { "C_SMILE", "on usein iloinen :)" },
363 { "C_FROWN", "on usein surullinen :(" },
364 { "C_EXCLAM", "möykkää paljon !" },
365 { "C_QUESTION", "kyselee liikaa ?" },
366 { "C_ME", "pitää itsestään" },
367 { "C_TOPIC", "vaihtaa usein aihetta" },
368 { "C_MODE", "haluaa elää muuttuvassa maailmassa" },
369 { "C_KICK", "pitää potkimisesta" },
370 { "C_KICKED", "tykkää tulla potkituksi" },
371 { "C_URL", "surffailee liikaa" },
372 { "C_JOIN", "ei tiedä ollakko vai eikö olla" },
373 { "C_NICK", "kärsii identiteettiongelmista" },
374 { "C_MONOLOGUE", "höpöttää paljon itsekseen" }
376 { /* Italian language */
377 /* contributed by Coviello Giuseppe <giuseppecoviello@tin.it> <http://coviello.altervista.org> */
379 { "CHARSET", "ISO-8859-1" },
380 { "HEADER", "Statistiche per il canale %s di %s" },
381 { "LEGEND", "Legenda" },
382 { "LASTDAYS", "Statistiche degli ultimi giorni" },
383 { "TOPHOURS", "Statistiche in ore" },
384 { "TOPUSERS", "Utenti più attivi" },
385 { "OTHERS", "Ci sono %d utenti non classificati..." },
386 { "NBLINES", "righe" },
388 { "AVGLETTERS", "lettere/righe" },
390 { "QUOTE", "messaggio casuale" },
391 { "TOPUSERSTIME", "Utenti più attivi del giorno (divisi per fasce orarie)" },
392 { "RANDTOPICS", "Alcuni topic" },
393 { "CHANGEDBY", "cambiato da" },
394 { "NEWTOPIC", "nuovo topic" },
395 { "RANDURLS", "ALcuni URL" },
396 { "POSTEDBY", "postato da" },
397 { "POSTEDURL", "URL" },
398 { "TOPWORDS", "Le parole più usate" },
399 { "WORD", "parola" },
400 { "OCCURRENCES", "usata" },
401 { "BIGNUMBERS", "Alcuni numeri ..." },
402 { "NUMBERS", "numeri" },
403 { "TIME", "%d righe (%d giorni) esaminate in %d secondi" },
404 { "FOOTER", "Statistiche generate da" },
405 { "C_SMILE", "è spesso felice :)" },
406 { "C_FROWN", "è spesso triste :(" },
407 { "C_EXCLAM", "esclama molto !" },
408 { "C_QUESTION", "fa molte domande ?" },
409 { "C_ME", "ama il comando /me" },
410 { "C_TOPIC", "cambia spesso il topic" },
411 { "C_MODE", "cambia spesso i mode" },
412 { "C_KICK", "ama /kick(are)" },
413 { "C_KICKED", "è kickato spesso" },
414 { "C_URL", "posta molti URL" },
415 { "C_JOIN", "non sa se è ora di andare o restare" },
416 { "C_NICK", "cambia spesso il nick" },
417 { "C_MONOLOGUE", "fa molti monologhi" }
419 { /* Dutch language */
420 /* contributed by Jeroen Ubbink <crasp@blackbyte.nl> */
421 /* updated by Wouter Horré <wouter@ligezin.be> */
423 { "CHARSET", "ISO-8859-1" },
424 { "HEADER", "Statistieken voor %s door %s" },
425 { "LEGEND", "Legenda" },
426 { "LASTDAYS", "Statistieken van de laatste dagen" },
427 { "TOPHOURS", "Statistieken per uur" },
428 { "TOPUSERS", "Meest actieve mensen" },
429 { "OTHERS", "Er zijn nog %d mensen die de top niet haalden..." },
430 { "NBLINES", "regels" },
432 { "AVGLETTERS", "letters/lijn" },
434 { "QUOTE", "Willekeurige regel" },
435 { "TOPUSERSTIME", "Meest actieve mensen per tijdstip per dag" },
436 { "RANDTOPICS", "Enkele topics" },
437 { "CHANGEDBY", "gewijzigd door" },
438 { "NEWTOPIC", "nieuwe topic" },
439 { "RANDURLS", "Enkele URLs" },
440 { "POSTEDBY", "Geplaatst door" },
441 { "POSTEDURL", "URL" },
442 { "TOPWORDS", "Meest gebruikte woorden" },
444 { "OCCURRENCES", "aantal" },
445 { "BIGNUMBERS", "Enkele grote aantallen..." },
446 { "NUMBERS", "numbers" },
447 { "TIME", "%d regels (%d dagen) verwerkt in %d seconden" },
448 { "FOOTER", "Statistieken gegenereerd door" },
449 { "C_SMILE", "is vaak vrolijk :)" },
450 { "C_FROWN", "is vaak droevig :(" },
451 { "C_EXCLAM", "schreeuwt veel !" },
452 { "C_QUESTION", "stelt veel vragen ?" },
453 { "C_ME", "vindt /me een leuk commando" },
454 { "C_TOPIC", "verandert vaak de topic" },
455 { "C_MODE", "verandert vaak de modes" },
456 { "C_KICK", "vindt /kick erg leuk" },
457 { "C_KICKED", "wordt vaak gekickt" },
458 { "C_URL", "plaatst veel URLs" },
459 { "C_JOIN", "twijfelt tussen blijven of gaan" },
460 { "C_NICK", "verandert vaak van nick" },
461 { "C_MONOLOGUE", "spreekt veel monologen" }
463 { /* Russian language */
464 /* contributed by kamikaze <kamikaze@rss.lv> */
466 { "CHARSET", "KOI8-R" },
467 { "HEADER", "óÔÁÔÉÓÔÉËÁ ÄÌÑ %s ÏÔ %s" },
468 { "LEGEND", "ïÂÏÚÎÁÞÅÎÉÑ" },
469 { "LASTDAYS", "óÔÁÔÉÓÔÉËÁ ÐÏÓÌÅÄÎÉÈ ÄÎÅÊ" },
470 { "TOPHOURS", "ðÏÞÁÓÏ×ÁÑ ÓÔÁÔÉÓÔÉËÁ" },
471 { "TOPUSERS", "áËÔÉ×ÎÅÊÛÉÅ ÌÀÄÉ" },
472 { "OTHERS", "ïÓÔÁÌÏÓØ %d ÎÅÐÏÄÓÞÉÔÁÎÙÈ..." },
473 { "NBLINES", "ÓÔÒÏËÉ" },
475 { "AVGLETTERS", "ÂÕË×Ù/ÓÔÒÏËÉ" },
477 { "QUOTE", "ÓÌÕÞÁÊÎÏÅ ÓÏÏÂÝÅÎÉÅ" },
478 { "TOPUSERSTIME", "áËÔÉ×ÎÅÊÛÉÅ ÌÀÄÉ ÐÏ ×ÒÅÍÅÎÉ ÄÎÑ" },
479 { "RANDTOPICS", "îÅÓËÏÌØËÏ ÔÏÐÉËÏ×" },
480 { "CHANGEDBY", "ÉÚÍÅΣÎ" },
481 { "NEWTOPIC", "ÎÏ×ÙÊ ÔÏÐÉË" },
482 { "RANDURLS", "îÅÓËÏÌØËÏ URLÏ×" },
483 { "POSTEDBY", "ÏÐÕÂÌÉËÏ×ÁÌ" },
484 { "POSTEDURL", "URL" },
485 { "TOPWORDS", "þÁÓÔÏ ÉÓÐÏÌØÚÕÅÍÙÅ ÓÌÏ×Á" },
487 { "OCCURRENCES", "ÐÒÏÉÛÅÓÔ×ÉÑ" },
488 { "BIGNUMBERS", "îÅÓËÏÌØËÏ ÂÏÌØÛÉÈ ÞÉÓÅÌ..." },
489 { "NUMBERS", "ÞÉÓÌÁ" },
490 { "TIME", "%d ÓÔÒÏË (%d ÄÎÅÊ) ÏÂÒÁÂÏÔÁÎÏ ÚÁ %d ÓÅËÕÎÄ" },
491 { "FOOTER", "óÔÁÔÉÓÔÉËÁ ÓÇÅÎÅÒÉÒÏ×ÁÎÁ" },
492 { "C_SMILE", "ÞÁÓÔÏ ÓÞÁÓÌÉ× :)" },
493 { "C_FROWN", "ÞÁÓÔÏ ÎÅÓÞÁÓÔÅÎ :(" },
494 { "C_EXCLAM", "ÍÎÏÇÏ ×ÏÓËÌÉÃÁÅÔ !" },
495 { "C_QUESTION", "ÚÁÄÁ£Ô ÍÎÏÇÏ ×ÏÐÒÏÓÏ× ?" },
496 { "C_ME", "ÌÀÂÉÔ /me command" },
497 { "C_TOPIC", "ÞÁÓÔÏ ÍÅÎÑÅÔ ÔÏÐÉË" },
498 { "C_MODE", "ÞÁÓÔÏ ÍÅÎÑÅÔ ÒÅÖÉÍÙ" },
499 { "C_KICK", "ÌÀÂÉÔ /kick" },
500 { "C_KICKED", "ÞÁÓÔÏ ×ÙËÉÄÙ×ÁÀÔ" },
501 { "C_URL", "ÐÕÂÌÉËÕÅÔ ÍÎÏÇÏ URLÏ×" },
502 { "C_JOIN", "ÎÅ ÚÎÁÅÔ - ÏÓÔÁÔØÓÑ ÉÌÉ ÕÊÔÉ" },
503 { "C_NICK", "ÞÁÓÔÏ ÍÅÎÑÅÔ Ó×ÏÊ ÎÉË" },
504 { "C_MONOLOGUE", "éÓÐÏÌØÚÕÅÔ ÍÎÏÇÏ ÍÏÎÏÌÏÇÏ×" }
506 { /* Estonian language */
507 /* contributed by Martin Vool <mardicas@hot.ee> */
508 { "Estonian", "et" },
509 { "CHARSET", "ISO-8859-4" },
510 { "HEADER", "Statistika kanalile %s on koostanud %s" },
511 { "LEGEND", "Legend" },
512 { "LASTDAYS", "Viimaste päevade statistika" },
513 { "TOPHOURS", "Tunni statistika" },
514 { "TOPUSERS", "Kõige aktiivsemad inimesed" },
515 { "OTHERS", "%d inimest on rääkinud" },
516 { "NBLINES", "rida" },
518 { "AVGLETTERS", "tähte/rida" },
520 { "QUOTE", "suvaline teade" },
521 { "TOPUSERSTIME", "Kõige aktiivsemad inimesed päeva aja järgi" },
522 { "RANDTOPICS", "Mõned topicud" },
523 { "CHANGEDBY", "muutis" },
524 { "NEWTOPIC", "topicud" },
525 { "RANDURLS", "Mõned aadressid" },
526 { "POSTEDBY", "postitas" },
527 { "POSTEDURL", "URL" },
528 { "TOPWORDS", "Enim kasutatud sõnad" },
530 { "OCCURRENCES", "sagedus" },
531 { "BIGNUMBERS", "Mõned suured numbrid" },
532 { "NUMBERS", "iseloom" },
533 { "TIME", "%d rida (%d päeva) on möödunud %d sekundit" },
534 { "FOOTER", "Statistika on koostanud" },
535 { "C_SMILE", "on tihti õnnelik :)" },
536 { "C_FROWN", "on tihti kurb :(" },
537 { "C_EXCLAM", "põrnitseb palju" },
538 { "C_QUESTION", "küsib palju küsimusi" },
539 { "C_ME", "/me manjakk" },
540 { "C_TOPIC", "vahetab tihti topicut" },
541 { "C_MODE", "vahetab tihti modesid" },
542 { "C_KICK", "kickib palju" },
543 { "C_KICKED", "saab tihti kicke" },
544 { "C_URL", "reklaamib palju" },
545 { "C_JOIN", "sõelub sisse ja välja" },
546 { "C_NICK", "vahetab pidevalt nime" },
547 { "C_MONOLOGUE", "räägib palju monolooge" }
551 int language=0; /* default to english */
556 for (i=1;i<=NBKEYS;i++) if (strcmp(key,keys[language][i][0])==0) return(keys[language][i][1]);
557 fprintf(stderr,"unknown language key: %s\n",key);
563 int debug=1; /* 0 = none ; 1 = normal ; 2 = verbose */
564 char channel[MAXLINELENGTH]="set_channel_in_config_file";
565 char maintainer[MAXLINELENGTH]="set_maintainer_in_config_file";
566 char theme[MAXLINELENGTH]="default,biseau,blue,dark,damier,grayscale,namour,niflheim,pisg,zeduel,zerezo";
567 int refresh_time=0; /* 0 = disabled */
568 int w3c_link=1; /* 0 = disabled */
569 int logo=1; /* 0 = disabled */
570 char header[MAXLINELENGTH]="none";
571 char footer[MAXLINELENGTH]="none";
574 int top_words=1; /* 0 = disabled */
575 int ranking=0; /* 0 = lines ; 1 = words ; 2 = letters */
576 int quarter=0; /* 1 = enabled */
581 char nick[MAXNICKLENGTH];
586 char quote[MAXQUOTELENGTH+1];
587 int counters[NBCOUNTERS];
592 int maxusers=BASEUSERS;
596 char nick[MAXNICKLENGTH];
597 char url[MAXLINELENGTH];
598 char shorturl[MAXQUOTELENGTH+1];
604 char nick[MAXNICKLENGTH];
605 char topic[MAXQUOTELENGTH+1];
623 struct letter *next[26];
629 char word[MAXLINELENGTH];
635 struct rusletter *next[33];
638 #define isletter(c) (((c>='a')&&(c<='z'))||((c>='A')&&(c<='Z')))
639 #define lowercase(c) (((c>='A')&&(c<='Z'))?c-'A'+'a':c)
640 //#define lowercase(c) (c | 0x20)
641 #define isrusletter(c) (memchr(koi,c,66)==NULL?0:1)
644 193,194,215,199,196,197,163,214,218,201,202,203,204,205,206,207,208,210,
645 211,212,213,198,200,195,222,219,221,216,223,217,220,192,209,
646 225,226,247,231,228,229,179,246,250,233,234,235,236,237,238,239,240,242,
647 243,244,245,230,232,227,254,251,253,248,255,249,252,224,241
649 int lowruscase(char c)
653 if (memchr(koi,c,66)==NULL) return koi[0];
654 ch=strlen(koi)-strlen(memchr(koi,c,66));
655 if (ch>33) return ch-33; else return ch;
657 /* cp1251 for encoding into koi8-r
659 224,225,226,227,228,229,184,230,231,232,233,234,235,236,237,238,239,240,
660 241,242,243,244,245,246,247,248,249,252,250,251,253,254,255,
661 192,193,194,195,196,197,168,198,199,200,201,202,203,204,205,206,207,208,
662 209,210,211,212,213,214,215,216,217,220,218,219,221,222,223
667 int findruswords(char *message)
671 struct rusletter *pos,*tmp;
674 while (!isrusletter(*message)) if (*message=='\0') return n; else message++;
676 while (isrusletter(*message))
678 c=lowruscase(*message);
679 /*tmp_p=memchr(koi,message[0],33);
680 if (tmp_p==NULL) return n;
681 c=strlen(koi)-strlen(tmp_p);*/
682 if (pos->next[(int)c]==NULL)
684 tmp=malloc(sizeof(struct rusletter));
687 fprintf(stderr, "findruswords(): malloc failure\n");
691 for (i=0;i<33;i++) tmp->next[i]=NULL;
692 pos->next[(int)c]=tmp;
694 pos=pos->next[(int)c];
704 void freeruswords(struct rusletter *pos)
707 for (i=0;i<33;i++) if (pos->next[i]!=NULL)
709 freeruswords(pos->next[i]);
715 int findwords(char *message)
718 struct letter *pos,*tmp;
721 while (!isletter(*message)) if (*message=='\0') return n; else message++;
723 while (isletter(*message))
725 c=lowercase(*message)-'a';
726 if (pos->next[(int)c]==NULL)
728 tmp=malloc(sizeof(struct letter));
731 fprintf(stderr, "findwords(): malloc failure\n");
735 for (i=0;i<26;i++) tmp->next[i]=NULL;
736 pos->next[(int)c]=tmp;
738 pos=pos->next[(int)c];
747 char tempword[MAXLINELENGTH];
748 void bestwords(struct letter pos,int cur)
751 if ((cur>=MINWORDLENGTH)&&(pos.nb>topwords[NBWORDS-1].nb))
753 for (i=0;pos.nb<topwords[i].nb;i++);
754 for (j=NBWORDS-1;j>i;j--)
756 topwords[j].nb=topwords[j-1].nb;
757 strcpy(topwords[j].word,topwords[j-1].word);
759 topwords[i].nb=pos.nb;
760 strcpy(topwords[i].word,tempword);
762 for (i=0;i<26;i++) if (pos.next[i]!=NULL)
765 bestwords(*(pos.next[i]),cur+1);
770 void bestruswords(struct rusletter pos,int cur)
773 if ((cur>=MINWORDLENGTH)&&(pos.nb>topwords[NBWORDS-1].nb))
775 for (i=0;pos.nb<topwords[i].nb;i++);
776 for (j=NBWORDS-1;j>i;j--)
778 topwords[j].nb=topwords[j-1].nb;
779 strcpy(topwords[j].word,topwords[j-1].word);
781 topwords[i].nb=pos.nb;
782 strcpy(topwords[i].word,tempword);
784 for (i=0;i<33;i++) if (pos.next[i]!=NULL)
786 tempword[cur]=koi[i];
787 bestruswords(*(pos.next[i]),cur+1);
792 void freewords(struct letter *pos)
795 for (i=0;i<26;i++) if (pos->next[i]!=NULL)
797 freewords(pos->next[i]);
803 void printhtml(FILE *fic,char *string) /* replace < and > by < and > */
805 while (*string!='\0')
809 case '<':fprintf(fic,"<"); break;
810 case '>':fprintf(fic,">"); break;
811 case '&':fprintf(fic,"&"); break;
812 default:fprintf(fic,"%c",*string); break;
819 int dichotomic(char *nick)
821 int i,j,start=0,end=nbusers-1,middle;
824 middle=(start+end)/2;
825 if (strcmp(nick,users[middle].nick)>0) start=middle+1; else end=middle-1;
827 if (strcmp(nick,users[start].nick)!=0)
830 if (nbusers>=maxusers)
833 if (debug==2) fprintf(stderr,"allocating more users : %d\n",maxusers);
834 if ((users=realloc(users,maxusers*sizeof(struct user)))==NULL) { fprintf(stderr,"too many users : unable to realloc memory\n"); exit(1); }
836 for (i=nbusers-1;i>start;i--)
838 strcpy(users[i].nick,users[i-1].nick);
839 users[i].lines=users[i-1].lines;
840 users[i].words=users[i-1].words;
841 users[i].letters=users[i-1].letters;
842 for (j=0;j<4;j++) users[i].hours[j]=users[i-1].hours[j];
843 strcpy(users[i].quote,users[i-1].quote);
844 for (j=0;j<NBCOUNTERS;j++) users[i].counters[j]=users[i-1].counters[j];
845 users[i].temp=users[i-1].temp;
846 users[i].photo=users[i-1].photo;
848 strcpy(users[start].nick,nick);
849 users[start].lines=0;
850 users[start].words=0;
851 users[start].letters=0;
852 for (j=0;j<4;j++) users[start].hours[j]=0;
853 users[start].quote[0]='\0';
854 for (j=0;j<NBCOUNTERS;j++) users[start].counters[j]=0;
856 users[start].photo=NULL;
861 void day_changed(char* date)
866 memcpy(newday, date, 11);
868 memcpy(newday+11, date+20, 4);
870 memcpy(newday+11, date+11, 4);
872 if (memcmp(currday, newday, 15)!=0)
874 /* we do not have a "current" day yet? */
879 lastdays[i].lines=lastdays[i-1].lines;
880 for (j=0;j<4;j++) lastdays[i].hours[j]=lastdays[i-1].hours[j];
883 for (j=0;j<4;j++) lastdays[0].hours[j]=0;
886 memcpy(currday, newday, 15);
888 fprintf(stderr, "day %d changed to: %s\n", days, currday);
891 fprintf(stderr, "but day did not change\n");
895 void parse_log(char *logfile)
898 char line[MAXLINELENGTH];
903 int mononick=-1,monolines=0;
907 if ((fic=fopen(logfile,"rt"))==NULL) { fprintf(stderr,"can't open log file \"%s\"\n",logfile); exit(1); }
908 if (debug) printf("working on %s : ",channel);
909 while (fgets(line,MAXLINELENGTH,fic)!=NULL)
912 for (i=0;line[i]!=0 && i<MAXLINELENGTH-1;i++);
913 if (i>=MAXLINELENGTH-1) {
915 fprintf(stderr,"line %d is too long, skipping\n",totallines+1);
921 fprintf(stderr, "line %d is too short to be valid, skipping\n",totallines+1);
928 if (totallines%10000==0 && debug) { printf("."); fflush(stdout); }
929 if (strncmp("--- Log opened",line,14)==0) /* --- Log opened Wed May 01 00:00 2002 */
932 fprintf(stderr, "log %s opened, ", logfile);
933 day_changed(line+15);
935 if (strncmp("--- Day changed",line,15)==0) /* --- Day changed Wed May 01 2002 */
938 fprintf(stderr, "within log file, ");
939 day_changed(line+16);
943 /* timelen is number of characters occupied by time 00:00.. plus any space */
945 if (line[timelen] == ':' && isdigit(line[timelen+1]) && isdigit(line[timelen+2]))
947 if (line[timelen] == ' ')
949 if (strncmp("-!- mode/",&line[timelen],9)==0) /* 00:00 -!- mode/#channel [...] by (Nick, Nick2, )Nick3 */
951 for (i=strlen(line);line[i]!=' ';i--);
953 users[dichotomic(nick)].counters[D_MODE]++;
955 else if (strncmp("-!-",&line[timelen],3)==0) /* 00:00 -!- Nick something... */
957 for (i=10;line[i]!=' ' && i <= 10 + MAXNICKLENGTH;i++);
958 if(i > 10 + MAXNICKLENGTH) {
960 fprintf(stderr,"nick on line %d is too long, skipping line\n",totallines);
965 nick=&line[timelen+4];
967 if (strncmp("changed the topic of",message,20)==0) /* 00:00 -!- Nick changed the topic of #channel to: new topic */
969 users[dichotomic(nick)].counters[D_TOPIC]++;
970 for (i=21;message[i]!=':';i++);
971 message=&message[i+2];
973 if ((nbtopics<=NBTOPICS) || (rand()%(nbtopics/NBTOPICS)==0))
975 temp=nbtopics<=NBTOPICS?nbtopics-1:rand()%NBTOPICS;
976 strcpy(topics[temp].nick,nick);
977 strncpy(topics[temp].topic,message,MAXQUOTELENGTH);
980 else if (strncmp("was kicked from",message,15)==0) /* 00:00 -!- Nick was kicked from #channel by Nick [Reason] */
982 users[dichotomic(nick)].counters[D_KICKED]++;
983 for (i=16;message[i]!=' ';i++);
984 message=&message[i+4];
985 for (i=0;message[i]!=' ';i++);
987 users[dichotomic(message)].counters[D_KICK]++;
989 else if (strncmp("is now known as",message,15)==0) /* 00:00 -!- Nick is now known as Nick */
990 users[dichotomic(nick)].counters[D_NICK]++;
991 else if (message[0]=='[') /* 00:00 -!- Nick [user@host] something... */
993 for (i=0;message[i]!=']';i++);
994 message=&message[i+2];
995 if (strncmp("has joined",message,10)==0) /* 00:00 -!- Nick [user@host] has joined #channel */
996 users[dichotomic(nick)].counters[D_JOIN]++;
997 else if (strncmp("has quit",message,8)==0); /* 00:00 -!- Nick [user@host] has quit [Reason] */
998 else if (strncmp("has left",message,8)==0); /* 00:00 -!- Nick [user@host] has left #channel [Reason] */
1002 else if ((line[timelen]=='<') || (line[timelen+1]=='*'))
1006 if (line[timelen+1]=='*') /* 00:00 * Nick the message */
1008 for (i=timelen+3;line[i]!=' ' && i <= timelen+3+MAXNICKLENGTH;i++);
1009 if(i > timelen+3+MAXNICKLENGTH) {
1011 fprintf(stderr,"nick on line %d is too long, skipping line\n",totallines);
1015 nick=&line[timelen+3];
1018 else if (line[timelen+1]=='>') /* 00:00 <>>>?Nick<<<> the personal message */
1019 /* 00:00 <>>?Nick<<> the personal message */
1021 for (i=timelen+4;line[i]!='<' && i <= timelen+4+MAXNICKLENGTH;i++);
1022 if(i > timelen+4+MAXNICKLENGTH) {
1024 fprintf(stderr,"nick on line %d is too long, skipping line\n",totallines);
1028 nick=&line[timelen+4];
1029 if (line[timelen+3]=='>') nick++;
1032 else /* 00:00 <?Nick> the message */
1036 * Irssi doesn't log channel mode with show_nickmode = OFF
1037 * the following covers op, half-op, voice and show_nickmode_empty
1039 switch (line[timelen+1])
1045 nickstart = timelen+2;
1048 nickstart = timelen+1;
1052 for (i=nickstart;line[i]!='>' && i <= nickstart + MAXNICKLENGTH;i++);
1053 if(i > nickstart + MAXNICKLENGTH) {
1055 fprintf(stderr,"nick on line %d is too long, skipping line\n",totallines);
1059 nick=&line[nickstart];
1062 /* remove identified character from nick (invalid nick character anyway) */
1064 fprintf(stderr, "nick starts with +! %s\n", nick);
1065 if (line[i-1] == '+' || line[i-1] == '*')
1069 if (line[timelen+1]=='*') users[i].counters[D_ME]++;
1073 if (monolines==5) users[i].counters[D_MONOLOGUE]++;
1082 if (top_words || ranking==1)
1084 if (L("CHARSET")=="KOI8-R") users[i].words+=findwords(message)+findruswords(message);
1085 else users[i].words+=findwords(message);
1087 users[i].letters+=j;
1088 users[i].hours[hour/6]++;
1089 lastdays[0].lines++;
1090 lastdays[0].hours[hour/6]++;
1095 hour=hour*4+atoi(&line[3])/15;
1098 if (message[j-1]=='?') users[i].counters[D_QUESTION]++;
1099 else if (message[j-1]=='!') users[i].counters[D_EXCLAM]++;
1100 else if ((message[j-3]==' ')&&(message[j-2]==':'))
1102 if (message[j-1]==')') users[i].counters[D_SMILE]++;
1103 else if (message[j-1]=='(') users[i].counters[D_FROWN]++;
1105 if (rand()%users[i].lines==0) strncpy(users[i].quote,message,MAXQUOTELENGTH);
1106 if (strncmp("http://",message,7)==0)
1108 users[i].counters[D_URL]++;
1109 for (i=0;(message[i]!=' ') && (i<strlen(message));i++);
1112 if ((nburls<=NBURLS) || (rand()%(nburls/NBURLS)==0))
1114 temp=nburls<=NBURLS?nburls-1:rand()%NBURLS;
1115 strcpy(urls[temp].nick,nick);
1116 strcpy(urls[temp].url,message);
1117 strncpy(urls[temp].shorturl,message,MAXQUOTELENGTH);
1125 if (debug) printf(" done\n");
1129 void parse_nick(char *nickfile)
1134 char line[MAXLINELENGTH];
1137 for (i=0;i<nbusers;i++) users[i].temp=users[i].lines;
1138 if ((fic=fopen(nickfile,"rt"))==NULL) { fprintf(stderr,"can't open nick file \"%s\"\n",nickfile); exit(1); }
1139 while (fscanf(fic,"%s",line)==1)
1141 user=dichotomic(line);
1142 fscanf(fic,"%s",line);
1143 if (regcomp(&preg,line,0)!=0) { fprintf(stderr,"error in nick file"); exit(1); }
1144 for (i=0;i<nbusers;i++) if ((i!=user) && (regexec(&preg,users[i].nick,0,0,0)==0) && (users[i].lines>=0))
1146 if (users[i].temp>users[user].temp) /* for nick alias, keep the random quote of the most used nick */
1148 strcpy(users[user].quote,users[i].quote);
1149 users[user].temp=users[i].temp;
1151 users[user].lines+=users[i].lines;
1152 users[user].words+=users[i].words;
1153 users[user].letters+=users[i].letters;
1154 for (j=0;j<4;j++) users[user].hours[j]+=users[i].hours[j];
1155 for (j=0;j<NBCOUNTERS;j++) users[user].counters[j]+=users[i].counters[j];
1156 /* "remove" old user */
1159 users[i].letters=-1;
1160 for (j=0;j<4;j++) users[i].hours[j]=-1;
1161 for (j=0;j<NBCOUNTERS;j++) users[i].counters[j]=-1;
1166 /* "remove" the ignored nicks */
1167 i=dichotomic("<NULL>");
1170 users[i].letters=-1;
1171 for (j=0;j<4;j++) users[i].hours[j]=-1;
1172 for (j=0;j<NBCOUNTERS;j++) users[i].counters[j]=-1;
1176 void parse_photo(char *photofile)
1179 char line[MAXLINELENGTH];
1182 if ((fic=fopen(photofile,"rt"))==NULL) { fprintf(stderr,"can't open photo file \"%s\"\n",photofile); exit(1); }
1183 while (fscanf(fic,"%s",line)==1)
1185 user=dichotomic(line);
1186 fscanf(fic,"%s",line);
1187 users[user].photo=malloc(strlen(line)+1);
1188 strcpy(users[user].photo,line);
1193 void gen_xhtml(char *xhtmlfile)
1199 char line[MAXLINELENGTH];
1203 for (i=0;i<nbusers;i++) if (users[i].photo!=NULL) photos=1;
1205 if ((fic=fopen(xhtmlfile,"wt"))==NULL) { fprintf(stderr,"can't open xhtml file \"%s\"\n",xhtmlfile); exit(1); }
1208 if (strcmp("none",header)==0)
1210 fprintf(fic,"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n\n");
1211 fprintf(fic,"<!-- Generated by irssistats %s : %s -->\n\n",VERSION,URL);
1212 fprintf(fic,"<html>\n\n<head>\n<title>");
1213 fprintf(fic,L("HEADER"),channel,maintainer);
1214 fprintf(fic,"</title>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=%s\" />\n",L("CHARSET"));
1216 fprintf(fic,"<meta http-equiv=\"Refresh\" content=\"%d\" />\n",refresh_time);
1217 subtheme=strtok(theme,",");
1218 fprintf(fic,"<link rel=\"stylesheet\" type=\"text/css\" href=\"%s.css\" title=\"%s\" />\n",subtheme,subtheme);
1219 while ((subtheme=strtok(NULL,","))!=NULL)
1220 fprintf(fic,"<link rel=\"alternate stylesheet\" type=\"text/css\" href=\"%s.css\" title=\"%s\" />\n",subtheme,subtheme);
1221 fprintf(fic,"</head>\n\n");
1222 fprintf(fic,"<body>\n\n");
1226 if ((sfic=fopen(header,"rt"))==NULL) { fprintf(stderr,"can't open header file \"%s\"\n",header); exit(1); }
1227 while ((temp=fread(line,1,MAXLINELENGTH,sfic))) fwrite(line,temp,1,fic);
1230 fprintf(fic,"<div id=\"irssistats\">\n\n<div id=\"irssistats_header\">\n<h1>");
1231 fprintf(fic,L("HEADER"),channel,maintainer);
1232 fprintf(fic,"</h1>\n<p>\n%s</p>\n</div>\n\n",ctime(&debut));
1235 fprintf(fic,"<div id=\"irssistats_legend\">\n<h2>%s</h2>\n<table>\n<tr>\n",L("LEGEND"));
1236 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);
1237 fprintf(fic,"</tr>\n</table>\n</div>\n\n");
1240 fprintf(fic,"<div id=\"irssistats_lastdays\">\n<h2>%s</h2>\n<table>\n<tr>\n",L("LASTDAYS"));
1242 for (i=30;i>=0;i--) if (lastdays[i].lines>max) max=lastdays[i].lines;
1245 fprintf(fic,"<td align=\"center\" valign=\"bottom\"><small>%d</small>",lastdays[i].lines);
1246 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);
1247 fprintf(fic,"</td>\n");
1249 fprintf(fic,"</tr>\n<tr>\n");
1251 fprintf(fic,"<th>%d</th>\n",i);
1252 fprintf(fic,"</tr>\n</table>\n</div>\n\n");
1255 fprintf(fic,"<div id=\"irssistats_tophours\">\n<h2>%s</h2>\n<table>\n<tr>\n",L("TOPHOURS"));
1257 for (i=0;i<24*4;i++) if (hours[i]>max) max=hours[i];
1258 if (quarter) for (i=0;i<24*4;i++)
1260 fprintf(fic,"<td align=\"center\" valign=\"bottom\">");
1261 if (hours[i]!=0) fprintf(fic,"<div class=\"v%d\" style=\"width:4px;height:%dpx\"></div>",i/4/6+1,150*hours[i]/max);
1262 fprintf(fic,"</td>\n");
1264 else for (i=0;i<24;i++)
1266 fprintf(fic,"<td align=\"center\" valign=\"bottom\"><small>%.1f%%</small>",lines!=0?(float)100*hours[i]/lines:0);
1267 if (hours[i]!=0) fprintf(fic,"<div class=\"v%d\" style=\"height:%dpx\"></div>",i/6+1,150*hours[i]/max);
1268 fprintf(fic,"</td>\n");
1270 fprintf(fic,"</tr>\n<tr>\n");
1272 if (quarter) fprintf(fic,"<th colspan=\"4\">%d</th>\n",i);
1273 else fprintf(fic,"<th>%d</th>\n",i);
1274 fprintf(fic,"</tr>\n</table>\n</div>\n\n");
1277 fprintf(fic,"<div id=\"irssistats_topusers\">\n<h2>%s</h2>\n",L("TOPUSERS"));
1281 fprintf(fic,"<table>\n<tr><th></th><th>%s</th><th>%s</th><th>%s</th><th colspan=\"2\">%s</th><th>%s</th>\n",L("NICK"),L("NBLINES"),L("HOURS"),L("AVGLETTERS"),L("QUOTE"));
1284 /* "letters" and "words" ranking are not yet translated so we use the generic word "rank" ... */
1285 fprintf(fic,"<table>\n<tr><th></th><th>%s</th><th>%s</th><th>%s</th><th colspan=\"2\">%s</th><th>%s</th>\n",L("NICK"),"rank",L("HOURS"),L("AVGLETTERS"),L("QUOTE"));
1288 if (photos) fprintf(fic,"<th></th>");
1289 fprintf(fic,"</tr>");
1290 for (i=1;i<=NBUSERS;i++)
1297 for (j=0;j<nbusers;j++) if (users[j].lines>max) max=users[user=j].lines;
1300 for (j=0;j<nbusers;j++) if (users[j].words>max) max=users[user=j].words;
1303 for (j=0;j<nbusers;j++) if (users[j].letters>max) max=users[user=j].letters;
1311 fprintf(fic,"<tr><td>%d</td><td>%s</td><td>%d</td><td class=\"oneline\">",i,users[user].nick,users[user].lines);
1314 fprintf(fic,"<tr><td>%d</td><td>%s</td><td>%d</td><td class=\"oneline\">",i,users[user].nick,users[user].words);
1317 fprintf(fic,"<tr><td>%d</td><td>%s</td><td>%d</td><td class=\"oneline\">",i,users[user].nick,users[user].letters);
1320 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);
1321 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);
1322 printhtml(fic,users[user].quote);
1323 fprintf(fic,"\"</td>");
1324 if (photos && users[user].photo!=NULL)
1327 fprintf(fic,"<td class=\"tdphoto\"><a href=\"%s\"><img src=\"%s\" class=\"imgphoto\" width=\"%d\" height=\"%d\" alt=\"\" style=\"border: 0\" /></a></td>",users[user].photo,users[user].photo,photo_size,photo_size);
1329 fprintf(fic,"<td class=\"tdphoto\"><img src=\"%s\" class=\"imgphoto\" alt=\"\" /></td>",users[user].photo);
1331 fprintf(fic,"</tr>\n");
1332 users[user].lines=-1;
1333 users[user].words=-1;
1334 users[user].letters=-1;
1337 fprintf(fic,"</table>\n");
1339 for (i=0;i<=nbusers;i++) if (users[i].lines>=0) temp++;
1343 fprintf(fic,L("OTHERS"),temp);
1344 fprintf(fic,"</p>\n");
1346 fprintf(fic,"</div>\n\n");
1348 /* top users by time */
1349 fprintf(fic,"<div id=\"irssistats_topuserstime\">\n<h2>%s</h2>\n",L("TOPUSERSTIME"));
1350 fprintf(fic,"<table>\n<tr><th></th>");
1351 for (i=0;i<4;i++) fprintf(fic,"<th colspan=\"2\">%s %d-%d</th>",L("HOURS"),i*6,i*6+5);
1352 fprintf(fic,"</tr>\n");
1353 for (i=1;i<=NBUSERSTIME;i++)
1355 fprintf(fic,"<tr><td>%d</td>",i);
1360 for (k=0;k<nbusers;k++) if (users[k].hours[j]>max) max=users[user=k].hours[j];
1363 fprintf(fic,"<td>%s</td><td>%d</td>",users[user].nick,users[user].hours[j]);
1364 users[user].hours[j]=-1;
1366 else fprintf(fic,"<td></td><td></td>");
1368 fprintf(fic,"</tr>\n");
1370 fprintf(fic,"</table>\n</div>\n\n");
1373 fprintf(fic,"<div id=\"irssistats_randtopics\">\n<h2>%s</h2>\n",L("RANDTOPICS"));
1374 fprintf(fic,"<table>\n<tr><th>%s</th><th>%s</th></tr>\n",L("CHANGEDBY"),L("NEWTOPIC"));
1375 for (i=nbtopics<NBTOPICS?nbtopics-1:NBTOPICS-1;i>=0;i--)
1377 fprintf(fic,"<tr><td>%s</td><td>\"",topics[i].nick);
1378 printhtml(fic,topics[i].topic);
1379 fprintf(fic,"\"</td></tr>\n");
1381 fprintf(fic,"</table>\n</div>\n\n");
1384 fprintf(fic,"<div id=\"irssistats_randurls\">\n<h2>%s</h2>\n",L("RANDURLS"));
1385 fprintf(fic,"<table>\n<tr><th>%s</th><th>%s</th></tr>\n",L("POSTEDBY"),L("POSTEDURL"));
1386 for (i=nburls<NBURLS?nburls-1:NBURLS-1;i>=0;i--)
1388 fprintf(fic,"<tr><td>%s</td><td>\"<a href=\"",urls[i].nick);
1389 printhtml(fic,urls[i].url);
1391 printhtml(fic,urls[i].shorturl);
1392 fprintf(fic,"</a>\"</td></tr>\n");
1394 fprintf(fic,"</table>\n</div>\n\n");
1399 fprintf(fic,"<div id=\"irssistats_topwords\">\n<h2>%s</h2>\n",L("TOPWORDS"));
1400 fprintf(fic,"<table>\n<tr><th></th><th>%s</th><th>%s</th></tr>\n",L("WORD"),L("OCCURRENCES"));
1401 for (i=0;i<NBWORDS;i++)
1402 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);
1403 fprintf(fic,"</table>\n</div>\n\n");
1407 fprintf(fic,"<div id=\"irssistats_bignumbers\">\n<h2>%s</h2>\n",L("BIGNUMBERS"));
1408 fprintf(fic,"<table>\n<tr><th>%s</th><th>%s</th><th>%s</th></tr>\n",L("NICK"),L("NUMBERS"),L("NBLINES"));
1409 for (i=0;i<NBCOUNTERS;i++)
1413 for (j=0;j<nbusers;j++) if (users[j].counters[i]>max) max=users[user=j].counters[i];
1414 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]);
1416 fprintf(fic,"</table>\n</div>\n\n");
1419 fprintf(fic,"<div id=\"irssistats_footer\">\n<p>");
1420 fprintf(fic,L("TIME"),totallines,days,(int)(time(NULL)-debut));
1421 fprintf(fic,"</p>\n<p>%s <a href=\"%s\">irssistats %s</a></p>\n",L("FOOTER"),URL,VERSION);
1424 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");
1425 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");
1427 fprintf(fic,"</div>\n\n");
1430 if (logo) fprintf(fic,"<div class=\"logo\"></div>\n\n");
1433 if (strcmp("none",footer)==0)
1435 fprintf(fic,"</div>\n\n</body>\n\n</html>\n");
1439 if ((sfic=fopen(footer,"rt"))==NULL) { fprintf(stderr,"can't open footer file \"%s\"\n",footer); exit(1); }
1440 while ((temp=fread(line,1,MAXLINELENGTH,sfic))) fwrite(line,temp,1,fic);
1447 void parse_config(char *configfile)
1449 void expand(char *path)
1451 char temp[MAXLINELENGTH];
1454 snprintf(temp,MAXLINELENGTH-1,"%s%s",getenv("HOME"),path+1);
1455 temp[MAXLINELENGTH-1]='\0';
1461 char line[MAXLINELENGTH];
1462 char keyword[MAXLINELENGTH];
1463 char value[MAXLINELENGTH];
1467 if (configfile!=NULL)
1469 if ((fic=fopen(configfile,"rt"))==NULL)
1471 fprintf(stderr,"can't open config file : \"%s\"\n",configfile);
1477 snprintf(line,MAXLINELENGTH-1,"%s/.irssistats",getenv("HOME"));
1478 line[MAXLINELENGTH-1]='\0';
1479 if ((fic=fopen(line,"rt"))==NULL)
1480 if ((fic=fopen(GLOBALCONF,"rt"))==NULL)
1482 fprintf(stderr,"can't find config file : \"%s\" nor \"" GLOBALCONF "\"\n",line);
1483 fprintf(stderr,"please give the path to the config file in argument\n");
1488 while (fgets(line,MAXLINELENGTH,fic))
1491 if (*line!=';' && *line!='#' && *line!='/' && *line!='-' && *line!='\n')
1493 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); }
1495 if (strcmp("debug",keyword)==0)
1497 if (strcmp("none",value)==0) debug=0;
1499 if (strcmp("normal",value)==0) debug=1;
1501 if (strcmp("verbose",value)==0) { debug=2; fprintf(stderr,"switching to verbose output\n"); }
1502 else { fprintf(stderr,"unknown value for \"debug\" option, must be \"normal\", \"verbose\" or \"none\"\n"); exit(1); }
1506 if (strcmp("channel",keyword)==0)
1508 if (debug==2) fprintf(stderr,"setting channel name to \"%s\"\n",value);
1509 strcpy(channel,value);
1513 if (strcmp("maintainer",keyword)==0)
1515 if (debug==2) fprintf(stderr,"setting maintainer to \"%s\"\n",value);
1516 strcpy(maintainer,value);
1520 if (strcmp("language",keyword)==0)
1522 if (debug==2) fprintf(stderr,"setting language to \"%s\"\n",value);
1523 for (i=0;i<NBLANGUAGES;i++) if (strcmp(value,keys[i][0][1])==0) { language=i; break; }
1526 fprintf(stderr,"Invalid language : %s\n",value);
1527 fprintf(stderr,"Supported languages :\n");
1528 for (i=0;i<NBLANGUAGES;i++) fprintf(stderr,"%s = %s\n",keys[i][0][1],keys[i][0][0]);
1534 if (strcmp("theme",keyword)==0)
1536 if (debug==2) fprintf(stderr,"setting theme to \"%s\"\n",value);
1537 strcpy(theme,value);
1541 if (strcmp("refresh_time",keyword)==0)
1543 refresh_time=atoi(value);
1544 if (debug==2) fprintf(stderr,"setting refresh_time to \"%d\"\n",refresh_time);
1548 if (strcmp("photo_size",keyword)==0)
1550 photo_size=atoi(value);
1551 if (debug==2) fprintf(stderr,"setting photo_size to \"%d\"\n",photo_size);
1555 if (strcmp("w3c_link",keyword)==0)
1557 if (debug==2) fprintf(stderr,"setting w3c_link to \"%s\"\n",value);
1558 if (strcmp("no",value)==0) w3c_link=0;
1559 else if (strcmp("yes",value)==0) w3c_link=1;
1560 else { fprintf(stderr,"unknown value for \"w3c_link\" option, must be \"yes\" or \"no\"\n"); exit(1); }
1564 if (strcmp("logo",keyword)==0)
1566 if (debug==2) fprintf(stderr,"setting logo to \"%s\"\n",value);
1567 if (strcmp("no",value)==0) logo=0;
1568 else if (strcmp("yes",value)==0) logo=1;
1569 else { fprintf(stderr,"unknown value for \"logo\" option, must be \"yes\" or \"no\"\n"); exit(1); }
1573 if (strcmp("header",keyword)==0)
1576 if (debug==2) fprintf(stderr,"setting header to \"%s\"\n",value);
1577 strcpy(header,value);
1581 if (strcmp("footer",keyword)==0)
1584 if (debug==2) fprintf(stderr,"setting footer to \"%s\"\n",value);
1585 strcpy(footer,value);
1589 if (strcmp("input",keyword)==0)
1592 if (debug==2) fprintf(stderr,"parsing log file \"%s\"\n",value);
1597 if (strcmp("nickfile",keyword)==0)
1600 if (debug==2) fprintf(stderr,"nick alias using file \"%s\"\n",value);
1602 fprintf(stderr,"no support for nickfile in WIN32 version\n");
1609 if (strcmp("photofile",keyword)==0)
1612 if (debug==2) fprintf(stderr,"parsing photo file \"%s\"\n",value);
1617 if (strcmp("output",keyword)==0)
1620 if (debug==2) fprintf(stderr,"generating xhtml file \"%s\"\n",value);
1622 if (L("CHARSET")=="KOI8-R") bestruswords(ruswords,0);
1625 /* reset variables */
1630 for (i=0;i<24*4;i++) hours[i]=0;
1633 freeruswords(&ruswords);
1634 for (i=0;i<NBWORDS;i++) topwords[i].nb=0;
1637 for (i=0;i<nbusers;i++)
1639 free(users[i].photo);
1640 users[i].photo=NULL;
1645 if (strcmp("top_words",keyword)==0)
1647 if (debug==2) fprintf(stderr,"setting top_words to \"%s\"\n",value);
1648 if (strcmp("no",value)==0) top_words=0;
1649 else if (strcmp("yes",value)==0) top_words=1;
1650 else { fprintf(stderr,"unknown value for \"top_words\" option, must be \"yes\" or \"no\"\n"); exit(1); }
1654 if (strcmp("ranking",keyword)==0)
1656 if (strcmp("lines",value)==0) ranking=0;
1658 if (strcmp("words",value)==0) ranking=1;
1660 if (strcmp("letters",value)==0) ranking=2;
1661 else { fprintf(stderr,"unknown value for \"ranking\" option, must be \"lines\", \"words\" or \"letters\"\n"); exit(1); }
1665 if (strcmp("quarter",keyword)==0)
1667 if (debug==2) fprintf(stderr,"setting quarter to \"%s\"\n",value);
1668 if (strcmp("no",value)==0) quarter=0;
1669 else if (strcmp("yes",value)==0) quarter=1;
1670 else { fprintf(stderr,"unknown value for \"quarter\" option, must be \"yes\" or \"no\"\n"); exit(1); }
1673 else { fprintf(stderr,"error in config file : \"%s\" is an unknown keyword (line %d)\n",keyword,configlines); exit(1); }
1679 int main(int argc,char *argv[])
1681 (void) setlocale(LC_ALL, "");
1683 if ((users=malloc(maxusers*sizeof(struct user)))==NULL) { fprintf(stderr,"unable to malloc memory\n"); exit(1); }
1684 srand(debut=time(NULL));
1685 if (argc==1) parse_config(NULL);
1686 else if (argc==2) parse_config(argv[1]);
1689 fprintf(stderr,"Usage : %s [/path/to/file.conf]\n",argv[0]);
1690 fprintf(stderr,"Version : irssistats %s\n",VERSION);