xref: /illumos-gate/usr/src/contrib/ast/src/lib/libpp/ppinput.c (revision b30d193948be5a7794d7ae3ba0ed9c2f72c88e0f)
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