0,0 → 1,247 |
/* |
* esh - the 'pluggable' shell. |
* |
* Developed by Godmar Back for CS 3214 Fall 2009 |
* Virginia Tech. |
* |
* This is based on an assignment I did in 1993 as an undergraduate |
* student at Technische Universitaet Berlin. |
* |
* Known bugs: leaks memory when parse errors occur. |
*/ |
%{ |
#include <stdio.h> |
#include <stdlib.h> |
#define YYDEBUG 1 |
int yydebug; |
void yyerror(const char *msg); |
int yylex(void); |
|
/* |
* Error messages, csh-style |
*/ |
#define MISRED "Missing name for redirect." |
#define INVNUL "Invalid null command." |
#define AMBINP "Ambiguous input redirect." |
#define AMBOUT "Ambiguous output redirect." |
|
#include "esh.h" |
|
#define obstack_chunk_alloc malloc |
#define obstack_chunk_free free |
|
struct cmd_helper { |
struct obstack words; /* an obstack of char * to collect argv */ |
char *iored_input; |
char *iored_output; |
bool append_to_output; |
}; |
|
/* Initialize cmd_helper and, optionally, set first argv */ |
static void |
init_cmd(struct cmd_helper *cmd, char *firstcmd, |
char *iored_input, char *iored_output, bool append_to_output) |
{ |
obstack_init(&cmd->words); |
if (firstcmd) |
obstack_ptr_grow(&cmd->words, firstcmd); |
|
cmd->iored_output = iored_output; |
cmd->iored_input = iored_input; |
cmd->append_to_output = append_to_output; |
} |
|
/* print error message */ |
static void p_error(char *msg); |
|
/* Convert cmd_helper to esh_command. |
* Ensures NULL-terminated argv[] array |
*/ |
static struct esh_command * |
make_esh_command(struct cmd_helper *cmd) |
{ |
obstack_ptr_grow(&cmd->words, NULL); |
|
int sz = obstack_object_size(&cmd->words); |
char **argv = malloc(sz); |
memcpy(argv, obstack_finish(&cmd->words), sz); |
obstack_free(&cmd->words, NULL); |
|
if (*argv == NULL) { |
free(argv); |
return NULL; |
} |
|
return esh_command_create(argv, |
cmd->iored_input, |
cmd->iored_output, |
cmd->append_to_output); |
} |
|
/* Called by parser when command line is complete */ |
static void cmdline_complete(struct esh_command_line *); |
|
/* work-around for bug in flex 2.31 and later */ |
static void yyunput (int c,char *buf_ptr ) __attribute__((unused)); |
|
%} |
|
/* LALR stack types */ |
%union { |
struct cmd_helper command; |
struct esh_pipeline * pipe; |
struct esh_command_line * cmdline; |
char *word; |
} |
|
/* Nonterminals */ |
%type <command> input output |
%type <command> command |
%type <pipe> pipeline |
%type <cmdline> cmd_list |
|
/* Terminals */ |
%token <word> WORD |
%token GREATER_GREATER |
|
%% |
cmd_line: cmd_list { cmdline_complete($1); } |
|
cmd_list: /* Null Command */ { $$ = esh_command_line_create_empty(); } |
| pipeline { |
esh_pipeline_finish($1); |
$$ = esh_command_line_create($1); |
} |
| cmd_list ';' |
| cmd_list '&' { |
$$ = $1; |
struct esh_pipeline * last; |
last = list_entry(list_back(&$1->pipes), |
struct esh_pipeline, elem); |
last->bg_job = true; |
} |
| cmd_list ';' pipeline { |
esh_pipeline_finish($3); |
$$ = $1; |
list_push_back(&$$->pipes, &$3->elem); |
} |
| cmd_list '&' pipeline { |
esh_pipeline_finish($3); |
$$ = $1; |
|
struct esh_pipeline * last; |
last = list_entry(list_back(&$1->pipes), |
struct esh_pipeline, elem); |
last->bg_job = true; |
|
list_push_back(&$$->pipes, &$3->elem); |
} |
|
pipeline: command { |
struct esh_command * pcmd = make_esh_command(&$1); |
if (pcmd == NULL) { p_error(INVNUL); YYABORT; } |
$$ = esh_pipeline_create(pcmd); |
} |
| pipeline '|' command { |
/* Error: 'ls >x | wc' */ |
struct esh_command * last; |
last = list_entry(list_back(&$1->commands), |
struct esh_command, elem); |
if (last->iored_output) { p_error(AMBOUT); YYABORT; } |
|
/* Error: 'ls | <x wc' */ |
if ($3.iored_input) { p_error(AMBINP); YYABORT; } |
|
struct esh_command * pcmd = make_esh_command(&$3); |
if (pcmd == NULL) { p_error(INVNUL); YYABORT; } |
|
list_push_back(&$1->commands, &pcmd->elem); |
pcmd->pipeline = $1; |
$$ = $1; |
} |
| '|' error { p_error(INVNUL); YYABORT; } |
| pipeline '|' error { p_error(INVNUL); YYABORT; } |
|
command: WORD { |
init_cmd(&$$, $1, NULL, NULL, false); |
} |
| input |
| output |
| command WORD { |
$$ = $1; |
obstack_ptr_grow(&$$.words, $2); |
} |
| command input { |
obstack_free(&$2.words, NULL); |
/* Error: ambiguous redirect 'a <b <c' */ |
if($1.iored_input) { p_error(AMBINP); YYABORT; } |
$$ = $1; |
$$.iored_input = $2.iored_input; |
} |
| command output { |
obstack_free(&$2.words, NULL); |
/* Error: ambiguous redirect 'a >b >c' */ |
if ($1.iored_output) { p_error(AMBOUT); YYABORT; } |
$$ = $1; |
$$.iored_output = $2.iored_output; |
$$.append_to_output = $2.append_to_output; |
} |
|
input: '<' WORD { |
init_cmd(&$$, NULL, $2, NULL, false); |
} |
| '<' error { p_error(MISRED); YYABORT; } |
|
output: '>' WORD { |
init_cmd(&$$, NULL, NULL, $2, false); |
} |
| GREATER_GREATER WORD { |
init_cmd(&$$, NULL, NULL, $2, true); |
} |
/* Error: missing redirect */ |
| '>' error { p_error(MISRED); YYABORT; } |
| GREATER_GREATER error { p_error(MISRED); YYABORT; } |
|
%% |
static char * inputline; /* currently processed input line */ |
#define YY_INPUT(buf,result,max_size) \ |
{ \ |
result = *inputline ? (buf[0] = *inputline++, 1) : YY_NULL; \ |
} |
|
#define YY_NO_UNPUT |
#define YY_NO_INPUT |
#include "lex.yy.c" |
|
static void |
p_error(char *msg) |
{ |
/* print error */ |
fprintf(stderr, "%s\n", msg); |
} |
|
extern int yyparse (void); |
|
/* do not use default error handling since errors are handled above. */ |
void |
yyerror(const char *msg) { } |
|
static struct esh_command_line * commandline; |
static void cmdline_complete(struct esh_command_line *cline) |
{ |
commandline = cline; |
} |
|
/* |
* parse a commandline. |
*/ |
struct esh_command_line * |
esh_parse_command_line(char * line) |
{ |
inputline = line; |
commandline = NULL; |
|
int error = yyparse(); |
|
return error ? NULL : commandline; |
} |