xref: /dflybsd-src/usr.bin/dfregress/parser.c (revision 86d7f5d305c6adaa56ff4582ece9859d73106103)
186d7f5d3SJohn Marino /*
286d7f5d3SJohn Marino  * Copyright (c) 2009-2011 Alex Hornung <alex@alexhornung.com>.
386d7f5d3SJohn Marino  * All rights reserved.
486d7f5d3SJohn Marino  *
586d7f5d3SJohn Marino  * Redistribution and use in source and binary forms, with or without
686d7f5d3SJohn Marino  * modification, are permitted provided that the following conditions
786d7f5d3SJohn Marino  * are met:
886d7f5d3SJohn Marino  *
986d7f5d3SJohn Marino  * 1. Redistributions of source code must retain the above copyright
1086d7f5d3SJohn Marino  *    notice, this list of conditions and the following disclaimer.
1186d7f5d3SJohn Marino  * 2. Redistributions in binary form must reproduce the above copyright
1286d7f5d3SJohn Marino  *    notice, this list of conditions and the following disclaimer in
1386d7f5d3SJohn Marino  *    the documentation and/or other materials provided with the
1486d7f5d3SJohn Marino  *    distribution.
1586d7f5d3SJohn Marino  *
1686d7f5d3SJohn Marino  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1786d7f5d3SJohn Marino  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1886d7f5d3SJohn Marino  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
1986d7f5d3SJohn Marino  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
2086d7f5d3SJohn Marino  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
2186d7f5d3SJohn Marino  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
2286d7f5d3SJohn Marino  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2386d7f5d3SJohn Marino  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
2486d7f5d3SJohn Marino  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
2586d7f5d3SJohn Marino  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
2686d7f5d3SJohn Marino  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2786d7f5d3SJohn Marino  * SUCH DAMAGE.
2886d7f5d3SJohn Marino  */
2986d7f5d3SJohn Marino 
3086d7f5d3SJohn Marino #include <stdio.h>
3186d7f5d3SJohn Marino #include <stdarg.h>
3286d7f5d3SJohn Marino #include <string.h>
3386d7f5d3SJohn Marino #include <stdlib.h>
3486d7f5d3SJohn Marino #include <unistd.h>
3586d7f5d3SJohn Marino #include <errno.h>
3686d7f5d3SJohn Marino #include <err.h>
3786d7f5d3SJohn Marino #include "parser.h"
3886d7f5d3SJohn Marino 
3986d7f5d3SJohn Marino #define _iswhitespace(X)	((((X) == ' ') || ((X) == '\t'))?1:0)
4086d7f5d3SJohn Marino 
4186d7f5d3SJohn Marino 
4286d7f5d3SJohn Marino static int line_no = 1;
4386d7f5d3SJohn Marino 
iswhitespace(char c)4486d7f5d3SJohn Marino static int iswhitespace(char c)
4586d7f5d3SJohn Marino {
4686d7f5d3SJohn Marino 	return _iswhitespace(c);
4786d7f5d3SJohn Marino }
4886d7f5d3SJohn Marino 
iscomma(char c)4986d7f5d3SJohn Marino static int iscomma(char c)
5086d7f5d3SJohn Marino {
5186d7f5d3SJohn Marino 	return (c == ',');
5286d7f5d3SJohn Marino }
5386d7f5d3SJohn Marino 
5486d7f5d3SJohn Marino void
syntax_error(const char * fmt,...)5586d7f5d3SJohn Marino syntax_error(const char *fmt, ...)
5686d7f5d3SJohn Marino {
5786d7f5d3SJohn Marino 	char buf[1024];
5886d7f5d3SJohn Marino 	va_list ap;
5986d7f5d3SJohn Marino 
6086d7f5d3SJohn Marino 	va_start(ap, fmt);
6186d7f5d3SJohn Marino 	vsnprintf(buf, sizeof(buf), fmt, ap);
6286d7f5d3SJohn Marino 	va_end(ap);
6386d7f5d3SJohn Marino 	errx(1, "syntax error on line %d: %s\n", line_no, buf);
6486d7f5d3SJohn Marino }
6586d7f5d3SJohn Marino 
6686d7f5d3SJohn Marino 
6786d7f5d3SJohn Marino int
entry_check_num_args(char ** tokens,int num)6886d7f5d3SJohn Marino entry_check_num_args(char **tokens, int num)
6986d7f5d3SJohn Marino {
7086d7f5d3SJohn Marino 	int i;
7186d7f5d3SJohn Marino 
7286d7f5d3SJohn Marino 	for (i = 0; tokens[i] != NULL; i++)
7386d7f5d3SJohn Marino 		;
7486d7f5d3SJohn Marino 
7586d7f5d3SJohn Marino 	if (i < num) {
7686d7f5d3SJohn Marino 		syntax_error("at least %d tokens were expected but only %d "
7786d7f5d3SJohn Marino 		    "were found", num, i);
7886d7f5d3SJohn Marino 		return 1;
7986d7f5d3SJohn Marino 	}
8086d7f5d3SJohn Marino 	return 0;
8186d7f5d3SJohn Marino }
8286d7f5d3SJohn Marino 
8386d7f5d3SJohn Marino static int
line_tokenize(char * buffer,int (* is_sep)(char),char comment_char,char ** tokens)8486d7f5d3SJohn Marino line_tokenize(char *buffer, int (*is_sep)(char), char comment_char, char **tokens)
8586d7f5d3SJohn Marino {
8686d7f5d3SJohn Marino 	int c, n, i;
8786d7f5d3SJohn Marino 	int quote = 0;
8886d7f5d3SJohn Marino 
8986d7f5d3SJohn Marino 	i = strlen(buffer) + 1;
9086d7f5d3SJohn Marino 	c = 0;
9186d7f5d3SJohn Marino 
9286d7f5d3SJohn Marino 	/* Skip leading white-space */
9386d7f5d3SJohn Marino 	while ((_iswhitespace(buffer[c])) && (c < i)) c++;
9486d7f5d3SJohn Marino 
9586d7f5d3SJohn Marino 	/*
9686d7f5d3SJohn Marino 	 * If this line effectively (after indentation) begins with the comment
9786d7f5d3SJohn Marino 	 * character, we ignore the rest of the line.
9886d7f5d3SJohn Marino 	 */
9986d7f5d3SJohn Marino 	if (buffer[c] == comment_char)
10086d7f5d3SJohn Marino 		return 0;
10186d7f5d3SJohn Marino 
10286d7f5d3SJohn Marino 	tokens[0] = &buffer[c];
10386d7f5d3SJohn Marino 	for (n = 1; c < i; c++) {
10486d7f5d3SJohn Marino 		if (buffer[c] == '"') {
10586d7f5d3SJohn Marino 			quote = !quote;
10686d7f5d3SJohn Marino 			if (quote) {
10786d7f5d3SJohn Marino 				if ((c >= 1) && (&buffer[c] != tokens[n-1])) {
10886d7f5d3SJohn Marino #if 0
10986d7f5d3SJohn Marino 					syntax_error("stray opening quote not "
11086d7f5d3SJohn Marino 					    "at beginning of token");
11186d7f5d3SJohn Marino 					/* NOTREACHED */
11286d7f5d3SJohn Marino #endif
11386d7f5d3SJohn Marino 				} else {
11486d7f5d3SJohn Marino 					tokens[n-1] = &buffer[c+1];
11586d7f5d3SJohn Marino 				}
11686d7f5d3SJohn Marino 			} else {
11786d7f5d3SJohn Marino 				if ((c < i-1) && (!is_sep(buffer[c+1]))) {
11886d7f5d3SJohn Marino #if 0
11986d7f5d3SJohn Marino 					syntax_error("stray closing quote not "
12086d7f5d3SJohn Marino 					    "at end of token");
12186d7f5d3SJohn Marino 					/* NOTREACHED */
12286d7f5d3SJohn Marino #endif
12386d7f5d3SJohn Marino 				} else {
12486d7f5d3SJohn Marino 					buffer[c] = '\0';
12586d7f5d3SJohn Marino 				}
12686d7f5d3SJohn Marino 			}
12786d7f5d3SJohn Marino 		}
12886d7f5d3SJohn Marino 
12986d7f5d3SJohn Marino 		if (quote) {
13086d7f5d3SJohn Marino 			continue;
13186d7f5d3SJohn Marino 		}
13286d7f5d3SJohn Marino 
13386d7f5d3SJohn Marino 		if (is_sep(buffer[c])) {
13486d7f5d3SJohn Marino 			buffer[c++] = '\0';
13586d7f5d3SJohn Marino 			while ((_iswhitespace(buffer[c])) && (c < i)) c++;
13686d7f5d3SJohn Marino 			tokens[n++] = &buffer[c--];
13786d7f5d3SJohn Marino 		}
13886d7f5d3SJohn Marino 	}
13986d7f5d3SJohn Marino 	tokens[n] = NULL;
14086d7f5d3SJohn Marino 
14186d7f5d3SJohn Marino 	if (quote) {
14286d7f5d3SJohn Marino 		tokens[0] = NULL;
14386d7f5d3SJohn Marino 		return 0;
14486d7f5d3SJohn Marino 	}
14586d7f5d3SJohn Marino 
14686d7f5d3SJohn Marino 	return n;
14786d7f5d3SJohn Marino }
14886d7f5d3SJohn Marino 
14986d7f5d3SJohn Marino static int
process_line(FILE * fd,parser_t parser,void * arg)15086d7f5d3SJohn Marino process_line(FILE* fd, parser_t parser, void *arg)
15186d7f5d3SJohn Marino {
15286d7f5d3SJohn Marino 	char buffer[4096];
15386d7f5d3SJohn Marino 	char *tokens[256];
15486d7f5d3SJohn Marino 	int c, n, i = 0;
15586d7f5d3SJohn Marino 	int ret = 0;
15686d7f5d3SJohn Marino 
15786d7f5d3SJohn Marino 	while (((c = fgetc(fd)) != EOF) && (c != '\n')) {
15886d7f5d3SJohn Marino 		buffer[i++] = (char)c;
15986d7f5d3SJohn Marino 		if (i == (sizeof(buffer) -1))
16086d7f5d3SJohn Marino 			break;
16186d7f5d3SJohn Marino 	}
16286d7f5d3SJohn Marino 	buffer[i] = '\0';
16386d7f5d3SJohn Marino 
16486d7f5d3SJohn Marino 	if (feof(fd) || ferror(fd))
16586d7f5d3SJohn Marino 		ret = 1;
16686d7f5d3SJohn Marino 
16786d7f5d3SJohn Marino 
16886d7f5d3SJohn Marino 	n = line_tokenize(buffer, &iswhitespace, '#', tokens);
16986d7f5d3SJohn Marino 
17086d7f5d3SJohn Marino 	/*
17186d7f5d3SJohn Marino 	 * If there are not enough arguments for any function or it is
17286d7f5d3SJohn Marino 	 * a line full of whitespaces, we just return here. Or if a
17386d7f5d3SJohn Marino 	 * quote wasn't closed.
17486d7f5d3SJohn Marino 	 */
17586d7f5d3SJohn Marino 	if ((n < 1) || (tokens[0][0] == '\0'))
17686d7f5d3SJohn Marino 		return ret;
17786d7f5d3SJohn Marino 
17886d7f5d3SJohn Marino 	parser(arg, tokens);
17986d7f5d3SJohn Marino 
18086d7f5d3SJohn Marino 	return ret;
18186d7f5d3SJohn Marino }
18286d7f5d3SJohn Marino 
18386d7f5d3SJohn Marino int
parse_options(char * str,char ** options)18486d7f5d3SJohn Marino parse_options(char *str, char **options)
18586d7f5d3SJohn Marino {
18686d7f5d3SJohn Marino 	int i;
18786d7f5d3SJohn Marino 
18886d7f5d3SJohn Marino 	i = line_tokenize(str, &iscomma, '#', options);
18986d7f5d3SJohn Marino 	if (i == 0)
19086d7f5d3SJohn Marino 		syntax_error("Invalid expression in options token");
19186d7f5d3SJohn Marino 		/* NOTREACHED */
19286d7f5d3SJohn Marino 
19386d7f5d3SJohn Marino 	return i;
19486d7f5d3SJohn Marino }
19586d7f5d3SJohn Marino 
19686d7f5d3SJohn Marino int
process_file(const char * file,parser_t parser,void * arg,int * nlines)19786d7f5d3SJohn Marino process_file(const char *file, parser_t parser, void *arg, int *nlines)
19886d7f5d3SJohn Marino {
19986d7f5d3SJohn Marino 	FILE *fd;
20086d7f5d3SJohn Marino 
20186d7f5d3SJohn Marino 	line_no = 0;
20286d7f5d3SJohn Marino 
20386d7f5d3SJohn Marino 	fd = fopen(file, "r");
20486d7f5d3SJohn Marino 	if (fd == NULL)
20586d7f5d3SJohn Marino 		err(1, "fopen");
20686d7f5d3SJohn Marino 		/* NOTREACHED */
20786d7f5d3SJohn Marino 
20886d7f5d3SJohn Marino 	while (process_line(fd, parser, arg) == 0)
20986d7f5d3SJohn Marino 		++line_no;
21086d7f5d3SJohn Marino 
21186d7f5d3SJohn Marino 	fclose(fd);
21286d7f5d3SJohn Marino 
21386d7f5d3SJohn Marino 	if (nlines != NULL)
21486d7f5d3SJohn Marino 		*nlines = line_no;
21586d7f5d3SJohn Marino 
21686d7f5d3SJohn Marino 	return 0;
21786d7f5d3SJohn Marino }
21886d7f5d3SJohn Marino 
219