2005-09-20 21:26:39 +08:00
|
|
|
/** \file highlight.c
|
|
|
|
Functions for syntax highlighting
|
|
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <wchar.h>
|
|
|
|
#include <wctype.h>
|
|
|
|
#include <termios.h>
|
|
|
|
#include <signal.h>
|
|
|
|
|
|
|
|
#include "config.h"
|
2006-02-28 21:17:16 +08:00
|
|
|
|
|
|
|
#include "fallback.h"
|
2005-09-20 21:26:39 +08:00
|
|
|
#include "util.h"
|
2006-02-28 21:17:16 +08:00
|
|
|
|
2005-09-20 21:26:39 +08:00
|
|
|
#include "wutil.h"
|
|
|
|
#include "highlight.h"
|
|
|
|
#include "tokenizer.h"
|
|
|
|
#include "proc.h"
|
|
|
|
#include "parser.h"
|
2006-01-31 00:51:50 +08:00
|
|
|
#include "parse_util.h"
|
2005-09-20 21:26:39 +08:00
|
|
|
#include "builtin.h"
|
|
|
|
#include "function.h"
|
|
|
|
#include "env.h"
|
|
|
|
#include "expand.h"
|
|
|
|
#include "sanity.h"
|
|
|
|
#include "common.h"
|
|
|
|
#include "complete.h"
|
|
|
|
#include "output.h"
|
|
|
|
|
|
|
|
static void highlight_universal_internal( wchar_t * buff,
|
|
|
|
int *color,
|
|
|
|
int pos,
|
|
|
|
array_list_t *error );
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
The environment variables used to specify the color of different tokens.
|
|
|
|
*/
|
2006-05-27 00:46:38 +08:00
|
|
|
static wchar_t *highlight_var[] =
|
2005-09-20 21:26:39 +08:00
|
|
|
{
|
|
|
|
L"fish_color_normal",
|
|
|
|
L"fish_color_command",
|
|
|
|
L"fish_color_redirection",
|
|
|
|
L"fish_color_end",
|
|
|
|
L"fish_color_error",
|
|
|
|
L"fish_color_param",
|
|
|
|
L"fish_color_comment",
|
|
|
|
L"fish_color_match",
|
|
|
|
L"fish_color_search_match",
|
2006-05-27 00:46:38 +08:00
|
|
|
L"fish_color_operator",
|
2006-05-27 20:35:16 +08:00
|
|
|
L"fish_color_escape",
|
|
|
|
L"fish_color_quote"
|
2005-09-20 21:26:39 +08:00
|
|
|
}
|
|
|
|
;
|
|
|
|
|
2006-05-27 00:46:38 +08:00
|
|
|
#define VAR_COUNT ( sizeof(highlight_var)/sizeof(wchar_t *) )
|
2005-09-20 21:26:39 +08:00
|
|
|
|
|
|
|
int highlight_get_color( int highlight )
|
|
|
|
{
|
|
|
|
if( highlight < 0 )
|
|
|
|
return FISH_COLOR_NORMAL;
|
2006-05-27 00:46:38 +08:00
|
|
|
if( highlight >= VAR_COUNT )
|
2005-09-20 21:26:39 +08:00
|
|
|
return FISH_COLOR_NORMAL;
|
|
|
|
|
2006-05-27 00:46:38 +08:00
|
|
|
wchar_t *val = env_get( highlight_var[highlight]);
|
2005-09-20 21:26:39 +08:00
|
|
|
if( val == 0 )
|
2006-05-27 00:46:38 +08:00
|
|
|
val = env_get( highlight_var[HIGHLIGHT_NORMAL]);
|
2005-09-20 21:26:39 +08:00
|
|
|
|
|
|
|
if( val == 0 )
|
|
|
|
{
|
|
|
|
return FISH_COLOR_NORMAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return output_color_code( val );
|
|
|
|
}
|
|
|
|
|
2006-05-27 08:56:18 +08:00
|
|
|
/**
|
|
|
|
Highligt operators (such as $, ~, %, as well as escaped characters.
|
|
|
|
*/
|
2006-05-27 00:46:38 +08:00
|
|
|
static void highlight_param( const wchar_t * buff,
|
|
|
|
int *color,
|
|
|
|
int pos,
|
|
|
|
array_list_t *error )
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
int mode = 0;
|
|
|
|
int in_pos, len = wcslen( buff );
|
|
|
|
int bracket_count=0;
|
|
|
|
wchar_t c;
|
|
|
|
|
|
|
|
for( in_pos=0;
|
|
|
|
in_pos<len;
|
|
|
|
in_pos++ )
|
|
|
|
{
|
|
|
|
c = buff[in_pos];
|
|
|
|
switch( mode )
|
|
|
|
{
|
|
|
|
|
|
|
|
/*
|
|
|
|
Mode 0 means unquoted string
|
|
|
|
*/
|
|
|
|
case 0:
|
|
|
|
{
|
|
|
|
if( c == L'\\' )
|
|
|
|
{
|
|
|
|
int start_pos = in_pos;
|
2006-05-30 08:35:35 +08:00
|
|
|
in_pos++;
|
2006-05-27 00:46:38 +08:00
|
|
|
|
2006-05-30 08:35:35 +08:00
|
|
|
if( wcschr( L"~%", buff[in_pos] ) )
|
2006-05-27 00:46:38 +08:00
|
|
|
{
|
2006-05-30 08:35:35 +08:00
|
|
|
if( in_pos == 1 )
|
2006-05-27 21:40:26 +08:00
|
|
|
{
|
2006-05-30 08:35:35 +08:00
|
|
|
color[start_pos] = HIGHLIGHT_ESCAPE;
|
|
|
|
color[in_pos+1] = HIGHLIGHT_NORMAL;
|
2006-05-27 21:40:26 +08:00
|
|
|
}
|
2006-05-30 08:35:35 +08:00
|
|
|
}
|
|
|
|
else if( buff[in_pos]==L',' )
|
|
|
|
{
|
|
|
|
if( bracket_count )
|
2006-05-27 21:40:26 +08:00
|
|
|
{
|
2006-05-30 08:35:35 +08:00
|
|
|
color[start_pos] = HIGHLIGHT_ESCAPE;
|
|
|
|
color[in_pos+1] = HIGHLIGHT_NORMAL;
|
2006-05-27 21:40:26 +08:00
|
|
|
}
|
2006-05-30 08:35:35 +08:00
|
|
|
}
|
|
|
|
else if( wcschr( L"nrtbe*?$(){}'\"<>^ \\#;|&", buff[in_pos] ) )
|
|
|
|
{
|
|
|
|
color[start_pos]=HIGHLIGHT_ESCAPE;
|
|
|
|
color[in_pos+1]=HIGHLIGHT_NORMAL;
|
|
|
|
}
|
|
|
|
else if( wcschr( L"uUxX01234567", buff[in_pos] ) )
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
long long res=0;
|
|
|
|
int chars=2;
|
|
|
|
int base=16;
|
2006-05-27 21:40:26 +08:00
|
|
|
|
2006-05-30 08:35:35 +08:00
|
|
|
int byte = 0;
|
|
|
|
wchar_t max_val = ASCII_MAX;
|
2006-05-27 00:46:38 +08:00
|
|
|
|
2006-05-30 08:35:35 +08:00
|
|
|
switch( buff[in_pos] )
|
2006-05-27 00:46:38 +08:00
|
|
|
{
|
2006-05-30 08:35:35 +08:00
|
|
|
case L'u':
|
|
|
|
{
|
|
|
|
chars=4;
|
|
|
|
max_val = UCS2_MAX;
|
|
|
|
break;
|
|
|
|
}
|
2006-05-27 00:46:38 +08:00
|
|
|
|
2006-05-30 08:35:35 +08:00
|
|
|
case L'U':
|
2006-05-27 00:46:38 +08:00
|
|
|
{
|
2006-05-30 08:35:35 +08:00
|
|
|
chars=8;
|
|
|
|
max_val = WCHAR_MAX;
|
|
|
|
break;
|
2006-05-27 00:46:38 +08:00
|
|
|
}
|
2006-05-30 08:35:35 +08:00
|
|
|
|
|
|
|
case L'x':
|
2006-05-27 00:46:38 +08:00
|
|
|
{
|
2006-05-30 08:35:35 +08:00
|
|
|
break;
|
2006-05-27 00:46:38 +08:00
|
|
|
}
|
2006-05-30 08:35:35 +08:00
|
|
|
|
|
|
|
case L'X':
|
2006-05-27 00:46:38 +08:00
|
|
|
{
|
2006-05-30 08:35:35 +08:00
|
|
|
byte=1;
|
|
|
|
max_val = BYTE_MAX;
|
|
|
|
break;
|
2006-05-27 00:46:38 +08:00
|
|
|
}
|
2006-05-30 08:35:35 +08:00
|
|
|
|
|
|
|
default:
|
|
|
|
{
|
|
|
|
base=8;
|
|
|
|
chars=3;
|
|
|
|
in_pos--;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for( i=0; i<chars; i++ )
|
|
|
|
{
|
|
|
|
int d = convert_digit( buff[++in_pos],base);
|
|
|
|
|
|
|
|
if( d < 0 )
|
|
|
|
{
|
|
|
|
in_pos--;
|
|
|
|
break;
|
2006-05-27 00:46:38 +08:00
|
|
|
}
|
|
|
|
|
2006-05-30 08:35:35 +08:00
|
|
|
res=(res*base)|d;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( (res <= max_val) )
|
|
|
|
{
|
|
|
|
color[start_pos] = HIGHLIGHT_ESCAPE;
|
|
|
|
color[in_pos+1] = HIGHLIGHT_NORMAL;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
color[start_pos] = HIGHLIGHT_ERROR;
|
|
|
|
color[in_pos+1] = HIGHLIGHT_NORMAL;
|
2006-05-27 00:46:38 +08:00
|
|
|
}
|
|
|
|
}
|
2006-05-30 08:35:35 +08:00
|
|
|
|
2006-05-27 00:46:38 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
switch( buff[in_pos]){
|
|
|
|
case L'~':
|
|
|
|
case L'%':
|
|
|
|
{
|
|
|
|
if( in_pos == 0 )
|
|
|
|
{
|
|
|
|
color[in_pos] = HIGHLIGHT_OPERATOR;
|
|
|
|
color[in_pos+1] = HIGHLIGHT_NORMAL;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2006-06-02 07:04:38 +08:00
|
|
|
case L'$':
|
|
|
|
{
|
|
|
|
wchar_t n = buff[in_pos+1];
|
|
|
|
color[in_pos] = (n==L'$'||wcsvarchr(n))? HIGHLIGHT_OPERATOR:HIGHLIGHT_ERROR;
|
|
|
|
color[in_pos+1] = HIGHLIGHT_NORMAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-05-27 00:46:38 +08:00
|
|
|
case L'*':
|
|
|
|
case L'?':
|
2006-05-27 08:56:18 +08:00
|
|
|
case L'(':
|
|
|
|
case L')':
|
2006-05-27 00:46:38 +08:00
|
|
|
{
|
|
|
|
color[in_pos] = HIGHLIGHT_OPERATOR;
|
|
|
|
color[in_pos+1] = HIGHLIGHT_NORMAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case L'{':
|
|
|
|
{
|
2006-05-27 08:56:18 +08:00
|
|
|
color[in_pos] = HIGHLIGHT_OPERATOR;
|
|
|
|
color[in_pos+1] = HIGHLIGHT_NORMAL;
|
2006-05-27 00:46:38 +08:00
|
|
|
bracket_count++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case L'}':
|
|
|
|
{
|
2006-05-27 08:56:18 +08:00
|
|
|
color[in_pos] = HIGHLIGHT_OPERATOR;
|
|
|
|
color[in_pos+1] = HIGHLIGHT_NORMAL;
|
2006-05-27 00:46:38 +08:00
|
|
|
bracket_count--;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case L',':
|
|
|
|
{
|
|
|
|
if( bracket_count )
|
|
|
|
{
|
|
|
|
color[in_pos] = HIGHLIGHT_OPERATOR;
|
|
|
|
color[in_pos+1] = HIGHLIGHT_NORMAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case L'\'':
|
|
|
|
{
|
2006-05-27 20:35:16 +08:00
|
|
|
color[in_pos] = HIGHLIGHT_QUOTE;
|
2006-05-27 00:46:38 +08:00
|
|
|
mode = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case L'\"':
|
|
|
|
{
|
2006-05-27 20:35:16 +08:00
|
|
|
color[in_pos] = HIGHLIGHT_QUOTE;
|
2006-05-27 00:46:38 +08:00
|
|
|
mode = 2;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Mode 1 means single quoted string, i.e 'foo'
|
|
|
|
*/
|
|
|
|
case 1:
|
|
|
|
{
|
|
|
|
if( c == L'\\' )
|
|
|
|
{
|
|
|
|
int start_pos = in_pos;
|
|
|
|
switch( buff[++in_pos] )
|
|
|
|
{
|
|
|
|
case '\\':
|
|
|
|
case L'\'':
|
|
|
|
{
|
|
|
|
color[start_pos] = HIGHLIGHT_ESCAPE;
|
2006-05-27 20:35:16 +08:00
|
|
|
color[in_pos+1] = HIGHLIGHT_QUOTE;
|
2006-05-27 00:46:38 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 0:
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
if( c == L'\'' )
|
|
|
|
{
|
|
|
|
mode = 0;
|
2006-05-27 20:35:16 +08:00
|
|
|
color[in_pos+1] = HIGHLIGHT_NORMAL;
|
2006-05-27 00:46:38 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Mode 2 means double quoted string, i.e. "foo"
|
|
|
|
*/
|
|
|
|
case 2:
|
|
|
|
{
|
|
|
|
switch( c )
|
|
|
|
{
|
|
|
|
case '"':
|
|
|
|
{
|
|
|
|
mode = 0;
|
2006-05-27 20:35:16 +08:00
|
|
|
color[in_pos+1] = HIGHLIGHT_NORMAL;
|
2006-05-27 00:46:38 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case '\\':
|
|
|
|
{
|
|
|
|
int start_pos = in_pos;
|
|
|
|
switch( buff[++in_pos] )
|
|
|
|
{
|
|
|
|
case L'\0':
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
case '\\':
|
|
|
|
case L'$':
|
|
|
|
case '"':
|
|
|
|
{
|
|
|
|
color[start_pos] = HIGHLIGHT_ESCAPE;
|
2006-05-27 20:35:16 +08:00
|
|
|
color[in_pos+1] = HIGHLIGHT_QUOTE;
|
2006-05-27 00:46:38 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case '$':
|
|
|
|
{
|
2006-06-02 07:04:38 +08:00
|
|
|
wchar_t n = buff[in_pos+1];
|
|
|
|
color[in_pos] = (n==L'$'||wcsvarchr(n))? HIGHLIGHT_OPERATOR:HIGHLIGHT_ERROR;
|
|
|
|
color[in_pos+1] = HIGHLIGHT_QUOTE;
|
2006-05-27 00:46:38 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-09-20 21:26:39 +08:00
|
|
|
|
|
|
|
void highlight_shell( wchar_t * buff,
|
|
|
|
int *color,
|
|
|
|
int pos,
|
|
|
|
array_list_t *error )
|
|
|
|
{
|
|
|
|
tokenizer tok;
|
|
|
|
int had_cmd=0;
|
|
|
|
int i;
|
|
|
|
int last_val;
|
|
|
|
wchar_t *last_cmd=0;
|
|
|
|
int len = wcslen(buff);
|
|
|
|
|
|
|
|
if( !len )
|
|
|
|
return;
|
|
|
|
|
|
|
|
for( i=0; buff[i] != 0; i++ )
|
|
|
|
color[i] = -1;
|
|
|
|
|
|
|
|
for( tok_init( &tok, buff, TOK_SHOW_COMMENTS );
|
|
|
|
tok_has_next( &tok );
|
|
|
|
tok_next( &tok ) )
|
|
|
|
{
|
|
|
|
int last_type = tok_last_type( &tok );
|
|
|
|
int prev_argc=0;
|
|
|
|
|
|
|
|
switch( last_type )
|
|
|
|
{
|
|
|
|
case TOK_STRING:
|
|
|
|
{
|
|
|
|
if( had_cmd )
|
|
|
|
{
|
|
|
|
|
|
|
|
/*Parameter */
|
|
|
|
wchar_t *param = tok_last( &tok );
|
|
|
|
if( param[0] == L'-' )
|
|
|
|
{
|
|
|
|
if( complete_is_valid_option( last_cmd, param, error ))
|
|
|
|
color[ tok_get_pos( &tok ) ] = HIGHLIGHT_PARAM;
|
|
|
|
else
|
|
|
|
color[ tok_get_pos( &tok ) ] = HIGHLIGHT_ERROR;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
color[ tok_get_pos( &tok ) ] = HIGHLIGHT_PARAM;
|
|
|
|
}
|
2006-05-27 00:46:38 +08:00
|
|
|
|
|
|
|
highlight_param( param,
|
|
|
|
&color[tok_get_pos( &tok )],
|
|
|
|
pos-tok_get_pos( &tok ),
|
|
|
|
error );
|
|
|
|
|
2005-09-20 21:26:39 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
prev_argc=0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
Command. First check that the command actually exists.
|
|
|
|
*/
|
2006-02-12 21:18:46 +08:00
|
|
|
wchar_t *cmd = expand_one( 0,
|
|
|
|
wcsdup(tok_last( &tok )),
|
|
|
|
EXPAND_SKIP_SUBSHELL | EXPAND_SKIP_VARIABLES);
|
|
|
|
|
2005-09-20 21:26:39 +08:00
|
|
|
if( cmd == 0 )
|
|
|
|
{
|
|
|
|
color[ tok_get_pos( &tok ) ] = HIGHLIGHT_ERROR;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int is_cmd = 0;
|
|
|
|
int is_subcommand = 0;
|
|
|
|
int mark = tok_get_pos( &tok );
|
|
|
|
color[ tok_get_pos( &tok ) ] = HIGHLIGHT_COMMAND;
|
|
|
|
|
|
|
|
if( parser_is_subcommand( cmd ) )
|
|
|
|
{
|
|
|
|
tok_next( &tok );
|
|
|
|
if(( wcscmp( L"-h", tok_last( &tok ) ) == 0 ) ||
|
|
|
|
( wcscmp( L"--help", tok_last( &tok ) ) == 0 ) )
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
The builtin and command builtins
|
|
|
|
are normally followed by another
|
|
|
|
command, but if they are invoked
|
|
|
|
with the -h option, their help text
|
|
|
|
is displayed instead
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
is_subcommand = 1;
|
|
|
|
}
|
|
|
|
tok_set_pos( &tok, mark );
|
|
|
|
}
|
|
|
|
|
|
|
|
if( !is_subcommand )
|
|
|
|
{
|
2006-06-02 06:42:17 +08:00
|
|
|
wchar_t *tmp;
|
2005-09-20 21:26:39 +08:00
|
|
|
/*
|
|
|
|
OK, this is a command, it has been
|
|
|
|
successfully expanded and everything
|
|
|
|
looks ok. Lets check if the command
|
|
|
|
exists.
|
|
|
|
*/
|
2006-06-02 06:42:17 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
First check if it is a builtin or
|
|
|
|
function, since we don't have to stat
|
|
|
|
any files for that
|
|
|
|
*/
|
2005-09-20 21:26:39 +08:00
|
|
|
is_cmd |= builtin_exists( cmd );
|
|
|
|
is_cmd |= function_exists( cmd );
|
2006-06-02 06:42:17 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
Moving on to expensive tests
|
2005-09-20 21:26:39 +08:00
|
|
|
*/
|
2006-06-02 06:42:17 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
Check if this is a regular command
|
|
|
|
*/
|
|
|
|
is_cmd |= !!(tmp=get_filename( cmd ));
|
2005-09-20 21:26:39 +08:00
|
|
|
free(tmp);
|
2006-06-02 06:42:17 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
Could not find the command. Maybe it is
|
|
|
|
a path for a implicit cd command.
|
|
|
|
*/
|
|
|
|
is_cmd |= !!(tmp=parser_cdpath_get( cmd ));
|
|
|
|
free( tmp );
|
|
|
|
|
2005-09-20 21:26:39 +08:00
|
|
|
if( is_cmd )
|
|
|
|
{
|
|
|
|
color[ tok_get_pos( &tok ) ] = HIGHLIGHT_COMMAND;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if( error )
|
|
|
|
al_push( error, wcsdupcat2 ( L"Unknown command \'", cmd, L"\'", 0 ));
|
|
|
|
color[ tok_get_pos( &tok ) ] = (HIGHLIGHT_ERROR);
|
|
|
|
}
|
|
|
|
had_cmd = 1;
|
|
|
|
}
|
|
|
|
free(cmd);
|
|
|
|
|
|
|
|
if( had_cmd )
|
|
|
|
{
|
|
|
|
if( last_cmd )
|
|
|
|
free( last_cmd );
|
|
|
|
last_cmd = wcsdup( tok_last( &tok ) );
|
2006-06-02 06:42:17 +08:00
|
|
|
}
|
2005-09-20 21:26:39 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case TOK_REDIRECT_OUT:
|
|
|
|
case TOK_REDIRECT_IN:
|
|
|
|
case TOK_REDIRECT_APPEND:
|
|
|
|
case TOK_REDIRECT_FD:
|
|
|
|
{
|
|
|
|
if( !had_cmd )
|
|
|
|
{
|
|
|
|
color[ tok_get_pos( &tok ) ] = HIGHLIGHT_ERROR;
|
|
|
|
if( error )
|
|
|
|
al_push( error, wcsdup ( L"Redirection without a command" ) );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
wchar_t *target=0;
|
|
|
|
|
|
|
|
color[ tok_get_pos( &tok ) ] = HIGHLIGHT_REDIRECTION;
|
|
|
|
tok_next( &tok );
|
|
|
|
|
|
|
|
/*
|
|
|
|
Check that we are redirecting into a file
|
|
|
|
*/
|
|
|
|
|
|
|
|
switch( tok_last_type( &tok ) )
|
|
|
|
{
|
|
|
|
case TOK_STRING:
|
|
|
|
{
|
2006-02-09 23:50:20 +08:00
|
|
|
target = expand_one( 0, wcsdup( tok_last( &tok ) ), EXPAND_SKIP_SUBSHELL);
|
2005-09-20 21:26:39 +08:00
|
|
|
/*
|
|
|
|
Redirect filename may contain a subshell.
|
|
|
|
If so, it will be ignored/not flagged.
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
{
|
|
|
|
color[ tok_get_pos( &tok ) ] = HIGHLIGHT_ERROR;
|
|
|
|
if( error )
|
|
|
|
al_push( error, wcsdup ( L"Invalid redirection" ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if( target != 0 )
|
|
|
|
{
|
|
|
|
wchar_t *dir = wcsdup( target );
|
|
|
|
wchar_t *dir_end = wcsrchr( dir, L'/' );
|
|
|
|
struct stat buff;
|
|
|
|
/*
|
|
|
|
If file is in directory other than '.', check
|
|
|
|
that the directory exists.
|
|
|
|
*/
|
|
|
|
if( dir_end != 0 )
|
|
|
|
{
|
|
|
|
*dir_end = 0;
|
|
|
|
if( wstat( dir, &buff ) == -1 )
|
|
|
|
{
|
|
|
|
color[ tok_get_pos( &tok ) ] = HIGHLIGHT_ERROR;
|
|
|
|
if( error )
|
|
|
|
al_push( error, wcsdupcat2( L"Directory \'", dir, L"\' does not exist", 0 ) );
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
free( dir );
|
|
|
|
|
|
|
|
/*
|
|
|
|
If the file is read from or appended to, check
|
|
|
|
if it exists.
|
|
|
|
*/
|
|
|
|
if( last_type == TOK_REDIRECT_IN ||
|
|
|
|
last_type == TOK_REDIRECT_APPEND )
|
|
|
|
{
|
|
|
|
if( wstat( target, &buff ) == -1 )
|
|
|
|
{
|
|
|
|
color[ tok_get_pos( &tok ) ] = HIGHLIGHT_ERROR;
|
|
|
|
if( error )
|
|
|
|
al_push( error, wcsdupcat2( L"File \'", target, L"\' does not exist", 0 ) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
free( target );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case TOK_PIPE:
|
|
|
|
case TOK_BACKGROUND:
|
|
|
|
{
|
|
|
|
if( had_cmd )
|
|
|
|
{
|
|
|
|
color[ tok_get_pos( &tok ) ] = HIGHLIGHT_END;
|
|
|
|
had_cmd = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
color[ tok_get_pos( &tok ) ] = HIGHLIGHT_ERROR;
|
|
|
|
if( error )
|
|
|
|
al_push( error, wcsdup ( L"No job to put in background" ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case TOK_END:
|
|
|
|
{
|
|
|
|
color[ tok_get_pos( &tok ) ] = HIGHLIGHT_END;
|
|
|
|
had_cmd = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case TOK_COMMENT:
|
|
|
|
{
|
|
|
|
color[ tok_get_pos( &tok ) ] = HIGHLIGHT_COMMENT;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case TOK_ERROR:
|
|
|
|
default:
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
If the tokenizer reports an error, highlight it as such.
|
|
|
|
*/
|
|
|
|
if( error )
|
|
|
|
al_push( error, wcsdup ( tok_last( &tok) ) );
|
|
|
|
color[ tok_get_pos( &tok ) ] = HIGHLIGHT_ERROR;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if( last_cmd )
|
|
|
|
free( last_cmd );
|
|
|
|
|
|
|
|
tok_destroy( &tok );
|
|
|
|
|
|
|
|
/*
|
|
|
|
Locate and syntax highlight subshells recursively
|
|
|
|
*/
|
2005-10-25 19:03:52 +08:00
|
|
|
|
2005-09-20 21:26:39 +08:00
|
|
|
wchar_t *buffcpy = wcsdup( buff );
|
|
|
|
wchar_t *subpos=buffcpy;
|
|
|
|
int done=0;
|
|
|
|
|
|
|
|
while( 1 )
|
|
|
|
{
|
|
|
|
wchar_t *begin, *end;
|
2006-01-31 00:51:50 +08:00
|
|
|
|
|
|
|
if( parse_util_locate_cmdsubst( subpos,
|
|
|
|
(const wchar_t **)&begin,
|
|
|
|
(const wchar_t **)&end,
|
|
|
|
1) <= 0)
|
2005-09-20 21:26:39 +08:00
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( !*end )
|
|
|
|
done=1;
|
|
|
|
else
|
|
|
|
*end=0;
|
|
|
|
|
|
|
|
highlight_shell( begin+1, color +(begin-buffcpy)+1, -1, error );
|
2006-05-27 08:56:18 +08:00
|
|
|
color[end-buffcpy]=HIGHLIGHT_OPERATOR;
|
2005-09-20 21:26:39 +08:00
|
|
|
|
|
|
|
if( done )
|
|
|
|
break;
|
|
|
|
|
|
|
|
subpos = end+1;
|
2005-10-25 19:03:52 +08:00
|
|
|
|
2005-09-20 21:26:39 +08:00
|
|
|
}
|
2005-10-25 19:03:52 +08:00
|
|
|
free( buffcpy);
|
2005-09-20 21:26:39 +08:00
|
|
|
|
|
|
|
|
|
|
|
last_val=0;
|
|
|
|
for( i=0; buff[i] != 0; i++ )
|
|
|
|
{
|
|
|
|
if( color[i] >= 0 )
|
|
|
|
last_val = color[i];
|
|
|
|
else
|
|
|
|
color[i] = last_val;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
highlight_universal_internal( buff, color, pos, error );
|
|
|
|
|
|
|
|
/*
|
|
|
|
Spaces should not be highlighted at all, since it makes cursor look funky in some terminals
|
|
|
|
*/
|
|
|
|
for( i=0; buff[i]; i++ )
|
|
|
|
{
|
|
|
|
if( iswspace(buff[i]) )
|
|
|
|
{
|
|
|
|
color[i]=0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
Perform quote and parenthesis highlighting on the specified string.
|
|
|
|
*/
|
|
|
|
static void highlight_universal_internal( wchar_t * buff,
|
|
|
|
int *color,
|
|
|
|
int pos,
|
|
|
|
array_list_t *error )
|
|
|
|
{
|
|
|
|
|
|
|
|
if( (pos >= 0) && (pos < wcslen(buff)) )
|
|
|
|
{
|
|
|
|
|
|
|
|
/*
|
|
|
|
Highlight matching quotes
|
|
|
|
*/
|
|
|
|
if( (buff[pos] == L'\'') || (buff[pos] == L'\"') )
|
|
|
|
{
|
|
|
|
|
|
|
|
array_list_t l;
|
|
|
|
al_init( &l );
|
|
|
|
|
|
|
|
int level=0;
|
|
|
|
wchar_t prev_q=0;
|
|
|
|
|
|
|
|
wchar_t *str=buff;
|
|
|
|
|
|
|
|
int match_found=0;
|
|
|
|
|
|
|
|
while(*str)
|
|
|
|
{
|
|
|
|
switch( *str )
|
|
|
|
{
|
|
|
|
case L'\\':
|
|
|
|
str++;
|
|
|
|
break;
|
|
|
|
case L'\"':
|
|
|
|
case L'\'':
|
|
|
|
if( level == 0 )
|
|
|
|
{
|
|
|
|
level++;
|
|
|
|
al_push( &l, (void *)(str-buff) );
|
|
|
|
prev_q = *str;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if( prev_q == *str )
|
|
|
|
{
|
2006-06-02 03:42:31 +08:00
|
|
|
long pos1, pos2;
|
2005-09-20 21:26:39 +08:00
|
|
|
|
|
|
|
level--;
|
2006-06-02 03:42:31 +08:00
|
|
|
pos1 = (long)al_pop( &l );
|
2005-09-20 21:26:39 +08:00
|
|
|
pos2 = str-buff;
|
|
|
|
if( pos1==pos || pos2==pos )
|
|
|
|
{
|
|
|
|
color[pos1]|=HIGHLIGHT_MATCH<<8;
|
|
|
|
color[pos2]|=HIGHLIGHT_MATCH<<8;
|
|
|
|
match_found = 1;
|
|
|
|
|
|
|
|
}
|
|
|
|
prev_q = *str==L'\"'?L'\'':L'\"';
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
level++;
|
|
|
|
al_push( &l, (void *)(str-buff) );
|
|
|
|
prev_q = *str;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if( (*str == L'\0'))
|
|
|
|
break;
|
|
|
|
|
|
|
|
str++;
|
|
|
|
}
|
|
|
|
|
|
|
|
al_destroy( &l );
|
|
|
|
|
|
|
|
if( !match_found )
|
|
|
|
color[pos] = HIGHLIGHT_ERROR<<8;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Highlight matching parenthesis
|
|
|
|
*/
|
|
|
|
if( wcschr( L"()[]{}", buff[pos] ) )
|
|
|
|
{
|
|
|
|
int step = wcschr(L"({[", buff[pos])?1:-1;
|
|
|
|
wchar_t dec_char = *(wcschr( L"()[]{}", buff[pos] ) + step);
|
|
|
|
wchar_t inc_char = buff[pos];
|
|
|
|
int level = 0;
|
|
|
|
wchar_t *str = &buff[pos];
|
2006-06-04 06:35:33 +08:00
|
|
|
int match_found=0;
|
2005-09-20 21:26:39 +08:00
|
|
|
|
|
|
|
while( (str >= buff) && *str)
|
|
|
|
{
|
|
|
|
if( *str == inc_char )
|
|
|
|
level++;
|
|
|
|
if( *str == dec_char )
|
|
|
|
level--;
|
|
|
|
if( level == 0 )
|
|
|
|
{
|
|
|
|
int pos2 = str-buff;
|
|
|
|
color[pos]|=HIGHLIGHT_MATCH<<8;
|
|
|
|
color[pos2]|=HIGHLIGHT_MATCH<<8;
|
|
|
|
match_found=1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
str+= step;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( !match_found )
|
|
|
|
color[pos] = HIGHLIGHT_ERROR<<8;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void highlight_universal( wchar_t * buff,
|
|
|
|
int *color,
|
|
|
|
int pos,
|
|
|
|
array_list_t *error )
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2006-06-04 06:35:33 +08:00
|
|
|
for( i=0; buff[i]; i++ )
|
2005-09-20 21:26:39 +08:00
|
|
|
color[i] = 0;
|
|
|
|
|
|
|
|
highlight_universal_internal( buff, color, pos, error );
|
|
|
|
}
|