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