Make fish avoid iterating through user list when completing file part of directory starting with tilde. Also add a timeout to the directory iteration, to protect against humongously large user databases.

darcs-hash:20090202224645-ac50b-353047a73e4d6f494f470fe2ea6c4a34b486d302.gz
This commit is contained in:
axel 2009-02-03 08:46:45 +10:00
parent 05341b055b
commit 72025a6a38
4 changed files with 133 additions and 137 deletions

View file

@ -1805,3 +1805,18 @@ void sb_format_size( string_buffer_t *sb,
}
}
}
double timef()
{
int time_res;
struct timeval tv;
time_res = gettimeofday(&tv, 0);
if( time_res )
{
return NAN;
}
return (double)tv.tv_sec + 0.000001*tv.tv_usec;
}

View file

@ -447,5 +447,15 @@ void bugreport();
void sb_format_size( string_buffer_t *sb,
long long sz );
/**
Return the number of seconds from the UNIX epoch, with subsecond
precision. This function uses the gettimeofday function, and will
have the same precision as that function.
If an error occurs, NAN is returned.
*/
double timef();
#endif

View file

@ -1,6 +1,6 @@
/** \file complete.c Functions related to tab-completion.
These functions are used for storing and retrieving tab-completion data, as well as for performing tab-completion.
These functions are used for storing and retrieving tab-completion data, as well as for performing tab-completion.
*/
#include "config.h"
@ -107,6 +107,14 @@ These functions are used for storing and retrieving tab-completion data, as well
#endif
/**
The maximum amount of time that we're willing to spend doing
username tilde completion. This special limit has been coded in
because user lookup can be extremely slow in cases of a humongous
LDAP database. (Google, I'm looking at you)
*/
#define MAX_USER_LOOKUP_TIME 0.2
/**
Struct describing a completion option entry.
@ -180,9 +188,9 @@ static void complete_free_entry( complete_entry_t *c );
*/
void completion_allocate( array_list_t *context,
const wchar_t *comp,
const wchar_t *desc,
int flags )
const wchar_t *comp,
const wchar_t *desc,
int flags )
{
completion_t *res = halloc( context, sizeof( completion_t) );
res->completion = halloc_wcsdup( context, comp );
@ -378,8 +386,8 @@ static complete_entry_t *complete_get_exact_entry( const wchar_t *cmd,
void complete_set_authoritative( const wchar_t *cmd,
int cmd_type,
int authoritative )
int cmd_type,
int authoritative )
{
complete_entry_t *c;
@ -473,9 +481,9 @@ static complete_entry_t *complete_remove_entry( complete_entry_t *e,
{
wchar_t *pos;
/* fwprintf( stderr,
L"remove option -%lc --%ls\n",
o->short_opt?o->short_opt:L' ',
o->long_opt );
L"remove option -%lc --%ls\n",
o->short_opt?o->short_opt:L' ',
o->long_opt );
*/
if( o->short_opt )
{
@ -1067,10 +1075,10 @@ static const wchar_t *complete_function_desc( const wchar_t *fn )
\param comp the list to add all completions to
*/
static void complete_cmd( const wchar_t *cmd,
array_list_t *comp,
int use_function,
int use_builtin,
int use_command )
array_list_t *comp,
int use_function,
int use_builtin,
int use_command )
{
wchar_t *path;
wchar_t *path_cpy;
@ -1124,18 +1132,18 @@ static void complete_cmd( const wchar_t *cmd,
add_slash = nxt_path[path_len-1]!=L'/';
nxt_completion = wcsdupcat( nxt_path,
add_slash?L"/":L"",
cmd );
add_slash?L"/":L"",
cmd );
if( ! nxt_completion )
continue;
prev_count = al_get_count( comp );
if( expand_string( 0,
nxt_completion,
comp,
ACCEPT_INCOMPLETE |
EXECUTABLES_ONLY ) != EXPAND_ERROR )
nxt_completion,
comp,
ACCEPT_INCOMPLETE |
EXECUTABLES_ONLY ) != EXPAND_ERROR )
{
for( i=prev_count; i<al_get_count( comp ); i++ )
{
@ -1188,8 +1196,8 @@ static void complete_cmd( const wchar_t *cmd,
{
wchar_t *nxt_completion=
wcsdupcat( nxt_path,
(nxt_path[wcslen(nxt_path)-1]==L'/'?L"":L"/"),
cmd );
(nxt_path[wcslen(nxt_path)-1]==L'/'?L"":L"/"),
cmd );
if( ! nxt_completion )
{
continue;
@ -1613,8 +1621,8 @@ static void complete_param_expand( wchar_t *str,
Complete the specified string as an environment variable
*/
static int complete_variable( const wchar_t *whole_var,
int start_offset,
array_list_t *comp_list )
int start_offset,
array_list_t *comp_list )
{
int i;
const wchar_t *var = &whole_var[start_offset];
@ -1671,9 +1679,9 @@ static int complete_variable( const wchar_t *whole_var,
sb_printf( &desc, COMPLETE_VAR_DESC_VAL, value );
completion_allocate( comp_list,
(wchar_t *)comp.buff,
(wchar_t *)desc.buff,
flags );
(wchar_t *)comp.buff,
(wchar_t *)desc.buff,
flags );
res =1;
free( value );
@ -1724,127 +1732,86 @@ static int try_complete_variable( const wchar_t *cmd,
static int try_complete_user( const wchar_t *cmd,
array_list_t *comp )
{
const wchar_t *first_char=0;
const wchar_t *p;
int mode = 0;
int res = 0;
const wchar_t *first_char=cmd;
int res=0;
double start_time = timef();
for( p=cmd; *p; p++ )
if( *first_char ==L'~' && !wcschr(first_char, L'/'))
{
switch( mode )
const wchar_t *user_name = first_char+1;
wchar_t *name_end = wcschr( user_name, L'~' );
if( name_end == 0 )
{
/*Between parameters*/
case 0:
switch( *p )
{
case L'\"':
mode=2;
p++;
first_char = p;
break;
case L' ':
case L'\t':
case L'\n':
case L'\r':
break;
default:
mode=1;
first_char = p;
}
break;
/*Inside non-quoted parameter*/
case 1:
switch( *p )
{
case L' ':
case L'\t':
case L'\n':
case L'\r':
mode = 0;
break;
}
break;
case 2:
switch( *p )
{
case L'\"':
if( *(p-1) != L'\\' )
mode =0;
break;
}
break;
}
}
struct passwd *pw;
int name_len = wcslen( user_name );
if( mode != 0 )
{
if( *first_char ==L'~' )
{
const wchar_t *user_name = first_char+1;
wchar_t *name_end = wcschr( user_name, L'~' );
if( name_end == 0 )
setpwent();
while((pw=getpwent()) != 0)
{
struct passwd *pw;
int name_len = wcslen( user_name );
double current_time = timef();
wchar_t *pw_name;
setpwent();
while((pw=getpwent()) != 0)
if( current_time - start_time > 0.2 )
{
wchar_t *pw_name = str2wcs( pw->pw_name );
if( pw_name )
{
if( wcsncmp( user_name, pw_name, name_len )==0 )
{
string_buffer_t desc;
sb_init( &desc );
sb_printf( &desc,
COMPLETE_USER_DESC,
pw_name );
completion_allocate( comp,
&pw_name[name_len],
(wchar_t *)desc.buff,
COMPLETE_NO_SPACE );
res=1;
sb_destroy( &desc );
}
else if( wcsncasecmp( user_name, pw_name, name_len )==0 )
{
string_buffer_t name;
string_buffer_t desc;
sb_init( &name );
sb_init( &desc );
sb_printf( &name,
L"~%ls",
pw_name );
sb_printf( &desc,
COMPLETE_USER_DESC,
pw_name );
completion_allocate( comp,
(wchar_t *)name.buff,
(wchar_t *)desc.buff,
COMPLETE_NO_CASE | COMPLETE_DONT_ESCAPE | COMPLETE_NO_SPACE );
res=1;
sb_destroy( &desc );
sb_destroy( &name );
}
free( pw_name );
}
return 1;
}
pw_name = str2wcs( pw->pw_name );
if( pw_name )
{
if( wcsncmp( user_name, pw_name, name_len )==0 )
{
string_buffer_t desc;
sb_init( &desc );
sb_printf( &desc,
COMPLETE_USER_DESC,
pw_name );
completion_allocate( comp,
&pw_name[name_len],
(wchar_t *)desc.buff,
COMPLETE_NO_SPACE );
res=1;
sb_destroy( &desc );
}
else if( wcsncasecmp( user_name, pw_name, name_len )==0 )
{
string_buffer_t name;
string_buffer_t desc;
sb_init( &name );
sb_init( &desc );
sb_printf( &name,
L"~%ls",
pw_name );
sb_printf( &desc,
COMPLETE_USER_DESC,
pw_name );
completion_allocate( comp,
(wchar_t *)name.buff,
(wchar_t *)desc.buff,
COMPLETE_NO_CASE | COMPLETE_DONT_ESCAPE | COMPLETE_NO_SPACE );
res=1;
sb_destroy( &desc );
sb_destroy( &name );
}
free( pw_name );
}
endpwent();
}
endpwent();
}
}
return res;}
return res;
}

View file

@ -469,4 +469,8 @@ long sysconf(int name);
#endif
#ifndef NAN
#define NAN (0.0/0.0)
#endif
#endif