Round of bug fixes and minor code improvements after the completions struct update. Moves the file description code to wildcard.c, where it was actually used. Simplifies the memory allocations in that code significantly. Makes sure directoriy names don't get a space inserted after the completion.

darcs-hash:20070225090524-ac50b-4d095bf8da7c788a7828e707556edbdc0bbf5000.gz
This commit is contained in:
axel 2007-02-25 19:05:24 +10:00
parent 9b10fa4762
commit a3c5718eb9
4 changed files with 389 additions and 332 deletions

View file

@ -65,61 +65,6 @@ These functions are used for storing and retrieving tab-completion data, as well
*/
#define COMPLETE_VAR_DESC_VAL _( L"Variable: %ls" )
/**
Description for generic executable
*/
#define COMPLETE_EXEC_DESC _( L"Executable" )
/**
Description for link to executable
*/
#define COMPLETE_EXEC_LINK_DESC _( L"Executable link" )
/**
Description for regular file
*/
#define COMPLETE_FILE_DESC _( L"File" )
/**
Description for character device
*/
#define COMPLETE_CHAR_DESC _( L"Character device" )
/**
Description for block device
*/
#define COMPLETE_BLOCK_DESC _( L"Block device" )
/**
Description for fifo buffer
*/
#define COMPLETE_FIFO_DESC _( L"Fifo" )
/**
Description for symlink
*/
#define COMPLETE_SYMLINK_DESC _( L"Symbolic link" )
/**
Description for symlink
*/
#define COMPLETE_DIRECTORY_SYMLINK_DESC _( L"Symbolic link to directory" )
/**
Description for Rotten symlink
*/
#define COMPLETE_ROTTEN_SYMLINK_DESC _( L"Rotten symbolic link" )
/**
Description for symlink loop
*/
#define COMPLETE_LOOP_SYMLINK_DESC _( L"Symbolic link loop" )
/**
Description for socket files
*/
#define COMPLETE_SOCKET_DESC _( L"Socket" )
/**
Description for directories
*/
#define COMPLETE_DIRECTORY_DESC _( L"Directory" )
/**
The command to run to get a description from a file suffix
*/
#define SUFFIX_CMD_STR L"mimedb 2>/dev/null -fd "
/**
The maximum number of commands on which to perform description
lookup. The lookup process is quite time consuming, so this should
@ -220,19 +165,13 @@ typedef struct complete_entry
/** First node in the linked list of all completion entries */
static complete_entry_t *first_entry=0;
/** Hashtable containing all descriptions that describe an executable */
static hash_table_t *suffix_hash=0;
/**
Table of completions conditions that have already been tested and
the corresponding test results
*/
static hash_table_t *condition_cache=0;
static void complete_free_entry( complete_entry_t *c );
static void clear_hash_entry( void *key, void *data );
/**
Create a new completion entry
@ -289,14 +228,6 @@ static void complete_destroy()
}
first_entry = 0;
if( suffix_hash )
{
hash_foreach( suffix_hash, &clear_hash_entry );
hash_destroy( suffix_hash );
free( suffix_hash );
suffix_hash=0;
}
parse_util_load_reset( L"fish_complete_path", 0 );
}
@ -404,15 +335,6 @@ static void complete_free_entry( complete_entry_t *c )
free( c );
}
/**
Free hash key and hash value
*/
static void clear_hash_entry( void *key, void *data )
{
free( (void *)key );
free( (void *)data );
}
/**
Search for an exactly matching completion entry
*/
@ -937,204 +859,6 @@ int complete_is_valid_argument( const wchar_t *str,
return 1;
}
static wchar_t *complete_get_desc_suffix_internal( const wchar_t *suff_orig )
{
wchar_t *suff = wcsdup( suff_orig );
wchar_t *cmd = wcsdupcat( SUFFIX_CMD_STR, suff );
wchar_t *desc = 0;
array_list_t l;
if( !suff || !cmd )
DIE_MEM();
al_init( &l );
if( exec_subshell( cmd, &l ) != -1 )
{
if( al_get_count( &l )>0 )
{
wchar_t *ln = (wchar_t *)al_get(&l, 0 );
if( wcscmp( ln, L"unknown" ) != 0 )
{
desc = wcsdup( ln);
/*
I have decided I prefer to have the description
begin in uppercase and the whole universe will just
have to accept it. Hah!
*/
desc[0]=towupper(desc[0]);
}
}
}
free(cmd);
al_foreach( &l, &free );
al_destroy( &l );
if( !desc )
{
desc = wcsdup(COMPLETE_FILE_DESC);
}
hash_put( suffix_hash, suff, desc );
return desc;
}
/**
Use the mimedb command to look up a description for a given suffix
*/
static const wchar_t *complete_get_desc_suffix( const wchar_t *suff_orig )
{
int len;
wchar_t *suff;
wchar_t *pos;
wchar_t *tmp;
wchar_t *desc;
len = wcslen(suff_orig );
if( len == 0 )
return COMPLETE_FILE_DESC;
if( !suffix_hash )
{
suffix_hash = malloc( sizeof( hash_table_t) );
if( !suffix_hash )
DIE_MEM();
hash_init( suffix_hash, &hash_wcs_func, &hash_wcs_cmp );
}
suff = wcsdup(suff_orig);
for( pos=suff; *pos; pos++ )
{
if( wcschr( L"?;#~@&", *pos ) )
{
*pos=0;
break;
}
}
tmp = escape( suff, 1 );
free(suff);
suff = tmp;
desc = (wchar_t *)hash_get( suffix_hash, suff );
if( !desc )
{
desc = complete_get_desc_suffix_internal( suff );
}
free( suff );
return desc;
}
const wchar_t *complete_get_desc( const wchar_t *filename )
{
static string_buffer_t *get_desc_buff=0;
struct stat buf;
CHECK( filename, 0 );
if( !get_desc_buff )
{
complete_init();
get_desc_buff = sb_halloc( global_context);
}
else
{
sb_clear( get_desc_buff );
}
if( lwstat( filename, &buf )==0)
{
if( S_ISCHR(buf.st_mode) )
{
sb_printf( get_desc_buff, L"%lc%ls", COMPLETE_SEP, COMPLETE_CHAR_DESC );
}
else if( S_ISBLK(buf.st_mode) )
sb_printf( get_desc_buff, L"%lc%ls", COMPLETE_SEP, COMPLETE_BLOCK_DESC );
else if( S_ISFIFO(buf.st_mode) )
sb_printf( get_desc_buff, L"%lc%ls", COMPLETE_SEP, COMPLETE_FIFO_DESC );
else if( S_ISLNK(buf.st_mode))
{
struct stat buf2;
if( wstat( filename, &buf2 ) == 0 )
{
if( S_ISDIR(buf2.st_mode) )
{
sb_printf( get_desc_buff, L"/%lc%ls", COMPLETE_SEP, COMPLETE_DIRECTORY_SYMLINK_DESC );
}
else if( waccess( filename, X_OK ) == 0 )
sb_printf( get_desc_buff, L"%lc%ls", COMPLETE_SEP, COMPLETE_EXEC_LINK_DESC );
else
sb_printf( get_desc_buff, L"%lc%ls", COMPLETE_SEP, COMPLETE_SYMLINK_DESC );
}
else
{
switch( errno )
{
case ENOENT:
{
sb_printf( get_desc_buff, L"%lc%ls", COMPLETE_SEP, COMPLETE_ROTTEN_SYMLINK_DESC );
break;
}
case ELOOP:
{
sb_printf( get_desc_buff, L"%lc%ls", COMPLETE_SEP, COMPLETE_LOOP_SYMLINK_DESC );
break;
}
}
/*
On unknown errors we do nothing. The file will be
given the default 'File' description.
*/
}
}
else if( S_ISSOCK(buf.st_mode))
sb_printf( get_desc_buff, L"%lc%ls", COMPLETE_SEP, COMPLETE_SOCKET_DESC );
else if( S_ISDIR(buf.st_mode) )
sb_printf( get_desc_buff, L"/%lc%ls", COMPLETE_SEP, COMPLETE_DIRECTORY_DESC );
else if( waccess( filename, X_OK ) == 0 )
{
sb_printf( get_desc_buff, L"%lc%ls", COMPLETE_SEP, COMPLETE_EXEC_DESC );
}
}
if( wcslen((wchar_t *)get_desc_buff->buff) == 0 )
{
wchar_t *suffix = wcsrchr( filename, L'.' );
if( suffix != 0 && !wcsrchr( suffix, L'/' ) )
{
sb_printf( get_desc_buff,
L"%lc%ls",
COMPLETE_SEP,
complete_get_desc_suffix( suffix ) );
}
else
{
sb_printf( get_desc_buff,
L"%lc%ls",
COMPLETE_SEP,
COMPLETE_FILE_DESC );
}
}
return (wchar_t *)get_desc_buff->buff;
}
/**
Copy any strings in possible_comp which have the specified prefix
@ -1177,7 +901,7 @@ static void complete_strings( array_list_t *comp_out,
wchar_t *next_str = (wchar_t *)al_get( possible_comp, i );
if( next_str )
{
wildcard_complete( next_str, wc, desc, desc_func, comp_out );
wildcard_complete( next_str, wc, desc, desc_func, comp_out, 0 );
}
}
@ -1230,7 +954,7 @@ static void complete_cmd_desc( const wchar_t *cmd, array_list_t *comp )
{
completion_t *c = (completion_t *)al_get( comp, i );
if( wcscmp( c->description, COMPLETE_DIRECTORY_DESC ) != 0 )
if( !wcslen( c->completion) || (c->completion[wcslen(c->completion)-1] != L'/' ))
{
skip = 0;
break;

View file

@ -200,15 +200,6 @@ void complete( const wchar_t *cmd, array_list_t *out );
*/
void complete_print( string_buffer_t *out );
/**
Obtain a description string for the file specified by the filename.
The returned value is a string constant and should not be freed.
\param filename The file for which to find a description string
*/
const wchar_t *complete_get_desc( const wchar_t *filename );
/**
Tests if the specified option is defined for the specified command
*/

View file

@ -17,6 +17,7 @@ wildcards using **.
#include <sys/stat.h>
#include <dirent.h>
#include <errno.h>
#include <string.h>
#include "fallback.h"
@ -29,6 +30,8 @@ wildcards using **.
#include "complete.h"
#include "reader.h"
#include "expand.h"
#include "exec.h"
#include "halloc_util.h"
/**
@ -44,6 +47,64 @@ wildcards using **.
*/
#define MAX_FILE_LENGTH 1024
/**
The command to run to get a description from a file suffix
*/
#define SUFFIX_CMD_STR L"mimedb 2>/dev/null -fd "
/**
Description for generic executable
*/
#define COMPLETE_EXEC_DESC _( L"Executable" )
/**
Description for link to executable
*/
#define COMPLETE_EXEC_LINK_DESC _( L"Executable link" )
/**
Description for regular file
*/
#define COMPLETE_FILE_DESC _( L"File" )
/**
Description for character device
*/
#define COMPLETE_CHAR_DESC _( L"Character device" )
/**
Description for block device
*/
#define COMPLETE_BLOCK_DESC _( L"Block device" )
/**
Description for fifo buffer
*/
#define COMPLETE_FIFO_DESC _( L"Fifo" )
/**
Description for symlink
*/
#define COMPLETE_SYMLINK_DESC _( L"Symbolic link" )
/**
Description for symlink
*/
#define COMPLETE_DIRECTORY_SYMLINK_DESC _( L"Symbolic link to directory" )
/**
Description for Rotten symlink
*/
#define COMPLETE_ROTTEN_SYMLINK_DESC _( L"Rotten symbolic link" )
/**
Description for symlink loop
*/
#define COMPLETE_LOOP_SYMLINK_DESC _( L"Symbolic link loop" )
/**
Description for socket files
*/
#define COMPLETE_SOCKET_DESC _( L"Socket" )
/**
Description for directories
*/
#define COMPLETE_DIRECTORY_DESC _( L"Directory" )
/** Hashtable containing all descriptions that describe an executable */
static hash_table_t *suffix_hash=0;
/**
Push the specified argument to the list if an identical string is
not already in the list. This function iterates over the list,
@ -67,6 +128,15 @@ static void al_push_check( array_list_t *l, const wchar_t *new )
}
/**
Free hash key and hash value
*/
static void clear_hash_entry( void *key, void *data )
{
free( (void *)key );
free( (void *)data );
}
int wildcard_has( const wchar_t *str, int internal )
{
wchar_t prev=0;
@ -166,7 +236,8 @@ static int wildcard_complete_internal( const wchar_t *orig,
int is_first,
const wchar_t *desc,
const wchar_t *(*desc_func)(const wchar_t *),
array_list_t *out )
array_list_t *out,
int flags )
{
if( !wc || !str || !orig)
{
@ -222,7 +293,7 @@ static int wildcard_complete_internal( const wchar_t *orig,
completion_allocate( out,
out_completion,
out_desc,
0 );
flags );
}
free ( out_completion );
@ -242,7 +313,7 @@ static int wildcard_complete_internal( const wchar_t *orig,
/* Try all submatches */
do
{
res |= wildcard_complete_internal( orig, str, wc+1, 0, desc, desc_func, out );
res |= wildcard_complete_internal( orig, str, wc+1, 0, desc, desc_func, out, flags );
if( res && !out )
break;
}
@ -252,11 +323,11 @@ static int wildcard_complete_internal( const wchar_t *orig,
}
else if( *wc == ANY_CHAR )
{
return wildcard_complete_internal( orig, str+1, wc+1, 0, desc, desc_func, out );
return wildcard_complete_internal( orig, str+1, wc+1, 0, desc, desc_func, out, flags );
}
else if( *wc == *str )
{
return wildcard_complete_internal( orig, str+1, wc+1, 0, desc, desc_func, out );
return wildcard_complete_internal( orig, str+1, wc+1, 0, desc, desc_func, out, flags );
}
return 0;
}
@ -265,9 +336,10 @@ int wildcard_complete( const wchar_t *str,
const wchar_t *wc,
const wchar_t *desc,
const wchar_t *(*desc_func)(const wchar_t *),
array_list_t *out )
array_list_t *out,
int flags )
{
return wildcard_complete_internal( str, str, wc, 1, desc, desc_func, out );
return wildcard_complete_internal( str, str, wc, 1, desc, desc_func, out, flags );
}
@ -293,14 +365,245 @@ static wchar_t *make_path( const wchar_t *base_dir, const wchar_t *name )
return long_name;
}
/**
Get the description of the specified filename. If this is a regular file, append the filesize to the description.
static wchar_t *complete_get_desc_suffix_internal( const wchar_t *suff_orig )
{
wchar_t *suff = wcsdup( suff_orig );
wchar_t *cmd = wcsdupcat( SUFFIX_CMD_STR, suff );
wchar_t *desc = 0;
array_list_t l;
if( !suff || !cmd )
DIE_MEM();
al_init( &l );
if( exec_subshell( cmd, &l ) != -1 )
{
if( al_get_count( &l )>0 )
{
wchar_t *ln = (wchar_t *)al_get(&l, 0 );
if( wcscmp( ln, L"unknown" ) != 0 )
{
desc = wcsdup( ln);
/*
I have decided I prefer to have the description
begin in uppercase and the whole universe will just
have to accept it. Hah!
*/
static void get_desc( wchar_t *fn, string_buffer_t *sb, int is_cmd )
desc[0]=towupper(desc[0]);
}
}
}
free(cmd);
al_foreach( &l, &free );
al_destroy( &l );
if( !desc )
{
desc = wcsdup(COMPLETE_FILE_DESC);
}
hash_put( suffix_hash, suff, desc );
return desc;
}
static void complete_get_desc_destroy_suffix_hash()
{
hash_foreach( suffix_hash, &clear_hash_entry );
hash_destroy( suffix_hash );
free( suffix_hash );
}
/**
Use the mimedb command to look up a description for a given suffix
*/
static const wchar_t *complete_get_desc_suffix( const wchar_t *suff_orig )
{
int len;
wchar_t *suff;
wchar_t *pos;
wchar_t *tmp;
wchar_t *desc;
len = wcslen(suff_orig );
if( len == 0 )
return COMPLETE_FILE_DESC;
if( !suffix_hash )
{
suffix_hash = malloc( sizeof( hash_table_t) );
if( !suffix_hash )
DIE_MEM();
hash_init( suffix_hash, &hash_wcs_func, &hash_wcs_cmp );
halloc_register_function_void( global_context, &complete_get_desc_destroy_suffix_hash );
}
suff = wcsdup(suff_orig);
/*
Drop characters that are commonly used as backup suffixes from the suffix
*/
for( pos=suff; *pos; pos++ )
{
if( wcschr( L"?;#~@&", *pos ) )
{
*pos=0;
break;
}
}
tmp = escape( suff, 1 );
free(suff);
suff = tmp;
desc = (wchar_t *)hash_get( suffix_hash, suff );
if( !desc )
{
desc = complete_get_desc_suffix_internal( suff );
}
free( suff );
return desc;
}
/**
Obtain a description string for the file specified by the filename.
The returned value is a string constant and should not be free'd.
\param filename The file for which to find a description string
\param lstat_res The result of calling lstat on the file
\param lbuf The struct buf output of calling lstat on the file
\param stat_res The result of calling stat on the file
\param buf The struct buf output of calling stat on the file
\param err The errno value after a failed stat call on the file.
*/
static const wchar_t *file_get_desc( const wchar_t *filename,
int lstat_res,
struct stat lbuf,
int stat_res,
struct stat buf,
int err )
{
wchar_t *suffix;
CHECK( filename, 0 );
if( !lstat_res )
{
if( S_ISLNK(lbuf.st_mode))
{
if( !stat_res )
{
if( S_ISDIR(buf.st_mode) )
{
return COMPLETE_DIRECTORY_SYMLINK_DESC;
}
else if( waccess( filename, X_OK ) == 0 )
{
return COMPLETE_EXEC_LINK_DESC;
}
return COMPLETE_SYMLINK_DESC;
}
else
{
switch( err )
{
case ENOENT:
{
return COMPLETE_ROTTEN_SYMLINK_DESC;
}
case ELOOP:
{
return COMPLETE_LOOP_SYMLINK_DESC;
}
}
/*
On unknown errors we do nothing. The file will be
given the default 'File' description or one based on the suffix.
*/
}
}
else if( S_ISCHR(buf.st_mode) )
{
return COMPLETE_CHAR_DESC;
}
else if( S_ISBLK(buf.st_mode) )
{
return COMPLETE_BLOCK_DESC;
}
else if( S_ISFIFO(buf.st_mode) )
{
return COMPLETE_FIFO_DESC;
}
else if( S_ISSOCK(buf.st_mode))
{
return COMPLETE_SOCKET_DESC;
}
else if( S_ISDIR(buf.st_mode) )
{
return COMPLETE_DIRECTORY_DESC;
}
else if( waccess( filename, X_OK ) == 0 )
{
return COMPLETE_EXEC_DESC;
}
}
suffix = wcsrchr( filename, L'.' );
if( suffix != 0 && !wcsrchr( suffix, L'/' ) )
{
return complete_get_desc_suffix( suffix );
}
return COMPLETE_FILE_DESC ;
}
/**
Add the specified filename if it matches the specified wildcard.
If the filename matches, first get the description of the specified
filename. If this is a regular file, append the filesize to the
description.
\param list the list to add he completion to
\param fullname the full filename of the file
\param completion the completion part of the file name
\param wc the wildcard to match against
\param is_cmd whether we are performing command completion
*/
static void wildcard_completion_allocate( array_list_t *list,
wchar_t *fullname,
wchar_t *completion,
wchar_t *wc,
int is_cmd )
{
const wchar_t *desc;
struct stat buf, lbuf;
static string_buffer_t *sb = 0;
struct stat buf;
int free_completion = 0;
int flags = 0;
int stat_res, lstat_res;
int stat_errno=0;
/*
This is a long long, not an off_t since we really need to know
@ -313,15 +616,36 @@ static void get_desc( wchar_t *fn, string_buffer_t *sb, int is_cmd )
}
;
if( !fn || !sb )
if( !sb )
{
debug( 2, L"Got null string on line %d of file %s", __LINE__, __FILE__ );
return;
sb = sb_halloc( global_context );
}
else
{
sb_clear( sb );
}
CHECK( fullname, );
sb_clear( sb );
if( wstat( fn, &buf ) )
/*
If the file is a symlink, we need to stat both the file itself
_and_ the destination file. But we try to avoid this with
non-symlinks by first doing an lstat, and if the file is not a
link we copy the results over to the regular stat buffer.
*/
if( ( lstat_res = lwstat( fullname, &lbuf ) ) )
{
sz=-1;
stat_res = lstat_res;
}
else
{
if( S_ISLNK(lbuf.st_mode))
{
if( ( stat_res = wstat( fullname, &buf ) ) )
{
sz=-1;
}
@ -330,10 +654,27 @@ static void get_desc( wchar_t *fn, string_buffer_t *sb, int is_cmd )
sz = (long long)buf.st_size;
}
desc = complete_get_desc( fn );
/*
In order to differentiate between e.g. rotten symlinks
and symlink loops, we also need to know the error status of wstat.
*/
stat_errno = errno;
}
else
{
stat_res = lstat_res;
memcpy( &buf, &lbuf, sizeof( struct stat ) );
sz = (long long)buf.st_size;
}
}
desc = file_get_desc( fullname, lstat_res, lbuf, stat_res, buf, stat_errno );
if( sz >= 0 && S_ISDIR(buf.st_mode) )
{
free_completion = 1;
flags = flags | COMPLETE_NO_SPACE;
completion = wcsdupcat( completion, L"/" );
sb_append( sb, desc );
}
else
@ -372,6 +713,11 @@ static void get_desc( wchar_t *fn, string_buffer_t *sb, int is_cmd )
}
}
}
wildcard_complete( completion, wc, (wchar_t *)sb->buff, 0, list, flags );
if( free_completion )
free( completion );
}
/**
@ -515,14 +861,11 @@ int wildcard_expand( const wchar_t *wc,
if( test_flags( long_name, flags ) )
{
get_desc( long_name,
&sb_desc,
flags & EXECUTABLES_ONLY );
completion_allocate( out,
wildcard_completion_allocate( out,
long_name,
name,
(wchar_t *)sb_desc.buff,
0 );
L"",
flags & EXECUTABLES_ONLY );
}
free( long_name );
@ -556,19 +899,17 @@ int wildcard_expand( const wchar_t *wc,
wc,
L"",
0,
0,
0 ) )
{
if( test_flags( long_name, flags ) )
{
get_desc( long_name,
&sb_desc,
wildcard_completion_allocate( out,
long_name,
name,
wc,
flags & EXECUTABLES_ONLY );
wildcard_complete( name,
wc,
(wchar_t *)sb_desc.buff,
0,
out );
}
}

View file

@ -90,6 +90,7 @@ int wildcard_complete( const wchar_t *str,
const wchar_t *wc,
const wchar_t *desc,
const wchar_t *(*desc_func)(const wchar_t *),
array_list_t *out );
array_list_t *out,
int flags );
#endif