* fix transparency rendering by rewinding the paths propperly
[rrdtool.git] / src / rrd_graph.c
index 313c518..0279d49 100644 (file)
@@ -1,80 +1,48 @@
 /****************************************************************************
- * RRDtool 1.0.33  Copyright Tobias Oetiker, 1997 - 2000
+ * RRDtool 1.2rc2  Copyright by Tobi Oetiker, 1997-2005
  ****************************************************************************
- * rrd__graph.c  make creates ne rrds
+ * rrd__graph.c  produce graphs from data in rrdfiles
  ****************************************************************************/
 
-#include "rrd_tool.h"
-#include <gd.h>
-#include <gdlucidan10.h>
-#include <gdlucidab12.h>
+
 #include <sys/stat.h>
+
+#include "rrd_tool.h"
+
 #ifdef WIN32
 #include <io.h>
 #include <fcntl.h>
 #endif
-#include "rrd_rpncalc.h"
-
-#define SmallFont gdLucidaNormal10
-#define LargeFont gdLucidaBold12
-
-/* #define DEBUG */
 
-#ifdef DEBUG
-# define DPRINT(x)    (void)(printf x, printf("\n"))
-#else
-# define DPRINT(x)
+#ifdef HAVE_TIME_H
+#include <time.h>
 #endif
 
-#define DEF_NAM_FMT "%29[_A-Za-z0-9]"
+#ifdef HAVE_LOCALE_H
+#include <locale.h>
+#endif
 
-enum tmt_en {TMT_SECOND=0,TMT_MINUTE,TMT_HOUR,TMT_DAY,
-            TMT_WEEK,TMT_MONTH,TMT_YEAR};
+#include "rrd_graph.h"
 
-enum grc_en {GRC_CANVAS=0,GRC_BACK,GRC_SHADEA,GRC_SHADEB,
-            GRC_GRID,GRC_MGRID,GRC_FONT,GRC_FRAME,GRC_ARROW,__GRC_END__};
+/* some constant definitions */
 
 
-enum gf_en {GF_PRINT=0,GF_GPRINT,GF_COMMENT,GF_HRULE,GF_VRULE,GF_LINE1,
-           GF_LINE2,GF_LINE3,GF_AREA,GF_STACK,GF_TICK,
-           GF_DEF, GF_CDEF, GF_VDEF};
+#ifdef WIN32
+char rrd_win_default_font[80];
+#endif
 
-enum if_en {IF_GIF=0,IF_PNG=1};
+#ifndef RRD_DEFAULT_FONT
+/* there is special code later to pick Cour.ttf when running on windows */
+#define RRD_DEFAULT_FONT "VeraMono.ttf"
+#endif
 
-enum vdef_op_en {
-                VDEF_MAXIMUM   /* like the MAX in (G)PRINT */
-               ,VDEF_MINIMUM   /* like the MIN in (G)PRINT */
-               ,VDEF_AVERAGE   /* like the AVERAGE in (G)PRINT */
-               ,VDEF_PERCENT   /* Nth percentile */
-               ,VDEF_FIRST     /* first non-unknown value and time */
-               ,VDEF_LAST      /* last  non-unknown value and time */
-               };
-typedef struct vdef_t {
-    enum vdef_op_en    op;
-    double             param;  /* parameter for function, if applicable */
-    double             val;    /* resulting value */
-    time_t             when;   /* timestamp, if applicable */
-} vdef_t;
-
-typedef struct col_trip_t {
-    int red; /* red = -1 is no color */
-    int green;
-    int blue;
-    int i; /* color index assigned in gif image i=-1 is unasigned*/
-} col_trip_t;
-
-
-typedef struct xlab_t {
-    long         minsec;       /* minimum sec per pix */
-    enum tmt_en  gridtm;       /* grid interval in what ?*/
-    long         gridst;       /* how many whats per grid*/
-    enum tmt_en  mgridtm;      /* label interval in what ?*/
-    long         mgridst;      /* how many whats per label*/
-    enum tmt_en  labtm;        /* label interval in what ?*/
-    long         labst;        /* how many whats per label*/
-    long         precis;       /* label precision -> label placement*/
-    char         *stst;        /* strftime string*/
-} xlab_t;
+text_prop_t text_prop[] = {   
+     { 9.0, RRD_DEFAULT_FONT }, /* default */
+     { 11.0, RRD_DEFAULT_FONT }, /* title */
+     { 8.0,  RRD_DEFAULT_FONT }, /* axis */
+     { 9.0, RRD_DEFAULT_FONT }, /* unit */
+     { 9.0, RRD_DEFAULT_FONT }  /* legend */
+};
 
 xlab_t xlab[] = {
     {0,        TMT_SECOND,30, TMT_MINUTE,5,  TMT_MINUTE,5,         0,"%H:%M"},
@@ -87,8 +55,8 @@ xlab_t xlab[] = {
     /*{300,      TMT_HOUR,3,    TMT_HOUR,12,   TMT_HOUR,12,    12*3600,"%a %p"},  this looks silly*/
     {600,      TMT_HOUR,6,    TMT_DAY,1,     TMT_DAY,1,      24*3600,"%a"},
     {1800,     TMT_HOUR,12,   TMT_DAY,1,     TMT_DAY,2,      24*3600,"%a"},
-    {3600,     TMT_DAY,1,     TMT_WEEK,1,     TMT_WEEK,1,    7*24*3600,"Week %W"},
-    {3*3600,   TMT_WEEK,1,      TMT_MONTH,1,     TMT_WEEK,2,    7*24*3600,"Week %W"},
+    {3600,     TMT_DAY,1,     TMT_WEEK,1,     TMT_WEEK,1,    7*24*3600,"Week %V"},
+    {3*3600,   TMT_WEEK,1,      TMT_MONTH,1,     TMT_WEEK,2,    7*24*3600,"Week %V"},
     {6*3600,   TMT_MONTH,1,   TMT_MONTH,1,   TMT_MONTH,1, 30*24*3600,"%b"},
     {48*3600,  TMT_MONTH,1,   TMT_MONTH,3,   TMT_MONTH,3, 30*24*3600,"%b"},
     {10*24*3600, TMT_YEAR,1,  TMT_YEAR,1,    TMT_YEAR,1, 365*24*3600,"%y"},
@@ -110,11 +78,6 @@ double yloglab[][12]= {{ 1e9, 1,  0,  0,  0,   0,  0,  0,  0,  0,  0,  0 },
 
 /* sensible y label intervals ...*/
 
-typedef struct ylab_t {
-    double   grid;    /* grid spacing */
-    int      lfac[4]; /* associated label spacing*/
-} ylab_t;
-
 ylab_t ylab[]= {
     {0.1, {1,2, 5,10}},
     {0.2, {1,5,10,20}},
@@ -131,151 +94,28 @@ ylab_t ylab[]= {
     {0.0,   {0,0,0,0}}};
 
 
-
-col_trip_t graph_col[] = { /* default colors */
-    {255,255,255,-1},   /* canvas */
-    {245,245,245,-1},   /* background */
-    {200,200,200,-1},   /* shade A    */
-    {150,150,150,-1},   /* shade B    */
-    {140,140,140,-1},      /* grid */
-    {130,30,30,-1},      /* major grid */
-    {0,0,0,-1},         /* font */     
-    {0,0,0,-1},         /* frame */
-    {255,0,0,-1}       /*arrow*/
+gfx_color_t graph_col[] =   /* default colors */
+{    0xFFFFFFFF,   /* canvas     */
+     0xF0F0F0FF,   /* background */
+     0xD0D0D0FF,   /* shade A    */
+     0xA0A0A0FF,   /* shade B    */
+     0x90909080,   /* grid       */
+     0xE0505080,   /* major grid */
+     0x000000FF,   /* font       */ 
+     0xFF0000FF,   /* arrow      */
+     0x404040FF    /* axis       */
 };
 
-/* this structure describes the elements which can make up a graph.
-   because they are quite diverse, not all elements will use all the
-   possible parts of the structure. */
-#ifdef HAVE_SNPRINTF
-#define FMT_LEG_LEN 200
+
+/* #define DEBUG */
+
+#ifdef DEBUG
+# define DPRINT(x)    (void)(printf x, printf("\n"))
 #else
-#define FMT_LEG_LEN 2000
+# define DPRINT(x)
 #endif
 
-typedef  struct graph_desc_t {
-    enum gf_en     gf;         /* graphing function */
-    char           vname[30];  /* name of the variable */
-    long           vidx;       /* gdes reference */
-    char           rrd[255];   /* name of the rrd_file containing data */
-    char           ds_nam[DS_NAM_SIZE]; /* data source name */
-    long           ds;         /* data source number */
-    enum cf_en     cf;         /* consolidation function */
-    col_trip_t     col;        /* graph color */
-    char           format[FMT_LEG_LEN+5]; /* format for PRINT AND GPRINT */
-    char           legend[FMT_LEG_LEN+5]; /* legend*/
-    gdPoint        legloc;     /* location of legend */   
-    double         yrule;      /* value for y rule line and for VDEF */
-    time_t         xrule;      /* time for x rule line and for VDEF */
-    vdef_t         vf;         /* instruction for VDEF function */
-    rpnp_t         *rpnp;     /* instructions for CDEF function */
-
-    /* description of data fetched for the graph element */
-    time_t         start,end; /* timestaps for first and last data element */
-    unsigned long  step;      /* time between samples */
-    unsigned long  ds_cnt; /* how many data sources are there in the fetch */
-    long           data_first; /* first pointer to this data */
-    char           **ds_namv; /* name of datasources  in the fetch. */
-    rrd_value_t    *data; /* the raw data drawn from the rrd */
-    rrd_value_t    *p_data; /* processed data, xsize elments */
-
-} graph_desc_t;
-
-#define ALTYGRID          0x01  /* use alternative y grid algorithm */
-#define ALTAUTOSCALE      0x02  /* use alternative algorithm to find lower and upper bounds */
-#define ALTAUTOSCALE_MAX  0x04  /* use alternative algorithm to find upper bounds */
-#define NOLEGEND          0x08  /* use no legend */
-
-typedef struct image_desc_t {
-
-    /* configuration of graph */
-
-    char           graphfile[MAXPATH]; /* filename for graphic */
-    long           xsize,ysize;    /* graph area size in pixels */
-    col_trip_t     graph_col[__GRC_END__]; /* real colors for the graph */   
-    char           ylegend[200];   /* legend along the yaxis */
-    char           title[200];     /* title for graph */
-    int            draw_x_grid;      /* no x-grid at all */
-    int            draw_y_grid;      /* no x-grid at all */
-    xlab_t         xlab_user;      /* user defined labeling for xaxis */
-    char           xlab_form[200]; /* format for the label on the xaxis */
-
-    double         ygridstep;      /* user defined step for y grid */
-    int            ylabfact;       /* every how many y grid shall a label be written ? */
-
-    time_t         start,end;      /* what time does the graph cover */
-    unsigned long           step;           /* any preference for the default step ? */
-    rrd_value_t    minval,maxval;  /* extreme values in the data */
-    int            rigid;          /* do not expand range even with 
-                                     values outside */
-    char*          imginfo;         /* construct an <IMG ... tag and return 
-                                     as first retval */
-    int            lazy;           /* only update the gif if there is reasonable
-                                     probablility that the existing one is out of date */
-    int            logarithmic;    /* scale the yaxis logarithmic */
-    enum if_en     imgformat;         /* image format */
-    
-    /* status information */
-           
-    long           xorigin,yorigin;/* where is (0,0) of the graph */
-    long           xgif,ygif;      /* total size of the gif */
-    int            interlaced;     /* will the graph be interlaced? */
-    double         magfact;        /* numerical magnitude*/
-    long         base;            /* 1000 or 1024 depending on what we graph */
-    char           symbol;         /* magnitude symbol for y-axis */
-    int            unitsexponent;    /* 10*exponent for units on y-asis */
-    int            extra_flags;    /* flags for boolean options */
-    /* data elements */
-
-    long  prt_c;                  /* number of print elements */
-    long  gdes_c;                  /* number of graphics elements */
-    graph_desc_t   *gdes;          /* points to an array of graph elements */
-
-} image_desc_t;
-
-/* Prototypes */
-int xtr(image_desc_t *,time_t);
-int ytr(image_desc_t *, double);
-enum gf_en gf_conv(char *);
-enum if_en if_conv(char *);
-enum tmt_en tmt_conv(char *);
-enum grc_en grc_conv(char *);
-int im_free(image_desc_t *);
-void auto_scale( image_desc_t *,  double *, char **, double *);
-void si_unit( image_desc_t *);
-void expand_range(image_desc_t *);
-void reduce_data( enum cf_en,  unsigned long,  time_t *, time_t *,  unsigned long *,  unsigned long *,  rrd_value_t **);
-int data_fetch( image_desc_t *);
-long find_var(image_desc_t *, char *);
-long find_var_wrapper(void *arg1, char *key);
-long lcd(long *);
-int data_calc( image_desc_t *);
-int data_proc( image_desc_t *);
-time_t find_first_time( time_t,  enum tmt_en,  long);
-time_t find_next_time( time_t,  enum tmt_en,  long);
-void gator( gdImagePtr, int, int);
-int print_calc(image_desc_t *, char ***);
-int leg_place(image_desc_t *);
-int horizontal_grid(gdImagePtr, image_desc_t *);
-int horizontal_log_grid(gdImagePtr, image_desc_t *);
-void vertical_grid( gdImagePtr, image_desc_t *);
-void axis_paint( image_desc_t *, gdImagePtr);
-void grid_paint( image_desc_t *, gdImagePtr);
-gdImagePtr MkLineBrush(image_desc_t *,long, enum gf_en);
-int lazy_check(image_desc_t *);
-int graph_paint(image_desc_t *, char ***);
-int gdes_alloc(image_desc_t *);
-int scan_for_col(char *, int, char *);
-int rrd_graph(int, char **, char ***, int *, int *);
-int bad_format(char *);
-int vdef_parse(struct graph_desc_t *,char *);
-int vdef_calc(image_desc_t *, int);
-int vdef_percent_compar(const void *,const void *);
-
-/* translate time values into x coordinates */   
-/*#define xtr(x) (int)((double)im->xorigin \
-               + ((double) im->xsize / (double)(im->end - im->start) ) \
-               * ((double)(x) - im->start)+0.5) */
+
 /* initialize with xtr(im,0); */
 int
 xtr(image_desc_t *im,time_t mytime){
@@ -289,11 +129,7 @@ xtr(image_desc_t *im,time_t mytime){
 }
 
 /* translate data values into y coordinates */
-
-/* #define ytr(x) (int)((double)im->yorigin \
-               - ((double) im->ysize / (im->maxval - im->minval) ) \
-               * ((double)(x) - im->minval)+0.5) */
-int
+double
 ytr(image_desc_t *im, double value){
     static double pixie;
     double yval;
@@ -304,30 +140,27 @@ ytr(image_desc_t *im, double value){
        pixie = (double) im->ysize / (log10(im->maxval) - log10(im->minval));
       yval = im->yorigin;
     } else if(!im->logarithmic) {
-      yval = im->yorigin - pixie * (value - im->minval) + 0.5;
+      yval = im->yorigin - pixie * (value - im->minval);
     } else {
       if (value < im->minval) {
        yval = im->yorigin;
       } else {
-       yval = im->yorigin - pixie * (log10(value) - log10(im->minval)) + 0.5;
+       yval = im->yorigin - pixie * (log10(value) - log10(im->minval));
       }
     }
     /* make sure we don't return anything too unreasonable. GD lib can
        get terribly slow when drawing lines outside its scope. This is 
        especially problematic in connection with the rigid option */
     if (! im->rigid) {
-      return (int)yval;
-    } else if ((int)yval > im->yorigin) {
-      return im->yorigin+2;
-    } else if ((int) yval < im->yorigin - im->ysize){
-      return im->yorigin - im->ysize - 2;
-    } else {
-      return (int)yval;
+      /* keep yval as-is */
+    } else if (yval > im->yorigin) {
+      yval = im->yorigin+2;
+    } else if (yval < im->yorigin - im->ysize){
+      yval = im->yorigin - im->ysize - 2;
     } 
+    return yval;
 }
 
-   
-    
 
 
 /* conversion function for symbolic entry names */
@@ -343,23 +176,28 @@ enum gf_en gf_conv(char *string){
     conv_if(COMMENT,GF_COMMENT)
     conv_if(HRULE,GF_HRULE)
     conv_if(VRULE,GF_VRULE)
-    conv_if(LINE1,GF_LINE1)
-    conv_if(LINE2,GF_LINE2)
-    conv_if(LINE3,GF_LINE3)
+    conv_if(LINE,GF_LINE)
     conv_if(AREA,GF_AREA)
     conv_if(STACK,GF_STACK)
-       conv_if(TICK,GF_TICK)
+    conv_if(TICK,GF_TICK)
     conv_if(DEF,GF_DEF)
     conv_if(CDEF,GF_CDEF)
     conv_if(VDEF,GF_VDEF)
+#ifdef WITH_PIECHART
+    conv_if(PART,GF_PART)
+#endif
+    conv_if(XPORT,GF_XPORT)
+    conv_if(SHIFT,GF_SHIFT)
     
     return (-1);
 }
 
-enum if_en if_conv(char *string){
+enum gfx_if_en if_conv(char *string){
     
-    conv_if(GIF,IF_GIF)
     conv_if(PNG,IF_PNG)
+    conv_if(SVG,IF_SVG)
+    conv_if(EPS,IF_EPS)
+    conv_if(PDF,IF_PDF)
 
     return (-1);
 }
@@ -385,22 +223,32 @@ enum grc_en grc_conv(char *string){
     conv_if(GRID,GRC_GRID)
     conv_if(MGRID,GRC_MGRID)
     conv_if(FONT,GRC_FONT)
-    conv_if(FRAME,GRC_FRAME)
     conv_if(ARROW,GRC_ARROW)
+    conv_if(AXIS,GRC_AXIS)
 
     return -1; 
 }
 
-#undef conv_if
+enum text_prop_en text_prop_conv(char *string){
+      
+    conv_if(DEFAULT,TEXT_PROP_DEFAULT)
+    conv_if(TITLE,TEXT_PROP_TITLE)
+    conv_if(AXIS,TEXT_PROP_AXIS)
+    conv_if(UNIT,TEXT_PROP_UNIT)
+    conv_if(LEGEND,TEXT_PROP_LEGEND)
+    return -1;
+}
 
 
+#undef conv_if
 
 int
 im_free(image_desc_t *im)
 {
-    long i,ii;
+    unsigned long      i,ii;
+
     if (im == NULL) return 0;
-    for(i=0;i<im->gdes_c;i++){
+    for(i=0;i<(unsigned)im->gdes_c;i++){
       if (im->gdes[i].data_first){
        /* careful here, because a single pointer can occur several times */
          free (im->gdes[i].data);
@@ -414,6 +262,7 @@ im_free(image_desc_t *im)
       free (im->gdes[i].rpnp);
     }
     free(im->gdes);
+    gfx_destroy(im->canvas);
     return 0;
 }
 
@@ -427,7 +276,7 @@ auto_scale(
           )
 {
        
-    char *symbol[] = {"a", /* 10e-18 Ato */
+    char *symbol[] = {"a", /* 10e-18 Atto */
                      "f", /* 10e-15 Femto */
                      "p", /* 10e-12 Pico */
                      "n", /* 10e-9  Nano */
@@ -437,7 +286,7 @@ auto_scale(
                      "k", /* 10e3   Kilo */
                      "M", /* 10e6   Mega */
                      "G", /* 10e9   Giga */
-                     "T", /* 10e12  Terra */
+                     "T", /* 10e12  Tera */
                      "P", /* 10e15  Peta */
                      "E"};/* 10e18  Exa */
 
@@ -468,7 +317,7 @@ si_unit(
 )
 {
 
-    char symbol[] = {'a', /* 10e-18 Ato */ 
+    char symbol[] = {'a', /* 10e-18 Atto */ 
                     'f', /* 10e-15 Femto */
                     'p', /* 10e-12 Pico */
                     'n', /* 10e-9  Nano */
@@ -478,7 +327,7 @@ si_unit(
                     'k', /* 10e3   Kilo */
                     'M', /* 10e6   Mega */
                     'G', /* 10e9   Giga */
-                    'T', /* 10e12  Terra */
+                    'T', /* 10e12  Tera */
                     'P', /* 10e15  Peta */
                     'E'};/* 10e18  Exa */
 
@@ -592,7 +441,73 @@ expand_range(image_desc_t *im)
 #endif
 }
 
-    
+void
+apply_gridfit(image_desc_t *im)
+{
+  if (isnan(im->minval) || isnan(im->maxval))
+    return;
+  ytr(im,DNAN);
+  if (im->logarithmic) {
+    double ya, yb, ypix, ypixfrac;
+    double log10_range = log10(im->maxval) - log10(im->minval);
+    ya = pow((double)10, floor(log10(im->minval)));
+    while (ya < im->minval)
+      ya *= 10;
+    if (ya > im->maxval)
+      return; /* don't have y=10^x gridline */
+    yb = ya * 10;
+    if (yb <= im->maxval) {
+      /* we have at least 2 y=10^x gridlines.
+        Make sure distance between them in pixels
+        are an integer by expanding im->maxval */
+      double y_pixel_delta = ytr(im, ya) - ytr(im, yb);
+      double factor = y_pixel_delta / floor(y_pixel_delta);
+      double new_log10_range = factor * log10_range;
+      double new_ymax_log10 = log10(im->minval) + new_log10_range;
+      im->maxval = pow(10, new_ymax_log10);
+      ytr(im, DNAN); /* reset precalc */
+      log10_range = log10(im->maxval) - log10(im->minval);
+    }
+    /* make sure first y=10^x gridline is located on 
+       integer pixel position by moving scale slightly 
+       downwards (sub-pixel movement) */
+    ypix = ytr(im, ya) + im->ysize; /* add im->ysize so it always is positive */
+    ypixfrac = ypix - floor(ypix);
+    if (ypixfrac > 0 && ypixfrac < 1) {
+      double yfrac = ypixfrac / im->ysize;
+      im->minval = pow(10, log10(im->minval) - yfrac * log10_range);
+      im->maxval = pow(10, log10(im->maxval) - yfrac * log10_range);
+      ytr(im, DNAN); /* reset precalc */
+    }
+  } else {
+    /* Make sure we have an integer pixel distance between
+       each minor gridline */
+    double ypos1 = ytr(im, im->minval);
+    double ypos2 = ytr(im, im->minval + im->ygrid_scale.gridstep);
+    double y_pixel_delta = ypos1 - ypos2;
+    double factor = y_pixel_delta / floor(y_pixel_delta);
+    double new_range = factor * (im->maxval - im->minval);
+    double gridstep = im->ygrid_scale.gridstep;
+    double minor_y, minor_y_px, minor_y_px_frac;
+    im->maxval = im->minval + new_range;
+    ytr(im, DNAN); /* reset precalc */
+    /* make sure first minor gridline is on integer pixel y coord */
+    minor_y = gridstep * floor(im->minval / gridstep);
+    while (minor_y < im->minval)
+      minor_y += gridstep;
+    minor_y_px = ytr(im, minor_y) + im->ysize; /* ensure > 0 by adding ysize */
+    minor_y_px_frac = minor_y_px - floor(minor_y_px);
+    if (minor_y_px_frac > 0 && minor_y_px_frac < 1) {
+      double yfrac = minor_y_px_frac / im->ysize;
+      double range = im->maxval - im->minval;
+      im->minval = im->minval - yfrac * range;
+      im->maxval = im->maxval - yfrac * range;
+      ytr(im, DNAN); /* reset precalc */
+    }
+    calc_horizontal_grid(im); /* recalc with changed im->maxval */
+  }
+}
+
 /* reduce data reimplementation by Alex */
 
 void
@@ -612,60 +527,68 @@ reduce_data(
     (*step) = cur_step*reduce_factor; /* set new step size for reduced data */
     dstptr = *data;
     srcptr = *data;
+    row_cnt = ((*end)-(*start))/cur_step;
 
-    /* We were given one extra row at the beginning of the interval.
-    ** We also need to return one extra row.  The extra interval is
-    ** the one defined by the start time in both cases.  It is not
-    ** used when graphing but maybe we can use it while reducing the
-    ** data.
-    */
-    row_cnt = ((*end)-(*start))/cur_step +1;
+#ifdef DEBUG
+#define DEBUG_REDUCE
+#endif
+#ifdef DEBUG_REDUCE
+printf("Reducing %lu rows with factor %i time %lu to %lu, step %lu\n",
+                       row_cnt,reduce_factor,*start,*end,cur_step);
+for (col=0;col<row_cnt;col++) {
+    printf("time %10lu: ",*start+(col+1)*cur_step);
+    for (i=0;i<*ds_cnt;i++)
+       printf(" %8.2e",srcptr[*ds_cnt*col+i]);
+    printf("\n");
+}
+#endif
 
-    /* alter start and end so that they are multiples of the new steptime.
-    ** End will be shifted towards the future and start will be shifted
-    ** towards the past in order to include the requested interval
-    */ 
+    /* We have to combine [reduce_factor] rows of the source
+    ** into one row for the destination.  Doing this we also
+    ** need to take care to combine the correct rows.  First
+    ** alter the start and end time so that they are multiples
+    ** of the new step time.  We cannot reduce the amount of
+    ** time so we have to move the end towards the future and
+    ** the start towards the past.
+    */
     end_offset = (*end) % (*step);
-    if (end_offset) end_offset = (*step)-end_offset;
     start_offset = (*start) % (*step);
-    (*end) = (*end)+end_offset;
-    (*start) = (*start)-start_offset;
 
-    /* The first destination row is unknown yet it still needs
-    ** to be present in the returned data.  Skip it.
-    ** Don't make it NaN or we might overwrite the source.
+    /* If there is a start offset (which cannot be more than
+    ** one destination row), skip the appropriate number of
+    ** source rows and one destination row.  The appropriate
+    ** number is what we do know (start_offset/cur_step) of
+    ** the new interval (*step/cur_step aka reduce_factor).
     */
-    dstptr += (*ds_cnt);
-
-    /* Depending on the amount of extra data needed at the
-    ** start of the destination, three things can happen:
-    ** -1- start_offset == 0:  skip the extra source row
-    ** -2- start_offset == cur_step: do nothing
-    ** -3- start_offset > cur_step: skip some source rows and 
-    **                      fill one destination row with NaN
-    */
-    if (start_offset==0) {
-       srcptr+=(*ds_cnt);
-       row_cnt--;
-    } else if (start_offset!=cur_step) {
-       skiprows=((*step)-start_offset)/cur_step+1;
-       srcptr += ((*ds_cnt)*skiprows);
+#ifdef DEBUG_REDUCE
+printf("start_offset: %lu  end_offset: %lu\n",start_offset,end_offset);
+printf("row_cnt before:  %lu\n",row_cnt);
+#endif
+    if (start_offset) {
+       (*start) = (*start)-start_offset;
+       skiprows=reduce_factor-start_offset/cur_step;
+       srcptr+=skiprows* *ds_cnt;
+        for (col=0;col<(*ds_cnt);col++) *dstptr++ = DNAN;
        row_cnt-=skiprows;
-       for (col=0;col<(*ds_cnt);col++) *dstptr++=DNAN;
     }
+#ifdef DEBUG_REDUCE
+printf("row_cnt between: %lu\n",row_cnt);
+#endif
 
-    /* If we had to alter the endtime, there won't be
-    ** enough data to fill the last row.  This means
-    ** we have to skip some rows at the end
+    /* At the end we have some rows that are not going to be
+    ** used, the amount is end_offset/cur_step
     */
     if (end_offset) {
-       skiprows = ((*step)-end_offset)/cur_step;
+       (*end) = (*end)-end_offset+(*step);
+       skiprows = end_offset/cur_step;
        row_cnt-=skiprows;
     }
-
+#ifdef DEBUG_REDUCE
+printf("row_cnt after:   %lu\n",row_cnt);
+#endif
 
 /* Sanity check: row_cnt should be multiple of reduce_factor */
-/* if this gets triggered, something is REALY WRONG ... we die immediately */
+/* if this gets triggered, something is REALLY WRONG ... we die immediately */
 
     if (row_cnt%reduce_factor) {
        printf("SANITY CHECK: %lu rows cannot be reduced by %i \n",
@@ -678,7 +601,7 @@ reduce_data(
     ** into one interval for the destination.
     */
 
-    for (dst_row=0;row_cnt>=reduce_factor;dst_row++) {
+    for (dst_row=0;(long int)row_cnt>=reduce_factor;dst_row++) {
        for (col=0;col<(*ds_cnt);col++) {
            rrd_value_t newval=DNAN;
            unsigned long validval=0;
@@ -733,11 +656,22 @@ reduce_data(
        srcptr+=(*ds_cnt)*reduce_factor;
        row_cnt-=reduce_factor;
     }
-
     /* If we had to alter the endtime, we didn't have enough
     ** source rows to fill the last row. Fill it with NaN.
     */
-    if (end_offset!=0) for (col=0;col<(*ds_cnt);col++) *dstptr++ = DNAN;
+    if (end_offset) for (col=0;col<(*ds_cnt);col++) *dstptr++ = DNAN;
+#ifdef DEBUG_REDUCE
+    row_cnt = ((*end)-(*start))/ *step;
+    srcptr = *data;
+    printf("Done reducing. Currently %lu rows, time %lu to %lu, step %lu\n",
+                               row_cnt,*start,*end,*step);
+for (col=0;col<row_cnt;col++) {
+    printf("time %10lu: ",*start+(col+1)*(*step));
+    for (i=0;i<*ds_cnt;i++)
+       printf(" %8.2e",srcptr[*ds_cnt*col+i]);
+    printf("\n");
+}
+#endif
 }
 
 
@@ -745,25 +679,31 @@ reduce_data(
    relevant rrds ... */
 
 int
-data_fetch( image_desc_t *im )
+data_fetch(image_desc_t *im )
 {
-    int       i,ii;
-    int skip;
+    int i,ii;
+    int                skip;
+
     /* pull the data from the log files ... */
-    for (i=0;i<im->gdes_c;i++){
+    for (i=0;i< (int)im->gdes_c;i++){
        /* only GF_DEF elements fetch data */
        if (im->gdes[i].gf != GF_DEF) 
            continue;
 
        skip=0;
        /* do we have it already ?*/
-       for (ii=0;ii<i;ii++){
+       for (ii=0;ii<i;ii++) {
            if (im->gdes[ii].gf != GF_DEF) 
                continue;
-           if((strcmp(im->gdes[i].rrd,im->gdes[ii].rrd) == 0)
-               && (im->gdes[i].cf == im->gdes[ii].cf)){
-               /* OK the data it is here already ... 
-                * we just copy the header portion */
+           if ((strcmp(im->gdes[i].rrd, im->gdes[ii].rrd) == 0)
+                       && (im->gdes[i].cf    == im->gdes[ii].cf)
+                       && (im->gdes[i].cf_reduce == im->gdes[ii].cf_reduce)
+                       && (im->gdes[i].start == im->gdes[ii].start)
+                       && (im->gdes[i].end   == im->gdes[ii].end)
+                       && (im->gdes[i].step  == im->gdes[ii].step)) {
+               /* OK, the data is already there.
+               ** Just copy the header portion
+               */
                im->gdes[i].start = im->gdes[ii].start;
                im->gdes[i].end = im->gdes[ii].end;
                im->gdes[i].step = im->gdes[ii].step;
@@ -790,9 +730,10 @@ 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_data(im->gdes[i].cf_reduce,
                            ft_step,
                            &im->gdes[i].start,
                            &im->gdes[i].end,
@@ -804,8 +745,8 @@ data_fetch( image_desc_t *im )
            }
        }
        
-        /* lets see if the required data source is realy there */
-       for(ii=0;ii<im->gdes[i].ds_cnt;ii++){
+        /* lets see if the required data source is really there */
+       for(ii=0;ii<(int)im->gdes[i].ds_cnt;ii++){
            if(strcmp(im->gdes[i].ds_namv[ii],im->gdes[i].ds_nam) == 0){
                im->gdes[i].ds=ii; }
        }
@@ -881,6 +822,30 @@ data_calc( image_desc_t *im){
         * so CDEFs can use VDEFs and vice versa
         */
        switch (im->gdes[gdi].gf) {
+           case GF_XPORT:
+             break;
+           case GF_SHIFT: {
+               graph_desc_t    *vdp = &im->gdes[im->gdes[gdi].vidx];
+               
+               /* remove current shift */
+               vdp->start -= vdp->shift;
+               vdp->end -= vdp->shift;
+               
+               /* vdef */
+               if (im->gdes[gdi].shidx >= 0) 
+                       vdp->shift = im->gdes[im->gdes[gdi].shidx].vf.val;
+               /* constant */
+               else
+                       vdp->shift = im->gdes[gdi].shval;
+
+               /* normalize shift to multiple of consolidated step */
+               vdp->shift = (vdp->shift / (long)vdp->step) * (long)vdp->step;
+
+               /* apply shift */
+               vdp->start += vdp->shift;
+               vdp->end += vdp->shift;
+               break;
+           }
            case GF_VDEF:
                /* A VDEF has no DS.  This also signals other parts
                 * of rrdtool that this is a VDEF value, not a CDEF.
@@ -907,11 +872,6 @@ data_calc( image_desc_t *im){
                /* Find the variables in the expression.
                 * - VDEF variables are substituted by their values
                 *   and the opcode is changed into OP_NUMBER.
-******************
-* Note to Jake: I cannot oversee the implications for your
-* COMPUTE DS stuff.  Please check if VDEF and COMPUTE are
-* compatible (or can be made so).
-******************
                 * - CDEF variables are analized for their step size,
                 *   the lowest common denominator of all the step
                 *   sizes of the data sources involved is calculated
@@ -919,19 +879,22 @@ data_calc( image_desc_t *im){
                 *   resulting data source.
                 */
                for(rpi=0;im->gdes[gdi].rpnp[rpi].op != OP_END;rpi++){
-                   if(im->gdes[gdi].rpnp[rpi].op == OP_VARIABLE){
+               if(im->gdes[gdi].rpnp[rpi].op == OP_VARIABLE ||
+                  im->gdes[gdi].rpnp[rpi].op == OP_PREV_OTHER){
                        long ptr = im->gdes[gdi].rpnp[rpi].ptr;
                        if (im->gdes[ptr].ds_cnt == 0) {
 #if 0
-printf("DEBUG: inside CDEF '%s' processing VDEF '%s'\n",
+                       printf("DEBUG: inside CDEF '%s' processing VDEF '%s'\n",
        im->gdes[gdi].vname,
        im->gdes[ptr].vname);
-printf("DEBUG: value from vdef is %f\n",im->gdes[ptr].vf.val);
+                       printf("DEBUG: value from vdef is %f\n",im->gdes[ptr].vf.val);
 #endif
                            im->gdes[gdi].rpnp[rpi].val = im->gdes[ptr].vf.val;
                            im->gdes[gdi].rpnp[rpi].op  = OP_NUMBER;
                        } else {
-                           if ((steparray = rrd_realloc(steparray, (++stepcnt+1)*sizeof(*steparray)))==NULL){
+                       if ((steparray =
+                                rrd_realloc(steparray,
+                                                        (++stepcnt+1)*sizeof(*steparray)))==NULL){
                                rrd_set_error("realloc steparray");
                                rpnstack_free(&rpnstack);
                                return -1;
@@ -956,8 +919,7 @@ printf("DEBUG: value from vdef is %f\n",im->gdes[ptr].vf.val);
                             * further save step size and data source
                             * count of this rra
                             */ 
-                           im->gdes[gdi].rpnp[rpi].data = 
-                                       im->gdes[ptr].data + im->gdes[ptr].ds; 
+                            im->gdes[gdi].rpnp[rpi].data =  im->gdes[ptr].data + im->gdes[ptr].ds;
                            im->gdes[gdi].rpnp[rpi].step = im->gdes[ptr].step;
                            im->gdes[gdi].rpnp[rpi].ds_cnt = im->gdes[ptr].ds_cnt;
 
@@ -965,11 +927,22 @@ printf("DEBUG: value from vdef is %f\n",im->gdes[ptr].vf.val);
                             * rpncalc() function doesn't have to treat
                             * the first case differently
                             */
-                           im->gdes[gdi].rpnp[rpi].data-=im->gdes[ptr].ds_cnt;
                        } /* if ds_cnt != 0 */
                    } /* if OP_VARIABLE */
                } /* loop through all rpi */
 
+                /* move the data pointers to the correct period */
+                for(rpi=0;im->gdes[gdi].rpnp[rpi].op != OP_END;rpi++){
+               if(im->gdes[gdi].rpnp[rpi].op == OP_VARIABLE ||
+                  im->gdes[gdi].rpnp[rpi].op == OP_PREV_OTHER){
+                        long ptr = im->gdes[gdi].rpnp[rpi].ptr;
+                       long diff = im->gdes[gdi].start - im->gdes[ptr].start;
+
+                        if(diff > 0)
+                           im->gdes[gdi].rpnp[rpi].data += (diff / im->gdes[ptr].step) * im->gdes[ptr].ds_cnt;
+                     }
+                }
+
                if(steparray == NULL){
                    rrd_set_error("rpn expressions without DEF"
                                " or CDEF variables are not supported");
@@ -984,7 +957,7 @@ printf("DEBUG: value from vdef is %f\n",im->gdes[ptr].vf.val);
                free(steparray);
                if((im->gdes[gdi].data = malloc((
                                (im->gdes[gdi].end-im->gdes[gdi].start) 
-                                   / im->gdes[gdi].step +1)
+                                   / im->gdes[gdi].step)
                                    * sizeof(double)))==NULL){
                    rrd_set_error("malloc im->gdes[gdi].data");
                    rpnstack_free(&rpnstack);
@@ -994,7 +967,7 @@ printf("DEBUG: value from vdef is %f\n",im->gdes[ptr].vf.val);
                /* Step through the new cdef results array and
                 * calculate the values
                 */
-               for (now = im->gdes[gdi].start;
+               for (now = im->gdes[gdi].start + im->gdes[gdi].step;
                                now<=im->gdes[gdi].end;
                                now += im->gdes[gdi].step)
                {
@@ -1033,74 +1006,73 @@ data_proc( image_desc_t *im ){
     unsigned long gr_time;    
 
     /* memory for the processed data */
-    for(i=0;i<im->gdes_c;i++){
-      if((im->gdes[i].gf==GF_LINE1) ||
-        (im->gdes[i].gf==GF_LINE2) ||
-        (im->gdes[i].gf==GF_LINE3) ||
-        (im->gdes[i].gf==GF_AREA) ||
-        (im->gdes[i].gf==GF_TICK) ||
-        (im->gdes[i].gf==GF_STACK)){
-       if((im->gdes[i].p_data = malloc((im->xsize +1)
+    for(i=0;i<im->gdes_c;i++) {
+       if((im->gdes[i].gf==GF_LINE) ||
+               (im->gdes[i].gf==GF_AREA) ||
+               (im->gdes[i].gf==GF_TICK) ||
+               (im->gdes[i].gf==GF_STACK)) {
+           if((im->gdes[i].p_data = malloc((im->xsize +1)
                                        * sizeof(rrd_value_t)))==NULL){
-         rrd_set_error("malloc data_proc");
-         return -1;
+               rrd_set_error("malloc data_proc");
+               return -1;
+           }
        }
-      }
     }
-    
-    for(i=0;i<im->xsize;i++){
+
+    for (i=0;i<im->xsize;i++) {        /* for each pixel */
        long vidx;
-       gr_time = im->start+pixstep*i; /* time of the 
-                                         current step */
+       gr_time = im->start+pixstep*i; /* time of the current step */
        paintval=0.0;
        
-       for(ii=0;ii<im->gdes_c;ii++){
-         double value;
-           switch(im->gdes[ii].gf){
-           case GF_LINE1:
-           case GF_LINE2:
-           case GF_LINE3:
-           case GF_AREA:
+       for (ii=0;ii<im->gdes_c;ii++) {
+           double value;
+           switch (im->gdes[ii].gf) {
+               case GF_LINE:
+               case GF_AREA:
                case GF_TICK:
-               paintval = 0.0;
-           case GF_STACK:
-               vidx = im->gdes[ii].vidx;
-
-               value =
-                   im->gdes[vidx].data[
-                                       ((unsigned long)floor((double)
-                                                            (gr_time - im->gdes[vidx].start ) 
-                                                            / im->gdes[vidx].step)+1)                  
-
-                                       /* added one because data was not being aligned properly
-                                          this fixes it. We may also be having a problem in fetch ... */
-
-                                       *im->gdes[vidx].ds_cnt
-                                       +im->gdes[vidx].ds];
-
-               if (! isnan(value)) {
-                 paintval += value;
-                 im->gdes[ii].p_data[i] = paintval;
-                 /* GF_TICK: the data values are not relevant for min and max */
-                 if (finite(paintval) && im->gdes[ii].gf != GF_TICK ){
-                  if (isnan(minval) || paintval <  minval)
-                    minval = paintval;
-                  if (isnan(maxval) || paintval >  maxval)
-                    maxval = paintval;
-                 }
-               } else {
-                 im->gdes[ii].p_data[i] = DNAN;
-               }
-               break;
-           case GF_PRINT:
-           case GF_GPRINT:
-           case GF_COMMENT:
-           case GF_HRULE:
-           case GF_VRULE:
-           case GF_DEF:               
-           case GF_CDEF:
-           case GF_VDEF:
-               break;
+                   if (!im->gdes[ii].stack)
+                       paintval = 0.0;
+               case GF_STACK:
+                   value = im->gdes[ii].yrule;
+                   if (isnan(value) || (im->gdes[ii].gf == GF_TICK)) {
+                       /* The time of the data doesn't necessarily match
+                       ** the time of the graph. Beware.
+                       */
+                       vidx = im->gdes[ii].vidx;
+                       if (im->gdes[vidx].gf == GF_VDEF) {
+                           value = im->gdes[vidx].vf.val;
+                       } else if (((long int)gr_time >= (long int)im->gdes[vidx].start) &&
+                                  ((long int)gr_time <= (long int)im->gdes[vidx].end) ) {
+                           value = im->gdes[vidx].data[
+                               (unsigned long) floor(
+                                   (double)(gr_time - im->gdes[vidx].start)
+                                               / im->gdes[vidx].step)
+                               * im->gdes[vidx].ds_cnt
+                               + im->gdes[vidx].ds
+                           ];
+                       } else {
+                           value = DNAN;
+                       }
+                   };
+
+                   if (! isnan(value)) {
+                       paintval += value;
+                       im->gdes[ii].p_data[i] = paintval;
+                       /* GF_TICK: the data values are not
+                       ** relevant for min and max
+                       */
+                       if (finite(paintval) && im->gdes[ii].gf != GF_TICK ) {
+                           if (isnan(minval) || paintval <  minval)
+                               minval = paintval;
+                           if (isnan(maxval) || paintval >  maxval)
+                               maxval = paintval;
+                       }
+                   } else {
+                       im->gdes[ii].p_data[i] = DNAN;
+                   }
+                   break;
+               default:
+                   break;
            }
        }
     }
@@ -1114,29 +1086,33 @@ data_proc( image_desc_t *im ){
     
     /* adjust min and max values */
     if (isnan(im->minval) 
-       || ((!im->logarithmic && !im->rigid) /* don't adjust low-end with log scale */
-           && im->minval > minval))
+       /* don't adjust low-end with log scale */
+       || ((!im->logarithmic && !im->rigid) && im->minval > minval)
+       )
        im->minval = minval;
     if (isnan(im->maxval) 
-       || (!im->rigid 
-           && im->maxval < maxval)){
+       || (!im->rigid && im->maxval < maxval)
+       {
        if (im->logarithmic)
            im->maxval = maxval * 1.1;
        else
            im->maxval = maxval;
     }
+    /* make sure min is smaller than max */
+    if (im->minval > im->maxval) {
+            im->minval = 0.99 * im->maxval;
+    }
+                      
     /* make sure min and max are not equal */
     if (im->minval == im->maxval) {
-      im->maxval *= 1.01; 
-      if (! im->logarithmic) {
-       im->minval *= 0.99;
-      }
-      
-      /* make sure min and max are not both zero */
-      if (im->maxval == 0.0) {
+       im->maxval *= 1.01; 
+       if (! im->logarithmic) {
+           im->minval *= 0.99;
+       }
+       /* make sure min and max are not both zero */
+       if (im->maxval == 0.0) {
            im->maxval = 1.0;
-      }
-        
+       }
     }
     return 0;
 }
@@ -1153,7 +1129,7 @@ find_first_time(
     )
 {
     struct tm tm;
-    tm = *localtime(&start);
+    localtime_r(&start, &tm);
     switch(baseint){
     case TMT_SECOND:
        tm.tm_sec -= tm.tm_sec % basestep; break;
@@ -1206,7 +1182,7 @@ find_next_time(
 {
     struct tm tm;
     time_t madetime;
-    tm = *localtime(&current);
+    localtime_r(&current, &tm);
     do {
        switch(baseint){
        case TMT_SECOND:
@@ -1231,53 +1207,6 @@ find_next_time(
          
 }
 
-void gator( gdImagePtr gif, int x, int y){ 
-
-/* this function puts the name of the author and the tool into the
-   graph. Remove if you must, but please note, that it is here,
-   because I would like people who look at rrdtool generated graphs to
-   see what was used to do it. No obviously you can also add a credit
-   line to your webpage or printed document, this is fine with me. But
-   as I have no control over this, I added the little tag in here. 
-*/
-
-/* the fact that the text of what gets put into the graph is not
-   visible in the function, has lead some to think this is for
-   obfuscation reasons. While this is a nice side effect (I addmit),
-   it is not the prime reason. The prime reason is, that the font
-   used, is so small, that I had to hand edit the characters to ensure
-   readability. I could thus not use the normal gd functions to write,
-   but had to embed a slightly compressed bitmap version into the code. 
-*/
-
-    int li[]={0,0,1, 0,4,5, 0,8,9, 0,12,14, 0,17,17, 0,21,21, 
-             0,24,24, 0,34,34, 0,40,42, 0,45,45, 0,48,49, 0,52,54, 
-             0,61,61, 0,64,66, 0,68,70, 0,72,74, 0,76,76, 0,78,78, 
-             0,80,82, 0,84,85, 
-             1,0,0, 1,2,2, 1,4,4, 1,6,6, 1,8,8, 1,10,10, 
-             1,13,13, 1,16,16, 1,18,18, 1,20,20, 1,22,22, 1,24,24, 
-             1,34,34, 1,41,41, 1,44,44, 1,46,46, 1,48,48, 1,50,50, 
-             1,53,53, 1,60,60, 1,62,62, 1,64,64, 1,69,69, 1,73,73, 
-             1,76,76, 1,78,78, 1,80,80, 1,84,84, 1,86,86, 
-             2,0,1, 2,4,5, 2,8,8, 2,10,10, 2,13,13, 2,16,16, 
-             2,18,18, 2,20,20, 2,22,22, 2,24,24, 2,33,33, 2,41,41, 
-             2,44,44, 2,46,46, 2,48,49, 2,53,53, 2,60,60, 2,62,62, 
-             2,64,65, 2,69,69, 2,73,73, 2,76,77, 2,80,81, 2,84,85,           
-             3,0,0, 3,2,2, 3,4,4, 3,6,6, 3,8,8, 3,10,10, 
-             3,13,13, 3,16,16, 3,18,18, 3,20,20, 3,22,22, 3,24,24, 
-             3,32,32, 3,41,41, 3,44,44, 3,46,46, 3,48,48, 3,50,50, 
-             3,53,53, 3,60,60, 3,62,62, 3,64,64, 3,69,69, 3,73,73, 
-             3,76,76, 3,78,78, 3,80,80, 3,84,84, 3,86,86, 
-             4,0,0, 4,2,2, 4,4,4, 4,6,6, 4,8,9, 4,13,13, 
-             4,17,17, 4,21,21, 4,24,26, 4,32,32, 4,41,41, 4,45,45, 
-             4,48,49, 4,52,54, 4,61,61, 4,64,66, 4,69,69, 4,72,74, 
-             4,76,76, 4,78,78, 4,80,82, 4,84,84}; 
-    int i,ii; 
-    for(i=0; i<DIM(li); i=i+3)
-       for(ii=y+li[i+1]; ii<=y+li[i+2];ii++)
-         gdImageSetPixel(gif,x-li[i],ii,graph_col[GRC_GRID].i); 
-}
-
 
 /* calculate values required for PRINT and GPRINT functions */
 
@@ -1286,6 +1215,7 @@ print_calc(image_desc_t *im, char ***prdata)
 {
     long i,ii,validsteps;
     double printval;
+    time_t printtime;
     int graphelement = 0;
     long vidx;
     int max_ii;        
@@ -1309,7 +1239,8 @@ 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].yrule;
+               printval = im->gdes[vidx].vf.val;
+               printtime = im->gdes[vidx].vf.when;
            } else { /* need to calculate max,min,avg etcetera */
                max_ii =((im->gdes[vidx].end 
                        - im->gdes[vidx].start)
@@ -1317,8 +1248,8 @@ print_calc(image_desc_t *im, char ***prdata)
                        * im->gdes[vidx].ds_cnt);
                printval = DNAN;
                validsteps = 0;
-               for(ii=im->gdes[vidx].ds+im->gdes[vidx].ds_cnt;
-                       ii < max_ii+im->gdes[vidx].ds_cnt;
+               for(    ii=im->gdes[vidx].ds;
+                       ii < max_ii;
                        ii+=im->gdes[vidx].ds_cnt){
                    if (! finite(im->gdes[vidx].data[ii]))
                        continue;
@@ -1355,7 +1286,19 @@ 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 */
+               if (im->gdes[i].gf == GF_PRINT){
+                   (*prdata)[prlines-2] = malloc((FMT_LEG_LEN+2)*sizeof(char));
+                   sprintf((*prdata)[prlines-2],"%s (%lu)",
+                                       ctime_r(&printtime,ctime_buf),printtime);
+                   (*prdata)[prlines-1] = NULL;
+               } else {
+                   sprintf(im->gdes[i].legend,"%s (%lu)",
+                                       ctime_r(&printtime,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.
@@ -1376,8 +1319,9 @@ print_calc(image_desc_t *im, char ***prdata)
 
            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 [G]PRINT in '%s'", im->gdes[i].format);
+                       rrd_set_error("bad format for PRINT in '%s'", im->gdes[i].format);
                        return -1;
                }
 #ifdef HAVE_SNPRINTF
@@ -1385,12 +1329,11 @@ print_calc(image_desc_t *im, char ***prdata)
 #else
                sprintf((*prdata)[prlines-2],im->gdes[i].format,printval,si_symb);
 #endif
-               (*prdata)[prlines-1] = NULL;
            } else {
                /* GF_GPRINT */
 
                if (bad_format(im->gdes[i].format)) {
-                       rrd_set_error("bad format for [G]PRINT in '%s'", im->gdes[i].format);
+                       rrd_set_error("bad format for GPRINT in '%s'", im->gdes[i].format);
                        return -1;
                }
 #ifdef HAVE_SNPRINTF
@@ -1400,11 +1343,9 @@ print_calc(image_desc_t *im, char ***prdata)
 #endif
                graphelement = 1;
            }
+           }
            break;
-        case GF_COMMENT:
-       case GF_LINE1:
-       case GF_LINE2:
-       case GF_LINE3:
+       case GF_LINE:
        case GF_AREA:
        case GF_TICK:
        case GF_STACK:
@@ -1412,9 +1353,15 @@ print_calc(image_desc_t *im, char ***prdata)
        case GF_VRULE:
            graphelement = 1;
            break;
+        case GF_COMMENT:
        case GF_DEF:
        case GF_CDEF:       
        case GF_VDEF:       
+#ifdef WITH_PIECHART
+       case GF_PART:
+#endif
+       case GF_SHIFT:
+       case GF_XPORT:
            break;
        }
     }
@@ -1427,19 +1374,18 @@ int
 leg_place(image_desc_t *im)
 {
     /* graph labels */
-    int   interleg = SmallFont->w*2;
-    int   box = SmallFont->h*1.2;
-    int   border = SmallFont->w*2;
+    int   interleg = im->text_prop[TEXT_PROP_LEGEND].size*2.0;
+    int   border = im->text_prop[TEXT_PROP_LEGEND].size*2.0;
     int   fill=0, fill_last;
     int   leg_c = 0;
-    int   leg_x = border, leg_y = im->ygif;
+    int   leg_x = border, leg_y = im->yimg;
     int   leg_cc;
     int   glue = 0;
     int   i,ii, mark = 0;
     char  prt_fctn; /*special printfunctions */
     int  *legspace;
 
-  if( !(im->extra_flags & NOLEGEND) ) {
+  if( !(im->extra_flags & NOLEGEND) & !(im->extra_flags & ONLY_GRAPH) ) {
     if ((legspace = malloc(im->gdes_c*sizeof(int)))==NULL){
        rrd_set_error("malloc for legspace");
        return -1;
@@ -1447,11 +1393,24 @@ leg_place(image_desc_t *im)
 
     for(i=0;i<im->gdes_c;i++){
        fill_last = fill;
+        
+        /* hid legends for rules which are not displayed */
+        
+       if(!(im->extra_flags & FORCE_RULES_LEGEND)) {
+               if (im->gdes[i].gf == GF_HRULE &&
+                   (im->gdes[i].yrule < im->minval || im->gdes[i].yrule > im->maxval))
+                   im->gdes[i].legend[0] = '\0';
+
+               if (im->gdes[i].gf == GF_VRULE &&
+                   (im->gdes[i].xrule < im->start || im->gdes[i].xrule > im->end))
+                   im->gdes[i].legend[0] = '\0';
+       }
 
        leg_cc = strlen(im->gdes[i].legend);
        
        /* is there a controle code ant the end of the legend string ? */ 
-       if (leg_cc >= 2 && im->gdes[i].legend[leg_cc-2] == '\\') {
+       /* and it is not a tab \\t */
+       if (leg_cc >= 2 && im->gdes[i].legend[leg_cc-2] == '\\' && im->gdes[i].legend[leg_cc-1] != 't') {
            prt_fctn = im->gdes[i].legend[leg_cc-1];
            leg_cc -= 2;
            im->gdes[i].legend[leg_cc] = '\0';
@@ -1465,18 +1424,18 @@ leg_place(image_desc_t *im)
           leg_cc--;
           im->gdes[i].legend[leg_cc]='\0';
        }
-       if (leg_cc != 0 ){          
+       if (leg_cc != 0 ){
           legspace[i]=(prt_fctn=='g' ? 0 : interleg);
           
           if (fill > 0){ 
               /* no interleg space if string ends in \g */
               fill += legspace[i];
            }
-           if (im->gdes[i].gf != GF_GPRINT && 
-               im->gdes[i].gf != GF_COMMENT) { 
-               fill += box;       
-           }
-           fill += leg_cc * SmallFont->w;
+          fill += gfx_get_text_width(im->canvas, fill+border,
+                                     im->text_prop[TEXT_PROP_LEGEND].font,
+                                     im->text_prop[TEXT_PROP_LEGEND].size,
+                                     im->tabwidth,
+                                     im->gdes[i].legend, 0);
            leg_c++;
        } else {
           legspace[i]=0;
@@ -1489,7 +1448,7 @@ leg_place(image_desc_t *im)
            if (i == im->gdes_c -1 ) prt_fctn ='l';
            
            /* is it time to place the legends ? */
-           if (fill > im->xgif - 2*border){
+           if (fill > im->ximg - 2*border){
                if (leg_c > 1) {
                    /* go back one */
                    i--; 
@@ -1504,38 +1463,38 @@ leg_place(image_desc_t *im)
        }
 
 
-       if (prt_fctn != '\0'){
+       if (prt_fctn != '\0'){  
            leg_x = border;
            if (leg_c >= 2 && prt_fctn == 'j') {
-               glue = (im->xgif - fill - 2* border) / (leg_c-1);
-               /* if (glue > 2 * SmallFont->w) glue = 0; */
+               glue = (im->ximg - fill - 2* border) / (leg_c-1);
            } else {
                glue = 0;
            }
-           if (prt_fctn =='c') leg_x =  (im->xgif - fill) / 2.0;
-           if (prt_fctn =='r') leg_x =  im->xgif - fill - border;
+           if (prt_fctn =='c') leg_x =  (im->ximg - fill) / 2.0;
+           if (prt_fctn =='r') leg_x =  im->ximg - fill - border;
 
            for(ii=mark;ii<=i;ii++){
                if(im->gdes[ii].legend[0]=='\0')
-                   continue;
-               im->gdes[ii].legloc.x = leg_x;
-               im->gdes[ii].legloc.y = leg_y;
-               leg_x =  leg_x 
-                   + strlen(im->gdes[ii].legend)*SmallFont->w 
-                   + legspace[ii]
-                   + glue;
-               if (im->gdes[ii].gf != GF_GPRINT && 
-                   im->gdes[ii].gf != GF_COMMENT) 
-                   leg_x += box;          
-           }       
-           leg_y = leg_y + SmallFont->h*1.2;
-           if (prt_fctn == 's') leg_y -= SmallFont->h *0.5;
+                   continue; /* skip empty legends */
+               im->gdes[ii].leg_x = leg_x;
+               im->gdes[ii].leg_y = leg_y;
+               leg_x += 
+                gfx_get_text_width(im->canvas, leg_x,
+                                     im->text_prop[TEXT_PROP_LEGEND].font,
+                                     im->text_prop[TEXT_PROP_LEGEND].size,
+                                     im->tabwidth,
+                                     im->gdes[ii].legend, 0) 
+                  + legspace[ii]
+                  + glue;
+           }                   
+           leg_y += im->text_prop[TEXT_PROP_LEGEND].size*1.7;
+           if (prt_fctn == 's') leg_y -=  im->text_prop[TEXT_PROP_LEGEND].size;           
            fill = 0;
            leg_c = 0;
            mark = ii;
        }          
     }
-    im->ygif = leg_y+6;
+    im->yimg = leg_y;
     free(legspace);
   }
   return 0;
@@ -1550,22 +1509,15 @@ leg_place(image_desc_t *im)
 
 
 int
-horizontal_grid(gdImagePtr gif, image_desc_t   *im)
+calc_horizontal_grid(image_desc_t   *im)
 {
     double   range;
     double   scaledrange;
     int      pixel,i;
-    int      sgrid,egrid;
-    double   gridstep;
-    double   scaledstep;
-    char     graph_label[100];
-    gdPoint  polyPoints[4];
-    int      labfact,gridind;
-    int      styleMinor[2],styleMajor[2];
+    int      gridind;
     int      decimals, fractionals;
-    char     labfmt[64];
 
-    labfact=2;
+    im->ygrid_scale.labfact=2;
     gridind=-1;
     range =  im->maxval - im->minval;
     scaledrange = range / im->magfact;
@@ -1576,12 +1528,6 @@ horizontal_grid(gdImagePtr gif, image_desc_t   *im)
                return 0;
        }
 
-    styleMinor[0] = graph_col[GRC_GRID].i;
-    styleMinor[1] = gdTransparent;
-
-    styleMajor[0] = graph_col[GRC_MGRID].i;
-    styleMajor[1] = gdTransparent;
-
     /* find grid spaceing */
     pixel=1;
     if(isnan(im->ygridstep)){
@@ -1593,25 +1539,25 @@ horizontal_grid(gdImagePtr gif, image_desc_t   *im)
            
            fractionals = floor(log10(range));
            if(fractionals < 0) /* small amplitude. */
-               sprintf(labfmt, "%%%d.%df", decimals - fractionals + 1, -fractionals + 1);
+               sprintf(im->ygrid_scale.labfmt, "%%%d.%df", decimals - fractionals + 1, -fractionals + 1);
            else
-               sprintf(labfmt, "%%%d.1f", decimals + 1);
-           gridstep = pow((double)10, (double)fractionals);
-           if(gridstep == 0) /* range is one -> 0.1 is reasonable scale */
-               gridstep = 0.1;
+               sprintf(im->ygrid_scale.labfmt, "%%%d.1f", decimals + 1);
+           im->ygrid_scale.gridstep = pow((double)10, (double)fractionals);
+           if(im->ygrid_scale.gridstep == 0) /* range is one -> 0.1 is reasonable scale */
+               im->ygrid_scale.gridstep = 0.1;
            /* should have at least 5 lines but no more then 15 */
-           if(range/gridstep < 5)
-                gridstep /= 10;
-           if(range/gridstep > 15)
-                gridstep *= 10;
-           if(range/gridstep > 5) {
-               labfact = 1;
-               if(range/gridstep > 8)
-                   labfact = 2;
+           if(range/im->ygrid_scale.gridstep < 5)
+                im->ygrid_scale.gridstep /= 10;
+           if(range/im->ygrid_scale.gridstep > 15)
+                im->ygrid_scale.gridstep *= 10;
+           if(range/im->ygrid_scale.gridstep > 5) {
+               im->ygrid_scale.labfact = 1;
+               if(range/im->ygrid_scale.gridstep > 8)
+                   im->ygrid_scale.labfact = 2;
            }
            else {
-               gridstep /= 5;
-               labfact = 5;
+               im->ygrid_scale.gridstep /= 5;
+               im->ygrid_scale.labfact = 5;
            }
        }
        else {
@@ -1624,33 +1570,41 @@ horizontal_grid(gdImagePtr gif, image_desc_t   *im)
            }
            
            for(i=0; i<4;i++) {
-               if (pixel * ylab[gridind].lfac[i] >=  2 * SmallFont->h) {
-                   labfact =  ylab[gridind].lfac[i];
-                   break;
-               }                         
+              if (pixel * ylab[gridind].lfac[i] >=  2 * im->text_prop[TEXT_PROP_AXIS].size) {
+                 im->ygrid_scale.labfact =  ylab[gridind].lfac[i];
+                 break;
+              }                          
            } 
            
-           gridstep = ylab[gridind].grid * im->magfact;
+           im->ygrid_scale.gridstep = ylab[gridind].grid * im->magfact;
        }
     } else {
-       gridstep = im->ygridstep;
-       labfact = im->ylabfact;
+       im->ygrid_scale.gridstep = im->ygridstep;
+       im->ygrid_scale.labfact = im->ylabfact;
     }
-    
-    polyPoints[0].x=im->xorigin;
-    polyPoints[1].x=im->xorigin+im->xsize;
-    sgrid = (int)( im->minval / gridstep - 1);
-    egrid = (int)( im->maxval / gridstep + 1);
-    scaledstep = gridstep/im->magfact;
+    return 1;
+}
+
+int draw_horizontal_grid(image_desc_t *im)
+{
+    int      i;
+    double   scaledstep;
+    char     graph_label[100];
+    double X0=im->xorigin;
+    double X1=im->xorigin+im->xsize;
+   
+    int sgrid = (int)( im->minval / im->ygrid_scale.gridstep - 1);
+    int egrid = (int)( im->maxval / im->ygrid_scale.gridstep + 1);
+    scaledstep = im->ygrid_scale.gridstep/im->magfact;
     for (i = sgrid; i <= egrid; i++){
-       polyPoints[0].y=ytr(im,gridstep*i);
-       if ( polyPoints[0].y >= im->yorigin-im->ysize
-            && polyPoints[0].y <= im->yorigin) {
-           if(i % labfact == 0){               
+       double Y0=ytr(im,im->ygrid_scale.gridstep*i);
+       if ( Y0 >= im->yorigin-im->ysize
+                && Y0 <= im->yorigin){       
+           if(i % im->ygrid_scale.labfact == 0){               
                if (i==0 || im->symbol == ' ') {
                    if(scaledstep < 1){
                        if(im->extra_flags & ALTYGRID) {
-                           sprintf(graph_label,labfmt,scaledstep*i);
+                           sprintf(graph_label,im->ygrid_scale.labfmt,scaledstep*i);
                        }
                        else {
                            sprintf(graph_label,"%4.1f",scaledstep*i);
@@ -1666,48 +1620,41 @@ horizontal_grid(gdImagePtr gif, image_desc_t   *im)
                    }
                }
 
-               gdImageString(gif, SmallFont,
-                              (polyPoints[0].x - (strlen(graph_label) * 
-                                                  SmallFont->w)-7), 
-                              polyPoints[0].y - SmallFont->h/2+1,
-                              (unsigned char *)graph_label, graph_col[GRC_FONT].i);
-               
-               gdImageSetStyle(gif, styleMajor, 2);
-
-               gdImageLine(gif, polyPoints[0].x-2,polyPoints[0].y,
-                           polyPoints[0].x+2,polyPoints[0].y,graph_col[GRC_MGRID].i);
-               gdImageLine(gif, polyPoints[1].x-2,polyPoints[0].y,
-                           polyPoints[1].x+2,polyPoints[0].y,graph_col[GRC_MGRID].i);              
-           } else {            
-               gdImageSetStyle(gif, styleMinor, 2);
-               gdImageLine(gif, polyPoints[0].x-1,polyPoints[0].y,
-                           polyPoints[0].x+1,polyPoints[0].y,graph_col[GRC_GRID].i);
-               gdImageLine(gif, polyPoints[1].x-1,polyPoints[0].y,
-                           polyPoints[1].x+1,polyPoints[0].y,graph_col[GRC_GRID].i);               
+              gfx_new_text ( im->canvas,
+                             X0-im->text_prop[TEXT_PROP_AXIS].size/1.5, 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 );
+              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);            
+              
+           } else if (!(im->extra_flags & NOMINOR)) {          
+              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);            
+              
            }       
-           gdImageLine(gif, polyPoints[0].x,polyPoints[0].y,
-                       polyPoints[1].x,polyPoints[0].y,gdStyled);
        }       
     } 
-/*    if(im->minval * im->maxval < 0){
-      polyPoints[0].y=ytr(0);
-      gdImageLine(gif, polyPoints[0].x,polyPoints[0].y,
-      polyPoints[1].x,polyPoints[0].y,graph_col[GRC_MGRID].i);
-      } */
-
     return 1;
 }
 
 /* logaritmic horizontal grid */
 int
-horizontal_log_grid(gdImagePtr gif, image_desc_t   *im)
+horizontal_log_grid(image_desc_t   *im)   
 {
     double   pixpex;
     int      ii,i;
     int      minoridx=0, majoridx=0;
     char     graph_label[100];
-    gdPoint  polyPoints[4];
-    int      styleMinor[2],styleMajor[2];
+    double   X0,X1,Y0;   
     double   value, pixperstep, minstep;
 
     /* find grid spaceing */
@@ -1727,17 +1674,11 @@ horizontal_log_grid(gdImagePtr gif, image_desc_t   *im)
        }
        pixperstep = pixpex * minstep;
        if(pixperstep > 5){minoridx = i;}
-       if(pixperstep > 2 * SmallFont->h){majoridx = i;}
+       if(pixperstep > 2 *  im->text_prop[TEXT_PROP_LEGEND].size){majoridx = i;}
     }
    
-    styleMinor[0] = graph_col[GRC_GRID].i;
-    styleMinor[1] = gdTransparent;
-
-    styleMajor[0] = graph_col[GRC_MGRID].i;
-    styleMajor[1] = gdTransparent;
-
-    polyPoints[0].x=im->xorigin;
-    polyPoints[1].x=im->xorigin+im->xsize;
+   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])));
@@ -1746,16 +1687,13 @@ horizontal_log_grid(gdImagePtr gif, image_desc_t   *im)
        if (value < im->minval) continue;
        i=0;    
        while(yloglab[minoridx][++i] > 0){          
-           polyPoints[0].y = ytr(im,value * yloglab[minoridx][i]);
-           if (polyPoints[0].y <= im->yorigin - im->ysize) break;
-           gdImageSetStyle(gif, styleMinor, 2);
-           gdImageLine(gif, polyPoints[0].x-1,polyPoints[0].y,
-                       polyPoints[0].x+1,polyPoints[0].y,graph_col[GRC_GRID].i);
-           gdImageLine(gif, polyPoints[1].x-1,polyPoints[0].y,
-                       polyPoints[1].x+1,polyPoints[0].y,graph_col[GRC_GRID].i);           
-
-           gdImageLine(gif, polyPoints[0].x,polyPoints[0].y,
-                       polyPoints[1].x,polyPoints[0].y,gdStyled);
+          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);
        }
     }
 
@@ -1767,22 +1705,22 @@ horizontal_log_grid(gdImagePtr gif, image_desc_t   *im)
        if (value < im->minval) continue;
        i=0;    
        while(yloglab[majoridx][++i] > 0){          
-           polyPoints[0].y = ytr(im,value * yloglab[majoridx][i]);         
-           if (polyPoints[0].y <= im->yorigin - im->ysize) break;
-           gdImageSetStyle(gif, styleMajor, 2);
-           gdImageLine(gif, polyPoints[0].x-2,polyPoints[0].y,
-                       polyPoints[0].x+2,polyPoints[0].y,graph_col[GRC_MGRID].i);
-           gdImageLine(gif, polyPoints[1].x-2,polyPoints[0].y,
-                       polyPoints[1].x+2,polyPoints[0].y,graph_col[GRC_MGRID].i);                  
-           
-           gdImageLine(gif, polyPoints[0].x,polyPoints[0].y,
-                       polyPoints[1].x,polyPoints[0].y,gdStyled);
-           sprintf(graph_label,"%3.0e",value * yloglab[majoridx][i]);
-           gdImageString(gif, SmallFont,
-                         (polyPoints[0].x - (strlen(graph_label) * 
-                                             SmallFont->w)-7), 
-                         polyPoints[0].y - SmallFont->h/2+1,
-                         (unsigned char *)graph_label, graph_col[GRC_FONT].i); 
+          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);
+          
+          sprintf(graph_label,"%3.0e",value * yloglab[majoridx][i]);
+          gfx_new_text ( im->canvas,
+                         X0-im->text_prop[TEXT_PROP_AXIS].size/1.5, 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;
@@ -1791,19 +1729,15 @@ horizontal_log_grid(gdImagePtr gif, image_desc_t   *im)
 
 void
 vertical_grid(
-    gdImagePtr     gif,
     image_desc_t   *im )
 {   
     int xlab_sel;              /* which sort of label and grid ? */
-    time_t ti, tilab;
+    time_t ti, tilab, timajor;
     long factor;
     char graph_label[100];
-    gdPoint polyPoints[4];      /* points for filled graph and more*/
-
-    /* style for grid lines */
-    int     styleDotted[4];
+    double X0,Y0,Y1; /* points for filled graph and more*/
+    struct tm tm;
 
-    
     /* the type of time grid is determined by finding
        the number of seconds per pixel in the graph */
     
@@ -1824,30 +1758,35 @@ vertical_grid(
     }
     
     /* y coords are the same for every line ... */
-    polyPoints[0].y = im->yorigin;
-    polyPoints[1].y = im->yorigin-im->ysize;
+    Y0 = im->yorigin;
+    Y1 = im->yorigin-im->ysize;
+   
 
     /* paint the minor grid */
-    for(ti = find_first_time(im->start,
-                           im->xlab_user.gridtm,
-                           im->xlab_user.gridst);
-       ti < im->end; 
-       ti = find_next_time(ti,im->xlab_user.gridtm,im->xlab_user.gridst)
-       ){
-       /* are we inside the graph ? */
-       if (ti < im->start || ti > im->end) continue;
-       polyPoints[0].x = xtr(im,ti);
-       styleDotted[0] = graph_col[GRC_GRID].i;
-       styleDotted[1] = gdTransparent;
-
-       gdImageSetStyle(gif, styleDotted, 2);
-
-       gdImageLine(gif, polyPoints[0].x,polyPoints[0].y,
-                   polyPoints[0].x,polyPoints[1].y,gdStyled);
-       gdImageLine(gif, polyPoints[0].x,polyPoints[0].y-1,
-                   polyPoints[0].x,polyPoints[0].y+1,graph_col[GRC_GRID].i);
-       gdImageLine(gif, polyPoints[0].x,polyPoints[1].y-1,
-                   polyPoints[0].x,polyPoints[1].y+1,graph_col[GRC_GRID].i);
+    if (!(im->extra_flags & NOMINOR))
+    {
+        for(ti = find_first_time(im->start,
+                                im->xlab_user.gridtm,
+                                im->xlab_user.gridst),
+            timajor = find_first_time(im->start,
+                                im->xlab_user.mgridtm,
+                                im->xlab_user.mgridst);
+            ti < im->end; 
+            ti = find_next_time(ti,im->xlab_user.gridtm,im->xlab_user.gridst)
+            ){
+            /* are we inside the graph ? */
+            if (ti < im->start || ti > im->end) continue;
+            while (timajor < ti) {
+                timajor = find_next_time(timajor,
+                        im->xlab_user.mgridtm, im->xlab_user.mgridst);
+            }
+            if (ti == timajor) continue; /* skip as falls on major grid line */
+           X0 = xtr(im,ti);       
+           gfx_new_dashed_line(im->canvas,X0,Y0+1, X0,Y1-1,GRIDWIDTH,
+               im->graph_col[GRC_GRID],
+               im->grid_dash_on, im->grid_dash_off);
+           
+        }
     }
 
     /* paint the major grid */
@@ -1859,40 +1798,37 @@ vertical_grid(
        ){
        /* are we inside the graph ? */
        if (ti < im->start || ti > im->end) continue;
-       polyPoints[0].x = xtr(im,ti);
-       styleDotted[0] = graph_col[GRC_MGRID].i;
-       styleDotted[1] = gdTransparent;
-       gdImageSetStyle(gif, styleDotted, 2);
-
-       gdImageLine(gif, polyPoints[0].x,polyPoints[0].y,
-                   polyPoints[0].x,polyPoints[1].y,gdStyled);
-       gdImageLine(gif, polyPoints[0].x,polyPoints[0].y-2,
-                   polyPoints[0].x,polyPoints[0].y+2,graph_col[GRC_MGRID].i);
-       gdImageLine(gif, polyPoints[0].x,polyPoints[1].y-2,
-                   polyPoints[0].x,polyPoints[1].y+2,graph_col[GRC_MGRID].i);
+       X0 = xtr(im,ti);
+       gfx_new_dashed_line(im->canvas,X0,Y0+3, X0,Y1-2,MGRIDWIDTH,
+          im->graph_col[GRC_MGRID],
+          im->grid_dash_on, im->grid_dash_off);
+       
     }
     /* paint the labels below the graph */
-    for(ti = find_first_time(im->start,
+    for(ti = find_first_time(im->start - im->xlab_user.precis/2,
                            im->xlab_user.labtm,
                            im->xlab_user.labst);
-       ti <= im->end; 
+       ti <= im->end - im->xlab_user.precis/2
        ti = find_next_time(ti,im->xlab_user.labtm,im->xlab_user.labst)
        ){
-       int gr_pos,width;
         tilab= ti + im->xlab_user.precis/2; /* correct time for the label */
+       /* are we inside the graph ? */
+       if (tilab < im->start || tilab > im->end) continue;
 
 #if HAVE_STRFTIME
-       strftime(graph_label,99,im->xlab_user.stst,localtime(&tilab));
+       localtime_r(&tilab, &tm);
+       strftime(graph_label,99,im->xlab_user.stst, &tm);
 #else
 # error "your libc has no strftime I guess we'll abort the exercise here."
 #endif
-       width=strlen(graph_label) *  SmallFont->w;
-       gr_pos=xtr(im,tilab) - width/2;
-       if (gr_pos  >= im->xorigin 
-           && gr_pos + width <= im->xorigin+im->xsize) 
-           gdImageString(gif, SmallFont,
-                         gr_pos,  polyPoints[0].y+4,
-                         (unsigned char *)graph_label, graph_col[GRC_FONT].i);
+       gfx_new_text ( im->canvas,
+                     xtr(im,tilab), Y0+im->text_prop[TEXT_PROP_AXIS].size/1.5,
+                     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_CENTER, GFX_V_TOP,
+                     graph_label );
+       
     }
 
 }
@@ -1900,193 +1836,158 @@ vertical_grid(
 
 void 
 axis_paint(
-    image_desc_t   *im,
-    gdImagePtr     gif
-    )
+   image_desc_t   *im
+          )
 {   
     /* draw x and y axis */
-    gdImageLine(gif, im->xorigin+im->xsize,im->yorigin,
-               im->xorigin+im->xsize,im->yorigin-im->ysize,
-               graph_col[GRC_GRID].i);
-    
-    gdImageLine(gif, im->xorigin,im->yorigin-im->ysize,
-               im->xorigin+im->xsize,im->yorigin-im->ysize,
-               graph_col[GRC_GRID].i);
-
-    gdImageLine(gif, im->xorigin-4,im->yorigin,
-               im->xorigin+im->xsize+4,im->yorigin,
-               graph_col[GRC_FONT].i);
-
-    gdImageLine(gif, im->xorigin,im->yorigin,
-               im->xorigin,im->yorigin-im->ysize,
-               graph_col[GRC_GRID].i);
+    /* gfx_new_line ( im->canvas, im->xorigin+im->xsize,im->yorigin,
+                     im->xorigin+im->xsize,im->yorigin-im->ysize,
+                     GRIDWIDTH, im->graph_col[GRC_AXIS]);
+       
+       gfx_new_line ( im->canvas, im->xorigin,im->yorigin-im->ysize,
+                        im->xorigin+im->xsize,im->yorigin-im->ysize,
+                        GRIDWIDTH, im->graph_col[GRC_AXIS]); */
+   
+       gfx_new_line ( im->canvas, im->xorigin-4,im->yorigin,
+                        im->xorigin+im->xsize+4,im->yorigin,
+                        MGRIDWIDTH, im->graph_col[GRC_AXIS]);
+   
+       gfx_new_line ( im->canvas, im->xorigin,im->yorigin+4,
+                        im->xorigin,im->yorigin-im->ysize-4,
+                        MGRIDWIDTH, im->graph_col[GRC_AXIS]);
+   
     
     /* arrow for X axis direction */
-    gdImageLine(gif, im->xorigin+im->xsize+4, im->yorigin-3, im->xorigin+im->xsize+4, im->yorigin+3,graph_col[GRC_ARROW].i);
-    gdImageLine(gif, im->xorigin+im->xsize+4, im->yorigin-3, im->xorigin+im->xsize+9, im->yorigin,graph_col[GRC_ARROW].i);
-    gdImageLine(gif, im->xorigin+im->xsize+4, im->yorigin+3, im->xorigin+im->xsize+9, im->yorigin,graph_col[GRC_ARROW].i);
-
-    /*    gdImageLine(gif, im->xorigin+im->xsize-1, im->yorigin-3, im->xorigin+im->xsize-1, im->yorigin+3,graph_col[GRC_MGRID].i);
-    gdImageLine(gif, im->xorigin+im->xsize, im->yorigin-2, im->xorigin+im->xsize, im->yorigin+2,graph_col[GRC_MGRID].i);
-    gdImageLine(gif, im->xorigin+im->xsize+1, im->yorigin-2, im->xorigin+im->xsize+1, im->yorigin+2,graph_col[GRC_MGRID].i);
-    gdImageLine(gif, im->xorigin+im->xsize+2, im->yorigin-2, im->xorigin+im->xsize+2, im->yorigin+2,graph_col[GRC_MGRID].i);
-    gdImageLine(gif, im->xorigin+im->xsize+3, im->yorigin-1, im->xorigin+im->xsize+3, im->yorigin+1,graph_col[GRC_MGRID].i);
-    gdImageLine(gif, im->xorigin+im->xsize+4, im->yorigin-1, im->xorigin+im->xsize+4, im->yorigin+1,graph_col[GRC_MGRID].i);
-    gdImageLine(gif, im->xorigin+im->xsize+5, im->yorigin, im->xorigin+im->xsize+5, im->yorigin,graph_col[GRC_MGRID].i); */
-
-
+    gfx_new_area ( im->canvas, 
+                  im->xorigin+im->xsize+3,  im->yorigin-3,
+                  im->xorigin+im->xsize+3,  im->yorigin+4,
+                  im->xorigin+im->xsize+8,  im->yorigin+0.5, /* LINEOFFSET */
+                  im->graph_col[GRC_ARROW]);
 
 }
 
 void
-grid_paint(
-    image_desc_t   *im,
-    gdImagePtr     gif
-    )
+grid_paint(image_desc_t   *im)
 {   
     long i;
-    int boxH=8, boxV=8;
     int res=0;
-    gdPoint polyPoints[4];      /* points for filled graph and more*/
+    double X0,Y0; /* points for filled graph and more*/
+    gfx_node_t *node;
 
     /* draw 3d border */
-    gdImageLine(gif,0,0,im->xgif-1,0,graph_col[GRC_SHADEA].i);
-    gdImageLine(gif,1,1,im->xgif-2,1,graph_col[GRC_SHADEA].i);
-    gdImageLine(gif,0,0,0,im->ygif-1,graph_col[GRC_SHADEA].i);
-    gdImageLine(gif,1,1,1,im->ygif-2,graph_col[GRC_SHADEA].i);
-    gdImageLine(gif,im->xgif-1,0,im->xgif-1,im->ygif-1,graph_col[GRC_SHADEB].i);
-    gdImageLine(gif,0,im->ygif-1,im->xgif-1,im->ygif-1,graph_col[GRC_SHADEB].i);
-    gdImageLine(gif,im->xgif-2,1,im->xgif-2,im->ygif-2,graph_col[GRC_SHADEB].i);
-    gdImageLine(gif,1,im->ygif-2,im->xgif-2,im->ygif-2,graph_col[GRC_SHADEB].i);
-
-
+    node = gfx_new_area (im->canvas, 0,im->yimg,
+                                 2,im->yimg-2,
+                                 2,2,im->graph_col[GRC_SHADEA]);
+    gfx_add_point( node , im->ximg - 2, 2 );
+    gfx_add_point( node , im->ximg, 0 );
+    gfx_add_point( node , 0,0 );
+/*    gfx_add_point( node , 0,im->yimg ); */
+   
+    node =  gfx_new_area (im->canvas, 2,im->yimg-2,
+                                  im->ximg-2,im->yimg-2,
+                                  im->ximg - 2, 2,
+                                 im->graph_col[GRC_SHADEB]);
+    gfx_add_point( node ,   im->ximg,0);
+    gfx_add_point( node ,   im->ximg,im->yimg);
+    gfx_add_point( node ,   0,im->yimg);
+/*    gfx_add_point( node , 0,im->yimg ); */
+   
+   
     if (im->draw_x_grid == 1 )
-      vertical_grid(gif, im);
+      vertical_grid(im);
     
     if (im->draw_y_grid == 1){
        if(im->logarithmic){
-               res = horizontal_log_grid(gif,im);
+               res = horizontal_log_grid(im);
        } else {
-               res = horizontal_grid(gif,im);
+               res = draw_horizontal_grid(im);
        }
 
        /* dont draw horizontal grid if there is no min and max val */
        if (! res ) {
          char *nodata = "No Data found";
-         gdImageString(gif, LargeFont,
-                       im->xgif/2 
-                       - (strlen(nodata)*LargeFont->w)/2,
-                       (2*im->yorigin-im->ysize) / 2,
-                       (unsigned char *)nodata, graph_col[GRC_FONT].i);
+          gfx_new_text(im->canvas,im->ximg/2, (2*im->yorigin-im->ysize) / 2,
+                       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_CENTER, GFX_V_CENTER,
+                       nodata );          
        }
     }
 
-    /* yaxis description */
-    gdImageStringUp(gif, SmallFont,
-                   7,
-                   (im->yorigin - im->ysize/2
-                    +(strlen(im->ylegend)*SmallFont->w)/2 ),
-                   (unsigned char *)im->ylegend, graph_col[GRC_FONT].i);
-    
+    /* yaxis unit description */
+    gfx_new_text( im->canvas,
+                  7, (im->yorigin - im->ysize/2),
+                  im->graph_col[GRC_FONT],
+                  im->text_prop[TEXT_PROP_UNIT].font,
+                  im->text_prop[TEXT_PROP_UNIT].size, im->tabwidth, 
+                  RRDGRAPH_YLEGEND_ANGLE,
+                  GFX_H_LEFT, GFX_V_CENTER,
+                  im->ylegend);
 
     /* graph title */
-    gdImageString(gif, LargeFont,
-                   im->xgif/2 
-                   - (strlen(im->title)*LargeFont->w)/2,
-                 8,
-                   (unsigned char *)im->title, graph_col[GRC_FONT].i);
+    gfx_new_text( im->canvas,
+                 im->ximg/2, im->text_prop[TEXT_PROP_TITLE].size*1.2,
+                 im->graph_col[GRC_FONT],
+                 im->text_prop[TEXT_PROP_TITLE].font,
+                 im->text_prop[TEXT_PROP_TITLE].size, im->tabwidth, 0.0,
+                 GFX_H_CENTER, GFX_V_CENTER,
+                 im->title);
     
     /* graph labels */
-    if( !(im->extra_flags & NOLEGEND) ) {
-      for(i=0;i<im->gdes_c;i++){
-       if(im->gdes[i].legend[0] =='\0')
-           continue;
-       
-       if(im->gdes[i].gf != GF_GPRINT && im->gdes[i].gf != GF_COMMENT){
-           
-           polyPoints[0].x = im->gdes[i].legloc.x;
-           polyPoints[0].y = im->gdes[i].legloc.y+1;
-           polyPoints[1].x = polyPoints[0].x+boxH;
-           polyPoints[2].x = polyPoints[0].x+boxH;
-           polyPoints[3].x = polyPoints[0].x;
-           polyPoints[1].y = polyPoints[0].y;
-           polyPoints[2].y = polyPoints[0].y+boxV;
-           polyPoints[3].y = polyPoints[0].y+boxV;
-           gdImageFilledPolygon(gif,polyPoints,4,im->gdes[i].col.i);
-           gdImagePolygon(gif,polyPoints,4,graph_col[GRC_FRAME].i);
-       
-           gdImageString(gif, SmallFont,
-                         polyPoints[0].x+boxH+6, 
-                         polyPoints[0].y-1,
-                         (unsigned char *)im->gdes[i].legend,
-                         graph_col[GRC_FONT].i);
-       } else {
-           polyPoints[0].x = im->gdes[i].legloc.x;
-           polyPoints[0].y = im->gdes[i].legloc.y;
-           
-           gdImageString(gif, SmallFont,
-                         polyPoints[0].x, 
-                         polyPoints[0].y,
-                         (unsigned char *)im->gdes[i].legend,
-                         graph_col[GRC_FONT].i);
-       }
-      }
+    if( !(im->extra_flags & NOLEGEND) & !(im->extra_flags & ONLY_GRAPH) ) {
+            for(i=0;i<im->gdes_c;i++){
+                    if(im->gdes[i].legend[0] =='\0')
+                            continue;
+                    
+                    /* im->gdes[i].leg_y is the bottom of the legend */
+                    X0 = im->gdes[i].leg_x;
+                    Y0 = im->gdes[i].leg_y;
+                    gfx_new_text ( im->canvas, X0, Y0,
+                                  im->graph_col[GRC_FONT],
+                                  im->text_prop[TEXT_PROP_LEGEND].font,
+                                  im->text_prop[TEXT_PROP_LEGEND].size,
+                                  im->tabwidth,0.0, GFX_H_LEFT, GFX_V_BOTTOM,
+                                  im->gdes[i].legend );
+                   /* The legend for GRAPH items starts with "M " to have
+                       enough space for the box */
+                    if (          im->gdes[i].gf != GF_PRINT &&
+                                  im->gdes[i].gf != GF_GPRINT &&
+                                   im->gdes[i].gf != GF_COMMENT) {
+                            int boxH, boxV;
+                            
+                            boxH = gfx_get_text_width(im->canvas, 0,
+                                                      im->text_prop[TEXT_PROP_LEGEND].font,
+                                                      im->text_prop[TEXT_PROP_LEGEND].size,
+                                                      im->tabwidth,"M", 0)*1.2;
+                            boxV = boxH;
+                            
+                            /* make sure transparent colors show up all the same */
+                           node = gfx_new_area(im->canvas,
+                                                X0,Y0-boxV,
+                                                X0,Y0,
+                                                X0+boxH,Y0,
+                                                im->graph_col[GRC_CANVAS]);
+                            gfx_add_point ( node, X0+boxH, Y0-boxV );
+
+                            node = gfx_new_area(im->canvas,
+                                                X0,Y0-boxV,
+                                                X0,Y0,
+                                                X0+boxH,Y0,
+                                                im->gdes[i].col);
+                            gfx_add_point ( node, X0+boxH, Y0-boxV );
+                            node = gfx_new_line(im->canvas,
+                                                X0,Y0-boxV, X0,Y0,
+                                                1,im->graph_col[GRC_FONT]);
+                            gfx_add_point(node,X0+boxH,Y0);
+                            gfx_add_point(node,X0+boxH,Y0-boxV);
+                            gfx_close_path(node);
+                    }
+            }
     }
-    
-    
-    gator(gif, (int) im->xgif-5, 5);
-
 }
 
 
-gdImagePtr
-MkLineBrush(image_desc_t *im,long cosel, enum gf_en typsel){
-  gdImagePtr brush;
-  int pen;
-  switch (typsel){
-  case GF_LINE1:
-    brush=gdImageCreate(1,1);
-    break;
-  case GF_LINE2:
-    brush=gdImageCreate(2,2);
-    break;
-  case GF_LINE3:
-    brush=gdImageCreate(3,3);
-    break;
-  default:
-    return NULL;
-  }
-
-  gdImageColorTransparent(brush, 
-                         gdImageColorAllocate(brush, 0, 0, 0));
-
-  pen = gdImageColorAllocate(brush, 
-                            im->gdes[cosel].col.red,
-                            im->gdes[cosel].col.green,
-                            im->gdes[cosel].col.blue);
-    
-  switch (typsel){
-  case GF_LINE1:
-    gdImageSetPixel(brush,0,0,pen);
-    break;
-  case GF_LINE2:
-    gdImageSetPixel(brush,0,0,pen);
-    gdImageSetPixel(brush,0,1,pen);
-    gdImageSetPixel(brush,1,0,pen);
-    gdImageSetPixel(brush,1,1,pen);
-    break;
-  case GF_LINE3:
-    gdImageSetPixel(brush,1,0,pen);
-    gdImageSetPixel(brush,0,1,pen);
-    gdImageSetPixel(brush,1,1,pen);
-    gdImageSetPixel(brush,2,1,pen);
-    gdImageSetPixel(brush,1,2,pen);
-    break;
-  default:
-    return NULL;
-  }
-  return brush;
-}
 /*****************************************************
  * lazy check make sure we rely need to create this graph
  *****************************************************/
@@ -2094,322 +1995,598 @@ MkLineBrush(image_desc_t *im,long cosel, enum gf_en typsel){
 int lazy_check(image_desc_t *im){
     FILE *fd = NULL;
        int size = 1;
-    struct stat  gifstat;
+    struct stat  imgstat;
     
     if (im->lazy == 0) return 0; /* no lazy option */
-    if (stat(im->graphfile,&gifstat) != 0) 
+    if (stat(im->graphfile,&imgstat) != 0) 
       return 0; /* can't stat */
     /* one pixel in the existing graph is more then what we would
        change here ... */
-    if (time(NULL) - gifstat.st_mtime > 
+    if (time(NULL) - imgstat.st_mtime > 
        (im->end - im->start) / im->xsize) 
       return 0;
     if ((fd = fopen(im->graphfile,"rb")) == NULL) 
       return 0; /* the file does not exist */
-    switch (im->imgformat) {
-    case IF_GIF:
-          size = GifSize(fd,&(im->xgif),&(im->ygif));
-          break;
+    switch (im->canvas->imgformat) {
     case IF_PNG:
-          size = PngSize(fd,&(im->xgif),&(im->ygif));
+          size = PngSize(fd,&(im->ximg),&(im->yimg));
           break;
+    default:
+          size = 1;
     }
     fclose(fd);
     return size;
 }
 
-/* draw that picture thing ... */
-int
-graph_paint(image_desc_t *im, char ***calcpr)
+#ifdef WITH_PIECHART
+void
+pie_part(image_desc_t *im, gfx_color_t color,
+           double PieCenterX, double PieCenterY, double Radius,
+           double startangle, double endangle)
 {
-    int i,ii;
-    int lazy =     lazy_check(im);
-    FILE  *fo;
-    
-    /* gif stuff */
-    gdImagePtr gif,brush;
+    gfx_node_t *node;
+    double angle;
+    double step=M_PI/50; /* Number of iterations for the circle;
+                        ** 10 is definitely too low, more than
+                        ** 50 seems to be overkill
+                        */
+
+    /* Strange but true: we have to work clockwise or else
+    ** anti aliasing nor transparency don't work.
+    **
+    ** This test is here to make sure we do it right, also
+    ** this makes the for...next loop more easy to implement.
+    ** The return will occur if the user enters a negative number
+    ** (which shouldn't be done according to the specs) or if the
+    ** programmers do something wrong (which, as we all know, never
+    ** happens anyway :)
+    */
+    if (endangle<startangle) return;
 
-    double areazero = 0.0;
-    enum gf_en stack_gf = GF_PRINT;
-    graph_desc_t *lastgdes = NULL;    
-    gdPoint canvas[4], back[4];         /* points for canvas*/
+    /* Hidden feature: Radius decreases each full circle */
+    angle=startangle;
+    while (angle>=2*M_PI) {
+       angle  -= 2*M_PI;
+       Radius *= 0.8;
+    }
 
-    /* if we are lazy and there is nothing to PRINT ... quit now */
-    if (lazy && im->prt_c==0) return 0;
-    
-    /* pull the data from the rrd files ... */
-    
-    if(data_fetch(im)==-1)
-       return -1;
+    node=gfx_new_area(im->canvas,
+               PieCenterX+sin(startangle)*Radius,
+               PieCenterY-cos(startangle)*Radius,
+               PieCenterX,
+               PieCenterY,
+               PieCenterX+sin(endangle)*Radius,
+               PieCenterY-cos(endangle)*Radius,
+               color);
+    for (angle=endangle;angle-startangle>=step;angle-=step) {
+       gfx_add_point(node,
+               PieCenterX+sin(angle)*Radius,
+               PieCenterY-cos(angle)*Radius );
+    }
+}
 
-    /* evaluate VDEF and CDEF operations ... */
-    if(data_calc(im)==-1)
-       return -1;
+#endif
 
-    /* calculate and PRINT and GPRINT definitions. We have to do it at
-     * this point because it will affect the length of the legends
-     * if there are no graph elements we stop here ... 
-     * if we are lazy, try to quit ... 
-     */
-    i=print_calc(im,calcpr);
-    if(i<0) return -1;
-    if(i==0 || lazy) return 0;
+int
+graph_size_location(image_desc_t *im, int elements
 
-    /* get actual drawing data and find min and max values*/
-    if(data_proc(im)==-1)
-       return -1;
+#ifdef WITH_PIECHART
+, int piechart
+#endif
 
-    if(!im->logarithmic){si_unit(im);}        /* identify si magnitude Kilo, Mega Giga ? */
+ )
+{
+    /* The actual size of the image to draw is determined from
+    ** several sources.  The size given on the command line is
+    ** the graph area but we need more as we have to draw labels
+    ** and other things outside the graph area
+    */
 
-    if(!im->rigid && ! im->logarithmic)
-       expand_range(im);   /* make sure the upper and lower limit are
-                          sensible values */
+    /* +-+-------------------------------------------+
+    ** |l|.................title.....................|
+    ** |e+--+-------------------------------+--------+
+    ** |b| b|                               |        |
+    ** |a| a|                               |  pie   |
+    ** |l| l|          main graph area      | chart  |
+    ** |.| .|                               |  area  |
+    ** |t| y|                               |        |
+    ** |r+--+-------------------------------+--------+
+    ** |e|  | x-axis labels                 |        |
+    ** |v+--+-------------------------------+--------+
+    ** | |..............legends......................|
+    ** +-+-------------------------------------------+
+    */
+    int Xvertical=0,   Yvertical=0,
+       Xtitle   =0,    Ytitle   =0,
+       Xylabel  =0,    Yylabel  =0,
+       Xmain    =0,    Ymain    =0,
+       Xpie     =0,    Ypie     =0,
+       Xxlabel  =0,    Yxlabel  =0,
+#if 0
+       Xlegend  =0,    Ylegend  =0,
+#endif
+        Xspacing =10,  Yspacing =10;
 
-    /* init xtr and ytr */
-    /* determine the actual size of the gif to draw. The size given
-       on the cmdline is the graph area. But we need more as we have
-       draw labels and other things outside the graph area */
+    if (im->extra_flags & ONLY_GRAPH) {
+       Xspacing =0;
+       Yspacing =0;
+    } else {
+        if (im->ylegend[0] != '\0') {
+           Xvertical = im->text_prop[TEXT_PROP_UNIT].size *2;
+           Yvertical = gfx_get_text_width(im->canvas, 0,
+                                          im->text_prop[TEXT_PROP_UNIT].font,
+                                          im->text_prop[TEXT_PROP_UNIT].size,
+                                          im->tabwidth,im->ylegend, 0);
+        }
+    }
 
+    if (im->title[0] != '\0') {
+       /* The title is placed "inbetween" two text lines so it
+       ** automatically has some vertical spacing.  The horizontal
+       ** spacing is added here, on each side.
+       */
+       Xtitle = gfx_get_text_width(im->canvas, 0,
+               im->text_prop[TEXT_PROP_TITLE].font,
+               im->text_prop[TEXT_PROP_TITLE].size,
+               im->tabwidth,
+               im->title, 0) + 2*Xspacing;
+       Ytitle = im->text_prop[TEXT_PROP_TITLE].size*2.5;
+    }
 
-    im->xorigin = 10 + 9 * SmallFont->w+SmallFont->h;
-    xtr(im,0); 
+    if (elements) {
+       Xmain=im->xsize;
+       Ymain=im->ysize;
+       if (im->draw_x_grid) {
+           Xxlabel=Xmain;
+           Yxlabel=im->text_prop[TEXT_PROP_AXIS].size *2.5;
+       }
+       if (im->draw_y_grid) {
+           Xylabel=im->text_prop[TEXT_PROP_AXIS].size *6;
+           Yylabel=Ymain;
+       }
+    }
 
-    im->yorigin = 14 + im->ysize;
-    ytr(im,DNAN);
+#ifdef WITH_PIECHART
+    if (piechart) {
+       im->piesize=im->xsize<im->ysize?im->xsize:im->ysize;
+       Xpie=im->piesize;
+       Ypie=im->piesize;
+    }
+#endif
 
-    if(im->title[0] != '\0')
-       im->yorigin += (LargeFont->h+4);
+    /* Now calculate the total size.  Insert some spacing where
+       desired.  im->xorigin and im->yorigin need to correspond
+       with the lower left corner of the main graph area or, if
+       this one is not set, the imaginary box surrounding the
+       pie chart area. */
 
-    im->xgif=20+im->xsize + im->xorigin;
-    im->ygif= im->yorigin+2*SmallFont->h;
-    
-    /* determine where to place the legends onto the graphics.
-       and set im->ygif to match space requirements for text */
-    if(leg_place(im)==-1)
-     return -1;
+    /* The legend width cannot yet be determined, as a result we
+    ** have problems adjusting the image to it.  For now, we just
+    ** forget about it at all; the legend will have to fit in the
+    ** size already allocated.
+    */
+    im->ximg = Xmain;
 
-    gif=gdImageCreate(im->xgif,im->ygif);
+    if ( !(im->extra_flags & ONLY_GRAPH) ) {
+        im->ximg = Xylabel + Xmain + Xpie + 2 * Xspacing;
+    }
 
-    gdImageInterlace(gif, im->interlaced);
-    
-    /* allocate colors for the screen elements */
-    for(i=0;i<DIM(graph_col);i++)
-       /* check for user override values */
-       if(im->graph_col[i].red != -1)
-           graph_col[i].i = 
-               gdImageColorAllocate( gif,
-                                     im->graph_col[i].red, 
-                                     im->graph_col[i].green, 
-                                     im->graph_col[i].blue);
-       else
-           graph_col[i].i = 
-               gdImageColorAllocate( gif,
-                                     graph_col[i].red, 
-                                     graph_col[i].green, 
-                                     graph_col[i].blue);
-       
-    
-    /* allocate colors for the graph */
-    for(i=0;i<im->gdes_c;i++)
-       /* only for elements which have a color defined */
-       if (im->gdes[i].col.red != -1)
-           im->gdes[i].col.i = 
-               gdImageColorAllocate(gif,
-                                    im->gdes[i].col.red,
-                                    im->gdes[i].col.green,
-                                    im->gdes[i].col.blue);
-    
-    
-    /* the actual graph is created by going through the individual
-       graph elements and then drawing them */
-    
-    back[0].x = 0;
-    back[0].y = 0;
-    back[1].x = back[0].x+im->xgif;
-    back[1].y = back[0].y;
-    back[2].x = back[1].x;
-    back[2].y = back[0].y+im->ygif;
-    back[3].x = back[0].x;
-    back[3].y = back[2].y;
-
-    gdImageFilledPolygon(gif,back,4,graph_col[GRC_BACK].i);
-
-    canvas[0].x = im->xorigin;
-    canvas[0].y = im->yorigin;
-    canvas[1].x = canvas[0].x+im->xsize;
-    canvas[1].y = canvas[0].y;
-    canvas[2].x = canvas[1].x;
-    canvas[2].y = canvas[0].y-im->ysize;
-    canvas[3].x = canvas[0].x;
-    canvas[3].y = canvas[2].y;
-
-    gdImageFilledPolygon(gif,canvas,4,graph_col[GRC_CANVAS].i);
+    if (Xmain) im->ximg += Xspacing;
+    if (Xpie) im->ximg += Xspacing;
 
-    if (im->minval > 0.0)
-       areazero = im->minval;
-    if (im->maxval < 0.0)
-       areazero = im->maxval;
+    if (im->extra_flags & ONLY_GRAPH) {
+       im->xorigin = 0;
+    } else {
+       im->xorigin = Xspacing + Xylabel;
+    }
 
-    axis_paint(im,gif);
+    if (Xtitle > im->ximg) im->ximg = Xtitle;
+    if (Xvertical) {
+       im->ximg += Xvertical;
+       im->xorigin += Xvertical;
+    }
+    xtr(im,0);
+
+    /* The vertical size is interesting... we need to compare
+    ** the sum of {Ytitle, Ymain, Yxlabel, Ylegend} with Yvertical
+    ** however we need to know {Ytitle+Ymain+Yxlabel} in order to
+    ** start even thinking about Ylegend.
+    **
+    ** Do it in three portions: First calculate the inner part,
+    ** then do the legend, then adjust the total height of the img.
+    */
 
-    for(i=0;i<im->gdes_c;i++){ 
-        
-       switch(im->gdes[i].gf){
-       case GF_CDEF:
-       case GF_VDEF:
-       case GF_DEF:
-       case GF_PRINT:
-       case GF_GPRINT:
-       case GF_COMMENT:
-       case GF_HRULE:
-       case GF_VRULE:
-               break;
-       case GF_TICK:
-               for (ii = 0; ii < im->xsize; ii++)
-               {
-                  if (!isnan(im->gdes[i].p_data[ii]) && 
-                          im->gdes[i].p_data[ii] > 0.0)
-                  { 
-                         /* generate a tick */
-                         gdImageLine(gif, im -> xorigin + ii, 
-                                im -> yorigin - (im -> gdes[i].yrule * im -> ysize),
-                                im -> xorigin + ii, 
-                                im -> yorigin,
-                                im -> gdes[i].col.i);
-                  }
-               }
-               break;
-       case GF_LINE1:
-       case GF_LINE2:
-       case GF_LINE3:
-       case GF_AREA:
-           stack_gf = im->gdes[i].gf;
-       case GF_STACK:      
-           /* fix data points at oo and -oo */
-           for(ii=0;ii<im->xsize;ii++){
-               if (isinf(im->gdes[i].p_data[ii])){
-                   if (im->gdes[i].p_data[ii] > 0) {
-                       im->gdes[i].p_data[ii] = im->maxval ;
-                   } else {
-                       im->gdes[i].p_data[ii] = im->minval ;
-                   }               
-               
-               }
-           }
+    /* reserve space for main and/or pie */
 
-           if (im->gdes[i].col.i != -1){               
-              /* GF_LINE and frined */
-              if(stack_gf == GF_LINE1 || stack_gf == GF_LINE2 || stack_gf == GF_LINE3 ){
-                  brush = MkLineBrush(im,i,stack_gf);
-                  gdImageSetBrush(gif, brush);
-                  for(ii=1;ii<im->xsize;ii++){
-                      if (isnan(im->gdes[i].p_data[ii-1]) ||
-                          isnan(im->gdes[i].p_data[ii]))
-                            continue;
-                      gdImageLine(gif,
-                                    ii+im->xorigin-1,ytr(im,im->gdes[i].p_data[ii-1]),
-                                    ii+im->xorigin,ytr(im,im->gdes[i].p_data[ii]),
-                                    gdBrushed);
-                        
-                    }
-                    gdImageDestroy(brush);
-                }
-                else 
-                    /* GF_AREA STACK type*/
-                    if (im->gdes[i].gf == GF_STACK )
-                        for(ii=0;ii<im->xsize;ii++){
-                           if(isnan(im->gdes[i].p_data[ii])){
-                               im->gdes[i].p_data[ii] = lastgdes->p_data[ii];
-                               continue;
-                           }
-                           
-                           if (lastgdes->p_data[ii] == im->gdes[i].p_data[ii]){
-                               continue;
-                           }
-                           gdImageLine(gif,
-                                       ii+im->xorigin,ytr(im,lastgdes->p_data[ii]),
-                                       ii+im->xorigin,ytr(im,im->gdes[i].p_data[ii]),
-                                       im->gdes[i].col.i);
-                       }
-              
-                   else /* simple GF_AREA */
-                       for(ii=0;ii<im->xsize;ii++){
-                            if (isnan(im->gdes[i].p_data[ii])) {
-                               im->gdes[i].p_data[ii] = 0;
-                                continue;
-                           }
-                            gdImageLine(gif,
-                                        ii+im->xorigin,ytr(im,areazero),
-                                        ii+im->xorigin,ytr(im,im->gdes[i].p_data[ii]),
-                                        im->gdes[i].col.i);
-                        }
-          }
-          lastgdes = &(im->gdes[i]);              
-          break;
-       }
+    if (im->extra_flags & ONLY_GRAPH) {
+        im->yimg = Ymain;
+    } else {
+        im->yimg = Ymain + Yxlabel;
     }
-    
-    grid_paint(im,gif);
 
-    /* the RULES are the last thing to paint ... */
-    for(i=0;i<im->gdes_c;i++){ 
-        
-       switch(im->gdes[i].gf){
-       case GF_HRULE:
-printf("DEBUG: HRULE at %f\n",im->gdes[i].yrule);
-           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)
-             gdImageLine(gif,
-                         im->xorigin,ytr(im,im->gdes[i].yrule),
-                         im->xorigin+im->xsize,ytr(im,im->gdes[i].yrule),
-                         im->gdes[i].col.i); 
-           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)
-               gdImageLine(gif,
-                       xtr(im,im->gdes[i].xrule),im->yorigin,
-                       xtr(im,im->gdes[i].xrule),im->yorigin-im->ysize,
-                       im->gdes[i].col.i); 
-           break;
-       default:
-           break;
-       }
+    if (im->yimg < Ypie) im->yimg = Ypie;
+
+    if (im->extra_flags & ONLY_GRAPH) {
+        im->yorigin = im->yimg;
+    } else {
+        im->yorigin = im->yimg - Yxlabel;
     }
 
-    if (strcmp(im->graphfile,"-")==0) {
-#ifdef WIN32
-        /* Change translation mode for stdout to BINARY */
-        _setmode( _fileno( stdout ), O_BINARY );
-#endif
-        fo = stdout;
+    /* reserve space for the title *or* some padding above the graph */
+    if (Ytitle) {
+       im->yimg += Ytitle;
+       im->yorigin += Ytitle;
     } else {
-       if ((fo = fopen(im->graphfile,"wb")) == NULL) {
-           rrd_set_error("Opening '%s' for write: %s",im->graphfile, strerror(errno));
-           return (-1);
-       }
+       im->yimg += Yspacing;
+       im->yorigin += Yspacing;
     }
-    switch (im->imgformat) {
-    case IF_GIF:
-       gdImageGif(gif, fo);    
-       break;
-    case IF_PNG:
-       gdImagePng(gif, fo);    
-       break;
+    /* reserve space for padding below the graph */
+    im->yimg += Yspacing;
+    ytr(im,DNAN);
+
+    /* Determine where to place the legends onto the image.
+    ** Adjust im->yimg to match the space requirements.
+    */
+    if(leg_place(im)==-1)
+       return -1;
+
+    /* last of three steps: check total height of image */
+    if (im->yimg < Yvertical) im->yimg = Yvertical;
+
+#if 0
+    if (Xlegend > im->ximg) {
+       im->ximg = Xlegend;
+       /* reposition Pie */
     }
-    if (strcmp(im->graphfile,"-") != 0)
-       fclose(fo);
-    gdImageDestroy(gif);
+#endif
+
+#ifdef WITH_PIECHART
+    /* The pie is placed in the upper right hand corner,
+    ** just below the title (if any) and with sufficient
+    ** padding.
+    */
+    if (elements) {
+       im->pie_x = im->ximg - Xspacing - Xpie/2;
+        im->pie_y = im->yorigin-Ymain+Ypie/2;
+    } else {
+       im->pie_x = im->ximg/2;
+        im->pie_y = im->yorigin-Ypie/2;
+    }
+#endif
 
     return 0;
 }
 
+/* draw that picture thing ... */
+int
+graph_paint(image_desc_t *im, char ***calcpr)
+{
+  int i,ii;
+  int lazy =     lazy_check(im);
+#ifdef WITH_PIECHART
+  int piechart = 0;
+  double PieStart=0.0;
+#endif
+  FILE  *fo;
+  gfx_node_t *node;
+  
+  double areazero = 0.0;
+  enum gf_en stack_gf = GF_PRINT;
+  graph_desc_t *lastgdes = NULL;    
+
+  /* if we are lazy and there is nothing to PRINT ... quit now */
+  if (lazy && im->prt_c==0) return 0;
+
+  /* pull the data from the rrd files ... */
+  
+  if(data_fetch(im)==-1)
+    return -1;
+
+  /* evaluate VDEF and CDEF operations ... */
+  if(data_calc(im)==-1)
+    return -1;
+
+#ifdef WITH_PIECHART  
+  /* check if we need to draw a piechart */
+  for(i=0;i<im->gdes_c;i++){
+    if (im->gdes[i].gf == GF_PART) {
+      piechart=1;
+      break;
+    }
+  }
+#endif
+
+  /* calculate and PRINT and GPRINT definitions. We have to do it at
+   * this point because it will affect the length of the legends
+   * if there are no graph elements we stop here ... 
+   * if we are lazy, try to quit ... 
+   */
+  i=print_calc(im,calcpr);
+  if(i<0) return -1;
+  if(((i==0)
+#ifdef WITH_PIECHART
+&&(piechart==0)
+#endif
+) || lazy) return 0;
+
+#ifdef WITH_PIECHART
+  /* If there's only the pie chart to draw, signal this */
+  if (i==0) piechart=2;
+#endif
+  
+  /* get actual drawing data and find min and max values*/
+  if(data_proc(im)==-1)
+    return -1;
+  
+  if(!im->logarithmic){si_unit(im);}        /* identify si magnitude Kilo, Mega Giga ? */
+  
+  if(!im->rigid && ! im->logarithmic)
+    expand_range(im);   /* make sure the upper and lower limit are
+                           sensible values */
+
+  if (!calc_horizontal_grid(im))
+    return -1;
+
+  if (im->gridfit)
+    apply_gridfit(im);
+
+
+/**************************************************************
+ *** Calculating sizes and locations became a bit confusing ***
+ *** so I moved this into a separate function.              ***
+ **************************************************************/
+  if(graph_size_location(im,i
+#ifdef WITH_PIECHART
+,piechart
+#endif
+)==-1)
+    return -1;
+
+  /* the actual graph is created by going through the individual
+     graph elements and then drawing them */
+  
+  node=gfx_new_area ( im->canvas,
+                      0, 0,
+                      im->ximg, 0,
+                      im->ximg, im->yimg,
+                      im->graph_col[GRC_BACK]);
+
+  gfx_add_point(node,0, im->yimg);
+
+#ifdef WITH_PIECHART
+  if (piechart != 2) {
+#endif
+    node=gfx_new_area ( im->canvas,
+                      im->xorigin,             im->yorigin, 
+                      im->xorigin + im->xsize, im->yorigin,
+                      im->xorigin + im->xsize, im->yorigin-im->ysize,
+                      im->graph_col[GRC_CANVAS]);
+  
+    gfx_add_point(node,im->xorigin, im->yorigin - im->ysize);
+
+    if (im->minval > 0.0)
+      areazero = im->minval;
+    if (im->maxval < 0.0)
+      areazero = im->maxval;
+#ifdef WITH_PIECHART
+   }
+#endif
+
+#ifdef WITH_PIECHART
+  if (piechart) {
+    pie_part(im,im->graph_col[GRC_CANVAS],im->pie_x,im->pie_y,im->piesize*0.5,0,2*M_PI);
+  }
+#endif
+
+  for(i=0;i<im->gdes_c;i++){
+    switch(im->gdes[i].gf){
+    case GF_CDEF:
+    case GF_VDEF:
+    case GF_DEF:
+    case GF_PRINT:
+    case GF_GPRINT:
+    case GF_COMMENT:
+    case GF_HRULE:
+    case GF_VRULE:
+    case GF_XPORT:
+    case GF_SHIFT:
+      break;
+    case GF_TICK:
+      for (ii = 0; ii < im->xsize; ii++)
+        {
+          if (!isnan(im->gdes[i].p_data[ii]) && 
+              im->gdes[i].p_data[ii] > 0.0)
+            { 
+              /* generate a tick */
+              gfx_new_line(im->canvas, im -> xorigin + ii, 
+                           im -> yorigin - (im -> gdes[i].yrule * im -> ysize),
+                           im -> xorigin + ii, 
+                           im -> yorigin,
+                           1.0,
+                           im -> gdes[i].col );
+            }
+        }
+      break;
+    case GF_LINE:
+    case GF_AREA:
+      stack_gf = im->gdes[i].gf;
+    case GF_STACK:          
+      /* fix data points at oo and -oo */
+      for(ii=0;ii<im->xsize;ii++){
+        if (isinf(im->gdes[i].p_data[ii])){
+          if (im->gdes[i].p_data[ii] > 0) {
+            im->gdes[i].p_data[ii] = im->maxval ;
+          } else {
+            im->gdes[i].p_data[ii] = im->minval ;
+          }                 
+          
+        }
+      } /* for */
+      
+      if (im->gdes[i].col != 0x0){               
+        /* GF_LINE and friend */
+        if(stack_gf == GF_LINE ){
+          node = NULL;
+          for(ii=1;ii<im->xsize;ii++){
+            if ( ! isnan(im->gdes[i].p_data[ii-1])
+                 && ! isnan(im->gdes[i].p_data[ii])){
+              if (node == NULL){
+                node = gfx_new_line(im->canvas,
+                                    ii-1+im->xorigin,ytr(im,im->gdes[i].p_data[ii-1]),
+                                    ii+im->xorigin,ytr(im,im->gdes[i].p_data[ii]),
+                                    im->gdes[i].linewidth,
+                                    im->gdes[i].col);
+              } else {
+                gfx_add_point(node,ii+im->xorigin,ytr(im,im->gdes[i].p_data[ii]));
+              }
+            } else {
+              node = NULL;
+            }
+          }
+        } else {
+          int area_start=-1;
+          node = NULL;
+          for(ii=1;ii<im->xsize;ii++){
+            /* open an area */
+            if ( ! isnan(im->gdes[i].p_data[ii-1])
+                 && ! isnan(im->gdes[i].p_data[ii])){
+              if (node == NULL){
+                float ybase = 0.0;
+/*
+                if (im->gdes[i].gf == GF_STACK) {
+*/
+               if ( (im->gdes[i].gf == GF_STACK)
+                 || (im->gdes[i].stack) ) {
+
+                  ybase = ytr(im,lastgdes->p_data[ii-1]);
+                } else {
+                  ybase =  ytr(im,areazero);
+                }
+                area_start = ii-1;
+                node = gfx_new_area(im->canvas,
+                                    ii-1+im->xorigin,ybase,
+                                    ii-1+im->xorigin,ytr(im,im->gdes[i].p_data[ii-1]),
+                                    ii+im->xorigin,ytr(im,im->gdes[i].p_data[ii]),
+                                    im->gdes[i].col
+                                    );
+              } else {
+                gfx_add_point(node,ii+im->xorigin,ytr(im,im->gdes[i].p_data[ii]));
+              }
+            }
+
+            if ( node != NULL && (ii+1==im->xsize || isnan(im->gdes[i].p_data[ii]) )){
+              /* GF_AREA STACK type*/
+/*
+              if (im->gdes[i].gf == GF_STACK ) {
+*/
+             if ( (im->gdes[i].gf == GF_STACK)
+               || (im->gdes[i].stack) ) {
+                int iii;
+                for (iii=ii-1;iii>area_start;iii--){
+                  gfx_add_point(node,iii+im->xorigin,ytr(im,lastgdes->p_data[iii]));
+                }
+              } else {
+                gfx_add_point(node,ii+im->xorigin,ytr(im,areazero));
+              };
+              node=NULL;
+            };
+          }             
+        } /* else GF_LINE */
+      } /* if color != 0x0 */
+      /* make sure we do not run into trouble when stacking on NaN */
+      for(ii=0;ii<im->xsize;ii++){
+        if (isnan(im->gdes[i].p_data[ii])) {
+          if (lastgdes && (im->gdes[i].gf == GF_STACK)) {
+            im->gdes[i].p_data[ii] = lastgdes->p_data[ii];
+          } else {
+            im->gdes[i].p_data[ii] =  ytr(im,areazero);
+          }
+        }
+      } 
+      lastgdes = &(im->gdes[i]);                         
+      break;
+#ifdef WITH_PIECHART
+    case GF_PART:
+      if(isnan(im->gdes[i].yrule)) /* fetch variable */
+       im->gdes[i].yrule = im->gdes[im->gdes[i].vidx].vf.val;
+     
+      if (finite(im->gdes[i].yrule)) { /* even the fetched var can be NaN */
+       pie_part(im,im->gdes[i].col,
+               im->pie_x,im->pie_y,im->piesize*0.4,
+               M_PI*2.0*PieStart/100.0,
+               M_PI*2.0*(PieStart+im->gdes[i].yrule)/100.0);
+       PieStart += im->gdes[i].yrule;
+      }
+      break;
+#endif
+       
+    } /* switch */
+  }
+#ifdef WITH_PIECHART
+  if (piechart==2) {
+    im->draw_x_grid=0;
+    im->draw_y_grid=0;
+  }
+#endif
+
+  if( !(im->extra_flags & ONLY_GRAPH) )  
+      axis_paint(im);
+
+  /* grid_paint also does the text */
+  if( !(im->extra_flags & ONLY_GRAPH) )  
+    grid_paint(im);
+  
+  /* the RULES are the last thing to paint ... */
+  for(i=0;i<im->gdes_c;i++){    
+    
+    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,
+                     im->xorigin,ytr(im,im->gdes[i].yrule),
+                     im->xorigin+im->xsize,ytr(im,im->gdes[i].yrule),
+                     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,
+                     xtr(im,im->gdes[i].xrule),im->yorigin,
+                     xtr(im,im->gdes[i].xrule),im->yorigin-im->ysize,
+                     1.0,im->gdes[i].col); 
+      break;
+    default:
+      break;
+    }
+  }
+
+  
+  if (strcmp(im->graphfile,"-")==0) {
+    fo = im->graphhandle ? im->graphhandle : stdout;
+#ifdef WIN32
+    /* Change translation mode for stdout to BINARY */
+    _setmode( _fileno( fo ), O_BINARY );
+#endif
+  } else {
+    if ((fo = fopen(im->graphfile,"wb")) == NULL) {
+      rrd_set_error("Opening '%s' for write: %s",im->graphfile,
+                    rrd_strerror(errno));
+      return (-1);
+    }
+  }
+  gfx_render (im->canvas,im->ximg,im->yimg,0x0,fo);
+  if (strcmp(im->graphfile,"-") != 0)
+    fclose(fo);
+  return 0;
+}
+
 
 /*****************************************************
  * graph stuff 
@@ -2418,13 +2595,7 @@ printf("DEBUG: HRULE at %f\n",im->gdes[i].yrule);
 int
 gdes_alloc(image_desc_t *im){
 
-    long def_step = (im->end-im->start)/im->xsize;
-    
-    if (im->step > def_step) /* step can be increassed ... no decreassed */
-      def_step = im->step;
-
     im->gdes_c++;
-    
     if ((im->gdes = (graph_desc_t *) rrd_realloc(im->gdes, (im->gdes_c)
                                           * sizeof(graph_desc_t)))==NULL){
        rrd_set_error("realloc graph_descs");
@@ -2432,7 +2603,9 @@ gdes_alloc(image_desc_t *im){
     }
 
 
-    im->gdes[im->gdes_c-1].step=def_step; 
+    im->gdes[im->gdes_c-1].step=im->step;
+    im->gdes[im->gdes_c-1].stack=0;
+    im->gdes[im->gdes_c-1].debug=0;
     im->gdes[im->gdes_c-1].start=im->start; 
     im->gdes[im->gdes_c-1].end=im->end; 
     im->gdes[im->gdes_c-1].vname[0]='\0'; 
@@ -2441,12 +2614,15 @@ gdes_alloc(image_desc_t *im){
     im->gdes[im->gdes_c-1].data_first=0;
     im->gdes[im->gdes_c-1].p_data=NULL;
     im->gdes[im->gdes_c-1].rpnp=NULL;
-    im->gdes[im->gdes_c-1].col.red = -1;
-    im->gdes[im->gdes_c-1].col.i=-1;
+    im->gdes[im->gdes_c-1].shift=0;
+    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].rrd[0]='\0';
     im->gdes[im->gdes_c-1].ds=-1;    
     im->gdes[im->gdes_c-1].p_data=NULL;    
+    im->gdes[im->gdes_c-1].yrule=DNAN;
+    im->gdes[im->gdes_c-1].xrule=0;
     return 0;
 }
 
@@ -2474,59 +2650,158 @@ scan_for_col(char *input, int len, char *output)
     output[outp] = '\0';
     return inp;
 }
-
+/* Some surgery done on this function, it became ridiculously big.
+** Things moved:
+** - initializing     now in rrd_graph_init()
+** - options parsing  now in rrd_graph_options()
+** - script parsing   now in rrd_graph_script()
+*/
 int 
-rrd_graph(int argc, char **argv, char ***prdata, int *xsize, int *ysize)
+rrd_graph(int argc, char **argv, char ***prdata, int *xsize, int *ysize, FILE *stream, double *ymin, double *ymax)
 {
-    
     image_desc_t   im;
-    int            i;
-    long           long_tmp;
-    time_t        start_tmp=0,end_tmp=0;
-    char           scan_gtm[12],scan_mtm[12],scan_ltm[12],col_nam[12];
-    char           symname[100];
-    unsigned int            col_red,col_green,col_blue;
-    long           scancount;
-    int linepass = 0; /* stack can only follow directly after LINE* AREA or STACK */    
-    struct time_value start_tv, end_tv;
-    char *parsetime_error = NULL;
-    int stroff;    
+            
+    rrd_graph_init(&im);
+    im.graphhandle = stream;
+    
+    rrd_graph_options(argc,argv,&im);
+    if (rrd_test_error()) {
+       im_free(&im);
+       return -1;
+    }
+    
+    if (strlen(argv[optind])>=MAXPATH) {
+       rrd_set_error("filename (including path) too long");
+       im_free(&im);
+       return -1;
+    }
+    strncpy(im.graphfile,argv[optind],MAXPATH-1);
+    im.graphfile[MAXPATH-1]='\0';
+
+    rrd_graph_script(argc,argv,&im,1);
+    if (rrd_test_error()) {
+       im_free(&im);
+       return -1;
+    }
+
+    /* Everything is now read and the actual work can start */
 
     (*prdata)=NULL;
+    if (graph_paint(&im,prdata)==-1){
+       im_free(&im);
+       return -1;
+    }
 
-    parsetime("end-24h", &start_tv);
-    parsetime("now", &end_tv);
+    /* The image is generated and needs to be output.
+    ** Also, if needed, print a line with information about the image.
+    */
 
-    im.xlab_user.minsec = -1;
-    im.xgif=0;
-    im.ygif=0;
-    im.xsize = 400;
-    im.ysize = 100;
-    im.step = 0;
-    im.ylegend[0] = '\0';
-    im.title[0] = '\0';
-    im.minval = DNAN;
-    im.maxval = DNAN;    
-    im.interlaced = 0;
-    im.unitsexponent= 9999;
-    im.extra_flags= 0;
-    im.rigid = 0;
-    im.imginfo = NULL;
-    im.lazy = 0;
-    im.logarithmic = 0;
-    im.ygridstep = DNAN;
-    im.draw_x_grid = 1;
-    im.draw_y_grid = 1;
-    im.base = 1000;
-    im.prt_c = 0;
-    im.gdes_c = 0;
-    im.gdes = NULL;
-    im.imgformat = IF_GIF; /* we default to GIF output */
+    *xsize=im.ximg;
+    *ysize=im.yimg;
+    *ymin=im.minval;
+    *ymax=im.maxval;
+    if (im.imginfo) {
+       char *filename;
+       if (!(*prdata)) {
+           /* maybe prdata is not allocated yet ... lets do it now */
+           if ((*prdata = calloc(2,sizeof(char *)))==NULL) {
+               rrd_set_error("malloc imginfo");
+               return -1; 
+           };
+       }
+       if(((*prdata)[0] = malloc((strlen(im.imginfo)+200+strlen(im.graphfile))*sizeof(char)))
+        ==NULL){
+           rrd_set_error("malloc imginfo");
+           return -1;
+       }
+       filename=im.graphfile+strlen(im.graphfile);
+       while(filename > im.graphfile) {
+           if (*(filename-1)=='/' || *(filename-1)=='\\' ) break;
+           filename--;
+       }
 
-    for(i=0;i<DIM(graph_col);i++)
-       im.graph_col[i].red=-1;
-    
+       sprintf((*prdata)[0],im.imginfo,filename,(long)(im.canvas->zoom*im.ximg),(long)(im.canvas->zoom*im.yimg));
+    }
+    im_free(&im);
+    return 0;
+}
+
+void
+rrd_graph_init(image_desc_t *im)
+{
+    unsigned int i;
+
+#ifdef HAVE_TZSET
+    tzset();
+#endif
+#ifdef HAVE_SETLOCALE
+    setlocale(LC_TIME,"");
+#endif
+
+    im->xlab_user.minsec = -1;
+    im->ximg=0;
+    im->yimg=0;
+    im->xsize = 400;
+    im->ysize = 100;
+    im->step = 0;
+    im->ylegend[0] = '\0';
+    im->title[0] = '\0';
+    im->minval = DNAN;
+    im->maxval = DNAN;    
+    im->unitsexponent= 9999;
+    im->extra_flags= 0;
+    im->rigid = 0;
+    im->gridfit = 1;
+    im->imginfo = NULL;
+    im->lazy = 0;
+    im->logarithmic = 0;
+    im->ygridstep = DNAN;
+    im->draw_x_grid = 1;
+    im->draw_y_grid = 1;
+    im->base = 1000;
+    im->prt_c = 0;
+    im->gdes_c = 0;
+    im->gdes = NULL;
+    im->canvas = gfx_new_canvas();
+    im->grid_dash_on = 1;
+    im->grid_dash_off = 1;
+    im->tabwidth = 40.0;
     
+    for(i=0;i<DIM(graph_col);i++)
+        im->graph_col[i]=graph_col[i];
+#ifdef WIN32
+    {
+            char *windir; 
+            windir = getenv("windir");
+            /* %windir% is something like D:\windows or C:\winnt */
+            if (windir != NULL) {
+                    strcpy(rrd_win_default_font,windir);
+                    strcat(rrd_win_default_font,"\\fonts\\cour.ttf");
+                    for(i=0;i<DIM(text_prop);i++)
+                            strcpy(text_prop[i].font,rrd_win_default_font);
+            }
+    }
+#endif
+    for(i=0;i<DIM(text_prop);i++){        
+      im->text_prop[i].size = text_prop[i].size;
+      strcpy(im->text_prop[i].font,text_prop[i].font);
+    }
+}
+
+void
+rrd_graph_options(int argc, char *argv[],image_desc_t *im)
+{
+    int                        stroff;    
+    char               *parsetime_error = NULL;
+    char               scan_gtm[12],scan_mtm[12],scan_ltm[12],col_nam[12];
+    time_t             start_tmp=0,end_tmp=0;
+    long               long_tmp;
+    struct rrd_time_value      start_tv, end_tv;
+    gfx_color_t         color;
+
+    parsetime("end-24h", &start_tv);
+    parsetime("now", &end_tv);
+
     while (1){
        static struct option long_options[] =
        {
@@ -2544,192 +2819,251 @@ rrd_graph(int argc, char **argv, char ***prdata, int *xsize, int *ysize)
            {"base",       required_argument, 0,  'b'},
            {"logarithmic",no_argument,       0,  'o'},
            {"color",      required_argument, 0,  'c'},
+            {"font",       required_argument, 0,  'n'},
            {"title",      required_argument, 0,  't'},
            {"imginfo",    required_argument, 0,  'f'},
            {"imgformat",  required_argument, 0,  'a'},
            {"lazy",       no_argument,       0,  'z'},
+            {"zoom",       required_argument, 0,  'm'},
            {"no-legend",  no_argument,       0,  'g'},
-           {"alt-y-grid", no_argument,       0,   257 },
-           {"alt-autoscale", no_argument,    0,   258 },
-           {"alt-autoscale-max", no_argument,    0,   259 },
-           {"units-exponent",required_argument, 0,  260},
-           {"step",       required_argument, 0,   261},
+           {"force-rules-legend",no_argument,0,  'F'},
+            {"only-graph", no_argument,       0,  'j'},
+           {"alt-y-grid", no_argument,       0,  'Y'},
+            {"no-minor",   no_argument,       0,  'I'},
+           {"alt-autoscale", no_argument,    0,  'A'},
+           {"alt-autoscale-max", no_argument, 0, 'M'},
+           {"units-exponent",required_argument, 0, 'X'},
+           {"step",       required_argument, 0,    'S'},
+            {"tabwidth",   required_argument, 0,    'T'},            
+           {"no-gridfit", no_argument,       0,   'N'},
            {0,0,0,0}};
        int option_index = 0;
        int opt;
 
-       
+
        opt = getopt_long(argc, argv, 
-                         "s:e:x:y:v:w:h:iu:l:rb:oc:t:f:a:z:g",
+                        "s:e:x:y:v:w:h:iu:l:rb:oc:n:m:t:f:a:I:zgjFYAMX:S:NT:",
                          long_options, &option_index);
 
        if (opt == EOF)
            break;
        
        switch(opt) {
-       case 257:
-           im.extra_flags |= ALTYGRID;
+        case 'I':
+            im->extra_flags |= NOMINOR;
+            break;
+       case 'Y':
+           im->extra_flags |= ALTYGRID;
            break;
-       case 258:
-           im.extra_flags |= ALTAUTOSCALE;
+       case 'A':
+           im->extra_flags |= ALTAUTOSCALE;
            break;
-       case 259:
-           im.extra_flags |= ALTAUTOSCALE_MAX;
+       case 'M':
+           im->extra_flags |= ALTAUTOSCALE_MAX;
            break;
+        case 'j':
+           im->extra_flags |= ONLY_GRAPH;
+           break;
        case 'g':
-           im.extra_flags |= NOLEGEND;
+           im->extra_flags |= NOLEGEND;
+           break;
+       case 'F':
+           im->extra_flags |= FORCE_RULES_LEGEND;
+           break;
+       case 'X':
+           im->unitsexponent = atoi(optarg);
            break;
-       case 260:
-           im.unitsexponent = atoi(optarg);
+       case 'T':
+           im->tabwidth = atof(optarg);
            break;
-       case 261:
-           im.step =  atoi(optarg);
+       case 'S':
+           im->step =  atoi(optarg);
+           break;
+       case 262:
+           im->gridfit = 0;
            break;
        case 's':
            if ((parsetime_error = parsetime(optarg, &start_tv))) {
                rrd_set_error( "start time: %s", parsetime_error );
-               return -1;
+               return;
            }
            break;
        case 'e':
            if ((parsetime_error = parsetime(optarg, &end_tv))) {
                rrd_set_error( "end time: %s", parsetime_error );
-               return -1;
+               return;
            }
            break;
        case 'x':
            if(strcmp(optarg,"none") == 0){
-             im.draw_x_grid=0;
+             im->draw_x_grid=0;
              break;
            };
                
            if(sscanf(optarg,
                      "%10[A-Z]:%ld:%10[A-Z]:%ld:%10[A-Z]:%ld:%ld:%n",
                      scan_gtm,
-                     &im.xlab_user.gridst,
+                     &im->xlab_user.gridst,
                      scan_mtm,
-                     &im.xlab_user.mgridst,
+                     &im->xlab_user.mgridst,
                      scan_ltm,
-                     &im.xlab_user.labst,
-                     &im.xlab_user.precis,
+                     &im->xlab_user.labst,
+                     &im->xlab_user.precis,
                      &stroff) == 7 && stroff != 0){
-                strncpy(im.xlab_form, optarg+stroff, sizeof(im.xlab_form) - 1);
-               if((im.xlab_user.gridtm = tmt_conv(scan_gtm)) == -1){
+                strncpy(im->xlab_form, optarg+stroff, sizeof(im->xlab_form) - 1);
+               if((int)(im->xlab_user.gridtm = tmt_conv(scan_gtm)) == -1){
                    rrd_set_error("unknown keyword %s",scan_gtm);
-                   return -1;
-               } else if ((im.xlab_user.mgridtm = tmt_conv(scan_mtm)) == -1){
+                   return;
+               } else if ((int)(im->xlab_user.mgridtm = tmt_conv(scan_mtm)) == -1){
                    rrd_set_error("unknown keyword %s",scan_mtm);
-                   return -1;
-               } else if ((im.xlab_user.labtm = tmt_conv(scan_ltm)) == -1){
+                   return;
+               } else if ((int)(im->xlab_user.labtm = tmt_conv(scan_ltm)) == -1){
                    rrd_set_error("unknown keyword %s",scan_ltm);
-                   return -1;
+                   return;
                } 
-               im.xlab_user.minsec = 1;
-               im.xlab_user.stst = im.xlab_form;
+               im->xlab_user.minsec = 1;
+               im->xlab_user.stst = im->xlab_form;
            } else {
                rrd_set_error("invalid x-grid format");
-               return -1;
+               return;
            }
            break;
        case 'y':
 
            if(strcmp(optarg,"none") == 0){
-             im.draw_y_grid=0;
+             im->draw_y_grid=0;
              break;
            };
 
            if(sscanf(optarg,
                      "%lf:%d",
-                     &im.ygridstep,
-                     &im.ylabfact) == 2) {
-               if(im.ygridstep<=0){
+                     &im->ygridstep,
+                     &im->ylabfact) == 2) {
+               if(im->ygridstep<=0){
                    rrd_set_error("grid step must be > 0");
-                   return -1;
-               } else if (im.ylabfact < 1){
+                   return;
+               } else if (im->ylabfact < 1){
                    rrd_set_error("label factor must be > 0");
-                   return -1;
+                   return;
                } 
            } else {
                rrd_set_error("invalid y-grid format");
-               return -1;
+               return;
            }
            break;
        case 'v':
-           strncpy(im.ylegend,optarg,150);
-           im.ylegend[150]='\0';
+           strncpy(im->ylegend,optarg,150);
+           im->ylegend[150]='\0';
            break;
        case 'u':
-           im.maxval = atof(optarg);
+           im->maxval = atof(optarg);
            break;
        case 'l':
-           im.minval = atof(optarg);
+           im->minval = atof(optarg);
            break;
        case 'b':
-           im.base = atol(optarg);
-           if(im.base != 1024 && im.base != 1000 ){
+           im->base = atol(optarg);
+           if(im->base != 1024 && im->base != 1000 ){
                rrd_set_error("the only sensible value for base apart from 1000 is 1024");
-               return -1;
+               return;
            }
            break;
        case 'w':
            long_tmp = atol(optarg);
            if (long_tmp < 10) {
                rrd_set_error("width below 10 pixels");
-               return -1;
+               return;
            }
-           im.xsize = long_tmp;
+           im->xsize = long_tmp;
            break;
        case 'h':
            long_tmp = atol(optarg);
            if (long_tmp < 10) {
                rrd_set_error("height below 10 pixels");
-               return -1;
+               return;
            }
-           im.ysize = long_tmp;
+           im->ysize = long_tmp;
            break;
        case 'i':
-           im.interlaced = 1;
+           im->canvas->interlaced = 1;
            break;
        case 'r':
-           im.rigid = 1;
+           im->rigid = 1;
            break;
        case 'f':
-           im.imginfo = optarg;
+           im->imginfo = optarg;
            break;
        case 'a':
-           if((im.imgformat = if_conv(optarg)) == -1) {
+           if((int)(im->canvas->imgformat = if_conv(optarg)) == -1) {
                rrd_set_error("unsupported graphics format '%s'",optarg);
-               return -1;
+               return;
            }
            break;
        case 'z':
-           im.lazy = 1;
+           im->lazy = 1;
            break;
        case 'o':
-           im.logarithmic = 1;
-           if (isnan(im.minval))
-               im.minval=1;
+           im->logarithmic = 1;
+           if (isnan(im->minval))
+               im->minval=1;
            break;
-       case 'c':
+        case 'c':
+            if(sscanf(optarg,
+                      "%10[A-Z]#%8lx",
+                      col_nam,&color) == 2){
+                int ci;
+                if((ci=grc_conv(col_nam)) != -1){
+                    im->graph_col[ci]=color;
+                }  else {
+                  rrd_set_error("invalid color name '%s'",col_nam);
+                }
+            } else {
+                rrd_set_error("invalid color def format");
+                return;
+            }
+            break;        
+        case 'n':{
+           char prop[15];
+           double size = 1;
+           char font[1024];
+
            if(sscanf(optarg,
-                     "%10[A-Z]#%2x%2x%2x",
-                     col_nam,&col_red,&col_green,&col_blue) == 4){
-               int ci;
-               if((ci=grc_conv(col_nam)) != -1){
-                   im.graph_col[ci].red=col_red;
-                   im.graph_col[ci].green=col_green;
-                   im.graph_col[ci].blue=col_blue;
-               }  else {
-                 rrd_set_error("invalid color name '%s'",col_nam);
+                               "%10[A-Z]:%lf:%1000s",
+                               prop,&size,font) == 3){
+               int sindex;
+               if((sindex=text_prop_conv(prop)) != -1){
+                   im->text_prop[sindex].size=size;              
+                   strcpy(im->text_prop[sindex].font,font);
+                   if (sindex==0) { /* the default */
+                       im->text_prop[TEXT_PROP_TITLE].size=size;
+                       strcpy(im->text_prop[TEXT_PROP_TITLE].font,font);
+                       im->text_prop[TEXT_PROP_AXIS].size=size;
+                       strcpy(im->text_prop[TEXT_PROP_AXIS].font,font);
+                       im->text_prop[TEXT_PROP_UNIT].size=size;
+                       strcpy(im->text_prop[TEXT_PROP_UNIT].font,font);
+                       im->text_prop[TEXT_PROP_LEGEND].size=size;
+                       strcpy(im->text_prop[TEXT_PROP_LEGEND].font,font);
+                   }
+               } else {
+                   rrd_set_error("invalid fonttag '%s'",prop);
+                   return;
                }
            } else {
-               rrd_set_error("invalid color def format");
-               return -1;
+               rrd_set_error("invalid text property format");
+               return;
            }
-           break;        
+           break;          
+       }
+        case 'm':
+           im->canvas->zoom = atof(optarg);
+           if (im->canvas->zoom <= 0.0) {
+               rrd_set_error("zoom factor must be > 0");
+               return;
+           }
+          break;
        case 't':
-           strncpy(im.title,optarg,150);
-           im.title[150]='\0';
+           strncpy(im->title,optarg,150);
+           im->title[150]='\0';
            break;
 
        case '?':
@@ -2737,532 +3071,129 @@ rrd_graph(int argc, char **argv, char ***prdata, int *xsize, int *ysize)
                 rrd_set_error("unknown option '%c'", optopt);
             else
                 rrd_set_error("unknown option '%s'",argv[optind-1]);
-            return -1;
+            return;
        }
     }
     
     if (optind >= argc) {
        rrd_set_error("missing filename");
-       return -1;
+       return;
     }
 
-    if (im.logarithmic == 1 && (im.minval <= 0 || isnan(im.minval))){
+    if (im->logarithmic == 1 && (im->minval <= 0 || isnan(im->minval))){
        rrd_set_error("for a logarithmic yaxis you must specify a lower-limit > 0");    
-       return -1;
+       return;
     }
 
-    strncpy(im.graphfile,argv[optind],MAXPATH-1);
-    im.graphfile[MAXPATH-1]='\0';
-
     if (proc_start_end(&start_tv,&end_tv,&start_tmp,&end_tmp) == -1){
-       return -1;
+       /* error string is set in parsetime.c */
+       return;
     }  
     
     if (start_tmp < 3600*24*365*10){
        rrd_set_error("the first entry to fetch should be after 1980 (%ld)",start_tmp);
-       return -1;
+       return;
     }
     
     if (end_tmp < start_tmp) {
        rrd_set_error("start (%ld) should be less than end (%ld)", 
               start_tmp, end_tmp);
-       return -1;
+       return;
     }
     
-    im.start = start_tmp;
-    im.end = end_tmp;
+    im->start = start_tmp;
+    im->end = end_tmp;
+    im->step = max((long)im->step, (im->end-im->start)/im->xsize);
+}
 
-    
-    for(i=optind+1;i<argc;i++){
-       int   argstart=0;
-       int   strstart=0;
-       char  varname[30],*rpnex;
-       gdes_alloc(&im);
-       if(sscanf(argv[i],"%10[A-Z0-9]:%n",symname,&argstart)==1){
-           if((im.gdes[im.gdes_c-1].gf=gf_conv(symname))==-1){
-               im_free(&im);
-               rrd_set_error("unknown function '%s'",symname);
-               return -1;
-           }
-       } else {
-           rrd_set_error("can't parse '%s'",argv[i]);
-           im_free(&im);
-           return -1;
+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;
+    graph_desc_t *gdp=&im->gdes[im->gdes_c-1];
+
+    color=strstr(var,"#");
+    if (color==NULL) {
+       if (optional==0) {
+           rrd_set_error("Found no color in %s",err);
+           return 0;
        }
+       return 0;
+    } else {
+       int n=0;
+       char *rest;
+       gfx_color_t    col;
 
-       /* reset linepass if a non LINE/STACK/AREA operator gets parsed 
-       
-          if (im.gdes[im.gdes_c-1].gf != GF_LINE1 &&
-          im.gdes[im.gdes_c-1].gf != GF_LINE2 &&
-          im.gdes[im.gdes_c-1].gf != GF_LINE3 &&
-          im.gdes[im.gdes_c-1].gf != GF_AREA &&
-          im.gdes[im.gdes_c-1].gf != GF_STACK) {
-          linepass = 0;
-          } 
-       */
-       
-       switch(im.gdes[im.gdes_c-1].gf){
-       case GF_PRINT:
-           im.prt_c++;
-       case GF_GPRINT:
-           strstart=0;
-           sscanf(&argv[i][argstart], DEF_NAM_FMT ":%n"
-                   ,varname
-                   ,&strstart
-           );
-
-           if (strstart==0) {
-               im_free(&im);
-               rrd_set_error("can't parse vname in '%s'",&argv[i][argstart]);
-               return -1;
-           };
-
-           if ((im.gdes[im.gdes_c-1].vidx=find_var(&im,varname))==-1){
-               im_free(&im);
-               rrd_set_error("Unknown variable '%s' in (G)PRINT",varname);
-               return -1;
-           } else {
-               int n=0;
-
-               sscanf(&argv[i][argstart+strstart],CF_NAM_FMT ":%n"
-                   ,symname
-                   ,&n
-               );
-               if (im.gdes[im.gdes[im.gdes_c-1].vidx].gf==GF_VDEF) {
-                   /* No consolidation function should be present */
-                   if (n != 0) {
-                       rrd_set_error("(G)PRINT of VDEF needs no CF");
-                       im_free(&im);
-                       return -1;
-                   }
-               } else {
-                   /* A consolidation function should follow */
-                   if (n==0) {
-                       im_free(&im);
-                       rrd_set_error("Missing or incorrect CF in (G)PRINTing '%s' (%s)",varname,&argv[i][argstart]);
-                       return -1;
-                   };
-                   if((im.gdes[im.gdes_c-1].cf=cf_conv(symname))==-1){
-                       im_free(&im);
-                       return -1;
-                   };
-                   strstart+=n;
-               };
-           };
-
-           scan_for_col(
-                       &argv[i][argstart+strstart]
-                       ,FMT_LEG_LEN
-                       ,im.gdes[im.gdes_c-1].format
-                       );
-           break;
-       case GF_COMMENT:
-           if(strlen(&argv[i][argstart])>FMT_LEG_LEN) argv[i][argstart+FMT_LEG_LEN-3]='\0' ;
-           strcpy(im.gdes[im.gdes_c-1].legend, &argv[i][argstart]);
-           break;
-       case GF_HRULE:
-           /* scan for either "HRULE:vname#..." or "HRULE:num#..."
-            *
-            * If a vname is used, the value NaN is set; this is catched
-            * when graphing.  Setting value NaN from the script is not
-            * permitted
-            */
-           strstart=0;
-           sscanf(&argv[i][argstart], "%lf#%n"
-               ,&im.gdes[im.gdes_c-1].yrule
-               ,&strstart
-           );
-           if (strstart==0) { /* no number, should be vname */
-               sscanf(&argv[i][argstart], DEF_NAM_FMT "#%n"
-                   ,varname
-                   ,&strstart
-               );
-               if (strstart) {
-                   im.gdes[im.gdes_c-1].yrule = DNAN;/* signal use of vname */
-                   if((im.gdes[im.gdes_c-1].vidx=find_var(&im,varname))==-1){
-                       im_free(&im);
-                       rrd_set_error("unknown variable '%s' in HRULE",varname);
-                       return -1;
-                   }           
-                   if(im.gdes[im.gdes[im.gdes_c-1].vidx].gf != GF_VDEF) {
-                       im_free(&im);
-                       rrd_set_error("Only VDEF is allowed in HRULE",varname);
-                       return -1;
-                   }
-               }
-           } else {
-printf("DEBUG: matched HRULE:num\n");
-printf("DEBUG: strstart==%i\n",strstart);
-           };
-           if (strstart==0) {
-               im_free(&im);
-               rrd_set_error("can't parse '%s'",&argv[i][argstart]);
-               return -1;
-           } else {
-               int n=0;
-               if(sscanf(
-                       &argv[i][argstart+strstart],
-                       "%2x%2x%2x:%n",
-                       &col_red,
-                       &col_green,
-                       &col_blue,
-                       &n)>=3) {
-                   im.gdes[im.gdes_c-1].col.red = col_red;
-                   im.gdes[im.gdes_c-1].col.green = col_green;
-                   im.gdes[im.gdes_c-1].col.blue = col_blue;
-                   if (n==0) {
-                       im.gdes[im.gdes_c-1].legend[0] = '\0';
-                   } else {
-                       scan_for_col(&argv[i][argstart+strstart+n],FMT_LEG_LEN,im.gdes[im.gdes_c-1].legend);
-                   }
-               } else {
-                   im_free(&im);
-                   rrd_set_error("can't parse '%s'",&argv[i][argstart]);
-                   return -1;
-               }
-           }
-           
-           break;
-       case GF_VRULE:
-           /* scan for either "VRULE:vname#..." or "VRULE:num#..."
-            *
-            * If a vname is used, the value 0 is set; this is catched
-            * when graphing.  Setting value 0 from the script is not
-            * permitted
-            */
-           strstart=0;
-           sscanf(&argv[i][argstart], "%lu#%n"
-               ,(long unsigned int *)&im.gdes[im.gdes_c-1].xrule
-               ,&strstart
-           );
-           if (strstart==0) { /* no number, should be vname */
-               sscanf(&argv[i][argstart], DEF_NAM_FMT "#%n"
-                   ,varname
-                   ,&strstart
-               );
-               if (strstart!=0) { /* vname matched */
-                   im.gdes[im.gdes_c-1].xrule = 0;/* signal use of vname */
-                   if((im.gdes[im.gdes_c-1].vidx=find_var(&im,varname))==-1){
-                       im_free(&im);
-                       rrd_set_error("unknown variable '%s' in VRULE",varname);
-                       return -1;
-                   }           
-                   if(im.gdes[im.gdes[im.gdes_c-1].vidx].gf != GF_VDEF) {
-                       im_free(&im);
-                       rrd_set_error("Only VDEF is allowed in VRULE",varname);
-                       return -1;
-                   }
-               }
-           } else {
-               if (im.gdes[im.gdes_c-1].xrule==0)
-                   strstart=0;
-           }
-
-           if (strstart==0) {
-               im_free(&im);
-               rrd_set_error("can't parse '%s'",&argv[i][argstart]);
-               return -1;
-           } else {
-               int n=0;
-               if(sscanf(
-                       &argv[i][argstart+strstart],
-                       "%2x%2x%2x:%n",
-                       &col_red,
-                       &col_green,
-                       &col_blue,
-                       &n)>=3) {
-                   im.gdes[im.gdes_c-1].col.red = col_red;
-                   im.gdes[im.gdes_c-1].col.green = col_green;
-                   im.gdes[im.gdes_c-1].col.blue = col_blue;
-                   if (n==0) {
-                       im.gdes[im.gdes_c-1].legend[0] = '\0';
-                   } else {
-                       scan_for_col(&argv[i][argstart+strstart+n],FMT_LEG_LEN,im.gdes[im.gdes_c-1].legend);
-                   }
-               } else {
-                   im_free(&im);
-                   rrd_set_error("can't parse '%s'",&argv[i][argstart]);
-                   return -1;
-               }
-           }
-           break;
-       case GF_TICK:
-           if((scancount=sscanf(
-               &argv[i][argstart],
-               "%29[^:#]#%2x%2x%2x:%lf:%n",
-               varname,
-               &col_red,
-               &col_green,
-               &col_blue,
-               &(im.gdes[im.gdes_c-1].yrule),
-               &strstart))>=1)
-               {
-               im.gdes[im.gdes_c-1].col.red = col_red;
-               im.gdes[im.gdes_c-1].col.green = col_green;
-               im.gdes[im.gdes_c-1].col.blue = col_blue;
-               if(strstart <= 0){
-                   im.gdes[im.gdes_c-1].legend[0] = '\0';
-               } else { 
-                   scan_for_col(&argv[i][argstart+strstart],FMT_LEG_LEN,im.gdes[im.gdes_c-1].legend);
-               }
-               if((im.gdes[im.gdes_c-1].vidx=find_var(&im,varname))==-1){
-                   im_free(&im);
-                   rrd_set_error("unknown variable '%s'",varname);
-                   return -1;
-               }
-               if (im.gdes[im.gdes_c-1].yrule <= 0.0 || im.gdes[im.gdes_c-1].yrule > 1.0)
-               {
-                   im_free(&im);
-                   rrd_set_error("Tick mark scaling factor out of range");
-                   return -1;
-               }
-               if (scancount < 4)
-                  im.gdes[im.gdes_c-1].col.red = -1;           
-           if (scancount < 5) 
-                  /* default tick marks: 10% of the y-axis */
-                  im.gdes[im.gdes_c-1].yrule = 0.1;
+       rest=strstr(color,":");
+       if (rest!=NULL)
+           n=rest-color;
+       else
+           n=strlen(color);
 
-               } else {
-                  im_free(&im);
-                  rrd_set_error("can't parse '%s'",&argv[i][argstart]);
-                  return -1;
-               } /* endif sscanf */
+       switch (n) {
+           case 7:
+               sscanf(color,"#%6lx%n",&col,&n);
+                col = (col << 8) + 0xff /* shift left by 8 */;
+               if (n!=7) rrd_set_error("Color problem in %s",err);
                break;
-       case GF_STACK:
-           if(linepass == 0){
-               im_free(&im);
-               rrd_set_error("STACK must follow AREA, LINE or STACK");
-               return -1; 
-           }           
-       case GF_LINE1:
-       case GF_LINE2:
-       case GF_LINE3:
-       case GF_AREA:
-           linepass = 1;
-           if((scancount=sscanf(
-               &argv[i][argstart],
-               "%29[^:#]#%2x%2x%2x:%n",
-               varname,
-               &col_red,
-               &col_green,
-                   &col_blue,
-               &strstart))>=1){
-               im.gdes[im.gdes_c-1].col.red = col_red;
-               im.gdes[im.gdes_c-1].col.green = col_green;
-               im.gdes[im.gdes_c-1].col.blue = col_blue;
-               if(strstart <= 0){
-                   im.gdes[im.gdes_c-1].legend[0] = '\0';
-               } else { 
-                   scan_for_col(&argv[i][argstart+strstart],FMT_LEG_LEN,im.gdes[im.gdes_c-1].legend);
-               }
-               if((im.gdes[im.gdes_c-1].vidx=find_var(&im,varname))==-1){
-                   im_free(&im);
-                   rrd_set_error("unknown variable '%s'",varname);
-                   return -1;
-               }               
-               if (scancount < 4)
-                   im.gdes[im.gdes_c-1].col.red = -1;          
-               
-           } else {
-               im_free(&im);
-               rrd_set_error("can't parse '%s'",&argv[i][argstart]);
-               return -1;
-           }
-           break;
-       case GF_CDEF:
-           if((rpnex = malloc(strlen(&argv[i][argstart])*sizeof(char)))==NULL){
-               rrd_set_error("malloc for CDEF");
-               return -1;
-           }
-           if(sscanf(
-                   &argv[i][argstart],
-                   DEF_NAM_FMT "=%[^: ]",
-                   im.gdes[im.gdes_c-1].vname,
-                   rpnex) != 2){
-               im_free(&im);
-               free(rpnex);
-               rrd_set_error("can't parse CDEF '%s'",&argv[i][argstart]);
-               return -1;
-           }
-           /* checking for duplicate variable names */
-           if(find_var(&im,im.gdes[im.gdes_c-1].vname) != -1){
-               im_free(&im);
-               rrd_set_error("duplicate variable '%s'",
-                             im.gdes[im.gdes_c-1].vname);
-               return -1; 
-           }      
-           if((im.gdes[im.gdes_c-1].rpnp = 
-                  rpn_parse((void*)&im,rpnex,&find_var_wrapper))== NULL){
-               rrd_set_error("invalid rpn expression '%s'", rpnex);
-               im_free(&im);           
-               return -1;
-           }
-           free(rpnex);
-           break;
-       case GF_VDEF:
-           /*
-            * strstart is set to zero and will NOT be changed
-            * if the comma is not matched.  This means that it
-            * remains zero. Although strstart is initialized to
-            * zero at the beginning of this loop, we do it again
-            * here just in case someone changes the code...
-            *
-            * According to the docs we cannot rely on the
-            * returned value from sscanf; it can be 2 or 3,
-            * depending on %n incrementing it or not.
-            */
-           strstart=0;
-           sscanf(
-                   &argv[i][argstart],
-                   DEF_NAM_FMT "=" DEF_NAM_FMT ",%n",
-                   im.gdes[im.gdes_c-1].vname,
-                   varname,
-                   &strstart);
-           if (strstart){
-               /* checking both variable names */
-               if (find_var(&im,im.gdes[im.gdes_c-1].vname) != -1){
-                   im_free(&im);
-                   rrd_set_error("duplicate variable '%s'",
-                               im.gdes[im.gdes_c-1].vname);
-                   return -1; 
-               } else {
-                   if ((im.gdes[im.gdes_c-1].vidx=find_var(&im,varname)) == -1){
-                       im_free(&im);
-                       rrd_set_error("variable '%s' not known in VDEF '%s'",
-                               varname,
-                               im.gdes[im.gdes_c-1].vname);
-                       return -1; 
-                   } else {
-                       if(im.gdes[im.gdes[im.gdes_c-1].vidx].gf != GF_DEF
-                       && im.gdes[im.gdes[im.gdes_c-1].vidx].gf != GF_CDEF){
-                           rrd_set_error("variable '%s' not DEF nor CDEF in VDEF '%s'",
-                               varname,
-                               im.gdes[im.gdes_c-1].vname);
-                           im_free(&im);
-                           return -1; 
-                       }
-                   }
-                   /* parsed upto and including the first comma. Now
-                    * see what function is requested.  This function
-                    * sets the error string.
-                    */
-                   if (vdef_parse(&im.gdes[im.gdes_c-1],&argv[i][argstart+strstart])<0) {
-                       im_free(&im);
-                       return -1;
-                   };
-               }
-           } else {
-               im_free(&im);
-               rrd_set_error("can't parse VDEF '%s'",&argv[i][argstart]);
-               return -1;
-           }
-           break;
-       case GF_DEF:
-           if (sscanf(
-               &argv[i][argstart],
-               DEF_NAM_FMT "=%n",
-               im.gdes[im.gdes_c-1].vname,
-               &strstart)== 1 && strstart){ /* is the = did not match %n returns 0 */ 
-               if(sscanf(&argv[i][argstart
-                                 +strstart
-                                 +scan_for_col(&argv[i][argstart+strstart],
-                                               MAXPATH,im.gdes[im.gdes_c-1].rrd)],
-                         ":" DS_NAM_FMT ":" CF_NAM_FMT,
-                         im.gdes[im.gdes_c-1].ds_nam,
-                         symname) != 2){
-                   im_free(&im);
-                   rrd_set_error("can't parse DEF '%s' -2",&argv[i][argstart]);
-                   return -1;
-               }
-           } else {
-               im_free(&im);
-               rrd_set_error("can't parse DEF '%s'",&argv[i][argstart]);
-               return -1;
-           }
-           
-           /* checking for duplicate DEF CDEFS */
-           if (find_var(&im,im.gdes[im.gdes_c-1].vname) != -1){
-               im_free(&im);
-               rrd_set_error("duplicate variable '%s'",
-                         im.gdes[im.gdes_c-1].vname);
-               return -1; 
-           }      
-           if((im.gdes[im.gdes_c-1].cf=cf_conv(symname))==-1){
-               im_free(&im);
-               rrd_set_error("unknown cf '%s'",symname);
-               return -1;
-           }
-           break;
+           case 9:
+               sscanf(color,"#%8lx%n",&col,&n);
+               if (n==9) break;
+           default:
+               rrd_set_error("Color problem in %s",err);
        }
-       
-    }
-
-    if (im.gdes_c==0){
-       rrd_set_error("can't make a graph without contents");
-       im_free(&im);
-       return(-1); 
-    }
-    
-       /* parse rest of arguments containing information on what to draw*/
-    if (graph_paint(&im,prdata)==-1){
-       im_free(&im);
-       return -1;
-    }
-    
-    *xsize=im.xgif;
-    *ysize=im.ygif;
-    if (im.imginfo){
-      char *filename;
-      if (! (*prdata)) {       
-       /* maybe prdata is not allocated yet ... lets do it now */
-       if((*prdata = calloc(2,sizeof(char *)))==NULL){
-         rrd_set_error("malloc imginfo");
-         return -1; 
-       };
-      }
-      if(((*prdata)[0] = malloc((strlen(im.imginfo)+200+strlen(im.graphfile))*sizeof(char)))
-        ==NULL){
-       rrd_set_error("malloc imginfo");
-       return -1;
-      }
-      filename=im.graphfile+strlen(im.graphfile);      
-      while(filename > im.graphfile){
-       if (*(filename-1)=='/' || *(filename-1)=='\\' ) break;
-       filename--;
-      }
-      
-      sprintf((*prdata)[0],im.imginfo,filename,im.xgif,im.ygif);
+       if (rrd_test_error()) return 0;
+       gdp->col = col;
+       return n;
     }
-    im_free(&im);
-    return 0;
 }
 
+
 int bad_format(char *fmt) {
-       char *ptr;
-
-       ptr = fmt;
-       while (*ptr != '\0') {
-               if (*ptr == '%') {ptr++;
-                       if (*ptr == '\0') return 1;
-                       while ((*ptr >= '0' && *ptr <= '9') || *ptr == '.') { 
-                               ptr++;
-                       }
-                       if (*ptr == '\0') return 1;
-                       if (*ptr == 'l') {
-                               ptr++;
-                               if (*ptr == '\0') return 1;
-                               if (*ptr == 'e' || *ptr == 'f') { 
-                                       ptr++; 
-                                       } else { return 1; }
-                       }
-                       else if (*ptr == 's' || *ptr == 'S' || *ptr == '%') { ++ptr; }
-                       else { return 1; }
-               } else {
-                       ++ptr;
-               }
-       }
-       return 0;
+    char *ptr;
+    int n=0;
+    ptr = fmt;
+    while (*ptr != '\0')
+        if (*ptr++ == '%') {
+             /* line cannot end with percent char */
+             if (*ptr == '\0') return 1;
+             /* '%s', '%S' and '%%' are allowed */
+             if (*ptr == 's' || *ptr == 'S' || *ptr == '%') ptr++;
+
+             /* or else '% 6.2lf' and such are allowed */
+             else {
+   
+                 /* optional padding character */
+                 if (*ptr == ' ' || *ptr == '+' || *ptr == '-') ptr++;
+  
+                 /* This should take care of 'm.n' with all three optional */
+                 while (*ptr >= '0' && *ptr <= '9') ptr++;
+                 if (*ptr == '.') ptr++;
+                 while (*ptr >= '0' && *ptr <= '9') ptr++;
+  
+                 /* Either 'le', 'lf' or 'lg' must follow here */
+                 if (*ptr++ != 'l') return 1;
+                 if (*ptr == 'e' || *ptr == 'f' || *ptr == 'g') ptr++;
+                 else return 1;
+                 n++;
+            }
+         }
+      
+      return (n!=1); 
 }
+
+
 int
 vdef_parse(gdes,str)
 struct graph_desc_t *gdes;
@@ -3277,12 +3208,12 @@ char *str;
     
     n=0;
     sscanf(str,"%le,%29[A-Z]%n",&param,func,&n);
-    if (n==strlen(str)) { /* matched */
+    if (n== (int)strlen(str)) { /* matched */
        ;
     } else {
        n=0;
        sscanf(str,"%29[A-Z]%n",func,&n);
-       if (n==strlen(str)) { /* matched */
+       if (n== (int)strlen(str)) { /* matched */
            param=DNAN;
        } else {
            rrd_set_error("Unknown function string '%s' in VDEF '%s'"
@@ -3296,6 +3227,7 @@ char *str;
     else if    (!strcmp("MAXIMUM",func)) gdes->vf.op = VDEF_MAXIMUM;
     else if    (!strcmp("AVERAGE",func)) gdes->vf.op = VDEF_AVERAGE;
     else if    (!strcmp("MINIMUM",func)) gdes->vf.op = VDEF_MINIMUM;
+    else if    (!strcmp("TOTAL",  func)) gdes->vf.op = VDEF_TOTAL;
     else if    (!strcmp("FIRST",  func)) gdes->vf.op = VDEF_FIRST;
     else if    (!strcmp("LAST",   func)) gdes->vf.op = VDEF_LAST;
     else {
@@ -3330,6 +3262,7 @@ char *str;
        case VDEF_MAXIMUM:
        case VDEF_AVERAGE:
        case VDEF_MINIMUM:
+       case VDEF_TOTAL:
        case VDEF_FIRST:
        case VDEF_LAST:
            if (isnan(param)) {
@@ -3347,6 +3280,8 @@ char *str;
     };
     return 0;
 }
+
+
 int
 vdef_calc(im,gdi)
 image_desc_t *im;
@@ -3358,7 +3293,7 @@ int gdi;
 
     dst = &im->gdes[gdi];
     src = &im->gdes[dst->vidx];
-    data = src->data + src->ds + src->ds_cnt; /* skip first value! */
+    data = src->data + src->ds;
     steps = (src->end - src->start) / src->step;
 
 #if 0
@@ -3369,7 +3304,7 @@ printf("DEBUG: start == %lu, end == %lu, %lu steps\n"
     );
 #endif
 
-    switch (im->gdes[gdi].vf.op) {
+    switch (dst->vf.op) {
        case VDEF_PERCENT: {
                rrd_value_t *   array;
                int             field;
@@ -3387,7 +3322,8 @@ printf("DEBUG: start == %lu, end == %lu, %lu steps\n"
                field = (steps-1)*dst->vf.param/100;
                dst->vf.val  = array[field];
                dst->vf.when = 0;       /* no time component */
-#if 1
+               free(array);
+#if 0
 for(step=0;step<steps;step++)
 printf("DEBUG: %3li:%10.2f %c\n",step,array[step],step==field?'*':' ');
 #endif
@@ -3400,19 +3336,20 @@ printf("DEBUG: %3li:%10.2f %c\n",step,array[step],step==field?'*':' ');
                dst->vf.val  = DNAN;
                dst->vf.when = 0;
            } else {
-               dst->vf.val  = data[steps*src->ds_cnt];
+               dst->vf.val  = data[step*src->ds_cnt];
                dst->vf.when = src->start + (step+1)*src->step;
            }
            while (step != steps) {
                if (finite(data[step*src->ds_cnt])) {
                    if (data[step*src->ds_cnt] > dst->vf.val) {
-                       dst->vf.val  = data[steps*src->ds_cnt];
+                       dst->vf.val  = data[step*src->ds_cnt];
                        dst->vf.when = src->start + (step+1)*src->step;
                    }
                }
                step++;
            }
            break;
+       case VDEF_TOTAL:
        case VDEF_AVERAGE: {
            int cnt=0;
            double sum=0.0;
@@ -3420,12 +3357,16 @@ printf("DEBUG: %3li:%10.2f %c\n",step,array[step],step==field?'*':' ');
                if (finite(data[step*src->ds_cnt])) {
                    sum += data[step*src->ds_cnt];
                    cnt ++;
-               }
-               step++;
+               };
            }
            if (cnt) {
-               dst->vf.val  = sum/cnt;
-               dst->vf.when = 0;       /* no time component */
+               if (dst->vf.op == VDEF_TOTAL) {
+                   dst->vf.val  = sum*src->step;
+                   dst->vf.when = cnt*src->step;       /* not really "when" */
+               } else {
+                   dst->vf.val = sum/cnt;
+                   dst->vf.when = 0;   /* no time component */
+               };
            } else {
                dst->vf.val  = DNAN;
                dst->vf.when = 0;
@@ -3439,13 +3380,13 @@ printf("DEBUG: %3li:%10.2f %c\n",step,array[step],step==field?'*':' ');
                dst->vf.val  = DNAN;
                dst->vf.when = 0;
            } else {
-               dst->vf.val  = data[steps*src->ds_cnt];
+               dst->vf.val  = data[step*src->ds_cnt];
                dst->vf.when = src->start + (step+1)*src->step;
            }
            while (step != steps) {
                if (finite(data[step*src->ds_cnt])) {
                    if (data[step*src->ds_cnt] < dst->vf.val) {
-                       dst->vf.val  = data[steps*src->ds_cnt];
+                       dst->vf.val  = data[step*src->ds_cnt];
                        dst->vf.when = src->start + (step+1)*src->step;
                    }
                }
@@ -3486,7 +3427,7 @@ printf("DEBUG: %3li:%10.2f %c\n",step,array[step],step==field?'*':' ');
     return 0;
 }
 
-/* NaN <= -INF <= finite_values <= INF */
+/* NaN < -INF < finite_values < INF */
 int
 vdef_percent_compar(a,b)
 const void *a,*b;
@@ -3499,7 +3440,7 @@ const void *a,*b;
     if (isnan( *(double *)a )) return -1;
     if (isnan( *(double *)b )) return  1;
 
-    /* NaN doestn't reach this part so INF and -INF are extremes.
+    /* NaN doesn't reach this part so INF and -INF are extremes.
      * The sign from isinf() is compatible with the sign we return
      */
     if (isinf( *(double *)a )) return isinf( *(double *)a );