xref: /openbsd/bin/chio/parse.y (revision a0722e21)
1 /*	$OpenBSD: parse.y,v 1.5 2007/09/11 22:16:15 deraadt Exp $ */
2 
3 /*
4  * Copyright (c) 2006 Bob Beck <beck@openbsd.org>
5  * Copyright (c) 2002-2006 Henning Brauer <henning@openbsd.org>
6  * Copyright (c) 2001 Markus Friedl.  All rights reserved.
7  * Copyright (c) 2001 Daniel Hartmeier.  All rights reserved.
8  * Copyright (c) 2001 Theo de Raadt.  All rights reserved.
9  *
10  * Permission to use, copy, modify, and distribute this software for any
11  * purpose with or without fee is hereby granted, provided that the above
12  * copyright notice and this permission notice appear in all copies.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
15  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
16  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
17  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
18  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
19  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
20  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21  */
22 
23 %{
24 #include <sys/types.h>
25 #include <sys/socket.h>
26 #include <sys/queue.h>
27 
28 #include <ctype.h>
29 #include <err.h>
30 #include <libgen.h>
31 #include <limits.h>
32 #include <stdarg.h>
33 #include <stdio.h>
34 #include <string.h>
35 
36 struct changer {
37 	TAILQ_ENTRY(changer)	  entry;
38 	char			 *name;
39 	char			**drives;
40 	u_int			  drivecnt;
41 };
42 TAILQ_HEAD(changers, changer)	 changers;
43 struct changer			*curchanger;
44 
45 static FILE			*fin = NULL;
46 static int			 lineno = 1;
47 static int			 errors = 0;
48 const char			*infile;
49 
50 int	 yyerror(const char *, ...);
51 int	 yyparse(void);
52 int	 kw_cmp(const void *, const void *);
53 int	 lookup(char *);
54 int	 lgetc(FILE *);
55 int	 lungetc(int);
56 int	 findeol(void);
57 int	 yylex(void);
58 
59 typedef struct {
60 	union {
61 		int64_t			 number;
62 		char			*string;
63 	} v;
64 	int lineno;
65 } YYSTYPE;
66 
67 %}
68 
69 %token	CHANGER
70 %token	DRIVE
71 %token	ERROR
72 %token	<v.string>		STRING
73 %token	<v.number>		NUMBER
74 %%
75 
76 grammar		: /* empty */
77 		| grammar '\n'
78 		| grammar changer '\n'
79 		| grammar error '\n'		{ errors++; }
80 		;
81 
82 optnl		: '\n' optnl
83 		|
84 		;
85 
86 nl		: '\n' optnl
87 		;
88 
89 changer		: CHANGER STRING optnl '{' optnl {
90 			curchanger = new_changer($2);
91 		}
92 		    changeropts_l '}' {
93 			TAILQ_INSERT_TAIL(&changers, curchanger, entry);
94 			curchanger = NULL;
95 		}
96 		;
97 
98 changeropts_l	: changeropts_l changeroptsl
99 		| changeroptsl
100 		;
101 
102 changeroptsl	: changeropts nl
103 		| error nl
104 		;
105 
106 changeropts	: DRIVE STRING	{
107 			void *newp;
108 
109 			if ((newp = realloc(curchanger->drives,
110 			    (curchanger->drivecnt + 1) *
111 			    sizeof(curchanger->drives))) == NULL)
112 				err(1, NULL);
113 			curchanger->drives = newp;
114 			if ((curchanger->drives[curchanger->drivecnt] =
115 			    strdup($2)) == NULL)
116 				err(1, NULL);
117 			curchanger->drivecnt++;
118 			free($2);
119 		}
120 		;
121 
122 %%
123 
124 struct keywords {
125 	const char	*k_name;
126 	int		 k_val;
127 };
128 
129 int
130 yyerror(const char *fmt, ...)
131 {
132 	va_list		 ap;
133 	char		*nfmt;
134 
135 	errors = 1;
136 	va_start(ap, fmt);
137 	if (asprintf(&nfmt, "%s:%d: %s", infile, yylval.lineno, fmt) == -1)
138 		err(1, "yyerror asprintf");
139 	err(1, nfmt, ap);
140 	va_end(ap);
141 	free(nfmt);
142 	return (0);
143 }
144 
145 int
146 kw_cmp(const void *k, const void *e)
147 {
148 	return (strcmp(k, ((const struct keywords *)e)->k_name));
149 }
150 
151 int
152 lookup(char *s)
153 {
154 	/* this has to be sorted always */
155 	static const struct keywords keywords[] = {
156 		{ "changer",		CHANGER},
157 		{ "drive",		DRIVE}
158 	};
159 	const struct keywords	*p;
160 
161 	p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
162 	    sizeof(keywords[0]), kw_cmp);
163 
164 	if (p)
165 		return (p->k_val);
166 	else
167 		return (STRING);
168 }
169 
170 #define MAXPUSHBACK	128
171 
172 char	*parsebuf;
173 int	 parseindex;
174 char	 pushback_buffer[MAXPUSHBACK];
175 int	 pushback_index = 0;
176 
177 int
178 lgetc(FILE *f)
179 {
180 	int	c, next;
181 
182 	if (parsebuf) {
183 		/* Read character from the parsebuffer instead of input. */
184 		if (parseindex >= 0) {
185 			c = parsebuf[parseindex++];
186 			if (c != '\0')
187 				return (c);
188 			parsebuf = NULL;
189 		} else
190 			parseindex++;
191 	}
192 
193 	if (pushback_index)
194 		return (pushback_buffer[--pushback_index]);
195 
196 	while ((c = getc(f)) == '\\') {
197 		next = getc(f);
198 		if (next != '\n') {
199 			c = next;
200 			break;
201 		}
202 		yylval.lineno = lineno;
203 		lineno++;
204 	}
205 	if (c == '\t' || c == ' ') {
206 		/* Compress blanks to a single space. */
207 		do {
208 			c = getc(f);
209 		} while (c == '\t' || c == ' ');
210 		ungetc(c, f);
211 		c = ' ';
212 	}
213 
214 	return (c);
215 }
216 
217 int
218 lungetc(int c)
219 {
220 	if (c == EOF)
221 		return (EOF);
222 	if (parsebuf) {
223 		parseindex--;
224 		if (parseindex >= 0)
225 			return (c);
226 	}
227 	if (pushback_index < MAXPUSHBACK-1)
228 		return (pushback_buffer[pushback_index++] = c);
229 	else
230 		return (EOF);
231 }
232 
233 int
234 findeol(void)
235 {
236 	int	c;
237 
238 	parsebuf = NULL;
239 	pushback_index = 0;
240 
241 	/* skip to either EOF or the first real EOL */
242 	while (1) {
243 		c = lgetc(fin);
244 		if (c == '\n') {
245 			lineno++;
246 			break;
247 		}
248 		if (c == EOF)
249 			break;
250 	}
251 	return (ERROR);
252 }
253 
254 int
255 yylex(void)
256 {
257 	char	 buf[8096];
258 	char	*p;
259 	int	 endc, c;
260 	int	 token;
261 
262 	p = buf;
263 	while ((c = lgetc(fin)) == ' ')
264 		; /* nothing */
265 
266 	yylval.lineno = lineno;
267 	if (c == '#')
268 		while ((c = lgetc(fin)) != '\n' && c != EOF)
269 			; /* nothing */
270 
271 	switch (c) {
272 	case '\'':
273 	case '"':
274 		endc = c;
275 		while (1) {
276 			if ((c = lgetc(fin)) == EOF)
277 				return (0);
278 			if (c == endc) {
279 				*p = '\0';
280 				break;
281 			}
282 			if (c == '\n') {
283 				lineno++;
284 				continue;
285 			}
286 			if (p + 1 >= buf + sizeof(buf) - 1) {
287 				yyerror("string too long");
288 				return (findeol());
289 			}
290 			*p++ = (char)c;
291 		}
292 		yylval.v.string = strdup(buf);
293 		if (yylval.v.string == NULL)
294 			err(1, "yylex: strdup");
295 		return (STRING);
296 	}
297 
298 #define allowed_to_end_number(x) \
299 	(isspace(x) || c == ')' || c ==',' || c == '/' || c == '}')
300 
301 	if (c == '-' || isdigit(c)) {
302 		do {
303 			*p++ = c;
304 			if ((unsigned)(p-buf) >= sizeof(buf)) {
305 				yyerror("string too long");
306 				return (findeol());
307 			}
308 		} while ((c = lgetc(fin)) != EOF && isdigit(c));
309 		lungetc(c);
310 		if (p == buf + 1 && buf[0] == '-')
311 			goto nodigits;
312 		if (c == EOF || allowed_to_end_number(c)) {
313 			const char *errstr = NULL;
314 
315 			*p = '\0';
316 			yylval.v.number = strtonum(buf, LLONG_MIN,
317 			    LLONG_MAX, &errstr);
318 			if (errstr) {
319 				yyerror("\"%s\" invalid number: %s",
320 				    buf, errstr);
321 				return (findeol());
322 			}
323 			return (NUMBER);
324 		} else {
325 nodigits:
326 			while (p > buf + 1)
327 				lungetc(*--p);
328 			c = *--p;
329 			if (c == '-')
330 				return (c);
331 		}
332 	}
333 
334 #define allowed_in_string(x) \
335 	(isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
336 	x != '{' && x != '}' && x != '<' && x != '>' && \
337 	x != '!' && x != '=' && x != '/' && x != '#' && \
338 	x != ','))
339 
340 	if (isalnum(c) || c == ':' || c == '_' || c == '*') {
341 		do {
342 			*p++ = c;
343 			if ((unsigned)(p-buf) >= sizeof(buf)) {
344 				yyerror("string too long");
345 				return (findeol());
346 			}
347 		} while ((c = lgetc(fin)) != EOF && (allowed_in_string(c)));
348 		lungetc(c);
349 		*p = '\0';
350 		if ((token = lookup(buf)) == STRING)
351 			if ((yylval.v.string = strdup(buf)) == NULL)
352 				err(1, "yylex: strdup");
353 		return (token);
354 	}
355 	if (c == '\n') {
356 		yylval.lineno = lineno;
357 		lineno++;
358 	}
359 	if (c == EOF)
360 		return (0);
361 	return (c);
362 }
363 
364 char *
365 parse_tapedev(const char *filename, const char *changer, int drive)
366 {
367 	struct changer	*p;
368 	char *tapedev = NULL;
369 
370 	lineno = 1;
371 	errors = 0;
372 	TAILQ_INIT(&changers);
373 
374 	if ((fin = fopen(filename, "r")) == NULL)
375 		goto guess;
376 
377 	infile = filename;
378 
379 	yyparse();
380 
381 	fclose(fin);
382 
383 	TAILQ_FOREACH(p, &changers, entry) {
384 		if (strcmp(basename(changer), p->name) == 0) {
385 			if (drive >= 0 && drive < p->drivecnt) {
386 				if (asprintf(&tapedev, "/dev/%s",
387 				     p->drives[drive]) == -1)
388 					errx(1, "malloc failed");
389 			} else
390 				tapedev = NULL;
391 		}
392 	}
393 
394 guess:
395 	/* if no device found, do the default of /dev/rstX */
396 	if (tapedev == NULL)
397 		if (asprintf(&tapedev, "/dev/rst%d", drive) == -1)
398 			errx(1, "malloc failed");
399 	return (tapedev);
400 }
401 
402 struct changer *
403 new_changer(char *name)
404 {
405 	struct changer	*p;
406 
407 	if ((p = calloc(1, sizeof(*p))) == NULL)
408 		err(1, NULL);
409 
410 	if ((p->name = strdup(name)) == NULL)
411 		err(1, NULL);
412 
413 	return (p);
414 }
415 
416 
417