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 \
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 \
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
COMMON_OBJS_WITH_HEADER := builtin_help.o

View file

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

View file

@ -159,6 +159,8 @@ int builtin_ulimit(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
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.h"
#include "translate.h"
#include "parse_util.h"
/**
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.
@ -57,14 +78,14 @@ enum
\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
*/
static void replace_part( wchar_t *begin,
wchar_t *end,
static void replace_part( const wchar_t *begin,
const wchar_t *end,
wchar_t *insert,
int append_mode )
{
wchar_t *buff = reader_get_buffer();
const wchar_t *buff = get_buffer();
string_buffer_t out;
int out_pos=reader_get_cursor_pos();
int out_pos=get_cursor_pos();
sb_init( &out );
@ -88,7 +109,7 @@ static void replace_part( wchar_t *begin,
}
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( &out, insert );
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 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,
wchar_t *end,
static void write_part( const wchar_t *begin,
const wchar_t *end,
int cut_at_cursor,
int tokenize )
{
@ -119,7 +140,7 @@ static void write_part( wchar_t *begin,
wchar_t *buff;
int pos;
pos = reader_get_cursor_pos()-(begin-reader_get_buffer());
pos = get_cursor_pos()-(begin-get_buffer());
if( tokenize )
{
@ -184,7 +205,7 @@ int builtin_commandline( wchar_t **argv )
int tokenize = 0;
if( !reader_get_buffer() )
if( !get_buffer() )
{
sb_append2( sb_err,
argv[0],
@ -429,32 +450,42 @@ int builtin_commandline( wchar_t **argv )
buffer_part = STRING_MODE;
}
wchar_t *begin, *end;
const wchar_t *begin, *end;
switch( buffer_part )
{
case STRING_MODE:
{
begin = reader_get_buffer();
begin = get_buffer();
end = begin+wcslen(begin);
break;
}
case PROCESS_MODE:
{
reader_current_process_extent( &begin, &end );
parse_util_process_extent( get_buffer(),
get_cursor_pos(),
&begin,
&end );
break;
}
case JOB_MODE:
{
reader_current_job_extent( &begin, &end );
parse_util_job_extent( get_buffer(),
get_cursor_pos(),
&begin,
&end );
break;
}
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;
}

View file

@ -19,8 +19,11 @@ Functions used for implementing the complete builtin.
#include "complete.h"
#include "wgetopt.h"
#include "parser.h"
#include "reader.h"
#include "translate.h"
const static 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
@ -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 res=0;
@ -247,9 +255,18 @@ int builtin_complete( wchar_t **argv )
array_list_t gnu_opt, old_opt;
wchar_t *comp=L"", *desc=L"", *condition=L"", *load=0;
wchar_t *do_complete = 0;
array_list_t cmd;
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( &path );
sb_init( &short_opt );
@ -320,6 +337,10 @@ int builtin_complete( wchar_t **argv )
L"load", required_argument, 0, 'y'
}
,
{
L"do-complete", required_argument, 0, 'C'
}
,
{
0, 0, 0, 0
}
@ -330,7 +351,7 @@ int builtin_complete( wchar_t **argv )
int opt = wgetopt_long( argc,
argv,
L"a:c:p:s:l:o:d:frxeun:y:",
L"a:c:p:s:l:o:d:frxeun:y:C:",
long_options,
&opt_index );
if( opt == -1 )
@ -410,8 +431,11 @@ int builtin_complete( wchar_t **argv )
case 'y':
load = woptarg;
break;
case 'C':
do_complete = woptarg?woptarg:reader_get_buffer();
break;
case '?':
// builtin_print_help( argv[0], sb_err );
@ -424,6 +448,40 @@ int builtin_complete( wchar_t **argv )
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 )
{

View file

@ -36,6 +36,7 @@
#include "history.h"
#include "intern.h"
#include "translate.h"
#include "parse_util.h"
#include "wutil.h"
@ -2007,7 +2008,8 @@ static int try_complete_user( const wchar_t *cmd,
void complete( const wchar_t *cmd,
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;
wchar_t *current_token=0, *current_command=0, *prev_token=0;
@ -2016,7 +2018,9 @@ void complete( const wchar_t *cmd,
int old_error_max = error_max;
int done=0;
int cursor_pos = wcslen(cmd );
error_max=0;
/**
@ -2042,7 +2046,7 @@ void complete( const wchar_t *cmd,
if( !done )
{
reader_current_subshell_extent( &begin, &end );
parse_util_cmdsubst_extent( cmd, cursor_pos, &begin, &end );
if( !begin )
done=1;
@ -2051,7 +2055,7 @@ void complete( const wchar_t *cmd,
if( !done )
{
pos = reader_get_cursor_pos()-(begin-reader_get_buffer());
pos = cursor_pos-(begin-cmd);
buff = wcsndup( begin, end-begin );
@ -2109,9 +2113,9 @@ void complete( const wchar_t *cmd,
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"");

1
exec.c
View file

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

View file

@ -39,6 +39,7 @@ parameter expansion.
#include "tokenizer.h"
#include "complete.h"
#include "translate.h"
#include "parse_util.h"
/**
Description for child process
@ -1154,105 +1155,23 @@ static int expand_brackets( wchar_t *in, int flags, array_list_t *out )
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
*/
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;
wchar_t prev=0;
wchar_t *subcmd;
array_list_t sub_res, tail_expand;
int i, j;
wchar_t *item_begin;
const wchar_t *item_begin;
switch( expand_locate_subshell(in,
&paran_begin,
&paran_end,
0 ) )
switch( parse_util_locate_cmdsubst(in,
&paran_begin,
&paran_end,
0 ) )
{
case -1:
error( SYNTAX_ERROR,

View file

@ -182,20 +182,6 @@ wchar_t *expand_escape_variable( const 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.

View file

@ -424,54 +424,54 @@ static void printed_length( wchar_t *str,
else
{
switch( *str )
{
case L'\n':
case L'\b':
case L'\r':
case L'\e':
case L'\t':
case L'\\':
case L'&':
case L'$':
case L' ':
case L'#':
case L'^':
case L'<':
case L'>':
case L'(':
case L')':
case L'[':
case L']':
case L'{':
case L'}':
case L'?':
case L'*':
case L'|':
case L';':
case L':':
case L'\'':
case L'"':
case L'%':
case L'~':
switch( *str )
{
case L'\n':
case L'\b':
case L'\r':
case L'\e':
case L'\t':
case L'\\':
case L'&':
case L'$':
case L' ':
case L'#':
case L'^':
case L'<':
case L'>':
case L'(':
case L')':
case L'[':
case L']':
case L'{':
case L'}':
case L'?':
case L'*':
case L'|':
case L';':
case L':':
case L'\'':
case L'"':
case L'%':
case L'~':
if( has_description )
desc_len++;
else
comp_len+=2;
break;
if( has_description )
desc_len++;
else
comp_len+=2;
break;
case COMPLETE_SEP:
has_description = 1;
break;
case COMPLETE_SEP:
has_description = 1;
break;
default:
if( has_description )
desc_len+= wcwidth(*str);
else
comp_len+= wcwidth(*str);
break;
}
default:
if( has_description )
desc_len+= wcwidth(*str);
else
comp_len+= wcwidth(*str);
break;
}
}
str++;

View file

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

View file

@ -18,6 +18,7 @@
#include "tokenizer.h"
#include "proc.h"
#include "parser.h"
#include "parse_util.h"
#include "builtin.h"
#include "function.h"
#include "env.h"
@ -365,11 +366,11 @@ void highlight_shell( wchar_t * buff,
while( 1 )
{
wchar_t *begin, *end;
if( expand_locate_subshell( subpos,
&begin,
&end,
1) <= 0)
if( parse_util_locate_cmdsubst( subpos,
(const wchar_t **)&begin,
(const wchar_t **)&end,
1) <= 0)
{
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 e -l erase -d (_ "Remove completion")
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
#
# 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 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 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
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
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"

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;
/** 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.
@ -726,7 +726,7 @@ void parser_forbid_function( wchar_t *function )
if( 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()
@ -735,7 +735,7 @@ void parser_allow_function()
if( 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, ... )
@ -848,7 +848,8 @@ void parser_init()
{
al_init( &profile_data);
}
al_init( &forbidden_function );
forbidden_function = al_new();
}
/**
@ -924,14 +925,15 @@ void parser_destroy()
al_destroy( &profile_data );
}
al_destroy( &forbidden_function );
if( lineinfo )
{
sb_destroy( lineinfo );
free(lineinfo );
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) )
{
// 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 )
{
sb_printf( lineinfo,
@ -1875,7 +1877,7 @@ static int parse_job( process_t *p,
int nxt_forbidden;
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 );
/*
@ -1887,7 +1889,7 @@ static int parse_job( process_t *p,
/*
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,
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;
block_t *start_current_block = current_block;
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;
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 );
forbid_count = al_get_count( &forbidden_function );
tok_init( current_tokenizer, cmd, 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 );
free( current_tokenizer );
while( forbid_count < al_get_count( &forbidden_function ))
while( al_get_count( forbidden_function ) > forbid_count )
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;
block_io = prev_io;
eval_level--;
code=error_code;
error_code=0;
block_io = prev_io;
eval_level--;
job_reap( 0 );
return code;
}

2
proc.c
View file

@ -388,7 +388,7 @@ static void handle_child_status( pid_t pid, int status )
"Process %d is %ls from job %ls\n",
(int) pid, p->actual_cmd, j->command );
write( 2, mess, strlen(mess ));
*/
*/
mark_process_status ( j, p, status);
if( p->completed && prev != 0 )

270
reader.c
View file

@ -80,6 +80,7 @@ commence.
#include "output.h"
#include "signal.h"
#include "translate.h"
#include "parse_util.h"
/**
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 )
{
wchar_t *begin, *end;
const wchar_t *begin, *end;
string_buffer_t sb;
int new_pos;
/*
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 )
return;
@ -2018,9 +1762,9 @@ static int contains( const wchar_t *needle,
*/
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 )
{
wcslcpy(data->search_buff, begin, end-begin+1);
@ -2718,10 +2462,10 @@ wchar_t *reader_readline()
if( comp_empty )
{
wchar_t *begin, *end;
const wchar_t *begin, *end;
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);
buffcpy = wcsndup( begin, len );

View file

@ -152,27 +152,6 @@ void reader_set_prompt( wchar_t *prompt );
*/
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
*/