xref: /netbsd-src/usr.bin/config/scan.l (revision 87d689fb734c654d2486f87f7be32f1b53ecdbec)
1 %{
2 /*	$NetBSD: scan.l,v 1.26 2016/08/07 10:37:24 christos Exp $	*/
3 
4 /*
5  * Copyright (c) 1992, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * This software was developed by the Computer Systems Engineering group
9  * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
10  * contributed to Berkeley.
11  *
12  * All advertising materials mentioning features or use of this software
13  * must display the following acknowledgement:
14  *	This product includes software developed by the University of
15  *	California, Lawrence Berkeley Laboratories.
16  *
17  * Redistribution and use in source and binary forms, with or without
18  * modification, are permitted provided that the following conditions
19  * are met:
20  * 1. Redistributions of source code must retain the above copyright
21  *    notice, this list of conditions and the following disclaimer.
22  * 2. Redistributions in binary form must reproduce the above copyright
23  *    notice, this list of conditions and the following disclaimer in the
24  *    documentation and/or other materials provided with the distribution.
25  * 3. Neither the name of the University nor the names of its contributors
26  *    may be used to endorse or promote products derived from this software
27  *    without specific prior written permission.
28  *
29  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
30  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
33  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
35  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
37  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
38  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39  * SUCH DAMAGE.
40  *
41  *	from: @(#)scan.l	8.1 (Berkeley) 6/6/93
42  */
43 
44 #include <sys/cdefs.h>
45 __RCSID("$NetBSD: scan.l,v 1.26 2016/08/07 10:37:24 christos Exp $");
46 
47 #include <sys/param.h>
48 #include <errno.h>
49 #include <libgen.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <string.h>
53 #include <unistd.h>
54 #include <stddef.h>
55 #include <ctype.h>
56 #include <util.h>
57 #undef ECHO
58 #include "defs.h"
59 #include "gram.h"
60 
61 int	yyline;
62 const char *yyfile;
63 const char *lastfile;
64 char curinclpath[PATH_MAX];
65 int ifdefstate = -1;
66 int st;
67 #define IDS_PARENT_DISABLED \
68     ((ifdefstate > 6) && ((((ifdefstate/6)-1) & 1) == 1))
69 #define IDS_MAX_DEPTH		362797056 /* 6^11 */
70 /* States for ifdefstate:
71 
72   0  -> matched ifdef
73   1  -> unmatched ifdef
74   2  -> matched elifdef
75   3  -> unmatched elifdef
76   4  -> matched else
77   5  -> unmatched else
78 
79   Upon "ifdef", add one and multiply by 6.
80   Upon "endif", divide by 6, remove 1.
81 
82   ifdef -> MATCH => continue
83            MISMATCH => set to 1
84   elifdef -> if (!1) -> MISMATCH
85              MATCH => set to 2
86              MISMATCH => if (2 || 3) set to 3, else set to 1
87   else -> if (1) -> MATCH
88           MATCH => set to 4
89           MISMATCH => set to 5
90 
91   in each case, if parent & 1 == 1, MISMATCH
92 */
93 
94 /*
95  * Data for returning to previous files from include files.
96  */
97 struct incl {
98 	struct	incl *in_prev;	/* previous includes in effect, if any */
99 	YY_BUFFER_STATE in_buf;	/* previous lex state */
100 	const char *in_fname;	/* previous file name */
101 	int	in_lineno;	/* previous line number */
102 	int	in_ateof;	/* token to insert at EOF */
103 	int	in_interesting;	/* previous value for "interesting" */
104 	int	in_ifdefstate;	/* conditional level */
105 };
106 static struct incl *incl;
107 static int endinclude(void);
108 static int getincludepath(void);
109 static int getcurifdef(void);
110 
111 
112 %}
113 
114 %option  noyywrap nounput noinput
115 
116 PATH	[A-Za-z_0-9]*[./][-A-Za-z_0-9./]*
117 QCHARS	\"(\\.|[^\\"])*\"
118 WORD	[A-Za-z_][-A-Za-z_0-9]*
119 FILENAME	({PATH}|{QCHARS})
120 RESTOFLINE	[ \t]*(#[^\n]*)?\n
121 
122 %x	IGNORED
123 
124 %%
125 		/* Local variables for yylex() */
126 		int tok;
127 
128 and		return AND;
129 at		return AT;
130 attach		return ATTACH;
131 block		return BLOCK;
132 build		return BUILD;
133 char		return CHAR;
134 compile-with	return COMPILE_WITH;
135 config		return CONFIG;
136 deffs		return DEFFS;
137 define		return DEFINE;
138 defflag		return DEFFLAG;
139 defopt		return DEFOPT;
140 defparam	return DEFPARAM;
141 defpseudo	return DEFPSEUDO;
142 defpseudodev	return DEFPSEUDODEV;
143 devclass	return DEVCLASS;
144 device		return DEVICE;
145 device-major	return DEVICE_MAJOR;
146 dumps		return DUMPS;
147 file		return XFILE;
148 file-system	return FILE_SYSTEM;
149 flags		return FLAGS;
150 ident		return IDENT;
151 ioconf		return IOCONF;
152 linkzero	return LINKZERO;
153 machine		return XMACHINE;
154 major		return MAJOR;
155 makeoptions	return MAKEOPTIONS;
156 maxpartitions	return MAXPARTITIONS;
157 maxusers	return MAXUSERS;
158 minor		return MINOR;
159 needs-count	return NEEDS_COUNT;
160 needs-flag	return NEEDS_FLAG;
161 no		return NO;
162 -no		return CNO;
163 object		return XOBJECT;
164 obsolete	return OBSOLETE;
165 on		return ON;
166 options		return OPTIONS;
167 prefix		return PREFIX;
168 buildprefix	return BUILDPREFIX;
169 pseudo-device	return PSEUDO_DEVICE;
170 pseudo-root	return PSEUDO_ROOT;
171 root		return ROOT;
172 select		return SELECT;
173 single		return SINGLE;
174 source		return SOURCE;
175 type		return TYPE;
176 vector 		return VECTOR;
177 version 	return VERSION;
178 with		return WITH;
179 
180 \+=		return PLUSEQ;
181 :=		return COLONEQ;
182 
183 <*>ifdef[ \t]+{WORD}{RESTOFLINE} {
184 		ifdefstate = (ifdefstate + 1) * 6;
185 		if (ifdefstate >= IDS_MAX_DEPTH) {
186 			yyerror("too many levels of conditional");
187 		}
188 		if (!IDS_PARENT_DISABLED && getcurifdef()) {
189 			BEGIN(INITIAL);
190 		} else {
191 			ifdefstate++;
192 			BEGIN(IGNORED);
193 		}
194 		yyline++;
195 	}
196 
197 <*>ifndef[ \t]+{WORD}{RESTOFLINE} {
198 		ifdefstate = (ifdefstate + 1) * 6;
199 		if (ifdefstate >= IDS_MAX_DEPTH) {
200 			yyerror("too many levels of conditional");
201 		}
202 		if (!IDS_PARENT_DISABLED && !getcurifdef()) {
203 			BEGIN(INITIAL);
204 		} else {
205 			ifdefstate++;
206 			BEGIN(IGNORED);
207 		}
208 		yyline++;
209 	}
210 
211 
212 <*>elifdef[ \t]+{WORD}{RESTOFLINE} {
213 		st = ifdefstate % 6;
214 		if (ifdefstate < 0 || st > 3) {
215 			yyerror("mismatched elifdef");
216 		}
217 		if (IDS_PARENT_DISABLED ||
218 		    st != 1 || !getcurifdef()) {
219 			if (st == 2 || st == 3) {
220 				ifdefstate += 3 - st;
221 			} else {
222 				ifdefstate += 1 - st;
223 			}
224 			BEGIN(IGNORED);
225 		} else {
226 			ifdefstate++;
227 			BEGIN(INITIAL);
228 		}
229 		yyline++;
230 	}
231 
232 <*>elifndef[ \t]+{WORD}{RESTOFLINE} {
233 		st = ifdefstate % 6;
234 		if (ifdefstate < 0 || st > 3) {
235 			yyerror("mismatched elifndef");
236 		}
237 		if (IDS_PARENT_DISABLED ||
238 		    st != 1 || getcurifdef()) {
239 			if (st == 2 || st == 3) {
240 				ifdefstate += 3 - st;
241 			} else {
242 				ifdefstate += 1 - st;
243 			}
244 			BEGIN(IGNORED);
245 		} else {
246 			ifdefstate++;
247 			BEGIN(INITIAL);
248 		}
249 		yyline++;
250 	}
251 
252 <*>else{RESTOFLINE} {
253 		st = ifdefstate % 6;
254 		if (ifdefstate < 0 || st > 3) {
255 			yyerror("mismatched else");
256 		}
257 		if (!IDS_PARENT_DISABLED && (st == 1)) {
258 			ifdefstate += 3;
259 			BEGIN(INITIAL);
260 		} else {
261 			ifdefstate += 5 - st;
262 			BEGIN(IGNORED);
263 		}
264 		yyline++;
265 	}
266 
267 <*>endif{RESTOFLINE} {
268 		if (ifdefstate < 0) {
269 			yyerror("mismatched endif");
270 		}
271 		if (!IDS_PARENT_DISABLED) {
272 			BEGIN(INITIAL);
273 		}
274 		ifdefstate = (ifdefstate/6) - 1;
275 		yyline++;
276 	}
277 
278 <IGNORED>\n		{
279 		yyline++;
280 	}
281 
282 <IGNORED>.	/* ignore */
283 
284 include[ \t]+{FILENAME}{RESTOFLINE}	{
285 		yyline++;
286 		if (getincludepath()) {
287 			include(curinclpath, 0, 0, 1);
288 		} else {
289 			yyerror("bad include path-name");
290 		}
291 	}
292 
293 cinclude[ \t]+{FILENAME}{RESTOFLINE}	{
294 		yyline++;
295 		if (getincludepath()) {
296 			include(curinclpath, 0, 1, 1);
297 		} else {
298 			yyerror("bad cinclude path-name");
299 		}
300 	}
301 
302 package[ \t]+{FILENAME}{RESTOFLINE}	{
303 		yyline++;
304 		if (!oktopackage) {
305 			yyerror("package not allowed here");
306 		} else if (getincludepath()) {
307 			package(curinclpath);
308 		} else {
309 			yyerror("bad package path-name");
310 		}
311 	}
312 
313 {PATH}	{
314 		yylval.str = intern(yytext);
315 		return PATHNAME;
316 	}
317 
318 {WORD}	{
319 		yylval.str = intern(yytext);
320 		return WORD;
321 	}
322 
323 \"\" {
324 		yylval.str = intern("");
325 		return EMPTYSTRING;
326 	}
327 
328 {QCHARS}	{
329 		size_t l = strlen(yytext);
330 		if (l > 1 && yytext[l - 1] == '"')
331 			yytext[l - 1] = '\0';
332 
333 		yylval.str = intern(yytext + 1);
334 		return QSTRING;
335 	}
336 0[0-7]*	{
337 		yylval.num.fmt = 8;
338 		yylval.num.val = strtoll(yytext, NULL, 8);
339 		return NUMBER;
340 	}
341 0[xX][0-9a-fA-F]+ {
342 		yylval.num.fmt = 16;
343 		yylval.num.val = (long long)strtoull(yytext + 2, NULL, 16);
344 		return NUMBER;
345 	}
346 [1-9][0-9]* {
347 		yylval.num.fmt = 10;
348 		yylval.num.val = strtoll(yytext, NULL, 10);
349 		return NUMBER;
350 	}
351 \n[ \t] {
352 		/*
353 		 * Note: newline followed by whitespace is always a
354 		 * continuation of the previous line, so do NOT
355 		 * return a token in this case.
356 		 */
357 		yyline++;
358 	}
359 \n	{
360 		yyline++;
361 		return '\n';
362 	}
363 \00	{
364 		/* Detect NUL characters in the config file and
365 		 * error out.
366 		 */
367 		cfgerror("NUL character detected at line %i", yyline);
368 	}
369 #.*	{ /* ignored (comment) */; }
370 [ \t]+	{ /* ignored (white space) */; }
371 .	{ return yytext[0]; }
372 <*><<EOF>> {
373 		if (ifdefstate > (incl == NULL ? -1 : incl->in_ifdefstate)) {
374 			yyerror("reached EOF while looking for endif");
375 		}
376 		if (incl == NULL)
377 			return YY_NULL;
378 		tok = endinclude();
379 		if (tok)
380 			return tok;
381 		/* otherwise continue scanning */
382 	}
383 
384 %%
385 
386 int interesting = 1;
387 
388 static int
389 curdir_push(const char *fname)
390 {
391 	struct prefix *pf;
392 	char *p, *d, *f;
393 
394 	/* Set up the initial "current directory" for include directives. */
395 	d = dirname(f = estrdup(fname));
396 	if (*d == '/')
397 		p = estrdup(d);
398 	else {
399 		char *cwd, buf[PATH_MAX];
400 
401 		if ((cwd = getcwd(buf, sizeof(buf))) == NULL) {
402 			free(f);
403 			return (-1);
404 		}
405 		easprintf(&p, "%s/%s", cwd, d);
406 	}
407 	free(f);
408 	pf = ecalloc(1, sizeof(*pf));
409 	pf->pf_prefix = p;
410 	SLIST_INSERT_HEAD(&curdirs, pf, pf_next);
411 
412 	return (0);
413 }
414 
415 static void
416 curdir_pop(void)
417 {
418 	struct prefix *pf;
419 
420 	pf = SLIST_FIRST(&curdirs);
421 	SLIST_REMOVE_HEAD(&curdirs, pf_next);
422 	if (SLIST_EMPTY(&curdirs))
423 		panic("curdirs is empty");
424 	/* LINTED cast away const (pf_prefix is malloc'd for curdirs) */
425 	free((void *)__UNCONST(pf->pf_prefix));
426 	free(pf);
427 }
428 
429 /*
430  * Open the "main" file (conffile).
431  */
432 int
433 firstfile(const char *fname)
434 {
435 
436 #if defined(__NetBSD__)
437 	if ((yyin = fopen(fname, "rf")) == NULL)
438 #else
439 	if ((yyin = fopen(fname, "r")) == NULL)
440 #endif
441 		return (-1);
442 
443 	if (curdir_push(fname) == -1)
444 		return (-1);
445 
446 	yyfile = conffile = fname;
447 	yyline = 1;
448 	return (0);
449 }
450 
451 /*
452  * Add a "package" to the configuration.  This is essentially
453  * syntactic sugar around the sequence:
454  *
455  *	prefix ../some/directory
456  *	include "files.package"
457  *	prefix
458  */
459 void
460 package(const char *fname)
461 {
462 	char *fname1 = estrdup(fname);
463 	char *fname2 = estrdup(fname);
464 	char *dir = dirname(fname1);
465 	char *file = basename(fname2);
466 
467 	/*
468 	 * Push the prefix on to the prefix stack and process the include
469 	 * file.  When we reach the end of the include file, inserting
470 	 * the PREFIX token into the input stream will pop the prefix off
471 	 * of the prefix stack.
472 	 */
473 	prefix_push(dir);
474 	(void) include(file, PREFIX, 0, 1);
475 
476 	free(fname1);
477 	free(fname2);
478 }
479 
480 int includedepth;
481 
482 /*
483  * Open the named file for inclusion at the current point.  Returns 0 on
484  * success (file opened and previous state pushed), nonzero on failure
485  * (fopen failed, complaint made).  The `ateof' parameter controls the
486  * token to be inserted at the end of the include file (i.e. ENDFILE).
487  * If ateof == 0 then nothing is inserted.
488  */
489 int
490 include(const char *fname, int ateof, int conditional, int direct)
491 {
492 	FILE *fp;
493 	struct incl *in;
494 	char *s;
495 	static int havedirs;
496 	extern int vflag;
497 
498 	if (havedirs == 0) {
499 		havedirs = 1;
500 		setupdirs();
501 	}
502 
503 	if (fname[0] == '/')
504 		s = estrdup(fname);
505 	else if (fname[0] == '.' && fname[1] == '/') {
506 		struct prefix *pf = SLIST_FIRST(&curdirs);
507 		easprintf(&s, "%s/%s", pf->pf_prefix, fname + 2);
508 	} else
509 		s = sourcepath(fname);
510 	if ((fp = fopen(s, "r")) == NULL) {
511 		if (conditional == 0)
512 			cfgerror("cannot open %s for reading: %s", s,
513 			    strerror(errno));
514 		else if (vflag)
515 			cfgwarn("cannot open conditional include file %s: %s",
516 			     s, strerror(errno));
517 		free(s);
518 		return (-1);
519 	}
520 	if (curdir_push(s) == -1) {
521 		cfgerror("cannot record current working directory for %s", s);
522 		fclose(fp);
523 		free(s);
524 		return (-1);
525 	}
526 	in = ecalloc(1, sizeof *in);
527 	in->in_prev = incl;
528 	in->in_buf = YY_CURRENT_BUFFER;
529 	in->in_fname = yyfile;
530 	in->in_lineno = yyline;
531 	in->in_ateof = ateof;
532 	in->in_interesting = interesting;
533 	in->in_ifdefstate = ifdefstate;
534 	interesting = direct & interesting;
535 	if (interesting)
536 		logconfig_include(fp, fname);
537 	incl = in;
538 	CFGDBG(1, "include `%s' from `%s' line %d", fname, yyfile, yyline);
539 	yy_switch_to_buffer(yy_create_buffer(fp, YY_BUF_SIZE));
540 	yyfile = intern(s);
541 	yyline = 1;
542 	free(s);
543 	includedepth++;
544 	return (0);
545 }
546 
547 /*
548  * Extract the pathname from a include/cinclude/package into curinclpath
549  */
550 static int
551 getincludepath(void)
552 {
553 	const char *p = yytext;
554 	ptrdiff_t len;
555 	const char *e;
556 
557 	while (*p && isascii((unsigned int)*p) && !isspace((unsigned int)*p))
558 		p++;
559 	while (*p && isascii((unsigned int)*p) && isspace((unsigned int)*p))
560 		p++;
561 	if (!*p)
562 		return 0;
563 	if (*p == '"') {
564 		p++;
565 		e = strchr(p, '"');
566 		if (!e) return 0;
567 	} else {
568 		e = p;
569 		while (*e && isascii((unsigned int)*e)
570 		    && !isspace((unsigned int)*e))
571 			e++;
572 	}
573 
574 	len = e-p;
575 	if (len > (ptrdiff_t)sizeof(curinclpath)-1)
576 		len = sizeof(curinclpath)-1;
577 	strncpy(curinclpath, p, sizeof(curinclpath));
578 	curinclpath[len] = '\0';
579 
580 	return 1;
581 }
582 
583 /*
584  * Terminate the most recent inclusion.
585  */
586 static int
587 endinclude(void)
588 {
589 	struct incl *in;
590 	int ateof;
591 
592 	curdir_pop();
593 	if ((in = incl) == NULL)
594 		panic("endinclude");
595 	incl = in->in_prev;
596 	lastfile = yyfile;
597 	yy_delete_buffer(YY_CURRENT_BUFFER);
598 	(void)fclose(yyin);
599 	yy_switch_to_buffer(in->in_buf);
600 	yyfile = in->in_fname;
601 	yyline = in->in_lineno;
602 	ateof  = in->in_ateof;
603 	interesting = in->in_interesting;
604 	free(in);
605 
606 	includedepth--;
607 
608 	return (ateof);
609 }
610 
611 /*
612  * Return the current line number.  If yacc has looked ahead and caused
613  * us to consume a newline, we have to subtract one.  yychar is yacc's
614  * token lookahead, so we can tell.
615  */
616 u_short
617 currentline(void)
618 {
619 	extern int yychar;
620 
621 	return (u_short)(yyline - (yychar == '\n'));
622 }
623 
624 static int
625 getcurifdef(void)
626 {
627 	char *p = yytext, *q;
628 
629 	while (*p && isascii((unsigned int)*p) && !isspace((unsigned int)*p))
630 		p++;
631 	while (*p && isascii((unsigned int)*p) && isspace((unsigned int)*p))
632 		p++;
633 	q = p;
634 	while (*q && isascii((unsigned int)*q) && !isspace((unsigned int)*q))
635 		q++;
636 	*q = '\0';
637 
638 	return ht_lookup(attrtab, intern(p)) != NULL;
639 }
640