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