diff options
Diffstat (limited to '')
-rw-r--r-- | parser.c | 271 |
1 files changed, 271 insertions, 0 deletions
diff --git a/parser.c b/parser.c new file mode 100644 index 0000000..c3da716 --- /dev/null +++ b/parser.c @@ -0,0 +1,271 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +void +push_end_of_file(struct parser_context *ctx) +{ + push_semicolon(ctx, 1); + if (ctx->parser_state->parent || ctx->parser_state->ncommands) + ctx->premature_end_of_file = 1; +} + + +void +push_whitespace(struct parser_context *ctx, int strict) +{ + if (ctx->parser_state->need_right_hand_side) { + if (strict) + eprintf("premature end of command\n"); + return; + } + + if (ctx->parser_state->current_argument) { + ctx->parser_state->arguments = erealloc(ctx->parser_state->arguments, + (ctx->parser_state->narguments + 1) * + sizeof(*ctx->parser_state->arguments)); + ctx->parser_state->arguments[ctx->parser_state->narguments++] = ctx->parser_state->current_argument; + ctx->parser_state->current_argument = NULL; + ctx->parser_state->current_argument_end = NULL; + } +} + + +static void +push_command_terminal(struct parser_context *ctx, enum command_terminal terminal) +{ + struct command *new_command; + + push_whitespace(ctx, 1); + + ctx->parser_state->commands = erealloc(ctx->parser_state->commands, + (ctx->parser_state->ncommands + 1) * + sizeof(*ctx->parser_state->commands)); + new_command = ecalloc(1, sizeof(*new_command)); + ctx->parser_state->commands[ctx->parser_state->ncommands++] = new_command; + new_command->terminal = terminal; + new_command->arguments = ctx->parser_state->arguments; + new_command->narguments = ctx->parser_state->narguments; + new_command->redirections = ctx->parser_state->redirections; + new_command->nredirections = ctx->parser_state->nredirections; + ctx->parser_state->arguments = NULL; + ctx->parser_state->narguments = 0; + ctx->parser_state->redirections = NULL; + ctx->parser_state->nredirections = 0; + + if (!ctx->parser_state->parent) { + if (terminal == DOUBLE_SEMICOLON || terminal == SEMICOLON || terminal == AMPERSAND) { + /* TODO unless in a special construct such as while, case, for, if, or {, run and clear + * also require that any here-document is specified (count them and run when given); + * if terminal == AMPERSAND: perform </dev/null first, and reset exist status to 0 + */ + } + } +} + + +void +push_semicolon(struct parser_context *ctx, int maybe) +{ + if (!maybe || ctx->parser_state->narguments) + push_command_terminal(ctx, SEMICOLON); +} + + +static void +push_new_argument_part(struct parser_context *ctx, enum argument_type type) +{ + struct argument *new_part; + + new_part = ecalloc(1, sizeof(*new_part)); + new_part->type = type; + new_part->line_number = ctx->tokeniser_line_number; + + if (ctx->parser_state->current_argument_end) { + ctx->parser_state->current_argument_end->next_part = new_part; + ctx->parser_state->current_argument_end = new_part; + } else { + ctx->parser_state->current_argument = new_part; + ctx->parser_state->current_argument_end = new_part; + } +} + + +static void +push_redirection(struct parser_context *ctx, enum redirection_type type) +{ + struct redirection *new_redirection; + struct argument *new_argument; + struct here_document *new_here_document; + + new_redirection = ecalloc(1, sizeof(*new_redirection)); + new_redirection->type = type; + + ctx->parser_state->redirections = erealloc(ctx->parser_state->redirections, + (ctx->parser_state->nredirections + 1) * + sizeof(*ctx->parser_state->redirections)); + ctx->parser_state->redirections[ctx->parser_state->nredirections++] = new_redirection; + + if (ctx->parser_state->current_argument) { + if (ctx->parser_state->current_argument->type == REDIRECTION || + ctx->parser_state->current_argument_end->type == QUOTED || + ctx->parser_state->current_argument_end->type == QUOTE_EXPRESSION || + type == REDIRECT_OUTPUT_AND_STDERR || + type == REDIRECT_OUTPUT_AND_STDERR_APPEND || + type == REDIRECT_OUTPUT_AND_STDERR_CLOBBER || + type == REDIRECT_OUTPUT_AND_STDERR_TO_FD) { + push_whitespace(ctx, 1); + } else { + new_redirection->left_hand_side = ctx->parser_state->current_argument; + } + } + + new_argument = ecalloc(1, sizeof(*new_argument)); + new_argument->type = REDIRECTION; + new_argument->line_number = ctx->tokeniser_line_number; + ctx->parser_state->current_argument = new_argument; + + if (type == HERE_DOCUMENT || type == HERE_DOCUMENT_INDENTED) { + new_here_document = emalloc(sizeof(*new_here_document)); + new_here_document->redirection = new_redirection; + new_here_document->argument = new_argument; + new_here_document->next = NULL; + *ctx->here_documents_next = new_here_document; + ctx->here_documents_next = &new_here_document->next; + } + + ctx->parser_state->need_right_hand_side = 1; +} + + +static void +push_shell_io(struct parser_context *ctx, enum argument_type type, enum tokeniser_mode mode) +{ + push_mode(ctx, mode); + push_enter(ctx, type); +} + + +static void +push_function_mark(struct parser_context *ctx) +{ + push_whitespace(ctx, 1); + push_new_argument_part(ctx, FUNCTION_MARK); + push_whitespace(ctx, 1); +} + + +size_t +push_symbol(struct parser_context *ctx, char *token, size_t token_len) +{ +#define LIST_SYMBOLS(_)\ + _("<<<", push_redirection(ctx, HERE_STRING))\ + _("<<-", push_redirection(ctx, HERE_DOCUMENT_INDENTED))\ + _("<>(", push_shell_io(ctx, PROCESS_SUBSTITUTION_INPUT_OUTPUT, NORMAL_MODE))\ + _("<>|", push_command_terminal(ctx, SOCKET_PIPE))\ + _("<>&", push_redirection(ctx, REDIRECT_INPUT_OUTPUT_TO_FD))\ + _("&>>", push_redirection(ctx, REDIRECT_OUTPUT_AND_STDERR_APPEND))\ + _("&>&", push_redirection(ctx, REDIRECT_OUTPUT_AND_STDERR_TO_FD))\ + _("&>|", push_redirection(ctx, REDIRECT_OUTPUT_AND_STDERR_CLOBBER))\ + _("()", push_function_mark(ctx))\ + _("((", push_shell_io(ctx, ARITHMETIC_SUBSHELL, RRB_QUOTE_MODE))\ + _(";;", push_command_terminal(ctx, DOUBLE_SEMICOLON))\ + _("<(", push_shell_io(ctx, PROCESS_SUBSTITUTION_OUTPUT, NORMAL_MODE))\ + _("<<", push_redirection(ctx, HERE_DOCUMENT))\ + _("<>", push_redirection(ctx, REDIRECT_INPUT_OUTPUT))\ + _("<&", push_redirection(ctx, REDIRECT_INPUT_TO_FD))\ + _(">(", push_shell_io(ctx, PROCESS_SUBSTITUTION_INPUT, NORMAL_MODE))\ + _(">>", push_redirection(ctx, REDIRECT_OUTPUT_APPEND))\ + _(">&", push_redirection(ctx, REDIRECT_OUTPUT_TO_FD))\ + _(">|", push_redirection(ctx, REDIRECT_OUTPUT_CLOBBER))\ + _("||", push_command_terminal(ctx, OR))\ + _("|&", push_command_terminal(ctx, PIPE_AMPERSAND))\ + _("&&", push_command_terminal(ctx, AND))\ + _("&|", push_command_terminal(ctx, PIPE_AMPERSAND)) /* synonym for |& to match &> */\ + _("&>", push_redirection(ctx, REDIRECT_OUTPUT_AND_STDERR))\ + _("(", push_shell_io(ctx, SUBSHELL, NORMAL_MODE))\ + _(";", push_semicolon(ctx, 0))\ + _("<", push_redirection(ctx, REDIRECT_INPUT))\ + _(">", push_redirection(ctx, REDIRECT_OUTPUT))\ + _("|", push_command_terminal(ctx, PIPE))\ + _("&", push_command_terminal(ctx, AMPERSAND)) + +#define X(SYMBOL, ACTION)\ + if (token_len >= sizeof(SYMBOL) - 1 && !strncmp(token, SYMBOL, sizeof(SYMBOL) - 1)) {\ + ACTION;\ + return token_len;\ + } + LIST_SYMBOLS(X) +#undef X + + push_unquoted(ctx, token, 1); + return 1; +} + + +static void +push_text(struct parser_context *ctx, char *text, size_t text_len, enum argument_type type) +{ + struct argument *arg_part; + + ctx->parser_state->need_right_hand_side = 0; + + if (!ctx->parser_state->current_argument_end || + ctx->parser_state->current_argument_end->type != type || + ctx->parser_state->current_argument_end->line_number != ctx->tokeniser_line_number) + push_new_argument_part(ctx, type); + arg_part = ctx->parser_state->current_argument_end; + + arg_part->text = erealloc(arg_part->text, arg_part->length + text_len + 1); + memcpy(&arg_part->text[arg_part->length], text, text_len); + arg_part->length += text_len; + arg_part->text[arg_part->length] = '\0'; +} + + +void +push_quoted(struct parser_context *ctx, char *text, size_t text_len) +{ + push_text(ctx, text, text_len, QUOTED); +} + + +void +push_escaped(struct parser_context *ctx, char *text, size_t text_len) +{ + /* TODO resolve backslashes in text */ + push_text(ctx, text, text_len, QUOTED); +} + + +void +push_unquoted(struct parser_context *ctx, char *text, size_t text_len) +{ + push_text(ctx, text, text_len, UNQUOTED); +} + + +void +push_enter(struct parser_context *ctx, enum argument_type type) +{ + struct parser_state *new_state; + + ctx->parser_state->need_right_hand_side = 0; + push_new_argument_part(ctx, type); + + new_state = ecalloc(1, sizeof(*new_state)); + new_state->parent = ctx->parser_state; + ctx->parser_state->current_argument_end->child = new_state; + ctx->parser_state = new_state; +} + + +void +push_leave(struct parser_context *ctx) +{ + if (ctx->mode_stack->mode == NORMAL_MODE) + push_semicolon(ctx, 1); + /* TODO else if (ctx->mode_stack->mode == BQ_QUOTE_MODE), parse content */ + /* TODO validate subshell content */ + ctx->parser_state = ctx->parser_state->parent; +} |