1 /****************************************************************************
2 * RRDtool 1.1.x Copyright Tobias Oetiker, 1997 - 2002
3 ****************************************************************************
4 * rrd__graph.c make creates ne rrds
5 ****************************************************************************/
25 #include "rrd_graph.h"
26 #include "rrd_graph_helper.h"
28 /* some constant definitions */
31 #ifndef RRD_DEFAULT_FONT
33 #define RRD_DEFAULT_FONT "c:/winnt/fonts/COUR.TTF"
35 #define RRD_DEFAULT_FONT "/usr/share/fonts/truetype/openoffice/ariosor.ttf"
36 /* #define RRD_DEFAULT_FONT "/usr/share/fonts/truetype/Arial.ttf" */
41 text_prop_t text_prop[] = {
42 { 10.0, RRD_DEFAULT_FONT }, /* default */
43 { 12.0, RRD_DEFAULT_FONT }, /* title */
44 { 8.0, RRD_DEFAULT_FONT }, /* axis */
45 { 10.0, RRD_DEFAULT_FONT }, /* unit */
46 { 10.0, RRD_DEFAULT_FONT } /* legend */
50 {0, TMT_SECOND,30, TMT_MINUTE,5, TMT_MINUTE,5, 0,"%H:%M"},
51 {2, TMT_MINUTE,1, TMT_MINUTE,5, TMT_MINUTE,5, 0,"%H:%M"},
52 {5, TMT_MINUTE,2, TMT_MINUTE,10, TMT_MINUTE,10, 0,"%H:%M"},
53 {10, TMT_MINUTE,5, TMT_MINUTE,20, TMT_MINUTE,20, 0,"%H:%M"},
54 {30, TMT_MINUTE,10, TMT_HOUR,1, TMT_HOUR,1, 0,"%H:%M"},
55 {60, TMT_MINUTE,30, TMT_HOUR,2, TMT_HOUR,2, 0,"%H:%M"},
56 {180, TMT_HOUR,1, TMT_HOUR,6, TMT_HOUR,6, 0,"%H:%M"},
57 /*{300, TMT_HOUR,3, TMT_HOUR,12, TMT_HOUR,12, 12*3600,"%a %p"}, this looks silly*/
58 {600, TMT_HOUR,6, TMT_DAY,1, TMT_DAY,1, 24*3600,"%a"},
59 {1800, TMT_HOUR,12, TMT_DAY,1, TMT_DAY,2, 24*3600,"%a"},
60 {3600, TMT_DAY,1, TMT_WEEK,1, TMT_WEEK,1, 7*24*3600,"Week %V"},
61 {3*3600, TMT_WEEK,1, TMT_MONTH,1, TMT_WEEK,2, 7*24*3600,"Week %V"},
62 {6*3600, TMT_MONTH,1, TMT_MONTH,1, TMT_MONTH,1, 30*24*3600,"%b"},
63 {48*3600, TMT_MONTH,1, TMT_MONTH,3, TMT_MONTH,3, 30*24*3600,"%b"},
64 {10*24*3600, TMT_YEAR,1, TMT_YEAR,1, TMT_YEAR,1, 365*24*3600,"%y"},
65 {-1,TMT_MONTH,0,TMT_MONTH,0,TMT_MONTH,0,0,""}
68 /* sensible logarithmic y label intervals ...
69 the first element of each row defines the possible starting points on the
70 y axis ... the other specify the */
72 double yloglab[][12]= {{ 1e9, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
73 { 1e3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
74 { 1e1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
75 /* { 1e1, 1, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, */
76 { 1e1, 1, 2.5, 5, 7.5, 0, 0, 0, 0, 0, 0, 0 },
77 { 1e1, 1, 2, 4, 6, 8, 0, 0, 0, 0, 0, 0 },
78 { 1e1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0 },
79 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }};
81 /* sensible y label intervals ...*/
99 gfx_color_t graph_col[] = /* default colors */
100 { 0xFFFFFFFF, /* canvas */
101 0xF0F0F0FF, /* background */
102 0xD0D0D0FF, /* shade A */
103 0xA0A0A0FF, /* shade B */
104 0x909090FF, /* grid */
105 0xE05050FF, /* major grid */
106 0x000000FF, /* font */
107 0x000000FF, /* frame */
108 0xFF0000FF /* arrow */
115 # define DPRINT(x) (void)(printf x, printf("\n"))
121 /* initialize with xtr(im,0); */
123 xtr(image_desc_t *im,time_t mytime){
126 pixie = (double) im->xsize / (double)(im->end - im->start);
129 return (int)((double)im->xorigin
130 + pixie * ( mytime - im->start ) );
133 /* translate data values into y coordinates */
135 ytr(image_desc_t *im, double value){
140 pixie = (double) im->ysize / (im->maxval - im->minval);
142 pixie = (double) im->ysize / (log10(im->maxval) - log10(im->minval));
144 } else if(!im->logarithmic) {
145 yval = im->yorigin - pixie * (value - im->minval);
147 if (value < im->minval) {
150 yval = im->yorigin - pixie * (log10(value) - log10(im->minval));
153 /* make sure we don't return anything too unreasonable. GD lib can
154 get terribly slow when drawing lines outside its scope. This is
155 especially problematic in connection with the rigid option */
157 /* keep yval as-is */
158 } else if (yval > im->yorigin) {
159 yval = im->yorigin+2;
160 } else if (yval < im->yorigin - im->ysize){
161 yval = im->yorigin - im->ysize - 2;
168 /* conversion function for symbolic entry names */
171 #define conv_if(VV,VVV) \
172 if (strcmp(#VV, string) == 0) return VVV ;
174 enum gf_en gf_conv(char *string){
176 conv_if(PRINT,GF_PRINT)
177 conv_if(GPRINT,GF_GPRINT)
178 conv_if(COMMENT,GF_COMMENT)
179 conv_if(HRULE,GF_HRULE)
180 conv_if(VRULE,GF_VRULE)
181 conv_if(LINE,GF_LINE)
182 conv_if(AREA,GF_AREA)
183 conv_if(STACK,GF_STACK)
184 conv_if(TICK,GF_TICK)
186 conv_if(CDEF,GF_CDEF)
187 conv_if(VDEF,GF_VDEF)
188 conv_if(PART,GF_PART)
189 conv_if(XPORT,GF_XPORT)
194 enum gfx_if_en if_conv(char *string){
204 enum tmt_en tmt_conv(char *string){
206 conv_if(SECOND,TMT_SECOND)
207 conv_if(MINUTE,TMT_MINUTE)
208 conv_if(HOUR,TMT_HOUR)
210 conv_if(WEEK,TMT_WEEK)
211 conv_if(MONTH,TMT_MONTH)
212 conv_if(YEAR,TMT_YEAR)
216 enum grc_en grc_conv(char *string){
218 conv_if(BACK,GRC_BACK)
219 conv_if(CANVAS,GRC_CANVAS)
220 conv_if(SHADEA,GRC_SHADEA)
221 conv_if(SHADEB,GRC_SHADEB)
222 conv_if(GRID,GRC_GRID)
223 conv_if(MGRID,GRC_MGRID)
224 conv_if(FONT,GRC_FONT)
225 conv_if(FRAME,GRC_FRAME)
226 conv_if(ARROW,GRC_ARROW)
231 enum text_prop_en text_prop_conv(char *string){
233 conv_if(DEFAULT,TEXT_PROP_DEFAULT)
234 conv_if(TITLE,TEXT_PROP_TITLE)
235 conv_if(AXIS,TEXT_PROP_AXIS)
236 conv_if(UNIT,TEXT_PROP_UNIT)
237 conv_if(LEGEND,TEXT_PROP_LEGEND)
247 im_free(image_desc_t *im)
250 if (im == NULL) return 0;
251 for(i=0;i<im->gdes_c;i++){
252 if (im->gdes[i].data_first){
253 /* careful here, because a single pointer can occur several times */
254 free (im->gdes[i].data);
255 if (im->gdes[i].ds_namv){
256 for (ii=0;ii<im->gdes[i].ds_cnt;ii++)
257 free(im->gdes[i].ds_namv[ii]);
258 free(im->gdes[i].ds_namv);
261 free (im->gdes[i].p_data);
262 free (im->gdes[i].rpnp);
265 gfx_destroy(im->canvas);
269 /* find SI magnitude symbol for the given number*/
272 image_desc_t *im, /* image description */
279 char *symbol[] = {"a", /* 10e-18 Atto */
280 "f", /* 10e-15 Femto */
281 "p", /* 10e-12 Pico */
282 "n", /* 10e-9 Nano */
283 "u", /* 10e-6 Micro */
284 "m", /* 10e-3 Milli */
289 "T", /* 10e12 Tera */
290 "P", /* 10e15 Peta */
296 if (*value == 0.0 || isnan(*value) ) {
300 sindex = floor(log(fabs(*value))/log((double)im->base));
301 *magfact = pow((double)im->base, (double)sindex);
302 (*value) /= (*magfact);
304 if ( sindex <= symbcenter && sindex >= -symbcenter) {
305 (*symb_ptr) = symbol[sindex+symbcenter];
313 /* find SI magnitude symbol for the numbers on the y-axis*/
316 image_desc_t *im /* image description */
320 char symbol[] = {'a', /* 10e-18 Atto */
321 'f', /* 10e-15 Femto */
322 'p', /* 10e-12 Pico */
323 'n', /* 10e-9 Nano */
324 'u', /* 10e-6 Micro */
325 'm', /* 10e-3 Milli */
330 'T', /* 10e12 Tera */
331 'P', /* 10e15 Peta */
337 if (im->unitsexponent != 9999) {
338 /* unitsexponent = 9, 6, 3, 0, -3, -6, -9, etc */
339 digits = floor(im->unitsexponent / 3);
341 digits = floor( log( max( fabs(im->minval),fabs(im->maxval)))/log((double)im->base));
343 im->magfact = pow((double)im->base , digits);
346 printf("digits %6.3f im->magfact %6.3f\n",digits,im->magfact);
349 if ( ((digits+symbcenter) < sizeof(symbol)) &&
350 ((digits+symbcenter) >= 0) )
351 im->symbol = symbol[(int)digits+symbcenter];
356 /* move min and max values around to become sensible */
359 expand_range(image_desc_t *im)
361 double sensiblevalues[] ={1000.0,900.0,800.0,750.0,700.0,
362 600.0,500.0,400.0,300.0,250.0,
363 200.0,125.0,100.0,90.0,80.0,
364 75.0,70.0,60.0,50.0,40.0,30.0,
365 25.0,20.0,10.0,9.0,8.0,
366 7.0,6.0,5.0,4.0,3.5,3.0,
367 2.5,2.0,1.8,1.5,1.2,1.0,
368 0.8,0.7,0.6,0.5,0.4,0.3,0.2,0.1,0.0,-1};
370 double scaled_min,scaled_max;
377 printf("Min: %6.2f Max: %6.2f MagFactor: %6.2f\n",
378 im->minval,im->maxval,im->magfact);
381 if (isnan(im->ygridstep)){
382 if(im->extra_flags & ALTAUTOSCALE) {
383 /* measure the amplitude of the function. Make sure that
384 graph boundaries are slightly higher then max/min vals
385 so we can see amplitude on the graph */
388 delt = im->maxval - im->minval;
390 fact = 2.0 * pow(10.0,
391 floor(log10(max(fabs(im->minval), fabs(im->maxval)))) - 2);
393 adj = (fact - delt) * 0.55;
395 printf("Min: %6.2f Max: %6.2f delt: %6.2f fact: %6.2f adj: %6.2f\n", im->minval, im->maxval, delt, fact, adj);
401 else if(im->extra_flags & ALTAUTOSCALE_MAX) {
402 /* measure the amplitude of the function. Make sure that
403 graph boundaries are slightly higher than max vals
404 so we can see amplitude on the graph */
405 adj = (im->maxval - im->minval) * 0.1;
409 scaled_min = im->minval / im->magfact;
410 scaled_max = im->maxval / im->magfact;
412 for (i=1; sensiblevalues[i] > 0; i++){
413 if (sensiblevalues[i-1]>=scaled_min &&
414 sensiblevalues[i]<=scaled_min)
415 im->minval = sensiblevalues[i]*(im->magfact);
417 if (-sensiblevalues[i-1]<=scaled_min &&
418 -sensiblevalues[i]>=scaled_min)
419 im->minval = -sensiblevalues[i-1]*(im->magfact);
421 if (sensiblevalues[i-1] >= scaled_max &&
422 sensiblevalues[i] <= scaled_max)
423 im->maxval = sensiblevalues[i-1]*(im->magfact);
425 if (-sensiblevalues[i-1]<=scaled_max &&
426 -sensiblevalues[i] >=scaled_max)
427 im->maxval = -sensiblevalues[i]*(im->magfact);
431 /* adjust min and max to the grid definition if there is one */
432 im->minval = (double)im->ylabfact * im->ygridstep *
433 floor(im->minval / ((double)im->ylabfact * im->ygridstep));
434 im->maxval = (double)im->ylabfact * im->ygridstep *
435 ceil(im->maxval /( (double)im->ylabfact * im->ygridstep));
439 fprintf(stderr,"SCALED Min: %6.2f Max: %6.2f Factor: %6.2f\n",
440 im->minval,im->maxval,im->magfact);
445 apply_gridfit(image_desc_t *im)
447 if (isnan(im->minval) || isnan(im->maxval))
450 if (im->logarithmic) {
451 double ya, yb, ypix, ypixfrac;
452 double log10_range = log10(im->maxval) - log10(im->minval);
453 ya = pow((double)10, floor(log10(im->minval)));
454 while (ya < im->minval)
457 return; /* don't have y=10^x gridline */
459 if (yb <= im->maxval) {
460 /* we have at least 2 y=10^x gridlines.
461 Make sure distance between them in pixels
462 are an integer by expanding im->maxval */
463 double y_pixel_delta = ytr(im, ya) - ytr(im, yb);
464 double factor = y_pixel_delta / floor(y_pixel_delta);
465 double new_log10_range = factor * log10_range;
466 double new_ymax_log10 = log10(im->minval) + new_log10_range;
467 im->maxval = pow(10, new_ymax_log10);
468 ytr(im, DNAN); /* reset precalc */
469 log10_range = log10(im->maxval) - log10(im->minval);
471 /* make sure first y=10^x gridline is located on
472 integer pixel position by moving scale slightly
473 downwards (sub-pixel movement) */
474 ypix = ytr(im, ya) + im->ysize; /* add im->ysize so it always is positive */
475 ypixfrac = ypix - floor(ypix);
476 if (ypixfrac > 0 && ypixfrac < 1) {
477 double yfrac = ypixfrac / im->ysize;
478 im->minval = pow(10, log10(im->minval) - yfrac * log10_range);
479 im->maxval = pow(10, log10(im->maxval) - yfrac * log10_range);
480 ytr(im, DNAN); /* reset precalc */
483 /* Make sure we have an integer pixel distance between
484 each minor gridline */
485 double ypos1 = ytr(im, im->minval);
486 double ypos2 = ytr(im, im->minval + im->ygrid_scale.gridstep);
487 double y_pixel_delta = ypos1 - ypos2;
488 double factor = y_pixel_delta / floor(y_pixel_delta);
489 double new_range = factor * (im->maxval - im->minval);
490 double gridstep = im->ygrid_scale.gridstep;
491 double minor_y, minor_y_px, minor_y_px_frac;
492 im->maxval = im->minval + new_range;
493 ytr(im, DNAN); /* reset precalc */
494 /* make sure first minor gridline is on integer pixel y coord */
495 minor_y = gridstep * floor(im->minval / gridstep);
496 while (minor_y < im->minval)
498 minor_y_px = ytr(im, minor_y) + im->ysize; /* ensure > 0 by adding ysize */
499 minor_y_px_frac = minor_y_px - floor(minor_y_px);
500 if (minor_y_px_frac > 0 && minor_y_px_frac < 1) {
501 double yfrac = minor_y_px_frac / im->ysize;
502 double range = im->maxval - im->minval;
503 im->minval = im->minval - yfrac * range;
504 im->maxval = im->maxval - yfrac * range;
505 ytr(im, DNAN); /* reset precalc */
507 calc_horizontal_grid(im); /* recalc with changed im->maxval */
511 /* reduce data reimplementation by Alex */
515 enum cf_en cf, /* which consolidation function ?*/
516 unsigned long cur_step, /* step the data currently is in */
517 time_t *start, /* start, end and step as requested ... */
518 time_t *end, /* ... by the application will be ... */
519 unsigned long *step, /* ... adjusted to represent reality */
520 unsigned long *ds_cnt, /* number of data sources in file */
521 rrd_value_t **data) /* two dimensional array containing the data */
523 int i,reduce_factor = ceil((double)(*step) / (double)cur_step);
524 unsigned long col,dst_row,row_cnt,start_offset,end_offset,skiprows=0;
525 rrd_value_t *srcptr,*dstptr;
527 (*step) = cur_step*reduce_factor; /* set new step size for reduced data */
530 row_cnt = ((*end)-(*start))/cur_step;
536 printf("Reducing %lu rows with factor %i time %lu to %lu, step %lu\n",
537 row_cnt,reduce_factor,*start,*end,cur_step);
538 for (col=0;col<row_cnt;col++) {
539 printf("time %10lu: ",*start+(col+1)*cur_step);
540 for (i=0;i<*ds_cnt;i++)
541 printf(" %8.2e",srcptr[*ds_cnt*col+i]);
546 /* We have to combine [reduce_factor] rows of the source
547 ** into one row for the destination. Doing this we also
548 ** need to take care to combine the correct rows. First
549 ** alter the start and end time so that they are multiples
550 ** of the new step time. We cannot reduce the amount of
551 ** time so we have to move the end towards the future and
552 ** the start towards the past.
554 end_offset = (*end) % (*step);
555 start_offset = (*start) % (*step);
557 /* If there is a start offset (which cannot be more than
558 ** one destination row), skip the appropriate number of
559 ** source rows and one destination row. The appropriate
560 ** number is what we do know (start_offset/cur_step) of
561 ** the new interval (*step/cur_step aka reduce_factor).
564 printf("start_offset: %lu end_offset: %lu\n",start_offset,end_offset);
565 printf("row_cnt before: %lu\n",row_cnt);
568 (*start) = (*start)-start_offset;
569 skiprows=reduce_factor-start_offset/cur_step;
570 srcptr+=skiprows* *ds_cnt;
571 for (col=0;col<(*ds_cnt);col++) *dstptr++ = DNAN;
575 printf("row_cnt between: %lu\n",row_cnt);
578 /* At the end we have some rows that are not going to be
579 ** used, the amount is end_offset/cur_step
582 (*end) = (*end)-end_offset+(*step);
583 skiprows = end_offset/cur_step;
587 printf("row_cnt after: %lu\n",row_cnt);
590 /* Sanity check: row_cnt should be multiple of reduce_factor */
591 /* if this gets triggered, something is REALLY WRONG ... we die immediately */
593 if (row_cnt%reduce_factor) {
594 printf("SANITY CHECK: %lu rows cannot be reduced by %i \n",
595 row_cnt,reduce_factor);
596 printf("BUG in reduce_data()\n");
600 /* Now combine reduce_factor intervals at a time
601 ** into one interval for the destination.
604 for (dst_row=0;row_cnt>=reduce_factor;dst_row++) {
605 for (col=0;col<(*ds_cnt);col++) {
606 rrd_value_t newval=DNAN;
607 unsigned long validval=0;
609 for (i=0;i<reduce_factor;i++) {
610 if (isnan(srcptr[i*(*ds_cnt)+col])) {
614 if (isnan(newval)) newval = srcptr[i*(*ds_cnt)+col];
622 newval += srcptr[i*(*ds_cnt)+col];
625 newval = min (newval,srcptr[i*(*ds_cnt)+col]);
628 /* an interval contains a failure if any subintervals contained a failure */
630 newval = max (newval,srcptr[i*(*ds_cnt)+col]);
633 newval = srcptr[i*(*ds_cnt)+col];
638 if (validval == 0){newval = DNAN;} else{
656 srcptr+=(*ds_cnt)*reduce_factor;
657 row_cnt-=reduce_factor;
659 /* If we had to alter the endtime, we didn't have enough
660 ** source rows to fill the last row. Fill it with NaN.
662 if (end_offset) for (col=0;col<(*ds_cnt);col++) *dstptr++ = DNAN;
664 row_cnt = ((*end)-(*start))/ *step;
666 printf("Done reducing. Currently %lu rows, time %lu to %lu, step %lu\n",
667 row_cnt,*start,*end,*step);
668 for (col=0;col<row_cnt;col++) {
669 printf("time %10lu: ",*start+(col+1)*(*step));
670 for (i=0;i<*ds_cnt;i++)
671 printf(" %8.2e",srcptr[*ds_cnt*col+i]);
678 /* get the data required for the graphs from the
682 data_fetch( image_desc_t *im )
687 /* pull the data from the log files ... */
688 for (i=0;i<im->gdes_c;i++){
689 /* only GF_DEF elements fetch data */
690 if (im->gdes[i].gf != GF_DEF)
694 /* do we have it already ?*/
695 for (ii=0;ii<i;ii++) {
696 if (im->gdes[ii].gf != GF_DEF)
698 if ((strcmp(im->gdes[i].rrd, im->gdes[ii].rrd) == 0)
699 && (im->gdes[i].cf == im->gdes[ii].cf)
700 && (im->gdes[i].start == im->gdes[ii].start)
701 && (im->gdes[i].end == im->gdes[ii].end)
702 && (im->gdes[i].step == im->gdes[ii].step)) {
703 /* OK, the data is already there.
704 ** Just copy the header portion
706 im->gdes[i].start = im->gdes[ii].start;
707 im->gdes[i].end = im->gdes[ii].end;
708 im->gdes[i].step = im->gdes[ii].step;
709 im->gdes[i].ds_cnt = im->gdes[ii].ds_cnt;
710 im->gdes[i].ds_namv = im->gdes[ii].ds_namv;
711 im->gdes[i].data = im->gdes[ii].data;
712 im->gdes[i].data_first = 0;
719 unsigned long ft_step = im->gdes[i].step ;
721 if((rrd_fetch_fn(im->gdes[i].rrd,
727 &im->gdes[i].ds_namv,
728 &im->gdes[i].data)) == -1){
731 im->gdes[i].data_first = 1;
733 if (ft_step < im->gdes[i].step) {
734 reduce_data(im->gdes[i].cf,
742 im->gdes[i].step = ft_step;
746 /* lets see if the required data source is realy there */
747 for(ii=0;ii<im->gdes[i].ds_cnt;ii++){
748 if(strcmp(im->gdes[i].ds_namv[ii],im->gdes[i].ds_nam) == 0){
751 if (im->gdes[i].ds== -1){
752 rrd_set_error("No DS called '%s' in '%s'",
753 im->gdes[i].ds_nam,im->gdes[i].rrd);
761 /* evaluate the expressions in the CDEF functions */
763 /*************************************************************
765 *************************************************************/
768 find_var_wrapper(void *arg1, char *key)
770 return find_var((image_desc_t *) arg1, key);
773 /* find gdes containing var*/
775 find_var(image_desc_t *im, char *key){
777 for(ii=0;ii<im->gdes_c-1;ii++){
778 if((im->gdes[ii].gf == GF_DEF
779 || im->gdes[ii].gf == GF_VDEF
780 || im->gdes[ii].gf == GF_CDEF)
781 && (strcmp(im->gdes[ii].vname,key) == 0)){
788 /* find the largest common denominator for all the numbers
789 in the 0 terminated num array */
794 for (i=0;num[i+1]!=0;i++){
796 rest=num[i] % num[i+1];
797 num[i]=num[i+1]; num[i+1]=rest;
801 /* return i==0?num[i]:num[i-1]; */
805 /* run the rpn calculator on all the VDEF and CDEF arguments */
807 data_calc( image_desc_t *im){
811 long *steparray, rpi;
816 rpnstack_init(&rpnstack);
818 for (gdi=0;gdi<im->gdes_c;gdi++){
819 /* Look for GF_VDEF and GF_CDEF in the same loop,
820 * so CDEFs can use VDEFs and vice versa
822 switch (im->gdes[gdi].gf) {
826 /* A VDEF has no DS. This also signals other parts
827 * of rrdtool that this is a VDEF value, not a CDEF.
829 im->gdes[gdi].ds_cnt = 0;
830 if (vdef_calc(im,gdi)) {
831 rrd_set_error("Error processing VDEF '%s'"
834 rpnstack_free(&rpnstack);
839 im->gdes[gdi].ds_cnt = 1;
840 im->gdes[gdi].ds = 0;
841 im->gdes[gdi].data_first = 1;
842 im->gdes[gdi].start = 0;
843 im->gdes[gdi].end = 0;
848 /* Find the variables in the expression.
849 * - VDEF variables are substituted by their values
850 * and the opcode is changed into OP_NUMBER.
851 * - CDEF variables are analized for their step size,
852 * the lowest common denominator of all the step
853 * sizes of the data sources involved is calculated
854 * and the resulting number is the step size for the
855 * resulting data source.
857 for(rpi=0;im->gdes[gdi].rpnp[rpi].op != OP_END;rpi++){
858 if(im->gdes[gdi].rpnp[rpi].op == OP_VARIABLE){
859 long ptr = im->gdes[gdi].rpnp[rpi].ptr;
860 if (im->gdes[ptr].ds_cnt == 0) {
862 printf("DEBUG: inside CDEF '%s' processing VDEF '%s'\n",
864 im->gdes[ptr].vname);
865 printf("DEBUG: value from vdef is %f\n",im->gdes[ptr].vf.val);
867 im->gdes[gdi].rpnp[rpi].val = im->gdes[ptr].vf.val;
868 im->gdes[gdi].rpnp[rpi].op = OP_NUMBER;
870 if ((steparray = rrd_realloc(steparray, (++stepcnt+1)*sizeof(*steparray)))==NULL){
871 rrd_set_error("realloc steparray");
872 rpnstack_free(&rpnstack);
876 steparray[stepcnt-1] = im->gdes[ptr].step;
878 /* adjust start and end of cdef (gdi) so
879 * that it runs from the latest start point
880 * to the earliest endpoint of any of the
881 * rras involved (ptr)
883 if(im->gdes[gdi].start < im->gdes[ptr].start)
884 im->gdes[gdi].start = im->gdes[ptr].start;
886 if(im->gdes[gdi].end == 0 ||
887 im->gdes[gdi].end > im->gdes[ptr].end)
888 im->gdes[gdi].end = im->gdes[ptr].end;
890 /* store pointer to the first element of
891 * the rra providing data for variable,
892 * further save step size and data source
895 im->gdes[gdi].rpnp[rpi].data = im->gdes[ptr].data + im->gdes[ptr].ds;
896 im->gdes[gdi].rpnp[rpi].step = im->gdes[ptr].step;
897 im->gdes[gdi].rpnp[rpi].ds_cnt = im->gdes[ptr].ds_cnt;
899 /* backoff the *.data ptr; this is done so
900 * rpncalc() function doesn't have to treat
901 * the first case differently
903 } /* if ds_cnt != 0 */
904 } /* if OP_VARIABLE */
905 } /* loop through all rpi */
907 /* move the data pointers to the correct period */
908 for(rpi=0;im->gdes[gdi].rpnp[rpi].op != OP_END;rpi++){
909 if(im->gdes[gdi].rpnp[rpi].op == OP_VARIABLE){
910 long ptr = im->gdes[gdi].rpnp[rpi].ptr;
911 if(im->gdes[gdi].start > im->gdes[ptr].start) {
912 im->gdes[gdi].rpnp[rpi].data += im->gdes[gdi].rpnp[rpi].ds_cnt;
918 if(steparray == NULL){
919 rrd_set_error("rpn expressions without DEF"
920 " or CDEF variables are not supported");
921 rpnstack_free(&rpnstack);
924 steparray[stepcnt]=0;
925 /* Now find the resulting step. All steps in all
926 * used RRAs have to be visited
928 im->gdes[gdi].step = lcd(steparray);
930 if((im->gdes[gdi].data = malloc((
931 (im->gdes[gdi].end-im->gdes[gdi].start)
932 / im->gdes[gdi].step)
933 * sizeof(double)))==NULL){
934 rrd_set_error("malloc im->gdes[gdi].data");
935 rpnstack_free(&rpnstack);
939 /* Step through the new cdef results array and
940 * calculate the values
942 for (now = im->gdes[gdi].start + im->gdes[gdi].step;
943 now<=im->gdes[gdi].end;
944 now += im->gdes[gdi].step)
946 rpnp_t *rpnp = im -> gdes[gdi].rpnp;
948 /* 3rd arg of rpn_calc is for OP_VARIABLE lookups;
949 * in this case we are advancing by timesteps;
950 * we use the fact that time_t is a synonym for long
952 if (rpn_calc(rpnp,&rpnstack,(long) now,
953 im->gdes[gdi].data,++dataidx) == -1) {
954 /* rpn_calc sets the error string */
955 rpnstack_free(&rpnstack);
958 } /* enumerate over time steps within a CDEF */
963 } /* enumerate over CDEFs */
964 rpnstack_free(&rpnstack);
968 /* massage data so, that we get one value for each x coordinate in the graph */
970 data_proc( image_desc_t *im ){
972 double pixstep = (double)(im->end-im->start)
973 /(double)im->xsize; /* how much time
974 passes in one pixel */
976 double minval=DNAN,maxval=DNAN;
978 unsigned long gr_time;
980 /* memory for the processed data */
981 for(i=0;i<im->gdes_c;i++) {
982 if((im->gdes[i].gf==GF_LINE) ||
983 (im->gdes[i].gf==GF_AREA) ||
984 (im->gdes[i].gf==GF_TICK) ||
985 (im->gdes[i].gf==GF_STACK)) {
986 if((im->gdes[i].p_data = malloc((im->xsize +1)
987 * sizeof(rrd_value_t)))==NULL){
988 rrd_set_error("malloc data_proc");
994 for (i=0;i<im->xsize;i++) { /* for each pixel */
996 gr_time = im->start+pixstep*i; /* time of the current step */
999 for (ii=0;ii<im->gdes_c;ii++) {
1001 switch (im->gdes[ii].gf) {
1005 if (!im->gdes[ii].stack)
1008 /* The time of the data doesn't necessarily match
1009 ** the time of the graph. Beware.
1011 vidx = im->gdes[ii].vidx;
1012 if ( (gr_time >= im->gdes[vidx].start) &&
1013 (gr_time <= im->gdes[vidx].end) ) {
1014 value = im->gdes[vidx].data[
1015 (unsigned long) floor(
1016 (double)(gr_time - im->gdes[vidx].start)
1017 / im->gdes[vidx].step)
1018 * im->gdes[vidx].ds_cnt
1025 if (! isnan(value)) {
1027 im->gdes[ii].p_data[i] = paintval;
1028 /* GF_TICK: the data values are not
1029 ** relevant for min and max
1031 if (finite(paintval) && im->gdes[ii].gf != GF_TICK ) {
1032 if (isnan(minval) || paintval < minval)
1034 if (isnan(maxval) || paintval > maxval)
1038 im->gdes[ii].p_data[i] = DNAN;
1047 /* if min or max have not been asigned a value this is because
1048 there was no data in the graph ... this is not good ...
1049 lets set these to dummy values then ... */
1051 if (isnan(minval)) minval = 0.0;
1052 if (isnan(maxval)) maxval = 1.0;
1054 /* adjust min and max values */
1055 if (isnan(im->minval)
1056 /* don't adjust low-end with log scale */
1057 || ((!im->logarithmic && !im->rigid) && im->minval > minval)
1059 im->minval = minval;
1060 if (isnan(im->maxval)
1061 || (!im->rigid && im->maxval < maxval)
1063 if (im->logarithmic)
1064 im->maxval = maxval * 1.1;
1066 im->maxval = maxval;
1068 /* make sure min and max are not equal */
1069 if (im->minval == im->maxval) {
1071 if (! im->logarithmic) {
1074 /* make sure min and max are not both zero */
1075 if (im->maxval == 0.0) {
1084 /* identify the point where the first gridline, label ... gets placed */
1088 time_t start, /* what is the initial time */
1089 enum tmt_en baseint, /* what is the basic interval */
1090 long basestep /* how many if these do we jump a time */
1094 tm = *localtime(&start);
1097 tm.tm_sec -= tm.tm_sec % basestep; break;
1100 tm.tm_min -= tm.tm_min % basestep;
1105 tm.tm_hour -= tm.tm_hour % basestep; break;
1107 /* we do NOT look at the basestep for this ... */
1110 tm.tm_hour = 0; break;
1112 /* we do NOT look at the basestep for this ... */
1116 tm.tm_mday -= tm.tm_wday -1; /* -1 because we want the monday */
1117 if (tm.tm_wday==0) tm.tm_mday -= 7; /* we want the *previous* monday */
1124 tm.tm_mon -= tm.tm_mon % basestep; break;
1132 tm.tm_year -= (tm.tm_year+1900) % basestep;
1137 /* identify the point where the next gridline, label ... gets placed */
1140 time_t current, /* what is the initial time */
1141 enum tmt_en baseint, /* what is the basic interval */
1142 long basestep /* how many if these do we jump a time */
1147 tm = *localtime(¤t);
1151 tm.tm_sec += basestep; break;
1153 tm.tm_min += basestep; break;
1155 tm.tm_hour += basestep; break;
1157 tm.tm_mday += basestep; break;
1159 tm.tm_mday += 7*basestep; break;
1161 tm.tm_mon += basestep; break;
1163 tm.tm_year += basestep;
1165 madetime = mktime(&tm);
1166 } while (madetime == -1); /* this is necessary to skip impssible times
1167 like the daylight saving time skips */
1173 /* calculate values required for PRINT and GPRINT functions */
1176 print_calc(image_desc_t *im, char ***prdata)
1178 long i,ii,validsteps;
1181 int graphelement = 0;
1184 double magfact = -1;
1188 if (im->imginfo) prlines++;
1189 for(i=0;i<im->gdes_c;i++){
1190 switch(im->gdes[i].gf){
1193 if(((*prdata) = rrd_realloc((*prdata),prlines*sizeof(char *)))==NULL){
1194 rrd_set_error("realloc prdata");
1198 /* PRINT and GPRINT can now print VDEF generated values.
1199 * There's no need to do any calculations on them as these
1200 * calculations were already made.
1202 vidx = im->gdes[i].vidx;
1203 if (im->gdes[vidx].gf==GF_VDEF) { /* simply use vals */
1204 printval = im->gdes[vidx].vf.val;
1205 printtime = im->gdes[vidx].vf.when;
1206 } else { /* need to calculate max,min,avg etcetera */
1207 max_ii =((im->gdes[vidx].end
1208 - im->gdes[vidx].start)
1209 / im->gdes[vidx].step
1210 * im->gdes[vidx].ds_cnt);
1213 for( ii=im->gdes[vidx].ds;
1215 ii+=im->gdes[vidx].ds_cnt){
1216 if (! finite(im->gdes[vidx].data[ii]))
1218 if (isnan(printval)){
1219 printval = im->gdes[vidx].data[ii];
1224 switch (im->gdes[i].cf){
1227 case CF_DEVSEASONAL:
1231 printval += im->gdes[vidx].data[ii];
1234 printval = min( printval, im->gdes[vidx].data[ii]);
1238 printval = max( printval, im->gdes[vidx].data[ii]);
1241 printval = im->gdes[vidx].data[ii];
1244 if (im->gdes[i].cf==CF_AVERAGE || im->gdes[i].cf > CF_LAST) {
1245 if (validsteps > 1) {
1246 printval = (printval / validsteps);
1249 } /* prepare printval */
1251 if (!strcmp(im->gdes[i].format,"%c")) { /* VDEF time print */
1252 if (im->gdes[i].gf == GF_PRINT){
1253 (*prdata)[prlines-2] = malloc((FMT_LEG_LEN+2)*sizeof(char));
1254 sprintf((*prdata)[prlines-2],"%s (%lu)",
1255 ctime(&printtime),printtime);
1256 (*prdata)[prlines-1] = NULL;
1258 sprintf(im->gdes[i].legend,"%s (%lu)",
1259 ctime(&printtime),printtime);
1263 if ((percent_s = strstr(im->gdes[i].format,"%S")) != NULL) {
1264 /* Magfact is set to -1 upon entry to print_calc. If it
1265 * is still less than 0, then we need to run auto_scale.
1266 * Otherwise, put the value into the correct units. If
1267 * the value is 0, then do not set the symbol or magnification
1268 * so next the calculation will be performed again. */
1269 if (magfact < 0.0) {
1270 auto_scale(im,&printval,&si_symb,&magfact);
1271 if (printval == 0.0)
1274 printval /= magfact;
1276 *(++percent_s) = 's';
1277 } else if (strstr(im->gdes[i].format,"%s") != NULL) {
1278 auto_scale(im,&printval,&si_symb,&magfact);
1281 if (im->gdes[i].gf == GF_PRINT){
1282 (*prdata)[prlines-2] = malloc((FMT_LEG_LEN+2)*sizeof(char));
1283 if (bad_format(im->gdes[i].format)) {
1284 rrd_set_error("bad format for [G]PRINT in '%s'", im->gdes[i].format);
1287 #ifdef HAVE_SNPRINTF
1288 snprintf((*prdata)[prlines-2],FMT_LEG_LEN,im->gdes[i].format,printval,si_symb);
1290 sprintf((*prdata)[prlines-2],im->gdes[i].format,printval,si_symb);
1292 (*prdata)[prlines-1] = NULL;
1296 if (bad_format(im->gdes[i].format)) {
1297 rrd_set_error("bad format for [G]PRINT in '%s'", im->gdes[i].format);
1300 #ifdef HAVE_SNPRINTF
1301 snprintf(im->gdes[i].legend,FMT_LEG_LEN-2,im->gdes[i].format,printval,si_symb);
1303 sprintf(im->gdes[i].legend,im->gdes[i].format,printval,si_symb);
1326 return graphelement;
1330 /* place legends with color spots */
1332 leg_place(image_desc_t *im)
1335 int interleg = im->text_prop[TEXT_PROP_LEGEND].size*2.0;
1336 int box =im->text_prop[TEXT_PROP_LEGEND].size*1.5;
1337 int border = im->text_prop[TEXT_PROP_LEGEND].size*2.0;
1338 int fill=0, fill_last;
1340 int leg_x = border, leg_y = im->yimg;
1344 char prt_fctn; /*special printfunctions */
1347 if( !(im->extra_flags & NOLEGEND) ) {
1348 if ((legspace = malloc(im->gdes_c*sizeof(int)))==NULL){
1349 rrd_set_error("malloc for legspace");
1353 for(i=0;i<im->gdes_c;i++){
1356 leg_cc = strlen(im->gdes[i].legend);
1358 /* is there a controle code ant the end of the legend string ? */
1359 if (leg_cc >= 2 && im->gdes[i].legend[leg_cc-2] == '\\') {
1360 prt_fctn = im->gdes[i].legend[leg_cc-1];
1362 im->gdes[i].legend[leg_cc] = '\0';
1366 /* remove exess space */
1367 while (prt_fctn=='g' &&
1369 im->gdes[i].legend[leg_cc-1]==' '){
1371 im->gdes[i].legend[leg_cc]='\0';
1374 legspace[i]=(prt_fctn=='g' ? 0 : interleg);
1377 /* no interleg space if string ends in \g */
1378 fill += legspace[i];
1380 if (im->gdes[i].gf != GF_GPRINT &&
1381 im->gdes[i].gf != GF_COMMENT) {
1384 fill += gfx_get_text_width(im->canvas, fill+border,
1385 im->text_prop[TEXT_PROP_LEGEND].font,
1386 im->text_prop[TEXT_PROP_LEGEND].size,
1388 im->gdes[i].legend);
1393 /* who said there was a special tag ... ?*/
1394 if (prt_fctn=='g') {
1397 if (prt_fctn == '\0') {
1398 if (i == im->gdes_c -1 ) prt_fctn ='l';
1400 /* is it time to place the legends ? */
1401 if (fill > im->ximg - 2*border){
1416 if (prt_fctn != '\0'){
1418 if (leg_c >= 2 && prt_fctn == 'j') {
1419 glue = (im->ximg - fill - 2* border) / (leg_c-1);
1423 if (prt_fctn =='c') leg_x = (im->ximg - fill) / 2.0;
1424 if (prt_fctn =='r') leg_x = im->ximg - fill - border;
1426 for(ii=mark;ii<=i;ii++){
1427 if(im->gdes[ii].legend[0]=='\0')
1429 im->gdes[ii].leg_x = leg_x;
1430 im->gdes[ii].leg_y = leg_y;
1432 gfx_get_text_width(im->canvas, leg_x,
1433 im->text_prop[TEXT_PROP_LEGEND].font,
1434 im->text_prop[TEXT_PROP_LEGEND].size,
1436 im->gdes[ii].legend)
1439 if (im->gdes[ii].gf != GF_GPRINT &&
1440 im->gdes[ii].gf != GF_COMMENT)
1443 leg_y = leg_y + im->text_prop[TEXT_PROP_LEGEND].size*1.2;
1444 if (prt_fctn == 's') leg_y -= im->text_prop[TEXT_PROP_LEGEND].size*1.2;
1456 /* create a grid on the graph. it determines what to do
1457 from the values of xsize, start and end */
1459 /* the xaxis labels are determined from the number of seconds per pixel
1460 in the requested graph */
1465 calc_horizontal_grid(image_desc_t *im)
1471 int decimals, fractionals;
1473 im->ygrid_scale.labfact=2;
1475 range = im->maxval - im->minval;
1476 scaledrange = range / im->magfact;
1478 /* does the scale of this graph make it impossible to put lines
1479 on it? If so, give up. */
1480 if (isnan(scaledrange)) {
1484 /* find grid spaceing */
1486 if(isnan(im->ygridstep)){
1487 if(im->extra_flags & ALTYGRID) {
1488 /* find the value with max number of digits. Get number of digits */
1489 decimals = ceil(log10(max(fabs(im->maxval), fabs(im->minval))));
1490 if(decimals <= 0) /* everything is small. make place for zero */
1493 fractionals = floor(log10(range));
1494 if(fractionals < 0) /* small amplitude. */
1495 sprintf(im->ygrid_scale.labfmt, "%%%d.%df", decimals - fractionals + 1, -fractionals + 1);
1497 sprintf(im->ygrid_scale.labfmt, "%%%d.1f", decimals + 1);
1498 im->ygrid_scale.gridstep = pow((double)10, (double)fractionals);
1499 if(im->ygrid_scale.gridstep == 0) /* range is one -> 0.1 is reasonable scale */
1500 im->ygrid_scale.gridstep = 0.1;
1501 /* should have at least 5 lines but no more then 15 */
1502 if(range/im->ygrid_scale.gridstep < 5)
1503 im->ygrid_scale.gridstep /= 10;
1504 if(range/im->ygrid_scale.gridstep > 15)
1505 im->ygrid_scale.gridstep *= 10;
1506 if(range/im->ygrid_scale.gridstep > 5) {
1507 im->ygrid_scale.labfact = 1;
1508 if(range/im->ygrid_scale.gridstep > 8)
1509 im->ygrid_scale.labfact = 2;
1512 im->ygrid_scale.gridstep /= 5;
1513 im->ygrid_scale.labfact = 5;
1517 for(i=0;ylab[i].grid > 0;i++){
1518 pixel = im->ysize / (scaledrange / ylab[i].grid);
1519 if (gridind == -1 && pixel > 5) {
1526 if (pixel * ylab[gridind].lfac[i] >= 2 * im->text_prop[TEXT_PROP_AXIS].size) {
1527 im->ygrid_scale.labfact = ylab[gridind].lfac[i];
1532 im->ygrid_scale.gridstep = ylab[gridind].grid * im->magfact;
1535 im->ygrid_scale.gridstep = im->ygridstep;
1536 im->ygrid_scale.labfact = im->ylabfact;
1541 int draw_horizontal_grid(image_desc_t *im)
1545 char graph_label[100];
1546 double X0=im->xorigin;
1547 double X1=im->xorigin+im->xsize;
1549 int sgrid = (int)( im->minval / im->ygrid_scale.gridstep - 1);
1550 int egrid = (int)( im->maxval / im->ygrid_scale.gridstep + 1);
1551 scaledstep = im->ygrid_scale.gridstep/im->magfact;
1552 for (i = sgrid; i <= egrid; i++){
1553 double Y0=ytr(im,im->ygrid_scale.gridstep*i);
1554 if ( Y0 >= im->yorigin-im->ysize
1555 && Y0 <= im->yorigin){
1556 if(i % im->ygrid_scale.labfact == 0){
1557 if (i==0 || im->symbol == ' ') {
1559 if(im->extra_flags & ALTYGRID) {
1560 sprintf(graph_label,im->ygrid_scale.labfmt,scaledstep*i);
1563 sprintf(graph_label,"%4.1f",scaledstep*i);
1566 sprintf(graph_label,"%4.0f",scaledstep*i);
1570 sprintf(graph_label,"%4.1f %c",scaledstep*i, im->symbol);
1572 sprintf(graph_label,"%4.0f %c",scaledstep*i, im->symbol);
1576 gfx_new_text ( im->canvas,
1577 X0-im->text_prop[TEXT_PROP_AXIS].size/1.5, Y0,
1578 im->graph_col[GRC_FONT],
1579 im->text_prop[TEXT_PROP_AXIS].font,
1580 im->text_prop[TEXT_PROP_AXIS].size,
1581 im->tabwidth, 0.0, GFX_H_RIGHT, GFX_V_CENTER,
1583 gfx_new_dashed_line ( im->canvas,
1586 MGRIDWIDTH, im->graph_col[GRC_MGRID],
1587 im->grid_dash_on, im->grid_dash_off);
1590 gfx_new_dashed_line ( im->canvas,
1593 GRIDWIDTH, im->graph_col[GRC_GRID],
1594 im->grid_dash_on, im->grid_dash_off);
1602 /* logaritmic horizontal grid */
1604 horizontal_log_grid(image_desc_t *im)
1608 int minoridx=0, majoridx=0;
1609 char graph_label[100];
1611 double value, pixperstep, minstep;
1613 /* find grid spaceing */
1614 pixpex= (double)im->ysize / (log10(im->maxval) - log10(im->minval));
1616 if (isnan(pixpex)) {
1620 for(i=0;yloglab[i][0] > 0;i++){
1621 minstep = log10(yloglab[i][0]);
1622 for(ii=1;yloglab[i][ii+1] > 0;ii++){
1623 if(yloglab[i][ii+2]==0){
1624 minstep = log10(yloglab[i][ii+1])-log10(yloglab[i][ii]);
1628 pixperstep = pixpex * minstep;
1629 if(pixperstep > 5){minoridx = i;}
1630 if(pixperstep > 2 * im->text_prop[TEXT_PROP_LEGEND].size){majoridx = i;}
1634 X1=im->xorigin+im->xsize;
1635 /* paint minor grid */
1636 for (value = pow((double)10, log10(im->minval)
1637 - fmod(log10(im->minval),log10(yloglab[minoridx][0])));
1638 value <= im->maxval;
1639 value *= yloglab[minoridx][0]){
1640 if (value < im->minval) continue;
1642 while(yloglab[minoridx][++i] > 0){
1643 Y0 = ytr(im,value * yloglab[minoridx][i]);
1644 if (Y0 <= im->yorigin - im->ysize) break;
1645 gfx_new_dashed_line ( im->canvas,
1648 GRIDWIDTH, im->graph_col[GRC_GRID],
1649 im->grid_dash_on, im->grid_dash_off);
1653 /* paint major grid and labels*/
1654 for (value = pow((double)10, log10(im->minval)
1655 - fmod(log10(im->minval),log10(yloglab[majoridx][0])));
1656 value <= im->maxval;
1657 value *= yloglab[majoridx][0]){
1658 if (value < im->minval) continue;
1660 while(yloglab[majoridx][++i] > 0){
1661 Y0 = ytr(im,value * yloglab[majoridx][i]);
1662 if (Y0 <= im->yorigin - im->ysize) break;
1663 gfx_new_dashed_line ( im->canvas,
1666 MGRIDWIDTH, im->graph_col[GRC_MGRID],
1667 im->grid_dash_on, im->grid_dash_off);
1669 sprintf(graph_label,"%3.0e",value * yloglab[majoridx][i]);
1670 gfx_new_text ( im->canvas,
1671 X0-im->text_prop[TEXT_PROP_AXIS].size/1.5, Y0,
1672 im->graph_col[GRC_FONT],
1673 im->text_prop[TEXT_PROP_AXIS].font,
1674 im->text_prop[TEXT_PROP_AXIS].size,
1675 im->tabwidth,0.0, GFX_H_RIGHT, GFX_V_CENTER,
1687 int xlab_sel; /* which sort of label and grid ? */
1688 time_t ti, tilab, timajor;
1690 char graph_label[100];
1691 double X0,Y0,Y1; /* points for filled graph and more*/
1694 /* the type of time grid is determined by finding
1695 the number of seconds per pixel in the graph */
1698 if(im->xlab_user.minsec == -1){
1699 factor=(im->end - im->start)/im->xsize;
1701 while ( xlab[xlab_sel+1].minsec != -1
1702 && xlab[xlab_sel+1].minsec <= factor){ xlab_sel++; }
1703 im->xlab_user.gridtm = xlab[xlab_sel].gridtm;
1704 im->xlab_user.gridst = xlab[xlab_sel].gridst;
1705 im->xlab_user.mgridtm = xlab[xlab_sel].mgridtm;
1706 im->xlab_user.mgridst = xlab[xlab_sel].mgridst;
1707 im->xlab_user.labtm = xlab[xlab_sel].labtm;
1708 im->xlab_user.labst = xlab[xlab_sel].labst;
1709 im->xlab_user.precis = xlab[xlab_sel].precis;
1710 im->xlab_user.stst = xlab[xlab_sel].stst;
1713 /* y coords are the same for every line ... */
1715 Y1 = im->yorigin-im->ysize;
1718 /* paint the minor grid */
1719 for(ti = find_first_time(im->start,
1720 im->xlab_user.gridtm,
1721 im->xlab_user.gridst),
1722 timajor = find_first_time(im->start,
1723 im->xlab_user.mgridtm,
1724 im->xlab_user.mgridst);
1726 ti = find_next_time(ti,im->xlab_user.gridtm,im->xlab_user.gridst)
1728 /* are we inside the graph ? */
1729 if (ti < im->start || ti > im->end) continue;
1730 while (timajor < ti) {
1731 timajor = find_next_time(timajor,
1732 im->xlab_user.mgridtm, im->xlab_user.mgridst);
1734 if (ti == timajor) continue; /* skip as falls on major grid line */
1736 gfx_new_dashed_line(im->canvas,X0,Y0+1, X0,Y1-1,GRIDWIDTH,
1737 im->graph_col[GRC_GRID],
1738 im->grid_dash_on, im->grid_dash_off);
1742 /* paint the major grid */
1743 for(ti = find_first_time(im->start,
1744 im->xlab_user.mgridtm,
1745 im->xlab_user.mgridst);
1747 ti = find_next_time(ti,im->xlab_user.mgridtm,im->xlab_user.mgridst)
1749 /* are we inside the graph ? */
1750 if (ti < im->start || ti > im->end) continue;
1752 gfx_new_dashed_line(im->canvas,X0,Y0+3, X0,Y1-2,MGRIDWIDTH,
1753 im->graph_col[GRC_MGRID],
1754 im->grid_dash_on, im->grid_dash_off);
1757 /* paint the labels below the graph */
1758 for(ti = find_first_time(im->start,
1759 im->xlab_user.labtm,
1760 im->xlab_user.labst);
1762 ti = find_next_time(ti,im->xlab_user.labtm,im->xlab_user.labst)
1764 tilab= ti + im->xlab_user.precis/2; /* correct time for the label */
1765 /* are we inside the graph ? */
1766 if (ti < im->start || ti > im->end) continue;
1769 strftime(graph_label,99,im->xlab_user.stst,localtime(&tilab));
1771 # error "your libc has no strftime I guess we'll abort the exercise here."
1773 gfx_new_text ( im->canvas,
1774 xtr(im,tilab), Y0+im->text_prop[TEXT_PROP_AXIS].size/1.5,
1775 im->graph_col[GRC_FONT],
1776 im->text_prop[TEXT_PROP_AXIS].font,
1777 im->text_prop[TEXT_PROP_AXIS].size,
1778 im->tabwidth, 0.0, GFX_H_CENTER, GFX_V_TOP,
1791 /* draw x and y axis */
1792 gfx_new_line ( im->canvas, im->xorigin+im->xsize,im->yorigin,
1793 im->xorigin+im->xsize,im->yorigin-im->ysize,
1794 GRIDWIDTH, im->graph_col[GRC_GRID]);
1796 gfx_new_line ( im->canvas, im->xorigin,im->yorigin-im->ysize,
1797 im->xorigin+im->xsize,im->yorigin-im->ysize,
1798 GRIDWIDTH, im->graph_col[GRC_GRID]);
1800 gfx_new_line ( im->canvas, im->xorigin-4,im->yorigin,
1801 im->xorigin+im->xsize+4,im->yorigin,
1802 MGRIDWIDTH, im->graph_col[GRC_GRID]);
1804 gfx_new_line ( im->canvas, im->xorigin,im->yorigin+4,
1805 im->xorigin,im->yorigin-im->ysize-4,
1806 MGRIDWIDTH, im->graph_col[GRC_GRID]);
1809 /* arrow for X axis direction */
1810 gfx_new_area ( im->canvas,
1811 im->xorigin+im->xsize+3, im->yorigin-3,
1812 im->xorigin+im->xsize+3, im->yorigin+4,
1813 im->xorigin+im->xsize+8, im->yorigin+0.5, /* LINEOFFSET */
1814 im->graph_col[GRC_ARROW]);
1821 grid_paint(image_desc_t *im)
1825 double X0,Y0; /* points for filled graph and more*/
1828 /* draw 3d border */
1829 node = gfx_new_area (im->canvas, 0,im->yimg,
1831 2,2,im->graph_col[GRC_SHADEA]);
1832 gfx_add_point( node , im->ximg - 2, 2 );
1833 gfx_add_point( node , im->ximg, 0 );
1834 gfx_add_point( node , 0,0 );
1835 /* gfx_add_point( node , 0,im->yimg ); */
1837 node = gfx_new_area (im->canvas, 2,im->yimg-2,
1838 im->ximg-2,im->yimg-2,
1840 im->graph_col[GRC_SHADEB]);
1841 gfx_add_point( node , im->ximg,0);
1842 gfx_add_point( node , im->ximg,im->yimg);
1843 gfx_add_point( node , 0,im->yimg);
1844 /* gfx_add_point( node , 0,im->yimg ); */
1847 if (im->draw_x_grid == 1 )
1850 if (im->draw_y_grid == 1){
1851 if(im->logarithmic){
1852 res = horizontal_log_grid(im);
1854 res = draw_horizontal_grid(im);
1857 /* dont draw horizontal grid if there is no min and max val */
1859 char *nodata = "No Data found";
1860 gfx_new_text(im->canvas,im->ximg/2, (2*im->yorigin-im->ysize) / 2,
1861 im->graph_col[GRC_FONT],
1862 im->text_prop[TEXT_PROP_AXIS].font,
1863 im->text_prop[TEXT_PROP_AXIS].size,
1864 im->tabwidth, 0.0, GFX_H_CENTER, GFX_V_CENTER,
1869 /* yaxis description */
1870 if (im->canvas->imgformat != IF_PNG) {
1871 gfx_new_text( im->canvas,
1872 7, (im->yorigin - im->ysize/2),
1873 im->graph_col[GRC_FONT],
1874 im->text_prop[TEXT_PROP_AXIS].font,
1875 im->text_prop[TEXT_PROP_AXIS].size, im->tabwidth, 270.0,
1876 GFX_H_CENTER, GFX_V_CENTER,
1879 /* horrible hack until we can actually print vertically */
1882 int l=strlen(im->ylegend);
1884 for (n=0;n<strlen(im->ylegend);n++) {
1885 s[0]=im->ylegend[n];
1887 gfx_new_text(im->canvas,7,im->text_prop[TEXT_PROP_AXIS].size*(l-n),
1888 im->graph_col[GRC_FONT],
1889 im->text_prop[TEXT_PROP_AXIS].font,
1890 im->text_prop[TEXT_PROP_AXIS].size, im->tabwidth, 270.0,
1891 GFX_H_CENTER, GFX_V_CENTER,
1898 gfx_new_text( im->canvas,
1899 im->ximg/2, im->text_prop[TEXT_PROP_TITLE].size,
1900 im->graph_col[GRC_FONT],
1901 im->text_prop[TEXT_PROP_TITLE].font,
1902 im->text_prop[TEXT_PROP_TITLE].size, im->tabwidth, 0.0,
1903 GFX_H_CENTER, GFX_V_CENTER,
1907 if( !(im->extra_flags & NOLEGEND) ) {
1908 for(i=0;i<im->gdes_c;i++){
1909 if(im->gdes[i].legend[0] =='\0')
1912 /* im->gdes[i].leg_y is the bottom of the legend */
1913 X0 = im->gdes[i].leg_x;
1914 Y0 = im->gdes[i].leg_y;
1916 if ( im->gdes[i].gf != GF_GPRINT
1917 && im->gdes[i].gf != GF_COMMENT) {
1920 boxH = gfx_get_text_width(im->canvas, 0,
1921 im->text_prop[TEXT_PROP_AXIS].font,
1922 im->text_prop[TEXT_PROP_AXIS].size,
1923 im->tabwidth,"M") * 1.25;
1926 node = gfx_new_area(im->canvas,
1931 gfx_add_point ( node, X0+boxH, Y0-boxV );
1932 node = gfx_new_line(im->canvas,
1935 gfx_add_point(node,X0+boxH,Y0);
1936 gfx_add_point(node,X0+boxH,Y0-boxV);
1937 gfx_close_path(node);
1938 X0 += boxH / 1.25 * 2;
1940 gfx_new_text ( im->canvas, X0, Y0,
1941 im->graph_col[GRC_FONT],
1942 im->text_prop[TEXT_PROP_AXIS].font,
1943 im->text_prop[TEXT_PROP_AXIS].size,
1944 im->tabwidth,0.0, GFX_H_LEFT, GFX_V_BOTTOM,
1945 im->gdes[i].legend );
1951 /*****************************************************
1952 * lazy check make sure we rely need to create this graph
1953 *****************************************************/
1955 int lazy_check(image_desc_t *im){
1958 struct stat imgstat;
1960 if (im->lazy == 0) return 0; /* no lazy option */
1961 if (stat(im->graphfile,&imgstat) != 0)
1962 return 0; /* can't stat */
1963 /* one pixel in the existing graph is more then what we would
1965 if (time(NULL) - imgstat.st_mtime >
1966 (im->end - im->start) / im->xsize)
1968 if ((fd = fopen(im->graphfile,"rb")) == NULL)
1969 return 0; /* the file does not exist */
1970 switch (im->canvas->imgformat) {
1972 size = PngSize(fd,&(im->ximg),&(im->yimg));
1982 pie_part(image_desc_t *im, gfx_color_t color,
1983 double PieCenterX, double PieCenterY, double Radius,
1984 double startangle, double endangle)
1988 double step=M_PI/50; /* Number of iterations for the circle;
1989 ** 10 is definitely too low, more than
1990 ** 50 seems to be overkill
1993 /* Strange but true: we have to work clockwise or else
1994 ** anti aliasing nor transparency don't work.
1996 ** This test is here to make sure we do it right, also
1997 ** this makes the for...next loop more easy to implement.
1998 ** The return will occur if the user enters a negative number
1999 ** (which shouldn't be done according to the specs) or if the
2000 ** programmers do something wrong (which, as we all know, never
2001 ** happens anyway :)
2003 if (endangle<startangle) return;
2005 /* Hidden feature: Radius decreases each full circle */
2007 while (angle>=2*M_PI) {
2012 node=gfx_new_area(im->canvas,
2013 PieCenterX+sin(startangle)*Radius,
2014 PieCenterY-cos(startangle)*Radius,
2017 PieCenterX+sin(endangle)*Radius,
2018 PieCenterY-cos(endangle)*Radius,
2020 for (angle=endangle;angle-startangle>=step;angle-=step) {
2022 PieCenterX+sin(angle)*Radius,
2023 PieCenterY-cos(angle)*Radius );
2028 graph_size_location(image_desc_t *im, int elements, int piechart )
2030 /* The actual size of the image to draw is determined from
2031 ** several sources. The size given on the command line is
2032 ** the graph area but we need more as we have to draw labels
2033 ** and other things outside the graph area
2036 /* +-+-------------------------------------------+
2037 ** |l|.................title.....................|
2038 ** |e+--+-------------------------------+--------+
2041 ** |l| l| main graph area | chart |
2044 ** |r+--+-------------------------------+--------+
2045 ** |e| | x-axis labels | |
2046 ** |v+--+-------------------------------+--------+
2047 ** | |..............legends......................|
2048 ** +-+-------------------------------------------+
2050 int Xvertical=0, Yvertical=0,
2051 Xtitle =0, Ytitle =0,
2052 Xylabel =0, Yylabel =0,
2055 Xxlabel =0, Yxlabel =0,
2057 Xlegend =0, Ylegend =0,
2059 Xspacing =10, Yspacing =10;
2061 if (im->ylegend[0] != '\0') {
2062 Xvertical = im->text_prop[TEXT_PROP_LEGEND].size *2;
2063 Yvertical = im->text_prop[TEXT_PROP_LEGEND].size * (strlen(im->ylegend)+1);
2066 if (im->title[0] != '\0') {
2067 /* The title is placed "inbetween" two text lines so it
2068 ** automatically has some vertical spacing. The horizontal
2069 ** spacing is added here, on each side.
2071 Xtitle = gfx_get_text_width(im->canvas, 0,
2072 im->text_prop[TEXT_PROP_TITLE].font,
2073 im->text_prop[TEXT_PROP_TITLE].size,
2075 im->title) + 2*Xspacing;
2076 Ytitle = im->text_prop[TEXT_PROP_TITLE].size*2;
2082 if (im->draw_x_grid) {
2084 Yxlabel=im->text_prop[TEXT_PROP_LEGEND].size *2;
2086 if (im->draw_y_grid) {
2087 Xylabel=im->text_prop[TEXT_PROP_LEGEND].size *6;
2093 im->piesize=im->xsize<im->ysize?im->xsize:im->ysize;
2098 /* Now calculate the total size. Insert some spacing where
2099 desired. im->xorigin and im->yorigin need to correspond
2100 with the lower left corner of the main graph area or, if
2101 this one is not set, the imaginary box surrounding the
2104 /* The legend width cannot yet be determined, as a result we
2105 ** have problems adjusting the image to it. For now, we just
2106 ** forget about it at all; the legend will have to fit in the
2107 ** size already allocated.
2109 im->ximg = Xylabel + Xmain + Xpie + Xspacing;
2110 if (Xmain) im->ximg += Xspacing;
2111 if (Xpie) im->ximg += Xspacing;
2112 im->xorigin = Xspacing + Xylabel;
2113 if (Xtitle > im->ximg) im->ximg = Xtitle;
2115 im->ximg += Xvertical;
2116 im->xorigin += Xvertical;
2120 /* The vertical size is interesting... we need to compare
2121 ** the sum of {Ytitle, Ymain, Yxlabel, Ylegend} with Yvertical
2122 ** however we need to know {Ytitle+Ymain+Yxlabel} in order to
2123 ** start even thinking about Ylegend.
2125 ** Do it in three portions: First calculate the inner part,
2126 ** then do the legend, then adjust the total height of the img.
2129 /* reserve space for main and/or pie */
2130 im->yimg = Ymain + Yxlabel;
2131 if (im->yimg < Ypie) im->yimg = Ypie;
2132 im->yorigin = im->yimg - Yxlabel;
2133 /* reserve space for the title *or* some padding above the graph */
2136 im->yorigin += Ytitle;
2138 im->yimg += Yspacing;
2139 im->yorigin += Yspacing;
2141 /* reserve space for padding below the graph */
2142 im->yimg += Yspacing;
2145 /* Determine where to place the legends onto the image.
2146 ** Adjust im->yimg to match the space requirements.
2148 if(leg_place(im)==-1)
2151 /* last of three steps: check total height of image */
2152 if (im->yimg < Yvertical) im->yimg = Yvertical;
2155 if (Xlegend > im->ximg) {
2157 /* reposition Pie */
2161 /* The pie is placed in the upper right hand corner,
2162 ** just below the title (if any) and with sufficient
2166 im->pie_x = im->ximg - Xspacing - Xpie/2;
2167 im->pie_y = im->yorigin-Ymain+Ypie/2;
2169 im->pie_x = im->ximg/2;
2170 im->pie_y = im->yorigin-Ypie/2;
2176 /* draw that picture thing ... */
2178 graph_paint(image_desc_t *im, char ***calcpr)
2181 int lazy = lazy_check(im);
2183 double PieStart=0.0;
2187 double areazero = 0.0;
2188 enum gf_en stack_gf = GF_PRINT;
2189 graph_desc_t *lastgdes = NULL;
2191 /* if we are lazy and there is nothing to PRINT ... quit now */
2192 if (lazy && im->prt_c==0) return 0;
2194 /* pull the data from the rrd files ... */
2196 if(data_fetch(im)==-1)
2199 /* evaluate VDEF and CDEF operations ... */
2200 if(data_calc(im)==-1)
2203 /* check if we need to draw a piechart */
2204 for(i=0;i<im->gdes_c;i++){
2205 if (im->gdes[i].gf == GF_PART) {
2211 /* calculate and PRINT and GPRINT definitions. We have to do it at
2212 * this point because it will affect the length of the legends
2213 * if there are no graph elements we stop here ...
2214 * if we are lazy, try to quit ...
2216 i=print_calc(im,calcpr);
2218 if(((i==0)&&(piechart==0)) || lazy) return 0;
2220 /* If there's only the pie chart to draw, signal this */
2221 if (i==0) piechart=2;
2223 /* get actual drawing data and find min and max values*/
2224 if(data_proc(im)==-1)
2227 if(!im->logarithmic){si_unit(im);} /* identify si magnitude Kilo, Mega Giga ? */
2229 if(!im->rigid && ! im->logarithmic)
2230 expand_range(im); /* make sure the upper and lower limit are
2233 if (!calc_horizontal_grid(im))
2238 /**************************************************************
2239 *** Calculating sizes and locations became a bit confusing ***
2240 *** so I moved this into a separate function. ***
2241 **************************************************************/
2242 if(graph_size_location(im,i,piechart)==-1)
2245 /* the actual graph is created by going through the individual
2246 graph elements and then drawing them */
2248 node=gfx_new_area ( im->canvas,
2252 im->graph_col[GRC_BACK]);
2254 gfx_add_point(node,0, im->yimg);
2256 if (piechart != 2) {
2257 node=gfx_new_area ( im->canvas,
2258 im->xorigin, im->yorigin,
2259 im->xorigin + im->xsize, im->yorigin,
2260 im->xorigin + im->xsize, im->yorigin-im->ysize,
2261 im->graph_col[GRC_CANVAS]);
2263 gfx_add_point(node,im->xorigin, im->yorigin - im->ysize);
2265 if (im->minval > 0.0)
2266 areazero = im->minval;
2267 if (im->maxval < 0.0)
2268 areazero = im->maxval;
2274 pie_part(im,im->graph_col[GRC_CANVAS],im->pie_x,im->pie_y,im->piesize*0.5,0,2*M_PI);
2277 for(i=0;i<im->gdes_c;i++){
2278 switch(im->gdes[i].gf){
2290 for (ii = 0; ii < im->xsize; ii++)
2292 if (!isnan(im->gdes[i].p_data[ii]) &&
2293 im->gdes[i].p_data[ii] > 0.0)
2295 /* generate a tick */
2296 gfx_new_line(im->canvas, im -> xorigin + ii,
2297 im -> yorigin - (im -> gdes[i].yrule * im -> ysize),
2301 im -> gdes[i].col );
2307 stack_gf = im->gdes[i].gf;
2309 /* fix data points at oo and -oo */
2310 for(ii=0;ii<im->xsize;ii++){
2311 if (isinf(im->gdes[i].p_data[ii])){
2312 if (im->gdes[i].p_data[ii] > 0) {
2313 im->gdes[i].p_data[ii] = im->maxval ;
2315 im->gdes[i].p_data[ii] = im->minval ;
2321 if (im->gdes[i].col != 0x0){
2322 /* GF_LINE and friend */
2323 if(stack_gf == GF_LINE ){
2325 for(ii=1;ii<im->xsize;ii++){
2326 if ( ! isnan(im->gdes[i].p_data[ii-1])
2327 && ! isnan(im->gdes[i].p_data[ii])){
2329 node = gfx_new_line(im->canvas,
2330 ii-1+im->xorigin,ytr(im,im->gdes[i].p_data[ii-1]),
2331 ii+im->xorigin,ytr(im,im->gdes[i].p_data[ii]),
2332 im->gdes[i].linewidth,
2335 gfx_add_point(node,ii+im->xorigin,ytr(im,im->gdes[i].p_data[ii]));
2344 for(ii=1;ii<im->xsize;ii++){
2346 if ( ! isnan(im->gdes[i].p_data[ii-1])
2347 && ! isnan(im->gdes[i].p_data[ii])){
2350 if (im->gdes[i].gf == GF_STACK) {
2351 ybase = ytr(im,lastgdes->p_data[ii-1]);
2353 ybase = ytr(im,areazero);
2356 node = gfx_new_area(im->canvas,
2357 ii-1+im->xorigin,ybase,
2358 ii-1+im->xorigin,ytr(im,im->gdes[i].p_data[ii-1]),
2359 ii+im->xorigin,ytr(im,im->gdes[i].p_data[ii]),
2363 gfx_add_point(node,ii+im->xorigin,ytr(im,im->gdes[i].p_data[ii]));
2367 if ( node != NULL && (ii+1==im->xsize || isnan(im->gdes[i].p_data[ii]) )){
2368 /* GF_AREA STACK type*/
2369 if (im->gdes[i].gf == GF_STACK ) {
2371 for (iii=ii-1;iii>area_start;iii--){
2372 gfx_add_point(node,iii+im->xorigin,ytr(im,lastgdes->p_data[iii]));
2375 gfx_add_point(node,ii+im->xorigin,ytr(im,areazero));
2380 } /* else GF_LINE */
2381 } /* if color != 0x0 */
2382 /* make sure we do not run into trouble when stacking on NaN */
2383 for(ii=0;ii<im->xsize;ii++){
2384 if (isnan(im->gdes[i].p_data[ii])) {
2387 ybase = ytr(im,lastgdes->p_data[ii-1]);
2389 if (isnan(ybase) || !lastgdes ){
2390 ybase = ytr(im,areazero);
2392 im->gdes[i].p_data[ii] = ybase;
2395 lastgdes = &(im->gdes[i]);
2398 if(isnan(im->gdes[i].yrule)) /* fetch variable */
2399 im->gdes[i].yrule = im->gdes[im->gdes[i].vidx].vf.val;
2401 if (finite(im->gdes[i].yrule)) { /* even the fetched var can be NaN */
2402 pie_part(im,im->gdes[i].col,
2403 im->pie_x,im->pie_y,im->piesize*0.4,
2404 M_PI*2.0*PieStart/100.0,
2405 M_PI*2.0*(PieStart+im->gdes[i].yrule)/100.0);
2406 PieStart += im->gdes[i].yrule;
2415 /* grid_paint also does the text */
2418 /* the RULES are the last thing to paint ... */
2419 for(i=0;i<im->gdes_c;i++){
2421 switch(im->gdes[i].gf){
2423 if(isnan(im->gdes[i].yrule)) { /* fetch variable */
2424 im->gdes[i].yrule = im->gdes[im->gdes[i].vidx].vf.val;
2426 if(im->gdes[i].yrule >= im->minval
2427 && im->gdes[i].yrule <= im->maxval)
2428 gfx_new_line(im->canvas,
2429 im->xorigin,ytr(im,im->gdes[i].yrule),
2430 im->xorigin+im->xsize,ytr(im,im->gdes[i].yrule),
2431 1.0,im->gdes[i].col);
2434 if(im->gdes[i].xrule == 0) { /* fetch variable */
2435 im->gdes[i].xrule = im->gdes[im->gdes[i].vidx].vf.when;
2437 if(im->gdes[i].xrule >= im->start
2438 && im->gdes[i].xrule <= im->end)
2439 gfx_new_line(im->canvas,
2440 xtr(im,im->gdes[i].xrule),im->yorigin,
2441 xtr(im,im->gdes[i].xrule),im->yorigin-im->ysize,
2442 1.0,im->gdes[i].col);
2450 if (strcmp(im->graphfile,"-")==0) {
2452 /* Change translation mode for stdout to BINARY */
2453 _setmode( _fileno( stdout ), O_BINARY );
2457 if ((fo = fopen(im->graphfile,"wb")) == NULL) {
2458 rrd_set_error("Opening '%s' for write: %s",im->graphfile,
2463 gfx_render (im->canvas,im->ximg,im->yimg,0x0,fo);
2464 if (strcmp(im->graphfile,"-") != 0)
2470 /*****************************************************
2472 *****************************************************/
2475 gdes_alloc(image_desc_t *im){
2477 long def_step = (im->end-im->start)/im->xsize;
2479 if (im->step > def_step) /* step can be increassed ... no decreassed */
2480 def_step = im->step;
2484 if ((im->gdes = (graph_desc_t *) rrd_realloc(im->gdes, (im->gdes_c)
2485 * sizeof(graph_desc_t)))==NULL){
2486 rrd_set_error("realloc graph_descs");
2491 im->gdes[im->gdes_c-1].step=def_step;
2492 im->gdes[im->gdes_c-1].stack=0;
2493 im->gdes[im->gdes_c-1].start=im->start;
2494 im->gdes[im->gdes_c-1].end=im->end;
2495 im->gdes[im->gdes_c-1].vname[0]='\0';
2496 im->gdes[im->gdes_c-1].data=NULL;
2497 im->gdes[im->gdes_c-1].ds_namv=NULL;
2498 im->gdes[im->gdes_c-1].data_first=0;
2499 im->gdes[im->gdes_c-1].p_data=NULL;
2500 im->gdes[im->gdes_c-1].rpnp=NULL;
2501 im->gdes[im->gdes_c-1].col = 0x0;
2502 im->gdes[im->gdes_c-1].legend[0]='\0';
2503 im->gdes[im->gdes_c-1].rrd[0]='\0';
2504 im->gdes[im->gdes_c-1].ds=-1;
2505 im->gdes[im->gdes_c-1].p_data=NULL;
2509 /* copies input untill the first unescaped colon is found
2510 or until input ends. backslashes have to be escaped as well */
2512 scan_for_col(char *input, int len, char *output)
2517 input[inp] != ':' &&
2520 if (input[inp] == '\\' &&
2521 input[inp+1] != '\0' &&
2522 (input[inp+1] == '\\' ||
2523 input[inp+1] == ':')){
2524 output[outp++] = input[++inp];
2527 output[outp++] = input[inp];
2530 output[outp] = '\0';
2533 /* Some surgery done on this function, it became ridiculously big.
2535 ** - initializing now in rrd_graph_init()
2536 ** - options parsing now in rrd_graph_options()
2537 ** - script parsing now in rrd_graph_script()
2540 rrd_graph(int argc, char **argv, char ***prdata, int *xsize, int *ysize)
2547 #ifdef HAVE_SETLOCALE
2548 setlocale(LC_TIME,"");
2552 rrd_graph_init(&im);
2554 rrd_graph_options(argc,argv,&im);
2555 if (rrd_test_error()) return -1;
2557 if (strlen(argv[optind])>=MAXPATH) {
2558 rrd_set_error("filename (including path) too long");
2561 strncpy(im.graphfile,argv[optind],MAXPATH-1);
2562 im.graphfile[MAXPATH-1]='\0';
2564 rrd_graph_script(argc,argv,&im);
2565 if (rrd_test_error()) return -1;
2567 /* Everything is now read and the actual work can start */
2570 if (graph_paint(&im,prdata)==-1){
2575 /* The image is generated and needs to be output.
2576 ** Also, if needed, print a line with information about the image.
2584 /* maybe prdata is not allocated yet ... lets do it now */
2585 if ((*prdata = calloc(2,sizeof(char *)))==NULL) {
2586 rrd_set_error("malloc imginfo");
2590 if(((*prdata)[0] = malloc((strlen(im.imginfo)+200+strlen(im.graphfile))*sizeof(char)))
2592 rrd_set_error("malloc imginfo");
2595 filename=im.graphfile+strlen(im.graphfile);
2596 while(filename > im.graphfile) {
2597 if (*(filename-1)=='/' || *(filename-1)=='\\' ) break;
2601 sprintf((*prdata)[0],im.imginfo,filename,(long)(im.canvas->zoom*im.ximg),(long)(im.canvas->zoom*im.yimg));
2608 rrd_graph_init(image_desc_t *im)
2612 im->xlab_user.minsec = -1;
2618 im->ylegend[0] = '\0';
2619 im->title[0] = '\0';
2622 im->unitsexponent= 9999;
2628 im->logarithmic = 0;
2629 im->ygridstep = DNAN;
2630 im->draw_x_grid = 1;
2631 im->draw_y_grid = 1;
2636 im->canvas = gfx_new_canvas();
2637 im->grid_dash_on = 1;
2638 im->grid_dash_off = 1;
2640 for(i=0;i<DIM(graph_col);i++)
2641 im->graph_col[i]=graph_col[i];
2643 for(i=0;i<DIM(text_prop);i++){
2644 im->text_prop[i].size = text_prop[i].size;
2645 im->text_prop[i].font = text_prop[i].font;
2650 rrd_graph_options(int argc, char *argv[],image_desc_t *im)
2653 char *parsetime_error = NULL;
2654 char scan_gtm[12],scan_mtm[12],scan_ltm[12],col_nam[12];
2655 time_t start_tmp=0,end_tmp=0;
2657 struct time_value start_tv, end_tv;
2660 parsetime("end-24h", &start_tv);
2661 parsetime("now", &end_tv);
2664 static struct option long_options[] =
2666 {"start", required_argument, 0, 's'},
2667 {"end", required_argument, 0, 'e'},
2668 {"x-grid", required_argument, 0, 'x'},
2669 {"y-grid", required_argument, 0, 'y'},
2670 {"vertical-label",required_argument,0,'v'},
2671 {"width", required_argument, 0, 'w'},
2672 {"height", required_argument, 0, 'h'},
2673 {"interlaced", no_argument, 0, 'i'},
2674 {"upper-limit",required_argument, 0, 'u'},
2675 {"lower-limit",required_argument, 0, 'l'},
2676 {"rigid", no_argument, 0, 'r'},
2677 {"base", required_argument, 0, 'b'},
2678 {"logarithmic",no_argument, 0, 'o'},
2679 {"color", required_argument, 0, 'c'},
2680 {"font", required_argument, 0, 'n'},
2681 {"title", required_argument, 0, 't'},
2682 {"imginfo", required_argument, 0, 'f'},
2683 {"imgformat", required_argument, 0, 'a'},
2684 {"lazy", no_argument, 0, 'z'},
2685 {"zoom", required_argument, 0, 'm'},
2686 {"no-legend", no_argument, 0, 'g'},
2687 {"alt-y-grid", no_argument, 0, 257 },
2688 {"alt-autoscale", no_argument, 0, 258 },
2689 {"alt-autoscale-max", no_argument, 0, 259 },
2690 {"units-exponent",required_argument, 0, 260},
2691 {"step", required_argument, 0, 261},
2692 {"no-gridfit", no_argument, 0, 262},
2694 int option_index = 0;
2698 opt = getopt_long(argc, argv,
2699 "s:e:x:y:v:w:h:iu:l:rb:oc:n:m:t:f:a:z:g",
2700 long_options, &option_index);
2707 im->extra_flags |= ALTYGRID;
2710 im->extra_flags |= ALTAUTOSCALE;
2713 im->extra_flags |= ALTAUTOSCALE_MAX;
2716 im->extra_flags |= NOLEGEND;
2719 im->unitsexponent = atoi(optarg);
2722 im->step = atoi(optarg);
2728 if ((parsetime_error = parsetime(optarg, &start_tv))) {
2729 rrd_set_error( "start time: %s", parsetime_error );
2734 if ((parsetime_error = parsetime(optarg, &end_tv))) {
2735 rrd_set_error( "end time: %s", parsetime_error );
2740 if(strcmp(optarg,"none") == 0){
2746 "%10[A-Z]:%ld:%10[A-Z]:%ld:%10[A-Z]:%ld:%ld:%n",
2748 &im->xlab_user.gridst,
2750 &im->xlab_user.mgridst,
2752 &im->xlab_user.labst,
2753 &im->xlab_user.precis,
2754 &stroff) == 7 && stroff != 0){
2755 strncpy(im->xlab_form, optarg+stroff, sizeof(im->xlab_form) - 1);
2756 if((im->xlab_user.gridtm = tmt_conv(scan_gtm)) == -1){
2757 rrd_set_error("unknown keyword %s",scan_gtm);
2759 } else if ((im->xlab_user.mgridtm = tmt_conv(scan_mtm)) == -1){
2760 rrd_set_error("unknown keyword %s",scan_mtm);
2762 } else if ((im->xlab_user.labtm = tmt_conv(scan_ltm)) == -1){
2763 rrd_set_error("unknown keyword %s",scan_ltm);
2766 im->xlab_user.minsec = 1;
2767 im->xlab_user.stst = im->xlab_form;
2769 rrd_set_error("invalid x-grid format");
2775 if(strcmp(optarg,"none") == 0){
2783 &im->ylabfact) == 2) {
2784 if(im->ygridstep<=0){
2785 rrd_set_error("grid step must be > 0");
2787 } else if (im->ylabfact < 1){
2788 rrd_set_error("label factor must be > 0");
2792 rrd_set_error("invalid y-grid format");
2797 strncpy(im->ylegend,optarg,150);
2798 im->ylegend[150]='\0';
2801 im->maxval = atof(optarg);
2804 im->minval = atof(optarg);
2807 im->base = atol(optarg);
2808 if(im->base != 1024 && im->base != 1000 ){
2809 rrd_set_error("the only sensible value for base apart from 1000 is 1024");
2814 long_tmp = atol(optarg);
2815 if (long_tmp < 10) {
2816 rrd_set_error("width below 10 pixels");
2819 im->xsize = long_tmp;
2822 long_tmp = atol(optarg);
2823 if (long_tmp < 10) {
2824 rrd_set_error("height below 10 pixels");
2827 im->ysize = long_tmp;
2830 im->canvas->interlaced = 1;
2836 im->imginfo = optarg;
2839 if((im->canvas->imgformat = if_conv(optarg)) == -1) {
2840 rrd_set_error("unsupported graphics format '%s'",optarg);
2848 im->logarithmic = 1;
2849 if (isnan(im->minval))
2855 col_nam,&color) == 2){
2857 if((ci=grc_conv(col_nam)) != -1){
2858 im->graph_col[ci]=color;
2860 rrd_set_error("invalid color name '%s'",col_nam);
2863 rrd_set_error("invalid color def format");
2868 /* originally this used char *prop = "" and
2869 ** char *font = "dummy" however this results
2870 ** in a SEG fault, at least on RH7.1
2872 ** The current implementation isn't proper
2873 ** either, font is never freed and prop uses
2874 ** a fixed width string
2883 prop,&size,font) == 3){
2885 if((sindex=text_prop_conv(prop)) != -1){
2886 im->text_prop[sindex].size=size;
2887 im->text_prop[sindex].font=font;
2888 if (sindex==0) { /* the default */
2889 im->text_prop[TEXT_PROP_TITLE].size=size;
2890 im->text_prop[TEXT_PROP_TITLE].font=font;
2891 im->text_prop[TEXT_PROP_AXIS].size=size;
2892 im->text_prop[TEXT_PROP_AXIS].font=font;
2893 im->text_prop[TEXT_PROP_UNIT].size=size;
2894 im->text_prop[TEXT_PROP_UNIT].font=font;
2895 im->text_prop[TEXT_PROP_LEGEND].size=size;
2896 im->text_prop[TEXT_PROP_LEGEND].font=font;
2899 rrd_set_error("invalid fonttag '%s'",prop);
2903 rrd_set_error("invalid text property format");
2909 im->canvas->zoom = atof(optarg);
2910 if (im->canvas->zoom <= 0.0) {
2911 rrd_set_error("zoom factor must be > 0");
2916 strncpy(im->title,optarg,150);
2917 im->title[150]='\0';
2922 rrd_set_error("unknown option '%c'", optopt);
2924 rrd_set_error("unknown option '%s'",argv[optind-1]);
2929 if (optind >= argc) {
2930 rrd_set_error("missing filename");
2934 if (im->logarithmic == 1 && (im->minval <= 0 || isnan(im->minval))){
2935 rrd_set_error("for a logarithmic yaxis you must specify a lower-limit > 0");
2939 if (proc_start_end(&start_tv,&end_tv,&start_tmp,&end_tmp) == -1){
2940 /* error string is set in parsetime.c */
2944 if (start_tmp < 3600*24*365*10){
2945 rrd_set_error("the first entry to fetch should be after 1980 (%ld)",start_tmp);
2949 if (end_tmp < start_tmp) {
2950 rrd_set_error("start (%ld) should be less than end (%ld)",
2951 start_tmp, end_tmp);
2955 im->start = start_tmp;
2959 /* rrd_name_or_num()
2961 ** Scans for a VDEF-variable or a number
2963 ** Returns an integer describing what was found:
2966 ** 1: found an integer; it is returned in both l and d
2967 ** 2: found a float; it is returned in d
2968 ** 3: found a vname; its index is returned in l
2970 ** l and d are undefined unless described above
2973 rrd_name_or_num(image_desc_t *im, char *param, long *l, double *d)
2975 int i1=0,i2=0,i3=0,i4=0,i5=0,i6=0;
2976 char vname[MAX_VNAME_LEN+1];
2978 sscanf(param, "%li%n%*s%n", l,&i1,&i2);
2979 sscanf(param, "%lf%n%*s%n", d,&i3,&i4);
2980 sscanf(param, DEF_NAM_FMT "%n%*s%n", vname, &i5,&i6);
2982 if ( (i1) && (!i2) ) return 1;
2983 if ( (i3) && (!i4) ) return 2;
2984 if ( (i5) && (!i6) ) {
2985 if ((*l = find_var(im,vname))!=-1) return 3;
2990 /* rrd_vname_color()
2992 ** Parses "[<vname|number>][#color]" where at least one
2993 ** of the optional strings must exist.
2995 ** Returns an integer describing what was found.
2996 ** If the result is 0, the rrd_error string may be set.
2999 ** ---------:-----------------------------------
3001 ** ....0000 : a value/variable was not found
3002 ** ....0001 : an integer number was found, returned in both l and d
3003 ** ....0010 : a floating point number was found, returned in d
3004 ** ....0011 : reserved for future values
3005 ** ....01xx : reserved for future values
3006 ** ....1000 : an existing DEF vname was found, idx returned in l
3007 ** ....1001 : an existing CDEF vname was found, idx returned in l
3008 ** ....1010 : an existing VDEF vname was found, idx returned in l
3009 ** ....1011 : reserved for future variables
3010 ** ....11xx : reserved for future variables
3011 ** ...0.... : a color was not found, returned in color
3012 ** ...1.... : a color was found, returned in color
3015 rrd_vname_color(image_desc_t *im, char * param,
3022 if (param[0]!='#') { /* vname or num present, or empty string */
3024 while ((*c!='\0')&&(*c!='#')) c++,i++;
3028 rrd_set_error("Out of memory in function rrd_vname_color");
3033 result=rrd_name_or_num(im, s, l, d);
3035 rrd_set_error("Use of uninitialized vname %s",s);
3039 result=rrd_name_or_num(im, param, l, d);
3041 rrd_set_error("Use of uninitialized vname %s",param);
3045 case 0: return 0; /* error set above */
3049 switch (im->gdes[*l].gf) {
3050 case GF_DEF: result=0x08;break;
3051 case GF_CDEF: result=0x09;break;
3052 case GF_VDEF: result=0x0A;break;
3054 rrd_set_error("Unexpected GF result from function "
3055 "rrd_name_or_num() called from rrd_vname_color");
3060 rrd_set_error("Unexpected result from function "
3061 "rrd_name_or_num() called from rrd_vname_color");
3065 /* Parse color, if any. */
3066 if (param[i] == '\0') return result;
3068 unsigned int r=0,g=0,b=0,a=0xFF;
3070 sscanf(¶m[i], "#%02x%02x%02x%n%02x%n",
3071 &r,&g,&b,&i1,&a,&i2);
3073 rrd_set_error("Unparsable color %s",¶m[i]);
3078 sscanf(¶m[i+i1],"%*s%n",&i2);
3080 rrd_set_error("Garbage after color %s",param[i]);
3083 *color=r<<24|g<<16|b<<8|a;
3088 /* rrd_find_function()
3090 ** Checks if the parameter is a valid function and
3091 ** if so, returns it in the graph description pointer.
3093 ** The return value is a boolean; true if found
3096 rrd_find_function(char *param, graph_desc_t *gdp)
3101 sscanf(param,"%10[A-Z]%n%*1[1-3]%n",funcname,(int *)&i1,(int *)&i2);
3102 gdp->gf=gf_conv(funcname);
3103 if ((int)gdp->gf == -1) {
3104 rrd_set_error("'%s' is not a valid function name",funcname);
3107 if (gdp->gf==GF_LINE) {
3109 gdp->linewidth=param[i1]-'0';
3111 rrd_set_error("LINE should have a width");
3116 rrd_set_error("Only LINE should have a width: %s",param);
3122 if (strlen(param) != i2) {
3123 rrd_set_error("Garbage after function name: %s",param);
3130 ** Takes a string as input; splits this line into multiple
3131 ** parameters on each ":" boundary.
3133 ** If this function returns successful, the caller will have
3134 ** to free() the allocated memory for param.
3136 ** The input string is destroyed, its memory is used by the
3140 rrd_split_line(char *line,char ***param)
3142 int i=0,n=0,quoted=0;
3146 /* scan the amount of colons in the line. We need
3147 ** at most this amount+1 pointers for the array. If
3148 ** any colons are escaped we waste some space.
3150 if (*src!='\0') n=1;
3152 while (*src != '\0')
3153 if (*src++ == ':') n++;
3156 rrd_set_error("No line to split. rrd_split_line was given the empty string.");
3162 /* Allocate memory for an array of n char pointers */
3163 *param=calloc(n,sizeof(char *));
3165 rrd_set_error("Memory allocation failed inside rrd_split_line");
3169 /* split the line and fill the array */
3173 while (*src != '\0') {
3179 case '\\': /* could be \: but also \n */
3184 rrd_set_error("Lone backslash inside rrd_split_line");
3195 if (quoted) *dst++=*src++;
3208 i++; /* i separators means i+1 parameters */
3214 rrd_graph_script_parse_def(int argc,char *argv[],int used,graph_desc_t *gdp) {
3215 time_t start_tmp=0,end_tmp=0;
3216 struct time_value start_tv, end_tv;
3217 char *parsetime_error = NULL;
3218 struct option long_options[] = {
3219 { "step", required_argument, NULL, 256 },
3220 { "start", required_argument, NULL, 's' },
3221 { "end", required_argument, NULL, 'e' },
3223 int option_index = 0;
3229 start_tv.type=ABSOLUTE_TIME;
3231 end_tv.type=ABSOLUTE_TIME;
3233 memcpy(&start_tv.tm, gmtime(&gdp->start) , sizeof(struct tm) );
3234 memcpy(&end_tv.tm, gmtime(&gdp->end) , sizeof(struct tm) );
3237 opt = getopt_long(argc,argv,"-s:e:", long_options,&option_index);
3241 printf("DEBUG: found non-option[%i] %s\n",optind,optarg);
3244 if ((parsetime_error = parsetime(optarg, &start_tv))) {
3245 rrd_set_error( "DEF start time: %s", parsetime_error );
3250 if ((parsetime_error = parsetime(optarg, &end_tv))) {
3251 rrd_set_error( "DEF end time: %s", parsetime_error );
3256 gdp->step = atol(optarg);
3259 printf("DEBUG: unrecognized option %s\n",argv[optind]);
3262 printf("DEBUG: option %c needs parameter\n",optopt);
3265 printf("DEBUG: getopt returned unknown char %c\n",opt);
3269 if (optind < argc) {
3270 printf("DEBUG: %i remaining parameters:\n",argc-optind);
3272 printf("DEBUG: %3i: %s\n",optind,argv[optind++]);
3276 if (proc_start_end(&start_tv,&end_tv,&start_tmp,&end_tmp) == -1){
3277 /* error string is set in parsetime.c */
3281 if (start_tmp < 3600*24*365*10){
3282 rrd_set_error("the first entry to fetch should be after 1980 (%ld)",start_tmp);
3286 if (end_tmp < start_tmp) {
3287 rrd_set_error("start (%ld) should be less than end (%ld)",
3288 start_tmp, end_tmp);
3292 gdp->start = start_tmp;
3297 rrd_graph_script(int argc, char *argv[], image_desc_t *im)
3301 int linepass = 0; /* stack must follow LINE*, AREA or STACK */
3303 int paramcnt,paramused;
3305 for (i=optind+1;i<argc;i++) {
3311 char vname[MAX_VNAME_LEN+1],sep[1];
3315 /* Each command is one element from *argv[]. This command is
3316 ** split at every unescaped colon.
3318 ** Each command defines the most current gdes inside struct im.
3319 ** In stead of typing "im->gdes[im->gdes_c-1]" we use "gdp".
3322 gdp=&im->gdes[im->gdes_c-1];
3323 strcpy(tmpline,argv[i]);
3325 if ((paramcnt=rrd_split_line(argv[i],¶m))==-1) return;
3329 printf("DEBUG: after splitting line:\n");
3330 for (j=0;j<paramcnt;j++)
3331 printf("DEBUG: %3i: %s\n",j,param[j]);
3334 if (!rrd_find_function(param[paramused],gdp)) {
3341 /* function:newvname=string[:ds-name:CF] for xDEF
3342 ** function:vname[#color[:string]] for LINEx,AREA,STACK
3343 ** function:vname#color[:num[:string]] for TICK
3344 ** function:vname-or-num#color[:string] for xRULE,PART
3345 ** function:vname:CF:string for xPRINT
3346 ** function:string for COMMENT
3349 /*TEMP*/argstart=strlen(param[paramused-1])+1;
3351 /* If anything fails just use rrd_set_error() and break from the
3352 ** switch. Just after the switch we call rrd_test_error() and
3353 ** clean up if it is set.
3360 rrd_set_error("Not enough parameters for %s",param[0]);
3363 if (strlen(param[1])>FMT_LEG_LEN) {
3364 rrd_set_error("Comment too long: %s:%s",param[0],param[1]);
3367 strcpy(gdp->legend,param[1]);
3374 rrd_set_error("No name or number in %s",param[0]);
3377 j=rrd_vname_color(im,param[1],
3378 &gdp->xrule,&gdp->yrule,&gdp->col);
3380 if (!j) break; /* error string set by function */
3383 rrd_set_error("Cannot parse name nor number "
3384 "in %s:%s",param[0],param[1]);
3388 rrd_set_error("Cannot use DEF or CDEF based "
3389 "variable in %s:%s",param[0],param[1]);
3392 gdp->vidx=gdp->xrule;
3400 rrd_set_error("Unexpected result while parsing "
3401 "%s:%s, program error",param[0],param[1]);
3403 if (rrd_test_error()) break;
3405 if (paramcnt>paramused) {
3406 if (strlen(param[paramused])>FMT_LEG_LEN) {
3407 rrd_set_error("Comment too long: %s:%s",
3411 strcpy(gdp->legend,param[paramused]);
3417 rrd_set_error("STACK must follow another graphing element");
3423 /* LINEx:vname[#color[:legend]][:STACK]
3424 ** AREA:vname[#color[:legend]][:STACK]
3425 ** STACK:vname[#color[:legend]]
3426 ** TICK:vname#color[:num[:legend]]
3429 j=rrd_vname_color(im,param[paramused++],
3430 &gdp->vidx,&gdp->yrule,&gdp->col);
3431 if (!j) break; /* error string set by function */
3434 rrd_set_error("Cannot parse name nor number "
3435 "in %s:%s",param[0],param[1]);
3439 rrd_set_error("Cannot %s a number",param[0]);
3445 rrd_set_error("Cannot use VDEF based variable "
3446 "with %s %s",param[0],gdp->vname);
3449 if (rrd_test_error()) break;
3451 if (gdp->gf == GF_TICK) {
3453 rrd_set_error("Color not optional for TICK");
3455 } else { /* parse optional number */
3457 sscanf(param[paramused], "%lf%n%*s%n",
3459 if ((k!=0)&&(l==0)) paramused++;
3460 if (paramused<paramcnt)
3461 strcpy(gdp->legend,param[paramused++]);
3464 if (j&0x10) { /* color present */
3465 /* next should be legend or STACK. If it
3466 ** is STACK then leave it at is, else parse
3467 ** the legend (if any)
3469 if (paramused<paramcnt)
3470 if (strcmp("STACK",param[paramused]))
3471 strcpy(gdp->legend,param[paramused++]);
3473 if (paramused<paramcnt)
3474 if (!strcmp("STACK",param[paramused])) {
3484 sscanf(&line[argstart], DEF_NAM_FMT ":%n",gdp->vname,&j);
3486 rrd_set_error("Cannot parse vname in line: '%s'",line);
3490 if (rrd_graph_check_vname(im,gdp->vname,line)) return;
3492 sscanf(&line[argstart], CF_NAM_FMT ":%n",symname,&j);
3494 k=(j!=0)?rrd_graph_check_CF(im,symname,line):1;
3495 #define VIDX im->gdes[gdp->vidx]
3497 case -1: /* looks CF but is not really CF */
3498 if (VIDX.gf == GF_VDEF) rrd_clear_error();
3500 case 0: /* CF present and correct */
3501 if (VIDX.gf == GF_VDEF)
3502 rrd_set_error("Don't use CF when printing VDEF");
3505 case 1: /* CF not present */
3506 if (VIDX.gf == GF_VDEF) rrd_clear_error();
3507 else rrd_set_error("Printing DEF or CDEF needs CF");
3510 rrd_set_error("Oops, bug in GPRINT scanning");
3513 if (rrd_test_error()) break;
3515 if (strlen(&line[argstart])!=0) {
3516 if (rrd_graph_legend(gdp,&line[argstart])==0)
3517 rrd_set_error("Cannot parse legend in line: %s",line);
3518 } else rrd_set_error("No legend in (G)PRINT line: %s",line);
3519 strcpy(gdp->format, gdp->legend);
3526 rrd_set_error("Nothing following %s",param[0]);
3529 sscanf(param[1], DEF_NAM_FMT "=%n",gdp->vname,&j);
3531 rrd_set_error("Could not parse %s:%s",param[0],param[1]);
3534 if (find_var(im,gdp->vname)!=-1) {
3535 rrd_set_error("Variable '%s' in %s:%s' already in use\n",
3536 gdp->vname,param[0],param[1]);
3540 argstart+=j; /* parsed upto and including "xDEF:vname=" */
3543 if (strlen(¶m[1][j])>MAXPATH) {
3544 rrd_set_error("Path too long: %s:%s",param[0],param[1]);
3547 strcpy(gdp->rrd,¶m[1][j]);
3550 rrd_set_error("No DS for %s:%s",param[0],param[1]);
3554 sscanf(param[2],DS_NAM_FMT "%n%*s%n",gdp->ds_nam,&j,&k);
3555 if ((j==0)||(k!=0)) {
3556 rrd_set_error("Cannot parse DS in %s:%s:%s",
3557 param[0],param[1],param[2]);
3562 rrd_set_error("No CF for %s:%s:%s",
3563 param[0],param[1],param[2]);
3567 sscanf(param[3],CF_NAM_FMT "%n%*s%n",symname,&j,&k);
3568 if ((j==0)||(k!=0)) {
3569 rrd_set_error("Cannot parse CF in %s:%s:%s:%s",
3570 param[0],param[1],param[2],param[3]);
3573 if ((gdp->cf = cf_conv(symname))==-1) {
3574 rrd_set_error("Unknown CF '%s' in %s:%s:%s:%s",
3575 param[0],param[1],param[2],param[3]);
3579 if (paramcnt>paramused) { /* optional parameters */
3580 rrd_graph_script_parse_def(paramcnt,param,paramused,gdp);
3581 if (rrd_test_error()) break;
3586 sscanf(¶m[1][j],DEF_NAM_FMT ",%n",vname,&k);
3588 rrd_set_error("Cannot parse vname: %s:%s",
3593 if (rrd_graph_check_vname(im,vname,line)) return;
3594 if ( im->gdes[gdp->vidx].gf != GF_DEF
3595 && im->gdes[gdp->vidx].gf != GF_CDEF) {
3596 rrd_set_error("variable '%s' not DEF nor "
3597 "CDEF in VDEF '%s'", vname,gdp->vname);
3600 vdef_parse(gdp,¶m[1][j]);
3603 if ((gdp->rpnp = rpn_parse(
3608 rrd_set_error("invalid rpn expression in: %s",param[1]);
3613 default: rrd_set_error("Big oops");
3615 if (rrd_test_error()) {
3622 rrd_set_error("can't make a graph without contents");
3623 im_free(im); /* ??? is this set ??? */
3629 rrd_graph_check_vname(image_desc_t *im, char *varname, char *err)
3631 if ((im->gdes[im->gdes_c-1].vidx=find_var(im,varname))==-1) {
3632 rrd_set_error("Unknown variable '%s' in %s",varname,err);
3638 rrd_graph_color(image_desc_t *im, char *var, char *err, int optional)
3641 graph_desc_t *gdp=&im->gdes[im->gdes_c-1];
3643 color=strstr(var,"#");
3646 rrd_set_error("Found no color in %s",err);
3655 rest=strstr(color,":");
3663 sscanf(color,"#%6lx%n",&col,&n);
3664 col = (col << 8) + 0xff /* shift left by 8 */;
3665 if (n!=7) rrd_set_error("Color problem in %s",err);
3668 sscanf(color,"#%8lx%n",&col,&n);
3671 rrd_set_error("Color problem in %s",err);
3673 if (rrd_test_error()) return 0;
3679 rrd_graph_check_CF(image_desc_t *im, char *symname, char *err)
3681 if ((im->gdes[im->gdes_c-1].cf=cf_conv(symname))==-1) {
3682 rrd_set_error("Unknown CF '%s' in %s",symname,err);
3688 rrd_graph_legend(graph_desc_t *gdp, char *line)
3692 i=scan_for_col(line,FMT_LEG_LEN,gdp->legend);
3694 return (strlen(&line[i])==0);
3698 int bad_format(char *fmt) {
3702 while (*ptr != '\0')
3703 if (*ptr++ == '%') {
3705 /* line cannot end with percent char */
3706 if (*ptr == '\0') return 1;
3708 /* '%s', '%S' and '%%' are allowed */
3709 if (*ptr == 's' || *ptr == 'S' || *ptr == '%') ptr++;
3711 /* or else '% 6.2lf' and such are allowed */
3714 /* optional padding character */
3715 if (*ptr == ' ' || *ptr == '+' || *ptr == '-') ptr++;
3717 /* This should take care of 'm.n' with all three optional */
3718 while (*ptr >= '0' && *ptr <= '9') ptr++;
3719 if (*ptr == '.') ptr++;
3720 while (*ptr >= '0' && *ptr <= '9') ptr++;
3722 /* Either 'le' or 'lf' must follow here */
3723 if (*ptr++ != 'l') return 1;
3724 if (*ptr == 'e' || *ptr == 'f') ptr++;
3735 vdef_parse(gdes,str)
3736 struct graph_desc_t *gdes;
3739 /* A VDEF currently is either "func" or "param,func"
3740 * so the parsing is rather simple. Change if needed.
3747 sscanf(str,"%le,%29[A-Z]%n",¶m,func,&n);
3748 if (n==strlen(str)) { /* matched */
3752 sscanf(str,"%29[A-Z]%n",func,&n);
3753 if (n==strlen(str)) { /* matched */
3756 rrd_set_error("Unknown function string '%s' in VDEF '%s'"
3763 if (!strcmp("PERCENT",func)) gdes->vf.op = VDEF_PERCENT;
3764 else if (!strcmp("MAXIMUM",func)) gdes->vf.op = VDEF_MAXIMUM;
3765 else if (!strcmp("AVERAGE",func)) gdes->vf.op = VDEF_AVERAGE;
3766 else if (!strcmp("MINIMUM",func)) gdes->vf.op = VDEF_MINIMUM;
3767 else if (!strcmp("TOTAL", func)) gdes->vf.op = VDEF_TOTAL;
3768 else if (!strcmp("FIRST", func)) gdes->vf.op = VDEF_FIRST;
3769 else if (!strcmp("LAST", func)) gdes->vf.op = VDEF_LAST;
3771 rrd_set_error("Unknown function '%s' in VDEF '%s'\n"
3778 switch (gdes->vf.op) {
3780 if (isnan(param)) { /* no parameter given */
3781 rrd_set_error("Function '%s' needs parameter in VDEF '%s'\n"
3787 if (param>=0.0 && param<=100.0) {
3788 gdes->vf.param = param;
3789 gdes->vf.val = DNAN; /* undefined */
3790 gdes->vf.when = 0; /* undefined */
3792 rrd_set_error("Parameter '%f' out of range in VDEF '%s'\n"
3806 gdes->vf.param = DNAN;
3807 gdes->vf.val = DNAN;
3810 rrd_set_error("Function '%s' needs no parameter in VDEF '%s'\n"
3827 graph_desc_t *src,*dst;
3831 dst = &im->gdes[gdi];
3832 src = &im->gdes[dst->vidx];
3833 data = src->data + src->ds;
3834 steps = (src->end - src->start) / src->step;
3837 printf("DEBUG: start == %lu, end == %lu, %lu steps\n"
3844 switch (dst->vf.op) {
3845 case VDEF_PERCENT: {
3846 rrd_value_t * array;
3850 if ((array = malloc(steps*sizeof(double)))==NULL) {
3851 rrd_set_error("malloc VDEV_PERCENT");
3854 for (step=0;step < steps; step++) {
3855 array[step]=data[step*src->ds_cnt];
3857 qsort(array,step,sizeof(double),vdef_percent_compar);
3859 field = (steps-1)*dst->vf.param/100;
3860 dst->vf.val = array[field];
3861 dst->vf.when = 0; /* no time component */
3863 for(step=0;step<steps;step++)
3864 printf("DEBUG: %3li:%10.2f %c\n",step,array[step],step==field?'*':' ');
3870 while (step != steps && isnan(data[step*src->ds_cnt])) step++;
3871 if (step == steps) {
3875 dst->vf.val = data[step*src->ds_cnt];
3876 dst->vf.when = src->start + (step+1)*src->step;
3878 while (step != steps) {
3879 if (finite(data[step*src->ds_cnt])) {
3880 if (data[step*src->ds_cnt] > dst->vf.val) {
3881 dst->vf.val = data[step*src->ds_cnt];
3882 dst->vf.when = src->start + (step+1)*src->step;
3889 case VDEF_AVERAGE: {
3892 for (step=0;step<steps;step++) {
3893 if (finite(data[step*src->ds_cnt])) {
3894 sum += data[step*src->ds_cnt];
3899 if (dst->vf.op == VDEF_TOTAL) {
3900 dst->vf.val = sum*src->step;
3901 dst->vf.when = cnt*src->step; /* not really "when" */
3903 dst->vf.val = sum/cnt;
3904 dst->vf.when = 0; /* no time component */
3914 while (step != steps && isnan(data[step*src->ds_cnt])) step++;
3915 if (step == steps) {
3919 dst->vf.val = data[step*src->ds_cnt];
3920 dst->vf.when = src->start + (step+1)*src->step;
3922 while (step != steps) {
3923 if (finite(data[step*src->ds_cnt])) {
3924 if (data[step*src->ds_cnt] < dst->vf.val) {
3925 dst->vf.val = data[step*src->ds_cnt];
3926 dst->vf.when = src->start + (step+1)*src->step;
3933 /* The time value returned here is one step before the
3934 * actual time value. This is the start of the first
3938 while (step != steps && isnan(data[step*src->ds_cnt])) step++;
3939 if (step == steps) { /* all entries were NaN */
3943 dst->vf.val = data[step*src->ds_cnt];
3944 dst->vf.when = src->start + step*src->step;
3948 /* The time value returned here is the
3949 * actual time value. This is the end of the last
3953 while (step >= 0 && isnan(data[step*src->ds_cnt])) step--;
3954 if (step < 0) { /* all entries were NaN */
3958 dst->vf.val = data[step*src->ds_cnt];
3959 dst->vf.when = src->start + (step+1)*src->step;
3966 /* NaN < -INF < finite_values < INF */
3968 vdef_percent_compar(a,b)
3971 /* Equality is not returned; this doesn't hurt except
3972 * (maybe) for a little performance.
3975 /* First catch NaN values. They are smallest */
3976 if (isnan( *(double *)a )) return -1;
3977 if (isnan( *(double *)b )) return 1;
3979 /* NaN doesn't reach this part so INF and -INF are extremes.
3980 * The sign from isinf() is compatible with the sign we return
3982 if (isinf( *(double *)a )) return isinf( *(double *)a );
3983 if (isinf( *(double *)b )) return isinf( *(double *)b );
3985 /* If we reach this, both values must be finite */
3986 if ( *(double *)a < *(double *)b ) return -1; else return 1;