diff --git a/expand.cpp b/expand.cpp index c6e851912..9e19cec0e 100644 --- a/expand.cpp +++ b/expand.cpp @@ -1351,22 +1351,13 @@ static wchar_t * expand_tilde_internal_compat( wchar_t *in ) void expand_tilde( wcstring &input) { - if( input[0] == L'~' ) + if( ! input.empty() && input.at(0) == L'~' ) { - input[0] = HOME_DIRECTORY; + input.at(0) = HOME_DIRECTORY; expand_tilde_internal( input ); } } -wchar_t * expand_tilde_compat( wchar_t *input ) -{ - if (input[0] == L'~') { - input[0] = HOME_DIRECTORY; - return expand_tilde_internal_compat(input); - } - return input; -} - /** Remove any internal separators. Also optionally convert wildcard characters to regular equivalents. This is done to support EXPAND_SKIP_WILDCARDS. diff --git a/expand.h b/expand.h index 14a092304..f9dd90d8f 100644 --- a/expand.h +++ b/expand.h @@ -162,7 +162,6 @@ wcstring expand_escape_variable( const wcstring &in ); \param input the string to tilde expand */ void expand_tilde(wcstring &input); -wchar_t * expand_tilde_compat( wchar_t *input ); /** diff --git a/highlight.cpp b/highlight.cpp index d4ceb2773..b74f52404 100644 --- a/highlight.cpp +++ b/highlight.cpp @@ -916,7 +916,7 @@ static void tokenize( const wchar_t * const buff, std::vector &color, const } else { - int is_cmd = 0; + bool is_cmd = false; int is_subcommand = 0; int mark = tok_get_pos( &tok ); color.at(tok_get_pos( &tok )) = HIGHLIGHT_COMMAND; @@ -984,11 +984,11 @@ static void tokenize( const wchar_t * const buff, std::vector &color, const function, since we don't have to stat any files for that */ - if( use_builtin ) - is_cmd = is_cmd || builtin_exists( cmd ); + if (! is_cmd && use_builtin ) + is_cmd = builtin_exists( cmd ); - if( use_function ) - is_cmd = is_cmd || function_exists_no_autoload( cmd, vars ); + if (! is_cmd && use_function ) + is_cmd = function_exists_no_autoload( cmd, vars ); /* Moving on to expensive tests @@ -997,12 +997,19 @@ static void tokenize( const wchar_t * const buff, std::vector &color, const /* Check if this is a regular command */ - if( use_command ) + if (! is_cmd && use_command ) { wcstring tmp; - is_cmd = is_cmd || path_get_path_string( cmd, tmp, vars ); + is_cmd = path_get_path_string( cmd, tmp, vars ); } + /* Maybe it is a path for a implicit cd command. */ + if (! is_cmd) + { + if (use_builtin || (use_function && function_exists_no_autoload( L"cd", vars))) + is_cmd = path_can_be_implicit_cd(cmd, NULL, working_directory.c_str()); + } + if( is_cmd ) { color.at(tok_get_pos( &tok )) = HIGHLIGHT_COMMAND; @@ -1181,7 +1188,7 @@ static void tokenize( const wchar_t * const buff, std::vector &color, const } -// PCA DOES_IO (calls is_potential_path, path_get_path, maybe others) +// PCA This function does I/O, (calls is_potential_path, path_get_path, maybe others) and so ought to only run on a background thread void highlight_shell( const wcstring &buff, std::vector &color, int pos, wcstring_list_t *error, const env_vars &vars ) { ASSERT_IS_BACKGROUND_THREAD(); diff --git a/parser.cpp b/parser.cpp index b6e03d16e..1d91e5b38 100644 --- a/parser.cpp +++ b/parser.cpp @@ -1956,10 +1956,28 @@ int parser_t::parse_job( process_t *p, p->actual_cmd = path_get_path( args.at(0).completion.c_str() ); err = errno; - /* - Check if the specified command exists - */ - if( p->actual_cmd == NULL ) + bool use_implicit_cd = false; + if (p->actual_cmd == NULL) + { + /* If the specified command does not exist, try using an implicit cd. */ + wcstring implicit_cd_path; + use_implicit_cd = path_can_be_implicit_cd(args.at(0).completion, &implicit_cd_path); + if (use_implicit_cd) + { + args.clear(); + args.push_back(completion_t(L"cd")); + args.push_back(completion_t(implicit_cd_path)); + + /* If we have defined a wrapper around cd, use it, otherwise use the cd builtin */ + if (use_function && function_exists(L"cd")) + p->type = INTERNAL_FUNCTION; + else + p->type = INTERNAL_BUILTIN; + } + } + + /* Check if the specified command exists */ + if( p->actual_cmd == NULL && ! use_implicit_cd ) { int tmp; diff --git a/path.cpp b/path.cpp index 1ffd91918..3dcfcadd8 100644 --- a/path.cpp +++ b/path.cpp @@ -431,13 +431,38 @@ wchar_t *path_allocate_cdpath( const wcstring &dir, const wchar_t *wd ) } -bool path_can_get_cdpath(const wcstring &in, const wchar_t *wd) { +bool path_can_get_cdpath(const wcstring &in, const wchar_t *wd) +{ wchar_t *tmp = path_allocate_cdpath(in, wd); bool result = (tmp != NULL); free(tmp); return result; } +bool path_can_be_implicit_cd(const wcstring &path, wcstring *out_path, const wchar_t *wd) +{ + wcstring exp_path = path; + expand_tilde(exp_path); + + bool result = false; + if (string_prefixes_string(L"/", exp_path) || + string_prefixes_string(L"./", exp_path) || + string_prefixes_string(L"../", exp_path) || + exp_path == L"..") + { + /* These paths can be implicit cd. Note that a single period cannot (that's used for sourcing files anyways) */ + wchar_t *cd_path = path_allocate_cdpath(exp_path, wd); + if (cd_path) + { + /* It worked. Return the path if desired */ + if (out_path) + out_path->assign(cd_path); + free(cd_path); + result = true; + } + } + return result; +} bool path_get_config(wcstring &path) { diff --git a/path.h b/path.h index eff5ac80c..9bac301b0 100644 --- a/path.h +++ b/path.h @@ -31,6 +31,9 @@ bool path_get_config(wcstring &path); */ wchar_t *path_get_path( const wchar_t *cmd ); +/** Returns whether the path can be used for an implicit cd command; if so, also returns the path by reference (if desired). This requires it to start with one of the allowed prefixes (., .., ~) and resolve to a directory. */ +bool path_can_be_implicit_cd(const wcstring &path, wcstring *out_path = NULL, const wchar_t *wd = NULL); + class env_vars; bool path_get_path_string(const wcstring &cmd, wcstring &output); bool path_get_path_string(const wcstring &cmd, wcstring &output, const env_vars &vars);