]>
Commit | Line | Data |
---|---|---|
0516711e | 1 | /**************************************************************************** |
f2aad7a8 | 2 | * RRDtool 1.2.13 Copyright by Tobi Oetiker, 1997-2006 |
0516711e TO |
3 | **************************************************************************** |
4 | * rrd_graph_helper.c commandline parser functions | |
054f4ff3 | 5 | * this code initially written by Alex van den Bogaerdt |
0516711e TO |
6 | ****************************************************************************/ |
7 | ||
adb27eb6 | 8 | #include "rrd_graph.h" |
6465207c AB |
9 | |
10 | #define dprintf if (gdp->debug) printf | |
11 | ||
7d0bd260 TO |
12 | /* NOTE ON PARSING: |
13 | * | |
14 | * we use the following: | |
15 | * | |
16 | * i=0; sscanf(&line[*eaten], "what to find%n", variables, &i) | |
17 | * | |
18 | * Usually you want to find a separator as well. Example: | |
19 | * i=0; sscanf(&line[*eaten], "%li:%n", &someint, &i) | |
20 | * | |
21 | * When the separator is not found, i is not set and thus remains zero. | |
22 | * Another way would be to compare strlen() to i | |
23 | * | |
24 | * Why is this important? Because 12345abc should not be matched as | |
25 | * integer 12345 ... | |
26 | */ | |
27 | ||
28 | /* NOTE ON VNAMES: | |
29 | * | |
30 | * "if ((gdp->vidx=find_var(im, l))!=-1)" is not good enough, at least | |
31 | * not by itself. | |
32 | * | |
33 | * A vname as a result of a VDEF is quite different from a vname | |
34 | * resulting of a DEF or CDEF. | |
35 | */ | |
36 | ||
37 | /* NOTE ON VNAMES: | |
38 | * | |
39 | * A vname called "123" is not to be parsed as the number 123 | |
40 | */ | |
41 | ||
42 | ||
b8d04741 TO |
43 | /* Define prototypes for the parsing methods. |
44 | Inputs: | |
7d0bd260 TO |
45 | const char *const line - a fixed pointer to a fixed string |
46 | unsigned int *const eaten - a fixed pointer to a changing index in that line | |
47 | graph_desc_t *const gdp - a fixed pointer to a changing graph description | |
48 | image_desc_t *const im - a fixed pointer to a changing image description | |
b8d04741 TO |
49 | */ |
50 | ||
7d0bd260 TO |
51 | int rrd_parse_find_gf (const char * const, unsigned int *const, graph_desc_t *const); |
52 | int rrd_parse_legend (const char * const, unsigned int *const, graph_desc_t *const); | |
53 | int rrd_parse_color (const char * const, graph_desc_t *const); | |
54 | int rrd_parse_CF (const char * const, unsigned int *const, graph_desc_t *const, enum cf_en *const); | |
55 | int rrd_parse_print (const char * const, unsigned int *const, graph_desc_t *const, image_desc_t *const); | |
56 | int rrd_parse_shift (const char * const, unsigned int *const, graph_desc_t *const, image_desc_t *const); | |
57 | int rrd_parse_xport (const char * const, unsigned int *const, graph_desc_t *const, image_desc_t *const); | |
58 | int rrd_parse_PVHLAST (const char * const, unsigned int *const, graph_desc_t *const, image_desc_t *const); | |
59 | int rrd_parse_make_vname (const char * const, unsigned int *const, graph_desc_t *const, image_desc_t *const); | |
60 | int rrd_parse_find_vname (const char * const, unsigned int *const, graph_desc_t *const, image_desc_t *const); | |
61 | int rrd_parse_def (const char * const, unsigned int *const, graph_desc_t *const, image_desc_t *const); | |
62 | int rrd_parse_vdef (const char * const, unsigned int *const, graph_desc_t *const, image_desc_t *const); | |
63 | int rrd_parse_cdef (const char * const, unsigned int *const, graph_desc_t *const, image_desc_t *const); | |
b8d04741 TO |
64 | |
65 | ||
66 | ||
adb27eb6 | 67 | int |
7d0bd260 | 68 | rrd_parse_find_gf(const char *const line, unsigned int *const eaten, graph_desc_t *const gdp) { |
829df33a | 69 | char funcname[11],c1=0; |
6465207c AB |
70 | int i=0; |
71 | ||
3da125e9 | 72 | /* start an argument with DEBUG to be able to see how it is parsed */ |
6465207c AB |
73 | sscanf(&line[*eaten], "DEBUG%n", &i); |
74 | if (i) { | |
75 | gdp->debug=1; | |
76 | (*eaten)+=i; | |
77 | i=0; | |
78 | dprintf("Scanning line '%s'\n",&line[*eaten]); | |
79 | } | |
7d0bd260 | 80 | i=0;c1='\0'; |
829df33a | 81 | sscanf(&line[*eaten], "%10[A-Z]%n%c", funcname, &i, &c1); |
6465207c AB |
82 | if (!i) { |
83 | rrd_set_error("Could not make sense out of '%s'",line); | |
84 | return 1; | |
85 | } | |
7d0bd260 | 86 | (*eaten)+=i; |
6465207c AB |
87 | if ((int)(gdp->gf=gf_conv(funcname)) == -1) { |
88 | rrd_set_error("'%s' is not a valid function name", funcname); | |
89 | return 1; | |
3da125e9 TO |
90 | } else { |
91 | dprintf("- found function name '%s'\n",funcname); | |
adb27eb6 | 92 | } |
3da125e9 | 93 | |
7d0bd260 TO |
94 | if (c1 == '\0') { |
95 | rrd_set_error("Function %s needs parameters. Line: %s\n",funcname,line); | |
96 | return 1; | |
97 | } | |
98 | if (c1 == ':') (*eaten)++; | |
99 | ||
100 | /* Some commands have a parameter before the colon | |
101 | * (currently only LINE) | |
102 | */ | |
103 | switch (gdp->gf) { | |
104 | case GF_LINE: | |
105 | if (c1 == ':') { | |
106 | gdp->linewidth=1; | |
107 | dprintf("- - using default width of 1\n"); | |
108 | } else { | |
109 | i=0;sscanf(&line[*eaten],"%lf:%n",&gdp->linewidth,&i); | |
110 | if (!i) { | |
111 | rrd_set_error("Cannot parse line width '%s' in line '%s'\n",&line[*eaten],line); | |
3da125e9 | 112 | return 1; |
7d0bd260 TO |
113 | } else { |
114 | dprintf("- - scanned width %f\n",gdp->linewidth); | |
115 | if (isnan(gdp->linewidth)) { | |
116 | rrd_set_error("LINE width '%s' is not a number in line '%s'\n",&line[*eaten],line); | |
117 | return 1; | |
118 | } | |
119 | if (isinf(gdp->linewidth)) { | |
120 | rrd_set_error("LINE width '%s' is out of range in line '%s'\n",&line[*eaten],line); | |
121 | return 1; | |
122 | } | |
123 | if (gdp->linewidth<0) { | |
124 | rrd_set_error("LINE width '%s' is less than 0 in line '%s'\n",&line[*eaten],line); | |
125 | return 1; | |
126 | } | |
829df33a | 127 | } |
7d0bd260 | 128 | (*eaten)+=i; |
3da125e9 | 129 | } |
7d0bd260 TO |
130 | break; |
131 | default: | |
132 | if (c1 == ':') break; | |
133 | rrd_set_error("Malformed '%s' command in line '%s'\n",&line[*eaten],line); | |
6465207c | 134 | return 1; |
6465207c | 135 | } |
6465207c AB |
136 | return 0; |
137 | } | |
adb27eb6 | 138 | |
6465207c | 139 | int |
7d0bd260 | 140 | rrd_parse_legend(const char *const line, unsigned int *const eaten, graph_desc_t *const gdp) { |
6465207c AB |
141 | int i; |
142 | ||
3da125e9 TO |
143 | if (line[*eaten]=='\0' || line[*eaten]==':') { |
144 | dprintf("- no (or: empty) legend found\n"); | |
145 | return 0; | |
146 | } | |
6465207c AB |
147 | |
148 | i=scan_for_col(&line[*eaten],FMT_LEG_LEN,gdp->legend); | |
149 | ||
3da125e9 TO |
150 | (*eaten)+=i; |
151 | ||
6465207c AB |
152 | if (line[*eaten]!='\0' && line[*eaten]!=':') { |
153 | rrd_set_error("Legend too long"); | |
154 | return 1; | |
155 | } else { | |
adb27eb6 AB |
156 | return 0; |
157 | } | |
6465207c AB |
158 | } |
159 | ||
160 | int | |
7d0bd260 | 161 | rrd_parse_color(const char *const string, graph_desc_t *const gdp) { |
3da125e9 | 162 | unsigned int r=0,g=0,b=0,a=0,i; |
6465207c | 163 | |
3da125e9 TO |
164 | /* matches the following formats: |
165 | ** RGB | |
166 | ** RGBA | |
167 | ** RRGGBB | |
168 | ** RRGGBBAA | |
169 | */ | |
6465207c | 170 | |
3da125e9 | 171 | i=0; |
4b1fb7ee | 172 | while (string[i] && isxdigit((unsigned int)string[i])) i++; |
3da125e9 TO |
173 | if (string[i] != '\0') return 1; /* garbage follows hexdigits */ |
174 | switch (i) { | |
175 | case 3: | |
176 | case 4: | |
177 | sscanf(string, "%1x%1x%1x%1x",&r,&g,&b,&a); | |
178 | r *= 0x11; | |
179 | g *= 0x11; | |
180 | b *= 0x11; | |
181 | a *= 0x11; | |
182 | if (i==3) a=0xFF; | |
183 | break; | |
184 | case 6: | |
185 | case 8: | |
186 | sscanf(string, "%02x%02x%02x%02x",&r,&g,&b,&a); | |
187 | if (i==6) a=0xFF; | |
188 | break; | |
189 | default: | |
190 | return 1; /* wrong number of digits */ | |
191 | } | |
6465207c AB |
192 | gdp->col = r<<24|g<<16|b<<8|a; |
193 | return 0; | |
194 | } | |
195 | ||
196 | int | |
7d0bd260 | 197 | rrd_parse_CF(const char *const line, unsigned int *const eaten, graph_desc_t *const gdp, enum cf_en *cf) { |
6465207c AB |
198 | char symname[CF_NAM_SIZE]; |
199 | int i=0; | |
200 | ||
201 | sscanf(&line[*eaten], CF_NAM_FMT "%n", symname,&i); | |
3da125e9 | 202 | if ((!i)||((line[(*eaten)+i]!='\0')&&(line[(*eaten)+i]!=':'))) { |
6465207c AB |
203 | rrd_set_error("Cannot parse CF in '%s'",line); |
204 | return 1; | |
205 | } | |
206 | (*eaten)+=i; | |
207 | dprintf("- using CF '%s'\n",symname); | |
adb27eb6 | 208 | |
8db0db54 | 209 | if ((int)(*cf = cf_conv(symname))==-1) { |
6465207c AB |
210 | rrd_set_error("Unknown CF '%s' in '%s'",symname,line); |
211 | return 1; | |
212 | } | |
213 | ||
214 | if (line[*eaten]!='\0') (*eaten)++; | |
215 | return 0; | |
216 | } | |
217 | ||
7d0bd260 TO |
218 | /* Try to match next token as a vname. |
219 | * | |
220 | * Returns: | |
221 | * -1 an error occured and the error string is set | |
222 | * other the vname index number | |
223 | * | |
224 | * *eaten is incremented only when a vname is found. | |
225 | */ | |
6465207c | 226 | int |
7d0bd260 | 227 | rrd_parse_find_vname(const char *const line, unsigned int *const eaten, graph_desc_t *const gdp, image_desc_t *const im) { |
6465207c | 228 | char tmpstr[MAX_VNAME_LEN+1]; |
7d0bd260 TO |
229 | int i; |
230 | long vidx; | |
6465207c | 231 | |
7d0bd260 | 232 | i=0;sscanf(&line[*eaten], DEF_NAM_FMT "%n", tmpstr,&i); |
6465207c AB |
233 | if (!i) { |
234 | rrd_set_error("Could not parse line '%s'",line); | |
7d0bd260 | 235 | return -1; |
6465207c | 236 | } |
7d0bd260 TO |
237 | if (line[*eaten+i]!=':' && line[*eaten+i]!='\0') { |
238 | rrd_set_error("Could not parse line '%s'",line); | |
239 | return -1; | |
240 | } | |
241 | dprintf("- Considering '%s'\n",tmpstr); | |
6465207c | 242 | |
7d0bd260 TO |
243 | if ((vidx=find_var(im,tmpstr))<0) { |
244 | dprintf("- Not a vname\n"); | |
6465207c | 245 | rrd_set_error("Not a valid vname: %s in line %s",tmpstr,line); |
7d0bd260 | 246 | return -1; |
6465207c | 247 | } |
7d0bd260 TO |
248 | dprintf("- Found vname '%s' vidx '%li'\n",tmpstr,gdp->vidx); |
249 | if (line[*eaten+i]==':') i++; | |
250 | (*eaten)+=i; | |
251 | return vidx; | |
252 | } | |
253 | ||
254 | /* Parsing old-style xPRINT and new-style xPRINT */ | |
255 | int | |
256 | rrd_parse_print(const char *const line, unsigned int *const eaten, graph_desc_t *const gdp, image_desc_t *const im) { | |
257 | /* vname:CF:format in case of DEF-based vname | |
258 | ** vname:CF:format in case of CDEF-based vname | |
259 | ** vname:format in case of VDEF-based vname | |
260 | */ | |
261 | if ((gdp->vidx=rrd_parse_find_vname(line,eaten,gdp,im))<0) return 1; | |
262 | ||
6465207c AB |
263 | switch (im->gdes[gdp->vidx].gf) { |
264 | case GF_DEF: | |
265 | case GF_CDEF: | |
266 | dprintf("- vname is of type DEF or CDEF, looking for CF\n"); | |
8db0db54 | 267 | if (rrd_parse_CF(line,eaten,gdp,&gdp->cf)) return 1; |
6465207c AB |
268 | break; |
269 | case GF_VDEF: | |
270 | dprintf("- vname is of type VDEF\n"); | |
271 | break; | |
272 | default: | |
7d0bd260 | 273 | rrd_set_error("Encountered unknown type variable '%s'",im->gdes[gdp->vidx].vname); |
6465207c AB |
274 | return 1; |
275 | } | |
276 | ||
277 | if (rrd_parse_legend(line,eaten,gdp)) return 1; | |
054f4ff3 | 278 | /* for *PRINT the legend itself gets rendered later. We only |
6d54723b | 279 | get the format at this juncture */ |
6465207c | 280 | strcpy(gdp->format,gdp->legend); |
6d54723b | 281 | gdp->legend[0]='\0'; |
6465207c AB |
282 | return 0; |
283 | } | |
284 | ||
7d0bd260 TO |
285 | /* SHIFT:_def_or_cdef:_vdef_or_number_ |
286 | */ | |
c4e7b680 | 287 | int |
7d0bd260 TO |
288 | rrd_parse_shift(const char *const line, unsigned int *const eaten, graph_desc_t *const gdp, image_desc_t *const im) { |
289 | int i; | |
290 | ||
291 | if ((gdp->vidx=rrd_parse_find_vname(line,eaten,gdp,im))<0) return 1; | |
292 | ||
293 | switch (im->gdes[gdp->vidx].gf) { | |
294 | case GF_DEF: | |
295 | case GF_CDEF: | |
296 | dprintf("- vname is of type DEF or CDEF, OK\n"); | |
297 | break; | |
298 | case GF_VDEF: | |
299 | rrd_set_error("Cannot shift a VDEF: '%s' in line '%s'\n",im->gdes[gdp->vidx].vname,line); | |
300 | return 1; | |
301 | default: | |
302 | rrd_set_error("Encountered unknown type variable '%s' in line '%s'",im->gdes[gdp->vidx].vname,line); | |
303 | return 1; | |
304 | } | |
305 | ||
306 | if ((gdp->shidx=rrd_parse_find_vname(line,eaten,gdp,im))>=0) { | |
307 | switch (im->gdes[gdp->shidx].gf) { | |
308 | case GF_DEF: | |
309 | case GF_CDEF: | |
310 | rrd_set_error("Offset cannot be a (C)DEF: '%s' in line '%s'\n",im->gdes[gdp->shidx].vname,line); | |
311 | return 1; | |
312 | case GF_VDEF: | |
313 | dprintf("- vname is of type VDEF, OK\n"); | |
314 | break; | |
315 | default: | |
316 | rrd_set_error("Encountered unknown type variable '%s' in line '%s'",im->gdes[gdp->vidx].vname,line); | |
317 | return 1; | |
c4e7b680 | 318 | } |
7d0bd260 TO |
319 | } else { |
320 | rrd_clear_error(); | |
321 | i=0; sscanf(&line[*eaten],"%li%n",&gdp->shval,&i); | |
322 | if (i!=(int)strlen(&line[*eaten])) { | |
323 | rrd_set_error("Not a valid offset: %s in line %s",&line[*eaten],line); | |
324 | return 1; | |
c4e7b680 | 325 | } |
7d0bd260 TO |
326 | (*eaten)+=i; |
327 | dprintf("- offset is number %li\n",gdp->shval); | |
328 | gdp->shidx = -1; | |
329 | } | |
330 | return 0; | |
c4e7b680 TO |
331 | } |
332 | ||
7d0bd260 TO |
333 | /* XPORT:_def_or_cdef[:legend] |
334 | */ | |
c4e7b680 | 335 | int |
7d0bd260 TO |
336 | rrd_parse_xport(const char *const line, unsigned int *const eaten, graph_desc_t *const gdp, image_desc_t *const im) { |
337 | if ((gdp->vidx=rrd_parse_find_vname(line,eaten,gdp,im))<0) return 1; | |
338 | ||
339 | switch (im->gdes[gdp->vidx].gf) { | |
340 | case GF_DEF: | |
341 | case GF_CDEF: | |
342 | dprintf("- vname is of type DEF or CDEF, OK\n"); | |
343 | break; | |
344 | case GF_VDEF: | |
345 | rrd_set_error("Cannot xport a VDEF: '%s' in line '%s'\n",im->gdes[gdp->vidx].vname,line); | |
346 | return 1; | |
347 | default: | |
348 | rrd_set_error("Encountered unknown type variable '%s' in line '%s'",im->gdes[gdp->vidx].vname,line); | |
349 | return 1; | |
350 | } | |
351 | dprintf("- looking for legend in '%s'\n",&line[*eaten]); | |
352 | if (rrd_parse_legend(line,eaten,gdp)) return 1; | |
353 | return 0; | |
c4e7b680 TO |
354 | } |
355 | ||
6465207c | 356 | /* Parsing of PART, VRULE, HRULE, LINE, AREA, STACK and TICK |
3da125e9 TO |
357 | ** is done in one function. |
358 | ** | |
359 | ** Stacking PART, VRULE, HRULE or TICK is not allowed. | |
6465207c AB |
360 | ** |
361 | ** If a number (which is valid to enter) is more than a | |
362 | ** certain amount of characters, it is caught as an error. | |
363 | ** While this is arguable, so is entering fixed numbers | |
364 | ** with more than MAX_VNAME_LEN significant digits. | |
365 | */ | |
366 | int | |
7d0bd260 | 367 | rrd_parse_PVHLAST(const char *const line, unsigned int *const eaten, graph_desc_t *const gdp, image_desc_t *const im) { |
3da125e9 | 368 | int i,j,k; |
6465207c AB |
369 | int colorfound=0; |
370 | char tmpstr[MAX_VNAME_LEN + 10]; /* vname#RRGGBBAA\0 */ | |
15dbcb6e TO |
371 | static int spacecnt = 0; |
372 | ||
373 | if (spacecnt == 0) { | |
374 | float one_space = gfx_get_text_width(im->canvas, 0, | |
375 | im->text_prop[TEXT_PROP_LEGEND].font, | |
376 | im->text_prop[TEXT_PROP_LEGEND].size, | |
377 | im->tabwidth," ", 0) / 4.0; | |
378 | float target_space = gfx_get_text_width(im->canvas, 0, | |
379 | im->text_prop[TEXT_PROP_LEGEND].font, | |
380 | im->text_prop[TEXT_PROP_LEGEND].size, | |
381 | im->tabwidth,"oo", 0); | |
382 | spacecnt = target_space / one_space; | |
383 | dprintf("- spacecnt: %i onespace: %f targspace: %f\n",spacecnt,one_space,target_space); | |
384 | } | |
385 | ||
6465207c AB |
386 | |
387 | dprintf("- parsing '%s'\n",&line[*eaten]); | |
6465207c | 388 | |
2cbe4e85 TO |
389 | /* have simpler code in the drawing section */ |
390 | if ( gdp->gf == GF_STACK ){ | |
391 | gdp->stack=1; | |
392 | } | |
393 | ||
6465207c AB |
394 | i=scan_for_col(&line[*eaten],MAX_VNAME_LEN+9,tmpstr); |
395 | if (line[*eaten+i]!='\0' && line[*eaten+i]!=':') { | |
396 | rrd_set_error("Cannot parse line '%s'",line); | |
397 | return 1; | |
398 | } | |
399 | ||
400 | j=i; while (j>0 && tmpstr[j]!='#') j--; | |
401 | ||
3da125e9 | 402 | if (j) { |
6465207c | 403 | tmpstr[j]='\0'; |
6465207c | 404 | } |
7d0bd260 TO |
405 | /* We now have: |
406 | * tmpstr[0] containing vname | |
407 | * tmpstr[j] if j!=0 then containing color | |
408 | * i size of vname + color | |
409 | * j if j!=0 then size of vname | |
410 | */ | |
6465207c | 411 | |
45e333f4 | 412 | /* Number or vname ? |
7d0bd260 TO |
413 | * If it is an existing vname, that's OK, provided that it is a |
414 | * valid type (need time for VRULE, not a float) | |
415 | * Else see if it parses as a number. | |
45e333f4 | 416 | */ |
7d0bd260 TO |
417 | dprintf("- examining string '%s'\n",tmpstr); |
418 | if ((gdp->vidx=find_var(im,tmpstr))>=0) { | |
419 | dprintf("- found vname: '%s' vidx %li\n",tmpstr,gdp->vidx); | |
420 | switch (gdp->gf) { | |
421 | #ifdef WITH_PIECHART | |
422 | case GF_PART: | |
423 | #endif | |
424 | case GF_VRULE: | |
425 | case GF_HRULE: | |
426 | if (im->gdes[gdp->vidx].gf != GF_VDEF) { | |
427 | rrd_set_error("Using vname %s of wrong type in line %s\n",im->gdes[gdp->gf].vname,line); | |
428 | return 1; | |
429 | } | |
430 | break; | |
431 | default:; | |
45e333f4 | 432 | } |
6465207c | 433 | } else { |
7d0bd260 TO |
434 | dprintf("- it is not an existing vname\n"); |
435 | switch (gdp->gf) { | |
436 | case GF_VRULE: | |
437 | k=0;sscanf(tmpstr,"%li%n",&gdp->xrule,&k); | |
438 | if (((j!=0)&&(k==j))||((j==0)&&(k==i))) { | |
439 | dprintf("- found time: %li\n",gdp->xrule); | |
440 | } else { | |
441 | dprintf("- is is not a valid number: %li\n",gdp->xrule); | |
442 | rrd_set_error("parameter '%s' does not represent time in line %s\n",tmpstr,line); | |
443 | return 1; | |
444 | } | |
445 | default: | |
446 | k=0;sscanf(tmpstr,"%lf%n",&gdp->yrule,&k); | |
447 | if (((j!=0)&&(k==j))||((j==0)&&(k==i))) { | |
448 | dprintf("- found number: %f\n",gdp->yrule); | |
449 | } else { | |
450 | dprintf("- is is not a valid number: %li\n",gdp->xrule); | |
451 | rrd_set_error("parameter '%s' does not represent a number in line %s\n",tmpstr,line); | |
452 | return 1; | |
453 | } | |
6465207c | 454 | } |
6465207c | 455 | } |
3da125e9 TO |
456 | |
457 | if (j) { | |
458 | j++; | |
459 | dprintf("- examining color '%s'\n",&tmpstr[j]); | |
460 | if (rrd_parse_color(&tmpstr[j],gdp)) { | |
461 | rrd_set_error("Could not parse color in '%s'",&tmpstr[j]); | |
462 | return 1; | |
463 | } | |
464 | dprintf("- parsed color 0x%08x\n",(unsigned int)gdp->col); | |
465 | colorfound=1; | |
466 | } else { | |
467 | dprintf("- no color present in '%s'\n",tmpstr); | |
6465207c | 468 | } |
6465207c | 469 | |
3da125e9 TO |
470 | (*eaten) += i; /* after vname#color */ |
471 | if (line[*eaten]!='\0') { | |
472 | (*eaten)++; /* after colon */ | |
473 | } | |
474 | ||
475 | if (gdp->gf == GF_TICK) { | |
476 | dprintf("- parsing '%s'\n",&line[*eaten]); | |
477 | dprintf("- looking for optional TICK number\n"); | |
478 | j=0; | |
479 | sscanf(&line[*eaten],"%lf%n",&gdp->yrule,&j); | |
480 | if (j) { | |
481 | if (line[*eaten+j]!='\0' && line[*eaten+j]!=':') { | |
482 | rrd_set_error("Cannot parse TICK fraction '%s'",line); | |
483 | return 1; | |
6465207c | 484 | } |
3da125e9 TO |
485 | dprintf("- found number %f\n",gdp->yrule); |
486 | if (gdp->yrule > 1.0 || gdp->yrule < -1.0) { | |
487 | rrd_set_error("Tick factor should be <= 1.0"); | |
488 | return 1; | |
489 | } | |
490 | (*eaten)+=j; | |
491 | } else { | |
492 | dprintf("- not found, defaulting to 0.1\n"); | |
493 | gdp->yrule=0.1; | |
6465207c | 494 | } |
3da125e9 TO |
495 | if (line[*eaten] == '\0') { |
496 | dprintf("- done parsing line\n"); | |
497 | return 0; | |
498 | } else { if (line[*eaten] == ':') { | |
499 | (*eaten)++; | |
500 | } else { | |
501 | rrd_set_error("Can't make sense of that TICK line"); | |
502 | return 1; | |
503 | } | |
504 | } | |
505 | } | |
506 | ||
507 | dprintf("- parsing '%s'\n",&line[*eaten]); | |
508 | ||
509 | /* Legend is next. A legend without a color is an error. | |
510 | ** Stacking an item without having a legend is OK however | |
511 | ** then an empty legend should be specified. | |
512 | ** LINE:val#color:STACK means legend is string "STACK" | |
513 | ** LINE:val#color::STACK means no legend, and do STACK | |
514 | ** LINE:val:STACK is an error (legend but no color) | |
515 | ** LINE:val::STACK means no legend, and do STACK | |
516 | */ | |
517 | if (colorfound) { | |
3da125e9 TO |
518 | int err=0; |
519 | char *linecp = strdup(line); | |
6465207c | 520 | dprintf("- looking for optional legend\n"); |
337d1b7f | 521 | |
3da125e9 | 522 | dprintf("- examining '%s'\n",&line[*eaten]); |
337d1b7f | 523 | if (linecp[*eaten] != '\0' && linecp[*eaten] != ':') { |
15dbcb6e TO |
524 | int spi; |
525 | /* If the legend is not empty, it has to be prefixed with spacecnt ' ' characters. This then gets | |
337d1b7f | 526 | * replaced by the color box later on. */ |
15dbcb6e TO |
527 | for (spi=0;spi<spacecnt && (*eaten) > 1;spi++){ |
528 | linecp[--(*eaten)]=' '; | |
529 | } | |
337d1b7f | 530 | } |
3da125e9 TO |
531 | |
532 | if (rrd_parse_legend(linecp, eaten, gdp)) err=1; | |
3da125e9 TO |
533 | free(linecp); |
534 | if (err) return 1; | |
535 | ||
536 | dprintf("- found legend '%s'\n", &gdp->legend[2]); | |
537 | } else { | |
538 | dprintf("- skipping empty legend\n"); | |
539 | if (line[*eaten] != '\0' && line[*eaten] != ':') { | |
540 | rrd_set_error("Legend set but no color: %s",&line[*eaten]); | |
541 | return 1; | |
542 | } | |
6465207c | 543 | } |
3da125e9 TO |
544 | if (line[*eaten]=='\0') { |
545 | dprintf("- done parsing line\n"); | |
546 | return 0; | |
547 | } | |
548 | (*eaten)++; /* after colon */ | |
6465207c | 549 | |
3da125e9 | 550 | /* PART, HRULE, VRULE and TICK cannot be stacked. */ |
6465207c AB |
551 | if ( (gdp->gf == GF_HRULE) |
552 | || (gdp->gf == GF_VRULE) | |
7d3f6363 | 553 | #ifdef WITH_PIECHART |
6465207c | 554 | || (gdp->gf == GF_PART) |
7d3f6363 | 555 | #endif |
6465207c AB |
556 | || (gdp->gf == GF_TICK) |
557 | ) return 0; | |
558 | ||
3da125e9 | 559 | dprintf("- parsing '%s'\n",&line[*eaten]); |
6465207c AB |
560 | if (line[*eaten]!='\0') { |
561 | dprintf("- still more, should be STACK\n"); | |
6465207c | 562 | j=scan_for_col(&line[*eaten],5,tmpstr); |
3da125e9 TO |
563 | if (line[*eaten+j]!='\0' && line[*eaten+j]!=':') { |
564 | /* not 5 chars */ | |
6465207c AB |
565 | rrd_set_error("Garbage found where STACK expected"); |
566 | return 1; | |
567 | } | |
568 | if (!strcmp("STACK",tmpstr)) { | |
569 | dprintf("- found STACK\n"); | |
570 | gdp->stack=1; | |
3da125e9 | 571 | (*eaten)+=j; |
6465207c AB |
572 | } else { |
573 | rrd_set_error("Garbage found where STACK expected"); | |
574 | return 1; | |
575 | } | |
576 | } | |
3da125e9 TO |
577 | if (line[*eaten]=='\0') { |
578 | dprintf("- done parsing line\n"); | |
579 | return 0; | |
580 | } | |
581 | (*eaten)++; | |
582 | dprintf("- parsing '%s'\n",&line[*eaten]); | |
583 | ||
6465207c AB |
584 | return 0; |
585 | } | |
586 | ||
587 | int | |
7d0bd260 | 588 | rrd_parse_make_vname(const char *const line, unsigned int *const eaten, graph_desc_t *const gdp, image_desc_t *const im) { |
6465207c AB |
589 | char tmpstr[MAX_VNAME_LEN + 10]; |
590 | int i=0; | |
adb27eb6 | 591 | |
6465207c AB |
592 | sscanf(&line[*eaten], DEF_NAM_FMT "=%n", tmpstr,&i); |
593 | if (!i) { | |
594 | rrd_set_error("Cannot parse vname from '%s'",line); | |
595 | return 1; | |
596 | } | |
597 | dprintf("- found candidate '%s'\n",tmpstr); | |
adb27eb6 | 598 | |
6465207c AB |
599 | if ((gdp->vidx=find_var(im,tmpstr))>=0) { |
600 | rrd_set_error("Attempting to reuse '%s'",im->gdes[gdp->vidx].vname); | |
601 | return 1; | |
602 | } | |
603 | strcpy(gdp->vname,tmpstr); | |
604 | dprintf("- created vname '%s' vidx %lu\n", gdp->vname,im->gdes_c-1); | |
605 | (*eaten)+=i; | |
606 | return 0; | |
adb27eb6 AB |
607 | } |
608 | ||
6465207c | 609 | int |
7d0bd260 | 610 | rrd_parse_def(const char *const line, unsigned int *const eaten, graph_desc_t *const gdp, image_desc_t *const im) { |
6465207c | 611 | int i=0; |
8db0db54 | 612 | char command[7]; /* step, start, end, reduce */ |
6465207c | 613 | char tmpstr[256]; |
051cc27e | 614 | struct rrd_time_value start_tv,end_tv; |
6465207c AB |
615 | time_t start_tmp=0,end_tmp=0; |
616 | char *parsetime_error=NULL; | |
617 | ||
618 | start_tv.type = end_tv.type=ABSOLUTE_TIME; | |
619 | start_tv.offset = end_tv.offset=0; | |
7ee93d7b TO |
620 | localtime_r(&gdp->start, &start_tv.tm); |
621 | localtime_r(&gdp->end, &end_tv.tm); | |
6465207c AB |
622 | |
623 | dprintf("- parsing '%s'\n",&line[*eaten]); | |
624 | dprintf("- from line '%s'\n",line); | |
625 | ||
7d0bd260 | 626 | if (rrd_parse_make_vname(line,eaten,gdp,im)) return 1; |
6465207c AB |
627 | i=scan_for_col(&line[*eaten],254,gdp->rrd); |
628 | if (line[*eaten+i]!=':') { | |
629 | rrd_set_error("Problems reading database name"); | |
630 | return 1; | |
631 | } | |
632 | (*eaten)+=++i; | |
633 | dprintf("- using file '%s'\n",gdp->rrd); | |
634 | ||
635 | i=0; | |
636 | sscanf(&line[*eaten], DS_NAM_FMT ":%n", gdp->ds_nam,&i); | |
637 | if (!i) { | |
638 | rrd_set_error("Cannot parse DS in '%s'",line); | |
639 | return 1; | |
640 | } | |
641 | (*eaten)+=i; | |
642 | dprintf("- using DS '%s'\n",gdp->ds_nam); | |
643 | ||
8db0db54 TO |
644 | if (rrd_parse_CF(line,eaten,gdp,&gdp->cf)) return 1; |
645 | gdp->cf_reduce = gdp->cf; | |
646 | ||
6465207c AB |
647 | if (line[*eaten]=='\0') return 0; |
648 | ||
649 | while (1) { | |
650 | dprintf("- optional parameter follows: %s\n", &line[*eaten]); | |
651 | i=0; | |
8db0db54 | 652 | sscanf(&line[*eaten], "%6[a-z]=%n", command, &i); |
6465207c AB |
653 | if (!i) { |
654 | rrd_set_error("Parse error in '%s'",line); | |
655 | return 1; | |
656 | } | |
657 | (*eaten)+=i; | |
658 | dprintf("- processing '%s'\n",command); | |
8db0db54 TO |
659 | if (!strcmp("reduce",command)) { |
660 | if (rrd_parse_CF(line,eaten,gdp,&gdp->cf_reduce)) return 1; | |
661 | if (line[*eaten] != '\0') | |
662 | (*eaten)--; | |
663 | } else if (!strcmp("step",command)) { | |
6465207c AB |
664 | i=0; |
665 | sscanf(&line[*eaten],"%lu%n",&gdp->step,&i); | |
564ca7e9 | 666 | gdp->step_orig = gdp->step; |
6465207c AB |
667 | (*eaten)+=i; |
668 | dprintf("- using step %lu\n",gdp->step); | |
669 | } else if (!strcmp("start",command)) { | |
670 | i=scan_for_col(&line[*eaten],255,tmpstr); | |
671 | (*eaten)+=i; | |
672 | if ((parsetime_error = parsetime(tmpstr, &start_tv))) { | |
673 | rrd_set_error( "start time: %s", parsetime_error ); | |
674 | return 1; | |
675 | } | |
676 | dprintf("- done parsing: '%s'\n",&line[*eaten]); | |
677 | } else if (!strcmp("end",command)) { | |
678 | i=scan_for_col(&line[*eaten],255,tmpstr); | |
679 | (*eaten)+=i; | |
680 | if ((parsetime_error = parsetime(tmpstr, &end_tv))) { | |
681 | rrd_set_error( "end time: %s", parsetime_error ); | |
682 | return 1; | |
683 | } | |
684 | dprintf("- done parsing: '%s'\n",&line[*eaten]); | |
685 | } else { | |
686 | rrd_set_error("Parse error in '%s'",line); | |
687 | return 1; | |
688 | } | |
689 | if (line[*eaten]=='\0') break; | |
690 | if (line[*eaten]!=':') { | |
691 | dprintf("- Expected to see end of string but got '%s'\n",\ | |
692 | &line[*eaten]); | |
693 | rrd_set_error("Parse error in '%s'",line); | |
694 | return 1; | |
695 | } | |
696 | (*eaten)++; | |
697 | } | |
698 | if (proc_start_end(&start_tv,&end_tv,&start_tmp,&end_tmp) == -1){ | |
699 | /* error string is set in parsetime.c */ | |
700 | return 1; | |
701 | } | |
702 | if (start_tmp < 3600*24*365*10) { | |
703 | rrd_set_error("the first entry to fetch should be " | |
704 | "after 1980 (%ld)",start_tmp); | |
705 | return 1; | |
706 | } | |
707 | ||
708 | if (end_tmp < start_tmp) { | |
709 | rrd_set_error("start (%ld) should be less than end (%ld)", | |
710 | start_tmp, end_tmp); | |
711 | return 1; | |
712 | } | |
713 | ||
714 | gdp->start = start_tmp; | |
715 | gdp->end = end_tmp; | |
564ca7e9 TO |
716 | gdp->start_orig = start_tmp; |
717 | gdp->end_orig = end_tmp; | |
6465207c AB |
718 | |
719 | dprintf("- start time %lu\n",gdp->start); | |
720 | dprintf("- end time %lu\n",gdp->end); | |
721 | ||
722 | return 0; | |
723 | } | |
adb27eb6 | 724 | |
6465207c | 725 | int |
7d0bd260 | 726 | rrd_parse_vdef(const char *const line, unsigned int *const eaten, graph_desc_t *const gdp, image_desc_t *const im) { |
6465207c AB |
727 | char tmpstr[MAX_VNAME_LEN+1]; /* vname\0 */ |
728 | int i=0; | |
729 | ||
730 | dprintf("- parsing '%s'\n",&line[*eaten]); | |
7d0bd260 | 731 | if (rrd_parse_make_vname(line,eaten,gdp,im)) return 1; |
6465207c AB |
732 | |
733 | sscanf(&line[*eaten], DEF_NAM_FMT ",%n", tmpstr,&i); | |
734 | if (!i) { | |
735 | rrd_set_error("Cannot parse line '%s'",line); | |
736 | return 1; | |
737 | } | |
738 | if ((gdp->vidx=find_var(im,tmpstr))<0) { | |
739 | rrd_set_error("Not a valid vname: %s in line %s",tmpstr,line); | |
740 | return 1; | |
741 | } | |
742 | if ( im->gdes[gdp->vidx].gf != GF_DEF | |
743 | && im->gdes[gdp->vidx].gf != GF_CDEF) { | |
744 | rrd_set_error("variable '%s' not DEF nor " | |
745 | "CDEF in VDEF '%s'", tmpstr,gdp->vname); | |
746 | return 1; | |
747 | } | |
748 | dprintf("- found vname: '%s' vidx %li\n",tmpstr,gdp->vidx); | |
749 | (*eaten)+=i; | |
750 | ||
751 | dprintf("- calling vdef_parse with param '%s'\n",&line[*eaten]); | |
752 | vdef_parse(gdp,&line[*eaten]); | |
753 | while (line[*eaten]!='\0'&&line[*eaten]!=':') | |
754 | (*eaten)++; | |
755 | ||
756 | return 0; | |
757 | } | |
758 | ||
759 | int | |
7d0bd260 | 760 | rrd_parse_cdef(const char *const line, unsigned int *const eaten, graph_desc_t *const gdp, image_desc_t *const im) { |
6465207c | 761 | dprintf("- parsing '%s'\n",&line[*eaten]); |
7d0bd260 | 762 | if (rrd_parse_make_vname(line,eaten,gdp,im)) return 1; |
6465207c AB |
763 | if ((gdp->rpnp = rpn_parse( |
764 | (void *)im, | |
765 | &line[*eaten], | |
766 | &find_var_wrapper) | |
767 | )==NULL) { | |
768 | rrd_set_error("invalid rpn expression in: %s",&line[*eaten]); | |
769 | return 1; | |
770 | }; | |
771 | while (line[*eaten]!='\0'&&line[*eaten]!=':') | |
772 | (*eaten)++; | |
773 | return 0; | |
774 | } | |
775 | ||
776 | void | |
7d0bd260 | 777 | rrd_graph_script(int argc, char *argv[], image_desc_t *const im, int optno) { |
6465207c | 778 | int i; |
e2cc7239 TO |
779 | /* save state for STACK backward compat function */ |
780 | enum gf_en last_gf=GF_PRINT; | |
781 | float last_linewidth=0.0; | |
6465207c | 782 | |
c4e7b680 | 783 | for (i=optind+optno;i<argc;i++) { |
6465207c AB |
784 | graph_desc_t *gdp; |
785 | unsigned int eaten=0; | |
786 | ||
787 | if (gdes_alloc(im)) return; /* the error string is already set */ | |
788 | gdp = &im->gdes[im->gdes_c-1]; | |
789 | #ifdef DEBUG | |
790 | gdp->debug = 1; | |
791 | #endif | |
792 | ||
793 | if (rrd_parse_find_gf(argv[i],&eaten,gdp)) return; | |
e2cc7239 | 794 | |
6465207c | 795 | switch (gdp->gf) { |
c4e7b680 TO |
796 | case GF_SHIFT: /* vname:value */ |
797 | if (rrd_parse_shift(argv[i],&eaten,gdp,im)) return; | |
798 | break; | |
6465207c | 799 | case GF_XPORT: |
c4e7b680 | 800 | if (rrd_parse_xport(argv[i],&eaten,gdp,im)) return; |
6465207c AB |
801 | break; |
802 | case GF_PRINT: /* vname:CF:format -or- vname:format */ | |
363838ac | 803 | im->prt_c++; |
6465207c AB |
804 | case GF_GPRINT: /* vname:CF:format -or- vname:format */ |
805 | if (rrd_parse_print(argv[i],&eaten,gdp,im)) return; | |
806 | break; | |
807 | case GF_COMMENT: /* text */ | |
808 | if (rrd_parse_legend(argv[i],&eaten,gdp)) return; | |
809 | break; | |
7d3f6363 | 810 | #ifdef WITH_PIECHART |
6465207c | 811 | case GF_PART: /* value[#color[:legend]] */ |
7d3f6363 | 812 | #endif |
6465207c AB |
813 | case GF_VRULE: /* value#color[:legend] */ |
814 | case GF_HRULE: /* value#color[:legend] */ | |
815 | case GF_LINE: /* vname-or-value[#color[:legend]][:STACK] */ | |
816 | case GF_AREA: /* vname-or-value[#color[:legend]][:STACK] */ | |
6465207c | 817 | case GF_TICK: /* vname#color[:num[:legend]] */ |
e2cc7239 TO |
818 | if (rrd_parse_PVHLAST(argv[i],&eaten,gdp,im))return; |
819 | last_gf = gdp->gf; | |
820 | last_linewidth = gdp->linewidth; | |
821 | break; | |
822 | case GF_STACK: /* vname-or-value[#color[:legend]] */ | |
823 | if (rrd_parse_PVHLAST(argv[i],&eaten,gdp,im))return; | |
824 | if (last_gf == GF_LINE || last_gf == GF_AREA){ | |
825 | gdp->gf = last_gf; | |
826 | gdp->linewidth = last_linewidth; | |
827 | } else { | |
828 | rrd_set_error("STACK must follow LINE or AREA! command:\n%s", | |
829 | &argv[i][eaten],argv[i]); | |
830 | return; | |
831 | } | |
6465207c AB |
832 | break; |
833 | /* data acquisition */ | |
834 | case GF_DEF: /* vname=x:DS:CF:[:step=#][:start=#][:end=#] */ | |
835 | if (rrd_parse_def(argv[i],&eaten,gdp,im)) return; | |
836 | break; | |
837 | case GF_CDEF: /* vname=rpn-expression */ | |
838 | if (rrd_parse_cdef(argv[i],&eaten,gdp,im)) return; | |
839 | break; | |
840 | case GF_VDEF: /* vname=rpn-expression */ | |
841 | if (rrd_parse_vdef(argv[i],&eaten,gdp,im)) return; | |
842 | break; | |
843 | } | |
844 | if (gdp->debug) { | |
845 | dprintf("used %i out of %i chars\n",eaten,strlen(argv[i])); | |
846 | dprintf("parsed line: '%s'\n",argv[i]); | |
847 | dprintf("remaining: '%s'\n",&argv[i][eaten]); | |
848 | if (eaten >= strlen(argv[i])) | |
849 | dprintf("Command finished successfully\n"); | |
850 | } | |
851 | if (eaten < strlen(argv[i])) { | |
852 | rrd_set_error("Garbage '%s' after command:\n%s", | |
853 | &argv[i][eaten],argv[i]); | |
854 | return; | |
855 | } | |
856 | } | |
857 | } |