019f49920d54181862dda2f956be271c39a6b8c6
1 /****************************************************************************
2 * RRDtool 1.0.33 Copyright Tobias Oetiker, 1997 - 2000
3 ****************************************************************************
4 * rrd__graph.c make creates ne rrds
5 ****************************************************************************/
7 #include "rrd_tool.h"
8 #include <gd.h>
9 #include <gdlucidan10.h>
10 #include <gdlucidab12.h>
11 #include <sys/stat.h>
12 #ifdef WIN32
13 #include <io.h>
14 #include <fcntl.h>
15 #endif
16 #include "rrd_rpncalc.h"
18 #define SmallFont gdLucidaNormal10
19 #define LargeFont gdLucidaBold12
21 /* #define DEBUG */
23 #ifdef DEBUG
24 # define DPRINT(x) (void)(printf x, printf("\n"))
25 #else
26 # define DPRINT(x)
27 #endif
29 #define DEF_NAM_FMT "%29[_A-Za-z0-9]"
31 enum tmt_en {TMT_SECOND=0,TMT_MINUTE,TMT_HOUR,TMT_DAY,
32 TMT_WEEK,TMT_MONTH,TMT_YEAR};
34 enum grc_en {GRC_CANVAS=0,GRC_BACK,GRC_SHADEA,GRC_SHADEB,
35 GRC_GRID,GRC_MGRID,GRC_FONT,GRC_FRAME,GRC_ARROW,__GRC_END__};
38 enum gf_en {GF_PRINT=0,GF_GPRINT,GF_COMMENT,GF_HRULE,GF_VRULE,GF_LINE1,
39 GF_LINE2,GF_LINE3,GF_AREA,GF_STACK,GF_TICK,GF_DEF, GF_CDEF};
41 enum if_en {IF_GIF=0,IF_PNG=1};
43 typedef struct col_trip_t {
44 int red; /* red = -1 is no color */
45 int green;
46 int blue;
47 int i; /* color index assigned in gif image i=-1 is unasigned*/
48 } col_trip_t;
51 typedef struct xlab_t {
52 long minsec; /* minimum sec per pix */
53 enum tmt_en gridtm; /* grid interval in what ?*/
54 long gridst; /* how many whats per grid*/
55 enum tmt_en mgridtm; /* label interval in what ?*/
56 long mgridst; /* how many whats per label*/
57 enum tmt_en labtm; /* label interval in what ?*/
58 long labst; /* how many whats per label*/
59 long precis; /* label precision -> label placement*/
60 char *stst; /* strftime string*/
61 } xlab_t;
63 xlab_t xlab[] = {
64 {0, TMT_SECOND,30, TMT_MINUTE,5, TMT_MINUTE,5, 0,"%H:%M"},
65 {2, TMT_MINUTE,1, TMT_MINUTE,5, TMT_MINUTE,5, 0,"%H:%M"},
66 {5, TMT_MINUTE,2, TMT_MINUTE,10, TMT_MINUTE,10, 0,"%H:%M"},
67 {10, TMT_MINUTE,5, TMT_MINUTE,20, TMT_MINUTE,20, 0,"%H:%M"},
68 {30, TMT_MINUTE,10, TMT_HOUR,1, TMT_HOUR,1, 0,"%H:%M"},
69 {60, TMT_MINUTE,30, TMT_HOUR,2, TMT_HOUR,2, 0,"%H:%M"},
70 {180, TMT_HOUR,1, TMT_HOUR,6, TMT_HOUR,6, 0,"%H:%M"},
71 /*{300, TMT_HOUR,3, TMT_HOUR,12, TMT_HOUR,12, 12*3600,"%a %p"}, this looks silly*/
72 {600, TMT_HOUR,6, TMT_DAY,1, TMT_DAY,1, 24*3600,"%a"},
73 {1800, TMT_HOUR,12, TMT_DAY,1, TMT_DAY,2, 24*3600,"%a"},
74 {3600, TMT_DAY,1, TMT_WEEK,1, TMT_WEEK,1, 7*24*3600,"Week %W"},
75 {3*3600, TMT_WEEK,1, TMT_MONTH,1, TMT_WEEK,2, 7*24*3600,"Week %W"},
76 {6*3600, TMT_MONTH,1, TMT_MONTH,1, TMT_MONTH,1, 30*24*3600,"%b"},
77 {48*3600, TMT_MONTH,1, TMT_MONTH,3, TMT_MONTH,3, 30*24*3600,"%b"},
78 {10*24*3600, TMT_YEAR,1, TMT_YEAR,1, TMT_YEAR,1, 365*24*3600,"%y"},
79 {-1,TMT_MONTH,0,TMT_MONTH,0,TMT_MONTH,0,0,""}
80 };
82 /* sensible logarithmic y label intervals ...
83 the first element of each row defines the possible starting points on the
84 y axis ... the other specify the */
86 double yloglab[][12]= {{ 1e9, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
87 { 1e3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
88 { 1e1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
89 /* { 1e1, 1, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, */
90 { 1e1, 1, 2.5, 5, 7.5, 0, 0, 0, 0, 0, 0, 0 },
91 { 1e1, 1, 2, 4, 6, 8, 0, 0, 0, 0, 0, 0 },
92 { 1e1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0 },
93 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }};
95 /* sensible y label intervals ...*/
97 typedef struct ylab_t {
98 double grid; /* grid spacing */
99 int lfac[4]; /* associated label spacing*/
100 } ylab_t;
102 ylab_t ylab[]= {
103 {0.1, {1,2, 5,10}},
104 {0.2, {1,5,10,20}},
105 {0.5, {1,2, 4,10}},
106 {1.0, {1,2, 5,10}},
107 {2.0, {1,5,10,20}},
108 {5.0, {1,2, 4,10}},
109 {10.0, {1,2, 5,10}},
110 {20.0, {1,5,10,20}},
111 {50.0, {1,2, 4,10}},
112 {100.0, {1,2, 5,10}},
113 {200.0, {1,5,10,20}},
114 {500.0, {1,2, 4,10}},
115 {0.0, {0,0,0,0}}};
119 col_trip_t graph_col[] = { /* default colors */
120 {255,255,255,-1}, /* canvas */
121 {245,245,245,-1}, /* background */
122 {200,200,200,-1}, /* shade A */
123 {150,150,150,-1}, /* shade B */
124 {140,140,140,-1}, /* grid */
125 {130,30,30,-1}, /* major grid */
126 {0,0,0,-1}, /* font */
127 {0,0,0,-1}, /* frame */
128 {255,0,0,-1} /*arrow*/
129 };
131 /* this structure describes the elements which can make up a graph.
132 because they are quite diverse, not all elements will use all the
133 possible parts of the structure. */
134 #ifdef HAVE_SNPRINTF
135 #define FMT_LEG_LEN 200
136 #else
137 #define FMT_LEG_LEN 2000
138 #endif
140 typedef struct graph_desc_t {
141 enum gf_en gf; /* graphing function */
142 char vname[30]; /* name of the variable */
143 long vidx; /* gdes reference */
144 char rrd[255]; /* name of the rrd_file containing data */
145 char ds_nam[DS_NAM_SIZE]; /* data source name */
146 long ds; /* data source number */
147 enum cf_en cf; /* consolidation function */
148 col_trip_t col; /* graph color */
149 char format[FMT_LEG_LEN+5]; /* format for PRINT AND GPRINT */
150 char legend[FMT_LEG_LEN+5]; /* legend*/
151 gdPoint legloc; /* location of legend */
152 double yrule; /* value for y rule line */
153 time_t xrule; /* value for x rule line */
154 rpnp_t *rpnp; /* instructions for CDEF function */
156 /* description of data fetched for the graph element */
157 time_t start,end; /* timestaps for first and last data element */
158 unsigned long step; /* time between samples */
159 unsigned long ds_cnt; /* how many data sources are there in the fetch */
160 long data_first; /* first pointer to this data */
161 char **ds_namv; /* name of datasources in the fetch. */
162 rrd_value_t *data; /* the raw data drawn from the rrd */
163 rrd_value_t *p_data; /* processed data, xsize elments */
165 } graph_desc_t;
167 #define ALTYGRID 0x01 /* use alternative y grid algorithm */
168 #define ALTAUTOSCALE 0x02 /* use alternative algorithm to find lower and upper bounds */
169 #define ALTAUTOSCALE_MAX 0x04 /* use alternative algorithm to find upper bounds */
170 #define NOLEGEND 0x08 /* use no legend */
172 typedef struct image_desc_t {
174 /* configuration of graph */
176 char graphfile[MAXPATH]; /* filename for graphic */
177 long xsize,ysize; /* graph area size in pixels */
178 col_trip_t graph_col[__GRC_END__]; /* real colors for the graph */
179 char ylegend[200]; /* legend along the yaxis */
180 char title[200]; /* title for graph */
181 int draw_x_grid; /* no x-grid at all */
182 int draw_y_grid; /* no x-grid at all */
183 xlab_t xlab_user; /* user defined labeling for xaxis */
184 char xlab_form[200]; /* format for the label on the xaxis */
186 double ygridstep; /* user defined step for y grid */
187 int ylabfact; /* every how many y grid shall a label be written ? */
189 time_t start,end; /* what time does the graph cover */
190 unsigned long step; /* any preference for the default step ? */
191 rrd_value_t minval,maxval; /* extreme values in the data */
192 int rigid; /* do not expand range even with
193 values outside */
194 char* imginfo; /* construct an <IMG ... tag and return
195 as first retval */
196 int lazy; /* only update the gif if there is reasonable
197 probablility that the existing one is out of date */
198 int logarithmic; /* scale the yaxis logarithmic */
199 enum if_en imgformat; /* image format */
201 /* status information */
203 long xorigin,yorigin;/* where is (0,0) of the graph */
204 long xgif,ygif; /* total size of the gif */
205 int interlaced; /* will the graph be interlaced? */
206 double magfact; /* numerical magnitude*/
207 long base; /* 1000 or 1024 depending on what we graph */
208 char symbol; /* magnitude symbol for y-axis */
209 int unitsexponent; /* 10*exponent for units on y-asis */
210 int extra_flags; /* flags for boolean options */
211 /* data elements */
213 long prt_c; /* number of print elements */
214 long gdes_c; /* number of graphics elements */
215 graph_desc_t *gdes; /* points to an array of graph elements */
217 } image_desc_t;
219 /* Prototypes */
220 int xtr(image_desc_t *,time_t);
221 int ytr(image_desc_t *, double);
222 enum gf_en gf_conv(char *);
223 enum if_en if_conv(char *);
224 enum tmt_en tmt_conv(char *);
225 enum grc_en grc_conv(char *);
226 int im_free(image_desc_t *);
227 void auto_scale( image_desc_t *, double *, char **, double *);
228 void si_unit( image_desc_t *);
229 void expand_range(image_desc_t *);
230 void reduce_data( enum cf_en, unsigned long, time_t *, time_t *, unsigned long *, unsigned long *, rrd_value_t **);
231 int data_fetch( image_desc_t *);
232 long find_var(image_desc_t *, char *);
233 long find_var_wrapper(void *arg1, char *key);
234 long lcd(long *);
235 int data_calc( image_desc_t *);
236 int data_proc( image_desc_t *);
237 time_t find_first_time( time_t, enum tmt_en, long);
238 time_t find_next_time( time_t, enum tmt_en, long);
239 void gator( gdImagePtr, int, int);
240 int print_calc(image_desc_t *, char ***);
241 int leg_place(image_desc_t *);
242 int horizontal_grid(gdImagePtr, image_desc_t *);
243 int horizontal_log_grid(gdImagePtr, image_desc_t *);
244 void vertical_grid( gdImagePtr, image_desc_t *);
245 void axis_paint( image_desc_t *, gdImagePtr);
246 void grid_paint( image_desc_t *, gdImagePtr);
247 gdImagePtr MkLineBrush(image_desc_t *,long, enum gf_en);
248 int lazy_check(image_desc_t *);
249 int graph_paint(image_desc_t *, char ***);
250 int gdes_alloc(image_desc_t *);
251 int scan_for_col(char *, int, char *);
252 int rrd_graph(int, char **, char ***, int *, int *);
253 int bad_format(char *);
255 /* translate time values into x coordinates */
256 /*#define xtr(x) (int)((double)im->xorigin \
257 + ((double) im->xsize / (double)(im->end - im->start) ) \
258 * ((double)(x) - im->start)+0.5) */
259 /* initialize with xtr(im,0); */
260 int
261 xtr(image_desc_t *im,time_t mytime){
262 static double pixie;
263 if (mytime==0){
264 pixie = (double) im->xsize / (double)(im->end - im->start);
265 return im->xorigin;
266 }
267 return (int)((double)im->xorigin
268 + pixie * ( mytime - im->start ) );
269 }
271 /* translate data values into y coordinates */
273 /* #define ytr(x) (int)((double)im->yorigin \
274 - ((double) im->ysize / (im->maxval - im->minval) ) \
275 * ((double)(x) - im->minval)+0.5) */
276 int
277 ytr(image_desc_t *im, double value){
278 static double pixie;
279 double yval;
280 if (isnan(value)){
281 if(!im->logarithmic)
282 pixie = (double) im->ysize / (im->maxval - im->minval);
283 else
284 pixie = (double) im->ysize / (log10(im->maxval) - log10(im->minval));
285 yval = im->yorigin;
286 } else if(!im->logarithmic) {
287 yval = im->yorigin - pixie * (value - im->minval) + 0.5;
288 } else {
289 if (value < im->minval) {
290 yval = im->yorigin;
291 } else {
292 yval = im->yorigin - pixie * (log10(value) - log10(im->minval)) + 0.5;
293 }
294 }
295 /* make sure we don't return anything too unreasonable. GD lib can
296 get terribly slow when drawing lines outside its scope. This is
297 especially problematic in connection with the rigid option */
298 if (! im->rigid) {
299 return (int)yval;
300 } else if ((int)yval > im->yorigin) {
301 return im->yorigin+2;
302 } else if ((int) yval < im->yorigin - im->ysize){
303 return im->yorigin - im->ysize - 2;
304 } else {
305 return (int)yval;
306 }
307 }
313 /* conversion function for symbolic entry names */
316 #define conv_if(VV,VVV) \
317 if (strcmp(#VV, string) == 0) return VVV ;
319 enum gf_en gf_conv(char *string){
321 conv_if(PRINT,GF_PRINT)
322 conv_if(GPRINT,GF_GPRINT)
323 conv_if(COMMENT,GF_COMMENT)
324 conv_if(HRULE,GF_HRULE)
325 conv_if(VRULE,GF_VRULE)
326 conv_if(LINE1,GF_LINE1)
327 conv_if(LINE2,GF_LINE2)
328 conv_if(LINE3,GF_LINE3)
329 conv_if(AREA,GF_AREA)
330 conv_if(STACK,GF_STACK)
331 conv_if(TICK,GF_TICK)
332 conv_if(DEF,GF_DEF)
333 conv_if(CDEF,GF_CDEF)
335 return (-1);
336 }
338 enum if_en if_conv(char *string){
340 conv_if(GIF,IF_GIF)
341 conv_if(PNG,IF_PNG)
343 return (-1);
344 }
346 enum tmt_en tmt_conv(char *string){
348 conv_if(SECOND,TMT_SECOND)
349 conv_if(MINUTE,TMT_MINUTE)
350 conv_if(HOUR,TMT_HOUR)
351 conv_if(DAY,TMT_DAY)
352 conv_if(WEEK,TMT_WEEK)
353 conv_if(MONTH,TMT_MONTH)
354 conv_if(YEAR,TMT_YEAR)
355 return (-1);
356 }
358 enum grc_en grc_conv(char *string){
360 conv_if(BACK,GRC_BACK)
361 conv_if(CANVAS,GRC_CANVAS)
362 conv_if(SHADEA,GRC_SHADEA)
363 conv_if(SHADEB,GRC_SHADEB)
364 conv_if(GRID,GRC_GRID)
365 conv_if(MGRID,GRC_MGRID)
366 conv_if(FONT,GRC_FONT)
367 conv_if(FRAME,GRC_FRAME)
368 conv_if(ARROW,GRC_ARROW)
370 return -1;
371 }
373 #undef conv_if
377 int
378 im_free(image_desc_t *im)
379 {
380 long i,ii;
381 if (im == NULL) return 0;
382 for(i=0;i<im->gdes_c;i++){
383 if (im->gdes[i].data_first){
384 /* careful here, because a single pointer can occur several times */
385 free (im->gdes[i].data);
386 if (im->gdes[i].ds_namv){
387 for (ii=0;ii<im->gdes[i].ds_cnt;ii++)
388 free(im->gdes[i].ds_namv[ii]);
389 free(im->gdes[i].ds_namv);
390 }
391 }
392 free (im->gdes[i].p_data);
393 free (im->gdes[i].rpnp);
394 }
395 free(im->gdes);
396 return 0;
397 }
399 /* find SI magnitude symbol for the given number*/
400 void
401 auto_scale(
402 image_desc_t *im, /* image description */
403 double *value,
404 char **symb_ptr,
405 double *magfact
406 )
407 {
409 char *symbol[] = {"a", /* 10e-18 Ato */
410 "f", /* 10e-15 Femto */
411 "p", /* 10e-12 Pico */
412 "n", /* 10e-9 Nano */
413 "u", /* 10e-6 Micro */
414 "m", /* 10e-3 Milli */
415 " ", /* Base */
416 "k", /* 10e3 Kilo */
417 "M", /* 10e6 Mega */
418 "G", /* 10e9 Giga */
419 "T", /* 10e12 Terra */
420 "P", /* 10e15 Peta */
421 "E"};/* 10e18 Exa */
423 int symbcenter = 6;
424 int sindex;
426 if (*value == 0.0 || isnan(*value) ) {
427 sindex = 0;
428 *magfact = 1.0;
429 } else {
430 sindex = floor(log(fabs(*value))/log((double)im->base));
431 *magfact = pow((double)im->base, (double)sindex);
432 (*value) /= (*magfact);
433 }
434 if ( sindex <= symbcenter && sindex >= -symbcenter) {
435 (*symb_ptr) = symbol[sindex+symbcenter];
436 }
437 else {
438 (*symb_ptr) = "?";
439 }
440 }
443 /* find SI magnitude symbol for the numbers on the y-axis*/
444 void
445 si_unit(
446 image_desc_t *im /* image description */
447 )
448 {
450 char symbol[] = {'a', /* 10e-18 Ato */
451 'f', /* 10e-15 Femto */
452 'p', /* 10e-12 Pico */
453 'n', /* 10e-9 Nano */
454 'u', /* 10e-6 Micro */
455 'm', /* 10e-3 Milli */
456 ' ', /* Base */
457 'k', /* 10e3 Kilo */
458 'M', /* 10e6 Mega */
459 'G', /* 10e9 Giga */
460 'T', /* 10e12 Terra */
461 'P', /* 10e15 Peta */
462 'E'};/* 10e18 Exa */
464 int symbcenter = 6;
465 double digits;
467 if (im->unitsexponent != 9999) {
468 /* unitsexponent = 9, 6, 3, 0, -3, -6, -9, etc */
469 digits = floor(im->unitsexponent / 3);
470 } else {
471 digits = floor( log( max( fabs(im->minval),fabs(im->maxval)))/log((double)im->base));
472 }
473 im->magfact = pow((double)im->base , digits);
475 #ifdef DEBUG
476 printf("digits %6.3f im->magfact %6.3f\n",digits,im->magfact);
477 #endif
479 if ( ((digits+symbcenter) < sizeof(symbol)) &&
480 ((digits+symbcenter) >= 0) )
481 im->symbol = symbol[(int)digits+symbcenter];
482 else
483 im->symbol = ' ';
484 }
486 /* move min and max values around to become sensible */
488 void
489 expand_range(image_desc_t *im)
490 {
491 double sensiblevalues[] ={1000.0,900.0,800.0,750.0,700.0,
492 600.0,500.0,400.0,300.0,250.0,
493 200.0,125.0,100.0,90.0,80.0,
494 75.0,70.0,60.0,50.0,40.0,30.0,
495 25.0,20.0,10.0,9.0,8.0,
496 7.0,6.0,5.0,4.0,3.5,3.0,
497 2.5,2.0,1.8,1.5,1.2,1.0,
498 0.8,0.7,0.6,0.5,0.4,0.3,0.2,0.1,0.0,-1};
500 double scaled_min,scaled_max;
501 double adj;
502 int i;
506 #ifdef DEBUG
507 printf("Min: %6.2f Max: %6.2f MagFactor: %6.2f\n",
508 im->minval,im->maxval,im->magfact);
509 #endif
511 if (isnan(im->ygridstep)){
512 if(im->extra_flags & ALTAUTOSCALE) {
513 /* measure the amplitude of the function. Make sure that
514 graph boundaries are slightly higher then max/min vals
515 so we can see amplitude on the graph */
516 double delt, fact;
518 delt = im->maxval - im->minval;
519 adj = delt * 0.1;
520 fact = 2.0 * pow(10.0,
521 floor(log10(max(fabs(im->minval), fabs(im->maxval)))) - 2);
522 if (delt < fact) {
523 adj = (fact - delt) * 0.55;
524 #ifdef DEBUG
525 printf("Min: %6.2f Max: %6.2f delt: %6.2f fact: %6.2f adj: %6.2f\n", im->minval, im->maxval, delt, fact, adj);
526 #endif
527 }
528 im->minval -= adj;
529 im->maxval += adj;
530 }
531 else if(im->extra_flags & ALTAUTOSCALE_MAX) {
532 /* measure the amplitude of the function. Make sure that
533 graph boundaries are slightly higher than max vals
534 so we can see amplitude on the graph */
535 adj = (im->maxval - im->minval) * 0.1;
536 im->maxval += adj;
537 }
538 else {
539 scaled_min = im->minval / im->magfact;
540 scaled_max = im->maxval / im->magfact;
542 for (i=1; sensiblevalues[i] > 0; i++){
543 if (sensiblevalues[i-1]>=scaled_min &&
544 sensiblevalues[i]<=scaled_min)
545 im->minval = sensiblevalues[i]*(im->magfact);
547 if (-sensiblevalues[i-1]<=scaled_min &&
548 -sensiblevalues[i]>=scaled_min)
549 im->minval = -sensiblevalues[i-1]*(im->magfact);
551 if (sensiblevalues[i-1] >= scaled_max &&
552 sensiblevalues[i] <= scaled_max)
553 im->maxval = sensiblevalues[i-1]*(im->magfact);
555 if (-sensiblevalues[i-1]<=scaled_max &&
556 -sensiblevalues[i] >=scaled_max)
557 im->maxval = -sensiblevalues[i]*(im->magfact);
558 }
559 }
560 } else {
561 /* adjust min and max to the grid definition if there is one */
562 im->minval = (double)im->ylabfact * im->ygridstep *
563 floor(im->minval / ((double)im->ylabfact * im->ygridstep));
564 im->maxval = (double)im->ylabfact * im->ygridstep *
565 ceil(im->maxval /( (double)im->ylabfact * im->ygridstep));
566 }
568 #ifdef DEBUG
569 fprintf(stderr,"SCALED Min: %6.2f Max: %6.2f Factor: %6.2f\n",
570 im->minval,im->maxval,im->magfact);
571 #endif
572 }
575 /* reduce data reimplementation by Alex */
577 void
578 reduce_data(
579 enum cf_en cf, /* which consolidation function ?*/
580 unsigned long cur_step, /* step the data currently is in */
581 time_t *start, /* start, end and step as requested ... */
582 time_t *end, /* ... by the application will be ... */
583 unsigned long *step, /* ... adjusted to represent reality */
584 unsigned long *ds_cnt, /* number of data sources in file */
585 rrd_value_t **data) /* two dimensional array containing the data */
586 {
587 int i,reduce_factor = ceil((double)(*step) / (double)cur_step);
588 unsigned long col,dst_row,row_cnt,start_offset,end_offset,skiprows=0;
589 rrd_value_t *srcptr,*dstptr;
591 (*step) = cur_step*reduce_factor; /* set new step size for reduced data */
592 dstptr = *data;
593 srcptr = *data;
595 /* We were given one extra row at the beginning of the interval.
596 ** We also need to return one extra row. The extra interval is
597 ** the one defined by the start time in both cases. It is not
598 ** used when graphing but maybe we can use it while reducing the
599 ** data.
600 */
601 row_cnt = ((*end)-(*start))/cur_step +1;
603 /* alter start and end so that they are multiples of the new steptime.
604 ** End will be shifted towards the future and start will be shifted
605 ** towards the past in order to include the requested interval
606 */
607 end_offset = (*end) % (*step);
608 if (end_offset) end_offset = (*step)-end_offset;
609 start_offset = (*start) % (*step);
610 (*end) = (*end)+end_offset;
611 (*start) = (*start)-start_offset;
613 /* The first destination row is unknown yet it still needs
614 ** to be present in the returned data. Skip it.
615 ** Don't make it NaN or we might overwrite the source.
616 */
617 dstptr += (*ds_cnt);
619 /* Depending on the amount of extra data needed at the
620 ** start of the destination, three things can happen:
621 ** -1- start_offset == 0: skip the extra source row
622 ** -2- start_offset == cur_step: do nothing
623 ** -3- start_offset > cur_step: skip some source rows and
624 ** fill one destination row with NaN
625 */
626 if (start_offset==0) {
627 srcptr+=(*ds_cnt);
628 row_cnt--;
629 } else if (start_offset!=cur_step) {
630 skiprows=((*step)-start_offset)/cur_step+1;
631 srcptr += ((*ds_cnt)*skiprows);
632 row_cnt-=skiprows;
633 for (col=0;col<(*ds_cnt);col++) *dstptr++=DNAN;
634 }
636 /* If we had to alter the endtime, there won't be
637 ** enough data to fill the last row. This means
638 ** we have to skip some rows at the end
639 */
640 if (end_offset) {
641 skiprows = ((*step)-end_offset)/cur_step;
642 row_cnt-=skiprows;
643 }
646 /* Sanity check: row_cnt should be multiple of reduce_factor */
647 /* if this gets triggered, something is REALY WRONG ... we die immediately */
649 if (row_cnt%reduce_factor) {
650 printf("SANITY CHECK: %lu rows cannot be reduced by %i \n",
651 row_cnt,reduce_factor);
652 printf("BUG in reduce_data()\n");
653 exit(1);
654 }
656 /* Now combine reduce_factor intervals at a time
657 ** into one interval for the destination.
658 */
660 for (dst_row=0;row_cnt>=reduce_factor;dst_row++) {
661 for (col=0;col<(*ds_cnt);col++) {
662 rrd_value_t newval=DNAN;
663 unsigned long validval=0;
665 for (i=0;i<reduce_factor;i++) {
666 if (isnan(srcptr[i*(*ds_cnt)+col])) {
667 continue;
668 }
669 validval++;
670 if (isnan(newval)) newval = srcptr[i*(*ds_cnt)+col];
671 else {
672 switch (cf) {
673 case CF_HWPREDICT:
674 case CF_DEVSEASONAL:
675 case CF_DEVPREDICT:
676 case CF_SEASONAL:
677 case CF_AVERAGE:
678 newval += srcptr[i*(*ds_cnt)+col];
679 break;
680 case CF_MINIMUM:
681 newval = min (newval,srcptr[i*(*ds_cnt)+col]);
682 break;
683 case CF_FAILURES:
684 /* an interval contains a failure if any subintervals contained a failure */
685 case CF_MAXIMUM:
686 newval = max (newval,srcptr[i*(*ds_cnt)+col]);
687 break;
688 case CF_LAST:
689 newval = srcptr[i*(*ds_cnt)+col];
690 break;
691 }
692 }
693 }
694 if (validval == 0){newval = DNAN;} else{
695 switch (cf) {
696 case CF_HWPREDICT:
697 case CF_DEVSEASONAL:
698 case CF_DEVPREDICT:
699 case CF_SEASONAL:
700 case CF_AVERAGE:
701 newval /= validval;
702 break;
703 case CF_MINIMUM:
704 case CF_FAILURES:
705 case CF_MAXIMUM:
706 case CF_LAST:
707 break;
708 }
709 }
710 *dstptr++=newval;
711 }
712 srcptr+=(*ds_cnt)*reduce_factor;
713 row_cnt-=reduce_factor;
714 }
716 /* If we had to alter the endtime, we didn't have enough
717 ** source rows to fill the last row. Fill it with NaN.
718 */
719 if (end_offset!=0) for (col=0;col<(*ds_cnt);col++) *dstptr++ = DNAN;
720 }
723 /* get the data required for the graphs from the
724 relevant rrds ... */
726 int
727 data_fetch( image_desc_t *im )
728 {
729 int i,ii;
730 int skip;
731 /* pull the data from the log files ... */
732 for (i=0;i<im->gdes_c;i++){
733 /* only GF_DEF elements fetch data */
734 if (im->gdes[i].gf != GF_DEF)
735 continue;
737 skip=0;
738 /* do we have it already ?*/
739 for (ii=0;ii<i;ii++){
740 if (im->gdes[ii].gf != GF_DEF)
741 continue;
742 if((strcmp(im->gdes[i].rrd,im->gdes[ii].rrd) == 0)
743 && (im->gdes[i].cf == im->gdes[ii].cf)){
744 /* OK the data it is here already ...
745 * we just copy the header portion */
746 im->gdes[i].start = im->gdes[ii].start;
747 im->gdes[i].end = im->gdes[ii].end;
748 im->gdes[i].step = im->gdes[ii].step;
749 im->gdes[i].ds_cnt = im->gdes[ii].ds_cnt;
750 im->gdes[i].ds_namv = im->gdes[ii].ds_namv;
751 im->gdes[i].data = im->gdes[ii].data;
752 im->gdes[i].data_first = 0;
753 skip=1;
754 }
755 if (skip)
756 break;
757 }
758 if (! skip) {
759 unsigned long ft_step = im->gdes[i].step ;
761 if((rrd_fetch_fn(im->gdes[i].rrd,
762 im->gdes[i].cf,
763 &im->gdes[i].start,
764 &im->gdes[i].end,
765 &ft_step,
766 &im->gdes[i].ds_cnt,
767 &im->gdes[i].ds_namv,
768 &im->gdes[i].data)) == -1){
769 return -1;
770 }
771 im->gdes[i].data_first = 1;
773 if (ft_step < im->gdes[i].step) {
774 reduce_data(im->gdes[i].cf,
775 ft_step,
776 &im->gdes[i].start,
777 &im->gdes[i].end,
778 &im->gdes[i].step,
779 &im->gdes[i].ds_cnt,
780 &im->gdes[i].data);
781 } else {
782 im->gdes[i].step = ft_step;
783 }
784 }
786 /* lets see if the required data source is realy there */
787 for(ii=0;ii<im->gdes[i].ds_cnt;ii++){
788 if(strcmp(im->gdes[i].ds_namv[ii],im->gdes[i].ds_nam) == 0){
789 im->gdes[i].ds=ii; }
790 }
791 if (im->gdes[i].ds== -1){
792 rrd_set_error("No DS called '%s' in '%s'",
793 im->gdes[i].ds_nam,im->gdes[i].rrd);
794 return -1;
795 }
797 }
798 return 0;
799 }
801 /* evaluate the expressions in the CDEF functions */
803 /*************************************************************
804 * CDEF stuff
805 *************************************************************/
807 long
808 find_var_wrapper(void *arg1, char *key)
809 {
810 return find_var((image_desc_t *) arg1, key);
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 }
844 /* run the rpn calculator on all the CDEF arguments */
845 int
846 data_calc( image_desc_t *im){
848 int gdi;
849 int dataidx;
850 long *steparray, rpi;
851 int stepcnt;
852 time_t now;
853 rpnstack_t rpnstack;
855 rpnstack_init(&rpnstack);
857 for (gdi=0;gdi<im->gdes_c;gdi++){
858 /* only GF_CDEF elements are of interest */
859 if (im->gdes[gdi].gf != GF_CDEF)
860 continue;
861 im->gdes[gdi].ds_cnt = 1;
862 im->gdes[gdi].ds = 0;
863 im->gdes[gdi].data_first = 1;
864 im->gdes[gdi].start = 0;
865 im->gdes[gdi].end = 0;
866 steparray=NULL;
867 stepcnt = 0;
868 dataidx=-1;
870 /* find the variables in the expression. And calc the lowest
871 common denominator of all step sizes of the data sources involved.
872 this will be the step size for the cdef created data source*/
874 for(rpi=0;im->gdes[gdi].rpnp[rpi].op != OP_END;rpi++){
875 if(im->gdes[gdi].rpnp[rpi].op == OP_VARIABLE){
876 long ptr = im->gdes[gdi].rpnp[rpi].ptr;
877 if ((steparray = rrd_realloc(steparray, (++stepcnt+1)*sizeof(*steparray)))==NULL){
878 rrd_set_error("realloc steparray");
879 rpnstack_free(&rpnstack);
880 return -1;
881 };
884 steparray[stepcnt-1] = im->gdes[ptr].step;
886 /* adjust start and end of cdef (gdi) so that it runs from
887 the latest start point to the earliest endpoint of any of the
888 rras involved (ptr) */
890 if(im->gdes[gdi].start < im->gdes[ptr].start)
891 im->gdes[gdi].start = im->gdes[ptr].start;
893 if(im->gdes[gdi].end == 0
894 || im->gdes[gdi].end > im->gdes[ptr].end)
895 im->gdes[gdi].end = im->gdes[ptr].end;
897 /* store pointer to the first element of the rra providing
898 data for variable, further save step size and data source count
899 of this rra*/
900 im->gdes[gdi].rpnp[rpi].data =
901 im->gdes[ptr].data + im->gdes[ptr].ds;
902 im->gdes[gdi].rpnp[rpi].step = im->gdes[ptr].step;
903 im->gdes[gdi].rpnp[rpi].ds_cnt = im->gdes[ptr].ds_cnt;
904 /* backoff the *.data ptr; this is done so rpncalc() function
905 * doesn't have to treat the first case differently */
906 im->gdes[gdi].rpnp[rpi].data -= im->gdes[ptr].ds_cnt;
907 }
909 }
910 if(steparray == NULL){
911 rrd_set_error("rpn expressions without variables are not supported");
912 rpnstack_free(&rpnstack);
913 return -1;
914 }
915 steparray[stepcnt]=0;
916 /* now find the step for the result of the cdef. so that we land on
917 each step in all of the variables rras */
919 im->gdes[gdi].step = lcd(steparray);
922 free(steparray);
924 if((im->gdes[gdi].data = malloc(((im->gdes[gdi].end
925 -im->gdes[gdi].start)
926 / im->gdes[gdi].step +1)
927 * sizeof(double)))==NULL){
928 rrd_set_error("malloc im->gdes[gdi].data");
929 rpnstack_free(&rpnstack);
930 return -1;
931 }
933 /* step through the new cdef results array and calculate the values */
934 for (now = im->gdes[gdi].start;
935 now<=im->gdes[gdi].end;
936 now += im->gdes[gdi].step){
937 rpnp_t *rpnp = im -> gdes[gdi].rpnp;
939 /* 3rd arg of rpn_calc is for OP_VARIABLE lookups;
940 * in this case we are advancing by timesteps;
941 * we use the fact that time_t is a synonym for long
942 */
943 if (rpn_calc(rpnp,&rpnstack,(long) now,
944 im->gdes[gdi].data,++dataidx) == -1)
945 {
946 /* rpn_calc sets the error string */
947 rpnstack_free(&rpnstack);
948 return -1;
949 }
951 } /* enumerate over time steps within a CDEF */
952 } /* enumerate over CDEFs */
953 rpnstack_free(&rpnstack);
954 return 0;
955 }
957 /* massage data so, that we get one value for each x coordinate in the graph */
958 int
959 data_proc( image_desc_t *im ){
960 long i,ii;
961 double pixstep = (double)(im->end-im->start)
962 /(double)im->xsize; /* how much time
963 passes in one pixel */
964 double paintval;
965 double minval=DNAN,maxval=DNAN;
967 unsigned long gr_time;
969 /* memory for the processed data */
970 for(i=0;i<im->gdes_c;i++){
971 if((im->gdes[i].gf==GF_LINE1) ||
972 (im->gdes[i].gf==GF_LINE2) ||
973 (im->gdes[i].gf==GF_LINE3) ||
974 (im->gdes[i].gf==GF_AREA) ||
975 (im->gdes[i].gf==GF_TICK) ||
976 (im->gdes[i].gf==GF_STACK)){
977 if((im->gdes[i].p_data = malloc((im->xsize +1)
978 * sizeof(rrd_value_t)))==NULL){
979 rrd_set_error("malloc data_proc");
980 return -1;
981 }
982 }
983 }
985 for(i=0;i<im->xsize;i++){
986 long vidx;
987 gr_time = im->start+pixstep*i; /* time of the
988 current step */
989 paintval=0.0;
991 for(ii=0;ii<im->gdes_c;ii++){
992 double value;
993 switch(im->gdes[ii].gf){
994 case GF_LINE1:
995 case GF_LINE2:
996 case GF_LINE3:
997 case GF_AREA:
998 case GF_TICK:
999 paintval = 0.0;
1000 case GF_STACK:
1001 vidx = im->gdes[ii].vidx;
1003 value =
1004 im->gdes[vidx].data[
1005 ((unsigned long)floor((double)
1006 (gr_time - im->gdes[vidx].start )
1007 / im->gdes[vidx].step)+1)
1009 /* added one because data was not being aligned properly
1010 this fixes it. We may also be having a problem in fetch ... */
1012 *im->gdes[vidx].ds_cnt
1013 +im->gdes[vidx].ds];
1015 if (! isnan(value)) {
1016 paintval += value;
1017 im->gdes[ii].p_data[i] = paintval;
1018 /* GF_TICK: the data values are not relevant for min and max */
1019 if (finite(paintval) && im->gdes[ii].gf != GF_TICK ){
1020 if (isnan(minval) || paintval < minval)
1021 minval = paintval;
1022 if (isnan(maxval) || paintval > maxval)
1023 maxval = paintval;
1024 }
1025 } else {
1026 im->gdes[ii].p_data[i] = DNAN;
1027 }
1028 break;
1029 case GF_PRINT:
1030 case GF_GPRINT:
1031 case GF_COMMENT:
1032 case GF_HRULE:
1033 case GF_VRULE:
1034 case GF_DEF:
1035 case GF_CDEF:
1036 break;
1037 }
1038 }
1039 }
1041 /* if min or max have not been asigned a value this is because
1042 there was no data in the graph ... this is not good ...
1043 lets set these to dummy values then ... */
1045 if (isnan(minval)) minval = 0.0;
1046 if (isnan(maxval)) maxval = 1.0;
1048 /* adjust min and max values */
1049 if (isnan(im->minval)
1050 || ((!im->logarithmic && !im->rigid) /* don't adjust low-end with log scale */
1051 && im->minval > minval))
1052 im->minval = minval;
1053 if (isnan(im->maxval)
1054 || (!im->rigid
1055 && im->maxval < maxval)){
1056 if (im->logarithmic)
1057 im->maxval = maxval * 1.1;
1058 else
1059 im->maxval = maxval;
1060 }
1061 /* make sure min and max are not equal */
1062 if (im->minval == im->maxval) {
1063 im->maxval *= 1.01;
1064 if (! im->logarithmic) {
1065 im->minval *= 0.99;
1066 }
1068 /* make sure min and max are not both zero */
1069 if (im->maxval == 0.0) {
1070 im->maxval = 1.0;
1071 }
1073 }
1074 return 0;
1075 }
1079 /* identify the point where the first gridline, label ... gets placed */
1081 time_t
1082 find_first_time(
1083 time_t start, /* what is the initial time */
1084 enum tmt_en baseint, /* what is the basic interval */
1085 long basestep /* how many if these do we jump a time */
1086 )
1087 {
1088 struct tm tm;
1089 tm = *localtime(&start);
1090 switch(baseint){
1091 case TMT_SECOND:
1092 tm.tm_sec -= tm.tm_sec % basestep; break;
1093 case TMT_MINUTE:
1094 tm.tm_sec=0;
1095 tm.tm_min -= tm.tm_min % basestep;
1096 break;
1097 case TMT_HOUR:
1098 tm.tm_sec=0;
1099 tm.tm_min = 0;
1100 tm.tm_hour -= tm.tm_hour % basestep; break;
1101 case TMT_DAY:
1102 /* we do NOT look at the basestep for this ... */
1103 tm.tm_sec=0;
1104 tm.tm_min = 0;
1105 tm.tm_hour = 0; break;
1106 case TMT_WEEK:
1107 /* we do NOT look at the basestep for this ... */
1108 tm.tm_sec=0;
1109 tm.tm_min = 0;
1110 tm.tm_hour = 0;
1111 tm.tm_mday -= tm.tm_wday -1; /* -1 because we want the monday */
1112 if (tm.tm_wday==0) tm.tm_mday -= 7; /* we want the *previous* monday */
1113 break;
1114 case TMT_MONTH:
1115 tm.tm_sec=0;
1116 tm.tm_min = 0;
1117 tm.tm_hour = 0;
1118 tm.tm_mday = 1;
1119 tm.tm_mon -= tm.tm_mon % basestep; break;
1121 case TMT_YEAR:
1122 tm.tm_sec=0;
1123 tm.tm_min = 0;
1124 tm.tm_hour = 0;
1125 tm.tm_mday = 1;
1126 tm.tm_mon = 0;
1127 tm.tm_year -= (tm.tm_year+1900) % basestep;
1129 }
1130 return mktime(&tm);
1131 }
1132 /* identify the point where the next gridline, label ... gets placed */
1133 time_t
1134 find_next_time(
1135 time_t current, /* what is the initial time */
1136 enum tmt_en baseint, /* what is the basic interval */
1137 long basestep /* how many if these do we jump a time */
1138 )
1139 {
1140 struct tm tm;
1141 time_t madetime;
1142 tm = *localtime(¤t);
1143 do {
1144 switch(baseint){
1145 case TMT_SECOND:
1146 tm.tm_sec += basestep; break;
1147 case TMT_MINUTE:
1148 tm.tm_min += basestep; break;
1149 case TMT_HOUR:
1150 tm.tm_hour += basestep; break;
1151 case TMT_DAY:
1152 tm.tm_mday += basestep; break;
1153 case TMT_WEEK:
1154 tm.tm_mday += 7*basestep; break;
1155 case TMT_MONTH:
1156 tm.tm_mon += basestep; break;
1157 case TMT_YEAR:
1158 tm.tm_year += basestep;
1159 }
1160 madetime = mktime(&tm);
1161 } while (madetime == -1); /* this is necessary to skip impssible times
1162 like the daylight saving time skips */
1163 return madetime;
1165 }
1167 void gator( gdImagePtr gif, int x, int y){
1169 /* this function puts the name of the author and the tool into the
1170 graph. Remove if you must, but please note, that it is here,
1171 because I would like people who look at rrdtool generated graphs to
1172 see what was used to do it. No obviously you can also add a credit
1173 line to your webpage or printed document, this is fine with me. But
1174 as I have no control over this, I added the little tag in here.
1175 */
1177 /* the fact that the text of what gets put into the graph is not
1178 visible in the function, has lead some to think this is for
1179 obfuscation reasons. While this is a nice side effect (I addmit),
1180 it is not the prime reason. The prime reason is, that the font
1181 used, is so small, that I had to hand edit the characters to ensure
1182 readability. I could thus not use the normal gd functions to write,
1183 but had to embed a slightly compressed bitmap version into the code.
1184 */
1186 int li[]={0,0,1, 0,4,5, 0,8,9, 0,12,14, 0,17,17, 0,21,21,
1187 0,24,24, 0,34,34, 0,40,42, 0,45,45, 0,48,49, 0,52,54,
1188 0,61,61, 0,64,66, 0,68,70, 0,72,74, 0,76,76, 0,78,78,
1189 0,80,82, 0,84,85,
1190 1,0,0, 1,2,2, 1,4,4, 1,6,6, 1,8,8, 1,10,10,
1191 1,13,13, 1,16,16, 1,18,18, 1,20,20, 1,22,22, 1,24,24,
1192 1,34,34, 1,41,41, 1,44,44, 1,46,46, 1,48,48, 1,50,50,
1193 1,53,53, 1,60,60, 1,62,62, 1,64,64, 1,69,69, 1,73,73,
1194 1,76,76, 1,78,78, 1,80,80, 1,84,84, 1,86,86,
1195 2,0,1, 2,4,5, 2,8,8, 2,10,10, 2,13,13, 2,16,16,
1196 2,18,18, 2,20,20, 2,22,22, 2,24,24, 2,33,33, 2,41,41,
1197 2,44,44, 2,46,46, 2,48,49, 2,53,53, 2,60,60, 2,62,62,
1198 2,64,65, 2,69,69, 2,73,73, 2,76,77, 2,80,81, 2,84,85,
1199 3,0,0, 3,2,2, 3,4,4, 3,6,6, 3,8,8, 3,10,10,
1200 3,13,13, 3,16,16, 3,18,18, 3,20,20, 3,22,22, 3,24,24,
1201 3,32,32, 3,41,41, 3,44,44, 3,46,46, 3,48,48, 3,50,50,
1202 3,53,53, 3,60,60, 3,62,62, 3,64,64, 3,69,69, 3,73,73,
1203 3,76,76, 3,78,78, 3,80,80, 3,84,84, 3,86,86,
1204 4,0,0, 4,2,2, 4,4,4, 4,6,6, 4,8,9, 4,13,13,
1205 4,17,17, 4,21,21, 4,24,26, 4,32,32, 4,41,41, 4,45,45,
1206 4,48,49, 4,52,54, 4,61,61, 4,64,66, 4,69,69, 4,72,74,
1207 4,76,76, 4,78,78, 4,80,82, 4,84,84};
1208 int i,ii;
1209 for(i=0; i<DIM(li); i=i+3)
1210 for(ii=y+li[i+1]; ii<=y+li[i+2];ii++)
1211 gdImageSetPixel(gif,x-li[i],ii,graph_col[GRC_GRID].i);
1212 }
1215 /* calculate values required for PRINT and GPRINT functions */
1217 int
1218 print_calc(image_desc_t *im, char ***prdata)
1219 {
1220 long i,ii,validsteps;
1221 double printval;
1222 int graphelement = 0;
1223 long vidx;
1224 int max_ii;
1225 double magfact = -1;
1226 char *si_symb = "";
1227 char *percent_s;
1228 int prlines = 1;
1229 if (im->imginfo) prlines++;
1230 for(i=0;i<im->gdes_c;i++){
1231 switch(im->gdes[i].gf){
1232 case GF_PRINT:
1233 prlines++;
1234 if(((*prdata) = rrd_realloc((*prdata),prlines*sizeof(char *)))==NULL){
1235 rrd_set_error("realloc prdata");
1236 return 0;
1237 }
1238 case GF_GPRINT:
1239 vidx = im->gdes[i].vidx;
1240 max_ii =((im->gdes[vidx].end
1241 - im->gdes[vidx].start)
1242 /im->gdes[vidx].step
1243 *im->gdes[vidx].ds_cnt);
1244 printval = DNAN;
1245 validsteps = 0;
1246 for(ii=im->gdes[vidx].ds+im->gdes[vidx].ds_cnt;
1247 ii < max_ii+im->gdes[vidx].ds_cnt;
1248 ii+=im->gdes[vidx].ds_cnt){
1249 if (! finite(im->gdes[vidx].data[ii]))
1250 continue;
1251 if (isnan(printval)){
1252 printval = im->gdes[vidx].data[ii];
1253 validsteps++;
1254 continue;
1255 }
1257 switch (im->gdes[i].cf){
1258 case CF_HWPREDICT:
1259 case CF_DEVPREDICT:
1260 case CF_DEVSEASONAL:
1261 case CF_SEASONAL:
1262 case CF_AVERAGE:
1263 validsteps++;
1264 printval += im->gdes[vidx].data[ii];
1265 break;
1266 case CF_MINIMUM:
1267 printval = min( printval, im->gdes[vidx].data[ii]);
1268 break;
1269 case CF_FAILURES:
1270 case CF_MAXIMUM:
1271 printval = max( printval, im->gdes[vidx].data[ii]);
1272 break;
1273 case CF_LAST:
1274 printval = im->gdes[vidx].data[ii];
1275 }
1276 }
1277 if (im->gdes[i].cf == CF_AVERAGE || im -> gdes[i].cf > CF_LAST) {
1278 if (validsteps > 1) {
1279 printval = (printval / validsteps);
1280 }
1281 }
1282 if ((percent_s = strstr(im->gdes[i].format,"%S")) != NULL) {
1283 /* Magfact is set to -1 upon entry to print_calc. If it
1284 * is still less than 0, then we need to run auto_scale.
1285 * Otherwise, put the value into the correct units. If
1286 * the value is 0, then do not set the symbol or magnification
1287 * so next the calculation will be performed again. */
1288 if (magfact < 0.0) {
1289 auto_scale(im,&printval,&si_symb,&magfact);
1290 if (printval == 0.0)
1291 magfact = -1.0;
1292 } else {
1293 printval /= magfact;
1294 }
1295 *(++percent_s) = 's';
1296 }
1297 else if (strstr(im->gdes[i].format,"%s") != NULL) {
1298 auto_scale(im,&printval,&si_symb,&magfact);
1299 }
1300 if (im->gdes[i].gf == GF_PRINT){
1301 (*prdata)[prlines-2] = malloc((FMT_LEG_LEN+2)*sizeof(char));
1302 if (bad_format(im->gdes[i].format)) {
1303 rrd_set_error("bad format for [G]PRINT in '%s'", im->gdes[i].format);
1304 return -1;
1305 }
1306 #ifdef HAVE_SNPRINTF
1307 snprintf((*prdata)[prlines-2],FMT_LEG_LEN,im->gdes[i].format,printval,si_symb);
1308 #else
1309 sprintf((*prdata)[prlines-2],im->gdes[i].format,printval,si_symb);
1310 #endif
1311 (*prdata)[prlines-1] = NULL;
1312 } else {
1313 /* GF_GPRINT */
1315 if (bad_format(im->gdes[i].format)) {
1316 rrd_set_error("bad format for [G]PRINT in '%s'", im->gdes[i].format);
1317 return -1;
1318 }
1319 #ifdef HAVE_SNPRINTF
1320 snprintf(im->gdes[i].legend,FMT_LEG_LEN-2,im->gdes[i].format,printval,si_symb);
1321 #else
1322 sprintf(im->gdes[i].legend,im->gdes[i].format,printval,si_symb);
1323 #endif
1324 graphelement = 1;
1325 }
1326 break;
1327 case GF_COMMENT:
1328 case GF_LINE1:
1329 case GF_LINE2:
1330 case GF_LINE3:
1331 case GF_AREA:
1332 case GF_TICK:
1333 case GF_STACK:
1334 case GF_HRULE:
1335 case GF_VRULE:
1336 graphelement = 1;
1337 break;
1338 case GF_DEF:
1339 case GF_CDEF:
1340 break;
1341 }
1342 }
1343 return graphelement;
1344 }
1347 /* place legends with color spots */
1348 int
1349 leg_place(image_desc_t *im)
1350 {
1351 /* graph labels */
1352 int interleg = SmallFont->w*2;
1353 int box = SmallFont->h*1.2;
1354 int border = SmallFont->w*2;
1355 int fill=0, fill_last;
1356 int leg_c = 0;
1357 int leg_x = border, leg_y = im->ygif;
1358 int leg_cc;
1359 int glue = 0;
1360 int i,ii, mark = 0;
1361 char prt_fctn; /*special printfunctions */
1362 int *legspace;
1364 if( !(im->extra_flags & NOLEGEND) ) {
1365 if ((legspace = malloc(im->gdes_c*sizeof(int)))==NULL){
1366 rrd_set_error("malloc for legspace");
1367 return -1;
1368 }
1370 for(i=0;i<im->gdes_c;i++){
1371 fill_last = fill;
1373 leg_cc = strlen(im->gdes[i].legend);
1375 /* is there a controle code ant the end of the legend string ? */
1376 if (leg_cc >= 2 && im->gdes[i].legend[leg_cc-2] == '\\') {
1377 prt_fctn = im->gdes[i].legend[leg_cc-1];
1378 leg_cc -= 2;
1379 im->gdes[i].legend[leg_cc] = '\0';
1380 } else {
1381 prt_fctn = '\0';
1382 }
1383 /* remove exess space */
1384 while (prt_fctn=='g' &&
1385 leg_cc > 0 &&
1386 im->gdes[i].legend[leg_cc-1]==' '){
1387 leg_cc--;
1388 im->gdes[i].legend[leg_cc]='\0';
1389 }
1390 if (leg_cc != 0 ){
1391 legspace[i]=(prt_fctn=='g' ? 0 : interleg);
1393 if (fill > 0){
1394 /* no interleg space if string ends in \g */
1395 fill += legspace[i];
1396 }
1397 if (im->gdes[i].gf != GF_GPRINT &&
1398 im->gdes[i].gf != GF_COMMENT) {
1399 fill += box;
1400 }
1401 fill += leg_cc * SmallFont->w;
1402 leg_c++;
1403 } else {
1404 legspace[i]=0;
1405 }
1406 /* who said there was a special tag ... ?*/
1407 if (prt_fctn=='g') {
1408 prt_fctn = '\0';
1409 }
1410 if (prt_fctn == '\0') {
1411 if (i == im->gdes_c -1 ) prt_fctn ='l';
1413 /* is it time to place the legends ? */
1414 if (fill > im->xgif - 2*border){
1415 if (leg_c > 1) {
1416 /* go back one */
1417 i--;
1418 fill = fill_last;
1419 leg_c--;
1420 prt_fctn = 'j';
1421 } else {
1422 prt_fctn = 'l';
1423 }
1425 }
1426 }
1429 if (prt_fctn != '\0'){
1430 leg_x = border;
1431 if (leg_c >= 2 && prt_fctn == 'j') {
1432 glue = (im->xgif - fill - 2* border) / (leg_c-1);
1433 /* if (glue > 2 * SmallFont->w) glue = 0; */
1434 } else {
1435 glue = 0;
1436 }
1437 if (prt_fctn =='c') leg_x = (im->xgif - fill) / 2.0;
1438 if (prt_fctn =='r') leg_x = im->xgif - fill - border;
1440 for(ii=mark;ii<=i;ii++){
1441 if(im->gdes[ii].legend[0]=='\0')
1442 continue;
1443 im->gdes[ii].legloc.x = leg_x;
1444 im->gdes[ii].legloc.y = leg_y;
1445 leg_x = leg_x
1446 + strlen(im->gdes[ii].legend)*SmallFont->w
1447 + legspace[ii]
1448 + glue;
1449 if (im->gdes[ii].gf != GF_GPRINT &&
1450 im->gdes[ii].gf != GF_COMMENT)
1451 leg_x += box;
1452 }
1453 leg_y = leg_y + SmallFont->h*1.2;
1454 if (prt_fctn == 's') leg_y -= SmallFont->h *0.5;
1455 fill = 0;
1456 leg_c = 0;
1457 mark = ii;
1458 }
1459 }
1460 im->ygif = leg_y+6;
1461 free(legspace);
1462 }
1463 return 0;
1464 }
1466 /* create a grid on the graph. it determines what to do
1467 from the values of xsize, start and end */
1469 /* the xaxis labels are determined from the number of seconds per pixel
1470 in the requested graph */
1474 int
1475 horizontal_grid(gdImagePtr gif, image_desc_t *im)
1476 {
1477 double range;
1478 double scaledrange;
1479 int pixel,i;
1480 int sgrid,egrid;
1481 double gridstep;
1482 double scaledstep;
1483 char graph_label[100];
1484 gdPoint polyPoints[4];
1485 int labfact,gridind;
1486 int styleMinor[2],styleMajor[2];
1487 int decimals, fractionals;
1488 char labfmt[64];
1490 labfact=2;
1491 gridind=-1;
1492 range = im->maxval - im->minval;
1493 scaledrange = range / im->magfact;
1495 /* does the scale of this graph make it impossible to put lines
1496 on it? If so, give up. */
1497 if (isnan(scaledrange)) {
1498 return 0;
1499 }
1501 styleMinor[0] = graph_col[GRC_GRID].i;
1502 styleMinor[1] = gdTransparent;
1504 styleMajor[0] = graph_col[GRC_MGRID].i;
1505 styleMajor[1] = gdTransparent;
1507 /* find grid spaceing */
1508 pixel=1;
1509 if(isnan(im->ygridstep)){
1510 if(im->extra_flags & ALTYGRID) {
1511 /* find the value with max number of digits. Get number of digits */
1512 decimals = ceil(log10(max(fabs(im->maxval), fabs(im->minval))));
1513 if(decimals <= 0) /* everything is small. make place for zero */
1514 decimals = 1;
1516 fractionals = floor(log10(range));
1517 if(fractionals < 0) /* small amplitude. */
1518 sprintf(labfmt, "%%%d.%df", decimals - fractionals + 1, -fractionals + 1);
1519 else
1520 sprintf(labfmt, "%%%d.1f", decimals + 1);
1521 gridstep = pow((double)10, (double)fractionals);
1522 if(gridstep == 0) /* range is one -> 0.1 is reasonable scale */
1523 gridstep = 0.1;
1524 /* should have at least 5 lines but no more then 15 */
1525 if(range/gridstep < 5)
1526 gridstep /= 10;
1527 if(range/gridstep > 15)
1528 gridstep *= 10;
1529 if(range/gridstep > 5) {
1530 labfact = 1;
1531 if(range/gridstep > 8)
1532 labfact = 2;
1533 }
1534 else {
1535 gridstep /= 5;
1536 labfact = 5;
1537 }
1538 }
1539 else {
1540 for(i=0;ylab[i].grid > 0;i++){
1541 pixel = im->ysize / (scaledrange / ylab[i].grid);
1542 if (gridind == -1 && pixel > 5) {
1543 gridind = i;
1544 break;
1545 }
1546 }
1548 for(i=0; i<4;i++) {
1549 if (pixel * ylab[gridind].lfac[i] >= 2 * SmallFont->h) {
1550 labfact = ylab[gridind].lfac[i];
1551 break;
1552 }
1553 }
1555 gridstep = ylab[gridind].grid * im->magfact;
1556 }
1557 } else {
1558 gridstep = im->ygridstep;
1559 labfact = im->ylabfact;
1560 }
1562 polyPoints[0].x=im->xorigin;
1563 polyPoints[1].x=im->xorigin+im->xsize;
1564 sgrid = (int)( im->minval / gridstep - 1);
1565 egrid = (int)( im->maxval / gridstep + 1);
1566 scaledstep = gridstep/im->magfact;
1567 for (i = sgrid; i <= egrid; i++){
1568 polyPoints[0].y=ytr(im,gridstep*i);
1569 if ( polyPoints[0].y >= im->yorigin-im->ysize
1570 && polyPoints[0].y <= im->yorigin) {
1571 if(i % labfact == 0){
1572 if (i==0 || im->symbol == ' ') {
1573 if(scaledstep < 1){
1574 if(im->extra_flags & ALTYGRID) {
1575 sprintf(graph_label,labfmt,scaledstep*i);
1576 }
1577 else {
1578 sprintf(graph_label,"%4.1f",scaledstep*i);
1579 }
1580 } else {
1581 sprintf(graph_label,"%4.0f",scaledstep*i);
1582 }
1583 }else {
1584 if(scaledstep < 1){
1585 sprintf(graph_label,"%4.1f %c",scaledstep*i, im->symbol);
1586 } else {
1587 sprintf(graph_label,"%4.0f %c",scaledstep*i, im->symbol);
1588 }
1589 }
1591 gdImageString(gif, SmallFont,
1592 (polyPoints[0].x - (strlen(graph_label) *
1593 SmallFont->w)-7),
1594 polyPoints[0].y - SmallFont->h/2+1,
1595 (unsigned char *)graph_label, graph_col[GRC_FONT].i);
1597 gdImageSetStyle(gif, styleMajor, 2);
1599 gdImageLine(gif, polyPoints[0].x-2,polyPoints[0].y,
1600 polyPoints[0].x+2,polyPoints[0].y,graph_col[GRC_MGRID].i);
1601 gdImageLine(gif, polyPoints[1].x-2,polyPoints[0].y,
1602 polyPoints[1].x+2,polyPoints[0].y,graph_col[GRC_MGRID].i);
1603 } else {
1604 gdImageSetStyle(gif, styleMinor, 2);
1605 gdImageLine(gif, polyPoints[0].x-1,polyPoints[0].y,
1606 polyPoints[0].x+1,polyPoints[0].y,graph_col[GRC_GRID].i);
1607 gdImageLine(gif, polyPoints[1].x-1,polyPoints[0].y,
1608 polyPoints[1].x+1,polyPoints[0].y,graph_col[GRC_GRID].i);
1609 }
1610 gdImageLine(gif, polyPoints[0].x,polyPoints[0].y,
1611 polyPoints[1].x,polyPoints[0].y,gdStyled);
1612 }
1613 }
1614 /* if(im->minval * im->maxval < 0){
1615 polyPoints[0].y=ytr(0);
1616 gdImageLine(gif, polyPoints[0].x,polyPoints[0].y,
1617 polyPoints[1].x,polyPoints[0].y,graph_col[GRC_MGRID].i);
1618 } */
1620 return 1;
1621 }
1623 /* logaritmic horizontal grid */
1624 int
1625 horizontal_log_grid(gdImagePtr gif, image_desc_t *im)
1626 {
1627 double pixpex;
1628 int ii,i;
1629 int minoridx=0, majoridx=0;
1630 char graph_label[100];
1631 gdPoint polyPoints[4];
1632 int styleMinor[2],styleMajor[2];
1633 double value, pixperstep, minstep;
1635 /* find grid spaceing */
1636 pixpex= (double)im->ysize / (log10(im->maxval) - log10(im->minval));
1638 if (isnan(pixpex)) {
1639 return 0;
1640 }
1642 for(i=0;yloglab[i][0] > 0;i++){
1643 minstep = log10(yloglab[i][0]);
1644 for(ii=1;yloglab[i][ii+1] > 0;ii++){
1645 if(yloglab[i][ii+2]==0){
1646 minstep = log10(yloglab[i][ii+1])-log10(yloglab[i][ii]);
1647 break;
1648 }
1649 }
1650 pixperstep = pixpex * minstep;
1651 if(pixperstep > 5){minoridx = i;}
1652 if(pixperstep > 2 * SmallFont->h){majoridx = i;}
1653 }
1655 styleMinor[0] = graph_col[GRC_GRID].i;
1656 styleMinor[1] = gdTransparent;
1658 styleMajor[0] = graph_col[GRC_MGRID].i;
1659 styleMajor[1] = gdTransparent;
1661 polyPoints[0].x=im->xorigin;
1662 polyPoints[1].x=im->xorigin+im->xsize;
1663 /* paint minor grid */
1664 for (value = pow((double)10, log10(im->minval)
1665 - fmod(log10(im->minval),log10(yloglab[minoridx][0])));
1666 value <= im->maxval;
1667 value *= yloglab[minoridx][0]){
1668 if (value < im->minval) continue;
1669 i=0;
1670 while(yloglab[minoridx][++i] > 0){
1671 polyPoints[0].y = ytr(im,value * yloglab[minoridx][i]);
1672 if (polyPoints[0].y <= im->yorigin - im->ysize) break;
1673 gdImageSetStyle(gif, styleMinor, 2);
1674 gdImageLine(gif, polyPoints[0].x-1,polyPoints[0].y,
1675 polyPoints[0].x+1,polyPoints[0].y,graph_col[GRC_GRID].i);
1676 gdImageLine(gif, polyPoints[1].x-1,polyPoints[0].y,
1677 polyPoints[1].x+1,polyPoints[0].y,graph_col[GRC_GRID].i);
1679 gdImageLine(gif, polyPoints[0].x,polyPoints[0].y,
1680 polyPoints[1].x,polyPoints[0].y,gdStyled);
1681 }
1682 }
1684 /* paint major grid and labels*/
1685 for (value = pow((double)10, log10(im->minval)
1686 - fmod(log10(im->minval),log10(yloglab[majoridx][0])));
1687 value <= im->maxval;
1688 value *= yloglab[majoridx][0]){
1689 if (value < im->minval) continue;
1690 i=0;
1691 while(yloglab[majoridx][++i] > 0){
1692 polyPoints[0].y = ytr(im,value * yloglab[majoridx][i]);
1693 if (polyPoints[0].y <= im->yorigin - im->ysize) break;
1694 gdImageSetStyle(gif, styleMajor, 2);
1695 gdImageLine(gif, polyPoints[0].x-2,polyPoints[0].y,
1696 polyPoints[0].x+2,polyPoints[0].y,graph_col[GRC_MGRID].i);
1697 gdImageLine(gif, polyPoints[1].x-2,polyPoints[0].y,
1698 polyPoints[1].x+2,polyPoints[0].y,graph_col[GRC_MGRID].i);
1700 gdImageLine(gif, polyPoints[0].x,polyPoints[0].y,
1701 polyPoints[1].x,polyPoints[0].y,gdStyled);
1702 sprintf(graph_label,"%3.0e",value * yloglab[majoridx][i]);
1703 gdImageString(gif, SmallFont,
1704 (polyPoints[0].x - (strlen(graph_label) *
1705 SmallFont->w)-7),
1706 polyPoints[0].y - SmallFont->h/2+1,
1707 (unsigned char *)graph_label, graph_col[GRC_FONT].i);
1708 }
1709 }
1710 return 1;
1711 }
1714 void
1715 vertical_grid(
1716 gdImagePtr gif,
1717 image_desc_t *im )
1718 {
1719 int xlab_sel; /* which sort of label and grid ? */
1720 time_t ti, tilab;
1721 long factor;
1722 char graph_label[100];
1723 gdPoint polyPoints[4]; /* points for filled graph and more*/
1725 /* style for grid lines */
1726 int styleDotted[4];
1729 /* the type of time grid is determined by finding
1730 the number of seconds per pixel in the graph */
1733 if(im->xlab_user.minsec == -1){
1734 factor=(im->end - im->start)/im->xsize;
1735 xlab_sel=0;
1736 while ( xlab[xlab_sel+1].minsec != -1
1737 && xlab[xlab_sel+1].minsec <= factor){ xlab_sel++; }
1738 im->xlab_user.gridtm = xlab[xlab_sel].gridtm;
1739 im->xlab_user.gridst = xlab[xlab_sel].gridst;
1740 im->xlab_user.mgridtm = xlab[xlab_sel].mgridtm;
1741 im->xlab_user.mgridst = xlab[xlab_sel].mgridst;
1742 im->xlab_user.labtm = xlab[xlab_sel].labtm;
1743 im->xlab_user.labst = xlab[xlab_sel].labst;
1744 im->xlab_user.precis = xlab[xlab_sel].precis;
1745 im->xlab_user.stst = xlab[xlab_sel].stst;
1746 }
1748 /* y coords are the same for every line ... */
1749 polyPoints[0].y = im->yorigin;
1750 polyPoints[1].y = im->yorigin-im->ysize;
1752 /* paint the minor grid */
1753 for(ti = find_first_time(im->start,
1754 im->xlab_user.gridtm,
1755 im->xlab_user.gridst);
1756 ti < im->end;
1757 ti = find_next_time(ti,im->xlab_user.gridtm,im->xlab_user.gridst)
1758 ){
1759 /* are we inside the graph ? */
1760 if (ti < im->start || ti > im->end) continue;
1761 polyPoints[0].x = xtr(im,ti);
1762 styleDotted[0] = graph_col[GRC_GRID].i;
1763 styleDotted[1] = gdTransparent;
1765 gdImageSetStyle(gif, styleDotted, 2);
1767 gdImageLine(gif, polyPoints[0].x,polyPoints[0].y,
1768 polyPoints[0].x,polyPoints[1].y,gdStyled);
1769 gdImageLine(gif, polyPoints[0].x,polyPoints[0].y-1,
1770 polyPoints[0].x,polyPoints[0].y+1,graph_col[GRC_GRID].i);
1771 gdImageLine(gif, polyPoints[0].x,polyPoints[1].y-1,
1772 polyPoints[0].x,polyPoints[1].y+1,graph_col[GRC_GRID].i);
1773 }
1775 /* paint the major grid */
1776 for(ti = find_first_time(im->start,
1777 im->xlab_user.mgridtm,
1778 im->xlab_user.mgridst);
1779 ti < im->end;
1780 ti = find_next_time(ti,im->xlab_user.mgridtm,im->xlab_user.mgridst)
1781 ){
1782 /* are we inside the graph ? */
1783 if (ti < im->start || ti > im->end) continue;
1784 polyPoints[0].x = xtr(im,ti);
1785 styleDotted[0] = graph_col[GRC_MGRID].i;
1786 styleDotted[1] = gdTransparent;
1787 gdImageSetStyle(gif, styleDotted, 2);
1789 gdImageLine(gif, polyPoints[0].x,polyPoints[0].y,
1790 polyPoints[0].x,polyPoints[1].y,gdStyled);
1791 gdImageLine(gif, polyPoints[0].x,polyPoints[0].y-2,
1792 polyPoints[0].x,polyPoints[0].y+2,graph_col[GRC_MGRID].i);
1793 gdImageLine(gif, polyPoints[0].x,polyPoints[1].y-2,
1794 polyPoints[0].x,polyPoints[1].y+2,graph_col[GRC_MGRID].i);
1795 }
1796 /* paint the labels below the graph */
1797 for(ti = find_first_time(im->start,
1798 im->xlab_user.labtm,
1799 im->xlab_user.labst);
1800 ti <= im->end;
1801 ti = find_next_time(ti,im->xlab_user.labtm,im->xlab_user.labst)
1802 ){
1803 int gr_pos,width;
1804 tilab= ti + im->xlab_user.precis/2; /* correct time for the label */
1806 #if HAVE_STRFTIME
1807 strftime(graph_label,99,im->xlab_user.stst,localtime(&tilab));
1808 #else
1809 # error "your libc has no strftime I guess we'll abort the exercise here."
1810 #endif
1811 width=strlen(graph_label) * SmallFont->w;
1812 gr_pos=xtr(im,tilab) - width/2;
1813 if (gr_pos >= im->xorigin
1814 && gr_pos + width <= im->xorigin+im->xsize)
1815 gdImageString(gif, SmallFont,
1816 gr_pos, polyPoints[0].y+4,
1817 (unsigned char *)graph_label, graph_col[GRC_FONT].i);
1818 }
1820 }
1823 void
1824 axis_paint(
1825 image_desc_t *im,
1826 gdImagePtr gif
1827 )
1828 {
1829 /* draw x and y axis */
1830 gdImageLine(gif, im->xorigin+im->xsize,im->yorigin,
1831 im->xorigin+im->xsize,im->yorigin-im->ysize,
1832 graph_col[GRC_GRID].i);
1834 gdImageLine(gif, im->xorigin,im->yorigin-im->ysize,
1835 im->xorigin+im->xsize,im->yorigin-im->ysize,
1836 graph_col[GRC_GRID].i);
1838 gdImageLine(gif, im->xorigin-4,im->yorigin,
1839 im->xorigin+im->xsize+4,im->yorigin,
1840 graph_col[GRC_FONT].i);
1842 gdImageLine(gif, im->xorigin,im->yorigin,
1843 im->xorigin,im->yorigin-im->ysize,
1844 graph_col[GRC_GRID].i);
1846 /* arrow for X axis direction */
1847 gdImageLine(gif, im->xorigin+im->xsize+4, im->yorigin-3, im->xorigin+im->xsize+4, im->yorigin+3,graph_col[GRC_ARROW].i);
1848 gdImageLine(gif, im->xorigin+im->xsize+4, im->yorigin-3, im->xorigin+im->xsize+9, im->yorigin,graph_col[GRC_ARROW].i);
1849 gdImageLine(gif, im->xorigin+im->xsize+4, im->yorigin+3, im->xorigin+im->xsize+9, im->yorigin,graph_col[GRC_ARROW].i);
1851 /* gdImageLine(gif, im->xorigin+im->xsize-1, im->yorigin-3, im->xorigin+im->xsize-1, im->yorigin+3,graph_col[GRC_MGRID].i);
1852 gdImageLine(gif, im->xorigin+im->xsize, im->yorigin-2, im->xorigin+im->xsize, im->yorigin+2,graph_col[GRC_MGRID].i);
1853 gdImageLine(gif, im->xorigin+im->xsize+1, im->yorigin-2, im->xorigin+im->xsize+1, im->yorigin+2,graph_col[GRC_MGRID].i);
1854 gdImageLine(gif, im->xorigin+im->xsize+2, im->yorigin-2, im->xorigin+im->xsize+2, im->yorigin+2,graph_col[GRC_MGRID].i);
1855 gdImageLine(gif, im->xorigin+im->xsize+3, im->yorigin-1, im->xorigin+im->xsize+3, im->yorigin+1,graph_col[GRC_MGRID].i);
1856 gdImageLine(gif, im->xorigin+im->xsize+4, im->yorigin-1, im->xorigin+im->xsize+4, im->yorigin+1,graph_col[GRC_MGRID].i);
1857 gdImageLine(gif, im->xorigin+im->xsize+5, im->yorigin, im->xorigin+im->xsize+5, im->yorigin,graph_col[GRC_MGRID].i); */
1861 }
1863 void
1864 grid_paint(
1865 image_desc_t *im,
1866 gdImagePtr gif
1867 )
1868 {
1869 long i;
1870 int boxH=8, boxV=8;
1871 int res=0;
1872 gdPoint polyPoints[4]; /* points for filled graph and more*/
1874 /* draw 3d border */
1875 gdImageLine(gif,0,0,im->xgif-1,0,graph_col[GRC_SHADEA].i);
1876 gdImageLine(gif,1,1,im->xgif-2,1,graph_col[GRC_SHADEA].i);
1877 gdImageLine(gif,0,0,0,im->ygif-1,graph_col[GRC_SHADEA].i);
1878 gdImageLine(gif,1,1,1,im->ygif-2,graph_col[GRC_SHADEA].i);
1879 gdImageLine(gif,im->xgif-1,0,im->xgif-1,im->ygif-1,graph_col[GRC_SHADEB].i);
1880 gdImageLine(gif,0,im->ygif-1,im->xgif-1,im->ygif-1,graph_col[GRC_SHADEB].i);
1881 gdImageLine(gif,im->xgif-2,1,im->xgif-2,im->ygif-2,graph_col[GRC_SHADEB].i);
1882 gdImageLine(gif,1,im->ygif-2,im->xgif-2,im->ygif-2,graph_col[GRC_SHADEB].i);
1885 if (im->draw_x_grid == 1 )
1886 vertical_grid(gif, im);
1888 if (im->draw_y_grid == 1){
1889 if(im->logarithmic){
1890 res = horizontal_log_grid(gif,im);
1891 } else {
1892 res = horizontal_grid(gif,im);
1893 }
1895 /* dont draw horizontal grid if there is no min and max val */
1896 if (! res ) {
1897 char *nodata = "No Data found";
1898 gdImageString(gif, LargeFont,
1899 im->xgif/2
1900 - (strlen(nodata)*LargeFont->w)/2,
1901 (2*im->yorigin-im->ysize) / 2,
1902 (unsigned char *)nodata, graph_col[GRC_FONT].i);
1903 }
1904 }
1906 /* yaxis description */
1907 gdImageStringUp(gif, SmallFont,
1908 7,
1909 (im->yorigin - im->ysize/2
1910 +(strlen(im->ylegend)*SmallFont->w)/2 ),
1911 (unsigned char *)im->ylegend, graph_col[GRC_FONT].i);
1914 /* graph title */
1915 gdImageString(gif, LargeFont,
1916 im->xgif/2
1917 - (strlen(im->title)*LargeFont->w)/2,
1918 8,
1919 (unsigned char *)im->title, graph_col[GRC_FONT].i);
1921 /* graph labels */
1922 if( !(im->extra_flags & NOLEGEND) ) {
1923 for(i=0;i<im->gdes_c;i++){
1924 if(im->gdes[i].legend[0] =='\0')
1925 continue;
1927 if(im->gdes[i].gf != GF_GPRINT && im->gdes[i].gf != GF_COMMENT){
1929 polyPoints[0].x = im->gdes[i].legloc.x;
1930 polyPoints[0].y = im->gdes[i].legloc.y+1;
1931 polyPoints[1].x = polyPoints[0].x+boxH;
1932 polyPoints[2].x = polyPoints[0].x+boxH;
1933 polyPoints[3].x = polyPoints[0].x;
1934 polyPoints[1].y = polyPoints[0].y;
1935 polyPoints[2].y = polyPoints[0].y+boxV;
1936 polyPoints[3].y = polyPoints[0].y+boxV;
1937 gdImageFilledPolygon(gif,polyPoints,4,im->gdes[i].col.i);
1938 gdImagePolygon(gif,polyPoints,4,graph_col[GRC_FRAME].i);
1940 gdImageString(gif, SmallFont,
1941 polyPoints[0].x+boxH+6,
1942 polyPoints[0].y-1,
1943 (unsigned char *)im->gdes[i].legend,
1944 graph_col[GRC_FONT].i);
1945 } else {
1946 polyPoints[0].x = im->gdes[i].legloc.x;
1947 polyPoints[0].y = im->gdes[i].legloc.y;
1949 gdImageString(gif, SmallFont,
1950 polyPoints[0].x,
1951 polyPoints[0].y,
1952 (unsigned char *)im->gdes[i].legend,
1953 graph_col[GRC_FONT].i);
1954 }
1955 }
1956 }
1959 gator(gif, (int) im->xgif-5, 5);
1961 }
1964 gdImagePtr
1965 MkLineBrush(image_desc_t *im,long cosel, enum gf_en typsel){
1966 gdImagePtr brush;
1967 int pen;
1968 switch (typsel){
1969 case GF_LINE1:
1970 brush=gdImageCreate(1,1);
1971 break;
1972 case GF_LINE2:
1973 brush=gdImageCreate(2,2);
1974 break;
1975 case GF_LINE3:
1976 brush=gdImageCreate(3,3);
1977 break;
1978 default:
1979 return NULL;
1980 }
1982 gdImageColorTransparent(brush,
1983 gdImageColorAllocate(brush, 0, 0, 0));
1985 pen = gdImageColorAllocate(brush,
1986 im->gdes[cosel].col.red,
1987 im->gdes[cosel].col.green,
1988 im->gdes[cosel].col.blue);
1990 switch (typsel){
1991 case GF_LINE1:
1992 gdImageSetPixel(brush,0,0,pen);
1993 break;
1994 case GF_LINE2:
1995 gdImageSetPixel(brush,0,0,pen);
1996 gdImageSetPixel(brush,0,1,pen);
1997 gdImageSetPixel(brush,1,0,pen);
1998 gdImageSetPixel(brush,1,1,pen);
1999 break;
2000 case GF_LINE3:
2001 gdImageSetPixel(brush,1,0,pen);
2002 gdImageSetPixel(brush,0,1,pen);
2003 gdImageSetPixel(brush,1,1,pen);
2004 gdImageSetPixel(brush,2,1,pen);
2005 gdImageSetPixel(brush,1,2,pen);
2006 break;
2007 default:
2008 return NULL;
2009 }
2010 return brush;
2011 }
2012 /*****************************************************
2013 * lazy check make sure we rely need to create this graph
2014 *****************************************************/
2016 int lazy_check(image_desc_t *im){
2017 FILE *fd = NULL;
2018 int size = 1;
2019 struct stat gifstat;
2021 if (im->lazy == 0) return 0; /* no lazy option */
2022 if (stat(im->graphfile,&gifstat) != 0)
2023 return 0; /* can't stat */
2024 /* one pixel in the existing graph is more then what we would
2025 change here ... */
2026 if (time(NULL) - gifstat.st_mtime >
2027 (im->end - im->start) / im->xsize)
2028 return 0;
2029 if ((fd = fopen(im->graphfile,"rb")) == NULL)
2030 return 0; /* the file does not exist */
2031 switch (im->imgformat) {
2032 case IF_GIF:
2033 size = GifSize(fd,&(im->xgif),&(im->ygif));
2034 break;
2035 case IF_PNG:
2036 size = PngSize(fd,&(im->xgif),&(im->ygif));
2037 break;
2038 }
2039 fclose(fd);
2040 return size;
2041 }
2043 /* draw that picture thing ... */
2044 int
2045 graph_paint(image_desc_t *im, char ***calcpr)
2046 {
2047 int i,ii;
2048 int lazy = lazy_check(im);
2049 FILE *fo;
2051 /* gif stuff */
2052 gdImagePtr gif,brush;
2054 double areazero = 0.0;
2055 enum gf_en stack_gf = GF_PRINT;
2056 graph_desc_t *lastgdes = NULL;
2057 gdPoint canvas[4], back[4]; /* points for canvas*/
2059 /* if we are lazy and there is nothing to PRINT ... quit now */
2060 if (lazy && im->prt_c==0) return 0;
2062 /* pull the data from the rrd files ... */
2064 if(data_fetch(im)==-1)
2065 return -1;
2067 /* evaluate CDEF operations ... */
2068 if(data_calc(im)==-1)
2069 return -1;
2071 /* calculate and PRINT and GPRINT definitions. We have to do it at
2072 * this point because it will affect the length of the legends
2073 * if there are no graph elements we stop here ...
2074 * if we are lazy, try to quit ...
2075 */
2076 i=print_calc(im,calcpr);
2077 if(i<0) return -1;
2078 if(i==0 || lazy) return 0;
2080 /* get actual drawing data and find min and max values*/
2081 if(data_proc(im)==-1)
2082 return -1;
2084 if(!im->logarithmic){si_unit(im);} /* identify si magnitude Kilo, Mega Giga ? */
2086 if(!im->rigid && ! im->logarithmic)
2087 expand_range(im); /* make sure the upper and lower limit are
2088 sensible values */
2090 /* init xtr and ytr */
2091 /* determine the actual size of the gif to draw. The size given
2092 on the cmdline is the graph area. But we need more as we have
2093 draw labels and other things outside the graph area */
2096 im->xorigin = 10 + 9 * SmallFont->w+SmallFont->h;
2097 xtr(im,0);
2099 im->yorigin = 14 + im->ysize;
2100 ytr(im,DNAN);
2102 if(im->title[0] != '\0')
2103 im->yorigin += (LargeFont->h+4);
2105 im->xgif=20+im->xsize + im->xorigin;
2106 im->ygif= im->yorigin+2*SmallFont->h;
2108 /* determine where to place the legends onto the graphics.
2109 and set im->ygif to match space requirements for text */
2110 if(leg_place(im)==-1)
2111 return -1;
2113 gif=gdImageCreate(im->xgif,im->ygif);
2115 gdImageInterlace(gif, im->interlaced);
2117 /* allocate colors for the screen elements */
2118 for(i=0;i<DIM(graph_col);i++)
2119 /* check for user override values */
2120 if(im->graph_col[i].red != -1)
2121 graph_col[i].i =
2122 gdImageColorAllocate( gif,
2123 im->graph_col[i].red,
2124 im->graph_col[i].green,
2125 im->graph_col[i].blue);
2126 else
2127 graph_col[i].i =
2128 gdImageColorAllocate( gif,
2129 graph_col[i].red,
2130 graph_col[i].green,
2131 graph_col[i].blue);
2134 /* allocate colors for the graph */
2135 for(i=0;i<im->gdes_c;i++)
2136 /* only for elements which have a color defined */
2137 if (im->gdes[i].col.red != -1)
2138 im->gdes[i].col.i =
2139 gdImageColorAllocate(gif,
2140 im->gdes[i].col.red,
2141 im->gdes[i].col.green,
2142 im->gdes[i].col.blue);
2145 /* the actual graph is created by going through the individual
2146 graph elements and then drawing them */
2148 back[0].x = 0;
2149 back[0].y = 0;
2150 back[1].x = back[0].x+im->xgif;
2151 back[1].y = back[0].y;
2152 back[2].x = back[1].x;
2153 back[2].y = back[0].y+im->ygif;
2154 back[3].x = back[0].x;
2155 back[3].y = back[2].y;
2157 gdImageFilledPolygon(gif,back,4,graph_col[GRC_BACK].i);
2159 canvas[0].x = im->xorigin;
2160 canvas[0].y = im->yorigin;
2161 canvas[1].x = canvas[0].x+im->xsize;
2162 canvas[1].y = canvas[0].y;
2163 canvas[2].x = canvas[1].x;
2164 canvas[2].y = canvas[0].y-im->ysize;
2165 canvas[3].x = canvas[0].x;
2166 canvas[3].y = canvas[2].y;
2168 gdImageFilledPolygon(gif,canvas,4,graph_col[GRC_CANVAS].i);
2170 if (im->minval > 0.0)
2171 areazero = im->minval;
2172 if (im->maxval < 0.0)
2173 areazero = im->maxval;
2175 axis_paint(im,gif);
2177 for(i=0;i<im->gdes_c;i++){
2179 switch(im->gdes[i].gf){
2180 case GF_CDEF:
2181 case GF_DEF:
2182 case GF_PRINT:
2183 case GF_GPRINT:
2184 case GF_COMMENT:
2185 case GF_HRULE:
2186 case GF_VRULE:
2187 break;
2188 case GF_TICK:
2189 for (ii = 0; ii < im->xsize; ii++)
2190 {
2191 if (!isnan(im->gdes[i].p_data[ii]) &&
2192 im->gdes[i].p_data[ii] > 0.0)
2193 {
2194 /* generate a tick */
2195 gdImageLine(gif, im -> xorigin + ii,
2196 im -> yorigin - (im -> gdes[i].yrule * im -> ysize),
2197 im -> xorigin + ii,
2198 im -> yorigin,
2199 im -> gdes[i].col.i);
2200 }
2201 }
2202 break;
2203 case GF_LINE1:
2204 case GF_LINE2:
2205 case GF_LINE3:
2206 case GF_AREA:
2207 stack_gf = im->gdes[i].gf;
2208 case GF_STACK:
2209 /* fix data points at oo and -oo */
2210 for(ii=0;ii<im->xsize;ii++){
2211 if (isinf(im->gdes[i].p_data[ii])){
2212 if (im->gdes[i].p_data[ii] > 0) {
2213 im->gdes[i].p_data[ii] = im->maxval ;
2214 } else {
2215 im->gdes[i].p_data[ii] = im->minval ;
2216 }
2218 }
2219 }
2221 if (im->gdes[i].col.i != -1){
2222 /* GF_LINE and frined */
2223 if(stack_gf == GF_LINE1 || stack_gf == GF_LINE2 || stack_gf == GF_LINE3 ){
2224 brush = MkLineBrush(im,i,stack_gf);
2225 gdImageSetBrush(gif, brush);
2226 for(ii=1;ii<im->xsize;ii++){
2227 if (isnan(im->gdes[i].p_data[ii-1]) ||
2228 isnan(im->gdes[i].p_data[ii]))
2229 continue;
2230 gdImageLine(gif,
2231 ii+im->xorigin-1,ytr(im,im->gdes[i].p_data[ii-1]),
2232 ii+im->xorigin,ytr(im,im->gdes[i].p_data[ii]),
2233 gdBrushed);
2235 }
2236 gdImageDestroy(brush);
2237 }
2238 else
2239 /* GF_AREA STACK type*/
2240 if (im->gdes[i].gf == GF_STACK )
2241 for(ii=0;ii<im->xsize;ii++){
2242 if(isnan(im->gdes[i].p_data[ii])){
2243 im->gdes[i].p_data[ii] = lastgdes->p_data[ii];
2244 continue;
2245 }
2247 if (lastgdes->p_data[ii] == im->gdes[i].p_data[ii]){
2248 continue;
2249 }
2250 gdImageLine(gif,
2251 ii+im->xorigin,ytr(im,lastgdes->p_data[ii]),
2252 ii+im->xorigin,ytr(im,im->gdes[i].p_data[ii]),
2253 im->gdes[i].col.i);
2254 }
2256 else /* simple GF_AREA */
2257 for(ii=0;ii<im->xsize;ii++){
2258 if (isnan(im->gdes[i].p_data[ii])) {
2259 im->gdes[i].p_data[ii] = 0;
2260 continue;
2261 }
2262 gdImageLine(gif,
2263 ii+im->xorigin,ytr(im,areazero),
2264 ii+im->xorigin,ytr(im,im->gdes[i].p_data[ii]),
2265 im->gdes[i].col.i);
2266 }
2267 }
2268 lastgdes = &(im->gdes[i]);
2269 break;
2270 }
2271 }
2273 grid_paint(im,gif);
2275 /* the RULES are the last thing to paint ... */
2276 for(i=0;i<im->gdes_c;i++){
2278 switch(im->gdes[i].gf){
2279 case GF_HRULE:
2280 if(im->gdes[i].yrule >= im->minval
2281 && im->gdes[i].yrule <= im->maxval)
2282 gdImageLine(gif,
2283 im->xorigin,ytr(im,im->gdes[i].yrule),
2284 im->xorigin+im->xsize,ytr(im,im->gdes[i].yrule),
2285 im->gdes[i].col.i);
2286 break;
2287 case GF_VRULE:
2288 if(im->gdes[i].xrule >= im->start
2289 && im->gdes[i].xrule <= im->end)
2290 gdImageLine(gif,
2291 xtr(im,im->gdes[i].xrule),im->yorigin,
2292 xtr(im,im->gdes[i].xrule),im->yorigin-im->ysize,
2293 im->gdes[i].col.i);
2294 break;
2295 default:
2296 break;
2297 }
2298 }
2300 if (strcmp(im->graphfile,"-")==0) {
2301 #ifdef WIN32
2302 /* Change translation mode for stdout to BINARY */
2303 _setmode( _fileno( stdout ), O_BINARY );
2304 #endif
2305 fo = stdout;
2306 } else {
2307 if ((fo = fopen(im->graphfile,"wb")) == NULL) {
2308 rrd_set_error("Opening '%s' for write: %s",im->graphfile, strerror(errno));
2309 return (-1);
2310 }
2311 }
2312 switch (im->imgformat) {
2313 case IF_GIF:
2314 gdImageGif(gif, fo);
2315 break;
2316 case IF_PNG:
2317 gdImagePng(gif, fo);
2318 break;
2319 }
2320 if (strcmp(im->graphfile,"-") != 0)
2321 fclose(fo);
2322 gdImageDestroy(gif);
2324 return 0;
2325 }
2328 /*****************************************************
2329 * graph stuff
2330 *****************************************************/
2332 int
2333 gdes_alloc(image_desc_t *im){
2335 long def_step = (im->end-im->start)/im->xsize;
2337 if (im->step > def_step) /* step can be increassed ... no decreassed */
2338 def_step = im->step;
2340 im->gdes_c++;
2342 if ((im->gdes = (graph_desc_t *) rrd_realloc(im->gdes, (im->gdes_c)
2343 * sizeof(graph_desc_t)))==NULL){
2344 rrd_set_error("realloc graph_descs");
2345 return -1;
2346 }
2349 im->gdes[im->gdes_c-1].step=def_step;
2350 im->gdes[im->gdes_c-1].start=im->start;
2351 im->gdes[im->gdes_c-1].end=im->end;
2352 im->gdes[im->gdes_c-1].vname[0]='\0';
2353 im->gdes[im->gdes_c-1].data=NULL;
2354 im->gdes[im->gdes_c-1].ds_namv=NULL;
2355 im->gdes[im->gdes_c-1].data_first=0;
2356 im->gdes[im->gdes_c-1].p_data=NULL;
2357 im->gdes[im->gdes_c-1].rpnp=NULL;
2358 im->gdes[im->gdes_c-1].col.red = -1;
2359 im->gdes[im->gdes_c-1].col.i=-1;
2360 im->gdes[im->gdes_c-1].legend[0]='\0';
2361 im->gdes[im->gdes_c-1].rrd[0]='\0';
2362 im->gdes[im->gdes_c-1].ds=-1;
2363 im->gdes[im->gdes_c-1].p_data=NULL;
2364 return 0;
2365 }
2367 /* copies input untill the first unescaped colon is found
2368 or until input ends. backslashes have to be escaped as well */
2369 int
2370 scan_for_col(char *input, int len, char *output)
2371 {
2372 int inp,outp=0;
2373 for (inp=0;
2374 inp < len &&
2375 input[inp] != ':' &&
2376 input[inp] != '\0';
2377 inp++){
2378 if (input[inp] == '\\' &&
2379 input[inp+1] != '\0' &&
2380 (input[inp+1] == '\\' ||
2381 input[inp+1] == ':')){
2382 output[outp++] = input[++inp];
2383 }
2384 else {
2385 output[outp++] = input[inp];
2386 }
2387 }
2388 output[outp] = '\0';
2389 return inp;
2390 }
2392 int
2393 rrd_graph(int argc, char **argv, char ***prdata, int *xsize, int *ysize)
2394 {
2396 image_desc_t im;
2397 int i;
2398 long long_tmp;
2399 time_t start_tmp=0,end_tmp=0;
2400 char scan_gtm[12],scan_mtm[12],scan_ltm[12],col_nam[12];
2401 char symname[100];
2402 unsigned int col_red,col_green,col_blue;
2403 long scancount;
2404 int linepass = 0; /* stack can only follow directly after LINE* AREA or STACK */
2405 struct time_value start_tv, end_tv;
2406 char *parsetime_error = NULL;
2407 int stroff;
2409 (*prdata)=NULL;
2411 parsetime("end-24h", &start_tv);
2412 parsetime("now", &end_tv);
2414 im.xlab_user.minsec = -1;
2415 im.xgif=0;
2416 im.ygif=0;
2417 im.xsize = 400;
2418 im.ysize = 100;
2419 im.step = 0;
2420 im.ylegend[0] = '\0';
2421 im.title[0] = '\0';
2422 im.minval = DNAN;
2423 im.maxval = DNAN;
2424 im.interlaced = 0;
2425 im.unitsexponent= 9999;
2426 im.extra_flags= 0;
2427 im.rigid = 0;
2428 im.imginfo = NULL;
2429 im.lazy = 0;
2430 im.logarithmic = 0;
2431 im.ygridstep = DNAN;
2432 im.draw_x_grid = 1;
2433 im.draw_y_grid = 1;
2434 im.base = 1000;
2435 im.prt_c = 0;
2436 im.gdes_c = 0;
2437 im.gdes = NULL;
2438 im.imgformat = IF_GIF; /* we default to GIF output */
2440 for(i=0;i<DIM(graph_col);i++)
2441 im.graph_col[i].red=-1;
2444 while (1){
2445 static struct option long_options[] =
2446 {
2447 {"start", required_argument, 0, 's'},
2448 {"end", required_argument, 0, 'e'},
2449 {"x-grid", required_argument, 0, 'x'},
2450 {"y-grid", required_argument, 0, 'y'},
2451 {"vertical-label",required_argument,0,'v'},
2452 {"width", required_argument, 0, 'w'},
2453 {"height", required_argument, 0, 'h'},
2454 {"interlaced", no_argument, 0, 'i'},
2455 {"upper-limit",required_argument, 0, 'u'},
2456 {"lower-limit",required_argument, 0, 'l'},
2457 {"rigid", no_argument, 0, 'r'},
2458 {"base", required_argument, 0, 'b'},
2459 {"logarithmic",no_argument, 0, 'o'},
2460 {"color", required_argument, 0, 'c'},
2461 {"title", required_argument, 0, 't'},
2462 {"imginfo", required_argument, 0, 'f'},
2463 {"imgformat", required_argument, 0, 'a'},
2464 {"lazy", no_argument, 0, 'z'},
2465 {"no-legend", no_argument, 0, 'g'},
2466 {"alt-y-grid", no_argument, 0, 257 },
2467 {"alt-autoscale", no_argument, 0, 258 },
2468 {"alt-autoscale-max", no_argument, 0, 259 },
2469 {"units-exponent",required_argument, 0, 260},
2470 {"step", required_argument, 0, 261},
2471 {0,0,0,0}};
2472 int option_index = 0;
2473 int opt;
2476 opt = getopt_long(argc, argv,
2477 "s:e:x:y:v:w:h:iu:l:rb:oc:t:f:a:z:g",
2478 long_options, &option_index);
2480 if (opt == EOF)
2481 break;
2483 switch(opt) {
2484 case 257:
2485 im.extra_flags |= ALTYGRID;
2486 break;
2487 case 258:
2488 im.extra_flags |= ALTAUTOSCALE;
2489 break;
2490 case 259:
2491 im.extra_flags |= ALTAUTOSCALE_MAX;
2492 break;
2493 case 'g':
2494 im.extra_flags |= NOLEGEND;
2495 break;
2496 case 260:
2497 im.unitsexponent = atoi(optarg);
2498 break;
2499 case 261:
2500 im.step = atoi(optarg);
2501 break;
2502 case 's':
2503 if ((parsetime_error = parsetime(optarg, &start_tv))) {
2504 rrd_set_error( "start time: %s", parsetime_error );
2505 return -1;
2506 }
2507 break;
2508 case 'e':
2509 if ((parsetime_error = parsetime(optarg, &end_tv))) {
2510 rrd_set_error( "end time: %s", parsetime_error );
2511 return -1;
2512 }
2513 break;
2514 case 'x':
2515 if(strcmp(optarg,"none") == 0){
2516 im.draw_x_grid=0;
2517 break;
2518 };
2520 if(sscanf(optarg,
2521 "%10[A-Z]:%ld:%10[A-Z]:%ld:%10[A-Z]:%ld:%ld:%n",
2522 scan_gtm,
2523 &im.xlab_user.gridst,
2524 scan_mtm,
2525 &im.xlab_user.mgridst,
2526 scan_ltm,
2527 &im.xlab_user.labst,
2528 &im.xlab_user.precis,
2529 &stroff) == 7 && stroff != 0){
2530 strncpy(im.xlab_form, optarg+stroff, sizeof(im.xlab_form) - 1);
2531 if((im.xlab_user.gridtm = tmt_conv(scan_gtm)) == -1){
2532 rrd_set_error("unknown keyword %s",scan_gtm);
2533 return -1;
2534 } else if ((im.xlab_user.mgridtm = tmt_conv(scan_mtm)) == -1){
2535 rrd_set_error("unknown keyword %s",scan_mtm);
2536 return -1;
2537 } else if ((im.xlab_user.labtm = tmt_conv(scan_ltm)) == -1){
2538 rrd_set_error("unknown keyword %s",scan_ltm);
2539 return -1;
2540 }
2541 im.xlab_user.minsec = 1;
2542 im.xlab_user.stst = im.xlab_form;
2543 } else {
2544 rrd_set_error("invalid x-grid format");
2545 return -1;
2546 }
2547 break;
2548 case 'y':
2550 if(strcmp(optarg,"none") == 0){
2551 im.draw_y_grid=0;
2552 break;
2553 };
2555 if(sscanf(optarg,
2556 "%lf:%d",
2557 &im.ygridstep,
2558 &im.ylabfact) == 2) {
2559 if(im.ygridstep<=0){
2560 rrd_set_error("grid step must be > 0");
2561 return -1;
2562 } else if (im.ylabfact < 1){
2563 rrd_set_error("label factor must be > 0");
2564 return -1;
2565 }
2566 } else {
2567 rrd_set_error("invalid y-grid format");
2568 return -1;
2569 }
2570 break;
2571 case 'v':
2572 strncpy(im.ylegend,optarg,150);
2573 im.ylegend[150]='\0';
2574 break;
2575 case 'u':
2576 im.maxval = atof(optarg);
2577 break;
2578 case 'l':
2579 im.minval = atof(optarg);
2580 break;
2581 case 'b':
2582 im.base = atol(optarg);
2583 if(im.base != 1024 && im.base != 1000 ){
2584 rrd_set_error("the only sensible value for base apart from 1000 is 1024");
2585 return -1;
2586 }
2587 break;
2588 case 'w':
2589 long_tmp = atol(optarg);
2590 if (long_tmp < 10) {
2591 rrd_set_error("width below 10 pixels");
2592 return -1;
2593 }
2594 im.xsize = long_tmp;
2595 break;
2596 case 'h':
2597 long_tmp = atol(optarg);
2598 if (long_tmp < 10) {
2599 rrd_set_error("height below 10 pixels");
2600 return -1;
2601 }
2602 im.ysize = long_tmp;
2603 break;
2604 case 'i':
2605 im.interlaced = 1;
2606 break;
2607 case 'r':
2608 im.rigid = 1;
2609 break;
2610 case 'f':
2611 im.imginfo = optarg;
2612 break;
2613 case 'a':
2614 if((im.imgformat = if_conv(optarg)) == -1) {
2615 rrd_set_error("unsupported graphics format '%s'",optarg);
2616 return -1;
2617 }
2618 break;
2619 case 'z':
2620 im.lazy = 1;
2621 break;
2622 case 'o':
2623 im.logarithmic = 1;
2624 if (isnan(im.minval))
2625 im.minval=1;
2626 break;
2627 case 'c':
2628 if(sscanf(optarg,
2629 "%10[A-Z]#%2x%2x%2x",
2630 col_nam,&col_red,&col_green,&col_blue) == 4){
2631 int ci;
2632 if((ci=grc_conv(col_nam)) != -1){
2633 im.graph_col[ci].red=col_red;
2634 im.graph_col[ci].green=col_green;
2635 im.graph_col[ci].blue=col_blue;
2636 } else {
2637 rrd_set_error("invalid color name '%s'",col_nam);
2638 }
2639 } else {
2640 rrd_set_error("invalid color def format");
2641 return -1;
2642 }
2643 break;
2644 case 't':
2645 strncpy(im.title,optarg,150);
2646 im.title[150]='\0';
2647 break;
2649 case '?':
2650 if (optopt != 0)
2651 rrd_set_error("unknown option '%c'", optopt);
2652 else
2653 rrd_set_error("unknown option '%s'",argv[optind-1]);
2654 return -1;
2655 }
2656 }
2658 if (optind >= argc) {
2659 rrd_set_error("missing filename");
2660 return -1;
2661 }
2663 if (im.logarithmic == 1 && (im.minval <= 0 || isnan(im.minval))){
2664 rrd_set_error("for a logarithmic yaxis you must specify a lower-limit > 0");
2665 return -1;
2666 }
2668 strncpy(im.graphfile,argv[optind],MAXPATH-1);
2669 im.graphfile[MAXPATH-1]='\0';
2671 if (proc_start_end(&start_tv,&end_tv,&start_tmp,&end_tmp) == -1){
2672 return -1;
2673 }
2675 if (start_tmp < 3600*24*365*10){
2676 rrd_set_error("the first entry to fetch should be after 1980 (%ld)",start_tmp);
2677 return -1;
2678 }
2680 if (end_tmp < start_tmp) {
2681 rrd_set_error("start (%ld) should be less than end (%ld)",
2682 start_tmp, end_tmp);
2683 return -1;
2684 }
2686 im.start = start_tmp;
2687 im.end = end_tmp;
2690 for(i=optind+1;i<argc;i++){
2691 int argstart=0;
2692 int strstart=0;
2693 char varname[30],*rpnex;
2694 gdes_alloc(&im);
2695 if(sscanf(argv[i],"%10[A-Z0-9]:%n",symname,&argstart)==1){
2696 if((im.gdes[im.gdes_c-1].gf=gf_conv(symname))==-1){
2697 im_free(&im);
2698 rrd_set_error("unknown function '%s'",symname);
2699 return -1;
2700 }
2701 } else {
2702 rrd_set_error("can't parse '%s'",argv[i]);
2703 im_free(&im);
2704 return -1;
2705 }
2707 /* reset linepass if a non LINE/STACK/AREA operator gets parsed
2709 if (im.gdes[im.gdes_c-1].gf != GF_LINE1 &&
2710 im.gdes[im.gdes_c-1].gf != GF_LINE2 &&
2711 im.gdes[im.gdes_c-1].gf != GF_LINE3 &&
2712 im.gdes[im.gdes_c-1].gf != GF_AREA &&
2713 im.gdes[im.gdes_c-1].gf != GF_STACK) {
2714 linepass = 0;
2715 }
2716 */
2718 switch(im.gdes[im.gdes_c-1].gf){
2719 case GF_PRINT:
2720 im.prt_c++;
2721 case GF_GPRINT:
2722 if(sscanf(
2723 &argv[i][argstart],
2724 "%29[^#:]:" CF_NAM_FMT ":%n",
2725 varname,symname,&strstart) == 2){
2726 scan_for_col(&argv[i][argstart+strstart],FMT_LEG_LEN,im.gdes[im.gdes_c-1].format);
2727 if((im.gdes[im.gdes_c-1].vidx=find_var(&im,varname))==-1){
2728 im_free(&im);
2729 rrd_set_error("unknown variable '%s'",varname);
2730 return -1;
2731 }
2732 if((im.gdes[im.gdes_c-1].cf=cf_conv(symname))==-1){
2733 im_free(&im);
2734 return -1;
2735 }
2737 } else {
2738 im_free(&im);
2739 rrd_set_error("can't parse '%s'",&argv[i][argstart]);
2740 return -1;
2741 }
2742 break;
2743 case GF_COMMENT:
2744 if(strlen(&argv[i][argstart])>FMT_LEG_LEN) argv[i][argstart+FMT_LEG_LEN-3]='\0' ;
2745 strcpy(im.gdes[im.gdes_c-1].legend, &argv[i][argstart]);
2746 break;
2747 case GF_HRULE:
2748 if(sscanf(
2749 &argv[i][argstart],
2750 "%lf#%2x%2x%2x:%n",
2751 &im.gdes[im.gdes_c-1].yrule,
2752 &col_red,&col_green,&col_blue,
2753 &strstart) >= 4){
2754 im.gdes[im.gdes_c-1].col.red = col_red;
2755 im.gdes[im.gdes_c-1].col.green = col_green;
2756 im.gdes[im.gdes_c-1].col.blue = col_blue;
2757 if(strstart <= 0){
2758 im.gdes[im.gdes_c-1].legend[0] = '\0';
2759 } else {
2760 scan_for_col(&argv[i][argstart+strstart],FMT_LEG_LEN,im.gdes[im.gdes_c-1].legend);
2761 }
2762 } else {
2763 im_free(&im);
2764 rrd_set_error("can't parse '%s'",&argv[i][argstart]);
2765 return -1;
2766 }
2767 break;
2768 case GF_VRULE:
2769 if(sscanf(
2770 &argv[i][argstart],
2771 "%lu#%2x%2x%2x:%n",
2772 &im.gdes[im.gdes_c-1].xrule,
2773 &col_red,
2774 &col_green,
2775 &col_blue,
2776 &strstart) >= 4){
2777 im.gdes[im.gdes_c-1].col.red = col_red;
2778 im.gdes[im.gdes_c-1].col.green = col_green;
2779 im.gdes[im.gdes_c-1].col.blue = col_blue;
2780 if(strstart <= 0){
2781 im.gdes[im.gdes_c-1].legend[0] = '\0';
2782 } else {
2783 scan_for_col(&argv[i][argstart+strstart],FMT_LEG_LEN,im.gdes[im.gdes_c-1].legend);
2784 }
2785 } else {
2786 im_free(&im);
2787 rrd_set_error("can't parse '%s'",&argv[i][argstart]);
2788 return -1;
2789 }
2790 break;
2791 case GF_TICK:
2792 if((scancount=sscanf(
2793 &argv[i][argstart],
2794 "%29[^:#]#%2x%2x%2x:%lf:%n",
2795 varname,
2796 &col_red,
2797 &col_green,
2798 &col_blue,
2799 &(im.gdes[im.gdes_c-1].yrule),
2800 &strstart))>=1)
2801 {
2802 im.gdes[im.gdes_c-1].col.red = col_red;
2803 im.gdes[im.gdes_c-1].col.green = col_green;
2804 im.gdes[im.gdes_c-1].col.blue = col_blue;
2805 if(strstart <= 0){
2806 im.gdes[im.gdes_c-1].legend[0] = '\0';
2807 } else {
2808 scan_for_col(&argv[i][argstart+strstart],FMT_LEG_LEN,im.gdes[im.gdes_c-1].legend);
2809 }
2810 if((im.gdes[im.gdes_c-1].vidx=find_var(&im,varname))==-1){
2811 im_free(&im);
2812 rrd_set_error("unknown variable '%s'",varname);
2813 return -1;
2814 }
2815 if (im.gdes[im.gdes_c-1].yrule <= 0.0 || im.gdes[im.gdes_c-1].yrule > 1.0)
2816 {
2817 im_free(&im);
2818 rrd_set_error("Tick mark scaling factor out of range");
2819 return -1;
2820 }
2821 if (scancount < 4)
2822 im.gdes[im.gdes_c-1].col.red = -1;
2823 if (scancount < 5)
2824 /* default tick marks: 10% of the y-axis */
2825 im.gdes[im.gdes_c-1].yrule = 0.1;
2827 } else {
2828 im_free(&im);
2829 rrd_set_error("can't parse '%s'",&argv[i][argstart]);
2830 return -1;
2831 } /* endif sscanf */
2832 break;
2833 case GF_STACK:
2834 if(linepass == 0){
2835 im_free(&im);
2836 rrd_set_error("STACK must follow AREA, LINE or STACK");
2837 return -1;
2838 }
2839 case GF_LINE1:
2840 case GF_LINE2:
2841 case GF_LINE3:
2842 case GF_AREA:
2843 linepass = 1;
2844 if((scancount=sscanf(
2845 &argv[i][argstart],
2846 "%29[^:#]#%2x%2x%2x:%n",
2847 varname,
2848 &col_red,
2849 &col_green,
2850 &col_blue,
2851 &strstart))>=1){
2852 im.gdes[im.gdes_c-1].col.red = col_red;
2853 im.gdes[im.gdes_c-1].col.green = col_green;
2854 im.gdes[im.gdes_c-1].col.blue = col_blue;
2855 if(strstart <= 0){
2856 im.gdes[im.gdes_c-1].legend[0] = '\0';
2857 } else {
2858 scan_for_col(&argv[i][argstart+strstart],FMT_LEG_LEN,im.gdes[im.gdes_c-1].legend);
2859 }
2860 if((im.gdes[im.gdes_c-1].vidx=find_var(&im,varname))==-1){
2861 im_free(&im);
2862 rrd_set_error("unknown variable '%s'",varname);
2863 return -1;
2864 }
2865 if (scancount < 4)
2866 im.gdes[im.gdes_c-1].col.red = -1;
2868 } else {
2869 im_free(&im);
2870 rrd_set_error("can't parse '%s'",&argv[i][argstart]);
2871 return -1;
2872 }
2873 break;
2874 case GF_CDEF:
2875 if((rpnex = malloc(strlen(&argv[i][argstart])*sizeof(char)))==NULL){
2876 rrd_set_error("malloc for CDEF");
2877 return -1;
2878 }
2879 if(sscanf(
2880 &argv[i][argstart],
2881 DEF_NAM_FMT "=%[^: ]",
2882 im.gdes[im.gdes_c-1].vname,
2883 rpnex) != 2){
2884 im_free(&im);
2885 free(rpnex);
2886 rrd_set_error("can't parse CDEF '%s'",&argv[i][argstart]);
2887 return -1;
2888 }
2889 /* checking for duplicate DEF CDEFS */
2890 if(find_var(&im,im.gdes[im.gdes_c-1].vname) != -1){
2891 im_free(&im);
2892 rrd_set_error("duplicate variable '%s'",
2893 im.gdes[im.gdes_c-1].vname);
2894 return -1;
2895 }
2896 if((im.gdes[im.gdes_c-1].rpnp =
2897 rpn_parse((void*)&im,rpnex,&find_var_wrapper))== NULL){
2898 rrd_set_error("invalid rpn expression '%s'", rpnex);
2899 im_free(&im);
2900 return -1;
2901 }
2902 free(rpnex);
2903 break;
2904 case GF_DEF:
2905 if (sscanf(
2906 &argv[i][argstart],
2907 DEF_NAM_FMT "=%n",
2908 im.gdes[im.gdes_c-1].vname,
2909 &strstart)== 1 && strstart){ /* is the = did not match %n returns 0 */
2910 if(sscanf(&argv[i][argstart
2911 +strstart
2912 +scan_for_col(&argv[i][argstart+strstart],
2913 MAXPATH,im.gdes[im.gdes_c-1].rrd)],
2914 ":" DS_NAM_FMT ":" CF_NAM_FMT,
2915 im.gdes[im.gdes_c-1].ds_nam,
2916 symname) != 2){
2917 im_free(&im);
2918 rrd_set_error("can't parse DEF '%s' -2",&argv[i][argstart]);
2919 return -1;
2920 }
2921 } else {
2922 im_free(&im);
2923 rrd_set_error("can't parse DEF '%s'",&argv[i][argstart]);
2924 return -1;
2925 }
2927 /* checking for duplicate DEF CDEFS */
2928 if (find_var(&im,im.gdes[im.gdes_c-1].vname) != -1){
2929 im_free(&im);
2930 rrd_set_error("duplicate variable '%s'",
2931 im.gdes[im.gdes_c-1].vname);
2932 return -1;
2933 }
2934 if((im.gdes[im.gdes_c-1].cf=cf_conv(symname))==-1){
2935 im_free(&im);
2936 rrd_set_error("unknown cf '%s'",symname);
2937 return -1;
2938 }
2939 break;
2940 }
2942 }
2944 if (im.gdes_c==0){
2945 rrd_set_error("can't make a graph without contents");
2946 im_free(&im);
2947 return(-1);
2948 }
2950 /* parse rest of arguments containing information on what to draw*/
2951 if (graph_paint(&im,prdata)==-1){
2952 im_free(&im);
2953 return -1;
2954 }
2956 *xsize=im.xgif;
2957 *ysize=im.ygif;
2958 if (im.imginfo){
2959 char *filename;
2960 if (! (*prdata)) {
2961 /* maybe prdata is not allocated yet ... lets do it now */
2962 if((*prdata = calloc(2,sizeof(char *)))==NULL){
2963 rrd_set_error("malloc imginfo");
2964 return -1;
2965 };
2966 }
2967 if(((*prdata)[0] = malloc((strlen(im.imginfo)+200+strlen(im.graphfile))*sizeof(char)))
2968 ==NULL){
2969 rrd_set_error("malloc imginfo");
2970 return -1;
2971 }
2972 filename=im.graphfile+strlen(im.graphfile);
2973 while(filename > im.graphfile){
2974 if (*(filename-1)=='/' || *(filename-1)=='\\' ) break;
2975 filename--;
2976 }
2978 sprintf((*prdata)[0],im.imginfo,filename,im.xgif,im.ygif);
2979 }
2980 im_free(&im);
2981 return 0;
2982 }
2984 int bad_format(char *fmt) {
2985 char *ptr;
2987 ptr = fmt;
2988 while (*ptr != '\0') {
2989 if (*ptr == '%') {ptr++;
2990 if (*ptr == '\0') return 1;
2991 while ((*ptr >= '0' && *ptr <= '9') || *ptr == '.') {
2992 ptr++;
2993 }
2994 if (*ptr == '\0') return 1;
2995 if (*ptr == 'l') {
2996 ptr++;
2997 if (*ptr == '\0') return 1;
2998 if (*ptr == 'e' || *ptr == 'f') {
2999 ptr++;
3000 } else { return 1; }
3001 }
3002 else if (*ptr == 's' || *ptr == 'S' || *ptr == '%') { ++ptr; }
3003 else { return 1; }
3004 } else {
3005 ++ptr;
3006 }
3007 }
3008 return 0;
3009 }