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