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