GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: bin/chio/parse.y Lines: 0 179 0.0 %
Date: 2017-11-13 Branches: 0 159 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: parse.y,v 1.20 2014/11/20 05:51:20 jsg 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
TAILQ_HEAD(files, file)		 files = TAILQ_HEAD_INITIALIZER(files);
37
static struct file {
38
	TAILQ_ENTRY(file)	 entry;
39
	FILE			*stream;
40
	char			*name;
41
	int			 lineno;
42
	int			 errors;
43
} *file, *topfile;
44
struct file	*pushfile(const char *);
45
int		 popfile(void);
46
int		 yyparse(void);
47
int		 yylex(void);
48
int		 yyerror(const char *, ...)
49
    __attribute__((__format__ (printf, 1, 2)))
50
    __attribute__((__nonnull__ (1)));
51
int		 kw_cmp(const void *, const void *);
52
int		 lookup(char *);
53
int		 lgetc(int);
54
int		 lungetc(int);
55
int		 findeol(void);
56
char		*parse_tapedev(const char *, const char *, int);
57
struct changer	*new_changer(char *);
58
59
struct changer {
60
	TAILQ_ENTRY(changer)	  entry;
61
	char			 *name;
62
	char			**drives;
63
	u_int			  drivecnt;
64
};
65
TAILQ_HEAD(changers, changer)	 changers;
66
struct changer			*curchanger;
67
68
typedef struct {
69
	union {
70
		int64_t			 number;
71
		char			*string;
72
	} v;
73
	int lineno;
74
} YYSTYPE;
75
76
%}
77
78
%token	CHANGER
79
%token	DRIVE
80
%token	ERROR
81
%token	<v.string>		STRING
82
%token	<v.number>		NUMBER
83
%%
84
85
grammar		: /* empty */
86
		| grammar '\n'
87
		| grammar main '\n'
88
		| grammar error '\n'		{ file->errors++; }
89
		;
90
91
optnl		: '\n' optnl
92
		|
93
		;
94
95
nl		: '\n' optnl
96
		;
97
98
main		: CHANGER STRING optnl '{' optnl {
99
			curchanger = new_changer($2);
100
		}
101
		    changeropts_l '}' {
102
			TAILQ_INSERT_TAIL(&changers, curchanger, entry);
103
			curchanger = NULL;
104
		}
105
		;
106
107
changeropts_l	: changeropts_l changeroptsl
108
		| changeroptsl
109
		;
110
111
changeroptsl	: changeropts nl
112
		| error nl
113
		;
114
115
changeropts	: DRIVE STRING	{
116
			void *newp;
117
118
			if ((newp = reallocarray(curchanger->drives,
119
			    curchanger->drivecnt + 1,
120
			    sizeof(curchanger->drives))) == NULL)
121
				err(1, NULL);
122
			curchanger->drives = newp;
123
			if ((curchanger->drives[curchanger->drivecnt] =
124
			    strdup($2)) == NULL)
125
				err(1, NULL);
126
			curchanger->drivecnt++;
127
			free($2);
128
		}
129
		;
130
131
%%
132
133
struct keywords {
134
	const char	*k_name;
135
	int		 k_val;
136
};
137
138
int
139
yyerror(const char *fmt, ...)
140
{
141
	va_list		 ap;
142
	char		*msg;
143
144
	file->errors++;
145
	va_start(ap, fmt);
146
	if (vasprintf(&msg, fmt, ap) == -1)
147
		err(1, "yyerror vasprintf");
148
	va_end(ap);
149
	err(1, "%s:%d: %s", file->name, yylval.lineno, msg);
150
	free(msg);
151
	return (0);
152
}
153
154
int
155
kw_cmp(const void *k, const void *e)
156
{
157
	return (strcmp(k, ((const struct keywords *)e)->k_name));
158
}
159
160
int
161
lookup(char *s)
162
{
163
	/* this has to be sorted always */
164
	static const struct keywords keywords[] = {
165
		{ "changer",		CHANGER},
166
		{ "drive",		DRIVE}
167
	};
168
	const struct keywords	*p;
169
170
	p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
171
	    sizeof(keywords[0]), kw_cmp);
172
173
	if (p)
174
		return (p->k_val);
175
	else
176
		return (STRING);
177
}
178
179
#define MAXPUSHBACK	128
180
181
u_char	*parsebuf;
182
int	 parseindex;
183
u_char	 pushback_buffer[MAXPUSHBACK];
184
int	 pushback_index = 0;
185
186
int
187
lgetc(int quotec)
188
{
189
	int		c, next;
190
191
	if (parsebuf) {
192
		/* Read character from the parsebuffer instead of input. */
193
		if (parseindex >= 0) {
194
			c = parsebuf[parseindex++];
195
			if (c != '\0')
196
				return (c);
197
			parsebuf = NULL;
198
		} else
199
			parseindex++;
200
	}
201
202
	if (pushback_index)
203
		return (pushback_buffer[--pushback_index]);
204
205
	if (quotec) {
206
		if ((c = getc(file->stream)) == EOF) {
207
			yyerror("reached end of file while parsing "
208
			    "quoted string");
209
			if (file == topfile || popfile() == EOF)
210
				return (EOF);
211
			return (quotec);
212
		}
213
		return (c);
214
	}
215
216
	while ((c = getc(file->stream)) == '\\') {
217
		next = getc(file->stream);
218
		if (next != '\n') {
219
			c = next;
220
			break;
221
		}
222
		yylval.lineno = file->lineno;
223
		file->lineno++;
224
	}
225
226
	while (c == EOF) {
227
		if (file == topfile || popfile() == EOF)
228
			return (EOF);
229
		c = getc(file->stream);
230
	}
231
	return (c);
232
}
233
234
int
235
lungetc(int c)
236
{
237
	if (c == EOF)
238
		return (EOF);
239
	if (parsebuf) {
240
		parseindex--;
241
		if (parseindex >= 0)
242
			return (c);
243
	}
244
	if (pushback_index < MAXPUSHBACK-1)
245
		return (pushback_buffer[pushback_index++] = c);
246
	else
247
		return (EOF);
248
}
249
250
int
251
findeol(void)
252
{
253
	int	c;
254
255
	parsebuf = NULL;
256
	pushback_index = 0;
257
258
	/* skip to either EOF or the first real EOL */
259
	while (1) {
260
		c = lgetc(0);
261
		if (c == '\n') {
262
			file->lineno++;
263
			break;
264
		}
265
		if (c == EOF)
266
			break;
267
	}
268
	return (ERROR);
269
}
270
271
int
272
yylex(void)
273
{
274
	u_char	 buf[8096];
275
	u_char	*p;
276
	int	 quotec, next, c;
277
	int	 token;
278
279
	p = buf;
280
	while ((c = lgetc(0)) == ' ' || c == '\t')
281
		; /* nothing */
282
283
	yylval.lineno = file->lineno;
284
	if (c == '#')
285
		while ((c = lgetc(0)) != '\n' && c != EOF)
286
			; /* nothing */
287
288
	switch (c) {
289
	case '\'':
290
	case '"':
291
		quotec = c;
292
		while (1) {
293
			if ((c = lgetc(quotec)) == EOF)
294
				return (0);
295
			if (c == '\n') {
296
				file->lineno++;
297
				continue;
298
			} else if (c == '\\') {
299
				if ((next = lgetc(quotec)) == EOF)
300
					return (0);
301
				if (next == quotec || c == ' ' || c == '\t')
302
					c = next;
303
				else if (next == '\n')
304
					continue;
305
				else
306
					lungetc(next);
307
			} else if (c == quotec) {
308
				*p = '\0';
309
				break;
310
			} else if (c == '\0') {
311
				yyerror("syntax error");
312
				return (findeol());
313
			}
314
			if (p + 1 >= buf + sizeof(buf) - 1) {
315
				yyerror("string too long");
316
				return (findeol());
317
			}
318
			*p++ = c;
319
		}
320
		yylval.v.string = strdup(buf);
321
		if (yylval.v.string == NULL)
322
			err(1, "yylex: strdup");
323
		return (STRING);
324
	}
325
326
#define allowed_to_end_number(x) \
327
	(isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
328
329
	if (c == '-' || isdigit(c)) {
330
		do {
331
			*p++ = c;
332
			if ((unsigned)(p-buf) >= sizeof(buf)) {
333
				yyerror("string too long");
334
				return (findeol());
335
			}
336
		} while ((c = lgetc(0)) != EOF && isdigit(c));
337
		lungetc(c);
338
		if (p == buf + 1 && buf[0] == '-')
339
			goto nodigits;
340
		if (c == EOF || allowed_to_end_number(c)) {
341
			const char *errstr = NULL;
342
343
			*p = '\0';
344
			yylval.v.number = strtonum(buf, LLONG_MIN,
345
			    LLONG_MAX, &errstr);
346
			if (errstr) {
347
				yyerror("\"%s\" invalid number: %s",
348
				    buf, errstr);
349
				return (findeol());
350
			}
351
			return (NUMBER);
352
		} else {
353
nodigits:
354
			while (p > buf + 1)
355
				lungetc(*--p);
356
			c = *--p;
357
			if (c == '-')
358
				return (c);
359
		}
360
	}
361
362
#define allowed_in_string(x) \
363
	(isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
364
	x != '{' && x != '}' && x != '<' && x != '>' && \
365
	x != '!' && x != '=' && x != '/' && x != '#' && \
366
	x != ','))
367
368
	if (isalnum(c) || c == ':' || c == '_' || c == '*') {
369
		do {
370
			*p++ = c;
371
			if ((unsigned)(p-buf) >= sizeof(buf)) {
372
				yyerror("string too long");
373
				return (findeol());
374
			}
375
		} while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
376
		lungetc(c);
377
		*p = '\0';
378
		if ((token = lookup(buf)) == STRING)
379
			if ((yylval.v.string = strdup(buf)) == NULL)
380
				err(1, "yylex: strdup");
381
		return (token);
382
	}
383
	if (c == '\n') {
384
		yylval.lineno = file->lineno;
385
		file->lineno++;
386
	}
387
	if (c == EOF)
388
		return (0);
389
	return (c);
390
}
391
392
struct file *
393
pushfile(const char *name)
394
{
395
	struct file	*nfile;
396
397
	if ((nfile = calloc(1, sizeof(struct file))) == NULL)
398
		return (NULL);
399
	if ((nfile->name = strdup(name)) == NULL) {
400
		free(nfile);
401
		return (NULL);
402
	}
403
	if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
404
		free(nfile->name);
405
		free(nfile);
406
		return (NULL);
407
	}
408
	nfile->lineno = 1;
409
	TAILQ_INSERT_TAIL(&files, nfile, entry);
410
	return (nfile);
411
}
412
413
int
414
popfile(void)
415
{
416
	struct file	*prev;
417
418
	if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
419
		prev->errors += file->errors;
420
421
	TAILQ_REMOVE(&files, file, entry);
422
	fclose(file->stream);
423
	free(file->name);
424
	free(file);
425
	file = prev;
426
	return (file ? 0 : EOF);
427
}
428
429
char *
430
parse_tapedev(const char *filename, const char *changer, int drive)
431
{
432
	struct changer	*p;
433
	char		*tapedev = NULL;
434
	int		 errors = 0;
435
436
	TAILQ_INIT(&changers);
437
438
	if ((file = pushfile(filename)) == NULL) {
439
		warnx("cannot open the main config file!");
440
		goto guess;
441
	}
442
	topfile = file;
443
444
	yyparse();
445
	errors = file->errors;
446
	popfile();
447
448
	TAILQ_FOREACH(p, &changers, entry) {
449
		if (strcmp(basename(changer), p->name) == 0) {
450
			if (drive >= 0 && drive < p->drivecnt) {
451
				if (asprintf(&tapedev, "/dev/%s",
452
				     p->drives[drive]) == -1)
453
					errx(1, "malloc failed");
454
			} else
455
				tapedev = NULL;
456
		}
457
	}
458
459
guess:
460
	/* if no device found, do the default of /dev/rstX */
461
	if (tapedev == NULL)
462
		if (asprintf(&tapedev, "/dev/rst%d", drive) == -1)
463
			errx(1, "malloc failed");
464
	return (tapedev);
465
}
466
467
struct changer *
468
new_changer(char *name)
469
{
470
	struct changer	*p;
471
472
	if ((p = calloc(1, sizeof(*p))) == NULL)
473
		err(1, NULL);
474
475
	if ((p->name = strdup(name)) == NULL)
476
		err(1, NULL);
477
478
	return (p);
479
}
480
481