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,GF_DEF, GF_CDEF};
41 enum if_en {IF_GIF=0,IF_PNG=1};
43 typedef struct col_trip_t {
44 int red; /* red = -1 is no color */
47 int i; /* color index assigned in gif image i=-1 is unasigned*/
51 typedef struct xlab_t {
52 long minsec; /* minimum sec per pix */
53 enum tmt_en gridtm; /* grid interval in what ?*/
54 long gridst; /* how many whats per grid*/
55 enum tmt_en mgridtm; /* label interval in what ?*/
56 long mgridst; /* how many whats per label*/
57 enum tmt_en labtm; /* label interval in what ?*/
58 long labst; /* how many whats per label*/
59 long precis; /* label precision -> label placement*/
60 char *stst; /* strftime string*/
64 {0, TMT_SECOND,30, TMT_MINUTE,5, TMT_MINUTE,5, 0,"%H:%M"},
65 {2, TMT_MINUTE,1, TMT_MINUTE,5, TMT_MINUTE,5, 0,"%H:%M"},
66 {5, TMT_MINUTE,2, TMT_MINUTE,10, TMT_MINUTE,10, 0,"%H:%M"},
67 {10, TMT_MINUTE,5, TMT_MINUTE,20, TMT_MINUTE,20, 0,"%H:%M"},
68 {30, TMT_MINUTE,10, TMT_HOUR,1, TMT_HOUR,1, 0,"%H:%M"},
69 {60, TMT_MINUTE,30, TMT_HOUR,2, TMT_HOUR,2, 0,"%H:%M"},
70 {180, TMT_HOUR,1, TMT_HOUR,6, TMT_HOUR,6, 0,"%H:%M"},
71 /*{300, TMT_HOUR,3, TMT_HOUR,12, TMT_HOUR,12, 12*3600,"%a %p"}, this looks silly*/
72 {600, TMT_HOUR,6, TMT_DAY,1, TMT_DAY,1, 24*3600,"%a"},
73 {1800, TMT_HOUR,12, TMT_DAY,1, TMT_DAY,2, 24*3600,"%a"},
74 {3600, TMT_DAY,1, TMT_WEEK,1, TMT_WEEK,1, 7*24*3600,"Week %W"},
75 {3*3600, TMT_WEEK,1, TMT_MONTH,1, TMT_WEEK,2, 7*24*3600,"Week %W"},
76 {6*3600, TMT_MONTH,1, TMT_MONTH,1, TMT_MONTH,1, 30*24*3600,"%b"},
77 {48*3600, TMT_MONTH,1, TMT_MONTH,3, TMT_MONTH,3, 30*24*3600,"%b"},
78 {10*24*3600, TMT_YEAR,1, TMT_YEAR,1, TMT_YEAR,1, 365*24*3600,"%y"},
79 {-1,TMT_MONTH,0,TMT_MONTH,0,TMT_MONTH,0,0,""}
82 /* sensible logarithmic y label intervals ...
83 the first element of each row defines the possible starting points on the
84 y axis ... the other specify the */
86 double yloglab[][12]= {{ 1e9, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
87 { 1e3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
88 { 1e1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
89 /* { 1e1, 1, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, */
90 { 1e1, 1, 2.5, 5, 7.5, 0, 0, 0, 0, 0, 0, 0 },
91 { 1e1, 1, 2, 4, 6, 8, 0, 0, 0, 0, 0, 0 },
92 { 1e1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0 },
93 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }};
95 /* sensible y label intervals ...*/
97 typedef struct ylab_t {
98 double grid; /* grid spacing */
99 int lfac[4]; /* associated label spacing*/
112 {100.0, {1,2, 5,10}},
113 {200.0, {1,5,10,20}},
114 {500.0, {1,2, 4,10}},
119 col_trip_t graph_col[] = { /* default colors */
120 {255,255,255,-1}, /* canvas */
121 {245,245,245,-1}, /* background */
122 {200,200,200,-1}, /* shade A */
123 {150,150,150,-1}, /* shade B */
124 {140,140,140,-1}, /* grid */
125 {130,30,30,-1}, /* major grid */
126 {0,0,0,-1}, /* font */
127 {0,0,0,-1}, /* frame */
128 {255,0,0,-1} /*arrow*/
131 /* this structure describes the elements which can make up a graph.
132 because they are quite diverse, not all elements will use all the
133 possible parts of the structure. */
135 #define FMT_LEG_LEN 200
137 #define FMT_LEG_LEN 2000
140 typedef struct graph_desc_t {
141 enum gf_en gf; /* graphing function */
142 char vname[30]; /* name of the variable */
143 long vidx; /* gdes reference */
144 char rrd[255]; /* name of the rrd_file containing data */
145 char ds_nam[DS_NAM_SIZE]; /* data source name */
146 long ds; /* data source number */
147 enum cf_en cf; /* consolidation function */
148 col_trip_t col; /* graph color */
149 char format[FMT_LEG_LEN+5]; /* format for PRINT AND GPRINT */
150 char legend[FMT_LEG_LEN+5]; /* legend*/
151 gdPoint legloc; /* location of legend */
152 double yrule; /* value for y rule line */
153 time_t xrule; /* value for x rule line */
154 rpnp_t *rpnp; /* instructions for CDEF function */
156 /* description of data fetched for the graph element */
157 time_t start,end; /* timestaps for first and last data element */
158 unsigned long step; /* time between samples */
159 unsigned long ds_cnt; /* how many data sources are there in the fetch */
160 long data_first; /* first pointer to this data */
161 char **ds_namv; /* name of datasources in the fetch. */
162 rrd_value_t *data; /* the raw data drawn from the rrd */
163 rrd_value_t *p_data; /* processed data, xsize elments */
167 #define ALTYGRID 0x01 /* use alternative y grid algorithm */
168 #define ALTAUTOSCALE 0x02 /* use alternative algorithm to find lower and upper bounds */
169 #define ALTAUTOSCALE_MAX 0x04 /* use alternative algorithm to find upper bounds */
170 #define NOLEGEND 0x08 /* use no legend */
172 typedef struct image_desc_t {
174 /* configuration of graph */
176 char graphfile[MAXPATH]; /* filename for graphic */
177 long xsize,ysize; /* graph area size in pixels */
178 col_trip_t graph_col[__GRC_END__]; /* real colors for the graph */
179 char ylegend[200]; /* legend along the yaxis */
180 char title[200]; /* title for graph */
181 int draw_x_grid; /* no x-grid at all */
182 int draw_y_grid; /* no x-grid at all */
183 xlab_t xlab_user; /* user defined labeling for xaxis */
184 char xlab_form[200]; /* format for the label on the xaxis */
186 double ygridstep; /* user defined step for y grid */
187 int ylabfact; /* every how many y grid shall a label be written ? */
189 time_t start,end; /* what time does the graph cover */
190 unsigned long step; /* any preference for the default step ? */
191 rrd_value_t minval,maxval; /* extreme values in the data */
192 int rigid; /* do not expand range even with
194 char* imginfo; /* construct an <IMG ... tag and return
196 int lazy; /* only update the gif if there is reasonable
197 probablility that the existing one is out of date */
198 int logarithmic; /* scale the yaxis logarithmic */
199 enum if_en imgformat; /* image format */
201 /* status information */
203 long xorigin,yorigin;/* where is (0,0) of the graph */
204 long xgif,ygif; /* total size of the gif */
205 int interlaced; /* will the graph be interlaced? */
206 double magfact; /* numerical magnitude*/
207 long base; /* 1000 or 1024 depending on what we graph */
208 char symbol; /* magnitude symbol for y-axis */
209 int unitsexponent; /* 10*exponent for units on y-asis */
210 int extra_flags; /* flags for boolean options */
213 long prt_c; /* number of print elements */
214 long gdes_c; /* number of graphics elements */
215 graph_desc_t *gdes; /* points to an array of graph elements */
220 int xtr(image_desc_t *,time_t);
221 int ytr(image_desc_t *, double);
222 enum gf_en gf_conv(char *);
223 enum if_en if_conv(char *);
224 enum tmt_en tmt_conv(char *);
225 enum grc_en grc_conv(char *);
226 int im_free(image_desc_t *);
227 void auto_scale( image_desc_t *, double *, char **, double *);
228 void si_unit( image_desc_t *);
229 void expand_range(image_desc_t *);
230 void reduce_data( enum cf_en, unsigned long, time_t *, time_t *, unsigned long *, unsigned long *, rrd_value_t **);
231 int data_fetch( image_desc_t *);
232 long find_var(image_desc_t *, char *);
233 long find_var_wrapper(void *arg1, char *key);
235 int data_calc( image_desc_t *);
236 int data_proc( image_desc_t *);
237 time_t find_first_time( time_t, enum tmt_en, long);
238 time_t find_next_time( time_t, enum tmt_en, long);
239 void gator( gdImagePtr, int, int);
240 int print_calc(image_desc_t *, char ***);
241 int leg_place(image_desc_t *);
242 int horizontal_grid(gdImagePtr, image_desc_t *);
243 int horizontal_log_grid(gdImagePtr, image_desc_t *);
244 void vertical_grid( gdImagePtr, image_desc_t *);
245 void axis_paint( image_desc_t *, gdImagePtr);
246 void grid_paint( image_desc_t *, gdImagePtr);
247 gdImagePtr MkLineBrush(image_desc_t *,long, enum gf_en);
248 int lazy_check(image_desc_t *);
249 int graph_paint(image_desc_t *, char ***);
250 int gdes_alloc(image_desc_t *);
251 int scan_for_col(char *, int, char *);
252 int rrd_graph(int, char **, char ***, int *, int *);
253 int bad_format(char *);
255 /* translate time values into x coordinates */
256 /*#define xtr(x) (int)((double)im->xorigin \
257 + ((double) im->xsize / (double)(im->end - im->start) ) \
258 * ((double)(x) - im->start)+0.5) */
259 /* initialize with xtr(im,0); */
261 xtr(image_desc_t *im,time_t mytime){
264 pixie = (double) im->xsize / (double)(im->end - im->start);
267 return (int)((double)im->xorigin
268 + pixie * ( mytime - im->start ) );
271 /* translate data values into y coordinates */
273 /* #define ytr(x) (int)((double)im->yorigin \
274 - ((double) im->ysize / (im->maxval - im->minval) ) \
275 * ((double)(x) - im->minval)+0.5) */
277 ytr(image_desc_t *im, double value){
282 pixie = (double) im->ysize / (im->maxval - im->minval);
284 pixie = (double) im->ysize / (log10(im->maxval) - log10(im->minval));
286 } else if(!im->logarithmic) {
287 yval = im->yorigin - pixie * (value - im->minval) + 0.5;
289 if (value < im->minval) {
292 yval = im->yorigin - pixie * (log10(value) - log10(im->minval)) + 0.5;
295 /* make sure we don't return anything too unreasonable. GD lib can
296 get terribly slow when drawing lines outside its scope. This is
297 especially problematic in connection with the rigid option */
300 } else if ((int)yval > im->yorigin) {
301 return im->yorigin+2;
302 } else if ((int) yval < im->yorigin - im->ysize){
303 return im->yorigin - im->ysize - 2;
313 /* conversion function for symbolic entry names */
316 #define conv_if(VV,VVV) \
317 if (strcmp(#VV, string) == 0) return VVV ;
319 enum gf_en gf_conv(char *string){
321 conv_if(PRINT,GF_PRINT)
322 conv_if(GPRINT,GF_GPRINT)
323 conv_if(COMMENT,GF_COMMENT)
324 conv_if(HRULE,GF_HRULE)
325 conv_if(VRULE,GF_VRULE)
326 conv_if(LINE1,GF_LINE1)
327 conv_if(LINE2,GF_LINE2)
328 conv_if(LINE3,GF_LINE3)
329 conv_if(AREA,GF_AREA)
330 conv_if(STACK,GF_STACK)
331 conv_if(TICK,GF_TICK)
333 conv_if(CDEF,GF_CDEF)
338 enum if_en if_conv(char *string){
346 enum tmt_en tmt_conv(char *string){
348 conv_if(SECOND,TMT_SECOND)
349 conv_if(MINUTE,TMT_MINUTE)
350 conv_if(HOUR,TMT_HOUR)
352 conv_if(WEEK,TMT_WEEK)
353 conv_if(MONTH,TMT_MONTH)
354 conv_if(YEAR,TMT_YEAR)
358 enum grc_en grc_conv(char *string){
360 conv_if(BACK,GRC_BACK)
361 conv_if(CANVAS,GRC_CANVAS)
362 conv_if(SHADEA,GRC_SHADEA)
363 conv_if(SHADEB,GRC_SHADEB)
364 conv_if(GRID,GRC_GRID)
365 conv_if(MGRID,GRC_MGRID)
366 conv_if(FONT,GRC_FONT)
367 conv_if(FRAME,GRC_FRAME)
368 conv_if(ARROW,GRC_ARROW)
378 im_free(image_desc_t *im)
381 if (im == NULL) return 0;
382 for(i=0;i<im->gdes_c;i++){
383 if (im->gdes[i].data_first){
384 /* careful here, because a single pointer can occur several times */
385 free (im->gdes[i].data);
386 if (im->gdes[i].ds_namv){
387 for (ii=0;ii<im->gdes[i].ds_cnt;ii++)
388 free(im->gdes[i].ds_namv[ii]);
389 free(im->gdes[i].ds_namv);
392 free (im->gdes[i].p_data);
393 free (im->gdes[i].rpnp);
399 /* find SI magnitude symbol for the given number*/
402 image_desc_t *im, /* image description */
409 char *symbol[] = {"a", /* 10e-18 Ato */
410 "f", /* 10e-15 Femto */
411 "p", /* 10e-12 Pico */
412 "n", /* 10e-9 Nano */
413 "u", /* 10e-6 Micro */
414 "m", /* 10e-3 Milli */
419 "T", /* 10e12 Terra */
420 "P", /* 10e15 Peta */
426 if (*value == 0.0 || isnan(*value) ) {
430 sindex = floor(log(fabs(*value))/log((double)im->base));
431 *magfact = pow((double)im->base, (double)sindex);
432 (*value) /= (*magfact);
434 if ( sindex <= symbcenter && sindex >= -symbcenter) {
435 (*symb_ptr) = symbol[sindex+symbcenter];
443 /* find SI magnitude symbol for the numbers on the y-axis*/
446 image_desc_t *im /* image description */
450 char symbol[] = {'a', /* 10e-18 Ato */
451 'f', /* 10e-15 Femto */
452 'p', /* 10e-12 Pico */
453 'n', /* 10e-9 Nano */
454 'u', /* 10e-6 Micro */
455 'm', /* 10e-3 Milli */
460 'T', /* 10e12 Terra */
461 'P', /* 10e15 Peta */
467 if (im->unitsexponent != 9999) {
468 /* unitsexponent = 9, 6, 3, 0, -3, -6, -9, etc */
469 digits = floor(im->unitsexponent / 3);
471 digits = floor( log( max( fabs(im->minval),fabs(im->maxval)))/log((double)im->base));
473 im->magfact = pow((double)im->base , digits);
476 printf("digits %6.3f im->magfact %6.3f\n",digits,im->magfact);
479 if ( ((digits+symbcenter) < sizeof(symbol)) &&
480 ((digits+symbcenter) >= 0) )
481 im->symbol = symbol[(int)digits+symbcenter];
486 /* move min and max values around to become sensible */
489 expand_range(image_desc_t *im)
491 double sensiblevalues[] ={1000.0,900.0,800.0,750.0,700.0,
492 600.0,500.0,400.0,300.0,250.0,
493 200.0,125.0,100.0,90.0,80.0,
494 75.0,70.0,60.0,50.0,40.0,30.0,
495 25.0,20.0,10.0,9.0,8.0,
496 7.0,6.0,5.0,4.0,3.5,3.0,
497 2.5,2.0,1.8,1.5,1.2,1.0,
498 0.8,0.7,0.6,0.5,0.4,0.3,0.2,0.1,0.0,-1};
500 double scaled_min,scaled_max;
507 printf("Min: %6.2f Max: %6.2f MagFactor: %6.2f\n",
508 im->minval,im->maxval,im->magfact);
511 if (isnan(im->ygridstep)){
512 if(im->extra_flags & ALTAUTOSCALE) {
513 /* measure the amplitude of the function. Make sure that
514 graph boundaries are slightly higher then max/min vals
515 so we can see amplitude on the graph */
518 delt = im->maxval - im->minval;
520 fact = 2.0 * pow(10.0,
521 floor(log10(max(fabs(im->minval), fabs(im->maxval)))) - 2);
523 adj = (fact - delt) * 0.55;
525 printf("Min: %6.2f Max: %6.2f delt: %6.2f fact: %6.2f adj: %6.2f\n", im->minval, im->maxval, delt, fact, adj);
531 else if(im->extra_flags & ALTAUTOSCALE_MAX) {
532 /* measure the amplitude of the function. Make sure that
533 graph boundaries are slightly higher than max vals
534 so we can see amplitude on the graph */
535 adj = (im->maxval - im->minval) * 0.1;
539 scaled_min = im->minval / im->magfact;
540 scaled_max = im->maxval / im->magfact;
542 for (i=1; sensiblevalues[i] > 0; i++){
543 if (sensiblevalues[i-1]>=scaled_min &&
544 sensiblevalues[i]<=scaled_min)
545 im->minval = sensiblevalues[i]*(im->magfact);
547 if (-sensiblevalues[i-1]<=scaled_min &&
548 -sensiblevalues[i]>=scaled_min)
549 im->minval = -sensiblevalues[i-1]*(im->magfact);
551 if (sensiblevalues[i-1] >= scaled_max &&
552 sensiblevalues[i] <= scaled_max)
553 im->maxval = sensiblevalues[i-1]*(im->magfact);
555 if (-sensiblevalues[i-1]<=scaled_max &&
556 -sensiblevalues[i] >=scaled_max)
557 im->maxval = -sensiblevalues[i]*(im->magfact);
561 /* adjust min and max to the grid definition if there is one */
562 im->minval = (double)im->ylabfact * im->ygridstep *
563 floor(im->minval / ((double)im->ylabfact * im->ygridstep));
564 im->maxval = (double)im->ylabfact * im->ygridstep *
565 ceil(im->maxval /( (double)im->ylabfact * im->ygridstep));
569 fprintf(stderr,"SCALED Min: %6.2f Max: %6.2f Factor: %6.2f\n",
570 im->minval,im->maxval,im->magfact);
575 /* reduce data reimplementation by Alex */
579 enum cf_en cf, /* which consolidation function ?*/
580 unsigned long cur_step, /* step the data currently is in */
581 time_t *start, /* start, end and step as requested ... */
582 time_t *end, /* ... by the application will be ... */
583 unsigned long *step, /* ... adjusted to represent reality */
584 unsigned long *ds_cnt, /* number of data sources in file */
585 rrd_value_t **data) /* two dimensional array containing the data */
587 int i,reduce_factor = ceil((double)(*step) / (double)cur_step);
588 unsigned long col,dst_row,row_cnt,start_offset,end_offset,skiprows=0;
589 rrd_value_t *srcptr,*dstptr;
591 (*step) = cur_step*reduce_factor; /* set new step size for reduced data */
595 /* We were given one extra row at the beginning of the interval.
596 ** We also need to return one extra row. The extra interval is
597 ** the one defined by the start time in both cases. It is not
598 ** used when graphing but maybe we can use it while reducing the
601 row_cnt = ((*end)-(*start))/cur_step +1;
603 /* alter start and end so that they are multiples of the new steptime.
604 ** End will be shifted towards the future and start will be shifted
605 ** towards the past in order to include the requested interval
607 end_offset = (*end) % (*step);
608 if (end_offset) end_offset = (*step)-end_offset;
609 start_offset = (*start) % (*step);
610 (*end) = (*end)+end_offset;
611 (*start) = (*start)-start_offset;
613 /* The first destination row is unknown yet it still needs
614 ** to be present in the returned data. Skip it.
615 ** Don't make it NaN or we might overwrite the source.
619 /* Depending on the amount of extra data needed at the
620 ** start of the destination, three things can happen:
621 ** -1- start_offset == 0: skip the extra source row
622 ** -2- start_offset == cur_step: do nothing
623 ** -3- start_offset > cur_step: skip some source rows and
624 ** fill one destination row with NaN
626 if (start_offset==0) {
629 } else if (start_offset!=cur_step) {
630 skiprows=((*step)-start_offset)/cur_step+1;
631 srcptr += ((*ds_cnt)*skiprows);
633 for (col=0;col<(*ds_cnt);col++) *dstptr++=DNAN;
636 /* If we had to alter the endtime, there won't be
637 ** enough data to fill the last row. This means
638 ** we have to skip some rows at the end
641 skiprows = ((*step)-end_offset)/cur_step;
646 /* Sanity check: row_cnt should be multiple of reduce_factor */
647 /* if this gets triggered, something is REALY WRONG ... we die immediately */
649 if (row_cnt%reduce_factor) {
650 printf("SANITY CHECK: %lu rows cannot be reduced by %i \n",
651 row_cnt,reduce_factor);
652 printf("BUG in reduce_data()\n");
656 /* Now combine reduce_factor intervals at a time
657 ** into one interval for the destination.
660 for (dst_row=0;row_cnt>=reduce_factor;dst_row++) {
661 for (col=0;col<(*ds_cnt);col++) {
662 rrd_value_t newval=DNAN;
663 unsigned long validval=0;
665 for (i=0;i<reduce_factor;i++) {
666 if (isnan(srcptr[i*(*ds_cnt)+col])) {
670 if (isnan(newval)) newval = srcptr[i*(*ds_cnt)+col];
678 newval += srcptr[i*(*ds_cnt)+col];
681 newval = min (newval,srcptr[i*(*ds_cnt)+col]);
684 /* an interval contains a failure if any subintervals contained a failure */
686 newval = max (newval,srcptr[i*(*ds_cnt)+col]);
689 newval = srcptr[i*(*ds_cnt)+col];
694 if (validval == 0){newval = DNAN;} else{
712 srcptr+=(*ds_cnt)*reduce_factor;
713 row_cnt-=reduce_factor;
716 /* If we had to alter the endtime, we didn't have enough
717 ** source rows to fill the last row. Fill it with NaN.
719 if (end_offset!=0) for (col=0;col<(*ds_cnt);col++) *dstptr++ = DNAN;
723 /* get the data required for the graphs from the
727 data_fetch( image_desc_t *im )
731 /* pull the data from the log files ... */
732 for (i=0;i<im->gdes_c;i++){
733 /* only GF_DEF elements fetch data */
734 if (im->gdes[i].gf != GF_DEF)
738 /* do we have it already ?*/
739 for (ii=0;ii<i;ii++){
740 if (im->gdes[ii].gf != GF_DEF)
742 if((strcmp(im->gdes[i].rrd,im->gdes[ii].rrd) == 0)
743 && (im->gdes[i].cf == im->gdes[ii].cf)){
744 /* OK the data it is here already ...
745 * we just copy the header portion */
746 im->gdes[i].start = im->gdes[ii].start;
747 im->gdes[i].end = im->gdes[ii].end;
748 im->gdes[i].step = im->gdes[ii].step;
749 im->gdes[i].ds_cnt = im->gdes[ii].ds_cnt;
750 im->gdes[i].ds_namv = im->gdes[ii].ds_namv;
751 im->gdes[i].data = im->gdes[ii].data;
752 im->gdes[i].data_first = 0;
759 unsigned long ft_step = im->gdes[i].step ;
761 if((rrd_fetch_fn(im->gdes[i].rrd,
767 &im->gdes[i].ds_namv,
768 &im->gdes[i].data)) == -1){
771 im->gdes[i].data_first = 1;
773 if (ft_step < im->gdes[i].step) {
774 reduce_data(im->gdes[i].cf,
782 im->gdes[i].step = ft_step;
786 /* lets see if the required data source is realy there */
787 for(ii=0;ii<im->gdes[i].ds_cnt;ii++){
788 if(strcmp(im->gdes[i].ds_namv[ii],im->gdes[i].ds_nam) == 0){
791 if (im->gdes[i].ds== -1){
792 rrd_set_error("No DS called '%s' in '%s'",
793 im->gdes[i].ds_nam,im->gdes[i].rrd);
801 /* evaluate the expressions in the CDEF functions */
803 /*************************************************************
805 *************************************************************/
808 find_var_wrapper(void *arg1, char *key)
810 return find_var((image_desc_t *) arg1, key);
813 /* find gdes containing var*/
815 find_var(image_desc_t *im, char *key){
817 for(ii=0;ii<im->gdes_c-1;ii++){
818 if((im->gdes[ii].gf == GF_DEF
819 || im->gdes[ii].gf == GF_CDEF)
820 && (strcmp(im->gdes[ii].vname,key) == 0)){
827 /* find the largest common denominator for all the numbers
828 in the 0 terminated num array */
833 for (i=0;num[i+1]!=0;i++){
835 rest=num[i] % num[i+1];
836 num[i]=num[i+1]; num[i+1]=rest;
840 /* return i==0?num[i]:num[i-1]; */
844 /* run the rpn calculator on all the CDEF arguments */
846 data_calc( image_desc_t *im){
850 long *steparray, rpi;
855 rpnstack_init(&rpnstack);
857 for (gdi=0;gdi<im->gdes_c;gdi++){
858 /* only GF_CDEF elements are of interest */
859 if (im->gdes[gdi].gf != GF_CDEF)
861 im->gdes[gdi].ds_cnt = 1;
862 im->gdes[gdi].ds = 0;
863 im->gdes[gdi].data_first = 1;
864 im->gdes[gdi].start = 0;
865 im->gdes[gdi].end = 0;
870 /* find the variables in the expression. And calc the lowest
871 common denominator of all step sizes of the data sources involved.
872 this will be the step size for the cdef created data source*/
874 for(rpi=0;im->gdes[gdi].rpnp[rpi].op != OP_END;rpi++){
875 if(im->gdes[gdi].rpnp[rpi].op == OP_VARIABLE){
876 long ptr = im->gdes[gdi].rpnp[rpi].ptr;
877 if ((steparray = rrd_realloc(steparray, (++stepcnt+1)*sizeof(*steparray)))==NULL){
878 rrd_set_error("realloc steparray");
879 rpnstack_free(&rpnstack);
884 steparray[stepcnt-1] = im->gdes[ptr].step;
886 /* adjust start and end of cdef (gdi) so that it runs from
887 the latest start point to the earliest endpoint of any of the
888 rras involved (ptr) */
890 if(im->gdes[gdi].start < im->gdes[ptr].start)
891 im->gdes[gdi].start = im->gdes[ptr].start;
893 if(im->gdes[gdi].end == 0
894 || im->gdes[gdi].end > im->gdes[ptr].end)
895 im->gdes[gdi].end = im->gdes[ptr].end;
897 /* store pointer to the first element of the rra providing
898 data for variable, further save step size and data source count
900 im->gdes[gdi].rpnp[rpi].data =
901 im->gdes[ptr].data + im->gdes[ptr].ds;
902 im->gdes[gdi].rpnp[rpi].step = im->gdes[ptr].step;
903 im->gdes[gdi].rpnp[rpi].ds_cnt = im->gdes[ptr].ds_cnt;
904 /* backoff the *.data ptr; this is done so rpncalc() function
905 * doesn't have to treat the first case differently */
906 im->gdes[gdi].rpnp[rpi].data -= im->gdes[ptr].ds_cnt;
910 if(steparray == NULL){
911 rrd_set_error("rpn expressions without variables are not supported");
912 rpnstack_free(&rpnstack);
915 steparray[stepcnt]=0;
916 /* now find the step for the result of the cdef. so that we land on
917 each step in all of the variables rras */
919 im->gdes[gdi].step = lcd(steparray);
924 if((im->gdes[gdi].data = malloc(((im->gdes[gdi].end
925 -im->gdes[gdi].start)
926 / im->gdes[gdi].step +1)
927 * sizeof(double)))==NULL){
928 rrd_set_error("malloc im->gdes[gdi].data");
929 rpnstack_free(&rpnstack);
933 /* step through the new cdef results array and calculate the values */
934 for (now = im->gdes[gdi].start;
935 now<=im->gdes[gdi].end;
936 now += im->gdes[gdi].step){
937 rpnp_t *rpnp = im -> gdes[gdi].rpnp;
939 /* 3rd arg of rpn_calc is for OP_VARIABLE lookups;
940 * in this case we are advancing by timesteps;
941 * we use the fact that time_t is a synonym for long
943 if (rpn_calc(rpnp,&rpnstack,(long) now,
944 im->gdes[gdi].data,++dataidx) == -1)
946 /* rpn_calc sets the error string */
947 rpnstack_free(&rpnstack);
951 } /* enumerate over time steps within a CDEF */
952 } /* enumerate over CDEFs */
953 rpnstack_free(&rpnstack);
957 /* massage data so, that we get one value for each x coordinate in the graph */
959 data_proc( image_desc_t *im ){
961 double pixstep = (double)(im->end-im->start)
962 /(double)im->xsize; /* how much time
963 passes in one pixel */
965 double minval=DNAN,maxval=DNAN;
967 unsigned long gr_time;
969 /* memory for the processed data */
970 for(i=0;i<im->gdes_c;i++){
971 if((im->gdes[i].gf==GF_LINE1) ||
972 (im->gdes[i].gf==GF_LINE2) ||
973 (im->gdes[i].gf==GF_LINE3) ||
974 (im->gdes[i].gf==GF_AREA) ||
975 (im->gdes[i].gf==GF_TICK) ||
976 (im->gdes[i].gf==GF_STACK)){
977 if((im->gdes[i].p_data = malloc((im->xsize +1)
978 * sizeof(rrd_value_t)))==NULL){
979 rrd_set_error("malloc data_proc");
985 for(i=0;i<im->xsize;i++){
987 gr_time = im->start+pixstep*i; /* time of the
991 for(ii=0;ii<im->gdes_c;ii++){
993 switch(im->gdes[ii].gf){
1001 vidx = im->gdes[ii].vidx;
1004 im->gdes[vidx].data[
1005 ((unsigned long)floor((double)
1006 (gr_time - im->gdes[vidx].start )
1007 / im->gdes[vidx].step)+1)
1009 /* added one because data was not being aligned properly
1010 this fixes it. We may also be having a problem in fetch ... */
1012 *im->gdes[vidx].ds_cnt
1013 +im->gdes[vidx].ds];
1015 if (! isnan(value)) {
1017 im->gdes[ii].p_data[i] = paintval;
1018 /* GF_TICK: the data values are not relevant for min and max */
1019 if (finite(paintval) && im->gdes[ii].gf != GF_TICK ){
1020 if (isnan(minval) || paintval < minval)
1022 if (isnan(maxval) || paintval > maxval)
1026 im->gdes[ii].p_data[i] = DNAN;
1041 /* if min or max have not been asigned a value this is because
1042 there was no data in the graph ... this is not good ...
1043 lets set these to dummy values then ... */
1045 if (isnan(minval)) minval = 0.0;
1046 if (isnan(maxval)) maxval = 1.0;
1048 /* adjust min and max values */
1049 if (isnan(im->minval)
1050 || ((!im->logarithmic && !im->rigid) /* don't adjust low-end with log scale */
1051 && im->minval > minval))
1052 im->minval = minval;
1053 if (isnan(im->maxval)
1055 && im->maxval < maxval)){
1056 if (im->logarithmic)
1057 im->maxval = maxval * 1.1;
1059 im->maxval = maxval;
1061 /* make sure min and max are not equal */
1062 if (im->minval == im->maxval) {
1064 if (! im->logarithmic) {
1068 /* make sure min and max are not both zero */
1069 if (im->maxval == 0.0) {
1079 /* identify the point where the first gridline, label ... gets placed */
1083 time_t start, /* what is the initial time */
1084 enum tmt_en baseint, /* what is the basic interval */
1085 long basestep /* how many if these do we jump a time */
1089 tm = *localtime(&start);
1092 tm.tm_sec -= tm.tm_sec % basestep; break;
1095 tm.tm_min -= tm.tm_min % basestep;
1100 tm.tm_hour -= tm.tm_hour % basestep; break;
1102 /* we do NOT look at the basestep for this ... */
1105 tm.tm_hour = 0; break;
1107 /* we do NOT look at the basestep for this ... */
1111 tm.tm_mday -= tm.tm_wday -1; /* -1 because we want the monday */
1112 if (tm.tm_wday==0) tm.tm_mday -= 7; /* we want the *previous* monday */
1119 tm.tm_mon -= tm.tm_mon % basestep; break;
1127 tm.tm_year -= (tm.tm_year+1900) % basestep;
1132 /* identify the point where the next gridline, label ... gets placed */
1135 time_t current, /* what is the initial time */
1136 enum tmt_en baseint, /* what is the basic interval */
1137 long basestep /* how many if these do we jump a time */
1142 tm = *localtime(¤t);
1146 tm.tm_sec += basestep; break;
1148 tm.tm_min += basestep; break;
1150 tm.tm_hour += basestep; break;
1152 tm.tm_mday += basestep; break;
1154 tm.tm_mday += 7*basestep; break;
1156 tm.tm_mon += basestep; break;
1158 tm.tm_year += basestep;
1160 madetime = mktime(&tm);
1161 } while (madetime == -1); /* this is necessary to skip impssible times
1162 like the daylight saving time skips */
1167 void gator( gdImagePtr gif, int x, int y){
1169 /* this function puts the name of the author and the tool into the
1170 graph. Remove if you must, but please note, that it is here,
1171 because I would like people who look at rrdtool generated graphs to
1172 see what was used to do it. No obviously you can also add a credit
1173 line to your webpage or printed document, this is fine with me. But
1174 as I have no control over this, I added the little tag in here.
1177 /* the fact that the text of what gets put into the graph is not
1178 visible in the function, has lead some to think this is for
1179 obfuscation reasons. While this is a nice side effect (I addmit),
1180 it is not the prime reason. The prime reason is, that the font
1181 used, is so small, that I had to hand edit the characters to ensure
1182 readability. I could thus not use the normal gd functions to write,
1183 but had to embed a slightly compressed bitmap version into the code.
1186 int li[]={0,0,1, 0,4,5, 0,8,9, 0,12,14, 0,17,17, 0,21,21,
1187 0,24,24, 0,34,34, 0,40,42, 0,45,45, 0,48,49, 0,52,54,
1188 0,61,61, 0,64,66, 0,68,70, 0,72,74, 0,76,76, 0,78,78,
1190 1,0,0, 1,2,2, 1,4,4, 1,6,6, 1,8,8, 1,10,10,
1191 1,13,13, 1,16,16, 1,18,18, 1,20,20, 1,22,22, 1,24,24,
1192 1,34,34, 1,41,41, 1,44,44, 1,46,46, 1,48,48, 1,50,50,
1193 1,53,53, 1,60,60, 1,62,62, 1,64,64, 1,69,69, 1,73,73,
1194 1,76,76, 1,78,78, 1,80,80, 1,84,84, 1,86,86,
1195 2,0,1, 2,4,5, 2,8,8, 2,10,10, 2,13,13, 2,16,16,
1196 2,18,18, 2,20,20, 2,22,22, 2,24,24, 2,33,33, 2,41,41,
1197 2,44,44, 2,46,46, 2,48,49, 2,53,53, 2,60,60, 2,62,62,
1198 2,64,65, 2,69,69, 2,73,73, 2,76,77, 2,80,81, 2,84,85,
1199 3,0,0, 3,2,2, 3,4,4, 3,6,6, 3,8,8, 3,10,10,
1200 3,13,13, 3,16,16, 3,18,18, 3,20,20, 3,22,22, 3,24,24,
1201 3,32,32, 3,41,41, 3,44,44, 3,46,46, 3,48,48, 3,50,50,
1202 3,53,53, 3,60,60, 3,62,62, 3,64,64, 3,69,69, 3,73,73,
1203 3,76,76, 3,78,78, 3,80,80, 3,84,84, 3,86,86,
1204 4,0,0, 4,2,2, 4,4,4, 4,6,6, 4,8,9, 4,13,13,
1205 4,17,17, 4,21,21, 4,24,26, 4,32,32, 4,41,41, 4,45,45,
1206 4,48,49, 4,52,54, 4,61,61, 4,64,66, 4,69,69, 4,72,74,
1207 4,76,76, 4,78,78, 4,80,82, 4,84,84};
1209 for(i=0; i<DIM(li); i=i+3)
1210 for(ii=y+li[i+1]; ii<=y+li[i+2];ii++)
1211 gdImageSetPixel(gif,x-li[i],ii,graph_col[GRC_GRID].i);
1215 /* calculate values required for PRINT and GPRINT functions */
1218 print_calc(image_desc_t *im, char ***prdata)
1220 long i,ii,validsteps;
1222 int graphelement = 0;
1225 double magfact = -1;
1229 if (im->imginfo) prlines++;
1230 for(i=0;i<im->gdes_c;i++){
1231 switch(im->gdes[i].gf){
1234 if(((*prdata) = rrd_realloc((*prdata),prlines*sizeof(char *)))==NULL){
1235 rrd_set_error("realloc prdata");
1239 vidx = im->gdes[i].vidx;
1240 max_ii =((im->gdes[vidx].end
1241 - im->gdes[vidx].start)
1242 /im->gdes[vidx].step
1243 *im->gdes[vidx].ds_cnt);
1246 for(ii=im->gdes[vidx].ds+im->gdes[vidx].ds_cnt;
1247 ii < max_ii+im->gdes[vidx].ds_cnt;
1248 ii+=im->gdes[vidx].ds_cnt){
1249 if (! finite(im->gdes[vidx].data[ii]))
1251 if (isnan(printval)){
1252 printval = im->gdes[vidx].data[ii];
1257 switch (im->gdes[i].cf){
1260 case CF_DEVSEASONAL:
1264 printval += im->gdes[vidx].data[ii];
1267 printval = min( printval, im->gdes[vidx].data[ii]);
1271 printval = max( printval, im->gdes[vidx].data[ii]);
1274 printval = im->gdes[vidx].data[ii];
1277 if (im->gdes[i].cf == CF_AVERAGE || im -> gdes[i].cf > CF_LAST) {
1278 if (validsteps > 1) {
1279 printval = (printval / validsteps);
1282 if ((percent_s = strstr(im->gdes[i].format,"%S")) != NULL) {
1283 /* Magfact is set to -1 upon entry to print_calc. If it
1284 * is still less than 0, then we need to run auto_scale.
1285 * Otherwise, put the value into the correct units. If
1286 * the value is 0, then do not set the symbol or magnification
1287 * so next the calculation will be performed again. */
1288 if (magfact < 0.0) {
1289 auto_scale(im,&printval,&si_symb,&magfact);
1290 if (printval == 0.0)
1293 printval /= magfact;
1295 *(++percent_s) = 's';
1297 else if (strstr(im->gdes[i].format,"%s") != NULL) {
1298 auto_scale(im,&printval,&si_symb,&magfact);
1300 if (im->gdes[i].gf == GF_PRINT){
1301 (*prdata)[prlines-2] = malloc((FMT_LEG_LEN+2)*sizeof(char));
1302 if (bad_format(im->gdes[i].format)) {
1303 rrd_set_error("bad format for [G]PRINT in '%s'", im->gdes[i].format);
1306 #ifdef HAVE_SNPRINTF
1307 snprintf((*prdata)[prlines-2],FMT_LEG_LEN,im->gdes[i].format,printval,si_symb);
1309 sprintf((*prdata)[prlines-2],im->gdes[i].format,printval,si_symb);
1311 (*prdata)[prlines-1] = NULL;
1315 if (bad_format(im->gdes[i].format)) {
1316 rrd_set_error("bad format for [G]PRINT in '%s'", im->gdes[i].format);
1319 #ifdef HAVE_SNPRINTF
1320 snprintf(im->gdes[i].legend,FMT_LEG_LEN-2,im->gdes[i].format,printval,si_symb);
1322 sprintf(im->gdes[i].legend,im->gdes[i].format,printval,si_symb);
1343 return graphelement;
1347 /* place legends with color spots */
1349 leg_place(image_desc_t *im)
1352 int interleg = SmallFont->w*2;
1353 int box = SmallFont->h*1.2;
1354 int border = SmallFont->w*2;
1355 int fill=0, fill_last;
1357 int leg_x = border, leg_y = im->ygif;
1361 char prt_fctn; /*special printfunctions */
1364 if( !(im->extra_flags & NOLEGEND) ) {
1365 if ((legspace = malloc(im->gdes_c*sizeof(int)))==NULL){
1366 rrd_set_error("malloc for legspace");
1370 for(i=0;i<im->gdes_c;i++){
1373 leg_cc = strlen(im->gdes[i].legend);
1375 /* is there a controle code ant the end of the legend string ? */
1376 if (leg_cc >= 2 && im->gdes[i].legend[leg_cc-2] == '\\') {
1377 prt_fctn = im->gdes[i].legend[leg_cc-1];
1379 im->gdes[i].legend[leg_cc] = '\0';
1383 /* remove exess space */
1384 while (prt_fctn=='g' &&
1386 im->gdes[i].legend[leg_cc-1]==' '){
1388 im->gdes[i].legend[leg_cc]='\0';
1391 legspace[i]=(prt_fctn=='g' ? 0 : interleg);
1394 /* no interleg space if string ends in \g */
1395 fill += legspace[i];
1397 if (im->gdes[i].gf != GF_GPRINT &&
1398 im->gdes[i].gf != GF_COMMENT) {
1401 fill += leg_cc * SmallFont->w;
1406 /* who said there was a special tag ... ?*/
1407 if (prt_fctn=='g') {
1410 if (prt_fctn == '\0') {
1411 if (i == im->gdes_c -1 ) prt_fctn ='l';
1413 /* is it time to place the legends ? */
1414 if (fill > im->xgif - 2*border){
1429 if (prt_fctn != '\0'){
1431 if (leg_c >= 2 && prt_fctn == 'j') {
1432 glue = (im->xgif - fill - 2* border) / (leg_c-1);
1433 /* if (glue > 2 * SmallFont->w) glue = 0; */
1437 if (prt_fctn =='c') leg_x = (im->xgif - fill) / 2.0;
1438 if (prt_fctn =='r') leg_x = im->xgif - fill - border;
1440 for(ii=mark;ii<=i;ii++){
1441 if(im->gdes[ii].legend[0]=='\0')
1443 im->gdes[ii].legloc.x = leg_x;
1444 im->gdes[ii].legloc.y = leg_y;
1446 + strlen(im->gdes[ii].legend)*SmallFont->w
1449 if (im->gdes[ii].gf != GF_GPRINT &&
1450 im->gdes[ii].gf != GF_COMMENT)
1453 leg_y = leg_y + SmallFont->h*1.2;
1454 if (prt_fctn == 's') leg_y -= SmallFont->h *0.5;
1466 /* create a grid on the graph. it determines what to do
1467 from the values of xsize, start and end */
1469 /* the xaxis labels are determined from the number of seconds per pixel
1470 in the requested graph */
1475 horizontal_grid(gdImagePtr gif, image_desc_t *im)
1483 char graph_label[100];
1484 gdPoint polyPoints[4];
1485 int labfact,gridind;
1486 int styleMinor[2],styleMajor[2];
1487 int decimals, fractionals;
1492 range = im->maxval - im->minval;
1493 scaledrange = range / im->magfact;
1495 /* does the scale of this graph make it impossible to put lines
1496 on it? If so, give up. */
1497 if (isnan(scaledrange)) {
1501 styleMinor[0] = graph_col[GRC_GRID].i;
1502 styleMinor[1] = gdTransparent;
1504 styleMajor[0] = graph_col[GRC_MGRID].i;
1505 styleMajor[1] = gdTransparent;
1507 /* find grid spaceing */
1509 if(isnan(im->ygridstep)){
1510 if(im->extra_flags & ALTYGRID) {
1511 /* find the value with max number of digits. Get number of digits */
1512 decimals = ceil(log10(max(fabs(im->maxval), fabs(im->minval))));
1513 if(decimals <= 0) /* everything is small. make place for zero */
1516 fractionals = floor(log10(range));
1517 if(fractionals < 0) /* small amplitude. */
1518 sprintf(labfmt, "%%%d.%df", decimals - fractionals + 1, -fractionals + 1);
1520 sprintf(labfmt, "%%%d.1f", decimals + 1);
1521 gridstep = pow((double)10, (double)fractionals);
1522 if(gridstep == 0) /* range is one -> 0.1 is reasonable scale */
1524 /* should have at least 5 lines but no more then 15 */
1525 if(range/gridstep < 5)
1527 if(range/gridstep > 15)
1529 if(range/gridstep > 5) {
1531 if(range/gridstep > 8)
1540 for(i=0;ylab[i].grid > 0;i++){
1541 pixel = im->ysize / (scaledrange / ylab[i].grid);
1542 if (gridind == -1 && pixel > 5) {
1549 if (pixel * ylab[gridind].lfac[i] >= 2 * SmallFont->h) {
1550 labfact = ylab[gridind].lfac[i];
1555 gridstep = ylab[gridind].grid * im->magfact;
1558 gridstep = im->ygridstep;
1559 labfact = im->ylabfact;
1562 polyPoints[0].x=im->xorigin;
1563 polyPoints[1].x=im->xorigin+im->xsize;
1564 sgrid = (int)( im->minval / gridstep - 1);
1565 egrid = (int)( im->maxval / gridstep + 1);
1566 scaledstep = gridstep/im->magfact;
1567 for (i = sgrid; i <= egrid; i++){
1568 polyPoints[0].y=ytr(im,gridstep*i);
1569 if ( polyPoints[0].y >= im->yorigin-im->ysize
1570 && polyPoints[0].y <= im->yorigin) {
1571 if(i % labfact == 0){
1572 if (i==0 || im->symbol == ' ') {
1574 if(im->extra_flags & ALTYGRID) {
1575 sprintf(graph_label,labfmt,scaledstep*i);
1578 sprintf(graph_label,"%4.1f",scaledstep*i);
1581 sprintf(graph_label,"%4.0f",scaledstep*i);
1585 sprintf(graph_label,"%4.1f %c",scaledstep*i, im->symbol);
1587 sprintf(graph_label,"%4.0f %c",scaledstep*i, im->symbol);
1591 gdImageString(gif, SmallFont,
1592 (polyPoints[0].x - (strlen(graph_label) *
1594 polyPoints[0].y - SmallFont->h/2+1,
1595 (unsigned char *)graph_label, graph_col[GRC_FONT].i);
1597 gdImageSetStyle(gif, styleMajor, 2);
1599 gdImageLine(gif, polyPoints[0].x-2,polyPoints[0].y,
1600 polyPoints[0].x+2,polyPoints[0].y,graph_col[GRC_MGRID].i);
1601 gdImageLine(gif, polyPoints[1].x-2,polyPoints[0].y,
1602 polyPoints[1].x+2,polyPoints[0].y,graph_col[GRC_MGRID].i);
1604 gdImageSetStyle(gif, styleMinor, 2);
1605 gdImageLine(gif, polyPoints[0].x-1,polyPoints[0].y,
1606 polyPoints[0].x+1,polyPoints[0].y,graph_col[GRC_GRID].i);
1607 gdImageLine(gif, polyPoints[1].x-1,polyPoints[0].y,
1608 polyPoints[1].x+1,polyPoints[0].y,graph_col[GRC_GRID].i);
1610 gdImageLine(gif, polyPoints[0].x,polyPoints[0].y,
1611 polyPoints[1].x,polyPoints[0].y,gdStyled);
1614 /* if(im->minval * im->maxval < 0){
1615 polyPoints[0].y=ytr(0);
1616 gdImageLine(gif, polyPoints[0].x,polyPoints[0].y,
1617 polyPoints[1].x,polyPoints[0].y,graph_col[GRC_MGRID].i);
1623 /* logaritmic horizontal grid */
1625 horizontal_log_grid(gdImagePtr gif, image_desc_t *im)
1629 int minoridx=0, majoridx=0;
1630 char graph_label[100];
1631 gdPoint polyPoints[4];
1632 int styleMinor[2],styleMajor[2];
1633 double value, pixperstep, minstep;
1635 /* find grid spaceing */
1636 pixpex= (double)im->ysize / (log10(im->maxval) - log10(im->minval));
1638 if (isnan(pixpex)) {
1642 for(i=0;yloglab[i][0] > 0;i++){
1643 minstep = log10(yloglab[i][0]);
1644 for(ii=1;yloglab[i][ii+1] > 0;ii++){
1645 if(yloglab[i][ii+2]==0){
1646 minstep = log10(yloglab[i][ii+1])-log10(yloglab[i][ii]);
1650 pixperstep = pixpex * minstep;
1651 if(pixperstep > 5){minoridx = i;}
1652 if(pixperstep > 2 * SmallFont->h){majoridx = i;}
1655 styleMinor[0] = graph_col[GRC_GRID].i;
1656 styleMinor[1] = gdTransparent;
1658 styleMajor[0] = graph_col[GRC_MGRID].i;
1659 styleMajor[1] = gdTransparent;
1661 polyPoints[0].x=im->xorigin;
1662 polyPoints[1].x=im->xorigin+im->xsize;
1663 /* paint minor grid */
1664 for (value = pow((double)10, log10(im->minval)
1665 - fmod(log10(im->minval),log10(yloglab[minoridx][0])));
1666 value <= im->maxval;
1667 value *= yloglab[minoridx][0]){
1668 if (value < im->minval) continue;
1670 while(yloglab[minoridx][++i] > 0){
1671 polyPoints[0].y = ytr(im,value * yloglab[minoridx][i]);
1672 if (polyPoints[0].y <= im->yorigin - im->ysize) break;
1673 gdImageSetStyle(gif, styleMinor, 2);
1674 gdImageLine(gif, polyPoints[0].x-1,polyPoints[0].y,
1675 polyPoints[0].x+1,polyPoints[0].y,graph_col[GRC_GRID].i);
1676 gdImageLine(gif, polyPoints[1].x-1,polyPoints[0].y,
1677 polyPoints[1].x+1,polyPoints[0].y,graph_col[GRC_GRID].i);
1679 gdImageLine(gif, polyPoints[0].x,polyPoints[0].y,
1680 polyPoints[1].x,polyPoints[0].y,gdStyled);
1684 /* paint major grid and labels*/
1685 for (value = pow((double)10, log10(im->minval)
1686 - fmod(log10(im->minval),log10(yloglab[majoridx][0])));
1687 value <= im->maxval;
1688 value *= yloglab[majoridx][0]){
1689 if (value < im->minval) continue;
1691 while(yloglab[majoridx][++i] > 0){
1692 polyPoints[0].y = ytr(im,value * yloglab[majoridx][i]);
1693 if (polyPoints[0].y <= im->yorigin - im->ysize) break;
1694 gdImageSetStyle(gif, styleMajor, 2);
1695 gdImageLine(gif, polyPoints[0].x-2,polyPoints[0].y,
1696 polyPoints[0].x+2,polyPoints[0].y,graph_col[GRC_MGRID].i);
1697 gdImageLine(gif, polyPoints[1].x-2,polyPoints[0].y,
1698 polyPoints[1].x+2,polyPoints[0].y,graph_col[GRC_MGRID].i);
1700 gdImageLine(gif, polyPoints[0].x,polyPoints[0].y,
1701 polyPoints[1].x,polyPoints[0].y,gdStyled);
1702 sprintf(graph_label,"%3.0e",value * yloglab[majoridx][i]);
1703 gdImageString(gif, SmallFont,
1704 (polyPoints[0].x - (strlen(graph_label) *
1706 polyPoints[0].y - SmallFont->h/2+1,
1707 (unsigned char *)graph_label, graph_col[GRC_FONT].i);
1719 int xlab_sel; /* which sort of label and grid ? */
1722 char graph_label[100];
1723 gdPoint polyPoints[4]; /* points for filled graph and more*/
1725 /* style for grid lines */
1729 /* the type of time grid is determined by finding
1730 the number of seconds per pixel in the graph */
1733 if(im->xlab_user.minsec == -1){
1734 factor=(im->end - im->start)/im->xsize;
1736 while ( xlab[xlab_sel+1].minsec != -1
1737 && xlab[xlab_sel+1].minsec <= factor){ xlab_sel++; }
1738 im->xlab_user.gridtm = xlab[xlab_sel].gridtm;
1739 im->xlab_user.gridst = xlab[xlab_sel].gridst;
1740 im->xlab_user.mgridtm = xlab[xlab_sel].mgridtm;
1741 im->xlab_user.mgridst = xlab[xlab_sel].mgridst;
1742 im->xlab_user.labtm = xlab[xlab_sel].labtm;
1743 im->xlab_user.labst = xlab[xlab_sel].labst;
1744 im->xlab_user.precis = xlab[xlab_sel].precis;
1745 im->xlab_user.stst = xlab[xlab_sel].stst;
1748 /* y coords are the same for every line ... */
1749 polyPoints[0].y = im->yorigin;
1750 polyPoints[1].y = im->yorigin-im->ysize;
1752 /* paint the minor grid */
1753 for(ti = find_first_time(im->start,
1754 im->xlab_user.gridtm,
1755 im->xlab_user.gridst);
1757 ti = find_next_time(ti,im->xlab_user.gridtm,im->xlab_user.gridst)
1759 /* are we inside the graph ? */
1760 if (ti < im->start || ti > im->end) continue;
1761 polyPoints[0].x = xtr(im,ti);
1762 styleDotted[0] = graph_col[GRC_GRID].i;
1763 styleDotted[1] = gdTransparent;
1765 gdImageSetStyle(gif, styleDotted, 2);
1767 gdImageLine(gif, polyPoints[0].x,polyPoints[0].y,
1768 polyPoints[0].x,polyPoints[1].y,gdStyled);
1769 gdImageLine(gif, polyPoints[0].x,polyPoints[0].y-1,
1770 polyPoints[0].x,polyPoints[0].y+1,graph_col[GRC_GRID].i);
1771 gdImageLine(gif, polyPoints[0].x,polyPoints[1].y-1,
1772 polyPoints[0].x,polyPoints[1].y+1,graph_col[GRC_GRID].i);
1775 /* paint the major grid */
1776 for(ti = find_first_time(im->start,
1777 im->xlab_user.mgridtm,
1778 im->xlab_user.mgridst);
1780 ti = find_next_time(ti,im->xlab_user.mgridtm,im->xlab_user.mgridst)
1782 /* are we inside the graph ? */
1783 if (ti < im->start || ti > im->end) continue;
1784 polyPoints[0].x = xtr(im,ti);
1785 styleDotted[0] = graph_col[GRC_MGRID].i;
1786 styleDotted[1] = gdTransparent;
1787 gdImageSetStyle(gif, styleDotted, 2);
1789 gdImageLine(gif, polyPoints[0].x,polyPoints[0].y,
1790 polyPoints[0].x,polyPoints[1].y,gdStyled);
1791 gdImageLine(gif, polyPoints[0].x,polyPoints[0].y-2,
1792 polyPoints[0].x,polyPoints[0].y+2,graph_col[GRC_MGRID].i);
1793 gdImageLine(gif, polyPoints[0].x,polyPoints[1].y-2,
1794 polyPoints[0].x,polyPoints[1].y+2,graph_col[GRC_MGRID].i);
1796 /* paint the labels below the graph */
1797 for(ti = find_first_time(im->start,
1798 im->xlab_user.labtm,
1799 im->xlab_user.labst);
1801 ti = find_next_time(ti,im->xlab_user.labtm,im->xlab_user.labst)
1804 tilab= ti + im->xlab_user.precis/2; /* correct time for the label */
1807 strftime(graph_label,99,im->xlab_user.stst,localtime(&tilab));
1809 # error "your libc has no strftime I guess we'll abort the exercise here."
1811 width=strlen(graph_label) * SmallFont->w;
1812 gr_pos=xtr(im,tilab) - width/2;
1813 if (gr_pos >= im->xorigin
1814 && gr_pos + width <= im->xorigin+im->xsize)
1815 gdImageString(gif, SmallFont,
1816 gr_pos, polyPoints[0].y+4,
1817 (unsigned char *)graph_label, graph_col[GRC_FONT].i);
1829 /* draw x and y axis */
1830 gdImageLine(gif, im->xorigin+im->xsize,im->yorigin,
1831 im->xorigin+im->xsize,im->yorigin-im->ysize,
1832 graph_col[GRC_GRID].i);
1834 gdImageLine(gif, im->xorigin,im->yorigin-im->ysize,
1835 im->xorigin+im->xsize,im->yorigin-im->ysize,
1836 graph_col[GRC_GRID].i);
1838 gdImageLine(gif, im->xorigin-4,im->yorigin,
1839 im->xorigin+im->xsize+4,im->yorigin,
1840 graph_col[GRC_FONT].i);
1842 gdImageLine(gif, im->xorigin,im->yorigin,
1843 im->xorigin,im->yorigin-im->ysize,
1844 graph_col[GRC_GRID].i);
1846 /* arrow for X axis direction */
1847 gdImageLine(gif, im->xorigin+im->xsize+4, im->yorigin-3, im->xorigin+im->xsize+4, im->yorigin+3,graph_col[GRC_ARROW].i);
1848 gdImageLine(gif, im->xorigin+im->xsize+4, im->yorigin-3, im->xorigin+im->xsize+9, im->yorigin,graph_col[GRC_ARROW].i);
1849 gdImageLine(gif, im->xorigin+im->xsize+4, im->yorigin+3, im->xorigin+im->xsize+9, im->yorigin,graph_col[GRC_ARROW].i);
1851 /* gdImageLine(gif, im->xorigin+im->xsize-1, im->yorigin-3, im->xorigin+im->xsize-1, im->yorigin+3,graph_col[GRC_MGRID].i);
1852 gdImageLine(gif, im->xorigin+im->xsize, im->yorigin-2, im->xorigin+im->xsize, im->yorigin+2,graph_col[GRC_MGRID].i);
1853 gdImageLine(gif, im->xorigin+im->xsize+1, im->yorigin-2, im->xorigin+im->xsize+1, im->yorigin+2,graph_col[GRC_MGRID].i);
1854 gdImageLine(gif, im->xorigin+im->xsize+2, im->yorigin-2, im->xorigin+im->xsize+2, im->yorigin+2,graph_col[GRC_MGRID].i);
1855 gdImageLine(gif, im->xorigin+im->xsize+3, im->yorigin-1, im->xorigin+im->xsize+3, im->yorigin+1,graph_col[GRC_MGRID].i);
1856 gdImageLine(gif, im->xorigin+im->xsize+4, im->yorigin-1, im->xorigin+im->xsize+4, im->yorigin+1,graph_col[GRC_MGRID].i);
1857 gdImageLine(gif, im->xorigin+im->xsize+5, im->yorigin, im->xorigin+im->xsize+5, im->yorigin,graph_col[GRC_MGRID].i); */
1872 gdPoint polyPoints[4]; /* points for filled graph and more*/
1874 /* draw 3d border */
1875 gdImageLine(gif,0,0,im->xgif-1,0,graph_col[GRC_SHADEA].i);
1876 gdImageLine(gif,1,1,im->xgif-2,1,graph_col[GRC_SHADEA].i);
1877 gdImageLine(gif,0,0,0,im->ygif-1,graph_col[GRC_SHADEA].i);
1878 gdImageLine(gif,1,1,1,im->ygif-2,graph_col[GRC_SHADEA].i);
1879 gdImageLine(gif,im->xgif-1,0,im->xgif-1,im->ygif-1,graph_col[GRC_SHADEB].i);
1880 gdImageLine(gif,0,im->ygif-1,im->xgif-1,im->ygif-1,graph_col[GRC_SHADEB].i);
1881 gdImageLine(gif,im->xgif-2,1,im->xgif-2,im->ygif-2,graph_col[GRC_SHADEB].i);
1882 gdImageLine(gif,1,im->ygif-2,im->xgif-2,im->ygif-2,graph_col[GRC_SHADEB].i);
1885 if (im->draw_x_grid == 1 )
1886 vertical_grid(gif, im);
1888 if (im->draw_y_grid == 1){
1889 if(im->logarithmic){
1890 res = horizontal_log_grid(gif,im);
1892 res = horizontal_grid(gif,im);
1895 /* dont draw horizontal grid if there is no min and max val */
1897 char *nodata = "No Data found";
1898 gdImageString(gif, LargeFont,
1900 - (strlen(nodata)*LargeFont->w)/2,
1901 (2*im->yorigin-im->ysize) / 2,
1902 (unsigned char *)nodata, graph_col[GRC_FONT].i);
1906 /* yaxis description */
1907 gdImageStringUp(gif, SmallFont,
1909 (im->yorigin - im->ysize/2
1910 +(strlen(im->ylegend)*SmallFont->w)/2 ),
1911 (unsigned char *)im->ylegend, graph_col[GRC_FONT].i);
1915 gdImageString(gif, LargeFont,
1917 - (strlen(im->title)*LargeFont->w)/2,
1919 (unsigned char *)im->title, graph_col[GRC_FONT].i);
1922 if( !(im->extra_flags & NOLEGEND) ) {
1923 for(i=0;i<im->gdes_c;i++){
1924 if(im->gdes[i].legend[0] =='\0')
1927 if(im->gdes[i].gf != GF_GPRINT && im->gdes[i].gf != GF_COMMENT){
1929 polyPoints[0].x = im->gdes[i].legloc.x;
1930 polyPoints[0].y = im->gdes[i].legloc.y+1;
1931 polyPoints[1].x = polyPoints[0].x+boxH;
1932 polyPoints[2].x = polyPoints[0].x+boxH;
1933 polyPoints[3].x = polyPoints[0].x;
1934 polyPoints[1].y = polyPoints[0].y;
1935 polyPoints[2].y = polyPoints[0].y+boxV;
1936 polyPoints[3].y = polyPoints[0].y+boxV;
1937 gdImageFilledPolygon(gif,polyPoints,4,im->gdes[i].col.i);
1938 gdImagePolygon(gif,polyPoints,4,graph_col[GRC_FRAME].i);
1940 gdImageString(gif, SmallFont,
1941 polyPoints[0].x+boxH+6,
1943 (unsigned char *)im->gdes[i].legend,
1944 graph_col[GRC_FONT].i);
1946 polyPoints[0].x = im->gdes[i].legloc.x;
1947 polyPoints[0].y = im->gdes[i].legloc.y;
1949 gdImageString(gif, SmallFont,
1952 (unsigned char *)im->gdes[i].legend,
1953 graph_col[GRC_FONT].i);
1959 gator(gif, (int) im->xgif-5, 5);
1965 MkLineBrush(image_desc_t *im,long cosel, enum gf_en typsel){
1970 brush=gdImageCreate(1,1);
1973 brush=gdImageCreate(2,2);
1976 brush=gdImageCreate(3,3);
1982 gdImageColorTransparent(brush,
1983 gdImageColorAllocate(brush, 0, 0, 0));
1985 pen = gdImageColorAllocate(brush,
1986 im->gdes[cosel].col.red,
1987 im->gdes[cosel].col.green,
1988 im->gdes[cosel].col.blue);
1992 gdImageSetPixel(brush,0,0,pen);
1995 gdImageSetPixel(brush,0,0,pen);
1996 gdImageSetPixel(brush,0,1,pen);
1997 gdImageSetPixel(brush,1,0,pen);
1998 gdImageSetPixel(brush,1,1,pen);
2001 gdImageSetPixel(brush,1,0,pen);
2002 gdImageSetPixel(brush,0,1,pen);
2003 gdImageSetPixel(brush,1,1,pen);
2004 gdImageSetPixel(brush,2,1,pen);
2005 gdImageSetPixel(brush,1,2,pen);
2012 /*****************************************************
2013 * lazy check make sure we rely need to create this graph
2014 *****************************************************/
2016 int lazy_check(image_desc_t *im){
2019 struct stat gifstat;
2021 if (im->lazy == 0) return 0; /* no lazy option */
2022 if (stat(im->graphfile,&gifstat) != 0)
2023 return 0; /* can't stat */
2024 /* one pixel in the existing graph is more then what we would
2026 if (time(NULL) - gifstat.st_mtime >
2027 (im->end - im->start) / im->xsize)
2029 if ((fd = fopen(im->graphfile,"rb")) == NULL)
2030 return 0; /* the file does not exist */
2031 switch (im->imgformat) {
2033 size = GifSize(fd,&(im->xgif),&(im->ygif));
2036 size = PngSize(fd,&(im->xgif),&(im->ygif));
2043 /* draw that picture thing ... */
2045 graph_paint(image_desc_t *im, char ***calcpr)
2048 int lazy = lazy_check(im);
2052 gdImagePtr gif,brush;
2054 double areazero = 0.0;
2055 enum gf_en stack_gf = GF_PRINT;
2056 graph_desc_t *lastgdes = NULL;
2057 gdPoint canvas[4], back[4]; /* points for canvas*/
2059 /* if we are lazy and there is nothing to PRINT ... quit now */
2060 if (lazy && im->prt_c==0) return 0;
2062 /* pull the data from the rrd files ... */
2064 if(data_fetch(im)==-1)
2067 /* evaluate CDEF operations ... */
2068 if(data_calc(im)==-1)
2071 /* calculate and PRINT and GPRINT definitions. We have to do it at
2072 * this point because it will affect the length of the legends
2073 * if there are no graph elements we stop here ...
2074 * if we are lazy, try to quit ...
2076 i=print_calc(im,calcpr);
2078 if(i==0 || lazy) return 0;
2080 /* get actual drawing data and find min and max values*/
2081 if(data_proc(im)==-1)
2084 if(!im->logarithmic){si_unit(im);} /* identify si magnitude Kilo, Mega Giga ? */
2086 if(!im->rigid && ! im->logarithmic)
2087 expand_range(im); /* make sure the upper and lower limit are
2090 /* init xtr and ytr */
2091 /* determine the actual size of the gif to draw. The size given
2092 on the cmdline is the graph area. But we need more as we have
2093 draw labels and other things outside the graph area */
2096 im->xorigin = 10 + 9 * SmallFont->w+SmallFont->h;
2099 im->yorigin = 14 + im->ysize;
2102 if(im->title[0] != '\0')
2103 im->yorigin += (LargeFont->h+4);
2105 im->xgif=20+im->xsize + im->xorigin;
2106 im->ygif= im->yorigin+2*SmallFont->h;
2108 /* determine where to place the legends onto the graphics.
2109 and set im->ygif to match space requirements for text */
2110 if(leg_place(im)==-1)
2113 gif=gdImageCreate(im->xgif,im->ygif);
2115 gdImageInterlace(gif, im->interlaced);
2117 /* allocate colors for the screen elements */
2118 for(i=0;i<DIM(graph_col);i++)
2119 /* check for user override values */
2120 if(im->graph_col[i].red != -1)
2122 gdImageColorAllocate( gif,
2123 im->graph_col[i].red,
2124 im->graph_col[i].green,
2125 im->graph_col[i].blue);
2128 gdImageColorAllocate( gif,
2134 /* allocate colors for the graph */
2135 for(i=0;i<im->gdes_c;i++)
2136 /* only for elements which have a color defined */
2137 if (im->gdes[i].col.red != -1)
2139 gdImageColorAllocate(gif,
2140 im->gdes[i].col.red,
2141 im->gdes[i].col.green,
2142 im->gdes[i].col.blue);
2145 /* the actual graph is created by going through the individual
2146 graph elements and then drawing them */
2150 back[1].x = back[0].x+im->xgif;
2151 back[1].y = back[0].y;
2152 back[2].x = back[1].x;
2153 back[2].y = back[0].y+im->ygif;
2154 back[3].x = back[0].x;
2155 back[3].y = back[2].y;
2157 gdImageFilledPolygon(gif,back,4,graph_col[GRC_BACK].i);
2159 canvas[0].x = im->xorigin;
2160 canvas[0].y = im->yorigin;
2161 canvas[1].x = canvas[0].x+im->xsize;
2162 canvas[1].y = canvas[0].y;
2163 canvas[2].x = canvas[1].x;
2164 canvas[2].y = canvas[0].y-im->ysize;
2165 canvas[3].x = canvas[0].x;
2166 canvas[3].y = canvas[2].y;
2168 gdImageFilledPolygon(gif,canvas,4,graph_col[GRC_CANVAS].i);
2170 if (im->minval > 0.0)
2171 areazero = im->minval;
2172 if (im->maxval < 0.0)
2173 areazero = im->maxval;
2177 for(i=0;i<im->gdes_c;i++){
2179 switch(im->gdes[i].gf){
2189 for (ii = 0; ii < im->xsize; ii++)
2191 if (!isnan(im->gdes[i].p_data[ii]) &&
2192 im->gdes[i].p_data[ii] > 0.0)
2194 /* generate a tick */
2195 gdImageLine(gif, im -> xorigin + ii,
2196 im -> yorigin - (im -> gdes[i].yrule * im -> ysize),
2199 im -> gdes[i].col.i);
2207 stack_gf = im->gdes[i].gf;
2209 /* fix data points at oo and -oo */
2210 for(ii=0;ii<im->xsize;ii++){
2211 if (isinf(im->gdes[i].p_data[ii])){
2212 if (im->gdes[i].p_data[ii] > 0) {
2213 im->gdes[i].p_data[ii] = im->maxval ;
2215 im->gdes[i].p_data[ii] = im->minval ;
2221 if (im->gdes[i].col.i != -1){
2222 /* GF_LINE and frined */
2223 if(stack_gf == GF_LINE1 || stack_gf == GF_LINE2 || stack_gf == GF_LINE3 ){
2224 brush = MkLineBrush(im,i,stack_gf);
2225 gdImageSetBrush(gif, brush);
2226 for(ii=1;ii<im->xsize;ii++){
2227 if (isnan(im->gdes[i].p_data[ii-1]) ||
2228 isnan(im->gdes[i].p_data[ii]))
2231 ii+im->xorigin-1,ytr(im,im->gdes[i].p_data[ii-1]),
2232 ii+im->xorigin,ytr(im,im->gdes[i].p_data[ii]),
2236 gdImageDestroy(brush);
2239 /* GF_AREA STACK type*/
2240 if (im->gdes[i].gf == GF_STACK )
2241 for(ii=0;ii<im->xsize;ii++){
2242 if(isnan(im->gdes[i].p_data[ii])){
2243 im->gdes[i].p_data[ii] = lastgdes->p_data[ii];
2247 if (lastgdes->p_data[ii] == im->gdes[i].p_data[ii]){
2251 ii+im->xorigin,ytr(im,lastgdes->p_data[ii]),
2252 ii+im->xorigin,ytr(im,im->gdes[i].p_data[ii]),
2256 else /* simple GF_AREA */
2257 for(ii=0;ii<im->xsize;ii++){
2258 if (isnan(im->gdes[i].p_data[ii])) {
2259 im->gdes[i].p_data[ii] = 0;
2263 ii+im->xorigin,ytr(im,areazero),
2264 ii+im->xorigin,ytr(im,im->gdes[i].p_data[ii]),
2268 lastgdes = &(im->gdes[i]);
2275 /* the RULES are the last thing to paint ... */
2276 for(i=0;i<im->gdes_c;i++){
2278 switch(im->gdes[i].gf){
2280 if(im->gdes[i].yrule >= im->minval
2281 && im->gdes[i].yrule <= im->maxval)
2283 im->xorigin,ytr(im,im->gdes[i].yrule),
2284 im->xorigin+im->xsize,ytr(im,im->gdes[i].yrule),
2288 if(im->gdes[i].xrule >= im->start
2289 && im->gdes[i].xrule <= im->end)
2291 xtr(im,im->gdes[i].xrule),im->yorigin,
2292 xtr(im,im->gdes[i].xrule),im->yorigin-im->ysize,
2300 if (strcmp(im->graphfile,"-")==0) {
2302 /* Change translation mode for stdout to BINARY */
2303 _setmode( _fileno( stdout ), O_BINARY );
2307 if ((fo = fopen(im->graphfile,"wb")) == NULL) {
2308 rrd_set_error("Opening '%s' for write: %s",im->graphfile, strerror(errno));
2312 switch (im->imgformat) {
2314 gdImageGif(gif, fo);
2317 gdImagePng(gif, fo);
2320 if (strcmp(im->graphfile,"-") != 0)
2322 gdImageDestroy(gif);
2328 /*****************************************************
2330 *****************************************************/
2333 gdes_alloc(image_desc_t *im){
2335 long def_step = (im->end-im->start)/im->xsize;
2337 if (im->step > def_step) /* step can be increassed ... no decreassed */
2338 def_step = im->step;
2342 if ((im->gdes = (graph_desc_t *) rrd_realloc(im->gdes, (im->gdes_c)
2343 * sizeof(graph_desc_t)))==NULL){
2344 rrd_set_error("realloc graph_descs");
2349 im->gdes[im->gdes_c-1].step=def_step;
2350 im->gdes[im->gdes_c-1].start=im->start;
2351 im->gdes[im->gdes_c-1].end=im->end;
2352 im->gdes[im->gdes_c-1].vname[0]='\0';
2353 im->gdes[im->gdes_c-1].data=NULL;
2354 im->gdes[im->gdes_c-1].ds_namv=NULL;
2355 im->gdes[im->gdes_c-1].data_first=0;
2356 im->gdes[im->gdes_c-1].p_data=NULL;
2357 im->gdes[im->gdes_c-1].rpnp=NULL;
2358 im->gdes[im->gdes_c-1].col.red = -1;
2359 im->gdes[im->gdes_c-1].col.i=-1;
2360 im->gdes[im->gdes_c-1].legend[0]='\0';
2361 im->gdes[im->gdes_c-1].rrd[0]='\0';
2362 im->gdes[im->gdes_c-1].ds=-1;
2363 im->gdes[im->gdes_c-1].p_data=NULL;
2367 /* copies input untill the first unescaped colon is found
2368 or until input ends. backslashes have to be escaped as well */
2370 scan_for_col(char *input, int len, char *output)
2375 input[inp] != ':' &&
2378 if (input[inp] == '\\' &&
2379 input[inp+1] != '\0' &&
2380 (input[inp+1] == '\\' ||
2381 input[inp+1] == ':')){
2382 output[outp++] = input[++inp];
2385 output[outp++] = input[inp];
2388 output[outp] = '\0';
2393 rrd_graph(int argc, char **argv, char ***prdata, int *xsize, int *ysize)
2399 time_t start_tmp=0,end_tmp=0;
2400 char scan_gtm[12],scan_mtm[12],scan_ltm[12],col_nam[12];
2402 unsigned int col_red,col_green,col_blue;
2404 int linepass = 0; /* stack can only follow directly after LINE* AREA or STACK */
2405 struct time_value start_tv, end_tv;
2406 char *parsetime_error = NULL;
2411 parsetime("end-24h", &start_tv);
2412 parsetime("now", &end_tv);
2414 im.xlab_user.minsec = -1;
2420 im.ylegend[0] = '\0';
2425 im.unitsexponent= 9999;
2431 im.ygridstep = DNAN;
2438 im.imgformat = IF_GIF; /* we default to GIF output */
2440 for(i=0;i<DIM(graph_col);i++)
2441 im.graph_col[i].red=-1;
2445 static struct option long_options[] =
2447 {"start", required_argument, 0, 's'},
2448 {"end", required_argument, 0, 'e'},
2449 {"x-grid", required_argument, 0, 'x'},
2450 {"y-grid", required_argument, 0, 'y'},
2451 {"vertical-label",required_argument,0,'v'},
2452 {"width", required_argument, 0, 'w'},
2453 {"height", required_argument, 0, 'h'},
2454 {"interlaced", no_argument, 0, 'i'},
2455 {"upper-limit",required_argument, 0, 'u'},
2456 {"lower-limit",required_argument, 0, 'l'},
2457 {"rigid", no_argument, 0, 'r'},
2458 {"base", required_argument, 0, 'b'},
2459 {"logarithmic",no_argument, 0, 'o'},
2460 {"color", required_argument, 0, 'c'},
2461 {"title", required_argument, 0, 't'},
2462 {"imginfo", required_argument, 0, 'f'},
2463 {"imgformat", required_argument, 0, 'a'},
2464 {"lazy", no_argument, 0, 'z'},
2465 {"no-legend", no_argument, 0, 'g'},
2466 {"alt-y-grid", no_argument, 0, 257 },
2467 {"alt-autoscale", no_argument, 0, 258 },
2468 {"alt-autoscale-max", no_argument, 0, 259 },
2469 {"units-exponent",required_argument, 0, 260},
2470 {"step", required_argument, 0, 261},
2472 int option_index = 0;
2476 opt = getopt_long(argc, argv,
2477 "s:e:x:y:v:w:h:iu:l:rb:oc:t:f:a:z:g",
2478 long_options, &option_index);
2485 im.extra_flags |= ALTYGRID;
2488 im.extra_flags |= ALTAUTOSCALE;
2491 im.extra_flags |= ALTAUTOSCALE_MAX;
2494 im.extra_flags |= NOLEGEND;
2497 im.unitsexponent = atoi(optarg);
2500 im.step = atoi(optarg);
2503 if ((parsetime_error = parsetime(optarg, &start_tv))) {
2504 rrd_set_error( "start time: %s", parsetime_error );
2509 if ((parsetime_error = parsetime(optarg, &end_tv))) {
2510 rrd_set_error( "end time: %s", parsetime_error );
2515 if(strcmp(optarg,"none") == 0){
2521 "%10[A-Z]:%ld:%10[A-Z]:%ld:%10[A-Z]:%ld:%ld:%n",
2523 &im.xlab_user.gridst,
2525 &im.xlab_user.mgridst,
2527 &im.xlab_user.labst,
2528 &im.xlab_user.precis,
2529 &stroff) == 7 && stroff != 0){
2530 strncpy(im.xlab_form, optarg+stroff, sizeof(im.xlab_form) - 1);
2531 if((im.xlab_user.gridtm = tmt_conv(scan_gtm)) == -1){
2532 rrd_set_error("unknown keyword %s",scan_gtm);
2534 } else if ((im.xlab_user.mgridtm = tmt_conv(scan_mtm)) == -1){
2535 rrd_set_error("unknown keyword %s",scan_mtm);
2537 } else if ((im.xlab_user.labtm = tmt_conv(scan_ltm)) == -1){
2538 rrd_set_error("unknown keyword %s",scan_ltm);
2541 im.xlab_user.minsec = 1;
2542 im.xlab_user.stst = im.xlab_form;
2544 rrd_set_error("invalid x-grid format");
2550 if(strcmp(optarg,"none") == 0){
2558 &im.ylabfact) == 2) {
2559 if(im.ygridstep<=0){
2560 rrd_set_error("grid step must be > 0");
2562 } else if (im.ylabfact < 1){
2563 rrd_set_error("label factor must be > 0");
2567 rrd_set_error("invalid y-grid format");
2572 strncpy(im.ylegend,optarg,150);
2573 im.ylegend[150]='\0';
2576 im.maxval = atof(optarg);
2579 im.minval = atof(optarg);
2582 im.base = atol(optarg);
2583 if(im.base != 1024 && im.base != 1000 ){
2584 rrd_set_error("the only sensible value for base apart from 1000 is 1024");
2589 long_tmp = atol(optarg);
2590 if (long_tmp < 10) {
2591 rrd_set_error("width below 10 pixels");
2594 im.xsize = long_tmp;
2597 long_tmp = atol(optarg);
2598 if (long_tmp < 10) {
2599 rrd_set_error("height below 10 pixels");
2602 im.ysize = long_tmp;
2611 im.imginfo = optarg;
2614 if((im.imgformat = if_conv(optarg)) == -1) {
2615 rrd_set_error("unsupported graphics format '%s'",optarg);
2624 if (isnan(im.minval))
2629 "%10[A-Z]#%2x%2x%2x",
2630 col_nam,&col_red,&col_green,&col_blue) == 4){
2632 if((ci=grc_conv(col_nam)) != -1){
2633 im.graph_col[ci].red=col_red;
2634 im.graph_col[ci].green=col_green;
2635 im.graph_col[ci].blue=col_blue;
2637 rrd_set_error("invalid color name '%s'",col_nam);
2640 rrd_set_error("invalid color def format");
2645 strncpy(im.title,optarg,150);
2651 rrd_set_error("unknown option '%c'", optopt);
2653 rrd_set_error("unknown option '%s'",argv[optind-1]);
2658 if (optind >= argc) {
2659 rrd_set_error("missing filename");
2663 if (im.logarithmic == 1 && (im.minval <= 0 || isnan(im.minval))){
2664 rrd_set_error("for a logarithmic yaxis you must specify a lower-limit > 0");
2668 strncpy(im.graphfile,argv[optind],MAXPATH-1);
2669 im.graphfile[MAXPATH-1]='\0';
2671 if (proc_start_end(&start_tv,&end_tv,&start_tmp,&end_tmp) == -1){
2675 if (start_tmp < 3600*24*365*10){
2676 rrd_set_error("the first entry to fetch should be after 1980 (%ld)",start_tmp);
2680 if (end_tmp < start_tmp) {
2681 rrd_set_error("start (%ld) should be less than end (%ld)",
2682 start_tmp, end_tmp);
2686 im.start = start_tmp;
2690 for(i=optind+1;i<argc;i++){
2693 char varname[30],*rpnex;
2695 if(sscanf(argv[i],"%10[A-Z0-9]:%n",symname,&argstart)==1){
2696 if((im.gdes[im.gdes_c-1].gf=gf_conv(symname))==-1){
2698 rrd_set_error("unknown function '%s'",symname);
2702 rrd_set_error("can't parse '%s'",argv[i]);
2707 /* reset linepass if a non LINE/STACK/AREA operator gets parsed
2709 if (im.gdes[im.gdes_c-1].gf != GF_LINE1 &&
2710 im.gdes[im.gdes_c-1].gf != GF_LINE2 &&
2711 im.gdes[im.gdes_c-1].gf != GF_LINE3 &&
2712 im.gdes[im.gdes_c-1].gf != GF_AREA &&
2713 im.gdes[im.gdes_c-1].gf != GF_STACK) {
2718 switch(im.gdes[im.gdes_c-1].gf){
2724 "%29[^#:]:" CF_NAM_FMT ":%n",
2725 varname,symname,&strstart) == 2){
2726 scan_for_col(&argv[i][argstart+strstart],FMT_LEG_LEN,im.gdes[im.gdes_c-1].format);
2727 if((im.gdes[im.gdes_c-1].vidx=find_var(&im,varname))==-1){
2729 rrd_set_error("unknown variable '%s'",varname);
2732 if((im.gdes[im.gdes_c-1].cf=cf_conv(symname))==-1){
2739 rrd_set_error("can't parse '%s'",&argv[i][argstart]);
2744 if(strlen(&argv[i][argstart])>FMT_LEG_LEN) argv[i][argstart+FMT_LEG_LEN-3]='\0' ;
2745 strcpy(im.gdes[im.gdes_c-1].legend, &argv[i][argstart]);
2751 &im.gdes[im.gdes_c-1].yrule,
2752 &col_red,&col_green,&col_blue,
2754 im.gdes[im.gdes_c-1].col.red = col_red;
2755 im.gdes[im.gdes_c-1].col.green = col_green;
2756 im.gdes[im.gdes_c-1].col.blue = col_blue;
2758 im.gdes[im.gdes_c-1].legend[0] = '\0';
2760 scan_for_col(&argv[i][argstart+strstart],FMT_LEG_LEN,im.gdes[im.gdes_c-1].legend);
2764 rrd_set_error("can't parse '%s'",&argv[i][argstart]);
2772 &im.gdes[im.gdes_c-1].xrule,
2777 im.gdes[im.gdes_c-1].col.red = col_red;
2778 im.gdes[im.gdes_c-1].col.green = col_green;
2779 im.gdes[im.gdes_c-1].col.blue = col_blue;
2781 im.gdes[im.gdes_c-1].legend[0] = '\0';
2783 scan_for_col(&argv[i][argstart+strstart],FMT_LEG_LEN,im.gdes[im.gdes_c-1].legend);
2787 rrd_set_error("can't parse '%s'",&argv[i][argstart]);
2792 if((scancount=sscanf(
2794 "%29[^:#]#%2x%2x%2x:%lf:%n",
2799 &(im.gdes[im.gdes_c-1].yrule),
2802 im.gdes[im.gdes_c-1].col.red = col_red;
2803 im.gdes[im.gdes_c-1].col.green = col_green;
2804 im.gdes[im.gdes_c-1].col.blue = col_blue;
2806 im.gdes[im.gdes_c-1].legend[0] = '\0';
2808 scan_for_col(&argv[i][argstart+strstart],FMT_LEG_LEN,im.gdes[im.gdes_c-1].legend);
2810 if((im.gdes[im.gdes_c-1].vidx=find_var(&im,varname))==-1){
2812 rrd_set_error("unknown variable '%s'",varname);
2815 if (im.gdes[im.gdes_c-1].yrule <= 0.0 || im.gdes[im.gdes_c-1].yrule > 1.0)
2818 rrd_set_error("Tick mark scaling factor out of range");
2822 im.gdes[im.gdes_c-1].col.red = -1;
2824 /* default tick marks: 10% of the y-axis */
2825 im.gdes[im.gdes_c-1].yrule = 0.1;
2829 rrd_set_error("can't parse '%s'",&argv[i][argstart]);
2831 } /* endif sscanf */
2836 rrd_set_error("STACK must follow AREA, LINE or STACK");
2844 if((scancount=sscanf(
2846 "%29[^:#]#%2x%2x%2x:%n",
2852 im.gdes[im.gdes_c-1].col.red = col_red;
2853 im.gdes[im.gdes_c-1].col.green = col_green;
2854 im.gdes[im.gdes_c-1].col.blue = col_blue;
2856 im.gdes[im.gdes_c-1].legend[0] = '\0';
2858 scan_for_col(&argv[i][argstart+strstart],FMT_LEG_LEN,im.gdes[im.gdes_c-1].legend);
2860 if((im.gdes[im.gdes_c-1].vidx=find_var(&im,varname))==-1){
2862 rrd_set_error("unknown variable '%s'",varname);
2866 im.gdes[im.gdes_c-1].col.red = -1;
2870 rrd_set_error("can't parse '%s'",&argv[i][argstart]);
2875 if((rpnex = malloc(strlen(&argv[i][argstart])*sizeof(char)))==NULL){
2876 rrd_set_error("malloc for CDEF");
2881 DEF_NAM_FMT "=%[^: ]",
2882 im.gdes[im.gdes_c-1].vname,
2886 rrd_set_error("can't parse CDEF '%s'",&argv[i][argstart]);
2889 /* checking for duplicate DEF CDEFS */
2890 if(find_var(&im,im.gdes[im.gdes_c-1].vname) != -1){
2892 rrd_set_error("duplicate variable '%s'",
2893 im.gdes[im.gdes_c-1].vname);
2896 if((im.gdes[im.gdes_c-1].rpnp =
2897 rpn_parse((void*)&im,rpnex,&find_var_wrapper))== NULL){
2898 rrd_set_error("invalid rpn expression '%s'", rpnex);
2908 im.gdes[im.gdes_c-1].vname,
2909 &strstart)== 1 && strstart){ /* is the = did not match %n returns 0 */
2910 if(sscanf(&argv[i][argstart
2912 +scan_for_col(&argv[i][argstart+strstart],
2913 MAXPATH,im.gdes[im.gdes_c-1].rrd)],
2914 ":" DS_NAM_FMT ":" CF_NAM_FMT,
2915 im.gdes[im.gdes_c-1].ds_nam,
2918 rrd_set_error("can't parse DEF '%s' -2",&argv[i][argstart]);
2923 rrd_set_error("can't parse DEF '%s'",&argv[i][argstart]);
2927 /* checking for duplicate DEF CDEFS */
2928 if (find_var(&im,im.gdes[im.gdes_c-1].vname) != -1){
2930 rrd_set_error("duplicate variable '%s'",
2931 im.gdes[im.gdes_c-1].vname);
2934 if((im.gdes[im.gdes_c-1].cf=cf_conv(symname))==-1){
2936 rrd_set_error("unknown cf '%s'",symname);
2945 rrd_set_error("can't make a graph without contents");
2950 /* parse rest of arguments containing information on what to draw*/
2951 if (graph_paint(&im,prdata)==-1){
2961 /* maybe prdata is not allocated yet ... lets do it now */
2962 if((*prdata = calloc(2,sizeof(char *)))==NULL){
2963 rrd_set_error("malloc imginfo");
2967 if(((*prdata)[0] = malloc((strlen(im.imginfo)+200+strlen(im.graphfile))*sizeof(char)))
2969 rrd_set_error("malloc imginfo");
2972 filename=im.graphfile+strlen(im.graphfile);
2973 while(filename > im.graphfile){
2974 if (*(filename-1)=='/' || *(filename-1)=='\\' ) break;
2978 sprintf((*prdata)[0],im.imginfo,filename,im.xgif,im.ygif);
2984 int bad_format(char *fmt) {
2988 while (*ptr != '\0') {
2989 if (*ptr == '%') {ptr++;
2990 if (*ptr == '\0') return 1;
2991 while ((*ptr >= '0' && *ptr <= '9') || *ptr == '.') {
2994 if (*ptr == '\0') return 1;
2997 if (*ptr == '\0') return 1;
2998 if (*ptr == 'e' || *ptr == 'f') {
3000 } else { return 1; }
3002 else if (*ptr == 's' || *ptr == 'S' || *ptr == '%') { ++ptr; }