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