1181834Sroberto/* -*- Mode: C -*- */ 2181834Sroberto 3181834Sroberto/* pathfind.c --- find a FILE MODE along PATH */ 4181834Sroberto 5285612Sdelphij/* Author: Gary V Vaughan <gvaughan@oranda.demon.co.uk> */ 6181834Sroberto 7181834Sroberto/* Code: */ 8181834Sroberto 9285612Sdelphijstatic char * 10285612Sdelphijpathfind( char const * path, 11285612Sdelphij char const * fname, 12285612Sdelphij char const * mode ); 13285612Sdelphij 14181834Sroberto#include "compat.h" 15181834Sroberto#ifndef HAVE_PATHFIND 16181834Sroberto#if defined(__windows__) && !defined(__CYGWIN__) 17285612Sdelphijstatic char * 18285612Sdelphijpathfind( char const * path, 19285612Sdelphij char const * fname, 20285612Sdelphij char const * mode ) 21181834Sroberto{ 22285612Sdelphij return strdup(fname); 23181834Sroberto} 24181834Sroberto#else 25181834Sroberto 26285612Sdelphijstatic char * make_absolute(char const * string, char const * dot_path); 27285612Sdelphijstatic char * canonicalize_pathname(char * path); 28285612Sdelphijstatic char * extract_colon_unit(char * dir, char const * string, int * p_index); 29181834Sroberto 30285612Sdelphij/** 31285612Sdelphij * local implementation of pathfind. 32285612Sdelphij * @param[in] path colon separated list of directories 33285612Sdelphij * @param[in] fname the name we are hunting for 34285612Sdelphij * @param[in] mode the required file mode 35285612Sdelphij * @returns an allocated string with the full path, or NULL 36285612Sdelphij */ 37285612Sdelphijstatic char * 38285612Sdelphijpathfind( char const * path, 39285612Sdelphij char const * fname, 40285612Sdelphij char const * mode ) 41181834Sroberto{ 42285612Sdelphij int p_index = 0; 43285612Sdelphij int mode_bits = 0; 44285612Sdelphij char * res_path = NULL; 45285612Sdelphij char zPath[ AG_PATH_MAX + 1 ]; 46181834Sroberto 47181834Sroberto if (strchr( mode, 'r' )) mode_bits |= R_OK; 48181834Sroberto if (strchr( mode, 'w' )) mode_bits |= W_OK; 49181834Sroberto if (strchr( mode, 'x' )) mode_bits |= X_OK; 50181834Sroberto 51181834Sroberto /* 52181834Sroberto * FOR each non-null entry in the colon-separated path, DO ... 53181834Sroberto */ 54181834Sroberto for (;;) { 55285612Sdelphij DIR * dirP; 56285612Sdelphij char * colon_unit = extract_colon_unit( zPath, path, &p_index ); 57181834Sroberto 58181834Sroberto if (colon_unit == NULL) 59181834Sroberto break; 60181834Sroberto 61181834Sroberto dirP = opendir( colon_unit ); 62181834Sroberto 63181834Sroberto /* 64181834Sroberto * IF the directory is inaccessable, THEN next directory 65181834Sroberto */ 66181834Sroberto if (dirP == NULL) 67181834Sroberto continue; 68181834Sroberto 69181834Sroberto for (;;) { 70181834Sroberto struct dirent *entP = readdir( dirP ); 71181834Sroberto 72285612Sdelphij if (entP == (struct dirent *)NULL) 73181834Sroberto break; 74181834Sroberto 75181834Sroberto /* 76181834Sroberto * IF the file name matches the one we are looking for, ... 77181834Sroberto */ 78285612Sdelphij if (strcmp(entP->d_name, fname) == 0) { 79285612Sdelphij char * abs_name = make_absolute(fname, colon_unit); 80181834Sroberto 81181834Sroberto /* 82181834Sroberto * Make sure we can access it in the way we want 83181834Sroberto */ 84285612Sdelphij if (access(abs_name, mode_bits) >= 0) { 85181834Sroberto /* 86181834Sroberto * We can, so normalize the name and return it below 87181834Sroberto */ 88285612Sdelphij res_path = canonicalize_pathname(abs_name); 89181834Sroberto } 90181834Sroberto 91285612Sdelphij free(abs_name); 92181834Sroberto break; 93181834Sroberto } 94181834Sroberto } 95181834Sroberto 96181834Sroberto closedir( dirP ); 97181834Sroberto 98285612Sdelphij if (res_path != NULL) 99181834Sroberto break; 100181834Sroberto } 101181834Sroberto 102285612Sdelphij return res_path; 103181834Sroberto} 104181834Sroberto 105181834Sroberto/* 106181834Sroberto * Turn STRING (a pathname) into an absolute pathname, assuming that 107181834Sroberto * DOT_PATH contains the symbolic location of `.'. This always returns 108181834Sroberto * a new string, even if STRING was an absolute pathname to begin with. 109181834Sroberto */ 110285612Sdelphijstatic char * 111285612Sdelphijmake_absolute( char const * string, char const * dot_path ) 112181834Sroberto{ 113285612Sdelphij char * result; 114181834Sroberto int result_len; 115181834Sroberto 116181834Sroberto if (!dot_path || *string == '/') { 117181834Sroberto result = strdup( string ); 118289997Sglebius if (result == NULL) { 119289997Sglebius return NULL; /* couldn't allocate memory */ 120289997Sglebius } 121181834Sroberto } else { 122181834Sroberto if (dot_path && dot_path[0]) { 123181834Sroberto result = malloc( 2 + strlen( dot_path ) + strlen( string ) ); 124289997Sglebius if (result == NULL) { 125289997Sglebius return NULL; /* couldn't allocate memory */ 126289997Sglebius } 127181834Sroberto strcpy( result, dot_path ); 128285612Sdelphij result_len = (int)strlen(result); 129181834Sroberto if (result[result_len - 1] != '/') { 130181834Sroberto result[result_len++] = '/'; 131181834Sroberto result[result_len] = '\0'; 132181834Sroberto } 133181834Sroberto } else { 134181834Sroberto result = malloc( 3 + strlen( string ) ); 135289997Sglebius if (result == NULL) { 136289997Sglebius return NULL; /* couldn't allocate memory */ 137289997Sglebius } 138181834Sroberto result[0] = '.'; result[1] = '/'; result[2] = '\0'; 139181834Sroberto result_len = 2; 140181834Sroberto } 141181834Sroberto 142181834Sroberto strcpy( result + result_len, string ); 143181834Sroberto } 144181834Sroberto 145181834Sroberto return result; 146181834Sroberto} 147181834Sroberto 148181834Sroberto/* 149181834Sroberto * Canonicalize PATH, and return a new path. The new path differs from 150181834Sroberto * PATH in that: 151181834Sroberto * 152181834Sroberto * Multiple `/'s are collapsed to a single `/'. 153181834Sroberto * Leading `./'s are removed. 154181834Sroberto * Trailing `/.'s are removed. 155181834Sroberto * Trailing `/'s are removed. 156181834Sroberto * Non-leading `../'s and trailing `..'s are handled by removing 157181834Sroberto * portions of the path. 158181834Sroberto */ 159285612Sdelphijstatic char * 160181834Srobertocanonicalize_pathname( char *path ) 161181834Sroberto{ 162181834Sroberto int i, start; 163181834Sroberto char stub_char, *result; 164181834Sroberto 165181834Sroberto /* The result cannot be larger than the input PATH. */ 166181834Sroberto result = strdup( path ); 167289997Sglebius if (result == NULL) { 168289997Sglebius return NULL; /* couldn't allocate memory */ 169289997Sglebius } 170181834Sroberto stub_char = (*path == '/') ? '/' : '.'; 171181834Sroberto 172181834Sroberto /* Walk along RESULT looking for things to compact. */ 173181834Sroberto i = 0; 174181834Sroberto while (result[i]) { 175181834Sroberto while (result[i] != '\0' && result[i] != '/') 176181834Sroberto i++; 177181834Sroberto 178181834Sroberto start = i++; 179181834Sroberto 180181834Sroberto /* If we didn't find any slashes, then there is nothing left to 181181834Sroberto * do. 182181834Sroberto */ 183181834Sroberto if (!result[start]) 184181834Sroberto break; 185181834Sroberto 186181834Sroberto /* Handle multiple `/'s in a row. */ 187181834Sroberto while (result[i] == '/') 188181834Sroberto i++; 189181834Sroberto 190181834Sroberto#if !defined (apollo) 191181834Sroberto if ((start + 1) != i) 192181834Sroberto#else 193181834Sroberto if ((start + 1) != i && (start != 0 || i != 2)) 194181834Sroberto#endif /* apollo */ 195181834Sroberto { 196181834Sroberto strcpy( result + start + 1, result + i ); 197181834Sroberto i = start + 1; 198181834Sroberto } 199181834Sroberto 200181834Sroberto /* Handle backquoted `/'. */ 201181834Sroberto if (start > 0 && result[start - 1] == '\\') 202181834Sroberto continue; 203181834Sroberto 204181834Sroberto /* Check for trailing `/', and `.' by itself. */ 205181834Sroberto if ((start && !result[i]) 206181834Sroberto || (result[i] == '.' && !result[i+1])) { 207181834Sroberto result[--i] = '\0'; 208181834Sroberto break; 209181834Sroberto } 210181834Sroberto 211181834Sroberto /* Check for `../', `./' or trailing `.' by itself. */ 212181834Sroberto if (result[i] == '.') { 213181834Sroberto /* Handle `./'. */ 214181834Sroberto if (result[i + 1] == '/') { 215181834Sroberto strcpy( result + i, result + i + 1 ); 216181834Sroberto i = (start < 0) ? 0 : start; 217181834Sroberto continue; 218181834Sroberto } 219181834Sroberto 220181834Sroberto /* Handle `../' or trailing `..' by itself. */ 221181834Sroberto if (result[i + 1] == '.' && 222181834Sroberto (result[i + 2] == '/' || !result[i + 2])) { 223181834Sroberto while (--start > -1 && result[start] != '/') 224181834Sroberto ; 225181834Sroberto strcpy( result + start + 1, result + i + 2 ); 226181834Sroberto i = (start < 0) ? 0 : start; 227181834Sroberto continue; 228181834Sroberto } 229181834Sroberto } 230181834Sroberto } 231181834Sroberto 232181834Sroberto if (!*result) { 233181834Sroberto *result = stub_char; 234181834Sroberto result[1] = '\0'; 235181834Sroberto } 236181834Sroberto 237181834Sroberto return result; 238181834Sroberto} 239181834Sroberto 240181834Sroberto/* 241181834Sroberto * Given a string containing units of information separated by colons, 242181834Sroberto * return the next one pointed to by (P_INDEX), or NULL if there are no 243181834Sroberto * more. Advance (P_INDEX) to the character after the colon. 244181834Sroberto */ 245285612Sdelphijstatic char * 246285612Sdelphijextract_colon_unit(char * pzDir, char const * string, int * p_index) 247181834Sroberto{ 248285612Sdelphij char * pzDest = pzDir; 249181834Sroberto int ix = *p_index; 250181834Sroberto 251181834Sroberto if (string == NULL) 252181834Sroberto return NULL; 253181834Sroberto 254181834Sroberto if ((unsigned)ix >= strlen( string )) 255181834Sroberto return NULL; 256181834Sroberto 257181834Sroberto { 258285612Sdelphij char const * pzSrc = string + ix; 259181834Sroberto 260181834Sroberto while (*pzSrc == ':') pzSrc++; 261181834Sroberto 262181834Sroberto for (;;) { 263181834Sroberto char ch = (*(pzDest++) = *(pzSrc++)); 264181834Sroberto switch (ch) { 265181834Sroberto case ':': 266181834Sroberto pzDest[-1] = NUL; 267285612Sdelphij /* FALLTHROUGH */ 268181834Sroberto case NUL: 269181834Sroberto goto copy_done; 270181834Sroberto } 271181834Sroberto 272285612Sdelphij if ((unsigned long)(pzDest - pzDir) >= AG_PATH_MAX) 273181834Sroberto break; 274181834Sroberto } copy_done:; 275181834Sroberto 276285612Sdelphij ix = (int)(pzSrc - string); 277181834Sroberto } 278181834Sroberto 279181834Sroberto if (*pzDir == NUL) 280181834Sroberto return NULL; 281181834Sroberto 282181834Sroberto *p_index = ix; 283181834Sroberto return pzDir; 284181834Sroberto} 285181834Sroberto#endif /* __windows__ / __CYGWIN__ */ 286181834Sroberto#endif /* HAVE_PATHFIND */ 287181834Sroberto 288181834Sroberto/* 289181834Sroberto * Local Variables: 290181834Sroberto * mode: C 291181834Sroberto * c-file-style: "stroustrup" 292181834Sroberto * indent-tabs-mode: nil 293181834Sroberto * End: 294181834Sroberto * end of compat/pathfind.c */ 295