/*
 * irssistats version 0.75
 *
 * This tool generates IRC stats based on irssi logs.
 * Usage: irssistats [/path/to/file.conf]
 *
 * Copyright (C) 2002-2004  Antoine Jacquet <royale@zerezo.com>
 * http://royale.zerezo.com/irssistats/
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */


#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <locale.h>
#ifdef __WIN32__
#define GLOBALCONF "irssistats.conf"
#else
#define GLOBALCONF "/usr/local/etc/irssistats.conf"
#include <regex.h>
#endif

/* Config */
#define BASEUSERS 1000
#define MAXNICKLENGTH 50
#define MAXLINELENGTH 2000
#define MAXQUOTELENGTH 100
#define NBUSERS 50
#define NBUSERSTIME 10
#define NBURLS 5
#define NBTOPICS 5
#define NBWORDS 20
#define MINWORDLENGTH 5

/* irssistats */
#define VERSION "0.75"
#define URL "http://royale.zerezo.com/irssistats/"

/* Counters */
#define D_SMILE     0
#define D_FROWN     1
#define D_EXCLAM    2
#define D_QUESTION  3
#define D_ME        4
#define D_TOPIC     5
#define D_MODE      6
#define D_KICK      7
#define D_KICKED    8
#define D_URL       9
#define D_JOIN      10
#define D_NICK      11
#define D_MONOLOGUE 12
#define NBCOUNTERS  13
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"};

/* Languages */
#define NBLANGUAGES 11
#define NBKEYS 41
char *keys[NBLANGUAGES][NBKEYS+1][2]= /* first key used for language name and abbreviation */
{
  { /* English language */
    { "English",      "en" },
    { "CHARSET",      "ISO-8859-1" },
    { "HEADER",       "Statistics for %s by %s" },
    { "LEGEND",       "Legend" },
    { "LASTDAYS",     "Last days statistics" },
    { "LASTWEEKS",    "Last weeks statistics" },
    { "LASTMONTHS",   "Last months statistics" },
    { "TOPHOURS",     "Hourly statistics" },
    { "TOPUSERS",     "Most active people" },
    { "OTHERS",       "There are %d left not ranked..." },
    { "NBLINES",      "lines" },
    { "NICK",         "nick" },
    { "AVGLETTERS",   "letters/lines" },
    { "HOURS",        "hours" },
    { "QUOTE",        "random message" },
    { "TOPUSERSTIME", "Most active people by time of day" },
    { "RANDTOPICS",   "Some topics" },
    { "CHANGEDBY",    "changed by" },
    { "NEWTOPIC",     "new topic" },
    { "RANDURLS",     "Some URLs" },
    { "POSTEDBY",     "posted by" },
    { "POSTEDURL",    "URL" },
    { "TOPWORDS",     "Most used words" },
    { "WORD",         "word" },
    { "OCCURRENCES",  "occurrences" },
    { "BIGNUMBERS",   "Some big numbers..." },
    { "NUMBERS",      "numbers" },
    { "TIME",         "%d lines (%d days) parsed in %d seconds" },
    { "FOOTER",       "Statistics generated by" },
    { "C_SMILE",      "is often happy :)" },
    { "C_FROWN",      "is often sad :(" },
    { "C_EXCLAM",     "yells a lot !" },
    { "C_QUESTION",   "asks a lot of questions ?" },
    { "C_ME",         "likes /me command" },
    { "C_TOPIC",      "often changes the topic" },
    { "C_MODE",       "often changes the modes" },
    { "C_KICK",       "likes to /kick" },
    { "C_KICKED",     "is often kicked" },
    { "C_URL",        "posts many URLs" },
    { "C_JOIN",       "doesn't know wether to stay or quit" },
    { "C_NICK",       "often changes his nick" },
    { "C_MONOLOGUE",  "speaks a lot of monologues" }
  },
  { /* French language */
    { "French",       "fr" },
    { "CHARSET",      "ISO-8859-1" },
    { "HEADER",       "Statistiques de %s par %s" },
    { "LEGEND",       "L&eacute;gende" },
    { "LASTDAYS",     "Statistiques des derniers jours" },
    { "LASTWEEKS",    "Statistiques des derni&egrave;res semaines" },
    { "LASTMONTHS",   "Statistiques des derniers mois" },
    { "TOPHOURS",     "Statistiques horaires" },
    { "TOPUSERS",     "Personnes les plus actives" },
    { "OTHERS",       "Il reste %d personnes non class&eacute;es..." },
    { "NBLINES",      "lignes" },
    { "NICK",         "nick" },
    { "AVGLETTERS",   "lettres/lignes" },
    { "HOURS",        "heures" },
    { "QUOTE",        "message al&eacute;atoire" },
    { "TOPUSERSTIME", "Personnes les plus actives par p&eacute;riode de la journ&eacute;e" },
    { "RANDTOPICS",   "Quelques topics" },
    { "CHANGEDBY",    "chang&eacute; par" },
    { "NEWTOPIC",     "nouveau topic" },
    { "RANDURLS",     "Quelques URLs" },
    { "POSTEDBY",     "post&eacute;e par" },
    { "POSTEDURL",    "URL" },
    { "TOPWORDS",     "Mots les plus utilis&eacute;s" },
    { "WORD",         "mot" },
    { "OCCURRENCES",  "occurrences" },
    { "BIGNUMBERS",   "Quelques grands nombres..." },
    { "NUMBERS",      "nombres" },
    { "TIME",         "%d lignes (%d jours) trait&eacute;es en %d secondes" },
    { "FOOTER",       "Statistiques g&eacute;n&eacute;r&eacute;es par" },
    { "C_SMILE",      "est souvent heureux :)" },
    { "C_FROWN",      "est souvent triste :(" },
    { "C_EXCLAM",     "hurle beaucoup !" },
    { "C_QUESTION",   "pose beaucoup de questions ?" },
    { "C_ME",         "aime la commande /me" },
    { "C_TOPIC",      "change souvent le topic" },
    { "C_MODE",       "change souvent les modes" },
    { "C_KICK",       "aime la commande /kick" },
    { "C_KICKED",     "est souvent kick&eacute;" },
    { "C_URL",        "poste beaucoup d'URLs" },
    { "C_JOIN",       "ne sait pas s'il doit rester ou partir" },
    { "C_NICK",       "change souvent de nick" },
    { "C_MONOLOGUE",  "parle beaucoup de monologues" }
  },
  { /* German language */
    /* contributed by Valentin Gelhorn <valentin.gelhorn@web.de> */
    { "German",       "de" },
    { "CHARSET",      "ISO-8859-1" },
    { "HEADER",       "Statistiken f&uuml;r %s von %s" },
    { "LEGEND",       "Legende" },
    { "LASTDAYS",     "Statistik der letzten Tage" },
    { "LASTWEEKS",    "Last weeks statistics" },
    { "LASTMONTHS",   "Last months statistics" },
    { "TOPHOURS",     "St&uuml;ndliche Statistik" },
    { "TOPUSERS",     "Die aktivsten Personen" },
    { "OTHERS",       "Es bleiben noch %d uneingetragene" },
    { "NBLINES",      "Zeilen" },
    { "NICK",         "Nick" },
    { "AVGLETTERS",   "Buchstaben/Zeile" },
    { "HOURS",        "Stunden" },
    { "QUOTE",        "Zuf&auml;llig ausgewaehlte Zitate" },
    { "TOPUSERSTIME", "Die aktivsten Personen zur bestimmten Tageszeit" },
    { "RANDTOPICS",   "Ein paar Topics" },
    { "CHANGEDBY",    "Gesetzt von" },
    { "NEWTOPIC",     "Neues topic" },
    { "RANDURLS",     "Ein paar URLs" },
    { "POSTEDBY",     "Geschrieben von" },
    { "POSTEDURL",    "URL" },
    { "TOPWORDS",     "Am h&auml;ufigsten benutze W&ouml;rter" },
    { "WORD",         "Wort" },
    { "OCCURRENCES",  "Vorkommen" },
    { "BIGNUMBERS",   "Ein paar grosse Zahlen" },
    { "NUMBERS",      "Zahlen" },
    { "TIME",         "%d Zeilen (%d Tage) analysiert in %d Sekunden" },
    { "FOOTER",       "Statistiken wurden erstellt von" },
    { "C_SMILE",      "ist oft gl&uuml;klich :)" },
    { "C_FROWN",      "ist oft traurig :(" },
    { "C_EXCLAM",     "schreit oft !" },
    { "C_QUESTION",   "stellt viele Fragen ?" },
    { "C_ME",         "mag /me'en" },
    { "C_TOPIC",      "aendert oft das Topic" },
    { "C_MODE",       "aendert oft die Modes" },
    { "C_KICK",       "mag /kick'en" },
    { "C_KICKED",     "wird oft gekickt"},
    { "C_URL",        "schreibt viele URLs"},
    { "C_JOIN",       "kann sich nicht entscheiden ob er bleiben oder gehen soll" },
    { "C_NICK",       "&auml;ndert oft seinen Nick" },
    { "C_MONOLOGUE",  "spricht oft Monologe" }
  },
  { /* Spanish language */
    /* contributed by Alex <ainaker@gmx.net> */
    { "Spanish",      "es" },
    { "CHARSET",      "ISO-8859-1" },
    { "HEADER",       "Estad&iacute;sticas de %s por %s" },
    { "LEGEND",       "Leyenda" },
    { "LASTDAYS",     "Estad&iacute;sticas de los &uacute;ltimos d&iacute;as" },
    { "LASTWEEKS",    "Last weeks statistics" },
    { "LASTMONTHS",   "Last months statistics" },
    { "TOPHOURS",     "Estad&iacute;sticas por horas" },
    { "TOPUSERS",     "Los que m&aacute;s escriben" },
    { "OTHERS",       "Hay %d m&aacute;s que no llegaron..." },
    { "NBLINES",      "l&iacute;neas" },
    { "NICK",         "nick" },
    { "AVGLETTERS",   "letras por l&iacute;nea" },
    { "HOURS",        "horas" },
    { "QUOTE",        "Frase aleatoria" },
    { "TOPUSERSTIME", "Los que m&aacute;s escriben seg&uacute;n la hora" },
    { "RANDTOPICS",   "Algunos topics" },
    { "CHANGEDBY",    "Puestos por" },
    { "NEWTOPIC",     "topic" },
    { "RANDURLS",     "Algunas URLs" },
    { "POSTEDBY",     "puestas por" },
    { "POSTEDURL",    "URL" },
    { "TOPWORDS",     "Palabras m&aacute;s usadas" },
    { "WORD",         "Palabra" },
    { "OCCURRENCES",  "Frecuencia" },
    { "BIGNUMBERS",   "Algunos datos..." },
    { "NUMBERS",      "N&uacute;mero de veces" },
    { "TIME",         "%d lineas (%d d&iacute;as) procesadas en %d segundos" },
    { "FOOTER",       "Estad&iacute;sticas generadas por" },
    { "C_SMILE",      "Suele estar fel&iacute;z :)" },
    { "C_FROWN",      "Suele estar triste :(" },
    { "C_EXCLAM",     "Grita mucho !" },
    { "C_QUESTION",   "Hace muchas preguntas ?" },
    { "C_ME",         "Abusa del comando /me" },
    { "C_TOPIC",      "Suele cambiar el topic" },
    { "C_MODE",       "Cambia a veces los modos del canal" },
    { "C_KICK",       "Le gusta patear" },
    { "C_KICKED",     "Es pateado con frecuencia" },
    { "C_URL",        "Pone muchas URLs" },
    { "C_JOIN",       "No sabe si irse o quedarse" },
    { "C_NICK",       "Cambia mucho de nick" },
    { "C_MONOLOGUE",  "Habla solo" }
  },
  { /* Polish language */
    /* contributed by Jakub Jankowski <shasta@atn.pl> */
    { "Polish",       "pl" },
    { "CHARSET",      "ISO-8859-2" },
    { "HEADER",       "Statystyki dla %s zebrane przez %s" },
    { "LEGEND",       "Legenda" },
    { "LASTDAYS",     "Statystyki z ostatnich dni" },
    { "LASTWEEKS",    "Last weeks statistics" },
    { "LASTMONTHS",   "Last months statistics" },
    { "TOPHOURS",     "Statystyki godzinowe" },
    { "TOPUSERS",     "Najaktywniejsi" },
    { "OTHERS",       "Jest jeszcze %d nie sklasyfikowanych..." },
    { "NBLINES",      "linii" },
    { "NICK",         "nick" },
    { "AVGLETTERS",   "liter/linię" },
    { "HOURS",        "godziny" },
    { "QUOTE",        "losowa wypowiedź" },
    { "TOPUSERSTIME", "Najaktywniejsi według pory dnia" },
    { "RANDTOPICS",   "Kilka topików" },
    { "CHANGEDBY",    "ustawiony przez" },
    { "NEWTOPIC",     "topik" },
    { "RANDURLS",     "Kilka URL-i" },
    { "POSTEDBY",     "podał(a)" },
    { "POSTEDURL",    "URL" },
    { "TOPWORDS",     "Najczęściej występujące słowa" },
    { "WORD",         "słowo" },
    { "OCCURRENCES",  "wystąpień" },
    { "BIGNUMBERS",   "Kilka wielkopomnych liczb..." },
    { "NUMBERS",      "kategorie" },
    { "TIME",         "Dokonano analizy %d linii (obejmujących %d dni) w czasie %d sekund" },
    { "FOOTER",       "Statystyki wygenerowane przez" },
    { "C_SMILE",      "jest często szczesliwy(a) :)" },
    { "C_FROWN",      "jest często smutny(a) :(" },
    { "C_EXCLAM",     "często KRZYCZY!" },
    { "C_QUESTION",   "zadaje dużo pytań?" },
    { "C_ME",         "lubi używać /me" },
    { "C_TOPIC",      "często zmienia topik" },
    { "C_MODE",       "często zmienia tryby kanału" },
    { "C_KICK",       "lubi kopać" },
    { "C_KICKED",     "często wykopywany(a)" },
    { "C_URL",        "podaje dużo URL-i" },
    { "C_JOIN",       "nie wie czy zostać, czy wyjść" },
    { "C_NICK",       "często zmienia nick" },
    { "C_MONOLOGUE",  "uwielbia monologi" }
  },
  { /* Polish language */
    /* contributed by Piotr Jarmuz <coreupper@yahoo.com> */
    { "Polish",       "pl-old" },
    { "CHARSET",      "ISO-8859-1" },
    { "HEADER",       "Statystyki dla %s przez %s" },
    { "LEGEND",       "Legenda" },
    { "LASTDAYS",     "Statystyki z ostatnich dni" },
    { "LASTWEEKS",    "Last weeks statistics" },
    { "LASTMONTHS",   "Last months statistics" },
    { "TOPHOURS",     "Statystyki godzinne" },
    { "TOPUSERS",     "Najaktywniejsi ludzie" },
    { "OTHERS",       "Zostalo jeszcze %d nie sklasyfikowanych..." },
    { "NBLINES",      "linie" },
    { "NICK",         "nick" },
    { "AVGLETTERS",   "litery/linie" },
    { "HOURS",        "godziny" },
    { "QUOTE",        "przypadkowa wiadomosc" },
    { "TOPUSERSTIME", "Najaktywniejsi ludzie wedlug czasu dnia" },
    { "RANDTOPICS",   "Pare tematow" },
    { "CHANGEDBY",    "zmienione przez" },
    { "NEWTOPIC",     "nowy temat" },
    { "RANDURLS",     "Pare URL-i" },
    { "POSTEDBY",     "wyslane przez" },
    { "POSTEDURL",    "URL" },
    { "TOPWORDS",     "Najczestsze slowa" },
    { "WORD",         "slowo" },
    { "OCCURRENCES",  "wystapienia" },
    { "BIGNUMBERS",   "Pare wielkich liczb..." },
    { "NUMBERS",      "liczby" },
    { "TIME",         "%d linii (%d dni) sparsowanych w %d sekund" },
    { "FOOTER",       "Statystyki wygenerowane przez" },
    { "C_SMILE",      "jest czesto szczesliwy :)" },
    { "C_FROWN",      "jest czesto smutny :(" },
    { "C_EXCLAM",     "duzo krzyczy !" },
    { "C_QUESTION",   "zadaje duzo pytan ?" },
    { "C_ME",         "lubi /mnie polecenie" },
    { "C_TOPIC",      "czesto zmienia temat" },
    { "C_MODE",       "czesto zmienia tryb" },
    { "C_KICK",       "lubi /kopac" },
    { "C_KICKED",     "czesto go wykopuja" },
    { "C_URL",        "wysyla duzo URL-i" },
    { "C_JOIN",       "nie wie czy zostac czy wyjsc" },
    { "C_NICK",       "czesto zmienia swojego nicka" },
    { "C_MONOLOGUE",  "czesto mowi monologiem" }
  },
  { /* Finnish language */
    /* contributed by Antti Huopana <ahuopana@ratol.fi> */
    { "Finnish",      "fi" },
    { "CHARSET",      "ISO-8859-1" },
    { "HEADER",       "Kanavan %s tilastot - %s" },
    { "LEGEND",       "Merkkien selitykset" },
    { "LASTDAYS",     "Viime päivien tilastot" },
    { "LASTWEEKS",    "Last weeks statistics" },
    { "LASTMONTHS",   "Last months statistics" },
    { "TOPHOURS",     "Tilastot tunneittain" },
    { "TOPUSERS",     "Aktiivisimmat ihmiset" },
    { "OTHERS",       "Jäljelle jäi %d joita ei listattu..." },
    { "NBLINES",      "rivit" },
    { "NICK",         "nikki" },
    { "AVGLETTERS",   "kirjainta/rivi" },
    { "HOURS",        "tunnit" },
    { "QUOTE",        "satunnainen viesti" },
    { "TOPUSERSTIME", "Vuorokauden ajan mukaan aktiivisimmat" },
    { "RANDTOPICS",   "Joitakin aiheita" },
    { "CHANGEDBY",    "vaihtaja" },
    { "NEWTOPIC",     "aihe" },
    { "RANDURLS",     "Joitakin URLeja" },
    { "POSTEDBY",     "lähettäjä" },
    { "POSTEDURL",    "URL" },
    { "TOPWORDS",     "Eniten käytettyjä sanoja" },
    { "WORD",         "sana" },
    { "OCCURRENCES",  "käytetty" },
    { "BIGNUMBERS",   "Joitakin isoja lukuja..." },
    { "NUMBERS",      "luvut" },
    { "TIME",         "%d riviä (%d päivää) parsittu %d sekunnissa" },
    { "FOOTER",       "Tilastot on generoinut" },
    { "C_SMILE",      "on usein iloinen :)" },
    { "C_FROWN",      "on usein surullinen :(" },
    { "C_EXCLAM",     "möykkää paljon !" },
    { "C_QUESTION",   "kyselee liikaa ?" },
    { "C_ME",         "pitää itsestään" },
    { "C_TOPIC",      "vaihtaa usein aihetta" },
    { "C_MODE",       "haluaa elää muuttuvassa maailmassa" },
    { "C_KICK",       "pitää potkimisesta" },
    { "C_KICKED",     "tykkää tulla potkituksi" },
    { "C_URL",        "surffailee liikaa" },
    { "C_JOIN",       "ei tiedä ollakko vai eikö olla" },
    { "C_NICK",       "kärsii identiteettiongelmista" },
    { "C_MONOLOGUE",  "höpöttää paljon itsekseen" }
  },
  { /* Italian language */
    /* contributed by Coviello Giuseppe <giuseppecoviello@tin.it> <http://coviello.altervista.org> */
    { "Italian",      "it" },
    { "CHARSET",      "ISO-8859-1" },
    { "HEADER",       "Statistiche per il canale %s di %s" },
    { "LEGEND",       "Legenda" },
    { "LASTDAYS",     "Statistiche degli ultimi giorni" },
    { "LASTWEEKS",    "Last weeks statistics" },
    { "LASTMONTHS",   "Last months statistics" },
    { "TOPHOURS",     "Statistiche in ore" },
    { "TOPUSERS",     "Utenti pi&ugrave; attivi" },
    { "OTHERS",       "Ci sono %d utenti non classificati..." },
    { "NBLINES",      "righe" },
    { "NICK",         "nick" },
    { "AVGLETTERS",   "lettere/righe" },
    { "HOURS",        "ore" },
    { "QUOTE",        "messaggio casuale" },
    { "TOPUSERSTIME", "Utenti piů attivi del giorno (divisi per fasce orarie)" },
    { "RANDTOPICS",   "Alcuni topic" },
    { "CHANGEDBY",    "cambiato da" },
    { "NEWTOPIC",     "nuovo topic" },
    { "RANDURLS",     "ALcuni URL" },
    { "POSTEDBY",     "postato da" },
    { "POSTEDURL",    "URL" },
    { "TOPWORDS",     "Le parole piů usate" },
    { "WORD",         "parola" },
    { "OCCURRENCES",  "usata" },
    { "BIGNUMBERS",   "Alcuni numeri ..." },
    { "NUMBERS",      "numeri" },
    { "TIME",         "%d righe (%d giorni) esaminate in %d secondi" },
    { "FOOTER",       "Statistiche generate da" },
    { "C_SMILE",      "č spesso felice :)" },
    { "C_FROWN",      "č spesso triste :(" },
    { "C_EXCLAM",     "esclama molto !" },
    { "C_QUESTION",   "fa molte domande ?" },
    { "C_ME",         "ama il comando /me" },
    { "C_TOPIC",      "cambia spesso il topic" },
    { "C_MODE",       "cambia spesso i mode" },
    { "C_KICK",       "ama /kick(are)" },
    { "C_KICKED",     "č kickato spesso" },
    { "C_URL",        "posta molti URL" },
    { "C_JOIN",       "non sa se č ora di andare o restare" },
    { "C_NICK",       "cambia spesso il nick" },
    { "C_MONOLOGUE",  "fa molti monologhi" }
  },
  { /* Dutch language */
    /* contributed by Jeroen Ubbink <crasp@blackbyte.nl> */
    /* updated by Wouter Horré <wouter@ligezin.be> */
    { "Dutch",        "nl" },
    { "CHARSET",      "ISO-8859-1" },
    { "HEADER",       "Statistieken voor %s door %s" },
    { "LEGEND",       "Legenda" },
    { "LASTDAYS",     "Statistieken van de laatste dagen" },
    { "LASTWEEKS",    "Last weeks statistics" },
    { "LASTMONTHS",   "Last months statistics" },
    { "TOPHOURS",     "Statistieken per uur" },
    { "TOPUSERS",     "Meest actieve mensen" },
    { "OTHERS",       "Er zijn nog %d mensen die de top niet haalden..." },
    { "NBLINES",      "regels" },
    { "NICK",         "nick" },
    { "AVGLETTERS",   "letters/lijn" },
    { "HOURS",        "uren" },
    { "QUOTE",        "Willekeurige regel" },
    { "TOPUSERSTIME", "Meest actieve mensen per tijdstip per dag" },
    { "RANDTOPICS",   "Enkele topics" },
    { "CHANGEDBY",    "gewijzigd door" },
    { "NEWTOPIC",     "nieuwe topic" },
    { "RANDURLS",     "Enkele URLs" },
    { "POSTEDBY",     "Geplaatst door" },
    { "POSTEDURL",    "URL" },
    { "TOPWORDS",     "Meest gebruikte woorden" },
    { "WORD",         "woord" },
    { "OCCURRENCES",  "aantal" },
    { "BIGNUMBERS",   "Enkele grote aantallen..." },
    { "NUMBERS",      "numbers" },
    { "TIME",         "%d regels (%d dagen) verwerkt in %d seconden" },
    { "FOOTER",       "Statistieken gegenereerd door" },
    { "C_SMILE",      "is vaak vrolijk :)" },
    { "C_FROWN",      "is vaak droevig :(" },
    { "C_EXCLAM",     "schreeuwt veel !" },
    { "C_QUESTION",   "stelt veel vragen ?" },
    { "C_ME",         "vindt /me een leuk commando" },
    { "C_TOPIC",      "verandert vaak de topic" },
    { "C_MODE",       "verandert vaak de modes" },
    { "C_KICK",       "vindt /kick erg leuk" },
    { "C_KICKED",     "wordt vaak gekickt" },
    { "C_URL",        "plaatst veel URLs" },
    { "C_JOIN",       "twijfelt tussen blijven of gaan" },
    { "C_NICK",       "verandert vaak van nick" },
    { "C_MONOLOGUE",  "spreekt veel monologen" }
  },
  { /* Russian language */
    /* contributed by kamikaze <kamikaze@rss.lv> */
    { "Russian",      "ru" },
    { "CHARSET",      "KOI8-R" },
    { "HEADER",       "óÔÁÔÉÓÔÉËÁ ÄĚŃ %s ĎÔ %s" },
    { "LEGEND",       "ďÂĎÚÎÁŢĹÎÉŃ" },
    { "LASTDAYS",     "óÔÁÔÉÓÔÉËÁ ĐĎÓĚĹÄÎÉČ ÄÎĹĘ" },
    { "LASTWEEKS",    "Last weeks statistics" },
    { "LASTMONTHS",   "Last months statistics" },
    { "TOPHOURS",     "đĎŢÁÓĎ×ÁŃ ÓÔÁÔÉÓÔÉËÁ" },
    { "TOPUSERS",     "áËÔÉ×ÎĹĘŰÉĹ ĚŔÄÉ" },
    { "OTHERS",       "ďÓÔÁĚĎÓŘ %d ÎĹĐĎÄÓŢÉÔÁÎŮČ..." },
    { "NBLINES",      "ÓÔŇĎËÉ" },
    { "NICK",         "ÎÉË" },
    { "AVGLETTERS",   "ÂŐË×Ů/ÓÔŇĎËÉ" },
    { "HOURS",        "ŢÁÓŮ" },
    { "QUOTE",        "ÓĚŐŢÁĘÎĎĹ ÓĎĎÂÝĹÎÉĹ" },
    { "TOPUSERSTIME", "áËÔÉ×ÎĹĘŰÉĹ ĚŔÄÉ ĐĎ ×ŇĹÍĹÎÉ ÄÎŃ" },
    { "RANDTOPICS",   "îĹÓËĎĚŘËĎ ÔĎĐÉËĎ×" },
    { "CHANGEDBY",    "ÉÚÍĹÎŁÎ" },
    { "NEWTOPIC",     "ÎĎ×ŮĘ ÔĎĐÉË" },
    { "RANDURLS",     "îĹÓËĎĚŘËĎ URLĎ×" },
    { "POSTEDBY",     "ĎĐŐÂĚÉËĎ×ÁĚ" },
    { "POSTEDURL",    "URL" },
    { "TOPWORDS",     "ţÁÓÔĎ ÉÓĐĎĚŘÚŐĹÍŮĹ ÓĚĎ×Á" },
    { "WORD",         "ÓĚĎ×Ď" },
    { "OCCURRENCES",  "ĐŇĎÉŰĹÓÔ×ÉŃ" },
    { "BIGNUMBERS",   "îĹÓËĎĚŘËĎ ÂĎĚŘŰÉČ ŢÉÓĹĚ..." },
    { "NUMBERS",      "ŢÉÓĚÁ" },
    { "TIME",         "%d ÓÔŇĎË (%d ÄÎĹĘ) ĎÂŇÁÂĎÔÁÎĎ ÚÁ %d ÓĹËŐÎÄ" },
    { "FOOTER",       "óÔÁÔÉÓÔÉËÁ ÓÇĹÎĹŇÉŇĎ×ÁÎÁ" },
    { "C_SMILE",      "ŢÁÓÔĎ ÓŢÁÓĚÉ× :)" },
    { "C_FROWN",      "ŢÁÓÔĎ ÎĹÓŢÁÓÔĹÎ :(" },
    { "C_EXCLAM",     "ÍÎĎÇĎ ×ĎÓËĚÉĂÁĹÔ !" },
    { "C_QUESTION",   "ÚÁÄÁŁÔ ÍÎĎÇĎ ×ĎĐŇĎÓĎ× ?" },
    { "C_ME",         "ĚŔÂÉÔ /me command" },
    { "C_TOPIC",      "ŢÁÓÔĎ ÍĹÎŃĹÔ ÔĎĐÉË" },
    { "C_MODE",       "ŢÁÓÔĎ ÍĹÎŃĹÔ ŇĹÖÉÍŮ" },
    { "C_KICK",       "ĚŔÂÉÔ /kick" },
    { "C_KICKED",     "ŢÁÓÔĎ ×ŮËÉÄŮ×ÁŔÔ" },
    { "C_URL",        "ĐŐÂĚÉËŐĹÔ ÍÎĎÇĎ URLĎ×" },
    { "C_JOIN",       "ÎĹ ÚÎÁĹÔ - ĎÓÔÁÔŘÓŃ ÉĚÉ ŐĘÔÉ" },
    { "C_NICK",       "ŢÁÓÔĎ ÍĹÎŃĹÔ Ó×ĎĘ ÎÉË" },
    { "C_MONOLOGUE",  "éÓĐĎĚŘÚŐĹÔ ÍÎĎÇĎ ÍĎÎĎĚĎÇĎ×" }
  },
  { /* Estonian language */
    /* contributed by Martin Vool <mardicas@hot.ee> */
    { "Estonian",     "et" },
    { "CHARSET",      "ISO-8859-4" },
    { "HEADER",       "Statistika kanalile %s on koostanud %s" },
    { "LEGEND",       "Legend" },
    { "LASTDAYS",     "Viimaste päevade statistika" },
    { "LASTWEEKS",    "Last weeks statistics" },
    { "LASTMONTHS",   "Last months statistics" },
    { "TOPHOURS",     "Tunni statistika" },
    { "TOPUSERS",     "Kőige aktiivsemad inimesed" },
    { "OTHERS",       "%d inimest on rääkinud" },
    { "NBLINES",      "rida" },
    { "NICK",         "nimi" },
    { "AVGLETTERS",   "tähte/rida" },
    { "HOURS",        "kell" },
    { "QUOTE",        "suvaline teade" },
    { "TOPUSERSTIME", "Kőige aktiivsemad inimesed päeva aja järgi" },
    { "RANDTOPICS",   "Mőned topicud" },
    { "CHANGEDBY",    "muutis" },
    { "NEWTOPIC",     "topicud" },
    { "RANDURLS",     "Mőned aadressid" },
    { "POSTEDBY",     "postitas" },
    { "POSTEDURL",    "URL" },
    { "TOPWORDS",     "Enim kasutatud sőnad" },
    { "WORD",         "sőna" },
    { "OCCURRENCES",  "sagedus" },
    { "BIGNUMBERS",   "Mőned suured numbrid" },
    { "NUMBERS",      "iseloom" },
    { "TIME",         "%d rida (%d päeva) on möödunud %d sekundit" },
    { "FOOTER",       "Statistika on koostanud" },
    { "C_SMILE",      "on tihti őnnelik :)" },
    { "C_FROWN",      "on tihti kurb :(" },
    { "C_EXCLAM",     "pőrnitseb palju" },
    { "C_QUESTION",   "küsib palju küsimusi" },
    { "C_ME",         "/me manjakk" },
    { "C_TOPIC",      "vahetab tihti topicut" },
    { "C_MODE",       "vahetab tihti modesid" },
    { "C_KICK",       "kickib palju" },
    { "C_KICKED",     "saab tihti kicke" },
    { "C_URL",        "reklaamib palju" },
    { "C_JOIN",       "sőelub sisse ja välja" },
    { "C_NICK",       "vahetab pidevalt nime" },
    { "C_MONOLOGUE",  "räägib palju monolooge" }
  }
};

int language=0; /* default to english */

char *L(char *key)
{
  int i;
  for (i=1;i<=NBKEYS;i++) if (strcmp(key,keys[language][i][0])==0) return(keys[language][i][1]);
  fprintf(stderr,"unknown language key: %s\n",key);
  return("");
}

/* Variables */

int debug=1; /* 0 = none ; 1 = normal ; 2 = verbose */
char channel[MAXLINELENGTH]="set_channel_in_config_file";
char maintainer[MAXLINELENGTH]="set_maintainer_in_config_file";
char theme[MAXLINELENGTH]="default,biseau,blue,dark,damier,grayscale,namour,niflheim,pisg,zeduel,zerezo";
int refresh_time=0; /* 0 = disabled */
int w3c_link=1; /* 0 = disabled */
int logo=1; /* 0 = disabled */
char header[MAXLINELENGTH]="none";
char footer[MAXLINELENGTH]="none";
int totallines=0;
time_t debut;
int top_words=1; /* 0 = disabled */
int ranking=0; /* 0 = lines ; 1 = words ; 2 = letters */
int quarter=0; /* 1 = enabled */
int months=0; /* 1 = enabled */
int weeks=0; /* 1 = enabled */
int photo_size=60;

struct user
{
  char nick[MAXNICKLENGTH];
  int lines;
  int words;
  int letters;
  int hours[4];
  char quote[MAXQUOTELENGTH+1];
  int counters[NBCOUNTERS];
  char *photo;
  int temp;
} *users;
int nbusers=0;
int maxusers=BASEUSERS;

struct
{
  char nick[MAXNICKLENGTH];
  char url[MAXLINELENGTH];
  char shorturl[MAXQUOTELENGTH+1];
} urls[NBURLS];
int nburls=0;

struct
{ 
  char nick[MAXNICKLENGTH];
  char topic[MAXQUOTELENGTH+1];
} topics[NBTOPICS];
int nbtopics=0;

struct
{
  int lines;
  int hours[4];
} lastdays[31], lastweeks[31], lastmonths[31];
int days=0;
char currday[16];
int currwday=-1, currmon=-1;

int hours[24*4];
int lines=0;

struct letter
{
  int nb;
  struct letter *next[26];
} words;

struct
{
  int nb;
  char word[MAXLINELENGTH];
} topwords[NBWORDS];

struct rusletter
{
  int nb;
  struct rusletter *next[33];
} ruswords;

#define isletter(c) (((c>='a')&&(c<='z'))||((c>='A')&&(c<='Z')))
#define lowercase(c) (((c>='A')&&(c<='Z'))?c-'A'+'a':c)
//#define lowercase(c) (c | 0x20)
#define isrusletter(c) (memchr(koi,c,66)==NULL?0:1)

const char koi[] = {
193,194,215,199,196,197,163,214,218,201,202,203,204,205,206,207,208,210,
211,212,213,198,200,195,222,219,221,216,223,217,220,192,209,
225,226,247,231,228,229,179,246,250,233,234,235,236,237,238,239,240,242,
243,244,245,230,232,227,254,251,253,248,255,249,252,224,241
};
int lowruscase(char c)
{
  char *ctmp_p;
  int ch=0;
  if (memchr(koi,c,66)==NULL) return koi[0];
  ch=strlen(koi)-strlen(memchr(koi,c,66));
  if (ch>33) return ch-33; else return ch;
}
/* cp1251 for encoding into koi8-r
const char win[] = {
224,225,226,227,228,229,184,230,231,232,233,234,235,236,237,238,239,240,
241,242,243,244,245,246,247,248,249,252,250,251,253,254,255,
192,193,194,195,196,197,168,198,199,200,201,202,203,204,205,206,207,208,
209,210,211,212,213,214,215,216,217,220,218,219,221,222,223
};*/



int findruswords(char *message)
{
  int i,c,n=0;
  //char *tmp_p;
  struct rusletter *pos,*tmp;
  for (;;)
  {
    while (!isrusletter(*message)) if (*message=='\0') return n; else message++;
    pos=&ruswords;
    while (isrusletter(*message))
    {
      c=lowruscase(*message);
      /*tmp_p=memchr(koi,message[0],33);
      if (tmp_p==NULL) return n;
      c=strlen(koi)-strlen(tmp_p);*/
      if (pos->next[(int)c]==NULL)
      {
        tmp=malloc(sizeof(struct rusletter));
        if (tmp==NULL)
        {
          fprintf(stderr, "findruswords(): malloc failure\n");
          exit(1);
        }
        tmp->nb=0;
        for (i=0;i<33;i++) tmp->next[i]=NULL;
        pos->next[(int)c]=tmp;
      }
      pos=pos->next[(int)c];
      message++; 
    }
    pos->nb++;
    n++;
  }
  return n;
}


void freeruswords(struct rusletter *pos)
{
  int i;
  for (i=0;i<33;i++) if (pos->next[i]!=NULL)
  {
    freeruswords(pos->next[i]);
    free(pos->next[i]);
    (*pos).next[i]=NULL;
  }
}

int findwords(char *message)
{
  int i,c,n=0;
  struct letter *pos,*tmp;
  for (;;)
  {
    while (!isletter(*message)) if (*message=='\0') return n; else message++;
    pos=&words;
    while (isletter(*message))
    {
      c=lowercase(*message)-'a';
      if (pos->next[(int)c]==NULL)
      {
        tmp=malloc(sizeof(struct letter));
        if (tmp==NULL)
        {
          fprintf(stderr, "findwords(): malloc failure\n");
          exit(1);
        }
        tmp->nb=0;
        for (i=0;i<26;i++) tmp->next[i]=NULL;
        pos->next[(int)c]=tmp;
      }
      pos=pos->next[(int)c];
      message++; 
    }
    pos->nb++;
    n++;
  }
  return n;
}

char tempword[MAXLINELENGTH];
void bestwords(struct letter pos,int cur)
{
  int i,j;
  if ((cur>=MINWORDLENGTH)&&(pos.nb>topwords[NBWORDS-1].nb))
  {
    for (i=0;pos.nb<topwords[i].nb;i++);
    for (j=NBWORDS-1;j>i;j--)
    {
      topwords[j].nb=topwords[j-1].nb;
      strcpy(topwords[j].word,topwords[j-1].word);
    }
    topwords[i].nb=pos.nb;
    strcpy(topwords[i].word,tempword);
  }
  for (i=0;i<26;i++) if (pos.next[i]!=NULL)
  {
    tempword[cur]='a'+i;
    bestwords(*(pos.next[i]),cur+1);
  }
  tempword[cur]='\0';
}

void bestruswords(struct rusletter pos,int cur)
{
  int i,j;
  if ((cur>=MINWORDLENGTH)&&(pos.nb>topwords[NBWORDS-1].nb))
  {
    for (i=0;pos.nb<topwords[i].nb;i++);
    for (j=NBWORDS-1;j>i;j--)
    {
      topwords[j].nb=topwords[j-1].nb;
      strcpy(topwords[j].word,topwords[j-1].word);
    }
    topwords[i].nb=pos.nb;
    strcpy(topwords[i].word,tempword);
  }
  for (i=0;i<33;i++) if (pos.next[i]!=NULL)
  {
    tempword[cur]=koi[i];
    bestruswords(*(pos.next[i]),cur+1);
  }
  tempword[cur]='\0';
}

void freewords(struct letter *pos)
{
  int i;
  for (i=0;i<26;i++) if (pos->next[i]!=NULL)
  {
    freewords(pos->next[i]);
    free(pos->next[i]);
    (*pos).next[i]=NULL;
  }
}

void printhtml(FILE *fic,char *string) /* replace < and > by &lt; and &gt; */
{
  while (*string!='\0')
  {
    switch (*string)
    {
      case '<':fprintf(fic,"&lt;"); break;
      case '>':fprintf(fic,"&gt;"); break;
      case '&':fprintf(fic,"&amp;"); break;
      default:fprintf(fic,"%c",*string); break;
    }
    string++;
  }
  return;
}

int dichotomic(char *nick)
{
  int i,j,start=0,end=nbusers-1,middle;
  while (start<=end)
  {
    middle=(start+end)/2;
    if (strcmp(nick,users[middle].nick)>0) start=middle+1; else end=middle-1;
  }
  if (strcmp(nick,users[start].nick)!=0)
  {
    nbusers++;
    if (nbusers>=maxusers)
    {
      maxusers+=BASEUSERS;
      if (debug==2) fprintf(stderr,"allocating more users : %d\n",maxusers);
      if ((users=realloc(users,maxusers*sizeof(struct user)))==NULL) { fprintf(stderr,"too many users : unable to realloc memory\n"); exit(1); }
    }
    for (i=nbusers-1;i>start;i--)
    {
      strcpy(users[i].nick,users[i-1].nick);
      users[i].lines=users[i-1].lines;
      users[i].words=users[i-1].words;
      users[i].letters=users[i-1].letters;
      for (j=0;j<4;j++) users[i].hours[j]=users[i-1].hours[j];
      strcpy(users[i].quote,users[i-1].quote);
      for (j=0;j<NBCOUNTERS;j++) users[i].counters[j]=users[i-1].counters[j];
      users[i].temp=users[i-1].temp;
      users[i].photo=users[i-1].photo;
    }
    strcpy(users[start].nick,nick);
    users[start].lines=0;
    users[start].words=0;
    users[start].letters=0;
    for (j=0;j<4;j++) users[start].hours[j]=0;
    users[start].quote[0]='\0';
    for (j=0;j<NBCOUNTERS;j++) users[start].counters[j]=0;
    users[start].temp=0;
    users[start].photo=NULL;
  }
  return(start);
}

void day_changed(char* date)
{
  int i,j;
  char newday[16];
  struct tm currdate;

  memcpy(newday, date, 11);
  if (date[13]==':')
    memcpy(newday+11, date+20, 4);
  else
    memcpy(newday+11, date+11, 4);
  newday[15]=0;
  if (memcmp(currday, newday, 15)!=0)
  {
    /* we do not have a "current" day yet? */
    if (currday[0]!=0)
    {
      for (i=30;i>0;i--)
      {
        lastdays[i].lines=lastdays[i-1].lines;
        for (j=0;j<4;j++) lastdays[i].hours[j]=lastdays[i-1].hours[j];
      }
      lastdays[0].lines=0;
      for (j=0;j<4;j++) lastdays[0].hours[j]=0;
      days++;
    }
    memcpy(currday, newday, 15);
    if (debug==2)
      fprintf(stderr, "day %d changed to: %s\n", days, currday);
    
    /* try to parse the date for weeks/months stats */
    if (strptime(currday, "%a %b %d %Y", &currdate))
    {
      /* each monday we change the week number */
      if (currdate.tm_wday == 1)
      {
        for (i=30;i>0;i--)
        {
          lastweeks[i].lines=lastweeks[i-1].lines;
          for (j=0;j<4;j++) lastweeks[i].hours[j]=lastweeks[i-1].hours[j];
        }
        lastweeks[0].lines=0;
        for (j=0;j<4;j++) lastweeks[0].hours[j]=0;
      }
      /* if the month has changed */
      if (currdate.tm_mon != currmon && currmon > 0)
      {
        for (i=30;i>0;i--)
        {
          lastmonths[i].lines=lastmonths[i-1].lines;
          for (j=0;j<4;j++) lastmonths[i].hours[j]=lastmonths[i-1].hours[j];
        }
        lastmonths[0].lines=0;
        for (j=0;j<4;j++) lastmonths[0].hours[j]=0;
      }
      currwday = currdate.tm_wday;
      currmon = currdate.tm_mon;
      
    }
  } else {
    if (debug==2)
      fprintf(stderr, "but day did not change\n");
  }
}

void parse_log(char *logfile)
{
  FILE *fic;
  char line[MAXLINELENGTH];
  int pos;
  int i,j;
  char *nick,*message;
  int nickstart;
  int mononick=-1,monolines=0;
  int temp,hour;
  int timelen;

  if ((fic=fopen(logfile,"rt"))==NULL) { fprintf(stderr,"can't open log file \"%s\"\n",logfile); exit(1); }
  if (debug) printf("working on %s : ",channel);
  while (fgets(line,MAXLINELENGTH,fic)!=NULL)
  {
    /* remove \n */
    for (i=0;line[i]!=0 && i<MAXLINELENGTH-1;i++);
    if (i>=MAXLINELENGTH-1) { 
      if(debug){
        fprintf(stderr,"line %d is too long, skipping\n",totallines+1); 
      }
      continue; 
    }
    if (i<8) {
      if(debug) {
        fprintf(stderr, "line %d is too short to be valid, skipping\n",totallines+1);
      }
      continue;
    }
    line[i-1]='\0';
    pos=0;
    totallines++;
    if (totallines%10000==0 && debug) { printf("."); fflush(stdout); }
    if (strncmp("--- Log opened",line,14)==0) /* --- Log opened Wed May 01 00:00 2002 */
    {
      if (debug==2)
        fprintf(stderr, "log %s opened, ", logfile);
      day_changed(line+15);
    }
    if (strncmp("--- Day changed",line,15)==0) /* --- Day changed Wed May 01 2002 */
    {
      if (debug==2)
        fprintf(stderr, "within log file, ");
      day_changed(line+16);
    }
    else
    {
      /* timelen is number of characters occupied by time 00:00.. plus any space */
      timelen = 5;
      if (line[timelen] == ':' && isdigit(line[timelen+1]) && isdigit(line[timelen+2]))
          timelen += 3;
      if (line[timelen] == ' ')
          timelen++;
      if (strncmp("-!- mode/",&line[timelen],9)==0) /* 00:00 -!- mode/#channel [...] by (Nick, Nick2, )Nick3 */
      {
        for (i=strlen(line);line[i]!=' ';i--);
        nick=&line[i+1];
        users[dichotomic(nick)].counters[D_MODE]++;
      }
      else if (strncmp("-!-",&line[timelen],3)==0) /* 00:00 -!- Nick something... */
      {
        for (i=10;line[i]!=' ' && i <= 10 + MAXNICKLENGTH;i++);
        if(i > 10 + MAXNICKLENGTH) {
          if(debug) {
            fprintf(stderr,"nick on line %d is too long, skipping line\n",totallines); 
          }
          continue;
        }
        line[i]='\0';
        nick=&line[timelen+4];
        message=&line[i+1];
        if (strncmp("changed the topic of",message,20)==0) /* 00:00 -!- Nick changed the topic of #channel to: new topic */
        {
          users[dichotomic(nick)].counters[D_TOPIC]++;
          for (i=21;message[i]!=':';i++);
          message=&message[i+2];
          nbtopics++;
          if ((nbtopics<=NBTOPICS) || (rand()%(nbtopics/NBTOPICS)==0))
          {
            temp=nbtopics<=NBTOPICS?nbtopics-1:rand()%NBTOPICS;
            strcpy(topics[temp].nick,nick);
            strncpy(topics[temp].topic,message,MAXQUOTELENGTH);
          }
        }
        else if (strncmp("was kicked from",message,15)==0) /* 00:00 -!- Nick was kicked from #channel by Nick [Reason] */
        {
          users[dichotomic(nick)].counters[D_KICKED]++;
          for (i=16;message[i]!=' ';i++);
          message=&message[i+4];
          for (i=0;message[i]!=' ';i++);
          message[i]='\0';
          users[dichotomic(message)].counters[D_KICK]++;
        }
        else if (strncmp("is now known as",message,15)==0) /* 00:00 -!- Nick is now known as Nick */
          users[dichotomic(nick)].counters[D_NICK]++;
        else if (message[0]=='[') /* 00:00 -!- Nick [user@host] something... */
        {
          for (i=0;message[i]!=']';i++);
          message=&message[i+2];
          if (strncmp("has joined",message,10)==0) /* 00:00 -!- Nick [user@host] has joined #channel */
            users[dichotomic(nick)].counters[D_JOIN]++;
          else if (strncmp("has quit",message,8)==0); /* 00:00 -!- Nick [user@host] has quit [Reason] */
          else if (strncmp("has left",message,8)==0); /* 00:00 -!- Nick [user@host] has left #channel [Reason] */
          else;
        }
      }
      else if ((line[timelen]=='<') || (line[timelen+1]=='*'))
      {
        line[2]='\0';
        hour=atoi(line);
        if (line[timelen+1]=='*') /* 00:00  * Nick the message */
        {
          for (i=timelen+3;line[i]!=' ' && i <= timelen+3+MAXNICKLENGTH;i++);
          if(i > timelen+3+MAXNICKLENGTH) {
            if(debug) {
              fprintf(stderr,"nick on line %d is too long, skipping line\n",totallines); 
            }
            continue; 
          }
          nick=&line[timelen+3];
          message=&line[i+1];
        }
        else if (line[timelen+1]=='>') /* 00:00 <>>>?Nick<<<> the personal message */
                               /* 00:00 <>>?Nick<<> the personal message */
        {
          for (i=timelen+4;line[i]!='<' && i <= timelen+4+MAXNICKLENGTH;i++);
           if(i > timelen+4+MAXNICKLENGTH) { 
            if(debug) {
              fprintf(stderr,"nick on line %d is too long, skipping line\n",totallines); 
            }
            continue; 
          }
          nick=&line[timelen+4];
          if (line[timelen+3]=='>') nick++;
          message=&line[i+5];
        }
        else /* 00:00 <?Nick> the message */
        {

          /* 
           * Irssi doesn't log channel mode with show_nickmode = OFF    
           * the following covers op, half-op, voice and show_nickmode_empty                 
           */
          switch (line[timelen+1])
          {
            case '@':
            case '%':
            case '+':
            case '&':
            case '~':
            case ' ': 
              nickstart = timelen+2; 
              break;
            default:
              nickstart = timelen+1;
              break;
          }
            
          for (i=nickstart;line[i]!='>' && i <= nickstart + MAXNICKLENGTH;i++);
          if(i > nickstart + MAXNICKLENGTH) { 
            if(debug) {
              fprintf(stderr,"nick on line %d is too long, skipping line\n",totallines); 
            }
            continue; 
          }
          nick=&line[nickstart];
          message=&line[i+2];
        }
        /* remove identified character from nick (invalid nick character anyway) */
        if (nick[0] == '+')
          fprintf(stderr, "nick starts with +! %s\n", nick);
        if (line[i-1] == '+' || line[i-1] == '*')
          i--;
        line[i]='\0';
        i=dichotomic(nick);
        if (line[timelen+1]=='*') users[i].counters[D_ME]++;
        if (i==mononick)
        {
          monolines++;
          if (monolines==5) users[i].counters[D_MONOLOGUE]++;
        }
        else
        {
          mononick=i;
          monolines=1;
        }
        j=strlen(message);
        users[i].lines++;
        if (top_words || ranking==1) 
        {
          if (L("CHARSET")=="KOI8-R") users[i].words+=findwords(message)+findruswords(message);
          else users[i].words+=findwords(message);
        }
        users[i].letters+=j;
        users[i].hours[hour/6]++;
        lastdays[0].lines++;
        lastdays[0].hours[hour/6]++;
        lastweeks[0].lines++;
        lastweeks[0].hours[hour/6]++;
        lastmonths[0].lines++;
        lastmonths[0].hours[hour/6]++;
        lines++;
        if (quarter)
        {
          line[5]='\0';
          hour=hour*4+atoi(&line[3])/15;
        }
        hours[hour]++;
        if (message[j-1]=='?') users[i].counters[D_QUESTION]++;
        else if (message[j-1]=='!') users[i].counters[D_EXCLAM]++;
        else if ((message[j-3]==' ')&&(message[j-2]==':'))
        {
          if (message[j-1]==')') users[i].counters[D_SMILE]++;
          else if (message[j-1]=='(') users[i].counters[D_FROWN]++;
        }
        // Fetch a random message, messages between 25 and 70 chars are
        // preferred (pisg-style, gets "better" quotes)
        //
        if (rand()%users[i].lines==0) {
            
            int len = strlen(message);
            // if we have a "good" quote, use it
            if ( len > 25 && len < 70 )
            {
                strncpy(users[i].quote,message,MAXQUOTELENGTH);
            } else {
                int len2 = strlen(users[i].quote);
                if ( !(len2 > 25 && len2 < 70 )) {
                    strncpy(users[i].quote,message,MAXQUOTELENGTH);
                } 
            }
        } 

        if (strncmp("http://",message,7)==0)
        {
          users[i].counters[D_URL]++;
          for (i=0;(message[i]!=' ') && (i<strlen(message));i++);
          message[i]='\0';
          nburls++;
          if ((nburls<=NBURLS) || (rand()%(nburls/NBURLS)==0))
          {
            temp=nburls<=NBURLS?nburls-1:rand()%NBURLS;
            strcpy(urls[temp].nick,nick);
            strcpy(urls[temp].url,message);
            strncpy(urls[temp].shorturl,message,MAXQUOTELENGTH);
          }
        }
      }
    }
    pos=0;
  }
  fclose(fic);
  if (debug) printf(" done\n");
}

#ifndef __WIN32__
void parse_nick(char *nickfile)
{
  FILE *fic;
  int i,j;
  regex_t preg;
  char line[MAXLINELENGTH];
  int user;
  
  for (i=0;i<nbusers;i++) users[i].temp=users[i].lines;
  if ((fic=fopen(nickfile,"rt"))==NULL) { fprintf(stderr,"can't open nick file \"%s\"\n",nickfile); exit(1); }
  while (fscanf(fic,"%s",line)==1)
  {
    user=dichotomic(line);
    fscanf(fic,"%s",line);
    if (regcomp(&preg,line,0)!=0) { fprintf(stderr,"error in nick file"); exit(1); }
    for (i=0;i<nbusers;i++) if ((i!=user) && (regexec(&preg,users[i].nick,0,0,0)==0) && (users[i].lines>=0))
    {
      if (users[i].temp>users[user].temp) /* for nick alias, keep the random quote of the most used nick */
      {
        strcpy(users[user].quote,users[i].quote);
        users[user].temp=users[i].temp;
      }
      users[user].lines+=users[i].lines;
      users[user].words+=users[i].words;
      users[user].letters+=users[i].letters;
      for (j=0;j<4;j++) users[user].hours[j]+=users[i].hours[j];
      for (j=0;j<NBCOUNTERS;j++) users[user].counters[j]+=users[i].counters[j];
      /* "remove" old user */
      users[i].lines=-1;
      users[i].words=-1;
      users[i].letters=-1;
      for (j=0;j<4;j++) users[i].hours[j]=-1;
      for (j=0;j<NBCOUNTERS;j++) users[i].counters[j]=-1;
    }
    regfree(&preg);
  }
  fclose(fic);
  /* "remove" the ignored nicks */
  i=dichotomic("<NULL>");
  users[i].lines=-1;
  users[i].words=-1;
  users[i].letters=-1;
  for (j=0;j<4;j++) users[i].hours[j]=-1;
  for (j=0;j<NBCOUNTERS;j++) users[i].counters[j]=-1;
}
#endif

void parse_photo(char *photofile)
{
  FILE *fic;
  char line[MAXLINELENGTH];
  int user;
  
  if ((fic=fopen(photofile,"rt"))==NULL) { fprintf(stderr,"can't open photo file \"%s\"\n",photofile); exit(1); }
  while (fscanf(fic,"%s",line)==1)
  {
    user=dichotomic(line);
    fscanf(fic,"%s",line);
    users[user].photo=malloc(strlen(line)+1);
    strcpy(users[user].photo,line);
  }
  fclose(fic);
}

void gen_xhtml(char *xhtmlfile)
{
  FILE *fic;
  FILE *sfic;
  int i,j,k;
  int user,max,temp;
  char line[MAXLINELENGTH];
  char *subtheme;
  int photos=0;
  
  for (i=0;i<nbusers;i++) if (users[i].photo!=NULL) photos=1;
  
  if ((fic=fopen(xhtmlfile,"wt"))==NULL) { fprintf(stderr,"can't open xhtml file \"%s\"\n",xhtmlfile); exit(1); }
  
  /* header */
  if (strcmp("none",header)==0)
  {
    fprintf(fic,"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n\n");
    fprintf(fic,"<!-- Generated by irssistats %s : %s -->\n\n",VERSION,URL);
    fprintf(fic,"<html>\n\n<head>\n<title>");
    fprintf(fic,L("HEADER"),channel,maintainer);
    fprintf(fic,"</title>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=%s\" />\n",L("CHARSET"));
    if (refresh_time)
      fprintf(fic,"<meta http-equiv=\"Refresh\" content=\"%d\" />\n",refresh_time);
    strcpy(line, theme);
    subtheme=strtok(line,",");
    fprintf(fic,"<link rel=\"stylesheet\" type=\"text/css\" href=\"%s.css\" title=\"%s\" />\n",subtheme,subtheme);
    while ((subtheme=strtok(NULL,","))!=NULL)
      fprintf(fic,"<link rel=\"alternate stylesheet\" type=\"text/css\" href=\"%s.css\" title=\"%s\" />\n",subtheme,subtheme);
    fprintf(fic,"</head>\n\n");
    fprintf(fic,"<body>\n\n");
  }
  else
  {
    if ((sfic=fopen(header,"rt"))==NULL) { fprintf(stderr,"can't open header file \"%s\"\n",header); exit(1); }
    while ((temp=fread(line,1,MAXLINELENGTH,sfic))) fwrite(line,temp,1,fic);
    fclose(sfic);
  }
  fprintf(fic,"<div id=\"irssistats\">\n\n<div id=\"irssistats_header\">\n<h1>");
  fprintf(fic,L("HEADER"),channel,maintainer);
  fprintf(fic,"</h1>\n<p>\n%s</p>\n</div>\n\n",ctime(&debut));

  /* legend */
  fprintf(fic,"<div id=\"irssistats_legend\">\n<h2>%s</h2>\n<table>\n<tr>\n",L("LEGEND"));
  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);
  fprintf(fic,"</tr>\n</table>\n</div>\n\n");
  
  if (months)
  {
    /* last months */
    fprintf(fic,"<div id=\"irssistats_lastmonths\">\n<h2>%s</h2>\n<table>\n<tr>\n",L("LASTMONTHS"));
    max=-1;
    for (i=30;i>=0;i--) if (lastmonths[i].lines>max) max=lastmonths[i].lines;
    for (i=30;i>=0;i--)
    {
      fprintf(fic,"<td align=\"center\" valign=\"bottom\"><small>%d</small>",lastmonths[i].lines);
      for (j=0;j<4;j++) if (lastmonths[i].hours[j]!=0) fprintf(fic,"<div class=\"v%d\" style=\"height:%dpx\"></div>",j+1,150*lastmonths[i].hours[j]/max);
      fprintf(fic,"</td>\n");
    }
    fprintf(fic,"</tr>\n<tr>\n");
    for (i=30;i>=0;i--)
      fprintf(fic,"<th>%d</th>\n",i);
    fprintf(fic,"</tr>\n</table>\n</div>\n\n");
  }
  
  if (weeks)
  {
    /* last weeks */
    fprintf(fic,"<div id=\"irssistats_lastweeks\">\n<h2>%s</h2>\n<table>\n<tr>\n",L("LASTWEEKS"));
    max=-1;
    for (i=30;i>=0;i--) if (lastweeks[i].lines>max) max=lastweeks[i].lines;
    for (i=30;i>=0;i--)
    {
      fprintf(fic,"<td align=\"center\" valign=\"bottom\"><small>%d</small>",lastweeks[i].lines);
      for (j=0;j<4;j++) if (lastweeks[i].hours[j]!=0) fprintf(fic,"<div class=\"v%d\" style=\"height:%dpx\"></div>",j+1,150*lastweeks[i].hours[j]/max);
      fprintf(fic,"</td>\n");
    }
    fprintf(fic,"</tr>\n<tr>\n");
    for (i=30;i>=0;i--)
      fprintf(fic,"<th>%d</th>\n",i);
    fprintf(fic,"</tr>\n</table>\n</div>\n\n");
  }
  
  /* last days */
  fprintf(fic,"<div id=\"irssistats_lastdays\">\n<h2>%s</h2>\n<table>\n<tr>\n",L("LASTDAYS"));
  max=-1;
  for (i=30;i>=0;i--) if (lastdays[i].lines>max) max=lastdays[i].lines;
  for (i=30;i>=0;i--)
  {
    fprintf(fic,"<td align=\"center\" valign=\"bottom\"><small>%d</small>",lastdays[i].lines);
    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);
    fprintf(fic,"</td>\n");
  }
  fprintf(fic,"</tr>\n<tr>\n");
  for (i=30;i>=0;i--)
    fprintf(fic,"<th>%d</th>\n",i);
  fprintf(fic,"</tr>\n</table>\n</div>\n\n");
  
  /* top hours */
  fprintf(fic,"<div id=\"irssistats_tophours\">\n<h2>%s</h2>\n<table>\n<tr>\n",L("TOPHOURS"));
  max=-1;
  for (i=0;i<24*4;i++) if (hours[i]>max) max=hours[i];
  if (quarter) for (i=0;i<24*4;i++)
  {
    fprintf(fic,"<td align=\"center\" valign=\"bottom\">");
    if (hours[i]!=0) fprintf(fic,"<div class=\"v%d\" style=\"width:4px;height:%dpx\"></div>",i/4/6+1,150*hours[i]/max);
    fprintf(fic,"</td>\n");
  }
  else for (i=0;i<24;i++)
  {
    fprintf(fic,"<td align=\"center\" valign=\"bottom\"><small>%.1f%%</small>",lines!=0?(float)100*hours[i]/lines:0);
    if (hours[i]!=0) fprintf(fic,"<div class=\"v%d\" style=\"height:%dpx\"></div>",i/6+1,150*hours[i]/max);
    fprintf(fic,"</td>\n");
  }
  fprintf(fic,"</tr>\n<tr>\n");
  for (i=0;i<24;i++)
    if (quarter) fprintf(fic,"<th colspan=\"4\">%d</th>\n",i);
    else fprintf(fic,"<th>%d</th>\n",i);
  fprintf(fic,"</tr>\n</table>\n</div>\n\n");
  
  /* top users */
  fprintf(fic,"<div id=\"irssistats_topusers\">\n<h2>%s</h2>\n",L("TOPUSERS"));
  switch (ranking)
  {
    case 0:
      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"));
      break;
    default:
      /* "letters" and "words" ranking are not yet translated so we use the generic word "rank" ... */
      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"));
      break;
  }
  if (photos) fprintf(fic,"<th></th>");
  fprintf(fic,"</tr>");
  for (i=1;i<=NBUSERS;i++)
  {
    user=-1;
    max=0;
    switch (ranking)
    {
      case 0:
        for (j=0;j<nbusers;j++) if (users[j].lines>max) max=users[user=j].lines;
        break;
      case 1:
        for (j=0;j<nbusers;j++) if (users[j].words>max) max=users[user=j].words;
        break;
      case 2:
        for (j=0;j<nbusers;j++) if (users[j].letters>max) max=users[user=j].letters;
        break;
    }
    if (user!=-1)
    {
      switch (ranking)
      {
        case 0:
          fprintf(fic,"<tr><td>%d</td><td>%s</td><td>%d</td><td class=\"oneline\">",i,users[user].nick,users[user].lines);
          break;
        case 1:
          fprintf(fic,"<tr><td>%d</td><td>%s</td><td>%d</td><td class=\"oneline\">",i,users[user].nick,users[user].words);
          break;
        case 2:
          fprintf(fic,"<tr><td>%d</td><td>%s</td><td>%d</td><td class=\"oneline\">",i,users[user].nick,users[user].letters);
          break;
      }
      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);
      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);
      printhtml(fic,users[user].quote);
      fprintf(fic,"\"</td>");
      if (photos && users[user].photo!=NULL)
      {
        if (photo_size)
          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);
        else
          fprintf(fic,"<td class=\"tdphoto\"><img src=\"%s\" class=\"imgphoto\" alt=\"\" /></td>",users[user].photo);
      }
      fprintf(fic,"</tr>\n");
      users[user].lines=-1;
      users[user].words=-1;
      users[user].letters=-1;
    }    
  }
  fprintf(fic,"</table>\n");
  temp=0;
  for (i=0;i<=nbusers;i++) if (users[i].lines>=0) temp++;
  if (temp>0)
  {
    fprintf(fic,"<p>");
    fprintf(fic,L("OTHERS"),temp);
    fprintf(fic,"</p>\n");
  }
  fprintf(fic,"</div>\n\n");
  
  /* top users by time */
  fprintf(fic,"<div id=\"irssistats_topuserstime\">\n<h2>%s</h2>\n",L("TOPUSERSTIME"));
  fprintf(fic,"<table>\n<tr><th></th>");
  for (i=0;i<4;i++) fprintf(fic,"<th colspan=\"2\">%s %d-%d</th>",L("HOURS"),i*6,i*6+5);
  fprintf(fic,"</tr>\n");
  for (i=1;i<=NBUSERSTIME;i++)
  {
    fprintf(fic,"<tr><td>%d</td>",i);
    for (j=0;j<4;j++)
    {
      user=-1;
      max=0;
      for (k=0;k<nbusers;k++) if (users[k].hours[j]>max) max=users[user=k].hours[j];
      if (user!=-1)
      {
        fprintf(fic,"<td>%s</td><td>%d</td>",users[user].nick,users[user].hours[j]);
        users[user].hours[j]=-1;
      }
      else fprintf(fic,"<td></td><td></td>");
    }
    fprintf(fic,"</tr>\n");
  }
  fprintf(fic,"</table>\n</div>\n\n");

  /* random topics */
  fprintf(fic,"<div id=\"irssistats_randtopics\">\n<h2>%s</h2>\n",L("RANDTOPICS"));
  fprintf(fic,"<table>\n<tr><th>%s</th><th>%s</th></tr>\n",L("CHANGEDBY"),L("NEWTOPIC"));
  for (i=nbtopics<NBTOPICS?nbtopics-1:NBTOPICS-1;i>=0;i--)
  {
    fprintf(fic,"<tr><td>%s</td><td>\"",topics[i].nick);
    printhtml(fic,topics[i].topic);
    fprintf(fic,"\"</td></tr>\n");
  }
  fprintf(fic,"</table>\n</div>\n\n");
  
  /* random urls */
  fprintf(fic,"<div id=\"irssistats_randurls\">\n<h2>%s</h2>\n",L("RANDURLS"));
  fprintf(fic,"<table>\n<tr><th>%s</th><th>%s</th></tr>\n",L("POSTEDBY"),L("POSTEDURL"));
  for (i=nburls<NBURLS?nburls-1:NBURLS-1;i>=0;i--)
  {
    fprintf(fic,"<tr><td>%s</td><td>\"<a href=\"",urls[i].nick);
    printhtml(fic,urls[i].url);
    fprintf(fic,"\">");
    printhtml(fic,urls[i].shorturl);
    fprintf(fic,"</a>\"</td></tr>\n");
  }
  fprintf(fic,"</table>\n</div>\n\n");
  
  /* top words */
  if (top_words)
  {
    fprintf(fic,"<div id=\"irssistats_topwords\">\n<h2>%s</h2>\n",L("TOPWORDS"));
    fprintf(fic,"<table>\n<tr><th></th><th>%s</th><th>%s</th></tr>\n",L("WORD"),L("OCCURRENCES"));
    for (i=0;i<NBWORDS;i++)
      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);
    fprintf(fic,"</table>\n</div>\n\n");
  }
  
  /* big numbers */
  fprintf(fic,"<div id=\"irssistats_bignumbers\">\n<h2>%s</h2>\n",L("BIGNUMBERS"));
  fprintf(fic,"<table>\n<tr><th>%s</th><th>%s</th><th>%s</th></tr>\n",L("NICK"),L("NUMBERS"),L("NBLINES"));
  for (i=0;i<NBCOUNTERS;i++)
  {
    user=-1;
    max=0;
    for (j=0;j<nbusers;j++) if (users[j].counters[i]>max) max=users[user=j].counters[i];
    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]);
  }
  fprintf(fic,"</table>\n</div>\n\n");
  
  /* footer */
  fprintf(fic,"<div id=\"irssistats_footer\">\n<p>");
  fprintf(fic,L("TIME"),totallines,days,(int)(time(NULL)-debut));
  fprintf(fic,"</p>\n<p>%s <a href=\"%s\">irssistats %s</a></p>\n",L("FOOTER"),URL,VERSION);
  if (w3c_link)
  {
    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");
    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");
  }
  fprintf(fic,"</div>\n\n");
  
  /* logo*/
  if (logo) fprintf(fic,"<div class=\"logo\"></div>\n\n");
  
  /* end */
  fprintf(fic,"</div>\n\n");
  if (strcmp("none",footer)==0)
  {
    fprintf(fic,"</body>\n\n</html>\n");
  }
  else
  {
    if ((sfic=fopen(footer,"rt"))==NULL) { fprintf(stderr,"can't open footer file \"%s\"\n",footer); exit(1); }
    while ((temp=fread(line,1,MAXLINELENGTH,sfic))) fwrite(line,temp,1,fic);
    fclose(sfic);
  }
  
  fclose(fic);
}

void parse_config(char *configfile)
{
  void expand(char *path)
  {
    char temp[MAXLINELENGTH];
    if (*path=='~')
    {
      snprintf(temp,MAXLINELENGTH-1,"%s%s",getenv("HOME"),path+1);
      temp[MAXLINELENGTH-1]='\0';
      strcpy(path,temp);
    }
  }
  
  FILE *fic;
  char line[MAXLINELENGTH];
  char keyword[MAXLINELENGTH];
  char value[MAXLINELENGTH];
  int configlines=0;
  int i,j;
  
  if (configfile!=NULL)
  {
    if ((fic=fopen(configfile,"rt"))==NULL)
    {
      fprintf(stderr,"can't open config file : \"%s\"\n",configfile);
      exit(1);
    }
  }
  else
  {
    snprintf(line,MAXLINELENGTH-1,"%s/.irssistats",getenv("HOME"));
    line[MAXLINELENGTH-1]='\0';
    if ((fic=fopen(line,"rt"))==NULL)
      if ((fic=fopen(GLOBALCONF,"rt"))==NULL)
      {
        fprintf(stderr,"can't find config file : \"%s\" nor \"" GLOBALCONF "\"\n",line);
        fprintf(stderr,"please give the path to the config file in argument\n");
        exit(1);
      }
  }

  while (fgets(line,MAXLINELENGTH,fic))
  {
    configlines++;
    if (*line!=';' && *line!='#' && *line!='/' && *line!='-' && *line!='\n')
    {
      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); }
      
      if (strcmp("debug",keyword)==0)
      {
        if (strcmp("none",value)==0) debug=0;
        else
        if (strcmp("normal",value)==0) debug=1;
        else
        if (strcmp("verbose",value)==0) { debug=2; fprintf(stderr,"switching to verbose output\n"); }
        else { fprintf(stderr,"unknown value for \"debug\" option, must be \"normal\", \"verbose\" or \"none\"\n"); exit(1); }
      }
      else
      
      if (strcmp("channel",keyword)==0)
      {
        if (debug==2) fprintf(stderr,"setting channel name to \"%s\"\n",value);
        strcpy(channel,value);
      }
      else
      
      if (strcmp("maintainer",keyword)==0)
      {
        if (debug==2) fprintf(stderr,"setting maintainer to \"%s\"\n",value);
        strcpy(maintainer,value);
      }
      else
      
      if (strcmp("language",keyword)==0)
      {
        if (debug==2) fprintf(stderr,"setting language to \"%s\"\n",value);
        for (i=0;i<NBLANGUAGES;i++) if (strcmp(value,keys[i][0][1])==0) { language=i; break; }
        if (i==NBLANGUAGES)
        {
          fprintf(stderr,"Invalid language : %s\n",value);
          fprintf(stderr,"Supported languages :\n");
          for (i=0;i<NBLANGUAGES;i++) fprintf(stderr,"%s = %s\n",keys[i][0][1],keys[i][0][0]);
          exit(1);
        }
      }
      else
      
      if (strcmp("theme",keyword)==0)
      {
        if (debug==2) fprintf(stderr,"setting theme to \"%s\"\n",value);
        strcpy(theme,value);
      }
      else
      
      if (strcmp("refresh_time",keyword)==0)
      {
        refresh_time=atoi(value);
        if (debug==2) fprintf(stderr,"setting refresh_time to \"%d\"\n",refresh_time);
      }
      else
      
      if (strcmp("photo_size",keyword)==0)
      {
        photo_size=atoi(value);
        if (debug==2) fprintf(stderr,"setting photo_size to \"%d\"\n",photo_size);
      }
      else
      
      if (strcmp("w3c_link",keyword)==0)
      {
        if (debug==2) fprintf(stderr,"setting w3c_link to \"%s\"\n",value);
        if (strcmp("no",value)==0) w3c_link=0;
        else if (strcmp("yes",value)==0) w3c_link=1;
        else { fprintf(stderr,"unknown value for \"w3c_link\" option, must be \"yes\" or \"no\"\n"); exit(1); }
      }
      else
      
      if (strcmp("logo",keyword)==0)
      {
        if (debug==2) fprintf(stderr,"setting logo to \"%s\"\n",value);
        if (strcmp("no",value)==0) logo=0;
        else if (strcmp("yes",value)==0) logo=1;
        else { fprintf(stderr,"unknown value for \"logo\" option, must be \"yes\" or \"no\"\n"); exit(1); }
      }
      else
      
      if (strcmp("header",keyword)==0)
      {
        expand(value);
        if (debug==2) fprintf(stderr,"setting header to \"%s\"\n",value);
        strcpy(header,value);
      }
      else
      
      if (strcmp("footer",keyword)==0)
      {
        expand(value);
        if (debug==2) fprintf(stderr,"setting footer to \"%s\"\n",value);
        strcpy(footer,value);
      }
      else
      
      if (strcmp("input",keyword)==0)
      {
        expand(value);
        if (debug==2) fprintf(stderr,"parsing log file \"%s\"\n",value);
        parse_log(value);
      }
      else
      
      if (strcmp("nickfile",keyword)==0)
      {
        expand(value);
        if (debug==2) fprintf(stderr,"nick alias using file \"%s\"\n",value);
#ifdef __WIN32__
        fprintf(stderr,"no support for nickfile in WIN32 version\n");
#else
        parse_nick(value);
#endif
      }
      else
      
      if (strcmp("photofile",keyword)==0)
      {
        expand(value);
        if (debug==2) fprintf(stderr,"parsing photo file \"%s\"\n",value);
        parse_photo(value);
      }
      else
      
      if (strcmp("output",keyword)==0)
      {
        expand(value);
        if (debug==2) fprintf(stderr,"generating xhtml file \"%s\"\n",value);
        bestwords(words,0);
        if (L("CHARSET")=="KOI8-R") bestruswords(ruswords,0);
        gen_xhtml(value);
        
        /* reset variables */
        nbusers=0;
        nburls=0;
        nbtopics=0;
        days=0;
        currwday=-1;
        currmon=-1;
        for (i=0;i<24*4;i++) hours[i]=0;
        lines=0;
        for (i=0;i<31;i++)
        {
          lastdays[i].lines=0;
          lastweeks[i].lines=0;
          lastmonths[i].lines=0;
          for (j=0;j<4;j++)
          {
            lastdays[i].hours[j]=0;
            lastweeks[i].hours[j]=0;
            lastmonths[i].hours[j]=0;
          }
        }
        freewords(&words);
        freeruswords(&ruswords);
        for (i=0;i<NBWORDS;i++) topwords[i].nb=0;
        totallines=0;
        debut=time(NULL);
        for (i=0;i<nbusers;i++)
        {
          free(users[i].photo);
          users[i].photo=NULL;
        }
      }
      else
      
      if (strcmp("top_words",keyword)==0)
      {
        if (debug==2) fprintf(stderr,"setting top_words to \"%s\"\n",value);
        if (strcmp("no",value)==0) top_words=0;
        else if (strcmp("yes",value)==0) top_words=1;
        else { fprintf(stderr,"unknown value for \"top_words\" option, must be \"yes\" or \"no\"\n"); exit(1); }
      }
      else
      
      if (strcmp("ranking",keyword)==0)
      {
        if (strcmp("lines",value)==0) ranking=0;
        else
        if (strcmp("words",value)==0) ranking=1;
        else
        if (strcmp("letters",value)==0) ranking=2;
        else { fprintf(stderr,"unknown value for \"ranking\" option, must be \"lines\", \"words\" or \"letters\"\n"); exit(1); }
      }
      else
      
      if (strcmp("quarter",keyword)==0)
      {
        if (debug==2) fprintf(stderr,"setting quarter to \"%s\"\n",value);
        if (strcmp("no",value)==0) quarter=0;
        else if (strcmp("yes",value)==0) quarter=1;
        else { fprintf(stderr,"unknown value for \"quarter\" option, must be \"yes\" or \"no\"\n"); exit(1); }
      }
      else
      
      if (strcmp("months",keyword)==0)
      {
        if (debug==2) fprintf(stderr,"setting months to \"%s\"\n",value);
        if (strcmp("no",value)==0) months=0;
        else if (strcmp("yes",value)==0) months=1;
        else { fprintf(stderr,"unknown value for \"months\" option, must be \"yes\" or \"no\"\n"); exit(1); }
      }
      else
      
      if (strcmp("weeks",keyword)==0)
      {
        if (debug==2) fprintf(stderr,"setting weeks to \"%s\"\n",value);
        if (strcmp("no",value)==0) weeks=0;
        else if (strcmp("yes",value)==0) weeks=1;
        else { fprintf(stderr,"unknown value for \"weeks\" option, must be \"yes\" or \"no\"\n"); exit(1); }
      }
      else
      
      { fprintf(stderr,"error in config file : \"%s\" is an unknown keyword (line %d)\n",keyword,configlines); exit(1); }        
    }
  }
  fclose(fic);
}

int main(int argc,char *argv[])
{
  (void) setlocale(LC_ALL, "");
  if ((users=malloc(maxusers*sizeof(struct user)))==NULL) { fprintf(stderr,"unable to malloc memory\n"); exit(1); }
  srand(debut=time(NULL));
  if (argc==1) parse_config(NULL);
  else if (argc==2) parse_config(argv[1]);
  else
  {
    fprintf(stderr,"Usage : %s [/path/to/file.conf]\n",argv[0]);
    fprintf(stderr,"Version : irssistats %s\n",VERSION);
    exit(1);
  }
  return(0);
}



syntax highlighted by Code2HTML, v. 0.9.1