#include "config.h" #include #include #include #include #include #include #include #include #include "fallback.h" #include "util.h" #include "common.h" #include "env.h" #include "wutil.h" #include "halloc.h" #include "halloc_util.h" #include "path.h" #include "expand.h" /** Unexpected error in path_get_path() */ #define MISSING_COMMAND_ERR_MSG _( L"Error while searching for command '%ls'" ) bool path_get_path_string(const wcstring &cmd_str, wcstring &output, const env_vars &vars) { const wchar_t * const cmd = cmd_str.c_str(); int err = ENOENT; debug( 3, L"path_get_path_string( '%ls' )", cmd ); if(wcschr( cmd, L'/' ) != 0 ) { if( waccess( cmd, X_OK )==0 ) { struct stat buff; if(wstat( cmd, &buff )) { return false; } if (S_ISREG(buff.st_mode)) { output = cmd_str; return true; } else { errno = EACCES; return false; } } else { //struct stat buff; //wstat( cmd, &buff ); return false; } } else { const wchar_t *path = vars.get(L"PATH"); if( path == 0 ) { if( contains( PREFIX L"/bin", L"/bin", L"/usr/bin" ) ) { path = L"/bin" ARRAY_SEP_STR L"/usr/bin"; } else { path = L"/bin" ARRAY_SEP_STR L"/usr/bin" ARRAY_SEP_STR PREFIX L"/bin"; } } wcstokenizer tokenizer(path, ARRAY_SEP_STR); wcstring new_cmd; while (tokenizer.next(new_cmd)) { size_t path_len = new_cmd.size(); if (path_len == 0) continue; append_path_component(new_cmd, cmd_str); if( waccess( new_cmd.c_str(), X_OK )==0 ) { struct stat buff; if( wstat( new_cmd.c_str(), &buff )==-1 ) { if( errno != EACCES ) { wperror( L"stat" ); } continue; } if( S_ISREG(buff.st_mode) ) { output = new_cmd; return true; } err = EACCES; } else { switch( errno ) { case ENOENT: case ENAMETOOLONG: case EACCES: case ENOTDIR: break; default: { debug( 1, MISSING_COMMAND_ERR_MSG, new_cmd.c_str() ); wperror( L"access" ); } } } } } errno = err; return false; } wchar_t *path_get_path( const wchar_t *cmd ) { int err = ENOENT; CHECK( cmd, 0 ); debug( 3, L"path_get_path( '%ls' )", cmd ); if(wcschr( cmd, L'/' ) != 0 ) { if( waccess( cmd, X_OK )==0 ) { struct stat buff; if(wstat( cmd, &buff )) { return 0; } if( S_ISREG(buff.st_mode) ) return wcsdup( cmd ); else { errno = EACCES; return 0; } } else { struct stat buff; wstat( cmd, &buff ); return 0; } } else { env_var_t path = env_get_string(L"PATH"); if( path.missing() ) { if( contains( PREFIX L"/bin", L"/bin", L"/usr/bin" ) ) { path = L"/bin" ARRAY_SEP_STR L"/usr/bin"; } else { path = L"/bin" ARRAY_SEP_STR L"/usr/bin" ARRAY_SEP_STR PREFIX L"/bin"; } } /* Allocate string long enough to hold the whole command */ wchar_t *new_cmd = (wchar_t *)calloc(wcslen(cmd)+path.size()+2, sizeof(wchar_t) ); /* We tokenize a copy of the path, since strtok modifies its arguments */ wchar_t *path_cpy = wcsdup( path.c_str() ); const wchar_t *nxt_path = path.c_str(); wchar_t *state; if( (new_cmd==0) || (path_cpy==0) ) { DIE_MEM(); } for( nxt_path = wcstok( path_cpy, ARRAY_SEP_STR, &state ); nxt_path != 0; nxt_path = wcstok( 0, ARRAY_SEP_STR, &state) ) { int path_len = wcslen( nxt_path ); wcscpy( new_cmd, nxt_path ); if( new_cmd[path_len-1] != L'/' ) { new_cmd[path_len++]=L'/'; } wcscpy( &new_cmd[path_len], cmd ); if( waccess( new_cmd, X_OK )==0 ) { struct stat buff; if( wstat( new_cmd, &buff )==-1 ) { if( errno != EACCES ) { wperror( L"stat" ); } continue; } if( S_ISREG(buff.st_mode) ) { free( path_cpy ); return new_cmd; } err = EACCES; } else { switch( errno ) { case ENOENT: case ENAMETOOLONG: case EACCES: case ENOTDIR: break; default: { debug( 1, MISSING_COMMAND_ERR_MSG, new_cmd ); wperror( L"access" ); } } } } free( path_cpy ); } errno = err; return 0; } bool path_get_path_string(const wcstring &cmd, wcstring &output) { bool success = false; wchar_t *tmp = path_get_path(cmd.c_str()); if (tmp) { output = tmp; free(tmp); success = true; } return success; } bool path_get_cdpath_string(const wcstring &dir_str, wcstring &result, const env_vars &vars) { wchar_t *res = 0; int err = ENOENT; bool success = false; const wchar_t *const dir = dir_str.c_str(); if( dir[0] == L'/'|| (wcsncmp( dir, L"./", 2 )==0) ) { struct stat buf; if( wstat( dir, &buf ) == 0 ) { if( S_ISDIR(buf.st_mode) ) { result = dir_str; success = true; } else { err = ENOTDIR; } } } else { const wchar_t *path = L"."; wcstokenizer tokenizer(path, ARRAY_SEP_STR); wcstring next_path; while (tokenizer.next(next_path)) { expand_tilde(next_path); if (next_path.size() == 0) continue; wcstring whole_path = next_path; append_path_component(whole_path, dir); struct stat buf; if( wstat( whole_path.c_str(), &buf ) == 0 ) { if( S_ISDIR(buf.st_mode) ) { result = whole_path; success = true; break; } else { err = ENOTDIR; } } else { if( lwstat( whole_path.c_str(), &buf ) == 0 ) { err = EROTTEN; } } } } if( !success ) { errno = err; } return res; } wchar_t *path_allocate_cdpath( const wchar_t *dir ) { wchar_t *res = 0; int err = ENOENT; if( !dir ) return 0; if( dir[0] == L'/'|| (wcsncmp( dir, L"./", 2 )==0) ) { struct stat buf; if( wstat( dir, &buf ) == 0 ) { if( S_ISDIR(buf.st_mode) ) { res = wcsdup(dir); } else { err = ENOTDIR; } } } else { wchar_t *path_cpy; const wchar_t *nxt_path; wchar_t *state; wchar_t *whole_path; env_var_t path = L"."; nxt_path = path.c_str(); path_cpy = wcsdup( path.c_str() ); if( !path_cpy ) { DIE_MEM(); } for( nxt_path = wcstok( path_cpy, ARRAY_SEP_STR, &state ); nxt_path != 0; nxt_path = wcstok( 0, ARRAY_SEP_STR, &state) ) { wchar_t *expanded_path = expand_tilde_compat( wcsdup(nxt_path) ); // debug( 2, L"woot %ls\n", expanded_path ); int path_len = wcslen( expanded_path ); if( path_len == 0 ) { free(expanded_path ); continue; } whole_path = wcsdupcat( expanded_path, ( expanded_path[path_len-1] != L'/' )?L"/":L"", dir ); free(expanded_path ); struct stat buf; if( wstat( whole_path, &buf ) == 0 ) { if( S_ISDIR(buf.st_mode) ) { res = whole_path; break; } else { err = ENOTDIR; } } else { if( lwstat( whole_path, &buf ) == 0 ) { err = EROTTEN; } } free( whole_path ); } free( path_cpy ); } if( !res ) { errno = err; } return res; } bool path_can_get_cdpath(const wcstring &in) { wchar_t *tmp = path_allocate_cdpath(in.c_str()); bool result = (tmp != NULL); free(tmp); return result; } wchar_t *path_get_config( void *context) { int done = 0; wcstring res; const env_var_t xdg_dir = env_get_string( L"XDG_CONFIG_HOME" ); if( ! xdg_dir.missing() ) { res = xdg_dir + L"/fish"; if( !create_directory( res.c_str() ) ) { done = 1; } } else { const env_var_t home = env_get_string( L"HOME" ); if( ! home.missing() ) { res = home + L"/.config/fish"; if( !create_directory( res.c_str() ) ) { done = 1; } } } if( done ) { wchar_t *result = wcsdup(res.c_str()); halloc_register_function( context, &free, result ); return result; } else { debug( 0, _(L"Unable to create a configuration directory for fish. Your personal settings will not be saved. Please set the $XDG_CONFIG_HOME variable to a directory where the current user has write access." )); return 0; } } bool path_get_config(wcstring &path) { int done = 0; wcstring res; const env_var_t xdg_dir = env_get_string( L"XDG_CONFIG_HOME" ); if( ! xdg_dir.missing() ) { res = xdg_dir + L"/fish"; if( !create_directory( res.c_str() ) ) { done = 1; } } else { const env_var_t home = env_get_string( L"HOME" ); if( ! home.missing() ) { res = home + L"/.config/fish"; if( !create_directory( res.c_str() ) ) { done = 1; } } } if( done ) { path = res; return true; } else { debug( 0, _(L"Unable to create a configuration directory for fish. Your personal settings will not be saved. Please set the $XDG_CONFIG_HOME variable to a directory where the current user has write access." )); return false; } } static void replace_all(wcstring &str, const wchar_t *needle, const wchar_t *replacement) { size_t needle_len = wcslen(needle); size_t offset = 0; while((offset = str.find(needle, offset)) != wcstring::npos) { str.replace(offset, needle_len, replacement); offset += needle_len; } } void path_make_canonical( wcstring &path ) { /* Remove double slashes */ replace_all(path, L"//", L"/"); /* Remove trailing slashes */ size_t size = path.size(); while (size--) { if (path.at(size) != L'/') break; } /* Now size is either -1 (if the entire string was slashes) or is the index of the last non-slash character. Either way this will set it to the correct size. */ path.resize(size+1); }