]> 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 84c85dba921ffefd6090a9c09e439d9b08167b1d..aba23b191b769db4af96cb708361bda67c3d6bdb 100644 (file)
--- a/util.c
+++ b/util.c
@@ -1,6 +1,6 @@
 /*
  * SARG Squid Analysis Report Generator      http://sarg.sourceforge.net
- *                                                            1998, 2010
+ *                                                            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,1252 +46,1686 @@ 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;
 
-/*void fgetword(char *word, char *line, int stop)
-{
-    //VARIANT N1
-    int x;
-
-    for (x=0; line[x] && (line[x] != stop); x++) word[x] = line[x];
-    word[x] = '\0';
-
-    //VARIANT N2
-    char *tchar;
-    int difflen;
+//! Directory where the images are stored.
+char ImageDir[MAXLEN]=IMAGEDIR;
 
-    tchar = strchr(line, stop);
-    if (tchar == NULL) strcpy(word, line);
-    else
-    {
-       difflen = tchar - line;
-       strncpy(word, line, difflen);
-       word[difflen] = '\0';
-    }
-}*/
+extern char *CurrentLocale;
 
 #if USE_GETWORD_BACKTRACE
 static void getword_backtrace(void)
 {
-   void *buffer[5];
-   int i, n;
-   char **calls;
-
-   n=backtrace(buffer,sizeof(buffer)/sizeof(buffer[0]));
-   if (n<=0) return;
-   calls=backtrace_symbols(buffer,n);
-   if (calls) {
-      debuga(_("getword backtrace:\n"));
-      for (i=0 ; i<n ; i++) {
-         fprintf(stderr,"SARG: %d:%s\n",i+1,calls[i]);
-      }
-      free(calls);
-   }
+       void *buffer[5];
+       int i, n;
+       char **calls;
+
+       n=backtrace(buffer,sizeof(buffer)/sizeof(buffer[0]));
+       if (n<=0) return;
+       calls=backtrace_symbols(buffer,n);
+       if (calls) {
+               debuga(__FILE__,__LINE__,_("getword backtrace:\n"));
+               for (i=0 ; i<n ; i++) {
+                       fprintf(stderr,"SARG: %d:%s\n",i+1,calls[i]);
+               }
+               free(calls);
+       }
 }
 #endif //USE_GETWORD_BACKTRACE
 
 void getword_start(struct getwordstruct *gwarea, const char *line)
 {
-   gwarea->beginning=line;
-   gwarea->current=line;
-   gwarea->modified=0;
+       gwarea->beginning=line;
+       gwarea->current=line;
+       gwarea->modified=0;
 }
 
 void getword_restart(struct getwordstruct *gwarea)
 {
-   if (gwarea->modified) {
-      debuga(_("Cannot parse again the line as it was modified\n"));
-      exit(EXIT_FAILURE);
-   }
-   gwarea->current=gwarea->beginning;
+       if (gwarea->modified) {
+               debuga(__FILE__,__LINE__,_("Cannot parse again the line as it was modified\n"));
+               exit(EXIT_FAILURE);
+       }
+       gwarea->current=gwarea->beginning;
 }
 
 int getword(char *word, int limit, struct getwordstruct *gwarea, char stop)
 {
-  int x;
-
-  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");
-        word[(limit>0) ? limit-1 : 0]='\0';
+       int x;
+
+       for(x=0;((gwarea->current[x]) && (gwarea->current[x] != stop ));x++) {
+               if(x>=limit) {
+                       /*
+                        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();
+                       getword_backtrace();
 #endif
-        return(-1);
-     }
-     word[x] = gwarea->current[x];
-  }
+                       return(-1);
+               }
+               word[x] = gwarea->current[x];
+       }
 
-  word[x] = '\0';
-  if (gwarea->current[x]) ++x;
-  gwarea->current+=x;
-  return(0);
+       word[x] = '\0';
+       if (gwarea->current[x]) ++x;
+       gwarea->current+=x;
+       return(0);
 }
 
 int getword_limit(char *word, int limit, struct getwordstruct *gwarea, char stop)
 {
-  int x;
+       int x;
 
-  limit--;
-  for(x=0; x<limit && gwarea->current[x] && gwarea->current[x] != stop ;x++) {
-     word[x] = gwarea->current[x];
-  }
-  word[x] = '\0';
-  gwarea->current+=x;
-  while (*gwarea->current && *gwarea->current != stop)  gwarea->current++;
-  if (*gwarea->current) ++gwarea->current;
-  return(0);
+       limit--;
+       for(x=0; x<limit && gwarea->current[x] && gwarea->current[x] != stop ;x++) {
+               word[x] = gwarea->current[x];
+       }
+       word[x] = '\0';
+       gwarea->current+=x;
+       while (*gwarea->current && *gwarea->current != stop)  gwarea->current++;
+       if (*gwarea->current) ++gwarea->current;
+       return(0);
 }
 
 int getword_multisep(char *word, int limit, struct getwordstruct *gwarea, char stop)
 {
-  int x;
+       int x;
 
-  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");
-        if (limit>0) word[limit-1]='\0';
+       for(x=0;((gwarea->current[x]) && (gwarea->current[x] != stop ));x++) {
+               if(x>=limit) {
+                       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();
+                       getword_backtrace();
 #endif
-        //exit(EXIT_FAILURE);
-        return(-1);
-     }
-     word[x] = gwarea->current[x];
-  }
+                       //exit(EXIT_FAILURE);
+                       return(-1);
+               }
+               word[x] = gwarea->current[x];
+       }
 
-  word[x] = '\0';
-  while (gwarea->current[x] && gwarea->current[x]==stop) ++x;
-  gwarea->current+=x;
-  return(0);
+       word[x] = '\0';
+       while (gwarea->current[x] && gwarea->current[x]==stop) ++x;
+       gwarea->current+=x;
+       return(0);
 }
 
 int getword_skip(int limit, struct getwordstruct *gwarea, char stop)
 {
-   int x;
+       int x;
 
-   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");
+       for(x=0;(gwarea->current[x] && (gwarea->current[x] != stop ));x++) {
+               if(x>=limit) {
+                       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();
+                       getword_backtrace();
 #endif
-         return(-1);
-      }
-   }
+                       return(-1);
+               }
+       }
 
-   if (gwarea->current[x]) ++x;
-   gwarea->current+=x;
-   return(0);
+       if (gwarea->current[x]) ++x;
+       gwarea->current+=x;
+       return(0);
 }
 
 int getword_atoll(long long int *number, struct getwordstruct *gwarea, char stop)
 {
-   int x;
-   int sign=+1;
-
-   if (gwarea->current[0] == '-') {
-      gwarea->current++;
-      sign=-1;
-   } else if (gwarea->current[0] == '+') {
-      gwarea->current++;
-   }
-   *number=0LL;
-   for(x=0;isdigit(gwarea->current[x]);x++) {
-      *number=(*number * 10) + gwarea->current[x]-'0';
-   }
-   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");
+       int x;
+       int sign=+1;
+       int digit;
+
+       if (gwarea->current[0] == '-') {
+               gwarea->current++;
+               sign=-1;
+       } else if (gwarea->current[0] == '+') {
+               gwarea->current++;
+       }
+       *number=0LL;
+       for(x=0;isdigit(gwarea->current[x]);x++) {
+               digit=gwarea->current[x]-'0';
+               if (*number >= (LLONG_MAX-digit)/10) {
+                       /*
+                        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) {
+               /*
+                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();
+               getword_backtrace();
 #endif
-      return(-1);
-   }
-   *number*=sign;
+               return(-1);
+       }
+       *number*=sign;
+
+       if (gwarea->current[x]) ++x;
+       gwarea->current+=x;
+       return(0);
+}
+
+int getword_atoi(int *number, struct getwordstruct *gwarea, char stop)
+{
+       int x;
+       int sign=+1;
+       int digit;
+
+       if (gwarea->current[0] == '-') {
+               gwarea->current++;
+               sign=-1;
+       } else if (gwarea->current[0] == '+') {
+               gwarea->current++;
+       }
+       *number=0;
+       for(x=0;isdigit(gwarea->current[x]);x++) {
+               digit=gwarea->current[x]-'0';
+               if (*number > (INT_MAX-digit)/10) {
+                       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) {
+               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
+               return(-1);
+       }
+       *number*=sign;
+
+       if (gwarea->current[x]) ++x;
+       gwarea->current+=x;
+       return(0);
+}
+
+int getword_atol(long int *number, struct getwordstruct *gwarea, char stop)
+{
+       int x;
+       long int sign=+1;
+       int digit;
+
+       if (gwarea->current[0] == '-') {
+               gwarea->current++;
+               sign=-1;
+       } else if (gwarea->current[0] == '+') {
+               gwarea->current++;
+       }
+       *number=0;
+       for(x=0;isdigit(gwarea->current[x]);x++) {
+               digit=gwarea->current[x]-'0';
+               if (*number > (LONG_MAX-digit)/10) {
+                       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) {
+               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
+               return(-1);
+       }
+       *number*=sign;
+
+       if (gwarea->current[x]) ++x;
+       gwarea->current+=x;
+       return(0);
+}
+
+int getword_atolu(unsigned long int *number, struct getwordstruct *gwarea, char stop)
+{
+       int x;
+       int digit;
+
+       if (gwarea->current[0] == '-') {
+               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] == '+') {
+               gwarea->current++;
+       }
+       *number=0;
+       for(x=0;isdigit(gwarea->current[x]);x++) {
+               digit=gwarea->current[x]-'0';
+               if (*number > (ULONG_MAX-digit)/10) {
+                       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) {
+               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
+               return(-1);
+       }
 
-   if (gwarea->current[x]) ++x;
-   gwarea->current+=x;
-   return(0);
+       if (gwarea->current[x]) ++x;
+       gwarea->current+=x;
+       return(0);
 }
 
 
 int getword_ptr(char *orig_line,char **word, struct getwordstruct *gwarea, char stop)
 {
-   /*!
-   \note Why pass the original buffer to the function ? Because we must modify it to
-   insert the terminating ASCII zero for the word we return and that's not compatible
-   with getword_restart(). Moreover, getword_start() sometime works on constant strings
-   so this function require the original buffer to detect any missuse.
-   */
-   int x;
-   int sep;
-   int start;
-
-   if (orig_line && orig_line!=gwarea->beginning) {
-      debuga(_("Invalid buffer passed to getword_ptr\n"));
-      return(-1);
-   }
-
-   start=(gwarea->current-gwarea->beginning);
-   if (word && orig_line) *word=orig_line+start;
-   for(x=0;((gwarea->current[x]) && (gwarea->current[x] != stop ));x++);
-   sep=(gwarea->current[x]!='\0');
-   if (word && orig_line) orig_line[start+x] = '\0';
-   if (sep) ++x;
-   gwarea->current+=x;
-   gwarea->modified=1;
-   return(0);
+       /*!
+       \note Why pass the original buffer to the function ? Because we must modify it to
+       insert the terminating ASCII zero for the word we return and that's not compatible
+       with getword_restart(). Moreover, getword_start() sometime works on constant strings
+       so this function require the original buffer to detect any missuse.
+       */
+       int x;
+       int sep;
+       int start;
+
+       if (orig_line && orig_line!=gwarea->beginning) {
+               debuga(__FILE__,__LINE__,_("Invalid buffer passed to getword_ptr\n"));
+               return(-1);
+       }
+
+       start=(gwarea->current-gwarea->beginning);
+       if (word && orig_line) *word=orig_line+start;
+       for(x=0;((gwarea->current[x]) && (gwarea->current[x] != stop ));x++);
+       sep=(gwarea->current[x]!='\0');
+       if (word && orig_line) orig_line[start+x] = '\0';
+       if (sep) ++x;
+       gwarea->current+=x;
+       gwarea->modified=1;
+       return(0);
 }
 
 #define MAXLLL 30 //!< Maximum number of digits in long long (a guess).
 long long int my_atoll (const char *nptr)
 {
-  long long int returnval=0LL;
-  int max_digits = MAXLLL ;
+       long long int returnval=0LL;
+       int max_digits = MAXLLL ;
 
-  // Soak up all the white space
-  while (isspace( *nptr )) {
-    nptr++;
-  }
+       // Soak up all the white space
+       while (isspace( *nptr )) {
+               nptr++;
+       }
 
-  //For each character left to right
-  //change the character to a single digit
-  //multiply what we had before by 10 and add the new digit
+       //For each character left to right
+       //change the character to a single digit
+       //multiply what we had before by 10 and add the new digit
 
-  while (--max_digits && isdigit( *nptr ))
-  {
-     returnval = ( returnval * 10 ) + ( *nptr++ - '0' ) ;
-  }
+       while (--max_digits && isdigit( *nptr ))
+       {
+               returnval = ( returnval * 10 ) + ( *nptr++ - '0' ) ;
+       }
 
-  return returnval;
+       return returnval;
 }
 
 int is_absolute(const char *path)
 {
-   if (*path=='/') return(1);
-#ifdef WINDOWS
-   if (isalpha(path[0]) && path[1]==':') return(1);
+       if (*path=='/') return(1);
+#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(0);
+       return(mkerror);
 }
 
 void my_mkdir(const char *name)
 {
-   char w0[MAXLEN];
-   int i;
-   int chars;
-
-   if(!is_absolute(name)) {
-      debuga(_("Invalid path (%s). Please, use absolute paths only.\n"),name);
-      debuga(_("process aborted.\n"));
-      exit(EXIT_FAILURE);
-   }
-
-   chars=0;
-   for (i=0 ; name[i] ; i++) {
-      if (i>=sizeof(w0)) {
-         debuga(_("directory name too long: %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(_("mkdir %s %s\n"),w0,strerror(errno));
-               debuga(_("process aborted.\n"));
-               exit(EXIT_FAILURE);
-            }
-         }
-      }
-      if (name[i] != '/') chars++;
-      w0[i] = name[i];
-   }
-
-   if(access(name, R_OK) != 0) {
-      if(mkdir(name,0755)) {
-         debuga(_("mkdir %s %s\n"),name,strerror(errno));
-         debuga(_("process aborted.\n"));
-         exit(EXIT_FAILURE);
-      }
-   }
+       char w0[MAXLEN];
+       int i;
+       int chars;
+
+       if(!is_absolute(name)) {
+               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(__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 (PortableMkDir(w0,0755)) {
+                                       debuga(__FILE__,__LINE__,_("Cannot create directory \"%s\": %s\n"),w0,strerror(errno));
+                                       exit(EXIT_FAILURE);
+                               }
+                       }
+               }
+               if (name[i] != '/') chars++;
+               w0[i] = name[i];
+       }
+
+       if (access(name, R_OK) != 0) {
+               if (PortableMkDir(name,0755)) {
+                       debuga(__FILE__,__LINE__,_("Cannot create directory \"%s\": %s\n"),name,strerror(errno));
+                       exit(EXIT_FAILURE);
+               }
+       }
 }
 
 
 void my_lltoa(unsigned long long int n, char *s, int ssize, int len)
 {
-   int i;
-   int slen = 0;
-   int j;
-   char c;
-
-   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);
-      abort();
-   }
-
-   do {
-      s[slen++] = (n % 10) + '0';
-   } while ((n /= 10) > 0 && slen<ssize);
-   s[slen] = '\0';
-
-   for (i = 0, j = slen-1; i<j; i++, j--) {
-      c = s[i];
-      s[i] = s[j];
-      s[j] = c;
-   }
-
-   if(len>slen) {
-      i=len-slen;
-      for(j=slen; j>=0; j--)
-         s[j+i]=s[j];
-      for(j=0 ; j<i ; j++)
-         s[j]='0';
-   }
+       int i;
+       int slen = 0;
+       int j;
+       char c;
+
+       ssize--;
+       if (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();
+       }
+
+       do {
+               s[slen++] = (n % 10) + '0';
+       } while ((n /= 10) > 0 && slen<ssize);
+       s[slen] = '\0';
+
+       for (i = 0, j = slen-1; i<j; i++, j--) {
+               c = s[i];
+               s[i] = s[j];
+               s[j] = c;
+       }
+
+       if(len>slen) {
+               i=len-slen;
+               for(j=slen; j>=0; j--)
+                       s[j+i]=s[j];
+               for(j=0 ; j<i ; j++)
+                       s[j]='0';
+       }
 }
 
 int month2num(const char *month)
 {
-   int m;
+       int m;
 
-   for(m=0 ; m<12 && strcmp(mtab1[m],month) != 0; m++);
-   return(m);
+       for(m=0 ; m<12 && strcmp(mtab1[m],month) != 0; m++);
+       return(m);
 }
 
 int builddia(int day, int month, int year)
 {
-   return(year*10000+month*100+day);
-}
-
-
-void buildymd(const char *dia, const char *mes, const char *ano, char *wdata)
-{
-   int nmes;
-
-   nmes=month2num(mes);
-   sprintf(wdata,"%04d%02d%02d",atoi(ano),nmes+1,atoi(dia));
+       return(year*10000+month*100+day);
 }
 
+/*!
+Compare two dates.
 
-int conv_month(const char *month)
-{
-   int  x;
-
-   for(x=0; x<12 && strncmp(mtab1[x],month,3)!=0; x++);
-   return(x+1);
-}
-
+\param date1 The first date to compare.
+\param date2 The second date to compare.
 
-const char *conv_month_name(int month)
+\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)
 {
-   static char str[4];
-
-   if (month<1 || month>12) {
-      snprintf(str,sizeof(str),"%03d",month);
-      return(str);
-   }
-   return(mtab1[month-1]);
+       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 name_month(char *month,int month_len)
+void buildymd(const char *dia, const char *mes, const char *ano, char *wdata,int wdata_size)
 {
-   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);
+       int nmes;
 
-   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);
-   }
+       nmes=month2num(mes);
+       snprintf(wdata,wdata_size,"%04d%02d%02d",atoi(ano),nmes+1,atoi(dia));
 }
 
 
-void debuga(const char *msg,...)
+int conv_month(const char *month)
 {
-   va_list ap;
+       int  x;
 
-   fputs(_("SARG: "),stderr);
-   va_start(ap,msg);
-   vfprintf(stderr,msg,ap);
-   va_end(ap);
+       for(x=0; x<12 && strncmp(mtab1[x],month,3)!=0; x++);
+       return(x+1);
 }
 
 
-void debugaz(const char *head, const char *msg)
+const char *conv_month_name(int month)
 {
-  fprintf(stderr, "SARG: (util) %s=%s\n",head, msg);
-
+       static char str[4];
+
+       if (month<1 || month>12) {
+               snprintf(str,sizeof(str),"%03d",month);
+               return(str);
+       }
+       return(mtab1[month-1]);
+}
+
+/*!
+Write a debug message to stderr. The message is prefixed by "SARG:" to identify its origin.
+
+\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,...)
+{
+       va_list ap;
+
+       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 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_more(const char *msg,...)
+{
+       va_list ap;
+
+       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 *File,int Line,const char *msg,...)
+{
+       va_list ap;
+
+       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);
 }
 
 
 char *fixnum(long long int value, int n)
 {
 #define MAXLEN_FIXNUM 256
-   char num[MAXLEN_FIXNUM]="";
-   char buf[MAXLEN_FIXNUM * 2];
-   char *pbuf;
-   static char ret[MAXLEN_FIXNUM * 2];
-   char *pret;
-   register int i, j, k;
-   int numlen;
-   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);
-      }
-
-      return(abbrev);
-   }
-
-   bzero(buf, MAXLEN_FIXNUM*2);
-
-   pbuf = buf;
-   pret = ret;
-   k = 0;
-
-   for ( i = strlen(num) - 1, j = 0 ; i > -1; i--) {
-      if ( k == 2 && i != 0 )  {
-         k = 0;
-         pbuf[j++] = num[i];
-         pbuf[j++] = (UseComma) ? ',' : '.';
-         continue;
-      }
-      pbuf[j] = num[i];
-      j++;
-      k++;
-   }
-
-   pret[0]='\0';
-
-   for ( i = strlen(pbuf) - 1, j = 0 ; i > -1; i--, j++)
-      pret[j] = pbuf[i];
-
-   pret[j] = '\0';
-
-   return pret;
+       char num[MAXLEN_FIXNUM]="";
+       char buf[MAXLEN_FIXNUM * 2];
+       char *pbuf;
+       static char ret[MAXLEN_FIXNUM * 2];
+       char *pret;
+       register int i, j, k;
+       int numlen;
+       static char abbrev[30]="";
+
+       my_lltoa(value, num, sizeof(num), 0);
+
+       if(DisplayedValues==DISPLAY_ABBREV) {
+               numlen = strlen(num);
+               if(numlen <= 3)
+                       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);
+       }
+
+       memset(buf,0,MAXLEN_FIXNUM*2);
+
+       pbuf = buf;
+       pret = ret;
+       k = 0;
+
+       for ( i = strlen(num) - 1, j = 0 ; i > -1; i--) {
+               if ( k == 2 && i != 0 )  {
+                       k = 0;
+                       pbuf[j++] = num[i];
+                       pbuf[j++] = (UseComma) ? ',' : '.';
+                       continue;
+               }
+               pbuf[j] = num[i];
+               j++;
+               k++;
+       }
+
+       pret[0]='\0';
+
+       for ( i = strlen(pbuf) - 1, j = 0 ; i > -1; i--, j++)
+               pret[j] = pbuf[i];
+
+       pret[j] = '\0';
+
+       return pret;
 }
 
 
 char *fixnum2(long long int value, int n)
 {
 #define MAXLEN_FIXNUM2 1024
-   char num[MAXLEN_FIXNUM2];
-   char buf[MAXLEN_FIXNUM2 * 2];
-   char *pbuf;
-   static char ret[MAXLEN_FIXNUM2 * 2];
-   char *pret;
-   register int i, j, k;
+       char num[MAXLEN_FIXNUM2];
+       char buf[MAXLEN_FIXNUM2 * 2];
+       char *pbuf;
+       static char ret[MAXLEN_FIXNUM2 * 2];
+       char *pret;
+       register int i, j, k;
 
-   my_lltoa(value, num, sizeof(num), 0);
-   bzero(buf, MAXLEN_FIXNUM2*2);
+       my_lltoa(value, num, sizeof(num), 0);
+       memset(buf,0,MAXLEN_FIXNUM2*2);
 
-   pbuf = buf;
-   pret = ret;
-   k = 0;
+       pbuf = buf;
+       pret = ret;
+       k = 0;
 
-   for ( i = strlen(num) - 1, j = 0 ; i > -1; i--) {
-      if ( k == 2 && i != 0 )  {
-         k = 0;
-         pbuf[j++] = num[i];
-         pbuf[j++] = (UseComma) ? ',' : '.';
-         continue;
-      }
-      pbuf[j] = num[i];
-      j++;
-      k++;
-   }
+       for ( i = strlen(num) - 1, j = 0 ; i > -1; i--) {
+               if ( k == 2 && i != 0 )  {
+                       k = 0;
+                       pbuf[j++] = num[i];
+                       pbuf[j++] = (UseComma) ? ',' : '.';
+                       continue;
+               }
+               pbuf[j] = num[i];
+               j++;
+               k++;
+       }
 
-   pret[0]='\0';
+       pret[0]='\0';
 
-   for ( i = strlen(pbuf) - 1, j = 0 ; i > -1; i--, j++)
-      pret[j] = pbuf[i];
+       for ( i = strlen(pbuf) - 1, j = 0 ; i > -1; i--, j++)
+               pret[j] = pbuf[i];
 
-   pret[j] = '\0';
+       pret[j] = '\0';
 
-   return pret;
+       return pret;
 }
 
 
 char *buildtime(long long int elap)
 {
+       long int num = elap / 1000LL;
+       int hor = 0;
+       int min = 0;
+       int sec = 0;
+       static char buf[20];
 
-   int num = elap / 1000;
-   int hor = 0;
-   int min = 0;
-   int sec = 0;
-   static char buf[12];
-
-   buf[0]='\0';
-
-   hor=num / 3600;
-   min=(num % 3600) / 60;
-   sec=num % 60;
-   sprintf(buf,"%02d:%02d:%02d",hor,min,sec);
-
-   return(buf);
+       hor=num / 3600L;
+       min=(num % 3600L) / 60L;
+       sec=num % 60L;
+       snprintf(buf,sizeof(buf),"%02d:%02d:%02d",hor,min,sec);
 
+       return(buf);
 }
 
 
-void obtdate(const char *dirname, const char *name, char *data)
-{
+/*!
+Get the date stored in the <tt>sarg-date</tt> file of a directory with the connection data.
 
-   FILE *fp_in;
-   char wdir[MAXLEN];
+\param dirname The directory to look for the connection directory.
+\param name The name of the directory whose <tt>sarg-date</tt> file must be read.
+\param data The buffer to store the content of the file. It must be more than 80
+bytes long.
 
-   sprintf(wdir,"%s%s/sarg-date",dirname,name);
-   if ((fp_in = fopen(wdir, "rt")) == 0) {
-      sprintf(wdir,"%s%s/date",dirname,name);
-      if ((fp_in = fopen(wdir, "rt")) == 0) {
-         data[0]='\0';
-         return;
-      }
-   }
+\retval 0 No error.
+\retval -1 File not found.
+*/
+int obtdate(const char *dirname, const char *name, char *data)
+{
+       FILE *fp_in;
+       char wdir[MAXLEN];
 
-   if (!fgets(data,80,fp_in)) {
-      debuga(_("Failed to read the date in %s\n"),wdir);
-      exit(EXIT_FAILURE);
-   }
-   fclose(fp_in);
-   fixendofline(data);
+       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) {
+               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);
+               }
+       }
 
-   return;
+       if (!fgets(data,80,fp_in)) {
+               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);
+       }
+       fixendofline(data);
 
+       return(0);
 }
 
 
 void formatdate(char *date,int date_size,int year,int month,int day,int hour,int minute,int second,int dst)
 {
-   struct tm ltm;
-   time_t unixtime;
-   struct tm *fulltm;
+       struct tm ltm;
+       time_t unixtime;
+       struct tm *fulltm;
 
-   memset(&ltm,0,sizeof(ltm));
-   if (year>=1900) ltm.tm_year=year-1900;
-   if (month>=1 && month<=12) ltm.tm_mon=month-1;
-   if (day>=1 && day<=31) ltm.tm_mday=day;
-   if (hour>=0 && hour<24) ltm.tm_hour=hour;
-   if (minute>=0 && minute<60) ltm.tm_min=minute;
-   if (second>=0 && second<60) ltm.tm_sec=second;
-   ltm.tm_isdst=dst;
-   unixtime=mktime(&ltm); //fill the missing entries
-   fulltm=localtime(&unixtime);
-   //strftime(date,date_size,"%a %b %d %H:%M:%S %Z %Y",fulltm);
-   strftime(date,date_size,"%c",fulltm);
+       memset(&ltm,0,sizeof(ltm));
+       if (year>=1900) ltm.tm_year=year-1900;
+       if (month>=1 && month<=12) ltm.tm_mon=month-1;
+       if (day>=1 && day<=31) ltm.tm_mday=day;
+       if (hour>=0 && hour<24) ltm.tm_hour=hour;
+       if (minute>=0 && minute<60) ltm.tm_min=minute;
+       if (second>=0 && second<60) ltm.tm_sec=second;
+       ltm.tm_isdst=dst;
+       unixtime=mktime(&ltm); //fill the missing entries
+       fulltm=localtime(&unixtime);
+       //strftime(date,date_size,"%a %b %d %H:%M:%S %Z %Y",fulltm);
+       strftime(date,date_size,"%c",fulltm);
 }
 
 
 void computedate(int year,int month,int day,struct tm *t)
 {
-   memset(t,0,sizeof(*t));
-   t->tm_year=year-1900;
-   t->tm_mon=month-1;
-   t->tm_mday=day;
+       memset(t,0,sizeof(*t));
+       t->tm_year=year-1900;
+       t->tm_mon=month-1;
+       t->tm_mday=day;
 }
 
 
 int obtuser(const char *dirname, const char *name)
 {
-
-   FILE *fp_in;
-   char wdir[MAXLEN];
-   char tuser[20];
-   int nuser;
-
-   sprintf(wdir,"%s%s/sarg-users",dirname,name);
-   if((fp_in=fopen(wdir,"r"))==NULL) {
-      sprintf(wdir,"%s%s/users",dirname,name);
-      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);
-      exit(EXIT_FAILURE);
-   }
-   fclose(fp_in);
-   nuser=atoi(tuser);
-
-   return(nuser);
+       FILE *fp_in;
+       char wdir[MAXLEN];
+       char tuser[20];
+       int nuser;
+
+       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) {
+               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(__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);
+       }
+       nuser=atoi(tuser);
+
+       return(nuser);
 }
 
 
 void obttotal(const char *dirname, const char *name, int nuser, long long int *tbytes, long long int *media)
 {
-   FILE *fp_in;
-   char *buf;
-   char wdir[MAXLEN];
-   char user[MAX_USER_LEN];
-   char sep;
-   struct getwordstruct gwarea;
-   longline line;
-
-   *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) {
-         return;
-      }
-   }
-
-   if ((line=longline_create())==NULL) {
-      debuga(_("Not enough memory to read the file %s\n"),wdir);
-      exit(EXIT_FAILURE);
-   }
-
-   while((buf=longline_read(fp_in,line))!=NULL) {
-      if (strncmp(buf,"TOTAL\t",6) == 0)
-         sep='\t'; //new file
-      else if (strncmp(buf,"TOTAL ",6) == 0)
-         sep=' '; //old file
-      else
-         continue;
-      getword_start(&gwarea,buf);
-      if (getword(user,sizeof(user),&gwarea,sep)<0) {
-         debuga(_("There is a 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);
-         exit(EXIT_FAILURE);
-      }
-      if (getword_atoll(tbytes,&gwarea,sep)<0) {
-         debuga(_("There is a broken number of bytes in file %s\n"),wdir);
-         exit(EXIT_FAILURE);
-      }
-      break;
-   }
-   fclose(fp_in);
-   longline_destroy(&line);
-
-   if (nuser <= 0)
-      return;
-
-   *media=*tbytes / nuser;
-   return;
+       FileObject *fp_in;
+       char *buf;
+       char wdir[MAXLEN];
+       char user[MAX_USER_LEN];
+       char sep;
+       struct getwordstruct gwarea;
+       longline line;
+
+       *tbytes=0;
+       *media=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(__FILE__,__LINE__,_("Not enough memory to read file \"%s\"\n"),wdir);
+               exit(EXIT_FAILURE);
+       }
+
+       while((buf=longline_read(fp_in,line))!=NULL) {
+               if (strncmp(buf,"TOTAL\t",6) == 0)
+                       sep='\t'; //new file
+               else if (strncmp(buf,"TOTAL ",6) == 0)
+                       sep=' '; //old file
+               else
+                       continue;
+               getword_start(&gwarea,buf);
+               if (getword(user,sizeof(user),&gwarea,sep)<0) {
+                       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(__FILE__,__LINE__,_("Invalid total number of accesses in file \"%s\"\n"),wdir);
+                       exit(EXIT_FAILURE);
+               }
+               if (getword_atoll(tbytes,&gwarea,sep)<0) {
+                       debuga(__FILE__,__LINE__,_("Invalid number of bytes in file \"%s\"\n"),wdir);
+                       exit(EXIT_FAILURE);
+               }
+               break;
+       }
+       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)
+               return;
+
+       *media=*tbytes / nuser;
+       return;
 }
 
 int getperiod_fromsarglog(const char *arqtt,struct periodstruct *period)
 {
-   const char *str;
-   int day0, month0, year0, hour0, minute0;
-   int day1, month1, year1, hour1, minute1;
-   char month[4];
-   int i;
-
-   memset(period,0,sizeof(*period));
-
-   str=arqtt;
-   while((str=strstr(str,"sarg-"))!=NULL) {
-      str+=5;
-      if (!isdigit(str[0]) || !isdigit(str[1])) continue;
-      day0=(str[0]-'0')*10+(str[1]-'0');
-      str+=2;
-      strncpy(month,str,3);
-      month[3]=0;
-      month0=month2num(month);
-      if (month0>=12) continue;
-      str+=3;
-      year0=0;
-      for (i=0 ; isdigit(str[i]) && i<4 ; i++) year0=year0*10+(str[i]-'0');
-      if (i!=4) continue;
-      str+=4;
-      if (str[0]!='_') continue;
-      str++;
-
-      if (!isdigit(str[0]) || !isdigit(str[1])) continue;
-      hour0=(str[0]-'0')*10+(str[1]-'0');
-      str+=2;
-      if (!isdigit(str[0]) || !isdigit(str[1])) continue;
-      minute0=(str[0]-'0')*10+(str[1]-'0');
-      str+=2;
-
-      if (*str != '-') continue;
-      str++;
-
-      if (!isdigit(str[0]) || !isdigit(str[1])) continue;
-      day1=(str[0]-'0')*10+(str[1]-'0');
-      str+=2;
-      strncpy(month,str,3);
-      month[3]=0;
-      month1=month2num(month);
-      if (month1>=12) continue;
-      str+=3;
-      year1=0;
-      for (i=0 ; isdigit(str[i]) && i<4 ; i++) year1=year1*10+(str[i]-'0');
-      if (i!=4) continue;
-      str+=4;
-
-      if (str[0]!='_') continue;
-      str++;
-
-      if (!isdigit(str[0]) || !isdigit(str[1])) continue;
-      hour1=(str[0]-'0')*10+(str[1]-'0');
-      str+=2;
-      if (!isdigit(str[0]) || !isdigit(str[1])) continue;
-      minute1=(str[0]-'0')*10+(str[1]-'0');
-      str+=2;
-
-      period->start.tm_mday=day0;
-      period->start.tm_mon=month0;
-      period->start.tm_year=year0-1900;
-      period->start.tm_hour=hour0;
-      period->start.tm_min=minute0;
-      period->end.tm_mday=day1;
-      period->end.tm_mon=month1;
-      period->end.tm_year=year1-1900;
-      period->end.tm_hour=hour1;
-      period->end.tm_min=minute1;
-      return(0);
-   }
-   return(-1);
+       const char *str;
+       int day0, month0, year0, hour0, minute0;
+       int day1, month1, year1, hour1, minute1;
+       int i;
+
+       memset(period,0,sizeof(*period));
+
+       str=arqtt;
+       while((str=strstr(str,"sarg-"))!=NULL) {
+               str+=5;
+               if (!isdigit(str[0]) || !isdigit(str[1])) continue;
+               day0=(str[0]-'0')*10+(str[1]-'0');
+               if (day0<1 || day0>31) continue;
+               str+=2;
+               month0=(str[0]-'0')*10+(str[1]-'0')-1;
+               if (month0<0 || month0>11) continue;
+               str+=2;
+               year0=0;
+               for (i=0 ; isdigit(str[i]) && i<4 ; i++) year0=year0*10+(str[i]-'0');
+               if (i!=4 || year0<1900) continue;
+               str+=4;
+               if (str[0]!='_') continue;
+               str++;
+
+               if (!isdigit(str[0]) || !isdigit(str[1])) continue;
+               hour0=(str[0]-'0')*10+(str[1]-'0');
+               str+=2;
+               if (!isdigit(str[0]) || !isdigit(str[1])) continue;
+               minute0=(str[0]-'0')*10+(str[1]-'0');
+               str+=2;
+
+               if (*str != '-') continue;
+               str++;
+
+               if (!isdigit(str[0]) || !isdigit(str[1])) continue;
+               day1=(str[0]-'0')*10+(str[1]-'0');
+               if (day1<1 || day1>31) continue;
+               str+=2;
+               month1=(str[0]-'0')*10+(str[1]-'0')-1;
+               if (month1<0 || month1>11) continue;
+               str+=2;
+               year1=0;
+               for (i=0 ; isdigit(str[i]) && i<4 ; i++) year1=year1*10+(str[i]-'0');
+               if (i!=4 || year1<1900) continue;
+               str+=4;
+
+               if (str[0]!='_') continue;
+               str++;
+
+               if (!isdigit(str[0]) || !isdigit(str[1])) continue;
+               hour1=(str[0]-'0')*10+(str[1]-'0');
+               str+=2;
+               if (!isdigit(str[0]) || !isdigit(str[1])) continue;
+               minute1=(str[0]-'0')*10+(str[1]-'0');
+               str+=2;
+
+               period->start.tm_mday=day0;
+               period->start.tm_mon=month0;
+               period->start.tm_year=year0-1900;
+               period->start.tm_hour=hour0;
+               period->start.tm_min=minute0;
+               period->end.tm_mday=day1;
+               period->end.tm_mon=month1;
+               period->end.tm_year=year1-1900;
+               period->end.tm_hour=hour1;
+               period->end.tm_min=minute1;
+               return(0);
+       }
+       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));
+       period->start.tm_mday=dfrom%100;
+       period->start.tm_mon=(dfrom/100)%100-1;
+       period->start.tm_year=(dfrom/10000)-1900;
+
+       memset(&period->end,0,sizeof(period->end));
+       period->end.tm_mday=duntil%100;
+       period->end.tm_mon=(duntil/100)%100-1;
+       period->end.tm_year=(duntil/10000)-1900;
 }
 
-void getperiod_fromrange(struct periodstruct *period,int dfrom,int duntil)
+/*!
+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)
 {
-   memset(&period->start,0,sizeof(period->start));
-   period->start.tm_mday=dfrom%100;
-   period->start.tm_mon=(dfrom/100)%100-1;
-   period->start.tm_year=(dfrom/10000)-1900;
+       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;
+}
 
-   memset(&period->end,0,sizeof(period->end));
-   period->end.tm_mday=duntil%100;
-   period->end.tm_mon=(duntil/100)%100-1;
-   period->end.tm_year=(duntil/10000)-1900;
+/*!
+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') {
-      i=strftime(text1, sizeof(text1), "%Y %b %d", &period->start);
-   }else if(df[0]=='e') {
-      i=strftime(text1, sizeof(text1), "%d %b %Y", &period->start);
-   } else /*if(df[0]=='w')*/ {
-      IndexTree=INDEX_TREE_FILE;
-      i=strftime(text1, sizeof(text1), "%Y.%U", &period->start);
-   }
-   if (i == 0) return(-1);
-
-   range=(period->start.tm_year!=period->end.tm_year ||
-       period->start.tm_mon!=period->end.tm_mon ||
-       period->start.tm_mday!=period->end.tm_mday);
-   if (range) {
-      if(df[0]=='u') {
-         i=strftime(text2, sizeof(text2)-i, "%Y %b %d", &period->end);
-      } else if(df[0]=='e') {
-         i=strftime(text2, sizeof(text2)-i, "%d %b %Y", &period->end);
-      } else {
-         i=strftime(text2, sizeof(text2)-i, "%Y.%U", &period->end);
-      }
-      if (i == 0) return(-1);
-   }
-
-   if (range) {
-      snprintf(period->text,sizeof(period->text),"%s-%s",text1,text2);
-      snprintf(period->html,sizeof(period->html),"%s&mdash;%s",text1,text2);
-   } else {
-      strncpy(period->text,text1,sizeof(period->text)-1);
-      period->text[sizeof(period->text)-1]='\0';
-      strncpy(period->html,text1,sizeof(period->html)-1);
-      period->html[sizeof(period->html)-1]='\0';
-   }
-   return(0);
+       int i;
+       int range;
+       char text1[40], text2[40];
+
+       if (df=='u') {
+               i=strftime(text1, sizeof(text1), "%Y %b %d", &period->start);
+       } else if(df=='e') {
+               i=strftime(text1, sizeof(text1), "%d %b %Y", &period->start);
+       } else /*if (df=='w')*/ {
+               IndexTree=INDEX_TREE_FILE;
+               i=strftime(text1, sizeof(text1), "%Y.%U", &period->start);
+       }
+       if (i == 0) return(-1);
+
+       range=(period->start.tm_year!=period->end.tm_year ||
+              period->start.tm_mon!=period->end.tm_mon ||
+              period->start.tm_mday!=period->end.tm_mday);
+       if (range) {
+               if (df=='u') {
+                       i=strftime(text2, sizeof(text2)-i, "%Y %b %d", &period->end);
+               } 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);
+               }
+               if (i == 0) return(-1);
+       }
+
+       if (range) {
+               snprintf(period->text,sizeof(period->text),"%s-%s",text1,text2);
+               snprintf(period->html,sizeof(period->html),"%s&mdash;%s",text1,text2);
+       } else {
+               safe_strcpy(period->text,text1,sizeof(period->text));
+               safe_strcpy(period->html,text1,sizeof(period->html));
+       }
+       return(0);
 }
 
 static void copy_images(void)
 {
-   FILE *img_in, *img_ou;
-   char images[512];
-   char imgdir[MAXLEN];
-   char srcfile[MAXLEN];
-   char dstfile[MAXLEN];
-   DIR *dirp;
-   struct dirent *direntp;
-   char buffer[MAXLEN];
-   size_t nread;
-   struct stat info;
-
-   if (snprintf(images,sizeof(images),"%simages",outdir)>=sizeof(images)) {
-      debuga(_("Cannot copy images to target directory %simages\n"),outdir);
-      exit(EXIT_FAILURE);
-   }
-   if (access(images,R_OK)!=0) {
-      mkdir(images,0755);
-   }
-
-   strcpy(imgdir,IMAGEDIR);
-   dirp = opendir(imgdir);
-   if(dirp==NULL) {
-      debuga(_("(util) Can't open directory %s: %s\n"),imgdir,strerror(errno));
-      return;
-   }
-   while ((direntp = readdir( dirp )) != NULL ){
-      if(direntp->d_name[0]=='.')
-         continue;
-      sprintf(srcfile,"%s/%s",imgdir,direntp->d_name);
-      if (stat(srcfile,&info)) {
-         debuga(_("Cannot stat \"%s\" - %s\n"),srcfile,strerror(errno));
-         continue;
-      }
-      if (S_ISREG(info.st_mode)) {
-         sprintf(dstfile,"%s/%s",images,direntp->d_name);
-         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);
-                     break;
-                  }
-               }
-               fclose(img_ou);
-            } 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);
-         } else
-            fprintf(stderr,"SARG: (util): %s %s: %s\n", _("Cannot open file")?_("Cannot open file"):"Can't open file", srcfile, strerror(errno));
-      }
-   }
-   (void) closedir(dirp);
-
-   return;
-}
-
-int vrfydir(const struct periodstruct *per1, const char *addr, const char *site, const char *us, const char *form)
-{
-   FILE *fp_ou;
-   int  num=1, count=0;
-   char wdir[MAXLEN];
-   char dirname2[MAXLEN];
-   int y1, y2;
-   int m1, m2;
-   int d1, d2;
-   int wlen, wlen2;
-   time_t curtime;
-   struct tm *loctm;
-
-   strcpy(wdir,outdir);
-   wlen=strlen(wdir);
-   y1=per1->start.tm_year+1900;
-   y2=per1->end.tm_year+1900;
-   m1=per1->start.tm_mon+1;
-   m2=per1->end.tm_mon+1;
-   d1=per1->start.tm_mday;
-   d2=per1->end.tm_mday;
-   if(IndexTree == INDEX_TREE_DATE) {
-      wlen+=sprintf(wdir+wlen,"%04d",y1);
-      if(y1!=y2) wlen+=sprintf(wdir+wlen,"-%04d",y2);
-      if(access(wdir, R_OK) != 0)
-         my_mkdir(wdir);
-
-      wlen+=sprintf(wdir+wlen,"/%02d",m1);
-      if(m1 != m2) wlen+=sprintf(wdir+wlen,"-%02d",m2);
-      if(access(wdir, R_OK) != 0)
-         my_mkdir(wdir);
-
-      wlen+=sprintf(wdir+wlen,"/%02d",d1);
-      if(d1!=d2) wlen+=sprintf(wdir+wlen,"-%02d",d2);
-   } else {
-      if(df[0] == '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') {
-         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') {
-         wlen2=strftime(wdir+wlen, sizeof(wdir)-wlen, "%Y.%U", &per1->start);
-         if (wlen2==0) return(-1);
-         wlen+=wlen2;
-      }
-   }
-
-   if(us[0] != '\0') {
-      struct userinfostruct *uinfo=userinfo_find_from_id(us);
-      if (uinfo) {
-         strcat(wdir,"-");
-         strcat(wdir,uinfo->filename);
-      }
-   }
-   if(addr[0] != '\0') {
-      strcat(wdir,"-");
-      strcat(wdir,addr);
-   }
-   if(site[0] != '\0') {
-      strcat(wdir,"-");
-      strcat(wdir,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;
-         }
-
-         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);
-         }
-      }
-      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(wdir, R_OK) != 0)
-         my_mkdir(wdir);
-   }
-
-   strcpy(dirname2,wdir);
-
-   sprintf(wdir,"%s/sarg-date",outdirname);
-   if ((fp_ou = fopen(wdir, "wt")) == 0) {
-      debuga(_("cannot open %s for writing\n"),wdir);
-      perror("SARG:");
-      exit(EXIT_FAILURE);
-   }
-   time(&curtime);
-   //strftime(wdir,sizeof(wdir),"%a %b %d %H:%M:%S %Z %Y",localtime(&curtime));
-   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);
-      perror("SARG:");
-      exit(EXIT_FAILURE);
-   }
-   if (fclose(fp_ou)==EOF) {
-      debuga(_("Failed to write the date in %s\n"),wdir);
-      perror("SARG:");
-      exit(EXIT_FAILURE);
-   }
-
-   copy_images();
-   return(0);
+       FILE *img_in, *img_ou;
+       char images[512];
+       char srcfile[MAXLEN];
+       char dstfile[MAXLEN];
+       DIR *dirp;
+       struct dirent *direntp;
+       char buffer[MAXLEN];
+       size_t nread;
+       struct stat info;
+
+       if (snprintf(images,sizeof(images),"%simages",outdir)>=sizeof(images)) {
+               debuga(__FILE__,__LINE__,_("Cannot copy images to target directory %simages\n"),outdir);
+               exit(EXIT_FAILURE);
+       }
+       if (access(images,R_OK)!=0) {
+               if (PortableMkDir(images,0755)) {
+                       debuga(__FILE__,__LINE__,_("Cannot create directory \"%s\": %s\n"),images,strerror(errno));
+                       exit(EXIT_FAILURE);
+               }
+       }
+
+       dirp = opendir(ImageDir);
+       if(dirp==NULL) {
+               debuga(__FILE__,__LINE__,_("Cannot open directory \"%s\": %s\n"),ImageDir,strerror(errno));
+               return;
+       }
+       while ((direntp = readdir( dirp )) != NULL ){
+               if(direntp->d_name[0]=='.')
+                       continue;
+               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(__FILE__,__LINE__,_("Cannot stat \"%s\": %s\n"),srcfile,strerror(errno));
+                       continue;
+               }
+               if (S_ISREG(info.st_mode)) {
+                       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(__FILE__,__LINE__,_("Failed to copy image %s to %s\n"),srcfile,dstfile);
+                                                       break;
+                                               }
+                                       }
+                                       if (fclose(img_ou)==EOF) {
+                                               debuga(__FILE__,__LINE__,_("Write error in \"%s\": %s\n"),dstfile,strerror(errno));
+                                               exit(EXIT_FAILURE);
+                                       }
+                               } else
+                                       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
+                               debuga(__FILE__,__LINE__,_("Cannot open file \"%s\": %s\n"), srcfile, strerror(errno));
+               }
+       }
+       (void) closedir(dirp);
+
+       return;
+}
+
+/*!
+ * 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;
+       char wdir[MAXLEN];
+       int y1, y2;
+       int m1, m2;
+       int d1, d2;
+       int wlen, wlen2;
+       time_t curtime;
+       struct tm *loctm;
+
+       strcpy(wdir,outdir);
+       wlen=strlen(wdir);
+       y1=per1->start.tm_year+1900;
+       y2=per1->end.tm_year+1900;
+       m1=per1->start.tm_mon+1;
+       m2=per1->end.tm_mon+1;
+       d1=per1->start.tm_mday;
+       d2=per1->end.tm_mday;
+       if(IndexTree == INDEX_TREE_DATE) {
+               wlen+=sprintf(wdir+wlen,"%04d",y1);
+               if(y1!=y2) wlen+=sprintf(wdir+wlen,"-%04d",y2);
+               if(access(wdir, R_OK) != 0)
+                       my_mkdir(wdir);
+
+               wlen+=sprintf(wdir+wlen,"/%02d",m1);
+               if(m1 != m2) wlen+=sprintf(wdir+wlen,"-%02d",m2);
+               if(access(wdir, R_OK) != 0)
+                       my_mkdir(wdir);
+
+               wlen+=sprintf(wdir+wlen,"/%02d",d1);
+               if(d1!=d2) wlen+=sprintf(wdir+wlen,"-%02d",d2);
+       } else {
+               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 == '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 == 'w') {
+                       wlen2=strftime(wdir+wlen, sizeof(wdir)-wlen, "%Y.%U", &per1->start);
+                       if (wlen2==0) return(-1);
+                       wlen+=wlen2;
+               }
+       }
+
+       if(us[0] != '\0') {
+               struct userinfostruct *uinfo=userinfo_find_from_id(us);
+               if (uinfo) {
+                       strcat(wdir,"-");
+                       strcat(wdir,uinfo->filename);
+               }
+       }
+       if(addr[0] != '\0') {
+               strcat(wdir,"-");
+               strcat(wdir,addr);
+       }
+       if(site[0] != '\0') {
+               strcat(wdir,"-");
+               strcat(wdir,site);
+       }
+
+       strcpy(outdirname,wdir);
+
+       // 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;
+
+               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);
+               }
+       } else {
+               if(access(outdirname,R_OK) == 0) {
+                       unlinkdir(outdirname,1);
+               }
+       }
+       my_mkdir(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(__FILE__,__LINE__,_("Cannot open file \"%s\": %s\n"),wdir,strerror(errno));
+               perror("SARG:");
+               exit(EXIT_FAILURE);
+       }
+       time(&curtime);
+       //strftime(wdir,sizeof(wdir),"%a %b %d %H:%M:%S %Z %Y",localtime(&curtime));
+       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(__FILE__,__LINE__,_("Failed to write the date in %s\n"),wdir);
+               perror("SARG:");
+               exit(EXIT_FAILURE);
+       }
+       if (fclose(fp_ou)==EOF) {
+               debuga(__FILE__,__LINE__,_("Write error in \"%s\": %s\n"),wdir,strerror(errno));
+               exit(EXIT_FAILURE);
+       }
+
+       copy_images();
+       return(0);
+}
+
+/*!
+  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
+  if the length is negative or zero.
+*/
+void safe_strcpy(char *dest,const char *src,int length)
+{
+       if (length<=0) {
+               debuga(__FILE__,__LINE__,_("Invalid buffer length passed to the function to safely copy a string\n"));
+               exit(EXIT_FAILURE);
+       }
+       strncpy(dest,src,length-1);
+       dest[length-1]='\0';
 }
 
 void strip_latin(char *line)
 {
-   int i,j;
-   int skip;
-
-   j=0;
-   skip=0;
-   for (i=0;line[i];i++){
-      if (skip){
-         if (line[i]==';') skip=0;
-      } else {
-         if (line[i]=='&')
-            skip=1;
-         else
-            line[j++]=line[i];
-      }
-   }
-   line[j]='\0';
-   return;
+       int i,j;
+       int skip;
 
+       j=0;
+       skip=0;
+       for (i=0;line[i];i++){
+               if (skip){
+                       if (line[i]==';') skip=0;
+               } else {
+                       if (line[i]=='&')
+                               skip=1;
+                       else
+                               line[j++]=line[i];
+               }
+       }
+       line[j]='\0';
+       return;
 }
 
-void zdate(char *ftime,int ftimesize, const char *DateFormat)
+void zdate(char *ftime,int ftimesize, char DateFormat)
 {
+       time_t t;
+       struct tm *local;
 
-   time_t t;
-   struct tm *local;
-
-   t = time(NULL);
-   local = localtime(&t);
-   if(strcmp(DateFormat,"u") == 0)
-      strftime(ftime, ftimesize, "%b/%d/%Y %H:%M", local);
-   if(strcmp(DateFormat,"e") == 0)
-      strftime(ftime, ftimesize, "%d/%b/%Y-%H:%M", local);
-   if(strcmp(DateFormat,"w") == 0)
-      strftime(ftime, ftimesize, "%W-%H-%M", local);
-   return;
+       t = time(NULL);
+       local = localtime(&t);
+       if (DateFormat=='u')
+               strftime(ftime, ftimesize, "%b/%d/%Y %H:%M", local);
+       else if (DateFormat=='e')
+               strftime(ftime, ftimesize, "%d/%b/%Y-%H:%M", local);
+       else if (DateFormat=='w')
+               strftime(ftime, ftimesize, "%W-%H-%M", local);
+       return;
 }
 
 
 char *fixtime(long long int elap)
 {
-   int num = elap / 1000;
-   int hor = 0;
-   int min = 0;
-   int sec = 0;
-   static char buf[12];
-
-   hor=num / 3600;
-   min=(num % 3600) / 60;
-   sec=num % 60;
-
-   if(hor==0 && min==0 && sec==0)
-      strcpy(buf,"0");
-   else
-      sprintf(buf,"%d:%02d:%02d",hor,min,sec);
-
-   return buf;
-}
-
-
-void date_from(char *date, int *dfrom, int *duntil)
-{
-   int d0=0;
-   int m0=0;
-   int y0=0;
-   int d1=0;
-   int m1=0;
-   int y1=0;
-
-   if (isdigit(date[0])) {
-      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"));
-         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"));
-            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"));
-         exit(EXIT_FAILURE);
-      } else {
-         d1=d0;
-         m1=m0;
-         y1=y0;
-      }
-   } else {
-      int i;
-      time_t Today,t1;
-      struct tm *Date0,Date1;
-
-      if (time(&Today)==(time_t)-1) {
-         debuga(_("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"));
-            exit(EXIT_FAILURE);
-         }
-         Today-=i*24*60*60;
-         Date0=localtime(&Today);
-         if (Date0==NULL) {
-            debuga(_("Cannot convert local time: %s\n"),strerror(errno));
-            exit(EXIT_FAILURE);
-         }
-         y0=y1=Date0->tm_year+1900;
-         m0=m1=Date0->tm_mon+1;
-         d0=d1=Date0->tm_mday;
-      } else if (sscanf(date,"week-%d",&i)==1) {
-         /*
-         There is no portable way to find the first day of the week even though the
-         information is available in the locale. nl_langinfo has the unofficial
-         parameters _NL_TIME_FIRST_WEEKDAY and _NL_TIME_WEEK_1STDAY but they are
-         undocumented as is their return value and it is discouraged to use them.
-         Beside, nl_langinfo isn't available on windows and the first day of the
-         week isn't available at all on that system.
-         */
-         const int FirstWeekDay=1;
-         time_t WeekBegin;
-
-         if (i<0) {
-            debuga(_("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));
-            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));
-            exit(EXIT_FAILURE);
-         }
-         y0=Date0->tm_year+1900;
-         m0=Date0->tm_mon+1;
-         d0=Date0->tm_mday;
-         WeekBegin+=6*24*60*60;
-         Date0=localtime(&WeekBegin);
-         if (Date0==NULL) {
-            debuga(_("Cannot convert local time: %s\n"),strerror(errno));
-            exit(EXIT_FAILURE);
-         }
-         y1=Date0->tm_year+1900;
-         m1=Date0->tm_mon+1;
-         d1=Date0->tm_mday;
-      } else if (sscanf(date,"month-%d",&i)==1) {
-         if (i<0) {
-            debuga(_("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));
-            exit(EXIT_FAILURE);
-         }
-         if (Date0->tm_mon<i%12) {
-            y0=Date0->tm_year+1900-i/12-1;
-            m0=(Date0->tm_mon+12-i%12)%12+1;
-            d0=1;
-         } else {
-            y0=Date0->tm_year+1900-i/12;
-            m0=Date0->tm_mon-i%12+1;
-            d0=1;
-         }
-         memcpy(&Date1,Date0,sizeof(struct tm));
-         Date1.tm_isdst=-1;
-         Date1.tm_mday=1;
-         if (m0<12) {
-            Date1.tm_mon=m0;
-            Date1.tm_year=y0-1900;
-         } else {
-            Date1.tm_mon=0;
-            Date1.tm_year=y0-1900+1;
-         }
-         t1=mktime(&Date1);
-         t1-=24*60*60;
-         Date0=localtime(&t1);
-         y1=Date0->tm_year+1900;
-         m1=Date0->tm_mon+1;
-         d1=Date0->tm_mday;
-      } else {
-         debuga(_("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);
-   return;
+       long int num = elap / 1000LL;
+       int hor = 0;
+       int min = 0;
+       int sec = 0;
+       static char buf[20];
+
+       hor=num / 3600L;
+       min=(num % 3600L) / 60L;
+       sec=num % 60L;
+
+       if(hor==0 && min==0 && sec==0)
+               strcpy(buf,"0");
+       else
+               snprintf(buf,sizeof(buf),"%d:%02d:%02d",hor,min,sec);
+
+       return buf;
+}
+
+
+void date_from(char *date,int date_size, int *dfrom, int *duntil)
+{
+       int d0=0;
+       int m0=0;
+       int y0=0;
+       int d1=0;
+       int m1=0;
+       int y1=0;
+
+       if (isdigit(date[0])) {
+               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(__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(__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(__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;
+                       m1=m0;
+                       y1=y0;
+               }
+       } else {
+               int i;
+               time_t Today,t1;
+               struct tm *Date0,Date1;
+
+               if (time(&Today)==(time_t)-1) {
+                       debuga(__FILE__,__LINE__,_("Failed to get the current time\n"));
+                       exit(EXIT_FAILURE);
+               }
+               if (sscanf(date,"day-%d",&i)==1) {
+                       if (i<0) {
+                               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(__FILE__,__LINE__,_("Cannot convert local time: %s\n"),strerror(errno));
+                               exit(EXIT_FAILURE);
+                       }
+                       y0=y1=Date0->tm_year+1900;
+                       m0=m1=Date0->tm_mon+1;
+                       d0=d1=Date0->tm_mday;
+               } else if (sscanf(date,"week-%d",&i)==1) {
+                       /*
+                       There is no portable way to find the first day of the week even though the
+                       information is available in the locale. nl_langinfo has the unofficial
+                       parameters _NL_TIME_FIRST_WEEKDAY and _NL_TIME_WEEK_1STDAY but they are
+                       undocumented as is their return value and it is discouraged to use them.
+                       Beside, nl_langinfo isn't available on windows and the first day of the
+                       week isn't available at all on that system.
+                       */
+                       const int FirstWeekDay=1;
+                       time_t WeekBegin;
+
+                       if (i<0) {
+                               debuga(__FILE__,__LINE__,_("Invalid number of weeks in -d parameter\n"));
+                               exit(EXIT_FAILURE);
+                       }
+                       Date0=localtime(&Today);
+                       if (Date0==NULL) {
+                               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(__FILE__,__LINE__,_("Cannot convert local time: %s\n"),strerror(errno));
+                               exit(EXIT_FAILURE);
+                       }
+                       y0=Date0->tm_year+1900;
+                       m0=Date0->tm_mon+1;
+                       d0=Date0->tm_mday;
+                       WeekBegin+=6*24*60*60;
+                       Date0=localtime(&WeekBegin);
+                       if (Date0==NULL) {
+                               debuga(__FILE__,__LINE__,_("Cannot convert local time: %s\n"),strerror(errno));
+                               exit(EXIT_FAILURE);
+                       }
+                       y1=Date0->tm_year+1900;
+                       m1=Date0->tm_mon+1;
+                       d1=Date0->tm_mday;
+               } else if (sscanf(date,"month-%d",&i)==1) {
+                       if (i<0) {
+                               debuga(__FILE__,__LINE__,_("Invalid number of months in -d parameter\n"));
+                               exit(EXIT_FAILURE);
+                       }
+                       Date0=localtime(&Today);
+                       if (Date0==NULL) {
+                               debuga(__FILE__,__LINE__,_("Cannot convert local time: %s\n"),strerror(errno));
+                               exit(EXIT_FAILURE);
+                       }
+                       if (Date0->tm_mon<i%12) {
+                               y0=Date0->tm_year+1900-i/12-1;
+                               m0=(Date0->tm_mon+12-i%12)%12+1;
+                               d0=1;
+                       } else {
+                               y0=Date0->tm_year+1900-i/12;
+                               m0=Date0->tm_mon-i%12+1;
+                               d0=1;
+                       }
+                       memcpy(&Date1,Date0,sizeof(struct tm));
+                       Date1.tm_isdst=-1;
+                       Date1.tm_mday=1;
+                       if (m0<12) {
+                               Date1.tm_mon=m0;
+                               Date1.tm_year=y0-1900;
+                       } else {
+                               Date1.tm_mon=0;
+                               Date1.tm_year=y0-1900+1;
+                       }
+                       t1=mktime(&Date1);
+                       t1-=24*60*60;
+                       Date0=localtime(&t1);
+                       y1=Date0->tm_year+1900;
+                       m1=Date0->tm_mon+1;
+                       d1=Date0->tm_mday;
+               } else {
+                       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;
+       snprintf(date,date_size,"%02d/%02d/%04d-%02d/%02d/%04d",d0,m0,y0,d1,m1,y1);
+       return;
 }
 
 
 char *strlow(char *string)
 {
-   char *s;
+       char *s;
 
-   if (string)
-   {
-      for (s = string; *s; ++s)
-         *s = tolower(*s);
-   }
+       if (string)
+       {
+               for (s = string; *s; ++s)
+                       *s = tolower(*s);
+       }
 
-   return string;
+       return string;
 }
 
 
@@ -1294,507 +1733,800 @@ char *strlow(char *string)
 
 char *strup(char *string)
 {
-   char *s;
+       char *s;
 
-   if (string)
-   {
-      for (s = string; *s; ++s)
-         *s = toupper(*s);
-   }
+       if (string)
+       {
+               for (s = string; *s; ++s)
+                       *s = toupper(*s);
+       }
 
-   return string;
+       return string;
 }
 
 
 void removetmp(const char *outdir)
 {
-   FILE *fp_in;
-   char warea[256];
-   char buf[MAXLEN];
-   long pos;
-
-   if(!RemoveTempFiles)
-      return;
-
-   if(debug) {
-      debuga(_("Purging temporary file sarg-general\n"));
-   }
-   if (snprintf(warea,sizeof(warea),"%s/sarg-general",outdir)>=sizeof(warea)) {
-      debuga(_("(removetmp) directory too long to remove %s/sarg-period\n"),outdir);
-      exit(EXIT_FAILURE);
-   }
-   if((fp_in=fopen(warea,"r+"))==NULL){
-      debuga(_("(removetmp) Cannot open file %s\n"),warea);
-      exit(EXIT_FAILURE);
-   }
-   while(fgets(buf,sizeof(buf),fp_in)!=NULL) {
-      if(strncmp(buf,"TOTAL",5) == 0 && (buf[6]=='\t' || buf[6]==' '))
-         break;
-   }
-   if (fseek(fp_in,0,SEEK_SET)==-1) {
-      debuga(_("Failed to rewind to the beginning of the file %s: %s\n"),warea,strerror(errno));
-      exit(EXIT_FAILURE);
-   }
-
-   if (fputs(buf,fp_in)==EOF) {
-      debuga(_("Failed to write the total line in %s - %s\n"),warea,strerror(errno));
-      exit(EXIT_FAILURE);
-   }
-   pos=ftell(fp_in);
-   if (pos>0 && ftruncate(fileno(fp_in),pos)==-1) {
-      debuga(_("Failed to truncate %s: %s\n"),warea,strerror(errno));
-      exit(EXIT_FAILURE);
-   }
-   if (fclose(fp_in)==EOF) {
-      debuga(_("Failed to close %s after writing the total line - %s\n"),warea,strerror(errno));
-      exit(EXIT_FAILURE);
-   }
-
-   return;
+       FILE *fp_gen;
+       char filename[256];
+
+       if(!RemoveTempFiles)
+               return;
+
+       if(debug) {
+               debuga(__FILE__,__LINE__,_("Purging temporary file sarg-general\n"));
+       }
+       if (snprintf(filename,sizeof(filename),"%s/sarg-general",outdir)>=sizeof(filename)) {
+               debuga(__FILE__,__LINE__,_("Path too long: "));
+               debuga_more("%s/sarg-period\n",outdir);
+               exit(EXIT_FAILURE);
+       }
+       if((fp_gen=fopen(filename,"w"))==NULL){
+               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(__FILE__,__LINE__,_("Write error in \"%s\": %s\n"),filename,strerror(errno));
+               exit(EXIT_FAILURE);
+       }
 }
 
 void load_excludecodes(const char *ExcludeCodes)
 {
-
-   FILE *fp_in;
-   char data[80];
-   int i;
-   int Stored;
-   long int MemSize;
-
-   if(ExcludeCodes[0] == '\0')
-      return;
-
-   if((fp_in=fopen(ExcludeCodes,"r"))==NULL) {
-      debuga(_("(util) Cannot open file %s (exclude_codes)\n"),ExcludeCodes);
-      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));
-      exit(EXIT_FAILURE);
-   }
-   MemSize = ftell(fp_in);
-   if (MemSize<0) {
-      debuga(_("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));
-      exit(EXIT_FAILURE);
-   }
-
-   MemSize+=1;
-   if((excludecode=(char *) malloc(MemSize))==NULL) {
-      debuga(_("malloc error (%ld)\n"),MemSize);
-      exit(EXIT_FAILURE);
-   }
-   memset(excludecode,0,MemSize);
-
-   Stored=0;
-   while(fgets(data,sizeof(data),fp_in)!=NULL) {
-      if (data[0]=='#') continue;
-      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);
-         break;
-      }
-      strcat(excludecode,data);
-      strcat(excludecode,";");
-      Stored+=i+1;
-   }
-
-   fclose(fp_in);
-   return;
+       FILE *fp_in;
+       char data[80];
+       int i;
+       int Stored;
+       long int MemSize;
+
+       if(ExcludeCodes[0] == '\0')
+               return;
+
+       if((fp_in=fopen(ExcludeCodes,"r"))==NULL) {
+               debuga(__FILE__,__LINE__,_("Cannot open file \"%s\": %s\n"),ExcludeCodes,strerror(errno));
+               exit(EXIT_FAILURE);
+       }
+
+       if (fseek(fp_in, 0, SEEK_END)==-1) {
+               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(__FILE__,__LINE__,_("Cannot get the size of file \"%s\"\n"),ExcludeCodes);
+               exit(EXIT_FAILURE);
+       }
+       if (fseek(fp_in, 0, SEEK_SET)==-1) {
+               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(__FILE__,__LINE__,_("malloc error (%ld bytes required)\n"),MemSize);
+               exit(EXIT_FAILURE);
+       }
+       memset(excludecode,0,MemSize);
+
+       Stored=0;
+       while(fgets(data,sizeof(data),fp_in)!=NULL) {
+               if (data[0]=='#') continue;
+               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(__FILE__,__LINE__,_("Too many codes to exclude in file \"%s\"\n"),ExcludeCodes);
+                       break;
+               }
+               strcat(excludecode,data);
+               strcat(excludecode,";");
+               Stored+=i+1;
+       }
+
+       if (fclose(fp_in)==EOF) {
+               debuga(__FILE__,__LINE__,_("Read error in \"%s\": %s\n"),ExcludeCodes,strerror(errno));
+               exit(EXIT_FAILURE);
+       }
+       return;
 }
 
 void free_excludecodes(void)
 {
-   if (excludecode) {
-      free(excludecode);
-      excludecode=NULL;
-   }
+       if (excludecode) {
+               free(excludecode);
+               excludecode=NULL;
+       }
 }
 
 int vercode(const char *code)
 {
-   char *cod;
-   int clen;
+       char *cod;
+       int clen;
 
-   if (excludecode && excludecode[0]!='\0') {
-      clen=strlen(code);
-      cod=excludecode;
-      while (cod) {
-         if (strncmp(code,cod,clen)==0 && cod[clen]==';')
-            return 1;
-         cod=strchr(cod,';');
-         if (cod) cod++;
-      }
-   }
-   return 0;
+       if (excludecode && excludecode[0]!='\0') {
+               clen=strlen(code);
+               cod=excludecode;
+               while (cod) {
+                       if (strncmp(code,cod,clen)==0 && cod[clen]==';')
+                               return 1;
+                       cod=strchr(cod,';');
+                       if (cod) cod++;
+               }
+       }
+       return 0;
 }
 
 void fixnone(char *str)
 {
-   int i;
+       int i;
 
-   for (i=strlen(str)-1 ; i>=0 && (unsigned char)str[i]<=' ' ; i--);
-   if(i==3 && strncmp(str,"none",4) == 0)
-      str[0]='\0';
+       for (i=strlen(str)-1 ; i>=0 && (unsigned char)str[i]<=' ' ; i--);
+       if(i==3 && strncmp(str,"none",4) == 0)
+               str[0]='\0';
 
-   return;
+       return;
 }
 
 void fixendofline(char *str)
 {
-   int i;
+       int i;
 
-   for (i=strlen(str)-1 ; i>=0 && (unsigned char)str[i]<=' ' ; i--) str[i]=0;
+       for (i=strlen(str)-1 ; i>=0 && (unsigned char)str[i]<=' ' ; i--) str[i]=0;
 }
 
 #ifdef LEGACY_TESTVALIDUSERCHAR
 int testvaliduserchar(const char *user)
 {
+       int x=0;
+       int y=0;
 
-   int x=0;
-   int y=0;
-
-   for (y=0; y<strlen(UserInvalidChar); y++) {
-      for (x=0; x<strlen(user); x++) {
-         if(user[x] == UserInvalidChar[y])
-            return 1;
-      }
-   }
-   return 0;
+       for (y=0; y<strlen(UserInvalidChar); y++) {
+               for (x=0; x<strlen(user); x++) {
+                       if(user[x] == UserInvalidChar[y])
+                               return 1;
+               }
+       }
+       return 0;
 }
 #else
 int testvaliduserchar(const char *user)
 {
+       char * p_UserInvalidChar = UserInvalidChar ;
+       const char * p_user ;
 
-   char * p_UserInvalidChar = UserInvalidChar ;
-   const char * p_user ;
-
-   while( *p_UserInvalidChar ) {
-      p_user = user ;
-      while ( *p_user ) {
-         if( *p_UserInvalidChar == *p_user )
-            return 1;
-         p_user++ ;
-      }
-      p_UserInvalidChar++ ;
-   }
-   return 0;
+       while( *p_UserInvalidChar ) {
+               p_user = user ;
+               while ( *p_user ) {
+                       if( *p_UserInvalidChar == *p_user )
+                               return 1;
+                       p_user++ ;
+               }
+               p_UserInvalidChar++ ;
+       }
+       return 0;
 }
 #endif
 
 int compar( const void *a, const void *b )
-{ if( *(int *)a > *(int *)b ) return 1;
-  if( *(int *)a < *(int *)b ) return -1;
-  return 0;
+{
+       if( *(int *)a > *(int *)b ) return 1;
+       if( *(int *)a < *(int *)b ) return -1;
+       return 0;
 }
 
 int getnumlist( char *buf, numlist *list, const int len, const int maxvalue )
 {
-   int i, j, d, flag, r1, r2;
-   char *pbuf, **bp, *strbufs[ 24 ];
-
-   bp = strbufs;
-   strtok( buf, " \t" );
-   for( *bp = strtok( NULL, "," ), list->len = 0; *bp; *bp = strtok( NULL, "," ) ) {
-      if( ++bp >= &strbufs[ 24 ] )
-         break;
-      list->len++;
-   }
-   if( ! list->len )
-      return -1;
-   d = 0;
-   for( i = 0; i < list->len; i++ ) {
-      if( strchr( strbufs[ i ], '-' ) != 0 ) {
-         pbuf = strbufs[ i ];
-         strtok( pbuf, "-" );
-         pbuf = strtok( NULL, "\0" );
-         r1 = atoi( strbufs[ i ] );
-         if( ( r2 = atoi( pbuf ) ) >= maxvalue || r1 >= r2 )
-            return -1;
-         if( i + d + ( r2 - r1 ) + 1 <= len ) {
-            for( j = r1; j <= r2; j++ )
-               list->list[ i + d++ ] = j;
-            d--;
-         }
-      }
-      else
-         if( ( list->list[ i + d ] = atoi( strbufs[ i ] ) ) >= maxvalue )
-            return 1;
-   }
-   list->len += d;
-   qsort( list->list, list->len, sizeof( int ), compar );
-   do {
-      flag = 0;
-      for( i = 0; i < list->len - 1; i++ )
-         if( list->list[ i ] == list->list[ i + 1 ] ) {
-            for( j = i + 1; j < list->len; j++ )
-               list->list[ j - 1 ] = list->list[ j ];
-            list->len--;
-            flag = 1;
-            break;
-         }
-   } while( flag );
-   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);
+       int i, j, d, flag, r1, r2;
+       char *pbuf, **bp, *strbufs[ 24 ];
+
+       bp = strbufs;
+       strtok( buf, " \t" );
+       for( *bp = strtok( NULL, "," ), list->len = 0; *bp; *bp = strtok( NULL, "," ) ) {
+               if( ++bp >= &strbufs[ 24 ] )
+                       break;
+               list->len++;
+       }
+       if( ! list->len )
+               return -1;
+       d = 0;
+       for( i = 0; i < list->len; i++ ) {
+               if( strchr( strbufs[ i ], '-' ) != 0 ) {
+                       pbuf = strbufs[ i ];
+                       strtok( pbuf, "-" );
+                       pbuf = strtok( NULL, "\0" );
+                       r1 = atoi( strbufs[ i ] );
+                       if( ( r2 = atoi( pbuf ) ) >= maxvalue || r1 >= r2 )
+                               return -1;
+                       if( i + d + ( r2 - r1 ) + 1 <= len ) {
+                               for( j = r1; j <= r2; j++ )
+                                       list->list[ i + d++ ] = j;
+                               d--;
+                       }
+               }
+               else
+                       if( ( list->list[ i + d ] = atoi( strbufs[ i ] ) ) >= maxvalue )
+                               return 1;
+       }
+       list->len += d;
+       qsort( list->list, list->len, sizeof( int ), compar );
+       do {
+               flag = 0;
+               for( i = 0; i < list->len - 1; i++ )
+                       if( list->list[ i ] == list->list[ i + 1 ] ) {
+                               for( j = i + 1; j < list->len; j++ )
+                                       list->list[ j - 1 ] = list->list[ j ];
+                               list->len--;
+                               flag = 1;
+                               break;
+                       }
+       } while( flag );
+       return 0;
 }
 
 void show_info(FILE *fp_ou)
 {
-   char ftime[127];
+       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);
+       if(!ShowSargInfo) return;
+       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)
 {
-   int i;
+       int i;
 
-   if(!ShowSargLogo) return;
-   fputs("<div class=\"logo\"><a href=\"http://sarg.sourceforge.net\"><img src=\"",fp_ou);
-   for (i=0 ; i<depth ; i++)
-      fputs("../",fp_ou);
-   fputs("images/sarg.png\" title=\"SARG, Squid Analysis Report Generator. Logo by Osamu Matsuzaki\" alt=\"Sarg\"></a>&nbsp;Squid Analysis Report Generator</div>\n",fp_ou);
+       if(!ShowSargLogo) return;
+       fputs("<div class=\"logo\"><a href=\"http://sarg.sourceforge.net\"><img src=\"",fp_ou);
+       for (i=0 ; i<depth ; i++)
+               fputs("../",fp_ou);
+       fputs("images/sarg.png\" title=\"SARG, Squid Analysis Report Generator. Logo by Osamu Matsuzaki\" alt=\"Sarg\"></a>&nbsp;Squid Analysis Report Generator</div>\n",fp_ou);
 }
 
 void write_logo_image(FILE *fp_ou)
 {
-   if(LogoImage[0]!='\0')
-      fprintf(fp_ou, "<div class=\"logo\"><img src=\"%s\" width=\"%s\" height=\"%s\" alt=\"Logo\">&nbsp;%s</div>\n",LogoImage,Width,Height,LogoText);
+       if(LogoImage[0]!='\0')
+               fprintf(fp_ou, "<div class=\"logo\"><img src=\"%s\" width=\"%s\" height=\"%s\" alt=\"Logo\">&nbsp;%s</div>\n",LogoImage,Width,Height,LogoText);
 }
 
 void write_html_head(FILE *fp_ou, int depth, const char *page_title,int javascript)
 {
-   int i;
+       int i;
 
-   fputs("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n<html>\n",fp_ou);
-   fprintf(fp_ou, "<head>\n  <meta http-equiv=\"Content-Type\" content=\"text/html; charset=%s\">\n",CharSet);
-   if (page_title) fprintf(fp_ou,"<title>%s</title>\n",page_title);
-   css(fp_ou);
-   if ((javascript & HTML_JS_SORTTABLE)!=0 && SortTableJs[0]) {
-      fputs("<script type=\"text/javascript\" src=\"",fp_ou);
-      if (strncmp(SortTableJs,"../",3)==0) {
-         for (i=0 ; i<depth ; i++) fputs("../",fp_ou);
-      }
-      fputs(SortTableJs,fp_ou);
-      fputs("\"></script>\n",fp_ou);
-   }
-   fputs("</head>\n<body>\n",fp_ou);
+       fputs("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n<html>\n",fp_ou);
+       fprintf(fp_ou, "<head>\n  <meta http-equiv=\"Content-Type\" content=\"text/html; charset=%s\">\n",CharSet);
+       if (page_title) fprintf(fp_ou,"<title>%s</title>\n",page_title);
+       css(fp_ou);
+       if ((javascript & HTML_JS_SORTTABLE)!=0 && SortTableJs[0]) {
+               fputs("<script type=\"text/javascript\" src=\"",fp_ou);
+               if (strncmp(SortTableJs,"../",3)==0) {
+                       for (i=0 ; i<depth ; i++) fputs("../",fp_ou);
+               }
+               fputs(SortTableJs,fp_ou);
+               fputs("\"></script>\n",fp_ou);
+       }
+       fputs("</head>\n<body>\n",fp_ou);
 }
 
 void write_html_header(FILE *fp_ou, int depth, const char *page_title,int javascript)
 {
-   write_html_head(fp_ou,depth,page_title,javascript);
-   write_logo_image(fp_ou);
-   show_sarg(fp_ou, depth);
-   fprintf(fp_ou,"<div class=\"title\"><table cellpadding=\"0\" cellspacing=\"0\">\n<tr><th class=\"title_c\">%s</th></tr>\n",Title);
+       write_html_head(fp_ou,depth,page_title,javascript);
+       write_logo_image(fp_ou);
+       show_sarg(fp_ou, depth);
+       fprintf(fp_ou,"<div class=\"title\"><table cellpadding=\"0\" cellspacing=\"0\">\n<tr><th class=\"title_c\">%s</th></tr>\n",Title);
 }
 
 void close_html_header(FILE *fp_ou)
 {
-   fputs("</table></div>\n",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);
+       show_info(fp_ou);
+       fputs("</body>\n</html>\n",fp_ou);
 }
 
 void output_html_string(FILE *fp_ou,const char *str,int maxlen)
 {
-   int i=0;
-
-   while (*str && (maxlen<=0 || i<maxlen)) {
-      switch (*str) {
-         case '&':
-            fputs("&amp;",fp_ou);
-            break;
-         case '<':
-            fputs("&lt;",fp_ou);
-            break;
-         case '>':
-            fputs("&gt;",fp_ou);
-            break;
-         case '"':
-            fputs("&quot;",fp_ou);
-            break;
-         case '\'':
-            fputs("&#39;",fp_ou);
-            break;
-         default:
-            fputc(*str,fp_ou);
-      }
-      str++;
-      i++;
-   }
-   if (maxlen>0 && i>=maxlen)
-      fputs("&hellip;",fp_ou);
+       int i=0;
+
+       while (*str && (maxlen<=0 || i<maxlen)) {
+               switch (*str) {
+                       case '&':
+                               fputs("&amp;",fp_ou);
+                               break;
+                       case '<':
+                               fputs("&lt;",fp_ou);
+                               break;
+                       case '>':
+                               fputs("&gt;",fp_ou);
+                               break;
+                       case '"':
+                               fputs("&quot;",fp_ou);
+                               break;
+                       case '\'':
+                               fputs("&#39;",fp_ou);
+                               break;
+                       default:
+                               fputc(*str,fp_ou);
+               }
+               str++;
+               i++;
+       }
+       if (maxlen>0 && i>=maxlen)
+               fputs("&hellip;",fp_ou);
 }
 
 void output_html_url(FILE *fp_ou,const char *url)
 {
-   while (*url) {
-      if (*url=='&')
-         fputs("&amp;",fp_ou);
-      else
-         fputc(*url,fp_ou);
-      url++;
-   }
+       while (*url) {
+               if (*url=='&')
+                       fputs("&amp;",fp_ou);
+               else
+                       fputc(*url,fp_ou);
+               url++;
+       }
 }
 
-void url_hostname(const char *url,char *hostname,int hostsize)
-{
-   int i;
+/*!
+  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.
 
-   hostsize--;
-   for (i=0 ; i<hostsize && url[i] && url[i]!='/' ; i++)
-      hostname[i]=url[i];
-   hostname[i]='\0';
+  \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.
+ */
+void output_html_link(FILE *fp_ou,const char *url,int maxlen)
+{
+       if (url[0]==ALIAS_PREFIX) {
+               // this is an alias, no need for a A tag
+               output_html_string(fp_ou,url+1,100);
+       } else {
+               if (skip_scheme(url)==url)
+                       fputs("<a href=\"http://",fp_ou);//no scheme in the url, assume http:// to make the link clickable
+               else
+                       fputs("<a href=\"",fp_ou);//the scheme is in the url, no need to add one
+               output_html_url(fp_ou,url);
+               fputs("\">",fp_ou);
+               output_html_string(fp_ou,url,100);
+               fputs("</a>",fp_ou);
+       }
 }
 
 void url_module(const char *url, char *w2)
 {
-   int x, y;
-   char w[255];
-
-   y=0;
-   for(x=strlen(url)-1; x>=0; x--) {
-      if(url[x] == '/' || y>=sizeof(w)-1) break;
-      w[y++]=url[x];
-   }
-   if (x<0) {
-      w2[0]='\0';
-      return;
-   }
-
-   x=0;
-   for(y=y-1; y>=0; y--) {
-      w2[x++]=w[y];
-   }
-   w2[x]='\0';
-}
-
-void url_to_file(const char *url,char *file,int filesize)
-{
-   int i,skip;
-
-   filesize--;
-   skip=0;
-   for(i=0; i<filesize && *url; url++) {
-      if(isalnum(*url) || *url=='-' || *url=='_' || *url=='.' || *url=='%') {
-         file[i++]=*url;
-         skip=0;
-      } else {
-         if (!skip) file[i++]='_';
-         skip=1;
-      }
-   }
-   file[i]='\0';
+       int x, y;
+       char w[255];
+
+       y=0;
+       for(x=strlen(url)-1; x>=0; x--) {
+               if(url[x] == '/' || y>=sizeof(w)-1) break;
+               w[y++]=url[x];
+       }
+       if (x<0) {
+               w2[0]='\0';
+               return;
+       }
+
+       x=0;
+       for(y=y-1; y>=0; y--) {
+               w2[x++]=w[y];
+       }
+       w2[x]='\0';
+}
+
+/*!
+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,j;
+       bool skip;
+
+       // 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) anchor[--j]='_';
+                       skip=true;
+               }
+               i--;
+       }
+       if (j>0)
+       {
+               while ( anchor[j])
+               {
+                       *anchor=anchor[j];
+                       anchor++;
+               }
+               *anchor='\0';
+       }
 }
 
 void version(void)
 {
-   printf(_("SARG Version: %s\n"),VERSION);
-   exit(EXIT_SUCCESS);
+       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);
 }
 
 char *get_param_value(const char *param,char *line)
 {
-   int plen;
-
-   while (*line==' ' || *line=='\t') line++;
-   plen=strlen(param);
-   if (strncasecmp(line,param,plen)) return(NULL);
-   if (line[plen]!=' ' && line[plen]!='\t') return(NULL);
-   line+=plen;
-   while (*line==' ' || *line=='\t') line++;
-   return(line);
-}
-
-void unlinkdir(const char *dir,int contentonly)
-{
-   struct stat st;
-   DIR *dirp;
-   struct dirent *direntp;
-   char dname[MAXLEN];
-   int err;
-
-   dirp=opendir(dir);
-   if (!dirp) return;
-   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;
-      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);
-         exit(EXIT_FAILURE);
-      }
+       int plen;
+
+       while (*line==' ' || *line=='\t') line++;
+       plen=strlen(param);
+       if (strncasecmp(line,param,plen)) return(NULL);
+       if (line[plen]!=' ' && line[plen]!='\t') return(NULL);
+       line+=plen;
+       while (*line==' ' || *line=='\t') line++;
+       return(line);
+}
+
+void unlinkdir(const char *dir,bool contentonly)
+{
+       struct stat st;
+       DIR *dirp;
+       struct dirent *direntp;
+       char dname[MAXLEN];
+       int err;
+
+       dirp=opendir(dir);
+       if (!dirp) return;
+       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;
+               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);
+               err=lstat(dname,&st);
 #else
-      err=stat(dname,&st);
+               err=stat(dname,&st);
 #endif
-      if (err) {
-         debuga(_("cannot stat %s\n"),dname);
-         exit(EXIT_FAILURE);
-      }
-      if (S_ISREG(st.st_mode)) {
-         if (unlink(dname)) {
-            debuga(_("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);
-      }
-   }
-   closedir(dirp);
-
-   if (!contentonly) {
-      if (rmdir(dir)) {
-         debuga(_("cannot delete %s - %s\n"),dir,strerror(errno));
-         exit(EXIT_FAILURE);
-      }
-   }
-}
+               if (err) {
+                       debuga(__FILE__,__LINE__,_("Cannot stat \"%s\": %s\n"),dname,strerror(errno));
+                       exit(EXIT_FAILURE);
+               }
+               if (S_ISREG(st.st_mode)) {
+                       if (unlink(dname)) {
+                               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(__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(__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);
+}
+
+/*!
+  Extract an url, IPv4 or IPv6 from a buffer. The IP addresses may end with a
+  prefix size.
+
+  \param buf The buffer to parse.
+  \param text A pointer to set to the beginning of the string pattern. No terminating zero is inserted.
+              The pointer may be NULL.
+  \param ipv4 A 4 bytes buffer to store the bytes of the IPv4 address.
+  \param ipv6 A 8 short integers buffer to store the values of the IPv6 address.
+  \param nbits The number of prefix bits for an IP address.
+  \param next The content of the line after the extracted address.
+
+  \retval 3 The pattern is a IPv6 address.
+  \retval 2 The pattern is a IPv4 address.
+  \retval 1 The patter is a string.
+  \retval 0 Empty pattern.
+ */
+int extract_address_mask(const char *buf,const char **text,unsigned char *ipv4,unsigned short int *ipv6,int *nbits,const char **next)
+{
+       int i;
+       int j;
+       int ip_size;
+       unsigned int value4, value6;
+       unsigned short int addr[8];
+       int addr_len;
+       int nibble6_len;
+       int mask, max_mask;
+       int pad_pos;
+       int pad_len;
+       bool bracket=false;
+       bool port=false;
+       bool port_num=0;
+
+       // skip leading spaces and tabs
+       while (*buf && (*buf==' ' || *buf=='\t')) buf++;
+
+       // find out the nature of the pattern
+       ip_size=0x60  | 0x04;
+       if (*buf=='[') {
+               bracket=true;
+               ip_size=0x60;
+               buf++;
+       }
+       value4=0U;
+       value6=0U;
+       addr_len=0;
+       nibble6_len=0;
+       pad_pos=-1;
+       for (i=0 ; (unsigned char)buf[i]>' ' && buf[i]!='/' && buf[i]!='?' && (!bracket || buf[i]!=']') && ip_size ; i++) {
+               if (ip_size & 0x04) {
+                       if (isdigit(buf[i])) {
+                               if (port) {
+                                       port_num=port_num*10+(buf[i]-'0');
+                                       if (port_num>65535) ip_size&=~0x04;
+                               } else {
+                                       value4=value4*10+(buf[i]-'0');
+                                       if (value4>0xFFU) ip_size&=~0x04;
+                               }
+                       } else if (buf[i]=='.' && addr_len<4) {
+                               addr[addr_len++]=(unsigned short)(value4 & 0xFFU);
+                               value4=0U;
+                       } else if (!port && buf[i]==':') {
+                               port=true;
+                       } else {
+                               ip_size&=~0x04;
+                       }
+               }
+               if (ip_size & 0x60) {
+                       if (isdigit(buf[i])) {
+                               value6=(value6<<4)+(buf[i]-'0');
+                               nibble6_len++;
+                               if (value6>0xFFFFU) ip_size&=~0x60;
+                       } else if (toupper(buf[i])>='A' && toupper(buf[i])<='F') {
+                               value6=(value6<<4)+(toupper(buf[i])-'A'+10);
+                               nibble6_len++;
+                               if (value6>0xFFFFU) ip_size&=~0x60;
+                       } else if (buf[i]==':' && addr_len<8) {
+                               if (nibble6_len>0) {
+                                       addr[addr_len++]=(unsigned short)(value6 & 0xFFFFU);
+                                       nibble6_len=0;
+                               }
+                               value6=0U;
+                               if (buf[i+1]==':') {
+                                       pad_pos=addr_len;
+                                       i++;
+                               }
+                       } else {
+                               ip_size&=~0x60;
+                       }
+               }
+       }
+       if (i==0) return(0);
+       if (ip_size & 0x04) {
+               if (addr_len!=3)
+                       ip_size&=~0x04;
+               else
+                       addr[addr_len++]=(unsigned short)(value4 & 0xFFU);
+       }
+       if (ip_size & 0x60) {
+               if (pad_pos<0 && addr_len!=7) {
+                       ip_size&=~0x60;
+               } else if (pad_pos>=0 && addr_len>=7)
+                       ip_size&=~0x60;
+               else if (nibble6_len>0)
+                       addr[addr_len++]=(unsigned short)(value6 & 0xFFFFU);
+       }
+       if (!ip_size) {
+               if (text) {
+                       *text=buf;
+                       if (bracket) (*text)--;
+               }
+               while ((unsigned char)buf[i]>' ') i++;
+               if (next) *next=buf+i;
+               return(1);
+       }
+       max_mask=(ip_size & 0x04) ? 4*8 : 8*16;
+       if (buf[i]=='/') {
+               i++;
+               mask=atoi(buf+i);
+               while (isdigit(buf[i])) i++;
+               if (mask<0 || mask>max_mask) mask=max_mask;
+       } else
+               mask=max_mask;
+       if (ip_size & 0x60 && bracket && buf[i]==']') i++;
+       if (next) *next=buf+i;
+       if (ip_size & 0x04) {
+               if (nbits) *nbits=mask;
+               for (i=0 ; i<addr_len ; i++)
+                       ipv4[i]=(unsigned char)addr[i];
+               return(2);
+       }
+
+       // IPv6 address
+       if (nbits) *nbits=mask;
+       i=0;
+       j=0;
+       if (pad_pos>=0) {
+               while (i<pad_pos)
+                       ipv6[j++]=(unsigned short int)addr[i++];
+               pad_len=8-addr_len;
+               while (j<pad_pos+pad_len)
+                       ipv6[j++]=0;
+       }
+       while (i<addr_len)
+               ipv6[j++]=(unsigned short int)addr[i++];
+       return(3);
+}