aboutsummaryrefslogtreecommitdiffhomepage
path: root/parser.c
diff options
context:
space:
mode:
Diffstat (limited to 'parser.c')
-rw-r--r--parser.c2230
1 files changed, 2230 insertions, 0 deletions
diff --git a/parser.c b/parser.c
new file mode 100644
index 00000000..4925f168
--- /dev/null
+++ b/parser.c
@@ -0,0 +1,2230 @@
+/** \file parser.c
+
+The fish parser. Contains functions for parsing code.
+
+*/
+
+#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>
+
+#include "config.h"
+#include "util.h"
+#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 "builtin_help.h"
+#include "env.h"
+#include "expand.h"
+#include "reader.h"
+#include "sanity.h"
+#include "env_universal.h"
+
+/** Length of the lineinfo string used for describing the current tokenizer position */
+#define LINEINFO_SIZE 128
+
+/**
+ 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
+
+/**
+ Error message for improper use of the exec builtin
+*/
+#define EXEC_ERR_MSG L"this command can not be used in a pipeline"
+
+/**
+ Error message for tokenizer error. The tokenizer message is
+ appended to this message.
+*/
+#define TOK_ERR_MSG L"Tokenizer error"
+
+/**
+ Error message for short circut command error.
+*/
+#define COND_ERR_MSG L"Short circut command requires additional command"
+
+/**
+ Error message on reaching maximum recusrion depth
+*/
+#define RECURSION_ERR_MSG L"Maximum recursion depth reached. Accidental infinite loop?"
+
+/**
+ Error message on reaching maximum number of block calls
+*/
+#define BLOCK_ERR_MSG L"Maximum number of nested blocks reached."
+
+/**
+ Error message on missing 'end'
+*/
+#define END_ERR_MSG L"Block missing 'end'"
+
+/**
+ Error message on pipe/bg character without command
+*/
+#define CMD_ERR_MSG L"Expected command"
+
+/** Last error code */
+int error_code;
+
+/** Position of last error */
+
+static int err_pos;
+
+/** Description of last error */
+static wchar_t err_str[256];
+
+/** Pointer to the current tokenizer */
+static tokenizer *current_tokenizer;
+
+/** String for representing the current line */
+static wchar_t lineinfo[LINEINFO_SIZE];
+
+/** 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 */
+static array_list_t forbidden_function;
+
+/**
+ String index where the current job started.
+*/
+static int job_start_pos;
+
+io_data_t *block_io;
+
+/**
+ List of all profiling data
+*/
+static array_list_t profile_data;
+
+static int eval_level=-1;
+
+static int parse_job( process_t *p,
+ job_t *j,
+ tokenizer *tok );
+
+typedef struct
+{
+ int exec, parse, level, skipped;
+ wchar_t *cmd;
+} profile_element_t;
+
+
+int block_count( block_t *b )
+{
+ if( b==0)
+ return 0;
+ return( block_count(b->outer)+1);
+}
+
+
+void parser_push_block( int type )
+{
+ block_t *new = malloc( sizeof( block_t ));
+
+// debug( 2, L"Block push %ls %d\n", bl[type], block_count( current_block)+1 );
+ new->outer = current_block;
+ new->type = (current_block && current_block->skip)?FAKE:type;
+
+ new->skip=current_block?current_block->skip:0;
+
+ new->loop_status=LOOP_NORMAL;
+
+ current_block = new;
+
+ if( (new->type != FUNCTION_DEF) &&
+ (new->type != FAKE) &&
+ (new->type != OR) &&
+ (new->type != AND) &&
+ (new->type != TOP) )
+ {
+ env_push( type == FUNCTION_CALL );
+ }
+}
+
+void parser_pop_block()
+{
+// debug( 2, L"Block pop %ls %d\n", bl[current_block->type], block_count(current_block)-1 );
+
+ if( (current_block->type != FUNCTION_DEF ) &&
+ (current_block->type != FAKE) &&
+ (current_block->type != OR) &&
+ (current_block->type != AND) &&
+ (current_block->type != TOP) )
+ {
+ env_pop();
+ }
+
+ switch( current_block->type)
+ {
+ case FOR:
+ {
+ free( current_block->for_variable );
+ al_foreach( &current_block->for_vars,
+ (void (*)(const void *))&free );
+ al_destroy( &current_block->for_vars );
+ break;
+ }
+
+ case SWITCH:
+ {
+ free( current_block->switch_value );
+ break;
+ }
+
+ case FUNCTION_DEF:
+ {
+ free( current_block->function_name );
+ free( current_block->function_description );
+ break;
+ }
+
+ }
+
+ block_t *old = current_block;
+ current_block = current_block->outer;
+ free( old );
+}
+
+wchar_t *parser_get_block_desc( int block )
+{
+ switch( block )
+ {
+ case WHILE:
+ return L"while block";
+
+ case FOR:
+ return L"for block";
+
+ case IF:
+ return L"'if' conditional block";
+
+ case FUNCTION_DEF:
+ return L"function definition block";
+
+ case FUNCTION_CALL:
+ return L"fuction invocation block";
+
+ case SWITCH:
+ return L"switch block";
+
+ case FAKE:
+ return L"unexecutable block";
+
+ case TOP:
+ return L"global root block";
+
+ case SUBST:
+ return L"command substitution block";
+
+ case BEGIN:
+ return L"unconditional block";
+
+ case AND:
+ return L"'and' conditional command";
+
+ case OR:
+ return L"'or' conditional command";
+
+ default:
+ return L"unknown/invalid block";
+ }
+
+}
+
+
+int parser_is_subcommand( const wchar_t *cmd )
+{
+ return contains_str( cmd,
+ L"command",
+ L"builtin",
+ L"while",
+ L"exec",
+ L"if",
+ L"and",
+ L"or",
+ L"not",
+ 0 );
+}
+
+/**
+ Test if the specified string is command that opens a new block
+*/
+
+static int parser_is_block( wchar_t *word)
+{
+ return contains_str( word,
+ L"for",
+ L"while",
+ L"if",
+ L"function",
+ L"switch",
+ L"begin",
+ 0 );
+}
+
+int parser_is_reserved( wchar_t *word)
+{
+ return parser_is_block(word) ||
+ parser_is_subcommand( word ) ||
+ contains_str( word,
+ L"end",
+ L"case",
+ L"else",
+ L"return",
+ L"continue",
+ L"break",
+ 0 );
+}
+
+int parser_is_pipe_forbidden( wchar_t *word )
+{
+ return contains_str( word,
+ L"exec",
+ L"case",
+ L"break",
+ L"return",
+ L"continue",
+ 0 );
+}
+
+static const wchar_t *parser_find_end( const wchar_t * buff )
+{
+ tokenizer tok;
+ int had_cmd=0;
+ int count = 0;
+ int error=0;
+ int mark=0;
+
+ 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;
+ }
+
+ case TOK_END:
+ {
+ had_cmd = 0;
+ break;
+ }
+
+ case TOK_PIPE:
+ case TOK_BACKGROUND:
+ {
+ if( had_cmd )
+ {
+ had_cmd = 0;
+ }
+ else
+ {
+ error = 1;
+ }
+ break;
+
+ }
+
+ case TOK_ERROR:
+ error = 1;
+ break;
+
+ default:
+ break;
+
+ }
+ if(!count)
+ {
+ tok_next( &tok );
+ mark = tok_get_pos( &tok );
+ break;
+ }
+
+ }
+
+ tok_destroy( &tok );
+ if(!count && !error){
+
+ return buff+mark;
+ }
+ return 0;
+
+}
+
+wchar_t *parser_cdpath_get( wchar_t *dir )
+{
+ wchar_t *res = 0;
+
+ if( !dir )
+ return 0;
+
+
+ if( dir[0] == L'/' )
+ {
+ struct stat buf;
+ if( wstat( dir, &buf ) == 0 )
+ {
+ if( S_ISDIR(buf.st_mode) )
+ {
+ res = wcsdup( dir );
+ }
+ }
+ }
+ else
+ {
+ wchar_t *path = env_get(L"CDPATH");
+
+ if( path == 0 )
+ {
+ path = L".";
+ }
+
+ wchar_t *path_cpy = wcsdup( path );
+ wchar_t *nxt_path = path;
+ wchar_t *state;
+ wchar_t *whole_path;
+
+ if( (path_cpy==0) )
+ {
+ die_mem();
+ }
+
+ 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;
+ }
+
+ whole_path =
+ 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;
+ 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 );
+*/
+ al_push( &forbidden_function, function?wcsdup(function):0 );
+}
+
+void parser_allow_function()
+{
+/*
+ if( al_peek( &forbidden_function) )
+ debug( 2, L"Allow %ls\n", al_peek( &forbidden_function) );
+*/
+ free( (void *) al_pop( &forbidden_function ) );
+}
+
+void error( int ec, const wchar_t *str, int p )
+{
+ error_code = ec;
+ wcsncpy( err_str, str, 256 );
+ err_pos = p;
+}
+
+/**
+ Wrapper for the error function, which sets the error string to "ec 'ea'".
+*/
+static void error_arg( int ec, const wchar_t *es, const wchar_t *ea, int p )
+{
+ wchar_t *msg = wcsdupcat2( es, L" \'", ea, L"\'", 0 );
+ error( ec, msg, p );
+ free(msg);
+}
+
+wchar_t *get_filename( const wchar_t *cmd )
+{
+ wchar_t *path;
+
+ if(wcschr( cmd, '/' ) != 0 )
+ {
+ if( waccess( cmd, X_OK )==0 )
+ {
+ struct stat buff;
+ wstat( cmd, &buff );
+ if( S_ISREG(buff.st_mode) )
+ return wcsdup( cmd );
+ else
+ return 0;
+ }
+ }
+ else
+ {
+ path = env_get(L"PATH");
+ if( path != 0 )
+ {
+ /*
+ Allocate string long enough to hold the whole command
+ */
+ wchar_t *new_cmd = malloc( sizeof(wchar_t)*(wcslen(cmd)+wcslen(path)+2) );
+ /*
+ 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) )
+ {
+ die_mem();
+
+ }
+
+ 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] != '/' )
+ {
+ new_cmd[path_len++]='/';
+ }
+ wcscpy( &new_cmd[path_len], cmd );
+ if( waccess( new_cmd, X_OK )==0 )
+ {
+ struct stat buff;
+ if( wstat( new_cmd, &buff )==-1 )
+ {
+ if( errno != EACCES )
+ wperror( L"stat" );
+ continue;
+ }
+ if( S_ISREG(buff.st_mode) )
+ {
+ free( path_cpy );
+ return new_cmd;
+ }
+ }
+ else
+ {
+ switch( errno )
+ {
+ case ENOENT:
+ case ENAMETOOLONG:
+ case EACCES:
+ case ENOTDIR:
+ break;
+ default:
+ debug( 1,
+ L"Error while searching for command %d",
+ new_cmd );
+ wperror( L"access" );
+ }
+ }
+ }
+ free( path_cpy );
+ free( new_cmd );
+ }
+ }
+
+ return 0;
+}
+
+void parser_init()
+{
+ if( profile )
+ {
+ al_init( &profile_data);
+ }
+ al_init( &forbidden_function );
+}
+
+void print_profile( array_list_t *p,
+ int pos,
+ FILE *out )
+{
+ profile_element_t *me, *prev;
+ int i;
+ int my_time;
+
+ if( pos >= al_get_count( p ) )
+ return;
+
+ me= (profile_element_t *)al_get( p, pos );
+ if( !me->skipped )
+ {
+ my_time=me->parse+me->exec;
+
+ for( i=pos+1; i<al_get_count(p); i++ )
+ {
+ prev = (profile_element_t *)al_get( p, i );
+ if( prev->skipped )
+ continue;
+
+ if( prev->level <= me->level )
+ break;
+ if( prev->level > me->level+1 )
+ continue;
+ my_time -= prev->parse;
+ my_time -= prev->exec;
+ }
+
+ if( me->cmd )
+ {
+ fwprintf( out, L"%d\t%d\t", my_time, me->parse+me->exec );
+ for( i=0; i<me->level; i++ )
+ {
+ fwprintf( out, L"-" );
+ }
+ fwprintf( out, L"> %ls\n", me->cmd );
+ }
+ }
+ print_profile( p, pos+1, out );
+ free( me->cmd );
+ free( me );
+}
+
+void parser_destroy()
+{
+ if( profile )
+ {
+ /*
+ Save profiling information
+ */
+ FILE *f = fopen( profile, "w" );
+ if( !f )
+ {
+ debug( 1,
+ L"Could not write profiling information to file '%s'",
+ profile );
+ }
+ else
+ {
+ fwprintf( f,
+ L"Time\tSum\tCommand\n",
+ al_get_count( &profile_data ) );
+ print_profile( &profile_data, 0, f );
+ fclose( f );
+ }
+ al_destroy( &profile_data );
+ }
+
+ al_destroy( &forbidden_function );
+}
+
+static void print_errors()
+{
+ if( error_code )
+ {
+ int tmp;
+
+
+ debug( 0, L"%ls", err_str );
+
+ tmp = current_tokenizer_pos;
+ current_tokenizer_pos = err_pos;
+
+ fwprintf( stderr, L"%ls", parser_current_line() );
+
+ current_tokenizer_pos=tmp;
+ }
+}
+
+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
+ */
+ tokenizer *previous_tokenizer=current_tokenizer;
+ int previous_pos=current_tokenizer_pos;
+ int do_loop=1;
+ tok_init( &tok, line, 0 );
+
+ current_tokenizer=&tok;
+
+ 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:
+ if( !expand_string( wcsdup(tok_last( &tok )), args, 0 ) )
+ {
+ err_pos=tok_get_pos( &tok );
+ do_loop=0;
+ }
+
+ break;
+ case TOK_END:
+ break;
+
+ case TOK_ERROR:
+ {
+ error_arg( SYNTAX_ERROR,
+ TOK_ERR_MSG,
+ tok_last(&tok),
+ tok_get_pos( &tok ) );
+
+ do_loop=0;
+ break;
+ }
+
+ default:
+ error_arg( SYNTAX_ERROR,
+ L"Unexpected token of type",
+ tok_get_desc( tok_last_type(&tok)),
+ tok_get_pos( &tok ) );
+ do_loop=0;
+ break;
+
+ }
+ }
+
+ print_errors();
+ tok_destroy( &tok );
+
+ current_tokenizer=previous_tokenizer;
+ current_tokenizer_pos = previous_pos;
+
+ return 1;
+}
+
+wchar_t *parser_current_line()
+{
+ int lineno=1;
+
+ wchar_t *file = reader_current_filename();
+ wchar_t *whole_str = tok_string( current_tokenizer );
+ wchar_t *line = whole_str;
+ wchar_t *line_end;
+ int i;
+ int offset;
+ int current_line_pos=current_tokenizer_pos;
+
+ if( !line )
+ return L"";
+
+ lineinfo[0]=0;
+
+ /*
+ Calculate line number, line offset, etc.
+ */
+ for( i=0; i<current_tokenizer_pos; i++ )
+ {
+ if( whole_str[i] == L'\n' )
+ {
+ lineno++;
+ current_line_pos = current_tokenizer_pos-i-1;
+ line = &whole_str[i+1];
+ }
+ }
+
+ /*
+ 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 );
+
+// debug( 2, L"Current pos %d, line pos %d, file_length %d\n", current_tokenizer_pos, current_line_pos, wcslen(whole_str));
+
+ if( !is_interactive )
+ {
+ swprintf( lineinfo,
+ LINEINFO_SIZE,
+ L"%ls (line %d): %n",
+ file,
+ lineno,
+ &offset );
+ }
+ else
+ {
+ offset=0;
+ }
+
+ /* Skip printing if we are in interactive mode and the error was on the first character of the line */
+ if( offset+current_line_pos )
+ swprintf( lineinfo+offset,
+ LINEINFO_SIZE-offset,
+ L"%ls\n%*c^\n",
+ line,
+ offset+current_line_pos,
+ L' ' );
+
+ free( line );
+
+ return lineinfo;
+}
+
+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);
+
+ return ( wcscmp( L"-h", s ) == 0 ) ||
+ ( len >= 3 && (wcsncmp( L"--help", s, len ) == 0) );
+}
+
+/**
+ 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;
+
+ /*
+ Test if this is the 'count' command. We need to special case
+ count, since it should display a help message on 'count .h',
+ but not on 'set foo -h; count $foo'.
+ */
+ 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;
+ }
+
+ while( 1 )
+ {
+
+ /* debug( 2, L"Read token %ls\n", wcsdup(tok_last( tok )) ); */
+
+ switch( tok_last_type( tok ) )
+ {
+ case TOK_PIPE:
+ if( (p->type == INTERNAL_EXEC) )
+ {
+ error( SYNTAX_ERROR,
+ EXEC_ERR_MSG,
+ tok_get_pos( tok ) );
+ return;
+ }
+
+ p->argv = list_to_char_arr( args );
+ p->next = calloc( 1, sizeof( process_t ) );
+ if( p->next == 0 )
+ {
+ die_mem();
+
+ }
+ tok_next( tok );
+ if( !parse_job( p->next, j, tok ))
+ {
+ /*
+ Do not free args content on error - it is
+ already in p->argv and will be freed by job_free
+ later on.
+ */
+ al_truncate( args, 0 );
+ }
+ is_finished = 1;
+ break;
+
+ case TOK_BACKGROUND:
+ j->fg = 0;
+ case TOK_END:
+ {
+ p->argv = list_to_char_arr( args );
+ if( tok_has_next(tok))
+ tok_next(tok);
+
+ is_finished = 1;
+
+ break;
+ }
+
+ case TOK_STRING:
+ {
+ int skip=0;
+
+ if( current_block->skip )
+ {
+ skip=1;
+ if( (current_block->type == SWITCH) &&
+ (wcscmp( al_get( args, 0), L"case" )==0) &&
+ p->type )
+ {
+ skip=0;
+ }
+
+ }
+
+ 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;
+ wcscpy( p->actual_cmd, L"count" );
+ }
+
+ if( !expand_string( wcsdup(tok_last( tok )),
+ args,
+ 0 )
+ )
+ {
+ err_pos=tok_get_pos( tok );
+ if( error_code == 0 )
+ {
+ error_arg( SYNTAX_ERROR,
+ L"Could not expand string",
+ tok_last(tok),
+ tok_get_pos( tok ) );
+ }
+
+ }
+ }
+
+ 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;
+
+
+
+ /*
+ Don't check redirections in skipped part
+
+ Otherwise, bogus errors may be the result
+ */
+ if( current_block->skip )
+ {
+ tok_next( tok );
+ break;
+ }
+
+ new_io = calloc( 1, sizeof(io_data_t) );
+ if( !new_io )
+ die_mem();
+
+ new_io->fd = wcstol( tok_last( tok ),
+ 0,
+ 10 );
+ tok_next( tok );
+
+ switch( tok_last_type( tok ) )
+ {
+ case TOK_STRING:
+ {
+ target = expand_one( wcsdup( tok_last( tok ) ), 0);
+ if( target == 0 && error_code == 0 )
+ {
+ error_arg( SYNTAX_ERROR,
+ L"Could not expand string",
+ tok_last( tok ),
+ tok_get_pos( tok ) );
+
+ }
+ }
+ break;
+ default:
+ error_arg( SYNTAX_ERROR,
+ L"Expected redirection, got token of type",
+ tok_get_desc( tok_last_type(tok)),
+ tok_get_pos( tok ) );
+ }
+
+ if( target == 0 || wcslen( target )==0 )
+ {
+ if( error_code == 0 )
+ error( SYNTAX_ERROR,
+ L"Invalid IO redirection",
+ tok_get_pos( tok ) );
+ tok_next(tok);
+ }
+ else
+ {
+
+
+ switch( type )
+ {
+ case TOK_REDIRECT_APPEND:
+ new_io->io_mode = IO_FILE;
+ new_io->flags = O_CREAT | O_APPEND | O_WRONLY;
+ new_io->filename = target;
+ break;
+
+ case TOK_REDIRECT_OUT:
+ new_io->io_mode = IO_FILE;
+ new_io->flags = O_CREAT | O_WRONLY | O_TRUNC;
+ new_io->filename = target;
+ break;
+
+ case TOK_REDIRECT_IN:
+ new_io->io_mode = IO_FILE;
+ new_io->flags = O_RDONLY;
+ new_io->filename = target;
+ break;
+
+ case TOK_REDIRECT_FD:
+ if( wcscmp( target, L"-" ) == 0 )
+ {
+ new_io->io_mode = IO_CLOSE;
+ }
+ else
+ {
+ new_io->io_mode = IO_FD;
+ new_io->old_fd = wcstol( target,
+ 0,
+ 10 );
+ if( ( new_io->old_fd < 0 ) ||
+ ( new_io->old_fd > 10 ) )
+ {
+ error_arg( SYNTAX_ERROR,
+ L"Requested redirection to something "
+ L"that is not a file descriptor",
+ target,
+ tok_get_pos( tok ) );
+ tok_next(tok);
+ }
+ free(target);
+ }
+ break;
+ }
+ }
+
+ j->io = io_add( j->io, new_io );
+
+ }
+ break;
+
+ case TOK_ERROR:
+ {
+ error_arg( SYNTAX_ERROR,
+ TOK_ERR_MSG,
+ tok_last(tok),
+ tok_get_pos( tok ) );
+
+ return;
+ }
+
+ default:
+ error_arg( SYNTAX_ERROR,
+ L"Unexpected token of type",
+ tok_get_desc( tok_last_type(tok)),
+ tok_get_pos( tok ) );
+ tok_next(tok);
+ break;
+ }
+
+ if( (is_finished) || (error_code != 0) )
+ break;
+
+ tok_next( tok );
+ }
+ return;
+}
+
+
+/**
+ Fully parse a single job. Does not call exec on it.
+
+ \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 )
+{
+ array_list_t args; // The list that will become the argc array for the program
+ int use_function = 1; // May functions be considered when checking what action this command represents
+ int use_builtin = 1; // May builtins be considered when checking what action this command represents
+ int is_new_block=0; // Does this command create a new block?
+
+ block_t *prev_block = current_block;
+
+// debug( 2, L"begin parse_job()\n" );
+ al_init( &args );
+
+ current_tokenizer_pos = tok_get_pos( tok );
+
+ while( al_get_count( &args ) == 0 )
+ {
+ wchar_t *nxt=0;
+ switch( tok_last_type( tok ))
+ {
+ case TOK_STRING:
+ nxt = expand_one( wcsdup(tok_last( tok )),
+ EXPAND_SKIP_SUBSHELL | EXPAND_SKIP_VARIABLES);
+ if( nxt == 0 )
+ {
+ error_arg( SYNTAX_ERROR,
+ L"Illegal command name ",
+ tok_last( tok ),
+ tok_get_pos( tok ) );
+ al_destroy( &args );
+ return 0;
+ }
+ break;
+
+ case TOK_ERROR:
+ {
+ error_arg( SYNTAX_ERROR,
+ TOK_ERR_MSG,
+ tok_last(tok),
+ tok_get_pos( tok ) );
+
+ al_destroy( &args );
+ return 0;
+ }
+
+ default:
+ error_arg( SYNTAX_ERROR,
+ L"Expected a command name, got token of type ",
+ tok_get_desc( tok_last_type(tok)),
+ tok_get_pos( tok ) );
+ al_destroy( &args );
+ return 0;
+ }
+
+ int mark = tok_get_pos( tok );
+
+ 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;
+ free( nxt );
+ continue;
+ }
+ }
+ 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;
+ free( nxt );
+ continue;
+ }
+ }
+ 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;
+ free( nxt );
+ continue;
+ }
+ }
+ else if( wcscmp( L"and", nxt )==0 )
+ {
+ tok_next( tok );
+ if( tok_last(tok)[0] == L'-' )
+ {
+ tok_set_pos( tok, mark);
+ }
+ else
+ {
+ parser_push_block( AND );
+ free( nxt );
+ continue;
+ }
+ }
+ else if( wcscmp( L"or", nxt )==0 )
+ {
+ tok_next( tok );
+ if( tok_last(tok)[0] == L'-' )
+ {
+ tok_set_pos( tok, mark);
+ }
+ else
+ {
+ parser_push_block( OR );
+ free( nxt );
+ continue;
+ }
+ }
+ else if( wcscmp( L"exec", nxt )==0 )
+ {
+ if( p != j->first_process )
+ {
+ error( SYNTAX_ERROR,
+ EXEC_ERR_MSG,
+ tok_get_pos( tok ) );
+ al_destroy( &args );
+ free(nxt);
+ return 0;
+ }
+
+ tok_next( tok );
+ if( tok_last(tok)[0] == L'-' )
+ {
+ tok_set_pos( tok, mark);
+ }
+ else
+ {
+ use_function = 0;
+ use_builtin=0;
+ p->type=INTERNAL_EXEC;
+ free( nxt );
+ continue;
+ }
+ }
+ else if( wcscmp( L"while", nxt ) ==0 )
+ {
+ int new_block = 0;
+ tok_next( tok );
+
+ if( (current_block->type != WHILE) )
+ {
+ new_block = 1;
+ }
+ else if( current_block->while_state == WHILE_TEST_AGAIN )
+ {
+ current_block->while_state = WHILE_TEST_FIRST;
+ }
+ else
+ {
+ new_block = 1;
+ }
+
+ if( new_block )
+ {
+ parser_push_block( WHILE );
+ current_block->while_state=WHILE_TEST_FIRST;
+ current_block->tok_pos = mark;
+ }
+
+ free( nxt );
+ is_new_block=1;
+
+ continue;
+ }
+ else if( wcscmp( L"if", nxt ) ==0 )
+ {
+ tok_next( tok );
+
+ parser_push_block( IF );
+
+ current_block->if_state=0;
+ current_block->tok_pos = mark;
+
+ free( nxt );
+ is_new_block=1;
+ continue;
+ }
+
+ if( use_function)
+ {
+ int nxt_forbidden;
+ wchar_t *forbid;
+
+ forbid = (wchar_t *)(al_get_count( &forbidden_function)?al_peek( &forbidden_function ):0);
+ nxt_forbidden = forbid && (wcscmp( forbid, nxt) == 0 );
+
+ /*
+ Make feeble attempt to avoid infinite recursion. Will at
+ least catch some accidental infinite recursion calls.
+ */
+ if( function_exists( nxt ) && !nxt_forbidden)
+ {
+ /*
+ Check if we have reached the maximum recursion depth
+ */
+ if( al_get_count( &forbidden_function ) > MAX_RECURSION_DEPTH )
+ {
+ error( SYNTAX_ERROR,
+ RECURSION_ERR_MSG,
+ tok_get_pos( tok ) );
+ }
+ else
+ {
+ p->type = INTERNAL_FUNCTION;
+ }
+ }
+ }
+ al_push( &args, nxt );
+ }
+
+ if( error_code == 0 )
+ {
+ if( !p->type )
+ {
+ if( use_builtin &&
+ builtin_exists( (wchar_t *)al_get( &args, 0 ) ) )
+ {
+ p->type = INTERNAL_BUILTIN;
+ is_new_block = parser_is_block( (wchar_t *)al_get( &args, 0 ) );
+ }
+ }
+
+ if( !p->type || (p->type == INTERNAL_EXEC) )
+ {
+ /*
+ If we are not executing the current block, allow
+ non-existent commands.
+ */
+ if( current_block->skip )
+ {
+ p->actual_cmd = wcsdup(L"");
+ }
+ else
+ {
+
+ p->actual_cmd = get_filename( (wchar_t *)al_get( &args, 0 ) );
+ /*
+ Check if the specified command exists
+ */
+ if( p->actual_cmd == 0 )
+ {
+
+ /*
+ That is not a command! Test if it is a
+ directory, in which case, we use 'cd' as the
+ implicit command.
+ */
+ wchar_t *pp =
+ parser_cdpath_get( (wchar_t *)al_get( &args, 0 ) );
+ if( pp )
+ {
+ wchar_t *tmp;
+ free( pp );
+
+ tmp = (wchar_t *)al_get( &args, 0 );
+ al_truncate( &args, 0 );
+ al_push( &args, wcsdup( L"cd" ) );
+ al_push( &args, tmp );
+ /*
+ If we have defined a wrapper around cd, use it,
+ otherwise use the cd builtin
+ */
+ if( function_exists( L"cd" ) )
+ p->type = INTERNAL_FUNCTION;
+ else
+ p->type = INTERNAL_BUILTIN;
+ }
+ else
+ {
+ error_arg( EVAL_ERROR,
+ L"Unknown command",
+ (wchar_t *)al_get( &args, 0 ),
+ tok_get_pos( tok ) );
+ }
+ }
+ }
+ }
+ }
+
+ if( is_new_block )
+ {
+ const wchar_t *end=parser_find_end( tok_string( tok ) +
+ current_tokenizer_pos );
+ tokenizer subtok;
+ int make_sub_block = j->first_process != p;
+
+ if( !end )
+ {
+ error( SYNTAX_ERROR,
+ L"Could not find end of block" ,
+ tok_get_pos( tok ) );
+ }
+
+ if( !make_sub_block )
+ {
+ tok_init( &subtok, end, 0 );
+ switch( tok_last_type( &subtok ) )
+ {
+ case TOK_END:
+ break;
+
+ case TOK_REDIRECT_OUT:
+ case TOK_REDIRECT_APPEND:
+ case TOK_REDIRECT_IN:
+ case TOK_REDIRECT_FD:
+ case TOK_PIPE:
+ {
+ make_sub_block = 1;
+ break;
+ }
+
+ default:
+ {
+ error_arg( SYNTAX_ERROR,
+ L"Expected end of command, got token of type ",
+ tok_get_desc( tok_last_type(tok)),
+ tok_get_pos( tok ) );
+ }
+ }
+ tok_destroy( &subtok );
+ }
+
+ if( make_sub_block )
+ {
+
+ int end_pos = end-tok_string( tok );
+ wchar_t *sub_block= wcsndup( tok_string( tok ) + current_tokenizer_pos,
+ end_pos - current_tokenizer_pos);
+
+ p->type = INTERNAL_BLOCK;
+ free( (void *)al_get( &args, 0 ) );
+ al_set( &args, 0, sub_block );
+
+ tok_set_pos( tok,
+ end_pos );
+
+ while( prev_block != current_block )
+ parser_pop_block();
+ }
+ else tok_next( tok );
+
+ }
+ else tok_next( tok );
+
+ if( !error_code )
+ parse_job_main_loop( p, j, tok, &args );
+
+ if( error_code )
+ {
+ /*
+ We don't know what the state of the args array and the argv
+ vector is on error, so we do an internal cleanup here.
+ */
+ al_foreach( &args,
+ (void (*)(const void *))&free );
+ free(p->argv);
+ p->argv=0;
+ /*
+ Make sure the block stack is consistent
+ */
+ while( prev_block != current_block )
+ parser_pop_block();
+
+ }
+ al_destroy( &args );
+
+// debug( 2, L"end parse_job()\n" );
+ 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
+
+*/
+static void skipped_exec( job_t * j )
+{
+ process_t *p;
+ for( p = j->first_process; p; p=p->next )
+ {
+ if( p->type )
+ {
+ if(( wcscmp( p->argv[0], L"for" )==0) ||
+ ( wcscmp( p->argv[0], L"switch" )==0) ||
+ ( 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 ) &&
+ (current_block->if_state != 0))
+ {
+ 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
+*/
+
+static void eval_job( tokenizer *tok )
+{
+ job_t *j;
+
+ int start_pos = job_start_pos = tok_get_pos( tok );
+ debug( 2, L"begin eval_job()\n" );
+ long long t1=0, t2=0, t3=0;
+ profile_element_t *p=0;
+ int skip = 0;
+
+ if( !is_block && !is_subshell )
+ env_universal_read_all();
+
+ if( profile )
+ {
+ p=malloc( sizeof(profile_element_t));
+ p->cmd=0;
+ al_push( &profile_data, p );
+ p->skipped=1;
+ t1 = get_time();
+ }
+
+ switch( tok_last_type( tok ) )
+ {
+ case TOK_STRING:
+ {
+ j = job_create();
+ j->command=0;
+ j->fg=1;
+ j->constructed=0;
+ j->skip_notification = is_subshell;
+
+ if( is_interactive )
+ {
+ if( tcgetattr (0, &j->tmodes) )
+ {
+ tok_next( tok );
+ wperror( L"tcgetattr" );
+ job_free( j );
+ break;
+ }
+ }
+
+ j->first_process = calloc( 1, sizeof( process_t ) );
+
+ /* Copy the command name */
+ if( current_block->type == OR )
+ {
+ skip = (proc_get_last_status() == 0 );
+ parser_pop_block();
+ }
+ else if( current_block->type == AND )
+ {
+ skip = (proc_get_last_status() != 0 );
+ parser_pop_block();
+ }
+
+
+ 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 );
+ wchar_t *newline = wcschr( tok_string(tok)+start_pos,
+ L'\n' );
+ if( newline )
+ stop_pos = mini( stop_pos, newline - tok_string(tok) );
+
+ j->command = wcsndup( tok_string(tok)+start_pos,
+ stop_pos-start_pos );
+ }
+ else
+ j->command = wcsdup( L"" );
+
+ if( profile )
+ {
+ t2 = get_time();
+ p->cmd = wcsdup( j->command );
+ p->skipped=current_block->skip;
+ }
+
+ skip |= current_block->skip;
+
+ if(!skip )
+ {
+ exec( j );
+ }
+ else
+ {
+ skipped_exec( j );
+ }
+
+ if( profile )
+ {
+ t3 = get_time();
+ p->level=eval_level;
+ p->parse = t2-t1;
+ p->exec=t3-t2;
+ }
+
+ if( current_block->type == WHILE )
+ {
+// debug( 2, L"We are at begining of a while block\n" );
+
+ switch( current_block->while_state )
+ {
+ case WHILE_TEST_FIRST:
+ {
+ current_block->skip = proc_get_last_status()!= 0;
+ current_block->while_state=WHILE_TESTED;
+ }
+ break;
+ }
+ }
+
+ if( current_block->type == IF )
+ {
+ if( (!current_block->if_state) &&
+ (!current_block->skip) )
+ {
+ /*
+ We need to call job_do_notification,
+ since this is the function which sets
+ the status of the last process to exit
+ */
+// debug( 2, L"Result of if block is %d\n", proc_get_last_status() );
+
+ current_block->skip = proc_get_last_status()!= 0;
+ current_block->if_state++;
+ }
+ }
+
+ }
+ else
+ {
+ job_free( j );
+ }
+ break;
+ }
+
+ case TOK_END:
+ {
+ if( tok_has_next( tok ))
+ tok_next( tok );
+ break;
+ }
+
+ case TOK_ERROR:
+ {
+ error_arg( SYNTAX_ERROR,
+ TOK_ERR_MSG,
+ tok_last(tok),
+ tok_get_pos( tok ) );
+
+ return;
+ }
+
+ default:
+ {
+ error_arg( SYNTAX_ERROR,
+ L"Expected a command string, got token of type",
+ tok_get_desc( tok_last_type(tok)),
+ tok_get_pos( tok ) );
+
+ return;
+ }
+ }
+
+ if( is_subshell )
+ job_do_notification();
+// debug( 2, L"end eval_job()\n" );
+}
+
+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;
+ block_io = io;
+
+ debug( 2, L"Eval command %ls", cmd );
+
+ if( !cmd )
+ {
+ debug( 1,
+ L"Tried to evaluate null pointer\n"
+ L"If this error can be reproduced, please file a bug report." );
+ return 1;
+ }
+
+ if( (block_type!=TOP) &&
+ (block_type != FUNCTION_CALL) &&
+ (block_type != SUBST))
+ {
+ debug( 1,
+ L"Tried to evaluate buffer using invalid block scope of type '%ls'\n"
+ L"If this error can be reproduced, please file a bug report.",
+ parser_get_block_desc( block_type ) );
+ return 1;
+ }
+
+
+ eval_level++;
+ current_tokenizer = malloc( sizeof(tokenizer));
+
+ parser_push_block( block_type );
+
+ forbid_count = al_get_count( &forbidden_function );
+
+ tok_init( current_tokenizer, cmd, 0 );
+ error_code = 0;
+
+ while( tok_has_next( current_tokenizer ) &&
+ !error_code &&
+ !sanity_check() &&
+ !exit_status() )
+ eval_job( current_tokenizer );
+
+ int prev_block_type = current_block->type;
+ parser_pop_block();
+
+ while( start_current_block != current_block )
+ {
+ if( current_block == 0 )
+ {
+ debug( 0,
+ L"End of block mismatch\n"
+ L"Program terminating. If this error can be reproduced,\n"
+ L"please file a bug report." );
+ exit(1);
+ break;
+ }
+
+ if( (!error_code) && (!exit_status()) && (!proc_get_last_status()) )
+ {
+ char *h;
+
+ //debug( 2, L"Status %d\n", proc_get_last_status() );
+
+ switch( prev_block_type )
+ {
+ case OR:
+ case AND:
+ debug( 1,
+ COND_ERR_MSG );
+ fwprintf( stderr, L"%ls", parser_current_line() );
+
+ h = builtin_help_get( prev_block_type == OR? L"or": L"and" );
+ if( h )
+ fwprintf( stderr, L"%s", h );
+ break;
+
+ default:
+ debug( 1,
+ L"%ls", parser_get_block_desc( current_block->type ) );
+ debug( 1,
+ END_ERR_MSG );
+ fwprintf( stderr, L"%ls", parser_current_line() );
+
+ h = builtin_help_get( L"end" );
+ if( h )
+ fwprintf( stderr, L"%s", h );
+ break;
+ }
+
+ }
+ prev_block_type = current_block->type;
+ parser_pop_block();
+ }
+
+ print_errors();
+
+ tok_destroy( current_tokenizer );
+ free( current_tokenizer );
+
+ while( forbid_count < al_get_count( &forbidden_function ))
+ parser_allow_function();
+
+ current_tokenizer=previous_tokenizer;
+
+ code=error_code;
+ error_code=0;
+
+ block_io = prev_io;
+
+ eval_level--;
+
+ return code;
+}
+
+
+int parser_test( wchar_t * buff,
+ int babble )
+{
+ tokenizer tok;
+ int had_cmd=0;
+ 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];
+ int is_pipeline = 0;
+ int forbid_pipeline = 0;
+ int needs_cmd=0;
+ int require_additional_commands=0;
+
+ current_tokenizer = &tok;
+
+ for( tok_init( &tok, buff, 0 );
+ tok_has_next( &tok ) && !err;
+ 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;
+
+ if( require_additional_commands )
+ {
+ if( contains_str( tok_last(&tok),
+ L"end",
+ 0 ) )
+ {
+ err=1;
+ if( babble )
+ {
+ error( SYNTAX_ERROR,
+ COND_ERR_MSG,
+ tok_get_pos( &tok ) );
+ print_errors();
+ }
+ }
+
+ require_additional_commands--;
+ }
+
+ if( wcscmp(tok_last(&tok), L"end")==0)
+ {
+ tok_next( &tok );
+ count--;
+ tok_set_pos( &tok, mark );
+ }
+ else if( parser_is_block( tok_last(&tok) ) )
+ {
+ if( count >= BLOCK_MAX_COUNT )
+ {
+ error( SYNTAX_ERROR, BLOCK_ERR_MSG, tok_get_pos( &tok ) );
+ print_errors();
+ }
+ else
+ {
+ if( wcscmp( tok_last(&tok), L"while") == 0 )
+ block_type[count] = WHILE;
+ else if( wcscmp( tok_last(&tok), L"for") == 0 )
+ block_type[count] = FOR;
+ else if( wcscmp( tok_last(&tok), L"switch") == 0 )
+ block_type[count] = SWITCH;
+ else if( wcscmp( tok_last(&tok), L"if") == 0 )
+ block_type[count] = IF;
+ else if( wcscmp( tok_last(&tok), L"function") == 0 )
+ block_type[count] = FUNCTION_DEF;
+ else
+ block_type[count] = -1;
+
+// debug( 2, L"add block of type %d after cmd %ls\n", block_type[count], tok_last(&tok) );
+
+
+ block_pos[count] = current_tokenizer_pos;
+ tok_next( &tok );
+ count++;
+ tok_set_pos( &tok, mark );
+ }
+ }
+
+ if( parser_is_subcommand( tok_last( &tok ) ) )
+ {
+ needs_cmd = 1;
+ had_cmd = 0;
+ }
+
+ if( contains_str( tok_last( &tok ),
+ L"or",
+ L"and",
+ 0 ) )
+ {
+ if( is_pipeline )
+ {
+ err=1;
+ if( babble )
+ {
+ error( SYNTAX_ERROR,
+ EXEC_ERR_MSG,
+ tok_get_pos( &tok ) );
+ print_errors();
+
+ }
+ }
+ require_additional_commands=2;
+ }
+
+
+ if( parser_is_pipe_forbidden( tok_last( &tok ) ) )
+ {
+ if( is_pipeline )
+ {
+ err=1;
+ if( babble )
+ {
+ error( SYNTAX_ERROR,
+ EXEC_ERR_MSG,
+ tok_get_pos( &tok ) );
+ print_errors();
+
+ }
+ }
+ forbid_pipeline = 1;
+ }
+
+ if( wcscmp( L"case", tok_last( &tok ) )==0 )
+ {
+ if( !count || block_type[count-1]!=SWITCH )
+ {
+ err=1;
+
+// debug( 2, L"Error on block type %d\n", block_type[count-1] );
+
+
+ if( babble )
+ {
+ error( SYNTAX_ERROR,
+ L"'case' builtin not inside of switch block",
+ tok_get_pos( &tok ) );
+ print_errors();
+ }
+ }
+ }
+ else if( wcscmp( L"break", tok_last( &tok ) )==0 ||
+ wcscmp( L"continue", tok_last( &tok ) )==0)
+ {
+ 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;
+ }
+ }
+
+ if( !found_loop )
+ {
+ err=1;
+
+ if( babble )
+ {
+ error( SYNTAX_ERROR,
+ L"Loop control command while not inside of loop",
+ tok_get_pos( &tok ) );
+ print_errors();
+ }
+ }
+ }
+ else if( wcscmp( L"else", tok_last( &tok ) )==0 )
+ {
+ if( !count || block_type[count-1]!=IF )
+ {
+ err=1;
+ if( babble )
+ {
+ error( SYNTAX_ERROR,
+ L"'else' builtin not inside of if block",
+ tok_get_pos( &tok ) );
+ print_errors();
+ }
+ }
+
+ }
+
+ if( count < 0 )
+ {
+ err = 1;
+ if( babble )
+ {
+ error( SYNTAX_ERROR,
+ L"'end' command outside of block",
+ tok_get_pos( &tok ) );
+ print_errors();
+ }
+ }
+ }
+ break;
+ }
+
+ case TOK_REDIRECT_OUT:
+ case TOK_REDIRECT_IN:
+ case TOK_REDIRECT_APPEND:
+ case TOK_REDIRECT_FD:
+ {
+ if( !had_cmd )
+ {
+ err = 1;
+ if( babble )
+ {
+ error( SYNTAX_ERROR,
+ L"Redirection error",
+ tok_get_pos( &tok ) );
+ print_errors();
+ }
+ }
+ break;
+ }
+
+ case TOK_END:
+ {
+ if( needs_cmd && !had_cmd )
+ {
+ err = 1;
+ if( babble )
+ {
+ error( SYNTAX_ERROR,
+ CMD_ERR_MSG,
+ tok_get_pos( &tok ) );
+ print_errors();
+ }
+ }
+ needs_cmd=0;
+ had_cmd = 0;
+ is_pipeline=0;
+ forbid_pipeline=0;
+ break;
+ }
+
+ case TOK_PIPE:
+ {
+ if( forbid_pipeline )
+ {
+ err=1;
+ if( babble )
+ {
+ error( SYNTAX_ERROR,
+ EXEC_ERR_MSG,
+ tok_get_pos( &tok ) );
+ print_errors();
+ }
+ }
+ needs_cmd=0;
+ is_pipeline=1;
+ }
+
+
+ case TOK_BACKGROUND:
+ {
+ if( needs_cmd && !had_cmd )
+ {
+ err = 1;
+ if( babble )
+ {
+ error( SYNTAX_ERROR,
+ CMD_ERR_MSG,
+ tok_get_pos( &tok ) );
+ print_errors();
+ }
+ }
+
+ if( had_cmd )
+ {
+ had_cmd = 0;
+ }
+ break;
+ }
+
+ case TOK_ERROR:
+ default:
+ err = 1;
+ if( babble )
+ {
+ error_arg( SYNTAX_ERROR,
+ TOK_ERR_MSG,
+ tok_last(&tok),
+ tok_get_pos( &tok ) );
+ print_errors();
+ //debug( 2, tok_last( &tok) );
+ }
+ break;
+ }
+ }
+
+ if( require_additional_commands )
+ {
+ err=1;
+ if( babble )
+ {
+ error( SYNTAX_ERROR,
+ COND_ERR_MSG,
+ tok_get_pos( &tok ) );
+ print_errors();
+ }
+ }
+
+
+ if( babble && count>0 )
+ {
+ error( SYNTAX_ERROR,
+ END_ERR_MSG L"\n",
+ block_pos[count-1] );
+ print_errors();
+ }
+
+ tok_destroy( &tok );
+
+
+ current_tokenizer=previous_tokenizer;
+ current_tokenizer_pos = previous_pos;
+
+ error_code=0;
+
+ return err | ((count!=0)<<1);
+}
+