2005-09-20 23:26:39 +10:00
/** \file reader.c
Functions for reading data from stdin and passing to the
parser . If stdin is a keyboard , it supplies a killring , history ,
syntax highlighting , tab - completion and various other interactive features .
Internally the interactive mode functions rely in the functions of the
input library to read individual characters of input .
Token search is handled incrementally . Actual searches are only done
on when searching backwards , since the previous results are saved . The
last search position is remembered and a new search continues from the
last search position . All search results are saved in the list
' search_prev ' . When the user searches forward , i . e . presses Alt - down ,
the list is consulted for previous search result , and subsequent
backwards searches are also handled by consultiung the list up until
the end of the list is reached , at which point regular searching will
commence .
*/
# include "config.h"
# include <stdlib.h>
# include <stdio.h>
# include <string.h>
# include <errno.h>
# include <termios.h>
# include <sys/types.h>
# include <sys/stat.h>
2006-08-10 08:53:38 +10:00
# ifdef HAVE_SYS_TERMIOS_H
# include <sys/termios.h>
# endif
# ifdef HAVE_SYS_IOCTL_H
2005-09-20 23:26:39 +10:00
# include <sys/ioctl.h>
2006-08-10 08:53:38 +10:00
# endif
2005-09-20 23:26:39 +10:00
# include <sys/time.h>
# include <sys/wait.h>
2005-10-14 00:08:33 +10:00
# include <sys/poll.h>
2005-09-20 23:26:39 +10:00
# include <unistd.h>
# include <wctype.h>
# if HAVE_NCURSES_H
# include <ncurses.h>
# else
# include <curses.h>
# endif
# if HAVE_TERMIO_H
# include <termio.h>
# endif
2006-01-19 22:22:07 +10:00
# if HAVE_TERM_H
2005-09-20 23:26:39 +10:00
# include <term.h>
2006-01-19 22:22:07 +10:00
# elif HAVE_NCURSES_TERM_H
# include <ncurses/term.h>
# endif
2006-07-31 06:26:59 +10:00
# ifdef HAVE_SIGINFO_H
# include <siginfo.h>
# endif
2006-08-10 08:26:05 +10:00
# ifdef HAVE_SYS_SELECT_H
# include <sys/select.h>
# endif
2005-09-20 23:26:39 +10:00
# include <signal.h>
# include <fcntl.h>
# include <dirent.h>
# include <wchar.h>
2005-12-26 08:00:44 +10:00
# include <assert.h>
2006-02-28 23:17:16 +10:00
# include "fallback.h"
2005-09-20 23:26:39 +10:00
# include "util.h"
2006-02-28 23:17:16 +10:00
2005-09-20 23:26:39 +10:00
# include "wutil.h"
# include "highlight.h"
# include "reader.h"
# include "proc.h"
# include "parser.h"
# include "complete.h"
# include "history.h"
# include "common.h"
# include "sanity.h"
# include "env.h"
# include "exec.h"
# include "expand.h"
# include "tokenizer.h"
# include "kill.h"
# include "input_common.h"
# include "input.h"
# include "function.h"
# include "output.h"
2005-10-15 10:51:26 +10:00
# include "signal.h"
2006-10-02 02:02:58 +10:00
# include "screen.h"
2006-07-20 08:55:49 +10:00
2006-01-31 02:51:50 +10:00
# include "parse_util.h"
2005-09-20 23:26:39 +10:00
/**
2005-12-31 02:29:19 +10:00
Maximum length of prefix string when printing completion
list . Longer prefixes will be ellipsized .
2005-09-20 23:26:39 +10:00
*/
# define PREFIX_MAX_LEN 8
/**
A simple prompt for reading shell commands that does not rely on
fish specific commands , meaning it will work even if fish is not
installed . This is used by read_i .
*/
# define DEFAULT_PROMPT L"whoami; echo @; hostname|cut -d . -f 1; echo \" \"; pwd; printf '> ';"
/**
The default title for the reader . This is used by reader_readline .
*/
# define DEFAULT_TITLE L"echo $_ \" \"; pwd"
2005-10-14 00:08:33 +10:00
/**
The maximum number of characters to read from the keyboard without
2005-10-26 20:51:02 +10:00
repainting . Note that this readahead will only occur if new
2005-10-14 00:08:33 +10:00
characters are avaialble for reading , fish will never block for
more input without repainting .
*/
# define READAHEAD_MAX 256
2006-10-12 23:27:32 +10:00
# define KILL_APPEND 0
# define KILL_PREPEND 1
2005-09-20 23:26:39 +10:00
/**
A struct describing the state of the interactive reader . These
states can be stacked , in case reader_readline is called from
input_read ( ) .
*/
typedef struct reader_data
{
/**
2006-10-02 01:59:18 +10:00
Buffer containing the whole current commandline
2005-09-20 23:26:39 +10:00
*/
wchar_t * buff ;
2006-10-02 02:02:58 +10:00
screen_t screen ;
2005-09-20 23:26:39 +10:00
/**
Buffer containing the current search item
*/
wchar_t * search_buff ;
2006-10-02 01:59:18 +10:00
2005-09-20 23:26:39 +10:00
/**
2005-12-31 02:29:19 +10:00
Saved position used by token history search
2005-09-20 23:26:39 +10:00
*/
int token_history_pos ;
/**
Saved search string for token history search . Not handled by check_size .
*/
const wchar_t * token_history_buff ;
/**
List for storing previous search results . Used to avoid duplicates .
*/
array_list_t search_prev ;
/**
The current position in search_prev
*/
int search_pos ;
/**
Current size of the buffers
*/
size_t buff_sz ;
/**
Length of the command in buff . ( Not the length of buff itself )
*/
size_t buff_len ;
/**
2005-12-31 02:29:19 +10:00
The current position of the cursor in buff .
2005-09-20 23:26:39 +10:00
*/
size_t buff_pos ;
/**
Name of the current application
*/
wchar_t * name ;
2006-10-02 02:02:58 +10:00
/** The prompt command */
2005-09-20 23:26:39 +10:00
wchar_t * prompt ;
2006-10-02 02:02:58 +10:00
/** The output of the last evaluation of the prompt command */
string_buffer_t prompt_buff ;
2005-09-20 23:26:39 +10:00
/**
Color is the syntax highlighting for buff . The format is that
color [ i ] is the classification ( according to the enum in
highlight . h ) of buff [ i ] .
*/
int * color ;
/**
2006-10-17 01:32:26 +10:00
An array defining the block level at each character .
2005-09-20 23:26:39 +10:00
*/
2006-10-07 10:56:25 +10:00
int * indent ;
2005-09-20 23:26:39 +10:00
/**
Function for tab completion
*/
void ( * complete_func ) ( const wchar_t * ,
array_list_t * ) ;
/**
Function for syntax highlighting
*/
void ( * highlight_func ) ( wchar_t * ,
int * ,
int ,
array_list_t * ) ;
/**
Function for testing if the string can be returned
*/
int ( * test_func ) ( wchar_t * ) ;
/**
2005-12-31 02:29:19 +10:00
When this is true , the reader will exit
2005-09-20 23:26:39 +10:00
*/
int end_loop ;
2006-10-02 02:02:58 +10:00
2006-05-14 20:16:23 +10:00
/**
If this is true , exit reader even if there are running
jobs . This happens if we press e . g . ^ D twice .
*/
int prev_end_loop ;
2005-09-20 23:26:39 +10:00
2006-10-12 23:27:32 +10:00
string_buffer_t kill_item ;
2005-09-20 23:26:39 +10:00
/**
Pointer to previous reader_data
*/
struct reader_data * next ;
}
reader_data_t ;
/**
The current interactive reading context
*/
static reader_data_t * data = 0 ;
/**
2005-12-31 02:29:19 +10:00
Flag for ending non - interactive shell
2005-09-20 23:26:39 +10:00
*/
static int end_loop = 0 ;
/**
The list containing names of files that are being parsed
*/
static array_list_t current_filename ;
/**
Store the pid of the parent process , so the exit function knows whether it should reset the terminal or not .
*/
static pid_t original_pid ;
2006-01-24 06:40:14 +10:00
/**
This variable is set to true by the signal handler when ^ C is pressed
*/
2006-10-05 07:42:04 +10:00
static int interrupted = 0 ;
2005-09-20 23:26:39 +10:00
2006-01-24 06:40:14 +10:00
/**
Original terminal mode when fish was started
*/
static struct termios old_modes ;
2005-09-20 23:26:39 +10:00
/*
Prototypes for a bunch of functions defined later on .
*/
2006-03-10 23:38:09 +10:00
/**
Stores the previous termios mode so we can reset the modes when
we execute programs and when the shell exits .
*/
static struct termios saved_modes ;
2006-10-17 01:32:26 +10:00
static void reader_super_highlight_me_plenty ( int pos , array_list_t * error ) ;
2005-09-20 23:26:39 +10:00
2006-05-14 20:16:23 +10:00
/**
Variable to keep track of forced exits - see \ c reader_exit_forced ( ) ;
*/
static int exit_forced ;
2005-09-20 23:26:39 +10:00
2006-01-24 06:40:14 +10:00
/**
Give up control of terminal
*/
2005-09-20 23:26:39 +10:00
static void term_donate ( )
{
tcgetattr ( 0 , & old_modes ) ; /* get the current terminal modes */
set_color ( FISH_COLOR_NORMAL , FISH_COLOR_NORMAL ) ;
while ( 1 )
{
if ( tcsetattr ( 0 , TCSANOW , & saved_modes ) )
{
if ( errno ! = EINTR )
{
2006-01-04 22:51:02 +10:00
debug ( 1 , _ ( L " Could not set terminal mode for new job " ) ) ;
2005-09-20 23:26:39 +10:00
wperror ( L " tcsetattr " ) ;
break ;
}
}
else
break ;
}
2006-01-31 05:53:10 +10:00
2005-09-20 23:26:39 +10:00
}
2006-01-24 06:40:14 +10:00
/**
Grab control of terminal
*/
2005-09-20 23:26:39 +10:00
static void term_steal ( )
2006-01-31 05:53:10 +10:00
{
2005-09-20 23:26:39 +10:00
while ( 1 )
{
if ( tcsetattr ( 0 , TCSANOW , & shell_modes ) )
{
if ( errno ! = EINTR )
{
2006-01-04 22:51:02 +10:00
debug ( 1 , _ ( L " Could not set terminal mode for shell " ) ) ;
2005-09-20 23:26:39 +10:00
wperror ( L " tcsetattr " ) ;
break ;
}
}
else
break ;
}
2006-01-31 05:53:10 +10:00
common_handle_winch ( 0 ) ;
2005-09-20 23:26:39 +10:00
2005-12-31 02:29:19 +10:00
if ( tcsetattr ( 0 , TCSANOW , & old_modes ) ) /* return to previous mode */
{
wperror ( L " tcsetattr " ) ;
exit ( 1 ) ;
}
2005-09-20 23:26:39 +10:00
}
2006-05-14 20:16:23 +10:00
int reader_exit_forced ( )
{
return exit_forced ;
}
2006-10-26 06:36:08 +10:00
/**
Internal helper function for handling killing parts of text .
*/
2006-10-12 23:27:32 +10:00
static void reader_kill ( wchar_t * begin , int length , int mode , int new )
{
if ( new )
{
sb_clear ( & data - > kill_item ) ;
sb_append_substring ( & data - > kill_item , begin , length ) ;
kill_add ( ( wchar_t * ) data - > kill_item . buff ) ;
}
else
{
wchar_t * old = wcsdup ( ( wchar_t * ) data - > kill_item . buff ) ;
if ( mode = = KILL_APPEND )
{
sb_append_substring ( & data - > kill_item , begin , length ) ;
}
else
{
sb_clear ( & data - > kill_item ) ;
sb_append_substring ( & data - > kill_item , begin , length ) ;
sb_append ( & data - > kill_item , old ) ;
}
kill_replace ( old , ( wchar_t * ) data - > kill_item . buff ) ;
free ( old ) ;
}
2006-10-13 05:30:00 +10:00
if ( data - > buff_pos > ( begin - data - > buff ) )
{
data - > buff_pos = maxi ( begin - data - > buff , data - > buff_pos - length ) ;
}
data - > buff_len - = length ;
memmove ( begin , begin + length , sizeof ( wchar_t ) * ( wcslen ( begin + length ) + 1 ) ) ;
2006-10-17 01:32:26 +10:00
reader_super_highlight_me_plenty ( data - > buff_pos , 0 ) ;
2006-10-13 05:30:00 +10:00
repaint ( ) ;
2006-10-12 23:27:32 +10:00
}
2006-05-14 20:16:23 +10:00
2005-10-06 08:37:08 +10:00
void reader_handle_int ( int sig )
{
block_t * c = current_block ;
while ( c )
{
c - > skip = 1 ;
c = c - > outer ;
}
2006-10-05 07:42:04 +10:00
interrupted = 1 ;
2005-10-06 08:37:08 +10:00
}
2005-09-20 23:26:39 +10:00
wchar_t * reader_current_filename ( )
{
2006-01-27 00:48:10 +10:00
return al_get_count ( & current_filename ) ? ( wchar_t * ) al_peek ( & current_filename ) : 0 ;
2005-09-20 23:26:39 +10:00
}
2006-02-03 01:23:56 +10:00
void reader_push_current_filename ( const wchar_t * fn )
2005-09-20 23:26:39 +10:00
{
al_push ( & current_filename , fn ) ;
}
wchar_t * reader_pop_current_filename ( )
{
return ( wchar_t * ) al_pop ( & current_filename ) ;
}
/**
Make sure buffers are large enough to hold current data plus one extra character .
*/
static int check_size ( )
{
if ( data - > buff_sz < data - > buff_len + 2 )
{
data - > buff_sz = maxi ( 128 , data - > buff_len * 2 ) ;
data - > buff = realloc ( data - > buff ,
sizeof ( wchar_t ) * data - > buff_sz ) ;
data - > search_buff = realloc ( data - > search_buff ,
sizeof ( wchar_t ) * data - > buff_sz ) ;
data - > color = realloc ( data - > color ,
sizeof ( int ) * data - > buff_sz ) ;
2006-10-02 02:02:58 +10:00
2006-10-07 10:56:25 +10:00
data - > indent = realloc ( data - > indent ,
sizeof ( int ) * data - > buff_sz ) ;
2005-09-20 23:26:39 +10:00
if ( data - > buff = = 0 | |
data - > search_buff = = 0 | |
data - > color = = 0 | |
2006-10-07 10:56:25 +10:00
data - > indent = = 0 )
2005-09-20 23:26:39 +10:00
{
2006-07-03 20:39:57 +10:00
DIE_MEM ( ) ;
2005-09-20 23:26:39 +10:00
}
}
return 1 ;
}
/**
2006-01-31 05:53:10 +10:00
Compare two completions , ignoring their description .
2005-09-20 23:26:39 +10:00
*/
static int fldcmp ( wchar_t * a , wchar_t * b )
{
while ( 1 )
{
if ( * a ! = * b )
return * a - * b ;
if ( ( ( * a = = COMPLETE_SEP ) | | ( * a = = L ' \0 ' ) ) & &
( ( * b = = COMPLETE_SEP ) | | ( * b = = L ' \0 ' ) ) )
return 0 ;
a + + ;
b + + ;
}
}
/**
Remove any duplicate completions in the list . This relies on the
list first beeing sorted .
*/
static void remove_duplicates ( array_list_t * l )
{
int in , out ;
wchar_t * prev ;
if ( al_get_count ( l ) = = 0 )
return ;
prev = ( wchar_t * ) al_get ( l , 0 ) ;
for ( in = 1 , out = 1 ; in < al_get_count ( l ) ; in + + )
2006-02-23 01:41:52 +10:00
{
2005-09-20 23:26:39 +10:00
wchar_t * curr = ( wchar_t * ) al_get ( l , in ) ;
2006-02-23 01:41:52 +10:00
2005-09-20 23:26:39 +10:00
if ( fldcmp ( prev , curr ) = = 0 )
{
2006-02-23 01:41:52 +10:00
free ( curr ) ;
2005-09-20 23:26:39 +10:00
}
else
{
al_set ( l , out + + , curr ) ;
prev = curr ;
}
}
al_truncate ( l , out ) ;
}
2006-10-05 07:42:04 +10:00
int reader_interrupted ( )
2005-09-20 23:26:39 +10:00
{
2006-10-05 07:42:04 +10:00
int res = interrupted ;
2005-09-20 23:26:39 +10:00
if ( res )
2006-10-05 07:42:04 +10:00
interrupted = 0 ;
2005-09-20 23:26:39 +10:00
return res ;
}
void reader_write_title ( )
{
wchar_t * title ;
array_list_t l ;
wchar_t * term = env_get ( L " TERM " ) ;
2006-01-31 05:53:10 +10:00
2005-09-20 23:26:39 +10:00
/*
This is a pretty lame heuristic for detecting terminals that do
not support setting the title . If we recognise the terminal name
as that of a virtual terminal , we assume it supports setting the
title . Otherwise we check the ttyname .
*/
2006-08-24 20:30:14 +10:00
if ( ! term | | ! contains_str ( term , L " xterm " , L " screen " , L " nxterm " , L " rxvt " , ( wchar_t * ) 0 ) )
2005-09-20 23:26:39 +10:00
{
char * n = ttyname ( STDIN_FILENO ) ;
if ( strstr ( n , " tty " ) | | strstr ( n , " /vc/ " ) )
return ;
}
title = function_exists ( L " fish_title " ) ? L " fish_title " : DEFAULT_TITLE ;
if ( wcslen ( title ) = = 0 )
return ;
al_init ( & l ) ;
2006-02-16 23:36:32 +10:00
proc_push_interactive ( 0 ) ;
2005-09-20 23:26:39 +10:00
if ( exec_subshell ( title , & l ) ! = - 1 )
{
int i ;
2006-08-09 21:34:24 +10:00
if ( al_get_count ( & l ) > 0 )
2005-09-20 23:26:39 +10:00
{
2006-08-09 21:34:24 +10:00
writestr ( L " \ e]2; " ) ;
for ( i = 0 ; i < al_get_count ( & l ) ; i + + )
{
writestr ( ( wchar_t * ) al_get ( & l , i ) ) ;
}
writestr ( L " \7 " ) ;
2005-09-20 23:26:39 +10:00
}
}
2006-02-16 23:36:32 +10:00
proc_pop_interactive ( ) ;
2006-06-13 07:47:42 +10:00
al_foreach ( & l , & free ) ;
2005-09-20 23:26:39 +10:00
al_destroy ( & l ) ;
set_color ( FISH_COLOR_RESET , FISH_COLOR_RESET ) ;
}
/**
2006-10-17 01:32:26 +10:00
Reexecute the prompt command . The output is inserted into data - > prompt_buff .
2005-09-20 23:26:39 +10:00
*/
2006-10-17 01:32:26 +10:00
static void exec_prompt ( )
2005-09-20 23:26:39 +10:00
{
int i ;
2006-10-17 01:32:26 +10:00
array_list_t prompt_list ;
al_init ( & prompt_list ) ;
if ( data - > prompt )
2005-09-20 23:26:39 +10:00
{
2006-10-17 01:32:26 +10:00
proc_push_interactive ( 0 ) ;
2006-10-05 07:42:04 +10:00
2006-10-17 01:32:26 +10:00
if ( exec_subshell ( data - > prompt , & prompt_list ) = = - 1 )
2005-09-20 23:26:39 +10:00
{
2006-10-17 01:32:26 +10:00
/* If executing the prompt fails, make sure we at least don't print any junk */
al_foreach ( & prompt_list , & free ) ;
al_destroy ( & prompt_list ) ;
al_init ( & prompt_list ) ;
2005-09-20 23:26:39 +10:00
}
2006-10-17 01:32:26 +10:00
proc_pop_interactive ( ) ;
2005-09-20 23:26:39 +10:00
}
2006-10-17 01:32:26 +10:00
reader_write_title ( ) ;
sb_clear ( & data - > prompt_buff ) ;
for ( i = 0 ; i < al_get_count ( & prompt_list ) ; i + + )
{
sb_append ( & data - > prompt_buff , ( wchar_t * ) al_get ( & prompt_list , i ) ) ;
}
al_foreach ( & prompt_list , & free ) ;
al_destroy ( & prompt_list ) ;
2005-09-20 23:26:39 +10:00
}
void reader_init ( )
{
2006-08-24 20:43:54 +10:00
2006-03-10 23:38:09 +10:00
tcgetattr ( 0 , & shell_modes ) ; /* get the current terminal modes */
memcpy ( & saved_modes ,
& shell_modes ,
sizeof ( saved_modes ) ) ; /* save a copy so we can reset the terminal later */
shell_modes . c_lflag & = ~ ICANON ; /* turn off canonical mode */
shell_modes . c_lflag & = ~ ECHO ; /* turn off echo mode */
shell_modes . c_cc [ VMIN ] = 1 ;
shell_modes . c_cc [ VTIME ] = 0 ;
2005-09-20 23:26:39 +10:00
al_init ( & current_filename ) ;
}
void reader_destroy ( )
{
al_destroy ( & current_filename ) ;
2006-03-10 23:38:09 +10:00
tcsetattr ( 0 , TCSANOW , & saved_modes ) ;
2005-09-20 23:26:39 +10:00
}
2006-05-14 20:16:23 +10:00
void reader_exit ( int do_exit , int forced )
2005-09-20 23:26:39 +10:00
{
2006-02-08 19:24:29 +10:00
if ( data )
2005-09-20 23:26:39 +10:00
data - > end_loop = do_exit ;
2006-02-08 19:24:29 +10:00
end_loop = do_exit ;
2006-05-14 20:16:23 +10:00
if ( forced )
exit_forced = 1 ;
2005-09-20 23:26:39 +10:00
}
2006-10-02 06:54:23 +10:00
void repaint ( )
2005-09-20 23:26:39 +10:00
{
2006-10-07 10:56:25 +10:00
parser_test ( data - > buff , data - > indent , 0 , 0 ) ;
2006-10-26 06:36:08 +10:00
2006-10-08 07:06:31 +10:00
s_write ( & data - > screen ,
( wchar_t * ) data - > prompt_buff . buff ,
data - > buff ,
data - > color ,
data - > indent ,
data - > buff_pos ) ;
2006-10-02 02:02:58 +10:00
2005-09-20 23:26:39 +10:00
}
/**
Remove the previous character in the character buffer and on the
screen using syntax highlighting , etc .
*/
static void remove_backward ( )
{
if ( data - > buff_pos < = 0 )
return ;
if ( data - > buff_pos < data - > buff_len )
{
memmove ( & data - > buff [ data - > buff_pos - 1 ] ,
& data - > buff [ data - > buff_pos ] ,
sizeof ( wchar_t ) * ( data - > buff_len - data - > buff_pos + 1 ) ) ;
}
data - > buff_pos - - ;
data - > buff_len - - ;
2006-10-02 02:02:58 +10:00
data - > buff [ data - > buff_len ] = 0 ;
2005-09-20 23:26:39 +10:00
2006-10-17 01:32:26 +10:00
reader_super_highlight_me_plenty ( data - > buff_pos ,
2005-09-20 23:26:39 +10:00
0 ) ;
2006-10-02 06:54:23 +10:00
repaint ( ) ;
2006-10-02 02:02:58 +10:00
2005-09-20 23:26:39 +10:00
}
/**
Insert the character into the command line buffer and print it to
the screen using syntax highlighting , etc .
*/
static int insert_char ( int c )
{
if ( ! check_size ( ) )
return 0 ;
/* Insert space for extra character at the right position */
if ( data - > buff_pos < data - > buff_len )
{
memmove ( & data - > buff [ data - > buff_pos + 1 ] ,
& data - > buff [ data - > buff_pos ] ,
sizeof ( wchar_t ) * ( data - > buff_len - data - > buff_pos ) ) ;
}
/* Set character */
data - > buff [ data - > buff_pos ] = c ;
/* Update lengths, etc */
data - > buff_pos + + ;
data - > buff_len + + ;
data - > buff [ data - > buff_len ] = ' \0 ' ;
/* Syntax highlight */
2006-10-17 01:32:26 +10:00
reader_super_highlight_me_plenty ( data - > buff_pos - 1 ,
2005-09-20 23:26:39 +10:00
0 ) ;
2006-10-02 06:54:23 +10:00
repaint ( ) ;
2006-10-02 02:02:58 +10:00
2005-09-20 23:26:39 +10:00
return 1 ;
}
/**
Insert the characters of the string into the command line buffer
and print them to the screen using syntax highlighting , etc .
*/
static int insert_str ( wchar_t * str )
{
2005-10-14 00:08:33 +10:00
int len = wcslen ( str ) ;
2006-10-02 02:02:58 +10:00
int old_len = data - > buff_len ;
2006-10-12 23:27:32 +10:00
assert ( data - > buff_pos > = 0 ) ;
assert ( data - > buff_pos < = data - > buff_len ) ;
assert ( len > = 0 ) ;
2006-10-02 02:02:58 +10:00
data - > buff_len + = len ;
check_size ( ) ;
/* Insert space for extra characters at the right position */
if ( data - > buff_pos < old_len )
2005-10-14 00:08:33 +10:00
{
2006-10-02 02:02:58 +10:00
memmove ( & data - > buff [ data - > buff_pos + len ] ,
& data - > buff [ data - > buff_pos ] ,
sizeof ( wchar_t ) * ( data - > buff_len - data - > buff_pos ) ) ;
2005-10-14 00:08:33 +10:00
}
2006-10-02 02:02:58 +10:00
memmove ( & data - > buff [ data - > buff_pos ] , str , sizeof ( wchar_t ) * len ) ;
data - > buff_pos + = len ;
data - > buff [ data - > buff_len ] = ' \0 ' ;
/* Syntax highlight */
2006-10-17 01:32:26 +10:00
reader_super_highlight_me_plenty ( data - > buff_pos - 1 ,
2006-10-02 02:02:58 +10:00
0 ) ;
/* repaint */
2006-10-02 06:54:23 +10:00
repaint ( ) ;
2005-09-20 23:26:39 +10:00
return 1 ;
}
/**
Calculate the length of the common prefix substring of two strings .
*/
static int comp_len ( wchar_t * a , wchar_t * b )
{
int i ;
for ( i = 0 ;
a [ i ] ! = ' \0 ' & & b [ i ] ! = ' \0 ' & & a [ i ] = = b [ i ] ;
i + + )
;
return i ;
}
/**
Find the outermost quoting style of current token . Returns 0 if token is not quoted .
*/
static wchar_t get_quote ( wchar_t * cmd , int l )
{
int i = 0 ;
wchar_t res = 0 ;
while ( 1 )
{
if ( ! cmd [ i ] )
break ;
2006-09-15 01:18:21 +10:00
if ( cmd [ i ] = = L ' \\ ' )
2005-09-20 23:26:39 +10:00
{
2006-09-15 01:18:21 +10:00
i + + ;
if ( ! cmd [ i ] )
2005-09-20 23:26:39 +10:00
break ;
2006-09-15 01:18:21 +10:00
i + + ;
2005-09-20 23:26:39 +10:00
}
else
2006-09-15 01:18:21 +10:00
{
if ( cmd [ i ] = = L ' \' ' | | cmd [ i ] = = L ' \" ' )
{
const wchar_t * end = quote_end ( & cmd [ i ] ) ;
//fwprintf( stderr, L"Jump %d\n", end-cmd );
if ( ( end = = 0 ) | | ( ! * end ) | | ( end - cmd > l ) )
{
res = cmd [ i ] ;
break ;
}
i = end - cmd + 1 ;
}
else
i + + ;
}
2005-09-20 23:26:39 +10:00
}
2006-09-15 01:18:21 +10:00
2005-09-20 23:26:39 +10:00
return res ;
}
/**
Calculates information on the parameter at the specified index .
\ param cmd The command to be analyzed
\ param pos An index in the string which is inside the parameter
\ param quote If not 0 , store the type of quote this parameter has , can be either ' , " or \\ 0, meaning the string is not quoted.
\ param offset If not 0 , get_param will store a pointer to the beginning of the parameter .
2005-12-04 11:56:13 +10:00
\ param string If not 0 , get_parm will store a copy of the parameter string as returned by the tokenizer .
2005-09-20 23:26:39 +10:00
\ param type If not 0 , get_param will store the token type as returned by tok_last .
*/
static void get_param ( wchar_t * cmd ,
int pos ,
wchar_t * quote ,
wchar_t * * offset ,
wchar_t * * string ,
int * type )
{
int prev_pos = 0 ;
wchar_t last_quote = ' \0 ' ;
int unfinished ;
tokenizer tok ;
tok_init ( & tok , cmd , TOK_ACCEPT_UNFINISHED ) ;
for ( ; tok_has_next ( & tok ) ; tok_next ( & tok ) )
{
if ( tok_get_pos ( & tok ) > pos )
break ;
if ( tok_last_type ( & tok ) = = TOK_STRING )
last_quote = get_quote ( tok_last ( & tok ) ,
pos - tok_get_pos ( & tok ) ) ;
if ( type ! = 0 )
* type = tok_last_type ( & tok ) ;
if ( string ! = 0 )
wcscpy ( * string , tok_last ( & tok ) ) ;
2005-12-04 11:56:13 +10:00
2005-09-20 23:26:39 +10:00
prev_pos = tok_get_pos ( & tok ) ;
}
tok_destroy ( & tok ) ;
wchar_t c = cmd [ pos ] ;
cmd [ pos ] = 0 ;
int cmdlen = wcslen ( cmd ) ;
unfinished = ( cmdlen = = 0 ) ;
if ( ! unfinished )
{
unfinished = ( quote ! = 0 ) ;
if ( ! unfinished )
{
if ( wcschr ( L " \t \n \r " , cmd [ cmdlen - 1 ] ) ! = 0 )
{
if ( ( cmdlen = = 1 ) | | ( cmd [ cmdlen - 2 ] ! = L ' \\ ' ) )
{
unfinished = 1 ;
}
}
}
}
if ( quote )
* quote = last_quote ;
if ( offset ! = 0 )
{
if ( ! unfinished )
{
while ( ( cmd [ prev_pos ] ! = 0 ) & & ( wcschr ( L " ;| " , cmd [ prev_pos ] ) ! = 0 ) )
prev_pos + + ;
* offset = cmd + prev_pos ;
}
else
{
* offset = cmd + pos ;
}
}
cmd [ pos ] = c ;
}
/**
Insert the string at the current cursor position . The function
checks if the string is quoted or not and correctly escapes the
string .
\ param val the string to insert
\ param is_complete Whether this completion is the whole string or
just the common prefix of several completions . If the former , end by
printing a space ( and an end quote if the parameter is quoted ) .
*/
static void completion_insert ( wchar_t * val , int is_complete )
{
wchar_t * replaced ;
wchar_t quote ;
get_param ( data - > buff ,
data - > buff_pos ,
& quote ,
0 , 0 , 0 ) ;
if ( quote = = L ' \0 ' )
{
2006-02-07 01:15:52 +10:00
replaced = escape ( val , 1 ) ;
2005-09-20 23:26:39 +10:00
}
else
{
int unescapable = 0 ;
wchar_t * pin , * pout ;
replaced = pout =
malloc ( sizeof ( wchar_t ) * ( wcslen ( val ) + 1 ) ) ;
for ( pin = val ; * pin ; pin + + )
{
switch ( * pin )
{
case L ' \n ' :
case L ' \t ' :
case L ' \b ' :
case L ' \r ' :
unescapable = 1 ;
break ;
default :
* pout + + = * pin ;
break ;
}
}
if ( unescapable )
{
free ( replaced ) ;
2006-02-07 01:15:52 +10:00
wchar_t * tmp = escape ( val , 1 ) ;
2005-09-20 23:26:39 +10:00
replaced = wcsdupcat ( L " " , tmp ) ;
free ( tmp ) ;
replaced [ 0 ] = quote ;
}
else
* pout = 0 ;
}
if ( insert_str ( replaced ) )
{
if ( is_complete ) /* Print trailing space since this is the only completion */
{
if ( ( quote ) & &
( data - > buff [ data - > buff_pos ] ! = quote ) ) /* This is a quoted parameter, first print a quote */
{
insert_char ( quote ) ;
}
insert_char ( L ' ' ) ;
}
}
free ( replaced ) ;
}
/**
Run the fish_pager command to display the completion list , and
insert the result into the backbuffer .
*/
static void run_pager ( wchar_t * prefix , int is_quoted , array_list_t * comp )
{
int i ;
string_buffer_t cmd ;
2006-08-13 11:46:02 +10:00
string_buffer_t msg ;
2005-09-20 23:26:39 +10:00
wchar_t * prefix_esc ;
2006-08-13 11:46:02 +10:00
char * foo ;
2005-09-20 23:26:39 +10:00
if ( ! prefix | | ( wcslen ( prefix ) = = 0 ) )
prefix_esc = wcsdup ( L " \" \" " ) ;
else
2005-10-07 20:36:51 +10:00
prefix_esc = escape ( prefix , 1 ) ;
2005-09-20 23:26:39 +10:00
sb_init ( & cmd ) ;
2006-08-13 11:46:02 +10:00
sb_init ( & msg ) ;
2006-01-31 05:53:10 +10:00
sb_printf ( & cmd ,
L " fish_pager %d %ls " ,
2006-02-15 12:49:00 +10:00
// L"valgrind --track-fds=yes --log-file=pager.txt --leak-check=full ./fish_pager %d %ls",
2006-01-31 05:53:10 +10:00
is_quoted ,
2005-09-20 23:26:39 +10:00
prefix_esc ) ;
2006-01-31 05:53:10 +10:00
2005-09-20 23:26:39 +10:00
free ( prefix_esc ) ;
2006-01-31 05:53:10 +10:00
2006-08-13 11:46:02 +10:00
io_data_t * in = io_buffer_create ( 1 ) ;
2005-09-20 23:26:39 +10:00
for ( i = 0 ; i < al_get_count ( comp ) ; i + + )
{
2006-10-02 02:02:58 +10:00
wchar_t * el = escape ( ( wchar_t * ) al_get ( comp , i ) , 1 ) ;
2006-08-13 11:46:02 +10:00
sb_printf ( & msg , L " %ls \n " , el ) ;
2006-08-22 23:58:15 +10:00
free ( el ) ;
2005-09-20 23:26:39 +10:00
}
2006-08-13 11:46:02 +10:00
foo = wcs2str ( ( wchar_t * ) msg . buff ) ;
b_append ( in - > param2 . out_buffer , foo , strlen ( foo ) ) ;
free ( foo ) ;
2006-01-31 05:53:10 +10:00
term_donate ( ) ;
2006-08-13 11:46:02 +10:00
io_data_t * out = io_buffer_create ( 0 ) ;
out - > next = in ;
out - > fd = 1 ;
2005-09-20 23:26:39 +10:00
eval ( ( wchar_t * ) cmd . buff , out , TOP ) ;
2006-01-31 05:53:10 +10:00
term_steal ( ) ;
2005-09-21 00:51:00 +10:00
2005-10-08 21:20:51 +10:00
io_buffer_read ( out ) ;
2006-01-31 05:53:10 +10:00
2005-09-25 05:31:17 +10:00
sb_destroy ( & cmd ) ;
2006-08-13 11:46:02 +10:00
sb_destroy ( & msg ) ;
2006-01-31 05:53:10 +10:00
int nil = 0 ;
2005-10-12 05:31:16 +10:00
b_append ( out - > param2 . out_buffer , & nil , 1 ) ;
2006-01-31 05:53:10 +10:00
2005-09-20 23:26:39 +10:00
wchar_t * tmp ;
2005-10-12 05:31:16 +10:00
wchar_t * str = str2wcs ( ( char * ) out - > param2 . out_buffer - > buff ) ;
2006-01-31 05:53:10 +10:00
2005-09-20 23:26:39 +10:00
if ( str )
{
for ( tmp = str + wcslen ( str ) - 1 ; tmp > = str ; tmp - - )
{
input_unreadch ( * tmp ) ;
2006-01-31 05:53:10 +10:00
}
2005-09-20 23:26:39 +10:00
free ( str ) ;
}
2006-01-31 05:53:10 +10:00
2006-08-13 11:46:02 +10:00
2005-10-08 21:20:51 +10:00
io_buffer_destroy ( out ) ;
2006-08-13 11:46:02 +10:00
io_buffer_destroy ( in ) ;
2006-01-31 05:53:10 +10:00
2005-09-20 23:26:39 +10:00
}
/**
Handle the list of completions . This means the following :
2006-02-16 23:40:25 +10:00
2005-09-20 23:26:39 +10:00
- If the list is empty , flash the terminal .
- If the list contains one element , write the whole element , and if
the element does not end on a ' / ' , ' @ ' , ' : ' , or a ' = ' , also write a trailing
space .
- If the list contains multiple elements with a common prefix , write
the prefix .
2006-10-17 01:32:26 +10:00
- If the list contains multiple elements without .
a common prefix , call run_pager to display a list of completions . Depending on terminal size and the length of the list , run_pager may either show less than a screenfull and exit or use an interactive pager to allow the user to scroll through the completions .
2006-02-16 23:36:32 +10:00
2005-09-20 23:26:39 +10:00
\ param comp the list of completion strings
*/
static int handle_completions ( array_list_t * comp )
{
int i ;
2006-02-16 23:36:32 +10:00
2005-09-20 23:26:39 +10:00
if ( al_get_count ( comp ) = = 0 )
{
if ( flash_screen ! = 0 )
writembs ( flash_screen ) ;
return 0 ;
}
else if ( al_get_count ( comp ) = = 1 )
{
wchar_t * comp_str = wcsdup ( ( wchar_t * ) al_get ( comp , 0 ) ) ;
wchar_t * woot = wcschr ( comp_str , COMPLETE_SEP ) ;
if ( woot ! = 0 )
* woot = L ' \0 ' ;
completion_insert ( comp_str ,
( wcslen ( comp_str ) = = 0 ) | |
( wcschr ( L " /=@: " ,
comp_str [ wcslen ( comp_str ) - 1 ] ) = = 0 ) ) ;
free ( comp_str ) ;
return 1 ;
}
else
{
wchar_t * base = wcsdup ( ( wchar_t * ) al_get ( comp , 0 ) ) ;
int len = wcslen ( base ) ;
for ( i = 1 ; i < al_get_count ( comp ) ; i + + )
{
int new_len = comp_len ( base , ( wchar_t * ) al_get ( comp , i ) ) ;
len = new_len < len ? new_len : len ;
}
if ( len > 0 )
{
base [ len ] = L ' \0 ' ;
wchar_t * woot = wcschr ( base , COMPLETE_SEP ) ;
if ( woot ! = 0 )
* woot = L ' \0 ' ;
completion_insert ( base , 0 ) ;
}
else
{
/*
2005-12-31 02:29:19 +10:00
There is no common prefix in the completions , and show_list
is true , so we print the list
2005-09-20 23:26:39 +10:00
*/
int len ;
wchar_t * prefix ;
wchar_t * prefix_start ;
get_param ( data - > buff ,
data - > buff_pos ,
0 ,
& prefix_start ,
0 ,
0 ) ;
2005-12-09 12:47:18 +10:00
len = & data - > buff [ data - > buff_pos ] - prefix_start + 1 ;
2006-01-31 05:53:10 +10:00
2005-09-20 23:26:39 +10:00
if ( len < = PREFIX_MAX_LEN )
{
prefix = malloc ( sizeof ( wchar_t ) * ( len + 1 ) ) ;
2005-12-04 11:56:13 +10:00
wcslcpy ( prefix , prefix_start , len ) ;
2005-09-20 23:26:39 +10:00
prefix [ len ] = L ' \0 ' ;
}
else
{
wchar_t tmp [ 2 ] =
2005-12-31 02:29:19 +10:00
{
ellipsis_char ,
0
}
2005-09-20 23:26:39 +10:00
;
2006-01-31 05:53:10 +10:00
2005-09-20 23:26:39 +10:00
prefix = wcsdupcat ( tmp ,
2005-12-09 12:47:18 +10:00
prefix_start + ( len - PREFIX_MAX_LEN ) ) ;
2005-12-04 11:56:13 +10:00
prefix [ PREFIX_MAX_LEN ] = 0 ;
2006-01-31 05:53:10 +10:00
2005-09-20 23:26:39 +10:00
}
{
int is_quoted ;
wchar_t quote ;
get_param ( data - > buff , data - > buff_pos , & quote , 0 , 0 , 0 ) ;
is_quoted = ( quote ! = L ' \0 ' ) ;
2006-09-25 03:57:23 +10:00
2006-10-12 23:27:32 +10:00
write ( 1 , " \n " , 1 ) ;
2006-09-25 03:57:23 +10:00
2005-09-20 23:26:39 +10:00
run_pager ( prefix , is_quoted , comp ) ;
}
free ( prefix ) ;
2006-10-04 20:27:06 +10:00
s_reset ( & data - > screen ) ;
2006-10-02 06:54:23 +10:00
repaint ( ) ;
2005-09-20 23:26:39 +10:00
}
free ( base ) ;
return len ;
}
}
/**
Initialize data for interactive use
*/
static void reader_interactive_init ( )
{
/* See if we are running interactively. */
pid_t shell_pgid ;
input_init ( ) ;
kill_init ( ) ;
shell_pgid = getpgrp ( ) ;
2006-02-05 23:12:53 +10:00
/*
2006-10-17 01:32:26 +10:00
This should enable job control on fish , even if our parent process did
2006-02-05 23:12:53 +10:00
not enable it for us .
*/
2005-09-20 23:26:39 +10:00
/* Loop until we are in the foreground. */
while ( tcgetpgrp ( 0 ) ! = shell_pgid )
{
2006-10-05 07:42:04 +10:00
killpg ( shell_pgid , SIGTTIN ) ;
2005-09-20 23:26:39 +10:00
}
/* Put ourselves in our own process group. */
shell_pgid = getpid ( ) ;
if ( getpgrp ( ) ! = shell_pgid )
{
if ( setpgid ( shell_pgid , shell_pgid ) < 0 )
{
debug ( 1 ,
2006-01-04 22:51:02 +10:00
_ ( L " Couldn't put the shell in its own process group " ) ) ;
2005-09-20 23:26:39 +10:00
wperror ( L " setpgid " ) ;
exit ( 1 ) ;
}
}
/* Grab control of the terminal. */
if ( tcsetpgrp ( STDIN_FILENO , shell_pgid ) )
{
debug ( 1 ,
2006-01-04 22:51:02 +10:00
_ ( L " Couldn't grab control of terminal " ) ) ;
2005-09-20 23:26:39 +10:00
wperror ( L " tcsetpgrp " ) ;
exit ( 1 ) ;
}
2006-01-31 05:53:10 +10:00
common_handle_winch ( 0 ) ;
2005-09-20 23:26:39 +10:00
if ( tcsetattr ( 0 , TCSANOW , & shell_modes ) ) /* set the new modes */
{
wperror ( L " tcsetattr " ) ;
exit ( 1 ) ;
}
2006-02-05 23:12:53 +10:00
/*
We need to know our own pid so we ' ll later know if we are a
fork
*/
2005-09-20 23:26:39 +10:00
original_pid = getpid ( ) ;
env_set ( L " _ " , L " fish " , ENV_GLOBAL ) ;
}
/**
Destroy data for interactive use
*/
static void reader_interactive_destroy ( )
{
kill_destroy ( ) ;
writestr ( L " \n " ) ;
set_color ( FISH_COLOR_RESET , FISH_COLOR_RESET ) ;
input_destroy ( ) ;
}
void reader_sanity_check ( )
{
if ( is_interactive )
{
if ( ! ( data - > buff_pos < = data - > buff_len ) )
sanity_lose ( ) ;
if ( ! ( data - > buff_len = = wcslen ( data - > buff ) ) )
sanity_lose ( ) ;
}
}
void reader_replace_current_token ( wchar_t * new_token )
{
2006-06-14 23:22:40 +10:00
wchar_t * begin , * end ;
2005-09-20 23:26:39 +10:00
string_buffer_t sb ;
int new_pos ;
/*
Find current token
*/
2006-01-31 02:51:50 +10:00
parse_util_token_extent ( data - > buff , data - > buff_pos , & begin , & end , 0 , 0 ) ;
2005-09-20 23:26:39 +10:00
if ( ! begin | | ! end )
return ;
/*
Make new string
*/
sb_init ( & sb ) ;
sb_append_substring ( & sb , data - > buff , begin - data - > buff ) ;
sb_append ( & sb , new_token ) ;
sb_append ( & sb , end ) ;
new_pos = ( begin - data - > buff ) + wcslen ( new_token ) ;
reader_set_buffer ( ( wchar_t * ) sb . buff , new_pos ) ;
sb_destroy ( & sb ) ;
}
/**
Set the specified string from the history as the current buffer . Do
not modify prefix_width .
*/
static void handle_history ( const wchar_t * new_str )
{
2006-10-26 06:36:08 +10:00
if ( new_str )
{
data - > buff_len = wcslen ( new_str ) ;
check_size ( ) ;
wcscpy ( data - > buff , new_str ) ;
data - > buff_pos = wcslen ( data - > buff ) ;
reader_super_highlight_me_plenty ( data - > buff_pos , 0 ) ;
2005-09-20 23:26:39 +10:00
2006-10-26 06:36:08 +10:00
repaint ( ) ;
}
2005-09-20 23:26:39 +10:00
}
/**
Check if the specified string is contained in the list , using
wcscmp as a comparison function
*/
2006-01-31 05:53:10 +10:00
static int contains ( const wchar_t * needle ,
2005-09-20 23:26:39 +10:00
array_list_t * haystack )
{
int i ;
for ( i = 0 ; i < al_get_count ( haystack ) ; i + + )
{
if ( ! wcscmp ( needle , al_get ( haystack , i ) ) )
return 1 ;
}
return 0 ;
2006-01-31 05:53:10 +10:00
2005-09-20 23:26:39 +10:00
}
2006-01-24 06:40:14 +10:00
/**
Reset the data structures associated with the token search
*/
2005-09-25 02:31:22 +10:00
static void reset_token_history ( )
{
2006-06-14 23:22:40 +10:00
wchar_t * begin , * end ;
2005-09-25 02:31:22 +10:00
2006-01-31 02:51:50 +10:00
parse_util_token_extent ( data - > buff , data - > buff_pos , & begin , & end , 0 , 0 ) ;
2005-09-25 02:31:22 +10:00
if ( begin )
{
wcslcpy ( data - > search_buff , begin , end - begin + 1 ) ;
}
else
data - > search_buff [ 0 ] = 0 ;
2006-01-31 05:53:10 +10:00
2005-09-25 02:31:22 +10:00
data - > token_history_pos = - 1 ;
data - > search_pos = 0 ;
2006-06-13 07:47:42 +10:00
al_foreach ( & data - > search_prev , & free ) ;
2005-09-25 02:31:22 +10:00
al_truncate ( & data - > search_prev , 0 ) ;
al_push ( & data - > search_prev , wcsdup ( data - > search_buff ) ) ;
}
2005-09-20 23:26:39 +10:00
/**
Handles a token search command .
\ param forward if the search should be forward or reverse
\ param reset whether the current token should be made the new search token
*/
static void handle_token_history ( int forward , int reset )
{
wchar_t * str = 0 ;
int current_pos ;
tokenizer tok ;
2005-12-11 14:30:17 +10:00
if ( reset )
2005-09-20 23:26:39 +10:00
{
/*
Start a new token search using the current token
*/
2005-09-25 02:31:22 +10:00
reset_token_history ( ) ;
2006-01-31 05:53:10 +10:00
2005-09-20 23:26:39 +10:00
}
2005-12-13 04:30:55 +10:00
2005-09-20 23:26:39 +10:00
current_pos = data - > token_history_pos ;
if ( forward | | data - > search_pos < ( al_get_count ( & data - > search_prev ) - 1 ) )
{
if ( forward )
{
if ( data - > search_pos > 0 )
{
data - > search_pos - - ;
}
str = ( wchar_t * ) al_get ( & data - > search_prev , data - > search_pos ) ;
}
else
{
data - > search_pos + + ;
str = ( wchar_t * ) al_get ( & data - > search_prev , data - > search_pos ) ;
}
reader_replace_current_token ( str ) ;
2006-10-17 01:32:26 +10:00
reader_super_highlight_me_plenty ( data - > buff_pos , 0 ) ;
2006-10-02 06:54:23 +10:00
repaint ( ) ;
2005-09-20 23:26:39 +10:00
}
else
{
if ( current_pos = = - 1 )
{
/*
Move to previous line
*/
free ( ( void * ) data - > token_history_buff ) ;
data - > token_history_buff = wcsdup ( history_prev_match ( L " " ) ) ;
current_pos = wcslen ( data - > token_history_buff ) ;
}
if ( ! wcslen ( data - > token_history_buff ) )
{
/*
We have reached the end of the history - check if the
history already contains the search string itself , if so
return , otherwise add it .
*/
2005-12-13 04:30:55 +10:00
2005-09-20 23:26:39 +10:00
const wchar_t * last = al_get ( & data - > search_prev , al_get_count ( & data - > search_prev ) - 1 ) ;
if ( wcscmp ( last , data - > search_buff ) )
{
str = wcsdup ( data - > search_buff ) ;
}
else
{
2006-01-31 05:53:10 +10:00
return ;
}
2005-09-20 23:26:39 +10:00
}
else
{
2005-12-13 04:30:55 +10:00
2006-01-31 05:53:10 +10:00
debug ( 3 , L " new '%ls' " , data - > token_history_buff ) ;
2005-12-13 04:30:55 +10:00
2005-09-20 23:26:39 +10:00
for ( tok_init ( & tok , data - > token_history_buff , TOK_ACCEPT_UNFINISHED ) ;
tok_has_next ( & tok ) ;
tok_next ( & tok ) )
{
switch ( tok_last_type ( & tok ) )
{
case TOK_STRING :
{
if ( wcsstr ( tok_last ( & tok ) , data - > search_buff ) )
{
2005-12-13 04:30:55 +10:00
debug ( 3 , L " Found token at pos %d \n " , tok_get_pos ( & tok ) ) ;
2005-09-20 23:26:39 +10:00
if ( tok_get_pos ( & tok ) > = current_pos )
{
break ;
}
2005-12-13 04:30:55 +10:00
debug ( 3 , L " ok pos " ) ;
2005-09-20 23:26:39 +10:00
if ( ! contains ( tok_last ( & tok ) , & data - > search_prev ) )
{
free ( str ) ;
data - > token_history_pos = tok_get_pos ( & tok ) ;
str = wcsdup ( tok_last ( & tok ) ) ;
}
2006-01-31 05:53:10 +10:00
2005-09-20 23:26:39 +10:00
}
}
}
}
tok_destroy ( & tok ) ;
}
2006-01-31 05:53:10 +10:00
2005-09-20 23:26:39 +10:00
if ( str )
{
reader_replace_current_token ( str ) ;
2006-10-17 01:32:26 +10:00
reader_super_highlight_me_plenty ( data - > buff_pos , 0 ) ;
2006-10-02 06:54:23 +10:00
repaint ( ) ;
2005-09-20 23:26:39 +10:00
al_push ( & data - > search_prev , str ) ;
data - > search_pos = al_get_count ( & data - > search_prev ) - 1 ;
}
else
{
data - > token_history_pos = - 1 ;
handle_token_history ( 0 , 0 ) ;
}
}
}
/**
Move buffer position one word or erase one word . This function
updates both the internal buffer and the screen . It is used by
M - left , M - right and ^ W to do block movement or block erase .
2006-01-31 05:53:10 +10:00
2005-09-20 23:26:39 +10:00
\ param dir Direction to move / erase . 0 means move left , 1 means move right .
\ param erase Whether to erase the characters along the way or only move past them .
2006-10-13 05:30:00 +10:00
\ param do_append if erase is true , this flag decides if the new kill item should be appended to the previous kill item .
2005-09-20 23:26:39 +10:00
*/
2006-10-13 05:30:00 +10:00
static void move_word ( int dir , int erase , int new )
2005-09-20 23:26:39 +10:00
{
int end_buff_pos = data - > buff_pos ;
int step = dir ? 1 : - 1 ;
2006-10-13 02:13:17 +10:00
/*
Return if we are already at the edge
*/
if ( ! dir & & data - > buff_pos = = 0 )
{
return ;
}
if ( dir & & data - > buff_pos = = data - > buff_len )
{
return ;
}
2006-08-31 10:45:25 +10:00
/*
If we are beyond the last character and moving left , start by
moving one step , since otehrwise we ' ll start on the \ 0 , which
should be ignored .
*/
if ( ! dir & & ( end_buff_pos = = data - > buff_len ) )
{
if ( ! end_buff_pos )
return ;
end_buff_pos - - ;
}
2006-10-13 02:13:17 +10:00
/*
When moving left , ignore the character under the cursor
*/
if ( ! dir )
{
end_buff_pos + = 2 * step ;
}
2006-10-17 01:32:26 +10:00
2006-09-17 09:05:32 +10:00
/*
Remove all whitespace characters before finding a word
*/
2006-08-31 10:45:25 +10:00
while ( 1 )
2005-09-20 23:26:39 +10:00
{
2006-08-31 10:45:25 +10:00
wchar_t c ;
2005-09-20 23:26:39 +10:00
if ( ! dir )
{
2006-10-17 00:39:12 +10:00
if ( end_buff_pos < = 0 )
2005-09-20 23:26:39 +10:00
break ;
}
else
{
2006-10-17 00:39:12 +10:00
if ( end_buff_pos > = data - > buff_len )
2005-09-20 23:26:39 +10:00
break ;
}
2006-09-17 09:05:32 +10:00
/*
Always eat at least one character
*/
if ( end_buff_pos ! = data - > buff_pos )
2006-08-31 10:45:25 +10:00
{
2006-09-17 09:05:32 +10:00
c = data - > buff [ end_buff_pos ] ;
if ( ! iswspace ( c ) )
{
break ;
}
2006-08-31 10:45:25 +10:00
}
end_buff_pos + = step ;
2006-09-17 09:05:32 +10:00
2006-08-31 10:45:25 +10:00
}
2006-09-17 09:05:32 +10:00
/*
Remove until we find a character that is not alphanumeric
*/
2006-08-31 10:45:25 +10:00
while ( 1 )
{
wchar_t c ;
if ( ! dir )
{
2006-10-17 00:39:12 +10:00
if ( end_buff_pos < = 0 )
2006-08-31 10:45:25 +10:00
break ;
}
else
{
2006-10-17 00:39:12 +10:00
if ( end_buff_pos > = data - > buff_len )
2006-08-31 10:45:25 +10:00
break ;
2005-09-20 23:26:39 +10:00
}
2006-10-13 02:13:17 +10:00
2006-08-31 10:45:25 +10:00
c = data - > buff [ end_buff_pos ] ;
2006-10-13 02:13:17 +10:00
2006-08-31 10:45:25 +10:00
if ( ! iswalnum ( c ) )
{
/*
2006-10-13 02:13:17 +10:00
Don ' t gobble the boundary character when moving to the
right
2006-08-31 10:45:25 +10:00
*/
2006-10-13 02:13:17 +10:00
if ( ! dir )
2006-08-31 10:45:25 +10:00
end_buff_pos - = step ;
break ;
}
end_buff_pos + = step ;
2005-09-20 23:26:39 +10:00
}
2006-10-13 02:13:17 +10:00
/*
Make sure we move at least one character
*/
if ( end_buff_pos = = data - > buff_pos )
{
end_buff_pos + = step ;
}
2006-10-17 00:39:12 +10:00
/*
Make sure we don ' t move beyond begining or end of buffer
*/
end_buff_pos = maxi ( 0 , mini ( end_buff_pos , data - > buff_len ) ) ;
2006-10-17 01:32:26 +10:00
2006-10-13 02:13:17 +10:00
2005-09-20 23:26:39 +10:00
if ( erase )
{
int remove_count = abs ( data - > buff_pos - end_buff_pos ) ;
int first_char = mini ( data - > buff_pos , end_buff_pos ) ;
// fwprintf( stderr, L"Remove from %d to %d\n", first_char, first_char+remove_count );
2006-10-13 05:30:00 +10:00
reader_kill ( data - > buff + first_char , remove_count , dir ? KILL_APPEND : KILL_PREPEND , new ) ;
2005-09-20 23:26:39 +10:00
}
else
{
2006-10-02 02:02:58 +10:00
data - > buff_pos = end_buff_pos ;
2006-10-02 06:54:23 +10:00
repaint ( ) ;
2005-09-20 23:26:39 +10:00
}
}
wchar_t * reader_get_buffer ( )
{
return data ? data - > buff : 0 ;
}
void reader_set_buffer ( wchar_t * b , int p )
{
int l = wcslen ( b ) ;
if ( ! data )
return ;
data - > buff_len = l ;
check_size ( ) ;
2006-10-05 07:39:48 +10:00
if ( data - > buff ! = b )
wcscpy ( data - > buff , b ) ;
2005-09-20 23:26:39 +10:00
if ( p > = 0 )
{
2006-10-05 07:39:48 +10:00
data - > buff_pos = mini ( p , l ) ;
2005-09-20 23:26:39 +10:00
}
else
{
data - > buff_pos = l ;
}
2006-10-17 01:32:26 +10:00
reader_super_highlight_me_plenty ( data - > buff_pos ,
2005-09-20 23:26:39 +10:00
0 ) ;
}
int reader_get_cursor_pos ( )
{
if ( ! data )
return - 1 ;
return data - > buff_pos ;
}
2006-06-22 00:03:44 +10:00
void reader_run_command ( const wchar_t * cmd )
2005-09-20 23:26:39 +10:00
{
2006-01-31 05:53:10 +10:00
2005-09-20 23:26:39 +10:00
wchar_t * ft ;
ft = tok_first ( cmd ) ;
if ( ft ! = 0 )
env_set ( L " _ " , ft , ENV_GLOBAL ) ;
free ( ft ) ;
reader_write_title ( ) ;
2006-01-31 05:53:10 +10:00
2005-09-20 23:26:39 +10:00
term_donate ( ) ;
2006-01-31 05:53:10 +10:00
2005-10-09 21:48:16 +10:00
eval ( cmd , 0 , TOP ) ;
2005-10-12 05:23:43 +10:00
job_reap ( 1 ) ;
2006-01-31 05:53:10 +10:00
2005-09-20 23:26:39 +10:00
term_steal ( ) ;
2006-05-27 23:49:18 +10:00
env_set ( L " _ " , program_name , ENV_GLOBAL ) ;
2005-09-20 23:26:39 +10:00
# ifdef HAVE__PROC_SELF_STAT
proc_update_jiffies ( ) ;
# endif
2006-01-31 05:53:10 +10:00
2005-09-20 23:26:39 +10:00
}
/**
Test if the given shell command contains errors . Uses parser_test
for testing .
*/
static int shell_test ( wchar_t * b )
{
2006-10-07 10:56:25 +10:00
int res = parser_test ( b , 0 , 0 , 0 ) ;
2006-10-02 02:02:58 +10:00
if ( res & PARSER_TEST_ERROR )
2006-05-27 21:14:56 +10:00
{
2006-06-02 12:15:17 +10:00
string_buffer_t sb ;
sb_init ( & sb ) ;
2006-10-02 02:02:58 +10:00
int tmp [ 1 ] ;
2006-10-07 10:56:25 +10:00
int tmp2 [ 1 ] ;
2006-10-02 02:02:58 +10:00
2006-10-07 10:56:25 +10:00
s_write ( & data - > screen , L " " , L " " , tmp , tmp2 , 0 ) ;
2006-06-02 12:15:17 +10:00
2006-10-07 10:56:25 +10:00
parser_test ( b , 0 , & sb , L " fish " ) ;
2006-06-02 12:15:17 +10:00
fwprintf ( stderr , L " %ls " , sb . buff ) ;
sb_destroy ( & sb ) ;
2006-05-27 21:14:56 +10:00
}
2006-10-02 02:02:58 +10:00
return res ;
2005-09-20 23:26:39 +10:00
}
/**
Test if the given string contains error . Since this is the error
detection for general purpose , there are no invalid strings , so
this function always returns false .
*/
static int default_test ( wchar_t * b )
{
return 0 ;
}
void reader_push ( wchar_t * name )
{
reader_data_t * n = calloc ( 1 , sizeof ( reader_data_t ) ) ;
n - > name = wcsdup ( name ) ;
n - > next = data ;
2006-10-12 23:27:32 +10:00
sb_init ( & n - > kill_item ) ;
2006-10-02 02:02:58 +10:00
2005-09-20 23:26:39 +10:00
data = n ;
2006-10-02 02:02:58 +10:00
s_init ( & data - > screen ) ;
sb_init ( & data - > prompt_buff ) ;
2005-09-20 23:26:39 +10:00
check_size ( ) ;
data - > buff [ 0 ] = data - > search_buff [ 0 ] = 0 ;
if ( data - > next = = 0 )
{
reader_interactive_init ( ) ;
}
2006-10-17 01:32:26 +10:00
exec_prompt ( ) ;
2005-09-20 23:26:39 +10:00
reader_set_highlight_function ( & highlight_universal ) ;
reader_set_test_function ( & default_test ) ;
reader_set_prompt ( L " " ) ;
history_set_mode ( name ) ;
al_init ( & data - > search_prev ) ;
data - > token_history_buff = 0 ;
}
void reader_pop ( )
{
reader_data_t * n = data ;
if ( data = = 0 )
{
2006-01-04 22:51:02 +10:00
debug ( 0 , _ ( L " Pop null reader block " ) ) ;
2005-09-20 23:26:39 +10:00
sanity_lose ( ) ;
return ;
}
data = data - > next ;
free ( n - > name ) ;
free ( n - > prompt ) ;
free ( n - > buff ) ;
free ( n - > color ) ;
2006-10-07 10:56:25 +10:00
free ( n - > indent ) ;
2005-09-20 23:26:39 +10:00
free ( n - > search_buff ) ;
2006-10-12 23:27:32 +10:00
sb_destroy ( & n - > kill_item ) ;
2006-10-02 02:02:58 +10:00
s_destroy ( & n - > screen ) ;
sb_destroy ( & n - > prompt_buff ) ;
2005-09-20 23:26:39 +10:00
/*
Clean up after history search
*/
2006-06-13 07:47:42 +10:00
al_foreach ( & n - > search_prev , & free ) ;
2006-01-31 05:53:10 +10:00
al_destroy ( & n - > search_prev ) ;
2005-09-20 23:26:39 +10:00
free ( ( void * ) n - > token_history_buff ) ;
2006-01-31 05:53:10 +10:00
2005-09-20 23:26:39 +10:00
free ( n ) ;
2006-01-31 05:53:10 +10:00
2005-09-20 23:26:39 +10:00
if ( data = = 0 )
{
reader_interactive_destroy ( ) ;
}
else
{
history_set_mode ( data - > name ) ;
2006-10-17 01:32:26 +10:00
exec_prompt ( ) ;
2005-09-20 23:26:39 +10:00
}
}
void reader_set_prompt ( wchar_t * new_prompt )
{
free ( data - > prompt ) ;
data - > prompt = wcsdup ( new_prompt ) ;
}
void reader_set_complete_function ( void ( * f ) ( const wchar_t * ,
array_list_t * ) )
{
data - > complete_func = f ;
}
void reader_set_highlight_function ( void ( * f ) ( wchar_t * ,
int * ,
int ,
array_list_t * ) )
{
data - > highlight_func = f ;
}
void reader_set_test_function ( int ( * f ) ( wchar_t * ) )
{
data - > test_func = f ;
}
/**
Call specified external highlighting function and then do search
2006-09-25 03:57:23 +10:00
highlighting . Lastly , clear the background color under the cursor
2006-10-22 19:40:18 +10:00
to avoid repaint issues on terminals where e . g . syntax highligthing
maykes characters under the sursor unreadable .
2006-09-25 03:57:23 +10:00
\ param match_highlight_pos the position to use for bracket matching . This need not be the same as the surrent cursor position
2006-10-17 01:32:26 +10:00
\ param error if non - null , any possible errors in the buffer are further descibed by the strings inserted into the specified arraylist
2005-09-20 23:26:39 +10:00
*/
2006-10-17 01:32:26 +10:00
static void reader_super_highlight_me_plenty ( int match_highlight_pos , array_list_t * error )
2005-09-20 23:26:39 +10:00
{
2006-10-17 01:32:26 +10:00
data - > highlight_func ( data - > buff , data - > color , match_highlight_pos , error ) ;
2006-10-05 07:42:04 +10:00
if ( data - > search_buff & & wcslen ( data - > search_buff ) )
2005-09-20 23:26:39 +10:00
{
2006-09-25 03:57:23 +10:00
wchar_t * match = wcsstr ( data - > buff , data - > search_buff ) ;
2005-09-20 23:26:39 +10:00
if ( match )
{
2006-09-25 03:57:23 +10:00
int start = match - data - > buff ;
2005-09-20 23:26:39 +10:00
int count = wcslen ( data - > search_buff ) ;
int i ;
for ( i = 0 ; i < count ; i + + )
{
2006-10-22 19:40:18 +10:00
data - > color [ start + i ] | = HIGHLIGHT_SEARCH_MATCH < < 16 ;
2005-09-20 23:26:39 +10:00
}
}
}
}
int exit_status ( )
{
if ( is_interactive )
return first_job = = 0 & & data - > end_loop ;
else
return end_loop ;
}
/**
Read interactively . Read input from stdin while providing editing
facilities .
*/
static int read_i ( )
{
reader_push ( L " fish " ) ;
reader_set_complete_function ( & complete ) ;
reader_set_highlight_function ( & highlight_shell ) ;
reader_set_test_function ( & shell_test ) ;
2006-05-14 20:16:23 +10:00
data - > prev_end_loop = 0 ;
2005-09-20 23:26:39 +10:00
while ( ( ! data - > end_loop ) & & ( ! sanity_check ( ) ) )
{
wchar_t * tmp ;
if ( function_exists ( L " fish_prompt " ) )
reader_set_prompt ( L " fish_prompt " ) ;
else
reader_set_prompt ( DEFAULT_PROMPT ) ;
/*
Put buff in temporary string and clear buff , so
that we can handle a call to reader_set_buffer
during evaluation .
*/
2006-01-31 05:53:10 +10:00
2006-05-14 20:16:23 +10:00
tmp = reader_readline ( ) ;
2005-09-20 23:26:39 +10:00
if ( data - > end_loop )
{
2006-04-04 21:27:22 +10:00
job_t * j ;
int has_job = 0 ;
for ( j = first_job ; j ; j = j - > next )
{
if ( ! job_is_completed ( j ) )
{
has_job = 1 ;
break ;
}
}
2006-05-14 20:16:23 +10:00
if ( ! reader_exit_forced ( ) & & ! data - > prev_end_loop & & has_job )
2005-09-20 23:26:39 +10:00
{
2006-01-04 22:51:02 +10:00
writestr ( _ ( L " There are stopped jobs \n " ) ) ;
2006-10-25 20:14:02 +10:00
reader_exit ( 0 , 0 ) ;
2006-05-14 20:16:23 +10:00
data - > prev_end_loop = 1 ;
2006-10-25 20:14:02 +10:00
repaint ( ) ;
2005-09-20 23:26:39 +10:00
}
}
else
{
2006-05-14 20:16:23 +10:00
tmp = wcsdup ( tmp ) ;
data - > buff_pos = data - > buff_len = 0 ;
data - > buff [ data - > buff_len ] = L ' \0 ' ;
reader_run_command ( tmp ) ;
free ( tmp ) ;
data - > prev_end_loop = 0 ;
2005-09-20 23:26:39 +10:00
}
2006-01-18 22:42:48 +10:00
2005-09-20 23:26:39 +10:00
}
reader_pop ( ) ;
return 0 ;
}
2005-10-20 22:06:10 +10:00
/**
Test if there are bytes available for reading on the specified file
descriptor
*/
2005-10-14 00:08:33 +10:00
static int can_read ( int fd )
{
2005-10-25 21:22:47 +10:00
struct timeval can_read_timeout = { 0 , 0 } ;
fd_set fds ;
2006-01-31 05:53:10 +10:00
2005-10-25 21:22:47 +10:00
FD_ZERO ( & fds ) ;
FD_SET ( fd , & fds ) ;
return select ( fd + 1 , & fds , 0 , 0 , & can_read_timeout ) = = 1 ;
2005-10-14 00:08:33 +10:00
}
2005-09-20 23:26:39 +10:00
2005-10-20 21:27:54 +10:00
/**
2005-10-20 22:06:10 +10:00
Test if the specified character is in the private use area that
fish uses to store internal characters
2005-10-20 21:27:54 +10:00
*/
static int wchar_private ( wchar_t c )
{
return ( ( c > = 0xe000 ) & & ( c < = 0xf8ff ) ) ;
}
2005-09-20 23:26:39 +10:00
wchar_t * reader_readline ( )
{
2005-10-12 05:48:31 +10:00
wint_t c ;
2005-09-20 23:26:39 +10:00
int i ;
int last_char = 0 , yank = 0 ;
wchar_t * yank_str ;
array_list_t comp ;
int comp_empty = 1 ;
int finished = 0 ;
struct termios old_modes ;
check_size ( ) ;
data - > search_buff [ 0 ] = data - > buff [ data - > buff_len ] = ' \0 ' ;
al_init ( & comp ) ;
2006-10-02 02:02:58 +10:00
s_reset ( & data - > screen ) ;
2006-10-17 01:32:26 +10:00
exec_prompt ( ) ;
2005-09-20 23:26:39 +10:00
2006-10-17 01:32:26 +10:00
reader_super_highlight_me_plenty ( data - > buff_pos , 0 ) ;
2006-10-02 06:54:23 +10:00
repaint ( ) ;
2005-09-20 23:26:39 +10:00
2006-10-26 06:36:08 +10:00
/* get the current terminal modes. These will be restored when the function returns. */
tcgetattr ( 0 , & old_modes ) ;
2005-12-31 02:29:19 +10:00
if ( tcsetattr ( 0 , TCSANOW , & shell_modes ) ) /* set the new modes */
{
2005-09-20 23:26:39 +10:00
wperror ( L " tcsetattr " ) ;
exit ( 1 ) ;
}
while ( ! finished & & ! data - > end_loop )
{
/*
2005-10-12 05:48:31 +10:00
Sometimes strange input sequences seem to generate a zero
byte . I believe these simply mean a character was pressed
but it should be ignored . ( Example : Trying to add a tilde
( ~ ) to digit )
2005-09-20 23:26:39 +10:00
*/
2005-10-14 00:08:33 +10:00
while ( 1 )
{
c = input_readch ( ) ;
2006-10-05 07:42:04 +10:00
2005-10-20 21:27:54 +10:00
if ( ( ( ! wchar_private ( c ) ) ) & & ( c > 31 ) & & ( c ! = 127 ) )
2005-10-14 00:08:33 +10:00
{
if ( can_read ( 0 ) )
{
wchar_t arr [ READAHEAD_MAX + 1 ] ;
int i ;
2006-01-31 05:53:10 +10:00
2005-10-14 00:08:33 +10:00
memset ( arr , 0 , sizeof ( arr ) ) ;
arr [ 0 ] = c ;
2006-01-31 05:53:10 +10:00
2005-10-14 00:08:33 +10:00
for ( i = 1 ; i < READAHEAD_MAX ; i + + )
{
2006-01-31 05:53:10 +10:00
2005-10-14 00:08:33 +10:00
if ( ! can_read ( 0 ) )
{
c = 0 ;
break ;
}
c = input_readch ( ) ;
2005-10-20 21:27:54 +10:00
if ( ( ! wchar_private ( c ) ) & & ( c > 31 ) & & ( c ! = 127 ) )
2005-10-14 00:08:33 +10:00
{
arr [ i ] = c ;
c = 0 ;
}
else
break ;
}
2006-01-31 05:53:10 +10:00
2005-10-14 00:08:33 +10:00
insert_str ( arr ) ;
2006-01-31 05:53:10 +10:00
2005-10-14 00:08:33 +10:00
}
}
2006-01-31 05:53:10 +10:00
2005-10-14 00:08:33 +10:00
if ( c ! = 0 )
break ;
}
2005-09-20 23:26:39 +10:00
if ( ( last_char = = R_COMPLETE ) & & ( c ! = R_COMPLETE ) & & ( ! comp_empty ) )
{
2006-06-13 07:47:42 +10:00
al_foreach ( & comp , & free ) ;
2005-09-20 23:26:39 +10:00
al_truncate ( & comp , 0 ) ;
comp_empty = 1 ;
}
if ( last_char ! = R_YANK & & last_char ! = R_YANK_POP )
yank = 0 ;
2006-10-05 09:33:12 +10:00
2005-12-13 20:18:59 +10:00
switch ( c )
2005-09-20 23:26:39 +10:00
{
/* go to beginning of line*/
case R_BEGINNING_OF_LINE :
2006-11-01 08:01:49 +10:00
{
while ( data - > buff_pos > 0 & & data - > buff [ data - > buff_pos - 1 ] ! = L ' \n ' )
data - > buff_pos - - ;
repaint ( ) ;
break ;
}
case R_END_OF_LINE :
{
while ( data - > buff [ data - > buff_pos ] & & data - > buff [ data - > buff_pos ] ! = L ' \n ' )
data - > buff_pos + + ;
repaint ( ) ;
break ;
}
case R_BEGINNING_OF_BUFFER :
2005-09-20 23:26:39 +10:00
{
data - > buff_pos = 0 ;
2006-10-02 06:54:23 +10:00
repaint ( ) ;
2005-09-20 23:26:39 +10:00
break ;
}
/* go to EOL*/
2006-11-01 08:01:49 +10:00
case R_END_OF_BUFFER :
2005-09-20 23:26:39 +10:00
{
data - > buff_pos = data - > buff_len ;
2006-10-02 06:54:23 +10:00
repaint ( ) ;
2005-09-20 23:26:39 +10:00
break ;
}
case R_NULL :
{
2006-10-17 01:32:26 +10:00
exec_prompt ( ) ;
2006-10-02 02:02:58 +10:00
s_reset ( & data - > screen ) ;
2006-10-02 06:54:23 +10:00
repaint ( ) ;
2005-09-20 23:26:39 +10:00
break ;
}
2006-01-31 05:53:10 +10:00
2006-10-05 07:45:02 +10:00
case R_WINCH :
{
repaint ( ) ;
break ;
}
2006-05-14 20:16:23 +10:00
case R_EOF :
{
exit_forced = 1 ;
data - > end_loop = 1 ;
break ;
}
2005-09-20 23:26:39 +10:00
/* complete */
case R_COMPLETE :
{
2006-01-31 05:53:10 +10:00
2005-09-20 23:26:39 +10:00
if ( ! data - > complete_func )
break ;
if ( ! comp_empty & & last_char = = R_COMPLETE )
break ;
if ( comp_empty )
{
2006-06-14 23:22:40 +10:00
wchar_t * begin , * end ;
wchar_t * token_begin , * token_end ;
2005-09-20 23:26:39 +10:00
wchar_t * buffcpy ;
2006-03-11 21:56:12 +10:00
int len ;
int cursor_steps ;
2006-01-31 02:51:50 +10:00
parse_util_cmdsubst_extent ( data - > buff , data - > buff_pos , & begin , & end ) ;
2005-09-20 23:26:39 +10:00
2006-03-11 21:56:12 +10:00
parse_util_token_extent ( begin , data - > buff_pos - ( begin - data - > buff ) , & token_begin , & token_end , 0 , 0 ) ;
2006-07-08 01:36:38 +10:00
2006-03-11 21:56:12 +10:00
cursor_steps = token_end - data - > buff - data - > buff_pos ;
data - > buff_pos + = cursor_steps ;
2006-10-02 06:54:23 +10:00
repaint ( ) ;
2006-07-08 01:36:38 +10:00
len = data - > buff_pos - ( begin - data - > buff ) ;
2005-09-20 23:26:39 +10:00
buffcpy = wcsndup ( begin , len ) ;
data - > complete_func ( buffcpy , & comp ) ;
sort_list ( & comp ) ;
remove_duplicates ( & comp ) ;
free ( buffcpy ) ;
2006-03-11 21:56:12 +10:00
2005-09-20 23:26:39 +10:00
}
if ( ( comp_empty =
handle_completions ( & comp ) ) )
{
2006-06-13 07:47:42 +10:00
al_foreach ( & comp , & free ) ;
2005-09-20 23:26:39 +10:00
al_truncate ( & comp , 0 ) ;
}
break ;
}
2006-10-12 23:27:32 +10:00
/* kill */
2005-09-20 23:26:39 +10:00
case R_KILL_LINE :
{
2006-10-12 23:27:32 +10:00
wchar_t * begin = & data - > buff [ data - > buff_pos ] ;
wchar_t * end = begin ;
int len ;
while ( * end & & * end ! = L ' \n ' )
end + + ;
if ( end = = begin & & * end )
end + + ;
len = end - begin ;
if ( len )
{
reader_kill ( begin , len , KILL_APPEND , last_char ! = R_KILL_LINE ) ;
}
2005-09-20 23:26:39 +10:00
break ;
}
case R_BACKWARD_KILL_LINE :
{
2006-10-12 23:27:32 +10:00
if ( data - > buff_pos > 0 )
{
wchar_t * end = & data - > buff [ data - > buff_pos ] ;
wchar_t * begin = end ;
int len ;
while ( begin > data - > buff & & * begin ! = L ' \n ' )
begin - - ;
if ( * begin = = L ' \n ' )
begin + + ;
len = maxi ( end - begin , 1 ) ;
begin = end - len ;
2006-10-13 05:30:00 +10:00
reader_kill ( begin , len , KILL_PREPEND , last_char ! = R_BACKWARD_KILL_LINE ) ;
2006-10-12 23:27:32 +10:00
}
2005-09-20 23:26:39 +10:00
break ;
2006-10-12 23:27:32 +10:00
2005-09-20 23:26:39 +10:00
}
case R_KILL_WHOLE_LINE :
{
2006-10-12 23:27:32 +10:00
wchar_t * end = & data - > buff [ data - > buff_pos ] ;
wchar_t * begin = end ;
int len ;
2006-10-17 01:32:26 +10:00
2006-10-12 23:27:32 +10:00
while ( begin > data - > buff & & * begin ! = L ' \n ' )
begin - - ;
if ( * begin = = L ' \n ' )
begin + + ;
len = maxi ( end - begin , 0 ) ;
begin = end - len ;
2005-09-20 23:26:39 +10:00
2006-10-12 23:27:32 +10:00
while ( * end & & * end ! = L ' \n ' )
end + + ;
if ( begin = = end & & * end )
end + + ;
len = end - begin ;
if ( len )
{
2006-10-17 01:32:26 +10:00
reader_kill ( begin , len , KILL_APPEND , last_char ! = R_KILL_WHOLE_LINE ) ;
2006-10-12 23:27:32 +10:00
}
2005-09-20 23:26:39 +10:00
break ;
}
/* yank*/
case R_YANK :
2006-10-12 23:27:32 +10:00
{
yank_str = kill_yank ( ) ;
2005-09-20 23:26:39 +10:00
insert_str ( yank_str ) ;
yank = wcslen ( yank_str ) ;
break ;
2005-12-31 02:29:19 +10:00
}
2006-01-31 05:53:10 +10:00
2005-12-31 02:29:19 +10:00
/* rotate killring*/
2005-09-20 23:26:39 +10:00
case R_YANK_POP :
2005-12-31 02:29:19 +10:00
{
2005-09-20 23:26:39 +10:00
if ( yank )
{
for ( i = 0 ; i < yank ; i + + )
remove_backward ( ) ;
yank_str = kill_yank_rotate ( ) ;
insert_str ( yank_str ) ;
yank = wcslen ( yank_str ) ;
}
break ;
2005-12-31 02:29:19 +10:00
}
2006-01-31 05:53:10 +10:00
2005-12-31 02:29:19 +10:00
/* Escape was pressed */
2005-09-20 23:26:39 +10:00
case L ' \e ' :
2005-12-31 02:29:19 +10:00
{
2005-09-20 23:26:39 +10:00
if ( * data - > search_buff )
{
2005-09-25 02:31:22 +10:00
if ( data - > token_history_pos = = - 1 )
2006-01-31 05:53:10 +10:00
{
2005-09-25 02:31:22 +10:00
history_reset ( ) ;
2006-01-31 05:53:10 +10:00
reader_set_buffer ( data - > search_buff ,
2005-09-25 02:31:22 +10:00
wcslen ( data - > search_buff ) ) ;
}
else
{
reader_replace_current_token ( data - > search_buff ) ;
}
2005-09-20 23:26:39 +10:00
* data - > search_buff = 0 ;
2006-10-02 06:54:23 +10:00
repaint ( ) ;
2006-01-31 05:53:10 +10:00
2005-09-20 23:26:39 +10:00
}
2006-01-31 05:53:10 +10:00
2005-09-20 23:26:39 +10:00
break ;
2005-12-31 02:29:19 +10:00
}
2006-01-31 05:53:10 +10:00
2005-12-31 02:29:19 +10:00
/* delete backward*/
2005-09-20 23:26:39 +10:00
case R_BACKWARD_DELETE_CHAR :
2005-12-31 02:29:19 +10:00
{
2005-09-20 23:26:39 +10:00
remove_backward ( ) ;
break ;
2005-12-31 02:29:19 +10:00
}
2006-01-31 05:53:10 +10:00
2005-12-31 02:29:19 +10:00
/* delete forward*/
2005-09-20 23:26:39 +10:00
case R_DELETE_CHAR :
2005-12-31 02:29:19 +10:00
{
2006-10-17 01:32:26 +10:00
/**
Remove the current character in the character buffer and on the
screen using syntax highlighting , etc .
*/
if ( data - > buff_pos < data - > buff_len )
{
data - > buff_pos + + ;
remove_backward ( ) ;
}
2005-09-20 23:26:39 +10:00
break ;
2005-12-31 02:29:19 +10:00
}
2006-01-31 05:53:10 +10:00
2006-01-18 22:42:48 +10:00
/* exit, but only if line is empty */
2005-09-20 23:26:39 +10:00
case R_EXIT :
2005-12-31 02:29:19 +10:00
{
2005-09-20 23:26:39 +10:00
if ( data - > buff_len = = 0 )
{
writestr ( L " \n " ) ;
data - > end_loop = 1 ;
}
break ;
2005-12-31 02:29:19 +10:00
}
2006-01-31 05:53:10 +10:00
2006-10-09 11:15:29 +10:00
/*
Evaluate . If the current command is unfinished , or if
the charater is escaped using a backslash , insert a
newline
*/
case R_EXECUTE :
2005-09-20 23:26:39 +10:00
{
2006-10-02 02:02:58 +10:00
/*
Allow backslash - escaped newlines
*/
2006-10-08 07:06:31 +10:00
if ( data - > buff_pos & & data - > buff [ data - > buff_pos - 1 ] = = L ' \\ ' )
2006-10-02 02:02:58 +10:00
{
insert_char ( ' \n ' ) ;
break ;
}
switch ( data - > test_func ( data - > buff ) )
2005-09-20 23:26:39 +10:00
{
2006-10-02 02:02:58 +10:00
case 0 :
2005-09-20 23:26:39 +10:00
{
2006-10-02 02:02:58 +10:00
/*
Finished commend , execute it
*/
if ( wcslen ( data - > buff ) )
{
2005-09-20 23:26:39 +10:00
// wcscpy(data->search_buff,L"");
2006-10-02 02:02:58 +10:00
history_add ( data - > buff ) ;
}
finished = 1 ;
data - > buff_pos = data - > buff_len ;
2006-10-02 06:54:23 +10:00
repaint ( ) ;
2006-10-02 02:02:58 +10:00
writestr ( L " \n " ) ;
break ;
}
/*
We are incomplete , continue editing
*/
case PARSER_TEST_INCOMPLETE :
{
insert_char ( ' \n ' ) ;
break ;
}
/*
Result must be some combination including an error . The error message will already be printed , all we need to do is repaint
*/
default :
{
s_reset ( & data - > screen ) ;
2006-10-02 06:54:23 +10:00
repaint ( ) ;
2006-10-02 02:02:58 +10:00
break ;
2005-09-20 23:26:39 +10:00
}
2006-01-31 05:53:10 +10:00
2006-10-02 02:02:58 +10:00
}
2005-09-20 23:26:39 +10:00
break ;
}
2005-12-31 02:29:19 +10:00
/* History up */
2005-09-20 23:26:39 +10:00
case R_HISTORY_SEARCH_BACKWARD :
2005-12-31 02:29:19 +10:00
{
2005-09-20 23:26:39 +10:00
if ( ( last_char ! = R_HISTORY_SEARCH_BACKWARD ) & &
2006-02-02 01:55:46 +10:00
( last_char ! = R_HISTORY_SEARCH_FORWARD ) & &
( last_char ! = R_FORWARD_CHAR ) & &
( last_char ! = R_BACKWARD_CHAR ) )
2005-09-20 23:26:39 +10:00
{
wcscpy ( data - > search_buff , data - > buff ) ;
data - > search_buff [ data - > buff_pos ] = 0 ;
}
handle_history ( history_prev_match ( data - > search_buff ) ) ;
break ;
2005-12-31 02:29:19 +10:00
}
2006-01-31 05:53:10 +10:00
2005-12-31 02:29:19 +10:00
/* History down */
2005-09-20 23:26:39 +10:00
case R_HISTORY_SEARCH_FORWARD :
2005-12-31 02:29:19 +10:00
{
2005-09-20 23:26:39 +10:00
if ( ( last_char ! = R_HISTORY_SEARCH_BACKWARD ) & &
2006-02-02 01:55:46 +10:00
( last_char ! = R_HISTORY_SEARCH_FORWARD ) & &
( last_char ! = R_FORWARD_CHAR ) & &
( last_char ! = R_BACKWARD_CHAR ) )
2005-09-20 23:26:39 +10:00
{
wcscpy ( data - > search_buff , data - > buff ) ;
data - > search_buff [ data - > buff_pos ] = 0 ;
}
handle_history ( history_next_match ( data - > search_buff ) ) ;
break ;
2005-12-31 02:29:19 +10:00
}
2006-01-31 05:53:10 +10:00
2005-12-31 02:29:19 +10:00
/* Token search for a earlier match */
2005-09-20 23:26:39 +10:00
case R_HISTORY_TOKEN_SEARCH_BACKWARD :
{
int reset = 0 ;
if ( ( last_char ! = R_HISTORY_TOKEN_SEARCH_BACKWARD ) & &
( last_char ! = R_HISTORY_TOKEN_SEARCH_FORWARD ) )
{
reset = 1 ;
}
handle_token_history ( 0 , reset ) ;
break ;
}
2005-12-31 02:29:19 +10:00
/* Token search for a later match */
2005-09-20 23:26:39 +10:00
case R_HISTORY_TOKEN_SEARCH_FORWARD :
{
int reset = 0 ;
if ( ( last_char ! = R_HISTORY_TOKEN_SEARCH_BACKWARD ) & &
( last_char ! = R_HISTORY_TOKEN_SEARCH_FORWARD ) )
{
reset = 1 ;
}
handle_token_history ( 1 , reset ) ;
break ;
}
2006-01-31 05:53:10 +10:00
2005-09-20 23:26:39 +10:00
/* Move left*/
case R_BACKWARD_CHAR :
2006-04-05 22:48:25 +10:00
{
2005-09-20 23:26:39 +10:00
if ( data - > buff_pos > 0 )
{
data - > buff_pos - - ;
2006-10-02 06:54:23 +10:00
repaint ( ) ;
2005-09-20 23:26:39 +10:00
}
break ;
2006-04-05 22:48:25 +10:00
}
/* Move right*/
2005-09-20 23:26:39 +10:00
case R_FORWARD_CHAR :
2005-12-31 02:29:19 +10:00
{
2005-09-20 23:26:39 +10:00
if ( data - > buff_pos < data - > buff_len )
{
2006-10-02 02:02:58 +10:00
data - > buff_pos + + ;
2006-10-02 06:54:23 +10:00
repaint ( ) ;
2005-09-20 23:26:39 +10:00
}
break ;
2005-12-31 02:29:19 +10:00
}
2006-01-31 05:53:10 +10:00
2005-09-20 23:26:39 +10:00
case R_DELETE_LINE :
2005-12-31 02:29:19 +10:00
{
2005-09-20 23:26:39 +10:00
data - > buff [ 0 ] = 0 ;
data - > buff_len = 0 ;
data - > buff_pos = 0 ;
2006-10-02 06:54:23 +10:00
repaint ( ) ;
2005-12-31 02:29:19 +10:00
break ;
}
2006-01-31 05:53:10 +10:00
2005-12-31 02:29:19 +10:00
/* kill one word left */
2005-09-20 23:26:39 +10:00
case R_BACKWARD_KILL_WORD :
2005-12-31 02:29:19 +10:00
{
2006-10-13 05:30:00 +10:00
move_word ( 0 , 1 , last_char ! = R_BACKWARD_KILL_WORD ) ;
2005-09-20 23:26:39 +10:00
break ;
2005-12-31 02:29:19 +10:00
}
2006-01-31 05:53:10 +10:00
2005-12-31 02:29:19 +10:00
/* kill one word right */
2005-09-20 23:26:39 +10:00
case R_KILL_WORD :
2005-12-31 02:29:19 +10:00
{
2006-10-13 05:30:00 +10:00
move_word ( 1 , 1 , last_char ! = R_KILL_WORD ) ;
2005-09-20 23:26:39 +10:00
break ;
2005-12-31 02:29:19 +10:00
}
2006-01-31 05:53:10 +10:00
2005-12-31 02:29:19 +10:00
/* move one word left*/
2005-09-20 23:26:39 +10:00
case R_BACKWARD_WORD :
2005-12-31 02:29:19 +10:00
{
2006-10-13 05:30:00 +10:00
move_word ( 0 , 0 , 0 ) ;
2005-09-20 23:26:39 +10:00
break ;
2005-12-31 02:29:19 +10:00
}
2006-01-31 05:53:10 +10:00
2005-12-31 02:29:19 +10:00
/* move one word right*/
2005-09-20 23:26:39 +10:00
case R_FORWARD_WORD :
2005-12-31 02:29:19 +10:00
{
2006-10-13 05:30:00 +10:00
move_word ( 1 , 0 , 0 ) ;
2005-09-20 23:26:39 +10:00
break ;
2005-12-31 02:29:19 +10:00
}
2006-01-31 05:53:10 +10:00
2005-09-20 23:26:39 +10:00
case R_BEGINNING_OF_HISTORY :
{
history_first ( ) ;
break ;
}
case R_END_OF_HISTORY :
{
history_reset ( ) ;
break ;
}
/* Other, if a normal character, we add it to the command */
default :
{
2006-10-09 11:15:29 +10:00
if ( ( ! wchar_private ( c ) ) & & ( ( ( c > 31 ) | | ( c = L ' \n ' ) ) & & ( c ! = 127 ) ) )
{
2005-09-20 23:26:39 +10:00
insert_char ( c ) ;
2006-10-09 11:15:29 +10:00
}
2005-10-14 00:08:33 +10:00
else
2006-03-10 23:38:09 +10:00
{
2006-04-05 22:48:25 +10:00
/*
2006-06-15 23:50:23 +10:00
Low priority debug message . These can happen if
the user presses an unefined control
sequnece . No reason to report .
2006-04-05 22:48:25 +10:00
*/
2006-06-15 23:50:23 +10:00
debug ( 2 , _ ( L " Unknown keybinding %d " ) , c ) ;
2006-03-10 23:38:09 +10:00
}
2005-09-20 23:26:39 +10:00
break ;
}
}
if ( ( c ! = R_HISTORY_SEARCH_BACKWARD ) & &
( c ! = R_HISTORY_SEARCH_FORWARD ) & &
( c ! = R_HISTORY_TOKEN_SEARCH_BACKWARD ) & &
2006-02-02 01:55:46 +10:00
( c ! = R_HISTORY_TOKEN_SEARCH_FORWARD ) & &
( c ! = R_FORWARD_CHAR ) & &
( c ! = R_BACKWARD_CHAR ) )
2005-09-20 23:26:39 +10:00
{
data - > search_buff [ 0 ] = 0 ;
history_reset ( ) ;
2005-09-25 02:31:22 +10:00
data - > token_history_pos = - 1 ;
2005-09-20 23:26:39 +10:00
}
last_char = c ;
}
al_destroy ( & comp ) ;
2006-05-14 20:16:23 +10:00
if ( ! reader_exit_forced ( ) )
{
if ( tcsetattr ( 0 , TCSANOW , & old_modes ) ) /* return to previous mode */
{
wperror ( L " tcsetattr " ) ;
exit ( 1 ) ;
}
set_color ( FISH_COLOR_RESET , FISH_COLOR_RESET ) ;
}
2005-09-20 23:26:39 +10:00
return data - > buff ;
}
/**
Read non - interactively . Read input from stdin without displaying
the prompt , using syntax highlighting . This is used for reading
scripts and init files .
*/
2005-10-19 22:07:44 +10:00
static int read_ni ( int fd )
2005-09-20 23:26:39 +10:00
{
FILE * in_stream ;
wchar_t * buff = 0 ;
buffer_t acc ;
2005-10-19 22:07:44 +10:00
int des = fd = = 0 ? dup ( 0 ) : fd ;
2005-09-20 23:26:39 +10:00
int res = 0 ;
2006-01-31 05:53:10 +10:00
2005-09-20 23:26:39 +10:00
if ( des = = - 1 )
{
wperror ( L " dup " ) ;
return 1 ;
}
2006-01-31 05:53:10 +10:00
2005-09-20 23:26:39 +10:00
b_init ( & acc ) ;
2006-01-31 05:53:10 +10:00
2005-09-20 23:26:39 +10:00
in_stream = fdopen ( des , " r " ) ;
if ( in_stream ! = 0 )
{
wchar_t * str ;
2005-10-19 22:07:44 +10:00
int acc_used ;
2006-01-31 05:53:10 +10:00
2005-09-20 23:26:39 +10:00
while ( ! feof ( in_stream ) )
{
char buff [ 4096 ] ;
2005-12-17 01:50:10 +10:00
int c ;
c = fread ( buff , 1 , 4096 , in_stream ) ;
if ( ferror ( in_stream ) )
{
2006-01-31 05:53:10 +10:00
debug ( 1 ,
2006-01-04 22:51:02 +10:00
_ ( L " Error while reading commands " ) ) ;
2005-12-17 01:50:10 +10:00
/*
2006-04-05 22:48:25 +10:00
Reset buffer on error . We won ' t evaluate incomplete files .
2005-12-17 01:50:10 +10:00
*/
acc . used = 0 ;
break ;
}
2006-01-31 05:53:10 +10:00
2005-09-20 23:26:39 +10:00
b_append ( & acc , buff , c ) ;
}
b_append ( & acc , " \0 " , 1 ) ;
2005-10-19 22:07:44 +10:00
acc_used = acc . used ;
2005-09-20 23:26:39 +10:00
str = str2wcs ( acc . buff ) ;
b_destroy ( & acc ) ;
2005-10-19 22:07:44 +10:00
if ( fclose ( in_stream ) )
{
2006-01-31 05:53:10 +10:00
debug ( 1 ,
2006-01-12 00:17:35 +10:00
_ ( L " Error while closing input stream " ) ) ;
2005-10-19 22:07:44 +10:00
wperror ( L " fclose " ) ;
res = 1 ;
}
2006-01-31 05:53:10 +10:00
2005-09-20 23:26:39 +10:00
if ( str )
2006-01-31 05:53:10 +10:00
{
2006-06-02 12:15:17 +10:00
string_buffer_t sb ;
sb_init ( & sb ) ;
2006-10-07 10:56:25 +10:00
if ( ! parser_test ( str , 0 , & sb , L " fish " ) )
2006-06-02 12:15:17 +10:00
{
eval ( str , 0 , TOP ) ;
}
else
{
fwprintf ( stderr , L " %ls " , sb . buff ) ;
res = 1 ;
}
sb_destroy ( & sb ) ;
free ( str ) ;
2005-09-20 23:26:39 +10:00
}
else
{
2005-10-19 22:07:44 +10:00
if ( acc_used > 1 )
2005-09-20 23:26:39 +10:00
{
debug ( 1 ,
2006-01-31 05:53:10 +10:00
_ ( L " Could not convert input. Read %d bytes. " ) ,
2005-10-19 22:07:44 +10:00
acc_used - 1 ) ;
2005-09-20 23:26:39 +10:00
}
else
{
2006-01-31 05:53:10 +10:00
debug ( 1 ,
2006-01-04 22:51:02 +10:00
_ ( L " Could not read input stream " ) ) ;
2005-09-20 23:26:39 +10:00
}
2006-01-31 05:53:10 +10:00
res = 1 ;
}
2005-09-20 23:26:39 +10:00
}
else
{
2006-01-31 05:53:10 +10:00
debug ( 1 ,
2006-01-12 00:17:35 +10:00
_ ( L " Error while opening input stream " ) ) ;
2005-09-20 23:26:39 +10:00
wperror ( L " fdopen " ) ;
free ( buff ) ;
res = 1 ;
}
return res ;
}
2005-10-19 22:07:44 +10:00
int reader_read ( int fd )
2005-09-20 23:26:39 +10:00
{
int res ;
2006-04-05 22:48:25 +10:00
2005-09-20 23:26:39 +10:00
/*
2006-04-05 22:48:25 +10:00
If reader_read is called recursively through the ' . ' builtin , we
need to preserve is_interactive . This , and signal handler setup
is handled by proc_push_interactive / proc_pop_interactive .
2005-09-20 23:26:39 +10:00
*/
2006-01-31 05:53:10 +10:00
2006-02-16 23:36:32 +10:00
proc_push_interactive ( ( ( fd = = 0 ) & & isatty ( STDIN_FILENO ) ) ) ;
2006-03-10 23:38:09 +10:00
2005-10-19 22:07:44 +10:00
res = is_interactive ? read_i ( ) : read_ni ( fd ) ;
2006-01-31 05:53:10 +10:00
2005-09-20 23:26:39 +10:00
/*
2005-10-19 22:07:44 +10:00
If the exit command was called in a script , only exit the
2006-04-05 22:48:25 +10:00
script , not the program .
2005-09-20 23:26:39 +10:00
*/
2006-02-08 19:24:29 +10:00
if ( data )
data - > end_loop = 0 ;
2005-09-20 23:26:39 +10:00
end_loop = 0 ;
2006-02-08 19:24:29 +10:00
2006-02-16 23:36:32 +10:00
proc_pop_interactive ( ) ;
2005-09-20 23:26:39 +10:00
return res ;
}