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