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