From ab3da84e34301f2f7dfbedbaddbc96a3a5358e8b Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 1 Jul 2026 00:02:10 +0100 Subject: [PATCH] Handle aliases. --- cmd-invoke.c | 71 +++++++++++++++++++++++++------- cmd-parse.y | 109 ++++++++++++++++++++++++++++++++++++++++++++++++-- tmux-parser.h | 3 ++ 3 files changed, 164 insertions(+), 19 deletions(-) diff --git a/cmd-invoke.c b/cmd-invoke.c index 82368870a..e6a5e2691 100644 --- a/cmd-invoke.c +++ b/cmd-invoke.c @@ -27,6 +27,7 @@ /* One frame in the parse tree stack. */ struct cmd_invoke_frame { + struct cmd_parse_tree *tree; struct cmd_parse_node *node; struct cmd_parse_node *next; struct cmd_parse_node *end; @@ -36,6 +37,7 @@ struct cmd_invoke_frame { struct cmd_invoke_state { u_int references; struct cmd_parse_tree *tree; + struct cmd_parse_tree *current_tree; struct cmd_invoke_frame *stack; u_int nstack; @@ -48,8 +50,9 @@ struct cmd_invoke_state { }; static void cmd_invoke_push(struct cmd_invoke_state *, - struct cmd_parse_node *, struct cmd_parse_node *, - struct cmd_parse_node *); + struct cmd_parse_tree *, struct cmd_parse_node *, + struct cmd_parse_node *, struct cmd_parse_node *); +static struct cmd_parse_tree *cmd_invoke_tree(struct cmd_invoke_state *); static struct cmd_parse_node *cmd_invoke_next(struct cmd_invoke_state *); static void cmd_invoke_skip_sequence(struct cmd_invoke_state *); static int cmd_invoke_expand_string(struct cmdq_item *, @@ -60,23 +63,33 @@ static int cmd_invoke_assignment(struct cmdq_item *, static int cmd_invoke_if(struct cmdq_item *, struct cmd_invoke_state *, struct cmd_parse_node *); static struct cmd *cmd_invoke_build_command(struct cmdq_item *, - struct cmd_invoke_state *, struct cmd_parse_node *); + struct cmd_invoke_state *, struct cmd_parse_node *); static int cmd_invoke_read_only(struct cmdq_item *, struct cmd *); /* Push a node's child range onto the traversal stack. */ static void -cmd_invoke_push(struct cmd_invoke_state *is, struct cmd_parse_node *node, - struct cmd_parse_node *first, struct cmd_parse_node *end) +cmd_invoke_push(struct cmd_invoke_state *is, struct cmd_parse_tree *tree, + struct cmd_parse_node *node, struct cmd_parse_node *first, + struct cmd_parse_node *end) { u_int n = is->nstack + 1; is->stack = xreallocarray(is->stack, n, sizeof *is->stack); + is->stack[is->nstack].tree = cmd_parse_add_ref(tree); is->stack[is->nstack].node = node; is->stack[is->nstack].next = first; is->stack[is->nstack].end = end; is->nstack = n; } +static struct cmd_parse_tree * +cmd_invoke_tree(struct cmd_invoke_state *is) +{ + if (is->current_tree != NULL) + return (is->current_tree); + return (is->tree); +} + /* Return the next node to execute from the traversal stack. */ static struct cmd_parse_node * cmd_invoke_next(struct cmd_invoke_state *is) @@ -90,11 +103,13 @@ cmd_invoke_next(struct cmd_invoke_state *is) frame = &is->stack[is->nstack - 1]; if (frame->next != NULL && frame->next != frame->end) break; + cmd_parse_free(frame->tree); is->nstack--; } node = frame->next; frame->next = cmd_parse_node_next(node); + is->current_tree = frame->tree; return (node); } @@ -113,6 +128,8 @@ cmd_invoke_skip_sequence(struct cmd_invoke_state *is) type = cmd_parse_node_type(is->stack[i - 1].node); if (type == CMD_PARSE_SEQUENCE) { is->stack[i - 1].next = is->stack[i - 1].end; + while (is->nstack > i) + cmd_parse_free(is->stack[--is->nstack].tree); is->nstack = i; break; } @@ -274,6 +291,7 @@ static int cmd_invoke_if(struct cmdq_item *item, struct cmd_invoke_state *is, struct cmd_parse_node *node) { + struct cmd_parse_tree *tree = cmd_invoke_tree(is); struct cmd_parse_node *child, *first, *next; int r; @@ -285,7 +303,7 @@ cmd_invoke_if(struct cmdq_item *item, struct cmd_invoke_state *is, return (-1); if (r) { next = cmd_invoke_if_branch_end(first); - cmd_invoke_push(is, node, first, next); + cmd_invoke_push(is, tree, node, first, next); return (0); } @@ -299,13 +317,13 @@ cmd_invoke_if(struct cmdq_item *item, struct cmd_invoke_state *is, return (-1); if (r) { next = cmd_parse_node_next(next); - cmd_invoke_push(is, child, next, NULL); + cmd_invoke_push(is, tree, child, next, NULL); return (0); } break; case CMD_PARSE_ELSE: next = cmd_parse_node_first_child(child); - cmd_invoke_push(is, child, next, NULL); + cmd_invoke_push(is, tree, child, next, NULL); return (0); default: break; @@ -319,8 +337,9 @@ static void cmd_invoke_error(struct cmdq_item *item, struct cmd_invoke_state *is, struct cmd_parse_node *node, const char *cause) { - const char *file = cmd_parse_file(is->tree); - u_int line = cmd_parse_node_line(node); + struct cmd_parse_tree *tree = cmd_invoke_tree(is); + const char *file = cmd_parse_file(tree); + u_int line = cmd_parse_node_line(node); if (cmdq_get_client(item) != NULL) { cmdq_error(item, "%s", cause); @@ -338,7 +357,7 @@ static struct cmd * cmd_invoke_build_command(struct cmdq_item *item, struct cmd_invoke_state *is, struct cmd_parse_node *node) { - struct cmd_parse_tree *tree = is->tree; + struct cmd_parse_tree *tree = cmd_invoke_tree(is); struct cmd_parse_node *child; struct args_value *values = NULL; struct cmd *cmd; @@ -426,7 +445,7 @@ cmd_invoke_get(struct cmd_parse_tree *tree, struct cmdq_state *state, } first = cmd_parse_node_first_child(root); - cmd_invoke_push(is, root, first, NULL); + cmd_invoke_push(is, tree, root, first, NULL); item = cmdq_get_invoke(is, state); cmd_invoke_state_free(is); @@ -446,6 +465,7 @@ void cmd_invoke_state_free(struct cmd_invoke_state *is) { int i; + u_int j; if (is == NULL) return; @@ -455,6 +475,8 @@ cmd_invoke_state_free(struct cmd_invoke_state *is) for (i = 0; i < is->argc; i++) free(is->argv[i]); free(is->argv); + for (j = 0; j < is->nstack; j++) + cmd_parse_free(is->stack[j].tree); cmd_parse_free(is->tree); free(is->stack); @@ -473,10 +495,13 @@ cmd_invoke_result(struct cmd_invoke_state *is, enum cmd_retval retval) enum cmd_retval cmd_invoke_fire(struct cmdq_item *item, struct cmd_invoke_state *is) { - struct cmd_parse_node *node; + struct cmd_parse_node *node, *first; + struct cmd_parse_tree *tree, *alias; struct cmdq_item *new_item, *next; struct cmdq_state *state; struct cmd *cmd; + char *cause; + int r; if (is->have_last && is->last == CMD_RETURN_ERROR) cmd_invoke_skip_sequence(is); @@ -489,11 +514,12 @@ cmd_invoke_fire(struct cmdq_item *item, struct cmd_invoke_state *is) return (CMD_RETURN_NORMAL); cmd_parse_log_node(__func__, node); + tree = cmd_invoke_tree(is); switch (cmd_parse_node_type(node)) { case CMD_PARSE_ROOT: case CMD_PARSE_SEQUENCE: - cmd_invoke_push(is, node, - cmd_parse_node_first_child(node), NULL); + first = cmd_parse_node_first_child(node); + cmd_invoke_push(is, tree, node, first, NULL); break; case CMD_PARSE_ASSIGN: case CMD_PARSE_HIDDEN_ASSIGN: @@ -512,6 +538,21 @@ cmd_invoke_fire(struct cmdq_item *item, struct cmd_invoke_state *is) case CMD_PARSE_ELSE: break; case CMD_PARSE_COMMAND: + r = cmd_parse_expand_alias(tree, node, &alias, &cause); + if (r == 1) { + node = cmd_parse_root(alias); + first = cmd_parse_node_first_child(node); + cmd_invoke_push(is, alias, node, first, NULL); + cmd_parse_free(alias); + break; + } + if (r == -1) { + cmd_invoke_error(item, is, node, cause); + free(cause); + cmd_invoke_skip_sequence(is); + break; + } + cmd = cmd_invoke_build_command(item, is, node); if (cmd == NULL) { cmd_invoke_skip_sequence(is); diff --git a/cmd-parse.y b/cmd-parse.y index f6aae3ca9..bd3801ce8 100644 --- a/cmd-parse.y +++ b/cmd-parse.y @@ -714,6 +714,81 @@ cmd_parse_from_node(struct cmd_parse_tree *tree, struct cmd_parse_node *node) return (new); } +/* Find the last command node in tree order. */ +static struct cmd_parse_node * +cmd_parse_last_command(struct cmd_parse_node *node) +{ + struct cmd_parse_node *child, *last = NULL, *found; + + if (node->type == CMD_PARSE_COMMAND) + last = node; + TAILQ_FOREACH(child, &node->children, entry) { + found = cmd_parse_last_command(child); + if (found != NULL) + last = found; + } + return (last); +} + +/* + * If cmd names an alias, parse the alias text as command language and append + * the original arguments after the alias name to the last expanded command. + */ +int +cmd_parse_expand_alias(struct cmd_parse_tree *src, struct cmd_parse_node *cmd, + struct cmd_parse_tree **out, char **cause) +{ + struct cmd_parse_input pi; + struct cmd_parse_tree *tree; + struct cmd_parse_node *first, *node, *last, *loop, *copy; + char *alias; + + *out = NULL; + *cause = NULL; + + if (cmd->type != CMD_PARSE_COMMAND) + return (0); + if (cmd_parse_flags(src) & CMD_PARSE_NOALIAS) + return (0); + + first = TAILQ_FIRST(&cmd->children); + if (first == NULL) + return (0); + if (first->type != CMD_PARSE_STRING) + return (0); + node = TAILQ_FIRST(&first->children); + if (node == NULL || TAILQ_NEXT(node, entry) != NULL) + return (0); + if (node->type != CMD_PARSE_TEXT) + return (0); + + alias = cmd_get_alias(node->value); + if (alias == NULL) + return (0); + + memset(&pi, 0, sizeof pi); + pi.file = cmd_parse_file(src); + pi.line = cmd_parse_node_line(cmd); + pi.flags = cmd_parse_flags(src) | CMD_PARSE_NOALIAS; + + tree = cmd_parse_from_string(alias, &pi, cause); + free(alias); + if (tree == NULL) + return (-1); + + last = cmd_parse_last_command(cmd_parse_root(tree)); + if (last != NULL) { + loop = TAILQ_NEXT(first, entry); + while (loop != NULL) { + copy = cmd_parse_copy_node(loop); + TAILQ_INSERT_TAIL(&last->children, copy, entry); + loop = TAILQ_NEXT(loop, entry); + } + } + *out = tree; + return (1); +} + /* Build a string node with a single literal text child. */ static struct cmd_parse_node * cmd_parse_new_string_node(const char *s, u_int line) @@ -1274,16 +1349,40 @@ static int cmd_parse_command_any_have(struct cmd_parse_tree *tree, struct cmd_parse_node *node, int flag) { - struct cmd_parse_node *child; + struct cmd_parse_node *child, *first, *text; int flags = tree->flags; struct args_value *values = NULL; struct cmd *cmd; char *cause = NULL; + const char *file = tree->file; u_int count = 0; - int found = 0; + int found = 0, r; if (node->type != CMD_PARSE_COMMAND) return (0); + r = cmd_parse_expand_alias(tree, node, &tree, &cause); + if (r == 1) { + found = cmd_parse_any_have(tree, flag); + cmd_parse_free(tree); + return (found); + } + if (r == -1) { + free(cause); + return (-1); + } + first = TAILQ_FIRST(&node->children); + if (first != NULL && first->type == CMD_PARSE_STRING) { + text = TAILQ_FIRST(&first->children); + if (text != NULL && TAILQ_NEXT(text, entry) == NULL && + text->type == CMD_PARSE_TEXT) { + if (cmd_find(text->value, &cause) == NULL) { + free(cause); + return (-1); + } + free(cause); + cause = NULL; + } + } TAILQ_FOREACH(child, &node->children, entry) { values = xreallocarray(values, count + 1, sizeof *values); @@ -1308,7 +1407,9 @@ cmd_parse_command_any_have(struct cmd_parse_tree *tree, count++; } - cmd = cmd_parse(values, count, tree->file, node->line, flags, &cause); + if (file == NULL) + file = ""; + cmd = cmd_parse(values, count, file, node->line, flags, &cause); if (cmd == NULL) { free(cause); found = -1; @@ -1338,7 +1439,7 @@ cmd_parse_any_have(struct cmd_parse_tree *tree, int flag) if (node->type != CMD_PARSE_COMMAND) continue; r = cmd_parse_command_any_have(tree, node, flag); - if (r < 0) + if (r < 0 && !found) return (0); if (r) found = 1; diff --git a/tmux-parser.h b/tmux-parser.h index 416a50a53..de16fab3c 100644 --- a/tmux-parser.h +++ b/tmux-parser.h @@ -79,6 +79,9 @@ struct cmd_parse_tree *cmd_parse_from_string(const char *, struct cmd_parse_input *, char **); struct cmd_parse_tree *cmd_parse_from_node(struct cmd_parse_tree *, struct cmd_parse_node *); +int cmd_parse_expand_alias(struct cmd_parse_tree *, + struct cmd_parse_node *, struct cmd_parse_tree **, + char **); struct cmd_parse_tree *cmd_parse_from_arguments(struct args_value *, u_int); struct cmd_parse_tree *cmd_parse_add_ref(struct cmd_parse_tree *); int cmd_parse_any_have(struct cmd_parse_tree *, int);