diff --git a/builtin_complete.c b/builtin_complete.c index 0b7eede63..1717e5dec 100644 --- a/builtin_complete.c +++ b/builtin_complete.c @@ -489,14 +489,14 @@ static int builtin_complete( wchar_t **argv ) { if( condition && wcslen( condition ) ) { - if( parser_test( condition, 0, 0 ) ) + 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, sb_err, argv[0] ); + parser_test( condition, 0, sb_err, argv[0] ); res = 1; } diff --git a/parser.c b/parser.c index c5a81fac6..7f4ba0cb5 100644 --- a/parser.c +++ b/parser.c @@ -2839,7 +2839,7 @@ static int parser_test_argument( const wchar_t *arg, string_buffer_t *out, const // debug( 1, L"%ls -> %ls %ls", arg_cpy, subst, tmp.buff ); - err |= parser_test( subst, out, prefix ); + err |= parser_test( subst, 0, out, prefix ); free( subst ); free( arg_cpy ); @@ -2981,6 +2981,7 @@ int parser_test_args(const wchar_t * buff, } int parser_test( const wchar_t * buff, + int *block_level, string_buffer_t *out, const wchar_t *prefix ) { @@ -2992,6 +2993,8 @@ int parser_test( const wchar_t * buff, int had_cmd=0; int count = 0; int err=0; + int unfinished = 0; + tokenizer *previous_tokenizer=current_tokenizer; int previous_pos=current_tokenizer_pos; static int block_pos[BLOCK_MAX_COUNT]; @@ -3033,6 +3036,17 @@ int parser_test( const wchar_t * buff, wchar_t *cmd=0; CHECK( buff, 1 ); + + if( block_level ) + { + int i; + int len = wcslen(buff); + for( i=0; i= 0 ) + last_level = block_level[i]; + block_level[i] = last_level; + } + + } + + if( count!= 0 ) + unfinished = 1; + if( err ) res |= PARSER_TEST_ERROR; - if( count!= 0 ) + + if( unfinished ) res |= PARSER_TEST_INCOMPLETE; return res; diff --git a/parser.h b/parser.h index be5a3d265..510bb6a5d 100644 --- a/parser.h +++ b/parser.h @@ -305,11 +305,17 @@ const wchar_t *parser_get_block_desc( int block ); /** Test if the specified string can be parsed, or if more bytes need - to be read first. The result has the first bit set if the string - contains errors, and the second bit is set if the string contains - an unclosed block. + to be read first. The result will have the PARSER_TEST_ERROR bit + set if there is a syntax error in the code, and the + PARSER_TEST_INCOMPLETE bit set if the code contains unclosed + blocks. + + \param buff the text buffer to test + \param block_level if non-null, the block nesting level will be filled out into this array + \param out if non-null, any errors in the command will be filled out into this buffer + \param prefix the prefix string to prepend to each error message written to the \c out buffer */ -int parser_test( const wchar_t * buff, string_buffer_t *out, const wchar_t *prefix ); +int parser_test( const wchar_t * buff, int *block_level, string_buffer_t *out, const wchar_t *prefix ); /** Test if the specified string can be parsed as an argument list, diff --git a/reader.c b/reader.c index 765470073..27efc055c 100644 --- a/reader.c +++ b/reader.c @@ -204,7 +204,7 @@ typedef struct reader_data /** New color buffer, used for syntax highlighting. */ - int *new_color; + int *indent; /** Should the prompt command be reexecuted on the next repaint @@ -419,13 +419,13 @@ static int check_size() data->color = realloc( data->color, sizeof(int)*data->buff_sz); - data->new_color = realloc( data->new_color, - sizeof(int)*data->buff_sz); + data->indent = realloc( data->indent, + sizeof(int)*data->buff_sz); if( data->buff==0 || data->search_buff==0 || data->color==0 || - data->new_color == 0 ) + data->indent == 0 ) { DIE_MEM(); } @@ -627,10 +627,10 @@ void reader_exit( int do_exit, int forced ) void repaint() { calc_prompt(); + + parser_test( data->buff, data->indent, 0, 0 ); -// assert( wcslen( (wchar_t *)data->prompt_buff.buff)); - - s_write( &data->screen, (wchar_t *)data->prompt_buff.buff, data->buff, data->color, data->buff_pos ); + s_write( &data->screen, (wchar_t *)data->prompt_buff.buff, data->buff, data->color, data->indent, data->buff_pos ); } @@ -1679,7 +1679,7 @@ void reader_run_command( const wchar_t *cmd ) static int shell_test( wchar_t *b ) { - int res = parser_test( b, 0, 0 ); + int res = parser_test( b, 0, 0, 0 ); if( res & PARSER_TEST_ERROR ) { @@ -1687,10 +1687,11 @@ static int shell_test( wchar_t *b ) sb_init( &sb ); int tmp[1]; + int tmp2[1]; - s_write( &data->screen, L"", L"", tmp, 0 ); + s_write( &data->screen, L"", L"", tmp, tmp2, 0 ); - parser_test( b, &sb, L"fish" ); + parser_test( b, 0, &sb, L"fish" ); fwprintf( stderr, L"%ls", sb.buff ); sb_destroy( &sb ); } @@ -1753,7 +1754,7 @@ void reader_pop() free( n->prompt ); free( n->buff ); free( n->color ); - free( n->new_color ); + free( n->indent ); free( n->search_buff ); s_destroy( &n->screen ); @@ -2550,7 +2551,7 @@ static int read_ni( int fd ) string_buffer_t sb; sb_init( &sb ); - if( !parser_test( str, &sb, L"fish" ) ) + if( !parser_test( str, 0, &sb, L"fish" ) ) { eval( str, 0, TOP ); } diff --git a/screen.c b/screen.c index 3c24285b2..6bbadefd8 100644 --- a/screen.c +++ b/screen.c @@ -337,8 +337,9 @@ static line_t *s_create_line() than the screen width. */ static void s_desired_append_char( screen_t *s, - wchar_t b, + wchar_t b, int c, + int indent, int prompt_width ) { int line_no = s->desired_cursor[1]; @@ -352,9 +353,9 @@ static void s_desired_append_char( screen_t *s, al_push( &s->desired, current ); s->desired_cursor[1]++; s->desired_cursor[0]=0; - for( i=0; i < prompt_width; i++ ) + for( i=0; i < prompt_width+indent*4; i++ ) { - s_desired_append_char( s, L' ', 0, prompt_width ); + s_desired_append_char( s, L' ', 0, indent, prompt_width ); } break; } @@ -400,9 +401,9 @@ static void s_desired_append_char( screen_t *s, s->desired_cursor[0]=0; for( i=0; i < (prompt_width-ew); i++ ) { - s_desired_append_char( s, L' ', 0, prompt_width ); + s_desired_append_char( s, L' ', 0, indent, prompt_width ); } - s_desired_append_char( s, ellipsis_char, HIGHLIGHT_COMMENT, prompt_width ); + s_desired_append_char( s, ellipsis_char, HIGHLIGHT_COMMENT, indent, prompt_width ); } al_set_long( ¤t->text, s->desired_cursor[0], b ); @@ -687,6 +688,7 @@ void s_write( screen_t *s, wchar_t *prompt, wchar_t *b, int *c, + int *indent, int cursor ) { int i; @@ -719,26 +721,42 @@ void s_write( screen_t *s, for( i=0; idesired_cursor[0] - wcwidth(b[i]); + cursor_arr[0] = s->desired_cursor[0]; cursor_arr[1] = s->desired_cursor[1]; } + s_desired_append_char( s, b[i], col, ind, prompt_width ); + + if( i== cursor && s->desired_cursor[1] != cursor_arr[1] && b[i] != L'\n' ) + { + /** + Ugh. We are placed exactly at the wrapping point of a + wrapped line, move cursor to the line below so the + cursor won't be on the ellipsis which looks + unintuitive. + */ + cursor_arr[0] = s->desired_cursor[0] - wcwidth(b[i]); + cursor_arr[1] = s->desired_cursor[1]; + } } if( i == cursor ) diff --git a/screen.h b/screen.h index 03e89e5ab..33f578cdf 100644 --- a/screen.h +++ b/screen.h @@ -90,6 +90,7 @@ void s_write( screen_t *s, wchar_t *prompt, wchar_t *commandline, int *colors, + int *indent, int cursor_pos ); /** diff --git a/tokenizer.c b/tokenizer.c index ae9e5bb89..a173716d3 100644 --- a/tokenizer.c +++ b/tokenizer.c @@ -30,6 +30,11 @@ segments. */ #define EOL_ERROR _( L"Unexpected end of token" ) +/** + Error string for unexpected end of string +*/ +#define QUOTE_ERROR _( L"Unterminated quote" ) + /** Error string for mismatched parenthesis */ @@ -101,19 +106,26 @@ static int check_size( tokenizer *tok, size_t len ) /** Set the latest tokens string to be the specified error message */ -static void tok_error( tokenizer *tok, const wchar_t *err ) +static void tok_error( tokenizer *tok, int error_type, const wchar_t *error_message ) { tok->last_type = TOK_ERROR; - if( !check_size( tok, wcslen( err)+1 )) + tok->error = error_type; + if( !check_size( tok, wcslen( error_message)+1 )) { if( tok->last != 0 ) *tok->last=0; return; } - wcscpy( tok->last, err ); + wcscpy( tok->last, error_message ); } +int tok_get_error( tokenizer *tok ) +{ + return tok->error; +} + + void tok_init( tokenizer *tok, const wchar_t *b, int flags ) { @@ -239,7 +251,7 @@ static void read_string( tokenizer *tok ) tok->buff++; if( *tok->buff == L'\0' ) { - tok_error( tok, EOL_ERROR ); + tok_error( tok, TOK_UNTERMINATED_ESCAPE, EOL_ERROR ); return; } else if( *tok->buff == L'\n' && mode == 0) @@ -298,7 +310,7 @@ static void read_string( tokenizer *tok ) if( (!tok->accept_unfinished) ) { - tok_error( tok, EOL_ERROR ); + tok_error( tok, TOK_UNTERMINATED_QUOTE, QUOTE_ERROR ); return; } do_loop = 0; @@ -331,7 +343,16 @@ static void read_string( tokenizer *tok ) tok->buff=(wchar_t *)end; } else + { + tok->buff += wcslen( tok->buff ); + if( (!tok->accept_unfinished) ) + { + tok_error( tok, TOK_UNTERMINATED_QUOTE, QUOTE_ERROR ); + return; + } do_loop = 0; + } + break; } @@ -380,7 +401,7 @@ static void read_string( tokenizer *tok ) if( (!tok->accept_unfinished) && (mode!=0) ) { - tok_error( tok, PARAN_ERROR ); + tok_error( tok, TOK_UNTERMINATED_SUBSHELL, PARAN_ERROR ); return; } @@ -441,7 +462,7 @@ static void read_redirect( tokenizer *tok, int fd ) { if( fd == 0 ) { - tok_error( tok, PIPE_ERROR ); + tok_error( tok, TOK_OTHER, PIPE_ERROR ); return; } check_size( tok, FD_STR_MAX_LEN ); @@ -458,7 +479,7 @@ static void read_redirect( tokenizer *tok, int fd ) } else { - tok_error( tok, REDIRECT_ERROR); + tok_error( tok, TOK_OTHER, REDIRECT_ERROR); } if( !check_size( tok, 2 )) diff --git a/tokenizer.h b/tokenizer.h index 2a0ad513f..bced30e5e 100644 --- a/tokenizer.h +++ b/tokenizer.h @@ -29,7 +29,20 @@ enum token_type TOK_BACKGROUND,/**< send job to bg token */ TOK_COMMENT/**< comment token */ } -; + ; + +/** + Tokenizer error types +*/ +enum tokenizer_error +{ + TOK_UNTERMINATED_QUOTE, + TOK_UNTERMINATED_SUBSHELL, + TOK_UNTERMINATED_ESCAPE, + TOK_OTHER +} + ; + /** Flag telling the tokenizer to accept incomplete parameters, @@ -73,6 +86,8 @@ typedef struct int free_orig; /** Type of last quote, can be either ' or ".*/ wchar_t last_quote; + /** Last error */ + int error; } tokenizer; @@ -151,4 +166,10 @@ void tok_set_pos( tokenizer *tok, int pos ); */ const wchar_t *tok_get_desc( int type ); +/** + Get tokenizer error type. Should only be called if tok_last_tope returns TOK_ERROR. +*/ +int tok_get_error( tokenizer *tok ); + + #endif