directive.c revision 1.2
1/*- 2 * Copyright (c) 2010, 2013 The NetBSD Foundation, Inc. 3 * All rights reserved. 4 * 5 * This code is derived from software contributed to The NetBSD Foundation 6 * by David A. Holland. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 21 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 * POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30#include <assert.h> 31#include <stdlib.h> 32#include <string.h> 33#include <limits.h> 34#include <errno.h> 35 36#include "bool.h" 37#include "utils.h" 38#include "mode.h" 39#include "place.h" 40#include "files.h" 41#include "directive.h" 42#include "macro.h" 43#include "eval.h" 44#include "output.h" 45 46struct ifstate { 47 struct ifstate *prev; 48 struct place startplace; 49 bool curtrue; 50 bool evertrue; 51 bool seenelse; 52}; 53 54static struct ifstate *ifstate; 55 56//////////////////////////////////////////////////////////// 57// common parsing bits 58 59static 60void 61uncomment(char *buf) 62{ 63 char *s, *t, *u = NULL; 64 bool incomment = false; 65 bool inesc = false; 66 bool inquote = false; 67 char quote = '\0'; 68 69 for (s = t = buf; *s; s++) { 70 if (incomment) { 71 if (s[0] == '*' && s[1] == '/') { 72 s++; 73 incomment = false; 74 } 75 } else { 76 if (!inquote && s[0] == '/' && s[1] == '*') { 77 incomment = true; 78 } else { 79 if (inesc) { 80 inesc = false; 81 } else if (s[0] == '\\') { 82 inesc = true; 83 } else if (!inquote && 84 (s[0] == '"' || s[0] == '\'')) { 85 inquote = true; 86 quote = s[0]; 87 } else if (inquote && s[0] == quote) { 88 inquote = false; 89 } 90 91 if (t != s) { 92 *t = *s; 93 } 94 if (!strchr(ws, *t)) { 95 u = t; 96 } 97 t++; 98 } 99 } 100 } 101 if (u) { 102 /* end string after last non-whitespace char */ 103 u[1] = '\0'; 104 } else { 105 *t = '\0'; 106 } 107} 108 109static 110void 111oneword(const char *what, struct place *p2, char *line) 112{ 113 size_t pos; 114 115 pos = strcspn(line, ws); 116 if (line[pos] != '\0') { 117 p2->column += pos; 118 complain(p2, "Garbage after %s argument", what); 119 complain_fail(); 120 line[pos] = '\0'; 121 } 122} 123 124//////////////////////////////////////////////////////////// 125// if handling 126 127static 128struct ifstate * 129ifstate_create(struct ifstate *prev, struct place *p, bool startstate) 130{ 131 struct ifstate *is; 132 133 is = domalloc(sizeof(*is)); 134 is->prev = prev; 135 if (p != NULL) { 136 is->startplace = *p; 137 } else { 138 place_setbuiltin(&is->startplace, 1); 139 } 140 is->curtrue = startstate; 141 is->evertrue = is->curtrue; 142 is->seenelse = false; 143 return is; 144} 145 146static 147void 148ifstate_destroy(struct ifstate *is) 149{ 150 dofree(is, sizeof(*is)); 151} 152 153static 154void 155ifstate_push(struct place *p, bool startstate) 156{ 157 struct ifstate *newstate; 158 159 newstate = ifstate_create(ifstate, p, startstate); 160 if (!ifstate->curtrue) { 161 newstate->curtrue = false; 162 newstate->evertrue = true; 163 } 164 ifstate = newstate; 165} 166 167static 168void 169ifstate_pop(void) 170{ 171 struct ifstate *is; 172 173 is = ifstate; 174 ifstate = ifstate->prev; 175 ifstate_destroy(is); 176} 177 178static 179void 180d_if(struct lineplace *lp, struct place *p2, char *line) 181{ 182 bool doprint; 183 char *expr; 184 bool val; 185 struct place p3 = *p2; 186 size_t oldlen; 187 188 doprint = ifstate->curtrue; 189 190 expr = macroexpand(p2, line, strlen(line), true); 191 192 oldlen = strlen(expr); 193 uncomment(expr); 194 /* trim to fit, so the malloc debugging won't complain */ 195 expr = dorealloc(expr, oldlen + 1, strlen(expr) + 1); 196 197 if (ifstate->curtrue) { 198 val = eval(&p3, expr); 199 } else { 200 val = 0; 201 } 202 ifstate_push(&lp->current, val); 203 dostrfree(expr); 204 205 if (doprint) { 206 debuglog(&lp->current, "#if: %s", 207 ifstate->curtrue ? "taken" : "not taken"); 208 } 209} 210 211static 212void 213d_ifdef(struct lineplace *lp, struct place *p2, char *line) 214{ 215 bool doprint; 216 217 doprint = ifstate->curtrue; 218 219 uncomment(line); 220 oneword("#ifdef", p2, line); 221 ifstate_push(&lp->current, macro_isdefined(line)); 222 223 if (doprint) { 224 debuglog(&lp->current, "#ifdef %s: %s", 225 line, ifstate->curtrue ? "taken" : "not taken"); 226 } 227} 228 229static 230void 231d_ifndef(struct lineplace *lp, struct place *p2, char *line) 232{ 233 bool doprint; 234 235 doprint = ifstate->curtrue; 236 237 uncomment(line); 238 oneword("#ifndef", p2, line); 239 ifstate_push(&lp->current, !macro_isdefined(line)); 240 241 if (doprint) { 242 debuglog(&lp->current, "#ifndef %s: %s", 243 line, ifstate->curtrue ? "taken" : "not taken"); 244 } 245} 246 247static 248void 249d_elif(struct lineplace *lp, struct place *p2, char *line) 250{ 251 bool doprint; 252 char *expr; 253 struct place p3 = *p2; 254 size_t oldlen; 255 256 if (ifstate->seenelse) { 257 complain(&lp->current, "#elif after #else"); 258 complain_fail(); 259 } 260 261 doprint = ifstate->curtrue; 262 263 if (ifstate->evertrue) { 264 ifstate->curtrue = false; 265 } else { 266 expr = macroexpand(p2, line, strlen(line), true); 267 268 oldlen = strlen(expr); 269 uncomment(expr); 270 /* trim to fit, so the malloc debugging won't complain */ 271 expr = dorealloc(expr, oldlen + 1, strlen(expr) + 1); 272 273 ifstate->curtrue = eval(&p3, expr); 274 ifstate->evertrue = ifstate->curtrue; 275 dostrfree(expr); 276 } 277 278 if (doprint) { 279 debuglog2(&lp->current, &ifstate->startplace, "#elif: %s", 280 ifstate->curtrue ? "taken" : "not taken"); 281 } 282} 283 284static 285void 286d_else(struct lineplace *lp, struct place *p2, char *line) 287{ 288 bool doprint; 289 290 (void)p2; 291 (void)line; 292 293 if (ifstate->seenelse) { 294 complain(&lp->current, 295 "Multiple #else directives in one conditional"); 296 complain_fail(); 297 } 298 299 doprint = ifstate->curtrue; 300 301 ifstate->curtrue = !ifstate->evertrue; 302 ifstate->evertrue = true; 303 ifstate->seenelse = true; 304 305 if (doprint) { 306 debuglog2(&lp->current, &ifstate->startplace, "#else: %s", 307 ifstate->curtrue ? "taken" : "not taken"); 308 } 309} 310 311static 312void 313d_endif(struct lineplace *lp, struct place *p2, char *line) 314{ 315 (void)p2; 316 (void)line; 317 318 if (ifstate->prev == NULL) { 319 complain(&lp->current, "Unmatched #endif"); 320 complain_fail(); 321 } else { 322 debuglog2(&lp->current, &ifstate->startplace, "#endif"); 323 ifstate_pop(); 324 } 325} 326 327//////////////////////////////////////////////////////////// 328// macros 329 330static 331void 332d_define(struct lineplace *lp, struct place *p2, char *line) 333{ 334 size_t pos, argpos; 335 struct place p3, p4; 336 337 (void)lp; 338 339 /* 340 * line may be: 341 * macro expansion 342 * macro(arg, arg, ...) expansion 343 */ 344 345 pos = strcspn(line, " \t\f\v("); 346 if (line[pos] == '(') { 347 line[pos++] = '\0'; 348 argpos = pos; 349 pos = pos + strcspn(line+pos, "()"); 350 if (line[pos] == '(') { 351 p2->column += pos; 352 complain(p2, "Left parenthesis in macro parameters"); 353 complain_fail(); 354 return; 355 } 356 if (line[pos] != ')') { 357 p2->column += pos; 358 complain(p2, "Unclosed macro parameter list"); 359 complain_fail(); 360 return; 361 } 362 line[pos++] = '\0'; 363#if 0 364 if (!strchr(ws, line[pos])) { 365 p2->column += pos; 366 complain(p2, "Trash after macro parameter list"); 367 complain_fail(); 368 return; 369 } 370#endif 371 } else if (line[pos] == '\0') { 372 argpos = 0; 373 } else { 374 line[pos++] = '\0'; 375 argpos = 0; 376 } 377 378 pos += strspn(line+pos, ws); 379 380 p3 = *p2; 381 p3.column += argpos; 382 383 p4 = *p2; 384 p4.column += pos; 385 386 if (argpos) { 387 debuglog(&lp->current, "Defining %s()", line); 388 macro_define_params(p2, line, &p3, 389 line + argpos, &p4, 390 line + pos); 391 } else { 392 debuglog(&lp->current, "Defining %s", line); 393 macro_define_plain(p2, line, &p4, line + pos); 394 } 395} 396 397static 398void 399d_undef(struct lineplace *lp, struct place *p2, char *line) 400{ 401 (void)lp; 402 403 uncomment(line); 404 oneword("#undef", p2, line); 405 debuglog(&lp->current, "Undef %s", line); 406 macro_undef(line); 407} 408 409//////////////////////////////////////////////////////////// 410// includes 411 412static 413bool 414tryinclude(struct place *p, char *line) 415{ 416 size_t len; 417 418 len = strlen(line); 419 if (len > 2 && line[0] == '"' && line[len-1] == '"') { 420 line[len-1] = '\0'; 421 debuglog(p, "Entering include file \"%s\"", line+1); 422 file_readquote(p, line+1); 423 debuglog(p, "Leaving include file \"%s\"", line+1); 424 line[len-1] = '"'; 425 return true; 426 } 427 if (len > 2 && line[0] == '<' && line[len-1] == '>') { 428 line[len-1] = '\0'; 429 debuglog(p, "Entering include file <%s>", line+1); 430 file_readbracket(p, line+1); 431 debuglog(p, "Leaving include file <%s>", line+1); 432 line[len-1] = '>'; 433 return true; 434 } 435 return false; 436} 437 438static 439void 440d_include(struct lineplace *lp, struct place *p2, char *line) 441{ 442 char *text; 443 size_t oldlen; 444 445 uncomment(line); 446 if (tryinclude(&lp->current, line)) { 447 return; 448 } 449 text = macroexpand(p2, line, strlen(line), false); 450 451 oldlen = strlen(text); 452 uncomment(text); 453 /* trim to fit, so the malloc debugging won't complain */ 454 text = dorealloc(text, oldlen + 1, strlen(text) + 1); 455 456 if (tryinclude(&lp->current, text)) { 457 dostrfree(text); 458 return; 459 } 460 complain(&lp->current, "Illegal #include directive"); 461 complain(&lp->current, "Before macro expansion: #include %s", line); 462 complain(&lp->current, "After macro expansion: #include %s", text); 463 dostrfree(text); 464 complain_fail(); 465} 466 467static 468void 469d_line(struct lineplace *lp, struct place *p2, char *line) 470{ 471 char *text; 472 size_t oldlen; 473 unsigned long val; 474 char *moretext; 475 size_t moretextlen; 476 char *filename; 477 478 text = macroexpand(p2, line, strlen(line), true); 479 480 oldlen = strlen(text); 481 uncomment(text); 482 /* trim to fit, so the malloc debugging won't complain */ 483 text = dorealloc(text, oldlen + 1, strlen(text) + 1); 484 485 /* 486 * What we should have here: either 1234 "file.c", 487 * or just 1234. 488 */ 489 490 errno = 0; 491 val = strtoul(text, &moretext, 10); 492 if (errno) { 493 complain(&lp->current, "No line number in #line directive"); 494 goto fail; 495 } 496#if UINT_MAX < ULONG_MAX 497 if (val > UINT_MAX) { 498 complain(&lp->current, 499 "Line number in #line directive too large"); 500 goto fail; 501 } 502#endif 503 moretext += strspn(moretext, ws); 504 moretextlen = strlen(moretext); 505 lp->current.column += (moretext - text); 506 507 if (moretextlen > 2 && 508 moretext[0] == '"' && moretext[moretextlen-1] == '"') { 509 filename = dostrndup(moretext+1, moretextlen-2); 510 place_changefile(&lp->nextline, filename); 511 dostrfree(filename); 512 } 513 else if (moretextlen > 0) { 514 complain(&lp->current, 515 "Invalid file name in #line directive"); 516 goto fail; 517 } 518 519 lp->nextline.line = val; 520 dostrfree(text); 521 return; 522 523fail: 524 complain(&lp->current, "Before macro expansion: #line %s", line); 525 complain(&lp->current, "After macro expansion: #line %s", text); 526 complain_fail(); 527 dostrfree(text); 528} 529 530//////////////////////////////////////////////////////////// 531// messages 532 533static 534void 535d_warning(struct lineplace *lp, struct place *p2, char *line) 536{ 537 char *msg; 538 539 msg = macroexpand(p2, line, strlen(line), false); 540 complain(&lp->current, "#warning: %s", msg); 541 if (mode.werror) { 542 complain_fail(); 543 } 544 dostrfree(msg); 545} 546 547static 548void 549d_error(struct lineplace *lp, struct place *p2, char *line) 550{ 551 char *msg; 552 553 msg = macroexpand(p2, line, strlen(line), false); 554 complain(&lp->current, "#error: %s", msg); 555 complain_fail(); 556 dostrfree(msg); 557} 558 559//////////////////////////////////////////////////////////// 560// other 561 562static 563void 564d_pragma(struct lineplace *lp, struct place *p2, char *line) 565{ 566 (void)p2; 567 568 complain(&lp->current, "#pragma %s", line); 569 complain_fail(); 570} 571 572//////////////////////////////////////////////////////////// 573// directive table 574 575static const struct { 576 const char *name; 577 bool ifskip; 578 void (*func)(struct lineplace *, struct place *, char *line); 579} directives[] = { 580 { "define", true, d_define }, 581 { "elif", false, d_elif }, 582 { "else", false, d_else }, 583 { "endif", false, d_endif }, 584 { "error", true, d_error }, 585 { "if", false, d_if }, 586 { "ifdef", false, d_ifdef }, 587 { "ifndef", false, d_ifndef }, 588 { "include", true, d_include }, 589 { "line", true, d_line }, 590 { "pragma", true, d_pragma }, 591 { "undef", true, d_undef }, 592 { "warning", true, d_warning }, 593}; 594static const unsigned numdirectives = HOWMANY(directives); 595 596static 597void 598directive_gotdirective(struct lineplace *lp, char *line) 599{ 600 struct place p2; 601 size_t len, skip; 602 unsigned i; 603 604 p2 = lp->current; 605 for (i=0; i<numdirectives; i++) { 606 len = strlen(directives[i].name); 607 if (!strncmp(line, directives[i].name, len) && 608 strchr(ws, line[len])) { 609 if (directives[i].ifskip && !ifstate->curtrue) { 610 return; 611 } 612 skip = len + strspn(line+len, ws); 613 p2.column += skip; 614 line += skip; 615 616 len = strlen(line); 617 len = notrailingws(line, len); 618 if (len < strlen(line)) { 619 line[len] = '\0'; 620 } 621 directives[i].func(lp, &p2, line); 622 return; 623 } 624 } 625 /* ugh. allow # by itself, including with a comment after it */ 626 uncomment(line); 627 if (line[0] == '\0') { 628 return; 629 } 630 631 skip = strcspn(line, ws); 632 complain(&lp->current, "Unknown directive #%.*s", (int)skip, line); 633 complain_fail(); 634} 635 636/* 637 * Check for nested comment delimiters in LINE. 638 */ 639static 640size_t 641directive_scancomments(const struct lineplace *lp, char *line, size_t len) 642{ 643 size_t pos; 644 bool incomment; 645 struct place p2; 646 647 p2 = lp->current; 648 incomment = 0; 649 for (pos = 0; pos+1 < len; pos++) { 650 if (line[pos] == '/' && line[pos+1] == '*') { 651 if (incomment) { 652 complain(&p2, "Warning: %c%c within comment", 653 '/', '*'); 654 if (mode.werror) { 655 complain_failed(); 656 } 657 } else { 658 incomment = true; 659 } 660 pos++; 661 } else if (line[pos] == '*' && line[pos+1] == '/') { 662 if (incomment) { 663 incomment = false; 664 } else { 665 /* stray end-comment; should we care? */ 666 } 667 pos++; 668 } 669 if (line[pos] == '\n') { 670 p2.line++; 671 p2.column = 0; 672 } else { 673 p2.column++; 674 } 675 } 676 677 /* multiline comments are supposed to arrive in a single buffer */ 678 assert(!incomment); 679 return len; 680} 681 682void 683directive_gotline(struct lineplace *lp, char *line, size_t len) 684{ 685 size_t skip; 686 687 if (warns.nestcomment) { 688 directive_scancomments(lp, line, len); 689 } 690 691 /* check if we have a directive line (# exactly in column 0) */ 692 if (len > 0 && line[0] == '#') { 693 skip = 1 + strspn(line + 1, ws); 694 assert(skip <= len); 695 lp->current.column += skip; 696 assert(line[len] == '\0'); 697 directive_gotdirective(lp, line+skip /*, length = len-skip */); 698 lp->current.column += len-skip; 699 } else if (ifstate->curtrue) { 700 macro_sendline(&lp->current, line, len); 701 lp->current.column += len; 702 } 703} 704 705void 706directive_goteof(struct place *p) 707{ 708 while (ifstate->prev != NULL) { 709 complain(p, "Missing #endif"); 710 complain(&ifstate->startplace, "...opened at this point"); 711 complain_failed(); 712 ifstate_pop(); 713 } 714 macro_sendeof(p); 715} 716 717//////////////////////////////////////////////////////////// 718// module initialization 719 720void 721directive_init(void) 722{ 723 ifstate = ifstate_create(NULL, NULL, true); 724} 725 726void 727directive_cleanup(void) 728{ 729 assert(ifstate->prev == NULL); 730 ifstate_destroy(ifstate); 731 ifstate = NULL; 732} 733