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