Commit d0d29bf1 authored by Kevin Wolf's avatar Kevin Wolf
Browse files

shell: Erst in Syntaxtree parsen, dann ausführen


* shell: Dieser Commit spendiert der Shell Ansätze von einem richtigen
  Parser anstatt nur Dateiumleitungen aus der Tokensuppe herauszufischen
  und den Rest als Argumente zu nehmen.
Signed-off-by: Kevin Wolf's avatarKevin Wolf <kevin@tyndur.org>
parent fc447b48
No related merge requests found
Showing with 420 additions and 147 deletions
+420 -147
......@@ -187,6 +187,71 @@ void shell_read_command(void)
free(cwd);
}
#define parser_new(var) \
({ int ret; var = calloc(1, sizeof(*var)); \
if (!var) { fprintf(stderr, "sh: %s\n", strerror(ENOMEM)); \
ret = -ENOMEM; } \
else { ret = 0; } \
ret; })
struct parser_str_list_entry {
char* value;
struct parser_str_list_entry* next;
};
struct parser_str_list {
struct parser_str_list_entry* head;
struct parser_str_list_entry** plast;
};
struct parser_str_list* parser_str_list_create(void)
{
struct parser_str_list* list;
if (parser_new(list) < 0) {
return NULL;
}
*list = (struct parser_str_list) {
.head = NULL,
.plast = &list->head,
};
return list;
}
int parser_str_list_add(struct parser_str_list* list, char* str)
{
struct parser_str_list_entry* entry;
int ret;
ret = parser_new(entry);
if (ret < 0) {
return ret;
}
*entry = (struct parser_str_list_entry) {
.value = str,
.next = NULL,
};
*list->plast = entry;
list->plast = &entry->next;
return 0;
}
struct parser_node_redir {
/* Parser-Ausgabe */
int fd;
int flags;
char* filename;
struct parser_node_redir* next;
/* Ausführung */
lio_stream_t orig_fd;
};
static bool is_redirection(const char* word, int* stdio_idx, int* flags)
{
*stdio_idx = -1;
......@@ -226,174 +291,403 @@ static int get_stdio_fd(int index)
return -1;
}
int handle_command(char* buf)
int pnt_redirection_parse(struct tokenizer* tok, struct token* t,
struct parser_node_redir** result)
{
char* args;
char** argv = NULL;
int num_tokens, argc;
int i;
int in_idx, out_idx;
struct token* tokens;
int stdio_orig[3] = { 0 };
int ret;
struct parser_node_redir* node;
int fd, flags, ret;
num_tokens = tokenize_cmdline(buf, NULL, NULL);
if (num_tokens < 0) {
if (!is_redirection(t->value, &fd, &flags)) {
return 0;
}
tokens = calloc(num_tokens, sizeof(*tokens));
if (num_tokens && tokens == NULL) {
free(argv);
fprintf(stderr, "Interner Fehler: Kein Speicher für tokens\n");
return 0;
ret = parser_new(node);
if (ret < 0) {
goto done;
}
*node = (struct parser_node_redir) {
.fd = fd,
.flags = flags,
};
free(t->value);
ret = tokenizer_get(tok, t);
if (ret < 0 || t->type != TT_WORD) {
fprintf(stderr, "Umleitungsziel erwartet\n");
ret = -EINVAL;
goto done;
}
tokenize_cmdline(buf, &args, tokens);
node->filename = t->value;
/* Befehlszeile parsen */
argc = 0;
for (i = 0; i < num_tokens; i++) {
switch (tokens[i].type) {
case TT_OPERATOR:
{
int stdio_idx, flags;
const char* path;
lio_resource_t r;
lio_stream_t fd;
int stdio_fd;
if (!is_redirection(tokens[i].value, &stdio_idx, &flags)) {
fprintf(stderr, "Unbekannter Operator '%s'\n",
tokens[i].value);
goto found;
}
ret = tokenizer_get(tok, t);
done:
if (ret < 0) {
free(node);
} else {
*result = node;
ret = 1;
}
return ret;
}
if (++i >= num_tokens || tokens[i].type != TT_WORD) {
fprintf(stderr, "Umleitungsziel erwartet\n");
goto found;
}
int pnt_redirection_pre_execute(struct parser_node_redir* node)
{
const char* path = node->filename;
lio_resource_t r;
lio_stream_t to_replace;
lio_stream_t fd;
int ret;
path = tokens[i].value;
r = lio_resource(path, 1);
if (r < 0 && (flags & LIO_WRITE)) {
lio_resource_t parent;
char* dirname = io_split_dirname(path);
char* filename = io_split_filename(path);
r = lio_resource(path, 1);
if (r < 0 && (node->flags & LIO_WRITE)) {
lio_resource_t parent;
char* dirname = io_split_dirname(path);
char* filename = io_split_filename(path);
if ((parent = lio_resource(dirname, 1)) > 0) {
r = lio_mkfile(parent, filename);
}
if ((parent = lio_resource(dirname, 1)) > 0) {
r = lio_mkfile(parent, filename);
}
free(dirname);
free(filename);
}
if (r < 0) {
fprintf(stderr, "Kann '%s' nicht öffnen: %s\n",
path, strerror(-r));
}
free(dirname);
free(filename);
}
if (r < 0) {
fprintf(stderr, "Kann '%s' nicht öffnen: %s\n",
path, strerror(-r));
return r;
}
fd = lio_open(r, flags);
if (fd < 0) {
fprintf(stderr, "Kann '%s' nicht öffnen: %s\n",
path, strerror(-fd));
goto found;
}
fd = lio_open(r, node->flags);
if (fd < 0) {
fprintf(stderr, "Kann '%s' nicht öffnen: %s\n",
path, strerror(-fd));
return fd;
}
to_replace = get_stdio_fd(node->fd);
node->orig_fd = lio_dup(to_replace, -1);
if (node->fd == 1) {
fflush(stdout);
}
ret = lio_dup(fd, to_replace);
lio_close(fd);
if (ret < 0) {
fprintf(stderr,
TMS(fail_dup, "lio_dup() fehlgeschlagen: %s\n"),
strerror(-ret));
return ret;
}
return 0;
}
int pnt_redirection_post_execute(struct parser_node_redir* node)
{
lio_stream_t to_replace;
int ret;
to_replace = get_stdio_fd(node->fd);
stdio_fd = get_stdio_fd(stdio_idx);
if (!stdio_orig[stdio_idx]) {
stdio_orig[stdio_idx] = lio_dup(stdio_fd, -1);
if (node->fd == 1) {
fflush(stdout);
}
ret = lio_dup(node->orig_fd, to_replace);
if (ret < 0) {
fprintf(stderr, "lio_dup() fehlgeschlagen: %s\n",
strerror(-ret));
}
lio_close(node->orig_fd);
return 0;
}
void pnt_redirection_free(struct parser_node_redir* node)
{
free(node->filename);
free(node);
}
struct parser_node_cmd {
struct parser_str_list* args;
struct parser_node_redir* redir;
struct parser_node_cmd* next;
};
int pnt_command_parse(struct tokenizer* tok, struct token* t,
struct parser_node_cmd** result)
{
struct parser_node_cmd* node;
struct parser_node_redir** redir_pnext;
bool matched = false;
int ret;
ret = parser_new(node);
if (ret < 0) {
goto done;
}
*node = (struct parser_node_cmd) {
.args = parser_str_list_create(),
.redir = NULL,
};
redir_pnext = &node->redir;
if (!node->args) {
ret = -ENOMEM;
goto done;
}
while (ret == 0) {
switch (t->type) {
case TT_WORD:
{
wordexp_t we;
int i;
ret = wordexp(t->value, &we, 0);
if (ret != 0) {
fprintf(stderr, TMS(parse_error, "Parserfehler in '%s'\n"),
t->value);
ret = -EINVAL;
goto done;
}
fflush(stdout);
ret = lio_dup(fd, stdio_fd);
lio_close(fd);
if (ret < 0) {
fprintf(stderr,
TMS(fail_dup, "lio_dup() fehlgeschlagen: %s\n"),
strerror(-ret));
goto found;
for (i = 0; i < we.we_wordc; i++) {
ret = parser_str_list_add(node->args, strdup(we.we_wordv[i]));
if (ret < 0) {
wordfree(&we);
goto done;
}
}
wordfree(&we);
free(t->value);
ret = tokenizer_get(tok, t);
break;
}
case TT_WORD:
ret = wordexp(tokens[i].value, &tokens[i].we, 0);
if (ret < 0) {
fprintf(stderr, TMS(parse_error, "Parserfehler in '%s'\n"),
tokens[i].value);
goto found;
case TT_OPERATOR:
ret = pnt_redirection_parse(tok, t, redir_pnext);
if (ret == 1) {
redir_pnext = &(*redir_pnext)->next;
} else {
goto done;
}
tokens[i].is_argv = true;
argc += tokens[i].we.we_wordc;
break;
default:
abort();
goto done;
}
if (ret >= 0) {
matched = true;
}
}
ret = 0;
done:
if (ret < 0) {
free(node);
} else {
*result = node;
ret = matched;
}
return ret;
}
int pnt_command_execute(struct parser_node_cmd* node)
{
int argc;
struct parser_str_list_entry* p;
struct parser_node_redir* r;
char** argv = NULL;
int i;
argc = 0;
for (p = node->args->head; p; p = p->next) {
argc++;
}
/* Leerzeilen ignorieren */
if (argc == 0) {
goto found;
return 0;
}
/* argv befüllen */
argv = calloc(argc + 1, sizeof(*argv));
if (argv == NULL) {
fprintf(stderr, "Interner Fehler: Kein Speicher für argv\n");
fprintf(stderr, "Interner Fehler: Kein Speicher fuer argv\n");
return 0;
}
for (in_idx = out_idx = 0; in_idx < num_tokens; in_idx++) {
if (tokens[in_idx].is_argv) {
int j;
for (j = 0; j < tokens[in_idx].we.we_wordc; j++) {
argv[out_idx++] = tokens[in_idx].we.we_wordv[j];
}
}
for (i = 0, p = node->args->head; i < argc; i++, p = p->next) {
argv[i] = p->value;
}
/* Passenden Befehl suchen und ausfuehren */
for (i = 0; shell_commands[i].handler != NULL; i++) {
if ((shell_commands[i].name == NULL) ||
!strcmp(shell_commands[i].name, argv[0]))
{
shell_commands[i].handler(argc, argv);
for (i = 0; shell_commands[i].handler!= NULL; i++) {
if ((shell_commands[i].name == NULL) || !strcmp(shell_commands[i].name, argv[0])) {
goto found;
}
}
fprintf(stderr, "Interner Fehler: Kein Befehl gefunden\n");
return 0;
goto out;
/* Aufraeumen */
found:
for (i = 0; i < num_tokens; i++) {
if (tokens[i].is_argv) {
wordfree(&tokens[i].we);
for (r = node->redir; r; r = r->next) {
int ret = pnt_redirection_pre_execute(r);
if (ret < 0) {
struct parser_node_redir* s;
for (s = node->redir; s != r; s = s->next) {
pnt_redirection_post_execute(s);
}
goto out;
}
}
free(tokens);
free(args);
shell_commands[i].handler(argc, argv);
for (r = node->redir; r; r = r->next) {
pnt_redirection_post_execute(r);
}
out:
free(argv);
return 0;
}
void pnt_command_free(struct parser_node_cmd* node)
{
struct parser_str_list_entry* p;
struct parser_str_list_entry* next_p;
struct parser_node_redir* r;
struct parser_node_redir* next_r;
for (p = node->args->head; p; p = next_p) {
next_p = p->next;
free(p->value);
free(p);
}
free(node->args);
/* Ausgabeumleitung rückgängig machen */
for (i = 0; i < 3; i++) {
if (stdio_orig[i]) {
int stdio_fd = get_stdio_fd(i);
for (r = node->redir; r; r = next_r) {
next_r = r->next;
pnt_redirection_free(r);
}
fflush(stdout);
free(node);
}
ret = lio_dup(stdio_orig[i], stdio_fd);
if (ret < 0) {
fprintf(stderr, "lio_dup() fehlgeschlagen: %s\n",
strerror(-ret));
}
struct parser_node_pipeline {
struct parser_node_cmd* cmd;
};
int pnt_pipeline_parse(struct tokenizer* tok, struct token* t,
struct parser_node_pipeline** result)
{
struct parser_node_pipeline* node;
struct parser_node_cmd** cmd_pnext;
bool matched = false;
int ret;
ret = parser_new(node);
if (ret < 0) {
goto done;
}
cmd_pnext = &node->cmd;
ret = pnt_command_parse(tok, t, cmd_pnext);
if (ret <= 0) {
goto done;
}
matched = true;
cmd_pnext = &(*cmd_pnext)->next;
while (t->type == TT_OPERATOR && !strcmp(t->value, "|")) {
free(t->value);
ret = tokenizer_get(tok, t);
if (ret < 0) {
goto done;
}
ret = pnt_command_parse(tok, t, cmd_pnext);
if (ret == 0) {
fprintf(stderr, "Befehl erwartet\n");
ret = -EINVAL;
}
if (ret < 0) {
goto done;
}
cmd_pnext = &(*cmd_pnext)->next;
}
return 0;
done:
if (ret < 0) {
free(node);
} else {
*result = node;
ret = matched;
}
return ret;
}
int pnt_pipeline_execute(struct parser_node_pipeline* node)
{
struct parser_node_cmd* p = node->cmd;
if (p->next) {
fprintf(stderr, "Pipes sind derzeit nicht unterstützt\n");
return -EINVAL;
}
return pnt_command_execute(p);
}
void pnt_pipeline_free(struct parser_node_pipeline* node)
{
struct parser_node_cmd* p;
struct parser_node_cmd* next_p;
for (p = node->cmd; p; p = next_p) {
next_p = p->next;
pnt_command_free(p);
}
free(node);
}
int handle_command(char* buf)
{
struct tokenizer* tok;
struct token t;
struct parser_node_pipeline* root;
int ret, result = 0;
tok = tokenizer_create(buf);
if (tok == NULL) {
fprintf(stderr, "Kein freier Speicher\n");
return 0;
}
ret = tokenizer_get(tok, &t);
if (ret < 0) {
goto fail;
}
ret = pnt_pipeline_parse(tok, &t, &root);
if (ret < 0) {
goto fail_parser;
}
if (t.type != TT_END_OF_INPUT) {
free(t.value);
}
result = pnt_pipeline_execute(root);
fail_parser:
pnt_pipeline_free(root);
fail:
tokenizer_free(tok);
return result;
}
/**
......
......@@ -43,6 +43,7 @@
/* Tokenizer */
enum token_type {
TT_END_OF_INPUT,
TT_WORD,
TT_OPERATOR,
};
......@@ -60,8 +61,6 @@ struct tokenizer* tokenizer_create(const char *str);
int tokenizer_get(struct tokenizer* tok, struct token* token);
void tokenizer_free(struct tokenizer* tok);
int tokenize_cmdline(const char* str, char** output, struct token* tokens);
/** Array mit den Befehlen */
typedef struct shell_command_t {
const char* name;
......
......@@ -96,7 +96,11 @@ int tokenizer_get(struct tokenizer* tok, struct token* token)
enum token_type type = TT_WORD;
if (!str || !*str) {
return -1;
*token = (struct token) {
.type = TT_END_OF_INPUT,
.value = NULL,
};
return 0;
}
restart:
......@@ -216,12 +220,10 @@ restart:
abort();
accept:
if (token) {
*token = (struct token) {
.type = type,
.value = strndup(tok->input, str - tok->input),
};
}
*token = (struct token) {
.type = type,
.value = strndup(tok->input, str - tok->input),
};
tok->input = str;
return 0;
......@@ -231,25 +233,3 @@ void tokenizer_free(struct tokenizer* tok)
{
free(tok);
}
int tokenize_cmdline(const char* str, char** output, struct token* tokens)
{
struct tokenizer* tok;
int num_tokens = 0;
if (output) {
*output = NULL;
}
tok = tokenizer_create(str);
while (tokenizer_get(tok, tokens) == 0) {
num_tokens++;
if (tokens) {
tokens++;
}
}
tokenizer_free(tok);
return num_tokens;
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment