the rrd_graph_check_vname function is not used anywhere in the code ...
[rrdtool.git] / src / rrd_graph.c
index 77a401a..cb95dd8 100644 (file)
@@ -1,5 +1,5 @@
 /****************************************************************************
- * RRDtool 1.2.12  Copyright by Tobi Oetiker, 1997-2005
+ * RRDtool 1.2.19  Copyright by Tobi Oetiker, 1997-2007
  ****************************************************************************
  * rrd__graph.c  produce graphs from data in rrdfiles
  ****************************************************************************/
@@ -7,9 +7,12 @@
 
 #include <sys/stat.h>
 
+#ifdef WIN32
+#include "strftime.h"
+#endif
 #include "rrd_tool.h"
 
-#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
+#if defined(WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
 #include <io.h>
 #include <fcntl.h>
 #endif
@@ -48,34 +51,23 @@ xlab_t xlab[] = {
     {10,                0,   TMT_MINUTE,5,  TMT_MINUTE,20, TMT_MINUTE,20,        0,"%H:%M"},
     {30,                0,   TMT_MINUTE,10, TMT_HOUR,1,    TMT_HOUR,1,           0,"%H:%M"},
     {60,                0,   TMT_MINUTE,30, TMT_HOUR,2,    TMT_HOUR,2,           0,"%H:%M"},
+    {60,          24*3600,   TMT_MINUTE,30, TMT_HOUR,2,    TMT_HOUR,4,           0,"%a %H:%M"},
     {180,               0,   TMT_HOUR,1,    TMT_HOUR,6,    TMT_HOUR,6,           0,"%H:%M"},
-    {180,       1*24*3600,   TMT_HOUR,1,    TMT_HOUR,6,    TMT_HOUR,6,           0,"%a %H:%M"},
+    {180,         24*3600,   TMT_HOUR,1,    TMT_HOUR,6,    TMT_HOUR,12,          0,"%a %H:%M"},
     /*{300,             0,   TMT_HOUR,3,    TMT_HOUR,12,   TMT_HOUR,12,    12*3600,"%a %p"},  this looks silly*/
     {600,               0,   TMT_HOUR,6,    TMT_DAY,1,     TMT_DAY,1,      24*3600,"%a"},
-    {600,       1*24*3600,   TMT_HOUR,6,    TMT_DAY,1,     TMT_DAY,1,      24*3600,"%a %d"},
-    {1800,              0,   TMT_HOUR,12,   TMT_DAY,1,     TMT_DAY,2,      24*3600,"%a"},
-    {1800,      1*24*3600,   TMT_HOUR,12,   TMT_DAY,1,     TMT_DAY,2,      24*3600,"%a %d"},
-    {3600,              0,   TMT_DAY,1,     TMT_WEEK,1,    TMT_WEEK,1,    7*24*3600,"Week %V"},
-    {3*3600,            0,   TMT_WEEK,1,    TMT_MONTH,1,   TMT_WEEK,2,    7*24*3600,"Week %V"},
+    {1200,             0,   TMT_HOUR,6,    TMT_DAY,1,     TMT_DAY,1,      24*3600,"%d"},
+    {1800,              0,   TMT_HOUR,12,   TMT_DAY,1,     TMT_DAY,2,      24*3600,"%a %d"},
+    {2400,              0,   TMT_HOUR,12,   TMT_DAY,1,     TMT_DAY,2,      24*3600,"%a"},
+    {3600,              0,   TMT_DAY,1,     TMT_WEEK,1,    TMT_WEEK,1,   7*24*3600,"Week %V"},
+    {3*3600,            0,   TMT_WEEK,1,    TMT_MONTH,1,   TMT_WEEK,2,   7*24*3600,"Week %V"},
     {6*3600,            0,   TMT_MONTH,1,   TMT_MONTH,1,   TMT_MONTH,1, 30*24*3600,"%b"},
     {48*3600,           0,   TMT_MONTH,1,   TMT_MONTH,3,   TMT_MONTH,3, 30*24*3600,"%b"},
+    {315360,            0,   TMT_MONTH,3,   TMT_YEAR,1,    TMT_YEAR,1,  365*24*3600,"%Y"},
     {10*24*3600,        0,   TMT_YEAR,1,  TMT_YEAR,1,    TMT_YEAR,1, 365*24*3600,"%y"},
     {-1,0,TMT_MONTH,0,TMT_MONTH,0,TMT_MONTH,0,0,""}
 };
 
-/* sensible logarithmic y label intervals ...
-   the first element of each row defines the possible starting points on the
-   y axis ... the other specify the */
-
-double yloglab[][12]= {{ 1e9, 1,  0,  0,  0,   0,  0,  0,  0,  0,  0,  0 },
-                      {  1e3, 1,  0,  0,  0,   0,  0,  0,  0,  0,  0,  0 },
-                      {  1e1, 1,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0 },
-                      /* {  1e1, 1,  5,  0,  0,  0,  0,  0,  0,  0,  0,  0 }, */
-                      {  1e1, 1,  2.5,  5,  7.5,  0,  0,  0,  0,  0,  0,  0 },
-                      {  1e1, 1,  2,  4,  6,  8,  0,  0,  0,  0,  0,  0 },
-                      {  1e1, 1,  2,  3,  4,  5,  6,  7,  8,  9,  0,  0 },
-                      {  0,   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0 }};
-
 /* sensible y label intervals ...*/
 
 ylab_t ylab[]= {
@@ -327,7 +319,7 @@ static char si_symbol[] = {
                     'P', /* 10e15  Peta */
                     'E', /* 10e18  Exa */
 };
-const static int si_symbcenter = 6;
+static const int si_symbcenter = 6;
 
 /* find SI magnitude symbol for the numbers on the y-axis*/
 void 
@@ -726,7 +718,7 @@ data_fetch(image_desc_t *im )
                break;
        }
        if (! skip) {
-           unsigned long  ft_step = im->gdes[i].step ;
+           unsigned long  ft_step = im->gdes[i].step ; /* ft_step will record what we got from fetch */
            
            if((rrd_fetch_fn(im->gdes[i].rrd,
                             im->gdes[i].cf,
@@ -739,7 +731,6 @@ data_fetch(image_desc_t *im )
                return -1;
            }
            im->gdes[i].data_first = 1;     
-           im->gdes[i].step = im->step;
        
            if (ft_step < im->gdes[i].step) {
                reduce_data(im->gdes[i].cf_reduce,
@@ -1073,8 +1064,9 @@ data_proc( image_desc_t *im ){
                        ** relevant for min and max
                        */
                        if (finite(paintval) && im->gdes[ii].gf != GF_TICK ) {
-                           if (isnan(minval) || paintval <  minval)
-                               minval = paintval;
+                           if ((isnan(minval) || paintval <  minval ) &&
+                             ! (im->logarithmic && paintval <= 0.0)) 
+                                       minval = paintval;
                            if (isnan(maxval) || paintval >  maxval)
                                maxval = paintval;
                        }
@@ -1096,20 +1088,30 @@ data_proc( image_desc_t *im ){
        there was no data in the graph ... this is not good ...
        lets set these to dummy values then ... */
 
-    if (isnan(minval)) minval = 0.0;
-    if (isnan(maxval)) maxval = 1.0;
+    if (im->logarithmic) {
+       if (isnan(minval)) minval = 0.2;
+       if (isnan(maxval)) maxval = 5.1;
+    }
+    else {
+       if (isnan(minval)) minval = 0.0;
+       if (isnan(maxval)) maxval = 1.0;
+    }
     
     /* adjust min and max values */
     if (isnan(im->minval) 
-       /* don't adjust low-end with log scale */
-       || ((!im->logarithmic && !im->rigid) && im->minval > minval)
-       )
-       im->minval = minval;
+       /* don't adjust low-end with log scale */ /* why not? */
+       || ((!im->rigid) && im->minval > minval)
+       ) {
+       if (im->logarithmic)
+           im->minval = minval * 0.5;
+       else
+           im->minval = minval;
+    }
     if (isnan(im->maxval) 
        || (!im->rigid && im->maxval < maxval)
        ) {
        if (im->logarithmic)
-           im->maxval = maxval * 1.1;
+           im->maxval = maxval * 2.0;
        else
            im->maxval = maxval;
     }
@@ -1230,7 +1232,7 @@ print_calc(image_desc_t *im, char ***prdata)
 {
     long i,ii,validsteps;
     double printval;
-    time_t printtime;
+    struct tm tmvdef;
     int graphelement = 0;
     long vidx;
     int max_ii;        
@@ -1238,6 +1240,9 @@ print_calc(image_desc_t *im, char ***prdata)
     char *si_symb = "";
     char *percent_s;
     int prlines = 1;
+    /* wow initializing tmvdef is quite a task :-) */
+    time_t now = time(NULL);
+    localtime_r(&now,&tmvdef);
     if (im->imginfo) prlines++;
     for(i=0;i<im->gdes_c;i++){
        switch(im->gdes[i].gf){
@@ -1255,7 +1260,7 @@ print_calc(image_desc_t *im, char ***prdata)
            vidx = im->gdes[i].vidx;
            if (im->gdes[vidx].gf==GF_VDEF) { /* simply use vals */
                printval = im->gdes[vidx].vf.val;
-               printtime = im->gdes[vidx].vf.when;
+               localtime_r(&im->gdes[vidx].vf.when,&tmvdef);
            } else { /* need to calculate max,min,avg etcetera */
                max_ii =((im->gdes[vidx].end 
                        - im->gdes[vidx].start)
@@ -1301,21 +1306,6 @@ print_calc(image_desc_t *im, char ***prdata)
                }
            } /* prepare printval */
 
-           if (!strcmp(im->gdes[i].format,"%c")) { /* VDEF time print */
-               char ctime_buf[128]; /* PS: for ctime_r, must be >= 26 chars */
-               int iii=0;
-               ctime_r(&printtime,ctime_buf); 
-               while(isprint(ctime_buf[iii])){iii++;}
-               ctime_buf[iii]='\0';
-               if (im->gdes[i].gf == GF_PRINT){
-                   (*prdata)[prlines-2] = malloc((FMT_LEG_LEN+2)*sizeof(char));
-                   sprintf((*prdata)[prlines-2],"%s (%lu)",ctime_buf,printtime);
-                   (*prdata)[prlines-1] = NULL;
-               } else {
-                   sprintf(im->gdes[i].legend,"%s (%lu)",ctime_buf,printtime);
-                   graphelement = 1;
-               }
-           } else {
            if ((percent_s = strstr(im->gdes[i].format,"%S")) != NULL) {
                /* Magfact is set to -1 upon entry to print_calc.  If it
                 * is still less than 0, then we need to run auto_scale.
@@ -1334,39 +1324,57 @@ print_calc(image_desc_t *im, char ***prdata)
                auto_scale(im,&printval,&si_symb,&magfact);
            }
 
-           if (im->gdes[i].gf == GF_PRINT){
+            if (im->gdes[i].gf == GF_PRINT){
                (*prdata)[prlines-2] = malloc((FMT_LEG_LEN+2)*sizeof(char));
                (*prdata)[prlines-1] = NULL;
-               if (bad_format(im->gdes[i].format)) {
-                       rrd_set_error("bad format for PRINT in '%s'", im->gdes[i].format);
+                if (im->gdes[i].strftm){
+                       strftime((*prdata)[prlines-2],FMT_LEG_LEN,im->gdes[i].format,&tmvdef);
+                } else {
+                 if (bad_format(im->gdes[i].format)) {
+                       rrd_set_error("bad format for PRINT in '%s'", im->gdes[i].format);
                        return -1;
-               }
+                 }
+
 #ifdef HAVE_SNPRINTF
-               snprintf((*prdata)[prlines-2],FMT_LEG_LEN,im->gdes[i].format,printval,si_symb);
+                 snprintf((*prdata)[prlines-2],FMT_LEG_LEN,im->gdes[i].format,printval,si_symb);
 #else
-               sprintf((*prdata)[prlines-2],im->gdes[i].format,printval,si_symb);
+                 sprintf((*prdata)[prlines-2],im->gdes[i].format,printval,si_symb);
 #endif
-           } else {
+               }
+             } else {
                /* GF_GPRINT */
 
-               if (bad_format(im->gdes[i].format)) {
+                if (im->gdes[i].strftm){
+                       strftime(im->gdes[i].legend,FMT_LEG_LEN,im->gdes[i].format,&tmvdef);
+                } else {
+                 if (bad_format(im->gdes[i].format)) {
                        rrd_set_error("bad format for GPRINT in '%s'", im->gdes[i].format);
                        return -1;
-               }
+                 }
 #ifdef HAVE_SNPRINTF
-               snprintf(im->gdes[i].legend,FMT_LEG_LEN-2,im->gdes[i].format,printval,si_symb);
+                 snprintf(im->gdes[i].legend,FMT_LEG_LEN-2,im->gdes[i].format,printval,si_symb);
 #else
-               sprintf(im->gdes[i].legend,im->gdes[i].format,printval,si_symb);
+                 sprintf(im->gdes[i].legend,im->gdes[i].format,printval,si_symb);
 #endif
-               graphelement = 1;
-           }
-           }
+                }
+               graphelement = 1;               
+           }       
            break;
        case GF_LINE:
        case GF_AREA:
        case GF_TICK:
+           graphelement = 1;
+           break;
        case GF_HRULE:
+            if(isnan(im->gdes[i].yrule)) { /* we must set this here or the legend printer can not decide to print the legend */
+               im->gdes[i].yrule=im->gdes[im->gdes[i].vidx].vf.val;
+            };
+           graphelement = 1;
+           break;
        case GF_VRULE:
+            if(im->gdes[i].xrule == 0) { /* again ... the legend printer needs it*/
+              im->gdes[i].xrule = im->gdes[im->gdes[i].vidx].vf.when;
+            };
            graphelement = 1;
            break;
         case GF_COMMENT:
@@ -1399,6 +1407,7 @@ leg_place(image_desc_t *im)
     int   fill=0, fill_last;
     int   leg_c = 0;
     int   leg_x = border, leg_y = im->yimg;
+    int   leg_y_prev = im->yimg;
     int   leg_cc;
     int   glue = 0;
     int   i,ii, mark = 0;
@@ -1437,7 +1446,27 @@ leg_place(image_desc_t *im)
        } else {
            prt_fctn = '\0';
        }
+       /* only valid control codes */
+        if (prt_fctn != 'l' && 
+           prt_fctn != 'n' && /* a synonym for l */
+           prt_fctn != 'r' &&
+           prt_fctn != 'j' &&
+           prt_fctn != 'c' &&
+           prt_fctn != 's' &&
+            prt_fctn != 't' &&
+            prt_fctn != '\0' &&
+            prt_fctn != 'g' ) {
+              free(legspace);
+              rrd_set_error("Unknown control code at the end of '%s\\%c'",im->gdes[i].legend,prt_fctn);
+                      return -1;
+
+       }
+
         /* remove exess space */
+        if ( prt_fctn == 'n' ){
+            prt_fctn='l';
+        }
+
         while (prt_fctn=='g' && 
               leg_cc > 0 && 
               im->gdes[i].legend[leg_cc-1]==' '){
@@ -1507,14 +1536,22 @@ leg_place(image_desc_t *im)
                   + legspace[ii]
                   + glue;
            }                   
-           leg_y += im->text_prop[TEXT_PROP_LEGEND].size*1.8;
-           if (prt_fctn == 's') leg_y -=  im->text_prop[TEXT_PROP_LEGEND].size;           
+            leg_y_prev = leg_y;
+            /* only add y space if there was text on the line */
+            if (leg_x > border || prt_fctn == 's')            
+              leg_y += im->text_prop[TEXT_PROP_LEGEND].size*1.8;
+           if (prt_fctn == 's')
+               leg_y -=  im->text_prop[TEXT_PROP_LEGEND].size;    
            fill = 0;
            leg_c = 0;
            mark = ii;
        }          
     }
-    im->yimg = leg_y;
+    im->yimg = leg_y_prev;
+    /* if we did place some legends we have to add vertical space */
+    if (leg_y != im->yimg){
+       im->yimg += im->text_prop[TEXT_PROP_LEGEND].size*1.8;
+    }
     free(legspace);
   }
   return 0;
@@ -1682,99 +1719,260 @@ int draw_horizontal_grid(image_desc_t *im)
     return 1;
 }
 
+/* this is frexp for base 10 */
+double frexp10(double, double *);
+double frexp10(double x, double *e) {
+    double mnt;
+    int iexp;
+
+    iexp = floor(log(fabs(x)) / log(10));
+    mnt = x / pow(10.0, iexp);
+    if(mnt >= 10.0) {
+       iexp++;
+       mnt = x / pow(10.0, iexp);
+    }
+    *e = iexp;
+    return mnt;
+}
+
+static int AlmostEqual2sComplement (float A, float B, int maxUlps)
+{
+
+    int aInt = *(int*)&A;
+    int bInt = *(int*)&B;
+    int intDiff;
+    /* Make sure maxUlps is non-negative and small enough that the
+       default NAN won't compare as equal to anything.  */
+
+    /* assert(maxUlps > 0 && maxUlps < 4 * 1024 * 1024); */
+
+    /* Make aInt lexicographically ordered as a twos-complement int */
+
+    if (aInt < 0)
+        aInt = 0x80000000l - aInt;
+
+    /* Make bInt lexicographically ordered as a twos-complement int */
+
+    if (bInt < 0)
+        bInt = 0x80000000l - bInt;
+
+    intDiff = abs(aInt - bInt);
+
+    if (intDiff <= maxUlps)
+        return 1;
+
+    return 0;
+}
+
 /* logaritmic horizontal grid */
 int
 horizontal_log_grid(image_desc_t   *im)   
 {
-    double   pixpex;
-    int      ii,i;
-    int      minoridx=0, majoridx=0;
-    char     graph_label[100];
-    double   X0,X1,Y0;   
-    double   value, pixperstep, minstep;
+    double yloglab[][10] = {
+       {1.0, 10., 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
+       {1.0, 5.0, 10., 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
+       {1.0, 2.0, 5.0, 7.0, 10., 0.0, 0.0, 0.0, 0.0, 0.0},
+       {1.0, 2.0, 4.0, 6.0, 8.0, 10., 0.0, 0.0, 0.0, 0.0},
+       {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.},
+       {0,0,0,0,0, 0,0,0,0,0} /* last line */ };
+
+    int i, j, val_exp, min_exp;
+    double nex;                /* number of decades in data */
+    double logscale;   /* scale in logarithmic space */
+    int exfrac = 1;    /* decade spacing */
+    int mid = -1;      /* row in yloglab for major grid */
+    double mspac;      /* smallest major grid spacing (pixels) */
+    int flab;          /* first value in yloglab to use */
+    double value, tmp, pre_value;
+    double X0,X1,Y0;   
+    char graph_label[100];
 
-    /* find grid spaceing */
-    pixpex= (double)im->ysize / (log10(im->maxval) - log10(im->minval));
+    nex = log10(im->maxval / im->minval);
+    logscale = im->ysize / nex;
 
-       if (isnan(pixpex)) {
-               return 0;
-       }
+    /* major spacing for data with high dynamic range */
+    while(logscale * exfrac < 3 * im->text_prop[TEXT_PROP_LEGEND].size) {
+       if(exfrac == 1) exfrac = 3;
+       else exfrac += 3;
+    }
 
-    for(i=0;yloglab[i][0] > 0;i++){
-       minstep = log10(yloglab[i][0]);
-       for(ii=1;yloglab[i][ii+1] > 0;ii++){
-           if(yloglab[i][ii+2]==0){
-               minstep = log10(yloglab[i][ii+1])-log10(yloglab[i][ii]);
-               break;
+    /* major spacing for less dynamic data */
+    do {
+       /* search best row in yloglab */
+       mid++;
+       for(i = 0; yloglab[mid][i + 1] < 10.0; i++);
+       mspac = logscale * log10(10.0 / yloglab[mid][i]);
+    } while(mspac > 2 * im->text_prop[TEXT_PROP_LEGEND].size && yloglab[mid][0] > 0);
+    if(mid) mid--;
+
+    /* find first value in yloglab */
+    for(flab = 0; yloglab[mid][flab] < 10 && frexp10(im->minval, &tmp) > yloglab[mid][flab] ; flab++);
+    if(yloglab[mid][flab] == 10.0) {
+       tmp += 1.0;
+       flab = 0;
+    }
+    val_exp = tmp;
+    if(val_exp % exfrac) val_exp += abs(-val_exp % exfrac);
+
+    X0=im->xorigin;
+    X1=im->xorigin+im->xsize;
+
+    /* draw grid */
+    pre_value = DNAN;
+    while(1) {       
+
+       value = yloglab[mid][flab] * pow(10.0, val_exp);
+        if (  AlmostEqual2sComplement(value,pre_value,4) ) break; /* it seems we are not converging */
+
+        pre_value = value;
+
+       Y0 = ytr(im, value);
+       if(Y0 <= im->yorigin - im->ysize) break;
+
+       /* major grid line */
+       gfx_new_dashed_line ( im->canvas,
+           X0-2,Y0,
+           X1+2,Y0,
+           MGRIDWIDTH, im->graph_col[GRC_MGRID],
+           im->grid_dash_on, im->grid_dash_off);
+
+       /* label */
+       if (im->extra_flags & FORCE_UNITS_SI) {
+           int scale;
+           double pvalue;
+           char symbol;
+
+           scale = floor(val_exp / 3.0);
+           if( value >= 1.0 ) pvalue = pow(10.0, val_exp % 3);
+           else pvalue = pow(10.0, ((val_exp + 1) % 3) + 2);
+           pvalue *= yloglab[mid][flab];
+
+           if ( ((scale+si_symbcenter) < (int)sizeof(si_symbol)) &&
+               ((scale+si_symbcenter) >= 0) )
+               symbol = si_symbol[scale+si_symbcenter];
+           else
+               symbol = '?';
+
+               sprintf(graph_label,"%3.0f %c", pvalue, symbol);
+        } else
+           sprintf(graph_label,"%3.0e", value);
+       gfx_new_text ( im->canvas,
+           X0-im->text_prop[TEXT_PROP_AXIS].size, Y0,
+           im->graph_col[GRC_FONT],
+           im->text_prop[TEXT_PROP_AXIS].font,
+           im->text_prop[TEXT_PROP_AXIS].size,
+           im->tabwidth,0.0, GFX_H_RIGHT, GFX_V_CENTER,
+           graph_label );
+
+       /* minor grid */
+       if(mid < 4 && exfrac == 1) {
+           /* find first and last minor line behind current major line
+            * i is the first line and j tha last */
+           if(flab == 0) {
+               min_exp = val_exp - 1;
+               for(i = 1; yloglab[mid][i] < 10.0; i++);
+               i = yloglab[mid][i - 1] + 1;
+               j = 10;
+           }
+           else {
+               min_exp = val_exp;
+               i = yloglab[mid][flab - 1] + 1;
+               j = yloglab[mid][flab];
            }
+
+           /* draw minor lines below current major line */
+           for(; i < j; i++) {
+
+               value = i * pow(10.0, min_exp);
+               if(value < im->minval) continue;
+
+               Y0 = ytr(im, value);
+               if(Y0 <= im->yorigin - im->ysize) break;
+
+               /* draw lines */
+               gfx_new_dashed_line ( im->canvas,
+                   X0-1,Y0,
+                   X1+1,Y0,
+                   GRIDWIDTH, im->graph_col[GRC_GRID],
+                   im->grid_dash_on, im->grid_dash_off);
+           }
+       }
+       else if(exfrac > 1) {
+           for(i = val_exp - exfrac / 3 * 2; i < val_exp; i += exfrac / 3) {
+               value = pow(10.0, i);
+               if(value < im->minval) continue;
+
+               Y0 = ytr(im, value);
+               if(Y0 <= im->yorigin - im->ysize) break;
+
+               /* draw lines */
+               gfx_new_dashed_line ( im->canvas,
+                   X0-1,Y0,
+                   X1+1,Y0,
+                   GRIDWIDTH, im->graph_col[GRC_GRID],
+                   im->grid_dash_on, im->grid_dash_off);
+           }
+       }
+
+       /* next decade */
+       if(yloglab[mid][++flab] == 10.0) {
+           flab = 0;
+           val_exp += exfrac;
        }
-       pixperstep = pixpex * minstep;
-       if(pixperstep > 5){minoridx = i;}
-       if(pixperstep > 2 *  im->text_prop[TEXT_PROP_LEGEND].size){majoridx = i;}
     }
-   
-   X0=im->xorigin;
-   X1=im->xorigin+im->xsize;
-    /* paint minor grid */
-    for (value = pow((double)10, log10(im->minval) 
-                         - fmod(log10(im->minval),log10(yloglab[minoridx][0])));
-        value  <= im->maxval;
-        value *= yloglab[minoridx][0]){
-       if (value < im->minval) continue;
-       i=0;    
-       while(yloglab[minoridx][++i] > 0){          
-          Y0 = ytr(im,value * yloglab[minoridx][i]);
-          if (Y0 <= im->yorigin - im->ysize) break;
-          gfx_new_dashed_line ( im->canvas,
-                         X0-1,Y0,
-                         X1+1,Y0,
-                         GRIDWIDTH, im->graph_col[GRC_GRID],
-                         im->grid_dash_on, im->grid_dash_off);
+
+    /* draw minor lines after highest major line */
+    if(mid < 4 && exfrac == 1) {
+       /* find first and last minor line below current major line
+        * i is the first line and j tha last */
+       if(flab == 0) {
+           min_exp = val_exp - 1;
+           for(i = 1; yloglab[mid][i] < 10.0; i++);
+           i = yloglab[mid][i - 1] + 1;
+           j = 10;
+       }
+       else {
+           min_exp = val_exp;
+           i = yloglab[mid][flab - 1] + 1;
+           j = yloglab[mid][flab];
+       }
+
+       /* draw minor lines below current major line */
+       for(; i < j; i++) {
+
+           value = i * pow(10.0, min_exp);
+           if(value < im->minval) continue;
+
+           Y0 = ytr(im, value);
+           if(Y0 <= im->yorigin - im->ysize) break;
+
+           /* draw lines */
+           gfx_new_dashed_line ( im->canvas,
+               X0-1,Y0,
+               X1+1,Y0,
+               GRIDWIDTH, im->graph_col[GRC_GRID],
+               im->grid_dash_on, im->grid_dash_off);
+       }
+    }
+    /* fancy minor gridlines */
+    else if(exfrac > 1) {
+       for(i = val_exp - exfrac / 3 * 2; i < val_exp; i += exfrac / 3) {
+           value = pow(10.0, i);
+           if(value < im->minval) continue;
+
+           Y0 = ytr(im, value);
+           if(Y0 <= im->yorigin - im->ysize) break;
+
+           /* draw lines */
+           gfx_new_dashed_line ( im->canvas,
+               X0-1,Y0,
+               X1+1,Y0,
+               GRIDWIDTH, im->graph_col[GRC_GRID],
+               im->grid_dash_on, im->grid_dash_off);
        }
     }
 
-    /* paint major grid and labels*/
-    for (value = pow((double)10, log10(im->minval) 
-                         - fmod(log10(im->minval),log10(yloglab[majoridx][0])));
-        value <= im->maxval;
-        value *= yloglab[majoridx][0]){
-       if (value < im->minval) continue;
-       i=0;    
-       while(yloglab[majoridx][++i] > 0){          
-          Y0 = ytr(im,value * yloglab[majoridx][i]);    
-          if (Y0 <= im->yorigin - im->ysize) break;
-          gfx_new_dashed_line ( im->canvas,
-                         X0-2,Y0,
-                         X1+2,Y0,
-                         MGRIDWIDTH, im->graph_col[GRC_MGRID],
-                         im->grid_dash_on, im->grid_dash_off);
-
-          if (im->extra_flags & FORCE_UNITS_SI) {
-             double pvalue = value * yloglab[majoridx][i];
-             double scale = floor( log10( fabs(pvalue)) / 3);
-             pvalue /= pow(10, 3*scale);
-
-             char symbol;
-             if ( ((scale+si_symbcenter) < sizeof(si_symbol)) &&
-                  ((scale+si_symbcenter) >= 0) )
-                symbol = si_symbol[(int)scale+si_symbcenter];
-             else
-                symbol = '?';
-
-             sprintf(graph_label,"%3.0f %c", pvalue, symbol);
-          } else
-             sprintf(graph_label,"%3.0e",value * yloglab[majoridx][i]);
-
-          gfx_new_text ( im->canvas,
-                         X0-im->text_prop[TEXT_PROP_AXIS].size, Y0,
-                         im->graph_col[GRC_FONT],
-                         im->text_prop[TEXT_PROP_AXIS].font,
-                         im->text_prop[TEXT_PROP_AXIS].size,
-                         im->tabwidth,0.0, GFX_H_RIGHT, GFX_V_CENTER,
-                         graph_label );
-       } 
-    }
-       return 1;
+    return 1;
 }
 
 
@@ -2226,7 +2424,7 @@ graph_size_location(image_desc_t *im, int elements
        if (im->draw_x_grid) {
            Yxlabel=im->text_prop[TEXT_PROP_AXIS].size *2.5;
        }
-       if (im->draw_y_grid) {
+       if (im->draw_y_grid || im->forceleftspace ) {
            Xylabel=gfx_get_text_width(im->canvas, 0,
                        im->text_prop[TEXT_PROP_AXIS].font,
                        im->text_prop[TEXT_PROP_AXIS].size,
@@ -2346,34 +2544,6 @@ graph_size_location(image_desc_t *im, int elements
 /* yes we are loosing precision by doing tos with floats instead of doubles
    but it seems more stable this way. */
    
-static int AlmostEqual2sComplement (float A, float B, int maxUlps)
-{
-
-    int aInt = *(int*)&A;
-    int bInt = *(int*)&B;
-    int intDiff;
-    /* Make sure maxUlps is non-negative and small enough that the
-       default NAN won't compare as equal to anything.  */
-
-    /* assert(maxUlps > 0 && maxUlps < 4 * 1024 * 1024); */
-
-    /* Make aInt lexicographically ordered as a twos-complement int */
-
-    if (aInt < 0)
-        aInt = 0x80000000l - aInt;
-
-    /* Make bInt lexicographically ordered as a twos-complement int */
-
-    if (bInt < 0)
-        bInt = 0x80000000l - bInt;
-
-    intDiff = abs(aInt - bInt);
-
-    if (intDiff <= maxUlps)
-        return 1;
-
-    return 0;
-}
 
 /* draw that picture thing ... */
 int
@@ -2730,9 +2900,6 @@ graph_paint(image_desc_t *im, char ***calcpr)
     
     switch(im->gdes[i].gf){
     case GF_HRULE:
-      if(isnan(im->gdes[i].yrule)) { /* fetch variable */
-        im->gdes[i].yrule = im->gdes[im->gdes[i].vidx].vf.val;
-      };
       if(im->gdes[i].yrule >= im->minval
          && im->gdes[i].yrule <= im->maxval)
         gfx_new_line(im->canvas,
@@ -2741,9 +2908,6 @@ graph_paint(image_desc_t *im, char ***calcpr)
                      1.0,im->gdes[i].col); 
       break;
     case GF_VRULE:
-      if(im->gdes[i].xrule == 0) { /* fetch variable */
-        im->gdes[i].xrule = im->gdes[im->gdes[i].vidx].vf.when;
-      };
       if(im->gdes[i].xrule >= im->start
          && im->gdes[i].xrule <= im->end)
         gfx_new_line(im->canvas,
@@ -2811,6 +2975,7 @@ gdes_alloc(image_desc_t *im){
     im->gdes[im->gdes_c-1].col = 0x0;
     im->gdes[im->gdes_c-1].legend[0]='\0';
     im->gdes[im->gdes_c-1].format[0]='\0';
+    im->gdes[im->gdes_c-1].strftm=0;   
     im->gdes[im->gdes_c-1].rrd[0]='\0';
     im->gdes[im->gdes_c-1].ds=-1;    
     im->gdes[im->gdes_c-1].cf_reduce=CF_AVERAGE;    
@@ -2950,6 +3115,7 @@ rrd_graph_init(image_desc_t *im)
     im->maxval = DNAN;    
     im->unitsexponent= 9999;
     im->unitslength= 6; 
+    im->forceleftspace = 0;
     im->symbol = ' ';
     im->viewfactor = 1.0;
     im->extra_flags= 0;
@@ -3119,6 +3285,7 @@ rrd_graph_options(int argc, char *argv[],image_desc_t *im)
            break;
        case 'L':
            im->unitslength = atoi(optarg);
+            im->forceleftspace = 1;
            break;
        case 'T':
            im->tabwidth = atof(optarg);
@@ -3256,8 +3423,6 @@ rrd_graph_options(int argc, char *argv[],image_desc_t *im)
 
        case 'o':
            im->logarithmic = 1;
-           if (isnan(im->minval))
-               im->minval=1;
            break;
         case 'c':
             if(sscanf(optarg,
@@ -3380,7 +3545,7 @@ rrd_graph_options(int argc, char *argv[],image_desc_t *im)
        return;
     }
 
-    if (im->logarithmic == 1 && (im->minval <= 0 || isnan(im->minval))){
+    if (im->logarithmic == 1 && im->minval <= 0){
        rrd_set_error("for a logarithmic yaxis you must specify a lower-limit > 0");    
        return;
     }
@@ -3407,15 +3572,6 @@ rrd_graph_options(int argc, char *argv[],image_desc_t *im)
 }
 
 int
-rrd_graph_check_vname(image_desc_t *im, char *varname, char *err)
-{
-    if ((im->gdes[im->gdes_c-1].vidx=find_var(im,varname))==-1) {
-       rrd_set_error("Unknown variable '%s' in %s",varname,err);
-       return -1;
-    }
-    return 0;
-}
-int
 rrd_graph_color(image_desc_t *im, char *var, char *err, int optional)
 {
     char *color;
@@ -3673,7 +3829,7 @@ printf("DEBUG: %3li:%10.2f %c\n",step,array[step],step==field?'*':' ');
            if (cnt) {
                if (dst->vf.op == VDEF_TOTAL) {
                    dst->vf.val  = sum*src->step;
-                   dst->vf.when = cnt*src->step;       /* not really "when" */
+                   dst->vf.when = 0;   /* no time component */
                } else {
                    dst->vf.val = sum/cnt;
                    dst->vf.when = 0;   /* no time component */
@@ -3761,21 +3917,21 @@ printf("DEBUG: %3li:%10.2f %c\n",step,array[step],step==field?'*':' ');
            if (cnt) {
                    if (dst->vf.op == VDEF_LSLSLOPE) {
                        dst->vf.val  = slope;
-                       dst->vf.when = cnt*src->step;
+                       dst->vf.when = 0;
                    } else if (dst->vf.op == VDEF_LSLINT)  {
                        dst->vf.val = y_intercept;
-                       dst->vf.when = cnt*src->step;
+                       dst->vf.when = 0;
                    } else if (dst->vf.op == VDEF_LSLCORREL)  {
                        dst->vf.val = correl;
-                       dst->vf.when = cnt*src->step;
+                       dst->vf.when = 0;
                    };
                
            } else {
                dst->vf.val  = DNAN;
                dst->vf.when = 0;
            }
-           }
-           break;
+       }
+       break;
     }
     return 0;
 }