Add support for calculating completions for arbitrary commands through the 'complete' builtin

darcs-hash:20060130165150-ac50b-5e2ef3bb0298dd5e1a5d6fbdade314cc73ef36f3.gz
This commit is contained in:
axel 2006-01-31 02:51:50 +10:00
parent 3efb0bf5cf
commit 521d09b6d0
22 changed files with 693 additions and 488 deletions

View File

@ -61,7 +61,7 @@ COMMON_OBJS := function.o builtin.o common.o complete.o env.o exec.o \
expand.o highlight.o history.o kill.o parser.o proc.o reader.o \ expand.o highlight.o history.o kill.o parser.o proc.o reader.o \
sanity.o tokenizer.o util.o wildcard.o wgetopt.o wutil.o input.o \ sanity.o tokenizer.o util.o wildcard.o wgetopt.o wutil.o input.o \
output.o intern.o env_universal.o env_universal_common.o \ output.o intern.o env_universal.o env_universal_common.o \
input_common.o event.o signal.o io.o translate.o input_common.o event.o signal.o io.o translate.o parse_util.o
# builtin_help.h exists, but builtin_help.c is autogenerated # builtin_help.h exists, but builtin_help.c is autogenerated
COMMON_OBJS_WITH_HEADER := builtin_help.o COMMON_OBJS_WITH_HEADER := builtin_help.o

View File

@ -1733,7 +1733,7 @@ static int builtin_status( wchar_t **argv )
case 'h': case 'h':
builtin_print_help( argv[0], sb_err ); builtin_print_help( argv[0], sb_err );
break; return 0;
case 'i': case 'i':
mode = INTERACTIVE; mode = INTERACTIVE;

View File

@ -159,6 +159,8 @@ int builtin_ulimit(wchar_t **argv);
*/ */
int builtin_complete(wchar_t **argv); int builtin_complete(wchar_t **argv);
const wchar_t *builtin_complete_get_temporary_buffer();
/** /**
This function works like wperror, but it prints its result into This function works like wperror, but it prints its result into
the sb_err string_buffer_t instead of to stderr. Used by the builtin the sb_err string_buffer_t instead of to stderr. Used by the builtin

View File

@ -24,6 +24,7 @@ Functions used for implementing the commandline builtin.
#include "input_common.h" #include "input_common.h"
#include "input.h" #include "input.h"
#include "translate.h" #include "translate.h"
#include "parse_util.h"
/** /**
Which part of the comandbuffer are we operating on Which part of the comandbuffer are we operating on
@ -48,6 +49,26 @@ enum
} }
; ;
/**
Returns the current commandline buffer.
*/
static const wchar_t *get_buffer()
{
const wchar_t *buff = builtin_complete_get_temporary_buffer();
if( !buff )
buff = reader_get_buffer();
return buff;
}
static int get_cursor_pos()
{
const wchar_t *buff = builtin_complete_get_temporary_buffer();
if( buff )
return wcslen( buff );
else
return reader_get_cursor_pos();
}
/** /**
Replace/append/insert the selection with/at/after the specified string. Replace/append/insert the selection with/at/after the specified string.
@ -57,14 +78,14 @@ enum
\param insert the string to insert \param insert the string to insert
\param append_mode can be one of REPLACE_MODE, INSERT_MODE or APPEND_MODE, affects the way the test update is performed \param append_mode can be one of REPLACE_MODE, INSERT_MODE or APPEND_MODE, affects the way the test update is performed
*/ */
static void replace_part( wchar_t *begin, static void replace_part( const wchar_t *begin,
wchar_t *end, const wchar_t *end,
wchar_t *insert, wchar_t *insert,
int append_mode ) int append_mode )
{ {
wchar_t *buff = reader_get_buffer(); const wchar_t *buff = get_buffer();
string_buffer_t out; string_buffer_t out;
int out_pos=reader_get_cursor_pos(); int out_pos=get_cursor_pos();
sb_init( &out ); sb_init( &out );
@ -88,7 +109,7 @@ static void replace_part( wchar_t *begin,
} }
case INSERT_MODE: case INSERT_MODE:
{ {
int cursor = reader_get_cursor_pos() -(begin-buff); int cursor = get_cursor_pos() -(begin-buff);
sb_append_substring( &out, begin, cursor ); sb_append_substring( &out, begin, cursor );
sb_append( &out, insert ); sb_append( &out, insert );
sb_append_substring( &out, begin+cursor, end-begin-cursor ); sb_append_substring( &out, begin+cursor, end-begin-cursor );
@ -109,8 +130,8 @@ static void replace_part( wchar_t *begin,
\param cut_at_cursor whether printing should stop at the surrent cursor position \param cut_at_cursor whether printing should stop at the surrent cursor position
\param tokenize whether the string should be tokenized, printing one string token on every line and skipping non-string tokens \param tokenize whether the string should be tokenized, printing one string token on every line and skipping non-string tokens
*/ */
static void write_part( wchar_t *begin, static void write_part( const wchar_t *begin,
wchar_t *end, const wchar_t *end,
int cut_at_cursor, int cut_at_cursor,
int tokenize ) int tokenize )
{ {
@ -119,7 +140,7 @@ static void write_part( wchar_t *begin,
wchar_t *buff; wchar_t *buff;
int pos; int pos;
pos = reader_get_cursor_pos()-(begin-reader_get_buffer()); pos = get_cursor_pos()-(begin-get_buffer());
if( tokenize ) if( tokenize )
{ {
@ -184,7 +205,7 @@ int builtin_commandline( wchar_t **argv )
int tokenize = 0; int tokenize = 0;
if( !reader_get_buffer() ) if( !get_buffer() )
{ {
sb_append2( sb_err, sb_append2( sb_err,
argv[0], argv[0],
@ -429,32 +450,42 @@ int builtin_commandline( wchar_t **argv )
buffer_part = STRING_MODE; buffer_part = STRING_MODE;
} }
wchar_t *begin, *end; const wchar_t *begin, *end;
switch( buffer_part ) switch( buffer_part )
{ {
case STRING_MODE: case STRING_MODE:
{ {
begin = reader_get_buffer(); begin = get_buffer();
end = begin+wcslen(begin); end = begin+wcslen(begin);
break; break;
} }
case PROCESS_MODE: case PROCESS_MODE:
{ {
reader_current_process_extent( &begin, &end ); parse_util_process_extent( get_buffer(),
get_cursor_pos(),
&begin,
&end );
break; break;
} }
case JOB_MODE: case JOB_MODE:
{ {
reader_current_job_extent( &begin, &end ); parse_util_job_extent( get_buffer(),
get_cursor_pos(),
&begin,
&end );
break; break;
} }
case TOKEN_MODE: case TOKEN_MODE:
{ {
reader_current_token_extent( &begin, &end, 0, 0 ); parse_util_token_extent( get_buffer(),
get_cursor_pos(),
&begin,
&end,
0, 0 );
break; break;
} }

View File

@ -19,8 +19,11 @@ Functions used for implementing the complete builtin.
#include "complete.h" #include "complete.h"
#include "wgetopt.h" #include "wgetopt.h"
#include "parser.h" #include "parser.h"
#include "reader.h"
#include "translate.h" #include "translate.h"
const static wchar_t *temporary_buffer;
/* /*
builtin_complete_* are a set of rather silly looping functions that builtin_complete_* are a set of rather silly looping functions that
make sure that all the proper combinations of complete_add or make sure that all the proper combinations of complete_add or
@ -235,6 +238,11 @@ static void builtin_complete_remove( array_list_t *cmd,
} }
const wchar_t *builtin_complete_get_temporary_buffer()
{
return temporary_buffer;
}
int builtin_complete( wchar_t **argv ) int builtin_complete( wchar_t **argv )
{ {
int res=0; int res=0;
@ -247,9 +255,18 @@ int builtin_complete( wchar_t **argv )
array_list_t gnu_opt, old_opt; array_list_t gnu_opt, old_opt;
wchar_t *comp=L"", *desc=L"", *condition=L"", *load=0; wchar_t *comp=L"", *desc=L"", *condition=L"", *load=0;
wchar_t *do_complete = 0;
array_list_t cmd; array_list_t cmd;
array_list_t path; array_list_t path;
static int recursion_level=0;
if( !is_interactive_session )
{
debug( 1, _(L"%ls: Command only available in interactive sessions"), argv[0] );
}
al_init( &cmd ); al_init( &cmd );
al_init( &path ); al_init( &path );
sb_init( &short_opt ); sb_init( &short_opt );
@ -320,6 +337,10 @@ int builtin_complete( wchar_t **argv )
L"load", required_argument, 0, 'y' L"load", required_argument, 0, 'y'
} }
, ,
{
L"do-complete", required_argument, 0, 'C'
}
,
{ {
0, 0, 0, 0 0, 0, 0, 0
} }
@ -330,7 +351,7 @@ int builtin_complete( wchar_t **argv )
int opt = wgetopt_long( argc, int opt = wgetopt_long( argc,
argv, argv,
L"a:c:p:s:l:o:d:frxeun:y:", L"a:c:p:s:l:o:d:frxeun:y:C:",
long_options, long_options,
&opt_index ); &opt_index );
if( opt == -1 ) if( opt == -1 )
@ -411,6 +432,9 @@ int builtin_complete( wchar_t **argv )
load = woptarg; load = woptarg;
break; break;
case 'C':
do_complete = woptarg?woptarg:reader_get_buffer();
break;
case '?': case '?':
// builtin_print_help( argv[0], sb_err ); // builtin_print_help( argv[0], sb_err );
@ -424,6 +448,40 @@ int builtin_complete( wchar_t **argv )
if( res != 0 ) if( res != 0 )
{ {
}
else if( do_complete )
{
array_list_t comp;
int i;
const wchar_t *prev_temporary_buffer = temporary_buffer;
temporary_buffer = do_complete;
if( recursion_level < 1 )
{
recursion_level++;
al_init( &comp );
complete( do_complete, &comp );
for( i=0; i<al_get_count( &comp ); i++ )
{
wchar_t *next = (wchar_t *)al_get( &comp, i );
wchar_t *sep = wcschr( next, COMPLETE_SEP );
if( sep )
*sep = L'\t';
sb_printf( sb_out, L"%ls\n", next );
}
al_foreach( &comp, (void (*)(const void *))&free );
al_destroy( &comp );
recursion_level--;
}
temporary_buffer = prev_temporary_buffer;
} }
else if( woptind != argc ) else if( woptind != argc )
{ {

View File

@ -36,6 +36,7 @@
#include "history.h" #include "history.h"
#include "intern.h" #include "intern.h"
#include "translate.h" #include "translate.h"
#include "parse_util.h"
#include "wutil.h" #include "wutil.h"
@ -2007,7 +2008,8 @@ static int try_complete_user( const wchar_t *cmd,
void complete( const wchar_t *cmd, void complete( const wchar_t *cmd,
array_list_t *comp ) array_list_t *comp )
{ {
wchar_t *begin, *end, *prev_begin, *prev_end, *buff; const wchar_t *begin, *end, *prev_begin, *prev_end;
wchar_t *buff;
tokenizer tok; tokenizer tok;
wchar_t *current_token=0, *current_command=0, *prev_token=0; wchar_t *current_token=0, *current_command=0, *prev_token=0;
@ -2017,6 +2019,8 @@ void complete( const wchar_t *cmd,
int old_error_max = error_max; int old_error_max = error_max;
int done=0; int done=0;
int cursor_pos = wcslen(cmd );
error_max=0; error_max=0;
/** /**
@ -2042,7 +2046,7 @@ void complete( const wchar_t *cmd,
if( !done ) if( !done )
{ {
reader_current_subshell_extent( &begin, &end ); parse_util_cmdsubst_extent( cmd, cursor_pos, &begin, &end );
if( !begin ) if( !begin )
done=1; done=1;
@ -2051,7 +2055,7 @@ void complete( const wchar_t *cmd,
if( !done ) if( !done )
{ {
pos = reader_get_cursor_pos()-(begin-reader_get_buffer()); pos = cursor_pos-(begin-cmd);
buff = wcsndup( begin, end-begin ); buff = wcsndup( begin, end-begin );
@ -2109,9 +2113,9 @@ void complete( const wchar_t *cmd,
Get the string to complete Get the string to complete
*/ */
reader_current_token_extent( &begin, &end, &prev_begin, &prev_end ); parse_util_token_extent( cmd, cursor_pos, &begin, &end, &prev_begin, &prev_end );
current_token = wcsndup( begin, reader_get_cursor_pos()-(begin-reader_get_buffer()) ); current_token = wcsndup( begin, cursor_pos-(begin-cmd) );
prev_token = prev_begin ? wcsndup( prev_begin, prev_end - prev_begin ): wcsdup(L""); prev_token = prev_begin ? wcsndup( prev_begin, prev_end - prev_begin ): wcsdup(L"");

1
exec.c
View File

@ -1077,7 +1077,6 @@ void exec( job_t *j )
p->completed = 1; p->completed = 1;
} }
io_buffer_destroy( io_buffer ); io_buffer_destroy( io_buffer );
io_buffer=0; io_buffer=0;

View File

@ -39,6 +39,7 @@ parameter expansion.
#include "tokenizer.h" #include "tokenizer.h"
#include "complete.h" #include "complete.h"
#include "translate.h" #include "translate.h"
#include "parse_util.h"
/** /**
Description for child process Description for child process
@ -1154,102 +1155,20 @@ static int expand_brackets( wchar_t *in, int flags, array_list_t *out )
return 1; return 1;
} }
int expand_locate_subshell( wchar_t *in,
wchar_t **begin,
wchar_t **end,
int allow_incomplete )
{
wchar_t *pos;
wchar_t prev=0;
int syntax_error=0;
int paran_count=0;
wchar_t *paran_begin=0, *paran_end=0;
for( pos=in; *pos; pos++ )
{
if( prev != '\\' )
{
if( wcschr( L"\'\"", *pos ) )
{
wchar_t *end = quote_end( pos );
if( end && *end)
{
pos=end;
}
else
break;
}
else
{
if( *pos == '(' )
{
if(( paran_count == 0)&&(paran_begin==0))
paran_begin = pos;
paran_count++;
}
else if( *pos == ')' )
{
paran_count--;
if( (paran_count == 0) && (paran_end == 0) )
{
paran_end = pos;
break;
}
if( paran_count < 0 )
{
syntax_error = 1;
break;
}
}
}
}
prev = *pos;
}
syntax_error |= (paran_count < 0 );
syntax_error |= ((paran_count>0)&&(!allow_incomplete));
if( syntax_error )
{
return -1;
}
if( paran_begin == 0 )
{
return 0;
}
*begin = paran_begin;
*end = paran_count?in+wcslen(in):paran_end;
/* assert( *begin >= in );
assert( *begin < (in+wcslen(in) ) );
assert( *end >= *begin );
assert( *end < (in+wcslen(in) ) );
*/
return 1;
}
/** /**
Perform subshell expansion Perform subshell expansion
*/ */
static int expand_subshell( wchar_t *in, array_list_t *out ) static int expand_subshell( wchar_t *in, array_list_t *out )
{ {
wchar_t *paran_begin=0, *paran_end=0; const wchar_t *paran_begin=0, *paran_end=0;
int len1, len2; int len1, len2;
wchar_t prev=0; wchar_t prev=0;
wchar_t *subcmd; wchar_t *subcmd;
array_list_t sub_res, tail_expand; array_list_t sub_res, tail_expand;
int i, j; int i, j;
wchar_t *item_begin; const wchar_t *item_begin;
switch( expand_locate_subshell(in, switch( parse_util_locate_cmdsubst(in,
&paran_begin, &paran_begin,
&paran_end, &paran_end,
0 ) ) 0 ) )

View File

@ -182,20 +182,6 @@ wchar_t *expand_escape_variable( const wchar_t *in );
*/ */
wchar_t *expand_tilde(wchar_t *in); wchar_t *expand_tilde(wchar_t *in);
/**
Locate the first subshell in the specified string.
\param in the string to search for subshells
\param begin the starting paranthesis of the subshell
\param end the ending paranthesis of the subshell
\param flags set this variable to ACCEPT_INCOMPLETE if in tab_completion mode
\return -1 on syntax error, 0 if no subshells exist and 1 on sucess
*/
int expand_locate_subshell( wchar_t *in,
wchar_t **begin,
wchar_t **end,
int flags );
/** /**
Tokenize the specified string into the specified array_list_t. Tokenize the specified string into the specified array_list_t.

View File

@ -86,16 +86,15 @@ void function_add( const wchar_t *name,
{ {
int i; int i;
wchar_t *cmd_end; wchar_t *cmd_end;
function_data_t *d;
if( function_exists( name ) ) if( function_exists( name ) )
function_remove( name ); function_remove( name );
d = malloc( sizeof( function_data_t ) );
function_data_t *d = malloc( sizeof( function_data_t ) );
d->definition_offset = count_lineno( parser_get_buffer(), current_block->tok_pos ); d->definition_offset = count_lineno( parser_get_buffer(), current_block->tok_pos );
d->cmd = wcsdup( val ); d->cmd = wcsdup( val );
cmd_end = d->cmd + wcslen(d->cmd)-1; cmd_end = d->cmd + wcslen(d->cmd)-1;
while( (cmd_end>d->cmd) && wcschr( L"\n\r\t ", *cmd_end ) ) while( (cmd_end>d->cmd) && wcschr( L"\n\r\t ", *cmd_end ) )
{ {
@ -105,6 +104,7 @@ void function_add( const wchar_t *name,
d->desc = desc?wcsdup( desc ):0; d->desc = desc?wcsdup( desc ):0;
d->is_binding = is_binding; d->is_binding = is_binding;
d->definition_file = reader_current_filename()?intern(reader_current_filename()):0; d->definition_file = reader_current_filename()?intern(reader_current_filename()):0;
hash_put( &function, intern(name), d ); hash_put( &function, intern(name), d );
for( i=0; i<al_get_count( events ); i++ ) for( i=0; i<al_get_count( events ); i++ )
@ -123,8 +123,8 @@ void function_remove( const wchar_t *name )
{ {
void *key; void *key;
function_data_t *d; function_data_t *d;
event_t ev; event_t ev;
ev.type=EVENT_ANY; ev.type=EVENT_ANY;
ev.function_name=name; ev.function_name=name;
event_remove( &ev ); event_remove( &ev );

View File

@ -18,6 +18,7 @@
#include "tokenizer.h" #include "tokenizer.h"
#include "proc.h" #include "proc.h"
#include "parser.h" #include "parser.h"
#include "parse_util.h"
#include "builtin.h" #include "builtin.h"
#include "function.h" #include "function.h"
#include "env.h" #include "env.h"
@ -366,9 +367,9 @@ void highlight_shell( wchar_t * buff,
{ {
wchar_t *begin, *end; wchar_t *begin, *end;
if( expand_locate_subshell( subpos, if( parse_util_locate_cmdsubst( subpos,
&begin, (const wchar_t **)&begin,
&end, (const wchar_t **)&end,
1) <= 0) 1) <= 0)
{ {
break; break;

View File

@ -11,3 +11,4 @@ complete -c complete -s d -l description -d (_ "Description of this completions"
complete -c complete -s u -l unauthorative -d (_ "Option list is not complete") complete -c complete -s u -l unauthorative -d (_ "Option list is not complete")
complete -c complete -s e -l erase -d (_ "Remove completion") complete -c complete -s e -l erase -d (_ "Remove completion")
complete -c complete -s h -l help -d (_ "Display help and exit") complete -c complete -s h -l help -d (_ "Display help and exit")
complete -c complete -s C -l do-complete -d (_ "Print all completions for the specified commandline")

View File

@ -1,10 +1,8 @@
# #
# Completion for sudo # Completion for sudo
# #
# Generate a list of commands
#
complete -c sudo -d (_ "Command to run") -x -a "(__fish_complete_command)" complete -c sudo -d (_ "Command to run") -x -a "(__fish_complete_subcommand)"
complete -c sudo -s h -d (_ "Display help and exit") complete -c sudo -s h -d (_ "Display help and exit")
complete -c sudo -s v -d (_ "Validate") complete -c sudo -s v -d (_ "Validate")

View File

@ -1,5 +1,5 @@
complete -c time -a "(__fish_complete_command)" -d (_ "Command") complete -c time -a "(__fish_complete_subcommand -- -o --output -f --format)" -d (_ "Command")
complete -c time -s f -l format -d (_ "Specify output format") -x complete -c time -s f -l format -d (_ "Specify output format") -x
complete -c time -s p -l portable -d (_ "Use the portable output format") complete -c time -s p -l portable -d (_ "Use the portable output format")

View File

@ -96,9 +96,46 @@ function __fish_complete_pids -d "Print a list of process identifiers along with
end end
function __fish_complete_command -d "Complete using all available commands" function __fish_complete_command -d "Complete using all available commands"
for i in $PATH; command ls $i; end printf "%s\n" (commandline -ct)(complete -C (commandline -ct))
end end
function __fish_complete_subcommand -d "Complete subcommand"
set -l res ""
set -l had_cmd 0
set -l cmd (commandline -cop) (commandline -ct)
set -l skip_next 1
for i in $cmd
if test "$skip_next" = 1
set skip_next 0
continue
end
if test "$had_cmd" = 1
set res "$res $i"
else
if contains $i $argv
set skip_next 1
continue
end
switch $i
case '-*'
case '*'
set had_cmd 1
set res $i
end
end
end
printf "%s\n" (commandline -ct)(complete -C $res)
end
function __fish_print_hostnames -d "Print a list of known hostnames" function __fish_print_hostnames -d "Print a list of known hostnames"

377
parse_util.c Normal file
View File

@ -0,0 +1,377 @@
/** \file parse_util.c
Various utility functions for parsing a command
*/
#include "config.h"
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <wctype.h>
#include <wchar.h>
#include <assert.h>
#include "util.h"
#include "wutil.h"
#include "common.h"
#include "tokenizer.h"
#include "parse_util.h"
int parse_util_locate_cmdsubst( const wchar_t *in,
const wchar_t **begin,
const wchar_t **end,
int allow_incomplete )
{
const wchar_t *pos;
wchar_t prev=0;
int syntax_error=0;
int paran_count=0;
const wchar_t *paran_begin=0, *paran_end=0;
for( pos=in; *pos; pos++ )
{
if( prev != '\\' )
{
if( wcschr( L"\'\"", *pos ) )
{
wchar_t *end = quote_end( pos );
if( end && *end)
{
pos=end;
}
else
break;
}
else
{
if( *pos == '(' )
{
if(( paran_count == 0)&&(paran_begin==0))
paran_begin = pos;
paran_count++;
}
else if( *pos == ')' )
{
paran_count--;
if( (paran_count == 0) && (paran_end == 0) )
{
paran_end = pos;
break;
}
if( paran_count < 0 )
{
syntax_error = 1;
break;
}
}
}
}
prev = *pos;
}
syntax_error |= (paran_count < 0 );
syntax_error |= ((paran_count>0)&&(!allow_incomplete));
if( syntax_error )
{
return -1;
}
if( paran_begin == 0 )
{
return 0;
}
*begin = paran_begin;
*end = paran_count?in+wcslen(in):paran_end;
/* assert( *begin >= in );
assert( *begin < (in+wcslen(in) ) );
assert( *end >= *begin );
assert( *end < (in+wcslen(in) ) );
*/
return 1;
}
void parse_util_cmdsubst_extent( const wchar_t *buff,
int cursor_pos,
const wchar_t **a,
const wchar_t **b )
{
const wchar_t *begin, *end;
const wchar_t *pos;
if( a )
*a=0;
if( b )
*b = 0;
if( !buff )
return;
pos = buff;
while( 1 )
{
int bc, ec;
if( parse_util_locate_cmdsubst( pos,
&begin,
&end,
1 ) <= 0)
{
begin=buff;
end = buff + wcslen(buff);
break;
}
if( !end )
{
end = buff + wcslen(buff);
}
bc = begin-buff;
ec = end-buff;
if(( bc < cursor_pos ) && (ec >= cursor_pos) )
{
begin++;
break;
}
pos = end+1;
}
if( a )
*a = begin;
if( b )
*b = end;
}
/**
Get the beginning and end of the job or process definition under the cursor
*/
static void job_or_process_extent( const wchar_t *buff,
int cursor_pos,
const wchar_t **a,
const wchar_t **b,
int process )
{
const wchar_t *begin, *end;
int pos;
wchar_t *buffcpy;
int finished=0;
tokenizer tok;
if( a )
*a=0;
if( b )
*b = 0;
parse_util_cmdsubst_extent( buff, cursor_pos, &begin, &end );
if( !end || !begin )
return;
pos = cursor_pos - (begin - buff);
// fwprintf( stderr, L"Subshell extent: %d %d %d\n", begin-buff, end-buff, pos );
if( a )
{
*a = begin;
}
if( b )
{
*b = end;
}
buffcpy = wcsndup( begin, end-begin );
if( !buffcpy )
{
die_mem();
}
// fwprintf( stderr, L"Strlen: %d\n", wcslen(buffcpy ) );
for( tok_init( &tok, buffcpy, TOK_ACCEPT_UNFINISHED );
tok_has_next( &tok ) && !finished;
tok_next( &tok ) )
{
int tok_begin = tok_get_pos( &tok );
// fwprintf( stderr, L".");
switch( tok_last_type( &tok ) )
{
case TOK_PIPE:
if( !process )
break;
case TOK_END:
case TOK_BACKGROUND:
{
// fwprintf( stderr, L"New cmd at %d\n", tok_begin );
if( tok_begin >= pos )
{
finished=1;
if( b )
*b = buff + tok_begin;
}
else
{
if( a )
*a = buff + tok_begin+1;
}
break;
}
}
}
// fwprintf( stderr, L"Res: %d %d\n", *a-buff, *b-buff );
free( buffcpy);
tok_destroy( &tok );
}
void parse_util_process_extent( const wchar_t *buff,
int pos,
const wchar_t **a,
const wchar_t **b )
{
job_or_process_extent( buff, pos, a, b, 1 );
}
void parse_util_job_extent( const wchar_t *buff,
int pos,
const wchar_t **a,
const wchar_t **b )
{
job_or_process_extent( buff,pos,a, b, 0 );
}
void parse_util_token_extent( const wchar_t *buff,
int cursor_pos,
const wchar_t **tok_begin,
const wchar_t **tok_end,
const wchar_t **prev_begin,
const wchar_t **prev_end )
{
const wchar_t *begin, *end;
int pos;
wchar_t *buffcpy;
tokenizer tok;
const wchar_t *a, *b, *pa, *pb;
a = b = pa = pb = 0;
parse_util_cmdsubst_extent( buff, cursor_pos, &begin, &end );
if( !end || !begin )
return;
pos = cursor_pos - (begin - buff);
a = buff + pos;
b = a;
pa = buff + pos;
pb = pa;
assert( begin >= buff );
assert( begin <= (buff+wcslen(buff) ) );
assert( end >= begin );
assert( end <= (buff+wcslen(buff) ) );
buffcpy = wcsndup( begin, end-begin );
if( !buffcpy )
{
die_mem();
}
for( tok_init( &tok, buffcpy, TOK_ACCEPT_UNFINISHED );
tok_has_next( &tok );
tok_next( &tok ) )
{
int tok_begin = tok_get_pos( &tok );
int tok_end=tok_begin;
/*
Calculate end of token
*/
if( tok_last_type( &tok ) == TOK_STRING )
tok_end +=wcslen(tok_last(&tok));
/*
Cursor was before beginning of this token, means that the
cursor is between two tokens, so we set it to a zero element
string and break
*/
if( tok_begin > pos )
{
a = b = buff + pos;
break;
}
/*
If cursor is inside the token, this is the token we are
looking for. If so, set a and b and break
*/
if( tok_end >= pos )
{
a = begin + tok_get_pos( &tok );
b = a + wcslen(tok_last(&tok));
// fwprintf( stderr, L"Whee %ls\n", *a );
break;
}
/*
Remember previous string token
*/
if( tok_last_type( &tok ) == TOK_STRING )
{
pa = begin + tok_get_pos( &tok );
pb = pa + wcslen(tok_last(&tok));
}
}
free( buffcpy);
tok_destroy( &tok );
if( tok_begin )
*tok_begin = a;
if( tok_end )
*tok_end = b;
if( prev_begin )
*prev_begin = pa;
if( prev_end )
*prev_end = pb;
assert( pa >= buff );
assert( pa <= (buff+wcslen(buff) ) );
assert( pb >= pa );
assert( pb <= (buff+wcslen(buff) ) );
}

50
parse_util.h Normal file
View File

@ -0,0 +1,50 @@
/** \file parse_util.h
Various utility functions for parsing a command
*/
#ifndef FISH_PARSE_UTIL_H
#define FISH_PARSE_UTIL_H
#include <wchar.h>
/**
Locate the first subshell in the specified string.
\param in the string to search for subshells
\param begin the starting paranthesis of the subshell
\param end the ending paranthesis of the subshell
\param flags set this variable to ACCEPT_INCOMPLETE if in tab_completion mode
\return -1 on syntax error, 0 if no subshells exist and 1 on sucess
*/
int parse_util_locate_cmdsubst( const wchar_t *in,
const wchar_t **begin,
const wchar_t **end,
int allow_incomplete );
void parse_util_cmdsubst_extent( const wchar_t *buff,
int cursor_pos,
const wchar_t **a,
const wchar_t **b );
void parse_util_process_extent( const wchar_t *buff,
int pos,
const wchar_t **a,
const wchar_t **b );
void parse_util_job_extent( const wchar_t *buff,
int pos,
const wchar_t **a,
const wchar_t **b );
void parse_util_token_extent( const wchar_t *buff,
int cursor_pos,
const wchar_t **tok_begin,
const wchar_t **tok_end,
const wchar_t **prev_begin,
const wchar_t **prev_end );
#endif

View File

@ -265,7 +265,7 @@ static int current_tokenizer_pos;
block_t *current_block=0; block_t *current_block=0;
/** List of called functions, used to help prevent infinite recursion */ /** List of called functions, used to help prevent infinite recursion */
static array_list_t forbidden_function; static array_list_t *forbidden_function;
/** /**
String index where the current job started. String index where the current job started.
@ -726,7 +726,7 @@ void parser_forbid_function( wchar_t *function )
if( function ) if( function )
debug( 2, L"Forbid %ls\n", function ); debug( 2, L"Forbid %ls\n", function );
*/ */
al_push( &forbidden_function, function?wcsdup(function):0 ); al_push( forbidden_function, function?wcsdup(function):0 );
} }
void parser_allow_function() void parser_allow_function()
@ -735,7 +735,7 @@ void parser_allow_function()
if( al_peek( &forbidden_function) ) if( al_peek( &forbidden_function) )
debug( 2, L"Allow %ls\n", al_peek( &forbidden_function) ); debug( 2, L"Allow %ls\n", al_peek( &forbidden_function) );
*/ */
free( (void *) al_pop( &forbidden_function ) ); free( (void *) al_pop( forbidden_function ) );
} }
void error( int ec, int p, const wchar_t *str, ... ) void error( int ec, int p, const wchar_t *str, ... )
@ -848,7 +848,8 @@ void parser_init()
{ {
al_init( &profile_data); al_init( &profile_data);
} }
al_init( &forbidden_function ); forbidden_function = al_new();
} }
/** /**
@ -924,8 +925,6 @@ void parser_destroy()
al_destroy( &profile_data ); al_destroy( &profile_data );
} }
al_destroy( &forbidden_function );
if( lineinfo ) if( lineinfo )
{ {
sb_destroy( lineinfo ); sb_destroy( lineinfo );
@ -933,6 +932,9 @@ void parser_destroy()
lineinfo = 0; lineinfo = 0;
} }
al_destroy( forbidden_function );
free( forbidden_function );
} }
/** /**
@ -1236,7 +1238,7 @@ wchar_t *parser_current_line()
*/ */
if( !is_interactive || is_function() || (current_line_width!=0) ) if( !is_interactive || is_function() || (current_line_width!=0) )
{ {
// Workaround since it seems impossible to print 0 copies of a character using printf // Workaround since it seems impossible to print 0 copies of a character using %*lc
if( offset+current_line_width ) if( offset+current_line_width )
{ {
sb_printf( lineinfo, sb_printf( lineinfo,
@ -1875,7 +1877,7 @@ static int parse_job( process_t *p,
int nxt_forbidden; int nxt_forbidden;
wchar_t *forbid; wchar_t *forbid;
forbid = (wchar_t *)(al_get_count( &forbidden_function)?al_peek( &forbidden_function ):0); forbid = (wchar_t *)(al_get_count( forbidden_function)?al_peek( forbidden_function ):0);
nxt_forbidden = forbid && (wcscmp( forbid, nxt) == 0 ); nxt_forbidden = forbid && (wcscmp( forbid, nxt) == 0 );
/* /*
@ -1887,7 +1889,7 @@ static int parse_job( process_t *p,
/* /*
Check if we have reached the maximum recursion depth Check if we have reached the maximum recursion depth
*/ */
if( al_get_count( &forbidden_function ) > MAX_RECURSION_DEPTH ) if( al_get_count( forbidden_function ) > MAX_RECURSION_DEPTH )
{ {
error( SYNTAX_ERROR, error( SYNTAX_ERROR,
tok_get_pos( tok ), tok_get_pos( tok ),
@ -2351,6 +2353,15 @@ int eval( const wchar_t *cmd, io_data_t *io, int block_type )
tokenizer *previous_tokenizer=current_tokenizer; tokenizer *previous_tokenizer=current_tokenizer;
block_t *start_current_block = current_block; block_t *start_current_block = current_block;
io_data_t *prev_io = block_io; io_data_t *prev_io = block_io;
array_list_t *prev_forbidden = forbidden_function;
if( block_type == SUBST )
{
forbidden_function = al_new();
}
forbid_count = al_get_count( forbidden_function );
block_io = io; block_io = io;
job_reap( 0 ); job_reap( 0 );
@ -2387,7 +2398,6 @@ int eval( const wchar_t *cmd, io_data_t *io, int block_type )
parser_push_block( block_type ); parser_push_block( block_type );
forbid_count = al_get_count( &forbidden_function );
tok_init( current_tokenizer, cmd, 0 ); tok_init( current_tokenizer, cmd, 0 );
error_code = 0; error_code = 0;
@ -2447,19 +2457,28 @@ int eval( const wchar_t *cmd, io_data_t *io, int block_type )
tok_destroy( current_tokenizer ); tok_destroy( current_tokenizer );
free( current_tokenizer ); free( current_tokenizer );
while( forbid_count < al_get_count( &forbidden_function )) while( al_get_count( forbidden_function ) > forbid_count )
parser_allow_function(); parser_allow_function();
if( block_type == SUBST )
{
al_destroy( forbidden_function );
free( forbidden_function );
}
/*
Restore previous eval state
*/
forbidden_function = prev_forbidden;
current_tokenizer=previous_tokenizer; current_tokenizer=previous_tokenizer;
block_io = prev_io;
eval_level--;
code=error_code; code=error_code;
error_code=0; error_code=0;
block_io = prev_io;
eval_level--;
job_reap( 0 ); job_reap( 0 );
return code; return code;
} }

270
reader.c
View File

@ -80,6 +80,7 @@ commence.
#include "output.h" #include "output.h"
#include "signal.h" #include "signal.h"
#include "translate.h" #include "translate.h"
#include "parse_util.h"
/** /**
Maximum length of prefix string when printing completion Maximum length of prefix string when printing completion
@ -1691,274 +1692,17 @@ void reader_sanity_check()
} }
} }
void reader_current_subshell_extent( wchar_t **a, wchar_t **b )
{
wchar_t *begin, *end;
wchar_t *pos;
if( a )
*a=0;
if( b )
*b = 0;
if( !data )
return;
pos = data->buff;
while( 1 )
{
int bc, ec;
if( expand_locate_subshell( pos,
&begin,
&end,
1 ) <= 0)
{
begin=data->buff;
end = data->buff + wcslen(data->buff);
break;
}
if( !end )
{
end = data->buff + wcslen(data->buff);
}
bc = begin-data->buff;
ec = end-data->buff;
if(( bc < data->buff_pos ) && (ec >= data->buff_pos) )
{
begin++;
break;
}
pos = end+1;
}
if( a )
*a = begin;
if( b )
*b = end;
}
/**
Get the beginning and dend of the job or process definition under the cursor
*/
static void reader_current_job_or_process_extent( wchar_t **a,
wchar_t **b,
int process )
{
wchar_t *begin, *end;
int pos;
wchar_t *buffcpy;
int finished=0;
tokenizer tok;
if( a )
*a=0;
if( b )
*b = 0;
reader_current_subshell_extent( &begin, &end );
if( !end || !begin )
return;
pos = data->buff_pos - (begin - data->buff);
// fwprintf( stderr, L"Subshell extent: %d %d %d\n", begin-data->buff, end-data->buff, pos );
if( a )
{
*a = begin;
}
if( b )
{
*b = end;
}
buffcpy = wcsndup( begin, end-begin );
if( !buffcpy )
{
die_mem();
}
// fwprintf( stderr, L"Strlen: %d\n", wcslen(buffcpy ) );
for( tok_init( &tok, buffcpy, TOK_ACCEPT_UNFINISHED );
tok_has_next( &tok ) && !finished;
tok_next( &tok ) )
{
int tok_begin = tok_get_pos( &tok );
// fwprintf( stderr, L".");
switch( tok_last_type( &tok ) )
{
case TOK_PIPE:
if( !process )
break;
case TOK_END:
case TOK_BACKGROUND:
{
// fwprintf( stderr, L"New cmd at %d\n", tok_begin );
if( tok_begin >= pos )
{
finished=1;
if( b )
*b = data->buff + tok_begin;
}
else
{
if( a )
*a = data->buff + tok_begin+1;
}
break;
}
}
}
// fwprintf( stderr, L"Res: %d %d\n", *a-data->buff, *b-data->buff );
free( buffcpy);
tok_destroy( &tok );
}
void reader_current_process_extent( wchar_t **a, wchar_t **b )
{
reader_current_job_or_process_extent( a, b, 1 );
}
void reader_current_job_extent( wchar_t **a, wchar_t **b )
{
reader_current_job_or_process_extent( a, b, 0 );
}
void reader_current_token_extent( wchar_t **tok_begin,
wchar_t **tok_end,
wchar_t **prev_begin,
wchar_t **prev_end )
{
wchar_t *begin, *end;
int pos;
wchar_t *buffcpy;
tokenizer tok;
wchar_t *a, *b, *pa, *pb;
a = b = pa = pb = 0;
reader_current_subshell_extent( &begin, &end );
if( !end || !begin )
return;
pos = data->buff_pos - (begin - data->buff);
a = data->buff + pos;
b = a;
pa = data->buff + pos;
pb = pa;
assert( begin >= data->buff );
assert( begin <= (data->buff+wcslen(data->buff) ) );
assert( end >= begin );
assert( end <= (data->buff+wcslen(data->buff) ) );
buffcpy = wcsndup( begin, end-begin );
if( !buffcpy )
{
die_mem();
}
for( tok_init( &tok, buffcpy, TOK_ACCEPT_UNFINISHED );
tok_has_next( &tok );
tok_next( &tok ) )
{
int tok_begin = tok_get_pos( &tok );
int tok_end=tok_begin;
/*
Calculate end of token
*/
if( tok_last_type( &tok ) == TOK_STRING )
tok_end +=wcslen(tok_last(&tok));
/*
Cursor was before beginning of this token, means that the
cursor is between two tokens, so we set it to a zero element
string and break
*/
if( tok_begin > pos )
{
a = b = data->buff + pos;
break;
}
/*
If cursor is inside the token, this is the token we are
looking for. If so, set a and b and break
*/
if( tok_end >= pos )
{
a = begin + tok_get_pos( &tok );
b = a + wcslen(tok_last(&tok));
// fwprintf( stderr, L"Whee %ls\n", *a );
break;
}
/*
Remember previous string token
*/
if( tok_last_type( &tok ) == TOK_STRING )
{
pa = begin + tok_get_pos( &tok );
pb = pa + wcslen(tok_last(&tok));
}
}
free( buffcpy);
tok_destroy( &tok );
if( tok_begin )
*tok_begin = a;
if( tok_end )
*tok_end = b;
if( prev_begin )
*prev_begin = pa;
if( prev_end )
*prev_end = pb;
assert( pa >= data->buff );
assert( pa <= (data->buff+wcslen(data->buff) ) );
assert( pb >= pa );
assert( pb <= (data->buff+wcslen(data->buff) ) );
}
void reader_replace_current_token( wchar_t *new_token ) void reader_replace_current_token( wchar_t *new_token )
{ {
wchar_t *begin, *end; const wchar_t *begin, *end;
string_buffer_t sb; string_buffer_t sb;
int new_pos; int new_pos;
/* /*
Find current token Find current token
*/ */
reader_current_token_extent( &begin, &end, 0, 0 ); parse_util_token_extent( data->buff, data->buff_pos, &begin, &end, 0, 0 );
if( !begin || !end ) if( !begin || !end )
return; return;
@ -2018,9 +1762,9 @@ static int contains( const wchar_t *needle,
*/ */
static void reset_token_history() static void reset_token_history()
{ {
wchar_t *begin, *end; const wchar_t *begin, *end;
reader_current_token_extent( &begin, &end, 0, 0 ); parse_util_token_extent( data->buff, data->buff_pos, &begin, &end, 0, 0 );
if( begin ) if( begin )
{ {
wcslcpy(data->search_buff, begin, end-begin+1); wcslcpy(data->search_buff, begin, end-begin+1);
@ -2718,10 +2462,10 @@ wchar_t *reader_readline()
if( comp_empty ) if( comp_empty )
{ {
wchar_t *begin, *end; const wchar_t *begin, *end;
wchar_t *buffcpy; wchar_t *buffcpy;
reader_current_subshell_extent( &begin, &end ); parse_util_cmdsubst_extent( data->buff, data->buff_pos, &begin, &end );
int len = data->buff_pos - (data->buff - begin); int len = data->buff_pos - (data->buff - begin);
buffcpy = wcsndup( begin, len ); buffcpy = wcsndup( begin, len );

View File

@ -152,27 +152,6 @@ void reader_set_prompt( wchar_t *prompt );
*/ */
int exit_status(); int exit_status();
/**
Find the beginning and the end of the current subshell
*/
void reader_current_subshell_extent( wchar_t **a, wchar_t **b );
/**
Find the beginning and the end of the job under the cursor
*/
void reader_current_job_extent( wchar_t **a, wchar_t **b );
/**
Find the beginning and the end of the process under the cursor
*/
void reader_current_process_extent( wchar_t **a, wchar_t **b );
/**
Find the beginning and the end of the token under the curor and the token before the cursor
*/
void reader_current_token_extent( wchar_t **a, wchar_t **b, wchar_t **pa, wchar_t **pb );
/** /**
Replace the current token with the specified string Replace the current token with the specified string
*/ */