/************************************************************************* * gruftistats program * Copyright (c) 1998-9 Andy Kempling (aurikan@hotmail.com) * Copyright (c) 1998-2001 Colin Phipps * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. *************************************************************************/ /* $Id: ircstats.c,v 1.28 2001/08/11 11:31:55 cph Exp $ */ #include #include #include #include #include #define DAYSEC0 6 #define DAYSEC1 12 #define DAYSEC2 18 #define DAYSEC3 24 #define USERMAX0 8 /* DO NOT MAKE THIS 0!!!!!!! */ #define CHANMAX0 1 /* DO NOT MAKE THIS 0!!!!!!! */ #define INFO_MAX 30 #define LINES_HISTORY 3 # include "ircstats.h" static some_random_stuff random_topics, random_kicks, random_signoffs, random_urls; #ifdef RECORDS recordset oldrec[RECORD_NUM]; #endif int channum = 0; int chanmax = CHANMAX0; int usernum = 0; int usermax = USERMAX0; int deluser = 0; int verbose = 0; /* Verbosity level */ chan *channel; chan *chan0; user *userloc; typedef char html_colour_t[7]; html_colour_t page_bg = { "E0E0F0" }, /* Background to the page */ page_text = { "181C18" }, /* Normal text */ page_text_contrast = { "000000" }, /* Normal text */ day_usage_date_bg = {"90A0D8"}, table_head_nick = {"A07088"}, table_head_other = {"70A098"}, table_index_bg = {"889480"}, table_nick_bg = {"C090A8"}, table_2_bg = {"90C0B8"}, table_3_bg = {"90A0D8"}; char botname1[10] = {"grufti"}; char botname2[10] = {"unused1"}; char botname3[10] = {"unused2"}; char botkick1[10] = {"Banned"}; char botkick2[10] = {"moron"}; char picext [10] = {".png"}; char randomwordslist[100], statword[20]; char credits[200]; char header_text[200]; char font1[20] = {"Times New Roman"}; char font2[20] = {"Verdana"}; char font3[20] = {"Arial Narrow"}; #define MISC_EXP_LEN 1000 char quit_msg_exp[MISC_EXP_LEN] = "Ping timeout"; char kick_str_exp[MISC_EXP_LEN] = "Public flood"; regex_t ignore_signoff_regex, ignore_kick_regex; void InitChan (chan * channel, const char *name); user *FindUser (char *name); user *CreateUser (char *name); void DeleteUser (char *name); chan *FindChan (const char *date); chan *CreateChan (const char* name); int compare (const void *arg1, const void *arg2); int comparechan (const void *arg1, const void *arg2); void quoterep (rquote_t *q, const char *source); long int datestr (char *str); void addrandom(some_random_stuff* p, char* text, char* bywho); void ParseLog(FILE* infp, int verbose, struct log_parse_s* plp, size_t* pbp); #ifdef RECORDS void GenRecords_One(char * recordin, char * recordnew, char * recordout); void GenRecords_Seven(char * recordin, char * recordnew, chan * weekstat, char * recordout); #endif /* RECORDS */ enum html_flags_e { html_bold = 1, html_underline = 2 }; /* addressed_to_bot - is a line directed at the/a bot */ void addletters(user *u, const char* text) { register char *swp = statword; while (*text) { register char ch = *text++; if (isalnum(ch)) { u->letters++; if (isupper(*text)) u->loud++; } if (ispunct(ch)) { u->letters++; u->punct++; if (ch == '?') { u->questions++; u->flags |= UF_ASKING; } if (ch == '!') u->loud++; } /* cph - word stats * we are looking for only this one word */ if (ch == *swp) { if (!*++swp) { u->statword++; swp = statword; if (verbose>2) fprintf(stderr, "Statword hit (%s)\n", statword); } } else swp = statword; } } void clearrandom(some_random_stuff* p) { int i; p->num = 0; p->delled = 0; for (i=0; irandom[i] = p->bywho[i] = NULL; } } void do_random_table(FILE* output, some_random_stuff* p, const char* whatisit) { int i; fprintf (output, "\n"); fprintf (output, "\n", table_head_nick, page_text_contrast, font1); fprintf (output, "\n", table_head_other, page_text_contrast, font1, whatisit); fprintf (output, "\n"); for (i=0; (i<5) && (inum - p->delled); i++) { fprintf(output, "" "" "\n", output); } fprintf (output, "
" "" "Who by" "%s
" "%i" "%s" "", table_index_bg, font2, i+1, table_nick_bg, font2, p->bywho[i], table_2_bg, font1); output_content(output, p->random[i]); fputs("
\n"); } void do_factoid(FILE* output, int (*userstat)(const user*), const char* spec, int min, int topnum) { int x, u_top = 0, u_next = 1, u_top_val = 0, u_next_val = 1; if (userstat(userloc) < userstat(userloc+1)) { u_top = 1; u_next = 0; } u_top_val = userstat(userloc+u_top); u_next_val = userstat(userloc+u_next); if (topnum > usernum) topnum = usernum; for (x = 2; x < topnum; x++) { int v = userstat(userloc+x); if (v > u_next_val) { if (v > u_top_val) { u_next_val = u_top_val; u_next = u_top; u_top_val = v; u_top = x; } else { u_next_val = v; u_next = x; } } } if (u_top_val >= min) { fprintf(output, "%s" "", table_nick_bg, font2, (userloc+u_top)->name, table_2_bg, font1); fprintf(output, spec, u_top_val); fprintf(output, "" "%s (%d)\n", table_3_bg, font2, (userloc+u_next)->name, u_next_val); } } void do_factoid_inv(FILE* output, int (*userstat)(const user*), const char* spec, int max, int topnum) { int x, u_top = 0, u_next = 1, u_top_val = 0, u_next_val = 1; if (-userstat(userloc) < -userstat(userloc+1)) { u_top = 1; u_next = 0; } u_top_val = -userstat(userloc+u_top); u_next_val = -userstat(userloc+u_next); if (topnum > usernum) topnum = usernum; for (x = 2; x < topnum; x++) { int v = -userstat(userloc+x); if (v == 0) continue; if (v > u_next_val) { if (v > u_top_val) { u_next_val = u_top_val; u_next = u_top; u_top_val = v; u_top = x; } else { u_next_val = v; u_next = x; } } } if (-u_top_val < max) { fprintf(output, "%s" "", table_nick_bg, font2, (userloc+u_top)->name, table_2_bg, font1); fprintf(output, spec, -u_top_val); fprintf(output, "" "%s (%d)\n", table_3_bg, font2, (userloc+u_next)->name, -u_next_val); } } /* Function to return the suffix for a given ordinal * i.e. st is the number ends in 1, nd if it ends in 2, ... * Made simpler and removed static buffer */ const char * position_suffix(int pos) { pos %= 10; switch(pos) { case 1: return "st"; case 2: return "nd"; case 3: return "rd"; default: return "th"; } } int user_kicks(const user *u) { return u->kicks; }; int user_kicked(const user *u) { return u->kicked; }; int user_nicks(const user *u) { return u->nicks; }; int user_joins(const user *u) { return u->joins; }; int user_linelength(const user *u) { return (u->lines ? u->letters/u->lines : 0); }; int user_questions(const user *u) { return (u->lines ? 100*u->questions/u->lines : 0); }; int user_loud(const user *u) { return (u->letters ? 100*u->loud/u->letters : 0); }; int user_punct(const user *u) { return (u->letters ? 100*u->punct/u->letters : 0); }; int user_word(const user *u) { return u->statword; }; int user_stats(const user *u) { return u->statcalls; }; int user_seens(const user *u) { return u->seens; }; int user_monos(const user *u) { return u->monologues; }; int user_urls(const user *u) { return u->urls; }; int user_fourseasons(const user *u) { if ((u->texts[0]+u->acts[0])!=0 && (u->texts[1]+u->acts[1])!=0 && (u->texts[2]+u->acts[2])!=0 && (u->texts[3]+u->acts[3])!=0) return (u->lines); else return (101); } int user_topics(const user *u) { return u->topics; }; int callback_constant = 0; int user_linesec(const user *u) { return u->texts[callback_constant] + u->acts[callback_constant]; } int user_lines(const user *u) { return u->lines; }; int user_answers(const user *u) { return u->answered; }; void do_factoids(FILE* output) { char *buf = malloc(100 + strlen(statword)); /* removed static limit */ int r = rand(); /* User number with most kicks/joins/etc */ sprintf(buf, (r&1) ? "knew the right word, and said \"%s\" %%ld times" : "kept saying \"%s\", a whole %%ld times in fact", statword); fprintf (output, "\n" "\n\n", table_head_nick, page_text_contrast, font1, table_head_other, page_text_contrast, font1, table_head_other, page_text_contrast, font1); do_factoid(output, user_kicks, (r&0x4)?"was in a mood not to be messed with, and kicked %ld times" : "was the channel bouncer, booting %ld people out", 1, 30); do_factoid(output, user_kicked, (r&0x10)?"wasn't very popular, and got booted out %ld times" : "just didn't fit in, getting kicked %ld times", 1, 30); do_factoid(output, user_nicks, (r&0x40)?"couldn't find the right nick, despite changing it %ld times" : "is still looking for that perfect nick after %ld changes", 2, 30); do_factoid(output, user_joins, (r&0x100)?"couldn't decide whether to stay or go, and was in and out %ld times" : "abused the door, coming and going %ld times", 7, 30); do_factoid(output, user_linelength, "had the longest lines, averaging %ld in length", 7, 30); do_factoid(output, user_loud, (r&0x400)?"was the loud mouth, with %ld%% shouting" : "was giving us earache, yelling %ld%% of the time", 5, 30); do_factoid(output, user_questions, (r&0x1000)?"seemed uncertain - %ld%% of lines were questions" : "didn't know, so asked questions %ld%% of the time", 5, 30); do_factoid(output, user_answers, (r&0x40)?"was very helpful, answering %ld questions": "was a know-it-all, answering %ld questions", 1, usernum); do_factoid(output, user_word, buf, 10, 30); r = rand(); /* heh, let's not exceed FFFF */ do_factoid(output, user_seens, (r&0x1)?"couldn't find who he was looking for, and performed %ld seens" : "missed his friends, he did %ld seens", 3, 30); do_factoid_inv(output, user_linelength, "wrote only %ld letters per line, despite talking our ears off", 10, 30); do_factoid(output, user_urls, (r&0x4)?"was busy surfing the web, and found %ld cool URLs" : "dug up %ld interesting web sites to visit", 5, 30); do_factoid_inv(output, user_fourseasons, "wrote only %ld lines, yet still managed to be around
morning, afternoon, evening, and the graveyard shift", 100, usernum); do_factoid(output, user_monos, (r&0x10)?"talked to himself a lot, with %ld monologues": "delivered %ld monologues, although nobody listened", 1, usernum); do_factoid(output, user_topics, "wasn't satisfied with the topic, so changed it %ld times", 4, 30); fprintf(output, "
" "" "Who" "" "Fact!" "" "Runner-up
\n"); free(buf); } #ifdef RECORDS void do_records(FILE* output, char * inputnewstr) { if (inputnewstr) { FILE * inputnew; fprintf (output, "\n" "\n\n", table_head_nick, page_text_contrast, font1, table_head_other, page_text_contrast, font1, table_head_other, page_text_contrast, font1); inputnew = fopen(inputnewstr, "rt"); if (inputnew != NULL) { char recordname[32], newname[32], oldname[32], newdate[32], olddate[32]; int newnum, oldnum, pos, numrecords = 0; while( fscanf(inputnew, "%[^\t] ", recordname) != EOF) { numrecords++; fscanf(inputnew, "%d", &pos); fscanf(inputnew, "%[^\t] %[^\t] %d ", newname, newdate, &newnum); fscanf(inputnew, "%[^\t] %[^\t] %d ", oldname, olddate, &oldnum); fprintf(output, "\n", table_3_bg, font2, oldname, oldnum); } fprintf(output, "\n", table_3_bg, font2); fclose(inputnew); } fprintf(output, "
" "" "Who" "" "Event" "" "Old Record
%s" "", table_nick_bg, font2, newname, table_2_bg, font1); fprintf(output, "took %d%s place for \"%s\" with %d", pos+1, position_suffix(pos), recordname, newnum); fprintf(output, "" "%s (%d)
%d" "", table_nick_bg, font2, numrecords, table_2_bg, font1); fprintf(output, "record%s set today", (numrecords==1)?" was":"s were"); fprintf(output, "" "
\n"); } } #endif char channame[INFO_MAX+1]; char myname[INFO_MAX+1]; char myemail[INFO_MAX+1]; char max_quote_len_str[6] = {"1000"}; char min_quote_len_str[6] = {"2"}; int min_quote_len, max_quote_len; void read_options(FILE* fp) { char line[200]; response_settings setting[] = { {"channame", channame, 30}, {"myname", myname, 30}, {"myemail", myemail, 30}, {"statwords", randomwordslist, 100}, {"page_text", page_text, 6}, {"page_text_contrast", page_text_contrast, 6}, {"page_bg", page_bg, 6}, {"date_bg", day_usage_date_bg, 6}, {"table_head_nick", table_head_nick, 6}, {"table_head_other", table_head_other, 6}, {"table_index_bg", table_index_bg, 6}, {"table_nick_bg", table_nick_bg, 6}, {"table_2_bg", table_2_bg, 6}, {"table_3_bg", table_3_bg, 6}, {"botname", botname1, 9}, {"botname2", botname2, 9}, {"botname3", botname3, 9}, {"picext",picext,9}, {"credits",credits,199}, {"max_quote_len",max_quote_len_str,6}, {"min_quote_len",min_quote_len_str,6}, {"ignore_kicks",kick_str_exp,sizeof(kick_str_exp)}, {"ignore_quits",quit_msg_exp,sizeof(quit_msg_exp)}, {"header",header_text,199}, {"font1",font1,19}, {"font2",font2,19}, {"font3",font3,19}, {"", NULL, 0} }; while (fgets(line, 200, fp) != NULL) { char *p = line + strlen(line) - 1; if ((p >= line) && (*p == '\n')) *p = 0; p = strchr(line, '='); if (p && line[0] != '#') { int i = 0; *p++ = 0; while (setting[i].where) { if (!strcasecmp(line, setting[i].tag)) { if (verbose>1) fprintf(stderr, "Setting %s to %s\n", setting[i].tag, p); strncpy(setting[i].where, p, setting[i].max); } i++; } } } } int main (int argc, char **argv) { FILE *inputstats; FILE *output; int filec, pfc = 0; char *filename; char *recordin = NULL; char *recordnew = NULL; char *recordout = NULL; const char *recordhtml = "-"; const char *outfile = "-"; time_t start = time (NULL); int optind = 1; int recordkeep = 0; char *banfile = NULL; struct log_parse_s* plp = NULL; size_t total_bytes_parsed = 0; clearrandom(&random_topics); clearrandom(&random_kicks); clearrandom(&random_signoffs); clearrandom(&random_urls); srand ((unsigned) time (NULL)); chan0 = (chan *) malloc (CHANMAX0 * sizeof (chan)); userloc = (user *) malloc (USERMAX0 * sizeof (user)); while ((optind < argc) && (argv[optind][0] == '-') && argv[optind][1] != '\0') { char opt = argv[optind++][1]; switch (opt) { case 'r': if (optind < argc) { FILE *optfile = fopen(argv[optind++], "rt"); if (!optfile) perror("fopen"); else { read_options(optfile); fclose(optfile); } } break; case 'o': if (optind < argc) outfile = argv[optind++]; break; case 's': if (optind < argc) recordin = argv[optind++]; break; case 'i': if (optind < argc) optind++; break; case 't': if (optind < argc) recordout = argv[optind++]; break; case 'l': if (optind < argc) { recordhtml = argv[optind++]; recordkeep = 1; } break; case 'n': if (optind < argc) recordnew = argv[optind++]; break; case 'v': verbose++; break; case 'b': if (optind < argc) banfile = argv[optind++]; break; case 'p': if (!plp && optind < argc) { FILE *infp = fopen(argv[optind++],"r"); if (!infp) perror("fopen"); else { plp = ReadLogFormatSpec(infp, verbose); fclose(infp); } } break; default: fprintf( stderr, "Unknown option %s", argv[optind]); break; } } if (!plp) { fprintf(stderr, "No log format file - unable to continue\n"); exit(-1); } { /* Compile some misc regexps */ int rc; rc = regcomp(&ignore_signoff_regex, quit_msg_exp, 0); if (rc) { fprintf(stderr, "Error %d compiling signoffs regexp %s\n", rc, quit_msg_exp); exit(-1); } rc = regcomp(&ignore_kick_regex, kick_str_exp, 0); if (rc) { fprintf(stderr, "Error %d compiling kicks regexp %s\n", rc, kick_str_exp); exit(-1); } } min_quote_len = atoi(min_quote_len_str); max_quote_len = atoi(max_quote_len_str); { /* Choose a random word to stat for */ if (!randomwordslist[0]) strlcpy(statword, "\n\n", sizeof(statword)); /* impossible to match */ else { int n = 1; char *p = randomwordslist; while (*p) { char *q = strchr(p, ','); if (q) *q++ = 0; if (!(rand() % n++)) strcpy(statword, p); if (q) p = q; else *p=0; } if (verbose) fprintf(stderr, "Collecting stats for word \"%s\"\n", statword); } } for (filec = optind; filec < argc; filec++) { { filename = *(argv + filec); if (strcmp(filename, "-")) inputstats = fopen (filename, "rt"); else inputstats = stdin; /* Support for input from stdin */ if (inputstats == NULL) printf ("Error opening %s, skipping file\n", filename); else { pfc++; ParseLog(inputstats, verbose, plp, &total_bytes_parsed); } if (inputstats) fclose (inputstats); #ifdef RECORDS if (recordkeep == 1 && pfc == 1) GenRecords_One(recordin, recordnew, recordout); #endif } } if (banfile) { FILE* bfp = fopen(banfile, "rt"); char nick[16]; if (bfp) { while (fgets(nick, 16, bfp)) if (strlen(nick)>2) { nick[strlen(nick)-1] = 0; /* Strip trailing '\n' */ DeleteUser(nick); } fclose(bfp); } else perror(banfile); } if (pfc == 0) { fprintf (stderr, "No logs parsed. Nothing to output"); return 0; } else { if (strcmp(outfile, "-")) output = fopen (outfile, "wt"); else output = stdout; if (output == NULL) { fprintf (stderr, "Error opening output html file"); return 0; } { int max_lines = 0, tot_lines = 0, x, y, z; chan *chantotal = (chan *) malloc (sizeof (chan)); chan *edate = FindChan("Unknown"); time_t now = time (NULL); InitChan (chantotal, "Unknown"); for (x = 0; x < channum; x++) for (y = 0; y < 24; y++) chantotal->lines[y] += (chan0 + x)->lines[y]; qsort ((void *) chan0, channum, sizeof (chan), comparechan); qsort ((void *) userloc, usernum, sizeof (user), compare); #ifdef RECORDS /* this is actually a 7-day tally */ /* Assuming that this is run at 0:05, where there is no significant impact of the 8th day */ if (recordkeep == 1 && pfc == 8) GenRecords_Seven(recordin, recordnew, chantotal, recordout); #endif /* Records */ for (x = 0; x < channum; x++) { if (strcmp ((chan0 + x)->date, "Unknown")) { edate = chan0 + x; break; } } fprintf (output, ""); fprintf (output, "\n",PACKAGE,VERSION); fprintf (output, "#%s statistics created by %s and %s\n", channame, myname, PACKAGE); fprintf (output, "
\n%s\n", page_bg, page_text, page_text, page_text, header_text); fprintf (output, "" "Channel stats for #%s - created by " "%s
\n", page_text_contrast, font2, channame, myemail, page_text_contrast, font2, myname); fprintf (output, "
\n" "Statistics generated from %s to %s
\n", font2, edate->date, (chan0 + channum - 1)->date); fprintf (output, "Stats generated on %s
\n", font2, ctime (&now)); fprintf (output, "During this %li-day reporting period a " "total of %i persons visited #%s
\n", font2, datestr ((chan0 + channum - 1)->date) - datestr (edate->date) + 1, usernum - deluser, channame); /* Print most recent topic only, if any */ for (x = channum-1; x>0; --x) if (chan0[x].curtopic) { fprintf (output,"", font2); fputs ("The current topic is ", output); output_content(output, chan0[x].curtopic); fputs ("
\n", output); break; } x = 0; if (channum != 1) while (!strcmp((chan0 + x)->date, "Unknown")) { x = (rand()%(channum)); if (verbose>1) fprintf(stderr, "Random Channel Number: %i (%s)\n", x, (chan0 + x)->date); } fputs("", output); if (chan0[x].quote.quote) output_content(output, chan0[x].quote.quote); fputs("
\n", output); fprintf (output, "
\n"); fprintf (output, "
\n"); fprintf (output, "Daily statistics" "" " (Number of lines / 6 hours)
\n", page_text_contrast, font2, page_text_contrast, font2); fprintf (output, "\n"); for (x = 0; x < channum; x++) for (y = 0; y < 4; y++) { tot_lines = 0; for (z = 0; z < 6; z++) tot_lines += (chan0 + x)->lines[z + 6 * y]; if (tot_lines > max_lines) max_lines = tot_lines; } for (x = 0; x < channum; x++) if ((chan0 + x)->tlines) for (y = 0; y < 4; y++) { tot_lines = 0; for (z = 0; z < 6; z++) tot_lines += (chan0 + x)->lines[z + 6 * y]; fprintf (output, "\n", font3, tot_lines, y + 1, picext, (int) (128 * tot_lines / max_lines)); } fprintf (output, "\n"); for (x = 0; x < channum; x++) if ((chan0 + x)->tlines) fprintf (output, "\n", day_usage_date_bg, font3, (chan0 + x)->date); fprintf (output, "
" "%i
" "\"Bar\"
" "%s
\n"); fprintf (output, "

\n\n"); fprintf (output, "\n", picext, page_text_contrast, font2, 0, DAYSEC0); fprintf (output, "\n", picext, page_text_contrast, font2, DAYSEC0, DAYSEC1); fprintf (output, "\n", picext, page_text_contrast, font2, DAYSEC1, DAYSEC2); fprintf (output, "\n", picext, page_text_contrast, font2, DAYSEC2, DAYSEC3); fprintf (output, "
\"Pink\" Hours %.2i-%.2i\"Pink\" Hours %.2i-%.2i\"Pink\" Hours %.2i-%.2i\"Pink\" Hours %.2i-%.2i
\n"); fprintf (output, "
\n"); fprintf (output, "" "Channel load by hours
\n", page_text_contrast, font2); fprintf (output, "\n"); max_lines = 1; tot_lines = 1; for (x = 0; x < 24; x++) { tot_lines += chantotal->lines[x]; if (max_lines < chantotal->lines[x]) max_lines = chantotal->lines[x]; } for (x = 0; x < 24; x++) fprintf (output, "\n", font3, (float) (1000 * chantotal->lines[x] / tot_lines) / 10, (int) (x / 6 + 1), picext, (int) (192 * chantotal->lines[x] / max_lines)); fprintf (output, "\n"); for (x = 0; x < 24; x++) fprintf (output, "\n", (int) (192 * chantotal->lines[x] / max_lines + 63), (int) (128 - 128 * chantotal->lines[x] / max_lines), 255, 255, 255, font3, x); fprintf (output, "
%.1f%%
\"Bar\"
%i
\n"); fprintf (output, "

\n"); fprintf (output, "" "Activity Statistics
" " (Nicks sorted by number of lines written)

\n", page_text_contrast, font2, font2); fprintf (output, "
\n"); fprintf (output, "\n"); fprintf (output, "\n", table_head_nick, page_text_contrast, font1); fprintf (output, "\n", table_head_other, page_text_contrast, font1); fprintf (output, "\n", table_head_other, page_text_contrast, font1); fprintf (output, "\n"); /* Count total lines */ tot_lines = 0; for (x = 0; x < usernum; x++) { if (!(userloc + x)->alive) { continue; } if ((userloc + x)->lines > tot_lines) tot_lines = (userloc + x)->lines; } /* Print users table */ for (deluser=0, x = 0; (x - deluser < 30) && (x < usernum); x++) { if (!(userloc + x)->alive || !(userloc + x)->lines) { deluser++; continue; } fprintf (output, "", table_index_bg, font2, x + 1 - deluser); fprintf (output, "\n", table_nick_bg, font2, (userloc + x)->name); fprintf (output, "\n", font2, (x - deluser) ? "" : "", (userloc + x)->lines, (x - deluser) ? "" : ""); fprintf (output, "\n\n", output); } fputs("
" " Nick" "" " Number of Lines" " Quote
" "%i" "%s\n", table_2_bg); for (y = 0; y < 4; y++) if ((userloc + x)->texts[y] + (userloc + x)->acts[y]) fprintf (output, "\"%li\"", y + 1, picext, (int) (100 * ((userloc + x)->texts[y] + (userloc + x)->acts[y]) / tot_lines), (userloc + x)->texts[y] + (userloc + x)->acts[y]); fprintf (output, " %s%li%s" "", table_3_bg, font1); if (userloc[x].quote.quote) output_content(output, userloc[x].quote.quote); fputs("
\n", output); fprintf (output, "\n"); tot_lines = 0; for (y = 0; y < 24; y++) tot_lines += chantotal->lines[y]; deluser = 0; for (x = 0; x < usernum; x++) { if (!(userloc + x)->alive) { deluser++; continue; } max_lines = (userloc + x)->lines; if (max_lines / .015625 > tot_lines) fprintf (output, "" "\n", page_text_contrast, font3, (userloc + x)->name, (x - deluser) % 4 + 1, picext, (int) (1024 * max_lines / tot_lines), (userloc + x)->name, max_lines); else { (userloc + x)->alive = 2; deluser++; } } fprintf (output, "
" "%s\"%s" "
\n"); /* Now the random topics table */ fprintf (output, "
" "5 Random Topics\n", page_text_contrast, font2); do_random_table(output, &random_topics, "Topic"); /* Now the random kicks table */ fprintf(output, "
" "5 Random Kicks\n", page_text_contrast, font2); do_random_table(output, &random_kicks, "What happened"); /* Now some random facts */ fprintf (output, "
" "Big Numbers\n", page_text_contrast, font2); if (0 < usernum) do_factoids(output); #ifdef RECORDS /* New records */ fprintf (output, "
" "Records Set Today\n", page_text_contrast, font2); if (0 < usernum) do_records(output, recordnew); #endif /* RECORDS */ /* Random URLs */ fprintf(output, "
" "5 Random URLs\n", page_text_contrast, font2); do_random_table(output, &random_urls, "URL"); /* Random signoffs */ fprintf(output, "
" "5 Random Signoffs\n", page_text_contrast, font2); do_random_table(output, &random_signoffs, "Quit message"); /* Now for the terminal table; credits, date, misc */ fprintf (output, "
\n"); fprintf (output, "\n", font2, total_bytes_parsed >> 10, (int)(now - start)); fprintf (output, ""); fprintf (output, "\n", font2, credits); fprintf (output, "
This page was created on %s,\n", font2, ctime (&now)); fprintf (output, "with %s %s by Andy Kempling and Colin Phipps.
", PACKAGE, VERSION); fprintf (output, "Processed %dKiB in %ds.
\"Valid%s
"); fprintf (output, "
"); if (verbose) fprintf (stderr, "Done (Parsed %i files)\n", pfc); free(chantotal); } fclose (output); } return 0; } void InitChan (chan * channel, const char *name) { int x; for (x = 0; x < 24; x++) channel->lines[x] = 0; channel->curtopic = NULL; channel->quote.quote = NULL; channel->quote.choices = 0; channel->tlines = 0; strcpy (channel->date, name); } chan * FindChan (const char *date) { int x; for (x = 0; x < channum; x++) { if (verbose>1) fprintf(stderr, "Channel Name: '%s' (%s)\n", (chan0+x)->date, date); if (!stricmp (date, (chan0 + x)->date)) return chan0 + x; } return 0; } chan * CreateChan (const char *name) { if (channum == chanmax) { chanmax *= 2; chan0 = realloc ((void *) chan0, chanmax * sizeof (chan)); } InitChan (chan0 + channum, name); return chan0 + channum++; } int compare_user_names (const void *arg1, const void *arg2) { return strcasecmp(((user *) arg2)->name, ((user *) arg1)->name); } user * FindUser (char *name) { user fakeuser; fakeuser.name = name; return bsearch(&fakeuser, userloc, usernum, sizeof *userloc, compare_user_names); } void DeleteUser (char *name) { user fakeuser, *p; fakeuser.name = strdup(name); p = bsearch(&fakeuser, userloc, usernum, sizeof *userloc, compare_user_names); if (p) { free(p->name); free(p->curnick); free(p->quote.quote); free(p->lastline); if (p != &userloc[usernum-1]) *p = userloc[usernum-1]; qsort(userloc, --usernum, sizeof *userloc, compare_user_names); } } user * CreateUser (char *name) { int x; if (usernum == usermax) { usermax *= 2; userloc = realloc ((void *) userloc, usermax * sizeof (user)); } userloc[usernum].lastline = userloc[usernum].quote.quote = NULL; userloc[usernum].quote.choices = 0; userloc[usernum].name = strdup(name); userloc[usernum].curnick = strdup(name); for (x = 0; x < 4; x++) { (userloc + usernum)->texts[x] = 0; (userloc + usernum)->acts[x] = 0; } (userloc + usernum)->lines = 0; (userloc + usernum)->letters = 0; (userloc + usernum)->loud = 0; (userloc + usernum)->questions = 0; (userloc + usernum)->punct = 0; (userloc + usernum)->statword = 0; (userloc + usernum)->joins = 0; (userloc + usernum)->kicked = 0; (userloc + usernum)->kicks = 0; (userloc + usernum)->nicks = 0; (userloc + usernum)->statcalls = 0; (userloc + usernum)->seens = 0; (userloc + usernum)->urls = 0; (userloc + usernum)->topics = 0; (userloc + usernum)->sought = 0; (userloc + usernum)->monologues = 0; (userloc + usernum)->alive = 1; (userloc + usernum)->answered = 0; (userloc + usernum)->flags = UF_NONE; qsort(userloc, ++usernum, sizeof *userloc, compare_user_names); return FindUser(name); } int compare (const void *arg1, const void *arg2) { return ((user *) arg2)->lines - ((user *) arg1)->lines; } void quoterep (rquote_t *q, const char *source) { { /* cph 2001/07/15 - allow length of quoted text to be limited */ size_t l = strlen(source); if (l < min_quote_len || l > max_quote_len) return; } { int prob = ++(q->choices); if (prob > 1 && rand() >= RAND_MAX / prob) return; if (q->quote) free(q->quote); q->quote = strdup(source); } } int comparechan (const void *arg1, const void *arg2) { long int day1 = 0, day2 = 0; if (!strcmp (((chan *) arg1)->date, "Unknown")) return -1; if (!strcmp (((chan *) arg2)->date, "Unknown")) return 1; if (!strcmp (((chan *) arg2)->date, ((chan *) arg1)->date)) return 0; day1 = datestr (((chan *) arg1)->date); day2 = datestr (((chan *) arg2)->date); return day1 - day2; } long int datestr (char *str) { long int x = 0; int y; y = atoi (str + 11); if (strstr (str, "Dec")) x = 334 + ((y % 4) ? 0 : 1) - ((y % 100) ? 0 : 1) + ((y % 400) ? 0 : 1); else if (strstr (str, "Nov")) x = 304 + ((y % 4) ? 0 : 1) - ((y % 100) ? 0 : 1) + ((y % 400) ? 0 : 1); else if (strstr (str, "Oct")) x = 273 + ((y % 4) ? 0 : 1) - ((y % 100) ? 0 : 1) + ((y % 400) ? 0 : 1); else if (strstr (str, "Sep")) x = 243 + ((y % 4) ? 0 : 1) - ((y % 100) ? 0 : 1) + ((y % 400) ? 0 : 1); else if (strstr (str, "Aug")) x = 212 + ((y % 4) ? 0 : 1) - ((y % 100) ? 0 : 1) + ((y % 400) ? 0 : 1); else if (strstr (str, "Jul")) x = 181 + ((y % 4) ? 0 : 1) - ((y % 100) ? 0 : 1) + ((y % 400) ? 0 : 1); else if (strstr (str, "Jun")) x = 151 + ((y % 4) ? 0 : 1) - ((y % 100) ? 0 : 1) + ((y % 400) ? 0 : 1); else if (strstr (str, "May")) x = 120 + ((y % 4) ? 0 : 1) - ((y % 100) ? 0 : 1) + ((y % 400) ? 0 : 1); else if (strstr (str, "Apr")) x = 90 + ((y % 4) ? 0 : 1) - ((y % 100) ? 0 : 1) + ((y % 400) ? 0 : 1); else if (strstr (str, "Mar")) x = 59 + ((y % 4) ? 0 : 1) - ((y % 100) ? 0 : 1) + ((y % 400) ? 0 : 1); else if (strstr (str, "Feb")) x = 31; else if (strstr (str, "Jan")) x = 0; x += (y - 1970) * 365 + (int) ((y - 1968) / 4); x += atoi (str + 8); return x; } void addrandom(some_random_stuff* p, char* text, char* bywho) { int n = (++(p->num))-(p->delled); int i; for (i=0; irandom[i] && !stricmp(text, p->random[i])) { p->delled++; return; } if (n<=NUM_RANDOM) i = n-1; else for (i=0; irandom[i]) free(p->random[i]); if (p->bywho[i]) free(p->bywho[i]); p->random[i] = strdup(text); p->bywho[i] = strdup(bywho); return; } static char* history_line[LINES_HISTORY]; /* line_with_history mallocs a new buffer and returns the given * line prepended with the LINES_HISTORY lines before it * Intended for kicks and such where some pre-context is needed */ char *line_with_history(char *line) { size_t len = 0; int i; char *retline; for (i=0; i LP_NUM, since there may be parts which * we don't use */ #define RM_NUM (2*LP_NUM) regmatch_t rmatch[RM_NUM]; for (i=1; i 3) { fprintf(stderr, "Bad hour \"%s\"\n", line+rmatch[plp[i].returned_bit[LP_TIME]].rm_so); exit(-1); } } /* If date stamped, start a new channel struct for this day */ if (plp[i].returned_bit[LP_DATE]) { /* Copy the name to a zero terminated string */ char *newchan = strdup_from_rmatch(line, &rmatch[plp[i].returned_bit[LP_DATE]]); if (!(channel = FindChan (newchan))) channel = CreateChan(newchan); free(newchan); } /* Now copy the other normal info stuff to nice zero terminated * strings to be friendly to the code below */ if (plp[i].returned_bit[LP_NICK]) nick = strdup_from_rmatch(line, &rmatch[plp[i].returned_bit[LP_NICK]]); if (plp[i].returned_bit[LP_OTHERNICK]) othernick = strdup_from_rmatch(line, &rmatch[plp[i].returned_bit[LP_OTHERNICK]]); if (plp[i].returned_bit[LP_STRING]) str = strdup_from_rmatch(line, &rmatch[plp[i].returned_bit[LP_STRING]]); if (verbose>1) fprintf(stderr, "Parsed line \"%s\" as %d, %s,%s,%s\n", line, linetype, nick, othernick, str); } /* Get the user record */ if (nick) if (!(puser = FindUser (nick))) puser = CreateUser (nick); if (puser == lastuser) { if (monologue++>=8) { puser->monologues++; monologue = 0; } } else { lastuser = puser; monologue = 0; } /* Smooth sailing from here; we have the line type and the bits of data, * so lets update the stats with it */ switch (linetype) { case LT_TEXT: { /* See if this line is addressed to someone */ char *p = str+1; while (*p && (*p != ' ') && (*p != 2) && (*p != ':') && (*p != ',')) p++; if (*p) { char *un = calloc(p-str+1,1); user *ou; memcpy(un, str, p-str); if ((ou = FindUser(un))) { if (ou->flags & UF_ASKING) puser->answered++; } free(un); } } case LT_ACT: channel->tlines++; channel->lines[hour]++; quoterep (&channel->quote, line); if (linetype == LT_ACT) puser->acts[daysec]++; else puser->texts[daysec]++; puser->lines++; { /* Scan for URLs */ char* us; if ((us = strstr(str,"http://")) || (us = strstr(str,"www.")) || (us = strstr(str,"ftp://"))) { /* cph - remove most of the logic from here. Just let * text2html.c do the hard work */ char *graburl = strchr(us, ' '); size_t len; puser->urls++; if (graburl) len = graburl - us; else len = strlen(us); graburl = malloc(len+1); graburl[len]=0; memcpy(graburl, us, len); addrandom(&random_urls, graburl, nick); free(graburl); } } { /* Scan for various interesting strings */ char *lwr = strdup(str); strlwr(lwr); /* Still a lot of hard coded strings and logic here */ if (strstr(lwr, botname1) || strstr(lwr, botname2) || strstr(lwr, botname3)) { if (strstr(lwr + strlen(nick) + 3, "stat")) puser->statcalls++; if (strstr(lwr, "seen")) puser->seens++; } else if (strstr(lwr, "!seen")) puser->seens++; free(lwr); } puser->flags &= ~UF_ASKING; addletters(puser, str); { /* We don't quote the [time] part of normal lines, * but we do want the * nick on actions. Messy part is, we * would rather not have the timestamp on actions, but * without knowing the line format we can't avoid it. FIXME. */ char *str_to_quote = (linetype == LT_ACT) ? line : str; quoterep(&puser->quote, str_to_quote); } break; case LT_SIGNOFF: /* If the quit message does not match our list of boring quits.. */ if (regexec(&ignore_signoff_regex, str, 0, NULL, REG_ICASE)) addrandom(&random_signoffs, str, nick); case LT_PART: break; case LT_KICK: quoterep (&channel->quote, line); puser->kicks++; { user* kuser; /* Get the user record */ if (!(kuser = FindUser (othernick))) kuser = CreateUser (othernick); kuser->kicked++; } /* If the kick message does nto match our list of boring kicks.. */ if (regexec(&ignore_kick_regex, str, 0, NULL, REG_ICASE)) { char* kickline = line_with_history(line); addrandom(&random_kicks, kickline, nick); free(kickline); } break; case LT_TOPIC: puser->topics++; addrandom(&random_topics, str, nick); free(channel->curtopic); channel->curtopic = strdup(str); break; case LT_JOIN: puser->joins++; /* Maybe trying to be too smart, but let this drop * thru like a nick change (user might have been under their * alternate nick previously, so we have to reset curnick). * WARNING: have to strdup it, because it's freed lower down. * Maybe this is too messy for the slight work saved. */ othernick = strdup(nick); case LT_NICK: if (linetype == LT_NICK) puser->nicks++; quoterep (&channel->quote, line); /* Set user's current nick */ free(puser->curnick); puser->curnick = strdup(othernick); break; case LT_MODE: quoterep (&channel->quote, line); break; default: /* LT_DATE_STAMP and LT_CHANNEL can be ignored */ continue; /* Avoid these being included in history lines */ } if (history_line[0]) free(history_line[0]); for (i=0; i= records->record[y].num) { overnum = records->record[y].num; strcpy(overname, records->record[y].name); strcpy(overdate, records->record[y].date); for (z=RECORD_MAX-2; z>=y; z--) records->record[z+1] = records->record[z]; records->record[y].num = critfunc(locstart + x); strcpy(records->record[y].date, date); strcpy(records->record[y].name, (locstart + x)->name); if (outputnew != NULL) fprintf(outputnew, "%s\t%d %s\t%s\t%d %s\t%s\t%d\n", records->name, y, (locstart + x)->name, date, critfunc(locstart + x), overname, overdate, overnum); break; } } } } void ClearRecords(recordset * records, const char * recordname) { int y; strcpy(records->name, recordname); for(y=0; yrecord[y].name, "None"); strcpy(records->record[y].date, "None"); records->record[y].num = 0; } } void GenRecords_One(char * recordin, char * recordnew, char * recordout) { FILE * outnew; FILE * inold; char date[16]; int x, y; strcpy(date, (chan0 + chanmax - 1)->date); for (x=0; x<4; x++) { char buf[32]; sprintf(buf, "Hours %i-%i one-day high", x * 6, x*6+6-1); ClearRecords(&oldrec[x], buf); } ClearRecords(&oldrec[4], "One day high"); if (recordin != NULL) { inold = fopen(recordin, "rt"); if (inold != NULL) { for( x=0; x1) fprintf(stderr, "Record %s: %s, %s (%i)\n", oldrec[x].name, oldrec[x].record[y].name, oldrec[x].record[y].date, oldrec[x].record[y].num); } } fclose(inold); } } outnew = fopen(recordnew, "wt"); for (x=0; x<4; x++) { callback_constant = x; qcsort((void *) userloc, usernum, sizeof(user), user_linesec); ReplaceRecord(outnew, userloc, user_linesec, &oldrec[x], date); } { qcsort((void *) userloc, usernum, sizeof(user), user_lines); ReplaceRecord(outnew, userloc, user_lines, &oldrec[4], date); } fclose(outnew); if (recordout != NULL) { inold = fopen(recordout, "wt"); if (inold != NULL) { for( x=0; xname, "The channel"); date = (chan0 + chanmax - 1)->date; } #endif /* RECORDS */