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 >= ¬dotdot[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