]> git.ipfire.org Git - thirdparty/sarg.git/blobdiff - util.c
Add support to decompress xz files
[thirdparty/sarg.git] / util.c
diff --git a/util.c b/util.c
index 59056ca9fa7158e484bfbc453ba437918f66713b..aba23b191b769db4af96cb708361bda67c3d6bdb 100644 (file)
--- a/util.c
+++ b/util.c
@@ -1,6 +1,6 @@
 /*
  * SARG Squid Analysis Report Generator      http://sarg.sourceforge.net
- *                                                            1998, 2012
+ *                                                            1998, 2015
  *
  * SARG donations:
  *      please look at http://sarg.sourceforge.net/donations.php
 #include "include/conf.h"
 #include "include/defs.h"
 
+#if defined(__MINGW32__) && defined(HAVE_DIRECT_H)
+#define NO_OLDNAMES 1
+#include <direct.h>
+#endif
+
 #if defined(HAVE_BACKTRACE)
 #define USE_GETWORD_BACKTRACE 1
 #else
@@ -41,6 +46,11 @@ static char mtab1[12][4]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep",
 //! The list of the HTTP codes to exclude from the report.
 static char *excludecode=NULL;
 
+//! Directory where the images are stored.
+char ImageDir[MAXLEN]=IMAGEDIR;
+
+extern char *CurrentLocale;
+
 #if USE_GETWORD_BACKTRACE
 static void getword_backtrace(void)
 {
@@ -52,7 +62,7 @@ static void getword_backtrace(void)
        if (n<=0) return;
        calls=backtrace_symbols(buffer,n);
        if (calls) {
-               debuga(_("getword backtrace:\n"));
+               debuga(__FILE__,__LINE__,_("getword backtrace:\n"));
                for (i=0 ; i<n ; i++) {
                        fprintf(stderr,"SARG: %d:%s\n",i+1,calls[i]);
                }
@@ -71,7 +81,7 @@ void getword_start(struct getwordstruct *gwarea, const char *line)
 void getword_restart(struct getwordstruct *gwarea)
 {
        if (gwarea->modified) {
-               debuga(_("Cannot parse again the line as it was modified\n"));
+               debuga(__FILE__,__LINE__,_("Cannot parse again the line as it was modified\n"));
                exit(EXIT_FAILURE);
        }
        gwarea->current=gwarea->beginning;
@@ -83,11 +93,14 @@ int getword(char *word, int limit, struct getwordstruct *gwarea, char stop)
 
        for(x=0;((gwarea->current[x]) && (gwarea->current[x] != stop ));x++) {
                if(x>=limit) {
-                       printf("SARG: getword loop detected after %d bytes.\n",x);
-                       printf("SARG: Line=\"%s\"\n",gwarea->beginning);
-                       printf("SARG: Record=\"%s\"\n",gwarea->current);
-                       printf("SARG: searching for \'x%x\'\n",stop);
-                       //printf("SARG: Maybe you have a broken record or garbage in your access.log file.\n");
+                       /*
+                        TRANSLATORS: The %s is the name of the function reporting the
+                        error message.
+                        */
+                       debuga(__FILE__,__LINE__,_("End of word not found in %s after %d bytes.\n"),__func__,x);
+                       debuga(__FILE__,__LINE__,_("Line=\"%s\"\n"),gwarea->beginning);
+                       debuga(__FILE__,__LINE__,_("Record=\"%s\"\n"),gwarea->current);
+                       debuga(__FILE__,__LINE__,_("searching for \'x%x\'\n"),stop);
                        word[(limit>0) ? limit-1 : 0]='\0';
 #if USE_GETWORD_BACKTRACE
                        getword_backtrace();
@@ -124,11 +137,10 @@ int getword_multisep(char *word, int limit, struct getwordstruct *gwarea, char s
 
        for(x=0;((gwarea->current[x]) && (gwarea->current[x] != stop ));x++) {
                if(x>=limit) {
-                       printf("SARG: getword_multisep loop detected.\n");
-                       printf("SARG: Line=\"%s\"\n",gwarea->beginning);
-                       printf("SARG: Record=\"%s\"\n",gwarea->current);
-                       printf("SARG: searching for \'x%x\'\n",stop);
-                       //printf("SARG: Maybe you have a broken record or garbage in your access.log file.\n");
+                       debuga(__FILE__,__LINE__,_("End of word not found in %s after %d bytes.\n"),__func__,x);
+                       debuga(__FILE__,__LINE__,_("Line=\"%s\"\n"),gwarea->beginning);
+                       debuga(__FILE__,__LINE__,_("Record=\"%s\"\n"),gwarea->current);
+                       debuga(__FILE__,__LINE__,_("searching for \'x%x\'\n"),stop);
                        if (limit>0) word[limit-1]='\0';
 #if USE_GETWORD_BACKTRACE
                        getword_backtrace();
@@ -151,11 +163,10 @@ int getword_skip(int limit, struct getwordstruct *gwarea, char stop)
 
        for(x=0;(gwarea->current[x] && (gwarea->current[x] != stop ));x++) {
                if(x>=limit) {
-                       printf("SARG: getword_skip loop detected after %d bytes.\n",x);
-                       printf("SARG: Line=\"%s\"\n",gwarea->beginning);
-                       printf("SARG: Record=\"%s\"\n",gwarea->current);
-                       printf("SARG: searching for \'x%x\'\n",stop);
-                       //printf("SARG: Maybe you have a broken record or garbage in your access.log file.\n");
+                       debuga(__FILE__,__LINE__,_("End of word not found in %s after %d bytes.\n"),__func__,x);
+                       debuga(__FILE__,__LINE__,_("Line=\"%s\"\n"),gwarea->beginning);
+                       debuga(__FILE__,__LINE__,_("Record=\"%s\"\n"),gwarea->current);
+                       debuga(__FILE__,__LINE__,_("searching for \'x%x\'\n"),stop);
 #if USE_GETWORD_BACKTRACE
                        getword_backtrace();
 #endif
@@ -184,17 +195,23 @@ int getword_atoll(long long int *number, struct getwordstruct *gwarea, char stop
        for(x=0;isdigit(gwarea->current[x]);x++) {
                digit=gwarea->current[x]-'0';
                if (*number >= (LLONG_MAX-digit)/10) {
-                       debuga(_("Integer overflow detected in getword_atoll in line %s\n"),gwarea->beginning);
+                       /*
+                        TRANSLATORS: The first %s is the function name (in the source code) where the
+                        overflow is detected.
+                       */
+                       debuga(__FILE__,__LINE__,_("Integer overflow detected in %s in line %s\n"),__func__,gwarea->beginning);
                        return(-1);
                }
                *number=(*number * 10) + digit;
        }
        if(gwarea->current[x] && gwarea->current[x]!=stop) {
-               printf("SARG: getword_atoll loop detected after %d bytes.\n",x);
-               printf("SARG: Line=\"%s\"\n",gwarea->beginning);
-               printf("SARG: Record=\"%s\"\n",gwarea->current);
-               printf("SARG: searching for \'x%x\'\n",stop);
-               //printf("SARG: Maybe you have a broken record or garbage in your access.log file.\n");
+               /*
+                TRANSLATORS: The %s is the function name, in the source code, where the problem occured.
+               */
+               debuga(__FILE__,__LINE__,_("End of number not found in %s after %d bytes.\n"),__func__,x);
+               debuga(__FILE__,__LINE__,_("Line=\"%s\"\n"),gwarea->beginning);
+               debuga(__FILE__,__LINE__,_("Record=\"%s\"\n"),gwarea->current);
+               debuga(__FILE__,__LINE__,_("searching for \'x%x\'\n"),stop);
 #if USE_GETWORD_BACKTRACE
                getword_backtrace();
 #endif
@@ -223,17 +240,16 @@ int getword_atoi(int *number, struct getwordstruct *gwarea, char stop)
        for(x=0;isdigit(gwarea->current[x]);x++) {
                digit=gwarea->current[x]-'0';
                if (*number > (INT_MAX-digit)/10) {
-                       debuga(_("Integer overflow detected in getword_atoi in line %s\n"),gwarea->beginning);
+                       debuga(__FILE__,__LINE__,_("Integer overflow detected in %s in line %s\n"),__func__,gwarea->beginning);
                        return(-1);
                }
                *number=(*number * 10) + digit;
        }
        if(gwarea->current[x] && gwarea->current[x]!=stop) {
-               printf("SARG: getword_atoi loop detected after %d bytes.\n",x);
-               printf("SARG: Line=\"%s\"\n",gwarea->beginning);
-               printf("SARG: Record=\"%s\"\n",gwarea->current);
-               printf("SARG: searching for \'x%x\'\n",stop);
-               //printf("SARG: Maybe you have a broken record or garbage in your access.log file.\n");
+               debuga(__FILE__,__LINE__,_("End of number not found in %s after %d bytes.\n"),__func__,x);
+               debuga(__FILE__,__LINE__,_("Line=\"%s\"\n"),gwarea->beginning);
+               debuga(__FILE__,__LINE__,_("Record=\"%s\"\n"),gwarea->current);
+               debuga(__FILE__,__LINE__,_("searching for \'x%x\'\n"),stop);
 #if USE_GETWORD_BACKTRACE
                getword_backtrace();
 #endif
@@ -248,7 +264,7 @@ int getword_atoi(int *number, struct getwordstruct *gwarea, char stop)
 
 int getword_atol(long int *number, struct getwordstruct *gwarea, char stop)
 {
-       long int x;
+       int x;
        long int sign=+1;
        int digit;
 
@@ -262,17 +278,16 @@ int getword_atol(long int *number, struct getwordstruct *gwarea, char stop)
        for(x=0;isdigit(gwarea->current[x]);x++) {
                digit=gwarea->current[x]-'0';
                if (*number > (LONG_MAX-digit)/10) {
-                       debuga(_("Integer overflow detected in getword_atol in line %s\n"),gwarea->beginning);
+                       debuga(__FILE__,__LINE__,_("Integer overflow detected in %s in line %s\n"),__func__,gwarea->beginning);
                        return(-1);
                }
                *number=(*number * 10) + digit;
        }
        if(gwarea->current[x] && gwarea->current[x]!=stop) {
-               printf("SARG: getword_atol loop detected after %ld bytes.\n",x);
-               printf("SARG: Line=\"%s\"\n",gwarea->beginning);
-               printf("SARG: Record=\"%s\"\n",gwarea->current);
-               printf("SARG: searching for \'x%x\'\n",stop);
-               //printf("SARG: Maybe you have a broken record or garbage in your access.log file.\n");
+               debuga(__FILE__,__LINE__,_("End of number not found in %s after %d bytes.\n"),__func__,x);
+               debuga(__FILE__,__LINE__,_("Line=\"%s\"\n"),gwarea->beginning);
+               debuga(__FILE__,__LINE__,_("Record=\"%s\"\n"),gwarea->current);
+               debuga(__FILE__,__LINE__,_("searching for \'x%x\'\n"),stop);
 #if USE_GETWORD_BACKTRACE
                getword_backtrace();
 #endif
@@ -287,13 +302,13 @@ int getword_atol(long int *number, struct getwordstruct *gwarea, char stop)
 
 int getword_atolu(unsigned long int *number, struct getwordstruct *gwarea, char stop)
 {
-       unsigned long int x;
+       int x;
        int digit;
 
        if (gwarea->current[0] == '-') {
-               printf("SARG: getword_atolu got a negative number.\n");
-               printf("SARG: Line=\"%s\"\n",gwarea->beginning);
-               printf("SARG: Record=\"%s\"\n",gwarea->current);
+               debuga(__FILE__,__LINE__,_("getword_atolu got a negative number.\n"));
+               debuga(__FILE__,__LINE__,_("Line=\"%s\"\n"),gwarea->beginning);
+               debuga(__FILE__,__LINE__,_("Record=\"%s\"\n"),gwarea->current);
                return(-1);
        }
        if (gwarea->current[0] == '+') {
@@ -303,17 +318,16 @@ int getword_atolu(unsigned long int *number, struct getwordstruct *gwarea, char
        for(x=0;isdigit(gwarea->current[x]);x++) {
                digit=gwarea->current[x]-'0';
                if (*number > (ULONG_MAX-digit)/10) {
-                       debuga(_("Integer overflow detected in getword_atolu in line %s\n"),gwarea->beginning);
+                       debuga(__FILE__,__LINE__,_("Integer overflow detected in %s in line %s\n"),__func__,gwarea->beginning);
                        return(-1);
                }
                *number=(*number * 10) + digit;
        }
        if(gwarea->current[x] && gwarea->current[x]!=stop) {
-               printf("SARG: getword_atolu loop detected after %ld bytes.\n",x);
-               printf("SARG: Line=\"%s\"\n",gwarea->beginning);
-               printf("SARG: Record=\"%s\"\n",gwarea->current);
-               printf("SARG: searching for \'x%x\'\n",stop);
-               //printf("SARG: Maybe you have a broken record or garbage in your access.log file.\n");
+               debuga(__FILE__,__LINE__,_("End of number not found in %s after %d bytes.\n"),__func__,x);
+               debuga(__FILE__,__LINE__,_("Line=\"%s\"\n"),gwarea->beginning);
+               debuga(__FILE__,__LINE__,_("Record=\"%s\"\n"),gwarea->current);
+               debuga(__FILE__,__LINE__,_("searching for \'x%x\'\n"),stop);
 #if USE_GETWORD_BACKTRACE
                getword_backtrace();
 #endif
@@ -339,7 +353,7 @@ int getword_ptr(char *orig_line,char **word, struct getwordstruct *gwarea, char
        int start;
 
        if (orig_line && orig_line!=gwarea->beginning) {
-               debuga(_("Invalid buffer passed to getword_ptr\n"));
+               debuga(__FILE__,__LINE__,_("Invalid buffer passed to getword_ptr\n"));
                return(-1);
        }
 
@@ -380,12 +394,23 @@ long long int my_atoll (const char *nptr)
 int is_absolute(const char *path)
 {
        if (*path=='/') return(1);
-#ifdef WINDOWS
+#ifdef _WIN32
        if (isalpha(path[0]) && path[1]==':') return(1);
 #endif
        return(0);
 }
 
+int PortableMkDir(const char *path,int mode)
+{
+#if defined(__linux__)
+       int mkerror=mkdir(path,mode);
+#else //mingw
+       (void)mode;
+       int mkerror=_mkdir(path);
+#endif
+       return(mkerror);
+}
+
 void my_mkdir(const char *name)
 {
        char w0[MAXLEN];
@@ -393,23 +418,22 @@ void my_mkdir(const char *name)
        int chars;
 
        if(!is_absolute(name)) {
-               debuga(_("Invalid path (%s). Please, use absolute paths only.\n"),name);
-               debuga(_("process aborted.\n"));
+               debuga(__FILE__,__LINE__,_("Invalid path (%s). Please, use absolute paths only.\n"),name);
                exit(EXIT_FAILURE);
        }
 
        chars=0;
        for (i=0 ; name[i] ; i++) {
                if (i>=sizeof(w0)) {
-                       debuga(_("directory name too long: %s\n"),name);
+                       debuga(__FILE__,__LINE__,_("Path too long: "));
+                       debuga_more("%s\n",name);
                        exit(EXIT_FAILURE);
                }
                if (chars>0 && name[i] == '/') {
                        w0[i] = '\0';
                        if (access(w0, R_OK) != 0) {
-                               if (mkdir(w0,0755)) {
-                                       debuga(_("Cannot create directory %s - %s\n"),w0,strerror(errno));
-                                       debuga(_("process aborted.\n"));
+                               if (PortableMkDir(w0,0755)) {
+                                       debuga(__FILE__,__LINE__,_("Cannot create directory \"%s\": %s\n"),w0,strerror(errno));
                                        exit(EXIT_FAILURE);
                                }
                        }
@@ -419,9 +443,8 @@ void my_mkdir(const char *name)
        }
 
        if (access(name, R_OK) != 0) {
-               if (mkdir(name,0755)) {
-                       debuga(_("Cannot create directory %s - %s\n"),name,strerror(errno));
-                       debuga(_("process aborted.\n"));
+               if (PortableMkDir(name,0755)) {
+                       debuga(__FILE__,__LINE__,_("Cannot create directory \"%s\": %s\n"),name,strerror(errno));
                        exit(EXIT_FAILURE);
                }
        }
@@ -437,7 +460,7 @@ void my_lltoa(unsigned long long int n, char *s, int ssize, int len)
 
        ssize--;
        if (len>ssize) {
-               debuga(_("The requested number of digits passed to my_lltoa (%d) is bigger than the output buffer size (%d)\n"),len,ssize);
+               debuga(__FILE__,__LINE__,_("The requested number of digits passed to my_lltoa (%d) is bigger than the output buffer size (%d)\n"),len,ssize);
                abort();
        }
 
@@ -474,13 +497,39 @@ int builddia(int day, int month, int year)
        return(year*10000+month*100+day);
 }
 
+/*!
+Compare two dates.
 
-void buildymd(const char *dia, const char *mes, const char *ano, char *wdata)
+\param date1 The first date to compare.
+\param date2 The second date to compare.
+
+\retval -1 If date1<date2.
+\retval 0 If date1==date2.
+\retval 1 if date1>date2.
+*/
+int compare_date(const struct tm *date1,const struct tm *date2)
+{
+       if (date1->tm_year<date2->tm_year) return(-1);
+       if (date1->tm_year>date2->tm_year) return(1);
+       if (date1->tm_mon<date2->tm_mon) return(-1);
+       if (date1->tm_mon>date2->tm_mon) return(1);
+       if (date1->tm_mday<date2->tm_mday) return(-1);
+       if (date1->tm_mday>date2->tm_mday) return(1);
+       if (date1->tm_hour<date2->tm_hour) return(-1);
+       if (date1->tm_hour>date2->tm_hour) return(1);
+       if (date1->tm_min<date2->tm_min) return(-1);
+       if (date1->tm_min>date2->tm_min) return(1);
+       if (date1->tm_sec<date2->tm_sec) return(-1);
+       if (date1->tm_sec>date2->tm_sec) return(1);
+       return(0);
+}
+
+void buildymd(const char *dia, const char *mes, const char *ano, char *wdata,int wdata_size)
 {
        int nmes;
 
        nmes=month2num(mes);
-       sprintf(wdata,"%04d%02d%02d",atoi(ano),nmes+1,atoi(dia));
+       snprintf(wdata,wdata_size,"%04d%02d%02d",atoi(ano),nmes+1,atoi(dia));
 }
 
 
@@ -504,57 +553,75 @@ const char *conv_month_name(int month)
        return(mtab1[month-1]);
 }
 
+/*!
+Write a debug message to stderr. The message is prefixed by "SARG:" to identify its origin.
 
-void name_month(char *month,int month_len)
+\param msg The printf like message to format.
+\param ... The arguments to format in the message.
+*/
+void debuga(const char *File,int Line,const char *msg,...)
 {
-       int  x, z=atoi(month)-1;
-       char m[255];
-       char w[20];
-       struct getwordstruct gwarea;
-
-       strcpy(m,_("January,February,March,April,May,June,July,August,September,October,November,December"));
-       getword_start(&gwarea,m);
+       va_list ap;
 
-       for(x=0; x<z; x++)
-               if (getword_multisep(w,sizeof(w),&gwarea,',')<0) {
-                       printf("SARG: Maybe you have a broken record or garbage in the names of the months.\n");
-                       exit(EXIT_FAILURE);
-               }
-       if (getword_multisep(month,month_len,&gwarea,',')<0) {
-               printf("SARG: Maybe you have a broken record or garbage in the name of the months.\n");
-               exit(EXIT_FAILURE);
+       if (debugz>=LogLevel_Source) {
+               /* The path is removed because every source file is in the same directory.
+                * There is no point in reporting the full path from the build directory.
+                */
+               const char *ptr=strrchr(File,'/');
+               if (!ptr) ptr=File;
+               /* TRANSLATORS: This is the prefix to stderr messages when the debug level is
+                set to display the source file (%s) and the line number (%d). */
+               fprintf(stderr,_("SARG(%s:%d): "),ptr,Line);
+       } else {
+               /* TRANSLATORS: This is the prefix to stderr messages when the debug level
+                is low. */
+               fputs(_("SARG: "),stderr);
        }
+       va_start(ap,msg);
+       vfprintf(stderr,msg,ap);
+       va_end(ap);
 }
 
-
 /*!
-Write a debug message to stderr. The message is prefixed by "SARG:" to identify its origin.
+Write a debug message to stderr. The message is supposed
+to be displayed after a message from debuga().
 
 \param msg The printf like message to format.
 \param ... The arguments to format in the message.
 */
-void debuga(const char *msg,...)
+void debuga_more(const char *msg,...)
 {
        va_list ap;
 
-       fputs(_("SARG: "),stderr);
        va_start(ap,msg);
        vfprintf(stderr,msg,ap);
        va_end(ap);
 }
 
-
 /*!
 Write a debug message to stderr. The message is prefixed by "SARG: (info)".
 
 \param msg The printf like message to format.
 \param ... The arguments to format in the message.
 */
-void debugaz(const char *msg,...)
+void debugaz(const char *File,int Line,const char *msg,...)
 {
        va_list ap;
 
-       fputs(_("SARG: (info) "),stderr);
+       if (debugz>=LogLevel_Source) {
+               /* The path is removed because every source file is in the same directory.
+                * There is no point in reporting the full path from the build directory.
+                */
+               const char *ptr=strrchr(File,'/');
+               if (!ptr) ptr=File;
+               /* TRANSLATORS: This is the prefix to information messages when the debug level is
+                set to display the source file (%s) and the line number (%d). */
+               fprintf(stderr,_("SARG(%s:%d): (info) "),ptr,Line);
+       } else {
+               /* TRANSLATORS: This is the prefix to information messages when the debug level
+                is low. */
+               fputs(_("SARG: (info) "),stderr);
+       }
        va_start(ap,msg);
        vfprintf(stderr,msg,ap);
        va_end(ap);
@@ -571,61 +638,65 @@ char *fixnum(long long int value, int n)
        char *pret;
        register int i, j, k;
        int numlen;
-       static char abbrev[30];
+       static char abbrev[30]="";
 
        my_lltoa(value, num, sizeof(num), 0);
 
        if(DisplayedValues==DISPLAY_ABBREV) {
                numlen = strlen(num);
                if(numlen <= 3)
-                       sprintf(abbrev,"%s",num);
-               if(numlen == 4 || numlen == 7 || numlen == 10 || numlen == 13) {
-                       snprintf(abbrev,2,"%s",num);
-                       strncat(abbrev,".",1);
-                       strncat(abbrev,num+1,2);
-                       if(!n) return(abbrev);
-                       if(numlen == 4)
-                               strncat(abbrev,"K",1);
-                       else if(numlen == 7)
-                               strncat(abbrev,"M",1);
-                       else if(numlen == 10)
-                               strncat(abbrev,"G",1);
-                       else if(numlen == 13)
-                               strncat(abbrev,"T",1);
-               }
-               if(numlen == 5 || numlen == 8 || numlen == 11 || numlen == 14) {
-                       snprintf(abbrev,3,"%s",num);
-                       strncat(abbrev,".",1);
-                       strncat(abbrev,num+2,2);
-                       if(!n) return(abbrev);
-                       if(numlen == 5)
-                               strncat(abbrev,"K",1);
-                       else if(numlen == 8)
-                               strncat(abbrev,"M",1);
-                       else if(numlen == 11)
-                               strncat(abbrev,"G",1);
-                       else if(numlen == 14)
-                               strncat(abbrev,"T",1);
-               }
-               if(numlen == 6 || numlen == 9 || numlen == 12 || numlen == 15) {
-                       snprintf(abbrev,4,"%s",num);
-                       strncat(abbrev,".",1);
-                       strncat(abbrev,num+3,2);
-                       if(!n) return(abbrev);
-                       if(numlen == 6)
-                               strncat(abbrev,"K",1);
-                       else if(numlen == 9)
-                               strncat(abbrev,"M",1);
-                       else if(numlen == 12)
-                               strncat(abbrev,"G",1);
-                       else if(numlen == 15)
-                               strncat(abbrev,"T",1);
+                       strcpy(abbrev,num);
+               else if (numlen%3 == 1) {
+                       abbrev[0]=num[0];
+                       abbrev[1]=(UseComma) ? ',' : '.';
+                       abbrev[2]=num[1];
+                       abbrev[3]=num[2];
+                       abbrev[4]='\0';
+               }
+               else if (numlen%3 == 2) {
+                       abbrev[0]=num[0];
+                       abbrev[1]=num[1];
+                       abbrev[2]=(UseComma) ? ',' : '.';
+                       abbrev[3]=num[2];
+                       abbrev[4]=num[3];
+                       abbrev[5]='\0';
+               }
+               else if (numlen%3 == 0) {
+                       abbrev[0]=num[0];
+                       abbrev[1]=num[1];
+                       abbrev[2]=num[2];
+                       abbrev[3]=(UseComma) ? ',' : '.';
+                       abbrev[4]=num[3];
+                       abbrev[5]=num[4];
+                       abbrev[6]='\0';
+               }
+               if (n) {
+                       if (numlen <= 3) {
+                               //no prefix
+                       }
+                       else if (numlen <= 6)
+                               strcat(abbrev,"K");
+                       else if (numlen <= 9)
+                               strcat(abbrev,"M");
+                       else if (numlen <= 12)
+                               strcat(abbrev,"G");
+                       else if (numlen <= 15)
+                               strcat(abbrev,"T");
+                       else if (numlen >= 18)
+                               strcat(abbrev,"P");
+                       else if (numlen <= 21)
+                               strcat(abbrev,"E");
+                       else if (numlen <= 24)
+                               strcat(abbrev,"Z");
+                       else if (numlen <= 27)
+                               strcat(abbrev,"Y");
+                       else
+                               strcat(abbrev,"???");
                }
-
                return(abbrev);
        }
 
-       bzero(buf, MAXLEN_FIXNUM*2);
+       memset(buf,0,MAXLEN_FIXNUM*2);
 
        pbuf = buf;
        pret = ret;
@@ -665,7 +736,7 @@ char *fixnum2(long long int value, int n)
        register int i, j, k;
 
        my_lltoa(value, num, sizeof(num), 0);
-       bzero(buf, MAXLEN_FIXNUM2*2);
+       memset(buf,0,MAXLEN_FIXNUM2*2);
 
        pbuf = buf;
        pret = ret;
@@ -696,18 +767,16 @@ char *fixnum2(long long int value, int n)
 
 char *buildtime(long long int elap)
 {
-       int num = elap / 1000;
+       long int num = elap / 1000LL;
        int hor = 0;
        int min = 0;
        int sec = 0;
-       static char buf[12];
-
-       buf[0]='\0';
+       static char buf[20];
 
-       hor=num / 3600;
-       min=(num % 3600) / 60;
-       sec=num % 60;
-       sprintf(buf,"%02d:%02d:%02d",hor,min,sec);
+       hor=num / 3600L;
+       min=(num % 3600L) / 60L;
+       sec=num % 60L;
+       snprintf(buf,sizeof(buf),"%02d:%02d:%02d",hor,min,sec);
 
        return(buf);
 }
@@ -729,9 +798,17 @@ int obtdate(const char *dirname, const char *name, char *data)
        FILE *fp_in;
        char wdir[MAXLEN];
 
-       sprintf(wdir,"%s%s/sarg-date",dirname,name);
+       if (snprintf(wdir,sizeof(wdir),"%s%s/sarg-date",dirname,name)>=sizeof(wdir)) {
+               debuga(__FILE__,__LINE__,_("Buffer too small to store "));
+               debuga_more("%s%s/sarg-date",dirname,name);
+               exit(EXIT_FAILURE);
+       }
        if ((fp_in = fopen(wdir, "rt")) == 0) {
-               sprintf(wdir,"%s%s/date",dirname,name);
+               if (snprintf(wdir,sizeof(wdir),"%s%s/date",dirname,name)>=sizeof(wdir)) {
+                       debuga(__FILE__,__LINE__,_("Buffer too small to store "));
+                       debuga_more("%s%s/date",dirname,name);
+                       exit(EXIT_FAILURE);
+               }
                if ((fp_in = fopen(wdir, "rt")) == 0) {
                        data[0]='\0';
                        return(-1);
@@ -739,10 +816,13 @@ int obtdate(const char *dirname, const char *name, char *data)
        }
 
        if (!fgets(data,80,fp_in)) {
-               debuga(_("Failed to read the date in %s\n"),wdir);
+               debuga(__FILE__,__LINE__,_("Failed to read the date in file \"%s\"\n"),wdir);
+               exit(EXIT_FAILURE);
+       }
+       if (fclose(fp_in)==EOF) {
+               debuga(__FILE__,__LINE__,_("Read error in \"%s\": %s\n"),wdir,strerror(errno));
                exit(EXIT_FAILURE);
        }
-       fclose(fp_in);
        fixendofline(data);
 
        return(0);
@@ -786,19 +866,30 @@ int obtuser(const char *dirname, const char *name)
        char tuser[20];
        int nuser;
 
-       sprintf(wdir,"%s%s/sarg-users",dirname,name);
+       if (snprintf(wdir,sizeof(wdir),"%s%s/sarg-users",dirname,name)>=sizeof(wdir)) {
+               debuga(__FILE__,__LINE__,_("Buffer too small to store "));
+               debuga_more("%s%s/sarg-users",dirname,name);
+               exit(EXIT_FAILURE);
+       }
        if((fp_in=fopen(wdir,"r"))==NULL) {
-               sprintf(wdir,"%s%s/users",dirname,name);
+               if (snprintf(wdir,sizeof(wdir),"%s%s/users",dirname,name)>=sizeof(wdir)) {
+                       debuga(__FILE__,__LINE__,_("Buffer too small to store "));
+                       debuga_more("%s%s/users",dirname,name);
+                       exit(EXIT_FAILURE);
+               }
                if((fp_in=fopen(wdir,"r"))==NULL) {
                        return(0);
                }
        }
 
        if (!fgets(tuser,sizeof(tuser),fp_in)) {
-               debuga(_("Failed to read the number of users in %s\n"),wdir);
+               debuga(__FILE__,__LINE__,_("Failed to read the number of users in file \"%s\"\n"),wdir);
+               exit(EXIT_FAILURE);
+       }
+       if (fclose(fp_in)==EOF) {
+               debuga(__FILE__,__LINE__,_("Read error in \"%s\": %s\n"),wdir,strerror(errno));
                exit(EXIT_FAILURE);
        }
-       fclose(fp_in);
        nuser=atoi(tuser);
 
        return(nuser);
@@ -807,7 +898,7 @@ int obtuser(const char *dirname, const char *name)
 
 void obttotal(const char *dirname, const char *name, int nuser, long long int *tbytes, long long int *media)
 {
-       FILE *fp_in;
+       FileObject *fp_in;
        char *buf;
        char wdir[MAXLEN];
        char user[MAX_USER_LEN];
@@ -818,16 +909,24 @@ void obttotal(const char *dirname, const char *name, int nuser, long long int *t
        *tbytes=0;
        *media=0;
 
-       sprintf(wdir,"%s%s/sarg-general",dirname,name);
-       if ((fp_in = fopen(wdir, "r")) == 0) {
-               sprintf(wdir,"%s%s/general",dirname,name);
-               if ((fp_in = fopen(wdir, "r")) == 0) {
+       if (snprintf(wdir,sizeof(wdir),"%s%s/sarg-general",dirname,name)>=sizeof(wdir)) {
+               debuga(__FILE__,__LINE__,_("Buffer too small to store "));
+               debuga_more("%s%s/sarg-general",dirname,name);
+               exit(EXIT_FAILURE);
+       }
+       if ((fp_in = FileObject_Open(wdir)) == NULL) {
+               if (snprintf(wdir,sizeof(wdir),"%s%s/general",dirname,name)>=sizeof(wdir)) {
+                       debuga(__FILE__,__LINE__,_("Buffer too small to store "));
+                       debuga_more("%s%s/general",dirname,name);
+                       exit(EXIT_FAILURE);
+               }
+               if ((fp_in = FileObject_Open(wdir)) == NULL) {
                        return;
                }
        }
 
        if ((line=longline_create())==NULL) {
-               debuga(_("Not enough memory to read the file %s\n"),wdir);
+               debuga(__FILE__,__LINE__,_("Not enough memory to read file \"%s\"\n"),wdir);
                exit(EXIT_FAILURE);
        }
 
@@ -840,22 +939,25 @@ void obttotal(const char *dirname, const char *name, int nuser, long long int *t
                        continue;
                getword_start(&gwarea,buf);
                if (getword(user,sizeof(user),&gwarea,sep)<0) {
-                       debuga(_("There is a invalid user in file %s\n"),wdir);
+                       debuga(__FILE__,__LINE__,_("Invalid user in file \"%s\"\n"),wdir);
                        exit(EXIT_FAILURE);
                }
                if(strcmp(user,"TOTAL") != 0)
                        continue;
                if (getword_skip(MAXLEN,&gwarea,sep)<0) {
-                       debuga(_("There a broken total number of access in file %s\n"),wdir);
+                       debuga(__FILE__,__LINE__,_("Invalid total number of accesses in file \"%s\"\n"),wdir);
                        exit(EXIT_FAILURE);
                }
                if (getword_atoll(tbytes,&gwarea,sep)<0) {
-                       debuga(_("There is a broken number of bytes in file %s\n"),wdir);
+                       debuga(__FILE__,__LINE__,_("Invalid number of bytes in file \"%s\"\n"),wdir);
                        exit(EXIT_FAILURE);
                }
                break;
        }
-       fclose(fp_in);
+       if (FileObject_Close(fp_in)) {
+               debuga(__FILE__,__LINE__,_("Read error in \"%s\": %s\n"),wdir,FileObject_GetLastCloseError());
+               exit(EXIT_FAILURE);
+       }
        longline_destroy(&line);
 
        if (nuser <= 0)
@@ -886,7 +988,7 @@ int getperiod_fromsarglog(const char *arqtt,struct periodstruct *period)
                str+=2;
                year0=0;
                for (i=0 ; isdigit(str[i]) && i<4 ; i++) year0=year0*10+(str[i]-'0');
-               if (i!=4) continue;
+               if (i!=4 || year0<1900) continue;
                str+=4;
                if (str[0]!='_') continue;
                str++;
@@ -910,7 +1012,7 @@ int getperiod_fromsarglog(const char *arqtt,struct periodstruct *period)
                str+=2;
                year1=0;
                for (i=0 ; isdigit(str[i]) && i<4 ; i++) year1=year1*10+(str[i]-'0');
-               if (i!=4) continue;
+               if (i!=4 || year1<1900) continue;
                str+=4;
 
                if (str[0]!='_') continue;
@@ -938,6 +1040,13 @@ int getperiod_fromsarglog(const char *arqtt,struct periodstruct *period)
        return(-1);
 }
 
+/*!
+Fill the period with the specified range.
+
+\param period The period to change.
+\param dfrom The start date in the form year*10000+month*100+day.
+\param duntil The end date in the form year*10000+month*100+day.
+*/
 void getperiod_fromrange(struct periodstruct *period,int dfrom,int duntil)
 {
        memset(&period->start,0,sizeof(period->start));
@@ -951,17 +1060,49 @@ void getperiod_fromrange(struct periodstruct *period,int dfrom,int duntil)
        period->end.tm_year=(duntil/10000)-1900;
 }
 
+/*!
+Get the range from a period.
+
+\param period The period to convert to a range.
+\param dfrom The variable to store the range beginning. It can be NULL.
+\param duntil The variable to store the range end. It can be NULL.
+*/
+void getperiod_torange(const struct periodstruct *period,int *dfrom,int *duntil)
+{
+       if (dfrom)
+               *dfrom=(period->start.tm_year+1900)*10000+(period->start.tm_mon+1)*100+period->start.tm_mday;
+       if (duntil)
+               *duntil=(period->end.tm_year+1900)*10000+(period->end.tm_mon+1)*100+period->end.tm_mday;
+}
+
+/*!
+Update the \a main period to encompass the period in \a candidate.
+*/
+void getperiod_merge(struct periodstruct *main,struct periodstruct *candidate)
+{
+       int cdate;
+       int mdate;
+
+       mdate=(main->start.tm_year)*10000+(main->start.tm_mon)*100+main->start.tm_mday;
+       cdate=(candidate->start.tm_year)*10000+(candidate->start.tm_mon)*100+candidate->start.tm_mday;
+       if (mdate==0 || cdate<mdate) memcpy(&main->start,&candidate->start,sizeof(struct tm));
+
+       mdate=(main->end.tm_year)*10000+(main->end.tm_mon)*100+main->end.tm_mday;
+       cdate=(candidate->end.tm_year)*10000+(candidate->end.tm_mon)*100+candidate->end.tm_mday;
+       if (cdate>mdate) memcpy(&main->end,&candidate->end,sizeof(struct tm));
+}
+
 int getperiod_buildtext(struct periodstruct *period)
 {
        int i;
        int range;
        char text1[40], text2[40];
 
-       if(df[0]=='u') {
+       if (df=='u') {
                i=strftime(text1, sizeof(text1), "%Y %b %d", &period->start);
-       }else if(df[0]=='e') {
+       } else if(df=='e') {
                i=strftime(text1, sizeof(text1), "%d %b %Y", &period->start);
-       } else /*if(df[0]=='w')*/ {
+       } else /*if (df=='w')*/ {
                IndexTree=INDEX_TREE_FILE;
                i=strftime(text1, sizeof(text1), "%Y.%U", &period->start);
        }
@@ -971,9 +1112,9 @@ int getperiod_buildtext(struct periodstruct *period)
               period->start.tm_mon!=period->end.tm_mon ||
               period->start.tm_mday!=period->end.tm_mday);
        if (range) {
-               if(df[0]=='u') {
+               if (df=='u') {
                        i=strftime(text2, sizeof(text2)-i, "%Y %b %d", &period->end);
-               } else if(df[0]=='e') {
+               } else if (df=='e') {
                        i=strftime(text2, sizeof(text2)-i, "%d %b %Y", &period->end);
                } else {
                        i=strftime(text2, sizeof(text2)-i, "%Y.%U", &period->end);
@@ -995,7 +1136,6 @@ static void copy_images(void)
 {
        FILE *img_in, *img_ou;
        char images[512];
-       char imgdir[MAXLEN];
        char srcfile[MAXLEN];
        char dstfile[MAXLEN];
        DIR *dirp;
@@ -1005,48 +1145,61 @@ static void copy_images(void)
        struct stat info;
 
        if (snprintf(images,sizeof(images),"%simages",outdir)>=sizeof(images)) {
-               debuga(_("Cannot copy images to target directory %simages\n"),outdir);
+               debuga(__FILE__,__LINE__,_("Cannot copy images to target directory %simages\n"),outdir);
                exit(EXIT_FAILURE);
        }
        if (access(images,R_OK)!=0) {
-               if (mkdir(images,0755)) {
-                       debuga(_("Cannot create directory %s - %s\n"),images,strerror(errno));
+               if (PortableMkDir(images,0755)) {
+                       debuga(__FILE__,__LINE__,_("Cannot create directory \"%s\": %s\n"),images,strerror(errno));
                        exit(EXIT_FAILURE);
                }
        }
 
-       strcpy(imgdir,IMAGEDIR);
-       dirp = opendir(imgdir);
+       dirp = opendir(ImageDir);
        if(dirp==NULL) {
-               debuga(_("(util) Can't open directory %s: %s\n"),imgdir,strerror(errno));
+               debuga(__FILE__,__LINE__,_("Cannot open directory \"%s\": %s\n"),ImageDir,strerror(errno));
                return;
        }
        while ((direntp = readdir( dirp )) != NULL ){
                if(direntp->d_name[0]=='.')
                        continue;
-               sprintf(srcfile,"%s/%s",imgdir,direntp->d_name);
+               if (snprintf(srcfile,sizeof(srcfile),"%s/%s",ImageDir,direntp->d_name)>=sizeof(srcfile)) {
+                       debuga(__FILE__,__LINE__,_("Buffer too small to store "));
+                       debuga_more("%s/%s",ImageDir,direntp->d_name);
+                       exit(EXIT_FAILURE);
+               }
                if (stat(srcfile,&info)) {
-                       debuga(_("Cannot stat \"%s\" - %s\n"),srcfile,strerror(errno));
+                       debuga(__FILE__,__LINE__,_("Cannot stat \"%s\": %s\n"),srcfile,strerror(errno));
                        continue;
                }
                if (S_ISREG(info.st_mode)) {
-                       sprintf(dstfile,"%s/%s",images,direntp->d_name);
+                       if (snprintf(dstfile,sizeof(dstfile),"%s/%s",images,direntp->d_name)>=sizeof(dstfile)) {
+                               debuga(__FILE__,__LINE__,_("Buffer too small to store "));
+                               debuga_more("%s/%s",images,direntp->d_name);
+                               exit(EXIT_FAILURE);
+                       }
                        img_in = fopen(srcfile, "rb");
                        if(img_in!=NULL) {
                                img_ou = fopen(dstfile, "wb");
                                if(img_ou!=NULL) {
                                        while ((nread = fread(buffer,1,sizeof(buffer),img_in))>0) {
                                                if (fwrite(buffer,1,nread,img_ou)!=nread) {
-                                                       debuga(_("Failed to copy image %s to %s\n"),srcfile,dstfile);
+                                                       debuga(__FILE__,__LINE__,_("Failed to copy image %s to %s\n"),srcfile,dstfile);
                                                        break;
                                                }
                                        }
-                                       fclose(img_ou);
+                                       if (fclose(img_ou)==EOF) {
+                                               debuga(__FILE__,__LINE__,_("Write error in \"%s\": %s\n"),dstfile,strerror(errno));
+                                               exit(EXIT_FAILURE);
+                                       }
                                } else
-                                       fprintf(stderr,"SARG: (util): %s %s: %s\n", _("Cannot open file")?_("Cannot open file"):"Can't open/create file", dstfile, strerror(errno));
-                               fclose(img_in);
+                                       debuga(__FILE__,__LINE__,_("Cannot open file \"%s\": %s\n"), dstfile, strerror(errno));
+                               if (fclose(img_in)==EOF) {
+                                       debuga(__FILE__,__LINE__,_("Read error in \"%s\": %s\n"),srcfile,strerror(errno));
+                                       exit(EXIT_FAILURE);
+                               }
                        } else
-                               fprintf(stderr,"SARG: (util): %s %s: %s\n", _("Cannot open file")?_("Cannot open file"):"Can't open file", srcfile, strerror(errno));
+                               debuga(__FILE__,__LINE__,_("Cannot open file \"%s\": %s\n"), srcfile, strerror(errno));
                }
        }
        (void) closedir(dirp);
@@ -1054,12 +1207,186 @@ static void copy_images(void)
        return;
 }
 
-int vrfydir(const struct periodstruct *per1, const char *addr, const char *site, const char *us, const char *form)
+/*!
+ * Check if the proposed file name conforms to the directory structure layed out
+ * as a file tree. It is used to check if the file name enumerated while scanning
+ * a directory content may have been created by sarg running with IndexTree set to
+ * INDEX_TREE_FILE.
+ */
+bool IsTreeFileDirName(const char *Name)
+{
+       char DateFormat;
+       int i;
+
+       // start year (date format u) or start day (date format e)
+       if (!isdigit(Name[0]) || !isdigit(Name[1])) return(false);
+
+       if (isdigit(Name[2]) && isdigit(Name[3]))
+       {
+               // date format is either u or w
+               if (Name[4]=='.')
+               {
+                       // date format is w
+                       if (!isdigit(Name[5]) || !isdigit(Name[6])) return(false);
+                       return(true);//date format w is confirmed
+               }
+
+               // date format is u
+               Name+=4;
+
+               // start month
+               if (!isalpha(Name[0]) || !isalpha(Name[1]) || !isalpha(Name[2])) return(false);
+               for (i=11 ; i>=0 && memcmp(mtab1[i],Name,3) ; i--);
+               if (i<0) return(false);
+               Name+=3;
+
+               // start day
+               if (!isdigit(Name[0]) || !isdigit(Name[1])) return(false);
+               Name+=2;
+
+               DateFormat='u';
+       }
+       else if (isalpha(Name[2]) && isalpha(Name[3]) && isalpha(Name[4]))
+       {
+               // date format is e
+               Name+=2;
+
+               // start month
+               if (!isalpha(Name[0]) || !isalpha(Name[1]) || !isalpha(Name[2])) return(false);
+               for (i=11 ; i>=0 && memcmp(mtab1[i],Name,3) ; i--);
+               if (i<0) return(false);
+               Name+=3;
+
+               // start day
+               if (!isdigit(Name[0]) || !isdigit(Name[1]) || !isdigit(Name[2]) || !isdigit(Name[3])) return(false);
+               Name+=4;
+
+               DateFormat='e';
+       }
+       else
+               return(false);
+
+       if (Name[0]!='-') return(false);
+       Name++;
+
+       if (DateFormat=='u')
+       {
+               if (!isdigit(Name[0]) || !isdigit(Name[1]) || !isdigit(Name[2]) || !isdigit(Name[3])) return(false);
+               Name+=4;
+
+               if (!isalpha(Name[0]) || !isalpha(Name[1]) || !isalpha(Name[2])) return(false);
+               for (i=11 ; i>=0 && memcmp(mtab1[i],Name,3) ; i--);
+               if (i<0) return(false);
+               Name+=3;
+
+               if (!isdigit(Name[0]) || !isdigit(Name[1])) return(false);
+               Name+=2;
+       }
+       else //DateFormat=='e'
+       {
+               if (!isdigit(Name[0]) || !isdigit(Name[1])) return(false);
+               Name+=2;
+
+               if (!isalpha(Name[0]) || !isalpha(Name[1]) || !isalpha(Name[2])) return(false);
+               for (i=11 ; i>=0 && memcmp(mtab1[i],Name,3) ; i--);
+               if (i<0) return(false);
+               Name+=3;
+
+               if (!isdigit(Name[0]) || !isdigit(Name[1]) || !isdigit(Name[2]) || !isdigit(Name[3])) return(false);
+               Name+=4;
+       }
+       /*
+        * The directory name may contains additional characters such as a counter if
+        * a previous report is never overwritten.
+        */
+       return(true);
+}
+
+/*!
+ * Check if the proposed file name can be the year part of a report tree build with
+ * IndexTree set to INDEX_TREE_DATE.
+ */
+bool IsTreeYearFileName(const char *Name)
+{
+       if (!isdigit(Name[0]) || !isdigit(Name[1]) || !isdigit(Name[2]) || !isdigit(Name[3])) return(false);
+       Name+=4;
+       if (Name[0]=='-')
+       {
+               Name++;
+               if (!isdigit(Name[0]) || !isdigit(Name[1]) || !isdigit(Name[2]) || !isdigit(Name[3])) return(false);
+               Name+=4;
+       }
+       if (Name[0]) return(false);
+       return(true);
+}
+
+/*!
+ * Check if the proposed file name can be the month part of a report tree build with
+ * IndexTree set to INDEX_TREE_DATE.
+ */
+bool IsTreeMonthFileName(const char *Name)
+{
+       int m;
+
+       if (!isdigit(Name[0]) || !isdigit(Name[1])) return(false);
+       m=(Name[0]-'0')*10+(Name[1]-'0');
+       if (m<1 || m>12) return(false);
+       Name+=2;
+       if (Name[0]=='-')
+       {
+               Name++;
+               if (!isdigit(Name[0]) || !isdigit(Name[1])) return(false);
+               m=(Name[0]-'0')*10+(Name[1]-'0');
+               if (m<1 || m>12) return(false);
+               Name+=2;
+       }
+       if (Name[0]) return(false);
+       return(true);
+}
+
+/*!
+ * Check if the proposed file name can be the day part of a report tree build with
+ * IndexTree set to INDEX_TREE_DATE.
+ */
+bool IsTreeDayFileName(const char *Name)
+{
+       int d;
+
+       if (!isdigit(Name[0]) || !isdigit(Name[1])) return(false);
+       d=(Name[0]-'0')*10+(Name[1]-'0');
+       if (d<1 || d>31) return(false);
+       if (Name[2]=='-')
+       {
+               Name+=3;
+               if (!isdigit(Name[0]) || !isdigit(Name[1])) return(false);
+               d=(Name[0]-'0')*10+(Name[1]-'0');
+               if (d<1 || d>31) return(false);
+       }
+       /*
+        * The directory name may contains additional characters such as a counter if
+        * a previous report is never overwritten.
+        */
+       return(true);
+}
+
+/*!
+ * Create a directory to generate a report for the specified connection data
+ * and populate it with the a <tt>sarg-date</tt> file containing the current
+ * date.
+ *
+ * The function also create an <tt>images</tt> directory in \a dir and copy all
+ * the files from the <tt>SYSCONFDIR/images</tt> into that directory.
+ *
+ * \param per1 The date range in the form: YYYYMMMDD-YYYYMMMDD or DDMMMYYYY-DDMMMYYYY depending on the value of
+ * ::DateFormat.
+ * \param addr The ip address or host name to which the report is limited. If the string is empty, all the addresses are accepted.
+ * \param site The destination site to which the report is limited. If the string is empty, all the sites are accepted.
+ * \param us The user to whom the report is limited. It is an empty string if all the users are accepted.
+ */
+int vrfydir(const struct periodstruct *per1, const char *addr, const char *site, const char *us)
 {
        FILE *fp_ou;
-       int  num=1, count=0;
        char wdir[MAXLEN];
-       char dirname2[MAXLEN];
        int y1, y2;
        int m1, m2;
        int d1, d2;
@@ -1089,13 +1416,13 @@ int vrfydir(const struct periodstruct *per1, const char *addr, const char *site,
                wlen+=sprintf(wdir+wlen,"/%02d",d1);
                if(d1!=d2) wlen+=sprintf(wdir+wlen,"-%02d",d2);
        } else {
-               if(df[0] == 'u') {
+               if (df == 'u') {
                        wlen=snprintf(wdir+wlen,sizeof(wdir)-wlen,"%04d%s%02d-%04d%s%02d",y1,
                                conv_month_name(m1),d1,y2,conv_month_name(m2),d2);
-               } else if(df[0] == 'e') {
+               } else if (df == 'e') {
                        wlen=snprintf(wdir+wlen,sizeof(wdir)-wlen,"%02d%s%04d-%02d%s%04d",d1,
                                conv_month_name(m1),y1,d2,conv_month_name(m2),y2);
-               } else if(df[0] == 'w') {
+               } else if (df == 'w') {
                        wlen2=strftime(wdir+wlen, sizeof(wdir)-wlen, "%Y.%U", &per1->start);
                        if (wlen2==0) return(-1);
                        wlen+=wlen2;
@@ -1120,61 +1447,35 @@ int vrfydir(const struct periodstruct *per1, const char *addr, const char *site,
 
        strcpy(outdirname,wdir);
 
-       if(IndexTree != INDEX_TREE_DATE) {
-               if(!OverwriteReport) {
-                       while(num) {
-                               if(access(wdir,R_OK) == 0) {
-                                       sprintf(wdir,"%s.%d",outdirname,num);
-                                       num++;
-                                       count++;
-                               } else
-                                       break;
-                       }
+       // manufacture a new unique name if configured to keep old reports or overwrite old report if configured to do so
+       if (!OverwriteReport) {
+               int num=1;
 
-                       if(count > 0) {
-                               if(debug)
-                                       debuga(_("File %s already exists, moved to %s\n"),outdirname,wdir);
-                               rename(outdirname,wdir);
-                       }
-               } else {
-                       if(access(outdirname,R_OK) == 0) {
-                               unlinkdir(outdirname,1);
-                       }
+               while (access(wdir,R_OK)==0 || errno==EACCES) //file exist or can't be read
+               {
+                       sprintf(wdir,"%s.%d",outdirname,num);
+                       num++;
+               }
+               if (num>1) {
+                       if(debug)
+                               debuga(__FILE__,__LINE__,_("File %s already exists, moved to %s\n"),outdirname,wdir);
+                       rename(outdirname,wdir);
                }
-               my_mkdir(outdirname);
        } else {
-               strcpy(dirname2,wdir);
-               if(!OverwriteReport) {
-                       while(num) {
-                               if(access(wdir,R_OK) == 0) {
-                                       sprintf(wdir,"%s.%d",dirname2,num);
-                                       num++;
-                                       count++;
-                               } else
-                                       break;
-                       }
-
-                       if(count > 0) {
-                               if(debug)
-                                       debuga(_("File %s already exists, moved to %s\n"),dirname2,wdir);
-                               rename(dirname2,wdir);
-                               strcpy(dirname2,wdir);
-                       }
-               } else {
-                       if(access(wdir,R_OK) == 0) {
-                               unlinkdir(wdir,1);
-                       }
+               if(access(outdirname,R_OK) == 0) {
+                       unlinkdir(outdirname,1);
                }
-
-               if(access(wdir, R_OK) != 0)
-                       my_mkdir(wdir);
        }
+       my_mkdir(outdirname);
 
-       strcpy(dirname2,wdir);
-
-       sprintf(wdir,"%s/sarg-date",outdirname);
+       // create sarg-date to keep track of the report creation date
+       if (snprintf(wdir,sizeof(wdir),"%s/sarg-date",outdirname)>=sizeof(wdir)) {
+               debuga(__FILE__,__LINE__,_("Buffer too small to store "));
+               debuga_more("%s/sarg-date",outdirname);
+               exit(EXIT_FAILURE);
+       }
        if ((fp_ou = fopen(wdir, "wt")) == 0) {
-               debuga(_("cannot open %s for writing\n"),wdir);
+               debuga(__FILE__,__LINE__,_("Cannot open file \"%s\": %s\n"),wdir,strerror(errno));
                perror("SARG:");
                exit(EXIT_FAILURE);
        }
@@ -1183,13 +1484,12 @@ int vrfydir(const struct periodstruct *per1, const char *addr, const char *site,
        loctm=localtime(&curtime);
        strftime(wdir,sizeof(wdir),"%Y-%m-%d %H:%M:%S",loctm);
        if (fprintf(fp_ou,"%s %d\n",wdir,loctm->tm_isdst)<0) {
-               debuga(_("Failed to write the date in %s\n"),wdir);
+               debuga(__FILE__,__LINE__,_("Failed to write the date in %s\n"),wdir);
                perror("SARG:");
                exit(EXIT_FAILURE);
        }
        if (fclose(fp_ou)==EOF) {
-               debuga(_("Failed to write the date in %s\n"),wdir);
-               perror("SARG:");
+               debuga(__FILE__,__LINE__,_("Write error in \"%s\": %s\n"),wdir,strerror(errno));
                exit(EXIT_FAILURE);
        }
 
@@ -1200,7 +1500,7 @@ int vrfydir(const struct periodstruct *per1, const char *addr, const char *site,
 /*!
   Copy a string without overflowing the buffer. The copied string
   is properly terminated by an ASCII zero.
-  
+
   \param dest The destination buffer.
   \param src The source buffer.
   \param length The size of the destination buffer. The program is aborted
@@ -1209,7 +1509,7 @@ int vrfydir(const struct periodstruct *per1, const char *addr, const char *site,
 void safe_strcpy(char *dest,const char *src,int length)
 {
        if (length<=0) {
-               debuga(_("Invalid buffer length passed to the function to safely copy a string\n"));
+               debuga(__FILE__,__LINE__,_("Invalid buffer length passed to the function to safely copy a string\n"));
                exit(EXIT_FAILURE);
        }
        strncpy(dest,src,length-1);
@@ -1237,18 +1537,18 @@ void strip_latin(char *line)
        return;
 }
 
-void zdate(char *ftime,int ftimesize, const char *DateFormat)
+void zdate(char *ftime,int ftimesize, char DateFormat)
 {
        time_t t;
        struct tm *local;
 
        t = time(NULL);
        local = localtime(&t);
-       if(strcmp(DateFormat,"u") == 0)
+       if (DateFormat=='u')
                strftime(ftime, ftimesize, "%b/%d/%Y %H:%M", local);
-       if(strcmp(DateFormat,"e") == 0)
+       else if (DateFormat=='e')
                strftime(ftime, ftimesize, "%d/%b/%Y-%H:%M", local);
-       if(strcmp(DateFormat,"w") == 0)
+       else if (DateFormat=='w')
                strftime(ftime, ftimesize, "%W-%H-%M", local);
        return;
 }
@@ -1256,26 +1556,26 @@ void zdate(char *ftime,int ftimesize, const char *DateFormat)
 
 char *fixtime(long long int elap)
 {
-       int num = elap / 1000;
+       long int num = elap / 1000LL;
        int hor = 0;
        int min = 0;
        int sec = 0;
-       static char buf[12];
+       static char buf[20];
 
-       hor=num / 3600;
-       min=(num % 3600) / 60;
-       sec=num % 60;
+       hor=num / 3600L;
+       min=(num % 3600L) / 60L;
+       sec=num % 60L;
 
        if(hor==0 && min==0 && sec==0)
                strcpy(buf,"0");
        else
-               sprintf(buf,"%d:%02d:%02d",hor,min,sec);
+               snprintf(buf,sizeof(buf),"%d:%02d:%02d",hor,min,sec);
 
        return buf;
 }
 
 
-void date_from(char *date, int *dfrom, int *duntil)
+void date_from(char *date,int date_size, int *dfrom, int *duntil)
 {
        int d0=0;
        int m0=0;
@@ -1288,16 +1588,16 @@ void date_from(char *date, int *dfrom, int *duntil)
                int next=-1;
 
                if (sscanf(date,"%d/%d/%d%n",&d0,&m0,&y0,&next)!=3 || y0<100 || m0<1 || m0>12 || d0<1 || d0>31 || next<0) {
-                       debuga(_("The date passed as argument is not formated as dd/mm/yyyy or dd/mm/yyyy-dd/mm/yyyy\n"));
+                       debuga(__FILE__,__LINE__,_("The date passed as argument is not formated as dd/mm/yyyy or dd/mm/yyyy-dd/mm/yyyy\n"));
                        exit(EXIT_FAILURE);
                }
                if (date[next]=='-') {
                        if (sscanf(date+next+1,"%d/%d/%d",&d1,&m1,&y1)!=3 || y1<100 || m1<1 || m1>12 || d1<1 || d1>31) {
-                               debuga(_("The date range passed as argument is not formated as dd/mm/yyyy or dd/mm/yyyy-dd/mm/yyyy\n"));
+                               debuga(__FILE__,__LINE__,_("The date range passed as argument is not formated as dd/mm/yyyy or dd/mm/yyyy-dd/mm/yyyy\n"));
                                exit(EXIT_FAILURE);
                        }
                } else if (date[next]!='\0') {
-                       debuga(_("The date range passed as argument is not formated as dd/mm/yyyy or dd/mm/yyyy-dd/mm/yyyy\n"));
+                       debuga(__FILE__,__LINE__,_("The date range passed as argument is not formated as dd/mm/yyyy or dd/mm/yyyy-dd/mm/yyyy\n"));
                        exit(EXIT_FAILURE);
                } else {
                        d1=d0;
@@ -1310,18 +1610,18 @@ void date_from(char *date, int *dfrom, int *duntil)
                struct tm *Date0,Date1;
 
                if (time(&Today)==(time_t)-1) {
-                       debuga(_("Failed to get the current time\n"));
+                       debuga(__FILE__,__LINE__,_("Failed to get the current time\n"));
                        exit(EXIT_FAILURE);
                }
                if (sscanf(date,"day-%d",&i)==1) {
                        if (i<0) {
-                               debuga(_("Invalid number of days in -d parameter\n"));
+                               debuga(__FILE__,__LINE__,_("Invalid number of days in -d parameter\n"));
                                exit(EXIT_FAILURE);
                        }
                        Today-=i*24*60*60;
                        Date0=localtime(&Today);
                        if (Date0==NULL) {
-                               debuga(_("Cannot convert local time: %s\n"),strerror(errno));
+                               debuga(__FILE__,__LINE__,_("Cannot convert local time: %s\n"),strerror(errno));
                                exit(EXIT_FAILURE);
                        }
                        y0=y1=Date0->tm_year+1900;
@@ -1340,19 +1640,19 @@ void date_from(char *date, int *dfrom, int *duntil)
                        time_t WeekBegin;
 
                        if (i<0) {
-                               debuga(_("Invalid number of weeks in -d parameter\n"));
+                               debuga(__FILE__,__LINE__,_("Invalid number of weeks in -d parameter\n"));
                                exit(EXIT_FAILURE);
                        }
                        Date0=localtime(&Today);
                        if (Date0==NULL) {
-                               debuga(_("Cannot convert local time: %s\n"),strerror(errno));
+                               debuga(__FILE__,__LINE__,_("Cannot convert local time: %s\n"),strerror(errno));
                                exit(EXIT_FAILURE);
                        }
                        WeekBegin=Today-((Date0->tm_wday-FirstWeekDay+7)%7)*24*60*60;
                        WeekBegin-=i*7*24*60*60;
                        Date0=localtime(&WeekBegin);
                        if (Date0==NULL) {
-                               debuga(_("Cannot convert local time: %s\n"),strerror(errno));
+                               debuga(__FILE__,__LINE__,_("Cannot convert local time: %s\n"),strerror(errno));
                                exit(EXIT_FAILURE);
                        }
                        y0=Date0->tm_year+1900;
@@ -1361,7 +1661,7 @@ void date_from(char *date, int *dfrom, int *duntil)
                        WeekBegin+=6*24*60*60;
                        Date0=localtime(&WeekBegin);
                        if (Date0==NULL) {
-                               debuga(_("Cannot convert local time: %s\n"),strerror(errno));
+                               debuga(__FILE__,__LINE__,_("Cannot convert local time: %s\n"),strerror(errno));
                                exit(EXIT_FAILURE);
                        }
                        y1=Date0->tm_year+1900;
@@ -1369,12 +1669,12 @@ void date_from(char *date, int *dfrom, int *duntil)
                        d1=Date0->tm_mday;
                } else if (sscanf(date,"month-%d",&i)==1) {
                        if (i<0) {
-                               debuga(_("Invalid number of months in -d parameter\n"));
+                               debuga(__FILE__,__LINE__,_("Invalid number of months in -d parameter\n"));
                                exit(EXIT_FAILURE);
                        }
                        Date0=localtime(&Today);
                        if (Date0==NULL) {
-                               debuga(_("Cannot convert local time: %s\n"),strerror(errno));
+                               debuga(__FILE__,__LINE__,_("Cannot convert local time: %s\n"),strerror(errno));
                                exit(EXIT_FAILURE);
                        }
                        if (Date0->tm_mon<i%12) {
@@ -1403,14 +1703,14 @@ void date_from(char *date, int *dfrom, int *duntil)
                        m1=Date0->tm_mon+1;
                        d1=Date0->tm_mday;
                } else {
-                       debuga(_("Invalid date range passed on command line\n"));
+                       debuga(__FILE__,__LINE__,_("Invalid date range passed on command line\n"));
                        exit(EXIT_FAILURE);
                }
        }
 
        *dfrom=y0*10000+m0*100+d0;
        *duntil=y1*10000+m1*100+d1;
-       sprintf(date,"%02d/%02d/%04d-%02d/%02d/%04d",d0,m0,y0,d1,m1,y1);
+       snprintf(date,date_size,"%02d/%02d/%04d-%02d/%02d/%04d",d0,m0,y0,d1,m1,y1);
        return;
 }
 
@@ -1454,19 +1754,20 @@ void removetmp(const char *outdir)
                return;
 
        if(debug) {
-               debuga(_("Purging temporary file sarg-general\n"));
+               debuga(__FILE__,__LINE__,_("Purging temporary file sarg-general\n"));
        }
        if (snprintf(filename,sizeof(filename),"%s/sarg-general",outdir)>=sizeof(filename)) {
-               debuga(_("(removetmp) directory too long to remove %s/sarg-period\n"),outdir);
+               debuga(__FILE__,__LINE__,_("Path too long: "));
+               debuga_more("%s/sarg-period\n",outdir);
                exit(EXIT_FAILURE);
        }
        if((fp_gen=fopen(filename,"w"))==NULL){
-               debuga(_("(removetmp) Cannot open file %s\n"),filename);
+               debuga(__FILE__,__LINE__,_("Cannot open file \"%s\": %s\n"),filename,strerror(errno));
                exit(EXIT_FAILURE);
        }
        totalger(fp_gen,filename);
        if (fclose(fp_gen)==EOF) {
-               debuga(_("Failed to close %s after writing the total line - %s\n"),filename,strerror(errno));
+               debuga(__FILE__,__LINE__,_("Write error in \"%s\": %s\n"),filename,strerror(errno));
                exit(EXIT_FAILURE);
        }
 }
@@ -1483,27 +1784,27 @@ void load_excludecodes(const char *ExcludeCodes)
                return;
 
        if((fp_in=fopen(ExcludeCodes,"r"))==NULL) {
-               debuga(_("(util) Cannot open file %s (exclude_codes)\n"),ExcludeCodes);
+               debuga(__FILE__,__LINE__,_("Cannot open file \"%s\": %s\n"),ExcludeCodes,strerror(errno));
                exit(EXIT_FAILURE);
        }
 
        if (fseek(fp_in, 0, SEEK_END)==-1) {
-               debuga(_("Failed to move till the end of the excluded codes file %s: %s\n"),ExcludeCodes,strerror(errno));
+               debuga(__FILE__,__LINE__,_("Failed to move till the end of file \"%s\": %s\n"),ExcludeCodes,strerror(errno));
                exit(EXIT_FAILURE);
        }
        MemSize = ftell(fp_in);
        if (MemSize<0) {
-               debuga(_("Cannot get the size of file %s\n"),ExcludeCodes);
+               debuga(__FILE__,__LINE__,_("Cannot get the size of file \"%s\"\n"),ExcludeCodes);
                exit(EXIT_FAILURE);
        }
        if (fseek(fp_in, 0, SEEK_SET)==-1) {
-               debuga(_("Failed to rewind the excluded codes file %s: %s\n"),ExcludeCodes,strerror(errno));
+               debuga(__FILE__,__LINE__,_("Failed to rewind file \"%s\": %s\n"),ExcludeCodes,strerror(errno));
                exit(EXIT_FAILURE);
        }
 
        MemSize+=1;
        if((excludecode=(char *) malloc(MemSize))==NULL) {
-               debuga(_("malloc error (%ld)\n"),MemSize);
+               debuga(__FILE__,__LINE__,_("malloc error (%ld bytes required)\n"),MemSize);
                exit(EXIT_FAILURE);
        }
        memset(excludecode,0,MemSize);
@@ -1514,7 +1815,7 @@ void load_excludecodes(const char *ExcludeCodes)
                for (i=strlen(data)-1 ; i>=0 && (unsigned char)data[i]<=' ' ; i--) data[i]='\0';
                if (i<0) continue;
                if (Stored+i+2>=MemSize) {
-                       debuga(_("Too many codes to exclude in file %s\n"),ExcludeCodes);
+                       debuga(__FILE__,__LINE__,_("Too many codes to exclude in file \"%s\"\n"),ExcludeCodes);
                        break;
                }
                strcat(excludecode,data);
@@ -1522,7 +1823,10 @@ void load_excludecodes(const char *ExcludeCodes)
                Stored+=i+1;
        }
 
-       fclose(fp_in);
+       if (fclose(fp_in)==EOF) {
+               debuga(__FILE__,__LINE__,_("Read error in \"%s\": %s\n"),ExcludeCodes,strerror(errno));
+               exit(EXIT_FAILURE);
+       }
        return;
 }
 
@@ -1659,44 +1963,15 @@ int getnumlist( char *buf, numlist *list, const int len, const int maxvalue )
        return 0;
 }
 
-
-char *get_size(const char *path, const char *file)
-{
-       FILE *fp;
-       static char response[255];
-       char cmd[255];
-       char *ptr;
-
-       if (snprintf(cmd,sizeof(cmd),"du -skh %s%s",path,file)>=sizeof(cmd)) {
-               debuga(_("Cannot get disk space because the path %s%s is too long\n"),path,file);
-               exit(EXIT_FAILURE);
-       }
-       if ((fp = popen(cmd, "r")) == NULL) {
-               debuga(_("Cannot get disk space with command %s\n"),cmd);
-               exit(EXIT_FAILURE);
-       }
-       if (!fgets(response, sizeof(response), fp)) {
-               debuga(_("Cannot get disk size with command %s\n"),cmd);
-               exit(EXIT_FAILURE);
-       }
-       ptr=strchr(response,'\t');
-       if (ptr==NULL) {
-               debuga(_("The command %s failed\n"),cmd);
-               exit(EXIT_FAILURE);
-       }
-       pclose(fp);
-       *ptr='\0';
-
-       return (response);
-}
-
 void show_info(FILE *fp_ou)
 {
        char ftime[127];
 
        if(!ShowSargInfo) return;
-       zdate(ftime, sizeof(ftime), DateFormat);
-       fprintf(fp_ou,"<div class=\"info\">%s <a href='%s'>%s-%s</a> %s %s</div>\n",_("Generated by"),URL,PGM,VERSION,_("on"),ftime);
+       zdate(ftime, sizeof(ftime), df);
+       fputs("<div class=\"info\">",fp_ou);
+       fprintf(fp_ou,_("Generated by <a href=\"%s\">%s-%s</a> on %s"),URL,PGM,VERSION,ftime);
+       fputs("</div>\n",fp_ou);
 }
 
 void show_sarg(FILE *fp_ou, int depth)
@@ -1748,11 +2023,10 @@ void close_html_header(FILE *fp_ou)
        fputs("</table></div>\n",fp_ou);
 }
 
-int write_html_trailer(FILE *fp_ou)
+void write_html_trailer(FILE *fp_ou)
 {
        show_info(fp_ou);
-       if (fputs("</body>\n</html>\n",fp_ou)==EOF) return(-1);
-       return(0);
+       fputs("</body>\n</html>\n",fp_ou);
 }
 
 void output_html_string(FILE *fp_ou,const char *str,int maxlen)
@@ -1801,7 +2075,7 @@ void output_html_url(FILE *fp_ou,const char *url)
   Write a host name inside an A tag of a HTML file. If the host name starts
   with a star, it is assumed to be an alias that cannot be put inside a link
   so the A tag is not written around the host name.
-  
+
   \param fp_ou The handle of the HTML file.
   \param url The host to display in the HTML file.
   \param maxlen The maximum number of characters to print into the host name.
@@ -1845,27 +2119,76 @@ void url_module(const char *url, char *w2)
        w2[x]='\0';
 }
 
-void url_to_file(const char *url,char *file,int filesize)
+/*!
+Mangle an URL to produce a part that can be used as an anchor in
+a html <a name=""> tag.
+
+\param url The URL to mangle.
+\param anchor The buffer to write the mangled URL.
+\param size The size of the buffer.
+*/
+void url_to_anchor(const char *url,char *anchor,int size)
 {
-       int i,skip;
+       int i,j;
+       bool skip;
 
-       filesize--;
-       skip=0;
-       for(i=0; i<filesize && *url; url++) {
-               if(isalnum(*url) || *url=='-' || *url=='_' || *url=='.' || *url=='%') {
-                       file[i++]=*url;
-                       skip=0;
+       // find url end
+       for (i=0 ; url[i] && url[i]!='/' && url[i]!='?' ; i++);
+       i--;
+       if (i<=0) {
+               anchor[0]='\0';
+               return;
+       }
+
+       // only keep really safe characters
+       skip=false;
+       j=size-1;
+       anchor[j]='\0';
+       while (j>0 && i>=0)
+       {
+               if(isalnum(url[i]) || url[i]=='-' || url[i]=='_' || url[i]=='.') {
+                       anchor[--j]=url[i];
+                       skip=false;
                } else {
-                       if (!skip) file[i++]='_';
-                       skip=1;
+                       if (!skip) anchor[--j]='_';
+                       skip=true;
                }
+               i--;
+       }
+       if (j>0)
+       {
+               while ( anchor[j])
+               {
+                       *anchor=anchor[j];
+                       anchor++;
+               }
+               *anchor='\0';
        }
-       file[i]='\0';
 }
 
 void version(void)
 {
        printf(_("SARG Version: %s\n"),VERSION);
+#if defined(ENABLE_NLS) && defined(HAVE_LOCALE_H)
+       if (debug) {
+               printf(_("\nFor the translation to work, a valid message file should be copied to "
+                                "\"%s/<Locale>/LC_MESSAGES/%s.mo\" where <Locale> is derived from the effective locale.\n"),LOCALEDIR,PACKAGE_NAME);
+               if (CurrentLocale) {
+                       printf(_("Currently effective locale is \"%s\".\n"),CurrentLocale);
+               } else {
+                       printf(_("Locale is not set in the environment variable.\n"));
+               }
+               // TRANSLATORS: You may change this message to tell the reader that the language is correctly supported.
+               printf(_("If this message is in English, then your language is not supported or not correctly installed.\n"));
+       }
+#endif
+       if (debug) {
+#ifdef HAVE_GLOB_H
+               printf(_("File globbing compiled in.\n"));
+#else
+               printf(_("File globbing NOT compiled in.\n"));
+#endif
+       }
        exit(EXIT_SUCCESS);
 }
 
@@ -1882,7 +2205,7 @@ char *get_param_value(const char *param,char *line)
        return(line);
 }
 
-void unlinkdir(const char *dir,int contentonly)
+void unlinkdir(const char *dir,bool contentonly)
 {
        struct stat st;
        DIR *dirp;
@@ -1897,7 +2220,8 @@ void unlinkdir(const char *dir,int contentonly)
                    (direntp->d_name[1] == '.' && direntp->d_name[2] == '\0')))
                        continue;
                if (snprintf(dname,sizeof(dname),"%s/%s",dir,direntp->d_name)>=sizeof(dname)) {
-                       debuga(_("directory name to delete too long: %s/%s\n"),dir,direntp->d_name);
+                       debuga(__FILE__,__LINE__,_("Path too long: "));
+                       debuga_more("%s/%s\n",dir,direntp->d_name);
                        exit(EXIT_FAILURE);
                }
 #ifdef HAVE_LSTAT
@@ -1906,28 +2230,157 @@ void unlinkdir(const char *dir,int contentonly)
                err=stat(dname,&st);
 #endif
                if (err) {
-                       debuga(_("cannot stat %s\n"),dname);
+                       debuga(__FILE__,__LINE__,_("Cannot stat \"%s\": %s\n"),dname,strerror(errno));
                        exit(EXIT_FAILURE);
                }
                if (S_ISREG(st.st_mode)) {
                        if (unlink(dname)) {
-                               debuga(_("cannot delete %s - %s\n"),dname,strerror(errno));
+                               debuga(__FILE__,__LINE__,_("Cannot delete \"%s\": %s\n"),dname,strerror(errno));
                                exit(EXIT_FAILURE);
                        }
                } else if (S_ISDIR(st.st_mode)) {
                        unlinkdir(dname,0);
                } else {
-                       debuga(_("unknown path type %s\n"),dname);
+                       debuga(__FILE__,__LINE__,_("Don't know how to delete \"%s\" (not a regular file nor a directory)\n"),dname);
                }
        }
        closedir(dirp);
 
        if (!contentonly) {
                if (rmdir(dir)) {
-                       debuga(_("cannot delete %s - %s\n"),dir,strerror(errno));
+                       debuga(__FILE__,__LINE__,_("Cannot delete \"%s\": %s\n"),dir,strerror(errno));
+                       exit(EXIT_FAILURE);
+               }
+       }
+}
+
+/*!
+Delete every file from the temporary directory where sarg is told to store its
+temporary files.
+
+As any stray file left over by a previous run would be included in the report, we
+must delete every file from the temporary directory before we start processing the logs.
+
+But the temporary directory is given by the user either in the configuration file or
+on the command line. We check that the user didn't give a wrong directory by looking
+at the files stored in the directory. If a single file is not one of ours, we abort.
+
+\param dir The temporary directory to purge.
+*/
+void emptytmpdir(const char *dir)
+{
+       struct stat st;
+       DIR *dirp;
+       struct dirent *direntp;
+       int dlen;
+       int elen;
+       char dname[MAXLEN];
+       int err;
+       int i;
+       static const char *TmpExt[]=
+       {
+               ".int_unsort",
+               ".int_log",
+               ".day",
+               "htmlrel.txt",
+               ".user_unsort",
+               ".user_log",
+               ".utmp",
+               ".ip",
+               "lastlog1",
+               "lastlog",
+               "emailrep"
+       };
+
+       dirp=opendir(dir);
+       if (!dirp) return;
+
+       // make sure the temporary directory contains only our files
+       while ((direntp = readdir(dirp)) != NULL) {
+               if (direntp->d_name[0] == '.' && (direntp->d_name[1] == '\0' ||
+                   (direntp->d_name[1] == '.' && direntp->d_name[2] == '\0')))
+                       continue;
+
+               // is it one of our files
+               dlen=strlen(direntp->d_name);
+               for (i=sizeof(TmpExt)/sizeof(TmpExt[0])-1 ; i>=0 ; i--) {
+                       elen=strlen(TmpExt[i]);
+                       if (dlen>=elen && strcasecmp(direntp->d_name+dlen-elen,TmpExt[i])==0) break;
+               }
+               if (i<0) {
+                       debuga(__FILE__,__LINE__,_("Unknown file \"%s\" found in temporary directory \"%s\". It is not one of our files. "
+                       "Please check the temporary directory you gave to sarg. Adjust the path to a safe "
+                       "directory or manually delete the content of \"%s\"\n"),direntp->d_name,dir,dir);
                        exit(EXIT_FAILURE);
                }
+
+               if (snprintf(dname,sizeof(dname),"%s/%s",dir,direntp->d_name)>=sizeof(dname)) {
+                       debuga(__FILE__,__LINE__,_("Path too long: "));
+                       debuga_more("%s/%s\n",dir,direntp->d_name);
+                       exit(EXIT_FAILURE);
+               }
+
+#ifdef HAVE_LSTAT
+               err=lstat(dname,&st);
+#else
+               err=stat(dname,&st);
+#endif
+               if (err) {
+                       debuga(__FILE__,__LINE__,_("Cannot stat \"%s\": %s\n"),dname,strerror(errno));
+                       exit(EXIT_FAILURE);
+               }
+               if (!S_ISDIR(st.st_mode) && !S_ISREG(st.st_mode)) {
+                       debuga(__FILE__,__LINE__,_("Unknown path type for \"%s\". Check temporary directory\n"),dname);
+                       exit(EXIT_FAILURE);
+               }
+       }
+       rewinddir(dirp);
+
+       // now delete our files
+       while ((direntp = readdir(dirp)) != NULL) {
+               if (direntp->d_name[0] == '.' && (direntp->d_name[1] == '\0' ||
+                   (direntp->d_name[1] == '.' && direntp->d_name[2] == '\0')))
+                       continue;
+
+               // is it one of our files
+               dlen=strlen(direntp->d_name);
+               for (i=sizeof(TmpExt)/sizeof(TmpExt[0])-1 ; i>=0 ; i--) {
+                       elen=strlen(TmpExt[i]);
+                       if (dlen>=elen && strcasecmp(direntp->d_name+dlen-elen,TmpExt[i])==0) break;
+               }
+               if (i<0) {
+                       debuga(__FILE__,__LINE__,_("Unknown file \"%s\" found in temporary directory \"%s\". It is not one of our files. "
+                       "Please check the temporary directory you gave to sarg. Adjust the path to a safe "
+                       "directory or manually delete the content of \"%s\"\n"),direntp->d_name,dir,dir);
+                       exit(EXIT_FAILURE);
+               }
+
+               if (snprintf(dname,sizeof(dname),"%s/%s",dir,direntp->d_name)>=sizeof(dname)) {
+                       debuga(__FILE__,__LINE__,_("Path too long: "));
+                       debuga_more("%s/%s\n",dir,direntp->d_name);
+                       exit(EXIT_FAILURE);
+               }
+#ifdef HAVE_LSTAT
+               err=lstat(dname,&st);
+#else
+               err=stat(dname,&st);
+#endif
+               if (err) {
+                       debuga(__FILE__,__LINE__,_("Cannot stat \"%s\": %s\n"),dname,strerror(errno));
+                       exit(EXIT_FAILURE);
+               }
+               if (S_ISDIR(st.st_mode)) {
+                       unlinkdir(dname,0);
+               } else if (S_ISREG(st.st_mode)) {
+                       if (unlink(dname)) {
+                               debuga(__FILE__,__LINE__,_("Cannot delete \"%s\": %s\n"),dname,strerror(errno));
+                               exit(EXIT_FAILURE);
+                       }
+               } else {
+                       debuga(__FILE__,__LINE__,_("Don't know how to delete \"%s\" (not a regular file)\n"),dname);
+               }
        }
+       closedir(dirp);
 }
 
 /*!
@@ -1965,7 +2418,7 @@ int extract_address_mask(const char *buf,const char **text,unsigned char *ipv4,u
 
        // skip leading spaces and tabs
        while (*buf && (*buf==' ' || *buf=='\t')) buf++;
-       
+
        // find out the nature of the pattern
        ip_size=0x60  | 0x04;
        if (*buf=='[') {