#include "../syntax.h" #include static bool parse_cmdcall_arg(struct parse_ctx *ctx, struct ast_node **out) { if (ctx->p_status != BSHELL_SUCCESS) { return false; } struct lex_token *tok = peek_token(ctx); if (!tok) { return false; } struct ast_node *arg = NULL; switch (tok->tok_type) { case TOK_WORD: { struct word_ast_node *n = (struct word_ast_node *)ast_node_create(AST_WORD); if (!n) { ctx->p_status = BSHELL_ERR_NO_MEMORY; return false; } n->n_value = claim_token(ctx); *out = (struct ast_node *)n; return true; } #if 0 case TOK_FLAG: { struct word_ast_node *n = (struct word_ast_node *)ast_node_create(AST_WORD); if (!n) { ctx->p_status = BSHELL_ERR_NO_MEMORY; return false; } n->n_value = claim_token(ctx); *out = (struct ast_node *)n; return true; } #endif case TOK_VAR: { struct var_ast_node *n = (struct var_ast_node *)ast_node_create(AST_VAR); if (!n) { ctx->p_status = BSHELL_ERR_NO_MEMORY; return false; } n->n_ident = claim_token(ctx); *out = (struct ast_node *)n; return true; } case TOK_VAR_SPLAT: { struct var_splat_ast_node *n = (struct var_splat_ast_node *)ast_node_create( AST_VAR_SPLAT); if (!n) { ctx->p_status = BSHELL_ERR_NO_MEMORY; return false; } n->n_ident = claim_token(ctx); *out = (struct ast_node *)n; return true; } case TOK_STRING: { struct string_ast_node *n = (struct string_ast_node *)ast_node_create(AST_STRING); if (!n) { ctx->p_status = BSHELL_ERR_NO_MEMORY; return false; } n->n_value = claim_token(ctx); *out = (struct ast_node *)n; return true; } default: return false; } return true; } static bool parse_redirect_to_fd( struct parse_ctx *ctx, unsigned int in_fd, bool append, struct ast_node **out) { if (ctx->p_status != BSHELL_SUCCESS) { return false; } struct redirection_ast_node *redirect = (struct redirection_ast_node *)ast_node_create( AST_REDIRECTION); redirect->n_in = in_fd; redirect->n_append = append; if (!parse_symbol(ctx, SYM_AMPERSAND)) { ast_node_destroy((struct ast_node *)redirect); return false; } struct lex_token *out_tok = NULL; struct ast_node *out_expr = NULL; long long out_fd = -1; if (peek_word(ctx, &out_tok)) { const char *s = out_tok->tok_str; char *ep; out_fd = strtoll(s, &ep, 10); if (*ep == '\0') { discard_token(ctx); out_tok = NULL; } else { out_fd = -1; } } else if (!parse_cmdcall_arg(ctx, &out_expr)) { return false; } redirect->n_out_is_fd = (out_fd >= 0) || out_expr; redirect->n_out_is_expr = out_expr != NULL; redirect->n_out = (unsigned int)out_fd; redirect->n_out_path_expr = out_expr; if (out_tok) { redirect->n_out_tok = claim_token(ctx); redirect->n_out_path = out_tok->tok_str; } *out = (struct ast_node *)redirect; return true; } static bool parse_redirect_to_file_squashed( struct parse_ctx *ctx, unsigned int in_fd, bool append, const char *str, struct ast_node **out) { if (ctx->p_status != BSHELL_SUCCESS) { return false; } struct lex_token *tok = peek_token(ctx); if (*str == '\0') { return false; } struct redirection_ast_node *redirect = (struct redirection_ast_node *)ast_node_create( AST_REDIRECTION); redirect->n_in = in_fd; redirect->n_append = append; redirect->n_out_is_fd = false; redirect->n_out_is_expr = false; redirect->n_out_path = str; redirect->n_out_tok = claim_token(ctx); *out = (struct ast_node *)redirect; return true; } static bool parse_redirect_to_file_separate( struct parse_ctx *ctx, unsigned int in_fd, bool append, struct ast_node **out) { if (ctx->p_status != BSHELL_SUCCESS) { return false; } struct ast_node *out_path = NULL; if (!parse_cmdcall_arg(ctx, &out_path)) { ctx->p_status = BSHELL_ERR_BAD_SYNTAX; return false; } struct redirection_ast_node *redirect = (struct redirection_ast_node *)ast_node_create( AST_REDIRECTION); redirect->n_in = in_fd; redirect->n_append = append; redirect->n_out_is_fd = false; redirect->n_out_is_expr = true; redirect->n_out_path_expr = out_path; *out = (struct ast_node *)redirect; return true; } bool parse_redirect(struct parse_ctx *ctx, struct ast_node **out) { struct lex_token *tok = peek_token(ctx); if (!tok || tok->tok_type != TOK_WORD) { return false; } unsigned int in_fd = 1; const char *str = tok->tok_str; bool append = false; if (fx_wchar_is_number(*str)) { in_fd = *str - '0'; str++; } if (*str != '>') { return false; } str++; if (*str == '>') { append = true; str++; } if (*str != '\0') { return parse_redirect_to_file_squashed( ctx, in_fd, append, str, out); } discard_token(ctx); if (parse_redirect_to_fd(ctx, in_fd, append, out)) { return true; } if (parse_redirect_to_file_separate(ctx, in_fd, append, out)) { return true; } return false; } static bool peek_cmdcall_item(struct parse_ctx *ctx, bool unrestricted) { /* each token type falls into one of three categories: * - cmdcall item: the token can be used as part of a command call. the * token indicates the start of a command call. * - NOT a cmdcall item: the token cannot be used as part of a command * call, usually because it as a cmdcall operator like | or &. * encountering one of these tokens ends the cmdcall currently being * parsed. * - RESTRICTED cmdcall item: the token can be used as part of a * command, but will not be considered the start of a cmdcall. to run * a command with this token as its name, the call operator must be * used. */ switch (peek_token_type(ctx)) { case TOK_KEYWORD: case TOK_INT: case TOK_DOUBLE: case TOK_VAR: case TOK_VAR_SPLAT: case TOK_STRING: case TOK_STR_START: return unrestricted; case TOK_SYMBOL: switch (peek_unknown_symbol(ctx)) { case SYM_PLUS: case SYM_HYPHEN: return unrestricted; case SYM_PIPE: case SYM_AMPERSAND: case SYM_SEMICOLON: return false; default: return true; } case TOK_NONE: case TOK_LINEFEED: return false; default: return true; } } bool parse_cmdcall(struct parse_ctx *ctx, struct ast_node **out) { struct cmdcall_ast_node *node = (struct cmdcall_ast_node *)ast_node_create(AST_CMDCALL); if (!node) { ctx->p_status = BSHELL_ERR_NO_MEMORY; return false; } struct ast_node *child = NULL; bool unrestricted = false; bool ok = true; bool stop = false; if (parse_symbol(ctx, SYM_AMPERSAND)) { unrestricted = true; } if (!peek_cmdcall_item(ctx, unrestricted)) { return false; } struct lex_token *tok = peek_token(ctx); if (!tok) { return false; } if (!parse_cmdcall_arg(ctx, &child)) { return false; } fx_queue_push_back(&node->n_args, &child->n_entry); while (ok && !stop) { if (!peek_cmdcall_item(ctx, true)) { break; } struct lex_token *tok = peek_token(ctx); if (!tok) { break; } if (parse_redirect(ctx, &child)) { fx_queue_push_back(&node->n_redirect, &child->n_entry); } else if (parse_cmdcall_arg(ctx, &child)) { fx_queue_push_back(&node->n_args, &child->n_entry); } else { ctx->p_status = BSHELL_ERR_BAD_SYNTAX; ok = false; break; } } if (!ok) { ast_node_destroy((struct ast_node *)node); node = NULL; } *out = (struct ast_node *)node; return ok; } bool peek_command(struct parse_ctx *ctx) { if (peek_symbol(ctx, SYM_AMPERSAND)) { return true; } return peek_cmdcall_item(ctx, false); } bool parse_command(struct parse_ctx *ctx, struct ast_node **out) { struct ast_node *cmdcall = NULL; if (!parse_cmdcall(ctx, &cmdcall)) { return false; } struct pipeline_ast_node *pipeline = NULL; while (1) { if (parse_symbol(ctx, SYM_SEMICOLON) || parse_linefeed(ctx)) { break; } if (!parse_symbol(ctx, SYM_PIPE)) { break; } if (!pipeline) { pipeline = (struct pipeline_ast_node *)ast_node_create( AST_PIPELINE); if (!pipeline) { ctx->p_status = BSHELL_ERR_NO_MEMORY; ast_node_destroy(cmdcall); return false; } fx_queue_push_back( &pipeline->n_stages, &cmdcall->n_entry); } if (!parse_cmdcall(ctx, &cmdcall)) { ctx->p_status = BSHELL_ERR_BAD_SYNTAX; return false; } fx_queue_push_back(&pipeline->n_stages, &cmdcall->n_entry); } if (pipeline) { *out = (struct ast_node *)pipeline; } else { *out = cmdcall; } return true; }