xref: /openbsd/bin/chio/parse.y (revision d5d66eae)
1 /*	$OpenBSD: parse.y,v 1.7 2007/10/11 14:39: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(int);
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(int inquot)
179 {
180 	int	c, next;
181 	FILE *f = fin;
182 
183 	if (parsebuf) {
184 		/* Read character from the parsebuffer instead of input. */
185 		if (parseindex >= 0) {
186 			c = parsebuf[parseindex++];
187 			if (c != '\0')
188 				return (c);
189 			parsebuf = NULL;
190 		} else
191 			parseindex++;
192 	}
193 
194 	if (pushback_index)
195 		return (pushback_buffer[--pushback_index]);
196 
197 	if (inquot) {
198 		c = getc(f);
199 		return (c);
200 	}
201 
202 	while ((c = getc(f)) == '\\') {
203 		next = getc(f);
204 		if (next != '\n') {
205 			c = next;
206 			break;
207 		}
208 		yylval.lineno = lineno;
209 		lineno++;
210 	}
211 	if (c == '\t' || c == ' ') {
212 		/* Compress blanks to a single space. */
213 		do {
214 			c = getc(f);
215 		} while (c == '\t' || c == ' ');
216 		ungetc(c, f);
217 		c = ' ';
218 	}
219 
220 	return (c);
221 }
222 
223 int
224 lungetc(int c)
225 {
226 	if (c == EOF)
227 		return (EOF);
228 	if (parsebuf) {
229 		parseindex--;
230 		if (parseindex >= 0)
231 			return (c);
232 	}
233 	if (pushback_index < MAXPUSHBACK-1)
234 		return (pushback_buffer[pushback_index++] = c);
235 	else
236 		return (EOF);
237 }
238 
239 int
240 findeol(void)
241 {
242 	int	c;
243 
244 	parsebuf = NULL;
245 	pushback_index = 0;
246 
247 	/* skip to either EOF or the first real EOL */
248 	while (1) {
249 		c = lgetc(0);
250 		if (c == '\n') {
251 			lineno++;
252 			break;
253 		}
254 		if (c == EOF)
255 			break;
256 	}
257 	return (ERROR);
258 }
259 
260 int
261 yylex(void)
262 {
263 	char	 buf[8096];
264 	char	*p;
265 	int	 endc, next, c;
266 	int	 token;
267 
268 	p = buf;
269 	while ((c = lgetc(0)) == ' ')
270 		; /* nothing */
271 
272 	yylval.lineno = lineno;
273 	if (c == '#')
274 		while ((c = lgetc(0)) != '\n' && c != EOF)
275 			; /* nothing */
276 
277 	switch (c) {
278 	case '\'':
279 	case '"':
280 		endc = c;
281 		while (1) {
282 			if ((c = lgetc(1)) == EOF)
283 				return (0);
284 			if (c == '\n') {
285 				lineno++;
286 				continue;
287 			} else if (c == '\\') {
288 				if ((next = lgetc(1)) == EOF)
289 					return (0);
290 				if (next == endc)
291 					c = next;
292 				else
293 					lungetc(next);
294 			} else if (c == endc) {
295 				*p = '\0';
296 				break;
297 			}
298 			if (p + 1 >= buf + sizeof(buf) - 1) {
299 				yyerror("string too long");
300 				return (findeol());
301 			}
302 			*p++ = (char)c;
303 		}
304 		yylval.v.string = strdup(buf);
305 		if (yylval.v.string == NULL)
306 			err(1, "yylex: strdup");
307 		return (STRING);
308 	}
309 
310 #define allowed_to_end_number(x) \
311 	(isspace(x) || x == ')' || x ==',' || x == '/' || x == '}')
312 
313 	if (c == '-' || isdigit(c)) {
314 		do {
315 			*p++ = c;
316 			if ((unsigned)(p-buf) >= sizeof(buf)) {
317 				yyerror("string too long");
318 				return (findeol());
319 			}
320 		} while ((c = lgetc(0)) != EOF && isdigit(c));
321 		lungetc(c);
322 		if (p == buf + 1 && buf[0] == '-')
323 			goto nodigits;
324 		if (c == EOF || allowed_to_end_number(c)) {
325 			const char *errstr = NULL;
326 
327 			*p = '\0';
328 			yylval.v.number = strtonum(buf, LLONG_MIN,
329 			    LLONG_MAX, &errstr);
330 			if (errstr) {
331 				yyerror("\"%s\" invalid number: %s",
332 				    buf, errstr);
333 				return (findeol());
334 			}
335 			return (NUMBER);
336 		} else {
337 nodigits:
338 			while (p > buf + 1)
339 				lungetc(*--p);
340 			c = *--p;
341 			if (c == '-')
342 				return (c);
343 		}
344 	}
345 
346 #define allowed_in_string(x) \
347 	(isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
348 	x != '{' && x != '}' && x != '<' && x != '>' && \
349 	x != '!' && x != '=' && x != '/' && x != '#' && \
350 	x != ','))
351 
352 	if (isalnum(c) || c == ':' || c == '_' || c == '*') {
353 		do {
354 			*p++ = c;
355 			if ((unsigned)(p-buf) >= sizeof(buf)) {
356 				yyerror("string too long");
357 				return (findeol());
358 			}
359 		} while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
360 		lungetc(c);
361 		*p = '\0';
362 		if ((token = lookup(buf)) == STRING)
363 			if ((yylval.v.string = strdup(buf)) == NULL)
364 				err(1, "yylex: strdup");
365 		return (token);
366 	}
367 	if (c == '\n') {
368 		yylval.lineno = lineno;
369 		lineno++;
370 	}
371 	if (c == EOF)
372 		return (0);
373 	return (c);
374 }
375 
376 char *
377 parse_tapedev(const char *filename, const char *changer, int drive)
378 {
379 	struct changer	*p;
380 	char *tapedev = NULL;
381 
382 	lineno = 1;
383 	errors = 0;
384 	TAILQ_INIT(&changers);
385 
386 	if ((fin = fopen(filename, "r")) == NULL)
387 		goto guess;
388 
389 	infile = filename;
390 
391 	yyparse();
392 
393 	fclose(fin);
394 
395 	TAILQ_FOREACH(p, &changers, entry) {
396 		if (strcmp(basename(changer), p->name) == 0) {
397 			if (drive >= 0 && drive < p->drivecnt) {
398 				if (asprintf(&tapedev, "/dev/%s",
399 				     p->drives[drive]) == -1)
400 					errx(1, "malloc failed");
401 			} else
402 				tapedev = NULL;
403 		}
404 	}
405 
406 guess:
407 	/* if no device found, do the default of /dev/rstX */
408 	if (tapedev == NULL)
409 		if (asprintf(&tapedev, "/dev/rst%d", drive) == -1)
410 			errx(1, "malloc failed");
411 	return (tapedev);
412 }
413 
414 struct changer *
415 new_changer(char *name)
416 {
417 	struct changer	*p;
418 
419 	if ((p = calloc(1, sizeof(*p))) == NULL)
420 		err(1, NULL);
421 
422 	if ((p->name = strdup(name)) == NULL)
423 		err(1, NULL);
424 
425 	return (p);
426 }
427 
428 
429