/* Copyright (C) 2015 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 2002 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 2001 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 1999 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 1996 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */ #include #include #include #include #include #include #include #include #include #include #include #include #include static unsigned env_changed = 0; static int srchpath_bss_count = -1; /* Search PATH for FILE. If successful, store the full pathname in static buffer and return a pointer to it. If not sucessful, return NULL. This is what the Borland searchpath() library function does. */ char * searchpath(const char *file) { static char found[PATH_MAX]; static char *path; if (!file || !*file) { errno = file ? ENOENT : EINVAL; return NULL; } memset(found, 0, sizeof(found)); /* Get $PATH and store it for reuse. */ if (path == 0 || srchpath_bss_count != __bss_count || env_changed != __environ_changed) { char *p = getenv("PATH"); if (path && srchpath_bss_count == __bss_count) free(path); path = (char *)calloc(p ? strlen(p) + 3 : 2, sizeof(char)); if (path == (char *)0) return (char *)0; /* Prepend `.' to the PATH, so current directory is always searched first. */ path[0] = '.'; if (p) { char *s, *name_start = 0; int preserve_case = _preserve_fncase(); path[1] = ';'; strcpy(path+2, p); /* switch FOO\BAR to foo/bar, downcase where appropriate */ for (s = path + 2, name_start = s; *name_start; s++) { char lname[FILENAME_MAX]; if (*s == '\\') *s = '/'; if (s == name_start) continue; if (*s == ':') name_start = s + 1; else if (!preserve_case && (*s == '/' || *s == ';' || *s == '\0')) { memcpy(lname, name_start+1, s - name_start - 1); lname[s - name_start - 1] = '\0'; if (_is_DOS83(lname)) { name_start++; while (name_start < s) { if (*name_start >= 'A' && *name_start <= 'Z') *name_start += 'a' - 'A'; name_start++; } } else name_start = s; } else if (*s == '\0') break; } } else path[1] = 0; } /* Borland's version (as of BC 3.1) always disregards the leading directories and only looks at the file-name component. So, for example, "foo/bar/baz.exe" could find "c:/bin/baz.exe". But that doesn't seem right, so we don't follow their lead here. */ /* If the file name includes slashes or the drive letter, maybe they already have a good name. */ if (strpbrk (file, "/\\:") != 0 && __file_exists(file)) { if (file[0] == '/' || file[0] == '\\' || file[1] == ':' || (file[0] == '.' && (file[1] == '/' || file[1] == '\\' || (file[1] == '.' && (file[2] == '/' || file[2] == '\\'))))) /* Either absolute file name or it begins with a "./". */ strcpy(found, file); else { /* Relative file name: add "./". */ strcpy(found, "./"); strcat(found, file); } return found; } else { char *test_dir = path; do { char *dp; dp = strchr(test_dir, ';'); if (dp == (char *)0) dp = test_dir + strlen(test_dir); if (dp == test_dir) strcpy(found, file); else { strncpy(found, test_dir, dp - test_dir); found[dp - test_dir] = '/'; strcpy(found + (dp - test_dir) + 1, file); } if (__file_exists(found)) return found; if (*dp == 0) break; test_dir = dp + 1; } while (*test_dir != 0); } /* FIXME: perhaps now that we failed to find it, we should try the basename alone, like BC does? But let somebody complain about this first... ;-) */ return NULL; } #ifdef TEST int main(int argc, char *argv[]) { if (argc > 1) { char *found = searchpath (argv[1]); printf ("%s: %s%s\n", argv[1], found ? "found as " : "not found", found ? found : " "); } return 0; } #endif