xref: /titanic_50/usr/src/lib/libpp/common/ppsearch.c (revision d6114e2d100d9ec3b45f9968d45ac2e3a0827af0)
1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 1986-2009 AT&T Intellectual Property          *
5 *                      and is licensed under the                       *
6 *                  Common Public License, Version 1.0                  *
7 *                    by AT&T Intellectual Property                     *
8 *                                                                      *
9 *                A copy of the License is available at                 *
10 *            http://www.opensource.org/licenses/cpl1.0.txt             *
11 *         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
12 *                                                                      *
13 *              Information and Software Systems Research               *
14 *                            AT&T Research                             *
15 *                           Florham Park NJ                            *
16 *                                                                      *
17 *                 Glenn Fowler <gsf@research.att.com>                  *
18 *                                                                      *
19 ***********************************************************************/
20 #pragma prototyped
21 /*
22  * Glenn Fowler
23  * AT&T Research
24  *
25  * include file search support
26  */
27 
28 #include "pplib.h"
29 
30 #define SEARCH_NEXT	(SEARCH_USER<<1)/* search for next (uncover)	*/
31 #define SEARCH_SKIP	(SEARCH_USER<<2)/* current binding skipped	*/
32 #define SEARCH_TEST	(SEARCH_USER<<3)/* test for binding		*/
33 #define SEARCH_FOUND	(SEARCH_USER<<4)/* current binding found	*/
34 
35 #define COLUMN_TAB	7
36 #define COLUMN_MAX	72
37 
38 #if ARCHIVE
39 
40 #include <vdb.h>
41 #include <ls.h>
42 
43 #endif
44 
45 /*
46  * multiple include test
47  * fp is a canonicalized ppfile pointer
48  *
49  * test
50  *
51  *	INC_CLEAR	can be included again
52  *	INC_TEST	test if include required
53  *	<symbol>	ifndef guard symbol
54  *
55  * test!=INC_CLEAR returns 1 if file can be included again
56  *
57  * NOTE:
58  *
59  *  (1)	different hard links to the same file are treated as
60  *	different files
61  *
62  *  (2)	symbolic links in combination with .. may cause two
63  *	different files to be treated as the same file:
64  *
65  *	"../h/<file>" == "/usr/include/sys/../h/<file>" -> "/usr/include/h/<file>"
66  *	"h/<file>" -> "/usr/include/h/<file>"
67  */
68 
69 int
70 ppmultiple(register struct ppfile* fp, register struct ppsymbol* test)
71 {
72 	register struct ppsymbol*	status;
73 
74 	status = fp->guard;
75 	message((-3, "search: %s: status=%s%s test=%s", fp->name, status == INC_CLEAR ? "[CLEAR]" : status == INC_TEST ? "[ONCE]" : status == INC_IGNORE ? "[IGNORE]" : status->name, (pp.mode & HOSTED) ? "[HOSTED]" : "", test == INC_CLEAR ? "[CLEAR]" : test == INC_TEST ? "[TEST]" : test->name));
76 	if (status == INC_IGNORE)
77 	{
78 		message((-2, "%s: ignored [%s]", fp->name, pp.ignore));
79 		return 0;
80 	}
81 	if (test == INC_TEST)
82 	{
83 		if (status != INC_CLEAR)
84 		{
85 			if (status == INC_TEST || status->macro)
86 			{
87 				if ((pp.mode & (ALLMULTIPLE|LOADING)) == LOADING)
88 					fp->guard = INC_IGNORE;
89 				if ((pp.state & WARN) && (pp.mode & (HOSTED|MARKHOSTED|RELAX|PEDANTIC)) == PEDANTIC)
90 					error(1, "%s: ignored -- already included", fp->name);
91 				else
92 					message((-3, "%s: ignored -- already included", fp->name));
93 				return 0;
94 			}
95 			return 1;
96 		}
97 		if ((pp.mode & (ALLMULTIPLE|LOADING)) == LOADING)
98 			test = INC_IGNORE;
99 		else
100 			return 1;
101 	}
102 	fp->guard = test;
103 	return 1;
104 }
105 
106 /*
107  * search for file using directories in dp
108  */
109 
110 static int
111 search(register struct ppfile* fp, register struct ppdirs* dp, int type, int flags)
112 {
113 	register char*		prefix;
114 	register struct ppdirs*	up;
115 	register struct ppfile*	xp;
116 	struct ppfile*		mp;
117 	int			fd;
118 	int			index;
119 	int			need;
120 	int			markhosted;
121 	char*			t;
122 
123 	if (!(pp.option & PREFIX))
124 		prefix = 0;
125 	else if ((prefix = strrchr(fp->name, '/')) && prefix > fp->name)
126 	{
127 		*prefix = 0;
128 		t = ppsetfile(fp->name)->name;
129 		*prefix = '/';
130 		prefix = t;
131 	}
132 	message((-3, "search: %s %s%s%s%s%s%s type=%s prefix=%s flags=|%s%s%s%s%s%s start=%s=\"%s\" pre=%s lcl=%s vnd=%s std=%s cur=%s",
133 		fp->name,
134 		(flags & SEARCH_INCLUDE) ? "include" : "exists",
135 		(flags & SEARCH_VENDOR) ? " vendor" : "",
136 		(flags & SEARCH_HOSTED) ? " hosted" : "",
137 		(flags & SEARCH_NEXT) ? " next" : "",
138 		(flags & SEARCH_SKIP) ? " skip" : "",
139 		(flags & SEARCH_TEST) ? " test" : "",
140 		type == T_HEADER ? "<*>" : "\"*\"", prefix,
141 		(fp->flags & INC_SELF) ? "SELF|" : "",
142 		(fp->flags & INC_EXISTS) ? "EXISTS|" : "",
143 		(fp->flags & INC_BOUND(INC_PREFIX)) ? "PREFIX|" : "",
144 		(fp->flags & INC_BOUND(INC_LOCAL)) ? "LOCAL|" : "",
145 		(fp->flags & INC_BOUND(INC_VENDOR)) ? "VENDOR|" : "",
146 		(fp->flags & INC_BOUND(INC_STANDARD)) ? "STANDARD|" : "",
147 		dp ? (dp->index == INC_PREFIX ? "pre" : dp->index == INC_LOCAL ? "lcl" : dp->index == INC_VENDOR ? "vnd" : "std") : NiL,
148 		dp ? dp->name : NiL,
149 		!(fp->flags & INC_MEMBER(INC_PREFIX)) && (xp = fp->bound[INC_PREFIX]) ? xp->name : NiL,
150 		!(fp->flags & INC_MEMBER(INC_LOCAL)) && (xp = fp->bound[INC_LOCAL]) ? xp->name : NiL,
151 		!(fp->flags & INC_MEMBER(INC_VENDOR)) && (xp = fp->bound[INC_VENDOR]) ? xp->name : NiL,
152 		!(fp->flags & INC_MEMBER(INC_STANDARD)) && (xp = fp->bound[INC_STANDARD]) ? xp->name : NiL,
153 		error_info.file
154 		));
155 	if (flags & SEARCH_HOSTED)
156 		need = TYPE_HOSTED;
157 	else if (flags & SEARCH_VENDOR)
158 		need = TYPE_VENDOR;
159 	else
160 		need = TYPE_INCLUDE;
161 	for (index = -1; dp; dp = dp->next)
162 		if (dp->type & need)
163 	{
164 		message((-3, "search: fp=%s need=%02x index=%d dp=%s type=%02x index=%d", fp->name, need, index, dp->name, dp->type, dp->index));
165 #if ARCHIVE
166 		if (!(dp->type & (TYPE_ARCHIVE|TYPE_DIRECTORY)))
167 		{
168 			struct stat	st;
169 
170 			if (stat(dp->name, &st))
171 			{
172 				message((-3, "search: omit %s", dp->name));
173 				dp->type = 0;
174 				continue;
175 			}
176 			if (S_ISREG(st.st_mode))
177 			{
178 				register char*		s;
179 				char*			e;
180 				int			delimiter;
181 				int			variant;
182 				unsigned long		siz;
183 				unsigned long		off;
184 				struct ppmember*	ap;
185 				Sfio_t*			sp;
186 
187 				/*
188 				 * check for vdb header archive
189 				 */
190 
191 				if (!(sp = sfopen(NiL, dp->name, "r")))
192 				{
193 					error(ERROR_SYSTEM|1, "%s: ignored -- cannot open", dp->name);
194 					dp->type = 0;
195 					continue;
196 				}
197 				variant = sfsprintf(pp.tmpbuf, MAXTOKEN, "%c%s%c%s:archive", VDB_DELIMITER, VDB_MAGIC, VDB_DELIMITER, pp.pass);
198 				if (!(s = sfgetr(sp, '\n', 1)) || !strneq(s, pp.tmpbuf, variant))
199 				{
200 					sfclose(sp);
201 					error(1, "%s: ignored -- not a directory or archive", dp->name);
202 					dp->type = 0;
203 					continue;
204 				}
205 
206 				/*
207 				 * parse the options
208 				 */
209 
210 				dp->type |= TYPE_ARCHIVE;
211 				for (s += variant;;)
212 				{
213 					while (*s == ' ') s++;
214 					e = s;
215 					for (t = 0; *s && *s != ' '; s++)
216 						if (*s == '=')
217 						{
218 							*s = 0;
219 							t = s + 1;
220 						}
221 					if (*s)
222 						*s++ = 0;
223 					if (!*e)
224 						break;
225 					switch ((int)hashref(pp.strtab, e))
226 					{
227 					case X_CHECKPOINT:
228 #if CHECKPOINT
229 						dp->type |= TYPE_CHECKPOINT;
230 						break;
231 #else
232 						error(1, "preprocessor not compiled with checkpoint enabled");
233 						goto notvdb;
234 #endif
235 					case X_HIDE:
236 
237 						if (t)
238 							error(1, "%s: %s: archive option value ignored", e);
239 						if (e = strrchr(dp->name, '/'))
240 							*e = 0;
241 						else
242 							dp->name = ".";
243 						break;
244 					case X_MAP:
245 						if (!t)
246 							error(1, "%s: archive option value expected", e);
247 						else
248 							dp->name = strdup(t);
249 						break;
250 					default:
251 						error(1, "%s: unknown archive option", e);
252 						break;
253 					}
254 				}
255 				if (sfseek(sp, -(VDB_LENGTH + 1), SEEK_END) <= 0 || !(s = sfgetr(sp, '\n', 1)))
256 				{
257 				notvdb:
258 					sfclose(sp);
259 					error(1, "%s: ignored -- cannot load archive", dp->name);
260 					dp->type = 0;
261 					continue;
262 				}
263 				if (variant = *s != 0)
264 					s++;
265 				else if (!(s = sfgetr(sp, '\n', 1)))
266 					goto notvdb;
267 				if (sfvalue(sp) != (VDB_LENGTH + variant))
268 					goto notvdb;
269 				if (!strneq(s, VDB_DIRECTORY, sizeof(VDB_DIRECTORY) - 1))
270 					goto notvdb;
271 				delimiter = s[VDB_OFFSET - 1];
272 				off = strtol(s + VDB_OFFSET, NiL, 10) - sizeof(VDB_DIRECTORY);
273 				siz = strtol(s + VDB_SIZE, NiL, 10);
274 				if (sfseek(sp, off, SEEK_SET) != off)
275 					goto notvdb;
276 				if (!(s = sfreserve(sp, siz + 1, 0)))
277 					goto notvdb;
278 				s[siz] = 0;
279 				if (!strneq(s, VDB_DIRECTORY, sizeof(VDB_DIRECTORY)) - 1)
280 					goto notvdb;
281 				if (!(s = strchr(s, '\n')))
282 					goto notvdb;
283 				s++;
284 				while (e = strchr(s, '\n'))
285 				{
286 					delimiter = variant ? *s++ : delimiter;
287 					if (!(t = strchr(s, delimiter)))
288 						break;
289 					*t = 0;
290 					if (!streq(s, VDB_DIRECTORY))
291 					{
292 						pathcanon(s, 0);
293 						ap = newof(0, struct ppmember, 1, 0);
294 						ap->archive = dp;
295 						ap->offset = strtol(t + 1, &t, 10);
296 						ap->size = strtol(t + 1, NiL, 10);
297 						xp = ppsetfile(s);
298 						xp->flags |= INC_MEMBER(dp->index);
299 						xp->bound[dp->index] = (struct ppfile*)ap;
300 if (pp.test & 0x0020) error(1, "VDB#%d %s %s index=%d data=<%lu,%lu>", __LINE__, dp->name, xp->name, index, ap->offset, ap->size);
301 					}
302 					s = e + 1;
303 				}
304 				if (sfseek(sp, 0L, SEEK_SET))
305 					goto notvdb;
306 				if (!(pp.test & 0x4000) &&
307 #if POOL
308 					(pp.pool.input || !(dp->type & TYPE_CHECKPOINT))
309 #else
310 					!(dp->type & TYPE_CHECKPOINT)
311 #endif
312 					&& (dp->info.buffer = sfreserve(sp, off, 0)))
313 					dp->type |= TYPE_BUFFER;
314 				else
315 				{
316 					dp->info.sp = sp;
317 #if POOL
318 					if (pp.pool.input)
319 						sfset(sp, SF_SHARE, 1);
320 #endif
321 				}
322 			}
323 			else
324 				dp->type |= TYPE_DIRECTORY;
325 		}
326 #endif
327 		if (streq(fp->name, "."))
328 			continue;
329 		if (prefix && *fp->name != '/' && dp->index != INC_PREFIX)
330 #if ARCHIVE
331 		if (dp->type & TYPE_DIRECTORY)
332 #endif
333 		{
334 			for (up = dp->info.subdir; up; up = up->next)
335 				if (up->name == prefix)
336 					break;
337 			if (!up)
338 			{
339 				up = newof(0, struct ppdirs, 1, 0);
340 				up->name = prefix;
341 				up->type = dp->type;
342 				up->next = dp->info.subdir;
343 				dp->info.subdir = up;
344 				if (!*dp->name)
345 					t = prefix;
346 				else
347 					sfsprintf(t = pp.path, PATH_MAX - 1, "%s/%s", dp->name, prefix);
348 				if (eaccess(t, X_OK))
349 				{
350 					message((-3, "search: omit %s", t));
351 					continue;
352 				}
353 				up->type |= TYPE_HOSTED;
354 			}
355 			else if (!(up->type & TYPE_HOSTED))
356 				continue;
357 		}
358 		mp = xp = 0;
359 		if (!(flags & SEARCH_NEXT) && index != dp->index && (!(need & TYPE_HOSTED) || dp->index == INC_STANDARD) && (!(need & TYPE_VENDOR) || dp->index == INC_VENDOR))
360 		{
361 			if (index >= 0 && !(fp->flags & INC_MEMBER(index)))
362 				fp->flags |= INC_BOUND(index);
363 			index = dp->index;
364 			if (fp->flags & INC_BOUND(index))
365 			{
366 				xp = fp->bound[index];
367 				if (index == INC_PREFIX)
368 				{
369 					if (*fp->name == '/' || !*dp->name)
370 						strcpy(pp.path, fp->name);
371 					else
372 						sfsprintf(pp.path, PATH_MAX - 1, "%s/%s", dp->name, fp->name);
373 					pathcanon(pp.path, 0);
374 					if (!xp || !streq(xp->name, pp.path))
375 					{
376 						fp->bound[index] = xp = ppsetfile(pp.path);
377 						if (dp->type & TYPE_HOSTED)
378 							xp->flags |= INC_HOSTED;
379 						if ((flags & SEARCH_INCLUDE) || (xp->flags & INC_EXISTS))
380 						{
381 							if (!(flags & SEARCH_INCLUDE))
382 								return 0;
383 							if (!ppmultiple(xp, INC_TEST))
384 							{
385 								if (flags & SEARCH_TEST)
386 									pp.include = xp->name;
387 								return 0;
388 							}
389 							mp = xp;
390 						}
391 					}
392 				}
393 				else if (!xp)
394 				{
395 					while (dp->next && dp->next->index == index)
396 						dp = dp->next;
397 					message((-3, "search: omit %s/%s", dp->name, fp->name));
398 					continue;
399 				}
400 				else
401 				{
402 					strcpy(pp.path, xp->name);
403 					if (!(flags & SEARCH_INCLUDE))
404 						return 0;
405 					if (!ppmultiple(xp, INC_TEST))
406 					{
407 						if (flags & SEARCH_TEST)
408 							pp.include = xp->name;
409 						return 0;
410 					}
411 					mp = xp;
412 				}
413 			}
414 		}
415 		if (!(fp->flags & INC_BOUND(index)) || (flags & SEARCH_NEXT))
416 		{
417 			if (*fp->name == '/' || !*dp->name)
418 				strcpy(pp.path, fp->name);
419 			else
420 				sfsprintf(pp.path, PATH_MAX - 1, "%s/%s", dp->name, fp->name);
421 			pathcanon(pp.path, 0);
422 			if (!(flags & SEARCH_SKIP))
423 			{
424 				int		found;
425 				struct ppinstk*	in;
426 
427 				if (streq(error_info.file, pp.path))
428 					found = 1;
429 				else
430 				{
431 					found = 0;
432 					for (in = pp.in; in; in = in->prev)
433 						if (in->type == IN_FILE && in->file && streq(in->file, pp.path))
434 						{
435 							found = 1;
436 							break;
437 						}
438 				}
439 				if (found)
440 				{
441 					flags |= SEARCH_FOUND;
442 					continue;
443 				}
444 				if (!(flags & SEARCH_FOUND))
445 					continue;
446 			}
447 		}
448 		if ((xp || (xp = ppgetfile(pp.path))) && (xp->flags & INC_SELF))
449 		{
450 			if (xp->flags & INC_EXISTS)
451 			{
452 				if (!(flags & SEARCH_INCLUDE))
453 					return 0;
454 				if (!(flags & SEARCH_NEXT) && mp != xp && (mp = xp) && !ppmultiple(xp, INC_TEST))
455 				{
456 					if (flags & SEARCH_TEST)
457 						pp.include = xp->name;
458 					return 0;
459 				}
460 			}
461 			else if (*fp->name == '/')
462 				break;
463 			else
464 				continue;
465 		}
466 		message((-3, "search: file=%s path=%s", fp->name, pp.path));
467 #if ARCHIVE
468 if (pp.test & 0x0040) error(1, "SEARCH#%d dir=%s%s%s%s%s file=%s%s path=%s index=%d", __LINE__, dp->name, (dp->type & TYPE_ARCHIVE) ? " ARCHIVE" : "",  (dp->type & TYPE_BUFFER) ? " BUFFER" : "", (dp->type & TYPE_CHECKPOINT) ? " CHECKPOINT" : "", (dp->type & TYPE_DIRECTORY) ? " DIRECTORY" : "", fp->name, (fp->flags & INC_MEMBER(index)) ? " MEMBER" : "", pp.path, index);
469 		if ((fp->flags & INC_MEMBER(index)) && ((struct ppmember*)fp->bound[index])->archive == dp)
470 		{
471 			fd = 0;
472 			pp.member = (struct ppmember*)fp->bound[index];
473 if (pp.test & 0x0010) error(1, "SEARCH#%d file=%s path=%s index=%d data=<%lu,%lu>", __LINE__, fp->name, pp.path, index, pp.member->offset, pp.member->size);
474 		}
475 		else if (!(dp->type & TYPE_DIRECTORY))
476 			continue;
477 		else
478 #endif
479 		{
480 			pp.member = 0;
481 			fd = (flags & SEARCH_INCLUDE) ? open(pp.path, O_RDONLY) : eaccess(pp.path, R_OK);
482 		}
483 		if (fd >= 0)
484 		{
485 			pp.found = dp;
486 			if ((pp.option & (PLUSPLUS|NOPROTO)) == PLUSPLUS && !(pp.test & TEST_noproto))
487 			{
488 				if (dp->c)
489 					pp.mode |= MARKC;
490 				else
491 					pp.mode &= ~MARKC;
492 			}
493 			if (xp)
494 				markhosted = xp->flags & INC_HOSTED;
495 			else if (!(markhosted = (dp->type & TYPE_HOSTED)) && dp->index == INC_PREFIX && (pp.mode & (FILEDEPS|HEADERDEPS|INIT)) == FILEDEPS)
496 			{
497 				up = dp;
498 				while ((up = up->next) && !streq(up->name, dp->name));
499 				if (up && (up->type & TYPE_HOSTED))
500 					markhosted = 1;
501 			}
502 			if (markhosted)
503 				pp.mode |= MARKHOSTED;
504 			else
505 				pp.mode &= ~MARKHOSTED;
506 			xp = ppsetfile(pp.path);
507 			if (markhosted)
508 				xp->flags |= INC_HOSTED;
509 			message((-2, "search: %s -> %s%s%s", fp->name, pp.path, (pp.mode & MARKC) ? " [C]" : "", (pp.mode & MARKHOSTED) ? " [hosted]" : ""));
510 #if ARCHIVE
511 			if (!pp.member)
512 			{
513 #endif
514 				fp->flags |= INC_BOUND(index);
515 				fp->bound[index] = xp;
516 				if ((index == INC_STANDARD || index == INC_VENDOR) && type != T_HEADER && !(fp->flags & INC_BOUND(INC_LOCAL)))
517 				{
518 					fp->flags |= INC_BOUND(INC_LOCAL);
519 					fp->bound[INC_LOCAL] = xp;
520 				}
521 #if ARCHIVE
522 			}
523 #endif
524 			xp->flags |= INC_SELF|INC_EXISTS;
525 			if (flags & SEARCH_INCLUDE)
526 			{
527 				if ((pp.prefix = prefix) || (pp.prefix = pp.in->prefix))
528 					message((-2, "search: %s: prefix=%s", xp->name, pp.prefix));
529 				if (!(pp.mode & ALLMULTIPLE))
530 				{
531 					if (xp->guard == INC_CLEAR || xp == mp)
532 						xp->guard = INC_TEST;
533 					else
534 					{
535 						if ((pp.state & WARN) && (pp.mode & (HOSTED|MARKHOSTED|RELAX|PEDANTIC)) == PEDANTIC)
536 							error(1, "%s: ignored -- already included", xp->name);
537 						else
538 							message((-3, "%s: ignored -- already included", xp->name));
539 						xp->guard = fp->guard = INC_IGNORE;
540 #if ARCHIVE
541 						if (!pp.member)
542 #endif
543 						if (fd > 0)
544 							close(fd);
545 						if (flags & SEARCH_TEST)
546 							pp.include = xp->name;
547 						return 0;
548 					}
549 				}
550 				pp.include = xp->name;
551 				if ((pp.mode & (FILEDEPS|INIT)) == FILEDEPS && ((pp.mode & HEADERDEPS) || !(pp.mode & MARKHOSTED)) && !(xp->flags & INC_LISTED))
552 				{
553 					xp->flags |= INC_LISTED;
554 					if ((pp.column + strlen(xp->name)) >= COLUMN_MAX)
555 					{
556 						sfprintf(pp.filedeps.sp, " \\\n");
557 						pp.column = COLUMN_TAB;
558 						index = '\t';
559 					}
560 					else
561 						index = ' ';
562 					pp.column += sfprintf(pp.filedeps.sp, "%c%s", index, xp->name);
563 				}
564 			}
565 			return fd;
566 		}
567 		if (xp)
568 			xp->flags |= INC_SELF;
569 		if (errno == EMFILE)
570 			error(3, "%s: too many open files", fp->name);
571 		else if (errno != ENOENT && errno != ENOTDIR)
572 			error(ERROR_SYSTEM|1, "%s: cannot open file for reading", pp.path);
573 		if (*fp->name == '/')
574 			break;
575 	}
576 	strcpy(pp.path, fp->name);
577 	message((-2, "search: %s%s not found", (flags & SEARCH_NEXT) ? "next " : "", fp->name));
578 	return -1;
579 }
580 
581 /*
582  * search for an include file
583  * if (flags&SEARCH_INCLUDE) then
584  *	if file found then open read file descriptor returned
585  *		with pp.path set to the full path and
586  *		pp.prefix set to the directory prefix
587  *	otherwise 0 returned if file found but ignored
588  *	otherwise -1 returned
589  * otherwise
590  *	if file found then 0 returned
591  *	otherwise -1 returned
592  */
593 
594 int
595 ppsearch(char* file, int type, int flags)
596 {
597 	register struct ppfile*	fp;
598 	register char*		s;
599 	register struct ppdirs*	dp;
600 	struct oplist*		cp;
601 	struct ppfile*		xp;
602 	int			dospath;
603 	int			chop;
604 	int			fd;
605 	int			index;
606 	char			name[MAXTOKEN + 1];
607 
608 	pp.include = 0;
609 	fd = -1;
610 	chop = 0;
611 	if (s = strchr(file, '\\'))
612 	{
613 		do *s++ = '/'; while (s = strchr(s, '\\'));
614 		dospath = 1;
615 	}
616 	else
617 		dospath = 0;
618  again:
619 	pathcanon(file, 0);
620 	if (chop)
621 		for (cp = pp.chop; cp; cp = cp->next)
622 			if (strneq(file, cp->value, cp->op))
623 			{
624 				if (cp->value[cp->op + 1])
625 				{
626 					sfsprintf(name, sizeof(name) - 1, "%s%s", cp->value + cp->op + 1, file + cp->op);
627 					message((-2, "search: %s -> %s", file, name));
628 					file = name;
629 				}
630 				else if (strchr(file + cp->op, '/'))
631 				{
632 					message((-2, "search: %s -> %s", file, file + cp->op));
633 					file += cp->op;
634 				}
635 				break;
636 			}
637 	fp = ppsetfile(file);
638 	while ((fp->flags & INC_MAPALL) || (fp->flags & INC_MAPHOSTED) && (pp.mode & HOSTED) || (fp->flags & INC_MAPNOHOSTED) && !(pp.mode & HOSTED))
639 	{
640 		if (!(xp = fp->bound[type == T_HEADER ? INC_STANDARD : INC_LOCAL]) || xp == fp)
641 			break;
642 		message((-1, "map: %s -> %s", fp->name, xp->name));
643 		fp = xp;
644 	}
645 	if ((fp->flags & INC_MAPNOLOCAL) && (pp.mode & HOSTED))
646 		flags |= SEARCH_HOSTED;
647 	else if (pp.vendor)
648 		flags |= SEARCH_VENDOR;
649 	pp.original = fp;
650 	if (type == T_HEADER && strneq(fp->name, "...", 3) && (!fp->name[3] || fp->name[3] == '/'))
651 	{
652 		if (fp->name[3] == '/')
653 		{
654 			int	n;
655 			int	m;
656 
657 			n = strlen(error_info.file);
658 			m = strlen(fp->name + 4);
659 			if (n < m || !streq(fp->name + 4, error_info.file + n - m))
660 			{
661 				if ((fd = ppsearch(fp->name + 4, type, flags|SEARCH_TEST)) < 0)
662 					return -1;
663 				if (fd > 0)
664 					close(fd);
665 				s = error_info.file;
666 				error_info.file = pp.include;
667 				fd = ppsearch(fp->name + 4, type, flags|SEARCH_NEXT);
668 				error_info.file = s;
669 				return fd;
670 			}
671 			file = error_info.file + n - m;
672 		}
673 		else if (file = strrchr(error_info.file, '/'))
674 			file++;
675 		else
676 			file = error_info.file;
677 		flags |= SEARCH_NEXT;
678 #if _HUH_2002_05_28
679 		if (pp.in->prefix)
680 		{
681 			sfsprintf(name, sizeof(name) - 1, "%s/%s", pp.in->prefix, file);
682 			fp = ppsetfile(name);
683 			if ((fd = ppsearch(fp->name, type, flags)) >= 0)
684 				return fd;
685 		}
686 #endif
687 		fp = ppsetfile(file);
688 		return ppsearch(fp->name, type, flags);
689 	}
690 	else if ((flags & SEARCH_INCLUDE) && fp->guard == INC_IGNORE)
691 	{
692 		strcpy(pp.path, fp->name);
693 		message((-2, "%s: ignored", fp->name));
694 		return 0;
695 	}
696 	else if (!(flags & SEARCH_NEXT))
697 		flags |= SEARCH_SKIP;
698 	pp.prefix = 0;
699 	if (type == T_HEADER)
700 		dp = pp.stddirs->next;
701 	else
702 	{
703 		dp = pp.lcldirs;
704 		if (dp == pp.firstdir)
705 		{
706 			/*
707 			 * look in directory of including file first
708 			 */
709 
710 			if (error_info.file && (s = strrchr(error_info.file, '/')))
711 			{
712 				*s = 0;
713 				dp->name = ppsetfile(error_info.file)->name;
714 				*s = '/';
715 			}
716 			else
717 				dp->name = "";
718 		}
719 		else if (pp.in->prefix && pp.lcldirs != pp.firstdir)
720 		{
721 			/*
722 			 * look in prefix directory of including file first
723 			 */
724 
725 			if (*fp->name != '/')
726 			{
727 				if ((s = strchr(fp->name, '/')) && (fp->name[0]
728 != '.' || fp->name[1] != '.' || fp->name[2] != '/'))
729 				{
730 					*s = 0;
731 					if (!streq(fp->name, pp.in->prefix))
732 						fd = 0;
733 					*s = '/';
734 				}
735 				else
736 					fd = 0;
737 			}
738 			if (fd >= 0)
739 			{
740 				sfsprintf(name, sizeof(name) - 1, "%s/%s", pp.in->prefix, fp->name);
741 				pathcanon(name, 0);
742 				xp = ppsetfile(name);
743 				if ((fd = search(xp, dp, type, flags)) >= 0)
744 					return fd;
745 			}
746 		}
747 	}
748 	if ((fd = search(fp, dp, type, flags)) < 0)
749 	{
750 		if ((pp.option & PLUSPLUS) && file != pp.tmpbuf)
751 		{
752 			s = file + strlen(file);
753 			while (s > file && *--s != '/' && *s != '\\' && *s != '.');
754 			if (*s != '.')
755 			{
756 				sfsprintf(pp.tmpbuf, MAXTOKEN, "%s.h", file);
757 				file = pp.tmpbuf;
758 				goto again;
759 			}
760 		}
761 
762 		/*
763 		 * hackery for msdos files viewed through unix
764 		 */
765 
766 		switch (dospath)
767 		{
768 		case 1:
769 			if (ppisid(file[0]) && file[1] == ':' && file[2] == '/')
770 			{
771 				file[1] = file[0];
772 				file[0] = '/';
773 				pathcanon(file, 0);
774 				dospath = 2;
775 				goto again;
776 			}
777 			break;
778 		case 2:
779 			file += 2;
780 			goto again;
781 		}
782 		if ((flags & (SEARCH_INCLUDE|SEARCH_NEXT)) == SEARCH_INCLUDE)
783 		{
784 			if (!chop && pp.chop)
785 			{
786 				chop = 1;
787 				type = T_STRING;
788 				goto again;
789 			}
790 			if (!(pp.mode & GENDEPS))
791 			{
792 				if (!(pp.option & ALLPOSSIBLE) || pp.in->prev->prev)
793 					error(2, "%s: cannot find include file", file);
794 			}
795 			else if (!(pp.mode & INIT))
796 			{
797 				xp = ppsetfile(file);
798 				if (!(xp->flags & INC_LISTED))
799 				{
800 					xp->flags |= INC_LISTED;
801 					if ((pp.column + strlen(file)) >= COLUMN_MAX)
802 					{
803 						sfprintf(pp.filedeps.sp, " \\\n");
804 						pp.column = COLUMN_TAB;
805 						index = '\t';
806 					}
807 					else
808 						index = ' ';
809 					pp.column += sfprintf(pp.filedeps.sp, "%c%s", index, file);
810 				}
811 			}
812 		}
813 	}
814 	return fd;
815 }
816