/*************************************************************************
* gruftistats program
* Copyright (c) 1998-9 Andy Kempling (aurikan@hotmail.com)
* Copyright (c) 1998-2001 Colin Phipps <cphipps@doomworld.com>
*
* 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 <stdio.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
#include <ctype.h>
#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; i<NUM_RANDOM; i++)
{
p->random[i] = p->bywho[i] = NULL;
}
}
void do_random_table(FILE* output, some_random_stuff* p, const char* whatisit)
{
int i;
fprintf (output, "<Table border=0 cellpadding=2 cellspacing=2>\n");
fprintf (output, "<tr><td></td><td align=\"left\" bgcolor=\"#%s\">"
"<font color=\"#%s\" size=3 face=\"%s\">"
"<i><b>Who by</b></i></font></td>\n", table_head_nick, page_text_contrast, font1);
fprintf (output, "<td align=\"left\" bgcolor=\"#%s\">"
"<font color=\"#%s\" size=3 face=\"%s\"><i><b>%s</b></i></font></td>\n",
table_head_other, page_text_contrast, font1, whatisit);
fprintf (output, "</tr>\n");
for (i=0; (i<5) && (i<p->num - p->delled); i++) {
fprintf(output, "<tr><td align=\"right\" bgcolor=\"#%s\">"
"<font size=1 face=\"%s\">%i</font></td>"
"<td align=\"left\" bgcolor=\"#%s\">"
"<font size=2 face=\"%s\">%s</font></td>"
"<td align=\"left\" bgcolor=\"#%s\">"
"<font size=2 face=\"%s\"><i>",
table_index_bg, font2, i+1, table_nick_bg, font2, p->bywho[i], table_2_bg, font1);
output_content(output, p->random[i]);
fputs("</i></font></td></tr>\n", output);
}
fprintf (output, "</table>\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, "<tr><td align=\"center\" bgcolor=\"#%s\"><font size=2 face=\"%s\">%s"
"</font></td><td align=\"left\" bgcolor=\"#%s\"><font size=3 face=\"%s\">",
table_nick_bg, font2, (userloc+u_top)->name, table_2_bg, font1);
fprintf(output, spec, u_top_val);
fprintf(output, "</font></td><td align=\"left\" bgcolor=\"#%s\">"
"<font size=1 face=\"%s\">%s (%d)</font></td></tr>\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, "<tr><td align=\"center\" bgcolor=\"#%s\"><font size=2 face=\"%s\">%s"
"</font></td><td align=\"left\" bgcolor=\"#%s\"><font size=3 face=\"%s\">",
table_nick_bg, font2, (userloc+u_top)->name, table_2_bg, font1);
fprintf(output, spec, -u_top_val);
fprintf(output, "</font></td><td align=\"left\" bgcolor=\"#%s\">"
"<font size=1 face=\"%s\">%s (%d)</font></td></tr>\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, "<Table border=0 cellpadding=2 cellspacing=2>\n"
"<tr><td align=\"left\" bgcolor=\"#%s\">"
"<font color=\"#%s\" size=3 face=\"%s\">"
"<i><b>Who</b></i></font></td>\n<td align=\"left\" bgcolor=\"#%s\">"
"<font color=\"#%s\" size=3 face=\"%s\">"
"<i><b>Fact!</b></i></font></td><td align=\"left\" bgcolor=\"#%s\">"
"<font color=\"#%s\" size=3 face=\"%s\">"
"<i><b>Runner-up</b></i></font></td></tr>\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<br>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, "</table>\n");
free(buf);
}
#ifdef RECORDS
void do_records(FILE* output, char * inputnewstr)
{
if (inputnewstr)
{
FILE * inputnew;
fprintf (output, "<Table border=0 cellpadding=2 cellspacing=2>\n"
"<tr><td align=\"left\" bgcolor=\"#%s\">"
"<font color=\"#%s\" size=3 face=\"%s\">"
"<i><b>Who</b></i></font></td>\n<td align=\"left\" bgcolor=\"#%s\">"
"<font color=\"#%s\" size=3 face=\"%s\">"
"<i><b>Event</b></i></font></td><td align=\"left\" bgcolor=\"#%s\">"
"<font color=\"#%s\" size=3 face=\"%s\">"
"<i><b>Old Record</b></i></font></td></tr>\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, "<tr><td align=\"center\" bgcolor=\"#%s\"><font size=2 face=\"%s\">%s"
"</font></td><td align=\"left\" bgcolor=\"#%s\"><font size=3 face=\"%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, "</font></td><td align=\"left\" bgcolor=\"#%s\">"
"<font size=1 face=\"%s\">%s (%d)</font></td></tr>\n",
table_3_bg, font2, oldname, oldnum);
}
fprintf(output, "<tr><td align=\"center\" bgcolor=\"#%s\"><font size=2 face=\"%s\">%d"
"</font></td><td align=\"left\" bgcolor=\"#%s\"><font size=3 face=\"%s\">",
table_nick_bg, font2, numrecords, table_2_bg, font1);
fprintf(output, "record%s set today", (numrecords==1)?" was":"s were");
fprintf(output, "</font></td><td align=\"left\" bgcolor=\"#%s\">"
"<font size=1 face=\"%s\"></font></td></tr>\n",
table_3_bg, font2);
fclose(inputnew);
}
fprintf(output, "</font></table>\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, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" \"http://www.w3.org/TR/REC-html40/loose.dtd\">");
fprintf (output, "<!-- %s %s $Id: ircstats.c,v 1.28 2001/08/11 11:31:55 cph Exp $ by Colin Phipps (cph@lxdoom.linuxgames.com) and Andy Kempling (aurikan@hotmail.com)-->\n",PACKAGE,VERSION);
fprintf (output, "<html><head><title>#%s statistics created by %s and %s</title></head>\n", channame, myname, PACKAGE);
fprintf (output, "<body bgcolor=\"#%s\" text=\"#%s\" link=\"#%s\" vlink=\"#%s\"><center>\n%s\n", page_bg, page_text, page_text, page_text, header_text);
fprintf (output, "<font color=\"#%s\" size=5 face=\"%s\">"
"Channel stats for #%s - created by <a href=\"mailto:%s\">"
"<font color=\"#%s\" size=4 face=\"%s\">%s</font></a></font><br>\n",
page_text_contrast, font2, channame, myemail, page_text_contrast, font2, myname);
fprintf (output, "<hr width=\"50%%\">\n<font size=2 face=\"%s\">"
"Statistics generated from %s to %s</font><br>\n",
font2, edate->date, (chan0 + channum - 1)->date);
fprintf (output, "<font size=2 face=\"%s\">Stats generated on %s</font><br>\n", font2, ctime (&now));
fprintf (output, "<font size=2 face=\"%s\">During this %li-day reporting period a "
"total of %i persons visited #%s</font><br>\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,"<font size=1 face=\"%s\">", font2);
fputs ("The current topic is <b>", output);
output_content(output, chan0[x].curtopic);
fputs ("</b></font><br>\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("<font size=1 face=\"", output);
fputs(font2, output);
fputs("\"><b><i>", output);
if (chan0[x].quote.quote) output_content(output, chan0[x].quote.quote);
fputs("</i></b></font><br>\n", output);
fprintf (output, "<hr width=\"50%%\">\n");
fprintf (output, "<hr width=\"90%%\">\n");
fprintf (output, "<font color=\"#%s\" size=4 face=\"%s\"><i><u>Daily statistics</u></i>"
"</font><font color=\"#%s\" size=2 face=\"%s\">"
" <i>(Number of lines / 6 hours)</i></font><br>\n",
page_text_contrast, font2, page_text_contrast, font2);
fprintf (output, "<Table border=0><tr>\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, "<td align=\"center\" valign=\"bottom\">"
"<font size=1 face=\"%s\">%i<br>"
"<img src=\"pipe%iv%s\" width=22 height=%i alt=\"Bar\"></font></td>\n",
font3, tot_lines, y + 1, picext, (int) (128 * tot_lines / max_lines));
}
fprintf (output, "</tr><tr>\n");
for (x = 0; x < channum; x++)
if ((chan0 + x)->tlines)
fprintf (output, "<td colspan=4 bgcolor=\"#%s\" align=\"center\" valign=\"bottom\">"
"<font size=1 face=\"%s\">%s</font></td>\n",
day_usage_date_bg, font3, (chan0 + x)->date);
fprintf (output, "</tr></table>\n");
fprintf (output, "<br><hr width=\"90%%\">\n<Table border=0><tr>\n");
fprintf (output, "<td width=150><img src=\"pipe1h%s\" height=15 width=30 align=top alt=\"Pink\"><font color=\"#%s\" size=2 face=\"%s\"> Hours %.2i-%.2i</font></td>\n", picext, page_text_contrast, font2, 0, DAYSEC0);
fprintf (output, "<td width=150><img src=\"pipe2h%s\" height=15 width=30 align=top alt=\"Pink\"><font color=\"#%s\" size=2 face=\"%s\"> Hours %.2i-%.2i</font></td>\n", picext, page_text_contrast, font2, DAYSEC0, DAYSEC1);
fprintf (output, "<td width=150><img src=\"pipe3h%s\" height=15 width=30 align=top alt=\"Pink\"><font color=\"#%s\" size=2 face=\"%s\"> Hours %.2i-%.2i</font></td>\n", picext, page_text_contrast, font2, DAYSEC1, DAYSEC2);
fprintf (output, "<td width=150><img src=\"pipe4h%s\" height=15 width=30 align=top alt=\"Pink\"><font color=\"#%s\" size=2 face=\"%s\"> Hours %.2i-%.2i</font></td>\n", picext, page_text_contrast, font2, DAYSEC2, DAYSEC3);
fprintf (output, "</tr></table>\n");
fprintf (output, "<hr width=\"90%%\">\n");
fprintf (output, "<font color=\"#%s\" size=4 face=\"%s\">"
"<i><u>Channel load by hours</u></i></font><br>\n", page_text_contrast, font2);
fprintf (output, "<Table border=0><tr>\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, "<td align=\"center\" valign=\"bottom\"><font size=1 face=\"%s\">%.1f%%<br><img src=\"pipe%iv%s\" width=20 height=%i alt=\"Bar\"></font></td>\n", font3, (float) (1000 * chantotal->lines[x] / tot_lines) / 10, (int) (x / 6 + 1), picext, (int) (192 * chantotal->lines[x] / max_lines));
fprintf (output, "</tr><tr>\n");
for (x = 0; x < 24; x++)
fprintf (output, "<td bgcolor=\"#%.2x00%.2x\" align=\"center\"><font color=\"#%.2x%.2x%.2x\" size=1 face=\"%s\">%i</font></td>\n", (int) (192 * chantotal->lines[x] / max_lines + 63), (int) (128 - 128 * chantotal->lines[x] / max_lines), 255, 255, 255, font3, x);
fprintf (output, "</tr></table>\n");
fprintf (output, "<hr width=\"90%%\"><br>\n");
fprintf (output, "<font color=\"#%s\" size=4 face=\"%s\">"
"<i><u>Activity Statistics</u></i></font><br><font size=2 face=\"%s\">"
" <i>(Nicks sorted by number of lines written)</i></font><br><br>\n",
page_text_contrast, font2, font2);
fprintf (output, "<Table border=0 cellspacing=10><tr valign=\"top\"><td>\n");
fprintf (output, "<Table border=0 cellpadding=2 cellspacing=2>\n");
fprintf (output, "<tr><td></td><td align=\"left\" bgcolor=\"#%s\">"
"<font color=\"#%s\" size=3 face=\"%s\"><i><b> Nick</b></i></font></td>\n",
table_head_nick, page_text_contrast, font1);
fprintf (output, "<td align=\"left\" bgcolor=\"#%s\">"
"<font color=\"#%s\" size=3 face=\"%s\">"
"<i><b> Number of Lines</b></i></font></td>\n", table_head_other, page_text_contrast, font1);
fprintf (output, "<td align=\"left\" bgcolor=\"#%s\">"
"<font color=\"#%s\" size=3 face=\"%s\"><i><b> Quote</b></i></font></td>\n",
table_head_other, page_text_contrast, font1);
fprintf (output, "</tr>\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, "<tr><td align=\"right\" bgcolor=\"#%s\">"
"<font size=1 face=\"%s\">%i</font></td>", table_index_bg, font2, x + 1 - deluser);
fprintf (output, "<td align=\"left\" bgcolor=\"#%s\"><font size=2 face=\"%s\">"
"%s</font></td>\n", table_nick_bg, font2, (userloc + x)->name);
fprintf (output, "<td valign=\"middle\" bgcolor=\"#%s\">\n", table_2_bg);
for (y = 0; y < 4; y++)
if ((userloc + x)->texts[y] + (userloc + x)->acts[y])
fprintf (output, "<img src=\"pipe%ih%s\" height=15 width=%i align=top alt=\"%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, "<font size=2 face=\"%s\"> %s%li%s</font></td>\n",
font2, (x - deluser) ? "" : "<b>", (userloc + x)->lines, (x - deluser) ? "" : "</b>");
fprintf (output, "<td width=\"75%%\" align=\"left\" bgcolor=\"#%s\">"
"<font size=2 face=\"%s\"><i>", table_3_bg, font1);
if (userloc[x].quote.quote) output_content(output, userloc[x].quote.quote);
fputs("</i></font></td>\n</tr>\n", output);
}
fputs("</table></td><td>\n", output);
fprintf (output, "<Table border=0 cellpadding=0 cellspacing=0>\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, "<tr><td align=\"center\" nowrap>"
"<font color=\"#%s\" size=1 face=\"%s\">%s</font></td>"
"<td><img src=\"pipe%iv%s\" height=%i width=22 alt=\"%s (%i)\">"
"</td></tr>\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, "</table></td></tr></table>\n");
/* Now the random topics table */
fprintf (output, "<hr width=\"90%%\"><font color=\"#%s\" size=4 face=\"%s\">"
"<i><u>5 Random Topics</u></i></font>\n", page_text_contrast, font2);
do_random_table(output, &random_topics, "Topic");
/* Now the random kicks table */
fprintf(output, "<hr width=\"90%%\"><font color=\"#%s\" size=4 face=\"%s\">"
"<i><u>5 Random Kicks</u></i></font>\n", page_text_contrast, font2);
do_random_table(output, &random_kicks, "What happened");
/* Now some random facts */
fprintf (output, "<hr width=\"90%%\"><font color=\"#%s\" size=4 face=\"%s\">"
"<i><u>Big Numbers</u></i></font>\n", page_text_contrast, font2);
if (0 < usernum)
do_factoids(output);
#ifdef RECORDS
/* New records */
fprintf (output, "<hr width=\"90%%\"><font color=\"#%s\" size=4 face=\"%s\">"
"<i><u>Records Set Today</u></i></font>\n", page_text_contrast, font2);
if (0 < usernum)
do_records(output, recordnew);
#endif /* RECORDS */
/* Random URLs */
fprintf(output, "<hr width=\"90%%\"><font color=\"#%s\" size=4 face=\"%s\">"
"<i><u>5 Random URLs</u></i></font>\n", page_text_contrast, font2);
do_random_table(output, &random_urls, "URL");
/* Random signoffs */
fprintf(output, "<hr width=\"90%%\"><font color=\"#%s\" size=4 face=\"%s\">"
"<i><u>5 Random Signoffs</u></i></font>\n", page_text_contrast, font2);
do_random_table(output, &random_signoffs, "Quit message");
/* Now for the terminal table; credits, date, misc */
fprintf (output, "<hr width=\"100%%\"><Table width=\"95%%\" border=2><tr><td><Table width=\"100%%\" border=0 cellpadding=5 cellspacing=0>\n");
fprintf (output, "<tr><td align=\"left\" valign=\"middle\"><font size=2 face=\"%s\">This page was created on %s,\n", font2, ctime (&now));
fprintf (output, "with <a href=\"http://gruftistats.sourceforge.net/\">%s</a> %s by <a href=\"mailto:aurikan@hotmail.com\">Andy Kempling</a> and <A HREF=\"mailto:cph@lxdoom.linuxgames.com\">Colin Phipps</a>.</font><br>", PACKAGE, VERSION);
fprintf (output, "<font size=1 face=\"%s\">Processed %dKiB in %ds.</font></td>\n", font2, total_bytes_parsed >> 10, (int)(now - start));
fprintf (output, "<td><a href=\"http://validator.w3.org/check/referer\"><img border=0 src=\"http://validator.w3.org/images/vh40\" alt=\"Valid HTML 4.0!\" height=31 width=88></a></td>");
fprintf (output, "<td align=\"right\" valign=\"middle\"><font size=2 face=\"%s\">%s</font></td></tr>\n", font2, credits);
fprintf (output, "</table></td></tr></table>");
fprintf (output, "</center></body></html>");
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; i<NUM_RANDOM; i++)
if (p->random[i] && !stricmp(text, p->random[i]))
{
p->delled++;
return;
}
if (n<=NUM_RANDOM)
i = n-1;
else for (i=0; i<NUM_RANDOM; i++)
if (!(rand() % n--) && i)
break;
if (i == NUM_RANDOM) return;
if (p->random[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<LINES_HISTORY; i++)
if (history_line[i]) len += strlen(history_line[i]) + 1;
len += strlen(line);
retline = malloc(len * sizeof *retline + 1);
retline[0] = 0;
for (i=0; i<LINES_HISTORY; i++)
if (history_line[i]) {
strcat(retline, history_line[i]);
strcat(retline, "\n");
}
strcat(retline, line);
return retline;
}
/* New log parser. Completely rewritten to use regexps, remove line
* length limit, and hopefully be cleaner */
void ParseLog(FILE* infp, int verbose, struct log_parse_s* plp, size_t* pbp)
{
size_t linelen = 512;
char *line = malloc(sizeof(char)*linelen);
int daysec = 0, hour = 0;
int i;
user* lastuser = NULL;
int monologue = 0;
if (!(channel = FindChan ("Unknown")))
channel = CreateChan ("Unknown");
for (i=0; i<LINES_HISTORY; i++) history_line[i] = NULL;
for (;;) {
enum line_type_e linetype;
user *puser = NULL;
char *nick = NULL;
char *str = NULL;
char *othernick = NULL;
size_t ll;
if (!fgets(line,linelen,infp)) {
if (feof(infp)) break;
perror("fgets"); exit(-1);
}
while (((ll = strlen(line)) == linelen-1) && line[linelen-1] != '\n') {
/* Fiddly logic here.. double the buffer, read into the latter
* half (with +/-1 fudging to allow for the old terminating \0)
*/
char *p;
line = realloc(line, linelen*2);
p = line + linelen-1;
fgets(p,linelen+1,infp);
linelen *= 2;
}
if ((str = strchr(line,'\n'))) *str = 0;
str = NULL;
*pbp += ll;
stripansi(line);
{
/* Max number of regexp subcomponents we may need to match
* This is possibly > 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<LT_NUM; i++)
if (!regexec(&plp[i].compiled_regex,line,RM_NUM,rmatch,0)) break;
if (i == LT_NUM) {
if (verbose)
fprintf(stderr, "Failed to parse \"%s\"\n", line);
continue;
}
linetype = i;
/* If the line was time stamped, set the day segment appropriately */
if (plp[i].returned_bit[LP_TIME]) {
daysec = (hour = atoi(line+rmatch[plp[i].returned_bit[LP_TIME]].rm_so))/6;
if (daysec < 0 || daysec > 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] <nick> 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<LINES_HISTORY-1; i++)
history_line[i] = history_line[i+1];
history_line[LINES_HISTORY-1] = strdup(line);
if (nick) free(nick);
if (othernick) free(othernick);
if (str) free(str);
}
free(line);
}
int (*criterion)(const user*);
int
compare_adv (const void *arg1, const void *arg2)
{
return criterion((user *) arg2) - criterion((user *) arg1);
}
void qcsort(void * init, int elem, int size, int (*critfunc)(const user *))
{
criterion = critfunc;
qsort(init, elem, size, compare_adv);
}
#ifdef RECORDS
void ReplaceRecord(FILE * outputnew, user * locstart, int (*critfunc)(const user *), recordset * records, char * date)
{
int x, y, z;
int overnum; char overname[32], overdate[32];
for (x=0; x< RECORD_MAX; x++)
{
for (y=x; y<RECORD_MAX; y++)
{
if (critfunc(locstart + x) >= 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; y<RECORD_MAX; y++)
{
strcpy(records->record[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; x<RECORD_NUM; x++)
{
fscanf(inold, "%[^\n] ", oldrec[x].name);
for (y=0; y<RECORD_MAX; y++)
{
oldrec[x].record[y].num = 0;
fscanf(inold, "%[^\t] ", oldrec[x].record[y].name);
fscanf(inold, "%[^\t] ", oldrec[x].record[y].date);
fscanf(inold, "%i ", &(oldrec[x].record[y].num));
if (verbose>1) 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; x<RECORD_NUM; x++)
{
fprintf(inold, "%s\n", oldrec[x].name);
for (y=0; y<RECORD_MAX; y++)
{
fprintf(inold, "%s\t", oldrec[x].record[y].name);
fprintf(inold, "%s\t", oldrec[x].record[y].date);
fprintf(inold, "%i\n", oldrec[x].record[y].num);
}
}
fclose(inold);
}
}
}
void GenRecords_Seven(char * recordin, char * recordnew, chan * weekstat, char * record)
{
char * date = NULL;
strcpy(weekstat->name, "The channel");
date = (chan0 + chanmax - 1)->date;
}
#endif /* RECORDS */
syntax highlighted by Code2HTML, v. 0.9.1