1 /****************************************************************************
2 * RRDtool 1.4.3 Copyright by Tobi Oetiker, 1997-2010
3 ****************************************************************************
4 * rrd_graph_helper.c commandline parser functions
5 * this code initially written by Alex van den Bogaerdt
6 ****************************************************************************/
8 #include "rrd_graph.h"
9 #define dprintf(...) if (gdp->debug&1) fprintf(stderr,__VA_ARGS__);
10 #define dprintfparsed(...) if (gdp->debug&2) fprintf(stderr,__VA_ARGS__);
12 void initParsedArguments(parsedargs_t* pa) {
13 /* initialize */
14 pa->arg=NULL;
15 pa->arg_orig=NULL;
16 pa->kv_args=NULL;
17 pa->kv_cnt=0;
18 }
20 void freeParsedArguments(parsedargs_t* pa) {
21 if (pa->arg) {free(pa->arg);}
22 if (pa->kv_args) {free(pa->kv_args);}
23 initParsedArguments(pa);
24 }
26 void dumpKeyValue(char* pre,keyvalue_t* t) {
27 if (t) {
28 fprintf(stderr,"%s%i: '%s' = '%s' %i\n",
29 pre,
30 t->pos,
31 t->key,
32 t->value,
33 t->flag
34 );
35 } else {
36 fprintf(stderr,"%sNULL\n",pre);
37 }
38 }
40 void dumpArguments(parsedargs_t* pa) {
41 fprintf(stderr,"====================\nParsed Arguments of: %s\n",pa->arg_orig);
42 for(int i=0;i<pa->kv_cnt;i++) {
43 dumpKeyValue(" ",&(pa->kv_args[i]));
44 }
45 fprintf(stderr,"---------------\n");
46 }
48 char* getKeyValueArgument(const char* key,int flag, parsedargs_t* pa) {
49 /* we count backwords for "overwrites" */
50 for(int i=pa->kv_cnt-1;i>=0;i--) {
51 char* akey=(pa->kv_args[i]).key;
52 if (strcmp(akey,key)==0) {
53 /* set flag */
54 if (flag) {pa->kv_args[i].flag=flag;}
55 /* return value */
56 return pa->kv_args[i].value;
57 }
58 }
59 return NULL;
60 }
62 keyvalue_t* getFirstUnusedArgument(int flag, parsedargs_t* pa) {
63 for(int i=0;i<pa->kv_cnt;i++) {
64 if (! pa->kv_args[i].flag) {
65 pa->kv_args[i].flag=flag;
66 return &(pa->kv_args[i]);
67 }
68 }
69 return NULL;
70 }
72 char* checkUnusedValues(parsedargs_t* pa){
73 char *res=NULL;
74 for(int i=0;i<pa->kv_cnt;i++) {
75 if (!pa->kv_args[i].flag) {
76 int len=0;
77 if (res) {len=strlen(res); }
78 char* t=realloc(res,len+3
79 +strlen(pa->kv_args[i].key)
80 +strlen(pa->kv_args[i].value)
81 );
82 if (! t) { return res; }
83 res=t;
84 strcat(res,pa->kv_args[i].key);
85 strcat(res,"=");
86 strcat(res,pa->kv_args[i].value);
87 strcat(res,":");
88 }
89 }
90 /* if we got one, then strip the final : */
91 if (res) { res[strlen(res)-1]=0; }
92 /* and return res */
93 return res;
94 }
96 int getMappedKeyValueArgument(const char* key,int flag, parsedargs_t* pa,
97 int* val,keyint_t** transpose) {
98 /* get the value itself as string */
99 char* v=getKeyValueArgument(key,flag,pa);
100 /* now try to parse the value */
101 if (v) {
102 for(;(*transpose)->key;transpose++) {
103 if (strcmp((*transpose)->key,v)==0) {
104 *val=(*transpose)->value;
105 return 0;
106 }
107 }
108 }
109 /* not found, so return error */
110 return 1;
111 }
113 int getLong(const char* v,long *val,char**extra,int base) {
114 /* try to execute the parser */
115 /* NOTE that this may be a bit different from the original parser */
116 *extra=NULL;
117 *val=strtol(v,extra,base);
118 /* and error handling */
119 if (extra==NULL) {
120 return 0;
121 } else {
122 if (*extra==v) {
123 return -1; /* failed miserably */
124 } else {
125 if ((*extra)[0]==0) { return 0; }
126 return 1; /* got extra bytes */
127 }
128 }
129 /* not found, so return error */
130 return -2;
131 }
133 int getDouble(const char* v, double *val,char**extra) {
134 /* try to execute the parser */
135 /* NOTE that this may be a bit different from the original parser */
136 *extra=NULL;
137 *val=strtod(v,extra);
138 /* and error handling */
139 if (extra==NULL) {
140 return 0;
141 } else {
142 if (*extra==v) {
143 return -1; /* failed miserably */
144 } else {
145 if ((*extra)[0]==0) { return 0; }
146 return 1; /* got extra bytes */
147 }
148 }
149 /* not found, so return error */
150 return -2;
151 }
153 int addToArguments(parsedargs_t* pa, char*key, char*value, int cnt) {
154 /* resize the field */
155 keyvalue_t* t=realloc(pa->kv_args,(pa->kv_cnt+1)*sizeof(keyvalue_t));
156 if (!t) {
157 rrd_set_error("could not realloc memory");
158 return -1;
159 } else {
160 /* assign pointer */
161 pa->kv_args=t;
162 }
163 /* fill in data */
164 pa->kv_args[pa->kv_cnt].key=key;
165 pa->kv_args[pa->kv_cnt].value=value;
166 pa->kv_args[pa->kv_cnt].pos=cnt;
167 pa->kv_args[pa->kv_cnt].flag=0;
168 pa->kv_cnt++;
169 /* and return ok */
170 return 0;
171 }
173 char *poskeys[]={"pos0","pos1","pos2","pos3","pos4","pos5","pos6","pos7","pos8","pos9"};
174 int parseArguments(const char* origarg, parsedargs_t* pa) {
175 initParsedArguments(pa);
176 /* now assign a copy */
177 pa->arg=strdup(origarg);
178 if (!pa->arg) { rrd_set_error("Could not allocate memory");return -1; }
179 pa->arg_orig=origarg;
181 /* first split arg into : */
182 char last=1;
183 char c;
184 int cnt=0;
185 int poscnt=0;
186 char* pos=pa->arg;
187 char* field=pos;
188 do {
189 c=*pos;
190 if (! field) { field=pos;cnt++;}
191 switch (c) {
192 /* if the char is a backslash, then this escapes the next one */
193 case '\\':
194 if (pos[1]) { pos++; }
195 break;
196 case 0:
197 case ':':
198 /* null and : separate the string */
199 *pos=0;
200 /* handle the case where we have got an = */
201 /* find equal sign */
202 char* equal=field;
203 for (equal=field;(*equal)&&(*equal!='=');equal++) { ; }
204 /* if we are on position 1 then check for position 0 to be [CV]?DEV */
205 int checkforkeyvalue=1;
206 /* nw define key to use */
207 char *key,*value;
208 if ((*equal=='=') && (checkforkeyvalue)) {
209 *equal=0;
210 key=field;
211 value=equal+1;
212 } else {
213 if ((poscnt>0)&&(strcmp(field,"STACK")==0)) {
214 key="stack";
215 value="1";
216 } else if ((poscnt>0)&&(strcmp(field,"strftime")==0)) {
217 key="strftime";
218 value="1";
219 } else {
220 if (poscnt>9) {
221 rrd_set_error("too many positional arguments");
222 freeParsedArguments(pa);
223 return -1;
224 }
225 key=poskeys[poscnt];
226 poscnt++;
227 value=field;
228 }
229 }
230 /* do some synonym translations */
231 if (strcmp(key,"label")==0) { key="legend"; }
232 if (strcmp(key,"colour")==0) { key="color"; }
233 if (strcmp(key,"colour2")==0) { key="color2"; }
235 /* add to fields */
236 if (addToArguments(pa,key,value,cnt)) {
237 freeParsedArguments(pa);
238 return -1;
239 }
241 /* and reset field */
242 field=NULL;
243 break;
244 default:
245 break;
246 }
247 /* assign last */
248 last=c;
249 /* and step to next one byte */
250 pos++;
251 } while (c);
252 /* and return OK */
253 return 0;
254 }
256 int parse_color( const char *const, struct gfx_color_t *);
257 int parse_color( const char *const string, struct gfx_color_t *c)
258 {
259 unsigned int r = 0, g = 0, b = 0, a = 0, i;
261 /* matches the following formats:
262 ** RGB
263 ** RGBA
264 ** RRGGBB
265 ** RRGGBBAA
266 */
268 i = 0;
269 while (string[i] && isxdigit((unsigned int) string[i]))
270 i++;
271 if (string[i] != '\0')
272 return 1; /* garbage follows hexdigits */
273 switch (i) {
274 case 3:
275 case 4:
276 sscanf(string, "%1x%1x%1x%1x", &r, &g, &b, &a);
277 r *= 0x11;
278 g *= 0x11;
279 b *= 0x11;
280 a *= 0x11;
281 if (i == 3)
282 a = 0xFF;
283 break;
284 case 6:
285 case 8:
286 sscanf(string, "%02x%02x%02x%02x", &r, &g, &b, &a);
287 if (i == 6)
288 a = 0xFF;
289 break;
290 default:
291 return 1; /* wrong number of digits */
292 }
293 /* I wonder how/why this works... */
294 *c=gfx_hex_to_col(r << 24 | g << 16 | b << 8 | a);
295 return 0;
296 }
298 /* this would allow for 240 different values */
299 #define PARSE_FIELD1 (1L<<60)
300 #define PARSE_FIELD2 (1L<<61)
301 #define PARSE_FIELD3 (1L<<62)
302 #define PARSE_FIELD4 (1L<<63)
303 #define PARSE_POSITIONAL (1L<<59)
304 #define PARSE_ONYAXIS (1L<<58)
305 #define PARSE_VNAME (PARSE_FIELD1|(1L<< 0))
306 #define PARSE_RRD (PARSE_FIELD1|(1L<< 1))
307 #define PARSE_DS (PARSE_FIELD1|(1L<< 2))
308 #define PARSE_CF (PARSE_FIELD1|(1L<< 3))
309 #define PARSE_COLOR (PARSE_FIELD1|(1L<< 4))
310 #define PARSE_COLOR2 (PARSE_FIELD1|(1L<< 5))
311 #define PARSE_LEGEND (PARSE_FIELD1|(1L<< 6))
312 #define PARSE_RPN (PARSE_FIELD1|(1L<< 7))
313 #define PARSE_START (PARSE_FIELD1|(1L<< 8))
314 #define PARSE_STEP (PARSE_FIELD1|(1L<< 9))
315 #define PARSE_END (PARSE_FIELD1|(1L<<10))
316 #define PARSE_STACK (PARSE_FIELD1|(1L<<11))
317 #define PARSE_LINEWIDTH (PARSE_FIELD1|(1L<<12))
318 #define PARSE_XAXIS (PARSE_FIELD1|(1L<<13))
319 #define PARSE_YAXIS (PARSE_FIELD1|(1L<<14))
320 #define PARSE_REDUCE (PARSE_FIELD1|(1L<<15))
321 #define PARSE_DASHES (PARSE_FIELD1|(1L<<20))
322 #define PARSE_HEIGHT (PARSE_FIELD1|(1L<<21))
323 #define PARSE_FORMAT (PARSE_FIELD1|(1L<<22))
324 #define PARSE_STRFTIME (PARSE_FIELD1|(1L<<23))
325 #define PARSE_FRACTION (PARSE_FIELD1|(1L<<24))
326 /* VNAME Special cases for generic parsing */
327 #define PARSE_VNAMEDEF (PARSE_VNAME|(1L<<57))
328 #define PARSE_VNAMEREF (PARSE_VNAME|(1L<<56))
329 #define PARSE_VNAMEREFNUM (PARSE_VNAMEREF|(1L<<55))
330 #define PARSE_VNAMEREFNUMNOPARSE (PARSE_FIELD1|(1L<<54))
331 /* special positional cases */
332 #define PARSE_VNAMERRDDSCF (PARSE_POSITIONAL|PARSE_VNAMEDEF|PARSE_RRD|PARSE_DS|PARSE_CF)
333 #define PARSE_VNAMECOLORLEGEND (PARSE_POSITIONAL|PARSE_VNAMEREFNUM|PARSE_COLOR|PARSE_COLOR2|PARSE_LEGEND)
334 #define PARSE_VNAMECOLORFRACTIONLEGEND (PARSE_VNAMECOLORLEGEND|PARSE_FRACTION)
335 #define PARSE_VNAMERPN (PARSE_POSITIONAL|PARSE_VNAMEDEF|PARSE_RPN)
336 #define PARSE_VNAMEREFPOS (PARSE_POSITIONAL|PARSE_VNAMEREF)
338 graph_desc_t* newGraphDescription(image_desc_t *const,enum gf_en,parsedargs_t*,unsigned long);
339 graph_desc_t* newGraphDescription(image_desc_t *const im,enum gf_en gf,parsedargs_t* pa,unsigned long bits) {
340 /* check that none of the othe bitfield marker is set */
341 if ((bits&PARSE_FIELD1)&&((bits&(PARSE_FIELD2|PARSE_FIELD3|PARSE_FIELD4)))) {
342 rrd_set_error("newGraphDescription: bad bitfield1 value %08x",bits);return NULL; }
343 /* the normal handler that adds to img */
344 if (gdes_alloc(im)) { return NULL; }
345 /* set gdp */
346 graph_desc_t *gdp= &im->gdes[im->gdes_c - 1];
348 /* set some generic things */
349 gdp->gf=gf;
350 if (1) {
351 char *t,*x;
352 long debug=0;
353 if ((t=getKeyValueArgument("debug",1,pa)) && ((getLong(t,&debug,&x,10)))) {
354 rrd_set_error("Bad debug value: %s",t); return NULL; }
355 gdp->debug=debug;
356 }
358 /* and the "flagged" parser implementation
359 *
360 * first the fields with legacy positional args
361 */
362 #define bitscmp(v) ((bits&v)==v)
363 char* vname=NULL;
364 if (bitscmp(PARSE_VNAME)) { vname=getKeyValueArgument("vname",1,pa);
365 dprintfparsed("got vname: %s\n",vname);}
366 char *rrd=NULL;
367 if (bitscmp(PARSE_RRD)) { rrd=getKeyValueArgument("rrd",1,pa);
368 dprintfparsed("got rrd: %s\n",rrd);}
369 char *ds=NULL;
370 if (bitscmp(PARSE_DS)) { ds=getKeyValueArgument("ds",1,pa);
371 dprintfparsed("got ds: %s\n",ds);}
372 char *cf=NULL;
373 if (bitscmp(PARSE_CF)) { cf=getKeyValueArgument("cf",1,pa);
374 dprintfparsed("got cf: %s\n",cf);}
375 char *color=NULL;
376 if (bitscmp(PARSE_COLOR)) { color=getKeyValueArgument("color",1,pa);
377 dprintfparsed("got color: %s\n",color);}
378 char *color2=NULL;
379 if (bitscmp(PARSE_COLOR2)) { color2=getKeyValueArgument("color2",1,pa);
380 dprintfparsed("got color2: %s\n",color2);}
381 char *rpn=NULL;
382 if (bitscmp(PARSE_RPN)) { rpn=getKeyValueArgument("rpn",1,pa);
383 dprintfparsed("got rpn: %s\n",rpn);}
384 char *legend=NULL;
385 if (bitscmp(PARSE_LEGEND)) { legend=getKeyValueArgument("legend",1,pa);
386 dprintfparsed("got legend: %s\n",legend);}
387 char *fraction=NULL;
388 if (bitscmp(PARSE_FRACTION)) { fraction=getKeyValueArgument("fraction",1,pa);
389 dprintfparsed("got fraction: %s\n",fraction);}
390 /*
391 * here the ones without delayed assigns (which are for positional parsers)
392 */
393 if (bitscmp(PARSE_FORMAT)) {
394 char *format=getKeyValueArgument("format",1,pa);
395 if(format) {
396 strncpy(gdp->format,format,FMT_LEG_LEN);
397 dprintfparsed("got format: %s\n",format);
398 }
399 }
400 if (bitscmp(PARSE_STRFTIME)) {
401 char *strft=getKeyValueArgument("strftime",1,pa);
402 gdp->strftm=(strft)?1:0;
403 dprintfparsed("got strftime: %s\n",strft);
404 }
405 if (bitscmp(PARSE_STACK)) {
406 char *stack=getKeyValueArgument("stack",1,pa);
407 gdp->stack=(stack)?1:0;
408 dprintfparsed("got stack: %s\n",stack);
409 }
410 if (bitscmp(PARSE_REDUCE)) {
411 char *reduce=getKeyValueArgument("reduce",1,pa);
412 if (reduce) {
413 gdp->cf_reduce=cf_conv(reduce);
414 dprintfparsed("got reduce: %s (%i)\n",reduce,gdp->cf_reduce);
415 if (((int)gdp->cf_reduce)==-1) { rrd_set_error("bad reduce CF: %s",reduce); return NULL; }
416 }
417 }
418 if (bitscmp(PARSE_XAXIS)) {
419 long xaxis=0;
420 char *t,*x;
421 if ((t=getKeyValueArgument("xaxis",1,pa)) && ((getLong(t,&xaxis,&x,10))||(xaxis<1)||(xaxis>MAX_AXIS))) {
422 rrd_set_error("Bad xaxis value: %s",t); return NULL; }
423 dprintfparsed("got xaxis: %s (%li)\n",t,xaxis);
424 gdp->xaxisidx=xaxis;
425 }
426 if (bitscmp(PARSE_YAXIS)) {
427 long yaxis=0;
428 char *t,*x;
429 if ((t=getKeyValueArgument("yaxis",1,pa)) && ((getLong(t,&yaxis,&x,10))||(yaxis<1)||(yaxis>MAX_AXIS))) {
430 rrd_set_error("Bad yaxis value: %s",t); return NULL; }
431 dprintfparsed("got yaxis: %s (%li)\n",t,yaxis);
432 gdp->yaxisidx=yaxis;
433 }
434 if (bitscmp(PARSE_LINEWIDTH)) {
435 double linewidth=0;
436 char *t,*x;
437 if ((t=getKeyValueArgument("linewidth",1,pa))&&(*t!=0)) {
438 if ((getDouble(t,&linewidth,&x))||(linewidth<=0)) {
439 rrd_set_error("Bad line width: %s",t); return NULL;
440 }
441 dprintfparsed("got linewidth: %s (%g)\n",t,linewidth);
442 gdp->linewidth=linewidth;
443 }
444 }
445 if (bitscmp(PARSE_HEIGHT)) {
446 double height=0;
447 char *t,*x;
448 if ((t=getKeyValueArgument("height",1,pa))&&(*t!=0)) {
449 if (getDouble(t,&height,&x)) {
450 rrd_set_error("Bad height: %s",t); return NULL;
451 }
452 dprintfparsed("got height: %s (%g)\n",t,height);
453 gdp->gradheight=height;
454 }
455 }
456 if (bitscmp(PARSE_STEP)) {
457 long step=0;
458 char *t,*x;
459 if ((t=getKeyValueArgument("step",1,pa)) && ((getLong(t,&step,&x,10))||(step<1))) {
460 rrd_set_error("Bad step value: %s",t); return NULL; }
461 dprintfparsed("got step: %s (%li)\n",t,step);
462 gdp->step=step;
463 }
464 if ((bitscmp(PARSE_START)||bitscmp(PARSE_END))) {
465 /* these should get done together to use the start/end code correctly*/
466 char* parsetime_error;
467 /* first start */
468 char* start;
469 rrd_time_value_t start_tv;
470 start_tv.type = ABSOLUTE_TIME;
471 start_tv.offset = 0;
472 localtime_r(&gdp->start, &start_tv.tm);
473 if (bitscmp(PARSE_START)) {
474 start=getKeyValueArgument("start",1,pa);
475 if ((start)&&(parsetime_error = rrd_parsetime(start, &start_tv))) {
476 rrd_set_error("start time: %s", parsetime_error);return NULL; }
477 dprintfparsed("got start: %s\n",start);
478 }
479 /* now end */
480 char* end;
481 rrd_time_value_t end_tv;
482 end_tv.type = ABSOLUTE_TIME;
483 end_tv.offset = 0;
484 localtime_r(&gdp->end, &end_tv.tm);
485 if (bitscmp(PARSE_END)) {
486 end=getKeyValueArgument("end",1,pa);
487 if ((end)&&(parsetime_error = rrd_parsetime(end, &end_tv))) {
488 rrd_set_error("end time: %s", parsetime_error); return NULL; }
489 dprintfparsed("got end: %s\n",end);
490 }
491 /* and now put the pieces together (relative times like start=end-2days) */
492 time_t start_tmp = 0, end_tmp = 0;
493 if (rrd_proc_start_end(&start_tv, &end_tv, &start_tmp, &end_tmp) == -1) {
494 return NULL;
495 }
496 dprintfparsed("got start %s translated to: %lld\n",start,(long long int)start_tmp);
497 dprintfparsed("got end %s translated to: %lld\n",end,(long long int)end_tmp);
499 /* check some ranges */
500 if (start_tmp < 3600 * 24 * 365 * 10) {
501 rrd_set_error("the first entry to fetch should be "
502 "after 1980 (%ld)", start_tmp);
503 return NULL; }
504 if (end_tmp < start_tmp) {
505 rrd_set_error("start (%ld) should be less than end (%ld)",
506 start_tmp, end_tmp);
507 return NULL; }
509 /* and finally set it irrespectively of if it has been set or not
510 * it may have been a relative time and if you just set it partially
511 * then that is wrong...
512 */
513 gdp->start = start_tmp;
514 gdp->start_orig = start_tmp;
515 gdp->end = end_tmp;
516 gdp->end_orig = end_tmp;
517 }
518 if (bitscmp(PARSE_DASHES)) {
519 char* dashes=getKeyValueArgument("dashes",1,pa);
520 /* if we got dashes */
521 if (dashes) {
522 gdp->offset = 0;
523 /* count the , in dashes */
524 int cnt=0;for(char*t=dashes;(*t)&&(t=strchr(t,','));t++,cnt++) {;}
525 dprintfparsed("Got dashes argument: %s with %i comma\n",dashes,cnt);
526 /* now handle */
527 gdp->ndash = cnt+1;
528 gdp->p_dashes = (double *) malloc(sizeof(double)*(gdp->ndash+1));
529 /* now loop dashes */
530 for(int i=0;i<gdp->ndash;i++) {
531 char *x;
532 int f=getDouble(dashes,&gdp->p_dashes[i],&x);
533 if(f<0) {
534 rrd_set_error("Could not parse number: %s",dashes); return NULL;
535 }
536 /* we should have this most of the time */
537 dprintfparsed("Processed %s to %g at index %i\n",dashes,gdp->p_dashes[i],i);
538 if (f>0) {
539 if (*x!=',') {
540 rrd_set_error("expected a ',' at : %s",x); return NULL;}
541 dashes=x+1;
542 }
543 if ((f==0)&&(i!=gdp->ndash-1)) {
544 rrd_set_error("unexpected end at : %s",dashes); return NULL;}
545 }
546 }
547 char* dashoffset=getKeyValueArgument("dash-offset",1,pa);
548 if (dashoffset) {
549 char* x;
550 if (getDouble(dashes,&gdp->offset,&x)) {
551 rrd_set_error("Could not parse dash-offset: %s",dashoffset); return NULL; }
552 }
553 }
555 /* here now the positional(=legacy) parsers which are EXCLUSIVE - SO ELSE IF !!!
556 * we also only parse the extra here and assign just further down
557 * TODO maybe we can generalize this a bit more...
558 */
559 if (bitscmp(PARSE_VNAMERRDDSCF)) {
560 if ((!vname)||(!rrd)) {
561 /* get the first unused argument */
562 keyvalue_t* first=getFirstUnusedArgument(1,pa);
563 if (!first) { rrd_set_error("No argument for definition of vdef/rrd in %s",pa->arg_orig); return NULL; }
564 dprintfparsed("got positional vname and rrd: %s - %s\n",first->key,first->value);
565 if (!vname) {vname=first->key;}
566 if (!rrd) {rrd=first->value; }
567 }
568 /* and now look for datasource */
569 if (!ds) {
570 /* get the first unused argument */
571 keyvalue_t* first=getFirstUnusedArgument(1,pa);
572 if (!first) { rrd_set_error("No argument for definition of DS in %s",pa->arg_orig); return NULL; }
573 dprintfparsed("got positional ds: %s - \n",first->value);
574 ds=first->value;
575 }
576 /* and for CF */
577 if (!cf) {
578 /* get the first unused argument */
579 keyvalue_t* first=getFirstUnusedArgument(1,pa);
580 if (!first) { rrd_set_error("No argument for definition of CF in %s",pa->arg_orig); return NULL; }
581 dprintfparsed("got positional cf: %s - \n",first->value);
582 cf=first->value;
583 }
584 } else if (bitscmp(PARSE_VNAMECOLORLEGEND)) {
585 /* vname */
586 if (!vname) {
587 keyvalue_t* first=getFirstUnusedArgument(1,pa);
588 if (first) { vname=first->value;
589 } else { rrd_set_error("No positional VNAME"); return NULL; }
590 }
591 /* fraction added into the parsing mix for TICK */
592 if ((bitscmp(PARSE_VNAMECOLORFRACTIONLEGEND))&&(!fraction)) {
593 keyvalue_t* first=getFirstUnusedArgument(1,pa);
594 if (first) { fraction=first->value;
595 } else { rrd_set_error("No positional FRACTION"); return NULL; }
596 }
597 /* legend */
598 if (!legend) {
599 keyvalue_t* first=getFirstUnusedArgument(1,pa);
600 if (first) { legend=first->value;
601 dprintfparsed("got positional legend: %s - \n",first->value);
602 } else { rrd_set_error("No positional legend found"); return NULL; }
603 }
604 } else if (bitscmp(PARSE_VNAMERPN)) {
605 if ((!vname)||(!rpn)) {
606 /* get the first unused argument */
607 keyvalue_t* first=getFirstUnusedArgument(1,pa);
608 if (!first) { rrd_set_error("No argument for definition of vdef/rrd in %s",pa->arg_orig); return NULL; }
609 dprintfparsed("got positional vname and rpn: %s - %s\n",first->key,first->value);
610 if (!vname) {vname=first->key;}
611 if (!rpn) {rpn=first->value; }
612 }
613 } else if (bitscmp(PARSE_VNAMEREFPOS)) {
614 if ((!vname)) {
615 /* get the first unused argument */
616 keyvalue_t* first=getFirstUnusedArgument(1,pa);
617 if (!first) { rrd_set_error("No argument for definition of vdef/rrd in %s",pa->arg_orig); return NULL; }
618 dprintfparsed("got positional vname and rrd: %s - %s\n",first->key,first->value);
619 if (!vname) {vname=first->value;}
620 }
621 }
623 /* and set some of those late assignments to accomodate the legacy parser*/
624 /* first split vname into color */
625 if (vname) {
626 /* check for color */
627 char *h1=strchr(vname,'#');
628 char* h2=NULL;
629 if (h1) {
630 *h1=0;h1++;
631 dprintfparsed("got positional color: %s - \n",h1);
632 h2=strchr(h1,'#');
633 if (h2) {
634 *h2=0;h2++;
635 dprintfparsed("got positional color2: %s - \n",h2);
636 }
637 }
638 if (bitscmp(PARSE_COLOR) && (! color) && (h1)) { color=h1;}
639 if (bitscmp(PARSE_COLOR2) && (! color2) && (h2)) { color2=h2;}
640 }
642 /* check if we are reusing the vname */
643 if (vname) {
644 int idx=find_var(im, vname);
645 dprintfparsed("got positional index %i for %s - \n",idx,vname);
647 /* some handling */
648 if (bitscmp(PARSE_VNAMEDEF)) {
649 if (idx>=0) {
650 rrd_set_error("trying to reuse vname %s",vname); return NULL; }
651 } else if (bitscmp(PARSE_VNAMEREF)) {
652 if (idx>=0) {
653 gdp->vidx=idx;
654 } else if (bitscmp(PARSE_VNAMEREFNUM)) {
655 if (!bitscmp(PARSE_VNAMEREFNUMNOPARSE)) {
656 double val;
657 char *x;
658 int f=getDouble(vname,&val,&x);
659 if (f) {
660 rrd_set_error("error parsing number %s",vname); return NULL;
661 }
662 gdp->yrule=val;
663 } else {
664 rrd_set_error("vname %s not found",vname); return NULL;
665 }
666 } else {
667 gdp->vidx=-1;
668 }
669 }
670 }
672 /* and assign it */
673 if (vname) { strncpy(gdp->vname,vname,MAX_VNAME_LEN + 1); }
674 if (rrd) { strncpy(gdp->rrd,rrd,1024); }
675 if (ds) { strncpy(gdp->ds_nam,ds,DS_NAM_SIZE); }
676 if (cf) {
677 gdp->cf=cf_conv(cf);
678 if (((int)gdp->cf)==-1) {
679 rrd_set_error("bad CF: %s",cf); return NULL; }
680 } else { if (bitscmp(PARSE_CF)) {gdp->cf=-1;}}
681 if ((color)&&(parse_color(color,&(gdp->col)))) { return NULL; }
682 if ((color2)&&(parse_color(color2,&(gdp->col2)))) { return NULL; }
683 if (rpn) {gdp->rpn=rpn;}
684 if ((legend)&&(*legend!=0)) {
685 /* some spacing before we really start with the legend - needed for some reason */
686 char* t=gdp->legend;
687 *t=' ';t++;
688 *t=' ';t++;
689 /* and copy it into place */
690 strncpy(t,legend,FMT_LEG_LEN);
691 }
692 if (fraction) {
693 if (strcmp(fraction,"vname")==0) {
694 /* check that vname is really a DEF|CDEF */
695 if (im->gdes[gdp->vidx].gf != GF_DEF && im->gdes[gdp->vidx].gf != GF_CDEF) {
696 rrd_set_error("variable '%s' not DEF nor CDEF when using dynamic fractions", gdp->vname);
697 return NULL;
698 }
699 /* add as flag to use (c)?def */
700 gdp->cf=CF_LAST;
701 gdp->yrule=0.5;
702 } else {
703 /* parse number */
704 double val;
705 char *x;
706 int f=getDouble(fraction,&val,&x);
707 if (f) {
708 rrd_set_error("error parsing number %s",vname); return NULL;
709 }
710 gdp->yrule=val;
711 }
712 }
713 /* and return it */
714 return gdp;
715 }
717 /* and some defines */
718 #define set_match(str,pat,cmd) if (strcmp(pat, str) == 0) { cmd ;}
720 /* prototypes */
721 int parse_axis(enum gf_en,parsedargs_t*,image_desc_t *const);
722 int parse_def(enum gf_en,parsedargs_t*,image_desc_t *const);
723 int parse_cvdef(enum gf_en,parsedargs_t*,image_desc_t *const);
724 int parse_line(enum gf_en,parsedargs_t*,image_desc_t *const);
725 int parse_area(enum gf_en,parsedargs_t*,image_desc_t *const);
726 int parse_stack(enum gf_en,parsedargs_t*,image_desc_t *const);
727 int parse_print(enum gf_en,parsedargs_t*,image_desc_t *const);
728 int parse_gprint(enum gf_en,parsedargs_t*,image_desc_t *const);
729 int parse_comment(enum gf_en,parsedargs_t*,image_desc_t *const);
730 int parse_hvrule(enum gf_en,parsedargs_t*,image_desc_t *const);
731 int parse_grad(enum gf_en,parsedargs_t*,image_desc_t *const);
732 int parse_tick(enum gf_en,parsedargs_t*,image_desc_t *const);
733 int parse_textalign(enum gf_en,parsedargs_t*,image_desc_t *const);
734 int parse_shift(enum gf_en,parsedargs_t*,image_desc_t *const);
735 int parse_xport(enum gf_en,parsedargs_t*,image_desc_t *const);
737 /* implementations */
738 int parse_axis(enum gf_en gf,parsedargs_t*pa,image_desc_t *const im){
739 #if 0
740 /* define X or y axis */
741 axis_t *a=im->xaxis;
742 if (gf == GF_YAXIS) { a=im->yaxis; }
743 /* try to parse the number */
744 char* cmd=getKeyValueArgument("cmd",1,pa);
745 if (cmd[5]) {
746 int num=atoi(cmd+5);
747 if ((num<1)||(num>MAX_AXIS)) {
748 rrd_set_error("invalid axis ID %i in %s - should be in range [1:%i]",num,cmd,MAX_AXIS);
749 return 1;
750 }
751 /* and forward by that much */
752 a=a+(num-1);
753 }
755 /* and set type */
756 char* t=getKeyValueArgument("type",1,pa);
757 if (t) {
758 set_match(t,"TIME",a->type=AXIS_TYPE_TIME)
759 else
760 set_match(t,"LINEAR",a->type=AXIS_TYPE_LINEAR)
761 else
762 set_match(t,"LOGARITHMIC",a->type=AXIS_TYPE_LOGARITHMIC)
763 else {
764 rrd_set_error("unsupported axis type %s",t);
765 return 1;
766 }
767 }
768 /* and other stuff */
769 a->bounds.lowertxt=getKeyValueArgument("min",1,pa);
770 a->bounds.uppertxt=getKeyValueArgument("max",1,pa);
771 #endif
772 /* and return */
773 return 0;
774 }
776 int parse_def(enum gf_en gf,parsedargs_t*pa,image_desc_t *const im){
777 /* get new graph that we fill */
778 graph_desc_t *gdp=newGraphDescription(im,gf,pa,
779 PARSE_VNAMERRDDSCF
780 |PARSE_START
781 |PARSE_STEP
782 |PARSE_END
783 |PARSE_REDUCE
784 );
785 if (!gdp) { return -1;}
787 /* debugging output */
788 dprintf("=================================\n");
789 dprintf("DEF : %s\n",pa->arg_orig);
790 dprintf("VNAME : %s\n",gdp->vname);
791 dprintf("RRD : %s\n",gdp->rrd);
792 dprintf("DS : %s\n",gdp->ds_nam);
793 dprintf("CF : %i\n",gdp->cf);
794 dprintf("START : (%lld)\n",(long long int)gdp->start);
795 dprintf("STEP : (%lld)\n",(long long int)gdp->step);
796 dprintf("END : (%lld)\n",(long long int)gdp->end);
797 dprintf("REDUCE: (%i)\n",gdp->cf_reduce);
798 dprintf("=================================\n");
800 /* and return fine */
801 return 0;
802 }
804 int parse_cvdef(enum gf_en gf,parsedargs_t*pa,image_desc_t *const im){
805 /* get new graph that we fill */
806 graph_desc_t *gdp=newGraphDescription(im,gf,pa,
807 PARSE_VNAMERPN
808 );
809 if (!gdp) { return -1;}
811 /* handle RPN parsing */
812 if (gf==GF_CDEF) {
813 /* parse rpn */
814 if ((gdp->rpnp= rpn_parse((void *) im, gdp->rpn, &find_var_wrapper)) == NULL) {
815 return -1; }
816 } else {
817 /* parse vdef, as vdef_parse is a bit "stupid" right now we have to touch things here */
818 /* so find first , */
819 char*c=strchr(gdp->rpn,',');
820 if (! c) { rrd_set_error("Comma expected in VDEF definition %s",gdp->rpn); return -1;}
821 /* found a comma, so copy the first part to ds_name (re/abusing it) */
822 *c=0;
823 strncpy(gdp->ds_nam,gdp->rpn,DS_NAM_SIZE);
824 *c=',';
825 /* trying to find the vidx for that name */
826 gdp->vidx = find_var(im, gdp->ds_nam);
827 if (gdp->vidx<0) { *c=',';
828 rrd_set_error("Not a valid vname: %s in line %s", gdp->ds_nam, gdp->rpn);
829 return -1;}
830 if (im->gdes[gdp->vidx].gf != GF_DEF && im->gdes[gdp->vidx].gf != GF_CDEF) {
831 rrd_set_error("variable '%s' not DEF nor "
832 "CDEF in VDEF '%s'", gdp->ds_nam, gdp->rpn);
833 return 1;
834 }
835 /* and parsing the rpn */
836 int r=vdef_parse(gdp, c+1);
837 /* original code does not check here for some reason */
838 if (r) { return -1; }
839 }
841 /* debugging output */
842 dprintf("=================================\n");
843 if (gf==GF_CDEF) {
844 dprintf("CDEF : %s\n",pa->arg_orig);
845 } else {
846 dprintf("VDEF : %s\n",pa->arg_orig);
847 }
848 dprintf("VNAME : %s\n",gdp->vname);
849 dprintf("RPN : %s\n",gdp->rpn);
850 dprintf("=================================\n");
852 /* and return fine */
853 return 0;
854 }
857 int parse_line(enum gf_en gf,parsedargs_t*pa,image_desc_t *const im){
858 /* get new graph that we fill */
859 graph_desc_t *gdp=newGraphDescription(im,gf,pa,
860 PARSE_VNAMECOLORLEGEND
861 |PARSE_STACK
862 |PARSE_LINEWIDTH
863 |PARSE_DASHES
864 |PARSE_XAXIS
865 |PARSE_YAXIS
866 );
867 if (!gdp) { return -1;}
869 /* debug output */
870 dprintf("=================================\n");
871 dprintf("LINE : %s\n",pa->arg_orig);
872 if (gdp->vidx<0) {
873 dprintf("VAL : %g\n",gdp->yrule);
874 } else {
875 dprintf("VNAME : %s (%li)\n",gdp->vname,gdp->vidx);
876 }
877 dprintf("COLOR : r=%g g=%g b=%g a=%g\n",
878 gdp->col.red,gdp->col.green,gdp->col.blue,gdp->col.alpha);
879 dprintf("COLOR2: r=%g g=%g b=%g a=%g\n",
880 gdp->col2.red,gdp->col2.green,gdp->col2.blue,gdp->col2.alpha);
881 dprintf("LEGEND: %s\n",gdp->legend);
882 dprintf("STACK : %i\n",gdp->stack);
883 dprintf("WIDTH : %g\n",gdp->linewidth);
884 dprintf("XAXIS : %i\n",gdp->xaxisidx);
885 dprintf("YAXIS : %i\n",gdp->yaxisidx);
886 if (gdp->ndash) {
887 dprintf("DASHES: %i - %g",gdp->ndash,gdp->p_dashes[0]);
888 for(int i=1;i<gdp->ndash;i++){dprintf(", %g",gdp->p_dashes[i]);}
889 dprintf("\n");
890 }
891 dprintf("=================================\n");
893 /* and return fine */
894 return 0;
895 }
897 int parse_area(enum gf_en gf,parsedargs_t*pa,image_desc_t *const im){
898 /* get new graph that we fill */
899 graph_desc_t *gdp=newGraphDescription(im,gf,pa,
900 PARSE_VNAMECOLORLEGEND
901 |PARSE_STACK
902 |PARSE_XAXIS
903 |PARSE_YAXIS
904 |PARSE_HEIGHT
905 );
906 if (!gdp) { return -1;}
908 /* debug output */
909 dprintf("=================================\n");
910 dprintf("AREA : %s\n",pa->arg_orig);
911 if (gdp->vidx<0) {
912 dprintf("VAL : %g\n",gdp->yrule);
913 } else {
914 dprintf("VNAME : %s (%li)\n",gdp->vname,gdp->vidx);
915 }
916 dprintf("COLOR : r=%g g=%g b=%g a=%g\n",
917 gdp->col.red,gdp->col.green,gdp->col.blue,gdp->col.alpha);
918 dprintf("COLOR2: r=%g g=%g b=%g a=%g\n",
919 gdp->col2.red,gdp->col2.green,gdp->col2.blue,gdp->col2.alpha);
920 dprintf("LEGEND: %s\n",gdp->legend);
921 dprintf("STACK : %i\n",gdp->stack);
922 dprintf("XAXIS : %i\n",gdp->xaxisidx);
923 dprintf("YAXIS : %i\n",gdp->yaxisidx);
924 dprintf("=================================\n");
926 /* and return fine */
927 return 0;
928 }
930 int parse_stack(enum gf_en gf,parsedargs_t*pa,image_desc_t *const im){
931 /* get new graph that we fill */
932 graph_desc_t *gdp=newGraphDescription(im,gf,pa,
933 PARSE_VNAMECOLORLEGEND
934 |PARSE_XAXIS
935 |PARSE_YAXIS
936 );
937 if (!gdp) { return -1;}
938 gdp->stack=1;
939 /* and try to get the one index before ourselves */
940 long i;
941 for (i=im->gdes_c;(gdp->gf==gf)&&(i>=0);i--) {
942 dprintfparsed("trying to process entry %li with type %u\n",i,im->gdes[i].gf);
943 switch (im->gdes[i].gf) {
944 case GF_LINE:
945 case GF_AREA:
946 gdp->gf=im->gdes[i].gf;
947 gdp->linewidth=im->gdes[i].linewidth;
948 dprintfparsed("found matching LINE/AREA at %li with type %u\n",i,im->gdes[i].gf);
949 break;
950 default: break;
951 }
952 }
953 /* error the unhandled */
954 if (gdp->gf==gf) {
955 rrd_set_error("No previous LINE or AREA found for %s",pa->arg_orig); return -1;}
957 /* debug output */
958 dprintf("=================================\n");
959 dprintf("STACK : %s\n",pa->arg_orig);
960 if (gdp->vidx<0) {
961 dprintf("VAL : %g\n",gdp->yrule);
962 } else {
963 dprintf("VNAME : %s (%li)\n",gdp->vname,gdp->vidx);
964 }
965 dprintf("COLOR : r=%g g=%g b=%g a=%g\n",
966 gdp->col.red,gdp->col.green,gdp->col.blue,gdp->col.alpha);
967 dprintf("COLOR2: r=%g g=%g b=%g a=%g\n",
968 gdp->col2.red,gdp->col2.green,gdp->col2.blue,gdp->col2.alpha);
969 dprintf("LEGEND: %s\n",gdp->legend);
970 dprintf("STACK : %i\n",gdp->stack);
971 dprintf("WIDTH : %g\n",gdp->linewidth);
972 dprintf("XAXIS : %i\n",gdp->xaxisidx);
973 dprintf("YAXIS : %i\n",gdp->yaxisidx);
974 dprintf("DASHES: TODI\n");
975 dprintf("=================================\n");
977 /* and return fine */
978 return 0;
979 }
981 int parse_hvrule(enum gf_en gf,parsedargs_t*pa,image_desc_t *const im){
982 /* get new graph that we fill */
983 graph_desc_t *gdp=newGraphDescription(im,gf,pa,
984 PARSE_VNAMECOLORLEGEND
985 |PARSE_VNAMEREFNUMNOPARSE
986 |PARSE_XAXIS
987 |PARSE_YAXIS
988 |PARSE_DASHES
989 );
990 if (!gdp) { return -1;}
992 /* check that vidx is of type VDEF */
993 if (im->gdes[gdp->vidx].gf != GF_VDEF) {
994 rrd_set_error("Using vname %s of wrong type in line %s\n",
995 gdp->vname,pa->arg_orig);
996 return -1;
997 }
999 /* and here we place number parsing - depends on axis in the long-run*/
1000 if (gdp->vidx<0) {
1001 rrd_set_error("TODO: NOT SUPPORTED: Need to add number handler here for %s\n",
1002 gdp->vname); return -1;
1003 /* depending on axis type this is either number or time
1004 essentially becoming generic when we get axis support
1005 */
1006 }
1008 /* debug output */
1009 dprintf("=================================\n");
1010 if (gf==GF_VRULE) {
1011 dprintf("VRULE : %s\n",pa->arg_orig);
1012 } else {
1013 dprintf("HRULE : %s\n",pa->arg_orig);
1014 }
1015 if (gdp->vidx<0) {
1016 if (gf==GF_VRULE) {
1017 dprintf("VAL : %g\n",gdp->yrule);
1018 } else {
1019 dprintf("VAL : %g\n",gdp->yrule);
1020 }
1021 } else {
1022 dprintf("VNAME : %s (%li)\n",gdp->vname,gdp->vidx);
1023 }
1024 dprintf("COLOR : r=%g g=%g b=%g a=%g\n",
1025 gdp->col.red,gdp->col.green,gdp->col.blue,gdp->col.alpha);
1026 dprintf("COLOR2: r=%g g=%g b=%g a=%g\n",
1027 gdp->col2.red,gdp->col2.green,gdp->col2.blue,gdp->col2.alpha);
1028 dprintf("LEGEND: %s\n",gdp->legend);
1029 dprintf("DASHES: TODO\n");
1030 dprintf("XAXIS : %i\n",gdp->xaxisidx);
1031 dprintf("YAXIS : %i\n",gdp->yaxisidx);
1032 dprintf("=================================\n");
1034 /* and return fine */
1035 return 0;
1036 }
1038 int parse_gprint(enum gf_en gf,parsedargs_t*pa,image_desc_t *const im) {
1039 /* get new graph that we fill */
1040 graph_desc_t *gdp=newGraphDescription(im,gf,pa,
1041 PARSE_VNAMEREF
1042 |PARSE_CF
1043 |PARSE_FORMAT
1044 |PARSE_STRFTIME
1045 );
1046 if (!gdp) { return -1;}
1047 /* here we parse pos arguments locally */
1048 /* vname */
1049 if (gdp->vname[0]==0) {
1050 dprintfparsed("Processing postitional vname\n");
1051 keyvalue_t* first=getFirstUnusedArgument(1,pa);
1052 if (first) {
1053 strncpy(gdp->vname,first->value,MAX_VNAME_LEN + 1);
1054 /* get type of reference */
1055 gdp->vidx=find_var(im, gdp->vname);
1056 if (gdp->vidx<0) {
1057 rrd_set_error("undefined vname %s",gdp->vname); return -1; }
1058 } else { rrd_set_error("No positional VNAME"); return -1; }
1059 }
1060 /* check type of ref in general */
1061 enum gf_en vnamegf=im->gdes[gdp->vidx].gf;
1062 dprintfparsed("Processing referenced type %i\n",vnamegf);
1063 switch (vnamegf) {
1064 /* depreciated */
1065 case GF_DEF:
1066 case GF_CDEF:
1067 dprintfparsed("Processing postitional CF\n");
1068 /* look for CF if not given */
1069 if (((int)gdp->cf)==-1) {
1070 keyvalue_t* first=getFirstUnusedArgument(1,pa);
1071 if (first) {
1072 gdp->cf=cf_conv(first->value);
1073 if (((int)gdp->cf)==-1) {
1074 rrd_set_error("bad CF: %s",first->value); return -1; }
1075 } else { rrd_set_error("No positional CDEF"); return -1; }
1076 }
1077 break;
1078 case GF_VDEF:
1079 break;
1080 default:
1081 rrd_set_error("Encountered unknown type variable '%s'",
1082 im->gdes[gdp->vidx].vname);
1083 return -1;
1084 }
1085 /* and get positional format */
1086 if (gdp->format[0]==0) {
1087 dprintfparsed("Processing postitional format\n");
1088 keyvalue_t* first=getFirstUnusedArgument(1,pa);
1089 if (first) {
1090 strncpy(gdp->format,first->value,FMT_LEG_LEN);
1091 dprintfparsed("got positional format: %s\n",gdp->format);
1092 } else { rrd_set_error("No positional CF/FORMAT"); return -1; }
1093 }
1094 /* debug output */
1095 dprintf("=================================\n");
1096 if (gf==GF_GPRINT) {
1097 dprintf("GPRINT : %s\n",pa->arg_orig);
1098 } else {
1099 dprintf("PRINT : %s\n",pa->arg_orig);
1100 }
1101 dprintf("VNAME : %s (%li)\n",gdp->vname,gdp->vidx);
1102 if ((int)gdp->cf>-1) {
1103 dprintf("CF : (%u)\n",gdp->cf);
1104 }
1105 dprintf("FORMAT: %s\n",gdp->legend);
1106 dprintf("=================================\n");
1108 /* and return */
1109 return 0;
1110 }
1112 int parse_comment(enum gf_en gf,parsedargs_t*pa,image_desc_t *const im){
1113 /* get new graph that we fill */
1114 graph_desc_t *gdp=newGraphDescription(im,gf,pa,
1115 PARSE_LEGEND
1116 );
1117 if (!gdp) { return -1;}
1119 /* and if we have no legend, then use the first positional one */
1120 if (gdp->legend[0]==0) {
1121 keyvalue_t* first=getFirstUnusedArgument(1,pa);
1122 if (first) {
1123 strncpy(gdp->legend,first->value,FMT_LEG_LEN);
1124 } else { rrd_set_error("No positional CF/FORMAT"); return -1; }
1125 }
1126 /* debug output */
1127 dprintf("=================================\n");
1128 dprintf("COMMENT : %s\n",pa->arg_orig);
1129 dprintf("LEGEND : %s\n",gdp->legend);
1130 /* and return */
1131 return 0;
1132 }
1134 int parse_tick(enum gf_en gf,parsedargs_t* pa,image_desc_t *const im) {
1135 /* get new graph that we fill */
1136 graph_desc_t *gdp=newGraphDescription(im,gf,pa,
1137 PARSE_VNAMECOLORFRACTIONLEGEND
1138 );
1139 if (!gdp) { return -1;}
1140 /* debug output */
1141 dprintf("=================================\n");
1142 dprintf("TICK : %s\n",pa->arg_orig);
1143 dprintf("VNAME : %s (%li)\n",gdp->vname,gdp->vidx);
1144 dprintf("COLOR : r=%g g=%g b=%g a=%g\n",
1145 gdp->col.red,gdp->col.green,gdp->col.blue,gdp->col.alpha);
1146 if (gdp->cf==CF_LAST) {
1147 dprintf("FRAC : %s\n",gdp->vname);
1148 } else {
1149 dprintf("FRAC : %g\n",gdp->yrule);
1150 }
1151 dprintf("LEGEND: %s\n",gdp->legend);
1152 dprintf("XAXIS : %i\n",gdp->xaxisidx);
1153 dprintf("YAXIS : %i\n",gdp->yaxisidx);
1154 dprintf("=================================\n");
1155 /* and return */
1156 return 0;
1157 }
1159 int parse_textalign(enum gf_en gf,parsedargs_t* pa,image_desc_t *const im) {
1160 /* get new graph that we fill */
1161 graph_desc_t *gdp=newGraphDescription(im,gf,pa,0);
1162 if (!gdp) { return -1;}
1164 /* get align */
1165 char* align=getKeyValueArgument("align",1,pa);
1166 if (!align) align=getFirstUnusedArgument(1,pa)->value;
1167 if (!align) { rrd_set_error("No alignment given"); return -1; }
1169 /* parse align */
1170 if (strcmp(align, "left") == 0) {
1171 gdp->txtalign = TXA_LEFT;
1172 } else if (strcmp(align, "right") == 0) {
1173 gdp->txtalign = TXA_RIGHT;
1174 } else if (strcmp(align, "justified") == 0) {
1175 gdp->txtalign = TXA_JUSTIFIED;
1176 } else if (strcmp(align, "center") == 0) {
1177 gdp->txtalign = TXA_CENTER;
1178 } else {
1179 rrd_set_error("Unknown alignement type '%s'", align);
1180 return 1;
1181 }
1183 /* debug output */
1184 dprintf("=================================\n");
1185 dprintf("TEXTALIGN : %s\n",pa->arg_orig);
1186 dprintf("ALIGNMENT : %s (%u)\n",align,gdp->txtalign);
1187 dprintf("=================================\n");
1188 /* and return */
1189 return 0;
1190 }
1192 int parse_shift(enum gf_en gf,parsedargs_t* pa,image_desc_t *const im) {
1193 /* get new graph that we fill */
1194 graph_desc_t *gdp=newGraphDescription(im,gf,pa,PARSE_VNAMEREFPOS);
1195 if (!gdp) { return -1;}
1196 /* and check that it is a CDEF */
1197 switch (im->gdes[gdp->vidx].gf) {
1198 case GF_DEF:
1199 case GF_CDEF:
1200 dprintf("- vname is of type DEF or CDEF, OK\n");
1201 break;
1202 case GF_VDEF:
1203 rrd_set_error("Cannot shift a VDEF: '%s' in line '%s'\n",
1204 im->gdes[gdp->vidx].vname, pa->arg_orig);
1205 return 1;
1206 default:
1207 rrd_set_error("Encountered unknown type variable '%s' in line '%s'",
1208 im->gdes[gdp->vidx].vname, pa->arg_orig);
1209 return 1;
1210 }
1212 /* now parse the "shift" */
1213 char* shift=getKeyValueArgument("shift",1,pa);
1214 if (!shift) {shift=getFirstUnusedArgument(1,pa)->value;}
1215 if (!shift) { rrd_set_error("No shift given"); return -1; }
1216 /* identify shift */
1217 gdp->shidx=find_var(im, shift);
1218 if (gdp->shidx>=0) {
1219 /* it is a def, so let us check its type*/
1220 switch (im->gdes[gdp->shidx].gf) {
1221 case GF_DEF:
1222 case GF_CDEF:
1223 rrd_set_error("Offset cannot be a (C)DEF: '%s' in line '%s'\n",
1224 im->gdes[gdp->shidx].vname, pa->arg_orig);
1225 return 1;
1226 case GF_VDEF:
1227 dprintf("- vname is of type VDEF, OK\n");
1228 break;
1229 default:
1230 rrd_set_error
1231 ("Encountered unknown type variable '%s' in line '%s'",
1232 im->gdes[gdp->vidx].vname, pa->arg_orig);
1233 return 1;
1234 }
1235 } else {
1236 /* it is no def, so parse as number */
1237 long val;
1238 char *x;
1239 int f=getLong(shift,&val,&x,10);
1240 if (f) { rrd_set_error("error parsing number %s",shift); return -1; }
1241 gdp->shval = val;
1242 gdp->shidx = -1;
1243 }
1245 /* debug output */
1246 dprintf("=================================\n");
1247 dprintf("SHIFT : %s\n",pa->arg_orig);
1248 dprintf("VNAME : %s (%li)\n",im->gdes[gdp->vidx].vname,gdp->vidx);
1249 if (gdp->shidx>=0) {
1250 dprintf("SHIFTBY : %s (%i)\n",im->gdes[gdp->shidx].vname,gdp->shidx);
1251 } else {
1252 dprintf("SHIFTBY : %li\n",gdp->shval);
1253 }
1254 dprintf("=================================\n");
1255 /* and return */
1256 return 0;
1257 }
1258 int parse_xport(enum gf_en gf,parsedargs_t* pa,image_desc_t *const im) {
1259 /* get new graph that we fill */
1260 graph_desc_t *gdp=newGraphDescription(im,gf,pa,PARSE_VNAMECOLORLEGEND);
1261 if (!gdp) { return -1;}
1262 /* check for cdef */
1263 /* and check that it is a CDEF */
1264 switch (im->gdes[gdp->vidx].gf) {
1265 case GF_DEF:
1266 case GF_CDEF:
1267 dprintf("- vname is of type DEF or CDEF, OK\n");
1268 break;
1269 case GF_VDEF:
1270 rrd_set_error("Cannot shift a VDEF: '%s' in line '%s'\n",
1271 im->gdes[gdp->vidx].vname, pa->arg_orig);
1272 return 1;
1273 default:
1274 rrd_set_error("Encountered unknown type variable '%s' in line '%s'",
1275 im->gdes[gdp->vidx].vname, pa->arg_orig);
1276 return 1;
1277 }
1279 /* debug output */
1280 dprintf("=================================\n");
1281 dprintf("LINE : %s\n",pa->arg_orig);
1282 dprintf("VNAME : %s (%li)\n",gdp->vname,gdp->vidx);
1283 dprintf("LEGEND: %s\n",gdp->legend);
1284 dprintf("=================================\n");
1286 return 0;
1287 }
1289 void rrd_graph_script(
1290 int argc,
1291 char *argv[],
1292 image_desc_t *const im,
1293 int optno)
1294 {
1295 int i;
1297 /* and now handle the things*/
1298 parsedargs_t pa;
1299 initParsedArguments(&pa);
1301 /* loop arguments */
1302 for (i = optind + optno; i < argc; i++) {
1303 /* release parsed args - avoiding late cleanups*/
1304 freeParsedArguments(&pa);
1305 /* processed parsed args */
1306 if (parseArguments(argv[i],&pa)) {
1307 return; }
1309 /* now let us handle the field based on the first command or cmd=...*/
1310 char*cmd=NULL;
1311 /* and try to get via cmd */
1312 char* t=getKeyValueArgument("cmd",1,&pa);
1313 if (t) {
1314 cmd=t;
1315 } else if ((t=getKeyValueArgument("pos0",1,&pa))) {
1316 cmd=t;
1317 } else {
1318 rrd_set_error("no command set in argument %s",pa.arg_orig);
1319 freeParsedArguments(&pa);
1320 return;
1321 }
1323 /* convert to enum but handling LINE special*/
1324 enum gf_en gf=-1;
1325 gf=gf_conv(cmd);
1326 if ((int)gf == -1) {
1327 if (strncmp("LINE",cmd,4)==0) {
1328 gf=GF_LINE;
1329 addToArguments(&pa,"linewidth",cmd+4,0);
1330 } else {
1331 rrd_set_error("'%s' is not a valid function name in %s", cmd,pa.arg_orig );
1332 return;
1333 }
1334 }
1335 /* now we can handle the commands */
1336 int r=0;
1337 switch (gf) {
1338 case GF_XAXIS: r=parse_axis(gf,&pa,im); break;
1339 case GF_YAXIS: r=parse_axis(gf,&pa,im); break;
1340 case GF_DEF: r=parse_def(gf,&pa,im); break;
1341 case GF_CDEF: r=parse_cvdef(gf,&pa,im); break;
1342 case GF_VDEF: r=parse_cvdef(gf,&pa,im); break;
1343 case GF_LINE: r=parse_line(gf,&pa,im); break;
1344 case GF_AREA: r=parse_area(gf,&pa,im); break;
1345 case GF_PRINT: r=parse_gprint(gf,&pa,im); break;
1346 case GF_GPRINT: r=parse_gprint(gf,&pa,im); break;
1347 case GF_COMMENT: r=parse_comment(gf,&pa,im); break;
1348 case GF_HRULE: r=parse_hvrule(gf,&pa,im); break;
1349 case GF_VRULE: r=parse_hvrule(gf,&pa,im); break;
1350 case GF_STACK: r=parse_stack(gf,&pa,im); break;
1351 case GF_TICK: r=parse_tick(gf,&pa,im); break;
1352 case GF_TEXTALIGN: r=parse_textalign(gf,&pa,im); break;
1353 case GF_SHIFT: r=parse_shift(gf,&pa,im); break;
1354 case GF_XPORT: r=parse_xport(gf,&pa,im); break;
1355 /* unsupported types right now */
1356 case GF_GRAD:
1357 rrd_set_error("GRAD unsupported - use AREA instead");
1358 break;
1359 }
1360 /* handle the return error case */
1361 if (r) { freeParsedArguments(&pa); return;}
1362 /* check for unprocessed keyvalue args */
1363 char *s;
1364 if ((s=checkUnusedValues(&pa))) {
1365 rrd_set_error("Unused Arguments in %s: %s",pa.arg_orig,s);
1366 freeParsedArguments(&pa);
1367 free(s);
1368 return;
1369 }
1370 }
1371 /* finally free arguments */
1372 freeParsedArguments(&pa);
1373 }