* rrd__graph.c make creates ne rrds
****************************************************************************/
+#if 0
#include "rrd_tool.h"
+#endif
+
#include <gd.h>
#include <gdlucidan10.h>
#include <gdlucidab12.h>
#include <fcntl.h>
#endif
+#include "rrd_graph.h"
+#include "rrd_graph_helper.h"
+
#define SmallFont gdLucidaNormal10
#define LargeFont gdLucidaBold12
# define DPRINT(x)
#endif
-#define DEF_NAM_FMT "%29[_A-Za-z0-9]"
-
-enum tmt_en {TMT_SECOND=0,TMT_MINUTE,TMT_HOUR,TMT_DAY,
- TMT_WEEK,TMT_MONTH,TMT_YEAR};
-
-enum grc_en {GRC_CANVAS=0,GRC_BACK,GRC_SHADEA,GRC_SHADEB,
- GRC_GRID,GRC_MGRID,GRC_FONT,GRC_FRAME,GRC_ARROW,__GRC_END__};
-
-
-enum gf_en {GF_PRINT=0,GF_GPRINT,GF_COMMENT,GF_HRULE,GF_VRULE,GF_LINE1,
- GF_LINE2,GF_LINE3,GF_AREA,GF_STACK, GF_DEF, GF_CDEF };
-
-enum op_en {OP_NUMBER=0,OP_VARIABLE,OP_INF,OP_PREV,OP_NEGINF,
- OP_UNKN,OP_NOW,OP_TIME,OP_LTIME,OP_ADD,OP_MOD,
- OP_SUB,OP_MUL,
- OP_DIV,OP_SIN, OP_DUP, OP_EXC, OP_POP,
- OP_COS,OP_LOG,OP_EXP,OP_LT,OP_LE,OP_GT,OP_GE,OP_EQ,OP_IF,
- OP_MIN,OP_MAX,OP_LIMIT, OP_FLOOR, OP_CEIL,
- OP_UN,OP_END};
-
-enum if_en {IF_GIF=0,IF_PNG=1};
-
-typedef struct rpnp_t {
- enum op_en op;
- double val; /* value for a OP_NUMBER */
- long ptr; /* pointer into the gdes array for OP_VAR */
- double *data; /* pointer to the current value from OP_VAR DAS*/
- long ds_cnt; /* data source count for data pointer */
- long step; /* time step for OP_VAR das */
-} rpnp_t;
-
-
-typedef struct col_trip_t {
- int red; /* red = -1 is no color */
- int green;
- int blue;
- int i; /* color index assigned in gif image i=-1 is unasigned*/
-} col_trip_t;
-
-
-typedef struct xlab_t {
- long minsec; /* minimum sec per pix */
- enum tmt_en gridtm; /* grid interval in what ?*/
- long gridst; /* how many whats per grid*/
- enum tmt_en mgridtm; /* label interval in what ?*/
- long mgridst; /* how many whats per label*/
- enum tmt_en labtm; /* label interval in what ?*/
- long labst; /* how many whats per label*/
- long precis; /* label precision -> label placement*/
- char *stst; /* strftime string*/
-} xlab_t;
-
xlab_t xlab[] = {
{0, TMT_SECOND,30, TMT_MINUTE,5, TMT_MINUTE,5, 0,"%H:%M"},
{2, TMT_MINUTE,1, TMT_MINUTE,5, TMT_MINUTE,5, 0,"%H:%M"},
/* sensible y label intervals ...*/
-typedef struct ylab_t {
- double grid; /* grid spacing */
- int lfac[4]; /* associated label spacing*/
-} ylab_t;
-
ylab_t ylab[]= {
{0.1, {1,2, 5,10}},
{0.2, {1,5,10,20}},
{255,0,0,-1} /*arrow*/
};
-/* this structure describes the elements which can make up a graph.
- because they are quite diverse, not all elements will use all the
- possible parts of the structure. */
-#ifdef HAVE_SNPRINTF
-#define FMT_LEG_LEN 200
-#else
-#define FMT_LEG_LEN 2000
-#endif
-
-typedef struct graph_desc_t {
- enum gf_en gf; /* graphing function */
- char vname[30]; /* name of the variable */
- long vidx; /* gdes reference */
- char rrd[255]; /* name of the rrd_file containing data */
- char ds_nam[DS_NAM_SIZE]; /* data source name */
- long ds; /* data source number */
- enum cf_en cf; /* consolidation function */
- col_trip_t col; /* graph color */
- char format[FMT_LEG_LEN+5]; /* format for PRINT AND GPRINT */
- char legend[FMT_LEG_LEN+5]; /* legend*/
- gdPoint legloc; /* location of legend */
- double yrule; /* value for y rule line */
- time_t xrule; /* value for x rule line */
- rpnp_t *rpnp; /* instructions for CDEF function */
-
- /* description of data fetched for the graph element */
- time_t start,end; /* timestaps for first and last data element */
- unsigned long step; /* time between samples */
- unsigned long ds_cnt; /* how many data sources are there in the fetch */
- long data_first; /* first pointer to this data */
- char **ds_namv; /* name of datasources in the fetch. */
- rrd_value_t *data; /* the raw data drawn from the rrd */
- rrd_value_t *p_data; /* processed data, xsize elments */
-
-} graph_desc_t;
-
-#define ALTYGRID 0x01 /* use alternative y grid algorithm */
-#define ALTAUTOSCALE 0x02 /* use alternative algorithm to find lower and upper bounds */
-#define ALTAUTOSCALE_MAX 0x04 /* use alternative algorithm to find upper bounds */
-#define NOLEGEND 0x08 /* use no legend */
-
-typedef struct image_desc_t {
-
- /* configuration of graph */
-
- char graphfile[MAXPATH]; /* filename for graphic */
- long xsize,ysize; /* graph area size in pixels */
- col_trip_t graph_col[__GRC_END__]; /* real colors for the graph */
- char ylegend[200]; /* legend along the yaxis */
- char title[200]; /* title for graph */
- int draw_x_grid; /* no x-grid at all */
- int draw_y_grid; /* no x-grid at all */
- xlab_t xlab_user; /* user defined labeling for xaxis */
- char xlab_form[200]; /* format for the label on the xaxis */
-
- double ygridstep; /* user defined step for y grid */
- int ylabfact; /* every how many y grid shall a label be written ? */
-
- time_t start,end; /* what time does the graph cover */
- unsigned long step; /* any preference for the default step ? */
- rrd_value_t minval,maxval; /* extreme values in the data */
- int rigid; /* do not expand range even with
- values outside */
- char* imginfo; /* construct an <IMG ... tag and return
- as first retval */
- int lazy; /* only update the gif if there is reasonable
- probablility that the existing one is out of date */
- int logarithmic; /* scale the yaxis logarithmic */
- enum if_en imgformat; /* image format */
-
- /* status information */
-
- long xorigin,yorigin;/* where is (0,0) of the graph */
- long xgif,ygif; /* total size of the gif */
- int interlaced; /* will the graph be interlaced? */
- double magfact; /* numerical magnitude*/
- long base; /* 1000 or 1024 depending on what we graph */
- char symbol; /* magnitude symbol for y-axis */
- int unitsexponent; /* 10*exponent for units on y-asis */
- int extra_flags; /* flags for boolean options */
- /* data elements */
-
- long prt_c; /* number of print elements */
- long gdes_c; /* number of graphics elements */
- graph_desc_t *gdes; /* points to an array of graph elements */
-
-} image_desc_t;
-
-/* Prototypes */
-int xtr(image_desc_t *,time_t);
-int ytr(image_desc_t *, double);
-enum gf_en gf_conv(char *);
-enum if_en if_conv(char *);
-enum tmt_en tmt_conv(char *);
-enum grc_en grc_conv(char *);
-int im_free(image_desc_t *);
-void auto_scale( image_desc_t *, double *, char **, double *);
-void si_unit( image_desc_t *);
-void expand_range(image_desc_t *);
-void reduce_data( enum cf_en, unsigned long, time_t *, time_t *, unsigned long *, unsigned long *, rrd_value_t **);
-int data_fetch( image_desc_t *);
-long find_var(image_desc_t *, char *);
-long lcd(long *);
-int data_calc( image_desc_t *);
-int data_proc( image_desc_t *);
-time_t find_first_time( time_t, enum tmt_en, long);
-time_t find_next_time( time_t, enum tmt_en, long);
-void gator( gdImagePtr, int, int);
-int tzoffset(time_t);
-int print_calc(image_desc_t *, char ***);
-int leg_place(image_desc_t *);
-int horizontal_grid(gdImagePtr, image_desc_t *);
-int horizontal_log_grid(gdImagePtr, image_desc_t *);
-void vertical_grid( gdImagePtr, image_desc_t *);
-void axis_paint( image_desc_t *, gdImagePtr);
-void grid_paint( image_desc_t *, gdImagePtr);
-gdImagePtr MkLineBrush(image_desc_t *,long, enum gf_en);
-int lazy_check(image_desc_t *);
-int graph_paint(image_desc_t *, char ***);
-int gdes_alloc(image_desc_t *);
-int scan_for_col(char *, int, char *);
-int rrd_graph(int, char **, char ***, int *, int *);
-int bad_format(char *);
-rpnp_t * str2rpn(image_desc_t *,char *);
/* translate time values into x coordinates */
/*#define xtr(x) (int)((double)im->xorigin \
}
}
-
-
/* conversion function for symbolic entry names */
conv_if(LINE3,GF_LINE3)
conv_if(AREA,GF_AREA)
conv_if(STACK,GF_STACK)
+ conv_if(TICK,GF_TICK)
conv_if(DEF,GF_DEF)
conv_if(CDEF,GF_CDEF)
+ conv_if(VDEF,GF_VDEF)
return (-1);
}
)
{
- char *symbol[] = {"a", /* 10e-18 Ato */
+ char *symbol[] = {"a", /* 10e-18 Atto */
"f", /* 10e-15 Femto */
"p", /* 10e-12 Pico */
"n", /* 10e-9 Nano */
"k", /* 10e3 Kilo */
"M", /* 10e6 Mega */
"G", /* 10e9 Giga */
- "T", /* 10e12 Terra */
+ "T", /* 10e12 Tera */
"P", /* 10e15 Peta */
"E"};/* 10e18 Exa */
)
{
- char symbol[] = {'a', /* 10e-18 Ato */
+ char symbol[] = {'a', /* 10e-18 Atto */
'f', /* 10e-15 Femto */
'p', /* 10e-12 Pico */
'n', /* 10e-9 Nano */
'k', /* 10e3 Kilo */
'M', /* 10e6 Mega */
'G', /* 10e9 Giga */
- 'T', /* 10e12 Terra */
+ 'T', /* 10e12 Tera */
'P', /* 10e15 Peta */
'E'};/* 10e18 Exa */
(*step) = cur_step*reduce_factor; /* set new step size for reduced data */
dstptr = *data;
srcptr = *data;
+ row_cnt = ((*end)-(*start))/cur_step;
- /* We were given one extra row at the beginning of the interval.
- ** We also need to return one extra row. The extra interval is
- ** the one defined by the start time in both cases. It is not
- ** used when graphing but maybe we can use it while reducing the
- ** data.
- */
- row_cnt = ((*end)-(*start))/cur_step +1;
+#ifdef DEBUG
+#define DEBUG_REDUCE
+#endif
+#ifdef DEBUG_REDUCE
+printf("Reducing %lu rows with factor %i time %lu to %lu, step %lu\n",
+ row_cnt,reduce_factor,*start,*end,cur_step);
+for (col=0;col<row_cnt;col++) {
+ printf("time %10lu: ",*start+(col+1)*cur_step);
+ for (i=0;i<*ds_cnt;i++)
+ printf(" %8.2e",srcptr[*ds_cnt*col+i]);
+ printf("\n");
+}
+#endif
- /* alter start and end so that they are multiples of the new steptime.
- ** End will be shifted towards the future and start will be shifted
- ** towards the past in order to include the requested interval
- */
+ /* We have to combine [reduce_factor] rows of the source
+ ** into one row for the destination. Doing this we also
+ ** need to take care to combine the correct rows. First
+ ** alter the start and end time so that they are multiples
+ ** of the new step time. We cannot reduce the amount of
+ ** time so we have to move the end towards the future and
+ ** the start towards the past.
+ */
end_offset = (*end) % (*step);
- if (end_offset) end_offset = (*step)-end_offset;
start_offset = (*start) % (*step);
- (*end) = (*end)+end_offset;
- (*start) = (*start)-start_offset;
- /* The first destination row is unknown yet it still needs
- ** to be present in the returned data. Skip it.
- ** Don't make it NaN or we might overwrite the source.
- */
- dstptr += (*ds_cnt);
-
- /* Depending on the amount of extra data needed at the
- ** start of the destination, three things can happen:
- ** -1- start_offset == 0: skip the extra source row
- ** -2- start_offset == cur_step: do nothing
- ** -3- start_offset > cur_step: skip some source rows and
- ** fill one destination row with NaN
+ /* If there is a start offset (which cannot be more than
+ ** one destination row), skip the appropriate number of
+ ** source rows and one destination row. The appropriate
+ ** number is what we do know (start_offset/cur_step) of
+ ** the new interval (*step/cur_step aka reduce_factor).
*/
- if (start_offset==0) {
- srcptr+=(*ds_cnt);
- row_cnt--;
- } else if (start_offset!=cur_step) {
- skiprows=((*step)-start_offset)/cur_step+1;
- srcptr += ((*ds_cnt)*skiprows);
+#ifdef DEBUG_REDUCE
+printf("start_offset: %lu end_offset: %lu\n",start_offset,end_offset);
+printf("row_cnt before: %lu\n",row_cnt);
+#endif
+ if (start_offset) {
+ (*start) = (*start)-start_offset;
+ skiprows=reduce_factor-start_offset/cur_step;
+ srcptr+=skiprows* *ds_cnt;
+ for (col=0;col<(*ds_cnt);col++) *dstptr++ = DNAN;
row_cnt-=skiprows;
- for (col=0;col<(*ds_cnt);col++) *dstptr++=DNAN;
}
+#ifdef DEBUG_REDUCE
+printf("row_cnt between: %lu\n",row_cnt);
+#endif
- /* If we had to alter the endtime, there won't be
- ** enough data to fill the last row. This means
- ** we have to skip some rows at the end
+ /* At the end we have some rows that are not going to be
+ ** used, the amount is end_offset/cur_step
*/
if (end_offset) {
- skiprows = ((*step)-end_offset)/cur_step;
+ (*end) = (*end)-end_offset+(*step);
+ skiprows = end_offset/cur_step;
row_cnt-=skiprows;
}
-
+#ifdef DEBUG_REDUCE
+printf("row_cnt after: %lu\n",row_cnt);
+#endif
/* Sanity check: row_cnt should be multiple of reduce_factor */
-/* if this gets triggered, something is REALY WRONG ... we die immediately */
+/* if this gets triggered, something is REALLY WRONG ... we die immediately */
if (row_cnt%reduce_factor) {
printf("SANITY CHECK: %lu rows cannot be reduced by %i \n",
if (isnan(newval)) newval = srcptr[i*(*ds_cnt)+col];
else {
switch (cf) {
+ case CF_HWPREDICT:
+ case CF_DEVSEASONAL:
+ case CF_DEVPREDICT:
+ case CF_SEASONAL:
case CF_AVERAGE:
newval += srcptr[i*(*ds_cnt)+col];
break;
case CF_MINIMUM:
newval = min (newval,srcptr[i*(*ds_cnt)+col]);
break;
+ case CF_FAILURES:
+ /* an interval contains a failure if any subintervals contained a failure */
case CF_MAXIMUM:
newval = max (newval,srcptr[i*(*ds_cnt)+col]);
break;
}
if (validval == 0){newval = DNAN;} else{
switch (cf) {
- case CF_AVERAGE:
- newval /= validval;
+ case CF_HWPREDICT:
+ case CF_DEVSEASONAL:
+ case CF_DEVPREDICT:
+ case CF_SEASONAL:
+ case CF_AVERAGE:
+ newval /= validval;
break;
case CF_MINIMUM:
- case CF_MAXIMUM:
+ case CF_FAILURES:
+ case CF_MAXIMUM:
case CF_LAST:
break;
}
srcptr+=(*ds_cnt)*reduce_factor;
row_cnt-=reduce_factor;
}
-
/* If we had to alter the endtime, we didn't have enough
** source rows to fill the last row. Fill it with NaN.
*/
- if (end_offset!=0) for (col=0;col<(*ds_cnt);col++) *dstptr++ = DNAN;
+ if (end_offset) for (col=0;col<(*ds_cnt);col++) *dstptr++ = DNAN;
+#ifdef DEBUG_REDUCE
+ row_cnt = ((*end)-(*start))/ *step;
+ srcptr = *data;
+ printf("Done reducing. Currently %lu rows, time %lu to %lu, step %lu\n",
+ row_cnt,*start,*end,*step);
+for (col=0;col<row_cnt;col++) {
+ printf("time %10lu: ",*start+(col+1)*(*step));
+ for (i=0;i<*ds_cnt;i++)
+ printf(" %8.2e",srcptr[*ds_cnt*col+i]);
+ printf("\n");
+}
+#endif
}
* CDEF stuff
*************************************************************/
+long
+find_var_wrapper(void *arg1, char *key)
+{
+ return find_var((image_desc_t *) arg1, key);
+}
+
/* find gdes containing var*/
long
find_var(image_desc_t *im, char *key){
long ii;
for(ii=0;ii<im->gdes_c-1;ii++){
if((im->gdes[ii].gf == GF_DEF
+ || im->gdes[ii].gf == GF_VDEF
|| im->gdes[ii].gf == GF_CDEF)
&& (strcmp(im->gdes[ii].vname,key) == 0)){
return ii;
return num[i];
}
-
-/* convert string to rpnp */
-rpnp_t *
-str2rpn(image_desc_t *im,char *expr){
- int pos=0;
- long steps=-1;
- rpnp_t *rpnp;
- char vname[30];
-
- rpnp=NULL;
-
- while(*expr){
- if ((rpnp = (rpnp_t *) rrd_realloc(rpnp, (++steps + 2)*
- sizeof(rpnp_t)))==NULL){
- return NULL;
- }
-
- else if((sscanf(expr,"%lf%n",&rpnp[steps].val,&pos) == 1)
- && (expr[pos] == ',')){
- rpnp[steps].op = OP_NUMBER;
- expr+=pos;
- }
-
-#define match_op(VV,VVV) \
- else if (strncmp(expr, #VVV, strlen(#VVV))==0 && \
- (expr[strlen(#VVV)] == ',' || expr[strlen(#VVV)] == '\0') ){ \
- rpnp[steps].op = VV; \
- expr+=strlen(#VVV); \
- }
-
- match_op(OP_ADD,+)
- match_op(OP_SUB,-)
- match_op(OP_MUL,*)
- match_op(OP_DIV,/)
- match_op(OP_MOD,%)
- match_op(OP_SIN,SIN)
- match_op(OP_COS,COS)
- match_op(OP_LOG,LOG)
- match_op(OP_FLOOR,FLOOR)
- match_op(OP_CEIL,CEIL)
- match_op(OP_EXP,EXP)
- match_op(OP_DUP,DUP)
- match_op(OP_EXC,EXC)
- match_op(OP_POP,POP)
- match_op(OP_LT,LT)
- match_op(OP_LE,LE)
- match_op(OP_GT,GT)
- match_op(OP_GE,GE)
- match_op(OP_EQ,EQ)
- match_op(OP_IF,IF)
- match_op(OP_MIN,MIN)
- match_op(OP_MAX,MAX)
- match_op(OP_LIMIT,LIMIT)
- /* order is important here ! .. match longest first */
- match_op(OP_UNKN,UNKN)
- match_op(OP_UN,UN)
- match_op(OP_NEGINF,NEGINF)
- match_op(OP_PREV,PREV)
- match_op(OP_INF,INF)
- match_op(OP_NOW,NOW)
- match_op(OP_LTIME,LTIME)
- match_op(OP_TIME,TIME)
-
-
-#undef match_op
-
-
- else if ((sscanf(expr,DEF_NAM_FMT "%n",
- vname,&pos) == 1)
- && ((rpnp[steps].ptr = find_var(im,vname)) != -1)){
- rpnp[steps].op = OP_VARIABLE;
- expr+=pos;
- }
-
- else {
- free(rpnp);
- return NULL;
- }
- if (*expr == 0)
- break;
- if (*expr == ',')
- expr++;
- else {
- free(rpnp);
- return NULL;
- }
- }
- rpnp[steps+1].op = OP_END;
- return rpnp;
-}
-
-/* figure out what the local timezone offset for any point in
- time was. Return it in seconds */
-
-int
-tzoffset( time_t now ){
- int gm_sec, gm_min, gm_hour, gm_yday, gm_year,
- l_sec, l_min, l_hour, l_yday, l_year;
- struct tm *t;
- int off;
- t = gmtime(&now);
- gm_sec = t->tm_sec;
- gm_min = t->tm_min;
- gm_hour = t->tm_hour;
- gm_yday = t->tm_yday;
- gm_year = t->tm_year;
- t = localtime(&now);
- l_sec = t->tm_sec;
- l_min = t->tm_min;
- l_hour = t->tm_hour;
- l_yday = t->tm_yday;
- l_year = t->tm_year;
- off = (l_sec-gm_sec)+(l_min-gm_min)*60+(l_hour-gm_hour)*3600;
- if ( l_yday > gm_yday || l_year > gm_year){
- off += 24*3600;
- } else if ( l_yday < gm_yday || l_year < gm_year){
- off -= 24*3600;
- }
-
- return off;
-}
-
-
-
-#define dc_stackblock 100
-
-/* run the rpn calculator on all the CDEF arguments */
-
+/* run the rpn calculator on all the VDEF and CDEF arguments */
int
data_calc( image_desc_t *im){
- int gdi,rpi;
+ int gdi;
int dataidx;
- long *steparray;
+ long *steparray, rpi;
int stepcnt;
time_t now;
- double *stack = NULL;
- long dc_stacksize = 0;
-
- for (gdi=0;gdi<im->gdes_c;gdi++){
- /* only GF_CDEF elements are of interest */
- if (im->gdes[gdi].gf != GF_CDEF)
- continue;
- im->gdes[gdi].ds_cnt = 1;
- im->gdes[gdi].ds = 0;
- im->gdes[gdi].data_first = 1;
- im->gdes[gdi].start = 0;
- im->gdes[gdi].end = 0;
- steparray=NULL;
- stepcnt = 0;
- dataidx=-1;
-
- /* find the variables in the expression. And calc the lowest
- common denominator of all step sizes of the data sources involved.
- this will be the step size for the cdef created data source*/
-
- for(rpi=0;im->gdes[gdi].rpnp[rpi].op != OP_END;rpi++){
- if(im->gdes[gdi].rpnp[rpi].op == OP_VARIABLE){
- long ptr = im->gdes[gdi].rpnp[rpi].ptr;
- if ((steparray = rrd_realloc(steparray, (++stepcnt+1)*sizeof(*steparray)))==NULL){
- rrd_set_error("realloc steparray");
- free(stack);
- return -1;
- };
-
+ rpnstack_t rpnstack;
- steparray[stepcnt-1] = im->gdes[ptr].step;
+ rpnstack_init(&rpnstack);
- /* adjust start and end of cdef (gdi) so that it runs from
- the latest start point to the earliest endpoint of any of the
- rras involved (ptr) */
-
- if(im->gdes[gdi].start < im->gdes[ptr].start)
- im->gdes[gdi].start = im->gdes[ptr].start;
-
- if(im->gdes[gdi].end == 0
- || im->gdes[gdi].end > im->gdes[ptr].end)
- im->gdes[gdi].end = im->gdes[ptr].end;
+ for (gdi=0;gdi<im->gdes_c;gdi++){
+ /* Look for GF_VDEF and GF_CDEF in the same loop,
+ * so CDEFs can use VDEFs and vice versa
+ */
+ switch (im->gdes[gdi].gf) {
+ case GF_VDEF:
+ /* A VDEF has no DS. This also signals other parts
+ * of rrdtool that this is a VDEF value, not a CDEF.
+ */
+ im->gdes[gdi].ds_cnt = 0;
+ if (vdef_calc(im,gdi)) {
+ rrd_set_error("Error processing VDEF '%s'"
+ ,im->gdes[gdi].vname
+ );
+ rpnstack_free(&rpnstack);
+ return -1;
+ }
+ break;
+ case GF_CDEF:
+ im->gdes[gdi].ds_cnt = 1;
+ im->gdes[gdi].ds = 0;
+ im->gdes[gdi].data_first = 1;
+ im->gdes[gdi].start = 0;
+ im->gdes[gdi].end = 0;
+ steparray=NULL;
+ stepcnt = 0;
+ dataidx=-1;
+
+ /* Find the variables in the expression.
+ * - VDEF variables are substituted by their values
+ * and the opcode is changed into OP_NUMBER.
+ * - CDEF variables are analized for their step size,
+ * the lowest common denominator of all the step
+ * sizes of the data sources involved is calculated
+ * and the resulting number is the step size for the
+ * resulting data source.
+ */
+ for(rpi=0;im->gdes[gdi].rpnp[rpi].op != OP_END;rpi++){
+ if(im->gdes[gdi].rpnp[rpi].op == OP_VARIABLE){
+ long ptr = im->gdes[gdi].rpnp[rpi].ptr;
+ if (im->gdes[ptr].ds_cnt == 0) {
+#if 0
+printf("DEBUG: inside CDEF '%s' processing VDEF '%s'\n",
+ im->gdes[gdi].vname,
+ im->gdes[ptr].vname);
+printf("DEBUG: value from vdef is %f\n",im->gdes[ptr].vf.val);
+#endif
+ im->gdes[gdi].rpnp[rpi].val = im->gdes[ptr].vf.val;
+ im->gdes[gdi].rpnp[rpi].op = OP_NUMBER;
+ } else {
+ if ((steparray = rrd_realloc(steparray, (++stepcnt+1)*sizeof(*steparray)))==NULL){
+ rrd_set_error("realloc steparray");
+ rpnstack_free(&rpnstack);
+ return -1;
+ };
+
+ steparray[stepcnt-1] = im->gdes[ptr].step;
+
+ /* adjust start and end of cdef (gdi) so
+ * that it runs from the latest start point
+ * to the earliest endpoint of any of the
+ * rras involved (ptr)
+ */
+ if(im->gdes[gdi].start < im->gdes[ptr].start)
+ im->gdes[gdi].start = im->gdes[ptr].start;
+
+ if(im->gdes[gdi].end == 0 ||
+ im->gdes[gdi].end > im->gdes[ptr].end)
+ im->gdes[gdi].end = im->gdes[ptr].end;
- /* store pointer to the first element of the rra providing
- data for variable, further save step size and data source count
- of this rra*/
- im->gdes[gdi].rpnp[rpi].data =
- im->gdes[ptr].data + im->gdes[ptr].ds;
- im->gdes[gdi].rpnp[rpi].step = im->gdes[ptr].step;
- im->gdes[gdi].rpnp[rpi].ds_cnt = im->gdes[ptr].ds_cnt;
- }
-
- }
- if(steparray == NULL){
- rrd_set_error("rpn expressions without variables are not supported");
- free(stack);
- return -1;
- }
- steparray[stepcnt]=0;
- /* now find the step for the result of the cdef. so that we land on
- each step in all of the variables rras */
-
- im->gdes[gdi].step = lcd(steparray);
-
-
- free(steparray);
-
- if((im->gdes[gdi].data = malloc(((im->gdes[gdi].end
- -im->gdes[gdi].start)
- / im->gdes[gdi].step +1)
+ /* store pointer to the first element of
+ * the rra providing data for variable,
+ * further save step size and data source
+ * count of this rra
+ */
+ im->gdes[gdi].rpnp[rpi].data = im->gdes[ptr].data;
+ im->gdes[gdi].rpnp[rpi].step = im->gdes[ptr].step;
+ im->gdes[gdi].rpnp[rpi].ds_cnt = im->gdes[ptr].ds_cnt;
+
+ /* backoff the *.data ptr; this is done so
+ * rpncalc() function doesn't have to treat
+ * the first case differently
+ */
+ } /* if ds_cnt != 0 */
+ } /* if OP_VARIABLE */
+ } /* loop through all rpi */
+
+ if(steparray == NULL){
+ rrd_set_error("rpn expressions without DEF"
+ " or CDEF variables are not supported");
+ rpnstack_free(&rpnstack);
+ return -1;
+ }
+ steparray[stepcnt]=0;
+ /* Now find the resulting step. All steps in all
+ * used RRAs have to be visited
+ */
+ im->gdes[gdi].step = lcd(steparray);
+ free(steparray);
+ if((im->gdes[gdi].data = malloc((
+ (im->gdes[gdi].end-im->gdes[gdi].start)
+ / im->gdes[gdi].step)
* sizeof(double)))==NULL){
- rrd_set_error("malloc im->gdes[gdi].data");
- free(stack);
- return -1;
- }
-
- /* step through the new cdef results array and calculate the values */
- for (now = im->gdes[gdi].start;
- now<=im->gdes[gdi].end;
- now += im->gdes[gdi].step){
- long stptr=-1;
- /* process each op from the rpn in turn */
- for (rpi=0;im->gdes[gdi].rpnp[rpi].op != OP_END;rpi++){
- if (stptr +5 > dc_stacksize){
- dc_stacksize += dc_stackblock;
- stack = rrd_realloc(stack,dc_stacksize*sizeof(*stack));
- if (stack==NULL){
- rrd_set_error("RPN stack overflow");
- return -1;
- }
+ rrd_set_error("malloc im->gdes[gdi].data");
+ rpnstack_free(&rpnstack);
+ return -1;
}
- switch (im->gdes[gdi].rpnp[rpi].op){
- case OP_NUMBER:
- stack[++stptr] = im->gdes[gdi].rpnp[rpi].val;
- break;
- case OP_VARIABLE:
- /* make sure we pull the correct value from the *.data array */
- /* adjust the pointer into the array acordingly. */
- if(now > im->gdes[gdi].start &&
- now % im->gdes[gdi].rpnp[rpi].step == 0){
- im->gdes[gdi].rpnp[rpi].data +=
- im->gdes[gdi].rpnp[rpi].ds_cnt;
- }
- stack[++stptr] = *im->gdes[gdi].rpnp[rpi].data;
- break;
- case OP_PREV:
- if (dataidx <= 0) {
- stack[++stptr] = DNAN;
- } else {
- stack[++stptr] = im->gdes[gdi].data[dataidx];
- }
- break;
- case OP_UNKN:
- stack[++stptr] = DNAN;
- break;
- case OP_INF:
- stack[++stptr] = DINF;
- break;
- case OP_NEGINF:
- stack[++stptr] = -DINF;
- break;
- case OP_NOW:
- stack[++stptr] = (double)time(NULL);
- break;
- case OP_TIME:
- stack[++stptr] = (double)now;
- break;
- case OP_LTIME:
- stack[++stptr] = (double)tzoffset(now)+(double)now;
- break;
- case OP_ADD:
- if(stptr<1){
- rrd_set_error("RPN stack underflow");
- free(stack);
- return -1;
- }
- stack[stptr-1] = stack[stptr-1] + stack[stptr];
- stptr--;
- break;
- case OP_SUB:
- if(stptr<1){
- rrd_set_error("RPN stack underflow");
- free(stack);
- return -1;
- }
- stack[stptr-1] = stack[stptr-1] - stack[stptr];
- stptr--;
- break;
- case OP_MUL:
- if(stptr<1){
- rrd_set_error("RPN stack underflow");
- free(stack);
- return -1;
- }
- stack[stptr-1] = stack[stptr-1] * stack[stptr];
- stptr--;
- break;
- case OP_DIV:
- if(stptr<1){
- rrd_set_error("RPN stack underflow");
- free(stack);
- return -1;
- }
- stack[stptr-1] = stack[stptr-1] / stack[stptr];
- stptr--;
- break;
- case OP_MOD:
- if(stptr<1){
- rrd_set_error("RPN stack underflow");
- free(stack);
- return -1;
- }
- stack[stptr-1] = fmod(stack[stptr-1],stack[stptr]);
- stptr--;
- break;
- case OP_SIN:
- if(stptr<0){
- rrd_set_error("RPN stack underflow");
- free(stack);
- return -1;
- }
- stack[stptr] = sin(stack[stptr]);
- break;
- case OP_COS:
- if(stptr<0){
- rrd_set_error("RPN stack underflow");
- free(stack);
- return -1;
- }
- stack[stptr] = cos(stack[stptr]);
- break;
- case OP_CEIL:
- if(stptr<0){
- rrd_set_error("RPN stack underflow");
- free(stack);
- return -1;
- }
- stack[stptr] = ceil(stack[stptr]);
- break;
- case OP_FLOOR:
- if(stptr<0){
- rrd_set_error("RPN stack underflow");
- free(stack);
- return -1;
- }
- stack[stptr] = floor(stack[stptr]);
- break;
- case OP_LOG:
- if(stptr<0){
- rrd_set_error("RPN stack underflow");
- free(stack);
- return -1;
- }
- stack[stptr] = log(stack[stptr]);
- break;
- case OP_DUP:
- if(stptr<0){
- rrd_set_error("RPN stack underflow");
- free(stack);
- return -1;
- }
- stack[stptr+1] = stack[stptr];
- stptr++;
- break;
- case OP_POP:
- if(stptr<0){
- rrd_set_error("RPN stack underflow");
- free(stack);
- return -1;
- }
- stptr--;
- break;
- case OP_EXC:
- if(stptr<1){
- rrd_set_error("RPN stack underflow");
- free(stack);
- return -1;
- } else {
- double dummy;
- dummy = stack[stptr] ;
- stack[stptr] = stack[stptr-1];
- stack[stptr-1] = dummy;
- }
- break;
- case OP_EXP:
- if(stptr<0){
- rrd_set_error("RPN stack underflow");
- free(stack);
- return -1;
- }
- stack[stptr] = exp(stack[stptr]);
- break;
- case OP_LT:
- if(stptr<1){
- rrd_set_error("RPN stack underflow");
- free(stack);
- return -1;
- }
- if (isnan(stack[stptr-1]) || isnan(stack[stptr]))
- stack[stptr-1] = 0.0;
- else
- stack[stptr-1] = stack[stptr-1] < stack[stptr] ? 1.0 : 0.0;
- stptr--;
- break;
- case OP_LE:
- if(stptr<1){
- rrd_set_error("RPN stack underflow");
- free(stack);
- return -1;
- }
- if (isnan(stack[stptr-1]) || isnan(stack[stptr]))
- stack[stptr-1] = 0.0;
- else
- stack[stptr-1] = stack[stptr-1] <= stack[stptr] ? 1.0 : 0.0;
- stptr--;
- break;
- case OP_GT:
- if(stptr<1){
- rrd_set_error("RPN stack underflow");
- free(stack);
- return -1;
- }
- if (isnan(stack[stptr-1]) || isnan(stack[stptr]))
- stack[stptr-1] = 0.0;
- else
- stack[stptr-1] = stack[stptr-1] > stack[stptr] ? 1.0 : 0.0;
- stptr--;
- break;
- case OP_GE:
- if(stptr<1){
- rrd_set_error("RPN stack underflow");
- free(stack);
- return -1;
- }
- if (isnan(stack[stptr-1]) || isnan(stack[stptr]))
- stack[stptr-1] = 0.0;
- else
- stack[stptr-1] = stack[stptr-1] >= stack[stptr] ? 1.0 : 0.0;
- stptr--;
- break;
- case OP_EQ:
- if(stptr<1){
- rrd_set_error("RPN stack underflow");
- free(stack);
- return -1;
- }
- if (isnan(stack[stptr-1]) || isnan(stack[stptr]))
- stack[stptr-1] = 0.0;
- else
- stack[stptr-1] = stack[stptr-1] == stack[stptr] ? 1.0 : 0.0;
- stptr--;
- break;
- case OP_IF:
- if(stptr<2){
- rrd_set_error("RPN stack underflow");
- free(stack);
- return -1;
- }
- stack[stptr-2] = stack[stptr-2] != 0.0 ? stack[stptr-1] : stack[stptr];
- stptr--;
- stptr--;
- break;
- case OP_MIN:
- if(stptr<1){
- rrd_set_error("RPN stack underflow");
- free(stack);
- return -1;
- }
- if (isnan(stack[stptr-1]))
- ;
- else if (isnan(stack[stptr]))
- stack[stptr-1] = stack[stptr];
- else if (stack[stptr-1] > stack[stptr])
- stack[stptr-1] = stack[stptr];
- stptr--;
- break;
- case OP_MAX:
- if(stptr<1){
- rrd_set_error("RPN stack underflow");
- free(stack);
- return -1;
- }
- if (isnan(stack[stptr-1]))
- ;
- else if (isnan(stack[stptr]))
- stack[stptr-1] = stack[stptr];
- else if (stack[stptr-1] < stack[stptr])
- stack[stptr-1] = stack[stptr];
- stptr--;
- break;
- case OP_LIMIT:
- if(stptr<2){
- rrd_set_error("RPN stack underflow");
- free(stack);
- return -1;
- }
- if (isnan(stack[stptr-2]))
- ;
- else if (isnan(stack[stptr-1]))
- stack[stptr-2] = stack[stptr-1];
- else if (isnan(stack[stptr]))
- stack[stptr-2] = stack[stptr];
- else if (stack[stptr-2] < stack[stptr-1])
- stack[stptr-2] = DNAN;
- else if (stack[stptr-2] > stack[stptr])
- stack[stptr-2] = DNAN;
- stptr-=2;
- break;
- case OP_UN:
- if(stptr<0){
- rrd_set_error("RPN stack underflow");
- free(stack);
+
+ /* Step through the new cdef results array and
+ * calculate the values
+ */
+ for (now = im->gdes[gdi].start + im->gdes[gdi].step;
+ now<=im->gdes[gdi].end;
+ now += im->gdes[gdi].step)
+ {
+ rpnp_t *rpnp = im -> gdes[gdi].rpnp;
+
+ /* 3rd arg of rpn_calc is for OP_VARIABLE lookups;
+ * in this case we are advancing by timesteps;
+ * we use the fact that time_t is a synonym for long
+ */
+ if (rpn_calc(rpnp,&rpnstack,(long) now,
+ im->gdes[gdi].data,++dataidx) == -1) {
+ /* rpn_calc sets the error string */
+ rpnstack_free(&rpnstack);
return -1;
- }
- stack[stptr] = isnan(stack[stptr]) ? 1.0 : 0.0;
- break;
- case OP_END:
- break;
- }
- }
- if(stptr!=0){
- rrd_set_error("RPN final stack size != 1");
- free(stack);
- return -1;
- }
- im->gdes[gdi].data[++dataidx] = stack[0];
+ }
+ } /* enumerate over time steps within a CDEF */
+ break;
+ default:
+ continue;
}
- }
- free(stack);
+ } /* enumerate over CDEFs */
+ rpnstack_free(&rpnstack);
return 0;
}
-#undef dc_stacksize
-
/* massage data so, that we get one value for each x coordinate in the graph */
int
data_proc( image_desc_t *im ){
(im->gdes[i].gf==GF_LINE2) ||
(im->gdes[i].gf==GF_LINE3) ||
(im->gdes[i].gf==GF_AREA) ||
+ (im->gdes[i].gf==GF_TICK) ||
(im->gdes[i].gf==GF_STACK)){
if((im->gdes[i].p_data = malloc((im->xsize +1)
* sizeof(rrd_value_t)))==NULL){
case GF_LINE2:
case GF_LINE3:
case GF_AREA:
+ case GF_TICK:
paintval = 0.0;
case GF_STACK:
vidx = im->gdes[ii].vidx;
value =
im->gdes[vidx].data[
- ((unsigned long)floor((double)
- (gr_time - im->gdes[vidx].start )
- / im->gdes[vidx].step)+1)
-
- /* added one because data was not being aligned properly
- this fixes it. We may also be having a problem in fetch ... */
-
- *im->gdes[vidx].ds_cnt
- +im->gdes[vidx].ds];
+ ((unsigned long)floor(
+ (double)(gr_time-im->gdes[vidx].start) / im->gdes[vidx].step
+ )
+ ) *im->gdes[vidx].ds_cnt
+ +im->gdes[vidx].ds];
if (! isnan(value)) {
paintval += value;
im->gdes[ii].p_data[i] = paintval;
- if (finite(paintval)){
+ /* GF_TICK: the data values are not relevant for min and max */
+ if (finite(paintval) && im->gdes[ii].gf != GF_TICK ){
if (isnan(minval) || paintval < minval)
minval = paintval;
if (isnan(maxval) || paintval > maxval)
case GF_VRULE:
case GF_DEF:
case GF_CDEF:
+ case GF_VDEF:
break;
}
}
{
long i,ii,validsteps;
double printval;
+ time_t printtime;
int graphelement = 0;
long vidx;
int max_ii;
return 0;
}
case GF_GPRINT:
+ /* PRINT and GPRINT can now print VDEF generated values.
+ * There's no need to do any calculations on them as these
+ * calculations were already made.
+ */
vidx = im->gdes[i].vidx;
- max_ii =((im->gdes[vidx].end
- - im->gdes[vidx].start)
- /im->gdes[vidx].step
- *im->gdes[vidx].ds_cnt);
- printval = DNAN;
- validsteps = 0;
- for(ii=im->gdes[vidx].ds+im->gdes[vidx].ds_cnt;
- ii < max_ii+im->gdes[vidx].ds_cnt;
- ii+=im->gdes[vidx].ds_cnt){
- if (! finite(im->gdes[vidx].data[ii]))
- continue;
- if (isnan(printval)){
- printval = im->gdes[vidx].data[ii];
- validsteps++;
- continue;
- }
+ if (im->gdes[vidx].gf==GF_VDEF) { /* simply use vals */
+ printval = im->gdes[vidx].vf.val;
+ printtime = im->gdes[vidx].vf.when;
+ } else { /* need to calculate max,min,avg etcetera */
+ max_ii =((im->gdes[vidx].end
+ - im->gdes[vidx].start)
+ / im->gdes[vidx].step
+ * im->gdes[vidx].ds_cnt);
+ printval = DNAN;
+ validsteps = 0;
+ for( ii=im->gdes[vidx].ds;
+ ii < max_ii;
+ ii+=im->gdes[vidx].ds_cnt){
+ if (! finite(im->gdes[vidx].data[ii]))
+ continue;
+ if (isnan(printval)){
+ printval = im->gdes[vidx].data[ii];
+ validsteps++;
+ continue;
+ }
- switch (im->gdes[i].cf){
- case CF_AVERAGE:
- validsteps++;
- printval += im->gdes[vidx].data[ii];
- break;
- case CF_MINIMUM:
- printval = min( printval, im->gdes[vidx].data[ii]);
- break;
- case CF_MAXIMUM:
- printval = max( printval, im->gdes[vidx].data[ii]);
- break;
- case CF_LAST:
- printval = im->gdes[vidx].data[ii];
+ switch (im->gdes[i].cf){
+ case CF_HWPREDICT:
+ case CF_DEVPREDICT:
+ case CF_DEVSEASONAL:
+ case CF_SEASONAL:
+ case CF_AVERAGE:
+ validsteps++;
+ printval += im->gdes[vidx].data[ii];
+ break;
+ case CF_MINIMUM:
+ printval = min( printval, im->gdes[vidx].data[ii]);
+ break;
+ case CF_FAILURES:
+ case CF_MAXIMUM:
+ printval = max( printval, im->gdes[vidx].data[ii]);
+ break;
+ case CF_LAST:
+ printval = im->gdes[vidx].data[ii];
+ }
}
- }
- if (im->gdes[i].cf == CF_AVERAGE) {
- if (validsteps > 1) {
- printval = (printval / validsteps);
+ if (im->gdes[i].cf==CF_AVERAGE || im->gdes[i].cf > CF_LAST) {
+ if (validsteps > 1) {
+ printval = (printval / validsteps);
+ }
}
- }
+ } /* prepare printval */
+
+ if (!strcmp(im->gdes[i].format,"%c")) { /* VDEF time print */
+ if (im->gdes[i].gf == GF_PRINT){
+ (*prdata)[prlines-2] = malloc((FMT_LEG_LEN+2)*sizeof(char));
+ sprintf((*prdata)[prlines-2],"%s (%lu)",
+ ctime(&printtime),printtime);
+ (*prdata)[prlines-1] = NULL;
+ } else {
+ sprintf(im->gdes[i].legend,"%s (%lu)",
+ ctime(&printtime),printtime);
+ graphelement = 1;
+ }
+ } else {
if ((percent_s = strstr(im->gdes[i].format,"%S")) != NULL) {
/* Magfact is set to -1 upon entry to print_calc. If it
* is still less than 0, then we need to run auto_scale.
printval /= magfact;
}
*(++percent_s) = 's';
- }
- else if (strstr(im->gdes[i].format,"%s") != NULL) {
+ } else if (strstr(im->gdes[i].format,"%s") != NULL) {
auto_scale(im,&printval,&si_symb,&magfact);
}
+
if (im->gdes[i].gf == GF_PRINT){
(*prdata)[prlines-2] = malloc((FMT_LEG_LEN+2)*sizeof(char));
if (bad_format(im->gdes[i].format)) {
#endif
graphelement = 1;
}
+ }
break;
case GF_COMMENT:
case GF_LINE1:
case GF_LINE2:
case GF_LINE3:
case GF_AREA:
+ case GF_TICK:
case GF_STACK:
case GF_HRULE:
case GF_VRULE:
break;
case GF_DEF:
case GF_CDEF:
+ case GF_VDEF:
break;
}
}
int lazy_check(image_desc_t *im){
FILE *fd = NULL;
- int size = 1;
+ int size = 1;
struct stat gifstat;
if (im->lazy == 0) return 0; /* no lazy option */
return 0; /* the file does not exist */
switch (im->imgformat) {
case IF_GIF:
- size = GifSize(fd,&(im->xgif),&(im->ygif));
- break;
+ size = GifSize(fd,&(im->xgif),&(im->ygif));
+ break;
case IF_PNG:
- size = PngSize(fd,&(im->xgif),&(im->ygif));
- break;
+ size = PngSize(fd,&(im->xgif),&(im->ygif));
+ break;
}
fclose(fd);
return size;
if(data_fetch(im)==-1)
return -1;
- /* evaluate CDEF operations ... */
+ /* evaluate VDEF and CDEF operations ... */
if(data_calc(im)==-1)
return -1;
switch(im->gdes[i].gf){
case GF_CDEF:
+ case GF_VDEF:
case GF_DEF:
case GF_PRINT:
case GF_GPRINT:
case GF_COMMENT:
case GF_HRULE:
case GF_VRULE:
- break;
+ break;
+ case GF_TICK:
+ for (ii = 0; ii < im->xsize; ii++)
+ {
+ if (!isnan(im->gdes[i].p_data[ii]) &&
+ im->gdes[i].p_data[ii] > 0.0)
+ {
+ /* generate a tick */
+ gdImageLine(gif, im -> xorigin + ii,
+ im -> yorigin - (im -> gdes[i].yrule * im -> ysize),
+ im -> xorigin + ii,
+ im -> yorigin,
+ im -> gdes[i].col.i);
+ }
+ }
+ break;
case GF_LINE1:
case GF_LINE2:
case GF_LINE3:
switch(im->gdes[i].gf){
case GF_HRULE:
+ if(isnan(im->gdes[i].yrule)) { /* fetch variable */
+ im->gdes[i].yrule = im->gdes[im->gdes[i].vidx].vf.val;
+ };
if(im->gdes[i].yrule >= im->minval
&& im->gdes[i].yrule <= im->maxval)
gdImageLine(gif,
im->gdes[i].col.i);
break;
case GF_VRULE:
- if(im->gdes[i].xrule >= im->start
- && im->gdes[i].xrule <= im->end)
- gdImageLine(gif,
+ if(im->gdes[i].xrule == 0) { /* fetch variable */
+ im->gdes[i].xrule = im->gdes[im->gdes[i].vidx].vf.when;
+ };
+ if(im->gdes[i].xrule >= im->start
+ && im->gdes[i].xrule <= im->end)
+ gdImageLine(gif,
xtr(im,im->gdes[i].xrule),im->yorigin,
xtr(im,im->gdes[i].xrule),im->yorigin-im->ysize,
im->gdes[i].col.i);
- break;
+ break;
default:
break;
}
return inp;
}
+/* Some surgery done on this function, it became ridiculously big.
+** Things moved:
+** - initializing now in rrd_graph_init()
+** - options parsing now in rrd_graph_options()
+** - script parsing now in rrd_graph_script()
+*/
int
rrd_graph(int argc, char **argv, char ***prdata, int *xsize, int *ysize)
{
-
image_desc_t im;
- int i;
- long long_tmp;
- time_t start_tmp=0,end_tmp=0;
- char scan_gtm[12],scan_mtm[12],scan_ltm[12],col_nam[12];
- char symname[100];
- unsigned int col_red,col_green,col_blue;
- long scancount;
- int linepass = 0; /* stack can only follow directly after LINE* AREA or STACK */
- struct time_value start_tv, end_tv;
- char *parsetime_error = NULL;
- int stroff;
+
+ rrd_graph_init(&im);
+
+ rrd_graph_options(argc,argv,&im);
+ if (rrd_test_error()) return -1;
+
+ if (strlen(argv[optind])>=MAXPATH) {
+ rrd_set_error("filename (including path) too long");
+ return -1;
+ }
+ strncpy(im.graphfile,argv[optind],MAXPATH-1);
+ im.graphfile[MAXPATH-1]='\0';
+
+ rrd_graph_script(argc,argv,&im);
+ if (rrd_test_error()) return -1;
+
+ /* Everything is now read and the actual work can start */
(*prdata)=NULL;
+ if (graph_paint(&im,prdata)==-1){
+ im_free(&im);
+ return -1;
+ }
- parsetime("end-24h", &start_tv);
- parsetime("now", &end_tv);
+ /* The image is generated and needs to be output.
+ ** Also, if needed, print a line with information about the image.
+ */
- im.xlab_user.minsec = -1;
- im.xgif=0;
- im.ygif=0;
- im.xsize = 400;
- im.ysize = 100;
- im.step = 0;
- im.ylegend[0] = '\0';
- im.title[0] = '\0';
- im.minval = DNAN;
- im.maxval = DNAN;
- im.interlaced = 0;
- im.unitsexponent= 9999;
- im.extra_flags= 0;
- im.rigid = 0;
- im.imginfo = NULL;
- im.lazy = 0;
- im.logarithmic = 0;
- im.ygridstep = DNAN;
- im.draw_x_grid = 1;
- im.draw_y_grid = 1;
- im.base = 1000;
- im.prt_c = 0;
- im.gdes_c = 0;
- im.gdes = NULL;
- im.imgformat = IF_GIF; /* we default to GIF output */
+ *xsize=im.xgif;
+ *ysize=im.ygif;
+ if (im.imginfo) {
+ char *filename;
+ if (!(*prdata)) {
+ /* maybe prdata is not allocated yet ... lets do it now */
+ if ((*prdata = calloc(2,sizeof(char *)))==NULL) {
+ rrd_set_error("malloc imginfo");
+ return -1;
+ };
+ }
+ if(((*prdata)[0] = malloc((strlen(im.imginfo)+200+strlen(im.graphfile))*sizeof(char)))
+ ==NULL){
+ rrd_set_error("malloc imginfo");
+ return -1;
+ }
+ filename=im.graphfile+strlen(im.graphfile);
+ while(filename > im.graphfile) {
+ if (*(filename-1)=='/' || *(filename-1)=='\\' ) break;
+ filename--;
+ }
+
+ sprintf((*prdata)[0],im.imginfo,filename,im.xgif,im.ygif);
+ }
+ im_free(&im);
+ return 0;
+}
+
+void
+rrd_graph_init(image_desc_t *im)
+{
+ int i;
+
+ im->xlab_user.minsec = -1;
+ im->xgif=0;
+ im->ygif=0;
+ im->xsize = 400;
+ im->ysize = 100;
+ im->step = 0;
+ im->ylegend[0] = '\0';
+ im->title[0] = '\0';
+ im->minval = DNAN;
+ im->maxval = DNAN;
+ im->interlaced = 0;
+ im->unitsexponent= 9999;
+ im->extra_flags= 0;
+ im->rigid = 0;
+ im->imginfo = NULL;
+ im->lazy = 0;
+ im->logarithmic = 0;
+ im->ygridstep = DNAN;
+ im->draw_x_grid = 1;
+ im->draw_y_grid = 1;
+ im->base = 1000;
+ im->prt_c = 0;
+ im->gdes_c = 0;
+ im->gdes = NULL;
+ im->imgformat = IF_GIF; /* we default to GIF output */
for(i=0;i<DIM(graph_col);i++)
- im.graph_col[i].red=-1;
-
-
+ im->graph_col[i].red=-1;
+}
+
+void
+rrd_graph_options(int argc, char *argv[],image_desc_t *im)
+{
+ int stroff;
+ char *parsetime_error = NULL;
+ char scan_gtm[12],scan_mtm[12],scan_ltm[12],col_nam[12];
+ time_t start_tmp=0,end_tmp=0;
+ long long_tmp;
+ struct time_value start_tv, end_tv;
+ unsigned int col_red,col_green,col_blue;
+
+ parsetime("end-24h", &start_tv);
+ parsetime("now", &end_tv);
+
while (1){
static struct option long_options[] =
{
int option_index = 0;
int opt;
-
+
opt = getopt_long(argc, argv,
"s:e:x:y:v:w:h:iu:l:rb:oc:t:f:a:z:g",
long_options, &option_index);
switch(opt) {
case 257:
- im.extra_flags |= ALTYGRID;
+ im->extra_flags |= ALTYGRID;
break;
case 258:
- im.extra_flags |= ALTAUTOSCALE;
+ im->extra_flags |= ALTAUTOSCALE;
break;
case 259:
- im.extra_flags |= ALTAUTOSCALE_MAX;
+ im->extra_flags |= ALTAUTOSCALE_MAX;
break;
case 'g':
- im.extra_flags |= NOLEGEND;
+ im->extra_flags |= NOLEGEND;
break;
case 260:
- im.unitsexponent = atoi(optarg);
+ im->unitsexponent = atoi(optarg);
break;
case 261:
- im.step = atoi(optarg);
+ im->step = atoi(optarg);
break;
case 's':
if ((parsetime_error = parsetime(optarg, &start_tv))) {
rrd_set_error( "start time: %s", parsetime_error );
- return -1;
+ return;
}
break;
case 'e':
if ((parsetime_error = parsetime(optarg, &end_tv))) {
rrd_set_error( "end time: %s", parsetime_error );
- return -1;
+ return;
}
break;
case 'x':
if(strcmp(optarg,"none") == 0){
- im.draw_x_grid=0;
+ im->draw_x_grid=0;
break;
};
if(sscanf(optarg,
"%10[A-Z]:%ld:%10[A-Z]:%ld:%10[A-Z]:%ld:%ld:%n",
scan_gtm,
- &im.xlab_user.gridst,
+ &im->xlab_user.gridst,
scan_mtm,
- &im.xlab_user.mgridst,
+ &im->xlab_user.mgridst,
scan_ltm,
- &im.xlab_user.labst,
- &im.xlab_user.precis,
+ &im->xlab_user.labst,
+ &im->xlab_user.precis,
&stroff) == 7 && stroff != 0){
- strncpy(im.xlab_form, optarg+stroff, sizeof(im.xlab_form) - 1);
- if((im.xlab_user.gridtm = tmt_conv(scan_gtm)) == -1){
+ strncpy(im->xlab_form, optarg+stroff, sizeof(im->xlab_form) - 1);
+ if((im->xlab_user.gridtm = tmt_conv(scan_gtm)) == -1){
rrd_set_error("unknown keyword %s",scan_gtm);
- return -1;
- } else if ((im.xlab_user.mgridtm = tmt_conv(scan_mtm)) == -1){
+ return;
+ } else if ((im->xlab_user.mgridtm = tmt_conv(scan_mtm)) == -1){
rrd_set_error("unknown keyword %s",scan_mtm);
- return -1;
- } else if ((im.xlab_user.labtm = tmt_conv(scan_ltm)) == -1){
+ return;
+ } else if ((im->xlab_user.labtm = tmt_conv(scan_ltm)) == -1){
rrd_set_error("unknown keyword %s",scan_ltm);
- return -1;
+ return;
}
- im.xlab_user.minsec = 1;
- im.xlab_user.stst = im.xlab_form;
+ im->xlab_user.minsec = 1;
+ im->xlab_user.stst = im->xlab_form;
} else {
rrd_set_error("invalid x-grid format");
- return -1;
+ return;
}
break;
case 'y':
if(strcmp(optarg,"none") == 0){
- im.draw_y_grid=0;
+ im->draw_y_grid=0;
break;
};
if(sscanf(optarg,
"%lf:%d",
- &im.ygridstep,
- &im.ylabfact) == 2) {
- if(im.ygridstep<=0){
+ &im->ygridstep,
+ &im->ylabfact) == 2) {
+ if(im->ygridstep<=0){
rrd_set_error("grid step must be > 0");
- return -1;
- } else if (im.ylabfact < 1){
+ return;
+ } else if (im->ylabfact < 1){
rrd_set_error("label factor must be > 0");
- return -1;
+ return;
}
} else {
rrd_set_error("invalid y-grid format");
- return -1;
+ return;
}
break;
case 'v':
- strncpy(im.ylegend,optarg,150);
- im.ylegend[150]='\0';
+ strncpy(im->ylegend,optarg,150);
+ im->ylegend[150]='\0';
break;
case 'u':
- im.maxval = atof(optarg);
+ im->maxval = atof(optarg);
break;
case 'l':
- im.minval = atof(optarg);
+ im->minval = atof(optarg);
break;
case 'b':
- im.base = atol(optarg);
- if(im.base != 1024 && im.base != 1000 ){
+ im->base = atol(optarg);
+ if(im->base != 1024 && im->base != 1000 ){
rrd_set_error("the only sensible value for base apart from 1000 is 1024");
- return -1;
+ return;
}
break;
case 'w':
long_tmp = atol(optarg);
if (long_tmp < 10) {
rrd_set_error("width below 10 pixels");
- return -1;
+ return;
}
- im.xsize = long_tmp;
+ im->xsize = long_tmp;
break;
case 'h':
long_tmp = atol(optarg);
if (long_tmp < 10) {
rrd_set_error("height below 10 pixels");
- return -1;
+ return;
}
- im.ysize = long_tmp;
+ im->ysize = long_tmp;
break;
case 'i':
- im.interlaced = 1;
+ im->interlaced = 1;
break;
case 'r':
- im.rigid = 1;
+ im->rigid = 1;
break;
case 'f':
- im.imginfo = optarg;
+ im->imginfo = optarg;
break;
case 'a':
- if((im.imgformat = if_conv(optarg)) == -1) {
+ if((im->imgformat = if_conv(optarg)) == -1) {
rrd_set_error("unsupported graphics format '%s'",optarg);
- return -1;
+ return;
}
break;
case 'z':
- im.lazy = 1;
+ im->lazy = 1;
break;
case 'o':
- im.logarithmic = 1;
- if (isnan(im.minval))
- im.minval=1;
+ im->logarithmic = 1;
+ if (isnan(im->minval))
+ im->minval=1;
break;
case 'c':
if(sscanf(optarg,
col_nam,&col_red,&col_green,&col_blue) == 4){
int ci;
if((ci=grc_conv(col_nam)) != -1){
- im.graph_col[ci].red=col_red;
- im.graph_col[ci].green=col_green;
- im.graph_col[ci].blue=col_blue;
+ im->graph_col[ci].red=col_red;
+ im->graph_col[ci].green=col_green;
+ im->graph_col[ci].blue=col_blue;
} else {
rrd_set_error("invalid color name '%s'",col_nam);
}
} else {
rrd_set_error("invalid color def format");
- return -1;
+ return;
}
break;
case 't':
- strncpy(im.title,optarg,150);
- im.title[150]='\0';
+ strncpy(im->title,optarg,150);
+ im->title[150]='\0';
break;
case '?':
rrd_set_error("unknown option '%c'", optopt);
else
rrd_set_error("unknown option '%s'",argv[optind-1]);
- return -1;
+ return;
}
}
if (optind >= argc) {
rrd_set_error("missing filename");
- return -1;
+ return;
}
- if (im.logarithmic == 1 && (im.minval <= 0 || isnan(im.minval))){
+ if (im->logarithmic == 1 && (im->minval <= 0 || isnan(im->minval))){
rrd_set_error("for a logarithmic yaxis you must specify a lower-limit > 0");
- return -1;
+ return;
}
- strncpy(im.graphfile,argv[optind],MAXPATH-1);
- im.graphfile[MAXPATH-1]='\0';
-
if (proc_start_end(&start_tv,&end_tv,&start_tmp,&end_tmp) == -1){
- return -1;
+ /* error string is set in parsetime.c */
+ return;
}
if (start_tmp < 3600*24*365*10){
rrd_set_error("the first entry to fetch should be after 1980 (%ld)",start_tmp);
- return -1;
+ return;
}
if (end_tmp < start_tmp) {
rrd_set_error("start (%ld) should be less than end (%ld)",
start_tmp, end_tmp);
- return -1;
+ return;
}
- im.start = start_tmp;
- im.end = end_tmp;
+ im->start = start_tmp;
+ im->end = end_tmp;
+}
+
+int
+rrd_graph_check_vname(image_desc_t *im, char *varname, char *err)
+{
+ if ((im->gdes[im->gdes_c-1].vidx=find_var(im,varname))==-1) {
+ im_free(im);
+ rrd_set_error("Unknown variable '%s' in %s",varname,err);
+ return -1;
+ }
+ return 0;
+}
+int
+rrd_graph_check_CF(image_desc_t *im, char *symname, char *err)
+{
+ if ((im->gdes[im->gdes_c-1].cf=cf_conv(symname))==-1) {
+ im_free(im);
+ rrd_set_error("Unknown CF '%s' in %s",symname,err);
+ return -1;
+ }
+ return 0;
+}
+
+void
+rrd_graph_script(int argc, char *myarg[], image_desc_t *im)
+{
+ int i;
+ char symname[100];
+ unsigned int col_red,col_green,col_blue;
+ long scancount;
+ int linepass = 0; /* stack can only follow directly after LINE* AREA or STACK */
+
+/* All code worked on "argv[i]", it made sense to formalize this
+** and use "arg" instead.
+**
+** The same can be said for "im->gdes[im->gdes_c-1]" which
+** has been changed into a simple "gdp".
+*/
-
for(i=optind+1;i<argc;i++){
- int argstart=0;
- int strstart=0;
- char varname[30],*rpnex;
- gdes_alloc(&im);
- if(sscanf(argv[i],"%10[A-Z0-9]:%n",symname,&argstart)==1){
- if((im.gdes[im.gdes_c-1].gf=gf_conv(symname))==-1){
- im_free(&im);
+ char *arg=myarg[i];
+ int argstart=0;
+ int strstart=0;
+ char varname[MAX_VNAME_LEN+1],*rpnex;
+ graph_desc_t *gdp;
+
+ gdes_alloc(im);
+ gdp=&im->gdes[im->gdes_c-1];
+
+ if(sscanf(arg,"%10[A-Z0-9]:%n",symname,&argstart)==1){
+ if((gdp->gf=gf_conv(symname))==-1){
+ im_free(im);
rrd_set_error("unknown function '%s'",symname);
- return -1;
+ return;
}
} else {
- rrd_set_error("can't parse '%s'",argv[i]);
- im_free(&im);
- return -1;
+ rrd_set_error("can't parse '%s'",arg);
+ im_free(im);
+ return;
}
- /* reset linepass if a non LINE/STACK/AREA operator gets parsed
-
- if (im.gdes[im.gdes_c-1].gf != GF_LINE1 &&
- im.gdes[im.gdes_c-1].gf != GF_LINE2 &&
- im.gdes[im.gdes_c-1].gf != GF_LINE3 &&
- im.gdes[im.gdes_c-1].gf != GF_AREA &&
- im.gdes[im.gdes_c-1].gf != GF_STACK) {
- linepass = 0;
- }
- */
-
- switch(im.gdes[im.gdes_c-1].gf){
+ switch(gdp->gf){
case GF_PRINT:
- im.prt_c++;
+ im->prt_c++;
case GF_GPRINT:
- if(sscanf(
- &argv[i][argstart],
- "%29[^#:]:" CF_NAM_FMT ":%n",
- varname,symname,&strstart) == 2){
- scan_for_col(&argv[i][argstart+strstart],FMT_LEG_LEN,im.gdes[im.gdes_c-1].format);
- if((im.gdes[im.gdes_c-1].vidx=find_var(&im,varname))==-1){
- im_free(&im);
- rrd_set_error("unknown variable '%s'",varname);
- return -1;
- }
- if((im.gdes[im.gdes_c-1].cf=cf_conv(symname))==-1){
- im_free(&im);
- return -1;
- }
-
- } else {
- im_free(&im);
- rrd_set_error("can't parse '%s'",&argv[i][argstart]);
- return -1;
- }
+ strstart=0;
+ sscanf(&arg[argstart], DEF_NAM_FMT ":%n"
+ ,varname
+ ,&strstart
+ );
+
+ if (strstart==0) {
+ im_free(im);
+ rrd_set_error("can't parse vname in '%s'",&arg[argstart]);
+ return;
+ };
+
+ if (rrd_graph_check_vname(im,varname,arg)) return;
+ else {
+ int n=0;
+
+ sscanf(&arg[argstart+strstart],CF_NAM_FMT ":%n"
+ ,symname
+ ,&n
+ );
+ if (im->gdes[gdp->vidx].gf==GF_VDEF) {
+ /* No consolidation function should be present */
+ if (n != 0) {
+ rrd_set_error("(G)PRINT of VDEF needs no CF");
+ im_free(im);
+ return;
+ }
+ } else {
+ /* A consolidation function should follow */
+ if (n==0) {
+ im_free(im);
+ rrd_set_error("Missing or incorrect CF in (G)PRINTing '%s' (%s)",varname,&arg[argstart]);
+ return;
+ };
+ if (rrd_graph_check_CF(im,symname,arg)) return;
+ strstart+=n;
+ };
+ };
+
+ scan_for_col(&arg[argstart+strstart],FMT_LEG_LEN,gdp->format);
break;
case GF_COMMENT:
- if(strlen(&argv[i][argstart])>FMT_LEG_LEN) argv[i][argstart+FMT_LEG_LEN-3]='\0' ;
- strcpy(im.gdes[im.gdes_c-1].legend, &argv[i][argstart]);
+ if(strlen(&arg[argstart])>FMT_LEG_LEN) arg[argstart+FMT_LEG_LEN-3]='\0' ;
+ strcpy(gdp->legend, &arg[argstart]);
break;
case GF_HRULE:
- if(sscanf(
- &argv[i][argstart],
- "%lf#%2x%2x%2x:%n",
- &im.gdes[im.gdes_c-1].yrule,
- &col_red,&col_green,&col_blue,
- &strstart) >= 4){
- im.gdes[im.gdes_c-1].col.red = col_red;
- im.gdes[im.gdes_c-1].col.green = col_green;
- im.gdes[im.gdes_c-1].col.blue = col_blue;
- if(strstart <= 0){
- im.gdes[im.gdes_c-1].legend[0] = '\0';
- } else {
- scan_for_col(&argv[i][argstart+strstart],FMT_LEG_LEN,im.gdes[im.gdes_c-1].legend);
+ /* scan for either "HRULE:vname#..." or "HRULE:num#..."
+ *
+ * If a vname is used, the value NaN is set; this is catched
+ * when graphing. Setting value NaN from the script is not
+ * permitted
+ */
+ strstart=0;
+ sscanf(&arg[argstart], "%lf#%n"
+ ,&im->gdes[im->gdes_c-1].yrule
+ ,&strstart
+ );
+ if (strstart==0) { /* no number, should be vname */
+ sscanf(&arg[argstart], DEF_NAM_FMT "#%n"
+ ,varname
+ ,&strstart
+ );
+ if (strstart) {
+ gdp->yrule = DNAN;/* signal use of vname */
+ if (rrd_graph_check_vname(im,varname,arg)) return;
+ if(im->gdes[gdp->vidx].gf != GF_VDEF) {
+ im_free(im);
+ rrd_set_error("Only VDEF is allowed in HRULE",varname);
+ return;
+ }
}
+ };
+ if (strstart==0) {
+ im_free(im);
+ rrd_set_error("can't parse '%s'",&arg[argstart]);
+ return;
} else {
- im_free(&im);
- rrd_set_error("can't parse '%s'",&argv[i][argstart]);
- return -1;
- }
+ int n=0;
+ if(sscanf(
+ &arg[argstart+strstart],
+ "%2x%2x%2x:%n",
+ &col_red,
+ &col_green,
+ &col_blue,
+ &n)>=3) {
+ gdp->col.red = col_red;
+ gdp->col.green = col_green;
+ gdp->col.blue = col_blue;
+ if (n==0) {
+ gdp->legend[0] = '\0';
+ } else {
+ scan_for_col(&arg[argstart+strstart+n],FMT_LEG_LEN,gdp->legend);
+ }
+ } else {
+ im_free(im);
+ rrd_set_error("can't parse '%s'",&arg[argstart]);
+ return;
+ }
+ }
+
break;
case GF_VRULE:
- if(sscanf(
- &argv[i][argstart],
- "%lu#%2x%2x%2x:%n",
- &im.gdes[im.gdes_c-1].xrule,
+ /* scan for either "VRULE:vname#..." or "VRULE:num#..."
+ *
+ * If a vname is used, the value 0 is set; this is catched
+ * when graphing. Setting value 0 from the script is not
+ * permitted
+ */
+ strstart=0;
+ sscanf(&arg[argstart], "%lu#%n"
+ ,(long unsigned int *)&gdp->xrule,&strstart);
+ if (strstart==0) { /* no number, should be vname */
+ sscanf(&arg[argstart], DEF_NAM_FMT "#%n"
+ ,varname
+ ,&strstart
+ );
+ if (strstart!=0) { /* vname matched */
+ gdp->xrule = 0;/* signal use of vname */
+ if (rrd_graph_check_vname(im,varname,arg)) return;
+ if(im->gdes[gdp->vidx].gf != GF_VDEF) {
+ im_free(im);
+ rrd_set_error("Only VDEF is allowed in VRULE",varname);
+ return;
+ }
+ }
+ } else {
+ if (gdp->xrule==0)
+ strstart=0;
+ }
+
+ if (strstart==0) {
+ im_free(im);
+ rrd_set_error("can't parse '%s'",&arg[argstart]);
+ return;
+ } else {
+ int n=0;
+ if(sscanf(
+ &arg[argstart+strstart],
+ "%2x%2x%2x:%n",
+ &col_red,
+ &col_green,
+ &col_blue,
+ &n)>=3) {
+ gdp->col.red = col_red;
+ gdp->col.green = col_green;
+ gdp->col.blue = col_blue;
+ if (n==0) {
+ gdp->legend[0] = '\0';
+ } else {
+ scan_for_col(&arg[argstart+strstart+n],FMT_LEG_LEN,gdp->legend);
+ }
+ } else {
+ im_free(im);
+ rrd_set_error("can't parse '%s'",&arg[argstart]);
+ return;
+ }
+ }
+ break;
+ case GF_TICK:
+ if((scancount=sscanf(
+ &arg[argstart],
+ "%29[^:#]#%2x%2x%2x:%lf:%n",
+ varname,
&col_red,
&col_green,
&col_blue,
- &strstart) >= 4){
- im.gdes[im.gdes_c-1].col.red = col_red;
- im.gdes[im.gdes_c-1].col.green = col_green;
- im.gdes[im.gdes_c-1].col.blue = col_blue;
- if(strstart <= 0){
- im.gdes[im.gdes_c-1].legend[0] = '\0';
+ &(gdp->yrule),
+ &strstart))>=1)
+ {
+ gdp->col.red = col_red;
+ gdp->col.green = col_green;
+ gdp->col.blue = col_blue;
+ if(strstart <= 0){
+ gdp->legend[0] = '\0';
} else {
- scan_for_col(&argv[i][argstart+strstart],FMT_LEG_LEN,im.gdes[im.gdes_c-1].legend);
+ scan_for_col(&arg[argstart+strstart],FMT_LEG_LEN,gdp->legend);
}
- } else {
- im_free(&im);
- rrd_set_error("can't parse '%s'",&argv[i][argstart]);
- return -1;
- }
- break;
+ if (rrd_graph_check_vname(im,varname,arg)) return;
+ if (gdp->yrule <= 0.0 || gdp->yrule > 1.0)
+ {
+ im_free(im);
+ rrd_set_error("Tick mark scaling factor out of range");
+ return;
+ }
+ if (scancount < 4)
+ gdp->col.red = -1;
+ if (scancount < 5)
+ /* default tick marks: 10% of the y-axis */
+ gdp->yrule = 0.1;
+
+ } else {
+ im_free(im);
+ rrd_set_error("can't parse '%s'",&arg[argstart]);
+ return;
+ } /* endif sscanf */
+ break;
case GF_STACK:
if(linepass == 0){
- im_free(&im);
+ im_free(im);
rrd_set_error("STACK must follow AREA, LINE or STACK");
- return -1;
+ return;
}
case GF_LINE1:
case GF_LINE2:
case GF_AREA:
linepass = 1;
if((scancount=sscanf(
- &argv[i][argstart],
+ &arg[argstart],
"%29[^:#]#%2x%2x%2x:%n",
varname,
&col_red,
&col_green,
&col_blue,
&strstart))>=1){
- im.gdes[im.gdes_c-1].col.red = col_red;
- im.gdes[im.gdes_c-1].col.green = col_green;
- im.gdes[im.gdes_c-1].col.blue = col_blue;
+ gdp->col.red = col_red;
+ gdp->col.green = col_green;
+ gdp->col.blue = col_blue;
if(strstart <= 0){
- im.gdes[im.gdes_c-1].legend[0] = '\0';
+ gdp->legend[0] = '\0';
} else {
- scan_for_col(&argv[i][argstart+strstart],FMT_LEG_LEN,im.gdes[im.gdes_c-1].legend);
+ scan_for_col(&arg[argstart+strstart],FMT_LEG_LEN,gdp->legend);
}
- if((im.gdes[im.gdes_c-1].vidx=find_var(&im,varname))==-1){
- im_free(&im);
- rrd_set_error("unknown variable '%s'",varname);
- return -1;
- }
+ if (rrd_graph_check_vname(im,varname,arg)) return;
if (scancount < 4)
- im.gdes[im.gdes_c-1].col.red = -1;
-
+ gdp->col.red = -1;
} else {
- im_free(&im);
- rrd_set_error("can't parse '%s'",&argv[i][argstart]);
- return -1;
+ im_free(im);
+ rrd_set_error("can't parse '%s'",&arg[argstart]);
+ return;
}
break;
case GF_CDEF:
- if((rpnex = malloc(strlen(&argv[i][argstart])*sizeof(char)))==NULL){
+ if((rpnex = malloc(strlen(&arg[argstart])*sizeof(char)))==NULL){
+ free(im);
rrd_set_error("malloc for CDEF");
- return -1;
+ return;
}
- if(sscanf(
- &argv[i][argstart],
- DEF_NAM_FMT "=%[^: ]",
- im.gdes[im.gdes_c-1].vname,
- rpnex) != 2){
- im_free(&im);
- free(rpnex);
- rrd_set_error("can't parse CDEF '%s'",&argv[i][argstart]);
- return -1;
+ strstart=parse_vname1(&arg[argstart],im,"CDEF");
+ argstart+=strstart;
+ /* parse_vname1() did free(im) and rrd_set_error() */
+ if (strstart==0) return;
+
+ strstart=0;
+ sscanf(&arg[argstart],"%[^: ]%n",rpnex,&strstart);
+ if (strstart==0) {
+ rrd_set_error("can't parse RPN in CDEF:%s=%s",
+ gdp->vname,
+ &arg[argstart]);
+ free(im);
+ return;
}
- /* checking for duplicate DEF CDEFS */
- if(find_var(&im,im.gdes[im.gdes_c-1].vname) != -1){
- im_free(&im);
- rrd_set_error("duplicate variable '%s'",
- im.gdes[im.gdes_c-1].vname);
- return -1;
- }
- if((im.gdes[im.gdes_c-1].rpnp = str2rpn(&im,rpnex))== NULL){
+ if((gdp->rpnp =
+ rpn_parse((void*)im,rpnex,&find_var_wrapper))== NULL){
rrd_set_error("invalid rpn expression '%s'", rpnex);
- im_free(&im);
- return -1;
+ im_free(im);
+ return;
}
free(rpnex);
break;
+ case GF_VDEF:
+ strstart=parse_vname1(&arg[argstart],im,"VDEF");
+ argstart+=strstart;
+ /* parse_vname1() did free(im) and rrd_set_error() */
+ if (strstart==0) return;
+
+ strstart=0;
+ sscanf(&arg[argstart],DEF_NAM_FMT ",%n",varname,&strstart);
+ if (strstart==0) {
+ im_free(im);
+ rrd_set_error("Cannot parse '%s' in VDEF '%s'",
+ &arg[argstart],
+ gdp->vname);
+ return;
+ }
+ if (rrd_graph_check_vname(im,varname,arg)) return;
+ if ( im->gdes[gdp->vidx].gf != GF_DEF
+ && im->gdes[gdp->vidx].gf != GF_CDEF) {
+ rrd_set_error("variable '%s' not DEF nor CDEF in VDEF '%s'",
+ varname,gdp->vname);
+ im_free(im);
+ return;
+ };
+
+ /* parsed upto and including the first comma. Now
+ * see what function is requested. This function
+ * sets the error string.
+ */
+ if (vdef_parse(gdp,&arg[argstart+strstart])<0) {
+ im_free(im);
+ return;
+ };
+ break;
case GF_DEF:
- if (sscanf(
- &argv[i][argstart],
- DEF_NAM_FMT "=%n",
- im.gdes[im.gdes_c-1].vname,
- &strstart)== 1 && strstart){ /* is the = did not match %n returns 0 */
- if(sscanf(&argv[i][argstart
- +strstart
- +scan_for_col(&argv[i][argstart+strstart],
- MAXPATH,im.gdes[im.gdes_c-1].rrd)],
- ":" DS_NAM_FMT ":" CF_NAM_FMT,
- im.gdes[im.gdes_c-1].ds_nam,
- symname) != 2){
- im_free(&im);
- rrd_set_error("can't parse DEF '%s' -2",&argv[i][argstart]);
- return -1;
- }
- } else {
- im_free(&im);
- rrd_set_error("can't parse DEF '%s'",&argv[i][argstart]);
- return -1;
+ strstart=parse_vname1(&arg[argstart],im,"DEF");
+ argstart+=strstart;
+ /* parse_vname1() did free(im) and rrd_set_error() */
+ if (strstart==0) return;
+
+ argstart+=scan_for_col(&arg[argstart],MAXPATH,gdp->rrd);
+ if(sscanf(&arg[argstart], ":" DS_NAM_FMT ":" CF_NAM_FMT,
+ gdp->ds_nam, symname) != 2){
+ im_free(im);
+ rrd_set_error("can't parse DEF '%s' -2",&arg[argstart]);
+ return;
}
- /* checking for duplicate DEF CDEFS */
- if (find_var(&im,im.gdes[im.gdes_c-1].vname) != -1){
- im_free(&im);
- rrd_set_error("duplicate variable '%s'",
- im.gdes[im.gdes_c-1].vname);
- return -1;
- }
- if((im.gdes[im.gdes_c-1].cf=cf_conv(symname))==-1){
- im_free(&im);
- rrd_set_error("unknown cf '%s'",symname);
- return -1;
- }
+ if (rrd_graph_check_CF(im,symname,arg)) return;
break;
}
-
}
- if (im.gdes_c==0){
+ if (im->gdes_c==0){
rrd_set_error("can't make a graph without contents");
- im_free(&im);
- return(-1);
+ im_free(im);
+ return;
}
-
- /* parse rest of arguments containing information on what to draw*/
- if (graph_paint(&im,prdata)==-1){
- im_free(&im);
- return -1;
- }
-
- *xsize=im.xgif;
- *ysize=im.ygif;
- if (im.imginfo){
- char *filename;
- if (! (*prdata)) {
- /* maybe prdata is not allocated yet ... lets do it now */
- if((*prdata = calloc(2,sizeof(char *)))==NULL){
- rrd_set_error("malloc imginfo");
- return -1;
- };
- }
- if(((*prdata)[0] = malloc((strlen(im.imginfo)+200+strlen(im.graphfile))*sizeof(char)))
- ==NULL){
- rrd_set_error("malloc imginfo");
- return -1;
- }
- filename=im.graphfile+strlen(im.graphfile);
- while(filename > im.graphfile){
- if (*(filename-1)=='/' || *(filename-1)=='\\' ) break;
- filename--;
- }
-
- sprintf((*prdata)[0],im.imginfo,filename,im.xgif,im.ygif);
- }
- im_free(&im);
- return 0;
}
+
int bad_format(char *fmt) {
char *ptr;
}
return 0;
}
+int
+vdef_parse(gdes,str)
+struct graph_desc_t *gdes;
+char *str;
+{
+ /* A VDEF currently is either "func" or "param,func"
+ * so the parsing is rather simple. Change if needed.
+ */
+ double param;
+ char func[30];
+ int n;
+
+ n=0;
+ sscanf(str,"%le,%29[A-Z]%n",¶m,func,&n);
+ if (n==strlen(str)) { /* matched */
+ ;
+ } else {
+ n=0;
+ sscanf(str,"%29[A-Z]%n",func,&n);
+ if (n==strlen(str)) { /* matched */
+ param=DNAN;
+ } else {
+ rrd_set_error("Unknown function string '%s' in VDEF '%s'"
+ ,str
+ ,gdes->vname
+ );
+ return -1;
+ }
+ }
+ if (!strcmp("PERCENT",func)) gdes->vf.op = VDEF_PERCENT;
+ else if (!strcmp("MAXIMUM",func)) gdes->vf.op = VDEF_MAXIMUM;
+ else if (!strcmp("AVERAGE",func)) gdes->vf.op = VDEF_AVERAGE;
+ else if (!strcmp("MINIMUM",func)) gdes->vf.op = VDEF_MINIMUM;
+ else if (!strcmp("TOTAL", func)) gdes->vf.op = VDEF_TOTAL;
+ else if (!strcmp("FIRST", func)) gdes->vf.op = VDEF_FIRST;
+ else if (!strcmp("LAST", func)) gdes->vf.op = VDEF_LAST;
+ else {
+ rrd_set_error("Unknown function '%s' in VDEF '%s'\n"
+ ,func
+ ,gdes->vname
+ );
+ return -1;
+ };
+
+ switch (gdes->vf.op) {
+ case VDEF_PERCENT:
+ if (isnan(param)) { /* no parameter given */
+ rrd_set_error("Function '%s' needs parameter in VDEF '%s'\n"
+ ,func
+ ,gdes->vname
+ );
+ return -1;
+ };
+ if (param>=0.0 && param<=100.0) {
+ gdes->vf.param = param;
+ gdes->vf.val = DNAN; /* undefined */
+ gdes->vf.when = 0; /* undefined */
+ } else {
+ rrd_set_error("Parameter '%f' out of range in VDEF '%s'\n"
+ ,param
+ ,gdes->vname
+ );
+ return -1;
+ };
+ break;
+ case VDEF_MAXIMUM:
+ case VDEF_AVERAGE:
+ case VDEF_MINIMUM:
+ case VDEF_TOTAL:
+ case VDEF_FIRST:
+ case VDEF_LAST:
+ if (isnan(param)) {
+ gdes->vf.param = DNAN;
+ gdes->vf.val = DNAN;
+ gdes->vf.when = 0;
+ } else {
+ rrd_set_error("Function '%s' needs no parameter in VDEF '%s'\n"
+ ,func
+ ,gdes->vname
+ );
+ return -1;
+ };
+ break;
+ };
+ return 0;
+}
+int
+vdef_calc(im,gdi)
+image_desc_t *im;
+int gdi;
+{
+ graph_desc_t *src,*dst;
+ rrd_value_t *data;
+ long step,steps;
+
+ dst = &im->gdes[gdi];
+ src = &im->gdes[dst->vidx];
+ data = src->data + src->ds;
+ steps = (src->end - src->start) / src->step;
+
+#if 0
+printf("DEBUG: start == %lu, end == %lu, %lu steps\n"
+ ,src->start
+ ,src->end
+ ,steps
+ );
+#endif
+
+ switch (dst->vf.op) {
+ case VDEF_PERCENT: {
+ rrd_value_t * array;
+ int field;
+
+
+ if ((array = malloc(steps*sizeof(double)))==NULL) {
+ rrd_set_error("malloc VDEV_PERCENT");
+ return -1;
+ }
+ for (step=0;step < steps; step++) {
+ array[step]=data[step*src->ds_cnt];
+ }
+ qsort(array,step,sizeof(double),vdef_percent_compar);
+
+ field = (steps-1)*dst->vf.param/100;
+ dst->vf.val = array[field];
+ dst->vf.when = 0; /* no time component */
+#if 0
+for(step=0;step<steps;step++)
+printf("DEBUG: %3li:%10.2f %c\n",step,array[step],step==field?'*':' ');
+#endif
+ }
+ break;
+ case VDEF_MAXIMUM:
+ step=0;
+ while (step != steps && isnan(data[step*src->ds_cnt])) step++;
+ if (step == steps) {
+ dst->vf.val = DNAN;
+ dst->vf.when = 0;
+ } else {
+ dst->vf.val = data[step*src->ds_cnt];
+ dst->vf.when = src->start + (step+1)*src->step;
+ }
+ while (step != steps) {
+ if (finite(data[step*src->ds_cnt])) {
+ if (data[step*src->ds_cnt] > dst->vf.val) {
+ dst->vf.val = data[step*src->ds_cnt];
+ dst->vf.when = src->start + (step+1)*src->step;
+ }
+ }
+ step++;
+ }
+ break;
+ case VDEF_TOTAL:
+ case VDEF_AVERAGE: {
+ int cnt=0;
+ double sum=0.0;
+ for (step=0;step<steps;step++) {
+ if (finite(data[step*src->ds_cnt])) {
+ sum += data[step*src->ds_cnt];
+ cnt ++;
+ };
+ }
+ if (cnt) {
+ if (dst->vf.op == VDEF_TOTAL) {
+ dst->vf.val = sum*src->step;
+ dst->vf.when = cnt*src->step; /* not really "when" */
+ } else {
+ dst->vf.val = sum/cnt;
+ dst->vf.when = 0; /* no time component */
+ };
+ } else {
+ dst->vf.val = DNAN;
+ dst->vf.when = 0;
+ }
+ }
+ break;
+ case VDEF_MINIMUM:
+ step=0;
+ while (step != steps && isnan(data[step*src->ds_cnt])) step++;
+ if (step == steps) {
+ dst->vf.val = DNAN;
+ dst->vf.when = 0;
+ } else {
+ dst->vf.val = data[step*src->ds_cnt];
+ dst->vf.when = src->start + (step+1)*src->step;
+ }
+ while (step != steps) {
+ if (finite(data[step*src->ds_cnt])) {
+ if (data[step*src->ds_cnt] < dst->vf.val) {
+ dst->vf.val = data[step*src->ds_cnt];
+ dst->vf.when = src->start + (step+1)*src->step;
+ }
+ }
+ step++;
+ }
+ break;
+ case VDEF_FIRST:
+ /* The time value returned here is one step before the
+ * actual time value. This is the start of the first
+ * non-NaN interval.
+ */
+ step=0;
+ while (step != steps && isnan(data[step*src->ds_cnt])) step++;
+ if (step == steps) { /* all entries were NaN */
+ dst->vf.val = DNAN;
+ dst->vf.when = 0;
+ } else {
+ dst->vf.val = data[step*src->ds_cnt];
+ dst->vf.when = src->start + step*src->step;
+ }
+ break;
+ case VDEF_LAST:
+ /* The time value returned here is the
+ * actual time value. This is the end of the last
+ * non-NaN interval.
+ */
+ step=steps-1;
+ while (step >= 0 && isnan(data[step*src->ds_cnt])) step--;
+ if (step < 0) { /* all entries were NaN */
+ dst->vf.val = DNAN;
+ dst->vf.when = 0;
+ } else {
+ dst->vf.val = data[step*src->ds_cnt];
+ dst->vf.when = src->start + (step+1)*src->step;
+ }
+ break;
+ }
+ return 0;
+}
+
+/* NaN < -INF < finite_values < INF */
+int
+vdef_percent_compar(a,b)
+const void *a,*b;
+{
+ /* Equality is not returned; this doesn't hurt except
+ * (maybe) for a little performance.
+ */
+
+ /* First catch NaN values. They are smallest */
+ if (isnan( *(double *)a )) return -1;
+ if (isnan( *(double *)b )) return 1;
+ /* NaN doesn't reach this part so INF and -INF are extremes.
+ * The sign from isinf() is compatible with the sign we return
+ */
+ if (isinf( *(double *)a )) return isinf( *(double *)a );
+ if (isinf( *(double *)b )) return isinf( *(double *)b );
+
+ /* If we reach this, both values must be finite */
+ if ( *(double *)a < *(double *)b ) return -1; else return 1;
+}