xref: /dragonfly/usr.bin/dfregress/parser.c (revision c37c9ab3)
1 /*
2  * Copyright (c) 2009-2011 Alex Hornung <alex@alexhornung.com>.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in
13  *    the documentation and/or other materials provided with the
14  *    distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
19  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
20  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
22  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
26  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 
30 #include <stdio.h>
31 #include <stdarg.h>
32 #include <string.h>
33 #include <stdlib.h>
34 #include <unistd.h>
35 #include <errno.h>
36 #include <err.h>
37 #include "parser.h"
38 
39 #define _iswhitespace(X)	((((X) == ' ') || ((X) == '\t'))?1:0)
40 
41 
42 static int line_no = 1;
43 
44 static int iswhitespace(char c)
45 {
46 	return _iswhitespace(c);
47 }
48 
49 static int iscomma(char c)
50 {
51 	return (c == ',');
52 }
53 
54 void
55 syntax_error(const char *fmt, ...)
56 {
57 	char buf[1024];
58 	va_list ap;
59 
60 	va_start(ap, fmt);
61 	vsnprintf(buf, sizeof(buf), fmt, ap);
62 	va_end(ap);
63 	errx(1, "syntax error on line %d: %s\n", line_no, buf);
64 }
65 
66 
67 int
68 entry_check_num_args(char **tokens, int num)
69 {
70 	int i;
71 
72 	for (i = 0; tokens[i] != NULL; i++)
73 		;
74 
75 	if (i < num) {
76 		syntax_error("at least %d tokens were expected but only %d "
77 		    "were found", num, i);
78 		return 1;
79 	}
80 	return 0;
81 }
82 
83 static int
84 line_tokenize(char *buffer, int (*is_sep)(char), char comment_char, char **tokens)
85 {
86 	int c, n, i;
87 	int quote = 0;
88 
89 	i = strlen(buffer) + 1;
90 	c = 0;
91 
92 	/* Skip leading white-space */
93 	while ((_iswhitespace(buffer[c])) && (c < i)) c++;
94 
95 	/*
96 	 * If this line effectively (after indentation) begins with the comment
97 	 * character, we ignore the rest of the line.
98 	 */
99 	if (buffer[c] == comment_char)
100 		return 0;
101 
102 	tokens[0] = &buffer[c];
103 	for (n = 1; c < i; c++) {
104 		if (buffer[c] == '"') {
105 			quote = !quote;
106 			if (quote) {
107 				if ((c >= 1) && (&buffer[c] != tokens[n-1])) {
108 #if 0
109 					syntax_error("stray opening quote not "
110 					    "at beginning of token");
111 					/* NOTREACHED */
112 #endif
113 				} else {
114 					tokens[n-1] = &buffer[c+1];
115 				}
116 			} else {
117 				if ((c < i-1) && (!is_sep(buffer[c+1]))) {
118 #if 0
119 					syntax_error("stray closing quote not "
120 					    "at end of token");
121 					/* NOTREACHED */
122 #endif
123 				} else {
124 					buffer[c] = '\0';
125 				}
126 			}
127 		}
128 
129 		if (quote) {
130 			continue;
131 		}
132 
133 		if (is_sep(buffer[c])) {
134 			buffer[c++] = '\0';
135 			while ((_iswhitespace(buffer[c])) && (c < i)) c++;
136 			tokens[n++] = &buffer[c--];
137 		}
138 	}
139 	tokens[n] = NULL;
140 
141 	if (quote) {
142 		tokens[0] = NULL;
143 		return 0;
144 	}
145 
146 	return n;
147 }
148 
149 static int
150 process_line(FILE* fd, parser_t parser, void *arg)
151 {
152 	char buffer[4096];
153 	char *tokens[256];
154 	int c, n, i = 0;
155 	int ret = 0;
156 
157 	while (((c = fgetc(fd)) != EOF) && (c != '\n')) {
158 		buffer[i++] = (char)c;
159 		if (i == (sizeof(buffer) -1))
160 			break;
161 	}
162 	buffer[i] = '\0';
163 
164 	if (feof(fd) || ferror(fd))
165 		ret = 1;
166 
167 
168 	n = line_tokenize(buffer, &iswhitespace, '#', tokens);
169 
170 	/*
171 	 * If there are not enough arguments for any function or it is
172 	 * a line full of whitespaces, we just return here. Or if a
173 	 * quote wasn't closed.
174 	 */
175 	if ((n < 1) || (tokens[0][0] == '\0'))
176 		return ret;
177 
178 	parser(arg, tokens);
179 
180 	return ret;
181 }
182 
183 int
184 parse_options(char *str, char **options)
185 {
186 	int i;
187 
188 	i = line_tokenize(str, &iscomma, '#', options);
189 	if (i == 0)
190 		syntax_error("Invalid expression in options token");
191 		/* NOTREACHED */
192 
193 	return i;
194 }
195 
196 int
197 process_file(const char *file, parser_t parser, void *arg, int *nlines)
198 {
199 	FILE *fd;
200 
201 	line_no = 0;
202 
203 	fd = fopen(file, "r");
204 	if (fd == NULL)
205 		err(1, "fopen");
206 		/* NOTREACHED */
207 
208 	while (process_line(fd, parser, arg) == 0)
209 		++line_no;
210 
211 	fclose(fd);
212 
213 	if (nlines != NULL)
214 		*nlines = line_no;
215 
216 	return 0;
217 }
218 
219