scan.l revision 1.20 1 %{
2 /* $NetBSD: scan.l,v 1.20 2014/10/29 19:10:18 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.20 2014/10/29 19:10:18 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
115
116 PATH [A-Za-z_0-9]*[./][-A-Za-z_0-9./]*
117 QCHARS ([^"\n]|\\\")+
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 object return XOBJECT;
163 obsolete return OBSOLETE;
164 on return ON;
165 options return OPTIONS;
166 prefix return PREFIX;
167 pseudo-device return PSEUDO_DEVICE;
168 pseudo-root return PSEUDO_ROOT;
169 root return ROOT;
170 single return SINGLE;
171 source return SOURCE;
172 type return TYPE;
173 vector return VECTOR;
174 version return VERSION;
175 with return WITH;
176
177 \+= return PLUSEQ;
178 := return COLONEQ;
179
180 <*>ifdef[ \t]+{WORD}{RESTOFLINE} {
181 ifdefstate = (ifdefstate + 1) * 6;
182 if (ifdefstate >= IDS_MAX_DEPTH) {
183 yyerror("too many levels of conditional");
184 }
185 if (!IDS_PARENT_DISABLED && getcurifdef()) {
186 BEGIN(INITIAL);
187 } else {
188 ifdefstate++;
189 BEGIN(IGNORED);
190 }
191 yyline++;
192 }
193
194 <*>ifndef[ \t]+{WORD}{RESTOFLINE} {
195 ifdefstate = (ifdefstate + 1) * 6;
196 if (ifdefstate >= IDS_MAX_DEPTH) {
197 yyerror("too many levels of conditional");
198 }
199 if (!IDS_PARENT_DISABLED && !getcurifdef()) {
200 BEGIN(INITIAL);
201 } else {
202 ifdefstate++;
203 BEGIN(IGNORED);
204 }
205 yyline++;
206 }
207
208
209 <*>elifdef[ \t]+{WORD}{RESTOFLINE} {
210 st = ifdefstate % 6;
211 if (ifdefstate < 0 || st > 3) {
212 yyerror("mismatched elifdef");
213 }
214 if (IDS_PARENT_DISABLED ||
215 st != 1 || !getcurifdef()) {
216 if (st == 2 || st == 3) {
217 ifdefstate += 3 - st;
218 } else {
219 ifdefstate += 1 - st;
220 }
221 BEGIN(IGNORED);
222 } else {
223 ifdefstate++;
224 BEGIN(INITIAL);
225 }
226 yyline++;
227 }
228
229 <*>elifndef[ \t]+{WORD}{RESTOFLINE} {
230 st = ifdefstate % 6;
231 if (ifdefstate < 0 || st > 3) {
232 yyerror("mismatched elifndef");
233 }
234 if (IDS_PARENT_DISABLED ||
235 st != 1 || getcurifdef()) {
236 if (st == 2 || st == 3) {
237 ifdefstate += 3 - st;
238 } else {
239 ifdefstate += 1 - st;
240 }
241 BEGIN(IGNORED);
242 } else {
243 ifdefstate++;
244 BEGIN(INITIAL);
245 }
246 yyline++;
247 }
248
249 <*>else{RESTOFLINE} {
250 st = ifdefstate % 6;
251 if (ifdefstate < 0 || st > 3) {
252 yyerror("mismatched else");
253 }
254 if (!IDS_PARENT_DISABLED && (st == 1)) {
255 ifdefstate += 3;
256 BEGIN(INITIAL);
257 } else {
258 ifdefstate += 5 - st;
259 BEGIN(IGNORED);
260 }
261 yyline++;
262 }
263
264 <*>endif{RESTOFLINE} {
265 if (ifdefstate < 0) {
266 yyerror("mismatched endif");
267 }
268 if (!IDS_PARENT_DISABLED) {
269 BEGIN(INITIAL);
270 }
271 ifdefstate = (ifdefstate/6) - 1;
272 yyline++;
273 }
274
275 <IGNORED>\n {
276 yyline++;
277 }
278
279 <IGNORED>. /* ignore */
280
281 include[ \t]+{FILENAME}{RESTOFLINE} {
282 yyline++;
283 if (getincludepath()) {
284 include(curinclpath, 0, 0, 1);
285 } else {
286 yyerror("bad include path-name");
287 }
288 }
289
290 cinclude[ \t]+{FILENAME}{RESTOFLINE} {
291 yyline++;
292 if (getincludepath()) {
293 include(curinclpath, 0, 1, 1);
294 } else {
295 yyerror("bad cinclude path-name");
296 }
297 }
298
299 package[ \t]+{FILENAME}{RESTOFLINE} {
300 yyline++;
301 if (!oktopackage) {
302 yyerror("package not allowed here");
303 } else if (getincludepath()) {
304 package(curinclpath);
305 } else {
306 yyerror("bad package path-name");
307 }
308 }
309
310 {PATH} {
311 yylval.str = intern(yytext);
312 return PATHNAME;
313 }
314
315 {WORD} {
316 yylval.str = intern(yytext);
317 return WORD;
318 }
319
320 \"\" {
321 yylval.str = intern("");
322 return EMPTYSTRING;
323 }
324
325 \"{QCHARS} {
326 tok = input(); /* eat closing quote */
327 if (tok != '"') {
328 cfgerror("closing quote missing\n");
329 unput(tok);
330 }
331 yylval.str = intern(yytext + 1);
332 return QSTRING;
333 }
334 0[0-7]* {
335 yylval.num.fmt = 8;
336 yylval.num.val = strtoll(yytext, NULL, 8);
337 return NUMBER;
338 }
339 0[xX][0-9a-fA-F]+ {
340 yylval.num.fmt = 16;
341 yylval.num.val = (long long)strtoull(yytext + 2, NULL, 16);
342 return NUMBER;
343 }
344 [1-9][0-9]* {
345 yylval.num.fmt = 10;
346 yylval.num.val = strtoll(yytext, NULL, 10);
347 return NUMBER;
348 }
349 \n[ \t] {
350 /*
351 * Note: newline followed by whitespace is always a
352 * continuation of the previous line, so do NOT
353 * return a token in this case.
354 */
355 yyline++;
356 }
357 \n {
358 yyline++;
359 return '\n';
360 }
361 \00 {
362 /* Detect NUL characters in the config file and
363 * error out.
364 */
365 cfgerror("NUL character detected at line %i\n", yyline);
366 }
367 #.* { /* ignored (comment) */; }
368 [ \t]+ { /* ignored (white space) */; }
369 . { return yytext[0]; }
370 <*><<EOF>> {
371 if (ifdefstate > (incl == NULL ? -1 : incl->in_ifdefstate)) {
372 yyerror("reached EOF while looking for endif");
373 }
374 if (incl == NULL)
375 return YY_NULL;
376 tok = endinclude();
377 if (tok)
378 return tok;
379 /* otherwise continue scanning */
380 }
381
382 %%
383
384 int interesting = 1;
385
386 static int
387 curdir_push(const char *fname)
388 {
389 struct prefix *pf;
390 char *p, *d, *f;
391
392 /* Set up the initial "current directory" for include directives. */
393 d = dirname(f = estrdup(fname));
394 if (*d == '/')
395 p = estrdup(d);
396 else {
397 char *cwd, buf[PATH_MAX];
398
399 if ((cwd = getcwd(buf, sizeof(buf))) == NULL) {
400 free(f);
401 return (-1);
402 }
403 p = emalloc(strlen(cwd) + strlen(d) + 2);
404 sprintf(p, "%s/%s", cwd, d);
405 }
406 free(f);
407 pf = ecalloc(1, sizeof(*pf));
408 pf->pf_prefix = p;
409 SLIST_INSERT_HEAD(&curdirs, pf, pf_next);
410
411 return (0);
412 }
413
414 static void
415 curdir_pop(void)
416 {
417 struct prefix *pf;
418
419 pf = SLIST_FIRST(&curdirs);
420 SLIST_REMOVE_HEAD(&curdirs, pf_next);
421 if (SLIST_EMPTY(&curdirs))
422 panic("curdirs is empty");
423 /* LINTED cast away const (pf_prefix is malloc'd for curdirs) */
424 free((void *)__UNCONST(pf->pf_prefix));
425 free(pf);
426 }
427
428 /*
429 * Open the "main" file (conffile).
430 */
431 int
432 firstfile(const char *fname)
433 {
434
435 #if defined(__NetBSD__)
436 if ((yyin = fopen(fname, "rf")) == NULL)
437 #else
438 if ((yyin = fopen(fname, "r")) == NULL)
439 #endif
440 return (-1);
441
442 if (curdir_push(fname) == -1)
443 return (-1);
444
445 yyfile = conffile = fname;
446 yyline = 1;
447 return (0);
448 }
449
450 /*
451 * Add a "package" to the configuration. This is essentially
452 * syntactic sugar around the sequence:
453 *
454 * prefix ../some/directory
455 * include "files.package"
456 * prefix
457 */
458 void
459 package(const char *fname)
460 {
461 char *fname1 = estrdup(fname);
462 char *fname2 = estrdup(fname);
463 char *dir = dirname(fname1);
464 char *file = basename(fname2);
465
466 /*
467 * Push the prefix on to the prefix stack and process the include
468 * file. When we reach the end of the include file, inserting
469 * the PREFIX token into the input stream will pop the prefix off
470 * of the prefix stack.
471 */
472 prefix_push(dir);
473 (void) include(file, PREFIX, 0, 1);
474
475 free(fname1);
476 free(fname2);
477 }
478
479 /*
480 * Open the named file for inclusion at the current point. Returns 0 on
481 * success (file opened and previous state pushed), nonzero on failure
482 * (fopen failed, complaint made). The `ateof' parameter controls the
483 * token to be inserted at the end of the include file (i.e. ENDFILE).
484 * If ateof == 0 then nothing is inserted.
485 */
486 int
487 include(const char *fname, int ateof, int conditional, int direct)
488 {
489 FILE *fp;
490 struct incl *in;
491 char *s;
492 static int havedirs;
493 extern int vflag;
494
495 if (havedirs == 0) {
496 havedirs = 1;
497 setupdirs();
498 }
499
500 if (fname[0] == '/')
501 s = estrdup(fname);
502 else if (fname[0] == '.' && fname[1] == '/') {
503 struct prefix *pf = SLIST_FIRST(&curdirs);
504 s = emalloc(strlen(pf->pf_prefix) + strlen(fname));
505 sprintf(s, "%s/%s", pf->pf_prefix, fname + 2);
506 } else
507 s = sourcepath(fname);
508 if ((fp = fopen(s, "r")) == NULL) {
509 if (conditional == 0)
510 cfgerror("cannot open %s for reading: %s\n", s,
511 strerror(errno));
512 else if (vflag)
513 cfgwarn("cannot open conditional include file %s: %s",
514 s, strerror(errno));
515 free(s);
516 return (-1);
517 }
518 if (curdir_push(s) == -1) {
519 cfgerror("cannot record current working directory for %s\n", s);
520 fclose(fp);
521 free(s);
522 return (-1);
523 }
524 in = ecalloc(1, sizeof *in);
525 in->in_prev = incl;
526 in->in_buf = YY_CURRENT_BUFFER;
527 in->in_fname = yyfile;
528 in->in_lineno = yyline;
529 in->in_ateof = ateof;
530 in->in_interesting = interesting;
531 in->in_ifdefstate = ifdefstate;
532 interesting = direct & interesting;
533 if (interesting)
534 logconfig_include(fp, fname);
535 incl = in;
536 CFGDBG(1, "include `%s' from `%s' line %d", fname, yyfile, yyline);
537 yy_switch_to_buffer(yy_create_buffer(fp, YY_BUF_SIZE));
538 yyfile = intern(s);
539 yyline = 1;
540 free(s);
541 return (0);
542 }
543
544 /*
545 * Extract the pathname from a include/cinclude/package into curinclpath
546 */
547 static int
548 getincludepath(void)
549 {
550 const char *p = yytext;
551 ptrdiff_t len;
552 const char *e;
553
554 while (*p && isascii((unsigned int)*p) && !isspace((unsigned int)*p))
555 p++;
556 while (*p && isascii((unsigned int)*p) && isspace((unsigned int)*p))
557 p++;
558 if (!*p)
559 return 0;
560 if (*p == '"') {
561 p++;
562 e = strchr(p, '"');
563 if (!e) return 0;
564 } else {
565 e = p;
566 while (*e && isascii((unsigned int)*e)
567 && !isspace((unsigned int)*e))
568 e++;
569 }
570
571 len = e-p;
572 if (len > (ptrdiff_t)sizeof(curinclpath)-1)
573 len = sizeof(curinclpath)-1;
574 strncpy(curinclpath, p, sizeof(curinclpath));
575 curinclpath[len] = '\0';
576
577 return 1;
578 }
579
580 /*
581 * Terminate the most recent inclusion.
582 */
583 static int
584 endinclude(void)
585 {
586 struct incl *in;
587 int ateof;
588
589 curdir_pop();
590 if ((in = incl) == NULL)
591 panic("endinclude");
592 incl = in->in_prev;
593 lastfile = yyfile;
594 yy_delete_buffer(YY_CURRENT_BUFFER);
595 (void)fclose(yyin);
596 yy_switch_to_buffer(in->in_buf);
597 yyfile = in->in_fname;
598 yyline = in->in_lineno;
599 ateof = in->in_ateof;
600 interesting = in->in_interesting;
601 free(in);
602
603 return (ateof);
604 }
605
606 /*
607 * Return the current line number. If yacc has looked ahead and caused
608 * us to consume a newline, we have to subtract one. yychar is yacc's
609 * token lookahead, so we can tell.
610 */
611 u_short
612 currentline(void)
613 {
614 extern int yychar;
615
616 return (u_short)(yyline - (yychar == '\n'));
617 }
618
619 static int
620 getcurifdef(void)
621 {
622 char *p = yytext, *q;
623
624 while (*p && isascii((unsigned int)*p) && !isspace((unsigned int)*p))
625 p++;
626 while (*p && isascii((unsigned int)*p) && isspace((unsigned int)*p))
627 p++;
628 q = p;
629 while (*q && isascii((unsigned int)*q) && !isspace((unsigned int)*q))
630 q++;
631 *q = '\0';
632
633 return ht_lookup(attrtab, intern(p)) != NULL;
634 }
635