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