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
43static void
44set(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
65static void
66inithash(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
82char*
83ppkeyname(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
103void
104ppmapinclude(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
234static int
235identical(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
249static int
250trunccomp(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
261static unsigned int
262trunchash(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
274static int
275context(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
294static int
295unguard(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
307static void
308undefine(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
329void
330ppop(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