]> git.ipfire.org Git - thirdparty/sarg.git/commitdiff
Split the top user report generation
authorFrederic Marchal <fmarchal@users.sourceforge.net>
Sun, 19 Jul 2015 12:55:31 +0000 (14:55 +0200)
committerFrederic Marchal <fmarchal@users.sourceforge.net>
Sun, 19 Jul 2015 12:55:31 +0000 (14:55 +0200)
Use one function to prepare the top user list and another to generate the
html report.

It is the first step to merge the html and email reports.

include/conf.h
topuser.c

index 44d53843fbbd0e11bcd1ec1d610cf1cd11f5beb8..875804c2f75057bc40895357fd57df28abc6a152 100644 (file)
@@ -500,7 +500,6 @@ int  realtime_access_log_lines;
 int  rc;
 int  ntopsites;
 int  nrepday;
-int  ntopuser;
 bool  squid24;
 //! \c True to keep the temporary files for inspection.
 bool KeepTempLog;
index 42a5f4976cb36ed87fee84f268831967b9cd00d6..19bd2bc40d3e2624fb21319aae94c93adc932c48 100644 (file)
--- a/topuser.c
+++ b/topuser.c
 #include "include/defs.h"
 #include "include/filelist.h"
 
+struct TopUserStatistics
+{
+       long long int ttnbytes;
+       long long int ttnacc;
+       long long int ttnelap;
+       long long int ttnincache;
+       long long int ttnoucache;
+       int totuser;
+};
+
+struct SortInfoStruct
+{
+       const char *sort_field;
+       const char *sort_order;
+};
+
 extern struct globalstatstruct globstat;
 extern bool smartfilter;
 extern FileListObject UserAgentLog;
@@ -56,14 +72,17 @@ static void set_total_users(int totuser)
        globstat.totuser=totuser;
 }
 
-void topuser(void)
+/*!
+ * Generate a HTML report with the users downloading the most.
+ *
+ * \param ListFile Name of the file with the sorted list of users.
+ * \param Statis Statistics about the data collected from the log file.
+ * \param SortInfo Strings explaining how the list was sorted.
+ */
+static void TopUser_HtmlReport(const char *ListFile,struct TopUserStatistics *Statis,struct SortInfoStruct *SortInfo)
 {
-       FileObject *fp_in = NULL;
        FileObject *fp_top1 = NULL;
-       FILE *fp_top2 = NULL, *fp_top3 = NULL;
-       long long int ttnbytes=0, ttnacc=0, tnacc=0;
-       long long int tnbytes=0, ttnelap=0, tnelap=0;
-       long long int tnincache=0, tnoucache=0, ttnincache=0, ttnoucache=0;
+       FILE *fp_top3 = NULL;
        long long int nbytes;
        long long int nacc;
        long long int elap, incac, oucac;
@@ -71,158 +90,23 @@ void topuser(void)
        double perc2=0.00;
        double inperc=0.00, ouperc=0.00;
        int posicao=0;
-       char olduser[MAX_USER_LEN], csort[MAXLEN];
-       char wger[MAXLEN], top1[MAXLEN], top2[MAXLEN], top3[MAXLEN];
+       char top3[MAXLEN];
        char user[MAX_USER_LEN];
-       const char *sfield="-n -k 2,2";
-       const char *order;
-       const char *sort_field;
-       const char *sort_order;
        char title[80];
        char *warea;
-       int  totuser=0;
-       int  topcount=0;
-       int cstatus;
+       bool ntopuser=false;
+       int topcount=0;
        struct getwordstruct gwarea;
        longline line;
-       struct generalitemstruct item;
        struct userinfostruct *uinfo;
 
-       if (debugz>=LogLevel_Process)
-               debuga(__FILE__,__LINE__,_("Creating top users report...\n"));
-       ntopuser = 0;
-       snprintf(wger,sizeof(wger),"%s/sarg-general",outdirname);
-       if((fp_in=FileObject_Open(wger))==NULL) {
-               debuga(__FILE__,__LINE__,_("Cannot open file \"%s\": %s\n"),wger,FileObject_GetLastOpenError());
-               exit(EXIT_FAILURE);
-       }
-
-       snprintf(top2,sizeof(top2),"%s/top.tmp",outdirname);
-       if((fp_top2=fopen(top2,"w"))==NULL) {
-               debuga(__FILE__,__LINE__,_("Cannot open file \"%s\": %s\n"),top2,strerror(errno));
-               exit(EXIT_FAILURE);
-       }
-
-       olduser[0]='\0';
-       totuser=0;
-
-       if ((line=longline_create())==NULL) {
-               debuga(__FILE__,__LINE__,_("Not enough memory to read file \"%s\"\n"),wger);
-               exit(EXIT_FAILURE);
-       }
-
-       while((warea=longline_read(fp_in,line))!=NULL) {
-               ger_read(warea,&item,wger);
-               if(item.total) continue;
-               if(strcmp(olduser,item.user) != 0) {
-                       totuser++;
-
-                       if (olduser[0] != '\0') {
-                               /*
-                               This complicated printf is due to Microsoft's inability to comply with any standard. Msvcrt is unable
-                               to print a long long int unless it is exactly 64-bits long.
-                               */
-                               fprintf(fp_top2,"%s\t%"PRIu64"\t%"PRIu64"\t%"PRIu64"\t%"PRIu64"\t%"PRIu64"\n",olduser,(uint64_t)tnbytes,(uint64_t)tnacc,(uint64_t)tnelap,(uint64_t)tnincache,(uint64_t)tnoucache);
-
-                               ttnbytes+=tnbytes;
-                               ttnacc+=tnacc;
-                               ttnelap+=tnelap;
-                               ttnincache+=tnincache;
-                               ttnoucache+=tnoucache;
-                       }
-                       strcpy(olduser,item.user);
-                       tnbytes=0;
-                       tnacc=0;
-                       tnelap=0;
-                       tnincache=0;
-                       tnoucache=0;
-               }
-
-               tnbytes+=item.nbytes;
-               tnacc+=item.nacc;
-               tnelap+=item.nelap;
-               tnincache+=item.incache;
-               tnoucache+=item.oucache;
-       }
-       if (FileObject_Close(fp_in)) {
-               debuga(__FILE__,__LINE__,_("Read error in \"%s\": %s\n"),wger,FileObject_GetLastCloseError());
-               exit(EXIT_FAILURE);
-       }
-       longline_destroy(&line);
-
-       if (olduser[0] != '\0') {
-               /*
-               This complicated printf is due to Microsoft's inability to comply with any standard. Msvcrt is unable
-               to print a long long int unless it is exactly 64-bits long.
-               */
-               fprintf(fp_top2,"%s\t%"PRIu64"\t%"PRIu64"\t%"PRIu64"\t%"PRIu64"\t%"PRIu64"\n",olduser,(uint64_t)tnbytes,(uint64_t)tnacc,(uint64_t)tnelap,(uint64_t)tnincache,(uint64_t)tnoucache);
-
-               ttnbytes+=tnbytes;
-               ttnacc+=tnacc;
-               ttnelap+=tnelap;
-               ttnincache+=tnincache;
-               ttnoucache+=tnoucache;
-       }
-       if (fclose(fp_top2)==EOF) {
-               debuga(__FILE__,__LINE__,_("Write error in \"%s\": %s\n"),top2,strerror(errno));
-               exit(EXIT_FAILURE);
-       }
-
-#ifdef ENABLE_DOUBLE_CHECK_DATA
-       if (ttnacc!=globstat.nacc || ttnbytes!=globstat.nbytes || ttnelap!=globstat.elap ||
-           ttnincache!=globstat.incache || ttnoucache!=globstat.oucache) {
-               debuga(__FILE__,__LINE__,_("Total statistics mismatch when reading \"%s\" to produce the top users\n"),wger);
-               exit(EXIT_FAILURE);
-       }
-#endif
-
-       set_total_users(totuser);
-
-       if((TopuserSort & TOPUSER_SORT_USER) != 0) {
-               sfield="-k 1,1";
-               sort_field=_("user");
-       } else if((TopuserSort & TOPUSER_SORT_CONNECT) != 0) {
-               sfield="-n -k 3,3";
-               sort_field=_("connect");
-       } else if((TopuserSort & TOPUSER_SORT_TIME) != 0) {
-               sfield="-n -k 4,4";
-               sort_field=pgettext("duration","time");
-       } else {
-               sort_field=_("bytes");
-       }
-
-       if((TopuserSort & TOPUSER_SORT_REVERSE) == 0) {
-               order="";
-               sort_order=_("normal");
-       } else {
-               order="-r";
-               sort_order=_("reverse");
-       }
-
-       snprintf(top1,sizeof(top1),"%s/top",outdirname);
-       if (snprintf(csort,sizeof(csort),"sort -T \"%s\" -t \"\t\" %s %s -o \"%s\" \"%s\"", tmp, order, sfield, top1, top2)>=sizeof(csort)) {
-               debuga(__FILE__,__LINE__,_("Sort command too long when sorting file \"%s\" to \"%s\"\n"),top2,top1);
-               exit(EXIT_FAILURE);
-       }
-       cstatus=system(csort);
-       if (!WIFEXITED(cstatus) || WEXITSTATUS(cstatus)) {
-               debuga(__FILE__,__LINE__,_("sort command return status %d\n"),WEXITSTATUS(cstatus));
-               debuga(__FILE__,__LINE__,_("sort command: %s\n"),csort);
-               exit(EXIT_FAILURE);
-       }
-
-       if((fp_top1=FileObject_Open(top1))==NULL) {
-               debuga(__FILE__,__LINE__,_("Cannot open file \"%s\": %s\n"),top1,FileObject_GetLastOpenError());
-               exit(EXIT_FAILURE);
-       }
-
-       if (!KeepTempLog && unlink(top2)) {
-               debuga(__FILE__,__LINE__,_("Cannot delete \"%s\": %s\n"),top2,strerror(errno));
+       if ((fp_top1=FileObject_Open(ListFile))==NULL) {
+               debuga(__FILE__,__LINE__,_("Cannot open file \"%s\": %s\n"),ListFile,FileObject_GetLastOpenError());
                exit(EXIT_FAILURE);
        }
 
        snprintf(top3,sizeof(top3),"%s/"INDEX_HTML_FILE,outdirname);
-       if((fp_top3=fopen(top3,"w"))==NULL) {
+       if ((fp_top3=fopen(top3,"w"))==NULL) {
                debuga(__FILE__,__LINE__,_("Cannot open file \"%s\": %s\n"),top3,strerror(errno));
                exit(EXIT_FAILURE);
        }
@@ -234,7 +118,7 @@ void topuser(void)
        fputs("</td></tr>\n",fp_top3);
        if ((ReportType & REPORT_TYPE_TOPUSERS) != 0) {
                fputs("<tr><td class=\"header_c\">",fp_top3);
-               fprintf(fp_top3,_("Sort: %s, %s"),sort_field,sort_order);
+               fprintf(fp_top3,_("Sort: %s, %s"),SortInfo->sort_field,SortInfo->sort_order);
                fputs("</td></tr>\n",fp_top3);
                fprintf(fp_top3,"<tr><th class=\"header_c\">%s</th></tr>\n",_("Top users"));
        } else {
@@ -310,49 +194,45 @@ void topuser(void)
 
        greport_prepare();
 
-       ntopuser = 0;
-
        if ((line=longline_create())==NULL) {
-               debuga(__FILE__,__LINE__,_("Not enough memory to read file \"%s\"\n"),top1);
+               debuga(__FILE__,__LINE__,_("Not enough memory to read file \"%s\"\n"),ListFile);
                exit(EXIT_FAILURE);
        }
 
-       while((warea=longline_read(fp_top1,line))!=NULL) {
+       while ((warea=longline_read(fp_top1,line))!=NULL) {
                getword_start(&gwarea,warea);
                if (getword(user,sizeof(user),&gwarea,'\t')<0) {
-                       debuga(__FILE__,__LINE__,_("Invalid user in file \"%s\"\n"),top1);
+                       debuga(__FILE__,__LINE__,_("Invalid user in file \"%s\"\n"),ListFile);
                        exit(EXIT_FAILURE);
                }
                if (getword_atoll(&nbytes,&gwarea,'\t')<0) {
-                       debuga(__FILE__,__LINE__,_("Invalid number of bytes in file \"%s\"\n"),top1);
+                       debuga(__FILE__,__LINE__,_("Invalid number of bytes in file \"%s\"\n"),ListFile);
                        exit(EXIT_FAILURE);
                }
                if (getword_atoll(&nacc,&gwarea,'\t')<0) {
-                       debuga(__FILE__,__LINE__,_("Invalid number of accesses in file \"%s\"\n"),top1);
+                       debuga(__FILE__,__LINE__,_("Invalid number of accesses in file \"%s\"\n"),ListFile);
                        exit(EXIT_FAILURE);
                }
                if (getword_atoll(&elap,&gwarea,'\t')<0) {
-                       debuga(__FILE__,__LINE__,_("Invalid elapsed time in file \"%s\"\n"),top1);
+                       debuga(__FILE__,__LINE__,_("Invalid elapsed time in file \"%s\"\n"),ListFile);
                        exit(EXIT_FAILURE);
                }
                if (getword_atoll(&incac,&gwarea,'\t')<0) {
-                       debuga(__FILE__,__LINE__,_("Invalid in-cache size in file \"%s\"\n"),top1);
+                       debuga(__FILE__,__LINE__,_("Invalid in-cache size in file \"%s\"\n"),ListFile);
                        exit(EXIT_FAILURE);
                }
                if (getword_atoll(&oucac,&gwarea,'\n')<0) {
-                       debuga(__FILE__,__LINE__,_("Invalid out-of-cache size in file \"%s\"\n"),top1);
+                       debuga(__FILE__,__LINE__,_("Invalid out-of-cache size in file \"%s\"\n"),ListFile);
                        exit(EXIT_FAILURE);
                }
-               if(nacc < 1)
+               if (nacc < 1)
                        continue;
-               ntopuser = 1;
-               if(TopUsersNum > 0 && topcount >= TopUsersNum) break;
-               tnbytes=nbytes;
-               tnelap=elap;
+               ntopuser=true;
+               if (TopUsersNum>0 && topcount>=TopUsersNum) break;
 
                uinfo=userinfo_find_from_id(user);
                if (!uinfo) {
-                       debuga(__FILE__,__LINE__,_("Unknown user ID %s in file \"%s\"\n"),user,top1);
+                       debuga(__FILE__,__LINE__,_("Unknown user ID %s in file \"%s\"\n"),user,ListFile);
                        exit(EXIT_FAILURE);
                }
                uinfo->topuser=1;
@@ -394,16 +274,16 @@ void topuser(void)
                }
                if((TopUserFields & TOPUSERFIELDS_BYTES) != 0) {
                        fputs("<td class=\"data\"",fp_top3);
-                       if (SortTableJs[0]) fprintf(fp_top3," sorttable_customkey=\"%"PRId64"\"",(int64_t)tnbytes);
-                       fprintf(fp_top3,">%s</td>",fixnum(tnbytes,1));
+                       if (SortTableJs[0]) fprintf(fp_top3," sorttable_customkey=\"%"PRId64"\"",(int64_t)nbytes);
+                       fprintf(fp_top3,">%s</td>",fixnum(nbytes,1));
                }
                if((TopUserFields & TOPUSERFIELDS_SETYB) != 0) {
-                       perc=(ttnbytes) ? tnbytes * 100. / ttnbytes : 0.;
+                       perc=(Statis->ttnbytes) ? nbytes * 100. / Statis->ttnbytes : 0.;
                        fprintf(fp_top3,"<td class=\"data\">%3.2lf%%</td>",perc);
                }
                if((TopUserFields & TOPUSERFIELDS_IN_CACHE_OUT) != 0) {
-                       inperc=(tnbytes) ? incac * 100. / tnbytes : 0.;
-                       ouperc=(tnbytes) ? oucac * 100. / tnbytes : 0.;
+                       inperc=(nbytes) ? incac * 100. / nbytes : 0.;
+                       ouperc=(nbytes) ? oucac * 100. / nbytes : 0.;
                        fprintf(fp_top3,"<td class=\"data\">%3.2lf%%</td><td class=\"data\">%3.2lf%%</td>",inperc,ouperc);
 #ifdef ENABLE_DOUBLE_CHECK_DATA
                        if ((inperc!=0. || ouperc!=0.) && fabs(inperc+ouperc-100.)>=0.01) {
@@ -413,16 +293,16 @@ void topuser(void)
                }
                if((TopUserFields & TOPUSERFIELDS_USED_TIME) != 0) {
                        fputs("<td class=\"data\"",fp_top3);
-                       if (SortTableJs[0]) fprintf(fp_top3," sorttable_customkey=\"%"PRId64"\"",(int64_t)tnelap);
-                       fprintf(fp_top3,">%s</td>",buildtime(tnelap));
+                       if (SortTableJs[0]) fprintf(fp_top3," sorttable_customkey=\"%"PRId64"\"",(int64_t)elap);
+                       fprintf(fp_top3,">%s</td>",buildtime(elap));
                }
                if((TopUserFields & TOPUSERFIELDS_MILISEC) != 0) {
                        fputs("<td class=\"data\"",fp_top3);
-                       if (SortTableJs[0]) fprintf(fp_top3," sorttable_customkey=\"%"PRId64"\"",(int64_t)tnelap);
-                       fprintf(fp_top3,">%s</td>",fixnum2(tnelap,1));
+                       if (SortTableJs[0]) fprintf(fp_top3," sorttable_customkey=\"%"PRId64"\"",(int64_t)elap);
+                       fprintf(fp_top3,">%s</td>",fixnum2(elap,1));
                }
                if((TopUserFields & TOPUSERFIELDS_PTIME) != 0) {
-                       perc2=(ttnelap) ? elap * 100. / ttnelap : 0.;
+                       perc2=(Statis->ttnelap) ? elap * 100. / Statis->ttnelap : 0.;
                        fprintf(fp_top3,"<td class=\"data\">%3.2lf%%</td>",perc2);
                }
 
@@ -431,16 +311,16 @@ void topuser(void)
                topcount++;
        }
        if (FileObject_Close(fp_top1)) {
-               debuga(__FILE__,__LINE__,_("Read error in \"%s\": %s\n"),top1,FileObject_GetLastCloseError());
+               debuga(__FILE__,__LINE__,_("Read error in \"%s\": %s\n"),ListFile,FileObject_GetLastCloseError());
                exit(EXIT_FAILURE);
        }
-       if (!KeepTempLog && unlink(top1)) {
-               debuga(__FILE__,__LINE__,_("Cannot delete \"%s\": %s\n"),top1,strerror(errno));
+       if (!KeepTempLog && unlink(ListFile)) {
+               debuga(__FILE__,__LINE__,_("Cannot delete \"%s\": %s\n"),ListFile,strerror(errno));
                exit(EXIT_FAILURE);
        }
        longline_destroy(&line);
 
-       if((TopUserFields & TOPUSERFIELDS_TOTAL) != 0) {
+       if ((TopUserFields & TOPUSERFIELDS_TOTAL) != 0) {
                fputs("<tfoot><tr>",fp_top3);
                if((TopUserFields & TOPUSERFIELDS_NUM) != 0)
                        fputs("<td></td>",fp_top3);
@@ -452,15 +332,15 @@ void topuser(void)
                        fprintf(fp_top3,"<th class=\"header_l\">%s</th>",_("TOTAL"));
 
                if((TopUserFields & TOPUSERFIELDS_CONNECT) != 0)
-                       fprintf(fp_top3,"<th class=\"header_r\">%s</th>",fixnum(ttnacc,1));
+                       fprintf(fp_top3,"<th class=\"header_r\">%s</th>",fixnum(Statis->ttnacc,1));
                if((TopUserFields & TOPUSERFIELDS_BYTES) != 0)
-                       fprintf(fp_top3,"<th class=\"header_r\">%15s</th>",fixnum(ttnbytes,1));
+                       fprintf(fp_top3,"<th class=\"header_r\">%15s</th>",fixnum(Statis->ttnbytes,1));
                if((TopUserFields & TOPUSERFIELDS_SETYB) != 0)
                        fputs("<td></td>",fp_top3);
                if((TopUserFields & TOPUSERFIELDS_IN_CACHE_OUT) != 0)
                {
-                       inperc=(ttnbytes) ? ttnincache * 100. / ttnbytes : 0.;
-                       ouperc=(ttnbytes) ? ttnoucache *100. / ttnbytes : 0.;
+                       inperc=(Statis->ttnbytes) ? Statis->ttnincache * 100. / Statis->ttnbytes : 0.;
+                       ouperc=(Statis->ttnbytes) ? Statis->ttnoucache *100. / Statis->ttnbytes : 0.;
                        fprintf(fp_top3,"<th class=\"header_r\">%3.2lf%%</th><th class=\"header_r\">%3.2lf%%</th>",inperc,ouperc);
 #ifdef ENABLE_DOUBLE_CHECK_DATA
                        if (fabs(inperc+ouperc-100.)>=0.01) {
@@ -469,15 +349,15 @@ void topuser(void)
 #endif
                }
                if((TopUserFields & TOPUSERFIELDS_USED_TIME) != 0)
-                       fprintf(fp_top3,"<th class=\"header_r\">%s</th>",buildtime(ttnelap));
+                       fprintf(fp_top3,"<th class=\"header_r\">%s</th>",buildtime(Statis->ttnelap));
                if((TopUserFields & TOPUSERFIELDS_MILISEC) != 0)
-                       fprintf(fp_top3,"<th class=\"header_r\">%s</th>",fixnum2(ttnelap,1));
+                       fprintf(fp_top3,"<th class=\"header_r\">%s</th>",fixnum2(Statis->ttnelap,1));
 
                fputs("</tr>\n",fp_top3);
        }
        greport_cleanup();
 
-       if(ntopuser && (TopUserFields & TOPUSERFIELDS_AVERAGE) != 0) {
+       if (ntopuser && (TopUserFields & TOPUSERFIELDS_AVERAGE) != 0) {
                fputs("<tr>",fp_top3);
                if((TopUserFields & TOPUSERFIELDS_NUM) != 0)
                        fputs("<td></td>",fp_top3);
@@ -489,19 +369,19 @@ void topuser(void)
                        fprintf(fp_top3,"<th class=\"header_l\">%s</th>",_("AVERAGE"));
 
                if((TopUserFields & TOPUSERFIELDS_CONNECT) != 0)
-                       fprintf(fp_top3,"<th class=\"header_r\">%s</th>",fixnum(ttnacc/totuser,1));
+                       fprintf(fp_top3,"<th class=\"header_r\">%s</th>",fixnum(Statis->ttnacc/Statis->totuser,1));
                if((TopUserFields & TOPUSERFIELDS_BYTES) != 0) {
-                       tnbytes=(totuser) ? ttnbytes / totuser : 0;
-                       fprintf(fp_top3,"<th class=\"header_r\">%15s</th>",fixnum(tnbytes,1));
+                       nbytes=(Statis->totuser) ? Statis->ttnbytes / Statis->totuser : 0;
+                       fprintf(fp_top3,"<th class=\"header_r\">%15s</th>",fixnum(nbytes,1));
                }
                if((TopUserFields & TOPUSERFIELDS_SETYB) != 0)
                        fputs("<td></td>",fp_top3);
                if((TopUserFields & TOPUSERFIELDS_IN_CACHE_OUT) != 0)
                        fputs("<td></td><td></td>",fp_top3);
                if((TopUserFields & TOPUSERFIELDS_USED_TIME) != 0)
-                       fprintf(fp_top3,"<th class=\"header_r\">%s</th>",buildtime(ttnelap/totuser));
+                       fprintf(fp_top3,"<th class=\"header_r\">%s</th>",buildtime(Statis->ttnelap/Statis->totuser));
                if((TopUserFields & TOPUSERFIELDS_MILISEC) != 0)
-                       fprintf(fp_top3,"<th class=\"header_r\">%s</th>",fixnum2(ttnelap/totuser,1));
+                       fprintf(fp_top3,"<th class=\"header_r\">%s</th>",fixnum2(Statis->ttnelap/Statis->totuser,1));
                fputs("</tr></tfoot>\n",fp_top3);
        }
 
@@ -511,6 +391,159 @@ void topuser(void)
                debuga(__FILE__,__LINE__,_("Write error in \"%s\": %s\n"),top3,strerror(errno));
                exit(EXIT_FAILURE);
        }
+}
+
+/*!
+ * Produce a report with the user downloading the most data.
+ */
+void topuser(void)
+{
+       FileObject *fp_in = NULL;
+       FILE *fp_top2;
+       char wger[MAXLEN];
+       char top1[MAXLEN];
+       char top2[MAXLEN];
+       longline line;
+       long long int tnacc=0;
+       long long int tnbytes=0, tnelap=0;
+       long long int tnincache=0, tnoucache=0;
+       char *warea;
+       struct generalitemstruct item;
+       char olduser[MAX_USER_LEN], csort[MAXLEN];
+       const char *sfield="-n -k 2,2";
+       const char *order;
+       int cstatus;
+       struct TopUserStatistics Statis;
+       struct SortInfoStruct SortInfo;
+
+       if (debugz>=LogLevel_Process)
+               debuga(__FILE__,__LINE__,_("Creating top users report...\n"));
+
+       memset(&Statis,0,sizeof(Statis));
+
+       snprintf(wger,sizeof(wger),"%s/sarg-general",outdirname);
+       if ((fp_in=FileObject_Open(wger))==NULL) {
+               debuga(__FILE__,__LINE__,_("Cannot open file \"%s\": %s\n"),wger,FileObject_GetLastOpenError());
+               exit(EXIT_FAILURE);
+       }
+
+       snprintf(top2,sizeof(top2),"%s/top.tmp",outdirname);
+       if ((fp_top2=fopen(top2,"w"))==NULL) {
+               debuga(__FILE__,__LINE__,_("Cannot open file \"%s\": %s\n"),top2,strerror(errno));
+               exit(EXIT_FAILURE);
+       }
+
+       olduser[0]='\0';
+
+       if ((line=longline_create())==NULL) {
+               debuga(__FILE__,__LINE__,_("Not enough memory to read file \"%s\"\n"),wger);
+               exit(EXIT_FAILURE);
+       }
+
+       while ((warea=longline_read(fp_in,line))!=NULL) {
+               ger_read(warea,&item,wger);
+               if (item.total) continue;
+               if (strcmp(olduser,item.user) != 0) {
+                       Statis.totuser++;
+
+                       if (olduser[0] != '\0') {
+                               /*
+                               This complicated printf is due to Microsoft's inability to comply with any standard. Msvcrt is unable
+                               to print a long long int unless it is exactly 64-bits long.
+                               */
+                               fprintf(fp_top2,"%s\t%"PRIu64"\t%"PRIu64"\t%"PRIu64"\t%"PRIu64"\t%"PRIu64"\n",olduser,(uint64_t)tnbytes,(uint64_t)tnacc,(uint64_t)tnelap,(uint64_t)tnincache,(uint64_t)tnoucache);
+
+                               Statis.ttnbytes+=tnbytes;
+                               Statis.ttnacc+=tnacc;
+                               Statis.ttnelap+=tnelap;
+                               Statis.ttnincache+=tnincache;
+                               Statis.ttnoucache+=tnoucache;
+                       }
+                       safe_strcpy(olduser,item.user,sizeof(olduser));
+                       tnbytes=0;
+                       tnacc=0;
+                       tnelap=0;
+                       tnincache=0;
+                       tnoucache=0;
+               }
+
+               tnbytes+=item.nbytes;
+               tnacc+=item.nacc;
+               tnelap+=item.nelap;
+               tnincache+=item.incache;
+               tnoucache+=item.oucache;
+       }
+       if (FileObject_Close(fp_in)) {
+               debuga(__FILE__,__LINE__,_("Read error in \"%s\": %s\n"),wger,FileObject_GetLastCloseError());
+               exit(EXIT_FAILURE);
+       }
+       longline_destroy(&line);
+
+       if (olduser[0] != '\0') {
+               /*
+               This complicated printf is due to Microsoft's inability to comply with any standard. Msvcrt is unable
+               to print a long long int unless it is exactly 64-bits long.
+               */
+               fprintf(fp_top2,"%s\t%"PRIu64"\t%"PRIu64"\t%"PRIu64"\t%"PRIu64"\t%"PRIu64"\n",olduser,(uint64_t)tnbytes,(uint64_t)tnacc,(uint64_t)tnelap,(uint64_t)tnincache,(uint64_t)tnoucache);
+
+               Statis.ttnbytes+=tnbytes;
+               Statis.ttnacc+=tnacc;
+               Statis.ttnelap+=tnelap;
+               Statis.ttnincache+=tnincache;
+               Statis.ttnoucache+=tnoucache;
+       }
+       if (fclose(fp_top2)==EOF) {
+               debuga(__FILE__,__LINE__,_("Write error in \"%s\": %s\n"),top2,strerror(errno));
+               exit(EXIT_FAILURE);
+       }
+
+#ifdef ENABLE_DOUBLE_CHECK_DATA
+       if (Statis.ttnacc!=globstat.nacc || Statis.ttnbytes!=globstat.nbytes || Statis.ttnelap!=globstat.elap ||
+               Statis.ttnincache!=globstat.incache || Statis.ttnoucache!=globstat.oucache) {
+               debuga(__FILE__,__LINE__,_("Total statistics mismatch when reading \"%s\" to produce the top users\n"),wger);
+               exit(EXIT_FAILURE);
+       }
+#endif
+
+       set_total_users(Statis.totuser);
+
+       if((TopuserSort & TOPUSER_SORT_USER) != 0) {
+               sfield="-k 1,1";
+               SortInfo.sort_field=_("user");
+       } else if((TopuserSort & TOPUSER_SORT_CONNECT) != 0) {
+               sfield="-n -k 3,3";
+               SortInfo.sort_field=_("connect");
+       } else if((TopuserSort & TOPUSER_SORT_TIME) != 0) {
+               sfield="-n -k 4,4";
+               SortInfo.sort_field=pgettext("duration","time");
+       } else {
+               SortInfo.sort_field=_("bytes");
+       }
+
+       if((TopuserSort & TOPUSER_SORT_REVERSE) == 0) {
+               order="";
+               SortInfo.sort_order=_("normal");
+       } else {
+               order="-r";
+               SortInfo.sort_order=_("reverse");
+       }
+
+       snprintf(top1,sizeof(top1),"%s/top",outdirname);
+       if (snprintf(csort,sizeof(csort),"sort -T \"%s\" -t \"\t\" %s %s -o \"%s\" \"%s\"", tmp, order, sfield, top1, top2)>=sizeof(csort)) {
+               debuga(__FILE__,__LINE__,_("Sort command too long when sorting file \"%s\" to \"%s\"\n"),top2,top1);
+               exit(EXIT_FAILURE);
+       }
+       cstatus=system(csort);
+       if (!WIFEXITED(cstatus) || WEXITSTATUS(cstatus)) {
+               debuga(__FILE__,__LINE__,_("sort command return status %d\n"),WEXITSTATUS(cstatus));
+               debuga(__FILE__,__LINE__,_("sort command: %s\n"),csort);
+               exit(EXIT_FAILURE);
+       }
+
+       if (!KeepTempLog && unlink(top2)) {
+               debuga(__FILE__,__LINE__,_("Cannot delete \"%s\": %s\n"),top2,strerror(errno));
+               exit(EXIT_FAILURE);
+       }
 
-       return;
+       TopUser_HtmlReport(top1,&Statis,&SortInfo);
 }