2006-10-19 19:50:23 +08:00
# include "config.h"
# include <stdlib.h>
# include <stdio.h>
# include <wchar.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <unistd.h>
# include <errno.h>
# include <libgen.h>
# include "fallback.h"
# include "util.h"
# include "common.h"
# include "env.h"
# include "wutil.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'" )
2011-12-27 11:18:46 +08:00
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 ) ;
2012-02-19 01:11:22 +08:00
if ( waccess ( new_cmd , X_OK ) = = 0 )
2011-12-27 11:18:46 +08:00
{
struct stat buff ;
2012-02-19 01:11:22 +08:00
if ( wstat ( new_cmd , & buff ) = = - 1 )
2011-12-27 11:18:46 +08:00
{
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 ;
2006-10-19 19:50:23 +08:00
2011-12-27 11:18:46 +08:00
}
2006-10-19 19:50:23 +08:00
2012-01-30 15:22:42 +08:00
wchar_t * path_get_path ( const wchar_t * cmd )
2006-10-19 19:50:23 +08:00
{
2008-01-09 03:31:45 +08:00
int err = ENOENT ;
2011-12-27 11:18:46 +08:00
2006-10-19 19:50:23 +08:00
CHECK ( cmd , 0 ) ;
2008-01-09 03:31:45 +08:00
2006-10-19 19:50:23 +08:00
debug ( 3 , L " path_get_path( '%ls' ) " , cmd ) ;
if ( wcschr ( cmd , L ' / ' ) ! = 0 )
{
if ( waccess ( cmd , X_OK ) = = 0 )
{
struct stat buff ;
2008-01-09 03:31:45 +08:00
if ( wstat ( cmd , & buff ) )
{
return 0 ;
}
2011-12-27 11:18:46 +08:00
2006-10-19 19:50:23 +08:00
if ( S_ISREG ( buff . st_mode ) )
2012-01-30 15:22:42 +08:00
return wcsdup ( cmd ) ;
2006-10-19 19:50:23 +08:00
else
2008-01-09 03:31:45 +08:00
{
errno = EACCES ;
2006-10-19 19:50:23 +08:00
return 0 ;
2008-01-09 03:31:45 +08:00
}
}
else
{
struct stat buff ;
wstat ( cmd , & buff ) ;
return 0 ;
2006-10-19 19:50:23 +08:00
}
2011-12-27 11:18:46 +08:00
2006-10-19 19:50:23 +08:00
}
else
{
2012-01-14 18:42:17 +08:00
env_var_t path = env_get_string ( L " PATH " ) ;
if ( path . missing ( ) )
2006-10-19 19:50:23 +08:00
{
2007-09-29 05:32:27 +08:00
if ( contains ( PREFIX L " /bin " , L " /bin " , L " /usr/bin " ) )
2006-10-19 19:50:23 +08:00
{
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 " ;
}
}
2011-12-27 11:18:46 +08:00
2006-10-19 19:50:23 +08:00
/*
Allocate string long enough to hold the whole command
*/
2012-01-30 15:22:42 +08:00
wchar_t * new_cmd = ( wchar_t * ) calloc ( wcslen ( cmd ) + path . size ( ) + 2 , sizeof ( wchar_t ) ) ;
2011-12-27 11:18:46 +08:00
2006-10-19 19:50:23 +08:00
/*
We tokenize a copy of the path , since strtok modifies
its arguments
*/
2012-01-14 18:42:17 +08:00
wchar_t * path_cpy = wcsdup ( path . c_str ( ) ) ;
2006-10-19 19:50:23 +08:00
wchar_t * state ;
2011-12-27 11:18:46 +08:00
2006-10-19 19:50:23 +08:00
if ( ( new_cmd = = 0 ) | | ( path_cpy = = 0 ) )
{
DIE_MEM ( ) ;
}
2012-03-26 16:21:10 +08:00
for ( const wchar_t * nxt_path = wcstok ( path_cpy , ARRAY_SEP_STR , & state ) ;
2006-10-19 19:50:23 +08:00
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 ;
}
2008-01-09 03:31:45 +08:00
err = EACCES ;
2011-12-27 11:18:46 +08:00
2006-10-19 19:50:23 +08:00
}
else
{
switch ( errno )
{
case ENOENT :
case ENAMETOOLONG :
case EACCES :
case ENOTDIR :
break ;
default :
{
debug ( 1 ,
MISSING_COMMAND_ERR_MSG ,
new_cmd ) ;
wperror ( L " access " ) ;
}
}
}
}
2012-03-01 10:09:20 +08:00
free ( new_cmd ) ;
2006-10-19 19:50:23 +08:00
free ( path_cpy ) ;
}
2008-01-09 03:31:45 +08:00
errno = err ;
2006-10-19 19:50:23 +08:00
return 0 ;
}
2012-02-08 14:44:10 +08:00
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 ;
}
2006-10-19 19:50:23 +08:00
2011-12-27 11:18:46 +08:00
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
{
2012-05-06 05:30:20 +08:00
const wchar_t * path = L " . " ;
2011-12-27 11:18:46 +08:00
2012-05-06 05:30:20 +08:00
// Respect CDPATH
env_var_t cdpath = env_get_string ( L " CDPATH " ) ;
if ( ! cdpath . missing_or_empty ( ) ) {
path = cdpath . c_str ( ) ;
printf ( " CDPATH: %ls \n " , path ) ;
}
2011-12-27 11:18:46 +08:00
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 ;
2012-02-19 01:58:54 +08:00
if ( wstat ( whole_path , & buf ) = = 0 )
2011-12-27 11:18:46 +08:00
{
if ( S_ISDIR ( buf . st_mode ) )
{
result = whole_path ;
success = true ;
break ;
}
else
{
err = ENOTDIR ;
}
}
else
{
2012-02-19 01:58:54 +08:00
if ( lwstat ( whole_path , & buf ) = = 0 )
2011-12-27 11:18:46 +08:00
{
err = EROTTEN ;
}
}
}
}
if ( ! success )
{
errno = err ;
}
return res ;
}
2012-02-09 03:48:51 +08:00
2012-05-09 17:33:42 +08:00
wchar_t * path_allocate_cdpath ( const wcstring & dir , const wchar_t * wd )
2006-10-19 19:50:23 +08:00
{
2012-02-19 10:54:36 +08:00
wchar_t * res = NULL ;
2007-09-21 01:29:28 +08:00
int err = ENOENT ;
2012-05-09 17:33:42 +08:00
if ( dir . empty ( ) )
return NULL ;
2012-02-19 10:54:36 +08:00
if ( wd ) {
size_t len = wcslen ( wd ) ;
assert ( wd [ len - 1 ] = = L ' / ' ) ;
}
wcstring_list_t paths ;
2012-05-09 17:33:42 +08:00
if ( dir . at ( 0 ) = = L ' / ' ) {
2012-02-19 10:54:36 +08:00
/* Absolute path */
paths . push_back ( dir ) ;
2012-05-09 17:33:42 +08:00
} else if ( string_prefixes_string ( L " ./ " , dir ) | |
string_prefixes_string ( L " ../ " , dir ) | |
dir = = L " . " | | dir = = L " .. " ) {
2012-02-19 10:54:36 +08:00
/* Path is relative to the working directory */
wcstring path ;
if ( wd )
path . append ( wd ) ;
path . append ( dir ) ;
paths . push_back ( path ) ;
} else {
wchar_t * path_cpy ;
wchar_t * state ;
2012-05-06 05:30:20 +08:00
// Respect CDPATH
env_var_t path = env_get_string ( L " CDPATH " ) ;
2012-05-07 05:53:19 +08:00
if ( path . missing_or_empty ( ) ) path = L " . " ; //We'll change this to the wd if we have one
2012-02-19 10:54:36 +08:00
path_cpy = wcsdup ( path . c_str ( ) ) ;
2012-03-26 16:21:10 +08:00
for ( const wchar_t * nxt_path = wcstok ( path_cpy , ARRAY_SEP_STR , & state ) ;
nxt_path ! = NULL ;
2012-02-19 10:54:36 +08:00
nxt_path = wcstok ( 0 , ARRAY_SEP_STR , & state ) )
{
2012-05-07 05:53:19 +08:00
if ( ! wcscmp ( nxt_path , L " . " ) & & wd ! = NULL ) {
// nxt_path is just '.', and we have a working directory, so use the wd instead
// TODO: if nxt_path starts with ./ we need to replace the . with the wd
nxt_path = wd ;
}
2012-05-09 17:33:42 +08:00
wcstring expanded_path = nxt_path ;
expand_tilde ( expanded_path ) ;
2006-10-19 19:50:23 +08:00
2012-05-09 17:33:42 +08:00
// debug( 2, L"woot %ls\n", expanded_path.c_str() );
2006-10-19 19:50:23 +08:00
2012-05-09 17:33:42 +08:00
if ( expanded_path . empty ( ) )
2012-02-19 10:54:36 +08:00
continue ;
2012-05-09 17:33:42 +08:00
wcstring whole_path = expanded_path ;
append_path_component ( whole_path , dir ) ;
2012-02-19 10:54:36 +08:00
paths . push_back ( whole_path ) ;
}
free ( path_cpy ) ;
}
2012-03-02 06:56:34 +08:00
for ( wcstring_list_t : : const_iterator iter = paths . begin ( ) ; iter ! = paths . end ( ) ; + + iter ) {
2006-10-19 19:50:23 +08:00
struct stat buf ;
2012-02-20 01:25:15 +08:00
const wcstring & dir = * iter ;
2006-10-19 19:50:23 +08:00
if ( wstat ( dir , & buf ) = = 0 )
{
if ( S_ISDIR ( buf . st_mode ) )
{
2012-02-20 01:25:15 +08:00
res = wcsdup ( dir . c_str ( ) ) ;
2012-02-19 10:54:36 +08:00
break ;
2006-10-19 19:50:23 +08:00
}
2007-09-21 01:29:28 +08:00
else
{
err = ENOTDIR ;
}
2006-10-19 19:50:23 +08:00
}
2012-02-19 10:54:36 +08:00
}
if ( ! res )
2007-09-21 01:29:28 +08:00
{
errno = err ;
}
2006-10-19 19:50:23 +08:00
return res ;
}
2012-02-09 03:48:51 +08:00
2012-02-19 10:54:36 +08:00
bool path_can_get_cdpath ( const wcstring & in , const wchar_t * wd ) {
2012-05-09 17:33:42 +08:00
wchar_t * tmp = path_allocate_cdpath ( in , wd ) ;
2012-02-01 08:50:03 +08:00
bool result = ( tmp ! = NULL ) ;
free ( tmp ) ;
return result ;
}
2006-10-19 19:50:23 +08:00
2012-02-09 03:48:51 +08:00
2012-02-06 08:42:24 +08:00
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 " ;
2012-02-19 01:58:54 +08:00
if ( ! create_directory ( res ) )
2012-02-06 08:42:24 +08:00
{
done = 1 ;
}
}
else
{
const env_var_t home = env_get_string ( L " HOME " ) ;
if ( ! home . missing ( ) )
{
res = home + L " /.config/fish " ;
2012-02-19 01:58:54 +08:00
if ( ! create_directory ( res ) )
2012-02-06 08:42:24 +08:00
{
done = 1 ;
}
}
2006-10-19 19:50:23 +08:00
}
2011-12-27 11:18:46 +08:00
2012-02-06 08:42:24 +08:00
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 ;
}
2006-10-19 19:50:23 +08:00
}
2012-02-08 13:23:12 +08:00
static void replace_all ( wcstring & str , const wchar_t * needle , const wchar_t * replacement )
2007-05-11 03:11:28 +08:00
{
2012-02-08 13:23:12 +08:00
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 ;
}
}
2007-05-11 03:11:28 +08:00
2012-02-08 13:23:12 +08:00
void path_make_canonical ( wcstring & path )
{
/* Remove double slashes */
2012-02-16 16:24:27 +08:00
size_t size ;
do {
size = path . size ( ) ;
replace_all ( path , L " // " , L " / " ) ;
} while ( path . size ( ) ! = size ) ;
2012-02-08 13:23:12 +08:00
/* Remove trailing slashes */
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 ) ;
2007-05-11 03:11:28 +08:00
}
2012-02-19 10:54:36 +08:00
bool path_is_valid ( const wcstring & path , const wcstring & working_directory )
{
bool path_is_valid ;
/* Some special paths are always valid */
if ( path . empty ( ) ) {
path_is_valid = false ;
} else if ( path = = L " . " | | path = = L " ./ " ) {
path_is_valid = true ;
} else if ( path = = L " .. " | | path = = L " ../ " ) {
path_is_valid = ( ! working_directory . empty ( ) & & working_directory ! = L " / " ) ;
} else if ( path . at ( 0 ) ! = ' / ' ) {
/* Prepend the working directory. Note that we know path is not empty here. */
wcstring tmp = working_directory ;
tmp . append ( path ) ;
path_is_valid = ( 0 = = waccess ( tmp . c_str ( ) , F_OK ) ) ;
} else {
/* Simple check */
path_is_valid = ( 0 = = waccess ( path . c_str ( ) , F_OK ) ) ;
}
return path_is_valid ;
}
2012-02-19 13:56:30 +08:00
bool paths_are_same_file ( const wcstring & path1 , const wcstring & path2 ) {
if ( path1 = = path2 )
return true ;
struct stat s1 , s2 ;
2012-02-20 01:25:15 +08:00
if ( wstat ( path1 , & s1 ) = = 0 & & wstat ( path2 , & s2 ) = = 0 ) {
2012-02-19 13:56:30 +08:00
return s1 . st_ino = = s2 . st_ino & & s1 . st_dev = = s2 . st_dev ;
} else {
return false ;
}
}
2012-02-19 10:54:36 +08:00
wcstring get_working_directory ( void ) {
wcstring wd = L " ./ " ;
wchar_t dir_path [ 4096 ] ;
const wchar_t * cwd = wgetcwd ( dir_path , 4096 ) ;
if ( cwd ) {
wd = cwd ;
/* Make sure the working directory ends with a slash */
if ( ! wd . empty ( ) & & wd . at ( wd . size ( ) - 1 ) ! = L ' / ' )
wd . push_back ( L ' / ' ) ;
}
return wd ;
}