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 block levels in code . This is not the same as
maximum recursion depth , this only has to do with how many block
levels are legal in the source code , not at evaluation .
*/
# define BLOCK_MAX_COUNT 64
/**
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
/**
Error message on reaching maximum recusrion depth
*/
2006-01-04 12:51:02 +00:00
# define 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
*/
2006-01-04 12:51:02 +00:00
# define INVALID_ELSE_ERR_MSG _( L"'else' builtin not inside of if block" )
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
2011-12-27 03:18:46 +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
2011-12-27 03:18:46 +00:00
/**
Breakpoint block
2006-11-11 10:54:00 +00:00
*/
2011-12-27 03:18:46 +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
2006-06-17 13:07:08 +00:00
/**
The block type id . The legal values are defined in parser . h .
*/
2006-06-16 12:56:16 +00:00
int type ;
2006-07-03 10:46:47 +00:00
2006-06-17 13:07:08 +00:00
/**
2011-12-27 03:18:46 +00:00
The name of the builtin that creates this type of block , if any .
2006-06-17 13:07:08 +00:00
*/
2006-06-16 12:56:16 +00:00
const wchar_t * name ;
2011-12-27 03:18:46 +00:00
2006-06-17 13:07:08 +00:00
/**
A description of this block type
*/
2006-06-16 12:56:16 +00:00
const wchar_t * desc ;
}
;
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
{
{
2011-12-27 03:18:46 +00:00
WHILE , L " while " , WHILE_BLOCK
2006-06-16 12:56:16 +00:00
}
,
{
2011-12-27 03:18:46 +00:00
FOR , L " for " , FOR_BLOCK
2006-06-16 12:56:16 +00:00
}
,
{
2011-12-27 03:18:46 +00:00
IF , L " if " , IF_BLOCK
2006-06-16 12:56:16 +00:00
}
,
{
2011-12-27 03:18:46 +00:00
FUNCTION_DEF , L " function " , FUNCTION_DEF_BLOCK
2006-06-16 12:56:16 +00:00
}
,
{
2011-12-27 03:18:46 +00:00
FUNCTION_CALL , 0 , FUNCTION_CALL_BLOCK
2006-06-16 12:56:16 +00:00
}
,
2007-04-22 22:10:33 +00:00
{
2011-12-27 03:18:46 +00:00
FUNCTION_CALL_NO_SHADOW , 0 , FUNCTION_CALL_NO_SHADOW_BLOCK
2007-04-22 22:10:33 +00:00
}
,
2006-06-16 12:56:16 +00:00
{
2011-12-27 03:18:46 +00:00
SWITCH , L " switch " , SWITCH_BLOCK
2006-06-16 12:56:16 +00:00
}
,
{
2011-12-27 03:18:46 +00:00
FAKE , 0 , FAKE_BLOCK
2006-06-16 12:56:16 +00:00
}
,
{
2011-12-27 03:18:46 +00:00
TOP , 0 , TOP_BLOCK
2006-06-16 12:56:16 +00:00
}
,
{
2011-12-27 03:18:46 +00:00
SUBST , 0 , SUBST_BLOCK
2006-06-16 12:56:16 +00:00
}
,
{
2011-12-27 03:18:46 +00:00
BEGIN , L " begin " , BEGIN_BLOCK
2006-06-16 12:56:16 +00:00
}
,
{
2011-12-27 03:18:46 +00:00
SOURCE , L " . " , SOURCE_BLOCK
2006-06-16 12:56:16 +00:00
}
,
{
2011-12-27 03:18:46 +00:00
EVENT , 0 , EVENT_BLOCK
2006-06-16 12:56:16 +00:00
}
,
{
2011-12-27 03:18:46 +00:00
BREAKPOINT , L " breakpoint " , BREAKPOINT_BLOCK
2006-11-11 10:54:00 +00:00
}
,
{
0 , 0 , 0
2006-06-16 12:56:16 +00:00
}
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 ) ,
block_io ( NULL )
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 ;
while ( c )
{
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 ;
}
}
}
2006-01-23 20:40:14 +00:00
/**
Return the current number of block nestings
*/
2009-02-22 20:28:52 +00:00
/*
2006-01-23 20:40:14 +00:00
static int block_count ( block_t * b )
2005-09-20 13:26:39 +00:00
{
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
if ( b = = 0 )
return 0 ;
return ( block_count ( b - > outer ) + 1 ) ;
}
2009-02-22 20:28:52 +00:00
*/
2005-09-20 13:26:39 +00:00
2012-01-23 04:47:13 +00:00
void parser_t : : push_block ( int type )
2005-09-20 13:26:39 +00:00
{
2012-02-10 03:26:44 +00:00
block_t zerod = { } ;
block_t * newv = new block_t ( zerod ) ;
2011-12-27 03:18:46 +00:00
2012-01-23 04:47:13 +00:00
newv - > src_lineno = parser_t : : get_lineno ( ) ;
newv - > src_filename = parser_t : : current_filename ( ) ? intern ( parser_t : : current_filename ( ) ) : 0 ;
2011-12-27 03:18:46 +00:00
newv - > outer = current_block ;
newv - > type = ( current_block & & current_block - > skip ) ? FAKE : type ;
2005-09-24 19:31:17 +00:00
/*
New blocks should be skipped if the outer block is skipped ,
2005-12-09 02:41:16 +00:00
except TOP ans SUBST block , which open up new environments . Fake
blocks should always be skipped . Rather complicated . . . : - (
2005-09-24 19:31:17 +00:00
*/
2011-12-27 03:18:46 +00:00
newv - > skip = current_block ? current_block - > skip : 0 ;
2006-07-12 14:22:42 +00:00
/*
Type TOP and SUBST are never skipped
*/
2005-09-24 19:31:17 +00:00
if ( type = = TOP | | type = = SUBST )
2006-02-20 13:11:46 +00:00
{
2011-12-27 03:18:46 +00:00
newv - > skip = 0 ;
2006-02-20 13:11:46 +00:00
}
2006-07-12 14:22:42 +00:00
/*
Fake blocks and function definition blocks are never executed
*/
if ( type = = FAKE | | type = = FUNCTION_DEF )
2006-02-20 13:11:46 +00:00
{
2011-12-27 03:18:46 +00:00
newv - > skip = 1 ;
2006-02-20 13:11:46 +00:00
}
2011-12-27 03:18:46 +00:00
newv - > job = 0 ;
newv - > loop_status = LOOP_NORMAL ;
2010-09-18 01:51:16 +00:00
2011-12-27 03:18:46 +00:00
current_block = newv ;
2005-09-20 13:26:39 +00:00
2011-12-27 03:18:46 +00:00
if ( ( newv - > type ! = FUNCTION_DEF ) & &
( newv - > type ! = FAKE ) & &
( newv - > type ! = TOP ) )
2005-09-20 13:26:39 +00:00
{
env_push ( type = = FUNCTION_CALL ) ;
2012-02-08 01:06:45 +00:00
newv - > wants_pop_env = true ;
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
{
block_t * old = current_block ;
2006-10-28 16:41:22 +00:00
if ( ! current_block )
{
debug ( 1 ,
2006-11-17 14:58:25 +00:00
L " function %s called on empty block stack. " ,
__func__ ) ;
bugreport ( ) ;
2006-10-28 16:41:22 +00:00
return ;
}
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
current_block = current_block - > outer ;
2012-02-08 01:06:45 +00:00
if ( old - > wants_pop_env )
env_pop ( ) ;
2012-02-10 03:26:44 +00:00
delete old ;
2005-09-20 13:26:39 +00:00
}
2012-01-23 05:40:08 +00:00
const wchar_t * parser_t : : get_block_desc ( int block ) const
2005-09-20 13:26:39 +00:00
{
2006-06-16 12:56:16 +00:00
int i ;
2011-12-27 03:18:46 +00:00
2006-06-16 12:56:16 +00:00
for ( i = 0 ; block_lookup [ i ] . desc ; i + + )
2005-09-20 13:26:39 +00:00
{
2006-06-16 12:56:16 +00:00
if ( block_lookup [ i ] . type = = block )
{
return _ ( block_lookup [ i ] . desc ) ;
}
2005-09-20 13:26:39 +00:00
}
2006-06-16 12:56:16 +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-02-01 05:30:09 +00:00
static int parser_is_pipe_forbidden ( const wcstring & word )
2005-09-20 13:26:39 +00:00
{
2007-09-28 21:32:27 +00:00
return contains ( word ,
2007-04-16 21:40:41 +00:00
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
*/
2006-01-30 19:53:10 +00:00
static const wchar_t * parser_find_end ( const wchar_t * buff )
2005-09-20 13:26:39 +00:00
{
tokenizer tok ;
int had_cmd = 0 ;
int count = 0 ;
int error = 0 ;
int mark = 0 ;
2006-01-30 19:53:10 +00:00
2006-10-28 16:41:22 +00:00
CHECK ( buff , 0 ) ;
2005-09-20 13:26:39 +00:00
for ( tok_init ( & tok , buff , 0 ) ;
tok_has_next ( & tok ) & & ! error ;
tok_next ( & tok ) )
{
int last_type = tok_last_type ( & tok ) ;
switch ( last_type )
{
case TOK_STRING :
{
if ( ! had_cmd )
{
if ( wcscmp ( tok_last ( & tok ) , L " end " ) = = 0 )
{
count - - ;
}
2007-04-22 09:50:26 +00:00
else if ( parser_keywords_is_block ( tok_last ( & tok ) ) )
2005-09-20 13:26:39 +00:00
{
count + + ;
}
if ( count < 0 )
{
error = 1 ;
}
had_cmd = 1 ;
}
break ;
}
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
case TOK_END :
{
had_cmd = 0 ;
break ;
}
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
case TOK_PIPE :
case TOK_BACKGROUND :
{
if ( had_cmd )
{
had_cmd = 0 ;
}
else
{
error = 1 ;
}
break ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
}
case TOK_ERROR :
error = 1 ;
break ;
default :
break ;
2006-01-30 19:53:10 +00:00
}
2005-09-20 13:26:39 +00:00
if ( ! count )
{
2006-01-30 19:53:10 +00:00
tok_next ( & tok ) ;
mark = tok_get_pos ( & tok ) ;
break ;
2005-09-20 13:26:39 +00:00
}
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
}
2006-01-30 19:53:10 +00:00
tok_destroy ( & tok ) ;
2005-09-20 13:26:39 +00:00
if ( ! count & & ! error ) {
return buff + mark ;
}
return 0 ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
}
2012-01-30 06:06:58 +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
{
/*
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-01-23 04:47:13 +00:00
void parser_t : : error ( int ec , int p , const wchar_t * str , . . . )
2005-09-20 13:26:39 +00:00
{
2005-12-07 16:06:47 +00:00
va_list va ;
2006-10-28 16:41:22 +00:00
CHECK ( str , ) ;
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
error_code = ec ;
err_pos = p ;
2005-12-07 16:06:47 +00:00
va_start ( va , str ) ;
2012-02-10 02:43:36 +00:00
err_buff = vformat_string ( str , va ) ;
2006-01-30 19:53:10 +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
*/
2012-01-23 04:47:13 +00:00
static void print_profile ( const std : : vector < profile_item_t > & items ,
size_t pos ,
2006-01-23 20:40:14 +00:00
FILE * out )
2005-09-20 13:26:39 +00:00
{
2012-01-23 04:47:13 +00:00
const profile_item_t * me , * prev ;
size_t i ;
2005-09-20 13:26:39 +00:00
int my_time ;
2006-01-30 19:53:10 +00:00
2012-01-23 04:47:13 +00:00
if ( pos > = items . size ( ) )
2006-02-20 13:11:46 +00:00
{
2005-09-20 13:26:39 +00:00
return ;
2006-02-20 13:11:46 +00:00
}
2011-12-27 03:18:46 +00:00
2012-01-23 04:47:13 +00:00
me = & items . at ( pos ) ;
2005-09-20 13:26:39 +00:00
if ( ! me - > skipped )
{
my_time = me - > parse + me - > exec ;
2006-01-30 19:53:10 +00:00
2012-01-23 04:47:13 +00:00
for ( i = pos + 1 ; i < items . size ( ) ; i + + )
2005-09-20 13:26:39 +00:00
{
2012-01-23 04:47:13 +00:00
prev = & items . at ( i ) ;
2005-09-20 13:26:39 +00:00
if ( prev - > skipped )
2006-02-20 13:11:46 +00:00
{
2005-09-20 13:26:39 +00:00
continue ;
2006-02-20 13:11:46 +00:00
}
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
if ( prev - > level < = me - > level )
2006-02-20 13:11:46 +00:00
{
2005-09-20 13:26:39 +00:00
break ;
2006-02-20 13:11:46 +00:00
}
2005-09-20 13:26:39 +00:00
if ( prev - > level > me - > level + 1 )
2006-02-20 13:11:46 +00:00
{
2005-09-20 13:26:39 +00:00
continue ;
2006-02-20 13:11:46 +00:00
}
2005-09-20 13:26:39 +00:00
my_time - = prev - > parse ;
my_time - = prev - > exec ;
}
2006-01-30 19:53:10 +00:00
2012-01-23 04:47:13 +00:00
if ( me - > cmd . size ( ) > 0 )
2005-09-20 13:26:39 +00:00
{
2006-06-21 09:54:30 +00:00
if ( fwprintf ( out , L " %d \t %d \t " , my_time , me - > parse + me - > exec ) < 0 )
{
wperror ( L " fwprintf " ) ;
return ;
}
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
for ( i = 0 ; i < me - > level ; i + + )
{
2006-06-21 09:54:30 +00:00
if ( fwprintf ( out , L " - " ) < 0 )
{
wperror ( L " fwprintf " ) ;
return ;
}
}
2012-01-23 04:47:13 +00:00
if ( fwprintf ( out , L " > %ls \n " , me - > cmd . c_str ( ) ) < 0 )
2006-06-21 09:54:30 +00:00
{
wperror ( L " fwprintf " ) ;
return ;
2005-09-20 13:26:39 +00:00
}
2006-06-21 09:54:30 +00:00
2005-09-20 13:26:39 +00:00
}
}
2012-01-23 04:47:13 +00:00
print_profile ( items , pos + 1 , out ) ;
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
{
if ( profile )
{
2012-03-02 08:27:40 +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) */
2005-09-20 13:26:39 +00:00
FILE * f = fopen ( profile , " w " ) ;
if ( ! f )
{
debug ( 1 ,
2006-01-04 12:51:02 +00:00
_ ( L " Could not write profiling information to file '%s' " ) ,
2005-09-20 13:26:39 +00:00
profile ) ;
}
else
{
2006-06-21 09:54:30 +00:00
if ( fwprintf ( f ,
_ ( L " Time \t Sum \t Command \n " ) ,
2012-01-19 18:28:44 +00:00
profile_items . size ( ) ) < 0 )
2006-06-21 09:54:30 +00:00
{
wperror ( L " fwprintf " ) ;
}
else
{
2012-01-23 04:47:13 +00:00
print_profile ( profile_items , 0 , f ) ;
2006-06-21 09:54:30 +00:00
}
2011-12-27 03:18:46 +00:00
2006-06-21 09:54:30 +00:00
if ( fclose ( f ) )
{
wperror ( L " fclose " ) ;
}
2005-09-20 13:26:39 +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-02-16 08:24:27 +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
\ param prefix : The string token to prefix the ech line with . Usually the name of the command trying to parse something .
2005-10-08 02:00:08 +00:00
*/
2012-02-22 18:51:06 +00:00
void parser_t : : print_errors ( wcstring & target , const wchar_t * prefix )
2005-09-20 13:26:39 +00:00
{
2006-10-28 16:41:22 +00:00
CHECK ( prefix , ) ;
2011-12-27 03:18:46 +00:00
2012-02-10 02:43:36 +00:00
if ( error_code & & ! err_buff . empty ( ) )
2005-09-20 13:26:39 +00:00
{
int tmp ;
2012-02-22 18:51:06 +00:00
append_format ( target , L " %ls: %ls \n " , prefix , err_buff . c_str ( ) ) ;
2005-09-20 13:26:39 +00:00
tmp = current_tokenizer_pos ;
current_tokenizer_pos = err_pos ;
2012-02-22 18:51:06 +00:00
append_format ( target , L " %ls " , this - > current_line ( ) ) ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
current_tokenizer_pos = tmp ;
}
}
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-02-10 02:43:36 +00:00
if ( error_code & & ! err_buff . empty ( ) )
2006-06-02 02:15:17 +00:00
{
2012-02-10 02:43:36 +00:00
debug ( 0 , L " %ls " , err_buff . c_str ( ) ) ;
2006-06-02 02:15:17 +00:00
int tmp ;
2011-12-27 03:18:46 +00:00
2006-06-02 02:15:17 +00:00
tmp = current_tokenizer_pos ;
current_tokenizer_pos = err_pos ;
2011-12-27 03:18:46 +00:00
2012-01-23 05:57:30 +00:00
fwprintf ( stderr , L " %ls " , this - > current_line ( ) ) ;
2011-12-27 03:18:46 +00:00
2006-06-02 02:15:17 +00:00
current_tokenizer_pos = tmp ;
}
2011-12-27 03:18:46 +00:00
2006-06-02 02:15:17 +00:00
}
2012-01-29 08:41:39 +00:00
int parser_t : : eval_args ( const wchar_t * line , std : : vector < completion_t > & args )
2005-09-20 13:26:39 +00:00
{
tokenizer tok ;
2012-02-26 02:54:49 +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 ;
2005-09-20 13:26:39 +00:00
/*
eval_args may be called while evaulating another command , so we
save the previous tokenizer and restore it on exit
*/
2006-01-30 19:53:10 +00:00
tokenizer * previous_tokenizer = current_tokenizer ;
2005-09-20 13:26:39 +00:00
int previous_pos = current_tokenizer_pos ;
int do_loop = 1 ;
2006-06-21 00:48:36 +00:00
CHECK ( line , 1 ) ;
2012-01-16 16:56:47 +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 ) ;
2006-05-10 11:53:51 +00:00
current_tokenizer = & tok ;
2006-02-04 11:34:33 +00:00
current_tokenizer_pos = 0 ;
2011-12-27 03:18:46 +00:00
2012-02-26 02:54:49 +00:00
tok_init ( & tok , line , ( show_errors ? 0 : TOK_SQUASH_ERRORS ) ) ;
2005-09-20 13:26:39 +00:00
error_code = 0 ;
for ( ; do_loop & & tok_has_next ( & tok ) ; tok_next ( & tok ) )
{
current_tokenizer_pos = tok_get_pos ( & tok ) ;
switch ( tok_last_type ( & tok ) )
{
case TOK_STRING :
2006-02-20 13:11:46 +00:00
{
2012-05-09 09:33:42 +00:00
const wcstring tmp = tok_last ( & tok ) ;
if ( expand_string ( tmp , args , eflags ) = = EXPAND_ERROR )
2005-09-20 13:26:39 +00:00
{
2006-02-20 13:11:46 +00:00
err_pos = tok_get_pos ( & tok ) ;
do_loop = 0 ;
2005-09-20 13:26:39 +00:00
}
break ;
2006-02-20 13:11:46 +00:00
}
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
case TOK_END :
2006-02-20 13:11:46 +00:00
{
2005-09-20 13:26:39 +00:00
break ;
2006-02-20 13:11:46 +00:00
}
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
case TOK_ERROR :
{
2012-02-26 02:54:49 +00:00
if ( show_errors )
error ( SYNTAX_ERROR ,
tok_get_pos ( & tok ) ,
TOK_ERR_MSG ,
tok_last ( & tok ) ) ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
do_loop = 0 ;
break ;
}
default :
2006-02-20 13:11:46 +00:00
{
2012-02-26 02:54:49 +00:00
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
2005-09-20 13:26:39 +00:00
do_loop = 0 ;
break ;
2006-02-20 13:11:46 +00:00
}
2005-09-20 13:26:39 +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 ( ) ;
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
tok_destroy ( & tok ) ;
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
current_tokenizer = previous_tokenizer ;
current_tokenizer_pos = previous_pos ;
2012-02-26 02:54:49 +00:00
if ( this - > parser_type = = PARSER_TYPE_GENERAL )
proc_pop_interactive ( ) ;
2005-09-20 13:26:39 +00:00
return 1 ;
}
2012-02-22 18:51:06 +00:00
void parser_t : : stack_trace ( block_t * b , wcstring & buff )
{
2006-06-08 23:52:12 +00:00
/*
Check if we should end the recursion
*/
if ( ! b )
return ;
2006-02-01 15:49:11 +00:00
if ( b - > type = = EVENT )
{
2006-06-08 23:55:57 +00:00
/*
This is an event handler
*/
2012-02-17 19:36:49 +00:00
wcstring description = event_get_desc ( b - > state1 < const event_t * > ( ) ) ;
2012-02-22 18:51:06 +00:00
append_format ( buff , _ ( L " in event handler: %ls \n " ) , description . c_str ( ) ) ;
buff . append ( L " \n " ) ;
2006-06-08 23:55:57 +00:00
/*
Stop recursing at event handler . No reason to belive that
any other code is relevant .
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 .
*/
2006-02-01 15:49:11 +00:00
return ;
}
2011-12-27 03:18:46 +00:00
2006-02-01 15:49:11 +00:00
if ( b - > type = = FUNCTION_CALL | | b - > type = = SOURCE | | b - > type = = SUBST )
2006-01-26 14:48:10 +00:00
{
2006-06-08 23:55:57 +00:00
/*
These types of blocks should be printed
*/
2006-01-26 14:48:10 +00:00
int i ;
2006-02-01 15:49:11 +00:00
switch ( b - > type )
{
case SOURCE :
2006-02-20 13:11:46 +00:00
{
2012-02-08 01:06:45 +00:00
const wcstring & source_dest = b - > state1 < wcstring > ( ) ;
2012-02-22 18:51:06 +00:00
append_format ( buff , _ ( L " in . (source) call of file '%ls', \n " ) , source_dest . c_str ( ) ) ;
2006-02-01 15:49:11 +00:00
break ;
2006-02-20 13:11:46 +00:00
}
2006-02-01 15:49:11 +00:00
case FUNCTION_CALL :
2006-02-20 13:11:46 +00:00
{
2012-02-08 01:06:45 +00:00
const wcstring & function_call_name = b - > state1 < wcstring > ( ) ;
2012-02-22 18:51:06 +00:00
append_format ( buff , _ ( L " in function '%ls', \n " ) , function_call_name . c_str ( ) ) ;
2006-02-01 15:49:11 +00:00
break ;
2006-02-20 13:11:46 +00:00
}
2006-02-01 15:49:11 +00:00
case SUBST :
2006-02-20 13:11:46 +00:00
{
2012-02-22 18:51:06 +00:00
append_format ( buff , _ ( L " in command substitution \n " ) ) ;
2006-02-01 15:49:11 +00:00
break ;
2006-02-20 13:11:46 +00:00
}
2006-02-01 15:49:11 +00:00
}
2006-01-28 11:34:40 +00:00
2006-02-01 15:49:11 +00:00
const wchar_t * file = b - > src_filename ;
2006-01-26 15:47:22 +00:00
2006-01-26 14:48:10 +00:00
if ( file )
2006-02-20 13:11:46 +00:00
{
2012-02-22 18:51:06 +00:00
append_format ( buff ,
2006-01-26 15:16:59 +00:00
_ ( L " \t called on line %d of file '%ls', \n " ) ,
2006-02-01 15:49:11 +00:00
b - > src_lineno ,
2006-01-26 14:48:10 +00:00
file ) ;
2006-02-20 13:11:46 +00:00
}
2006-01-26 14:48:10 +00:00
else
2006-02-20 13:11:46 +00:00
{
2012-02-22 18:51:06 +00:00
append_format ( buff ,
2006-01-26 15:16:59 +00:00
_ ( L " \t called on standard input, \n " ) ) ;
2006-02-20 13:11:46 +00:00
}
2011-12-27 03:18:46 +00:00
2006-02-01 15:49:11 +00:00
if ( b - > type = = FUNCTION_CALL )
2011-12-27 03:18:46 +00:00
{
2012-02-08 01:06:45 +00:00
const process_t * const process = b - > state2 < process_t * > ( ) ;
if ( process - > argv ( 1 ) )
2006-01-26 14:48:10 +00:00
{
2012-02-22 20:00:02 +00:00
wcstring tmp ;
2011-12-27 03:18:46 +00:00
2012-02-08 01:06:45 +00:00
for ( i = 1 ; process - > argv ( i ) ; i + + )
2006-02-01 15:49:11 +00:00
{
2012-02-22 20:00:02 +00:00
if ( i > 1 )
tmp . push_back ( L ' ' ) ;
tmp . append ( process - > argv ( i ) ) ;
2006-02-01 15:49:11 +00:00
}
2012-02-22 20:00:02 +00:00
append_format ( buff , _ ( L " \t with parameter list '%ls' \n " ) , tmp . c_str ( ) ) ;
2006-01-26 14:48:10 +00:00
}
2006-01-30 19:53:10 +00:00
}
2011-12-27 03:18:46 +00:00
2012-02-22 18:51:06 +00:00
append_format ( buff , L " \n " ) ;
2006-01-30 19:53:10 +00:00
}
2006-06-08 23:55:57 +00:00
/*
Recursively print the next block
*/
2012-01-23 05:40:08 +00:00
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 ;
2006-01-26 14:48:10 +00:00
block_t * b = current_block ;
while ( 1 )
{
if ( ! b )
{
2012-02-08 01:06:45 +00:00
return NULL ;
2006-01-26 14:48:10 +00:00
}
if ( b - > type = = FUNCTION_CALL )
{
2012-02-08 01:06:45 +00:00
result = b - > state1 < wcstring > ( ) ;
return result . c_str ( ) ;
2006-01-26 14:48:10 +00:00
}
b = b - > outer ;
}
}
2012-01-23 05:40:08 +00:00
int parser_t : : get_lineno ( ) const
2006-01-26 14:48:10 +00:00
{
2006-02-01 15:49:11 +00:00
const wchar_t * whole_str ;
2006-01-26 14:48:10 +00:00
const wchar_t * function_name ;
2006-02-05 13:10:35 +00:00
int lineno ;
2011-12-27 03:18:46 +00:00
2006-02-05 13:10:35 +00:00
/* static const wchar_t *prev_str = 0;
2006-04-20 18:35:02 +00:00
static int i = 0 ;
static int lineno = 1 ;
2006-02-05 13:10:35 +00:00
*/
2006-02-01 15:49:11 +00:00
if ( ! current_tokenizer )
return - 1 ;
2011-12-27 03:18:46 +00:00
2006-02-01 15:49:11 +00:00
whole_str = tok_string ( current_tokenizer ) ;
if ( ! whole_str )
return - 1 ;
2011-12-27 03:18:46 +00:00
2006-02-05 13:10:35 +00:00
lineno = parse_util_lineno ( whole_str , current_tokenizer_pos ) ;
2006-01-30 19:53:10 +00:00
2006-01-26 14:48:10 +00:00
if ( ( function_name = is_function ( ) ) )
{
lineno + = function_get_definition_offset ( function_name ) ;
2006-01-30 19:53:10 +00:00
}
2006-01-26 14:48:10 +00:00
return lineno ;
}
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 ( ) ) ;
2006-01-26 14:48:10 +00:00
block_t * b = current_block ;
2006-01-30 19:53:10 +00:00
2006-01-26 14:48:10 +00:00
while ( 1 )
{
if ( ! b )
{
2006-01-30 19:53:10 +00:00
return reader_current_filename ( ) ;
2006-01-26 14:48:10 +00:00
}
if ( b - > type = = FUNCTION_CALL )
{
2012-02-08 01:06:45 +00:00
wcstring function_call_name = b - > state1 < wcstring > ( ) ;
2012-02-19 17:25:15 +00:00
return function_get_definition_file ( function_call_name ) ;
2006-01-26 14:48:10 +00:00
}
b = b - > outer ;
2006-01-30 19:53:10 +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
allignment of the tab character , but other wise behaves like
repeatedly calling wcwidth .
*/
2006-01-26 14:48:10 +00:00
static int printed_width ( const wchar_t * str , int len )
{
int res = 0 ;
int i ;
2006-10-28 16:41:22 +00:00
CHECK ( str , 0 ) ;
2006-06-08 23:57:19 +00:00
for ( i = 0 ; str [ i ] & & i < len ; i + + )
2006-01-26 14:48:10 +00:00
{
if ( str [ i ] = = L ' \t ' )
{
res = ( res + 8 ) & ~ 7 ;
}
else
{
res + = wcwidth ( str [ i ] ) ;
}
}
return res ;
}
2012-01-23 04:47:13 +00:00
const wchar_t * parser_t : : current_line ( )
2005-09-20 13:26:39 +00:00
{
int lineno = 1 ;
2006-05-10 11:54:31 +00:00
const wchar_t * file ;
2012-02-24 17:32:15 +00:00
const wchar_t * whole_str ;
const wchar_t * line ;
const wchar_t * line_end ;
2005-09-20 13:26:39 +00:00
int i ;
int offset ;
2006-01-26 14:48:10 +00:00
int current_line_width ;
const wchar_t * function_name = 0 ;
int current_line_start = 0 ;
2006-01-30 19:53:10 +00:00
2006-05-10 11:54:31 +00:00
if ( ! current_tokenizer )
{
return L " " ;
}
2012-01-23 05:40:08 +00:00
file = parser_t : : current_filename ( ) ;
2006-05-10 11:54:31 +00:00
whole_str = tok_string ( current_tokenizer ) ;
line = whole_str ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
if ( ! line )
return L " " ;
2006-01-30 19:53:10 +00:00
2012-02-22 18:51:06 +00:00
lineinfo . clear ( ) ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
/*
Calculate line number , line offset , etc .
*/
2006-02-08 18:44:37 +00:00
for ( i = 0 ; i < current_tokenizer_pos & & whole_str [ i ] ; i + + )
2005-09-20 13:26:39 +00:00
{
if ( whole_str [ i ] = = L ' \n ' )
{
lineno + + ;
2006-01-26 14:48:10 +00:00
current_line_start = i + 1 ;
2005-09-20 13:26:39 +00:00
line = & whole_str [ i + 1 ] ;
}
}
2006-02-19 01:17:02 +00:00
// lineno = current_tokenizer_pos;
2011-12-27 03:18:46 +00:00
2006-02-19 01:17:02 +00:00
2006-07-21 01:08:31 +00:00
current_line_width = printed_width ( whole_str + current_line_start ,
current_tokenizer_pos - current_line_start ) ;
2006-01-26 14:48:10 +00:00
if ( ( function_name = is_function ( ) ) )
{
lineno + = function_get_definition_offset ( function_name ) ;
2006-01-30 19:53:10 +00:00
}
2006-01-26 14:48:10 +00:00
2005-09-20 13:26:39 +00:00
/*
Copy current line from whole string
*/
line_end = wcschr ( line , L ' \n ' ) ;
if ( ! line_end )
line_end = line + wcslen ( line ) ;
line = wcsndup ( line , line_end - line ) ;
2006-01-26 14:48:10 +00:00
/**
If we are not going to print a stack trace , at least print the line number and filename
*/
2012-02-26 02:54:49 +00:00
if ( ! get_is_interactive ( ) | | is_function ( ) )
2005-09-20 13:26:39 +00:00
{
2012-02-22 18:51:06 +00:00
int prev_width = my_wcswidth ( lineinfo . c_str ( ) ) ;
2006-01-26 14:48:10 +00:00
if ( file )
2012-02-22 18:51:06 +00:00
append_format ( lineinfo ,
2006-01-26 14:48:10 +00:00
_ ( L " %ls (line %d): " ) ,
file ,
lineno ) ;
else
2012-02-22 18:51:06 +00:00
append_format ( lineinfo ,
2006-01-26 14:48:10 +00:00
L " %ls: " ,
_ ( L " Standard input " ) ,
lineno ) ;
2012-02-22 18:51:06 +00:00
offset = my_wcswidth ( lineinfo . c_str ( ) ) - prev_width ;
2005-09-20 13:26:39 +00:00
}
else
{
offset = 0 ;
}
2006-01-26 14:48:10 +00:00
// 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);
2006-01-30 19:53:10 +00:00
/*
2006-04-20 18:35:02 +00:00
Skip printing character position if we are in interactive mode
and the error was on the first character of the line .
2005-12-14 01:07:12 +00:00
*/
2012-02-26 02:54:49 +00:00
if ( ! get_is_interactive ( ) | | is_function ( ) | | ( current_line_width ! = 0 ) )
2006-01-12 12:54:57 +00:00
{
2006-01-30 16:51:50 +00:00
// Workaround since it seems impossible to print 0 copies of a character using %*lc
2006-01-26 14:48:10 +00:00
if ( offset + current_line_width )
{
2012-02-22 18:51:06 +00:00
append_format ( lineinfo ,
2006-01-26 14:48:10 +00:00
L " %ls \n %*lc^ \n " ,
line ,
offset + current_line_width ,
L ' ' ) ;
}
else
{
2012-02-22 18:51:06 +00:00
append_format ( lineinfo ,
2006-01-26 14:48:10 +00:00
L " %ls \n ^ \n " ,
line ) ;
}
2006-01-12 12:54:57 +00:00
}
2006-01-30 19:53:10 +00:00
2012-02-24 17:32:15 +00:00
free ( ( void * ) line ) ;
2012-01-23 05:40:08 +00:00
parser_t : : stack_trace ( current_block , lineinfo ) ;
2005-09-20 13:26:39 +00:00
2012-02-22 18:51:06 +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
{
return tok_get_pos ( current_tokenizer ) ;
}
2012-01-23 05:40:08 +00:00
int parser_t : : get_job_pos ( ) const
2005-09-20 13:26:39 +00:00
{
return job_start_pos ;
}
2012-01-23 05:40:08 +00:00
void parser_t : : set_pos ( int p )
2005-09-20 13:26:39 +00:00
{
tok_set_pos ( current_tokenizer , p ) ;
}
2012-01-23 05:40:08 +00:00
const wchar_t * parser_t : : get_buffer ( ) const
2005-09-20 13:26:39 +00:00
{
return tok_string ( current_tokenizer ) ;
}
2012-01-30 06:06:58 +00:00
int parser_t : : is_help ( const wchar_t * s , int min_match ) const
2005-09-20 13:26:39 +00:00
{
2006-10-28 16:41:22 +00:00
int len ;
CHECK ( s , 0 ) ;
len = wcslen ( s ) ;
2005-09-20 13:26:39 +00:00
2006-07-24 12:48:45 +00:00
min_match = maxi ( min_match , 3 ) ;
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
return ( wcscmp ( L " -h " , s ) = = 0 ) | |
2006-07-24 12:48:45 +00:00
( len > = 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 ) ;
job_set_flag ( res ,
JOB_CONTROL ,
( job_control_mode = = JOB_CONTROL_ALL ) | |
( ( job_control_mode = = JOB_CONTROL_INTERACTIVE ) & & ( get_is_interactive ( ) ) ) ) ;
return res ;
}
bool parser_t : : job_remove ( job_t * j )
{
job_list_t : : iterator iter = std : : find ( my_job_list . begin ( ) , my_job_list . end ( ) , j ) ;
if ( iter ! = my_job_list . end ( ) ) {
my_job_list . erase ( iter ) ;
return true ;
} else {
debug ( 1 , _ ( L " Job inconsistency " ) ) ;
sanity_lose ( ) ;
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 ( ) ) ;
/* 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 ;
while ( ( job = jobs . next ( ) ) ) {
if ( id < = 0 | | job - > job_id = = id )
return job ;
}
return NULL ;
}
job_t * parser_t : : job_get_from_pid ( int pid )
{
job_iterator_t jobs ;
job_t * job ;
while ( ( job = jobs . next ( ) ) ) {
if ( job - > pgid = = pid )
return job ;
}
return 0 ;
}
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-01-23 04:47:13 +00:00
void parser_t : : parse_job_argument_list ( process_t * p ,
2006-10-28 16:44:48 +00:00
job_t * j ,
tokenizer * tok ,
2012-01-16 16:56:47 +00:00
std : : vector < completion_t > & args )
2005-09-20 13:26:39 +00:00
{
int is_finished = 0 ;
int proc_is_count = 0 ;
2005-12-03 16:43:56 +00:00
int matched_wildcard = 0 , unmatched_wildcard = 0 ;
2006-01-30 19:53:10 +00:00
2012-02-10 02:59:15 +00:00
wcstring unmatched ;
2005-12-03 16:43:56 +00:00
int unmatched_pos = 0 ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
/*
Test if this is the ' count ' command . We need to special case
2006-06-05 00:42:01 +00:00
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 .
2005-09-20 13:26:39 +00:00
*/
2012-01-16 16:56:47 +00:00
proc_is_count = ( args . at ( 0 ) . completion = = L " count " ) ;
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
while ( 1 )
{
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
switch ( tok_last_type ( tok ) )
{
case TOK_PIPE :
2006-02-19 01:54:38 +00:00
{
2007-01-09 13:41:17 +00:00
wchar_t * end ;
2011-12-27 03:18:46 +00:00
2012-02-29 19:27:14 +00:00
if ( p - > type = = INTERNAL_EXEC )
2005-09-20 13:26:39 +00:00
{
error ( SYNTAX_ERROR ,
2005-12-07 16:06:47 +00:00
tok_get_pos ( tok ) ,
EXEC_ERR_MSG ) ;
2006-01-30 19:53:10 +00:00
return ;
2005-09-20 13:26:39 +00:00
}
2007-01-09 13:41:17 +00:00
errno = 0 ;
p - > pipe_write_fd = wcstol ( 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 ;
}
2011-12-27 03:18:46 +00:00
2012-02-01 02:06:20 +00:00
p - > set_argv ( completions_to_wcstring_list ( args ) ) ;
2012-01-30 06:06:58 +00:00
p - > next = new process_t ( ) ;
2007-01-09 13:41:17 +00:00
2005-09-20 13:26:39 +00:00
tok_next ( tok ) ;
2011-12-27 03:18:46 +00:00
2006-02-19 01:54:38 +00:00
/*
2006-06-08 23:55:57 +00:00
Don ' t do anything on failiure . parse_job will notice
the error flag and report any errors for us
2006-02-19 01:54:38 +00:00
*/
parse_job ( p - > next , j , tok ) ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
is_finished = 1 ;
break ;
2006-02-19 01:54:38 +00:00
}
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
case TOK_BACKGROUND :
2006-02-19 01:54:38 +00:00
{
2006-10-25 20:47:59 +00:00
job_set_flag ( j , JOB_FOREGROUND , 0 ) ;
2006-02-19 01:54:38 +00:00
}
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
case TOK_END :
{
2012-01-30 06:06:58 +00:00
if ( ! p - > get_argv ( ) )
2012-02-01 02:06:20 +00:00
p - > set_argv ( completions_to_wcstring_list ( args ) ) ;
2005-09-20 13:26:39 +00:00
if ( tok_has_next ( tok ) )
tok_next ( tok ) ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
is_finished = 1 ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
break ;
}
case TOK_STRING :
{
int skip = 0 ;
2006-10-25 20:47:59 +00:00
if ( job_get_flag ( j , JOB_SKIP ) )
2006-10-08 23:44:58 +00:00
{
skip = 1 ;
}
else if ( current_block - > skip )
2005-09-20 13:26:39 +00:00
{
2005-09-24 19:31:17 +00:00
/*
If this command should be skipped , we do not expand the arguments
*/
2005-09-20 13:26:39 +00:00
skip = 1 ;
2006-01-30 19:53:10 +00:00
2005-09-24 19:31:17 +00:00
/*
But if this is in fact a case statement , then it should be evaluated
*/
2006-01-30 19:53:10 +00:00
2012-01-16 16:56:47 +00:00
if ( ( current_block - > type = = SWITCH ) & & args . at ( 0 ) . completion = = L " case " & & p - > type = = INTERNAL_BUILTIN )
2005-09-20 13:26:39 +00:00
{
skip = 0 ;
}
}
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
if ( ! skip )
{
2007-07-31 21:23:32 +00:00
if ( ( proc_is_count ) & &
2012-01-16 16:56:47 +00:00
( args . size ( ) = = 1 ) & &
2012-01-23 05:40:08 +00:00
( parser_t : : is_help ( tok_last ( tok ) , 0 ) ) & &
2007-07-31 21:23:32 +00:00
( p - > type = = INTERNAL_BUILTIN ) )
2005-09-20 13:26:39 +00:00
{
/*
Display help for count
*/
2007-07-31 21:23:32 +00:00
p - > count_help_magic = 1 ;
2005-09-20 13:26:39 +00:00
}
2006-01-30 19:53:10 +00:00
2012-02-06 00:42:24 +00:00
switch ( expand_string ( tok_last ( tok ) , args , 0 ) )
2005-09-20 13:26:39 +00:00
{
2005-12-03 16:43:56 +00:00
case EXPAND_ERROR :
2005-09-20 13:26:39 +00:00
{
2005-12-03 16:43:56 +00:00
err_pos = tok_get_pos ( tok ) ;
if ( error_code = = 0 )
{
2005-12-07 16:06:47 +00:00
error ( SYNTAX_ERROR ,
2007-07-31 21:23:32 +00:00
tok_get_pos ( tok ) ,
_ ( L " Could not expand string '%ls' " ) ,
tok_last ( tok ) ) ;
2006-01-30 19:53:10 +00:00
2005-12-03 16:43:56 +00:00
}
break ;
2005-09-20 13:26:39 +00:00
}
2006-01-30 19:53:10 +00:00
2005-12-03 16:43:56 +00:00
case EXPAND_WILDCARD_NO_MATCH :
{
unmatched_wildcard = 1 ;
2012-02-10 02:59:15 +00:00
if ( unmatched . empty ( ) )
2005-12-03 16:43:56 +00:00
{
2012-02-10 02:59:15 +00:00
unmatched = tok_last ( tok ) ;
2005-12-03 16:43:56 +00:00
unmatched_pos = tok_get_pos ( tok ) ;
}
2006-01-30 19:53:10 +00:00
2005-12-03 16:43:56 +00:00
break ;
}
2006-01-30 19:53:10 +00:00
2005-12-03 16:43:56 +00:00
case EXPAND_WILDCARD_MATCH :
{
matched_wildcard = 1 ;
break ;
}
2006-01-30 19:53:10 +00:00
2005-12-03 16:43:56 +00:00
case EXPAND_OK :
{
break ;
}
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
}
2005-12-03 16:43:56 +00:00
2005-09-20 13:26:39 +00:00
}
break ;
}
case TOK_REDIRECT_OUT :
case TOK_REDIRECT_IN :
case TOK_REDIRECT_APPEND :
case TOK_REDIRECT_FD :
2007-10-26 18:42:32 +00:00
case TOK_REDIRECT_NOCLOB :
2005-09-20 13:26:39 +00:00
{
int type = tok_last_type ( tok ) ;
2012-02-10 02:43:36 +00:00
std : : auto_ptr < io_data_t > new_io ;
2012-02-01 05:30:09 +00:00
wcstring target ;
bool has_target = false ;
2007-01-09 13:41:17 +00:00
wchar_t * end ;
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
/*
Don ' t check redirections in skipped part
2005-12-14 01:07:12 +00:00
Otherwise , bogus errors may be the result . ( Do check
that token is string , though )
2005-09-20 13:26:39 +00:00
*/
if ( current_block - > skip )
{
tok_next ( tok ) ;
2005-12-14 01:07:12 +00:00
if ( tok_last_type ( tok ) ! = TOK_STRING )
{
error ( SYNTAX_ERROR ,
tok_get_pos ( tok ) ,
REDIRECT_TOKEN_ERR_MSG ,
tok_get_desc ( tok_last_type ( tok ) ) ) ;
}
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
break ;
}
2006-01-30 19:53:10 +00:00
2012-02-10 02:43:36 +00:00
new_io . reset ( new io_data_t ) ;
2006-01-30 19:53:10 +00:00
2007-01-09 13:41:17 +00:00
errno = 0 ;
2005-09-20 13:26:39 +00:00
new_io - > fd = wcstol ( tok_last ( tok ) ,
2007-01-09 13:41:17 +00:00
& end ,
2005-09-20 13:26:39 +00:00
10 ) ;
2007-01-09 13:41:17 +00:00
if ( new_io - > fd < 0 | | errno | | * end )
2005-09-20 13:26:39 +00:00
{
2007-01-09 13:41:17 +00:00
error ( SYNTAX_ERROR ,
tok_get_pos ( tok ) ,
ILLEGAL_FD_ERR_MSG ,
tok_last ( tok ) ) ;
2005-09-20 13:26:39 +00:00
}
else
{
2011-12-27 03:18:46 +00:00
2007-01-09 13:41:17 +00:00
tok_next ( tok ) ;
2006-01-30 19:53:10 +00:00
2007-01-09 13:41:17 +00:00
switch ( tok_last_type ( tok ) )
2005-09-20 13:26:39 +00:00
{
2007-01-09 13:41:17 +00:00
case TOK_STRING :
{
2012-02-01 05:30:09 +00:00
target = tok_last ( tok ) ;
has_target = expand_one ( target , no_exec ? EXPAND_SKIP_VARIABLES : 0 ) ;
2005-09-20 13:26:39 +00:00
2012-02-01 05:30:09 +00:00
if ( ! has_target & & error_code = = 0 )
2007-01-09 13:41:17 +00:00
{
error ( SYNTAX_ERROR ,
tok_get_pos ( tok ) ,
REDIRECT_TOKEN_ERR_MSG ,
tok_last ( tok ) ) ;
2005-09-20 13:26:39 +00:00
2007-01-09 13:41:17 +00:00
}
2005-09-20 13:26:39 +00:00
break ;
2007-01-09 13:41:17 +00:00
}
default :
error ( SYNTAX_ERROR ,
tok_get_pos ( tok ) ,
REDIRECT_TOKEN_ERR_MSG ,
tok_get_desc ( tok_last_type ( tok ) ) ) ;
}
2005-09-20 13:26:39 +00:00
2012-02-01 05:30:09 +00:00
if ( ! has_target | | target . size ( ) = = 0 )
2007-01-09 13:41:17 +00:00
{
if ( error_code = = 0 )
error ( SYNTAX_ERROR ,
tok_get_pos ( tok ) ,
_ ( L " Invalid IO redirection " ) ) ;
tok_next ( tok ) ;
}
else
{
switch ( type )
2006-07-21 01:08:31 +00:00
{
2007-01-09 13:41:17 +00:00
case TOK_REDIRECT_APPEND :
new_io - > io_mode = IO_FILE ;
new_io - > param2 . flags = O_CREAT | O_APPEND | O_WRONLY ;
2012-03-09 07:21:07 +00:00
new_io - > set_filename ( target ) ;
2007-01-09 13:41:17 +00:00
break ;
case TOK_REDIRECT_OUT :
new_io - > io_mode = IO_FILE ;
new_io - > param2 . flags = O_CREAT | O_WRONLY | O_TRUNC ;
2012-03-09 07:21:07 +00:00
new_io - > set_filename ( target ) ;
2007-01-09 13:41:17 +00:00
break ;
2007-10-26 18:42:32 +00:00
case TOK_REDIRECT_NOCLOB :
new_io - > io_mode = IO_FILE ;
new_io - > param2 . flags = O_CREAT | O_EXCL | O_WRONLY ;
2012-03-09 07:21:07 +00:00
new_io - > set_filename ( target ) ;
2007-10-26 18:42:32 +00:00
break ;
2007-01-09 13:41:17 +00:00
case TOK_REDIRECT_IN :
new_io - > io_mode = IO_FILE ;
new_io - > param2 . flags = O_RDONLY ;
2012-03-09 07:21:07 +00:00
new_io - > set_filename ( target ) ;
2007-01-09 13:41:17 +00:00
break ;
case TOK_REDIRECT_FD :
2005-09-20 13:26:39 +00:00
{
2012-02-01 05:30:09 +00:00
if ( target = = L " - " )
2005-09-20 13:26:39 +00:00
{
2007-01-09 13:41:17 +00:00
new_io - > io_mode = IO_CLOSE ;
}
else
{
2007-01-27 13:50:10 +00:00
wchar_t * end ;
2011-12-27 03:18:46 +00:00
2007-01-09 13:41:17 +00:00
new_io - > io_mode = IO_FD ;
2007-01-27 13:50:10 +00:00
errno = 0 ;
2011-12-27 03:18:46 +00:00
2012-02-01 05:30:09 +00:00
new_io - > param1 . old_fd = wcstol ( target . c_str ( ) ,
2007-01-27 13:50:10 +00:00
& end ,
2007-01-09 13:41:17 +00:00
10 ) ;
2011-12-27 03:18:46 +00:00
2007-01-09 13:41:17 +00:00
if ( ( new_io - > param1 . old_fd < 0 ) | |
2007-01-27 13:50:10 +00:00
errno | | * end )
2007-01-09 13:41:17 +00:00
{
error ( SYNTAX_ERROR ,
tok_get_pos ( tok ) ,
_ ( L " Requested redirection to something that is not a file descriptor %ls " ) ,
2012-02-01 05:30:09 +00:00
target . c_str ( ) ) ;
2007-01-09 13:41:17 +00:00
tok_next ( tok ) ;
}
2005-09-20 13:26:39 +00:00
}
2007-01-09 13:41:17 +00:00
break ;
2005-09-20 13:26:39 +00:00
}
2006-07-21 01:08:31 +00:00
}
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
}
}
2006-01-30 19:53:10 +00:00
2012-02-10 02:43:36 +00:00
j - > io = io_add ( j - > io , new_io . release ( ) ) ;
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
}
break ;
case TOK_ERROR :
2006-01-30 19:53:10 +00:00
{
2005-12-07 16:06:47 +00:00
error ( SYNTAX_ERROR ,
tok_get_pos ( tok ) ,
TOK_ERR_MSG ,
tok_last ( tok ) ) ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
return ;
}
default :
2005-12-07 16:06:47 +00:00
error ( SYNTAX_ERROR ,
tok_get_pos ( tok ) ,
2005-12-14 03:39:39 +00:00
UNEXPECTED_TOKEN_ERR_MSG ,
2005-12-07 16:06:47 +00:00
tok_get_desc ( tok_last_type ( tok ) ) ) ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
tok_next ( tok ) ;
break ;
}
if ( ( is_finished ) | | ( error_code ! = 0 ) )
break ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
tok_next ( tok ) ;
}
2005-12-03 16:43:56 +00:00
if ( ! error_code )
{
if ( unmatched_wildcard & & ! matched_wildcard )
{
2006-10-25 20:47:59 +00:00
job_set_flag ( j , JOB_WILDCARD_ERROR , 1 ) ;
2006-10-09 01:21:02 +00:00
proc_set_last_status ( STATUS_UNMATCHED_WILDCARD ) ;
2012-02-26 02:54:49 +00:00
if ( get_is_interactive ( ) & & ! is_block )
2005-12-09 02:41:16 +00:00
{
int tmp ;
2006-01-30 19:53:10 +00:00
2012-02-10 02:59:15 +00:00
debug ( 1 , WILDCARD_ERR_MSG , unmatched . c_str ( ) ) ;
2005-12-09 02:41:16 +00:00
tmp = current_tokenizer_pos ;
current_tokenizer_pos = unmatched_pos ;
2012-01-23 04:47:13 +00:00
fwprintf ( stderr , L " %ls " , parser_t : : current_line ( ) ) ;
2006-01-30 19:53:10 +00:00
2005-12-09 02:41:16 +00:00
current_tokenizer_pos = tmp ;
}
2006-01-30 19:53:10 +00:00
2005-12-03 16:43:56 +00:00
}
}
2005-09-20 13:26:39 +00:00
return ;
}
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 ) ;
2011-12-27 03:18:46 +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
*/
2011-12-27 03:18:46 +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-01-23 04:47:13 +00:00
int parser_t : : parse_job ( process_t * p ,
2005-09-20 13:26:39 +00:00
job_t * j ,
tokenizer * tok )
{
2012-02-01 01:01:19 +00:00
std : : vector < completion_t > args ; // The list that will become the argc array for the program
2005-09-20 13:26:39 +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
2006-12-14 00:01:52 +00:00
int use_command = 1 ; // May commands be considered when checking what action this command represents
2006-01-30 19:53:10 +00:00
int is_new_block = 0 ; // Does this command create a new block?
block_t * prev_block = current_block ;
2011-12-27 03:18:46 +00:00
int prev_tokenizer_pos = current_tokenizer_pos ;
2006-10-29 21:09:11 +00:00
2005-09-20 13:26:39 +00:00
current_tokenizer_pos = tok_get_pos ( tok ) ;
2012-02-01 00:50:03 +00:00
while ( args . size ( ) = = 0 )
2005-09-20 13:26:39 +00:00
{
2012-02-01 05:30:09 +00:00
wcstring nxt ;
bool has_nxt = false ;
2006-06-16 12:56:16 +00:00
int consumed = 0 ; // 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
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
switch ( tok_last_type ( tok ) )
{
case TOK_STRING :
2005-12-03 16:43:56 +00:00
{
2012-02-01 05:30:09 +00:00
nxt = tok_last ( tok ) ;
has_nxt = expand_one ( nxt , EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_VARIABLES ) ;
2011-12-27 03:18:46 +00:00
2012-02-01 05:30:09 +00:00
if ( ! has_nxt )
2005-09-20 13:26:39 +00:00
{
2005-12-07 16:06:47 +00:00
error ( SYNTAX_ERROR ,
tok_get_pos ( tok ) ,
2005-12-14 03:39:39 +00:00
ILLEGAL_CMD_ERR_MSG ,
2005-12-07 16:06:47 +00:00
tok_last ( tok ) ) ;
2011-12-27 03:18:46 +00:00
current_tokenizer_pos = prev_tokenizer_pos ;
2005-09-20 13:26:39 +00:00
return 0 ;
}
break ;
2005-12-03 16:43:56 +00:00
}
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
case TOK_ERROR :
{
2005-12-07 16:06:47 +00:00
error ( SYNTAX_ERROR ,
tok_get_pos ( tok ) ,
TOK_ERR_MSG ,
tok_last ( tok ) ) ;
2006-01-30 19:53:10 +00:00
2011-12-27 03:18:46 +00:00
current_tokenizer_pos = prev_tokenizer_pos ;
2005-09-20 13:26:39 +00:00
return 0 ;
}
2006-01-05 16:02:28 +00:00
case TOK_PIPE :
{
2012-02-24 17:32:15 +00:00
const wchar_t * str = tok_string ( tok ) ;
2006-01-05 16:02:28 +00:00
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 ,
2006-04-20 18:35:02 +00:00
tok_get_pos ( tok ) ,
CMD_ERR_MSG ,
2006-01-05 16:02:28 +00:00
tok_get_desc ( tok_last_type ( tok ) ) ) ;
}
2006-01-30 19:53:10 +00:00
2011-12-27 03:18:46 +00:00
current_tokenizer_pos = prev_tokenizer_pos ;
2006-01-05 16:02:28 +00:00
return 0 ;
}
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
default :
2005-12-03 16:43:56 +00:00
{
2005-12-07 16:06:47 +00:00
error ( SYNTAX_ERROR ,
tok_get_pos ( tok ) ,
2005-12-14 03:39:39 +00:00
CMD_ERR_MSG ,
2005-12-07 16:06:47 +00:00
tok_get_desc ( tok_last_type ( tok ) ) ) ;
2006-01-30 19:53:10 +00:00
2011-12-27 03:18:46 +00:00
current_tokenizer_pos = prev_tokenizer_pos ;
2005-09-20 13:26:39 +00:00
return 0 ;
2005-12-03 16:43:56 +00:00
}
2005-09-20 13:26:39 +00:00
}
2011-12-27 03:18:46 +00:00
2006-06-16 12:56:16 +00:00
mark = tok_get_pos ( tok ) ;
2006-02-09 15:50:20 +00:00
2007-09-28 21:32:27 +00:00
if ( contains ( nxt ,
2007-04-16 21:40:41 +00:00
L " command " ,
L " builtin " ,
L " not " ,
L " and " ,
L " or " ,
L " exec " ) )
2011-12-27 03:18:46 +00:00
{
2006-12-13 15:44:49 +00:00
int sw ;
2012-02-01 05:30:09 +00:00
int is_exec = nxt = = L " exec " ;
2011-12-27 03:18:46 +00:00
2006-12-13 15:44:49 +00:00
if ( is_exec & & ( p ! = j - > first_process ) )
2005-09-20 13:26:39 +00:00
{
error ( SYNTAX_ERROR ,
2005-12-07 16:06:47 +00:00
tok_get_pos ( tok ) ,
EXEC_ERR_MSG ) ;
2011-12-27 03:18:46 +00:00
current_tokenizer_pos = prev_tokenizer_pos ;
2005-09-20 13:26:39 +00:00
return 0 ;
}
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
tok_next ( tok ) ;
2007-04-22 09:50:26 +00:00
sw = parser_keywords_is_switch ( tok_last ( tok ) ) ;
2011-12-27 03:18:46 +00:00
2006-12-13 15:44:49 +00:00
if ( sw = = ARG_SWITCH )
2005-09-20 13:26:39 +00:00
{
tok_set_pos ( tok , mark ) ;
}
else
{
2006-12-13 15:44:49 +00:00
if ( sw = = ARG_SKIP )
{
tok_next ( tok ) ;
}
2011-12-27 03:18:46 +00:00
2006-02-09 15:50:20 +00:00
consumed = 1 ;
2006-12-13 15:44:49 +00:00
2012-02-01 05:30:09 +00:00
if ( nxt = = L " command " | | nxt = = L " builtin " )
2006-12-13 15:44:49 +00:00
{
use_function = 0 ;
2012-02-01 05:30:09 +00:00
if ( nxt = = L " command " )
2006-12-14 00:01:52 +00:00
{
use_builtin = 0 ;
use_command = 1 ;
2011-12-27 03:18:46 +00:00
}
2006-12-14 00:01:52 +00:00
else
{
2011-12-27 03:18:46 +00:00
use_builtin = 1 ;
use_command = 0 ;
2006-12-14 00:01:52 +00:00
}
2006-12-13 15:44:49 +00:00
}
2012-02-01 05:30:09 +00:00
else if ( nxt = = L " not " )
2006-12-13 15:44:49 +00:00
{
job_set_flag ( j , JOB_NEGATE , ! job_get_flag ( j , JOB_NEGATE ) ) ;
}
2012-02-01 05:30:09 +00:00
else if ( nxt = = L " and " )
2006-12-13 15:44:49 +00:00
{
job_set_flag ( j , JOB_SKIP , proc_get_last_status ( ) ) ;
}
2012-02-01 05:30:09 +00:00
else if ( nxt = = L " or " )
2006-12-13 15:44:49 +00:00
{
job_set_flag ( j , JOB_SKIP , ! proc_get_last_status ( ) ) ;
}
else if ( is_exec )
{
use_function = 0 ;
use_builtin = 0 ;
p - > type = INTERNAL_EXEC ;
2011-12-27 03:18:46 +00:00
current_tokenizer_pos = prev_tokenizer_pos ;
2006-12-13 15:44:49 +00:00
}
2005-09-20 13:26:39 +00:00
}
}
2012-02-01 05:30:09 +00:00
else if ( nxt = = L " while " )
2005-09-20 13:26:39 +00:00
{
int new_block = 0 ;
tok_next ( tok ) ;
2005-12-03 16:43:56 +00:00
if ( ( current_block - > type ! = WHILE ) )
2005-09-20 13:26:39 +00:00
{
new_block = 1 ;
}
2012-02-08 01:06:45 +00:00
else if ( current_block - > state1 < int > ( ) = = WHILE_TEST_AGAIN )
2005-09-20 13:26:39 +00:00
{
2012-02-08 01:06:45 +00:00
current_block - > state1 < int > ( ) = WHILE_TEST_FIRST ;
2005-09-20 13:26:39 +00:00
}
else
{
new_block = 1 ;
}
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
if ( new_block )
{
2012-01-23 04:47:13 +00:00
this - > push_block ( WHILE ) ;
2012-02-08 01:06:45 +00:00
current_block - > state1 < int > ( ) = WHILE_TEST_FIRST ;
2005-09-20 13:26:39 +00:00
current_block - > tok_pos = mark ;
}
2006-01-30 19:53:10 +00:00
2006-02-09 15:50:20 +00:00
consumed = 1 ;
2005-09-20 13:26:39 +00:00
is_new_block = 1 ;
2006-05-14 22:29:05 +00:00
2005-09-20 13:26:39 +00:00
}
2012-02-01 05:30:09 +00:00
else if ( nxt = = L " if " )
2005-09-20 13:26:39 +00:00
{
tok_next ( tok ) ;
2012-01-23 04:47:13 +00:00
this - > push_block ( IF ) ;
2005-09-20 13:26:39 +00:00
2012-02-08 01:06:45 +00:00
current_block - > state1 < int > ( ) = 0 ;
2005-09-20 13:26:39 +00:00
current_block - > tok_pos = mark ;
is_new_block = 1 ;
2011-12-27 03:18:46 +00:00
consumed = 1 ;
2006-02-09 15:50:20 +00:00
}
2006-06-16 12:56:16 +00:00
/*
Test if we need another command
*/
2006-02-09 15:50:20 +00:00
if ( consumed )
{
2006-06-16 12:56:16 +00:00
/*
Yes we do , around in the loop for another lap , then !
*/
2005-09-20 13:26:39 +00:00
continue ;
}
2011-12-27 03:18:46 +00:00
2006-02-08 09:20:05 +00:00
if ( use_function & & ! current_block - > skip )
2005-09-20 13:26:39 +00:00
{
2006-10-28 16:41:22 +00:00
int nxt_forbidden = 0 ;
2011-12-27 06:51:34 +00:00
wcstring forbid ;
2005-09-20 13:26:39 +00:00
2006-11-02 13:45:37 +00:00
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 .
*/
2011-12-27 03:18:46 +00:00
if ( ( current_block - > type = = TOP ) & &
( current_block - > outer ) & &
2006-11-02 13:45:37 +00:00
( current_block - > outer - > type = = FUNCTION_CALL ) )
is_function_call = 1 ;
2011-12-27 03:18:46 +00:00
2006-11-02 13:45:37 +00:00
/*
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 )
2006-10-28 16:41:22 +00:00
{
2011-12-27 06:51:34 +00:00
forbid = forbidden_function . empty ( ) ? wcstring ( L " " ) : forbidden_function . back ( ) ;
nxt_forbidden = ( forbid = = nxt ) ;
2006-10-28 16:41:22 +00:00
}
2011-12-27 03:18:46 +00:00
2012-02-19 17:25:15 +00:00
if ( ! nxt_forbidden & & has_nxt & & function_exists ( nxt ) )
2005-09-20 13:26:39 +00:00
{
/*
Check if we have reached the maximum recursion depth
*/
2011-12-27 06:51:34 +00:00
if ( forbidden_function . size ( ) > MAX_RECURSION_DEPTH )
2005-09-20 13:26:39 +00:00
{
error ( SYNTAX_ERROR ,
2005-12-07 16:06:47 +00:00
tok_get_pos ( tok ) ,
RECURSION_ERR_MSG ) ;
2005-09-20 13:26:39 +00:00
}
else
{
p - > type = INTERNAL_FUNCTION ;
}
}
}
2012-02-02 00:27:14 +00:00
args . push_back ( completion_t ( nxt ) ) ;
2005-09-20 13:26:39 +00:00
}
if ( error_code = = 0 )
{
if ( ! p - > type )
{
if ( use_builtin & &
2012-02-01 04:22:25 +00:00
builtin_exists ( args . at ( 0 ) . completion ) )
2005-09-20 13:26:39 +00:00
{
p - > type = INTERNAL_BUILTIN ;
2012-02-01 00:50:03 +00:00
is_new_block | = parser_keywords_is_block ( args . at ( 0 ) . completion ) ;
2006-01-30 19:53:10 +00:00
}
2005-09-20 13:26:39 +00:00
}
2011-12-27 03:18:46 +00:00
2006-12-14 00:01:52 +00:00
if ( ( ! p - > type | | ( p - > type = = INTERNAL_EXEC ) ) )
2005-09-20 13:26:39 +00:00
{
2006-01-30 19:53:10 +00:00
/*
2006-04-20 18:35:02 +00:00
If we are not executing the current block , allow
non - existent commands .
2005-09-20 13:26:39 +00:00
*/
if ( current_block - > skip )
{
2012-01-30 07:22:42 +00:00
p - > actual_cmd = wcsdup ( L " " ) ;
2005-09-20 13:26:39 +00:00
}
else
{
2008-01-08 19:31:45 +00:00
int err ;
2012-02-01 00:50:03 +00:00
p - > actual_cmd = path_get_path ( args . at ( 0 ) . completion . c_str ( ) ) ;
2008-01-08 19:31:45 +00:00
err = errno ;
2011-12-27 03:18:46 +00:00
2012-06-02 21:04:25 +00:00
bool use_implicit_cd = false ;
if ( p - > actual_cmd = = NULL )
{
/* 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 ) ) ;
/* 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 ;
}
}
/* Check if the specified command exists */
if ( p - > actual_cmd = = NULL & & ! use_implicit_cd )
2005-09-20 13:26:39 +00:00
{
2012-03-02 19:12:08 +00:00
2012-02-08 19:48:51 +00:00
int tmp ;
2012-03-02 19:12:08 +00:00
const wchar_t * cmd = args . at ( 0 ) . completion . c_str ( ) ;
/*
We couldn ' t find the specified command .
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 ) .
*/
if ( wcschr ( cmd , L ' = ' ) )
{
wchar_t * cpy = wcsdup ( cmd ) ;
wchar_t * valpart = wcschr ( cpy , L ' = ' ) ;
* valpart + + = 0 ;
debug ( 0 ,
COMMAND_ASSIGN_ERR_MSG ,
cmd ,
cpy ,
valpart ) ;
free ( cpy ) ;
}
else if ( cmd [ 0 ] = = L ' $ ' )
{
const env_var_t val_wstr = env_get_string ( cmd + 1 ) ;
const wchar_t * val = val_wstr . missing ( ) ? NULL : val_wstr . c_str ( ) ;
if ( val )
{
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'. " ) ,
cmd + 1 ,
val ,
cmd ) ;
}
else
{
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 ) ;
}
}
else if ( wcschr ( cmd , L ' $ ' ) )
{
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'. " ) ,
cmd ,
cmd ) ;
}
else if ( err ! = ENOENT )
{
debug ( 0 ,
_ ( L " The file '%ls' is not executable by this user " ) ,
cmd ? cmd : L " UNKNOWN " ) ;
}
else
{
debug ( 0 ,
_ ( L " Unknown command '%ls' " ) ,
cmd ? cmd : L " UNKNOWN " ) ;
}
tmp = current_tokenizer_pos ;
current_tokenizer_pos = tok_get_pos ( tok ) ;
fwprintf ( stderr , L " %ls " , parser_t : : current_line ( ) ) ;
current_tokenizer_pos = tmp ;
job_set_flag ( j , JOB_SKIP , 1 ) ;
event_fire_generic ( L " fish_command_not_found " , ( wchar_t * ) ( args . at ( 0 ) . completion . c_str ( ) ) ) ;
proc_set_last_status ( err = = ENOENT ? STATUS_UNKNOWN_COMMAND : STATUS_NOT_EXECUTABLE ) ;
2005-09-20 13:26:39 +00:00
}
}
}
2011-12-27 03:18:46 +00:00
2006-12-14 00:01:52 +00:00
if ( ( p - > type = = EXTERNAL ) & & ! use_command )
{
error ( SYNTAX_ERROR ,
tok_get_pos ( tok ) ,
UNKNOWN_BUILTIN_ERR_MSG ,
2012-02-01 00:50:03 +00:00
args . back ( ) . completion . c_str ( ) ) ;
2006-12-14 00:01:52 +00:00
}
2005-09-20 13:26:39 +00:00
}
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
if ( is_new_block )
{
2011-12-27 03:18:46 +00:00
2006-01-30 19:53:10 +00:00
const wchar_t * end = parser_find_end ( tok_string ( tok ) +
2005-09-20 13:26:39 +00:00
current_tokenizer_pos ) ;
tokenizer subtok ;
int make_sub_block = j - > first_process ! = p ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
if ( ! end )
{
error ( SYNTAX_ERROR ,
2005-12-07 16:06:47 +00:00
tok_get_pos ( tok ) ,
2005-12-14 03:39:39 +00:00
BLOCK_END_ERR_MSG ) ;
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
}
2007-04-22 09:48:47 +00:00
else
2005-09-20 13:26:39 +00:00
{
2011-12-27 03:18:46 +00:00
2007-04-22 09:48:47 +00:00
if ( ! make_sub_block )
2005-09-20 13:26:39 +00:00
{
2007-04-22 09:48:47 +00:00
int done = 0 ;
2011-12-27 03:18:46 +00:00
for ( tok_init ( & subtok , end , 0 ) ;
! done & & tok_has_next ( & subtok ) ;
2007-04-22 09:48:47 +00:00
tok_next ( & subtok ) )
2005-09-20 13:26:39 +00:00
{
2011-12-27 03:18:46 +00:00
2007-04-22 09:48:47 +00:00
switch ( tok_last_type ( & subtok ) )
2006-09-05 20:43:47 +00:00
{
2007-04-22 09:48:47 +00:00
case TOK_END :
done = 1 ;
break ;
2011-12-27 03:18:46 +00:00
2007-04-22 09:48:47 +00:00
case TOK_REDIRECT_OUT :
2007-10-26 18:42:32 +00:00
case TOK_REDIRECT_NOCLOB :
2007-04-22 09:48:47 +00:00
case TOK_REDIRECT_APPEND :
case TOK_REDIRECT_IN :
case TOK_REDIRECT_FD :
case TOK_PIPE :
{
done = 1 ;
make_sub_block = 1 ;
break ;
}
2011-12-27 03:18:46 +00:00
2007-04-22 09:48:47 +00:00
case TOK_STRING :
{
break ;
}
2011-12-27 03:18:46 +00:00
2007-04-22 09:48:47 +00:00
default :
{
done = 1 ;
error ( SYNTAX_ERROR ,
current_tokenizer_pos ,
BLOCK_END_ERR_MSG ) ;
}
2006-09-05 20:43:47 +00:00
}
2005-09-20 13:26:39 +00:00
}
2011-12-27 03:18:46 +00:00
2007-04-22 09:48:47 +00:00
tok_destroy ( & subtok ) ;
}
2011-12-27 03:18:46 +00:00
2007-04-22 09:48:47 +00:00
if ( make_sub_block )
{
2011-12-27 03:18:46 +00:00
2007-04-22 09:48:47 +00:00
int end_pos = end - tok_string ( tok ) ;
2012-01-30 10:23:58 +00:00
const wcstring sub_block ( tok_string ( tok ) + current_tokenizer_pos , end_pos - current_tokenizer_pos ) ;
2011-12-27 03:18:46 +00:00
2007-04-22 09:48:47 +00:00
p - > type = INTERNAL_BLOCK ;
2012-02-02 00:27:14 +00:00
args . at ( 0 ) = completion_t ( sub_block ) ;
2011-12-27 03:18:46 +00:00
2007-04-22 09:48:47 +00:00
tok_set_pos ( tok ,
end_pos ) ;
2005-09-20 13:26:39 +00:00
2007-04-22 09:48:47 +00:00
while ( prev_block ! = current_block )
{
2012-01-23 05:40:08 +00:00
parser_t : : pop_block ( ) ;
2007-04-22 09:48:47 +00:00
}
2006-01-30 19:53:10 +00:00
2007-04-22 09:48:47 +00:00
}
else tok_next ( tok ) ;
2005-09-20 13:26:39 +00:00
}
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
}
else tok_next ( tok ) ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
if ( ! error_code )
2005-09-25 11:25:42 +00:00
{
2012-02-01 00:50:03 +00:00
if ( p - > type = = INTERNAL_BUILTIN & & parser_keywords_skip_arguments ( args . at ( 0 ) . completion ) )
2011-12-27 03:18:46 +00:00
{
2012-01-30 06:06:58 +00:00
if ( ! p - > get_argv ( ) )
2012-02-01 02:06:20 +00:00
p - > set_argv ( completions_to_wcstring_list ( args ) ) ;
2005-09-25 11:25:42 +00:00
}
else
{
2012-02-01 00:50:03 +00:00
parse_job_argument_list ( p , j , tok , args ) ;
2005-09-25 11:25:42 +00:00
}
}
2006-01-30 19:53:10 +00:00
2006-11-02 13:45:37 +00:00
if ( ! error_code )
{
if ( ! is_new_block )
{
current_block - > had_command = 1 ;
}
}
2005-09-20 13:26:39 +00:00
if ( error_code )
{
/*
Make sure the block stack is consistent
*/
while ( prev_block ! = current_block )
2005-12-12 15:51:04 +00:00
{
2012-01-23 05:40:08 +00:00
parser_t : : pop_block ( ) ;
2005-12-12 15:51:04 +00:00
}
2005-09-20 13:26:39 +00:00
}
2011-12-27 03:18:46 +00:00
current_tokenizer_pos = prev_tokenizer_pos ;
2005-09-20 13:26:39 +00:00
return ! error_code ;
}
/**
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-01-23 04:47:13 +00:00
void parser_t : : skipped_exec ( job_t * j )
2005-09-20 13:26:39 +00:00
{
process_t * p ;
2005-12-09 02:41:16 +00:00
2005-09-20 13:26:39 +00:00
for ( p = j - > first_process ; p ; p = p - > next )
{
2005-12-09 02:41:16 +00:00
if ( p - > type = = INTERNAL_BUILTIN )
2005-09-20 13:26:39 +00:00
{
2012-01-30 06:06:58 +00:00
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 ) )
2005-09-20 13:26:39 +00:00
{
2012-01-23 04:47:13 +00:00
this - > push_block ( FAKE ) ;
2005-09-20 13:26:39 +00:00
}
2012-01-30 06:06:58 +00:00
else if ( wcscmp ( p - > argv0 ( ) , L " end " ) = = 0 )
2005-09-20 13:26:39 +00:00
{
if ( ! current_block - > outer - > skip )
{
2012-01-23 04:47:13 +00:00
exec ( * this , j ) ;
2005-09-20 13:26:39 +00:00
return ;
}
2012-01-23 05:40:08 +00:00
parser_t : : pop_block ( ) ;
2005-09-20 13:26:39 +00:00
}
2012-01-30 06:06:58 +00:00
else if ( wcscmp ( p - > argv0 ( ) , L " else " ) = = 0 )
2005-09-20 13:26:39 +00:00
{
if ( ( current_block - > type = = IF ) & &
2012-02-08 01:06:45 +00:00
( current_block - > state1 < int > ( ) ! = 0 ) )
2005-09-20 13:26:39 +00:00
{
2012-01-23 04:47:13 +00:00
exec ( * this , j ) ;
2005-09-20 13:26:39 +00:00
return ;
}
}
2012-01-30 06:06:58 +00:00
else if ( wcscmp ( p - > argv0 ( ) , L " case " ) = = 0 )
2005-09-20 13:26:39 +00:00
{
2012-02-29 19:27:14 +00:00
if ( current_block - > type = = SWITCH )
2005-09-20 13:26:39 +00:00
{
2012-01-23 04:47:13 +00:00
exec ( * this , j ) ;
2005-09-20 13:26:39 +00:00
return ;
}
}
}
}
job_free ( j ) ;
}
/**
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-01-23 04:47:13 +00:00
void parser_t : : eval_job ( tokenizer * tok )
2005-09-20 13:26:39 +00:00
{
2012-03-31 22:33:34 +00:00
ASSERT_IS_MAIN_THREAD ( ) ;
2005-09-20 13:26:39 +00:00
job_t * j ;
int start_pos = job_start_pos = tok_get_pos ( tok ) ;
long long t1 = 0 , t2 = 0 , t3 = 0 ;
2012-01-19 18:28:44 +00:00
profile_item_t * profile_item = NULL ;
2012-06-04 22:10:35 +00:00
bool skip = false ;
2006-02-19 01:17:02 +00:00
int job_begin_pos , prev_tokenizer_pos ;
2012-03-26 08:21:10 +00:00
const bool do_profile = profile ;
if ( do_profile )
2005-09-20 13:26:39 +00:00
{
2012-01-23 04:47:13 +00:00
profile_items . resize ( profile_items . size ( ) + 1 ) ;
profile_item = & profile_items . back ( ) ;
2012-01-19 18:28:44 +00:00
profile_item - > cmd = L " " ;
profile_item - > skipped = 1 ;
2005-09-20 13:26:39 +00:00
t1 = get_time ( ) ;
}
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
switch ( tok_last_type ( tok ) )
{
case TOK_STRING :
{
2012-02-28 02:43:24 +00:00
j = this - > job_create ( ) ;
2006-10-25 20:47:59 +00:00
job_set_flag ( j , JOB_FOREGROUND , 1 ) ;
job_set_flag ( j , JOB_TERMINAL , job_get_flag ( j , JOB_CONTROL ) ) ;
2010-10-16 17:39:52 +00:00
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 \
2012-02-26 02:54:49 +00:00
| | ( ! get_is_interactive ( ) ) ) ;
2011-12-27 03:18:46 +00:00
2006-01-18 12:42:48 +00:00
current_block - > job = j ;
2011-12-27 03:18:46 +00:00
2012-02-26 02:54:49 +00:00
if ( get_is_interactive ( ) )
2005-09-20 13:26:39 +00:00
{
if ( tcgetattr ( 0 , & j - > tmodes ) )
{
tok_next ( tok ) ;
wperror ( L " tcgetattr " ) ;
job_free ( j ) ;
break ;
}
}
2006-01-30 19:53:10 +00:00
2012-01-30 06:06:58 +00:00
j - > first_process = new process_t ( ) ;
2006-02-19 01:17:02 +00:00
job_begin_pos = tok_get_pos ( tok ) ;
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
if ( parse_job ( j - > first_process , j , tok ) & &
2012-01-30 06:06:58 +00:00
j - > first_process - > get_argv ( ) )
2005-09-20 13:26:39 +00:00
{
if ( job_start_pos < tok_get_pos ( tok ) )
{
int stop_pos = tok_get_pos ( tok ) ;
2012-03-02 19:12:08 +00:00
const wchar_t * newline = wcschr ( tok_string ( tok ) + start_pos , L ' \n ' ) ;
2005-09-20 13:26:39 +00:00
if ( newline )
stop_pos = mini ( stop_pos , newline - tok_string ( tok ) ) ;
2006-01-30 19:53:10 +00:00
2012-03-09 07:21:07 +00:00
j - > set_command ( wcstring ( tok_string ( tok ) + start_pos , stop_pos - start_pos ) ) ;
2005-09-20 13:26:39 +00:00
}
else
2012-03-09 07:21:07 +00:00
j - > set_command ( L " " ) ;
2006-01-30 19:53:10 +00:00
2012-03-26 08:21:10 +00:00
if ( do_profile )
2005-09-20 13:26:39 +00:00
{
t2 = get_time ( ) ;
2012-03-09 07:21:07 +00:00
profile_item - > cmd = wcsdup ( j - > command_wcstr ( ) ) ;
2012-01-19 18:28:44 +00:00
profile_item - > skipped = current_block - > skip ;
2005-09-20 13:26:39 +00:00
}
2011-12-27 03:18:46 +00:00
2012-06-04 22:10:35 +00:00
skip = skip | | current_block - > skip ;
skip = skip | | job_get_flag ( j , JOB_WILDCARD_ERROR ) ;
skip = skip | | job_get_flag ( j , JOB_SKIP ) ;
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
if ( ! skip )
{
2005-11-29 19:51:32 +00:00
int was_builtin = 0 ;
2012-04-10 03:17:06 +00:00
if ( j - > first_process - > type = = INTERNAL_BUILTIN & & ! j - > first_process - > next )
was_builtin = 1 ;
2006-02-19 01:17:02 +00:00
prev_tokenizer_pos = current_tokenizer_pos ;
2011-12-27 03:18:46 +00:00
current_tokenizer_pos = job_begin_pos ;
2012-01-23 04:47:13 +00:00
exec ( * this , j ) ;
2006-02-19 01:17:02 +00:00
current_tokenizer_pos = prev_tokenizer_pos ;
2011-12-27 03:18:46 +00:00
2005-11-29 19:51:32 +00:00
/* Only external commands require a new fishd barrier */
if ( ! was_builtin )
2012-03-31 22:33:34 +00:00
set_proc_had_barrier ( false ) ;
2005-09-20 13:26:39 +00:00
}
else
{
2012-01-23 04:47:13 +00:00
this - > skipped_exec ( j ) ;
2005-09-20 13:26:39 +00:00
}
2006-01-30 19:53:10 +00:00
2012-03-26 08:21:10 +00:00
if ( do_profile )
2005-09-20 13:26:39 +00:00
{
t3 = get_time ( ) ;
2012-01-19 18:28:44 +00:00
profile_item - > level = eval_level ;
profile_item - > parse = t2 - t1 ;
profile_item - > exec = t3 - t2 ;
2005-09-20 13:26:39 +00:00
}
if ( current_block - > type = = WHILE )
{
2006-01-30 19:53:10 +00:00
2012-02-08 01:06:45 +00:00
switch ( current_block - > state1 < int > ( ) )
2005-09-20 13:26:39 +00:00
{
case WHILE_TEST_FIRST :
{
2012-06-04 22:10:35 +00:00
// PCA I added the 'current_block->skip ||' part because we couldn't reliably
// control-C out of loops like this: while test 1 -eq 1; end
current_block - > skip = current_block - > skip | | proc_get_last_status ( ) ! = 0 ;
2012-02-08 01:06:45 +00:00
current_block - > state1 < int > ( ) = WHILE_TESTED ;
2005-09-20 13:26:39 +00:00
}
break ;
}
}
if ( current_block - > type = = IF )
{
2012-02-08 01:06:45 +00:00
if ( ( ! current_block - > state1 < int > ( ) ) & &
2005-09-20 13:26:39 +00:00
( ! current_block - > skip ) )
{
current_block - > skip = proc_get_last_status ( ) ! = 0 ;
2012-02-08 01:06:45 +00:00
current_block - > state1 < int > ( ) + + ;
2005-09-20 13:26:39 +00:00
}
}
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
}
else
{
2005-09-24 19:31:17 +00:00
/*
2006-06-08 23:55:57 +00:00
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 .
2005-09-24 19:31:17 +00:00
*/
2005-09-20 13:26:39 +00:00
job_free ( j ) ;
2005-12-07 13:02:09 +00:00
proc_set_last_status ( 1 ) ;
2005-09-20 13:26:39 +00:00
}
2005-10-15 00:51:26 +00:00
current_block - > job = 0 ;
2005-09-20 13:26:39 +00:00
break ;
}
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
case TOK_END :
{
if ( tok_has_next ( tok ) )
tok_next ( tok ) ;
break ;
}
2006-01-05 16:02:28 +00:00
case TOK_BACKGROUND :
{
2012-02-24 17:32:15 +00:00
const wchar_t * str = tok_string ( tok ) ;
2006-01-05 16:02:28 +00:00
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 ) ) ) ;
}
2006-01-30 19:53:10 +00:00
2006-01-08 23:00:49 +00:00
return ;
2006-01-05 16:02:28 +00:00
}
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
case TOK_ERROR :
{
2005-12-07 16:06:47 +00:00
error ( SYNTAX_ERROR ,
tok_get_pos ( tok ) ,
TOK_ERR_MSG ,
tok_last ( tok ) ) ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
return ;
}
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
default :
{
2005-12-07 16:06:47 +00:00
error ( SYNTAX_ERROR ,
tok_get_pos ( tok ) ,
2005-12-14 03:39:39 +00:00
CMD_ERR_MSG ,
2005-12-07 16:06:47 +00:00
tok_get_desc ( tok_last_type ( tok ) ) ) ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
return ;
}
}
2006-01-30 19:53:10 +00:00
2005-10-11 19:23:43 +00:00
job_reap ( 0 ) ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
}
2012-01-23 04:47:13 +00:00
int parser_t : : eval ( const wcstring & cmdStr , io_data_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 ( ) ;
2011-12-27 06:51:34 +00:00
size_t forbid_count ;
2005-09-20 13:26:39 +00:00
int code ;
tokenizer * previous_tokenizer = current_tokenizer ;
block_t * start_current_block = current_block ;
io_data_t * prev_io = block_io ;
2011-12-27 06:51:34 +00:00
std : : vector < wcstring > prev_forbidden = forbidden_function ;
2006-01-30 16:51:50 +00:00
if ( block_type = = SUBST )
{
2012-02-16 08:24:27 +00:00
forbidden_function . clear ( ) ;
2006-01-30 16:51:50 +00:00
}
2011-12-27 03:18:46 +00:00
2006-10-29 21:09:11 +00:00
CHECK_BLOCK ( 1 ) ;
2011-12-27 03:18:46 +00:00
2011-12-27 06:51:34 +00:00
forbid_count = forbidden_function . size ( ) ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
block_io = io ;
2006-01-30 19:53:10 +00:00
2005-10-11 19:23:43 +00:00
job_reap ( 0 ) ;
2005-12-12 15:51:04 +00:00
debug ( 4 , L " eval: %ls " , cmd ) ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
if ( ! cmd )
{
debug ( 1 ,
2006-01-04 12:51:02 +00:00
EVAL_NULL_ERR_MSG ) ;
2006-11-17 14:58:25 +00:00
bugreport ( ) ;
2005-09-20 13:26:39 +00:00
return 1 ;
}
2006-02-01 15:49:11 +00:00
if ( ( block_type ! = TOP ) & &
2005-09-20 13:26:39 +00:00
( block_type ! = SUBST ) )
{
debug ( 1 ,
2006-01-04 12:51:02 +00:00
INVALID_SCOPE_ERR_MSG ,
2012-01-23 05:40:08 +00:00
parser_t : : get_block_desc ( block_type ) ) ;
2006-11-17 14:58:25 +00:00
bugreport ( ) ;
2005-12-14 01:07:12 +00:00
return 1 ;
}
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
eval_level + + ;
2006-01-30 19:53:10 +00:00
2012-01-23 04:47:13 +00:00
this - > push_block ( block_type ) ;
2006-01-30 19:53:10 +00:00
2011-12-27 03:18:46 +00:00
current_tokenizer = ( tokenizer * ) malloc ( sizeof ( tokenizer ) ) ;
2005-09-20 13:26:39 +00:00
tok_init ( current_tokenizer , cmd , 0 ) ;
2006-02-01 15:49:11 +00:00
2005-09-20 13:26:39 +00:00
error_code = 0 ;
2006-01-30 19:53:10 +00:00
2010-09-18 13:30:02 +00:00
event_fire ( NULL ) ;
2005-10-14 11:40:33 +00:00
2005-09-20 13:26:39 +00:00
while ( tok_has_next ( current_tokenizer ) & &
! error_code & &
! sanity_check ( ) & &
! exit_status ( ) )
2005-10-14 11:40:33 +00:00
{
2012-01-23 04:47:13 +00:00
this - > eval_job ( current_tokenizer ) ;
2010-09-18 13:30:02 +00:00
event_fire ( NULL ) ;
2005-10-14 11:40:33 +00:00
}
2006-01-30 19:53:10 +00:00
2012-01-23 05:40:08 +00:00
parser_t : : pop_block ( ) ;
2005-09-20 13:26:39 +00:00
while ( start_current_block ! = current_block )
{
if ( current_block = = 0 )
{
debug ( 0 ,
2006-01-04 12:51:02 +00:00
_ ( L " End of block mismatch. Program terminating. " ) ) ;
2006-11-17 14:58:25 +00:00
bugreport ( ) ;
2007-01-21 15:03:41 +00:00
FATAL_EXIT ( ) ;
2005-09-20 13:26:39 +00:00
break ;
}
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
if ( ( ! error_code ) & & ( ! exit_status ( ) ) & & ( ! proc_get_last_status ( ) ) )
{
//debug( 2, L"Status %d\n", proc_get_last_status() );
2006-01-30 19:53:10 +00:00
debug ( 1 ,
2012-01-23 05:40:08 +00:00
L " %ls " , parser_t : : get_block_desc ( current_block - > type ) ) ;
2006-01-30 19:53:10 +00:00
debug ( 1 ,
2006-01-05 15:37:53 +00:00
BLOCK_END_ERR_MSG ) ;
2012-01-23 04:47:13 +00:00
fwprintf ( stderr , L " %ls " , parser_t : : current_line ( ) ) ;
2006-01-30 19:53:10 +00:00
2012-02-01 04:22:25 +00:00
const wcstring h = builtin_help_get ( * this , L " end " ) ;
if ( h . size ( ) )
fwprintf ( stderr , L " %ls " , h . c_str ( ) ) ;
2006-01-05 15:37:53 +00:00
break ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
}
2012-01-23 05:40:08 +00:00
parser_t : : pop_block ( ) ;
2005-09-20 13:26:39 +00:00
}
2012-01-23 05:57:30 +00:00
this - > print_errors_stderr ( ) ;
2005-09-20 13:26:39 +00:00
tok_destroy ( current_tokenizer ) ;
free ( current_tokenizer ) ;
2011-12-27 06:51:34 +00:00
while ( forbidden_function . size ( ) > forbid_count )
2012-01-23 05:40:08 +00:00
parser_t : : allow_function ( ) ;
2005-09-20 13:26:39 +00:00
2006-01-30 19:53:10 +00:00
/*
2006-04-20 18:35:02 +00:00
Restore previous eval state
2006-01-30 16:51:50 +00:00
*/
2006-01-30 19:53:10 +00:00
forbidden_function = prev_forbidden ;
2005-09-20 13:26:39 +00:00
current_tokenizer = previous_tokenizer ;
2006-01-30 19:53:10 +00:00
block_io = prev_io ;
2006-01-30 16:51:50 +00:00
eval_level - - ;
2005-09-20 13:26:39 +00:00
code = error_code ;
error_code = 0 ;
2005-10-11 19:23:43 +00:00
job_reap ( 0 ) ;
2006-01-30 16:51:50 +00:00
2005-09-20 13:26:39 +00:00
return code ;
}
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-02-01 05:30:09 +00:00
int parser_get_block_type ( const wcstring & cmd )
2006-05-26 11:25:25 +00:00
{
2006-06-16 12:56:16 +00:00
int i ;
2011-12-27 03:18:46 +00:00
2006-06-16 12:56:16 +00:00
for ( i = 0 ; block_lookup [ i ] . desc ; i + + )
{
2012-02-01 05:30:09 +00:00
if ( block_lookup [ i ] . name & & cmd = = block_lookup [ i ] . name )
2006-06-16 12:56:16 +00:00
{
return block_lookup [ i ] . type ;
}
}
return - 1 ;
}
/**
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
*/
2006-09-05 20:43:47 +00:00
const wchar_t * parser_get_block_command ( int type )
2006-06-16 12:56:16 +00:00
{
int i ;
2011-12-27 03:18:46 +00:00
2006-06-16 12:56:16 +00:00
for ( i = 0 ; block_lookup [ i ] . desc ; i + + )
{
if ( block_lookup [ i ] . type = = type )
{
return block_lookup [ i ] . name ;
}
}
return 0 ;
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-02-22 18:51:06 +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
{
wchar_t * unesc ;
wchar_t * pos ;
int err = 0 ;
2011-12-27 03:18:46 +00:00
2006-06-14 13:22:40 +00:00
wchar_t * paran_begin , * paran_end ;
2006-06-08 23:52:12 +00:00
wchar_t * arg_cpy ;
2006-05-21 19:25:24 +00:00
int do_loop = 1 ;
2011-12-27 03:18:46 +00:00
2006-06-21 00:48:36 +00:00
CHECK ( arg , 1 ) ;
2011-12-27 03:18:46 +00:00
2006-06-08 23:52:12 +00:00
arg_cpy = wcsdup ( arg ) ;
2011-12-27 03:18:46 +00:00
2006-05-21 19:25:24 +00:00
while ( do_loop )
{
switch ( parse_util_locate_cmdsubst ( arg_cpy ,
& paran_begin ,
& paran_end ,
0 ) )
{
case - 1 :
err = 1 ;
2006-06-02 02:15:17 +00:00
if ( out )
2006-05-21 19:25:24 +00:00
{
error ( SYNTAX_ERROR ,
2006-05-31 15:41:47 +00:00
offset ,
2006-05-21 19:25:24 +00:00
L " Mismatched parans " ) ;
2012-02-22 18:51:06 +00:00
this - > print_errors ( * out , prefix ) ;
2006-05-21 19:25:24 +00:00
}
free ( arg_cpy ) ;
2012-03-26 08:21:10 +00:00
return err ;
2011-12-27 03:18:46 +00:00
2006-05-21 19:25:24 +00:00
case 0 :
do_loop = 0 ;
break ;
case 1 :
{
2011-12-27 03:18:46 +00:00
2006-05-21 19:25:24 +00:00
wchar_t * subst = wcsndup ( paran_begin + 1 , paran_end - paran_begin - 1 ) ;
2012-02-22 20:00:02 +00:00
wcstring tmp ;
2011-12-27 03:18:46 +00:00
2012-02-22 20:00:02 +00:00
tmp . append ( arg_cpy , paran_begin - arg_cpy ) ;
tmp . push_back ( INTERNAL_SEPARATOR ) ;
tmp . append ( paran_end + 1 ) ;
2011-12-27 03:18:46 +00:00
2006-05-21 19:25:24 +00:00
// debug( 1, L"%ls -> %ls %ls", arg_cpy, subst, tmp.buff );
2011-12-27 03:18:46 +00:00
2012-01-23 05:40:08 +00:00
err | = parser_t : : test ( subst , 0 , out , prefix ) ;
2011-12-27 03:18:46 +00:00
2006-05-21 19:25:24 +00:00
free ( subst ) ;
free ( arg_cpy ) ;
2012-02-22 20:00:02 +00:00
arg_cpy = wcsdup ( tmp . c_str ( ) ) ;
2011-12-27 03:18:46 +00:00
2006-05-21 19:25:24 +00:00
/*
Do _not_ call sb_destroy on this stringbuffer - it ' s
2006-05-31 15:41:47 +00:00
buffer is used as the new ' arg_cpy ' . It is free ' d at
the end of the loop .
2006-05-21 19:25:24 +00:00
*/
break ;
2011-12-27 03:18:46 +00:00
}
2006-05-21 19:25:24 +00:00
}
2006-05-31 15:41:47 +00:00
}
2006-05-21 19:25:24 +00:00
unesc = unescape ( arg_cpy , 1 ) ;
2006-06-15 01:11:54 +00:00
if ( ! unesc )
2006-05-21 19:25:24 +00:00
{
2006-06-15 01:11:54 +00:00
if ( out )
{
error ( SYNTAX_ERROR ,
offset ,
L " Invalid token '%ls' " , arg_cpy ) ;
2012-02-22 18:51:06 +00:00
print_errors ( * out , prefix ) ;
2006-06-15 01:11:54 +00:00
}
return 1 ;
}
else
2011-12-27 03:18:46 +00:00
{
2006-06-15 01:11:54 +00:00
/*
Check for invalid variable expansions
*/
for ( pos = unesc ; * pos ; pos + + )
2006-05-21 19:25:24 +00:00
{
2006-06-15 01:11:54 +00:00
switch ( * pos )
2006-05-21 19:25:24 +00:00
{
2006-06-15 01:11:54 +00:00
case VARIABLE_EXPAND :
case VARIABLE_EXPAND_SINGLE :
2006-05-21 19:25:24 +00:00
{
2006-07-20 13:02:46 +00:00
wchar_t n = * ( pos + 1 ) ;
2011-12-27 03:18:46 +00:00
2006-07-20 13:02:46 +00:00
if ( n ! = VARIABLE_EXPAND & &
n ! = VARIABLE_EXPAND_SINGLE & &
! wcsvarchr ( n ) )
2006-05-21 19:25:24 +00:00
{
2006-07-20 13:02:46 +00:00
err = 1 ;
if ( out )
2006-06-15 01:11:54 +00:00
{
2012-01-23 04:47:13 +00:00
expand_variable_error ( * this , unesc , pos - unesc , offset ) ;
2012-02-22 18:51:06 +00:00
print_errors ( * out , prefix ) ;
2006-06-15 01:11:54 +00:00
}
}
2011-12-27 03:18:46 +00:00
2006-06-15 01:11:54 +00:00
break ;
}
2011-12-27 03:18:46 +00:00
}
2006-05-21 19:25:24 +00:00
}
}
2006-06-15 01:11:54 +00:00
free ( arg_cpy ) ;
2011-12-27 03:18:46 +00:00
2006-05-21 19:25:24 +00:00
free ( unesc ) ;
return err ;
2011-12-27 03:18:46 +00:00
2006-05-21 19:25:24 +00:00
}
2012-02-22 18:51:06 +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
{
tokenizer tok ;
tokenizer * previous_tokenizer = current_tokenizer ;
int previous_pos = current_tokenizer_pos ;
int do_loop = 1 ;
int err = 0 ;
2011-12-27 03:18:46 +00:00
2006-06-21 00:48:36 +00:00
CHECK ( buff , 1 ) ;
2011-12-27 03:18:46 +00:00
2006-05-21 19:25:24 +00:00
current_tokenizer = & tok ;
2011-12-27 03:18:46 +00:00
2006-05-21 19:25:24 +00:00
for ( tok_init ( & tok , buff , 0 ) ;
do_loop & & tok_has_next ( & tok ) ;
tok_next ( & tok ) )
{
current_tokenizer_pos = tok_get_pos ( & tok ) ;
switch ( tok_last_type ( & tok ) )
{
2011-12-27 03:18:46 +00:00
2006-05-21 19:25:24 +00:00
case TOK_STRING :
{
2006-06-02 02:15:17 +00:00
err | = parser_test_argument ( tok_last ( & tok ) , out , prefix , tok_get_pos ( & tok ) ) ;
2006-05-21 19:25:24 +00:00
break ;
}
2011-12-27 03:18:46 +00:00
2006-05-21 19:25:24 +00:00
case TOK_END :
{
break ;
}
2011-12-27 03:18:46 +00:00
2006-05-21 19:25:24 +00:00
case TOK_ERROR :
{
2006-06-02 02:15:17 +00:00
if ( out )
2006-05-21 19:25:24 +00:00
{
error ( SYNTAX_ERROR ,
tok_get_pos ( & tok ) ,
TOK_ERR_MSG ,
tok_last ( & tok ) ) ;
2012-02-22 18:51:06 +00:00
print_errors ( * out , prefix ) ;
2006-05-21 19:25:24 +00:00
}
err = 1 ;
do_loop = 0 ;
break ;
}
2011-12-27 03:18:46 +00:00
2006-05-21 19:25:24 +00:00
default :
{
2006-06-02 02:15:17 +00:00
if ( out )
2006-05-21 19:25:24 +00:00
{
error ( SYNTAX_ERROR ,
tok_get_pos ( & tok ) ,
UNEXPECTED_TOKEN_ERR_MSG ,
tok_get_desc ( tok_last_type ( & tok ) ) ) ;
2012-02-22 18:51:06 +00:00
print_errors ( * out , prefix ) ;
2006-05-21 19:25:24 +00:00
}
2011-12-27 03:18:46 +00:00
err = 1 ;
2006-05-21 19:25:24 +00:00
do_loop = 0 ;
break ;
2011-12-27 03:18:46 +00:00
}
2006-05-21 19:25:24 +00:00
}
}
2011-12-27 03:18:46 +00:00
2006-05-21 19:25:24 +00:00
tok_destroy ( & tok ) ;
2011-12-27 03:18:46 +00:00
2006-05-21 19:25:24 +00:00
current_tokenizer = previous_tokenizer ;
current_tokenizer_pos = previous_pos ;
2011-12-27 03:18:46 +00:00
2006-05-21 19:25:24 +00:00
error_code = 0 ;
2011-12-27 03:18:46 +00:00
2006-05-21 19:25:24 +00:00
return err ;
}
2012-01-23 05:40:08 +00:00
int parser_t : : test ( const wchar_t * buff ,
2011-12-27 03:18:46 +00:00
int * block_level ,
2012-02-22 18:51:06 +00:00
wcstring * out ,
2006-06-05 00:42:01 +00:00
const wchar_t * prefix )
2005-09-20 13:26:39 +00:00
{
2011-12-27 03:18:46 +00:00
ASSERT_IS_MAIN_THREAD ( ) ;
2005-09-20 13:26:39 +00:00
tokenizer tok ;
2011-12-27 03:18:46 +00:00
/*
2006-05-21 19:25:24 +00:00
Set to one if a command name has been given for the currently
2011-12-27 03:18:46 +00:00
parsed process specification
2006-05-21 19:25:24 +00:00
*/
2011-12-27 03:18:46 +00:00
int had_cmd = 0 ;
2005-09-20 13:26:39 +00:00
int count = 0 ;
int err = 0 ;
2006-10-07 00:56:25 +00:00
int unfinished = 0 ;
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
tokenizer * previous_tokenizer = current_tokenizer ;
int previous_pos = current_tokenizer_pos ;
static int block_pos [ BLOCK_MAX_COUNT ] ;
static int block_type [ BLOCK_MAX_COUNT ] ;
2006-10-09 10:34:08 +00:00
int res = 0 ;
2011-12-27 03:18:46 +00:00
2006-07-21 01:08:31 +00:00
/*
Set to 1 if the current command is inside a pipeline
*/
2005-09-20 13:26:39 +00:00
int is_pipeline = 0 ;
2006-07-21 01:08:31 +00:00
2006-05-21 19:25:24 +00:00
/*
Set to one if the currently specified process can not be used inside a pipeline
*/
2005-09-20 13:26:39 +00:00
int forbid_pipeline = 0 ;
2006-07-21 01:08:31 +00:00
2011-12-27 03:18:46 +00:00
/*
Set to one if an additional process specification is needed
2006-05-21 19:25:24 +00:00
*/
2011-12-27 03:18:46 +00:00
int needs_cmd = 0 ;
2006-07-21 01:08:31 +00:00
/*
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.
*/
2006-05-21 22:16:04 +00:00
int arg_count = 0 ;
2011-12-27 03:18:46 +00:00
2006-07-21 01:08:31 +00:00
/*
The currently validated command .
*/
2012-02-01 05:30:09 +00:00
wcstring command ;
bool has_command = false ;
2011-12-27 03:18:46 +00:00
2006-06-21 00:48:36 +00:00
CHECK ( buff , 1 ) ;
2006-10-07 00:56:25 +00:00
if ( block_level )
{
int i ;
int len = wcslen ( buff ) ;
for ( i = 0 ; i < len ; i + + )
{
block_level [ i ] = - 1 ;
}
2011-12-27 03:18:46 +00:00
2006-10-07 00:56:25 +00:00
}
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
current_tokenizer = & tok ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
for ( tok_init ( & tok , buff , 0 ) ;
2006-10-01 16:02:58 +00:00
;
2005-09-20 13:26:39 +00:00
tok_next ( & tok ) )
{
current_tokenizer_pos = tok_get_pos ( & tok ) ;
int last_type = tok_last_type ( & tok ) ;
2006-09-26 12:41:09 +00:00
int end_of_cmd = 0 ;
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
switch ( last_type )
{
case TOK_STRING :
{
if ( ! had_cmd )
{
2007-01-31 23:18:20 +00:00
int is_else ;
2005-09-20 13:26:39 +00:00
int mark = tok_get_pos ( & tok ) ;
had_cmd = 1 ;
2006-05-21 22:16:04 +00:00
arg_count = 0 ;
2011-12-27 03:18:46 +00:00
2012-02-01 05:30:09 +00:00
command = tok_last ( & tok ) ;
has_command = expand_one ( command , EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_VARIABLES ) ;
if ( ! has_command )
2006-05-21 19:25:24 +00:00
{
2012-02-01 05:30:09 +00:00
command = L " " ;
2006-05-21 19:25:24 +00:00
err = 1 ;
2006-06-02 02:15:17 +00:00
if ( out )
2006-05-21 19:25:24 +00:00
{
error ( SYNTAX_ERROR ,
tok_get_pos ( & tok ) ,
ILLEGAL_CMD_ERR_MSG ,
2006-07-03 10:46:47 +00:00
tok_last ( & tok ) ) ;
2011-12-27 03:18:46 +00:00
2012-02-22 18:51:06 +00:00
print_errors ( * out , prefix ) ;
2006-07-03 10:46:47 +00:00
}
break ;
2006-05-21 19:25:24 +00:00
}
2011-12-27 03:18:46 +00:00
2006-05-21 19:25:24 +00:00
if ( needs_cmd )
2005-09-20 13:26:39 +00:00
{
2006-05-26 11:25:25 +00:00
/*
end is not a valid command when a followup
command is needed , such as after ' and ' or
' while '
*/
2012-02-01 05:30:09 +00:00
if ( contains ( command ,
2007-04-16 21:40:41 +00:00
L " end " ) )
2005-09-20 13:26:39 +00:00
{
err = 1 ;
2006-06-02 02:15:17 +00:00
if ( out )
2006-01-30 19:53:10 +00:00
{
2005-09-20 13:26:39 +00:00
error ( SYNTAX_ERROR ,
2005-12-07 16:06:47 +00:00
tok_get_pos ( & tok ) ,
COND_ERR_MSG ) ;
2011-12-27 03:18:46 +00:00
2012-02-22 18:51:06 +00:00
print_errors ( * out , prefix ) ;
2005-09-20 13:26:39 +00:00
}
}
2011-12-27 03:18:46 +00:00
2006-05-21 19:25:24 +00:00
needs_cmd = 0 ;
2005-09-20 13:26:39 +00:00
}
2011-12-27 03:18:46 +00:00
2005-09-25 11:25:42 +00:00
/*
Decrement block count on end command
*/
2012-02-01 05:30:09 +00:00
if ( command = = L " end " )
2005-09-20 13:26:39 +00:00
{
tok_next ( & tok ) ;
count - - ;
tok_set_pos ( & tok , mark ) ;
}
2006-01-30 19:53:10 +00:00
2012-02-01 05:30:09 +00:00
is_else = ( command = = L " else " ) ;
2011-12-27 03:18:46 +00:00
2006-10-07 00:56:25 +00:00
/*
Store the block level . This needs to be done
_after_ checking for end commands , but _before_
shecking for block opening commands .
*/
if ( block_level )
{
2007-01-31 23:18:20 +00:00
block_level [ tok_get_pos ( & tok ) ] = count + ( is_else ? - 1 : 0 ) ;
2006-10-07 00:56:25 +00:00
}
2011-12-27 03:18:46 +00:00
2005-09-25 11:25:42 +00:00
/*
Handle block commands
*/
2012-02-01 05:30:09 +00:00
if ( parser_keywords_is_block ( command ) )
2005-09-20 13:26:39 +00:00
{
if ( count > = BLOCK_MAX_COUNT )
{
2012-02-22 18:51:06 +00:00
if ( out ) {
error ( SYNTAX_ERROR ,
tok_get_pos ( & tok ) ,
BLOCK_ERR_MSG ) ;
2006-01-30 19:53:10 +00:00
2012-02-22 18:51:06 +00:00
print_errors ( * out , prefix ) ;
}
2005-09-20 13:26:39 +00:00
}
else
{
2012-02-01 05:30:09 +00:00
block_type [ count ] = parser_get_block_type ( command ) ;
2005-09-20 13:26:39 +00:00
block_pos [ count ] = current_tokenizer_pos ;
tok_next ( & tok ) ;
count + + ;
tok_set_pos ( & tok , mark ) ;
}
}
2006-01-30 19:53:10 +00:00
2005-09-25 11:25:42 +00:00
/*
2007-04-22 09:50:26 +00:00
If parser_keywords_is_subcommand is true , the command
2005-09-25 11:25:42 +00:00
accepts a second command as it ' s first
argument . If parser_skip_arguments is true , the
second argument is optional .
*/
2012-02-01 05:30:09 +00:00
if ( parser_keywords_is_subcommand ( command ) & & ! parser_keywords_skip_arguments ( command ) )
2005-09-20 13:26:39 +00:00
{
needs_cmd = 1 ;
had_cmd = 0 ;
}
2011-12-27 03:18:46 +00:00
2012-02-01 05:30:09 +00:00
if ( contains ( command ,
2007-04-16 21:40:41 +00:00
L " or " ,
L " and " ) )
2005-09-20 13:26:39 +00:00
{
2006-05-26 11:25:25 +00:00
/*
' or ' and ' and ' can not be used inside pipelines
*/
2005-09-20 13:26:39 +00:00
if ( is_pipeline )
{
err = 1 ;
2006-06-02 02:15:17 +00:00
if ( out )
2006-01-30 19:53:10 +00:00
{
2005-09-20 13:26:39 +00:00
error ( SYNTAX_ERROR ,
2005-12-07 16:06:47 +00:00
tok_get_pos ( & tok ) ,
EXEC_ERR_MSG ) ;
2006-01-30 19:53:10 +00:00
2012-02-22 18:51:06 +00:00
print_errors ( * out , prefix ) ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
}
}
}
2011-12-27 03:18:46 +00:00
2005-09-25 11:25:42 +00:00
/*
There are a lot of situations where pipelines
2006-05-21 19:25:24 +00:00
are forbidden , including when using the exec
2005-09-25 11:25:42 +00:00
builtin .
*/
2012-02-01 05:30:09 +00:00
if ( parser_is_pipe_forbidden ( command ) )
2005-09-20 13:26:39 +00:00
{
if ( is_pipeline )
{
err = 1 ;
2006-06-02 02:15:17 +00:00
if ( out )
2006-01-30 19:53:10 +00:00
{
2005-09-20 13:26:39 +00:00
error ( SYNTAX_ERROR ,
2005-12-07 16:06:47 +00:00
tok_get_pos ( & tok ) ,
EXEC_ERR_MSG ) ;
2006-01-30 19:53:10 +00:00
2012-02-22 18:51:06 +00:00
print_errors ( * out , prefix ) ;
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
}
}
2006-01-30 19:53:10 +00:00
forbid_pipeline = 1 ;
2005-09-20 13:26:39 +00:00
}
2006-01-30 19:53:10 +00:00
2005-09-25 11:25:42 +00:00
/*
2006-05-26 11:25:25 +00:00
Test that the case builtin is only used directly in a switch block
2005-09-25 11:25:42 +00:00
*/
2012-02-01 05:30:09 +00:00
if ( command = = L " case " )
2005-09-20 13:26:39 +00:00
{
if ( ! count | | block_type [ count - 1 ] ! = SWITCH )
{
err = 1 ;
2006-01-30 19:53:10 +00:00
2006-06-02 02:15:17 +00:00
if ( out )
2005-12-14 01:07:12 +00:00
{
error ( SYNTAX_ERROR ,
tok_get_pos ( & tok ) ,
2005-12-13 18:28:03 +00:00
INVALID_CASE_ERR_MSG ) ;
2006-01-30 19:53:10 +00:00
2012-02-22 18:51:06 +00:00
print_errors ( * out , prefix ) ;
2012-02-01 04:22:25 +00:00
const wcstring h = builtin_help_get ( * this , L " case " ) ;
if ( h . size ( ) )
2012-02-22 18:51:06 +00:00
append_format ( * out , L " %ls " , h . c_str ( ) ) ;
2005-12-14 01:07:12 +00:00
}
}
2006-01-30 19:53:10 +00:00
}
2005-09-25 11:25:42 +00:00
2006-09-18 00:15:18 +00:00
/*
Test that the return bultin is only used within function definitions
*/
2012-02-01 05:30:09 +00:00
if ( command = = L " return " )
2006-09-18 00:15:18 +00:00
{
int found_func = 0 ;
int i ;
for ( i = count - 1 ; i > = 0 ; i - - )
{
if ( block_type [ i ] = = FUNCTION_DEF )
{
found_func = 1 ;
break ;
}
}
if ( ! found_func )
{
/*
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 ;
2011-12-27 03:18:46 +00:00
2006-09-18 00:15:18 +00:00
tok_next ( & tok ) ;
if ( tok_last_type ( & tok ) = = TOK_STRING )
{
2012-02-01 05:30:09 +00:00
wcstring first_arg = tok_last ( & tok ) ;
if ( expand_one ( first_arg , EXPAND_SKIP_CMDSUBST ) & & parser_t : : is_help ( first_arg . c_str ( ) , 3 ) )
2006-09-18 00:15:18 +00:00
{
is_help = 1 ;
}
}
2011-12-27 03:18:46 +00:00
2006-09-18 00:15:18 +00:00
tok_set_pos ( & tok , old_pos ) ;
2011-12-27 03:18:46 +00:00
2006-09-18 00:15:18 +00:00
if ( ! is_help )
{
err = 1 ;
if ( out )
{
error ( SYNTAX_ERROR ,
tok_get_pos ( & tok ) ,
INVALID_RETURN_ERR_MSG ) ;
2012-02-22 18:51:06 +00:00
print_errors ( * out , prefix ) ;
2006-09-18 00:15:18 +00:00
}
}
}
}
2011-12-27 03:18:46 +00:00
2006-09-18 00:15:18 +00:00
2005-09-25 11:25:42 +00:00
/*
2006-05-26 11:25:25 +00:00
Test that break and continue are only used within loop blocks
2005-09-25 11:25:42 +00:00
*/
2012-02-01 05:30:09 +00:00
if ( contains ( command , L " break " , L " continue " ) )
2005-09-20 13:26:39 +00:00
{
int found_loop = 0 ;
int i ;
for ( i = count - 1 ; i > = 0 ; i - - )
{
if ( ( block_type [ i ] = = WHILE ) | |
( block_type [ i ] = = FOR ) )
{
found_loop = 1 ;
break ;
}
}
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
if ( ! found_loop )
{
2006-09-18 00:15:18 +00:00
/*
Peek to see if the next argument is
- - help , in which case we ' ll allow it to
show the help .
*/
2006-01-30 19:53:10 +00:00
2006-09-18 00:15:18 +00:00
int old_pos = tok_get_pos ( & tok ) ;
int is_help = 0 ;
2011-12-27 03:18:46 +00:00
2006-09-18 00:15:18 +00:00
tok_next ( & tok ) ;
if ( tok_last_type ( & tok ) = = TOK_STRING )
2005-12-14 01:07:12 +00:00
{
2012-02-01 05:30:09 +00:00
wcstring first_arg = tok_last ( & tok ) ;
if ( expand_one ( first_arg , EXPAND_SKIP_CMDSUBST ) & & parser_t : : is_help ( first_arg . c_str ( ) , 3 ) )
2006-09-18 00:15:18 +00:00
{
is_help = 1 ;
}
}
2011-12-27 03:18:46 +00:00
2006-09-18 00:15:18 +00:00
tok_set_pos ( & tok , old_pos ) ;
2011-12-27 03:18:46 +00:00
2006-09-18 00:15:18 +00:00
if ( ! is_help )
{
err = 1 ;
if ( out )
{
error ( SYNTAX_ERROR ,
tok_get_pos ( & tok ) ,
INVALID_LOOP_ERR_MSG ) ;
2012-02-22 18:51:06 +00:00
print_errors ( * out , prefix ) ;
2006-09-18 00:15:18 +00:00
}
2005-12-14 01:07:12 +00:00
}
2006-01-30 19:53:10 +00:00
}
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
}
2006-01-30 19:53:10 +00:00
2005-09-25 11:25:42 +00:00
/*
2006-05-26 11:25:25 +00:00
Test that else is only used directly in an if - block
2005-09-25 11:25:42 +00:00
*/
2012-02-01 05:30:09 +00:00
if ( command = = L " else " )
2005-09-20 13:26:39 +00:00
{
if ( ! count | | block_type [ count - 1 ] ! = IF )
2005-12-14 01:07:12 +00:00
{
err = 1 ;
2006-06-02 02:15:17 +00:00
if ( out )
2005-12-14 01:07:12 +00:00
{
error ( SYNTAX_ERROR ,
tok_get_pos ( & tok ) ,
2005-12-13 18:28:03 +00:00
INVALID_ELSE_ERR_MSG ) ;
2006-01-30 19:53:10 +00:00
2012-02-22 18:51:06 +00:00
print_errors ( * out , prefix ) ;
2005-12-14 01:07:12 +00:00
}
}
2005-09-20 13:26:39 +00:00
}
2006-01-30 19:53:10 +00:00
2005-09-25 11:25:42 +00:00
/*
2006-05-26 11:25:25 +00:00
Test that end is not used when not inside any block
2005-09-25 11:25:42 +00:00
*/
2005-09-20 13:26:39 +00:00
if ( count < 0 )
{
err = 1 ;
2006-06-02 02:15:17 +00:00
if ( out )
2005-09-20 13:26:39 +00:00
{
error ( SYNTAX_ERROR ,
2005-12-07 16:06:47 +00:00
tok_get_pos ( & tok ) ,
2005-12-13 20:11:21 +00:00
INVALID_END_ERR_MSG ) ;
2012-02-22 18:51:06 +00:00
print_errors ( * out , prefix ) ;
2012-02-01 04:22:25 +00:00
const wcstring h = builtin_help_get ( * this , L " end " ) ;
if ( h . size ( ) )
2012-02-22 18:51:06 +00:00
append_format ( * out , L " %ls " , h . c_str ( ) ) ;
2006-01-30 19:53:10 +00:00
}
2005-09-20 13:26:39 +00:00
}
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
}
2006-05-21 19:25:24 +00:00
else
{
2006-06-02 02:15:17 +00:00
err | = parser_test_argument ( tok_last ( & tok ) , out , prefix , tok_get_pos ( & tok ) ) ;
2011-12-27 03:18:46 +00:00
2006-05-26 11:25:25 +00:00
/*
If possible , keep track of number of supplied arguments
*/
2006-05-21 22:16:04 +00:00
if ( arg_count > = 0 & & expand_is_clean ( tok_last ( & tok ) ) )
{
arg_count + + ;
}
else
{
arg_count = - 1 ;
}
2011-12-27 03:18:46 +00:00
2012-02-01 05:30:09 +00:00
if ( has_command )
2006-05-21 22:16:04 +00:00
{
2011-12-27 03:18:46 +00:00
2006-09-07 20:15:59 +00:00
/*
Try to make sure the second argument to ' for ' is ' in '
*/
2012-02-01 05:30:09 +00:00
if ( command = = L " for " )
2006-05-21 22:16:04 +00:00
{
2006-10-01 15:57:34 +00:00
if ( arg_count = = 1 )
{
2011-12-27 03:18:46 +00:00
2006-10-01 15:57:34 +00:00
if ( wcsvarname ( tok_last ( & tok ) ) )
{
2011-12-27 03:18:46 +00:00
2006-10-01 15:57:34 +00:00
err = 1 ;
2011-12-27 03:18:46 +00:00
2006-10-01 15:57:34 +00:00
if ( out )
{
error ( SYNTAX_ERROR ,
tok_get_pos ( & tok ) ,
BUILTIN_FOR_ERR_NAME ,
L " for " ,
tok_last ( & tok ) ) ;
2011-12-27 03:18:46 +00:00
2012-02-22 18:51:06 +00:00
print_errors ( * out , prefix ) ;
2011-12-27 03:18:46 +00:00
}
2006-10-01 15:57:34 +00:00
}
2011-12-27 03:18:46 +00:00
2006-10-01 15:57:34 +00:00
}
else if ( arg_count = = 2 )
2006-05-21 22:16:04 +00:00
{
2006-09-07 20:15:59 +00:00
if ( wcscmp ( tok_last ( & tok ) , L " in " ) ! = 0 )
2006-05-21 22:16:04 +00:00
{
2006-09-07 20:15:59 +00:00
err = 1 ;
2011-12-27 03:18:46 +00:00
2006-09-07 20:15:59 +00:00
if ( out )
{
error ( SYNTAX_ERROR ,
tok_get_pos ( & tok ) ,
BUILTIN_FOR_ERR_IN ,
L " for " ) ;
2011-12-27 03:18:46 +00:00
2012-02-22 18:51:06 +00:00
print_errors ( * out , prefix ) ;
2006-09-07 20:15:59 +00:00
}
2006-05-21 22:16:04 +00:00
}
}
}
}
2006-09-05 20:43:47 +00:00
2006-05-21 19:25:24 +00:00
}
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
break ;
}
case TOK_REDIRECT_OUT :
case TOK_REDIRECT_IN :
case TOK_REDIRECT_APPEND :
case TOK_REDIRECT_FD :
2007-10-26 18:42:32 +00:00
case TOK_REDIRECT_NOCLOB :
2005-09-20 13:26:39 +00:00
{
if ( ! had_cmd )
{
err = 1 ;
2006-06-02 02:15:17 +00:00
if ( out )
2005-09-20 13:26:39 +00:00
{
error ( SYNTAX_ERROR ,
2005-12-07 16:06:47 +00:00
tok_get_pos ( & tok ) ,
2005-12-14 03:39:39 +00:00
INVALID_REDIRECTION_ERR_MSG ) ;
2012-02-22 18:51:06 +00:00
print_errors ( * out , prefix ) ;
2005-09-20 13:26:39 +00:00
}
}
break ;
}
case TOK_END :
{
if ( needs_cmd & & ! had_cmd )
{
2006-01-30 19:53:10 +00:00
err = 1 ;
2006-06-02 02:15:17 +00:00
if ( out )
2005-12-14 01:07:12 +00:00
{
error ( SYNTAX_ERROR ,
tok_get_pos ( & tok ) ,
2005-12-14 03:39:39 +00:00
CMD_ERR_MSG ,
tok_get_desc ( tok_last_type ( & tok ) ) ) ;
2012-02-22 18:51:06 +00:00
print_errors ( * out , prefix ) ;
2005-12-14 01:07:12 +00:00
}
2006-01-30 19:53:10 +00:00
}
needs_cmd = 0 ;
2005-09-20 13:26:39 +00:00
had_cmd = 0 ;
2006-01-30 19:53:10 +00:00
is_pipeline = 0 ;
2005-09-20 13:26:39 +00:00
forbid_pipeline = 0 ;
2006-09-26 12:41:09 +00:00
end_of_cmd = 1 ;
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
break ;
}
case TOK_PIPE :
{
2006-05-21 19:25:24 +00:00
if ( ! had_cmd )
{
err = 1 ;
2006-06-02 02:15:17 +00:00
if ( out )
2006-05-21 19:25:24 +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 ) ) ) ;
2011-12-27 03:18:46 +00:00
2006-05-21 19:25:24 +00:00
}
else
{
error ( SYNTAX_ERROR ,
tok_get_pos ( & tok ) ,
CMD_ERR_MSG ,
tok_get_desc ( tok_last_type ( & tok ) ) ) ;
}
2011-12-27 03:18:46 +00:00
2012-02-22 18:51:06 +00:00
print_errors ( * out , prefix ) ;
2006-05-21 19:25:24 +00:00
}
}
else if ( forbid_pipeline )
2005-09-20 13:26:39 +00:00
{
err = 1 ;
2006-06-02 02:15:17 +00:00
if ( out )
2006-01-30 19:53:10 +00:00
{
2005-09-20 13:26:39 +00:00
error ( SYNTAX_ERROR ,
2005-12-07 16:06:47 +00:00
tok_get_pos ( & tok ) ,
EXEC_ERR_MSG ) ;
2011-12-27 03:18:46 +00:00
2012-02-22 18:51:06 +00:00
print_errors ( * out , prefix ) ;
2005-09-20 13:26:39 +00:00
}
}
2006-05-21 19:25:24 +00:00
else
{
needs_cmd = 1 ;
is_pipeline = 1 ;
had_cmd = 0 ;
2006-09-26 12:41:09 +00:00
end_of_cmd = 1 ;
2011-12-27 03:18:46 +00:00
2006-05-21 19:25:24 +00:00
}
break ;
2005-09-20 13:26:39 +00:00
}
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
case TOK_BACKGROUND :
{
2006-05-21 19:25:24 +00:00
if ( ! had_cmd )
2005-09-20 13:26:39 +00:00
{
2006-01-30 19:53:10 +00:00
err = 1 ;
2006-06-02 02:15:17 +00:00
if ( out )
2005-12-14 01:07:12 +00:00
{
2006-05-21 19:25:24 +00:00
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 ) ) ) ;
2011-12-27 03:18:46 +00:00
2006-05-21 19:25:24 +00:00
}
else
{
error ( SYNTAX_ERROR ,
tok_get_pos ( & tok ) ,
CMD_ERR_MSG ,
tok_get_desc ( tok_last_type ( & tok ) ) ) ;
}
2011-12-27 03:18:46 +00:00
2012-02-22 18:51:06 +00:00
print_errors ( * out , prefix ) ;
2005-12-14 01:07:12 +00:00
}
2006-01-30 19:53:10 +00:00
}
2011-12-27 03:18:46 +00:00
2006-09-18 00:15:18 +00:00
had_cmd = 0 ;
2006-09-26 12:41:09 +00:00
end_of_cmd = 1 ;
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
break ;
}
case TOK_ERROR :
default :
2006-10-07 00:56:25 +00:00
if ( tok_get_error ( & tok ) = = TOK_UNTERMINATED_QUOTE )
2005-09-20 13:26:39 +00:00
{
2006-10-07 00:56:25 +00:00
unfinished = 1 ;
2005-09-20 13:26:39 +00:00
}
2006-10-07 00:56:25 +00:00
else
{
err = 1 ;
if ( out )
{
error ( SYNTAX_ERROR ,
tok_get_pos ( & tok ) ,
TOK_ERR_MSG ,
tok_last ( & tok ) ) ;
2011-12-27 03:18:46 +00:00
2012-02-22 18:51:06 +00:00
print_errors ( * out , prefix ) ;
2006-10-07 00:56:25 +00:00
}
}
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
break ;
}
2006-09-26 12:41:09 +00:00
if ( end_of_cmd )
{
2012-02-01 05:30:09 +00:00
if ( has_command & & command = = L " for " )
2006-09-26 12:41:09 +00:00
{
if ( arg_count > = 0 & & arg_count < 2 )
{
/*
Not enough arguments to the for builtin
*/
err = 1 ;
2011-12-27 03:18:46 +00:00
2006-09-26 12:41:09 +00:00
if ( out )
{
error ( SYNTAX_ERROR ,
tok_get_pos ( & tok ) ,
BUILTIN_FOR_ERR_COUNT ,
L " for " ,
arg_count ) ;
2011-12-27 03:18:46 +00:00
2012-02-22 18:51:06 +00:00
print_errors ( * out , prefix ) ;
2006-09-26 12:41:09 +00:00
}
}
2011-12-27 03:18:46 +00:00
2006-09-26 12:41:09 +00:00
}
2011-12-27 03:18:46 +00:00
2006-09-26 12:41:09 +00:00
}
2011-12-27 03:18:46 +00:00
2006-10-01 16:02:58 +00:00
if ( ! tok_has_next ( & tok ) )
break ;
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
}
2006-01-30 19:53:10 +00:00
2006-05-21 19:25:24 +00:00
if ( needs_cmd )
2005-09-20 13:26:39 +00:00
{
err = 1 ;
2006-06-02 02:15:17 +00:00
if ( out )
2006-01-30 19:53:10 +00:00
{
2005-09-20 13:26:39 +00:00
error ( SYNTAX_ERROR ,
2006-01-30 19:53:10 +00:00
tok_get_pos ( & tok ) ,
2005-12-07 16:06:47 +00:00
COND_ERR_MSG ) ;
2006-01-30 19:53:10 +00:00
2012-02-22 18:51:06 +00:00
print_errors ( * out , prefix ) ;
2005-09-20 13:26:39 +00:00
}
}
2006-01-30 19:53:10 +00:00
2006-06-02 02:15:17 +00:00
if ( out & & count > 0 )
2005-09-20 13:26:39 +00:00
{
2006-06-16 12:56:16 +00:00
const wchar_t * cmd ;
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
error ( SYNTAX_ERROR ,
2005-12-07 16:06:47 +00:00
block_pos [ count - 1 ] ,
2005-12-14 03:39:39 +00:00
BLOCK_END_ERR_MSG ) ;
2006-06-16 12:56:16 +00:00
2012-02-22 18:51:06 +00:00
print_errors ( * out , prefix ) ;
2006-06-16 12:56:16 +00:00
cmd = parser_get_block_command ( block_type [ count - 1 ] ) ;
if ( cmd )
{
2012-02-01 04:22:25 +00:00
const wcstring h = builtin_help_get ( * this , cmd ) ;
if ( h . size ( ) )
2006-06-16 12:56:16 +00:00
{
2012-02-22 18:51:06 +00:00
append_format ( * out , L " %ls " , h . c_str ( ) ) ;
2006-06-16 12:56:16 +00:00
}
}
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
}
2006-01-30 19:53:10 +00:00
2006-10-09 10:34:08 +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 .
*/
2006-10-07 00:56:25 +00:00
if ( block_level )
{
int last_level = 0 ;
2006-10-08 23:23:51 +00:00
int i , j ;
2006-10-07 00:56:25 +00:00
int len = wcslen ( buff ) ;
for ( i = 0 ; i < len ; i + + )
{
if ( block_level [ i ] > = 0 )
2006-10-08 23:23:51 +00:00
{
2006-10-07 00:56:25 +00:00
last_level = block_level [ i ] ;
2006-10-08 23:23:51 +00:00
/*
2006-10-09 10:34:08 +00:00
Make all whitespace before a token have the new
level . This avoid using the wrong indentation level
if a new line starts with whitespace .
2006-10-08 23:23:51 +00:00
*/
for ( j = i - 1 ; j > = 0 ; j - - )
{
if ( ! wcschr ( L " \n \t \r " , buff [ j ] ) )
break ;
block_level [ j ] = last_level ;
}
}
2006-10-07 00:56:25 +00:00
block_level [ i ] = last_level ;
}
2006-10-09 10:34:08 +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 .
*/
2006-10-09 09:59:31 +00:00
for ( j = len - 1 ; j > = 0 ; j - - )
{
if ( ! wcschr ( L " \n \t \r " , buff [ j ] ) )
break ;
block_level [ j ] = count ;
}
2011-12-27 03:18:46 +00:00
2006-10-09 09:59:31 +00:00
2011-12-27 03:18:46 +00:00
}
2006-10-07 00:56:25 +00:00
2006-10-09 10:34:08 +00:00
/*
Calculate exit status
*/
2006-10-07 00:56:25 +00:00
if ( count ! = 0 )
unfinished = 1 ;
2006-10-01 16:02:58 +00:00
if ( err )
res | = PARSER_TEST_ERROR ;
2006-10-07 00:56:25 +00:00
if ( unfinished )
2006-10-01 16:02:58 +00:00
res | = PARSER_TEST_INCOMPLETE ;
2006-10-09 10:34:08 +00:00
/*
Cleanup
*/
2012-02-10 03:26:44 +00:00
2006-10-09 10:34:08 +00:00
tok_destroy ( & tok ) ;
current_tokenizer = previous_tokenizer ;
current_tokenizer_pos = previous_pos ;
2011-12-27 03:18:46 +00:00
2006-10-09 10:34:08 +00:00
error_code = 0 ;
2011-12-27 03:18:46 +00:00
2006-10-09 10:34:08 +00:00
2006-10-01 16:02:58 +00:00
return res ;
2011-12-27 03:18:46 +00:00
2005-09-20 13:26:39 +00:00
}