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 vidx = im->gdes[i].vidx;
1307 max_ii =((im->gdes[vidx].end
1308 - im->gdes[vidx].start)
1309 /im->gdes[vidx].step
1310 *im->gdes[vidx].ds_cnt);
1313 for(ii=im->gdes[vidx].ds+im->gdes[vidx].ds_cnt;
1314 ii < max_ii+im->gdes[vidx].ds_cnt;
1315 ii+=im->gdes[vidx].ds_cnt){
1316 if (! finite(im->gdes[vidx].data[ii]))
1318 if (isnan(printval)){
1319 printval = im->gdes[vidx].data[ii];
1324 switch (im->gdes[i].cf){
1327 case CF_DEVSEASONAL:
1331 printval += im->gdes[vidx].data[ii];
1334 printval = min( printval, im->gdes[vidx].data[ii]);
1338 printval = max( printval, im->gdes[vidx].data[ii]);
1341 printval = im->gdes[vidx].data[ii];
1344 if (im->gdes[i].cf == CF_AVERAGE || im -> gdes[i].cf > CF_LAST) {
1345 if (validsteps > 1) {
1346 printval = (printval / validsteps);
1349 if ((percent_s = strstr(im->gdes[i].format,"%S")) != NULL) {
1350 /* Magfact is set to -1 upon entry to print_calc. If it
1351 * is still less than 0, then we need to run auto_scale.
1352 * Otherwise, put the value into the correct units. If
1353 * the value is 0, then do not set the symbol or magnification
1354 * so next the calculation will be performed again. */
1355 if (magfact < 0.0) {
1356 auto_scale(im,&printval,&si_symb,&magfact);
1357 if (printval == 0.0)
1360 printval /= magfact;
1362 *(++percent_s) = 's';
1364 else if (strstr(im->gdes[i].format,"%s") != NULL) {
1365 auto_scale(im,&printval,&si_symb,&magfact);
1367 if (im->gdes[i].gf == GF_PRINT){
1368 (*prdata)[prlines-2] = malloc((FMT_LEG_LEN+2)*sizeof(char));
1369 if (bad_format(im->gdes[i].format)) {
1370 rrd_set_error("bad format for [G]PRINT in '%s'", im->gdes[i].format);
1373 #ifdef HAVE_SNPRINTF
1374 snprintf((*prdata)[prlines-2],FMT_LEG_LEN,im->gdes[i].format,printval,si_symb);
1376 sprintf((*prdata)[prlines-2],im->gdes[i].format,printval,si_symb);
1378 (*prdata)[prlines-1] = NULL;
1382 if (bad_format(im->gdes[i].format)) {
1383 rrd_set_error("bad format for [G]PRINT in '%s'", im->gdes[i].format);
1386 #ifdef HAVE_SNPRINTF
1387 snprintf(im->gdes[i].legend,FMT_LEG_LEN-2,im->gdes[i].format,printval,si_symb);
1389 sprintf(im->gdes[i].legend,im->gdes[i].format,printval,si_symb);
1411 return graphelement;
1415 /* place legends with color spots */
1417 leg_place(image_desc_t *im)
1420 int interleg = SmallFont->w*2;
1421 int box = SmallFont->h*1.2;
1422 int border = SmallFont->w*2;
1423 int fill=0, fill_last;
1425 int leg_x = border, leg_y = im->ygif;
1429 char prt_fctn; /*special printfunctions */
1432 if( !(im->extra_flags & NOLEGEND) ) {
1433 if ((legspace = malloc(im->gdes_c*sizeof(int)))==NULL){
1434 rrd_set_error("malloc for legspace");
1438 for(i=0;i<im->gdes_c;i++){
1441 leg_cc = strlen(im->gdes[i].legend);
1443 /* is there a controle code ant the end of the legend string ? */
1444 if (leg_cc >= 2 && im->gdes[i].legend[leg_cc-2] == '\\') {
1445 prt_fctn = im->gdes[i].legend[leg_cc-1];
1447 im->gdes[i].legend[leg_cc] = '\0';
1451 /* remove exess space */
1452 while (prt_fctn=='g' &&
1454 im->gdes[i].legend[leg_cc-1]==' '){
1456 im->gdes[i].legend[leg_cc]='\0';
1459 legspace[i]=(prt_fctn=='g' ? 0 : interleg);
1462 /* no interleg space if string ends in \g */
1463 fill += legspace[i];
1465 if (im->gdes[i].gf != GF_GPRINT &&
1466 im->gdes[i].gf != GF_COMMENT) {
1469 fill += leg_cc * SmallFont->w;
1474 /* who said there was a special tag ... ?*/
1475 if (prt_fctn=='g') {
1478 if (prt_fctn == '\0') {
1479 if (i == im->gdes_c -1 ) prt_fctn ='l';
1481 /* is it time to place the legends ? */
1482 if (fill > im->xgif - 2*border){
1497 if (prt_fctn != '\0'){
1499 if (leg_c >= 2 && prt_fctn == 'j') {
1500 glue = (im->xgif - fill - 2* border) / (leg_c-1);
1501 /* if (glue > 2 * SmallFont->w) glue = 0; */
1505 if (prt_fctn =='c') leg_x = (im->xgif - fill) / 2.0;
1506 if (prt_fctn =='r') leg_x = im->xgif - fill - border;
1508 for(ii=mark;ii<=i;ii++){
1509 if(im->gdes[ii].legend[0]=='\0')
1511 im->gdes[ii].legloc.x = leg_x;
1512 im->gdes[ii].legloc.y = leg_y;
1514 + strlen(im->gdes[ii].legend)*SmallFont->w
1517 if (im->gdes[ii].gf != GF_GPRINT &&
1518 im->gdes[ii].gf != GF_COMMENT)
1521 leg_y = leg_y + SmallFont->h*1.2;
1522 if (prt_fctn == 's') leg_y -= SmallFont->h *0.5;
1534 /* create a grid on the graph. it determines what to do
1535 from the values of xsize, start and end */
1537 /* the xaxis labels are determined from the number of seconds per pixel
1538 in the requested graph */
1543 horizontal_grid(gdImagePtr gif, image_desc_t *im)
1551 char graph_label[100];
1552 gdPoint polyPoints[4];
1553 int labfact,gridind;
1554 int styleMinor[2],styleMajor[2];
1555 int decimals, fractionals;
1560 range = im->maxval - im->minval;
1561 scaledrange = range / im->magfact;
1563 /* does the scale of this graph make it impossible to put lines
1564 on it? If so, give up. */
1565 if (isnan(scaledrange)) {
1569 styleMinor[0] = graph_col[GRC_GRID].i;
1570 styleMinor[1] = gdTransparent;
1572 styleMajor[0] = graph_col[GRC_MGRID].i;
1573 styleMajor[1] = gdTransparent;
1575 /* find grid spaceing */
1577 if(isnan(im->ygridstep)){
1578 if(im->extra_flags & ALTYGRID) {
1579 /* find the value with max number of digits. Get number of digits */
1580 decimals = ceil(log10(max(fabs(im->maxval), fabs(im->minval))));
1581 if(decimals <= 0) /* everything is small. make place for zero */
1584 fractionals = floor(log10(range));
1585 if(fractionals < 0) /* small amplitude. */
1586 sprintf(labfmt, "%%%d.%df", decimals - fractionals + 1, -fractionals + 1);
1588 sprintf(labfmt, "%%%d.1f", decimals + 1);
1589 gridstep = pow((double)10, (double)fractionals);
1590 if(gridstep == 0) /* range is one -> 0.1 is reasonable scale */
1592 /* should have at least 5 lines but no more then 15 */
1593 if(range/gridstep < 5)
1595 if(range/gridstep > 15)
1597 if(range/gridstep > 5) {
1599 if(range/gridstep > 8)
1608 for(i=0;ylab[i].grid > 0;i++){
1609 pixel = im->ysize / (scaledrange / ylab[i].grid);
1610 if (gridind == -1 && pixel > 5) {
1617 if (pixel * ylab[gridind].lfac[i] >= 2 * SmallFont->h) {
1618 labfact = ylab[gridind].lfac[i];
1623 gridstep = ylab[gridind].grid * im->magfact;
1626 gridstep = im->ygridstep;
1627 labfact = im->ylabfact;
1630 polyPoints[0].x=im->xorigin;
1631 polyPoints[1].x=im->xorigin+im->xsize;
1632 sgrid = (int)( im->minval / gridstep - 1);
1633 egrid = (int)( im->maxval / gridstep + 1);
1634 scaledstep = gridstep/im->magfact;
1635 for (i = sgrid; i <= egrid; i++){
1636 polyPoints[0].y=ytr(im,gridstep*i);
1637 if ( polyPoints[0].y >= im->yorigin-im->ysize
1638 && polyPoints[0].y <= im->yorigin) {
1639 if(i % labfact == 0){
1640 if (i==0 || im->symbol == ' ') {
1642 if(im->extra_flags & ALTYGRID) {
1643 sprintf(graph_label,labfmt,scaledstep*i);
1646 sprintf(graph_label,"%4.1f",scaledstep*i);
1649 sprintf(graph_label,"%4.0f",scaledstep*i);
1653 sprintf(graph_label,"%4.1f %c",scaledstep*i, im->symbol);
1655 sprintf(graph_label,"%4.0f %c",scaledstep*i, im->symbol);
1659 gdImageString(gif, SmallFont,
1660 (polyPoints[0].x - (strlen(graph_label) *
1662 polyPoints[0].y - SmallFont->h/2+1,
1663 (unsigned char *)graph_label, graph_col[GRC_FONT].i);
1665 gdImageSetStyle(gif, styleMajor, 2);
1667 gdImageLine(gif, polyPoints[0].x-2,polyPoints[0].y,
1668 polyPoints[0].x+2,polyPoints[0].y,graph_col[GRC_MGRID].i);
1669 gdImageLine(gif, polyPoints[1].x-2,polyPoints[0].y,
1670 polyPoints[1].x+2,polyPoints[0].y,graph_col[GRC_MGRID].i);
1672 gdImageSetStyle(gif, styleMinor, 2);
1673 gdImageLine(gif, polyPoints[0].x-1,polyPoints[0].y,
1674 polyPoints[0].x+1,polyPoints[0].y,graph_col[GRC_GRID].i);
1675 gdImageLine(gif, polyPoints[1].x-1,polyPoints[0].y,
1676 polyPoints[1].x+1,polyPoints[0].y,graph_col[GRC_GRID].i);
1678 gdImageLine(gif, polyPoints[0].x,polyPoints[0].y,
1679 polyPoints[1].x,polyPoints[0].y,gdStyled);
1682 /* if(im->minval * im->maxval < 0){
1683 polyPoints[0].y=ytr(0);
1684 gdImageLine(gif, polyPoints[0].x,polyPoints[0].y,
1685 polyPoints[1].x,polyPoints[0].y,graph_col[GRC_MGRID].i);
1691 /* logaritmic horizontal grid */
1693 horizontal_log_grid(gdImagePtr gif, image_desc_t *im)
1697 int minoridx=0, majoridx=0;
1698 char graph_label[100];
1699 gdPoint polyPoints[4];
1700 int styleMinor[2],styleMajor[2];
1701 double value, pixperstep, minstep;
1703 /* find grid spaceing */
1704 pixpex= (double)im->ysize / (log10(im->maxval) - log10(im->minval));
1706 if (isnan(pixpex)) {
1710 for(i=0;yloglab[i][0] > 0;i++){
1711 minstep = log10(yloglab[i][0]);
1712 for(ii=1;yloglab[i][ii+1] > 0;ii++){
1713 if(yloglab[i][ii+2]==0){
1714 minstep = log10(yloglab[i][ii+1])-log10(yloglab[i][ii]);
1718 pixperstep = pixpex * minstep;
1719 if(pixperstep > 5){minoridx = i;}
1720 if(pixperstep > 2 * SmallFont->h){majoridx = i;}
1723 styleMinor[0] = graph_col[GRC_GRID].i;
1724 styleMinor[1] = gdTransparent;
1726 styleMajor[0] = graph_col[GRC_MGRID].i;
1727 styleMajor[1] = gdTransparent;
1729 polyPoints[0].x=im->xorigin;
1730 polyPoints[1].x=im->xorigin+im->xsize;
1731 /* paint minor grid */
1732 for (value = pow((double)10, log10(im->minval)
1733 - fmod(log10(im->minval),log10(yloglab[minoridx][0])));
1734 value <= im->maxval;
1735 value *= yloglab[minoridx][0]){
1736 if (value < im->minval) continue;
1738 while(yloglab[minoridx][++i] > 0){
1739 polyPoints[0].y = ytr(im,value * yloglab[minoridx][i]);
1740 if (polyPoints[0].y <= im->yorigin - im->ysize) break;
1741 gdImageSetStyle(gif, styleMinor, 2);
1742 gdImageLine(gif, polyPoints[0].x-1,polyPoints[0].y,
1743 polyPoints[0].x+1,polyPoints[0].y,graph_col[GRC_GRID].i);
1744 gdImageLine(gif, polyPoints[1].x-1,polyPoints[0].y,
1745 polyPoints[1].x+1,polyPoints[0].y,graph_col[GRC_GRID].i);
1747 gdImageLine(gif, polyPoints[0].x,polyPoints[0].y,
1748 polyPoints[1].x,polyPoints[0].y,gdStyled);
1752 /* paint major grid and labels*/
1753 for (value = pow((double)10, log10(im->minval)
1754 - fmod(log10(im->minval),log10(yloglab[majoridx][0])));
1755 value <= im->maxval;
1756 value *= yloglab[majoridx][0]){
1757 if (value < im->minval) continue;
1759 while(yloglab[majoridx][++i] > 0){
1760 polyPoints[0].y = ytr(im,value * yloglab[majoridx][i]);
1761 if (polyPoints[0].y <= im->yorigin - im->ysize) break;
1762 gdImageSetStyle(gif, styleMajor, 2);
1763 gdImageLine(gif, polyPoints[0].x-2,polyPoints[0].y,
1764 polyPoints[0].x+2,polyPoints[0].y,graph_col[GRC_MGRID].i);
1765 gdImageLine(gif, polyPoints[1].x-2,polyPoints[0].y,
1766 polyPoints[1].x+2,polyPoints[0].y,graph_col[GRC_MGRID].i);
1768 gdImageLine(gif, polyPoints[0].x,polyPoints[0].y,
1769 polyPoints[1].x,polyPoints[0].y,gdStyled);
1770 sprintf(graph_label,"%3.0e",value * yloglab[majoridx][i]);
1771 gdImageString(gif, SmallFont,
1772 (polyPoints[0].x - (strlen(graph_label) *
1774 polyPoints[0].y - SmallFont->h/2+1,
1775 (unsigned char *)graph_label, graph_col[GRC_FONT].i);
1787 int xlab_sel; /* which sort of label and grid ? */
1790 char graph_label[100];
1791 gdPoint polyPoints[4]; /* points for filled graph and more*/
1793 /* style for grid lines */
1797 /* the type of time grid is determined by finding
1798 the number of seconds per pixel in the graph */
1801 if(im->xlab_user.minsec == -1){
1802 factor=(im->end - im->start)/im->xsize;
1804 while ( xlab[xlab_sel+1].minsec != -1
1805 && xlab[xlab_sel+1].minsec <= factor){ xlab_sel++; }
1806 im->xlab_user.gridtm = xlab[xlab_sel].gridtm;
1807 im->xlab_user.gridst = xlab[xlab_sel].gridst;
1808 im->xlab_user.mgridtm = xlab[xlab_sel].mgridtm;
1809 im->xlab_user.mgridst = xlab[xlab_sel].mgridst;
1810 im->xlab_user.labtm = xlab[xlab_sel].labtm;
1811 im->xlab_user.labst = xlab[xlab_sel].labst;
1812 im->xlab_user.precis = xlab[xlab_sel].precis;
1813 im->xlab_user.stst = xlab[xlab_sel].stst;
1816 /* y coords are the same for every line ... */
1817 polyPoints[0].y = im->yorigin;
1818 polyPoints[1].y = im->yorigin-im->ysize;
1820 /* paint the minor grid */
1821 for(ti = find_first_time(im->start,
1822 im->xlab_user.gridtm,
1823 im->xlab_user.gridst);
1825 ti = find_next_time(ti,im->xlab_user.gridtm,im->xlab_user.gridst)
1827 /* are we inside the graph ? */
1828 if (ti < im->start || ti > im->end) continue;
1829 polyPoints[0].x = xtr(im,ti);
1830 styleDotted[0] = graph_col[GRC_GRID].i;
1831 styleDotted[1] = gdTransparent;
1833 gdImageSetStyle(gif, styleDotted, 2);
1835 gdImageLine(gif, polyPoints[0].x,polyPoints[0].y,
1836 polyPoints[0].x,polyPoints[1].y,gdStyled);
1837 gdImageLine(gif, polyPoints[0].x,polyPoints[0].y-1,
1838 polyPoints[0].x,polyPoints[0].y+1,graph_col[GRC_GRID].i);
1839 gdImageLine(gif, polyPoints[0].x,polyPoints[1].y-1,
1840 polyPoints[0].x,polyPoints[1].y+1,graph_col[GRC_GRID].i);
1843 /* paint the major grid */
1844 for(ti = find_first_time(im->start,
1845 im->xlab_user.mgridtm,
1846 im->xlab_user.mgridst);
1848 ti = find_next_time(ti,im->xlab_user.mgridtm,im->xlab_user.mgridst)
1850 /* are we inside the graph ? */
1851 if (ti < im->start || ti > im->end) continue;
1852 polyPoints[0].x = xtr(im,ti);
1853 styleDotted[0] = graph_col[GRC_MGRID].i;
1854 styleDotted[1] = gdTransparent;
1855 gdImageSetStyle(gif, styleDotted, 2);
1857 gdImageLine(gif, polyPoints[0].x,polyPoints[0].y,
1858 polyPoints[0].x,polyPoints[1].y,gdStyled);
1859 gdImageLine(gif, polyPoints[0].x,polyPoints[0].y-2,
1860 polyPoints[0].x,polyPoints[0].y+2,graph_col[GRC_MGRID].i);
1861 gdImageLine(gif, polyPoints[0].x,polyPoints[1].y-2,
1862 polyPoints[0].x,polyPoints[1].y+2,graph_col[GRC_MGRID].i);
1864 /* paint the labels below the graph */
1865 for(ti = find_first_time(im->start,
1866 im->xlab_user.labtm,
1867 im->xlab_user.labst);
1869 ti = find_next_time(ti,im->xlab_user.labtm,im->xlab_user.labst)
1872 tilab= ti + im->xlab_user.precis/2; /* correct time for the label */
1875 strftime(graph_label,99,im->xlab_user.stst,localtime(&tilab));
1877 # error "your libc has no strftime I guess we'll abort the exercise here."
1879 width=strlen(graph_label) * SmallFont->w;
1880 gr_pos=xtr(im,tilab) - width/2;
1881 if (gr_pos >= im->xorigin
1882 && gr_pos + width <= im->xorigin+im->xsize)
1883 gdImageString(gif, SmallFont,
1884 gr_pos, polyPoints[0].y+4,
1885 (unsigned char *)graph_label, graph_col[GRC_FONT].i);
1897 /* draw x and y axis */
1898 gdImageLine(gif, im->xorigin+im->xsize,im->yorigin,
1899 im->xorigin+im->xsize,im->yorigin-im->ysize,
1900 graph_col[GRC_GRID].i);
1902 gdImageLine(gif, im->xorigin,im->yorigin-im->ysize,
1903 im->xorigin+im->xsize,im->yorigin-im->ysize,
1904 graph_col[GRC_GRID].i);
1906 gdImageLine(gif, im->xorigin-4,im->yorigin,
1907 im->xorigin+im->xsize+4,im->yorigin,
1908 graph_col[GRC_FONT].i);
1910 gdImageLine(gif, im->xorigin,im->yorigin,
1911 im->xorigin,im->yorigin-im->ysize,
1912 graph_col[GRC_GRID].i);
1914 /* arrow for X axis direction */
1915 gdImageLine(gif, im->xorigin+im->xsize+4, im->yorigin-3, im->xorigin+im->xsize+4, im->yorigin+3,graph_col[GRC_ARROW].i);
1916 gdImageLine(gif, im->xorigin+im->xsize+4, im->yorigin-3, im->xorigin+im->xsize+9, im->yorigin,graph_col[GRC_ARROW].i);
1917 gdImageLine(gif, im->xorigin+im->xsize+4, im->yorigin+3, im->xorigin+im->xsize+9, im->yorigin,graph_col[GRC_ARROW].i);
1919 /* gdImageLine(gif, im->xorigin+im->xsize-1, im->yorigin-3, im->xorigin+im->xsize-1, im->yorigin+3,graph_col[GRC_MGRID].i);
1920 gdImageLine(gif, im->xorigin+im->xsize, im->yorigin-2, im->xorigin+im->xsize, im->yorigin+2,graph_col[GRC_MGRID].i);
1921 gdImageLine(gif, im->xorigin+im->xsize+1, im->yorigin-2, im->xorigin+im->xsize+1, im->yorigin+2,graph_col[GRC_MGRID].i);
1922 gdImageLine(gif, im->xorigin+im->xsize+2, im->yorigin-2, im->xorigin+im->xsize+2, im->yorigin+2,graph_col[GRC_MGRID].i);
1923 gdImageLine(gif, im->xorigin+im->xsize+3, im->yorigin-1, im->xorigin+im->xsize+3, im->yorigin+1,graph_col[GRC_MGRID].i);
1924 gdImageLine(gif, im->xorigin+im->xsize+4, im->yorigin-1, im->xorigin+im->xsize+4, im->yorigin+1,graph_col[GRC_MGRID].i);
1925 gdImageLine(gif, im->xorigin+im->xsize+5, im->yorigin, im->xorigin+im->xsize+5, im->yorigin,graph_col[GRC_MGRID].i); */
1940 gdPoint polyPoints[4]; /* points for filled graph and more*/
1942 /* draw 3d border */
1943 gdImageLine(gif,0,0,im->xgif-1,0,graph_col[GRC_SHADEA].i);
1944 gdImageLine(gif,1,1,im->xgif-2,1,graph_col[GRC_SHADEA].i);
1945 gdImageLine(gif,0,0,0,im->ygif-1,graph_col[GRC_SHADEA].i);
1946 gdImageLine(gif,1,1,1,im->ygif-2,graph_col[GRC_SHADEA].i);
1947 gdImageLine(gif,im->xgif-1,0,im->xgif-1,im->ygif-1,graph_col[GRC_SHADEB].i);
1948 gdImageLine(gif,0,im->ygif-1,im->xgif-1,im->ygif-1,graph_col[GRC_SHADEB].i);
1949 gdImageLine(gif,im->xgif-2,1,im->xgif-2,im->ygif-2,graph_col[GRC_SHADEB].i);
1950 gdImageLine(gif,1,im->ygif-2,im->xgif-2,im->ygif-2,graph_col[GRC_SHADEB].i);
1953 if (im->draw_x_grid == 1 )
1954 vertical_grid(gif, im);
1956 if (im->draw_y_grid == 1){
1957 if(im->logarithmic){
1958 res = horizontal_log_grid(gif,im);
1960 res = horizontal_grid(gif,im);
1963 /* dont draw horizontal grid if there is no min and max val */
1965 char *nodata = "No Data found";
1966 gdImageString(gif, LargeFont,
1968 - (strlen(nodata)*LargeFont->w)/2,
1969 (2*im->yorigin-im->ysize) / 2,
1970 (unsigned char *)nodata, graph_col[GRC_FONT].i);
1974 /* yaxis description */
1975 gdImageStringUp(gif, SmallFont,
1977 (im->yorigin - im->ysize/2
1978 +(strlen(im->ylegend)*SmallFont->w)/2 ),
1979 (unsigned char *)im->ylegend, graph_col[GRC_FONT].i);
1983 gdImageString(gif, LargeFont,
1985 - (strlen(im->title)*LargeFont->w)/2,
1987 (unsigned char *)im->title, graph_col[GRC_FONT].i);
1990 if( !(im->extra_flags & NOLEGEND) ) {
1991 for(i=0;i<im->gdes_c;i++){
1992 if(im->gdes[i].legend[0] =='\0')
1995 if(im->gdes[i].gf != GF_GPRINT && im->gdes[i].gf != GF_COMMENT){
1997 polyPoints[0].x = im->gdes[i].legloc.x;
1998 polyPoints[0].y = im->gdes[i].legloc.y+1;
1999 polyPoints[1].x = polyPoints[0].x+boxH;
2000 polyPoints[2].x = polyPoints[0].x+boxH;
2001 polyPoints[3].x = polyPoints[0].x;
2002 polyPoints[1].y = polyPoints[0].y;
2003 polyPoints[2].y = polyPoints[0].y+boxV;
2004 polyPoints[3].y = polyPoints[0].y+boxV;
2005 gdImageFilledPolygon(gif,polyPoints,4,im->gdes[i].col.i);
2006 gdImagePolygon(gif,polyPoints,4,graph_col[GRC_FRAME].i);
2008 gdImageString(gif, SmallFont,
2009 polyPoints[0].x+boxH+6,
2011 (unsigned char *)im->gdes[i].legend,
2012 graph_col[GRC_FONT].i);
2014 polyPoints[0].x = im->gdes[i].legloc.x;
2015 polyPoints[0].y = im->gdes[i].legloc.y;
2017 gdImageString(gif, SmallFont,
2020 (unsigned char *)im->gdes[i].legend,
2021 graph_col[GRC_FONT].i);
2027 gator(gif, (int) im->xgif-5, 5);
2033 MkLineBrush(image_desc_t *im,long cosel, enum gf_en typsel){
2038 brush=gdImageCreate(1,1);
2041 brush=gdImageCreate(2,2);
2044 brush=gdImageCreate(3,3);
2050 gdImageColorTransparent(brush,
2051 gdImageColorAllocate(brush, 0, 0, 0));
2053 pen = gdImageColorAllocate(brush,
2054 im->gdes[cosel].col.red,
2055 im->gdes[cosel].col.green,
2056 im->gdes[cosel].col.blue);
2060 gdImageSetPixel(brush,0,0,pen);
2063 gdImageSetPixel(brush,0,0,pen);
2064 gdImageSetPixel(brush,0,1,pen);
2065 gdImageSetPixel(brush,1,0,pen);
2066 gdImageSetPixel(brush,1,1,pen);
2069 gdImageSetPixel(brush,1,0,pen);
2070 gdImageSetPixel(brush,0,1,pen);
2071 gdImageSetPixel(brush,1,1,pen);
2072 gdImageSetPixel(brush,2,1,pen);
2073 gdImageSetPixel(brush,1,2,pen);
2080 /*****************************************************
2081 * lazy check make sure we rely need to create this graph
2082 *****************************************************/
2084 int lazy_check(image_desc_t *im){
2087 struct stat gifstat;
2089 if (im->lazy == 0) return 0; /* no lazy option */
2090 if (stat(im->graphfile,&gifstat) != 0)
2091 return 0; /* can't stat */
2092 /* one pixel in the existing graph is more then what we would
2094 if (time(NULL) - gifstat.st_mtime >
2095 (im->end - im->start) / im->xsize)
2097 if ((fd = fopen(im->graphfile,"rb")) == NULL)
2098 return 0; /* the file does not exist */
2099 switch (im->imgformat) {
2101 size = GifSize(fd,&(im->xgif),&(im->ygif));
2104 size = PngSize(fd,&(im->xgif),&(im->ygif));
2111 /* draw that picture thing ... */
2113 graph_paint(image_desc_t *im, char ***calcpr)
2116 int lazy = lazy_check(im);
2120 gdImagePtr gif,brush;
2122 double areazero = 0.0;
2123 enum gf_en stack_gf = GF_PRINT;
2124 graph_desc_t *lastgdes = NULL;
2125 gdPoint canvas[4], back[4]; /* points for canvas*/
2127 /* if we are lazy and there is nothing to PRINT ... quit now */
2128 if (lazy && im->prt_c==0) return 0;
2130 /* pull the data from the rrd files ... */
2132 if(data_fetch(im)==-1)
2135 /* evaluate VDEF and CDEF operations ... */
2136 if(data_calc(im)==-1)
2139 /* calculate and PRINT and GPRINT definitions. We have to do it at
2140 * this point because it will affect the length of the legends
2141 * if there are no graph elements we stop here ...
2142 * if we are lazy, try to quit ...
2144 i=print_calc(im,calcpr);
2146 if(i==0 || lazy) return 0;
2148 /* get actual drawing data and find min and max values*/
2149 if(data_proc(im)==-1)
2152 if(!im->logarithmic){si_unit(im);} /* identify si magnitude Kilo, Mega Giga ? */
2154 if(!im->rigid && ! im->logarithmic)
2155 expand_range(im); /* make sure the upper and lower limit are
2158 /* init xtr and ytr */
2159 /* determine the actual size of the gif to draw. The size given
2160 on the cmdline is the graph area. But we need more as we have
2161 draw labels and other things outside the graph area */
2164 im->xorigin = 10 + 9 * SmallFont->w+SmallFont->h;
2167 im->yorigin = 14 + im->ysize;
2170 if(im->title[0] != '\0')
2171 im->yorigin += (LargeFont->h+4);
2173 im->xgif=20+im->xsize + im->xorigin;
2174 im->ygif= im->yorigin+2*SmallFont->h;
2176 /* determine where to place the legends onto the graphics.
2177 and set im->ygif to match space requirements for text */
2178 if(leg_place(im)==-1)
2181 gif=gdImageCreate(im->xgif,im->ygif);
2183 gdImageInterlace(gif, im->interlaced);
2185 /* allocate colors for the screen elements */
2186 for(i=0;i<DIM(graph_col);i++)
2187 /* check for user override values */
2188 if(im->graph_col[i].red != -1)
2190 gdImageColorAllocate( gif,
2191 im->graph_col[i].red,
2192 im->graph_col[i].green,
2193 im->graph_col[i].blue);
2196 gdImageColorAllocate( gif,
2202 /* allocate colors for the graph */
2203 for(i=0;i<im->gdes_c;i++)
2204 /* only for elements which have a color defined */
2205 if (im->gdes[i].col.red != -1)
2207 gdImageColorAllocate(gif,
2208 im->gdes[i].col.red,
2209 im->gdes[i].col.green,
2210 im->gdes[i].col.blue);
2213 /* the actual graph is created by going through the individual
2214 graph elements and then drawing them */
2218 back[1].x = back[0].x+im->xgif;
2219 back[1].y = back[0].y;
2220 back[2].x = back[1].x;
2221 back[2].y = back[0].y+im->ygif;
2222 back[3].x = back[0].x;
2223 back[3].y = back[2].y;
2225 gdImageFilledPolygon(gif,back,4,graph_col[GRC_BACK].i);
2227 canvas[0].x = im->xorigin;
2228 canvas[0].y = im->yorigin;
2229 canvas[1].x = canvas[0].x+im->xsize;
2230 canvas[1].y = canvas[0].y;
2231 canvas[2].x = canvas[1].x;
2232 canvas[2].y = canvas[0].y-im->ysize;
2233 canvas[3].x = canvas[0].x;
2234 canvas[3].y = canvas[2].y;
2236 gdImageFilledPolygon(gif,canvas,4,graph_col[GRC_CANVAS].i);
2238 if (im->minval > 0.0)
2239 areazero = im->minval;
2240 if (im->maxval < 0.0)
2241 areazero = im->maxval;
2245 for(i=0;i<im->gdes_c;i++){
2247 switch(im->gdes[i].gf){
2258 for (ii = 0; ii < im->xsize; ii++)
2260 if (!isnan(im->gdes[i].p_data[ii]) &&
2261 im->gdes[i].p_data[ii] > 0.0)
2263 /* generate a tick */
2264 gdImageLine(gif, im -> xorigin + ii,
2265 im -> yorigin - (im -> gdes[i].yrule * im -> ysize),
2268 im -> gdes[i].col.i);
2276 stack_gf = im->gdes[i].gf;
2278 /* fix data points at oo and -oo */
2279 for(ii=0;ii<im->xsize;ii++){
2280 if (isinf(im->gdes[i].p_data[ii])){
2281 if (im->gdes[i].p_data[ii] > 0) {
2282 im->gdes[i].p_data[ii] = im->maxval ;
2284 im->gdes[i].p_data[ii] = im->minval ;
2290 if (im->gdes[i].col.i != -1){
2291 /* GF_LINE and frined */
2292 if(stack_gf == GF_LINE1 || stack_gf == GF_LINE2 || stack_gf == GF_LINE3 ){
2293 brush = MkLineBrush(im,i,stack_gf);
2294 gdImageSetBrush(gif, brush);
2295 for(ii=1;ii<im->xsize;ii++){
2296 if (isnan(im->gdes[i].p_data[ii-1]) ||
2297 isnan(im->gdes[i].p_data[ii]))
2300 ii+im->xorigin-1,ytr(im,im->gdes[i].p_data[ii-1]),
2301 ii+im->xorigin,ytr(im,im->gdes[i].p_data[ii]),
2305 gdImageDestroy(brush);
2308 /* GF_AREA STACK type*/
2309 if (im->gdes[i].gf == GF_STACK )
2310 for(ii=0;ii<im->xsize;ii++){
2311 if(isnan(im->gdes[i].p_data[ii])){
2312 im->gdes[i].p_data[ii] = lastgdes->p_data[ii];
2316 if (lastgdes->p_data[ii] == im->gdes[i].p_data[ii]){
2320 ii+im->xorigin,ytr(im,lastgdes->p_data[ii]),
2321 ii+im->xorigin,ytr(im,im->gdes[i].p_data[ii]),
2325 else /* simple GF_AREA */
2326 for(ii=0;ii<im->xsize;ii++){
2327 if (isnan(im->gdes[i].p_data[ii])) {
2328 im->gdes[i].p_data[ii] = 0;
2332 ii+im->xorigin,ytr(im,areazero),
2333 ii+im->xorigin,ytr(im,im->gdes[i].p_data[ii]),
2337 lastgdes = &(im->gdes[i]);
2344 /* the RULES are the last thing to paint ... */
2345 for(i=0;i<im->gdes_c;i++){
2347 switch(im->gdes[i].gf){
2349 printf("DEBUG: HRULE at %f\n",im->gdes[i].yrule);
2350 if(isnan(im->gdes[i].yrule)) { /* fetch variable */
2351 im->gdes[i].yrule = im->gdes[im->gdes[i].vidx].vf.val;
2353 if(im->gdes[i].yrule >= im->minval
2354 && im->gdes[i].yrule <= im->maxval)
2356 im->xorigin,ytr(im,im->gdes[i].yrule),
2357 im->xorigin+im->xsize,ytr(im,im->gdes[i].yrule),
2361 if(im->gdes[i].xrule == 0) { /* fetch variable */
2362 im->gdes[i].xrule = im->gdes[im->gdes[i].vidx].vf.when;
2364 if(im->gdes[i].xrule >= im->start
2365 && im->gdes[i].xrule <= im->end)
2367 xtr(im,im->gdes[i].xrule),im->yorigin,
2368 xtr(im,im->gdes[i].xrule),im->yorigin-im->ysize,
2376 if (strcmp(im->graphfile,"-")==0) {
2378 /* Change translation mode for stdout to BINARY */
2379 _setmode( _fileno( stdout ), O_BINARY );
2383 if ((fo = fopen(im->graphfile,"wb")) == NULL) {
2384 rrd_set_error("Opening '%s' for write: %s",im->graphfile, strerror(errno));
2388 switch (im->imgformat) {
2390 gdImageGif(gif, fo);
2393 gdImagePng(gif, fo);
2396 if (strcmp(im->graphfile,"-") != 0)
2398 gdImageDestroy(gif);
2404 /*****************************************************
2406 *****************************************************/
2409 gdes_alloc(image_desc_t *im){
2411 long def_step = (im->end-im->start)/im->xsize;
2413 if (im->step > def_step) /* step can be increassed ... no decreassed */
2414 def_step = im->step;
2418 if ((im->gdes = (graph_desc_t *) rrd_realloc(im->gdes, (im->gdes_c)
2419 * sizeof(graph_desc_t)))==NULL){
2420 rrd_set_error("realloc graph_descs");
2425 im->gdes[im->gdes_c-1].step=def_step;
2426 im->gdes[im->gdes_c-1].start=im->start;
2427 im->gdes[im->gdes_c-1].end=im->end;
2428 im->gdes[im->gdes_c-1].vname[0]='\0';
2429 im->gdes[im->gdes_c-1].data=NULL;
2430 im->gdes[im->gdes_c-1].ds_namv=NULL;
2431 im->gdes[im->gdes_c-1].data_first=0;
2432 im->gdes[im->gdes_c-1].p_data=NULL;
2433 im->gdes[im->gdes_c-1].rpnp=NULL;
2434 im->gdes[im->gdes_c-1].col.red = -1;
2435 im->gdes[im->gdes_c-1].col.i=-1;
2436 im->gdes[im->gdes_c-1].legend[0]='\0';
2437 im->gdes[im->gdes_c-1].rrd[0]='\0';
2438 im->gdes[im->gdes_c-1].ds=-1;
2439 im->gdes[im->gdes_c-1].p_data=NULL;
2443 /* copies input untill the first unescaped colon is found
2444 or until input ends. backslashes have to be escaped as well */
2446 scan_for_col(char *input, int len, char *output)
2451 input[inp] != ':' &&
2454 if (input[inp] == '\\' &&
2455 input[inp+1] != '\0' &&
2456 (input[inp+1] == '\\' ||
2457 input[inp+1] == ':')){
2458 output[outp++] = input[++inp];
2461 output[outp++] = input[inp];
2464 output[outp] = '\0';
2469 rrd_graph(int argc, char **argv, char ***prdata, int *xsize, int *ysize)
2475 time_t start_tmp=0,end_tmp=0;
2476 char scan_gtm[12],scan_mtm[12],scan_ltm[12],col_nam[12];
2478 unsigned int col_red,col_green,col_blue;
2480 int linepass = 0; /* stack can only follow directly after LINE* AREA or STACK */
2481 struct time_value start_tv, end_tv;
2482 char *parsetime_error = NULL;
2487 parsetime("end-24h", &start_tv);
2488 parsetime("now", &end_tv);
2490 im.xlab_user.minsec = -1;
2496 im.ylegend[0] = '\0';
2501 im.unitsexponent= 9999;
2507 im.ygridstep = DNAN;
2514 im.imgformat = IF_GIF; /* we default to GIF output */
2516 for(i=0;i<DIM(graph_col);i++)
2517 im.graph_col[i].red=-1;
2521 static struct option long_options[] =
2523 {"start", required_argument, 0, 's'},
2524 {"end", required_argument, 0, 'e'},
2525 {"x-grid", required_argument, 0, 'x'},
2526 {"y-grid", required_argument, 0, 'y'},
2527 {"vertical-label",required_argument,0,'v'},
2528 {"width", required_argument, 0, 'w'},
2529 {"height", required_argument, 0, 'h'},
2530 {"interlaced", no_argument, 0, 'i'},
2531 {"upper-limit",required_argument, 0, 'u'},
2532 {"lower-limit",required_argument, 0, 'l'},
2533 {"rigid", no_argument, 0, 'r'},
2534 {"base", required_argument, 0, 'b'},
2535 {"logarithmic",no_argument, 0, 'o'},
2536 {"color", required_argument, 0, 'c'},
2537 {"title", required_argument, 0, 't'},
2538 {"imginfo", required_argument, 0, 'f'},
2539 {"imgformat", required_argument, 0, 'a'},
2540 {"lazy", no_argument, 0, 'z'},
2541 {"no-legend", no_argument, 0, 'g'},
2542 {"alt-y-grid", no_argument, 0, 257 },
2543 {"alt-autoscale", no_argument, 0, 258 },
2544 {"alt-autoscale-max", no_argument, 0, 259 },
2545 {"units-exponent",required_argument, 0, 260},
2546 {"step", required_argument, 0, 261},
2548 int option_index = 0;
2552 opt = getopt_long(argc, argv,
2553 "s:e:x:y:v:w:h:iu:l:rb:oc:t:f:a:z:g",
2554 long_options, &option_index);
2561 im.extra_flags |= ALTYGRID;
2564 im.extra_flags |= ALTAUTOSCALE;
2567 im.extra_flags |= ALTAUTOSCALE_MAX;
2570 im.extra_flags |= NOLEGEND;
2573 im.unitsexponent = atoi(optarg);
2576 im.step = atoi(optarg);
2579 if ((parsetime_error = parsetime(optarg, &start_tv))) {
2580 rrd_set_error( "start time: %s", parsetime_error );
2585 if ((parsetime_error = parsetime(optarg, &end_tv))) {
2586 rrd_set_error( "end time: %s", parsetime_error );
2591 if(strcmp(optarg,"none") == 0){
2597 "%10[A-Z]:%ld:%10[A-Z]:%ld:%10[A-Z]:%ld:%ld:%n",
2599 &im.xlab_user.gridst,
2601 &im.xlab_user.mgridst,
2603 &im.xlab_user.labst,
2604 &im.xlab_user.precis,
2605 &stroff) == 7 && stroff != 0){
2606 strncpy(im.xlab_form, optarg+stroff, sizeof(im.xlab_form) - 1);
2607 if((im.xlab_user.gridtm = tmt_conv(scan_gtm)) == -1){
2608 rrd_set_error("unknown keyword %s",scan_gtm);
2610 } else if ((im.xlab_user.mgridtm = tmt_conv(scan_mtm)) == -1){
2611 rrd_set_error("unknown keyword %s",scan_mtm);
2613 } else if ((im.xlab_user.labtm = tmt_conv(scan_ltm)) == -1){
2614 rrd_set_error("unknown keyword %s",scan_ltm);
2617 im.xlab_user.minsec = 1;
2618 im.xlab_user.stst = im.xlab_form;
2620 rrd_set_error("invalid x-grid format");
2626 if(strcmp(optarg,"none") == 0){
2634 &im.ylabfact) == 2) {
2635 if(im.ygridstep<=0){
2636 rrd_set_error("grid step must be > 0");
2638 } else if (im.ylabfact < 1){
2639 rrd_set_error("label factor must be > 0");
2643 rrd_set_error("invalid y-grid format");
2648 strncpy(im.ylegend,optarg,150);
2649 im.ylegend[150]='\0';
2652 im.maxval = atof(optarg);
2655 im.minval = atof(optarg);
2658 im.base = atol(optarg);
2659 if(im.base != 1024 && im.base != 1000 ){
2660 rrd_set_error("the only sensible value for base apart from 1000 is 1024");
2665 long_tmp = atol(optarg);
2666 if (long_tmp < 10) {
2667 rrd_set_error("width below 10 pixels");
2670 im.xsize = long_tmp;
2673 long_tmp = atol(optarg);
2674 if (long_tmp < 10) {
2675 rrd_set_error("height below 10 pixels");
2678 im.ysize = long_tmp;
2687 im.imginfo = optarg;
2690 if((im.imgformat = if_conv(optarg)) == -1) {
2691 rrd_set_error("unsupported graphics format '%s'",optarg);
2700 if (isnan(im.minval))
2705 "%10[A-Z]#%2x%2x%2x",
2706 col_nam,&col_red,&col_green,&col_blue) == 4){
2708 if((ci=grc_conv(col_nam)) != -1){
2709 im.graph_col[ci].red=col_red;
2710 im.graph_col[ci].green=col_green;
2711 im.graph_col[ci].blue=col_blue;
2713 rrd_set_error("invalid color name '%s'",col_nam);
2716 rrd_set_error("invalid color def format");
2721 strncpy(im.title,optarg,150);
2727 rrd_set_error("unknown option '%c'", optopt);
2729 rrd_set_error("unknown option '%s'",argv[optind-1]);
2734 if (optind >= argc) {
2735 rrd_set_error("missing filename");
2739 if (im.logarithmic == 1 && (im.minval <= 0 || isnan(im.minval))){
2740 rrd_set_error("for a logarithmic yaxis you must specify a lower-limit > 0");
2744 strncpy(im.graphfile,argv[optind],MAXPATH-1);
2745 im.graphfile[MAXPATH-1]='\0';
2747 if (proc_start_end(&start_tv,&end_tv,&start_tmp,&end_tmp) == -1){
2751 if (start_tmp < 3600*24*365*10){
2752 rrd_set_error("the first entry to fetch should be after 1980 (%ld)",start_tmp);
2756 if (end_tmp < start_tmp) {
2757 rrd_set_error("start (%ld) should be less than end (%ld)",
2758 start_tmp, end_tmp);
2762 im.start = start_tmp;
2766 for(i=optind+1;i<argc;i++){
2769 char varname[30],*rpnex;
2771 if(sscanf(argv[i],"%10[A-Z0-9]:%n",symname,&argstart)==1){
2772 if((im.gdes[im.gdes_c-1].gf=gf_conv(symname))==-1){
2774 rrd_set_error("unknown function '%s'",symname);
2778 rrd_set_error("can't parse '%s'",argv[i]);
2783 /* reset linepass if a non LINE/STACK/AREA operator gets parsed
2785 if (im.gdes[im.gdes_c-1].gf != GF_LINE1 &&
2786 im.gdes[im.gdes_c-1].gf != GF_LINE2 &&
2787 im.gdes[im.gdes_c-1].gf != GF_LINE3 &&
2788 im.gdes[im.gdes_c-1].gf != GF_AREA &&
2789 im.gdes[im.gdes_c-1].gf != GF_STACK) {
2794 switch(im.gdes[im.gdes_c-1].gf){
2800 "%29[^#:]:" CF_NAM_FMT ":%n",
2801 varname,symname,&strstart) == 2){
2802 scan_for_col(&argv[i][argstart+strstart],FMT_LEG_LEN,im.gdes[im.gdes_c-1].format);
2803 if((im.gdes[im.gdes_c-1].vidx=find_var(&im,varname))==-1){
2805 rrd_set_error("unknown variable '%s'",varname);
2808 if((im.gdes[im.gdes_c-1].cf=cf_conv(symname))==-1){
2815 rrd_set_error("can't parse '%s'",&argv[i][argstart]);
2820 if(strlen(&argv[i][argstart])>FMT_LEG_LEN) argv[i][argstart+FMT_LEG_LEN-3]='\0' ;
2821 strcpy(im.gdes[im.gdes_c-1].legend, &argv[i][argstart]);
2824 /* scan for either "HRULE:vname#..." or "HRULE:num#..."
2826 * If a vname is used, the value NaN is set; this is catched
2827 * when graphing. Setting value NaN from the script is not
2831 sscanf(&argv[i][argstart], "%lf#%n"
2832 ,&im.gdes[im.gdes_c-1].yrule
2835 if (strstart==0) { /* no number, should be vname */
2836 sscanf(&argv[i][argstart], DEF_NAM_FMT "#%n"
2841 im.gdes[im.gdes_c-1].yrule = DNAN;/* signal use of vname */
2842 if((im.gdes[im.gdes_c-1].vidx=find_var(&im,varname))==-1){
2844 rrd_set_error("unknown variable '%s' in HRULE",varname);
2847 if(im.gdes[im.gdes[im.gdes_c-1].vidx].gf != GF_VDEF) {
2849 rrd_set_error("Only VDEF is allowed in HRULE",varname);
2854 printf("DEBUG: matched HRULE:num\n");
2855 printf("DEBUG: strstart==%i\n",strstart);
2859 rrd_set_error("can't parse '%s'",&argv[i][argstart]);
2864 &argv[i][argstart+strstart],
2870 im.gdes[im.gdes_c-1].col.red = col_red;
2871 im.gdes[im.gdes_c-1].col.green = col_green;
2872 im.gdes[im.gdes_c-1].col.blue = col_blue;
2874 im.gdes[im.gdes_c-1].legend[0] = '\0';
2876 scan_for_col(&argv[i][argstart+strstart+n],FMT_LEG_LEN,im.gdes[im.gdes_c-1].legend);
2880 rrd_set_error("can't parse '%s'",&argv[i][argstart]);
2887 /* scan for either "VRULE:vname#..." or "VRULE:num#..."
2889 * If a vname is used, the value 0 is set; this is catched
2890 * when graphing. Setting value 0 from the script is not
2894 sscanf(&argv[i][argstart], "%lu#%n"
2895 ,(long unsigned int *)&im.gdes[im.gdes_c-1].xrule
2898 if (strstart==0) { /* no number, should be vname */
2899 sscanf(&argv[i][argstart], DEF_NAM_FMT "#%n"
2903 if (strstart!=0) { /* vname matched */
2904 im.gdes[im.gdes_c-1].xrule = 0;/* signal use of vname */
2905 if((im.gdes[im.gdes_c-1].vidx=find_var(&im,varname))==-1){
2907 rrd_set_error("unknown variable '%s' in VRULE",varname);
2910 if(im.gdes[im.gdes[im.gdes_c-1].vidx].gf != GF_VDEF) {
2912 rrd_set_error("Only VDEF is allowed in VRULE",varname);
2917 if (im.gdes[im.gdes_c-1].xrule==0)
2923 rrd_set_error("can't parse '%s'",&argv[i][argstart]);
2928 &argv[i][argstart+strstart],
2934 im.gdes[im.gdes_c-1].col.red = col_red;
2935 im.gdes[im.gdes_c-1].col.green = col_green;
2936 im.gdes[im.gdes_c-1].col.blue = col_blue;
2938 im.gdes[im.gdes_c-1].legend[0] = '\0';
2940 scan_for_col(&argv[i][argstart+strstart+n],FMT_LEG_LEN,im.gdes[im.gdes_c-1].legend);
2944 rrd_set_error("can't parse '%s'",&argv[i][argstart]);
2950 if((scancount=sscanf(
2952 "%29[^:#]#%2x%2x%2x:%lf:%n",
2957 &(im.gdes[im.gdes_c-1].yrule),
2960 im.gdes[im.gdes_c-1].col.red = col_red;
2961 im.gdes[im.gdes_c-1].col.green = col_green;
2962 im.gdes[im.gdes_c-1].col.blue = col_blue;
2964 im.gdes[im.gdes_c-1].legend[0] = '\0';
2966 scan_for_col(&argv[i][argstart+strstart],FMT_LEG_LEN,im.gdes[im.gdes_c-1].legend);
2968 if((im.gdes[im.gdes_c-1].vidx=find_var(&im,varname))==-1){
2970 rrd_set_error("unknown variable '%s'",varname);
2973 if (im.gdes[im.gdes_c-1].yrule <= 0.0 || im.gdes[im.gdes_c-1].yrule > 1.0)
2976 rrd_set_error("Tick mark scaling factor out of range");
2980 im.gdes[im.gdes_c-1].col.red = -1;
2982 /* default tick marks: 10% of the y-axis */
2983 im.gdes[im.gdes_c-1].yrule = 0.1;
2987 rrd_set_error("can't parse '%s'",&argv[i][argstart]);
2989 } /* endif sscanf */
2994 rrd_set_error("STACK must follow AREA, LINE or STACK");
3002 if((scancount=sscanf(
3004 "%29[^:#]#%2x%2x%2x:%n",
3010 im.gdes[im.gdes_c-1].col.red = col_red;
3011 im.gdes[im.gdes_c-1].col.green = col_green;
3012 im.gdes[im.gdes_c-1].col.blue = col_blue;
3014 im.gdes[im.gdes_c-1].legend[0] = '\0';
3016 scan_for_col(&argv[i][argstart+strstart],FMT_LEG_LEN,im.gdes[im.gdes_c-1].legend);
3018 if((im.gdes[im.gdes_c-1].vidx=find_var(&im,varname))==-1){
3020 rrd_set_error("unknown variable '%s'",varname);
3024 im.gdes[im.gdes_c-1].col.red = -1;
3028 rrd_set_error("can't parse '%s'",&argv[i][argstart]);
3033 if((rpnex = malloc(strlen(&argv[i][argstart])*sizeof(char)))==NULL){
3034 rrd_set_error("malloc for CDEF");
3039 DEF_NAM_FMT "=%[^: ]",
3040 im.gdes[im.gdes_c-1].vname,
3044 rrd_set_error("can't parse CDEF '%s'",&argv[i][argstart]);
3047 /* checking for duplicate variable names */
3048 if(find_var(&im,im.gdes[im.gdes_c-1].vname) != -1){
3050 rrd_set_error("duplicate variable '%s'",
3051 im.gdes[im.gdes_c-1].vname);
3054 if((im.gdes[im.gdes_c-1].rpnp =
3055 rpn_parse((void*)&im,rpnex,&find_var_wrapper))== NULL){
3056 rrd_set_error("invalid rpn expression '%s'", rpnex);
3064 * strstart is set to zero and will NOT be changed
3065 * if the comma is not matched. This means that it
3066 * remains zero. Although strstart is initialized to
3067 * zero at the beginning of this loop, we do it again
3068 * here just in case someone changes the code...
3070 * According to the docs we cannot rely on the
3071 * returned value from sscanf; it can be 2 or 3,
3072 * depending on %n incrementing it or not.
3077 DEF_NAM_FMT "=" DEF_NAM_FMT ",%n",
3078 im.gdes[im.gdes_c-1].vname,
3082 /* checking both variable names */
3083 if (find_var(&im,im.gdes[im.gdes_c-1].vname) != -1){
3085 rrd_set_error("duplicate variable '%s'",
3086 im.gdes[im.gdes_c-1].vname);
3089 if ((im.gdes[im.gdes_c-1].vidx=find_var(&im,varname)) == -1){
3091 rrd_set_error("variable '%s' not known in VDEF '%s'",
3093 im.gdes[im.gdes_c-1].vname);
3096 if(im.gdes[im.gdes[im.gdes_c-1].vidx].gf != GF_DEF
3097 && im.gdes[im.gdes[im.gdes_c-1].vidx].gf != GF_CDEF){
3098 rrd_set_error("variable '%s' not DEF nor CDEF in VDEF '%s'",
3100 im.gdes[im.gdes_c-1].vname);
3105 /* parsed upto and including the first comma. Now
3106 * see what function is requested. This function
3107 * sets the error string.
3109 if (vdef_parse(&im.gdes[im.gdes_c-1],&argv[i][argstart+strstart])<0) {
3116 rrd_set_error("can't parse VDEF '%s'",&argv[i][argstart]);
3124 im.gdes[im.gdes_c-1].vname,
3125 &strstart)== 1 && strstart){ /* is the = did not match %n returns 0 */
3126 if(sscanf(&argv[i][argstart
3128 +scan_for_col(&argv[i][argstart+strstart],
3129 MAXPATH,im.gdes[im.gdes_c-1].rrd)],
3130 ":" DS_NAM_FMT ":" CF_NAM_FMT,
3131 im.gdes[im.gdes_c-1].ds_nam,
3134 rrd_set_error("can't parse DEF '%s' -2",&argv[i][argstart]);
3139 rrd_set_error("can't parse DEF '%s'",&argv[i][argstart]);
3143 /* checking for duplicate DEF CDEFS */
3144 if (find_var(&im,im.gdes[im.gdes_c-1].vname) != -1){
3146 rrd_set_error("duplicate variable '%s'",
3147 im.gdes[im.gdes_c-1].vname);
3150 if((im.gdes[im.gdes_c-1].cf=cf_conv(symname))==-1){
3152 rrd_set_error("unknown cf '%s'",symname);
3161 rrd_set_error("can't make a graph without contents");
3166 /* parse rest of arguments containing information on what to draw*/
3167 if (graph_paint(&im,prdata)==-1){
3177 /* maybe prdata is not allocated yet ... lets do it now */
3178 if((*prdata = calloc(2,sizeof(char *)))==NULL){
3179 rrd_set_error("malloc imginfo");
3183 if(((*prdata)[0] = malloc((strlen(im.imginfo)+200+strlen(im.graphfile))*sizeof(char)))
3185 rrd_set_error("malloc imginfo");
3188 filename=im.graphfile+strlen(im.graphfile);
3189 while(filename > im.graphfile){
3190 if (*(filename-1)=='/' || *(filename-1)=='\\' ) break;
3194 sprintf((*prdata)[0],im.imginfo,filename,im.xgif,im.ygif);
3200 int bad_format(char *fmt) {
3204 while (*ptr != '\0') {
3205 if (*ptr == '%') {ptr++;
3206 if (*ptr == '\0') return 1;
3207 while ((*ptr >= '0' && *ptr <= '9') || *ptr == '.') {
3210 if (*ptr == '\0') return 1;
3213 if (*ptr == '\0') return 1;
3214 if (*ptr == 'e' || *ptr == 'f') {
3216 } else { return 1; }
3218 else if (*ptr == 's' || *ptr == 'S' || *ptr == '%') { ++ptr; }
3227 vdef_parse(gdes,str)
3228 struct graph_desc_t *gdes;
3231 /* A VDEF currently is either "func" or "param,func"
3232 * so the parsing is rather simple. Change if needed.
3239 sscanf(str,"%le,%29[A-Z]%n",¶m,func,&n);
3240 if (n==strlen(str)) { /* matched */
3244 sscanf(str,"%29[A-Z]%n",func,&n);
3245 if (n==strlen(str)) { /* matched */
3248 rrd_set_error("Unknown function string '%s' in VDEF '%s'"
3255 if (!strcmp("PERCENT",func)) gdes->vf.op = VDEF_PERCENT;
3256 else if (!strcmp("MAXIMUM",func)) gdes->vf.op = VDEF_MAXIMUM;
3257 else if (!strcmp("AVERAGE",func)) gdes->vf.op = VDEF_AVERAGE;
3258 else if (!strcmp("MINIMUM",func)) gdes->vf.op = VDEF_MINIMUM;
3259 else if (!strcmp("FIRST", func)) gdes->vf.op = VDEF_FIRST;
3260 else if (!strcmp("LAST", func)) gdes->vf.op = VDEF_LAST;
3262 rrd_set_error("Unknown function '%s' in VDEF '%s'\n"
3269 switch (gdes->vf.op) {
3271 if (isnan(param)) { /* no parameter given */
3272 rrd_set_error("Function '%s' needs parameter in VDEF '%s'\n"
3278 if (param>=0.0 && param<=100.0) {
3279 gdes->vf.param = param;
3280 gdes->vf.val = DNAN; /* undefined */
3281 gdes->vf.when = 0; /* undefined */
3283 rrd_set_error("Parameter '%f' out of range in VDEF '%s'\n"
3296 gdes->vf.param = DNAN;
3297 gdes->vf.val = DNAN;
3300 rrd_set_error("Function '%s' needs no parameter in VDEF '%s'\n"
3315 graph_desc_t *src,*dst;
3319 dst = &im->gdes[gdi];
3320 src = &im->gdes[dst->vidx];
3321 data = src->data + src->ds + src->ds_cnt; /* skip first value! */
3322 steps = (src->end - src->start) / src->step;
3325 printf("DEBUG: start == %lu, end == %lu, %lu steps\n"
3332 switch (im->gdes[gdi].vf.op) {
3333 case VDEF_PERCENT: {
3334 rrd_value_t * array;
3338 if ((array = malloc(steps*sizeof(double)))==NULL) {
3339 rrd_set_error("malloc VDEV_PERCENT");
3342 for (step=0;step < steps; step++) {
3343 array[step]=data[step*src->ds_cnt];
3345 qsort(array,step,sizeof(double),vdef_percent_compar);
3347 field = (steps-1)*dst->vf.param/100;
3348 dst->vf.val = array[field];
3349 dst->vf.when = 0; /* no time component */
3351 for(step=0;step<steps;step++)
3352 printf("DEBUG: %3li:%10.2f %c\n",step,array[step],step==field?'*':' ');
3358 while (step != steps && isnan(data[step*src->ds_cnt])) step++;
3359 if (step == steps) {
3363 dst->vf.val = data[steps*src->ds_cnt];
3364 dst->vf.when = src->start + (step+1)*src->step;
3366 while (step != steps) {
3367 if (finite(data[step*src->ds_cnt])) {
3368 if (data[step*src->ds_cnt] > dst->vf.val) {
3369 dst->vf.val = data[steps*src->ds_cnt];
3370 dst->vf.when = src->start + (step+1)*src->step;
3376 case VDEF_AVERAGE: {
3379 for (step=0;step<steps;step++) {
3380 if (finite(data[step*src->ds_cnt])) {
3381 sum += data[step*src->ds_cnt];
3387 dst->vf.val = sum/cnt;
3388 dst->vf.when = 0; /* no time component */
3397 while (step != steps && isnan(data[step*src->ds_cnt])) step++;
3398 if (step == steps) {
3402 dst->vf.val = data[steps*src->ds_cnt];
3403 dst->vf.when = src->start + (step+1)*src->step;
3405 while (step != steps) {
3406 if (finite(data[step*src->ds_cnt])) {
3407 if (data[step*src->ds_cnt] < dst->vf.val) {
3408 dst->vf.val = data[steps*src->ds_cnt];
3409 dst->vf.when = src->start + (step+1)*src->step;
3416 /* The time value returned here is one step before the
3417 * actual time value. This is the start of the first
3421 while (step != steps && isnan(data[step*src->ds_cnt])) step++;
3422 if (step == steps) { /* all entries were NaN */
3426 dst->vf.val = data[step*src->ds_cnt];
3427 dst->vf.when = src->start + step*src->step;
3431 /* The time value returned here is the
3432 * actual time value. This is the end of the last
3436 while (step >= 0 && isnan(data[step*src->ds_cnt])) step--;
3437 if (step < 0) { /* all entries were NaN */
3441 dst->vf.val = data[step*src->ds_cnt];
3442 dst->vf.when = src->start + (step+1)*src->step;
3449 /* NaN <= -INF <= finite_values <= INF */
3451 vdef_percent_compar(a,b)
3454 /* Equality is not returned; this doesn't hurt except
3455 * (maybe) for a little performance.
3458 /* First catch NaN values. They are smallest */
3459 if (isnan( *(double *)a )) return -1;
3460 if (isnan( *(double *)b )) return 1;
3462 /* NaN doestn't reach this part so INF and -INF are extremes.
3463 * The sign from isinf() is compatible with the sign we return
3465 if (isinf( *(double *)a )) return isinf( *(double *)a );
3466 if (isinf( *(double *)b )) return isinf( *(double *)b );
3468 /* If we reach this, both values must be finite */
3469 if ( *(double *)a < *(double *)b ) return -1; else return 1;