Preserve argument type in command and convert to string on demand.
authornicm <nicm@openbsd.org>
Sat, 21 Aug 2021 20:46:43 +0000 (20:46 +0000)
committernicm <nicm@openbsd.org>
Sat, 21 Aug 2021 20:46:43 +0000 (20:46 +0000)
usr.bin/tmux/arguments.c
usr.bin/tmux/cmd-bind-key.c
usr.bin/tmux/cmd-find-window.c
usr.bin/tmux/cmd-parse.y
usr.bin/tmux/tmux.h

index 7f7d37a..ea6bf9c 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: arguments.c,v 1.42 2021/08/21 18:39:07 nicm Exp $ */
+/* $OpenBSD: arguments.c,v 1.43 2021/08/21 20:46:43 nicm Exp $ */
 
 /*
  * Copyright (c) 2010 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -40,8 +40,8 @@ struct args_entry {
 
 struct args {
        struct args_tree          tree;
-       int                       argc;
-       char                    **argv;
+       u_int                    count;
+       struct args_value       *values;
 };
 
 static struct args_entry       *args_find(struct args *, u_char);
@@ -66,17 +66,37 @@ args_find(struct args *args, u_char flag)
        return (RB_FIND(args_tree, &args->tree, &entry));
 }
 
+/* Copy value. */
+static void
+args_copy_value(struct args_value *to, struct args_value *from)
+{
+       to->type = from->type;
+       switch (from->type) {
+       case ARGS_NONE:
+               break;
+       case ARGS_COMMANDS:
+               to->cmdlist = from->cmdlist;
+               to->cmdlist->references++;
+               break;
+       case ARGS_STRING:
+               to->string = xstrdup(from->string);
+               break;
+       }
+}
+
 /* Get value as string. */
-static char *
+static const char *
 args_value_as_string(struct args_value *value)
 {
        switch (value->type) {
        case ARGS_NONE:
-               return (xstrdup(""));
+               return ("");
        case ARGS_COMMANDS:
-               return (cmd_list_print(value->cmdlist, 0));
+               if (value->cached == NULL)
+                       value->cached = cmd_list_print(value->cmdlist, 0);
+               return (value->cached);
        case ARGS_STRING:
-               return (xstrdup(value->string));
+               return (value->string);
        }
 }
 
@@ -98,10 +118,9 @@ args_parse(const struct args_parse *parse, struct args_value *values,
 {
        struct args             *args;
        u_int                    i;
-       struct args_value       *value;
+       struct args_value       *value, *new;
        u_char                   flag, argument;
-       const char              *found, *string;
-       char                    *s;
+       const char              *found, *string, *s;
 
        if (count == 0)
                return (args_create());
@@ -109,11 +128,6 @@ args_parse(const struct args_parse *parse, struct args_value *values,
        args = args_create();
        for (i = 1; i < count; /* nothing */) {
                value = &values[i];
-
-               s = args_value_as_string(value);
-               log_debug("%s: %u = %s", __func__, i, s);
-               free(s);
-
                if (value->type != ARGS_STRING)
                        break;
 
@@ -143,18 +157,20 @@ args_parse(const struct args_parse *parse, struct args_value *values,
                                args_set(args, flag, NULL);
                                continue;
                        }
-                       if (*string != '\0')
-                               s = xstrdup(string);
-                       else {
+                       new = xcalloc(1, sizeof *value);
+                       if (*string != '\0') {
+                               new->type = ARGS_STRING;
+                               new->string = xstrdup(string);
+                       } else {
                                if (i == count) {
                                        args_free(args);
                                        return (NULL);
                                }
-                               s = args_value_as_string(&values[i++]);
+                               args_copy_value(new, &values[i++]);
                        }
+                       s = args_value_as_string(new);
                        log_debug("%s: add -%c = %s", __func__, flag, s);
-                       args_set(args, flag, s);
-                       free(s);
+                       args_set(args, flag, new);
                        break;
                }
        }
@@ -165,13 +181,15 @@ args_parse(const struct args_parse *parse, struct args_value *values,
 
                        s = args_value_as_string(value);
                        log_debug("%s: %u = %s", __func__, i, s);
-                       cmd_append_argv(&args->argc, &args->argv, s);
-                       free(s);
+
+                       args->values = xrecallocarray(args->values,
+                           args->count, args->count + 1, sizeof *args->values);
+                       args_copy_value(&args->values[args->count++], value);
                }
        }
 
-       if ((parse->lower != -1 && args->argc < parse->lower) ||
-           (parse->upper != -1 && args->argc > parse->upper)) {
+       if ((parse->lower != -1 && args->count < (u_int)parse->lower) ||
+           (parse->upper != -1 && args->count > (u_int)parse->upper)) {
                args_free(args);
                return (NULL);
        }
@@ -192,6 +210,7 @@ args_free_value(struct args_value *value)
                cmd_list_free(value->cmdlist);
                break;
        }
+       free(value->cached);
 }
 
 /* Free an arguments set. */
@@ -202,8 +221,11 @@ args_free(struct args *args)
        struct args_entry       *entry1;
        struct args_value       *value;
        struct args_value       *value1;
+       u_int                    i;
 
-       cmd_free_argv(args->argc, args->argv);
+       for (i = 0; i < args->count; i++)
+               args_free_value(&args->values[i]);
+       free(args->values);
 
        RB_FOREACH_SAFE(entry, args_tree, &args->tree, entry1) {
                RB_REMOVE(args_tree, &args->tree, entry);
@@ -222,8 +244,16 @@ args_free(struct args *args)
 void
 args_vector(struct args *args, int *argc, char ***argv)
 {
-       *argc = args->argc;
-       *argv = cmd_copy_argv(args->argc, args->argv);
+       struct args_value       *value;
+       u_int                    i;
+
+       *argc = 0;
+       *argv = NULL;
+
+       for (i = 0; i < args->count; i++) {
+               value = &args->values[i];
+               cmd_append_argv(argc, argv, args_value_as_string(value));
+       }
 }
 
 /* Add to string. */
@@ -245,18 +275,28 @@ args_print_add(char **buf, size_t *len, const char *fmt, ...)
        free(s);
 }
 
-/* Add argument to string. */
+/* Add value to string. */
 static void
-args_print_add_argument(char **buf, size_t *len, const char *argument)
+args_print_add_value(char **buf, size_t *len, struct args_value *value)
 {
-       char    *escaped;
+       char    *expanded = NULL;
 
        if (**buf != '\0')
                args_print_add(buf, len, " ");
 
-       escaped = args_escape(argument);
-       args_print_add(buf, len, "%s", escaped);
-       free(escaped);
+       switch (value->type) {
+       case ARGS_NONE:
+               break;
+       case ARGS_COMMANDS:
+               expanded = cmd_list_print(value->cmdlist, 0);
+               args_print_add(buf, len, "{ %s }", expanded);
+               break;
+       case ARGS_STRING:
+               expanded = args_escape(value->string);
+               args_print_add(buf, len, "%s", expanded);
+               break;
+       }
+       free(expanded);
 }
 
 /* Print a set of arguments. */
@@ -265,8 +305,7 @@ args_print(struct args *args)
 {
        size_t                   len;
        char                    *buf;
-       int                      i;
-       u_int                    j;
+       u_int                    i, j;
        struct args_entry       *entry;
        struct args_value       *value;
 
@@ -291,13 +330,13 @@ args_print(struct args *args)
                                args_print_add(&buf, &len, " -%c", entry->flag);
                        else
                                args_print_add(&buf, &len, "-%c", entry->flag);
-                       args_print_add_argument(&buf, &len, value->string);
+                       args_print_add_value(&buf, &len, value);
                }
        }
 
        /* And finally the argument vector. */
-       for (i = 0; i < args->argc; i++)
-               args_print_add_argument(&buf, &len, args->argv[i]);
+       for (i = 0; i < args->count; i++)
+               args_print_add_value(&buf, &len, &args->values[i]);
 
        return (buf);
 }
@@ -363,10 +402,9 @@ args_has(struct args *args, u_char flag)
 
 /* Set argument value in the arguments tree. */
 void
-args_set(struct args *args, u_char flag, const char *s)
+args_set(struct args *args, u_char flag, struct args_value *value)
 {
        struct args_entry       *entry;
-       struct args_value       *value;
 
        entry = args_find(args, flag);
        if (entry == NULL) {
@@ -377,12 +415,8 @@ args_set(struct args *args, u_char flag, const char *s)
                RB_INSERT(args_tree, &args->tree, entry);
        } else
                entry->count++;
-
-       if (s != NULL) {
-               value = xcalloc(1, sizeof *value);
-               value->string = xstrdup(s);
+       if (value != NULL && value->type != ARGS_NONE)
                TAILQ_INSERT_TAIL(&entry->values, value, entry);
-       }
 }
 
 /* Get argument value. Will be NULL if it isn't present. */
@@ -422,16 +456,25 @@ args_next(struct args_entry **entry)
 u_int
 args_count(struct args *args)
 {
-       return (args->argc);
+       return (args->count);
+}
+
+/* Get argument value. */
+struct args_value *
+args_value(struct args *args, u_int idx)
+{
+       if (idx >= args->count)
+               return (NULL);
+       return (&args->values[idx]);
 }
 
 /* Return argument as string. */
 const char *
 args_string(struct args *args, u_int idx)
 {
-       if (idx >= (u_int)args->argc)
+       if (idx >= (u_int)args->count)
                return (NULL);
-       return (args->argv[idx]);
+       return (args_value_as_string(&args->values[idx]));
 }
 
 /* Get first value in argument. */
index 1ad89b8..7a6cde2 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: cmd-bind-key.c,v 1.41 2021/08/21 17:25:32 nicm Exp $ */
+/* $OpenBSD: cmd-bind-key.c,v 1.42 2021/08/21 20:46:43 nicm Exp $ */
 
 /*
  * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -50,6 +50,7 @@ cmd_bind_key_exec(struct cmd *self, struct cmdq_item *item)
        struct cmd_parse_result  *pr;
        char                    **argv;
        int                       argc, repeat;
+       struct args_value        *value;
        u_int                     count = args_count(args);
 
        key = key_string_lookup_string(args_string(args, 0));
@@ -66,24 +67,32 @@ cmd_bind_key_exec(struct cmd *self, struct cmdq_item *item)
                tablename = "prefix";
        repeat = args_has(args, 'r');
 
-       if (count != 1) {
-               if (count == 2)
-                       pr = cmd_parse_from_string(args_string(args, 1), NULL);
-               else {
-                       args_vector(args, &argc, &argv);
-                       pr = cmd_parse_from_arguments(argc - 1, argv + 1, NULL);
-                       cmd_free_argv(argc, argv);
-               }
-               switch (pr->status) {
-               case CMD_PARSE_ERROR:
-                       cmdq_error(item, "%s", pr->error);
-                       free(pr->error);
-                       return (CMD_RETURN_ERROR);
-               case CMD_PARSE_SUCCESS:
-                       break;
-               }
-               key_bindings_add(tablename, key, note, repeat, pr->cmdlist);
-       } else
+       if (count == 1) {
                key_bindings_add(tablename, key, note, repeat, NULL);
+               return (CMD_RETURN_NORMAL);
+       }
+
+       value = args_value(args, 1);
+       if (count == 2 && value->type == ARGS_COMMANDS) {
+               key_bindings_add(tablename, key, note, repeat, value->cmdlist);
+               return (CMD_RETURN_NORMAL);
+       }
+
+       if (count == 2)
+               pr = cmd_parse_from_string(args_string(args, 1), NULL);
+       else {
+               args_vector(args, &argc, &argv);
+               pr = cmd_parse_from_arguments(argc - 1, argv + 1, NULL);
+               cmd_free_argv(argc, argv);
+       }
+       switch (pr->status) {
+       case CMD_PARSE_ERROR:
+               cmdq_error(item, "%s", pr->error);
+               free(pr->error);
+               return (CMD_RETURN_ERROR);
+       case CMD_PARSE_SUCCESS:
+               break;
+       }
+       key_bindings_add(tablename, key, note, repeat, pr->cmdlist);
        return (CMD_RETURN_NORMAL);
 }
index 27171d2..977a378 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: cmd-find-window.c,v 1.53 2021/08/21 10:22:39 nicm Exp $ */
+/* $OpenBSD: cmd-find-window.c,v 1.54 2021/08/21 20:46:43 nicm Exp $ */
 
 /*
  * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -48,7 +48,7 @@ cmd_find_window_exec(struct cmd *self, struct cmdq_item *item)
        struct cmd_find_state   *target = cmdq_get_target(item);
        struct window_pane      *wp = target->wp;
        const char              *s = args_string(args, 0), *suffix = "";
-       char                    *filter;
+       struct args_value       *filter;
        int                      C, N, T;
 
        C = args_has(args, 'C');
@@ -65,31 +65,41 @@ cmd_find_window_exec(struct cmd *self, struct cmdq_item *item)
        if (!C && !N && !T)
                C = N = T = 1;
 
+       filter = xcalloc(1, sizeof *filter);
+       filter->type = ARGS_STRING;
+
        if (C && N && T) {
-               xasprintf(&filter,
+               xasprintf(&filter->string,
                    "#{||:"
                    "#{C%s:%s},#{||:#{m%s:*%s*,#{window_name}},"
                    "#{m%s:*%s*,#{pane_title}}}}",
                    suffix, s, suffix, s, suffix, s);
        } else if (C && N) {
-               xasprintf(&filter,
+               xasprintf(&filter->string,
                    "#{||:#{C%s:%s},#{m%s:*%s*,#{window_name}}}",
                    suffix, s, suffix, s);
        } else if (C && T) {
-               xasprintf(&filter,
+               xasprintf(&filter->string,
                    "#{||:#{C%s:%s},#{m%s:*%s*,#{pane_title}}}",
                    suffix, s, suffix, s);
        } else if (N && T) {
-               xasprintf(&filter,
+               xasprintf(&filter->string,
                    "#{||:#{m%s:*%s*,#{window_name}},"
                    "#{m%s:*%s*,#{pane_title}}}",
                    suffix, s, suffix, s);
-       } else if (C)
-               xasprintf(&filter, "#{C%s:%s}", suffix, s);
-       else if (N)
-               xasprintf(&filter, "#{m%s:*%s*,#{window_name}}", suffix, s);
-       else
-               xasprintf(&filter, "#{m%s:*%s*,#{pane_title}}", suffix, s);
+       } else if (C) {
+               xasprintf(&filter->string,
+                   "#{C%s:%s}",
+                   suffix, s);
+       } else if (N) {
+               xasprintf(&filter->string,
+                   "#{m%s:*%s*,#{window_name}}",
+                   suffix, s);
+       } else {
+               xasprintf(&filter->string,
+                   "#{m%s:*%s*,#{pane_title}}",
+                   suffix, s);
+       }
 
        new_args = args_create();
        if (args_has(args, 'Z'))
@@ -97,9 +107,7 @@ cmd_find_window_exec(struct cmd *self, struct cmdq_item *item)
        args_set(new_args, 'f', filter);
 
        window_pane_set_mode(wp, NULL, &window_tree_mode, target, new_args);
-
        args_free(new_args);
-       free(filter);
 
        return (CMD_RETURN_NORMAL);
 }
index 829d88b..37d3eba 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: cmd-parse.y,v 1.40 2021/08/21 18:39:07 nicm Exp $ */
+/* $OpenBSD: cmd-parse.y,v 1.41 2021/08/21 20:46:43 nicm Exp $ */
 
 /*
  * Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -804,7 +804,8 @@ cmd_parse_build_command(struct cmd_parse_command *cmd,
                return (cmdlist);
 
        TAILQ_FOREACH(arg, &cmd->arguments, entry) {
-               values = xreallocarray(values, count + 1, sizeof *values);
+               values = xrecallocarray(values, count, count + 1,
+                   sizeof *values);
                switch (arg->type) {
                case CMD_PARSE_STRING:
                        values[count].type = ARGS_STRING;
index 4130c2a..2d88812 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: tmux.h,v 1.1133 2021/08/21 18:39:07 nicm Exp $ */
+/* $OpenBSD: tmux.h,v 1.1134 2021/08/21 20:46:43 nicm Exp $ */
 
 /*
  * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -1369,6 +1369,7 @@ struct args_value {
                char            *string;
                struct cmd_list *cmdlist;
        };
+       char                    *cached;
        TAILQ_ENTRY(args_value)  entry;
 };
 
@@ -2196,7 +2197,7 @@ void              tty_keys_free(struct tty *);
 int            tty_keys_next(struct tty *);
 
 /* arguments.c */
-void            args_set(struct args *, u_char, const char *);
+void            args_set(struct args *, u_char, struct args_value *);
 struct args    *args_create(void);
 struct args    *args_parse(const struct args_parse *, struct args_value *,
                     u_int);
@@ -2210,6 +2211,7 @@ const char        *args_get(struct args *, u_char);
 u_char          args_first(struct args *, struct args_entry **);
 u_char          args_next(struct args_entry **);
 u_int           args_count(struct args *);
+struct args_value *args_value(struct args *, u_int);
 const char     *args_string(struct args *, u_int);
 struct args_value *args_first_value(struct args *, u_char);
 struct args_value *args_next_value(struct args_value *);