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