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