1 %{ 2 /* $NetBSD: scan.l,v 1.34 2021/09/10 21:52:17 rillig 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.34 2021/09/10 21:52:17 rillig 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 (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'", fname); 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