1 /****************************************************************************
2 * RRDtool 1.0.33 Copyright Tobias Oetiker, 1997 - 2000
3 ****************************************************************************
4 * rrd__graph.c make creates ne rrds
5 ****************************************************************************/
9 #include <gdlucidan10.h>
10 #include <gdlucidab12.h>
16 #include "rrd_rpncalc.h"
18 #define SmallFont gdLucidaNormal10
19 #define LargeFont gdLucidaBold12
24 # define DPRINT(x) (void)(printf x, printf("\n"))
29 #define DEF_NAM_FMT "%29[_A-Za-z0-9]"
31 enum tmt_en {TMT_SECOND=0,TMT_MINUTE,TMT_HOUR,TMT_DAY,
32 TMT_WEEK,TMT_MONTH,TMT_YEAR};
34 enum grc_en {GRC_CANVAS=0,GRC_BACK,GRC_SHADEA,GRC_SHADEB,
35 GRC_GRID,GRC_MGRID,GRC_FONT,GRC_FRAME,GRC_ARROW,__GRC_END__};
38 enum gf_en {GF_PRINT=0,GF_GPRINT,GF_COMMENT,GF_HRULE,GF_VRULE,GF_LINE1,
39 GF_LINE2,GF_LINE3,GF_AREA,GF_STACK,GF_TICK,
40 GF_DEF, GF_CDEF, GF_VDEF};
42 enum if_en {IF_GIF=0,IF_PNG=1};
45 VDEF_MAXIMUM /* like the MAX in (G)PRINT */
46 ,VDEF_MINIMUM /* like the MIN in (G)PRINT */
47 ,VDEF_AVERAGE /* like the AVERAGE in (G)PRINT */
48 ,VDEF_PERCENT /* Nth percentile */
49 ,VDEF_FIRST /* first non-unknown value and time */
50 ,VDEF_LAST /* last non-unknown value and time */
52 typedef struct vdef_t {
54 double param; /* parameter for function, if applicable */
55 double val; /* resulting value */
56 time_t when; /* timestamp, if applicable */
59 typedef struct col_trip_t {
60 int red; /* red = -1 is no color */
63 int i; /* color index assigned in gif image i=-1 is unasigned*/
67 typedef struct xlab_t {
68 long minsec; /* minimum sec per pix */
69 enum tmt_en gridtm; /* grid interval in what ?*/
70 long gridst; /* how many whats per grid*/
71 enum tmt_en mgridtm; /* label interval in what ?*/
72 long mgridst; /* how many whats per label*/
73 enum tmt_en labtm; /* label interval in what ?*/
74 long labst; /* how many whats per label*/
75 long precis; /* label precision -> label placement*/
76 char *stst; /* strftime string*/
80 {0, TMT_SECOND,30, TMT_MINUTE,5, TMT_MINUTE,5, 0,"%H:%M"},
81 {2, TMT_MINUTE,1, TMT_MINUTE,5, TMT_MINUTE,5, 0,"%H:%M"},
82 {5, TMT_MINUTE,2, TMT_MINUTE,10, TMT_MINUTE,10, 0,"%H:%M"},
83 {10, TMT_MINUTE,5, TMT_MINUTE,20, TMT_MINUTE,20, 0,"%H:%M"},
84 {30, TMT_MINUTE,10, TMT_HOUR,1, TMT_HOUR,1, 0,"%H:%M"},
85 {60, TMT_MINUTE,30, TMT_HOUR,2, TMT_HOUR,2, 0,"%H:%M"},
86 {180, TMT_HOUR,1, TMT_HOUR,6, TMT_HOUR,6, 0,"%H:%M"},
87 /*{300, TMT_HOUR,3, TMT_HOUR,12, TMT_HOUR,12, 12*3600,"%a %p"}, this looks silly*/
88 {600, TMT_HOUR,6, TMT_DAY,1, TMT_DAY,1, 24*3600,"%a"},
89 {1800, TMT_HOUR,12, TMT_DAY,1, TMT_DAY,2, 24*3600,"%a"},
90 {3600, TMT_DAY,1, TMT_WEEK,1, TMT_WEEK,1, 7*24*3600,"Week %W"},
91 {3*3600, TMT_WEEK,1, TMT_MONTH,1, TMT_WEEK,2, 7*24*3600,"Week %W"},
92 {6*3600, TMT_MONTH,1, TMT_MONTH,1, TMT_MONTH,1, 30*24*3600,"%b"},
93 {48*3600, TMT_MONTH,1, TMT_MONTH,3, TMT_MONTH,3, 30*24*3600,"%b"},
94 {10*24*3600, TMT_YEAR,1, TMT_YEAR,1, TMT_YEAR,1, 365*24*3600,"%y"},
95 {-1,TMT_MONTH,0,TMT_MONTH,0,TMT_MONTH,0,0,""}
98 /* sensible logarithmic y label intervals ...
99 the first element of each row defines the possible starting points on the
100 y axis ... the other specify the */
102 double yloglab[][12]= {{ 1e9, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
103 { 1e3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
104 { 1e1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
105 /* { 1e1, 1, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, */
106 { 1e1, 1, 2.5, 5, 7.5, 0, 0, 0, 0, 0, 0, 0 },
107 { 1e1, 1, 2, 4, 6, 8, 0, 0, 0, 0, 0, 0 },
108 { 1e1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0 },
109 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }};
111 /* sensible y label intervals ...*/
113 typedef struct ylab_t {
114 double grid; /* grid spacing */
115 int lfac[4]; /* associated label spacing*/
128 {100.0, {1,2, 5,10}},
129 {200.0, {1,5,10,20}},
130 {500.0, {1,2, 4,10}},
135 col_trip_t graph_col[] = { /* default colors */
136 {255,255,255,-1}, /* canvas */
137 {245,245,245,-1}, /* background */
138 {200,200,200,-1}, /* shade A */
139 {150,150,150,-1}, /* shade B */
140 {140,140,140,-1}, /* grid */
141 {130,30,30,-1}, /* major grid */
142 {0,0,0,-1}, /* font */
143 {0,0,0,-1}, /* frame */
144 {255,0,0,-1} /*arrow*/
147 /* this structure describes the elements which can make up a graph.
148 because they are quite diverse, not all elements will use all the
149 possible parts of the structure. */
151 #define FMT_LEG_LEN 200
153 #define FMT_LEG_LEN 2000
156 typedef struct graph_desc_t {
157 enum gf_en gf; /* graphing function */
158 char vname[30]; /* name of the variable */
159 long vidx; /* gdes reference */
160 char rrd[255]; /* name of the rrd_file containing data */
161 char ds_nam[DS_NAM_SIZE]; /* data source name */
162 long ds; /* data source number */
163 enum cf_en cf; /* consolidation function */
164 col_trip_t col; /* graph color */
165 char format[FMT_LEG_LEN+5]; /* format for PRINT AND GPRINT */
166 char legend[FMT_LEG_LEN+5]; /* legend*/
167 gdPoint legloc; /* location of legend */
168 double yrule; /* value for y rule line and for VDEF */
169 time_t xrule; /* time for x rule line and for VDEF */
170 vdef_t vf; /* instruction for VDEF function */
171 rpnp_t *rpnp; /* instructions for CDEF function */
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 */
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 */
189 typedef struct image_desc_t {
191 /* configuration of graph */
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 */
203 double ygridstep; /* user defined step for y grid */
204 int ylabfact; /* every how many y grid shall a label be written ? */
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
211 char* imginfo; /* construct an <IMG ... tag and return
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 */
218 /* status information */
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 */
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 */
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 find_var_wrapper(void *arg1, char *key);
252 int data_calc( image_desc_t *);
253 int data_proc( image_desc_t *);
254 time_t find_first_time( time_t, enum tmt_en, long);
255 time_t find_next_time( time_t, enum tmt_en, long);
256 void gator( gdImagePtr, int, int);
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 int vdef_parse(struct graph_desc_t *,char *);
272 int vdef_calc(image_desc_t *, int);
273 int vdef_percent_compar(const void *,const void *);
275 /* translate time values into x coordinates */
276 /*#define xtr(x) (int)((double)im->xorigin \
277 + ((double) im->xsize / (double)(im->end - im->start) ) \
278 * ((double)(x) - im->start)+0.5) */
279 /* initialize with xtr(im,0); */
281 xtr(image_desc_t *im,time_t mytime){
284 pixie = (double) im->xsize / (double)(im->end - im->start);
287 return (int)((double)im->xorigin
288 + pixie * ( mytime - im->start ) );
291 /* translate data values into y coordinates */
293 /* #define ytr(x) (int)((double)im->yorigin \
294 - ((double) im->ysize / (im->maxval - im->minval) ) \
295 * ((double)(x) - im->minval)+0.5) */
297 ytr(image_desc_t *im, double value){
302 pixie = (double) im->ysize / (im->maxval - im->minval);
304 pixie = (double) im->ysize / (log10(im->maxval) - log10(im->minval));
306 } else if(!im->logarithmic) {
307 yval = im->yorigin - pixie * (value - im->minval) + 0.5;
309 if (value < im->minval) {
312 yval = im->yorigin - pixie * (log10(value) - log10(im->minval)) + 0.5;
315 /* make sure we don't return anything too unreasonable. GD lib can
316 get terribly slow when drawing lines outside its scope. This is
317 especially problematic in connection with the rigid option */
320 } else if ((int)yval > im->yorigin) {
321 return im->yorigin+2;
322 } else if ((int) yval < im->yorigin - im->ysize){
323 return im->yorigin - im->ysize - 2;
333 /* conversion function for symbolic entry names */
336 #define conv_if(VV,VVV) \
337 if (strcmp(#VV, string) == 0) return VVV ;
339 enum gf_en gf_conv(char *string){
341 conv_if(PRINT,GF_PRINT)
342 conv_if(GPRINT,GF_GPRINT)
343 conv_if(COMMENT,GF_COMMENT)
344 conv_if(HRULE,GF_HRULE)
345 conv_if(VRULE,GF_VRULE)
346 conv_if(LINE1,GF_LINE1)
347 conv_if(LINE2,GF_LINE2)
348 conv_if(LINE3,GF_LINE3)
349 conv_if(AREA,GF_AREA)
350 conv_if(STACK,GF_STACK)
351 conv_if(TICK,GF_TICK)
353 conv_if(CDEF,GF_CDEF)
354 conv_if(VDEF,GF_VDEF)
359 enum if_en if_conv(char *string){
367 enum tmt_en tmt_conv(char *string){
369 conv_if(SECOND,TMT_SECOND)
370 conv_if(MINUTE,TMT_MINUTE)
371 conv_if(HOUR,TMT_HOUR)
373 conv_if(WEEK,TMT_WEEK)
374 conv_if(MONTH,TMT_MONTH)
375 conv_if(YEAR,TMT_YEAR)
379 enum grc_en grc_conv(char *string){
381 conv_if(BACK,GRC_BACK)
382 conv_if(CANVAS,GRC_CANVAS)
383 conv_if(SHADEA,GRC_SHADEA)
384 conv_if(SHADEB,GRC_SHADEB)
385 conv_if(GRID,GRC_GRID)
386 conv_if(MGRID,GRC_MGRID)
387 conv_if(FONT,GRC_FONT)
388 conv_if(FRAME,GRC_FRAME)
389 conv_if(ARROW,GRC_ARROW)
399 im_free(image_desc_t *im)
402 if (im == NULL) return 0;
403 for(i=0;i<im->gdes_c;i++){
404 if (im->gdes[i].data_first){
405 /* careful here, because a single pointer can occur several times */
406 free (im->gdes[i].data);
407 if (im->gdes[i].ds_namv){
408 for (ii=0;ii<im->gdes[i].ds_cnt;ii++)
409 free(im->gdes[i].ds_namv[ii]);
410 free(im->gdes[i].ds_namv);
413 free (im->gdes[i].p_data);
414 free (im->gdes[i].rpnp);
420 /* find SI magnitude symbol for the given number*/
423 image_desc_t *im, /* image description */
430 char *symbol[] = {"a", /* 10e-18 Ato */
431 "f", /* 10e-15 Femto */
432 "p", /* 10e-12 Pico */
433 "n", /* 10e-9 Nano */
434 "u", /* 10e-6 Micro */
435 "m", /* 10e-3 Milli */
440 "T", /* 10e12 Terra */
441 "P", /* 10e15 Peta */
447 if (*value == 0.0 || isnan(*value) ) {
451 sindex = floor(log(fabs(*value))/log((double)im->base));
452 *magfact = pow((double)im->base, (double)sindex);
453 (*value) /= (*magfact);
455 if ( sindex <= symbcenter && sindex >= -symbcenter) {
456 (*symb_ptr) = symbol[sindex+symbcenter];
464 /* find SI magnitude symbol for the numbers on the y-axis*/
467 image_desc_t *im /* image description */
471 char symbol[] = {'a', /* 10e-18 Ato */
472 'f', /* 10e-15 Femto */
473 'p', /* 10e-12 Pico */
474 'n', /* 10e-9 Nano */
475 'u', /* 10e-6 Micro */
476 'm', /* 10e-3 Milli */
481 'T', /* 10e12 Terra */
482 'P', /* 10e15 Peta */
488 if (im->unitsexponent != 9999) {
489 /* unitsexponent = 9, 6, 3, 0, -3, -6, -9, etc */
490 digits = floor(im->unitsexponent / 3);
492 digits = floor( log( max( fabs(im->minval),fabs(im->maxval)))/log((double)im->base));
494 im->magfact = pow((double)im->base , digits);
497 printf("digits %6.3f im->magfact %6.3f\n",digits,im->magfact);
500 if ( ((digits+symbcenter) < sizeof(symbol)) &&
501 ((digits+symbcenter) >= 0) )
502 im->symbol = symbol[(int)digits+symbcenter];
507 /* move min and max values around to become sensible */
510 expand_range(image_desc_t *im)
512 double sensiblevalues[] ={1000.0,900.0,800.0,750.0,700.0,
513 600.0,500.0,400.0,300.0,250.0,
514 200.0,125.0,100.0,90.0,80.0,
515 75.0,70.0,60.0,50.0,40.0,30.0,
516 25.0,20.0,10.0,9.0,8.0,
517 7.0,6.0,5.0,4.0,3.5,3.0,
518 2.5,2.0,1.8,1.5,1.2,1.0,
519 0.8,0.7,0.6,0.5,0.4,0.3,0.2,0.1,0.0,-1};
521 double scaled_min,scaled_max;
528 printf("Min: %6.2f Max: %6.2f MagFactor: %6.2f\n",
529 im->minval,im->maxval,im->magfact);
532 if (isnan(im->ygridstep)){
533 if(im->extra_flags & ALTAUTOSCALE) {
534 /* measure the amplitude of the function. Make sure that
535 graph boundaries are slightly higher then max/min vals
536 so we can see amplitude on the graph */
539 delt = im->maxval - im->minval;
541 fact = 2.0 * pow(10.0,
542 floor(log10(max(fabs(im->minval), fabs(im->maxval)))) - 2);
544 adj = (fact - delt) * 0.55;
546 printf("Min: %6.2f Max: %6.2f delt: %6.2f fact: %6.2f adj: %6.2f\n", im->minval, im->maxval, delt, fact, adj);
552 else if(im->extra_flags & ALTAUTOSCALE_MAX) {
553 /* measure the amplitude of the function. Make sure that
554 graph boundaries are slightly higher than max vals
555 so we can see amplitude on the graph */
556 adj = (im->maxval - im->minval) * 0.1;
560 scaled_min = im->minval / im->magfact;
561 scaled_max = im->maxval / im->magfact;
563 for (i=1; sensiblevalues[i] > 0; i++){
564 if (sensiblevalues[i-1]>=scaled_min &&
565 sensiblevalues[i]<=scaled_min)
566 im->minval = sensiblevalues[i]*(im->magfact);
568 if (-sensiblevalues[i-1]<=scaled_min &&
569 -sensiblevalues[i]>=scaled_min)
570 im->minval = -sensiblevalues[i-1]*(im->magfact);
572 if (sensiblevalues[i-1] >= scaled_max &&
573 sensiblevalues[i] <= scaled_max)
574 im->maxval = sensiblevalues[i-1]*(im->magfact);
576 if (-sensiblevalues[i-1]<=scaled_max &&
577 -sensiblevalues[i] >=scaled_max)
578 im->maxval = -sensiblevalues[i]*(im->magfact);
582 /* adjust min and max to the grid definition if there is one */
583 im->minval = (double)im->ylabfact * im->ygridstep *
584 floor(im->minval / ((double)im->ylabfact * im->ygridstep));
585 im->maxval = (double)im->ylabfact * im->ygridstep *
586 ceil(im->maxval /( (double)im->ylabfact * im->ygridstep));
590 fprintf(stderr,"SCALED Min: %6.2f Max: %6.2f Factor: %6.2f\n",
591 im->minval,im->maxval,im->magfact);
596 /* reduce data reimplementation by Alex */
600 enum cf_en cf, /* which consolidation function ?*/
601 unsigned long cur_step, /* step the data currently is in */
602 time_t *start, /* start, end and step as requested ... */
603 time_t *end, /* ... by the application will be ... */
604 unsigned long *step, /* ... adjusted to represent reality */
605 unsigned long *ds_cnt, /* number of data sources in file */
606 rrd_value_t **data) /* two dimensional array containing the data */
608 int i,reduce_factor = ceil((double)(*step) / (double)cur_step);
609 unsigned long col,dst_row,row_cnt,start_offset,end_offset,skiprows=0;
610 rrd_value_t *srcptr,*dstptr;
612 (*step) = cur_step*reduce_factor; /* set new step size for reduced data */
616 /* We were given one extra row at the beginning of the interval.
617 ** We also need to return one extra row. The extra interval is
618 ** the one defined by the start time in both cases. It is not
619 ** used when graphing but maybe we can use it while reducing the
622 row_cnt = ((*end)-(*start))/cur_step +1;
624 /* alter start and end so that they are multiples of the new steptime.
625 ** End will be shifted towards the future and start will be shifted
626 ** towards the past in order to include the requested interval
628 end_offset = (*end) % (*step);
629 if (end_offset) end_offset = (*step)-end_offset;
630 start_offset = (*start) % (*step);
631 (*end) = (*end)+end_offset;
632 (*start) = (*start)-start_offset;
634 /* The first destination row is unknown yet it still needs
635 ** to be present in the returned data. Skip it.
636 ** Don't make it NaN or we might overwrite the source.
640 /* Depending on the amount of extra data needed at the
641 ** start of the destination, three things can happen:
642 ** -1- start_offset == 0: skip the extra source row
643 ** -2- start_offset == cur_step: do nothing
644 ** -3- start_offset > cur_step: skip some source rows and
645 ** fill one destination row with NaN
647 if (start_offset==0) {
650 } else if (start_offset!=cur_step) {
651 skiprows=((*step)-start_offset)/cur_step+1;
652 srcptr += ((*ds_cnt)*skiprows);
654 for (col=0;col<(*ds_cnt);col++) *dstptr++=DNAN;
657 /* If we had to alter the endtime, there won't be
658 ** enough data to fill the last row. This means
659 ** we have to skip some rows at the end
662 skiprows = ((*step)-end_offset)/cur_step;
667 /* Sanity check: row_cnt should be multiple of reduce_factor */
668 /* if this gets triggered, something is REALY WRONG ... we die immediately */
670 if (row_cnt%reduce_factor) {
671 printf("SANITY CHECK: %lu rows cannot be reduced by %i \n",
672 row_cnt,reduce_factor);
673 printf("BUG in reduce_data()\n");
677 /* Now combine reduce_factor intervals at a time
678 ** into one interval for the destination.
681 for (dst_row=0;row_cnt>=reduce_factor;dst_row++) {
682 for (col=0;col<(*ds_cnt);col++) {
683 rrd_value_t newval=DNAN;
684 unsigned long validval=0;
686 for (i=0;i<reduce_factor;i++) {
687 if (isnan(srcptr[i*(*ds_cnt)+col])) {
691 if (isnan(newval)) newval = srcptr[i*(*ds_cnt)+col];
699 newval += srcptr[i*(*ds_cnt)+col];
702 newval = min (newval,srcptr[i*(*ds_cnt)+col]);
705 /* an interval contains a failure if any subintervals contained a failure */
707 newval = max (newval,srcptr[i*(*ds_cnt)+col]);
710 newval = srcptr[i*(*ds_cnt)+col];
715 if (validval == 0){newval = DNAN;} else{
733 srcptr+=(*ds_cnt)*reduce_factor;
734 row_cnt-=reduce_factor;
737 /* If we had to alter the endtime, we didn't have enough
738 ** source rows to fill the last row. Fill it with NaN.
740 if (end_offset!=0) for (col=0;col<(*ds_cnt);col++) *dstptr++ = DNAN;
744 /* get the data required for the graphs from the
748 data_fetch( image_desc_t *im )
752 /* pull the data from the log files ... */
753 for (i=0;i<im->gdes_c;i++){
754 /* only GF_DEF elements fetch data */
755 if (im->gdes[i].gf != GF_DEF)
759 /* do we have it already ?*/
760 for (ii=0;ii<i;ii++){
761 if (im->gdes[ii].gf != GF_DEF)
763 if((strcmp(im->gdes[i].rrd,im->gdes[ii].rrd) == 0)
764 && (im->gdes[i].cf == im->gdes[ii].cf)){
765 /* OK the data it is here already ...
766 * we just copy the header portion */
767 im->gdes[i].start = im->gdes[ii].start;
768 im->gdes[i].end = im->gdes[ii].end;
769 im->gdes[i].step = im->gdes[ii].step;
770 im->gdes[i].ds_cnt = im->gdes[ii].ds_cnt;
771 im->gdes[i].ds_namv = im->gdes[ii].ds_namv;
772 im->gdes[i].data = im->gdes[ii].data;
773 im->gdes[i].data_first = 0;
780 unsigned long ft_step = im->gdes[i].step ;
782 if((rrd_fetch_fn(im->gdes[i].rrd,
788 &im->gdes[i].ds_namv,
789 &im->gdes[i].data)) == -1){
792 im->gdes[i].data_first = 1;
794 if (ft_step < im->gdes[i].step) {
795 reduce_data(im->gdes[i].cf,
803 im->gdes[i].step = ft_step;
807 /* lets see if the required data source is realy there */
808 for(ii=0;ii<im->gdes[i].ds_cnt;ii++){
809 if(strcmp(im->gdes[i].ds_namv[ii],im->gdes[i].ds_nam) == 0){
812 if (im->gdes[i].ds== -1){
813 rrd_set_error("No DS called '%s' in '%s'",
814 im->gdes[i].ds_nam,im->gdes[i].rrd);
822 /* evaluate the expressions in the CDEF functions */
824 /*************************************************************
826 *************************************************************/
829 find_var_wrapper(void *arg1, char *key)
831 return find_var((image_desc_t *) arg1, key);
834 /* find gdes containing var*/
836 find_var(image_desc_t *im, char *key){
838 for(ii=0;ii<im->gdes_c-1;ii++){
839 if((im->gdes[ii].gf == GF_DEF
840 || im->gdes[ii].gf == GF_VDEF
841 || im->gdes[ii].gf == GF_CDEF)
842 && (strcmp(im->gdes[ii].vname,key) == 0)){
849 /* find the largest common denominator for all the numbers
850 in the 0 terminated num array */
855 for (i=0;num[i+1]!=0;i++){
857 rest=num[i] % num[i+1];
858 num[i]=num[i+1]; num[i+1]=rest;
862 /* return i==0?num[i]:num[i-1]; */
866 /* run the rpn calculator on all the VDEF and CDEF arguments */
868 data_calc( image_desc_t *im){
872 long *steparray, rpi;
877 rpnstack_init(&rpnstack);
879 for (gdi=0;gdi<im->gdes_c;gdi++){
880 /* Look for GF_VDEF and GF_CDEF in the same loop,
881 * so CDEFs can use VDEFs and vice versa
883 switch (im->gdes[gdi].gf) {
885 /* A VDEF has no DS. This also signals other parts
886 * of rrdtool that this is a VDEF value, not a CDEF.
888 im->gdes[gdi].ds_cnt = 0;
889 if (vdef_calc(im,gdi)) {
890 rrd_set_error("Error processing VDEF '%s'"
893 rpnstack_free(&rpnstack);
898 im->gdes[gdi].ds_cnt = 1;
899 im->gdes[gdi].ds = 0;
900 im->gdes[gdi].data_first = 1;
901 im->gdes[gdi].start = 0;
902 im->gdes[gdi].end = 0;
907 /* Find the variables in the expression.
908 * - VDEF variables are substituted by their values
909 * and the opcode is changed into OP_NUMBER.
911 * Note to Jake: I cannot oversee the implications for your
912 * COMPUTE DS stuff. Please check if VDEF and COMPUTE are
913 * compatible (or can be made so).
915 * - CDEF variables are analized for their step size,
916 * the lowest common denominator of all the step
917 * sizes of the data sources involved is calculated
918 * and the resulting number is the step size for the
919 * resulting data source.
921 for(rpi=0;im->gdes[gdi].rpnp[rpi].op != OP_END;rpi++){
922 if(im->gdes[gdi].rpnp[rpi].op == OP_VARIABLE){
923 long ptr = im->gdes[gdi].rpnp[rpi].ptr;
924 if (im->gdes[ptr].ds_cnt == 0) {
926 printf("DEBUG: inside CDEF '%s' processing VDEF '%s'\n",
928 im->gdes[ptr].vname);
929 printf("DEBUG: value from vdef is %f\n",im->gdes[ptr].vf.val);
931 im->gdes[gdi].rpnp[rpi].val = im->gdes[ptr].vf.val;
932 im->gdes[gdi].rpnp[rpi].op = OP_NUMBER;
934 if ((steparray = rrd_realloc(steparray, (++stepcnt+1)*sizeof(*steparray)))==NULL){
935 rrd_set_error("realloc steparray");
936 rpnstack_free(&rpnstack);
940 steparray[stepcnt-1] = im->gdes[ptr].step;
942 /* adjust start and end of cdef (gdi) so
943 * that it runs from the latest start point
944 * to the earliest endpoint of any of the
945 * rras involved (ptr)
947 if(im->gdes[gdi].start < im->gdes[ptr].start)
948 im->gdes[gdi].start = im->gdes[ptr].start;
950 if(im->gdes[gdi].end == 0 ||
951 im->gdes[gdi].end > im->gdes[ptr].end)
952 im->gdes[gdi].end = im->gdes[ptr].end;
954 /* store pointer to the first element of
955 * the rra providing data for variable,
956 * further save step size and data source
959 im->gdes[gdi].rpnp[rpi].data =
960 im->gdes[ptr].data + im->gdes[ptr].ds;
961 im->gdes[gdi].rpnp[rpi].step = im->gdes[ptr].step;
962 im->gdes[gdi].rpnp[rpi].ds_cnt = im->gdes[ptr].ds_cnt;
964 /* backoff the *.data ptr; this is done so
965 * rpncalc() function doesn't have to treat
966 * the first case differently
968 im->gdes[gdi].rpnp[rpi].data-=im->gdes[ptr].ds_cnt;
969 } /* if ds_cnt != 0 */
970 } /* if OP_VARIABLE */
971 } /* loop through all rpi */
973 if(steparray == NULL){
974 rrd_set_error("rpn expressions without DEF"
975 " or CDEF variables are not supported");
976 rpnstack_free(&rpnstack);
979 steparray[stepcnt]=0;
980 /* Now find the resulting step. All steps in all
981 * used RRAs have to be visited
983 im->gdes[gdi].step = lcd(steparray);
985 if((im->gdes[gdi].data = malloc((
986 (im->gdes[gdi].end-im->gdes[gdi].start)
987 / im->gdes[gdi].step +1)
988 * sizeof(double)))==NULL){
989 rrd_set_error("malloc im->gdes[gdi].data");
990 rpnstack_free(&rpnstack);
994 /* Step through the new cdef results array and
995 * calculate the values
997 for (now = im->gdes[gdi].start;
998 now<=im->gdes[gdi].end;
999 now += im->gdes[gdi].step)
1001 rpnp_t *rpnp = im -> gdes[gdi].rpnp;
1003 /* 3rd arg of rpn_calc is for OP_VARIABLE lookups;
1004 * in this case we are advancing by timesteps;
1005 * we use the fact that time_t is a synonym for long
1007 if (rpn_calc(rpnp,&rpnstack,(long) now,
1008 im->gdes[gdi].data,++dataidx) == -1) {
1009 /* rpn_calc sets the error string */
1010 rpnstack_free(&rpnstack);
1013 } /* enumerate over time steps within a CDEF */
1018 } /* enumerate over CDEFs */
1019 rpnstack_free(&rpnstack);
1023 /* massage data so, that we get one value for each x coordinate in the graph */
1025 data_proc( image_desc_t *im ){
1027 double pixstep = (double)(im->end-im->start)
1028 /(double)im->xsize; /* how much time
1029 passes in one pixel */
1031 double minval=DNAN,maxval=DNAN;
1033 unsigned long gr_time;
1035 /* memory for the processed data */
1036 for(i=0;i<im->gdes_c;i++){
1037 if((im->gdes[i].gf==GF_LINE1) ||
1038 (im->gdes[i].gf==GF_LINE2) ||
1039 (im->gdes[i].gf==GF_LINE3) ||
1040 (im->gdes[i].gf==GF_AREA) ||
1041 (im->gdes[i].gf==GF_TICK) ||
1042 (im->gdes[i].gf==GF_STACK)){
1043 if((im->gdes[i].p_data = malloc((im->xsize +1)
1044 * sizeof(rrd_value_t)))==NULL){
1045 rrd_set_error("malloc data_proc");
1051 for(i=0;i<im->xsize;i++){
1053 gr_time = im->start+pixstep*i; /* time of the
1057 for(ii=0;ii<im->gdes_c;ii++){
1059 switch(im->gdes[ii].gf){
1067 vidx = im->gdes[ii].vidx;
1070 im->gdes[vidx].data[
1071 ((unsigned long)floor((double)
1072 (gr_time - im->gdes[vidx].start )
1073 / im->gdes[vidx].step)+1)
1075 /* added one because data was not being aligned properly
1076 this fixes it. We may also be having a problem in fetch ... */
1078 *im->gdes[vidx].ds_cnt
1079 +im->gdes[vidx].ds];
1081 if (! isnan(value)) {
1083 im->gdes[ii].p_data[i] = paintval;
1084 /* GF_TICK: the data values are not relevant for min and max */
1085 if (finite(paintval) && im->gdes[ii].gf != GF_TICK ){
1086 if (isnan(minval) || paintval < minval)
1088 if (isnan(maxval) || paintval > maxval)
1092 im->gdes[ii].p_data[i] = DNAN;
1108 /* if min or max have not been asigned a value this is because
1109 there was no data in the graph ... this is not good ...
1110 lets set these to dummy values then ... */
1112 if (isnan(minval)) minval = 0.0;
1113 if (isnan(maxval)) maxval = 1.0;
1115 /* adjust min and max values */
1116 if (isnan(im->minval)
1117 || ((!im->logarithmic && !im->rigid) /* don't adjust low-end with log scale */
1118 && im->minval > minval))
1119 im->minval = minval;
1120 if (isnan(im->maxval)
1122 && im->maxval < maxval)){
1123 if (im->logarithmic)
1124 im->maxval = maxval * 1.1;
1126 im->maxval = maxval;
1128 /* make sure min and max are not equal */
1129 if (im->minval == im->maxval) {
1131 if (! im->logarithmic) {
1135 /* make sure min and max are not both zero */
1136 if (im->maxval == 0.0) {
1146 /* identify the point where the first gridline, label ... gets placed */
1150 time_t start, /* what is the initial time */
1151 enum tmt_en baseint, /* what is the basic interval */
1152 long basestep /* how many if these do we jump a time */
1156 tm = *localtime(&start);
1159 tm.tm_sec -= tm.tm_sec % basestep; break;
1162 tm.tm_min -= tm.tm_min % basestep;
1167 tm.tm_hour -= tm.tm_hour % basestep; break;
1169 /* we do NOT look at the basestep for this ... */
1172 tm.tm_hour = 0; break;
1174 /* we do NOT look at the basestep for this ... */
1178 tm.tm_mday -= tm.tm_wday -1; /* -1 because we want the monday */
1179 if (tm.tm_wday==0) tm.tm_mday -= 7; /* we want the *previous* monday */
1186 tm.tm_mon -= tm.tm_mon % basestep; break;
1194 tm.tm_year -= (tm.tm_year+1900) % basestep;
1199 /* identify the point where the next gridline, label ... gets placed */
1202 time_t current, /* what is the initial time */
1203 enum tmt_en baseint, /* what is the basic interval */
1204 long basestep /* how many if these do we jump a time */
1209 tm = *localtime(¤t);
1213 tm.tm_sec += basestep; break;
1215 tm.tm_min += basestep; break;
1217 tm.tm_hour += basestep; break;
1219 tm.tm_mday += basestep; break;
1221 tm.tm_mday += 7*basestep; break;
1223 tm.tm_mon += basestep; break;
1225 tm.tm_year += basestep;
1227 madetime = mktime(&tm);
1228 } while (madetime == -1); /* this is necessary to skip impssible times
1229 like the daylight saving time skips */
1234 void gator( gdImagePtr gif, int x, int y){
1236 /* this function puts the name of the author and the tool into the
1237 graph. Remove if you must, but please note, that it is here,
1238 because I would like people who look at rrdtool generated graphs to
1239 see what was used to do it. No obviously you can also add a credit
1240 line to your webpage or printed document, this is fine with me. But
1241 as I have no control over this, I added the little tag in here.
1244 /* the fact that the text of what gets put into the graph is not
1245 visible in the function, has lead some to think this is for
1246 obfuscation reasons. While this is a nice side effect (I addmit),
1247 it is not the prime reason. The prime reason is, that the font
1248 used, is so small, that I had to hand edit the characters to ensure
1249 readability. I could thus not use the normal gd functions to write,
1250 but had to embed a slightly compressed bitmap version into the code.
1253 int li[]={0,0,1, 0,4,5, 0,8,9, 0,12,14, 0,17,17, 0,21,21,
1254 0,24,24, 0,34,34, 0,40,42, 0,45,45, 0,48,49, 0,52,54,
1255 0,61,61, 0,64,66, 0,68,70, 0,72,74, 0,76,76, 0,78,78,
1257 1,0,0, 1,2,2, 1,4,4, 1,6,6, 1,8,8, 1,10,10,
1258 1,13,13, 1,16,16, 1,18,18, 1,20,20, 1,22,22, 1,24,24,
1259 1,34,34, 1,41,41, 1,44,44, 1,46,46, 1,48,48, 1,50,50,
1260 1,53,53, 1,60,60, 1,62,62, 1,64,64, 1,69,69, 1,73,73,
1261 1,76,76, 1,78,78, 1,80,80, 1,84,84, 1,86,86,
1262 2,0,1, 2,4,5, 2,8,8, 2,10,10, 2,13,13, 2,16,16,
1263 2,18,18, 2,20,20, 2,22,22, 2,24,24, 2,33,33, 2,41,41,
1264 2,44,44, 2,46,46, 2,48,49, 2,53,53, 2,60,60, 2,62,62,
1265 2,64,65, 2,69,69, 2,73,73, 2,76,77, 2,80,81, 2,84,85,
1266 3,0,0, 3,2,2, 3,4,4, 3,6,6, 3,8,8, 3,10,10,
1267 3,13,13, 3,16,16, 3,18,18, 3,20,20, 3,22,22, 3,24,24,
1268 3,32,32, 3,41,41, 3,44,44, 3,46,46, 3,48,48, 3,50,50,
1269 3,53,53, 3,60,60, 3,62,62, 3,64,64, 3,69,69, 3,73,73,
1270 3,76,76, 3,78,78, 3,80,80, 3,84,84, 3,86,86,
1271 4,0,0, 4,2,2, 4,4,4, 4,6,6, 4,8,9, 4,13,13,
1272 4,17,17, 4,21,21, 4,24,26, 4,32,32, 4,41,41, 4,45,45,
1273 4,48,49, 4,52,54, 4,61,61, 4,64,66, 4,69,69, 4,72,74,
1274 4,76,76, 4,78,78, 4,80,82, 4,84,84};
1276 for(i=0; i<DIM(li); i=i+3)
1277 for(ii=y+li[i+1]; ii<=y+li[i+2];ii++)
1278 gdImageSetPixel(gif,x-li[i],ii,graph_col[GRC_GRID].i);
1282 /* calculate values required for PRINT and GPRINT functions */
1285 print_calc(image_desc_t *im, char ***prdata)
1287 long i,ii,validsteps;
1289 int graphelement = 0;
1292 double magfact = -1;
1296 if (im->imginfo) prlines++;
1297 for(i=0;i<im->gdes_c;i++){
1298 switch(im->gdes[i].gf){
1301 if(((*prdata) = rrd_realloc((*prdata),prlines*sizeof(char *)))==NULL){
1302 rrd_set_error("realloc prdata");
1306 /* PRINT and GPRINT can now print VDEF generated values.
1307 * There's no need to do any calculations on them as these
1308 * calculations were already made.
1310 vidx = im->gdes[i].vidx;
1311 if (im->gdes[vidx].gf==GF_VDEF) { /* simply use vals */
1312 printval = im->gdes[vidx].yrule;
1313 } else { /* need to calculate max,min,avg etcetera */
1314 max_ii =((im->gdes[vidx].end
1315 - im->gdes[vidx].start)
1316 / im->gdes[vidx].step
1317 * im->gdes[vidx].ds_cnt);
1320 for(ii=im->gdes[vidx].ds+im->gdes[vidx].ds_cnt;
1321 ii < max_ii+im->gdes[vidx].ds_cnt;
1322 ii+=im->gdes[vidx].ds_cnt){
1323 if (! finite(im->gdes[vidx].data[ii]))
1325 if (isnan(printval)){
1326 printval = im->gdes[vidx].data[ii];
1331 switch (im->gdes[i].cf){
1334 case CF_DEVSEASONAL:
1338 printval += im->gdes[vidx].data[ii];
1341 printval = min( printval, im->gdes[vidx].data[ii]);
1345 printval = max( printval, im->gdes[vidx].data[ii]);
1348 printval = im->gdes[vidx].data[ii];
1351 if (im->gdes[i].cf==CF_AVERAGE || im->gdes[i].cf > CF_LAST) {
1352 if (validsteps > 1) {
1353 printval = (printval / validsteps);
1356 } /* prepare printval */
1359 if ((percent_s = strstr(im->gdes[i].format,"%S")) != NULL) {
1360 /* Magfact is set to -1 upon entry to print_calc. If it
1361 * is still less than 0, then we need to run auto_scale.
1362 * Otherwise, put the value into the correct units. If
1363 * the value is 0, then do not set the symbol or magnification
1364 * so next the calculation will be performed again. */
1365 if (magfact < 0.0) {
1366 auto_scale(im,&printval,&si_symb,&magfact);
1367 if (printval == 0.0)
1370 printval /= magfact;
1372 *(++percent_s) = 's';
1373 } else if (strstr(im->gdes[i].format,"%s") != NULL) {
1374 auto_scale(im,&printval,&si_symb,&magfact);
1377 if (im->gdes[i].gf == GF_PRINT){
1378 (*prdata)[prlines-2] = malloc((FMT_LEG_LEN+2)*sizeof(char));
1379 if (bad_format(im->gdes[i].format)) {
1380 rrd_set_error("bad format for [G]PRINT in '%s'", im->gdes[i].format);
1383 #ifdef HAVE_SNPRINTF
1384 snprintf((*prdata)[prlines-2],FMT_LEG_LEN,im->gdes[i].format,printval,si_symb);
1386 sprintf((*prdata)[prlines-2],im->gdes[i].format,printval,si_symb);
1388 (*prdata)[prlines-1] = NULL;
1392 if (bad_format(im->gdes[i].format)) {
1393 rrd_set_error("bad format for [G]PRINT in '%s'", im->gdes[i].format);
1396 #ifdef HAVE_SNPRINTF
1397 snprintf(im->gdes[i].legend,FMT_LEG_LEN-2,im->gdes[i].format,printval,si_symb);
1399 sprintf(im->gdes[i].legend,im->gdes[i].format,printval,si_symb);
1421 return graphelement;
1425 /* place legends with color spots */
1427 leg_place(image_desc_t *im)
1430 int interleg = SmallFont->w*2;
1431 int box = SmallFont->h*1.2;
1432 int border = SmallFont->w*2;
1433 int fill=0, fill_last;
1435 int leg_x = border, leg_y = im->ygif;
1439 char prt_fctn; /*special printfunctions */
1442 if( !(im->extra_flags & NOLEGEND) ) {
1443 if ((legspace = malloc(im->gdes_c*sizeof(int)))==NULL){
1444 rrd_set_error("malloc for legspace");
1448 for(i=0;i<im->gdes_c;i++){
1451 leg_cc = strlen(im->gdes[i].legend);
1453 /* is there a controle code ant the end of the legend string ? */
1454 if (leg_cc >= 2 && im->gdes[i].legend[leg_cc-2] == '\\') {
1455 prt_fctn = im->gdes[i].legend[leg_cc-1];
1457 im->gdes[i].legend[leg_cc] = '\0';
1461 /* remove exess space */
1462 while (prt_fctn=='g' &&
1464 im->gdes[i].legend[leg_cc-1]==' '){
1466 im->gdes[i].legend[leg_cc]='\0';
1469 legspace[i]=(prt_fctn=='g' ? 0 : interleg);
1472 /* no interleg space if string ends in \g */
1473 fill += legspace[i];
1475 if (im->gdes[i].gf != GF_GPRINT &&
1476 im->gdes[i].gf != GF_COMMENT) {
1479 fill += leg_cc * SmallFont->w;
1484 /* who said there was a special tag ... ?*/
1485 if (prt_fctn=='g') {
1488 if (prt_fctn == '\0') {
1489 if (i == im->gdes_c -1 ) prt_fctn ='l';
1491 /* is it time to place the legends ? */
1492 if (fill > im->xgif - 2*border){
1507 if (prt_fctn != '\0'){
1509 if (leg_c >= 2 && prt_fctn == 'j') {
1510 glue = (im->xgif - fill - 2* border) / (leg_c-1);
1511 /* if (glue > 2 * SmallFont->w) glue = 0; */
1515 if (prt_fctn =='c') leg_x = (im->xgif - fill) / 2.0;
1516 if (prt_fctn =='r') leg_x = im->xgif - fill - border;
1518 for(ii=mark;ii<=i;ii++){
1519 if(im->gdes[ii].legend[0]=='\0')
1521 im->gdes[ii].legloc.x = leg_x;
1522 im->gdes[ii].legloc.y = leg_y;
1524 + strlen(im->gdes[ii].legend)*SmallFont->w
1527 if (im->gdes[ii].gf != GF_GPRINT &&
1528 im->gdes[ii].gf != GF_COMMENT)
1531 leg_y = leg_y + SmallFont->h*1.2;
1532 if (prt_fctn == 's') leg_y -= SmallFont->h *0.5;
1544 /* create a grid on the graph. it determines what to do
1545 from the values of xsize, start and end */
1547 /* the xaxis labels are determined from the number of seconds per pixel
1548 in the requested graph */
1553 horizontal_grid(gdImagePtr gif, image_desc_t *im)
1561 char graph_label[100];
1562 gdPoint polyPoints[4];
1563 int labfact,gridind;
1564 int styleMinor[2],styleMajor[2];
1565 int decimals, fractionals;
1570 range = im->maxval - im->minval;
1571 scaledrange = range / im->magfact;
1573 /* does the scale of this graph make it impossible to put lines
1574 on it? If so, give up. */
1575 if (isnan(scaledrange)) {
1579 styleMinor[0] = graph_col[GRC_GRID].i;
1580 styleMinor[1] = gdTransparent;
1582 styleMajor[0] = graph_col[GRC_MGRID].i;
1583 styleMajor[1] = gdTransparent;
1585 /* find grid spaceing */
1587 if(isnan(im->ygridstep)){
1588 if(im->extra_flags & ALTYGRID) {
1589 /* find the value with max number of digits. Get number of digits */
1590 decimals = ceil(log10(max(fabs(im->maxval), fabs(im->minval))));
1591 if(decimals <= 0) /* everything is small. make place for zero */
1594 fractionals = floor(log10(range));
1595 if(fractionals < 0) /* small amplitude. */
1596 sprintf(labfmt, "%%%d.%df", decimals - fractionals + 1, -fractionals + 1);
1598 sprintf(labfmt, "%%%d.1f", decimals + 1);
1599 gridstep = pow((double)10, (double)fractionals);
1600 if(gridstep == 0) /* range is one -> 0.1 is reasonable scale */
1602 /* should have at least 5 lines but no more then 15 */
1603 if(range/gridstep < 5)
1605 if(range/gridstep > 15)
1607 if(range/gridstep > 5) {
1609 if(range/gridstep > 8)
1618 for(i=0;ylab[i].grid > 0;i++){
1619 pixel = im->ysize / (scaledrange / ylab[i].grid);
1620 if (gridind == -1 && pixel > 5) {
1627 if (pixel * ylab[gridind].lfac[i] >= 2 * SmallFont->h) {
1628 labfact = ylab[gridind].lfac[i];
1633 gridstep = ylab[gridind].grid * im->magfact;
1636 gridstep = im->ygridstep;
1637 labfact = im->ylabfact;
1640 polyPoints[0].x=im->xorigin;
1641 polyPoints[1].x=im->xorigin+im->xsize;
1642 sgrid = (int)( im->minval / gridstep - 1);
1643 egrid = (int)( im->maxval / gridstep + 1);
1644 scaledstep = gridstep/im->magfact;
1645 for (i = sgrid; i <= egrid; i++){
1646 polyPoints[0].y=ytr(im,gridstep*i);
1647 if ( polyPoints[0].y >= im->yorigin-im->ysize
1648 && polyPoints[0].y <= im->yorigin) {
1649 if(i % labfact == 0){
1650 if (i==0 || im->symbol == ' ') {
1652 if(im->extra_flags & ALTYGRID) {
1653 sprintf(graph_label,labfmt,scaledstep*i);
1656 sprintf(graph_label,"%4.1f",scaledstep*i);
1659 sprintf(graph_label,"%4.0f",scaledstep*i);
1663 sprintf(graph_label,"%4.1f %c",scaledstep*i, im->symbol);
1665 sprintf(graph_label,"%4.0f %c",scaledstep*i, im->symbol);
1669 gdImageString(gif, SmallFont,
1670 (polyPoints[0].x - (strlen(graph_label) *
1672 polyPoints[0].y - SmallFont->h/2+1,
1673 (unsigned char *)graph_label, graph_col[GRC_FONT].i);
1675 gdImageSetStyle(gif, styleMajor, 2);
1677 gdImageLine(gif, polyPoints[0].x-2,polyPoints[0].y,
1678 polyPoints[0].x+2,polyPoints[0].y,graph_col[GRC_MGRID].i);
1679 gdImageLine(gif, polyPoints[1].x-2,polyPoints[0].y,
1680 polyPoints[1].x+2,polyPoints[0].y,graph_col[GRC_MGRID].i);
1682 gdImageSetStyle(gif, styleMinor, 2);
1683 gdImageLine(gif, polyPoints[0].x-1,polyPoints[0].y,
1684 polyPoints[0].x+1,polyPoints[0].y,graph_col[GRC_GRID].i);
1685 gdImageLine(gif, polyPoints[1].x-1,polyPoints[0].y,
1686 polyPoints[1].x+1,polyPoints[0].y,graph_col[GRC_GRID].i);
1688 gdImageLine(gif, polyPoints[0].x,polyPoints[0].y,
1689 polyPoints[1].x,polyPoints[0].y,gdStyled);
1692 /* if(im->minval * im->maxval < 0){
1693 polyPoints[0].y=ytr(0);
1694 gdImageLine(gif, polyPoints[0].x,polyPoints[0].y,
1695 polyPoints[1].x,polyPoints[0].y,graph_col[GRC_MGRID].i);
1701 /* logaritmic horizontal grid */
1703 horizontal_log_grid(gdImagePtr gif, image_desc_t *im)
1707 int minoridx=0, majoridx=0;
1708 char graph_label[100];
1709 gdPoint polyPoints[4];
1710 int styleMinor[2],styleMajor[2];
1711 double value, pixperstep, minstep;
1713 /* find grid spaceing */
1714 pixpex= (double)im->ysize / (log10(im->maxval) - log10(im->minval));
1716 if (isnan(pixpex)) {
1720 for(i=0;yloglab[i][0] > 0;i++){
1721 minstep = log10(yloglab[i][0]);
1722 for(ii=1;yloglab[i][ii+1] > 0;ii++){
1723 if(yloglab[i][ii+2]==0){
1724 minstep = log10(yloglab[i][ii+1])-log10(yloglab[i][ii]);
1728 pixperstep = pixpex * minstep;
1729 if(pixperstep > 5){minoridx = i;}
1730 if(pixperstep > 2 * SmallFont->h){majoridx = i;}
1733 styleMinor[0] = graph_col[GRC_GRID].i;
1734 styleMinor[1] = gdTransparent;
1736 styleMajor[0] = graph_col[GRC_MGRID].i;
1737 styleMajor[1] = gdTransparent;
1739 polyPoints[0].x=im->xorigin;
1740 polyPoints[1].x=im->xorigin+im->xsize;
1741 /* paint minor grid */
1742 for (value = pow((double)10, log10(im->minval)
1743 - fmod(log10(im->minval),log10(yloglab[minoridx][0])));
1744 value <= im->maxval;
1745 value *= yloglab[minoridx][0]){
1746 if (value < im->minval) continue;
1748 while(yloglab[minoridx][++i] > 0){
1749 polyPoints[0].y = ytr(im,value * yloglab[minoridx][i]);
1750 if (polyPoints[0].y <= im->yorigin - im->ysize) break;
1751 gdImageSetStyle(gif, styleMinor, 2);
1752 gdImageLine(gif, polyPoints[0].x-1,polyPoints[0].y,
1753 polyPoints[0].x+1,polyPoints[0].y,graph_col[GRC_GRID].i);
1754 gdImageLine(gif, polyPoints[1].x-1,polyPoints[0].y,
1755 polyPoints[1].x+1,polyPoints[0].y,graph_col[GRC_GRID].i);
1757 gdImageLine(gif, polyPoints[0].x,polyPoints[0].y,
1758 polyPoints[1].x,polyPoints[0].y,gdStyled);
1762 /* paint major grid and labels*/
1763 for (value = pow((double)10, log10(im->minval)
1764 - fmod(log10(im->minval),log10(yloglab[majoridx][0])));
1765 value <= im->maxval;
1766 value *= yloglab[majoridx][0]){
1767 if (value < im->minval) continue;
1769 while(yloglab[majoridx][++i] > 0){
1770 polyPoints[0].y = ytr(im,value * yloglab[majoridx][i]);
1771 if (polyPoints[0].y <= im->yorigin - im->ysize) break;
1772 gdImageSetStyle(gif, styleMajor, 2);
1773 gdImageLine(gif, polyPoints[0].x-2,polyPoints[0].y,
1774 polyPoints[0].x+2,polyPoints[0].y,graph_col[GRC_MGRID].i);
1775 gdImageLine(gif, polyPoints[1].x-2,polyPoints[0].y,
1776 polyPoints[1].x+2,polyPoints[0].y,graph_col[GRC_MGRID].i);
1778 gdImageLine(gif, polyPoints[0].x,polyPoints[0].y,
1779 polyPoints[1].x,polyPoints[0].y,gdStyled);
1780 sprintf(graph_label,"%3.0e",value * yloglab[majoridx][i]);
1781 gdImageString(gif, SmallFont,
1782 (polyPoints[0].x - (strlen(graph_label) *
1784 polyPoints[0].y - SmallFont->h/2+1,
1785 (unsigned char *)graph_label, graph_col[GRC_FONT].i);
1797 int xlab_sel; /* which sort of label and grid ? */
1800 char graph_label[100];
1801 gdPoint polyPoints[4]; /* points for filled graph and more*/
1803 /* style for grid lines */
1807 /* the type of time grid is determined by finding
1808 the number of seconds per pixel in the graph */
1811 if(im->xlab_user.minsec == -1){
1812 factor=(im->end - im->start)/im->xsize;
1814 while ( xlab[xlab_sel+1].minsec != -1
1815 && xlab[xlab_sel+1].minsec <= factor){ xlab_sel++; }
1816 im->xlab_user.gridtm = xlab[xlab_sel].gridtm;
1817 im->xlab_user.gridst = xlab[xlab_sel].gridst;
1818 im->xlab_user.mgridtm = xlab[xlab_sel].mgridtm;
1819 im->xlab_user.mgridst = xlab[xlab_sel].mgridst;
1820 im->xlab_user.labtm = xlab[xlab_sel].labtm;
1821 im->xlab_user.labst = xlab[xlab_sel].labst;
1822 im->xlab_user.precis = xlab[xlab_sel].precis;
1823 im->xlab_user.stst = xlab[xlab_sel].stst;
1826 /* y coords are the same for every line ... */
1827 polyPoints[0].y = im->yorigin;
1828 polyPoints[1].y = im->yorigin-im->ysize;
1830 /* paint the minor grid */
1831 for(ti = find_first_time(im->start,
1832 im->xlab_user.gridtm,
1833 im->xlab_user.gridst);
1835 ti = find_next_time(ti,im->xlab_user.gridtm,im->xlab_user.gridst)
1837 /* are we inside the graph ? */
1838 if (ti < im->start || ti > im->end) continue;
1839 polyPoints[0].x = xtr(im,ti);
1840 styleDotted[0] = graph_col[GRC_GRID].i;
1841 styleDotted[1] = gdTransparent;
1843 gdImageSetStyle(gif, styleDotted, 2);
1845 gdImageLine(gif, polyPoints[0].x,polyPoints[0].y,
1846 polyPoints[0].x,polyPoints[1].y,gdStyled);
1847 gdImageLine(gif, polyPoints[0].x,polyPoints[0].y-1,
1848 polyPoints[0].x,polyPoints[0].y+1,graph_col[GRC_GRID].i);
1849 gdImageLine(gif, polyPoints[0].x,polyPoints[1].y-1,
1850 polyPoints[0].x,polyPoints[1].y+1,graph_col[GRC_GRID].i);
1853 /* paint the major grid */
1854 for(ti = find_first_time(im->start,
1855 im->xlab_user.mgridtm,
1856 im->xlab_user.mgridst);
1858 ti = find_next_time(ti,im->xlab_user.mgridtm,im->xlab_user.mgridst)
1860 /* are we inside the graph ? */
1861 if (ti < im->start || ti > im->end) continue;
1862 polyPoints[0].x = xtr(im,ti);
1863 styleDotted[0] = graph_col[GRC_MGRID].i;
1864 styleDotted[1] = gdTransparent;
1865 gdImageSetStyle(gif, styleDotted, 2);
1867 gdImageLine(gif, polyPoints[0].x,polyPoints[0].y,
1868 polyPoints[0].x,polyPoints[1].y,gdStyled);
1869 gdImageLine(gif, polyPoints[0].x,polyPoints[0].y-2,
1870 polyPoints[0].x,polyPoints[0].y+2,graph_col[GRC_MGRID].i);
1871 gdImageLine(gif, polyPoints[0].x,polyPoints[1].y-2,
1872 polyPoints[0].x,polyPoints[1].y+2,graph_col[GRC_MGRID].i);
1874 /* paint the labels below the graph */
1875 for(ti = find_first_time(im->start,
1876 im->xlab_user.labtm,
1877 im->xlab_user.labst);
1879 ti = find_next_time(ti,im->xlab_user.labtm,im->xlab_user.labst)
1882 tilab= ti + im->xlab_user.precis/2; /* correct time for the label */
1885 strftime(graph_label,99,im->xlab_user.stst,localtime(&tilab));
1887 # error "your libc has no strftime I guess we'll abort the exercise here."
1889 width=strlen(graph_label) * SmallFont->w;
1890 gr_pos=xtr(im,tilab) - width/2;
1891 if (gr_pos >= im->xorigin
1892 && gr_pos + width <= im->xorigin+im->xsize)
1893 gdImageString(gif, SmallFont,
1894 gr_pos, polyPoints[0].y+4,
1895 (unsigned char *)graph_label, graph_col[GRC_FONT].i);
1907 /* draw x and y axis */
1908 gdImageLine(gif, im->xorigin+im->xsize,im->yorigin,
1909 im->xorigin+im->xsize,im->yorigin-im->ysize,
1910 graph_col[GRC_GRID].i);
1912 gdImageLine(gif, im->xorigin,im->yorigin-im->ysize,
1913 im->xorigin+im->xsize,im->yorigin-im->ysize,
1914 graph_col[GRC_GRID].i);
1916 gdImageLine(gif, im->xorigin-4,im->yorigin,
1917 im->xorigin+im->xsize+4,im->yorigin,
1918 graph_col[GRC_FONT].i);
1920 gdImageLine(gif, im->xorigin,im->yorigin,
1921 im->xorigin,im->yorigin-im->ysize,
1922 graph_col[GRC_GRID].i);
1924 /* arrow for X axis direction */
1925 gdImageLine(gif, im->xorigin+im->xsize+4, im->yorigin-3, im->xorigin+im->xsize+4, im->yorigin+3,graph_col[GRC_ARROW].i);
1926 gdImageLine(gif, im->xorigin+im->xsize+4, im->yorigin-3, im->xorigin+im->xsize+9, im->yorigin,graph_col[GRC_ARROW].i);
1927 gdImageLine(gif, im->xorigin+im->xsize+4, im->yorigin+3, im->xorigin+im->xsize+9, im->yorigin,graph_col[GRC_ARROW].i);
1929 /* gdImageLine(gif, im->xorigin+im->xsize-1, im->yorigin-3, im->xorigin+im->xsize-1, im->yorigin+3,graph_col[GRC_MGRID].i);
1930 gdImageLine(gif, im->xorigin+im->xsize, im->yorigin-2, im->xorigin+im->xsize, im->yorigin+2,graph_col[GRC_MGRID].i);
1931 gdImageLine(gif, im->xorigin+im->xsize+1, im->yorigin-2, im->xorigin+im->xsize+1, im->yorigin+2,graph_col[GRC_MGRID].i);
1932 gdImageLine(gif, im->xorigin+im->xsize+2, im->yorigin-2, im->xorigin+im->xsize+2, im->yorigin+2,graph_col[GRC_MGRID].i);
1933 gdImageLine(gif, im->xorigin+im->xsize+3, im->yorigin-1, im->xorigin+im->xsize+3, im->yorigin+1,graph_col[GRC_MGRID].i);
1934 gdImageLine(gif, im->xorigin+im->xsize+4, im->yorigin-1, im->xorigin+im->xsize+4, im->yorigin+1,graph_col[GRC_MGRID].i);
1935 gdImageLine(gif, im->xorigin+im->xsize+5, im->yorigin, im->xorigin+im->xsize+5, im->yorigin,graph_col[GRC_MGRID].i); */
1950 gdPoint polyPoints[4]; /* points for filled graph and more*/
1952 /* draw 3d border */
1953 gdImageLine(gif,0,0,im->xgif-1,0,graph_col[GRC_SHADEA].i);
1954 gdImageLine(gif,1,1,im->xgif-2,1,graph_col[GRC_SHADEA].i);
1955 gdImageLine(gif,0,0,0,im->ygif-1,graph_col[GRC_SHADEA].i);
1956 gdImageLine(gif,1,1,1,im->ygif-2,graph_col[GRC_SHADEA].i);
1957 gdImageLine(gif,im->xgif-1,0,im->xgif-1,im->ygif-1,graph_col[GRC_SHADEB].i);
1958 gdImageLine(gif,0,im->ygif-1,im->xgif-1,im->ygif-1,graph_col[GRC_SHADEB].i);
1959 gdImageLine(gif,im->xgif-2,1,im->xgif-2,im->ygif-2,graph_col[GRC_SHADEB].i);
1960 gdImageLine(gif,1,im->ygif-2,im->xgif-2,im->ygif-2,graph_col[GRC_SHADEB].i);
1963 if (im->draw_x_grid == 1 )
1964 vertical_grid(gif, im);
1966 if (im->draw_y_grid == 1){
1967 if(im->logarithmic){
1968 res = horizontal_log_grid(gif,im);
1970 res = horizontal_grid(gif,im);
1973 /* dont draw horizontal grid if there is no min and max val */
1975 char *nodata = "No Data found";
1976 gdImageString(gif, LargeFont,
1978 - (strlen(nodata)*LargeFont->w)/2,
1979 (2*im->yorigin-im->ysize) / 2,
1980 (unsigned char *)nodata, graph_col[GRC_FONT].i);
1984 /* yaxis description */
1985 gdImageStringUp(gif, SmallFont,
1987 (im->yorigin - im->ysize/2
1988 +(strlen(im->ylegend)*SmallFont->w)/2 ),
1989 (unsigned char *)im->ylegend, graph_col[GRC_FONT].i);
1993 gdImageString(gif, LargeFont,
1995 - (strlen(im->title)*LargeFont->w)/2,
1997 (unsigned char *)im->title, graph_col[GRC_FONT].i);
2000 if( !(im->extra_flags & NOLEGEND) ) {
2001 for(i=0;i<im->gdes_c;i++){
2002 if(im->gdes[i].legend[0] =='\0')
2005 if(im->gdes[i].gf != GF_GPRINT && im->gdes[i].gf != GF_COMMENT){
2007 polyPoints[0].x = im->gdes[i].legloc.x;
2008 polyPoints[0].y = im->gdes[i].legloc.y+1;
2009 polyPoints[1].x = polyPoints[0].x+boxH;
2010 polyPoints[2].x = polyPoints[0].x+boxH;
2011 polyPoints[3].x = polyPoints[0].x;
2012 polyPoints[1].y = polyPoints[0].y;
2013 polyPoints[2].y = polyPoints[0].y+boxV;
2014 polyPoints[3].y = polyPoints[0].y+boxV;
2015 gdImageFilledPolygon(gif,polyPoints,4,im->gdes[i].col.i);
2016 gdImagePolygon(gif,polyPoints,4,graph_col[GRC_FRAME].i);
2018 gdImageString(gif, SmallFont,
2019 polyPoints[0].x+boxH+6,
2021 (unsigned char *)im->gdes[i].legend,
2022 graph_col[GRC_FONT].i);
2024 polyPoints[0].x = im->gdes[i].legloc.x;
2025 polyPoints[0].y = im->gdes[i].legloc.y;
2027 gdImageString(gif, SmallFont,
2030 (unsigned char *)im->gdes[i].legend,
2031 graph_col[GRC_FONT].i);
2037 gator(gif, (int) im->xgif-5, 5);
2043 MkLineBrush(image_desc_t *im,long cosel, enum gf_en typsel){
2048 brush=gdImageCreate(1,1);
2051 brush=gdImageCreate(2,2);
2054 brush=gdImageCreate(3,3);
2060 gdImageColorTransparent(brush,
2061 gdImageColorAllocate(brush, 0, 0, 0));
2063 pen = gdImageColorAllocate(brush,
2064 im->gdes[cosel].col.red,
2065 im->gdes[cosel].col.green,
2066 im->gdes[cosel].col.blue);
2070 gdImageSetPixel(brush,0,0,pen);
2073 gdImageSetPixel(brush,0,0,pen);
2074 gdImageSetPixel(brush,0,1,pen);
2075 gdImageSetPixel(brush,1,0,pen);
2076 gdImageSetPixel(brush,1,1,pen);
2079 gdImageSetPixel(brush,1,0,pen);
2080 gdImageSetPixel(brush,0,1,pen);
2081 gdImageSetPixel(brush,1,1,pen);
2082 gdImageSetPixel(brush,2,1,pen);
2083 gdImageSetPixel(brush,1,2,pen);
2090 /*****************************************************
2091 * lazy check make sure we rely need to create this graph
2092 *****************************************************/
2094 int lazy_check(image_desc_t *im){
2097 struct stat gifstat;
2099 if (im->lazy == 0) return 0; /* no lazy option */
2100 if (stat(im->graphfile,&gifstat) != 0)
2101 return 0; /* can't stat */
2102 /* one pixel in the existing graph is more then what we would
2104 if (time(NULL) - gifstat.st_mtime >
2105 (im->end - im->start) / im->xsize)
2107 if ((fd = fopen(im->graphfile,"rb")) == NULL)
2108 return 0; /* the file does not exist */
2109 switch (im->imgformat) {
2111 size = GifSize(fd,&(im->xgif),&(im->ygif));
2114 size = PngSize(fd,&(im->xgif),&(im->ygif));
2121 /* draw that picture thing ... */
2123 graph_paint(image_desc_t *im, char ***calcpr)
2126 int lazy = lazy_check(im);
2130 gdImagePtr gif,brush;
2132 double areazero = 0.0;
2133 enum gf_en stack_gf = GF_PRINT;
2134 graph_desc_t *lastgdes = NULL;
2135 gdPoint canvas[4], back[4]; /* points for canvas*/
2137 /* if we are lazy and there is nothing to PRINT ... quit now */
2138 if (lazy && im->prt_c==0) return 0;
2140 /* pull the data from the rrd files ... */
2142 if(data_fetch(im)==-1)
2145 /* evaluate VDEF and CDEF operations ... */
2146 if(data_calc(im)==-1)
2149 /* calculate and PRINT and GPRINT definitions. We have to do it at
2150 * this point because it will affect the length of the legends
2151 * if there are no graph elements we stop here ...
2152 * if we are lazy, try to quit ...
2154 i=print_calc(im,calcpr);
2156 if(i==0 || lazy) return 0;
2158 /* get actual drawing data and find min and max values*/
2159 if(data_proc(im)==-1)
2162 if(!im->logarithmic){si_unit(im);} /* identify si magnitude Kilo, Mega Giga ? */
2164 if(!im->rigid && ! im->logarithmic)
2165 expand_range(im); /* make sure the upper and lower limit are
2168 /* init xtr and ytr */
2169 /* determine the actual size of the gif to draw. The size given
2170 on the cmdline is the graph area. But we need more as we have
2171 draw labels and other things outside the graph area */
2174 im->xorigin = 10 + 9 * SmallFont->w+SmallFont->h;
2177 im->yorigin = 14 + im->ysize;
2180 if(im->title[0] != '\0')
2181 im->yorigin += (LargeFont->h+4);
2183 im->xgif=20+im->xsize + im->xorigin;
2184 im->ygif= im->yorigin+2*SmallFont->h;
2186 /* determine where to place the legends onto the graphics.
2187 and set im->ygif to match space requirements for text */
2188 if(leg_place(im)==-1)
2191 gif=gdImageCreate(im->xgif,im->ygif);
2193 gdImageInterlace(gif, im->interlaced);
2195 /* allocate colors for the screen elements */
2196 for(i=0;i<DIM(graph_col);i++)
2197 /* check for user override values */
2198 if(im->graph_col[i].red != -1)
2200 gdImageColorAllocate( gif,
2201 im->graph_col[i].red,
2202 im->graph_col[i].green,
2203 im->graph_col[i].blue);
2206 gdImageColorAllocate( gif,
2212 /* allocate colors for the graph */
2213 for(i=0;i<im->gdes_c;i++)
2214 /* only for elements which have a color defined */
2215 if (im->gdes[i].col.red != -1)
2217 gdImageColorAllocate(gif,
2218 im->gdes[i].col.red,
2219 im->gdes[i].col.green,
2220 im->gdes[i].col.blue);
2223 /* the actual graph is created by going through the individual
2224 graph elements and then drawing them */
2228 back[1].x = back[0].x+im->xgif;
2229 back[1].y = back[0].y;
2230 back[2].x = back[1].x;
2231 back[2].y = back[0].y+im->ygif;
2232 back[3].x = back[0].x;
2233 back[3].y = back[2].y;
2235 gdImageFilledPolygon(gif,back,4,graph_col[GRC_BACK].i);
2237 canvas[0].x = im->xorigin;
2238 canvas[0].y = im->yorigin;
2239 canvas[1].x = canvas[0].x+im->xsize;
2240 canvas[1].y = canvas[0].y;
2241 canvas[2].x = canvas[1].x;
2242 canvas[2].y = canvas[0].y-im->ysize;
2243 canvas[3].x = canvas[0].x;
2244 canvas[3].y = canvas[2].y;
2246 gdImageFilledPolygon(gif,canvas,4,graph_col[GRC_CANVAS].i);
2248 if (im->minval > 0.0)
2249 areazero = im->minval;
2250 if (im->maxval < 0.0)
2251 areazero = im->maxval;
2255 for(i=0;i<im->gdes_c;i++){
2257 switch(im->gdes[i].gf){
2268 for (ii = 0; ii < im->xsize; ii++)
2270 if (!isnan(im->gdes[i].p_data[ii]) &&
2271 im->gdes[i].p_data[ii] > 0.0)
2273 /* generate a tick */
2274 gdImageLine(gif, im -> xorigin + ii,
2275 im -> yorigin - (im -> gdes[i].yrule * im -> ysize),
2278 im -> gdes[i].col.i);
2286 stack_gf = im->gdes[i].gf;
2288 /* fix data points at oo and -oo */
2289 for(ii=0;ii<im->xsize;ii++){
2290 if (isinf(im->gdes[i].p_data[ii])){
2291 if (im->gdes[i].p_data[ii] > 0) {
2292 im->gdes[i].p_data[ii] = im->maxval ;
2294 im->gdes[i].p_data[ii] = im->minval ;
2300 if (im->gdes[i].col.i != -1){
2301 /* GF_LINE and frined */
2302 if(stack_gf == GF_LINE1 || stack_gf == GF_LINE2 || stack_gf == GF_LINE3 ){
2303 brush = MkLineBrush(im,i,stack_gf);
2304 gdImageSetBrush(gif, brush);
2305 for(ii=1;ii<im->xsize;ii++){
2306 if (isnan(im->gdes[i].p_data[ii-1]) ||
2307 isnan(im->gdes[i].p_data[ii]))
2310 ii+im->xorigin-1,ytr(im,im->gdes[i].p_data[ii-1]),
2311 ii+im->xorigin,ytr(im,im->gdes[i].p_data[ii]),
2315 gdImageDestroy(brush);
2318 /* GF_AREA STACK type*/
2319 if (im->gdes[i].gf == GF_STACK )
2320 for(ii=0;ii<im->xsize;ii++){
2321 if(isnan(im->gdes[i].p_data[ii])){
2322 im->gdes[i].p_data[ii] = lastgdes->p_data[ii];
2326 if (lastgdes->p_data[ii] == im->gdes[i].p_data[ii]){
2330 ii+im->xorigin,ytr(im,lastgdes->p_data[ii]),
2331 ii+im->xorigin,ytr(im,im->gdes[i].p_data[ii]),
2335 else /* simple GF_AREA */
2336 for(ii=0;ii<im->xsize;ii++){
2337 if (isnan(im->gdes[i].p_data[ii])) {
2338 im->gdes[i].p_data[ii] = 0;
2342 ii+im->xorigin,ytr(im,areazero),
2343 ii+im->xorigin,ytr(im,im->gdes[i].p_data[ii]),
2347 lastgdes = &(im->gdes[i]);
2354 /* the RULES are the last thing to paint ... */
2355 for(i=0;i<im->gdes_c;i++){
2357 switch(im->gdes[i].gf){
2359 printf("DEBUG: HRULE at %f\n",im->gdes[i].yrule);
2360 if(isnan(im->gdes[i].yrule)) { /* fetch variable */
2361 im->gdes[i].yrule = im->gdes[im->gdes[i].vidx].vf.val;
2363 if(im->gdes[i].yrule >= im->minval
2364 && im->gdes[i].yrule <= im->maxval)
2366 im->xorigin,ytr(im,im->gdes[i].yrule),
2367 im->xorigin+im->xsize,ytr(im,im->gdes[i].yrule),
2371 if(im->gdes[i].xrule == 0) { /* fetch variable */
2372 im->gdes[i].xrule = im->gdes[im->gdes[i].vidx].vf.when;
2374 if(im->gdes[i].xrule >= im->start
2375 && im->gdes[i].xrule <= im->end)
2377 xtr(im,im->gdes[i].xrule),im->yorigin,
2378 xtr(im,im->gdes[i].xrule),im->yorigin-im->ysize,
2386 if (strcmp(im->graphfile,"-")==0) {
2388 /* Change translation mode for stdout to BINARY */
2389 _setmode( _fileno( stdout ), O_BINARY );
2393 if ((fo = fopen(im->graphfile,"wb")) == NULL) {
2394 rrd_set_error("Opening '%s' for write: %s",im->graphfile, strerror(errno));
2398 switch (im->imgformat) {
2400 gdImageGif(gif, fo);
2403 gdImagePng(gif, fo);
2406 if (strcmp(im->graphfile,"-") != 0)
2408 gdImageDestroy(gif);
2414 /*****************************************************
2416 *****************************************************/
2419 gdes_alloc(image_desc_t *im){
2421 long def_step = (im->end-im->start)/im->xsize;
2423 if (im->step > def_step) /* step can be increassed ... no decreassed */
2424 def_step = im->step;
2428 if ((im->gdes = (graph_desc_t *) rrd_realloc(im->gdes, (im->gdes_c)
2429 * sizeof(graph_desc_t)))==NULL){
2430 rrd_set_error("realloc graph_descs");
2435 im->gdes[im->gdes_c-1].step=def_step;
2436 im->gdes[im->gdes_c-1].start=im->start;
2437 im->gdes[im->gdes_c-1].end=im->end;
2438 im->gdes[im->gdes_c-1].vname[0]='\0';
2439 im->gdes[im->gdes_c-1].data=NULL;
2440 im->gdes[im->gdes_c-1].ds_namv=NULL;
2441 im->gdes[im->gdes_c-1].data_first=0;
2442 im->gdes[im->gdes_c-1].p_data=NULL;
2443 im->gdes[im->gdes_c-1].rpnp=NULL;
2444 im->gdes[im->gdes_c-1].col.red = -1;
2445 im->gdes[im->gdes_c-1].col.i=-1;
2446 im->gdes[im->gdes_c-1].legend[0]='\0';
2447 im->gdes[im->gdes_c-1].rrd[0]='\0';
2448 im->gdes[im->gdes_c-1].ds=-1;
2449 im->gdes[im->gdes_c-1].p_data=NULL;
2453 /* copies input untill the first unescaped colon is found
2454 or until input ends. backslashes have to be escaped as well */
2456 scan_for_col(char *input, int len, char *output)
2461 input[inp] != ':' &&
2464 if (input[inp] == '\\' &&
2465 input[inp+1] != '\0' &&
2466 (input[inp+1] == '\\' ||
2467 input[inp+1] == ':')){
2468 output[outp++] = input[++inp];
2471 output[outp++] = input[inp];
2474 output[outp] = '\0';
2479 rrd_graph(int argc, char **argv, char ***prdata, int *xsize, int *ysize)
2485 time_t start_tmp=0,end_tmp=0;
2486 char scan_gtm[12],scan_mtm[12],scan_ltm[12],col_nam[12];
2488 unsigned int col_red,col_green,col_blue;
2490 int linepass = 0; /* stack can only follow directly after LINE* AREA or STACK */
2491 struct time_value start_tv, end_tv;
2492 char *parsetime_error = NULL;
2497 parsetime("end-24h", &start_tv);
2498 parsetime("now", &end_tv);
2500 im.xlab_user.minsec = -1;
2506 im.ylegend[0] = '\0';
2511 im.unitsexponent= 9999;
2517 im.ygridstep = DNAN;
2524 im.imgformat = IF_GIF; /* we default to GIF output */
2526 for(i=0;i<DIM(graph_col);i++)
2527 im.graph_col[i].red=-1;
2531 static struct option long_options[] =
2533 {"start", required_argument, 0, 's'},
2534 {"end", required_argument, 0, 'e'},
2535 {"x-grid", required_argument, 0, 'x'},
2536 {"y-grid", required_argument, 0, 'y'},
2537 {"vertical-label",required_argument,0,'v'},
2538 {"width", required_argument, 0, 'w'},
2539 {"height", required_argument, 0, 'h'},
2540 {"interlaced", no_argument, 0, 'i'},
2541 {"upper-limit",required_argument, 0, 'u'},
2542 {"lower-limit",required_argument, 0, 'l'},
2543 {"rigid", no_argument, 0, 'r'},
2544 {"base", required_argument, 0, 'b'},
2545 {"logarithmic",no_argument, 0, 'o'},
2546 {"color", required_argument, 0, 'c'},
2547 {"title", required_argument, 0, 't'},
2548 {"imginfo", required_argument, 0, 'f'},
2549 {"imgformat", required_argument, 0, 'a'},
2550 {"lazy", no_argument, 0, 'z'},
2551 {"no-legend", no_argument, 0, 'g'},
2552 {"alt-y-grid", no_argument, 0, 257 },
2553 {"alt-autoscale", no_argument, 0, 258 },
2554 {"alt-autoscale-max", no_argument, 0, 259 },
2555 {"units-exponent",required_argument, 0, 260},
2556 {"step", required_argument, 0, 261},
2558 int option_index = 0;
2562 opt = getopt_long(argc, argv,
2563 "s:e:x:y:v:w:h:iu:l:rb:oc:t:f:a:z:g",
2564 long_options, &option_index);
2571 im.extra_flags |= ALTYGRID;
2574 im.extra_flags |= ALTAUTOSCALE;
2577 im.extra_flags |= ALTAUTOSCALE_MAX;
2580 im.extra_flags |= NOLEGEND;
2583 im.unitsexponent = atoi(optarg);
2586 im.step = atoi(optarg);
2589 if ((parsetime_error = parsetime(optarg, &start_tv))) {
2590 rrd_set_error( "start time: %s", parsetime_error );
2595 if ((parsetime_error = parsetime(optarg, &end_tv))) {
2596 rrd_set_error( "end time: %s", parsetime_error );
2601 if(strcmp(optarg,"none") == 0){
2607 "%10[A-Z]:%ld:%10[A-Z]:%ld:%10[A-Z]:%ld:%ld:%n",
2609 &im.xlab_user.gridst,
2611 &im.xlab_user.mgridst,
2613 &im.xlab_user.labst,
2614 &im.xlab_user.precis,
2615 &stroff) == 7 && stroff != 0){
2616 strncpy(im.xlab_form, optarg+stroff, sizeof(im.xlab_form) - 1);
2617 if((im.xlab_user.gridtm = tmt_conv(scan_gtm)) == -1){
2618 rrd_set_error("unknown keyword %s",scan_gtm);
2620 } else if ((im.xlab_user.mgridtm = tmt_conv(scan_mtm)) == -1){
2621 rrd_set_error("unknown keyword %s",scan_mtm);
2623 } else if ((im.xlab_user.labtm = tmt_conv(scan_ltm)) == -1){
2624 rrd_set_error("unknown keyword %s",scan_ltm);
2627 im.xlab_user.minsec = 1;
2628 im.xlab_user.stst = im.xlab_form;
2630 rrd_set_error("invalid x-grid format");
2636 if(strcmp(optarg,"none") == 0){
2644 &im.ylabfact) == 2) {
2645 if(im.ygridstep<=0){
2646 rrd_set_error("grid step must be > 0");
2648 } else if (im.ylabfact < 1){
2649 rrd_set_error("label factor must be > 0");
2653 rrd_set_error("invalid y-grid format");
2658 strncpy(im.ylegend,optarg,150);
2659 im.ylegend[150]='\0';
2662 im.maxval = atof(optarg);
2665 im.minval = atof(optarg);
2668 im.base = atol(optarg);
2669 if(im.base != 1024 && im.base != 1000 ){
2670 rrd_set_error("the only sensible value for base apart from 1000 is 1024");
2675 long_tmp = atol(optarg);
2676 if (long_tmp < 10) {
2677 rrd_set_error("width below 10 pixels");
2680 im.xsize = long_tmp;
2683 long_tmp = atol(optarg);
2684 if (long_tmp < 10) {
2685 rrd_set_error("height below 10 pixels");
2688 im.ysize = long_tmp;
2697 im.imginfo = optarg;
2700 if((im.imgformat = if_conv(optarg)) == -1) {
2701 rrd_set_error("unsupported graphics format '%s'",optarg);
2710 if (isnan(im.minval))
2715 "%10[A-Z]#%2x%2x%2x",
2716 col_nam,&col_red,&col_green,&col_blue) == 4){
2718 if((ci=grc_conv(col_nam)) != -1){
2719 im.graph_col[ci].red=col_red;
2720 im.graph_col[ci].green=col_green;
2721 im.graph_col[ci].blue=col_blue;
2723 rrd_set_error("invalid color name '%s'",col_nam);
2726 rrd_set_error("invalid color def format");
2731 strncpy(im.title,optarg,150);
2737 rrd_set_error("unknown option '%c'", optopt);
2739 rrd_set_error("unknown option '%s'",argv[optind-1]);
2744 if (optind >= argc) {
2745 rrd_set_error("missing filename");
2749 if (im.logarithmic == 1 && (im.minval <= 0 || isnan(im.minval))){
2750 rrd_set_error("for a logarithmic yaxis you must specify a lower-limit > 0");
2754 strncpy(im.graphfile,argv[optind],MAXPATH-1);
2755 im.graphfile[MAXPATH-1]='\0';
2757 if (proc_start_end(&start_tv,&end_tv,&start_tmp,&end_tmp) == -1){
2761 if (start_tmp < 3600*24*365*10){
2762 rrd_set_error("the first entry to fetch should be after 1980 (%ld)",start_tmp);
2766 if (end_tmp < start_tmp) {
2767 rrd_set_error("start (%ld) should be less than end (%ld)",
2768 start_tmp, end_tmp);
2772 im.start = start_tmp;
2776 for(i=optind+1;i<argc;i++){
2779 char varname[30],*rpnex;
2781 if(sscanf(argv[i],"%10[A-Z0-9]:%n",symname,&argstart)==1){
2782 if((im.gdes[im.gdes_c-1].gf=gf_conv(symname))==-1){
2784 rrd_set_error("unknown function '%s'",symname);
2788 rrd_set_error("can't parse '%s'",argv[i]);
2793 /* reset linepass if a non LINE/STACK/AREA operator gets parsed
2795 if (im.gdes[im.gdes_c-1].gf != GF_LINE1 &&
2796 im.gdes[im.gdes_c-1].gf != GF_LINE2 &&
2797 im.gdes[im.gdes_c-1].gf != GF_LINE3 &&
2798 im.gdes[im.gdes_c-1].gf != GF_AREA &&
2799 im.gdes[im.gdes_c-1].gf != GF_STACK) {
2804 switch(im.gdes[im.gdes_c-1].gf){
2809 sscanf(&argv[i][argstart], DEF_NAM_FMT ":%n"
2816 rrd_set_error("can't parse vname in '%s'",&argv[i][argstart]);
2820 if ((im.gdes[im.gdes_c-1].vidx=find_var(&im,varname))==-1){
2822 rrd_set_error("Unknown variable '%s' in (G)PRINT",varname);
2827 sscanf(&argv[i][argstart+strstart],CF_NAM_FMT ":%n"
2831 if (im.gdes[im.gdes[im.gdes_c-1].vidx].gf==GF_VDEF) {
2832 /* No consolidation function should be present */
2834 rrd_set_error("(G)PRINT of VDEF needs no CF");
2839 /* A consolidation function should follow */
2842 rrd_set_error("Missing or incorrect CF in (G)PRINTing '%s' (%s)",varname,&argv[i][argstart]);
2845 if((im.gdes[im.gdes_c-1].cf=cf_conv(symname))==-1){
2854 &argv[i][argstart+strstart]
2856 ,im.gdes[im.gdes_c-1].format
2860 if(strlen(&argv[i][argstart])>FMT_LEG_LEN) argv[i][argstart+FMT_LEG_LEN-3]='\0' ;
2861 strcpy(im.gdes[im.gdes_c-1].legend, &argv[i][argstart]);
2864 /* scan for either "HRULE:vname#..." or "HRULE:num#..."
2866 * If a vname is used, the value NaN is set; this is catched
2867 * when graphing. Setting value NaN from the script is not
2871 sscanf(&argv[i][argstart], "%lf#%n"
2872 ,&im.gdes[im.gdes_c-1].yrule
2875 if (strstart==0) { /* no number, should be vname */
2876 sscanf(&argv[i][argstart], DEF_NAM_FMT "#%n"
2881 im.gdes[im.gdes_c-1].yrule = DNAN;/* signal use of vname */
2882 if((im.gdes[im.gdes_c-1].vidx=find_var(&im,varname))==-1){
2884 rrd_set_error("unknown variable '%s' in HRULE",varname);
2887 if(im.gdes[im.gdes[im.gdes_c-1].vidx].gf != GF_VDEF) {
2889 rrd_set_error("Only VDEF is allowed in HRULE",varname);
2894 printf("DEBUG: matched HRULE:num\n");
2895 printf("DEBUG: strstart==%i\n",strstart);
2899 rrd_set_error("can't parse '%s'",&argv[i][argstart]);
2904 &argv[i][argstart+strstart],
2910 im.gdes[im.gdes_c-1].col.red = col_red;
2911 im.gdes[im.gdes_c-1].col.green = col_green;
2912 im.gdes[im.gdes_c-1].col.blue = col_blue;
2914 im.gdes[im.gdes_c-1].legend[0] = '\0';
2916 scan_for_col(&argv[i][argstart+strstart+n],FMT_LEG_LEN,im.gdes[im.gdes_c-1].legend);
2920 rrd_set_error("can't parse '%s'",&argv[i][argstart]);
2927 /* scan for either "VRULE:vname#..." or "VRULE:num#..."
2929 * If a vname is used, the value 0 is set; this is catched
2930 * when graphing. Setting value 0 from the script is not
2934 sscanf(&argv[i][argstart], "%lu#%n"
2935 ,(long unsigned int *)&im.gdes[im.gdes_c-1].xrule
2938 if (strstart==0) { /* no number, should be vname */
2939 sscanf(&argv[i][argstart], DEF_NAM_FMT "#%n"
2943 if (strstart!=0) { /* vname matched */
2944 im.gdes[im.gdes_c-1].xrule = 0;/* signal use of vname */
2945 if((im.gdes[im.gdes_c-1].vidx=find_var(&im,varname))==-1){
2947 rrd_set_error("unknown variable '%s' in VRULE",varname);
2950 if(im.gdes[im.gdes[im.gdes_c-1].vidx].gf != GF_VDEF) {
2952 rrd_set_error("Only VDEF is allowed in VRULE",varname);
2957 if (im.gdes[im.gdes_c-1].xrule==0)
2963 rrd_set_error("can't parse '%s'",&argv[i][argstart]);
2968 &argv[i][argstart+strstart],
2974 im.gdes[im.gdes_c-1].col.red = col_red;
2975 im.gdes[im.gdes_c-1].col.green = col_green;
2976 im.gdes[im.gdes_c-1].col.blue = col_blue;
2978 im.gdes[im.gdes_c-1].legend[0] = '\0';
2980 scan_for_col(&argv[i][argstart+strstart+n],FMT_LEG_LEN,im.gdes[im.gdes_c-1].legend);
2984 rrd_set_error("can't parse '%s'",&argv[i][argstart]);
2990 if((scancount=sscanf(
2992 "%29[^:#]#%2x%2x%2x:%lf:%n",
2997 &(im.gdes[im.gdes_c-1].yrule),
3000 im.gdes[im.gdes_c-1].col.red = col_red;
3001 im.gdes[im.gdes_c-1].col.green = col_green;
3002 im.gdes[im.gdes_c-1].col.blue = col_blue;
3004 im.gdes[im.gdes_c-1].legend[0] = '\0';
3006 scan_for_col(&argv[i][argstart+strstart],FMT_LEG_LEN,im.gdes[im.gdes_c-1].legend);
3008 if((im.gdes[im.gdes_c-1].vidx=find_var(&im,varname))==-1){
3010 rrd_set_error("unknown variable '%s'",varname);
3013 if (im.gdes[im.gdes_c-1].yrule <= 0.0 || im.gdes[im.gdes_c-1].yrule > 1.0)
3016 rrd_set_error("Tick mark scaling factor out of range");
3020 im.gdes[im.gdes_c-1].col.red = -1;
3022 /* default tick marks: 10% of the y-axis */
3023 im.gdes[im.gdes_c-1].yrule = 0.1;
3027 rrd_set_error("can't parse '%s'",&argv[i][argstart]);
3029 } /* endif sscanf */
3034 rrd_set_error("STACK must follow AREA, LINE or STACK");
3042 if((scancount=sscanf(
3044 "%29[^:#]#%2x%2x%2x:%n",
3050 im.gdes[im.gdes_c-1].col.red = col_red;
3051 im.gdes[im.gdes_c-1].col.green = col_green;
3052 im.gdes[im.gdes_c-1].col.blue = col_blue;
3054 im.gdes[im.gdes_c-1].legend[0] = '\0';
3056 scan_for_col(&argv[i][argstart+strstart],FMT_LEG_LEN,im.gdes[im.gdes_c-1].legend);
3058 if((im.gdes[im.gdes_c-1].vidx=find_var(&im,varname))==-1){
3060 rrd_set_error("unknown variable '%s'",varname);
3064 im.gdes[im.gdes_c-1].col.red = -1;
3068 rrd_set_error("can't parse '%s'",&argv[i][argstart]);
3073 if((rpnex = malloc(strlen(&argv[i][argstart])*sizeof(char)))==NULL){
3074 rrd_set_error("malloc for CDEF");
3079 DEF_NAM_FMT "=%[^: ]",
3080 im.gdes[im.gdes_c-1].vname,
3084 rrd_set_error("can't parse CDEF '%s'",&argv[i][argstart]);
3087 /* checking for duplicate variable names */
3088 if(find_var(&im,im.gdes[im.gdes_c-1].vname) != -1){
3090 rrd_set_error("duplicate variable '%s'",
3091 im.gdes[im.gdes_c-1].vname);
3094 if((im.gdes[im.gdes_c-1].rpnp =
3095 rpn_parse((void*)&im,rpnex,&find_var_wrapper))== NULL){
3096 rrd_set_error("invalid rpn expression '%s'", rpnex);
3104 * strstart is set to zero and will NOT be changed
3105 * if the comma is not matched. This means that it
3106 * remains zero. Although strstart is initialized to
3107 * zero at the beginning of this loop, we do it again
3108 * here just in case someone changes the code...
3110 * According to the docs we cannot rely on the
3111 * returned value from sscanf; it can be 2 or 3,
3112 * depending on %n incrementing it or not.
3117 DEF_NAM_FMT "=" DEF_NAM_FMT ",%n",
3118 im.gdes[im.gdes_c-1].vname,
3122 /* checking both variable names */
3123 if (find_var(&im,im.gdes[im.gdes_c-1].vname) != -1){
3125 rrd_set_error("duplicate variable '%s'",
3126 im.gdes[im.gdes_c-1].vname);
3129 if ((im.gdes[im.gdes_c-1].vidx=find_var(&im,varname)) == -1){
3131 rrd_set_error("variable '%s' not known in VDEF '%s'",
3133 im.gdes[im.gdes_c-1].vname);
3136 if(im.gdes[im.gdes[im.gdes_c-1].vidx].gf != GF_DEF
3137 && im.gdes[im.gdes[im.gdes_c-1].vidx].gf != GF_CDEF){
3138 rrd_set_error("variable '%s' not DEF nor CDEF in VDEF '%s'",
3140 im.gdes[im.gdes_c-1].vname);
3145 /* parsed upto and including the first comma. Now
3146 * see what function is requested. This function
3147 * sets the error string.
3149 if (vdef_parse(&im.gdes[im.gdes_c-1],&argv[i][argstart+strstart])<0) {
3156 rrd_set_error("can't parse VDEF '%s'",&argv[i][argstart]);
3164 im.gdes[im.gdes_c-1].vname,
3165 &strstart)== 1 && strstart){ /* is the = did not match %n returns 0 */
3166 if(sscanf(&argv[i][argstart
3168 +scan_for_col(&argv[i][argstart+strstart],
3169 MAXPATH,im.gdes[im.gdes_c-1].rrd)],
3170 ":" DS_NAM_FMT ":" CF_NAM_FMT,
3171 im.gdes[im.gdes_c-1].ds_nam,
3174 rrd_set_error("can't parse DEF '%s' -2",&argv[i][argstart]);
3179 rrd_set_error("can't parse DEF '%s'",&argv[i][argstart]);
3183 /* checking for duplicate DEF CDEFS */
3184 if (find_var(&im,im.gdes[im.gdes_c-1].vname) != -1){
3186 rrd_set_error("duplicate variable '%s'",
3187 im.gdes[im.gdes_c-1].vname);
3190 if((im.gdes[im.gdes_c-1].cf=cf_conv(symname))==-1){
3192 rrd_set_error("unknown cf '%s'",symname);
3201 rrd_set_error("can't make a graph without contents");
3206 /* parse rest of arguments containing information on what to draw*/
3207 if (graph_paint(&im,prdata)==-1){
3217 /* maybe prdata is not allocated yet ... lets do it now */
3218 if((*prdata = calloc(2,sizeof(char *)))==NULL){
3219 rrd_set_error("malloc imginfo");
3223 if(((*prdata)[0] = malloc((strlen(im.imginfo)+200+strlen(im.graphfile))*sizeof(char)))
3225 rrd_set_error("malloc imginfo");
3228 filename=im.graphfile+strlen(im.graphfile);
3229 while(filename > im.graphfile){
3230 if (*(filename-1)=='/' || *(filename-1)=='\\' ) break;
3234 sprintf((*prdata)[0],im.imginfo,filename,im.xgif,im.ygif);
3240 int bad_format(char *fmt) {
3244 while (*ptr != '\0') {
3245 if (*ptr == '%') {ptr++;
3246 if (*ptr == '\0') return 1;
3247 while ((*ptr >= '0' && *ptr <= '9') || *ptr == '.') {
3250 if (*ptr == '\0') return 1;
3253 if (*ptr == '\0') return 1;
3254 if (*ptr == 'e' || *ptr == 'f') {
3256 } else { return 1; }
3258 else if (*ptr == 's' || *ptr == 'S' || *ptr == '%') { ++ptr; }
3267 vdef_parse(gdes,str)
3268 struct graph_desc_t *gdes;
3271 /* A VDEF currently is either "func" or "param,func"
3272 * so the parsing is rather simple. Change if needed.
3279 sscanf(str,"%le,%29[A-Z]%n",¶m,func,&n);
3280 if (n==strlen(str)) { /* matched */
3284 sscanf(str,"%29[A-Z]%n",func,&n);
3285 if (n==strlen(str)) { /* matched */
3288 rrd_set_error("Unknown function string '%s' in VDEF '%s'"
3295 if (!strcmp("PERCENT",func)) gdes->vf.op = VDEF_PERCENT;
3296 else if (!strcmp("MAXIMUM",func)) gdes->vf.op = VDEF_MAXIMUM;
3297 else if (!strcmp("AVERAGE",func)) gdes->vf.op = VDEF_AVERAGE;
3298 else if (!strcmp("MINIMUM",func)) gdes->vf.op = VDEF_MINIMUM;
3299 else if (!strcmp("FIRST", func)) gdes->vf.op = VDEF_FIRST;
3300 else if (!strcmp("LAST", func)) gdes->vf.op = VDEF_LAST;
3302 rrd_set_error("Unknown function '%s' in VDEF '%s'\n"
3309 switch (gdes->vf.op) {
3311 if (isnan(param)) { /* no parameter given */
3312 rrd_set_error("Function '%s' needs parameter in VDEF '%s'\n"
3318 if (param>=0.0 && param<=100.0) {
3319 gdes->vf.param = param;
3320 gdes->vf.val = DNAN; /* undefined */
3321 gdes->vf.when = 0; /* undefined */
3323 rrd_set_error("Parameter '%f' out of range in VDEF '%s'\n"
3336 gdes->vf.param = DNAN;
3337 gdes->vf.val = DNAN;
3340 rrd_set_error("Function '%s' needs no parameter in VDEF '%s'\n"
3355 graph_desc_t *src,*dst;
3359 dst = &im->gdes[gdi];
3360 src = &im->gdes[dst->vidx];
3361 data = src->data + src->ds + src->ds_cnt; /* skip first value! */
3362 steps = (src->end - src->start) / src->step;
3365 printf("DEBUG: start == %lu, end == %lu, %lu steps\n"
3372 switch (im->gdes[gdi].vf.op) {
3373 case VDEF_PERCENT: {
3374 rrd_value_t * array;
3378 if ((array = malloc(steps*sizeof(double)))==NULL) {
3379 rrd_set_error("malloc VDEV_PERCENT");
3382 for (step=0;step < steps; step++) {
3383 array[step]=data[step*src->ds_cnt];
3385 qsort(array,step,sizeof(double),vdef_percent_compar);
3387 field = (steps-1)*dst->vf.param/100;
3388 dst->vf.val = array[field];
3389 dst->vf.when = 0; /* no time component */
3391 for(step=0;step<steps;step++)
3392 printf("DEBUG: %3li:%10.2f %c\n",step,array[step],step==field?'*':' ');
3398 while (step != steps && isnan(data[step*src->ds_cnt])) step++;
3399 if (step == steps) {
3403 dst->vf.val = data[steps*src->ds_cnt];
3404 dst->vf.when = src->start + (step+1)*src->step;
3406 while (step != steps) {
3407 if (finite(data[step*src->ds_cnt])) {
3408 if (data[step*src->ds_cnt] > dst->vf.val) {
3409 dst->vf.val = data[steps*src->ds_cnt];
3410 dst->vf.when = src->start + (step+1)*src->step;
3416 case VDEF_AVERAGE: {
3419 for (step=0;step<steps;step++) {
3420 if (finite(data[step*src->ds_cnt])) {
3421 sum += data[step*src->ds_cnt];
3427 dst->vf.val = sum/cnt;
3428 dst->vf.when = 0; /* no time component */
3437 while (step != steps && isnan(data[step*src->ds_cnt])) step++;
3438 if (step == steps) {
3442 dst->vf.val = data[steps*src->ds_cnt];
3443 dst->vf.when = src->start + (step+1)*src->step;
3445 while (step != steps) {
3446 if (finite(data[step*src->ds_cnt])) {
3447 if (data[step*src->ds_cnt] < dst->vf.val) {
3448 dst->vf.val = data[steps*src->ds_cnt];
3449 dst->vf.when = src->start + (step+1)*src->step;
3456 /* The time value returned here is one step before the
3457 * actual time value. This is the start of the first
3461 while (step != steps && isnan(data[step*src->ds_cnt])) step++;
3462 if (step == steps) { /* all entries were NaN */
3466 dst->vf.val = data[step*src->ds_cnt];
3467 dst->vf.when = src->start + step*src->step;
3471 /* The time value returned here is the
3472 * actual time value. This is the end of the last
3476 while (step >= 0 && isnan(data[step*src->ds_cnt])) step--;
3477 if (step < 0) { /* all entries were NaN */
3481 dst->vf.val = data[step*src->ds_cnt];
3482 dst->vf.when = src->start + (step+1)*src->step;
3489 /* NaN <= -INF <= finite_values <= INF */
3491 vdef_percent_compar(a,b)
3494 /* Equality is not returned; this doesn't hurt except
3495 * (maybe) for a little performance.
3498 /* First catch NaN values. They are smallest */
3499 if (isnan( *(double *)a )) return -1;
3500 if (isnan( *(double *)b )) return 1;
3502 /* NaN doestn't reach this part so INF and -INF are extremes.
3503 * The sign from isinf() is compatible with the sign we return
3505 if (isinf( *(double *)a )) return isinf( *(double *)a );
3506 if (isinf( *(double *)b )) return isinf( *(double *)b );
3508 /* If we reach this, both values must be finite */
3509 if ( *(double *)a < *(double *)b ) return -1; else return 1;