include.c revision 079e7944
1/*
2
3Copyright (c) 1993, 1994, 1998 The Open Group
4
5Permission to use, copy, modify, distribute, and sell this software and its
6documentation for any purpose is hereby granted without fee, provided that
7the above copyright notice appear in all copies and that both that
8copyright notice and this permission notice appear in supporting
9documentation.
10
11The above copyright notice and this permission notice shall be included in
12all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
21Except as contained in this notice, the name of The Open Group shall not be
22used in advertising or otherwise to promote the sale, use or other dealings
23in this Software without prior written authorization from The Open Group.
24
25*/
26
27
28#include "def.h"
29
30static boolean
31isdot(const char *p)
32{
33	if(p && *p++ == '.' && *p++ == '\0')
34		return(TRUE);
35	return(FALSE);
36}
37
38static boolean
39isdotdot(const char *p)
40{
41	if(p && *p++ == '.' && *p++ == '.' && *p++ == '\0')
42		return(TRUE);
43	return(FALSE);
44}
45
46static boolean
47issymbolic(const char *dir, const char *component)
48{
49#ifdef S_IFLNK
50	struct stat	st;
51	char	buf[ BUFSIZ ], **pp;
52
53	snprintf(buf, sizeof(buf), "%s%s%s", dir, *dir ? "/" : "", component);
54	for (pp=notdotdot; *pp; pp++)
55		if (strcmp(*pp, buf) == 0)
56			return (TRUE);
57	if (lstat(buf, &st) == 0
58	&& (st.st_mode & S_IFMT) == S_IFLNK) {
59		*pp++ = strdup(buf);
60		if (pp >= &notdotdot[ MAXDIRS ])
61			fatalerr("out of .. dirs, increase MAXDIRS\n");
62		return(TRUE);
63	}
64#endif
65	return(FALSE);
66}
67
68/*
69 * Occasionally, pathnames are created that look like .../x/../y
70 * Any of the 'x/..' sequences within the name can be eliminated.
71 * (but only if 'x' is not a symbolic link!!)
72 */
73static void
74remove_dotdot(char *path)
75{
76	register char	*end, *from, *to, **cp;
77	char		*components[ MAXFILES ],
78			newpath[ BUFSIZ ];
79	boolean		component_copied;
80
81	/*
82	 * slice path up into components.
83	 */
84	to = newpath;
85	if (*path == '/')
86		*to++ = '/';
87	*to = '\0';
88	cp = components;
89	for (from=end=path; *end; end++)
90		if (*end == '/') {
91			while (*end == '/')
92				*end++ = '\0';
93			if (*from)
94				*cp++ = from;
95			from = end;
96		}
97	*cp++ = from;
98	*cp = NULL;
99
100	/*
101	 * Recursively remove all 'x/..' component pairs.
102	 */
103	cp = components;
104	while(*cp) {
105		if (!isdot(*cp) && !isdotdot(*cp) && isdotdot(*(cp+1))
106		    && !issymbolic(newpath, *cp))
107		{
108		    char **fp = cp + 2;
109		    char **tp = cp;
110
111		    do
112			*tp++ = *fp; /* move all the pointers down */
113		    while (*fp++);
114		    if (cp != components)
115			cp--;	/* go back and check for nested ".." */
116		} else {
117		    cp++;
118		}
119	}
120	/*
121	 * Concatenate the remaining path elements.
122	 */
123	cp = components;
124	component_copied = FALSE;
125	while(*cp) {
126		if (component_copied)
127			*to++ = '/';
128		component_copied = TRUE;
129		for (from = *cp; *from; )
130			*to++ = *from++;
131		*to = '\0';
132		cp++;
133	}
134	*to++ = '\0';
135
136	/*
137	 * copy the reconstituted path back to our pointer.
138	 */
139	strcpy(path, newpath);
140}
141
142/*
143 * Add an include file to the list of those included by 'file'.
144 */
145struct inclist *
146newinclude(const char *newfile, const char *incstring)
147{
148	register struct inclist	*ip;
149
150	/*
151	 * First, put this file on the global list of include files.
152	 */
153	ip = inclistp++;
154	if (inclistp == inclist + MAXFILES - 1)
155		fatalerr("out of space: increase MAXFILES\n");
156	ip->i_file = strdup(newfile);
157
158	if (incstring == NULL)
159		ip->i_incstring = ip->i_file;
160	else
161		ip->i_incstring = strdup(incstring);
162
163	inclistnext = inclistp;
164	return(ip);
165}
166
167void
168included_by(struct inclist *ip, struct inclist *newfile)
169{
170	register int i;
171
172	if (ip == NULL)
173		return;
174	/*
175	 * Put this include file (newfile) on the list of files included
176	 * by 'file'.  If 'file' is NULL, then it is not an include
177	 * file itself (i.e. was probably mentioned on the command line).
178	 * If it is already on the list, don't stick it on again.
179	 */
180	if (ip->i_list == NULL) {
181		ip->i_list = malloc(sizeof(struct inclist *) * ++ip->i_listlen);
182		ip->i_merged = malloc(sizeof(boolean) * ip->i_listlen);
183	} else {
184		for (i=0; i<ip->i_listlen; i++)
185			if (ip->i_list[ i ] == newfile) {
186			    i = strlen(newfile->i_file);
187			    if (!(ip->i_flags & INCLUDED_SYM) &&
188				!(i > 2 &&
189				  newfile->i_file[i-1] == 'c' &&
190				  newfile->i_file[i-2] == '.'))
191			    {
192				/* only bitch if ip has */
193				/* no #include SYMBOL lines  */
194				/* and is not a .c file */
195				if (warn_multiple)
196				{
197					warning("%s includes %s more than once!\n",
198						ip->i_file, newfile->i_file);
199					warning1("Already have\n");
200					for (i=0; i<ip->i_listlen; i++)
201						warning1("\t%s\n", ip->i_list[i]->i_file);
202				}
203			    }
204			    return;
205			}
206		ip->i_list = realloc(ip->i_list,
207			sizeof(struct inclist *) * ++ip->i_listlen);
208		ip->i_merged =
209		    realloc(ip->i_merged, sizeof(boolean) * ip->i_listlen);
210	}
211	ip->i_list[ ip->i_listlen-1 ] = newfile;
212	ip->i_merged[ ip->i_listlen-1 ] = FALSE;
213}
214
215void
216inc_clean (void)
217{
218	register struct inclist *ip;
219
220	for (ip = inclist; ip < inclistp; ip++) {
221		ip->i_flags &= ~MARKED;
222	}
223}
224
225struct inclist *
226inc_path(const char *file, const char *include, int type)
227{
228	static char		path[ BUFSIZ ];
229	register const char	**pp, *p;
230	register struct inclist	*ip;
231	struct stat		st;
232
233	/*
234	 * Check all previously found include files for a path that
235	 * has already been expanded.
236	 */
237	if ((type == INCLUDE) || (type == INCLUDEDOT))
238		inclistnext = inclist;
239	ip = inclistnext;
240
241	for (; ip->i_file; ip++) {
242		if ((strcmp(ip->i_incstring, include) == 0) &&
243		    !(ip->i_flags & INCLUDED_SYM)) {
244			inclistnext = ip + 1;
245			return ip;
246		}
247	}
248
249	if (inclistnext == inclist) {
250		/*
251		 * If the path was surrounded by "" or is an absolute path,
252		 * then check the exact path provided.
253		 */
254		if ((type == INCLUDEDOT) ||
255		    (type == INCLUDENEXTDOT) ||
256		    (*include == '/')) {
257			if (stat(include, &st) == 0 && !S_ISDIR(st.st_mode))
258				return newinclude(include, include);
259			if (show_where_not)
260				warning1("\tnot in %s\n", include);
261		}
262
263		/*
264		 * If the path was surrounded by "" see if this include file is
265		 * in the directory of the file being parsed.
266		 */
267		if ((type == INCLUDEDOT) || (type == INCLUDENEXTDOT)) {
268			for (p=file+strlen(file); p>file; p--)
269				if (*p == '/')
270					break;
271			if (p == file) {
272				strcpy(path, include);
273			} else {
274				strncpy(path, file, (p-file) + 1);
275				path[ (p-file) + 1 ] = '\0';
276				strcpy(path + (p-file) + 1, include);
277			}
278			remove_dotdot(path);
279			if (stat(path, &st) == 0 && !S_ISDIR(st.st_mode))
280				return newinclude(path, include);
281			if (show_where_not)
282				warning1("\tnot in %s\n", path);
283		}
284	}
285
286	/*
287	 * Check the include directories specified.  Standard include dirs
288	 * should be at the end.
289	 */
290	if ((type == INCLUDE) || (type == INCLUDEDOT))
291		includedirsnext = includedirs;
292	pp = includedirsnext;
293
294	for (; *pp; pp++) {
295		snprintf(path, sizeof(path), "%s/%s", *pp, include);
296		remove_dotdot(path);
297		if (stat(path, &st) == 0 && !S_ISDIR(st.st_mode)) {
298			includedirsnext = pp + 1;
299			return newinclude(path, include);
300		}
301		if (show_where_not)
302			warning1("\tnot in %s\n", path);
303	}
304
305	return NULL;
306}
307