From 91e5543757dad266f041f46d11bf973966368a30 Mon Sep 17 00:00:00 2001 From: Jon Bergli Heier Date: Sat, 5 Jun 2010 23:59:38 +0200 Subject: Mostly-working implementation of time tracking for users. Needs some more testing. --- config.c | 28 +++++- config.h | 1 + export_xml.c | 12 +++ parsing.c | 315 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- regexset.c | 103 ++++++++++++++++--- regexset.h | 10 +- user.c | 50 ++++++++++ user.h | 15 +++ 8 files changed, 506 insertions(+), 28 deletions(-) diff --git a/config.c b/config.c index 2cb6aa6..e710e83 100644 --- a/config.c +++ b/config.c @@ -39,6 +39,14 @@ int cfg_init() { ircstats_config.wordlen_min = 3; } + if(!config_lookup_string(&config, "log_date_format", &ircstats_config.log_date_format)) { + ircstats_config.log_date_format = NULL; + } + + if(!config_lookup_string(&config, "day_date_format", &ircstats_config.day_date_format)) { + ircstats_config.day_date_format = NULL; + } + config_setting_t *regexes_setting = config_lookup(&config, "regexes"); if(!config_setting_is_aggregate(regexes_setting)) { fprintf(stderr, "Setting \"regexes\" must be an aggregate type.\n"); @@ -49,12 +57,24 @@ int cfg_init() { for(int i = 0; i < regex_count; i++) { config_setting_t *re_setting = config_setting_get_elem(regexes_setting, i); - const char *text, *join, *kick; - if(!(config_setting_lookup_string(re_setting, "text", &text) && config_setting_lookup_string(re_setting, "join", &join) && config_setting_lookup_string(re_setting, "kick", &kick))) { + const char *text, *join, *part, *quit, *kick, *nick_changed, *log_opened, *day_changed, *log_date_format, *day_date_format; + if(!(config_setting_lookup_string(re_setting, "text", &text) + && config_setting_lookup_string(re_setting, "join", &join) + && config_setting_lookup_string(re_setting, "part", &part) + && config_setting_lookup_string(re_setting, "quit", &quit) + && config_setting_lookup_string(re_setting, "kick", &kick) + && config_setting_lookup_string(re_setting, "nick_changed", &nick_changed) + && config_setting_lookup_string(re_setting, "log_opened", &log_opened) + && config_setting_lookup_string(re_setting, "day_changed", &day_changed) + )) { fprintf(stderr, "Regex set #%d missing one or more keys.\n", i+1); return 0; } - if(!rs_add(text, join, kick)) + if(!config_setting_lookup_string(re_setting, "log_date_format", &log_date_format)) + log_date_format = NULL; + if(!config_setting_lookup_string(re_setting, "day_date_format", &day_date_format)) + day_date_format = NULL; + if(!rs_add(text, join, part, quit, kick, nick_changed, log_opened, day_changed, log_date_format, day_date_format)) return 0; } @@ -86,7 +106,7 @@ int cfg_init() { } config_setting_t *files = config_setting_get_member(channel_setting, "files"); if(!config_setting_is_aggregate(files)) { - fprintf(stderr, "Setting \"files\" must be an aggregate type (no files added).\n", name); + fprintf(stderr, "Setting \"files\" must be an aggregate type (no files added).\n"); continue; } int file_count = config_setting_length(files); diff --git a/config.h b/config.h index c62dfa5..163be18 100644 --- a/config.h +++ b/config.h @@ -6,6 +6,7 @@ void cfg_free(); struct ircstats_config_t { int threads, monolog_min, wordlen_min; + const char *log_date_format, *day_date_format; }; extern struct ircstats_config_t ircstats_config; diff --git a/export_xml.c b/export_xml.c index 1bed18b..4f77bbc 100644 --- a/export_xml.c +++ b/export_xml.c @@ -79,6 +79,18 @@ int export_xml(struct channel_t *channel, struct user_t *users) { xmlTextWriterWriteFormatString(writer, "%llu", user->monologs); xmlTextWriterEndElement(writer); + xmlTextWriterStartElement(writer, (const xmlChar*)"seen_first"); + xmlTextWriterWriteFormatString(writer, "%lu", user->seen_first); + xmlTextWriterEndElement(writer); + + xmlTextWriterStartElement(writer, (const xmlChar*)"seen_last"); + xmlTextWriterWriteFormatString(writer, "%lu", user->seen_last); + xmlTextWriterEndElement(writer); + + xmlTextWriterStartElement(writer, (const xmlChar*)"time_total"); + xmlTextWriterWriteFormatString(writer, "%lu", user->time_total); + xmlTextWriterEndElement(writer); + /* Add lines for this user. */ xmlTextWriterStartElement(writer, (const xmlChar*)"lines"); for(int h = 0; h < 24; h++) { diff --git a/parsing.c b/parsing.c index 9653eb8..fea0e96 100644 --- a/parsing.c +++ b/parsing.c @@ -3,6 +3,7 @@ #include #include #include +#include #include "parsing.h" #include "channel.h" @@ -15,12 +16,15 @@ #define TEXT_BUFFER_SIZE 0x400 #define LINE_BUFFER_SIZE 0x400 #define TIME_BUFFER_SIZE 0xf +#define DATE_BUFFER_SIZE 0x20 -static pthread_mutex_t user_mutex, word_mutex, channel_mutex; +static pthread_mutex_t user_mutex, user_time_mutex, word_mutex, channel_mutex, time_mutex; static struct user_t *last_user = NULL; static int in_monolog = 0, monolog_len = 0; +static struct tm now_global; + static inline void add_word(struct user_t *user, wchar_t *word, int len) { pthread_mutex_lock(&user_mutex); user->words++; @@ -36,6 +40,24 @@ static inline void add_word(struct user_t *user, wchar_t *word, int len) { static void process_file(FILE *f, struct channel_t *channel, struct regexset_t *rs) { char line[LINE_BUFFER_SIZE]; + const char *log_date_format, *day_date_format; + struct tm now; + + if(rs->log_date_format) { + log_date_format = rs->log_date_format; + } else if(ircstats_config.log_date_format) { + log_date_format = ircstats_config.log_date_format; + } else { + log_date_format = NULL; + } + + if(rs->day_date_format) { + day_date_format = rs->day_date_format; + } else if(ircstats_config.day_date_format) { + day_date_format = ircstats_config.day_date_format; + } else { + day_date_format = NULL; + } while(fgets(line, LINE_BUFFER_SIZE, f)) { int rc; @@ -82,6 +104,30 @@ static void process_file(FILE *f, struct channel_t *channel, struct regexset_t * user->lines[time_i]++; pthread_mutex_unlock(&user_mutex); + pthread_mutex_lock(&time_mutex); + now = now_global; + pthread_mutex_unlock(&time_mutex); + + now.tm_hour = hour; + now.tm_min = min; + time_t now_ut = mktime(&now); + + pthread_mutex_lock(&user_time_mutex); + struct user_time_t *user_time = user_time_get(nick, 0); + if(user_time->seen_first == 0) { + user_time->seen_first = now_ut; + } + /* Set if user is not yet seen. */ + if(user_time->last_join == 0) { + user_time->last_join = now_ut; + } + /* Add to online total. */ + if(user_time->seen_last > 0) { + user_time->time_total += now_ut - user_time->seen_last; + } + user_time->seen_last = now_ut; + pthread_mutex_unlock(&user_time_mutex); + pthread_mutex_lock(&channel_mutex); channel->hours[time_i]++; pthread_mutex_unlock(&channel_mutex); @@ -112,25 +158,240 @@ static void process_file(FILE *f, struct channel_t *channel, struct regexset_t * rc = pcre_exec(rs->join, rs->join_e, line, strlen(line), 0, 0, ovector, 30); if(rc > 0) { - char nick[NICK_BUFFER_SIZE]; + char nick[NICK_BUFFER_SIZE], hour_s[TIME_BUFFER_SIZE], min_s[TIME_BUFFER_SIZE]; pcre_copy_named_substring(rs->join, line, ovector, rc, "nick", nick, NICK_BUFFER_SIZE); - pthread_mutex_lock(&user_mutex); - struct user_t *user = user_get(nick); - pthread_mutex_unlock(&user_mutex); + pcre_copy_named_substring(rs->join, line, ovector, rc, "hour", hour_s, TIME_BUFFER_SIZE); + pcre_copy_named_substring(rs->join, line, ovector, rc, "minute", min_s, TIME_BUFFER_SIZE); + + int hour, min; + hour = atoi(hour_s); + min = atoi(min_s); + + pthread_mutex_lock(&time_mutex); + now = now_global; + pthread_mutex_unlock(&time_mutex); + + now.tm_hour = hour; + now.tm_min = min; + time_t now_ut = mktime(&now); + + pthread_mutex_lock(&user_time_mutex); + struct user_time_t *user_time = user_time_get(nick, 0); + + if(user_time->seen_first == 0) { + user_time->seen_first = now_ut; + } + user_time->last_join = user_time->seen_last = now_ut; + pthread_mutex_unlock(&user_time_mutex); + + continue; + } + + rc = pcre_exec(rs->part, rs->part_e, line, strlen(line), 0, 0, ovector, 30); + if(rc > 0) { + char nick[NICK_BUFFER_SIZE], hour_s[TIME_BUFFER_SIZE], min_s[TIME_BUFFER_SIZE]; + pcre_copy_named_substring(rs->part, line, ovector, rc, "nick", nick, NICK_BUFFER_SIZE); + pcre_copy_named_substring(rs->part, line, ovector, rc, "hour", hour_s, TIME_BUFFER_SIZE); + pcre_copy_named_substring(rs->part, line, ovector, rc, "minute", min_s, TIME_BUFFER_SIZE); + + int hour, min; + hour = atoi(hour_s); + min = atoi(min_s); + + pthread_mutex_lock(&time_mutex); + now = now_global; + pthread_mutex_unlock(&time_mutex); + + now.tm_hour = hour; + now.tm_min = min; + time_t now_ut = mktime(&now); + + pthread_mutex_lock(&user_time_mutex); + struct user_time_t *user_time = user_time_get(nick, 1); + + if(user_time->real_user) { + struct user_time_t *temp = user_time->real_user; + user_time->real_user = NULL; + user_time = temp; + } + + if(user_time->seen_first == 0) { + user_time->seen_first = now_ut; + } + /* Add to online total. */ + if(user_time->seen_last > 0) { + user_time->time_total += now_ut - user_time->seen_last; + } + user_time->seen_last = now_ut; + pthread_mutex_unlock(&user_time_mutex); + + continue; + } + + rc = pcre_exec(rs->quit, rs->quit_e, line, strlen(line), 0, 0, ovector, 30); + if(rc > 0) { + char nick[NICK_BUFFER_SIZE], hour_s[TIME_BUFFER_SIZE], min_s[TIME_BUFFER_SIZE]; + pcre_copy_named_substring(rs->quit, line, ovector, rc, "nick", nick, NICK_BUFFER_SIZE); + pcre_copy_named_substring(rs->quit, line, ovector, rc, "hour", hour_s, TIME_BUFFER_SIZE); + pcre_copy_named_substring(rs->quit, line, ovector, rc, "minute", min_s, TIME_BUFFER_SIZE); + + int hour, min; + hour = atoi(hour_s); + min = atoi(min_s); + + pthread_mutex_lock(&time_mutex); + now = now_global; + pthread_mutex_unlock(&time_mutex); + + now.tm_hour = hour; + now.tm_min = min; + time_t now_ut = mktime(&now); + + pthread_mutex_lock(&user_time_mutex); + struct user_time_t *user_time = user_time_get(nick, 1); + + if(user_time->real_user) { + struct user_time_t *temp = user_time->real_user; + user_time->real_user = NULL; + user_time = temp; + } + + if(user_time->seen_first == 0) { + user_time->seen_first = now_ut; + } + /* Add to online total. */ + if(user_time->seen_last > 0) { + user_time->time_total += now_ut - user_time->seen_last; + } + user_time->seen_last = now_ut; + pthread_mutex_unlock(&user_time_mutex); + + continue; + } + + rc = pcre_exec(rs->nick_changed, rs->nick_changed_e, line, strlen(line), 0, 0, ovector, 30); + if(rc > 0) { + char oldnick[NICK_BUFFER_SIZE], newnick[NICK_BUFFER_SIZE], hour_s[TIME_BUFFER_SIZE], min_s[TIME_BUFFER_SIZE]; + pcre_copy_named_substring(rs->nick_changed, line, ovector, rc, "old_nick", oldnick, NICK_BUFFER_SIZE); + pcre_copy_named_substring(rs->nick_changed, line, ovector, rc, "new_nick", newnick, NICK_BUFFER_SIZE); + pcre_copy_named_substring(rs->nick_changed, line, ovector, rc, "hour", hour_s, TIME_BUFFER_SIZE); + pcre_copy_named_substring(rs->nick_changed, line, ovector, rc, "minute", min_s, TIME_BUFFER_SIZE); + + int hour, min; + hour = atoi(hour_s); + min = atoi(min_s); + + pthread_mutex_lock(&time_mutex); + now = now_global; + pthread_mutex_unlock(&time_mutex); + + now.tm_hour = hour; + now.tm_min = min; + time_t now_ut = mktime(&now); + + pthread_mutex_lock(&user_time_mutex); + struct user_time_t *from_user = user_time_get(oldnick, 1); + struct user_time_t *to_user = user_time_get(newnick, 1); + + if(from_user->real_user) { + to_user->real_user = from_user->real_user; + from_user->real_user = NULL; + } else { + to_user->real_user = from_user; + } + if(to_user->real_user) { + to_user->time_total += now_ut - to_user->seen_last; + to_user->real_user->seen_last = now_ut; + } else { + from_user->time_total += now_ut - from_user->seen_last; + from_user->seen_last = now_ut; + } + pthread_mutex_unlock(&user_time_mutex); + + continue; + } + + rc = pcre_exec(rs->log_opened, rs->log_opened_e, line, strlen(line), 0, 0, ovector, 30); + if(rc > 0) { + char date[DATE_BUFFER_SIZE]; + + if(!log_date_format) { + continue; + } + + pcre_copy_named_substring(rs->log_opened, line, ovector, rc, "date", date, DATE_BUFFER_SIZE); + if(!strptime(date, log_date_format, &now)) { + printf("log fail: %s\n", date); + continue; + } + + pthread_mutex_lock(&time_mutex); + now_global = now; + pthread_mutex_unlock(&time_mutex); + + continue; + } + + rc = pcre_exec(rs->day_changed, rs->day_changed_e, line, strlen(line), 0, 0, ovector, 30); + if(rc > 0) { + char date[DATE_BUFFER_SIZE]; + + if(!day_date_format) { + continue; + } + + pcre_copy_named_substring(rs->day_changed, line, ovector, rc, "date", date, DATE_BUFFER_SIZE); + if(!strptime(date, day_date_format, &now)) { + printf("day fail: %s\n", date); + continue; + } + + pthread_mutex_lock(&time_mutex); + now_global = now; + pthread_mutex_unlock(&time_mutex); + continue; } rc = pcre_exec(rs->kick, rs->kick_e, line, strlen(line), 0, 0, ovector, 30); if(rc > 0) { - char nick[NICK_BUFFER_SIZE], victim[NICK_BUFFER_SIZE]; + char nick[NICK_BUFFER_SIZE], victim[NICK_BUFFER_SIZE], hour_s[TIME_BUFFER_SIZE], min_s[TIME_BUFFER_SIZE]; + pcre_copy_named_substring(rs->kick, line, ovector, rc, "hour", hour_s, TIME_BUFFER_SIZE); + pcre_copy_named_substring(rs->kick, line, ovector, rc, "minute", min_s, TIME_BUFFER_SIZE); pcre_copy_named_substring(rs->kick, line, ovector, rc, "nick", nick, NICK_BUFFER_SIZE); pcre_copy_named_substring(rs->kick, line, ovector, rc, "victim", victim, NICK_BUFFER_SIZE); + + int hour, min; + hour = atoi(hour_s); + min = atoi(min_s); + + pthread_mutex_lock(&time_mutex); + now = now_global; + pthread_mutex_unlock(&time_mutex); + + now.tm_hour = hour; + now.tm_min = min; + time_t now_ut = mktime(&now); + pthread_mutex_lock(&user_mutex); struct user_t *user = user_get(nick), *victim_user = user_get(victim); user->kicks++; victim_user->kicked++; pthread_mutex_unlock(&user_mutex); + + pthread_mutex_lock(&user_time_mutex); + struct user_time_t *user_time = user_time_get(nick, 0); + if(user_time->seen_first == 0) { + user_time->seen_first = now_ut; + } + /* Add to online total. */ + if(user_time->seen_last > 0) { + user_time->time_total += now_ut - user_time->seen_last; + } + user_time->seen_last = now_ut; + pthread_mutex_unlock(&user_time_mutex); + continue; } } @@ -149,13 +410,49 @@ static void *thread_func(void *arg) { return NULL; } +static void merge_user_time_data(struct user_time_t *user_time) { + struct user_t *user; + + user = user_get(user_time->nick); + + if(user->seen_first == 0 || user_time->seen_first < user->seen_first) { + user->seen_first = user_time->seen_first; + } + + if(user_time->seen_last > user->seen_last) { + user->seen_last = user_time->seen_last; + } + + printf("adding %lu to %s\n", user_time->time_total, user->nick); + user->time_total += user_time->time_total; +} + +static void merge_time_data() { + for(int i = 0; i < USERS_MAX; i++) { + struct user_time_t *user_time; + + user_time = &users_time[i]; + if(user_time->nick) + merge_user_time_data(user_time); + user_time = user_time->next; + + while(user_time) { + merge_user_time_data(user_time); + user_time = user_time->next; + } + } +} + void process(int thread_n) { pthread_mutex_init(&user_mutex, NULL); + pthread_mutex_init(&user_time_mutex, NULL); pthread_mutex_init(&word_mutex, NULL); pthread_mutex_init(&channel_mutex, NULL); + pthread_mutex_init(&time_mutex, NULL); /* Parsing stuff goes here. */ for(int chan_i = 0; chan_i < channel_get_count(); chan_i++) { user_init(); + user_time_init(); word_init(); struct channel_t *channel = channel_get(chan_i); printf("Channel %s\n", channel->name); @@ -190,11 +487,17 @@ void process(int thread_n) { fclose(f); file = file->next; } + + merge_time_data(); + export_xml(channel, users); user_free(); + user_time_free(); word_free(); } + pthread_mutex_destroy(&time_mutex); pthread_mutex_destroy(&user_mutex); + pthread_mutex_destroy(&user_time_mutex); pthread_mutex_destroy(&word_mutex); pthread_mutex_destroy(&channel_mutex); } diff --git a/regexset.c b/regexset.c index 61a22be..0d28113 100644 --- a/regexset.c +++ b/regexset.c @@ -20,10 +20,12 @@ static void re_error(const char *name, const char *pattern, const char *error, i fprintf(stderr, "%s\n", pattern); for(int i = 0; i < erroffset; i++) fprintf(stderr, " "); fprintf(stderr, "^\n"); - regexes = realloc(regexes, --rs_count * sizeof(struct regexset_t)); } -struct regexset_t *rs_add(const char *text, const char *join, const char *kick) { +struct regexset_t *rs_add(const char *text, const char *join, const char *part, + const char *quit, const char *kick, const char *nick_changed, + const char *log_opened, const char *day_changed, const char *log_date_format, + const char *day_date_format) { regexes = realloc(regexes, ++rs_count * sizeof(struct regexset_t)); if(!regexes) { /* If we end up here, we cannot free any previously compiled pcre patterns. */ @@ -39,7 +41,7 @@ struct regexset_t *rs_add(const char *text, const char *join, const char *kick) rs->text = pcre_compile(text, 0, &error, &erroffset, rs_pcre_tables); if(rs->text == NULL) { re_error("text", text, error, erroffset); - return NULL; + goto free_last_regex; } rs->text_e = pcre_study(rs->text, 0, &error); @@ -47,29 +49,94 @@ struct regexset_t *rs_add(const char *text, const char *join, const char *kick) rs->join = pcre_compile(join, 0, &error, &erroffset, rs_pcre_tables); if(rs->join == NULL) { - pcre_free(rs->text); - if(rs->text_e) - pcre_free(rs->text_e); re_error("join", join, error, erroffset); - return NULL; + goto free_text; } rs->join_e = pcre_study(rs->join, 0, &error); + rs->part = pcre_compile(part, 0, &error, &erroffset, rs_pcre_tables); + if(rs->part == NULL) { + re_error("part", part, error, erroffset); + goto free_join; + } + rs->part_e = pcre_study(rs->part, 0, &error); + + rs->quit = pcre_compile(quit, 0, &error, &erroffset, rs_pcre_tables); + if(rs->quit == NULL) { + re_error("quit", quit, error, erroffset); + goto free_part; + } + rs->quit_e = pcre_study(rs->quit, 0, &error); + rs->kick = pcre_compile(kick, 0, &error, &erroffset, rs_pcre_tables); if(rs->kick == NULL) { - pcre_free(rs->text); - pcre_free(rs->join); - if(rs->text_e) { - pcre_free(rs->text_e); - pcre_free(rs->join_e); - } re_error("kick", kick, error, erroffset); - return NULL; - + goto free_quit; } rs->kick_e = pcre_study(rs->kick, 0, &error); + rs->nick_changed = pcre_compile(nick_changed, 0, &error, &erroffset, rs_pcre_tables); + if(rs->nick_changed == NULL) { + re_error("nick_changed", nick_changed, error, erroffset); + goto free_kick; + } + rs->nick_changed_e = pcre_study(rs->nick_changed, 0, &error); + + rs->log_opened = pcre_compile(log_opened, 0, &error, &erroffset, rs_pcre_tables); + if(rs->log_opened == NULL) { + re_error("log_opened", log_opened, error, erroffset); + goto free_nick_changed; + } + rs->log_opened_e = pcre_study(rs->log_opened, 0, &error); + + rs->day_changed = pcre_compile(day_changed, 0, &error, &erroffset, rs_pcre_tables); + if(rs->day_changed == NULL) { + re_error("day_changed", day_changed, error, erroffset); + goto free_log_opened; + } + rs->day_changed_e = pcre_study(rs->day_changed, 0, &error); + + /* These are managed by libconfig and must never be free'd. */ + rs->log_date_format = log_date_format; + rs->day_date_format = day_date_format; + return rs; + + + /* NOTE: Make sure these are in the exact opposite order than above. */ + +free_log_opened: + pcre_free(rs->log_opened); + if(rs->log_opened_e) pcre_free(rs->log_opened_e); + +free_nick_changed: + pcre_free(rs->nick_changed); + if(rs->nick_changed_e) pcre_free(rs->nick_changed_e); + +free_kick: + pcre_free(rs->kick); + if(rs->kick_e) pcre_free(rs->kick_e); + +free_quit: + pcre_free(rs->quit); + if(rs->quit_e) pcre_free(rs->quit_e); + +free_part: + pcre_free(rs->part); + if(rs->part_e) pcre_free(rs->part_e); + +free_join: + pcre_free(rs->join); + if(rs->join_e) pcre_free(rs->join_e); + +free_text: + pcre_free(rs->text); + if(rs->text_e) pcre_free(rs->text_e); + +free_last_regex: + regexes = realloc(regexes, --rs_count * sizeof(struct regexset_t)); + + return NULL; } struct regexset_t *rs_get(int index) { @@ -84,6 +151,12 @@ void rs_free() { pcre_free(regexes[i].join); if(regexes[i].join_e) pcre_free(regexes[i].join_e); + pcre_free(regexes[i].part); + if(regexes[i].part_e) + pcre_free(regexes[i].part_e); + pcre_free(regexes[i].quit); + if(regexes[i].quit_e) + pcre_free(regexes[i].quit_e); pcre_free(regexes[i].kick); if(regexes[i].kick_e) pcre_free(regexes[i].kick_e); diff --git a/regexset.h b/regexset.h index 3c023f1..aba4431 100644 --- a/regexset.h +++ b/regexset.h @@ -4,12 +4,16 @@ #include struct regexset_t { - pcre *text, *join, *kick; - pcre_extra *text_e, *join_e, *kick_e; + pcre *text, *join, *part, *quit, *kick, *nick_changed, *log_opened, *day_changed; + pcre_extra *text_e, *join_e, *part_e, *quit_e, *kick_e, *nick_changed_e, *log_opened_e, *day_changed_e; + const char *log_date_format, *day_date_format; }; void rs_init(); -struct regexset_t *rs_add(const char *text, const char *join, const char *kick); +struct regexset_t *rs_add(const char *text, const char *join, const char *part, + const char *quit, const char *kick, const char *nick_changed, + const char *log_opened, const char *day_changed, const char *log_date_format, + const char *day_date_format); struct regexset_t *rs_get(int index); void rs_free(); diff --git a/user.c b/user.c index b64ca7c..d6554b9 100644 --- a/user.c +++ b/user.c @@ -7,6 +7,8 @@ struct user_t *users; +struct user_time_t *users_time; + void user_init() { users = malloc(sizeof(struct user_t) * USERS_MAX); memset(users, 0, sizeof(struct user_t) * USERS_MAX); @@ -33,6 +35,7 @@ struct user_t *user_get(char *nick) { user->nick = strdup(nick); memset(user->lines, 0, 24*4 * sizeof(unsigned long)); user->words = user->characters = user->kicks = user->kicked = user->monolog_lines = user->monologs = 0; + user->seen_first = user->seen_last = user->last_join = user->time_total = 0; user->next = NULL; char *_nick = nick_get(nick); user->real_user = strcmp(nick, _nick) ? user_get(_nick) : NULL; @@ -56,3 +59,50 @@ void user_free() { } free(users); } + +void user_time_init() { + users_time = malloc(sizeof(struct user_time_t) * USERS_MAX); + memset(users_time, 0, sizeof(struct user_time_t) * USERS_MAX); +} + +struct user_time_t *user_time_get(char *nick, int ignore_real) { + unsigned long hash = sdbm(nick); + int index = hash % USERS_MAX; + + struct user_time_t *user = &users_time[index]; + /* If hash doesn't match and there exists another user, fetch it. */ + while(user->hash != hash && user->next) user = user->next; + /* If hash still doesn't match and the user exists, add a new user. */ + if(user->hash != hash && user->nick) { + struct user_time_t *temp_user = malloc(sizeof(struct user_time_t)); + user->next = temp_user; + user = temp_user; + /* Initialize nick to NULL so the user can be correctly added. */ + user->nick = NULL; + } + /* Add the new user data to the current pointer if none was found. */ + if(!user->nick) { + user->hash = hash; + user->nick = strdup(nick); + user->seen_first = user->seen_last = user->last_join = user->time_total = 0; + user->real_user = user->next = NULL; + } + + return (!ignore_real && user->real_user ? user->real_user : user); +} + +void user_time_free() { + struct user_time_t *user; + for(int i = 0; i < USERS_MAX; i++) { + if(users_time[i].nick) + free(users_time[i].nick); + user = users_time[i].next; + while(user) { + struct user_time_t *temp = user->next; + free(user->nick); + free(user); + user = temp; + } + } + free(users_time); +} diff --git a/user.h b/user.h index b08dd36..dc4304e 100644 --- a/user.h +++ b/user.h @@ -1,6 +1,8 @@ #ifndef _USER_H_ #define _USER_H_ +#include + #define USERS_MAX 1000 struct user_t { @@ -8,13 +10,26 @@ struct user_t { char *nick; unsigned long lines[24*4]; unsigned long long words, characters, kicks, kicked, monolog_lines, monologs; + time_t seen_first, seen_last, last_join, time_total; struct user_t *real_user, *next; }; +struct user_time_t { + unsigned long hash; + char *nick; + time_t seen_first, seen_last, last_join, time_total; + struct user_time_t *real_user, *next; +}; + void user_init(); struct user_t *user_get(char *nick); void user_free(); +void user_time_init(); +struct user_time_t *user_time_get(char *nick, int ignore_real); +void user_time_free(); + extern struct user_t *users; +extern struct user_time_t *users_time; #endif -- cgit v1.2.3