xref: /titanic_41/usr/src/lib/libpp/common/ppop.c (revision c037192b037119c9fd354732fc29b38ce097d356)
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 library control interface
26  */
27 
28 #include "pplib.h"
29 #include "pptab.h"
30 
31 #include <ls.h>
32 
33 #define REFONE	(pp.truncate?(Hash_table_t*)0:pp.symtab)
34 #define REFALL	(pp.truncate?pp.dirtab:pp.symtab)
35 
36 #define ppiskey(t,v,p)	(p=t,v>=p->value&&value<=(p+elementsof(t)-2)->value)
37 
38 /*
39  * set option value
40  * initialization files have lowest precedence
41  */
42 
43 static void
44 set(register long* p, register long op, int val)
45 {
46 	long*	r;
47 
48 	r = p == &pp.state ? &pp.ro_state : p == &pp.mode ? &pp.ro_mode : &pp.ro_option;
49 	if (!(pp.mode & INIT) || !(pp.in->type == IN_FILE) || !(*r & op))
50 	{
51 		if (!pp.initialized && !(pp.mode & INIT))
52 			*r |= op;
53 		if (val)
54 			*p |= op;
55 		else
56 			*p &= ~op;
57 	}
58 	debug((-7, "set(%s)=%s", p == &pp.state ? "state" : p == &pp.mode ? "mode" : "option", p == &pp.state ? ppstatestr(*p) : p == &pp.mode ? ppmodestr(*p) : ppoptionstr(*p)));
59 }
60 
61 /*
62  * initialize hash table with keywords from key
63  */
64 
65 static void
66 inithash(register Hash_table_t* tab, register struct ppkeyword* key)
67 {
68 	register char*	s;
69 
70 	for (; s = key->name; key++)
71 	{
72 		if (!ppisid(*s))
73 			s++;
74 		hashput(tab, s, key->value);
75 	}
76 }
77 
78 /*
79  * return ppkeyword table name given value
80  */
81 
82 char*
83 ppkeyname(register int value, int dir)
84 {
85 	register char*			s;
86 	register struct ppkeyword*	p;
87 
88 	if (dir && ppiskey(directives, value, p) || !dir && (ppiskey(options, value, p) || ppiskey(predicates, value, p) || ppiskey(variables, value, p)))
89 	{
90 		s = (p + (value - p->value))->name;
91 		return s + !ppisid(*s);
92 	}
93 #if DEBUG
94 	error(PANIC, "no keyword table name for value=%d", value);
95 #endif
96 	return "UNKNOWN";
97 }
98 
99 /*
100  * add to the include maps
101  */
102 
103 void
104 ppmapinclude(char* file, register char* s)
105 {
106 	register int		c;
107 	register struct ppdirs*	dp;
108 	int			fd;
109 	int			flags;
110 	int			index;
111 	int			token;
112 	char*			t;
113 	char*			old_file;
114 	long			old_state;
115 	struct ppfile*		fp;
116 	struct ppfile*		mp;
117 
118 	old_file = error_info.file;
119 	old_state = pp.state;
120 	if (s)
121 		PUSH_BUFFER("mapinclude", s, 1);
122 	else if (file)
123 	{
124 		if (*file == '-')
125 		{
126 			if (!error_info.file)
127 			{
128 				error(1, "%s: input file name required for %s ignore", file, dirname(INCLUDE));
129 				return;
130 			}
131 			s = t = strcopy(pp.tmpbuf, error_info.file);
132 			c = *++file;
133 			for (;;)
134 			{
135 				if (s <= pp.tmpbuf || *s == '/')
136 				{
137 					s = t;
138 					break;
139 				}
140 				else if (*s == c)
141 					break;
142 				s--;
143 			}
144 			strcpy(s, file);
145 			file = pp.tmpbuf;
146 		}
147 		if ((fd = ppsearch(file, INC_LOCAL, SEARCH_INCLUDE)) < 0)
148 			return;
149 		PUSH_FILE(file, fd);
150 	}
151 	else
152 		return;
153 #if CATSTRINGS
154 	pp.state |= (COMPILE|FILEPOP|HEADER|JOINING|STRIP|NOSPACE|PASSEOF);
155 #else
156 	pp.state |= (COMPILE|FILEPOP|HEADER|STRIP|NOSPACE|PASSEOF);
157 #endif
158 	pp.level++;
159 	flags = INC_MAPALL;
160 	fp = mp = 0;
161 	for (;;)
162 	{
163 		switch (token = pplex())
164 		{
165 		case 0:
166 		case T_STRING:
167 		case T_HEADER:
168 			if (fp)
169 			{
170 				fp->guard = INC_IGNORE;
171 				for (dp = pp.firstdir->next; dp; dp = dp->next)
172 					if (dp->name && (c = strlen(dp->name)) && !strncmp(dp->name, fp->name, c) && fp->name[c] == '/')
173 					{
174 						ppsetfile(fp->name + c + 1)->guard = INC_IGNORE;
175 						break;
176 					}
177 			}
178 			if (!token)
179 				break;
180 			pathcanon(pp.token, 0);
181 			fp = ppsetfile(pp.token);
182 			if (mp)
183 			{
184 				mp->flags |= flags;
185 				if (streq(fp->name, "."))
186 					mp->flags |= INC_MAPNOLOCAL;
187 				else
188 					mp->bound[index] = fp;
189 
190 				fp = mp = 0;
191 			}
192 			else
193 				index = token == T_HEADER ? INC_STANDARD : INC_LOCAL;
194 			continue;
195 		case '=':
196 			if (!(mp = fp))
197 				error(3, "%s: \"name\" = \"binding\" expected");
198 			fp = 0;
199 			continue;
200 		case '\n':
201 			continue;
202 		case T_ID:
203 			if (streq(pp.token, "all"))
204 			{
205 				flags = INC_MAPALL;
206 				continue;
207 			}
208 			else if (streq(pp.token, "hosted"))
209 			{
210 				flags = INC_MAPHOSTED;
211 				continue;
212 			}
213 			else if (streq(pp.token, "nohosted"))
214 			{
215 				flags = INC_MAPNOHOSTED;
216 				continue;
217 			}
218 			/*FALLTHROUGH*/
219 		default:
220 			error(3, "%s unexpected in %s map list", pptokstr(pp.token, 0), dirname(INCLUDE));
221 			break;
222 		}
223 		break;
224 	}
225 	pp.level--;
226 	error_info.file = old_file;
227 	pp.state = old_state;
228 }
229 
230 /*
231  * return non-0 if file is identical to fd
232  */
233 
234 static int
235 identical(char* file, int fd)
236 {
237 	struct stat	a;
238 	struct stat	b;
239 
240 	return !stat(file, &a) && !fstat(fd, &b) && a.st_dev == b.st_dev && a.st_ino == b.st_ino;
241 }
242 
243 /*
244  * compare up to pp.truncate chars
245  *
246  * NOTE: __STD* and symbols containing ' ' are not truncated
247  */
248 
249 static int
250 trunccomp(register char* a, register char* b)
251 {
252 	return !strchr(b, ' ') && !strneq(b, "__STD", 5) ? strncmp(a, b, pp.truncate) : strcmp(a, b);
253 }
254 
255 /*
256  * hash up to pp.truncate chars
257  *
258  * NOTE: __STD* and symbols containing ' ' are not truncated
259  */
260 
261 static unsigned int
262 trunchash(char* a)
263 {
264 	int	n;
265 
266 	return memhash(a, (n = strlen(a)) > pp.truncate && !strchr(a, ' ') && !strneq(a, "__STD", 5) ? pp.truncate : n);
267 }
268 
269 #if DEBUG & TRACE_debug
270 /*
271  * append context to debug trace
272  */
273 
274 static int
275 context(Sfio_t* sp, int level, int flags)
276 {
277 	static int	state;
278 
279 	NoP(level);
280 	NoP(flags);
281 	if (error_info.trace <= -10 && pp.state != state)
282 	{
283 		state = pp.state;
284 		sfprintf(sp, " %s", ppstatestr(pp.state));
285 	}
286 	return 1;
287 }
288 #endif
289 
290 /*
291  * reset include guard
292  */
293 
294 static int
295 unguard(const char* name, char* v, void* handle)
296 {
297 	register struct ppfile*		fp = (struct ppfile*)v;
298 
299 	fp->guard = 0;
300 	return 0;
301 }
302 
303 /*
304  * reset macro definition
305  */
306 
307 static void
308 undefine(void* p)
309 {
310 	struct ppmacro*		mac = ((struct ppsymbol*)p)->macro;
311 
312 	if (mac)
313 	{
314 		if (mac->formals)
315 			free(mac->formals);
316 		free(mac->value);
317 		free(mac);
318 	}
319 }
320 
321 /*
322  * pp operations
323  *
324  * NOTE: PP_INIT must be done before the first pplex() call
325  *	 PP_DONE must be done after the last pplex() call
326  *	 PP_INIT-PP_DONE must be done for each new PP_INPUT
327  */
328 
329 void
330 ppop(int op, ...)
331 {
332 	va_list				ap;
333 	register char*			p;
334 	register struct ppkeyword*	kp;
335 	register char*			s;
336 	int				c;
337 	long				n;
338 	char*				t;
339 	struct ppdirs*			dp;
340 	struct ppdirs*			hp;
341 	struct ppsymkey*		key;
342 	struct oplist*			xp;
343 	Sfio_t*				sp;
344 	struct stat			st;
345 	PPCOMMENT			ppcomment;
346 	PPLINESYNC			pplinesync;
347 
348 	static int			initialized;
349 
350 	va_start(ap, op);
351 	switch (op)
352 	{
353 	case PP_ASSERT:
354 	case PP_DEFINE:
355 	case PP_DIRECTIVE:
356 	case PP_OPTION:
357 	case PP_READ:
358 	case PP_UNDEF:
359 		if (pp.initialized)
360 			goto before;
361 		if ((p = va_arg(ap, char*)) && *p)
362 		{
363 			if (pp.lastop)
364 				pp.lastop = (pp.lastop->next = newof(0, struct oplist, 1, 0));
365 			else
366 				pp.firstop = pp.lastop = newof(0, struct oplist, 1, 0);
367 			pp.lastop->op = op;
368 			pp.lastop->value = p;
369 		}
370 		break;
371 	case PP_BUILTIN:
372 		pp.builtin = va_arg(ap, PPBUILTIN);
373 		break;
374 	case PP_CDIR:
375 		p = va_arg(ap, char*);
376 		c = va_arg(ap, int);
377 		pp.cdir.path = 0;
378 		if (!p)
379 			pp.c = c;
380 		else if (streq(p, "-"))
381 		{
382 			pp.c = c;
383 			for (dp = pp.firstdir; dp; dp = dp->next)
384 				dp->c = c;
385 		}
386 		else if (!pp.c)
387 		{
388 			if (!*p || stat((pathcanon(p, 0), p), &st))
389 				pp.c = c;
390 			else
391 			{
392 				for (dp = pp.firstdir; dp; dp = dp->next)
393 				{
394 					if (!pp.c && (dp->c || dp->name && SAMEID(&dp->id, &st)))
395 						pp.c = 1;
396 					dp->c = pp.c == 1;
397 				}
398 				if (!pp.c)
399 				{
400 					pp.cdir.path = p;
401 					SAVEID(&pp.cdir.id, &st);
402 				}
403 			}
404 		}
405 		break;
406 	case PP_CHOP:
407 		if (p = va_arg(ap, char*))
408 		{
409 			c = strlen(p);
410 			xp = newof(0, struct oplist, 1, c + 1);
411 			xp->value = ((char*)xp) + sizeof(struct oplist);
412 			s = xp->value;
413 			c = *p++;
414 			while (*p && *p != c)
415 				*s++ = *p++;
416 			*s++ = '/';
417 			xp->op = s - xp->value;
418 			*s++ = 0;
419 			if (*p && *++p && *p != c)
420 			{
421 				while (*p && *p != c)
422 					*s++ = *p++;
423 				*s++ = '/';
424 			}
425 			*s = 0;
426 			xp->next = pp.chop;
427 			pp.chop = xp;
428 		}
429 		break;
430 	case PP_COMMENT:
431 		if (pp.comment = va_arg(ap, PPCOMMENT))
432 			pp.flags |= PP_comment;
433 		else
434 			pp.flags &= ~PP_comment;
435 		break;
436 	case PP_COMPATIBILITY:
437 		set(&pp.state, COMPATIBILITY, va_arg(ap, int));
438 #if COMPATIBLE
439 		if (pp.initialized)
440 			ppfsm(FSM_COMPATIBILITY, NiL);
441 #else
442 		if (pp.state & COMPATIBILITY)
443 			error(3, "preprocessor not compiled with compatibility dialect enabled [COMPATIBLE]");
444 #endif
445 		if (pp.state & COMPATIBILITY)
446 			pp.flags |= PP_compatibility;
447 		else
448 			pp.flags &= ~PP_compatibility;
449 		break;
450 	case PP_COMPILE:
451 		if (pp.initialized)
452 			goto before;
453 		pp.state |= COMPILE;
454 		if (!pp.symtab)
455 			pp.symtab = hashalloc(NiL, HASH_name, "symbols", 0);
456 		if (kp = va_arg(ap, struct ppkeyword*))
457 			for (; s = kp->name; kp++)
458 		{
459 			n = SYM_LEX;
460 			switch (*s)
461 			{
462 			case '-':
463 				s++;
464 				break;
465 			case '+':
466 				s++;
467 				if (!(pp.option & PLUSPLUS))
468 					break;
469 				/*FALLTHROUGH*/
470 			default:
471 				n |= SYM_KEYWORD;
472 				break;
473 			}
474 			if (key = ppkeyset(pp.symtab, s))
475 			{
476 				key->sym.flags = n;
477 				key->lex = kp->value;
478 			}
479 		}
480 		break;
481 	case PP_DEBUG:
482 		error_info.trace = va_arg(ap, int);
483 		break;
484 	case PP_DEFAULT:
485 		if (p = va_arg(ap, char*))
486 			p = strdup(p);
487 		if (pp.ppdefault)
488 			free(pp.ppdefault);
489 		pp.ppdefault = p;
490 		break;
491 	case PP_DONE:
492 #if CHECKPOINT
493 		if (pp.mode & DUMP)
494 			ppdump();
495 #endif
496 		if (pp.mode & FILEDEPS)
497 		{
498 			sfputc(pp.filedeps.sp, '\n');
499 			if (pp.filedeps.sp == sfstdout)
500 				sfsync(pp.filedeps.sp);
501 			else
502 				sfclose(pp.filedeps.sp);
503 		}
504 		if (pp.state & STANDALONE)
505 		{
506 			if ((pp.state & (NOTEXT|HIDDEN)) == HIDDEN && pplastout() != '\n')
507 				ppputchar('\n');
508 			ppflushout();
509 		}
510 		error_info.file = 0;
511 		break;
512 	case PP_DUMP:
513 		set(&pp.mode, DUMP, va_arg(ap, int));
514 #if !CHECKPOINT
515 		if (pp.mode & DUMP)
516 			error(3, "preprocessor not compiled with checkpoint enabled [CHECKPOINT]");
517 #endif
518 		break;
519 	case PP_FILEDEPS:
520 		if (n = va_arg(ap, int))
521 			pp.filedeps.flags |= n;
522 		else
523 			pp.filedeps.flags = 0;
524 		break;
525 	case PP_FILENAME:
526 		error_info.file = va_arg(ap, char*);
527 		break;
528 	case PP_HOSTDIR:
529 		if (!(pp.mode & INIT))
530 			pp.ro_mode |= HOSTED;
531 		else if (pp.ro_mode & HOSTED)
532 			break;
533 		pp.ro_mode |= INIT;
534 		p = va_arg(ap, char*);
535 		c = va_arg(ap, int);
536 		pp.hostdir.path = 0;
537 		if (!p)
538 			pp.hosted = c;
539 		else if (streq(p, "-"))
540 		{
541 			if (pp.initialized)
542 				set(&pp.mode, HOSTED, c);
543 			else
544 			{
545 				pp.hosted = c ? 1 : 2;
546 				for (dp = pp.firstdir; dp; dp = dp->next)
547 					if (pp.hosted == 1)
548 						dp->type |= TYPE_HOSTED;
549 					else
550 						dp->type &= ~TYPE_HOSTED;
551 			}
552 		}
553 		else if (!pp.hosted)
554 		{
555 			if (!*p || stat((pathcanon(p, 0), p), &st))
556 				pp.hosted = 1;
557 			else
558 			{
559 				for (dp = pp.firstdir; dp; dp = dp->next)
560 				{
561 					if (!pp.hosted && ((dp->type & TYPE_HOSTED) || dp->name && SAMEID(&dp->id, &st)))
562 						pp.hosted = 1;
563 					if (pp.hosted == 1)
564 						dp->type |= TYPE_HOSTED;
565 					else
566 						dp->type &= ~TYPE_HOSTED;
567 				}
568 				if (!pp.hosted)
569 				{
570 					pp.hostdir.path = p;
571 					SAVEID(&pp.hostdir.id, &st);
572 				}
573 			}
574 		}
575 		break;
576 	case PP_ID:
577 		p = va_arg(ap, char*);
578 		c = va_arg(ap, int);
579 		if (p)
580 			ppfsm(c ? FSM_IDADD : FSM_IDDEL, p);
581 		break;
582 	case PP_IGNORE:
583 		if (p = va_arg(ap, char*))
584 		{
585 			pathcanon(p, 0);
586 			ppsetfile(p)->guard = INC_IGNORE;
587 			message((-3, "%s: ignore", p));
588 		}
589 		break;
590 	case PP_IGNORELIST:
591 		if (pp.initialized)
592 			goto before;
593 		pp.ignore = va_arg(ap, char*);
594 		break;
595 	case PP_INCLUDE:
596 		if ((p = va_arg(ap, char*)) && *p)
597 		{
598 			pathcanon(p, 0);
599 			if (stat(p, &st))
600 				break;
601 			for (dp = pp.stddirs; dp = dp->next;)
602 				if (dp->name && SAMEID(&dp->id, &st))
603 					break;
604 			if (pp.cdir.path && SAMEID(&pp.cdir.id, &st))
605 			{
606 				pp.cdir.path = 0;
607 				pp.c = 1;
608 			}
609 			if (pp.hostdir.path && SAMEID(&pp.hostdir.id, &st))
610 			{
611 				pp.hostdir.path = 0;
612 				pp.hosted = 1;
613 			}
614 			if ((pp.mode & INIT) && !(pp.ro_mode & INIT))
615 				pp.hosted = 1;
616 			c = dp && dp->c || pp.c == 1;
617 			n = dp && (dp->type & TYPE_HOSTED) || pp.hosted == 1;
618 			if (!dp || dp == pp.lastdir->next)
619 			{
620 				if (dp)
621 				{
622 					c = dp->c;
623 					n = dp->type & TYPE_HOSTED;
624 				}
625 				dp = newof(0, struct ppdirs, 1, 0);
626 				dp->name = p;
627 				SAVEID(&dp->id, &st);
628 				dp->type |= TYPE_INCLUDE;
629 				dp->index = INC_LOCAL + pp.ignoresrc != 0;
630 				dp->next = pp.lastdir->next;
631 				pp.lastdir = pp.lastdir->next = dp;
632 			}
633 			dp->c = c;
634 			if (n)
635 				dp->type |= TYPE_HOSTED;
636 			else
637 				dp->type &= ~TYPE_HOSTED;
638 		}
639 		break;
640 	case PP_INCREF:
641 		pp.incref = va_arg(ap, PPINCREF);
642 		break;
643 	case PP_RESET:
644 		pp.reset.on = 1;
645 		break;
646 	case PP_INIT:
647 		if (pp.initialized)
648 		{
649 			error_info.errors = 0;
650 			error_info.warnings = 0;
651 		}
652 		else
653 		{
654 			/*
655 			 * context initialization
656 			 */
657 
658 			if (!initialized)
659 			{
660 				/*
661 				 * out of malloc is fatal
662 				 */
663 
664 				memfatal();
665 
666 				/*
667 				 * initialize the error message interface
668 				 */
669 
670 				error_info.version = (char*)pp.version;
671 #if DEBUG & TRACE_debug
672 				error_info.auxilliary = context;
673 				pptrace(0);
674 #endif
675 
676 				/*
677 				 * initialize pplex tables
678 				 */
679 
680 				ppfsm(FSM_INIT, NiL);
681 
682 				/*
683 				 * fixed macro stack size -- room for improvement
684 				 */
685 
686 				pp.macp = newof(0, struct ppmacstk, DEFMACSTACK, 0);
687 				pp.macp->next = pp.macp + 1;
688 				pp.maxmac = (char*)pp.macp + DEFMACSTACK;
689 				initialized = 1;
690 
691 				/*
692 				 * initial include/if control stack
693 				 */
694 
695 				pp.control = newof(0, long, pp.constack, 0);
696 				pp.maxcon = pp.control + pp.constack - 1;
697 			}
698 
699 			/*
700 			 * validate modes
701 			 */
702 
703 			switch (pp.arg_mode)
704 			{
705 			case 'a':
706 			case 'C':
707 				ppop(PP_COMPATIBILITY, 0);
708 				ppop(PP_TRANSITION, 1);
709 				break;
710 			case 'A':
711 			case 'c':
712 				ppop(PP_COMPATIBILITY, 0);
713 				ppop(PP_STRICT, 1);
714 				break;
715 			case 'f':
716 				ppop(PP_COMPATIBILITY, 1);
717 				ppop(PP_PLUSPLUS, 1);
718 				ppop(PP_TRANSITION, 1);
719 				break;
720 			case 'F':
721 				ppop(PP_COMPATIBILITY, 0);
722 				ppop(PP_PLUSPLUS, 1);
723 				break;
724 			case 'k':
725 			case 's':
726 				ppop(PP_COMPATIBILITY, 1);
727 				ppop(PP_STRICT, 1);
728 				break;
729 			case 'o':
730 			case 'O':
731 				ppop(PP_COMPATIBILITY, 1);
732 				ppop(PP_TRANSITION, 0);
733 				break;
734 			case 't':
735 				ppop(PP_COMPATIBILITY, 1);
736 				ppop(PP_TRANSITION, 1);
737 				break;
738 			}
739 			if (!(pp.state & WARN) && !(pp.arg_style & STYLE_gnu))
740 				ppop(PP_PEDANTIC, 1);
741 			if (pp.state & PASSTHROUGH)
742 			{
743 				if (pp.state & COMPILE)
744 				{
745 					pp.state &= ~PASSTHROUGH;
746 					error(1, "passthrough ignored for compile");
747 				}
748 				else
749 				{
750 					ppop(PP_COMPATIBILITY, 1);
751 					ppop(PP_HOSTDIR, "-", 1);
752 					ppop(PP_SPACEOUT, 1);
753 					set(&pp.state, DISABLE, va_arg(ap, int));
754 				}
755 			}
756 
757 			/*
758 			 * create the hash tables
759 			 */
760 
761 			if (!pp.symtab)
762 				pp.symtab = hashalloc(NiL, HASH_name, "symbols", 0);
763 			if (!pp.dirtab)
764 			{
765 				pp.dirtab = hashalloc(REFONE, HASH_name, "directives", 0);
766 				inithash(pp.dirtab, directives);
767 			}
768 			if (!pp.filtab)
769 				pp.filtab = hashalloc(REFALL, HASH_name, "files", 0);
770 			if (!pp.prdtab)
771 				pp.prdtab = hashalloc(REFALL, HASH_name, "predicates", 0);
772 			if (!pp.strtab)
773 			{
774 				pp.strtab = hashalloc(REFALL, HASH_name, "strings", 0);
775 				inithash(pp.strtab, options);
776 				inithash(pp.strtab, predicates);
777 				inithash(pp.strtab, variables);
778 			}
779 			pp.optflags[X_PROTOTYPED] = OPT_GLOBAL;
780 			pp.optflags[X_SYSTEM_HEADER] = OPT_GLOBAL|OPT_PASS;
781 
782 			/*
783 			 * mark macros that are builtin predicates
784 			 */
785 
786 			for (kp = predicates; s = kp->name; kp++)
787 			{
788 				if (!ppisid(*s))
789 					s++;
790 				ppassert(DEFINE, s, 0);
791 			}
792 
793 			/*
794 			 * the remaining entry names must be allocated
795 			 */
796 
797 			hashset(pp.dirtab, HASH_ALLOCATE);
798 			hashset(pp.filtab, HASH_ALLOCATE);
799 			hashset(pp.prdtab, HASH_ALLOCATE);
800 			hashset(pp.strtab, HASH_ALLOCATE);
801 			hashset(pp.symtab, HASH_ALLOCATE);
802 			if (pp.test & TEST_nonoise)
803 			{
804 				c = error_info.trace;
805 				error_info.trace = 0;
806 			}
807 #if DEBUG
808 			if (!(pp.test & TEST_noinit))
809 			{
810 #endif
811 
812 			/*
813 			 * compose, push and read the builtin initialization script
814 			 */
815 
816 			if (!(sp = sfstropen()))
817 				error(3, "temporary buffer allocation error");
818 			sfprintf(sp,
819 "\
820 #%s %s:%s \"/#<assert> /\" \"/assert /%s #/\"\n\
821 #%s %s:%s \"/#<unassert> /\" \"/unassert /%s #/\"\n\
822 ",
823 				dirname(PRAGMA),
824 				pp.pass,
825 				keyname(X_MAP),
826 				dirname(DEFINE),
827 				dirname(PRAGMA),
828 				pp.pass,
829 				keyname(X_MAP),
830 				dirname(UNDEF));
831 			if (pp.ppdefault && *pp.ppdefault)
832 			{
833 				if (pp.probe)
834 				{
835 					c = pp.lastdir->next->type;
836 					pp.lastdir->next->type = 0;
837 				}
838 				if (ppsearch(pp.ppdefault, T_STRING, SEARCH_EXISTS) < 0)
839 				{
840 					free(pp.ppdefault);
841 					if (!(pp.ppdefault = pathprobe(pp.path, NiL, "C", pp.pass, pp.probe ? pp.probe : PPPROBE, 0)))
842 						error(1, "cannot determine default definitions for %s", pp.probe ? pp.probe : PPPROBE);
843 				}
844 				if (pp.probe)
845 					pp.lastdir->next->type = c;
846 			}
847 			while (pp.firstop)
848 			{
849 				switch (pp.firstop->op)
850 				{
851 				case PP_ASSERT:
852 					sfprintf(sp, "#%s #%s\n", dirname(DEFINE), pp.firstop->value);
853 					break;
854 				case PP_DEFINE:
855 					if (*pp.firstop->value == '#')
856 						sfprintf(sp, "#%s %s\n", dirname(DEFINE), pp.firstop->value);
857 					else
858 					{
859 						if (s = strchr(pp.firstop->value, '='))
860 							sfprintf(sp, "#%s %-.*s %s\n", dirname(DEFINE), s - pp.firstop->value, pp.firstop->value, s + 1);
861 						else
862 							sfprintf(sp, "#%s %s 1\n", dirname(DEFINE), pp.firstop->value);
863 					}
864 					break;
865 				case PP_DIRECTIVE:
866 					sfprintf(sp, "#%s\n", pp.firstop->value);
867 					break;
868 				case PP_OPTION:
869 					if (s = strchr(pp.firstop->value, '='))
870 						sfprintf(sp, "#%s %s:%-.*s %s\n", dirname(PRAGMA), pp.pass, s - pp.firstop->value, pp.firstop->value, s + 1);
871 					else
872 						sfprintf(sp, "#%s %s:%s\n", dirname(PRAGMA), pp.pass, pp.firstop->value);
873 					break;
874 				case PP_READ:
875 					sfprintf(sp, "#%s \"%s\"\n", dirname(INCLUDE), pp.firstop->value);
876 					break;
877 				case PP_UNDEF:
878 					sfprintf(sp, "#%s %s\n", dirname(UNDEF), pp.firstop->value);
879 					break;
880 				}
881 				pp.lastop = pp.firstop;
882 				pp.firstop = pp.firstop->next;
883 				free(pp.lastop);
884 			}
885 			sfprintf(sp,
886 "\
887 #%s %s:%s\n\
888 #%s %s:%s\n\
889 #%s !#%s(%s)\n\
890 #%s !#%s(%s) || #%s(%s)\n\
891 "
892 				, dirname(PRAGMA)
893 				, pp.pass
894 				, keyname(X_BUILTIN)
895 				, dirname(PRAGMA)
896 				, pp.pass
897 				, keyname(X_PREDEFINED)
898 				, dirname(IF)
899 				, keyname(X_OPTION)
900 				, keyname(X_PLUSPLUS)
901 				, dirname(IF)
902 				, keyname(X_OPTION)
903 				, keyname(X_COMPATIBILITY)
904 				, keyname(X_OPTION)
905 				, keyname(X_TRANSITION)
906 				);
907 			sfprintf(sp,
908 "\
909 #%s #%s(%s)\n\
910 #%s %s:%s\n\
911 #%s %s:%s\n\
912 #%s __STRICT__ 1\n\
913 #%s\n\
914 #%s\n\
915 "
916 				, dirname(IF)
917 				, keyname(X_OPTION)
918 				, keyname(X_STRICT)
919 				, dirname(PRAGMA)
920 				, pp.pass
921 				, keyname(X_ALLMULTIPLE)
922 				, dirname(PRAGMA)
923 				, pp.pass
924 				, keyname(X_READONLY)
925 				, dirname(DEFINE)
926 				, dirname(ENDIF)
927 				, dirname(ENDIF)
928 				);
929 			for (kp = readonlys; s = kp->name; kp++)
930 			{
931 				if (!ppisid(*s))
932 					s++;
933 				sfprintf(sp, "#%s %s\n", dirname(UNDEF), s);
934 			}
935 			sfprintf(sp,
936 "\
937 #%s\n\
938 #%s __STDPP__ 1\n\
939 #%s %s:no%s\n\
940 "
941 				, dirname(ENDIF)
942 				, dirname(DEFINE)
943 				, dirname(PRAGMA)
944 				, pp.pass
945 				, keyname(X_PREDEFINED)
946 				);
947 			if (!pp.truncate)
948 				sfprintf(sp,
949 "\
950 #%s __STDPP__directive #(%s)\n\
951 "
952 				, dirname(DEFINE)
953 				, keyname(V_DIRECTIVE)
954 				);
955 			for (kp = variables; s = kp->name; kp++)
956 				if (ppisid(*s) || *s++ == '+')
957 				{
958 					t = *s == '_' ? "" : "__";
959 					sfprintf(sp, "#%s %s%s%s #(%s)\n" , dirname(DEFINE), t, s, t, s);
960 				}
961 			sfprintf(sp,
962 "\
963 #%s %s:no%s\n\
964 #%s %s:no%s\n\
965 "
966 				, dirname(PRAGMA)
967 				, pp.pass
968 				, keyname(X_READONLY)
969 				, dirname(PRAGMA)
970 				, pp.pass
971 				, keyname(X_BUILTIN)
972 				);
973 			if (pp.ppdefault && *pp.ppdefault)
974 				sfprintf(sp, "#%s \"%s\"\n", dirname(INCLUDE), pp.ppdefault);
975 			sfprintf(sp,
976 "\
977 #%s !defined(__STDC__) && (!#option(compatibility) || #option(transition))\n\
978 #%s __STDC__ #(STDC)\n\
979 #%s\n\
980 "
981 				, dirname(IF)
982 				, dirname(DEFINE)
983 				, dirname(ENDIF)
984 				);
985 			t = sfstruse(sp);
986 			debug((-9, "\n/* begin initialization */\n%s/* end initialization */", t));
987 			ppcomment = pp.comment;
988 			pp.comment = 0;
989 			pplinesync = pp.linesync;
990 			pp.linesync = 0;
991 			PUSH_INIT(pp.pass, t);
992 			pp.mode |= INIT;
993 			while (pplex());
994 			pp.mode &= ~INIT;
995 			pp.comment = ppcomment;
996 			pp.linesync = pplinesync;
997 			pp.prefix = 0;
998 			sfstrclose(sp);
999 			if (error_info.trace)
1000 				for (dp = pp.firstdir; dp; dp = dp->next)
1001 					message((-1, "include directory %s%s%s%s", dp->name, (dp->type & TYPE_VENDOR) ? " [VENDOR]" : "", (dp->type & TYPE_HOSTED) ? " [HOSTED]" : "", dp->c ? " [C]" : ""));
1002 #if DEBUG
1003 			}
1004 			if (pp.test & TEST_nonoise)
1005 				error_info.trace = c;
1006 #endif
1007 			{
1008 				/*
1009 				 * this is sleazy but at least it's
1010 				 * hidden in the library
1011 				 */
1012 #include <preroot.h>
1013 #if FS_PREROOT
1014 				struct pplist*	preroot;
1015 
1016 				if ((preroot = (struct pplist*)hashget(pp.prdtab, "preroot")))
1017 					setpreroot(NiL, preroot->value);
1018 #endif
1019 			}
1020 			if (pp.ignoresrc)
1021 			{
1022 				if (pp.ignoresrc > 1 && pp.stddirs != pp.firstdir)
1023 					error(1, "directories up to and including %s are for \"...\" include files only", pp.stddirs->name);
1024 				pp.lcldirs = pp.lcldirs->next;
1025 			}
1026 			if (pp.ignore)
1027 			{
1028 				if (*pp.ignore)
1029 					ppmapinclude(pp.ignore, NiL);
1030 				else
1031 					pp.ignore = 0;
1032 			}
1033 			if (pp.standalone)
1034 				pp.state |= STANDALONE;
1035 #if COMPATIBLE
1036 			ppfsm(FSM_COMPATIBILITY, NiL);
1037 #endif
1038 			ppfsm(FSM_PLUSPLUS, NiL);
1039 			pp.initialized = 1;
1040 			if (pp.reset.on)
1041 			{
1042 				pp.reset.symtab = pp.symtab;
1043 				pp.symtab = 0;
1044 				pp.reset.ro_state = pp.ro_state;
1045 				pp.reset.ro_mode = pp.ro_mode;
1046 				pp.reset.ro_option = pp.ro_option;
1047 			}
1048 		}
1049 		if (pp.reset.on)
1050 		{
1051 			if (pp.symtab)
1052 			{
1053 				hashwalk(pp.filtab, 0, unguard, NiL);
1054 				hashfree(pp.symtab);
1055 			}
1056 			pp.symtab = hashalloc(NiL, HASH_name, "symbols", HASH_free, undefine, HASH_set, HASH_ALLOCATE|HASH_BUCKET, 0);
1057 			hashview(pp.symtab, pp.reset.symtab);
1058 			pp.ro_state = pp.reset.ro_state;
1059 			pp.ro_mode = pp.reset.ro_mode;
1060 			pp.ro_option = pp.reset.ro_option;
1061 		}
1062 #if CHECKPOINT
1063 		if (pp.mode & DUMP)
1064 		{
1065 			if (!pp.pragma)
1066 				error(3, "#%s must be enabled for checkpoints", dirname(PRAGMA));
1067 			(*pp.pragma)(dirname(PRAGMA), pp.pass, keyname(X_CHECKPOINT), pp.checkpoint, 1);
1068 		}
1069 #endif
1070 		if (n = pp.filedeps.flags)
1071 		{
1072 			if (!(n & PP_deps_file))
1073 			{
1074 				pp.state |= NOTEXT;
1075 				pp.option |= KEEPNOTEXT;
1076 				pp.linesync = 0;
1077 			}
1078 			if (n & PP_deps_generated)
1079 				pp.mode |= GENDEPS;
1080 			if (n & PP_deps_local)
1081 				pp.mode &= ~HEADERDEPS;
1082 			else if (!(pp.mode & FILEDEPS))
1083 				pp.mode |= HEADERDEPS;
1084 			pp.mode |= FILEDEPS;
1085 		}
1086 
1087 		/*
1088 		 * push the main input file -- special case for hosted mark
1089 		 */
1090 
1091 		if (pp.firstdir->type & TYPE_HOSTED)
1092 			pp.mode |= MARKHOSTED;
1093 		else
1094 			pp.mode &= ~MARKHOSTED;
1095 #if CHECKPOINT
1096 		if (!(pp.mode & DUMP))
1097 #endif
1098 		{
1099 			if (!(p = error_info.file))
1100 				p = "";
1101 			else
1102 			{
1103 				error_info.file = 0;
1104 				if (*p)
1105 				{
1106 					pathcanon(p, 0);
1107 					p = ppsetfile(p)->name;
1108 				}
1109 			}
1110 			PUSH_FILE(p, 0);
1111 		}
1112 		if (pp.mode & FILEDEPS)
1113 		{
1114 			if (s = strrchr(error_info.file, '/'))
1115 				s++;
1116 			else
1117 				s = error_info.file;
1118 			if (!*s)
1119 				s = "-";
1120 			s = strcpy(pp.tmpbuf, s);
1121 			if ((t = p = strrchr(s, '.')) && (*++p == 'c' || *p == 'C'))
1122 			{
1123 				if (c = *++p)
1124 					while (*++p == c);
1125 				if (*p)
1126 					t = 0;
1127 				else
1128 					t++;
1129 			}
1130 			if (!t)
1131 			{
1132 				t = s + strlen(s);
1133 				*t++ = '.';
1134 			}
1135 			*(t + 1) = 0;
1136 			if (pp.state & NOTEXT)
1137 				pp.filedeps.sp = sfstdout;
1138 			else
1139 			{
1140 				*t = 'd';
1141 				if (!(pp.filedeps.sp = sfopen(NiL, s, "w")))
1142 					error(ERROR_SYSTEM|3, "%s: cannot create", s);
1143 			}
1144 			*t = 'o';
1145 			pp.column = sfprintf(pp.filedeps.sp, "%s :", s);
1146 			if (*error_info.file)
1147 				pp.column += sfprintf(pp.filedeps.sp, " %s", error_info.file);
1148 		}
1149 		if (xp = pp.firsttx)
1150 		{
1151 			if (!(sp = sfstropen()))
1152 				error(3, "temporary buffer allocation error");
1153 			while (xp)
1154 			{
1155 				sfprintf(sp, "#%s \"%s\"\n", dirname(INCLUDE), xp->value);
1156 				xp = xp->next;
1157 			}
1158 			t = sfstruse(sp);
1159 			PUSH_BUFFER("options", t, 1);
1160 			sfstrclose(sp);
1161 		}
1162 		break;
1163 	case PP_INPUT:
1164 #if CHECKPOINT && POOL
1165 		if (!(pp.mode & DUMP) || pp.pool.input)
1166 #else
1167 #if CHECKPOINT
1168 		if (!(pp.mode & DUMP))
1169 #else
1170 #if POOL
1171 		if (pp.pool.input)
1172 #endif
1173 #endif
1174 #endif
1175 		{
1176 			p = va_arg(ap, char*);
1177 			if (!error_info.file)
1178 				error_info.file = p;
1179 			close(0);
1180 			if (open(p, O_RDONLY) != 0)
1181 				error(ERROR_SYSTEM|3, "%s: cannot read", p);
1182 			if (strmatch(p, "*.(s|S|as|AS|asm|ASM)"))
1183 			{
1184 				set(&pp.mode, CATLITERAL, 0);
1185 				ppop(PP_SPACEOUT, 1);
1186 			}
1187 			break;
1188 		}
1189 		/*FALLTHROUGH*/
1190 	case PP_TEXT:
1191 		if (pp.initialized)
1192 			goto before;
1193 		if ((p = va_arg(ap, char*)) && *p)
1194 		{
1195 			if (pp.lasttx)
1196 				pp.lasttx = pp.lasttx->next = newof(0, struct oplist, 1, 0);
1197 			else
1198 				pp.firsttx = pp.lasttx = newof(0, struct oplist, 1, 0);
1199 			pp.lasttx->op = op;
1200 			pp.lasttx->value = p;
1201 		}
1202 		break;
1203 	case PP_KEYARGS:
1204 		if (pp.initialized)
1205 			goto before;
1206 		set(&pp.option, KEYARGS, va_arg(ap, int));
1207 		if (pp.option & KEYARGS)
1208 #if MACKEYARGS
1209 			set(&pp.mode, CATLITERAL, 1);
1210 #else
1211 			error(3, "preprocessor not compiled with macro keyword arguments enabled [MACKEYARGS]");
1212 #endif
1213 		break;
1214 	case PP_LINE:
1215 		pp.linesync = va_arg(ap, PPLINESYNC);
1216 		break;
1217 	case PP_LINEBASE:
1218 		if (va_arg(ap, int))
1219 			pp.flags |= PP_linebase;
1220 		else
1221 			pp.flags &= ~PP_linebase;
1222 		break;
1223 	case PP_LINEFILE:
1224 		if (va_arg(ap, int))
1225 			pp.flags |= PP_linefile;
1226 		else
1227 			pp.flags &= ~PP_linefile;
1228 		break;
1229 	case PP_LINEID:
1230 		if (!(p = va_arg(ap, char*)))
1231 			pp.lineid = "";
1232 		else if (*p != '-')
1233 			pp.lineid = strdup(p);
1234 		else
1235 			pp.option |= IGNORELINE;
1236 		break;
1237 	case PP_LINETYPE:
1238 		if ((n = va_arg(ap, int)) >= 1)
1239 			pp.flags |= PP_linetype;
1240 		else
1241 			pp.flags &= ~PP_linetype;
1242 		if (n >= 2)
1243 			pp.flags |= PP_linehosted;
1244 		else
1245 			pp.flags &= ~PP_linehosted;
1246 		break;
1247 	case PP_LOCAL:
1248 		if (pp.initialized)
1249 			goto before;
1250 		pp.ignoresrc++;
1251 		pp.stddirs = pp.lastdir;
1252 		if (!(pp.ro_option & PREFIX))
1253 			pp.option &= ~PREFIX;
1254 		break;
1255 	case PP_MACREF:
1256 		pp.macref = va_arg(ap, PPMACREF);
1257 		break;
1258 	case PP_MULTIPLE:
1259 		set(&pp.mode, ALLMULTIPLE, va_arg(ap, int));
1260 		break;
1261 	case PP_NOHASH:
1262 		set(&pp.option, NOHASH, va_arg(ap, int));
1263 		break;
1264 	case PP_NOISE:
1265 		op = va_arg(ap, int);
1266 		set(&pp.option, NOISE, op);
1267 		set(&pp.option, NOISEFILTER, op < 0);
1268 		break;
1269 	case PP_OPTARG:
1270 		pp.optarg = va_arg(ap, PPOPTARG);
1271 		break;
1272 	case PP_OUTPUT:
1273 		pp.outfile = va_arg(ap, char*);
1274 		if (identical(pp.outfile, 0))
1275 			error(3, "%s: identical to input", pp.outfile);
1276 		close(1);
1277 		if (open(pp.outfile, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) != 1)
1278 			error(ERROR_SYSTEM|3, "%s: cannot create", pp.outfile);
1279 		break;
1280 	case PP_PASSTHROUGH:
1281 		if (!(pp.state & COMPILE))
1282 			set(&pp.state, PASSTHROUGH, va_arg(ap, int));
1283 		break;
1284 	case PP_PEDANTIC:
1285 		set(&pp.mode, PEDANTIC, va_arg(ap, int));
1286 		break;
1287 	case PP_PLUSCOMMENT:
1288 		set(&pp.option, PLUSCOMMENT, va_arg(ap, int));
1289 		if (pp.initialized)
1290 			ppfsm(FSM_PLUSPLUS, NiL);
1291 		break;
1292 	case PP_PLUSPLUS:
1293 		set(&pp.option, PLUSPLUS, va_arg(ap, int));
1294 		set(&pp.option, PLUSCOMMENT, va_arg(ap, int));
1295 		if (pp.initialized)
1296 			ppfsm(FSM_PLUSPLUS, NiL);
1297 		break;
1298 	case PP_POOL:
1299 		if (pp.initialized)
1300 			goto before;
1301 		if (va_arg(ap, int))
1302 		{
1303 #if POOL
1304 			pp.pool.input = dup(0);
1305 			pp.pool.output = dup(1);
1306 			p = "/dev/null";
1307 			if (!identical(p, 0))
1308 			{
1309 				if (!identical(p, 1))
1310 					ppop(PP_OUTPUT, p);
1311 				ppop(PP_INPUT, p);
1312 			}
1313 #else
1314 			error(3, "preprocessor not compiled with input pool enabled [POOL]");
1315 #endif
1316 		}
1317 		break;
1318 	case PP_PRAGMA:
1319 		pp.pragma = va_arg(ap, PPPRAGMA);
1320 		break;
1321 	case PP_PRAGMAFLAGS:
1322 		if (p = va_arg(ap, char*))
1323 		{
1324 			n = OPT_GLOBAL;
1325 			if (*p == '-')
1326 				p++;
1327 			else
1328 				n |= OPT_PASS;
1329 			if ((c = (int)hashref(pp.strtab, p)) > 0 && c <= X_last_option)
1330 				pp.optflags[c] = n;
1331 		}
1332 		break;
1333 	case PP_PROBE:
1334 		pp.probe = va_arg(ap, char*);
1335 		break;
1336 	case PP_QUOTE:
1337 		p = va_arg(ap, char*);
1338 		c = va_arg(ap, int);
1339 		if (p)
1340 			ppfsm(c ? FSM_QUOTADD : FSM_QUOTDEL, p);
1341 		break;
1342 	case PP_REGUARD:
1343 		set(&pp.option, REGUARD, va_arg(ap, int));
1344 		break;
1345 	case PP_RESERVED:
1346 		if ((pp.state & COMPILE) && (p = va_arg(ap, char*)))
1347 		{
1348 			if (!(sp = sfstropen()))
1349 				error(3, "temporary buffer allocation error");
1350 			sfputr(sp, p, -1);
1351 			p = sfstruse(sp);
1352 			if (s = strchr(p, '='))
1353 				*s++ = 0;
1354 			else
1355 				s = p;
1356 			while (*s == '_')
1357 				s++;
1358 			for (t = s + strlen(s); t > s && *(t - 1) == '_'; t--);
1359 			if (*t == '_')
1360 				*t = 0;
1361 			else
1362 				t = 0;
1363 			op = ((key = ppkeyref(pp.symtab, s)) && (key->sym.flags & SYM_LEX)) ? key->lex : T_NOISE;
1364 			if (pp.test & 0x0400)
1365 				error(1, "reserved#1 `%s' %d", s, op);
1366 			if (t)
1367 				*t = '_';
1368 			if (!(key = ppkeyget(pp.symtab, p)))
1369 				key = ppkeyset(pp.symtab, NiL);
1370 			else if (!(key->sym.flags & SYM_LEX))
1371 			{
1372 				struct ppsymbol	tmp;
1373 
1374 				tmp = key->sym;
1375 				hashlook(pp.symtab, p, HASH_DELETE, NiL);
1376 				key = ppkeyset(pp.symtab, NiL);
1377 				key->sym.flags = tmp.flags;
1378 				key->sym.macro = tmp.macro;
1379 				key->sym.value = tmp.value;
1380 				key->sym.hidden = tmp.hidden;
1381 			}
1382 			if (!(key->sym.flags & SYM_KEYWORD))
1383 			{
1384 				key->sym.flags |= SYM_KEYWORD|SYM_LEX;
1385 				key->lex = op;
1386 				if (pp.test & 0x0400)
1387 					error(1, "reserved#2 `%s' %d", p, op);
1388 			}
1389 			sfstrclose(sp);
1390 		}
1391 		break;
1392 	case PP_SPACEOUT:
1393 		set(&pp.state, SPACEOUT, va_arg(ap, int));
1394 		break;
1395 	case PP_STANDALONE:
1396 		if (pp.initialized)
1397 			goto before;
1398 		pp.standalone = 1;
1399 		break;
1400 	case PP_STANDARD:
1401 		if ((pp.lastdir->next->name = ((p = va_arg(ap, char*)) && *p) ? p : NiL) && !stat(p, &st))
1402 			SAVEID(&pp.lastdir->next->id, &st);
1403 		for (dp = pp.firstdir; dp; dp = dp->next)
1404 			if (dp->name)
1405 				for (hp = pp.firstdir; hp != dp; hp = hp->next)
1406 					if (hp->name && SAMEID(&hp->id, &dp->id))
1407 					{
1408 						hp->c = dp->c;
1409 						if (dp->type & TYPE_HOSTED)
1410 							hp->type |= TYPE_HOSTED;
1411 						else
1412 							hp->type &= ~TYPE_HOSTED;
1413 					}
1414 		break;
1415 	case PP_STRICT:
1416 		set(&pp.state, TRANSITION, 0);
1417 		pp.flags &= ~PP_transition;
1418 		set(&pp.state, STRICT, va_arg(ap, int));
1419 		if (pp.state & STRICT)
1420 			pp.flags |= PP_strict;
1421 		else
1422 			pp.flags &= ~PP_strict;
1423 		break;
1424 	case PP_TEST:
1425 		if (p = va_arg(ap, char*))
1426 			for (;;)
1427 			{
1428 				while (*p == ' ' || *p == '\t') p++;
1429 				for (s = p; n = *s; s++)
1430 					if (n == ',' || n == ' ' || n == '\t')
1431 					{
1432 						*s++ = 0;
1433 						break;
1434 					}
1435 				if (!*p)
1436 					break;
1437 				n = 0;
1438 				if (*p == 'n' && *(p + 1) == 'o')
1439 				{
1440 					p += 2;
1441 					op = 0;
1442 				}
1443 				else
1444 					op = 1;
1445 				if (streq(p, "count"))
1446 					n = TEST_count;
1447 				else if (streq(p, "hashcount"))
1448 					n = TEST_hashcount;
1449 				else if (streq(p, "hashdump"))
1450 					n = TEST_hashdump;
1451 				else if (streq(p, "hit"))
1452 					n = TEST_hit;
1453 				else if (streq(p, "init"))
1454 					n = TEST_noinit|TEST_INVERT;
1455 				else if (streq(p, "noise"))
1456 					n = TEST_nonoise|TEST_INVERT;
1457 				else if (streq(p, "proto"))
1458 					n = TEST_noproto|TEST_INVERT;
1459 				else if (*p >= '0' && *p <= '9')
1460 					n = strtoul(p, NiL, 0);
1461 				else
1462 				{
1463 					error(1, "%s: unknown test", p);
1464 					break;
1465 				}
1466 				if (n & TEST_INVERT)
1467 				{
1468 					n &= ~TEST_INVERT;
1469 					op = !op;
1470 				}
1471 				if (op)
1472 					pp.test |= n;
1473 				else
1474 					pp.test &= ~n;
1475 				p = s;
1476 				debug((-4, "test = 0%o", pp.test));
1477 			}
1478 		break;
1479 	case PP_TRANSITION:
1480 		set(&pp.state, STRICT, 0);
1481 		pp.flags &= ~PP_strict;
1482 		set(&pp.state, TRANSITION, va_arg(ap, int));
1483 		if (pp.state & TRANSITION)
1484 			pp.flags |= PP_transition;
1485 		else
1486 			pp.flags &= ~PP_transition;
1487 		break;
1488 	case PP_TRUNCATE:
1489 		if (pp.initialized)
1490 			goto before;
1491 		if ((op = va_arg(ap, int)) < 0)
1492 			op = 0;
1493 		set(&pp.option, TRUNCATE, op);
1494 		if (pp.option & TRUNCATE)
1495 		{
1496 			Hash_bucket_t*		b;
1497 			Hash_bucket_t*		p;
1498 			Hash_position_t*	pos;
1499 			Hash_table_t*		tab;
1500 
1501 			pp.truncate = op;
1502 			tab = pp.symtab;
1503 			pp.symtab = hashalloc(NiL, HASH_set, tab ? HASH_ALLOCATE : 0, HASH_compare, trunccomp, HASH_hash, trunchash, HASH_name, "truncate", 0);
1504 			if (tab && (pos = hashscan(tab, 0)))
1505 			{
1506 				if (p = hashnext(pos))
1507 					do
1508 					{
1509 						b = hashnext(pos);
1510 						hashlook(pp.symtab, (char*)p, HASH_BUCKET|HASH_INSTALL, NiL);
1511 					} while (p = b);
1512 				hashdone(pos);
1513 			}
1514 		}
1515 		else
1516 			pp.truncate = 0;
1517 		break;
1518 	case PP_VENDOR:
1519 		p = va_arg(ap, char*);
1520 		c = va_arg(ap, int) != 0;
1521 		if (!p || !*p)
1522 			for (dp = pp.firstdir; dp; dp = dp->next)
1523 				dp->type &= ~TYPE_VENDOR;
1524 		else if (streq(p, "-"))
1525 		{
1526 			for (dp = pp.firstdir; dp; dp = dp->next)
1527 				if (c)
1528 					dp->type |= TYPE_VENDOR;
1529 				else
1530 					dp->type &= ~TYPE_VENDOR;
1531 		}
1532 		else if (!stat((pathcanon(p, 0), p), &st))
1533 		{
1534 			c = 0;
1535 			for (dp = pp.firstdir; dp; dp = dp->next)
1536 			{
1537 				if (!c && ((dp->type & TYPE_VENDOR) || dp->name && SAMEID(&dp->id, &st)))
1538 					c = 1;
1539 				if (c)
1540 					dp->type |= TYPE_VENDOR;
1541 				else
1542 					dp->type &= ~TYPE_VENDOR;
1543 			}
1544 		}
1545 		break;
1546 	case PP_WARN:
1547 		set(&pp.state, WARN, va_arg(ap, int));
1548 		break;
1549 	before:
1550 		error(3, "ppop(%d): preprocessor operation must be done before PP_INIT", op);
1551 		break;
1552 	default:
1553 		error(3, "ppop(%d): invalid preprocessor operation", op);
1554 		break;
1555 	}
1556 	va_end(ap);
1557 }
1558