xref: /titanic_41/usr/src/lib/libpp/common/ppcontrol.c (revision 7d1e83948cb684521e72cab96020be241508f449)
1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 1986-2008 AT&T Intellectual Property          *
5 *                      and is licensed under the                       *
6 *                  Common Public License, Version 1.0                  *
7 *                    by AT&T Intellectual Property                     *
8 *                                                                      *
9 *                A copy of the License is available at                 *
10 *            http://www.opensource.org/licenses/cpl1.0.txt             *
11 *         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
12 *                                                                      *
13 *              Information and Software Systems Research               *
14 *                            AT&T Research                             *
15 *                           Florham Park NJ                            *
16 *                                                                      *
17 *                 Glenn Fowler <gsf@research.att.com>                  *
18 *                                                                      *
19 ***********************************************************************/
20 #pragma prototyped
21 /*
22  * Glenn Fowler
23  * AT&T Research
24  *
25  * preprocessor 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 							if ((pp.state & WARN) && !(pp.mode & (HOSTED|RELAX)) && var.type != TOK_TOKCAT && !(var.type & TOK_ID))
911 							{
912 								s = pp.in->nextchr;
913 								while ((c = *s++) && (c == ' ' || c == '\t'));
914 								if (c == '\n')
915 									c = 0;
916 								else if (c == '*' && *s == ')')
917 									c = ')';
918 								else if (c == '=' || ppisidig(c) || c == *s || *s == '=')
919 									c = 0;
920 								if (o != '.' && o != T_PTRMEM)
921 								{
922 									if ((var.type & TOK_ID) || o == ' ' || ppisseparate(o))
923 										o = 0;
924 									if (!((o == 0 || o == '(' || o == ')' || o == '[' || o == ']' || o == ',' || o == '|' || o == ';' || o == '{' || o == '}') && (c == '(' || c == ')' || c == '[' || c == ']' || c == ',' || c == '|' || c == ';' || c == '}' || c == 0)) && !(o == '*' && c == ')'))
925 										error(1, "%s: %s: formal should be parenthesized in macro value (t=%x o=%#c c=%#c)", sym->name, pp.token, var.type, o, c);
926 								}
927 							}
928 							var.type = TOK_FORMAL|TOK_ID;
929 							c = '>';
930 							goto checkvalue;
931 						}
932 					if (var.type == TOK_BUILTIN) switch ((int)hashget(pp.strtab, pp.token))
933 					{
934 					case V_DEFAULT:
935 					case V_EMPTY:
936 						sym->flags |= SYM_EMPTY;
937 						break;
938 					}
939 					else if (pp.hiding && (var.symbol = ppsymref(pp.symtab, pp.token)) && var.symbol->hidden)
940 					{
941 						for (var.inp = pp.in; var.inp->type != IN_FILE && var.inp->prev; var.inp = var.inp->prev);
942 						p += sfsprintf(p, MAXTOKEN, "_%d_%s_hIDe", var.inp->hide, pp.token);
943 						var.type = TOK_ID;
944 						goto checkvalue;
945 					}
946 					var.type = TOK_ID;
947 					break;
948 				case '#':
949 					var.type = 0;
950 #if MACDEF
951 					if (!(sym->flags & (SYM_FUNCTION|SYM_MULTILINE))) break;
952 #else
953 					if (!(sym->flags & SYM_FUNCTION)) break;
954 #endif
955 					pp.state |= NOSPACE;
956 					c = pplex();
957 					if (c == '@')
958 					{
959 						c = pplex();
960 						i4 = 'S';
961 					}
962 					else i4 = 'Q';
963 					pp.state &= ~NOSPACE;
964 					if (c != T_ID) c = mac->arity;
965 					else for (c = 0; c < mac->arity; c++)
966 						if (streq(formargs[c], pp.token))
967 							break;
968 					if (c >= mac->arity)
969 					{
970 #if MACDEF
971 						if (sym->flags & SYM_MULTILINE)
972 						{
973 							if (n3 & NEWLINE)
974 							{
975 								pp.state &= ~NOEXPAND;
976 								switch ((int)hashref(pp.dirtab, pp.token))
977 								{
978 								case ENDMAC:
979 									if (!i2--) goto gotdefinition;
980 									break;
981 								case INCLUDE:
982 									/* PARSE HEADER constant */
983 									break;
984 								case MACDEF:
985 									i2++;
986 									break;
987 								}
988 								*p++ = '#';
989 							}
990 						}
991 						else
992 #endif
993 #if COMPATIBLE
994 						if (pp.state & COMPATIBILITY) *p++ = '#';
995 						else
996 #endif
997 						error(2, "# must precede a formal parameter");
998 					}
999 					else
1000 					{
1001 						if (p > mac->value && ppisidig(*(p - 1)) && !(pp.option & PRESERVE)) *p++ = ' ';
1002 						*p++ = MARK;
1003 						*p++ = i4;
1004 						*p++ = c + ARGOFFSET;
1005 						goto checkvalue;
1006 					}
1007 					break;
1008 				case T_TOKCAT:
1009 					if (p <= mac->value) error(2, "%s lhs operand omitted", pp.token);
1010 					else
1011 					{
1012 						if (*(p - 1) == ' ') p--;
1013 						if (var.type == (TOK_FORMAL|TOK_ID)) *(p - 2) = 'C';
1014 					}
1015 					pp.state |= NOSPACE;
1016 					c = pplex();
1017 					pp.state &= ~NOSPACE;
1018 					if (c == '\n') error(2, "%s rhs operand omitted", pptokchr(T_TOKCAT));
1019 					var.type = TOK_TOKCAT;
1020 					continue;
1021 				case '(':
1022 					if (*pp.token == '#')
1023 					{
1024 						var.type = TOK_BUILTIN;
1025 						n1++;
1026 					}
1027 					else
1028 					{
1029 						var.type = 0;
1030 						if (n1) n1++;
1031 					}
1032 					break;
1033 				case ')':
1034 					var.type = 0;
1035 					if (n1) n1--;
1036 					break;
1037 				case T_STRING:
1038 				case T_CHARCONST:
1039 					pp.state &= ~NOEXPAND;
1040 					var.type = 0;
1041 					if (strchr(pp.token, MARK)) pp.state &= ~NOEXPAND;
1042 #if COMPATIBLE
1043 					/*UNDENT*/
1044 
1045 	if ((sym->flags & SYM_FUNCTION) && (pp.state & (COMPATIBILITY|TRANSITION)))
1046 	{
1047 		char*	v;
1048 
1049 		s = pp.token;
1050 		for (;;)
1051 		{
1052 			if (!*s) goto checkvalue;
1053 			if (ppisid(*s))
1054 			{
1055 				v = s;
1056 				while (ppisid(*++s));
1057 				i1 = *s;
1058 				*s = 0;
1059 				for (c = 0; c < mac->arity; c++)
1060 					if (streq(formargs[c], v))
1061 					{
1062 						*p++ = MARK;
1063 						*p++ = 'C';
1064 						*p++ = c + ARGOFFSET;
1065 						if (!(pp.mode & HOSTED) && (!(pp.state & COMPATIBILITY) || (pp.state & WARN))) switch (*pp.token)
1066 						{
1067 						case '"':
1068 							error(1, "use the # operator to \"...\" quote macro arguments");
1069 							break;
1070 						case '\'':
1071 							error(1, "macro arguments should be '...' quoted before substitution");
1072 							break;
1073 						}
1074 						goto quotearg;
1075 					}
1076 				STRCOPY2(p, v);
1077 			quotearg:
1078 				*s = i1;
1079 			}
1080 			else *p++ = *s++;
1081 		}
1082 	}
1083 					/*INDENT*/
1084 #endif
1085 					break;
1086 				case '\n':
1087 #if MACDEF
1088 					if (sym->flags & SYM_MULTILINE)
1089 					{
1090 						if (pp.state & EOF2NL)
1091 						{
1092 							error_info.line++;
1093 							pp.state |= HIDDEN;
1094 							pp.hidden++;
1095 							var.type = 0;
1096 							if (!i3++)
1097 								goto checkvalue;
1098 							break;
1099 						}
1100 						pp.state |= EOF2NL;
1101 						error(2, "%s: missing #%s", sym->name, dirname(ENDMAC));
1102 					}
1103 #endif
1104 					goto gotdefinition;
1105 				case 0:
1106 					c = '\n';
1107 					goto gotdefinition;
1108 #if COMPATIBLE
1109 				case ' ':
1110 					if (pp.state & COMPATIBILITY) var.type = 0;
1111 					if (pp.option & PRESERVE) break;
1112 					if (p > mac->value && *(p - 1) != ' ') *p++ = ' ';
1113 					goto checkvalue;
1114 				case '\t':
1115 					if (var.type & TOK_ID)
1116 					{
1117 						while ((c = pplex()) == '\t');
1118 						if (c == T_ID)
1119 						{
1120 							if (var.type == (TOK_FORMAL|TOK_ID)) *(p - 2) = 'C';
1121 							var.type = TOK_TOKCAT;
1122 							if (pp.state & WARN) error(1, "use the ## operator to concatenate macro arguments");
1123 						}
1124 						else var.type = 0;
1125 						continue;
1126 					}
1127 					var.type = 0;
1128 					if (pp.option & PRESERVE) break;
1129 					if (p > mac->value && *(p - 1) != ' ') *p++ = ' ';
1130 					goto checkvalue;
1131 #endif
1132 				case MARK:
1133 					pp.state &= ~NOEXPAND;
1134 					/*FALLTHROUGH*/
1135 
1136 				default:
1137 					var.type = 0;
1138 					break;
1139 				}
1140 				STRCOPY(p, pp.token, s);
1141 			checkvalue:
1142 				o = c;
1143 				if (p > &mac->value[n - MAXTOKEN] && (s = newof(mac->value, char, n += MAXTOKEN, 0)) != mac->value)
1144 				{
1145 					c = p - mac->value;
1146 					mac->value = s;
1147 					p = mac->value + c;
1148 				}
1149 #if MACDEF
1150 				n3 = pp.state;
1151 #endif
1152 				c = pplex();
1153 			}
1154 		gotdefinition:
1155 			while (p > mac->value && *(p - 1) == ' ') p--;
1156 			if (p > mac->value && (pp.option & PLUSPLUS) && (pp.state & (COMPATIBILITY|TRANSITION)) != COMPATIBILITY)
1157 				switch (o)
1158 				{
1159 				case '+':
1160 				case '-':
1161 				case '&':
1162 				case '|':
1163 				case '<':
1164 				case '>':
1165 				case ':':
1166 				case '=':
1167 					*p++ = ' ';
1168 					break;
1169 				}
1170 			*p = 0;
1171 #if MACKEYARGS
1172 			if (!mac->arity) /* ok */;
1173 			else if (pp.option & KEYARGS)
1174 			{
1175 				p0 = mac->formals;
1176 				mac->formkeys = newof(0, struct ppkeyarg, n, p1 - p0 + 1);
1177 				s = (char*)&mac->formkeys[mac->arity];
1178 				(void)memcpy(s, p0, p1 - p0 + 1);
1179 				free(p0);
1180 				for (n = 0; n < mac->arity; n++)
1181 				{
1182 					mac->formkeys[n].name = s + (formargs[n] - p0);
1183 					mac->formkeys[n].value = s + (formvals[n] - p0);
1184 				}
1185 			}
1186 			else
1187 #endif
1188 			for (n = 1; n < mac->arity; n++)
1189 				*(formargs[n] - 1) = ',';
1190 			if (old.value)
1191 			{
1192 				if ((i0 & SYM_FUNCTION) != (sym->flags & SYM_FUNCTION) || old.arity != mac->arity || !streq(old.value, mac->value)) goto redefined;
1193 				if (!old.formals)
1194 				{
1195 					if (mac->formals) goto redefined;
1196 				}
1197 				else if (mac->formals)
1198 				{
1199 #if MACKEYARGS
1200 					if (pp.option & KEYARGS)
1201 					{
1202 						for (n = 0; n < mac->arity; n++)
1203 							if (!streq(mac->formkeys[n].name, old.formkeys[n].name) || !streq(mac->formkeys[n].value, old.formkeys[n].value))
1204 								goto redefined;
1205 					}
1206 					else
1207 #endif
1208 					if (!streq(mac->formals, old.formals)) goto redefined;
1209 				}
1210 #if MACKEYARGS
1211 				if (pp.option & KEYARGS)
1212 				{
1213 					if (mac->formkeys) free(mac->formkeys);
1214 					mac->formkeys = old.formkeys;
1215 				}
1216 				else
1217 #endif
1218 				{
1219 					if (mac->formals) free(mac->formals);
1220 					mac->formals = old.formals;
1221 				}
1222 				free(mac->value);
1223 				mac->value = old.value;
1224 				goto benign;
1225 			redefined:
1226 				if (!(pp.mode & HOSTED) || !(i0 & SYM_INITIAL))
1227 					error(1, "%s redefined", sym->name);
1228 #if MACKEYARGS
1229 				if ((pp.option & KEYARGS) && mac->formkeys)
1230 					free(mac->formkeys);
1231 #endif
1232 #if MACKEYARGS
1233 				if (!(pp.option & KEYARGS))
1234 #endif
1235 				if (old.formals) free(old.formals);
1236 				free(old.value);
1237 			}
1238 			else if (!pp.truncate) ppfsm(FSM_MACRO, sym->name);
1239 			mac->value = newof(mac->value, char, (mac->size = p - mac->value) + 1, 0);
1240 			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)))
1241 			{
1242 				ppsync();
1243 				ppprintf("#%s %s", dirname(DEFINE), sym->name);
1244 				if (sym->flags & SYM_FUNCTION)
1245 				{
1246 					ppputchar('(');
1247 					if (mac->formals)
1248 						ppprintf("%s", mac->formals);
1249 					ppputchar(')');
1250 				}
1251 				if ((p = mac->value) && *p)
1252 				{
1253 					ppputchar(' ');
1254 					i0 = 0;
1255 					while (n = *p++)
1256 					{
1257 						if (n != MARK || (n = *p++) == MARK)
1258 						{
1259 							ppputchar(n);
1260 							i0 = ppisid(n);
1261 						}
1262 						else
1263 						{
1264 							if (n == 'Q')
1265 								ppputchar('#');
1266 							else if (i0)
1267 							{
1268 								ppputchar('#');
1269 								ppputchar('#');
1270 							}
1271 							s = formargs[*p++ - ARGOFFSET];
1272 							while ((n = *s++) && n != ',')
1273 								ppputchar(n);
1274 							if (ppisid(*p) || *p == MARK)
1275 							{
1276 								ppputchar('#');
1277 								ppputchar('#');
1278 							}
1279 							i0 = 0;
1280 						}
1281 						ppcheckout();
1282 					}
1283 				}
1284 				emitted = 1;
1285 			}
1286 		benign:
1287 			if (pp.mode & BUILTIN) sym->flags |= SYM_BUILTIN;
1288 			if (pp.option & FINAL) sym->flags |= SYM_FINAL;
1289 			if (pp.mode & INIT) sym->flags |= SYM_INIT;
1290 			if (pp.option & INITIAL) sym->flags |= SYM_INITIAL;
1291 			if (pp.state & NOEXPAND)  sym->flags |= SYM_NOEXPAND;
1292 			if (pp.option & PREDEFINED) sym->flags |= SYM_PREDEFINED;
1293 			if (pp.mode & READONLY) sym->flags |= SYM_READONLY;
1294 			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);
1295 			break;
1296 		assertion:
1297 			c = pplex();
1298 			if ((pp.state & STRICT) && !(pp.mode & (HOSTED|RELAX)))
1299 				error(1, "#%s #%s: assertions are non-standard", dirname(directive), pptokstr(pp.token, 0));
1300 			if (c != T_ID)
1301 			{
1302 				error(2, "%s: invalid predicate name", pptokstr(pp.token, 0));
1303 				goto eatdirective;
1304 			}
1305 			switch ((int)hashref(pp.strtab, pp.token))
1306 			{
1307 			case X_DEFINED:
1308 			case X_EXISTS:
1309 			case X_STRCMP:
1310 				error(2, "%s is a builtin predicate", pp.token);
1311 				goto eatdirective;
1312 			case X_SIZEOF:
1313 				error(2, "%s cannot be a predicate", pp.token);
1314 				goto eatdirective;
1315 			}
1316 			strcpy(pp.tmpbuf, pp.token);
1317 			switch (pppredargs())
1318 			{
1319 			case T_ID:
1320 			case T_STRING:
1321 				assert(directive, pp.tmpbuf, pp.args);
1322 				break;
1323 			case 0:
1324 				assert(directive, pp.tmpbuf, NiL);
1325 				break;
1326 			default:
1327 				error(2, "invalid predicate argument list");
1328 				goto eatdirective;
1329 			}
1330 			break;
1331 		tuple:
1332 			pp.state |= DEFINITION|NOEXPAND|NOSPACE;
1333 			rp = 0;
1334 			tp = mac->tuple;
1335 			if (!tp && !mac->value)
1336 				ppfsm(FSM_MACRO, sym->name);
1337 			while ((c = pplex()) && c != '>' && c != '\n')
1338 			{
1339 				for (; tp; tp = tp->nomatch)
1340 					if (streq(tp->token, pp.token))
1341 						break;
1342 				if (!tp)
1343 				{
1344 					if (!(tp = newof(0, struct pptuple, 1, strlen(pp.token))))
1345 						error(3, "out of space");
1346 					strcpy(tp->token, pp.token);
1347 					if (rp)
1348 					{
1349 						tp->nomatch = rp;
1350 						rp->nomatch = tp;
1351 					}
1352 					else
1353 					{
1354 						tp->nomatch = mac->tuple;
1355 						mac->tuple = tp;
1356 					}
1357 				}
1358 				rp = tp;
1359 				tp = tp->match;
1360 			}
1361 			pp.state &= ~NOSPACE;
1362 			if (!rp || c != '>')
1363 				error(2, "%s: > omitted in tuple macro definition", sym->name);
1364 			else
1365 			{
1366 				n = 2 * MAXTOKEN;
1367 				p = v = oldof(0, char, 0, n);
1368 				while ((c = pplex()) && c != '\n')
1369 					if (p > v || c != ' ')
1370 					{
1371 						STRCOPY(p, pp.token, s);
1372 						if (p > &v[n - MAXTOKEN] && (s = newof(v, char, n += MAXTOKEN, 0)) != v)
1373 						{
1374 							c = p - v;
1375 							v = s;
1376 							p = v + c;
1377 						}
1378 					}
1379 				while (p > v && *(p - 1) == ' ')
1380 					p--;
1381 				n = p - v;
1382 				tp = newof(0, struct pptuple, 1, n);
1383 				strcpy(tp->token, v);
1384 				tp->match = rp->match;
1385 				rp->match = tp;
1386 			}
1387 			goto benign;
1388 		case WARNING:
1389 			if ((pp.state & STRICT) && !(pp.mode & (HOSTED|RELAX)))
1390 				error(1, "#%s: non-standard directive", pp.token);
1391 			/*FALLTHROUGH*/
1392 		case ERROR:
1393 			pp.state &= ~DISABLE;
1394 			p = pp.tmpbuf;
1395 			while ((c = pplex()) != '\n')
1396 				if (p + strlen(pp.token) < &pp.tmpbuf[MAXTOKEN])
1397 				{
1398 					STRCOPY(p, pp.token, s);
1399 					pp.state &= ~NOSPACE;
1400 				}
1401 			*p = 0;
1402 			p = *pp.tmpbuf ? pp.tmpbuf : ((directive == WARNING) ? "user warning" : "user error");
1403 			n = (directive == WARNING) ? 1 : 3;
1404 			error(n, "%s", p);
1405 			break;
1406 		case LET:
1407 			n2 = error_info.line;
1408 			if ((pp.state & STRICT) && !(pp.mode & (HOSTED|RELAX)))
1409 				error(1, "#%s: non-standard directive", pp.token);
1410 			if (!(sym = macsym(c = pplex()))) goto eatdirective;
1411 			if ((c = pplex()) != '=')
1412 			{
1413 				error(2, "%s: = expected", sym->name);
1414 				goto eatdirective;
1415 			}
1416 			sym->flags &= ~(SYM_BUILTIN|SYM_FUNCTION|SYM_MULTILINE|SYM_PREDEFINED|SYM_VARIADIC);
1417 			mac = sym->macro;
1418 			mac->arity = 0;
1419 			if (mac->value)
1420 			{
1421 				if (!(sym->flags & SYM_REDEFINE) && !sym->hidden)
1422 					error(1, "%s: redefined", sym->name);
1423 #if MACKEYARGS
1424 				if ((pp.option & KEYARGS) && mac->formkeys) free(mac->formkeys);
1425 				else
1426 #endif
1427 				free(mac->formals);
1428 				mac->formals = 0;
1429 				n = strlen(mac->value) + 1;
1430 			}
1431 			else
1432 			{
1433 				ppfsm(FSM_MACRO, sym->name);
1434 				n = 0;
1435 			}
1436 			n1 = ppexpr(&i1);
1437 			if (i1) c = sfsprintf(pp.tmpbuf, MAXTOKEN, "%luU", n1);
1438 			else c = sfsprintf(pp.tmpbuf, MAXTOKEN, "%ld", n1);
1439 			if (n < ++c)
1440 			{
1441 				if (mac->value) free(mac->value);
1442 				mac->value = oldof(0, char, 0, c);
1443 			}
1444 			strcpy(mac->value, pp.tmpbuf);
1445 			sym->flags |= SYM_REDEFINE;
1446 			c = (pp.state & NEWLINE) ? '\n' : ' ';
1447 			goto benign;
1448 		case LINE:
1449 			pp.state &= ~DISABLE;
1450 			if ((c = pplex()) == '#')
1451 			{
1452 				c = pplex();
1453 				directive = INCLUDE;
1454 			}
1455 			if (c != T_DECIMAL && c != T_OCTAL)
1456 			{
1457 				error(1, "#%s: line number expected", dirname(LINE));
1458 				goto eatdirective;
1459 			}
1460 		linesync:
1461 			n = error_info.line;
1462 			error_info.line = strtol(pp.token, NiL, 0);
1463 			if (error_info.line == 0 && directive == LINE && (pp.state & STRICT) && !(pp.mode & HOSTED))
1464 				error(1, "#%s: line number should be > 0", dirname(LINE));
1465 			pp.state &= ~DISABLE;
1466 			pp.state |= STRIP;
1467 			switch (c = pplex())
1468 			{
1469 			case T_STRING:
1470 				s = error_info.file;
1471 				if (*(p = pp.token)) pathcanon(p, 0);
1472 				fp = ppsetfile(p);
1473 				error_info.file = fp->name;
1474 				if (error_info.line == 1)
1475 					ppmultiple(fp, INC_TEST);
1476 				switch (c = pplex())
1477 				{
1478 				case '\n':
1479 					break;
1480 				case T_DECIMAL:
1481 				case T_OCTAL:
1482 					if (directive == LINE && (pp.state & STRICT) && !(pp.mode & (HOSTED|RELAX)))
1483 						error(1, "#%s: integer file type argument is non-standard", dirname(LINE));
1484 					break;
1485 				default:
1486 					error(1, "#%s: integer file type argument expected", dirname(LINE));
1487 					break;
1488 				}
1489 				if (directive == LINE) pp.in->flags &= ~IN_ignoreline;
1490 				else if (pp.incref)
1491 				{
1492 					if (error_info.file != s)
1493 					{
1494 						switch (*pp.token)
1495 						{
1496 						case PP_sync_push:
1497 							if (pp.insert) (*pp.incref)(s, error_info.file, n, PP_SYNC_INSERT);
1498 							else (*pp.incref)(s, error_info.file, n, PP_SYNC_PUSH);
1499 							break;
1500 						case PP_sync_pop:
1501 							if (pp.insert) (*pp.incref)(s, error_info.file, n, PP_SYNC_INSERT);
1502 							else (*pp.incref)(s, error_info.file, n - 1, PP_SYNC_POP);
1503 							break;
1504 						case PP_sync_ignore:
1505 							if (pp.insert) (*pp.incref)(s, error_info.file, n, PP_SYNC_INSERT);
1506 							else
1507 							{
1508 								(*pp.incref)(s, error_info.file, n, PP_SYNC_IGNORE);
1509 								error_info.file = s;
1510 							}
1511 							break;
1512 						default:
1513 							if (*s)
1514 							{
1515 								if (fp == pp.insert)
1516 									pp.insert = 0;
1517 								else if (error_info.line == 1 && !pp.insert)
1518 									(*pp.incref)(s, error_info.file, n, PP_SYNC_PUSH);
1519 								else
1520 								{
1521 									if (!pp.insert) pp.insert = ppgetfile(s);
1522 									(*pp.incref)(s, error_info.file, n, PP_SYNC_INSERT);
1523 								}
1524 							}
1525 							break;
1526 						}
1527 					}
1528 				}
1529 				break;
1530 			case '\n':
1531 				break;
1532 			default:
1533 				error(1, "#%s: \"file-name\" expected", dirname(LINE));
1534 				break;
1535 			}
1536 			if (directive == LINE && (pp.in->flags & IN_ignoreline))
1537 				error_info.line = n + 1;
1538 			else
1539 			{
1540 				pp.hidden = 0;
1541 				pp.state &= ~HIDDEN;
1542 				if (pp.linesync)
1543 				{
1544 #if CATSTRINGS
1545 					if (pp.state & JOINING) pp.state |= HIDDEN|SYNCLINE;
1546 					else
1547 #endif
1548 					{
1549 						s = pp.lineid;
1550 						n = pp.flags;
1551 						if (directive == LINE)
1552 						{
1553 							pp.flags &= ~PP_linetype;
1554 							if (pp.macref) pp.lineid = dirname(LINE);
1555 						}
1556 						(*pp.linesync)(error_info.line, error_info.file);
1557 						pp.flags = n;
1558 						pp.lineid = s;
1559 					}
1560 				}
1561 			}
1562 			directive = LINE;
1563 			break;
1564 		case PRAGMA:
1565 			/*
1566 			 * #pragma [STDC] [pass:] [no]option [arg ...]
1567 			 *
1568 			 * pragma args are not expanded by default
1569 			 *
1570 			 * if STDC is present then it is silently passed on
1571 			 *
1572 			 * if pass is pp.pass then the option is used
1573 			 * and verified but is not passed on
1574 			 *
1575 			 * if pass is omitted then the option is passed on
1576 			 *
1577 			 * otherwise if pass is non-null and not pp.pass then
1578 			 * the option is passed on but not used
1579 			 *
1580 			 * if the line does not match this form then
1581 			 * it is passed on unchanged
1582 			 *
1583 			 *	#directive   pass:  option  [...]
1584 			 *	^         ^  ^   ^  ^     ^  ^   ^
1585 			 *	pp.valbuf p0 p1  p2 p3    p4 p5  p6
1586 			 *
1587 			 * p?	0 if component omitted
1588 			 * i0	0 if ``no''option
1589 			 */
1590 
1591 			p = pp.valbuf;
1592 			*p++ = '#';
1593 			STRCOPY(p, pp.token, s);
1594 			p0 = p;
1595 			if (pp.option & PRAGMAEXPAND)
1596 				pp.state &= ~DISABLE;
1597 			if (!(p6 = getline(p, &pp.valbuf[MAXTOKEN], !!(pp.option & PRAGMAEXPAND))))
1598 			{
1599 				*p0 = 0;
1600 				error(2, "%s: directive too long", pp.valbuf);
1601 				c = 0;
1602 				goto eatdirective;
1603 			}
1604 			p1 = ++p;
1605 			while (ppisid(*p))
1606 				p++;
1607 			if (p == p1)
1608 			{
1609 				p5 = p;
1610 				p4 = 0;
1611 				p3 = 0;
1612 				p2 = 0;
1613 				p1 = 0;
1614 			}
1615 			else if (*p != ':')
1616 			{
1617 				p5 = *p ? p + (*p == ' ') : 0;
1618 				p4 = p;
1619 				p3 = p1;
1620 				p2 = 0;
1621 				p1 = 0;
1622 			}
1623 			else
1624 			{
1625 				p2 = p++;
1626 				p3 = p;
1627 				while (ppisid(*p))
1628 					p++;
1629 				if (p == p3)
1630 				{
1631 					p4 = p1;
1632 					p3 = 0;
1633 					p2 = 0;
1634 					p1 = 0;
1635 				}
1636 				else
1637 					p4 = p;
1638 				p5 = *p4 ? p4 + (*p4 == ' ') : 0;
1639 			}
1640 			if (!p1 && p3 && (p4 - p3) == 4 && strneq(p3, "STDC", 4))
1641 				goto pass;
1642 			if ((pp.state & WARN) && (pp.mode & (HOSTED|RELAX|PEDANTIC)) == PEDANTIC)
1643 				error(1, "#%s: non-standard directive", dirname(PRAGMA));
1644 			i0 = !p3 || *p3 != 'n' || *(p3 + 1) != 'o';
1645 			if (!p3)
1646 				goto checkmap;
1647 			if (p1)
1648 			{
1649 				*p2 = 0;
1650 				n = streq(p1, pp.pass);
1651 				*p2 = ':';
1652 				if (!n)
1653 					goto checkmap;
1654 			}
1655 			else
1656 				n = 0;
1657 			i2 = *p4;
1658 			*p4 = 0;
1659 			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))
1660 				i1 = 0;
1661 			if ((pp.state & (COMPATIBILITY|STRICT)) == STRICT && !(pp.mode & (HOSTED|RELAX)))
1662 			{
1663 				if (pp.optflags[i1] & OPT_GLOBAL)
1664 					goto donedirective;
1665 				if (n || (pp.mode & WARN))
1666 				{
1667 					n = 0;
1668 					error(1, "#%s: non-standard directive ignored", dirname(PRAGMA));
1669 				}
1670 				i1 = 0;
1671 			}
1672 			if (!n)
1673 			{
1674 				if (!(pp.optflags[i1] & OPT_GLOBAL))
1675 				{
1676 					*p4 = i2;
1677 					goto checkmap;
1678 				}
1679 				if (!(pp.optflags[i1] & OPT_PASS))
1680 					n = 1;
1681 			}
1682 			else if (!i1)
1683 				error(2, "%s: unknown option", p1);
1684 			else if ((pp.state & STRICT) && !(pp.mode & (HOSTED|RELAX)))
1685 				error(1, "%s: non-standard option", p1);
1686 			p = p5;
1687 			switch (i1)
1688 			{
1689 			case X_ALLMULTIPLE:
1690 				ppop(PP_MULTIPLE, i0);
1691 				break;
1692 			case X_ALLPOSSIBLE:
1693 				setoption(ALLPOSSIBLE, i0);
1694 				break;
1695 			case X_BUILTIN:
1696 				setmode(BUILTIN, i0);
1697 				break;
1698 			case X_CATLITERAL:
1699 				setmode(CATLITERAL, i0);
1700 				if (pp.mode & CATLITERAL)
1701 					setoption(STRINGSPLIT, 0);
1702 				break;
1703 			case X_CDIR:
1704 				tokop(PP_CDIR, p3, p, i0, TOKOP_UNSET|TOKOP_STRING|TOKOP_DUP);
1705 				break;
1706 			case X_CHECKPOINT:
1707 #if CHECKPOINT
1708 				ppload(p);
1709 #else
1710 				error(3, "%s: preprocessor not compiled with checkpoint enabled", p3);
1711 #endif
1712 				break;
1713 			case X_CHOP:
1714 				tokop(PP_CHOP, p3, p, i0, TOKOP_UNSET|TOKOP_STRING);
1715 				break;
1716 			case X_COMPATIBILITY:
1717 				ppop(PP_COMPATIBILITY, i0);
1718 				break;
1719 			case X_DEBUG:
1720 				error_info.trace = i0 ? (p ? -strtol(p, NiL, 0) : -1) : 0;
1721 				break;
1722 			case X_ELSEIF:
1723 				setoption(ELSEIF, i0);
1724 				break;
1725 			case X_EXTERNALIZE:
1726 				setmode(EXTERNALIZE, i0);
1727 				break;
1728 			case X_FINAL:
1729 				setoption(FINAL, i0);
1730 				break;
1731 			case X_HEADEREXPAND:
1732 				setoption(HEADEREXPAND, i0);
1733 				break;
1734 			case X_HEADEREXPANDALL:
1735 				setoption(HEADEREXPANDALL, i0);
1736 				break;
1737 			case X_HIDE:
1738 			case X_NOTE:
1739 				PUSH_LINE(p);
1740 				/* UNDENT...*/
1741 	while (c = pplex())
1742 	{
1743 		if (c != T_ID) error(1, "%s: %s: identifier expected", p3, pp.token);
1744 		else if (sym = ppsymset(pp.symtab, pp.token))
1745 		{
1746 			if (i1 == X_NOTE)
1747 			{
1748 				sym->flags &= ~SYM_NOTICED;
1749 				ppfsm(FSM_MACRO, sym->name);
1750 			}
1751 			else if (i0)
1752 			{
1753 				if (!sym->hidden && !(sym->hidden = newof(0, struct pphide, 1, 0)))
1754 					error(3, "out of space");
1755 				if (!sym->macro)
1756 					ppfsm(FSM_MACRO, sym->name);
1757 				if (!sym->hidden->level++)
1758 				{
1759 					pp.hiding++;
1760 					if (sym->macro && !(sym->flags & (SYM_ACTIVE|SYM_READONLY)))
1761 					{
1762 						sym->hidden->macro = sym->macro;
1763 						sym->macro = 0;
1764 						sym->hidden->flags = sym->flags;
1765 						sym->flags &= ~(SYM_BUILTIN|SYM_FUNCTION|SYM_INIT|SYM_MULTILINE|SYM_PREDEFINED|SYM_REDEFINE|SYM_VARIADIC);
1766 					}
1767 				}
1768 			}
1769 			else if (sym->hidden)
1770 			{
1771 				if ((mac = sym->macro) && !(sym->flags & (SYM_ACTIVE|SYM_READONLY)))
1772 				{
1773 					if (mac->formals) free(mac->formals);
1774 					free(mac->value);
1775 					free(mac);
1776 					sym->macro = 0;
1777 					sym->flags &= ~(SYM_BUILTIN|SYM_FUNCTION|SYM_INIT|SYM_MULTILINE|SYM_PREDEFINED|SYM_REDEFINE|SYM_VARIADIC);
1778 				}
1779 				if (!--sym->hidden->level)
1780 				{
1781 					pp.hiding--;
1782 					if (sym->hidden->macro)
1783 					{
1784 						sym->macro = sym->hidden->macro;
1785 						sym->flags = sym->hidden->flags;
1786 					}
1787 					free(sym->hidden);
1788 					sym->hidden = 0;
1789 				}
1790 			}
1791 		}
1792 	}
1793 				/*...INDENT*/
1794 				POP_LINE();
1795 				break;
1796 			case X_HOSTDIR:
1797 				tokop(PP_HOSTDIR, p3, p, i0, TOKOP_UNSET|TOKOP_STRING|TOKOP_DUP);
1798 				break;
1799 			case X_HOSTED:
1800 				setmode(HOSTED, i0);
1801 				break;
1802 			case X_HOSTEDTRANSITION:
1803 				setmode(HOSTEDTRANSITION, i0);
1804 				break;
1805 			case X_ID:
1806 				tokop(PP_ID, p3, p, i0, TOKOP_UNSET|TOKOP_STRING);
1807 				break;
1808 			case X_IGNORE:
1809 				tokop(PP_IGNORE, p3, p, i0, TOKOP_UNSET|TOKOP_STRING);
1810 				break;
1811 			case X_INCLUDE:
1812 				tokop(PP_INCLUDE, p3, p, i0, TOKOP_STRING|TOKOP_DUP);
1813 				break;
1814 			case X_INITIAL:
1815 				setoption(INITIAL, i0);
1816 				break;
1817 			case X_KEYARGS:
1818 				ppop(PP_KEYARGS, i0);
1819 				break;
1820 			case X_LINE:
1821 				if (pp.linesync) pp.olinesync = pp.linesync;
1822 				pp.linesync = i0 ? pp.olinesync : (PPLINESYNC)0;
1823 				break;
1824 			case X_LINEBASE:
1825 				ppop(PP_LINEBASE, i0);
1826 				break;
1827 			case X_LINEFILE:
1828 				ppop(PP_LINEFILE, i0);
1829 				break;
1830 			case X_LINEID:
1831 				ppop(PP_LINEID, i0 ? p : (char*)0);
1832 				break;
1833 			case X_LINETYPE:
1834 				ppop(PP_LINETYPE, i0 ? (p ? strtol(p, NiL, 0) : 1) : 0);
1835 				break;
1836 			case X_MACREF:
1837 				if (!p)
1838 				{
1839 					if (i0 && !pp.macref)
1840 					{
1841 						ppop(PP_LINETYPE, 1);
1842 						ppop(PP_MACREF, ppmacref);
1843 					}
1844 					else error(2, "%s: option cannot be unset", p3);
1845 				}
1846 				else if (s = strchr(p, ' '))
1847 				{
1848 					if (pp.macref && (s = strchr(p, ' ')))
1849 					{
1850 						*s++ = 0;
1851 						c = strtol(s, NiL, 0);
1852 						var.type = pp.truncate;
1853 						pp.truncate = PPTOKSIZ;
1854 						(*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);
1855 						pp.truncate = var.type;
1856 					}
1857 					error_info.line -= 2;
1858 				}
1859 				break;
1860 			case X_MAP:
1861 				/*UNDENT*/
1862 	/*
1863 	 * #pragma pp:map [id ...] "/from/[,/to/]" [ "/old/new/[glnu]" ... ]
1864 	 */
1865 
1866 	if (!i0)
1867 	{
1868 		error(2, "%s: option cannot be unset", p3);
1869 		goto donedirective;
1870 	}
1871 	if (!p5)
1872 	{
1873 		error(2, "%s: address argument expected", p3);
1874 		goto donedirective;
1875 	}
1876 	PUSH_LINE(p5);
1877 	while ((c = pplex()) == T_ID)
1878 	{
1879 		sfsprintf(pp.tmpbuf, MAXTOKEN, "__%s__", s = pp.token);
1880 		if (c = (int)hashget(pp.dirtab, s))
1881 		{
1882 			hashput(pp.dirtab, 0, 0);
1883 			hashput(pp.dirtab, pp.tmpbuf, c);
1884 		}
1885 		if (c = (int)hashget(pp.strtab, s))
1886 		{
1887 			hashput(pp.strtab, 0, 0);
1888 			hashput(pp.strtab, pp.tmpbuf, c);
1889 		}
1890 	}
1891 	if (c != T_STRING || !*(s = pp.token))
1892 	{
1893 		if (c)
1894 			error(2, "%s: %s: address argument expected", p3, pptokstr(pp.token, 0));
1895 		goto eatmap;
1896 	}
1897 	map = newof(0, struct map, 1, 0);
1898 
1899 	/*
1900 	 * /from/
1901 	 */
1902 
1903 	if (i0 = regcomp(&map->re, s, REG_AUGMENTED|REG_DELIMITED|REG_LENIENT|REG_NULL))
1904 		regfatal(&map->re, 3, i0);
1905 	if (*(s += map->re.re_npat))
1906 	{
1907 		error(2, "%s: invalid characters after pattern: %s ", p3, s);
1908 		goto eatmap;
1909 	}
1910 
1911 	/*
1912 	 * /old/new/[flags]
1913 	 */
1914 
1915 	edit = 0;
1916 	while ((c = pplex()) == T_STRING)
1917 	{
1918 		if (!*(s = pp.token))
1919 		{
1920 			error(2, "%s: substitution argument expected", p3);
1921 			goto eatmap;
1922 		}
1923 		if (edit)
1924 			edit = edit->next = newof(0, struct edit, 1, 0);
1925 		else
1926 			edit = map->edit = newof(0, struct edit, 1, 0);
1927 		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)))
1928 			s += edit->re.re_npat;
1929 		if (i0)
1930 			regfatal(&edit->re, 3, i0);
1931 		if (*s)
1932 		{
1933 			error(2, "%s: invalid characters after substitution: %s ", p3, s);
1934 			goto eatmap;
1935 		}
1936 	}
1937 	if (c)
1938 	{
1939 		error(2, "%s: %s: substitution argument expected", p3, pptokstr(pp.token, 0));
1940 		goto eatmap;
1941 	}
1942 	map->next = (struct map*)pp.maps;
1943 	pp.maps = (char*)map;
1944  eatmap:
1945 	POP_LINE();
1946 				/*INDENT*/
1947 				break;
1948 			case X_MAPINCLUDE:
1949 				ppmapinclude(NiL, p5);
1950 				break;
1951 			case X_MODERN:
1952 				setoption(MODERN, i0);
1953 				break;
1954 			case X_MULTIPLE:
1955 				n = 1;
1956 				if (pp.in->type == IN_FILE)
1957 					ppmultiple(ppsetfile(error_info.file), i0 ? INC_CLEAR : INC_TEST);
1958 				break;
1959 			case X_NATIVE:
1960 				setoption(NATIVE, i0);
1961 				break;
1962 			case X_OPSPACE:
1963 				ppfsm(FSM_OPSPACE, i0 ? p4 : (char*)0);
1964 				break;
1965 			case X_PASSTHROUGH:
1966 				ppop(PP_PASSTHROUGH, i0);
1967 				break;
1968 			case X_PEDANTIC:
1969 				ppop(PP_PEDANTIC, i0);
1970 				break;
1971 			case X_PLUSCOMMENT:
1972 				ppop(PP_PLUSCOMMENT, i0);
1973 				break;
1974 			case X_PLUSPLUS:
1975 				ppop(PP_PLUSPLUS, i0);
1976 				break;
1977 			case X_PLUSSPLICE:
1978 				setoption(PLUSSPLICE, i0);
1979 				break;
1980 			case X_PRAGMAEXPAND:
1981 				setoption(PRAGMAEXPAND, i0);
1982 				break;
1983 			case X_PRAGMAFLAGS:
1984 				tokop(PP_PRAGMAFLAGS, p3, p, i0, 0);
1985 				break;
1986 			case X_PREDEFINED:
1987 				setoption(PREDEFINED, i0);
1988 				break;
1989 			case X_PREFIX:
1990 				setoption(PREFIX, i0);
1991 				break;
1992 			case X_PRESERVE:
1993 				setoption(PRESERVE, i0);
1994 				if (pp.option & PRESERVE)
1995 				{
1996 					setmode(CATLITERAL, 0);
1997 					ppop(PP_COMPATIBILITY, 1);
1998 					ppop(PP_TRANSITION, 0);
1999 					ppop(PP_PLUSCOMMENT, 1);
2000 					ppop(PP_SPACEOUT, 1);
2001 					setoption(STRINGSPAN, 1);
2002 					setoption(STRINGSPLIT, 0);
2003 					ppop(PP_HOSTDIR, "-", 1);
2004 				}
2005 				break;
2006 			case X_PROTOTYPED:
2007 				/*
2008 				 * this option doesn't bump the token count
2009 				 */
2010 
2011 				n = 1;
2012 				directive = ENDIF;
2013 #if PROTOTYPE
2014 				setoption(PROTOTYPED, i0);
2015 #else
2016 				error(1, "preprocessor not compiled with prototype conversion enabled");
2017 #endif
2018 				break;
2019 			case X_PROTO:
2020 				setoption(NOPROTO, !i0);
2021 				break;
2022 			case X_QUOTE:
2023 				tokop(PP_QUOTE, p3, p, i0, TOKOP_UNSET|TOKOP_STRING);
2024 				break;
2025 			case X_READONLY:
2026 				setmode(READONLY, i0);
2027 				break;
2028 			case X_REGUARD:
2029 				setoption(REGUARD, i0);
2030 				break;
2031 			case X_RESERVED:
2032 				tokop(PP_RESERVED, p3, p, i0, 0);
2033 				break;
2034 			case X_SPACEOUT:
2035 				if (!(pp.state & (COMPATIBILITY|COMPILE)))
2036 					ppop(PP_SPACEOUT, i0);
2037 				break;
2038 			case X_SPLICECAT:
2039 				setoption(SPLICECAT, i0);
2040 				break;
2041 			case X_SPLICESPACE:
2042 				setoption(SPLICESPACE, i0);
2043 				break;
2044 			case X_STANDARD:
2045 				tokop(PP_STANDARD, p3, p, i0, TOKOP_UNSET|TOKOP_STRING|TOKOP_DUP);
2046 				break;
2047 			case X_STRICT:
2048 				ppop(PP_STRICT, i0);
2049 				break;
2050 			case X_STRINGSPAN:
2051 				setoption(STRINGSPAN, i0);
2052 				break;
2053 			case X_STRINGSPLIT:
2054 				setoption(STRINGSPLIT, i0);
2055 				if (pp.option & STRINGSPLIT)
2056 					setmode(CATLITERAL, 0);
2057 				break;
2058 			case X_SYSTEM_HEADER:
2059 				if (i0)
2060 				{
2061 					pp.mode |= HOSTED;
2062 					pp.flags |= PP_hosted;
2063 					pp.in->flags |= IN_hosted;
2064 				}
2065 				else
2066 				{
2067 					pp.mode &= ~HOSTED;
2068 					pp.flags &= ~PP_hosted;
2069 					pp.in->flags &= ~PP_hosted;
2070 				}
2071 				break;
2072 			case X_TEST:
2073 				ppop(PP_TEST, p);
2074 				break;
2075 			case X_TEXT:
2076 				if (!(pp.option & KEEPNOTEXT))
2077 					setstate(NOTEXT, !i0);
2078 				break;
2079 			case X_TRANSITION:
2080 				ppop(PP_TRANSITION, i0);
2081 				if (pp.state & TRANSITION) ppop(PP_COMPATIBILITY, i0);
2082 				break;
2083 			case X_TRUNCATE:
2084 				ppop(PP_TRUNCATE, i0 ? (p ? strtol(p, NiL, 0) : TRUNCLENGTH) : 0);
2085 				break;
2086 			case X_VENDOR:
2087 				tokop(PP_VENDOR, p3, p, i0, TOKOP_UNSET|TOKOP_STRING|TOKOP_DUP);
2088 				break;
2089 			case X_VERSION:
2090 				if (!(*pp.control & SKIP) && pp.pragma && !(pp.state & NOTEXT))
2091 				{
2092 					sfsprintf(pp.tmpbuf, MAXTOKEN, "\"%s\"", pp.version);
2093 					(*pp.pragma)(dirname(PRAGMA), pp.pass, p3, pp.tmpbuf, !n);
2094 					if (pp.linesync && !n)
2095 						(*pp.linesync)(error_info.line, error_info.file);
2096 					emitted = 1;
2097 				}
2098 				break;
2099 			case X_WARN:
2100 				ppop(PP_WARN, i0);
2101 				break;
2102 			case X_ZEOF:
2103 				setoption(ZEOF, i0);
2104 				break;
2105 #if DEBUG
2106 			case 0:
2107 			case X_INCLUDED:
2108 			case X_NOTICED:
2109 			case X_OPTION:
2110 			case X_STATEMENT:
2111 				break;
2112 			default:
2113 				error(PANIC, "%s: option recognized but not implemented", pp.valbuf);
2114 				break;
2115 #endif
2116 			}
2117 			*p4 = i2;
2118 			if (!n)
2119 				goto checkmap;
2120 			goto donedirective;
2121 		case RENAME:
2122 			if ((pp.state & STRICT) && !(pp.mode & (HOSTED|RELAX)))
2123 				error(1, "#%s: non-standard directive", pp.token);
2124 			if ((c = pplex()) != T_ID)
2125 			{
2126 				error(1, "%s: invalid macro name", pptokstr(pp.token, 0));
2127 				goto eatdirective;
2128 			}
2129 			if (!(sym = pprefmac(pp.token, REF_DELETE)) || !sym->macro)
2130 				goto eatdirective;
2131 			if (sym->flags & (SYM_ACTIVE|SYM_READONLY))
2132 			{
2133 				if (!(pp.option & ALLPOSSIBLE))
2134 					error(2, "%s: macro is %s", sym->name, (sym->flags & SYM_READONLY) ? "readonly" : "active");
2135 				goto eatdirective;
2136 			}
2137 			if ((c = pplex()) != T_ID)
2138 			{
2139 				error(1, "%s: invalid macro name", pptokstr(pp.token, 0));
2140 				goto eatdirective;
2141 			}
2142 			var.symbol = pprefmac(pp.token, REF_CREATE);
2143 			if (mac = var.symbol->macro)
2144 			{
2145 				if (var.symbol->flags & (SYM_ACTIVE|SYM_READONLY))
2146 				{
2147 					if (!(pp.option & ALLPOSSIBLE))
2148 						error(2, "%s: macro is %s", var.symbol->name, (var.symbol->flags & SYM_READONLY) ? "readonly" : "active");
2149 					goto eatdirective;
2150 				}
2151 				if (!(pp.mode & HOSTED) || !(var.symbol->flags & SYM_INITIAL))
2152 					error(1, "%s redefined", var.symbol->name);
2153 				if (mac->formals) free(mac->formals);
2154 				free(mac->value);
2155 				free(mac);
2156 			}
2157 			ppfsm(FSM_MACRO, var.symbol->name);
2158 			var.symbol->flags = sym->flags;
2159 			sym->flags &= ~(SYM_BUILTIN|SYM_FUNCTION|SYM_INIT|SYM_MULTILINE|SYM_PREDEFINED|SYM_REDEFINE|SYM_VARIADIC);
2160 			var.symbol->macro = sym->macro;
2161 			sym->macro = 0;
2162 			break;
2163 		case UNDEF:
2164 			if ((c = pplex()) != T_ID)
2165 			{
2166 				error(1, "%s: invalid macro name", pptokstr(pp.token, 0));
2167 				goto eatdirective;
2168 			}
2169 			if (sym = pprefmac(pp.token, REF_DELETE))
2170 			{
2171 				if (mac = sym->macro)
2172 				{
2173 					if (sym->flags & (SYM_ACTIVE|SYM_READONLY))
2174 					{
2175 						if (!(pp.option & ALLPOSSIBLE))
2176 							error(2, "%s: macro is %s", sym->name, (sym->flags & SYM_READONLY) ? "readonly" : "active");
2177 						goto eatdirective;
2178 					}
2179 					if (mac->formals) free(mac->formals);
2180 					free(mac->value);
2181 					free(mac);
2182 					mac = sym->macro = 0;
2183 				}
2184 				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)))
2185 				{
2186 					ppsync();
2187 					ppprintf("#%s %s", dirname(UNDEF), sym->name);
2188 					emitted = 1;
2189 				}
2190 				sym->flags &= ~(SYM_BUILTIN|SYM_FUNCTION|SYM_INIT|SYM_MULTILINE|SYM_PREDEFINED|SYM_REDEFINE|SYM_VARIADIC);
2191 				n2 = error_info.line;
2192 				goto benign;
2193 			}
2194 			else pprefmac(pp.token, REF_UNDEF);
2195 			break;
2196 #if DEBUG
2197 		default:
2198 			error(PANIC, "#%s: directive recognized but not implemented", pp.token);
2199 			goto eatdirective;
2200 #endif
2201 		}
2202 		break;
2203 	case '\n':
2204 		break;
2205 	default:
2206 		error(1, "%s: invalid directive name", pptokstr(pp.token, 0));
2207 		goto eatdirective;
2208 	}
2209  enddirective:
2210 #if COMPATIBLE
2211 	if (c != '\n' && !(pp.state & COMPATIBILITY))
2212 #else
2213 	if (c != '\n')
2214 #endif
2215 	{
2216 		pp.state |= DISABLE|NOSPACE;
2217 		if ((c = pplex()) != '\n' && (pp.mode & (HOSTED|PEDANTIC)) == PEDANTIC)
2218 			error(1, "%s: invalid characters after directive", pptokstr(pp.token, 0));
2219 	}
2220  eatdirective:
2221 	if (c != '\n')
2222 	{
2223 		pp.state |= DISABLE;
2224 		while (pplex() != '\n');
2225 	}
2226  donedirective:
2227 #if _HUH_2002_05_09
2228 	if (!(pp.state & EOF2NL))
2229 		error(2, "%s in directive", pptokchr(0));
2230 #endif
2231 	pp.state &= ~RESTORE;
2232 	pp.mode &= ~RELAX;
2233 	if (!(*pp.control & SKIP))
2234 	{
2235 		pp.state |= restore;
2236 		switch (directive)
2237 		{
2238 		case LINE:
2239 			return 0;
2240 		case INCLUDE:
2241 			if (pp.include)
2242 			{
2243 				error_info.line++;
2244 				PUSH_FILE(pp.include, n);
2245 				if (!pp.vendor && (pp.found->type & TYPE_VENDOR))
2246 					pp.vendor = 1;
2247 				pp.include = 0;
2248 				return 0;
2249 			}
2250 			if (pp.incref)
2251 				(*pp.incref)(error_info.file, ppgetfile(pp.path)->name, error_info.line, PP_SYNC_IGNORE);
2252 			else if (pp.linesync && pp.macref)
2253 			{
2254 				pp.flags |= PP_lineignore;
2255 				(*pp.linesync)(error_info.line, ppgetfile(pp.path)->name);
2256 			}
2257 			/*FALLTHROUGH*/
2258 		default:
2259 			pp.in->flags |= IN_tokens;
2260 			/*FALLTHROUGH*/
2261 		case ENDIF:
2262 			error_info.line++;
2263 			if (emitted)
2264 			{
2265 				ppputchar('\n');
2266 				ppcheckout();
2267 			}
2268 			else
2269 			{
2270 				pp.state |= HIDDEN;
2271 				pp.hidden++;
2272 			}
2273 			return 0;
2274 		}
2275 	}
2276 	pp.state |= restore|HIDDEN|SKIPCONTROL;
2277 	pp.hidden++;
2278 	pp.level++;
2279 	error_info.line++;
2280 	return 0;
2281 }
2282 
2283 /*
2284  * grow the pp nesting control stack
2285  */
2286 
2287 void
2288 ppnest(void)
2289 {
2290 	register struct ppinstk*	ip;
2291 	int				oz;
2292 	int				nz;
2293 	long				adjust;
2294 	long*				op;
2295 	long*				np;
2296 
2297 	oz = pp.constack;
2298 	op = pp.maxcon - oz + 1;
2299 	nz = oz * 2;
2300 	np = newof(op, long, nz, 0);
2301 	if (adjust = (np - op))
2302 	{
2303 		ip = pp.in;
2304 		do
2305 		{
2306 			if (ip->control)
2307 				ip->control += adjust;
2308 		} while (ip = ip->prev);
2309 	}
2310 	pp.control = np + oz;
2311 	pp.constack = nz;
2312 	pp.maxcon = np + nz - 1;
2313 }
2314