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