]> git.ipfire.org Git - thirdparty/sarg.git/blobdiff - grepday.c
Add support to decompress xz files
[thirdparty/sarg.git] / grepday.c
index 0482dcfee860e129e3a6f0dacbfe08a96f2d4e91..8b409f1010dc1df0a3adbe164491cba075aeaf5d 100644 (file)
--- a/grepday.c
+++ b/grepday.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
 
 struct GraphDataStruct
 {
-#ifdef USE_ICONV
-   iconv_t localtoutf;
-#endif
-   int lavender;
-   int darkblue;
-   int dimgray;
-   int goldenrod;
-   int goldenrod2;
-   int gray;
-   int silver;
-   int black;
-   int color1;
-   int color2;
-   int color3;
-   gdImage *im;
-   char *string;
-   size_t string_size;
+       int lavender;
+       int darkblue;
+       int dimgray;
+       int goldenrod;
+       int goldenrod2;
+       int gray;
+       int silver;
+       int black;
+       //! The color of the top side of a graph bar.
+       int color1;
+       //! The color of the right side of a graph bar.
+       int color2;
+       //! The color of the front side of a graph bar.
+       int color3;
+       //! The libgd image we are drawing on.
+       gdImage *im;
+       //! An allocated buffer to convert the string into UTF-8.
+       char *string;
+       //! The number of bytes allocated for the string buffer.
+       size_t string_size;
+       //! The bottom border of the graph.
+       int BottomGraph;
+       //! The top border of the graph.
+       int TopGraph;
+       //! The left border of the graph.
+       int LeftGraph;
+       //! The right border of the graph.
+       int RightGraph;
+       //! The height at which the bottom depth border of the graph extends.
+       int BottomDepth;
+       //! The distance between two ticks on the horizontal axis.
+       double XScale;
+       //! The distance between two ticks on the vertical axis.
+       double YScale;
+       //! The exterior length of a tick on the scales.
+       int TickLength;
+       //! The distance, in pixels, between two ticks along the Y axis.
+       int YTickSpace;
+};
+
+enum PlotType
+{
+       PTG_LinBin,
+       PTG_LogBin,
+       PTG_Time,
+};
+
+struct PlotStruct
+{
+       //! The data points to plot.
+       long long int *datapoints;
+       //! The number of points to plot.
+       int npoints;
+       //! The minimum data to plot along the Y axis.
+       long long int ymin;
+       //! The maximum data to plot along the Y axis.
+       long long int ymax;
+       //! The type of Y axis to draw.
+       enum PlotType ytype;
+       //! The label to write on the X axis.
+       const char *XLabel;
+       //! The label to write on the Y axis.
+       const char *YLabel;
+       //! The name of the output PNG file.
+       const char *pngfile;
 };
 
 enum TextRefPos
 {
-   TRP_TopLeft,
-   TRP_TopCenter,
-   TRP_TopRight,
-   TRP_BottomLeft,
-   TRP_BottomCenter,
-   TRP_BottomRight,
-   TRP_CenterLeft,
-   TRP_Center,
-   TRP_CenterRight,
+       TRP_TopLeft,
+       TRP_TopCenter,
+       TRP_TopRight,
+       TRP_BottomLeft,
+       TRP_BottomCenter,
+       TRP_BottomRight,
+       TRP_CenterLeft,
+       TRP_Center,
+       TRP_CenterRight,
 };
 
-static char * Sarg_gdImageStringFT (struct GraphDataStruct *gdata, int fg, char *fontlist,
-                         double ptsize, double angle, int x, int y, char *string,enum TextRefPos RefPos)
+#ifdef USE_ICONV
+//! The iconv object to convert the text from the locale character set to UTF-8.
+iconv_t localtoutf=(iconv_t)-1;
+#endif
+
+extern char GraphConfigFile[MAXLEN];
+
+static void Sarg_gdImageStringFT (struct GraphDataStruct *gdata, int fg, char *fontlist,
+         double ptsize, double angle, int x, int y, const char *string,enum TextRefPos RefPos)
 {
-   char *sstring, *retval;
-   int brect[8];
-   int minx,miny,maxx,maxy;
-   int i;
+       char *sstring;
+       char *retval;
+       int brect[8];
+       int minx,miny,maxx,maxy;
+       int i;
 
 #ifdef USE_ICONV
-   if (gdata->localtoutf!=(iconv_t)-1) {
-      char *str;
-      char *sstr;
-      size_t slen, sslen;
-
-      slen = strlen(string) + 1; // We must include string termination character
-      sslen = slen * 2;          // We assume that UTF8 maximum 2 times large than local
-      if (sslen>gdata->string_size) {
-         sstring = (char *)realloc(gdata->string,sslen);
-         if (!sstring) {
-            debuga(_("realloc error (%zu bytes required)\n"),sslen);
-            exit(EXIT_FAILURE);
-         }
-         gdata->string=sstring;
-         gdata->string_size=sslen;
-      } else {
-         sstring=gdata->string;
-         sslen=gdata->string_size;
-      }
-
-      str = string;
-      sstr = sstring;
-      if (iconv (gdata->localtoutf, (ICONV_CONST char **)&str, &slen, &sstr, &sslen)==-1) {
-         debuga(_("(grepday) iconv failed to convert string \"%s\" from %s to UTF-8 - %s\n"),string,CharSet,strerror(errno));
-         sstring=string; //show something sensible on the graph
-      }
-   } else {
-      sstring=string; //show something sensible on the graph
-   }
+       if (localtoutf!=(iconv_t)-1) {
+               const char *str;
+               char *sstr;
+               size_t slen, sslen;
+
+               slen = strlen(string) + 1; // We must include string termination character
+               sslen = slen * 3;          // We assume that the UTF8 string will not be bigger than 3 times the original size.
+               if (sslen>gdata->string_size) {
+                       sstring = (char *)realloc(gdata->string,sslen);
+                       if (!sstring) {
+                               debuga(__FILE__,__LINE__,_("realloc error (%"PRIu64" bytes required)\n"),(uint64_t)sslen);
+                               exit(EXIT_FAILURE);
+                       }
+                       gdata->string=(char *)sstring;
+                       gdata->string_size=sslen;
+               } else {
+                       sstring=gdata->string;
+                       sslen=gdata->string_size;
+               }
+
+               str = string;
+               sstr = sstring;
+               if (iconv (localtoutf, (ICONV_CONST char **)&str, &slen, &sstr, &sslen)==-1) {
+                       debuga(__FILE__,__LINE__,_("iconv failed to convert string \"%s\" from %s to UTF-8: %s\n"),string,CharSet,strerror(errno));
+                       sstring=(char *)string; //show something sensible on the graph
+               }
+       } else {
+               sstring=(char *)string; //show something sensible on the graph
+       }
 #else
-   sstring=string;
+       sstring=(char *)string;
 #endif
 
-   if (RefPos!=TRP_BottomLeft) {
-      retval = gdImageStringFTEx (NULL, brect, fg, fontlist, ptsize, angle, 0, 0, sstring, gdFTEX_Unicode);
-      if (retval) {
-         debuga(_("libgd failed to calculate the bounding box of the text \"%s\": %s\n"),sstring,retval);
-         exit(EXIT_FAILURE);
-      }
-      /*
-      From libgd documentation, brect contains this without taking into account the angle:
-      0  lower left corner, X position
-      1  lower left corner, Y position
-      2  lower right corner, X position
-      3  lower right corner, Y position
-      4  upper right corner, X position
-      5  upper right corner, Y position
-      6  upper left corner, X position
-      7  upper left corner, Y position
-      */
-      minx=maxx=brect[0];
-      miny=maxy=brect[1];
-      for (i=2 ; i<7 ; i+=2) {
-         if (minx>brect[i]) minx=brect[i];
-         if (maxx<brect[i]) maxx=brect[i];
-         if (miny>brect[i+1]) miny=brect[i+1];
-         if (maxy<brect[i+1]) maxy=brect[i+1];
-      }
-   }
-
-   switch (RefPos)
-   {
-      case TRP_TopLeft:
-         y-=miny;
-         break;
-
-      case TRP_TopCenter:
-         x-=(maxx-minx)/2;
-         y-=miny;
-         break;
-
-      case TRP_TopRight:
-         x-=maxx;
-         y-=miny;
-         break;
-
-      case TRP_BottomLeft:
-         break;
-
-      case TRP_BottomCenter:
-         x-=(maxx-minx)/2;
-         break;
-
-      case TRP_BottomRight:
-         x-=maxx;
-         break;
-
-      case TRP_Center:
-         x-=(maxx-minx)/2;
-         y+=(maxy-miny)/2;
-         break;
-
-      case TRP_CenterLeft:
-         y+=(maxy-miny)/2;
-         break;
-
-      case TRP_CenterRight:
-         x-=maxx;
-         y+=(maxy-miny)/2;
-         break;
-   }
-   retval = gdImageStringFTEx (gdata->im, brect, fg, fontlist, ptsize, angle, x, y, sstring, gdFTEX_Unicode);
-   if (retval) {
-      debuga(_("libgd failed to render the text \"%s\": %s\n"),sstring,retval);
-      exit(EXIT_FAILURE);
-   }
-
-   return retval;
+       if (RefPos!=TRP_BottomLeft) {
+               retval = gdImageStringFTEx (NULL, brect, fg, fontlist, ptsize, angle, 0, 0, sstring, gdFTEX_Unicode);
+               if (retval) {
+                       debuga(__FILE__,__LINE__,_("libgd failed to calculate the bounding box of the text \"%s\": %s\n"),sstring,retval);
+                       exit(EXIT_FAILURE);
+               }
+               /*
+               From libgd documentation, brect contains this without taking into account the angle:
+               0  lower left corner, X position
+               1  lower left corner, Y position
+               2  lower right corner, X position
+               3  lower right corner, Y position
+               4  upper right corner, X position
+               5  upper right corner, Y position
+               6  upper left corner, X position
+               7  upper left corner, Y position
+               */
+               minx=maxx=brect[0];
+               miny=maxy=brect[1];
+               for (i=2 ; i<7 ; i+=2) {
+                       if (minx>brect[i]) minx=brect[i];
+                       if (maxx<brect[i]) maxx=brect[i];
+                       if (miny>brect[i+1]) miny=brect[i+1];
+                       if (maxy<brect[i+1]) maxy=brect[i+1];
+               }
+       }
+
+       switch (RefPos)
+       {
+               case TRP_TopLeft:
+                       y-=miny;
+                       break;
+
+               case TRP_TopCenter:
+                       x-=(maxx-minx)/2;
+                       y-=miny;
+                       break;
+
+               case TRP_TopRight:
+                       x-=maxx;
+                       y-=miny;
+                       break;
+
+               case TRP_BottomLeft:
+                       break;
+
+               case TRP_BottomCenter:
+                       x-=(maxx-minx)/2;
+                       break;
+
+               case TRP_BottomRight:
+                       x-=maxx;
+                       break;
+
+               case TRP_Center:
+                       x-=(maxx-minx)/2;
+                       y+=(maxy-miny)/2;
+                       break;
+
+               case TRP_CenterLeft:
+                       y+=(maxy-miny)/2;
+                       break;
+
+               case TRP_CenterRight:
+                       x-=maxx;
+                       y+=(maxy-miny)/2;
+                       break;
+       }
+       retval = gdImageStringFTEx (gdata->im, brect, fg, fontlist, ptsize, angle, x, y, sstring, gdFTEX_Unicode);
+       if (retval) {
+               debuga(__FILE__,__LINE__,_("libgd failed to render the text \"%s\": %s\n"),sstring,retval);
+               exit(EXIT_FAILURE);
+       }
+}
+
+static void bar(struct GraphDataStruct *gdata,int x1,double y,const char *label)
+{
+       gdPoint points[4];
+       int val=0;
+       int width2;
+
+       val=gdata->BottomGraph+5-(int)(y*gdata->YScale+0.5);
+       width2=(int)(gdata->XScale/4.);
+
+       // front side of the bar
+       if (val<gdata->BottomDepth)
+               gdImageFilledRectangle(gdata->im, x1-width2, val, x1+width2, gdata->BottomDepth, gdata->color3);
+
+       // top side of the bar
+       points[0].x = x1-width2+5;
+       points[0].y = val-5;
+       points[1].x = x1-width2;
+       points[1].y = val;
+       points[2].x = x1+width2;
+       points[2].y = val;
+       points[3].x = x1+width2+5;
+       points[3].y = val-5;
+       gdImageFilledPolygon(gdata->im, points, 4, gdata->color1);
+
+       gdImageLine(gdata->im, x1+2, val-2, x1+2, val-10, gdata->dimgray);
+       gdImageFilledRectangle(gdata->im, x1-8, val-20, x1+12, val-10, gdata->goldenrod);
+       gdImageRectangle(gdata->im, x1-8, val-20, x1+12, val-10, gdata->goldenrod2);
+
+       Sarg_gdImageStringFT(gdata,gdata->black,GraphFont,6,0.0,x1+2,val-12,label,TRP_BottomCenter);
+
+       // lateral side of the bar
+       if (val<gdata->BottomDepth) {
+               points[0].x = x1+width2+5;
+               points[0].y = val-5;
+               points[1].x = x1+width2;
+               points[1].y = val;
+               points[2].x = x1+width2;
+               points[2].y = gdata->BottomDepth;
+               points[3].x = x1+width2+5;
+               points[3].y = gdata->BottomGraph;
+               gdImageFilledPolygon(gdata->im, points, 4, gdata->color2);
+       }
+
+       return;
+}
+
+static int greport_compute_yaxis(struct PlotStruct *pdata,struct GraphDataStruct *gdata)
+{
+       double symin,symax;
+       double range;
+       double yscale;
+
+       if (pdata->ymin<0.) {
+               debuga(__FILE__,__LINE__,_("Minimum for Y scale of the graph is out of range: %"PRId64"\n"),(int64_t)pdata->ymin);
+               return(-1);
+       }
+       if (pdata->ymax<=0.) {
+               debuga(__FILE__,__LINE__,_("Maximum for Y scale of the graph is out of range: %"PRId64"\n"),(int64_t)pdata->ymax);
+               return(-1);
+       }
+
+       switch(pdata->ytype)
+       {
+               case PTG_LinBin:
+                       symin=(double)pdata->ymin;
+                       symax=(double)pdata->ymax;
+                       break;
+
+               case PTG_LogBin:
+                       if (pdata->ymin>0.)
+                               symin=log(pdata->ymin);
+                       else
+                               symin=0.;
+                       symax=log(pdata->ymax);
+                       break;
+
+               case PTG_Time:
+                       symin=(double)pdata->ymin;
+                       symax=(double)pdata->ymax;
+                       break;
+
+               default:
+                       debuga(__FILE__,__LINE__,_("Unknown type %d for Y axis scale\n"),pdata->ytype);
+                       return(-1);
+       }
+       gdata->YTickSpace=10;
+
+       range=symax-symin;
+       yscale=(double)(gdata->BottomGraph-gdata->TopGraph)/range;
+       gdata->YScale=yscale;
+       return(0);
+}
+
+static void greport_formatbin(double yval,int maxdigits,char *string,int slen)
+{
+       int len;
+       char schar[]={'\0','k','M','G','T','P'};
+       int scount;
+       int i;
+       int ndigits;
+
+       for (scount=0 ; scount<sizeof(schar)/sizeof(*schar) && yval>=1000. ; scount++)
+               yval/=1000.;
+       if (yval<2.)
+               ndigits=2;
+       else if (yval<3.)
+               ndigits=1;
+       else
+               ndigits=0;
+       if (ndigits>maxdigits) ndigits=maxdigits;
+       len=snprintf(string,slen,"%.*f",ndigits,(float)yval);
+       if (UseComma)
+               for (i=0 ; i<len ; i++)
+                       if (string[i]=='.') string[i]=',';
+       if (schar[scount] && len<slen) {
+               string[len++]=schar[scount];
+               string[len]='\0';
+       }
 }
 
-static void bar(struct GraphDataStruct *gdata,int x1,long long int n)
+static void greport_draw_yaxis(struct PlotStruct *pdata,struct GraphDataStruct *gdata)
 {
-   gdPoint points[4];
-   int val=0, x;
-   long long int num;
-
-   static const long long int limits[400]={0,500,1000,2000,3000,4000,
-       5000,6000,7000,8000,9000,10000,
-       20000,30000,40000,50000,70000,90000,
-       110000,130000,150000,170000,190000,210000,
-       230000,250000,280000,310000,340000,370000,
-       400000,430000,450000,460000,490000,500000,
-       550000,600000,650000,700000,750000,800000,
-       850000,900000,950000,1000000,1100000,1200000,
-       1300000,1400000,1500000,1600000,1700000,1800000,
-       1900000,2000000,2100000,2200000,2300000,2400000,
-       2500000,2600000,2700000,2800000,2900000,3000000,
-       3100000,3200000,3300000,3400000,3500000,3600000,
-       3700000,3800000,3900000,4000000,4100000,4200000,
-       4300000,4400000,4500000,4600000,4700000,4800000,
-       4900000,5000000,5100000,5200000,5300000,5400000,
-       5500000,5600000,5700000,5800000,5900000,6000000,
-       6100000,6200000,6300000,6400000,6500000,6600000,
-       6700000,6800000,6900000,7000000,7100000,7200000,
-       7300000,7400000,7500000,7600000,7700000,7800000,
-       7900000,8000000,8100000,8200000,8300000,8400000,
-       8500000,8600000,8700000,8800000,8900000,9000000,
-       9100000,9200000,9300000,9400000,9500000,9600000,
-       9700000,9800000,9900000,10000000,10500000,11000000,
-       11500000,12000000,12500000,13000000,13500000,14000000,
-       14500000,15000000,15500000,16000000,16500000,17000000,
-       17500000,18000000,18500000,19000000,19500000,20000000,
-       21000000,22000000,23000000,24000000,25000000,26000000,
-       27000000,28000000,29000000,30000000,31000000,32000000,
-       33000000,34000000,35000000,36000000,37000000,38000000,
-       39000000,40000000,41000000,42000000,43000000,44000000,
-       45000000,46000000,47000000,48000000,49000000,50000000,
-       51000000,52000000,53000000,54000000,55000000,56000000,
-       57000000,58000000,59000000,60000000,61000000,62000000,
-       63000000,64000000,65000000,66000000,67000000,68000000,
-       69000000,70000000,71000000,72000000,73000000,74000000,
-       75000000,76000000,77000000,78000000,79000000,80000000,
-       81000000,82000000,83000000,84000000,85000000,86000000,
-       87000000,88000000,89000000,90000000,91000000,92000000,
-       93000000,94000000,95000000,96000000,97000000,98000000,
-       99000000,100000000,110000000,120000000,130000000,140000000,
-       150000000,160000000,170000000,180000000,190000000,200000000,
-       210000000,220000000,230000000,240000000,250000000,260000000,
-       270000000,280000000,290000000,300000000,310000000,320000000,
-       330000000,340000000,350000000,360000000,370000000,380000000,
-       390000000,400000000,410000000,420000000,430000000,440000000,
-       450000000,460000000,470000000,480000000,490000000,500000000,
-       510000000,520000000,530000000,540000000,550000000,560000000,
-       570000000,580000000,590000000,600000000,610000000,620000000,
-       630000000,640000000,650000000,660000000,670000000,680000000,
-       690000000,700000000,710000000,720000000,730000000,740000000,
-       750000000,760000000,770000000,780000000,790000000,800000000,
-       810000000,820000000,830000000,840000000,850000000,860000000,
-       870000000,880000000,890000000,900000000,910000000,920000000,
-       930000000,940000000,950000000,960000000,970000000,980000000,
-    990000000LL,1000000000LL,1100000000LL,1200000000LL,1300000000LL,1400000000LL,
-    1500000000LL,1600000000LL,1700000000LL,1800000000LL,1900000000LL,2000000000LL,
-    2100000000LL,2200000000LL,2300000000LL,2400000000LL,2500000000LL,2600000000LL,
-    2700000000LL,2800000000LL,2900000000LL,3000000000LL,3100000000LL,3200000000LL,
-    3300000000LL,3400000000LL,3500000000LL,3600000000LL,3700000000LL,3800000000LL,
-    3900000000LL,4000000000LL,4100000000LL,4200000000LL,4300000000LL,4400000000LL,
-    4500000000LL,4600000000LL,4700000000LL,4800000000LL,4900000000LL,5000000000LL};
-
-   num = n;
-   val = 55;
-   for(x=0; x<=366; x++) {
-      if(limits[x] >= num) {
-         val = 425 - x;
-         break;
-      }
-   }
-
-   gdImageFilledRectangle(gdata->im, x1, val, x1+11, 425, gdata->color3);
-
-   points[0].x = x1+7;
-   points[0].y = val-5;
-   points[1].x = x1;
-   points[1].y = val;
-   points[2].x = x1+11;
-   points[2].y = val;
-   points[3].x = x1+17;
-   points[3].y = val-5;
-   gdImageFilledPolygon(gdata->im, points, 4, gdata->color1);
-
-   gdImageLine(gdata->im, x1+8, val-2, x1+8, val-10, gdata->dimgray);
-   gdImageFilledRectangle(gdata->im, x1-2, val-20, x1+18, val-10, gdata->goldenrod);
-   gdImageRectangle(gdata->im, x1-2, val-20, x1+18, val-10, gdata->goldenrod2);
-
-   Sarg_gdImageStringFT(gdata,gdata->black,GraphFont,6,0.0,x1+8,val-12,fixnum(num,0),TRP_BottomCenter);
-
-   points[0].x = x1+17;
-   points[0].y = val-5;
-   points[1].x = x1+11;
-   points[1].y = val;
-   points[2].x = x1+11;
-   points[2].y = 426;
-   points[3].x = x1+17;
-   points[3].y = 420;
-   gdImageFilledPolygon(gdata->im, points, 4, gdata->color2);
-
-   return;
+       double yval;
+       int y0;
+       int y;
+       int yt;
+       char YLabel[50];
+       int xexterior;
+       int xinterior;
+       int xtick;
+
+       y0=gdata->BottomGraph;
+       yt=gdata->BottomDepth-gdata->BottomGraph;
+       xexterior=gdata->LeftGraph-10;
+       xinterior=gdata->LeftGraph;
+       xtick=gdata->LeftGraph-10-gdata->TickLength;
+       for(y=y0-gdata->YTickSpace ; y>=gdata->TopGraph ; y-=gdata->YTickSpace) {
+               gdImageLine(gdata->im, xtick, y+yt, xexterior, y+yt, gdata->dimgray);
+               gdImageLine(gdata->im, xexterior, y+yt, xinterior, y, gdata->dimgray);
+               gdImageLine(gdata->im, xinterior, y, gdata->RightGraph, y, gdata->dimgray);
+               switch (pdata->ytype)
+               {
+                       case PTG_LinBin:
+                               yval=(double)(y0-y)/gdata->YScale+(double)pdata->ymin;
+                               greport_formatbin(yval,2,YLabel,sizeof(YLabel));
+                               break;
+
+                       case PTG_LogBin:
+                               yval=exp((double)(y0-y)/gdata->YScale+log(pdata->ymin));
+                               greport_formatbin(yval,2,YLabel,sizeof(YLabel));
+                               break;
+
+                       case PTG_Time:
+                       {
+                               int t;
+
+                               yval=(double)(y0-y)/gdata->YScale+(double)pdata->ymin;
+                               t=(int)(yval/60000.+0.5);
+                               snprintf(YLabel,sizeof(YLabel),"%02d:%02d",t/60,t%60);
+                               break;
+                       }
+               }
+               Sarg_gdImageStringFT(gdata,gdata->dimgray,GraphFont,7,0.0,xtick,y+yt,YLabel,TRP_CenterRight);
+       }
 }
+
+static void greport_plot(const struct userinfostruct *uinfo,struct PlotStruct *pdata)
+{
+       FILE *pngout;
+       int x, y;
+       int day;
+       int x1;
+       char graph[MAXLEN];
+       char s[15];
+       char ftime[128];
+       time_t t;
+       struct tm *local;
+       gdPoint points[4];
+       struct GraphDataStruct gdata;
+       const int ImgXSize=720;
+       const int ImgYSize=480;
+       const int LeftMargin=60;
+       const int RightMargin=20;
+       const int TopMargin=60;
+       const int BottomMargin=60;
+       const int TickLength=3;
+       const int ZTickLength=5;
+       double yval;
+       char blabel[50];
+       double logpmin;
+
+       memset(&gdata,0,sizeof(gdata));
+
+       gdata.im = gdImageCreate(ImgXSize, ImgYSize);
+       gdata.BottomGraph=ImgYSize-BottomMargin;
+       gdata.LeftGraph=LeftMargin;
+       gdata.RightGraph=ImgXSize-RightMargin;
+       gdata.TopGraph=TopMargin;
+       gdata.BottomDepth=gdata.BottomGraph+5;
+       gdata.XScale=(double)(gdata.RightGraph-gdata.LeftGraph)/(pdata->npoints+1);
+       if (greport_compute_yaxis(pdata,&gdata)<0) return;
+       gdata.TickLength=TickLength;
+
+       // first allocated color is the background
+       gdata.lavender = gdImageColorAllocate(gdata.im, 230, 230, 250);
+       gdata.gray = gdImageColorAllocate(gdata.im, 192, 192, 192);
+       gdata.silver = gdImageColorAllocate(gdata.im, 211, 211, 211);
+       gdata.black = gdImageColorAllocate(gdata.im, 0, 0, 0);
+       gdata.dimgray = gdImageColorAllocate(gdata.im, 105, 105, 105);
+       gdata.darkblue = gdImageColorAllocate(gdata.im, 0, 0, 139);
+       gdata.goldenrod = gdImageColorAllocate(gdata.im, 234, 234, 174);
+       gdata.goldenrod2 = gdImageColorAllocate(gdata.im, 207, 181, 59);
+
+       if(strcmp(GraphDaysBytesBarColor,"orange") == 0) {
+               gdata.color1 = gdImageColorAllocate(gdata.im, 255, 233, 142);
+               gdata.color2 = gdImageColorAllocate(gdata.im, 220, 163, 72);
+               gdata.color3 = gdImageColorAllocate(gdata.im, 255, 198, 107);
+       }
+       else if(strcmp(GraphDaysBytesBarColor,"blue") == 0) {
+               gdata.color1 = gdImageColorAllocate(gdata.im, 62, 80, 167);
+               gdata.color2 = gdImageColorAllocate(gdata.im, 40, 51, 101);
+               gdata.color3 = gdImageColorAllocate(gdata.im, 57, 73, 150);
+       }
+       else if(strcmp(GraphDaysBytesBarColor,"green") == 0) {
+               gdata.color1 = gdImageColorAllocate(gdata.im,120,166,129);
+               gdata.color2 = gdImageColorAllocate(gdata.im,84,113,82);
+               gdata.color3 = gdImageColorAllocate(gdata.im,158,223,167);
+       }
+       else if(strcmp(GraphDaysBytesBarColor,"yellow") == 0) {
+               gdata.color1 = gdImageColorAllocate(gdata.im,185,185,10);
+               gdata.color2 = gdImageColorAllocate(gdata.im,111,111,10);
+               gdata.color3 = gdImageColorAllocate(gdata.im,166,166,10);
+       }
+       else if(strcmp(GraphDaysBytesBarColor,"brown") == 0) {
+               gdata.color1 = gdImageColorAllocate(gdata.im,97,45,27);
+               gdata.color2 = gdImageColorAllocate(gdata.im,60,30,20);
+               gdata.color3 = gdImageColorAllocate(gdata.im,88,41,26);
+       }
+       else if(strcmp(GraphDaysBytesBarColor,"red")  == 0){
+               gdata.color1 = gdImageColorAllocate(gdata.im,185,10,10);
+               gdata.color2 = gdImageColorAllocate(gdata.im,111,10,10);
+               gdata.color3 = gdImageColorAllocate(gdata.im,166,10,10);
+       } else {
+               debuga(__FILE__,__LINE__,_("Unknown color \"%s\" requested for the graph. Using orange instead\n"),GraphDaysBytesBarColor);
+               gdata.color1 = gdImageColorAllocate(gdata.im, 255, 233, 142);
+               gdata.color2 = gdImageColorAllocate(gdata.im, 220, 163, 72);
+               gdata.color3 = gdImageColorAllocate(gdata.im, 255, 198, 107);
+       }
+
+       // rectangle around the image
+       gdImageRectangle(gdata.im, 0, 0, ImgXSize-1, ImgYSize-1, gdata.dimgray);
+       // backtround of the graph
+       gdImageFilledRectangle(gdata.im, LeftMargin, gdata.TopGraph, gdata.RightGraph, gdata.BottomGraph, gdata.silver);
+
+       // depth of the left Y axis
+       points[0].x = gdata.LeftGraph-10;
+       points[0].y = gdata.TopGraph+5;
+       points[1].x = gdata.LeftGraph-10;
+       points[1].y = gdata.BottomDepth;
+       points[2].x = gdata.LeftGraph;
+       points[2].y = gdata.BottomGraph;
+       points[3].x = gdata.LeftGraph;
+       points[3].y = gdata.TopGraph;
+       gdImageFilledPolygon(gdata.im, points, 4, gdata.gray);
+
+       // depth of the bottom X axis
+       points[0].x = gdata.LeftGraph;
+       points[0].y = gdata.BottomGraph;
+       points[1].x = gdata.LeftGraph-10;
+       points[1].y = gdata.BottomDepth;
+       points[2].x = gdata.RightGraph-10;
+       points[2].y = gdata.BottomDepth;
+       points[3].x = gdata.RightGraph;
+       points[3].y = gdata.BottomGraph;
+       gdImageFilledPolygon(gdata.im, points, 4, gdata.gray);
+
+       // vertical exterior line of the depth
+       gdImageLine(gdata.im, LeftMargin-10, TopMargin+5, LeftMargin-10, gdata.BottomDepth+ZTickLength, gdata.black);
+       // horizontal exterior line of the depth
+       gdImageLine(gdata.im, LeftMargin-10-ZTickLength, gdata.BottomDepth, gdata.RightGraph-10, gdata.BottomDepth, gdata.black);
+       // diagonal line between the two depths
+       gdImageLine(gdata.im, LeftMargin-10, gdata.BottomDepth, LeftMargin, gdata.BottomGraph, gdata.black);
+       // vertical left line of the graph
+       gdImageLine(gdata.im, LeftMargin, gdata.BottomGraph, LeftMargin, gdata.TopGraph, gdata.black);
+       // horizontal bottom line of the graph
+       gdImageLine(gdata.im, LeftMargin, gdata.BottomGraph, gdata.RightGraph, gdata.BottomGraph, gdata.black);
+       // vertical right line of the graph
+       gdImageLine(gdata.im, gdata.RightGraph, gdata.TopGraph, gdata.RightGraph, gdata.BottomGraph, gdata.black);
+       // diagonal line to close the right of the bottom depth
+       gdImageLine(gdata.im, gdata.RightGraph-10, gdata.BottomDepth, gdata.RightGraph, gdata.BottomGraph, gdata.black);
+
+       // Y axis ticks
+       greport_draw_yaxis(pdata,&gdata);
+
+       // X axis ticks and labels
+       for(y=1; y<=pdata->npoints; y++) {
+               x=gdata.LeftGraph-10+(int)((double)y*gdata.XScale+0.5);
+               gdImageLine(gdata.im, x, gdata.BottomDepth, x, gdata.BottomDepth+TickLength, gdata.dimgray);
+               sprintf(s,"%02d",y);
+               Sarg_gdImageStringFT(&gdata,gdata.dimgray,GraphFont,7,0.0,x,gdata.BottomDepth+TickLength+1,s,TRP_TopCenter);
+       }
+
+       t = time(NULL);
+       local = localtime(&t);
+       if (df=='u')
+               strftime(ftime, sizeof(ftime), "%b/%d/%Y %H:%M", local);
+       if (df=='e')
+               strftime(ftime, sizeof(ftime), "%d/%b/%Y-%H:%M", local);
+
+       x=ImgXSize*5/12;
+       Sarg_gdImageStringFT(&gdata,gdata.darkblue,GraphFont,7,0.0,ImgXSize-10,ImgYSize-10,ftime,TRP_BottomRight);
+       if(ShowSargInfo) Sarg_gdImageStringFT(&gdata,gdata.darkblue,GraphFont,10,0.0,x,15,_("SARG, "),TRP_BottomRight);
+       Sarg_gdImageStringFT(&gdata,gdata.darkblue,GraphFont,10,0.0,x,15,Title,TRP_BottomLeft);
+       sprintf(warea,_("Period: %s"),period.text);
+       Sarg_gdImageStringFT(&gdata,gdata.darkblue,GraphFont,9,0.0,x,27,warea,TRP_BottomLeft);
+       sprintf(warea,_("User: %s"),uinfo->label);
+       Sarg_gdImageStringFT(&gdata,gdata.darkblue,GraphFont,9,0.0,x,38,warea,TRP_BottomLeft);
+
+       Sarg_gdImageStringFT(&gdata,gdata.black,GraphFont,10,3.141592/2,15,ImgYSize/2,pdata->YLabel,TRP_CenterLeft);
+       Sarg_gdImageStringFT(&gdata,gdata.black,GraphFont,10,0.0,ImgXSize/2,ImgYSize-20,pdata->XLabel,TRP_BottomCenter);
+
+       logpmin=(pdata->ytype==PTG_LogBin && pdata->ymin>0.) ? log(pdata->ymin) : 0.;
+       for (day=0 ; day<pdata->npoints ; day++) {
+               if (pdata->datapoints[day]>0) {
+                       x1=gdata.LeftGraph-10+(int)((double)(day+1)*gdata.XScale+0.5);
+                       switch (pdata->ytype)
+                       {
+                               case PTG_LinBin:
+                                       yval=(double)pdata->datapoints[day];
+                                       if (yval<pdata->ymin)
+                                               yval=0.;
+                                       else if (yval>pdata->ymax)
+                                               yval=pdata->ymax;
+                                       else
+                                               yval-=pdata->ymin;
+                                       greport_formatbin(pdata->datapoints[day],1,blabel,sizeof(blabel));
+                                       break;
+                               case PTG_LogBin:
+                                       yval=(double)pdata->datapoints[day];
+                                       if (yval<=pdata->ymin)
+                                               yval=0.;
+                                       else if (yval>pdata->ymax)
+                                               yval=log(pdata->ymax)-logpmin;
+                                       else
+                                               yval=log(yval)-logpmin;
+                                       greport_formatbin(pdata->datapoints[day],1,blabel,sizeof(blabel));
+                                       break;
+                               case PTG_Time:
+                               {
+                                       int t;
+
+                                       yval=(double)pdata->datapoints[day];
+                                       if (yval<pdata->ymin)
+                                               yval=0.;
+                                       else if (yval>pdata->ymax)
+                                               yval=pdata->ymax;
+                                       else
+                                               yval-=pdata->ymin;
+                                       t=(int)(pdata->datapoints[day]/60000.);
+                                       snprintf(blabel,sizeof(blabel),"%d:%02d",t/60,t%60);
+                                       break;
+                               }
+                               default:
+                                       yval=-1.;
+                                       break;
+                       }
+                       if (yval>=0.) bar(&gdata,x1,yval,blabel);
+               }
+       }
+
+       if (snprintf(graph,sizeof(graph),"%s/%s/%s",outdirname,uinfo->filename,pdata->pngfile)>=sizeof(graph)) {
+               /* TRANSLATORS: The message is followed by the path that is too long. */
+               debuga(__FILE__,__LINE__,_("User name too long to manufacture file name "));
+               debuga_more("%s/%s/%s\n",outdirname,uinfo->filename,pdata->pngfile);
+               exit(EXIT_FAILURE);
+       }
+       if((pngout=fopen(graph,"wb"))==NULL) {
+               debuga(__FILE__,__LINE__,_("Cannot open file \"%s\": %s\n"),graph,strerror(errno));
+               exit(EXIT_FAILURE);
+       }
+       gdImagePng(gdata.im, pngout);
+       if (fclose(pngout)==EOF) {
+               debuga(__FILE__,__LINE__,_("Write error in \"%s\": %s\n"),graph,strerror(errno));
+       }
+       gdImageDestroy(gdata.im);
+
+       if (gdata.string) free(gdata.string);
+}
+
 #endif //HAVE_GD
 
-void greport_day(const struct userinfostruct *uinfo)
+void greport_prepare(void)
 {
 #ifdef HAVE_GD
-   FILE *fp_in, *pngout;
-   int x, y;
-   int x1;
-   char wdirname[MAXLEN];
-   char graph[MAXLEN];
-   char buf[MAXLEN];
-   char csort[255];
-   char s[15];
-   char tmp5[MAXLEN];
-   int oday=0;
-   int day=0;
-   long long int llday;
-   long long int bytes;
-   char ftime[128];
-   long long int tot=0;
-   time_t t;
-   struct tm *local;
-   int cstatus;
-   long long datapoints[31];
-   gdPoint points[4];
-   struct getwordstruct gwarea;
-   struct GraphDataStruct gdata;
-   const int ImgXSize=720;
-   const int ImgYSize=480;
-   const int LeftMargin=60;
-   const int RightMargin=20;
-   const int TopMargin=60;
-   const int BottomMargin=60;
-   const int TickLength=3;
-   const int ZTickLength=5;
-   int BottomGraph;
-   int RightGraph;
-
-   if (snprintf(wdirname,sizeof(wdirname),"%s/%s.day",tmp,uinfo->filename)>=sizeof(wdirname)) {
-      debuga(_("user name too long for %s/%s.day\n"),tmp,uinfo->filename);
-      exit(EXIT_FAILURE);
-   }
-   if(access(wdirname, R_OK) != 0) {
-      return;
-   }
-   if(!Graphs || GraphFont[0]=='\0') {
-      unlink(wdirname);
-      return;
-   }
-
-   if (snprintf(tmp5,sizeof(tmp5),"%s/%s.graph",tmp,uinfo->filename)>=sizeof(tmp5)) {
-      debuga(_("user name too long for %s/%s.graph\n"),tmp,uinfo->filename);
-      exit(EXIT_FAILURE);
-   }
-   if(strcmp(DateFormat,"e") == 0)
-      sprintf(csort,"sort -t\"/\" -k 1,1 -o \"%s\" \"%s\"",tmp5,wdirname);
-   else
-      sprintf(csort,"sort -t\"/\" -k 2,2 -o \"%s\" \"%s\"",tmp5,wdirname);
-
-   cstatus=system(csort);
-   if (!WIFEXITED(cstatus) || WEXITSTATUS(cstatus)) {
-      debuga(_("sort command return status %d\n"),WEXITSTATUS(cstatus));
-      debuga(_("sort command: %s\n"),csort);
-      exit(EXIT_FAILURE);
-   }
-
-   if((fp_in=fopen(tmp5,"r"))==NULL) {
-      debuga(_("(grepday) Cannot open log file %s\n"),tmp5);
-      exit(EXIT_FAILURE);
-   }
-   unlink(wdirname);
-
-   memset(datapoints,0,sizeof(datapoints));
-   oday=-1;
-   while(fgets(buf,sizeof(buf),fp_in)!=NULL) {
-      fixendofline(buf);
-      getword_start(&gwarea,buf);
-      if (getword_atoll(&llday,&gwarea,'/')<0) {
-         debuga(_("Maybe you have a broken record or garbage in your %s file\n"),tmp5);
-         exit(EXIT_FAILURE);
-      }
-      if(DateFormat[0]=='u') {
-         if (getword_atoll(&llday,&gwarea,'/')<0) {
-            debuga(_("Maybe you have a broken record or garbage in your %s file\n"),tmp5);
-            exit(EXIT_FAILURE);
-         }
-      }
-      day=(int)llday;
-      if (day<1 || day>31) continue;
-      if (getword_skip(20,&gwarea,'\t')<0 || getword_skip(20,&gwarea,'\t')<0 || getword_atoll(&bytes,&gwarea,'\t')<0) {
-         debuga(_("Maybe you have a broken record or garbage in your %s file\n"),tmp5);
-         exit(EXIT_FAILURE);
-      }
-
-      if(oday!=day) {
-         if (oday>0) datapoints[oday-1]=tot;
-         oday=day;
-         tot=0;
-      }
-      tot+=bytes;
-   }
-   fclose(fp_in);
-   unlink(tmp5);
-   if (oday>0) datapoints[oday-1]=tot;
-
-   if(access(GraphFont, R_OK) != 0) {
-      debuga(_("(grepday) Fontname %s not found\n"),GraphFont);
-      exit(EXIT_FAILURE);
-   }
-   memset(&gdata,0,sizeof(gdata));
+       if (!Graphs) {
+               if (debugz>=LogLevel_Process)
+                       debugaz(__FILE__,__LINE__,_("Graphs disabled as requested in %s\n"),GraphConfigFile);
+               return;
+       }
+       if (GraphFont[0]=='\0') {
+               if (debugz>=LogLevel_Process) {
+                       const char *File=(GraphConfigFile[0]) ? GraphConfigFile : ConfigFile;
+                       debugaz(__FILE__,__LINE__,_("Graphs disabled as no font names were provided in %s\n"),File);
+               }
+               return;
+       }
+
+       if(access(GraphFont, R_OK) != 0) {
+               debuga(__FILE__,__LINE__,_("Font name %s not found\n"),GraphFont);
+               exit(EXIT_FAILURE);
+       }
 
 #ifdef USE_ICONV
-   gdata.localtoutf = iconv_open ("UTF-8", CharSet);
-   if (gdata.localtoutf==(iconv_t)-1) {
-      debuga(_("(grepday) iconv cannot convert from %s to UTF-8 - %s\n"),CharSet,strerror(errno));
-   }
+       localtoutf = iconv_open ("UTF-8", CharSet);
+       if (localtoutf==(iconv_t)-1) {
+               debuga(__FILE__,__LINE__,_("iconv cannot convert from %s to UTF-8: %s\n"),CharSet,strerror(errno));
+       }
 #endif
 
-   gdata.im = gdImageCreate(ImgXSize, ImgYSize);
-   BottomGraph=ImgYSize-BottomMargin;
-   RightGraph=ImgXSize-RightMargin;
-
-   // first allocated color is the background
-   gdata.lavender = gdImageColorAllocate(gdata.im, 230, 230, 250);
-   gdata.gray = gdImageColorAllocate(gdata.im, 192, 192, 192);
-   gdata.silver = gdImageColorAllocate(gdata.im, 211, 211, 211);
-   gdata.black = gdImageColorAllocate(gdata.im, 0, 0, 0);
-   gdata.dimgray = gdImageColorAllocate(gdata.im, 105, 105, 105);
-   gdata.darkblue = gdImageColorAllocate(gdata.im, 0, 0, 139);
-   gdata.goldenrod = gdImageColorAllocate(gdata.im, 234, 234, 174);
-   gdata.goldenrod2 = gdImageColorAllocate(gdata.im, 207, 181, 59);
-
-   if(strcmp(GraphDaysBytesBarColor,"orange") == 0) {
-      gdata.color1 = gdImageColorAllocate(gdata.im, 255, 233, 142);
-      gdata.color2 = gdImageColorAllocate(gdata.im, 220, 163, 72);
-      gdata.color3 = gdImageColorAllocate(gdata.im, 255, 198, 107);
-   }
-   else if(strcmp(GraphDaysBytesBarColor,"blue") == 0) {
-      gdata.color1 = gdImageColorAllocate(gdata.im, 62, 80, 167);
-      gdata.color2 = gdImageColorAllocate(gdata.im, 40, 51, 101);
-      gdata.color3 = gdImageColorAllocate(gdata.im, 57, 73, 150);
-   }
-   else if(strcmp(GraphDaysBytesBarColor,"green") == 0) {
-      gdata.color1 = gdImageColorAllocate(gdata.im,120,166,129);
-      gdata.color2 = gdImageColorAllocate(gdata.im,84,113,82);
-      gdata.color3 = gdImageColorAllocate(gdata.im,158,223,167);
-   }
-   else if(strcmp(GraphDaysBytesBarColor,"yellow") == 0) {
-      gdata.color1 = gdImageColorAllocate(gdata.im,185,185,10);
-      gdata.color2 = gdImageColorAllocate(gdata.im,111,111,10);
-      gdata.color3 = gdImageColorAllocate(gdata.im,166,166,10);
-   }
-   else if(strcmp(GraphDaysBytesBarColor,"brown") == 0) {
-      gdata.color1 = gdImageColorAllocate(gdata.im,97,45,27);
-      gdata.color2 = gdImageColorAllocate(gdata.im,60,30,20);
-      gdata.color3 = gdImageColorAllocate(gdata.im,88,41,26);
-   }
-   else if(strcmp(GraphDaysBytesBarColor,"red")  == 0){
-      gdata.color1 = gdImageColorAllocate(gdata.im,185,10,10);
-      gdata.color2 = gdImageColorAllocate(gdata.im,111,10,10);
-      gdata.color3 = gdImageColorAllocate(gdata.im,166,10,10);
-   } else {
-      debuga(_("Unknown color \"%s\" requested for the graph. Using orange instead\n"),GraphDaysBytesBarColor);
-      gdata.color1 = gdImageColorAllocate(gdata.im, 255, 233, 142);
-      gdata.color2 = gdImageColorAllocate(gdata.im, 220, 163, 72);
-      gdata.color3 = gdImageColorAllocate(gdata.im, 255, 198, 107);
-   }
-
-   // rectangle around the image
-   gdImageRectangle(gdata.im, 0, 0, ImgXSize-1, ImgYSize-1, gdata.dimgray);
-   // backtround of the graph
-   gdImageFilledRectangle(gdata.im, LeftMargin, TopMargin, RightGraph, BottomGraph, gdata.silver);
-
-   // depth of the left Y axis
-   points[0].x = LeftMargin-10;
-   points[0].y = TopMargin+5;
-   points[1].x = LeftMargin-10;
-   points[1].y = BottomGraph+5;
-   points[2].x = LeftMargin;
-   points[2].y = BottomGraph;
-   points[3].x = LeftMargin;
-   points[3].y = TopMargin;
-   gdImageFilledPolygon(gdata.im, points, 4, gdata.gray);
-
-   // depth of the bottom X axis
-   points[0].x = LeftMargin;
-   points[0].y = BottomGraph;
-   points[1].x = LeftMargin-10;
-   points[1].y = BottomGraph+5;
-   points[2].x = RightGraph-10;
-   points[2].y = BottomGraph+5;
-   points[3].x = RightGraph;
-   points[3].y = BottomGraph;
-   gdImageFilledPolygon(gdata.im, points, 4, gdata.gray);
-
-   // vertical exterior line of the depth
-   gdImageLine(gdata.im, LeftMargin-10, TopMargin+5, LeftMargin-10, BottomGraph+5+ZTickLength, gdata.black);
-   // horizontal exterior line of the depth
-   gdImageLine(gdata.im, LeftMargin-10-ZTickLength, BottomGraph+5, RightGraph-10, BottomGraph+5, gdata.black);
-   // diagonal line between the two depths
-   gdImageLine(gdata.im, LeftMargin-10, BottomGraph+5, LeftMargin, BottomGraph, gdata.black);
-   // vertical left line of the graph
-   gdImageLine(gdata.im, LeftMargin, BottomGraph, LeftMargin, TopMargin, gdata.black);
-   // horizontal bottom line of the graph
-   gdImageLine(gdata.im, LeftMargin, BottomGraph, RightGraph, BottomGraph, gdata.black);
-   // vertical right line of the graph
-   gdImageLine(gdata.im, RightGraph, TopMargin, RightGraph, BottomGraph, gdata.black);
-   // diagonal line to close the right of the bottom depth
-   gdImageLine(gdata.im, RightGraph-10, BottomGraph+5, RightGraph, BottomGraph, gdata.black);
-
-   // Y axis ticks
-   for(x=BottomGraph-10; x>=TopMargin; x=x-10) {
-      gdImageLine(gdata.im, LeftMargin-10, x+5, LeftMargin, x, gdata.dimgray);
-      gdImageLine(gdata.im, LeftMargin-10-TickLength, x+5, LeftMargin-10, x+5, gdata.dimgray);
-   }
-
-   // horizontal background lines
-   for(x=TopMargin; x<=BottomGraph-10; x=x+10)
-      gdImageLine(gdata.im, LeftMargin, x, RightGraph, x, gdata.dimgray);
-
-   // X axis ticks and labels
-   x=LeftMargin-10+20;
-   for(y=1; y<=31; y++) {
-      gdImageLine(gdata.im, x, BottomGraph+5, x, BottomGraph+5+TickLength, gdata.dimgray);
-      sprintf(s,"%02d",y);
-      Sarg_gdImageStringFT(&gdata,gdata.dimgray,GraphFont,7,0.0,x,BottomGraph+5+TickLength+1,s,TRP_TopCenter);
-      x=x+20;
-   }
-
-   t = time(NULL);
-   local = localtime(&t);
-   if(strcmp(DateFormat,"u") == 0)
-      strftime(ftime, sizeof(ftime), "%b/%d/%Y %H:%M", local);
-   if(strcmp(DateFormat,"e") == 0)
-      strftime(ftime, sizeof(ftime), "%d/%b/%Y-%H:%M", local);
-
-   Sarg_gdImageStringFT(&gdata,gdata.darkblue,GraphFont,7,0.0,ImgXSize-10,ImgYSize-10,ftime,TRP_BottomRight);
-   if(ShowSargInfo) Sarg_gdImageStringFT(&gdata,gdata.darkblue,GraphFont,10,0.0,300,15,_("SARG, "),TRP_BottomRight);
-   Sarg_gdImageStringFT(&gdata,gdata.darkblue,GraphFont,10,0.0,300,15,Title,TRP_BottomLeft);
-   sprintf(warea,_("Period: %s"),period.text);
-   Sarg_gdImageStringFT(&gdata,gdata.darkblue,GraphFont,9,0.0,300,27,warea,TRP_BottomLeft);
-   sprintf(warea,_("User: %s"),uinfo->label);
-   Sarg_gdImageStringFT(&gdata,gdata.darkblue,GraphFont,9,0.0,300,38,warea,TRP_BottomLeft);
-
-   Sarg_gdImageStringFT(&gdata,gdata.dimgray,GraphFont,7,0.0,LeftMargin-10-TickLength,BottomGraph-10+5,"50K",TRP_CenterRight);
-   Sarg_gdImageStringFT(&gdata,gdata.dimgray,GraphFont,7,0.0,LeftMargin-10-TickLength,BottomGraph-20+5,"250K",TRP_CenterRight);
-   Sarg_gdImageStringFT(&gdata,gdata.dimgray,GraphFont,7,0.0,LeftMargin-10-TickLength,BottomGraph-30+5,"500K",TRP_CenterRight);
-   Sarg_gdImageStringFT(&gdata,gdata.dimgray,GraphFont,7,0.0,LeftMargin-10-TickLength,BottomGraph-40+5,"1M",TRP_CenterRight);
-   Sarg_gdImageStringFT(&gdata,gdata.dimgray,GraphFont,7,0.0,LeftMargin-10-TickLength,BottomGraph-50+5,"2M",TRP_CenterRight);
-   Sarg_gdImageStringFT(&gdata,gdata.dimgray,GraphFont,7,0.0,LeftMargin-10-TickLength,BottomGraph-60+5,"3M",TRP_CenterRight);
-   Sarg_gdImageStringFT(&gdata,gdata.dimgray,GraphFont,7,0.0,LeftMargin-10-TickLength,BottomGraph-70+5,"4M",TRP_CenterRight);
-   Sarg_gdImageStringFT(&gdata,gdata.dimgray,GraphFont,7,0.0,LeftMargin-10-TickLength,BottomGraph-80+5,"5M",TRP_CenterRight);
-   Sarg_gdImageStringFT(&gdata,gdata.dimgray,GraphFont,7,0.0,LeftMargin-10-TickLength,BottomGraph-90+5,"6M",TRP_CenterRight);
-   Sarg_gdImageStringFT(&gdata,gdata.dimgray,GraphFont,7,0.0,LeftMargin-10-TickLength,BottomGraph-100+5,"7M",TRP_CenterRight);
-   Sarg_gdImageStringFT(&gdata,gdata.dimgray,GraphFont,7,0.0,LeftMargin-10-TickLength,BottomGraph-110+5,"8M",TRP_CenterRight);
-   Sarg_gdImageStringFT(&gdata,gdata.dimgray,GraphFont,7,0.0,LeftMargin-10-TickLength,BottomGraph-120+5,"9M",TRP_CenterRight);
-   Sarg_gdImageStringFT(&gdata,gdata.dimgray,GraphFont,7,0.0,LeftMargin-10-TickLength,BottomGraph-130+5,"10M",TRP_CenterRight);
-   Sarg_gdImageStringFT(&gdata,gdata.dimgray,GraphFont,7,0.0,LeftMargin-10-TickLength,BottomGraph-140+5,"15M",TRP_CenterRight);
-   Sarg_gdImageStringFT(&gdata,gdata.dimgray,GraphFont,7,0.0,LeftMargin-10-TickLength,BottomGraph-150+5,"20M",TRP_CenterRight);
-   Sarg_gdImageStringFT(&gdata,gdata.dimgray,GraphFont,7,0.0,LeftMargin-10-TickLength,BottomGraph-160+5,"30M",TRP_CenterRight);
-   Sarg_gdImageStringFT(&gdata,gdata.dimgray,GraphFont,7,0.0,LeftMargin-10-TickLength,BottomGraph-170+5,"40M",TRP_CenterRight);
-   Sarg_gdImageStringFT(&gdata,gdata.dimgray,GraphFont,7,0.0,LeftMargin-10-TickLength,BottomGraph-180+5,"50M",TRP_CenterRight);
-   Sarg_gdImageStringFT(&gdata,gdata.dimgray,GraphFont,7,0.0,LeftMargin-10-TickLength,BottomGraph-190+5,"60M",TRP_CenterRight);
-   Sarg_gdImageStringFT(&gdata,gdata.dimgray,GraphFont,7,0.0,LeftMargin-10-TickLength,BottomGraph-200+5,"70M",TRP_CenterRight);
-   Sarg_gdImageStringFT(&gdata,gdata.dimgray,GraphFont,7,0.0,LeftMargin-10-TickLength,BottomGraph-210+5,"80M",TRP_CenterRight);
-   Sarg_gdImageStringFT(&gdata,gdata.dimgray,GraphFont,7,0.0,LeftMargin-10-TickLength,BottomGraph-220+5,"90M",TRP_CenterRight);
-   Sarg_gdImageStringFT(&gdata,gdata.dimgray,GraphFont,7,0.0,LeftMargin-10-TickLength,BottomGraph-230+5,"100M",TRP_CenterRight);
-   Sarg_gdImageStringFT(&gdata,gdata.dimgray,GraphFont,7,0.0,LeftMargin-10-TickLength,BottomGraph-240+5,"200M",TRP_CenterRight);
-   Sarg_gdImageStringFT(&gdata,gdata.dimgray,GraphFont,7,0.0,LeftMargin-10-TickLength,BottomGraph-250+5,"300M",TRP_CenterRight);
-   Sarg_gdImageStringFT(&gdata,gdata.dimgray,GraphFont,7,0.0,LeftMargin-10-TickLength,BottomGraph-260+5,"400M",TRP_CenterRight);
-   Sarg_gdImageStringFT(&gdata,gdata.dimgray,GraphFont,7,0.0,LeftMargin-10-TickLength,BottomGraph-270+5,"500M",TRP_CenterRight);
-   Sarg_gdImageStringFT(&gdata,gdata.dimgray,GraphFont,7,0.0,LeftMargin-10-TickLength,BottomGraph-280+5,"600M",TRP_CenterRight);
-   Sarg_gdImageStringFT(&gdata,gdata.dimgray,GraphFont,7,0.0,LeftMargin-10-TickLength,BottomGraph-290+5,"700M",TRP_CenterRight);
-   Sarg_gdImageStringFT(&gdata,gdata.dimgray,GraphFont,7,0.0,LeftMargin-10-TickLength,BottomGraph-300+5,"800M",TRP_CenterRight);
-   Sarg_gdImageStringFT(&gdata,gdata.dimgray,GraphFont,7,0.0,LeftMargin-10-TickLength,BottomGraph-310+5,"900M",TRP_CenterRight);
-   Sarg_gdImageStringFT(&gdata,gdata.dimgray,GraphFont,7,0.0,LeftMargin-10-TickLength,BottomGraph-320+5,"1G",TRP_CenterRight);
-   Sarg_gdImageStringFT(&gdata,gdata.dimgray,GraphFont,7,0.0,LeftMargin-10-TickLength,BottomGraph-330+5,"2G",TRP_CenterRight);
-   Sarg_gdImageStringFT(&gdata,gdata.dimgray,GraphFont,7,0.0,LeftMargin-10-TickLength,BottomGraph-340+5,"3G",TRP_CenterRight);
-   Sarg_gdImageStringFT(&gdata,gdata.dimgray,GraphFont,7,0.0,LeftMargin-10-TickLength,BottomGraph-350+5,"4G",TRP_CenterRight);
-   Sarg_gdImageStringFT(&gdata,gdata.dimgray,GraphFont,7,0.0,LeftMargin-10-TickLength,BottomGraph-360+5,"5G",TRP_CenterRight);
-   if(strcmp(datetimeby,"bytes") == 0)
-      Sarg_gdImageStringFT(&gdata,gdata.black,GraphFont,10,3.141592/2,20,ImgYSize/2,_("BYTES"),TRP_CenterLeft);
-   else
-      Sarg_gdImageStringFT(&gdata,gdata.black,GraphFont,10,3.141592/2,20,ImgYSize/2,_("ELAPSED TIME"),TRP_CenterLeft);
-   Sarg_gdImageStringFT(&gdata,gdata.black,GraphFont,10,0.0,ImgXSize/2,ImgYSize-20,_("DAYS"),TRP_BottomCenter);
-
-   for (day=0 ; day<31 ; day++) {
-      if (datapoints[day]>0) {
-         x1 = 44 +((day+1) * 20);
-         bar(&gdata,x1,datapoints[day]);
-      }
-   }
-
-   if (snprintf(graph,sizeof(graph),"%s/%s/graph_day.png",outdirname,uinfo->filename)>=sizeof(graph)) {
-      debuga(_("user name too long for %s/%s/graph_day.png\n"),outdirname,uinfo->filename);
-      exit(EXIT_FAILURE);
-   }
-   if((pngout=fopen(graph,"wb"))==NULL) {
-      debuga(_("(grepday) Cannot open log file %s\n"),graph);
-      exit(EXIT_FAILURE);
-   }
-   gdImagePng(gdata.im, pngout);
-   fclose(pngout);
-   gdImageDestroy(gdata.im);
-
-   if (gdata.string) free(gdata.string);
-#ifdef USE_ICONV
-   if (gdata.localtoutf!=(iconv_t)-1)
-      iconv_close (gdata.localtoutf);
-#endif
+#endif //HAVE_GD
+}
 
+void greport_day(const struct userinfostruct *uinfo)
+{
+#ifdef HAVE_GD
+       FILE *fp_in, *fp_ou;
+       char wdirname[MAXLEN];
+       char buf[MAXLEN];
+       int day;
+       long long int llday;
+       long long int bytes;
+       long long int elap;
+       long long int bytespoints[31];
+       long long int elappoints[31];
+       struct getwordstruct gwarea;
+       struct PlotStruct pdata;
+
+       if (datetimeby==0) return;
+       if (!Graphs || GraphFont[0]=='\0') return;
+       if (snprintf(wdirname,sizeof(wdirname),"%s/%s.day",tmp,uinfo->filename)>=sizeof(wdirname)) {
+               debuga(__FILE__,__LINE__,_("User name too long to manufacture file name "));
+               debuga_more("%s/%s.day\n",tmp,uinfo->filename);
+               exit(EXIT_FAILURE);
+       }
+       if(access(wdirname, R_OK) != 0) {
+               return;
+       }
+
+       if((fp_in=fopen(wdirname,"r"))==NULL) {
+               debuga(__FILE__,__LINE__,_("Cannot open file \"%s\": %s\n"),wdirname,strerror(errno));
+               exit(EXIT_FAILURE);
+       }
+
+       memset(bytespoints,0,sizeof(bytespoints));
+       memset(elappoints,0,sizeof(elappoints));
+       while(fgets(buf,sizeof(buf),fp_in)!=NULL) {
+               fixendofline(buf);
+               getword_start(&gwarea,buf);
+               if (getword_atoll(&llday,&gwarea,'/')<0) {
+                       debuga(__FILE__,__LINE__,_("Invalid date in file \"%s\"\n"),wdirname);
+                       exit(EXIT_FAILURE);
+               }
+               day=(int)llday;
+               if (day<1 || day>31) continue;
+               if (getword_skip(20,&gwarea,'\t')<0 || getword_skip(20,&gwarea,'\t')<0) {
+                       debuga(__FILE__,__LINE__,_("Invalid entry in file \"%s\"\n"),wdirname);
+                       exit(EXIT_FAILURE);
+               }
+               if ((datetimeby & DATETIME_BYTE)!=0) {
+                       if (getword_atoll(&bytes,&gwarea,'\t')<0) {
+                               debuga(__FILE__,__LINE__,_("Invalid number of bytes in file \"%s\"\n"),wdirname);
+                               exit(EXIT_FAILURE);
+                       }
+                       bytespoints[day-1]+=bytes;
+               }
+               if ((datetimeby & DATETIME_ELAP)!=0) {
+                       if (getword_atoll(&elap,&gwarea,'\0')<0) {
+                               debuga(__FILE__,__LINE__,_("Invalid elapsed time in file \"%s\"\n"),wdirname);
+                               exit(EXIT_FAILURE);
+                       }
+                       elappoints[day-1]+=elap;
+               }
+       }
+       if (fclose(fp_in)==EOF) {
+               debuga(__FILE__,__LINE__,_("Read error in \"%s\": %s\n"),wdirname,strerror(errno));
+               exit(EXIT_FAILURE);
+       }
+
+       if (snprintf(wdirname,sizeof(wdirname),"%s/%s/graph.html",outdirname,uinfo->filename)>=sizeof(wdirname)) {
+               debuga(__FILE__,__LINE__,_("User name too long to manufacture file name "));
+               debuga_more("%s/%s/%s\n",outdirname,uinfo->filename,"graph.html");
+               exit(EXIT_FAILURE);
+       }
+       if ((fp_ou=fopen(wdirname,"wt"))==NULL) {
+               debuga(__FILE__,__LINE__,_("Cannot open file \"%s\": %s\n"),wdirname,strerror(errno));
+               exit(EXIT_FAILURE);
+       }
+       write_html_head(fp_ou,(IndexTree == INDEX_TREE_DATE) ? 4 : 2,_("Graph report"),HTML_JS_NONE);
+
+       fputs("<table class=\"report\" cellpadding=\"0\" cellspacing=\"2\">\n", fp_ou);
+       if((datetimeby & DATETIME_BYTE)!=0) {
+               memset(&pdata,0,sizeof(pdata));
+               pdata.datapoints=bytespoints;
+               pdata.npoints=31;
+               pdata.XLabel=_("DAYS");
+               pdata.ymin=50LL*1000LL;
+               pdata.ymax=5LL*1000LL*1000LL*1000LL;
+               pdata.ytype=PTG_LogBin;
+               pdata.YLabel=_("BYTES");
+               pdata.pngfile="graph_day_byte.png";
+               greport_plot(uinfo,&pdata);
+               fprintf(fp_ou,"<tr><td><img src=\"%s\" alt=\"B\"></td></tr>\n",pdata.pngfile);
+       }
+       if((datetimeby & DATETIME_ELAP)!=0) {
+               memset(&pdata,0,sizeof(pdata));
+               pdata.datapoints=elappoints;
+               pdata.npoints=31;
+               pdata.XLabel=_("DAYS");
+               pdata.ymin=0;
+               pdata.ymax=86400000;
+               pdata.ytype=PTG_Time;
+               pdata.YLabel=_("ELAPSED TIME");
+               pdata.pngfile="graph_day_elap.png";
+               greport_plot(uinfo,&pdata);
+               fprintf(fp_ou,"<tr><td><img src=\"%s\" alt=\"E\"></td></tr>\n",pdata.pngfile);
+       }
+       fputs("</table>\n",fp_ou);
+
+       write_html_trailer(fp_ou);
+       if (fclose(fp_ou)==EOF) {
+               debuga(__FILE__,__LINE__,_("Write error in \"%s\": %s\n"),wdirname,strerror(errno));
+               exit(EXIT_FAILURE);
+       }
 #endif //HAVE_GD
 
-   return;
+       return;
 }
 
 void greport_cleanup(void)
 {
 #ifdef HAVE_GD
-   gdFontCacheShutdown();
+       gdFontCacheShutdown();
+
+#ifdef USE_ICONV
+       if (localtoutf!=(iconv_t)-1)
+               iconv_close (localtoutf);
 #endif
+#endif //HAVE_GD
 }