xref: /titanic_50/usr/src/lib/libpp/common/ppinput.c (revision 54925bf60766fbb4f1f2d7c843721406a7b7a3fb)
1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *           Copyright (c) 1986-2007 AT&T Knowledge Ventures            *
5 *                      and is licensed under the                       *
6 *                  Common Public License, Version 1.0                  *
7 *                      by AT&T Knowledge Ventures                      *
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