sh

/* $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);
}