fish-shell/builtin_complete.cpp

649 lines
12 KiB
C++

/** \file builtin_complete.c Functions defining the complete builtin
Functions used for implementing the complete builtin.
*/
#include "config.h"
#include <stdlib.h>
#include <stdio.h>
#include <wchar.h>
#include <wctype.h>
#include <sys/types.h>
#include <termios.h>
#include <signal.h>
#include "fallback.h"
#include "util.h"
#include "wutil.h"
#include "builtin.h"
#include "common.h"
#include "complete.h"
#include "wgetopt.h"
#include "parser.h"
#include "reader.h"
/**
Internal storage for the builtin_complete_get_temporary_buffer() function.
*/
static const wchar_t *temporary_buffer;
/*
builtin_complete_* are a set of rather silly looping functions that
make sure that all the proper combinations of complete_add or
complete_remove get called. This is needed since complete allows you
to specify multiple switches on a single commandline, like 'complete
-s a -s b -s c', but the complete_add function only accepts one
short switch and one long switch.
*/
/**
Silly function
*/
static void builtin_complete_add2( const wchar_t *cmd,
int cmd_type,
const wchar_t *short_opt,
array_list_t *gnu_opt,
array_list_t *old_opt,
int result_mode,
const wchar_t *condition,
const wchar_t *comp,
const wchar_t *desc,
int flags )
{
int i;
const wchar_t *s;
for( s=short_opt; *s; s++ )
{
complete_add( cmd,
cmd_type,
*s,
0,
0,
result_mode,
condition,
comp,
desc,
flags );
}
for( i=0; i<al_get_count( gnu_opt ); i++ )
{
complete_add( cmd,
cmd_type,
0,
(wchar_t *)al_get(gnu_opt, i ),
0,
result_mode,
condition,
comp,
desc,
flags );
}
for( i=0; i<al_get_count( old_opt ); i++ )
{
complete_add( cmd,
cmd_type,
0,
(wchar_t *)al_get(old_opt, i ),
1,
result_mode,
condition,
comp,
desc,
flags );
}
if( al_get_count( old_opt )+al_get_count( gnu_opt )+wcslen(short_opt) == 0 )
{
complete_add( cmd,
cmd_type,
0,
0,
0,
result_mode,
condition,
comp,
desc,
flags );
}
}
/**
Silly function
*/
static void builtin_complete_add( array_list_t *cmd,
array_list_t *path,
const wchar_t *short_opt,
array_list_t *gnu_opt,
array_list_t *old_opt,
int result_mode,
int authoritative,
const wchar_t *condition,
const wchar_t *comp,
const wchar_t *desc,
int flags )
{
int i;
for( i=0; i<al_get_count( cmd ); i++ )
{
builtin_complete_add2( (const wchar_t *)al_get( cmd, i ),
COMMAND,
short_opt,
gnu_opt,
old_opt,
result_mode,
condition,
comp,
desc,
flags );
if( authoritative != -1 )
{
complete_set_authoritative( (const wchar_t *)al_get( cmd, i ),
COMMAND,
authoritative );
}
}
for( i=0; i<al_get_count( path ); i++ )
{
builtin_complete_add2( (const wchar_t *)al_get( path, i ),
PATH,
short_opt,
gnu_opt,
old_opt,
result_mode,
condition,
comp,
desc,
flags );
if( authoritative != -1 )
{
complete_set_authoritative( (const wchar_t *)al_get( path, i ),
PATH,
authoritative );
}
}
}
/**
Silly function
*/
static void builtin_complete_remove3( wchar_t *cmd,
int cmd_type,
wchar_t short_opt,
array_list_t *long_opt )
{
int i;
for( i=0; i<al_get_count( long_opt ); i++ )
{
complete_remove( cmd,
cmd_type,
short_opt,
(wchar_t *)al_get( long_opt, i ) );
}
}
/**
Silly function
*/
static void builtin_complete_remove2( wchar_t *cmd,
int cmd_type,
const wchar_t *short_opt,
array_list_t *gnu_opt,
array_list_t *old_opt )
{
const wchar_t *s = (wchar_t *)short_opt;
if( *s )
{
for( ; *s; s++ )
{
if( al_get_count( old_opt) + al_get_count( gnu_opt ) == 0 )
{
complete_remove(cmd,
cmd_type,
*s,
0 );
}
else
{
builtin_complete_remove3( cmd,
cmd_type,
*s,
gnu_opt );
builtin_complete_remove3( cmd,
cmd_type,
*s,
old_opt );
}
}
}
else
{
builtin_complete_remove3( cmd,
cmd_type,
0,
gnu_opt );
builtin_complete_remove3( cmd,
cmd_type,
0,
old_opt );
}
}
/**
Silly function
*/
static void builtin_complete_remove( array_list_t *cmd,
array_list_t *path,
const wchar_t *short_opt,
array_list_t *gnu_opt,
array_list_t *old_opt )
{
int i;
for( i=0; i<al_get_count( cmd ); i++ )
{
builtin_complete_remove2( (wchar_t *)al_get( cmd, i ),
COMMAND,
short_opt,
gnu_opt,
old_opt );
}
for( i=0; i<al_get_count( path ); i++ )
{
builtin_complete_remove2( (wchar_t *)al_get( path, i ),
PATH,
short_opt,
gnu_opt,
old_opt );
}
}
const wchar_t *builtin_complete_get_temporary_buffer()
{
ASSERT_IS_MAIN_THREAD();
return temporary_buffer;
}
/**
The complete builtin. Used for specifying programmable
tab-completions. Calls the functions in complete.c for any heavy
lifting. Defined in builtin_complete.c
*/
static int builtin_complete( parser_t &parser, wchar_t **argv )
{
ASSERT_IS_MAIN_THREAD();
int res=0;
int argc=0;
int result_mode=SHARED;
int remove = 0;
int authoritative = -1;
int flags = COMPLETE_AUTO_SPACE;
string_buffer_t short_opt;
array_list_t gnu_opt, old_opt;
const wchar_t *comp=L"", *desc=L"", *condition=L"";
wchar_t *do_complete = 0;
array_list_t cmd;
array_list_t path;
static int recursion_level=0;
al_init( &cmd );
al_init( &path );
sb_init( &short_opt );
al_init( &gnu_opt );
al_init( &old_opt );
argc = builtin_count_args( argv );
woptind=0;
while( res == 0 )
{
static const struct woption
long_options[] =
{
{
L"exclusive", no_argument, 0, 'x'
}
,
{
L"no-files", no_argument, 0, 'f'
}
,
{
L"require-parameter", no_argument, 0, 'r'
}
,
{
L"path", required_argument, 0, 'p'
}
,
{
L"command", required_argument, 0, 'c'
}
,
{
L"short-option", required_argument, 0, 's'
}
,
{
L"long-option", required_argument, 0, 'l'
}
,
{
L"old-option", required_argument, 0, 'o'
}
,
{
L"description", required_argument, 0, 'd'
}
,
{
L"arguments", required_argument, 0, 'a'
}
,
{
L"erase", no_argument, 0, 'e'
}
,
{
L"unauthoritative", no_argument, 0, 'u'
}
,
{
L"authoritative", no_argument, 0, 'A'
}
,
{
L"condition", required_argument, 0, 'n'
}
,
{
L"do-complete", optional_argument, 0, 'C'
}
,
{
L"help", no_argument, 0, 'h'
}
,
{
0, 0, 0, 0
}
}
;
int opt_index = 0;
int opt = wgetopt_long( argc,
argv,
L"a:c:p:s:l:o:d:frxeuAn:C::h",
long_options,
&opt_index );
if( opt == -1 )
break;
switch( opt )
{
case 0:
if(long_options[opt_index].flag != 0)
break;
sb_printf( sb_err,
BUILTIN_ERR_UNKNOWN,
argv[0],
long_options[opt_index].name );
builtin_print_help( parser, argv[0], sb_err );
res = 1;
break;
case 'x':
result_mode |= EXCLUSIVE;
break;
case 'f':
result_mode |= NO_FILES;
break;
case 'r':
result_mode |= NO_COMMON;
break;
case 'p':
case 'c':
{
wchar_t *a = unescape( woptarg, 1);
if( a )
{
al_push( (opt=='p'?&path:&cmd), a );
}
else
{
sb_printf( sb_err, L"%ls: Invalid token '%ls'\n", argv[0], woptarg );
res = 1;
}
break;
}
case 'd':
desc = woptarg;
break;
case 'u':
authoritative=0;
break;
case 'A':
authoritative=1;
break;
case 's':
sb_append( &short_opt, woptarg );
break;
case 'l':
al_push( &gnu_opt, woptarg );
break;
case 'o':
al_push( &old_opt, woptarg );
break;
case 'a':
comp = woptarg;
break;
case 'e':
remove = 1;
break;
case 'n':
condition = woptarg;
break;
case 'C':
do_complete = woptarg?woptarg:reader_get_buffer();
break;
case 'h':
builtin_print_help( parser, argv[0], sb_out );
return 0;
case '?':
builtin_unknown_option( parser, argv[0], argv[woptind-1] );
res = 1;
break;
}
}
if( !res )
{
if( condition && wcslen( condition ) )
{
if( parser.test( condition, 0, 0, 0 ) )
{
sb_printf( sb_err,
L"%ls: Condition '%ls' contained a syntax error\n",
argv[0],
condition );
parser.test( condition, 0, sb_err, argv[0] );
res = 1;
}
}
}
if( !res )
{
if( comp && wcslen( comp ) )
{
if( parser.test_args( comp, 0, 0 ) )
{
sb_printf( sb_err,
L"%ls: Completion '%ls' contained a syntax error\n",
argv[0],
comp );
parser.test_args( comp, sb_err, argv[0] );
res = 1;
}
}
}
if( !res )
{
if( do_complete )
{
std::vector<completion_t> comp;
const wchar_t *prev_temporary_buffer = temporary_buffer;
wchar_t *token;
parse_util_token_extent( do_complete, wcslen( do_complete ), &token, 0, 0, 0 );
temporary_buffer = do_complete;
if( recursion_level < 1 )
{
recursion_level++;
// comp = al_halloc( 0 );
complete2( do_complete, comp );
for( size_t i=0; i< comp.size() ; i++ )
{
const completion_t &next = comp.at( i );
const wchar_t *prepend;
if( next.flags & COMPLETE_NO_CASE )
{
prepend = L"";
}
else
{
prepend = token;
}
if( !(next.description).empty() )
{
sb_printf( sb_out, L"%ls%ls\t%ls\n", prepend, next.completion.c_str(), next.description.c_str() );
}
else
{
sb_printf( sb_out, L"%ls%ls\n", prepend, next.completion.c_str() );
}
}
// halloc_free( comp );
recursion_level--;
}
temporary_buffer = prev_temporary_buffer;
}
else if( woptind != argc )
{
sb_printf( sb_err,
_( L"%ls: Too many arguments\n" ),
argv[0] );
builtin_print_help( parser, argv[0], sb_err );
res = 1;
}
else if( (al_get_count( &cmd) == 0 ) && (al_get_count( &path) == 0 ) )
{
/* No arguments specified, meaning we print the definitions of
* all specified completions to stdout.*/
complete_print( sb_out );
}
else
{
if( remove )
{
builtin_complete_remove( &cmd,
&path,
(wchar_t *)short_opt.buff,
&gnu_opt,
&old_opt );
}
else
{
builtin_complete_add( &cmd,
&path,
(wchar_t *)short_opt.buff,
&gnu_opt,
&old_opt,
result_mode,
authoritative,
condition,
comp,
desc,
flags );
}
}
}
al_foreach( &cmd, &free );
al_foreach( &path, &free );
al_destroy( &cmd );
al_destroy( &path );
sb_destroy( &short_opt );
al_destroy( &gnu_opt );
al_destroy( &old_opt );
return res;
}