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