diff --git a/FishsFish.xcodeproj/project.pbxproj b/FishsFish.xcodeproj/project.pbxproj index ccfcdb3ed..8ef0bb0f7 100644 --- a/FishsFish.xcodeproj/project.pbxproj +++ b/FishsFish.xcodeproj/project.pbxproj @@ -225,7 +225,7 @@ /* Begin PBXLegacyTarget section */ D0A084F713B3AC130099B651 /* FishsFish */ = { isa = PBXLegacyTarget; - buildArgumentsString = "-k ${ACTION}"; + buildArgumentsString = "-k ${ACTION} -j 3"; buildConfigurationList = D0A084FA13B3AC130099B651 /* Build configuration list for PBXLegacyTarget "FishsFish" */; buildPhases = ( ); diff --git a/common.cpp b/common.cpp index 3f22c0b9b..eb3bd582d 100644 --- a/common.cpp +++ b/common.cpp @@ -1826,12 +1826,14 @@ int create_directory( const wcstring &d ) return ok?0:-1; } +__attribute__((noinline)) void bugreport() { debug( 1, - _( L"This is a bug. " + _( L"This is a bug. Break on bugreport to debug." L"If you can reproduce it, please send a bug report to %s." ), PACKAGE_BUGREPORT ); + while (1) sleep(10000); } diff --git a/highlight.cpp b/highlight.cpp index 4ca9843f6..8f7adf32e 100644 --- a/highlight.cpp +++ b/highlight.cpp @@ -528,7 +528,150 @@ static int has_expand_reserved( const wchar_t *str ) return 0; } +bool autosuggest_handle_special(const wcstring &str, const env_vars &vars, const wcstring &working_directory, bool *outSuggestionOK) { + ASSERT_IS_BACKGROUND_THREAD(); + assert(outSuggestionOK != NULL); + + if (str.empty()) + return false; + + wcstring cmd; + bool had_cmd = false; + + bool handled = false; + bool suggestionOK = true; + + tokenizer tok; + for( tok_init( &tok, str.c_str(), TOK_SQUASH_ERRORS ); + tok_has_next( &tok ); + tok_next( &tok ) ) + { + int last_type = tok_last_type( &tok ); + + switch( last_type ) + { + case TOK_STRING: + { + if( had_cmd ) + { + if( cmd == L"cd" ) + { + wcstring dir = tok_last( &tok ); + if (expand_one(dir, EXPAND_SKIP_CMDSUBST)) + { + /* We can specially handle the cd command */ + handled = true; + bool is_help = string_prefixes_string(dir, L"--help") || string_prefixes_string(dir, L"-h"); + if (! is_help && ! path_can_get_cdpath(dir, working_directory.c_str())) { + suggestionOK = false; + break; + } + } + } + } + else + { + /* + Command. First check that the command actually exists. + */ + cmd = tok_last( &tok ); + bool expanded = expand_one(cmd, EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_VARIABLES); + if (! expanded || has_expand_reserved(cmd.c_str())) + { + } + else + { + int is_subcommand = 0; + int mark = tok_get_pos( &tok ); + + if( parser_keywords_is_subcommand( cmd ) ) + { + + int sw; + + if( cmd == L"builtin") + { + } + else if( cmd == L"command") + { + } + + tok_next( &tok ); + + sw = parser_keywords_is_switch( tok_last( &tok ) ); + + if( !parser_keywords_is_block( cmd ) && + sw == ARG_SWITCH ) + { + + } + else + { + if( sw == ARG_SKIP ) + { + mark = tok_get_pos( &tok ); + } + + is_subcommand = 1; + } + tok_set_pos( &tok, mark ); + } + + if( !is_subcommand ) + { + had_cmd = true; + } + } + + } + break; + } + + case TOK_REDIRECT_NOCLOB: + case TOK_REDIRECT_OUT: + case TOK_REDIRECT_IN: + case TOK_REDIRECT_APPEND: + case TOK_REDIRECT_FD: + { + if( !had_cmd ) + { + break; + } + tok_next( &tok ); + break; + } + + case TOK_PIPE: + case TOK_BACKGROUND: + { + had_cmd = false; + break; + } + + case TOK_END: + { + had_cmd = false; + break; + } + + case TOK_COMMENT: + { + break; + } + + case TOK_ERROR: + default: + { + break; + } + } + } + tok_destroy( &tok ); + + *outSuggestionOK = suggestionOK; + return handled; +} // This function does I/O static void tokenize( const wchar_t * const buff, int * const color, const int pos, wcstring_list_t *error, const env_vars &vars) { @@ -544,7 +687,7 @@ static void tokenize( const wchar_t * const buff, int * const color, const int p int use_function = 1; int use_command = 1; - int use_builtin = 1; + int use_builtin = 1; CHECK( buff, ); CHECK( color, ); @@ -563,7 +706,6 @@ static void tokenize( const wchar_t * const buff, int * const color, const int p tok_next( &tok ) ) { int last_type = tok_last_type( &tok ); - int prev_argc=0; switch( last_type ) { @@ -620,8 +762,6 @@ static void tokenize( const wchar_t * const buff, int * const color, const int p } else { - prev_argc=0; - /* Command. First check that the command actually exists. */ diff --git a/highlight.h b/highlight.h index fe56df5a3..275b83154 100644 --- a/highlight.h +++ b/highlight.h @@ -106,4 +106,7 @@ void highlight_universal( const wchar_t *buff, int *color, int pos, wcstring_lis */ rgb_color_t highlight_get_color( int highlight, bool is_background ); +bool autosuggest_handle_special(const wcstring &str, const env_vars &vars, const wcstring &working_directory, bool *outSuggestionOK); + #endif + diff --git a/history.cpp b/history.cpp index d4f8a7fd5..09657b5ea 100644 --- a/history.cpp +++ b/history.cpp @@ -694,27 +694,8 @@ int file_detection_context_t::perform_file_detection(bool test_all) { ASSERT_IS_BACKGROUND_THREAD(); valid_paths.clear(); int result = 1; - for (path_list_t::const_iterator iter = potential_paths.begin(); iter != potential_paths.end(); iter++) { - wcstring path = *iter; - - 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 { - /* Maybe append the working directory. Note that we know path is not empty here. */ - if (path.at(0) != '/') { - path.insert(0, working_directory); - } - path_is_valid = (0 == waccess(path, F_OK)); - } - - - if (path_is_valid) { + for (path_list_t::const_iterator iter = potential_paths.begin(); iter != potential_paths.end(); iter++) { + if (path_is_valid(*iter, working_directory)) { /* Push the original (possibly relative) path */ valid_paths.push_front(*iter); } else { @@ -736,17 +717,9 @@ bool file_detection_context_t::paths_are_valid(const path_list_t &paths) { file_detection_context_t::file_detection_context_t(history_t *hist, const wcstring &cmd) : history(hist), command(cmd), - when(time(NULL)) { - /* Stash the working directory. TODO: We should be respecting CDPATH here*/ - wchar_t dir_path[4096]; - const wchar_t *cwd = wgetcwd( dir_path, 4096 ); - if (cwd) { - wcstring wd = cwd; - /* Make sure the working directory ends with a slash */ - if (! wd.empty() && wd.at(wd.size() - 1) != L'/') - wd.push_back(L'/'); - working_directory.swap(wd); - } + when(time(NULL)), + working_directory(get_working_directory()) +{ } static int threaded_perform_file_detection(file_detection_context_t *ctx) { diff --git a/path.cpp b/path.cpp index 8a05adf17..57a8b1239 100644 --- a/path.cpp +++ b/path.cpp @@ -336,96 +336,91 @@ bool path_get_cdpath_string(const wcstring &dir_str, wcstring &result, const env } -wchar_t *path_allocate_cdpath( const wchar_t *dir ) +wchar_t *path_allocate_cdpath( const wchar_t *dir, const wchar_t *wd ) { - wchar_t *res = 0; + wchar_t *res = NULL; int err = ENOENT; if( !dir ) return 0; + + if (wd) { + size_t len = wcslen(wd); + assert(wd[len - 1] == L'/'); + } + + wcstring_list_t paths; + + if (dir[0] == L'/') { + /* Absolute path */ + paths.push_back(dir); + } else if (wcsncmp(dir, L"./", 2) == 0 || + wcsncmp(dir, L"../", 3) == 0 || + wcscmp(dir, L".") == 0 || + wcscmp(dir, L"..") == 0) { + /* 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; + const wchar_t *nxt_path; + wchar_t *state; + wchar_t *whole_path; + env_var_t path = env_get_string(L"CDPATH"); + if (path.missing()) + path = wd ? wd : L"."; - if( dir[0] == L'/'|| (wcsncmp( dir, L"./", 2 )==0) ) - { + nxt_path = path.c_str(); + path_cpy = wcsdup( path.c_str() ); + + 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 ); + paths.push_back(whole_path); + free( whole_path ); + } + free( path_cpy ); + } + + for (wcstring_list_t::const_iterator iter = paths.begin(); iter != paths.end(); iter++) { struct stat buf; + const wchar_t *dir = iter->c_str(); if( wstat( dir, &buf ) == 0 ) { if( S_ISDIR(buf.st_mode) ) { res = wcsdup(dir); + break; } 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 ) + } + + if( !res ) { errno = err; } @@ -434,8 +429,8 @@ wchar_t *path_allocate_cdpath( const wchar_t *dir ) } -bool path_can_get_cdpath(const wcstring &in) { - wchar_t *tmp = path_allocate_cdpath(in.c_str()); +bool path_can_get_cdpath(const wcstring &in, const wchar_t *wd) { + wchar_t *tmp = path_allocate_cdpath(in.c_str(), wd); bool result = (tmp != NULL); free(tmp); return result; @@ -512,3 +507,37 @@ void path_make_canonical( wcstring &path ) path.resize(size+1); } +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; +} + +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; +} diff --git a/path.h b/path.h index 60496e35a..86a2bf03a 100644 --- a/path.h +++ b/path.h @@ -49,11 +49,12 @@ bool path_get_path_string(const wcstring &cmd, wcstring &output, const env_vars will be returned. \param in The name of the directory. + \param wd The working directory, or NULL to use the default. The working directory should have a slash appended at the end. \return 0 if the command can not be found, the path of the command otherwise. The path should be free'd with free(). */ -wchar_t *path_allocate_cdpath( const wchar_t *in ); -bool path_can_get_cdpath(const wcstring &in); +wchar_t *path_allocate_cdpath( const wchar_t *in, const wchar_t *wd = NULL); +bool path_can_get_cdpath(const wcstring &in, const wchar_t *wd = NULL); bool path_get_cdpath_string(const wcstring &in, wcstring &out, const env_vars &vars); /** @@ -62,5 +63,8 @@ bool path_get_cdpath_string(const wcstring &in, wcstring &out, const env_vars &v */ void path_make_canonical( wcstring &path ); +bool path_is_valid(const wcstring &path, const wcstring &working_directory); + +wcstring get_working_directory(void); #endif diff --git a/reader.cpp b/reader.cpp index 8d5e0d57e..69269c02f 100644 --- a/reader.cpp +++ b/reader.cpp @@ -103,6 +103,7 @@ commence. #include "screen.h" #include "iothread.h" #include "intern.h" +#include "path.h" #include "parse_util.h" @@ -1270,31 +1271,43 @@ static void run_pager( wchar_t *prefix, int is_quoted, const std::vectorsearcher.go_backwards()) { - history_item_t item = ctx->searcher.current_item(); - /* See if the item has any required paths */ - bool item_ok; - const path_list_t &paths = item.get_required_paths(); - if (paths.empty()) { - item_ok = true; - } else { - ctx->detector.potential_paths = paths; - item_ok = ctx->detector.paths_are_valid(paths); - } - if (item_ok) - return 1; - } - return 0; + return ctx->threaded_autosuggest(); } static bool can_autosuggest(void) { @@ -2587,6 +2600,7 @@ static int read_i() data->buff_pos=0; data->command_line.clear(); + data->check_size(); reader_run_command( parser, tmp ); free( (void *)tmp ); if( data->end_loop)