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 46 struct ifstate { 47 struct ifstate *prev; 48 struct place startplace; 49 bool curtrue; 50 bool evertrue; 51 bool seenelse; 52 }; 53 54 static struct ifstate *ifstate; 55 56 //////////////////////////////////////////////////////////// 57 // common parsing bits 58 59 static 60 void 61 uncomment(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 109 static 110 void 111 oneword(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 place_addcolumns(p2, pos); 118 complain(p2, "Garbage after %s argument", what); 119 complain_fail(); 120 line[pos] = '\0'; 121 } 122 } 123 124 //////////////////////////////////////////////////////////// 125 // if handling 126 127 static 128 struct ifstate * 129 ifstate_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 146 static 147 void 148 ifstate_destroy(struct ifstate *is) 149 { 150 dofree(is, sizeof(*is)); 151 } 152 153 static 154 void 155 ifstate_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 167 static 168 void 169 ifstate_pop(void) 170 { 171 struct ifstate *is; 172 173 is = ifstate; 174 ifstate = ifstate->prev; 175 ifstate_destroy(is); 176 } 177 178 static 179 void 180 d_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 211 static 212 void 213 d_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 229 static 230 void 231 d_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 247 static 248 void 249 d_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 284 static 285 void 286 d_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 311 static 312 void 313 d_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 330 static 331 void 332 d_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 place_addcolumns(p2, pos); 352 complain(p2, "Left parenthesis in macro parameters"); 353 complain_fail(); 354 return; 355 } 356 if (line[pos] != ')') { 357 place_addcolumns(p2, 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 place_addcolumns(&p3, argpos); 382 383 p4 = *p2; 384 place_addcolumns(&p4, 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 397 static 398 void 399 d_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 412 static 413 bool 414 tryinclude(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 438 static 439 void 440 d_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 467 static 468 void 469 d_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, 494 "Invalid line number in #line directive"); 495 goto fail; 496 } 497 #if UINT_MAX < ULONG_MAX 498 if (val > UINT_MAX) { 499 complain(&lp->current, 500 "Line number in #line directive too large"); 501 goto fail; 502 } 503 #endif 504 moretext += strspn(moretext, ws); 505 moretextlen = strlen(moretext); 506 place_addcolumns(&lp->current, moretext - text); 507 508 if (moretextlen > 2 && 509 moretext[0] == '"' && moretext[moretextlen-1] == '"') { 510 filename = dostrndup(moretext+1, moretextlen-2); 511 place_changefile(&lp->nextline, filename); 512 dostrfree(filename); 513 } 514 else if (moretextlen > 0) { 515 complain(&lp->current, 516 "Invalid file name in #line directive"); 517 goto fail; 518 } 519 520 lp->nextline.line = val; 521 dostrfree(text); 522 return; 523 524 fail: 525 complain(&lp->current, "Before macro expansion: #line %s", line); 526 complain(&lp->current, "After macro expansion: #line %s", text); 527 complain_fail(); 528 dostrfree(text); 529 } 530 531 //////////////////////////////////////////////////////////// 532 // messages 533 534 static 535 void 536 d_warning(struct lineplace *lp, struct place *p2, char *line) 537 { 538 char *msg; 539 540 msg = macroexpand(p2, line, strlen(line), false); 541 complain(&lp->current, "#warning: %s", msg); 542 if (mode.werror) { 543 complain_fail(); 544 } 545 dostrfree(msg); 546 } 547 548 static 549 void 550 d_error(struct lineplace *lp, struct place *p2, char *line) 551 { 552 char *msg; 553 554 msg = macroexpand(p2, line, strlen(line), false); 555 complain(&lp->current, "#error: %s", msg); 556 complain_fail(); 557 dostrfree(msg); 558 } 559 560 //////////////////////////////////////////////////////////// 561 // other 562 563 static 564 void 565 d_pragma(struct lineplace *lp, struct place *p2, char *line) 566 { 567 (void)p2; 568 569 complain(&lp->current, "#pragma %s", line); 570 complain_fail(); 571 } 572 573 //////////////////////////////////////////////////////////// 574 // directive table 575 576 static const struct { 577 const char *name; 578 bool ifskip; 579 void (*func)(struct lineplace *, struct place *, char *line); 580 } directives[] = { 581 { "define", true, d_define }, 582 { "elif", false, d_elif }, 583 { "else", false, d_else }, 584 { "endif", false, d_endif }, 585 { "error", true, d_error }, 586 { "if", false, d_if }, 587 { "ifdef", false, d_ifdef }, 588 { "ifndef", false, d_ifndef }, 589 { "include", true, d_include }, 590 { "line", true, d_line }, 591 { "pragma", true, d_pragma }, 592 { "undef", true, d_undef }, 593 { "warning", true, d_warning }, 594 }; 595 static const unsigned numdirectives = HOWMANY(directives); 596 597 static 598 void 599 directive_gotdirective(struct lineplace *lp, char *line) 600 { 601 struct place p2; 602 size_t len, skip; 603 unsigned i; 604 605 p2 = lp->current; 606 for (i=0; i<numdirectives; i++) { 607 len = strlen(directives[i].name); 608 if (!strncmp(line, directives[i].name, len) && 609 strchr(ws, line[len])) { 610 if (directives[i].ifskip && !ifstate->curtrue) { 611 return; 612 } 613 skip = len + strspn(line+len, ws); 614 place_addcolumns(&p2, skip); 615 line += skip; 616 617 len = strlen(line); 618 len = notrailingws(line, len); 619 if (len < strlen(line)) { 620 line[len] = '\0'; 621 } 622 directives[i].func(lp, &p2, line); 623 return; 624 } 625 } 626 /* ugh. allow # by itself, including with a comment after it */ 627 uncomment(line); 628 if (line[0] == '\0') { 629 return; 630 } 631 632 skip = strcspn(line, ws); 633 complain(&lp->current, "Unknown directive #%.*s", (int)skip, line); 634 complain_fail(); 635 } 636 637 /* 638 * Check for nested comment delimiters in LINE. 639 */ 640 static 641 size_t 642 directive_scancomments(const struct lineplace *lp, char *line, size_t len) 643 { 644 size_t pos; 645 bool incomment; 646 struct place p2; 647 648 p2 = lp->current; 649 incomment = 0; 650 for (pos = 0; pos+1 < len; pos++) { 651 if (line[pos] == '/' && line[pos+1] == '*') { 652 if (incomment) { 653 complain(&p2, "Warning: %c%c within comment", 654 '/', '*'); 655 if (mode.werror) { 656 complain_failed(); 657 } 658 } else { 659 incomment = true; 660 } 661 pos++; 662 } else if (line[pos] == '*' && line[pos+1] == '/') { 663 if (incomment) { 664 incomment = false; 665 } else { 666 /* stray end-comment; should we care? */ 667 } 668 pos++; 669 } 670 if (line[pos] == '\n') { 671 place_addlines(&p2, 1); 672 p2.column = 0; 673 } else { 674 place_addcolumns(&p2, 1); 675 } 676 } 677 678 /* multiline comments are supposed to arrive in a single buffer */ 679 assert(!incomment); 680 return len; 681 } 682 683 void 684 directive_gotline(struct lineplace *lp, char *line, size_t len) 685 { 686 size_t skip; 687 688 if (warns.nestcomment) { 689 directive_scancomments(lp, line, len); 690 } 691 692 /* check if we have a directive line (# exactly in column 0) */ 693 if (len > 0 && line[0] == '#') { 694 skip = 1 + strspn(line + 1, ws); 695 assert(skip <= len); 696 place_addcolumns(&lp->current, skip); 697 assert(line[len] == '\0'); 698 directive_gotdirective(lp, line+skip /*, length = len-skip */); 699 place_addcolumns(&lp->current, len-skip); 700 } else if (ifstate->curtrue) { 701 macro_sendline(&lp->current, line, len); 702 place_addcolumns(&lp->current, len); 703 } 704 } 705 706 void 707 directive_goteof(struct place *p) 708 { 709 while (ifstate->prev != NULL) { 710 complain(p, "Missing #endif"); 711 complain(&ifstate->startplace, "...opened at this point"); 712 complain_failed(); 713 ifstate_pop(); 714 } 715 macro_sendeof(p); 716 } 717 718 //////////////////////////////////////////////////////////// 719 // module initialization 720 721 void 722 directive_init(void) 723 { 724 ifstate = ifstate_create(NULL, NULL, true); 725 } 726 727 void 728 directive_cleanup(void) 729 { 730 assert(ifstate->prev == NULL); 731 ifstate_destroy(ifstate); 732 ifstate = NULL; 733 } 734