int rrd_parse_legend(char *line, unsigned int *eaten, graph_desc_t *gdp) { int i; if (line[*eaten]=='\0' || line[*eaten]==':') { dprintf("- no (or: empty) legend found\n"); return 0; } i=scan_for_col(&line[*eaten],FMT_LEG_LEN,gdp->legend); (*eaten)+=i; if (line[*eaten]!='\0' && line[*eaten]!=':') { rrd_set_error("Legend too long"); return 1; } else { return 0; } }
/* Parsing of PART, VRULE, HRULE, LINE, AREA, STACK and TICK ** is done in one function. ** ** Stacking PART, VRULE, HRULE or TICK is not allowed. ** ** If a number (which is valid to enter) is more than a ** certain amount of characters, it is caught as an error. ** While this is arguable, so is entering fixed numbers ** with more than MAX_VNAME_LEN significant digits. */ int rrd_parse_PVHLAST(char *line, unsigned int *eaten, graph_desc_t *gdp, image_desc_t *im) { int i,j,k; int colorfound=0; char tmpstr[MAX_VNAME_LEN + 10]; /* vname#RRGGBBAA\0 */ static int spacecnt = 0; if (spacecnt == 0) { float one_space = gfx_get_text_width(im->canvas, 0, im->text_prop[TEXT_PROP_LEGEND].font, im->text_prop[TEXT_PROP_LEGEND].size, im->tabwidth," ", 0) / 4.0; float target_space = gfx_get_text_width(im->canvas, 0, im->text_prop[TEXT_PROP_LEGEND].font, im->text_prop[TEXT_PROP_LEGEND].size, im->tabwidth,"oo", 0); spacecnt = target_space / one_space; dprintf("- spacecnt: %i onespace: %f targspace: %f\n",spacecnt,one_space,target_space); } dprintf("- parsing '%s'\n",&line[*eaten]); /* have simpler code in the drawing section */ if ( gdp->gf == GF_STACK ){ gdp->stack=1; } i=scan_for_col(&line[*eaten],MAX_VNAME_LEN+9,tmpstr); if (line[*eaten+i]!='\0' && line[*eaten+i]!=':') { rrd_set_error("Cannot parse line '%s'",line); return 1; } j=i; while (j>0 && tmpstr[j]!='#') j--; if (j) { tmpstr[j]='\0'; } /* Number or vname ? * It is a number only if "k" equals either "j" or "i", * depending on which is appropriate */ dprintf("- examining value '%s'\n",tmpstr); k=0; if (gdp->gf == GF_VRULE) { sscanf(tmpstr,"%li%n",&gdp->xrule,&k); if (((j!=0)&&(k==j))||((j==0)&&(k==i))) { dprintf("- found time: %li\n",gdp->xrule); } else { gdp->xrule=0; /* reset the value to "none" */ } } else { sscanf(tmpstr,"%lf%n",&gdp->yrule,&k); if (((j!=0)&&(k==j))||((j==0)&&(k==i))) { dprintf("- found number: %f\n",gdp->yrule); } else { gdp->yrule=DNAN; /* reset the value to "none" */ } } if (((j!=0)&&(k!=j))||((j==0)&&(k!=i))) { if ((gdp->vidx=find_var(im,tmpstr))<0) { rrd_set_error("Not a valid vname: %s in line %s",tmpstr,line); return 1; } dprintf("- found vname: '%s' vidx %li\n",tmpstr,gdp->vidx); } if (j) { j++; dprintf("- examining color '%s'\n",&tmpstr[j]); if (rrd_parse_color(&tmpstr[j],gdp)) { rrd_set_error("Could not parse color in '%s'",&tmpstr[j]); return 1; } dprintf("- parsed color 0x%08x\n",(unsigned int)gdp->col); colorfound=1; } else { dprintf("- no color present in '%s'\n",tmpstr); } (*eaten) += i; /* after vname#color */ if (line[*eaten]!='\0') { (*eaten)++; /* after colon */ } if (gdp->gf == GF_TICK) { dprintf("- parsing '%s'\n",&line[*eaten]); dprintf("- looking for optional TICK number\n"); j=0; sscanf(&line[*eaten],"%lf%n",&gdp->yrule,&j); if (j) { if (line[*eaten+j]!='\0' && line[*eaten+j]!=':') { rrd_set_error("Cannot parse TICK fraction '%s'",line); return 1; } dprintf("- found number %f\n",gdp->yrule); if (gdp->yrule > 1.0 || gdp->yrule < -1.0) { rrd_set_error("Tick factor should be <= 1.0"); return 1; } (*eaten)+=j; } else { dprintf("- not found, defaulting to 0.1\n"); gdp->yrule=0.1; } if (line[*eaten] == '\0') { dprintf("- done parsing line\n"); return 0; } else { if (line[*eaten] == ':') { (*eaten)++; } else { rrd_set_error("Can't make sense of that TICK line"); return 1; } } } dprintf("- parsing '%s'\n",&line[*eaten]); /* Legend is next. A legend without a color is an error. ** Stacking an item without having a legend is OK however ** then an empty legend should be specified. ** LINE:val#color:STACK means legend is string "STACK" ** LINE:val#color::STACK means no legend, and do STACK ** LINE:val:STACK is an error (legend but no color) ** LINE:val::STACK means no legend, and do STACK */ if (colorfound) { int err=0; char *linecp = strdup(line); dprintf("- looking for optional legend\n"); dprintf("- examining '%s'\n",&line[*eaten]); if (linecp[*eaten] != '\0' && linecp[*eaten] != ':') { int spi; /* If the legend is not empty, it has to be prefixed with spacecnt ' ' characters. This then gets * replaced by the color box later on. */ for (spi=0;spi<spacecnt && (*eaten) > 1;spi++){ linecp[--(*eaten)]=' '; } } if (rrd_parse_legend(linecp, eaten, gdp)) err=1; free(linecp); if (err) return 1; dprintf("- found legend '%s'\n", &gdp->legend[2]); } else { dprintf("- skipping empty legend\n"); if (line[*eaten] != '\0' && line[*eaten] != ':') { rrd_set_error("Legend set but no color: %s",&line[*eaten]); return 1; } } if (line[*eaten]=='\0') { dprintf("- done parsing line\n"); return 0; } (*eaten)++; /* after colon */ /* PART, HRULE, VRULE and TICK cannot be stacked. */ if ( (gdp->gf == GF_HRULE) || (gdp->gf == GF_VRULE) #ifdef WITH_PIECHART || (gdp->gf == GF_PART) #endif || (gdp->gf == GF_TICK) ) return 0; dprintf("- parsing '%s'\n",&line[*eaten]); if (line[*eaten]!='\0') { dprintf("- still more, should be STACK\n"); j=scan_for_col(&line[*eaten],5,tmpstr); if (line[*eaten+j]!='\0' && line[*eaten+j]!=':') { /* not 5 chars */ rrd_set_error("Garbage found where STACK expected"); return 1; } if (!strcmp("STACK",tmpstr)) { dprintf("- found STACK\n"); gdp->stack=1; (*eaten)+=j; } else { rrd_set_error("Garbage found where STACK expected"); return 1; } } if (line[*eaten]=='\0') { dprintf("- done parsing line\n"); return 0; } (*eaten)++; dprintf("- parsing '%s'\n",&line[*eaten]); return 0; }
int rrd_parse_def(char *line, unsigned int *eaten, graph_desc_t *gdp, image_desc_t *im) { int i=0; char command[7]; /* step, start, end, reduce */ char tmpstr[256]; struct rrd_time_value start_tv,end_tv; time_t start_tmp=0,end_tmp=0; char *parsetime_error=NULL; start_tv.type = end_tv.type=ABSOLUTE_TIME; start_tv.offset = end_tv.offset=0; localtime_r(&gdp->start, &start_tv.tm); localtime_r(&gdp->end, &end_tv.tm); dprintf("- parsing '%s'\n",&line[*eaten]); dprintf("- from line '%s'\n",line); if (rrd_parse_vname(line,eaten,gdp,im)) return 1; i=scan_for_col(&line[*eaten],254,gdp->rrd); if (line[*eaten+i]!=':') { rrd_set_error("Problems reading database name"); return 1; } (*eaten)+=++i; dprintf("- using file '%s'\n",gdp->rrd); i=0; sscanf(&line[*eaten], DS_NAM_FMT ":%n", gdp->ds_nam,&i); if (!i) { rrd_set_error("Cannot parse DS in '%s'",line); return 1; } (*eaten)+=i; dprintf("- using DS '%s'\n",gdp->ds_nam); if (rrd_parse_CF(line,eaten,gdp,&gdp->cf)) return 1; gdp->cf_reduce = gdp->cf; if (line[*eaten]=='\0') return 0; while (1) { dprintf("- optional parameter follows: %s\n", &line[*eaten]); i=0; sscanf(&line[*eaten], "%6[a-z]=%n", command, &i); if (!i) { rrd_set_error("Parse error in '%s'",line); return 1; } (*eaten)+=i; dprintf("- processing '%s'\n",command); if (!strcmp("reduce",command)) { if (rrd_parse_CF(line,eaten,gdp,&gdp->cf_reduce)) return 1; if (line[*eaten] != '\0') (*eaten)--; } else if (!strcmp("step",command)) { i=0; sscanf(&line[*eaten],"%lu%n",&gdp->step,&i); (*eaten)+=i; dprintf("- using step %lu\n",gdp->step); } else if (!strcmp("start",command)) { i=scan_for_col(&line[*eaten],255,tmpstr); (*eaten)+=i; if ((parsetime_error = parsetime(tmpstr, &start_tv))) { rrd_set_error( "start time: %s", parsetime_error ); return 1; } dprintf("- done parsing: '%s'\n",&line[*eaten]); } else if (!strcmp("end",command)) { i=scan_for_col(&line[*eaten],255,tmpstr); (*eaten)+=i; if ((parsetime_error = parsetime(tmpstr, &end_tv))) { rrd_set_error( "end time: %s", parsetime_error ); return 1; } dprintf("- done parsing: '%s'\n",&line[*eaten]); } else { rrd_set_error("Parse error in '%s'",line); return 1; } if (line[*eaten]=='\0') break; if (line[*eaten]!=':') { dprintf("- Expected to see end of string but got '%s'\n",\ &line[*eaten]); rrd_set_error("Parse error in '%s'",line); return 1; } (*eaten)++; } if (proc_start_end(&start_tv,&end_tv,&start_tmp,&end_tmp) == -1){ /* error string is set in parsetime.c */ return 1; } if (start_tmp < 3600*24*365*10) { rrd_set_error("the first entry to fetch should be " "after 1980 (%ld)",start_tmp); return 1; } if (end_tmp < start_tmp) { rrd_set_error("start (%ld) should be less than end (%ld)", start_tmp, end_tmp); return 1; } gdp->start = start_tmp; gdp->end = end_tmp; dprintf("- start time %lu\n",gdp->start); dprintf("- end time %lu\n",gdp->end); return 0; }
/* mostly rrd_graph(), just pushed a bit here and stretched a bit there */ int rrd_xport(int argc, char **argv, int *xsize, time_t *start, time_t *end, /* which time frame do you want ? * will be changed to represent reality */ unsigned long *step, /* which stepsize do you want? * will be changed to represent reality */ unsigned long *col_cnt, /* number of data columns in the result */ char ***legend_v, /* legend entries */ rrd_value_t **data) /* two dimensional array containing the data */ { image_desc_t im; int i; long long_tmp; time_t start_tmp=0,end_tmp=0; char symname[100]; long scancount; struct rrd_time_value start_tv, end_tv; char *parsetime_error = NULL; parsetime("end-24h", &start_tv); parsetime("now", &end_tv); /* use the default values from rrd_graph.c */ 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.unitslength= 9; 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 */ while (1) { static struct option long_options[] = { {"start", required_argument, 0, 's'}, {"end", required_argument, 0, 'e'}, {"maxrows", required_argument, 0, 'm'}, {"step", required_argument, 0, 261}, {0,0,0,0} }; int option_index = 0; int opt; opt = getopt_long(argc, argv, "s:e:m:", long_options, &option_index); if (opt == EOF) break; switch(opt) { case 261: im.step = atoi(optarg); break; case 's': if ((parsetime_error = parsetime(optarg, &start_tv))) { rrd_set_error( "start time: %s", parsetime_error ); return -1; } break; case 'e': if ((parsetime_error = parsetime(optarg, &end_tv))) { rrd_set_error( "end time: %s", parsetime_error ); return -1; } break; case 'm': long_tmp = atol(optarg); if (long_tmp < 10) { rrd_set_error("maxrows below 10 rows"); return -1; } im.xsize = long_tmp; break; case '?': if (optopt != 0) rrd_set_error("unknown option '%c'", optopt); else rrd_set_error("unknown option '%s'",argv[optind-1]); return -1; } } /* if (optind >= argc) { rrd_set_error("missing filename"); return -1; } */ if (proc_start_end(&start_tv,&end_tv,&start_tmp,&end_tmp) == -1) { return -1; } if (start_tmp < 3600*24*365*10) { rrd_set_error("the first entry to fetch should be after 1980 (%ld)",start_tmp); return -1; } if (end_tmp < start_tmp) { rrd_set_error("start (%ld) should be less than end (%ld)", start_tmp, end_tmp); return -1; } im.start = start_tmp; im.end = end_tmp; for(i=optind; 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); rrd_set_error("unknown function '%s'",symname); return -1; } } else { rrd_set_error("can't parse '%s'",argv[i]); im_free(&im); return -1; } switch(im.gdes[im.gdes_c-1].gf) { case GF_CDEF: if((rpnex = malloc(strlen(&argv[i][argstart])*sizeof(char)))==NULL) { rrd_set_error("malloc for CDEF"); return -1; } 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; } /* 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) { rrd_set_error("invalid rpn expression '%s'", rpnex); im_free(&im); return -1; } free(rpnex); 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; } /* 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; } break; case GF_XPORT: if((scancount=sscanf( &argv[i][argstart], "%29[^:]:%n", varname, &strstart))>=1) { 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); } 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; } } else { im_free(&im); rrd_set_error("can't parse '%s'",&argv[i][argstart]); return -1; } break; default: break; } } if (im.gdes_c == 0) { rrd_set_error("can't make a graph without contents"); im_free(&im); return(-1); } if (rrd_xport_fn(&im, start, end, step, col_cnt, legend_v, data) == -1) { im_free(&im); return -1; } im_free(&im); return 0; }