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