mirror of
https://github.com/fish-shell/fish-shell
synced 2024-12-26 04:43:10 +00:00
Add function stack trace to error output
darcs-hash:20060126144810-ac50b-3426191f596674504ce49dd61fcfa3c2c0c0f2bb.gz
This commit is contained in:
parent
2e35e1ea60
commit
312c7ab7b2
7 changed files with 263 additions and 33 deletions
17
exec.c
17
exec.c
|
@ -806,7 +806,7 @@ void exec( job_t *j )
|
|||
wchar_t **arg;
|
||||
int i;
|
||||
string_buffer_t sb;
|
||||
|
||||
|
||||
const wchar_t * def = function_get_definition( p->argv[0] );
|
||||
// fwprintf( stderr, L"run function %ls\n", argv[0] );
|
||||
if( def == 0 )
|
||||
|
@ -814,14 +814,24 @@ void exec( job_t *j )
|
|||
debug( 0, _( L"Unknown function '%ls'" ), p->argv[0] );
|
||||
break;
|
||||
}
|
||||
|
||||
int lineno = parser_get_lineno();
|
||||
|
||||
parser_push_block( FUNCTION_CALL );
|
||||
|
||||
al_init( ¤t_block->param2.function_vars );
|
||||
current_block->param1.function_name = wcsdup( p->argv[0] );
|
||||
current_block->param3.function_lineno = lineno;
|
||||
|
||||
if( builtin_count_args(p->argv)>1 )
|
||||
{
|
||||
sb_init( &sb );
|
||||
|
||||
for( i=1, arg=p->argv+1; *arg; i++, arg++ )
|
||||
{
|
||||
al_push( ¤t_block->param2.function_vars,
|
||||
escape(*arg, 1) );
|
||||
|
||||
if( i != 1 )
|
||||
sb_append( &sb, ARRAY_SEP_STR );
|
||||
sb_append( &sb, *arg );
|
||||
|
@ -830,6 +840,11 @@ void exec( job_t *j )
|
|||
env_set( L"argv", (wchar_t *)sb.buff, ENV_LOCAL );
|
||||
sb_destroy( &sb );
|
||||
}
|
||||
else
|
||||
{
|
||||
env_set( L"argv", 0, ENV_LOCAL );
|
||||
}
|
||||
|
||||
parser_forbid_function( p->argv[0] );
|
||||
|
||||
if( p->next )
|
||||
|
|
47
function.c
47
function.c
|
@ -17,6 +17,7 @@
|
|||
#include "common.h"
|
||||
#include "intern.h"
|
||||
#include "event.h"
|
||||
#include "reader.h"
|
||||
|
||||
|
||||
/**
|
||||
|
@ -28,6 +29,8 @@ typedef struct
|
|||
wchar_t *cmd;
|
||||
/** Function description */
|
||||
wchar_t *desc;
|
||||
const wchar_t *definition_file;
|
||||
int definition_offset;
|
||||
int is_binding;
|
||||
}
|
||||
function_data_t;
|
||||
|
@ -62,6 +65,19 @@ void function_destroy()
|
|||
hash_destroy( &function );
|
||||
}
|
||||
|
||||
static int count_lineno( const wchar_t *str, int len )
|
||||
{
|
||||
int res = 0;
|
||||
int i;
|
||||
for( i=0; i<len; i++ )
|
||||
{
|
||||
if( str[i] == L'\n' )
|
||||
res++;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
void function_add( const wchar_t *name,
|
||||
const wchar_t *val,
|
||||
const wchar_t *desc,
|
||||
|
@ -75,18 +91,22 @@ void function_add( const wchar_t *name,
|
|||
if( function_exists( name ) )
|
||||
function_remove( name );
|
||||
|
||||
|
||||
|
||||
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 ) )
|
||||
{
|
||||
*cmd_end--=0;
|
||||
*cmd_end-- = 0;
|
||||
}
|
||||
|
||||
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++ )
|
||||
{
|
||||
event_add_handler( (event_t *)al_get( events, i ) );
|
||||
|
@ -172,3 +192,26 @@ void function_get_names( array_list_t *list, int get_hidden )
|
|||
|
||||
}
|
||||
|
||||
const wchar_t *function_get_definition_file( const wchar_t *argv )
|
||||
{
|
||||
function_data_t *data =
|
||||
(function_data_t *)hash_get( &function, argv );
|
||||
if( data == 0 )
|
||||
return 0;
|
||||
|
||||
return data->definition_file;
|
||||
}
|
||||
|
||||
|
||||
int function_get_definition_offset( const wchar_t *argv )
|
||||
{
|
||||
function_data_t *data =
|
||||
(function_data_t *)hash_get( &function, argv );
|
||||
if( data == 0 )
|
||||
return -1;
|
||||
|
||||
return data->definition_offset;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -69,4 +69,8 @@ int function_exists( const wchar_t *name );
|
|||
void function_get_names( array_list_t *list,
|
||||
int get_hidden );
|
||||
|
||||
const wchar_t *function_get_definition_file( const wchar_t *name );
|
||||
|
||||
int function_get_definition_offset( const wchar_t *name );
|
||||
|
||||
#endif
|
||||
|
|
14
main.c
14
main.c
|
@ -184,7 +184,7 @@ int main( int argc, char **argv )
|
|||
|
||||
case 'v':
|
||||
fwprintf( stderr,
|
||||
L"%s, version %s\n",
|
||||
_(L"%s, version %s\n"),
|
||||
PACKAGE_NAME,
|
||||
PACKAGE_VERSION );
|
||||
exit( 0 );
|
||||
|
@ -222,8 +222,6 @@ int main( int argc, char **argv )
|
|||
complete_init();
|
||||
reader_init();
|
||||
|
||||
reader_push_current_filename( L"(internal)" );
|
||||
|
||||
if( read_init() )
|
||||
{
|
||||
if( cmd != 0 )
|
||||
|
@ -237,9 +235,7 @@ int main( int argc, char **argv )
|
|||
{
|
||||
if( my_optind == argc )
|
||||
{
|
||||
reader_push_current_filename( L"(stdin)" );
|
||||
res = reader_read( 0 );
|
||||
reader_pop_current_filename();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -278,8 +274,8 @@ int main( int argc, char **argv )
|
|||
if( res )
|
||||
{
|
||||
debug( 1,
|
||||
L"Error while reading file %ls\n",
|
||||
reader_current_filename() );
|
||||
_(L"Error while reading file %ls\n"),
|
||||
reader_current_filename()?reader_current_filename(): _(L"Standard input") );
|
||||
}
|
||||
free(reader_pop_current_filename());
|
||||
}
|
||||
|
@ -287,9 +283,7 @@ int main( int argc, char **argv )
|
|||
}
|
||||
|
||||
proc_fire_event( L"PROCESS_EXIT", EVENT_EXIT, getpid(), res );
|
||||
|
||||
reader_pop_current_filename();
|
||||
|
||||
|
||||
proc_destroy();
|
||||
env_destroy();
|
||||
builtin_destroy();
|
||||
|
|
203
parser.c
203
parser.c
|
@ -398,6 +398,15 @@ void parser_pop_block()
|
|||
break;
|
||||
}
|
||||
|
||||
case FUNCTION_CALL:
|
||||
{
|
||||
free( current_block->param1.function_name );
|
||||
al_foreach( ¤t_block->param2.function_vars,
|
||||
(void (*)(const void *))&free );
|
||||
al_destroy( ¤t_block->param2.function_vars );
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for( eb=current_block->first_event_block; eb; eb=eb_next )
|
||||
|
@ -899,6 +908,14 @@ void parser_destroy()
|
|||
}
|
||||
|
||||
al_destroy( &forbidden_function );
|
||||
|
||||
if( lineinfo )
|
||||
{
|
||||
sb_destroy( lineinfo );
|
||||
free(lineinfo );
|
||||
lineinfo = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -995,17 +1012,139 @@ int eval_args( const wchar_t *line, array_list_t *args )
|
|||
return 1;
|
||||
}
|
||||
|
||||
static void parser_stack_trace( block_t *b, string_buffer_t *buff)
|
||||
{
|
||||
if( !b )
|
||||
return;
|
||||
|
||||
if( b->type == FUNCTION_CALL )
|
||||
{
|
||||
int i;
|
||||
|
||||
sb_printf( buff, _(L"in function '%ls',\n"), b->param1.function_name );
|
||||
|
||||
const wchar_t *file = function_get_definition_file( b->param1.function_name );
|
||||
if( file )
|
||||
sb_printf( buff,
|
||||
_(L"\tcalled on line %d of file '%ls'\n"),
|
||||
b->param3.function_lineno,
|
||||
file );
|
||||
else
|
||||
sb_printf( buff,
|
||||
_(L"\tcalled on standard input\n") );
|
||||
|
||||
if( al_get_count( &b->param2.function_vars ) )
|
||||
{
|
||||
string_buffer_t tmp;
|
||||
sb_init( &tmp );
|
||||
|
||||
for( i=0; i<al_get_count( &b->param2.function_vars ); i++ )
|
||||
{
|
||||
sb_append2( &tmp, i?L" ":L"", (wchar_t *)al_get( &b->param2.function_vars, i ), (void *)0 );
|
||||
}
|
||||
sb_printf( buff, _(L"\twith parameters '%ls',\n"), (wchar_t *)tmp.buff );
|
||||
|
||||
sb_destroy( &tmp );
|
||||
}
|
||||
sb_printf( buff,
|
||||
L"\n" );
|
||||
}
|
||||
parser_stack_trace( b->outer, buff );
|
||||
}
|
||||
|
||||
static const wchar_t *is_function()
|
||||
{
|
||||
block_t *b = current_block;
|
||||
while( 1 )
|
||||
{
|
||||
if( !b )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if( b->type == FUNCTION_CALL )
|
||||
{
|
||||
return b->param1.function_name;
|
||||
}
|
||||
b=b->outer;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int parser_get_lineno()
|
||||
{
|
||||
int i;
|
||||
const wchar_t *whole_str = tok_string( current_tokenizer );
|
||||
const wchar_t *function_name;
|
||||
|
||||
int lineno = 1;
|
||||
|
||||
for( i=0; i<current_tokenizer_pos; i++ )
|
||||
{
|
||||
if( whole_str[i] == L'\n' )
|
||||
{
|
||||
lineno++;
|
||||
}
|
||||
}
|
||||
|
||||
if( (function_name = is_function()) )
|
||||
{
|
||||
lineno += function_get_definition_offset( function_name );
|
||||
}
|
||||
|
||||
return lineno;
|
||||
}
|
||||
|
||||
static const wchar_t *parser_current_filename()
|
||||
{
|
||||
block_t *b = current_block;
|
||||
|
||||
while( 1 )
|
||||
{
|
||||
if( !b )
|
||||
{
|
||||
return reader_current_filename();
|
||||
}
|
||||
if( b->type == FUNCTION_CALL )
|
||||
{
|
||||
return function_get_definition_file(b->param1.function_name );
|
||||
}
|
||||
b=b->outer;
|
||||
}
|
||||
}
|
||||
|
||||
static int printed_width( const wchar_t *str, int len )
|
||||
{
|
||||
int res=0;
|
||||
int i;
|
||||
for( i=0; i<len; i++ )
|
||||
{
|
||||
if( str[i] == L'\t' )
|
||||
{
|
||||
res=(res+8)&~7;
|
||||
}
|
||||
else
|
||||
{
|
||||
res += wcwidth( str[i] );
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
wchar_t *parser_current_line()
|
||||
{
|
||||
int lineno=1;
|
||||
|
||||
wchar_t *file = reader_current_filename();
|
||||
const wchar_t *file = parser_current_filename();
|
||||
wchar_t *whole_str = tok_string( current_tokenizer );
|
||||
wchar_t *line = whole_str;
|
||||
wchar_t *line_end;
|
||||
int i;
|
||||
int offset;
|
||||
int current_line_pos=current_tokenizer_pos;
|
||||
int current_line_width;
|
||||
const wchar_t *function_name=0;
|
||||
int current_line_start=0;
|
||||
|
||||
|
||||
if( !line )
|
||||
return L"";
|
||||
|
@ -1015,7 +1154,7 @@ wchar_t *parser_current_line()
|
|||
lineinfo = malloc( sizeof(string_buffer_t) );
|
||||
sb_init( lineinfo );
|
||||
}
|
||||
sb_clear( lineinfo );
|
||||
sb_clear( lineinfo );
|
||||
|
||||
/*
|
||||
Calculate line number, line offset, etc.
|
||||
|
@ -1025,11 +1164,18 @@ wchar_t *parser_current_line()
|
|||
if( whole_str[i] == L'\n' )
|
||||
{
|
||||
lineno++;
|
||||
current_line_pos = current_tokenizer_pos-i-1;
|
||||
current_line_start=i+1;
|
||||
line = &whole_str[i+1];
|
||||
}
|
||||
}
|
||||
|
||||
current_line_width=printed_width(whole_str+current_line_start, current_tokenizer_pos-current_line_start );
|
||||
|
||||
if( (function_name = is_function()) )
|
||||
{
|
||||
lineno += function_get_definition_offset( function_name );
|
||||
}
|
||||
|
||||
/*
|
||||
Copy current line from whole string
|
||||
*/
|
||||
|
@ -1039,34 +1185,55 @@ wchar_t *parser_current_line()
|
|||
|
||||
line = wcsndup( line, line_end-line );
|
||||
|
||||
debug( 4, L"Current pos %d, line pos %d, file_length %d, is_interactive %d\n", current_tokenizer_pos, current_line_pos, wcslen(whole_str), is_interactive);
|
||||
|
||||
/**
|
||||
If we are not going to print a stack trace, at least print the line number and filename
|
||||
*/
|
||||
if( !is_interactive )
|
||||
{
|
||||
sb_printf( lineinfo,
|
||||
_(L"%ls (line %d): "),
|
||||
file,
|
||||
lineno );
|
||||
offset = my_wcswidth( (wchar_t *)lineinfo->buff );
|
||||
int prev_width = my_wcswidth( (wchar_t *)lineinfo->buff );
|
||||
if( file )
|
||||
sb_printf( lineinfo,
|
||||
_(L"%ls (line %d): "),
|
||||
file,
|
||||
lineno );
|
||||
else
|
||||
sb_printf( lineinfo,
|
||||
L"%ls: ",
|
||||
_(L"Standard input"),
|
||||
lineno );
|
||||
offset = my_wcswidth( (wchar_t *)lineinfo->buff ) - prev_width;
|
||||
}
|
||||
else
|
||||
{
|
||||
offset=0;
|
||||
}
|
||||
|
||||
// debug( 1, L"Current pos %d, line pos %d, file_length %d, is_interactive %d, offset %d\n", current_tokenizer_pos, current_line_pos, wcslen(whole_str), is_interactive, offset);
|
||||
/*
|
||||
Skip printing character position if we are in interactive mode
|
||||
and the error was on the first character of the line
|
||||
and the error was on the first character of the line.
|
||||
*/
|
||||
if( !is_interactive || (current_line_pos!=0) )
|
||||
if( !is_interactive || is_function() || (current_line_width!=0) )
|
||||
{
|
||||
sb_printf( lineinfo,
|
||||
L"%ls\n%*c^\n",
|
||||
line,
|
||||
offset+current_line_pos,
|
||||
L' ' );
|
||||
// Workaround since it seems impossible to print 0 copies of a character using printf
|
||||
if( offset+current_line_width )
|
||||
{
|
||||
sb_printf( lineinfo,
|
||||
L"%ls\n%*lc^\n",
|
||||
line,
|
||||
offset+current_line_width,
|
||||
L' ' );
|
||||
}
|
||||
else
|
||||
{
|
||||
sb_printf( lineinfo,
|
||||
L"%ls\n^\n",
|
||||
line );
|
||||
}
|
||||
}
|
||||
|
||||
free( line );
|
||||
parser_stack_trace( current_block, lineinfo );
|
||||
|
||||
return (wchar_t *)lineinfo->buff;
|
||||
}
|
||||
|
|
9
parser.h
9
parser.h
|
@ -63,7 +63,7 @@ typedef struct block
|
|||
wchar_t *for_variable; /**< Name of the variable to loop over */
|
||||
int if_state; /**< The state of the if block */
|
||||
wchar_t *switch_value; /**< The value to test in a switch block */
|
||||
wchar_t *function_name; /**< The name of the function to define */
|
||||
wchar_t *function_name; /**< The name of the function to define or the function called*/
|
||||
} param1;
|
||||
|
||||
/**
|
||||
|
@ -74,6 +74,7 @@ typedef struct block
|
|||
array_list_t for_vars; /**< List of values for a for block */
|
||||
int switch_taken; /**< Whether a switch match has already been found */
|
||||
wchar_t *function_description; /**< The description of the function to define */
|
||||
array_list_t function_vars; /**< List of arguments for a function call */
|
||||
} param2;
|
||||
|
||||
/**
|
||||
|
@ -82,6 +83,7 @@ typedef struct block
|
|||
union
|
||||
{
|
||||
int function_is_binding; /**< Whether a function is a keybinding */
|
||||
int function_lineno; /**< Function invocation line number */
|
||||
} param3;
|
||||
|
||||
/**
|
||||
|
@ -251,6 +253,11 @@ int parser_is_reserved( wchar_t *word );
|
|||
*/
|
||||
wchar_t *parser_current_line();
|
||||
|
||||
/**
|
||||
Returns the current line number
|
||||
*/
|
||||
int parser_get_lineno();
|
||||
|
||||
/**
|
||||
Returns the current position in the latest string of the tokenizer.
|
||||
*/
|
||||
|
|
2
reader.c
2
reader.c
|
@ -385,7 +385,7 @@ void reader_handle_int( int sig )
|
|||
|
||||
wchar_t *reader_current_filename()
|
||||
{
|
||||
return (wchar_t *)al_peek( ¤t_filename );
|
||||
return al_get_count( ¤t_filename )?(wchar_t *)al_peek( ¤t_filename ):0;
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue