1*eabc0478Schristos /* $NetBSD: pathfind.c,v 1.9 2024/08/18 20:47:25 christos Exp $ */ 2abb0f93cSkardel 3abb0f93cSkardel /* -*- Mode: C -*- */ 4abb0f93cSkardel 5abb0f93cSkardel /* pathfind.c --- find a FILE MODE along PATH */ 6abb0f93cSkardel 72950cc38Schristos /* Author: Gary V Vaughan <gvaughan@oranda.demon.co.uk> */ 8abb0f93cSkardel 9abb0f93cSkardel /* Code: */ 10abb0f93cSkardel 112950cc38Schristos static char * 122950cc38Schristos pathfind( char const * path, 132950cc38Schristos char const * fname, 142950cc38Schristos char const * mode ); 152950cc38Schristos 16abb0f93cSkardel #include "compat.h" 17abb0f93cSkardel #ifndef HAVE_PATHFIND 18abb0f93cSkardel #if defined(__windows__) && !defined(__CYGWIN__) 192950cc38Schristos static char * 20abb0f93cSkardel pathfind( char const * path, 212950cc38Schristos char const * fname, 22abb0f93cSkardel char const * mode ) 23abb0f93cSkardel { 242950cc38Schristos return strdup(fname); 25abb0f93cSkardel } 26abb0f93cSkardel #else 27abb0f93cSkardel 28abb0f93cSkardel static char * make_absolute(char const * string, char const * dot_path); 29abb0f93cSkardel static char * canonicalize_pathname(char * path); 30abb0f93cSkardel static char * extract_colon_unit(char * dir, char const * string, int * p_index); 31abb0f93cSkardel 322950cc38Schristos /** 332950cc38Schristos * local implementation of pathfind. 342950cc38Schristos * @param[in] path colon separated list of directories 352950cc38Schristos * @param[in] fname the name we are hunting for 362950cc38Schristos * @param[in] mode the required file mode 372950cc38Schristos * @returns an allocated string with the full path, or NULL 382950cc38Schristos */ 392950cc38Schristos static char * 40abb0f93cSkardel pathfind( char const * path, 412950cc38Schristos char const * fname, 42abb0f93cSkardel char const * mode ) 43abb0f93cSkardel { 44abb0f93cSkardel int p_index = 0; 45abb0f93cSkardel int mode_bits = 0; 462950cc38Schristos char * res_path = NULL; 47abb0f93cSkardel char zPath[ AG_PATH_MAX + 1 ]; 48abb0f93cSkardel 49abb0f93cSkardel if (strchr( mode, 'r' )) mode_bits |= R_OK; 50abb0f93cSkardel if (strchr( mode, 'w' )) mode_bits |= W_OK; 51abb0f93cSkardel if (strchr( mode, 'x' )) mode_bits |= X_OK; 52abb0f93cSkardel 53abb0f93cSkardel /* 54abb0f93cSkardel * FOR each non-null entry in the colon-separated path, DO ... 55abb0f93cSkardel */ 56abb0f93cSkardel for (;;) { 57abb0f93cSkardel DIR * dirP; 58abb0f93cSkardel char * colon_unit = extract_colon_unit( zPath, path, &p_index ); 59abb0f93cSkardel 60abb0f93cSkardel if (colon_unit == NULL) 61abb0f93cSkardel break; 62abb0f93cSkardel 63abb0f93cSkardel dirP = opendir( colon_unit ); 64abb0f93cSkardel 65abb0f93cSkardel /* 66abb0f93cSkardel * IF the directory is inaccessable, THEN next directory 67abb0f93cSkardel */ 68abb0f93cSkardel if (dirP == NULL) 69abb0f93cSkardel continue; 70abb0f93cSkardel 71abb0f93cSkardel for (;;) { 72abb0f93cSkardel struct dirent *entP = readdir( dirP ); 73abb0f93cSkardel 74abb0f93cSkardel if (entP == (struct dirent *)NULL) 75abb0f93cSkardel break; 76abb0f93cSkardel 77abb0f93cSkardel /* 78abb0f93cSkardel * IF the file name matches the one we are looking for, ... 79abb0f93cSkardel */ 802950cc38Schristos if (strcmp(entP->d_name, fname) == 0) { 812950cc38Schristos char * abs_name = make_absolute(fname, colon_unit); 82abb0f93cSkardel 83abb0f93cSkardel /* 84abb0f93cSkardel * Make sure we can access it in the way we want 85abb0f93cSkardel */ 862950cc38Schristos if (access(abs_name, mode_bits) >= 0) { 87abb0f93cSkardel /* 88abb0f93cSkardel * We can, so normalize the name and return it below 89abb0f93cSkardel */ 902950cc38Schristos res_path = canonicalize_pathname(abs_name); 91abb0f93cSkardel } 92abb0f93cSkardel 932950cc38Schristos free(abs_name); 94abb0f93cSkardel break; 95abb0f93cSkardel } 96abb0f93cSkardel } 97abb0f93cSkardel 98abb0f93cSkardel closedir( dirP ); 99abb0f93cSkardel 1002950cc38Schristos if (res_path != NULL) 101abb0f93cSkardel break; 102abb0f93cSkardel } 103abb0f93cSkardel 1042950cc38Schristos return res_path; 105abb0f93cSkardel } 106abb0f93cSkardel 107abb0f93cSkardel /* 108abb0f93cSkardel * Turn STRING (a pathname) into an absolute pathname, assuming that 109abb0f93cSkardel * DOT_PATH contains the symbolic location of `.'. This always returns 110abb0f93cSkardel * a new string, even if STRING was an absolute pathname to begin with. 111abb0f93cSkardel */ 112abb0f93cSkardel static char * 113abb0f93cSkardel make_absolute( char const * string, char const * dot_path ) 114abb0f93cSkardel { 115abb0f93cSkardel char * result; 116abb0f93cSkardel int result_len; 117abb0f93cSkardel 118abb0f93cSkardel if (!dot_path || *string == '/') { 119abb0f93cSkardel result = strdup( string ); 120abb0f93cSkardel } else { 121abb0f93cSkardel if (dot_path && dot_path[0]) { 122abb0f93cSkardel result = malloc( 2 + strlen( dot_path ) + strlen( string ) ); 123abb0f93cSkardel strcpy( result, dot_path ); 1242950cc38Schristos result_len = (int)strlen(result); 125abb0f93cSkardel if (result[result_len - 1] != '/') { 126abb0f93cSkardel result[result_len++] = '/'; 127abb0f93cSkardel result[result_len] = '\0'; 128abb0f93cSkardel } 129abb0f93cSkardel } else { 130abb0f93cSkardel result = malloc( 3 + strlen( string ) ); 131abb0f93cSkardel result[0] = '.'; result[1] = '/'; result[2] = '\0'; 132abb0f93cSkardel result_len = 2; 133abb0f93cSkardel } 134abb0f93cSkardel 135abb0f93cSkardel strcpy( result + result_len, string ); 136abb0f93cSkardel } 137abb0f93cSkardel 138abb0f93cSkardel return result; 139abb0f93cSkardel } 140abb0f93cSkardel 141abb0f93cSkardel /* 142abb0f93cSkardel * Canonicalize PATH, and return a new path. The new path differs from 143abb0f93cSkardel * PATH in that: 144abb0f93cSkardel * 145abb0f93cSkardel * Multiple `/'s are collapsed to a single `/'. 146abb0f93cSkardel * Leading `./'s are removed. 147abb0f93cSkardel * Trailing `/.'s are removed. 148abb0f93cSkardel * Trailing `/'s are removed. 149abb0f93cSkardel * Non-leading `../'s and trailing `..'s are handled by removing 150abb0f93cSkardel * portions of the path. 151abb0f93cSkardel */ 152abb0f93cSkardel static char * 153abb0f93cSkardel canonicalize_pathname( char *path ) 154abb0f93cSkardel { 155abb0f93cSkardel int i, start; 156abb0f93cSkardel char stub_char, *result; 157abb0f93cSkardel 158abb0f93cSkardel /* The result cannot be larger than the input PATH. */ 159abb0f93cSkardel result = strdup( path ); 160*eabc0478Schristos 161abb0f93cSkardel stub_char = (*path == '/') ? '/' : '.'; 162abb0f93cSkardel 163abb0f93cSkardel /* Walk along RESULT looking for things to compact. */ 164abb0f93cSkardel i = 0; 165abb0f93cSkardel while (result[i]) { 166abb0f93cSkardel while (result[i] != '\0' && result[i] != '/') 167abb0f93cSkardel i++; 168abb0f93cSkardel 169abb0f93cSkardel start = i++; 170abb0f93cSkardel 171abb0f93cSkardel /* If we didn't find any slashes, then there is nothing left to 172abb0f93cSkardel * do. 173abb0f93cSkardel */ 174abb0f93cSkardel if (!result[start]) 175abb0f93cSkardel break; 176abb0f93cSkardel 177abb0f93cSkardel /* Handle multiple `/'s in a row. */ 178abb0f93cSkardel while (result[i] == '/') 179abb0f93cSkardel i++; 180abb0f93cSkardel 181abb0f93cSkardel #if !defined (apollo) 182abb0f93cSkardel if ((start + 1) != i) 183abb0f93cSkardel #else 184abb0f93cSkardel if ((start + 1) != i && (start != 0 || i != 2)) 185abb0f93cSkardel #endif /* apollo */ 186abb0f93cSkardel { 187abb0f93cSkardel strcpy( result + start + 1, result + i ); 188abb0f93cSkardel i = start + 1; 189abb0f93cSkardel } 190abb0f93cSkardel 191abb0f93cSkardel /* Handle backquoted `/'. */ 192abb0f93cSkardel if (start > 0 && result[start - 1] == '\\') 193abb0f93cSkardel continue; 194abb0f93cSkardel 195abb0f93cSkardel /* Check for trailing `/', and `.' by itself. */ 196abb0f93cSkardel if ((start && !result[i]) 197abb0f93cSkardel || (result[i] == '.' && !result[i+1])) { 198abb0f93cSkardel result[--i] = '\0'; 199abb0f93cSkardel break; 200abb0f93cSkardel } 201abb0f93cSkardel 202abb0f93cSkardel /* Check for `../', `./' or trailing `.' by itself. */ 203abb0f93cSkardel if (result[i] == '.') { 204abb0f93cSkardel /* Handle `./'. */ 205abb0f93cSkardel if (result[i + 1] == '/') { 206abb0f93cSkardel strcpy( result + i, result + i + 1 ); 207abb0f93cSkardel i = (start < 0) ? 0 : start; 208abb0f93cSkardel continue; 209abb0f93cSkardel } 210abb0f93cSkardel 211abb0f93cSkardel /* Handle `../' or trailing `..' by itself. */ 212abb0f93cSkardel if (result[i + 1] == '.' && 213abb0f93cSkardel (result[i + 2] == '/' || !result[i + 2])) { 214abb0f93cSkardel while (--start > -1 && result[start] != '/') 215abb0f93cSkardel ; 216abb0f93cSkardel strcpy( result + start + 1, result + i + 2 ); 217abb0f93cSkardel i = (start < 0) ? 0 : start; 218abb0f93cSkardel continue; 219abb0f93cSkardel } 220abb0f93cSkardel } 221abb0f93cSkardel } 222abb0f93cSkardel 223abb0f93cSkardel if (!*result) { 224abb0f93cSkardel *result = stub_char; 225abb0f93cSkardel result[1] = '\0'; 226abb0f93cSkardel } 227abb0f93cSkardel 228abb0f93cSkardel return result; 229abb0f93cSkardel } 230abb0f93cSkardel 231abb0f93cSkardel /* 232abb0f93cSkardel * Given a string containing units of information separated by colons, 233abb0f93cSkardel * return the next one pointed to by (P_INDEX), or NULL if there are no 234abb0f93cSkardel * more. Advance (P_INDEX) to the character after the colon. 235abb0f93cSkardel */ 236abb0f93cSkardel static char * 237abb0f93cSkardel extract_colon_unit(char * pzDir, char const * string, int * p_index) 238abb0f93cSkardel { 239abb0f93cSkardel char * pzDest = pzDir; 240abb0f93cSkardel int ix = *p_index; 241abb0f93cSkardel 242abb0f93cSkardel if (string == NULL) 243abb0f93cSkardel return NULL; 244abb0f93cSkardel 245abb0f93cSkardel if ((unsigned)ix >= strlen( string )) 246abb0f93cSkardel return NULL; 247abb0f93cSkardel 248abb0f93cSkardel { 249abb0f93cSkardel char const * pzSrc = string + ix; 250abb0f93cSkardel 251abb0f93cSkardel while (*pzSrc == ':') pzSrc++; 252abb0f93cSkardel 253abb0f93cSkardel for (;;) { 254abb0f93cSkardel char ch = (*(pzDest++) = *(pzSrc++)); 255abb0f93cSkardel switch (ch) { 256abb0f93cSkardel case ':': 257abb0f93cSkardel pzDest[-1] = NUL; 2582950cc38Schristos /* FALLTHROUGH */ 259abb0f93cSkardel case NUL: 260abb0f93cSkardel goto copy_done; 261abb0f93cSkardel } 262abb0f93cSkardel 2632950cc38Schristos if ((unsigned long)(pzDest - pzDir) >= AG_PATH_MAX) 264abb0f93cSkardel break; 265abb0f93cSkardel } copy_done:; 266abb0f93cSkardel 2672950cc38Schristos ix = (int)(pzSrc - string); 268abb0f93cSkardel } 269abb0f93cSkardel 270abb0f93cSkardel if (*pzDir == NUL) 271abb0f93cSkardel return NULL; 272abb0f93cSkardel 273abb0f93cSkardel *p_index = ix; 274abb0f93cSkardel return pzDir; 275abb0f93cSkardel } 276abb0f93cSkardel #endif /* __windows__ / __CYGWIN__ */ 277abb0f93cSkardel #endif /* HAVE_PATHFIND */ 278abb0f93cSkardel 279abb0f93cSkardel /* 280abb0f93cSkardel * Local Variables: 281abb0f93cSkardel * mode: C 282abb0f93cSkardel * c-file-style: "stroustrup" 283abb0f93cSkardel * indent-tabs-mode: nil 284abb0f93cSkardel * End: 285abb0f93cSkardel * end of compat/pathfind.c */ 286