From ec1c19451d83bda19075453e469cfbf634cc9b7d Mon Sep 17 00:00:00 2001 From: Antoine Jacquet Date: Thu, 16 May 2002 00:00:00 +0200 Subject: [PATCH] version 0.2 * languages support (currently english and french) * themes support * most used words * now handles personal messages and "/me" * parameters on command line * some code change * faster * no more image when 0 lines * maximum height for graph is now calculated --- README | 7 +- irssistats.c | 473 +++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 348 insertions(+), 132 deletions(-) diff --git a/README b/README index d2ee540..da6ed11 100644 --- a/README +++ b/README @@ -1,16 +1,13 @@ -irssistats 0.1 +irssistats 0.2 site: http://royale.zerezo.com/programmation/irssistats/ mail: royale@zerezo.com -config: -edit irssistats.c - install: make cp pix/*.png /path/to/webdir/ usage: -cat /path/to/file.log | ./irssistats > /path/to/webdir/index.html +cat /path/to/file.log | ./irssistats channel maintainer language theme > /path/to/webdir/index.html links: http://torus.lnet.lut.fi/ircstats/ diff --git a/irssistats.c b/irssistats.c index e9a26c3..62d1194 100644 --- a/irssistats.c +++ b/irssistats.c @@ -1,69 +1,149 @@ -/* Usage: cat /path/to/file.log | ./irssistats > /path/to/file.html */ +/* Usage: cat /path/to/file.log | ./irssistats channel maintainer language theme > /path/to/file.html */ + +#include +#include +#include +#include /* Config */ -#define CHANNEL "#channel" -#define MAINTAINER "maintainer" +#define MAXUSERS 2000 +#define MAXNICKLENGTH 50 +#define MAXLINELENGTH 2000 +#define MAXQUOTELENGTH 100 +#define NBUSERS 50 +#define NBURLS 5 +#define NBTOPICS 5 +#define NBWORDS 20 +#define MINWORDLENGTH 5 -/* Language */ -#define HEADER "Statistiques de %s par %s" -#define LEGEND "Légende" -#define LASTDAYS "Statistiques des derniers jours" -#define TOPHOURS "Statistiques horaires" -#define TOPUSERS "Personnes les plus actives" -#define OTHERS "Il reste %d personnes non classées..." -#define NBLINES "lignes" -#define NICK "nick" -#define AVGLETTERS "lettres/lignes" -#define HOURS "heures" -#define QUOTE "message aléatoire" -#define RANDTOPICS "Quelques topics" -#define CHANGEDBY "changé par" -#define NEWTOPIC "nouveau topic" -#define RANDURLS "Quelques URLs" -#define POSTEDBY "postée par" -#define POSTEDURL "URL" -#define BIGNUMBERS "Quelques grands nombres..." -#define TIME "%d lignes traitées en %d secondes" -#define FOOTER "Statistiques générées par" +/* irssistats */ +#define VERSION "0.2" +#define URL "http://royale.zerezo.com/programmation/irssistats/" -/* Colors */ -#define BGCOLOR "#FFFFFF" -#define TEXT "#000000" -#define LINK "#0000EE" -#define VLINK "#551A8B" -#define ALINK "#FF0000" -#define TITLE1 "#000000" -#define TITLE2 "#000000" -#define BGTABLE "#EEEEEE" -#define BGTITLE "#FFEEEE" +/* Languages */ +#define NBLANGUAGES 2 +#define NBKEYS 22 +char *keys[NBLANGUAGES][NBKEYS+1][2]= /* first key used for language name and abbreviation */ +{ + { /* English language */ + { "English", "en" }, + { "HEADER", "Statistics for #%s by %s" }, + { "LEGEND", "Legend" }, + { "LASTDAYS", "Lastdays 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" }, + { "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" }, + { "TIME", "%d lines parsed in %d seconds" }, + { "FOOTER", "Statistics generated by" } + }, + { /* French language */ + { "Français", "fr" }, + { "HEADER", "Statistiques de #%s par %s" }, + { "LEGEND", "Légende" }, + { "LASTDAYS", "Statistiques des derniers jours" }, + { "TOPHOURS", "Statistiques horaires" }, + { "TOPUSERS", "Personnes les plus actives" }, + { "OTHERS", "Il reste %d personnes non classées..." }, + { "NBLINES", "lignes" }, + { "NICK", "nick" }, + { "AVGLETTERS", "lettres/lignes" }, + { "HOURS", "heures" }, + { "QUOTE", "message aléatoire" }, + { "RANDTOPICS", "Quelques topics" }, + { "CHANGEDBY", "changé par" }, + { "NEWTOPIC", "nouveau topic" }, + { "RANDURLS", "Quelques URLs" }, + { "POSTEDBY", "postée par" }, + { "POSTEDURL", "URL" }, + { "TOPWORDS", "Mots les plus utilisés" }, + { "WORD", "mot" }, + { "OCCURRENCES", "occurrences" }, + { "TIME", "%d lignes traitées en %d secondes" }, + { "FOOTER", "Statistiques générées par" } + } +}; -/* Dark Theme... */ -/* -#define BGCOLOR "#000000" -#define TEXT "#FFFFFF" -#define LINK "#AAAAFF" -#define VLINK "#CCCCDD" -#define ALINK "#FFAAAA" -#define TITLE1 "#AAAAFF" -#define TITLE2 "#FFAAAA" -#define BGTABLE "#225522" -#define BGTITLE "#552222" -*/ +int language; +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(""); +} -/* irssistats */ -#define VERSION "0.1" -#define URL "http://royale.zerezo.com/programmation/irssistats/" +/* Themes */ +#define NBTHEMES 3 +#define NBCOLORS 9 +char *colors[NBTHEMES][NBCOLORS+1][2]= /* first key used for theme name/description and abbreviation */ +{ + { /* Default theme */ + { "White background", "default" }, + { "BGCOLOR", "#FFFFFF" }, + { "TEXT", "#000000" }, + { "LINK", "#0000EE" }, + { "VLINK", "#551A8B" }, + { "ALINK", "#FF0000" }, + { "TITLE1", "#000000" }, + { "TITLE2", "#000000" }, + { "BGTABLE", "#EEEEEE" }, + { "BGTITLE", "#FFEEEE" } + }, + { /* Dark theme */ + { "Black background", "dark" }, + { "BGCOLOR", "#000000" }, + { "TEXT", "#FFFFFF" }, + { "LINK", "#AAAAFF" }, + { "VLINK", "#CCCCDD" }, + { "ALINK", "#FFAAAA" }, + { "TITLE1", "#AAAAFF" }, + { "TITLE2", "#FFAAAA" }, + { "BGTABLE", "#225522" }, + { "BGTITLE", "#552222" } + }, + { /* zeRezo theme */ + { "Green theme...", "zerezo" }, + { "BGCOLOR", "#000000" }, + { "TEXT", "#FFFFFF" }, + { "LINK", "#14F024" }, + { "VLINK", "#0CBA18" }, + { "ALINK", "#FFFFFF" }, + { "TITLE1", "#0CBA18" }, + { "TITLE2", "#84DB8C" }, + { "BGTABLE", "#085D10" }, + { "BGTITLE", "#0B810B" } + } +}; +int theme; -#include -#include -#include -#include +char *T(char *color) +{ + int i; + for (i=1;i<=NBCOLORS;i++) if (strcmp(color,colors[theme][i][0])==0) return(colors[theme][i][1]); + fprintf(stderr,"unknown theme color: %s\n",color); + return(""); +} -#define MAXUSERS 2000 -#define MAXNICKLENGTH 50 -#define MAXLINELENGTH 2000 +/* Variables */ + +char *channel; +char *maintainer; struct { @@ -99,6 +179,69 @@ int days=0; int hours[24]; int lines=0; +struct letter +{ + int nb; + struct letter *next[26]; +} words; + +struct +{ + int nb; + char word[MAXLINELENGTH]; +} topwords[NBWORDS]; + +#define isletter(c) (((c>='a')&&(c<='z'))||((c>='A')&&(c<='Z'))) +#define lowercase(c) (((c>='A')&&(c<='Z'))?c-'A'+'a':c) +void findwords(char *message) +{ + int i,c; + struct letter *pos,*tmp; + for (;;) + { + while (!isletter(*message)) if (*message=='\0') return; else message++; + pos=&words; + while (isletter(*message)) + { + c=lowercase(*message)-'a'; + if ((*pos).next[(int)c]==NULL) + { + tmp=malloc(sizeof(struct letter)); + (*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++; + } + return; +} + +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.nbi;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 unhtml(char *string) /* replace < and > by { and } */ { while (*string!='\0') @@ -110,6 +253,35 @@ void unhtml(char *string) /* replace < and > by { and } */ 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) { fprintf(stderr,"too many users\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].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); + } + strcpy(users[start].nick,nick); + users[start].lines=0; + users[start].letters=0; + for (j=0;j<4;j++) users[start].hours[j]=0; + users[start].quote[0]='\0'; + } + return(start); +} + int main(int argc,char *argv[]) { int i,j; @@ -121,10 +293,35 @@ int main(int argc,char *argv[]) char *nick,*message; char line[MAXLINELENGTH]; + /*** INIT ***/ + if (argc!=5) + { + fprintf(stderr,"Usage: cat /path/to/file.log | ./irssistats channel maintainer language theme > /path/to/file.html\n\n"); + fprintf(stderr,"Supported languages :\n"); + for (i=0;i the message */ + else if ((line[6]=='<') || (line[7]=='*')) { line[2]='\0'; hour=atoi(line); - if ((line[6]=='<') && (line[7]!='>')) + if (line[7]=='*') /* 00:00 * Nick the message */ + { + for (i=9;line[i]!=' ';i++); + nick=&line[9]; + message=&line[i+1]; + } + else if (line[7]=='>') /* 00:00 <>>>?Nick<<<> the personal message */ { - for (i=7;line[i]!='>';i++); - line[i]='\0'; + for (i=11;line[i]!='<';i++); + nick=&line[11]; + message=&line[i+5]; + } + else /* 00:00 the message */ + { + for (i=8;line[i]!='>';i++); nick=&line[8]; - unhtml(nick); - line[pos-1]='\0'; message=&line[i+2]; - unhtml(message); - for (i=0;i=MAXUSERS) { fprintf(stderr,"too many users\n"); exit(1); } - } - users[i].lines++; - users[i].letters+=strlen(message); - users[i].hours[hour/6]++; - lastdays[0].lines++; - lastdays[0].hours[hour/6]++; - lines++; - hours[hour]++; - if (rand()%users[i].lines==0) strncpy(users[i].quote,message,100); - if (strncmp("http://",message,7)==0) + } + line[i]='\0'; + line[pos-1]='\0'; + unhtml(nick); + unhtml(message); + i=dichotomic(nick); + users[i].lines++; + users[i].letters+=strlen(message); + users[i].hours[hour/6]++; + lastdays[0].lines++; + lastdays[0].hours[hour/6]++; + lines++; + hours[hour]++; + if (rand()%users[i].lines==0) strncpy(users[i].quote,message,MAXQUOTELENGTH); + if (strncmp("http://",message,7)==0) + { + for (i=0;(message[i]!=' ') && (i0;i--) { - for (i=4;i>0;i--) - { - strcpy(urls[i].nick,urls[i-1].nick); - strcpy(urls[i].url,urls[i-1].url); - } - strcpy(urls[0].nick,nick); - strcpy(urls[0].url,message); + strcpy(urls[i].nick,urls[i-1].nick); + strcpy(urls[i].url,urls[i-1].url); } + strcpy(urls[0].nick,nick); + strcpy(urls[0].url,message); } } + findwords(message); } pos=0; } } fprintf(stderr,"done\n"); + bestwords(words,0); + /*** HTML ***/ /* header */ printf("\n\n",VERSION,URL); printf("\n\n\n\n"); - printf(HEADER,CHANNEL,MAINTAINER); + printf(L("HEADER"),channel,maintainer); printf("\n\n\n"); - printf("\n\n
\n\n

",BGCOLOR,TEXT,LINK,VLINK,ALINK,TITLE1); - printf(HEADER,CHANNEL,MAINTAINER); + printf("\n\n
\n\n

",T("BGCOLOR"),T("TEXT"),T("LINK"),T("VLINK"),T("ALINK"),T("TITLE1")); + printf(L("HEADER"),channel,maintainer); printf("

\n%s
\n

\n\n",ctime(&debut)); /* legend */ - printf("

%s

\n\n\n",TITLE2,LEGEND,BGTABLE); - for (i=0;i<4;i++) printf("\n",i+1,HOURS,i*6,i*6+5); + printf("

%s

\n
: %s %d-%d 
\n\n",T("TITLE2"),L("LEGEND"),T("BGTABLE")); + for (i=0;i<4;i++) printf("\n",i+1,L("HOURS"),i*6,i*6+5); printf("\n
: %s %d-%d 
\n

\n\n"); /* last days */ - printf("

%s

\n\n\n",TITLE2,LASTDAYS); + printf("

%s

\n
\n\n",T("TITLE2"),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--) { - printf("\n"); } printf("\n\n"); for (i=30;i>=0;i--) - printf("\n",BGTABLE,i); + printf("\n",T("BGTABLE"),i); printf("\n
%d
",lastdays[i].lines); - if (max!=0) for (j=0;j<4;j++) printf("
",j+1,150*lastdays[i].hours[j]/max); + printf("
%d
",lastdays[i].lines); + for (j=0;j<4;j++) if (lastdays[i].hours[j]!=0) printf("
",j+1,150*lastdays[i].hours[j]/max); printf("
%d%d
\n

\n\n"); /* top hours */ - printf("

%s

\n\n\n",TITLE2,TOPHOURS); - if (lines!=0) for (i=0;i<24;i++) - printf("\n",(float)100*hours[i]/lines,i/6+1,1500*hours[i]/lines); + printf("

%s

\n
%.1f%%
\n\n",T("TITLE2"),L("TOPHOURS")); + max=-1; + for (i=0;i<24;i++) if (hours[i]>max) max=hours[i]; + for (i=0;i<24;i++) + { + printf("\n"); + } printf("\n\n"); for (i=0;i<24;i++) - printf("\n",BGTABLE,i); + printf("\n",T("BGTABLE"),i); printf("\n
%.1f%%
",lines!=0?(float)100*hours[i]/lines:0); + if (hours[i]!=0) printf("
",i/6+1,150*hours[i]/max); + printf("
%d%d
\n

\n\n"); /* top users */ - printf("

%s

\n",TITLE2,TOPUSERS); - printf("\n\n",BGTITLE,NICK,BGTITLE,NBLINES,BGTITLE,HOURS,BGTITLE,AVGLETTERS,BGTITLE,QUOTE); - for (i=1;i<=50;i++) + printf("

%s

\n",T("TITLE2"),L("TOPUSERS")); + printf("
%s%s%s%s%s
\n\n",T("BGTITLE"),L("NICK"),T("BGTITLE"),L("NBLINES"),T("BGTITLE"),L("HOURS"),T("BGTITLE"),L("AVGLETTERS"),T("BGTITLE"),L("QUOTE")); + for (i=1;i<=NBUSERS;i++) { user=-1; max=-1; for (j=0;jmax) max=users[user=j].lines; if (user!=-1) { - printf("\n",BGTABLE,users[user].letters/users[user].lines,BGTABLE,users[user].letters/users[user].lines,BGTABLE,users[user].quote); + printf("\n",T("BGTABLE"),users[user].letters/users[user].lines,T("BGTABLE"),users[user].letters/users[user].lines,T("BGTABLE"),users[user].quote); users[user].lines=-1; } } - printf("
%s%s%s%s%s
%d%s%d",BGTABLE,i,BGTABLE,users[user].nick,BGTABLE,users[user].lines,BGTABLE); - for (j=0;j<4;j++) - printf("",j+1,100*users[user].hours[j]/users[user].lines); - printf("%d\"%s\"
%d%s%d",T("BGTABLE"),i,T("BGTABLE"),users[user].nick,T("BGTABLE"),users[user].lines,T("BGTABLE")); + for (j=0;j<4;j++) if (users[user].hours[j]!=0) printf("",j+1,100*users[user].hours[j]/users[user].lines); + printf("%d\"%s\"

\n"); - if (nbusers>50) printf(OTHERS,nbusers-50); - printf("
\n

\n\n"); + printf("\n"); + if (nbusers>NBUSERS) + { + printf("
"); + printf(L("OTHERS"),nbusers-50); + printf("
\n"); + } + printf("

\n\n"); /* random topics */ - printf("

%s

\n",TITLE2,RANDTOPICS); - printf("\n\n",BGTITLE,CHANGEDBY,BGTITLE,NEWTOPIC); - for (i=4;i>=0;i--) - printf("\n",BGTABLE,topics[i].nick,BGTABLE,topics[i].topic); + printf("

%s

\n",T("TITLE2"),L("RANDTOPICS")); + printf("
%s%s
%s\"%s\"
\n\n",T("BGTITLE"),L("CHANGEDBY"),T("BGTITLE"),L("NEWTOPIC")); + for (i=nbtopics=0;i--) + printf("\n",T("BGTABLE"),topics[i].nick,T("BGTABLE"),topics[i].topic); printf("
%s%s
%s\"%s\"
\n

\n\n"); /* random urls */ - printf("

%s

\n",TITLE2,RANDURLS); - printf("\n\n",BGTITLE,POSTEDBY,BGTITLE,POSTEDURL); - for (i=4;i>=0;i--) - printf("\n",BGTABLE,urls[i].nick,BGTABLE,urls[i].url,urls[i].url); + printf("

%s

\n",T("TITLE2"),L("RANDURLS")); + printf("
%s%s
%s\"%s\"
\n\n",T("BGTITLE"),L("POSTEDBY"),T("BGTITLE"),L("POSTEDURL")); + for (i=nburls=0;i--) + printf("\n",T("BGTABLE"),urls[i].nick,T("BGTABLE"),urls[i].url,urls[i].url); printf("
%s%s
%s\"%s\"
\n

\n\n"); - /* big numbers (todo...) - printf("

%s

\n",TITLE2,BIGNUMBERS); - */ + /* top words */ + printf("

%s

\n",T("TITLE2"),L("TOPWORDS")); + printf("\n\n",T("BGTITLE"),L("WORD"),T("BGTITLE"),L("OCCURRENCES")); + for (i=0;i\n",T("BGTABLE"),i+1,T("BGTABLE"),topwords[i].word,T("BGTABLE"),topwords[i].nb); + printf("
%s%s
%d\"%s\"%d
\n

\n\n"); /* footer */ - printf(TIME,totallines,(int)(time(NULL)-debut)); - printf("
\n%s irssistats %s",FOOTER,URL,VERSION); + printf(L("TIME"),totallines,(int)(time(NULL)-debut)); + printf("
\n%s irssistats %s",L("FOOTER"),URL,VERSION); printf("\n\n
\n\n\n\n\n"); return(0); -- 2.20.1