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