1*08f6ba19Snaddy /* $OpenBSD: parse.y,v 1.24 2021/10/15 15:01:27 naddy Exp $ */
2b2b21931Sbeck
3b2b21931Sbeck /*
4b2b21931Sbeck * Copyright (c) 2006 Bob Beck <beck@openbsd.org>
5b2b21931Sbeck * Copyright (c) 2002-2006 Henning Brauer <henning@openbsd.org>
6b2b21931Sbeck * Copyright (c) 2001 Markus Friedl. All rights reserved.
7b2b21931Sbeck * Copyright (c) 2001 Daniel Hartmeier. All rights reserved.
8b2b21931Sbeck * Copyright (c) 2001 Theo de Raadt. All rights reserved.
9b2b21931Sbeck *
10b2b21931Sbeck * Permission to use, copy, modify, and distribute this software for any
11b2b21931Sbeck * purpose with or without fee is hereby granted, provided that the above
12b2b21931Sbeck * copyright notice and this permission notice appear in all copies.
13b2b21931Sbeck *
14b2b21931Sbeck * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
15b2b21931Sbeck * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
16b2b21931Sbeck * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
17b2b21931Sbeck * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
18b2b21931Sbeck * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
19b2b21931Sbeck * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
20b2b21931Sbeck * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21b2b21931Sbeck */
22b2b21931Sbeck
23b2b21931Sbeck %{
24b2b21931Sbeck #include <sys/types.h>
25b2b21931Sbeck #include <sys/socket.h>
26b2b21931Sbeck #include <sys/queue.h>
27b2b21931Sbeck
28b2b21931Sbeck #include <ctype.h>
29ff5d7ab3Scnst #include <err.h>
30b2b21931Sbeck #include <libgen.h>
31b2b21931Sbeck #include <limits.h>
32ec7da445Snaddy #include <paths.h>
33b2b21931Sbeck #include <stdarg.h>
34b2b21931Sbeck #include <stdio.h>
35b2b21931Sbeck #include <string.h>
36b2b21931Sbeck
3720741916Sderaadt TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files);
3820741916Sderaadt static struct file {
3920741916Sderaadt TAILQ_ENTRY(file) entry;
4020741916Sderaadt FILE *stream;
4120741916Sderaadt char *name;
4220741916Sderaadt int lineno;
4320741916Sderaadt int errors;
44c6004ab9Smpf } *file, *topfile;
4520741916Sderaadt struct file *pushfile(const char *);
4620741916Sderaadt int popfile(void);
4720741916Sderaadt int yyparse(void);
4820741916Sderaadt int yylex(void);
490f79392cSdoug int yyerror(const char *, ...)
500f79392cSdoug __attribute__((__format__ (printf, 1, 2)))
510f79392cSdoug __attribute__((__nonnull__ (1)));
5220741916Sderaadt int kw_cmp(const void *, const void *);
5320741916Sderaadt int lookup(char *);
5420741916Sderaadt int lgetc(int);
5520741916Sderaadt int lungetc(int);
5620741916Sderaadt int findeol(void);
57a3427febSderaadt char *parse_tapedev(const char *, const char *, int);
58a3427febSderaadt struct changer *new_changer(char *);
5920741916Sderaadt
60b2b21931Sbeck struct changer {
61b2b21931Sbeck TAILQ_ENTRY(changer) entry;
62b2b21931Sbeck char *name;
63b2b21931Sbeck char **drives;
64b2b21931Sbeck u_int drivecnt;
65b2b21931Sbeck };
66b2b21931Sbeck TAILQ_HEAD(changers, changer) changers;
67b2b21931Sbeck struct changer *curchanger;
68b2b21931Sbeck
69b2b21931Sbeck typedef struct {
70b2b21931Sbeck union {
71a0722e21Sderaadt int64_t number;
72b2b21931Sbeck char *string;
73b2b21931Sbeck } v;
74b2b21931Sbeck int lineno;
75b2b21931Sbeck } YYSTYPE;
76b2b21931Sbeck
77b2b21931Sbeck %}
78b2b21931Sbeck
79b2b21931Sbeck %token CHANGER
80b2b21931Sbeck %token DRIVE
81b2b21931Sbeck %token ERROR
82b2b21931Sbeck %token <v.string> STRING
83a0722e21Sderaadt %token <v.number> NUMBER
84b2b21931Sbeck %%
85b2b21931Sbeck
86b2b21931Sbeck grammar : /* empty */
87b2b21931Sbeck | grammar '\n'
8820741916Sderaadt | grammar main '\n'
8920741916Sderaadt | grammar error '\n' { file->errors++; }
90b2b21931Sbeck ;
91b2b21931Sbeck
92b2b21931Sbeck optnl : '\n' optnl
93b2b21931Sbeck |
94b2b21931Sbeck ;
95b2b21931Sbeck
96b2b21931Sbeck nl : '\n' optnl
97b2b21931Sbeck ;
98b2b21931Sbeck
9920741916Sderaadt main : CHANGER STRING optnl '{' optnl {
100b2b21931Sbeck curchanger = new_changer($2);
101b2b21931Sbeck }
102b2b21931Sbeck changeropts_l '}' {
103b2b21931Sbeck TAILQ_INSERT_TAIL(&changers, curchanger, entry);
104b2b21931Sbeck curchanger = NULL;
105b2b21931Sbeck }
106b2b21931Sbeck ;
107b2b21931Sbeck
108b2b21931Sbeck changeropts_l : changeropts_l changeroptsl
109b2b21931Sbeck | changeroptsl
110b2b21931Sbeck ;
111b2b21931Sbeck
112b2b21931Sbeck changeroptsl : changeropts nl
113b2b21931Sbeck | error nl
114b2b21931Sbeck ;
115b2b21931Sbeck
116b2b21931Sbeck changeropts : DRIVE STRING {
117b2b21931Sbeck void *newp;
118b2b21931Sbeck
119dca6acb1Sespie if ((newp = reallocarray(curchanger->drives,
120dca6acb1Sespie curchanger->drivecnt + 1,
121b2b21931Sbeck sizeof(curchanger->drives))) == NULL)
122b2b21931Sbeck err(1, NULL);
123b2b21931Sbeck curchanger->drives = newp;
124704a8ad5Shenning if ((curchanger->drives[curchanger->drivecnt] =
125704a8ad5Shenning strdup($2)) == NULL)
126704a8ad5Shenning err(1, NULL);
127b2b21931Sbeck curchanger->drivecnt++;
128b2b21931Sbeck free($2);
129b2b21931Sbeck }
130b2b21931Sbeck ;
131b2b21931Sbeck
132b2b21931Sbeck %%
133b2b21931Sbeck
134b2b21931Sbeck struct keywords {
135b2b21931Sbeck const char *k_name;
136b2b21931Sbeck int k_val;
137b2b21931Sbeck };
138b2b21931Sbeck
139b2b21931Sbeck int
yyerror(const char * fmt,...)140b2b21931Sbeck yyerror(const char *fmt, ...)
141b2b21931Sbeck {
142b2b21931Sbeck va_list ap;
143e78bcbddSbluhm char *msg;
144b2b21931Sbeck
14520741916Sderaadt file->errors++;
146b2b21931Sbeck va_start(ap, fmt);
147e78bcbddSbluhm if (vasprintf(&msg, fmt, ap) == -1)
148e78bcbddSbluhm err(1, "yyerror vasprintf");
149b2b21931Sbeck va_end(ap);
150e78bcbddSbluhm err(1, "%s:%d: %s", file->name, yylval.lineno, msg);
151e78bcbddSbluhm free(msg);
152b2b21931Sbeck return (0);
153b2b21931Sbeck }
154b2b21931Sbeck
155b2b21931Sbeck int
kw_cmp(const void * k,const void * e)156b2b21931Sbeck kw_cmp(const void *k, const void *e)
157b2b21931Sbeck {
158b2b21931Sbeck return (strcmp(k, ((const struct keywords *)e)->k_name));
159b2b21931Sbeck }
160b2b21931Sbeck
161b2b21931Sbeck int
lookup(char * s)162b2b21931Sbeck lookup(char *s)
163b2b21931Sbeck {
164b2b21931Sbeck /* this has to be sorted always */
165b2b21931Sbeck static const struct keywords keywords[] = {
166b2b21931Sbeck { "changer", CHANGER},
167b2b21931Sbeck { "drive", DRIVE}
168b2b21931Sbeck };
169b2b21931Sbeck const struct keywords *p;
170b2b21931Sbeck
171b2b21931Sbeck p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
172b2b21931Sbeck sizeof(keywords[0]), kw_cmp);
173b2b21931Sbeck
174b2b21931Sbeck if (p)
175b2b21931Sbeck return (p->k_val);
176b2b21931Sbeck else
177b2b21931Sbeck return (STRING);
178b2b21931Sbeck }
179b2b21931Sbeck
180b2b21931Sbeck #define MAXPUSHBACK 128
181b2b21931Sbeck
182*08f6ba19Snaddy char *parsebuf;
183b2b21931Sbeck int parseindex;
184*08f6ba19Snaddy char pushback_buffer[MAXPUSHBACK];
185b2b21931Sbeck int pushback_index = 0;
186b2b21931Sbeck
187b2b21931Sbeck int
lgetc(int quotec)18820741916Sderaadt lgetc(int quotec)
189b2b21931Sbeck {
190b2b21931Sbeck int c, next;
191b2b21931Sbeck
192b2b21931Sbeck if (parsebuf) {
193b2b21931Sbeck /* Read character from the parsebuffer instead of input. */
194b2b21931Sbeck if (parseindex >= 0) {
195*08f6ba19Snaddy c = (unsigned char)parsebuf[parseindex++];
196b2b21931Sbeck if (c != '\0')
197b2b21931Sbeck return (c);
198b2b21931Sbeck parsebuf = NULL;
199b2b21931Sbeck } else
200b2b21931Sbeck parseindex++;
201b2b21931Sbeck }
202b2b21931Sbeck
203b2b21931Sbeck if (pushback_index)
204*08f6ba19Snaddy return ((unsigned char)pushback_buffer[--pushback_index]);
205b2b21931Sbeck
20620741916Sderaadt if (quotec) {
20720741916Sderaadt if ((c = getc(file->stream)) == EOF) {
208c6004ab9Smpf yyerror("reached end of file while parsing "
209c6004ab9Smpf "quoted string");
210c6004ab9Smpf if (file == topfile || popfile() == EOF)
21120741916Sderaadt return (EOF);
21220741916Sderaadt return (quotec);
21320741916Sderaadt }
214d5d66eaeSderaadt return (c);
215d5d66eaeSderaadt }
216d5d66eaeSderaadt
21720741916Sderaadt while ((c = getc(file->stream)) == '\\') {
21820741916Sderaadt next = getc(file->stream);
219b2b21931Sbeck if (next != '\n') {
220b2b21931Sbeck c = next;
221b2b21931Sbeck break;
222b2b21931Sbeck }
22320741916Sderaadt yylval.lineno = file->lineno;
22420741916Sderaadt file->lineno++;
225b2b21931Sbeck }
226b2b21931Sbeck
22720741916Sderaadt while (c == EOF) {
228c6004ab9Smpf if (file == topfile || popfile() == EOF)
22920741916Sderaadt return (EOF);
23020741916Sderaadt c = getc(file->stream);
23120741916Sderaadt }
232b2b21931Sbeck return (c);
233b2b21931Sbeck }
234b2b21931Sbeck
235b2b21931Sbeck int
lungetc(int c)236b2b21931Sbeck lungetc(int c)
237b2b21931Sbeck {
238b2b21931Sbeck if (c == EOF)
239b2b21931Sbeck return (EOF);
240b2b21931Sbeck if (parsebuf) {
241b2b21931Sbeck parseindex--;
242b2b21931Sbeck if (parseindex >= 0)
243b2b21931Sbeck return (c);
244b2b21931Sbeck }
245*08f6ba19Snaddy if (pushback_index + 1 >= MAXPUSHBACK)
246b2b21931Sbeck return (EOF);
247*08f6ba19Snaddy pushback_buffer[pushback_index++] = c;
248*08f6ba19Snaddy return (c);
249b2b21931Sbeck }
250b2b21931Sbeck
251b2b21931Sbeck int
findeol(void)252b2b21931Sbeck findeol(void)
253b2b21931Sbeck {
254b2b21931Sbeck int c;
255b2b21931Sbeck
256b2b21931Sbeck parsebuf = NULL;
257b2b21931Sbeck pushback_index = 0;
258b2b21931Sbeck
259b2b21931Sbeck /* skip to either EOF or the first real EOL */
260b2b21931Sbeck while (1) {
261d5d66eaeSderaadt c = lgetc(0);
262b2b21931Sbeck if (c == '\n') {
26320741916Sderaadt file->lineno++;
264b2b21931Sbeck break;
265b2b21931Sbeck }
266b2b21931Sbeck if (c == EOF)
267b2b21931Sbeck break;
268b2b21931Sbeck }
269b2b21931Sbeck return (ERROR);
270b2b21931Sbeck }
271b2b21931Sbeck
272b2b21931Sbeck int
yylex(void)273b2b21931Sbeck yylex(void)
274b2b21931Sbeck {
275*08f6ba19Snaddy char buf[8096];
276*08f6ba19Snaddy char *p;
27720741916Sderaadt int quotec, next, c;
278b2b21931Sbeck int token;
279b2b21931Sbeck
280b2b21931Sbeck p = buf;
2812053f12aSmpf while ((c = lgetc(0)) == ' ' || c == '\t')
282b2b21931Sbeck ; /* nothing */
283b2b21931Sbeck
28420741916Sderaadt yylval.lineno = file->lineno;
285b2b21931Sbeck if (c == '#')
286d5d66eaeSderaadt while ((c = lgetc(0)) != '\n' && c != EOF)
287b2b21931Sbeck ; /* nothing */
288b2b21931Sbeck
289b2b21931Sbeck switch (c) {
290b2b21931Sbeck case '\'':
291b2b21931Sbeck case '"':
29220741916Sderaadt quotec = c;
293b2b21931Sbeck while (1) {
29420741916Sderaadt if ((c = lgetc(quotec)) == EOF)
295b2b21931Sbeck return (0);
296b2b21931Sbeck if (c == '\n') {
29720741916Sderaadt file->lineno++;
298b2b21931Sbeck continue;
299d5d66eaeSderaadt } else if (c == '\\') {
30020741916Sderaadt if ((next = lgetc(quotec)) == EOF)
301d5d66eaeSderaadt return (0);
302ea014f46Sderaadt if (next == quotec || c == ' ' || c == '\t')
303d5d66eaeSderaadt c = next;
304ea014f46Sderaadt else if (next == '\n')
305ea014f46Sderaadt continue;
306d5d66eaeSderaadt else
307d5d66eaeSderaadt lungetc(next);
30820741916Sderaadt } else if (c == quotec) {
309d5d66eaeSderaadt *p = '\0';
310d5d66eaeSderaadt break;
31141eef22fSjsg } else if (c == '\0') {
31241eef22fSjsg yyerror("syntax error");
31341eef22fSjsg return (findeol());
314b2b21931Sbeck }
315b2b21931Sbeck if (p + 1 >= buf + sizeof(buf) - 1) {
316b2b21931Sbeck yyerror("string too long");
317b2b21931Sbeck return (findeol());
318b2b21931Sbeck }
319015d7b4dSbenno *p++ = c;
320b2b21931Sbeck }
321b2b21931Sbeck yylval.v.string = strdup(buf);
322b2b21931Sbeck if (yylval.v.string == NULL)
323a062aa9dSkrw err(1, "%s", __func__);
324b2b21931Sbeck return (STRING);
325b2b21931Sbeck }
326b2b21931Sbeck
327a0722e21Sderaadt #define allowed_to_end_number(x) \
3280cf2c9c3Smpf (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
329a0722e21Sderaadt
330a0722e21Sderaadt if (c == '-' || isdigit(c)) {
331a0722e21Sderaadt do {
332a0722e21Sderaadt *p++ = c;
333915c3f33Sderaadt if ((size_t)(p-buf) >= sizeof(buf)) {
334a0722e21Sderaadt yyerror("string too long");
335a0722e21Sderaadt return (findeol());
336a0722e21Sderaadt }
337d5d66eaeSderaadt } while ((c = lgetc(0)) != EOF && isdigit(c));
338a0722e21Sderaadt lungetc(c);
339a0722e21Sderaadt if (p == buf + 1 && buf[0] == '-')
340a0722e21Sderaadt goto nodigits;
341a0722e21Sderaadt if (c == EOF || allowed_to_end_number(c)) {
342a0722e21Sderaadt const char *errstr = NULL;
343a0722e21Sderaadt
344a0722e21Sderaadt *p = '\0';
345a0722e21Sderaadt yylval.v.number = strtonum(buf, LLONG_MIN,
346a0722e21Sderaadt LLONG_MAX, &errstr);
347a0722e21Sderaadt if (errstr) {
348a0722e21Sderaadt yyerror("\"%s\" invalid number: %s",
349a0722e21Sderaadt buf, errstr);
350a0722e21Sderaadt return (findeol());
351a0722e21Sderaadt }
352a0722e21Sderaadt return (NUMBER);
353a0722e21Sderaadt } else {
354a0722e21Sderaadt nodigits:
355a0722e21Sderaadt while (p > buf + 1)
356*08f6ba19Snaddy lungetc((unsigned char)*--p);
357*08f6ba19Snaddy c = (unsigned char)*--p;
358a0722e21Sderaadt if (c == '-')
359a0722e21Sderaadt return (c);
360a0722e21Sderaadt }
361a0722e21Sderaadt }
362a0722e21Sderaadt
363b2b21931Sbeck #define allowed_in_string(x) \
364b2b21931Sbeck (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
365b2b21931Sbeck x != '{' && x != '}' && x != '<' && x != '>' && \
366b2b21931Sbeck x != '!' && x != '=' && x != '/' && x != '#' && \
367b2b21931Sbeck x != ','))
368b2b21931Sbeck
369b2b21931Sbeck if (isalnum(c) || c == ':' || c == '_' || c == '*') {
370b2b21931Sbeck do {
371b2b21931Sbeck *p++ = c;
372915c3f33Sderaadt if ((size_t)(p-buf) >= sizeof(buf)) {
373b2b21931Sbeck yyerror("string too long");
374b2b21931Sbeck return (findeol());
375b2b21931Sbeck }
376d5d66eaeSderaadt } while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
377b2b21931Sbeck lungetc(c);
378b2b21931Sbeck *p = '\0';
379b2b21931Sbeck if ((token = lookup(buf)) == STRING)
380b2b21931Sbeck if ((yylval.v.string = strdup(buf)) == NULL)
381a062aa9dSkrw err(1, "%s", __func__);
382b2b21931Sbeck return (token);
383b2b21931Sbeck }
384b2b21931Sbeck if (c == '\n') {
38520741916Sderaadt yylval.lineno = file->lineno;
38620741916Sderaadt file->lineno++;
387b2b21931Sbeck }
388b2b21931Sbeck if (c == EOF)
389b2b21931Sbeck return (0);
390b2b21931Sbeck return (c);
391b2b21931Sbeck }
392b2b21931Sbeck
39320741916Sderaadt struct file *
pushfile(const char * name)39420741916Sderaadt pushfile(const char *name)
39520741916Sderaadt {
39620741916Sderaadt struct file *nfile;
39720741916Sderaadt
3987fc93de0Stobias if ((nfile = calloc(1, sizeof(struct file))) == NULL)
39920741916Sderaadt return (NULL);
4007fc93de0Stobias if ((nfile->name = strdup(name)) == NULL) {
4017fc93de0Stobias free(nfile);
4027fc93de0Stobias return (NULL);
4037fc93de0Stobias }
40420741916Sderaadt if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
40520741916Sderaadt free(nfile->name);
40620741916Sderaadt free(nfile);
40720741916Sderaadt return (NULL);
40820741916Sderaadt }
40920741916Sderaadt nfile->lineno = 1;
41020741916Sderaadt TAILQ_INSERT_TAIL(&files, nfile, entry);
41120741916Sderaadt return (nfile);
41220741916Sderaadt }
41320741916Sderaadt
41420741916Sderaadt int
popfile(void)41520741916Sderaadt popfile(void)
41620741916Sderaadt {
41720741916Sderaadt struct file *prev;
41820741916Sderaadt
419c6004ab9Smpf if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
42020741916Sderaadt prev->errors += file->errors;
421c6004ab9Smpf
42220741916Sderaadt TAILQ_REMOVE(&files, file, entry);
42320741916Sderaadt fclose(file->stream);
42420741916Sderaadt free(file->name);
42520741916Sderaadt free(file);
42620741916Sderaadt file = prev;
427c6004ab9Smpf return (file ? 0 : EOF);
42820741916Sderaadt }
42920741916Sderaadt
430b2b21931Sbeck char *
parse_tapedev(const char * filename,const char * changer,int drive)431b2b21931Sbeck parse_tapedev(const char *filename, const char *changer, int drive)
432b2b21931Sbeck {
433b2b21931Sbeck struct changer *p;
434b2b21931Sbeck char *tapedev = NULL;
43520741916Sderaadt int errors = 0;
436b2b21931Sbeck
437b2b21931Sbeck TAILQ_INIT(&changers);
438b2b21931Sbeck
43920741916Sderaadt if ((file = pushfile(filename)) == NULL) {
44020741916Sderaadt warnx("cannot open the main config file!");
441b2b21931Sbeck goto guess;
44220741916Sderaadt }
443cc57095fSmpf topfile = file;
444b2b21931Sbeck
445b2b21931Sbeck yyparse();
44620741916Sderaadt errors = file->errors;
44720741916Sderaadt popfile();
448b2b21931Sbeck
449ec7da445Snaddy if (strncmp(changer, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
450ec7da445Snaddy changer += sizeof(_PATH_DEV) - 1;
451b2b21931Sbeck TAILQ_FOREACH(p, &changers, entry) {
452ec7da445Snaddy if (strcmp(changer, p->name) == 0) {
453b2b21931Sbeck if (drive >= 0 && drive < p->drivecnt) {
454ec7da445Snaddy if (asprintf(&tapedev, _PATH_DEV "%s",
455b2b21931Sbeck p->drives[drive]) == -1)
456b2b21931Sbeck errx(1, "malloc failed");
4577633e3bcShenning } else
458b2b21931Sbeck tapedev = NULL;
459b2b21931Sbeck }
460b2b21931Sbeck }
461b2b21931Sbeck
462b2b21931Sbeck guess:
463b2b21931Sbeck /* if no device found, do the default of /dev/rstX */
464b2b21931Sbeck if (tapedev == NULL)
465b2b21931Sbeck if (asprintf(&tapedev, "/dev/rst%d", drive) == -1)
466b2b21931Sbeck errx(1, "malloc failed");
467b2b21931Sbeck return (tapedev);
468b2b21931Sbeck }
469b2b21931Sbeck
470b2b21931Sbeck struct changer *
new_changer(char * name)471b2b21931Sbeck new_changer(char *name)
472b2b21931Sbeck {
473b2b21931Sbeck struct changer *p;
474b2b21931Sbeck
475b2b21931Sbeck if ((p = calloc(1, sizeof(*p))) == NULL)
476b2b21931Sbeck err(1, NULL);
477b2b21931Sbeck
478b2b21931Sbeck if ((p->name = strdup(name)) == NULL)
479b2b21931Sbeck err(1, NULL);
480b2b21931Sbeck
481b2b21931Sbeck return (p);
482b2b21931Sbeck }
483b2b21931Sbeck
484b2b21931Sbeck
485