/* $Id$ */
/* Copyright (c) 2004-2012 Pierre Pronchery <khorben@defora.org> */
/* This file is part of DeforaOS Unix sh */
/* This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, version 3 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>. */



#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <stdint.h>
#include <ctype.h>
#include <assert.h>
#include "token.h"
#include "scanner.h"
#include "builtin.h"
#include "job.h"
#include "parser.h"


/* Parser */
/* private */
/* types */
typedef struct _Parser
{
	Prefs * prefs;
	int argc;
	char ** argv;
	Scanner scanner;
	Token ** tokens;
	unsigned int tokens_cnt;
	Token * token;
} Parser;

/* public */
/* functions */
/* parser */
static void parser_free(Parser * parser);
static void parser_scan(Parser * parser);
static int parser_exec(Parser * parser, unsigned int * pos, int skip);
static int complete_command(Parser * parser);

int parser(Prefs * prefs, char const * string, FILE * fp, int argc,
		char * argv[])
{
	Parser parser;
	unsigned int pos;

	parser.prefs = prefs;
	parser.argc = argc;
	parser.argv = argv;
	scanner_init(&parser.scanner, prefs, fp, string);
	parser.tokens = NULL;
	parser.tokens_cnt = 0;
	for(parser_scan(&parser); parser.token != NULL;)
	{
		if(parser.token->code == TC_EOI)
		{
			token_delete(parser.token);
			break;
		}
		complete_command(&parser);
		if(parser.token != NULL)
			for(pos = 0; pos < parser.tokens_cnt; pos++)
				parser_exec(&parser, &pos, 0);
		parser_free(&parser);
		if(parser.token == NULL)
			parser_scan(&parser);
	}
	free(parser.tokens);
	return 0;
}


static void parser_free(Parser * parser)
{
	unsigned int i;

#ifdef DEBUG
	fputs("parser_free()\n", stderr);
#endif
	if(parser->token == NULL && parser->tokens_cnt > 0)
		/* FIXME ugly workaround for newlines */
		parser->token = parser->tokens[parser->tokens_cnt-1];
	for(i = 0; i < parser->tokens_cnt-1; i++)
		token_delete(parser->tokens[i]);
	free(parser->tokens);
	parser->tokens = NULL;
	parser->tokens_cnt = 0;
	if(parser->token == NULL)
		return;
	if(parser->token->code == TC_EOI)
		return;
	token_delete(parser->token);
	parser->token = NULL;
}


static void parser_error(Parser * parser, char * format, ...)
{
	va_list vl;

	fputs("sh: ", stderr);
	va_start(vl, format);
	vfprintf(stderr, format, vl);
	va_end(vl);
	fputc('\n', stderr);
	parser->token = NULL;
}


static void parser_scan(Parser * parser)
{
	Token ** p;

	if((parser->token = scanner_next(&parser->scanner)) == NULL)
		return;
	if((p = realloc(parser->tokens, (parser->tokens_cnt + 1)
					* sizeof(*p))) == NULL)
	{
		sh_error("malloc", 0);
		return;
	}
	parser->tokens = p;
	parser->tokens[parser->tokens_cnt++] = parser->token;
}


static int parser_check(Parser * parser, TokenCode code)
{
	if(parser->token == NULL || parser->token->code != code)
	{
		parser_error(parser, "%s%s%s", "\"", sTokenCode[code],
				"\" expected");
		return 0;
	}
	parser_scan(parser);
	return 1;
}


static int parser_check_word(Parser * parser, char const * word)
{
	if(parser->token == NULL || parser->token->code != TC_TOKEN
			|| parser->token->string == NULL
			|| strcmp(parser->token->string, word) != 0)
	{
		parser_error(parser, "%s%s%s", "\"", word, "\" expected");
		return 0;
	}
	parser_scan(parser);
	return 1;
}


/* parser_exec */
static int _exec_do(Parser * parser, unsigned int * pos, int skip, int * ret,
		int * skiplocal);
static int _exec_cmd(Parser * parser, unsigned int * pos, int skip);
static int _exec_for(Parser * parser, unsigned int * pos, int skip);
static int _exec_if(Parser * parser, unsigned int * pos, int skip);
static int _exec_case(Parser * parser, unsigned int * pos, int skip);
static int _exec_until(Parser * parser, unsigned int * pos, int skip);
static int _exec_while(Parser * parser, unsigned int * pos, int skip);

static int parser_exec(Parser * parser, unsigned int * pos, int skip)
{
	int ret = skip;
	int skiplocal = skip;

#ifdef DEBUG
	fprintf(stderr, "%s%u%s%s", "parser_exec(", *pos, ")", skip ? " skip\n"
			: "\n");
#endif
	while(*pos < parser->tokens_cnt)
		ret = _exec_do(parser, pos, skip, &ret, &skiplocal);
	return ret;
}

static int _exec_do(Parser * parser, unsigned int * pos, int skip, int * ret,
		int * skiplocal)
{
	switch(parser->tokens[*pos]->code)
	{
		case TC_OP_AND_IF:
			*skiplocal = (skip || *ret != 0);
			(*pos)++;
			break;
		case TC_OP_OR_IF:
			*skiplocal = (skip || *ret == 0);
			(*pos)++;
			break;
		case TC_ASSIGNMENT_WORD:
		case TC_IO_NUMBER:
		case TC_OP_DLESS:
		case TC_OP_DGREAT:
		case TC_OP_LESSAND:
		case TC_OP_GREATAND:
		case TC_OP_LESSGREAT:
		case TC_OP_CLOBBER:
		case TC_OP_LESS:
		case TC_OP_GREAT:
		case TC_WORD:
			*ret = _exec_cmd(parser, pos, *skiplocal);
			break;
		case TC_RW_IF:
			*ret = _exec_if(parser, pos, *skiplocal);
			break;
		case TC_RW_CASE:
			*ret = _exec_case(parser, pos, *skiplocal);
			break;
		case TC_RW_WHILE:
			*ret = _exec_while(parser, pos, *skiplocal);
			break;
		case TC_RW_UNTIL:
			*ret = _exec_until(parser, pos, *skiplocal);
			break;
		case TC_RW_FOR:
			*ret = _exec_for(parser, pos, *skiplocal);
			break;
		case TC_EOI:
		case TC_NEWLINE:
		case TC_OP_SEMICOLON:
#ifdef DEBUG
			fprintf(stderr, "DEBUG: %s() \";\"\n", __func__);
#endif
			if(*skiplocal != skip)
				*skiplocal = 0;
			(*pos)++;
			break;
		default:
#ifdef DEBUG /* FIXME the assertion is reached: (3) 27 (do), (7) 28 (done) */
			fprintf(stderr, "%s%u%s%u%s", "parser_exec(", *pos,
					") ", parser->tokens[*pos]->code,
					skip ? " skip\n" : "\n");
#endif
			assert(0);
			break;
	}
	return *ret;
}


/* exec_cmd */
static int _exec_cmd_env(char * envp[]);
static int _exec_cmd_builtin(int argc, char ** argv, uint8_t * bg_error);
static int _exec_cmd_child(int argc, char ** argv, uint8_t * bg_error,
		char * inf, char * outf); /* XXX group args in a struct? */

static int _exec_cmd(Parser * parser, unsigned int * pos, int skip)
{
	int ret = 1;
	char ** argv = NULL;
	unsigned int argv_cnt = 0;
	char ** envp = NULL;
	unsigned int envp_cnt = 0;
	char ** p;
	uint8_t bg_error = 0;
	char * inf = NULL;
	char * outf = NULL;

	if(!skip && (envp = sh_export()) != NULL)
		for(envp_cnt = 0; envp[envp_cnt] != NULL; envp_cnt++);
	for(; *pos < parser->tokens_cnt; (*pos)++)
	{
		switch(parser->tokens[*pos]->code)
		{
			case TC_ASSIGNMENT_WORD:
				if(skip)
					break;
				if((p = realloc(envp, sizeof(*p)*(envp_cnt+2)))
						== NULL)
					/* FIXME should not exit */
					exit(sh_error("malloc", 125));
				envp = p;
				envp[envp_cnt++] = parser->tokens[*pos]->string;
				break;
			case TC_IO_NUMBER:
			case TC_OP_BAR:
			case TC_OP_CLOBBER:
			case TC_OP_GREATAND:
			case TC_OP_LESSAND:
			case TC_OP_LESSGREAT:
				/* FIXME implement */
				break;
			case TC_OP_GREAT:
				outf = parser->tokens[++(*pos)]->string;
				break;
			case TC_OP_LESS:
				inf = parser->tokens[++(*pos)]->string;
				break;
			case TC_WORD:
				if(skip)
					break;
				if((p = realloc(argv, sizeof(*p)*(argv_cnt+2)))
						== NULL)
					/* FIXME should not exit */
					exit(sh_error("malloc", 125));
				argv = p;
				argv[argv_cnt++] = parser->tokens[*pos]->string;
				break;
			case TC_OP_AMPERSAND:
				bg_error = 1;
			default:
				ret = -1;
				break;
		}
		if(ret == -1)
			break;
	}
	if(skip)
		return skip;
	if(envp != NULL)
		envp[envp_cnt] = NULL;
	/* FIXME assignment words should affect only a child...? */
	ret = _exec_cmd_env(envp);
	free(envp);
	if(argv != NULL && ret == 0)
	{
		argv[argv_cnt] = NULL;
		/* FIXME look for builtins utilities (should be none) */
		/* FIXME look for functions */
		if(_exec_cmd_builtin(argv_cnt, argv, &bg_error) != 0)
			if(_exec_cmd_child(argv_cnt, argv, &bg_error, inf, outf)
					!= 0)
				ret = 1;
		if(ret == 0)
			ret = bg_error;
	}
	free(argv);
	return ret;
}

static int _exec_cmd_env(char * envp[])
	/* FIXME share code with the builtin set? */
{
	char ** e;
	char * p;
	int ret = 0;

	if(envp == NULL)
		return 0;
	for(e = envp; *e != NULL; e++)
	{
		for(p = *e; *p != '\0' && *p != '='; p++);
		if(*p == '\0')
			continue;
		*p = '\0';
		if(setenv(*e, p + 1, 1) != 0)
			ret |= sh_error("setenv", 1);
		*p = '=';
	}
	return ret;
}

static int _exec_cmd_builtin(int argc, char ** argv, uint8_t * bg_error)
	/* FIXME should handle redirection */
{
	struct
	{
		char * cmd;
		int (*func)(int, char**);
	} builtins[] =
	{
		{ "bg",     builtin_bg		},
		{ "cd",     builtin_cd		},
		{ "exit",   builtin_exit	},
		{ "export", builtin_export	},
		{ "fg",     builtin_fg		},
		{ "jobs",   builtin_jobs	},
		{ "read",   builtin_read	},
		{ "set",    builtin_set		},
		{ "umask",  builtin_umask	},
		{ "unset",  builtin_unset	},
		{ NULL,	    NULL		}
	};
	unsigned int i;

	for(i = 0; builtins[i].cmd != NULL; i++)
		if(strcmp(builtins[i].cmd, argv[0]) == 0)
		{
			/* FIXME if(*bg_error != 0) jobs_new(); */
			*bg_error = builtins[i].func(argc, argv);
			return 0;
		}
	return -1;
}

static int _exec_cmd_child(int argc, char ** argv, uint8_t * bg_error,
		char * inf, char * outf)
{
	pid_t pid;
	int infd = -1;
	int outfd = -1;

	assert(argv[argc] == NULL);
	if(inf != NULL && (infd = open(inf, O_RDONLY)) < 0)
		return sh_error(inf, 1);
	if(outf != NULL && (outfd = open(outf, O_WRONLY | O_CREAT | O_TRUNC,
					0666)) < 0)
		return sh_error(outf, 1);
	if((pid = fork()) == -1)
	{
		if(infd != -1)
			close(infd);
		if(outfd != -1)
			close(outfd);
		return sh_error("fork", 1);
	}
	if(pid != 0)
	{
		if(infd != -1)
			close(infd);
		if(outfd != -1)
			close(outfd);
		if(*bg_error != 0)
			*bg_error = job_add(argv[0], pid, JS_RUNNING);
		else
			*bg_error = job_add(argv[0], pid, JS_WAIT);
		return 0;
	}
	if(infd != -1 && dup2(infd, 0) == -1)
		_exit(sh_error(inf, -1));
	if(outfd != -1 && dup2(outfd, 1) == -1)
		_exit(sh_error(outf, -1));
	execvp(argv[0], argv);
	_exit(sh_error(argv[0], -1));
	return 1;
}

static int _exec_for(Parser * parser, unsigned int * pos, int skip)
{
	Token * name;
	unsigned int count = 0;
	unsigned int i;
	unsigned int p;
	char const * string;

	name = parser->tokens[++(*pos)];
	(*pos)++;
	if(parser->tokens[*pos]->code == TC_RW_IN)
		for((*pos)++; parser->tokens[*pos]->code == TC_WORD; (*pos)++)
			count++;
	for((*pos)++; parser->tokens[*pos]->code != TC_RW_DO; (*pos)++);
	p = ++(*pos);
	for(i = 0; i < count; i++)
	{
		string = parser->tokens[p - 2 - count + i]->string;
		if(setenv(name->string, string, 1) != 0)
			skip = sh_error("setenv", 1);
		*pos = p;
		parser_exec(parser, pos, skip);
		if(skip != 0)
			break;
	}
	assert(parser->tokens[*pos]->code == TC_RW_DONE);
	(*pos)++;
	return skip;
}

static int _exec_if(Parser * parser, unsigned int * pos, int skip)
{
	int execd = 0;

	while(*pos < parser->tokens_cnt)
	{
		switch(parser->tokens[*pos]->code)
		{
			case TC_RW_IF:
			case TC_RW_ELIF:
				(*pos)++;
				skip = parser_exec(parser, pos, skip
						&& execd != 0);
				(*pos)--;
				continue;
			case TC_RW_ELSE:
				if(parser->tokens[(*pos)+1]->code == TC_RW_IF)
					break;
				skip = skip && execd != 0;
			case TC_RW_THEN:
				(*pos)++;
				parser_exec(parser, pos, skip);
				if(skip == 0)
					execd = 1;
				(*pos)--;
				continue;
			case TC_RW_FI:
				return 0;
			default:
				assert(0);
				break;
		}
		(*pos)++;
	}
	return skip;
}

static int _exec_case(Parser * parser, unsigned int * pos, int skip)
{
	/* FIXME actually implement */
	for((*pos)++; parser->tokens[(*pos)++]->code != TC_RW_ESAC;);
	return skip;
}

static int _exec_until(Parser * parser, unsigned int * pos, int skip)
{
	unsigned int test;

	for(test = ++(*pos);; *pos = test)
	{
		skip = (parser_exec(parser, pos, skip) == 0) || skip;
		assert(parser->tokens[*pos]->code == TC_RW_DO);
		(*pos)++;
		parser_exec(parser, pos, skip);
		if(skip != 0)
			break;
	}
	assert(parser->tokens[*pos]->code == TC_RW_DONE);
	(*pos)++;
	return skip;
}

static int _exec_while(Parser * parser, unsigned int * pos, int skip)
{
	unsigned int cond;
	int res;
	int s;

#ifdef DEBUG
	fprintf(stderr, "DEBUG: %s()\n", __func__);
#endif
	/* while */
	assert(parser->tokens[*pos]->code == TC_RW_WHILE);
	/* loop */
	for(cond = ++(*pos);; *pos = cond)
	{
		s = skip;
		while(*pos < parser->tokens_cnt
				&& parser->tokens[*pos]->code != TC_RW_DO)
			s = (_exec_do(parser, pos, skip, &res, &s) != 0)
				|| skip;
		skip = s;
		/* do */
		assert(parser->tokens[*pos]->code == TC_RW_DO);
		(*pos)++;
		while(*pos < parser->tokens_cnt
				&& parser->tokens[*pos]->code != TC_RW_DONE)
			_exec_do(parser, pos, skip, &res, &s);
		if(s != 0)
			/* the condition was false */
			break;
	}
	/* done */
	assert(parser->tokens[*pos]->code == TC_RW_DONE);
	(*pos)++;
	return skip;
}


/* rules */
static void parser_rule1(Parser * parser)
{
	unsigned int i;

#ifdef DEBUG
	fputs("rule 1\n", stderr);
#endif
	if(parser->token == NULL || parser->token->string == NULL)
		return;
	for(i = TC_RW_IF; i <= TC_RW_IN; i++)
		if(strcmp(parser->token->string, sTokenCode[i]) == 0)
		{
			parser->token->code = i;
			return;
		}
	parser->token->code = TC_WORD;
}


static void parser_rule4(Parser * parser)
{
	if(parser->token == NULL || parser->token->string == NULL)
		return;
	if(strcmp(parser->token->string, sTokenCode[TC_RW_ESAC]) == 0)
		parser->token->code = TC_RW_ESAC;
	else
		parser->token->code = TC_WORD;
}


static void parser_rule5(Parser * parser)
{
	unsigned int i;
	int c;

#ifdef DEBUG
	fputs("rule 5\n", stderr);
#endif
	if(parser->token == NULL || parser->token->string == NULL)
		return;
	c = parser->token->string[0];
	if(isdigit(c))
	{
		parser->token->code = TC_WORD;
		return;
	}
	for(i = 0; (c = parser->token->string[i]) != '\0'; i++)
		if(!isalnum(c) && c != '_')
		{
			parser->token->code = TC_WORD;
			return;
		}
	parser->token->code = TC_NAME;
}


static void parser_rule6_for(Parser * parser)
{
#ifdef DEBUG
	fputs("rule 6 (for)\n", stderr);
#endif
	if(parser->token == NULL || parser->token->string == NULL)
		return;
	if(strcmp(sTokenCode[TC_RW_DO], parser->token->string) == 0)
		parser->token->code = TC_RW_DO;
	if(strcmp(sTokenCode[TC_RW_IN], parser->token->string) == 0)
		parser->token->code = TC_RW_IN;
	else
		parser->token->code = TC_WORD;
}


static void parser_rule7b(Parser * parser);
static void parser_rule7a(Parser * parser)
{
	unsigned int i;

#ifdef DEBUG
	fputs("rule 7a\n", stderr);
#endif
	if(parser->token == NULL || parser->token->string == NULL)
		return;
	for(i = 0; parser->token->string[i] != '\0'; i++)
		if(parser->token->string[i] == '=')
		{
			parser_rule7b(parser);
			return;
		}
	parser_rule1(parser);
}


static void parser_rule7b(Parser * parser)
{
	unsigned int i;

#ifdef DEBUG
	fputs("rule 7b\n", stderr);
#endif
	if(parser->token == NULL || parser->token->string == NULL)
		return;
	switch(parser->token->string[0])
	{
		case '=':
		case '\0':
			parser->token->code = TC_WORD;
			return;
		default:
			break;
	}
	for(i = 1; parser->token->string[i] != '\0'; i++)
		if(parser->token->string[i] == '=')
		{
			parser->token->code = TC_ASSIGNMENT_WORD;
			return;
		}
	parser->token->code = TC_WORD;
}


static void parser_rule8(Parser * parser)
{
#ifdef DEBUG
	fputs("rule 8\n", stderr);
#endif
	if(parser->token == NULL)
		return;
	parser_rule1(parser);
	if(parser->token->code != TC_WORD)
		return;
	parser_rule5(parser);
	if(parser->token->code != TC_NAME)
		return;
	parser_rule7a(parser);
}


/* complete_command */
static void list(Parser * p);
static void separator(Parser * p);
static int complete_command(Parser * p)
	/* list [separator] */
{
#ifdef DEBUG
	fprintf(stderr, "%s", "complete_command()\n");
#endif
	list(p);
	if(p->token != NULL && token_in_set(p->token, TS_SEPARATOR))
		separator(p);
	return 0;
}


/* list */
static void and_or(Parser * p);
static void separator_op(Parser * p);
static void list(Parser * p)
	/* and_or { separator_op and_or } */
{
#ifdef DEBUG
	fprintf(stderr, "%s", "list()\n");
#endif
	and_or(p);
	while(p->token != NULL && token_in_set(p->token, TS_SEPARATOR_OP))
	{
		separator_op(p);
		and_or(p);
	}
}


/* and_or */
static void pipeline(Parser * p);
static void linebreak(Parser * p);
static void and_or(Parser * p)
	/* pipeline { (AND_IF | OR_IF) linebreak pipeline } */
{
#ifdef DEBUG
	fprintf(stderr, "%s", "and_or()\n");
#endif
	pipeline(p);
	while(p->token != NULL)
	{
		if(p->token->code != TC_OP_AND_IF
				&& p->token->code != TC_OP_OR_IF)
			return;
		parser_scan(p);
		linebreak(p);
		pipeline(p);
	}
}


/* pipeline */
static void pipe_sequence(Parser * p);
static void pipeline(Parser * p)
	/* [Bang] pipe_sequence */
{
#ifdef DEBUG
	fprintf(stderr, "%s", "pipeline()\n");
#endif
	if(p->token != NULL && p->token->code == TC_RW_BANG)
		parser_scan(p);
	pipe_sequence(p);
}


/* pipe_sequence */
static void command(Parser * p);
static void pipe_sequence(Parser * p)
	/* command { '|' linebreak command } */
{
	command(p);
	while(p->token != NULL && p->token->code == TC_OP_BAR)
	{
		parser_scan(p);
		linebreak(p);
		command(p);
	}
}


/* command */
static void compound_command(Parser * p);
static void redirect_list(Parser * p);
static void function_definition(Parser * p);
static void simple_command(Parser * p);
static void command(Parser * p)
	/* simple_command
	 * | compound_command [redirect_list]
	 * | function_definition */
{
#ifdef DEBUG
	fprintf(stderr, "%s", "command()\n");
#endif
	if(p->token == NULL)
		return;
	parser_rule7a(p);
	if(token_in_set(p->token, TS_COMPOUND_COMMAND))
	{
		compound_command(p);
		if(p->token != NULL && token_in_set(p->token, TS_REDIRECT_LIST))
			redirect_list(p);
	}
	else if(token_in_set(p->token, TS_FUNCTION_DEFINITION))
		function_definition(p);
	else
		simple_command(p);
}


/* compound_command */
static void brace_group(Parser * p);
static void subshell(Parser * p);
static void for_clause(Parser * p);
static void case_clause(Parser * p);
static void if_clause(Parser * p);
static void while_clause(Parser * p);
static void until_clause(Parser * p);
static void compound_command(Parser * p)
	/* brace_group
	 * | subshell
	 * | for_clause
	 * | case_clause
	 * | if_clause
	 * | while_clause
	 * | until_clause */
{
#ifdef DEBUG
	fprintf(stderr, "%s", "compound_command()\n");
#endif
	if(p->token == NULL)
		return;
	switch(p->token->code)
	{
		case TC_RW_LBRACE:
			brace_group(p);
			return;
		case TC_RW_FOR:
			for_clause(p);
			return;
		case TC_RW_CASE:
			case_clause(p);
			return;
		case TC_RW_IF:
			if_clause(p);
			return;
		case TC_RW_WHILE:
			while_clause(p);
			return;
		case TC_RW_UNTIL:
			until_clause(p);
			return;
		default:
			break;
	}
	if(p->token != NULL && p->token->code == TC_TOKEN
			&& p->token->string != NULL
			&& strcmp("(", p->token->string) == 0)
		subshell(p);
}


/* subshell */
static void compound_list(Parser * p);
static void subshell(Parser * p)
	/* '(' compound_list ')' */
{
#ifdef DEBUG
	fprintf(stderr, "%s", "subshell()\n");
#endif
	parser_scan(p);
	compound_list(p);
	parser_check_word(p, ")");
}


/* compound_list */
static void newline_list(Parser * p);
static void term(Parser * p);
static void compound_list(Parser * p)
	/* [newline_list] term [separator] */
{
#ifdef DEBUG
	fprintf(stderr, "%s", "compound_list()\n");
#endif
	if(p->token != NULL && token_in_set(p->token, TS_NEWLINE_LIST))
		newline_list(p);
	term(p);
	if(p->token != NULL && token_in_set(p->token, TS_SEPARATOR))
		separator(p);
}


/* term */
static void term(Parser * p)
	/* and_or { separator and_or } */
{
	and_or(p);
	while(p->token != NULL && token_in_set(p->token, TS_SEPARATOR))
	{
		separator(p);
		parser_rule1(p);
		if(p->token != NULL && !token_in_set(p->token, TS_AND_OR))
			break;
		and_or(p);
	}
}


/* for_clause */
static void do_group(Parser * p);
static void name(Parser * p);
static void in(Parser * p);
static void wordlist(Parser * p);
static void sequential_sep(Parser * p);
static void for_clause(Parser * p)
	/* For name linebreak [in [wordlist] sequential_sep] do_group */
{
#ifdef DEBUG
	fprintf(stderr, "%s", "for_clause()\n");
#endif
	parser_scan(p);
	name(p);
	linebreak(p);
	parser_rule6_for(p);
	if(p->token != NULL && p->token->code == TC_RW_IN)
	{
		parser_scan(p);
		parser_rule1(p);
		if(p->token != NULL && token_in_set(p->token, TS_WORDLIST))
			wordlist(p);
		sequential_sep(p);
	}
	do_group(p);
}


/* name */
static void name(Parser * p)
	/* NAME  (rule 5) */
{
#ifdef DEBUG
	fprintf(stderr, "%s", "name()\n");
#endif
	parser_rule5(p);
	parser_check(p, TC_NAME);
}


/* in */
static void in(Parser * p)
	/* In  (rule 6) */
{
	parser_scan(p);
}


/* wordlist */
static void wordlist(Parser * p)
	/* WORD { WORD } */
{
#ifdef DEBUG
	fprintf(stderr, "%s", "wordlist()\n");
#endif
	parser_scan(p);
	for(parser_rule1(p); p->token != NULL && p->token->code == TC_WORD;
			parser_rule1(p))
		parser_scan(p);
}


/* case_clause */
static void case_list(Parser * p);
static void case_clause(Parser * p)
	/* Case WORD linebreak in linebreak case_list Esac */
{
#ifdef DEBUG
	fprintf(stderr, "%s", "case_clause()\n");
#endif
	parser_scan(p);
	if(!parser_check(p, TC_TOKEN))
		return;
	linebreak(p);
	in(p);
	linebreak(p);
	parser_rule4(p);
	case_list(p);
	parser_check(p, TC_RW_ESAC);
}


/* case_list */
static void case_item(Parser * p);
static void case_list(Parser * p)
	/* { case_item } */
{
#ifdef DEBUG
	fprintf(stderr, "%s", "case_list()\n");
#endif
	while(p->token != NULL && p->token->code != TC_RW_ESAC)
		case_item(p);
}


/* case_item */
static void pattern(Parser * p);
static void case_item(Parser * p)
	/* ["("] pattern ")" (linebreak | compound_list) [DSEMI] linebreak */
{
	if(p->token != NULL && p->token->code == TC_TOKEN
			&& p->token->string != NULL
			&& strcmp(p->token->string, "(") == 0)
		parser_scan(p); /* FIXME also check for the code? */
	pattern(p);
	parser_check_word(p, ")"); /* FIXME not detected yet */
	linebreak(p);
	compound_list(p);
	if(p->token != NULL && p->token->code == TC_OP_DSEMI)
		parser_scan(p);
	linebreak(p);
}


/* pattern */
static void pattern(Parser * p)
	/* WORD  (rule 4) { '|' WORD } */
{
#ifdef DEBUG
	fprintf(stderr, "%s", "pattern()\n");
#endif
	parser_scan(p);
	while(p->token != NULL && p->token->code == TC_OP_BAR)
	{
		parser_scan(p); /* '|' */
		parser_scan(p); /* WORD */
	}
}


/* if_clause */
static void else_part(Parser * p);
static void if_clause(Parser * p)
	/* If compound_list Then compound_list [else_part] Fi */
{
#ifdef DEBUG
	fprintf(stderr, "%s", "if_clause()\n");
#endif
	parser_scan(p);
	compound_list(p);
	if(!parser_check(p, TC_RW_THEN))
		return;
	compound_list(p);
	if(p->token != NULL && token_in_set(p->token, TS_ELSE_PART))
		else_part(p);
	parser_check(p, TC_RW_FI);
}


/* else_part */
static void else_part(Parser * p)
	/* Elif compound_list Then else_part
	 * | Else compound_list */
{
#ifdef DEBUG
	fprintf(stderr, "%s", "else_part()\n");
#endif
	if(p->token == NULL)
		return;
	if(p->token->code == TC_RW_ELIF)
	{
		parser_scan(p);
		compound_list(p);
		parser_check(p, TC_RW_THEN);
		else_part(p);
	}
	else if(p->token->code == TC_RW_ELSE)
	{
		parser_scan(p);
		compound_list(p);
	}
}


/* while_clause */
static void while_clause(Parser * p)
	/* While compound_list do_group */
{
#ifdef DEBUG
	fprintf(stderr, "%s", "while_clause()\n");
#endif
	parser_scan(p);
	compound_list(p);
	do_group(p);
}


/* until_clause */
static void until_clause(Parser * p)
	/* Until compound_list do_group */
{
#ifdef DEBUG
	fprintf(stderr, "%s", "until_clause()\n");
#endif
	parser_scan(p);
	compound_list(p);
	do_group(p);
}


/* function_definition */
static void fname(Parser * p);
static void function_body(Parser * p);
static void function_definition(Parser * p)
	/* fname "(" ")" linebreak function_body */
{
	fname(p);
	parser_scan(p);
	parser_check_word(p, "(");
	parser_check_word(p, ")");
	linebreak(p);
	function_body(p);
}


/* function_body */
static void io_redirect(Parser * p);
static void function_body(Parser * p)
	/* compound_command [redirect_list]  (rule 9) */
{
	compound_command(p);
	if(p->token != NULL && token_in_set(p->token, TS_IO_REDIRECT))
		io_redirect(p);
}


/* fname */
static void fname(Parser * p)
	/* NAME  (rule 8) */
{
	parser_rule8(p); /* FIXME untested */
	parser_scan(p);
}


/* brace_group */
static void brace_group(Parser * p)
	/* Lbrace compound_list Rbrace */
{
	parser_scan(p);
	compound_list(p);
	parser_check(p, TC_RW_RBRACE);
}


/* do_group */
static void do_group(Parser * p)
	/* Do compound_list Done */
{
	parser_rule1(p);
	parser_check(p, TC_RW_DO);
	compound_list(p);
	parser_check(p, TC_RW_DONE);
}


/* simple_command */
static void cmd_prefix(Parser * p);
static void cmd_word(Parser * p);
static void cmd_suffix(Parser * p);
static void cmd_name(Parser * p);

static void simple_command(Parser * p)
	/* cmd_prefix [cmd_word [cmd_suffix]]
	 * | cmd_name [cmd_suffix] */
{
#ifdef DEBUG
	fprintf(stderr, "%s", "simple_command()\n");
#endif
	if(p->token == NULL)
		return;
	if(token_in_set(p->token, TS_CMD_PREFIX))
	{
		cmd_prefix(p);
		if(p->token == NULL || !token_in_set(p->token, TS_CMD_WORD))
			return;
		cmd_word(p);
	}
	else if(!token_in_set(p->token, TS_CMD_NAME))
		/* FIXME parser_error(p, "%s", "prefix or name expected"); */
		return;
	else
		cmd_name(p);
	if(p->token == NULL)
		return;
	parser_rule1(p);
	if(token_in_set(p->token, TS_CMD_SUFFIX))
		cmd_suffix(p);
}


/* cmd_name */
static void cmd_name(Parser * p)
	/* WORD  (rule 7a) */
{
#ifdef DEBUG
	fprintf(stderr, "%s", "cmd_name()\n");
#endif
	parser_scan(p);
}


/* cmd_word */
static void cmd_word(Parser * p)
	/* WORD  (rule 7b) */
{
#ifdef DEBUG
	fprintf(stderr, "%s", "cmd_word()\n");
#endif
	parser_scan(p);
}


/* cmd_prefix */
static void cmd_prefix(Parser * p)
	/* (ASSIGNMENT_WORD | io_redirect) { ASSIGNMENT_WORD | io_redirect } */
{
#ifdef DEBUG
	fprintf(stderr, "%s", "cmd_prefix()\n");
#endif
	while(p->token != NULL)
	{
		if(p->token->code == TC_ASSIGNMENT_WORD)
			parser_scan(p);
		else if(token_in_set(p->token, TS_IO_REDIRECT))
			io_redirect(p);
		else
			return;
		parser_rule7b(p);
	}
}


/* cmd_suffix */
static void cmd_suffix(Parser * p)
	/* { WORD | io_redirect } */
{
#ifdef DEBUG
	fprintf(stderr, "%s", "cmd_suffix()\n");
#endif
	for(; p->token != NULL; parser_rule1(p))
		if(token_in_set(p->token, TS_IO_REDIRECT))
			io_redirect(p);
		else if(p->token->code == TC_WORD)
			parser_scan(p);
		else
			break;
}


/* redirect_list */
static void redirect_list(Parser * p)
	/* io_redirect { io_redirect } */
{
	io_redirect(p);
	while(p->token != NULL && token_in_set(p->token, TS_IO_REDIRECT))
		io_redirect(p);
}


/* io_redirect */
static void io_file(Parser * p);
static void io_here(Parser * p);
static void io_redirect(Parser * p)
	/* [IO_NUMBER] (io_file | io_here) */
{
#ifdef DEBUG
	fputs("cmd_suffix()\n", stderr);
#endif
	if(p->token == NULL)
		return;
	if(p->token->code == TC_IO_NUMBER)
		parser_scan(p);
	if(p->token == NULL)
		return;
	if(token_in_set(p->token, TS_IO_FILE))
		io_file(p);
	else if(token_in_set(p->token, TS_IO_HERE))
		io_here(p);
}


/* io_file */
static void filename(Parser * p);

static void io_file(Parser * p)
	/* '<' filename
	 * | LESSAND filename
	 * | '>' filename
	 * | GREATAND filename */
{
#ifdef DEBUG
	fputs("io_file()\n", stderr);
#endif
	parser_scan(p);
	/* FIXME check if it's an appropriate token for filename */
	filename(p);
}


/* filename */
static void filename(Parser * p)
	/* WORD  (rule 2) */
{
#ifdef DEBUG
	fputs("filename()\n", stderr);
#endif
	parser_scan(p);
}


/* io_here */
static void here_end(Parser * p);
static void io_here(Parser * p)
	/* (DLESS | DLESSDASH) here_end */
{
	parser_scan(p);
	here_end(p);
}


/* here_end */
static void here_end(Parser * p)
	/* WORD  (rule 3) */
{
	/* FIXME */
	parser_scan(p);
}


/* linebreak */
static void newline_list(Parser * p);
static void linebreak(Parser * p)
	/* newline_list
	 * | */
{
#ifdef DEBUG
	fputs("linebreak()\n", stderr);
#endif
	if(p->token != NULL && token_in_set(p->token, TS_NEWLINE_LIST))
		newline_list(p);
}


/* newline_list */
static void newline_list(Parser * p)
	/* NEWLINE { NEWLINE } */
{
#ifdef DEBUG
	fputs("newline_list()\n", stderr);
#endif
	if(p->token != NULL && p->token->code != TC_NEWLINE)
	{
		parser_error(p, "%s", "newline expected");
		return;
	}
	/* FIXME
	parser_scan(p);
	while(p->token != NULL && p->token->code == TC_NEWLINE)
		parser_scan(p); */
}


/* separator_op */
static void separator_op(Parser * p)
	/* '&' | ';' */
{
#ifdef DEBUG
	fprintf(stderr, "%s", "separator_op()\n");
#endif
	if(p->token == NULL)
		return;
	if(p->token->code == TC_OP_AMPERSAND || p->token->code
			== TC_OP_SEMICOLON)
		parser_scan(p);
	else
		parser_error(p, "%s", "\"&\" or \";\" expected");
}


/* separator */
static void separator(Parser * p)
	/* separator_op linebreak
	 * | newline_list */
{
#ifdef DEBUG
	fprintf(stderr, "%s", "separator()\n");
#endif
	if(p->token == NULL)
		return;
	if(token_in_set(p->token, TS_NEWLINE_LIST))
	{
		newline_list(p);
		return;
	}
	if(!token_in_set(p->token, TS_SEPARATOR_OP))
	{
		parser_error(p, "%s", "separator or newline expected");
		return;
	}
	separator_op(p);
	linebreak(p);
}


/* sequential_sep */
static void sequential_sep(Parser * p)
		/* ";" linebreak
		 * | newline_list */
{
#ifdef DEBUG
	fprintf(stderr, "%s", "sequential_sep()\n");
#endif
	if(p->token != NULL && p->token->code == TC_OP_SEMICOLON)
	{
		parser_scan(p);
		linebreak(p);
		return;
	}
	newline_list(p);
}
