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