1/* 2 3Copyright (c) 1993, 1994, 1998 The Open Group 4 5Permission to use, copy, modify, distribute, and sell this software and its 6documentation for any purpose is hereby granted without fee, provided that 7the above copyright notice appear in all copies and that both that 8copyright notice and this permission notice appear in supporting 9documentation. 10 11The above copyright notice and this permission notice shall be included in 12all copies or substantial portions of the Software. 13 14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 18AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 21Except as contained in this notice, the name of The Open Group shall not be 22used in advertising or otherwise to promote the sale, use or other dealings 23in this Software without prior written authorization from The Open Group. 24 25*/ 26 27#include "def.h" 28 29static int deftype(char *line, struct filepointer *filep, 30 struct inclist *file_red, struct inclist *file, 31 int parse_it); 32static int zero_value(char *filename, char *exp, struct filepointer *filep, 33 struct inclist *file_red); 34static struct symtab **slookup(const char *symbol, struct inclist *file); 35static boolean merge2defines(struct inclist *file1, struct inclist *file2); 36 37static const char *const directives[] = { 38 "if", 39 "ifdef", 40 "ifndef", 41 "else", 42 "endif", 43 "define", 44 "undef", 45 "include", 46 "line", 47 "pragma", 48 "error", 49 "ident", 50 "sccs", 51 "elif", 52 "eject", 53 "warning", 54 "include_next", 55 NULL 56}; 57 58static int 59gobble(struct filepointer *filep, struct inclist *file, 60 struct inclist *file_red) 61{ 62 char *line; 63 64 while ((line = getnextline(filep))) { 65 int type = deftype(line, filep, file_red, file, FALSE); 66 67 switch (type) { 68 case IF: 69 case IFFALSE: 70 case IFGUESSFALSE: 71 case IFDEF: 72 case IFNDEF: 73 type = gobble(filep, file, file_red); 74 while ((type == ELIF) || (type == ELIFFALSE) || 75 (type == ELIFGUESSFALSE)) 76 type = gobble(filep, file, file_red); 77 if (type == ELSE) 78 (void) gobble(filep, file, file_red); 79 break; 80 case ELSE: 81 case ENDIF: 82 debug(0, ("%s, line %d: #%s\n", 83 file->i_file, filep->f_line, directives[type])); 84 return (type); 85 case DEFINE: 86 case UNDEF: 87 case INCLUDE: 88 case INCLUDEDOT: 89 case PRAGMA: 90 case ERROR: 91 case IDENT: 92 case SCCS: 93 case EJECT: 94 case WARNING: 95 case INCLUDENEXT: 96 case INCLUDENEXTDOT: 97 break; 98 case ELIF: 99 case ELIFFALSE: 100 case ELIFGUESSFALSE: 101 return (type); 102 case -1: 103 warning("%s", file_red->i_file); 104 if (file_red != file) 105 warning1(" (reading %s)", file->i_file); 106 warning1(", line %ld: unknown directive == \"%s\"\n", 107 filep->f_line, line); 108 break; 109 } 110 } 111 return (-1); 112} 113 114/* 115 * Decide what type of # directive this line is. 116 */ 117static int 118deftype(char *line, struct filepointer *filep, 119 struct inclist *file_red, struct inclist *file, int parse_it) 120{ 121 char *p; 122 char *directive, savechar, *q; 123 int ret; 124 125 /* 126 * Parse the directive... 127 */ 128 directive = line + 1; 129 while (*directive == ' ' || *directive == '\t') 130 directive++; 131 132 p = directive; 133 while ((*p == '_') || (*p >= 'a' && *p <= 'z')) 134 p++; 135 savechar = *p; 136 *p = '\0'; 137 ret = match(directive, directives); 138 *p = savechar; 139 140 /* If we don't recognize this compiler directive or we happen to just 141 * be gobbling up text while waiting for an #endif or #elif or #else 142 * in the case of an #elif we must check the zero_value and return an 143 * ELIF or an ELIFFALSE. 144 */ 145 146 if (ret == ELIF && !parse_it) { 147 while (*p == ' ' || *p == '\t') 148 p++; 149 /* 150 * parse an expression. 151 */ 152 debug(0, ("%s, line %d: #elif %s ", file->i_file, filep->f_line, p)); 153 ret = zero_value(file->i_file, p, filep, file_red); 154 if (ret != IF) { 155 debug(0, ("false...\n")); 156 if (ret == IFFALSE) 157 return (ELIFFALSE); 158 else 159 return (ELIFGUESSFALSE); 160 } 161 else { 162 debug(0, ("true...\n")); 163 return (ELIF); 164 } 165 } 166 167 if (ret < 0 || !parse_it) 168 return (ret); 169 170 /* 171 * now decide how to parse the directive, and do it. 172 */ 173 while (*p == ' ' || *p == '\t') 174 p++; 175 q = p + strlen(p); 176 do { 177 q--; 178 } while (*q == ' ' || *q == '\t'); 179 q[1] = '\0'; 180 switch (ret) { 181 case IF: 182 /* 183 * parse an expression. 184 */ 185 ret = zero_value(file->i_file, p, filep, file_red); 186 debug(0, ("%s, line %d: %s #if %s\n", 187 file->i_file, filep->f_line, ret ? "false" : "true", p)); 188 break; 189 case IFDEF: 190 case IFNDEF: 191 debug(0, ("%s, line %d: #%s %s\n", 192 file->i_file, filep->f_line, directives[ret], p)); 193 case UNDEF: 194 /* 195 * separate the name of a single symbol. 196 */ 197 while (isalnum(*p) || *p == '_') 198 *line++ = *p++; 199 *line = '\0'; 200 break; 201 case INCLUDE: 202 case INCLUDENEXT: 203 debug(2, ("%s, line %d: #include%s %s\n", 204 file->i_file, filep->f_line, 205 (ret == INCLUDE) ? "" : "_next", p)); 206 207 /* Support ANSI macro substitution */ 208 while (1) { 209 struct symtab **sym; 210 211 if (!*p || *p == '"' || *p == '<') 212 break; 213 214 sym = isdefined(p, file_red, NULL); 215 if (!sym) 216 break; 217 218 p = (*sym)->s_value; 219 debug(3, ("%s : #includes SYMBOL %s = %s\n", 220 file->i_incstring, (*sym)->s_name, (*sym)->s_value)); 221 /* mark file as having included a 'soft include' */ 222 file->i_flags |= INCLUDED_SYM; 223 } 224 225 /* 226 * Separate the name of the include file. 227 */ 228 while (*p && *p != '"' && *p != '<') 229 p++; 230 if (!*p) 231 return (-2); 232 if (*p++ == '"') { 233 if (ret == INCLUDE) 234 ret = INCLUDEDOT; 235 else 236 ret = INCLUDENEXTDOT; 237 while (*p && *p != '"') 238 *line++ = *p++; 239 } 240 else 241 while (*p && *p != '>') 242 *line++ = *p++; 243 *line = '\0'; 244 break; 245 case DEFINE: 246 /* 247 * copy the definition back to the beginning of the line. 248 */ 249 memmove(line, p, strlen(p) + 1); 250 break; 251 case ELSE: 252 case ENDIF: 253 case ELIF: 254 case PRAGMA: 255 case ERROR: 256 case IDENT: 257 case SCCS: 258 case EJECT: 259 case WARNING: 260 debug(0, ("%s, line %d: #%s\n", 261 file->i_file, filep->f_line, directives[ret])); 262 /* 263 * nothing to do. 264 */ 265 break; 266 } 267 return (ret); 268} 269 270static struct symtab ** 271fdefined(const char *symbol, struct inclist *file, struct inclist **srcfile) 272{ 273 struct symtab **val; 274 static int recurse_lvl = 0; 275 276 if (file->i_flags & DEFCHECKED) 277 return (NULL); 278 debug(2, ("Looking for %s in %s\n", symbol, file->i_file)); 279 file->i_flags |= DEFCHECKED; 280 if ((val = slookup(symbol, file))) 281 debug(1, ("%s defined in %s as %s\n", 282 symbol, file->i_file, (*val)->s_value)); 283 if (val == NULL && file->i_list) { 284 struct inclist **ip; 285 unsigned int i; 286 287 for (ip = file->i_list, i = 0; i < file->i_listlen; i++, ip++) { 288 if (file->i_merged[i] == FALSE) { 289 val = fdefined(symbol, *ip, srcfile); 290 file->i_merged[i] = merge2defines(file, *ip); 291 if (val != NULL) 292 break; 293 } 294 } 295 } 296 else if (val != NULL && srcfile != NULL) 297 *srcfile = file; 298 recurse_lvl--; 299 file->i_flags &= ~DEFCHECKED; 300 301 return (val); 302} 303 304struct symtab ** 305isdefined(const char *symbol, struct inclist *file, struct inclist **srcfile) 306{ 307 struct symtab **val; 308 309 if ((val = slookup(symbol, &maininclist))) { 310 debug(1, ("%s defined on command line\n", symbol)); 311 if (srcfile != NULL) 312 *srcfile = &maininclist; 313 return (val); 314 } 315 if ((val = fdefined(symbol, file, srcfile))) 316 return (val); 317 debug(1, ("%s not defined in %s\n", symbol, file->i_file)); 318 return (NULL); 319} 320 321/* 322 * Return type based on if the #if expression evaluates to 0 323 */ 324static int 325zero_value(char *filename, char *exp, 326 struct filepointer *filep, struct inclist *file_red) 327{ 328 if (cppsetup(filename, exp, filep, file_red)) 329 return (IFFALSE); 330 else 331 return (IF); 332} 333 334void 335define2(const char *name, const char *val, struct inclist *file) 336{ 337 int first, last, below; 338 struct symtab **sp = NULL, **dest; 339 struct symtab *stab; 340 341 /* Make space if it's needed */ 342 if (file->i_defs == NULL) { 343 file->i_defs = mallocarray(SYMTABINC, sizeof(struct symtab *)); 344 file->i_ndefs = 0; 345 } 346 else if (!(file->i_ndefs % SYMTABINC)) 347 file->i_defs = reallocarray(file->i_defs, (file->i_ndefs + SYMTABINC), 348 sizeof(struct symtab *)); 349 350 if (file->i_defs == NULL) 351 fatalerr("malloc()/realloc() failure in insert_defn()\n"); 352 353 below = first = 0; 354 last = file->i_ndefs - 1; 355 while (last >= first) { 356 /* Fast inline binary search */ 357 const char *s1; 358 const char *s2; 359 int middle = (first + last) / 2; 360 361 /* Fast inline strcmp() */ 362 s1 = name; 363 s2 = file->i_defs[middle]->s_name; 364 while (*s1++ == *s2++) 365 if (s2[-1] == '\0') 366 break; 367 368 /* If exact match, set sp and break */ 369 if (*--s1 == *--s2) { 370 sp = file->i_defs + middle; 371 break; 372 } 373 374 /* If name > i_defs[middle] ... */ 375 if (*s1 > *s2) { 376 below = first; 377 first = middle + 1; 378 } 379 /* else ... */ 380 else { 381 below = last = middle - 1; 382 } 383 } 384 385 /* Search is done. If we found an exact match to the symbol name, 386 just replace its s_value */ 387 if (sp != NULL) { 388 debug(1, ("redefining %s from %s to %s in file %s\n", 389 name, (*sp)->s_value, val, file->i_file)); 390 free((*sp)->s_value); 391 (*sp)->s_value = strdup(val); 392 if ((*sp)->s_value == NULL) 393 fatalerr("strdup() failure in %s()\n", __func__); 394 return; 395 } 396 397 sp = file->i_defs + file->i_ndefs++; 398 dest = file->i_defs + below + 1; 399 while (sp > dest) { 400 *sp = sp[-1]; 401 sp--; 402 } 403 stab = malloc(sizeof(struct symtab)); 404 if (stab == NULL) 405 fatalerr("malloc()/realloc() failure in insert_defn()\n"); 406 407 debug(1, ("defining %s to %s in file %s\n", name, val, file->i_file)); 408 stab->s_name = strdup(name); 409 stab->s_value = strdup(val); 410 if ((stab->s_name == NULL) || (stab->s_value == NULL)) 411 fatalerr("strdup() failure in %s()\n", __func__); 412 *sp = stab; 413} 414 415void 416define(char *def, struct inclist *file) 417{ 418 char *val; 419 420 /* Separate symbol name and its value */ 421 val = def; 422 while (isalnum(*val) || *val == '_') 423 val++; 424 if (*val) 425 *val++ = '\0'; 426 while (*val == ' ' || *val == '\t') 427 val++; 428 429 if (!*val) 430 define2(def, "1", file); 431 else 432 define2(def, val, file); 433} 434 435struct symtab ** 436slookup(const char *symbol, struct inclist *file) 437{ 438 int first = 0; 439 int last; 440 441 if (file == NULL) 442 return NULL; 443 444 last = file->i_ndefs - 1; 445 446 while (last >= first) { 447 /* Fast inline binary search */ 448 const char *s1; 449 const char *s2; 450 int middle = (first + last) / 2; 451 452 /* Fast inline strcmp() */ 453 s1 = symbol; 454 s2 = file->i_defs[middle]->s_name; 455 while (*s1++ == *s2++) 456 if (s2[-1] == '\0') 457 break; 458 459 /* If exact match, we're done */ 460 if (*--s1 == *--s2) { 461 return file->i_defs + middle; 462 } 463 464 /* If symbol > i_defs[middle] ... */ 465 if (*s1 > *s2) { 466 first = middle + 1; 467 } 468 /* else ... */ 469 else { 470 last = middle - 1; 471 } 472 } 473 return (NULL); 474} 475 476static boolean 477merge2defines(struct inclist *file1, struct inclist *file2) 478{ 479 if ((file1 == NULL) || (file2 == NULL) || !(file2->i_flags & FINISHED)) 480 return 0; 481 482 for (unsigned int i = 0; i < file2->i_listlen; i++) 483 if (file2->i_merged[i] == FALSE) 484 return 0; 485 486 { 487 int first1 = 0; 488 int last1 = file1->i_ndefs - 1; 489 490 int first2 = 0; 491 int last2 = file2->i_ndefs - 1; 492 493 int first = 0; 494 struct symtab **i_defs = NULL; 495 int deflen = file1->i_ndefs + file2->i_ndefs; 496 497 debug(2, ("merging %s into %s\n", file2->i_file, file1->i_file)); 498 499 if (deflen > 0) { 500 /* make sure deflen % SYMTABINC == 0 is still true */ 501 deflen += (SYMTABINC - deflen % SYMTABINC) % SYMTABINC; 502 i_defs = mallocarray(deflen, sizeof(struct symtab *)); 503 if (i_defs == NULL) 504 return 0; 505 } 506 507 while ((last1 >= first1) && (last2 >= first2)) { 508 const char *s1 = file1->i_defs[first1]->s_name; 509 const char *s2 = file2->i_defs[first2]->s_name; 510 511 if (strcmp(s1, s2) < 0) 512 i_defs[first++] = file1->i_defs[first1++]; 513 else if (strcmp(s1, s2) > 0) 514 i_defs[first++] = file2->i_defs[first2++]; 515 else { /* equal */ 516 i_defs[first++] = file2->i_defs[first2++]; 517 first1++; 518 } 519 } 520 while (last1 >= first1) { 521 i_defs[first++] = file1->i_defs[first1++]; 522 } 523 while (last2 >= first2) { 524 i_defs[first++] = file2->i_defs[first2++]; 525 } 526 527 if (file1->i_defs) 528 free(file1->i_defs); 529 file1->i_defs = i_defs; 530 file1->i_ndefs = first; 531 532 return 1; 533 } 534} 535 536void 537undefine(const char *symbol, struct inclist *file) 538{ 539 struct symtab **ptr; 540 struct inclist *srcfile; 541 542 while ((ptr = isdefined(symbol, file, &srcfile)) != NULL) { 543 srcfile->i_ndefs--; 544 for (; ptr < srcfile->i_defs + srcfile->i_ndefs; ptr++) 545 *ptr = ptr[1]; 546 } 547} 548 549int 550find_includes(struct filepointer *filep, struct inclist *file, 551 struct inclist *file_red, int recursion, boolean failOK) 552{ 553 char *line; 554 555 while ((line = getnextline(filep))) { 556 int type = deftype(line, filep, file_red, file, TRUE); 557 558 switch (type) { 559 case IF: 560 doif: 561 type = find_includes(filep, file, file_red, recursion + 1, failOK); 562 while ((type == ELIF) || (type == ELIFFALSE) || 563 (type == ELIFGUESSFALSE)) 564 type = gobble(filep, file, file_red); 565 if (type == ELSE) 566 gobble(filep, file, file_red); 567 break; 568 case IFFALSE: 569 case IFGUESSFALSE: 570 doiffalse: 571 { 572 boolean recfailOK; 573 574 if (type == IFGUESSFALSE || type == ELIFGUESSFALSE) 575 recfailOK = TRUE; 576 else 577 recfailOK = failOK; 578 type = gobble(filep, file, file_red); 579 if (type == ELSE) 580 find_includes(filep, file, file_red, recursion + 1, recfailOK); 581 else if (type == ELIF) 582 goto doif; 583 else if ((type == ELIFFALSE) || (type == ELIFGUESSFALSE)) 584 goto doiffalse; 585 } 586 break; 587 case IFDEF: 588 case IFNDEF: 589 if ((type == IFDEF && isdefined(line, file_red, NULL)) 590 || (type == IFNDEF && !isdefined(line, file_red, NULL))) { 591 debug(1, (type == IFNDEF ? 592 "line %d: %s !def'd in %s via %s%s\n" : "", 593 filep->f_line, line, 594 file->i_file, file_red->i_file, ": doit")); 595 type = find_includes(filep, file, 596 file_red, recursion + 1, failOK); 597 while (type == ELIF || type == ELIFFALSE || 598 type == ELIFGUESSFALSE) 599 type = gobble(filep, file, file_red); 600 if (type == ELSE) 601 gobble(filep, file, file_red); 602 } 603 else { 604 debug(1, (type == IFDEF ? 605 "line %d: %s !def'd in %s via %s%s\n" : "", 606 filep->f_line, line, 607 file->i_file, file_red->i_file, ": gobble")); 608 type = gobble(filep, file, file_red); 609 if (type == ELSE) 610 find_includes(filep, file, file_red, recursion + 1, failOK); 611 else if (type == ELIF) 612 goto doif; 613 else if (type == ELIFFALSE || type == ELIFGUESSFALSE) 614 goto doiffalse; 615 } 616 break; 617 case ELSE: 618 case ELIFFALSE: 619 case ELIFGUESSFALSE: 620 case ELIF: 621 if (!recursion) 622 gobble(filep, file, file_red); 623 case ENDIF: 624 if (recursion) 625 return (type); 626 case DEFINE: 627 define(line, file); 628 break; 629 case UNDEF: 630 if (!*line) { 631 warning("%s", file_red->i_file); 632 if (file_red != file) 633 warning1(" (reading %s)", file->i_file); 634 warning1(", line %ld: incomplete undef == \"%s\"\n", 635 filep->f_line, line); 636 break; 637 } 638 undefine(line, file_red); 639 break; 640 case INCLUDE: 641 case INCLUDEDOT: 642 case INCLUDENEXT: 643 case INCLUDENEXTDOT: 644 { 645 struct inclist *inclist_save = inclistnext; 646 const char **includedirs_save = includedirsnext; 647 648 debug(2, ("%s, reading %s, includes %s\n", 649 file_red->i_file, file->i_file, line)); 650 add_include(filep, file, file_red, line, type, failOK); 651 inclistnext = inclist_save; 652 includedirsnext = includedirs_save; 653 } 654 break; 655 case ERROR: 656 case WARNING: 657 warning("%s", file_red->i_file); 658 if (file_red != file) 659 warning1(" (reading %s)", file->i_file); 660 warning1(", line %ld: %s\n", filep->f_line, line); 661 break; 662 663 case PRAGMA: 664 case IDENT: 665 case SCCS: 666 case EJECT: 667 break; 668 case -1: 669 warning("%s", file_red->i_file); 670 if (file_red != file) 671 warning1(" (reading %s)", file->i_file); 672 warning1(", line %ld: unknown directive == \"%s\"\n", 673 filep->f_line, line); 674 break; 675 case -2: 676 warning("%s", file_red->i_file); 677 if (file_red != file) 678 warning1(" (reading %s)", file->i_file); 679 warning1(", line %ld: incomplete include == \"%s\"\n", 680 filep->f_line, line); 681 break; 682 } 683 } 684 file->i_flags |= FINISHED; 685 debug(2, ("finished with %s\n", file->i_file)); 686 return (-1); 687} 688