Eliminated halloc usage from complete.cpp

This commit is contained in:
ridiculousfish 2012-02-08 16:15:53 -08:00
parent 4f8b4379f5
commit 1bbe901bcd

View file

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