include.c revision fadff096
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#include "def.h"
28
29static boolean
30isdot(const char *p)
31{
32    if (p && *p++ == '.' && *p++ == '\0')
33        return (TRUE);
34    return (FALSE);
35}
36
37static boolean
38isdotdot(const char *p)
39{
40    if (p && *p++ == '.' && *p++ == '.' && *p++ == '\0')
41        return (TRUE);
42    return (FALSE);
43}
44
45static boolean
46issymbolic(const char *dir, const char *component)
47{
48#ifdef S_IFLNK
49    struct stat st;
50    char buf[BUFSIZ], **pp;
51
52    snprintf(buf, sizeof(buf), "%s%s%s", dir, *dir ? "/" : "", component);
53    for (pp = notdotdot; *pp; pp++)
54        if (strcmp(*pp, buf) == 0)
55            return (TRUE);
56    if (lstat(buf, &st) == 0 && (st.st_mode & S_IFMT) == S_IFLNK) {
57        char *p = strdup(buf);
58        if (p == NULL)
59            fatalerr("strdup() failure in %s()\n", __func__);
60        *pp++ = p;
61        if (pp >= &notdotdot[MAXDIRS])
62            fatalerr("out of .. dirs, increase MAXDIRS\n");
63        return (TRUE);
64    }
65#endif
66    return (FALSE);
67}
68
69/*
70 * Occasionally, pathnames are created that look like .../x/../y
71 * Any of the 'x/..' sequences within the name can be eliminated.
72 * (but only if 'x' is not a symbolic link!!)
73 */
74static void
75remove_dotdot(char *path)
76{
77    char        *end, *from, *to, **cp;
78    char        *components[MAXFILES], 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            char **fp = cp + 2;
108            char **tp = cp;
109
110            do
111                *tp++ = *fp;    /* move all the pointers down */
112            while (*fp++);
113            if (cp != components)
114                cp--;           /* go back and check for nested ".." */
115        }
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, const char *incpath)
147{
148    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    if (ip->i_file == NULL)
158            fatalerr("strdup() failure in %s()\n", __func__);
159
160    if (incstring == NULL)
161        ip->i_incstring = ip->i_file;
162    else {
163        ip->i_incstring = strdup(incstring);
164        if (ip->i_incstring == NULL)
165            fatalerr("strdup() failure in %s()\n", __func__);
166    }
167
168    if (incpath == NULL) {
169        char r_include[PATHMAX + 1];
170
171        if (realpath(ip->i_file, r_include) == NULL)
172            ip->i_realpath = ip->i_file;
173        else
174            ip->i_realpath = strdup(r_include);
175    }
176    else {
177        ip->i_realpath = strdup(incpath);
178    }
179    if (ip->i_realpath == NULL)
180        fatalerr("strdup() failure in %s()\n", __func__);
181
182    inclistnext = inclistp;
183    return (ip);
184}
185
186void
187included_by(struct inclist *ip, struct inclist *newfile)
188{
189    if (ip == NULL)
190        return;
191    /*
192     * Put this include file (newfile) on the list of files included
193     * by 'file'.  If 'file' is NULL, then it is not an include
194     * file itself (i.e. was probably mentioned on the command line).
195     * If it is already on the list, don't stick it on again.
196     */
197    if (ip->i_list == NULL) {
198        ip->i_listlen++;
199        ip->i_list = mallocarray(ip->i_listlen, sizeof(struct inclist *));
200        ip->i_merged = mallocarray(ip->i_listlen, sizeof(boolean));
201    }
202    else {
203        for (unsigned int i = 0; i < ip->i_listlen; i++) {
204            if (ip->i_list[i] == newfile) {
205                size_t l = strlen(newfile->i_file);
206                if (!(ip->i_flags & INCLUDED_SYM) &&
207                    !(l > 2 &&
208                      newfile->i_file[l - 1] == 'c' &&
209                      newfile->i_file[l - 2] == '.')) {
210                    /* only bitch if ip has */
211                    /* no #include SYMBOL lines  */
212                    /* and is not a .c file */
213                    if (warn_multiple) {
214                        warning("%s includes %s more than once!\n",
215                                ip->i_file, newfile->i_file);
216                        warning1("Already have\n");
217                        for (i = 0; i < ip->i_listlen; i++)
218                            warning1("\t%s\n", ip->i_list[i]->i_file);
219                    }
220                }
221                return;
222            }
223        }
224        ip->i_listlen++;
225        ip->i_list = reallocarray(ip->i_list, ip->i_listlen,
226                                  sizeof(struct inclist *));
227        ip->i_merged = reallocarray(ip->i_merged, ip->i_listlen,
228                                    sizeof(boolean));
229    }
230    if ((ip->i_list == NULL) || (ip->i_merged == NULL))
231        fatalerr("malloc()/realloc() failure in %s()\n", __func__);
232
233    ip->i_list[ip->i_listlen - 1] = newfile;
234    ip->i_merged[ip->i_listlen - 1] = FALSE;
235}
236
237void
238inc_clean(void)
239{
240    struct inclist *ip;
241
242    for (ip = inclist; ip < inclistp; ip++) {
243        ip->i_flags &= ~MARKED;
244    }
245}
246
247/*
248 * Return full path for the "include" file of the given "type",
249 * which may be found relative to the source file "file".
250 */
251static const char *
252find_full_inc_path(const char *file, const char *include, int type)
253{
254    static char         path[BUFSIZ];
255    struct stat         st;
256
257    if (inclistnext == inclist) {
258        /*
259         * If the path was surrounded by "" or is an absolute path,
260         * then check the exact path provided.
261         */
262        if ((type == INCLUDEDOT) ||
263            (type == INCLUDENEXTDOT) ||
264            (*include == '/')) {
265            if (stat(include, &st) == 0 && !S_ISDIR(st.st_mode))
266                return include;
267            if (show_where_not)
268                warning1("\tnot in %s\n", include);
269        }
270
271        /*
272         * If the path was surrounded by "" see if this include file is
273         * in the directory of the file being parsed.
274         */
275        if ((type == INCLUDEDOT) || (type == INCLUDENEXTDOT)) {
276            const char *p = strrchr(file, '/');
277
278            if ((p == NULL) || (p == file)) {
279                strcpy(path, include);
280            }
281            else {
282                strncpy(path, file, (p - file) + 1);
283                path[(p - file) + 1] = '\0';
284                strcpy(path + (p - file) + 1, include);
285            }
286            remove_dotdot(path);
287            if (stat(path, &st) == 0 && !S_ISDIR(st.st_mode))
288                return path;
289            if (show_where_not)
290                warning1("\tnot in %s\n", path);
291        }
292    }
293
294    /*
295     * Check the include directories specified.  Standard include dirs
296     * should be at the end.
297     */
298    if ((type == INCLUDE) || (type == INCLUDEDOT))
299        includedirsnext = includedirs;
300
301    for (const char **pp = includedirsnext; *pp; pp++) {
302        snprintf(path, sizeof(path), "%s/%s", *pp, include);
303        remove_dotdot(path);
304        if (stat(path, &st) == 0 && !S_ISDIR(st.st_mode)) {
305            includedirsnext = pp + 1;
306            return path;
307        }
308        if (show_where_not)
309            warning1("\tnot in %s\n", path);
310    }
311
312    return NULL;
313}
314
315struct inclist *
316inc_path(const char *file, const char *include, int type)
317{
318    const char      *fp;
319    struct inclist  *ip;
320    char r_include[PATHMAX + 1];
321
322    /*
323     * Check all previously found include files for a path that
324     * has already been expanded.
325     */
326    if ((type == INCLUDE) || (type == INCLUDEDOT))
327        inclistnext = inclist;
328    ip = inclistnext;
329
330    fp = find_full_inc_path(file, include, type);
331    if (fp == NULL)
332        return NULL;
333    if (realpath(fp, r_include) == NULL)
334        return NULL;
335
336    for (; ip->i_file; ip++) {
337        if ((strcmp(ip->i_incstring, include) == 0) &&
338            !(ip->i_flags & INCLUDED_SYM)) {
339            /*
340             * Same filename but same file ?
341             */
342            if (!strcmp(r_include, ip->i_realpath)) {
343                inclistnext = ip + 1;
344                return ip;
345            }
346        }
347    }
348
349    return newinclude(fp, include, r_include);
350}
351