086f5c6f34a295925473fcb2ce499f76d953b1e7
[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       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 }
95
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 }
112
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 }   
132
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 }
152
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 }
172
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;
180
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"; }
234
235       /* add to fields */
236       if (addToArguments(pa,key,value,cnt)) {
237         freeParsedArguments(pa);
238         return -1;
239       }
240
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 }
255
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;
260   
261   /* matches the following formats:
262   ** RGB
263   ** RGBA
264   ** RRGGBB
265   ** RRGGBBAA
266   */
267   
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 }
297
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)
337
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];
347
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   }
357
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);
498
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; }
508     
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   }
554
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   }
622     
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   }
641
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);
646     
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   }
671   
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 }
716
717 /* and some defines */
718 #define set_match(str,pat,cmd) if (strcmp(pat, str) == 0)  { cmd ;}
719
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);
736
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   }
754
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 }
775
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;}
786
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");
799
800   /* and return fine */
801   return 0;
802 }  
803
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;}
810
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   }
840
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");
851
852   /* and return fine */
853   return 0;
854 }  
855
856
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;}
868
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");
892
893   /* and return fine */
894   return 0;
895 }
896
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;}
907
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");
925
926   /* and return fine */
927   return 0;
928 }
929
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;}
956
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");
976
977   /* and return fine */
978   return 0;
979 }
980
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;}
991
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   }
998
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   }
1007
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");
1033
1034   /* and return fine */
1035   return 0;
1036 }
1037
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");
1107
1108   /* and return */
1109   return 0;
1110 }
1111
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;}
1118
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 }
1133
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 }
1158
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;}
1163
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; }
1168
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   }
1182
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 }
1191
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   }
1211
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   }
1244
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   }
1278
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");
1285
1286   return 0;
1287 }
1288
1289 void rrd_graph_script(
1290     int argc,
1291     char *argv[],
1292     image_desc_t *const im,
1293     int optno)
1294 {
1295     int       i;
1296
1297     /* and now handle the things*/
1298     parsedargs_t pa;
1299     initParsedArguments(&pa);
1300
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; }
1308
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         }
1322
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 }