2005-09-20 13:26:39 +00: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"
2012-01-16 16:56:47 +00:00
# include <algorithm>
2005-09-20 13:26:39 +00:00
# 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-09 22:53:38 +00:00
# ifdef HAVE_SYS_TERMIOS_H
# include <sys/termios.h>
# endif
# ifdef HAVE_SYS_IOCTL_H
2005-09-20 13:26:39 +00:00
# include <sys/ioctl.h>
2006-08-09 22:53:38 +00:00
# endif
2006-11-30 23:57:49 +00:00
# include <time.h>
2005-09-20 13:26:39 +00:00
# include <sys/time.h>
# include <sys/wait.h>
2005-10-13 14:08:33 +00:00
# include <sys/poll.h>
2005-09-20 13:26:39 +00:00
# include <unistd.h>
# include <wctype.h>
2012-02-02 23:05:08 +00:00
# include <stack>
2005-09-20 13:26:39 +00:00
# if HAVE_NCURSES_H
# include <ncurses.h>
# else
# include <curses.h>
# endif
# if HAVE_TERMIO_H
# include <termio.h>
# endif
2006-01-19 12:22:07 +00:00
# if HAVE_TERM_H
2005-09-20 13:26:39 +00:00
# include <term.h>
2006-01-19 12:22:07 +00:00
# elif HAVE_NCURSES_TERM_H
# include <ncurses/term.h>
# endif
2006-07-30 20:26:59 +00:00
# ifdef HAVE_SIGINFO_H
# include <siginfo.h>
# endif
2006-08-09 22:26:05 +00:00
# ifdef HAVE_SYS_SELECT_H
# include <sys/select.h>
# endif
2005-09-20 13:26:39 +00:00
# include <signal.h>
# include <fcntl.h>
# include <dirent.h>
# include <wchar.h>
2005-12-25 22:00:44 +00:00
# include <assert.h>
2006-02-28 13:17:16 +00:00
# include "fallback.h"
2005-09-20 13:26:39 +00:00
# include "util.h"
2006-02-28 13:17:16 +00:00
2005-09-20 13:26:39 +00: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 00:51:26 +00:00
# include "signal.h"
2006-10-01 16:02:58 +00:00
# include "screen.h"
2011-12-27 03:18:46 +00:00
# include "iothread.h"
2012-02-02 23:05:08 +00:00
# include "intern.h"
2012-02-19 02:54:36 +00:00
# include "path.h"
2006-07-19 22:55:49 +00:00
2006-01-30 16:51:50 +00:00
# include "parse_util.h"
2005-09-20 13:26:39 +00:00
/**
2005-12-30 16:29:19 +00:00
Maximum length of prefix string when printing completion
list . Longer prefixes will be ellipsized .
2005-09-20 13:26:39 +00: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 .
*/
2007-08-22 08:00:52 +00:00
# define DEFAULT_PROMPT L"echo \"$USER@\"; hostname|cut -d . -f 1; echo \" \"; pwd; printf '> ';"
2005-09-20 13:26:39 +00:00
2008-01-13 16:47:47 +00:00
/**
The name of the function that prints the fish prompt
*/
2007-01-15 17:51:44 +00:00
# define PROMPT_FUNCTION_NAME L"fish_prompt"
2005-09-20 13:26:39 +00:00
/**
The default title for the reader . This is used by reader_readline .
*/
# define DEFAULT_TITLE L"echo $_ \" \"; pwd"
2005-10-13 14:08:33 +00:00
/**
The maximum number of characters to read from the keyboard without
2005-10-26 10:51:02 +00:00
repainting . Note that this readahead will only occur if new
2005-10-13 14:08:33 +00:00
characters are avaialble for reading , fish will never block for
more input without repainting .
*/
# define READAHEAD_MAX 256
2008-01-13 16:47:47 +00:00
/**
A mode for calling the reader_kill function . In this mode , the new
string is appended to the current contents of the kill buffer .
*/
2006-10-12 13:27:32 +00:00
# define KILL_APPEND 0
2008-01-13 16:47:47 +00:00
/**
A mode for calling the reader_kill function . In this mode , the new
string is prepended to the current contents of the kill buffer .
*/
2006-10-12 13:27:32 +00:00
# define KILL_PREPEND 1
2008-01-13 16:47:47 +00:00
/**
History search mode . This value means that no search is currently
performed .
*/
2007-04-16 21:26:15 +00:00
# define NO_SEARCH 0
2008-01-13 16:47:47 +00:00
/**
History search mode . This value means that we are perforing a line
history search .
*/
2007-04-16 21:26:15 +00:00
# define LINE_SEARCH 1
2008-01-13 16:47:47 +00:00
/**
History search mode . This value means that we are perforing a token
history search .
*/
2007-04-16 21:26:15 +00:00
# define TOKEN_SEARCH 2
2008-01-13 16:47:47 +00:00
/**
History search mode . This value means we are searching backwards .
*/
2007-04-16 21:26:15 +00:00
# define SEARCH_BACKWARD 0
2008-01-13 16:47:47 +00:00
/**
History search mode . This value means we are searching forwards .
*/
2007-04-16 21:26:15 +00:00
# define SEARCH_FORWARD 1
2012-02-28 20:40:20 +00:00
/* Any time the contents of a buffer changes, we update the generation count. This allows for our background highlighting thread to notice it and skip doing work that it would otherwise have to do. */
static unsigned int s_generation_count ;
2012-02-06 18:52:13 +00:00
/* A color is an int */
typedef int color_t ;
2005-09-20 13:26:39 +00:00
/**
A struct describing the state of the interactive reader . These
2007-01-15 17:53:46 +00:00
states can be stacked , in case reader_readline ( ) calls are
nested . This happens when the ' read ' builtin is used .
2005-09-20 13:26:39 +00:00
*/
2011-12-28 02:41:38 +00:00
class reader_data_t
2005-09-20 13:26:39 +00:00
{
2011-12-28 02:41:38 +00:00
public :
2012-02-06 18:52:13 +00:00
/** String containing the whole current commandline */
2012-02-06 09:45:16 +00:00
wcstring command_line ;
2012-02-06 18:52:13 +00:00
/** String containing the autosuggestion */
wcstring autosuggestion ;
2005-09-20 13:26:39 +00:00
2012-02-07 03:31:06 +00:00
/** When backspacing, we suppress autosuggestions */
bool suppress_autosuggestion ;
/** The representation of the current screen contents */
2006-10-01 16:02:58 +00:00
screen_t screen ;
2012-02-06 00:42:24 +00:00
/** The history */
history_t * history ;
2005-09-20 13:26:39 +00:00
/**
2012-02-04 04:01:01 +00:00
String containing the current search item
2005-09-20 13:26:39 +00:00
*/
2012-02-04 04:01:01 +00:00
wcstring search_buff ;
2006-10-01 15:59:18 +00:00
2012-02-06 00:42:24 +00:00
/* History search */
history_search_t history_search ;
2005-09-20 13:26:39 +00:00
/**
2005-12-30 16:29:19 +00:00
Saved position used by token history search
2005-09-20 13:26:39 +00:00
*/
int token_history_pos ;
/**
2012-02-28 20:40:20 +00:00
Saved search string for token history search . Not handled by command_line_changed .
2005-09-20 13:26:39 +00:00
*/
2012-02-06 07:22:18 +00:00
wcstring token_history_buff ;
2005-09-20 13:26:39 +00:00
/**
List for storing previous search results . Used to avoid duplicates .
*/
2012-02-02 22:27:13 +00:00
wcstring_list_t search_prev ;
2005-09-20 13:26:39 +00:00
2012-02-18 02:24:47 +00:00
/** The current position in search_prev */
2005-09-20 13:26:39 +00:00
int search_pos ;
2012-02-06 09:53:03 +00:00
/** Length of the command */
size_t command_length ( ) const { return command_line . size ( ) ; }
2012-02-06 19:34:49 +00:00
2012-02-28 20:40:20 +00:00
/** Do what we need to do whenever our command line changes */
void command_line_changed ( void ) ;
2005-09-20 13:26:39 +00:00
2012-02-18 02:24:47 +00:00
/** The current position of the cursor in buff. */
2005-09-20 13:26:39 +00:00
size_t buff_pos ;
2012-02-18 02:24:47 +00:00
/** Name of the current application */
2011-12-28 20:36:47 +00:00
wcstring app_name ;
2005-09-20 13:26:39 +00:00
2006-10-01 16:02:58 +00:00
/** The prompt command */
2011-12-28 20:36:47 +00:00
wcstring prompt ;
2005-09-20 13:26:39 +00:00
2006-10-01 16:02:58 +00:00
/** The output of the last evaluation of the prompt command */
2011-12-28 20:36:47 +00:00
wcstring prompt_buff ;
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00: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 ] .
*/
2012-02-06 18:52:13 +00:00
std : : vector < color_t > colors ;
2005-09-20 13:26:39 +00:00
2012-02-06 18:52:13 +00:00
/** An array defining the block level at each character. */
std : : vector < int > indents ;
2005-09-20 13:26:39 +00:00
/**
Function for tab completion
*/
2012-02-24 20:13:35 +00:00
complete_function_t complete_func ;
2005-09-20 13:26:39 +00:00
/**
Function for syntax highlighting
*/
2011-12-27 03:18:46 +00:00
highlight_function_t highlight_function ;
2005-09-20 13:26:39 +00:00
/**
Function for testing if the string can be returned
*/
2012-02-06 09:45:16 +00:00
int ( * test_func ) ( const wchar_t * ) ;
2005-09-20 13:26:39 +00:00
/**
2005-12-30 16:29:19 +00:00
When this is true , the reader will exit
2005-09-20 13:26:39 +00:00
*/
2012-02-18 02:24:47 +00:00
bool end_loop ;
2006-10-01 16:02:58 +00:00
2006-05-14 10:16:23 +00:00
/**
If this is true , exit reader even if there are running
jobs . This happens if we press e . g . ^ D twice .
*/
2012-02-18 02:24:47 +00:00
bool prev_end_loop ;
2005-09-20 13:26:39 +00:00
2012-03-04 03:37:55 +00:00
/** The current contents of the top item in the kill ring. */
wcstring kill_item ;
2006-10-12 13:27:32 +00:00
2005-09-20 13:26:39 +00:00
/**
Pointer to previous reader_data
*/
2011-12-28 02:41:38 +00:00
reader_data_t * next ;
2007-09-21 14:05:49 +00:00
/**
This variable keeps state on if we are in search mode , and
if yes , what mode
*/
int search_mode ;
2007-10-05 14:59:19 +00:00
/**
Keep track of whether any internal code has done something
which is known to require a repaint .
*/
2012-03-25 10:00:38 +00:00
bool repaint_needed ;
/** Whether the a screen reset is needed after a repaint. */
bool screen_reset_needed ;
2011-12-28 02:41:38 +00:00
} ;
2005-09-20 13:26:39 +00:00
/**
The current interactive reading context
*/
static reader_data_t * data = 0 ;
2007-01-31 23:58:10 +00:00
/**
This flag is set to true when fish is interactively reading from
stdin . It changes how a ^ C is handled by the fish interrupt
handler .
*/
static int is_interactive_read ;
2005-09-20 13:26:39 +00:00
/**
2005-12-30 16:29:19 +00:00
Flag for ending non - interactive shell
2005-09-20 13:26:39 +00:00
*/
static int end_loop = 0 ;
2012-03-03 23:20:30 +00:00
/** The stack containing names of files that are being parsed */
static std : : stack < const wchar_t * , std : : list < const wchar_t * > > current_filename ;
2005-09-20 13:26:39 +00:00
/**
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-23 20:40:14 +00:00
/**
This variable is set to true by the signal handler when ^ C is pressed
*/
2006-10-04 21:42:04 +00:00
static int interrupted = 0 ;
2005-09-20 13:26:39 +00:00
2006-01-23 20:40:14 +00:00
2005-09-20 13:26:39 +00:00
/*
Prototypes for a bunch of functions defined later on .
*/
2006-03-10 13:38:09 +00: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 ;
2012-02-21 18:47:21 +00:00
static void reader_super_highlight_me_plenty ( int pos ) ;
2005-09-20 13:26:39 +00:00
2006-05-14 10:16:23 +00:00
/**
Variable to keep track of forced exits - see \ c reader_exit_forced ( ) ;
*/
static int exit_forced ;
2005-09-20 13:26:39 +00:00
2006-01-23 20:40:14 +00:00
/**
Give up control of terminal
*/
2005-09-20 13:26:39 +00:00
static void term_donate ( )
{
2012-02-12 01:07:56 +00:00
set_color ( rgb_color_t : : normal ( ) , rgb_color_t : : normal ( ) ) ;
2005-09-20 13:26:39 +00:00
while ( 1 )
{
if ( tcsetattr ( 0 , TCSANOW , & saved_modes ) )
{
if ( errno ! = EINTR )
{
2006-01-04 12:51:02 +00:00
debug ( 1 , _ ( L " Could not set terminal mode for new job " ) ) ;
2005-09-20 13:26:39 +00:00
wperror ( L " tcsetattr " ) ;
break ;
}
}
else
break ;
}
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
}
2006-01-23 20:40:14 +00:00
/**
Grab control of terminal
*/
2005-09-20 13:26:39 +00:00
static void term_steal ( )
2006-01-30 19:53:10 +00:00
{
2005-09-20 13:26:39 +00:00
while ( 1 )
{
if ( tcsetattr ( 0 , TCSANOW , & shell_modes ) )
{
if ( errno ! = EINTR )
{
2006-01-04 12:51:02 +00:00
debug ( 1 , _ ( L " Could not set terminal mode for shell " ) ) ;
2005-09-20 13:26:39 +00:00
wperror ( L " tcsetattr " ) ;
break ;
}
}
else
break ;
}
2006-01-30 19:53:10 +00:00
common_handle_winch ( 0 ) ;
2005-09-20 13:26:39 +00:00
}
2006-05-14 10:16:23 +00:00
int reader_exit_forced ( )
{
return exit_forced ;
}
2007-10-05 14:59:19 +00:00
/**
Repaint the entire commandline . This means reset and clear the
commandline , write the prompt , perform syntax highlighting , write
the commandline and move the cursor .
*/
static void reader_repaint ( )
{
2012-02-06 18:52:13 +00:00
//Update the indentation
parser_t : : principal_parser ( ) . test ( data - > command_line . c_str ( ) , & data - > indents [ 0 ] , 0 , 0 ) ;
#if 0
2007-10-05 14:59:19 +00:00
s_write ( & data - > screen ,
2011-12-28 20:36:47 +00:00
data - > prompt_buff . c_str ( ) ,
2012-02-06 09:45:16 +00:00
data - > command_line . c_str ( ) ,
2012-02-06 18:52:13 +00:00
& data - > colors [ 0 ] ,
& data - > indents [ 0 ] ,
data - > buff_pos ) ;
# else
wcstring full_line = ( data - > autosuggestion . empty ( ) ? data - > command_line : data - > autosuggestion ) ;
size_t len = std : : max ( ( size_t ) 1 , full_line . size ( ) ) ;
std : : vector < color_t > colors = data - > colors ;
2012-02-07 03:31:06 +00:00
colors . resize ( len , HIGHLIGHT_AUTOSUGGESTION ) ;
2012-02-06 18:52:13 +00:00
std : : vector < int > indents = data - > indents ;
indents . resize ( len ) ;
s_write ( & data - > screen ,
data - > prompt_buff . c_str ( ) ,
full_line . c_str ( ) ,
& colors [ 0 ] ,
& indents [ 0 ] ,
2007-10-05 14:59:19 +00:00
data - > buff_pos ) ;
2012-02-06 18:52:13 +00:00
# endif
2012-03-25 10:00:38 +00:00
data - > repaint_needed = false ;
2007-10-05 14:59:19 +00:00
}
2006-10-25 20:36:08 +00:00
/**
Internal helper function for handling killing parts of text .
*/
2012-02-06 09:45:16 +00:00
static void reader_kill ( size_t begin_idx , int length , int mode , int newv )
2006-10-12 13:27:32 +00:00
{
2012-02-06 09:45:16 +00:00
const wchar_t * begin = data - > command_line . c_str ( ) + begin_idx ;
2011-12-27 03:18:46 +00:00
if ( newv )
2006-10-12 13:27:32 +00:00
{
2012-03-04 03:37:55 +00:00
data - > kill_item = wcstring ( begin , length ) ;
kill_add ( data - > kill_item ) ;
2006-10-12 13:27:32 +00:00
}
else
{
2012-03-04 03:37:55 +00:00
wcstring old = data - > kill_item ;
2006-10-12 13:27:32 +00:00
if ( mode = = KILL_APPEND )
{
2012-03-04 03:37:55 +00:00
data - > kill_item . append ( begin , length ) ;
2006-10-12 13:27:32 +00:00
}
else
{
2012-03-04 03:37:55 +00:00
data - > kill_item = wcstring ( begin , length ) ;
data - > kill_item . append ( old ) ;
2006-10-12 13:27:32 +00:00
}
2011-12-27 03:18:46 +00:00
2012-03-04 03:37:55 +00:00
kill_replace ( old , data - > kill_item ) ;
2006-10-12 13:27:32 +00:00
}
2006-10-12 19:30:00 +00:00
2012-02-06 09:45:16 +00:00
if ( data - > buff_pos > begin_idx ) {
data - > buff_pos = maxi ( begin_idx , data - > buff_pos - length ) ;
2006-10-12 19:30:00 +00:00
}
2011-12-27 03:18:46 +00:00
2012-02-06 09:45:16 +00:00
data - > command_line . erase ( begin_idx , length ) ;
2012-02-28 20:40:20 +00:00
data - > command_line_changed ( ) ;
2011-12-27 03:18:46 +00:00
2012-02-21 18:47:21 +00:00
reader_super_highlight_me_plenty ( data - > buff_pos ) ;
2007-10-05 14:59:19 +00:00
reader_repaint ( ) ;
2011-12-27 03:18:46 +00:00
2006-10-12 13:27:32 +00:00
}
2006-05-14 10:16:23 +00:00
2012-06-04 21:20:01 +00:00
/* This is called from a signal handler! */
2005-10-05 22:37:08 +00:00
void reader_handle_int ( int sig )
{
2007-01-31 23:58:10 +00:00
if ( ! is_interactive_read )
2005-10-05 22:37:08 +00:00
{
2012-06-04 21:20:01 +00:00
parser_t : : skip_all_blocks ( ) ;
2005-10-05 22:37:08 +00:00
}
2011-12-27 03:18:46 +00:00
2006-10-04 21:42:04 +00:00
interrupted = 1 ;
2011-12-27 03:18:46 +00:00
2005-10-05 22:37:08 +00:00
}
2012-02-02 23:05:08 +00:00
const wchar_t * reader_current_filename ( )
2005-09-20 13:26:39 +00:00
{
2012-02-02 23:05:08 +00:00
ASSERT_IS_MAIN_THREAD ( ) ;
return current_filename . empty ( ) ? NULL : current_filename . top ( ) ;
2005-09-20 13:26:39 +00:00
}
2006-02-02 15:23:56 +00:00
void reader_push_current_filename ( const wchar_t * fn )
2005-09-20 13:26:39 +00:00
{
2012-02-02 23:05:08 +00:00
ASSERT_IS_MAIN_THREAD ( ) ;
current_filename . push ( intern ( fn ) ) ;
2005-09-20 13:26:39 +00:00
}
2012-02-02 23:05:08 +00:00
void reader_pop_current_filename ( )
2005-09-20 13:26:39 +00:00
{
2012-02-02 23:05:08 +00:00
ASSERT_IS_MAIN_THREAD ( ) ;
current_filename . pop ( ) ;
2005-09-20 13:26:39 +00:00
}
2012-02-06 19:34:49 +00:00
/** Make sure buffers are large enough to hold the current string length */
2012-02-28 20:40:20 +00:00
void reader_data_t : : command_line_changed ( ) {
ASSERT_IS_MAIN_THREAD ( ) ;
2012-02-18 02:24:47 +00:00
size_t len = command_length ( ) ;
colors . resize ( len ) ;
indents . resize ( len ) ;
2012-02-28 20:40:20 +00:00
/* Update the gen count */
s_generation_count + + ;
2005-09-20 13:26:39 +00:00
}
2007-02-18 23:25:20 +00:00
2012-03-26 08:21:10 +00:00
/** Remove any duplicate completions in the list. This relies on the list first beeing sorted. */
2012-01-16 16:56:47 +00:00
static void remove_duplicates ( std : : vector < completion_t > & l ) {
l . erase ( std : : unique ( l . begin ( ) , l . end ( ) ) , l . end ( ) ) ;
}
2005-09-20 13:26:39 +00:00
2006-10-04 21:42:04 +00:00
int reader_interrupted ( )
2005-09-20 13:26:39 +00:00
{
2006-10-04 21:42:04 +00:00
int res = interrupted ;
2005-09-20 13:26:39 +00:00
if ( res )
2006-10-04 21:42:04 +00:00
interrupted = 0 ;
2005-09-20 13:26:39 +00:00
return res ;
}
void reader_write_title ( )
{
2011-12-27 03:18:46 +00:00
const wchar_t * title ;
2012-01-14 10:42:17 +00:00
const env_var_t term_str = env_get_string ( L " TERM " ) ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00: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
2007-03-17 23:38:46 +00:00
title . If we recognise it as that of a console , we assume it
does not support setting the title . Otherwise we check the
ttyname and see if we belive it is a virtual terminal .
One situation in which this breaks down is with screen , since
screen supports setting the terminal title if the underlying
terminal does so , but will print garbage on terminals that
don ' t . Since we can ' t see the underlying terminal below screen
there is no way to fix this .
2005-09-20 13:26:39 +00:00
*/
2012-01-14 10:42:17 +00:00
if ( term_str . missing ( ) )
2010-10-06 12:58:13 +00:00
return ;
2012-01-14 10:42:17 +00:00
const wchar_t * term = term_str . c_str ( ) ;
2012-01-02 21:40:03 +00:00
bool recognized = false ;
recognized = recognized | | contains ( term , L " xterm " , L " screen " , L " nxterm " , L " rxvt " ) ;
recognized = recognized | | ! wcsncmp ( term , L " xterm- " , wcslen ( L " xterm- " ) ) ;
if ( ! recognized )
2005-09-20 13:26:39 +00:00
{
char * n = ttyname ( STDIN_FILENO ) ;
2007-03-17 23:38:46 +00:00
2010-10-06 12:58:13 +00:00
2007-09-28 21:32:27 +00:00
if ( contains ( term , L " linux " ) )
2007-03-17 23:38:46 +00:00
{
return ;
}
2005-09-20 13:26:39 +00:00
if ( strstr ( n , " tty " ) | | strstr ( n , " /vc/ " ) )
return ;
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
}
title = function_exists ( L " fish_title " ) ? L " fish_title " : DEFAULT_TITLE ;
2011-12-27 03:18:46 +00:00
if ( wcslen ( title ) = = 0 )
2005-09-20 13:26:39 +00:00
return ;
2011-12-28 20:36:47 +00:00
wcstring_list_t lst ;
2005-09-20 13:26:39 +00:00
2006-02-16 13:36:32 +00:00
proc_push_interactive ( 0 ) ;
2012-02-08 07:35:41 +00:00
if ( exec_subshell ( title , lst ) ! = - 1 )
2005-09-20 13:26:39 +00:00
{
2012-01-15 06:00:00 +00:00
size_t i ;
2011-12-28 20:36:47 +00:00
if ( lst . size ( ) > 0 )
2005-09-20 13:26:39 +00:00
{
2012-06-07 05:15:46 +00:00
writestr ( L " \x1b ]; " ) ;
2011-12-28 20:36:47 +00:00
for ( i = 0 ; i < lst . size ( ) ; i + + )
2006-08-09 11:34:24 +00:00
{
2011-12-28 20:36:47 +00:00
writestr ( lst . at ( i ) . c_str ( ) ) ;
2006-08-09 11:34:24 +00:00
}
writestr ( L " \7 " ) ;
2005-09-20 13:26:39 +00:00
}
}
2011-12-28 20:36:47 +00:00
proc_pop_interactive ( ) ;
2012-02-12 01:07:56 +00:00
set_color ( rgb_color_t : : reset ( ) , rgb_color_t : : reset ( ) ) ;
2005-09-20 13:26:39 +00:00
}
/**
2006-10-16 15:32:26 +00:00
Reexecute the prompt command . The output is inserted into data - > prompt_buff .
2005-09-20 13:26:39 +00:00
*/
2006-10-16 15:32:26 +00:00
static void exec_prompt ( )
2005-09-20 13:26:39 +00:00
{
2012-01-10 20:51:09 +00:00
size_t i ;
2005-09-20 13:26:39 +00:00
2012-01-10 20:51:09 +00:00
wcstring_list_t prompt_list ;
2011-12-27 03:18:46 +00:00
2011-12-28 20:36:47 +00:00
if ( data - > prompt . size ( ) )
2005-09-20 13:26:39 +00:00
{
2006-10-16 15:32:26 +00:00
proc_push_interactive ( 0 ) ;
2011-12-27 03:18:46 +00:00
2012-02-18 17:58:54 +00:00
if ( exec_subshell ( data - > prompt , prompt_list ) = = - 1 )
2005-09-20 13:26:39 +00:00
{
2006-10-16 15:32:26 +00:00
/* If executing the prompt fails, make sure we at least don't print any junk */
2012-01-10 20:51:09 +00:00
prompt_list . clear ( ) ;
2005-09-20 13:26:39 +00:00
}
2006-10-16 15:32:26 +00:00
proc_pop_interactive ( ) ;
2005-09-20 13:26:39 +00:00
}
2011-12-27 03:18:46 +00:00
2006-10-16 15:32:26 +00:00
reader_write_title ( ) ;
2011-12-27 03:18:46 +00:00
2012-02-04 04:01:01 +00:00
data - > prompt_buff . clear ( ) ;
2011-12-27 03:18:46 +00:00
2012-01-10 20:51:09 +00:00
for ( i = 0 ; i < prompt_list . size ( ) ; i + + )
2006-10-16 15:32:26 +00:00
{
2012-01-10 20:51:09 +00:00
if ( i > 0 ) data - > prompt_buff + = L ' \n ' ;
data - > prompt_buff + = prompt_list . at ( i ) ;
}
2005-09-20 13:26:39 +00:00
}
void reader_init ( )
{
2006-08-24 10:43:54 +00:00
2006-03-10 13:38:09 +00: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 */
2011-12-27 03:18:46 +00:00
2006-03-10 13:38:09 +00:00
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 ;
2012-03-25 10:00:38 +00:00
2012-06-02 22:43:18 +00:00
// PCA disable VDSUSP (typically control-Y), which is a funny job control
// function available only on OS X and BSD systems
// This lets us use control-Y for yank instead
# ifdef VDSUSP
shell_modes . c_cc [ VDSUSP ] = _POSIX_VDISABLE ;
# endif
2012-03-25 10:00:38 +00:00
/* Repaint if necessary before each byte is read. This lets us react immediately to universal variable color changes. */
input_common_set_poll_callback ( reader_repaint_if_needed ) ;
2005-09-20 13:26:39 +00:00
}
void reader_destroy ( )
{
2006-03-10 13:38:09 +00:00
tcsetattr ( 0 , TCSANOW , & saved_modes ) ;
2005-09-20 13:26:39 +00:00
}
2006-05-14 10:16:23 +00:00
void reader_exit ( int do_exit , int forced )
2005-09-20 13:26:39 +00:00
{
2006-02-08 09:24:29 +00:00
if ( data )
2005-09-20 13:26:39 +00:00
data - > end_loop = do_exit ;
2006-02-08 09:24:29 +00:00
end_loop = do_exit ;
2006-05-14 10:16:23 +00:00
if ( forced )
exit_forced = 1 ;
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
}
2007-10-05 14:59:19 +00:00
void reader_repaint_needed ( )
2005-09-20 13:26:39 +00:00
{
2012-03-25 10:00:38 +00:00
if ( data ) {
data - > repaint_needed = true ;
2007-10-05 14:59:19 +00:00
}
2005-09-20 13:26:39 +00:00
}
2012-03-25 10:00:38 +00:00
void reader_repaint_if_needed ( ) {
if ( data & & data - > screen_reset_needed ) {
s_reset ( & data - > screen , false ) ;
data - > screen_reset_needed = false ;
}
if ( data & & data - > repaint_needed ) {
reader_repaint ( ) ;
/* reader_repaint clears repaint_needed */
}
}
void reader_react_to_color_change ( ) {
if ( data ) {
2012-04-01 00:24:11 +00:00
data - > repaint_needed = true ;
2012-03-25 10:00:38 +00:00
data - > screen_reset_needed = true ;
}
}
2007-09-21 14:05:49 +00:00
2007-10-05 14:59:19 +00:00
2005-09-20 13:26:39 +00: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 ;
2012-02-06 09:45:16 +00:00
data - > command_line . erase ( data - > buff_pos - 1 , 1 ) ;
2005-09-20 13:26:39 +00:00
data - > buff_pos - - ;
2012-02-28 20:40:20 +00:00
data - > command_line_changed ( ) ;
2012-02-07 03:31:06 +00:00
data - > suppress_autosuggestion = true ;
2005-09-20 13:26:39 +00:00
2012-02-21 18:47:21 +00:00
reader_super_highlight_me_plenty ( data - > buff_pos ) ;
2005-09-20 13:26:39 +00:00
2007-10-05 14:59:19 +00:00
reader_repaint ( ) ;
2006-10-01 16:02:58 +00:00
2005-09-20 13:26:39 +00:00
}
/**
Insert the characters of the string into the command line buffer
and print them to the screen using syntax highlighting , etc .
*/
2012-02-06 10:20:50 +00:00
static int insert_string ( const wcstring & str )
2005-09-20 13:26:39 +00:00
{
2012-02-06 09:45:16 +00:00
size_t len = str . size ( ) ;
2012-02-28 20:40:20 +00:00
if ( len = = 0 )
return 0 ;
2012-02-06 09:45:16 +00:00
data - > command_line . insert ( data - > buff_pos , str ) ;
data - > buff_pos + = len ;
2012-02-28 20:40:20 +00:00
data - > command_line_changed ( ) ;
2012-02-07 03:31:06 +00:00
data - > suppress_autosuggestion = false ;
2012-02-06 19:34:49 +00:00
2012-02-06 09:45:16 +00:00
/* Syntax highlight */
2012-02-21 18:47:21 +00:00
reader_super_highlight_me_plenty ( data - > buff_pos - 1 ) ;
2011-12-27 03:18:46 +00:00
2007-10-05 14:59:19 +00:00
reader_repaint ( ) ;
2005-09-20 13:26:39 +00:00
return 1 ;
}
2006-11-02 13:50:19 +00:00
/**
Insert the character into the command line buffer and print it to
the screen using syntax highlighting , etc .
*/
2012-02-06 18:52:13 +00:00
static int insert_char ( wchar_t c )
2006-11-02 13:50:19 +00:00
{
2012-05-05 20:34:09 +00:00
return insert_string ( wcstring ( & c , 1 ) ) ;
2006-11-02 13:50:19 +00:00
}
2005-09-20 13:26:39 +00:00
/**
Calculate the length of the common prefix substring of two strings .
*/
2007-02-18 23:25:20 +00:00
static int comp_len ( const wchar_t * a , const wchar_t * b )
2005-09-20 13:26:39 +00:00
{
int i ;
for ( i = 0 ;
a [ i ] ! = ' \0 ' & & b [ i ] ! = ' \0 ' & & a [ i ] = = b [ i ] ;
i + + )
;
return i ;
}
2007-02-28 21:43:27 +00:00
/**
Calculate the case insensitive length of the common prefix substring of two strings .
*/
static int comp_ilen ( const wchar_t * a , const wchar_t * b )
{
int i ;
for ( i = 0 ;
a [ i ] ! = ' \0 ' & & b [ i ] ! = ' \0 ' & & towlower ( a [ i ] ) = = towlower ( b [ i ] ) ;
i + + )
;
return i ;
}
2005-09-20 13:26:39 +00:00
/**
2006-11-02 13:50:19 +00:00
Find the outermost quoting style of current token . Returns 0 if
token is not quoted .
2005-09-20 13:26:39 +00:00
*/
2006-11-02 13:50:19 +00:00
static wchar_t get_quote ( wchar_t * cmd , int len )
2005-09-20 13:26:39 +00:00
{
int i = 0 ;
wchar_t res = 0 ;
while ( 1 )
{
if ( ! cmd [ i ] )
break ;
2006-09-14 15:18:21 +00:00
if ( cmd [ i ] = = L ' \\ ' )
2005-09-20 13:26:39 +00:00
{
2006-09-14 15:18:21 +00:00
i + + ;
if ( ! cmd [ i ] )
2005-09-20 13:26:39 +00:00
break ;
2011-12-27 03:18:46 +00:00
i + + ;
2005-09-20 13:26:39 +00:00
}
else
2006-09-14 15:18:21 +00:00
{
if ( cmd [ i ] = = L ' \' ' | | cmd [ i ] = = L ' \" ' )
{
const wchar_t * end = quote_end ( & cmd [ i ] ) ;
//fwprintf( stderr, L"Jump %d\n", end-cmd );
2006-11-02 13:50:19 +00:00
if ( ( end = = 0 ) | | ( ! * end ) | | ( end - cmd > len ) )
2006-09-14 15:18:21 +00:00
{
res = cmd [ i ] ;
break ;
}
i = end - cmd + 1 ;
}
else
i + + ;
}
2005-09-20 13:26:39 +00:00
}
2006-09-14 15:18:21 +00:00
2005-09-20 13:26:39 +00: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 01:56:13 +00:00
\ param string If not 0 , get_parm will store a copy of the parameter string as returned by the tokenizer .
2005-09-20 13:26:39 +00:00
\ param type If not 0 , get_param will store the token type as returned by tok_last .
*/
2012-02-06 09:45:16 +00:00
static void get_param ( const wchar_t * cmd ,
const int pos ,
2005-09-20 13:26:39 +00:00
wchar_t * quote ,
2012-02-06 09:45:16 +00:00
const wchar_t * * offset ,
2005-09-20 13:26:39 +00:00
wchar_t * * string ,
int * type )
{
int prev_pos = 0 ;
wchar_t last_quote = ' \0 ' ;
int unfinished ;
tokenizer tok ;
2012-05-06 20:36:51 +00:00
tok_init ( & tok , cmd , TOK_ACCEPT_UNFINISHED | TOK_SQUASH_ERRORS ) ;
2005-09-20 13:26:39 +00:00
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 01:56:13 +00:00
2005-09-20 13:26:39 +00:00
prev_pos = tok_get_pos ( & tok ) ;
}
tok_destroy ( & tok ) ;
2012-02-06 09:45:16 +00:00
wchar_t * cmd_tmp = wcsdup ( cmd ) ;
cmd_tmp [ pos ] = 0 ;
int cmdlen = wcslen ( cmd_tmp ) ;
2005-09-20 13:26:39 +00:00
unfinished = ( cmdlen = = 0 ) ;
if ( ! unfinished )
{
unfinished = ( quote ! = 0 ) ;
if ( ! unfinished )
{
2012-02-06 09:45:16 +00:00
if ( wcschr ( L " \t \n \r " , cmd_tmp [ cmdlen - 1 ] ) ! = 0 )
2005-09-20 13:26:39 +00:00
{
2012-02-06 09:45:16 +00:00
if ( ( cmdlen = = 1 ) | | ( cmd_tmp [ cmdlen - 2 ] ! = L ' \\ ' ) )
2005-09-20 13:26:39 +00:00
{
unfinished = 1 ;
}
}
}
}
if ( quote )
* quote = last_quote ;
if ( offset ! = 0 )
{
if ( ! unfinished )
{
2012-02-06 09:45:16 +00:00
while ( ( cmd_tmp [ prev_pos ] ! = 0 ) & & ( wcschr ( L " ;| " , cmd_tmp [ prev_pos ] ) ! = 0 ) )
2005-09-20 13:26:39 +00:00
prev_pos + + ;
* offset = cmd + prev_pos ;
}
else
{
* offset = cmd + pos ;
}
}
}
2012-05-05 20:34:09 +00:00
/**
Insert the string in the given command line at the given cursor
position . The function checks if the string is quoted or not and
correctly escapes the string .
\ param val the string to insert
\ param flags A union of all flags describing the completion to insert . See the completion_t struct for more information on possible values .
\ param command_line The command line into which we will insert
\ param inout_cursor_pos On input , the location of the cursor within the command line . On output , the new desired position .
\ return The completed string
*/
2012-05-06 20:36:51 +00:00
static wcstring completion_apply_to_command_line ( const wcstring & val_str , int flags , const wcstring & command_line , size_t * inout_cursor_pos )
2012-05-05 20:34:09 +00:00
{
const wchar_t * val = val_str . c_str ( ) ;
bool add_space = ! ( flags & COMPLETE_NO_SPACE ) ;
bool do_replace = ! ! ( flags & COMPLETE_NO_CASE ) ;
bool do_escape = ! ( flags & COMPLETE_DONT_ESCAPE ) ;
const size_t cursor_pos = * inout_cursor_pos ;
// debug( 0, L"Insert completion %ls with flags %d", val, flags);
if ( do_replace )
{
int move_cursor ;
const wchar_t * begin , * end ;
wchar_t * escaped ;
const wchar_t * buff = command_line . c_str ( ) ;
parse_util_token_extent ( buff , cursor_pos , & begin , 0 , 0 , 0 ) ;
end = buff + cursor_pos ;
wcstring sb ( buff , begin - buff ) ;
if ( do_escape )
{
escaped = escape ( val , ESCAPE_ALL | ESCAPE_NO_QUOTED ) ;
sb . append ( escaped ) ;
move_cursor = wcslen ( escaped ) ;
free ( escaped ) ;
}
else
{
sb . append ( val ) ;
move_cursor = wcslen ( val ) ;
}
if ( add_space )
{
sb . append ( L " " ) ;
move_cursor + = 1 ;
}
sb . append ( end ) ;
size_t new_cursor_pos = ( begin - buff ) + move_cursor ;
* inout_cursor_pos = new_cursor_pos ;
return sb ;
}
else
{
wchar_t quote = L ' \0 ' ;
2012-05-09 09:33:42 +00:00
wcstring replaced ;
2012-05-05 20:34:09 +00:00
if ( do_escape )
{
get_param ( command_line . c_str ( ) , cursor_pos , & quote , 0 , 0 , 0 ) ;
if ( quote = = L ' \0 ' )
{
2012-05-09 09:33:42 +00:00
replaced = escape_string ( val , ESCAPE_ALL | ESCAPE_NO_QUOTED ) ;
2012-05-05 20:34:09 +00:00
}
else
{
bool unescapable = false ;
2012-05-09 09:33:42 +00:00
for ( const wchar_t * pin = val ; * pin ; pin + + )
2012-05-05 20:34:09 +00:00
{
2012-05-09 09:33:42 +00:00
switch ( * pin )
2012-05-05 20:34:09 +00:00
{
case L ' \n ' :
case L ' \t ' :
case L ' \b ' :
case L ' \r ' :
unescapable = true ;
break ;
default :
2012-05-09 09:33:42 +00:00
replaced . push_back ( * pin ) ;
2012-05-05 20:34:09 +00:00
break ;
}
}
if ( unescapable )
{
2012-05-09 09:33:42 +00:00
replaced = escape_string ( val , ESCAPE_ALL | ESCAPE_NO_QUOTED ) ;
replaced . insert ( 0 , & quote , 1 ) ;
2012-05-05 20:34:09 +00:00
}
}
}
else
{
2012-05-09 09:33:42 +00:00
replaced = val ;
2012-05-05 20:34:09 +00:00
}
wcstring result = command_line ;
result . insert ( cursor_pos , replaced ) ;
2012-05-09 09:33:42 +00:00
size_t new_cursor_pos = cursor_pos + replaced . size ( ) ;
2012-05-05 20:34:09 +00:00
if ( add_space )
{
if ( quote & & ( command_line . c_str ( ) [ cursor_pos ] ! = quote ) )
{
/* This is a quoted parameter, first print a quote */
result . insert ( new_cursor_pos + + , wcstring ( & quote , 1 ) ) ;
}
result . insert ( new_cursor_pos + + , L " " ) ;
}
* inout_cursor_pos = new_cursor_pos ;
return result ;
}
}
2005-09-20 13:26:39 +00:00
/**
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
2008-01-13 16:47:47 +00:00
\ param flags A union of all flags describing the completion to insert . See the completion_t struct for more information on possible values .
2005-09-20 13:26:39 +00:00
*/
2007-02-28 21:43:27 +00:00
static void completion_insert ( const wchar_t * val , int flags )
2012-05-05 20:34:09 +00:00
{
size_t cursor = data - > buff_pos ;
2012-05-06 20:36:51 +00:00
wcstring new_command_line = completion_apply_to_command_line ( val , flags , data - > command_line , & cursor ) ;
2012-05-05 20:34:09 +00:00
reader_set_buffer ( new_command_line , cursor ) ;
/* Since we just inserted a completion, don't immediately do a new autosuggestion */
data - > suppress_autosuggestion = true ;
}
2005-09-20 13:26:39 +00:00
/**
2006-11-02 13:50:19 +00:00
Run the fish_pager command to display the completion list . If the
fish_pager outputs any text , it is inserted into the input
backbuffer .
2011-12-27 03:18:46 +00:00
\ param prefix the string to display before every completion .
2006-11-02 13:50:19 +00:00
\ param is_quoted should be set if the argument is quoted . This will change the display style .
\ param comp the list of completions to display
2005-09-20 13:26:39 +00:00
*/
2012-05-09 09:33:42 +00:00
static void run_pager ( const wcstring & prefix , int is_quoted , const std : : vector < completion_t > & comp )
2005-09-20 13:26:39 +00:00
{
2012-03-04 05:59:45 +00:00
wcstring msg ;
2012-05-09 09:33:42 +00:00
wcstring prefix_esc ;
2006-08-13 01:46:02 +00:00
char * foo ;
2007-02-09 09:33:50 +00:00
io_data_t * in ;
wchar_t * escaped_separator ;
2008-01-09 00:09:28 +00:00
int has_case_sensitive = 0 ;
2012-05-09 09:33:42 +00:00
if ( prefix . empty ( ) )
2007-02-09 09:33:50 +00:00
{
2012-05-09 09:33:42 +00:00
prefix_esc = L " \" \" " ;
2007-02-09 09:33:50 +00:00
}
2005-09-20 13:26:39 +00:00
else
2007-02-09 09:33:50 +00:00
{
2012-05-09 09:33:42 +00:00
prefix_esc = escape_string ( prefix , 1 ) ;
2007-02-09 09:33:50 +00:00
}
2011-12-27 03:18:46 +00:00
2012-03-04 05:59:45 +00:00
wcstring cmd = format_string ( L " fish_pager -c 3 -r 4 %ls -p %ls " ,
// L"valgrind --track-fds=yes --log-file=pager.txt --leak-check=full ./fish_pager %d %ls",
is_quoted ? L " -q " : L " " ,
2012-05-09 09:33:42 +00:00
prefix_esc . c_str ( ) ) ;
2012-03-04 05:59:45 +00:00
2007-02-09 09:33:50 +00:00
in = io_buffer_create ( 1 ) ;
2007-01-07 14:13:36 +00:00
in - > fd = 3 ;
2007-02-09 09:33:50 +00:00
escaped_separator = escape ( COMPLETE_SEP_STR , 1 ) ;
2011-12-27 03:18:46 +00:00
2012-02-01 01:01:19 +00:00
for ( size_t i = 0 ; i < comp . size ( ) ; i + + )
2008-01-09 00:09:28 +00:00
{
2012-01-16 16:56:47 +00:00
const completion_t & el = comp . at ( i ) ;
has_case_sensitive | = ! ( el . flags & COMPLETE_NO_CASE ) ;
2008-01-09 00:09:28 +00:00
}
2011-12-27 03:18:46 +00:00
2012-02-01 01:01:19 +00:00
for ( size_t i = 0 ; i < comp . size ( ) ; i + + )
2005-09-20 13:26:39 +00:00
{
2007-02-09 09:33:50 +00:00
2007-02-28 21:43:27 +00:00
int base_len = - 1 ;
2012-01-16 16:56:47 +00:00
const completion_t & el = comp . at ( i ) ;
2007-02-09 09:33:50 +00:00
wchar_t * foo = 0 ;
wchar_t * baz = 0 ;
2012-01-16 16:56:47 +00:00
if ( has_case_sensitive & & ( el . flags & COMPLETE_NO_CASE ) )
2008-01-09 00:09:28 +00:00
{
continue ;
}
2012-01-20 16:50:31 +00:00
if ( el . completion . empty ( ) ) {
continue ;
2007-02-28 21:43:27 +00:00
}
2011-12-27 03:18:46 +00:00
2012-01-20 16:50:31 +00:00
if ( el . flags & COMPLETE_NO_CASE )
{
if ( base_len = = - 1 )
2012-02-06 09:45:16 +00:00
{
const wchar_t * begin , * buff = data - > command_line . c_str ( ) ;
parse_util_token_extent ( buff , data - > buff_pos , & begin , 0 , 0 , 0 ) ;
base_len = data - > buff_pos - ( begin - buff ) ;
}
2012-01-20 16:50:31 +00:00
wcstring foo_wstr = escape_string ( el . completion . c_str ( ) + base_len , ESCAPE_ALL | ESCAPE_NO_QUOTED ) ;
foo = wcsdup ( foo_wstr . c_str ( ) ) ;
}
else
2012-02-06 09:45:16 +00:00
{
2012-01-20 16:50:31 +00:00
wcstring foo_wstr = escape_string ( el . completion , ESCAPE_ALL | ESCAPE_NO_QUOTED ) ;
foo = wcsdup ( foo_wstr . c_str ( ) ) ;
2012-02-06 09:45:16 +00:00
}
2012-01-20 16:50:31 +00:00
2012-01-16 16:56:47 +00:00
if ( ! el . description . empty ( ) )
2007-02-09 09:33:50 +00:00
{
2012-01-16 16:56:47 +00:00
wcstring baz_wstr = escape_string ( el . description , 1 ) ;
2012-01-18 18:33:19 +00:00
baz = wcsdup ( baz_wstr . c_str ( ) ) ;
2007-02-09 09:33:50 +00:00
}
2011-12-27 03:18:46 +00:00
2007-02-09 09:33:50 +00:00
if ( ! foo )
{
debug ( 0 , L " Run pager called with bad argument. " ) ;
bugreport ( ) ;
show_stackframe ( ) ;
}
else if ( baz )
{
2012-03-05 18:03:26 +00:00
append_format ( msg , L " %ls%ls%ls \n " , foo , escaped_separator , baz ) ;
2007-02-09 09:33:50 +00:00
}
else
{
2012-03-05 18:03:26 +00:00
append_format ( msg , L " %ls \n " , foo ) ;
2007-02-09 09:33:50 +00:00
}
2011-12-27 03:18:46 +00:00
free ( foo ) ;
free ( baz ) ;
2005-09-20 13:26:39 +00:00
}
2007-02-09 09:33:50 +00:00
2011-12-27 03:18:46 +00:00
free ( escaped_separator ) ;
2012-03-04 05:59:45 +00:00
foo = wcs2str ( msg . c_str ( ) ) ;
2012-03-04 10:35:30 +00:00
in - > out_buffer_append ( foo , strlen ( foo ) ) ;
2006-08-13 01:46:02 +00:00
free ( foo ) ;
2011-12-27 03:18:46 +00:00
2006-01-30 19:53:10 +00:00
term_donate ( ) ;
2011-12-27 03:18:46 +00:00
2006-08-13 01:46:02 +00:00
io_data_t * out = io_buffer_create ( 0 ) ;
out - > next = in ;
2007-01-07 14:13:36 +00:00
out - > fd = 4 ;
2011-12-27 03:18:46 +00:00
2012-01-23 05:57:30 +00:00
parser_t & parser = parser_t : : principal_parser ( ) ;
2012-03-04 05:59:45 +00:00
parser . eval ( cmd , out , TOP ) ;
2006-01-30 19:53:10 +00:00
term_steal ( ) ;
2005-09-20 14:51:00 +00:00
2005-10-08 11:20:51 +00:00
io_buffer_read ( out ) ;
2006-01-30 19:53:10 +00:00
int nil = 0 ;
2012-03-04 10:35:30 +00:00
out - > out_buffer_append ( ( char * ) & nil , 1 ) ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
wchar_t * tmp ;
2012-03-04 10:35:30 +00:00
wchar_t * str = str2wcs ( out - > out_buffer_ptr ( ) ) ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
if ( str )
{
for ( tmp = str + wcslen ( str ) - 1 ; tmp > = str ; tmp - - )
{
input_unreadch ( * tmp ) ;
2006-01-30 19:53:10 +00:00
}
2005-09-20 13:26:39 +00:00
free ( str ) ;
}
2006-01-30 19:53:10 +00:00
2006-08-13 01:46:02 +00:00
2005-10-08 11:20:51 +00:00
io_buffer_destroy ( out ) ;
2006-08-13 01:46:02 +00:00
io_buffer_destroy ( in ) ;
2005-09-20 13:26:39 +00:00
}
2012-02-16 08:24:27 +00:00
struct autosuggestion_context_t {
2012-02-20 10:13:31 +00:00
wcstring search_string ;
wcstring autosuggestion ;
2012-05-05 20:34:09 +00:00
size_t cursor_pos ;
2012-02-16 08:24:27 +00:00
history_search_t searcher ;
file_detection_context_t detector ;
2012-02-19 02:54:36 +00:00
const wcstring working_directory ;
const env_vars vars ;
2012-02-27 04:11:34 +00:00
wcstring_list_t commands_to_load ;
2012-02-28 20:40:20 +00:00
const unsigned int generation_count ;
2012-02-16 08:24:27 +00:00
2012-02-27 23:33:46 +00:00
// don't reload more than once
bool has_tried_reloading ;
2012-05-05 20:34:09 +00:00
autosuggestion_context_t ( history_t * history , const wcstring & term , size_t pos ) :
2012-02-20 10:13:31 +00:00
search_string ( term ) ,
2012-05-05 20:34:09 +00:00
cursor_pos ( pos ) ,
2012-02-16 08:24:27 +00:00
searcher ( * history , term , HISTORY_SEARCH_TYPE_PREFIX ) ,
2012-02-19 02:54:36 +00:00
detector ( history , term ) ,
working_directory ( get_working_directory ( ) ) ,
2012-02-27 23:33:46 +00:00
vars ( env_vars : : highlighting_keys ) ,
2012-02-28 20:40:20 +00:00
generation_count ( s_generation_count ) ,
2012-02-27 23:33:46 +00:00
has_tried_reloading ( false )
2012-02-16 08:24:27 +00:00
{
}
2012-02-19 02:54:36 +00:00
2012-03-26 05:41:22 +00:00
/* The function run in the background thread to determine an autosuggestion */
2012-02-19 02:54:36 +00:00
int threaded_autosuggest ( void ) {
ASSERT_IS_BACKGROUND_THREAD ( ) ;
2012-02-28 20:40:20 +00:00
2012-03-30 18:16:24 +00:00
/* If the main thread has moved on, skip all the work */
2012-02-28 20:40:20 +00:00
if ( generation_count ! = s_generation_count ) {
return 0 ;
}
2012-05-05 20:34:09 +00:00
/* Let's make sure we aren't using the empty string */
if ( search_string . empty ( ) ) {
return 0 ;
}
2012-02-19 02:54:36 +00:00
while ( searcher . go_backwards ( ) ) {
history_item_t item = searcher . current_item ( ) ;
2012-03-26 05:41:22 +00:00
/* Skip items with newlines because they make terrible autosuggestions */
if ( item . str ( ) . find ( ' \n ' ) ! = wcstring : : npos )
continue ;
2012-02-19 02:54:36 +00:00
bool item_ok = false ;
2012-05-07 19:55:13 +00:00
if ( autosuggest_special_validate_from_history ( item . str ( ) , working_directory , & item_ok ) ) {
2012-02-19 02:54:36 +00:00
/* The command autosuggestion was handled specially, so we're done */
} else {
/* See if the item has any required paths */
const path_list_t & paths = item . get_required_paths ( ) ;
if ( paths . empty ( ) ) {
item_ok = true ;
} else {
detector . potential_paths = paths ;
item_ok = detector . paths_are_valid ( paths ) ;
}
}
2012-02-20 10:13:31 +00:00
if ( item_ok ) {
this - > autosuggestion = searcher . current_string ( ) ;
2012-02-19 02:54:36 +00:00
return 1 ;
2012-02-20 10:13:31 +00:00
}
}
2012-03-30 18:16:24 +00:00
/* Try handling a special command like cd */
wcstring special_suggestion ;
if ( autosuggest_suggest_special ( search_string , working_directory , special_suggestion ) ) {
this - > autosuggestion = special_suggestion ;
return 1 ;
}
2012-05-05 20:34:09 +00:00
// Here we do something a little funny
// If the line ends with a space, and the cursor is not at the end,
2012-05-08 00:31:24 +00:00
// don't use completion autosuggestions. It ends up being pretty weird seeing stuff get spammed on the right
// while you go back to edit a line
2012-05-05 20:34:09 +00:00
const bool line_ends_with_space = iswspace ( search_string . at ( search_string . size ( ) - 1 ) ) ;
const bool cursor_at_end = ( this - > cursor_pos = = search_string . size ( ) ) ;
if ( line_ends_with_space & & ! cursor_at_end )
return 0 ;
2012-02-26 02:54:49 +00:00
/* Try normal completions */
std : : vector < completion_t > completions ;
2012-02-27 23:33:46 +00:00
complete ( search_string , completions , COMPLETE_AUTOSUGGEST , & this - > commands_to_load ) ;
2012-03-02 01:31:45 +00:00
if ( ! completions . empty ( ) ) {
const completion_t & comp = completions . at ( 0 ) ;
2012-05-06 20:36:51 +00:00
size_t cursor = this - > cursor_pos ;
this - > autosuggestion = completion_apply_to_command_line ( comp . completion . c_str ( ) , comp . flags , this - > search_string , & cursor ) ;
2012-02-26 02:54:49 +00:00
return 1 ;
2012-02-19 02:54:36 +00:00
}
2012-02-20 10:13:31 +00:00
2012-02-19 02:54:36 +00:00
return 0 ;
}
2012-02-16 08:24:27 +00:00
} ;
static int threaded_autosuggest ( autosuggestion_context_t * ctx ) {
2012-02-19 02:54:36 +00:00
return ctx - > threaded_autosuggest ( ) ;
2012-02-16 08:24:27 +00:00
}
static bool can_autosuggest ( void ) {
2012-03-05 17:34:31 +00:00
/* We autosuggest if suppress_autosuggestion is not set, if we're not doing a history search, and our command line contains a non-whitespace character. */
const wchar_t * whitespace = L " \t \r \n \v " ;
return ! data - > suppress_autosuggestion & &
data - > history_search . is_at_end ( ) & &
data - > command_line . find_first_not_of ( whitespace ) ! = wcstring : : npos ;
2012-02-16 08:24:27 +00:00
}
static void autosuggest_completed ( autosuggestion_context_t * ctx , int result ) {
2012-02-27 04:11:34 +00:00
/* Extract the commands to load */
wcstring_list_t commands_to_load ;
ctx - > commands_to_load . swap ( commands_to_load ) ;
/* If we have autosuggestions to load, load them and try again */
2012-02-27 23:33:46 +00:00
if ( ! result & & ! commands_to_load . empty ( ) & & ! ctx - > has_tried_reloading )
2012-02-27 04:11:34 +00:00
{
2012-02-27 23:33:46 +00:00
ctx - > has_tried_reloading = true ;
2012-03-01 22:56:34 +00:00
for ( wcstring_list_t : : const_iterator iter = commands_to_load . begin ( ) ; iter ! = commands_to_load . end ( ) ; + + iter )
2012-02-27 04:11:34 +00:00
{
complete_load ( * iter , false ) ;
}
iothread_perform ( threaded_autosuggest , autosuggest_completed , ctx ) ;
return ;
}
2012-03-02 01:31:45 +00:00
2012-02-20 10:13:31 +00:00
if ( result & &
can_autosuggest ( ) & &
ctx - > search_string = = data - > command_line & &
2012-03-02 01:31:45 +00:00
string_prefixes_string_case_insensitive ( ctx - > search_string , ctx - > autosuggestion ) ) {
2012-02-16 08:24:27 +00:00
/* Autosuggestion is active and the search term has not changed, so we're good to go */
2012-02-20 10:13:31 +00:00
data - > autosuggestion = ctx - > autosuggestion ;
sanity_check ( ) ;
reader_repaint ( ) ;
2012-02-16 08:24:27 +00:00
}
delete ctx ;
}
2012-02-06 18:52:13 +00:00
static void update_autosuggestion ( void ) {
/* Updates autosuggestion. We look for an autosuggestion if the command line is non-empty and if we're not doing a history search. */
2012-02-16 08:24:27 +00:00
#if 0
/* Old non-threaded mode */
2012-02-06 18:52:13 +00:00
data - > autosuggestion . clear ( ) ;
2012-02-16 08:24:27 +00:00
if ( can_autosuggest ( ) ) {
2012-02-06 18:52:13 +00:00
history_search_t searcher = history_search_t ( * data - > history , data - > command_line , HISTORY_SEARCH_TYPE_PREFIX ) ;
if ( searcher . go_backwards ( ) ) {
data - > autosuggestion = searcher . current_item ( ) ;
}
}
2012-02-16 08:24:27 +00:00
# else
data - > autosuggestion . clear ( ) ;
if ( ! data - > suppress_autosuggestion & & ! data - > command_line . empty ( ) & & data - > history_search . is_at_end ( ) ) {
2012-05-05 20:34:09 +00:00
autosuggestion_context_t * ctx = new autosuggestion_context_t ( data - > history , data - > command_line , data - > buff_pos ) ;
2012-02-16 08:24:27 +00:00
iothread_perform ( threaded_autosuggest , autosuggest_completed , ctx ) ;
}
# endif
2012-02-06 18:52:13 +00:00
}
2008-01-13 16:47:47 +00:00
/**
2006-11-30 23:57:49 +00:00
Flash the screen . This function only changed the color of the
current line , since the flash_screen sequnce is rather painful to
look at in most terminal emulators .
*/
static void reader_flash ( )
{
struct timespec pollint ;
2012-01-15 06:48:53 +00:00
for ( size_t i = 0 ; i < data - > buff_pos ; i + + )
2006-11-30 23:57:49 +00:00
{
2012-02-06 18:52:13 +00:00
data - > colors . at ( i ) = HIGHLIGHT_SEARCH_MATCH < < 16 ;
2006-11-30 23:57:49 +00:00
}
2011-12-27 03:18:46 +00:00
2007-10-05 14:59:19 +00:00
reader_repaint ( ) ;
2011-12-27 03:18:46 +00:00
2006-11-30 23:57:49 +00:00
pollint . tv_sec = 0 ;
pollint . tv_nsec = 100 * 1000000 ;
nanosleep ( & pollint , NULL ) ;
2012-02-21 18:47:21 +00:00
reader_super_highlight_me_plenty ( data - > buff_pos ) ;
2012-02-06 18:52:13 +00:00
2007-10-05 14:59:19 +00:00
reader_repaint ( ) ;
2006-11-30 23:57:49 +00:00
}
2008-01-14 01:00:32 +00:00
/**
Characters that may not be part of a token that is to be replaced
by a case insensitive completion .
*/
# define REPLACE_UNCLEAN L"$*?({})"
2008-01-13 19:32:21 +00:00
2008-01-14 01:00:32 +00:00
/**
Check if the specified string can be replaced by a case insensitive
complition with the specified flags .
Advanced tokens like those containing { } - style expansion can not at
the moment be replaced , other than if the new token is already an
exact replacement , e . g . if the COMPLETE_DONT_ESCAPE flag is set .
*/
2012-02-10 02:59:15 +00:00
static int reader_can_replace ( const wcstring & in , int flags )
2008-01-13 19:32:21 +00:00
{
2012-02-10 02:59:15 +00:00
const wchar_t * str = in . c_str ( ) ;
2008-01-13 19:32:21 +00:00
2008-01-14 01:00:32 +00:00
if ( flags & COMPLETE_DONT_ESCAPE )
{
return 1 ;
}
2008-01-13 19:32:21 +00:00
/*
Test characters that have a special meaning in any character position
*/
while ( * str )
{
2008-01-14 01:00:32 +00:00
if ( wcschr ( REPLACE_UNCLEAN , * str ) )
2008-01-13 19:32:21 +00:00
return 0 ;
str + + ;
}
return 1 ;
}
2006-11-30 23:57:49 +00:00
2005-09-20 13:26:39 +00:00
/**
Handle the list of completions . This means the following :
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00: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-16 15:32:26 +00: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 .
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
\ param comp the list of completion strings
*/
2012-05-05 20:34:09 +00:00
static int handle_completions ( const std : : vector < completion_t > & comp )
2005-09-20 13:26:39 +00:00
{
2012-05-05 20:34:09 +00:00
wchar_t * base = NULL ;
2007-02-28 21:43:27 +00:00
int len = 0 ;
2012-05-05 20:34:09 +00:00
bool done = false ;
2007-02-28 21:43:27 +00:00
int count = 0 ;
int flags = 0 ;
2012-02-06 09:45:16 +00:00
const wchar_t * begin , * end , * buff = data - > command_line . c_str ( ) ;
2011-12-27 03:18:46 +00:00
2012-02-06 09:45:16 +00:00
parse_util_token_extent ( buff , data - > buff_pos , & begin , 0 , 0 , 0 ) ;
end = buff + data - > buff_pos ;
2011-12-27 03:18:46 +00:00
2012-02-10 02:59:15 +00:00
const wcstring tok ( begin , end - begin ) ;
2011-12-27 03:18:46 +00:00
2008-01-13 19:32:21 +00:00
/*
Check trivial cases
*/
2012-05-05 20:34:09 +00:00
switch ( comp . size ( ) )
2005-09-20 13:26:39 +00:00
{
2012-05-05 20:34:09 +00:00
/* No suitable completions found, flash screen and return */
2007-03-24 19:07:38 +00:00
case 0 :
{
reader_flash ( ) ;
2012-05-05 20:34:09 +00:00
done = true ;
2007-03-24 19:07:38 +00:00
break ;
}
2008-01-13 19:32:21 +00:00
2012-05-05 20:34:09 +00:00
/* Exactly one suitable completion found - insert it */
2007-03-24 19:07:38 +00:00
case 1 :
{
2011-12-27 03:18:46 +00:00
2012-01-16 16:56:47 +00:00
const completion_t & c = comp . at ( 0 ) ;
2011-12-27 03:18:46 +00:00
2008-01-13 19:32:21 +00:00
/*
If this is a replacement completion , check
that we know how to replace it , e . g . that
the token doesn ' t contain evil operators
like { }
*/
2012-01-16 16:56:47 +00:00
if ( ! ( c . flags & COMPLETE_NO_CASE ) | | reader_can_replace ( tok , c . flags ) )
2007-03-24 19:07:38 +00:00
{
2012-02-26 02:54:49 +00:00
completion_insert ( c . completion . c_str ( ) , c . flags ) ;
2007-03-24 19:07:38 +00:00
}
2012-05-05 20:34:09 +00:00
done = true ;
len = 1 ; // I think this just means it's a true return
2007-10-28 09:08:40 +00:00
break ;
2007-03-24 19:07:38 +00:00
}
2005-09-20 13:26:39 +00:00
}
2011-12-27 03:18:46 +00:00
2007-03-24 19:07:38 +00:00
if ( ! done )
2005-09-20 13:26:39 +00:00
{
2012-05-05 20:34:09 +00:00
/* Try to find something to insert whith the correct case */
2012-02-01 01:01:19 +00:00
for ( size_t i = 0 ; i < comp . size ( ) ; i + + )
2005-09-20 13:26:39 +00:00
{
2012-01-16 16:56:47 +00:00
const completion_t & c = comp . at ( i ) ;
2007-02-28 21:43:27 +00:00
2012-05-05 20:34:09 +00:00
/* Ignore case insensitive completions for now */
2012-01-16 16:56:47 +00:00
if ( c . flags & COMPLETE_NO_CASE )
2007-02-28 21:43:27 +00:00
continue ;
2011-12-27 03:18:46 +00:00
2007-02-28 21:43:27 +00:00
count + + ;
2011-12-27 03:18:46 +00:00
2007-02-28 21:43:27 +00:00
if ( base )
{
2012-05-05 20:34:09 +00:00
int new_len = comp_len ( base , c . completion . c_str ( ) ) ;
len = mini ( new_len , len ) ;
2007-02-28 21:43:27 +00:00
}
else
{
2012-01-16 16:56:47 +00:00
base = wcsdup ( c . completion . c_str ( ) ) ;
2007-02-28 21:43:27 +00:00
len = wcslen ( base ) ;
2012-01-16 16:56:47 +00:00
flags = c . flags ;
2007-02-28 21:43:27 +00:00
}
2005-09-20 13:26:39 +00:00
}
2007-02-28 21:43:27 +00:00
2012-05-05 20:34:09 +00:00
/* If we found something to insert, do it. */
2005-09-20 13:26:39 +00:00
if ( len > 0 )
{
2007-02-28 21:43:27 +00:00
if ( count > 1 )
flags = flags | COMPLETE_NO_SPACE ;
2005-09-20 13:26:39 +00:00
base [ len ] = L ' \0 ' ;
2007-02-28 21:43:27 +00:00
completion_insert ( base , flags ) ;
2012-05-05 20:34:09 +00:00
done = true ;
2005-09-20 13:26:39 +00:00
}
2007-03-24 19:07:38 +00:00
}
2011-12-27 03:18:46 +00:00
2007-02-28 21:43:27 +00:00
2012-05-05 20:34:09 +00:00
if ( ! done & & base = = NULL )
2007-03-24 19:07:38 +00:00
{
2012-05-05 20:34:09 +00:00
/* Try to find something to insert ignoring case */
2007-03-24 19:07:38 +00:00
if ( begin )
{
2007-02-28 21:43:27 +00:00
2012-02-10 02:59:15 +00:00
int offset = tok . size ( ) ;
2011-12-27 03:18:46 +00:00
2008-01-14 01:00:32 +00:00
count = 0 ;
2011-12-27 03:18:46 +00:00
2012-02-01 01:01:19 +00:00
for ( size_t i = 0 ; i < comp . size ( ) ; i + + )
2007-02-28 21:43:27 +00:00
{
2012-01-16 16:56:47 +00:00
const completion_t & c = comp . at ( i ) ;
2008-01-14 01:00:32 +00:00
int new_len ;
2007-02-28 21:43:27 +00:00
2012-01-16 16:56:47 +00:00
if ( ! ( c . flags & COMPLETE_NO_CASE ) )
2008-01-14 01:00:32 +00:00
continue ;
2011-12-27 03:18:46 +00:00
2012-01-16 16:56:47 +00:00
if ( ! reader_can_replace ( tok , c . flags ) )
2008-01-14 01:00:32 +00:00
{
len = 0 ;
break ;
2007-03-24 19:07:38 +00:00
}
2007-02-28 21:43:27 +00:00
2008-01-14 01:00:32 +00:00
count + + ;
2007-02-28 21:43:27 +00:00
2008-01-14 01:00:32 +00:00
if ( base )
{
2012-01-16 16:56:47 +00:00
new_len = offset + comp_ilen ( base + offset , c . completion . c_str ( ) + offset ) ;
2008-01-14 01:00:32 +00:00
len = new_len < len ? new_len : len ;
2007-02-28 21:43:27 +00:00
}
2008-01-14 01:00:32 +00:00
else
{
2012-01-16 16:56:47 +00:00
base = wcsdup ( c . completion . c_str ( ) ) ;
2008-01-14 01:00:32 +00:00
len = wcslen ( base ) ;
2012-01-16 16:56:47 +00:00
flags = c . flags ;
2011-12-27 03:18:46 +00:00
2008-01-14 01:00:32 +00:00
}
}
if ( len > offset )
{
if ( count > 1 )
flags = flags | COMPLETE_NO_SPACE ;
2011-12-27 03:18:46 +00:00
2008-01-14 01:00:32 +00:00
base [ len ] = L ' \0 ' ;
completion_insert ( base , flags ) ;
done = 1 ;
2007-02-28 21:43:27 +00:00
}
2011-12-27 03:18:46 +00:00
2007-02-28 21:43:27 +00:00
}
2007-03-24 19:07:38 +00:00
}
2011-12-27 03:18:46 +00:00
2007-03-24 19:07:38 +00:00
free ( base ) ;
2007-02-28 21:43:27 +00:00
2007-03-24 19:07:38 +00:00
if ( ! done )
{
/*
There is no common prefix in the completions , and show_list
is true , so we print the list
*/
int len ;
2012-05-09 09:33:42 +00:00
wcstring prefix ;
2012-02-06 09:45:16 +00:00
const wchar_t * prefix_start ;
const wchar_t * buff = data - > command_line . c_str ( ) ;
get_param ( buff ,
2007-03-24 19:07:38 +00:00
data - > buff_pos ,
0 ,
& prefix_start ,
0 ,
0 ) ;
2012-02-06 09:45:16 +00:00
len = & buff [ data - > buff_pos ] - prefix_start + 1 ;
2007-03-24 19:07:38 +00:00
if ( len < = PREFIX_MAX_LEN )
2005-09-20 13:26:39 +00:00
{
2012-05-09 09:33:42 +00:00
prefix . append ( prefix_start , len ) ;
2007-03-24 19:07:38 +00:00
}
else
{
2012-05-09 09:33:42 +00:00
prefix = wcstring ( & ellipsis_char , 1 ) ;
prefix . append ( prefix_start + ( len - PREFIX_MAX_LEN ) ) ;
2007-03-24 19:07:38 +00:00
}
2005-09-20 13:26:39 +00:00
2007-03-24 19:07:38 +00:00
{
int is_quoted ;
2005-09-20 13:26:39 +00:00
2007-03-24 19:07:38 +00:00
wchar_t quote ;
2012-02-06 09:45:16 +00:00
const wchar_t * buff = data - > command_line . c_str ( ) ;
get_param ( buff , data - > buff_pos , & quote , 0 , 0 , 0 ) ;
2007-03-24 19:07:38 +00:00
is_quoted = ( quote ! = L ' \0 ' ) ;
2011-12-27 03:18:46 +00:00
2009-02-22 20:28:52 +00:00
write_loop ( 1 , " \n " , 1 ) ;
2011-12-27 03:18:46 +00:00
2007-03-24 19:07:38 +00:00
run_pager ( prefix , is_quoted , comp ) ;
}
2012-03-25 10:00:38 +00:00
s_reset ( & data - > screen , true ) ;
2007-10-05 14:59:19 +00:00
reader_repaint ( ) ;
2005-09-20 13:26:39 +00:00
2012-02-10 02:59:15 +00:00
}
2007-02-28 21:43:27 +00:00
return len ;
2011-12-27 03:18:46 +00:00
2007-02-28 21:43:27 +00:00
2005-09-20 13:26:39 +00:00
}
/**
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 13:12:53 +00:00
/*
2006-10-16 15:32:26 +00:00
This should enable job control on fish , even if our parent process did
2006-02-05 13:12:53 +00:00
not enable it for us .
*/
2011-12-27 03:18:46 +00:00
/*
2009-03-01 02:14:41 +00:00
Check if we are in control of the terminal , so that we don ' t do
semi - expensive things like reset signal handlers unless we
really have to , which we often don ' t .
*/
if ( tcgetpgrp ( 0 ) ! = shell_pgid )
2005-09-20 13:26:39 +00:00
{
2009-03-01 02:14:41 +00:00
int block_count = 0 ;
int i ;
2011-12-27 03:18:46 +00:00
2009-03-01 02:14:41 +00:00
/*
Bummer , we are not in control of the terminal . Stop until
parent has given us control of it . Stopping in fish is a bit
of a challange , what with all the signal fidgeting , we need
to reset a bunch of signal state , making this coda a but
unobvious .
In theory , reseting signal handlers could cause us to miss
signal deliveries . In practice , this code should only be run
suring startup , when we ' re not waiting for any signals .
*/
2011-12-27 03:18:46 +00:00
while ( signal_is_blocked ( ) )
2009-03-01 02:14:41 +00:00
{
signal_unblock ( ) ;
block_count + + ;
}
signal_reset_handlers ( ) ;
2011-12-27 03:18:46 +00:00
2009-03-01 02:14:41 +00:00
/*
Ok , signal handlers are taken out of the picture . Stop ourself in a loop
until we are in control of the terminal .
*/
while ( tcgetpgrp ( 0 ) ! = shell_pgid )
{
killpg ( shell_pgid , SIGTTIN ) ;
}
2011-12-27 03:18:46 +00:00
2009-03-01 02:14:41 +00:00
signal_set_handlers ( ) ;
2011-12-27 03:18:46 +00:00
for ( i = 0 ; i < block_count ; i + + )
2009-03-01 02:14:41 +00:00
{
signal_block ( ) ;
}
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
}
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00: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 12:51:02 +00:00
_ ( L " Couldn't put the shell in its own process group " ) ) ;
2005-09-20 13:26:39 +00:00
wperror ( L " setpgid " ) ;
exit ( 1 ) ;
}
}
/* Grab control of the terminal. */
if ( tcsetpgrp ( STDIN_FILENO , shell_pgid ) )
{
debug ( 1 ,
2006-01-04 12:51:02 +00:00
_ ( L " Couldn't grab control of terminal " ) ) ;
2005-09-20 13:26:39 +00:00
wperror ( L " tcsetpgrp " ) ;
2012-02-28 23:11:46 +00:00
exit_without_destructors ( 1 ) ;
2005-09-20 13:26:39 +00:00
}
2006-01-30 19:53:10 +00:00
common_handle_winch ( 0 ) ;
2005-09-20 13:26:39 +00:00
if ( tcsetattr ( 0 , TCSANOW , & shell_modes ) ) /* set the new modes */
{
wperror ( L " tcsetattr " ) ;
}
2011-12-27 03:18:46 +00:00
/*
2006-02-05 13:12:53 +00:00
We need to know our own pid so we ' ll later know if we are a
2011-12-27 03:18:46 +00:00
fork
2006-02-05 13:12:53 +00:00
*/
2005-09-20 13:26:39 +00: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 " ) ;
2012-02-12 01:07:56 +00:00
set_color ( rgb_color_t : : reset ( ) , rgb_color_t : : reset ( ) ) ;
2005-09-20 13:26:39 +00:00
input_destroy ( ) ;
}
void reader_sanity_check ( )
{
2012-02-26 02:54:49 +00:00
if ( get_is_interactive ( ) )
2005-09-20 13:26:39 +00:00
{
2007-10-06 10:54:53 +00:00
if ( ! data )
sanity_lose ( ) ;
2012-02-06 09:53:03 +00:00
if ( ! ( data - > buff_pos < = data - > command_length ( ) ) )
2005-09-20 13:26:39 +00:00
sanity_lose ( ) ;
2012-02-06 19:34:49 +00:00
2012-02-18 02:24:47 +00:00
if ( data - > colors . size ( ) ! = data - > command_length ( ) )
2012-02-06 19:34:49 +00:00
sanity_lose ( ) ;
2012-02-18 02:24:47 +00:00
if ( data - > indents . size ( ) ! = data - > command_length ( ) )
2012-02-06 19:34:49 +00:00
sanity_lose ( ) ;
2005-09-20 13:26:39 +00:00
}
}
2012-02-02 22:27:13 +00:00
void reader_replace_current_token ( const wchar_t * new_token )
2005-09-20 13:26:39 +00:00
{
2012-02-06 08:57:43 +00:00
const wchar_t * begin , * end ;
2005-09-20 13:26:39 +00:00
int new_pos ;
2012-03-04 05:59:45 +00:00
/* Find current token */
2012-02-06 09:45:16 +00:00
const wchar_t * buff = data - > command_line . c_str ( ) ;
parse_util_token_extent ( ( wchar_t * ) buff , data - > buff_pos , & begin , & end , 0 , 0 ) ;
2005-09-20 13:26:39 +00:00
if ( ! begin | | ! end )
return ;
2012-03-04 05:59:45 +00:00
/* Make new string */
wcstring new_buff ( buff , begin - buff ) ;
new_buff . append ( new_token ) ;
new_buff . append ( end ) ;
2012-02-06 09:45:16 +00:00
new_pos = ( begin - buff ) + wcslen ( new_token ) ;
2012-03-04 05:59:45 +00:00
reader_set_buffer ( new_buff , new_pos ) ;
2005-09-20 13:26:39 +00:00
}
/**
Set the specified string from the history as the current buffer . Do
not modify prefix_width .
*/
2012-02-06 07:22:18 +00:00
static void handle_history ( const wcstring & new_str )
2005-09-20 13:26:39 +00:00
{
2012-02-06 09:45:16 +00:00
data - > command_line = new_str ;
2012-02-28 20:40:20 +00:00
data - > command_line_changed ( ) ;
2012-02-06 09:45:16 +00:00
data - > buff_pos = data - > command_line . size ( ) ;
2012-02-21 18:47:21 +00:00
reader_super_highlight_me_plenty ( data - > buff_pos ) ;
2012-02-06 07:22:18 +00:00
reader_repaint ( ) ;
2005-09-20 13:26:39 +00:00
}
2006-01-23 20:40:14 +00:00
/**
Reset the data structures associated with the token search
*/
2005-09-24 16:31:22 +00:00
static void reset_token_history ( )
{
2012-02-06 08:57:43 +00:00
const wchar_t * begin , * end ;
2012-02-06 09:45:16 +00:00
const wchar_t * buff = data - > command_line . c_str ( ) ;
parse_util_token_extent ( ( wchar_t * ) buff , data - > buff_pos , & begin , & end , 0 , 0 ) ;
2011-12-27 03:18:46 +00:00
2012-02-04 04:01:01 +00:00
data - > search_buff . clear ( ) ;
2005-09-24 16:31:22 +00:00
if ( begin )
{
2012-02-04 04:01:01 +00:00
data - > search_buff . append ( begin , end - begin ) ;
2005-09-24 16:31:22 +00:00
}
2006-01-30 19:53:10 +00:00
2005-09-24 16:31:22 +00:00
data - > token_history_pos = - 1 ;
data - > search_pos = 0 ;
2012-02-04 04:01:01 +00:00
data - > search_prev . clear ( ) ;
data - > search_prev . push_back ( data - > search_buff ) ;
2005-09-24 16:31:22 +00:00
}
2005-09-20 13:26:39 +00: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 )
{
2012-03-26 08:21:10 +00:00
/* Paranoia */
if ( ! data )
return ;
2012-02-02 22:27:13 +00:00
const wchar_t * str = 0 ;
2005-09-20 13:26:39 +00:00
int current_pos ;
tokenizer tok ;
2005-12-11 04:30:17 +00:00
if ( reset )
2005-09-20 13:26:39 +00:00
{
/*
Start a new token search using the current token
*/
2005-09-24 16:31:22 +00:00
reset_token_history ( ) ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
}
2005-12-12 18:30:55 +00:00
2005-09-20 13:26:39 +00:00
current_pos = data - > token_history_pos ;
2012-02-02 22:27:13 +00:00
if ( forward | | data - > search_pos + 1 < ( long ) data - > search_prev . size ( ) )
2005-09-20 13:26:39 +00:00
{
if ( forward )
{
if ( data - > search_pos > 0 )
{
data - > search_pos - - ;
}
2012-02-02 22:27:13 +00:00
str = data - > search_prev . at ( data - > search_pos ) . c_str ( ) ;
2005-09-20 13:26:39 +00:00
}
else
{
data - > search_pos + + ;
2012-02-02 22:27:13 +00:00
str = data - > search_prev . at ( data - > search_pos ) . c_str ( ) ;
2005-09-20 13:26:39 +00:00
}
reader_replace_current_token ( str ) ;
2012-02-21 18:47:21 +00:00
reader_super_highlight_me_plenty ( data - > buff_pos ) ;
2007-10-05 14:59:19 +00:00
reader_repaint ( ) ;
2005-09-20 13:26:39 +00:00
}
else
{
if ( current_pos = = - 1 )
{
2012-02-06 07:22:18 +00:00
data - > token_history_buff . clear ( ) ;
2006-11-17 23:37:26 +00:00
/*
Search for previous item that contains this substring
*/
2012-02-06 07:22:18 +00:00
if ( data - > history_search . go_backwards ( ) ) {
2012-02-16 08:24:27 +00:00
wcstring item = data - > history_search . current_string ( ) ;
data - > token_history_buff = data - > history_search . current_string ( ) ;
2012-02-06 00:42:24 +00:00
}
2012-02-06 07:22:18 +00:00
current_pos = data - > token_history_buff . size ( ) ;
2006-11-17 23:37:26 +00:00
2005-09-20 13:26:39 +00:00
}
2012-02-06 07:22:18 +00:00
if ( data - > token_history_buff . empty ( ) )
2005-09-20 13:26:39 +00:00
{
/*
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-12 18:30:55 +00:00
2012-02-04 04:01:01 +00:00
const wcstring & last = data - > search_prev . back ( ) ;
if ( data - > search_buff ! = last )
2005-09-20 13:26:39 +00:00
{
2012-02-04 04:01:01 +00:00
str = wcsdup ( data - > search_buff . c_str ( ) ) ;
2005-09-20 13:26:39 +00:00
}
else
{
2006-01-30 19:53:10 +00:00
return ;
}
2005-09-20 13:26:39 +00:00
}
else
{
2005-12-12 18:30:55 +00:00
2012-02-06 07:22:18 +00:00
//debug( 3, L"new '%ls'", data->token_history_buff.c_str() );
2005-12-12 18:30:55 +00:00
2012-02-06 07:22:18 +00:00
for ( tok_init ( & tok , data - > token_history_buff . c_str ( ) , TOK_ACCEPT_UNFINISHED ) ;
2005-09-20 13:26:39 +00:00
tok_has_next ( & tok ) ;
tok_next ( & tok ) )
{
switch ( tok_last_type ( & tok ) )
{
case TOK_STRING :
{
2012-02-04 04:01:01 +00:00
if ( wcsstr ( tok_last ( & tok ) , data - > search_buff . c_str ( ) ) )
2005-09-20 13:26:39 +00:00
{
2006-11-20 13:14:12 +00:00
//debug( 3, L"Found token at pos %d\n", tok_get_pos( &tok ) );
2005-09-20 13:26:39 +00:00
if ( tok_get_pos ( & tok ) > = current_pos )
{
break ;
}
2006-11-20 13:14:12 +00:00
//debug( 3, L"ok pos" );
2005-09-20 13:26:39 +00:00
2012-02-02 22:27:13 +00:00
const wcstring last_tok = tok_last ( & tok ) ;
if ( find ( data - > search_prev . begin ( ) , data - > search_prev . end ( ) , last_tok ) ! = data - > search_prev . end ( ) ) {
2005-09-20 13:26:39 +00:00
data - > token_history_pos = tok_get_pos ( & tok ) ;
str = wcsdup ( tok_last ( & tok ) ) ;
}
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
}
}
}
}
tok_destroy ( & tok ) ;
}
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
if ( str )
{
reader_replace_current_token ( str ) ;
2012-02-21 18:47:21 +00:00
reader_super_highlight_me_plenty ( data - > buff_pos ) ;
2007-10-05 14:59:19 +00:00
reader_repaint ( ) ;
2012-02-02 22:27:13 +00:00
data - > search_prev . push_back ( str ) ;
data - > search_pos = data - > search_prev . size ( ) - 1 ;
2005-09-20 13:26:39 +00:00
}
2006-11-17 13:06:12 +00:00
else if ( ! reader_interrupted ( ) )
2005-09-20 13:26:39 +00:00
{
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-30 19:53:10 +00:00
2005-09-20 13:26:39 +00: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 .
2008-01-13 16:47:47 +00:00
\ param new if the new kill item should be appended to the previous kill item or not .
2005-09-20 13:26:39 +00:00
*/
2011-12-27 03:18:46 +00:00
static void move_word ( int dir , int erase , int newv )
2005-09-20 13:26:39 +00:00
{
2012-01-15 06:48:53 +00:00
size_t end_buff_pos = data - > buff_pos ;
2005-09-20 13:26:39 +00:00
int step = dir ? 1 : - 1 ;
2006-10-12 16:13:17 +00:00
/*
Return if we are already at the edge
*/
if ( ! dir & & data - > buff_pos = = 0 )
{
return ;
}
2011-12-27 03:18:46 +00:00
2012-02-06 09:53:03 +00:00
if ( dir & & data - > buff_pos = = data - > command_length ( ) )
2006-10-12 16:13:17 +00:00
{
return ;
}
2011-12-27 03:18:46 +00:00
2006-08-31 00:45:25 +00: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 .
*/
2012-02-06 09:53:03 +00:00
if ( ! dir & & ( end_buff_pos = = data - > command_length ( ) ) )
2006-08-31 00:45:25 +00:00
{
if ( ! end_buff_pos )
return ;
2011-12-27 03:18:46 +00:00
2006-08-31 00:45:25 +00:00
end_buff_pos - - ;
}
2011-12-27 03:18:46 +00:00
2006-10-12 16:13:17 +00:00
/*
When moving left , ignore the character under the cursor
*/
if ( ! dir )
{
end_buff_pos + = 2 * step ;
}
2011-12-27 03:18:46 +00:00
2006-09-16 23:05:32 +00:00
/*
Remove all whitespace characters before finding a word
*/
2006-08-31 00:45:25 +00:00
while ( 1 )
2005-09-20 13:26:39 +00:00
{
2006-08-31 00:45:25 +00:00
wchar_t c ;
2005-09-20 13:26:39 +00:00
if ( ! dir )
{
2006-10-16 14:39:12 +00:00
if ( end_buff_pos < = 0 )
2005-09-20 13:26:39 +00:00
break ;
}
else
{
2012-02-06 09:53:03 +00:00
if ( end_buff_pos > = data - > command_length ( ) )
2005-09-20 13:26:39 +00:00
break ;
}
2006-09-16 23:05:32 +00:00
/*
Always eat at least one character
*/
if ( end_buff_pos ! = data - > buff_pos )
2006-08-31 00:45:25 +00:00
{
2011-12-27 03:18:46 +00:00
2012-02-06 09:45:16 +00:00
c = data - > command_line . c_str ( ) [ end_buff_pos ] ;
2011-12-27 03:18:46 +00:00
2006-09-16 23:05:32 +00:00
if ( ! iswspace ( c ) )
{
break ;
}
2006-08-31 00:45:25 +00:00
}
2011-12-27 03:18:46 +00:00
2006-08-31 00:45:25 +00:00
end_buff_pos + = step ;
2006-09-16 23:05:32 +00:00
2006-08-31 00:45:25 +00:00
}
2011-12-27 03:18:46 +00:00
2006-09-16 23:05:32 +00:00
/*
Remove until we find a character that is not alphanumeric
*/
2006-08-31 00:45:25 +00:00
while ( 1 )
{
wchar_t c ;
2011-12-27 03:18:46 +00:00
2006-08-31 00:45:25 +00:00
if ( ! dir )
{
2006-10-16 14:39:12 +00:00
if ( end_buff_pos < = 0 )
2006-08-31 00:45:25 +00:00
break ;
}
else
{
2012-02-06 09:53:03 +00:00
if ( end_buff_pos > = data - > command_length ( ) )
2006-08-31 00:45:25 +00:00
break ;
2005-09-20 13:26:39 +00:00
}
2011-12-27 03:18:46 +00:00
2012-02-06 09:45:16 +00:00
c = data - > command_line . c_str ( ) [ end_buff_pos ] ;
2011-12-27 03:18:46 +00:00
2006-08-31 00:45:25 +00:00
if ( ! iswalnum ( c ) )
{
/*
2006-10-12 16:13:17 +00:00
Don ' t gobble the boundary character when moving to the
right
2006-08-31 00:45:25 +00:00
*/
2006-10-12 16:13:17 +00:00
if ( ! dir )
2006-08-31 00:45:25 +00:00
end_buff_pos - = step ;
break ;
}
end_buff_pos + = step ;
2005-09-20 13:26:39 +00:00
}
2006-10-12 16:13:17 +00:00
/*
Make sure we move at least one character
*/
if ( end_buff_pos = = data - > buff_pos )
{
end_buff_pos + = step ;
}
2006-10-16 14:39:12 +00:00
/*
Make sure we don ' t move beyond begining or end of buffer
*/
2012-02-06 09:53:03 +00:00
end_buff_pos = maxi ( 0 , mini ( end_buff_pos , data - > command_length ( ) ) ) ;
2011-12-27 03:18:46 +00:00
2006-10-16 15:32:26 +00:00
2006-10-12 16:13:17 +00:00
2005-09-20 13:26:39 +00: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 );
2011-12-27 03:18:46 +00:00
2012-02-06 09:45:16 +00:00
reader_kill ( first_char , remove_count , dir ? KILL_APPEND : KILL_PREPEND , newv ) ;
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
}
else
{
2006-10-01 16:02:58 +00:00
data - > buff_pos = end_buff_pos ;
2007-10-05 14:59:19 +00:00
reader_repaint ( ) ;
2005-09-20 13:26:39 +00:00
}
}
2012-02-06 09:45:16 +00:00
const wchar_t * reader_get_buffer ( void )
2005-09-20 13:26:39 +00:00
{
2011-12-27 03:18:46 +00:00
ASSERT_IS_MAIN_THREAD ( ) ;
2012-02-06 09:45:16 +00:00
return data ? data - > command_line . c_str ( ) : NULL ;
2005-09-20 13:26:39 +00:00
}
2012-02-06 00:42:24 +00:00
history_t * reader_get_history ( void ) {
ASSERT_IS_MAIN_THREAD ( ) ;
return data ? data - > history : NULL ;
}
2012-02-22 19:07:34 +00:00
void reader_set_buffer ( const wcstring & b , int p )
2005-09-20 13:26:39 +00:00
{
if ( ! data )
return ;
2012-02-06 09:45:16 +00:00
/* Callers like to pass us pointers into ourselves, so be careful! I don't know if we can use operator= with a pointer to our interior, so use an intermediate. */
2012-02-22 19:07:34 +00:00
size_t l = b . size ( ) ;
data - > command_line = b ;
2012-02-28 20:40:20 +00:00
data - > command_line_changed ( ) ;
2005-09-20 13:26:39 +00:00
if ( p > = 0 )
{
2006-10-04 21:39:48 +00:00
data - > buff_pos = mini ( p , l ) ;
2005-09-20 13:26:39 +00:00
}
else
{
data - > buff_pos = l ;
}
2009-02-21 15:44:20 +00:00
data - > search_mode = NO_SEARCH ;
2012-02-04 04:01:01 +00:00
data - > search_buff . clear ( ) ;
2012-02-06 00:42:24 +00:00
data - > history_search . go_to_end ( ) ;
2009-02-21 15:44:20 +00:00
2012-02-21 18:47:21 +00:00
reader_super_highlight_me_plenty ( data - > buff_pos ) ;
2007-10-05 14:59:19 +00:00
reader_repaint_needed ( ) ;
2005-09-20 13:26:39 +00:00
}
int reader_get_cursor_pos ( )
{
if ( ! data )
return - 1 ;
return data - > buff_pos ;
}
2009-11-24 15:28:42 +00:00
# define ENV_CMD_DURATION L"CMD_DURATION"
void set_env_cmd_duration ( struct timeval * after , struct timeval * before )
{
time_t secs = after - > tv_sec - before - > tv_sec ;
suseconds_t usecs = after - > tv_usec - before - > tv_usec ;
wchar_t buf [ 16 ] ;
if ( after - > tv_usec < before - > tv_usec ) {
usecs + = 1000000 ;
secs - = 1 ;
}
if ( secs < 1 ) {
env_remove ( ENV_CMD_DURATION , 0 ) ;
} else {
if ( secs < 10 ) { // 10 secs
swprintf ( buf , 16 , L " %lu.%02us " , secs , usecs / 10000 ) ;
} else if ( secs < 60 ) { // 1 min
swprintf ( buf , 16 , L " %lu.%01us " , secs , usecs / 100000 ) ;
} else if ( secs < 600 ) { // 10 mins
swprintf ( buf , 16 , L " %lum %lu.%01us " , secs / 60 , secs % 60 , usecs / 100000 ) ;
} else if ( secs < 5400 ) { // 1.5 hours
swprintf ( buf , 16 , L " %lum %lus " , secs / 60 , secs % 60 ) ;
} else {
swprintf ( buf , 16 , L " %.1fh " , secs / 3600.0f ) ;
}
env_set ( ENV_CMD_DURATION , buf , ENV_EXPORT ) ;
}
}
2005-09-20 13:26:39 +00:00
2012-01-23 05:40:08 +00:00
void reader_run_command ( parser_t & parser , const wchar_t * cmd )
2005-09-20 13:26:39 +00:00
{
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
wchar_t * ft ;
2009-11-24 15:28:42 +00:00
struct timeval time_before , time_after ;
2005-09-20 13:26:39 +00:00
ft = tok_first ( cmd ) ;
if ( ft ! = 0 )
env_set ( L " _ " , ft , ENV_GLOBAL ) ;
free ( ft ) ;
reader_write_title ( ) ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
term_donate ( ) ;
2006-01-30 19:53:10 +00:00
2009-11-24 15:28:42 +00:00
gettimeofday ( & time_before , NULL ) ;
2012-01-23 05:40:08 +00:00
parser . eval ( cmd , 0 , TOP ) ;
2005-10-11 19:23:43 +00:00
job_reap ( 1 ) ;
2006-01-30 19:53:10 +00:00
2009-11-24 15:28:42 +00:00
gettimeofday ( & time_after , NULL ) ;
set_env_cmd_duration ( & time_after , & time_before ) ;
2005-09-20 13:26:39 +00:00
term_steal ( ) ;
2006-05-27 13:49:18 +00:00
env_set ( L " _ " , program_name , ENV_GLOBAL ) ;
2005-09-20 13:26:39 +00:00
# ifdef HAVE__PROC_SELF_STAT
proc_update_jiffies ( ) ;
# endif
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
}
2012-02-06 09:45:16 +00:00
int reader_shell_test ( const wchar_t * b )
2005-09-20 13:26:39 +00:00
{
2012-01-23 05:40:08 +00:00
int res = parser_t : : principal_parser ( ) . test ( b , 0 , 0 , 0 ) ;
2011-12-27 03:18:46 +00:00
2006-10-01 16:02:58 +00:00
if ( res & PARSER_TEST_ERROR )
2006-05-27 11:14:56 +00:00
{
2012-02-22 18:51:06 +00:00
wcstring sb ;
2006-10-01 16:02:58 +00:00
int tmp [ 1 ] ;
2006-10-07 00:56:25 +00:00
int tmp2 [ 1 ] ;
2011-12-27 03:18:46 +00:00
2006-10-07 00:56:25 +00:00
s_write ( & data - > screen , L " " , L " " , tmp , tmp2 , 0 ) ;
2011-12-27 03:18:46 +00:00
2012-01-23 05:40:08 +00:00
parser_t : : principal_parser ( ) . test ( b , 0 , & sb , L " fish " ) ;
2012-02-22 18:51:06 +00:00
fwprintf ( stderr , L " %ls " , sb . c_str ( ) ) ;
2006-05-27 11:14:56 +00:00
}
2006-10-01 16:02:58 +00:00
return res ;
2005-09-20 13:26:39 +00: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 .
*/
2012-02-06 09:45:16 +00:00
static int default_test ( const wchar_t * b )
2005-09-20 13:26:39 +00:00
{
return 0 ;
}
2011-12-27 03:18:46 +00:00
void reader_push ( const wchar_t * name )
2005-09-20 13:26:39 +00:00
{
2012-02-01 04:03:48 +00:00
// use something nasty which guarantees value initialization (that is, all fields zero)
reader_data_t zerod = { } ;
reader_data_t * n = new reader_data_t ( zerod ) ;
2011-12-28 02:41:38 +00:00
2012-02-06 00:42:24 +00:00
n - > history = & history_t : : history_with_name ( name ) ;
2011-12-28 20:36:47 +00:00
n - > app_name = name ;
2005-09-20 13:26:39 +00:00
n - > next = data ;
2006-10-01 16:02:58 +00:00
2005-09-20 13:26:39 +00:00
data = n ;
2006-10-01 16:02:58 +00:00
2012-02-28 20:40:20 +00:00
data - > command_line_changed ( ) ;
2005-09-20 13:26:39 +00:00
if ( data - > next = = 0 )
{
reader_interactive_init ( ) ;
}
2006-10-16 15:32:26 +00:00
exec_prompt ( ) ;
2005-09-20 13:26:39 +00:00
reader_set_highlight_function ( & highlight_universal ) ;
reader_set_test_function ( & default_test ) ;
reader_set_prompt ( L " " ) ;
2012-02-06 00:42:24 +00:00
//history_set_mode( name );
2005-09-20 13:26:39 +00:00
}
void reader_pop ( )
{
reader_data_t * n = data ;
if ( data = = 0 )
{
2006-01-04 12:51:02 +00:00
debug ( 0 , _ ( L " Pop null reader block " ) ) ;
2005-09-20 13:26:39 +00:00
sanity_lose ( ) ;
return ;
}
data = data - > next ;
2011-12-27 03:18:46 +00:00
2012-02-01 04:03:48 +00:00
/* Invoke the destructor to balance our new */
delete n ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
if ( data = = 0 )
{
reader_interactive_destroy ( ) ;
}
else
{
2007-01-29 17:45:01 +00:00
end_loop = 0 ;
2012-02-06 00:42:24 +00:00
//history_set_mode( data->app_name.c_str() );
2012-03-25 10:00:38 +00:00
s_reset ( & data - > screen , true ) ;
2005-09-20 13:26:39 +00:00
}
}
2011-12-27 03:18:46 +00:00
void reader_set_prompt ( const wchar_t * new_prompt )
2005-09-20 13:26:39 +00:00
{
2011-12-28 20:36:47 +00:00
data - > prompt = new_prompt ;
2005-09-20 13:26:39 +00:00
}
2012-02-24 20:13:35 +00:00
void reader_set_complete_function ( complete_function_t f )
2005-09-20 13:26:39 +00:00
{
data - > complete_func = f ;
}
2011-12-27 03:18:46 +00:00
void reader_set_highlight_function ( highlight_function_t func )
2005-09-20 13:26:39 +00:00
{
2011-12-27 03:18:46 +00:00
data - > highlight_function = func ;
2005-09-20 13:26:39 +00:00
}
2012-02-06 09:45:16 +00:00
void reader_set_test_function ( int ( * f ) ( const wchar_t * ) )
2005-09-20 13:26:39 +00:00
{
data - > test_func = f ;
}
2011-12-27 03:18:46 +00:00
/** A class as the context pointer for a background (threaded) highlight operation. */
2012-02-04 02:39:41 +00:00
class background_highlight_context_t {
2011-12-27 03:18:46 +00:00
public :
2012-02-28 20:40:20 +00:00
/** The string to highlight */
2012-02-06 09:45:16 +00:00
const wcstring string_to_highlight ;
2011-12-27 03:18:46 +00:00
2012-02-18 02:24:47 +00:00
/** Color buffer */
std : : vector < color_t > colors ;
2011-12-27 03:18:46 +00:00
2012-02-28 20:40:20 +00:00
/** The position to use for bracket matching */
2012-02-04 02:39:41 +00:00
const int match_highlight_pos ;
2011-12-27 03:18:46 +00:00
2012-02-28 20:40:20 +00:00
/** Function for syntax highlighting */
2012-02-04 02:39:41 +00:00
const highlight_function_t highlight_function ;
2011-12-27 03:18:46 +00:00
2012-02-28 20:40:20 +00:00
/** Environment variables */
2012-02-04 02:39:41 +00:00
const env_vars vars ;
2011-12-27 03:18:46 +00:00
2012-02-28 20:40:20 +00:00
/** When the request was made */
2012-02-04 02:39:41 +00:00
const double when ;
2011-12-27 03:18:46 +00:00
2012-02-28 20:40:20 +00:00
/** Gen count at the time the request was made */
const unsigned int generation_count ;
2012-02-18 02:24:47 +00:00
background_highlight_context_t ( const wcstring & pbuff , int phighlight_pos , highlight_function_t phighlight_func ) :
2012-02-06 09:45:16 +00:00
string_to_highlight ( pbuff ) ,
2012-02-04 02:39:41 +00:00
match_highlight_pos ( phighlight_pos ) ,
highlight_function ( phighlight_func ) ,
vars ( env_vars : : highlighting_keys ) ,
2012-02-28 20:40:20 +00:00
when ( timef ( ) ) ,
generation_count ( s_generation_count )
2011-12-27 03:18:46 +00:00
{
2012-02-18 02:24:47 +00:00
colors . resize ( string_to_highlight . size ( ) , 0 ) ;
2011-12-27 03:18:46 +00:00
}
2012-02-18 02:24:47 +00:00
int threaded_highlight ( ) {
2012-02-28 20:40:20 +00:00
if ( generation_count ! = s_generation_count ) {
// The gen count has changed, so don't do anything
return 0 ;
}
2012-02-18 02:24:47 +00:00
const wchar_t * delayer = vars . get ( L " HIGHLIGHT_DELAY " ) ;
double secDelay = 0 ;
if ( delayer ) {
wcstring tmp = delayer ;
secDelay = from_string < double > ( tmp ) ;
}
if ( secDelay > 0 ) usleep ( ( useconds_t ) ( secDelay * 1E6 ) ) ;
//write(0, "Start", 5);
if ( ! string_to_highlight . empty ( ) ) {
2012-02-21 19:45:13 +00:00
highlight_function ( string_to_highlight . c_str ( ) , colors , match_highlight_pos , NULL /* error */ , vars ) ;
2012-02-18 02:24:47 +00:00
}
//write(0, "End", 3);
return 0 ;
2011-12-27 03:18:46 +00:00
}
} ;
2012-02-04 04:05:50 +00:00
/* Called to set the highlight flag for search results */
static void highlight_search ( void ) {
2012-02-06 07:22:18 +00:00
if ( ! data - > search_buff . empty ( ) & & ! data - > history_search . is_at_end ( ) )
2012-02-04 04:05:50 +00:00
{
2012-02-06 09:45:16 +00:00
const wchar_t * buff = data - > command_line . c_str ( ) ;
const wchar_t * match = wcsstr ( buff , data - > search_buff . c_str ( ) ) ;
2012-02-04 04:05:50 +00:00
if ( match )
{
2012-02-06 09:45:16 +00:00
int start = match - buff ;
2012-02-04 04:05:50 +00:00
int count = data - > search_buff . size ( ) ;
int i ;
for ( i = 0 ; i < count ; i + + )
{
2012-02-06 18:52:13 +00:00
data - > colors . at ( start + i ) | = HIGHLIGHT_SEARCH_MATCH < < 16 ;
2012-02-04 04:05:50 +00:00
}
}
}
}
2012-02-15 19:33:41 +00:00
static void highlight_complete ( background_highlight_context_t * ctx , int result ) {
2012-02-06 00:42:24 +00:00
ASSERT_IS_MAIN_THREAD ( ) ;
2012-02-06 09:45:16 +00:00
if ( ctx - > string_to_highlight = = data - > command_line ) {
2011-12-27 03:18:46 +00:00
/* The data hasn't changed, so swap in our colors */
2012-02-18 02:24:47 +00:00
assert ( ctx - > colors . size ( ) = = data - > command_length ( ) ) ;
data - > colors . swap ( ctx - > colors ) ;
2012-02-06 18:52:13 +00:00
2012-02-06 19:34:49 +00:00
2011-12-27 03:18:46 +00:00
//data->repaint_needed = 1;
//s_reset( &data->screen, 1 );
2012-02-06 19:34:49 +00:00
sanity_check ( ) ;
2012-02-04 04:05:50 +00:00
highlight_search ( ) ;
2011-12-27 03:18:46 +00:00
reader_repaint ( ) ;
}
/* Free our context */
delete ctx ;
}
2012-02-15 19:33:41 +00:00
static int threaded_highlight ( background_highlight_context_t * ctx ) {
2012-02-18 02:24:47 +00:00
return ctx - > threaded_highlight ( ) ;
2011-12-27 03:18:46 +00:00
}
2005-09-20 13:26:39 +00:00
/**
Call specified external highlighting function and then do search
2006-09-24 17:57:23 +00:00
highlighting . Lastly , clear the background color under the cursor
2006-10-22 09:40:18 +00:00
to avoid repaint issues on terminals where e . g . syntax highligthing
maykes characters under the sursor unreadable .
2006-09-24 17:57:23 +00: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-16 15:32:26 +00: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 13:26:39 +00:00
*/
2012-02-21 18:47:21 +00:00
static void reader_super_highlight_me_plenty ( int match_highlight_pos )
2005-09-20 13:26:39 +00:00
{
2012-02-06 19:34:49 +00:00
reader_sanity_check ( ) ;
2012-02-18 02:24:47 +00:00
background_highlight_context_t * ctx = new background_highlight_context_t ( data - > command_line , match_highlight_pos , data - > highlight_function ) ;
2011-12-27 03:18:46 +00:00
iothread_perform ( threaded_highlight , highlight_complete , ctx ) ;
2012-02-04 04:05:50 +00:00
highlight_search ( ) ;
2012-02-16 08:24:27 +00:00
/* Here's a hack. Check to see if our autosuggestion still applies; if so, don't recompute it. Since the autosuggestion computation is asynchronous, this avoids "flashing" as you type into the autosuggestion. */
const wcstring & cmd = data - > command_line , & suggest = data - > autosuggestion ;
2012-03-02 01:31:45 +00:00
if ( can_autosuggest ( ) & & ! suggest . empty ( ) & & string_prefixes_string_case_insensitive ( cmd , suggest ) ) {
2012-02-16 08:24:27 +00:00
/* The autosuggestion is still reasonable, so do nothing */
} else {
update_autosuggestion ( ) ;
}
2005-09-20 13:26:39 +00:00
}
int exit_status ( )
{
2012-02-26 02:54:49 +00:00
if ( get_is_interactive ( ) )
2012-02-28 02:43:24 +00:00
return job_list_is_empty ( ) & & data - > end_loop ;
2005-09-20 13:26:39 +00:00
else
return end_loop ;
}
2007-09-26 09:01:59 +00:00
/**
This function is called when the main loop notices that end_loop
has been set while in interactive mode . It checks if it is ok to
exit .
*/
static void handle_end_loop ( )
{
job_t * j ;
int job_count = 0 ;
int is_breakpoint = 0 ;
block_t * b ;
2012-01-23 05:40:08 +00:00
parser_t & parser = parser_t : : principal_parser ( ) ;
for ( b = parser . current_block ;
2011-12-27 03:18:46 +00:00
b ;
2007-09-26 09:01:59 +00:00
b = b - > outer )
{
if ( b - > type = = BREAKPOINT )
{
is_breakpoint = 1 ;
break ;
}
}
2011-12-27 03:18:46 +00:00
2012-01-30 00:36:21 +00:00
job_iterator_t jobs ;
while ( ( j = jobs . next ( ) ) )
2007-09-26 09:01:59 +00:00
{
if ( ! job_is_completed ( j ) )
{
job_count + + ;
break ;
}
}
2011-12-27 03:18:46 +00:00
2007-09-26 09:01:59 +00:00
if ( ! reader_exit_forced ( ) & & ! data - > prev_end_loop & & job_count & & ! is_breakpoint )
{
2009-02-01 15:16:01 +00:00
writestr ( _ ( L " There are stopped jobs. A second attempt to exit will enforce their termination. \n " ) ) ;
2011-12-27 03:18:46 +00:00
2007-09-26 09:01:59 +00:00
reader_exit ( 0 , 0 ) ;
data - > prev_end_loop = 1 ;
}
else
{
if ( ! isatty ( 0 ) )
{
/*
We already know that stdin is a tty since we ' re
in interactive mode . If isatty returns false , it
2011-12-27 03:18:46 +00:00
means stdin must have been closed .
2007-09-26 09:01:59 +00:00
*/
2012-01-30 00:36:21 +00:00
job_iterator_t jobs ;
while ( ( j = jobs . next ( ) ) )
2007-09-26 09:01:59 +00:00
{
if ( ! job_is_completed ( j ) )
{
2011-12-27 03:18:46 +00:00
job_signal ( j , SIGHUP ) ;
2007-09-26 09:01:59 +00:00
}
}
}
}
}
2005-09-20 13:26:39 +00:00
/**
Read interactively . Read input from stdin while providing editing
facilities .
*/
static int read_i ( )
{
2007-08-19 16:42:30 +00:00
event_fire_generic ( L " fish_prompt " ) ;
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
reader_push ( L " fish " ) ;
2012-02-06 00:42:24 +00:00
reader_set_complete_function ( & complete ) ;
2005-09-20 13:26:39 +00:00
reader_set_highlight_function ( & highlight_shell ) ;
2007-01-29 16:26:24 +00:00
reader_set_test_function ( & reader_shell_test ) ;
2012-01-23 05:40:08 +00:00
parser_t & parser = parser_t : : principal_parser ( ) ;
2006-05-14 10:16:23 +00:00
data - > prev_end_loop = 0 ;
2005-09-20 13:26:39 +00:00
while ( ( ! data - > end_loop ) & & ( ! sanity_check ( ) ) )
{
2012-02-06 09:45:16 +00:00
const wchar_t * tmp ;
2005-09-20 13:26:39 +00:00
2007-01-15 17:51:44 +00:00
if ( function_exists ( PROMPT_FUNCTION_NAME ) )
reader_set_prompt ( PROMPT_FUNCTION_NAME ) ;
2005-09-20 13:26:39 +00:00
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-30 19:53:10 +00:00
2011-12-27 03:18:46 +00:00
2006-05-14 10:16:23 +00:00
tmp = reader_readline ( ) ;
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
if ( data - > end_loop )
{
2007-09-26 09:01:59 +00:00
handle_end_loop ( ) ;
2005-09-20 13:26:39 +00:00
}
2007-01-15 17:53:46 +00:00
else if ( tmp )
2005-09-20 13:26:39 +00:00
{
2006-05-14 10:16:23 +00:00
tmp = wcsdup ( tmp ) ;
2011-12-27 03:18:46 +00:00
2012-02-06 09:53:03 +00:00
data - > buff_pos = 0 ;
data - > command_line . clear ( ) ;
2012-02-28 20:40:20 +00:00
data - > command_line_changed ( ) ;
2012-01-23 05:40:08 +00:00
reader_run_command ( parser , tmp ) ;
2012-02-06 09:45:16 +00:00
free ( ( void * ) tmp ) ;
2007-09-26 09:01:59 +00:00
if ( data - > end_loop )
{
handle_end_loop ( ) ;
}
else
{
data - > prev_end_loop = 0 ;
}
2005-09-20 13:26:39 +00:00
}
2011-12-27 03:18:46 +00:00
2006-01-18 12:42:48 +00:00
2005-09-20 13:26:39 +00:00
}
reader_pop ( ) ;
return 0 ;
}
2005-10-20 12:06:10 +00:00
/**
Test if there are bytes available for reading on the specified file
descriptor
*/
2005-10-13 14:08:33 +00:00
static int can_read ( int fd )
{
2005-10-25 11:22:47 +00:00
struct timeval can_read_timeout = { 0 , 0 } ;
fd_set fds ;
2006-01-30 19:53:10 +00:00
2005-10-25 11:22:47 +00:00
FD_ZERO ( & fds ) ;
FD_SET ( fd , & fds ) ;
return select ( fd + 1 , & fds , 0 , 0 , & can_read_timeout ) = = 1 ;
2005-10-13 14:08:33 +00:00
}
2005-09-20 13:26:39 +00:00
2005-10-20 11:27:54 +00:00
/**
2005-10-20 12:06:10 +00:00
Test if the specified character is in the private use area that
fish uses to store internal characters
2005-10-20 11:27:54 +00:00
*/
static int wchar_private ( wchar_t c )
{
return ( ( c > = 0xe000 ) & & ( c < = 0xf8ff ) ) ;
}
2005-09-20 13:26:39 +00:00
2006-11-20 02:19:34 +00:00
/**
Test if the specified character in the specified string is
backslashed .
*/
static int is_backslashed ( const wchar_t * str , int pos )
{
int count = 0 ;
int i ;
2011-12-27 03:18:46 +00:00
2006-11-20 02:19:34 +00:00
for ( i = pos - 1 ; i > = 0 ; i - - )
{
if ( str [ i ] ! = L ' \\ ' )
break ;
2011-12-27 03:18:46 +00:00
2006-11-20 02:19:34 +00:00
count + + ;
}
return count % 2 ;
}
2012-02-06 09:45:16 +00:00
const wchar_t * reader_readline ( )
2005-09-20 13:26:39 +00:00
{
2005-10-11 19:48:31 +00:00
wint_t c ;
2005-09-20 13:26:39 +00:00
int i ;
int last_char = 0 , yank = 0 ;
2012-01-14 11:41:50 +00:00
const wchar_t * yank_str ;
2012-01-16 16:56:47 +00:00
std : : vector < completion_t > comp ;
2005-09-20 13:26:39 +00:00
int comp_empty = 1 ;
int finished = 0 ;
struct termios old_modes ;
2012-02-04 04:01:01 +00:00
data - > search_buff . clear ( ) ;
2007-09-21 14:05:49 +00:00
data - > search_mode = NO_SEARCH ;
2011-12-27 03:18:46 +00:00
2006-10-16 15:32:26 +00:00
exec_prompt ( ) ;
2005-09-20 13:26:39 +00:00
2012-02-21 18:47:21 +00:00
reader_super_highlight_me_plenty ( data - > buff_pos ) ;
2012-03-25 10:00:38 +00:00
s_reset ( & data - > screen , true ) ;
2007-10-05 14:59:19 +00:00
reader_repaint ( ) ;
2005-09-20 13:26:39 +00:00
2011-12-27 03:18:46 +00:00
/*
2006-11-02 13:50:19 +00:00
get the current terminal modes . These will be restored when the
function returns .
*/
2011-12-27 03:18:46 +00:00
tcgetattr ( 0 , & old_modes ) ;
2006-11-02 13:50:19 +00:00
/* set the new modes */
2006-11-20 13:14:12 +00:00
if ( tcsetattr ( 0 , TCSANOW , & shell_modes ) )
2005-12-30 16:29:19 +00:00
{
2006-11-20 13:14:12 +00:00
wperror ( L " tcsetattr " ) ;
2005-09-20 13:26:39 +00:00
}
while ( ! finished & & ! data - > end_loop )
{
/*
2005-10-11 19:48:31 +00: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 13:26:39 +00:00
*/
2005-10-13 14:08:33 +00:00
while ( 1 )
{
2007-01-31 23:58:10 +00:00
int was_interactive_read = is_interactive_read ;
is_interactive_read = 1 ;
2005-10-13 14:08:33 +00:00
c = input_readch ( ) ;
2007-01-31 23:58:10 +00:00
is_interactive_read = was_interactive_read ;
2011-12-27 03:18:46 +00:00
2005-10-20 11:27:54 +00:00
if ( ( ( ! wchar_private ( c ) ) ) & & ( c > 31 ) & & ( c ! = 127 ) )
2005-10-13 14:08:33 +00:00
{
if ( can_read ( 0 ) )
{
wchar_t arr [ READAHEAD_MAX + 1 ] ;
int i ;
2006-01-30 19:53:10 +00:00
2005-10-13 14:08:33 +00:00
memset ( arr , 0 , sizeof ( arr ) ) ;
arr [ 0 ] = c ;
2006-01-30 19:53:10 +00:00
2005-10-13 14:08:33 +00:00
for ( i = 1 ; i < READAHEAD_MAX ; i + + )
{
2006-01-30 19:53:10 +00:00
2005-10-13 14:08:33 +00:00
if ( ! can_read ( 0 ) )
{
c = 0 ;
break ;
}
c = input_readch ( ) ;
2005-10-20 11:27:54 +00:00
if ( ( ! wchar_private ( c ) ) & & ( c > 31 ) & & ( c ! = 127 ) )
2005-10-13 14:08:33 +00:00
{
arr [ i ] = c ;
c = 0 ;
}
else
break ;
}
2006-01-30 19:53:10 +00:00
2012-02-06 10:20:50 +00:00
insert_string ( arr ) ;
2006-01-30 19:53:10 +00:00
2005-10-13 14:08:33 +00:00
}
}
2006-01-30 19:53:10 +00:00
2005-10-13 14:08:33 +00:00
if ( c ! = 0 )
break ;
}
2007-02-09 09:33:50 +00:00
/*
2007-03-24 19:07:38 +00:00
if ( ( last_char = = R_COMPLETE ) & & ( c ! = R_COMPLETE ) & & ( ! comp_empty ) )
{
halloc_destroy ( comp ) ;
comp = 0 ;
}
2007-02-09 09:33:50 +00:00
*/
2005-09-20 13:26:39 +00:00
if ( last_char ! = R_YANK & & last_char ! = R_YANK_POP )
yank = 0 ;
2012-02-06 09:45:16 +00:00
const wchar_t * buff = data - > command_line . c_str ( ) ;
2005-12-13 10:18:59 +00:00
switch ( c )
2005-09-20 13:26:39 +00:00
{
/* go to beginning of line*/
case R_BEGINNING_OF_LINE :
2006-10-31 22:01:49 +00:00
{
2011-12-27 03:18:46 +00:00
while ( ( data - > buff_pos > 0 ) & &
2012-02-06 09:45:16 +00:00
( buff [ data - > buff_pos - 1 ] ! = L ' \n ' ) )
2006-11-02 13:50:19 +00:00
{
2006-10-31 22:01:49 +00:00
data - > buff_pos - - ;
2006-11-02 13:50:19 +00:00
}
2011-12-27 03:18:46 +00:00
2007-10-05 14:59:19 +00:00
reader_repaint ( ) ;
2006-10-31 22:01:49 +00:00
break ;
}
case R_END_OF_LINE :
{
2012-02-06 09:45:16 +00:00
while ( buff [ data - > buff_pos ] & &
buff [ data - > buff_pos ] ! = L ' \n ' )
2006-11-02 13:50:19 +00:00
{
2006-10-31 22:01:49 +00:00
data - > buff_pos + + ;
2006-11-02 13:50:19 +00:00
}
2011-12-27 03:18:46 +00:00
2007-10-05 14:59:19 +00:00
reader_repaint ( ) ;
2006-10-31 22:01:49 +00:00
break ;
}
2011-12-27 03:18:46 +00:00
2006-10-31 22:01:49 +00:00
case R_BEGINNING_OF_BUFFER :
2005-09-20 13:26:39 +00:00
{
data - > buff_pos = 0 ;
2007-10-05 14:59:19 +00:00
reader_repaint ( ) ;
2005-09-20 13:26:39 +00:00
break ;
}
/* go to EOL*/
2006-10-31 22:01:49 +00:00
case R_END_OF_BUFFER :
2005-09-20 13:26:39 +00:00
{
2012-02-06 09:53:03 +00:00
data - > buff_pos = data - > command_length ( ) ;
2005-09-20 13:26:39 +00:00
2007-10-05 14:59:19 +00:00
reader_repaint ( ) ;
2005-09-20 13:26:39 +00:00
break ;
}
case R_NULL :
{
2012-03-25 10:00:38 +00:00
reader_repaint_if_needed ( ) ;
2006-12-12 10:13:48 +00:00
break ;
}
case R_REPAINT :
{
exec_prompt ( ) ;
2009-02-22 20:28:52 +00:00
write_loop ( 1 , " \r " , 1 ) ;
2012-03-25 10:00:38 +00:00
s_reset ( & data - > screen , false ) ;
2007-10-05 14:59:19 +00:00
reader_repaint ( ) ;
2006-10-04 21:45:02 +00:00
break ;
}
2006-05-14 10:16:23 +00:00
case R_EOF :
{
exit_forced = 1 ;
data - > end_loop = 1 ;
break ;
}
2005-09-20 13:26:39 +00:00
/* complete */
case R_COMPLETE :
{
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
if ( ! data - > complete_func )
break ;
2006-11-30 23:58:52 +00:00
if ( comp_empty | | last_char ! = R_COMPLETE )
2005-09-20 13:26:39 +00:00
{
2012-02-06 08:57:43 +00:00
const wchar_t * begin , * end ;
const wchar_t * token_begin , * token_end ;
2012-02-06 09:45:16 +00:00
const wchar_t * buff = data - > command_line . c_str ( ) ;
2006-03-11 11:56:12 +00:00
int len ;
int cursor_steps ;
2007-02-09 09:33:50 +00:00
2012-02-06 09:45:16 +00:00
parse_util_cmdsubst_extent ( buff , data - > buff_pos , & begin , & end ) ;
2005-09-20 13:26:39 +00:00
2012-02-06 09:45:16 +00:00
parse_util_token_extent ( begin , data - > buff_pos - ( begin - buff ) , & token_begin , & token_end , 0 , 0 ) ;
2011-12-27 03:18:46 +00:00
2012-02-06 09:45:16 +00:00
cursor_steps = token_end - buff - data - > buff_pos ;
2006-03-11 11:56:12 +00:00
data - > buff_pos + = cursor_steps ;
2012-02-06 09:45:16 +00:00
if ( is_backslashed ( buff , data - > buff_pos ) )
2007-01-26 17:14:13 +00:00
{
remove_backward ( ) ;
}
2011-12-27 03:18:46 +00:00
2007-10-05 14:59:19 +00:00
reader_repaint ( ) ;
2011-12-27 03:18:46 +00:00
2012-02-06 09:45:16 +00:00
len = data - > buff_pos - ( begin - buff ) ;
2012-03-26 08:21:10 +00:00
const wcstring buffcpy = wcstring ( begin , len ) ;
2005-09-20 13:26:39 +00:00
2012-02-27 04:11:34 +00:00
data - > complete_func ( buffcpy , comp , COMPLETE_DEFAULT , NULL ) ;
2011-12-27 03:18:46 +00:00
2012-03-26 08:21:10 +00:00
sort ( comp . begin ( ) , comp . end ( ) ) ;
2007-02-18 23:25:20 +00:00
remove_duplicates ( comp ) ;
2011-12-27 03:18:46 +00:00
2007-02-09 09:33:50 +00:00
comp_empty = handle_completions ( comp ) ;
2012-01-16 16:56:47 +00:00
comp . clear ( ) ;
2005-09-20 13:26:39 +00:00
}
break ;
}
2006-10-12 13:27:32 +00:00
/* kill */
2005-09-20 13:26:39 +00:00
case R_KILL_LINE :
{
2012-02-06 09:45:16 +00:00
const wchar_t * buff = data - > command_line . c_str ( ) ;
const wchar_t * begin = & buff [ data - > buff_pos ] ;
const wchar_t * end = begin ;
2006-10-12 13:27:32 +00:00
int len ;
2011-12-27 03:18:46 +00:00
2006-10-12 13:27:32 +00:00
while ( * end & & * end ! = L ' \n ' )
end + + ;
2011-12-27 03:18:46 +00:00
2006-10-12 13:27:32 +00:00
if ( end = = begin & & * end )
end + + ;
2011-12-27 03:18:46 +00:00
2006-10-12 13:27:32 +00:00
len = end - begin ;
2011-12-27 03:18:46 +00:00
2006-10-12 13:27:32 +00:00
if ( len )
{
2012-02-06 09:45:16 +00:00
reader_kill ( begin - buff , len , KILL_APPEND , last_char ! = R_KILL_LINE ) ;
2006-10-12 13:27:32 +00:00
}
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
break ;
}
case R_BACKWARD_KILL_LINE :
{
2006-10-12 13:27:32 +00:00
if ( data - > buff_pos > 0 )
{
2012-02-06 09:45:16 +00:00
const wchar_t * buff = data - > command_line . c_str ( ) ;
const wchar_t * end = & buff [ data - > buff_pos ] ;
const wchar_t * begin = end ;
2006-10-12 13:27:32 +00:00
int len ;
2011-12-27 03:18:46 +00:00
2012-02-06 09:45:16 +00:00
while ( begin > buff & & * begin ! = L ' \n ' )
2006-10-12 13:27:32 +00:00
begin - - ;
2011-12-27 03:18:46 +00:00
2006-10-12 13:27:32 +00:00
if ( * begin = = L ' \n ' )
begin + + ;
2011-12-27 03:18:46 +00:00
2006-10-12 13:27:32 +00:00
len = maxi ( end - begin , 1 ) ;
begin = end - len ;
2011-12-27 03:18:46 +00:00
2012-02-06 09:45:16 +00:00
reader_kill ( begin - buff , len , KILL_PREPEND , last_char ! = R_BACKWARD_KILL_LINE ) ;
2011-12-27 03:18:46 +00:00
2006-10-12 13:27:32 +00:00
}
2005-09-20 13:26:39 +00:00
break ;
2006-10-12 13:27:32 +00:00
2005-09-20 13:26:39 +00:00
}
case R_KILL_WHOLE_LINE :
{
2012-02-06 09:45:16 +00:00
const wchar_t * buff = data - > command_line . c_str ( ) ;
const wchar_t * end = & buff [ data - > buff_pos ] ;
const wchar_t * begin = end ;
2006-10-12 13:27:32 +00:00
int len ;
2011-12-27 03:18:46 +00:00
2012-02-06 09:45:16 +00:00
while ( begin > buff & & * begin ! = L ' \n ' )
2006-10-12 13:27:32 +00:00
begin - - ;
2011-12-27 03:18:46 +00:00
2006-10-12 13:27:32 +00:00
if ( * begin = = L ' \n ' )
begin + + ;
2011-12-27 03:18:46 +00:00
2006-10-12 13:27:32 +00:00
len = maxi ( end - begin , 0 ) ;
begin = end - len ;
2005-09-20 13:26:39 +00:00
2006-10-12 13:27:32 +00:00
while ( * end & & * end ! = L ' \n ' )
end + + ;
2011-12-27 03:18:46 +00:00
2006-10-12 13:27:32 +00:00
if ( begin = = end & & * end )
end + + ;
2011-12-27 03:18:46 +00:00
2006-10-12 13:27:32 +00:00
len = end - begin ;
2011-12-27 03:18:46 +00:00
2006-10-12 13:27:32 +00:00
if ( len )
{
2012-02-06 09:45:16 +00:00
reader_kill ( begin - buff , len , KILL_APPEND , last_char ! = R_KILL_WHOLE_LINE ) ;
2006-10-12 13:27:32 +00:00
}
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
break ;
}
/* yank*/
case R_YANK :
2006-10-12 13:27:32 +00:00
{
yank_str = kill_yank ( ) ;
2012-02-06 10:20:50 +00:00
insert_string ( yank_str ) ;
2005-09-20 13:26:39 +00:00
yank = wcslen ( yank_str ) ;
break ;
2005-12-30 16:29:19 +00:00
}
2006-01-30 19:53:10 +00:00
2005-12-30 16:29:19 +00:00
/* rotate killring*/
2005-09-20 13:26:39 +00:00
case R_YANK_POP :
2005-12-30 16:29:19 +00:00
{
2005-09-20 13:26:39 +00:00
if ( yank )
{
for ( i = 0 ; i < yank ; i + + )
remove_backward ( ) ;
yank_str = kill_yank_rotate ( ) ;
2012-02-06 10:20:50 +00:00
insert_string ( yank_str ) ;
2005-09-20 13:26:39 +00:00
yank = wcslen ( yank_str ) ;
}
break ;
2005-12-30 16:29:19 +00:00
}
2006-01-30 19:53:10 +00:00
2005-12-30 16:29:19 +00:00
/* Escape was pressed */
2007-08-22 07:52:39 +00:00
case L ' \x1b ' :
2005-12-30 16:29:19 +00:00
{
2007-09-21 14:05:49 +00:00
if ( data - > search_mode )
2005-09-20 13:26:39 +00:00
{
2007-09-21 14:05:49 +00:00
data - > search_mode = NO_SEARCH ;
2011-12-27 03:18:46 +00:00
2005-09-24 16:31:22 +00:00
if ( data - > token_history_pos = = - 1 )
2006-01-30 19:53:10 +00:00
{
2012-02-06 00:42:24 +00:00
//history_reset();
data - > history_search . go_to_end ( ) ;
2012-02-04 04:01:01 +00:00
reader_set_buffer ( data - > search_buff . c_str ( ) , data - > search_buff . size ( ) ) ;
2005-09-24 16:31:22 +00:00
}
else
{
2012-02-04 04:01:01 +00:00
reader_replace_current_token ( data - > search_buff . c_str ( ) ) ;
2005-09-24 16:31:22 +00:00
}
2012-02-04 04:01:01 +00:00
data - > search_buff . clear ( ) ;
2012-02-21 18:47:21 +00:00
reader_super_highlight_me_plenty ( data - > buff_pos ) ;
2007-10-05 14:59:19 +00:00
reader_repaint ( ) ;
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
}
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
break ;
2005-12-30 16:29:19 +00:00
}
2006-01-30 19:53:10 +00:00
2005-12-30 16:29:19 +00:00
/* delete backward*/
2005-09-20 13:26:39 +00:00
case R_BACKWARD_DELETE_CHAR :
2005-12-30 16:29:19 +00:00
{
2005-09-20 13:26:39 +00:00
remove_backward ( ) ;
break ;
2005-12-30 16:29:19 +00:00
}
2006-01-30 19:53:10 +00:00
2005-12-30 16:29:19 +00:00
/* delete forward*/
2005-09-20 13:26:39 +00:00
case R_DELETE_CHAR :
2005-12-30 16:29:19 +00:00
{
2006-10-16 15:32:26 +00:00
/**
Remove the current character in the character buffer and on the
screen using syntax highlighting , etc .
*/
2012-02-06 09:53:03 +00:00
if ( data - > buff_pos < data - > command_length ( ) )
2006-10-16 15:32:26 +00:00
{
data - > buff_pos + + ;
remove_backward ( ) ;
}
2005-09-20 13:26:39 +00:00
break ;
2005-12-30 16:29:19 +00:00
}
2006-01-30 19:53:10 +00:00
2006-10-09 01:15:29 +00: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 13:26:39 +00:00
{
2012-02-07 04:14:19 +00:00
/* Delete any autosuggestion */
data - > autosuggestion . clear ( ) ;
2006-10-01 16:02:58 +00:00
/*
Allow backslash - escaped newlines
*/
2012-02-06 09:45:16 +00:00
if ( is_backslashed ( data - > command_line . c_str ( ) , data - > buff_pos ) )
2006-10-01 16:02:58 +00:00
{
insert_char ( ' \n ' ) ;
break ;
}
2011-12-27 03:18:46 +00:00
2012-02-06 09:45:16 +00:00
switch ( data - > test_func ( data - > command_line . c_str ( ) ) )
2005-09-20 13:26:39 +00:00
{
2006-10-01 16:02:58 +00:00
case 0 :
2005-09-20 13:26:39 +00:00
{
2006-10-01 16:02:58 +00:00
/*
Finished commend , execute it
*/
2012-02-06 09:45:16 +00:00
if ( ! data - > command_line . empty ( ) )
2006-10-01 16:02:58 +00:00
{
2012-02-15 19:33:41 +00:00
if ( data - > history ) {
data - > history - > add_with_file_detection ( data - > command_line ) ;
}
2006-10-01 16:02:58 +00:00
}
finished = 1 ;
2012-02-06 09:53:03 +00:00
data - > buff_pos = data - > command_length ( ) ;
2007-10-05 14:59:19 +00:00
reader_repaint ( ) ;
2006-10-01 16:02:58 +00:00
break ;
}
2011-12-27 03:18:46 +00:00
2006-10-01 16:02:58 +00:00
/*
We are incomplete , continue editing
*/
case PARSER_TEST_INCOMPLETE :
2011-12-27 03:18:46 +00:00
{
2006-10-01 16:02:58 +00:00
insert_char ( ' \n ' ) ;
break ;
}
/*
2006-11-02 13:50:19 +00:00
Result must be some combination including an
error . The error message will already be
printed , all we need to do is repaint
2006-10-01 16:02:58 +00:00
*/
default :
{
2012-03-25 10:00:38 +00:00
s_reset ( & data - > screen , true ) ;
2007-10-05 14:59:19 +00:00
reader_repaint ( ) ;
2006-10-01 16:02:58 +00:00
break ;
2005-09-20 13:26:39 +00:00
}
2006-01-30 19:53:10 +00:00
2006-10-01 16:02:58 +00:00
}
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
break ;
}
2007-04-16 21:26:15 +00:00
/* History functions */
2005-09-20 13:26:39 +00:00
case R_HISTORY_SEARCH_BACKWARD :
2007-04-16 21:26:15 +00:00
case R_HISTORY_TOKEN_SEARCH_BACKWARD :
2005-09-20 13:26:39 +00:00
case R_HISTORY_SEARCH_FORWARD :
2007-04-16 21:26:15 +00:00
case R_HISTORY_TOKEN_SEARCH_FORWARD :
2005-12-30 16:29:19 +00:00
{
2007-04-16 21:26:15 +00:00
int reset = 0 ;
2011-12-27 03:18:46 +00:00
2007-09-21 14:05:49 +00:00
if ( data - > search_mode = = NO_SEARCH )
2005-09-20 13:26:39 +00:00
{
2007-04-16 21:26:15 +00:00
reset = 1 ;
if ( ( c = = R_HISTORY_SEARCH_BACKWARD ) | |
2007-09-21 14:05:49 +00:00
( c = = R_HISTORY_SEARCH_FORWARD ) )
2007-04-16 21:26:15 +00:00
{
2007-09-21 14:05:49 +00:00
data - > search_mode = LINE_SEARCH ;
2007-04-16 21:26:15 +00:00
}
else
{
2007-09-21 14:05:49 +00:00
data - > search_mode = TOKEN_SEARCH ;
2007-04-16 21:26:15 +00:00
}
2011-12-27 03:18:46 +00:00
2012-02-06 09:45:16 +00:00
data - > search_buff . append ( data - > command_line ) ;
2012-02-06 18:52:13 +00:00
data - > history_search = history_search_t ( * data - > history , data - > search_buff , HISTORY_SEARCH_TYPE_CONTAINS ) ;
2012-02-06 19:52:24 +00:00
/* Skip the autosuggestion as history */
const wcstring & suggest = data - > autosuggestion ;
if ( ! suggest . empty ( ) ) {
data - > history_search . skip_matches ( wcstring_list_t ( & suggest , 1 + & suggest ) ) ;
}
2005-09-20 13:26:39 +00:00
}
2007-09-21 14:05:49 +00:00
switch ( data - > search_mode )
2005-09-20 13:26:39 +00:00
{
2007-04-16 21:26:15 +00:00
case LINE_SEARCH :
{
if ( ( c = = R_HISTORY_SEARCH_BACKWARD ) | |
( c = = R_HISTORY_TOKEN_SEARCH_BACKWARD ) )
{
2012-02-06 07:22:18 +00:00
data - > history_search . go_backwards ( ) ;
2007-04-16 21:26:15 +00:00
}
else
{
2012-02-06 07:22:18 +00:00
if ( ! data - > history_search . go_forwards ( ) ) {
/* If you try to go forwards past the end, we just go to the end */
data - > history_search . go_to_end ( ) ;
}
2007-04-16 21:26:15 +00:00
}
2011-12-27 03:18:46 +00:00
2012-02-06 00:42:24 +00:00
wcstring new_text ;
2012-02-06 07:22:18 +00:00
if ( data - > history_search . is_at_end ( ) ) {
2012-02-06 00:42:24 +00:00
new_text = data - > search_buff ;
2012-02-06 07:22:18 +00:00
} else {
2012-02-16 08:24:27 +00:00
new_text = data - > history_search . current_string ( ) ;
2012-02-06 00:42:24 +00:00
}
2012-02-06 07:22:18 +00:00
handle_history ( new_text ) ;
2011-12-27 03:18:46 +00:00
2007-04-16 21:26:15 +00:00
break ;
}
2011-12-27 03:18:46 +00:00
2007-04-16 21:26:15 +00:00
case TOKEN_SEARCH :
{
if ( ( c = = R_HISTORY_SEARCH_BACKWARD ) | |
( c = = R_HISTORY_TOKEN_SEARCH_BACKWARD ) )
{
handle_token_history ( SEARCH_BACKWARD , reset ) ;
}
else
{
handle_token_history ( SEARCH_FORWARD , reset ) ;
}
2011-12-27 03:18:46 +00:00
2007-04-16 21:26:15 +00:00
break ;
}
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
}
break ;
}
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
/* Move left*/
case R_BACKWARD_CHAR :
2006-04-05 12:48:25 +00:00
{
2005-09-20 13:26:39 +00:00
if ( data - > buff_pos > 0 )
{
data - > buff_pos - - ;
2007-10-05 14:59:19 +00:00
reader_repaint ( ) ;
2005-09-20 13:26:39 +00:00
}
break ;
2006-04-05 12:48:25 +00:00
}
2011-12-27 03:18:46 +00:00
2006-04-05 12:48:25 +00:00
/* Move right*/
2005-09-20 13:26:39 +00:00
case R_FORWARD_CHAR :
2005-12-30 16:29:19 +00:00
{
2012-02-06 09:53:03 +00:00
if ( data - > buff_pos < data - > command_length ( ) )
2005-09-20 13:26:39 +00:00
{
2011-12-27 03:18:46 +00:00
data - > buff_pos + + ;
2007-10-05 14:59:19 +00:00
reader_repaint ( ) ;
2012-02-06 19:59:34 +00:00
} else {
/* We're at the end of our buffer, and the user hit right. Try autosuggestion. */
if ( ! data - > autosuggestion . empty ( ) ) {
/* Accept the autosuggestion */
data - > command_line = data - > autosuggestion ;
data - > buff_pos = data - > command_line . size ( ) ;
2012-02-28 20:40:20 +00:00
data - > command_line_changed ( ) ;
2012-02-21 18:47:21 +00:00
reader_super_highlight_me_plenty ( data - > buff_pos ) ;
2012-02-06 19:59:34 +00:00
reader_repaint ( ) ;
}
}
2005-09-20 13:26:39 +00:00
break ;
2005-12-30 16:29:19 +00:00
}
2006-01-30 19:53:10 +00:00
2005-12-30 16:29:19 +00:00
/* kill one word left */
2005-09-20 13:26:39 +00:00
case R_BACKWARD_KILL_WORD :
2005-12-30 16:29:19 +00:00
{
2006-10-12 19:30:00 +00:00
move_word ( 0 , 1 , last_char ! = R_BACKWARD_KILL_WORD ) ;
2005-09-20 13:26:39 +00:00
break ;
2005-12-30 16:29:19 +00:00
}
2006-01-30 19:53:10 +00:00
2005-12-30 16:29:19 +00:00
/* kill one word right */
2005-09-20 13:26:39 +00:00
case R_KILL_WORD :
2005-12-30 16:29:19 +00:00
{
2006-10-12 19:30:00 +00:00
move_word ( 1 , 1 , last_char ! = R_KILL_WORD ) ;
2005-09-20 13:26:39 +00:00
break ;
2005-12-30 16:29:19 +00:00
}
2006-01-30 19:53:10 +00:00
2005-12-30 16:29:19 +00:00
/* move one word left*/
2005-09-20 13:26:39 +00:00
case R_BACKWARD_WORD :
2005-12-30 16:29:19 +00:00
{
2006-10-12 19:30:00 +00:00
move_word ( 0 , 0 , 0 ) ;
2005-09-20 13:26:39 +00:00
break ;
2005-12-30 16:29:19 +00:00
}
2006-01-30 19:53:10 +00:00
2005-12-30 16:29:19 +00:00
/* move one word right*/
2005-09-20 13:26:39 +00:00
case R_FORWARD_WORD :
2005-12-30 16:29:19 +00:00
{
2006-10-12 19:30:00 +00:00
move_word ( 1 , 0 , 0 ) ;
2005-09-20 13:26:39 +00:00
break ;
2005-12-30 16:29:19 +00:00
}
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
case R_BEGINNING_OF_HISTORY :
{
2012-02-06 00:42:24 +00:00
data - > history_search . go_to_beginning ( ) ;
2005-09-20 13:26:39 +00:00
break ;
}
case R_END_OF_HISTORY :
{
2012-02-06 00:42:24 +00:00
data - > history_search . go_to_end ( ) ;
2005-09-20 13:26:39 +00:00
break ;
}
2007-09-21 14:05:49 +00:00
case R_UP_LINE :
case R_DOWN_LINE :
{
2012-02-06 09:45:16 +00:00
int line_old = parse_util_get_line_from_offset ( data - > command_line . c_str ( ) , data - > buff_pos ) ;
2007-09-21 14:23:01 +00:00
int line_new ;
2011-12-27 03:18:46 +00:00
2007-09-21 14:05:49 +00:00
if ( c = = R_UP_LINE )
2007-09-21 14:23:01 +00:00
line_new = line_old - 1 ;
2007-09-21 14:05:49 +00:00
else
2007-09-21 14:23:01 +00:00
line_new = line_old + 1 ;
2011-12-27 03:18:46 +00:00
2012-02-06 09:53:03 +00:00
int line_count = parse_util_lineno ( data - > command_line . c_str ( ) , data - > command_length ( ) ) - 1 ;
2011-12-27 03:18:46 +00:00
2007-09-21 14:23:01 +00:00
if ( line_new > = 0 & & line_new < = line_count )
2007-09-21 14:05:49 +00:00
{
2007-09-21 14:23:01 +00:00
int base_pos_new ;
int base_pos_old ;
2011-12-27 03:18:46 +00:00
2007-09-21 14:05:49 +00:00
int indent_old ;
int indent_new ;
2007-09-21 14:23:01 +00:00
int line_offset_old ;
int total_offset_new ;
2007-09-21 14:05:49 +00:00
2012-02-06 09:45:16 +00:00
base_pos_new = parse_util_get_offset_from_line ( data - > command_line , line_new ) ;
2007-09-21 14:44:26 +00:00
2012-02-06 09:45:16 +00:00
base_pos_old = parse_util_get_offset_from_line ( data - > command_line , line_old ) ;
2011-12-27 03:18:46 +00:00
2012-02-06 18:52:13 +00:00
indent_old = data - > indents . at ( base_pos_old ) ;
indent_new = data - > indents . at ( base_pos_new ) ;
2011-12-27 03:18:46 +00:00
2012-02-06 09:45:16 +00:00
line_offset_old = data - > buff_pos - parse_util_get_offset_from_line ( data - > command_line , line_old ) ;
total_offset_new = parse_util_get_offset ( data - > command_line , line_new , line_offset_old - 4 * ( indent_new - indent_old ) ) ;
2007-09-21 14:23:01 +00:00
data - > buff_pos = total_offset_new ;
2007-10-05 14:59:19 +00:00
reader_repaint ( ) ;
2007-09-21 14:05:49 +00:00
}
2011-12-27 03:18:46 +00:00
2007-09-21 14:05:49 +00:00
break ;
}
2011-12-27 03:18:46 +00:00
2007-09-21 14:05:49 +00:00
2005-09-20 13:26:39 +00:00
/* Other, if a normal character, we add it to the command */
default :
{
2011-12-27 03:18:46 +00:00
2006-11-03 00:34:57 +00:00
if ( ( ! wchar_private ( c ) ) & & ( ( ( c > 31 ) | | ( c = = L ' \n ' ) ) & & ( c ! = 127 ) ) )
2006-10-09 01:15:29 +00:00
{
2012-03-26 08:21:10 +00:00
/* Regular character */
2005-09-20 13:26:39 +00:00
insert_char ( c ) ;
2006-10-09 01:15:29 +00:00
}
2005-10-13 14:08:33 +00:00
else
2006-03-10 13:38:09 +00:00
{
2006-04-05 12:48:25 +00:00
/*
2006-06-15 13:50:23 +00:00
Low priority debug message . These can happen if
the user presses an unefined control
sequnece . No reason to report .
2006-04-05 12:48:25 +00:00
*/
2006-06-15 13:50:23 +00:00
debug ( 2 , _ ( L " Unknown keybinding %d " ) , c ) ;
2006-03-10 13:38:09 +00:00
}
2005-09-20 13:26:39 +00:00
break ;
}
}
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
if ( ( c ! = R_HISTORY_SEARCH_BACKWARD ) & &
2007-09-21 14:05:49 +00:00
( c ! = R_HISTORY_SEARCH_FORWARD ) & &
( c ! = R_HISTORY_TOKEN_SEARCH_BACKWARD ) & &
( c ! = R_HISTORY_TOKEN_SEARCH_FORWARD ) & &
( c ! = R_NULL ) )
2005-09-20 13:26:39 +00:00
{
2007-09-21 14:05:49 +00:00
data - > search_mode = NO_SEARCH ;
2012-02-04 04:01:01 +00:00
data - > search_buff . clear ( ) ;
2012-02-06 00:42:24 +00:00
data - > history_search . go_to_end ( ) ;
2005-09-24 16:31:22 +00:00
data - > token_history_pos = - 1 ;
2005-09-20 13:26:39 +00:00
}
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
last_char = c ;
}
2007-01-29 17:45:01 +00:00
writestr ( L " \n " ) ;
2007-02-09 09:33:50 +00:00
/*
2007-03-24 19:07:38 +00:00
if ( comp )
halloc_free ( comp ) ;
2007-02-09 09:33:50 +00:00
*/
2006-05-14 10:16:23 +00:00
if ( ! reader_exit_forced ( ) )
{
if ( tcsetattr ( 0 , TCSANOW , & old_modes ) ) /* return to previous mode */
{
wperror ( L " tcsetattr " ) ;
}
2011-12-27 03:18:46 +00:00
2012-02-12 01:07:56 +00:00
set_color ( rgb_color_t : : reset ( ) , rgb_color_t : : reset ( ) ) ;
2006-05-14 10:16:23 +00:00
}
2011-12-27 03:18:46 +00:00
2012-02-06 09:45:16 +00:00
return finished ? data - > command_line . c_str ( ) : 0 ;
2005-09-20 13:26:39 +00:00
}
2007-09-21 14:05:49 +00:00
int reader_search_mode ( )
{
if ( ! data )
{
return - 1 ;
}
2011-12-27 03:18:46 +00:00
return ! ! data - > search_mode ;
2007-09-21 14:05:49 +00:00
}
2005-09-20 13:26:39 +00:00
/**
Read non - interactively . Read input from stdin without displaying
the prompt , using syntax highlighting . This is used for reading
scripts and init files .
*/
2007-04-25 18:30:02 +00:00
static int read_ni ( int fd , io_data_t * io )
2005-09-20 13:26:39 +00:00
{
2012-01-23 05:40:08 +00:00
parser_t & parser = parser_t : : principal_parser ( ) ;
2005-09-20 13:26:39 +00:00
FILE * in_stream ;
wchar_t * buff = 0 ;
2012-03-04 06:48:21 +00:00
std : : vector < char > acc ;
2005-09-20 13:26:39 +00:00
2005-10-19 12:07:44 +00:00
int des = fd = = 0 ? dup ( 0 ) : fd ;
2005-09-20 13:26:39 +00:00
int res = 0 ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
if ( des = = - 1 )
{
wperror ( L " dup " ) ;
return 1 ;
}
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
in_stream = fdopen ( des , " r " ) ;
if ( in_stream ! = 0 )
{
wchar_t * str ;
2005-10-19 12:07:44 +00:00
int acc_used ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
while ( ! feof ( in_stream ) )
{
char buff [ 4096 ] ;
2005-12-16 15:50:10 +00:00
int c ;
c = fread ( buff , 1 , 4096 , in_stream ) ;
2011-12-27 03:18:46 +00:00
2007-08-01 17:38:01 +00:00
if ( ferror ( in_stream ) & & ( errno ! = EINTR ) )
2005-12-16 15:50:10 +00:00
{
2006-01-30 19:53:10 +00:00
debug ( 1 ,
2007-08-01 17:38:01 +00:00
_ ( L " Error while reading from file descriptor " ) ) ;
2011-12-27 03:18:46 +00:00
2005-12-16 15:50:10 +00:00
/*
2006-04-05 12:48:25 +00:00
Reset buffer on error . We won ' t evaluate incomplete files .
2005-12-16 15:50:10 +00:00
*/
2012-03-04 06:48:21 +00:00
acc . clear ( ) ;
2005-12-16 15:50:10 +00:00
break ;
2011-12-27 03:18:46 +00:00
2005-12-16 15:50:10 +00:00
}
2006-01-30 19:53:10 +00:00
2012-03-04 06:48:21 +00:00
acc . insert ( acc . end ( ) , buff , buff + c ) ;
2005-09-20 13:26:39 +00:00
}
2012-03-04 06:48:21 +00:00
acc . push_back ( 0 ) ;
acc_used = acc . size ( ) ;
str = str2wcs ( & acc . at ( 0 ) ) ;
acc . clear ( ) ;
2005-09-20 13:26:39 +00:00
2005-10-19 12:07:44 +00:00
if ( fclose ( in_stream ) )
{
2006-01-30 19:53:10 +00:00
debug ( 1 ,
2006-01-11 14:17:35 +00:00
_ ( L " Error while closing input stream " ) ) ;
2005-10-19 12:07:44 +00:00
wperror ( L " fclose " ) ;
res = 1 ;
}
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
if ( str )
2006-01-30 19:53:10 +00:00
{
2012-02-22 18:51:06 +00:00
wcstring sb ;
2012-01-23 05:40:08 +00:00
if ( ! parser . test ( str , 0 , & sb , L " fish " ) )
2007-03-24 19:07:38 +00:00
{
2012-01-23 05:40:08 +00:00
parser . eval ( str , io , TOP ) ;
2007-03-24 19:07:38 +00:00
}
else
{
2012-02-22 18:51:06 +00:00
fwprintf ( stderr , L " %ls " , sb . c_str ( ) ) ;
2007-03-24 19:07:38 +00:00
res = 1 ;
}
free ( str ) ;
2005-09-20 13:26:39 +00:00
}
else
{
2005-10-19 12:07:44 +00:00
if ( acc_used > 1 )
2005-09-20 13:26:39 +00:00
{
debug ( 1 ,
2006-01-30 19:53:10 +00:00
_ ( L " Could not convert input. Read %d bytes. " ) ,
2005-10-19 12:07:44 +00:00
acc_used - 1 ) ;
2005-09-20 13:26:39 +00:00
}
else
{
2006-01-30 19:53:10 +00:00
debug ( 1 ,
2006-01-04 12:51:02 +00:00
_ ( L " Could not read input stream " ) ) ;
2005-09-20 13:26:39 +00:00
}
2006-01-30 19:53:10 +00:00
res = 1 ;
}
2005-09-20 13:26:39 +00:00
}
else
{
2006-01-30 19:53:10 +00:00
debug ( 1 ,
2006-01-11 14:17:35 +00:00
_ ( L " Error while opening input stream " ) ) ;
2005-09-20 13:26:39 +00:00
wperror ( L " fdopen " ) ;
free ( buff ) ;
res = 1 ;
}
return res ;
}
2007-04-25 18:30:02 +00:00
int reader_read ( int fd , io_data_t * io )
2005-09-20 13:26:39 +00:00
{
int res ;
2006-04-05 12:48:25 +00:00
2005-09-20 13:26:39 +00:00
/*
2006-04-05 12:48:25 +00: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 13:26:39 +00:00
*/
2006-01-30 19:53:10 +00:00
2007-10-06 10:55:27 +00:00
int inter = ( ( fd = = STDIN_FILENO ) & & isatty ( STDIN_FILENO ) ) ;
proc_push_interactive ( inter ) ;
2011-12-27 03:18:46 +00:00
2012-02-26 02:54:49 +00:00
res = get_is_interactive ( ) ? read_i ( ) : read_ni ( fd , io ) ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
/*
2005-10-19 12:07:44 +00:00
If the exit command was called in a script , only exit the
2006-04-05 12:48:25 +00:00
script , not the program .
2005-09-20 13:26:39 +00:00
*/
2006-02-08 09:24:29 +00:00
if ( data )
data - > end_loop = 0 ;
2005-09-20 13:26:39 +00:00
end_loop = 0 ;
2011-12-27 03:18:46 +00:00
2006-02-16 13:36:32 +00:00
proc_pop_interactive ( ) ;
2005-09-20 13:26:39 +00:00
return res ;
}