Subversion Repositories Code-Repo

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
141 Kevin 1
/*
2
 * esh - the 'pluggable' shell.
3
 *
4
 * Developed by Godmar Back for CS 3214 Fall 2009
5
 * Virginia Tech.
6
 *
7
 * This is based on an assignment I did in 1993 as an undergraduate
8
 * student at Technische Universitaet Berlin.
9
 *
10
 * Known bugs: leaks memory when parse errors occur.
11
 */
12
%{
13
#include <stdio.h>
14
#include <stdlib.h>
15
#define YYDEBUG	1
16
int yydebug;
17
void yyerror(const char *msg);
18
int yylex(void);
19
 
20
/*
21
 * Error messages, csh-style
22
 */
23
#define MISRED	"Missing name for redirect."
24
#define INVNUL  "Invalid null command."
25
#define AMBINP  "Ambiguous input redirect."
26
#define AMBOUT  "Ambiguous output redirect."
27
 
28
#include "esh.h"
29
 
30
#define obstack_chunk_alloc malloc
31
#define obstack_chunk_free free
32
 
33
struct cmd_helper {
34
    struct obstack words;   /* an obstack of char * to collect argv */
35
    char *iored_input;
36
    char *iored_output;
37
    bool append_to_output;
38
};
39
 
40
/* Initialize cmd_helper and, optionally, set first argv */
41
static void
42
init_cmd(struct cmd_helper *cmd, char *firstcmd, 
43
         char *iored_input, char *iored_output, bool append_to_output)
44
{
45
    obstack_init(&cmd->words);
46
    if (firstcmd)
47
        obstack_ptr_grow(&cmd->words, firstcmd);
48
 
49
    cmd->iored_output = iored_output;
50
    cmd->iored_input = iored_input;
51
    cmd->append_to_output = append_to_output;
52
}
53
 
54
/* print error message */
55
static void p_error(char *msg);
56
 
57
/* Convert cmd_helper to esh_command.
58
 * Ensures NULL-terminated argv[] array
59
 */
60
static struct esh_command * 
61
make_esh_command(struct cmd_helper *cmd)
62
{
63
    obstack_ptr_grow(&cmd->words, NULL);
64
 
65
    int sz = obstack_object_size(&cmd->words);
66
    char **argv = malloc(sz);
67
    memcpy(argv, obstack_finish(&cmd->words), sz);
68
    obstack_free(&cmd->words, NULL);
69
 
70
    if (*argv == NULL) {
71
        free(argv);
72
        return NULL; 
73
    }
74
 
75
    return esh_command_create(argv,
76
                              cmd->iored_input,
77
                              cmd->iored_output,
78
                              cmd->append_to_output);
79
}
80
 
81
/* Called by parser when command line is complete */
82
static void cmdline_complete(struct esh_command_line *);
83
 
84
/* work-around for bug in flex 2.31 and later */
85
static void yyunput (int c,char *buf_ptr  ) __attribute__((unused));
86
 
87
%}
88
 
89
/* LALR stack types */
90
%union {
91
  struct cmd_helper command;
92
  struct esh_pipeline * pipe;
93
  struct esh_command_line * cmdline;
94
  char *word;
95
}
96
 
97
/* Nonterminals */
98
%type <command> input output
99
%type <command> command
100
%type <pipe> pipeline
101
%type <cmdline> cmd_list
102
 
103
/* Terminals */
104
%token <word> WORD
105
%token GREATER_GREATER 
106
 
107
%%
108
cmd_line: cmd_list { cmdline_complete($1); }
109
 
110
cmd_list:	/* Null Command */ { $$ = esh_command_line_create_empty(); }
111
|		pipeline { 
112
            esh_pipeline_finish($1);
113
            $$ = esh_command_line_create($1);
114
        } 
115
|		cmd_list ';'
116
|		cmd_list '&' {
117
            $$ = $1;
118
            struct esh_pipeline * last;
119
            last = list_entry(list_back(&$1->pipes), 
120
                              struct esh_pipeline, elem);
121
            last->bg_job = true;
122
        }
123
|		cmd_list ';' pipeline	{ 
124
            esh_pipeline_finish($3);
125
            $$ = $1;
126
            list_push_back(&$$->pipes, &$3->elem);
127
        }
128
|		cmd_list '&' pipeline	{ 
129
            esh_pipeline_finish($3);
130
            $$ = $1;
131
 
132
            struct esh_pipeline * last;
133
            last = list_entry(list_back(&$1->pipes), 
134
                              struct esh_pipeline, elem);
135
            last->bg_job = true;
136
 
137
            list_push_back(&$$->pipes, &$3->elem);
138
        }
139
 
140
pipeline: command {
141
            struct esh_command * pcmd = make_esh_command(&$1);
142
            if (pcmd == NULL) { p_error(INVNUL); YYABORT; }
143
            $$ = esh_pipeline_create(pcmd);
144
		}
145
|		pipeline '|' command {
146
		    /* Error: 'ls >x | wc' */
147
            struct esh_command * last;
148
            last = list_entry(list_back(&$1->commands), 
149
                              struct esh_command, elem);
150
		    if (last->iored_output) { p_error(AMBOUT); YYABORT; }
151
 
152
		    /* Error: 'ls | <x wc' */
153
		    if ($3.iored_input) { p_error(AMBINP); YYABORT; }
154
 
155
            struct esh_command * pcmd = make_esh_command(&$3);
156
            if (pcmd == NULL) { p_error(INVNUL); YYABORT; }
157
 
158
            list_push_back(&$1->commands, &pcmd->elem);
159
            pcmd->pipeline = $1;
160
            $$ = $1;
161
		}
162
|		'|' error 	   { p_error(INVNUL); YYABORT; }
163
|		pipeline '|' error { p_error(INVNUL); YYABORT; }
164
 
165
command:   WORD { 
166
            init_cmd(&$$, $1, NULL, NULL, false);
167
        }
168
|		input   
169
|		output
170
|		command WORD {
171
            $$ = $1;
172
            obstack_ptr_grow(&$$.words, $2);
173
		}
174
|		command input {
175
            obstack_free(&$2.words, NULL);
176
            /* Error: ambiguous redirect 'a <b <c' */
177
            if($1.iored_input)   { p_error(AMBINP); YYABORT; }
178
            $$ = $1; 
179
            $$.iored_input = $2.iored_input;
180
		}
181
|		command output {
182
            obstack_free(&$2.words, NULL);
183
            /* Error: ambiguous redirect 'a >b >c' */
184
            if ($1.iored_output) { p_error(AMBOUT); YYABORT; }
185
            $$ = $1; 
186
            $$.iored_output = $2.iored_output;
187
            $$.append_to_output = $2.append_to_output;
188
		}
189
 
190
input:	'<' WORD { 
191
            init_cmd(&$$, NULL, $2, NULL, false);
192
        }
193
|		'<' error	  { p_error(MISRED); YYABORT; }
194
 
195
output:	'>' WORD { 
196
            init_cmd(&$$, NULL, NULL, $2, false);
197
        }
198
|		GREATER_GREATER WORD { 
199
            init_cmd(&$$, NULL, NULL, $2, true);
200
        }
201
		/* Error: missing redirect */
202
|		'>' error 	  { p_error(MISRED); YYABORT; }
203
|		GREATER_GREATER error { p_error(MISRED); YYABORT; }
204
 
205
%%
206
static char * inputline;    /* currently processed input line */
207
#define YY_INPUT(buf,result,max_size) \
208
    { \
209
        result = *inputline ? (buf[0] = *inputline++, 1) : YY_NULL; \
210
    }
211
 
212
#define YY_NO_UNPUT
213
#define YY_NO_INPUT
214
#include "lex.yy.c"
215
 
216
static void
217
p_error(char *msg) 
218
{ 
219
    /* print error */
220
    fprintf(stderr, "%s\n", msg); 
221
}
222
 
223
extern int yyparse (void);
224
 
225
/* do not use default error handling since errors are handled above. */
226
void 
227
yyerror(const char *msg) { }
228
 
229
static struct esh_command_line * commandline;
230
static void cmdline_complete(struct esh_command_line *cline)
231
{
232
    commandline = cline;
233
}
234
 
235
/* 
236
 * parse a commandline.
237
 */
238
struct esh_command_line *
239
esh_parse_command_line(char * line)
240
{
241
    inputline = line;
242
    commandline = NULL;
243
 
244
    int error = yyparse();
245
 
246
    return error ? NULL : commandline;
247
}