2005-09-20 13:26:39 +00:00
/** \file parser.c
2006-11-18 21:24:59 +00:00
The fish parser . Contains functions for parsing and evaluating code .
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 <sys/types.h>
# include <sys/stat.h>
# include <unistd.h>
# include <errno.h>
# include <fcntl.h>
# include <termios.h>
# include <pwd.h>
# include <dirent.h>
# include <signal.h>
2012-02-28 15:50:09 +00:00
# include <algorithm>
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 "common.h"
# include "wutil.h"
# include "proc.h"
# include "parser.h"
2007-04-22 09:50:26 +00:00
# include "parser_keywords.h"
2005-09-20 13:26:39 +00:00
# include "tokenizer.h"
# include "exec.h"
# include "wildcard.h"
# include "function.h"
# include "builtin.h"
# include "env.h"
# include "expand.h"
# include "reader.h"
# include "sanity.h"
# include "env_universal.h"
2005-10-05 22:37:08 +00:00
# include "event.h"
2006-02-02 15:23:56 +00:00
# include "intern.h"
2006-02-05 13:10:35 +00:00
# include "parse_util.h"
2006-10-19 11:50:23 +00:00
# include "path.h"
2006-10-29 21:09:11 +00:00
# include "signal.h"
2012-01-16 16:56:47 +00:00
# include "complete.h"
2005-09-20 13:26:39 +00:00
/**
Maximum number of function calls , i . e . recursion depth .
*/
# define MAX_RECURSION_DEPTH 128
2006-12-14 00:01:52 +00:00
/**
Error message for unknown builtin
*/
# define UNKNOWN_BUILTIN_ERR_MSG _(L"Unknown builtin '%ls'")
2005-09-20 13:26:39 +00:00
/**
Error message for improper use of the exec builtin
*/
2006-01-04 12:51:02 +00:00
# define EXEC_ERR_MSG _(L"This command can not be used in a pipeline")
2005-09-20 13:26:39 +00:00
/**
Error message for tokenizer error . The tokenizer message is
appended to this message .
*/
2006-01-04 12:51:02 +00:00
# define TOK_ERR_MSG _( L"Tokenizer error: '%ls'")
2005-09-20 13:26:39 +00:00
/**
2006-02-13 21:36:59 +00:00
Error message for short circuit command error .
2005-09-20 13:26:39 +00:00
*/
2006-06-08 23:57:19 +00:00
# define COND_ERR_MSG _( L"An additional command is required" )
2005-09-20 13:26:39 +00:00
/**
2012-07-09 22:18:22 +00:00
Error message on a function that calls itself immediately
2005-09-20 13:26:39 +00:00
*/
2012-07-09 22:18:22 +00:00
# define INFINITE_RECURSION_ERR_MSG _( L"The function calls itself immediately, which would result in an infinite loop.")
/**
Error message on reaching maximum recursion depth
*/
# define OVERFLOW_RECURSION_ERR_MSG _( L"Maximum recursion depth reached. Accidental infinite loop?")
2005-09-20 13:26:39 +00:00
2005-10-08 02:00:08 +00:00
/**
Error message used when the end of a block can ' t be located
*/
2006-01-11 14:17:35 +00:00
# define BLOCK_END_ERR_MSG _( L"Could not locate end of block. The 'end' command is missing, misspelled or a ';' is missing.")
2005-10-08 00:23:49 +00:00
2005-09-20 13:26:39 +00:00
/**
Error message on reaching maximum number of block calls
*/
2006-01-04 12:51:02 +00:00
# define BLOCK_ERR_MSG _( L"Maximum number of nested blocks reached.")
2005-09-20 13:26:39 +00:00
/**
2005-12-14 03:39:39 +00:00
Error message when a non - string token is found when expecting a command name
2005-09-20 13:26:39 +00:00
*/
2006-01-11 14:17:35 +00:00
# define CMD_ERR_MSG _( L"Expected a command name, got token of type '%ls'")
2005-09-20 13:26:39 +00:00
2006-01-05 16:02:28 +00:00
/**
Error message when a non - string token is found when expecting a command name
*/
2006-04-22 10:06:30 +00:00
# define CMD_OR_ERR_MSG _( L"Expected a command name, got token of type '%ls'. Did you mean 'COMMAND; or COMMAND'? See the help section for the 'or' builtin command by typing 'help or'.")
2006-01-05 16:02:28 +00:00
/**
Error message when a non - string token is found when expecting a command name
*/
2006-04-22 10:06:30 +00:00
# define CMD_AND_ERR_MSG _( L"Expected a command name, got token of type '%ls'. Did you mean 'COMMAND; and COMMAND'? See the help section for the 'and' builtin command by typing 'help and'.")
2006-01-05 16:02:28 +00:00
2005-09-20 13:26:39 +00:00
/**
2005-12-14 03:39:39 +00:00
Error message when encountering an illegal command name
2005-09-20 13:26:39 +00:00
*/
2006-01-11 14:17:35 +00:00
# define ILLEGAL_CMD_ERR_MSG _( L"Illegal command name '%ls'")
2005-09-20 13:26:39 +00:00
2007-01-09 13:41:17 +00:00
/**
Error message when encountering an illegal file descriptor
*/
# define ILLEGAL_FD_ERR_MSG _( L"Illegal file descriptor '%ls'")
2005-12-03 16:43:56 +00:00
/**
Error message for wildcards with no matches
*/
2012-03-10 21:44:35 +00:00
# define WILDCARD_ERR_MSG _( L"No matches for wildcard '%ls'.")
2005-12-07 16:06:47 +00:00
2005-12-13 18:28:03 +00:00
/**
Error when using case builtin outside of switch block
*/
2006-01-04 12:51:02 +00:00
# define INVALID_CASE_ERR_MSG _( L"'case' builtin not inside of switch block")
2005-12-13 18:28:03 +00:00
/**
Error when using loop control builtins ( break or continue ) outside of loop
*/
2006-01-04 12:51:02 +00:00
# define INVALID_LOOP_ERR_MSG _( L"Loop control command while not inside of loop" )
2005-12-13 18:28:03 +00:00
2006-09-18 00:15:18 +00:00
/**
Error when using return builtin outside of function definition
*/
2007-01-09 02:53:56 +00:00
# define INVALID_RETURN_ERR_MSG _( L"'return' builtin command outside of function definition" )
2006-09-18 00:15:18 +00:00
2005-12-13 18:28:03 +00:00
/**
Error when using else builtin outside of if block
*/
2012-09-03 20:24:01 +00:00
# define INVALID_ELSE_ERR_MSG _( L"'%ls' builtin not inside of if block" )
/**
Error when using ' else if ' past a naked ' else '
*/
# define INVALID_ELSEIF_PAST_ELSE_ERR_MSG _( L"'%ls' used past terminating 'else'" )
2005-12-13 18:28:03 +00:00
2005-12-13 20:11:21 +00:00
/**
Error when using end builtin outside of block
*/
2006-01-04 12:51:02 +00:00
# define INVALID_END_ERR_MSG _( L"'end' command outside of block")
2005-12-13 20:11:21 +00:00
2005-12-07 16:06:47 +00:00
/**
Error message for Posix - style assignment
*/
2006-07-19 23:20:20 +00:00
# define COMMAND_ASSIGN_ERR_MSG _( L"Unknown command '%ls'. Did you mean 'set %ls %ls'? For information on assigning values to variables, see the help section on the set command by typing 'help set'.")
2005-12-03 16:43:56 +00:00
2005-12-14 01:07:12 +00:00
/**
Error for invalid redirection token
*/
2006-01-04 12:51:02 +00:00
# define REDIRECT_TOKEN_ERR_MSG _( L"Expected redirection specification, got token of type '%ls'")
2005-12-14 03:39:39 +00:00
/**
Error when encountering redirection without a command
*/
2006-01-04 12:51:02 +00:00
# define INVALID_REDIRECTION_ERR_MSG _( L"Encountered redirection when expecting a command name. Fish does not allow a redirection operation before a command.")
/**
Error for evaluating null pointer
*/
# define EVAL_NULL_ERR_MSG _( L"Tried to evaluate null pointer." )
/**
Error for evaluating in illegal scope
*/
2006-01-11 14:17:35 +00:00
# define INVALID_SCOPE_ERR_MSG _( L"Tried to evaluate commands using invalid block type '%ls'" )
2006-01-04 12:51:02 +00:00
2005-12-14 03:39:39 +00:00
2006-01-30 19:53:10 +00:00
/**
2006-04-20 18:35:02 +00:00
Error for wrong token type
2005-12-14 03:39:39 +00:00
*/
2006-01-04 12:51:02 +00:00
# define UNEXPECTED_TOKEN_ERR_MSG _( L"Unexpected token of type '%ls'")
2006-01-30 19:53:10 +00:00
/**
2006-04-20 18:35:02 +00:00
While block description
2012-11-18 10:23:22 +00:00
*/
2006-06-16 12:56:16 +00:00
# define WHILE_BLOCK N_( L"'while' block" )
2006-01-04 12:51:02 +00:00
2006-01-30 19:53:10 +00:00
/**
2006-04-20 18:35:02 +00:00
For block description
2006-01-04 12:51:02 +00:00
*/
2006-06-16 12:56:16 +00:00
# define FOR_BLOCK N_( L"'for' block" )
2006-01-04 12:51:02 +00:00
2012-11-18 10:23:22 +00:00
/**
Breakpoint block
2006-11-11 10:54:00 +00:00
*/
2012-11-18 10:23:22 +00:00
# define BREAKPOINT_BLOCK N_( L"Block created by breakpoint" )
2006-11-11 10:54:00 +00:00
2006-01-04 12:51:02 +00:00
2006-01-30 19:53:10 +00:00
/**
2006-04-20 18:35:02 +00:00
If block description
2006-01-04 12:51:02 +00:00
*/
2006-06-16 12:56:16 +00:00
# define IF_BLOCK N_( L"'if' conditional block" )
2006-01-04 12:51:02 +00:00
2006-01-30 19:53:10 +00:00
/**
2006-04-20 18:35:02 +00:00
Function definition block description
2006-01-04 12:51:02 +00:00
*/
2006-06-16 12:56:16 +00:00
# define FUNCTION_DEF_BLOCK N_( L"function definition block" )
2006-01-04 12:51:02 +00:00
2006-01-30 19:53:10 +00:00
/**
2006-04-20 18:35:02 +00:00
Function invocation block description
2006-01-04 12:51:02 +00:00
*/
2006-06-16 12:56:16 +00:00
# define FUNCTION_CALL_BLOCK N_( L"function invocation block" )
2006-01-04 12:51:02 +00:00
2007-04-22 22:10:33 +00:00
/**
Function invocation block description
*/
# define FUNCTION_CALL_NO_SHADOW_BLOCK N_( L"function invocation block with no variable shadowing" )
2006-01-04 12:51:02 +00:00
2006-01-30 19:53:10 +00:00
/**
2006-04-20 18:35:02 +00:00
Switch block description
2006-01-04 12:51:02 +00:00
*/
2006-06-16 12:56:16 +00:00
# define SWITCH_BLOCK N_( L"'switch' block" )
2006-01-04 12:51:02 +00:00
2006-01-30 19:53:10 +00:00
/**
2006-04-20 18:35:02 +00:00
Fake block description
2006-01-04 12:51:02 +00:00
*/
2006-06-16 12:56:16 +00:00
# define FAKE_BLOCK N_( L"unexecutable block" )
2006-01-04 12:51:02 +00:00
2006-01-30 19:53:10 +00:00
/**
2006-04-20 18:35:02 +00:00
Top block description
2006-01-04 12:51:02 +00:00
*/
2006-06-16 12:56:16 +00:00
# define TOP_BLOCK N_( L"global root block" )
2006-01-04 12:51:02 +00:00
2006-01-30 19:53:10 +00:00
/**
2006-04-20 18:35:02 +00:00
Command substitution block description
2006-01-04 12:51:02 +00:00
*/
2006-06-16 12:56:16 +00:00
# define SUBST_BLOCK N_( L"command substitution block" )
2006-01-04 12:51:02 +00:00
2006-01-30 19:53:10 +00:00
/**
2006-04-20 18:35:02 +00:00
Begin block description
2006-01-04 12:51:02 +00:00
*/
2006-06-16 12:56:16 +00:00
# define BEGIN_BLOCK N_( L"'begin' unconditional block" )
2006-01-04 12:51:02 +00:00
2006-01-30 19:53:10 +00:00
/**
2006-04-20 18:35:02 +00:00
Source block description
2006-01-28 11:34:40 +00:00
*/
2006-06-16 12:56:16 +00:00
# define SOURCE_BLOCK N_( L"Block created by the . builtin" )
2006-01-28 11:34:40 +00:00
2006-02-01 15:49:11 +00:00
/**
2006-04-20 18:35:02 +00:00
Source block description
2006-02-01 15:49:11 +00:00
*/
2006-06-16 12:56:16 +00:00
# define EVENT_BLOCK N_( L"event handler block" )
2006-02-01 15:49:11 +00:00
2006-01-28 11:34:40 +00:00
2006-01-30 19:53:10 +00:00
/**
2006-04-20 18:35:02 +00:00
Unknown block description
2006-01-04 12:51:02 +00:00
*/
2006-06-16 12:56:16 +00:00
# define UNKNOWN_BLOCK N_( L"unknown / invalid block" )
2006-01-04 12:51:02 +00:00
2005-12-14 01:07:12 +00:00
2006-06-17 13:07:08 +00:00
/**
Datastructure to describe a block type , like while blocks , command substitution blocks , etc .
*/
2006-06-16 12:56:16 +00:00
struct block_lookup_entry
{
2006-07-03 10:46:47 +00:00
2012-11-19 00:30:30 +00:00
/**
The block type id . The legal values are defined in parser . h .
*/
block_type_t type ;
/**
The name of the builtin that creates this type of block , if any .
*/
const wchar_t * name ;
/**
A description of this block type
*/
const wchar_t * desc ;
2006-06-16 12:56:16 +00:00
}
2012-11-19 00:30:30 +00:00
;
2006-06-16 12:56:16 +00:00
2006-06-17 13:07:08 +00:00
/**
List of all legal block types
*/
2010-10-08 00:43:57 +00:00
static const struct block_lookup_entry block_lookup [ ] =
2006-06-16 12:56:16 +00:00
{
2013-01-24 22:59:52 +00:00
{ WHILE , L " while " , WHILE_BLOCK } ,
{ FOR , L " for " , FOR_BLOCK } ,
{ IF , L " if " , IF_BLOCK } ,
{ FUNCTION_DEF , L " function " , FUNCTION_DEF_BLOCK } ,
{ FUNCTION_CALL , 0 , FUNCTION_CALL_BLOCK } ,
{ FUNCTION_CALL_NO_SHADOW , 0 , FUNCTION_CALL_NO_SHADOW_BLOCK } ,
{ SWITCH , L " switch " , SWITCH_BLOCK } ,
{ FAKE , 0 , FAKE_BLOCK } ,
{ TOP , 0 , TOP_BLOCK } ,
{ SUBST , 0 , SUBST_BLOCK } ,
{ BEGIN , L " begin " , BEGIN_BLOCK } ,
{ SOURCE , L " . " , SOURCE_BLOCK } ,
{ EVENT , 0 , EVENT_BLOCK } ,
{ BREAKPOINT , L " breakpoint " , BREAKPOINT_BLOCK } ,
{ ( block_type_t ) 0 , 0 , 0 }
2012-01-23 05:57:30 +00:00
} ;
2012-10-17 08:07:34 +00:00
static bool job_should_skip_elseif ( const job_t * job , const block_t * current_block ) ;
2012-01-23 05:57:30 +00:00
2012-05-06 20:36:51 +00:00
parser_t : : parser_t ( enum parser_type_t type , bool errors ) :
2012-01-23 05:57:30 +00:00
parser_type ( type ) ,
2012-05-06 20:36:51 +00:00
show_errors ( errors ) ,
2012-01-23 05:57:30 +00:00
error_code ( 0 ) ,
err_pos ( 0 ) ,
current_tokenizer ( NULL ) ,
current_tokenizer_pos ( 0 ) ,
job_start_pos ( 0 ) ,
eval_level ( - 1 ) ,
current_block ( NULL ) ,
2013-01-07 15:04:55 +00:00
block_io ( shared_ptr < io_data_t > ( ) )
2012-01-23 05:40:08 +00:00
{
2012-11-18 10:23:22 +00:00
2012-01-23 05:40:08 +00:00
}
2005-09-20 13:26:39 +00:00
2012-06-04 21:20:01 +00:00
/* A pointer to the principal parser (which is a static local) */
static parser_t * s_principal_parser = NULL ;
2012-01-23 05:40:08 +00:00
parser_t & parser_t : : principal_parser ( void )
{
2012-02-28 02:43:24 +00:00
ASSERT_IS_NOT_FORKED_CHILD ( ) ;
2012-01-23 05:40:08 +00:00
ASSERT_IS_MAIN_THREAD ( ) ;
2012-05-06 20:36:51 +00:00
static parser_t parser ( PARSER_TYPE_GENERAL , true ) ;
2012-06-04 21:20:01 +00:00
if ( ! s_principal_parser )
{
s_principal_parser = & parser ;
}
2012-01-23 05:40:08 +00:00
return parser ;
}
2005-09-20 13:26:39 +00:00
2012-06-04 21:20:01 +00:00
void parser_t : : skip_all_blocks ( void )
{
/* Tell all blocks to skip */
if ( s_principal_parser )
{
2012-06-04 22:10:35 +00:00
//write(2, "Cancelling blocks\n", strlen("Cancelling blocks\n"));
2012-06-04 21:20:01 +00:00
block_t * c = s_principal_parser - > current_block ;
2012-11-19 00:30:30 +00:00
while ( c )
2012-06-04 21:20:01 +00:00
{
c - > skip = true ;
2012-06-04 22:10:35 +00:00
//fprintf(stderr, " Cancelled %p\n", c);
2012-06-04 21:20:01 +00:00
c = c - > outer ;
}
}
}
2012-11-19 00:30:30 +00:00
void parser_t : : push_block ( block_t * newv )
2005-09-20 13:26:39 +00:00
{
2012-08-27 06:16:20 +00:00
const enum block_type_t type = newv - > type ( ) ;
2012-11-19 00:30:30 +00:00
newv - > src_lineno = parser_t : : get_lineno ( ) ;
newv - > src_filename = parser_t : : current_filename ( ) ? intern ( parser_t : : current_filename ( ) ) : 0 ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
newv - > outer = current_block ;
2012-08-27 05:42:29 +00:00
if ( current_block & & current_block - > skip )
2012-08-27 06:16:20 +00:00
newv - > mark_as_fake ( ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
/*
New blocks should be skipped if the outer block is skipped ,
except TOP ans SUBST block , which open up new environments . Fake
blocks should always be skipped . Rather complicated . . . : - (
*/
newv - > skip = current_block ? current_block - > skip : 0 ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
/*
Type TOP and SUBST are never skipped
*/
if ( type = = TOP | | type = = SUBST )
{
newv - > skip = 0 ;
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
/*
Fake blocks and function definition blocks are never executed
*/
if ( type = = FAKE | | type = = FUNCTION_DEF )
{
newv - > skip = 1 ;
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
newv - > job = 0 ;
newv - > loop_status = LOOP_NORMAL ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
current_block = newv ;
if ( ( newv - > type ( ) ! = FUNCTION_DEF ) & &
( newv - > type ( ) ! = FAKE ) & &
( newv - > type ( ) ! = TOP ) )
{
env_push ( type = = FUNCTION_CALL ) ;
2012-02-08 01:06:45 +00:00
newv - > wants_pop_env = true ;
2012-11-19 00:30:30 +00:00
}
2005-09-20 13:26:39 +00:00
}
2012-01-23 05:40:08 +00:00
void parser_t : : pop_block ( )
2005-09-20 13:26:39 +00:00
{
2012-11-19 00:30:30 +00:00
block_t * old = current_block ;
if ( ! current_block )
{
debug ( 1 ,
L " function %s called on empty block stack. " ,
__func__ ) ;
bugreport ( ) ;
return ;
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
current_block = current_block - > outer ;
2012-11-18 10:23:22 +00:00
2012-02-08 01:06:45 +00:00
if ( old - > wants_pop_env )
env_pop ( ) ;
2012-11-18 10:23:22 +00:00
2012-02-10 03:26:44 +00:00
delete old ;
2005-09-20 13:26:39 +00:00
}
2012-11-19 00:30:30 +00:00
const wchar_t * parser_t : : get_block_desc ( int block ) const
2005-09-20 13:26:39 +00:00
{
2013-01-24 22:59:52 +00:00
for ( size_t i = 0 ; block_lookup [ i ] . desc ; i + + )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
if ( block_lookup [ i ] . type = = block )
{
return _ ( block_lookup [ i ] . desc ) ;
}
2012-11-18 10:23:22 +00:00
}
2012-11-19 00:30:30 +00:00
return _ ( UNKNOWN_BLOCK ) ;
2005-09-20 13:26:39 +00:00
}
2006-01-23 20:40:14 +00:00
/**
Returns 1 if the specified command is a builtin that may not be used in a pipeline
*/
2012-11-19 00:30:30 +00:00
static int parser_is_pipe_forbidden ( const wcstring & word )
2005-09-20 13:26:39 +00:00
{
2012-11-19 00:30:30 +00:00
return contains ( word ,
L " exec " ,
L " case " ,
L " break " ,
L " return " ,
L " continue " ) ;
2005-09-20 13:26:39 +00:00
}
2005-10-08 02:00:08 +00:00
/**
Search the text for the end of the current block
*/
2012-11-19 00:30:30 +00:00
static const wchar_t * parser_find_end ( const wchar_t * buff )
2005-09-20 13:26:39 +00:00
{
2012-11-19 00:30:30 +00:00
int had_cmd = 0 ;
int count = 0 ;
int error = 0 ;
int mark = 0 ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
CHECK ( buff , 0 ) ;
2012-11-18 10:23:22 +00:00
2012-11-22 01:48:35 +00:00
tokenizer_t tok ( buff , 0 ) ;
for ( ; tok_has_next ( & tok ) & & ! error ; tok_next ( & tok ) )
2012-11-19 00:30:30 +00:00
{
int last_type = tok_last_type ( & tok ) ;
switch ( last_type )
{
2012-11-19 08:31:03 +00:00
case TOK_STRING :
2012-11-19 00:30:30 +00:00
{
2012-11-19 08:31:03 +00:00
if ( ! had_cmd )
2012-11-19 00:30:30 +00:00
{
2012-11-19 08:31:03 +00:00
if ( wcscmp ( tok_last ( & tok ) , L " end " ) = = 0 )
{
count - - ;
}
else if ( parser_keywords_is_block ( tok_last ( & tok ) ) )
{
count + + ;
}
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
if ( count < 0 )
{
error = 1 ;
}
had_cmd = 1 ;
2012-11-19 00:30:30 +00:00
}
2012-11-19 08:31:03 +00:00
break ;
2012-11-19 00:30:30 +00:00
}
2012-11-19 08:31:03 +00:00
case TOK_END :
2012-11-19 00:30:30 +00:00
{
had_cmd = 0 ;
2012-11-19 08:31:03 +00:00
break ;
2012-11-19 00:30:30 +00:00
}
2012-11-19 08:31:03 +00:00
case TOK_PIPE :
case TOK_BACKGROUND :
2012-11-19 00:30:30 +00:00
{
2012-11-19 08:31:03 +00:00
if ( had_cmd )
{
had_cmd = 0 ;
}
else
{
error = 1 ;
}
break ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
}
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
case TOK_ERROR :
error = 1 ;
break ;
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
default :
break ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
}
if ( ! count )
{
tok_next ( & tok ) ;
mark = tok_get_pos ( & tok ) ;
break ;
}
2012-11-18 10:23:22 +00:00
}
2012-11-19 00:30:30 +00:00
if ( ! count & & ! error )
{
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
return buff + mark ;
}
return 0 ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
}
2012-11-19 00:30:30 +00:00
void parser_t : : forbid_function ( const wcstring & function )
2005-09-20 13:26:39 +00:00
{
2012-01-30 06:06:58 +00:00
forbidden_function . push_back ( function ) ;
2005-09-20 13:26:39 +00:00
}
2012-01-23 05:40:08 +00:00
void parser_t : : allow_function ( )
2005-09-20 13:26:39 +00:00
{
2012-11-19 00:30:30 +00:00
/*
if ( al_peek ( & forbidden_function ) )
debug ( 2 , L " Allow %ls \n " , al_peek ( & forbidden_function ) ) ;
*/
2011-12-27 06:51:34 +00:00
forbidden_function . pop_back ( ) ;
2005-09-20 13:26:39 +00:00
}
2012-11-19 00:30:30 +00:00
void parser_t : : error ( int ec , int p , const wchar_t * str , . . . )
2005-09-20 13:26:39 +00:00
{
2012-11-19 00:30:30 +00:00
va_list va ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
CHECK ( str , ) ;
2005-12-07 16:06:47 +00:00
2012-11-19 00:30:30 +00:00
error_code = ec ;
err_pos = p ;
2005-09-20 13:26:39 +00:00
2012-11-19 00:30:30 +00:00
va_start ( va , str ) ;
2012-02-10 02:43:36 +00:00
err_buff = vformat_string ( str , va ) ;
2012-11-19 00:30:30 +00:00
va_end ( va ) ;
2005-12-07 16:06:47 +00:00
2005-09-20 13:26:39 +00:00
}
2006-01-23 20:40:14 +00:00
/**
Print profiling information to the specified stream
*/
2013-01-13 23:49:32 +00:00
static void print_profile ( const std : : vector < profile_item_t * > & items ,
2012-11-19 00:30:30 +00:00
FILE * out )
2005-09-20 13:26:39 +00:00
{
2013-01-13 23:49:32 +00:00
size_t pos ;
for ( pos = 0 ; pos < items . size ( ) ; pos + + )
2012-11-18 10:23:22 +00:00
{
2013-01-13 23:49:32 +00:00
const profile_item_t * me , * prev ;
size_t i ;
int my_time ;
2012-11-18 10:23:22 +00:00
2013-01-13 23:49:32 +00:00
me = items . at ( pos ) ;
if ( ! me - > skipped )
2012-11-19 00:30:30 +00:00
{
2013-01-13 23:49:32 +00:00
my_time = me - > parse + me - > exec ;
2012-11-18 10:23:22 +00:00
2013-01-13 23:49:32 +00:00
for ( i = pos + 1 ; i < items . size ( ) ; i + + )
2012-11-19 00:30:30 +00:00
{
2013-01-13 23:49:32 +00:00
prev = items . at ( i ) ;
if ( prev - > skipped )
{
continue ;
}
2012-11-18 10:23:22 +00:00
2013-01-13 23:49:32 +00:00
if ( prev - > level < = me - > level )
{
break ;
}
2012-11-18 10:23:22 +00:00
2013-01-13 23:49:32 +00:00
if ( prev - > level > me - > level + 1 )
{
continue ;
}
2012-11-18 10:23:22 +00:00
2013-01-13 23:49:32 +00:00
my_time - = prev - > parse ;
my_time - = prev - > exec ;
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
2013-01-13 23:49:32 +00:00
if ( me - > cmd . size ( ) > 0 )
2012-11-19 00:30:30 +00:00
{
2013-01-13 23:49:32 +00:00
if ( fwprintf ( out , L " %d \t %d \t " , my_time , me - > parse + me - > exec ) < 0 )
2012-11-19 00:30:30 +00:00
{
wperror ( L " fwprintf " ) ;
return ;
}
2013-01-13 23:49:32 +00:00
for ( i = 0 ; i < me - > level ; i + + )
{
if ( fwprintf ( out , L " - " ) < 0 )
{
wperror ( L " fwprintf " ) ;
return ;
}
2012-11-18 10:23:22 +00:00
2013-01-13 23:49:32 +00:00
}
if ( fwprintf ( out , L " > %ls \n " , me - > cmd . c_str ( ) ) < 0 )
{
wperror ( L " fwprintf " ) ;
return ;
}
}
2013-01-16 22:50:08 +00:00
delete me ;
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
}
2005-09-20 13:26:39 +00:00
}
2012-01-23 04:47:13 +00:00
void parser_t : : destroy ( )
2005-09-20 13:26:39 +00:00
{
2012-11-19 00:30:30 +00:00
if ( profile )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
/* Save profiling information. OK to not use CLO_EXEC here because this is called while fish is dying (and hence will not fork) */
FILE * f = fopen ( profile , " w " ) ;
if ( ! f )
{
debug ( 1 ,
_ ( L " Could not write profiling information to file '%s' " ) ,
profile ) ;
}
else
{
if ( fwprintf ( f ,
_ ( L " Time \t Sum \t Command \n " ) ,
profile_items . size ( ) ) < 0 )
{
wperror ( L " fwprintf " ) ;
}
else
{
2013-01-13 23:49:32 +00:00
print_profile ( profile_items , f ) ;
2012-11-19 00:30:30 +00:00
}
if ( fclose ( f ) )
{
wperror ( L " fclose " ) ;
}
}
2012-11-18 10:23:22 +00:00
}
2006-01-30 19:53:10 +00:00
2012-02-22 18:51:06 +00:00
lineinfo . clear ( ) ;
2006-01-30 16:51:50 +00:00
2012-11-19 00:30:30 +00:00
forbidden_function . clear ( ) ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
}
2005-10-08 02:00:08 +00:00
/**
2012-03-04 06:08:34 +00:00
Print error message to string if an error has occured while parsing
2006-06-20 00:50:10 +00:00
\ param target the buffer to write to
2012-12-16 04:23:24 +00:00
\ param prefix : The string token to prefix the each line with . Usually the name of the command trying to parse something .
2005-10-08 02:00:08 +00:00
*/
2012-11-19 00:30:30 +00:00
void parser_t : : print_errors ( wcstring & target , const wchar_t * prefix )
2005-09-20 13:26:39 +00:00
{
2012-11-19 00:30:30 +00:00
CHECK ( prefix , ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( error_code & & ! err_buff . empty ( ) )
{
int tmp ;
2005-09-20 13:26:39 +00:00
2012-11-19 00:30:30 +00:00
append_format ( target , L " %ls: %ls \n " , prefix , err_buff . c_str ( ) ) ;
2005-09-20 13:26:39 +00:00
2012-11-19 00:30:30 +00:00
tmp = current_tokenizer_pos ;
current_tokenizer_pos = err_pos ;
2005-09-20 13:26:39 +00:00
2012-11-19 00:30:30 +00:00
append_format ( target , L " %ls " , this - > current_line ( ) ) ;
2006-01-30 19:53:10 +00:00
2012-11-19 00:30:30 +00:00
current_tokenizer_pos = tmp ;
}
2005-09-20 13:26:39 +00:00
}
2006-06-20 00:50:10 +00:00
/**
Print error message to stderr if an error has occured while parsing
*/
2012-01-23 05:57:30 +00:00
void parser_t : : print_errors_stderr ( )
2006-06-02 02:15:17 +00:00
{
2012-11-19 00:30:30 +00:00
if ( error_code & & ! err_buff . empty ( ) )
{
debug ( 0 , L " %ls " , err_buff . c_str ( ) ) ;
int tmp ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
tmp = current_tokenizer_pos ;
current_tokenizer_pos = err_pos ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
fwprintf ( stderr , L " %ls " , this - > current_line ( ) ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
current_tokenizer_pos = tmp ;
}
2012-11-18 10:23:22 +00:00
2006-06-02 02:15:17 +00:00
}
2012-11-19 00:30:30 +00:00
int parser_t : : eval_args ( const wchar_t * line , std : : vector < completion_t > & args )
2005-09-20 13:26:39 +00:00
{
2012-11-18 10:23:22 +00:00
2012-02-28 02:43:24 +00:00
expand_flags_t eflags = 0 ;
if ( ! show_errors )
eflags | = EXPAND_NO_DESCRIPTIONS ;
2012-05-06 20:51:11 +00:00
if ( this - > parser_type ! = PARSER_TYPE_GENERAL )
2012-02-28 02:43:24 +00:00
eflags | = EXPAND_SKIP_CMDSUBST ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
/*
eval_args may be called while evaulating another command , so we
save the previous tokenizer and restore it on exit
*/
2012-11-22 01:48:35 +00:00
tokenizer_t * const previous_tokenizer = current_tokenizer ;
const int previous_pos = current_tokenizer_pos ;
2012-11-19 00:30:30 +00:00
int do_loop = 1 ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
CHECK ( line , 1 ) ;
2012-11-18 10:23:22 +00:00
// CHECK( args, 1 );
2012-02-26 02:54:49 +00:00
// PCA we need to suppress calling proc_push_interactive off of the main thread. I'm not sure exactly what it does.
if ( this - > parser_type = = PARSER_TYPE_GENERAL )
proc_push_interactive ( 0 ) ;
2012-11-18 10:23:22 +00:00
2012-11-22 01:48:35 +00:00
tokenizer_t tok ( line , ( show_errors ? 0 : TOK_SQUASH_ERRORS ) ) ;
2012-11-19 00:30:30 +00:00
current_tokenizer = & tok ;
current_tokenizer_pos = 0 ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
error_code = 0 ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
for ( ; do_loop & & tok_has_next ( & tok ) ; tok_next ( & tok ) )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
current_tokenizer_pos = tok_get_pos ( & tok ) ;
switch ( tok_last_type ( & tok ) )
2012-11-18 10:23:22 +00:00
{
2012-11-19 08:31:03 +00:00
case TOK_STRING :
2012-11-19 00:30:30 +00:00
{
2012-11-19 08:31:03 +00:00
const wcstring tmp = tok_last ( & tok ) ;
if ( expand_string ( tmp , args , eflags ) = = EXPAND_ERROR )
{
err_pos = tok_get_pos ( & tok ) ;
do_loop = 0 ;
}
break ;
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
case TOK_END :
{
break ;
}
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
case TOK_ERROR :
{
if ( show_errors )
error ( SYNTAX_ERROR ,
tok_get_pos ( & tok ) ,
TOK_ERR_MSG ,
tok_last ( & tok ) ) ;
2006-01-30 19:53:10 +00:00
2012-11-19 08:31:03 +00:00
do_loop = 0 ;
break ;
}
2005-09-20 13:26:39 +00:00
2012-11-19 08:31:03 +00:00
default :
{
if ( show_errors )
error ( SYNTAX_ERROR ,
tok_get_pos ( & tok ) ,
UNEXPECTED_TOKEN_ERR_MSG ,
tok_get_desc ( tok_last_type ( & tok ) ) ) ;
2006-01-30 19:53:10 +00:00
2012-11-19 08:31:03 +00:00
do_loop = 0 ;
break ;
}
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
}
2006-06-02 02:15:17 +00:00
2012-02-26 02:54:49 +00:00
if ( show_errors )
this - > print_errors_stderr ( ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
current_tokenizer = previous_tokenizer ;
current_tokenizer_pos = previous_pos ;
2012-11-18 10:23:22 +00:00
2012-02-26 02:54:49 +00:00
if ( this - > parser_type = = PARSER_TYPE_GENERAL )
proc_pop_interactive ( ) ;
2012-11-19 00:30:30 +00:00
return 1 ;
2005-09-20 13:26:39 +00:00
}
2012-11-19 00:30:30 +00:00
void parser_t : : stack_trace ( block_t * b , wcstring & buff )
2012-11-18 10:23:22 +00:00
{
/*
2012-11-19 00:30:30 +00:00
Check if we should end the recursion
2012-11-18 10:23:22 +00:00
*/
2012-11-19 00:30:30 +00:00
if ( ! b )
return ;
if ( b - > type ( ) = = EVENT )
{
/*
This is an event handler
*/
2012-08-27 05:42:29 +00:00
const event_block_t * eb = static_cast < const event_block_t * > ( b ) ;
2012-11-19 00:30:30 +00:00
wcstring description = event_get_desc ( eb - > event ) ;
append_format ( buff , _ ( L " in event handler: %ls \n " ) , description . c_str ( ) ) ;
buff . append ( L " \n " ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
/*
2012-12-16 04:23:24 +00:00
Stop recursing at event handler . No reason to believe that
2012-11-19 00:30:30 +00:00
any other code is relevant .
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
It might make sense in the future to continue printing the
stack trace of the code that invoked the event , if this is a
programmatic event , but we can ' t currently detect that .
*/
return ;
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( b - > type ( ) = = FUNCTION_CALL | | b - > type ( ) = = SOURCE | | b - > type ( ) = = SUBST )
{
/*
These types of blocks should be printed
*/
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
int i ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
switch ( b - > type ( ) )
{
2012-11-19 08:31:03 +00:00
case SOURCE :
{
const source_block_t * sb = static_cast < const source_block_t * > ( b ) ;
const wchar_t * source_dest = sb - > source_file ;
append_format ( buff , _ ( L " in . (source) call of file '%ls', \n " ) , source_dest ) ;
break ;
}
case FUNCTION_CALL :
{
const function_block_t * fb = static_cast < const function_block_t * > ( b ) ;
append_format ( buff , _ ( L " in function '%ls', \n " ) , fb - > name . c_str ( ) ) ;
break ;
}
case SUBST :
{
append_format ( buff , _ ( L " in command substitution \n " ) ) ;
break ;
}
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
default : /* Can't get here */
break ;
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
const wchar_t * file = b - > src_filename ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( file )
{
append_format ( buff ,
_ ( L " \t called on line %d of file '%ls', \n " ) ,
b - > src_lineno ,
file ) ;
}
else
{
append_format ( buff ,
_ ( L " \t called on standard input, \n " ) ) ;
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( b - > type ( ) = = FUNCTION_CALL )
2012-08-27 05:42:29 +00:00
{
const function_block_t * fb = static_cast < const function_block_t * > ( b ) ;
const process_t * const process = fb - > process ;
2012-11-19 00:30:30 +00:00
if ( process - > argv ( 1 ) )
{
wcstring tmp ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
for ( i = 1 ; process - > argv ( i ) ; i + + )
{
2012-02-22 20:00:02 +00:00
if ( i > 1 )
tmp . push_back ( L ' ' ) ;
tmp . append ( process - > argv ( i ) ) ;
2012-11-19 00:30:30 +00:00
}
append_format ( buff , _ ( L " \t with parameter list '%ls' \n " ) , tmp . c_str ( ) ) ;
}
2012-11-18 10:23:22 +00:00
}
2012-11-19 00:30:30 +00:00
append_format ( buff , L " \n " ) ;
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
/*
Recursively print the next block
*/
parser_t : : stack_trace ( b - > outer , buff ) ;
2006-01-26 14:48:10 +00:00
}
2006-06-08 23:55:57 +00:00
/**
2006-10-28 16:41:22 +00:00
Returns the name of the currently evaluated function if we are
currently evaluating a function , null otherwise . This is tested by
moving down the block - scope - stack , checking every block if it is of
type FUNCTION_CALL .
2006-06-08 23:55:57 +00:00
*/
2012-01-23 05:57:30 +00:00
const wchar_t * parser_t : : is_function ( ) const
2006-01-26 14:48:10 +00:00
{
2012-02-08 01:06:45 +00:00
// PCA: Have to make this a string somehow
ASSERT_IS_MAIN_THREAD ( ) ;
wcstring result ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
block_t * b = current_block ;
while ( 1 )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
if ( ! b )
{
return NULL ;
}
if ( b - > type ( ) = = FUNCTION_CALL )
{
2012-08-27 05:42:29 +00:00
const function_block_t * fb = static_cast < const function_block_t * > ( b ) ;
2012-11-19 00:30:30 +00:00
return fb - > name . c_str ( ) ;
}
b = b - > outer ;
2012-11-18 10:23:22 +00:00
}
2006-01-26 14:48:10 +00:00
}
2012-01-23 05:40:08 +00:00
int parser_t : : get_lineno ( ) const
2006-01-26 14:48:10 +00:00
{
2012-11-19 00:30:30 +00:00
int lineno ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( ! current_tokenizer | | ! tok_string ( current_tokenizer ) )
return - 1 ;
2012-11-18 10:23:22 +00:00
2012-08-05 00:44:14 +00:00
lineno = current_tokenizer - > line_number_of_character_at_offset ( current_tokenizer_pos ) ;
2012-11-18 10:23:22 +00:00
2012-08-05 00:44:14 +00:00
const wchar_t * function_name ;
2012-11-19 00:30:30 +00:00
if ( ( function_name = is_function ( ) ) )
{
lineno + = function_get_definition_offset ( function_name ) ;
}
2006-01-30 19:53:10 +00:00
2012-11-19 00:30:30 +00:00
return lineno ;
2006-01-26 14:48:10 +00:00
}
2012-08-05 00:44:14 +00:00
int parser_t : : line_number_of_character_at_offset ( size_t idx ) const
{
2012-11-19 00:30:30 +00:00
if ( ! current_tokenizer )
2012-08-05 00:44:14 +00:00
return - 1 ;
2012-11-18 10:23:22 +00:00
2012-08-05 00:44:14 +00:00
int result = current_tokenizer - > line_number_of_character_at_offset ( idx ) ;
//assert(result == parse_util_lineno(tok_string( current_tokenizer ), idx));
return result ;
}
2012-01-23 05:40:08 +00:00
const wchar_t * parser_t : : current_filename ( ) const
2006-01-26 14:48:10 +00:00
{
2012-02-02 23:05:08 +00:00
/* We query a global array for the current file name, so it only makes sense to ask this on the principal parser. */
ASSERT_IS_MAIN_THREAD ( ) ;
assert ( this = = & principal_parser ( ) ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
block_t * b = current_block ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
while ( 1 )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
if ( ! b )
{
return reader_current_filename ( ) ;
}
if ( b - > type ( ) = = FUNCTION_CALL )
{
2012-08-27 05:42:29 +00:00
const function_block_t * fb = static_cast < const function_block_t * > ( b ) ;
2012-11-19 00:30:30 +00:00
return function_get_definition_file ( fb - > name ) ;
}
b = b - > outer ;
2012-11-18 10:23:22 +00:00
}
2006-01-26 14:48:10 +00:00
}
2006-06-08 23:55:57 +00:00
/**
Calculates the on - screen width of the specified substring of the
specified string . This function takes into account the width and
2012-12-16 04:23:24 +00:00
alignment of the tab character , but other wise behaves like
2006-06-08 23:55:57 +00:00
repeatedly calling wcwidth .
*/
2012-11-19 00:30:30 +00:00
static int printed_width ( const wchar_t * str , int len )
2006-01-26 14:48:10 +00:00
{
2012-11-19 00:30:30 +00:00
int res = 0 ;
int i ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
CHECK ( str , 0 ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
for ( i = 0 ; str [ i ] & & i < len ; i + + )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
if ( str [ i ] = = L ' \t ' )
{
res = ( res + 8 ) & ~ 7 ;
}
else
{
res + = fish_wcwidth ( str [ i ] ) ;
}
2012-11-18 10:23:22 +00:00
}
2012-11-19 00:30:30 +00:00
return res ;
2006-01-26 14:48:10 +00:00
}
2012-01-23 04:47:13 +00:00
const wchar_t * parser_t : : current_line ( )
2005-09-20 13:26:39 +00:00
{
2012-11-19 00:30:30 +00:00
int lineno = 1 ;
const wchar_t * file ;
const wchar_t * whole_str ;
const wchar_t * line ;
const wchar_t * line_end ;
int i ;
int offset ;
int current_line_width ;
const wchar_t * function_name = 0 ;
int current_line_start = 0 ;
if ( ! current_tokenizer )
{
return L " " ;
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
file = parser_t : : current_filename ( ) ;
whole_str = tok_string ( current_tokenizer ) ;
line = whole_str ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( ! line )
return L " " ;
2012-11-18 10:23:22 +00:00
2005-09-20 13:26:39 +00:00
2012-11-18 10:23:22 +00:00
lineinfo . clear ( ) ;
2006-01-30 19:53:10 +00:00
2012-11-19 00:30:30 +00:00
/*
Calculate line number , line offset , etc .
*/
for ( i = 0 ; i < current_tokenizer_pos & & whole_str [ i ] ; i + + )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
if ( whole_str [ i ] = = L ' \n ' )
{
lineno + + ;
current_line_start = i + 1 ;
line = & whole_str [ i + 1 ] ;
}
2012-11-18 10:23:22 +00:00
}
2006-05-10 11:54:31 +00:00
2012-11-18 10:23:22 +00:00
// lineno = current_tokenizer_pos;
2006-01-30 19:53:10 +00:00
2012-11-19 00:30:30 +00:00
current_line_width = printed_width ( whole_str + current_line_start ,
current_tokenizer_pos - current_line_start ) ;
2012-02-22 18:51:06 +00:00
2012-11-19 00:30:30 +00:00
if ( ( function_name = is_function ( ) ) )
{
lineno + = function_get_definition_offset ( function_name ) ;
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
/*
Copy current line from whole string
*/
line_end = wcschr ( line , L ' \n ' ) ;
if ( ! line_end )
line_end = line + wcslen ( line ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
line = wcsndup ( line , line_end - line ) ;
2006-01-30 19:53:10 +00:00
2012-11-19 00:30:30 +00:00
/**
If we are not going to print a stack trace , at least print the line number and filename
*/
if ( ! get_is_interactive ( ) | | is_function ( ) )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
int prev_width = my_wcswidth ( lineinfo . c_str ( ) ) ;
if ( file )
append_format ( lineinfo ,
_ ( L " %ls (line %d): " ) ,
file ,
lineno ) ;
else
append_format ( lineinfo ,
L " %ls: " ,
_ ( L " Standard input " ) ,
lineno ) ;
offset = my_wcswidth ( lineinfo . c_str ( ) ) - prev_width ;
2012-11-18 10:23:22 +00:00
}
else
{
2012-11-19 00:30:30 +00:00
offset = 0 ;
}
// debug( 1, L"Current pos %d, line pos %d, file_length %d, is_interactive %d, offset %d\n", current_tokenizer_pos, current_line_pos, wcslen(whole_str), is_interactive, offset);
/*
Skip printing character position if we are in interactive mode
and the error was on the first character of the line .
*/
if ( ! get_is_interactive ( ) | | is_function ( ) | | ( current_line_width ! = 0 ) )
{
// Workaround since it seems impossible to print 0 copies of a character using %*lc
if ( offset + current_line_width )
{
append_format ( lineinfo ,
L " %ls \n %*lc^ \n " ,
line ,
offset + current_line_width ,
L ' ' ) ;
}
else
{
append_format ( lineinfo ,
L " %ls \n ^ \n " ,
line ) ;
}
2012-11-18 10:23:22 +00:00
}
2012-11-19 00:30:30 +00:00
free ( ( void * ) line ) ;
parser_t : : stack_trace ( current_block , lineinfo ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
return lineinfo . c_str ( ) ;
2005-09-20 13:26:39 +00:00
}
2012-01-23 05:40:08 +00:00
int parser_t : : get_pos ( ) const
2005-09-20 13:26:39 +00:00
{
2012-11-19 00:30:30 +00:00
return tok_get_pos ( current_tokenizer ) ;
2005-09-20 13:26:39 +00:00
}
2012-01-23 05:40:08 +00:00
int parser_t : : get_job_pos ( ) const
2005-09-20 13:26:39 +00:00
{
2012-11-19 00:30:30 +00:00
return job_start_pos ;
2005-09-20 13:26:39 +00:00
}
2012-11-19 00:30:30 +00:00
void parser_t : : set_pos ( int p )
2005-09-20 13:26:39 +00:00
{
2012-11-19 00:30:30 +00:00
tok_set_pos ( current_tokenizer , p ) ;
2005-09-20 13:26:39 +00:00
}
2012-01-23 05:40:08 +00:00
const wchar_t * parser_t : : get_buffer ( ) const
2005-09-20 13:26:39 +00:00
{
2012-11-19 00:30:30 +00:00
return tok_string ( current_tokenizer ) ;
2005-09-20 13:26:39 +00:00
}
2012-11-19 00:30:30 +00:00
int parser_t : : is_help ( const wchar_t * s , int min_match ) const
2005-09-20 13:26:39 +00:00
{
2012-11-19 00:30:30 +00:00
CHECK ( s , 0 ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
size_t len = wcslen ( s ) ;
2006-10-28 16:41:22 +00:00
2012-11-19 00:30:30 +00:00
min_match = maxi ( min_match , 3 ) ;
2005-09-20 13:26:39 +00:00
2012-11-19 00:30:30 +00:00
return ( wcscmp ( L " -h " , s ) = = 0 ) | |
2012-12-21 01:37:09 +00:00
( len > = ( size_t ) min_match & & ( wcsncmp ( L " --help " , s , len ) = = 0 ) ) ;
2005-09-20 13:26:39 +00:00
}
2012-02-28 02:43:24 +00:00
job_t * parser_t : : job_create ( void )
{
job_t * res = new job_t ( acquire_job_id ( ) ) ;
this - > my_job_list . push_front ( res ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
job_set_flag ( res ,
JOB_CONTROL ,
( job_control_mode = = JOB_CONTROL_ALL ) | |
( ( job_control_mode = = JOB_CONTROL_INTERACTIVE ) & & ( get_is_interactive ( ) ) ) ) ;
2012-02-28 02:43:24 +00:00
return res ;
}
2012-11-19 00:30:30 +00:00
bool parser_t : : job_remove ( job_t * j )
2012-02-28 02:43:24 +00:00
{
job_list_t : : iterator iter = std : : find ( my_job_list . begin ( ) , my_job_list . end ( ) , j ) ;
2012-11-19 00:30:30 +00:00
if ( iter ! = my_job_list . end ( ) )
{
2012-02-28 02:43:24 +00:00
my_job_list . erase ( iter ) ;
return true ;
2012-11-19 00:30:30 +00:00
}
else
{
debug ( 1 , _ ( L " Job inconsistency " ) ) ;
sanity_lose ( ) ;
2012-02-28 02:43:24 +00:00
return false ;
}
}
void parser_t : : job_promote ( job_t * job )
{
2012-02-28 15:50:09 +00:00
job_list_t : : iterator loc = std : : find ( my_job_list . begin ( ) , my_job_list . end ( ) , job ) ;
2012-02-28 02:43:24 +00:00
assert ( loc ! = my_job_list . end ( ) ) ;
2012-11-18 10:23:22 +00:00
2012-02-28 02:43:24 +00:00
/* Move the job to the beginning */
my_job_list . splice ( my_job_list . begin ( ) , my_job_list , loc ) ;
}
job_t * parser_t : : job_get ( job_id_t id )
{
job_iterator_t jobs ( my_job_list ) ;
job_t * job ;
2012-11-19 00:30:30 +00:00
while ( ( job = jobs . next ( ) ) )
{
if ( id < = 0 | | job - > job_id = = id )
2012-02-28 02:43:24 +00:00
return job ;
2012-11-19 00:30:30 +00:00
}
return NULL ;
2012-02-28 02:43:24 +00:00
}
2012-11-19 00:30:30 +00:00
job_t * parser_t : : job_get_from_pid ( int pid )
2012-02-28 02:43:24 +00:00
{
job_iterator_t jobs ;
job_t * job ;
2012-11-19 00:30:30 +00:00
while ( ( job = jobs . next ( ) ) )
{
if ( job - > pgid = = pid )
return job ;
}
return 0 ;
2012-02-28 02:43:24 +00:00
}
2005-09-20 13:26:39 +00:00
/**
Parse options for the specified job
\ param p the process to parse options for
\ param j the job to which the process belongs to
\ param tok the tokenizer to read options from
\ param args the argument list to insert options into
2012-11-05 01:11:02 +00:00
\ param args unskip whether we should ignore current_block - > skip . Big hack because of our dumb handling of if statements .
2005-09-20 13:26:39 +00:00
*/
2012-11-19 00:30:30 +00:00
void parser_t : : parse_job_argument_list ( process_t * p ,
job_t * j ,
2012-11-22 01:48:35 +00:00
tokenizer_t * tok ,
2012-11-19 00:30:30 +00:00
std : : vector < completion_t > & args ,
bool unskip )
2005-09-20 13:26:39 +00:00
{
2012-11-19 00:30:30 +00:00
int is_finished = 0 ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
int proc_is_count = 0 ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
int matched_wildcard = 0 , unmatched_wildcard = 0 ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
wcstring unmatched ;
int unmatched_pos = 0 ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
/*
Test if this is the ' count ' command . We need to special case
count in the shell , since it should display a help message on
' count - h ' , but not on ' set foo - h ; count $ foo ' . This is an ugly
workaround and a huge hack , but as near as I can tell , the
alternatives are worse .
*/
proc_is_count = ( args . at ( 0 ) . completion = = L " count " ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
while ( 1 )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
switch ( tok_last_type ( tok ) )
2012-11-18 10:23:22 +00:00
{
2012-11-19 08:31:03 +00:00
case TOK_PIPE :
2012-11-19 00:30:30 +00:00
{
2012-11-19 08:31:03 +00:00
wchar_t * end ;
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
if ( p - > type = = INTERNAL_EXEC )
{
error ( SYNTAX_ERROR ,
tok_get_pos ( tok ) ,
EXEC_ERR_MSG ) ;
return ;
}
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
errno = 0 ;
p - > pipe_write_fd = fish_wcstoi ( tok_last ( tok ) , & end , 10 ) ;
if ( p - > pipe_write_fd < 0 | | errno | | * end )
{
error ( SYNTAX_ERROR ,
tok_get_pos ( tok ) ,
ILLEGAL_FD_ERR_MSG ,
tok_last ( tok ) ) ;
return ;
}
2012-11-19 00:30:30 +00:00
p - > set_argv ( completions_to_wcstring_list ( args ) ) ;
2012-11-19 08:31:03 +00:00
p - > next = new process_t ( ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
tok_next ( tok ) ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
/*
2012-12-16 04:23:24 +00:00
Don ' t do anything on failure . parse_job will notice
2012-11-19 08:31:03 +00:00
the error flag and report any errors for us
*/
parse_job ( p - > next , j , tok ) ;
2006-01-30 19:53:10 +00:00
2012-11-19 08:31:03 +00:00
is_finished = 1 ;
break ;
}
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
case TOK_BACKGROUND :
2012-11-18 10:23:22 +00:00
{
2012-11-19 08:31:03 +00:00
job_set_flag ( j , JOB_FOREGROUND , 0 ) ;
2012-11-18 10:23:22 +00:00
}
2012-11-19 08:31:03 +00:00
case TOK_END :
2012-11-18 10:23:22 +00:00
{
2012-11-19 08:31:03 +00:00
if ( ! p - > get_argv ( ) )
p - > set_argv ( completions_to_wcstring_list ( args ) ) ;
if ( tok_has_next ( tok ) )
tok_next ( tok ) ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
is_finished = 1 ;
break ;
2012-11-18 10:23:22 +00:00
}
2012-11-19 08:31:03 +00:00
case TOK_STRING :
2012-11-18 10:23:22 +00:00
{
2012-11-19 08:31:03 +00:00
int skip = 0 ;
if ( job_get_flag ( j , JOB_SKIP ) )
2012-11-19 00:30:30 +00:00
{
skip = 1 ;
}
2012-11-19 08:31:03 +00:00
else if ( current_block - > skip & & ! unskip )
2012-11-19 00:30:30 +00:00
{
/*
2012-11-19 08:31:03 +00:00
If this command should be skipped , we do not expand the arguments
2012-11-19 00:30:30 +00:00
*/
2012-11-19 08:31:03 +00:00
skip = 1 ;
2006-01-30 19:53:10 +00:00
2012-11-19 08:31:03 +00:00
/* But if this is in fact a case statement or an elseif statement, then it should be evaluated */
block_type_t type = current_block - > type ( ) ;
if ( type = = SWITCH & & args . at ( 0 ) . completion = = L " case " & & p - > type = = INTERNAL_BUILTIN )
2012-11-19 00:30:30 +00:00
{
2012-11-19 08:31:03 +00:00
skip = 0 ;
}
else if ( job_get_flag ( j , JOB_ELSEIF ) & & ! job_should_skip_elseif ( j , current_block ) )
{
skip = 0 ;
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
/* If this is an else if, and we should skip it, then don't expand any arguments */
if ( job_get_flag ( j , JOB_ELSEIF ) & & job_should_skip_elseif ( j , current_block ) )
2012-11-19 00:30:30 +00:00
{
2012-11-19 08:31:03 +00:00
skip = 1 ;
2012-11-19 00:30:30 +00:00
}
}
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
if ( ! skip )
2012-11-19 00:30:30 +00:00
{
2012-11-19 08:31:03 +00:00
if ( ( proc_is_count ) & &
( args . size ( ) = = 1 ) & &
( parser_t : : is_help ( tok_last ( tok ) , 0 ) ) & &
( p - > type = = INTERNAL_BUILTIN ) )
{
/*
Display help for count
*/
p - > count_help_magic = 1 ;
}
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
switch ( expand_string ( tok_last ( tok ) , args , 0 ) )
{
case EXPAND_ERROR :
{
err_pos = tok_get_pos ( tok ) ;
if ( error_code = = 0 )
{
error ( SYNTAX_ERROR ,
tok_get_pos ( tok ) ,
_ ( L " Could not expand string '%ls' " ) ,
tok_last ( tok ) ) ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
}
break ;
}
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
case EXPAND_WILDCARD_NO_MATCH :
{
unmatched_wildcard = 1 ;
if ( unmatched . empty ( ) )
{
unmatched = tok_last ( tok ) ;
unmatched_pos = tok_get_pos ( tok ) ;
}
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
break ;
}
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
case EXPAND_WILDCARD_MATCH :
{
matched_wildcard = 1 ;
break ;
}
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
case EXPAND_OK :
{
break ;
}
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
break ;
2012-11-18 10:23:22 +00:00
}
2012-11-19 08:31:03 +00:00
case TOK_REDIRECT_OUT :
case TOK_REDIRECT_IN :
case TOK_REDIRECT_APPEND :
case TOK_REDIRECT_FD :
case TOK_REDIRECT_NOCLOB :
2012-11-18 10:23:22 +00:00
{
2012-11-19 08:31:03 +00:00
int type = tok_last_type ( tok ) ;
2013-01-07 15:04:55 +00:00
shared_ptr < io_data_t > new_io ;
2012-11-19 08:31:03 +00:00
wcstring target ;
bool has_target = false ;
wchar_t * end ;
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
/*
Don ' t check redirections in skipped part
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
Otherwise , bogus errors may be the result . ( Do check
that token is string , though )
*/
if ( current_block - > skip & & ! unskip )
2012-11-19 00:30:30 +00:00
{
2012-11-19 08:31:03 +00:00
tok_next ( tok ) ;
if ( tok_last_type ( tok ) ! = TOK_STRING )
2012-11-19 00:30:30 +00:00
{
error ( SYNTAX_ERROR ,
tok_get_pos ( tok ) ,
REDIRECT_TOKEN_ERR_MSG ,
2012-11-19 08:31:03 +00:00
tok_get_desc ( tok_last_type ( tok ) ) ) ;
2012-11-19 00:30:30 +00:00
}
2012-11-19 08:31:03 +00:00
2012-11-19 00:30:30 +00:00
break ;
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
errno = 0 ;
2013-01-09 07:56:52 +00:00
int fd = fish_wcstoi ( tok_last ( tok ) ,
& end ,
10 ) ;
if ( fd < 0 | | errno | | * end )
2012-11-18 10:23:22 +00:00
{
2012-11-19 08:31:03 +00:00
error ( SYNTAX_ERROR ,
tok_get_pos ( tok ) ,
ILLEGAL_FD_ERR_MSG ,
tok_last ( tok ) ) ;
2012-11-18 10:23:22 +00:00
}
else
{
2012-11-19 08:31:03 +00:00
tok_next ( tok ) ;
switch ( tok_last_type ( tok ) )
2012-11-19 00:30:30 +00:00
{
2012-11-19 08:31:03 +00:00
case TOK_STRING :
{
target = tok_last ( tok ) ;
has_target = expand_one ( target , no_exec ? EXPAND_SKIP_VARIABLES : 0 ) ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
if ( ! has_target & & error_code = = 0 )
{
error ( SYNTAX_ERROR ,
tok_get_pos ( tok ) ,
REDIRECT_TOKEN_ERR_MSG ,
tok_last ( tok ) ) ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
}
break ;
}
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
default :
error ( SYNTAX_ERROR ,
tok_get_pos ( tok ) ,
REDIRECT_TOKEN_ERR_MSG ,
tok_get_desc ( tok_last_type ( tok ) ) ) ;
}
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
if ( ! has_target | | target . empty ( ) )
2012-11-19 00:30:30 +00:00
{
2012-11-19 08:31:03 +00:00
if ( error_code = = 0 )
error ( SYNTAX_ERROR ,
tok_get_pos ( tok ) ,
_ ( L " Invalid IO redirection " ) ) ;
tok_next ( tok ) ;
}
2013-01-15 08:18:03 +00:00
else if ( type = = TOK_REDIRECT_FD )
2012-11-19 08:31:03 +00:00
{
2013-01-15 08:18:03 +00:00
if ( target = = L " - " )
{
new_io . reset ( new io_close_t ( fd ) ) ;
}
else
{
wchar_t * end ;
errno = 0 ;
int old_fd = fish_wcstoi ( target . c_str ( ) , & end , 10 ) ;
2012-11-19 08:31:03 +00:00
2013-01-15 08:18:03 +00:00
if ( old_fd < 0 | | errno | | * end )
{
error ( SYNTAX_ERROR ,
tok_get_pos ( tok ) ,
_ ( L " Requested redirection to something that is not a file descriptor %ls " ) ,
target . c_str ( ) ) ;
tok_next ( tok ) ;
}
else
{
new_io . reset ( new io_fd_t ( fd , old_fd ) ) ;
}
}
}
else
{
int flags = 0 ;
2012-11-19 08:31:03 +00:00
switch ( type )
2012-11-19 00:30:30 +00:00
{
2012-11-19 08:31:03 +00:00
case TOK_REDIRECT_APPEND :
2013-01-15 08:18:03 +00:00
flags = O_CREAT | O_APPEND | O_WRONLY ;
2012-11-19 08:31:03 +00:00
break ;
case TOK_REDIRECT_OUT :
2013-01-15 08:18:03 +00:00
flags = O_CREAT | O_WRONLY | O_TRUNC ;
2012-11-19 08:31:03 +00:00
break ;
case TOK_REDIRECT_NOCLOB :
2013-01-15 08:18:03 +00:00
flags = O_CREAT | O_EXCL | O_WRONLY ;
2012-11-19 08:31:03 +00:00
break ;
case TOK_REDIRECT_IN :
2013-01-15 08:18:03 +00:00
flags = O_RDONLY ;
2012-11-19 08:31:03 +00:00
break ;
2012-11-19 00:30:30 +00:00
}
2013-01-19 18:59:43 +00:00
io_file_t * new_io_file = new io_file_t ( fd , target , flags ) ;
2013-01-15 08:18:03 +00:00
new_io . reset ( new_io_file ) ;
2012-11-19 08:31:03 +00:00
}
2012-11-18 10:23:22 +00:00
}
2013-01-07 15:04:55 +00:00
j - > io . push_back ( new_io ) ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
}
break ;
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
case TOK_ERROR :
{
error ( SYNTAX_ERROR ,
tok_get_pos ( tok ) ,
TOK_ERR_MSG ,
tok_last ( tok ) ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
return ;
}
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
default :
error ( SYNTAX_ERROR ,
tok_get_pos ( tok ) ,
UNEXPECTED_TOKEN_ERR_MSG ,
tok_get_desc ( tok_last_type ( tok ) ) ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
tok_next ( tok ) ;
break ;
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( ( is_finished ) | | ( error_code ! = 0 ) )
break ;
2012-11-18 10:23:22 +00:00
tok_next ( tok ) ;
}
2012-11-19 00:30:30 +00:00
if ( ! error_code )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
if ( unmatched_wildcard & & ! matched_wildcard )
{
job_set_flag ( j , JOB_WILDCARD_ERROR , 1 ) ;
proc_set_last_status ( STATUS_UNMATCHED_WILDCARD ) ;
if ( get_is_interactive ( ) & & ! is_block )
{
int tmp ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
debug ( 1 , WILDCARD_ERR_MSG , unmatched . c_str ( ) ) ;
tmp = current_tokenizer_pos ;
current_tokenizer_pos = unmatched_pos ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
fwprintf ( stderr , L " %ls " , parser_t : : current_line ( ) ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
current_tokenizer_pos = tmp ;
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
}
2012-11-19 00:30:30 +00:00
return ;
2005-09-20 13:26:39 +00:00
}
2006-11-02 13:45:37 +00:00
2006-10-26 10:22:53 +00:00
/*
2007-01-09 13:41:17 +00:00
static void print_block_stack ( block_t * b )
{
if ( ! b )
return ;
print_block_stack ( b - > outer ) ;
2012-11-18 10:23:22 +00:00
debug ( 0 , L " Block type %ls, skip: %d " , parser_get_block_desc ( b - > type ) , b - > skip ) ;
2007-01-09 13:41:17 +00:00
}
2006-10-26 10:22:53 +00:00
*/
2012-11-18 10:23:22 +00:00
2005-09-20 13:26:39 +00:00
/**
2006-06-08 23:55:57 +00:00
Fully parse a single job . Does not call exec on it , but any command substitutions in the job will be executed .
2005-09-20 13:26:39 +00:00
\ param p The process structure that should be used to represent the first process in the job .
\ param j The job structure to contain the parsed job
\ param tok tokenizer to read from
2011-12-27 03:18:46 +00:00
f
2005-09-20 13:26:39 +00:00
\ return 1 on success , 0 on error
*/
2012-11-19 00:30:30 +00:00
int parser_t : : parse_job ( process_t * p ,
job_t * j ,
2012-11-22 01:48:35 +00:00
tokenizer_t * tok )
2005-09-20 13:26:39 +00:00
{
2012-12-16 04:23:24 +00:00
std : : vector < completion_t > args ; // The list that will become the argv array for the program
2012-11-19 00:30:30 +00:00
int use_function = 1 ; // May functions be considered when checking what action this command represents
int use_builtin = 1 ; // May builtins be considered when checking what action this command represents
int use_command = 1 ; // May commands be considered when checking what action this command represents
int is_new_block = 0 ; // Does this command create a new block?
bool unskip = false ; // Maybe we are an elseif inside an if block; if so we may want to evaluate this even if the if block is currently set to skip
bool allow_bogus_command = false ; // If we are an elseif that will not be executed, or an AND or OR that will have been short circuited, don't complain about non-existent commands
2006-01-30 19:53:10 +00:00
2012-11-19 00:30:30 +00:00
block_t * prev_block = current_block ;
int prev_tokenizer_pos = current_tokenizer_pos ;
2006-10-29 21:09:11 +00:00
2012-11-19 00:30:30 +00:00
current_tokenizer_pos = tok_get_pos ( tok ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
while ( args . empty ( ) )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
wcstring nxt ;
bool has_nxt = false ;
bool consumed = false ; // Set to one if the command requires a second command, like e.g. while does
int mark ; // Use to save the position of the beginning of the token
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
switch ( tok_last_type ( tok ) )
2012-11-18 10:23:22 +00:00
{
2012-11-19 08:31:03 +00:00
case TOK_STRING :
{
nxt = tok_last ( tok ) ;
has_nxt = expand_one ( nxt , EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_VARIABLES ) ;
if ( ! has_nxt )
{
error ( SYNTAX_ERROR ,
tok_get_pos ( tok ) ,
ILLEGAL_CMD_ERR_MSG ,
tok_last ( tok ) ) ;
current_tokenizer_pos = prev_tokenizer_pos ;
return 0 ;
}
break ;
}
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
case TOK_ERROR :
2012-11-19 00:30:30 +00:00
{
error ( SYNTAX_ERROR ,
tok_get_pos ( tok ) ,
2012-11-19 08:31:03 +00:00
TOK_ERR_MSG ,
2012-11-19 00:30:30 +00:00
tok_last ( tok ) ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
current_tokenizer_pos = prev_tokenizer_pos ;
return 0 ;
}
2012-11-19 08:31:03 +00:00
case TOK_PIPE :
2012-11-19 00:30:30 +00:00
{
2012-11-19 08:31:03 +00:00
const wchar_t * str = tok_string ( tok ) ;
if ( tok_get_pos ( tok ) > 0 & & str [ tok_get_pos ( tok ) - 1 ] = = L ' | ' )
{
error ( SYNTAX_ERROR ,
tok_get_pos ( tok ) ,
CMD_OR_ERR_MSG ,
tok_get_desc ( tok_last_type ( tok ) ) ) ;
}
else
{
error ( SYNTAX_ERROR ,
tok_get_pos ( tok ) ,
CMD_ERR_MSG ,
tok_get_desc ( tok_last_type ( tok ) ) ) ;
}
current_tokenizer_pos = prev_tokenizer_pos ;
return 0 ;
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
{
error ( SYNTAX_ERROR ,
tok_get_pos ( tok ) ,
CMD_ERR_MSG ,
tok_get_desc ( tok_last_type ( tok ) ) ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
current_tokenizer_pos = prev_tokenizer_pos ;
return 0 ;
}
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
mark = tok_get_pos ( tok ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( contains ( nxt ,
L " command " ,
L " builtin " ,
L " not " ,
L " and " ,
L " or " ,
L " exec " ) )
{
int sw ;
int is_exec = nxt = = L " exec " ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( is_exec & & ( p ! = j - > first_process ) )
{
error ( SYNTAX_ERROR ,
tok_get_pos ( tok ) ,
EXEC_ERR_MSG ) ;
current_tokenizer_pos = prev_tokenizer_pos ;
return 0 ;
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
tok_next ( tok ) ;
sw = parser_keywords_is_switch ( tok_last ( tok ) ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( sw = = ARG_SWITCH )
{
tok_set_pos ( tok , mark ) ;
}
else
{
if ( sw = = ARG_SKIP )
{
tok_next ( tok ) ;
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
consumed = true ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( nxt = = L " command " | | nxt = = L " builtin " )
{
use_function = 0 ;
if ( nxt = = L " command " )
{
use_builtin = 0 ;
use_command = 1 ;
}
else
{
use_builtin = 1 ;
use_command = 0 ;
}
}
else if ( nxt = = L " not " )
{
job_set_flag ( j , JOB_NEGATE , ! job_get_flag ( j , JOB_NEGATE ) ) ;
}
else if ( nxt = = L " and " )
{
2012-10-17 08:07:34 +00:00
bool skip = ( proc_get_last_status ( ) ! = 0 ) ;
2012-11-19 00:30:30 +00:00
job_set_flag ( j , JOB_SKIP , skip ) ;
2012-10-17 08:07:34 +00:00
allow_bogus_command = skip ;
2012-11-19 00:30:30 +00:00
}
else if ( nxt = = L " or " )
{
2012-10-17 08:07:34 +00:00
bool skip = ( proc_get_last_status ( ) = = 0 ) ;
2012-11-19 00:30:30 +00:00
job_set_flag ( j , JOB_SKIP , skip ) ;
2012-10-17 08:07:34 +00:00
allow_bogus_command = skip ;
2012-11-19 00:30:30 +00:00
}
else if ( is_exec )
{
use_function = 0 ;
use_builtin = 0 ;
p - > type = INTERNAL_EXEC ;
current_tokenizer_pos = prev_tokenizer_pos ;
}
}
2012-11-18 10:23:22 +00:00
}
2012-11-19 00:30:30 +00:00
else if ( nxt = = L " while " )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
bool new_block = false ;
tok_next ( tok ) ;
2012-08-27 05:42:29 +00:00
while_block_t * wb = NULL ;
2005-09-20 13:26:39 +00:00
2012-11-19 00:30:30 +00:00
if ( ( current_block - > type ( ) ! = WHILE ) )
{
new_block = true ;
}
else if ( ( wb = static_cast < while_block_t * > ( current_block ) ) - > status = = WHILE_TEST_AGAIN )
{
wb - > status = WHILE_TEST_FIRST ;
}
else
{
new_block = true ;
}
if ( new_block )
{
2012-08-27 05:42:29 +00:00
while_block_t * wb = new while_block_t ( ) ;
2012-11-19 00:30:30 +00:00
wb - > status = WHILE_TEST_FIRST ;
wb - > tok_pos = mark ;
this - > push_block ( wb ) ;
}
2006-01-30 19:53:10 +00:00
2012-11-19 00:30:30 +00:00
consumed = true ;
is_new_block = 1 ;
2006-05-14 22:29:05 +00:00
2012-11-19 00:30:30 +00:00
}
else if ( nxt = = L " if " )
{
tok_next ( tok ) ;
2005-09-20 13:26:39 +00:00
2012-08-27 05:42:29 +00:00
if_block_t * ib = new if_block_t ( ) ;
2012-11-19 00:30:30 +00:00
this - > push_block ( ib ) ;
ib - > tok_pos = mark ;
2005-09-20 13:26:39 +00:00
2012-11-19 00:30:30 +00:00
is_new_block = 1 ;
consumed = true ;
}
2012-09-03 20:24:01 +00:00
else if ( nxt = = L " else " )
2012-09-01 08:46:14 +00:00
{
2012-09-03 20:24:01 +00:00
/* Record where the else is for error reporting */
const int else_pos = tok_get_pos ( tok ) ;
/* See if we have any more arguments, that is, whether we're ELSE IF ... or just ELSE. */
tok_next ( tok ) ;
if ( tok_last_type ( tok ) = = TOK_STRING & & current_block - > type ( ) = = IF )
2012-09-01 08:46:14 +00:00
{
const if_block_t * ib = static_cast < const if_block_t * > ( current_block ) ;
2012-11-18 10:23:22 +00:00
2012-09-03 20:24:01 +00:00
/* If we've already encountered an else, complain */
if ( ib - > else_evaluated )
{
2012-11-19 00:30:30 +00:00
error ( SYNTAX_ERROR ,
else_pos ,
INVALID_ELSEIF_PAST_ELSE_ERR_MSG ,
L " else if " ) ;
2012-09-03 20:24:01 +00:00
}
else
{
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
job_set_flag ( j , JOB_ELSEIF , 1 ) ;
2012-09-03 20:24:01 +00:00
consumed = true ;
2012-11-18 10:23:22 +00:00
2012-09-03 20:24:01 +00:00
/* We're at the IF. Go past it. */
tok_next ( tok ) ;
2012-11-18 10:23:22 +00:00
2012-09-03 20:24:01 +00:00
/* We want to execute this ELSEIF if the IF expression was evaluated, it failed, and so has every other ELSEIF (if any) */
unskip = ( ib - > if_expr_evaluated & & ! ib - > any_branch_taken ) ;
2012-11-18 10:23:22 +00:00
2012-10-17 08:07:34 +00:00
/* But if we're not executing it, don't complain about its command if it doesn't exist */
if ( ! unskip )
allow_bogus_command = true ;
2012-09-03 20:24:01 +00:00
}
2012-09-01 08:46:14 +00:00
}
}
2006-02-09 15:50:20 +00:00
2012-11-19 00:30:30 +00:00
/*
Test if we need another command
*/
if ( consumed )
{
/*
Yes we do , around in the loop for another lap , then !
*/
continue ;
}
if ( use_function & & ( unskip | | ! current_block - > skip ) )
{
bool nxt_forbidden = false ;
wcstring forbid ;
int is_function_call = 0 ;
/*
This is a bit fragile . It is a test to see if we are
inside of function call , but not inside a block in that
function call . If , in the future , the rules for what
block scopes are pushed on function invocation changes ,
then this check will break .
*/
if ( ( current_block - > type ( ) = = TOP ) & &
( current_block - > outer ) & &
( current_block - > outer - > type ( ) = = FUNCTION_CALL ) )
is_function_call = 1 ;
/*
If we are directly in a function , and this is the first
command of the block , then the function we are executing
may not be called , since that would mean an infinite
recursion .
*/
if ( is_function_call & & ! current_block - > had_command )
{
2011-12-27 06:51:34 +00:00
forbid = forbidden_function . empty ( ) ? wcstring ( L " " ) : forbidden_function . back ( ) ;
2012-07-09 22:18:22 +00:00
if ( forbid = = nxt )
{
/* Infinite recursive loop */
nxt_forbidden = true ;
2012-11-19 00:30:30 +00:00
error ( SYNTAX_ERROR , tok_get_pos ( tok ) , INFINITE_RECURSION_ERR_MSG ) ;
2012-07-09 22:18:22 +00:00
}
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( ! nxt_forbidden & & has_nxt & & function_exists ( nxt ) )
{
/*
Check if we have reached the maximum recursion depth
*/
if ( forbidden_function . size ( ) > MAX_RECURSION_DEPTH )
{
error ( SYNTAX_ERROR , tok_get_pos ( tok ) , OVERFLOW_RECURSION_ERR_MSG ) ;
}
else
{
p - > type = INTERNAL_FUNCTION ;
}
}
2012-11-18 10:23:22 +00:00
}
2012-11-19 00:30:30 +00:00
args . push_back ( completion_t ( nxt ) ) ;
2012-11-18 10:23:22 +00:00
}
2012-11-19 00:30:30 +00:00
if ( error_code = = 0 )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
if ( ! p - > type )
{
if ( use_builtin & &
builtin_exists ( args . at ( 0 ) . completion ) )
{
p - > type = INTERNAL_BUILTIN ;
is_new_block | = parser_keywords_is_block ( args . at ( 0 ) . completion ) ;
}
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( ( ! p - > type | | ( p - > type = = INTERNAL_EXEC ) ) )
{
/*
If we are not executing the current block , allow
non - existent commands .
*/
2012-10-17 08:07:34 +00:00
if ( current_block - > skip & & ! unskip )
allow_bogus_command = true ; //note this may already be true for other reasons
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( allow_bogus_command )
{
p - > actual_cmd . clear ( ) ;
}
else
{
int err ;
2012-07-21 03:39:31 +00:00
bool has_command = path_get_path ( args . at ( 0 ) . completion , & p - > actual_cmd ) ;
2012-11-19 00:30:30 +00:00
err = errno ;
2012-11-18 10:23:22 +00:00
2012-06-02 21:04:25 +00:00
bool use_implicit_cd = false ;
2012-07-21 03:39:31 +00:00
if ( ! has_command )
2012-06-02 21:04:25 +00:00
{
/* If the specified command does not exist, try using an implicit cd. */
wcstring implicit_cd_path ;
use_implicit_cd = path_can_be_implicit_cd ( args . at ( 0 ) . completion , & implicit_cd_path ) ;
if ( use_implicit_cd )
{
args . clear ( ) ;
args . push_back ( completion_t ( L " cd " ) ) ;
args . push_back ( completion_t ( implicit_cd_path ) ) ;
2012-11-18 10:23:22 +00:00
2012-06-02 21:04:25 +00:00
/* If we have defined a wrapper around cd, use it, otherwise use the cd builtin */
if ( use_function & & function_exists ( L " cd " ) )
p - > type = INTERNAL_FUNCTION ;
else
p - > type = INTERNAL_BUILTIN ;
}
}
2012-11-18 10:23:22 +00:00
2013-01-04 21:09:01 +00:00
// Disabled pending discussion in https://github.com/fish-shell/fish-shell/issues/367
#if 0
if ( ! has_command & & ! use_implicit_cd )
{
if ( fish_openSUSE_dbus_hack_hack_hack_hack ( & args ) )
{
has_command = true ;
p - > type = INTERNAL_BUILTIN ;
}
}
# endif
2012-11-19 00:30:30 +00:00
/* Check if the specified command exists */
if ( ! has_command & & ! use_implicit_cd )
{
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
int tmp ;
const wchar_t * cmd = args . at ( 0 ) . completion . c_str ( ) ;
2012-11-18 10:23:22 +00:00
/*
2012-03-02 19:12:08 +00:00
We couldn ' t find the specified command .
2012-11-18 10:23:22 +00:00
2012-03-02 19:12:08 +00:00
What we want to happen now is that the
specified job won ' t get executed , and an
error message is printed on - screen , but
otherwise , the parsing / execution of the
file continues . Because of this , we don ' t
want to call error ( ) , since that would stop
execution of the file . Instead we let
p - > actual_command be 0 ( null ) , which will
cause the job to silently not execute . We
also print an error message and set the
status to 127 ( This is the standard number
for this , used by other shells like bash
and zsh ) .
*/
2012-11-19 00:30:30 +00:00
if ( wcschr ( cmd , L ' = ' ) )
2012-03-02 19:12:08 +00:00
{
2012-11-19 00:30:30 +00:00
wchar_t * cpy = wcsdup ( cmd ) ;
wchar_t * valpart = wcschr ( cpy , L ' = ' ) ;
2012-03-02 19:12:08 +00:00
* valpart + + = 0 ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
debug ( 0 ,
2012-03-02 19:12:08 +00:00
COMMAND_ASSIGN_ERR_MSG ,
cmd ,
cpy ,
valpart ) ;
free ( cpy ) ;
2012-11-18 10:23:22 +00:00
2012-03-02 19:12:08 +00:00
}
2012-11-19 00:30:30 +00:00
else if ( cmd [ 0 ] = = L ' $ ' )
2012-03-02 19:12:08 +00:00
{
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
const env_var_t val_wstr = env_get_string ( cmd + 1 ) ;
2012-11-18 10:23:22 +00:00
const wchar_t * val = val_wstr . missing ( ) ? NULL : val_wstr . c_str ( ) ;
2012-11-19 00:30:30 +00:00
if ( val )
2012-03-02 19:12:08 +00:00
{
2012-11-19 00:30:30 +00:00
debug ( 0 ,
_ ( L " Variables may not be used as commands. Instead, define a function like 'function %ls; %ls $argv; end'. See the help section for the function command by typing 'help function'. " ) ,
2012-03-02 19:12:08 +00:00
cmd + 1 ,
val ,
2012-11-19 00:30:30 +00:00
cmd ) ;
2012-03-02 19:12:08 +00:00
}
else
{
2012-11-19 00:30:30 +00:00
debug ( 0 ,
_ ( L " Variables may not be used as commands. Instead, define a function. See the help section for the function command by typing 'help function'. " ) ,
cmd ) ;
2012-11-18 10:23:22 +00:00
}
2012-03-02 19:12:08 +00:00
}
2012-11-19 00:30:30 +00:00
else if ( wcschr ( cmd , L ' $ ' ) )
2012-03-02 19:12:08 +00:00
{
2012-11-19 00:30:30 +00:00
debug ( 0 ,
_ ( L " Commands may not contain variables. Use the eval builtin instead, like 'eval %ls'. See the help section for the eval command by typing 'help eval'. " ) ,
2012-03-02 19:12:08 +00:00
cmd ,
2012-11-19 00:30:30 +00:00
cmd ) ;
2012-03-02 19:12:08 +00:00
}
2012-11-19 00:30:30 +00:00
else if ( err ! = ENOENT )
2012-03-02 19:12:08 +00:00
{
2012-11-19 00:30:30 +00:00
debug ( 0 ,
2012-03-02 19:12:08 +00:00
_ ( L " The file '%ls' is not executable by this user " ) ,
2012-11-19 00:30:30 +00:00
cmd ? cmd : L " UNKNOWN " ) ;
2012-03-02 19:12:08 +00:00
}
else
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
debug ( 0 ,
2012-03-02 19:12:08 +00:00
_ ( L " Unknown command '%ls' " ) ,
2012-11-19 00:30:30 +00:00
cmd ? cmd : L " UNKNOWN " ) ;
2012-03-02 19:12:08 +00:00
}
2012-11-18 10:23:22 +00:00
2012-03-02 19:12:08 +00:00
tmp = current_tokenizer_pos ;
current_tokenizer_pos = tok_get_pos ( tok ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
fwprintf ( stderr , L " %ls " , parser_t : : current_line ( ) ) ;
2012-11-18 10:23:22 +00:00
2012-03-02 19:12:08 +00:00
current_tokenizer_pos = tmp ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
job_set_flag ( j , JOB_SKIP , 1 ) ;
2012-12-20 00:11:55 +00:00
2012-12-22 20:40:34 +00:00
wcstring_list_t event_args ;
2012-12-20 00:11:55 +00:00
event_args . push_back ( args . at ( 0 ) . completion ) ;
event_fire_generic ( L " fish_command_not_found " , & event_args ) ;
2012-11-19 00:30:30 +00:00
proc_set_last_status ( err = = ENOENT ? STATUS_UNKNOWN_COMMAND : STATUS_NOT_EXECUTABLE ) ;
}
}
2012-11-18 10:23:22 +00:00
}
2012-11-19 00:30:30 +00:00
if ( ( p - > type = = EXTERNAL ) & & ! use_command )
{
error ( SYNTAX_ERROR ,
tok_get_pos ( tok ) ,
UNKNOWN_BUILTIN_ERR_MSG ,
args . back ( ) . completion . c_str ( ) ) ;
}
2012-11-18 10:23:22 +00:00
}
2012-11-19 00:30:30 +00:00
if ( is_new_block )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
const wchar_t * end = parser_find_end ( tok_string ( tok ) +
current_tokenizer_pos ) ;
int make_sub_block = j - > first_process ! = p ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( ! end )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
error ( SYNTAX_ERROR ,
tok_get_pos ( tok ) ,
BLOCK_END_ERR_MSG ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
}
else
{
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( ! make_sub_block )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
int done = 0 ;
2012-11-18 10:23:22 +00:00
2012-11-22 01:48:35 +00:00
tokenizer_t subtok ( end , 0 ) ;
for ( ; ! done & & tok_has_next ( & subtok ) ; tok_next ( & subtok ) )
2012-11-19 00:30:30 +00:00
{
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
switch ( tok_last_type ( & subtok ) )
{
2012-11-19 08:31:03 +00:00
case TOK_END :
done = 1 ;
break ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
case TOK_REDIRECT_OUT :
case TOK_REDIRECT_NOCLOB :
case TOK_REDIRECT_APPEND :
case TOK_REDIRECT_IN :
case TOK_REDIRECT_FD :
case TOK_PIPE :
{
done = 1 ;
make_sub_block = 1 ;
break ;
}
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
case TOK_STRING :
{
break ;
}
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
default :
{
done = 1 ;
error ( SYNTAX_ERROR ,
current_tokenizer_pos ,
BLOCK_END_ERR_MSG ) ;
}
2012-11-19 00:30:30 +00:00
}
}
2012-11-18 10:23:22 +00:00
}
2012-11-19 00:30:30 +00:00
if ( make_sub_block )
{
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
long end_pos = end - tok_string ( tok ) ;
const wcstring sub_block ( tok_string ( tok ) + current_tokenizer_pos , end_pos - current_tokenizer_pos ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
p - > type = INTERNAL_BLOCK ;
args . at ( 0 ) = completion_t ( sub_block ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
tok_set_pos ( tok , ( int ) end_pos ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
while ( prev_block ! = current_block )
{
parser_t : : pop_block ( ) ;
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
}
else tok_next ( tok ) ;
2012-11-18 10:23:22 +00:00
}
}
2012-11-19 00:30:30 +00:00
else tok_next ( tok ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( ! error_code )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
if ( p - > type = = INTERNAL_BUILTIN & & parser_keywords_skip_arguments ( args . at ( 0 ) . completion ) )
{
if ( ! p - > get_argv ( ) )
2012-02-01 02:06:20 +00:00
p - > set_argv ( completions_to_wcstring_list ( args ) ) ;
2012-11-19 00:30:30 +00:00
}
else
{
parse_job_argument_list ( p , j , tok , args , unskip ) ;
}
2012-11-18 10:23:22 +00:00
}
2012-11-19 00:30:30 +00:00
if ( ! error_code )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
if ( ! is_new_block )
{
current_block - > had_command = true ;
}
2012-11-18 10:23:22 +00:00
}
2012-11-19 00:30:30 +00:00
if ( error_code )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
/*
Make sure the block stack is consistent
*/
while ( prev_block ! = current_block )
{
parser_t : : pop_block ( ) ;
}
2012-11-18 10:23:22 +00:00
}
2012-11-19 00:30:30 +00:00
current_tokenizer_pos = prev_tokenizer_pos ;
return ! error_code ;
2005-09-20 13:26:39 +00:00
}
/**
Do skipped execution of command . This means that only limited
execution of block level commands such as end and switch should be
preformed .
\ param j the job to execute
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
*/
2012-11-19 00:30:30 +00:00
void parser_t : : skipped_exec ( job_t * j )
2005-09-20 13:26:39 +00:00
{
2012-11-19 00:30:30 +00:00
process_t * p ;
2005-12-09 02:41:16 +00:00
2012-09-01 08:46:14 +00:00
/* Handle other skipped guys */
2012-11-19 00:30:30 +00:00
for ( p = j - > first_process ; p ; p = p - > next )
{
if ( p - > type = = INTERNAL_BUILTIN )
{
if ( ( wcscmp ( p - > argv0 ( ) , L " for " ) = = 0 ) | |
( wcscmp ( p - > argv0 ( ) , L " switch " ) = = 0 ) | |
( wcscmp ( p - > argv0 ( ) , L " begin " ) = = 0 ) | |
( wcscmp ( p - > argv0 ( ) , L " function " ) = = 0 ) )
{
this - > push_block ( new fake_block_t ( ) ) ;
}
else if ( wcscmp ( p - > argv0 ( ) , L " end " ) = = 0 )
{
if ( ! current_block - > outer - > skip )
{
exec ( * this , j ) ;
return ;
}
parser_t : : pop_block ( ) ;
}
else if ( wcscmp ( p - > argv0 ( ) , L " else " ) = = 0 )
{
if ( current_block - > type ( ) = = IF )
2012-09-01 08:46:14 +00:00
{
/* Evaluate this ELSE if the IF expression failed, and so has every ELSEIF (if any) expression thus far */
const if_block_t * ib = static_cast < const if_block_t * > ( current_block ) ;
if ( ib - > if_expr_evaluated & & ! ib - > any_branch_taken )
{
2012-11-19 00:30:30 +00:00
exec ( * this , j ) ;
2012-09-01 08:46:14 +00:00
return ;
}
}
}
2012-11-19 00:30:30 +00:00
else if ( wcscmp ( p - > argv0 ( ) , L " case " ) = = 0 )
{
if ( current_block - > type ( ) = = SWITCH )
{
exec ( * this , j ) ;
return ;
}
}
2012-11-18 10:23:22 +00:00
}
}
2012-11-19 00:30:30 +00:00
job_free ( j ) ;
2005-09-20 13:26:39 +00:00
}
2012-09-01 08:46:14 +00:00
/* Return whether we should skip the current block, if it is an elseif. */
static bool job_should_skip_elseif ( const job_t * job , const block_t * current_block )
{
if ( current_block - > type ( ) ! = IF )
{
/* Not an IF block, so just honor the skip property */
return current_block - > skip ;
}
else
{
/* We are an IF block */
const if_block_t * ib = static_cast < const if_block_t * > ( current_block ) ;
2012-11-18 10:23:22 +00:00
2012-09-01 08:46:14 +00:00
/* Execute this ELSEIF if the IF expression has been evaluated, it evaluated to false, and all ELSEIFs so far have evaluated to false. */
bool execute_elseif = ( ib - > if_expr_evaluated & & ! ib - > any_branch_taken ) ;
2012-11-18 10:23:22 +00:00
2012-09-01 08:46:14 +00:00
/* Invert the sense */
return ! execute_elseif ;
}
}
2005-09-20 13:26:39 +00:00
/**
Evaluates a job from the specified tokenizer . First calls
parse_job to parse the job and then calls exec to execute it .
\ param tok The tokenizer to read tokens from
*/
2006-01-30 19:53:10 +00:00
2012-11-22 01:48:35 +00:00
void parser_t : : eval_job ( tokenizer_t * tok )
2005-09-20 13:26:39 +00:00
{
2012-03-31 22:33:34 +00:00
ASSERT_IS_MAIN_THREAD ( ) ;
2012-11-19 00:30:30 +00:00
job_t * j ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
int start_pos = job_start_pos = tok_get_pos ( tok ) ;
long long t1 = 0 , t2 = 0 , t3 = 0 ;
2012-11-18 10:23:22 +00:00
2005-09-20 13:26:39 +00:00
2012-01-19 18:28:44 +00:00
profile_item_t * profile_item = NULL ;
2012-11-19 00:30:30 +00:00
bool skip = false ;
int job_begin_pos , prev_tokenizer_pos ;
const bool do_profile = profile ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( do_profile )
{
2013-01-13 23:49:32 +00:00
profile_item = new profile_item_t ( ) ;
2012-11-18 10:23:22 +00:00
profile_item - > skipped = 1 ;
2013-01-13 23:49:32 +00:00
profile_items . push_back ( profile_item ) ;
2012-11-19 00:30:30 +00:00
t1 = get_time ( ) ;
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
switch ( tok_last_type ( tok ) )
{
2012-11-19 08:31:03 +00:00
case TOK_STRING :
2012-11-18 10:23:22 +00:00
{
2012-11-19 08:31:03 +00:00
j = this - > job_create ( ) ;
job_set_flag ( j , JOB_FOREGROUND , 1 ) ;
job_set_flag ( j , JOB_TERMINAL , job_get_flag ( j , JOB_CONTROL ) ) ;
job_set_flag ( j , JOB_TERMINAL , job_get_flag ( j , JOB_CONTROL ) \
& & ( ! is_subshell & & ! is_event ) ) ;
job_set_flag ( j , JOB_SKIP_NOTIFICATION , is_subshell \
| | is_block \
| | is_event \
| | ( ! get_is_interactive ( ) ) ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
current_block - > job = j ;
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
if ( get_is_interactive ( ) )
2012-11-19 00:30:30 +00:00
{
2012-11-19 08:31:03 +00:00
if ( tcgetattr ( 0 , & j - > tmodes ) )
{
tok_next ( tok ) ;
wperror ( L " tcgetattr " ) ;
job_free ( j ) ;
break ;
}
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
j - > first_process = new process_t ( ) ;
job_begin_pos = tok_get_pos ( tok ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
if ( parse_job ( j - > first_process , j , tok ) & &
j - > first_process - > get_argv ( ) )
2012-11-19 00:30:30 +00:00
{
2012-11-19 08:31:03 +00:00
if ( job_start_pos < tok_get_pos ( tok ) )
2012-11-19 00:30:30 +00:00
{
2012-11-19 08:31:03 +00:00
long stop_pos = tok_get_pos ( tok ) ;
const wchar_t * newline = wcschr ( tok_string ( tok ) + start_pos , L ' \n ' ) ;
if ( newline )
stop_pos = mini < long > ( stop_pos , newline - tok_string ( tok ) ) ;
j - > set_command ( wcstring ( tok_string ( tok ) + start_pos , stop_pos - start_pos ) ) ;
2012-09-01 08:46:14 +00:00
}
2012-11-19 08:31:03 +00:00
else
j - > set_command ( L " " ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
if ( do_profile )
{
t2 = get_time ( ) ;
2013-01-14 00:33:40 +00:00
profile_item - > cmd = j - > command ( ) ;
2012-11-19 08:31:03 +00:00
profile_item - > skipped = current_block - > skip ;
}
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
/* If we're an ELSEIF, then we may want to unskip, if we're skipping because of an IF */
if ( job_get_flag ( j , JOB_ELSEIF ) )
{
bool skip_elseif = job_should_skip_elseif ( j , current_block ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
/* Record that we're entering an elseif */
if ( ! skip_elseif )
{
/* We must be an IF block here */
assert ( current_block - > type ( ) = = IF ) ;
static_cast < if_block_t * > ( current_block ) - > is_elseif_entry = true ;
}
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
/* Record that in the block too. This is similar to what builtin_else does. */
current_block - > skip = skip_elseif ;
}
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
skip = skip | | current_block - > skip ;
skip = skip | | job_get_flag ( j , JOB_WILDCARD_ERROR ) ;
skip = skip | | job_get_flag ( j , JOB_SKIP ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
if ( ! skip )
2012-11-19 00:30:30 +00:00
{
2012-11-19 08:31:03 +00:00
int was_builtin = 0 ;
if ( j - > first_process - > type = = INTERNAL_BUILTIN & & ! j - > first_process - > next )
was_builtin = 1 ;
prev_tokenizer_pos = current_tokenizer_pos ;
current_tokenizer_pos = job_begin_pos ;
exec ( * this , j ) ;
current_tokenizer_pos = prev_tokenizer_pos ;
/* Only external commands require a new fishd barrier */
if ( ! was_builtin )
set_proc_had_barrier ( false ) ;
2012-11-19 00:30:30 +00:00
}
2012-11-19 08:31:03 +00:00
else
{
this - > skipped_exec ( j ) ;
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
if ( do_profile )
2012-11-19 00:30:30 +00:00
{
2012-11-19 08:31:03 +00:00
t3 = get_time ( ) ;
profile_item - > level = eval_level ;
profile_item - > parse = ( int ) ( t2 - t1 ) ;
profile_item - > exec = ( int ) ( t3 - t2 ) ;
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
if ( current_block - > type ( ) = = WHILE )
{
while_block_t * wb = static_cast < while_block_t * > ( current_block ) ;
switch ( wb - > status )
{
case WHILE_TEST_FIRST :
{
// PCA I added the 'wb->skip ||' part because we couldn't reliably
// control-C out of loops like this: while test 1 -eq 1; end
wb - > skip = wb - > skip | | proc_get_last_status ( ) ! = 0 ;
wb - > status = WHILE_TESTED ;
}
break ;
}
2012-11-19 00:30:30 +00:00
}
2012-11-19 08:31:03 +00:00
if ( current_block - > type ( ) = = IF )
2012-11-19 00:30:30 +00:00
{
2012-11-19 08:31:03 +00:00
if_block_t * ib = static_cast < if_block_t * > ( current_block ) ;
if ( ib - > skip )
{
/* Nothing */
}
else if ( ! ib - > if_expr_evaluated )
{
/* Execute the IF */
bool if_result = ( proc_get_last_status ( ) = = 0 ) ;
ib - > any_branch_taken = if_result ;
/* Don't execute if the expression failed */
current_block - > skip = ! if_result ;
ib - > if_expr_evaluated = true ;
}
else if ( ib - > is_elseif_entry & & ! ib - > any_branch_taken )
{
/* Maybe mark an ELSEIF branch as taken */
bool elseif_taken = ( proc_get_last_status ( ) = = 0 ) ;
ib - > any_branch_taken = elseif_taken ;
current_block - > skip = ! elseif_taken ;
ib - > is_elseif_entry = false ;
}
2012-11-19 00:30:30 +00:00
}
2012-11-19 08:31:03 +00:00
2012-11-19 00:30:30 +00:00
}
2012-11-19 08:31:03 +00:00
else
{
/*
This job could not be properly parsed . We free it
instead , and set the status to 1. This should be
rare , since most errors should be detected by the
ahead of time validator .
*/
job_free ( j ) ;
2006-01-30 19:53:10 +00:00
2012-11-19 08:31:03 +00:00
proc_set_last_status ( 1 ) ;
}
current_block - > job = 0 ;
break ;
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
case TOK_END :
{
if ( tok_has_next ( tok ) )
tok_next ( tok ) ;
break ;
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
case TOK_BACKGROUND :
2012-11-19 00:30:30 +00:00
{
2012-11-19 08:31:03 +00:00
const wchar_t * str = tok_string ( tok ) ;
if ( tok_get_pos ( tok ) > 0 & & str [ tok_get_pos ( tok ) - 1 ] = = L ' & ' )
{
error ( SYNTAX_ERROR ,
tok_get_pos ( tok ) ,
CMD_AND_ERR_MSG ,
tok_get_desc ( tok_last_type ( tok ) ) ) ;
}
else
{
error ( SYNTAX_ERROR ,
tok_get_pos ( tok ) ,
CMD_ERR_MSG ,
tok_get_desc ( tok_last_type ( tok ) ) ) ;
}
return ;
2012-11-19 00:30:30 +00:00
}
2012-11-19 08:31:03 +00:00
case TOK_ERROR :
2012-11-19 00:30:30 +00:00
{
error ( SYNTAX_ERROR ,
tok_get_pos ( tok ) ,
2012-11-19 08:31:03 +00:00
TOK_ERR_MSG ,
tok_last ( tok ) ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
return ;
}
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
default :
{
error ( SYNTAX_ERROR ,
tok_get_pos ( tok ) ,
CMD_ERR_MSG ,
tok_get_desc ( tok_last_type ( tok ) ) ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
return ;
}
2012-11-18 10:23:22 +00:00
}
2012-11-19 00:30:30 +00:00
job_reap ( 0 ) ;
2012-11-18 10:23:22 +00:00
}
2005-09-20 13:26:39 +00:00
2012-11-19 00:30:30 +00:00
int parser_t : : eval ( const wcstring & cmdStr , const io_chain_t & io , enum block_type_t block_type )
2005-09-20 13:26:39 +00:00
{
2012-01-23 04:47:13 +00:00
const wchar_t * const cmd = cmdStr . c_str ( ) ;
2012-11-19 00:30:30 +00:00
size_t forbid_count ;
int code ;
2012-11-22 01:48:35 +00:00
tokenizer_t * previous_tokenizer = current_tokenizer ;
2012-11-19 00:30:30 +00:00
block_t * start_current_block = current_block ;
2012-11-18 10:23:22 +00:00
2012-08-15 07:57:56 +00:00
/* Record the current chain so we can put it back later */
const io_chain_t prev_io = block_io ;
block_io = io ;
2012-11-18 10:23:22 +00:00
2011-12-27 06:51:34 +00:00
std : : vector < wcstring > prev_forbidden = forbidden_function ;
2006-01-30 16:51:50 +00:00
2012-11-19 00:30:30 +00:00
if ( block_type = = SUBST )
{
forbidden_function . clear ( ) ;
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
CHECK_BLOCK ( 1 ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
forbid_count = forbidden_function . size ( ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
block_io = io ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
job_reap ( 0 ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
debug ( 4 , L " eval: %ls " , cmd ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( ! cmd )
{
debug ( 1 ,
EVAL_NULL_ERR_MSG ) ;
bugreport ( ) ;
return 1 ;
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( ( block_type ! = TOP ) & &
( block_type ! = SUBST ) )
{
debug ( 1 ,
INVALID_SCOPE_ERR_MSG ,
parser_t : : get_block_desc ( block_type ) ) ;
bugreport ( ) ;
return 1 ;
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
eval_level + + ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
this - > push_block ( new scope_block_t ( block_type ) ) ;
2012-11-18 10:23:22 +00:00
2012-11-22 01:48:35 +00:00
current_tokenizer = new tokenizer_t ( cmd , 0 ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
error_code = 0 ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
event_fire ( NULL ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
while ( tok_has_next ( current_tokenizer ) & &
! error_code & &
! sanity_check ( ) & &
! exit_status ( ) )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
this - > eval_job ( current_tokenizer ) ;
event_fire ( NULL ) ;
2012-11-18 10:23:22 +00:00
}
2012-11-19 00:30:30 +00:00
parser_t : : pop_block ( ) ;
while ( start_current_block ! = current_block )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
if ( current_block = = 0 )
{
debug ( 0 ,
_ ( L " End of block mismatch. Program terminating. " ) ) ;
bugreport ( ) ;
FATAL_EXIT ( ) ;
break ;
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( ( ! error_code ) & & ( ! exit_status ( ) ) & & ( ! proc_get_last_status ( ) ) )
{
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
//debug( 2, L"Status %d\n", proc_get_last_status() );
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
debug ( 1 ,
L " %ls " , parser_t : : get_block_desc ( current_block - > type ( ) ) ) ;
debug ( 1 ,
BLOCK_END_ERR_MSG ) ;
fwprintf ( stderr , L " %ls " , parser_t : : current_line ( ) ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
const wcstring h = builtin_help_get ( * this , L " end " ) ;
if ( h . size ( ) )
fwprintf ( stderr , L " %ls " , h . c_str ( ) ) ;
break ;
}
parser_t : : pop_block ( ) ;
2012-11-18 10:23:22 +00:00
}
2012-11-19 00:30:30 +00:00
this - > print_errors_stderr ( ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
delete current_tokenizer ;
2005-09-20 13:26:39 +00:00
2011-12-27 06:51:34 +00:00
while ( forbidden_function . size ( ) > forbid_count )
2012-11-19 00:30:30 +00:00
parser_t : : allow_function ( ) ;
2005-09-20 13:26:39 +00:00
2012-11-19 00:30:30 +00:00
/*
Restore previous eval state
*/
forbidden_function = prev_forbidden ;
current_tokenizer = previous_tokenizer ;
block_io = prev_io ;
eval_level - - ;
2005-09-20 13:26:39 +00:00
2012-11-19 00:30:30 +00:00
code = error_code ;
error_code = 0 ;
2005-09-20 13:26:39 +00:00
2012-11-19 00:30:30 +00:00
job_reap ( 0 ) ;
2006-01-30 16:51:50 +00:00
2012-11-19 00:30:30 +00:00
return code ;
2005-09-20 13:26:39 +00:00
}
2006-06-16 12:56:16 +00:00
2006-06-08 23:55:57 +00:00
/**
\ return the block type created by the specified builtin , or - 1 on error .
*/
2012-11-19 00:30:30 +00:00
block_type_t parser_get_block_type ( const wcstring & cmd )
2006-05-26 11:25:25 +00:00
{
2013-01-24 22:59:52 +00:00
for ( size_t i = 0 ; block_lookup [ i ] . desc ; i + + )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
if ( block_lookup [ i ] . name & & cmd = = block_lookup [ i ] . name )
{
return block_lookup [ i ] . type ;
}
2012-11-18 10:23:22 +00:00
}
2012-11-19 00:30:30 +00:00
return ( block_type_t ) - 1 ;
2006-06-16 12:56:16 +00:00
}
/**
2006-08-22 14:38:31 +00:00
\ return the block command that createa the specified block type , or null on error .
2006-06-16 12:56:16 +00:00
*/
2012-11-19 00:30:30 +00:00
const wchar_t * parser_get_block_command ( int type )
2006-06-16 12:56:16 +00:00
{
2013-01-24 22:59:52 +00:00
for ( size_t i = 0 ; block_lookup [ i ] . desc ; i + + )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
if ( block_lookup [ i ] . type = = type )
{
return block_lookup [ i ] . name ;
}
2012-11-18 10:23:22 +00:00
}
2013-01-24 22:59:52 +00:00
return NULL ;
2006-05-26 11:25:25 +00:00
}
2006-06-08 23:55:57 +00:00
/**
Test if this argument contains any errors . Detected errors include
2006-07-21 01:08:31 +00:00
syntax errors in command substitutions , improperly escaped
2006-06-08 23:55:57 +00:00
characters and improper use of the variable expansion operator .
*/
2012-11-19 00:30:30 +00:00
int parser_t : : parser_test_argument ( const wchar_t * arg , wcstring * out , const wchar_t * prefix , int offset )
2006-05-21 19:25:24 +00:00
{
2012-11-19 00:30:30 +00:00
wchar_t * unesc ;
wchar_t * pos ;
int err = 0 ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
wchar_t * paran_begin , * paran_end ;
wchar_t * arg_cpy ;
int do_loop = 1 ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
CHECK ( arg , 1 ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
arg_cpy = wcsdup ( arg ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
while ( do_loop )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
switch ( parse_util_locate_cmdsubst ( arg_cpy ,
& paran_begin ,
& paran_end ,
0 ) )
2012-11-18 10:23:22 +00:00
{
2012-11-19 08:31:03 +00:00
case - 1 :
err = 1 ;
if ( out )
{
error ( SYNTAX_ERROR ,
offset ,
L " Mismatched parenthesis " ) ;
this - > print_errors ( * out , prefix ) ;
}
free ( arg_cpy ) ;
return err ;
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
case 0 :
do_loop = 0 ;
break ;
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
case 1 :
{
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
wchar_t * subst = wcsndup ( paran_begin + 1 , paran_end - paran_begin - 1 ) ;
wcstring tmp ;
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
tmp . append ( arg_cpy , paran_begin - arg_cpy ) ;
tmp . push_back ( INTERNAL_SEPARATOR ) ;
tmp . append ( paran_end + 1 ) ;
2012-11-18 10:23:22 +00:00
// debug( 1, L"%ls -> %ls %ls", arg_cpy, subst, tmp.buff );
2012-11-19 08:31:03 +00:00
err | = parser_t : : test ( subst , 0 , out , prefix ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
free ( subst ) ;
free ( arg_cpy ) ;
arg_cpy = wcsdup ( tmp . c_str ( ) ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
/*
Do _not_ call sb_destroy on this stringbuffer - it ' s
buffer is used as the new ' arg_cpy ' . It is free ' d at
the end of the loop .
*/
break ;
}
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
}
2012-11-19 00:30:30 +00:00
unesc = unescape ( arg_cpy , 1 ) ;
if ( ! unesc )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
if ( out )
{
error ( SYNTAX_ERROR ,
offset ,
L " Invalid token '%ls' " , arg_cpy ) ;
print_errors ( * out , prefix ) ;
}
return 1 ;
2012-11-18 10:23:22 +00:00
}
2012-11-19 00:30:30 +00:00
else
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
/*
Check for invalid variable expansions
*/
for ( pos = unesc ; * pos ; pos + + )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
switch ( * pos )
2012-11-18 10:23:22 +00:00
{
2012-11-19 08:31:03 +00:00
case VARIABLE_EXPAND :
case VARIABLE_EXPAND_SINGLE :
2012-11-19 00:30:30 +00:00
{
2012-11-19 08:31:03 +00:00
wchar_t n = * ( pos + 1 ) ;
if ( n ! = VARIABLE_EXPAND & &
n ! = VARIABLE_EXPAND_SINGLE & &
! wcsvarchr ( n ) )
2012-11-19 00:30:30 +00:00
{
2012-11-19 08:31:03 +00:00
err = 1 ;
if ( out )
{
expand_variable_error ( * this , unesc , pos - unesc , offset ) ;
print_errors ( * out , prefix ) ;
}
2012-11-19 00:30:30 +00:00
}
2012-11-19 08:31:03 +00:00
break ;
}
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
}
}
2012-11-19 00:30:30 +00:00
free ( arg_cpy ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
free ( unesc ) ;
return err ;
2012-11-18 10:23:22 +00:00
2006-05-21 19:25:24 +00:00
}
2012-11-19 00:30:30 +00:00
int parser_t : : test_args ( const wchar_t * buff , wcstring * out , const wchar_t * prefix )
2006-05-21 19:25:24 +00:00
{
2012-11-22 01:48:35 +00:00
tokenizer_t * const previous_tokenizer = current_tokenizer ;
const int previous_pos = current_tokenizer_pos ;
2012-11-19 00:30:30 +00:00
int do_loop = 1 ;
int err = 0 ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
CHECK ( buff , 1 ) ;
2012-11-18 10:23:22 +00:00
2012-11-22 01:48:35 +00:00
tokenizer_t tok ( buff , 0 ) ;
current_tokenizer = & tok ;
for ( ; do_loop & & tok_has_next ( & tok ) ; tok_next ( & tok ) )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
current_tokenizer_pos = tok_get_pos ( & tok ) ;
switch ( tok_last_type ( & tok ) )
{
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
case TOK_STRING :
{
err | = parser_test_argument ( tok_last ( & tok ) , out , prefix , tok_get_pos ( & tok ) ) ;
break ;
}
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
case TOK_END :
{
break ;
}
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
case TOK_ERROR :
2012-11-19 00:30:30 +00:00
{
2012-11-19 08:31:03 +00:00
if ( out )
{
error ( SYNTAX_ERROR ,
tok_get_pos ( & tok ) ,
TOK_ERR_MSG ,
tok_last ( & tok ) ) ;
print_errors ( * out , prefix ) ;
}
err = 1 ;
do_loop = 0 ;
break ;
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +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
if ( out )
{
error ( SYNTAX_ERROR ,
tok_get_pos ( & tok ) ,
UNEXPECTED_TOKEN_ERR_MSG ,
tok_get_desc ( tok_last_type ( & tok ) ) ) ;
print_errors ( * out , prefix ) ;
}
err = 1 ;
do_loop = 0 ;
break ;
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
}
}
2012-11-22 01:48:35 +00:00
current_tokenizer = previous_tokenizer ;
2012-11-19 00:30:30 +00:00
current_tokenizer_pos = previous_pos ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
error_code = 0 ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
return err ;
2006-05-21 19:25:24 +00:00
}
2013-01-24 22:59:52 +00:00
// helper type used in parser::test below
struct block_info_t {
int position ; //tokenizer position
block_type_t type ; //type of the block
int indentation ; //indentation associated with the block
bool has_had_case ; //if we are a switch, whether we've encountered a case
} ;
int parser_t : : test ( const wchar_t * buff , int * block_level , wcstring * out , const wchar_t * prefix )
2005-09-20 13:26:39 +00:00
{
2011-12-27 03:18:46 +00:00
ASSERT_IS_MAIN_THREAD ( ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
/*
Set to one if a command name has been given for the currently
parsed process specification
*/
int had_cmd = 0 ;
int err = 0 ;
int unfinished = 0 ;
2012-11-18 10:23:22 +00:00
2012-11-22 01:48:35 +00:00
tokenizer_t * const previous_tokenizer = current_tokenizer ;
const int previous_pos = current_tokenizer_pos ;
2013-01-24 22:59:52 +00:00
// These are very nearly stacks, but sometimes we have to inspect non-top elements (e.g. return)
std : : vector < struct block_info_t > block_infos ;
int indentation_sum = 0 ; //sum of indentation in block_infos
2012-11-19 00:30:30 +00:00
int res = 0 ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
/*
Set to 1 if the current command is inside a pipeline
*/
int is_pipeline = 0 ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
/*
Set to one if the currently specified process can not be used inside a pipeline
*/
int forbid_pipeline = 0 ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
/*
Set to one if an additional process specification is needed
*/
bool needs_cmd = false ;
/*
Counter on the number of arguments this function has encountered
so far . Is set to - 1 when the count is unknown , i . e . after
encountering an argument that contains substitutions that can
expand to more / less arguemtns then 1.
*/
int arg_count = 0 ;
/*
The currently validated command .
*/
wcstring command ;
bool has_command = false ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
CHECK ( buff , 1 ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( block_level )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
size_t len = wcslen ( buff ) ;
for ( size_t i = 0 ; i < len ; i + + )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
block_level [ i ] = - 1 ;
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
2012-11-22 01:48:35 +00:00
tokenizer_t tok ( buff , 0 ) ;
2012-11-19 00:30:30 +00:00
current_tokenizer = & tok ;
2012-11-18 10:23:22 +00:00
2012-11-22 01:48:35 +00:00
for ( ; ; tok_next ( & tok ) )
2012-11-19 00:30:30 +00:00
{
current_tokenizer_pos = tok_get_pos ( & tok ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
int last_type = tok_last_type ( & tok ) ;
int end_of_cmd = 0 ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
switch ( last_type )
{
2012-11-19 08:31:03 +00:00
case TOK_STRING :
2012-11-18 10:23:22 +00:00
{
2012-11-19 08:31:03 +00:00
if ( ! had_cmd )
2012-11-19 00:30:30 +00:00
{
2012-11-19 08:31:03 +00:00
int mark = tok_get_pos ( & tok ) ;
had_cmd = 1 ;
arg_count = 0 ;
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
command = tok_last ( & tok ) ;
2013-01-12 20:55:23 +00:00
2013-01-12 20:53:40 +00:00
// Pass SKIP_HOME_DIRECTORIES for https://github.com/fish-shell/fish-shell/issues/512
has_command = expand_one ( command , EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_VARIABLES | EXPAND_SKIP_HOME_DIRECTORIES ) ;
if ( ! has_command )
2012-11-19 00:30:30 +00:00
{
2012-11-19 08:31:03 +00:00
command = L " " ;
2012-11-19 00:30:30 +00:00
err = 1 ;
if ( out )
{
error ( SYNTAX_ERROR ,
tok_get_pos ( & tok ) ,
2012-11-19 08:31:03 +00:00
ILLEGAL_CMD_ERR_MSG ,
tok_last ( & tok ) ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
print_errors ( * out , prefix ) ;
}
2012-11-19 08:31:03 +00:00
break ;
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
if ( needs_cmd )
2012-11-19 00:30:30 +00:00
{
2012-11-19 08:31:03 +00:00
/*
end is not a valid command when a followup
command is needed , such as after ' and ' or
' while '
*/
if ( contains ( command ,
L " end " ) )
2012-11-19 00:30:30 +00:00
{
2012-11-19 08:31:03 +00:00
err = 1 ;
if ( out )
{
error ( SYNTAX_ERROR ,
tok_get_pos ( & tok ) ,
COND_ERR_MSG ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
print_errors ( * out , prefix ) ;
}
2012-11-19 00:30:30 +00:00
}
2012-11-19 08:31:03 +00:00
needs_cmd = false ;
2012-11-19 00:30:30 +00:00
}
2012-11-19 08:31:03 +00:00
/*
Decrement block count on end command
*/
if ( command = = L " end " )
2012-11-19 00:30:30 +00:00
{
tok_next ( & tok ) ;
tok_set_pos ( & tok , mark ) ;
2013-01-24 22:59:52 +00:00
/* Test that end is not used when not inside any block */
if ( block_infos . empty ( ) )
{
err = 1 ;
if ( out )
{
error ( SYNTAX_ERROR ,
tok_get_pos ( & tok ) ,
INVALID_END_ERR_MSG ) ;
print_errors ( * out , prefix ) ;
const wcstring h = builtin_help_get ( * this , L " end " ) ;
if ( ! h . empty ( ) )
append_format ( * out , L " %ls " , h . c_str ( ) ) ;
}
}
else
{
indentation_sum - = block_infos . back ( ) . indentation ;
block_infos . pop_back ( ) ;
}
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
/*
2012-11-19 08:31:03 +00:00
Store the block level . This needs to be done
_after_ checking for end commands , but _before_
checking for block opening commands .
2012-11-19 00:30:30 +00:00
*/
2013-01-24 22:59:52 +00:00
if ( block_level ! = NULL )
2012-11-19 00:30:30 +00:00
{
2013-01-24 22:59:52 +00:00
int indentation_adjust = 0 ;
if ( command = = L " else " )
{
// if or else if goes back
indentation_adjust = - 1 ;
}
else if ( command = = L " case " )
{
if ( ! block_infos . empty ( ) & & block_infos . back ( ) . type = = SWITCH )
{
// mark that we've encountered a case, and increase the indentation
// by doing this now, we avoid overly indenting the first case as the user types it
if ( ! block_infos . back ( ) . has_had_case )
{
block_infos . back ( ) . has_had_case = true ;
block_infos . back ( ) . indentation + = 1 ;
indentation_sum + = 1 ;
}
// unindent this case
indentation_adjust = - 1 ;
}
}
block_level [ tok_get_pos ( & tok ) ] = indentation_sum + indentation_adjust ;
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
/*
Handle block commands
*/
if ( parser_keywords_is_block ( command ) )
2012-11-19 00:30:30 +00:00
{
2013-01-24 22:59:52 +00:00
struct block_info_t info = { current_tokenizer_pos , parser_get_block_type ( command ) , 1 /* indent */ } ;
block_infos . push_back ( info ) ;
indentation_sum + = info . indentation ;
tok_next ( & tok ) ;
tok_set_pos ( & tok , mark ) ;
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
/*
If parser_keywords_is_subcommand is true , the command
accepts a second command as it ' s first
argument . If parser_skip_arguments is true , the
second argument is optional .
*/
if ( parser_keywords_is_subcommand ( command ) & & ! parser_keywords_skip_arguments ( command ) )
2012-11-19 00:30:30 +00:00
{
2012-11-19 08:31:03 +00:00
needs_cmd = true ;
had_cmd = 0 ;
2012-11-19 00:30:30 +00:00
}
2006-01-30 19:53:10 +00:00
2012-11-19 08:31:03 +00:00
if ( contains ( command ,
L " or " ,
L " and " ) )
2012-11-19 00:30:30 +00:00
{
/*
2012-11-19 08:31:03 +00:00
' or ' and ' and ' can not be used inside pipelines
2012-11-19 00:30:30 +00:00
*/
2012-11-19 08:31:03 +00:00
if ( is_pipeline )
2012-11-19 00:30:30 +00:00
{
2012-11-19 08:31:03 +00:00
err = 1 ;
if ( out )
2012-11-19 00:30:30 +00:00
{
2012-11-19 08:31:03 +00:00
error ( SYNTAX_ERROR ,
tok_get_pos ( & tok ) ,
EXEC_ERR_MSG ) ;
print_errors ( * out , prefix ) ;
2012-11-19 00:30:30 +00:00
}
}
2012-11-19 08:31:03 +00:00
}
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
/*
There are a lot of situations where pipelines
are forbidden , including when using the exec
builtin .
*/
if ( parser_is_pipe_forbidden ( command ) )
{
if ( is_pipeline )
2012-11-19 00:30:30 +00:00
{
err = 1 ;
if ( out )
{
error ( SYNTAX_ERROR ,
tok_get_pos ( & tok ) ,
2012-11-19 08:31:03 +00:00
EXEC_ERR_MSG ) ;
2012-11-19 00:30:30 +00:00
print_errors ( * out , prefix ) ;
2012-11-19 08:31:03 +00:00
2012-11-19 00:30:30 +00:00
}
}
2012-11-19 08:31:03 +00:00
forbid_pipeline = 1 ;
2012-11-19 00:30:30 +00:00
}
2012-11-19 08:31:03 +00:00
/*
Test that the case builtin is only used directly in a switch block
*/
if ( command = = L " case " )
2012-11-19 00:30:30 +00:00
{
2013-01-24 22:59:52 +00:00
if ( block_infos . empty ( ) | | block_infos . back ( ) . type ! = SWITCH )
2012-11-19 00:30:30 +00:00
{
2012-11-19 08:31:03 +00:00
err = 1 ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
if ( out )
{
error ( SYNTAX_ERROR ,
tok_get_pos ( & tok ) ,
INVALID_CASE_ERR_MSG ) ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
print_errors ( * out , prefix ) ;
const wcstring h = builtin_help_get ( * this , L " case " ) ;
if ( h . size ( ) )
append_format ( * out , L " %ls " , h . c_str ( ) ) ;
}
}
}
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
/*
Test that the return bultin is only used within function definitions
*/
if ( command = = L " return " )
{
2013-01-24 22:59:52 +00:00
bool found_func = false ;
size_t block_idx = block_infos . size ( ) ;
while ( block_idx - - )
2012-11-19 00:30:30 +00:00
{
2013-01-24 22:59:52 +00:00
if ( block_infos . at ( block_idx ) . type = = FUNCTION_DEF )
2012-11-19 00:30:30 +00:00
{
2013-01-24 22:59:52 +00:00
found_func = true ;
2012-11-19 08:31:03 +00:00
break ;
2012-11-19 00:30:30 +00:00
}
}
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
if ( ! found_func )
{
/*
Peek to see if the next argument is
- - help , in which case we ' ll allow it to
show the help .
*/
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
int old_pos = tok_get_pos ( & tok ) ;
int is_help = 0 ;
tok_next ( & tok ) ;
if ( tok_last_type ( & tok ) = = TOK_STRING )
{
wcstring first_arg = tok_last ( & tok ) ;
if ( expand_one ( first_arg , EXPAND_SKIP_CMDSUBST ) & & parser_t : : is_help ( first_arg . c_str ( ) , 3 ) )
{
is_help = 1 ;
}
}
tok_set_pos ( & tok , old_pos ) ;
if ( ! is_help )
{
err = 1 ;
if ( out )
{
error ( SYNTAX_ERROR ,
tok_get_pos ( & tok ) ,
INVALID_RETURN_ERR_MSG ) ;
print_errors ( * out , prefix ) ;
}
}
}
}
/*
Test that break and continue are only used within loop blocks
*/
if ( contains ( command , L " break " , L " continue " ) )
{
2013-01-24 22:59:52 +00:00
bool found_loop = false ;
size_t block_idx = block_infos . size ( ) ;
while ( block_idx - - )
2012-11-19 00:30:30 +00:00
{
2013-01-24 22:59:52 +00:00
block_type_t type = block_infos . at ( block_idx ) . type ;
if ( type = = WHILE | | type = = FOR )
2012-11-19 08:31:03 +00:00
{
2013-01-24 22:59:52 +00:00
found_loop = true ;
2012-11-19 08:31:03 +00:00
break ;
}
}
if ( ! found_loop )
{
/*
Peek to see if the next argument is
- - help , in which case we ' ll allow it to
show the help .
*/
int old_pos = tok_get_pos ( & tok ) ;
int is_help = 0 ;
tok_next ( & tok ) ;
if ( tok_last_type ( & tok ) = = TOK_STRING )
{
wcstring first_arg = tok_last ( & tok ) ;
if ( expand_one ( first_arg , EXPAND_SKIP_CMDSUBST ) & & parser_t : : is_help ( first_arg . c_str ( ) , 3 ) )
{
is_help = 1 ;
}
}
tok_set_pos ( & tok , old_pos ) ;
if ( ! is_help )
{
err = 1 ;
if ( out )
{
error ( SYNTAX_ERROR ,
tok_get_pos ( & tok ) ,
INVALID_LOOP_ERR_MSG ) ;
print_errors ( * out , prefix ) ;
}
}
}
}
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
/*
Test that else and else - if are only used directly in an if - block
*/
if ( command = = L " else " )
{
2013-01-24 22:59:52 +00:00
if ( block_infos . empty ( ) | | block_infos . back ( ) . type ! = IF )
2012-11-19 08:31:03 +00:00
{
err = 1 ;
2012-11-19 00:30:30 +00:00
if ( out )
{
error ( SYNTAX_ERROR ,
tok_get_pos ( & tok ) ,
2012-11-19 08:31:03 +00:00
INVALID_ELSE_ERR_MSG ,
command . c_str ( ) ) ;
2012-11-19 00:30:30 +00:00
print_errors ( * out , prefix ) ;
}
}
}
}
else
{
2012-11-19 08:31:03 +00:00
err | = parser_test_argument ( tok_last ( & tok ) , out , prefix , tok_get_pos ( & tok ) ) ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
/* If possible, keep track of number of supplied arguments */
if ( arg_count > = 0 & & expand_is_clean ( tok_last ( & tok ) ) )
{
arg_count + + ;
}
else
{
arg_count = - 1 ;
}
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
if ( has_command )
2012-11-19 00:30:30 +00:00
{
2012-11-19 08:31:03 +00:00
/*
Try to make sure the second argument to ' for ' is ' in '
*/
if ( command = = L " for " )
{
if ( arg_count = = 1 )
2012-09-03 20:24:01 +00:00
{
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
if ( wcsvarname ( tok_last ( & tok ) ) )
2012-09-03 20:24:01 +00:00
{
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
err = 1 ;
if ( out )
{
error ( SYNTAX_ERROR ,
tok_get_pos ( & tok ) ,
BUILTIN_FOR_ERR_NAME ,
L " for " ,
tok_last ( & tok ) ) ;
print_errors ( * out , prefix ) ;
}
2012-09-03 20:24:01 +00:00
}
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
}
else if ( arg_count = = 2 )
2012-11-19 00:30:30 +00:00
{
2012-11-19 08:31:03 +00:00
if ( wcscmp ( tok_last ( & tok ) , L " in " ) ! = 0 )
2012-11-19 00:30:30 +00:00
{
2012-11-19 08:31:03 +00:00
err = 1 ;
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
if ( out )
{
error ( SYNTAX_ERROR ,
tok_get_pos ( & tok ) ,
BUILTIN_FOR_ERR_IN ,
L " for " ) ;
print_errors ( * out , prefix ) ;
}
2012-11-19 00:30:30 +00:00
}
}
}
2012-11-19 08:31:03 +00:00
else if ( command = = L " else " )
2012-11-19 00:30:30 +00:00
{
2012-11-19 08:31:03 +00:00
if ( arg_count = = 1 )
2012-11-19 00:30:30 +00:00
{
2012-11-19 08:31:03 +00:00
/* Any second argument must be "if" */
if ( wcscmp ( tok_last ( & tok ) , L " if " ) ! = 0 )
2012-11-19 00:30:30 +00:00
{
2012-11-19 08:31:03 +00:00
err = 1 ;
if ( out )
{
error ( SYNTAX_ERROR ,
tok_get_pos ( & tok ) ,
BUILTIN_ELSEIF_ERR_ARGUMENT ,
L " else " ) ;
print_errors ( * out , prefix ) ;
}
}
else
{
/* Successfully detected "else if". Now we need a new command. */
needs_cmd = true ;
had_cmd = false ;
2012-11-19 00:30:30 +00:00
}
}
}
}
}
2012-11-19 08:31:03 +00:00
break ;
2012-11-18 10:23:22 +00:00
}
2012-11-19 08:31:03 +00:00
case TOK_REDIRECT_OUT :
case TOK_REDIRECT_IN :
case TOK_REDIRECT_APPEND :
case TOK_REDIRECT_FD :
case TOK_REDIRECT_NOCLOB :
2012-11-19 00:30:30 +00:00
{
2012-11-19 08:31:03 +00:00
if ( ! had_cmd )
2012-11-19 00:30:30 +00:00
{
2012-11-19 08:31:03 +00:00
err = 1 ;
if ( out )
2012-11-19 00:30:30 +00:00
{
error ( SYNTAX_ERROR ,
tok_get_pos ( & tok ) ,
2012-11-19 08:31:03 +00:00
INVALID_REDIRECTION_ERR_MSG ) ;
print_errors ( * out , prefix ) ;
2012-11-19 00:30:30 +00:00
}
2012-11-19 08:31:03 +00:00
}
break ;
}
case TOK_END :
{
if ( needs_cmd & & ! had_cmd )
{
err = 1 ;
if ( out )
2012-11-19 00:30:30 +00:00
{
error ( SYNTAX_ERROR ,
tok_get_pos ( & tok ) ,
CMD_ERR_MSG ,
tok_get_desc ( tok_last_type ( & tok ) ) ) ;
2012-11-19 08:31:03 +00:00
print_errors ( * out , prefix ) ;
2012-11-19 00:30:30 +00:00
}
}
2012-11-19 08:31:03 +00:00
needs_cmd = false ;
had_cmd = 0 ;
is_pipeline = 0 ;
forbid_pipeline = 0 ;
2012-11-19 00:30:30 +00:00
end_of_cmd = 1 ;
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
break ;
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
case TOK_PIPE :
2012-11-19 00:30:30 +00:00
{
2012-11-19 08:31:03 +00:00
if ( ! had_cmd )
2012-11-19 00:30:30 +00:00
{
2012-11-19 08:31:03 +00:00
err = 1 ;
if ( out )
2012-11-19 00:30:30 +00:00
{
2012-11-19 08:31:03 +00:00
if ( tok_get_pos ( & tok ) > 0 & & buff [ tok_get_pos ( & tok ) - 1 ] = = L ' | ' )
{
error ( SYNTAX_ERROR ,
tok_get_pos ( & tok ) ,
CMD_OR_ERR_MSG ,
tok_get_desc ( tok_last_type ( & tok ) ) ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
}
else
{
error ( SYNTAX_ERROR ,
tok_get_pos ( & tok ) ,
CMD_ERR_MSG ,
tok_get_desc ( tok_last_type ( & tok ) ) ) ;
}
print_errors ( * out , prefix ) ;
2012-11-19 00:30:30 +00:00
}
2012-11-19 08:31:03 +00:00
}
else if ( forbid_pipeline )
{
err = 1 ;
if ( out )
2012-11-19 00:30:30 +00:00
{
error ( SYNTAX_ERROR ,
tok_get_pos ( & tok ) ,
2012-11-19 08:31:03 +00:00
EXEC_ERR_MSG ) ;
print_errors ( * out , prefix ) ;
2012-11-19 00:30:30 +00:00
}
2012-11-19 08:31:03 +00:00
}
else
{
needs_cmd = true ;
is_pipeline = 1 ;
had_cmd = 0 ;
end_of_cmd = 1 ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
}
2012-11-19 08:31:03 +00:00
break ;
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
case TOK_BACKGROUND :
{
if ( ! had_cmd )
{
err = 1 ;
if ( out )
{
if ( tok_get_pos ( & tok ) > 0 & & buff [ tok_get_pos ( & tok ) - 1 ] = = L ' & ' )
{
error ( SYNTAX_ERROR ,
tok_get_pos ( & tok ) ,
CMD_AND_ERR_MSG ,
tok_get_desc ( tok_last_type ( & tok ) ) ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
}
else
{
error ( SYNTAX_ERROR ,
tok_get_pos ( & tok ) ,
CMD_ERR_MSG ,
tok_get_desc ( tok_last_type ( & tok ) ) ) ;
}
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
print_errors ( * out , prefix ) ;
}
}
had_cmd = 0 ;
end_of_cmd = 1 ;
break ;
2012-11-19 00:30:30 +00:00
}
2012-11-19 08:31:03 +00:00
case TOK_ERROR :
default :
if ( tok_get_error ( & tok ) = = TOK_UNTERMINATED_QUOTE )
2012-11-19 00:30:30 +00:00
{
2012-11-19 08:31:03 +00:00
unfinished = 1 ;
}
else
{
err = 1 ;
if ( out )
{
error ( SYNTAX_ERROR ,
tok_get_pos ( & tok ) ,
TOK_ERR_MSG ,
tok_last ( & tok ) ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
print_errors ( * out , prefix ) ;
}
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
break ;
2012-11-18 10:23:22 +00:00
}
2012-11-19 00:30:30 +00:00
if ( end_of_cmd )
{
if ( has_command & & command = = L " for " )
{
if ( arg_count > = 0 & & arg_count < 2 )
{
/*
Not enough arguments to the for builtin
*/
err = 1 ;
if ( out )
{
error ( SYNTAX_ERROR ,
tok_get_pos ( & tok ) ,
BUILTIN_FOR_ERR_COUNT ,
L " for " ,
arg_count ) ;
print_errors ( * out , prefix ) ;
}
}
}
2012-09-03 20:24:01 +00:00
else if ( has_command & & command = = L " else " )
{
if ( arg_count = = 1 )
{
/* If we have any arguments, we must have at least two...either "else" or "else if foo..." */
err = true ;
if ( out )
{
2012-11-19 00:30:30 +00:00
error ( SYNTAX_ERROR ,
tok_get_pos ( & tok ) ,
BUILTIN_ELSEIF_ERR_COUNT ,
L " else " ,
arg_count ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
print_errors ( * out , prefix ) ;
2012-09-03 20:24:01 +00:00
}
}
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( ! tok_has_next ( & tok ) )
break ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( needs_cmd )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
err = 1 ;
if ( out )
{
error ( SYNTAX_ERROR ,
tok_get_pos ( & tok ) ,
COND_ERR_MSG ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
print_errors ( * out , prefix ) ;
}
2012-11-18 10:23:22 +00:00
}
2013-01-24 22:59:52 +00:00
if ( out ! = NULL & & ! block_infos . empty ( ) )
2012-11-19 00:30:30 +00:00
{
const wchar_t * cmd ;
2013-01-24 22:59:52 +00:00
int bad_pos = block_infos . back ( ) . position ;
block_type_t bad_type = block_infos . back ( ) . type ;
2012-11-18 10:23:22 +00:00
2013-01-24 22:59:52 +00:00
error ( SYNTAX_ERROR , bad_pos , BLOCK_END_ERR_MSG ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
print_errors ( * out , prefix ) ;
2012-11-18 10:23:22 +00:00
2013-01-24 22:59:52 +00:00
cmd = parser_get_block_command ( bad_type ) ;
2012-11-19 00:30:30 +00:00
if ( cmd )
{
const wcstring h = builtin_help_get ( * this , cmd ) ;
if ( h . size ( ) )
{
append_format ( * out , L " %ls " , h . c_str ( ) ) ;
}
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
/*
Fill in the unset block_level entries . Until now , only places
where the block level _changed_ have been filled out . This fills
in the rest .
*/
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( block_level )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
int last_level = 0 ;
size_t i , len = wcslen ( buff ) ;
for ( i = 0 ; i < len ; i + + )
{
if ( block_level [ i ] > = 0 )
{
last_level = block_level [ i ] ;
/*
Make all whitespace before a token have the new
level . This avoid using the wrong indentation level
if a new line starts with whitespace .
*/
2012-08-04 20:02:44 +00:00
size_t prev_char_idx = i ;
while ( prev_char_idx - - )
2012-11-19 00:30:30 +00:00
{
if ( ! wcschr ( L " \n \t \r " , buff [ prev_char_idx ] ) )
break ;
block_level [ prev_char_idx ] = last_level ;
}
}
block_level [ i ] = last_level ;
2012-11-18 10:23:22 +00:00
}
2012-11-19 00:30:30 +00:00
/*
Make all trailing whitespace have the block level that the
validator had at exit . This makes sure a new line is
correctly indented even if it is empty .
*/
2013-01-24 22:59:52 +00:00
int last_indent = block_infos . empty ( ) ? 0 : block_infos . back ( ) . indentation ;
2012-08-04 20:02:44 +00:00
size_t suffix_idx = len ;
while ( suffix_idx - - )
2012-11-19 00:30:30 +00:00
{
if ( ! wcschr ( L " \n \t \r " , buff [ suffix_idx ] ) )
break ;
2013-01-24 22:59:52 +00:00
block_level [ suffix_idx ] = last_indent ;
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
}
2012-11-19 00:30:30 +00:00
/*
Calculate exit status
*/
2013-01-24 22:59:52 +00:00
if ( ! block_infos . empty ( ) )
2012-11-19 00:30:30 +00:00
unfinished = 1 ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( err )
res | = PARSER_TEST_ERROR ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( unfinished )
res | = PARSER_TEST_INCOMPLETE ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
/*
Cleanup
*/
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
current_tokenizer = previous_tokenizer ;
current_tokenizer_pos = previous_pos ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
error_code = 0 ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
return res ;
2012-11-18 10:23:22 +00:00
2005-09-20 13:26:39 +00:00
}
2012-08-27 06:16:20 +00:00
block_t : : block_t ( block_type_t t ) :
2012-11-19 00:30:30 +00:00
block_type ( t ) ,
made_fake ( false ) ,
skip ( ) ,
had_command ( ) ,
tok_pos ( ) ,
loop_status ( ) ,
job ( ) ,
src_filename ( ) ,
src_lineno ( ) ,
wants_pop_env ( false ) ,
event_blocks ( ) ,
outer ( NULL )
2012-08-27 05:42:29 +00:00
{
}
block_t : : ~ block_t ( )
{
2012-08-27 06:16:20 +00:00
}
/* Various block constructors */
if_block_t : : if_block_t ( ) :
2012-09-01 19:29:00 +00:00
block_t ( IF ) ,
2012-08-27 06:16:20 +00:00
if_expr_evaluated ( false ) ,
2012-09-01 08:46:14 +00:00
is_elseif_entry ( false ) ,
2012-09-01 19:29:00 +00:00
any_branch_taken ( false ) ,
else_evaluated ( false )
2012-08-27 06:16:20 +00:00
{
}
2012-12-22 17:38:28 +00:00
event_block_t : : event_block_t ( const event_t & evt ) :
2012-08-27 06:16:20 +00:00
block_t ( EVENT ) ,
event ( evt )
{
}
function_block_t : : function_block_t ( process_t * p , const wcstring & n , bool shadows ) :
2012-11-19 00:30:30 +00:00
block_t ( shadows ? FUNCTION_CALL : FUNCTION_CALL_NO_SHADOW ) ,
2012-08-27 06:16:20 +00:00
process ( p ) ,
2012-09-01 19:29:00 +00:00
name ( n )
2012-08-27 06:16:20 +00:00
{
}
source_block_t : : source_block_t ( const wchar_t * src ) :
2012-09-01 19:29:00 +00:00
block_t ( SOURCE ) ,
source_file ( src )
2012-08-27 06:16:20 +00:00
{
}
for_block_t : : for_block_t ( const wcstring & var ) :
2012-09-01 19:29:00 +00:00
block_t ( FOR ) ,
2012-08-27 06:16:20 +00:00
variable ( var ) ,
2012-09-01 19:29:00 +00:00
sequence ( )
2012-08-27 06:16:20 +00:00
{
}
while_block_t : : while_block_t ( ) :
2012-09-01 19:29:00 +00:00
block_t ( WHILE ) ,
status ( 0 )
2012-08-27 06:16:20 +00:00
{
}
switch_block_t : : switch_block_t ( const wcstring & sv ) :
2012-09-01 19:29:00 +00:00
block_t ( SWITCH ) ,
2012-08-27 06:16:20 +00:00
switch_taken ( false ) ,
2012-09-01 19:29:00 +00:00
switch_value ( sv )
2012-08-27 06:16:20 +00:00
{
}
fake_block_t : : fake_block_t ( ) :
block_t ( FAKE )
{
}
function_def_block_t : : function_def_block_t ( ) :
2012-09-01 19:29:00 +00:00
block_t ( FUNCTION_DEF ) ,
function_data ( )
2012-08-27 06:16:20 +00:00
{
}
scope_block_t : : scope_block_t ( block_type_t type ) :
block_t ( type )
{
assert ( type = = BEGIN | | type = = TOP | | type = = SUBST ) ;
}
breakpoint_block_t : : breakpoint_block_t ( ) :
block_t ( BREAKPOINT )
{
2012-08-27 05:42:29 +00:00
}
2013-01-04 21:09:01 +00:00