xref: /titanic_50/usr/src/lib/libpp/common/ppop.c (revision 2df1fe9ca32bb227b9158c67f5c00b54c20b10fd)
1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *           Copyright (c) 1986-2007 AT&T Knowledge Ventures            *
5 *                      and is licensed under the                       *
6 *                  Common Public License, Version 1.0                  *
7 *                      by AT&T Knowledge Ventures                      *
8 *                                                                      *
9 *                A copy of the License is available at                 *
10 *            http://www.opensource.org/licenses/cpl1.0.txt             *
11 *         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
12 *                                                                      *
13 *              Information and Software Systems Research               *
14 *                            AT&T Research                             *
15 *                           Florham Park NJ                            *
16 *                                                                      *
17 *                 Glenn Fowler <gsf@research.att.com>                  *
18 *                                                                      *
19 ***********************************************************************/
20 #pragma prototyped
21 /*
22  * Glenn Fowler
23  * AT&T Research
24  *
25  * preprocessor 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.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.ppdefault)
845 					sfprintf(sp, "#%s \"%s\"\n", dirname(INCLUDE), pp.ppdefault);
846 				if (pp.probe)
847 					pp.lastdir->next->type = c;
848 			}
849 			while (pp.firstop)
850 			{
851 				switch (pp.firstop->op)
852 				{
853 				case PP_ASSERT:
854 					sfprintf(sp, "#%s #%s\n", dirname(DEFINE), pp.firstop->value);
855 					break;
856 				case PP_DEFINE:
857 					if (*pp.firstop->value == '#')
858 						sfprintf(sp, "#%s %s\n", dirname(DEFINE), pp.firstop->value);
859 					else
860 					{
861 						if (s = strchr(pp.firstop->value, '='))
862 							sfprintf(sp, "#%s %-.*s %s\n", dirname(DEFINE), s - pp.firstop->value, pp.firstop->value, s + 1);
863 						else
864 							sfprintf(sp, "#%s %s 1\n", dirname(DEFINE), pp.firstop->value);
865 					}
866 					break;
867 				case PP_DIRECTIVE:
868 					sfprintf(sp, "#%s\n", pp.firstop->value);
869 					break;
870 				case PP_OPTION:
871 					if (s = strchr(pp.firstop->value, '='))
872 						sfprintf(sp, "#%s %s:%-.*s %s\n", dirname(PRAGMA), pp.pass, s - pp.firstop->value, pp.firstop->value, s + 1);
873 					else
874 						sfprintf(sp, "#%s %s:%s\n", dirname(PRAGMA), pp.pass, pp.firstop->value);
875 					break;
876 				case PP_READ:
877 					sfprintf(sp, "#%s \"%s\"\n", dirname(INCLUDE), pp.firstop->value);
878 					break;
879 				case PP_UNDEF:
880 					sfprintf(sp, "#%s %s\n", dirname(UNDEF), pp.firstop->value);
881 					break;
882 				}
883 				pp.lastop = pp.firstop;
884 				pp.firstop = pp.firstop->next;
885 				free(pp.lastop);
886 			}
887 			sfprintf(sp,
888 "\
889 #%s %s:%s\n\
890 #%s %s:%s\n\
891 #%s !#%s(%s)\n\
892 #%s !#%s(%s) || #%s(%s)\n\
893 "
894 				, dirname(PRAGMA)
895 				, pp.pass
896 				, keyname(X_BUILTIN)
897 				, dirname(PRAGMA)
898 				, pp.pass
899 				, keyname(X_PREDEFINED)
900 				, dirname(IF)
901 				, keyname(X_OPTION)
902 				, keyname(X_PLUSPLUS)
903 				, dirname(IF)
904 				, keyname(X_OPTION)
905 				, keyname(X_COMPATIBILITY)
906 				, keyname(X_OPTION)
907 				, keyname(X_TRANSITION)
908 				);
909 			sfprintf(sp,
910 "\
911 #%s __STDC__\n\
912 #%s __STDC__ #(STDC)\n\
913 #%s\n\
914 #%s #%s(%s)\n\
915 #%s %s:%s\n\
916 #%s %s:%s\n\
917 #%s __STRICT__ 1\n\
918 #%s\n\
919 #%s\n\
920 "
921 				, dirname(IFNDEF)
922 				, dirname(DEFINE)
923 				, dirname(ENDIF)
924 				, dirname(IF)
925 				, keyname(X_OPTION)
926 				, keyname(X_STRICT)
927 				, dirname(PRAGMA)
928 				, pp.pass
929 				, keyname(X_ALLMULTIPLE)
930 				, dirname(PRAGMA)
931 				, pp.pass
932 				, keyname(X_READONLY)
933 				, dirname(DEFINE)
934 				, dirname(ENDIF)
935 				, dirname(ENDIF)
936 				);
937 			for (kp = readonlys; s = kp->name; kp++)
938 			{
939 				if (!ppisid(*s))
940 					s++;
941 				sfprintf(sp, "#%s %s\n", dirname(UNDEF), s);
942 			}
943 			sfprintf(sp,
944 "\
945 #%s\n\
946 #%s __STDPP__ 1\n\
947 #%s %s:no%s\n\
948 "
949 				, dirname(ENDIF)
950 				, dirname(DEFINE)
951 				, dirname(PRAGMA)
952 				, pp.pass
953 				, keyname(X_PREDEFINED)
954 				);
955 			if (!pp.truncate)
956 				sfprintf(sp,
957 "\
958 #%s __STDPP__directive #(%s)\n\
959 "
960 				, dirname(DEFINE)
961 				, keyname(V_DIRECTIVE)
962 				);
963 			for (kp = variables; s = kp->name; kp++)
964 				if (ppisid(*s) || *s++ == '+')
965 				{
966 					t = *s == '_' ? "" : "__";
967 					sfprintf(sp, "#%s %s%s%s #(%s)\n" , dirname(DEFINE), t, s, t, s);
968 				}
969 			sfprintf(sp,
970 "\
971 #%s %s:no%s\n\
972 #%s %s:no%s\n\
973 "
974 				, dirname(PRAGMA)
975 				, pp.pass
976 				, keyname(X_READONLY)
977 				, dirname(PRAGMA)
978 				, pp.pass
979 				, keyname(X_BUILTIN)
980 				);
981 			t = sfstruse(sp);
982 			debug((-9, "\n/* begin initialization */\n%s/* end initialization */", t));
983 			ppcomment = pp.comment;
984 			pp.comment = 0;
985 			pplinesync = pp.linesync;
986 			pp.linesync = 0;
987 			PUSH_INIT(pp.pass, t);
988 			pp.mode |= INIT;
989 			while (pplex());
990 			pp.mode &= ~INIT;
991 			pp.comment = ppcomment;
992 			pp.linesync = pplinesync;
993 			pp.prefix = 0;
994 			sfstrclose(sp);
995 			if (error_info.trace)
996 				for (dp = pp.firstdir; dp; dp = dp->next)
997 					message((-1, "include directory %s%s%s%s", dp->name, (dp->type & TYPE_VENDOR) ? " [VENDOR]" : "", (dp->type & TYPE_HOSTED) ? " [HOSTED]" : "", dp->c ? " [C]" : ""));
998 #if DEBUG
999 			}
1000 			if (pp.test & TEST_nonoise)
1001 				error_info.trace = c;
1002 #endif
1003 			{
1004 				/*
1005 				 * this is sleazy but at least it's
1006 				 * hidden in the library
1007 				 */
1008 #include <preroot.h>
1009 #if FS_PREROOT
1010 				struct pplist*	preroot;
1011 
1012 				if ((preroot = (struct pplist*)hashget(pp.prdtab, "preroot")))
1013 					setpreroot(NiL, preroot->value);
1014 #endif
1015 			}
1016 			if (pp.ignoresrc)
1017 			{
1018 				if (pp.ignoresrc > 1 && pp.stddirs != pp.firstdir)
1019 					error(1, "directories up to and including %s are for \"...\" include files only", pp.stddirs->name);
1020 				pp.lcldirs = pp.lcldirs->next;
1021 			}
1022 			if (pp.ignore)
1023 			{
1024 				if (*pp.ignore)
1025 					ppmapinclude(pp.ignore, NiL);
1026 				else
1027 					pp.ignore = 0;
1028 			}
1029 			if (pp.standalone)
1030 				pp.state |= STANDALONE;
1031 #if COMPATIBLE
1032 			ppfsm(FSM_COMPATIBILITY, NiL);
1033 #endif
1034 			ppfsm(FSM_PLUSPLUS, NiL);
1035 			pp.initialized = 1;
1036 			if (pp.reset.on)
1037 			{
1038 				pp.reset.symtab = pp.symtab;
1039 				pp.symtab = 0;
1040 				pp.reset.ro_state = pp.ro_state;
1041 				pp.reset.ro_mode = pp.ro_mode;
1042 				pp.reset.ro_option = pp.ro_option;
1043 			}
1044 		}
1045 		if (pp.reset.on)
1046 		{
1047 			if (pp.symtab)
1048 			{
1049 				hashwalk(pp.filtab, 0, unguard, NiL);
1050 				hashfree(pp.symtab);
1051 			}
1052 			pp.symtab = hashalloc(NiL, HASH_name, "symbols", HASH_free, undefine, HASH_set, HASH_ALLOCATE|HASH_BUCKET, 0);
1053 			hashview(pp.symtab, pp.reset.symtab);
1054 			pp.ro_state = pp.reset.ro_state;
1055 			pp.ro_mode = pp.reset.ro_mode;
1056 			pp.ro_option = pp.reset.ro_option;
1057 		}
1058 #if CHECKPOINT
1059 		if (pp.mode & DUMP)
1060 		{
1061 			if (!pp.pragma)
1062 				error(3, "#%s must be enabled for checkpoints", dirname(PRAGMA));
1063 			(*pp.pragma)(dirname(PRAGMA), pp.pass, keyname(X_CHECKPOINT), pp.checkpoint, 1);
1064 		}
1065 #endif
1066 		if (n = pp.filedeps.flags)
1067 		{
1068 			if (!(n & PP_deps_file))
1069 			{
1070 				pp.state |= NOTEXT;
1071 				pp.option |= KEEPNOTEXT;
1072 				pp.linesync = 0;
1073 			}
1074 			if (n & PP_deps_generated)
1075 				pp.mode |= GENDEPS;
1076 			if (n & PP_deps_local)
1077 				pp.mode &= ~HEADERDEPS;
1078 			else if (!(pp.mode & FILEDEPS))
1079 				pp.mode |= HEADERDEPS;
1080 			pp.mode |= FILEDEPS;
1081 		}
1082 
1083 		/*
1084 		 * push the main input file -- special case for hosted mark
1085 		 */
1086 
1087 		if (pp.firstdir->type & TYPE_HOSTED)
1088 			pp.mode |= MARKHOSTED;
1089 		else
1090 			pp.mode &= ~MARKHOSTED;
1091 #if CHECKPOINT
1092 		if (!(pp.mode & DUMP))
1093 #endif
1094 		{
1095 			if (!(p = error_info.file))
1096 				p = "";
1097 			else
1098 			{
1099 				error_info.file = 0;
1100 				if (*p)
1101 				{
1102 					pathcanon(p, 0);
1103 					p = ppsetfile(p)->name;
1104 				}
1105 			}
1106 			PUSH_FILE(p, 0);
1107 		}
1108 		if (pp.mode & FILEDEPS)
1109 		{
1110 			if (s = strrchr(error_info.file, '/'))
1111 				s++;
1112 			else
1113 				s = error_info.file;
1114 			if (!*s)
1115 				s = "-";
1116 			s = strcpy(pp.tmpbuf, s);
1117 			if ((t = p = strrchr(s, '.')) && (*++p == 'c' || *p == 'C'))
1118 			{
1119 				if (c = *++p)
1120 					while (*++p == c);
1121 				if (*p)
1122 					t = 0;
1123 				else
1124 					t++;
1125 			}
1126 			if (!t)
1127 			{
1128 				t = s + strlen(s);
1129 				*t++ = '.';
1130 			}
1131 			*(t + 1) = 0;
1132 			if (pp.state & NOTEXT)
1133 				pp.filedeps.sp = sfstdout;
1134 			else
1135 			{
1136 				*t = 'd';
1137 				if (!(pp.filedeps.sp = sfopen(NiL, s, "w")))
1138 					error(ERROR_SYSTEM|3, "%s: cannot create", s);
1139 			}
1140 			*t = 'o';
1141 			pp.column = sfprintf(pp.filedeps.sp, "%s :", s);
1142 			if (*error_info.file)
1143 				pp.column += sfprintf(pp.filedeps.sp, " %s", error_info.file);
1144 		}
1145 		if (xp = pp.firsttx)
1146 		{
1147 			if (!(sp = sfstropen()))
1148 				error(3, "temporary buffer allocation error");
1149 			while (xp)
1150 			{
1151 				sfprintf(sp, "#%s \"%s\"\n", dirname(INCLUDE), xp->value);
1152 				xp = xp->next;
1153 			}
1154 			t = sfstruse(sp);
1155 			PUSH_BUFFER("options", t, 1);
1156 			sfstrclose(sp);
1157 		}
1158 		break;
1159 	case PP_INPUT:
1160 #if CHECKPOINT && POOL
1161 		if (!(pp.mode & DUMP) || pp.pool.input)
1162 #else
1163 #if CHECKPOINT
1164 		if (!(pp.mode & DUMP))
1165 #else
1166 #if POOL
1167 		if (pp.pool.input)
1168 #endif
1169 #endif
1170 #endif
1171 		{
1172 			p = va_arg(ap, char*);
1173 			if (!error_info.file)
1174 				error_info.file = p;
1175 			close(0);
1176 			if (open(p, O_RDONLY) != 0)
1177 				error(ERROR_SYSTEM|3, "%s: cannot read", p);
1178 			if (strmatch(p, "*.(s|S|as|AS|asm|ASM)"))
1179 			{
1180 				set(&pp.mode, CATLITERAL, 0);
1181 				ppop(PP_SPACEOUT, 1);
1182 			}
1183 			break;
1184 		}
1185 		/*FALLTHROUGH*/
1186 	case PP_TEXT:
1187 		if (pp.initialized)
1188 			goto before;
1189 		if ((p = va_arg(ap, char*)) && *p)
1190 		{
1191 			if (pp.lasttx)
1192 				pp.lasttx = pp.lasttx->next = newof(0, struct oplist, 1, 0);
1193 			else
1194 				pp.firsttx = pp.lasttx = newof(0, struct oplist, 1, 0);
1195 			pp.lasttx->op = op;
1196 			pp.lasttx->value = p;
1197 		}
1198 		break;
1199 	case PP_KEYARGS:
1200 		if (pp.initialized)
1201 			goto before;
1202 		set(&pp.option, KEYARGS, va_arg(ap, int));
1203 		if (pp.option & KEYARGS)
1204 #if MACKEYARGS
1205 			set(&pp.mode, CATLITERAL, 1);
1206 #else
1207 			error(3, "preprocessor not compiled with macro keyword arguments enabled [MACKEYARGS]");
1208 #endif
1209 		break;
1210 	case PP_LINE:
1211 		pp.linesync = va_arg(ap, PPLINESYNC);
1212 		break;
1213 	case PP_LINEBASE:
1214 		if (va_arg(ap, int))
1215 			pp.flags |= PP_linebase;
1216 		else
1217 			pp.flags &= ~PP_linebase;
1218 		break;
1219 	case PP_LINEFILE:
1220 		if (va_arg(ap, int))
1221 			pp.flags |= PP_linefile;
1222 		else
1223 			pp.flags &= ~PP_linefile;
1224 		break;
1225 	case PP_LINEID:
1226 		if (!(p = va_arg(ap, char*)))
1227 			pp.lineid = "";
1228 		else if (*p != '-')
1229 			pp.lineid = strdup(p);
1230 		else
1231 			pp.option |= IGNORELINE;
1232 		break;
1233 	case PP_LINETYPE:
1234 		if ((n = va_arg(ap, int)) >= 1)
1235 			pp.flags |= PP_linetype;
1236 		else
1237 			pp.flags &= ~PP_linetype;
1238 		if (n >= 2)
1239 			pp.flags |= PP_linehosted;
1240 		else
1241 			pp.flags &= ~PP_linehosted;
1242 		break;
1243 	case PP_LOCAL:
1244 		if (pp.initialized)
1245 			goto before;
1246 		pp.ignoresrc++;
1247 		pp.stddirs = pp.lastdir;
1248 		if (!(pp.ro_option & PREFIX))
1249 			pp.option &= ~PREFIX;
1250 		break;
1251 	case PP_MACREF:
1252 		pp.macref = va_arg(ap, PPMACREF);
1253 		break;
1254 	case PP_MULTIPLE:
1255 		set(&pp.mode, ALLMULTIPLE, va_arg(ap, int));
1256 		break;
1257 	case PP_NOHASH:
1258 		set(&pp.option, NOHASH, va_arg(ap, int));
1259 		break;
1260 	case PP_NOISE:
1261 		op = va_arg(ap, int);
1262 		set(&pp.option, NOISE, op);
1263 		set(&pp.option, NOISEFILTER, op < 0);
1264 		break;
1265 	case PP_OPTARG:
1266 		pp.optarg = va_arg(ap, PPOPTARG);
1267 		break;
1268 	case PP_OUTPUT:
1269 		pp.outfile = va_arg(ap, char*);
1270 		if (identical(pp.outfile, 0))
1271 			error(3, "%s: identical to input", pp.outfile);
1272 		close(1);
1273 		if (open(pp.outfile, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) != 1)
1274 			error(ERROR_SYSTEM|3, "%s: cannot create", pp.outfile);
1275 		break;
1276 	case PP_PASSTHROUGH:
1277 		if (!(pp.state & COMPILE))
1278 			set(&pp.state, PASSTHROUGH, va_arg(ap, int));
1279 		break;
1280 	case PP_PEDANTIC:
1281 		set(&pp.mode, PEDANTIC, va_arg(ap, int));
1282 		break;
1283 	case PP_PLUSCOMMENT:
1284 		set(&pp.option, PLUSCOMMENT, va_arg(ap, int));
1285 		if (pp.initialized)
1286 			ppfsm(FSM_PLUSPLUS, NiL);
1287 		break;
1288 	case PP_PLUSPLUS:
1289 		set(&pp.option, PLUSPLUS, va_arg(ap, int));
1290 		set(&pp.option, PLUSCOMMENT, va_arg(ap, int));
1291 		if (pp.initialized)
1292 			ppfsm(FSM_PLUSPLUS, NiL);
1293 		break;
1294 	case PP_POOL:
1295 		if (pp.initialized)
1296 			goto before;
1297 		if (va_arg(ap, int))
1298 		{
1299 #if POOL
1300 			pp.pool.input = dup(0);
1301 			pp.pool.output = dup(1);
1302 			p = "/dev/null";
1303 			if (!identical(p, 0))
1304 			{
1305 				if (!identical(p, 1))
1306 					ppop(PP_OUTPUT, p);
1307 				ppop(PP_INPUT, p);
1308 			}
1309 #else
1310 			error(3, "preprocessor not compiled with input pool enabled [POOL]");
1311 #endif
1312 		}
1313 		break;
1314 	case PP_PRAGMA:
1315 		pp.pragma = va_arg(ap, PPPRAGMA);
1316 		break;
1317 	case PP_PRAGMAFLAGS:
1318 		if (p = va_arg(ap, char*))
1319 		{
1320 			n = OPT_GLOBAL;
1321 			if (*p == '-')
1322 				p++;
1323 			else
1324 				n |= OPT_PASS;
1325 			if ((c = (int)hashref(pp.strtab, p)) > 0 && c <= X_last_option)
1326 				pp.optflags[c] = n;
1327 		}
1328 		break;
1329 	case PP_PROBE:
1330 		pp.probe = va_arg(ap, char*);
1331 		break;
1332 	case PP_QUOTE:
1333 		p = va_arg(ap, char*);
1334 		c = va_arg(ap, int);
1335 		if (p)
1336 			ppfsm(c ? FSM_QUOTADD : FSM_QUOTDEL, p);
1337 		break;
1338 	case PP_REGUARD:
1339 		set(&pp.option, REGUARD, va_arg(ap, int));
1340 		break;
1341 	case PP_RESERVED:
1342 		if ((pp.state & COMPILE) && (p = va_arg(ap, char*)))
1343 		{
1344 			if (!(sp = sfstropen()))
1345 				error(3, "temporary buffer allocation error");
1346 			sfputr(sp, p, -1);
1347 			p = sfstruse(sp);
1348 			if (s = strchr(p, '='))
1349 				*s++ = 0;
1350 			else
1351 				s = p;
1352 			while (*s == '_')
1353 				s++;
1354 			for (t = s + strlen(s); t > s && *(t - 1) == '_'; t--);
1355 			if (*t == '_')
1356 				*t = 0;
1357 			else
1358 				t = 0;
1359 			op = ((key = ppkeyref(pp.symtab, s)) && (key->sym.flags & SYM_LEX)) ? key->lex : T_NOISE;
1360 			if (pp.test & 0x0400)
1361 				error(1, "reserved#1 `%s' %d", s, op);
1362 			if (t)
1363 				*t = '_';
1364 			if (!(key = ppkeyget(pp.symtab, p)))
1365 				key = ppkeyset(pp.symtab, NiL);
1366 			else if (!(key->sym.flags & SYM_LEX))
1367 			{
1368 				struct ppsymbol	tmp;
1369 
1370 				tmp = key->sym;
1371 				hashlook(pp.symtab, p, HASH_DELETE, NiL);
1372 				key = ppkeyset(pp.symtab, NiL);
1373 				key->sym.flags = tmp.flags;
1374 				key->sym.macro = tmp.macro;
1375 				key->sym.value = tmp.value;
1376 				key->sym.hidden = tmp.hidden;
1377 			}
1378 			if (!(key->sym.flags & SYM_KEYWORD))
1379 			{
1380 				key->sym.flags |= SYM_KEYWORD|SYM_LEX;
1381 				key->lex = op;
1382 				if (pp.test & 0x0400)
1383 					error(1, "reserved#2 `%s' %d", p, op);
1384 			}
1385 			sfstrclose(sp);
1386 		}
1387 		break;
1388 	case PP_SPACEOUT:
1389 		set(&pp.state, SPACEOUT, va_arg(ap, int));
1390 		break;
1391 	case PP_STANDALONE:
1392 		if (pp.initialized)
1393 			goto before;
1394 		pp.standalone = 1;
1395 		break;
1396 	case PP_STANDARD:
1397 		if ((pp.lastdir->next->name = ((p = va_arg(ap, char*)) && *p) ? p : NiL) && !stat(p, &st))
1398 			SAVEID(&pp.lastdir->next->id, &st);
1399 		for (dp = pp.firstdir; dp; dp = dp->next)
1400 			if (dp->name)
1401 				for (hp = pp.firstdir; hp != dp; hp = hp->next)
1402 					if (hp->name && SAMEID(&hp->id, &dp->id))
1403 					{
1404 						hp->c = dp->c;
1405 						if (dp->type & TYPE_HOSTED)
1406 							hp->type |= TYPE_HOSTED;
1407 						else
1408 							hp->type &= ~TYPE_HOSTED;
1409 					}
1410 		break;
1411 	case PP_STRICT:
1412 		set(&pp.state, TRANSITION, 0);
1413 		pp.flags &= ~PP_transition;
1414 		set(&pp.state, STRICT, va_arg(ap, int));
1415 		if (pp.state & STRICT)
1416 			pp.flags |= PP_strict;
1417 		else
1418 			pp.flags &= ~PP_strict;
1419 		break;
1420 	case PP_TEST:
1421 		if (p = va_arg(ap, char*))
1422 			for (;;)
1423 			{
1424 				while (*p == ' ' || *p == '\t') p++;
1425 				for (s = p; n = *s; s++)
1426 					if (n == ',' || n == ' ' || n == '\t')
1427 					{
1428 						*s++ = 0;
1429 						break;
1430 					}
1431 				if (!*p)
1432 					break;
1433 				n = 0;
1434 				if (*p == 'n' && *(p + 1) == 'o')
1435 				{
1436 					p += 2;
1437 					op = 0;
1438 				}
1439 				else
1440 					op = 1;
1441 				if (streq(p, "count"))
1442 					n = TEST_count;
1443 				else if (streq(p, "hashcount"))
1444 					n = TEST_hashcount;
1445 				else if (streq(p, "hashdump"))
1446 					n = TEST_hashdump;
1447 				else if (streq(p, "hit"))
1448 					n = TEST_hit;
1449 				else if (streq(p, "init"))
1450 					n = TEST_noinit|TEST_INVERT;
1451 				else if (streq(p, "noise"))
1452 					n = TEST_nonoise|TEST_INVERT;
1453 				else if (streq(p, "proto"))
1454 					n = TEST_noproto|TEST_INVERT;
1455 				else if (*p >= '0' && *p <= '9')
1456 					n = strtoul(p, NiL, 0);
1457 				else
1458 				{
1459 					error(1, "%s: unknown test", p);
1460 					break;
1461 				}
1462 				if (n & TEST_INVERT)
1463 				{
1464 					n &= ~TEST_INVERT;
1465 					op = !op;
1466 				}
1467 				if (op)
1468 					pp.test |= n;
1469 				else
1470 					pp.test &= ~n;
1471 				p = s;
1472 				debug((-4, "test = 0%o", pp.test));
1473 			}
1474 		break;
1475 	case PP_TRANSITION:
1476 		set(&pp.state, STRICT, 0);
1477 		pp.flags &= ~PP_strict;
1478 		set(&pp.state, TRANSITION, va_arg(ap, int));
1479 		if (pp.state & TRANSITION)
1480 			pp.flags |= PP_transition;
1481 		else
1482 			pp.flags &= ~PP_transition;
1483 		break;
1484 	case PP_TRUNCATE:
1485 		if (pp.initialized)
1486 			goto before;
1487 		if ((op = va_arg(ap, int)) < 0)
1488 			op = 0;
1489 		set(&pp.option, TRUNCATE, op);
1490 		if (pp.option & TRUNCATE)
1491 		{
1492 			Hash_bucket_t*		b;
1493 			Hash_bucket_t*		p;
1494 			Hash_position_t*	pos;
1495 			Hash_table_t*		tab;
1496 
1497 			pp.truncate = op;
1498 			tab = pp.symtab;
1499 			pp.symtab = hashalloc(NiL, HASH_set, tab ? HASH_ALLOCATE : 0, HASH_compare, trunccomp, HASH_hash, trunchash, HASH_name, "truncate", 0);
1500 			if (tab && (pos = hashscan(tab, 0)))
1501 			{
1502 				if (p = hashnext(pos))
1503 					do
1504 					{
1505 						b = hashnext(pos);
1506 						hashlook(pp.symtab, (char*)p, HASH_BUCKET|HASH_INSTALL, NiL);
1507 					} while (p = b);
1508 				hashdone(pos);
1509 			}
1510 		}
1511 		else
1512 			pp.truncate = 0;
1513 		break;
1514 	case PP_VENDOR:
1515 		p = va_arg(ap, char*);
1516 		c = va_arg(ap, int) != 0;
1517 		if (!p || !*p)
1518 			for (dp = pp.firstdir; dp; dp = dp->next)
1519 				dp->type &= ~TYPE_VENDOR;
1520 		else if (streq(p, "-"))
1521 		{
1522 			for (dp = pp.firstdir; dp; dp = dp->next)
1523 				if (c)
1524 					dp->type |= TYPE_VENDOR;
1525 				else
1526 					dp->type &= ~TYPE_VENDOR;
1527 		}
1528 		else if (!stat((pathcanon(p, 0), p), &st))
1529 		{
1530 			c = 0;
1531 			for (dp = pp.firstdir; dp; dp = dp->next)
1532 			{
1533 				if (!c && ((dp->type & TYPE_VENDOR) || dp->name && SAMEID(&dp->id, &st)))
1534 					c = 1;
1535 				if (c)
1536 					dp->type |= TYPE_VENDOR;
1537 				else
1538 					dp->type &= ~TYPE_VENDOR;
1539 			}
1540 		}
1541 		break;
1542 	case PP_WARN:
1543 		set(&pp.state, WARN, va_arg(ap, int));
1544 		break;
1545 	before:
1546 		error(3, "ppop(%d): preprocessor operation must be done before PP_INIT", op);
1547 		break;
1548 	default:
1549 		error(3, "ppop(%d): invalid preprocessor operation", op);
1550 		break;
1551 	}
1552 	va_end(ap);
1553 }
1554