xref: /titanic_50/usr/src/lib/libpp/common/ppcontrol.c (revision 7eea693d6b672899726e75993fddc4e95b52647f)
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 control directive support
26  */
27 
28 #include "pplib.h"
29 
30 #include <regex.h>
31 
32 #define TOKOP_DUP	(1<<0)
33 #define TOKOP_STRING	(1<<1)
34 #define TOKOP_UNSET	(1<<2)
35 
36 struct edit
37 {
38 	struct edit*	next;
39 	regex_t		re;
40 };
41 
42 struct map
43 {
44 	struct map*	next;
45 	regex_t		re;
46 	struct edit*	edit;
47 };
48 
49 #define RESTORE		(COLLECTING|CONDITIONAL|DEFINITION|DIRECTIVE|DISABLE|EOF2NL|HEADER|NOSPACE|NOVERTICAL|PASSEOF|STRIP)
50 
51 /*
52  * common predicate assertion operations
53  * op is DEFINE or UNDEF
54  */
55 
56 static void
57 assert(int op, char* pred, char* args)
58 {
59 	register struct pplist*		a;
60 	register struct ppsymbol*	sym;
61 	register struct pplist*		p;
62 	register struct pplist*		q;
63 
64 	if (!args) switch (op)
65 	{
66 	case DEFINE:
67 		goto mark;
68 	case UNDEF:
69 		a = 0;
70 		goto unmark;
71 	}
72 	if (a = (struct pplist*)hashget(pp.prdtab, pred))
73 	{
74 		p = 0;
75 		q = a;
76 		while (q)
77 		{
78 			if (streq(q->value, args))
79 			{
80 				if (op == DEFINE) return;
81 				q = q->next;
82 				if (p) p->next = q;
83 				else a = q;
84 			}
85 			else
86 			{
87 				p = q;
88 				q = q->next;
89 			}
90 		}
91 		if (op == UNDEF)
92 		{
93 		unmark:
94 			hashput(pp.prdtab, pred, a);
95 			if (sym = ppsymref(pp.symtab, pred))
96 				sym->flags &= ~SYM_PREDICATE;
97 			return;
98 		}
99 	}
100 	if (op == DEFINE)
101 	{
102 		p = newof(0, struct pplist, 1, 0);
103 		p->next = a;
104 		p->value = strdup(args);
105 		hashput(pp.prdtab, NiL, p);
106 	mark:
107 		if ((pp.state & COMPILE) && pp.truncate) return;
108 		if (sym = ppsymset(pp.symtab, pred))
109 			sym->flags |= SYM_PREDICATE;
110 	}
111 }
112 
113 /*
114  * tokenize string ppop()
115  *
116  *	op	PP_* op
117  *	name	option name
118  *	s	string of option values
119  *	n	option sense
120  *	flags	TOKOP_* flags
121  */
122 
123 static void
124 tokop(int op, char* name, register char* s, register int n, int flags)
125 {
126 	register int	c;
127 	register char*	t;
128 
129 	if (!(flags & TOKOP_UNSET) && !n) error(2, "%s: option cannot be unset", name);
130 	else if (!s) ppop(op, s, n);
131 	else if (flags & TOKOP_STRING)
132 	{
133 		PUSH_LINE(s);
134 		for (;;)
135 		{
136 			pp.state &= ~NOSPACE;
137 			c = pplex();
138 			pp.state |= NOSPACE;
139 			if (!c) break;
140 			if (c != ' ')
141 				ppop(op, (flags & TOKOP_DUP) ? strdup(pp.token) : pp.token, n);
142 		}
143 		POP_LINE();
144 	}
145 	else do
146 	{
147 		while (*s == ' ') s++;
148 		for (t = s; *t && *t != ' '; t++);
149 		if (*t) *t++ = 0;
150 		else t = 0;
151 		if (*s) ppop(op, (flags & TOKOP_DUP) ? strdup(s) : s, n);
152 	} while (s = t);
153 }
154 
155 /*
156  * return symbol pointer for next token macro (re)definition
157  */
158 
159 static struct ppsymbol*
160 macsym(int tok)
161 {
162 	register struct ppsymbol*	sym;
163 
164 	if (tok != T_ID)
165 	{
166 		error(2, "%s: invalid macro name", pptokstr(pp.token, 0));
167 		return 0;
168 	}
169 	sym = pprefmac(pp.token, REF_CREATE);
170 	if ((sym->flags & SYM_FINAL) && (pp.mode & HOSTED)) return 0;
171 	if (sym->flags & (SYM_ACTIVE|SYM_READONLY))
172 	{
173 		if (!(pp.option & ALLPOSSIBLE))
174 			error(2, "%s: macro is %s", sym->name, (sym->flags & SYM_READONLY) ? "readonly" : "active");
175 		return 0;
176 	}
177 	if (!sym->macro) sym->macro = newof(0, struct ppmacro, 1, 0);
178 	return sym;
179 }
180 
181 /*
182  * get one space canonical pplex() line, sans '\n', and place in p
183  * x is max+1 pos in p
184  * 0 returned if line too large
185  * otherwise end of p ('\0') returned
186  */
187 
188 static char*
189 getline(register char* p, char* x, int disable)
190 {
191 	register int	c;
192 	register char*	s;
193 	char*		b;
194 	long		restore;
195 
196 	restore = pp.state & (NOSPACE|STRIP);
197 	pp.state &= ~(NEWLINE|NOSPACE|STRIP);
198 	pp.state |= EOF2NL;
199 	b = p;
200 	while ((c = pplex()) != '\n')
201 	{
202 		if (disable)
203 		{
204 			if (c == ' ')
205 				/*ignore*/;
206 			else if (disable == 1)
207 				disable = (c == T_ID && streq(pp.token, pp.pass)) ? 2 : 0;
208 			else
209 			{
210 				disable = 0;
211 				if (c == ':')
212 					pp.state |= DISABLE;
213 			}
214 		}
215 		s = pp.token;
216 		while (*p = *s++)
217 			if (++p >= x)
218 			{
219 				p = 0;
220 				goto done;
221 			}
222 	}
223 	if (p > b && *(p - 1) == ' ')
224 		p--;
225 	if (p >= x)
226 		p = 0;
227 	else
228 		*p = 0;
229  done:
230 	pp.state &= ~(NOSPACE|STRIP);
231 	pp.state |= restore;
232 	return p;
233 }
234 
235 /*
236  * regex error handler
237  */
238 
239 void
240 regfatal(regex_t* p, int level, int code)
241 {
242 	char	buf[128];
243 
244 	regerror(code, p, buf, sizeof(buf));
245 	regfree(p);
246 	error(level, "regular expression: %s", buf);
247 }
248 
249 /*
250  * process a single directive line
251  */
252 
253 int
254 ppcontrol(void)
255 {
256 	register char*			p;
257 	register int			c;
258 	register int			n;
259 	register char*			s;
260 	register struct ppmacro*	mac;
261 	register struct ppsymbol*	sym;
262 	struct edit*			edit;
263 	struct map*			map;
264 	struct ppfile*			fp;
265 	int				o;
266 	int				directive;
267 	long				restore;
268 	struct pptuple*			rp;
269 	struct pptuple*			tp;
270 	char*				v;
271 	int				emitted;
272 
273 	union
274 	{
275 		struct map*		best;
276 		struct ppinstk*		inp;
277 		struct pplist*		list;
278 		char*			string;
279 		struct ppsymbol*	symbol;
280 		int			type;
281 		PPLINESYNC		linesync;
282 	}				var;
283 
284 	static char			__va_args__[] = "__VA_ARGS__";
285 	static int			i0;
286 	static int			i1;
287 	static int			i2;
288 	static int			i3;
289 	static int			i4;
290 
291 	static long			n1;
292 	static long			n2;
293 	static long			n3;
294 
295 	static char*			p0;
296 	static char*			p1;
297 	static char*			p2;
298 	static char*			p3;
299 	static char*			p4;
300 	static char*			p5;
301 	static char*			p6;
302 
303 	static struct ppmacro		old;
304 	static char*			formargs[MAXFORMALS];
305 #if MACKEYARGS
306 	static char*			formvals[MAXFORMALS];
307 #endif
308 
309 	emitted = 0;
310 	if (pp.state & SKIPCONTROL) pp.level--;
311 	restore = (pp.state & RESTORE)|NEWLINE;
312 	if (pp.state & PASSTHROUGH) restore |= DISABLE;
313 	else restore &= ~DISABLE;
314 	pp.state &= ~(NEWLINE|RESTORE|SKIPCONTROL);
315 	pp.state |= DIRECTIVE|DISABLE|EOF2NL|NOSPACE|NOVERTICAL;
316 #if COMPATIBLE
317 	if ((pp.state & (COMPATIBILITY|STRICT)) == COMPATIBILITY || (pp.mode & HOSTED)) pp.state &= ~NOVERTICAL;
318 #else
319 	if (pp.mode & HOSTED) pp.state &= ~NOVERTICAL;
320 #endif
321 	switch (c = pplex())
322 	{
323 	case T_DECIMAL:
324 	case T_OCTAL:
325 		if ((pp.state & STRICT) && !(pp.mode & (HOSTED|RELAX)))
326 			error(1, "# <line> [ \"<file>\" [ <type> ] ]: non-standard directive");
327 		directive = INCLUDE;
328 		goto linesync;
329 	case T_ID:
330 		switch (directive = (int)hashref(pp.dirtab, pp.token))
331 		{
332 		case ELIF:
333 		else_if:
334 			if ((pp.option & ALLPOSSIBLE) && !pp.in->prev->prev)
335 				goto eatdirective;
336 			if (pp.control <= pp.in->control)
337 			{
338 				error(2, "no matching #%s for #%s", dirname(IF), dirname(ELIF));
339 				goto eatdirective;
340 			}
341 			if (pp.control == (pp.in->control + 1)) pp.in->flags |= IN_noguard;
342 			if (*pp.control & HADELSE)
343 			{
344 				error(2, "invalid #%s after #%s", dirname(ELIF), dirname(ELSE));
345 				*pp.control |= SKIP;
346 				goto eatdirective;
347 			}
348 			if (*pp.control & KEPT)
349 			{
350 				*pp.control |= SKIP;
351 				goto eatdirective;
352 			}
353 			if (directive == IFDEF || directive == IFNDEF)
354 			{
355 				*pp.control &= ~SKIP;
356 				goto else_ifdef;
357 			}
358 		conditional:
359 			if (ppexpr(&i1))
360 			{
361 				*pp.control &= ~SKIP;
362 				*pp.control |= KEPT;
363 			}
364 			else *pp.control |= SKIP;
365 			c = (pp.state & NEWLINE) ? '\n' : ' ';
366 			goto eatdirective;
367 		case ELSE:
368 			if ((pp.option & ALLPOSSIBLE) && !pp.in->prev->prev)
369 				goto eatdirective;
370 			if ((pp.option & ELSEIF) && (c = pplex()) == T_ID && ((n = (int)hashref(pp.dirtab, pp.token)) == IF || n == IFDEF || n == IFNDEF))
371 			{
372 				error(1, "#%s %s is non-standard -- use #%s", dirname(directive), dirname(n), dirname(ELIF));
373 				directive = n;
374 				goto else_if;
375 			}
376 			if (pp.control <= pp.in->control) error(2, "no matching #%s for #%s", dirname(IF), dirname(ELSE));
377 			else
378 			{
379 				if (pp.control == (pp.in->control + 1)) pp.in->flags |= IN_noguard;
380 				if (!(*pp.control & KEPT))
381 				{
382 					*pp.control &= ~SKIP;
383 					*pp.control |= HADELSE|KEPT;
384 				}
385 				else
386 				{
387 					if (*pp.control & HADELSE) error(2, "more than one #%s for #%s", dirname(ELSE), dirname(IF));
388 					*pp.control |= HADELSE|SKIP;
389 				}
390 			}
391 			goto enddirective;
392 		case ENDIF:
393 			if ((pp.option & ALLPOSSIBLE) && !pp.in->prev->prev)
394 				goto eatdirective;
395 			if (pp.control <= pp.in->control) error(2, "no matching #%s for #%s", dirname(IF), dirname(ENDIF));
396 			else if (--pp.control == pp.in->control && pp.in->symbol)
397 			{
398 				if (pp.in->flags & IN_endguard) pp.in->flags |= IN_noguard;
399 				else
400 				{
401 					pp.in->flags &= ~IN_tokens;
402 					pp.in->flags |= IN_endguard;
403 				}
404 			}
405 			goto enddirective;
406 		case IF:
407 		case IFDEF:
408 		case IFNDEF:
409 			if ((pp.option & ALLPOSSIBLE) && !pp.in->prev->prev)
410 				goto eatdirective;
411 			pushcontrol();
412 			SETIFBLOCK(pp.control);
413 			if (*pp.control & SKIP)
414 			{
415 				*pp.control |= KEPT;
416 				goto eatdirective;
417 			}
418 			if (directive == IF) goto conditional;
419 		else_ifdef:
420 			if ((c = pplex()) == T_ID)
421 			{
422 				sym = pprefmac(pp.token, REF_IF);
423 				if (directive == IFNDEF && pp.control == pp.in->control + 1)
424 				{
425 					if (pp.in->flags & (IN_defguard|IN_endguard))
426 						pp.in->flags |= IN_noguard;
427 					else
428 					{
429 						pp.in->flags |= IN_defguard;
430 						if (!(pp.in->flags & IN_tokens))
431 							pp.in->symbol = sym ? sym : pprefmac(pp.token, REF_CREATE);
432 					}
433 				}
434 			}
435 			else
436 			{
437 				sym = 0;
438 				if (!(pp.mode & HOSTED))
439 					error(1, "%s: invalid macro name", pptokstr(pp.token, 0));
440 			}
441 			*pp.control |= ((sym != 0) == (directive == IFDEF)) ? KEPT : SKIP;
442 			goto enddirective;
443 		case INCLUDE:
444 			if (*pp.control & SKIP)
445 			{
446 				pp.state |= HEADER;
447 				c = pplex();
448 				pp.state &= ~HEADER;
449 				goto eatdirective;
450 			}
451 			pp.state &= ~DISABLE;
452 			pp.state |= HEADER|STRIP;
453 			switch (c = pplex())
454 			{
455 			case T_STRING:
456 				p = pp.token;
457 				do pp.token = pp.toknxt; while ((c = pplex()) == T_STRING);
458 				*pp.token = 0;
459 				pp.token = p;
460 				/*FALLTHROUGH*/
461 			case T_HEADER:
462 			header:
463 				if (!*pp.token)
464 				{
465 					error(2, "#%s: null file name", dirname(INCLUDE));
466 					break;
467 				}
468 				if (*pp.token == '/' && !(pp.mode & (HOSTED|RELAX)))
469 					error(1, "#%s: reference to %s is not portable", dirname(INCLUDE), pp.token);
470 				n = ppsearch(pp.token, c, SEARCH_INCLUDE);
471 				break;
472 			case '<':
473 				/*
474 				 * HEADEREXPAND|HEADEREXPANDALL gets us here
475 				 */
476 
477 				if (!(p = pp.hdrbuf) && !(p = pp.hdrbuf = newof(0, char, MAXTOKEN, 0)))
478 					error(3, "out of space");
479 				pp.state &= ~NOSPACE;
480 				while ((c = pplex()) && c != '>')
481 				{
482 					v = p + 1;
483 					STRCOPY(p, pp.token, s);
484 					if (p == v && *(p - 1) == ' ' && pp.in->type != IN_MACRO)
485 						p--;
486 				}
487 				pp.state |= NOSPACE;
488 				*p++ = 0;
489 				memcpy(pp.token, pp.hdrbuf, p - pp.hdrbuf);
490 				c = T_HEADER;
491 				goto header;
492 			default:
493 				error(2, "#%s: \"...\" or <...> argument expected", dirname(INCLUDE));
494 				goto eatdirective;
495 			}
496 			goto enddirective;
497 		case 0:
498 			{
499 				regmatch_t	match[10];
500 
501 				/*UNDENT*/
502 	p = pp.valbuf;
503 	*p++ = '#';
504 	STRCOPY(p, pp.token, s);
505 	p0 = p;
506 	pp.mode |= EXPOSE;
507 	pp.state |= HEADER;
508 	p6 = getline(p, &pp.valbuf[MAXTOKEN], 0);
509 	pp.state &= ~HEADER;
510 	pp.mode &= ~EXPOSE;
511 	if (!p6)
512 	{
513 		*p0 = 0;
514 		error(2, "%s: directive too long", pp.valbuf);
515 		c = 0;
516 		goto eatdirective;
517 	}
518 	p1 = p2 = p3 = p4 = 0;
519 	p5 = *p ? p + 1 : 0;
520  checkmap:
521 	i0 = *p0;
522 	p = pp.valbuf;
523 	var.best = 0;
524 	n = 0;
525 	for (map = (struct map*)pp.maps; map; map = map->next)
526 		if (!(i1 = regexec(&map->re, p, elementsof(match), match, 0)))
527 		{
528 			if ((c = match[0].rm_eo - match[0].rm_so) > n)
529 			{
530 				n = c;
531 				var.best = map;
532 			}
533 		}
534 		else if (i1 != REG_NOMATCH)
535 			regfatal(&map->re, 3, i1);
536 	c = '\n';
537 	if (map = var.best)
538 	{
539 		if ((pp.state & (STRICT|WARN)) && !(pp.mode & (HOSTED|RELAX)))
540 		{
541 			*p0 = 0;
542 			if (!(pp.state & WARN) || strcmp(p + 1, dirname(PRAGMA)))
543 				error(1, "%s: non-standard directive", p);
544 			*p0 = i0;
545 		}
546 		if (!(*pp.control & SKIP))
547 		{
548 			n = 0;
549 			for (edit = map->edit; edit; edit = edit->next)
550 				if (!(i0 = regexec(&edit->re, p, elementsof(match), match, 0)))
551 				{
552 					n++;
553 					if (i0 = regsubexec(&edit->re, p, elementsof(match), match))
554 						regfatal(&edit->re, 3, i0);
555 					p = edit->re.re_sub->re_buf;
556 					if (edit->re.re_sub->re_flags & REG_SUB_STOP)
557 						break;
558 				}
559 				else if (i0 != REG_NOMATCH)
560 					regfatal(&edit->re, 3, i0);
561 			if (n && *p)
562 			{
563 				p1 = s = oldof(0, char, 0, strlen(p) + 32);
564 				while (*s = *p++) s++;
565 				debug((-4, "map: %s", p1));
566 				*s++ = '\n';
567 				*s = 0;
568 				error_info.line++;
569 				PUSH_RESCAN(p1);
570 				error_info.line--;
571 				directive = LINE;
572 			}
573 		}
574 		goto donedirective;
575 	}
576 	if (directive != PRAGMA && (!(*pp.control & SKIP) || !(pp.mode & (HOSTED|RELAX))))
577 	{
578 		*p0 = 0;
579 		error(1, "%s: unknown directive", pptokstr(pp.valbuf, 0));
580 		*p0 = i0;
581 	}
582  pass:
583 	if (!(*pp.control & SKIP) && pp.pragma && !(pp.state & NOTEXT) && (directive == PRAGMA || !(pp.mode & INIT)))
584 	{
585 		*p0 = 0;
586 		if (p2) *p2 = 0;
587 		if (p4)
588 		{
589 			if (p4 == p5)
590 			{
591 				p5 = strcpy(pp.tmpbuf, p5);
592 				if (p = strchr(p5, MARK))
593 				{
594 					s = p;
595 					while (*p)
596 						if ((*s++ = *p++) == MARK && *p == MARK) p++;
597 					*s = 0;
598 				}
599 			}
600 			*p4 = 0;
601 		}
602 		if (p = (char*)memchr(pp.valbuf + 1, MARK, p6 - pp.valbuf - 1))
603 		{
604 			s = p;
605 			while (p < p6) switch (*s++ = *p++)
606 			{
607 			case 0:
608 				s = p;
609 				break;
610 			case MARK:
611 				p++;
612 				break;
613 			}
614 			*s = 0;
615 		}
616 		(*pp.pragma)(pp.valbuf + 1, p1, p3, p5, (pp.state & COMPILE) || (pp.mode & INIT) != 0);
617 		emitted = 1;
618 	}
619 	goto donedirective;
620 
621 				/*INDENT*/
622 			}
623 		}
624 		if (*pp.control & SKIP) goto eatdirective;
625 		switch (directive)
626 		{
627 #if MACDEF
628 		case ENDMAC:
629 			c = pplex();
630 			error(2, "no matching #%s for #%s", dirname(MACDEF), dirname(ENDMAC));
631 			goto enddirective;
632 #endif
633 #if MACDEF
634 		case MACDEF:
635 			if ((pp.state & STRICT) && !(pp.mode & (HOSTED|RELAX)))
636 				error(1, "#%s: non-standard directive", pp.token);
637 			/*FALLTHROUGH*/
638 #endif
639 		case DEFINE:
640 			n2 = error_info.line;
641 			if ((c = pplex()) == '#' && directive == DEFINE)
642 				goto assertion;
643 			if (c == '<')
644 			{
645 				n = 1;
646 				c = pplex();
647 			}
648 			else
649 				n = 0;
650 			if (!(sym = macsym(c)))
651 				goto eatdirective;
652 			if (pp.truncate)
653 				ppfsm(FSM_MACRO, pp.token);
654 			mac = sym->macro;
655 			if ((pp.option & ALLPOSSIBLE) && !pp.in->prev->prev && mac->value)
656 				goto eatdirective;
657 			if (n)
658 				goto tuple;
659 			old = *mac;
660 			i0 = sym->flags;
661 			sym->flags &= ~(SYM_BUILTIN|SYM_EMPTY|SYM_FINAL|SYM_FUNCTION|SYM_INIT|SYM_INITIAL|SYM_MULTILINE|SYM_NOEXPAND|SYM_PREDEFINED|SYM_REDEFINE|SYM_VARIADIC);
662 #if MACDEF
663 			if (directive == MACDEF)
664 				sym->flags |= SYM_MULTILINE;
665 #endif
666 			mac->arity = 0;
667 			mac->formals = 0;
668 			mac->value = 0;
669 			pp.state &= ~NOSPACE;
670 			pp.state |= DEFINITION|NOEXPAND;
671 			switch (c = pplex())
672 			{
673 			case '(':
674 				sym->flags |= SYM_FUNCTION;
675 				pp.state |= NOSPACE;
676 #if MACKEYARGS
677 				if (pp.option & KEYARGS)
678 				{
679 					n = 2 * MAXTOKEN;
680 					p = mac->formals = oldof(0, char, 0, n);
681 					if ((c = pplex()) == T_ID) for (;;)
682 					{
683 						if (mac->arity < MAXFORMALS)
684 						{
685 							if (mac->arity) p++;
686 							formargs[mac->arity] = p;
687 							STRAPP(p, pp.token, s);
688 							formvals[mac->arity++] = p1 = p;
689 							if (mac->arity == 1) *p++ = ' ';
690 							*p++ = ' ';
691 							*p = 0;
692 						}
693 						else error(2, "%s: formal argument %s ignored", sym->name, pp.token);
694 						switch (c = pplex())
695 						{
696 						case '=':
697 							c = pplex();
698 							break;
699 						case ',':
700 							break;
701 						default:
702 							goto endformals;
703 						}
704 						pp.state &= ~NOSPACE;
705 						p0 = 0;
706 						for (;;)
707 						{
708 							switch (c)
709 							{
710 							case '\n':
711 								goto endformals;
712 							case '(':
713 								p0++;
714 								break;
715 							case ')':
716 								if (!p0--)
717 								{
718 									if (p > formvals[mac->arity - 1] && *(p - 1) == ' ') *--p = 0;
719 									goto endformals;
720 								}
721 								break;
722 							case ',':
723 								if (!p0)
724 								{
725 									if (p > formvals[mac->arity - 1] && *(p - 1) == ' ') *--p = 0;
726 									goto nextformal;
727 								}
728 								break;
729 							case ' ':
730 								if (p > formvals[mac->arity - 1] && *(p - 1) == ' ') continue;
731 								break;
732 							}
733 							STRCOPY(p, pp.token, s);
734 							if (p > &mac->formals[n - MAXTOKEN] && (s = newof(mac->formals, char, n += MAXTOKEN, 0)) != mac->formals)
735 							{
736 								n1 = s - mac->formals;
737 								for (n = 0; n < mac->arity; n++)
738 								{
739 									formargs[n] += n1;
740 									formvals[n] += n1;
741 								}
742 								c = p - mac->formals;
743 								mac->formals = s;
744 								p = mac->formals + c;
745 							}
746 							c = pplex();
747 						}
748 					nextformal:
749 						pp.state |= NOSPACE;
750 						if ((c = pplex()) != T_ID)
751 						{
752 							c = ',';
753 							break;
754 						}
755 					}
756 				endformals: /*NOP*/;
757 				}
758 				else
759 #endif
760 				{
761 					p = mac->formals = oldof(0, char, 0, MAXFORMALS * (MAXID + 1));
762 					c = pplex();
763 #if COMPATIBLE
764 					if ((pp.state & COMPATIBILITY) && c == ',')
765 					{
766 						if ((pp.state & WARN) && !(pp.mode & HOSTED))
767 							error(1, "%s: macro formal argument expected", sym->name);
768 						while ((c = pplex()) == ',');
769 					}
770 #endif
771 					for (;;)
772 					{
773 						if (c == T_VARIADIC)
774 						{
775 							if (sym->flags & SYM_VARIADIC)
776 								error(2, "%s: %s: duplicate macro formal argument", sym->name, pp.token);
777 							sym->flags |= SYM_VARIADIC;
778 							v = __va_args__;
779 						}
780 						else if (c == T_ID)
781 						{
782 							v = pp.token;
783 							if (sym->flags & SYM_VARIADIC)
784 								error(2, "%s: %s: macro formal argument cannot follow ...", sym->name, v);
785 							else if (streq(v, __va_args__))
786 								error(2, "%s: %s: invalid macro formal argument", sym->name, v);
787 						}
788 						else
789 							break;
790 						if (mac->arity < MAXFORMALS)
791 						{
792 							for (n = 0; n < mac->arity; n++)
793 								if (streq(formargs[n], v))
794 									error(2, "%s: %s: duplicate macro formal argument", sym->name, v);
795 							formargs[mac->arity++] = p;
796 							STRAPP(p, v, s);
797 						}
798 						else
799 							error(2, "%s: %s: macro formal argument ignored", sym->name, v);
800 						if ((c = pplex()) == ',')
801 						{
802 							c = pplex();
803 #if COMPATIBLE
804 							if ((pp.state & COMPATIBILITY) && c == ',')
805 							{
806 								if ((pp.state & WARN) && !(pp.mode & HOSTED))
807 									error(1, "%s: macro formal argument expected", sym->name);
808 								while ((c = pplex()) == ',');
809 							}
810 #endif
811 						}
812 						else if (c != T_VARIADIC)
813 							break;
814 						else
815 						{
816 							if (sym->flags & SYM_VARIADIC)
817 								error(2, "%s: %s: duplicate macro formal argument", sym->name, pp.token);
818 							sym->flags |= SYM_VARIADIC;
819 							c = pplex();
820 							break;
821 						}
822 					}
823 					if (mac->arity && (s = newof(mac->formals, char, p - mac->formals, 0)) != mac->formals)
824 					{
825 						n1 = s - mac->formals;
826 						for (n = 0; n < mac->arity; n++)
827 							formargs[n] += n1;
828 						mac->formals = s;
829 					}
830 				}
831 				if (!mac->arity)
832 				{
833 					free(mac->formals);
834 					mac->formals = 0;
835 				}
836 				switch (c)
837 				{
838 				case ')':
839 #if MACKEYARGS
840 					pp.state |= NOEXPAND|NOSPACE;
841 #else
842 					pp.state |= NOEXPAND;
843 #endif
844 					c = pplex();
845 					break;
846 				default:
847 					error(2, "%s: invalid macro formal argument list", sym->name);
848 					if (mac->formals)
849 					{
850 						free(mac->formals);
851 						mac->formals = 0;
852 						mac->arity = 0;
853 					}
854 					free(mac);
855 					sym->macro = 0;
856 					goto eatdirective;
857 				}
858 				pp.state &= ~NOSPACE;
859 				break;
860 			case ' ':
861 			case '\t':
862 				c = pplex();
863 				break;
864 			}
865 			n = 2 * MAXTOKEN;
866 #if MACKEYARGS
867 			p1 = p;
868 #endif
869 			p = mac->value = oldof(0, char, 0, n);
870 			var.type = 0;
871 			n1 = 0;
872 #if MACDEF
873 			i2 = i3 = 0;
874 			n3 = pp.state;
875 #endif
876 			if ((pp.option & PLUSPLUS) && (pp.state & (COMPATIBILITY|TRANSITION)) != COMPATIBILITY)
877 				switch (c)
878 				{
879 				case '+':
880 				case '-':
881 				case '&':
882 				case '|':
883 				case '<':
884 				case '>':
885 				case ':':
886 				case '=':
887 					*p++ = ' ';
888 					break;
889 				}
890 			o = 0;
891 			for (;;)
892 			{
893 				switch (c)
894 				{
895 				case T_ID:
896 					for (c = 0; c < mac->arity; c++)
897 						if (streq(formargs[c], pp.token))
898 						{
899 #if COMPATIBLE
900 							if (!(pp.state & COMPATIBILITY))
901 #endif
902 							if (var.type != TOK_TOKCAT && p > mac->value && *(p - 1) != ' ' && !(pp.option & PRESERVE)) *p++ = ' ';
903 							*p++ = MARK;
904 #if COMPATIBLE
905 							if ((pp.state & (COMPATIBILITY|TRANSITION)) == COMPATIBILITY) *p++ = 'C';
906 							else
907 #endif
908 							*p++ = (n1 || var.type == TOK_TOKCAT) ? 'C' : 'A';
909 							*p++ = c + ARGOFFSET;
910 							var.type = TOK_FORMAL|TOK_ID;
911 							c = '>';
912 							goto checkvalue;
913 						}
914 					if (var.type == TOK_BUILTIN) switch ((int)hashget(pp.strtab, pp.token))
915 					{
916 					case V_DEFAULT:
917 					case V_EMPTY:
918 						sym->flags |= SYM_EMPTY;
919 						break;
920 					}
921 					else if (pp.hiding && (var.symbol = ppsymref(pp.symtab, pp.token)) && var.symbol->hidden)
922 					{
923 						for (var.inp = pp.in; var.inp->type != IN_FILE && var.inp->prev; var.inp = var.inp->prev);
924 						p += sfsprintf(p, MAXTOKEN, "_%d_%s_hIDe", var.inp->hide, pp.token);
925 						var.type = TOK_ID;
926 						goto checkvalue;
927 					}
928 					var.type = TOK_ID;
929 					break;
930 				case '#':
931 					var.type = 0;
932 #if MACDEF
933 					if (!(sym->flags & (SYM_FUNCTION|SYM_MULTILINE))) break;
934 #else
935 					if (!(sym->flags & SYM_FUNCTION)) break;
936 #endif
937 					pp.state |= NOSPACE;
938 					c = pplex();
939 					if (c == '@')
940 					{
941 						c = pplex();
942 						i4 = 'S';
943 					}
944 					else i4 = 'Q';
945 					pp.state &= ~NOSPACE;
946 					if (c != T_ID) c = mac->arity;
947 					else for (c = 0; c < mac->arity; c++)
948 						if (streq(formargs[c], pp.token))
949 							break;
950 					if (c >= mac->arity)
951 					{
952 #if MACDEF
953 						if (sym->flags & SYM_MULTILINE)
954 						{
955 							if (n3 & NEWLINE)
956 							{
957 								pp.state &= ~NOEXPAND;
958 								switch ((int)hashref(pp.dirtab, pp.token))
959 								{
960 								case ENDMAC:
961 									if (!i2--) goto gotdefinition;
962 									break;
963 								case INCLUDE:
964 									/* PARSE HEADER constant */
965 									break;
966 								case MACDEF:
967 									i2++;
968 									break;
969 								}
970 								*p++ = '#';
971 							}
972 						}
973 						else
974 #endif
975 #if COMPATIBLE
976 						if (pp.state & COMPATIBILITY) *p++ = '#';
977 						else
978 #endif
979 						error(2, "# must precede a formal parameter");
980 					}
981 					else
982 					{
983 						if (p > mac->value && ppisidig(*(p - 1)) && !(pp.option & PRESERVE)) *p++ = ' ';
984 						*p++ = MARK;
985 						*p++ = i4;
986 						*p++ = c + ARGOFFSET;
987 						goto checkvalue;
988 					}
989 					break;
990 				case T_TOKCAT:
991 					if (p <= mac->value) error(2, "%s lhs operand omitted", pp.token);
992 					else
993 					{
994 						if (*(p - 1) == ' ') p--;
995 						if (var.type == (TOK_FORMAL|TOK_ID)) *(p - 2) = 'C';
996 					}
997 					pp.state |= NOSPACE;
998 					c = pplex();
999 					pp.state &= ~NOSPACE;
1000 					if (c == '\n') error(2, "%s rhs operand omitted", pptokchr(T_TOKCAT));
1001 					var.type = TOK_TOKCAT;
1002 					continue;
1003 				case '(':
1004 					if (*pp.token == '#')
1005 					{
1006 						var.type = TOK_BUILTIN;
1007 						n1++;
1008 					}
1009 					else
1010 					{
1011 						var.type = 0;
1012 						if (n1) n1++;
1013 					}
1014 					break;
1015 				case ')':
1016 					var.type = 0;
1017 					if (n1) n1--;
1018 					break;
1019 				case T_STRING:
1020 				case T_CHARCONST:
1021 					pp.state &= ~NOEXPAND;
1022 					var.type = 0;
1023 					if (strchr(pp.token, MARK)) pp.state &= ~NOEXPAND;
1024 #if COMPATIBLE
1025 					/*UNDENT*/
1026 
1027 	if ((sym->flags & SYM_FUNCTION) && (pp.state & (COMPATIBILITY|TRANSITION)))
1028 	{
1029 		char*	v;
1030 
1031 		s = pp.token;
1032 		for (;;)
1033 		{
1034 			if (!*s) goto checkvalue;
1035 			if (ppisid(*s))
1036 			{
1037 				v = s;
1038 				while (ppisid(*++s));
1039 				i1 = *s;
1040 				*s = 0;
1041 				for (c = 0; c < mac->arity; c++)
1042 					if (streq(formargs[c], v))
1043 					{
1044 						*p++ = MARK;
1045 						*p++ = 'C';
1046 						*p++ = c + ARGOFFSET;
1047 						if (!(pp.mode & HOSTED) && (!(pp.state & COMPATIBILITY) || (pp.state & WARN))) switch (*pp.token)
1048 						{
1049 						case '"':
1050 							error(1, "use the # operator to \"...\" quote macro arguments");
1051 							break;
1052 						case '\'':
1053 							error(1, "macro arguments should be '...' quoted before substitution");
1054 							break;
1055 						}
1056 						goto quotearg;
1057 					}
1058 				STRCOPY2(p, v);
1059 			quotearg:
1060 				*s = i1;
1061 			}
1062 			else *p++ = *s++;
1063 		}
1064 	}
1065 					/*INDENT*/
1066 #endif
1067 					break;
1068 				case '\n':
1069 #if MACDEF
1070 					if (sym->flags & SYM_MULTILINE)
1071 					{
1072 						if (pp.state & EOF2NL)
1073 						{
1074 							error_info.line++;
1075 							pp.state |= HIDDEN;
1076 							pp.hidden++;
1077 							var.type = 0;
1078 							if (!i3++)
1079 								goto checkvalue;
1080 							break;
1081 						}
1082 						pp.state |= EOF2NL;
1083 						error(2, "%s: missing #%s", sym->name, dirname(ENDMAC));
1084 					}
1085 #endif
1086 					goto gotdefinition;
1087 				case 0:
1088 					c = '\n';
1089 					goto gotdefinition;
1090 #if COMPATIBLE
1091 				case ' ':
1092 					if (pp.state & COMPATIBILITY) var.type = 0;
1093 					if (pp.option & PRESERVE) break;
1094 					if (p > mac->value && *(p - 1) != ' ') *p++ = ' ';
1095 					goto checkvalue;
1096 				case '\t':
1097 					if (var.type & TOK_ID)
1098 					{
1099 						while ((c = pplex()) == '\t');
1100 						if (c == T_ID)
1101 						{
1102 							if (var.type == (TOK_FORMAL|TOK_ID)) *(p - 2) = 'C';
1103 							var.type = TOK_TOKCAT;
1104 							if (pp.state & WARN) error(1, "use the ## operator to concatenate macro arguments");
1105 						}
1106 						else var.type = 0;
1107 						continue;
1108 					}
1109 					var.type = 0;
1110 					if (pp.option & PRESERVE) break;
1111 					if (p > mac->value && *(p - 1) != ' ') *p++ = ' ';
1112 					goto checkvalue;
1113 #endif
1114 				case MARK:
1115 					pp.state &= ~NOEXPAND;
1116 					/*FALLTHROUGH*/
1117 
1118 				default:
1119 					var.type = 0;
1120 					break;
1121 				}
1122 				STRCOPY(p, pp.token, s);
1123 			checkvalue:
1124 				o = c;
1125 				if (p > &mac->value[n - MAXTOKEN] && (s = newof(mac->value, char, n += MAXTOKEN, 0)) != mac->value)
1126 				{
1127 					c = p - mac->value;
1128 					mac->value = s;
1129 					p = mac->value + c;
1130 				}
1131 #if MACDEF
1132 				n3 = pp.state;
1133 #endif
1134 				c = pplex();
1135 			}
1136 		gotdefinition:
1137 			while (p > mac->value && *(p - 1) == ' ') p--;
1138 			if (p > mac->value && (pp.option & PLUSPLUS) && (pp.state & (COMPATIBILITY|TRANSITION)) != COMPATIBILITY)
1139 				switch (o)
1140 				{
1141 				case '+':
1142 				case '-':
1143 				case '&':
1144 				case '|':
1145 				case '<':
1146 				case '>':
1147 				case ':':
1148 				case '=':
1149 					*p++ = ' ';
1150 					break;
1151 				}
1152 			*p = 0;
1153 #if MACKEYARGS
1154 			if (!mac->arity) /* ok */;
1155 			else if (pp.option & KEYARGS)
1156 			{
1157 				p0 = mac->formals;
1158 				mac->formkeys = newof(0, struct ppkeyarg, n, p1 - p0 + 1);
1159 				s = (char*)&mac->formkeys[mac->arity];
1160 				(void)memcpy(s, p0, p1 - p0 + 1);
1161 				free(p0);
1162 				for (n = 0; n < mac->arity; n++)
1163 				{
1164 					mac->formkeys[n].name = s + (formargs[n] - p0);
1165 					mac->formkeys[n].value = s + (formvals[n] - p0);
1166 				}
1167 			}
1168 			else
1169 #endif
1170 			for (n = 1; n < mac->arity; n++)
1171 				*(formargs[n] - 1) = ',';
1172 			if (old.value)
1173 			{
1174 				if ((i0 & SYM_FUNCTION) != (sym->flags & SYM_FUNCTION) || old.arity != mac->arity || !streq(old.value, mac->value)) goto redefined;
1175 				if (!old.formals)
1176 				{
1177 					if (mac->formals) goto redefined;
1178 				}
1179 				else if (mac->formals)
1180 				{
1181 #if MACKEYARGS
1182 					if (pp.option & KEYARGS)
1183 					{
1184 						for (n = 0; n < mac->arity; n++)
1185 							if (!streq(mac->formkeys[n].name, old.formkeys[n].name) || !streq(mac->formkeys[n].value, old.formkeys[n].value))
1186 								goto redefined;
1187 					}
1188 					else
1189 #endif
1190 					if (!streq(mac->formals, old.formals)) goto redefined;
1191 				}
1192 #if MACKEYARGS
1193 				if (pp.option & KEYARGS)
1194 				{
1195 					if (mac->formkeys) free(mac->formkeys);
1196 					mac->formkeys = old.formkeys;
1197 				}
1198 				else
1199 #endif
1200 				{
1201 					if (mac->formals) free(mac->formals);
1202 					mac->formals = old.formals;
1203 				}
1204 				free(mac->value);
1205 				mac->value = old.value;
1206 				goto benign;
1207 			redefined:
1208 				if (!(pp.mode & HOSTED) || !(i0 & SYM_INITIAL))
1209 					error(1, "%s redefined", sym->name);
1210 #if MACKEYARGS
1211 				if ((pp.option & KEYARGS) && mac->formkeys)
1212 					free(mac->formkeys);
1213 #endif
1214 #if MACKEYARGS
1215 				if (!(pp.option & KEYARGS))
1216 #endif
1217 				if (old.formals) free(old.formals);
1218 				free(old.value);
1219 			}
1220 			else if (!pp.truncate) ppfsm(FSM_MACRO, sym->name);
1221 			mac->value = newof(mac->value, char, (mac->size = p - mac->value) + 1, 0);
1222 			if ((pp.option & (DEFINITIONS|PREDEFINITIONS|REGUARD)) && !sym->hidden && !(sym->flags & SYM_MULTILINE) && ((pp.option & PREDEFINITIONS) || !(pp.mode & INIT)) && ((pp.option & (DEFINITIONS|PREDEFINITIONS)) || !(pp.state & NOTEXT)))
1223 			{
1224 				ppsync();
1225 				ppprintf("#%s %s", dirname(DEFINE), sym->name);
1226 				if (sym->flags & SYM_FUNCTION)
1227 				{
1228 					ppputchar('(');
1229 					if (mac->formals)
1230 						ppprintf("%s", mac->formals);
1231 					ppputchar(')');
1232 				}
1233 				if ((p = mac->value) && *p)
1234 				{
1235 					ppputchar(' ');
1236 					i0 = 0;
1237 					while (n = *p++)
1238 					{
1239 						if (n != MARK || (n = *p++) == MARK)
1240 						{
1241 							ppputchar(n);
1242 							i0 = ppisid(n);
1243 						}
1244 						else
1245 						{
1246 							if (n == 'Q')
1247 								ppputchar('#');
1248 							else if (i0)
1249 							{
1250 								ppputchar('#');
1251 								ppputchar('#');
1252 							}
1253 							s = formargs[*p++ - ARGOFFSET];
1254 							while ((n = *s++) && n != ',')
1255 								ppputchar(n);
1256 							if (ppisid(*p) || *p == MARK)
1257 							{
1258 								ppputchar('#');
1259 								ppputchar('#');
1260 							}
1261 							i0 = 0;
1262 						}
1263 						ppcheckout();
1264 					}
1265 				}
1266 				emitted = 1;
1267 			}
1268 		benign:
1269 			if (pp.mode & BUILTIN) sym->flags |= SYM_BUILTIN;
1270 			if (pp.option & FINAL) sym->flags |= SYM_FINAL;
1271 			if (pp.mode & INIT) sym->flags |= SYM_INIT;
1272 			if (pp.option & INITIAL) sym->flags |= SYM_INITIAL;
1273 			if (pp.state & NOEXPAND)  sym->flags |= SYM_NOEXPAND;
1274 			if (pp.option & PREDEFINED) sym->flags |= SYM_PREDEFINED;
1275 			if (pp.mode & READONLY) sym->flags |= SYM_READONLY;
1276 			if (pp.macref) (*pp.macref)(sym, error_info.file, n2, mac ? error_info.line - n2 + 1 : REF_UNDEF, mac ? strsum(mac->value, (long)mac->arity) : 0L);
1277 			break;
1278 		assertion:
1279 			c = pplex();
1280 			if ((pp.state & STRICT) && !(pp.mode & (HOSTED|RELAX)))
1281 				error(1, "#%s #%s: assertions are non-standard", dirname(directive), pptokstr(pp.token, 0));
1282 			if (c != T_ID)
1283 			{
1284 				error(2, "%s: invalid predicate name", pptokstr(pp.token, 0));
1285 				goto eatdirective;
1286 			}
1287 			switch ((int)hashref(pp.strtab, pp.token))
1288 			{
1289 			case X_DEFINED:
1290 			case X_EXISTS:
1291 			case X_STRCMP:
1292 				error(2, "%s is a builtin predicate", pp.token);
1293 				goto eatdirective;
1294 			case X_SIZEOF:
1295 				error(2, "%s cannot be a predicate", pp.token);
1296 				goto eatdirective;
1297 			}
1298 			strcpy(pp.tmpbuf, pp.token);
1299 			switch (pppredargs())
1300 			{
1301 			case T_ID:
1302 			case T_STRING:
1303 				assert(directive, pp.tmpbuf, pp.args);
1304 				break;
1305 			case 0:
1306 				assert(directive, pp.tmpbuf, NiL);
1307 				break;
1308 			default:
1309 				error(2, "invalid predicate argument list");
1310 				goto eatdirective;
1311 			}
1312 			break;
1313 		tuple:
1314 			pp.state |= DEFINITION|NOEXPAND|NOSPACE;
1315 			rp = 0;
1316 			tp = mac->tuple;
1317 			if (!tp && !mac->value)
1318 				ppfsm(FSM_MACRO, sym->name);
1319 			while ((c = pplex()) && c != '>' && c != '\n')
1320 			{
1321 				for (; tp; tp = tp->nomatch)
1322 					if (streq(tp->token, pp.token))
1323 						break;
1324 				if (!tp)
1325 				{
1326 					if (!(tp = newof(0, struct pptuple, 1, strlen(pp.token))))
1327 						error(3, "out of space");
1328 					strcpy(tp->token, pp.token);
1329 					if (rp)
1330 					{
1331 						tp->nomatch = rp;
1332 						rp->nomatch = tp;
1333 					}
1334 					else
1335 					{
1336 						tp->nomatch = mac->tuple;
1337 						mac->tuple = tp;
1338 					}
1339 				}
1340 				rp = tp;
1341 				tp = tp->match;
1342 			}
1343 			pp.state &= ~NOSPACE;
1344 			if (!rp || c != '>')
1345 				error(2, "%s: > omitted in tuple macro definition", sym->name);
1346 			else
1347 			{
1348 				n = 2 * MAXTOKEN;
1349 				p = v = oldof(0, char, 0, n);
1350 				while ((c = pplex()) && c != '\n')
1351 					if (p > v || c != ' ')
1352 					{
1353 						STRCOPY(p, pp.token, s);
1354 						if (p > &v[n - MAXTOKEN] && (s = newof(v, char, n += MAXTOKEN, 0)) != v)
1355 						{
1356 							c = p - v;
1357 							v = s;
1358 							p = v + c;
1359 						}
1360 					}
1361 				while (p > v && *(p - 1) == ' ')
1362 					p--;
1363 				n = p - v;
1364 				tp = newof(0, struct pptuple, 1, n);
1365 				strcpy(tp->token, v);
1366 				tp->match = rp->match;
1367 				rp->match = tp;
1368 			}
1369 			goto benign;
1370 		case WARNING:
1371 			if ((pp.state & STRICT) && !(pp.mode & (HOSTED|RELAX)))
1372 				error(1, "#%s: non-standard directive", pp.token);
1373 			/*FALLTHROUGH*/
1374 		case ERROR:
1375 			pp.state &= ~DISABLE;
1376 			p = pp.tmpbuf;
1377 			while ((c = pplex()) != '\n')
1378 				if (p + strlen(pp.token) < &pp.tmpbuf[MAXTOKEN])
1379 				{
1380 					STRCOPY(p, pp.token, s);
1381 					pp.state &= ~NOSPACE;
1382 				}
1383 			*p = 0;
1384 			p = *pp.tmpbuf ? pp.tmpbuf : ((directive == WARNING) ? "user warning" : "user error");
1385 			n = (directive == WARNING) ? 1 : 3;
1386 			error(n, "%s", p);
1387 			break;
1388 		case LET:
1389 			n2 = error_info.line;
1390 			if ((pp.state & STRICT) && !(pp.mode & (HOSTED|RELAX)))
1391 				error(1, "#%s: non-standard directive", pp.token);
1392 			if (!(sym = macsym(c = pplex()))) goto eatdirective;
1393 			if ((c = pplex()) != '=')
1394 			{
1395 				error(2, "%s: = expected", sym->name);
1396 				goto eatdirective;
1397 			}
1398 			sym->flags &= ~(SYM_BUILTIN|SYM_FUNCTION|SYM_MULTILINE|SYM_PREDEFINED|SYM_VARIADIC);
1399 			mac = sym->macro;
1400 			mac->arity = 0;
1401 			if (mac->value)
1402 			{
1403 				if (!(sym->flags & SYM_REDEFINE) && !sym->hidden)
1404 					error(1, "%s: redefined", sym->name);
1405 #if MACKEYARGS
1406 				if ((pp.option & KEYARGS) && mac->formkeys) free(mac->formkeys);
1407 				else
1408 #endif
1409 				free(mac->formals);
1410 				mac->formals = 0;
1411 				n = strlen(mac->value) + 1;
1412 			}
1413 			else
1414 			{
1415 				ppfsm(FSM_MACRO, sym->name);
1416 				n = 0;
1417 			}
1418 			n1 = ppexpr(&i1);
1419 			if (i1) c = sfsprintf(pp.tmpbuf, MAXTOKEN, "%luU", n1);
1420 			else c = sfsprintf(pp.tmpbuf, MAXTOKEN, "%ld", n1);
1421 			if (n < ++c)
1422 			{
1423 				if (mac->value) free(mac->value);
1424 				mac->value = oldof(0, char, 0, c);
1425 			}
1426 			strcpy(mac->value, pp.tmpbuf);
1427 			sym->flags |= SYM_REDEFINE;
1428 			c = (pp.state & NEWLINE) ? '\n' : ' ';
1429 			goto benign;
1430 		case LINE:
1431 			pp.state &= ~DISABLE;
1432 			if ((c = pplex()) == '#')
1433 			{
1434 				c = pplex();
1435 				directive = INCLUDE;
1436 			}
1437 			if (c != T_DECIMAL && c != T_OCTAL)
1438 			{
1439 				error(1, "#%s: line number expected", dirname(LINE));
1440 				goto eatdirective;
1441 			}
1442 		linesync:
1443 			n = error_info.line;
1444 			error_info.line = strtol(pp.token, NiL, 0);
1445 			if (error_info.line == 0 && directive == LINE && (pp.state & STRICT) && !(pp.mode & HOSTED))
1446 				error(1, "#%s: line number should be > 0", dirname(LINE));
1447 			pp.state &= ~DISABLE;
1448 			pp.state |= STRIP;
1449 			switch (c = pplex())
1450 			{
1451 			case T_STRING:
1452 				s = error_info.file;
1453 				if (*(p = pp.token)) pathcanon(p, 0);
1454 				fp = ppsetfile(p);
1455 				error_info.file = fp->name;
1456 				if (error_info.line == 1)
1457 					ppmultiple(fp, INC_TEST);
1458 				switch (c = pplex())
1459 				{
1460 				case '\n':
1461 					break;
1462 				case T_DECIMAL:
1463 				case T_OCTAL:
1464 					if (directive == LINE && (pp.state & STRICT) && !(pp.mode & (HOSTED|RELAX)))
1465 						error(1, "#%s: integer file type argument is non-standard", dirname(LINE));
1466 					break;
1467 				default:
1468 					error(1, "#%s: integer file type argument expected", dirname(LINE));
1469 					break;
1470 				}
1471 				if (directive == LINE) pp.in->flags &= ~IN_ignoreline;
1472 				else if (pp.incref)
1473 				{
1474 					if (error_info.file != s)
1475 					{
1476 						switch (*pp.token)
1477 						{
1478 						case PP_sync_push:
1479 							if (pp.insert) (*pp.incref)(s, error_info.file, n, PP_SYNC_INSERT);
1480 							else (*pp.incref)(s, error_info.file, n, PP_SYNC_PUSH);
1481 							break;
1482 						case PP_sync_pop:
1483 							if (pp.insert) (*pp.incref)(s, error_info.file, n, PP_SYNC_INSERT);
1484 							else (*pp.incref)(s, error_info.file, n - 1, PP_SYNC_POP);
1485 							break;
1486 						case PP_sync_ignore:
1487 							if (pp.insert) (*pp.incref)(s, error_info.file, n, PP_SYNC_INSERT);
1488 							else
1489 							{
1490 								(*pp.incref)(s, error_info.file, n, PP_SYNC_IGNORE);
1491 								error_info.file = s;
1492 							}
1493 							break;
1494 						default:
1495 							if (*s)
1496 							{
1497 								if (fp == pp.insert)
1498 									pp.insert = 0;
1499 								else if (error_info.line == 1 && !pp.insert)
1500 									(*pp.incref)(s, error_info.file, n, PP_SYNC_PUSH);
1501 								else
1502 								{
1503 									if (!pp.insert) pp.insert = ppgetfile(s);
1504 									(*pp.incref)(s, error_info.file, n, PP_SYNC_INSERT);
1505 								}
1506 							}
1507 							break;
1508 						}
1509 					}
1510 				}
1511 				break;
1512 			case '\n':
1513 				break;
1514 			default:
1515 				error(1, "#%s: \"file-name\" expected", dirname(LINE));
1516 				break;
1517 			}
1518 			if (directive == LINE && (pp.in->flags & IN_ignoreline))
1519 				error_info.line = n + 1;
1520 			else
1521 			{
1522 				pp.hidden = 0;
1523 				pp.state &= ~HIDDEN;
1524 				if (pp.linesync)
1525 				{
1526 #if CATSTRINGS
1527 					if (pp.state & JOINING) pp.state |= HIDDEN|SYNCLINE;
1528 					else
1529 #endif
1530 					{
1531 						s = pp.lineid;
1532 						n = pp.flags;
1533 						if (directive == LINE)
1534 						{
1535 							pp.flags &= ~PP_linetype;
1536 							if (pp.macref) pp.lineid = dirname(LINE);
1537 						}
1538 						(*pp.linesync)(error_info.line, error_info.file);
1539 						pp.flags = n;
1540 						pp.lineid = s;
1541 					}
1542 				}
1543 			}
1544 			directive = LINE;
1545 			break;
1546 		case PRAGMA:
1547 			/*
1548 			 * #pragma [STDC] [pass:] [no]option [arg ...]
1549 			 *
1550 			 * pragma args are not expanded by default
1551 			 *
1552 			 * if STDC is present then it is silently passed on
1553 			 *
1554 			 * if pass is pp.pass then the option is used
1555 			 * and verified but is not passed on
1556 			 *
1557 			 * if pass is omitted then the option is passed on
1558 			 *
1559 			 * otherwise if pass is non-null and not pp.pass then
1560 			 * the option is passed on but not used
1561 			 *
1562 			 * if the line does not match this form then
1563 			 * it is passed on unchanged
1564 			 *
1565 			 *	#directive   pass:  option  [...]
1566 			 *	^         ^  ^   ^  ^     ^  ^   ^
1567 			 *	pp.valbuf p0 p1  p2 p3    p4 p5  p6
1568 			 *
1569 			 * p?	0 if component omitted
1570 			 * i0	0 if ``no''option
1571 			 */
1572 
1573 			p = pp.valbuf;
1574 			*p++ = '#';
1575 			STRCOPY(p, pp.token, s);
1576 			p0 = p;
1577 			if (pp.option & PRAGMAEXPAND)
1578 				pp.state &= ~DISABLE;
1579 			if (!(p6 = getline(p, &pp.valbuf[MAXTOKEN], !!(pp.option & PRAGMAEXPAND))))
1580 			{
1581 				*p0 = 0;
1582 				error(2, "%s: directive too long", pp.valbuf);
1583 				c = 0;
1584 				goto eatdirective;
1585 			}
1586 			p1 = ++p;
1587 			while (ppisid(*p))
1588 				p++;
1589 			if (p == p1)
1590 			{
1591 				p5 = p;
1592 				p4 = 0;
1593 				p3 = 0;
1594 				p2 = 0;
1595 				p1 = 0;
1596 			}
1597 			else if (*p != ':')
1598 			{
1599 				p5 = *p ? p + (*p == ' ') : 0;
1600 				p4 = p;
1601 				p3 = p1;
1602 				p2 = 0;
1603 				p1 = 0;
1604 			}
1605 			else
1606 			{
1607 				p2 = p++;
1608 				p3 = p;
1609 				while (ppisid(*p))
1610 					p++;
1611 				if (p == p3)
1612 				{
1613 					p4 = p1;
1614 					p3 = 0;
1615 					p2 = 0;
1616 					p1 = 0;
1617 				}
1618 				else
1619 					p4 = p;
1620 				p5 = *p4 ? p4 + (*p4 == ' ') : 0;
1621 			}
1622 			if (!p1 && p3 && (p4 - p3) == 4 && strneq(p3, "STDC", 4))
1623 				goto pass;
1624 			if ((pp.state & WARN) && !(pp.mode & (HOSTED|RELAX)))
1625 				error(1, "#%s: non-standard directive", dirname(PRAGMA));
1626 			i0 = !p3 || *p3 != 'n' || *(p3 + 1) != 'o';
1627 			if (!p3)
1628 				goto checkmap;
1629 			if (p1)
1630 			{
1631 				*p2 = 0;
1632 				n = streq(p1, pp.pass);
1633 				*p2 = ':';
1634 				if (!n)
1635 					goto checkmap;
1636 			}
1637 			else
1638 				n = 0;
1639 			i2 = *p4;
1640 			*p4 = 0;
1641 			if (((i1 = (int)hashref(pp.strtab, p3 + (i0 ? 0 : 2))) < 1 || i1 > X_last_option) && (i0 || (i1 = (int)hashref(pp.strtab, p3)) > X_last_option))
1642 				i1 = 0;
1643 			if ((pp.state & (COMPATIBILITY|STRICT)) == STRICT && !(pp.mode & (HOSTED|RELAX)))
1644 			{
1645 				if (pp.optflags[i1] & OPT_GLOBAL)
1646 					goto donedirective;
1647 				if (n || (pp.mode & WARN))
1648 				{
1649 					n = 0;
1650 					error(1, "#%s: non-standard directive ignored", dirname(PRAGMA));
1651 				}
1652 				i1 = 0;
1653 			}
1654 			if (!n)
1655 			{
1656 				if (!(pp.optflags[i1] & OPT_GLOBAL))
1657 				{
1658 					*p4 = i2;
1659 					goto checkmap;
1660 				}
1661 				if (!(pp.optflags[i1] & OPT_PASS))
1662 					n = 1;
1663 			}
1664 			else if (!i1)
1665 				error(2, "%s: unknown option", p1);
1666 			else if ((pp.state & STRICT) && !(pp.mode & (HOSTED|RELAX)))
1667 				error(1, "%s: non-standard option", p1);
1668 			p = p5;
1669 			switch (i1)
1670 			{
1671 			case X_ALLMULTIPLE:
1672 				ppop(PP_MULTIPLE, i0);
1673 				break;
1674 			case X_ALLPOSSIBLE:
1675 				setoption(ALLPOSSIBLE, i0);
1676 				break;
1677 			case X_BUILTIN:
1678 				setmode(BUILTIN, i0);
1679 				break;
1680 			case X_CATLITERAL:
1681 				setmode(CATLITERAL, i0);
1682 				if (pp.mode & CATLITERAL)
1683 					setoption(STRINGSPLIT, 0);
1684 				break;
1685 			case X_CDIR:
1686 				tokop(PP_CDIR, p3, p, i0, TOKOP_UNSET|TOKOP_STRING|TOKOP_DUP);
1687 				break;
1688 			case X_CHECKPOINT:
1689 #if CHECKPOINT
1690 				ppload(p);
1691 #else
1692 				error(3, "%s: preprocessor not compiled with checkpoint enabled", p3);
1693 #endif
1694 				break;
1695 			case X_CHOP:
1696 				tokop(PP_CHOP, p3, p, i0, TOKOP_UNSET|TOKOP_STRING);
1697 				break;
1698 			case X_COMPATIBILITY:
1699 				ppop(PP_COMPATIBILITY, i0);
1700 				break;
1701 			case X_DEBUG:
1702 				error_info.trace = i0 ? (p ? -strtol(p, NiL, 0) : -1) : 0;
1703 				break;
1704 			case X_ELSEIF:
1705 				setoption(ELSEIF, i0);
1706 				break;
1707 			case X_EXTERNALIZE:
1708 				setmode(EXTERNALIZE, i0);
1709 				break;
1710 			case X_FINAL:
1711 				setoption(FINAL, i0);
1712 				break;
1713 			case X_HEADEREXPAND:
1714 				setoption(HEADEREXPAND, i0);
1715 				break;
1716 			case X_HEADEREXPANDALL:
1717 				setoption(HEADEREXPANDALL, i0);
1718 				break;
1719 			case X_HIDE:
1720 			case X_NOTE:
1721 				PUSH_LINE(p);
1722 				/* UNDENT...*/
1723 	while (c = pplex())
1724 	{
1725 		if (c != T_ID) error(1, "%s: %s: identifier expected", p3, pp.token);
1726 		else if (sym = ppsymset(pp.symtab, pp.token))
1727 		{
1728 			if (i1 == X_NOTE)
1729 			{
1730 				sym->flags &= ~SYM_NOTICED;
1731 				ppfsm(FSM_MACRO, sym->name);
1732 			}
1733 			else if (i0)
1734 			{
1735 				if (!sym->hidden && !(sym->hidden = newof(0, struct pphide, 1, 0)))
1736 					error(3, "out of space");
1737 				if (!sym->macro)
1738 					ppfsm(FSM_MACRO, sym->name);
1739 				if (!sym->hidden->level++)
1740 				{
1741 					pp.hiding++;
1742 					if (sym->macro && !(sym->flags & (SYM_ACTIVE|SYM_READONLY)))
1743 					{
1744 						sym->hidden->macro = sym->macro;
1745 						sym->macro = 0;
1746 						sym->hidden->flags = sym->flags;
1747 						sym->flags &= ~(SYM_BUILTIN|SYM_FUNCTION|SYM_INIT|SYM_MULTILINE|SYM_PREDEFINED|SYM_REDEFINE|SYM_VARIADIC);
1748 					}
1749 				}
1750 			}
1751 			else if (sym->hidden)
1752 			{
1753 				if ((mac = sym->macro) && !(sym->flags & (SYM_ACTIVE|SYM_READONLY)))
1754 				{
1755 					if (mac->formals) free(mac->formals);
1756 					free(mac->value);
1757 					free(mac);
1758 					sym->macro = 0;
1759 					sym->flags &= ~(SYM_BUILTIN|SYM_FUNCTION|SYM_INIT|SYM_MULTILINE|SYM_PREDEFINED|SYM_REDEFINE|SYM_VARIADIC);
1760 				}
1761 				if (!--sym->hidden->level)
1762 				{
1763 					pp.hiding--;
1764 					if (sym->hidden->macro)
1765 					{
1766 						sym->macro = sym->hidden->macro;
1767 						sym->flags = sym->hidden->flags;
1768 					}
1769 					free(sym->hidden);
1770 					sym->hidden = 0;
1771 				}
1772 			}
1773 		}
1774 	}
1775 				/*...INDENT*/
1776 				POP_LINE();
1777 				break;
1778 			case X_HOSTDIR:
1779 				tokop(PP_HOSTDIR, p3, p, i0, TOKOP_UNSET|TOKOP_STRING|TOKOP_DUP);
1780 				break;
1781 			case X_HOSTED:
1782 				setmode(HOSTED, i0);
1783 				break;
1784 			case X_HOSTEDTRANSITION:
1785 				setmode(HOSTEDTRANSITION, i0);
1786 				break;
1787 			case X_ID:
1788 				tokop(PP_ID, p3, p, i0, TOKOP_UNSET|TOKOP_STRING);
1789 				break;
1790 			case X_IGNORE:
1791 				tokop(PP_IGNORE, p3, p, i0, TOKOP_UNSET|TOKOP_STRING);
1792 				break;
1793 			case X_INCLUDE:
1794 				tokop(PP_INCLUDE, p3, p, i0, TOKOP_STRING|TOKOP_DUP);
1795 				break;
1796 			case X_INITIAL:
1797 				setoption(INITIAL, i0);
1798 				break;
1799 			case X_KEYARGS:
1800 				ppop(PP_KEYARGS, i0);
1801 				break;
1802 			case X_LINE:
1803 				if (pp.linesync) pp.olinesync = pp.linesync;
1804 				pp.linesync = i0 ? pp.olinesync : (PPLINESYNC)0;
1805 				break;
1806 			case X_LINEBASE:
1807 				ppop(PP_LINEBASE, i0);
1808 				break;
1809 			case X_LINEFILE:
1810 				ppop(PP_LINEFILE, i0);
1811 				break;
1812 			case X_LINEID:
1813 				ppop(PP_LINEID, i0 ? p : (char*)0);
1814 				break;
1815 			case X_LINETYPE:
1816 				ppop(PP_LINETYPE, i0 ? (p ? strtol(p, NiL, 0) : 1) : 0);
1817 				break;
1818 			case X_MACREF:
1819 				if (!p)
1820 				{
1821 					if (i0 && !pp.macref)
1822 					{
1823 						ppop(PP_LINETYPE, 1);
1824 						ppop(PP_MACREF, ppmacref);
1825 					}
1826 					else error(2, "%s: option cannot be unset", p3);
1827 				}
1828 				else if (s = strchr(p, ' '))
1829 				{
1830 					if (pp.macref && (s = strchr(p, ' ')))
1831 					{
1832 						*s++ = 0;
1833 						c = strtol(s, NiL, 0);
1834 						var.type = pp.truncate;
1835 						pp.truncate = PPTOKSIZ;
1836 						(*pp.macref)(pprefmac(p, REF_CREATE), error_info.file, error_info.line - (c == REF_NORMAL ? 2 : 1), c, (s = strchr(s, ' ')) ? strtol(s, NiL, 0) : 0L);
1837 						pp.truncate = var.type;
1838 					}
1839 					error_info.line -= 2;
1840 				}
1841 				break;
1842 			case X_MAP:
1843 				/*UNDENT*/
1844 	/*
1845 	 * #pragma pp:map [id ...] "/from/[,/to/]" [ "/old/new/[glnu]" ... ]
1846 	 */
1847 
1848 	if (!i0)
1849 	{
1850 		error(2, "%s: option cannot be unset", p3);
1851 		goto donedirective;
1852 	}
1853 	if (!p5)
1854 	{
1855 		error(2, "%s: address argument expected", p3);
1856 		goto donedirective;
1857 	}
1858 	PUSH_LINE(p5);
1859 	while ((c = pplex()) == T_ID)
1860 	{
1861 		sfsprintf(pp.tmpbuf, MAXTOKEN, "__%s__", s = pp.token);
1862 		if (c = (int)hashget(pp.dirtab, s))
1863 		{
1864 			hashput(pp.dirtab, 0, 0);
1865 			hashput(pp.dirtab, pp.tmpbuf, c);
1866 		}
1867 		if (c = (int)hashget(pp.strtab, s))
1868 		{
1869 			hashput(pp.strtab, 0, 0);
1870 			hashput(pp.strtab, pp.tmpbuf, c);
1871 		}
1872 	}
1873 	if (c != T_STRING || !*(s = pp.token))
1874 	{
1875 		if (c)
1876 			error(2, "%s: %s: address argument expected", p3, pptokstr(pp.token, 0));
1877 		goto eatmap;
1878 	}
1879 	map = newof(0, struct map, 1, 0);
1880 
1881 	/*
1882 	 * /from/
1883 	 */
1884 
1885 	if (i0 = regcomp(&map->re, s, REG_AUGMENTED|REG_DELIMITED|REG_LENIENT|REG_NULL))
1886 		regfatal(&map->re, 3, i0);
1887 	if (*(s += map->re.re_npat))
1888 	{
1889 		error(2, "%s: invalid characters after pattern: %s ", p3, s);
1890 		goto eatmap;
1891 	}
1892 
1893 	/*
1894 	 * /old/new/[flags]
1895 	 */
1896 
1897 	edit = 0;
1898 	while ((c = pplex()) == T_STRING)
1899 	{
1900 		if (!*(s = pp.token))
1901 		{
1902 			error(2, "%s: substitution argument expected", p3);
1903 			goto eatmap;
1904 		}
1905 		if (edit)
1906 			edit = edit->next = newof(0, struct edit, 1, 0);
1907 		else
1908 			edit = map->edit = newof(0, struct edit, 1, 0);
1909 		if (!(i0 = regcomp(&edit->re, s, REG_AUGMENTED|REG_DELIMITED|REG_LENIENT|REG_NULL)) && !(i0 = regsubcomp(&edit->re, s += edit->re.re_npat, NiL, 0, 0)))
1910 			s += edit->re.re_npat;
1911 		if (i0)
1912 			regfatal(&edit->re, 3, i0);
1913 		if (*s)
1914 		{
1915 			error(2, "%s: invalid characters after substitution: %s ", p3, s);
1916 			goto eatmap;
1917 		}
1918 	}
1919 	if (c)
1920 	{
1921 		error(2, "%s: %s: substitution argument expected", p3, pptokstr(pp.token, 0));
1922 		goto eatmap;
1923 	}
1924 	map->next = (struct map*)pp.maps;
1925 	pp.maps = (char*)map;
1926  eatmap:
1927 	POP_LINE();
1928 				/*INDENT*/
1929 				break;
1930 			case X_MAPINCLUDE:
1931 				ppmapinclude(NiL, p5);
1932 				break;
1933 			case X_MODERN:
1934 				setoption(MODERN, i0);
1935 				break;
1936 			case X_MULTIPLE:
1937 				n = 1;
1938 				if (pp.in->type == IN_FILE)
1939 					ppmultiple(ppsetfile(error_info.file), i0 ? INC_CLEAR : INC_TEST);
1940 				break;
1941 			case X_NATIVE:
1942 				setoption(NATIVE, i0);
1943 				break;
1944 			case X_OPSPACE:
1945 				ppfsm(FSM_OPSPACE, i0 ? p4 : (char*)0);
1946 				break;
1947 			case X_PASSTHROUGH:
1948 				ppop(PP_PASSTHROUGH, i0);
1949 				break;
1950 			case X_PEDANTIC:
1951 				ppop(PP_PEDANTIC, i0);
1952 				break;
1953 			case X_PLUSCOMMENT:
1954 				ppop(PP_PLUSCOMMENT, i0);
1955 				break;
1956 			case X_PLUSPLUS:
1957 				ppop(PP_PLUSPLUS, i0);
1958 				break;
1959 			case X_PLUSSPLICE:
1960 				setoption(PLUSSPLICE, i0);
1961 				break;
1962 			case X_PRAGMAEXPAND:
1963 				setoption(PRAGMAEXPAND, i0);
1964 				break;
1965 			case X_PRAGMAFLAGS:
1966 				tokop(PP_PRAGMAFLAGS, p3, p, i0, 0);
1967 				break;
1968 			case X_PREDEFINED:
1969 				setoption(PREDEFINED, i0);
1970 				break;
1971 			case X_PREFIX:
1972 				setoption(PREFIX, i0);
1973 				break;
1974 			case X_PRESERVE:
1975 				setoption(PRESERVE, i0);
1976 				if (pp.option & PRESERVE)
1977 				{
1978 					setmode(CATLITERAL, 0);
1979 					ppop(PP_COMPATIBILITY, 1);
1980 					ppop(PP_TRANSITION, 0);
1981 					ppop(PP_PLUSCOMMENT, 1);
1982 					ppop(PP_SPACEOUT, 1);
1983 					setoption(STRINGSPAN, 1);
1984 					setoption(STRINGSPLIT, 0);
1985 					ppop(PP_HOSTDIR, "-", 1);
1986 				}
1987 				break;
1988 			case X_PROTOTYPED:
1989 				/*
1990 				 * this option doesn't bump the token count
1991 				 */
1992 
1993 				n = 1;
1994 				directive = ENDIF;
1995 #if PROTOTYPE
1996 				setoption(PROTOTYPED, i0);
1997 #else
1998 				error(1, "preprocessor not compiled with prototype conversion enabled");
1999 #endif
2000 				break;
2001 			case X_PROTO:
2002 				setoption(NOPROTO, !i0);
2003 				break;
2004 			case X_QUOTE:
2005 				tokop(PP_QUOTE, p3, p, i0, TOKOP_UNSET|TOKOP_STRING);
2006 				break;
2007 			case X_READONLY:
2008 				setmode(READONLY, i0);
2009 				break;
2010 			case X_REGUARD:
2011 				setoption(REGUARD, i0);
2012 				break;
2013 			case X_RESERVED:
2014 				tokop(PP_RESERVED, p3, p, i0, 0);
2015 				break;
2016 			case X_SPACEOUT:
2017 				if (!(pp.state & (COMPATIBILITY|COMPILE)))
2018 					ppop(PP_SPACEOUT, i0);
2019 				break;
2020 			case X_SPLICECAT:
2021 				setoption(SPLICECAT, i0);
2022 				break;
2023 			case X_SPLICESPACE:
2024 				setoption(SPLICESPACE, i0);
2025 				break;
2026 			case X_STANDARD:
2027 				tokop(PP_STANDARD, p3, p, i0, TOKOP_UNSET|TOKOP_STRING|TOKOP_DUP);
2028 				break;
2029 			case X_STRICT:
2030 				ppop(PP_STRICT, i0);
2031 				break;
2032 			case X_STRINGSPAN:
2033 				setoption(STRINGSPAN, i0);
2034 				break;
2035 			case X_STRINGSPLIT:
2036 				setoption(STRINGSPLIT, i0);
2037 				if (pp.option & STRINGSPLIT)
2038 					setmode(CATLITERAL, 0);
2039 				break;
2040 			case X_SYSTEM_HEADER:
2041 				if (i0)
2042 				{
2043 					pp.mode |= HOSTED;
2044 					pp.flags |= PP_hosted;
2045 					pp.in->flags |= IN_hosted;
2046 				}
2047 				else
2048 				{
2049 					pp.mode &= ~HOSTED;
2050 					pp.flags &= ~PP_hosted;
2051 					pp.in->flags &= ~PP_hosted;
2052 				}
2053 				break;
2054 			case X_TEST:
2055 				ppop(PP_TEST, p);
2056 				break;
2057 			case X_TEXT:
2058 				if (!(pp.option & KEEPNOTEXT))
2059 					setstate(NOTEXT, !i0);
2060 				break;
2061 			case X_TRANSITION:
2062 				ppop(PP_TRANSITION, i0);
2063 				if (pp.state & TRANSITION) ppop(PP_COMPATIBILITY, i0);
2064 				break;
2065 			case X_TRUNCATE:
2066 				ppop(PP_TRUNCATE, i0 ? (p ? strtol(p, NiL, 0) : TRUNCLENGTH) : 0);
2067 				break;
2068 			case X_VENDOR:
2069 				tokop(PP_VENDOR, p3, p, i0, TOKOP_UNSET|TOKOP_STRING|TOKOP_DUP);
2070 				break;
2071 			case X_VERSION:
2072 				if (!(*pp.control & SKIP) && pp.pragma && !(pp.state & NOTEXT))
2073 				{
2074 					sfsprintf(pp.tmpbuf, MAXTOKEN, "\"%s\"", pp.version);
2075 					(*pp.pragma)(dirname(PRAGMA), pp.pass, p3, pp.tmpbuf, !n);
2076 					if (pp.linesync && !n)
2077 						(*pp.linesync)(error_info.line, error_info.file);
2078 					emitted = 1;
2079 				}
2080 				break;
2081 			case X_WARN:
2082 				ppop(PP_WARN, i0);
2083 				break;
2084 			case X_ZEOF:
2085 				setoption(ZEOF, i0);
2086 				break;
2087 #if DEBUG
2088 			case 0:
2089 			case X_INCLUDED:
2090 			case X_NOTICED:
2091 			case X_OPTION:
2092 			case X_STATEMENT:
2093 				break;
2094 			default:
2095 				error(PANIC, "%s: option recognized but not implemented", pp.valbuf);
2096 				break;
2097 #endif
2098 			}
2099 			*p4 = i2;
2100 			if (!n)
2101 				goto checkmap;
2102 			goto donedirective;
2103 		case RENAME:
2104 			if ((pp.state & STRICT) && !(pp.mode & (HOSTED|RELAX)))
2105 				error(1, "#%s: non-standard directive", pp.token);
2106 			if ((c = pplex()) != T_ID)
2107 			{
2108 				error(1, "%s: invalid macro name", pptokstr(pp.token, 0));
2109 				goto eatdirective;
2110 			}
2111 			if (!(sym = pprefmac(pp.token, REF_DELETE)) || !sym->macro)
2112 				goto eatdirective;
2113 			if (sym->flags & (SYM_ACTIVE|SYM_READONLY))
2114 			{
2115 				if (!(pp.option & ALLPOSSIBLE))
2116 					error(2, "%s: macro is %s", sym->name, (sym->flags & SYM_READONLY) ? "readonly" : "active");
2117 				goto eatdirective;
2118 			}
2119 			if ((c = pplex()) != T_ID)
2120 			{
2121 				error(1, "%s: invalid macro name", pptokstr(pp.token, 0));
2122 				goto eatdirective;
2123 			}
2124 			var.symbol = pprefmac(pp.token, REF_CREATE);
2125 			if (mac = var.symbol->macro)
2126 			{
2127 				if (var.symbol->flags & (SYM_ACTIVE|SYM_READONLY))
2128 				{
2129 					if (!(pp.option & ALLPOSSIBLE))
2130 						error(2, "%s: macro is %s", var.symbol->name, (var.symbol->flags & SYM_READONLY) ? "readonly" : "active");
2131 					goto eatdirective;
2132 				}
2133 				if (!(pp.mode & HOSTED) || !(var.symbol->flags & SYM_INITIAL))
2134 					error(1, "%s redefined", var.symbol->name);
2135 				if (mac->formals) free(mac->formals);
2136 				free(mac->value);
2137 				free(mac);
2138 			}
2139 			ppfsm(FSM_MACRO, var.symbol->name);
2140 			var.symbol->flags = sym->flags;
2141 			sym->flags &= ~(SYM_BUILTIN|SYM_FUNCTION|SYM_INIT|SYM_MULTILINE|SYM_PREDEFINED|SYM_REDEFINE|SYM_VARIADIC);
2142 			var.symbol->macro = sym->macro;
2143 			sym->macro = 0;
2144 			break;
2145 		case UNDEF:
2146 			if ((c = pplex()) != T_ID)
2147 			{
2148 				error(1, "%s: invalid macro name", pptokstr(pp.token, 0));
2149 				goto eatdirective;
2150 			}
2151 			if (sym = pprefmac(pp.token, REF_DELETE))
2152 			{
2153 				if (mac = sym->macro)
2154 				{
2155 					if (sym->flags & (SYM_ACTIVE|SYM_READONLY))
2156 					{
2157 						if (!(pp.option & ALLPOSSIBLE))
2158 							error(2, "%s: macro is %s", sym->name, (sym->flags & SYM_READONLY) ? "readonly" : "active");
2159 						goto eatdirective;
2160 					}
2161 					if (mac->formals) free(mac->formals);
2162 					free(mac->value);
2163 					free(mac);
2164 					mac = sym->macro = 0;
2165 				}
2166 				if ((pp.option & (DEFINITIONS|PREDEFINITIONS|REGUARD)) && !sym->hidden && !(sym->flags & SYM_MULTILINE) && ((pp.option & PREDEFINITIONS) || !(pp.mode & INIT)) && ((pp.option & (DEFINITIONS|PREDEFINITIONS)) || !(pp.state & NOTEXT)))
2167 				{
2168 					ppsync();
2169 					ppprintf("#%s %s", dirname(UNDEF), sym->name);
2170 					emitted = 1;
2171 				}
2172 				sym->flags &= ~(SYM_BUILTIN|SYM_FUNCTION|SYM_INIT|SYM_MULTILINE|SYM_PREDEFINED|SYM_REDEFINE|SYM_VARIADIC);
2173 				n2 = error_info.line;
2174 				goto benign;
2175 			}
2176 			else pprefmac(pp.token, REF_UNDEF);
2177 			break;
2178 #if DEBUG
2179 		default:
2180 			error(PANIC, "#%s: directive recognized but not implemented", pp.token);
2181 			goto eatdirective;
2182 #endif
2183 		}
2184 		break;
2185 	case '\n':
2186 		break;
2187 	default:
2188 		error(1, "%s: invalid directive name", pptokstr(pp.token, 0));
2189 		goto eatdirective;
2190 	}
2191  enddirective:
2192 #if COMPATIBLE
2193 	if (c != '\n' && !(pp.state & COMPATIBILITY))
2194 #else
2195 	if (c != '\n')
2196 #endif
2197 	{
2198 		pp.state |= DISABLE|NOSPACE;
2199 		if ((c = pplex()) != '\n' && (pp.mode & (HOSTED|PEDANTIC)) == PEDANTIC)
2200 			error(1, "%s: invalid characters after directive", pptokstr(pp.token, 0));
2201 	}
2202  eatdirective:
2203 	if (c != '\n')
2204 	{
2205 		pp.state |= DISABLE;
2206 		while (pplex() != '\n');
2207 	}
2208  donedirective:
2209 #if _HUH_2002_05_09
2210 	if (!(pp.state & EOF2NL))
2211 		error(2, "%s in directive", pptokchr(0));
2212 #endif
2213 	pp.state &= ~RESTORE;
2214 	pp.mode &= ~RELAX;
2215 	if (!(*pp.control & SKIP))
2216 	{
2217 		pp.state |= restore;
2218 		switch (directive)
2219 		{
2220 		case LINE:
2221 			return 0;
2222 		case INCLUDE:
2223 			if (pp.include)
2224 			{
2225 				error_info.line++;
2226 				PUSH_FILE(pp.include, n);
2227 				if (!pp.vendor && (pp.found->type & TYPE_VENDOR))
2228 					pp.vendor = 1;
2229 				pp.include = 0;
2230 				return 0;
2231 			}
2232 			if (pp.incref)
2233 				(*pp.incref)(error_info.file, ppgetfile(pp.path)->name, error_info.line, PP_SYNC_IGNORE);
2234 			else if (pp.linesync && pp.macref)
2235 			{
2236 				pp.flags |= PP_lineignore;
2237 				(*pp.linesync)(error_info.line, ppgetfile(pp.path)->name);
2238 			}
2239 			/*FALLTHROUGH*/
2240 		default:
2241 			pp.in->flags |= IN_tokens;
2242 			/*FALLTHROUGH*/
2243 		case ENDIF:
2244 			error_info.line++;
2245 			if (emitted)
2246 			{
2247 				ppputchar('\n');
2248 				ppcheckout();
2249 			}
2250 			else
2251 			{
2252 				pp.state |= HIDDEN;
2253 				pp.hidden++;
2254 			}
2255 			return 0;
2256 		}
2257 	}
2258 	pp.state |= restore|HIDDEN|SKIPCONTROL;
2259 	pp.hidden++;
2260 	pp.level++;
2261 	error_info.line++;
2262 	return 0;
2263 }
2264 
2265 /*
2266  * grow the pp nesting control stack
2267  */
2268 
2269 void
2270 ppnest(void)
2271 {
2272 	register struct ppinstk*	ip;
2273 	int				oz;
2274 	int				nz;
2275 	long				adjust;
2276 	long*				op;
2277 	long*				np;
2278 
2279 	oz = pp.constack;
2280 	op = pp.maxcon - oz + 1;
2281 	nz = oz * 2;
2282 	np = newof(op, long, nz, 0);
2283 	if (adjust = (np - op))
2284 	{
2285 		ip = pp.in;
2286 		do
2287 		{
2288 			if (ip->control)
2289 				ip->control += adjust;
2290 		} while (ip = ip->prev);
2291 	}
2292 	pp.control = np + oz;
2293 	pp.constack = nz;
2294 	pp.maxcon = np + nz - 1;
2295 }
2296