diff --git a/complete.cpp b/complete.cpp index 513c1b659..2d139a0dc 100644 --- a/complete.cpp +++ b/complete.cpp @@ -41,8 +41,6 @@ #include "intern.h" #include "parse_util.h" #include "parser_keywords.h" -#include "halloc.h" -#include "halloc_util.h" #include "wutil.h" #include "path.h" #include "builtin_scripts.h" @@ -131,29 +129,33 @@ typedef struct complete_entry_opt /** Short style option */ wchar_t short_opt; /** Long style option */ - const wchar_t *long_opt; + wcstring long_opt; /** Arguments to the option */ - const wchar_t *comp; + wcstring comp; /** Description of the completion */ - const wchar_t *desc; + wcstring desc; /** Condition under which to use the option */ - const wchar_t *condition; + wcstring condition; /** Must be one of the values SHARED, NO_FILES, NO_COMMON, EXCLUSIVE, and determines how completions should be performed on the argument after the switch. */ int result_mode; /** True if old style long options are used */ int old_mode; - /** Next option in the linked list */ - struct complete_entry_opt *next; /** Completion flags */ int flags; -} - complete_entry_opt_t; + + const wchar_t *localized_desc() const + { + const wchar_t *tmp = desc.c_str(); + return C_(tmp); + } +} complete_entry_opt_t; /** Struct describing a command completion */ +typedef std::list option_list_t; struct completion_entry_t { /** True if command is a path */ @@ -163,8 +165,10 @@ struct completion_entry_t wcstring cmd; /** String containing all short option characters */ wcstring short_opt_str; - /** Linked list of all options */ - complete_entry_opt_t *first_option; + + /** List of all options */ + option_list_t options; + /** True if no other options than the ones supplied are possible */ int authoritative; }; @@ -177,7 +181,7 @@ static completion_entry_list_t completion_entries; Table of completions conditions that have already been tested and the corresponding test results */ -static hash_table_t *condition_cache=0; +static std::map condition_cache; /* Autoloader for completions */ class completion_autoload_t : public autoload_t { @@ -222,12 +226,7 @@ static void complete_init() */ static void condition_cache_clear() { - if( condition_cache ) - { - hash_destroy( condition_cache ); - free( condition_cache ); - condition_cache = 0; - } + condition_cache.clear(); } /** @@ -236,63 +235,27 @@ static void condition_cache_clear() be evaluated once. condition_cache_clear must be called after a completion run to make sure that there are no stale completions. */ -static int condition_test( const wchar_t *condition ) +static int condition_test( const wcstring &condition ) { - const void *test_res; - - if( !condition || !wcslen(condition) ) + ASSERT_IS_MAIN_THREAD(); + + if( condition.empty() ) { // fwprintf( stderr, L"No condition specified\n" ); return 1; } - - if( !condition_cache ) - { - condition_cache = (hash_table_t *)malloc( sizeof( hash_table_t ) ); - if( !condition_cache ) - { - DIE_MEM(); - - } - - hash_init( condition_cache, - &hash_wcs_func, - &hash_wcs_cmp ); - - - } - - test_res = hash_get( condition_cache, condition ); - - if (test_res == CC_NOT_TESTED ) - { - test_res = exec_subshell( condition)?CC_FALSE:CC_TRUE; - hash_put( condition_cache, condition, test_res ); - - /* - Restore previous status information - */ - } - - if( wcscmp( (const wchar_t *)test_res, CC_TRUE ) == 0 ) - { - return 1; - } - - return 0; -} - - -/** - Recursively free all complete_entry_opt_t structs and their contents -*/ -static void complete_free_opt_recursive( complete_entry_opt_t *o ) -{ - if( !o ) - return; - - complete_free_opt_recursive( o->next ); - halloc_free( o ); + + bool test_res; + std::map::iterator cached_entry = condition_cache.find(condition); + if (cached_entry == condition_cache.end()) { + /* Compute new value and reinsert it */ + test_res = (0 == exec_subshell( condition)); + condition_cache[condition] = test_res; + } else { + /* Use the old value */ + test_res = cached_entry->second; + } + return test_res; } @@ -327,7 +290,6 @@ static completion_entry_t *complete_get_exact_entry( const wchar_t *cmd, { c = new completion_entry_t(); completion_entries.push_front(c); - c->first_option = 0; c->cmd = cmd; c->cmd_type = cmd_type; c->short_opt_str = L""; @@ -361,17 +323,14 @@ void complete_add( const wchar_t *cmd, const wchar_t *desc, int flags ) { - completion_entry_t *c; - complete_entry_opt_t *opt; - CHECK( cmd, ); + completion_entry_t *c; c = complete_get_exact_entry( cmd, cmd_type ); - - opt = (complete_entry_opt_t *)halloc( 0, sizeof( complete_entry_opt_t ) ); - - opt->next = c->first_option; - c->first_option = opt; + + c->options.push_front(complete_entry_opt_t()); + complete_entry_opt_t &opt = c->options.front(); + if( short_opt != L'\0' ) { int len = 1 + ((result_mode & NO_COMMON) != 0); @@ -383,24 +342,15 @@ void complete_add( const wchar_t *cmd, } } - opt->short_opt = short_opt; - opt->result_mode = result_mode; - opt->old_mode=old_mode; + opt.short_opt = short_opt; + opt.result_mode = result_mode; + opt.old_mode=old_mode; - opt->comp = comp?halloc_wcsdup(opt, comp):L""; - opt->condition = condition?halloc_wcsdup(opt, condition):L""; - opt->long_opt = long_opt?halloc_wcsdup(opt, long_opt):L"" ; - opt->flags = flags; - - if( desc && wcslen( desc ) ) - { - opt->desc = halloc_wcsdup( opt, desc ); - } - else - { - opt->desc = L""; - } - + if (comp) opt.comp = comp; + if (condition) opt.condition = condition; + if (long_opt) opt.long_opt = long_opt; + if (desc) opt.desc = desc; + opt.flags = flags; } /** @@ -411,31 +361,25 @@ void complete_add( const wchar_t *cmd, static bool complete_remove_entry( completion_entry_t *e, wchar_t short_opt, const wchar_t *long_opt ) { - complete_entry_opt_t *o, *oprev=0, *onext=0; - if(( short_opt == 0 ) && (long_opt == 0 ) ) { - complete_free_opt_recursive( e->first_option ); - e->first_option=0; + e->options.clear(); } else { - - for( o= e->first_option; o; o=onext ) + for (option_list_t::iterator iter = e->options.begin(); iter != e->options.end(); ) { - onext=o->next; - - if( ( short_opt==o->short_opt ) || - ( wcscmp( long_opt, o->long_opt ) == 0 ) ) + complete_entry_opt_t &o = *iter; + if(short_opt==o.short_opt || long_opt == o.long_opt) { /* fwprintf( stderr, L"remove option -%lc --%ls\n", o->short_opt?o->short_opt:L' ', o->long_opt ); */ - if( o->short_opt ) + if( o.short_opt ) { - size_t idx = e->short_opt_str.find(o->short_opt); + size_t idx = e->short_opt_str.find(o.short_opt); if (idx != wcstring::npos) { /* Consume all colons */ @@ -445,25 +389,18 @@ static bool complete_remove_entry( completion_entry_t *e, wchar_t short_opt, con e->short_opt_str.erase(idx, first_non_colon - idx); } } - - if( oprev == 0 ) - { - e->first_option = o->next; - } - else - { - oprev->next = o->next; - } - free( o ); + + /* Destroy this option and go to the next one */ + iter = e->options.erase(iter); } else { - oprev = o; + /* Just go to the next one */ + iter++; } } } - - return e->first_option == 0; + return e->options.empty(); } @@ -514,12 +451,11 @@ int complete_is_valid_option( const wchar_t *str, array_list_t *errors, bool allow_autoload ) { - complete_entry_opt_t *o; wcstring cmd, path; int found_match = 0; int authoritative = 1; int opt_found=0; - hash_table_t gnu_match_hash; + std::set gnu_match_set; int is_gnu_opt=0; int is_old_opt=0; int is_short_opt=0; @@ -566,10 +502,6 @@ int complete_is_valid_option( const wchar_t *str, short_validated.resize(wcslen(opt), 0); - hash_init( &gnu_match_hash, - &hash_wcs_func, - &hash_wcs_cmp ); - is_gnu_opt = opt[1]==L'-'; if( is_gnu_opt ) { @@ -614,19 +546,20 @@ int complete_is_valid_option( const wchar_t *str, if( is_gnu_opt ) { - for( o = i->first_option; o; o=o->next ) - { - if( o->old_mode ) + for (option_list_t::const_iterator iter = i->options.begin(); iter != i->options.end(); iter++) + { + const complete_entry_opt_t &o = *iter; + if( o.old_mode ) { continue; } - if( wcsncmp( &opt[2], o->long_opt, gnu_opt_len )==0) + if( wcsncmp( &opt[2], o.long_opt.c_str(), gnu_opt_len )==0) { - hash_put( &gnu_match_hash, o->long_opt, L"" ); + gnu_match_set.insert(o.long_opt); if( (wcsncmp( &opt[2], - o->long_opt, - wcslen( o->long_opt) )==0) ) + o.long_opt.c_str(), + o.long_opt.size())==0) ) { is_gnu_exact=1; } @@ -636,13 +569,15 @@ int complete_is_valid_option( const wchar_t *str, else { /* Check for old style options */ - for( o = i->first_option; o; o=o->next ) + for (option_list_t::const_iterator iter = i->options.begin(); iter != i->options.end(); iter++) { - if( !o->old_mode ) + const complete_entry_opt_t &o = *iter; + + if( !o.old_mode ) continue; - if( wcscmp( &opt[1], o->long_opt )==0) + if( wcscmp( &opt[1], o.long_opt.c_str() )==0) { opt_found = 1; is_old_opt = 1; @@ -720,10 +655,10 @@ int complete_is_valid_option( const wchar_t *str, if( is_gnu_opt ) { - opt_found = is_gnu_exact || (hash_get_count( &gnu_match_hash )==1); + opt_found = is_gnu_exact || (gnu_match_set.size() == 1); if( errors && !opt_found ) { - if( hash_get_count( &gnu_match_hash )==0) + if( gnu_match_set.empty()) { al_push( errors, wcsdupcat( _(L"Unknown option: "), L"'", opt, L"\'" ) ); @@ -737,8 +672,6 @@ int complete_is_valid_option( const wchar_t *str, } } - hash_destroy( &gnu_match_hash ); - return (authoritative && found_match)?opt_found:1; } @@ -1093,9 +1026,9 @@ static void complete_cmd( const wchar_t *cmd, \param desc Description of the completion \param comp_out The list into which the results will be inserted */ -static void complete_from_args( const wchar_t *str, - const wchar_t *args, - const wchar_t *desc, +static void complete_from_args( const wcstring &str, + const wcstring &args, + const wcstring &desc, std::vector &comp_out, int flags ) { @@ -1104,11 +1037,11 @@ static void complete_from_args( const wchar_t *str, parser_t parser(PARSER_TYPE_COMPLETIONS_ONLY); proc_push_interactive(0); - parser.eval_args( args, possible_comp ); + parser.eval_args( args.c_str(), possible_comp ); proc_pop_interactive(); - complete_strings( comp_out, str, desc, 0, possible_comp, flags ); + complete_strings( comp_out, str.c_str(), desc.c_str(), 0, possible_comp, flags ); // al_foreach( &possible_comp, &free ); // al_destroy( &possible_comp ); @@ -1117,10 +1050,10 @@ static void complete_from_args( const wchar_t *str, /** Match against an old style long option */ -static int param_match_old( complete_entry_opt_t *e, +static int param_match_old( const complete_entry_opt_t *e, const wchar_t *optstr ) { - return (optstr[0] == L'-') && (wcscmp( e->long_opt, &optstr[1] ) == 0); + return (optstr[0] == L'-') && (e->long_opt == &optstr[1]); } /** @@ -1135,7 +1068,7 @@ static int param_match( const complete_entry_opt_t *e, if( !e->old_mode && (wcsncmp( L"--", optstr, 2 ) == 0 )) { - if( wcscmp( e->long_opt, &optstr[2] ) == 0 ) + if( e->long_opt == &optstr[2]) { return 1; } @@ -1154,9 +1087,9 @@ static wchar_t *param_match2( const complete_entry_opt_t *e, return (wchar_t *)&optstr[2]; if( !e->old_mode && (wcsncmp( L"--", optstr, 2 ) == 0) ) { - int len = wcslen( e->long_opt ); + size_t len = e->long_opt.size(); - if( wcsncmp( e->long_opt, &optstr[2],len ) == 0 ) + if( wcsncmp( e->long_opt.c_str(), &optstr[2],len ) == 0 ) { if( optstr[len+2] == L'=' ) return (wchar_t *)&optstr[len+3]; @@ -1220,7 +1153,6 @@ static int complete_param( const wchar_t *cmd_orig, int use_switches, std::vector &comp_out ) { - complete_entry_opt_t *o; int use_common=1, use_files=1; @@ -1247,14 +1179,15 @@ static int complete_param( const wchar_t *cmd_orig, { /* Check if we are entering a combined option and argument (like --color=auto or -I/usr/include) */ - for( o = i->first_option; o; o=o->next ) + for (option_list_t::const_iterator oiter = i->options.begin(); oiter != i->options.end(); oiter++) { + const complete_entry_opt_t *o = &*oiter; wchar_t *arg; if( (arg=param_match2( o, str ))!=0 && condition_test( o->condition )) { use_common &= ((o->result_mode & NO_COMMON )==0); use_files &= ((o->result_mode & NO_FILES )==0); - complete_from_args( arg, o->comp, C_(o->desc), comp_out, o->flags ); + complete_from_args( arg, o->comp, o->localized_desc(), comp_out, o->flags ); } } @@ -1268,8 +1201,9 @@ static int complete_param( const wchar_t *cmd_orig, If we are using old style long options, check for them first */ - for( o = i->first_option; o; o=o->next ) + for (option_list_t::const_iterator oiter = i->options.begin(); oiter != i->options.end(); oiter++) { + const complete_entry_opt_t *o = &*oiter; if( o->old_mode ) { if( param_match_old( o, popt ) && condition_test( o->condition )) @@ -1277,7 +1211,7 @@ static int complete_param( const wchar_t *cmd_orig, old_style_match = 1; use_common &= ((o->result_mode & NO_COMMON )==0); use_files &= ((o->result_mode & NO_FILES )==0); - complete_from_args( str, o->comp, C_(o->desc), comp_out, o->flags ); + complete_from_args( str, o->comp, o->localized_desc(), comp_out, o->flags ); } } } @@ -1289,21 +1223,22 @@ static int complete_param( const wchar_t *cmd_orig, */ if( !old_style_match ) { - for( o = i->first_option; o; o=o->next ) - { + for (option_list_t::const_iterator oiter = i->options.begin(); oiter != i->options.end(); oiter++) + { + const complete_entry_opt_t *o = &*oiter; /* Gnu-style options with _optional_ arguments must be specified as a single token, so that it can be differed from a regular argument. */ - if( !o->old_mode && wcslen(o->long_opt) && !(o->result_mode & NO_COMMON) ) + if( !o->old_mode && ! o->long_opt.empty() && !(o->result_mode & NO_COMMON) ) continue; if( param_match( o, popt ) && condition_test( o->condition )) { use_common &= ((o->result_mode & NO_COMMON )==0); use_files &= ((o->result_mode & NO_FILES )==0); - complete_from_args( str, o->comp, C_(o->desc), comp_out, o->flags ); + complete_from_args( str, o->comp.c_str(), o->localized_desc(), comp_out, o->flags ); } } @@ -1314,8 +1249,9 @@ static int complete_param( const wchar_t *cmd_orig, if( use_common ) { - for( o = i->first_option; o; o=o->next ) - { + for (option_list_t::const_iterator oiter = i->options.begin(); oiter != i->options.end(); oiter++) + { + const complete_entry_opt_t *o = &*oiter; /* If this entry is for the base command, check if any of the arguments match @@ -1328,7 +1264,7 @@ static int complete_param( const wchar_t *cmd_orig, if( (o->short_opt == L'\0' ) && (o->long_opt[0]==L'\0')) { use_files &= ((o->result_mode & NO_FILES )==0); - complete_from_args( str, o->comp, C_(o->desc), comp_out, o->flags ); + complete_from_args( str, o->comp, o->localized_desc(), comp_out, o->flags ); } if( wcslen(str) > 0 && use_switches ) @@ -1339,7 +1275,7 @@ static int complete_param( const wchar_t *cmd_orig, if( o->short_opt != L'\0' && short_ok( str, o->short_opt, i->short_opt_str ) ) { - const wchar_t *desc = C_(o->desc ); + const wchar_t *desc = o->localized_desc(); wchar_t completion[2]; completion[0] = o->short_opt; completion[1] = 0; @@ -1380,7 +1316,7 @@ static int complete_param( const wchar_t *cmd_orig, else flags = COMPLETE_NO_CASE; - has_arg = !!wcslen( o->comp ); + has_arg = ! o->comp.empty(); req_arg = (o->result_mode & NO_COMMON ); if( !o->old_mode && ( has_arg && !req_arg ) ) @@ -1404,7 +1340,7 @@ static int complete_param( const wchar_t *cmd_orig, completion_allocate( comp_out, (wchar_t *)completion.buff, - C_(o->desc), + C_(o->desc.c_str()), flags ); sb_destroy( &completion ); @@ -1413,7 +1349,7 @@ static int complete_param( const wchar_t *cmd_orig, completion_allocate( comp_out, whole_opt.c_str() + offset, - C_(o->desc), + C_(o->desc.c_str()), flags ); } } @@ -1929,9 +1865,9 @@ void complete_print( string_buffer_t *out ) for (completion_entry_list_t::iterator iter = completion_entries.begin(); iter != completion_entries.end(); iter++) { completion_entry_t *e = *iter; - complete_entry_opt_t *o; - for( o= e->first_option; o; o=o->next ) - { + for (option_list_t::const_iterator oiter = e->options.begin(); oiter != e->options.end(); oiter++) + { + const complete_entry_opt_t *o = &*oiter; const wchar_t *modestr[] = { L"", @@ -1964,7 +1900,7 @@ void complete_print( string_buffer_t *out ) append_switch( out, L"description", - C_(o->desc) ); + C_(o->desc.c_str()) ); append_switch( out, L"arguments",