/*
* FISG - Fast IRC Statistic Generator
* Programmed and designed by Matti 'ccr' Hamalainen
* (C) Copyright 2003-2004 Tecnic Software productions (TNSP)
*
* Please read file 'COPYING' for information on license and distribution.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <errno.h>
#include <ctype.h>
#include <time.h>
#include "fisg.h"
#include "parser.h"
#include "th_util.h"
#include "th_args.h"
#include "th_config.h"
#include "in_formats.h"
#include "out_formats.h"
/*
* Misc globals
*/
int setOutputFormat = 0;
int setCurrInputFormat = -1;
t_uint nsourceFileNames = 0, nconfigFileNames = 0;
int sourceFileFormats[SET_MAX_INFILES];
char *sourceFileNames[SET_MAX_INFILES],
*destFileName = NULL,
*userFilename = NULL,
*dumpUserFileName = NULL;
char *configFileNames[SET_MAX_INFILES];
char *progName = NULL;
/*
* Options and help
*/
t_opt optList[] = {
{ 0, '?', "help", "Show this help and exit", 0 },
{ 1, 'V', "version", "Show version and exit", 0 },
{ 2, 'o', "output", "Specify output file (default: stdout)", 1 },
{ 3, 'u', "user-file", "Users file (default: users.cfg)", 1 },
{ 4, 'f', "format", "Specify input format (no default)", 1 },
{ 5, 'O', "output-format","Specify output format", 1 },
{ 6, 'L', "list-formats", "Show list of predefined log- and output-formats", 0 },
{ 7, 'c', "config", "Specify configuration file (no default)", 1 },
{ 8, 'q', "quiet", "Use multiple times for more silence", 0 },
{ 9, 'd', "dump-userfile","Dump internal user information to given file", 1 },
};
const int optListN = (sizeof(optList) / sizeof(t_opt));
/*#define NICKDEBUG*/
void NDMSG(const char *pcFormat, ...)
{
#ifdef NICKDEBUG
va_list ap;
va_start(ap, pcFormat);
fprintf(stderr, "ND: ");
vfprintf(stderr, pcFormat, ap);
va_end(ap);
#endif
}
void NDPRINT(const char *pcFormat, ...)
{
#ifdef NICKDEBUG
va_list ap;
va_start(ap, pcFormat);
vfprintf(stderr, pcFormat, ap);
va_end(ap);
#endif
}
void showHelp()
{
th_showHelp(stdout,
optList, optListN,
progName, "[options] [source#1] [source#2...]");
}
void handleOpt(const int optN, char *optArg, char *currArg)
{
int i;
BOOL isFound;
switch (optN) {
case 0:
showHelp();
exit(0);
break;
case 1:
printf("%s v%s %s\n", th_prog_name, th_prog_version, th_prog_author);
exit(0);
break;
case 2:
/* Specify output filename */
if (optArg)
destFileName = optArg;
else {
THERR("No output filename specified!\n");
exit(2);
}
break;
case 3:
/* Specify user-file filename */
if (optArg)
userFilename = optArg;
else {
THERR("No userfile filename specified!\n");
exit(2);
}
break;
case 4:
/* Specify input format */
if (optArg)
{
/* Go through the list */
isFound = FALSE;
i = 0;
while ((i < nInputFormats) && (!isFound))
{
if (strcmp(optArg, inputFormats[i].ifName) == 0)
isFound = TRUE;
else
i++;
}
/* Check if found from predefined formats */
if (!isFound)
{
THERR("Invalid input (log-file) format '%s'\n", optArg);
exit(2);
}
setCurrInputFormat = i;
} else {
THERR("No input (logfile) format specified!\n");
exit(2);
}
break;
case 5:
/* Specify output format */
if (optArg)
{
/* Go through the list */
isFound = FALSE;
i = 0;
while ((i < nOutputFormats) && (!isFound))
{
if (strcmp(optArg, outputFormats[i].ofName) == 0)
isFound = TRUE;
else
i++;
}
/* Check */
if (!isFound)
{
THERR("Invalid output format '%s'\n", optArg);
exit(2);
}
setOutputFormat = i;
} else {
THERR("No output format specified!\n");
exit(2);
}
break;
case 6:
/* Show list of input and output formats */
THERR("Available pre-defined INPUT (log) formats:\n");
for (i = 0; i < nInputFormats; i++)
{
fprintf(stderr, " %-8s - %s %s\n",
inputFormats[i].ifName,
inputFormats[i].ifDescription,
(i == 0) ? "(default)" : "");
}
fprintf(stderr, "\n");
THERR("Available OUTPUT formats:\n");
for (i = 0; i < nOutputFormats; i++)
{
fprintf(stderr, " %-8s - %s %s\n",
outputFormats[i].ofName,
outputFormats[i].ofDescription,
(i == setOutputFormat) ? "(default)" : "");
}
fprintf(stderr, "\n");
exit(0);
break;
case 7:
/* Specify configuration filename */
if (optArg)
{
if (nconfigFileNames < SET_MAX_INFILES)
{
configFileNames[nconfigFileNames] = optArg;
nconfigFileNames++;
}
} else {
THERR("No configuration filename specified!\n");
exit(2);
}
break;
case 8:
/* Quiet -- lessen verbosity */
th_verbosityLevel--;
break;
case 9:
/* Specify dump-userfile filename */
if (optArg)
{
dumpUserFileName = optArg;
} else {
THERR("No user-dump filename specified!\n");
exit(2);
}
break;
default:
/* Error */
THERR("Unknown argument '%s'.\n", currArg);
break;
}
}
void handleFile(char *currArg)
{
/* Check if current input format has been set */
if (setCurrInputFormat < 0)
{
THERR("You need to specify an input log-format! (-f <format>)\n");
exit(3);
}
/* Add a filename */
if (nsourceFileNames < SET_MAX_INFILES)
{
sourceFileNames[nsourceFileNames] = currArg;
sourceFileFormats[nsourceFileNames] = setCurrInputFormat;
nsourceFileNames++;
} else {
THERR("Maximum number of input files (%i) exceeded!\n", SET_MAX_INFILES);
exit(3);
}
}
/*
* Allocates and initializes a new stats structure
*/
t_stats *fisg_stats_new(void)
{
t_stats *pStats;
/* Allocate memory for new node */
pStats = (t_stats *) calloc(1, sizeof(t_stats));
if (pStats == NULL) return NULL;
/* Initialize fields */
pStats->usersList = user_data_new();
pStats->usersIgnored = user_data_new();
pStats->usersRemoved = user_data_new();
/* Return result */
return pStats;
}
/*
* Deallocates stats structure
*/
void fisg_stats_free(t_stats *pStats)
{
assert(pStats);
/* Free user data */
user_data_free(pStats->usersList);
user_data_free(pStats->usersIgnored);
user_data_free(pStats->usersRemoved);
/* Free hashlists */
th_strhash_free(pStats->nickList);
th_strhash_free(pStats->urlList);
th_strlist_free(pStats->topicList);
/* Free indexes */
th_strindex_free(pStats->urlIndex);
th_strindex_free(pStats->topicIndex);
/* Free stats structure */
free(pStats);
}
/*
* Cleaning pass:
* - move ignored users to "ignored list"
* - move users who have stats below specified limits to "removed list"
*/
void fisg_stats_clean(t_stats *pStats)
{
t_user_data *pData;
t_user_entry *pCurr, *pNext;
assert(pStats);
/* Go through main linked list of users, moving nodes
* to other respective lists according to wanted rules
*/
pData = user_data_new();
if (!pData)
{
THERR("Could not allocate temporary t_user_data structure!\n");
return;
}
pCurr = pStats->usersList->pList;
while (pCurr)
{
pNext = pCurr->pNext;
if (pCurr->isIgnored)
{
/* Move ignored user to ignored list */
user_insert(pStats->usersIgnored, pCurr);
} else
if ((pCurr->nChars == 0) && (pCurr->nPublics == 0))
{
/* Remove user who does not pass qualifications */
user_insert(pStats->usersRemoved, pCurr);
} else
{
/* Move to new list */
user_insert(pData, pCurr);
}
pCurr = pNext;
}
/* Move the new list to current list */
pStats->usersList->pList = pData->pList;
/* We can't use user_data_free() here because that would delete all nodes */
free(pData);
}
/*
* Compute stats from userlist
*/
int fisg_stats_pass1(t_stats *pStats, t_fisgconfig *pCfg)
{
t_user_entry *pCurr;
int i;
t_float iTotalActivity, iMaxActivity;
assert(pStats);
/* Go through the userlist */
pCurr = pStats->usersList->pList;
while (pCurr)
{
/* Activity */
iTotalActivity = 0;
for (i = 0; i < SET_HOURS_DAY; i++)
{
if (pCfg->usePisgScoring)
pCurr->fActivityPerHour[i] = pCurr->nPublicsPerHour[i];
else
pCurr->fActivityPerHour[i] =
((t_float) pCurr->nWordsPerHour[i]) * ((t_float) pCurr->nPublicsPerHour[i]);
iTotalActivity += pCurr->fActivityPerHour[i];
pStats->fActivityPerHour[i] += pCurr->fActivityPerHour[i];
}
/* Compute activity-% for each hour */
for (i = 0; i < SET_HOURS_DAY; i++)
{
if (iTotalActivity > 0)
{
pCurr->fActivityPerHour[i] =
(pCurr->fActivityPerHour[i] / iTotalActivity) * 100.0f;
} else
pCurr->fActivityPerHour[i] = 0.0f;
}
/* Compute W/P and C/W */
if (pCurr->nPublics > 0)
pCurr->fWordsPerPublic = ((t_float) pCurr->nWords / (t_float) pCurr->nPublics);
else
pCurr->fWordsPerPublic = 0;
if (pCurr->nWords > 0)
pCurr->fCharsPerWord = ((t_float) pCurr->nChars / (t_float) pCurr->nWords);
else
pCurr->fCharsPerWord = 0;
/* Compute total score */
if (pCfg->usePisgScoring)
pCurr->fTotalScore = pCurr->nPublics;
else
pCurr->fTotalScore = (pCurr->fWordsPerPublic + pCurr->fCharsPerWord) * pCurr->nPublics;
/* Next node */
pCurr = pCurr->pNext;
}
/* Compute total activity percent */
pStats->activityPeak = -1;
iMaxActivity = -1;
iTotalActivity = 0.0f;
for (i = 0; i < SET_HOURS_DAY; i++)
{
iTotalActivity += pStats->fActivityPerHour[i];
if (pStats->fActivityPerHour[i] > iMaxActivity)
{
iMaxActivity = pStats->fActivityPerHour[i];
pStats->activityPeak = i;
}
}
if (iTotalActivity > 0)
{
for (i = 0; i < SET_HOURS_DAY; i++)
{
pStats->fActivityPerHour[i] = (pStats->fActivityPerHour[i] / iTotalActivity) * 100.0f;
}
}
return 0;
}
int fisg_stats_pass2(t_stats *pStats, t_fisgconfig *pCfg)
{
t_user_entry *pCurr;
t_ulint statMax, nUser;
assert(pStats);
/* Get configuration options */
if (pCfg->statOnlyListed)
{
statMax = pCfg->showTopUserMax;
if (statMax > pStats->usersList->n)
statMax = pStats->usersList->n;
} else
statMax = pStats->usersList->n;
/* Initialize */
pStats->mostStupid = pStats->mostLoud = pStats->mostActions =
pStats->mostModes = pStats->mostKicks = pStats->mostKicked =
pStats->mostCaps = pStats->mostHappy = pStats->mostSad =
pStats->mostURLs = pStats->mostJoins = pStats->mostTopics =
pStats->usersList->ppIndex[0];
/* Go through the userlist */
for (nUser = 0; nUser < statMax; nUser++)
{
pCurr = pStats->usersList->ppIndex[nUser];
/* More stupid */
if (pCurr->nQuestions >= pStats->mostStupid->nQuestions)
pStats->mostStupid = pCurr;
/* More loud? */
if (pCurr->nYelling >= pStats->mostLoud->nYelling)
pStats->mostLoud = pCurr;
/* More actions? */
if (pCurr->nActions >= pStats->mostActions->nActions)
pStats->mostActions = pCurr;
/* More kicks? */
if (pCurr->nKicks >= pStats->mostKicks->nKicks)
pStats->mostKicks = pCurr;
/* More kicked? */
if (pCurr->nGotKicked >= pStats->mostKicked->nGotKicked)
pStats->mostKicked = pCurr;
/* More caps per chars? */
if (pCurr->nCaps > 0)
pCurr->fCapsPercent = ((t_float) pCurr->nCaps / (t_float) pCurr->nChars) * 100.0f;
if (pCurr->fCapsPercent > pStats->mostCaps->fCapsPercent)
pStats->mostCaps = pCurr;
/* More happy? */
if (pCurr->fHappiness > pStats->mostHappy->fHappiness)
pStats->mostHappy = pCurr;
/* More sad? */
if (pCurr->fHappiness < pStats->mostSad->fHappiness)
pStats->mostSad = pCurr;
/* More URLs pasted? */
if (pCurr->nURLs >= pStats->mostURLs->nURLs)
pStats->mostURLs = pCurr;
/* More Joins? */
if (pCurr->nJoins >= pStats->mostJoins->nJoins)
pStats->mostJoins = pCurr;
/* More Topics? */
if (pCurr->nTopics >= pStats->mostTopics->nTopics)
pStats->mostTopics = pCurr;
}
return 0;
}
/* Compare function for qsort() that compares 2 nodes for fTotalScore
*/
int stats_index_cmp(const void *pNode1, const void *pNode2)
{
t_user_entry *pUser1, *pUser2;
pUser1 = * (t_user_entry **) pNode1;
pUser2 = * (t_user_entry **) pNode2;
if (pUser1->fTotalScore > pUser2->fTotalScore)
return -1;
else
if (pUser1->fTotalScore < pUser2->fTotalScore)
return 1;
else
return 0;
}
/*
* Output current user information to a give file
*/
void fisg_output_userfile_user(FILE *f, t_stats *pStats, t_user_entry *pNode)
{
t_str_node *pNick;
int i;
assert(f);
assert(pNode);
/* If ignored ... */
if (pNode->isIgnored)
fprintf(f, "!");
/* Username/handle */
fprintf(f, "%s:", pNode->userHandle);
/* Nicks */
for (i = 0; i < SET_HASH_MAXINDEX; i++)
{
/* Find from linked list */
pNick = pStats->nickList[i];
while (pNick)
{
if (pNick->pData == pNode)
fprintf(f, "%s ", pNick->pcStr);
pNick = pNick->pNext;
}
}
/* Rest of information */
fprintf(f, ":");
if (pNode->picPath)
fprintf(f, "%s:", pNode->picPath);
else
if (pNode->linkURL)
fprintf(f, ":");
if (pNode->linkURL)
fprintf(f, "%s", pNode->linkURL);
fprintf(f, "\n");
}
void fisg_output_userfile(FILE *f, t_stats *pStats)
{
t_user_entry *pCurr;
assert(f);
assert(pStats);
fprintf(f, "# Userfile for %s (%s)\n", th_prog_name, th_prog_fullname);
/* Output ignored users */
fprintf(f, "# Ignored users\n");
pCurr = pStats->usersIgnored->pList;
while (pCurr)
{
fisg_output_userfile_user(f, pStats, pCurr);
pCurr = pCurr->pNext;
}
/* Output normal users */
fprintf(f, "\n\n# Normal users\n");
pCurr = pStats->usersList->pList;
while (pCurr)
{
fisg_output_userfile_user(f, pStats, pCurr);
pCurr = pCurr->pNext;
}
}
BOOL fisg_warn_deprecated(t_config_item *pNode)
{
THERR("Setting '%s' is deprecated, update your configuration file!\n", pNode->itemName);
return TRUE;
}
/*
* NOTICE: Since this utility is designed to be "one pass/shot" program,
* we don't free any memory used here. If you take this code, remember it
* and add possible *free()'s where appropriate.
*/
int main(int argc, char *argv[])
{
FILE *tmpFile;
t_uint i, j;
int iResult, n;
t_fisgconfig genSet;
t_config *genConfig = NULL;
t_stats *genStats = NULL;
time_t myTime1, myTime2;
char tmpStr[1024] = "";
/*
* Initialize fundamentals
*/
time(&myTime1);
srandom(myTime1);
progName = argv[0];
th_init(FISG_NAME, FISG_FULLNAME, FISG_VERSION, FISG_COPYRIGHT, NULL);
/*
* Allocate stats
*/
genStats = fisg_stats_new();
if (genStats == NULL)
{
THERR("Could not allocate memory for statistics!\n");
return -11;
}
/*
* Parse arguments
*/
th_processArgs(argc, argv,
optList, optListN,
handleOpt, handleFile);
if (nsourceFileNames <= 0)
{
THERR("No input files specified!\n");
return 0;
}
/*
* Initialize configuration, read and parse configuration files
*/
/* Initialize configuration for main program */
genConfig = th_config_new();
th_config_add_str(genConfig, "gen_channel", NULL, &genSet.ircChannel, "#????");
th_config_add_str(genConfig, "gen_ircnet", NULL, &genSet.ircNetwork, NULL);
th_config_add_str(genConfig, "gen_message", NULL, &genSet.message, NULL);
th_config_add_str(genConfig, "gen_dateformat", NULL, &genSet.dateFormat, "%c");
th_config_add_bool(genConfig,"gen_auto_follow_nicks", NULL, &genSet.autoFollowNicks, FALSE);
th_config_add_bool(genConfig,"gen_auto_follow_heuristics",NULL,&genSet.autoHeuristics, FALSE);
th_config_add_str(genConfig, "gen_user_file", NULL, &genSet.userFilename, "users.genSet");
th_config_add_bool(genConfig,"gen_use_pisg_scoring", NULL, &genSet.usePisgScoring, FALSE);
th_config_add_bool(genConfig,"gen_strip_ctrlchars", NULL, &genSet.stripCtrlChars, FALSE);
th_config_add_bool(genConfig,"gen_stat_active_times", NULL, &genSet.statActiveTimes, TRUE);
th_config_add_bool(genConfig,"gen_stat_top_users", NULL, &genSet.statTopUsers, TRUE);
th_config_add_bool(genConfig,"gen_stat_almost_top", NULL, &genSet.statAlmostTop, TRUE);
th_config_add_bool(genConfig,"gen_stat_urls", NULL, &genSet.statURLs, TRUE);
th_config_add_bool(genConfig,"gen_stat_topics", NULL, &genSet.statTopics, TRUE);
th_config_add_bool(genConfig,"gen_stat_big_numbers", NULL, &genSet.statBigNumbers, TRUE);
th_config_add_bool(genConfig,"gen_stat_only_listed", NULL, &genSet.statOnlyListed, FALSE);
th_config_add_bool(genConfig,"gen_stat_show_happiness",NULL, &genSet.showHappy, TRUE);
th_config_add_bool(genConfig,"gen_stat_show_comment", NULL, &genSet.showComment, TRUE);
th_config_add_bool(genConfig,"gen_stat_show_picture", NULL, &genSet.showPicture, FALSE);
th_config_add_bool(genConfig,"gen_stat_show_url", NULL, &genSet.showURL, FALSE);
th_config_add_uint(genConfig,"gen_showmax", fisg_warn_deprecated, &genSet.showTopUserMax, 35);
th_config_add_uint(genConfig,"gen_showrest", fisg_warn_deprecated, &genSet.showAlmostMax, 20);
th_config_add_uint(genConfig,"gen_show_topmax", NULL, &genSet.showTopUserMax, 35);
th_config_add_uint(genConfig,"gen_show_restmax", NULL, &genSet.showAlmostMax, 20);
th_config_add_uint(genConfig,"gen_show_urlsmax", NULL, &genSet.showURLsMax, 10);
th_config_add_uint(genConfig,"gen_show_topicsmax", NULL, &genSet.showTopicsMax, 10);
th_config_add_uint(genConfig,"gen_min_comment_length", NULL, &genSet.commentMinLength, 10);
th_config_add_uint(genConfig,"gen_max_comment_length", NULL, &genSet.commentMaxLength, 60);
/*
th_config_add_str(genConfig, "", NULL, &, );
th_config_add_bool(genConfig,"", NULL, &, );
th_config_add_int(genConfig, "", NULL, &, );
*/
/* Initialize modules configuration */
for (n = 0; n < nOutputFormats; n++)
if (outputFormats[n].ofInit(genConfig) != 0)
{
THERR("Error initializing output module #%d (%s).\n", n, outputFormats[n].ofName);
}
/* Read configuration files */
if (nconfigFileNames <= 0)
{
THERR("No configuration file(s) specified.\n");
}
for (i = 0; i < nconfigFileNames; i++)
{
THMSG(1, "Configuration '%s'\n", configFileNames[i]);
if (th_config_read(configFileNames[i], genConfig) != 0)
return -12;
}
/*
* Parse the users file
*/
if (userFilename)
parse_userfile(userFilename, genStats, &genSet);
else
parse_userfile(genSet.userFilename, genStats, &genSet);
/*
* Read the source-file(s)
*/
THMSG(1, "Parsing %d sources. Please wait...\n", nsourceFileNames);
THMSG(2, "Processed ");
for (i = 0; i < nsourceFileNames; i++)
{
/* Try to open the logfile */
if ((tmpFile = fopen(sourceFileNames[i], "ra")) == NULL)
{
THERR("Error opening input file '%s' (%s)\n", sourceFileNames[i], strerror(errno));
return -1;
}
/* Parse with selected parser */
iResult = fisg_parse_log(tmpFile, genStats,
&inputFormats[sourceFileFormats[i]],
&genSet);
if (th_verbosityLevel >= 2)
{
/* Show progress meter */
for (j = 0; j < strlen(tmpStr); j++)
fputc('\b', stderr);
snprintf(tmpStr, sizeof(tmpStr),
"%i%%", (((i+1) * 100) / nsourceFileNames));
fputs(tmpStr, stderr);
}
/* Close file, report errors */
fclose(tmpFile);
if (iResult < 0)
{
THERR("Error #%i reading file (%s)\n", iResult, strerror(errno));
return 2;
}
}
THPRINT(2, "\n");
/*
* Calculate rank and stats for sorting
*/
THMSG(2, "Computing statistics...\n");
if (fisg_stats_pass1(genStats, &genSet) < 0)
{
THERR("Error while computing rankings!\n");
return -10;
}
/*
* Perform cleaning pass
*/
THMSG(2, "Cleaning up...\n");
fisg_stats_clean(genStats);
/*
* Index userlists, check number of users
*/
THMSG(2, "Indexing...\n");
if (user_data_makeindex(genStats->usersList) != 0 ||
user_data_makeindex(genStats->usersIgnored) != 0 ||
user_data_makeindex(genStats->usersRemoved) != 0)
{
THERR("Error while indexing userlists!\n");
return -9;
}
if (genStats->usersList->n <= 0)
{
THERR("No users found? Check that you really specified correct log-format!\n");
return -10;
}
/*
* Sort the indexes by score
*/
THMSG(1, "%ld users (%ld total, %ld ignored, %ld removed), sorting...\n",
genStats->usersList->n,
(genStats->usersList->n + genStats->usersIgnored->n + genStats->usersRemoved->n),
genStats->usersIgnored->n,
genStats->usersRemoved->n);
qsort(genStats->usersList->ppIndex, genStats->usersList->n,
sizeof(t_user_entry *), stats_index_cmp);
/*
* Compute rest of user-related statistics
*/
THMSG(1, "Computing user-related stats...\n");
if (fisg_stats_pass2(genStats, &genSet) < 0)
{
THERR("Error while computing statistics!\n");
return -10;
}
/*
* Create sorted indexes
*/
THMSG(1, "Computing rest of stats...\n");
if (genStats->urlList)
{
genStats->urlIndex = th_strhash_makeindex(genStats->urlList);
if (genStats->urlIndex)
th_strindex_sort_nused(genStats->urlIndex);
}
if (genStats->topicList)
{
genStats->topicIndex = th_strlist_makeindex(genStats->topicList);
if (!genStats->topicIndex)
{
THERR("Could not create topicIndex!\n");
return -10;
}
}
/*
* Get current and calculate elapsed time
*/
time(&myTime2);
genStats->nTimeElapsed = (myTime2 - myTime1);
/*
* Output statistics in wanted format
*/
if ((setOutputFormat < 0) || (setOutputFormat >= nOutputFormats))
{
THERR("Invalid output format, falling back to default.\n");
setOutputFormat = 0;
}
THMSG(1, "Using %s\n", outputFormats[setOutputFormat].ofDescription);
if (destFileName == NULL) tmpFile = stdout; else
if ((tmpFile = fopen(destFileName, "wa")) == NULL)
{
THERR("Error opening output file '%s' (%s)\n", destFileName, strerror(errno));
return -1;
}
iResult = outputFormats[setOutputFormat].ofFunction(tmpFile, genStats, &genSet);
fclose(tmpFile);
/*
* Output userfile, if wanted
*/
if (dumpUserFileName)
{
if ((tmpFile = fopen(dumpUserFileName, "wa")) == NULL)
{
THERR("Error opening dump-userfile '%s' (%s)\n", dumpUserFileName, strerror(errno));
} else {
/* Output userfile */
fisg_output_userfile(tmpFile, genStats);
fclose(tmpFile);
}
}
/*
* OK! Show final stats
*/
THMSG(1, "%ld lines in %ld logfile(s), total of %1.2f MB\n",
genStats->nLines,
genStats->nLogFiles,
((t_float) genStats->nChars) / (1024.0f*1024.0f)
);
if (iResult == 0)
{
THMSG(1, "Done. Time elapsed %ld hours, %ld minutes and %ld seconds.\n",
(genStats->nTimeElapsed / (60*60)),
(genStats->nTimeElapsed % (60*60)) / 60,
(genStats->nTimeElapsed % (60*60)) % 60
);
} else
THERR("Error creating output file! Return code #%i.\n", iResult);
/*
* Free allocated memory
*/
if (genStats)
fisg_stats_free(genStats);
if (genConfig)
th_config_free(genConfig);
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1