pathfind.c revision 289997
1177633Sdfr/* -*- Mode: C -*- */ 2177633Sdfr 3177633Sdfr/* pathfind.c --- find a FILE MODE along PATH */ 4177633Sdfr 5177633Sdfr/* Author: Gary V Vaughan <gvaughan@oranda.demon.co.uk> */ 6177633Sdfr 7177633Sdfr/* Code: */ 8177633Sdfr 9177633Sdfrstatic char * 10177633Sdfrpathfind( char const * path, 11177633Sdfr char const * fname, 12177633Sdfr char const * mode ); 13177633Sdfr 14177633Sdfr#include "compat.h" 15177633Sdfr#ifndef HAVE_PATHFIND 16177633Sdfr#if defined(__windows__) && !defined(__CYGWIN__) 17177633Sdfrstatic char * 18177633Sdfrpathfind( char const * path, 19177633Sdfr char const * fname, 20177633Sdfr char const * mode ) 21177633Sdfr{ 22177633Sdfr return strdup(fname); 23177633Sdfr} 24177633Sdfr#else 25177633Sdfr 26177633Sdfrstatic char * make_absolute(char const * string, char const * dot_path); 27177633Sdfrstatic char * canonicalize_pathname(char * path); 28177633Sdfrstatic char * extract_colon_unit(char * dir, char const * string, int * p_index); 29177633Sdfr 30177633Sdfr/** 31177633Sdfr * local implementation of pathfind. 32177633Sdfr * @param[in] path colon separated list of directories 33177633Sdfr * @param[in] fname the name we are hunting for 34177633Sdfr * @param[in] mode the required file mode 35177633Sdfr * @returns an allocated string with the full path, or NULL 36177633Sdfr */ 37177633Sdfrstatic char * 38177633Sdfrpathfind( char const * path, 39177633Sdfr char const * fname, 40177633Sdfr char const * mode ) 41177633Sdfr{ 42177633Sdfr int p_index = 0; 43177633Sdfr int mode_bits = 0; 44177633Sdfr char * res_path = NULL; 45177633Sdfr char zPath[ AG_PATH_MAX + 1 ]; 46177633Sdfr 47177633Sdfr if (strchr( mode, 'r' )) mode_bits |= R_OK; 48177633Sdfr if (strchr( mode, 'w' )) mode_bits |= W_OK; 49177633Sdfr if (strchr( mode, 'x' )) mode_bits |= X_OK; 50177633Sdfr 51177633Sdfr /* 52177633Sdfr * FOR each non-null entry in the colon-separated path, DO ... 53177633Sdfr */ 54177633Sdfr for (;;) { 55177633Sdfr DIR * dirP; 56177633Sdfr char * colon_unit = extract_colon_unit( zPath, path, &p_index ); 57177633Sdfr 58177633Sdfr if (colon_unit == NULL) 59177633Sdfr break; 60177633Sdfr 61177633Sdfr dirP = opendir( colon_unit ); 62177633Sdfr 63177633Sdfr /* 64177633Sdfr * IF the directory is inaccessable, THEN next directory 65177633Sdfr */ 66177633Sdfr if (dirP == NULL) 67177633Sdfr continue; 68177633Sdfr 69192971Skmacy for (;;) { 70192971Skmacy struct dirent *entP = readdir( dirP ); 71177633Sdfr 72177633Sdfr if (entP == (struct dirent *)NULL) 73177633Sdfr break; 74177633Sdfr 75177633Sdfr /* 76177633Sdfr * IF the file name matches the one we are looking for, ... 77177633Sdfr */ 78177633Sdfr if (strcmp(entP->d_name, fname) == 0) { 79192971Skmacy char * abs_name = make_absolute(fname, colon_unit); 80192971Skmacy 81177633Sdfr /* 82177633Sdfr * Make sure we can access it in the way we want 83177633Sdfr */ 84177633Sdfr if (access(abs_name, mode_bits) >= 0) { 85177633Sdfr /* 86177633Sdfr * We can, so normalize the name and return it below 87177633Sdfr */ 88177633Sdfr res_path = canonicalize_pathname(abs_name); 89177633Sdfr } 90177633Sdfr 91192971Skmacy free(abs_name); 92192971Skmacy break; 93177633Sdfr } 94177633Sdfr } 95177633Sdfr 96177633Sdfr closedir( dirP ); 97177633Sdfr 98177633Sdfr if (res_path != NULL) 99177633Sdfr break; 100177633Sdfr } 101177633Sdfr 102177633Sdfr return res_path; 103177633Sdfr} 104177633Sdfr 105177633Sdfr/* 106177633Sdfr * Turn STRING (a pathname) into an absolute pathname, assuming that 107177633Sdfr * DOT_PATH contains the symbolic location of `.'. This always returns 108177633Sdfr * a new string, even if STRING was an absolute pathname to begin with. 109177633Sdfr */ 110177633Sdfrstatic char * 111177633Sdfrmake_absolute( char const * string, char const * dot_path ) 112177633Sdfr{ 113177633Sdfr char * result; 114177633Sdfr int result_len; 115177633Sdfr 116177633Sdfr if (!dot_path || *string == '/') { 117177633Sdfr result = strdup( string ); 118177633Sdfr if (result == NULL) { 119177633Sdfr return NULL; /* couldn't allocate memory */ 120177633Sdfr } 121177633Sdfr } else { 122177633Sdfr if (dot_path && dot_path[0]) { 123177633Sdfr result = malloc( 2 + strlen( dot_path ) + strlen( string ) ); 124177633Sdfr if (result == NULL) { 125177633Sdfr return NULL; /* couldn't allocate memory */ 126177633Sdfr } 127177633Sdfr strcpy( result, dot_path ); 128177633Sdfr result_len = (int)strlen(result); 129177633Sdfr if (result[result_len - 1] != '/') { 130177633Sdfr result[result_len++] = '/'; 131177633Sdfr result[result_len] = '\0'; 132177633Sdfr } 133177633Sdfr } else { 134177633Sdfr result = malloc( 3 + strlen( string ) ); 135177633Sdfr if (result == NULL) { 136177633Sdfr return NULL; /* couldn't allocate memory */ 137177633Sdfr } 138177633Sdfr result[0] = '.'; result[1] = '/'; result[2] = '\0'; 139177633Sdfr result_len = 2; 140177633Sdfr } 141177633Sdfr 142177633Sdfr strcpy( result + result_len, string ); 143177633Sdfr } 144177633Sdfr 145177633Sdfr return result; 146177633Sdfr} 147177633Sdfr 148177633Sdfr/* 149177633Sdfr * Canonicalize PATH, and return a new path. The new path differs from 150177633Sdfr * PATH in that: 151177633Sdfr * 152177633Sdfr * Multiple `/'s are collapsed to a single `/'. 153177633Sdfr * Leading `./'s are removed. 154177633Sdfr * Trailing `/.'s are removed. 155177633Sdfr * Trailing `/'s are removed. 156177633Sdfr * Non-leading `../'s and trailing `..'s are handled by removing 157177633Sdfr * portions of the path. 158177633Sdfr */ 159177633Sdfrstatic char * 160177633Sdfrcanonicalize_pathname( char *path ) 161177633Sdfr{ 162177633Sdfr int i, start; 163177633Sdfr char stub_char, *result; 164177633Sdfr 165177633Sdfr /* The result cannot be larger than the input PATH. */ 166177633Sdfr result = strdup( path ); 167177633Sdfr if (result == NULL) { 168177633Sdfr return NULL; /* couldn't allocate memory */ 169177633Sdfr } 170177633Sdfr stub_char = (*path == '/') ? '/' : '.'; 171177633Sdfr 172177633Sdfr /* Walk along RESULT looking for things to compact. */ 173177633Sdfr i = 0; 174177633Sdfr while (result[i]) { 175177633Sdfr while (result[i] != '\0' && result[i] != '/') 176177633Sdfr i++; 177177633Sdfr 178177633Sdfr start = i++; 179177633Sdfr 180177633Sdfr /* If we didn't find any slashes, then there is nothing left to 181177633Sdfr * do. 182177633Sdfr */ 183177633Sdfr if (!result[start]) 184177633Sdfr break; 185177633Sdfr 186177633Sdfr /* Handle multiple `/'s in a row. */ 187177633Sdfr while (result[i] == '/') 188177633Sdfr i++; 189177633Sdfr 190177633Sdfr#if !defined (apollo) 191177633Sdfr if ((start + 1) != i) 192177633Sdfr#else 193177633Sdfr if ((start + 1) != i && (start != 0 || i != 2)) 194177633Sdfr#endif /* apollo */ 195177633Sdfr { 196177633Sdfr strcpy( result + start + 1, result + i ); 197177633Sdfr i = start + 1; 198177633Sdfr } 199177633Sdfr 200177633Sdfr /* Handle backquoted `/'. */ 201177633Sdfr if (start > 0 && result[start - 1] == '\\') 202177633Sdfr continue; 203177633Sdfr 204177633Sdfr /* Check for trailing `/', and `.' by itself. */ 205177633Sdfr if ((start && !result[i]) 206177633Sdfr || (result[i] == '.' && !result[i+1])) { 207177633Sdfr result[--i] = '\0'; 208177633Sdfr break; 209177633Sdfr } 210177633Sdfr 211177633Sdfr /* Check for `../', `./' or trailing `.' by itself. */ 212177633Sdfr if (result[i] == '.') { 213177633Sdfr /* Handle `./'. */ 214177633Sdfr if (result[i + 1] == '/') { 215177633Sdfr strcpy( result + i, result + i + 1 ); 216177633Sdfr i = (start < 0) ? 0 : start; 217177633Sdfr continue; 218177633Sdfr } 219177633Sdfr 220177633Sdfr /* Handle `../' or trailing `..' by itself. */ 221177633Sdfr if (result[i + 1] == '.' && 222177633Sdfr (result[i + 2] == '/' || !result[i + 2])) { 223177633Sdfr while (--start > -1 && result[start] != '/') 224177633Sdfr ; 225177633Sdfr strcpy( result + start + 1, result + i + 2 ); 226177633Sdfr i = (start < 0) ? 0 : start; 227177633Sdfr continue; 228177633Sdfr } 229177633Sdfr } 230177633Sdfr } 231177633Sdfr 232177633Sdfr if (!*result) { 233177633Sdfr *result = stub_char; 234192971Skmacy result[1] = '\0'; 235192971Skmacy } 236192971Skmacy 237192971Skmacy return result; 238192971Skmacy} 239192971Skmacy 240192971Skmacy/* 241192971Skmacy * Given a string containing units of information separated by colons, 242192971Skmacy * return the next one pointed to by (P_INDEX), or NULL if there are no 243192971Skmacy * more. Advance (P_INDEX) to the character after the colon. 244192971Skmacy */ 245192971Skmacystatic char * 246192971Skmacyextract_colon_unit(char * pzDir, char const * string, int * p_index) 247192971Skmacy{ 248192971Skmacy char * pzDest = pzDir; 249192971Skmacy int ix = *p_index; 250192971Skmacy 251192971Skmacy if (string == NULL) 252192971Skmacy return NULL; 253192971Skmacy 254192971Skmacy if ((unsigned)ix >= strlen( string )) 255192971Skmacy return NULL; 256192971Skmacy 257192971Skmacy { 258192971Skmacy char const * pzSrc = string + ix; 259192971Skmacy 260192971Skmacy while (*pzSrc == ':') pzSrc++; 261192971Skmacy 262192971Skmacy for (;;) { 263192971Skmacy char ch = (*(pzDest++) = *(pzSrc++)); 264192971Skmacy switch (ch) { 265192971Skmacy case ':': 266192971Skmacy pzDest[-1] = NUL; 267192971Skmacy /* FALLTHROUGH */ 268192971Skmacy case NUL: 269192971Skmacy goto copy_done; 270192971Skmacy } 271192971Skmacy 272192971Skmacy if ((unsigned long)(pzDest - pzDir) >= AG_PATH_MAX) 273192971Skmacy break; 274192971Skmacy } copy_done:; 275192971Skmacy 276 ix = (int)(pzSrc - string); 277 } 278 279 if (*pzDir == NUL) 280 return NULL; 281 282 *p_index = ix; 283 return pzDir; 284} 285#endif /* __windows__ / __CYGWIN__ */ 286#endif /* HAVE_PATHFIND */ 287 288/* 289 * Local Variables: 290 * mode: C 291 * c-file-style: "stroustrup" 292 * indent-tabs-mode: nil 293 * End: 294 * end of compat/pathfind.c */ 295