2005-09-20 13:26:39 +00:00
/** \file builtin.c
Functions for executing builtin functions .
How to add a new builtin function :
1 ) . Create a function in builtin . c with the following signature :
2012-06-05 04:24:42 +00:00
< tt > static int builtin_NAME ( parser_t & parser , wchar_t * * args ) < / tt >
2006-01-30 19:53:10 +00:00
where NAME is the name of the builtin , and args is a zero - terminated list of arguments .
2005-09-20 13:26:39 +00:00
2012-02-01 03:47:56 +00:00
2 ) . Add a line like { L " NAME " , & builtin_NAME , N_ ( L " Bla bla bla " ) } , to the builtin_data_t variable . The description is used by the completion system . Note that this array is sorted !
2005-09-20 13:26:39 +00:00
2006-02-05 21:20:50 +00:00
3 ) . Create a file doc_src / NAME . txt , containing the manual for the builtin in Doxygen - format . Check the other builtin manuals for proper syntax .
2005-09-20 13:26:39 +00:00
2012-12-16 04:23:24 +00:00
4 ) . Use ' git add doc_src / NAME . txt ' to start tracking changes to the documentation file .
2005-12-14 23:48:08 +00:00
2005-09-20 13:26:39 +00:00
*/
2006-08-11 01:18:35 +00:00
# include "config.h"
2005-09-20 13:26:39 +00:00
# include <stdlib.h>
# include <stdio.h>
# include <wchar.h>
# include <unistd.h>
# include <termios.h>
# include <errno.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <fcntl.h>
# include <dirent.h>
# include <string.h>
# include <signal.h>
# include <wctype.h>
# include <sys/time.h>
2005-09-24 19:55:58 +00:00
# include <time.h>
2012-02-08 07:53:34 +00:00
# include <stack>
2005-09-20 13:26:39 +00:00
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 "builtin.h"
# include "function.h"
# include "complete.h"
# include "proc.h"
# include "parser.h"
# include "reader.h"
# include "env.h"
# include "common.h"
# include "wgetopt.h"
# include "sanity.h"
# include "tokenizer.h"
# include "wildcard.h"
# include "input_common.h"
# include "input.h"
# include "intern.h"
2005-10-05 22:37:08 +00:00
# include "event.h"
2005-10-08 11:20:51 +00:00
# include "signal.h"
2006-11-17 16:24:38 +00:00
# include "exec.h"
2007-01-29 16:26:24 +00:00
# include "highlight.h"
2006-02-14 19:56:36 +00:00
# include "parse_util.h"
2007-04-22 09:50:26 +00:00
# include "parser_keywords.h"
2006-02-14 19:56:36 +00:00
# include "expand.h"
2006-10-19 11:50:23 +00:00
# include "path.h"
2012-06-05 04:24:42 +00:00
# include "history.h"
2013-06-15 21:32:38 +00:00
# include "parse_tree.h"
2014-09-22 02:18:56 +00:00
# include "wcstringutil.h"
2006-06-20 21:20:16 +00:00
2005-09-20 13:26:39 +00:00
/**
The default prompt for the read command
*/
2012-07-03 02:28:04 +00:00
# define DEFAULT_READ_PROMPT L"set_color green; echo -n read; set_color normal; echo -n \"> \""
2005-09-20 13:26:39 +00:00
/**
The mode name to pass to history and input
*/
# define READ_MODE_NAME L"fish_read"
2005-12-15 13:59:02 +00:00
2005-09-20 13:26:39 +00:00
/**
2005-12-15 13:59:02 +00:00
The send stuff to foreground message
2005-09-20 13:26:39 +00:00
*/
2006-01-04 12:51:02 +00:00
# define FG_MSG _( L"Send job %d, '%ls' to foreground\n" )
2005-12-11 23:30:01 +00:00
2006-06-17 13:07:08 +00:00
/**
2012-11-19 00:30:30 +00:00
Datastructure to describe a builtin .
2006-06-17 13:07:08 +00:00
*/
2012-02-01 03:47:56 +00:00
struct builtin_data_t
2006-02-05 13:08:40 +00:00
{
2012-11-19 00:30:30 +00:00
/**
Name of the builtin
*/
const wchar_t * name ;
/**
Function pointer tothe builtin implementation
*/
int ( * func ) ( parser_t & parser , wchar_t * * argv ) ;
/**
Description of what the builtin does
*/
const wchar_t * desc ;
2012-02-01 04:22:25 +00:00
bool operator < ( const wcstring & ) const ;
2012-02-01 03:47:56 +00:00
bool operator < ( const builtin_data_t * ) const ;
} ;
2006-02-05 13:08:40 +00:00
2012-02-01 04:22:25 +00:00
bool builtin_data_t : : operator < ( const wcstring & other ) const
2012-02-01 03:47:56 +00:00
{
2012-02-01 04:22:25 +00:00
return wcscmp ( this - > name , other . c_str ( ) ) < 0 ;
2012-02-01 03:47:56 +00:00
}
2006-02-05 13:08:40 +00:00
2012-02-01 03:47:56 +00:00
bool builtin_data_t : : operator < ( const builtin_data_t * other ) const
{
return wcscmp ( this - > name , other - > name ) < 0 ;
}
2005-09-20 13:26:39 +00:00
int builtin_out_redirect ;
int builtin_err_redirect ;
2012-02-22 18:51:06 +00:00
/* Buffers for storing the output of builtin functions */
wcstring stdout_buffer , stderr_buffer ;
2012-11-19 00:30:30 +00:00
const wcstring & get_stdout_buffer ( )
{
2012-02-22 18:51:06 +00:00
ASSERT_IS_MAIN_THREAD ( ) ;
return stdout_buffer ;
}
2012-11-19 00:30:30 +00:00
const wcstring & get_stderr_buffer ( )
{
2012-02-22 18:51:06 +00:00
ASSERT_IS_MAIN_THREAD ( ) ;
return stderr_buffer ;
}
2012-11-19 00:30:30 +00:00
void builtin_show_error ( const wcstring & err )
{
2012-03-07 09:02:46 +00:00
ASSERT_IS_MAIN_THREAD ( ) ;
stderr_buffer . append ( err ) ;
}
2005-12-15 13:59:02 +00:00
2005-09-20 13:26:39 +00:00
/**
Stack containing builtin I / O for recursive builtin calls .
*/
2012-11-19 00:30:30 +00:00
struct io_stack_elem_t
{
2012-02-08 07:53:34 +00:00
int in ;
2012-02-22 18:51:06 +00:00
wcstring out ;
wcstring err ;
2012-02-08 07:53:34 +00:00
} ;
2012-07-25 05:31:31 +00:00
static std : : stack < io_stack_elem_t , std : : vector < io_stack_elem_t > > io_stack ;
2005-09-20 13:26:39 +00:00
2006-01-30 19:53:10 +00:00
/**
2006-02-05 21:20:50 +00:00
The file from which builtin functions should attempt to read , use
instead of stdin .
2005-09-20 13:26:39 +00:00
*/
static int builtin_stdin ;
2007-04-25 18:30:02 +00:00
/**
2007-10-06 10:55:27 +00:00
The underlying IO redirections behind the current builtin . This
2007-04-25 18:30:02 +00:00
should normally not be used - sb_out and friends are already
configured to handle everything .
*/
2012-08-15 07:57:56 +00:00
static const io_chain_t * real_io ;
2007-04-25 18:30:02 +00:00
2006-06-20 21:20:16 +00:00
/**
Counts the number of non null pointers in the specified array
*/
2013-12-27 09:38:43 +00:00
static int builtin_count_args ( const wchar_t * const * argv )
2005-09-20 13:26:39 +00:00
{
2012-11-19 00:30:30 +00:00
int argc = 1 ;
2013-02-14 23:50:24 +00:00
while ( argv [ argc ] ! = NULL )
2012-11-19 00:30:30 +00:00
{
argc + + ;
}
return argc ;
2005-09-20 13:26:39 +00:00
}
2012-11-19 00:30:30 +00:00
/**
2006-06-20 21:20:16 +00:00
This function works like wperror , but it prints its result into
2012-03-04 06:08:34 +00:00
the sb_err string instead of to stderr . Used by the builtin
2006-06-20 21:20:16 +00:00
commands .
*/
2005-10-14 22:33:01 +00:00
2012-11-19 00:30:30 +00:00
static void builtin_wperror ( const wchar_t * s )
2005-09-20 13:26:39 +00:00
{
2012-11-19 00:30:30 +00:00
if ( s ! = 0 )
{
2012-02-22 18:51:06 +00:00
stderr_buffer . append ( s ) ;
stderr_buffer . append ( L " : " ) ;
2012-11-19 00:30:30 +00:00
}
char * err = strerror ( errno ) ;
2012-12-19 21:31:06 +00:00
if ( err )
2012-11-19 00:30:30 +00:00
{
2012-12-19 21:31:06 +00:00
const wcstring werr = str2wcstring ( err ) ;
2012-02-22 18:51:06 +00:00
stderr_buffer . append ( werr ) ;
stderr_buffer . push_back ( L ' \n ' ) ;
2012-11-19 00:30:30 +00:00
}
2005-09-20 13:26:39 +00:00
}
2006-06-17 13:07:08 +00:00
/**
Count the number of times the specified character occurs in the specified string
*/
2012-11-19 00:30:30 +00:00
static int count_char ( const wchar_t * str , wchar_t c )
2006-05-26 11:24:02 +00:00
{
2012-11-19 00:30:30 +00:00
int res = 0 ;
for ( ; * str ; str + + )
{
res + = ( * str = = c ) ;
}
return res ;
2006-05-26 11:24:02 +00:00
}
2005-09-20 13:26:39 +00:00
2012-11-19 00:30:30 +00:00
wcstring builtin_help_get ( parser_t & parser , const wchar_t * name )
2006-11-17 16:24:38 +00:00
{
2013-03-25 23:06:12 +00:00
/* This won't ever work if no_exec is set */
if ( no_exec )
return wcstring ( ) ;
2013-05-05 09:33:17 +00:00
2011-12-31 23:57:30 +00:00
wcstring_list_t lst ;
2012-02-01 04:22:25 +00:00
wcstring out ;
const wcstring name_esc = escape_string ( name , 1 ) ;
const wcstring cmd = format_string ( L " __fish_print_help %ls " , name_esc . c_str ( ) ) ;
2013-01-31 23:57:08 +00:00
if ( exec_subshell ( cmd , lst , false /* don't apply exit status */ ) > = 0 )
2012-11-19 00:30:30 +00:00
{
for ( size_t i = 0 ; i < lst . size ( ) ; i + + )
{
2012-02-01 04:22:25 +00:00
out . append ( lst . at ( i ) ) ;
out . push_back ( L ' \n ' ) ;
2012-11-19 00:30:30 +00:00
}
}
return out ;
2006-11-17 16:24:38 +00:00
}
2007-10-02 10:09:37 +00:00
/**
Print help for the specified builtin . If \ c b is sb_err , also print
the line information
If \ c b is the buffer representing standard error , and the help
message is about to be printed to an interactive screen , it may be
shortened to fit the screen .
2012-11-19 00:30:30 +00:00
2007-10-02 10:09:37 +00:00
*/
2006-06-20 21:20:16 +00:00
2012-11-19 00:30:30 +00:00
static void builtin_print_help ( parser_t & parser , const wchar_t * cmd , wcstring & b )
2005-09-20 13:26:39 +00:00
{
2012-11-19 00:30:30 +00:00
if ( & b = = & stderr_buffer )
{
2012-02-22 18:51:06 +00:00
stderr_buffer . append ( parser . current_line ( ) ) ;
2012-11-19 00:30:30 +00:00
}
const wcstring h = builtin_help_get ( parser , cmd ) ;
if ( ! h . size ( ) )
return ;
wchar_t * str = wcsdup ( h . c_str ( ) ) ;
if ( str )
{
2014-01-12 21:33:35 +00:00
bool is_short = false ;
2012-11-19 00:30:30 +00:00
if ( & b = = & stderr_buffer )
{
/*
Interactive mode help to screen - only print synopsis if
the rest won ' t fit
*/
int screen_height , lines ;
screen_height = common_get_height ( ) ;
lines = count_char ( str , L ' \n ' ) ;
if ( ! get_is_interactive ( ) | | ( lines > 2 * screen_height / 3 ) )
{
wchar_t * pos ;
int cut = 0 ;
int i ;
2014-01-12 21:33:35 +00:00
is_short = true ;
2012-11-19 00:30:30 +00:00
/*
First move down 4 lines
*/
pos = str ;
for ( i = 0 ; ( i < 4 ) & & pos & & * pos ; i + + )
{
pos = wcschr ( pos + 1 , L ' \n ' ) ;
}
if ( pos & & * pos )
{
/*
Then find the next empty line
*/
for ( ; * pos ; pos + + )
{
if ( * pos = = L ' \n ' )
{
wchar_t * pos2 ;
int is_empty = 1 ;
for ( pos2 = pos + 1 ; * pos2 ; pos2 + + )
{
if ( * pos2 = = L ' \n ' )
break ;
if ( * pos2 ! = L ' \t ' & & * pos2 ! = L ' ' )
{
is_empty = 0 ;
break ;
}
}
if ( is_empty )
{
/*
And cut it
*/
* ( pos2 + 1 ) = L ' \0 ' ;
cut = 1 ;
break ;
}
}
}
}
/*
We did not find a good place to cut message to
shorten it - so we make sure we don ' t print
anything .
*/
if ( ! cut )
{
* str = 0 ;
}
}
}
2012-02-22 18:51:06 +00:00
b . append ( str ) ;
2012-11-19 00:30:30 +00:00
if ( is_short )
{
2012-02-22 18:51:06 +00:00
append_format ( b , _ ( L " %ls: Type 'help %ls' for related documentation \n \n " ) , cmd , cmd ) ;
2012-11-19 00:30:30 +00:00
}
free ( str ) ;
}
2005-09-20 13:26:39 +00:00
}
2006-06-20 21:20:16 +00:00
2007-01-21 14:55:27 +00:00
/**
Perform error reporting for encounter with unknown option
*/
2012-11-19 00:30:30 +00:00
static void builtin_unknown_option ( parser_t & parser , const wchar_t * cmd , const wchar_t * opt )
2007-01-21 14:55:27 +00:00
{
2012-02-22 18:51:06 +00:00
append_format ( stderr_buffer , BUILTIN_ERR_UNKNOWN , cmd , opt ) ;
2012-11-19 00:30:30 +00:00
builtin_print_help ( parser , cmd , stderr_buffer ) ;
2007-01-21 14:55:27 +00:00
}
2007-08-01 19:44:50 +00:00
/**
Perform error reporting for encounter with missing argument
*/
2012-11-19 00:30:30 +00:00
static void builtin_missing_argument ( parser_t & parser , const wchar_t * cmd , const wchar_t * opt )
2007-08-01 19:44:50 +00:00
{
2012-02-22 18:51:06 +00:00
append_format ( stderr_buffer , BUILTIN_ERR_MISSING , cmd , opt ) ;
2012-11-19 00:30:30 +00:00
builtin_print_help ( parser , cmd , stderr_buffer ) ;
2007-08-01 19:44:50 +00:00
}
2006-06-20 21:20:16 +00:00
/*
Here follows the definition of all builtin commands . The function
names are all on the form builtin_NAME where NAME is the name of the
builtin . so the function name for the builtin ' fg ' is
' builtin_fg ' .
A few builtins , including ' while ' , ' command ' and ' builtin ' are not
defined here as they are handled directly by the parser . ( They are
not parsed as commands , instead they only alter the parser state )
The builtins ' break ' and ' continue ' are so closely related that they
share the same implementation , namely ' builtin_break_continue .
Several other builtins , including jobs , ulimit and set are so big
that they have been given their own file . These files are all named
2007-01-06 14:24:30 +00:00
' builtin_NAME . c ' , where NAME is the name of the builtin . These files
are included directly below .
2006-06-20 21:20:16 +00:00
*/
2011-12-27 03:18:46 +00:00
# include "builtin_set.cpp"
# include "builtin_commandline.cpp"
# include "builtin_complete.cpp"
# include "builtin_ulimit.cpp"
# include "builtin_jobs.cpp"
2013-02-14 23:50:24 +00:00
# include "builtin_set_color.cpp"
2013-01-22 17:07:28 +00:00
# include "builtin_printf.cpp"
2006-06-20 21:20:16 +00:00
2012-03-07 08:54:01 +00:00
/* builtin_test lives in builtin_test.cpp */
2012-11-19 00:30:30 +00:00
int builtin_test ( parser_t & parser , wchar_t * * argv ) ;
2012-03-07 08:54:01 +00:00
2008-01-13 16:47:47 +00:00
/**
2014-09-23 04:04:06 +00:00
List a single key binding .
Returns false if no binding with that sequence and mode exists .
2008-01-13 16:47:47 +00:00
*/
2014-09-23 04:04:06 +00:00
static bool builtin_bind_list_one ( const wcstring & seq , const wcstring & bind_mode )
2007-09-25 16:14:47 +00:00
{
2014-09-23 04:04:06 +00:00
std : : vector < wcstring > ecmds ;
wcstring sets_mode ;
2012-11-19 00:30:30 +00:00
2014-09-23 04:04:06 +00:00
if ( ! input_mapping_get ( seq , bind_mode , & ecmds , & sets_mode ) )
2012-11-19 00:30:30 +00:00
{
2014-09-23 04:04:06 +00:00
return false ;
}
2012-11-19 00:30:30 +00:00
2014-09-23 04:04:06 +00:00
stdout_buffer . append ( L " bind " ) ;
2013-12-31 00:52:41 +00:00
2014-09-23 04:04:06 +00:00
// Append the mode flags if applicable
if ( bind_mode ! = DEFAULT_BIND_MODE )
{
const wcstring emode = escape_string ( bind_mode , ESCAPE_ALL ) ;
stdout_buffer . append ( L " -M " ) ;
stdout_buffer . append ( emode ) ;
}
if ( sets_mode ! = bind_mode )
{
const wcstring esets_mode = escape_string ( sets_mode , ESCAPE_ALL ) ;
stdout_buffer . append ( L " -m " ) ;
stdout_buffer . append ( esets_mode ) ;
}
2014-03-31 17:01:39 +00:00
2014-09-23 04:04:06 +00:00
// Append the name
wcstring tname ;
if ( input_terminfo_get_name ( seq , & tname ) )
{
// Note that we show -k here because we have an input key name
append_format ( stdout_buffer , L " -k %ls " , tname . c_str ( ) ) ;
}
else
{
// No key name, so no -k; we show the escape sequence directly
const wcstring eseq = escape_string ( seq , ESCAPE_ALL ) ;
append_format ( stdout_buffer , L " %ls " , eseq . c_str ( ) ) ;
}
2014-09-22 19:37:11 +00:00
2014-09-23 04:04:06 +00:00
// Now show the list of commands
for ( size_t i = 0 ; i < ecmds . size ( ) ; i + + )
{
const wcstring & ecmd = ecmds . at ( i ) ;
const wcstring escaped_ecmd = escape_string ( ecmd , ESCAPE_ALL ) ;
stdout_buffer . push_back ( ' ' ) ;
stdout_buffer . append ( escaped_ecmd ) ;
}
stdout_buffer . push_back ( L ' \n ' ) ;
2014-09-22 19:37:11 +00:00
2014-09-23 04:04:06 +00:00
return true ;
}
2014-09-22 19:37:11 +00:00
2014-09-23 04:04:06 +00:00
/**
List all current key bindings
*/
static void builtin_bind_list ( const wchar_t * bind_mode )
{
const std : : vector < input_mapping_name_t > lst = input_mapping_get_names ( ) ;
2014-09-22 19:37:11 +00:00
2014-09-23 04:04:06 +00:00
for ( std : : vector < input_mapping_name_t > : : const_iterator it = lst . begin ( ) , end = lst . end ( ) ;
it ! = end ;
+ + it )
{
if ( bind_mode ! = NULL & & bind_mode ! = it - > mode )
2012-11-19 00:30:30 +00:00
{
2014-09-23 04:04:06 +00:00
continue ;
2012-11-19 00:30:30 +00:00
}
2014-09-23 04:04:06 +00:00
builtin_bind_list_one ( it - > seq , it - > mode ) ;
2012-11-19 00:30:30 +00:00
}
2007-09-25 16:14:47 +00:00
}
2008-01-13 16:47:47 +00:00
/**
Print terminfo key binding names to string buffer used for standard output .
\ param all if set , all terminfo key binding names will be
printed . If not set , only ones that are defined for this terminal
are printed .
*/
2012-11-19 00:30:30 +00:00
static void builtin_bind_key_names ( int all )
2007-09-25 16:14:47 +00:00
{
2012-11-19 00:30:30 +00:00
const wcstring_list_t names = input_terminfo_get_names ( ! all ) ;
for ( size_t i = 0 ; i < names . size ( ) ; i + + )
{
2012-02-08 01:06:45 +00:00
const wcstring & name = names . at ( i ) ;
2012-11-19 00:30:30 +00:00
append_format ( stdout_buffer , L " %ls \n " , name . c_str ( ) ) ;
}
2007-09-25 16:14:47 +00:00
}
2008-01-13 16:47:47 +00:00
/**
Print all the special key binding functions to string buffer used for standard output .
*/
2007-09-25 16:14:47 +00:00
static void builtin_bind_function_names ( )
{
2012-01-24 04:48:47 +00:00
wcstring_list_t names = input_function_get_names ( ) ;
2012-11-19 00:30:30 +00:00
for ( size_t i = 0 ; i < names . size ( ) ; i + + )
{
const wchar_t * seq = names . at ( i ) . c_str ( ) ;
append_format ( stdout_buffer , L " %ls \n " , seq ) ;
}
2007-09-25 16:14:47 +00:00
}
2014-09-23 02:21:08 +00:00
// Wraps input_terminfo_get_sequence(), appending the correct error messages as needed.
static int get_terminfo_sequence ( const wchar_t * seq , wcstring * out_seq )
{
if ( input_terminfo_get_sequence ( seq , out_seq ) )
{
return 1 ;
}
2014-09-23 04:30:44 +00:00
wcstring eseq = escape_string ( seq , 0 ) ;
2014-09-23 02:21:08 +00:00
switch ( errno )
{
case ENOENT :
{
2014-09-23 04:30:44 +00:00
append_format ( stderr_buffer , _ ( L " %ls: No key with name '%ls' found \n " ) , L " bind " , eseq . c_str ( ) ) ;
2014-09-23 02:21:08 +00:00
break ;
}
case EILSEQ :
{
2014-09-23 04:30:44 +00:00
append_format ( stderr_buffer , _ ( L " %ls: Key with name '%ls' does not have any mapping \n " ) , L " bind " , eseq . c_str ( ) ) ;
2014-09-23 02:21:08 +00:00
break ;
}
default :
{
2014-09-23 04:30:44 +00:00
append_format ( stderr_buffer , _ ( L " %ls: Unknown error trying to bind to key named '%ls' \n " ) , L " bind " , eseq . c_str ( ) ) ;
2014-09-23 02:21:08 +00:00
break ;
}
}
return 0 ;
}
2008-01-13 16:47:47 +00:00
/**
Add specified key binding .
*/
2013-12-31 23:11:32 +00:00
static int builtin_bind_add ( const wchar_t * seq , const wchar_t * * cmds , size_t cmds_len ,
2014-01-15 10:39:19 +00:00
const wchar_t * mode , const wchar_t * sets_mode , int terminfo )
2007-09-25 16:14:47 +00:00
{
2012-11-19 00:30:30 +00:00
if ( terminfo )
{
2013-04-15 20:07:17 +00:00
wcstring seq2 ;
2014-09-23 02:21:08 +00:00
if ( get_terminfo_sequence ( seq , & seq2 ) )
2012-11-19 00:30:30 +00:00
{
2014-01-15 10:39:19 +00:00
input_mapping_add ( seq2 . c_str ( ) , cmds , cmds_len , mode , sets_mode ) ;
2012-11-19 00:30:30 +00:00
}
else
{
return 1 ;
}
}
else
{
2014-01-15 10:39:19 +00:00
input_mapping_add ( seq , cmds , cmds_len , mode , sets_mode ) ;
2012-11-19 00:30:30 +00:00
}
return 0 ;
2007-09-25 16:14:47 +00:00
}
2008-01-13 16:47:47 +00:00
/**
Erase specified key bindings
\ param seq an array of all key bindings to erase
\ param all if specified , _all_ key bindings will be erased
2014-09-23 04:04:06 +00:00
\ param mode if specified , only bindings from that mode will be erased . If not given and \ c all is \ c false , \ c DEFAULT_BIND_MODE will be used .
2008-01-13 16:47:47 +00:00
*/
2014-09-23 02:21:08 +00:00
static int builtin_bind_erase ( wchar_t * * seq , int all , const wchar_t * mode , int use_terminfo )
2005-09-20 13:26:39 +00:00
{
2012-11-19 00:30:30 +00:00
if ( all )
{
2014-09-23 04:04:06 +00:00
const std : : vector < input_mapping_name_t > lst = input_mapping_get_names ( ) ;
for ( std : : vector < input_mapping_name_t > : : const_iterator it = lst . begin ( ) , end = lst . end ( ) ;
it ! = end ;
+ + it )
2012-11-19 00:30:30 +00:00
{
2014-09-23 04:04:06 +00:00
if ( mode = = NULL | | mode = = it - > mode )
{
input_mapping_erase ( it - > seq , it - > mode ) ;
}
2012-11-19 00:30:30 +00:00
}
2006-01-30 19:53:10 +00:00
2014-09-23 02:21:08 +00:00
return 0 ;
2012-11-19 00:30:30 +00:00
}
else
{
2014-09-23 02:21:08 +00:00
int res = 0 ;
2014-09-23 04:04:06 +00:00
if ( mode = = NULL ) mode = DEFAULT_BIND_MODE ;
2012-11-19 00:30:30 +00:00
while ( * seq )
{
2014-09-23 03:56:45 +00:00
if ( use_terminfo )
2014-09-23 02:21:08 +00:00
{
2014-09-23 03:56:45 +00:00
wcstring seq2 ;
if ( get_terminfo_sequence ( * seq + + , & seq2 ) )
{
input_mapping_erase ( seq2 . c_str ( ) , mode ) ;
}
else
{
res = 1 ;
}
2014-09-23 02:21:08 +00:00
}
else
{
2014-09-23 03:56:45 +00:00
input_mapping_erase ( * seq + + , mode ) ;
2014-09-23 02:21:08 +00:00
}
2012-11-19 00:30:30 +00:00
}
2005-12-11 22:21:01 +00:00
2014-09-23 02:21:08 +00:00
return res ;
2012-11-19 00:30:30 +00:00
}
2005-12-11 22:21:01 +00:00
}
2012-11-19 00:30:30 +00:00
2005-09-20 13:26:39 +00:00
/**
2012-11-19 00:30:30 +00:00
The bind builtin , used for setting character sequences
2005-09-20 13:26:39 +00:00
*/
2012-11-19 00:30:30 +00:00
static int builtin_bind ( parser_t & parser , wchar_t * * argv )
2005-09-20 13:26:39 +00:00
{
2006-01-30 19:53:10 +00:00
2012-11-19 00:30:30 +00:00
enum
{
BIND_INSERT ,
BIND_ERASE ,
BIND_KEY_NAMES ,
BIND_FUNCTION_NAMES
2014-02-12 20:49:32 +00:00
} ;
2006-01-30 19:53:10 +00:00
2012-11-19 00:30:30 +00:00
int argc = builtin_count_args ( argv ) ;
int mode = BIND_INSERT ;
int res = STATUS_BUILTIN_OK ;
int all = 0 ;
2006-01-30 19:53:10 +00:00
2013-12-31 00:52:41 +00:00
const wchar_t * bind_mode = DEFAULT_BIND_MODE ;
2013-12-31 13:53:29 +00:00
bool bind_mode_given = false ;
2014-01-15 11:04:52 +00:00
const wchar_t * sets_bind_mode = DEFAULT_BIND_MODE ;
bool sets_bind_mode_given = false ;
2013-12-31 00:52:41 +00:00
2012-11-19 00:30:30 +00:00
int use_terminfo = 0 ;
2006-01-30 19:53:10 +00:00
2012-11-19 00:30:30 +00:00
woptind = 0 ;
2006-01-30 19:53:10 +00:00
2014-03-29 21:19:45 +00:00
static const struct woption long_options [ ] =
2012-11-19 00:30:30 +00:00
{
2014-01-27 08:56:13 +00:00
{ L " all " , no_argument , 0 , ' a ' } ,
{ L " erase " , no_argument , 0 , ' e ' } ,
{ L " function-names " , no_argument , 0 , ' f ' } ,
{ L " help " , no_argument , 0 , ' h ' } ,
{ L " key " , no_argument , 0 , ' k ' } ,
{ L " key-names " , no_argument , 0 , ' K ' } ,
2014-03-29 21:19:45 +00:00
{ L " mode " , required_argument , 0 , ' M ' } ,
{ L " sets-mode " , required_argument , 0 , ' m ' } ,
2014-01-27 08:56:13 +00:00
{ 0 , 0 , 0 , 0 }
} ;
2006-01-30 19:53:10 +00:00
2012-11-19 00:30:30 +00:00
while ( 1 )
{
int opt_index = 0 ;
int opt = wgetopt_long ( argc ,
argv ,
2013-12-31 00:52:41 +00:00
L " aehkKfM:m: " ,
2012-11-19 00:30:30 +00:00
long_options ,
& opt_index ) ;
if ( opt = = - 1 )
break ;
2005-09-20 13:26:39 +00:00
2012-11-19 00:30:30 +00:00
switch ( opt )
{
2012-11-19 08:31:03 +00:00
case 0 :
if ( long_options [ opt_index ] . flag ! = 0 )
break ;
append_format ( stderr_buffer ,
BUILTIN_ERR_UNKNOWN ,
argv [ 0 ] ,
long_options [ opt_index ] . name ) ;
builtin_print_help ( parser , argv [ 0 ] , stderr_buffer ) ;
2005-09-20 13:26:39 +00:00
2012-11-19 08:31:03 +00:00
return STATUS_BUILTIN_ERROR ;
2007-08-19 16:42:30 +00:00
2012-11-19 08:31:03 +00:00
case ' a ' :
all = 1 ;
break ;
2007-08-19 16:42:30 +00:00
2012-11-19 08:31:03 +00:00
case ' e ' :
mode = BIND_ERASE ;
break ;
2007-08-19 16:42:30 +00:00
2012-11-19 08:31:03 +00:00
case ' h ' :
builtin_print_help ( parser , argv [ 0 ] , stdout_buffer ) ;
return STATUS_BUILTIN_OK ;
2007-08-19 16:42:30 +00:00
2012-11-19 08:31:03 +00:00
case ' k ' :
use_terminfo = 1 ;
break ;
2007-08-19 16:42:30 +00:00
2012-11-19 08:31:03 +00:00
case ' K ' :
mode = BIND_KEY_NAMES ;
break ;
2007-08-19 16:42:30 +00:00
2012-11-19 08:31:03 +00:00
case ' f ' :
mode = BIND_FUNCTION_NAMES ;
break ;
2007-08-19 16:42:30 +00:00
2013-12-31 00:52:41 +00:00
case ' M ' :
bind_mode = woptarg ;
2013-12-31 13:53:29 +00:00
bind_mode_given = true ;
2013-12-31 00:52:41 +00:00
break ;
case ' m ' :
2014-01-15 11:04:52 +00:00
sets_bind_mode = woptarg ;
sets_bind_mode_given = true ;
2013-12-31 00:52:41 +00:00
break ;
2012-11-19 08:31:03 +00:00
case ' ? ' :
builtin_unknown_option ( parser , argv [ 0 ] , argv [ woptind - 1 ] ) ;
return STATUS_BUILTIN_ERROR ;
2006-01-30 19:53:10 +00:00
2013-12-31 00:52:41 +00:00
2012-11-19 00:30:30 +00:00
}
2013-12-31 13:53:29 +00:00
}
2006-01-30 19:53:10 +00:00
2013-12-31 13:53:29 +00:00
/*
2014-03-31 17:01:39 +00:00
* if mode is given , but not new mode , default to new mode to mode
2013-12-31 13:53:29 +00:00
*/
2014-03-31 17:01:39 +00:00
if ( bind_mode_given & & ! sets_bind_mode_given )
2013-12-31 13:53:29 +00:00
{
2014-03-31 17:01:39 +00:00
sets_bind_mode = bind_mode ;
2012-11-19 00:30:30 +00:00
}
2006-01-30 19:53:10 +00:00
2012-11-19 00:30:30 +00:00
switch ( mode )
{
2006-01-30 19:53:10 +00:00
2012-11-19 08:31:03 +00:00
case BIND_ERASE :
2012-11-19 00:30:30 +00:00
{
2014-09-23 02:21:08 +00:00
if ( builtin_bind_erase ( & argv [ woptind ] , all , bind_mode_given ? bind_mode : NULL , use_terminfo ) )
{
res = STATUS_BUILTIN_ERROR ;
}
2012-11-19 00:30:30 +00:00
break ;
}
2006-01-30 19:53:10 +00:00
2012-11-19 08:31:03 +00:00
case BIND_INSERT :
2012-11-19 00:30:30 +00:00
{
2012-11-19 08:31:03 +00:00
switch ( argc - woptind )
{
case 0 :
{
2013-12-31 13:53:29 +00:00
builtin_bind_list ( bind_mode_given ? bind_mode : NULL ) ;
2012-11-19 08:31:03 +00:00
break ;
}
2013-12-31 23:11:32 +00:00
case 1 :
2012-11-19 08:31:03 +00:00
{
2014-09-23 04:30:44 +00:00
wcstring seq ;
if ( use_terminfo )
{
if ( ! get_terminfo_sequence ( argv [ woptind ] , & seq ) )
{
res = STATUS_BUILTIN_ERROR ;
// get_terminfo_sequence already printed the error
break ;
}
}
else
{
seq = argv [ woptind ] ;
}
if ( ! builtin_bind_list_one ( seq , bind_mode ) )
{
res = STATUS_BUILTIN_ERROR ;
wcstring eseq = escape_string ( argv [ woptind ] , 0 ) ;
if ( use_terminfo )
{
append_format ( stderr_buffer , _ ( L " %ls: No binding found for key '%ls' \n " ) , argv [ 0 ] , eseq . c_str ( ) ) ;
}
else
{
append_format ( stderr_buffer , _ ( L " %ls: No binding found for sequence '%ls' \n " ) , argv [ 0 ] , eseq . c_str ( ) ) ;
}
}
2012-11-19 08:31:03 +00:00
break ;
}
default :
{
2014-09-23 02:21:08 +00:00
if ( builtin_bind_add ( argv [ woptind ] , ( const wchar_t * * ) argv + ( woptind + 1 ) , argc - ( woptind + 1 ) , bind_mode , sets_bind_mode , use_terminfo ) )
{
res = STATUS_BUILTIN_ERROR ;
}
2012-11-19 08:31:03 +00:00
break ;
}
2013-12-31 23:11:32 +00:00
2012-11-19 08:31:03 +00:00
}
2012-11-19 00:30:30 +00:00
break ;
}
2006-01-23 23:33:47 +00:00
2012-11-19 08:31:03 +00:00
case BIND_KEY_NAMES :
2012-11-19 00:30:30 +00:00
{
2012-11-19 08:31:03 +00:00
builtin_bind_key_names ( all ) ;
2012-11-19 00:30:30 +00:00
break ;
}
2006-01-23 23:33:47 +00:00
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
case BIND_FUNCTION_NAMES :
{
builtin_bind_function_names ( ) ;
break ;
}
2005-12-17 12:25:46 +00:00
2012-06-30 02:22:41 +00:00
2012-11-19 08:31:03 +00:00
default :
{
res = STATUS_BUILTIN_ERROR ;
append_format ( stderr_buffer , _ ( L " %ls: Invalid state \n " ) , argv [ 0 ] ) ;
break ;
}
2012-03-26 05:41:22 +00:00
}
2005-12-17 12:25:46 +00:00
2012-11-19 00:30:30 +00:00
return res ;
}
2005-09-20 13:26:39 +00:00
2013-12-31 00:52:41 +00:00
2005-09-20 13:26:39 +00:00
/**
2012-11-19 00:30:30 +00:00
The block builtin , used for temporarily blocking events
2005-09-20 13:26:39 +00:00
*/
2012-11-19 00:30:30 +00:00
static int builtin_block ( parser_t & parser , wchar_t * * argv )
2005-09-20 13:26:39 +00:00
{
2012-11-19 00:30:30 +00:00
enum
{
UNSET ,
GLOBAL ,
LOCAL ,
}
;
2006-01-30 19:53:10 +00:00
2012-11-19 00:30:30 +00:00
int scope = UNSET ;
int erase = 0 ;
int argc = builtin_count_args ( argv ) ;
2012-03-07 19:35:22 +00:00
2012-11-19 00:30:30 +00:00
woptind = 0 ;
static const struct woption
long_options [ ] =
2012-10-17 09:56:03 +00:00
{
2012-11-19 00:30:30 +00:00
{
L " erase " , no_argument , 0 , ' e '
}
,
{
L " local " , no_argument , 0 , ' l '
}
,
{
L " global " , no_argument , 0 , ' g '
}
,
{
L " help " , no_argument , 0 , ' h '
}
,
{
0 , 0 , 0 , 0
}
2012-10-17 09:56:03 +00:00
}
2012-11-19 00:30:30 +00:00
;
2012-10-17 09:56:03 +00:00
2012-11-19 00:30:30 +00:00
while ( 1 )
2012-10-17 09:56:03 +00:00
{
2012-11-19 00:30:30 +00:00
int opt_index = 0 ;
int opt = wgetopt_long ( argc ,
argv ,
L " elgh " ,
long_options ,
& opt_index ) ;
if ( opt = = - 1 )
break ;
switch ( opt )
{
2012-11-19 08:31:03 +00:00
case 0 :
if ( long_options [ opt_index ] . flag ! = 0 )
break ;
append_format ( stderr_buffer ,
BUILTIN_ERR_UNKNOWN ,
argv [ 0 ] ,
long_options [ opt_index ] . name ) ;
builtin_print_help ( parser , argv [ 0 ] , stderr_buffer ) ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
return STATUS_BUILTIN_ERROR ;
case ' h ' :
builtin_print_help ( parser , argv [ 0 ] , stdout_buffer ) ;
return STATUS_BUILTIN_OK ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
case ' g ' :
scope = GLOBAL ;
break ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
case ' l ' :
scope = LOCAL ;
break ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
case ' e ' :
erase = 1 ;
break ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
case ' ? ' :
builtin_unknown_option ( parser , argv [ 0 ] , argv [ woptind - 1 ] ) ;
return STATUS_BUILTIN_ERROR ;
2012-11-19 00:30:30 +00:00
}
}
if ( erase )
{
if ( scope ! = UNSET )
{
append_format ( stderr_buffer , _ ( L " %ls: Can not specify scope when removing block \n " ) , argv [ 0 ] ) ;
return STATUS_BUILTIN_ERROR ;
}
if ( parser . global_event_blocks . empty ( ) )
{
append_format ( stderr_buffer , _ ( L " %ls: No blocks defined \n " ) , argv [ 0 ] ) ;
return STATUS_BUILTIN_ERROR ;
}
parser . global_event_blocks . pop_front ( ) ;
}
else
{
2013-12-21 01:41:21 +00:00
size_t block_idx = 0 ;
block_t * block = parser . block_at_index ( block_idx ) ;
2012-11-19 00:30:30 +00:00
event_blockage_t eb = { } ;
2014-01-12 21:33:35 +00:00
eb . typemask = ( 1 < < EVENT_ANY ) ;
2012-11-19 00:30:30 +00:00
switch ( scope )
{
2012-11-19 08:31:03 +00:00
case LOCAL :
{
2013-12-21 01:41:21 +00:00
// If this is the outermost block, then we're global
if ( block_idx + 1 > = parser . block_count ( ) )
{
block = NULL ;
}
2012-11-19 08:31:03 +00:00
break ;
}
case GLOBAL :
{
2013-12-21 01:41:21 +00:00
block = NULL ;
2012-11-19 08:31:03 +00:00
}
case UNSET :
{
2013-12-21 01:41:21 +00:00
while ( block ! = NULL & & block - > type ( ) ! = FUNCTION_CALL & & block - > type ( ) ! = FUNCTION_CALL_NO_SHADOW )
{
// Set it in function scope
block = parser . block_at_index ( + + block_idx ) ;
}
2012-11-19 08:31:03 +00:00
}
2012-11-19 00:30:30 +00:00
}
if ( block )
{
block - > event_blocks . push_front ( eb ) ;
}
else
{
parser . global_event_blocks . push_front ( eb ) ;
}
}
return STATUS_BUILTIN_OK ;
2012-10-17 09:56:03 +00:00
}
2012-11-19 00:30:30 +00:00
/**
The builtin builtin , used for giving builtins precedence over
functions . Mostly handled by the parser . All this code does is some
additional operational modes , such as printing a list of all
builtins , printing help , etc .
2012-10-17 09:56:03 +00:00
*/
2012-11-19 00:30:30 +00:00
static int builtin_builtin ( parser_t & parser , wchar_t * * argv )
2012-10-17 09:56:03 +00:00
{
2012-11-19 00:30:30 +00:00
int argc = builtin_count_args ( argv ) ;
int list = 0 ;
woptind = 0 ;
static const struct woption
long_options [ ] =
2012-10-17 09:56:03 +00:00
{
2012-11-19 00:30:30 +00:00
{
L " names " , no_argument , 0 , ' n '
}
,
{
L " help " , no_argument , 0 , ' h '
}
,
{
0 , 0 , 0 , 0
}
}
;
while ( 1 )
{
int opt_index = 0 ;
int opt = wgetopt_long ( argc ,
argv ,
L " nh " ,
long_options ,
& opt_index ) ;
if ( opt = = - 1 )
break ;
switch ( opt )
{
2012-11-19 08:31:03 +00:00
case 0 :
if ( long_options [ opt_index ] . flag ! = 0 )
break ;
append_format ( stderr_buffer ,
BUILTIN_ERR_UNKNOWN ,
argv [ 0 ] ,
long_options [ opt_index ] . name ) ;
builtin_print_help ( parser , argv [ 0 ] , stderr_buffer ) ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
return STATUS_BUILTIN_ERROR ;
case ' h ' :
builtin_print_help ( parser , argv [ 0 ] , stdout_buffer ) ;
return STATUS_BUILTIN_OK ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
case ' n ' :
list = 1 ;
break ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
case ' ? ' :
builtin_unknown_option ( parser , argv [ 0 ] , argv [ woptind - 1 ] ) ;
return STATUS_BUILTIN_ERROR ;
2012-11-19 00:30:30 +00:00
}
2012-10-18 00:08:45 +00:00
2012-10-17 09:56:03 +00:00
}
2012-11-19 00:30:30 +00:00
if ( list )
2012-10-17 09:56:03 +00:00
{
2012-11-19 00:30:30 +00:00
wcstring_list_t names = builtin_get_names ( ) ;
sort ( names . begin ( ) , names . end ( ) ) ;
for ( size_t i = 0 ; i < names . size ( ) ; i + + )
{
const wchar_t * el = names . at ( i ) . c_str ( ) ;
stdout_buffer . append ( el ) ;
stdout_buffer . append ( L " \n " ) ;
}
2012-10-17 09:56:03 +00:00
}
2012-11-19 00:30:30 +00:00
return STATUS_BUILTIN_OK ;
}
/**
Implementation of the builtin emit command , used to create events .
*/
static int builtin_emit ( parser_t & parser , wchar_t * * argv )
{
int argc = builtin_count_args ( argv ) ;
woptind = 0 ;
static const struct woption
long_options [ ] =
2012-10-17 09:56:03 +00:00
{
2012-10-18 00:08:45 +00:00
{
2012-11-19 00:30:30 +00:00
L " help " , no_argument , 0 , ' h '
2012-10-18 00:08:45 +00:00
}
2012-11-19 00:30:30 +00:00
,
2012-10-18 00:08:45 +00:00
{
2012-11-19 00:30:30 +00:00
0 , 0 , 0 , 0
2012-10-18 00:08:45 +00:00
}
2012-10-17 09:56:03 +00:00
}
2012-11-19 00:30:30 +00:00
;
while ( 1 )
{
int opt_index = 0 ;
int opt = wgetopt_long ( argc ,
argv ,
L " h " ,
long_options ,
& opt_index ) ;
if ( opt = = - 1 )
break ;
switch ( opt )
{
2012-11-19 08:31:03 +00:00
case 0 :
if ( long_options [ opt_index ] . flag ! = 0 )
break ;
append_format ( stderr_buffer ,
BUILTIN_ERR_UNKNOWN ,
argv [ 0 ] ,
long_options [ opt_index ] . name ) ;
builtin_print_help ( parser , argv [ 0 ] , stderr_buffer ) ;
return STATUS_BUILTIN_ERROR ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
case ' h ' :
builtin_print_help ( parser , argv [ 0 ] , stdout_buffer ) ;
return STATUS_BUILTIN_OK ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
case ' ? ' :
builtin_unknown_option ( parser , argv [ 0 ] , argv [ woptind - 1 ] ) ;
return STATUS_BUILTIN_ERROR ;
2012-11-19 00:30:30 +00:00
}
}
2012-12-22 20:21:31 +00:00
if ( ! argv [ woptind ] )
{
append_format ( stderr_buffer , L " %ls: expected event name \n " , argv [ 0 ] ) ;
return STATUS_BUILTIN_ERROR ;
2012-11-19 00:30:30 +00:00
}
2012-12-22 20:40:34 +00:00
const wchar_t * eventname = argv [ woptind ] ;
2012-12-22 17:38:28 +00:00
wcstring_list_t args ( argv + woptind + 1 , argv + argc ) ;
2012-12-20 00:11:55 +00:00
event_fire_generic ( eventname , & args ) ;
2012-11-19 00:30:30 +00:00
return STATUS_BUILTIN_OK ;
2012-10-17 09:56:03 +00:00
}
2006-01-30 19:53:10 +00:00
2014-07-10 01:21:06 +00:00
/**
Implementation of the builtin ' command ' . Actual command running is handled by
the parser , this just processes the flags .
*/
static int builtin_command ( parser_t & parser , wchar_t * * argv )
{
int argc = builtin_count_args ( argv ) ;
int print_path = 0 ;
woptind = 0 ;
static const struct woption
long_options [ ] =
{
2014-07-11 02:16:32 +00:00
{ L " search " , no_argument , 0 , ' s ' } ,
2014-07-10 01:21:06 +00:00
{ L " help " , no_argument , 0 , ' h ' } ,
{ 0 , 0 , 0 , 0 }
} ;
while ( 1 )
{
int opt_index = 0 ;
int opt = wgetopt_long ( argc ,
argv ,
2014-07-11 02:16:32 +00:00
L " svh " ,
2014-07-10 01:21:06 +00:00
long_options ,
& opt_index ) ;
if ( opt = = - 1 )
break ;
switch ( opt )
{
case 0 :
if ( long_options [ opt_index ] . flag ! = 0 )
break ;
append_format ( stderr_buffer ,
BUILTIN_ERR_UNKNOWN ,
argv [ 0 ] ,
long_options [ opt_index ] . name ) ;
builtin_print_help ( parser , argv [ 0 ] , stderr_buffer ) ;
return STATUS_BUILTIN_ERROR ;
case ' h ' :
builtin_print_help ( parser , argv [ 0 ] , stdout_buffer ) ;
return STATUS_BUILTIN_OK ;
2014-07-11 02:16:32 +00:00
case ' s ' :
case ' v ' :
2014-07-10 01:21:06 +00:00
print_path = 1 ;
break ;
case ' ? ' :
builtin_unknown_option ( parser , argv [ 0 ] , argv [ woptind - 1 ] ) ;
return STATUS_BUILTIN_ERROR ;
}
}
if ( ! print_path )
{
builtin_print_help ( parser , argv [ 0 ] , stdout_buffer ) ;
return STATUS_BUILTIN_ERROR ;
}
int found = 0 ;
for ( int idx = woptind ; argv [ idx ] ; + + idx )
{
const wchar_t * command_name = argv [ idx ] ;
wcstring path ;
if ( path_get_path ( command_name , & path ) )
{
append_format ( stdout_buffer , L " %ls \n " , path . c_str ( ) ) ;
+ + found ;
}
}
return found ? STATUS_BUILTIN_OK : STATUS_BUILTIN_ERROR ;
}
2012-11-19 00:30:30 +00:00
/**
A generic bultin that only supports showing a help message . This is
only a placeholder that prints the help message . Useful for
commands that live in the parser .
*/
static int builtin_generic ( parser_t & parser , wchar_t * * argv )
2012-03-07 19:35:22 +00:00
{
2012-11-19 00:30:30 +00:00
int argc = builtin_count_args ( argv ) ;
2014-01-15 09:40:40 +00:00
2014-01-12 23:10:59 +00:00
/* Hackish - if we have no arguments other than the command, we are a "naked invocation" and we just print help */
if ( argc = = 1 )
{
builtin_print_help ( parser , argv [ 0 ] , stdout_buffer ) ;
return STATUS_BUILTIN_ERROR ;
}
2014-01-15 09:40:40 +00:00
2012-11-19 00:30:30 +00:00
woptind = 0 ;
static const struct woption
long_options [ ] =
{
2014-01-12 23:10:59 +00:00
{ L " help " , no_argument , 0 , ' h ' } ,
{ 0 , 0 , 0 , 0 }
} ;
2012-11-19 00:30:30 +00:00
while ( 1 )
{
int opt_index = 0 ;
int opt = wgetopt_long ( argc ,
argv ,
L " h " ,
long_options ,
& opt_index ) ;
if ( opt = = - 1 )
2012-05-19 23:59:56 +00:00
break ;
2012-11-19 00:30:30 +00:00
switch ( opt )
{
2012-11-19 08:31:03 +00:00
case 0 :
if ( long_options [ opt_index ] . flag ! = 0 )
break ;
append_format ( stderr_buffer ,
BUILTIN_ERR_UNKNOWN ,
argv [ 0 ] ,
long_options [ opt_index ] . name ) ;
builtin_print_help ( parser , argv [ 0 ] , stderr_buffer ) ;
return STATUS_BUILTIN_ERROR ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
case ' h ' :
builtin_print_help ( parser , argv [ 0 ] , stdout_buffer ) ;
return STATUS_BUILTIN_OK ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
case ' ? ' :
builtin_unknown_option ( parser , argv [ 0 ] , argv [ woptind - 1 ] ) ;
return STATUS_BUILTIN_ERROR ;
2012-11-19 00:30:30 +00:00
2012-05-19 23:59:56 +00:00
}
2012-11-19 00:30:30 +00:00
2012-03-07 19:35:22 +00:00
}
2012-11-19 00:30:30 +00:00
return STATUS_BUILTIN_ERROR ;
}
2012-10-17 09:56:03 +00:00
2012-11-19 00:30:30 +00:00
/**
Output a definition of the specified function to the specified
string . Used by the functions builtin .
*/
static void functions_def ( const wcstring & name , wcstring & out )
{
CHECK ( ! name . empty ( ) , ) ;
2012-10-17 09:56:03 +00:00
2012-11-19 00:30:30 +00:00
wcstring desc , def ;
function_get_desc ( name , & desc ) ;
function_get_definition ( name , & def ) ;
event_t search ( EVENT_ANY ) ;
search . function_name = name ;
std : : vector < event_t * > ev ;
2012-12-20 10:48:36 +00:00
event_get ( search , & ev ) ;
2012-11-19 00:30:30 +00:00
out . append ( L " function " ) ;
/* Typically we prefer to specify the function name first, e.g. "function foo --description bar"
But If the function name starts with a - , we ' ll need to output it after all the options . */
bool defer_function_name = ( name . at ( 0 ) = = L ' - ' ) ;
if ( ! defer_function_name )
{
2013-10-09 14:39:24 +00:00
out . append ( escape_string ( name , true ) ) ;
2012-11-19 00:30:30 +00:00
}
if ( ! desc . empty ( ) )
{
wcstring esc_desc = escape_string ( desc , true ) ;
out . append ( L " --description " ) ;
out . append ( esc_desc ) ;
}
if ( ! function_get_shadows ( name ) )
{
out . append ( L " --no-scope-shadowing " ) ;
}
for ( size_t i = 0 ; i < ev . size ( ) ; i + + )
{
event_t * next = ev . at ( i ) ;
switch ( next - > type )
2012-10-17 09:56:03 +00:00
{
2012-11-19 08:31:03 +00:00
case EVENT_SIGNAL :
{
append_format ( out , L " --on-signal %ls " , sig2wcs ( next - > param1 . signal ) ) ;
break ;
}
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
case EVENT_VARIABLE :
{
append_format ( out , L " --on-variable %ls " , next - > str_param1 . c_str ( ) ) ;
break ;
}
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
case EVENT_EXIT :
{
if ( next - > param1 . pid > 0 )
append_format ( out , L " --on-process-exit %d " , next - > param1 . pid ) ;
else
append_format ( out , L " --on-job-exit %d " , - next - > param1 . pid ) ;
break ;
}
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
case EVENT_JOB_ID :
{
const job_t * j = job_get ( next - > param1 . job_id ) ;
if ( j )
append_format ( out , L " --on-job-exit %d " , j - > pgid ) ;
break ;
}
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
case EVENT_GENERIC :
{
append_format ( out , L " --on-event %ls " , next - > str_param1 . c_str ( ) ) ;
break ;
}
2012-11-19 00:30:30 +00:00
}
}
wcstring_list_t named = function_get_named_arguments ( name ) ;
2013-02-16 08:02:40 +00:00
if ( ! named . empty ( ) )
2012-11-19 00:30:30 +00:00
{
append_format ( out , L " --argument " ) ;
for ( size_t i = 0 ; i < named . size ( ) ; i + + )
{
append_format ( out , L " %ls " , named . at ( i ) . c_str ( ) ) ;
}
}
/* Output the function name if we deferred it */
if ( defer_function_name )
{
out . append ( L " -- " ) ;
2013-10-09 14:39:24 +00:00
out . append ( escape_string ( name , true ) ) ;
2012-11-19 00:30:30 +00:00
}
/* This forced tab is sort of crummy - not all functions start with a tab */
append_format ( out , L " \n \t %ls " , def . c_str ( ) ) ;
/* Append a newline before the 'end', unless there already is one there */
if ( ! string_suffixes_string ( L " \n " , def ) )
{
out . push_back ( L ' \n ' ) ;
}
out . append ( L " end \n " ) ;
}
/**
The functions builtin , used for listing and erasing functions .
*/
static int builtin_functions ( parser_t & parser , wchar_t * * argv )
{
int i ;
int erase = 0 ;
wchar_t * desc = 0 ;
int argc = builtin_count_args ( argv ) ;
int list = 0 ;
int show_hidden = 0 ;
int res = STATUS_BUILTIN_OK ;
int query = 0 ;
int copy = 0 ;
woptind = 0 ;
static const struct woption
long_options [ ] =
{
{
L " erase " , no_argument , 0 , ' e '
}
,
{
L " description " , required_argument , 0 , ' d '
}
,
{
L " names " , no_argument , 0 , ' n '
}
,
{
L " all " , no_argument , 0 , ' a '
}
,
{
L " help " , no_argument , 0 , ' h '
}
,
{
L " query " , no_argument , 0 , ' q '
}
,
{
L " copy " , no_argument , 0 , ' c '
}
,
{
0 , 0 , 0 , 0
}
}
;
while ( 1 )
{
int opt_index = 0 ;
int opt = wgetopt_long ( argc ,
argv ,
L " ed:nahqc " ,
long_options ,
& opt_index ) ;
if ( opt = = - 1 )
break ;
switch ( opt )
{
2012-11-19 08:31:03 +00:00
case 0 :
if ( long_options [ opt_index ] . flag ! = 0 )
break ;
append_format ( stderr_buffer ,
BUILTIN_ERR_UNKNOWN ,
argv [ 0 ] ,
long_options [ opt_index ] . name ) ;
builtin_print_help ( parser , argv [ 0 ] , stderr_buffer ) ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
return STATUS_BUILTIN_ERROR ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
case ' e ' :
erase = 1 ;
break ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
case ' d ' :
desc = woptarg ;
break ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
case ' n ' :
list = 1 ;
break ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
case ' a ' :
show_hidden = 1 ;
break ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
case ' h ' :
builtin_print_help ( parser , argv [ 0 ] , stdout_buffer ) ;
return STATUS_BUILTIN_OK ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
case ' q ' :
query = 1 ;
break ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
case ' c ' :
copy = 1 ;
break ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
case ' ? ' :
builtin_unknown_option ( parser , argv [ 0 ] , argv [ woptind - 1 ] ) ;
return STATUS_BUILTIN_ERROR ;
2012-11-19 00:30:30 +00:00
}
}
/*
Erase , desc , query , copy and list are mutually exclusive
*/
if ( ( erase + ( ! ! desc ) + list + query + copy ) > 1 )
{
append_format ( stderr_buffer ,
_ ( L " %ls: Invalid combination of options \n " ) ,
argv [ 0 ] ) ;
builtin_print_help ( parser , argv [ 0 ] , stderr_buffer ) ;
return STATUS_BUILTIN_ERROR ;
}
if ( erase )
{
int i ;
for ( i = woptind ; i < argc ; i + + )
2013-10-12 19:55:11 +00:00
function_remove_ignore_autoload ( argv [ i ] ) ;
2012-11-19 00:30:30 +00:00
return STATUS_BUILTIN_OK ;
}
else if ( desc )
{
wchar_t * func ;
if ( argc - woptind ! = 1 )
{
append_format ( stderr_buffer ,
_ ( L " %ls: Expected exactly one function name \n " ) ,
argv [ 0 ] ) ;
builtin_print_help ( parser , argv [ 0 ] , stderr_buffer ) ;
return STATUS_BUILTIN_ERROR ;
}
func = argv [ woptind ] ;
if ( ! function_exists ( func ) )
{
append_format ( stderr_buffer ,
_ ( L " %ls: Function '%ls' does not exist \n " ) ,
argv [ 0 ] ,
func ) ;
builtin_print_help ( parser , argv [ 0 ] , stderr_buffer ) ;
return STATUS_BUILTIN_ERROR ;
}
function_set_desc ( func , desc ) ;
return STATUS_BUILTIN_OK ;
}
else if ( list | | ( argc = = woptind ) )
{
int is_screen = ! builtin_out_redirect & & isatty ( 1 ) ;
size_t i ;
wcstring_list_t names = function_get_names ( show_hidden ) ;
std : : sort ( names . begin ( ) , names . end ( ) ) ;
if ( is_screen )
{
wcstring buff ;
for ( i = 0 ; i < names . size ( ) ; i + + )
{
buff . append ( names . at ( i ) ) ;
buff . append ( L " , " ) ;
}
write_screen ( buff , stdout_buffer ) ;
}
else
{
for ( i = 0 ; i < names . size ( ) ; i + + )
{
stdout_buffer . append ( names . at ( i ) . c_str ( ) ) ;
stdout_buffer . append ( L " \n " ) ;
}
}
return STATUS_BUILTIN_OK ;
}
else if ( copy )
{
wcstring current_func ;
wcstring new_func ;
if ( argc - woptind ! = 2 )
{
append_format ( stderr_buffer ,
_ ( L " %ls: Expected exactly two names (current function name, and new function name) \n " ) ,
argv [ 0 ] ) ;
builtin_print_help ( parser , argv [ 0 ] , stderr_buffer ) ;
return STATUS_BUILTIN_ERROR ;
}
current_func = argv [ woptind ] ;
new_func = argv [ woptind + 1 ] ;
if ( ! function_exists ( current_func ) )
{
append_format ( stderr_buffer ,
_ ( L " %ls: Function '%ls' does not exist \n " ) ,
argv [ 0 ] ,
current_func . c_str ( ) ) ;
builtin_print_help ( parser , argv [ 0 ] , stderr_buffer ) ;
return STATUS_BUILTIN_ERROR ;
}
if ( ( wcsfuncname ( new_func . c_str ( ) ) ! = 0 ) | | parser_keywords_is_reserved ( new_func ) )
{
append_format ( stderr_buffer ,
_ ( L " %ls: Illegal function name '%ls' \n " ) ,
argv [ 0 ] ,
new_func . c_str ( ) ) ;
builtin_print_help ( parser , argv [ 0 ] , stderr_buffer ) ;
return STATUS_BUILTIN_ERROR ;
}
// keep things simple: don't allow existing names to be copy targets.
if ( function_exists ( new_func ) )
{
append_format ( stderr_buffer ,
_ ( L " %ls: Function '%ls' already exists. Cannot create copy '%ls' \n " ) ,
argv [ 0 ] ,
new_func . c_str ( ) ,
current_func . c_str ( ) ) ;
builtin_print_help ( parser , argv [ 0 ] , stderr_buffer ) ;
return STATUS_BUILTIN_ERROR ;
}
if ( function_copy ( current_func , new_func ) )
return STATUS_BUILTIN_OK ;
return STATUS_BUILTIN_ERROR ;
}
for ( i = woptind ; i < argc ; i + + )
{
if ( ! function_exists ( argv [ i ] ) )
res + + ;
else
{
if ( ! query )
{
if ( i ! = woptind )
stdout_buffer . append ( L " \n " ) ;
functions_def ( argv [ i ] , stdout_buffer ) ;
}
}
}
return res ;
}
static unsigned int builtin_echo_digit ( wchar_t wc , unsigned int base )
{
// base must be hex or octal
assert ( base = = 8 | | base = = 16 ) ;
switch ( wc )
{
2012-11-19 08:31:03 +00:00
case L ' 0 ' :
return 0 ;
case L ' 1 ' :
return 1 ;
case L ' 2 ' :
return 2 ;
case L ' 3 ' :
return 3 ;
case L ' 4 ' :
return 4 ;
case L ' 5 ' :
return 5 ;
case L ' 6 ' :
return 6 ;
case L ' 7 ' :
return 7 ;
2012-11-19 00:30:30 +00:00
}
if ( base = = 16 ) switch ( wc )
{
2012-11-19 08:31:03 +00:00
case L ' 8 ' :
return 8 ;
case L ' 9 ' :
return 9 ;
case L ' a ' :
case L ' A ' :
return 10 ;
case L ' b ' :
case L ' B ' :
return 11 ;
case L ' c ' :
case L ' C ' :
return 12 ;
case L ' d ' :
case L ' D ' :
return 13 ;
case L ' e ' :
case L ' E ' :
return 14 ;
case L ' f ' :
case L ' F ' :
return 15 ;
2012-11-19 00:30:30 +00:00
}
return UINT_MAX ;
}
/* Parse a numeric escape sequence in str, returning whether we succeeded.
Also return the number of characters consumed and the resulting value .
Supported escape sequences :
\ 0 nnn : octal value , zero to three digits
\ nnn : octal value , one to three digits
\ xhh : hex value , one to two digits
*/
static bool builtin_echo_parse_numeric_sequence ( const wchar_t * str , size_t * consumed , unsigned char * out_val )
{
bool success = false ;
unsigned char val = 0 ; //resulting character
unsigned int start = 0 ; //the first character of the numeric part of the sequence
unsigned int base = 0 , max_digits = 0 ;
if ( builtin_echo_digit ( str [ 0 ] , 8 ) ! = UINT_MAX )
{
// Octal escape
base = 8 ;
// If the first digit is a 0, we allow four digits (including that zero)
// Otherwise we allow 3.
max_digits = ( str [ 0 ] = = L ' 0 ' ? 4 : 3 ) ;
}
else if ( str [ 0 ] = = L ' x ' )
{
// Hex escape
base = 16 ;
max_digits = 2 ;
// Skip the x
start = 1 ;
}
if ( base ! = 0 )
{
unsigned int idx ;
for ( idx = start ; idx < start + max_digits ; idx + + )
{
unsigned int digit = builtin_echo_digit ( str [ idx ] , base ) ;
if ( digit = = UINT_MAX ) break ;
val = val * base + digit ;
}
// We succeeded if we consumed at least one digit
if ( idx > start )
{
* consumed = idx ;
* out_val = val ;
success = true ;
}
}
return success ;
}
/** The echo builtin.
bash only respects - n if it ' s the first argument . We ' ll do the same .
We also support a new option - s to mean " no spaces "
*/
static int builtin_echo ( parser_t & parser , wchar_t * * argv )
{
/* Skip first arg */
if ( ! * argv + + )
return STATUS_BUILTIN_ERROR ;
2014-05-16 07:19:07 +00:00
/* Process options. Options must come at the beginning - the first non-option kicks us out. */
2012-11-19 00:30:30 +00:00
bool print_newline = true , print_spaces = true , interpret_special_chars = false ;
2014-05-16 07:19:07 +00:00
size_t option_idx = 0 ;
for ( option_idx = 0 ; argv [ option_idx ] ! = NULL ; option_idx + + )
2012-11-19 00:30:30 +00:00
{
2014-05-16 07:19:07 +00:00
const wchar_t * arg = argv [ option_idx ] ;
assert ( arg ! = NULL ) ;
bool arg_is_valid_option = false ;
if ( arg [ 0 ] = = L ' - ' )
2012-11-19 00:30:30 +00:00
{
2014-05-16 07:19:07 +00:00
// We have a leading dash. Ensure that every subseqnent character is a valid option.
size_t i = 1 ;
while ( arg [ i ] ! = L ' \0 ' & & wcschr ( L " nesE " , arg [ i ] ) ! = NULL )
2013-09-19 21:20:05 +00:00
{
2014-05-16 07:19:07 +00:00
i + + ;
2013-09-19 21:20:05 +00:00
}
2014-05-16 07:19:07 +00:00
// We must have at least two characters to be a valid option, and have consumed the whole string
arg_is_valid_option = ( i > = 2 & & arg [ i ] = = L ' \0 ' ) ;
2012-11-19 00:30:30 +00:00
}
2014-05-16 07:19:07 +00:00
if ( ! arg_is_valid_option )
2012-11-19 00:30:30 +00:00
{
2014-05-16 07:19:07 +00:00
// This argument is not an option, so there are no more options
2012-11-19 00:30:30 +00:00
break ;
}
2014-05-16 07:19:07 +00:00
// Ok, we are sure the argument is an option. Parse it.
assert ( arg_is_valid_option ) ;
for ( size_t i = 1 ; arg [ i ] ! = L ' \0 ' ; i + + )
{
switch ( arg [ i ] )
{
case L ' n ' :
print_newline = false ;
break ;
case L ' e ' :
interpret_special_chars = true ;
break ;
case L ' s ' :
print_spaces = false ;
break ;
case L ' E ' :
interpret_special_chars = false ;
break ;
default :
assert ( 0 & & " Unexpected character in builtin_echo argument " ) ;
break ;
}
}
2012-11-19 00:30:30 +00:00
}
/* The special character \c can be used to indicate no more output */
bool continue_output = true ;
2014-05-16 07:19:07 +00:00
/* Skip over the options */
const wchar_t * const * args_to_echo = argv + option_idx ;
for ( size_t idx = 0 ; continue_output & & args_to_echo [ idx ] ! = NULL ; idx + + )
2012-11-19 00:30:30 +00:00
{
if ( print_spaces & & idx > 0 )
2014-05-16 07:19:07 +00:00
{
2012-11-19 00:30:30 +00:00
stdout_buffer . push_back ( ' ' ) ;
2014-05-16 07:19:07 +00:00
}
2012-11-19 00:30:30 +00:00
2014-05-16 07:19:07 +00:00
const wchar_t * str = args_to_echo [ idx ] ;
2012-11-19 00:30:30 +00:00
for ( size_t j = 0 ; continue_output & & str [ j ] ; j + + )
{
if ( ! interpret_special_chars | | str [ j ] ! = L ' \\ ' )
{
/* Not an escape */
stdout_buffer . push_back ( str [ j ] ) ;
}
else
{
/* Most escapes consume one character in addition to the backslash; the numeric sequences may consume more, while an unrecognized escape sequence consumes none. */
wchar_t wc ;
size_t consumed = 1 ;
switch ( str [ j + 1 ] )
{
2012-11-19 08:31:03 +00:00
case L ' a ' :
wc = L ' \a ' ;
break ;
case L ' b ' :
wc = L ' \b ' ;
break ;
case L ' e ' :
2013-07-12 07:40:29 +00:00
wc = L ' \x1B ' ;
2012-11-19 08:31:03 +00:00
break ;
case L ' f ' :
wc = L ' \f ' ;
break ;
case L ' n ' :
wc = L ' \n ' ;
break ;
case L ' r ' :
wc = L ' \r ' ;
break ;
case L ' t ' :
wc = L ' \t ' ;
break ;
case L ' v ' :
wc = L ' \v ' ;
break ;
case L ' \\ ' :
wc = L ' \\ ' ;
break ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
case L ' c ' :
wc = 0 ;
continue_output = false ;
break ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
default :
2012-11-19 00:30:30 +00:00
{
2012-11-19 08:31:03 +00:00
/* Octal and hex escape sequences */
unsigned char narrow_val = 0 ;
if ( builtin_echo_parse_numeric_sequence ( str + j + 1 , & consumed , & narrow_val ) )
{
/* Here consumed must have been set to something */
wc = narrow_val ; //is this OK for conversion?
}
else
{
/* Not a recognized escape. We consume only the backslash. */
wc = L ' \\ ' ;
consumed = 0 ;
}
break ;
2012-11-19 00:30:30 +00:00
}
}
/* Skip over characters that were part of this escape sequence (but not the backslash, which will be handled by the loop increment */
j + = consumed ;
if ( continue_output )
2014-05-16 07:19:07 +00:00
{
2012-11-19 00:30:30 +00:00
stdout_buffer . push_back ( wc ) ;
2014-05-16 07:19:07 +00:00
}
2012-11-19 00:30:30 +00:00
}
}
}
if ( print_newline & & continue_output )
2014-05-16 07:19:07 +00:00
{
2012-11-19 00:30:30 +00:00
stdout_buffer . push_back ( ' \n ' ) ;
2014-05-16 07:19:07 +00:00
}
2012-11-19 00:30:30 +00:00
return STATUS_BUILTIN_OK ;
}
/** The pwd builtin. We don't respect -P to resolve symbolic links because we try to always resolve them. */
static int builtin_pwd ( parser_t & parser , wchar_t * * argv )
{
wchar_t dir_path [ 4096 ] ;
wchar_t * res = wgetcwd ( dir_path , 4096 ) ;
if ( res = = NULL )
{
return STATUS_BUILTIN_ERROR ;
}
else
{
stdout_buffer . append ( dir_path ) ;
stdout_buffer . push_back ( L ' \n ' ) ;
return STATUS_BUILTIN_OK ;
}
}
2014-03-02 21:11:17 +00:00
/** Adds a function to the function set. It calls into function.cpp to perform any heavy lifting. */
2014-03-16 23:45:00 +00:00
int define_function ( parser_t & parser , const wcstring_list_t & c_args , const wcstring & contents , int definition_line_offset , wcstring * out_err )
2013-12-27 09:38:43 +00:00
{
assert ( out_err ! = NULL ) ;
2014-01-15 09:40:40 +00:00
2013-12-27 11:58:42 +00:00
/* wgetopt expects 'function' as the first argument. Make a new wcstring_list with that property. */
wcstring_list_t args ;
args . push_back ( L " function " ) ;
args . insert ( args . end ( ) , c_args . begin ( ) , c_args . end ( ) ) ;
2013-12-27 09:38:43 +00:00
/* Hackish const_cast matches the one in builtin_run */
const null_terminated_array_t < wchar_t > argv_array ( args ) ;
wchar_t * * argv = const_cast < wchar_t * * > ( argv_array . get ( ) ) ;
2014-01-15 09:40:40 +00:00
2012-11-19 00:30:30 +00:00
int argc = builtin_count_args ( argv ) ;
int res = STATUS_BUILTIN_OK ;
wchar_t * desc = 0 ;
std : : vector < event_t > events ;
std : : auto_ptr < wcstring_list_t > named_arguments ( NULL ) ;
wchar_t * name = 0 ;
bool shadows = true ;
woptind = 0 ;
2014-08-16 01:14:36 +00:00
wcstring_list_t wrap_targets ;
2012-11-19 00:30:30 +00:00
2013-04-13 08:32:07 +00:00
const struct woption long_options [ ] =
2012-11-19 00:30:30 +00:00
{
2013-04-13 08:32:07 +00:00
{ L " description " , required_argument , 0 , ' d ' } ,
{ L " on-signal " , required_argument , 0 , ' s ' } ,
{ L " on-job-exit " , required_argument , 0 , ' j ' } ,
{ L " on-process-exit " , required_argument , 0 , ' p ' } ,
{ L " on-variable " , required_argument , 0 , ' v ' } ,
{ L " on-event " , required_argument , 0 , ' e ' } ,
2014-08-16 01:14:36 +00:00
{ L " wraps " , required_argument , 0 , ' w ' } ,
2013-04-13 08:32:07 +00:00
{ L " help " , no_argument , 0 , ' h ' } ,
{ L " argument-names " , no_argument , 0 , ' a ' } ,
{ L " no-scope-shadowing " , no_argument , 0 , ' S ' } ,
{ 0 , 0 , 0 , 0 }
} ;
2012-11-19 00:30:30 +00:00
while ( 1 & & ( ! res ) )
{
int opt_index = 0 ;
int opt = wgetopt_long ( argc ,
argv ,
L " d:s:j:p:v:e:haS " ,
long_options ,
& opt_index ) ;
if ( opt = = - 1 )
break ;
switch ( opt )
{
2012-11-19 08:31:03 +00:00
case 0 :
if ( long_options [ opt_index ] . flag ! = 0 )
break ;
2014-01-15 09:40:40 +00:00
2013-12-27 09:38:43 +00:00
append_format ( * out_err ,
2012-11-19 08:31:03 +00:00
BUILTIN_ERR_UNKNOWN ,
argv [ 0 ] ,
long_options [ opt_index ] . name ) ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
res = 1 ;
break ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
case ' d ' :
desc = woptarg ;
break ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
case ' s ' :
2012-11-19 00:30:30 +00:00
{
2012-11-19 08:31:03 +00:00
int sig = wcs2sig ( woptarg ) ;
if ( sig < 0 )
{
2013-12-27 09:38:43 +00:00
append_format ( * out_err ,
2012-11-19 08:31:03 +00:00
_ ( L " %ls: Unknown signal '%ls' \n " ) ,
argv [ 0 ] ,
woptarg ) ;
res = 1 ;
break ;
}
events . push_back ( event_t : : signal_event ( sig ) ) ;
2012-11-19 00:30:30 +00:00
break ;
}
2012-11-19 08:31:03 +00:00
case ' v ' :
2012-11-19 00:30:30 +00:00
{
2012-11-19 08:31:03 +00:00
if ( wcsvarname ( woptarg ) )
{
2013-12-27 09:38:43 +00:00
append_format ( * out_err ,
2012-11-19 08:31:03 +00:00
_ ( L " %ls: Invalid variable name '%ls' \n " ) ,
argv [ 0 ] ,
woptarg ) ;
res = STATUS_BUILTIN_ERROR ;
break ;
}
events . push_back ( event_t : : variable_event ( woptarg ) ) ;
2012-11-19 00:30:30 +00:00
break ;
}
2012-11-19 08:31:03 +00:00
case ' e ' :
{
events . push_back ( event_t : : generic_event ( woptarg ) ) ;
break ;
}
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
case ' j ' :
case ' p ' :
2012-11-19 00:30:30 +00:00
{
2012-11-19 08:31:03 +00:00
pid_t pid ;
wchar_t * end ;
event_t e ( EVENT_ANY ) ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
if ( ( opt = = ' j ' ) & &
( wcscasecmp ( woptarg , L " caller " ) = = 0 ) )
2012-11-19 00:30:30 +00:00
{
2012-11-19 08:31:03 +00:00
int job_id = - 1 ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
if ( is_subshell )
{
2013-12-21 01:41:21 +00:00
size_t block_idx = 0 ;
2014-01-15 09:40:40 +00:00
2013-12-21 01:41:21 +00:00
/* Find the outermost substitution block */
for ( block_idx = 0 ; ; block_idx + + )
2012-11-19 08:31:03 +00:00
{
2013-12-21 01:41:21 +00:00
const block_t * b = parser . block_at_index ( block_idx ) ;
if ( b = = NULL | | b - > type ( ) = = SUBST )
break ;
2012-11-19 08:31:03 +00:00
}
2014-01-15 09:40:40 +00:00
2013-12-21 01:41:21 +00:00
/* Go one step beyond that, to get to the caller */
const block_t * caller_block = parser . block_at_index ( block_idx + 1 ) ;
if ( caller_block ! = NULL & & caller_block - > job ! = NULL )
2012-11-19 08:31:03 +00:00
{
2013-12-21 01:41:21 +00:00
job_id = caller_block - > job - > job_id ;
2012-11-19 08:31:03 +00:00
}
}
if ( job_id = = - 1 )
2012-11-19 00:30:30 +00:00
{
2013-12-27 09:38:43 +00:00
append_format ( * out_err ,
2012-11-19 08:31:03 +00:00
_ ( L " %ls: Cannot find calling job for event handler \n " ) ,
argv [ 0 ] ) ;
res = 1 ;
2012-11-19 00:30:30 +00:00
}
2012-11-19 08:31:03 +00:00
else
2012-11-19 00:30:30 +00:00
{
2012-11-19 08:31:03 +00:00
e . type = EVENT_JOB_ID ;
e . param1 . job_id = job_id ;
2012-11-19 00:30:30 +00:00
}
}
else
{
2012-11-19 08:31:03 +00:00
errno = 0 ;
pid = fish_wcstoi ( woptarg , & end , 10 ) ;
if ( errno | | ! end | | * end )
{
2013-12-27 09:38:43 +00:00
append_format ( * out_err ,
2012-11-19 08:31:03 +00:00
_ ( L " %ls: Invalid process id %ls \n " ) ,
argv [ 0 ] ,
woptarg ) ;
res = 1 ;
break ;
}
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
e . type = EVENT_EXIT ;
e . param1 . pid = ( opt = = ' j ' ? - 1 : 1 ) * abs ( pid ) ;
}
if ( res )
2012-11-19 00:30:30 +00:00
{
2012-11-19 08:31:03 +00:00
/* nothing */
2012-11-19 00:30:30 +00:00
}
2012-11-19 08:31:03 +00:00
else
{
events . push_back ( e ) ;
}
break ;
2012-11-19 00:30:30 +00:00
}
2012-11-19 08:31:03 +00:00
case ' a ' :
if ( named_arguments . get ( ) = = NULL )
named_arguments . reset ( new wcstring_list_t ) ;
break ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
case ' S ' :
shadows = 0 ;
break ;
2014-08-16 01:14:36 +00:00
case ' w ' :
wrap_targets . push_back ( woptarg ) ;
break ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
case ' h ' :
builtin_print_help ( parser , argv [ 0 ] , stdout_buffer ) ;
return STATUS_BUILTIN_OK ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
case ' ? ' :
builtin_unknown_option ( parser , argv [ 0 ] , argv [ woptind - 1 ] ) ;
res = 1 ;
break ;
2012-11-19 00:30:30 +00:00
}
}
if ( ! res )
{
if ( argc = = woptind )
{
2013-12-27 09:38:43 +00:00
append_format ( * out_err ,
2012-11-19 00:30:30 +00:00
_ ( L " %ls: Expected function name \n " ) ,
argv [ 0 ] ) ;
res = 1 ;
}
else if ( wcsfuncname ( argv [ woptind ] ) )
{
2013-12-27 09:38:43 +00:00
append_format ( * out_err ,
2012-11-19 00:30:30 +00:00
_ ( L " %ls: Illegal function name '%ls' \n " ) ,
argv [ 0 ] ,
argv [ woptind ] ) ;
res = 1 ;
}
else if ( parser_keywords_is_reserved ( argv [ woptind ] ) )
{
2013-12-27 09:38:43 +00:00
append_format ( * out_err ,
2012-11-19 00:30:30 +00:00
_ ( L " %ls: The name '%ls' is reserved, \n and can not be used as a function name \n " ) ,
argv [ 0 ] ,
argv [ woptind ] ) ;
res = 1 ;
}
2013-12-27 09:38:43 +00:00
else if ( ! wcslen ( argv [ woptind ] ) )
{
append_format ( * out_err , _ ( L " %ls: No function name given \n " ) , argv [ 0 ] ) ;
2014-05-06 10:07:16 +00:00
res = 1 ;
2013-12-27 09:38:43 +00:00
}
2012-11-19 00:30:30 +00:00
else
{
name = argv [ woptind + + ] ;
if ( named_arguments . get ( ) )
{
while ( woptind < argc )
{
if ( wcsvarname ( argv [ woptind ] ) )
{
2013-12-27 09:38:43 +00:00
append_format ( * out_err ,
2012-11-19 00:30:30 +00:00
_ ( L " %ls: Invalid variable name '%ls' \n " ) ,
argv [ 0 ] ,
argv [ woptind ] ) ;
res = STATUS_BUILTIN_ERROR ;
break ;
}
named_arguments - > push_back ( argv [ woptind + + ] ) ;
}
}
else if ( woptind ! = argc )
{
2013-12-27 09:38:43 +00:00
append_format ( * out_err ,
2012-11-19 00:30:30 +00:00
_ ( L " %ls: Expected one argument, got %d \n " ) ,
argv [ 0 ] ,
argc ) ;
res = 1 ;
}
}
}
if ( res )
{
2013-12-27 09:38:43 +00:00
builtin_print_help ( parser , argv [ 0 ] , * out_err ) ;
2012-11-19 00:30:30 +00:00
}
else
{
2013-12-27 09:38:43 +00:00
function_data_t d ;
2012-11-19 00:30:30 +00:00
d . name = name ;
if ( desc )
d . description = desc ;
d . events . swap ( events ) ;
d . shadows = shadows ;
if ( named_arguments . get ( ) )
d . named_arguments . swap ( * named_arguments ) ;
for ( size_t i = 0 ; i < d . events . size ( ) ; i + + )
{
event_t & e = d . events . at ( i ) ;
e . function_name = d . name ;
}
2013-12-27 09:38:43 +00:00
d . definition = contents . c_str ( ) ;
2012-11-19 00:30:30 +00:00
2014-03-16 23:45:00 +00:00
function_add ( d , parser , definition_line_offset ) ;
2014-08-16 01:14:36 +00:00
// Handle wrap targets
for ( size_t w = 0 ; w < wrap_targets . size ( ) ; w + + )
{
complete_add_wrapper ( name , wrap_targets . at ( w ) ) ;
}
2013-12-27 09:38:43 +00:00
}
return res ;
2012-11-19 00:30:30 +00:00
}
/**
The random builtin . For generating random numbers .
*/
static int builtin_random ( parser_t & parser , wchar_t * * argv )
{
static int seeded = 0 ;
static struct drand48_data seed_buffer ;
int argc = builtin_count_args ( argv ) ;
woptind = 0 ;
static const struct woption
long_options [ ] =
{
{
L " help " , no_argument , 0 , ' h '
}
,
{
0 , 0 , 0 , 0
}
}
;
while ( 1 )
{
int opt_index = 0 ;
int opt = wgetopt_long ( argc ,
argv ,
L " h " ,
long_options ,
& opt_index ) ;
if ( opt = = - 1 )
break ;
switch ( opt )
{
2012-11-19 08:31:03 +00:00
case 0 :
if ( long_options [ opt_index ] . flag ! = 0 )
break ;
append_format ( stderr_buffer ,
BUILTIN_ERR_UNKNOWN ,
argv [ 0 ] ,
long_options [ opt_index ] . name ) ;
builtin_print_help ( parser , argv [ 0 ] , stderr_buffer ) ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
return STATUS_BUILTIN_ERROR ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
case ' h ' :
builtin_print_help ( parser , argv [ 0 ] , stdout_buffer ) ;
break ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
case ' ? ' :
builtin_unknown_option ( parser , argv [ 0 ] , argv [ woptind - 1 ] ) ;
return STATUS_BUILTIN_ERROR ;
2012-11-19 00:30:30 +00:00
}
}
switch ( argc - woptind )
{
2012-11-19 08:31:03 +00:00
case 0 :
2012-11-19 00:30:30 +00:00
{
2012-11-19 08:31:03 +00:00
long res ;
if ( ! seeded )
{
seeded = 1 ;
srand48_r ( time ( 0 ) , & seed_buffer ) ;
}
lrand48_r ( & seed_buffer , & res ) ;
append_format ( stdout_buffer , L " %ld \n " , labs ( res % 32767 ) ) ;
break ;
2012-11-19 00:30:30 +00:00
}
2012-11-19 08:31:03 +00:00
case 1 :
{
long foo ;
wchar_t * end = 0 ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
errno = 0 ;
foo = wcstol ( argv [ woptind ] , & end , 10 ) ;
if ( errno | | * end )
{
append_format ( stderr_buffer ,
_ ( L " %ls: Seed value '%ls' is not a valid number \n " ) ,
argv [ 0 ] ,
argv [ woptind ] ) ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
return STATUS_BUILTIN_ERROR ;
}
seeded = 1 ;
srand48_r ( foo , & seed_buffer ) ;
break ;
}
default :
2012-11-19 00:30:30 +00:00
{
append_format ( stderr_buffer ,
2012-11-19 08:31:03 +00:00
_ ( L " %ls: Expected zero or one argument, got %d \n " ) ,
2012-11-19 00:30:30 +00:00
argv [ 0 ] ,
2012-11-19 08:31:03 +00:00
argc - woptind ) ;
builtin_print_help ( parser , argv [ 0 ] , stderr_buffer ) ;
2012-11-19 00:30:30 +00:00
return STATUS_BUILTIN_ERROR ;
}
}
return STATUS_BUILTIN_OK ;
}
/**
The read builtin . Reads from stdin and stores the values in environment variables .
*/
static int builtin_read ( parser_t & parser , wchar_t * * argv )
{
2014-09-22 02:18:56 +00:00
wcstring buff ;
2012-11-19 00:30:30 +00:00
int i , argc = builtin_count_args ( argv ) ;
int place = ENV_USER ;
const wchar_t * prompt = DEFAULT_READ_PROMPT ;
2014-09-22 04:09:21 +00:00
const wchar_t * right_prompt = L " " ;
2012-11-19 00:30:30 +00:00
const wchar_t * commandline = L " " ;
int exit_res = STATUS_BUILTIN_OK ;
const wchar_t * mode_name = READ_MODE_NAME ;
2014-08-19 12:28:08 +00:00
int nchars = 0 ;
wchar_t * end ;
2012-11-19 00:30:30 +00:00
int shell = 0 ;
2014-07-14 05:36:26 +00:00
int array = 0 ;
2014-09-22 02:18:56 +00:00
bool split_null = false ;
2012-11-19 00:30:30 +00:00
woptind = 0 ;
while ( 1 )
{
static const struct woption
long_options [ ] =
{
{
L " export " , no_argument , 0 , ' x '
}
,
{
L " global " , no_argument , 0 , ' g '
}
,
{
L " local " , no_argument , 0 , ' l '
}
,
{
L " universal " , no_argument , 0 , ' U '
}
,
{
L " unexport " , no_argument , 0 , ' u '
}
,
{
L " prompt " , required_argument , 0 , ' p '
}
,
2014-09-22 04:09:21 +00:00
{
L " right-prompt " , required_argument , 0 , ' R '
}
,
2012-11-19 00:30:30 +00:00
{
L " command " , required_argument , 0 , ' c '
}
,
{
L " mode-name " , required_argument , 0 , ' m '
}
,
2014-08-19 12:28:08 +00:00
{
L " nchars " , required_argument , 0 , ' n '
}
,
2012-11-19 00:30:30 +00:00
{
L " shell " , no_argument , 0 , ' s '
}
,
2014-07-14 05:36:26 +00:00
{
L " array " , no_argument , 0 , ' a '
}
,
2014-09-22 02:18:56 +00:00
{
L " null " , no_argument , 0 , ' z '
}
,
2012-11-19 00:30:30 +00:00
{
L " help " , no_argument , 0 , ' h '
}
,
{
0 , 0 , 0 , 0
}
}
;
int opt_index = 0 ;
int opt = wgetopt_long ( argc ,
argv ,
2014-09-22 04:09:21 +00:00
L " xglUup:R:c:hm:n:saz " ,
2012-11-19 00:30:30 +00:00
long_options ,
& opt_index ) ;
if ( opt = = - 1 )
break ;
switch ( opt )
{
2012-11-19 08:31:03 +00:00
case 0 :
if ( long_options [ opt_index ] . flag ! = 0 )
break ;
append_format ( stderr_buffer ,
BUILTIN_ERR_UNKNOWN ,
argv [ 0 ] ,
long_options [ opt_index ] . name ) ;
builtin_print_help ( parser , argv [ 0 ] , stderr_buffer ) ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
return STATUS_BUILTIN_ERROR ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
case L ' x ' :
place | = ENV_EXPORT ;
break ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
case L ' g ' :
place | = ENV_GLOBAL ;
break ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
case L ' l ' :
place | = ENV_LOCAL ;
break ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
case L ' U ' :
place | = ENV_UNIVERSAL ;
break ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
case L ' u ' :
place | = ENV_UNEXPORT ;
break ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
case L ' p ' :
prompt = woptarg ;
break ;
2012-11-19 00:30:30 +00:00
2014-09-22 04:09:21 +00:00
case L ' R ' :
right_prompt = woptarg ;
break ;
2012-11-19 08:31:03 +00:00
case L ' c ' :
commandline = woptarg ;
break ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
case L ' m ' :
mode_name = woptarg ;
break ;
2012-11-19 00:30:30 +00:00
2014-08-19 12:28:08 +00:00
case L ' n ' :
errno = 0 ;
nchars = fish_wcstoi ( woptarg , & end , 10 ) ;
if ( errno | | * end ! = 0 )
{
switch ( errno )
{
case ERANGE :
append_format ( stderr_buffer ,
_ ( L " %ls: Argument '%ls' is out of range \n " ) ,
argv [ 0 ] ,
woptarg ) ;
builtin_print_help ( parser , argv [ 0 ] , stderr_buffer ) ;
return STATUS_BUILTIN_ERROR ;
default :
append_format ( stderr_buffer ,
_ ( L " %ls: Argument '%ls' must be an integer \n " ) ,
argv [ 0 ] ,
woptarg ) ;
builtin_print_help ( parser , argv [ 0 ] , stderr_buffer ) ;
return STATUS_BUILTIN_ERROR ;
}
}
break ;
2012-11-19 08:31:03 +00:00
case ' s ' :
shell = 1 ;
break ;
2012-11-19 00:30:30 +00:00
2014-07-14 05:36:26 +00:00
case ' a ' :
array = 1 ;
break ;
2014-09-22 02:18:56 +00:00
case L ' z ' :
split_null = true ;
break ;
2012-11-19 08:31:03 +00:00
case ' h ' :
builtin_print_help ( parser , argv [ 0 ] , stdout_buffer ) ;
return STATUS_BUILTIN_OK ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
case L ' ? ' :
builtin_unknown_option ( parser , argv [ 0 ] , argv [ woptind - 1 ] ) ;
return STATUS_BUILTIN_ERROR ;
2012-11-19 00:30:30 +00:00
}
}
if ( ( place & ENV_UNEXPORT ) & & ( place & ENV_EXPORT ) )
{
append_format ( stderr_buffer ,
BUILTIN_ERR_EXPUNEXP ,
argv [ 0 ] ) ;
builtin_print_help ( parser , argv [ 0 ] , stderr_buffer ) ;
return STATUS_BUILTIN_ERROR ;
}
if ( ( place & ENV_LOCAL ? 1 : 0 ) + ( place & ENV_GLOBAL ? 1 : 0 ) + ( place & ENV_UNIVERSAL ? 1 : 0 ) > 1 )
{
append_format ( stderr_buffer ,
BUILTIN_ERR_GLOCAL ,
argv [ 0 ] ) ;
builtin_print_help ( parser , argv [ 0 ] , stderr_buffer ) ;
return STATUS_BUILTIN_ERROR ;
}
2014-07-14 05:36:26 +00:00
if ( array & & woptind + 1 ! = argc )
{
append_format ( stderr_buffer , _ ( L " %ls: --array option requires a single variable name. \n " ) , argv [ 0 ] ) ;
builtin_print_help ( parser , argv [ 0 ] , stderr_buffer ) ;
return STATUS_BUILTIN_ERROR ;
}
2012-11-19 00:30:30 +00:00
/*
Verify all variable names
*/
for ( i = woptind ; i < argc ; i + + )
{
wchar_t * src ;
if ( ! wcslen ( argv [ i ] ) )
{
append_format ( stderr_buffer , BUILTIN_ERR_VARNAME_ZERO , argv [ 0 ] ) ;
return STATUS_BUILTIN_ERROR ;
}
for ( src = argv [ i ] ; * src ; src + + )
{
if ( ( ! iswalnum ( * src ) ) & & ( * src ! = L ' _ ' ) )
{
append_format ( stderr_buffer , BUILTIN_ERR_VARCHAR , argv [ 0 ] , * src ) ;
builtin_print_help ( parser , argv [ 0 ] , stderr_buffer ) ;
return STATUS_BUILTIN_ERROR ;
}
}
}
/*
The call to reader_readline may change woptind , so we save it away here
*/
i = woptind ;
/*
Check if we should read interactively using \ c reader_readline ( )
*/
2014-09-22 02:18:56 +00:00
if ( isatty ( 0 ) & & builtin_stdin = = 0 & & ! split_null )
2012-11-19 00:30:30 +00:00
{
const wchar_t * line ;
reader_push ( mode_name ) ;
reader_set_left_prompt ( prompt ) ;
2014-09-22 04:09:21 +00:00
reader_set_right_prompt ( right_prompt ) ;
2012-11-19 00:30:30 +00:00
if ( shell )
{
reader_set_complete_function ( & complete ) ;
reader_set_highlight_function ( & highlight_shell ) ;
reader_set_test_function ( & reader_shell_test ) ;
}
2013-07-17 07:38:04 +00:00
/* No autosuggestions or abbreviations in builtin_read */
2012-11-19 00:30:30 +00:00
reader_set_allow_autosuggesting ( false ) ;
2013-07-17 07:38:04 +00:00
reader_set_expand_abbreviations ( false ) ;
2013-01-22 10:25:17 +00:00
reader_set_exit_on_interrupt ( true ) ;
2012-11-19 00:30:30 +00:00
reader_set_buffer ( commandline , wcslen ( commandline ) ) ;
proc_push_interactive ( 1 ) ;
event_fire_generic ( L " fish_prompt " ) ;
2014-08-19 12:28:08 +00:00
line = reader_readline ( nchars ) ;
2012-11-19 00:30:30 +00:00
proc_pop_interactive ( ) ;
if ( line )
{
2014-08-19 12:28:08 +00:00
if ( 0 < nchars & & nchars < wcslen ( line ) )
{
// line may be longer than nchars if a keybinding used `commandline -i`
// note: we're deliberately throwing away the tail of the commandline.
// It shouldn't be unread because it was produced with `commandline -i`,
// not typed.
2014-09-22 02:18:56 +00:00
buff = wcstring ( line , nchars ) ;
2014-08-19 12:28:08 +00:00
}
else
{
2014-09-22 02:18:56 +00:00
buff = wcstring ( line ) ;
2014-08-19 12:28:08 +00:00
}
2012-11-19 00:30:30 +00:00
}
else
{
exit_res = STATUS_BUILTIN_ERROR ;
}
reader_pop ( ) ;
}
else
{
int eof = 0 ;
2014-09-22 02:18:56 +00:00
buff . clear ( ) ;
2012-11-19 00:30:30 +00:00
while ( 1 )
{
int finished = 0 ;
wchar_t res = 0 ;
2014-09-22 03:47:03 +00:00
mbstate_t state = { } ;
2012-11-19 00:30:30 +00:00
while ( ! finished )
{
char b ;
if ( read_blocked ( builtin_stdin , & b , 1 ) < = 0 )
{
eof = 1 ;
break ;
}
size_t sz = mbrtowc ( & res , & b , 1 , & state ) ;
switch ( sz )
{
2012-11-19 08:31:03 +00:00
case ( size_t ) ( - 1 ) :
memset ( & state , ' \0 ' , sizeof ( state ) ) ;
break ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
case ( size_t ) ( - 2 ) :
break ;
case 0 :
finished = 1 ;
break ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
default :
finished = 1 ;
break ;
2012-11-19 00:30:30 +00:00
}
}
if ( eof )
break ;
2014-09-22 02:18:56 +00:00
if ( ! split_null & & res = = L ' \n ' )
2012-11-19 00:30:30 +00:00
break ;
2014-09-22 02:18:56 +00:00
if ( split_null & & res = = L ' \0 ' )
break ;
buff . push_back ( res ) ;
2014-08-19 12:28:08 +00:00
2014-09-22 02:18:56 +00:00
if ( 0 < nchars & & ( size_t ) nchars < = buff . size ( ) )
2014-08-19 12:28:08 +00:00
{
break ;
}
2012-11-19 00:30:30 +00:00
}
2014-09-22 03:00:26 +00:00
if ( buff . empty ( ) & & eof )
2012-11-19 00:30:30 +00:00
{
exit_res = 1 ;
}
}
if ( i ! = argc & & ! exit_res )
{
env_var_t ifs = env_get_string ( L " IFS " ) ;
2014-07-14 05:36:26 +00:00
if ( ifs . missing_or_empty ( ) )
{
/* Every character is a separate token */
2014-09-22 02:18:56 +00:00
size_t bufflen = buff . size ( ) ;
2014-07-14 05:36:26 +00:00
if ( array )
{
if ( bufflen > 0 )
{
wcstring chars ( bufflen + ( bufflen - 1 ) , ARRAY_SEP ) ;
2014-09-22 02:18:56 +00:00
wcstring : : iterator out = chars . begin ( ) ;
for ( wcstring : : const_iterator it = buff . begin ( ) , end = buff . end ( ) ; it ! = end ; + + it )
2014-07-14 05:36:26 +00:00
{
2014-09-22 02:18:56 +00:00
* out = * it ;
out + = 2 ;
2014-07-14 05:36:26 +00:00
}
env_set ( argv [ i ] , chars . c_str ( ) , place ) ;
}
else
{
env_set ( argv [ i ] , NULL , place ) ;
}
}
else
{
size_t j = 0 ;
for ( ; i + 1 < argc ; + + i )
{
2014-09-22 02:18:56 +00:00
if ( j < bufflen )
{
wchar_t buffer [ 2 ] = { buff [ j + + ] , 0 } ;
2014-08-22 19:52:41 +00:00
env_set ( argv [ i ] , buffer , place ) ;
}
2014-09-22 02:18:56 +00:00
else
{
2014-08-22 19:52:41 +00:00
env_set ( argv [ i ] , L " " , place ) ;
}
2014-07-14 05:36:26 +00:00
}
if ( i < argc ) env_set ( argv [ i ] , & buff [ j ] , place ) ;
}
}
else if ( array )
{
wcstring tokens ;
2014-09-22 02:18:56 +00:00
tokens . reserve ( buff . size ( ) ) ;
2014-07-14 05:36:26 +00:00
bool empty = true ;
2012-11-19 00:30:30 +00:00
2014-09-22 02:18:56 +00:00
for ( wcstring_range loc = wcstring_tok ( buff , ifs ) ; loc . first ! = wcstring : : npos ; loc = wcstring_tok ( buff , ifs , loc ) )
2014-07-14 05:36:26 +00:00
{
2014-09-22 02:18:56 +00:00
if ( ! empty ) tokens . push_back ( ARRAY_SEP ) ;
tokens . append ( buff , loc . first , loc . second ) ;
2014-07-14 05:36:26 +00:00
empty = false ;
}
env_set ( argv [ i ] , empty ? NULL : tokens . c_str ( ) , place ) ;
}
else
2012-11-19 00:30:30 +00:00
{
2014-09-22 02:18:56 +00:00
wcstring_range loc = wcstring_range ( 0 , 0 ) ;
2012-11-19 00:30:30 +00:00
2014-07-14 05:36:26 +00:00
while ( i < argc )
{
2014-09-22 02:18:56 +00:00
loc = wcstring_tok ( buff , ( i + 1 < argc ) ? ifs : L " " , loc ) ;
env_set ( argv [ i ] , loc . first = = wcstring : : npos ? L " " : & buff . c_str ( ) [ loc . first ] , place ) ;
2012-11-19 00:30:30 +00:00
2014-09-22 02:18:56 +00:00
+ + i ;
2014-07-14 05:36:26 +00:00
}
2014-09-22 02:18:56 +00:00
2012-11-19 00:30:30 +00:00
}
}
return exit_res ;
}
/**
The status builtin . Gives various status information on fish .
*/
static int builtin_status ( parser_t & parser , wchar_t * * argv )
{
enum
{
NORMAL ,
IS_SUBST ,
IS_BLOCK ,
IS_INTERACTIVE ,
IS_LOGIN ,
IS_FULL_JOB_CONTROL ,
IS_INTERACTIVE_JOB_CONTROL ,
IS_NO_JOB_CONTROL ,
STACK_TRACE ,
DONE ,
CURRENT_FILENAME ,
CURRENT_LINE_NUMBER
}
;
int mode = NORMAL ;
int argc = builtin_count_args ( argv ) ;
int res = STATUS_BUILTIN_OK ;
woptind = 0 ;
const struct woption
long_options [ ] =
{
{
L " help " , no_argument , 0 , ' h '
}
,
{
L " is-command-substitution " , no_argument , 0 , ' c '
}
,
{
L " is-block " , no_argument , 0 , ' b '
}
,
{
L " is-interactive " , no_argument , 0 , ' i '
}
,
{
L " is-login " , no_argument , 0 , ' l '
}
,
{
L " is-full-job-control " , no_argument , & mode , IS_FULL_JOB_CONTROL
}
,
{
L " is-interactive-job-control " , no_argument , & mode , IS_INTERACTIVE_JOB_CONTROL
}
,
{
L " is-no-job-control " , no_argument , & mode , IS_NO_JOB_CONTROL
}
,
{
L " current-filename " , no_argument , 0 , ' f '
}
,
{
L " current-line-number " , no_argument , 0 , ' n '
}
,
{
L " job-control " , required_argument , 0 , ' j '
}
,
{
L " print-stack-trace " , no_argument , 0 , ' t '
}
,
{
0 , 0 , 0 , 0
}
}
;
while ( 1 )
{
int opt_index = 0 ;
int opt = wgetopt_long ( argc ,
argv ,
L " :cbilfnhj:t " ,
long_options ,
& opt_index ) ;
if ( opt = = - 1 )
break ;
switch ( opt )
{
2012-11-19 08:31:03 +00:00
case 0 :
if ( long_options [ opt_index ] . flag ! = 0 )
break ;
append_format ( stderr_buffer ,
BUILTIN_ERR_UNKNOWN ,
argv [ 0 ] ,
long_options [ opt_index ] . name ) ;
builtin_print_help ( parser , argv [ 0 ] , stderr_buffer ) ;
return STATUS_BUILTIN_ERROR ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
case ' c ' :
mode = IS_SUBST ;
break ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
case ' b ' :
mode = IS_BLOCK ;
break ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
case ' i ' :
mode = IS_INTERACTIVE ;
break ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
case ' l ' :
mode = IS_LOGIN ;
break ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
case ' f ' :
mode = CURRENT_FILENAME ;
break ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
case ' n ' :
mode = CURRENT_LINE_NUMBER ;
break ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
case ' h ' :
builtin_print_help ( parser , argv [ 0 ] , stdout_buffer ) ;
return STATUS_BUILTIN_OK ;
case ' j ' :
if ( wcscmp ( woptarg , L " full " ) = = 0 )
job_control_mode = JOB_CONTROL_ALL ;
else if ( wcscmp ( woptarg , L " interactive " ) = = 0 )
job_control_mode = JOB_CONTROL_INTERACTIVE ;
else if ( wcscmp ( woptarg , L " none " ) = = 0 )
job_control_mode = JOB_CONTROL_NONE ;
else
{
append_format ( stderr_buffer ,
L " %ls: Invalid job control mode '%ls' \n " ,
2014-07-08 21:05:04 +00:00
L " status " , woptarg ) ;
2012-11-19 08:31:03 +00:00
res = 1 ;
}
mode = DONE ;
break ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
case ' t ' :
mode = STACK_TRACE ;
break ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
case ' : ' :
builtin_missing_argument ( parser , argv [ 0 ] , argv [ woptind - 1 ] ) ;
return STATUS_BUILTIN_ERROR ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
case ' ? ' :
builtin_unknown_option ( parser , argv [ 0 ] , argv [ woptind - 1 ] ) ;
return STATUS_BUILTIN_ERROR ;
2012-11-19 00:30:30 +00:00
}
}
if ( ! res )
{
switch ( mode )
{
2012-11-19 08:31:03 +00:00
case CURRENT_FILENAME :
{
const wchar_t * fn = parser . current_filename ( ) ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
if ( ! fn )
fn = _ ( L " Standard input " ) ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
append_format ( stdout_buffer , L " %ls \n " , fn ) ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
break ;
}
2005-09-20 13:26:39 +00:00
2012-11-19 08:31:03 +00:00
case CURRENT_LINE_NUMBER :
{
append_format ( stdout_buffer , L " %d \n " , parser . get_lineno ( ) ) ;
break ;
}
2005-10-15 00:51:26 +00:00
2012-11-19 08:31:03 +00:00
case IS_INTERACTIVE :
return ! is_interactive_session ;
2012-02-09 07:53:23 +00:00
2012-11-19 08:31:03 +00:00
case IS_SUBST :
return ! is_subshell ;
2005-09-20 13:26:39 +00:00
2012-11-19 08:31:03 +00:00
case IS_BLOCK :
return ! is_block ;
2006-01-04 12:51:02 +00:00
2012-11-19 08:31:03 +00:00
case IS_LOGIN :
return ! is_login ;
2006-05-14 09:47:21 +00:00
2012-11-19 08:31:03 +00:00
case IS_FULL_JOB_CONTROL :
return job_control_mode ! = JOB_CONTROL_ALL ;
2006-01-30 19:53:10 +00:00
2012-11-19 08:31:03 +00:00
case IS_INTERACTIVE_JOB_CONTROL :
return job_control_mode ! = JOB_CONTROL_INTERACTIVE ;
2005-09-20 13:26:39 +00:00
2012-11-19 08:31:03 +00:00
case IS_NO_JOB_CONTROL :
return job_control_mode ! = JOB_CONTROL_NONE ;
2006-01-30 19:53:10 +00:00
2012-11-19 08:31:03 +00:00
case STACK_TRACE :
{
2013-12-21 01:41:21 +00:00
parser . stack_trace ( 0 , stdout_buffer ) ;
2012-11-19 08:31:03 +00:00
break ;
}
2005-09-20 13:26:39 +00:00
2012-11-19 08:31:03 +00:00
case NORMAL :
{
if ( is_login )
append_format ( stdout_buffer , _ ( L " This is a login shell \n " ) ) ;
else
append_format ( stdout_buffer , _ ( L " This is not a login shell \n " ) ) ;
2005-09-20 13:26:39 +00:00
2012-11-19 08:31:03 +00:00
append_format ( stdout_buffer , _ ( L " Job control: %ls \n " ) ,
job_control_mode = = JOB_CONTROL_INTERACTIVE ? _ ( L " Only on interactive jobs " ) :
( job_control_mode = = JOB_CONTROL_NONE ? _ ( L " Never " ) : _ ( L " Always " ) ) ) ;
2005-09-20 13:26:39 +00:00
2013-12-21 01:41:21 +00:00
parser . stack_trace ( 0 , stdout_buffer ) ;
2012-11-19 08:31:03 +00:00
break ;
}
2012-11-19 00:30:30 +00:00
}
}
2005-09-20 13:26:39 +00:00
2012-11-19 00:30:30 +00:00
return res ;
2005-09-20 13:26:39 +00:00
}
/**
The exit builtin . Calls reader_exit to exit and returns the value specified .
*/
2012-11-19 00:30:30 +00:00
static int builtin_exit ( parser_t & parser , wchar_t * * argv )
2006-01-30 19:53:10 +00:00
{
2012-11-19 00:30:30 +00:00
int argc = builtin_count_args ( argv ) ;
long ec = 0 ;
switch ( argc )
{
2012-11-19 08:31:03 +00:00
case 1 :
{
ec = proc_get_last_status ( ) ;
break ;
}
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
case 2 :
{
wchar_t * end ;
errno = 0 ;
ec = wcstol ( argv [ 1 ] , & end , 10 ) ;
if ( errno | | * end ! = 0 )
{
append_format ( stderr_buffer ,
_ ( L " %ls: Argument '%ls' must be an integer \n " ) ,
argv [ 0 ] ,
argv [ 1 ] ) ;
builtin_print_help ( parser , argv [ 0 ] , stderr_buffer ) ;
return STATUS_BUILTIN_ERROR ;
}
break ;
}
default :
2012-11-19 00:30:30 +00:00
{
append_format ( stderr_buffer ,
2012-11-19 08:31:03 +00:00
BUILTIN_ERR_TOO_MANY_ARGUMENTS ,
argv [ 0 ] ) ;
2012-11-19 00:30:30 +00:00
builtin_print_help ( parser , argv [ 0 ] , stderr_buffer ) ;
return STATUS_BUILTIN_ERROR ;
}
}
reader_exit ( 1 , 0 ) ;
return ( int ) ec ;
2005-09-20 13:26:39 +00:00
}
/**
The cd builtin . Changes the current directory to the one specified
2007-09-28 21:39:22 +00:00
or to $ HOME if none is specified . The directory can be relative to
any directory in the CDPATH variable .
2005-09-20 13:26:39 +00:00
*/
2012-11-19 00:30:30 +00:00
static int builtin_cd ( parser_t & parser , wchar_t * * argv )
2005-09-20 13:26:39 +00:00
{
2012-11-19 00:30:30 +00:00
env_var_t dir_in ;
wcstring dir ;
int res = STATUS_BUILTIN_OK ;
if ( argv [ 1 ] = = NULL )
{
dir_in = env_get_string ( L " HOME " ) ;
if ( dir_in . missing_or_empty ( ) )
{
append_format ( stderr_buffer ,
_ ( L " %ls: Could not find home directory \n " ) ,
argv [ 0 ] ) ;
}
}
else
{
dir_in = argv [ 1 ] ;
2012-05-09 09:33:42 +00:00
}
2006-01-30 19:53:10 +00:00
2012-07-21 05:11:05 +00:00
bool got_cd_path = false ;
if ( ! dir_in . missing ( ) )
{
got_cd_path = path_get_cdpath ( dir_in , & dir ) ;
2012-05-09 09:33:42 +00:00
}
2006-01-30 19:53:10 +00:00
2012-11-19 00:30:30 +00:00
if ( ! got_cd_path )
{
if ( errno = = ENOTDIR )
{
append_format ( stderr_buffer ,
_ ( L " %ls: '%ls' is not a directory \n " ) ,
argv [ 0 ] ,
dir_in . c_str ( ) ) ;
}
else if ( errno = = ENOENT )
{
append_format ( stderr_buffer ,
_ ( L " %ls: The directory '%ls' does not exist \n " ) ,
argv [ 0 ] ,
dir_in . c_str ( ) ) ;
}
else if ( errno = = EROTTEN )
{
append_format ( stderr_buffer ,
_ ( L " %ls: '%ls' is a rotten symlink \n " ) ,
argv [ 0 ] ,
dir_in . c_str ( ) ) ;
}
else
{
append_format ( stderr_buffer ,
_ ( L " %ls: Unknown error trying to locate directory '%ls' \n " ) ,
argv [ 0 ] ,
dir_in . c_str ( ) ) ;
}
if ( ! get_is_interactive ( ) )
{
2012-02-22 18:51:06 +00:00
stderr_buffer . append ( parser . current_line ( ) ) ;
2012-11-19 00:30:30 +00:00
}
res = 1 ;
}
else if ( wchdir ( dir ) ! = 0 )
{
struct stat buffer ;
int status ;
status = wstat ( dir , & buffer ) ;
if ( ! status & & S_ISDIR ( buffer . st_mode ) )
{
append_format ( stderr_buffer ,
_ ( L " %ls: Permission denied: '%ls' \n " ) ,
argv [ 0 ] ,
dir . c_str ( ) ) ;
}
else
{
append_format ( stderr_buffer ,
_ ( L " %ls: '%ls' is not a directory \n " ) ,
argv [ 0 ] ,
dir . c_str ( ) ) ;
}
if ( ! get_is_interactive ( ) )
{
2012-02-22 18:51:06 +00:00
stderr_buffer . append ( parser . current_line ( ) ) ;
2012-11-19 00:30:30 +00:00
}
res = 1 ;
}
else if ( ! env_set_pwd ( ) )
{
res = 1 ;
append_format ( stderr_buffer , _ ( L " %ls: Could not set PWD variable \n " ) , argv [ 0 ] ) ;
}
return res ;
2005-09-20 13:26:39 +00:00
}
2008-01-13 16:47:47 +00:00
/**
Implementation of the builtin count command , used to count the
number of arguments sent to it .
*/
2012-11-19 00:30:30 +00:00
static int builtin_count ( parser_t & parser , wchar_t * * argv )
2007-07-31 21:23:32 +00:00
{
2012-11-19 00:30:30 +00:00
int argc ;
argc = builtin_count_args ( argv ) ;
append_format ( stdout_buffer , L " %d \n " , argc - 1 ) ;
return ! ( argc - 1 ) ;
2007-07-31 21:23:32 +00:00
}
2005-09-20 13:26:39 +00:00
2008-01-13 16:47:47 +00:00
/**
Implementation of the builtin contains command , used to check if a
specified string is part of a list .
*/
2012-11-19 00:30:30 +00:00
static int builtin_contains ( parser_t & parser , wchar_t * * argv )
2007-08-01 22:53:18 +00:00
{
2012-11-19 00:30:30 +00:00
int argc ;
argc = builtin_count_args ( argv ) ;
wchar_t * needle ;
2013-04-28 21:35:00 +00:00
bool should_output_index = false ;
2012-11-19 00:30:30 +00:00
woptind = 0 ;
2013-04-28 21:35:00 +00:00
const struct woption long_options [ ] =
2012-11-19 00:30:30 +00:00
{
2013-04-28 21:35:00 +00:00
{ L " help " , no_argument , 0 , ' h ' } ,
{ L " index " , no_argument , 0 , ' i ' } ,
{ 0 , 0 , 0 , 0 }
} ;
2012-11-19 00:30:30 +00:00
while ( 1 )
{
int opt_index = 0 ;
int opt = wgetopt_long ( argc ,
argv ,
L " +hi " ,
long_options ,
& opt_index ) ;
if ( opt = = - 1 )
break ;
switch ( opt )
{
2012-11-19 08:31:03 +00:00
case 0 :
assert ( opt_index > = 0 & & ( size_t ) opt_index < sizeof long_options / sizeof * long_options ) ;
if ( long_options [ opt_index ] . flag ! = 0 )
break ;
append_format ( stderr_buffer ,
BUILTIN_ERR_UNKNOWN ,
argv [ 0 ] ,
long_options [ opt_index ] . name ) ;
builtin_print_help ( parser , argv [ 0 ] , stderr_buffer ) ;
return STATUS_BUILTIN_ERROR ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
case ' h ' :
builtin_print_help ( parser , argv [ 0 ] , stdout_buffer ) ;
return STATUS_BUILTIN_OK ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
case ' : ' :
builtin_missing_argument ( parser , argv [ 0 ] , argv [ woptind - 1 ] ) ;
return STATUS_BUILTIN_ERROR ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
case ' ? ' :
builtin_unknown_option ( parser , argv [ 0 ] , argv [ woptind - 1 ] ) ;
return STATUS_BUILTIN_ERROR ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
case ' i ' :
2013-04-28 21:35:00 +00:00
should_output_index = true ;
2012-11-19 08:31:03 +00:00
break ;
2012-11-19 00:30:30 +00:00
}
}
needle = argv [ woptind ] ;
if ( ! needle )
{
append_format ( stderr_buffer , _ ( L " %ls: Key not specified \n " ) , argv [ 0 ] ) ;
}
2013-04-28 21:35:00 +00:00
for ( int i = woptind + 1 ; i < argc ; i + + )
2012-11-19 00:30:30 +00:00
{
if ( ! wcscmp ( needle , argv [ i ] ) )
{
2013-04-28 21:35:00 +00:00
if ( should_output_index ) append_format ( stdout_buffer , L " %d \n " , i - woptind ) ;
2012-11-19 00:30:30 +00:00
return 0 ;
}
}
return 1 ;
2007-08-14 21:42:57 +00:00
2007-08-01 22:53:18 +00:00
}
2005-09-20 13:26:39 +00:00
/**
2006-01-30 19:53:10 +00:00
The . ( dot ) builtin , sometimes called source . Evaluates the contents of a file .
2005-09-20 13:26:39 +00:00
*/
2012-11-19 00:30:30 +00:00
static int builtin_source ( parser_t & parser , wchar_t * * argv )
2005-09-20 13:26:39 +00:00
{
2012-03-02 08:27:40 +00:00
ASSERT_IS_MAIN_THREAD ( ) ;
2012-11-19 00:30:30 +00:00
int fd ;
int res = STATUS_BUILTIN_OK ;
struct stat buf ;
int argc ;
argc = builtin_count_args ( argv ) ;
2013-12-16 00:43:22 +00:00
const wchar_t * fn , * fn_intern ;
2012-11-19 00:30:30 +00:00
if ( argc < 2 | | ( wcscmp ( argv [ 1 ] , L " - " ) = = 0 ) )
{
fn = L " - " ;
fn_intern = fn ;
fd = dup ( builtin_stdin ) ;
}
else
{
if ( ( fd = wopen_cloexec ( argv [ 1 ] , O_RDONLY ) ) = = - 1 )
{
append_format ( stderr_buffer , _ ( L " %ls: Error encountered while sourcing file '%ls': \n " ) , argv [ 0 ] , argv [ 1 ] ) ;
2013-08-14 15:43:09 +00:00
builtin_wperror ( L " source " ) ;
2012-11-19 00:30:30 +00:00
return STATUS_BUILTIN_ERROR ;
}
if ( fstat ( fd , & buf ) = = - 1 )
{
close ( fd ) ;
append_format ( stderr_buffer , _ ( L " %ls: Error encountered while sourcing file '%ls': \n " ) , argv [ 0 ] , argv [ 1 ] ) ;
2013-08-14 15:43:09 +00:00
builtin_wperror ( L " source " ) ;
2012-11-19 00:30:30 +00:00
return STATUS_BUILTIN_ERROR ;
}
if ( ! S_ISREG ( buf . st_mode ) )
{
close ( fd ) ;
append_format ( stderr_buffer , _ ( L " %ls: '%ls' is not a file \n " ) , argv [ 0 ] , argv [ 1 ] ) ;
return STATUS_BUILTIN_ERROR ;
}
2013-12-16 00:43:22 +00:00
fn_intern = intern ( argv [ 1 ] ) ;
2012-11-19 00:30:30 +00:00
}
parser . push_block ( new source_block_t ( fn_intern ) ) ;
reader_push_current_filename ( fn_intern ) ;
parse_util_set_argv ( ( argc > 2 ) ? ( argv + 2 ) : ( argv + 1 ) , wcstring_list_t ( ) ) ;
res = reader_read ( fd , real_io ? * real_io : io_chain_t ( ) ) ;
parser . pop_block ( ) ;
if ( res )
{
append_format ( stderr_buffer ,
_ ( L " %ls: Error while reading file '%ls' \n " ) ,
argv [ 0 ] ,
fn_intern = = intern_static ( L " - " ) ? L " <stdin> " : fn_intern ) ;
}
else
{
res = proc_get_last_status ( ) ;
}
/*
Do not close fd after calling reader_read . reader_read
automatically closes it before calling eval .
*/
reader_pop_current_filename ( ) ;
return res ;
}
/**
Make the specified job the first job of the job list . Moving jobs
around in the list makes the list reflect the order in which the
jobs were used .
*/
static void make_first ( job_t * j )
{
job_promote ( j ) ;
2005-09-20 13:26:39 +00:00
}
2012-11-19 00:30:30 +00:00
/**
Builtin for putting a job in the foreground
*/
static int builtin_fg ( parser_t & parser , wchar_t * * argv )
{
job_t * j = NULL ;
if ( argv [ 1 ] = = 0 )
{
/*
Select last constructed job ( I . e . first job in the job que )
that is possible to put in the foreground
*/
job_iterator_t jobs ;
while ( ( j = jobs . next ( ) ) )
{
if ( job_get_flag ( j , JOB_CONSTRUCTED ) & & ( ! job_is_completed ( j ) ) & &
( ( job_is_stopped ( j ) | | ( ! job_get_flag ( j , JOB_FOREGROUND ) ) ) & & job_get_flag ( j , JOB_CONTROL ) ) )
{
break ;
}
}
if ( ! j )
{
append_format ( stderr_buffer ,
_ ( L " %ls: There are no suitable jobs \n " ) ,
argv [ 0 ] ) ;
builtin_print_help ( parser , argv [ 0 ] , stderr_buffer ) ;
}
}
else if ( argv [ 2 ] ! = 0 )
{
/*
Specifying what more than one job to put to the foreground
is a syntax error , we still try to locate the job argv [ 1 ] ,
since we want to know if this is an ambigous job
specification or if this is an malformed job id
*/
wchar_t * endptr ;
int pid ;
int found_job = 0 ;
errno = 0 ;
pid = fish_wcstoi ( argv [ 1 ] , & endptr , 10 ) ;
if ( ! ( * endptr | | errno ) )
{
j = job_get_from_pid ( pid ) ;
if ( j )
found_job = 1 ;
}
if ( found_job )
{
append_format ( stderr_buffer ,
_ ( L " %ls: Ambiguous job \n " ) ,
argv [ 0 ] ) ;
}
else
{
append_format ( stderr_buffer ,
_ ( L " %ls: '%ls' is not a job \n " ) ,
argv [ 0 ] ,
argv [ 1 ] ) ;
}
builtin_print_help ( parser , argv [ 0 ] , stderr_buffer ) ;
j = 0 ;
}
else
{
wchar_t * end ;
int pid ;
errno = 0 ;
pid = abs ( fish_wcstoi ( argv [ 1 ] , & end , 10 ) ) ;
if ( * end | | errno )
{
append_format ( stderr_buffer ,
BUILTIN_ERR_NOT_NUMBER ,
argv [ 0 ] ,
argv [ 1 ] ) ;
builtin_print_help ( parser , argv [ 0 ] , stderr_buffer ) ;
}
else
{
j = job_get_from_pid ( pid ) ;
if ( ! j | | ! job_get_flag ( j , JOB_CONSTRUCTED ) | | job_is_completed ( j ) )
{
append_format ( stderr_buffer ,
_ ( L " %ls: No suitable job: %d \n " ) ,
argv [ 0 ] ,
pid ) ;
builtin_print_help ( parser , argv [ 0 ] , stderr_buffer ) ;
j = 0 ;
}
else if ( ! job_get_flag ( j , JOB_CONTROL ) )
{
append_format ( stderr_buffer ,
_ ( L " %ls: Can't put job %d, '%ls' to foreground because it is not under job control \n " ) ,
argv [ 0 ] ,
pid ,
j - > command_wcstr ( ) ) ;
builtin_print_help ( parser , argv [ 0 ] , stderr_buffer ) ;
j = 0 ;
}
}
}
if ( j )
{
if ( builtin_err_redirect )
{
append_format ( stderr_buffer ,
FG_MSG ,
j - > job_id ,
j - > command_wcstr ( ) ) ;
}
else
{
/*
If we aren ' t redirecting , send output to real stderr ,
since stuff in sb_err won ' t get printed until the
command finishes .
*/
fwprintf ( stderr ,
FG_MSG ,
j - > job_id ,
j - > command_wcstr ( ) ) ;
}
2012-11-22 06:23:48 +00:00
const wcstring ft = tok_first ( j - > command_wcstr ( ) ) ;
if ( ! ft . empty ( ) )
env_set ( L " _ " , ft . c_str ( ) , ENV_EXPORT ) ;
2014-08-03 04:01:40 +00:00
reader_write_title ( j - > command ( ) ) ;
2012-11-19 00:30:30 +00:00
make_first ( j ) ;
job_set_flag ( j , JOB_FOREGROUND , 1 ) ;
2005-09-20 13:26:39 +00:00
2012-11-19 00:30:30 +00:00
job_continue ( j , job_is_stopped ( j ) ) ;
}
return j ! = 0 ;
2005-09-20 13:26:39 +00:00
}
/**
Helper function for builtin_bg ( )
*/
2012-11-19 00:30:30 +00:00
static int send_to_bg ( parser_t & parser , job_t * j , const wchar_t * name )
2005-09-20 13:26:39 +00:00
{
2012-11-19 00:30:30 +00:00
if ( j = = 0 )
{
append_format ( stderr_buffer ,
_ ( L " %ls: Unknown job '%ls' \n " ) ,
L " bg " ,
name ) ;
builtin_print_help ( parser , L " bg " , stderr_buffer ) ;
return STATUS_BUILTIN_ERROR ;
}
else if ( ! job_get_flag ( j , JOB_CONTROL ) )
{
append_format ( stderr_buffer ,
_ ( L " %ls: Can't put job %d, '%ls' to background because it is not under job control \n " ) ,
L " bg " ,
j - > job_id ,
j - > command_wcstr ( ) ) ;
builtin_print_help ( parser , L " bg " , stderr_buffer ) ;
return STATUS_BUILTIN_ERROR ;
}
else
{
append_format ( stderr_buffer ,
_ ( L " Send job %d '%ls' to background \n " ) ,
j - > job_id ,
j - > command_wcstr ( ) ) ;
}
make_first ( j ) ;
job_set_flag ( j , JOB_FOREGROUND , 0 ) ;
job_continue ( j , job_is_stopped ( j ) ) ;
return STATUS_BUILTIN_OK ;
2005-09-20 13:26:39 +00:00
}
/**
Builtin for putting a job in the background
*/
2012-11-19 00:30:30 +00:00
static int builtin_bg ( parser_t & parser , wchar_t * * argv )
2005-09-20 13:26:39 +00:00
{
2012-11-19 00:30:30 +00:00
int res = STATUS_BUILTIN_OK ;
2006-01-30 19:53:10 +00:00
2012-11-19 00:30:30 +00:00
if ( argv [ 1 ] = = 0 )
{
job_t * j ;
2012-01-30 00:36:21 +00:00
job_iterator_t jobs ;
while ( ( j = jobs . next ( ) ) )
{
2012-11-19 00:30:30 +00:00
if ( job_is_stopped ( j ) & & job_get_flag ( j , JOB_CONTROL ) & & ( ! job_is_completed ( j ) ) )
{
break ;
}
}
if ( ! j )
{
append_format ( stderr_buffer ,
_ ( L " %ls: There are no suitable jobs \n " ) ,
argv [ 0 ] ) ;
res = 1 ;
}
else
{
res = send_to_bg ( parser , j , _ ( L " (default) " ) ) ;
}
}
else
{
wchar_t * end ;
int i ;
int pid ;
int err = 0 ;
for ( i = 1 ; argv [ i ] ; i + + )
{
errno = 0 ;
pid = fish_wcstoi ( argv [ i ] , & end , 10 ) ;
if ( errno | | pid < 0 | | * end | | ! job_get_from_pid ( pid ) )
{
append_format ( stderr_buffer ,
_ ( L " %ls: '%ls' is not a job \n " ) ,
argv [ 0 ] ,
argv [ i ] ) ;
err = 1 ;
break ;
}
}
if ( ! err )
{
for ( i = 1 ; ! res & & argv [ i ] ; i + + )
{
pid = fish_wcstoi ( argv [ i ] , 0 , 10 ) ;
res | = send_to_bg ( parser , job_get_from_pid ( pid ) , * argv ) ;
}
}
}
return res ;
2005-09-20 13:26:39 +00:00
}
/**
This function handles both the ' continue ' and the ' break ' builtins
that are used for loop control .
*/
2012-11-19 00:30:30 +00:00
static int builtin_break_continue ( parser_t & parser , wchar_t * * argv )
2005-09-20 13:26:39 +00:00
{
2012-11-19 00:30:30 +00:00
int is_break = ( wcscmp ( argv [ 0 ] , L " break " ) = = 0 ) ;
int argc = builtin_count_args ( argv ) ;
if ( argc ! = 1 )
{
append_format ( stderr_buffer ,
BUILTIN_ERR_UNKNOWN ,
argv [ 0 ] ,
argv [ 1 ] ) ;
builtin_print_help ( parser , argv [ 0 ] , stderr_buffer ) ;
return STATUS_BUILTIN_ERROR ;
}
2013-12-21 01:41:21 +00:00
/* Find the index of the enclosing for or while loop. Recall that incrementing loop_idx goes 'up' to outer blocks */
size_t loop_idx ;
for ( loop_idx = 0 ; loop_idx < parser . block_count ( ) ; loop_idx + + )
2012-11-19 00:30:30 +00:00
{
2013-12-21 01:41:21 +00:00
const block_t * b = parser . block_at_index ( loop_idx ) ;
if ( b - > type ( ) = = WHILE | | b - > type ( ) = = FOR )
break ;
2012-11-19 00:30:30 +00:00
}
2013-12-21 01:41:21 +00:00
if ( loop_idx > = parser . block_count ( ) )
2012-11-19 00:30:30 +00:00
{
append_format ( stderr_buffer ,
_ ( L " %ls: Not inside of loop \n " ) ,
argv [ 0 ] ) ;
builtin_print_help ( parser , argv [ 0 ] , stderr_buffer ) ;
return STATUS_BUILTIN_ERROR ;
}
2013-12-21 01:41:21 +00:00
/* Skip blocks interior to the loop */
size_t block_idx = loop_idx ;
while ( block_idx - - )
2012-11-19 00:30:30 +00:00
{
2013-12-21 01:41:21 +00:00
parser . block_at_index ( block_idx ) - > skip = true ;
2012-11-19 00:30:30 +00:00
}
2014-01-15 09:40:40 +00:00
2013-12-21 01:41:21 +00:00
/* Skip the loop itself */
block_t * loop_block = parser . block_at_index ( loop_idx ) ;
loop_block - > skip = true ;
loop_block - > loop_status = is_break ? LOOP_BREAK : LOOP_CONTINUE ;
2012-11-19 00:30:30 +00:00
return STATUS_BUILTIN_OK ;
2005-09-20 13:26:39 +00:00
}
2008-01-13 16:47:47 +00:00
/**
2012-08-27 05:42:29 +00:00
Implementation of the builtin breakpoint command , used to launch the
2008-01-13 16:47:47 +00:00
interactive debugger .
*/
2012-11-19 00:30:30 +00:00
static int builtin_breakpoint ( parser_t & parser , wchar_t * * argv )
2006-11-11 10:54:00 +00:00
{
2012-11-19 00:30:30 +00:00
parser . push_block ( new breakpoint_block_t ( ) ) ;
reader_read ( STDIN_FILENO , real_io ? * real_io : io_chain_t ( ) ) ;
parser . pop_block ( ) ;
return proc_get_last_status ( ) ;
2006-11-11 10:54:00 +00:00
}
2005-09-20 13:26:39 +00:00
/**
Function for handling the \ c return builtin
*/
2012-11-19 00:30:30 +00:00
static int builtin_return ( parser_t & parser , wchar_t * * argv )
2005-09-20 13:26:39 +00:00
{
2012-11-19 00:30:30 +00:00
int argc = builtin_count_args ( argv ) ;
int status = proc_get_last_status ( ) ;
switch ( argc )
{
2012-11-19 08:31:03 +00:00
case 1 :
break ;
case 2 :
2012-11-19 00:30:30 +00:00
{
2012-11-19 08:31:03 +00:00
wchar_t * end ;
errno = 0 ;
status = fish_wcstoi ( argv [ 1 ] , & end , 10 ) ;
if ( errno | | * end ! = 0 )
{
append_format ( stderr_buffer ,
_ ( L " %ls: Argument '%ls' must be an integer \n " ) ,
argv [ 0 ] ,
argv [ 1 ] ) ;
builtin_print_help ( parser , argv [ 0 ] , stderr_buffer ) ;
return STATUS_BUILTIN_ERROR ;
}
break ;
}
default :
2012-11-19 00:30:30 +00:00
append_format ( stderr_buffer ,
2012-11-19 08:31:03 +00:00
_ ( L " %ls: Too many arguments \n " ) ,
argv [ 0 ] ) ;
2012-11-19 00:30:30 +00:00
builtin_print_help ( parser , argv [ 0 ] , stderr_buffer ) ;
return STATUS_BUILTIN_ERROR ;
}
2013-12-21 01:41:21 +00:00
/* Find the function block */
size_t function_block_idx ;
for ( function_block_idx = 0 ; function_block_idx < parser . block_count ( ) ; function_block_idx + + )
2012-11-19 00:30:30 +00:00
{
2013-12-21 01:41:21 +00:00
const block_t * b = parser . block_at_index ( function_block_idx ) ;
if ( b - > type ( ) = = FUNCTION_CALL | | b - > type ( ) = = FUNCTION_CALL_NO_SHADOW )
break ;
2012-11-19 00:30:30 +00:00
}
2013-12-21 01:41:21 +00:00
if ( function_block_idx > = parser . block_count ( ) )
2012-11-19 00:30:30 +00:00
{
append_format ( stderr_buffer ,
_ ( L " %ls: Not inside of function \n " ) ,
argv [ 0 ] ) ;
builtin_print_help ( parser , argv [ 0 ] , stderr_buffer ) ;
return STATUS_BUILTIN_ERROR ;
}
2014-01-15 09:40:40 +00:00
2013-12-21 01:41:21 +00:00
/* Skip everything up to (and then including) the function block */
for ( size_t i = 0 ; i < function_block_idx ; i + + )
2012-11-19 00:30:30 +00:00
{
2013-12-21 01:41:21 +00:00
block_t * b = parser . block_at_index ( i ) ;
b - > skip = true ;
2012-11-19 00:30:30 +00:00
}
2013-12-21 01:41:21 +00:00
parser . block_at_index ( function_block_idx ) - > skip = true ;
2012-11-19 00:30:30 +00:00
return status ;
2005-09-20 13:26:39 +00:00
}
2012-06-05 04:24:42 +00:00
/**
History of commands executed by user
*/
2012-11-19 00:30:30 +00:00
static int builtin_history ( parser_t & parser , wchar_t * * argv )
2012-06-05 04:24:42 +00:00
{
int argc = builtin_count_args ( argv ) ;
2012-11-19 00:30:30 +00:00
bool search_history = false ;
2012-06-05 04:24:42 +00:00
bool delete_item = false ;
bool search_prefix = false ;
bool save_history = false ;
bool clear_history = false ;
2014-07-25 17:08:21 +00:00
bool merge_history = false ;
2012-06-05 04:24:42 +00:00
static const struct woption long_options [ ] =
2012-11-19 00:30:30 +00:00
{
{ L " prefix " , no_argument , 0 , ' p ' } ,
{ L " delete " , no_argument , 0 , ' d ' } ,
{ L " search " , no_argument , 0 , ' s ' } ,
{ L " contains " , no_argument , 0 , ' c ' } ,
{ L " save " , no_argument , 0 , ' v ' } ,
{ L " clear " , no_argument , 0 , ' l ' } ,
2014-07-25 17:08:21 +00:00
{ L " merge " , no_argument , 0 , ' m ' } ,
2012-11-19 00:30:30 +00:00
{ L " help " , no_argument , 0 , ' h ' } ,
{ 0 , 0 , 0 , 0 }
} ;
2012-06-05 04:24:42 +00:00
int opt = 0 ;
int opt_index = 0 ;
woptind = 0 ;
history_t * history = reader_get_history ( ) ;
2012-11-19 00:30:30 +00:00
2012-07-27 07:31:00 +00:00
/* Use the default history if we have none (which happens if invoked non-interactively, e.g. from webconfig.py */
if ( ! history )
history = & history_t : : history_with_name ( L " fish " ) ;
2012-11-19 00:30:30 +00:00
2014-09-24 14:18:29 +00:00
while ( ( opt = wgetopt_long_only ( argc , argv , L " pdscvl " , long_options , & opt_index ) ) ! = EOF )
2012-06-05 04:24:42 +00:00
{
2012-11-19 00:30:30 +00:00
switch ( opt )
2012-06-05 04:24:42 +00:00
{
2012-11-19 08:31:03 +00:00
case ' p ' :
search_prefix = true ;
break ;
case ' d ' :
delete_item = true ;
break ;
case ' s ' :
search_history = true ;
break ;
case ' c ' :
break ;
case ' v ' :
save_history = true ;
break ;
case ' l ' :
clear_history = true ;
break ;
2014-07-25 17:08:21 +00:00
case ' m ' :
merge_history = true ;
break ;
2012-11-19 08:31:03 +00:00
case ' h ' :
builtin_print_help ( parser , argv [ 0 ] , stdout_buffer ) ;
return STATUS_BUILTIN_OK ;
break ;
case ' ? ' :
append_format ( stderr_buffer , BUILTIN_ERR_UNKNOWN , argv [ 0 ] , argv [ woptind - 1 ] ) ;
return STATUS_BUILTIN_ERROR ;
break ;
default :
append_format ( stderr_buffer , BUILTIN_ERR_UNKNOWN , argv [ 0 ] , argv [ woptind - 1 ] ) ;
return STATUS_BUILTIN_ERROR ;
2012-06-05 04:24:42 +00:00
}
2012-11-19 00:30:30 +00:00
}
2012-06-05 04:24:42 +00:00
2012-07-27 07:31:00 +00:00
/* Everything after is an argument */
const wcstring_list_t args ( argv + woptind , argv + argc ) ;
2012-06-05 04:24:42 +00:00
if ( argc = = 1 )
{
wcstring full_history ;
2014-07-29 21:41:21 +00:00
history - > get_string_representation ( & full_history , wcstring ( L " \n " ) ) ;
2012-06-05 04:24:42 +00:00
stdout_buffer . append ( full_history ) ;
2012-06-05 06:59:04 +00:00
stdout_buffer . push_back ( ' \n ' ) ;
2012-06-05 04:24:42 +00:00
return STATUS_BUILTIN_OK ;
}
2014-07-25 17:08:21 +00:00
if ( merge_history )
{
history - > incorporate_external_changes ( ) ;
}
2012-06-05 04:24:42 +00:00
if ( search_history )
{
int res = STATUS_BUILTIN_ERROR ;
2012-07-27 07:31:00 +00:00
for ( wcstring_list_t : : const_iterator iter = args . begin ( ) ; iter ! = args . end ( ) ; + + iter )
2012-06-05 04:24:42 +00:00
{
2012-07-27 07:31:00 +00:00
const wcstring & search_string = * iter ;
if ( search_string . empty ( ) )
{
append_format ( stderr_buffer , BUILTIN_ERR_COMBO2 , argv [ 0 ] , L " Use --search with either --contains or --prefix " ) ;
return res ;
}
history_search_t searcher = history_search_t ( * history , search_string , search_prefix ? HISTORY_SEARCH_TYPE_PREFIX : HISTORY_SEARCH_TYPE_CONTAINS ) ;
while ( searcher . go_backwards ( ) )
{
stdout_buffer . append ( searcher . current_string ( ) ) ;
2012-11-19 00:30:30 +00:00
stdout_buffer . append ( L " \n " ) ;
2012-07-27 07:31:00 +00:00
res = STATUS_BUILTIN_OK ;
}
2012-06-05 04:24:42 +00:00
}
return res ;
}
if ( delete_item )
{
2012-07-27 07:31:00 +00:00
for ( wcstring_list_t : : const_iterator iter = args . begin ( ) ; iter ! = args . end ( ) ; + + iter )
{
wcstring delete_string = * iter ;
if ( delete_string [ 0 ] = = ' " ' & & delete_string [ delete_string . length ( ) - 1 ] = = ' " ' )
delete_string = delete_string . substr ( 1 , delete_string . length ( ) - 2 ) ;
2012-11-19 00:30:30 +00:00
2012-07-27 07:31:00 +00:00
history - > remove ( delete_string ) ;
}
2012-06-05 04:24:42 +00:00
return STATUS_BUILTIN_OK ;
}
if ( save_history )
{
history - > save ( ) ;
return STATUS_BUILTIN_OK ;
}
if ( clear_history )
{
history - > clear ( ) ;
history - > save ( ) ;
return STATUS_BUILTIN_OK ;
}
return STATUS_BUILTIN_ERROR ;
}
2013-06-15 21:32:38 +00:00
int builtin_parse ( parser_t & parser , wchar_t * * argv )
{
2013-06-30 22:38:31 +00:00
struct sigaction act ;
sigemptyset ( & act . sa_mask ) ;
act . sa_flags = 0 ;
act . sa_handler = SIG_DFL ;
sigaction ( SIGINT , & act , 0 ) ;
2013-06-15 21:32:38 +00:00
std : : vector < char > txt ;
for ( ; ; )
{
char buff [ 256 ] ;
ssize_t amt = read_loop ( builtin_stdin , buff , sizeof buff ) ;
if ( amt < = 0 ) break ;
txt . insert ( txt . end ( ) , buff , buff + amt ) ;
}
if ( ! txt . empty ( ) )
{
const wcstring src = str2wcstring ( & txt . at ( 0 ) , txt . size ( ) ) ;
parse_node_tree_t parse_tree ;
2013-06-15 22:21:35 +00:00
parse_error_list_t errors ;
2014-01-14 08:01:26 +00:00
bool success = parse_tree_from_string ( src , parse_flag_none , & parse_tree , & errors ) ;
2013-06-15 22:21:35 +00:00
if ( ! success )
{
stdout_buffer . append ( L " Parsing failed: \n " ) ;
for ( size_t i = 0 ; i < errors . size ( ) ; i + + )
{
stdout_buffer . append ( errors . at ( i ) . describe ( src ) ) ;
stdout_buffer . push_back ( L ' \n ' ) ;
}
2014-01-15 09:40:40 +00:00
2013-12-08 22:13:23 +00:00
stdout_buffer . append ( L " (Reparsed with continue after error) \n " ) ;
parse_tree . clear ( ) ;
errors . clear ( ) ;
2014-01-14 08:01:26 +00:00
parse_tree_from_string ( src , parse_flag_continue_after_error , & parse_tree , & errors ) ;
2013-12-08 22:13:23 +00:00
}
const wcstring dump = parse_dump_tree ( parse_tree , src ) ;
2013-12-13 02:17:30 +00:00
stdout_buffer . append ( dump ) ;
2013-06-15 21:32:38 +00:00
}
return STATUS_BUILTIN_OK ;
}
2012-06-05 04:24:42 +00:00
2005-09-20 13:26:39 +00:00
/*
END OF BUILTIN COMMANDS
2012-02-01 03:47:56 +00:00
Below are functions for handling the builtin commands .
THESE MUST BE SORTED BY NAME ! Completion lookup uses binary search .
2005-09-20 13:26:39 +00:00
*/
2005-12-15 13:59:02 +00:00
2006-06-17 13:07:08 +00:00
/**
2012-02-01 03:47:56 +00:00
Data about all the builtin commands in fish .
Functions that are bound to builtin_generic are handled directly by the parser .
2012-09-01 08:46:14 +00:00
NOTE : These must be kept in sorted order !
2006-06-17 13:07:08 +00:00
*/
2012-02-01 03:47:56 +00:00
static const builtin_data_t builtin_datas [ ] =
2005-09-20 13:26:39 +00:00
{
2013-01-05 09:30:03 +00:00
{ L " [ " , & builtin_test , N_ ( L " Test a condition " ) } ,
2014-01-08 23:13:08 +00:00
{ L " __fish_parse " , & builtin_parse , N_ ( L " Try out the new parser " ) } ,
2012-11-19 00:30:30 +00:00
{ L " and " , & builtin_generic , N_ ( L " Execute command if previous command suceeded " ) } ,
2014-03-02 21:11:17 +00:00
{ L " begin " , & builtin_generic , N_ ( L " Create a block of code " ) } ,
2012-11-19 00:30:30 +00:00
{ L " bg " , & builtin_bg , N_ ( L " Send job to background " ) } ,
{ L " bind " , & builtin_bind , N_ ( L " Handle fish key bindings " ) } ,
{ L " block " , & builtin_block , N_ ( L " Temporarily block delivery of events " ) } ,
{ L " break " , & builtin_break_continue , N_ ( L " Stop the innermost loop " ) } ,
{ L " breakpoint " , & builtin_breakpoint , N_ ( L " Temporarily halt execution of a script and launch an interactive debug prompt " ) } ,
{ L " builtin " , & builtin_builtin , N_ ( L " Run a builtin command instead of a function " ) } ,
2014-03-02 21:11:17 +00:00
{ L " case " , & builtin_generic , N_ ( L " Conditionally execute a block of commands " ) } ,
2012-11-19 00:30:30 +00:00
{ L " cd " , & builtin_cd , N_ ( L " Change working directory " ) } ,
2014-07-10 01:21:06 +00:00
{ L " command " , & builtin_command , N_ ( L " Run a program instead of a function or builtin " ) } ,
2012-11-19 00:30:30 +00:00
{ L " commandline " , & builtin_commandline , N_ ( L " Set or get the commandline " ) } ,
{ L " complete " , & builtin_complete , N_ ( L " Edit command specific completions " ) } ,
{ L " contains " , & builtin_contains , N_ ( L " Search for a specified string in a list " ) } ,
{ L " continue " , & builtin_break_continue , N_ ( L " Skip the rest of the current lap of the innermost loop " ) } ,
{ L " count " , & builtin_count , N_ ( L " Count the number of arguments " ) } ,
{ L " echo " , & builtin_echo , N_ ( L " Print arguments " ) } ,
2014-03-02 21:11:17 +00:00
{ L " else " , & builtin_generic , N_ ( L " Evaluate block if condition is false " ) } ,
2012-11-19 00:30:30 +00:00
{ L " emit " , & builtin_emit , N_ ( L " Emit an event " ) } ,
2014-03-02 21:11:17 +00:00
{ L " end " , & builtin_generic , N_ ( L " End a block of commands " ) } ,
2012-11-19 00:30:30 +00:00
{ L " exec " , & builtin_generic , N_ ( L " Run command in current process " ) } ,
{ L " exit " , & builtin_exit , N_ ( L " Exit the shell " ) } ,
{ L " fg " , & builtin_fg , N_ ( L " Send job to foreground " ) } ,
2014-03-02 21:46:30 +00:00
{ L " for " , & builtin_generic , N_ ( L " Perform a set of commands multiple times " ) } ,
2014-03-02 21:11:17 +00:00
{ L " function " , & builtin_generic , N_ ( L " Define a new function " ) } ,
2012-11-19 00:30:30 +00:00
{ L " functions " , & builtin_functions , N_ ( L " List or remove functions " ) } ,
{ L " history " , & builtin_history , N_ ( L " History of commands executed by user " ) } ,
{ L " if " , & builtin_generic , N_ ( L " Evaluate block if condition is true " ) } ,
{ L " jobs " , & builtin_jobs , N_ ( L " Print currently running jobs " ) } ,
{ L " not " , & builtin_generic , N_ ( L " Negate exit status of job " ) } ,
{ L " or " , & builtin_generic , N_ ( L " Execute command if previous command failed " ) } ,
2013-01-22 17:07:28 +00:00
{ L " printf " , & builtin_printf , N_ ( L " Prints formatted text " ) } ,
2012-11-19 00:30:30 +00:00
{ L " pwd " , & builtin_pwd , N_ ( L " Print the working directory " ) } ,
{ L " random " , & builtin_random , N_ ( L " Generate random number " ) } ,
{ L " read " , & builtin_read , N_ ( L " Read a line of input into variables " ) } ,
{ L " return " , & builtin_return , N_ ( L " Stop the currently evaluated function " ) } ,
{ L " set " , & builtin_set , N_ ( L " Handle environment variables " ) } ,
2013-02-14 23:50:24 +00:00
{ L " set_color " , & builtin_set_color , N_ ( L " Set the terminal color " ) } ,
2013-08-14 15:43:09 +00:00
{ L " source " , & builtin_source , N_ ( L " Evaluate contents of file " ) } ,
2012-11-19 00:30:30 +00:00
{ L " status " , & builtin_status , N_ ( L " Return status information about fish " ) } ,
2014-03-02 21:11:17 +00:00
{ L " switch " , & builtin_generic , N_ ( L " Conditionally execute a block of commands " ) } ,
2012-11-19 00:30:30 +00:00
{ L " test " , & builtin_test , N_ ( L " Test a condition " ) } ,
{ L " ulimit " , & builtin_ulimit , N_ ( L " Set or get the shells resource usage limits " ) } ,
{ L " while " , & builtin_generic , N_ ( L " Perform a command multiple times " ) }
2012-02-01 03:47:56 +00:00
} ;
# define BUILTIN_COUNT (sizeof builtin_datas / sizeof *builtin_datas)
2012-11-19 00:30:30 +00:00
static const builtin_data_t * builtin_lookup ( const wcstring & name )
{
2012-02-01 03:47:56 +00:00
const builtin_data_t * array_end = builtin_datas + BUILTIN_COUNT ;
const builtin_data_t * found = std : : lower_bound ( builtin_datas , array_end , name ) ;
2012-11-19 00:30:30 +00:00
if ( found ! = array_end & & name = = found - > name )
{
2012-02-01 03:47:56 +00:00
return found ;
2012-11-19 00:30:30 +00:00
}
else
{
2012-02-01 03:47:56 +00:00
return NULL ;
}
2006-02-05 13:08:40 +00:00
}
2006-01-30 19:53:10 +00:00
2006-02-05 13:08:40 +00:00
void builtin_init ( )
{
2012-11-19 00:30:30 +00:00
wopterr = 0 ;
for ( size_t i = 0 ; i < BUILTIN_COUNT ; i + + )
{
intern_static ( builtin_datas [ i ] . name ) ;
}
2005-09-20 13:26:39 +00:00
}
void builtin_destroy ( )
{
}
2012-11-19 00:30:30 +00:00
int builtin_exists ( const wcstring & cmd )
2005-09-20 13:26:39 +00:00
{
2012-11-19 00:30:30 +00:00
return ! ! builtin_lookup ( cmd ) ;
2005-09-20 13:26:39 +00:00
}
/**
Return true if the specified builtin should handle it ' s own help ,
false otherwise .
*/
2012-11-19 00:30:30 +00:00
static int internal_help ( const wchar_t * cmd )
2005-09-20 13:26:39 +00:00
{
2012-11-19 00:30:30 +00:00
CHECK ( cmd , 0 ) ;
return contains ( cmd , L " for " , L " while " , L " function " ,
2014-09-22 06:48:47 +00:00
L " if " , L " end " , L " switch " , L " case " , L " count " , L " printf " ) ;
2005-09-20 13:26:39 +00:00
}
2012-11-19 00:30:30 +00:00
int builtin_run ( parser_t & parser , const wchar_t * const * argv , const io_chain_t & io )
2005-09-20 13:26:39 +00:00
{
2012-11-19 00:30:30 +00:00
int ( * cmd ) ( parser_t & parser , const wchar_t * const * argv ) = 0 ;
real_io = & io ;
CHECK ( argv , STATUS_BUILTIN_ERROR ) ;
CHECK ( argv [ 0 ] , STATUS_BUILTIN_ERROR ) ;
2012-02-01 03:47:56 +00:00
const builtin_data_t * data = builtin_lookup ( argv [ 0 ] ) ;
2012-11-19 00:30:30 +00:00
cmd = ( int ( * ) ( parser_t & parser , const wchar_t * const * ) ) ( data ? data - > func : NULL ) ;
if ( argv [ 1 ] ! = 0 & & ! internal_help ( argv [ 0 ] ) )
{
2014-03-18 21:42:38 +00:00
if ( argv [ 2 ] = = 0 & & ( parse_util_argument_is_help ( argv [ 1 ] , 0 ) ) )
2012-11-19 00:30:30 +00:00
{
builtin_print_help ( parser , argv [ 0 ] , stdout_buffer ) ;
return STATUS_BUILTIN_OK ;
}
}
if ( data ! = NULL )
{
int status ;
status = cmd ( parser , argv ) ;
return status ;
}
else
{
2014-03-27 01:20:38 +00:00
debug ( 0 , UNKNOWN_BUILTIN_ERR_MSG , argv [ 0 ] ) ;
2012-11-19 00:30:30 +00:00
}
return STATUS_BUILTIN_ERROR ;
2005-09-20 13:26:39 +00:00
}
2012-02-01 03:47:56 +00:00
wcstring_list_t builtin_get_names ( void )
2005-09-20 13:26:39 +00:00
{
2012-02-01 03:47:56 +00:00
wcstring_list_t result ;
result . reserve ( BUILTIN_COUNT ) ;
2012-11-19 00:30:30 +00:00
for ( size_t i = 0 ; i < BUILTIN_COUNT ; i + + )
{
2012-02-01 03:47:56 +00:00
result . push_back ( builtin_datas [ i ] . name ) ;
}
return result ;
2005-09-20 13:26:39 +00:00
}
2012-11-19 00:30:30 +00:00
void builtin_get_names ( std : : vector < completion_t > & list )
{
for ( size_t i = 0 ; i < BUILTIN_COUNT ; i + + )
{
2014-01-07 22:57:58 +00:00
append_completion ( list , builtin_datas [ i ] . name ) ;
2012-11-19 00:30:30 +00:00
}
2012-01-16 16:56:47 +00:00
}
2012-11-19 00:30:30 +00:00
wcstring builtin_get_desc ( const wcstring & name )
2005-09-20 13:26:39 +00:00
{
2012-05-18 02:37:46 +00:00
wcstring result ;
2012-11-19 00:30:30 +00:00
const builtin_data_t * builtin = builtin_lookup ( name ) ;
if ( builtin )
{
2012-05-18 02:37:46 +00:00
result = _ ( builtin - > desc ) ;
}
return result ;
2005-09-20 13:26:39 +00:00
}
2012-11-19 00:30:30 +00:00
void builtin_push_io ( parser_t & parser , int in )
2005-09-20 13:26:39 +00:00
{
2012-02-08 07:53:34 +00:00
ASSERT_IS_MAIN_THREAD ( ) ;
2012-11-19 00:30:30 +00:00
if ( builtin_stdin ! = - 1 )
{
2012-02-22 18:51:06 +00:00
struct io_stack_elem_t elem = { builtin_stdin , stdout_buffer , stderr_buffer } ;
2012-02-08 07:53:34 +00:00
io_stack . push ( elem ) ;
2012-11-19 00:30:30 +00:00
}
builtin_stdin = in ;
2012-02-22 18:51:06 +00:00
stdout_buffer . clear ( ) ;
stderr_buffer . clear ( ) ;
2005-09-20 13:26:39 +00:00
}
2012-01-23 05:40:08 +00:00
void builtin_pop_io ( parser_t & parser )
2005-09-20 13:26:39 +00:00
{
2012-02-08 07:53:34 +00:00
ASSERT_IS_MAIN_THREAD ( ) ;
2012-11-19 00:30:30 +00:00
builtin_stdin = 0 ;
if ( ! io_stack . empty ( ) )
{
2012-02-08 07:53:34 +00:00
struct io_stack_elem_t & elem = io_stack . top ( ) ;
2012-11-19 00:30:30 +00:00
stderr_buffer = elem . err ;
stdout_buffer = elem . out ;
builtin_stdin = elem . in ;
2012-02-08 07:53:34 +00:00
io_stack . pop ( ) ;
2012-11-19 00:30:30 +00:00
}
else
{
2012-02-22 18:51:06 +00:00
stdout_buffer . clear ( ) ;
stderr_buffer . clear ( ) ;
2012-11-19 00:30:30 +00:00
builtin_stdin = 0 ;
}
2005-09-20 13:26:39 +00:00
}