Add send-keys -K to handle keys directly as if typed (so look up in key
authornicm <nicm@openbsd.org>
Fri, 16 Dec 2022 08:13:40 +0000 (08:13 +0000)
committernicm <nicm@openbsd.org>
Fri, 16 Dec 2022 08:13:40 +0000 (08:13 +0000)
table). GitHub issue 3361.

usr.bin/tmux/arguments.c
usr.bin/tmux/cmd-find-window.c
usr.bin/tmux/cmd-send-keys.c
usr.bin/tmux/tmux.1
usr.bin/tmux/tmux.h

index 4e6515e..2a0d4ca 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: arguments.c,v 1.56 2022/08/02 09:23:34 nicm Exp $ */
+/* $OpenBSD: arguments.c,v 1.57 2022/12/16 08:13:40 nicm Exp $ */
 
 /*
  * Copyright (c) 2010 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -37,6 +37,10 @@ struct args_entry {
        u_char                   flag;
        struct args_values       values;
        u_int                    count;
+
+       int                      flags;
+#define ARGS_ENTRY_OPTIONAL_VALUE 0x1
+
        RB_ENTRY(args_entry)     entry;
 };
 
@@ -122,6 +126,101 @@ args_create(void)
        return (args);
 }
 
+/* Parse a single flag. */
+static int
+args_parse_flag_argument(struct args_value *values, u_int count, char **cause,
+    struct args *args, u_int *i, const char *string, int flag,
+    int optional_argument)
+{
+       struct args_value       *argument, *new;
+       const char              *s;
+
+       new = xcalloc(1, sizeof *new);
+       if (*string != '\0') {
+               new->type = ARGS_STRING;
+               new->string = xstrdup(string);
+               goto out;
+       }
+
+       if (*i == count)
+               argument = NULL;
+       else {
+               argument = &values[*i];
+               if (argument->type != ARGS_STRING) {
+                       xasprintf(cause, "-%c argument must be a string", flag);
+                       return (-1);
+               }
+               if (argument->string[0] == '-')
+                       argument = NULL;
+       }
+       if (argument == NULL) {
+               if (optional_argument) {
+                       log_debug("%s: -%c (optional)", __func__, flag);
+                       args_set(args, flag, NULL, ARGS_ENTRY_OPTIONAL_VALUE);
+                       return (0); /* either - or end */
+               }
+               xasprintf(cause, "-%c expects an argument", flag);
+               return (-1);
+       }
+       args_copy_value(new, argument);
+       (*i)++;
+
+out:
+       s = args_value_as_string(new);
+       log_debug("%s: -%c = %s", __func__, flag, s);
+       args_set(args, flag, new, 0);
+       return (0);
+}
+
+/* Parse flags argument. */
+static int
+args_parse_flags(const struct args_parse *parse, struct args_value *values,
+    u_int count, char **cause, struct args *args, int *i)
+{
+       struct args_value       *value;
+       u_char                   flag;
+       const char              *found, *string;
+       int                      optional_argument;
+
+       value = &values[*i];
+       if (value->type != ARGS_STRING)
+               return (1);
+
+       string = value->string;
+       log_debug("%s: next %s", __func__, string);
+       if (*string++ != '-' || *string == '\0')
+               return (1);
+       (*i)++;
+       if (string[0] == '-' && string[1] == '\0')
+               return (1);
+
+       for (;;) {
+               flag = *string++;
+               if (flag == '\0')
+                       return (0);
+               if (flag == '?')
+                       return (-1);
+               if (!isalnum(flag)) {
+                       xasprintf(cause, "invalid flag -%c", flag);
+                       return (-1);
+               }
+
+               found = strchr(parse->template, flag);
+               if (found == NULL) {
+                       xasprintf(cause, "unknown flag -%c", flag);
+                       return (-1);
+               }
+               if (*++found != ':') {
+                       log_debug("%s: -%c", __func__, flag);
+                       args_set(args, flag, NULL, 0);
+                       continue;
+               }
+               optional_argument = (*found == ':');
+               return (args_parse_flag_argument(values, count, cause, args, i,
+                   string, flag, optional_argument));
+       }
+}
+
 /* Parse arguments into a new argument set. */
 struct args *
 args_parse(const struct args_parse *parse, struct args_value *values,
@@ -131,86 +230,21 @@ args_parse(const struct args_parse *parse, struct args_value *values,
        u_int                    i;
        enum args_parse_type     type;
        struct args_value       *value, *new;
-       u_char                   flag;
-       const char              *found, *string, *s;
-       int                      optional_argument;
+       const char              *s;
+       int                      stop;
 
        if (count == 0)
                return (args_create());
 
        args = args_create();
        for (i = 1; i < count; /* nothing */) {
-               value = &values[i];
-               if (value->type != ARGS_STRING)
-                       break;
-
-               string = value->string;
-               if (*string++ != '-' || *string == '\0')
-                       break;
-               i++;
-               if (string[0] == '-' && string[1] == '\0')
-                       break;
-
-               for (;;) {
-                       flag = *string++;
-                       if (flag == '\0')
-                               break;
-                       if (flag == '?') {
-                               args_free(args);
-                               return (NULL);
-                       }
-                       if (!isalnum(flag)) {
-                               xasprintf(cause, "invalid flag -%c", flag);
-                               args_free(args);
-                               return (NULL);
-                       }
-                       found = strchr(parse->template, flag);
-                       if (found == NULL) {
-                               xasprintf(cause, "unknown flag -%c", flag);
-                               args_free(args);
-                               return (NULL);
-                       }
-                       if (*++found != ':') {
-                               log_debug("%s: -%c", __func__, flag);
-                               args_set(args, flag, NULL);
-                               continue;
-                       }
-                       if (*found == ':') {
-                               optional_argument = 1;
-                               found++;
-                       }
-                       new = xcalloc(1, sizeof *new);
-                       if (*string != '\0') {
-                               new->type = ARGS_STRING;
-                               new->string = xstrdup(string);
-                       } else {
-                               if (i == count) {
-                                       if (optional_argument) {
-                                               log_debug("%s: -%c", __func__,
-                                                   flag);
-                                               args_set(args, flag, NULL);
-                                               continue;
-                                       }
-                                       xasprintf(cause,
-                                           "-%c expects an argument",
-                                           flag);
-                                       args_free(args);
-                                       return (NULL);
-                               }
-                               if (values[i].type != ARGS_STRING) {
-                                       xasprintf(cause,
-                                           "-%c argument must be a string",
-                                           flag);
-                                       args_free(args);
-                                       return (NULL);
-                               }
-                               args_copy_value(new, &values[i++]);
-                       }
-                       s = args_value_as_string(new);
-                       log_debug("%s: -%c = %s", __func__, flag, s);
-                       args_set(args, flag, new);
-                       break;
+               stop = args_parse_flags(parse, values, count, cause, args, &i);
+               if (stop == -1) {
+                       args_free(args);
+                       return (NULL);
                }
+               if (stop == 1)
+                       break;
        }
        log_debug("%s: flags end at %u of %u", __func__, i, count);
        if (i != count) {
@@ -323,13 +357,13 @@ args_copy(struct args *args, int argc, char **argv)
        RB_FOREACH(entry, args_tree, &args->tree) {
                if (TAILQ_EMPTY(&entry->values)) {
                        for (i = 0; i < entry->count; i++)
-                               args_set(new_args, entry->flag, NULL);
+                               args_set(new_args, entry->flag, NULL, 0);
                        continue;
                }
                TAILQ_FOREACH(value, &entry->values, entry) {
                        new_value = xcalloc(1, sizeof *new_value);
                        args_copy_copy_value(new_value, value, argc, argv);
-                       args_set(new_args, entry->flag, new_value);
+                       args_set(new_args, entry->flag, new_value, 0);
                }
        }
        if (args->count == 0)
@@ -487,6 +521,7 @@ args_print(struct args *args)
        char                    *buf;
        u_int                    i, j;
        struct args_entry       *entry;
+       struct args_entry       *last = NULL;
        struct args_value       *value;
 
        len = 1;
@@ -494,6 +529,8 @@ args_print(struct args *args)
 
        /* Process the flags first. */
        RB_FOREACH(entry, args_tree, &args->tree) {
+               if (entry->flags & ARGS_ENTRY_OPTIONAL_VALUE)
+                       continue;
                if (!TAILQ_EMPTY(&entry->values))
                        continue;
 
@@ -505,6 +542,16 @@ args_print(struct args *args)
 
        /* Then the flags with arguments. */
        RB_FOREACH(entry, args_tree, &args->tree) {
+               if (entry->flags & ARGS_ENTRY_OPTIONAL_VALUE) {
+                       if (*buf != '\0')
+                               args_print_add(&buf, &len, " -%c", entry->flag);
+                       else
+                               args_print_add(&buf, &len, "-%c", entry->flag);
+                       last = entry;
+                       continue;
+               }
+               if (TAILQ_EMPTY(&entry->values))
+                       continue;
                TAILQ_FOREACH(value, &entry->values, entry) {
                        if (*buf != '\0')
                                args_print_add(&buf, &len, " -%c", entry->flag);
@@ -512,7 +559,10 @@ args_print(struct args *args)
                                args_print_add(&buf, &len, "-%c", entry->flag);
                        args_print_add_value(&buf, &len, value);
                }
+               last = entry;
        }
+       if (last && (last->flags & ARGS_ENTRY_OPTIONAL_VALUE))
+               args_print_add(&buf, &len, " --");
 
        /* And finally the argument vector. */
        for (i = 0; i < args->count; i++)
@@ -582,7 +632,7 @@ args_has(struct args *args, u_char flag)
 
 /* Set argument value in the arguments tree. */
 void
-args_set(struct args *args, u_char flag, struct args_value *value)
+args_set(struct args *args, u_char flag, struct args_value *value, int flags)
 {
        struct args_entry       *entry;
 
@@ -591,6 +641,7 @@ args_set(struct args *args, u_char flag, struct args_value *value)
                entry = xcalloc(1, sizeof *entry);
                entry->flag = flag;
                entry->count = 1;
+               entry->flags = flags;
                TAILQ_INIT(&entry->values);
                RB_INSERT(args_tree, &args->tree, entry);
        } else
index 977a378..5b7db89 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: cmd-find-window.c,v 1.54 2021/08/21 20:46:43 nicm Exp $ */
+/* $OpenBSD: cmd-find-window.c,v 1.55 2022/12/16 08:13:40 nicm Exp $ */
 
 /*
  * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -103,8 +103,8 @@ cmd_find_window_exec(struct cmd *self, struct cmdq_item *item)
 
        new_args = args_create();
        if (args_has(args, 'Z'))
-               args_set(new_args, 'Z', NULL);
-       args_set(new_args, 'f', filter);
+               args_set(new_args, 'Z', NULL, 0);
+       args_set(new_args, 'f', filter, 0);
 
        window_pane_set_mode(wp, NULL, &window_tree_mode, target, new_args);
        args_free(new_args);
index 4373f29..e655977 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: cmd-send-keys.c,v 1.72 2022/06/07 10:02:19 nicm Exp $ */
+/* $OpenBSD: cmd-send-keys.c,v 1.73 2022/12/16 08:13:40 nicm Exp $ */
 
 /*
  * Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -33,13 +33,13 @@ const struct cmd_entry cmd_send_keys_entry = {
        .name = "send-keys",
        .alias = "send",
 
-       .args = { "FHlMN:Rt:X", 0, -1, NULL },
-       .usage = "[-FHlMRX] [-N repeat-count] " CMD_TARGET_PANE_USAGE
-                " key ...",
+       .args = { "c:FHKlMN:Rt:X", 0, -1, NULL },
+       .usage = "[-FHKlMRX] [-c target-client] [-N repeat-count] "
+                CMD_TARGET_PANE_USAGE " key ...",
 
        .target = { 't', CMD_FIND_PANE, 0 },
 
-       .flags = CMD_AFTERHOOK,
+       .flags = CMD_AFTERHOOK|CMD_CLIENT_CFLAG,
        .exec = cmd_send_keys_exec
 };
 
@@ -58,7 +58,7 @@ const struct cmd_entry cmd_send_prefix_entry = {
 
 static struct cmdq_item *
 cmd_send_keys_inject_key(struct cmdq_item *item, struct cmdq_item *after,
-    key_code key)
+    struct args *args, key_code key)
 {
        struct cmd_find_state           *target = cmdq_get_target(item);
        struct client                   *tc = cmdq_get_target_client(item);
@@ -66,8 +66,18 @@ cmd_send_keys_inject_key(struct cmdq_item *item, struct cmdq_item *after,
        struct winlink                  *wl = target->wl;
        struct window_pane              *wp = target->wp;
        struct window_mode_entry        *wme;
-       struct key_table                *table;
+       struct key_table                *table = NULL;
        struct key_binding              *bd;
+       struct key_event                *event;
+
+       if (args_has(args, 'K')) {
+               event = xmalloc(sizeof *event);
+               event->key = key;
+               memset(&event->m, 0, sizeof event->m);
+               if (server_client_handle_key(tc, event) == 0)
+                       free(event);
+               return (item);
+       }
 
        wme = TAILQ_FIRST(&wp->modes);
        if (wme == NULL || wme->mode->key_table == NULL) {
@@ -102,14 +112,16 @@ cmd_send_keys_inject_string(struct cmdq_item *item, struct cmdq_item *after,
                n = strtol(s, &endptr, 16);
                if (*s =='\0' || n < 0 || n > 0xff || *endptr != '\0')
                        return (item);
-               return (cmd_send_keys_inject_key(item, after, KEYC_LITERAL|n));
+               return (cmd_send_keys_inject_key(item, after, args,
+                   KEYC_LITERAL|n));
        }
 
        literal = args_has(args, 'l');
        if (!literal) {
                key = key_string_lookup_string(s);
                if (key != KEYC_NONE && key != KEYC_UNKNOWN) {
-                       after = cmd_send_keys_inject_key(item, after, key);
+                       after = cmd_send_keys_inject_key(item, after, args,
+                           key);
                        if (after != NULL)
                                return (after);
                }
@@ -125,7 +137,8 @@ cmd_send_keys_inject_string(struct cmdq_item *item, struct cmdq_item *after,
                                        continue;
                                key = uc;
                        }
-                       after = cmd_send_keys_inject_key(item, after, key);
+                       after = cmd_send_keys_inject_key(item, after, args,
+                           key);
                }
                free(ud);
        }
@@ -193,7 +206,7 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item)
                        key = options_get_number(s->options, "prefix2");
                else
                        key = options_get_number(s->options, "prefix");
-               cmd_send_keys_inject_key(item, item, key);
+               cmd_send_keys_inject_key(item, item, args, key);
                return (CMD_RETURN_NORMAL);
        }
 
@@ -207,7 +220,7 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item)
                if (args_has(args, 'N') || args_has(args, 'R'))
                        return (CMD_RETURN_NORMAL);
                for (; np != 0; np--)
-                       cmd_send_keys_inject_key(item, NULL, event->key);
+                       cmd_send_keys_inject_key(item, NULL, args, event->key);
                return (CMD_RETURN_NORMAL);
        }
 
index 9f4cb08..3cfc134 100644 (file)
@@ -1,4 +1,4 @@
-.\" $OpenBSD: tmux.1,v 1.908 2022/11/11 08:37:55 nicm Exp $
+.\" $OpenBSD: tmux.1,v 1.909 2022/12/16 08:13:40 nicm Exp $
 .\"
 .\" Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
 .\"
@@ -14,7 +14,7 @@
 .\" IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
 .\" OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\"
-.Dd $Mdocdate: November 11 2022 $
+.Dd $Mdocdate: December 16 2022 $
 .Dt TMUX 1
 .Os
 .Sh NAME
@@ -3212,13 +3212,14 @@ lists only the first matching key.
 lists the command for keys that do not have a note rather than skipping them.
 .Tg send
 .It Xo Ic send-keys
-.Op Fl FHlMRX
+.Op Fl FHKlMRX
+.Op Fl c Ar target-client
 .Op Fl N Ar repeat-count
 .Op Fl t Ar target-pane
 .Ar key Ar ...
 .Xc
 .D1 Pq alias: Ic send
-Send a key or keys to a window.
+Send a key or keys to a window or client.
 Each argument
 .Ar key
 is the name of the key (such as
@@ -3227,6 +3228,12 @@ or
 .Ql NPage )
 to send; if the string is not recognised as a key, it is sent as a series of
 characters.
+If
+.Fl K
+is given, keys are sent to
+.Ar target-client ,
+so they are looked up in the client's key table, rather than to
+.Ar target-pane .
 All arguments are sent sequentially from first to last.
 If no keys are given and the command is bound to a key, then that key is used.
 .Pp
index bff6b26..9805e77 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: tmux.h,v 1.1186 2022/12/07 09:44:44 nicm Exp $ */
+/* $OpenBSD: tmux.h,v 1.1187 2022/12/16 08:13:40 nicm Exp $ */
 
 /*
  * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -2387,7 +2387,7 @@ void              tty_keys_free(struct tty *);
 int            tty_keys_next(struct tty *);
 
 /* arguments.c */
-void            args_set(struct args *, u_char, struct args_value *);
+void            args_set(struct args *, u_char, struct args_value *, int);
 struct args    *args_create(void);
 struct args    *args_parse(const struct args_parse *, struct args_value *,
                     u_int, char **);