10eb10989Smrg/*
20eb10989Smrg
30eb10989SmrgCopyright (c) 1993, 1994, 1998 The Open Group
40eb10989Smrg
50eb10989SmrgPermission to use, copy, modify, distribute, and sell this software and its
60eb10989Smrgdocumentation for any purpose is hereby granted without fee, provided that
70eb10989Smrgthe above copyright notice appear in all copies and that both that
80eb10989Smrgcopyright notice and this permission notice appear in supporting
90eb10989Smrgdocumentation.
100eb10989Smrg
110eb10989SmrgThe above copyright notice and this permission notice shall be included in
120eb10989Smrgall copies or substantial portions of the Software.
130eb10989Smrg
140eb10989SmrgTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
150eb10989SmrgIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
160eb10989SmrgFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
170eb10989SmrgOPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
180eb10989SmrgAN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
190eb10989SmrgCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
200eb10989Smrg
210eb10989SmrgExcept as contained in this notice, the name of The Open Group shall not be
220eb10989Smrgused in advertising or otherwise to promote the sale, use or other dealings
230eb10989Smrgin this Software without prior written authorization from The Open Group.
240eb10989Smrg
250eb10989Smrg*/
260eb10989Smrg
270eb10989Smrg#include "def.h"
280eb10989Smrg
290eb10989Smrgstatic boolean
3063165362Smrgisdot(const char *p)
310eb10989Smrg{
32fadff096Smrg    if (p && *p++ == '.' && *p++ == '\0')
33fadff096Smrg        return (TRUE);
34fadff096Smrg    return (FALSE);
350eb10989Smrg}
360eb10989Smrg
370eb10989Smrgstatic boolean
3863165362Smrgisdotdot(const char *p)
390eb10989Smrg{
40fadff096Smrg    if (p && *p++ == '.' && *p++ == '.' && *p++ == '\0')
41fadff096Smrg        return (TRUE);
42fadff096Smrg    return (FALSE);
430eb10989Smrg}
440eb10989Smrg
450eb10989Smrgstatic boolean
4663165362Smrgissymbolic(const char *dir, const char *component)
470eb10989Smrg{
480eb10989Smrg#ifdef S_IFLNK
49fadff096Smrg    struct stat st;
50fadff096Smrg    char buf[BUFSIZ], **pp;
51fadff096Smrg
52fadff096Smrg    snprintf(buf, sizeof(buf), "%s%s%s", dir, *dir ? "/" : "", component);
53fadff096Smrg    for (pp = notdotdot; *pp; pp++)
54fadff096Smrg        if (strcmp(*pp, buf) == 0)
55fadff096Smrg            return (TRUE);
56fadff096Smrg    if (lstat(buf, &st) == 0 && (st.st_mode & S_IFMT) == S_IFLNK) {
57fadff096Smrg        char *p = strdup(buf);
58fadff096Smrg        if (p == NULL)
59fadff096Smrg            fatalerr("strdup() failure in %s()\n", __func__);
60fadff096Smrg        *pp++ = p;
61fadff096Smrg        if (pp >= &notdotdot[MAXDIRS])
62fadff096Smrg            fatalerr("out of .. dirs, increase MAXDIRS\n");
63fadff096Smrg        return (TRUE);
64fadff096Smrg    }
650eb10989Smrg#endif
66fadff096Smrg    return (FALSE);
670eb10989Smrg}
680eb10989Smrg
690eb10989Smrg/*
700eb10989Smrg * Occasionally, pathnames are created that look like .../x/../y
710eb10989Smrg * Any of the 'x/..' sequences within the name can be eliminated.
720eb10989Smrg * (but only if 'x' is not a symbolic link!!)
730eb10989Smrg */
740eb10989Smrgstatic void
750eb10989Smrgremove_dotdot(char *path)
760eb10989Smrg{
77fadff096Smrg    char        *end, *from, *to, **cp;
78fadff096Smrg    char        *components[MAXFILES], newpath[BUFSIZ];
79fadff096Smrg    boolean     component_copied;
80fadff096Smrg
81fadff096Smrg    /*
82fadff096Smrg     * slice path up into components.
83fadff096Smrg     */
84fadff096Smrg    to = newpath;
85fadff096Smrg    if (*path == '/')
86fadff096Smrg        *to++ = '/';
87fadff096Smrg    *to = '\0';
88fadff096Smrg    cp = components;
89fadff096Smrg    for (from = end = path; *end; end++)
90fadff096Smrg        if (*end == '/') {
91fadff096Smrg            while (*end == '/')
92fadff096Smrg                *end++ = '\0';
93fadff096Smrg            if (*from)
94fadff096Smrg                *cp++ = from;
95fadff096Smrg            from = end;
96fadff096Smrg        }
97fadff096Smrg    *cp++ = from;
98fadff096Smrg    *cp = NULL;
99fadff096Smrg
100fadff096Smrg    /*
101fadff096Smrg     * Recursively remove all 'x/..' component pairs.
102fadff096Smrg     */
103fadff096Smrg    cp = components;
104fadff096Smrg    while (*cp) {
105fadff096Smrg        if (!isdot(*cp) && !isdotdot(*cp) && isdotdot(*(cp + 1))
106fadff096Smrg            && !issymbolic(newpath, *cp)) {
107fadff096Smrg            char **fp = cp + 2;
108fadff096Smrg            char **tp = cp;
109fadff096Smrg
110fadff096Smrg            do
111fadff096Smrg                *tp++ = *fp;    /* move all the pointers down */
112fadff096Smrg            while (*fp++);
113fadff096Smrg            if (cp != components)
114fadff096Smrg                cp--;           /* go back and check for nested ".." */
115fadff096Smrg        }
116fadff096Smrg        else {
117fadff096Smrg            cp++;
118fadff096Smrg        }
119fadff096Smrg    }
120fadff096Smrg    /*
121fadff096Smrg     * Concatenate the remaining path elements.
122fadff096Smrg     */
123fadff096Smrg    cp = components;
124fadff096Smrg    component_copied = FALSE;
125fadff096Smrg    while (*cp) {
126fadff096Smrg        if (component_copied)
127fadff096Smrg            *to++ = '/';
128fadff096Smrg        component_copied = TRUE;
129fadff096Smrg        for (from = *cp; *from;)
130fadff096Smrg            *to++ = *from++;
131fadff096Smrg        *to = '\0';
132fadff096Smrg        cp++;
133fadff096Smrg    }
134fadff096Smrg    *to++ = '\0';
135fadff096Smrg
136fadff096Smrg    /*
137fadff096Smrg     * copy the reconstituted path back to our pointer.
138fadff096Smrg     */
139fadff096Smrg    strcpy(path, newpath);
1400eb10989Smrg}
1410eb10989Smrg
1420eb10989Smrg/*
1430eb10989Smrg * Add an include file to the list of those included by 'file'.
1440eb10989Smrg */
1450eb10989Smrgstruct inclist *
146fadff096Smrgnewinclude(const char *newfile, const char *incstring, const char *incpath)
1470eb10989Smrg{
148fadff096Smrg    struct inclist *ip;
149fadff096Smrg
150fadff096Smrg    /*
151fadff096Smrg     * First, put this file on the global list of include files.
152fadff096Smrg     */
153fadff096Smrg    ip = inclistp++;
154fadff096Smrg    if (inclistp == inclist + MAXFILES - 1)
155fadff096Smrg        fatalerr("out of space: increase MAXFILES\n");
156fadff096Smrg    ip->i_file = strdup(newfile);
157fadff096Smrg    if (ip->i_file == NULL)
158fadff096Smrg            fatalerr("strdup() failure in %s()\n", __func__);
159fadff096Smrg
160fadff096Smrg    if (incstring == NULL)
161fadff096Smrg        ip->i_incstring = ip->i_file;
162fadff096Smrg    else {
163fadff096Smrg        ip->i_incstring = strdup(incstring);
164fadff096Smrg        if (ip->i_incstring == NULL)
165fadff096Smrg            fatalerr("strdup() failure in %s()\n", __func__);
166fadff096Smrg    }
167fadff096Smrg
168fadff096Smrg    if (incpath == NULL) {
169fadff096Smrg        char r_include[PATHMAX + 1];
170fadff096Smrg
171fadff096Smrg        if (realpath(ip->i_file, r_include) == NULL)
172fadff096Smrg            ip->i_realpath = ip->i_file;
173fadff096Smrg        else
174fadff096Smrg            ip->i_realpath = strdup(r_include);
175fadff096Smrg    }
176fadff096Smrg    else {
177fadff096Smrg        ip->i_realpath = strdup(incpath);
178fadff096Smrg    }
179fadff096Smrg    if (ip->i_realpath == NULL)
180fadff096Smrg        fatalerr("strdup() failure in %s()\n", __func__);
181fadff096Smrg
182fadff096Smrg    inclistnext = inclistp;
183fadff096Smrg    return (ip);
1840eb10989Smrg}
1850eb10989Smrg
1860eb10989Smrgvoid
1870eb10989Smrgincluded_by(struct inclist *ip, struct inclist *newfile)
1880eb10989Smrg{
189fadff096Smrg    if (ip == NULL)
190fadff096Smrg        return;
191fadff096Smrg    /*
192fadff096Smrg     * Put this include file (newfile) on the list of files included
193fadff096Smrg     * by 'file'.  If 'file' is NULL, then it is not an include
194fadff096Smrg     * file itself (i.e. was probably mentioned on the command line).
195fadff096Smrg     * If it is already on the list, don't stick it on again.
196fadff096Smrg     */
197fadff096Smrg    if (ip->i_list == NULL) {
198fadff096Smrg        ip->i_listlen++;
199fadff096Smrg        ip->i_list = mallocarray(ip->i_listlen, sizeof(struct inclist *));
200fadff096Smrg        ip->i_merged = mallocarray(ip->i_listlen, sizeof(boolean));
201fadff096Smrg    }
202fadff096Smrg    else {
203fadff096Smrg        for (unsigned int i = 0; i < ip->i_listlen; i++) {
204fadff096Smrg            if (ip->i_list[i] == newfile) {
205fadff096Smrg                size_t l = strlen(newfile->i_file);
206fadff096Smrg                if (!(ip->i_flags & INCLUDED_SYM) &&
207fadff096Smrg                    !(l > 2 &&
208fadff096Smrg                      newfile->i_file[l - 1] == 'c' &&
209fadff096Smrg                      newfile->i_file[l - 2] == '.')) {
210fadff096Smrg                    /* only bitch if ip has */
211fadff096Smrg                    /* no #include SYMBOL lines  */
212fadff096Smrg                    /* and is not a .c file */
213fadff096Smrg                    if (warn_multiple) {
214fadff096Smrg                        warning("%s includes %s more than once!\n",
215fadff096Smrg                                ip->i_file, newfile->i_file);
216fadff096Smrg                        warning1("Already have\n");
217fadff096Smrg                        for (i = 0; i < ip->i_listlen; i++)
218fadff096Smrg                            warning1("\t%s\n", ip->i_list[i]->i_file);
219fadff096Smrg                    }
220fadff096Smrg                }
221fadff096Smrg                return;
222fadff096Smrg            }
223fadff096Smrg        }
224fadff096Smrg        ip->i_listlen++;
225fadff096Smrg        ip->i_list = reallocarray(ip->i_list, ip->i_listlen,
226fadff096Smrg                                  sizeof(struct inclist *));
227fadff096Smrg        ip->i_merged = reallocarray(ip->i_merged, ip->i_listlen,
228fadff096Smrg                                    sizeof(boolean));
229fadff096Smrg    }
230fadff096Smrg    if ((ip->i_list == NULL) || (ip->i_merged == NULL))
231fadff096Smrg        fatalerr("malloc()/realloc() failure in %s()\n", __func__);
232fadff096Smrg
233fadff096Smrg    ip->i_list[ip->i_listlen - 1] = newfile;
234fadff096Smrg    ip->i_merged[ip->i_listlen - 1] = FALSE;
2350eb10989Smrg}
2360eb10989Smrg
2370eb10989Smrgvoid
238fadff096Smrginc_clean(void)
2390eb10989Smrg{
240fadff096Smrg    struct inclist *ip;
2410eb10989Smrg
242fadff096Smrg    for (ip = inclist; ip < inclistp; ip++) {
243fadff096Smrg        ip->i_flags &= ~MARKED;
244fadff096Smrg    }
2450eb10989Smrg}
2460eb10989Smrg
247ee0db89dSmrg/*
248ee0db89dSmrg * Return full path for the "include" file of the given "type",
249ee0db89dSmrg * which may be found relative to the source file "file".
250ee0db89dSmrg */
251ee0db89dSmrgstatic const char *
252ee0db89dSmrgfind_full_inc_path(const char *file, const char *include, int type)
2530eb10989Smrg{
254fadff096Smrg    static char         path[BUFSIZ];
255fadff096Smrg    struct stat         st;
256fadff096Smrg
257fadff096Smrg    if (inclistnext == inclist) {
258fadff096Smrg        /*
259fadff096Smrg         * If the path was surrounded by "" or is an absolute path,
260fadff096Smrg         * then check the exact path provided.
261fadff096Smrg         */
262fadff096Smrg        if ((type == INCLUDEDOT) ||
263fadff096Smrg            (type == INCLUDENEXTDOT) ||
264fadff096Smrg            (*include == '/')) {
265fadff096Smrg            if (stat(include, &st) == 0 && !S_ISDIR(st.st_mode))
266fadff096Smrg                return include;
267fadff096Smrg            if (show_where_not)
268fadff096Smrg                warning1("\tnot in %s\n", include);
269fadff096Smrg        }
270fadff096Smrg
271fadff096Smrg        /*
272fadff096Smrg         * If the path was surrounded by "" see if this include file is
273fadff096Smrg         * in the directory of the file being parsed.
274fadff096Smrg         */
275fadff096Smrg        if ((type == INCLUDEDOT) || (type == INCLUDENEXTDOT)) {
276fadff096Smrg            const char *p = strrchr(file, '/');
277fadff096Smrg
278fadff096Smrg            if ((p == NULL) || (p == file)) {
279fadff096Smrg                strcpy(path, include);
280fadff096Smrg            }
281fadff096Smrg            else {
282fadff096Smrg                strncpy(path, file, (p - file) + 1);
283fadff096Smrg                path[(p - file) + 1] = '\0';
284fadff096Smrg                strcpy(path + (p - file) + 1, include);
285fadff096Smrg            }
286fadff096Smrg            remove_dotdot(path);
287fadff096Smrg            if (stat(path, &st) == 0 && !S_ISDIR(st.st_mode))
288fadff096Smrg                return path;
289fadff096Smrg            if (show_where_not)
290fadff096Smrg                warning1("\tnot in %s\n", path);
291fadff096Smrg        }
292fadff096Smrg    }
293fadff096Smrg
294fadff096Smrg    /*
295fadff096Smrg     * Check the include directories specified.  Standard include dirs
296fadff096Smrg     * should be at the end.
297fadff096Smrg     */
298fadff096Smrg    if ((type == INCLUDE) || (type == INCLUDEDOT))
299fadff096Smrg        includedirsnext = includedirs;
300fadff096Smrg
301fadff096Smrg    for (const char **pp = includedirsnext; *pp; pp++) {
302fadff096Smrg        snprintf(path, sizeof(path), "%s/%s", *pp, include);
303fadff096Smrg        remove_dotdot(path);
304fadff096Smrg        if (stat(path, &st) == 0 && !S_ISDIR(st.st_mode)) {
305fadff096Smrg            includedirsnext = pp + 1;
306fadff096Smrg            return path;
307fadff096Smrg        }
308fadff096Smrg        if (show_where_not)
309fadff096Smrg            warning1("\tnot in %s\n", path);
310fadff096Smrg    }
311fadff096Smrg
312fadff096Smrg    return NULL;
3130eb10989Smrg}
314ee0db89dSmrg
315ee0db89dSmrgstruct inclist *
316ee0db89dSmrginc_path(const char *file, const char *include, int type)
317ee0db89dSmrg{
318fadff096Smrg    const char      *fp;
319fadff096Smrg    struct inclist  *ip;
320fadff096Smrg    char r_include[PATHMAX + 1];
321fadff096Smrg
322fadff096Smrg    /*
323fadff096Smrg     * Check all previously found include files for a path that
324fadff096Smrg     * has already been expanded.
325fadff096Smrg     */
326fadff096Smrg    if ((type == INCLUDE) || (type == INCLUDEDOT))
327fadff096Smrg        inclistnext = inclist;
328fadff096Smrg    ip = inclistnext;
329fadff096Smrg
330fadff096Smrg    fp = find_full_inc_path(file, include, type);
331fadff096Smrg    if (fp == NULL)
332fadff096Smrg        return NULL;
333fadff096Smrg    if (realpath(fp, r_include) == NULL)
334fadff096Smrg        return NULL;
335fadff096Smrg
336fadff096Smrg    for (; ip->i_file; ip++) {
337fadff096Smrg        if ((strcmp(ip->i_incstring, include) == 0) &&
338fadff096Smrg            !(ip->i_flags & INCLUDED_SYM)) {
339fadff096Smrg            /*
340fadff096Smrg             * Same filename but same file ?
341fadff096Smrg             */
342fadff096Smrg            if (!strcmp(r_include, ip->i_realpath)) {
343fadff096Smrg                inclistnext = ip + 1;
344fadff096Smrg                return ip;
345fadff096Smrg            }
346fadff096Smrg        }
347fadff096Smrg    }
348fadff096Smrg
349fadff096Smrg    return newinclude(fp, include, r_include);
350ee0db89dSmrg}
351