1 /***********************************************************************
2 * *
3 * This software is part of the ast package *
4 * Copyright (c) 1986-2009 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*
native(register const char * s)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
pppush(register int t,register char * s,register char * p,int n)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
ppinput(char * b,char * f,int n)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*
ppexpand(register char * p)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
dump(const char * name,char * v,void * handle)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
ppdump(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
ppload(register char * s)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