Actually parse contents of {} as a command and then convert to a string
authornicm <nicm@openbsd.org>
Fri, 20 Aug 2021 09:06:26 +0000 (09:06 +0000)
committernicm <nicm@openbsd.org>
Fri, 20 Aug 2021 09:06:26 +0000 (09:06 +0000)
instead of just copying arguments around as strings.

usr.bin/tmux/cmd-parse.y

index e71fceb..bef99c1 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: cmd-parse.y,v 1.35 2021/08/20 06:30:57 nicm Exp $ */
+/* $OpenBSD: cmd-parse.y,v 1.36 2021/08/20 09:06:26 nicm Exp $ */
 
 /*
  * Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -60,9 +60,6 @@ struct cmd_parse_command {
        u_int                            line;
        struct cmd_parse_arguments       arguments;
 
-       int                              argc;
-       char                           **argv;
-
        TAILQ_ENTRY(cmd_parse_command)   entry;
 };
 TAILQ_HEAD(cmd_parse_commands, cmd_parse_command);
@@ -92,10 +89,10 @@ static char *cmd_parse_get_error(const char *, u_int, const char *);
 static void     cmd_parse_free_command(struct cmd_parse_command *);
 static struct cmd_parse_commands *cmd_parse_new_commands(void);
 static void     cmd_parse_free_commands(struct cmd_parse_commands *);
-static char    *cmd_parse_commands_to_string(struct cmd_parse_commands *);
-static void     cmd_parse_print_commands(struct cmd_parse_input *, u_int,
+static void     cmd_parse_build_commands(struct cmd_parse_commands *,
+                    struct cmd_parse_input *, struct cmd_parse_result *);
+static void     cmd_parse_print_commands(struct cmd_parse_input *,
                     struct cmd_list *);
-static void     cmd_parse_flatten_command(struct cmd_parse_command *);
 
 %}
 
@@ -587,19 +584,32 @@ cmd_parse_get_error(const char *file, u_int line, const char *error)
 }
 
 static void
-cmd_parse_print_commands(struct cmd_parse_input *pi, u_int line,
-    struct cmd_list *cmdlist)
+cmd_parse_print_commands(struct cmd_parse_input *pi, struct cmd_list *cmdlist)
 {
        char    *s;
 
-       if (pi->item != NULL && (pi->flags & CMD_PARSE_VERBOSE)) {
-               s = cmd_list_print(cmdlist, 0);
-               if (pi->file != NULL)
-                       cmdq_print(pi->item, "%s:%u: %s", pi->file, line, s);
-               else
-                       cmdq_print(pi->item, "%u: %s", line, s);
-               free(s);
+       if (pi->item == NULL || (~pi->flags & CMD_PARSE_VERBOSE))
+               return;
+       s = cmd_list_print(cmdlist, 0);
+       if (pi->file != NULL)
+               cmdq_print(pi->item, "%s:%u: %s", pi->file, pi->line, s);
+       else
+               cmdq_print(pi->item, "%u: %s", pi->line, s);
+       free(s);
+}
+
+static void
+cmd_parse_free_argument(struct cmd_parse_argument *arg)
+{
+       switch (arg->type) {
+       case CMD_PARSE_STRING:
+               free(arg->string);
+               break;
+       case CMD_PARSE_COMMANDS:
+               cmd_parse_free_commands(arg->commands);
+               break;
        }
+       free(arg);
 }
 
 static void
@@ -608,23 +618,14 @@ cmd_parse_free_arguments(struct cmd_parse_arguments *args)
        struct cmd_parse_argument       *arg, *arg1;
 
        TAILQ_FOREACH_SAFE(arg, args, entry, arg1) {
-               switch (arg->type) {
-               case CMD_PARSE_STRING:
-                       free(arg->string);
-                       break;
-               case CMD_PARSE_COMMANDS:
-                       cmd_parse_free_commands(arg->commands);
-                       break;
-               }
                TAILQ_REMOVE(args, arg, entry);
-               free(arg);
+               cmd_parse_free_argument(arg);
        }
 }
 
 static void
 cmd_parse_free_command(struct cmd_parse_command *cmd)
 {
-       cmd_free_argv(cmd->argc, cmd->argv);
        cmd_parse_free_arguments(&cmd->arguments);
        free(cmd);
 }
@@ -729,144 +730,120 @@ cmd_parse_log_commands(struct cmd_parse_commands *cmds, const char *prefix)
        }
 }
 
-static char *
-cmd_parse_commands_to_string(struct cmd_parse_commands *cmds)
+static int
+cmd_parse_expand_alias(struct cmd_parse_command *cmd,
+    struct cmd_parse_input *pi, struct cmd_parse_result *pr,
+    struct cmd_list **cmdlist)
 {
-       struct cmd_parse_command         *cmd;
-       char                             *string = NULL, *s, *line;
+       struct cmd_parse_argument       *arg, *arg1, *first, *after;
+       struct cmd_parse_commands       *cmds;
+       struct cmd_parse_command        *last;
+       char                            *alias, *name, *cause;
 
-       TAILQ_FOREACH(cmd, cmds, entry) {
-               cmd_parse_flatten_command(cmd);
+       *cmdlist = NULL;
 
-               line = cmd_stringify_argv(cmd->argc, cmd->argv);
-               if (string == NULL)
-                       s = line;
-               else {
-                       xasprintf(&s, "%s ; %s", s, line);
-                       free(line);
-               }
+       first = TAILQ_FIRST(&cmd->arguments);
+       if (first == NULL || first->type != CMD_PARSE_STRING) {
+               pr->status = CMD_PARSE_EMPTY;
+               return (1);
+       }
+       name = first->string;
+
+       alias = cmd_get_alias(name);
+       if (alias == NULL)
+               return (0);
+       log_debug("%s: %u alias %s = %s", __func__, pi->line, name, alias);
+
+       cmds = cmd_parse_do_buffer(alias, strlen(alias), pi, &cause);
+       free(alias);
+       if (cmds == NULL) {
+               pr->status = CMD_PARSE_ERROR;
+               pr->error = cause;
+               return (1);
+       }
+
+       last = TAILQ_LAST(cmds, cmd_parse_commands);
+       if (last == NULL) {
+               *cmdlist = cmd_list_new();
+               return (1);
+       }
 
-               free(string);
-               string = s;
+       TAILQ_REMOVE(&cmd->arguments, first, entry);
+       cmd_parse_free_argument(first);
+
+       after = TAILQ_NEXT(TAILQ_FIRST(&last->arguments), entry);
+       TAILQ_FOREACH_SAFE(arg, &cmd->arguments, entry, arg1) {
+               TAILQ_REMOVE(&cmd->arguments, arg, entry);
+               if (after == NULL)
+                       TAILQ_INSERT_TAIL(&last->arguments, arg, entry);
+               else
+                       TAILQ_INSERT_AFTER(&last->arguments, after, arg, entry);
+               after = arg;
        }
-       if (string == NULL)
-               string = xstrdup("");
-       log_debug("%s: %s", __func__, string);
-       return (string);
+       cmd_parse_log_commands(cmds, __func__);
+
+       cmd_parse_build_commands(cmds, pi, pr);
+       if (pr->status != CMD_PARSE_SUCCESS)
+               *cmdlist = pr->cmdlist;
+       return (1);
 }
 
-static void
-cmd_parse_flatten_command(struct cmd_parse_command *cmd)
+static struct cmd_list *
+cmd_parse_build_command(struct cmd_parse_command *cmd,
+    struct cmd_parse_input *pi, struct cmd_parse_result *pr)
 {
        struct cmd_parse_argument       *arg;
-       char                            *s;
+       struct cmd_list                 *cmdlist;
+       struct cmd                      *add;
+       char                            *s, **argv = NULL, *cause;
+       int                              argc = 0;
 
-       cmd->argc = 0;
-       cmd->argv = NULL;
+       if (cmd_parse_expand_alias(cmd, pi, pr, &cmdlist))
+               return (cmdlist);
 
        TAILQ_FOREACH(arg, &cmd->arguments, entry) {
                switch (arg->type) {
                case CMD_PARSE_STRING:
-                       cmd_append_argv(&cmd->argc, &cmd->argv, arg->string);
+                       cmd_append_argv(&argc, &argv, arg->string);
                        break;
                case CMD_PARSE_COMMANDS:
-                       s = cmd_parse_commands_to_string(arg->commands);
-                       cmd_append_argv(&cmd->argc, &cmd->argv, s);
+                       cmd_parse_build_commands(arg->commands, pi, pr);
+                       if (pr->status != CMD_PARSE_SUCCESS)
+                               return (NULL);
+                       s = cmd_list_print(pr->cmdlist, 0);
+                       cmd_append_argv(&argc, &argv, s);
                        free(s);
                        break;
                }
        }
-}
 
-static struct cmd *
-cmd_parse_build_command(struct cmd_parse_command *cmd,
-    struct cmd_parse_input *pi, u_int line, struct cmd_parse_result *pr)
-{
-       struct cmd      *add;
-       char            *cause;
-
-       add = cmd_parse(cmd->argc, cmd->argv, pi->file, line, &cause);
+       add = cmd_parse(argc, argv, pi->file, pi->line, &cause);
        if (add == NULL) {
                pr->status = CMD_PARSE_ERROR;
-               pr->error = cmd_parse_get_error(pi->file, line, cause);
+               pr->error = cmd_parse_get_error(pi->file, pi->line, cause);
                free(cause);
                return (NULL);
        }
-       return (add);
+       cmdlist = cmd_list_new();
+       cmd_list_append(cmdlist, add);
+       return (cmdlist);
 }
 
-static struct cmd_parse_result *
+static void
 cmd_parse_build_commands(struct cmd_parse_commands *cmds,
-    struct cmd_parse_input *pi)
+    struct cmd_parse_input *pi, struct cmd_parse_result *pr)
 {
-       static struct cmd_parse_result   pr;
-       struct cmd_parse_commands       *cmds2;
-       struct cmd_parse_command        *cmd, *cmd2, *next, *next2, *after;
+       struct cmd_parse_command        *cmd;
        u_int                            line = UINT_MAX;
-       int                              i;
-       struct cmd_list                 *current = NULL, *result;
-       struct cmd                      *add;
-       char                            *name, *alias, *cause, *s;
+       struct cmd_list                 *current = NULL, *result, *add;
+       char                            *s;
 
        /* Check for an empty list. */
        if (TAILQ_EMPTY(cmds)) {
-               cmd_parse_free_commands(cmds);
-               pr.status = CMD_PARSE_EMPTY;
-               return (&pr);
-       }
-
-       /* Flatten command arguments. */
-       cmd_parse_log_commands(cmds, __func__);
-       TAILQ_FOREACH(cmd, cmds, entry)
-               cmd_parse_flatten_command(cmd);
-
-       /*
-        * Walk the commands and expand any aliases. Each alias is parsed
-        * individually to a new command list, any trailing arguments appended
-        * to the last command, and all commands inserted into the original
-        * command list.
-        */
-       TAILQ_FOREACH_SAFE(cmd, cmds, entry, next) {
-               name = cmd->argv[0];
-
-               alias = cmd_get_alias(name);
-               if (alias == NULL)
-                       continue;
-
-               line = cmd->line;
-               log_debug("%s: %u %s = %s", __func__, line, name, alias);
-
-               pi->line = line;
-               cmds2 = cmd_parse_do_buffer(alias, strlen(alias), pi, &cause);
-               free(alias);
-               if (cmds2 == NULL) {
-                       pr.status = CMD_PARSE_ERROR;
-                       pr.error = cause;
-                       goto out;
-               }
-
-               cmd2 = TAILQ_LAST(cmds2, cmd_parse_commands);
-               if (cmd2 == NULL) {
-                       TAILQ_REMOVE(cmds, cmd, entry);
-                       cmd_parse_free_command(cmd);
-                       continue;
-               }
-               cmd_parse_flatten_command(cmd2);
-               for (i = 1; i < cmd->argc; i++)
-                       cmd_append_argv(&cmd2->argc, &cmd2->argv, cmd->argv[i]);
-
-               after = cmd;
-               TAILQ_FOREACH_SAFE(cmd2, cmds2, entry, next2) {
-                       cmd2->line = line;
-                       TAILQ_REMOVE(cmds2, cmd2, entry);
-                       TAILQ_INSERT_AFTER(cmds, after, cmd2, entry);
-                       after = cmd2;
-               }
-               cmd_parse_free_commands(cmds2);
-
-               TAILQ_REMOVE(cmds, cmd, entry);
-               cmd_parse_free_command(cmd);
+               pr->status = CMD_PARSE_EMPTY;
+               return;
        }
+       cmd_parse_log_commands(cmds, __func__);
 
        /*
         * Parse each command into a command list. Create a new command list
@@ -878,7 +855,7 @@ cmd_parse_build_commands(struct cmd_parse_commands *cmds,
        TAILQ_FOREACH(cmd, cmds, entry) {
                if (((~pi->flags & CMD_PARSE_ONEGROUP) && cmd->line != line)) {
                        if (current != NULL) {
-                               cmd_parse_print_commands(pi, line, current);
+                               cmd_parse_print_commands(pi, current);
                                cmd_list_move(result, current);
                                cmd_list_free(current);
                        }
@@ -886,18 +863,19 @@ cmd_parse_build_commands(struct cmd_parse_commands *cmds,
                }
                if (current == NULL)
                        current = cmd_list_new();
-               line = cmd->line;
+               line = pi->line = cmd->line;
 
-               add = cmd_parse_build_command(cmd, pi, line, &pr);
+               add = cmd_parse_build_command(cmd, pi, pr);
                if (add == NULL) {
                        cmd_list_free(result);
                        cmd_list_free(current);
-                       goto out;
+                       return;
                }
-               cmd_list_append(current, add);
+               cmd_list_move(current, add);
+               cmd_list_free(add);
        }
        if (current != NULL) {
-               cmd_parse_print_commands(pi, line, current);
+               cmd_parse_print_commands(pi, current);
                cmd_list_move(result, current);
                cmd_list_free(current);
        }
@@ -906,13 +884,8 @@ cmd_parse_build_commands(struct cmd_parse_commands *cmds,
        log_debug("%s: %s", __func__, s);
        free(s);
 
-       pr.status = CMD_PARSE_SUCCESS;
-       pr.cmdlist = result;
-
-out:
-       cmd_parse_free_commands(cmds);
-
-       return (&pr);
+       pr->status = CMD_PARSE_SUCCESS;
+       pr->cmdlist = result;
 }
 
 struct cmd_parse_result *
@@ -935,7 +908,10 @@ cmd_parse_from_file(FILE *f, struct cmd_parse_input *pi)
                pr.error = cause;
                return (&pr);
        }
-       return (cmd_parse_build_commands(cmds, pi));
+       cmd_parse_build_commands(cmds, pi, &pr);
+       cmd_parse_free_commands(cmds);
+       return (&pr);
+
 }
 
 struct cmd_parse_result *
@@ -1036,7 +1012,9 @@ cmd_parse_from_buffer(const void *buf, size_t len, struct cmd_parse_input *pi)
                pr.error = cause;
                return (&pr);
        }
-       return (cmd_parse_build_commands(cmds, pi));
+       cmd_parse_build_commands(cmds, pi, &pr);
+       cmd_parse_free_commands(cmds);
+       return (&pr);
 }
 
 static void
@@ -1066,6 +1044,7 @@ cmd_parse_add_command(struct cmd_parse_commands *cmds,
 struct cmd_parse_result *
 cmd_parse_from_arguments(int argc, char **argv, struct cmd_parse_input *pi)
 {
+       static struct cmd_parse_result    pr;
        struct cmd_parse_input            input;
        struct cmd_parse_commands        *cmds;
        char                            **copy, **new_argv;
@@ -1115,7 +1094,9 @@ cmd_parse_from_arguments(int argc, char **argv, struct cmd_parse_input *pi)
        }
 
        cmd_free_argv(argc, copy);
-       return (cmd_parse_build_commands(cmds, pi));
+       cmd_parse_build_commands(cmds, pi, &pr);
+       cmd_parse_free_commands(cmds);
+       return (&pr);
 }
 
 static int printflike(1, 2)