2005-09-20 21:26:39 +08:00
/** \file parser.c
The fish parser . Contains functions for parsing code .
*/
2006-08-11 09:18:35 +08:00
# include "config.h"
2005-09-20 21:26:39 +08: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>
2006-02-28 21:17:16 +08:00
# include "fallback.h"
2005-09-20 21:26:39 +08:00
# include "util.h"
2006-02-28 21:17:16 +08:00
2005-09-20 21:26:39 +08:00
# include "common.h"
# include "wutil.h"
# include "proc.h"
# include "parser.h"
# 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-06 06:37:08 +08:00
# include "event.h"
2006-07-20 06:55:49 +08:00
2006-02-02 23:23:56 +08:00
# include "intern.h"
2006-02-05 21:10:35 +08:00
# include "parse_util.h"
2006-02-06 22:25:02 +08:00
# include "halloc.h"
2006-02-09 23:50:20 +08:00
# include "halloc_util.h"
2005-09-20 21:26:39 +08: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
2005-10-24 23:26:25 +08:00
/**
Message about reporting bugs , used on weird internal error to
hopefully get them to report stuff .
*/
2006-01-04 20:51:02 +08:00
# define BUGREPORT_MSG _( L"If this error can be reproduced, please send a bug report to %s.")
2005-10-09 19:48:16 +08:00
2005-09-20 21:26:39 +08:00
/**
Error message for improper use of the exec builtin
*/
2006-01-04 20:51:02 +08:00
# define EXEC_ERR_MSG _(L"This command can not be used in a pipeline")
2005-09-20 21:26:39 +08:00
/**
Error message for tokenizer error . The tokenizer message is
appended to this message .
*/
2006-01-04 20:51:02 +08:00
# define TOK_ERR_MSG _( L"Tokenizer error: '%ls'")
2005-09-20 21:26:39 +08:00
/**
2006-02-14 05:36:59 +08:00
Error message for short circuit command error .
2005-09-20 21:26:39 +08:00
*/
2006-06-09 07:57:19 +08:00
# define COND_ERR_MSG _( L"An additional command is required" )
2005-09-20 21:26:39 +08:00
/**
Error message on reaching maximum recusrion depth
*/
2006-01-04 20:51:02 +08:00
# define RECURSION_ERR_MSG _( L"Maximum recursion depth reached. Accidental infinite loop?")
2005-09-20 21:26:39 +08:00
2005-10-08 10:00:08 +08:00
/**
Error message used when the end of a block can ' t be located
*/
2006-01-11 22:17:35 +08: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 08:23:49 +08:00
2005-09-20 21:26:39 +08:00
/**
Error message on reaching maximum number of block calls
*/
2006-01-04 20:51:02 +08:00
# define BLOCK_ERR_MSG _( L"Maximum number of nested blocks reached.")
2005-09-20 21:26:39 +08:00
/**
2005-12-14 11:39:39 +08:00
Error message when a non - string token is found when expecting a command name
2005-09-20 21:26:39 +08:00
*/
2006-01-11 22:17:35 +08:00
# define CMD_ERR_MSG _( L"Expected a command name, got token of type '%ls'")
2005-09-20 21:26:39 +08:00
2006-01-06 00:02:28 +08:00
/**
Error message when a non - string token is found when expecting a command name
*/
2006-04-22 18:06:30 +08: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-06 00:02:28 +08:00
/**
Error message when a non - string token is found when expecting a command name
*/
2006-04-22 18:06:30 +08: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-06 00:02:28 +08:00
2005-09-20 21:26:39 +08:00
/**
2005-12-14 11:39:39 +08:00
Error message when encountering an illegal command name
2005-09-20 21:26:39 +08:00
*/
2006-01-11 22:17:35 +08:00
# define ILLEGAL_CMD_ERR_MSG _( L"Illegal command name '%ls'")
2005-09-20 21:26:39 +08:00
2005-12-04 00:43:56 +08:00
/**
Error message for wildcards with no matches
*/
2006-01-04 20:51:02 +08:00
# define WILDCARD_ERR_MSG _( L"Warning: No match for wildcard '%ls'. The command will not be executed.")
2005-12-08 00:06:47 +08:00
2005-12-14 02:28:03 +08:00
/**
Error when using case builtin outside of switch block
*/
2006-01-04 20:51:02 +08:00
# define INVALID_CASE_ERR_MSG _( L"'case' builtin not inside of switch block")
2005-12-14 02:28:03 +08:00
/**
Error when using loop control builtins ( break or continue ) outside of loop
*/
2006-01-04 20:51:02 +08:00
# define INVALID_LOOP_ERR_MSG _( L"Loop control command while not inside of loop" )
2005-12-14 02:28:03 +08:00
/**
Error when using else builtin outside of if block
*/
2006-01-04 20:51:02 +08:00
# define INVALID_ELSE_ERR_MSG _( L"'else' builtin not inside of if block" )
2005-12-14 02:28:03 +08:00
2005-12-14 04:11:21 +08:00
/**
Error when using end builtin outside of block
*/
2006-01-04 20:51:02 +08:00
# define INVALID_END_ERR_MSG _( L"'end' command outside of block")
2005-12-14 04:11:21 +08:00
2005-12-08 00:06:47 +08:00
/**
Error message for Posix - style assignment
*/
2006-07-20 07:20:20 +08: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-04 00:43:56 +08:00
2005-12-14 09:07:12 +08:00
/**
Error for invalid redirection token
*/
2006-01-04 20:51:02 +08:00
# define REDIRECT_TOKEN_ERR_MSG _( L"Expected redirection specification, got token of type '%ls'")
2005-12-14 11:39:39 +08:00
/**
Error when encountering redirection without a command
*/
2006-01-04 20:51:02 +08: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 22:17:35 +08:00
# define INVALID_SCOPE_ERR_MSG _( L"Tried to evaluate commands using invalid block type '%ls'" )
2006-01-04 20:51:02 +08:00
2005-12-14 11:39:39 +08:00
2006-01-31 03:53:10 +08:00
/**
2006-04-21 02:35:02 +08:00
Error for wrong token type
2005-12-14 11:39:39 +08:00
*/
2006-01-04 20:51:02 +08:00
# define UNEXPECTED_TOKEN_ERR_MSG _( L"Unexpected token of type '%ls'")
/**
Unexpected error in parser_get_filename ( )
*/
# define MISSING_COMMAND_ERR_MSG _( L"Error while searching for command '%ls'" )
2006-01-31 03:53:10 +08:00
/**
2006-04-21 02:35:02 +08:00
While block description
2006-01-04 20:51:02 +08:00
*/
2006-06-16 20:56:16 +08:00
# define WHILE_BLOCK N_( L"'while' block" )
2006-01-04 20:51:02 +08:00
2006-01-31 03:53:10 +08:00
/**
2006-04-21 02:35:02 +08:00
For block description
2006-01-04 20:51:02 +08:00
*/
2006-06-16 20:56:16 +08:00
# define FOR_BLOCK N_( L"'for' block" )
2006-01-04 20:51:02 +08:00
2006-01-31 03:53:10 +08:00
/**
2006-04-21 02:35:02 +08:00
If block description
2006-01-04 20:51:02 +08:00
*/
2006-06-16 20:56:16 +08:00
# define IF_BLOCK N_( L"'if' conditional block" )
2006-01-04 20:51:02 +08:00
2006-01-31 03:53:10 +08:00
/**
2006-04-21 02:35:02 +08:00
Function definition block description
2006-01-04 20:51:02 +08:00
*/
2006-06-16 20:56:16 +08:00
# define FUNCTION_DEF_BLOCK N_( L"function definition block" )
2006-01-04 20:51:02 +08:00
2006-01-31 03:53:10 +08:00
/**
2006-04-21 02:35:02 +08:00
Function invocation block description
2006-01-04 20:51:02 +08:00
*/
2006-06-16 20:56:16 +08:00
# define FUNCTION_CALL_BLOCK N_( L"function invocation block" )
2006-01-04 20:51:02 +08:00
2006-01-31 03:53:10 +08:00
/**
2006-04-21 02:35:02 +08:00
Switch block description
2006-01-04 20:51:02 +08:00
*/
2006-06-16 20:56:16 +08:00
# define SWITCH_BLOCK N_( L"'switch' block" )
2006-01-04 20:51:02 +08:00
2006-01-31 03:53:10 +08:00
/**
2006-04-21 02:35:02 +08:00
Fake block description
2006-01-04 20:51:02 +08:00
*/
2006-06-16 20:56:16 +08:00
# define FAKE_BLOCK N_( L"unexecutable block" )
2006-01-04 20:51:02 +08:00
2006-01-31 03:53:10 +08:00
/**
2006-04-21 02:35:02 +08:00
Top block description
2006-01-04 20:51:02 +08:00
*/
2006-06-16 20:56:16 +08:00
# define TOP_BLOCK N_( L"global root block" )
2006-01-04 20:51:02 +08:00
2006-01-31 03:53:10 +08:00
/**
2006-04-21 02:35:02 +08:00
Command substitution block description
2006-01-04 20:51:02 +08:00
*/
2006-06-16 20:56:16 +08:00
# define SUBST_BLOCK N_( L"command substitution block" )
2006-01-04 20:51:02 +08:00
2006-01-31 03:53:10 +08:00
/**
2006-04-21 02:35:02 +08:00
Begin block description
2006-01-04 20:51:02 +08:00
*/
2006-06-16 20:56:16 +08:00
# define BEGIN_BLOCK N_( L"'begin' unconditional block" )
2006-01-04 20:51:02 +08:00
2006-01-31 03:53:10 +08:00
/**
2006-04-21 02:35:02 +08:00
Source block description
2006-01-28 19:34:40 +08:00
*/
2006-06-16 20:56:16 +08:00
# define SOURCE_BLOCK N_( L"Block created by the . builtin" )
2006-01-28 19:34:40 +08:00
2006-02-01 23:49:11 +08:00
/**
2006-04-21 02:35:02 +08:00
Source block description
2006-02-01 23:49:11 +08:00
*/
2006-06-16 20:56:16 +08:00
# define EVENT_BLOCK N_( L"event handler block" )
2006-02-01 23:49:11 +08:00
2006-01-28 19:34:40 +08:00
2006-01-31 03:53:10 +08:00
/**
2006-04-21 02:35:02 +08:00
Unknown block description
2006-01-04 20:51:02 +08:00
*/
2006-06-16 20:56:16 +08:00
# define UNKNOWN_BLOCK N_( L"unknown / invalid block" )
2006-01-04 20:51:02 +08:00
2005-12-14 09:07:12 +08:00
2006-05-31 23:40:28 +08:00
/**
Size of the error string buffer
*/
# define ERR_STR_SZ 1024
2006-06-13 21:43:28 +08:00
2006-06-17 21:07:08 +08:00
/**
Datastructure to describe a block type , like while blocks , command substitution blocks , etc .
*/
2006-06-16 20:56:16 +08:00
struct block_lookup_entry
{
2006-07-03 18:46:47 +08:00
2006-06-17 21:07:08 +08:00
/**
The block type id . The legal values are defined in parser . h .
*/
2006-06-16 20:56:16 +08:00
int type ;
2006-07-03 18:46:47 +08:00
2006-06-17 21:07:08 +08:00
/**
The name of the builtin that creates this type of block , if any .
*/
2006-06-16 20:56:16 +08:00
const wchar_t * name ;
2006-07-03 18:46:47 +08:00
2006-06-17 21:07:08 +08:00
/**
A description of this block type
*/
2006-06-16 20:56:16 +08:00
const wchar_t * desc ;
}
;
2006-06-17 21:07:08 +08:00
/**
List of all legal block types
*/
2006-06-16 20:56:16 +08:00
const static struct block_lookup_entry block_lookup [ ] =
{
{
WHILE , L " while " , WHILE_BLOCK
}
,
{
FOR , L " for " , FOR_BLOCK
}
,
{
IF , L " if " , IF_BLOCK
}
,
{
FUNCTION_DEF , L " function " , FUNCTION_DEF_BLOCK
}
,
{
FUNCTION_CALL , 0 , FUNCTION_CALL_BLOCK
}
,
{
SWITCH , L " switch " , SWITCH_BLOCK
}
,
{
FAKE , 0 , FAKE_BLOCK
}
,
{
TOP , 0 , TOP_BLOCK
}
,
{
SUBST , 0 , SUBST_BLOCK
}
,
{
BEGIN , L " begin " , BEGIN_BLOCK
}
,
{
SOURCE , L " . " , SOURCE_BLOCK
}
,
{
EVENT , 0 , EVENT_BLOCK
}
,
{
0 , 0 , 0
}
}
;
2005-09-20 21:26:39 +08:00
/** Last error code */
2006-06-13 21:43:28 +08:00
static int error_code ;
2005-09-20 21:26:39 +08:00
2005-12-12 06:21:01 +08:00
event_block_t * global_event_block = 0 ;
2006-06-13 21:43:28 +08:00
io_data_t * block_io ;
2005-09-20 21:26:39 +08:00
/** Position of last error */
static int err_pos ;
/** Description of last error */
2006-05-31 23:40:28 +08:00
static wchar_t err_str [ ERR_STR_SZ ] ;
2005-09-20 21:26:39 +08:00
/** Pointer to the current tokenizer */
static tokenizer * current_tokenizer ;
/** String for representing the current line */
2006-01-12 20:54:57 +08:00
static string_buffer_t * lineinfo = 0 ;
2005-09-20 21:26:39 +08:00
/** This is the position of the beginning of the currently parsed command */
static int current_tokenizer_pos ;
/** The current innermost block */
block_t * current_block = 0 ;
/** List of called functions, used to help prevent infinite recursion */
2006-01-31 00:51:50 +08:00
static array_list_t * forbidden_function ;
2005-09-20 21:26:39 +08:00
/**
String index where the current job started .
*/
static int job_start_pos ;
/**
List of all profiling data
*/
static array_list_t profile_data ;
2005-10-08 10:00:08 +08:00
/**
Keeps track of how many recursive eval calls have been made . Eval
doesn ' t call itself directly , recursion happens on blocks and on
command substitutions .
*/
2005-09-20 21:26:39 +08:00
static int eval_level = - 1 ;
static int parse_job ( process_t * p ,
job_t * j ,
tokenizer * tok ) ;
2005-10-08 10:00:08 +08:00
/**
Struct used to keep track of profiling data for a command
*/
2005-09-20 21:26:39 +08:00
typedef struct
{
2005-10-24 23:26:25 +08:00
/**
2005-12-04 00:43:56 +08:00
Time spent executing the specified command , including parse time for nested blocks .
2005-10-24 23:26:25 +08:00
*/
int exec ;
/**
2005-12-04 00:43:56 +08:00
Time spent parsing the specified command , including execution time for command substitutions .
2005-10-24 23:26:25 +08:00
*/
int parse ;
/**
2005-12-04 00:43:56 +08:00
The block level of the specified command . nested blocks and command substitutions both increase the block level .
2005-10-24 23:26:25 +08:00
*/
int level ;
/**
2005-12-04 00:43:56 +08:00
If the execution of this command was skipped .
2005-10-24 23:26:25 +08:00
*/
int skipped ;
/**
2005-12-04 00:43:56 +08:00
The command string .
2005-10-24 23:26:25 +08:00
*/
2005-09-20 21:26:39 +08:00
wchar_t * cmd ;
} profile_element_t ;
2006-01-24 04:40:14 +08:00
/**
Return the current number of block nestings
*/
static int block_count ( block_t * b )
2005-09-20 21:26:39 +08:00
{
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
if ( b = = 0 )
return 0 ;
return ( block_count ( b - > outer ) + 1 ) ;
}
void parser_push_block ( int type )
{
2006-02-07 02:11:01 +08:00
block_t * new = halloc ( 0 , sizeof ( block_t ) ) ;
2006-02-09 23:50:20 +08:00
2006-02-01 23:49:11 +08:00
new - > src_lineno = parser_get_lineno ( ) ;
2006-02-02 23:23:56 +08:00
new - > src_filename = parser_current_filename ( ) ? intern ( parser_current_filename ( ) ) : 0 ;
2006-02-01 23:49:11 +08:00
2005-09-20 21:26:39 +08:00
new - > outer = current_block ;
new - > type = ( current_block & & current_block - > skip ) ? FAKE : type ;
2006-02-09 23:50:20 +08:00
2005-09-25 03:31:17 +08:00
/*
New blocks should be skipped if the outer block is skipped ,
2005-12-09 10:41:16 +08:00
except TOP ans SUBST block , which open up new environments . Fake
blocks should always be skipped . Rather complicated . . . : - (
2005-09-25 03:31:17 +08:00
*/
2005-09-20 21:26:39 +08:00
new - > skip = current_block ? current_block - > skip : 0 ;
2006-07-12 22:22:42 +08:00
/*
Type TOP and SUBST are never skipped
*/
2005-09-25 03:31:17 +08:00
if ( type = = TOP | | type = = SUBST )
2006-02-20 21:11:46 +08:00
{
2005-09-25 03:31:17 +08:00
new - > skip = 0 ;
2006-02-20 21:11:46 +08:00
}
2006-07-12 22:22:42 +08:00
/*
Fake blocks and function definition blocks are never executed
*/
if ( type = = FAKE | | type = = FUNCTION_DEF )
2006-02-20 21:11:46 +08:00
{
2005-12-09 10:41:16 +08:00
new - > skip = 1 ;
2006-02-20 21:11:46 +08:00
}
2006-02-09 23:50:20 +08:00
2006-01-31 03:53:10 +08:00
new - > job = 0 ;
2005-09-20 21:26:39 +08:00
new - > loop_status = LOOP_NORMAL ;
current_block = new ;
2006-01-31 03:53:10 +08:00
if ( ( new - > type ! = FUNCTION_DEF ) & &
( new - > type ! = FAKE ) & &
2005-09-20 21:26:39 +08:00
( new - > type ! = TOP ) )
{
env_push ( type = = FUNCTION_CALL ) ;
2006-02-09 23:50:20 +08:00
halloc_register_function_void ( current_block , & env_pop ) ;
2005-09-20 21:26:39 +08:00
}
}
void parser_pop_block ( )
{
block_t * old = current_block ;
current_block = current_block - > outer ;
2006-02-07 02:11:01 +08:00
halloc_free ( old ) ;
2005-09-20 21:26:39 +08:00
}
2006-01-04 20:51:02 +08:00
const wchar_t * parser_get_block_desc ( int block )
2005-09-20 21:26:39 +08:00
{
2006-06-16 20:56:16 +08:00
int i ;
for ( i = 0 ; block_lookup [ i ] . desc ; i + + )
2005-09-20 21:26:39 +08:00
{
2006-06-16 20:56:16 +08:00
if ( block_lookup [ i ] . type = = block )
{
return _ ( block_lookup [ i ] . desc ) ;
}
2005-09-20 21:26:39 +08:00
}
2006-06-16 20:56:16 +08:00
return _ ( UNKNOWN_BLOCK ) ;
2005-09-20 21:26:39 +08:00
}
2006-01-24 04:40:14 +08:00
/**
Check if the specified bcommand is one of the builtins that cannot
have arguments , any followin argument is interpreted as a new
command
*/
static int parser_skip_arguments ( const wchar_t * cmd )
2005-09-20 21:26:39 +08:00
{
2006-01-31 03:53:10 +08:00
2005-12-14 09:07:12 +08:00
return contains_str ( cmd ,
2005-09-25 19:25:42 +08:00
L " else " ,
L " begin " ,
2005-10-01 02:28:26 +08:00
( void * ) 0 ) ;
2005-09-20 21:26:39 +08:00
}
2005-09-25 19:25:42 +08:00
int parser_is_subcommand ( const wchar_t * cmd )
{
2006-01-31 03:53:10 +08:00
2005-09-25 19:25:42 +08:00
return parser_skip_arguments ( cmd ) | |
contains_str ( cmd ,
2006-01-31 03:53:10 +08:00
L " command " ,
L " builtin " ,
2005-09-25 19:25:42 +08:00
L " while " ,
L " exec " ,
L " if " ,
L " and " ,
L " or " ,
L " not " ,
2005-10-01 02:28:26 +08:00
( void * ) 0 ) ;
2005-09-25 19:25:42 +08:00
}
2006-03-30 05:29:42 +08:00
int parser_is_block ( const wchar_t * word )
2005-09-20 21:26:39 +08:00
{
2006-01-31 03:53:10 +08:00
return contains_str ( word ,
2005-09-20 21:26:39 +08:00
L " for " ,
2006-01-31 03:53:10 +08:00
L " while " ,
L " if " ,
2005-09-20 21:26:39 +08:00
L " function " ,
2006-01-31 03:53:10 +08:00
L " switch " ,
2005-09-20 21:26:39 +08:00
L " begin " ,
2005-10-01 02:28:26 +08:00
( void * ) 0 ) ;
2005-09-20 21:26:39 +08:00
}
2006-02-08 18:22:03 +08:00
int parser_is_reserved ( const wchar_t * word )
2005-09-20 21:26:39 +08:00
{
return parser_is_block ( word ) | |
parser_is_subcommand ( word ) | |
2006-01-31 03:53:10 +08:00
contains_str ( word ,
L " end " ,
L " case " ,
L " else " ,
2005-09-20 21:26:39 +08:00
L " return " ,
L " continue " ,
L " break " ,
2005-10-01 02:28:26 +08:00
( void * ) 0 ) ;
2005-09-20 21:26:39 +08:00
}
2006-01-24 04:40:14 +08:00
/**
Returns 1 if the specified command is a builtin that may not be used in a pipeline
*/
static int parser_is_pipe_forbidden ( wchar_t * word )
2005-09-20 21:26:39 +08:00
{
return contains_str ( word ,
L " exec " ,
2006-01-31 03:53:10 +08:00
L " case " ,
2005-09-20 21:26:39 +08:00
L " break " ,
2006-01-31 03:53:10 +08:00
L " return " ,
L " continue " ,
2005-10-01 02:28:26 +08:00
( void * ) 0 ) ;
2005-09-20 21:26:39 +08:00
}
2005-10-08 10:00:08 +08:00
/**
Search the text for the end of the current block
*/
2006-01-31 03:53:10 +08:00
static const wchar_t * parser_find_end ( const wchar_t * buff )
2005-09-20 21:26:39 +08:00
{
tokenizer tok ;
int had_cmd = 0 ;
int count = 0 ;
int error = 0 ;
int mark = 0 ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08: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 - - ;
}
else if ( parser_is_block ( tok_last ( & tok ) ) )
{
count + + ;
}
if ( count < 0 )
{
error = 1 ;
}
had_cmd = 1 ;
}
break ;
}
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
case TOK_END :
{
had_cmd = 0 ;
break ;
}
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
case TOK_PIPE :
case TOK_BACKGROUND :
{
if ( had_cmd )
{
had_cmd = 0 ;
}
else
{
error = 1 ;
}
break ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
}
case TOK_ERROR :
error = 1 ;
break ;
default :
break ;
2006-01-31 03:53:10 +08:00
}
2005-09-20 21:26:39 +08:00
if ( ! count )
{
2006-01-31 03:53:10 +08:00
tok_next ( & tok ) ;
mark = tok_get_pos ( & tok ) ;
break ;
2005-09-20 21:26:39 +08:00
}
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
}
2006-01-31 03:53:10 +08:00
tok_destroy ( & tok ) ;
2005-09-20 21:26:39 +08:00
if ( ! count & & ! error ) {
return buff + mark ;
}
return 0 ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
}
2006-06-12 22:12:33 +08:00
wchar_t * parser_cdpath_get ( void * context , wchar_t * dir )
2005-09-20 21:26:39 +08:00
{
wchar_t * res = 0 ;
if ( ! dir )
return 0 ;
2006-02-08 23:29:09 +08:00
if ( dir [ 0 ] = = L ' / ' | | ( wcsncmp ( dir , L " ./ " , 2 ) = = 0 ) )
2005-09-20 21:26:39 +08:00
{
struct stat buf ;
if ( wstat ( dir , & buf ) = = 0 )
{
if ( S_ISDIR ( buf . st_mode ) )
{
2006-06-12 22:12:33 +08:00
res = halloc_wcsdup ( context , dir ) ;
2005-09-20 21:26:39 +08:00
}
}
}
else
{
2005-12-14 04:10:36 +08:00
wchar_t * path ;
wchar_t * path_cpy ;
wchar_t * nxt_path ;
wchar_t * state ;
wchar_t * whole_path ;
2005-09-20 21:26:39 +08:00
2005-12-14 04:10:36 +08:00
path = env_get ( L " CDPATH " ) ;
if ( ! path | | ! wcslen ( path ) )
2005-09-20 21:26:39 +08:00
{
path = L " . " ;
}
2005-12-14 04:10:36 +08:00
nxt_path = path ;
path_cpy = wcsdup ( path ) ;
2006-01-31 03:53:10 +08:00
2005-12-14 04:10:36 +08:00
if ( ! path_cpy )
2005-09-20 21:26:39 +08:00
{
2006-07-03 18:39:57 +08:00
DIE_MEM ( ) ;
2005-09-20 21:26:39 +08:00
}
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
for ( nxt_path = wcstok ( path_cpy , ARRAY_SEP_STR , & state ) ;
nxt_path ! = 0 ;
nxt_path = wcstok ( 0 , ARRAY_SEP_STR , & state ) )
{
wchar_t * expanded_path = expand_tilde ( wcsdup ( nxt_path ) ) ;
// debug( 2, L"woot %ls\n", expanded_path );
int path_len = wcslen ( expanded_path ) ;
if ( path_len = = 0 )
{
free ( expanded_path ) ;
continue ;
}
2006-01-31 03:53:10 +08:00
whole_path =
2005-09-20 21:26:39 +08:00
wcsdupcat2 ( expanded_path ,
( expanded_path [ path_len - 1 ] ! = L ' / ' ) ? L " / " : L " " ,
dir , 0 ) ;
free ( expanded_path ) ;
struct stat buf ;
if ( wstat ( whole_path , & buf ) = = 0 )
{
if ( S_ISDIR ( buf . st_mode ) )
{
res = whole_path ;
2006-06-12 22:12:33 +08:00
halloc_register ( context , whole_path ) ;
2005-09-20 21:26:39 +08:00
break ;
}
}
free ( whole_path ) ;
}
free ( path_cpy ) ;
}
return res ;
}
void parser_forbid_function ( wchar_t * function )
{
/*
if ( function )
debug ( 2 , L " Forbid %ls \n " , function ) ;
*/
2006-01-31 00:51:50 +08:00
al_push ( forbidden_function , function ? wcsdup ( function ) : 0 ) ;
2005-09-20 21:26:39 +08:00
}
void parser_allow_function ( )
{
/*
if ( al_peek ( & forbidden_function ) )
debug ( 2 , L " Allow %ls \n " , al_peek ( & forbidden_function ) ) ;
*/
2006-01-31 00:51:50 +08:00
free ( ( void * ) al_pop ( forbidden_function ) ) ;
2005-09-20 21:26:39 +08:00
}
2005-12-08 00:06:47 +08:00
void error ( int ec , int p , const wchar_t * str , . . . )
2005-09-20 21:26:39 +08:00
{
2005-12-08 00:06:47 +08:00
va_list va ;
2005-09-20 21:26:39 +08:00
error_code = ec ;
err_pos = p ;
2005-12-08 00:06:47 +08:00
va_start ( va , str ) ;
2006-05-31 23:40:28 +08:00
vswprintf ( err_str , ERR_STR_SZ , str , va ) ;
2006-01-31 03:53:10 +08:00
va_end ( va ) ;
2005-12-08 00:06:47 +08:00
2005-09-20 21:26:39 +08:00
}
2006-06-12 22:12:33 +08:00
wchar_t * parser_get_filename ( void * context , const wchar_t * cmd )
2005-09-20 21:26:39 +08:00
{
wchar_t * path ;
2006-06-21 08:48:36 +08:00
CHECK ( cmd , 0 ) ;
2006-06-12 22:12:33 +08:00
debug ( 3 , L " parser_get_filename( '%ls' ) " , cmd ) ;
2005-12-12 07:39:39 +08:00
2006-02-13 19:10:23 +08:00
if ( wcschr ( cmd , L ' / ' ) ! = 0 )
2005-09-20 21:26:39 +08:00
{
if ( waccess ( cmd , X_OK ) = = 0 )
{
struct stat buff ;
wstat ( cmd , & buff ) ;
if ( S_ISREG ( buff . st_mode ) )
2006-06-12 22:12:33 +08:00
return halloc_wcsdup ( context , cmd ) ;
2005-09-20 21:26:39 +08:00
else
return 0 ;
}
}
else
{
path = env_get ( L " PATH " ) ;
2006-04-21 02:35:02 +08:00
if ( path = = 0 )
2005-09-20 21:26:39 +08:00
{
2006-04-21 02:35:02 +08:00
if ( contains_str ( PREFIX L " /bin " , L " /bin " , L " /usr/bin " , ( void * ) 0 ) )
{
path = L " /bin " ARRAY_SEP_STR L " /usr/bin " ;
}
else
2005-09-20 21:26:39 +08:00
{
2006-04-21 02:35:02 +08:00
path = L " /bin " ARRAY_SEP_STR L " /usr/bin " ARRAY_SEP_STR PREFIX L " /bin " ;
2005-09-20 21:26:39 +08:00
}
2006-04-21 02:35:02 +08:00
}
/*
Allocate string long enough to hold the whole command
*/
2006-06-13 04:54:38 +08:00
wchar_t * new_cmd = halloc ( context , sizeof ( wchar_t ) * ( wcslen ( cmd ) + wcslen ( path ) + 2 ) ) ;
2006-04-21 02:35:02 +08:00
/*
We tokenize a copy of the path , since strtok modifies
its arguments
*/
wchar_t * path_cpy = wcsdup ( path ) ;
wchar_t * nxt_path = path ;
wchar_t * state ;
if ( ( new_cmd = = 0 ) | | ( path_cpy = = 0 ) )
{
2006-07-03 18:39:57 +08:00
DIE_MEM ( ) ;
2006-04-21 02:35:02 +08:00
}
2005-09-20 21:26:39 +08:00
2006-04-21 02:35:02 +08:00
for ( nxt_path = wcstok ( path_cpy , ARRAY_SEP_STR , & state ) ;
nxt_path ! = 0 ;
nxt_path = wcstok ( 0 , ARRAY_SEP_STR , & state ) )
{
int path_len = wcslen ( nxt_path ) ;
wcscpy ( new_cmd , nxt_path ) ;
if ( new_cmd [ path_len - 1 ] ! = L ' / ' )
2005-09-20 21:26:39 +08:00
{
2006-04-21 02:35:02 +08:00
new_cmd [ path_len + + ] = L ' / ' ;
}
wcscpy ( & new_cmd [ path_len ] , cmd ) ;
if ( waccess ( new_cmd , X_OK ) = = 0 )
{
struct stat buff ;
if ( wstat ( new_cmd , & buff ) = = - 1 )
2005-09-20 21:26:39 +08:00
{
2006-04-21 02:35:02 +08:00
if ( errno ! = EACCES )
2005-09-20 21:26:39 +08:00
{
2006-04-21 02:35:02 +08:00
wperror ( L " stat " ) ;
2005-09-20 21:26:39 +08:00
}
2006-04-21 02:35:02 +08:00
continue ;
2005-09-20 21:26:39 +08:00
}
2006-04-21 02:35:02 +08:00
if ( S_ISREG ( buff . st_mode ) )
2005-09-20 21:26:39 +08:00
{
2006-04-21 02:35:02 +08:00
free ( path_cpy ) ;
return new_cmd ;
}
}
else
{
switch ( errno )
{
case ENOENT :
case ENAMETOOLONG :
case EACCES :
case ENOTDIR :
break ;
default :
2005-09-20 21:26:39 +08:00
{
2006-04-21 02:35:02 +08:00
debug ( 1 ,
MISSING_COMMAND_ERR_MSG ,
new_cmd ) ;
wperror ( L " access " ) ;
2005-09-20 21:26:39 +08:00
}
}
}
}
2006-04-21 02:35:02 +08:00
free ( path_cpy ) ;
2005-09-20 21:26:39 +08:00
}
return 0 ;
}
void parser_init ( )
{
if ( profile )
{
al_init ( & profile_data ) ;
}
2006-01-31 00:51:50 +08:00
forbidden_function = al_new ( ) ;
2005-09-20 21:26:39 +08:00
}
2006-01-24 04:40:14 +08:00
/**
Print profiling information to the specified stream
*/
2006-01-31 03:53:10 +08:00
static void print_profile ( array_list_t * p ,
int pos ,
2006-01-24 04:40:14 +08:00
FILE * out )
2005-09-20 21:26:39 +08:00
{
profile_element_t * me , * prev ;
2006-01-31 03:53:10 +08:00
int i ;
2005-09-20 21:26:39 +08:00
int my_time ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
if ( pos > = al_get_count ( p ) )
2006-02-20 21:11:46 +08:00
{
2005-09-20 21:26:39 +08:00
return ;
2006-02-20 21:11:46 +08:00
}
2005-09-20 21:26:39 +08:00
me = ( profile_element_t * ) al_get ( p , pos ) ;
if ( ! me - > skipped )
{
my_time = me - > parse + me - > exec ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
for ( i = pos + 1 ; i < al_get_count ( p ) ; i + + )
{
prev = ( profile_element_t * ) al_get ( p , i ) ;
if ( prev - > skipped )
2006-02-20 21:11:46 +08:00
{
2005-09-20 21:26:39 +08:00
continue ;
2006-02-20 21:11:46 +08:00
}
2005-09-20 21:26:39 +08:00
if ( prev - > level < = me - > level )
2006-02-20 21:11:46 +08:00
{
2005-09-20 21:26:39 +08:00
break ;
2006-02-20 21:11:46 +08:00
}
2005-09-20 21:26:39 +08:00
if ( prev - > level > me - > level + 1 )
2006-02-20 21:11:46 +08:00
{
2005-09-20 21:26:39 +08:00
continue ;
2006-02-20 21:11:46 +08:00
}
2005-09-20 21:26:39 +08:00
my_time - = prev - > parse ;
my_time - = prev - > exec ;
}
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
if ( me - > cmd )
{
2006-06-21 17:54:30 +08:00
if ( fwprintf ( out , L " %d \t %d \t " , my_time , me - > parse + me - > exec ) < 0 )
{
wperror ( L " fwprintf " ) ;
return ;
}
2005-09-20 21:26:39 +08:00
for ( i = 0 ; i < me - > level ; i + + )
{
2006-06-21 17:54:30 +08:00
if ( fwprintf ( out , L " - " ) < 0 )
{
wperror ( L " fwprintf " ) ;
return ;
}
}
if ( fwprintf ( out , L " > %ls \n " , me - > cmd ) < 0 )
{
wperror ( L " fwprintf " ) ;
return ;
2005-09-20 21:26:39 +08:00
}
2006-06-21 17:54:30 +08:00
2005-09-20 21:26:39 +08:00
}
}
2006-01-31 03:53:10 +08:00
print_profile ( p , pos + 1 , out ) ;
free ( me - > cmd ) ;
free ( me ) ;
2005-09-20 21:26:39 +08:00
}
void parser_destroy ( )
{
if ( profile )
{
/*
Save profiling information
*/
FILE * f = fopen ( profile , " w " ) ;
if ( ! f )
{
debug ( 1 ,
2006-01-04 20:51:02 +08:00
_ ( L " Could not write profiling information to file '%s' " ) ,
2005-09-20 21:26:39 +08:00
profile ) ;
}
else
{
2006-06-21 17:54:30 +08:00
if ( fwprintf ( f ,
_ ( L " Time \t Sum \t Command \n " ) ,
al_get_count ( & profile_data ) ) < 0 )
{
wperror ( L " fwprintf " ) ;
}
else
{
print_profile ( & profile_data , 0 , f ) ;
}
if ( fclose ( f ) )
{
wperror ( L " fclose " ) ;
}
2005-09-20 21:26:39 +08:00
}
al_destroy ( & profile_data ) ;
}
2006-01-31 03:53:10 +08:00
2006-01-26 22:48:10 +08:00
if ( lineinfo )
{
sb_destroy ( lineinfo ) ;
free ( lineinfo ) ;
lineinfo = 0 ;
}
2006-01-31 00:51:50 +08:00
al_destroy ( forbidden_function ) ;
free ( forbidden_function ) ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
}
2005-10-08 10:00:08 +08:00
/**
2006-06-20 08:50:10 +08:00
Print error message to string_buffer_t if an error has occured while parsing
\ 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 10:00:08 +08:00
*/
2006-06-02 10:15:17 +08:00
static void print_errors ( string_buffer_t * target , const wchar_t * prefix )
2005-09-20 21:26:39 +08:00
{
if ( error_code )
{
int tmp ;
2006-06-02 10:15:17 +08:00
sb_printf ( target , L " %ls: %ls \n " , prefix , err_str ) ;
2005-09-20 21:26:39 +08:00
tmp = current_tokenizer_pos ;
current_tokenizer_pos = err_pos ;
2006-06-02 10:15:17 +08:00
sb_printf ( target , L " %ls " , parser_current_line ( ) ) ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
current_tokenizer_pos = tmp ;
}
}
2006-06-20 08:50:10 +08:00
/**
Print error message to stderr if an error has occured while parsing
*/
2006-06-02 10:15:17 +08:00
static void print_errors_stderr ( )
{
if ( error_code )
{
debug ( 0 , L " %ls " , err_str ) ;
int tmp ;
tmp = current_tokenizer_pos ;
current_tokenizer_pos = err_pos ;
fwprintf ( stderr , L " %ls " , parser_current_line ( ) ) ;
current_tokenizer_pos = tmp ;
}
}
2005-09-20 21:26:39 +08:00
int eval_args ( const wchar_t * line , array_list_t * args )
{
tokenizer tok ;
/*
eval_args may be called while evaulating another command , so we
save the previous tokenizer and restore it on exit
*/
2006-01-31 03:53:10 +08:00
tokenizer * previous_tokenizer = current_tokenizer ;
2005-09-20 21:26:39 +08:00
int previous_pos = current_tokenizer_pos ;
int do_loop = 1 ;
2006-06-21 08:48:36 +08:00
CHECK ( line , 1 ) ;
CHECK ( args , 1 ) ;
2006-02-16 21:36:32 +08:00
proc_push_interactive ( 0 ) ;
2006-05-10 19:53:51 +08:00
current_tokenizer = & tok ;
2006-02-04 19:34:33 +08:00
current_tokenizer_pos = 0 ;
tok_init ( & tok , line , 0 ) ;
2005-09-20 21:26:39 +08: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 21:11:46 +08:00
{
2006-02-22 23:41:52 +08:00
wchar_t * tmp = wcsdup ( tok_last ( & tok ) ) ;
if ( ! tmp )
{
2006-07-03 18:39:57 +08:00
DIE_MEM ( ) ;
2006-02-22 23:41:52 +08:00
}
if ( expand_string ( 0 , tmp , args , 0 ) = = EXPAND_ERROR )
2005-09-20 21:26:39 +08:00
{
2006-02-20 21:11:46 +08:00
err_pos = tok_get_pos ( & tok ) ;
do_loop = 0 ;
2005-09-20 21:26:39 +08:00
}
break ;
2006-02-20 21:11:46 +08:00
}
2005-09-20 21:26:39 +08:00
case TOK_END :
2006-02-20 21:11:46 +08:00
{
2005-09-20 21:26:39 +08:00
break ;
2006-02-20 21:11:46 +08:00
}
2005-09-20 21:26:39 +08:00
case TOK_ERROR :
{
2005-12-08 00:06:47 +08:00
error ( SYNTAX_ERROR ,
tok_get_pos ( & tok ) ,
TOK_ERR_MSG ,
tok_last ( & tok ) ) ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
do_loop = 0 ;
break ;
}
default :
2006-02-20 21:11:46 +08:00
{
2005-12-08 00:06:47 +08:00
error ( SYNTAX_ERROR ,
tok_get_pos ( & tok ) ,
2005-12-14 11:39:39 +08:00
UNEXPECTED_TOKEN_ERR_MSG ,
2005-12-08 00:06:47 +08:00
tok_get_desc ( tok_last_type ( & tok ) ) ) ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
do_loop = 0 ;
break ;
2006-02-20 21:11:46 +08:00
}
2005-09-20 21:26:39 +08:00
}
}
2006-06-02 10:15:17 +08:00
print_errors_stderr ( ) ;
2006-02-09 23:50:20 +08:00
2005-09-20 21:26:39 +08:00
tok_destroy ( & tok ) ;
2006-02-09 23:50:20 +08:00
2005-09-20 21:26:39 +08:00
current_tokenizer = previous_tokenizer ;
current_tokenizer_pos = previous_pos ;
2006-02-16 21:36:32 +08:00
proc_pop_interactive ( ) ;
2006-02-04 19:34:33 +08:00
2005-09-20 21:26:39 +08:00
return 1 ;
}
2006-01-31 01:54:26 +08:00
void parser_stack_trace ( block_t * b , string_buffer_t * buff )
2006-01-26 22:48:10 +08:00
{
2006-06-09 07:52:12 +08:00
/*
Validate input
*/
2006-06-21 08:48:36 +08:00
CHECK ( buff , ) ;
2006-06-09 07:52:12 +08:00
/*
Check if we should end the recursion
*/
if ( ! b )
return ;
2006-02-01 23:49:11 +08:00
if ( b - > type = = EVENT )
{
2006-06-09 07:55:57 +08:00
/*
This is an event handler
*/
2006-02-01 23:49:11 +08:00
sb_printf ( buff , _ ( L " in event handler: %ls \n " ) , event_get_desc ( b - > param1 . event ) ) ;
sb_printf ( buff ,
L " \n " ) ;
2006-06-09 07:55:57 +08: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 23:49:11 +08:00
return ;
}
if ( b - > type = = FUNCTION_CALL | | b - > type = = SOURCE | | b - > type = = SUBST )
2006-01-26 22:48:10 +08:00
{
2006-06-09 07:55:57 +08:00
/*
These types of blocks should be printed
*/
2006-01-26 22:48:10 +08:00
int i ;
2006-02-01 23:49:11 +08:00
switch ( b - > type )
{
case SOURCE :
2006-02-20 21:11:46 +08:00
{
2006-02-01 23:49:11 +08:00
sb_printf ( buff , _ ( L " in . (source) call of file '%ls', \n " ) , b - > param1 . source_dest ) ;
break ;
2006-02-20 21:11:46 +08:00
}
2006-02-01 23:49:11 +08:00
case FUNCTION_CALL :
2006-02-20 21:11:46 +08:00
{
2006-02-01 23:49:11 +08:00
sb_printf ( buff , _ ( L " in function '%ls', \n " ) , b - > param1 . function_name ) ;
break ;
2006-02-20 21:11:46 +08:00
}
2006-02-01 23:49:11 +08:00
case SUBST :
2006-02-20 21:11:46 +08:00
{
2006-02-01 23:49:11 +08:00
sb_printf ( buff , _ ( L " in command substitution \n " ) ) ;
break ;
2006-02-20 21:11:46 +08:00
}
2006-02-01 23:49:11 +08:00
}
2006-01-28 19:34:40 +08:00
2006-02-01 23:49:11 +08:00
const wchar_t * file = b - > src_filename ;
2006-01-26 23:47:22 +08:00
2006-01-26 22:48:10 +08:00
if ( file )
2006-02-20 21:11:46 +08:00
{
2006-01-31 03:53:10 +08:00
sb_printf ( buff ,
2006-01-26 23:16:59 +08:00
_ ( L " \t called on line %d of file '%ls', \n " ) ,
2006-02-01 23:49:11 +08:00
b - > src_lineno ,
2006-01-26 22:48:10 +08:00
file ) ;
2006-02-20 21:11:46 +08:00
}
2006-01-26 22:48:10 +08:00
else
2006-02-20 21:11:46 +08:00
{
2006-01-31 03:53:10 +08:00
sb_printf ( buff ,
2006-01-26 23:16:59 +08:00
_ ( L " \t called on standard input, \n " ) ) ;
2006-02-20 21:11:46 +08:00
}
2006-02-01 23:49:11 +08:00
if ( b - > type = = FUNCTION_CALL )
{
if ( b - > param2 . function_call_process - > argv [ 1 ] )
2006-01-26 22:48:10 +08:00
{
2006-02-01 23:49:11 +08:00
string_buffer_t tmp ;
sb_init ( & tmp ) ;
for ( i = 1 ; b - > param2 . function_call_process - > argv [ i ] ; i + + )
{
sb_append2 ( & tmp , i > 1 ? L " " : L " " , b - > param2 . function_call_process - > argv [ i ] , ( void * ) 0 ) ;
}
sb_printf ( buff , _ ( L " \t with parameter list '%ls' \n " ) , ( wchar_t * ) tmp . buff ) ;
sb_destroy ( & tmp ) ;
2006-01-26 22:48:10 +08:00
}
2006-01-31 03:53:10 +08:00
}
2006-02-01 23:49:11 +08:00
2006-01-31 03:53:10 +08:00
sb_printf ( buff ,
2006-01-26 22:48:10 +08:00
L " \n " ) ;
2006-01-31 03:53:10 +08:00
}
2006-06-09 07:55:57 +08:00
/*
Recursively print the next block
*/
2006-01-31 03:53:10 +08:00
parser_stack_trace ( b - > outer , buff ) ;
2006-01-26 22:48:10 +08:00
}
2006-06-09 07:55:57 +08:00
/**
Returns true if we are currently evaluating a function . This is
tested by moving down the block - scope - stack , checking every block
if it is of type FUNCTION_CALL .
*/
2006-01-26 22:48:10 +08:00
static const wchar_t * is_function ( )
{
block_t * b = current_block ;
while ( 1 )
{
if ( ! b )
{
return 0 ;
}
if ( b - > type = = FUNCTION_CALL )
{
return b - > param1 . function_name ;
}
b = b - > outer ;
}
}
int parser_get_lineno ( )
{
2006-02-01 23:49:11 +08:00
const wchar_t * whole_str ;
2006-01-26 22:48:10 +08:00
const wchar_t * function_name ;
2006-02-05 21:10:35 +08:00
int lineno ;
2006-02-04 21:09:14 +08:00
2006-02-05 21:10:35 +08:00
/* static const wchar_t *prev_str = 0;
2006-04-21 02:35:02 +08:00
static int i = 0 ;
static int lineno = 1 ;
2006-02-05 21:10:35 +08:00
*/
2006-02-01 23:49:11 +08:00
if ( ! current_tokenizer )
return - 1 ;
whole_str = tok_string ( current_tokenizer ) ;
if ( ! whole_str )
return - 1 ;
2006-02-04 21:09:14 +08:00
2006-02-05 21:10:35 +08:00
lineno = parse_util_lineno ( whole_str , current_tokenizer_pos ) ;
2006-01-31 03:53:10 +08:00
2006-01-26 22:48:10 +08:00
if ( ( function_name = is_function ( ) ) )
{
lineno + = function_get_definition_offset ( function_name ) ;
2006-01-31 03:53:10 +08:00
}
2006-01-26 22:48:10 +08:00
return lineno ;
}
2006-01-26 23:47:22 +08:00
const wchar_t * parser_current_filename ( )
2006-01-26 22:48:10 +08:00
{
block_t * b = current_block ;
2006-01-31 03:53:10 +08:00
2006-01-26 22:48:10 +08:00
while ( 1 )
{
if ( ! b )
{
2006-01-31 03:53:10 +08:00
return reader_current_filename ( ) ;
2006-01-26 22:48:10 +08:00
}
if ( b - > type = = FUNCTION_CALL )
{
return function_get_definition_file ( b - > param1 . function_name ) ;
}
b = b - > outer ;
2006-01-31 03:53:10 +08:00
}
2006-01-26 22:48:10 +08:00
}
2006-06-09 07:55:57 +08: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 22:48:10 +08:00
static int printed_width ( const wchar_t * str , int len )
{
int res = 0 ;
int i ;
2006-06-09 07:57:19 +08:00
for ( i = 0 ; str [ i ] & & i < len ; i + + )
2006-01-26 22:48:10 +08:00
{
if ( str [ i ] = = L ' \t ' )
{
res = ( res + 8 ) & ~ 7 ;
}
else
{
res + = wcwidth ( str [ i ] ) ;
}
}
return res ;
}
2005-09-20 21:26:39 +08:00
wchar_t * parser_current_line ( )
{
int lineno = 1 ;
2006-05-10 19:54:31 +08:00
const wchar_t * file ;
wchar_t * whole_str ;
wchar_t * line ;
2005-09-20 21:26:39 +08:00
wchar_t * line_end ;
int i ;
int offset ;
2006-01-26 22:48:10 +08:00
int current_line_width ;
const wchar_t * function_name = 0 ;
int current_line_start = 0 ;
2006-01-31 03:53:10 +08:00
2006-05-10 19:54:31 +08:00
if ( ! current_tokenizer )
{
return L " " ;
}
file = parser_current_filename ( ) ;
whole_str = tok_string ( current_tokenizer ) ;
line = whole_str ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
if ( ! line )
return L " " ;
2006-01-31 03:53:10 +08:00
2006-01-12 20:54:57 +08:00
if ( ! lineinfo )
{
lineinfo = malloc ( sizeof ( string_buffer_t ) ) ;
sb_init ( lineinfo ) ;
}
2006-01-31 03:53:10 +08:00
sb_clear ( lineinfo ) ;
2005-09-20 21:26:39 +08:00
/*
Calculate line number , line offset , etc .
*/
2006-02-09 02:44:37 +08:00
for ( i = 0 ; i < current_tokenizer_pos & & whole_str [ i ] ; i + + )
2005-09-20 21:26:39 +08:00
{
if ( whole_str [ i ] = = L ' \n ' )
{
lineno + + ;
2006-01-26 22:48:10 +08:00
current_line_start = i + 1 ;
2005-09-20 21:26:39 +08:00
line = & whole_str [ i + 1 ] ;
}
}
2006-02-19 09:17:02 +08:00
// lineno = current_tokenizer_pos;
2006-07-21 09:08:31 +08:00
current_line_width = printed_width ( whole_str + current_line_start ,
current_tokenizer_pos - current_line_start ) ;
2006-01-26 22:48:10 +08:00
if ( ( function_name = is_function ( ) ) )
{
lineno + = function_get_definition_offset ( function_name ) ;
2006-01-31 03:53:10 +08:00
}
2006-01-26 22:48:10 +08:00
2005-09-20 21:26:39 +08: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 22:48:10 +08:00
/**
If we are not going to print a stack trace , at least print the line number and filename
*/
2006-01-26 23:47:22 +08:00
if ( ! is_interactive | | is_function ( ) )
2005-09-20 21:26:39 +08:00
{
2006-01-26 22:48:10 +08:00
int prev_width = my_wcswidth ( ( wchar_t * ) lineinfo - > buff ) ;
if ( file )
sb_printf ( lineinfo ,
_ ( L " %ls (line %d): " ) ,
file ,
lineno ) ;
else
sb_printf ( lineinfo ,
L " %ls: " ,
_ ( L " Standard input " ) ,
lineno ) ;
offset = my_wcswidth ( ( wchar_t * ) lineinfo - > buff ) - prev_width ;
2005-09-20 21:26:39 +08:00
}
else
{
offset = 0 ;
}
2006-01-26 22:48:10 +08: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-31 03:53:10 +08:00
/*
2006-04-21 02:35:02 +08: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 09:07:12 +08:00
*/
2006-01-26 22:48:10 +08:00
if ( ! is_interactive | | is_function ( ) | | ( current_line_width ! = 0 ) )
2006-01-12 20:54:57 +08:00
{
2006-01-31 00:51:50 +08:00
// Workaround since it seems impossible to print 0 copies of a character using %*lc
2006-01-26 22:48:10 +08:00
if ( offset + current_line_width )
{
sb_printf ( lineinfo ,
L " %ls \n %*lc^ \n " ,
line ,
offset + current_line_width ,
L ' ' ) ;
}
else
{
2006-01-31 03:53:10 +08:00
sb_printf ( lineinfo ,
2006-01-26 22:48:10 +08:00
L " %ls \n ^ \n " ,
line ) ;
}
2006-01-12 20:54:57 +08:00
}
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
free ( line ) ;
2006-01-26 22:48:10 +08:00
parser_stack_trace ( current_block , lineinfo ) ;
2005-09-20 21:26:39 +08:00
2006-01-12 20:54:57 +08:00
return ( wchar_t * ) lineinfo - > buff ;
2005-09-20 21:26:39 +08:00
}
int parser_get_pos ( )
{
return tok_get_pos ( current_tokenizer ) ;
}
int parser_get_job_pos ( )
{
return job_start_pos ;
}
void parser_set_pos ( int p )
{
tok_set_pos ( current_tokenizer , p ) ;
}
const wchar_t * parser_get_buffer ( )
{
return tok_string ( current_tokenizer ) ;
}
int parser_is_help ( wchar_t * s , int min_match )
{
int len = wcslen ( s ) ;
2006-07-24 20:48:45 +08:00
min_match = maxi ( min_match , 3 ) ;
2005-09-20 21:26:39 +08:00
return ( wcscmp ( L " -h " , s ) = = 0 ) | |
2006-07-24 20:48:45 +08:00
( len > = min_match & & ( wcsncmp ( L " --help " , s , len ) = = 0 ) ) ;
2005-09-20 21:26:39 +08: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
*/
static void parse_job_main_loop ( process_t * p ,
job_t * j ,
tokenizer * tok ,
array_list_t * args )
{
int is_finished = 0 ;
int proc_is_count = 0 ;
2005-12-04 00:43:56 +08:00
int matched_wildcard = 0 , unmatched_wildcard = 0 ;
2006-01-31 03:53:10 +08:00
2005-12-04 00:43:56 +08:00
wchar_t * unmatched = 0 ;
int unmatched_pos = 0 ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
/*
Test if this is the ' count ' command . We need to special case
2006-06-05 08:42:01 +08: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 21:26:39 +08:00
*/
if ( p - > actual_cmd )
{
wchar_t * woot = wcsrchr ( p - > actual_cmd , L ' / ' ) ;
if ( ! woot )
woot = p - > actual_cmd ;
else
woot + + ;
proc_is_count = wcscmp ( woot , L " count " ) = = 0 ;
}
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
while ( 1 )
{
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
switch ( tok_last_type ( tok ) )
{
case TOK_PIPE :
2006-02-19 09:54:38 +08:00
{
2005-09-20 21:26:39 +08:00
if ( ( p - > type = = INTERNAL_EXEC ) )
{
error ( SYNTAX_ERROR ,
2005-12-08 00:06:47 +08:00
tok_get_pos ( tok ) ,
EXEC_ERR_MSG ) ;
2006-01-31 03:53:10 +08:00
return ;
2005-09-20 21:26:39 +08:00
}
2005-10-07 22:08:57 +08:00
p - > pipe_fd = wcstol ( tok_last ( tok ) , 0 , 10 ) ;
2006-02-23 01:51:07 +08:00
if ( ! p - > argv )
halloc_register ( j , p - > argv = list_to_char_arr ( args ) ) ;
2006-02-06 22:25:02 +08:00
p - > next = halloc ( j , sizeof ( process_t ) ) ;
2005-09-20 21:26:39 +08:00
if ( p - > next = = 0 )
{
2006-07-03 18:39:57 +08:00
DIE_MEM ( ) ;
2005-09-20 21:26:39 +08:00
}
tok_next ( tok ) ;
2006-02-19 09:17:02 +08:00
2006-02-19 09:54:38 +08:00
/*
2006-06-09 07:55:57 +08:00
Don ' t do anything on failiure . parse_job will notice
the error flag and report any errors for us
2006-02-19 09:54:38 +08:00
*/
parse_job ( p - > next , j , tok ) ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
is_finished = 1 ;
break ;
2006-02-19 09:54:38 +08:00
}
2005-09-20 21:26:39 +08:00
case TOK_BACKGROUND :
2006-02-19 09:54:38 +08:00
{
2005-09-20 21:26:39 +08:00
j - > fg = 0 ;
2006-02-19 09:54:38 +08:00
}
2005-09-20 21:26:39 +08:00
case TOK_END :
{
2006-02-23 01:51:07 +08:00
if ( ! p - > argv )
halloc_register ( j , p - > argv = list_to_char_arr ( args ) ) ;
2005-09-20 21:26:39 +08:00
if ( tok_has_next ( tok ) )
tok_next ( tok ) ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
is_finished = 1 ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
break ;
}
case TOK_STRING :
{
int skip = 0 ;
if ( current_block - > skip )
{
2005-09-25 03:31:17 +08:00
/*
If this command should be skipped , we do not expand the arguments
*/
2005-09-20 21:26:39 +08:00
skip = 1 ;
2006-01-31 03:53:10 +08:00
2005-09-25 03:31:17 +08:00
/*
But if this is in fact a case statement , then it should be evaluated
*/
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
if ( ( current_block - > type = = SWITCH ) & &
( wcscmp ( al_get ( args , 0 ) , L " case " ) = = 0 ) & &
2005-09-25 03:31:17 +08:00
p - > type = = INTERNAL_BUILTIN )
2005-09-20 21:26:39 +08:00
{
skip = 0 ;
}
}
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
if ( ! skip )
{
if ( proc_is_count & &
( al_get_count ( args ) = = 1 ) & &
( parser_is_help ( tok_last ( tok ) , 0 ) ) )
{
/*
Display help for count
*/
p - > type = INTERNAL_BUILTIN ;
2006-02-07 02:11:01 +08:00
p - > actual_cmd = L " count " ;
2005-09-20 21:26:39 +08:00
}
2006-01-31 03:53:10 +08:00
2005-12-04 00:43:56 +08:00
2006-02-09 23:50:20 +08:00
switch ( expand_string ( j , wcsdup ( tok_last ( tok ) ) , args , 0 ) )
2005-09-20 21:26:39 +08:00
{
2005-12-04 00:43:56 +08:00
case EXPAND_ERROR :
2005-09-20 21:26:39 +08:00
{
2005-12-04 00:43:56 +08:00
err_pos = tok_get_pos ( tok ) ;
if ( error_code = = 0 )
{
2005-12-08 00:06:47 +08:00
error ( SYNTAX_ERROR ,
tok_get_pos ( tok ) ,
2006-01-04 20:51:02 +08:00
_ ( L " Could not expand string '%ls' " ) ,
2005-12-08 00:06:47 +08:00
tok_last ( tok ) ) ;
2006-01-31 03:53:10 +08:00
2005-12-04 00:43:56 +08:00
}
break ;
2005-09-20 21:26:39 +08:00
}
2006-01-31 03:53:10 +08:00
2005-12-04 00:43:56 +08:00
case EXPAND_WILDCARD_NO_MATCH :
{
unmatched_wildcard = 1 ;
if ( ! unmatched )
{
2006-02-11 08:13:17 +08:00
unmatched = halloc_wcsdup ( j , tok_last ( tok ) ) ;
2005-12-04 00:43:56 +08:00
unmatched_pos = tok_get_pos ( tok ) ;
}
2006-01-31 03:53:10 +08:00
2005-12-04 00:43:56 +08:00
break ;
}
2006-01-31 03:53:10 +08:00
2005-12-04 00:43:56 +08:00
case EXPAND_WILDCARD_MATCH :
{
matched_wildcard = 1 ;
break ;
}
2006-01-31 03:53:10 +08:00
2005-12-04 00:43:56 +08:00
case EXPAND_OK :
{
break ;
}
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
}
2005-12-04 00:43:56 +08:00
2005-09-20 21:26:39 +08:00
}
break ;
}
case TOK_REDIRECT_OUT :
case TOK_REDIRECT_IN :
case TOK_REDIRECT_APPEND :
case TOK_REDIRECT_FD :
{
int type = tok_last_type ( tok ) ;
io_data_t * new_io ;
wchar_t * target = 0 ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
/*
Don ' t check redirections in skipped part
2005-12-14 09:07:12 +08:00
Otherwise , bogus errors may be the result . ( Do check
that token is string , though )
2005-09-20 21:26:39 +08:00
*/
if ( current_block - > skip )
{
tok_next ( tok ) ;
2005-12-14 09:07:12 +08: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-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
break ;
}
2006-01-31 03:53:10 +08:00
2006-02-06 22:25:02 +08:00
new_io = halloc ( j , sizeof ( io_data_t ) ) ;
2005-09-20 21:26:39 +08:00
if ( ! new_io )
2006-07-03 18:39:57 +08:00
DIE_MEM ( ) ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
new_io - > fd = wcstol ( tok_last ( tok ) ,
0 ,
10 ) ;
tok_next ( tok ) ;
switch ( tok_last_type ( tok ) )
{
case TOK_STRING :
{
2006-02-09 23:50:20 +08:00
target = ( wchar_t * ) expand_one ( j , wcsdup ( tok_last ( tok ) ) , 0 ) ;
2006-02-06 22:25:02 +08:00
2005-09-20 21:26:39 +08:00
if ( target = = 0 & & error_code = = 0 )
{
2005-12-08 00:06:47 +08:00
error ( SYNTAX_ERROR ,
tok_get_pos ( tok ) ,
2005-12-14 09:07:12 +08:00
REDIRECT_TOKEN_ERR_MSG ,
2005-12-08 00:06:47 +08:00
tok_last ( tok ) ) ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
}
2006-07-21 09:08:31 +08:00
break ;
2005-09-20 21:26:39 +08:00
}
2006-07-21 09:08:31 +08:00
2005-09-20 21:26:39 +08:00
default :
2005-12-08 00:06:47 +08:00
error ( SYNTAX_ERROR ,
tok_get_pos ( tok ) ,
2005-12-14 11:39:39 +08:00
REDIRECT_TOKEN_ERR_MSG ,
2005-12-08 00:06:47 +08:00
tok_get_desc ( tok_last_type ( tok ) ) ) ;
2005-09-20 21:26:39 +08:00
}
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
if ( target = = 0 | | wcslen ( target ) = = 0 )
{
if ( error_code = = 0 )
error ( SYNTAX_ERROR ,
2005-12-08 00:06:47 +08:00
tok_get_pos ( tok ) ,
2006-01-04 20:51:02 +08:00
_ ( L " Invalid IO redirection " ) ) ;
2005-09-20 21:26:39 +08:00
tok_next ( tok ) ;
}
else
{
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
switch ( type )
{
case TOK_REDIRECT_APPEND :
new_io - > io_mode = IO_FILE ;
2005-10-12 03:31:16 +08:00
new_io - > param2 . flags = O_CREAT | O_APPEND | O_WRONLY ;
new_io - > param1 . filename = target ;
2005-09-20 21:26:39 +08:00
break ;
case TOK_REDIRECT_OUT :
new_io - > io_mode = IO_FILE ;
2005-10-12 03:31:16 +08:00
new_io - > param2 . flags = O_CREAT | O_WRONLY | O_TRUNC ;
new_io - > param1 . filename = target ;
2005-09-20 21:26:39 +08:00
break ;
case TOK_REDIRECT_IN :
new_io - > io_mode = IO_FILE ;
2005-10-12 03:31:16 +08:00
new_io - > param2 . flags = O_RDONLY ;
new_io - > param1 . filename = target ;
2005-09-20 21:26:39 +08:00
break ;
case TOK_REDIRECT_FD :
2006-07-21 09:08:31 +08:00
{
2005-09-20 21:26:39 +08:00
if ( wcscmp ( target , L " - " ) = = 0 )
{
new_io - > io_mode = IO_CLOSE ;
}
else
{
new_io - > io_mode = IO_FD ;
2005-10-12 03:31:16 +08:00
new_io - > param1 . old_fd = wcstol ( target ,
2005-12-14 09:07:12 +08:00
0 ,
10 ) ;
2005-10-12 03:31:16 +08:00
if ( ( new_io - > param1 . old_fd < 0 ) | |
( new_io - > param1 . old_fd > 10 ) )
2005-09-20 21:26:39 +08:00
{
2005-12-08 00:06:47 +08:00
error ( SYNTAX_ERROR ,
tok_get_pos ( tok ) ,
2006-01-04 20:51:02 +08:00
_ ( L " Requested redirection to something that is not a file descriptor %ls " ) ,
2005-12-08 00:06:47 +08:00
target ) ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
tok_next ( tok ) ;
}
}
break ;
2006-07-21 09:08:31 +08:00
}
2005-09-20 21:26:39 +08:00
}
}
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
j - > io = io_add ( j - > io , new_io ) ;
2006-02-06 22:25:02 +08:00
2005-09-20 21:26:39 +08:00
}
break ;
case TOK_ERROR :
2006-01-31 03:53:10 +08:00
{
2005-12-08 00:06:47 +08:00
error ( SYNTAX_ERROR ,
tok_get_pos ( tok ) ,
TOK_ERR_MSG ,
tok_last ( tok ) ) ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
return ;
}
default :
2005-12-08 00:06:47 +08:00
error ( SYNTAX_ERROR ,
tok_get_pos ( tok ) ,
2005-12-14 11:39:39 +08:00
UNEXPECTED_TOKEN_ERR_MSG ,
2005-12-08 00:06:47 +08:00
tok_get_desc ( tok_last_type ( tok ) ) ) ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
tok_next ( tok ) ;
break ;
}
if ( ( is_finished ) | | ( error_code ! = 0 ) )
break ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
tok_next ( tok ) ;
}
2005-12-04 00:43:56 +08:00
if ( ! error_code )
{
if ( unmatched_wildcard & & ! matched_wildcard )
{
2005-12-09 10:41:16 +08:00
j - > wildcard_error = 1 ;
proc_set_last_status ( 1 ) ;
if ( is_interactive & & ! is_block )
{
int tmp ;
2006-01-31 03:53:10 +08:00
2005-12-09 10:41:16 +08:00
debug ( 1 , WILDCARD_ERR_MSG , unmatched ) ;
tmp = current_tokenizer_pos ;
current_tokenizer_pos = unmatched_pos ;
fwprintf ( stderr , L " %ls " , parser_current_line ( ) ) ;
2006-01-31 03:53:10 +08:00
2005-12-09 10:41:16 +08:00
current_tokenizer_pos = tmp ;
}
2006-01-31 03:53:10 +08:00
2005-12-04 00:43:56 +08:00
}
}
2005-09-20 21:26:39 +08:00
return ;
}
2006-07-21 09:08:31 +08:00
2005-09-20 21:26:39 +08:00
/**
2006-06-09 07:55:57 +08: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 21:26:39 +08: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
\ return 1 on success , 0 on error
*/
static int parse_job ( process_t * p ,
job_t * j ,
tokenizer * tok )
{
2006-02-09 23:50:20 +08:00
array_list_t * args = al_halloc ( j ) ; // The list that will become the argc array for the program
2005-09-20 21:26:39 +08: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-01-31 03:53:10 +08:00
int is_new_block = 0 ; // Does this command create a new block?
block_t * prev_block = current_block ;
2006-02-09 02:44:37 +08:00
int prev_tokenizer_pos = current_tokenizer_pos ;
2005-09-20 21:26:39 +08:00
current_tokenizer_pos = tok_get_pos ( tok ) ;
2006-02-09 23:50:20 +08:00
while ( al_get_count ( args ) = = 0 )
2005-09-20 21:26:39 +08:00
{
wchar_t * nxt = 0 ;
2006-06-16 20:56:16 +08: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
2005-09-20 21:26:39 +08:00
switch ( tok_last_type ( tok ) )
{
case TOK_STRING :
2005-12-04 00:43:56 +08:00
{
2006-02-09 23:50:20 +08:00
nxt = expand_one ( j ,
wcsdup ( tok_last ( tok ) ) ,
2006-08-22 22:38:31 +08:00
EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_VARIABLES ) ;
2006-02-09 23:50:20 +08:00
2005-09-20 21:26:39 +08:00
if ( nxt = = 0 )
{
2005-12-08 00:06:47 +08:00
error ( SYNTAX_ERROR ,
tok_get_pos ( tok ) ,
2005-12-14 11:39:39 +08:00
ILLEGAL_CMD_ERR_MSG ,
2005-12-08 00:06:47 +08:00
tok_last ( tok ) ) ;
2006-02-09 23:50:20 +08:00
2006-02-09 02:44:37 +08:00
current_tokenizer_pos = prev_tokenizer_pos ;
2005-09-20 21:26:39 +08:00
return 0 ;
}
break ;
2005-12-04 00:43:56 +08:00
}
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
case TOK_ERROR :
{
2005-12-08 00:06:47 +08:00
error ( SYNTAX_ERROR ,
tok_get_pos ( tok ) ,
TOK_ERR_MSG ,
tok_last ( tok ) ) ;
2006-01-31 03:53:10 +08:00
2006-02-09 02:44:37 +08:00
current_tokenizer_pos = prev_tokenizer_pos ;
2005-09-20 21:26:39 +08:00
return 0 ;
}
2006-01-06 00:02:28 +08:00
case TOK_PIPE :
{
wchar_t * str = tok_string ( tok ) ;
if ( tok_get_pos ( tok ) > 0 & & str [ tok_get_pos ( tok ) - 1 ] = = L ' | ' )
{
error ( SYNTAX_ERROR ,
tok_get_pos ( tok ) ,
CMD_OR_ERR_MSG ,
tok_get_desc ( tok_last_type ( tok ) ) ) ;
}
else
{
error ( SYNTAX_ERROR ,
2006-04-21 02:35:02 +08:00
tok_get_pos ( tok ) ,
CMD_ERR_MSG ,
2006-01-06 00:02:28 +08:00
tok_get_desc ( tok_last_type ( tok ) ) ) ;
}
2006-01-31 03:53:10 +08:00
2006-02-09 02:44:37 +08:00
current_tokenizer_pos = prev_tokenizer_pos ;
2006-01-06 00:02:28 +08:00
return 0 ;
}
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
default :
2005-12-04 00:43:56 +08:00
{
2005-12-08 00:06:47 +08:00
error ( SYNTAX_ERROR ,
tok_get_pos ( tok ) ,
2005-12-14 11:39:39 +08:00
CMD_ERR_MSG ,
2005-12-08 00:06:47 +08:00
tok_get_desc ( tok_last_type ( tok ) ) ) ;
2006-01-31 03:53:10 +08:00
2006-02-09 02:44:37 +08:00
current_tokenizer_pos = prev_tokenizer_pos ;
2005-09-20 21:26:39 +08:00
return 0 ;
2005-12-04 00:43:56 +08:00
}
2005-09-20 21:26:39 +08:00
}
2006-02-09 23:50:20 +08:00
2006-06-16 20:56:16 +08:00
mark = tok_get_pos ( tok ) ;
2006-02-09 23:50:20 +08:00
2006-06-16 20:56:16 +08:00
/*
Test if this command is one of the many special builtins
that work directly on the parser , like e . g . ' not ' , that
simply flips the status inversion flag in the job .
*/
2005-09-20 21:26:39 +08:00
if ( wcscmp ( L " command " , nxt ) = = 0 )
{
tok_next ( tok ) ;
if ( parser_is_help ( tok_last ( tok ) , 0 ) )
{
tok_set_pos ( tok , mark ) ;
}
else
{
use_function = 0 ;
use_builtin = 0 ;
2006-02-09 23:50:20 +08:00
consumed = 1 ;
2005-09-20 21:26:39 +08:00
}
}
else if ( wcscmp ( L " builtin " , nxt ) = = 0 )
{
tok_next ( tok ) ;
if ( tok_last ( tok ) [ 0 ] = = L ' - ' )
{
tok_set_pos ( tok , mark ) ;
}
else
{
use_function = 0 ;
2006-02-09 23:50:20 +08:00
consumed = 1 ;
2005-09-20 21:26:39 +08:00
}
}
else if ( wcscmp ( L " not " , nxt ) = = 0 )
{
tok_next ( tok ) ;
if ( tok_last ( tok ) [ 0 ] = = L ' - ' )
{
tok_set_pos ( tok , mark ) ;
}
else
{
j - > negate = 1 - j - > negate ;
2006-02-09 23:50:20 +08:00
consumed = 1 ;
2005-09-20 21:26:39 +08:00
}
}
else if ( wcscmp ( L " and " , nxt ) = = 0 )
{
tok_next ( tok ) ;
if ( tok_last ( tok ) [ 0 ] = = L ' - ' )
{
tok_set_pos ( tok , mark ) ;
}
else
{
2006-01-31 03:53:10 +08:00
j - > skip = proc_get_last_status ( ) ;
2006-02-09 23:50:20 +08:00
consumed = 1 ;
2005-09-20 21:26:39 +08:00
}
}
else if ( wcscmp ( L " or " , nxt ) = = 0 )
{
tok_next ( tok ) ;
if ( tok_last ( tok ) [ 0 ] = = L ' - ' )
{
tok_set_pos ( tok , mark ) ;
}
else
{
2006-01-31 03:53:10 +08:00
j - > skip = ! proc_get_last_status ( ) ;
2006-02-09 23:50:20 +08:00
consumed = 1 ;
2005-09-20 21:26:39 +08:00
}
}
else if ( wcscmp ( L " exec " , nxt ) = = 0 )
{
if ( p ! = j - > first_process )
{
error ( SYNTAX_ERROR ,
2005-12-08 00:06:47 +08:00
tok_get_pos ( tok ) ,
EXEC_ERR_MSG ) ;
2006-02-09 02:44:37 +08:00
current_tokenizer_pos = prev_tokenizer_pos ;
2005-09-20 21:26:39 +08:00
return 0 ;
}
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
tok_next ( tok ) ;
if ( tok_last ( tok ) [ 0 ] = = L ' - ' )
{
tok_set_pos ( tok , mark ) ;
}
else
{
use_function = 0 ;
use_builtin = 0 ;
2006-01-31 03:53:10 +08:00
p - > type = INTERNAL_EXEC ;
2006-02-09 23:50:20 +08:00
consumed = 1 ;
2006-02-09 02:44:37 +08:00
current_tokenizer_pos = prev_tokenizer_pos ;
2005-09-20 21:26:39 +08:00
}
}
else if ( wcscmp ( L " while " , nxt ) = = 0 )
{
int new_block = 0 ;
tok_next ( tok ) ;
2005-12-04 00:43:56 +08:00
if ( ( current_block - > type ! = WHILE ) )
2005-09-20 21:26:39 +08:00
{
new_block = 1 ;
}
2005-10-12 03:31:16 +08:00
else if ( current_block - > param1 . while_state = = WHILE_TEST_AGAIN )
2005-09-20 21:26:39 +08:00
{
2005-10-12 03:31:16 +08:00
current_block - > param1 . while_state = WHILE_TEST_FIRST ;
2005-09-20 21:26:39 +08:00
}
else
{
new_block = 1 ;
}
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
if ( new_block )
{
parser_push_block ( WHILE ) ;
2005-10-12 03:31:16 +08:00
current_block - > param1 . while_state = WHILE_TEST_FIRST ;
2005-09-20 21:26:39 +08:00
current_block - > tok_pos = mark ;
}
2006-01-31 03:53:10 +08:00
2006-02-09 23:50:20 +08:00
consumed = 1 ;
2005-09-20 21:26:39 +08:00
is_new_block = 1 ;
2006-05-15 06:29:05 +08:00
2005-09-20 21:26:39 +08:00
}
else if ( wcscmp ( L " if " , nxt ) = = 0 )
{
tok_next ( tok ) ;
parser_push_block ( IF ) ;
2005-10-12 03:31:16 +08:00
current_block - > param1 . if_state = 0 ;
2005-09-20 21:26:39 +08:00
current_block - > tok_pos = mark ;
is_new_block = 1 ;
2006-02-09 23:50:20 +08:00
consumed = 1 ;
}
2006-06-16 20:56:16 +08:00
/*
Test if we need another command
*/
2006-02-09 23:50:20 +08:00
if ( consumed )
{
2006-06-16 20:56:16 +08:00
/*
Yes we do , around in the loop for another lap , then !
*/
2005-09-20 21:26:39 +08:00
continue ;
}
2006-07-21 09:08:31 +08:00
2006-02-08 17:20:05 +08:00
if ( use_function & & ! current_block - > skip )
2005-09-20 21:26:39 +08:00
{
int nxt_forbidden ;
wchar_t * forbid ;
2006-01-31 00:51:50 +08:00
forbid = ( wchar_t * ) ( al_get_count ( forbidden_function ) ? al_peek ( forbidden_function ) : 0 ) ;
2005-09-20 21:26:39 +08:00
nxt_forbidden = forbid & & ( wcscmp ( forbid , nxt ) = = 0 ) ;
2006-07-12 22:22:42 +08:00
2005-09-20 21:26:39 +08:00
/*
Make feeble attempt to avoid infinite recursion . Will at
least catch some accidental infinite recursion calls .
*/
2006-07-12 22:22:42 +08:00
2005-09-20 21:26:39 +08:00
if ( function_exists ( nxt ) & & ! nxt_forbidden )
{
/*
Check if we have reached the maximum recursion depth
*/
2006-01-31 00:51:50 +08:00
if ( al_get_count ( forbidden_function ) > MAX_RECURSION_DEPTH )
2005-09-20 21:26:39 +08:00
{
error ( SYNTAX_ERROR ,
2005-12-08 00:06:47 +08:00
tok_get_pos ( tok ) ,
RECURSION_ERR_MSG ) ;
2005-09-20 21:26:39 +08:00
}
else
{
p - > type = INTERNAL_FUNCTION ;
}
}
}
2006-02-09 23:50:20 +08:00
al_push ( args , nxt ) ;
2005-09-20 21:26:39 +08:00
}
if ( error_code = = 0 )
{
if ( ! p - > type )
{
if ( use_builtin & &
2006-02-09 23:50:20 +08:00
builtin_exists ( ( wchar_t * ) al_get ( args , 0 ) ) )
2005-09-20 21:26:39 +08:00
{
p - > type = INTERNAL_BUILTIN ;
2006-05-15 06:29:05 +08:00
is_new_block | = parser_is_block ( ( wchar_t * ) al_get ( args , 0 ) ) ;
2006-01-31 03:53:10 +08:00
}
2005-09-20 21:26:39 +08:00
}
if ( ! p - > type | | ( p - > type = = INTERNAL_EXEC ) )
{
2006-01-31 03:53:10 +08:00
/*
2006-04-21 02:35:02 +08:00
If we are not executing the current block , allow
non - existent commands .
2005-09-20 21:26:39 +08:00
*/
if ( current_block - > skip )
{
2006-02-07 02:11:01 +08:00
p - > actual_cmd = L " " ;
2005-09-20 21:26:39 +08:00
}
else
{
2006-06-12 22:12:33 +08:00
p - > actual_cmd = parser_get_filename ( j , ( wchar_t * ) al_get ( args , 0 ) ) ;
2006-02-07 02:11:01 +08:00
2005-09-20 21:26:39 +08:00
/*
Check if the specified command exists
*/
if ( p - > actual_cmd = = 0 )
{
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
/*
That is not a command ! Test if it is a
directory , in which case , we use ' cd ' as the
implicit command .
*/
wchar_t * pp =
2006-06-12 22:12:33 +08:00
parser_cdpath_get ( j , ( wchar_t * ) al_get ( args , 0 ) ) ;
2005-09-20 21:26:39 +08:00
if ( pp )
{
wchar_t * tmp ;
2006-01-31 03:53:10 +08:00
2006-02-09 23:50:20 +08:00
tmp = ( wchar_t * ) al_get ( args , 0 ) ;
al_truncate ( args , 0 ) ;
2006-05-28 19:06:30 +08:00
al_push ( args , halloc_wcsdup ( j , L " cd " ) ) ;
2006-02-09 23:50:20 +08:00
al_push ( args , tmp ) ;
2005-09-20 21:26:39 +08:00
/*
2006-01-31 03:53:10 +08:00
If we have defined a wrapper around cd , use it ,
2005-09-20 21:26:39 +08:00
otherwise use the cd builtin
*/
if ( function_exists ( L " cd " ) )
p - > type = INTERNAL_FUNCTION ;
else
p - > type = INTERNAL_BUILTIN ;
}
else
{
2006-02-20 20:55:24 +08:00
int tmp ;
2006-04-21 22:29:39 +08:00
wchar_t * cmd = ( wchar_t * ) al_get ( args , 0 ) ;
2006-02-20 20:55:24 +08:00
/*
2006-07-20 07:20:20 +08:00
We couldn ' t find the specified command .
2006-05-22 03:25:24 +08:00
2006-02-20 20:55:24 +08:00
What we want to happen now is that the
specified job won ' t get executed , and an
error message is printed on - screen , but
otherwise , the parsing / execution of the
file continues . Because of this , we don ' t
want to call error ( ) , since that would stop
execution of the file . Instead we let
p - > actual_command be 0 ( null ) , which will
cause the job to silently not execute . We
also print an error message .
*/
2006-04-21 22:29:39 +08:00
if ( wcschr ( cmd , L ' = ' ) )
2005-12-08 00:06:47 +08:00
{
2006-07-20 07:20:20 +08:00
wchar_t * cpy = halloc_wcsdup ( j , cmd ) ;
wchar_t * valpart = wcschr ( cpy , L ' = ' ) ;
* valpart + + = 0 ;
2006-02-20 20:55:24 +08:00
debug ( 0 ,
2005-12-08 00:06:47 +08:00
COMMAND_ASSIGN_ERR_MSG ,
2006-07-20 07:20:20 +08:00
cmd ,
cpy ,
valpart ) ;
2005-12-08 00:06:47 +08:00
}
2006-04-21 22:29:39 +08:00
else if ( cmd [ 0 ] = = L ' $ ' )
{
wchar_t * val = env_get ( cmd + 1 ) ;
if ( val )
{
debug ( 0 ,
2006-04-22 18:06:30 +08:00
_ ( 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'. " ) ,
2006-04-21 22:29:39 +08:00
cmd + 1 ,
val ,
cmd ) ;
}
else
{
debug ( 0 ,
2006-04-22 18:06:30 +08:00
_ ( L " Variables may not be used as commands. Instead, define a function. See the help section for the function command by typing 'help function'. " ) ,
2006-04-21 22:29:39 +08:00
cmd ) ;
}
}
else if ( wcschr ( cmd , L ' $ ' ) )
2005-12-08 00:06:47 +08:00
{
2006-04-21 22:29:39 +08:00
debug ( 0 ,
2006-04-22 18:06:30 +08:00
_ ( 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'. " ) ,
2006-04-21 22:29:39 +08:00
cmd ,
cmd ) ;
}
else
{
2006-02-20 20:55:24 +08:00
debug ( 0 ,
2006-01-04 20:51:02 +08:00
_ ( L " Unknown command '%ls' " ) ,
2006-04-21 22:29:39 +08:00
cmd ) ;
2005-12-08 00:06:47 +08:00
}
2006-02-20 20:55:24 +08:00
tmp = current_tokenizer_pos ;
current_tokenizer_pos = tok_get_pos ( tok ) ;
fwprintf ( stderr , L " %ls " , parser_current_line ( ) ) ;
2006-02-20 21:11:46 +08:00
2006-02-20 20:55:24 +08:00
current_tokenizer_pos = tmp ;
j - > skip = 1 ;
2005-09-20 21:26:39 +08:00
}
}
}
}
}
2006-02-20 20:55:24 +08:00
2005-09-20 21:26:39 +08:00
if ( is_new_block )
{
2006-02-09 02:44:37 +08:00
2006-01-31 03:53:10 +08:00
const wchar_t * end = parser_find_end ( tok_string ( tok ) +
2005-09-20 21:26:39 +08:00
current_tokenizer_pos ) ;
tokenizer subtok ;
int make_sub_block = j - > first_process ! = p ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
if ( ! end )
{
error ( SYNTAX_ERROR ,
2005-12-08 00:06:47 +08:00
tok_get_pos ( tok ) ,
2005-12-14 11:39:39 +08:00
BLOCK_END_ERR_MSG ) ;
2006-02-09 23:50:20 +08:00
2005-09-20 21:26:39 +08:00
}
if ( ! make_sub_block )
{
2006-05-15 06:29:05 +08:00
2005-09-20 21:26:39 +08:00
tok_init ( & subtok , end , 0 ) ;
switch ( tok_last_type ( & subtok ) )
{
case TOK_END :
break ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
case TOK_REDIRECT_OUT :
case TOK_REDIRECT_APPEND :
case TOK_REDIRECT_IN :
case TOK_REDIRECT_FD :
case TOK_PIPE :
{
make_sub_block = 1 ;
2006-01-31 03:53:10 +08:00
break ;
2005-09-20 21:26:39 +08:00
}
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
default :
{
2005-10-08 08:23:49 +08:00
error ( SYNTAX_ERROR ,
2005-12-08 00:06:47 +08:00
current_tokenizer_pos ,
BLOCK_END_ERR_MSG ) ;
2005-09-20 21:26:39 +08:00
}
}
tok_destroy ( & subtok ) ;
}
2006-05-18 21:00:39 +08:00
2005-09-20 21:26:39 +08:00
if ( make_sub_block )
{
2006-05-18 21:00:39 +08:00
2005-09-20 21:26:39 +08:00
int end_pos = end - tok_string ( tok ) ;
2006-05-15 06:39:03 +08:00
wchar_t * sub_block = halloc_wcsndup ( j ,
tok_string ( tok ) + current_tokenizer_pos ,
end_pos - current_tokenizer_pos ) ;
2006-05-18 21:00:39 +08:00
2005-09-20 21:26:39 +08:00
p - > type = INTERNAL_BLOCK ;
2006-02-09 23:50:20 +08:00
al_set ( args , 0 , sub_block ) ;
2006-05-15 06:39:03 +08:00
2006-01-31 03:53:10 +08:00
tok_set_pos ( tok ,
2005-09-20 21:26:39 +08:00
end_pos ) ;
while ( prev_block ! = current_block )
2005-12-12 23:51:04 +08:00
{
2005-09-20 21:26:39 +08:00
parser_pop_block ( ) ;
2005-12-12 23:51:04 +08:00
}
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
}
else tok_next ( tok ) ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
}
else tok_next ( tok ) ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
if ( ! error_code )
2005-09-25 19:25:42 +08:00
{
2006-02-09 23:50:20 +08:00
if ( p - > type = = INTERNAL_BUILTIN & & parser_skip_arguments ( ( wchar_t * ) al_get ( args , 0 ) ) )
2006-02-11 08:13:17 +08:00
{
2006-02-23 01:51:07 +08:00
if ( ! p - > argv )
halloc_register ( j , p - > argv = list_to_char_arr ( args ) ) ;
2005-09-25 19:25:42 +08:00
}
else
{
2006-02-09 23:50:20 +08:00
parse_job_main_loop ( p , j , tok , args ) ;
2005-09-25 19:25:42 +08:00
}
}
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
if ( error_code )
{
/*
Make sure the block stack is consistent
*/
while ( prev_block ! = current_block )
2005-12-12 23:51:04 +08:00
{
2005-09-20 21:26:39 +08:00
parser_pop_block ( ) ;
2005-12-12 23:51:04 +08:00
}
2005-09-20 21:26:39 +08:00
}
2006-02-09 02:44:37 +08:00
current_tokenizer_pos = prev_tokenizer_pos ;
2005-09-20 21:26:39 +08: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-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
*/
static void skipped_exec ( job_t * j )
{
process_t * p ;
2005-12-09 10:41:16 +08:00
2005-09-20 21:26:39 +08:00
for ( p = j - > first_process ; p ; p = p - > next )
{
2005-12-09 10:41:16 +08:00
if ( p - > type = = INTERNAL_BUILTIN )
2005-09-20 21:26:39 +08:00
{
if ( ( wcscmp ( p - > argv [ 0 ] , L " for " ) = = 0 ) | |
( wcscmp ( p - > argv [ 0 ] , L " switch " ) = = 0 ) | |
2005-12-12 23:51:04 +08:00
( wcscmp ( p - > argv [ 0 ] , L " begin " ) = = 0 ) | |
2005-09-20 21:26:39 +08:00
( wcscmp ( p - > argv [ 0 ] , L " function " ) = = 0 ) )
{
parser_push_block ( FAKE ) ;
}
else if ( wcscmp ( p - > argv [ 0 ] , L " end " ) = = 0 )
{
if ( ! current_block - > outer - > skip )
{
exec ( j ) ;
return ;
}
parser_pop_block ( ) ;
}
else if ( wcscmp ( p - > argv [ 0 ] , L " else " ) = = 0 )
{
if ( ( current_block - > type = = IF ) & &
2005-10-12 03:31:16 +08:00
( current_block - > param1 . if_state ! = 0 ) )
2005-09-20 21:26:39 +08:00
{
exec ( j ) ;
return ;
}
}
else if ( wcscmp ( p - > argv [ 0 ] , L " case " ) = = 0 )
{
if ( ( current_block - > type = = SWITCH ) )
{
exec ( j ) ;
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-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
static void eval_job ( tokenizer * tok )
{
job_t * j ;
int start_pos = job_start_pos = tok_get_pos ( tok ) ;
long long t1 = 0 , t2 = 0 , t3 = 0 ;
profile_element_t * p = 0 ;
int skip = 0 ;
2006-02-19 09:17:02 +08:00
int job_begin_pos , prev_tokenizer_pos ;
2005-09-20 21:26:39 +08:00
if ( profile )
{
p = malloc ( sizeof ( profile_element_t ) ) ;
2006-01-31 03:53:10 +08:00
p - > cmd = 0 ;
2005-09-20 21:26:39 +08:00
al_push ( & profile_data , p ) ;
p - > skipped = 1 ;
t1 = get_time ( ) ;
}
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
switch ( tok_last_type ( tok ) )
{
case TOK_STRING :
{
j = job_create ( ) ;
j - > fg = 1 ;
2006-02-01 20:27:15 +08:00
j - > terminal = j - > job_control & & ( ! is_subshell & & ! is_event ) ;
2005-10-12 03:23:43 +08:00
j - > skip_notification = is_subshell | | is_block | | is_event | | ( ! is_interactive ) ;
2006-02-01 20:27:15 +08:00
2006-01-18 20:42:48 +08:00
current_block - > job = j ;
2006-02-01 20:27:15 +08:00
2005-09-20 21:26:39 +08:00
if ( is_interactive )
{
if ( tcgetattr ( 0 , & j - > tmodes ) )
{
tok_next ( tok ) ;
wperror ( L " tcgetattr " ) ;
job_free ( j ) ;
break ;
}
}
2006-01-31 03:53:10 +08:00
2006-02-06 22:25:02 +08:00
j - > first_process = halloc ( j , sizeof ( process_t ) ) ;
2005-09-20 21:26:39 +08:00
2006-02-19 09:17:02 +08:00
job_begin_pos = tok_get_pos ( tok ) ;
2005-09-20 21:26:39 +08:00
if ( parse_job ( j - > first_process , j , tok ) & &
j - > first_process - > argv )
{
if ( job_start_pos < tok_get_pos ( tok ) )
{
int stop_pos = tok_get_pos ( tok ) ;
2006-01-31 03:53:10 +08:00
wchar_t * newline = wcschr ( tok_string ( tok ) + start_pos ,
2005-09-20 21:26:39 +08:00
L ' \n ' ) ;
if ( newline )
stop_pos = mini ( stop_pos , newline - tok_string ( tok ) ) ;
2006-01-31 03:53:10 +08:00
2006-02-11 08:13:17 +08:00
j - > command = halloc_wcsndup ( j ,
tok_string ( tok ) + start_pos ,
stop_pos - start_pos ) ;
2005-09-20 21:26:39 +08:00
}
else
2006-02-07 02:11:01 +08:00
j - > command = L " " ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
if ( profile )
{
t2 = get_time ( ) ;
2006-01-31 03:53:10 +08:00
p - > cmd = wcsdup ( j - > command ) ;
2005-09-20 21:26:39 +08:00
p - > skipped = current_block - > skip ;
}
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
skip | = current_block - > skip ;
2005-12-09 10:41:16 +08:00
skip | = j - > wildcard_error ;
2006-01-05 23:37:53 +08:00
skip | = j - > skip ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
if ( ! skip )
{
2005-11-30 03:51:32 +08:00
int was_builtin = 0 ;
2005-12-01 23:45:44 +08:00
// if( j->first_process->type==INTERNAL_BUILTIN && !j->first_process->next)
// was_builtin = 1;
2006-02-19 09:17:02 +08:00
prev_tokenizer_pos = current_tokenizer_pos ;
current_tokenizer_pos = job_begin_pos ;
2006-01-31 03:53:10 +08:00
exec ( j ) ;
2006-02-19 09:17:02 +08:00
current_tokenizer_pos = prev_tokenizer_pos ;
2005-11-30 03:51:32 +08:00
/* Only external commands require a new fishd barrier */
if ( ! was_builtin )
proc_had_barrier = 0 ;
2005-09-20 21:26:39 +08:00
}
else
{
2006-01-31 03:53:10 +08:00
skipped_exec ( j ) ;
2005-09-20 21:26:39 +08:00
}
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
if ( profile )
{
t3 = get_time ( ) ;
p - > level = eval_level ;
p - > parse = t2 - t1 ;
p - > exec = t3 - t2 ;
}
if ( current_block - > type = = WHILE )
{
2006-01-31 03:53:10 +08:00
2005-10-12 03:31:16 +08:00
switch ( current_block - > param1 . while_state )
2005-09-20 21:26:39 +08:00
{
case WHILE_TEST_FIRST :
{
current_block - > skip = proc_get_last_status ( ) ! = 0 ;
2005-10-12 03:31:16 +08:00
current_block - > param1 . while_state = WHILE_TESTED ;
2005-09-20 21:26:39 +08:00
}
break ;
}
}
if ( current_block - > type = = IF )
{
2005-10-12 03:31:16 +08:00
if ( ( ! current_block - > param1 . if_state ) & &
2005-09-20 21:26:39 +08:00
( ! current_block - > skip ) )
{
current_block - > skip = proc_get_last_status ( ) ! = 0 ;
2005-10-12 03:31:16 +08:00
current_block - > param1 . if_state + + ;
2005-09-20 21:26:39 +08:00
}
}
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
}
else
{
2005-09-25 03:31:17 +08:00
/*
2006-06-09 07:55:57 +08: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-25 03:31:17 +08:00
*/
2005-09-20 21:26:39 +08:00
job_free ( j ) ;
2005-12-07 21:02:09 +08:00
proc_set_last_status ( 1 ) ;
2005-09-20 21:26:39 +08:00
}
2005-10-15 08:51:26 +08:00
current_block - > job = 0 ;
2005-09-20 21:26:39 +08:00
break ;
}
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
case TOK_END :
{
if ( tok_has_next ( tok ) )
tok_next ( tok ) ;
break ;
}
2006-01-06 00:02:28 +08:00
case TOK_BACKGROUND :
{
wchar_t * str = tok_string ( tok ) ;
if ( tok_get_pos ( tok ) > 0 & & str [ tok_get_pos ( tok ) - 1 ] = = L ' & ' )
{
error ( SYNTAX_ERROR ,
tok_get_pos ( tok ) ,
CMD_AND_ERR_MSG ,
tok_get_desc ( tok_last_type ( tok ) ) ) ;
}
else
{
error ( SYNTAX_ERROR ,
tok_get_pos ( tok ) ,
CMD_ERR_MSG ,
tok_get_desc ( tok_last_type ( tok ) ) ) ;
}
2006-01-31 03:53:10 +08:00
2006-01-09 07:00:49 +08:00
return ;
2006-01-06 00:02:28 +08:00
}
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
case TOK_ERROR :
{
2005-12-08 00:06:47 +08:00
error ( SYNTAX_ERROR ,
tok_get_pos ( tok ) ,
TOK_ERR_MSG ,
tok_last ( tok ) ) ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
return ;
}
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
default :
{
2005-12-08 00:06:47 +08:00
error ( SYNTAX_ERROR ,
tok_get_pos ( tok ) ,
2005-12-14 11:39:39 +08:00
CMD_ERR_MSG ,
2005-12-08 00:06:47 +08:00
tok_get_desc ( tok_last_type ( tok ) ) ) ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
return ;
}
}
2006-01-31 03:53:10 +08:00
2005-10-12 03:23:43 +08:00
job_reap ( 0 ) ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
}
int eval ( const wchar_t * cmd , io_data_t * io , int block_type )
{
int forbid_count ;
int code ;
tokenizer * previous_tokenizer = current_tokenizer ;
block_t * start_current_block = current_block ;
io_data_t * prev_io = block_io ;
2006-01-31 00:51:50 +08:00
array_list_t * prev_forbidden = forbidden_function ;
if ( block_type = = SUBST )
{
forbidden_function = al_new ( ) ;
}
2006-02-13 19:09:29 +08:00
2006-01-31 00:51:50 +08:00
forbid_count = al_get_count ( forbidden_function ) ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
block_io = io ;
2006-01-31 03:53:10 +08:00
2005-10-12 03:23:43 +08:00
job_reap ( 0 ) ;
2005-12-12 23:51:04 +08:00
debug ( 4 , L " eval: %ls " , cmd ) ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
if ( ! cmd )
{
debug ( 1 ,
2006-01-04 20:51:02 +08:00
EVAL_NULL_ERR_MSG ) ;
debug ( 1 ,
BUGREPORT_MSG ,
2005-10-09 19:48:16 +08:00
PACKAGE_BUGREPORT ) ;
2005-09-20 21:26:39 +08:00
return 1 ;
}
2006-02-01 23:49:11 +08:00
if ( ( block_type ! = TOP ) & &
2005-09-20 21:26:39 +08:00
( block_type ! = SUBST ) )
{
debug ( 1 ,
2006-01-04 20:51:02 +08:00
INVALID_SCOPE_ERR_MSG ,
parser_get_block_desc ( block_type ) ) ;
2006-01-31 03:53:10 +08:00
2006-01-04 20:51:02 +08:00
debug ( 1 ,
BUGREPORT_MSG ,
2005-10-09 19:48:16 +08:00
PACKAGE_BUGREPORT ) ;
2005-12-14 09:07:12 +08:00
return 1 ;
}
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
eval_level + + ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
parser_push_block ( block_type ) ;
2006-01-31 03:53:10 +08:00
2006-02-01 23:49:11 +08:00
current_tokenizer = malloc ( sizeof ( tokenizer ) ) ;
2005-09-20 21:26:39 +08:00
tok_init ( current_tokenizer , cmd , 0 ) ;
2006-02-01 23:49:11 +08:00
2005-09-20 21:26:39 +08:00
error_code = 0 ;
2006-01-31 03:53:10 +08:00
event_fire ( 0 ) ;
2005-10-14 19:40:33 +08:00
2005-09-20 21:26:39 +08:00
while ( tok_has_next ( current_tokenizer ) & &
! error_code & &
! sanity_check ( ) & &
! exit_status ( ) )
2005-10-14 19:40:33 +08:00
{
2005-09-20 21:26:39 +08:00
eval_job ( current_tokenizer ) ;
2006-01-31 03:53:10 +08:00
event_fire ( 0 ) ;
2005-10-14 19:40:33 +08:00
}
2006-01-31 03:53:10 +08:00
int prev_block_type = current_block - > type ;
2005-09-20 21:26:39 +08:00
parser_pop_block ( ) ;
while ( start_current_block ! = current_block )
{
if ( current_block = = 0 )
{
debug ( 0 ,
2006-01-04 20:51:02 +08:00
_ ( L " End of block mismatch. Program terminating. " ) ) ;
debug ( 0 ,
2006-01-31 03:53:10 +08:00
BUGREPORT_MSG ,
2005-10-09 19:48:16 +08:00
PACKAGE_BUGREPORT ) ;
2005-09-20 21:26:39 +08:00
exit ( 1 ) ;
break ;
}
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
if ( ( ! error_code ) & & ( ! exit_status ( ) ) & & ( ! proc_get_last_status ( ) ) )
{
char * h ;
//debug( 2, L"Status %d\n", proc_get_last_status() );
2006-01-31 03:53:10 +08:00
debug ( 1 ,
2006-01-05 23:37:53 +08:00
L " %ls " , parser_get_block_desc ( current_block - > type ) ) ;
2006-01-31 03:53:10 +08:00
debug ( 1 ,
2006-01-05 23:37:53 +08:00
BLOCK_END_ERR_MSG ) ;
fwprintf ( stderr , L " %ls " , parser_current_line ( ) ) ;
2006-01-31 03:53:10 +08:00
2006-01-05 23:37:53 +08:00
h = builtin_help_get ( L " end " ) ;
if ( h )
fwprintf ( stderr , L " %s " , h ) ;
break ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
}
2006-01-31 03:53:10 +08:00
prev_block_type = current_block - > type ;
2005-09-20 21:26:39 +08:00
parser_pop_block ( ) ;
}
2006-06-02 10:15:17 +08:00
print_errors_stderr ( ) ;
2005-09-20 21:26:39 +08:00
tok_destroy ( current_tokenizer ) ;
free ( current_tokenizer ) ;
2006-01-31 00:51:50 +08:00
while ( al_get_count ( forbidden_function ) > forbid_count )
2005-09-20 21:26:39 +08:00
parser_allow_function ( ) ;
2006-01-31 00:51:50 +08:00
if ( block_type = = SUBST )
{
al_destroy ( forbidden_function ) ;
2006-01-31 03:53:10 +08:00
free ( forbidden_function ) ;
2006-01-31 00:51:50 +08:00
}
2006-01-31 03:53:10 +08:00
/*
2006-04-21 02:35:02 +08:00
Restore previous eval state
2006-01-31 00:51:50 +08:00
*/
2006-01-31 03:53:10 +08:00
forbidden_function = prev_forbidden ;
2005-09-20 21:26:39 +08:00
current_tokenizer = previous_tokenizer ;
2006-01-31 03:53:10 +08:00
block_io = prev_io ;
2006-01-31 00:51:50 +08:00
eval_level - - ;
2005-09-20 21:26:39 +08:00
code = error_code ;
error_code = 0 ;
2005-10-12 03:23:43 +08:00
job_reap ( 0 ) ;
2006-01-31 00:51:50 +08:00
2005-09-20 21:26:39 +08:00
return code ;
}
2006-06-16 20:56:16 +08:00
2006-06-09 07:55:57 +08:00
/**
\ return the block type created by the specified builtin , or - 1 on error .
*/
2006-05-26 19:25:25 +08:00
static int parser_get_block_type ( const wchar_t * cmd )
{
2006-06-16 20:56:16 +08:00
int i ;
for ( i = 0 ; block_lookup [ i ] . desc ; i + + )
{
if ( block_lookup [ i ] . name & & ( wcscmp ( block_lookup [ i ] . name , cmd ) = = 0 ) )
{
return block_lookup [ i ] . type ;
}
}
return - 1 ;
}
/**
2006-08-22 22:38:31 +08:00
\ return the block command that createa the specified block type , or null on error .
2006-06-16 20:56:16 +08:00
*/
static const wchar_t * parser_get_block_command ( int type )
{
int i ;
for ( i = 0 ; block_lookup [ i ] . desc ; i + + )
{
if ( block_lookup [ i ] . type = = type )
{
return block_lookup [ i ] . name ;
}
}
return 0 ;
2006-05-26 19:25:25 +08:00
}
2006-06-09 07:55:57 +08:00
/**
Test if this argument contains any errors . Detected errors include
2006-07-21 09:08:31 +08:00
syntax errors in command substitutions , improperly escaped
2006-06-09 07:55:57 +08:00
characters and improper use of the variable expansion operator .
*/
2006-06-02 10:15:17 +08:00
static int parser_test_argument ( const wchar_t * arg , string_buffer_t * out , const wchar_t * prefix , int offset )
2006-05-22 03:25:24 +08:00
{
wchar_t * unesc ;
wchar_t * pos ;
int err = 0 ;
2006-06-14 21:22:40 +08:00
wchar_t * paran_begin , * paran_end ;
2006-06-09 07:52:12 +08:00
wchar_t * arg_cpy ;
2006-05-22 03:25:24 +08:00
int do_loop = 1 ;
2006-06-21 08:48:36 +08:00
CHECK ( arg , 1 ) ;
2006-06-09 07:52:12 +08:00
arg_cpy = wcsdup ( arg ) ;
2006-05-22 03:25:24 +08:00
while ( do_loop )
{
switch ( parse_util_locate_cmdsubst ( arg_cpy ,
& paran_begin ,
& paran_end ,
0 ) )
{
case - 1 :
err = 1 ;
2006-06-02 10:15:17 +08:00
if ( out )
2006-05-22 03:25:24 +08:00
{
error ( SYNTAX_ERROR ,
2006-05-31 23:41:47 +08:00
offset ,
2006-05-22 03:25:24 +08:00
L " Mismatched parans " ) ;
2006-06-02 10:15:17 +08:00
print_errors ( out , prefix ) ;
2006-05-22 03:25:24 +08:00
}
free ( arg_cpy ) ;
return 1 ;
2006-08-22 22:38:31 +08:00
2006-05-22 03:25:24 +08:00
case 0 :
do_loop = 0 ;
break ;
case 1 :
{
wchar_t * subst = wcsndup ( paran_begin + 1 , paran_end - paran_begin - 1 ) ;
string_buffer_t tmp ;
sb_init ( & tmp ) ;
sb_append_substring ( & tmp , arg_cpy , paran_begin - arg_cpy ) ;
sb_append_char ( & tmp , INTERNAL_SEPARATOR ) ;
sb_append ( & tmp , paran_end + 1 ) ;
// debug( 1, L"%ls -> %ls %ls", arg_cpy, subst, tmp.buff );
2006-06-02 10:15:17 +08:00
err | = parser_test ( subst , out , prefix ) ;
2006-05-22 03:25:24 +08:00
free ( subst ) ;
free ( arg_cpy ) ;
arg_cpy = ( wchar_t * ) tmp . buff ;
/*
Do _not_ call sb_destroy on this stringbuffer - it ' s
2006-05-31 23:41:47 +08:00
buffer is used as the new ' arg_cpy ' . It is free ' d at
the end of the loop .
2006-05-22 03:25:24 +08:00
*/
break ;
2006-05-31 23:41:47 +08:00
}
2006-05-22 03:25:24 +08:00
}
2006-05-31 23:41:47 +08:00
}
2006-05-22 03:25:24 +08:00
unesc = unescape ( arg_cpy , 1 ) ;
2006-06-15 09:11:54 +08:00
if ( ! unesc )
2006-05-22 03:25:24 +08:00
{
2006-06-15 09:11:54 +08:00
if ( out )
{
error ( SYNTAX_ERROR ,
offset ,
L " Invalid token '%ls' " , arg_cpy ) ;
print_errors ( out , prefix ) ;
}
return 1 ;
}
else
{
/*
Check for invalid variable expansions
*/
for ( pos = unesc ; * pos ; pos + + )
2006-05-22 03:25:24 +08:00
{
2006-06-15 09:11:54 +08:00
switch ( * pos )
2006-05-22 03:25:24 +08:00
{
2006-06-15 09:11:54 +08:00
case VARIABLE_EXPAND :
case VARIABLE_EXPAND_SINGLE :
2006-05-22 03:25:24 +08:00
{
2006-07-20 21:02:46 +08:00
wchar_t n = * ( pos + 1 ) ;
if ( n ! = VARIABLE_EXPAND & &
n ! = VARIABLE_EXPAND_SINGLE & &
! wcsvarchr ( n ) )
2006-05-22 03:25:24 +08:00
{
2006-07-20 21:02:46 +08:00
err = 1 ;
if ( out )
2006-06-15 09:11:54 +08:00
{
2006-07-20 21:02:46 +08:00
expand_variable_error ( unesc , pos - unesc , offset ) ;
print_errors ( out , prefix ) ;
2006-06-15 09:11:54 +08:00
}
}
2006-05-22 03:25:24 +08:00
2006-06-15 09:11:54 +08:00
break ;
}
}
2006-05-22 03:25:24 +08:00
}
}
2006-06-15 09:11:54 +08:00
free ( arg_cpy ) ;
2006-05-22 03:25:24 +08:00
free ( unesc ) ;
return err ;
}
int parser_test_args ( const wchar_t * buff ,
2006-06-02 10:15:17 +08:00
string_buffer_t * out , const wchar_t * prefix )
2006-05-22 03:25:24 +08:00
{
tokenizer tok ;
tokenizer * previous_tokenizer = current_tokenizer ;
int previous_pos = current_tokenizer_pos ;
int do_loop = 1 ;
int err = 0 ;
2006-06-21 08:48:36 +08:00
CHECK ( buff , 1 ) ;
2006-05-22 03:25:24 +08:00
current_tokenizer = & tok ;
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 ) )
{
2006-05-22 05:46:01 +08:00
2006-05-22 03:25:24 +08:00
case TOK_STRING :
{
2006-06-02 10:15:17 +08:00
err | = parser_test_argument ( tok_last ( & tok ) , out , prefix , tok_get_pos ( & tok ) ) ;
2006-05-22 03:25:24 +08:00
break ;
}
case TOK_END :
{
break ;
}
case TOK_ERROR :
{
2006-06-02 10:15:17 +08:00
if ( out )
2006-05-22 03:25:24 +08:00
{
error ( SYNTAX_ERROR ,
tok_get_pos ( & tok ) ,
TOK_ERR_MSG ,
tok_last ( & tok ) ) ;
2006-06-02 10:15:17 +08:00
print_errors ( out , prefix ) ;
2006-05-22 03:25:24 +08:00
}
err = 1 ;
do_loop = 0 ;
break ;
}
default :
{
2006-06-02 10:15:17 +08:00
if ( out )
2006-05-22 03:25:24 +08:00
{
error ( SYNTAX_ERROR ,
tok_get_pos ( & tok ) ,
UNEXPECTED_TOKEN_ERR_MSG ,
tok_get_desc ( tok_last_type ( & tok ) ) ) ;
2006-06-02 10:15:17 +08:00
print_errors ( out , prefix ) ;
2006-05-22 03:25:24 +08:00
}
err = 1 ;
do_loop = 0 ;
break ;
}
}
}
tok_destroy ( & tok ) ;
current_tokenizer = previous_tokenizer ;
current_tokenizer_pos = previous_pos ;
error_code = 0 ;
return err ;
}
int parser_test ( const wchar_t * buff ,
2006-06-02 10:15:17 +08:00
string_buffer_t * out ,
2006-06-05 08:42:01 +08:00
const wchar_t * prefix )
2005-09-20 21:26:39 +08:00
{
tokenizer tok ;
2006-05-22 03:25:24 +08:00
/*
Set to one if a command name has been given for the currently
parsed process specification
*/
int had_cmd = 0 ;
2005-09-20 21:26:39 +08:00
int count = 0 ;
int err = 0 ;
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-07-21 09:08:31 +08:00
/*
Set to 1 if the current command is inside a pipeline
*/
2005-09-20 21:26:39 +08:00
int is_pipeline = 0 ;
2006-07-21 09:08:31 +08:00
2006-05-22 03:25:24 +08:00
/*
Set to one if the currently specified process can not be used inside a pipeline
*/
2005-09-20 21:26:39 +08:00
int forbid_pipeline = 0 ;
2006-07-21 09:08:31 +08:00
2006-05-22 03:25:24 +08:00
/*
Set to one if an additional process specification is needed
*/
int needs_cmd = 0 ;
2006-07-21 09:08:31 +08:00
/*
halloc context used for calls to expand ( ) and other memory
allocations . Free ' d at end of this function .
*/
2006-06-09 07:52:12 +08:00
void * context ;
2006-07-21 09:08:31 +08: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-22 06:16:04 +08:00
int arg_count = 0 ;
2006-07-21 09:08:31 +08:00
/*
The currently validated command .
*/
2006-05-27 21:49:18 +08:00
wchar_t * cmd = 0 ;
2006-05-22 06:16:04 +08:00
2006-06-21 08:48:36 +08:00
CHECK ( buff , 1 ) ;
2006-06-09 07:52:12 +08:00
context = halloc ( 0 , 0 ) ;
2005-09-20 21:26:39 +08:00
current_tokenizer = & tok ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
for ( tok_init ( & tok , buff , 0 ) ;
2006-03-18 09:04:59 +08:00
tok_has_next ( & tok ) ;
2005-09-20 21:26:39 +08:00
tok_next ( & tok ) )
{
current_tokenizer_pos = tok_get_pos ( & tok ) ;
int last_type = tok_last_type ( & tok ) ;
switch ( last_type )
{
case TOK_STRING :
{
if ( ! had_cmd )
{
int mark = tok_get_pos ( & tok ) ;
had_cmd = 1 ;
2006-05-22 06:16:04 +08:00
arg_count = 0 ;
2006-07-20 21:02:46 +08:00
2006-05-22 06:16:04 +08:00
if ( ! ( cmd = expand_one ( context ,
wcsdup ( tok_last ( & tok ) ) ,
2006-08-22 22:38:31 +08:00
EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_VARIABLES ) ) )
2006-05-22 03:25:24 +08:00
{
err = 1 ;
2006-06-02 10:15:17 +08:00
if ( out )
2006-05-22 03:25:24 +08:00
{
error ( SYNTAX_ERROR ,
tok_get_pos ( & tok ) ,
ILLEGAL_CMD_ERR_MSG ,
2006-07-03 18:46:47 +08:00
tok_last ( & tok ) ) ;
2006-05-22 03:25:24 +08:00
2006-06-02 10:15:17 +08:00
print_errors ( out , prefix ) ;
2006-07-03 18:46:47 +08:00
}
break ;
2006-05-22 03:25:24 +08:00
}
if ( needs_cmd )
2005-09-20 21:26:39 +08:00
{
2006-05-26 19:25:25 +08:00
/*
end is not a valid command when a followup
command is needed , such as after ' and ' or
' while '
*/
2006-05-22 06:16:04 +08:00
if ( contains_str ( cmd ,
2005-09-20 21:26:39 +08:00
L " end " ,
0 ) )
{
err = 1 ;
2006-06-02 10:15:17 +08:00
if ( out )
2006-01-31 03:53:10 +08:00
{
2005-09-20 21:26:39 +08:00
error ( SYNTAX_ERROR ,
2005-12-08 00:06:47 +08:00
tok_get_pos ( & tok ) ,
COND_ERR_MSG ) ;
2006-05-22 03:25:24 +08:00
2006-06-02 10:15:17 +08:00
print_errors ( out , prefix ) ;
2005-09-20 21:26:39 +08:00
}
}
2006-05-22 03:25:24 +08:00
needs_cmd = 0 ;
2005-09-20 21:26:39 +08:00
}
2006-05-22 03:25:24 +08:00
2005-09-25 19:25:42 +08:00
/*
Decrement block count on end command
*/
2006-05-22 06:16:04 +08:00
if ( wcscmp ( cmd , L " end " ) = = 0 )
2005-09-20 21:26:39 +08:00
{
tok_next ( & tok ) ;
count - - ;
tok_set_pos ( & tok , mark ) ;
}
2006-01-31 03:53:10 +08:00
2005-09-25 19:25:42 +08:00
/*
Handle block commands
*/
2006-05-22 06:16:04 +08:00
if ( parser_is_block ( cmd ) )
2005-09-20 21:26:39 +08:00
{
if ( count > = BLOCK_MAX_COUNT )
{
2006-01-31 03:53:10 +08:00
error ( SYNTAX_ERROR ,
tok_get_pos ( & tok ) ,
2005-12-08 00:06:47 +08:00
BLOCK_ERR_MSG ) ;
2006-01-31 03:53:10 +08:00
2006-06-02 10:15:17 +08:00
print_errors ( out , prefix ) ;
2005-09-20 21:26:39 +08:00
}
else
{
2006-05-26 19:25:25 +08:00
block_type [ count ] = parser_get_block_type ( cmd ) ;
2005-09-20 21:26:39 +08:00
block_pos [ count ] = current_tokenizer_pos ;
tok_next ( & tok ) ;
count + + ;
tok_set_pos ( & tok , mark ) ;
}
}
2006-01-31 03:53:10 +08:00
2005-09-25 19:25:42 +08:00
/*
If parser_is_subcommand is true , the command
accepts a second command as it ' s first
argument . If parser_skip_arguments is true , the
second argument is optional .
*/
2006-05-26 19:25:25 +08:00
if ( parser_is_subcommand ( cmd ) & & ! parser_skip_arguments ( cmd ) )
2005-09-20 21:26:39 +08:00
{
needs_cmd = 1 ;
had_cmd = 0 ;
}
2006-05-22 03:25:24 +08:00
2006-05-26 19:25:25 +08:00
if ( contains_str ( cmd ,
2005-09-20 21:26:39 +08:00
L " or " ,
L " and " ,
0 ) )
{
2006-05-26 19:25:25 +08:00
/*
' or ' and ' and ' can not be used inside pipelines
*/
2005-09-20 21:26:39 +08:00
if ( is_pipeline )
{
err = 1 ;
2006-06-02 10:15:17 +08:00
if ( out )
2006-01-31 03:53:10 +08:00
{
2005-09-20 21:26:39 +08:00
error ( SYNTAX_ERROR ,
2005-12-08 00:06:47 +08:00
tok_get_pos ( & tok ) ,
EXEC_ERR_MSG ) ;
2006-01-31 03:53:10 +08:00
2006-06-02 10:15:17 +08:00
print_errors ( out , prefix ) ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
}
}
}
2006-05-22 03:25:24 +08:00
2005-09-25 19:25:42 +08:00
/*
There are a lot of situations where pipelines
2006-05-22 03:25:24 +08:00
are forbidden , including when using the exec
2005-09-25 19:25:42 +08:00
builtin .
*/
2006-05-26 19:25:25 +08:00
if ( parser_is_pipe_forbidden ( cmd ) )
2005-09-20 21:26:39 +08:00
{
if ( is_pipeline )
{
err = 1 ;
2006-06-02 10:15:17 +08:00
if ( out )
2006-01-31 03:53:10 +08:00
{
2005-09-20 21:26:39 +08:00
error ( SYNTAX_ERROR ,
2005-12-08 00:06:47 +08:00
tok_get_pos ( & tok ) ,
EXEC_ERR_MSG ) ;
2006-01-31 03:53:10 +08:00
2006-06-02 10:15:17 +08:00
print_errors ( out , prefix ) ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
}
}
2006-01-31 03:53:10 +08:00
forbid_pipeline = 1 ;
2005-09-20 21:26:39 +08:00
}
2006-01-31 03:53:10 +08:00
2005-09-25 19:25:42 +08:00
/*
2006-05-26 19:25:25 +08:00
Test that the case builtin is only used directly in a switch block
2005-09-25 19:25:42 +08:00
*/
2006-05-26 19:25:25 +08:00
if ( wcscmp ( L " case " , cmd ) = = 0 )
2005-09-20 21:26:39 +08:00
{
if ( ! count | | block_type [ count - 1 ] ! = SWITCH )
{
err = 1 ;
2006-01-31 03:53:10 +08:00
2006-06-02 10:15:17 +08:00
if ( out )
2005-12-14 09:07:12 +08:00
{
2006-07-20 07:22:26 +08:00
char * h ;
2005-12-14 09:07:12 +08:00
error ( SYNTAX_ERROR ,
tok_get_pos ( & tok ) ,
2005-12-14 02:28:03 +08:00
INVALID_CASE_ERR_MSG ) ;
2006-01-31 03:53:10 +08:00
2006-06-02 10:15:17 +08:00
print_errors ( out , prefix ) ;
2006-07-20 07:22:26 +08:00
h = builtin_help_get ( L " case " ) ;
if ( h )
sb_printf ( out , L " %s " , h ) ;
2005-12-14 09:07:12 +08:00
}
}
2006-01-31 03:53:10 +08:00
}
2005-09-25 19:25:42 +08:00
/*
2006-05-26 19:25:25 +08:00
Test that break and continue are only used within loop blocks
2005-09-25 19:25:42 +08:00
*/
2006-05-26 19:25:25 +08:00
if ( contains_str ( cmd , L " break " , L " continue " , ( void * ) 0 ) )
2005-09-20 21:26:39 +08: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-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
if ( ! found_loop )
{
err = 1 ;
2006-01-31 03:53:10 +08:00
2006-06-02 10:15:17 +08:00
if ( out )
2005-12-14 09:07:12 +08:00
{
error ( SYNTAX_ERROR ,
tok_get_pos ( & tok ) ,
INVALID_LOOP_ERR_MSG ) ;
2006-06-02 10:15:17 +08:00
print_errors ( out , prefix ) ;
2005-12-14 09:07:12 +08:00
}
2006-01-31 03:53:10 +08:00
}
2005-09-20 21:26:39 +08:00
}
2006-01-31 03:53:10 +08:00
2005-09-25 19:25:42 +08:00
/*
2006-05-26 19:25:25 +08:00
Test that else is only used directly in an if - block
2005-09-25 19:25:42 +08:00
*/
2006-05-26 19:25:25 +08:00
if ( wcscmp ( L " else " , cmd ) = = 0 )
2005-09-20 21:26:39 +08:00
{
if ( ! count | | block_type [ count - 1 ] ! = IF )
2005-12-14 09:07:12 +08:00
{
err = 1 ;
2006-06-02 10:15:17 +08:00
if ( out )
2005-12-14 09:07:12 +08:00
{
error ( SYNTAX_ERROR ,
tok_get_pos ( & tok ) ,
2005-12-14 02:28:03 +08:00
INVALID_ELSE_ERR_MSG ) ;
2006-01-31 03:53:10 +08:00
2006-06-02 10:15:17 +08:00
print_errors ( out , prefix ) ;
2005-12-14 09:07:12 +08:00
}
}
2005-09-20 21:26:39 +08:00
}
2006-01-31 03:53:10 +08:00
2005-09-25 19:25:42 +08:00
/*
2006-05-26 19:25:25 +08:00
Test that end is not used when not inside any block
2005-09-25 19:25:42 +08:00
*/
2005-09-20 21:26:39 +08:00
if ( count < 0 )
{
err = 1 ;
2006-06-02 10:15:17 +08:00
if ( out )
2005-09-20 21:26:39 +08:00
{
2006-06-16 20:56:16 +08:00
char * h ;
2005-09-20 21:26:39 +08:00
error ( SYNTAX_ERROR ,
2005-12-08 00:06:47 +08:00
tok_get_pos ( & tok ) ,
2005-12-14 04:11:21 +08:00
INVALID_END_ERR_MSG ) ;
2006-06-02 10:15:17 +08:00
print_errors ( out , prefix ) ;
2006-06-16 20:56:16 +08:00
h = builtin_help_get ( L " end " ) ;
if ( h )
sb_printf ( out , L " %s " , h ) ;
2006-01-31 03:53:10 +08:00
}
2005-09-20 21:26:39 +08:00
}
2006-07-03 18:46:47 +08:00
2005-09-20 21:26:39 +08:00
}
2006-05-22 03:25:24 +08:00
else
{
2006-06-02 10:15:17 +08:00
err | = parser_test_argument ( tok_last ( & tok ) , out , prefix , tok_get_pos ( & tok ) ) ;
2006-05-26 19:25:25 +08:00
/*
If possible , keep track of number of supplied arguments
*/
2006-05-22 06:16:04 +08:00
if ( arg_count > = 0 & & expand_is_clean ( tok_last ( & tok ) ) )
{
arg_count + + ;
}
else
{
arg_count = - 1 ;
}
/*
Try to make sure the second argument to ' for ' is ' in '
*/
if ( wcscmp ( cmd , L " for " ) = = 0 )
{
if ( arg_count = = 2 )
{
if ( wcscmp ( tok_last ( & tok ) , L " in " ) ! = 0 )
{
err = 1 ;
2006-06-02 10:15:17 +08:00
if ( out )
2006-05-22 06:16:04 +08:00
{
error ( SYNTAX_ERROR ,
tok_get_pos ( & tok ) ,
BUILTIN_FOR_ERR_IN ,
L " for " ) ;
2006-06-02 10:15:17 +08:00
print_errors ( out , prefix ) ;
2006-05-22 06:16:04 +08:00
}
}
}
}
2006-05-22 03:25:24 +08:00
}
2005-09-20 21:26:39 +08:00
break ;
}
case TOK_REDIRECT_OUT :
case TOK_REDIRECT_IN :
case TOK_REDIRECT_APPEND :
case TOK_REDIRECT_FD :
{
if ( ! had_cmd )
{
err = 1 ;
2006-06-02 10:15:17 +08:00
if ( out )
2005-09-20 21:26:39 +08:00
{
error ( SYNTAX_ERROR ,
2005-12-08 00:06:47 +08:00
tok_get_pos ( & tok ) ,
2005-12-14 11:39:39 +08:00
INVALID_REDIRECTION_ERR_MSG ) ;
2006-06-02 10:15:17 +08:00
print_errors ( out , prefix ) ;
2005-09-20 21:26:39 +08:00
}
}
break ;
}
case TOK_END :
{
if ( needs_cmd & & ! had_cmd )
{
2006-01-31 03:53:10 +08:00
err = 1 ;
2006-06-02 10:15:17 +08:00
if ( out )
2005-12-14 09:07:12 +08:00
{
error ( SYNTAX_ERROR ,
tok_get_pos ( & tok ) ,
2005-12-14 11:39:39 +08:00
CMD_ERR_MSG ,
tok_get_desc ( tok_last_type ( & tok ) ) ) ;
2006-06-02 10:15:17 +08:00
print_errors ( out , prefix ) ;
2005-12-14 09:07:12 +08:00
}
2006-01-31 03:53:10 +08:00
}
needs_cmd = 0 ;
2005-09-20 21:26:39 +08:00
had_cmd = 0 ;
2006-01-31 03:53:10 +08:00
is_pipeline = 0 ;
2005-09-20 21:26:39 +08:00
forbid_pipeline = 0 ;
break ;
}
case TOK_PIPE :
{
2006-05-22 03:25:24 +08:00
if ( ! had_cmd )
{
err = 1 ;
2006-06-02 10:15:17 +08:00
if ( out )
2006-05-22 03:25:24 +08: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 ) ) ) ;
}
else
{
error ( SYNTAX_ERROR ,
tok_get_pos ( & tok ) ,
CMD_ERR_MSG ,
tok_get_desc ( tok_last_type ( & tok ) ) ) ;
}
2006-06-02 10:15:17 +08:00
print_errors ( out , prefix ) ;
2006-05-22 03:25:24 +08:00
}
}
else if ( forbid_pipeline )
2005-09-20 21:26:39 +08:00
{
err = 1 ;
2006-06-02 10:15:17 +08:00
if ( out )
2006-01-31 03:53:10 +08:00
{
2005-09-20 21:26:39 +08:00
error ( SYNTAX_ERROR ,
2005-12-08 00:06:47 +08:00
tok_get_pos ( & tok ) ,
EXEC_ERR_MSG ) ;
2006-05-22 03:25:24 +08:00
2006-06-02 10:15:17 +08:00
print_errors ( out , prefix ) ;
2005-09-20 21:26:39 +08:00
}
}
2006-05-22 03:25:24 +08:00
else
{
needs_cmd = 1 ;
is_pipeline = 1 ;
had_cmd = 0 ;
}
break ;
2005-09-20 21:26:39 +08:00
}
2006-05-22 03:25:24 +08:00
2005-09-20 21:26:39 +08:00
case TOK_BACKGROUND :
{
2006-05-22 03:25:24 +08:00
if ( ! had_cmd )
2005-09-20 21:26:39 +08:00
{
2006-01-31 03:53:10 +08:00
err = 1 ;
2006-06-02 10:15:17 +08:00
if ( out )
2005-12-14 09:07:12 +08:00
{
2006-05-22 03:25:24 +08: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 ) ) ) ;
}
else
{
error ( SYNTAX_ERROR ,
tok_get_pos ( & tok ) ,
CMD_ERR_MSG ,
tok_get_desc ( tok_last_type ( & tok ) ) ) ;
}
2006-06-02 10:15:17 +08:00
print_errors ( out , prefix ) ;
2005-12-14 09:07:12 +08:00
}
2006-01-31 03:53:10 +08:00
}
2006-05-22 03:25:24 +08:00
2005-09-20 21:26:39 +08:00
if ( had_cmd )
{
had_cmd = 0 ;
}
break ;
}
case TOK_ERROR :
default :
err = 1 ;
2006-06-02 10:15:17 +08:00
if ( out )
2005-09-20 21:26:39 +08:00
{
2005-12-08 00:06:47 +08:00
error ( SYNTAX_ERROR ,
tok_get_pos ( & tok ) ,
TOK_ERR_MSG ,
tok_last ( & tok ) ) ;
2006-01-31 03:53:10 +08:00
2006-06-02 10:15:17 +08:00
print_errors ( out , prefix ) ;
2005-09-20 21:26:39 +08:00
//debug( 2, tok_last( &tok) );
}
break ;
}
}
2006-01-31 03:53:10 +08:00
2006-05-22 03:25:24 +08:00
if ( needs_cmd )
2005-09-20 21:26:39 +08:00
{
err = 1 ;
2006-06-02 10:15:17 +08:00
if ( out )
2006-01-31 03:53:10 +08:00
{
2005-09-20 21:26:39 +08:00
error ( SYNTAX_ERROR ,
2006-01-31 03:53:10 +08:00
tok_get_pos ( & tok ) ,
2005-12-08 00:06:47 +08:00
COND_ERR_MSG ) ;
2006-01-31 03:53:10 +08:00
2006-06-02 10:15:17 +08:00
print_errors ( out , prefix ) ;
2005-09-20 21:26:39 +08:00
}
}
2006-01-31 03:53:10 +08:00
2006-06-02 10:15:17 +08:00
if ( out & & count > 0 )
2005-09-20 21:26:39 +08:00
{
2006-06-16 20:56:16 +08:00
const char * h ;
const wchar_t * cmd ;
2005-09-20 21:26:39 +08:00
error ( SYNTAX_ERROR ,
2005-12-08 00:06:47 +08:00
block_pos [ count - 1 ] ,
2005-12-14 11:39:39 +08:00
BLOCK_END_ERR_MSG ) ;
2006-06-16 20:56:16 +08:00
2006-06-02 10:15:17 +08:00
print_errors ( out , prefix ) ;
2006-06-16 20:56:16 +08:00
cmd = parser_get_block_command ( block_type [ count - 1 ] ) ;
if ( cmd )
{
h = builtin_help_get ( cmd ) ;
if ( cmd )
{
sb_printf ( out , L " %s " , h ) ;
}
}
2005-09-20 21:26:39 +08:00
}
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
tok_destroy ( & tok ) ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
current_tokenizer = previous_tokenizer ;
current_tokenizer_pos = previous_pos ;
error_code = 0 ;
2006-05-22 03:25:24 +08:00
halloc_free ( context ) ;
2006-05-22 06:16:04 +08:00
2005-09-20 21:26:39 +08:00
return err | ( ( count ! = 0 ) < < 1 ) ;
}