include.c revision ee0db89d
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
225/*
226 * Return full path for the "include" file of the given "type",
227 * which may be found relative to the source file "file".
228 */
229static const char *
230find_full_inc_path(const char *file, const char *include, int type)
231{
232	static char		path[ BUFSIZ ];
233	register const char	**pp, *p;
234	struct stat		st;
235
236	if (inclistnext == inclist) {
237		/*
238		 * If the path was surrounded by "" or is an absolute path,
239		 * then check the exact path provided.
240		 */
241		if ((type == INCLUDEDOT) ||
242		    (type == INCLUDENEXTDOT) ||
243		    (*include == '/')) {
244			if (stat(include, &st) == 0 && !S_ISDIR(st.st_mode))
245				return include;
246			if (show_where_not)
247				warning1("\tnot in %s\n", include);
248		}
249
250		/*
251		 * If the path was surrounded by "" see if this include file is
252		 * in the directory of the file being parsed.
253		 */
254		if ((type == INCLUDEDOT) || (type == INCLUDENEXTDOT)) {
255			for (p=file+strlen(file); p>file; p--)
256				if (*p == '/')
257					break;
258			if (p == file) {
259				strcpy(path, include);
260			} else {
261				strncpy(path, file, (p-file) + 1);
262				path[ (p-file) + 1 ] = '\0';
263				strcpy(path + (p-file) + 1, include);
264			}
265			remove_dotdot(path);
266			if (stat(path, &st) == 0 && !S_ISDIR(st.st_mode))
267				return path;
268			if (show_where_not)
269				warning1("\tnot in %s\n", path);
270		}
271	}
272
273	/*
274	 * Check the include directories specified.  Standard include dirs
275	 * should be at the end.
276	 */
277	if ((type == INCLUDE) || (type == INCLUDEDOT))
278		includedirsnext = includedirs;
279	pp = includedirsnext;
280
281	for (; *pp; pp++) {
282		snprintf(path, sizeof(path), "%s/%s", *pp, include);
283		remove_dotdot(path);
284		if (stat(path, &st) == 0 && !S_ISDIR(st.st_mode)) {
285			includedirsnext = pp + 1;
286			return path;
287		}
288		if (show_where_not)
289			warning1("\tnot in %s\n", path);
290	}
291
292	return NULL;
293}
294
295struct inclist *
296inc_path(const char *file, const char *include, int type)
297{
298	const char	*fp;
299	struct inclist	*ip;
300	char r_include[PATHMAX+1];
301
302	/*
303	 * Check all previously found include files for a path that
304	 * has already been expanded.
305	 */
306	if ((type == INCLUDE) || (type == INCLUDEDOT))
307		inclistnext = inclist;
308	ip = inclistnext;
309
310	fp = find_full_inc_path(file, include, type);
311	if (fp == NULL)
312		return NULL;
313	if (realpath(fp, r_include) == NULL)
314		return NULL;
315
316	for (; ip->i_file; ip++) {
317		if ((strcmp(ip->i_incstring, include) == 0) &&
318		    !(ip->i_flags & INCLUDED_SYM)) {
319			/*
320			 * Same filename but same file ?
321			 */
322			char r_saved_path[PATHMAX+1];
323			if (realpath(ip->i_file, r_saved_path) == NULL)
324				continue;
325			if (!strcmp(r_include, r_saved_path)) {
326				inclistnext = ip + 1;
327				return ip;
328			}
329		}
330	}
331
332	return newinclude(fp, include);
333}
334