1 /*********************************************************************** 2 * * 3 * This software is part of the ast package * 4 * Copyright (c) 1986-2008 AT&T Intellectual Property * 5 * and is licensed under the * 6 * Common Public License, Version 1.0 * 7 * by AT&T Intellectual Property * 8 * * 9 * A copy of the License is available at * 10 * http://www.opensource.org/licenses/cpl1.0.txt * 11 * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * 12 * * 13 * Information and Software Systems Research * 14 * AT&T Research * 15 * Florham Park NJ * 16 * * 17 * Glenn Fowler <gsf@research.att.com> * 18 * * 19 ***********************************************************************/ 20 #pragma prototyped 21 /* 22 * Glenn Fowler 23 * AT&T Research 24 * 25 * preprocessor stacked input stream support 26 */ 27 28 #include "pplib.h" 29 30 31 /* 32 * convert path to native representation 33 */ 34 35 #if 0 36 #include "../../lib/libast/path/pathnative.c" /* drop in 2002 */ 37 #else 38 /* Modified by gisburn 2006-08-18 for OpenSolaris ksh93-integration */ 39 #include "../../libast/common/path/pathnative.c" 40 #endif 41 42 static char* 43 native(register const char* s) 44 { 45 register int c; 46 register struct ppfile* xp; 47 int m; 48 int n; 49 50 static Sfio_t* np; 51 static Sfio_t* qp; 52 53 if (!s) 54 return 0; 55 if (!np && !(np = sfstropen()) || !qp && !(qp = sfstropen())) 56 return (char*)s; 57 n = PATH_MAX; 58 do 59 { 60 m = n; 61 n = pathnative(s, sfstrrsrv(np, m), m); 62 } while (n > m); 63 sfstrseek(np, n, SEEK_CUR); 64 s = (const char*)sfstruse(np); 65 for (;;) 66 { 67 switch (c = *s++) 68 { 69 case 0: 70 break; 71 case '\\': 72 case '"': 73 sfputc(qp, '\\'); 74 /*FALLTHROUGH*/ 75 default: 76 sfputc(qp, c); 77 continue; 78 } 79 break; 80 } 81 if (!(xp = ppsetfile(sfstruse(qp)))) 82 return (char*)s; 83 return xp->name; 84 } 85 86 /* 87 * push stream onto input stack 88 * used by the PUSH_type macros 89 */ 90 91 void 92 pppush(register int t, register char* s, register char* p, int n) 93 { 94 register struct ppinstk* cur; 95 96 PUSH(t, cur); 97 cur->line = error_info.line; 98 cur->file = error_info.file; 99 switch (t) 100 { 101 case IN_FILE: 102 if (pp.option & NATIVE) 103 s = native(s); 104 cur->flags |= IN_newline; 105 cur->fd = n; 106 cur->hide = ++pp.hide; 107 cur->symbol = 0; 108 #if CHECKPOINT 109 if ((pp.mode & (DUMP|INIT)) == DUMP) 110 { 111 cur->index = newof(0, struct ppindex, 1, 0); 112 if (pp.lastindex) pp.lastindex->next = cur->index; 113 else pp.firstindex = cur->index; 114 pp.lastindex = cur->index; 115 cur->index->file = pp.original; 116 cur->index->begin = ppoffset(); 117 } 118 #endif 119 n = 1; 120 #if CHECKPOINT 121 if (!(pp.mode & DUMP)) 122 #endif 123 if (!cur->prev->prev && !(pp.state & COMPILE) && isatty(0)) 124 cur->flags |= IN_flush; 125 #if ARCHIVE 126 if (pp.member) 127 { 128 switch (pp.member->archive->type & (TYPE_BUFFER|TYPE_CHECKPOINT)) 129 { 130 case 0: 131 #if CHECKPOINT 132 cur->buflen = pp.member->size; 133 #endif 134 p = (cur->buffer = oldof(0, char, 0, pp.member->size + PPBAKSIZ + 1)) + PPBAKSIZ; 135 if (sfseek(pp.member->archive->info.sp, pp.member->offset, SEEK_SET) != pp.member->offset) 136 error(3, "%s: archive seek error", pp.member->archive->name); 137 if (sfread(pp.member->archive->info.sp, p, pp.member->size) != pp.member->size) 138 error(3, "%s: archive read error", pp.member->archive->name); 139 pp.member = 0; 140 break; 141 case TYPE_BUFFER: 142 #if CHECKPOINT 143 case TYPE_CHECKPOINT|TYPE_BUFFER: 144 cur->buflen = pp.member->size; 145 #endif 146 p = cur->buffer = pp.member->archive->info.buffer + pp.member->offset; 147 cur->flags |= IN_static; 148 pp.member = 0; 149 break; 150 #if CHECKPOINT 151 case TYPE_CHECKPOINT: 152 p = cur->buffer = ""; 153 cur->flags |= IN_static; 154 break; 155 #endif 156 } 157 cur->flags |= IN_eof|IN_newline; 158 cur->fd = -1; 159 } 160 else 161 #endif 162 { 163 if (lseek(cur->fd, 0L, SEEK_END) > 0 && !lseek(cur->fd, 0L, SEEK_SET)) 164 cur->flags |= IN_regular; 165 errno = 0; 166 #if PROTOTYPE 167 if (!(pp.option & NOPROTO) && !(pp.test & TEST_noproto) && ((pp.state & (COMPATIBILITY|TRANSITION)) == COMPATIBILITY || (pp.option & PLUSPLUS) || (pp.mode & EXTERNALIZE)) && (cur->buffer = pppopen(NiL, cur->fd, NiL, NiL, NiL, NiL, (PROTO_HEADER|PROTO_RETAIN)|(((pp.mode & EXTERNALIZE) || (pp.option & PROTOTYPED)) ? PROTO_FORCE : PROTO_PASS)|((pp.mode & EXTERNALIZE) ? PROTO_EXTERNALIZE : 0)|((pp.mode & MARKC) ? PROTO_PLUSPLUS : 0)))) 168 { 169 *(p = cur->buffer - 1) = 0; 170 cur->buffer -= PPBAKSIZ; 171 cur->flags |= IN_prototype; 172 cur->fd = -1; 173 } 174 else 175 #endif 176 *(p = (cur->buffer = oldof(0, char, 0, PPBUFSIZ + PPBAKSIZ + 1)) + PPBAKSIZ) = 0; 177 } 178 if (pp.incref && !(pp.mode & INIT)) 179 (*pp.incref)(error_info.file, s, error_info.line - 1, PP_SYNC_PUSH); 180 if (pp.macref || (pp.option & IGNORELINE)) 181 cur->flags |= IN_ignoreline; 182 cur->prefix = pp.prefix; 183 /*FALLTHROUGH*/ 184 case IN_BUFFER: 185 case IN_INIT: 186 case IN_RESCAN: 187 pushcontrol(); 188 cur->control = pp.control; 189 *pp.control = 0; 190 cur->vendor = pp.vendor; 191 if (cur->type != IN_RESCAN) 192 { 193 if (cur->type == IN_INIT) 194 pp.mode |= MARKHOSTED; 195 error_info.file = s; 196 error_info.line = n; 197 } 198 if (pp.state & HIDDEN) 199 { 200 pp.state &= ~HIDDEN; 201 pp.hidden = 0; 202 if (!(pp.state & NOTEXT) && pplastout() != '\n') 203 ppputchar('\n'); 204 } 205 pp.state |= NEWLINE; 206 if (pp.mode & HOSTED) cur->flags |= IN_hosted; 207 else cur->flags &= ~IN_hosted; 208 if (pp.mode & (INIT|MARKHOSTED)) 209 { 210 pp.mode |= HOSTED; 211 pp.flags |= PP_hosted; 212 } 213 switch (cur->type) 214 { 215 case IN_FILE: 216 if (!(pp.mode & (INIT|MARKHOSTED))) 217 { 218 pp.mode &= ~HOSTED; 219 pp.flags &= ~PP_hosted; 220 } 221 #if CATSTRINGS 222 if (pp.state & JOINING) pp.state |= HIDDEN|SYNCLINE; 223 else 224 #endif 225 if (pp.linesync) 226 (*pp.linesync)(error_info.line, error_info.file); 227 #if ARCHIVE && CHECKPOINT 228 if (pp.member) 229 ppload(NiL); 230 #endif 231 if (pp.mode & MARKC) 232 { 233 cur->flags |= IN_c; 234 pp.mode &= ~MARKC; 235 if (!(cur->prev->flags & IN_c)) 236 { 237 debug((-7, "PUSH in=%s next=%s [%s]", ppinstr(pp.in), pptokchr(*pp.in->nextchr), pp.in->nextchr)); 238 PUSH_BUFFER("C", "extern \"C\" {\n", 1); 239 return; 240 } 241 } 242 else if (cur->prev->flags & IN_c) 243 { 244 debug((-7, "PUSH in=%s next=%s [%s]", ppinstr(pp.in), pptokchr(*pp.in->nextchr), pp.in->nextchr)); 245 PUSH_BUFFER("C", "extern \"C++\" {\n", 1); 246 return; 247 } 248 break; 249 case IN_BUFFER: 250 cur->buffer = p = strdup(p); 251 break; 252 default: 253 cur->buffer = p; 254 break; 255 } 256 cur->nextchr = p; 257 break; 258 #if DEBUG 259 default: 260 error(PANIC, "use PUSH_<%d>(...) instead of pppush(IN_<%d>, ...)", cur->type, cur->type); 261 break; 262 #endif 263 } 264 debug((-7, "PUSH in=%s next=%s", ppinstr(pp.in), pptokchr(*pp.in->nextchr))); 265 } 266 267 /* 268 * external buffer push 269 */ 270 271 void 272 ppinput(char* b, char* f, int n) 273 { 274 PUSH_BUFFER(f, b, n); 275 } 276 277 /* 278 * return expanded value of buffer p 279 */ 280 281 char* 282 ppexpand(register char* p) 283 { 284 register char* m; 285 register int n; 286 register int c; 287 long restore; 288 char* pptoken; 289 char* ppmactop; 290 struct ppmacstk* nextmacp; 291 struct ppinstk* cur; 292 293 debug((-7, "before expand: %s", p)); 294 if (ppmactop = pp.mactop) 295 { 296 nextmacp = pp.macp->next; 297 nextframe(pp.macp, pp.mactop); 298 } 299 restore = pp.state & (COLLECTING|DISABLE|STRIP); 300 pp.state &= ~restore; 301 pp.mode &= ~MARKMACRO; 302 PUSH_STRING(p); 303 cur = pp.in; 304 pp.in->flags |= IN_expand; 305 pptoken = pp.token; 306 n = 2 * MAXTOKEN; 307 pp.token = p = oldof(0, char, 0, n); 308 m = p + MAXTOKEN; 309 for (;;) 310 { 311 if (pplex()) 312 { 313 if ((pp.token = pp.toknxt) > m) 314 { 315 c = pp.token - p; 316 p = newof(p, char, n += MAXTOKEN, 0); 317 m = p + n - MAXTOKEN; 318 pp.token = p + c; 319 } 320 if (pp.mode & MARKMACRO) 321 { 322 pp.mode &= ~MARKMACRO; 323 *pp.token++ = MARK; 324 *pp.token++ = 'X'; 325 } 326 } 327 else if (pp.in == cur) 328 break; 329 } 330 *pp.token = 0; 331 if (ppmactop) 332 pp.macp->next = nextmacp; 333 debug((-7, "after expand: %s", p)); 334 pp.token = pptoken; 335 pp.state |= restore; 336 pp.in = pp.in->prev; 337 return p; 338 } 339 340 #if CHECKPOINT 341 342 #define LOAD_FUNCTION (1<<0) 343 #define LOAD_MULTILINE (1<<1) 344 #define LOAD_NOEXPAND (1<<2) 345 #define LOAD_PREDICATE (1<<3) 346 #define LOAD_READONLY (1<<4) 347 #define LOAD_VARIADIC (1<<5) 348 349 /* 350 * macro definition dump 351 */ 352 353 static int 354 dump(const char* name, char* v, void* handle) 355 { 356 register struct ppmacro* mac; 357 register struct ppsymbol* sym = (struct ppsymbol*)v; 358 register int flags; 359 360 NoP(name); 361 NoP(handle); 362 if ((mac = sym->macro) && !(sym->flags & (SYM_BUILTIN|SYM_PREDEFINED))) 363 { 364 ppprintf("%s", sym->name); 365 ppputchar(0); 366 flags = 0; 367 if (sym->flags & SYM_FUNCTION) flags |= LOAD_FUNCTION; 368 if (sym->flags & SYM_MULTILINE) flags |= LOAD_MULTILINE; 369 if (sym->flags & SYM_NOEXPAND) flags |= LOAD_NOEXPAND; 370 if (sym->flags & SYM_PREDICATE) flags |= LOAD_PREDICATE; 371 if (sym->flags & SYM_READONLY) flags |= LOAD_READONLY; 372 if (sym->flags & SYM_VARIADIC) flags |= LOAD_VARIADIC; 373 ppputchar(flags); 374 if (sym->flags & SYM_FUNCTION) 375 { 376 ppprintf("%d", mac->arity); 377 ppputchar(0); 378 if (mac->arity) 379 { 380 ppprintf("%s", mac->formals); 381 ppputchar(0); 382 } 383 } 384 ppprintf("%s", mac->value); 385 ppputchar(0); 386 } 387 return(0); 388 } 389 390 /* 391 * dump macro definitions for quick loading via ppload() 392 */ 393 394 void 395 ppdump(void) 396 { 397 register struct ppindex* ip; 398 unsigned long macro_offset; 399 unsigned long index_offset; 400 401 /* 402 * NOTE: we assume '\0' does not occur in valid preprocessed output 403 */ 404 405 ppputchar(0); 406 407 /* 408 * output global flags 409 */ 410 411 macro_offset = ppoffset(); 412 ppputchar(0); 413 414 /* 415 * output macro definitions 416 */ 417 418 hashwalk(pp.symtab, 0, dump, NiL); 419 ppputchar(0); 420 421 /* 422 * output include file index 423 */ 424 425 index_offset = ppoffset(); 426 ip = pp.firstindex; 427 while (ip) 428 { 429 ppprintf("%s", ip->file->name); 430 ppputchar(0); 431 if (ip->file->guard != INC_CLEAR && ip->file->guard != INC_IGNORE && ip->file->guard != INC_TEST) 432 ppprintf("%s", ip->file->guard->name); 433 ppputchar(0); 434 ppprintf("%lu", ip->begin); 435 ppputchar(0); 436 ppprintf("%lu", ip->end); 437 ppputchar(0); 438 ip = ip->next; 439 } 440 ppputchar(0); 441 442 /* 443 * output offset directory 444 */ 445 446 ppprintf("%010lu", macro_offset); 447 ppputchar(0); 448 ppprintf("%010lu", index_offset); 449 ppputchar(0); 450 ppflushout(); 451 } 452 453 /* 454 * load text and macro definitions from a previous ppdump() 455 * s is the string argument from the pragma (including quotes) 456 */ 457 458 void 459 ppload(register char* s) 460 { 461 register char* b; 462 register Sfio_t* sp; 463 int m; 464 char* g; 465 char* t; 466 unsigned long n; 467 unsigned long p; 468 unsigned long macro_offset; 469 unsigned long index_offset; 470 unsigned long file_offset; 471 unsigned long file_size; 472 unsigned long keep_begin; 473 unsigned long keep_end; 474 unsigned long skip_end; 475 unsigned long next_begin; 476 unsigned long next_end; 477 struct ppfile* fp; 478 struct ppsymbol* sym; 479 struct ppmacro* mac; 480 481 char* ip = 0; 482 483 pp.mode |= LOADING; 484 if (!(pp.state & STANDALONE)) 485 error(3, "checkpoint load in standalone mode only"); 486 #if ARCHIVE 487 if (pp.member) 488 { 489 sp = pp.member->archive->info.sp; 490 file_offset = pp.member->offset; 491 file_size = pp.member->size; 492 if (sfseek(sp, file_offset + 22, SEEK_SET) != file_offset + 22 || !(s = sfgetr(sp, '\n', 1))) 493 error(3, "checkpoint magic error"); 494 } 495 else 496 #endif 497 { 498 if (pp.in->type != IN_FILE) 499 error(3, "checkpoint load from files only"); 500 if (pp.in->flags & IN_prototype) 501 pp.in->fd = pppdrop(pp.in->buffer + PPBAKSIZ); 502 file_offset = 0; 503 if (pp.in->fd >= 0) 504 { 505 if (!(sp = sfnew(NiL, NiL, SF_UNBOUND, pp.in->fd, SF_READ))) 506 error(3, "checkpoint read error"); 507 file_size = sfseek(sp, 0L, SEEK_END); 508 } 509 else 510 { 511 file_size = pp.in->buflen; 512 if (!(sp = sfnew(NiL, pp.in->buffer + ((pp.in->flags & IN_static) ? 0 : PPBAKSIZ), file_size, -1, SF_READ|SF_STRING))) 513 error(3, "checkpoint read error"); 514 } 515 } 516 if (!streq(s, pp.checkpoint)) 517 error(3, "checkpoint version %s does not match %s", s, pp.checkpoint); 518 519 /* 520 * get the macro and index offsets 521 */ 522 523 p = file_offset + file_size - 22; 524 if ((n = sfseek(sp, p, SEEK_SET)) != p) 525 error(3, "checkpoint directory seek error"); 526 if (!(t = sfreserve(sp, 22, 0))) 527 error(3, "checkpoint directory read error"); 528 macro_offset = file_offset + strtol(t, &t, 10); 529 index_offset = file_offset + strtol(t + 1, NiL, 10); 530 531 /* 532 * read the include index 533 */ 534 535 if (sfseek(sp, index_offset, SEEK_SET) != index_offset) 536 error(3, "checkpoint index seek error"); 537 if (!(s = sfreserve(sp, n - index_offset, 0))) 538 error(3, "checkpoint index read error"); 539 if (sfset(sp, 0, 0) & SF_STRING) 540 b = s; 541 else if (!(b = ip = memdup(s, n - index_offset))) 542 error(3, "checkpoint index alloc error"); 543 544 /* 545 * loop on the index and copy the non-ignored chunks to the output 546 */ 547 548 ppcheckout(); 549 p = PPBUFSIZ - (pp.outp - pp.outbuf); 550 keep_begin = 0; 551 keep_end = 0; 552 skip_end = 0; 553 while (*b) 554 { 555 fp = ppsetfile(b); 556 while (*b++); 557 g = b; 558 while (*b++); 559 next_begin = strtol(b, &t, 10); 560 next_end = strtol(t + 1, &t, 10); 561 if (pp.test & 0x0200) error(2, "%s: %s p=%lu next=<%lu,%lu> keep=<%lu,%lu> skip=<-,%lu> guard=%s", keyname(X_CHECKPOINT), fp->name, p, next_begin, next_end, keep_begin, keep_end, skip_end, fp->guard == INC_CLEAR ? "[CLEAR]" : fp->guard == INC_TEST ? "[TEST]" : fp->guard == INC_IGNORE ? "[IGNORE]" : fp->guard->name); 562 b = t + 1; 563 if (next_begin >= skip_end) 564 { 565 if (!ppmultiple(fp, INC_TEST)) 566 { 567 if (pp.test & 0x0100) error(2, "%s: %s IGNORE", keyname(X_CHECKPOINT), fp->name); 568 if (!keep_begin && skip_end < next_begin) 569 keep_begin = skip_end; 570 if (keep_begin) 571 { 572 flush: 573 if (sfseek(sp, file_offset + keep_begin, SEEK_SET) != file_offset + keep_begin) 574 error(3, "checkpoint data seek error"); 575 n = next_begin - keep_begin; 576 if (pp.test & 0x0100) error(2, "%s: copy <%lu,%lu> n=%lu p=%lu", keyname(X_CHECKPOINT), keep_begin, next_begin - 1, n, p); 577 while (n > p) 578 { 579 if (sfread(sp, pp.outp, p) != p) 580 error(3, "checkpoint data read error"); 581 PPWRITE(PPBUFSIZ); 582 pp.outp = pp.outbuf; 583 n -= p; 584 p = PPBUFSIZ; 585 } 586 if (n) 587 { 588 if (sfread(sp, pp.outp, n) != n) 589 error(3, "checkpoint data read error"); 590 pp.outp += n; 591 p -= n; 592 } 593 keep_begin = 0; 594 if (keep_end <= next_end) 595 keep_end = 0; 596 } 597 skip_end = next_end; 598 } 599 else if (!keep_begin) 600 { 601 if (skip_end) 602 { 603 keep_begin = skip_end; 604 skip_end = 0; 605 } 606 else keep_begin = next_begin; 607 if (keep_end < next_end) 608 keep_end = next_end; 609 } 610 } 611 if (*g && fp->guard != INC_IGNORE) 612 fp->guard = ppsymset(pp.symtab, g); 613 } 614 if (keep_end) 615 { 616 if (!keep_begin) 617 keep_begin = skip_end > next_end ? skip_end : next_end; 618 next_begin = next_end = keep_end; 619 g = b; 620 goto flush; 621 } 622 if (pp.test & 0x0100) error(2, "%s: loop", keyname(X_CHECKPOINT)); 623 624 /* 625 * read the compacted definitions 626 */ 627 628 if (sfseek(sp, macro_offset, SEEK_SET) != macro_offset) 629 error(3, "checkpoint macro seek error"); 630 if (!(s = sfreserve(sp, index_offset - macro_offset, 0))) 631 error(3, "checkpoint macro read error"); 632 633 /* 634 * read the flags 635 */ 636 637 while (*s) 638 { 639 #if _options_dumped_ 640 if (streq(s, "OPTION")) /* ... */; 641 else 642 #endif 643 error(3, "%-.48s: unknown flags in checkpoint file", s); 644 } 645 s++; 646 647 /* 648 * unpack and enter the definitions 649 */ 650 651 while (*s) 652 { 653 b = s; 654 while (*s++); 655 m = *s++; 656 sym = ppsymset(pp.symtab, b); 657 if (sym->macro) 658 { 659 if (m & LOAD_FUNCTION) 660 { 661 if (*s++ != '0') 662 while (*s++); 663 while (*s++); 664 } 665 if (pp.test & 0x1000) error(2, "checkpoint SKIP %s=%s [%s]", sym->name, s, sym->macro->value); 666 while (*s++); 667 } 668 else 669 { 670 ppfsm(FSM_MACRO, b); 671 sym->flags = 0; 672 if (m & LOAD_FUNCTION) sym->flags |= SYM_FUNCTION; 673 if (m & LOAD_MULTILINE) sym->flags |= SYM_MULTILINE; 674 if (m & LOAD_NOEXPAND) sym->flags |= SYM_NOEXPAND; 675 if (m & LOAD_PREDICATE) sym->flags |= SYM_PREDICATE; 676 if (m & LOAD_READONLY) sym->flags |= SYM_READONLY; 677 if (m & LOAD_VARIADIC) sym->flags |= SYM_VARIADIC; 678 mac = sym->macro = newof(0, struct ppmacro, 1, 0); 679 if (sym->flags & SYM_FUNCTION) 680 { 681 for (n = 0; *s >= '0' && *s <= '9'; n = n * 10 + *s++ - '0'); 682 if (*s++) error(3, "%-.48: checkpoint macro arity botched", sym->name); 683 if (mac->arity = n) 684 { 685 b = s; 686 while (*s++); 687 mac->formals = (char*)memcpy(oldof(0, char, 0, s - b), b, s - b); 688 } 689 } 690 b = s; 691 while (*s++); 692 mac->size = s - b - 1; 693 mac->value = (char*)memcpy(oldof(0, char, 0, mac->size + 1), b, mac->size + 1); 694 if (pp.test & 0x1000) error(2, "checkpoint LOAD %s=%s", sym->name, mac->value); 695 } 696 } 697 698 /* 699 * we are now at EOF 700 */ 701 702 if (ip) 703 { 704 pp.in->fd = -1; 705 free(ip); 706 } 707 #if ARCHIVE 708 if (pp.member) pp.member = 0; 709 else 710 #endif 711 { 712 sfclose(sp); 713 pp.in->flags |= IN_eof|IN_newline; 714 pp.in->nextchr = pp.in->buffer + PPBAKSIZ; 715 *pp.in->nextchr++ = 0; 716 *pp.in->nextchr = 0; 717 } 718 pp.mode &= ~LOADING; 719 } 720 721 #endif 722