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
42static char*
43native(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
91void
92pppush(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
271void
272ppinput(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
281char*
282ppexpand(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
353static int
354dump(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
394void
395ppdump(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
458void
459ppload(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);
561if (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			{
567if (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;
576if (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	}
622if (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			}
665if (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);
694if (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