include.c revision e4c0bf4d
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
30extern struct	inclist	inclist[ MAXFILES ],
31			*inclistp, *inclistnext;
32extern const char	*includedirs[ ],
33			**includedirsnext;
34extern char	*notdotdot[ ];
35extern boolean show_where_not;
36extern boolean warn_multiple;
37
38static boolean
39isdot(const char *p)
40{
41	if(p && *p++ == '.' && *p++ == '\0')
42		return(TRUE);
43	return(FALSE);
44}
45
46static boolean
47isdotdot(const char *p)
48{
49	if(p && *p++ == '.' && *p++ == '.' && *p++ == '\0')
50		return(TRUE);
51	return(FALSE);
52}
53
54static boolean
55issymbolic(const char *dir, const char *component)
56{
57#ifdef S_IFLNK
58	struct stat	st;
59	char	buf[ BUFSIZ ], **pp;
60
61	sprintf(buf, "%s%s%s", dir, *dir ? "/" : "", component);
62	for (pp=notdotdot; *pp; pp++)
63		if (strcmp(*pp, buf) == 0)
64			return (TRUE);
65	if (lstat(buf, &st) == 0
66	&& (st.st_mode & S_IFMT) == S_IFLNK) {
67		*pp++ = copy(buf);
68		if (pp >= &notdotdot[ MAXDIRS ])
69			fatalerr("out of .. dirs, increase MAXDIRS\n");
70		return(TRUE);
71	}
72#endif
73	return(FALSE);
74}
75
76/*
77 * Occasionally, pathnames are created that look like .../x/../y
78 * Any of the 'x/..' sequences within the name can be eliminated.
79 * (but only if 'x' is not a symbolic link!!)
80 */
81static void
82remove_dotdot(char *path)
83{
84	register char	*end, *from, *to, **cp;
85	char		*components[ MAXFILES ],
86			newpath[ BUFSIZ ];
87	boolean		component_copied;
88
89	/*
90	 * slice path up into components.
91	 */
92	to = newpath;
93	if (*path == '/')
94		*to++ = '/';
95	*to = '\0';
96	cp = components;
97	for (from=end=path; *end; end++)
98		if (*end == '/') {
99			while (*end == '/')
100				*end++ = '\0';
101			if (*from)
102				*cp++ = from;
103			from = end;
104		}
105	*cp++ = from;
106	*cp = NULL;
107
108	/*
109	 * Recursively remove all 'x/..' component pairs.
110	 */
111	cp = components;
112	while(*cp) {
113		if (!isdot(*cp) && !isdotdot(*cp) && isdotdot(*(cp+1))
114		    && !issymbolic(newpath, *cp))
115		{
116		    char **fp = cp + 2;
117		    char **tp = cp;
118
119		    do
120			*tp++ = *fp; /* move all the pointers down */
121		    while (*fp++);
122		    if (cp != components)
123			cp--;	/* go back and check for nested ".." */
124		} else {
125		    cp++;
126		}
127	}
128	/*
129	 * Concatenate the remaining path elements.
130	 */
131	cp = components;
132	component_copied = FALSE;
133	while(*cp) {
134		if (component_copied)
135			*to++ = '/';
136		component_copied = TRUE;
137		for (from = *cp; *from; )
138			*to++ = *from++;
139		*to = '\0';
140		cp++;
141	}
142	*to++ = '\0';
143
144	/*
145	 * copy the reconstituted path back to our pointer.
146	 */
147	strcpy(path, newpath);
148}
149
150/*
151 * Add an include file to the list of those included by 'file'.
152 */
153struct inclist *
154newinclude(const char *newfile, const char *incstring)
155{
156	register struct inclist	*ip;
157
158	/*
159	 * First, put this file on the global list of include files.
160	 */
161	ip = inclistp++;
162	if (inclistp == inclist + MAXFILES - 1)
163		fatalerr("out of space: increase MAXFILES\n");
164	ip->i_file = copy(newfile);
165
166	if (incstring == NULL)
167		ip->i_incstring = ip->i_file;
168	else
169		ip->i_incstring = copy(incstring);
170
171	inclistnext = inclistp;
172	return(ip);
173}
174
175void
176included_by(struct inclist *ip, struct inclist *newfile)
177{
178	register int i;
179
180	if (ip == NULL)
181		return;
182	/*
183	 * Put this include file (newfile) on the list of files included
184	 * by 'file'.  If 'file' is NULL, then it is not an include
185	 * file itself (i.e. was probably mentioned on the command line).
186	 * If it is already on the list, don't stick it on again.
187	 */
188	if (ip->i_list == NULL) {
189		ip->i_list = (struct inclist **)
190			malloc(sizeof(struct inclist *) * ++ip->i_listlen);
191		ip->i_merged = (boolean *)
192		    malloc(sizeof(boolean) * ip->i_listlen);
193	} else {
194		for (i=0; i<ip->i_listlen; i++)
195			if (ip->i_list[ i ] == newfile) {
196			    i = strlen(newfile->i_file);
197			    if (!(ip->i_flags & INCLUDED_SYM) &&
198				!(i > 2 &&
199				  newfile->i_file[i-1] == 'c' &&
200				  newfile->i_file[i-2] == '.'))
201			    {
202				/* only bitch if ip has */
203				/* no #include SYMBOL lines  */
204				/* and is not a .c file */
205				if (warn_multiple)
206				{
207					warning("%s includes %s more than once!\n",
208						ip->i_file, newfile->i_file);
209					warning1("Already have\n");
210					for (i=0; i<ip->i_listlen; i++)
211						warning1("\t%s\n", ip->i_list[i]->i_file);
212				}
213			    }
214			    return;
215			}
216		ip->i_list = (struct inclist **) realloc(ip->i_list,
217			sizeof(struct inclist *) * ++ip->i_listlen);
218		ip->i_merged = (boolean *)
219		    realloc(ip->i_merged, sizeof(boolean) * ip->i_listlen);
220	}
221	ip->i_list[ ip->i_listlen-1 ] = newfile;
222	ip->i_merged[ ip->i_listlen-1 ] = FALSE;
223}
224
225void
226inc_clean (void)
227{
228	register struct inclist *ip;
229
230	for (ip = inclist; ip < inclistp; ip++) {
231		ip->i_flags &= ~MARKED;
232	}
233}
234
235struct inclist *
236inc_path(const char *file, const char *include, int type)
237{
238	static char		path[ BUFSIZ ];
239	register const char	**pp, *p;
240	register struct inclist	*ip;
241	struct stat		st;
242
243	/*
244	 * Check all previously found include files for a path that
245	 * has already been expanded.
246	 */
247	if ((type == INCLUDE) || (type == INCLUDEDOT))
248		inclistnext = inclist;
249	ip = inclistnext;
250
251	for (; ip->i_file; ip++) {
252		if ((strcmp(ip->i_incstring, include) == 0) &&
253		    !(ip->i_flags & INCLUDED_SYM)) {
254			inclistnext = ip + 1;
255			return ip;
256		}
257	}
258
259	if (inclistnext == inclist) {
260		/*
261		 * If the path was surrounded by "" or is an absolute path,
262		 * then check the exact path provided.
263		 */
264		if ((type == INCLUDEDOT) ||
265		    (type == INCLUDENEXTDOT) ||
266		    (*include == '/')) {
267			if (stat(include, &st) == 0 && !S_ISDIR(st.st_mode))
268				return newinclude(include, include);
269			if (show_where_not)
270				warning1("\tnot in %s\n", include);
271		}
272
273		/*
274		 * If the path was surrounded by "" see if this include file is
275		 * in the directory of the file being parsed.
276		 */
277		if ((type == INCLUDEDOT) || (type == INCLUDENEXTDOT)) {
278			for (p=file+strlen(file); p>file; p--)
279				if (*p == '/')
280					break;
281			if (p == file) {
282				strcpy(path, include);
283			} else {
284				strncpy(path, file, (p-file) + 1);
285				path[ (p-file) + 1 ] = '\0';
286				strcpy(path + (p-file) + 1, include);
287			}
288			remove_dotdot(path);
289			if (stat(path, &st) == 0 && !S_ISDIR(st.st_mode))
290				return newinclude(path, include);
291			if (show_where_not)
292				warning1("\tnot in %s\n", path);
293		}
294	}
295
296	/*
297	 * Check the include directories specified.  Standard include dirs
298	 * should be at the end.
299	 */
300	if ((type == INCLUDE) || (type == INCLUDEDOT))
301		includedirsnext = includedirs;
302	pp = includedirsnext;
303
304	for (; *pp; pp++) {
305		sprintf(path, "%s/%s", *pp, include);
306		remove_dotdot(path);
307		if (stat(path, &st) == 0 && !S_ISDIR(st.st_mode)) {
308			includedirsnext = pp + 1;
309			return newinclude(path, include);
310		}
311		if (show_where_not)
312			warning1("\tnot in %s\n", path);
313	}
314
315	return NULL;
316}
317