1 /*
2 * Copyright (c) 2002-2006 Daniel Hartmeier
3 * Copyright (c) 2013 Nikola Kolev
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above
13 * copyright notice, this list of conditions and the following
14 * disclaimer in the documentation and/or other materials provided
15 * with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
20 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
21 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
23 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
27 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 *
30 */
31
32 %{
33
34 #include <sys/types.h>
35 #include <ctype.h>
36 #include <errno.h>
37 #include <err.h>
38 #include <pwd.h>
39 #include <stdarg.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <time.h>
44
45 #include "data.h"
46 #include "graph.h"
47
48 extern int add_col(unsigned nr, const char *arg, int diff);
49
50 static const char *infile = NULL;
51 static struct matrix **matrices = NULL;
52 static FILE *fin = NULL;
53 static int debug = 0;
54 static int lineno = 1;
55 static int errors = 0;
56
57 int yyerror(char *, ...);
58 int yyparse();
59
60 struct node_graph {
61 struct graph graph;
62 struct node_graph *next;
63 };
64
65 typedef struct {
66 union {
67 int number;
68 char *string;
69 struct {
70 int beg;
71 int end;
72 } time;
73 struct {
74 int width;
75 int height;
76 } size;
77 struct {
78 struct node_graph *graph;
79 } side;
80 struct node_graph *graph;
81 struct {
82 int type;
83 char *arg;
84 int idx;
85 int diff;
86 } col;
87 } v;
88 int lineno;
89 } YYSTYPE;
90
91 %}
92
93 %token ERROR IMAGE TIME MINUTES HOURS DAYS WEEKS MONTHS YEARS TO NOW
94 %token WIDTH HEIGHT LEFT RIGHT GRAPH COLOR FILLED TYPE JPEG PNG
95 %token COLLECT DIFF BPS AVG MIN MAX
96 %token <v.string> STRING
97 %token <v.number> NUMBER
98 %type <v.time> timerange
99 %type <v.size> size
100 %type <v.number> type
101 %type <v.side> left right
102 %type <v.graph> graph_item graph_list
103 %type <v.number> time filled diff bps avg
104 %%
105
106 configuration : /* empty */
107 | configuration collect
108 | configuration image
109 | configuration error { errors++; }
110 ;
111
112 collect : COLLECT NUMBER '=' STRING diff
113 {
114 if (add_col($2, $4, $5)) {
115 yyerror("add_col() failed");
116 YYERROR;
117 }
118 }
119 ;
120
121 diff : /* empty */ { $$ = 0; }
122 | DIFF { $$ = 1; }
123 ;
124
125 image : IMAGE STRING '{' timerange type size left right '}'
126 {
127 struct node_graph *g, *h;
128
129 if ($7.graph == NULL && $8.graph == NULL) {
130 yyerror("neither left nor right graph defined");
131 YYERROR;
132 }
133 graph_add_matrix(matrices, $2, $5, $6.width, $6.height,
134 $4.beg, $4.end);
135 g = $7.graph;
136 while (g != NULL) {
137 graph_add_graph(&(*matrices)->graphs[0],
138 (*matrices)->w0, g->graph.desc_nr,
139 g->graph.label, g->graph.unit,
140 g->graph.color, g->graph.filled,
141 g->graph.bytes, g->graph.type);
142 h = g;
143 g = g->next;
144 free(h->graph.label);
145 free(h);
146 }
147 g = $8.graph;
148 while (g != NULL) {
149 graph_add_graph(&(*matrices)->graphs[1],
150 (*matrices)->w0, g->graph.desc_nr,
151 g->graph.label, g->graph.unit,
152 g->graph.color, g->graph.filled,
153 g->graph.bytes, g->graph.type);
154 h = g;
155 g = g->next;
156 free(h->graph.label);
157 free(h);
158 }
159 }
160 ;
161
162 timerange : /* empty */ {
163 $$.end = time(0);
164 $$.beg = $$.end - 24 * 60;
165 }
166 | TIME time {
167 $$.end = time(0);
168 $$.beg = $2;
169 }
170 | TIME time TO time {
171 $$.end = $4;
172 $$.beg = $2;
173 }
174 ;
175
176 time : NOW {
177 $$ = time(0);
178 }
179 | NUMBER {
180 $$ = $1;
181 }
182 | NUMBER MINUTES {
183 $$ = time(0) - $1 * 60;
184 }
185 | NUMBER HOURS {
186 $$ = time(0) - $1 * 60 * 60;
187 }
188 | NUMBER DAYS {
189 $$ = time(0) - $1 * 60 * 60 * 24;
190 }
191 | NUMBER WEEKS {
192 $$ = time(0) - $1 * 60 * 60 * 24 * 7;
193 }
194 | NUMBER MONTHS {
195 $$ = time(0)- $1 * 60 * 60 * 24 * 30;
196 }
197 | NUMBER YEARS {
198 $$ = time(0)- $1 * 60 * 60 * 24 * 365;
199 }
200 ;
201
202 size : /* empty */ {
203 $$.width = 640;
204 $$.height = 320;
205 }
206 | WIDTH NUMBER HEIGHT NUMBER {
207 $$.width = $2;
208 $$.height = $4;
209 }
210 ;
211
212 type : /* empty */ { $$ = 0; }
213 | TYPE JPEG { $$ = 0; }
214 | TYPE PNG { $$ = 1; }
215 ;
216
217 left : /* empty */ { $$.graph = NULL; }
218 | LEFT graph_list { $$.graph = $2; }
219 ;
220
221 right : /* empty */ { $$.graph = NULL; }
222 | RIGHT graph_list { $$.graph = $2; }
223 ;
224
225 graph_list : graph_item { $$ = $1; }
226 | graph_list ',' graph_item { $3->next = $1; $$ = $3; }
227 ;
228
229 graph_item : GRAPH NUMBER bps avg STRING STRING COLOR NUMBER NUMBER NUMBER filled
230 {
231 $$ = calloc(1, sizeof(struct node_graph));
232 if ($$ == NULL)
233 err(1, "graph_item: calloc");
234 $$->graph.desc_nr = $2;
235 $$->graph.bytes = $3;
236 $$->graph.type = $4;
237 $$->graph.label = strdup($5);
238 $$->graph.unit = strdup($6);
239 if ($8 < 0 || $8 > 255 || $9 < 0 || $9 > 255 ||
240 $10 < 0 || $10 > 255) {
241 yyerror("invalid color %d %d %d", $8, $9, $10);
242 YYERROR;
243 }
244 $$->graph.color = $8 << 16 | $9 << 8 | $10;
245 $$->graph.filled = $11;
246 }
247 ;
248
249 avg : /* empty */ { $$ = DATA_TYPE_AVG; }
250 | AVG { $$ = DATA_TYPE_AVG; }
251 | MIN { $$ = DATA_TYPE_MIN; }
252 | MAX { $$ = DATA_TYPE_MAX; }
253 ;
254
255 filled : /* empty */ { $$ = 0; }
256 | FILLED { $$ = 1; }
257 ;
258
259 bps : /* empty */ { $$ = 0; }
260 | BPS { $$ = 1; }
261 ;
262
263 %%
264
265 int
266 yyerror(char *fmt, ...)
267 {
268 va_list ap;
269 errors = 1;
270
271 va_start(ap, fmt);
272 fprintf(stderr, "%s:%d: ", infile, yylval.lineno);
273 vfprintf(stderr, fmt, ap);
274 fprintf(stderr, "\n");
275 va_end(ap);
276 return (0);
277 }
278
279 struct keywords {
280 const char *k_name;
281 int k_val;
282 };
283
284 int
kw_cmp(const void * k,const void * e)285 kw_cmp(const void *k, const void *e)
286 {
287 return (strcmp(k, ((struct keywords *)e)->k_name));
288 }
289
290 int
lookup(char * s)291 lookup(char *s)
292 {
293 /* this has to be sorted always */
294 static const struct keywords keywords[] = {
295 { "avg", AVG },
296 { "bps", BPS },
297 { "collect", COLLECT },
298 { "color", COLOR },
299 { "days", DAYS },
300 { "diff", DIFF },
301 { "filled", FILLED },
302 { "from", TIME },
303 { "graph", GRAPH },
304 { "height", HEIGHT },
305 { "hours", HOURS },
306 { "image", IMAGE },
307 { "jpeg", JPEG },
308 { "left", LEFT },
309 { "max", MAX },
310 { "min", MIN },
311 { "minutes", MINUTES },
312 { "months", MONTHS },
313 { "now", NOW },
314 { "png", PNG },
315 { "right", RIGHT },
316 { "to", TO },
317 { "type", TYPE },
318 { "weeks", WEEKS },
319 { "width", WIDTH },
320 { "years", YEARS },
321 };
322 const struct keywords *p;
323
324 p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
325 sizeof(keywords[0]), kw_cmp);
326
327 if (p)
328 return (p->k_val);
329 else
330 return (STRING);
331 }
332
333 char *parsebuf;
334 int parseindex;
335
336 int
lgetc(FILE * fin)337 lgetc(FILE *fin)
338 {
339 int c, next;
340
341 restart:
342 if (parsebuf) {
343 /* Reading characters from the parse buffer, instead of input */
344 c = parsebuf[parseindex++];
345 if (c != '\0')
346 return (c);
347 free(parsebuf);
348 parsebuf = NULL;
349 parseindex = 0;
350 goto restart;
351 }
352
353 c = getc(fin);
354 if (c == '\\') {
355 next = getc(fin);
356 if (next != '\n') {
357 ungetc(next, fin);
358 return (c);
359 }
360 yylval.lineno = lineno;
361 lineno++;
362 goto restart;
363 }
364 if (c == '#') {
365 c = getc(fin);
366 while (c != '\n' && c != EOF)
367 c = getc(fin);
368 }
369 if (c == '\n') {
370 yylval.lineno = lineno;
371 lineno++;
372 }
373 return (c);
374 }
375
376 int
lungetc(int c,FILE * fin)377 lungetc(int c, FILE *fin)
378 {
379 if (parsebuf && parseindex) {
380 /* XXX breaks on index 0 */
381 parseindex--;
382 return (c);
383 }
384 if (c == '\n') {
385 yylval.lineno = lineno;
386 lineno--;
387 }
388 return ungetc(c, fin);
389 }
390
391 int
findeol()392 findeol()
393 {
394 int c;
395
396 if (parsebuf) {
397 free(parsebuf);
398 parsebuf = NULL;
399 parseindex = 0;
400 }
401
402 /* skip to either EOF or the first real EOL */
403 while (1) {
404 c = lgetc(fin);
405 if (c == '\n')
406 break;
407 if (c == EOF)
408 break;
409 }
410 return (ERROR);
411 }
412
413 int
yylex(void)414 yylex(void)
415 {
416 char buf[8096], *p;
417 int endc, c;
418 int token;
419
420 p = buf;
421 while ((c = lgetc(fin)) == ' ' || c == '\t' || c == '\n')
422 ;
423
424 yylval.lineno = lineno;
425 if (c == '#')
426 while ((c = lgetc(fin)) != '\n' && c != EOF)
427 ;
428
429 switch (c) {
430 case '\'':
431 case '"':
432 endc = c;
433 while (1) {
434 if ((c = lgetc(fin)) == EOF)
435 return (0);
436 if (c == endc) {
437 *p = '\0';
438 break;
439 }
440 if (c == '\n')
441 continue;
442 if (p + 1 >= buf + sizeof(buf) - 1) {
443 yyerror("string too long");
444 return (findeol());
445 }
446 *p++ = (char)c;
447 }
448 yylval.v.string = strdup(buf);
449 if (yylval.v.string == NULL)
450 err(1, "yylex: strdup");
451 return (STRING);
452 }
453
454 if (isdigit(c)) {
455 int index = 0, base = 10;
456 u_int64_t n = 0;
457
458 yylval.v.number = 0;
459 while (1) {
460 if (base == 10) {
461 if (!isdigit(c))
462 break;
463 c -= '0';
464 } else if (base == 16) {
465 if (isdigit(c))
466 c -= '0';
467 else if (c >= 'a' && c <= 'f')
468 c -= 'a' - 10;
469 else if (c >= 'A' && c <= 'F')
470 c -= 'A' - 10;
471 else
472 break;
473 }
474 n = n * base + c;
475
476 c = lgetc(fin);
477 if (c == EOF)
478 break;
479 if (index++ == 0 && n == 0 && c == 'x') {
480 base = 16;
481 c = lgetc(fin);
482 if (c == EOF)
483 break;
484 }
485 }
486 yylval.v.number = (u_int32_t)n;
487
488 if (c != EOF)
489 lungetc(c, fin);
490 if (debug > 1)
491 fprintf(stderr, "number: %d\n", yylval.v.number);
492 return (NUMBER);
493 }
494
495 #define allowed_in_string(x) \
496 (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
497 x != '{' && x != '}' && x != '<' && x != '>' && \
498 x != '!' && x != '=' && x != '/' && x != '#' && \
499 x != ',' && x != '(' && x != ')'))
500
501 if (isalnum(c)) {
502 do {
503 *p++ = c;
504 if (p-buf >= sizeof buf) {
505 yyerror("string too long");
506 return (ERROR);
507 }
508 } while ((c = lgetc(fin)) != EOF && (allowed_in_string(c)));
509 lungetc(c, fin);
510 *p = '\0';
511 token = lookup(buf);
512 yylval.v.string = strdup(buf);
513 if (yylval.v.string == NULL)
514 err(1, "yylex: strdup");
515 return (token);
516 }
517 if (c == EOF)
518 return (0);
519 return (c);
520 }
521
522 int
parse_config(const char * n,struct matrix ** m)523 parse_config(const char *n, struct matrix **m)
524 {
525 infile = n;
526 fin = fopen(infile, "rb");
527 if (fin == NULL) {
528 perror("fopen() failed");
529 return (1);
530 }
531 matrices = m;
532 lineno = 1;
533 errors = 0;
534 yyparse();
535 fclose(fin);
536 return (errors ? -1 : 0);
537 }
538