Initial revision
[rrdtool.git] / src / rrd_graph.c
1 /****************************************************************************
2  * RRDtool 1.0.33  Copyright Tobias Oetiker, 1997 - 2000
3  ****************************************************************************
4  * rrd__graph.c  make creates ne rrds
5  ****************************************************************************/
6
7 #include "rrd_tool.h"
8 #include <gd.h>
9 #include <gdlucidan10.h>
10 #include <gdlucidab12.h>
11 #include <sys/stat.h>
12 #ifdef WIN32
13 #include <io.h>
14 #include <fcntl.h>
15 #endif
16
17 #define SmallFont gdLucidaNormal10
18 #define LargeFont gdLucidaBold12
19
20 /* #define DEBUG */
21
22 #ifdef DEBUG
23 # define DPRINT(x)    (void)(printf x, printf("\n"))
24 #else
25 # define DPRINT(x)
26 #endif
27
28 #define DEF_NAM_FMT "%29[_A-Za-z0-9]"
29
30 enum tmt_en {TMT_SECOND=0,TMT_MINUTE,TMT_HOUR,TMT_DAY,
31              TMT_WEEK,TMT_MONTH,TMT_YEAR};
32
33 enum grc_en {GRC_CANVAS=0,GRC_BACK,GRC_SHADEA,GRC_SHADEB,
34              GRC_GRID,GRC_MGRID,GRC_FONT,GRC_FRAME,GRC_ARROW,__GRC_END__};
35
36
37 enum gf_en {GF_PRINT=0,GF_GPRINT,GF_COMMENT,GF_HRULE,GF_VRULE,GF_LINE1,
38             GF_LINE2,GF_LINE3,GF_AREA,GF_STACK, GF_DEF, GF_CDEF };
39
40 enum op_en {OP_NUMBER=0,OP_VARIABLE,OP_INF,OP_PREV,OP_NEGINF,
41             OP_UNKN,OP_NOW,OP_TIME,OP_LTIME,OP_ADD,OP_MOD,
42             OP_SUB,OP_MUL,
43             OP_DIV,OP_SIN, OP_DUP, OP_EXC, OP_POP,
44             OP_COS,OP_LOG,OP_EXP,OP_LT,OP_LE,OP_GT,OP_GE,OP_EQ,OP_IF,
45             OP_MIN,OP_MAX,OP_LIMIT, OP_FLOOR, OP_CEIL,
46             OP_UN,OP_END};
47
48 enum if_en {IF_GIF=0,IF_PNG=1};
49
50 typedef struct rpnp_t {
51     enum op_en   op;
52     double val; /* value for a OP_NUMBER */
53     long ptr; /* pointer into the gdes array for OP_VAR */
54     double *data; /* pointer to the current value from OP_VAR DAS*/
55     long ds_cnt;   /* data source count for data pointer */
56     long step; /* time step for OP_VAR das */
57 } rpnp_t;
58  
59
60 typedef struct col_trip_t {
61     int red; /* red = -1 is no color */
62     int green;
63     int blue;
64     int i; /* color index assigned in gif image i=-1 is unasigned*/
65 } col_trip_t;
66
67
68 typedef struct xlab_t {
69     long         minsec;       /* minimum sec per pix */
70     enum tmt_en  gridtm;       /* grid interval in what ?*/
71     long         gridst;       /* how many whats per grid*/
72     enum tmt_en  mgridtm;      /* label interval in what ?*/
73     long         mgridst;      /* how many whats per label*/
74     enum tmt_en  labtm;        /* label interval in what ?*/
75     long         labst;        /* how many whats per label*/
76     long         precis;       /* label precision -> label placement*/
77     char         *stst;        /* strftime string*/
78 } xlab_t;
79
80 xlab_t xlab[] = {
81     {0,        TMT_SECOND,30, TMT_MINUTE,5,  TMT_MINUTE,5,         0,"%H:%M"},
82     {2,        TMT_MINUTE,1,  TMT_MINUTE,5,  TMT_MINUTE,5,         0,"%H:%M"},
83     {5,        TMT_MINUTE,2,  TMT_MINUTE,10, TMT_MINUTE,10,        0,"%H:%M"},
84     {10,       TMT_MINUTE,5,  TMT_MINUTE,20, TMT_MINUTE,20,        0,"%H:%M"},
85     {30,       TMT_MINUTE,10, TMT_HOUR,1,    TMT_HOUR,1,           0,"%H:%M"},
86     {60,       TMT_MINUTE,30, TMT_HOUR,2,    TMT_HOUR,2,           0,"%H:%M"},
87     {180,      TMT_HOUR,1,    TMT_HOUR,6,    TMT_HOUR,6,           0,"%H:%M"},
88     /*{300,      TMT_HOUR,3,    TMT_HOUR,12,   TMT_HOUR,12,    12*3600,"%a %p"},  this looks silly*/
89     {600,      TMT_HOUR,6,    TMT_DAY,1,     TMT_DAY,1,      24*3600,"%a"},
90     {1800,     TMT_HOUR,12,   TMT_DAY,1,     TMT_DAY,2,      24*3600,"%a"},
91     {3600,     TMT_DAY,1,     TMT_WEEK,1,     TMT_WEEK,1,    7*24*3600,"Week %W"},
92     {3*3600,   TMT_WEEK,1,      TMT_MONTH,1,     TMT_WEEK,2,    7*24*3600,"Week %W"},
93     {6*3600,   TMT_MONTH,1,   TMT_MONTH,1,   TMT_MONTH,1, 30*24*3600,"%b"},
94     {48*3600,  TMT_MONTH,1,   TMT_MONTH,3,   TMT_MONTH,3, 30*24*3600,"%b"},
95     {10*24*3600, TMT_YEAR,1,  TMT_YEAR,1,    TMT_YEAR,1, 365*24*3600,"%y"},
96     {-1,TMT_MONTH,0,TMT_MONTH,0,TMT_MONTH,0,0,""}
97 };
98
99 /* sensible logarithmic y label intervals ...
100    the first element of each row defines the possible starting points on the
101    y axis ... the other specify the */
102
103 double yloglab[][12]= {{ 1e9, 1,  0,  0,  0,   0,  0,  0,  0,  0,  0,  0 },
104                        {  1e3, 1,  0,  0,  0,   0,  0,  0,  0,  0,  0,  0 },
105                        {  1e1, 1,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0 },
106                        /* {  1e1, 1,  5,  0,  0,  0,  0,  0,  0,  0,  0,  0 }, */
107                        {  1e1, 1,  2.5,  5,  7.5,  0,  0,  0,  0,  0,  0,  0 },
108                        {  1e1, 1,  2,  4,  6,  8,  0,  0,  0,  0,  0,  0 },
109                        {  1e1, 1,  2,  3,  4,  5,  6,  7,  8,  9,  0,  0 },
110                        {  0,   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0 }};
111
112 /* sensible y label intervals ...*/
113
114 typedef struct ylab_t {
115     double   grid;    /* grid spacing */
116     int      lfac[4]; /* associated label spacing*/
117 } ylab_t;
118
119 ylab_t ylab[]= {
120     {0.1, {1,2, 5,10}},
121     {0.2, {1,5,10,20}},
122     {0.5, {1,2, 4,10}},
123     {1.0,   {1,2, 5,10}},
124     {2.0,   {1,5,10,20}},
125     {5.0,   {1,2, 4,10}},
126     {10.0,  {1,2, 5,10}},
127     {20.0,  {1,5,10,20}},
128     {50.0,  {1,2, 4,10}},
129     {100.0, {1,2, 5,10}},
130     {200.0, {1,5,10,20}},
131     {500.0, {1,2, 4,10}},
132     {0.0,   {0,0,0,0}}};
133
134
135
136 col_trip_t graph_col[] = { /* default colors */
137     {255,255,255,-1},   /* canvas */
138     {245,245,245,-1},   /* background */
139     {200,200,200,-1},   /* shade A    */
140     {150,150,150,-1},   /* shade B    */
141     {140,140,140,-1},      /* grid */
142     {130,30,30,-1},      /* major grid */
143     {0,0,0,-1},         /* font */      
144     {0,0,0,-1},         /* frame */
145     {255,0,0,-1}        /*arrow*/
146 };
147
148 /* this structure describes the elements which can make up a graph.
149    because they are quite diverse, not all elements will use all the
150    possible parts of the structure. */
151 #ifdef HAVE_SNPRINTF
152 #define FMT_LEG_LEN 200
153 #else
154 #define FMT_LEG_LEN 2000
155 #endif
156
157 typedef  struct graph_desc_t {
158     enum gf_en     gf;         /* graphing function */
159     char           vname[30];  /* name of the variable */
160     long           vidx;       /* gdes reference */
161     char           rrd[255];   /* name of the rrd_file containing data */
162     char           ds_nam[DS_NAM_SIZE]; /* data source name */
163     long           ds;         /* data source number */
164     enum cf_en     cf;         /* consolidation function */
165     col_trip_t     col;        /* graph color */
166     char           format[FMT_LEG_LEN+5]; /* format for PRINT AND GPRINT */
167     char           legend[FMT_LEG_LEN+5]; /* legend*/
168     gdPoint        legloc;     /* location of legend */   
169     double         yrule;      /* value for y rule line */
170     time_t         xrule;      /* value for x rule line */
171     rpnp_t         *rpnp;     /* instructions for CDEF function */
172
173     /* description of data fetched for the graph element */
174     time_t         start,end; /* timestaps for first and last data element */
175     unsigned long  step;      /* time between samples */
176     unsigned long  ds_cnt; /* how many data sources are there in the fetch */
177     long           data_first; /* first pointer to this data */
178     char           **ds_namv; /* name of datasources  in the fetch. */
179     rrd_value_t    *data; /* the raw data drawn from the rrd */
180     rrd_value_t    *p_data; /* processed data, xsize elments */
181
182 } graph_desc_t;
183
184 #define ALTYGRID          0x01  /* use alternative y grid algorithm */
185 #define ALTAUTOSCALE      0x02  /* use alternative algorithm to find lower and upper bounds */
186 #define ALTAUTOSCALE_MAX  0x04  /* use alternative algorithm to find upper bounds */
187 #define NOLEGEND          0x08  /* use no legend */
188
189 typedef struct image_desc_t {
190
191     /* configuration of graph */
192
193     char           graphfile[MAXPATH]; /* filename for graphic */
194     long           xsize,ysize;    /* graph area size in pixels */
195     col_trip_t     graph_col[__GRC_END__]; /* real colors for the graph */   
196     char           ylegend[200];   /* legend along the yaxis */
197     char           title[200];     /* title for graph */
198     int            draw_x_grid;      /* no x-grid at all */
199     int            draw_y_grid;      /* no x-grid at all */
200     xlab_t         xlab_user;      /* user defined labeling for xaxis */
201     char           xlab_form[200]; /* format for the label on the xaxis */
202
203     double         ygridstep;      /* user defined step for y grid */
204     int            ylabfact;       /* every how many y grid shall a label be written ? */
205
206     time_t         start,end;      /* what time does the graph cover */
207     unsigned long           step;           /* any preference for the default step ? */
208     rrd_value_t    minval,maxval;  /* extreme values in the data */
209     int            rigid;          /* do not expand range even with 
210                                       values outside */
211     char*          imginfo;         /* construct an <IMG ... tag and return 
212                                       as first retval */
213     int            lazy;           /* only update the gif if there is reasonable
214                                       probablility that the existing one is out of date */
215     int            logarithmic;    /* scale the yaxis logarithmic */
216     enum if_en     imgformat;         /* image format */
217     
218     /* status information */
219             
220     long           xorigin,yorigin;/* where is (0,0) of the graph */
221     long           xgif,ygif;      /* total size of the gif */
222     int            interlaced;     /* will the graph be interlaced? */
223     double         magfact;        /* numerical magnitude*/
224     long         base;            /* 1000 or 1024 depending on what we graph */
225     char           symbol;         /* magnitude symbol for y-axis */
226     int            unitsexponent;    /* 10*exponent for units on y-asis */
227     int            extra_flags;    /* flags for boolean options */
228     /* data elements */
229
230     long  prt_c;                  /* number of print elements */
231     long  gdes_c;                  /* number of graphics elements */
232     graph_desc_t   *gdes;          /* points to an array of graph elements */
233
234 } image_desc_t;
235
236 /* Prototypes */
237 int xtr(image_desc_t *,time_t);
238 int ytr(image_desc_t *, double);
239 enum gf_en gf_conv(char *);
240 enum if_en if_conv(char *);
241 enum tmt_en tmt_conv(char *);
242 enum grc_en grc_conv(char *);
243 int im_free(image_desc_t *);
244 void auto_scale( image_desc_t *,  double *, char **, double *);
245 void si_unit( image_desc_t *);
246 void expand_range(image_desc_t *);
247 void reduce_data( enum cf_en,  unsigned long,  time_t *, time_t *,  unsigned long *,  unsigned long *,  rrd_value_t **);
248 int data_fetch( image_desc_t *);
249 long find_var(image_desc_t *, char *);
250 long lcd(long *);
251 int data_calc( image_desc_t *);
252 int data_proc( image_desc_t *);
253 time_t find_first_time( time_t,  enum tmt_en,  long);
254 time_t find_next_time( time_t,  enum tmt_en,  long);
255 void gator( gdImagePtr, int, int);
256 int tzoffset(time_t);
257 int print_calc(image_desc_t *, char ***);
258 int leg_place(image_desc_t *);
259 int horizontal_grid(gdImagePtr, image_desc_t *);
260 int horizontal_log_grid(gdImagePtr, image_desc_t *);
261 void vertical_grid( gdImagePtr, image_desc_t *);
262 void axis_paint( image_desc_t *, gdImagePtr);
263 void grid_paint( image_desc_t *, gdImagePtr);
264 gdImagePtr MkLineBrush(image_desc_t *,long, enum gf_en);
265 int lazy_check(image_desc_t *);
266 int graph_paint(image_desc_t *, char ***);
267 int gdes_alloc(image_desc_t *);
268 int scan_for_col(char *, int, char *);
269 int rrd_graph(int, char **, char ***, int *, int *);
270 int bad_format(char *);
271 rpnp_t * str2rpn(image_desc_t *,char *);
272
273 /* translate time values into x coordinates */   
274 /*#define xtr(x) (int)((double)im->xorigin \
275                 + ((double) im->xsize / (double)(im->end - im->start) ) \
276                 * ((double)(x) - im->start)+0.5) */
277 /* initialize with xtr(im,0); */
278 int
279 xtr(image_desc_t *im,time_t mytime){
280     static double pixie;
281     if (mytime==0){
282         pixie = (double) im->xsize / (double)(im->end - im->start);
283         return im->xorigin;
284     }
285     return (int)((double)im->xorigin 
286                  + pixie * ( mytime - im->start ) );
287 }
288
289 /* translate data values into y coordinates */
290
291 /* #define ytr(x) (int)((double)im->yorigin \
292                 - ((double) im->ysize / (im->maxval - im->minval) ) \
293                 * ((double)(x) - im->minval)+0.5) */
294 int
295 ytr(image_desc_t *im, double value){
296     static double pixie;
297     double yval;
298     if (isnan(value)){
299       if(!im->logarithmic)
300         pixie = (double) im->ysize / (im->maxval - im->minval);
301       else 
302         pixie = (double) im->ysize / (log10(im->maxval) - log10(im->minval));
303       yval = im->yorigin;
304     } else if(!im->logarithmic) {
305       yval = im->yorigin - pixie * (value - im->minval) + 0.5;
306     } else {
307       if (value < im->minval) {
308         yval = im->yorigin;
309       } else {
310         yval = im->yorigin - pixie * (log10(value) - log10(im->minval)) + 0.5;
311       }
312     }
313     /* make sure we don't return anything too unreasonable. GD lib can
314        get terribly slow when drawing lines outside its scope. This is 
315        especially problematic in connection with the rigid option */
316     if (! im->rigid) {
317       return (int)yval;
318     } else if ((int)yval > im->yorigin) {
319       return im->yorigin+2;
320     } else if ((int) yval < im->yorigin - im->ysize){
321       return im->yorigin - im->ysize - 2;
322     } else {
323       return (int)yval;
324     } 
325 }
326
327    
328     
329
330
331 /* conversion function for symbolic entry names */
332
333
334 #define conv_if(VV,VVV) \
335    if (strcmp(#VV, string) == 0) return VVV ;
336
337 enum gf_en gf_conv(char *string){
338     
339     conv_if(PRINT,GF_PRINT)
340     conv_if(GPRINT,GF_GPRINT)
341     conv_if(COMMENT,GF_COMMENT)
342     conv_if(HRULE,GF_HRULE)
343     conv_if(VRULE,GF_VRULE)
344     conv_if(LINE1,GF_LINE1)
345     conv_if(LINE2,GF_LINE2)
346     conv_if(LINE3,GF_LINE3)
347     conv_if(AREA,GF_AREA)
348     conv_if(STACK,GF_STACK)
349     conv_if(DEF,GF_DEF)
350     conv_if(CDEF,GF_CDEF)
351     
352     return (-1);
353 }
354
355 enum if_en if_conv(char *string){
356     
357     conv_if(GIF,IF_GIF)
358     conv_if(PNG,IF_PNG)
359
360     return (-1);
361 }
362
363 enum tmt_en tmt_conv(char *string){
364
365     conv_if(SECOND,TMT_SECOND)
366     conv_if(MINUTE,TMT_MINUTE)
367     conv_if(HOUR,TMT_HOUR)
368     conv_if(DAY,TMT_DAY)
369     conv_if(WEEK,TMT_WEEK)
370     conv_if(MONTH,TMT_MONTH)
371     conv_if(YEAR,TMT_YEAR)
372     return (-1);
373 }
374
375 enum grc_en grc_conv(char *string){
376
377     conv_if(BACK,GRC_BACK)
378     conv_if(CANVAS,GRC_CANVAS)
379     conv_if(SHADEA,GRC_SHADEA)
380     conv_if(SHADEB,GRC_SHADEB)
381     conv_if(GRID,GRC_GRID)
382     conv_if(MGRID,GRC_MGRID)
383     conv_if(FONT,GRC_FONT)
384     conv_if(FRAME,GRC_FRAME)
385     conv_if(ARROW,GRC_ARROW)
386
387     return -1;  
388 }
389
390 #undef conv_if
391
392
393
394 int
395 im_free(image_desc_t *im)
396 {
397     long i,ii;
398     if (im == NULL) return 0;
399     for(i=0;i<im->gdes_c;i++){
400       if (im->gdes[i].data_first){
401         /* careful here, because a single pointer can occur several times */
402           free (im->gdes[i].data);
403           if (im->gdes[i].ds_namv){
404               for (ii=0;ii<im->gdes[i].ds_cnt;ii++)
405                   free(im->gdes[i].ds_namv[ii]);
406               free(im->gdes[i].ds_namv);
407           }
408       }
409       free (im->gdes[i].p_data);
410       free (im->gdes[i].rpnp);
411     }
412     free(im->gdes);
413     return 0;
414 }
415
416 /* find SI magnitude symbol for the given number*/
417 void
418 auto_scale(
419            image_desc_t *im,   /* image description */
420            double *value,
421            char **symb_ptr,
422            double *magfact
423            )
424 {
425         
426     char *symbol[] = {"a", /* 10e-18 Ato */
427                       "f", /* 10e-15 Femto */
428                       "p", /* 10e-12 Pico */
429                       "n", /* 10e-9  Nano */
430                       "u", /* 10e-6  Micro */
431                       "m", /* 10e-3  Milli */
432                       " ", /* Base */
433                       "k", /* 10e3   Kilo */
434                       "M", /* 10e6   Mega */
435                       "G", /* 10e9   Giga */
436                       "T", /* 10e12  Terra */
437                       "P", /* 10e15  Peta */
438                       "E"};/* 10e18  Exa */
439
440     int symbcenter = 6;
441     int sindex;  
442
443     if (*value == 0.0 || isnan(*value) ) {
444         sindex = 0;
445         *magfact = 1.0;
446     } else {
447         sindex = floor(log(fabs(*value))/log((double)im->base)); 
448         *magfact = pow((double)im->base, (double)sindex);
449         (*value) /= (*magfact);
450     }
451     if ( sindex <= symbcenter && sindex >= -symbcenter) {
452         (*symb_ptr) = symbol[sindex+symbcenter];
453     }
454     else {
455         (*symb_ptr) = "?";
456     }
457 }
458
459
460 /* find SI magnitude symbol for the numbers on the y-axis*/
461 void 
462 si_unit(
463     image_desc_t *im   /* image description */
464 )
465 {
466
467     char symbol[] = {'a', /* 10e-18 Ato */ 
468                      'f', /* 10e-15 Femto */
469                      'p', /* 10e-12 Pico */
470                      'n', /* 10e-9  Nano */
471                      'u', /* 10e-6  Micro */
472                      'm', /* 10e-3  Milli */
473                      ' ', /* Base */
474                      'k', /* 10e3   Kilo */
475                      'M', /* 10e6   Mega */
476                      'G', /* 10e9   Giga */
477                      'T', /* 10e12  Terra */
478                      'P', /* 10e15  Peta */
479                      'E'};/* 10e18  Exa */
480
481     int   symbcenter = 6;
482     double digits;  
483     
484     if (im->unitsexponent != 9999) {
485         /* unitsexponent = 9, 6, 3, 0, -3, -6, -9, etc */
486         digits = floor(im->unitsexponent / 3);
487     } else {
488         digits = floor( log( max( fabs(im->minval),fabs(im->maxval)))/log((double)im->base)); 
489     }
490     im->magfact = pow((double)im->base , digits);
491
492 #ifdef DEBUG
493     printf("digits %6.3f  im->magfact %6.3f\n",digits,im->magfact);
494 #endif
495
496     if ( ((digits+symbcenter) < sizeof(symbol)) &&
497                     ((digits+symbcenter) >= 0) )
498         im->symbol = symbol[(int)digits+symbcenter];
499     else
500         im->symbol = ' ';
501  }
502
503 /*  move min and max values around to become sensible */
504
505 void 
506 expand_range(image_desc_t *im)
507 {
508     double sensiblevalues[] ={1000.0,900.0,800.0,750.0,700.0,
509                               600.0,500.0,400.0,300.0,250.0,
510                               200.0,125.0,100.0,90.0,80.0,
511                               75.0,70.0,60.0,50.0,40.0,30.0,
512                               25.0,20.0,10.0,9.0,8.0,
513                               7.0,6.0,5.0,4.0,3.5,3.0,
514                               2.5,2.0,1.8,1.5,1.2,1.0,
515                               0.8,0.7,0.6,0.5,0.4,0.3,0.2,0.1,0.0,-1};
516     
517     double scaled_min,scaled_max;  
518     double adj;
519     int i;
520     
521
522     
523 #ifdef DEBUG
524     printf("Min: %6.2f Max: %6.2f MagFactor: %6.2f\n",
525            im->minval,im->maxval,im->magfact);
526 #endif
527
528     if (isnan(im->ygridstep)){
529         if(im->extra_flags & ALTAUTOSCALE) {
530             /* measure the amplitude of the function. Make sure that
531                graph boundaries are slightly higher then max/min vals
532                so we can see amplitude on the graph */
533               double delt, fact;
534
535               delt = im->maxval - im->minval;
536               adj = delt * 0.1;
537               fact = 2.0 * pow(10.0,
538                     floor(log10(max(fabs(im->minval), fabs(im->maxval)))) - 2);
539               if (delt < fact) {
540                 adj = (fact - delt) * 0.55;
541 #ifdef DEBUG
542               printf("Min: %6.2f Max: %6.2f delt: %6.2f fact: %6.2f adj: %6.2f\n", im->minval, im->maxval, delt, fact, adj);
543 #endif
544               }
545               im->minval -= adj;
546               im->maxval += adj;
547         }
548         else if(im->extra_flags & ALTAUTOSCALE_MAX) {
549             /* measure the amplitude of the function. Make sure that
550                graph boundaries are slightly higher than max vals
551                so we can see amplitude on the graph */
552               adj = (im->maxval - im->minval) * 0.1;
553               im->maxval += adj;
554         }
555         else {
556             scaled_min = im->minval / im->magfact;
557             scaled_max = im->maxval / im->magfact;
558             
559             for (i=1; sensiblevalues[i] > 0; i++){
560                 if (sensiblevalues[i-1]>=scaled_min &&
561                     sensiblevalues[i]<=scaled_min)      
562                     im->minval = sensiblevalues[i]*(im->magfact);
563                 
564                 if (-sensiblevalues[i-1]<=scaled_min &&
565                 -sensiblevalues[i]>=scaled_min)
566                     im->minval = -sensiblevalues[i-1]*(im->magfact);
567                 
568                 if (sensiblevalues[i-1] >= scaled_max &&
569                     sensiblevalues[i] <= scaled_max)
570                     im->maxval = sensiblevalues[i-1]*(im->magfact);
571                 
572                 if (-sensiblevalues[i-1]<=scaled_max &&
573                     -sensiblevalues[i] >=scaled_max)
574                     im->maxval = -sensiblevalues[i]*(im->magfact);
575             }
576         }
577     } else {
578         /* adjust min and max to the grid definition if there is one */
579         im->minval = (double)im->ylabfact * im->ygridstep * 
580             floor(im->minval / ((double)im->ylabfact * im->ygridstep));
581         im->maxval = (double)im->ylabfact * im->ygridstep * 
582             ceil(im->maxval /( (double)im->ylabfact * im->ygridstep));
583     }
584     
585 #ifdef DEBUG
586     fprintf(stderr,"SCALED Min: %6.2f Max: %6.2f Factor: %6.2f\n",
587            im->minval,im->maxval,im->magfact);
588 #endif
589 }
590
591     
592 /* reduce data reimplementation by Alex */
593
594 void
595 reduce_data(
596     enum cf_en     cf,         /* which consolidation function ?*/
597     unsigned long  cur_step,   /* step the data currently is in */
598     time_t         *start,     /* start, end and step as requested ... */
599     time_t         *end,       /* ... by the application will be   ... */
600     unsigned long  *step,      /* ... adjusted to represent reality    */
601     unsigned long  *ds_cnt,    /* number of data sources in file */
602     rrd_value_t    **data)     /* two dimensional array containing the data */
603 {
604     int i,reduce_factor = ceil((double)(*step) / (double)cur_step);
605     unsigned long col,dst_row,row_cnt,start_offset,end_offset,skiprows=0;
606     rrd_value_t    *srcptr,*dstptr;
607
608     (*step) = cur_step*reduce_factor; /* set new step size for reduced data */
609     dstptr = *data;
610     srcptr = *data;
611
612     /* We were given one extra row at the beginning of the interval.
613     ** We also need to return one extra row.  The extra interval is
614     ** the one defined by the start time in both cases.  It is not
615     ** used when graphing but maybe we can use it while reducing the
616     ** data.
617     */
618     row_cnt = ((*end)-(*start))/cur_step +1;
619
620     /* alter start and end so that they are multiples of the new steptime.
621     ** End will be shifted towards the future and start will be shifted
622     ** towards the past in order to include the requested interval
623     */ 
624     end_offset = (*end) % (*step);
625     if (end_offset) end_offset = (*step)-end_offset;
626     start_offset = (*start) % (*step);
627     (*end) = (*end)+end_offset;
628     (*start) = (*start)-start_offset;
629
630     /* The first destination row is unknown yet it still needs
631     ** to be present in the returned data.  Skip it.
632     ** Don't make it NaN or we might overwrite the source.
633     */
634     dstptr += (*ds_cnt);
635
636     /* Depending on the amount of extra data needed at the
637     ** start of the destination, three things can happen:
638     ** -1- start_offset == 0:  skip the extra source row
639     ** -2- start_offset == cur_step: do nothing
640     ** -3- start_offset > cur_step: skip some source rows and 
641     **                      fill one destination row with NaN
642     */
643     if (start_offset==0) {
644         srcptr+=(*ds_cnt);
645         row_cnt--;
646     } else if (start_offset!=cur_step) {
647         skiprows=((*step)-start_offset)/cur_step+1;
648         srcptr += ((*ds_cnt)*skiprows);
649         row_cnt-=skiprows;
650         for (col=0;col<(*ds_cnt);col++) *dstptr++=DNAN;
651     }
652
653     /* If we had to alter the endtime, there won't be
654     ** enough data to fill the last row.  This means
655     ** we have to skip some rows at the end
656     */
657     if (end_offset) {
658         skiprows = ((*step)-end_offset)/cur_step;
659         row_cnt-=skiprows;
660     }
661
662
663 /* Sanity check: row_cnt should be multiple of reduce_factor */
664 /* if this gets triggered, something is REALY WRONG ... we die immediately */
665
666     if (row_cnt%reduce_factor) {
667         printf("SANITY CHECK: %lu rows cannot be reduced by %i \n",
668                                 row_cnt,reduce_factor);
669         printf("BUG in reduce_data()\n");
670         exit(1);
671     }
672
673     /* Now combine reduce_factor intervals at a time
674     ** into one interval for the destination.
675     */
676
677     for (dst_row=0;row_cnt>=reduce_factor;dst_row++) {
678         for (col=0;col<(*ds_cnt);col++) {
679             rrd_value_t newval=DNAN;
680             unsigned long validval=0;
681
682             for (i=0;i<reduce_factor;i++) {
683                 if (isnan(srcptr[i*(*ds_cnt)+col])) {
684                     continue;
685                 }
686                 validval++;
687                 if (isnan(newval)) newval = srcptr[i*(*ds_cnt)+col];
688                 else {
689                     switch (cf) {
690                         case CF_AVERAGE:
691                             newval += srcptr[i*(*ds_cnt)+col];
692                             break;
693                         case CF_MINIMUM:
694                             newval = min (newval,srcptr[i*(*ds_cnt)+col]);
695                             break;
696                         case CF_MAXIMUM:
697                             newval = max (newval,srcptr[i*(*ds_cnt)+col]);
698                             break;
699                         case CF_LAST:
700                             newval = srcptr[i*(*ds_cnt)+col];
701                             break;
702                     }
703                 }
704             }
705             if (validval == 0){newval = DNAN;} else{
706                 switch (cf) {
707                     case CF_AVERAGE:            
708                         newval /= validval;
709                         break;
710                     case CF_MINIMUM:
711                     case CF_MAXIMUM:
712                     case CF_LAST:
713                         break;
714                 }
715             }
716             *dstptr++=newval;
717         }
718         srcptr+=(*ds_cnt)*reduce_factor;
719         row_cnt-=reduce_factor;
720     }
721
722     /* If we had to alter the endtime, we didn't have enough
723     ** source rows to fill the last row. Fill it with NaN.
724     */
725     if (end_offset!=0) for (col=0;col<(*ds_cnt);col++) *dstptr++ = DNAN;
726 }
727
728
729 /* get the data required for the graphs from the 
730    relevant rrds ... */
731
732 int
733 data_fetch( image_desc_t *im )
734 {
735     int       i,ii;
736     int skip;
737     /* pull the data from the log files ... */
738     for (i=0;i<im->gdes_c;i++){
739         /* only GF_DEF elements fetch data */
740         if (im->gdes[i].gf != GF_DEF) 
741             continue;
742
743         skip=0;
744         /* do we have it already ?*/
745         for (ii=0;ii<i;ii++){
746             if (im->gdes[ii].gf != GF_DEF) 
747                 continue;
748             if((strcmp(im->gdes[i].rrd,im->gdes[ii].rrd) == 0)
749                 && (im->gdes[i].cf == im->gdes[ii].cf)){
750                 /* OK the data it is here already ... 
751                  * we just copy the header portion */
752                 im->gdes[i].start = im->gdes[ii].start;
753                 im->gdes[i].end = im->gdes[ii].end;
754                 im->gdes[i].step = im->gdes[ii].step;
755                 im->gdes[i].ds_cnt = im->gdes[ii].ds_cnt;
756                 im->gdes[i].ds_namv = im->gdes[ii].ds_namv;             
757                 im->gdes[i].data = im->gdes[ii].data;
758                 im->gdes[i].data_first = 0;
759                 skip=1;
760             }
761             if (skip) 
762                 break;
763         }
764         if (! skip) {
765             unsigned long  ft_step = im->gdes[i].step ;
766             
767             if((rrd_fetch_fn(im->gdes[i].rrd,
768                              im->gdes[i].cf,
769                              &im->gdes[i].start,
770                              &im->gdes[i].end,
771                              &ft_step,
772                              &im->gdes[i].ds_cnt,
773                              &im->gdes[i].ds_namv,
774                              &im->gdes[i].data)) == -1){                
775                 return -1;
776             }
777             im->gdes[i].data_first = 1;     
778         
779             if (ft_step < im->gdes[i].step) {
780                 reduce_data(im->gdes[i].cf,
781                             ft_step,
782                             &im->gdes[i].start,
783                             &im->gdes[i].end,
784                             &im->gdes[i].step,
785                             &im->gdes[i].ds_cnt,
786                             &im->gdes[i].data);
787             } else {
788                 im->gdes[i].step = ft_step;
789             }
790         }
791         
792         /* lets see if the required data source is realy there */
793         for(ii=0;ii<im->gdes[i].ds_cnt;ii++){
794             if(strcmp(im->gdes[i].ds_namv[ii],im->gdes[i].ds_nam) == 0){
795                 im->gdes[i].ds=ii; }
796         }
797         if (im->gdes[i].ds== -1){
798             rrd_set_error("No DS called '%s' in '%s'",
799                           im->gdes[i].ds_nam,im->gdes[i].rrd);
800             return -1; 
801         }
802         
803     }
804     return 0;
805 }
806
807 /* evaluate the expressions in the CDEF functions */
808
809 /*************************************************************
810  * CDEF stuff 
811  *************************************************************/
812
813 /* find gdes containing var*/
814 long
815 find_var(image_desc_t *im, char *key){
816     long ii;
817     for(ii=0;ii<im->gdes_c-1;ii++){
818         if((im->gdes[ii].gf == GF_DEF 
819             || im->gdes[ii].gf == GF_CDEF) 
820            && (strcmp(im->gdes[ii].vname,key) == 0)){
821             return ii; 
822         }          
823     }               
824     return -1;
825 }
826
827 /* find the largest common denominator for all the numbers
828    in the 0 terminated num array */
829 long
830 lcd(long *num){
831     long rest;
832     int i;
833     for (i=0;num[i+1]!=0;i++){
834         do { 
835             rest=num[i] % num[i+1];
836             num[i]=num[i+1]; num[i+1]=rest;
837         } while (rest!=0);
838         num[i+1] = num[i];
839     }
840 /*    return i==0?num[i]:num[i-1]; */
841       return num[i];
842 }
843
844
845 /* convert string to rpnp */
846 rpnp_t * 
847 str2rpn(image_desc_t *im,char *expr){
848     int pos=0;
849     long steps=-1;    
850     rpnp_t  *rpnp;
851     char vname[30];
852
853     rpnp=NULL;
854
855     while(*expr){
856         if ((rpnp = (rpnp_t *) rrd_realloc(rpnp, (++steps + 2)* 
857                                        sizeof(rpnp_t)))==NULL){
858             return NULL;
859         }
860
861         else if((sscanf(expr,"%lf%n",&rpnp[steps].val,&pos) == 1) 
862                 && (expr[pos] == ',')){
863             rpnp[steps].op = OP_NUMBER;
864             expr+=pos;
865         } 
866         
867 #define match_op(VV,VVV) \
868         else if (strncmp(expr, #VVV, strlen(#VVV))==0 && \
869                 (expr[strlen(#VVV)] == ',' || expr[strlen(#VVV)] == '\0') ){ \
870             rpnp[steps].op = VV; \
871             expr+=strlen(#VVV); \
872         }
873
874         match_op(OP_ADD,+)
875         match_op(OP_SUB,-)
876         match_op(OP_MUL,*)
877         match_op(OP_DIV,/)
878         match_op(OP_MOD,%)
879         match_op(OP_SIN,SIN)
880         match_op(OP_COS,COS)
881         match_op(OP_LOG,LOG)
882         match_op(OP_FLOOR,FLOOR)
883         match_op(OP_CEIL,CEIL)
884         match_op(OP_EXP,EXP)
885         match_op(OP_DUP,DUP)
886         match_op(OP_EXC,EXC)
887         match_op(OP_POP,POP)
888         match_op(OP_LT,LT)
889         match_op(OP_LE,LE)
890         match_op(OP_GT,GT)
891         match_op(OP_GE,GE)
892         match_op(OP_EQ,EQ)
893         match_op(OP_IF,IF)
894         match_op(OP_MIN,MIN)
895         match_op(OP_MAX,MAX)
896         match_op(OP_LIMIT,LIMIT)
897           /* order is important here ! .. match longest first */
898         match_op(OP_UNKN,UNKN)
899         match_op(OP_UN,UN)
900         match_op(OP_NEGINF,NEGINF)
901         match_op(OP_PREV,PREV)
902         match_op(OP_INF,INF)
903         match_op(OP_NOW,NOW)
904         match_op(OP_LTIME,LTIME)
905         match_op(OP_TIME,TIME)
906
907
908 #undef match_op
909
910
911         else if ((sscanf(expr,DEF_NAM_FMT "%n",
912                          vname,&pos) == 1) 
913                  && ((rpnp[steps].ptr = find_var(im,vname)) != -1)){
914             rpnp[steps].op = OP_VARIABLE;
915             expr+=pos;
916         }          
917
918         else {
919             free(rpnp);
920             return NULL;
921         }
922         if (*expr == 0)
923           break;
924         if (*expr == ',')
925             expr++;
926         else {
927             free(rpnp);
928             return NULL;
929         }  
930     }
931     rpnp[steps+1].op = OP_END;
932     return rpnp;
933 }
934
935 /* figure out what the local timezone offset for any point in
936    time was. Return it in seconds */
937
938 int
939 tzoffset( time_t now ){
940   int gm_sec, gm_min, gm_hour, gm_yday, gm_year,
941     l_sec, l_min, l_hour, l_yday, l_year;
942   struct tm *t;
943   int off;
944   t = gmtime(&now);
945   gm_sec = t->tm_sec;
946   gm_min = t->tm_min;
947   gm_hour = t->tm_hour;
948   gm_yday = t->tm_yday;
949   gm_year = t->tm_year;
950   t = localtime(&now);
951   l_sec = t->tm_sec;
952   l_min = t->tm_min;
953   l_hour = t->tm_hour;
954   l_yday = t->tm_yday;
955   l_year = t->tm_year;
956   off = (l_sec-gm_sec)+(l_min-gm_min)*60+(l_hour-gm_hour)*3600; 
957   if ( l_yday > gm_yday || l_year > gm_year){
958         off += 24*3600;
959   } else if ( l_yday < gm_yday || l_year < gm_year){
960         off -= 24*3600;
961   }
962
963   return off;
964 }
965
966     
967
968 #define dc_stackblock 100
969
970 /* run the rpn calculator on all the CDEF arguments */
971
972 int
973 data_calc( image_desc_t *im){
974
975     int       gdi,rpi;
976     int       dataidx;
977     long      *steparray;
978     int       stepcnt;
979     time_t    now;
980     double    *stack = NULL;
981     long      dc_stacksize = 0;
982
983     for (gdi=0;gdi<im->gdes_c;gdi++){
984         /* only GF_CDEF elements are of interest */
985         if (im->gdes[gdi].gf != GF_CDEF) 
986             continue;
987         im->gdes[gdi].ds_cnt = 1;
988         im->gdes[gdi].ds = 0;
989         im->gdes[gdi].data_first = 1;
990         im->gdes[gdi].start = 0;
991         im->gdes[gdi].end = 0;
992         steparray=NULL;
993         stepcnt = 0;
994         dataidx=-1;
995
996         /* find the variables in the expression. And calc the lowest
997            common denominator of all step sizes of the data sources involved.
998            this will be the step size for the cdef created data source*/
999
1000         for(rpi=0;im->gdes[gdi].rpnp[rpi].op != OP_END;rpi++){
1001             if(im->gdes[gdi].rpnp[rpi].op == OP_VARIABLE){
1002                 long ptr = im->gdes[gdi].rpnp[rpi].ptr;
1003                 if ((steparray = rrd_realloc(steparray, (++stepcnt+1)*sizeof(*steparray)))==NULL){
1004                   rrd_set_error("realloc steparray");
1005                   free(stack);
1006                   return -1;
1007                 };
1008         
1009
1010                 steparray[stepcnt-1] = im->gdes[ptr].step;
1011
1012                 /* adjust start and end of cdef (gdi) so that it runs from
1013                    the latest start point to the earliest endpoint of any of the
1014                    rras involved (ptr) */
1015
1016                 if(im->gdes[gdi].start < im->gdes[ptr].start)
1017                     im->gdes[gdi].start = im->gdes[ptr].start;
1018
1019                 if(im->gdes[gdi].end == 0 
1020                    || im->gdes[gdi].end > im->gdes[ptr].end)
1021                     im->gdes[gdi].end = im->gdes[ptr].end;
1022                 
1023                 /* store pointer to the first element of the rra providing
1024                    data for variable, further save step size and data source count
1025                    of this rra*/ 
1026                 im->gdes[gdi].rpnp[rpi].data = 
1027                     im->gdes[ptr].data + im->gdes[ptr].ds;
1028                 im->gdes[gdi].rpnp[rpi].step = im->gdes[ptr].step;
1029                 im->gdes[gdi].rpnp[rpi].ds_cnt = im->gdes[ptr].ds_cnt;
1030             }
1031
1032         }
1033         if(steparray == NULL){
1034             rrd_set_error("rpn expressions without variables are not supported");
1035             free(stack);
1036             return -1;    
1037         }
1038         steparray[stepcnt]=0;
1039         /* now find the step for the result of the cdef. so that we land on
1040            each step in all of the variables rras */
1041
1042         im->gdes[gdi].step = lcd(steparray);
1043         
1044         
1045         free(steparray);
1046
1047         if((im->gdes[gdi].data = malloc(((im->gdes[gdi].end
1048                                      -im->gdes[gdi].start) 
1049                                     / im->gdes[gdi].step +1)
1050                                     * sizeof(double)))==NULL){
1051             rrd_set_error("malloc im->gdes[gdi].data");
1052             free(stack);
1053             return -1;
1054         }
1055         
1056         /* step through the new cdef results array and calculate the values */
1057         for (now = im->gdes[gdi].start;
1058              now<=im->gdes[gdi].end;
1059              now += im->gdes[gdi].step){
1060             long       stptr=-1;
1061             /* process each op from the rpn in turn */
1062             for (rpi=0;im->gdes[gdi].rpnp[rpi].op != OP_END;rpi++){
1063                 if (stptr +5 > dc_stacksize){
1064                     dc_stacksize += dc_stackblock;              
1065                     stack = rrd_realloc(stack,dc_stacksize*sizeof(*stack));
1066                     if (stack==NULL){
1067                         rrd_set_error("RPN stack overflow");
1068                         return -1;
1069                     }
1070                 }
1071                 switch (im->gdes[gdi].rpnp[rpi].op){
1072                 case OP_NUMBER:
1073                     stack[++stptr] = im->gdes[gdi].rpnp[rpi].val;
1074                     break;
1075                 case OP_VARIABLE:
1076                     /* make sure we pull the correct value from the *.data array */
1077                     /* adjust the pointer into the array acordingly. */
1078                     if(now >  im->gdes[gdi].start &&
1079                        now % im->gdes[gdi].rpnp[rpi].step == 0){
1080                         im->gdes[gdi].rpnp[rpi].data +=
1081                             im->gdes[gdi].rpnp[rpi].ds_cnt;
1082                     }
1083                     stack[++stptr] =  *im->gdes[gdi].rpnp[rpi].data;
1084                     break;
1085                 case OP_PREV:
1086                     if (dataidx <= 0) {
1087                        stack[++stptr] = DNAN;
1088                     } else {
1089                        stack[++stptr] = im->gdes[gdi].data[dataidx];
1090                     }
1091                     break;
1092                 case OP_UNKN:
1093                     stack[++stptr] = DNAN; 
1094                     break;
1095                 case OP_INF:
1096                     stack[++stptr] = DINF; 
1097                     break;
1098                 case OP_NEGINF:
1099                     stack[++stptr] = -DINF; 
1100                     break;
1101                 case OP_NOW:
1102                     stack[++stptr] = (double)time(NULL);
1103                     break;
1104                 case OP_TIME:
1105                     stack[++stptr] = (double)now;
1106                     break;
1107                 case OP_LTIME:
1108                     stack[++stptr] = (double)tzoffset(now)+(double)now;
1109                     break;
1110                 case OP_ADD:
1111                     if(stptr<1){
1112                         rrd_set_error("RPN stack underflow");
1113                         free(stack);
1114                         return -1;
1115                     }
1116                     stack[stptr-1] = stack[stptr-1] + stack[stptr];
1117                     stptr--;
1118                     break;
1119                 case OP_SUB:
1120                     if(stptr<1){
1121                         rrd_set_error("RPN stack underflow");
1122                         free(stack);
1123                         return -1;
1124                     }
1125                     stack[stptr-1] = stack[stptr-1] - stack[stptr];
1126                     stptr--;
1127                     break;
1128                 case OP_MUL:
1129                     if(stptr<1){
1130                         rrd_set_error("RPN stack underflow");
1131                         free(stack);
1132                         return -1;
1133                     }
1134                     stack[stptr-1] = stack[stptr-1] * stack[stptr];
1135                     stptr--;
1136                     break;
1137                 case OP_DIV:
1138                     if(stptr<1){
1139                         rrd_set_error("RPN stack underflow");
1140                         free(stack);
1141                         return -1;
1142                     }
1143                     stack[stptr-1] = stack[stptr-1] / stack[stptr];
1144                     stptr--;
1145                     break;
1146                 case OP_MOD:
1147                     if(stptr<1){
1148                         rrd_set_error("RPN stack underflow");
1149                         free(stack);
1150                         return -1;
1151                     }
1152                     stack[stptr-1] = fmod(stack[stptr-1],stack[stptr]);
1153                     stptr--;
1154                     break;
1155                 case OP_SIN:
1156                     if(stptr<0){
1157                         rrd_set_error("RPN stack underflow");
1158                         free(stack);
1159                         return -1;
1160                     }
1161                     stack[stptr] = sin(stack[stptr]);
1162                     break;
1163                 case OP_COS:
1164                     if(stptr<0){
1165                         rrd_set_error("RPN stack underflow");
1166                         free(stack);
1167                         return -1;
1168                     }
1169                     stack[stptr] = cos(stack[stptr]);
1170                     break;
1171                 case OP_CEIL:
1172                     if(stptr<0){
1173                         rrd_set_error("RPN stack underflow");
1174                         free(stack);
1175                         return -1;
1176                     }
1177                     stack[stptr] = ceil(stack[stptr]);
1178                     break;
1179                 case OP_FLOOR:
1180                     if(stptr<0){
1181                         rrd_set_error("RPN stack underflow");
1182                         free(stack);
1183                         return -1;
1184                     }
1185                     stack[stptr] = floor(stack[stptr]);
1186                     break;
1187                 case OP_LOG:
1188                     if(stptr<0){
1189                         rrd_set_error("RPN stack underflow");
1190                         free(stack);
1191                         return -1;
1192                     }
1193                     stack[stptr] = log(stack[stptr]);
1194                     break;
1195                 case OP_DUP:
1196                     if(stptr<0){
1197                         rrd_set_error("RPN stack underflow");
1198                         free(stack);
1199                         return -1;
1200                     }
1201                     stack[stptr+1] = stack[stptr];
1202                     stptr++;
1203                     break;
1204                 case OP_POP:
1205                     if(stptr<0){
1206                         rrd_set_error("RPN stack underflow");
1207                         free(stack);
1208                         return -1;
1209                     }
1210                     stptr--;
1211                     break;
1212                 case OP_EXC:
1213                     if(stptr<1){
1214                         rrd_set_error("RPN stack underflow");
1215                         free(stack);
1216                         return -1;
1217                     } else {
1218                       double dummy;
1219                       dummy = stack[stptr] ;
1220                       stack[stptr] = stack[stptr-1];
1221                       stack[stptr-1] = dummy;
1222                     }
1223                     break;
1224                 case OP_EXP:
1225                     if(stptr<0){
1226                         rrd_set_error("RPN stack underflow");
1227                         free(stack);
1228                         return -1;
1229                     }
1230                     stack[stptr] = exp(stack[stptr]);
1231                     break;
1232                 case OP_LT:
1233                     if(stptr<1){
1234                         rrd_set_error("RPN stack underflow");
1235                         free(stack);
1236                         return -1;
1237                     }
1238                     if (isnan(stack[stptr-1]) || isnan(stack[stptr]))
1239                         stack[stptr-1] = 0.0;
1240                     else
1241                         stack[stptr-1] = stack[stptr-1] < stack[stptr] ? 1.0 : 0.0;
1242                     stptr--;
1243                     break;
1244                 case OP_LE:
1245                     if(stptr<1){
1246                         rrd_set_error("RPN stack underflow");
1247                         free(stack);
1248                         return -1;
1249                     }
1250                     if (isnan(stack[stptr-1]) || isnan(stack[stptr]))
1251                         stack[stptr-1] = 0.0;
1252                     else
1253                         stack[stptr-1] = stack[stptr-1] <= stack[stptr] ? 1.0 : 0.0;
1254                     stptr--;
1255                     break;
1256                 case OP_GT:
1257                     if(stptr<1){
1258                         rrd_set_error("RPN stack underflow");
1259                         free(stack);
1260                         return -1;
1261                     }
1262                     if (isnan(stack[stptr-1]) || isnan(stack[stptr]))
1263                         stack[stptr-1] = 0.0;
1264                     else
1265                         stack[stptr-1] = stack[stptr-1] > stack[stptr] ? 1.0 : 0.0;
1266                     stptr--;
1267                     break;
1268                 case OP_GE:
1269                     if(stptr<1){
1270                         rrd_set_error("RPN stack underflow");
1271                         free(stack);
1272                         return -1;
1273                     }
1274                     if (isnan(stack[stptr-1]) || isnan(stack[stptr]))
1275                         stack[stptr-1] = 0.0;
1276                     else
1277                         stack[stptr-1] = stack[stptr-1] >= stack[stptr] ? 1.0 : 0.0;
1278                     stptr--;
1279                     break;
1280                 case OP_EQ:
1281                     if(stptr<1){
1282                         rrd_set_error("RPN stack underflow");
1283                         free(stack);
1284                         return -1;
1285                     }
1286                     if (isnan(stack[stptr-1]) || isnan(stack[stptr]))
1287                         stack[stptr-1] = 0.0;
1288                     else
1289                         stack[stptr-1] = stack[stptr-1] == stack[stptr] ? 1.0 : 0.0;
1290                     stptr--;
1291                     break;
1292                 case OP_IF:
1293                     if(stptr<2){
1294                         rrd_set_error("RPN stack underflow");
1295                         free(stack);
1296                         return -1;
1297                     }
1298                     stack[stptr-2] = stack[stptr-2] != 0.0 ? stack[stptr-1] : stack[stptr];
1299                     stptr--;
1300                     stptr--;
1301                     break;
1302                 case OP_MIN:
1303                     if(stptr<1){
1304                         rrd_set_error("RPN stack underflow");
1305                         free(stack);
1306                         return -1;
1307                     }
1308                     if (isnan(stack[stptr-1])) 
1309                         ;
1310                     else if (isnan(stack[stptr]))
1311                         stack[stptr-1] = stack[stptr];
1312                     else if (stack[stptr-1] > stack[stptr])
1313                         stack[stptr-1] = stack[stptr];
1314                     stptr--;
1315                     break;
1316                 case OP_MAX:
1317                     if(stptr<1){
1318                         rrd_set_error("RPN stack underflow");
1319                         free(stack);
1320                         return -1;
1321                     }
1322                     if (isnan(stack[stptr-1])) 
1323                         ;
1324                     else if (isnan(stack[stptr]))
1325                         stack[stptr-1] = stack[stptr];
1326                     else if (stack[stptr-1] < stack[stptr])
1327                         stack[stptr-1] = stack[stptr];
1328                     stptr--;
1329                     break;
1330                 case OP_LIMIT:
1331                     if(stptr<2){
1332                         rrd_set_error("RPN stack underflow");
1333                         free(stack);
1334                         return -1;
1335                     }
1336                     if (isnan(stack[stptr-2])) 
1337                         ;
1338                     else if (isnan(stack[stptr-1]))
1339                         stack[stptr-2] = stack[stptr-1];
1340                     else if (isnan(stack[stptr]))
1341                         stack[stptr-2] = stack[stptr];
1342                     else if (stack[stptr-2] < stack[stptr-1])
1343                         stack[stptr-2] = DNAN;
1344                     else if (stack[stptr-2] > stack[stptr])
1345                         stack[stptr-2] = DNAN;
1346                     stptr-=2;
1347                     break;
1348                 case OP_UN:
1349                     if(stptr<0){
1350                         rrd_set_error("RPN stack underflow");
1351                         free(stack);
1352                         return -1;
1353                     }
1354                     stack[stptr] = isnan(stack[stptr]) ? 1.0 : 0.0;
1355                     break;
1356                 case OP_END:
1357                     break;
1358                 }
1359             }
1360             if(stptr!=0){
1361                 rrd_set_error("RPN final stack size != 1");
1362                 free(stack);
1363                 return -1;
1364             }
1365             im->gdes[gdi].data[++dataidx] = stack[0];
1366         }
1367     }
1368     free(stack);
1369     return 0;
1370 }
1371
1372 #undef dc_stacksize
1373
1374 /* massage data so, that we get one value for each x coordinate in the graph */
1375 int
1376 data_proc( image_desc_t *im ){
1377     long i,ii;
1378     double pixstep = (double)(im->end-im->start)
1379         /(double)im->xsize; /* how much time 
1380                                passes in one pixel */
1381     double paintval;
1382     double minval=DNAN,maxval=DNAN;
1383     
1384     unsigned long gr_time;    
1385
1386     /* memory for the processed data */
1387     for(i=0;i<im->gdes_c;i++){
1388       if((im->gdes[i].gf==GF_LINE1) ||
1389          (im->gdes[i].gf==GF_LINE2) ||
1390          (im->gdes[i].gf==GF_LINE3) ||
1391          (im->gdes[i].gf==GF_AREA) ||
1392          (im->gdes[i].gf==GF_STACK)){
1393         if((im->gdes[i].p_data = malloc((im->xsize +1)
1394                                         * sizeof(rrd_value_t)))==NULL){
1395           rrd_set_error("malloc data_proc");
1396           return -1;
1397         }
1398       }
1399     }
1400     
1401     for(i=0;i<im->xsize;i++){
1402         long vidx;
1403         gr_time = im->start+pixstep*i; /* time of the 
1404                                           current step */
1405         paintval=0.0;
1406         
1407         for(ii=0;ii<im->gdes_c;ii++){
1408           double value;
1409             switch(im->gdes[ii].gf){
1410             case GF_LINE1:
1411             case GF_LINE2:
1412             case GF_LINE3:
1413             case GF_AREA:
1414                 paintval = 0.0;
1415             case GF_STACK:
1416                 vidx = im->gdes[ii].vidx;
1417
1418                 value =
1419                     im->gdes[vidx].data[
1420                                         ((unsigned long)floor((double)
1421                                                              (gr_time - im->gdes[vidx].start ) 
1422                                                              / im->gdes[vidx].step)+1)                  
1423
1424                                         /* added one because data was not being aligned properly
1425                                            this fixes it. We may also be having a problem in fetch ... */
1426
1427                                         *im->gdes[vidx].ds_cnt
1428                                         +im->gdes[vidx].ds];
1429
1430                 if (! isnan(value)) {
1431                   paintval += value;
1432                   im->gdes[ii].p_data[i] = paintval;
1433                   if (finite(paintval)){
1434                    if (isnan(minval) || paintval <  minval)
1435                      minval = paintval;
1436                    if (isnan(maxval) || paintval >  maxval)
1437                      maxval = paintval;
1438                   }
1439                 } else {
1440                   im->gdes[ii].p_data[i] = DNAN;
1441                 }
1442                 break;
1443             case GF_PRINT:
1444             case GF_GPRINT:
1445             case GF_COMMENT:
1446             case GF_HRULE:
1447             case GF_VRULE:
1448             case GF_DEF:               
1449             case GF_CDEF:
1450                 break;
1451             }
1452         }
1453     }
1454
1455     /* if min or max have not been asigned a value this is because
1456        there was no data in the graph ... this is not good ...
1457        lets set these to dummy values then ... */
1458
1459     if (isnan(minval)) minval = 0.0;
1460     if (isnan(maxval)) maxval = 1.0;
1461     
1462     /* adjust min and max values */
1463     if (isnan(im->minval) 
1464         || ((!im->logarithmic && !im->rigid) /* don't adjust low-end with log scale */
1465             && im->minval > minval))
1466         im->minval = minval;
1467     if (isnan(im->maxval) 
1468         || (!im->rigid 
1469             && im->maxval < maxval)){
1470         if (im->logarithmic)
1471             im->maxval = maxval * 1.1;
1472         else
1473             im->maxval = maxval;
1474     }
1475     /* make sure min and max are not equal */
1476     if (im->minval == im->maxval) {
1477       im->maxval *= 1.01; 
1478       if (! im->logarithmic) {
1479         im->minval *= 0.99;
1480       }
1481       
1482       /* make sure min and max are not both zero */
1483       if (im->maxval == 0.0) {
1484             im->maxval = 1.0;
1485       }
1486         
1487     }
1488     return 0;
1489 }
1490
1491
1492
1493 /* identify the point where the first gridline, label ... gets placed */
1494
1495 time_t
1496 find_first_time(
1497     time_t   start, /* what is the initial time */
1498     enum tmt_en baseint,  /* what is the basic interval */
1499     long     basestep /* how many if these do we jump a time */
1500     )
1501 {
1502     struct tm tm;
1503     tm = *localtime(&start);
1504     switch(baseint){
1505     case TMT_SECOND:
1506         tm.tm_sec -= tm.tm_sec % basestep; break;
1507     case TMT_MINUTE: 
1508         tm.tm_sec=0;
1509         tm.tm_min -= tm.tm_min % basestep; 
1510         break;
1511     case TMT_HOUR:
1512         tm.tm_sec=0;
1513         tm.tm_min = 0;
1514         tm.tm_hour -= tm.tm_hour % basestep; break;
1515     case TMT_DAY:
1516         /* we do NOT look at the basestep for this ... */
1517         tm.tm_sec=0;
1518         tm.tm_min = 0;
1519         tm.tm_hour = 0; break;
1520     case TMT_WEEK:
1521         /* we do NOT look at the basestep for this ... */
1522         tm.tm_sec=0;
1523         tm.tm_min = 0;
1524         tm.tm_hour = 0;
1525         tm.tm_mday -= tm.tm_wday -1;    /* -1 because we want the monday */
1526         if (tm.tm_wday==0) tm.tm_mday -= 7; /* we want the *previous* monday */
1527         break;
1528     case TMT_MONTH:
1529         tm.tm_sec=0;
1530         tm.tm_min = 0;
1531         tm.tm_hour = 0;
1532         tm.tm_mday = 1;
1533         tm.tm_mon -= tm.tm_mon % basestep; break;
1534
1535     case TMT_YEAR:
1536         tm.tm_sec=0;
1537         tm.tm_min = 0;
1538         tm.tm_hour = 0;
1539         tm.tm_mday = 1;
1540         tm.tm_mon = 0;
1541         tm.tm_year -= (tm.tm_year+1900) % basestep;
1542         
1543     }
1544     return mktime(&tm);
1545 }
1546 /* identify the point where the next gridline, label ... gets placed */
1547 time_t 
1548 find_next_time(
1549     time_t   current, /* what is the initial time */
1550     enum tmt_en baseint,  /* what is the basic interval */
1551     long     basestep /* how many if these do we jump a time */
1552     )
1553 {
1554     struct tm tm;
1555     time_t madetime;
1556     tm = *localtime(&current);
1557     do {
1558         switch(baseint){
1559         case TMT_SECOND:
1560             tm.tm_sec += basestep; break;
1561         case TMT_MINUTE: 
1562             tm.tm_min += basestep; break;
1563         case TMT_HOUR:
1564             tm.tm_hour += basestep; break;
1565         case TMT_DAY:
1566             tm.tm_mday += basestep; break;
1567         case TMT_WEEK:
1568             tm.tm_mday += 7*basestep; break;
1569         case TMT_MONTH:
1570             tm.tm_mon += basestep; break;
1571         case TMT_YEAR:
1572             tm.tm_year += basestep;     
1573         }
1574         madetime = mktime(&tm);
1575     } while (madetime == -1); /* this is necessary to skip impssible times
1576                                  like the daylight saving time skips */
1577     return madetime;
1578           
1579 }
1580
1581 void gator( gdImagePtr gif, int x, int y){ 
1582
1583 /* this function puts the name of the author and the tool into the
1584    graph. Remove if you must, but please note, that it is here,
1585    because I would like people who look at rrdtool generated graphs to
1586    see what was used to do it. No obviously you can also add a credit
1587    line to your webpage or printed document, this is fine with me. But
1588    as I have no control over this, I added the little tag in here. 
1589 */
1590
1591 /* the fact that the text of what gets put into the graph is not
1592    visible in the function, has lead some to think this is for
1593    obfuscation reasons. While this is a nice side effect (I addmit),
1594    it is not the prime reason. The prime reason is, that the font
1595    used, is so small, that I had to hand edit the characters to ensure
1596    readability. I could thus not use the normal gd functions to write,
1597    but had to embed a slightly compressed bitmap version into the code. 
1598 */
1599
1600     int li[]={0,0,1, 0,4,5, 0,8,9, 0,12,14, 0,17,17, 0,21,21, 
1601               0,24,24, 0,34,34, 0,40,42, 0,45,45, 0,48,49, 0,52,54, 
1602               0,61,61, 0,64,66, 0,68,70, 0,72,74, 0,76,76, 0,78,78, 
1603               0,80,82, 0,84,85, 
1604               1,0,0, 1,2,2, 1,4,4, 1,6,6, 1,8,8, 1,10,10, 
1605               1,13,13, 1,16,16, 1,18,18, 1,20,20, 1,22,22, 1,24,24, 
1606               1,34,34, 1,41,41, 1,44,44, 1,46,46, 1,48,48, 1,50,50, 
1607               1,53,53, 1,60,60, 1,62,62, 1,64,64, 1,69,69, 1,73,73, 
1608               1,76,76, 1,78,78, 1,80,80, 1,84,84, 1,86,86, 
1609               2,0,1, 2,4,5, 2,8,8, 2,10,10, 2,13,13, 2,16,16, 
1610               2,18,18, 2,20,20, 2,22,22, 2,24,24, 2,33,33, 2,41,41, 
1611               2,44,44, 2,46,46, 2,48,49, 2,53,53, 2,60,60, 2,62,62, 
1612               2,64,65, 2,69,69, 2,73,73, 2,76,77, 2,80,81, 2,84,85,           
1613               3,0,0, 3,2,2, 3,4,4, 3,6,6, 3,8,8, 3,10,10, 
1614               3,13,13, 3,16,16, 3,18,18, 3,20,20, 3,22,22, 3,24,24, 
1615               3,32,32, 3,41,41, 3,44,44, 3,46,46, 3,48,48, 3,50,50, 
1616               3,53,53, 3,60,60, 3,62,62, 3,64,64, 3,69,69, 3,73,73, 
1617               3,76,76, 3,78,78, 3,80,80, 3,84,84, 3,86,86, 
1618               4,0,0, 4,2,2, 4,4,4, 4,6,6, 4,8,9, 4,13,13, 
1619               4,17,17, 4,21,21, 4,24,26, 4,32,32, 4,41,41, 4,45,45, 
1620               4,48,49, 4,52,54, 4,61,61, 4,64,66, 4,69,69, 4,72,74, 
1621               4,76,76, 4,78,78, 4,80,82, 4,84,84}; 
1622     int i,ii; 
1623     for(i=0; i<DIM(li); i=i+3)
1624         for(ii=y+li[i+1]; ii<=y+li[i+2];ii++)
1625           gdImageSetPixel(gif,x-li[i],ii,graph_col[GRC_GRID].i); 
1626 }
1627
1628
1629 /* calculate values required for PRINT and GPRINT functions */
1630
1631 int
1632 print_calc(image_desc_t *im, char ***prdata) 
1633 {
1634     long i,ii,validsteps;
1635     double printval;
1636     int graphelement = 0;
1637     long vidx;
1638     int max_ii; 
1639     double magfact = -1;
1640     char *si_symb = "";
1641     char *percent_s;
1642     int prlines = 1;
1643     if (im->imginfo) prlines++;
1644     for(i=0;i<im->gdes_c;i++){
1645         switch(im->gdes[i].gf){
1646         case GF_PRINT:
1647             prlines++;
1648             if(((*prdata) = rrd_realloc((*prdata),prlines*sizeof(char *)))==NULL){
1649                 rrd_set_error("realloc prdata");
1650                 return 0;
1651             }
1652         case GF_GPRINT:
1653             vidx = im->gdes[i].vidx;
1654             max_ii =((im->gdes[vidx].end 
1655                       - im->gdes[vidx].start)
1656                      /im->gdes[vidx].step
1657                      *im->gdes[vidx].ds_cnt);
1658             printval = DNAN;
1659             validsteps = 0;
1660             for(ii=im->gdes[vidx].ds+im->gdes[vidx].ds_cnt;
1661                 ii < max_ii+im->gdes[vidx].ds_cnt;
1662                 ii+=im->gdes[vidx].ds_cnt){
1663                  if (! finite(im->gdes[vidx].data[ii]))
1664                      continue;
1665                  if (isnan(printval)){
1666                      printval = im->gdes[vidx].data[ii];
1667                      validsteps++;
1668                     continue;
1669                 }
1670
1671                 switch (im->gdes[i].cf){
1672                 case CF_AVERAGE:
1673                     validsteps++;
1674                     printval += im->gdes[vidx].data[ii];
1675                     break;
1676                 case CF_MINIMUM:
1677                     printval = min( printval, im->gdes[vidx].data[ii]);
1678                     break;
1679                 case CF_MAXIMUM:
1680                     printval = max( printval, im->gdes[vidx].data[ii]);
1681                     break;
1682                 case CF_LAST:
1683                     printval = im->gdes[vidx].data[ii];
1684                 }
1685             }
1686             if (im->gdes[i].cf ==  CF_AVERAGE) {
1687                 if (validsteps > 1) {
1688                     printval = (printval / validsteps);
1689                 }
1690             }
1691             if ((percent_s = strstr(im->gdes[i].format,"%S")) != NULL) {
1692                 /* Magfact is set to -1 upon entry to print_calc.  If it
1693                  * is still less than 0, then we need to run auto_scale.
1694                  * Otherwise, put the value into the correct units.  If
1695                  * the value is 0, then do not set the symbol or magnification
1696                  * so next the calculation will be performed again. */
1697                 if (magfact < 0.0) {
1698                     auto_scale(im,&printval,&si_symb,&magfact);
1699                     if (printval == 0.0)
1700                         magfact = -1.0;
1701                 } else {
1702                     printval /= magfact;
1703                 }
1704                 *(++percent_s) = 's';
1705             }
1706             else if (strstr(im->gdes[i].format,"%s") != NULL) {
1707                 auto_scale(im,&printval,&si_symb,&magfact);
1708             }
1709             if (im->gdes[i].gf == GF_PRINT){
1710                 (*prdata)[prlines-2] = malloc((FMT_LEG_LEN+2)*sizeof(char));
1711                 if (bad_format(im->gdes[i].format)) {
1712                         rrd_set_error("bad format for [G]PRINT in '%s'", im->gdes[i].format);
1713                         return -1;
1714                 }
1715 #ifdef HAVE_SNPRINTF
1716                 snprintf((*prdata)[prlines-2],FMT_LEG_LEN,im->gdes[i].format,printval,si_symb);
1717 #else
1718                 sprintf((*prdata)[prlines-2],im->gdes[i].format,printval,si_symb);
1719 #endif
1720                 (*prdata)[prlines-1] = NULL;
1721             } else {
1722                 /* GF_GPRINT */
1723
1724                 if (bad_format(im->gdes[i].format)) {
1725                         rrd_set_error("bad format for [G]PRINT in '%s'", im->gdes[i].format);
1726                         return -1;
1727                 }
1728 #ifdef HAVE_SNPRINTF
1729                 snprintf(im->gdes[i].legend,FMT_LEG_LEN-2,im->gdes[i].format,printval,si_symb);
1730 #else
1731                 sprintf(im->gdes[i].legend,im->gdes[i].format,printval,si_symb);
1732 #endif
1733                 graphelement = 1;
1734             }
1735             break;
1736         case GF_COMMENT:
1737         case GF_LINE1:
1738         case GF_LINE2:
1739         case GF_LINE3:
1740         case GF_AREA:
1741         case GF_STACK:
1742         case GF_HRULE:
1743         case GF_VRULE:
1744             graphelement = 1;
1745             break;
1746         case GF_DEF:
1747         case GF_CDEF:       
1748             break;
1749         }
1750     }
1751     return graphelement;
1752 }
1753
1754
1755 /* place legends with color spots */
1756 int
1757 leg_place(image_desc_t *im)
1758 {
1759     /* graph labels */
1760     int   interleg = SmallFont->w*2;
1761     int   box = SmallFont->h*1.2;
1762     int   border = SmallFont->w*2;
1763     int   fill=0, fill_last;
1764     int   leg_c = 0;
1765     int   leg_x = border, leg_y = im->ygif;
1766     int   leg_cc;
1767     int   glue = 0;
1768     int   i,ii, mark = 0;
1769     char  prt_fctn; /*special printfunctions */
1770     int  *legspace;
1771
1772   if( !(im->extra_flags & NOLEGEND) ) {
1773     if ((legspace = malloc(im->gdes_c*sizeof(int)))==NULL){
1774        rrd_set_error("malloc for legspace");
1775        return -1;
1776     }
1777
1778     for(i=0;i<im->gdes_c;i++){
1779         fill_last = fill;
1780
1781         leg_cc = strlen(im->gdes[i].legend);
1782         
1783         /* is there a controle code ant the end of the legend string ? */ 
1784         if (leg_cc >= 2 && im->gdes[i].legend[leg_cc-2] == '\\') {
1785             prt_fctn = im->gdes[i].legend[leg_cc-1];
1786             leg_cc -= 2;
1787             im->gdes[i].legend[leg_cc] = '\0';
1788         } else {
1789             prt_fctn = '\0';
1790         }
1791         /* remove exess space */
1792         while (prt_fctn=='g' && 
1793                leg_cc > 0 && 
1794                im->gdes[i].legend[leg_cc-1]==' '){
1795            leg_cc--;
1796            im->gdes[i].legend[leg_cc]='\0';
1797         }
1798         if (leg_cc != 0 ){          
1799            legspace[i]=(prt_fctn=='g' ? 0 : interleg);
1800            
1801            if (fill > 0){ 
1802                /* no interleg space if string ends in \g */
1803                fill += legspace[i];
1804             }
1805             if (im->gdes[i].gf != GF_GPRINT && 
1806                 im->gdes[i].gf != GF_COMMENT) { 
1807                 fill += box;       
1808             }
1809             fill += leg_cc * SmallFont->w;
1810             leg_c++;
1811         } else {
1812            legspace[i]=0;
1813         }
1814         /* who said there was a special tag ... ?*/
1815         if (prt_fctn=='g') {    
1816            prt_fctn = '\0';
1817         }
1818         if (prt_fctn == '\0') {
1819             if (i == im->gdes_c -1 ) prt_fctn ='l';
1820             
1821             /* is it time to place the legends ? */
1822             if (fill > im->xgif - 2*border){
1823                 if (leg_c > 1) {
1824                     /* go back one */
1825                     i--; 
1826                     fill = fill_last;
1827                     leg_c--;
1828                     prt_fctn = 'j';
1829                 } else {
1830                     prt_fctn = 'l';
1831                 }
1832                 
1833             }
1834         }
1835
1836
1837         if (prt_fctn != '\0'){
1838             leg_x = border;
1839             if (leg_c >= 2 && prt_fctn == 'j') {
1840                 glue = (im->xgif - fill - 2* border) / (leg_c-1);
1841                 /* if (glue > 2 * SmallFont->w) glue = 0; */
1842             } else {
1843                 glue = 0;
1844             }
1845             if (prt_fctn =='c') leg_x =  (im->xgif - fill) / 2.0;
1846             if (prt_fctn =='r') leg_x =  im->xgif - fill - border;
1847
1848             for(ii=mark;ii<=i;ii++){
1849                 if(im->gdes[ii].legend[0]=='\0')
1850                     continue;
1851                 im->gdes[ii].legloc.x = leg_x;
1852                 im->gdes[ii].legloc.y = leg_y;
1853                 leg_x =  leg_x 
1854                     + strlen(im->gdes[ii].legend)*SmallFont->w 
1855                     + legspace[ii]
1856                     + glue;
1857                 if (im->gdes[ii].gf != GF_GPRINT && 
1858                     im->gdes[ii].gf != GF_COMMENT) 
1859                     leg_x += box;          
1860             }       
1861             leg_y = leg_y + SmallFont->h*1.2;
1862             if (prt_fctn == 's') leg_y -= SmallFont->h *0.5;
1863             fill = 0;
1864             leg_c = 0;
1865             mark = ii;
1866         }          
1867     }
1868     im->ygif = leg_y+6;
1869     free(legspace);
1870   }
1871   return 0;
1872 }
1873
1874 /* create a grid on the graph. it determines what to do
1875    from the values of xsize, start and end */
1876
1877 /* the xaxis labels are determined from the number of seconds per pixel
1878    in the requested graph */
1879
1880
1881
1882 int
1883 horizontal_grid(gdImagePtr gif, image_desc_t   *im)
1884 {
1885     double   range;
1886     double   scaledrange;
1887     int      pixel,i;
1888     int      sgrid,egrid;
1889     double   gridstep;
1890     double   scaledstep;
1891     char     graph_label[100];
1892     gdPoint  polyPoints[4];
1893     int      labfact,gridind;
1894     int      styleMinor[2],styleMajor[2];
1895     int      decimals, fractionals;
1896     char     labfmt[64];
1897
1898     labfact=2;
1899     gridind=-1;
1900     range =  im->maxval - im->minval;
1901     scaledrange = range / im->magfact;
1902
1903         /* does the scale of this graph make it impossible to put lines
1904            on it? If so, give up. */
1905         if (isnan(scaledrange)) {
1906                 return 0;
1907         }
1908
1909     styleMinor[0] = graph_col[GRC_GRID].i;
1910     styleMinor[1] = gdTransparent;
1911
1912     styleMajor[0] = graph_col[GRC_MGRID].i;
1913     styleMajor[1] = gdTransparent;
1914
1915     /* find grid spaceing */
1916     pixel=1;
1917     if(isnan(im->ygridstep)){
1918         if(im->extra_flags & ALTYGRID) {
1919             /* find the value with max number of digits. Get number of digits */
1920             decimals = ceil(log10(max(fabs(im->maxval), fabs(im->minval))));
1921             if(decimals <= 0) /* everything is small. make place for zero */
1922                 decimals = 1;
1923             
1924             fractionals = floor(log10(range));
1925             if(fractionals < 0) /* small amplitude. */
1926                 sprintf(labfmt, "%%%d.%df", decimals - fractionals + 1, -fractionals + 1);
1927             else
1928                 sprintf(labfmt, "%%%d.1f", decimals + 1);
1929             gridstep = pow((double)10, (double)fractionals);
1930             if(gridstep == 0) /* range is one -> 0.1 is reasonable scale */
1931                 gridstep = 0.1;
1932             /* should have at least 5 lines but no more then 15 */
1933             if(range/gridstep < 5)
1934                 gridstep /= 10;
1935             if(range/gridstep > 15)
1936                 gridstep *= 10;
1937             if(range/gridstep > 5) {
1938                 labfact = 1;
1939                 if(range/gridstep > 8)
1940                     labfact = 2;
1941             }
1942             else {
1943                 gridstep /= 5;
1944                 labfact = 5;
1945             }
1946         }
1947         else {
1948             for(i=0;ylab[i].grid > 0;i++){
1949                 pixel = im->ysize / (scaledrange / ylab[i].grid);
1950                 if (gridind == -1 && pixel > 5) {
1951                     gridind = i;
1952                     break;
1953                 }
1954             }
1955             
1956             for(i=0; i<4;i++) {
1957                 if (pixel * ylab[gridind].lfac[i] >=  2 * SmallFont->h) {
1958                     labfact =  ylab[gridind].lfac[i];
1959                     break;
1960                 }                         
1961             } 
1962             
1963             gridstep = ylab[gridind].grid * im->magfact;
1964         }
1965     } else {
1966         gridstep = im->ygridstep;
1967         labfact = im->ylabfact;
1968     }
1969     
1970     polyPoints[0].x=im->xorigin;
1971     polyPoints[1].x=im->xorigin+im->xsize;
1972     sgrid = (int)( im->minval / gridstep - 1);
1973     egrid = (int)( im->maxval / gridstep + 1);
1974     scaledstep = gridstep/im->magfact;
1975     for (i = sgrid; i <= egrid; i++){
1976         polyPoints[0].y=ytr(im,gridstep*i);
1977         if ( polyPoints[0].y >= im->yorigin-im->ysize
1978              && polyPoints[0].y <= im->yorigin) {
1979             if(i % labfact == 0){               
1980                 if (i==0 || im->symbol == ' ') {
1981                     if(scaledstep < 1){
1982                         if(im->extra_flags & ALTYGRID) {
1983                             sprintf(graph_label,labfmt,scaledstep*i);
1984                         }
1985                         else {
1986                             sprintf(graph_label,"%4.1f",scaledstep*i);
1987                         }
1988                     } else {
1989                         sprintf(graph_label,"%4.0f",scaledstep*i);
1990                     }
1991                 }else {
1992                     if(scaledstep < 1){
1993                         sprintf(graph_label,"%4.1f %c",scaledstep*i, im->symbol);
1994                     } else {
1995                         sprintf(graph_label,"%4.0f %c",scaledstep*i, im->symbol);
1996                     }
1997                 }
1998
1999                 gdImageString(gif, SmallFont,
2000                               (polyPoints[0].x - (strlen(graph_label) * 
2001                                                   SmallFont->w)-7), 
2002                               polyPoints[0].y - SmallFont->h/2+1,
2003                               (unsigned char *)graph_label, graph_col[GRC_FONT].i);
2004                 
2005                 gdImageSetStyle(gif, styleMajor, 2);
2006
2007                 gdImageLine(gif, polyPoints[0].x-2,polyPoints[0].y,
2008                             polyPoints[0].x+2,polyPoints[0].y,graph_col[GRC_MGRID].i);
2009                 gdImageLine(gif, polyPoints[1].x-2,polyPoints[0].y,
2010                             polyPoints[1].x+2,polyPoints[0].y,graph_col[GRC_MGRID].i);              
2011             } else {            
2012                 gdImageSetStyle(gif, styleMinor, 2);
2013                 gdImageLine(gif, polyPoints[0].x-1,polyPoints[0].y,
2014                             polyPoints[0].x+1,polyPoints[0].y,graph_col[GRC_GRID].i);
2015                 gdImageLine(gif, polyPoints[1].x-1,polyPoints[0].y,
2016                             polyPoints[1].x+1,polyPoints[0].y,graph_col[GRC_GRID].i);               
2017             }       
2018             gdImageLine(gif, polyPoints[0].x,polyPoints[0].y,
2019                         polyPoints[1].x,polyPoints[0].y,gdStyled);
2020         }       
2021     } 
2022 /*    if(im->minval * im->maxval < 0){
2023       polyPoints[0].y=ytr(0);
2024       gdImageLine(gif, polyPoints[0].x,polyPoints[0].y,
2025       polyPoints[1].x,polyPoints[0].y,graph_col[GRC_MGRID].i);
2026       } */
2027
2028     return 1;
2029 }
2030
2031 /* logaritmic horizontal grid */
2032 int
2033 horizontal_log_grid(gdImagePtr gif, image_desc_t   *im)
2034 {
2035     double   pixpex;
2036     int      ii,i;
2037     int      minoridx=0, majoridx=0;
2038     char     graph_label[100];
2039     gdPoint  polyPoints[4];
2040     int      styleMinor[2],styleMajor[2];
2041     double   value, pixperstep, minstep;
2042
2043     /* find grid spaceing */
2044     pixpex= (double)im->ysize / (log10(im->maxval) - log10(im->minval));
2045
2046         if (isnan(pixpex)) {
2047                 return 0;
2048         }
2049
2050     for(i=0;yloglab[i][0] > 0;i++){
2051         minstep = log10(yloglab[i][0]);
2052         for(ii=1;yloglab[i][ii+1] > 0;ii++){
2053             if(yloglab[i][ii+2]==0){
2054                 minstep = log10(yloglab[i][ii+1])-log10(yloglab[i][ii]);
2055                 break;
2056             }
2057         }
2058         pixperstep = pixpex * minstep;
2059         if(pixperstep > 5){minoridx = i;}
2060         if(pixperstep > 2 * SmallFont->h){majoridx = i;}
2061     }
2062    
2063     styleMinor[0] = graph_col[GRC_GRID].i;
2064     styleMinor[1] = gdTransparent;
2065
2066     styleMajor[0] = graph_col[GRC_MGRID].i;
2067     styleMajor[1] = gdTransparent;
2068
2069     polyPoints[0].x=im->xorigin;
2070     polyPoints[1].x=im->xorigin+im->xsize;
2071     /* paint minor grid */
2072     for (value = pow((double)10, log10(im->minval) 
2073                           - fmod(log10(im->minval),log10(yloglab[minoridx][0])));
2074          value  <= im->maxval;
2075          value *= yloglab[minoridx][0]){
2076         if (value < im->minval) continue;
2077         i=0;    
2078         while(yloglab[minoridx][++i] > 0){          
2079             polyPoints[0].y = ytr(im,value * yloglab[minoridx][i]);
2080             if (polyPoints[0].y <= im->yorigin - im->ysize) break;
2081             gdImageSetStyle(gif, styleMinor, 2);
2082             gdImageLine(gif, polyPoints[0].x-1,polyPoints[0].y,
2083                         polyPoints[0].x+1,polyPoints[0].y,graph_col[GRC_GRID].i);
2084             gdImageLine(gif, polyPoints[1].x-1,polyPoints[0].y,
2085                         polyPoints[1].x+1,polyPoints[0].y,graph_col[GRC_GRID].i);           
2086
2087             gdImageLine(gif, polyPoints[0].x,polyPoints[0].y,
2088                         polyPoints[1].x,polyPoints[0].y,gdStyled);
2089         }
2090     }
2091
2092     /* paint major grid and labels*/
2093     for (value = pow((double)10, log10(im->minval) 
2094                           - fmod(log10(im->minval),log10(yloglab[majoridx][0])));
2095          value <= im->maxval;
2096          value *= yloglab[majoridx][0]){
2097         if (value < im->minval) continue;
2098         i=0;    
2099         while(yloglab[majoridx][++i] > 0){          
2100             polyPoints[0].y = ytr(im,value * yloglab[majoridx][i]);         
2101             if (polyPoints[0].y <= im->yorigin - im->ysize) break;
2102             gdImageSetStyle(gif, styleMajor, 2);
2103             gdImageLine(gif, polyPoints[0].x-2,polyPoints[0].y,
2104                         polyPoints[0].x+2,polyPoints[0].y,graph_col[GRC_MGRID].i);
2105             gdImageLine(gif, polyPoints[1].x-2,polyPoints[0].y,
2106                         polyPoints[1].x+2,polyPoints[0].y,graph_col[GRC_MGRID].i);                  
2107             
2108             gdImageLine(gif, polyPoints[0].x,polyPoints[0].y,
2109                         polyPoints[1].x,polyPoints[0].y,gdStyled);
2110             sprintf(graph_label,"%3.0e",value * yloglab[majoridx][i]);
2111             gdImageString(gif, SmallFont,
2112                           (polyPoints[0].x - (strlen(graph_label) * 
2113                                               SmallFont->w)-7), 
2114                           polyPoints[0].y - SmallFont->h/2+1,
2115                           (unsigned char *)graph_label, graph_col[GRC_FONT].i); 
2116         } 
2117     }
2118         return 1;
2119 }
2120
2121
2122 void
2123 vertical_grid(
2124     gdImagePtr     gif,
2125     image_desc_t   *im )
2126 {   
2127     int xlab_sel;               /* which sort of label and grid ? */
2128     time_t ti, tilab;
2129     long factor;
2130     char graph_label[100];
2131     gdPoint polyPoints[4];       /* points for filled graph and more*/
2132
2133     /* style for grid lines */
2134     int     styleDotted[4];
2135
2136     
2137     /* the type of time grid is determined by finding
2138        the number of seconds per pixel in the graph */
2139     
2140     
2141     if(im->xlab_user.minsec == -1){
2142         factor=(im->end - im->start)/im->xsize;
2143         xlab_sel=0;
2144         while ( xlab[xlab_sel+1].minsec != -1 
2145                 && xlab[xlab_sel+1].minsec <= factor){ xlab_sel++; }
2146         im->xlab_user.gridtm = xlab[xlab_sel].gridtm;
2147         im->xlab_user.gridst = xlab[xlab_sel].gridst;
2148         im->xlab_user.mgridtm = xlab[xlab_sel].mgridtm;
2149         im->xlab_user.mgridst = xlab[xlab_sel].mgridst;
2150         im->xlab_user.labtm = xlab[xlab_sel].labtm;
2151         im->xlab_user.labst = xlab[xlab_sel].labst;
2152         im->xlab_user.precis = xlab[xlab_sel].precis;
2153         im->xlab_user.stst = xlab[xlab_sel].stst;
2154     }
2155     
2156     /* y coords are the same for every line ... */
2157     polyPoints[0].y = im->yorigin;
2158     polyPoints[1].y = im->yorigin-im->ysize;
2159
2160     /* paint the minor grid */
2161     for(ti = find_first_time(im->start,
2162                             im->xlab_user.gridtm,
2163                             im->xlab_user.gridst);
2164         ti < im->end; 
2165         ti = find_next_time(ti,im->xlab_user.gridtm,im->xlab_user.gridst)
2166         ){
2167         /* are we inside the graph ? */
2168         if (ti < im->start || ti > im->end) continue;
2169         polyPoints[0].x = xtr(im,ti);
2170         styleDotted[0] = graph_col[GRC_GRID].i;
2171         styleDotted[1] = gdTransparent;
2172
2173         gdImageSetStyle(gif, styleDotted, 2);
2174
2175         gdImageLine(gif, polyPoints[0].x,polyPoints[0].y,
2176                     polyPoints[0].x,polyPoints[1].y,gdStyled);
2177         gdImageLine(gif, polyPoints[0].x,polyPoints[0].y-1,
2178                     polyPoints[0].x,polyPoints[0].y+1,graph_col[GRC_GRID].i);
2179         gdImageLine(gif, polyPoints[0].x,polyPoints[1].y-1,
2180                     polyPoints[0].x,polyPoints[1].y+1,graph_col[GRC_GRID].i);
2181     }
2182
2183     /* paint the major grid */
2184     for(ti = find_first_time(im->start,
2185                             im->xlab_user.mgridtm,
2186                             im->xlab_user.mgridst);
2187         ti < im->end; 
2188         ti = find_next_time(ti,im->xlab_user.mgridtm,im->xlab_user.mgridst)
2189         ){
2190         /* are we inside the graph ? */
2191         if (ti < im->start || ti > im->end) continue;
2192         polyPoints[0].x = xtr(im,ti);
2193         styleDotted[0] = graph_col[GRC_MGRID].i;
2194         styleDotted[1] = gdTransparent;
2195         gdImageSetStyle(gif, styleDotted, 2);
2196
2197         gdImageLine(gif, polyPoints[0].x,polyPoints[0].y,
2198                     polyPoints[0].x,polyPoints[1].y,gdStyled);
2199         gdImageLine(gif, polyPoints[0].x,polyPoints[0].y-2,
2200                     polyPoints[0].x,polyPoints[0].y+2,graph_col[GRC_MGRID].i);
2201         gdImageLine(gif, polyPoints[0].x,polyPoints[1].y-2,
2202                     polyPoints[0].x,polyPoints[1].y+2,graph_col[GRC_MGRID].i);
2203     }
2204     /* paint the labels below the graph */
2205     for(ti = find_first_time(im->start,
2206                             im->xlab_user.labtm,
2207                             im->xlab_user.labst);
2208         ti <= im->end; 
2209         ti = find_next_time(ti,im->xlab_user.labtm,im->xlab_user.labst)
2210         ){
2211         int gr_pos,width;
2212         tilab= ti + im->xlab_user.precis/2; /* correct time for the label */
2213
2214 #if HAVE_STRFTIME
2215         strftime(graph_label,99,im->xlab_user.stst,localtime(&tilab));
2216 #else
2217 # error "your libc has no strftime I guess we'll abort the exercise here."
2218 #endif
2219         width=strlen(graph_label) *  SmallFont->w;
2220         gr_pos=xtr(im,tilab) - width/2;
2221         if (gr_pos  >= im->xorigin 
2222             && gr_pos + width <= im->xorigin+im->xsize) 
2223             gdImageString(gif, SmallFont,
2224                           gr_pos,  polyPoints[0].y+4,
2225                           (unsigned char *)graph_label, graph_col[GRC_FONT].i);
2226     }
2227
2228 }
2229
2230
2231 void 
2232 axis_paint(
2233     image_desc_t   *im,
2234     gdImagePtr     gif
2235     )
2236 {   
2237     /* draw x and y axis */
2238     gdImageLine(gif, im->xorigin+im->xsize,im->yorigin,
2239                 im->xorigin+im->xsize,im->yorigin-im->ysize,
2240                 graph_col[GRC_GRID].i);
2241     
2242     gdImageLine(gif, im->xorigin,im->yorigin-im->ysize,
2243                 im->xorigin+im->xsize,im->yorigin-im->ysize,
2244                 graph_col[GRC_GRID].i);
2245
2246     gdImageLine(gif, im->xorigin-4,im->yorigin,
2247                 im->xorigin+im->xsize+4,im->yorigin,
2248                 graph_col[GRC_FONT].i);
2249
2250     gdImageLine(gif, im->xorigin,im->yorigin,
2251                 im->xorigin,im->yorigin-im->ysize,
2252                 graph_col[GRC_GRID].i);
2253     
2254     /* arrow for X axis direction */
2255     gdImageLine(gif, im->xorigin+im->xsize+4, im->yorigin-3, im->xorigin+im->xsize+4, im->yorigin+3,graph_col[GRC_ARROW].i);
2256     gdImageLine(gif, im->xorigin+im->xsize+4, im->yorigin-3, im->xorigin+im->xsize+9, im->yorigin,graph_col[GRC_ARROW].i);
2257     gdImageLine(gif, im->xorigin+im->xsize+4, im->yorigin+3, im->xorigin+im->xsize+9, im->yorigin,graph_col[GRC_ARROW].i);
2258
2259     /*    gdImageLine(gif, im->xorigin+im->xsize-1, im->yorigin-3, im->xorigin+im->xsize-1, im->yorigin+3,graph_col[GRC_MGRID].i);
2260     gdImageLine(gif, im->xorigin+im->xsize, im->yorigin-2, im->xorigin+im->xsize, im->yorigin+2,graph_col[GRC_MGRID].i);
2261     gdImageLine(gif, im->xorigin+im->xsize+1, im->yorigin-2, im->xorigin+im->xsize+1, im->yorigin+2,graph_col[GRC_MGRID].i);
2262     gdImageLine(gif, im->xorigin+im->xsize+2, im->yorigin-2, im->xorigin+im->xsize+2, im->yorigin+2,graph_col[GRC_MGRID].i);
2263     gdImageLine(gif, im->xorigin+im->xsize+3, im->yorigin-1, im->xorigin+im->xsize+3, im->yorigin+1,graph_col[GRC_MGRID].i);
2264     gdImageLine(gif, im->xorigin+im->xsize+4, im->yorigin-1, im->xorigin+im->xsize+4, im->yorigin+1,graph_col[GRC_MGRID].i);
2265     gdImageLine(gif, im->xorigin+im->xsize+5, im->yorigin, im->xorigin+im->xsize+5, im->yorigin,graph_col[GRC_MGRID].i); */
2266
2267
2268
2269 }
2270
2271 void
2272 grid_paint(
2273     image_desc_t   *im,
2274     gdImagePtr     gif
2275     )
2276 {   
2277     long i;
2278     int boxH=8, boxV=8;
2279     int res=0;
2280     gdPoint polyPoints[4];       /* points for filled graph and more*/
2281
2282     /* draw 3d border */
2283     gdImageLine(gif,0,0,im->xgif-1,0,graph_col[GRC_SHADEA].i);
2284     gdImageLine(gif,1,1,im->xgif-2,1,graph_col[GRC_SHADEA].i);
2285     gdImageLine(gif,0,0,0,im->ygif-1,graph_col[GRC_SHADEA].i);
2286     gdImageLine(gif,1,1,1,im->ygif-2,graph_col[GRC_SHADEA].i);
2287     gdImageLine(gif,im->xgif-1,0,im->xgif-1,im->ygif-1,graph_col[GRC_SHADEB].i);
2288     gdImageLine(gif,0,im->ygif-1,im->xgif-1,im->ygif-1,graph_col[GRC_SHADEB].i);
2289     gdImageLine(gif,im->xgif-2,1,im->xgif-2,im->ygif-2,graph_col[GRC_SHADEB].i);
2290     gdImageLine(gif,1,im->ygif-2,im->xgif-2,im->ygif-2,graph_col[GRC_SHADEB].i);
2291
2292
2293     if (im->draw_x_grid == 1 )
2294       vertical_grid(gif, im);
2295     
2296     if (im->draw_y_grid == 1){
2297         if(im->logarithmic){
2298                 res = horizontal_log_grid(gif,im);
2299         } else {
2300                 res = horizontal_grid(gif,im);
2301         }
2302
2303         /* dont draw horizontal grid if there is no min and max val */
2304         if (! res ) {
2305           char *nodata = "No Data found";
2306           gdImageString(gif, LargeFont,
2307                         im->xgif/2 
2308                         - (strlen(nodata)*LargeFont->w)/2,
2309                         (2*im->yorigin-im->ysize) / 2,
2310                         (unsigned char *)nodata, graph_col[GRC_FONT].i);
2311         }
2312     }
2313
2314     /* yaxis description */
2315     gdImageStringUp(gif, SmallFont,
2316                     7,
2317                     (im->yorigin - im->ysize/2
2318                      +(strlen(im->ylegend)*SmallFont->w)/2 ),
2319                     (unsigned char *)im->ylegend, graph_col[GRC_FONT].i);
2320     
2321
2322     /* graph title */
2323     gdImageString(gif, LargeFont,
2324                     im->xgif/2 
2325                     - (strlen(im->title)*LargeFont->w)/2,
2326                   8,
2327                     (unsigned char *)im->title, graph_col[GRC_FONT].i);
2328     
2329     /* graph labels */
2330     if( !(im->extra_flags & NOLEGEND) ) {
2331       for(i=0;i<im->gdes_c;i++){
2332         if(im->gdes[i].legend[0] =='\0')
2333             continue;
2334         
2335         if(im->gdes[i].gf != GF_GPRINT && im->gdes[i].gf != GF_COMMENT){
2336             
2337             polyPoints[0].x = im->gdes[i].legloc.x;
2338             polyPoints[0].y = im->gdes[i].legloc.y+1;
2339             polyPoints[1].x = polyPoints[0].x+boxH;
2340             polyPoints[2].x = polyPoints[0].x+boxH;
2341             polyPoints[3].x = polyPoints[0].x;
2342             polyPoints[1].y = polyPoints[0].y;
2343             polyPoints[2].y = polyPoints[0].y+boxV;
2344             polyPoints[3].y = polyPoints[0].y+boxV;
2345             gdImageFilledPolygon(gif,polyPoints,4,im->gdes[i].col.i);
2346             gdImagePolygon(gif,polyPoints,4,graph_col[GRC_FRAME].i);
2347         
2348             gdImageString(gif, SmallFont,
2349                           polyPoints[0].x+boxH+6, 
2350                           polyPoints[0].y-1,
2351                           (unsigned char *)im->gdes[i].legend,
2352                           graph_col[GRC_FONT].i);
2353         } else {
2354             polyPoints[0].x = im->gdes[i].legloc.x;
2355             polyPoints[0].y = im->gdes[i].legloc.y;
2356             
2357             gdImageString(gif, SmallFont,
2358                           polyPoints[0].x, 
2359                           polyPoints[0].y,
2360                           (unsigned char *)im->gdes[i].legend,
2361                           graph_col[GRC_FONT].i);
2362         }
2363       }
2364     }
2365     
2366     
2367     gator(gif, (int) im->xgif-5, 5);
2368
2369 }
2370
2371
2372 gdImagePtr
2373 MkLineBrush(image_desc_t *im,long cosel, enum gf_en typsel){
2374   gdImagePtr brush;
2375   int pen;
2376   switch (typsel){
2377   case GF_LINE1:
2378     brush=gdImageCreate(1,1);
2379     break;
2380   case GF_LINE2:
2381     brush=gdImageCreate(2,2);
2382     break;
2383   case GF_LINE3:
2384     brush=gdImageCreate(3,3);
2385     break;
2386   default:
2387     return NULL;
2388   }
2389
2390   gdImageColorTransparent(brush, 
2391                           gdImageColorAllocate(brush, 0, 0, 0));
2392
2393   pen = gdImageColorAllocate(brush, 
2394                              im->gdes[cosel].col.red,
2395                              im->gdes[cosel].col.green,
2396                              im->gdes[cosel].col.blue);
2397     
2398   switch (typsel){
2399   case GF_LINE1:
2400     gdImageSetPixel(brush,0,0,pen);
2401     break;
2402   case GF_LINE2:
2403     gdImageSetPixel(brush,0,0,pen);
2404     gdImageSetPixel(brush,0,1,pen);
2405     gdImageSetPixel(brush,1,0,pen);
2406     gdImageSetPixel(brush,1,1,pen);
2407     break;
2408   case GF_LINE3:
2409     gdImageSetPixel(brush,1,0,pen);
2410     gdImageSetPixel(brush,0,1,pen);
2411     gdImageSetPixel(brush,1,1,pen);
2412     gdImageSetPixel(brush,2,1,pen);
2413     gdImageSetPixel(brush,1,2,pen);
2414     break;
2415   default:
2416     return NULL;
2417   }
2418   return brush;
2419 }
2420 /*****************************************************
2421  * lazy check make sure we rely need to create this graph
2422  *****************************************************/
2423
2424 int lazy_check(image_desc_t *im){
2425     FILE *fd = NULL;
2426     int size = 1;
2427     struct stat  gifstat;
2428     
2429     if (im->lazy == 0) return 0; /* no lazy option */
2430     if (stat(im->graphfile,&gifstat) != 0) 
2431       return 0; /* can't stat */
2432     /* one pixel in the existing graph is more then what we would
2433        change here ... */
2434     if (time(NULL) - gifstat.st_mtime > 
2435         (im->end - im->start) / im->xsize) 
2436       return 0;
2437     if ((fd = fopen(im->graphfile,"rb")) == NULL) 
2438       return 0; /* the file does not exist */
2439     switch (im->imgformat) {
2440     case IF_GIF:
2441         size = GifSize(fd,&(im->xgif),&(im->ygif));
2442         break;
2443     case IF_PNG:
2444         size = PngSize(fd,&(im->xgif),&(im->ygif));
2445         break;
2446     }
2447     fclose(fd);
2448     return size;
2449 }
2450
2451 /* draw that picture thing ... */
2452 int
2453 graph_paint(image_desc_t *im, char ***calcpr)
2454 {
2455     int i,ii;
2456     int lazy =     lazy_check(im);
2457     FILE  *fo;
2458     
2459     /* gif stuff */
2460     gdImagePtr  gif,brush;
2461
2462     double areazero = 0.0;
2463     enum gf_en stack_gf = GF_PRINT;
2464     graph_desc_t *lastgdes = NULL;    
2465     gdPoint canvas[4], back[4];  /* points for canvas*/
2466
2467     /* if we are lazy and there is nothing to PRINT ... quit now */
2468     if (lazy && im->prt_c==0) return 0;
2469     
2470     /* pull the data from the rrd files ... */
2471     
2472     if(data_fetch(im)==-1)
2473         return -1;
2474
2475     /* evaluate CDEF  operations ... */
2476     if(data_calc(im)==-1)
2477         return -1;
2478
2479     /* calculate and PRINT and GPRINT definitions. We have to do it at
2480      * this point because it will affect the length of the legends
2481      * if there are no graph elements we stop here ... 
2482      * if we are lazy, try to quit ... 
2483      */
2484     i=print_calc(im,calcpr);
2485     if(i<0) return -1;
2486     if(i==0 || lazy) return 0;
2487
2488     /* get actual drawing data and find min and max values*/
2489     if(data_proc(im)==-1)
2490         return -1;
2491
2492     if(!im->logarithmic){si_unit(im);}        /* identify si magnitude Kilo, Mega Giga ? */
2493
2494     if(!im->rigid && ! im->logarithmic)
2495         expand_range(im);   /* make sure the upper and lower limit are
2496                            sensible values */
2497
2498     /* init xtr and ytr */
2499     /* determine the actual size of the gif to draw. The size given
2500        on the cmdline is the graph area. But we need more as we have
2501        draw labels and other things outside the graph area */
2502
2503
2504     im->xorigin = 10 + 9 * SmallFont->w+SmallFont->h;
2505     xtr(im,0); 
2506
2507     im->yorigin = 14 + im->ysize;
2508     ytr(im,DNAN);
2509
2510     if(im->title[0] != '\0')
2511         im->yorigin += (LargeFont->h+4);
2512
2513     im->xgif=20+im->xsize + im->xorigin;
2514     im->ygif= im->yorigin+2*SmallFont->h;
2515     
2516     /* determine where to place the legends onto the graphics.
2517        and set im->ygif to match space requirements for text */
2518     if(leg_place(im)==-1)
2519      return -1;
2520
2521     gif=gdImageCreate(im->xgif,im->ygif);
2522
2523     gdImageInterlace(gif, im->interlaced);
2524     
2525     /* allocate colors for the screen elements */
2526     for(i=0;i<DIM(graph_col);i++)
2527         /* check for user override values */
2528         if(im->graph_col[i].red != -1)
2529             graph_col[i].i = 
2530                 gdImageColorAllocate( gif,
2531                                       im->graph_col[i].red, 
2532                                       im->graph_col[i].green, 
2533                                       im->graph_col[i].blue);
2534         else
2535             graph_col[i].i = 
2536                 gdImageColorAllocate( gif,
2537                                       graph_col[i].red, 
2538                                       graph_col[i].green, 
2539                                       graph_col[i].blue);
2540         
2541     
2542     /* allocate colors for the graph */
2543     for(i=0;i<im->gdes_c;i++)
2544         /* only for elements which have a color defined */
2545         if (im->gdes[i].col.red != -1)
2546             im->gdes[i].col.i = 
2547                 gdImageColorAllocate(gif,
2548                                      im->gdes[i].col.red,
2549                                      im->gdes[i].col.green,
2550                                      im->gdes[i].col.blue);
2551     
2552     
2553     /* the actual graph is created by going through the individual
2554        graph elements and then drawing them */
2555     
2556     back[0].x = 0;
2557     back[0].y = 0;
2558     back[1].x = back[0].x+im->xgif;
2559     back[1].y = back[0].y;
2560     back[2].x = back[1].x;
2561     back[2].y = back[0].y+im->ygif;
2562     back[3].x = back[0].x;
2563     back[3].y = back[2].y;
2564
2565     gdImageFilledPolygon(gif,back,4,graph_col[GRC_BACK].i);
2566
2567     canvas[0].x = im->xorigin;
2568     canvas[0].y = im->yorigin;
2569     canvas[1].x = canvas[0].x+im->xsize;
2570     canvas[1].y = canvas[0].y;
2571     canvas[2].x = canvas[1].x;
2572     canvas[2].y = canvas[0].y-im->ysize;
2573     canvas[3].x = canvas[0].x;
2574     canvas[3].y = canvas[2].y;
2575
2576     gdImageFilledPolygon(gif,canvas,4,graph_col[GRC_CANVAS].i);
2577
2578     if (im->minval > 0.0)
2579         areazero = im->minval;
2580     if (im->maxval < 0.0)
2581         areazero = im->maxval;
2582
2583     axis_paint(im,gif);
2584
2585     for(i=0;i<im->gdes_c;i++){  
2586         
2587         switch(im->gdes[i].gf){
2588         case GF_CDEF:
2589         case GF_DEF:
2590         case GF_PRINT:
2591         case GF_GPRINT:
2592         case GF_COMMENT:
2593         case GF_HRULE:
2594         case GF_VRULE:
2595           break;
2596         case GF_LINE1:
2597         case GF_LINE2:
2598         case GF_LINE3:
2599         case GF_AREA:
2600             stack_gf = im->gdes[i].gf;
2601         case GF_STACK:      
2602             /* fix data points at oo and -oo */
2603             for(ii=0;ii<im->xsize;ii++){
2604                 if (isinf(im->gdes[i].p_data[ii])){
2605                     if (im->gdes[i].p_data[ii] > 0) {
2606                         im->gdes[i].p_data[ii] = im->maxval ;
2607                     } else {
2608                         im->gdes[i].p_data[ii] = im->minval ;
2609                     }               
2610                 
2611                 }
2612             }
2613
2614             if (im->gdes[i].col.i != -1){               
2615                /* GF_LINE and frined */
2616                if(stack_gf == GF_LINE1 || stack_gf == GF_LINE2 || stack_gf == GF_LINE3 ){
2617                    brush = MkLineBrush(im,i,stack_gf);
2618                    gdImageSetBrush(gif, brush);
2619                    for(ii=1;ii<im->xsize;ii++){
2620                        if (isnan(im->gdes[i].p_data[ii-1]) ||
2621                            isnan(im->gdes[i].p_data[ii]))
2622                             continue;
2623                        gdImageLine(gif,
2624                                     ii+im->xorigin-1,ytr(im,im->gdes[i].p_data[ii-1]),
2625                                     ii+im->xorigin,ytr(im,im->gdes[i].p_data[ii]),
2626                                     gdBrushed);
2627                         
2628                     }
2629                     gdImageDestroy(brush);
2630                 }
2631                 else 
2632                     /* GF_AREA STACK type*/
2633                     if (im->gdes[i].gf == GF_STACK )
2634                         for(ii=0;ii<im->xsize;ii++){
2635                             if(isnan(im->gdes[i].p_data[ii])){
2636                                 im->gdes[i].p_data[ii] = lastgdes->p_data[ii];
2637                                 continue;
2638                             }
2639                             
2640                             if (lastgdes->p_data[ii] == im->gdes[i].p_data[ii]){
2641                                 continue;
2642                             }
2643                             gdImageLine(gif,
2644                                         ii+im->xorigin,ytr(im,lastgdes->p_data[ii]),
2645                                         ii+im->xorigin,ytr(im,im->gdes[i].p_data[ii]),
2646                                         im->gdes[i].col.i);
2647                         }
2648                
2649                     else /* simple GF_AREA */
2650                         for(ii=0;ii<im->xsize;ii++){
2651                             if (isnan(im->gdes[i].p_data[ii])) {
2652                                 im->gdes[i].p_data[ii] = 0;
2653                                 continue;
2654                             }
2655                             gdImageLine(gif,
2656                                         ii+im->xorigin,ytr(im,areazero),
2657                                         ii+im->xorigin,ytr(im,im->gdes[i].p_data[ii]),
2658                                         im->gdes[i].col.i);
2659                         }
2660            }
2661            lastgdes = &(im->gdes[i]);              
2662            break;
2663         }
2664     }
2665     
2666     grid_paint(im,gif);
2667
2668     /* the RULES are the last thing to paint ... */
2669     for(i=0;i<im->gdes_c;i++){  
2670         
2671         switch(im->gdes[i].gf){
2672         case GF_HRULE:
2673             if(im->gdes[i].yrule >= im->minval
2674                && im->gdes[i].yrule <= im->maxval)
2675               gdImageLine(gif,
2676                           im->xorigin,ytr(im,im->gdes[i].yrule),
2677                           im->xorigin+im->xsize,ytr(im,im->gdes[i].yrule),
2678                           im->gdes[i].col.i); 
2679             break;
2680         case GF_VRULE:
2681           if(im->gdes[i].xrule >= im->start
2682              && im->gdes[i].xrule <= im->end)
2683             gdImageLine(gif,
2684                         xtr(im,im->gdes[i].xrule),im->yorigin,
2685                         xtr(im,im->gdes[i].xrule),im->yorigin-im->ysize,
2686                         im->gdes[i].col.i); 
2687           break;
2688         default:
2689             break;
2690         }
2691     }
2692
2693     if (strcmp(im->graphfile,"-")==0) {
2694 #ifdef WIN32
2695         /* Change translation mode for stdout to BINARY */
2696         _setmode( _fileno( stdout ), O_BINARY );
2697 #endif
2698         fo = stdout;
2699     } else {
2700         if ((fo = fopen(im->graphfile,"wb")) == NULL) {
2701             rrd_set_error("Opening '%s' for write: %s",im->graphfile, strerror(errno));
2702             return (-1);
2703         }
2704     }
2705     switch (im->imgformat) {
2706     case IF_GIF:
2707         gdImageGif(gif, fo);    
2708         break;
2709     case IF_PNG:
2710         gdImagePng(gif, fo);    
2711         break;
2712     }
2713     if (strcmp(im->graphfile,"-") != 0)
2714         fclose(fo);
2715     gdImageDestroy(gif);
2716
2717     return 0;
2718 }
2719
2720
2721 /*****************************************************
2722  * graph stuff 
2723  *****************************************************/
2724
2725 int
2726 gdes_alloc(image_desc_t *im){
2727
2728     long def_step = (im->end-im->start)/im->xsize;
2729     
2730     if (im->step > def_step) /* step can be increassed ... no decreassed */
2731       def_step = im->step;
2732
2733     im->gdes_c++;
2734     
2735     if ((im->gdes = (graph_desc_t *) rrd_realloc(im->gdes, (im->gdes_c)
2736                                            * sizeof(graph_desc_t)))==NULL){
2737         rrd_set_error("realloc graph_descs");
2738         return -1;
2739     }
2740
2741
2742     im->gdes[im->gdes_c-1].step=def_step; 
2743     im->gdes[im->gdes_c-1].start=im->start; 
2744     im->gdes[im->gdes_c-1].end=im->end; 
2745     im->gdes[im->gdes_c-1].vname[0]='\0'; 
2746     im->gdes[im->gdes_c-1].data=NULL;
2747     im->gdes[im->gdes_c-1].ds_namv=NULL;
2748     im->gdes[im->gdes_c-1].data_first=0;
2749     im->gdes[im->gdes_c-1].p_data=NULL;
2750     im->gdes[im->gdes_c-1].rpnp=NULL;
2751     im->gdes[im->gdes_c-1].col.red = -1;
2752     im->gdes[im->gdes_c-1].col.i=-1;
2753     im->gdes[im->gdes_c-1].legend[0]='\0';
2754     im->gdes[im->gdes_c-1].rrd[0]='\0';
2755     im->gdes[im->gdes_c-1].ds=-1;    
2756     im->gdes[im->gdes_c-1].p_data=NULL;    
2757     return 0;
2758 }
2759
2760 /* copies input untill the first unescaped colon is found
2761    or until input ends. backslashes have to be escaped as well */
2762 int
2763 scan_for_col(char *input, int len, char *output)
2764 {
2765     int inp,outp=0;
2766     for (inp=0; 
2767          inp < len &&
2768            input[inp] != ':' &&
2769            input[inp] != '\0';
2770          inp++){
2771       if (input[inp] == '\\' &&
2772           input[inp+1] != '\0' && 
2773           (input[inp+1] == '\\' ||
2774            input[inp+1] == ':')){
2775         output[outp++] = input[++inp];
2776       }
2777       else {
2778         output[outp++] = input[inp];
2779       }
2780     }
2781     output[outp] = '\0';
2782     return inp;
2783 }
2784
2785 int 
2786 rrd_graph(int argc, char **argv, char ***prdata, int *xsize, int *ysize)
2787 {
2788     
2789     image_desc_t   im;
2790     int            i;
2791     long           long_tmp;
2792     time_t         start_tmp=0,end_tmp=0;
2793     char           scan_gtm[12],scan_mtm[12],scan_ltm[12],col_nam[12];
2794     char           symname[100];
2795     unsigned int            col_red,col_green,col_blue;
2796     long           scancount;
2797     int linepass = 0; /* stack can only follow directly after LINE* AREA or STACK */    
2798     struct time_value start_tv, end_tv;
2799     char *parsetime_error = NULL;
2800     int stroff;    
2801
2802     (*prdata)=NULL;
2803
2804     parsetime("end-24h", &start_tv);
2805     parsetime("now", &end_tv);
2806
2807     im.xlab_user.minsec = -1;
2808     im.xgif=0;
2809     im.ygif=0;
2810     im.xsize = 400;
2811     im.ysize = 100;
2812     im.step = 0;
2813     im.ylegend[0] = '\0';
2814     im.title[0] = '\0';
2815     im.minval = DNAN;
2816     im.maxval = DNAN;    
2817     im.interlaced = 0;
2818     im.unitsexponent= 9999;
2819     im.extra_flags= 0;
2820     im.rigid = 0;
2821     im.imginfo = NULL;
2822     im.lazy = 0;
2823     im.logarithmic = 0;
2824     im.ygridstep = DNAN;
2825     im.draw_x_grid = 1;
2826     im.draw_y_grid = 1;
2827     im.base = 1000;
2828     im.prt_c = 0;
2829     im.gdes_c = 0;
2830     im.gdes = NULL;
2831     im.imgformat = IF_GIF; /* we default to GIF output */
2832
2833     for(i=0;i<DIM(graph_col);i++)
2834         im.graph_col[i].red=-1;
2835     
2836     
2837     while (1){
2838         static struct option long_options[] =
2839         {
2840             {"start",      required_argument, 0,  's'},
2841             {"end",        required_argument, 0,  'e'},
2842             {"x-grid",     required_argument, 0,  'x'},
2843             {"y-grid",     required_argument, 0,  'y'},
2844             {"vertical-label",required_argument,0,'v'},
2845             {"width",      required_argument, 0,  'w'},
2846             {"height",     required_argument, 0,  'h'},
2847             {"interlaced", no_argument,       0,  'i'},
2848             {"upper-limit",required_argument, 0,  'u'},
2849             {"lower-limit",required_argument, 0,  'l'},
2850             {"rigid",      no_argument,       0,  'r'},
2851             {"base",       required_argument, 0,  'b'},
2852             {"logarithmic",no_argument,       0,  'o'},
2853             {"color",      required_argument, 0,  'c'},
2854             {"title",      required_argument, 0,  't'},
2855             {"imginfo",    required_argument, 0,  'f'},
2856             {"imgformat",  required_argument, 0,  'a'},
2857             {"lazy",       no_argument,       0,  'z'},
2858             {"no-legend",  no_argument,       0,  'g'},
2859             {"alt-y-grid", no_argument,       0,   257 },
2860             {"alt-autoscale", no_argument,    0,   258 },
2861             {"alt-autoscale-max", no_argument,    0,   259 },
2862             {"units-exponent",required_argument, 0,  260},
2863             {"step",       required_argument, 0,   261},
2864             {0,0,0,0}};
2865         int option_index = 0;
2866         int opt;
2867
2868         
2869         opt = getopt_long(argc, argv, 
2870                           "s:e:x:y:v:w:h:iu:l:rb:oc:t:f:a:z:g",
2871                           long_options, &option_index);
2872
2873         if (opt == EOF)
2874             break;
2875         
2876         switch(opt) {
2877         case 257:
2878             im.extra_flags |= ALTYGRID;
2879             break;
2880         case 258:
2881             im.extra_flags |= ALTAUTOSCALE;
2882             break;
2883         case 259:
2884             im.extra_flags |= ALTAUTOSCALE_MAX;
2885             break;
2886         case 'g':
2887             im.extra_flags |= NOLEGEND;
2888             break;
2889         case 260:
2890             im.unitsexponent = atoi(optarg);
2891             break;
2892         case 261:
2893             im.step =  atoi(optarg);
2894             break;
2895         case 's':
2896             if ((parsetime_error = parsetime(optarg, &start_tv))) {
2897                 rrd_set_error( "start time: %s", parsetime_error );
2898                 return -1;
2899             }
2900             break;
2901         case 'e':
2902             if ((parsetime_error = parsetime(optarg, &end_tv))) {
2903                 rrd_set_error( "end time: %s", parsetime_error );
2904                 return -1;
2905             }
2906             break;
2907         case 'x':
2908             if(strcmp(optarg,"none") == 0){
2909               im.draw_x_grid=0;
2910               break;
2911             };
2912                 
2913             if(sscanf(optarg,
2914                       "%10[A-Z]:%ld:%10[A-Z]:%ld:%10[A-Z]:%ld:%ld:%n",
2915                       scan_gtm,
2916                       &im.xlab_user.gridst,
2917                       scan_mtm,
2918                       &im.xlab_user.mgridst,
2919                       scan_ltm,
2920                       &im.xlab_user.labst,
2921                       &im.xlab_user.precis,
2922                       &stroff) == 7 && stroff != 0){
2923                 strncpy(im.xlab_form, optarg+stroff, sizeof(im.xlab_form) - 1);
2924                 if((im.xlab_user.gridtm = tmt_conv(scan_gtm)) == -1){
2925                     rrd_set_error("unknown keyword %s",scan_gtm);
2926                     return -1;
2927                 } else if ((im.xlab_user.mgridtm = tmt_conv(scan_mtm)) == -1){
2928                     rrd_set_error("unknown keyword %s",scan_mtm);
2929                     return -1;
2930                 } else if ((im.xlab_user.labtm = tmt_conv(scan_ltm)) == -1){
2931                     rrd_set_error("unknown keyword %s",scan_ltm);
2932                     return -1;
2933                 } 
2934                 im.xlab_user.minsec = 1;
2935                 im.xlab_user.stst = im.xlab_form;
2936             } else {
2937                 rrd_set_error("invalid x-grid format");
2938                 return -1;
2939             }
2940             break;
2941         case 'y':
2942
2943             if(strcmp(optarg,"none") == 0){
2944               im.draw_y_grid=0;
2945               break;
2946             };
2947
2948             if(sscanf(optarg,
2949                       "%lf:%d",
2950                       &im.ygridstep,
2951                       &im.ylabfact) == 2) {
2952                 if(im.ygridstep<=0){
2953                     rrd_set_error("grid step must be > 0");
2954                     return -1;
2955                 } else if (im.ylabfact < 1){
2956                     rrd_set_error("label factor must be > 0");
2957                     return -1;
2958                 } 
2959             } else {
2960                 rrd_set_error("invalid y-grid format");
2961                 return -1;
2962             }
2963             break;
2964         case 'v':
2965             strncpy(im.ylegend,optarg,150);
2966             im.ylegend[150]='\0';
2967             break;
2968         case 'u':
2969             im.maxval = atof(optarg);
2970             break;
2971         case 'l':
2972             im.minval = atof(optarg);
2973             break;
2974         case 'b':
2975             im.base = atol(optarg);
2976             if(im.base != 1024 && im.base != 1000 ){
2977                 rrd_set_error("the only sensible value for base apart from 1000 is 1024");
2978                 return -1;
2979             }
2980             break;
2981         case 'w':
2982             long_tmp = atol(optarg);
2983             if (long_tmp < 10) {
2984                 rrd_set_error("width below 10 pixels");
2985                 return -1;
2986             }
2987             im.xsize = long_tmp;
2988             break;
2989         case 'h':
2990             long_tmp = atol(optarg);
2991             if (long_tmp < 10) {
2992                 rrd_set_error("height below 10 pixels");
2993                 return -1;
2994             }
2995             im.ysize = long_tmp;
2996             break;
2997         case 'i':
2998             im.interlaced = 1;
2999             break;
3000         case 'r':
3001             im.rigid = 1;
3002             break;
3003         case 'f':
3004             im.imginfo = optarg;
3005             break;
3006         case 'a':
3007             if((im.imgformat = if_conv(optarg)) == -1) {
3008                 rrd_set_error("unsupported graphics format '%s'",optarg);
3009                 return -1;
3010             }
3011             break;
3012         case 'z':
3013             im.lazy = 1;
3014             break;
3015         case 'o':
3016             im.logarithmic = 1;
3017             if (isnan(im.minval))
3018                 im.minval=1;
3019             break;
3020         case 'c':
3021             if(sscanf(optarg,
3022                       "%10[A-Z]#%2x%2x%2x",
3023                       col_nam,&col_red,&col_green,&col_blue) == 4){
3024                 int ci;
3025                 if((ci=grc_conv(col_nam)) != -1){
3026                     im.graph_col[ci].red=col_red;
3027                     im.graph_col[ci].green=col_green;
3028                     im.graph_col[ci].blue=col_blue;
3029                 }  else {
3030                   rrd_set_error("invalid color name '%s'",col_nam);
3031                 }
3032             } else {
3033                 rrd_set_error("invalid color def format");
3034                 return -1;
3035             }
3036             break;        
3037         case 't':
3038             strncpy(im.title,optarg,150);
3039             im.title[150]='\0';
3040             break;
3041
3042         case '?':
3043             if (optopt != 0)
3044                 rrd_set_error("unknown option '%c'", optopt);
3045             else
3046                 rrd_set_error("unknown option '%s'",argv[optind-1]);
3047             return -1;
3048         }
3049     }
3050     
3051     if (optind >= argc) {
3052        rrd_set_error("missing filename");
3053        return -1;
3054     }
3055
3056     if (im.logarithmic == 1 && (im.minval <= 0 || isnan(im.minval))){
3057         rrd_set_error("for a logarithmic yaxis you must specify a lower-limit > 0");    
3058         return -1;
3059     }
3060
3061     strncpy(im.graphfile,argv[optind],MAXPATH-1);
3062     im.graphfile[MAXPATH-1]='\0';
3063
3064     if (proc_start_end(&start_tv,&end_tv,&start_tmp,&end_tmp) == -1){
3065         return -1;
3066     }  
3067     
3068     if (start_tmp < 3600*24*365*10){
3069         rrd_set_error("the first entry to fetch should be after 1980 (%ld)",start_tmp);
3070         return -1;
3071     }
3072     
3073     if (end_tmp < start_tmp) {
3074         rrd_set_error("start (%ld) should be less than end (%ld)", 
3075                start_tmp, end_tmp);
3076         return -1;
3077     }
3078     
3079     im.start = start_tmp;
3080     im.end = end_tmp;
3081
3082     
3083     for(i=optind+1;i<argc;i++){
3084         int   argstart=0;
3085         int   strstart=0;
3086         char  varname[30],*rpnex;
3087         gdes_alloc(&im);
3088         if(sscanf(argv[i],"%10[A-Z0-9]:%n",symname,&argstart)==1){
3089             if((im.gdes[im.gdes_c-1].gf=gf_conv(symname))==-1){
3090                 im_free(&im);
3091                 rrd_set_error("unknown function '%s'",symname);
3092                 return -1;
3093             }
3094         } else {
3095             rrd_set_error("can't parse '%s'",argv[i]);
3096             im_free(&im);
3097             return -1;
3098         }
3099
3100         /* reset linepass if a non LINE/STACK/AREA operator gets parsed 
3101         
3102            if (im.gdes[im.gdes_c-1].gf != GF_LINE1 &&
3103            im.gdes[im.gdes_c-1].gf != GF_LINE2 &&
3104            im.gdes[im.gdes_c-1].gf != GF_LINE3 &&
3105            im.gdes[im.gdes_c-1].gf != GF_AREA &&
3106            im.gdes[im.gdes_c-1].gf != GF_STACK) {
3107            linepass = 0;
3108            } 
3109         */
3110         
3111         switch(im.gdes[im.gdes_c-1].gf){
3112         case GF_PRINT:
3113             im.prt_c++;
3114         case GF_GPRINT:
3115             if(sscanf(
3116                 &argv[i][argstart],
3117                 "%29[^#:]:" CF_NAM_FMT ":%n",
3118                 varname,symname,&strstart) == 2){
3119                 scan_for_col(&argv[i][argstart+strstart],FMT_LEG_LEN,im.gdes[im.gdes_c-1].format);
3120                 if((im.gdes[im.gdes_c-1].vidx=find_var(&im,varname))==-1){
3121                     im_free(&im);
3122                     rrd_set_error("unknown variable '%s'",varname);
3123                     return -1;
3124                 }       
3125                 if((im.gdes[im.gdes_c-1].cf=cf_conv(symname))==-1){
3126                     im_free(&im);
3127                     return -1;
3128                 }
3129                 
3130             } else {
3131                 im_free(&im);
3132                 rrd_set_error("can't parse '%s'",&argv[i][argstart]);
3133                 return -1;
3134             }
3135             break;
3136         case GF_COMMENT:
3137             if(strlen(&argv[i][argstart])>FMT_LEG_LEN) argv[i][argstart+FMT_LEG_LEN-3]='\0' ;
3138             strcpy(im.gdes[im.gdes_c-1].legend, &argv[i][argstart]);
3139             break;
3140         case GF_HRULE:
3141             if(sscanf(
3142                 &argv[i][argstart],
3143                 "%lf#%2x%2x%2x:%n",
3144                 &im.gdes[im.gdes_c-1].yrule,
3145                 &col_red,&col_green,&col_blue,
3146                 &strstart) >=  4){
3147                 im.gdes[im.gdes_c-1].col.red = col_red;
3148                 im.gdes[im.gdes_c-1].col.green = col_green;
3149                 im.gdes[im.gdes_c-1].col.blue = col_blue;
3150                 if(strstart <= 0){
3151                     im.gdes[im.gdes_c-1].legend[0] = '\0';
3152                 } else { 
3153                     scan_for_col(&argv[i][argstart+strstart],FMT_LEG_LEN,im.gdes[im.gdes_c-1].legend);
3154                 }
3155             } else {
3156                 im_free(&im);
3157                 rrd_set_error("can't parse '%s'",&argv[i][argstart]);
3158                 return -1;
3159             } 
3160             break;
3161         case GF_VRULE:
3162             if(sscanf(
3163                 &argv[i][argstart],
3164                 "%lu#%2x%2x%2x:%n",
3165                 &im.gdes[im.gdes_c-1].xrule,
3166                 &col_red,
3167                 &col_green,
3168                 &col_blue,
3169                 &strstart) >=  4){
3170                 im.gdes[im.gdes_c-1].col.red = col_red;
3171                 im.gdes[im.gdes_c-1].col.green = col_green;
3172                 im.gdes[im.gdes_c-1].col.blue = col_blue;
3173                 if(strstart <= 0){                    
3174                     im.gdes[im.gdes_c-1].legend[0] = '\0';
3175                 } else { 
3176                     scan_for_col(&argv[i][argstart+strstart],FMT_LEG_LEN,im.gdes[im.gdes_c-1].legend);
3177                 }
3178             } else {
3179                 im_free(&im);
3180                 rrd_set_error("can't parse '%s'",&argv[i][argstart]);
3181                 return -1;
3182             }
3183             break;
3184         case GF_STACK:
3185             if(linepass == 0){
3186                 im_free(&im);
3187                 rrd_set_error("STACK must follow AREA, LINE or STACK");
3188                 return -1; 
3189             }           
3190         case GF_LINE1:
3191         case GF_LINE2:
3192         case GF_LINE3:
3193         case GF_AREA:
3194             linepass = 1;
3195             if((scancount=sscanf(
3196                 &argv[i][argstart],
3197                 "%29[^:#]#%2x%2x%2x:%n",
3198                 varname,
3199                 &col_red,
3200                 &col_green,
3201                     &col_blue,
3202                 &strstart))>=1){
3203                 im.gdes[im.gdes_c-1].col.red = col_red;
3204                 im.gdes[im.gdes_c-1].col.green = col_green;
3205                 im.gdes[im.gdes_c-1].col.blue = col_blue;
3206                 if(strstart <= 0){
3207                     im.gdes[im.gdes_c-1].legend[0] = '\0';
3208                 } else { 
3209                     scan_for_col(&argv[i][argstart+strstart],FMT_LEG_LEN,im.gdes[im.gdes_c-1].legend);
3210                 }
3211                 if((im.gdes[im.gdes_c-1].vidx=find_var(&im,varname))==-1){
3212                     im_free(&im);
3213                     rrd_set_error("unknown variable '%s'",varname);
3214                     return -1;
3215                 }               
3216                 if (scancount < 4)
3217                     im.gdes[im.gdes_c-1].col.red = -1;          
3218                 
3219             } else {
3220                 im_free(&im);
3221                 rrd_set_error("can't parse '%s'",&argv[i][argstart]);
3222                 return -1;
3223             }
3224             break;
3225         case GF_CDEF:
3226             if((rpnex = malloc(strlen(&argv[i][argstart])*sizeof(char)))==NULL){
3227                 rrd_set_error("malloc for CDEF");
3228                 return -1;
3229             }
3230             if(sscanf(
3231                     &argv[i][argstart],
3232                     DEF_NAM_FMT "=%[^: ]",
3233                     im.gdes[im.gdes_c-1].vname,
3234                     rpnex) != 2){
3235                 im_free(&im);
3236                 free(rpnex);
3237                 rrd_set_error("can't parse CDEF '%s'",&argv[i][argstart]);
3238                 return -1;
3239             }
3240             /* checking for duplicate DEF CDEFS */
3241             if(find_var(&im,im.gdes[im.gdes_c-1].vname) != -1){
3242                 im_free(&im);
3243                 rrd_set_error("duplicate variable '%s'",
3244                               im.gdes[im.gdes_c-1].vname);
3245                 return -1; 
3246             }      
3247             if((im.gdes[im.gdes_c-1].rpnp = str2rpn(&im,rpnex))== NULL){
3248                 rrd_set_error("invalid rpn expression '%s'", rpnex);
3249                 im_free(&im);           
3250                 return -1;
3251             }
3252             free(rpnex);
3253             break;
3254         case GF_DEF:
3255             if (sscanf(
3256                 &argv[i][argstart],
3257                 DEF_NAM_FMT "=%n",
3258                 im.gdes[im.gdes_c-1].vname,
3259                 &strstart)== 1 && strstart){ /* is the = did not match %n returns 0 */ 
3260                 if(sscanf(&argv[i][argstart
3261                                   +strstart
3262                                   +scan_for_col(&argv[i][argstart+strstart],
3263                                                 MAXPATH,im.gdes[im.gdes_c-1].rrd)],
3264                           ":" DS_NAM_FMT ":" CF_NAM_FMT,
3265                           im.gdes[im.gdes_c-1].ds_nam,
3266                           symname) != 2){
3267                     im_free(&im);
3268                     rrd_set_error("can't parse DEF '%s' -2",&argv[i][argstart]);
3269                     return -1;
3270                 }
3271             } else {
3272                 im_free(&im);
3273                 rrd_set_error("can't parse DEF '%s'",&argv[i][argstart]);
3274                 return -1;
3275             }
3276             
3277             /* checking for duplicate DEF CDEFS */
3278             if (find_var(&im,im.gdes[im.gdes_c-1].vname) != -1){
3279                 im_free(&im);
3280                 rrd_set_error("duplicate variable '%s'",
3281                           im.gdes[im.gdes_c-1].vname);
3282                 return -1; 
3283             }      
3284             if((im.gdes[im.gdes_c-1].cf=cf_conv(symname))==-1){
3285                 im_free(&im);
3286                 rrd_set_error("unknown cf '%s'",symname);
3287                 return -1;
3288             }
3289             break;
3290         }
3291         
3292     }
3293
3294     if (im.gdes_c==0){
3295         rrd_set_error("can't make a graph without contents");
3296         im_free(&im);
3297         return(-1); 
3298     }
3299     
3300         /* parse rest of arguments containing information on what to draw*/
3301     if (graph_paint(&im,prdata)==-1){
3302         im_free(&im);
3303         return -1;
3304     }
3305     
3306     *xsize=im.xgif;
3307     *ysize=im.ygif;
3308     if (im.imginfo){
3309       char *filename;
3310       if (! (*prdata)) {        
3311         /* maybe prdata is not allocated yet ... lets do it now */
3312         if((*prdata = calloc(2,sizeof(char *)))==NULL){
3313           rrd_set_error("malloc imginfo");
3314           return -1; 
3315         };
3316       }
3317       if(((*prdata)[0] = malloc((strlen(im.imginfo)+200+strlen(im.graphfile))*sizeof(char)))
3318          ==NULL){
3319         rrd_set_error("malloc imginfo");
3320         return -1;
3321       }
3322       filename=im.graphfile+strlen(im.graphfile);      
3323       while(filename > im.graphfile){
3324         if (*(filename-1)=='/' || *(filename-1)=='\\' ) break;
3325         filename--;
3326       }
3327       
3328       sprintf((*prdata)[0],im.imginfo,filename,im.xgif,im.ygif);
3329     }
3330     im_free(&im);
3331     return 0;
3332 }
3333
3334 int bad_format(char *fmt) {
3335         char *ptr;
3336
3337         ptr = fmt;
3338         while (*ptr != '\0') {
3339                 if (*ptr == '%') {ptr++;
3340                         if (*ptr == '\0') return 1;
3341                         while ((*ptr >= '0' && *ptr <= '9') || *ptr == '.') { 
3342                                 ptr++;
3343                         }
3344                         if (*ptr == '\0') return 1;
3345                         if (*ptr == 'l') {
3346                                 ptr++;
3347                                 if (*ptr == '\0') return 1;
3348                                 if (*ptr == 'e' || *ptr == 'f') { 
3349                                         ptr++; 
3350                                         } else { return 1; }
3351                         }
3352                         else if (*ptr == 's' || *ptr == 'S' || *ptr == '%') { ++ptr; }
3353                         else { return 1; }
3354                 } else {
3355                         ++ptr;
3356                 }
3357         }
3358         return 0;
3359 }
3360