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