1*b6124414Snicm /* $OpenBSD: cmd.c,v 1.177 2025/01/27 09:16:05 nicm Exp $ */ 2311827fbSnicm 3311827fbSnicm /* 498ca8272Snicm * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> 5311827fbSnicm * 6311827fbSnicm * Permission to use, copy, modify, and distribute this software for any 7311827fbSnicm * purpose with or without fee is hereby granted, provided that the above 8311827fbSnicm * copyright notice and this permission notice appear in all copies. 9311827fbSnicm * 10311827fbSnicm * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11311827fbSnicm * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12311827fbSnicm * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13311827fbSnicm * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14311827fbSnicm * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 15311827fbSnicm * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 16311827fbSnicm * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17311827fbSnicm */ 18311827fbSnicm 19311827fbSnicm #include <sys/types.h> 20311827fbSnicm #include <sys/time.h> 21311827fbSnicm 223ce750e3Snicm #include <fnmatch.h> 233ce750e3Snicm #include <paths.h> 241581b699Snicm #include <pwd.h> 25311827fbSnicm #include <stdlib.h> 26311827fbSnicm #include <string.h> 27311827fbSnicm #include <unistd.h> 28311827fbSnicm 29311827fbSnicm #include "tmux.h" 30311827fbSnicm 31bf77695eSnicm extern const struct cmd_entry cmd_attach_session_entry; 32bf77695eSnicm extern const struct cmd_entry cmd_bind_key_entry; 33bf77695eSnicm extern const struct cmd_entry cmd_break_pane_entry; 34bf77695eSnicm extern const struct cmd_entry cmd_capture_pane_entry; 35bf77695eSnicm extern const struct cmd_entry cmd_choose_buffer_entry; 36bf77695eSnicm extern const struct cmd_entry cmd_choose_client_entry; 37bf77695eSnicm extern const struct cmd_entry cmd_choose_tree_entry; 38bf77695eSnicm extern const struct cmd_entry cmd_clear_history_entry; 39bc5a8fc2Snicm extern const struct cmd_entry cmd_clear_prompt_history_entry; 40bf77695eSnicm extern const struct cmd_entry cmd_clock_mode_entry; 41bf77695eSnicm extern const struct cmd_entry cmd_command_prompt_entry; 42bf77695eSnicm extern const struct cmd_entry cmd_confirm_before_entry; 43bf77695eSnicm extern const struct cmd_entry cmd_copy_mode_entry; 4467c16a7cSnicm extern const struct cmd_entry cmd_customize_mode_entry; 45bf77695eSnicm extern const struct cmd_entry cmd_delete_buffer_entry; 46bf77695eSnicm extern const struct cmd_entry cmd_detach_client_entry; 473546f4c9Snicm extern const struct cmd_entry cmd_display_menu_entry; 48bf77695eSnicm extern const struct cmd_entry cmd_display_message_entry; 490cac3d2dSnicm extern const struct cmd_entry cmd_display_popup_entry; 50bf77695eSnicm extern const struct cmd_entry cmd_display_panes_entry; 51bf77695eSnicm extern const struct cmd_entry cmd_find_window_entry; 52bf77695eSnicm extern const struct cmd_entry cmd_has_session_entry; 53bf77695eSnicm extern const struct cmd_entry cmd_if_shell_entry; 54bf77695eSnicm extern const struct cmd_entry cmd_join_pane_entry; 55bf77695eSnicm extern const struct cmd_entry cmd_kill_pane_entry; 56bf77695eSnicm extern const struct cmd_entry cmd_kill_server_entry; 57bf77695eSnicm extern const struct cmd_entry cmd_kill_session_entry; 58bf77695eSnicm extern const struct cmd_entry cmd_kill_window_entry; 59bf77695eSnicm extern const struct cmd_entry cmd_last_pane_entry; 60bf77695eSnicm extern const struct cmd_entry cmd_last_window_entry; 61bf77695eSnicm extern const struct cmd_entry cmd_link_window_entry; 62bf77695eSnicm extern const struct cmd_entry cmd_list_buffers_entry; 63bf77695eSnicm extern const struct cmd_entry cmd_list_clients_entry; 64bf77695eSnicm extern const struct cmd_entry cmd_list_commands_entry; 65bf77695eSnicm extern const struct cmd_entry cmd_list_keys_entry; 66bf77695eSnicm extern const struct cmd_entry cmd_list_panes_entry; 67bf77695eSnicm extern const struct cmd_entry cmd_list_sessions_entry; 68bf77695eSnicm extern const struct cmd_entry cmd_list_windows_entry; 69bf77695eSnicm extern const struct cmd_entry cmd_load_buffer_entry; 70bf77695eSnicm extern const struct cmd_entry cmd_lock_client_entry; 71bf77695eSnicm extern const struct cmd_entry cmd_lock_server_entry; 72bf77695eSnicm extern const struct cmd_entry cmd_lock_session_entry; 73bf77695eSnicm extern const struct cmd_entry cmd_move_pane_entry; 74bf77695eSnicm extern const struct cmd_entry cmd_move_window_entry; 75bf77695eSnicm extern const struct cmd_entry cmd_new_session_entry; 76bf77695eSnicm extern const struct cmd_entry cmd_new_window_entry; 77bf77695eSnicm extern const struct cmd_entry cmd_next_layout_entry; 78bf77695eSnicm extern const struct cmd_entry cmd_next_window_entry; 79bf77695eSnicm extern const struct cmd_entry cmd_paste_buffer_entry; 80bf77695eSnicm extern const struct cmd_entry cmd_pipe_pane_entry; 81bf77695eSnicm extern const struct cmd_entry cmd_previous_layout_entry; 82bf77695eSnicm extern const struct cmd_entry cmd_previous_window_entry; 83bf77695eSnicm extern const struct cmd_entry cmd_refresh_client_entry; 84bf77695eSnicm extern const struct cmd_entry cmd_rename_session_entry; 85bf77695eSnicm extern const struct cmd_entry cmd_rename_window_entry; 86bf77695eSnicm extern const struct cmd_entry cmd_resize_pane_entry; 877b470e93Snicm extern const struct cmd_entry cmd_resize_window_entry; 88bf77695eSnicm extern const struct cmd_entry cmd_respawn_pane_entry; 89bf77695eSnicm extern const struct cmd_entry cmd_respawn_window_entry; 90bf77695eSnicm extern const struct cmd_entry cmd_rotate_window_entry; 91bf77695eSnicm extern const struct cmd_entry cmd_run_shell_entry; 92bf77695eSnicm extern const struct cmd_entry cmd_save_buffer_entry; 93bf77695eSnicm extern const struct cmd_entry cmd_select_layout_entry; 94bf77695eSnicm extern const struct cmd_entry cmd_select_pane_entry; 95bf77695eSnicm extern const struct cmd_entry cmd_select_window_entry; 96bf77695eSnicm extern const struct cmd_entry cmd_send_keys_entry; 97bf77695eSnicm extern const struct cmd_entry cmd_send_prefix_entry; 988ab000fcSnicm extern const struct cmd_entry cmd_server_access_entry; 99bf77695eSnicm extern const struct cmd_entry cmd_set_buffer_entry; 100bf77695eSnicm extern const struct cmd_entry cmd_set_environment_entry; 1011443aefcSnicm extern const struct cmd_entry cmd_set_hook_entry; 102bf77695eSnicm extern const struct cmd_entry cmd_set_option_entry; 103bf77695eSnicm extern const struct cmd_entry cmd_set_window_option_entry; 104bf77695eSnicm extern const struct cmd_entry cmd_show_buffer_entry; 105bf77695eSnicm extern const struct cmd_entry cmd_show_environment_entry; 1061443aefcSnicm extern const struct cmd_entry cmd_show_hooks_entry; 107bf77695eSnicm extern const struct cmd_entry cmd_show_messages_entry; 108bf77695eSnicm extern const struct cmd_entry cmd_show_options_entry; 109bc5a8fc2Snicm extern const struct cmd_entry cmd_show_prompt_history_entry; 110bf77695eSnicm extern const struct cmd_entry cmd_show_window_options_entry; 111bf77695eSnicm extern const struct cmd_entry cmd_source_file_entry; 112bf77695eSnicm extern const struct cmd_entry cmd_split_window_entry; 113bf77695eSnicm extern const struct cmd_entry cmd_start_server_entry; 114bf77695eSnicm extern const struct cmd_entry cmd_suspend_client_entry; 115bf77695eSnicm extern const struct cmd_entry cmd_swap_pane_entry; 116bf77695eSnicm extern const struct cmd_entry cmd_swap_window_entry; 117bf77695eSnicm extern const struct cmd_entry cmd_switch_client_entry; 118bf77695eSnicm extern const struct cmd_entry cmd_unbind_key_entry; 119bf77695eSnicm extern const struct cmd_entry cmd_unlink_window_entry; 120bf77695eSnicm extern const struct cmd_entry cmd_wait_for_entry; 121bf77695eSnicm 122311827fbSnicm const struct cmd_entry *cmd_table[] = { 123311827fbSnicm &cmd_attach_session_entry, 124311827fbSnicm &cmd_bind_key_entry, 125311827fbSnicm &cmd_break_pane_entry, 1263373972cSnicm &cmd_capture_pane_entry, 1275f093833Snicm &cmd_choose_buffer_entry, 128be3558aeSnicm &cmd_choose_client_entry, 129e001d847Snicm &cmd_choose_tree_entry, 130311827fbSnicm &cmd_clear_history_entry, 131bc5a8fc2Snicm &cmd_clear_prompt_history_entry, 132311827fbSnicm &cmd_clock_mode_entry, 133311827fbSnicm &cmd_command_prompt_entry, 134311827fbSnicm &cmd_confirm_before_entry, 135311827fbSnicm &cmd_copy_mode_entry, 13667c16a7cSnicm &cmd_customize_mode_entry, 137311827fbSnicm &cmd_delete_buffer_entry, 138311827fbSnicm &cmd_detach_client_entry, 1393546f4c9Snicm &cmd_display_menu_entry, 1409629a58fSnicm &cmd_display_message_entry, 1410cac3d2dSnicm &cmd_display_popup_entry, 142669c539cSnicm &cmd_display_panes_entry, 143311827fbSnicm &cmd_find_window_entry, 144311827fbSnicm &cmd_has_session_entry, 1454983507eSnicm &cmd_if_shell_entry, 146572cd943Snicm &cmd_join_pane_entry, 147311827fbSnicm &cmd_kill_pane_entry, 148311827fbSnicm &cmd_kill_server_entry, 149311827fbSnicm &cmd_kill_session_entry, 150311827fbSnicm &cmd_kill_window_entry, 1512bb1dad1Snicm &cmd_last_pane_entry, 152311827fbSnicm &cmd_last_window_entry, 153311827fbSnicm &cmd_link_window_entry, 154311827fbSnicm &cmd_list_buffers_entry, 155311827fbSnicm &cmd_list_clients_entry, 156311827fbSnicm &cmd_list_commands_entry, 157311827fbSnicm &cmd_list_keys_entry, 15858d0df6fSnicm &cmd_list_panes_entry, 159311827fbSnicm &cmd_list_sessions_entry, 160311827fbSnicm &cmd_list_windows_entry, 161311827fbSnicm &cmd_load_buffer_entry, 162168ee6d3Snicm &cmd_lock_client_entry, 163311827fbSnicm &cmd_lock_server_entry, 164168ee6d3Snicm &cmd_lock_session_entry, 1659c03dbf0Snicm &cmd_move_pane_entry, 166311827fbSnicm &cmd_move_window_entry, 167311827fbSnicm &cmd_new_session_entry, 168311827fbSnicm &cmd_new_window_entry, 169311827fbSnicm &cmd_next_layout_entry, 170311827fbSnicm &cmd_next_window_entry, 171311827fbSnicm &cmd_paste_buffer_entry, 172095994acSnicm &cmd_pipe_pane_entry, 173311827fbSnicm &cmd_previous_layout_entry, 174311827fbSnicm &cmd_previous_window_entry, 175311827fbSnicm &cmd_refresh_client_entry, 176311827fbSnicm &cmd_rename_session_entry, 177311827fbSnicm &cmd_rename_window_entry, 178311827fbSnicm &cmd_resize_pane_entry, 1797b470e93Snicm &cmd_resize_window_entry, 180ff0002bdSnicm &cmd_respawn_pane_entry, 181311827fbSnicm &cmd_respawn_window_entry, 182311827fbSnicm &cmd_rotate_window_entry, 183e74cd7aaSnicm &cmd_run_shell_entry, 184311827fbSnicm &cmd_save_buffer_entry, 185311827fbSnicm &cmd_select_layout_entry, 186311827fbSnicm &cmd_select_pane_entry, 187311827fbSnicm &cmd_select_window_entry, 188311827fbSnicm &cmd_send_keys_entry, 189311827fbSnicm &cmd_send_prefix_entry, 1908ab000fcSnicm &cmd_server_access_entry, 191311827fbSnicm &cmd_set_buffer_entry, 1926f7d62ebSnicm &cmd_set_environment_entry, 1931443aefcSnicm &cmd_set_hook_entry, 194311827fbSnicm &cmd_set_option_entry, 195311827fbSnicm &cmd_set_window_option_entry, 196311827fbSnicm &cmd_show_buffer_entry, 1976f7d62ebSnicm &cmd_show_environment_entry, 1981443aefcSnicm &cmd_show_hooks_entry, 199dca899eaSnicm &cmd_show_messages_entry, 200311827fbSnicm &cmd_show_options_entry, 201bc5a8fc2Snicm &cmd_show_prompt_history_entry, 202311827fbSnicm &cmd_show_window_options_entry, 203311827fbSnicm &cmd_source_file_entry, 204311827fbSnicm &cmd_split_window_entry, 205311827fbSnicm &cmd_start_server_entry, 206311827fbSnicm &cmd_suspend_client_entry, 207311827fbSnicm &cmd_swap_pane_entry, 208311827fbSnicm &cmd_swap_window_entry, 209311827fbSnicm &cmd_switch_client_entry, 210311827fbSnicm &cmd_unbind_key_entry, 211311827fbSnicm &cmd_unlink_window_entry, 2128d127fbbSnicm &cmd_wait_for_entry, 213311827fbSnicm NULL 214311827fbSnicm }; 215311827fbSnicm 21690d7ba38Snicm /* Instance of a command. */ 21790d7ba38Snicm struct cmd { 21890d7ba38Snicm const struct cmd_entry *entry; 21990d7ba38Snicm struct args *args; 22090d7ba38Snicm u_int group; 22190d7ba38Snicm 22290d7ba38Snicm char *file; 22390d7ba38Snicm u_int line; 22490d7ba38Snicm 22590d7ba38Snicm TAILQ_ENTRY(cmd) qentry; 22690d7ba38Snicm }; 22790d7ba38Snicm TAILQ_HEAD(cmds, cmd); 22890d7ba38Snicm 22990d7ba38Snicm /* Next group number for new command list. */ 2309895c18aSnicm static u_int cmd_list_next_group = 1; 2319895c18aSnicm 23290d7ba38Snicm /* Log an argument vector. */ 2332732babfSnicm void printflike(3, 4) 2342732babfSnicm cmd_log_argv(int argc, char **argv, const char *fmt, ...) 23588c4d57dSnicm { 2362732babfSnicm char *prefix; 2372732babfSnicm va_list ap; 23888c4d57dSnicm int i; 23988c4d57dSnicm 2402732babfSnicm va_start(ap, fmt); 2412732babfSnicm xvasprintf(&prefix, fmt, ap); 2422732babfSnicm va_end(ap); 2432732babfSnicm 24488c4d57dSnicm for (i = 0; i < argc; i++) 24588c4d57dSnicm log_debug("%s: argv[%d]=%s", prefix, i, argv[i]); 2462732babfSnicm free(prefix); 24788c4d57dSnicm } 24888c4d57dSnicm 24990d7ba38Snicm /* Prepend to an argument vector. */ 250df6ab229Snicm void 251aab759e8Snicm cmd_prepend_argv(int *argc, char ***argv, const char *arg) 252df6ab229Snicm { 253df6ab229Snicm char **new_argv; 254df6ab229Snicm int i; 255df6ab229Snicm 256df6ab229Snicm new_argv = xreallocarray(NULL, (*argc) + 1, sizeof *new_argv); 257df6ab229Snicm new_argv[0] = xstrdup(arg); 258df6ab229Snicm for (i = 0; i < *argc; i++) 259df6ab229Snicm new_argv[1 + i] = (*argv)[i]; 260df6ab229Snicm 261df6ab229Snicm free(*argv); 262df6ab229Snicm *argv = new_argv; 263df6ab229Snicm (*argc)++; 264df6ab229Snicm } 265df6ab229Snicm 26690d7ba38Snicm /* Append to an argument vector. */ 267df6ab229Snicm void 268aab759e8Snicm cmd_append_argv(int *argc, char ***argv, const char *arg) 269df6ab229Snicm { 270df6ab229Snicm *argv = xreallocarray(*argv, (*argc) + 1, sizeof **argv); 271df6ab229Snicm (*argv)[(*argc)++] = xstrdup(arg); 272df6ab229Snicm } 273df6ab229Snicm 27490d7ba38Snicm /* Pack an argument vector up into a buffer. */ 275454be688Snicm int 276454be688Snicm cmd_pack_argv(int argc, char **argv, char *buf, size_t len) 277454be688Snicm { 278454be688Snicm size_t arglen; 279454be688Snicm int i; 280454be688Snicm 2817184a3d6Snicm if (argc == 0) 2827184a3d6Snicm return (0); 2832732babfSnicm cmd_log_argv(argc, argv, "%s", __func__); 2847184a3d6Snicm 285454be688Snicm *buf = '\0'; 286454be688Snicm for (i = 0; i < argc; i++) { 287454be688Snicm if (strlcpy(buf, argv[i], len) >= len) 288454be688Snicm return (-1); 289454be688Snicm arglen = strlen(argv[i]) + 1; 290454be688Snicm buf += arglen; 291454be688Snicm len -= arglen; 292454be688Snicm } 293454be688Snicm 294454be688Snicm return (0); 295454be688Snicm } 296454be688Snicm 29790d7ba38Snicm /* Unpack an argument vector from a packed buffer. */ 298454be688Snicm int 299454be688Snicm cmd_unpack_argv(char *buf, size_t len, int argc, char ***argv) 300454be688Snicm { 301454be688Snicm int i; 302454be688Snicm size_t arglen; 303454be688Snicm 304454be688Snicm if (argc == 0) 305454be688Snicm return (0); 306454be688Snicm *argv = xcalloc(argc, sizeof **argv); 307454be688Snicm 308454be688Snicm buf[len - 1] = '\0'; 309454be688Snicm for (i = 0; i < argc; i++) { 310454be688Snicm if (len == 0) { 311454be688Snicm cmd_free_argv(argc, *argv); 312454be688Snicm return (-1); 313454be688Snicm } 314454be688Snicm 315454be688Snicm arglen = strlen(buf) + 1; 316454be688Snicm (*argv)[i] = xstrdup(buf); 31788c4d57dSnicm 318454be688Snicm buf += arglen; 319454be688Snicm len -= arglen; 320454be688Snicm } 3212732babfSnicm cmd_log_argv(argc, *argv, "%s", __func__); 322454be688Snicm 323454be688Snicm return (0); 324454be688Snicm } 325454be688Snicm 32690d7ba38Snicm /* Copy an argument vector, ensuring it is terminated by NULL. */ 327a0c8ba61Snicm char ** 328b2eee475Snicm cmd_copy_argv(int argc, char **argv) 329a0c8ba61Snicm { 330a0c8ba61Snicm char **new_argv; 331a0c8ba61Snicm int i; 332a0c8ba61Snicm 333a0c8ba61Snicm if (argc == 0) 334a0c8ba61Snicm return (NULL); 33558034113Snicm new_argv = xcalloc(argc + 1, sizeof *new_argv); 336a0c8ba61Snicm for (i = 0; i < argc; i++) { 337a0c8ba61Snicm if (argv[i] != NULL) 338a0c8ba61Snicm new_argv[i] = xstrdup(argv[i]); 339a0c8ba61Snicm } 340a0c8ba61Snicm return (new_argv); 341a0c8ba61Snicm } 342a0c8ba61Snicm 34390d7ba38Snicm /* Free an argument vector. */ 344454be688Snicm void 345454be688Snicm cmd_free_argv(int argc, char **argv) 346454be688Snicm { 347454be688Snicm int i; 348454be688Snicm 349454be688Snicm if (argc == 0) 350454be688Snicm return; 3517d053cf9Snicm for (i = 0; i < argc; i++) 3527d053cf9Snicm free(argv[i]); 3537d053cf9Snicm free(argv); 354454be688Snicm } 355454be688Snicm 35690d7ba38Snicm /* Convert argument vector to a string. */ 35758034113Snicm char * 35858034113Snicm cmd_stringify_argv(int argc, char **argv) 35958034113Snicm { 3602a840c62Snicm char *buf = NULL, *s; 3612a840c62Snicm size_t len = 0; 36258034113Snicm int i; 36358034113Snicm 36458034113Snicm if (argc == 0) 36558034113Snicm return (xstrdup("")); 36658034113Snicm 36758034113Snicm for (i = 0; i < argc; i++) { 3682a840c62Snicm s = args_escape(argv[i]); 3692a840c62Snicm log_debug("%s: %u %s = %s", __func__, i, argv[i], s); 3702a840c62Snicm 3712a840c62Snicm len += strlen(s) + 1; 37264cf113cSnicm buf = xrealloc(buf, len); 37358034113Snicm 37458034113Snicm if (i == 0) 37558034113Snicm *buf = '\0'; 37658034113Snicm else 37758034113Snicm strlcat(buf, " ", len); 3782a840c62Snicm strlcat(buf, s, len); 3792a840c62Snicm 3802a840c62Snicm free(s); 38158034113Snicm } 38258034113Snicm return (buf); 38358034113Snicm } 38458034113Snicm 38590d7ba38Snicm /* Get entry for command. */ 38690d7ba38Snicm const struct cmd_entry * 38790d7ba38Snicm cmd_get_entry(struct cmd *cmd) 38890d7ba38Snicm { 38990d7ba38Snicm return (cmd->entry); 39090d7ba38Snicm } 39190d7ba38Snicm 39290d7ba38Snicm /* Get arguments for command. */ 39390d7ba38Snicm struct args * 39490d7ba38Snicm cmd_get_args(struct cmd *cmd) 39590d7ba38Snicm { 39690d7ba38Snicm return (cmd->args); 39790d7ba38Snicm } 39890d7ba38Snicm 399c1e0bdabSnicm /* Get group for command. */ 400c1e0bdabSnicm u_int 401c1e0bdabSnicm cmd_get_group(struct cmd *cmd) 402c1e0bdabSnicm { 403c1e0bdabSnicm return (cmd->group); 404c1e0bdabSnicm } 405c1e0bdabSnicm 40690d7ba38Snicm /* Get file and line for command. */ 40790d7ba38Snicm void 40890d7ba38Snicm cmd_get_source(struct cmd *cmd, const char **file, u_int *line) 40990d7ba38Snicm { 41090d7ba38Snicm if (file != NULL) 41190d7ba38Snicm *file = cmd->file; 41290d7ba38Snicm if (line != NULL) 41390d7ba38Snicm *line = cmd->line; 41490d7ba38Snicm } 41590d7ba38Snicm 41690d7ba38Snicm /* Look for an alias for a command. */ 417df6ab229Snicm char * 418df6ab229Snicm cmd_get_alias(const char *name) 419a3834af8Snicm { 420a3834af8Snicm struct options_entry *o; 42139052edfSnicm struct options_array_item *a; 42284306383Snicm union options_value *ov; 423df6ab229Snicm size_t wanted, n; 424df6ab229Snicm const char *equals; 425a3834af8Snicm 426a3834af8Snicm o = options_get_only(global_options, "command-alias"); 42739052edfSnicm if (o == NULL) 428df6ab229Snicm return (NULL); 429df6ab229Snicm wanted = strlen(name); 430a3834af8Snicm 43139052edfSnicm a = options_array_first(o); 43239052edfSnicm while (a != NULL) { 43384306383Snicm ov = options_array_item_value(a); 434df6ab229Snicm 435df6ab229Snicm equals = strchr(ov->string, '='); 436df6ab229Snicm if (equals != NULL) { 437df6ab229Snicm n = equals - ov->string; 438df6ab229Snicm if (n == wanted && strncmp(name, ov->string, n) == 0) 439df6ab229Snicm return (xstrdup(equals + 1)); 440df6ab229Snicm } 441df6ab229Snicm 44239052edfSnicm a = options_array_next(a); 44339052edfSnicm } 444df6ab229Snicm return (NULL); 445df6ab229Snicm } 446a3834af8Snicm 44790d7ba38Snicm /* Look up a command entry by name. */ 448*b6124414Snicm const struct cmd_entry * 449df6ab229Snicm cmd_find(const char *name, char **cause) 450df6ab229Snicm { 451df6ab229Snicm const struct cmd_entry **loop, *entry, *found = NULL; 452df6ab229Snicm int ambiguous; 4534512d27dSnicm char s[8192]; 454a3834af8Snicm 455df6ab229Snicm ambiguous = 0; 456df6ab229Snicm for (loop = cmd_table; *loop != NULL; loop++) { 457df6ab229Snicm entry = *loop; 458df6ab229Snicm if (entry->alias != NULL && strcmp(entry->alias, name) == 0) { 459df6ab229Snicm ambiguous = 0; 460df6ab229Snicm found = entry; 461df6ab229Snicm break; 462df6ab229Snicm } 463a3834af8Snicm 464df6ab229Snicm if (strncmp(entry->name, name, strlen(name)) != 0) 465df6ab229Snicm continue; 466df6ab229Snicm if (found != NULL) 467df6ab229Snicm ambiguous = 1; 468df6ab229Snicm found = entry; 469a3834af8Snicm 470df6ab229Snicm if (strcmp(entry->name, name) == 0) 471df6ab229Snicm break; 472df6ab229Snicm } 473df6ab229Snicm if (ambiguous) 474df6ab229Snicm goto ambiguous; 475df6ab229Snicm if (found == NULL) { 476df6ab229Snicm xasprintf(cause, "unknown command: %s", name); 477df6ab229Snicm return (NULL); 478df6ab229Snicm } 479df6ab229Snicm return (found); 480a3834af8Snicm 481df6ab229Snicm ambiguous: 482df6ab229Snicm *s = '\0'; 483df6ab229Snicm for (loop = cmd_table; *loop != NULL; loop++) { 484df6ab229Snicm entry = *loop; 485df6ab229Snicm if (strncmp(entry->name, name, strlen(name)) != 0) 486df6ab229Snicm continue; 487df6ab229Snicm if (strlcat(s, entry->name, sizeof s) >= sizeof s) 488df6ab229Snicm break; 489df6ab229Snicm if (strlcat(s, ", ", sizeof s) >= sizeof s) 490df6ab229Snicm break; 491df6ab229Snicm } 492df6ab229Snicm s[strlen(s) - 2] = '\0'; 493df6ab229Snicm xasprintf(cause, "ambiguous command: %s, could be: %s", name, s); 494df6ab229Snicm return (NULL); 495a3834af8Snicm } 496a3834af8Snicm 49790d7ba38Snicm /* Parse a single command from an argument vector. */ 498311827fbSnicm struct cmd * 499fb147d85Snicm cmd_parse(struct args_value *values, u_int count, const char *file, u_int line, 500fb147d85Snicm char **cause) 501311827fbSnicm { 502df6ab229Snicm const struct cmd_entry *entry; 503311827fbSnicm struct cmd *cmd; 504ca7befccSnicm struct args *args; 505dddb32eeSnicm char *error = NULL; 506311827fbSnicm 507fb147d85Snicm if (count == 0 || values[0].type != ARGS_STRING) { 508fc2c8743Snicm xasprintf(cause, "no command"); 509311827fbSnicm return (NULL); 510fc2c8743Snicm } 511fb147d85Snicm entry = cmd_find(values[0].string, cause); 512df6ab229Snicm if (entry == NULL) 513311827fbSnicm return (NULL); 514311827fbSnicm 515e4c0b811Snicm args = args_parse(&entry->args, values, count, &error); 516e4c0b811Snicm if (args == NULL && error == NULL) { 517fb147d85Snicm xasprintf(cause, "usage: %s %s", entry->name, entry->usage); 518fb147d85Snicm return (NULL); 519fb147d85Snicm } 520e4c0b811Snicm if (args == NULL) { 521e4c0b811Snicm xasprintf(cause, "command %s: %s", entry->name, error); 522e4c0b811Snicm free(error); 523e4c0b811Snicm return (NULL); 524e4c0b811Snicm } 525311827fbSnicm 526175d36ccSnicm cmd = xcalloc(1, sizeof *cmd); 527311827fbSnicm cmd->entry = entry; 528ca7befccSnicm cmd->args = args; 529175d36ccSnicm 530175d36ccSnicm if (file != NULL) 531175d36ccSnicm cmd->file = xstrdup(file); 532175d36ccSnicm cmd->line = line; 533175d36ccSnicm 534df6ab229Snicm return (cmd); 535311827fbSnicm } 536311827fbSnicm 53790d7ba38Snicm /* Free a command. */ 538df6ab229Snicm void 539df6ab229Snicm cmd_free(struct cmd *cmd) 540df6ab229Snicm { 541df6ab229Snicm free(cmd->file); 542df6ab229Snicm 543df6ab229Snicm args_free(cmd->args); 544df6ab229Snicm free(cmd); 545df6ab229Snicm } 546df6ab229Snicm 547d8b32369Snicm /* Copy a command. */ 548d8b32369Snicm struct cmd * 549d8b32369Snicm cmd_copy(struct cmd *cmd, int argc, char **argv) 550d8b32369Snicm { 551d8b32369Snicm struct cmd *new_cmd; 552d8b32369Snicm 553d8b32369Snicm new_cmd = xcalloc(1, sizeof *new_cmd); 554d8b32369Snicm new_cmd->entry = cmd->entry; 555d8b32369Snicm new_cmd->args = args_copy(cmd->args, argc, argv); 556d8b32369Snicm 557d8b32369Snicm if (cmd->file != NULL) 558d8b32369Snicm new_cmd->file = xstrdup(cmd->file); 559d8b32369Snicm new_cmd->line = cmd->line; 560d8b32369Snicm 561d8b32369Snicm return (new_cmd); 562d8b32369Snicm } 563d8b32369Snicm 56490d7ba38Snicm /* Get a command as a string. */ 5658ae71cbbSnicm char * 5668ae71cbbSnicm cmd_print(struct cmd *cmd) 567311827fbSnicm { 5688ae71cbbSnicm char *out, *s; 569ca7befccSnicm 5708ae71cbbSnicm s = args_print(cmd->args); 5718ae71cbbSnicm if (*s != '\0') 5728ae71cbbSnicm xasprintf(&out, "%s %s", cmd->entry->name, s); 5733435deaeSnicm else 5748ae71cbbSnicm out = xstrdup(cmd->entry->name); 5758ae71cbbSnicm free(s); 5768ae71cbbSnicm 5778ae71cbbSnicm return (out); 578311827fbSnicm } 579311827fbSnicm 58090d7ba38Snicm /* Create a new command list. */ 5819895c18aSnicm struct cmd_list * 5829895c18aSnicm cmd_list_new(void) 5839895c18aSnicm { 5849895c18aSnicm struct cmd_list *cmdlist; 5859895c18aSnicm 5869895c18aSnicm cmdlist = xcalloc(1, sizeof *cmdlist); 5879895c18aSnicm cmdlist->references = 1; 5889895c18aSnicm cmdlist->group = cmd_list_next_group++; 58990d7ba38Snicm cmdlist->list = xcalloc(1, sizeof *cmdlist->list); 59090d7ba38Snicm TAILQ_INIT(cmdlist->list); 5919895c18aSnicm return (cmdlist); 5929895c18aSnicm } 5939895c18aSnicm 59490d7ba38Snicm /* Append a command to a command list. */ 5959895c18aSnicm void 5969895c18aSnicm cmd_list_append(struct cmd_list *cmdlist, struct cmd *cmd) 5979895c18aSnicm { 5989895c18aSnicm cmd->group = cmdlist->group; 59990d7ba38Snicm TAILQ_INSERT_TAIL(cmdlist->list, cmd, qentry); 6009895c18aSnicm } 6019895c18aSnicm 602da650dc0Snicm /* Append all commands from one list to another. */ 603da650dc0Snicm void 604da650dc0Snicm cmd_list_append_all(struct cmd_list *cmdlist, struct cmd_list *from) 605da650dc0Snicm { 606da650dc0Snicm struct cmd *cmd; 607da650dc0Snicm 608da650dc0Snicm TAILQ_FOREACH(cmd, from->list, qentry) 609da650dc0Snicm cmd->group = cmdlist->group; 610da650dc0Snicm TAILQ_CONCAT(cmdlist->list, from->list, qentry); 611da650dc0Snicm } 612da650dc0Snicm 613da650dc0Snicm /* Move all commands from one command list to another. */ 6149895c18aSnicm void 6159895c18aSnicm cmd_list_move(struct cmd_list *cmdlist, struct cmd_list *from) 6169895c18aSnicm { 6178cfec971Sbket TAILQ_CONCAT(cmdlist->list, from->list, qentry); 6189895c18aSnicm cmdlist->group = cmd_list_next_group++; 6199895c18aSnicm } 6209895c18aSnicm 62190d7ba38Snicm /* Free a command list. */ 6229895c18aSnicm void 6239895c18aSnicm cmd_list_free(struct cmd_list *cmdlist) 6249895c18aSnicm { 6259895c18aSnicm struct cmd *cmd, *cmd1; 6269895c18aSnicm 6279895c18aSnicm if (--cmdlist->references != 0) 6289895c18aSnicm return; 6299895c18aSnicm 63090d7ba38Snicm TAILQ_FOREACH_SAFE(cmd, cmdlist->list, qentry, cmd1) { 63190d7ba38Snicm TAILQ_REMOVE(cmdlist->list, cmd, qentry); 6329895c18aSnicm cmd_free(cmd); 6339895c18aSnicm } 63490d7ba38Snicm free(cmdlist->list); 6359895c18aSnicm free(cmdlist); 6369895c18aSnicm } 6379895c18aSnicm 638d8b32369Snicm /* Copy a command list, expanding %s in arguments. */ 639d8b32369Snicm struct cmd_list * 640d8b32369Snicm cmd_list_copy(struct cmd_list *cmdlist, int argc, char **argv) 641d8b32369Snicm { 642d8b32369Snicm struct cmd *cmd; 643d8b32369Snicm struct cmd_list *new_cmdlist; 644d8b32369Snicm struct cmd *new_cmd; 645d8b32369Snicm u_int group = cmdlist->group; 646d8b32369Snicm char *s; 647d8b32369Snicm 648d8b32369Snicm s = cmd_list_print(cmdlist, 0); 649d8b32369Snicm log_debug("%s: %s", __func__, s); 650d8b32369Snicm free(s); 651d8b32369Snicm 652d8b32369Snicm new_cmdlist = cmd_list_new(); 653d8b32369Snicm TAILQ_FOREACH(cmd, cmdlist->list, qentry) { 654d8b32369Snicm if (cmd->group != group) { 655d8b32369Snicm new_cmdlist->group = cmd_list_next_group++; 656d8b32369Snicm group = cmd->group; 657d8b32369Snicm } 658d8b32369Snicm new_cmd = cmd_copy(cmd, argc, argv); 659d8b32369Snicm cmd_list_append(new_cmdlist, new_cmd); 660d8b32369Snicm } 661d8b32369Snicm 662d8b32369Snicm s = cmd_list_print(new_cmdlist, 0); 663d8b32369Snicm log_debug("%s: %s", __func__, s); 664d8b32369Snicm free(s); 665d8b32369Snicm 666d8b32369Snicm return (new_cmdlist); 667d8b32369Snicm } 668d8b32369Snicm 66990d7ba38Snicm /* Get a command list as a string. */ 6709895c18aSnicm char * 6719895c18aSnicm cmd_list_print(struct cmd_list *cmdlist, int escaped) 6729895c18aSnicm { 673a49ab104Snicm struct cmd *cmd, *next; 6749895c18aSnicm char *buf, *this; 6759895c18aSnicm size_t len; 6769895c18aSnicm 6779895c18aSnicm len = 1; 6789895c18aSnicm buf = xcalloc(1, len); 6799895c18aSnicm 68090d7ba38Snicm TAILQ_FOREACH(cmd, cmdlist->list, qentry) { 6819895c18aSnicm this = cmd_print(cmd); 6829895c18aSnicm 683a49ab104Snicm len += strlen(this) + 6; 6849895c18aSnicm buf = xrealloc(buf, len); 6859895c18aSnicm 6869895c18aSnicm strlcat(buf, this, len); 687a49ab104Snicm 688a49ab104Snicm next = TAILQ_NEXT(cmd, qentry); 689a49ab104Snicm if (next != NULL) { 690a49ab104Snicm if (cmd->group != next->group) { 691a49ab104Snicm if (escaped) 692a49ab104Snicm strlcat(buf, " \\;\\; ", len); 693a49ab104Snicm else 694a49ab104Snicm strlcat(buf, " ;; ", len); 695a49ab104Snicm } else { 6969895c18aSnicm if (escaped) 6979895c18aSnicm strlcat(buf, " \\; ", len); 6989895c18aSnicm else 6999895c18aSnicm strlcat(buf, " ; ", len); 7009895c18aSnicm } 701a49ab104Snicm } 7029895c18aSnicm 7039895c18aSnicm free(this); 7049895c18aSnicm } 7059895c18aSnicm 7069895c18aSnicm return (buf); 7079895c18aSnicm } 7089895c18aSnicm 70990d7ba38Snicm /* Get first command in list. */ 71090d7ba38Snicm struct cmd * 711c1e0bdabSnicm cmd_list_first(struct cmd_list *cmdlist) 71290d7ba38Snicm { 713c1e0bdabSnicm return (TAILQ_FIRST(cmdlist->list)); 71490d7ba38Snicm } 71590d7ba38Snicm 71690d7ba38Snicm /* Get next command in list. */ 71790d7ba38Snicm struct cmd * 718c1e0bdabSnicm cmd_list_next(struct cmd *cmd) 71990d7ba38Snicm { 720c1e0bdabSnicm return (TAILQ_NEXT(cmd, qentry)); 72190d7ba38Snicm } 72290d7ba38Snicm 72390d7ba38Snicm /* Do all of the commands in this command list have this flag? */ 72490d7ba38Snicm int 72590d7ba38Snicm cmd_list_all_have(struct cmd_list *cmdlist, int flag) 72690d7ba38Snicm { 72790d7ba38Snicm struct cmd *cmd; 72890d7ba38Snicm 72990d7ba38Snicm TAILQ_FOREACH(cmd, cmdlist->list, qentry) { 73090d7ba38Snicm if (~cmd->entry->flags & flag) 73190d7ba38Snicm return (0); 73290d7ba38Snicm } 73390d7ba38Snicm return (1); 73490d7ba38Snicm } 73590d7ba38Snicm 73690d7ba38Snicm /* Do any of the commands in this command list have this flag? */ 73790d7ba38Snicm int 73890d7ba38Snicm cmd_list_any_have(struct cmd_list *cmdlist, int flag) 73990d7ba38Snicm { 74090d7ba38Snicm struct cmd *cmd; 74190d7ba38Snicm 74290d7ba38Snicm TAILQ_FOREACH(cmd, cmdlist->list, qentry) { 74390d7ba38Snicm if (cmd->entry->flags & flag) 74490d7ba38Snicm return (1); 74590d7ba38Snicm } 74690d7ba38Snicm return (0); 74790d7ba38Snicm } 74890d7ba38Snicm 749e048bb79Snicm /* Adjust current mouse position for a pane. */ 750e048bb79Snicm int 751e048bb79Snicm cmd_mouse_at(struct window_pane *wp, struct mouse_event *m, u_int *xp, 752e048bb79Snicm u_int *yp, int last) 753e048bb79Snicm { 754e048bb79Snicm u_int x, y; 755e048bb79Snicm 756e048bb79Snicm if (last) { 757c7de2738Snicm x = m->lx + m->ox; 758c7de2738Snicm y = m->ly + m->oy; 759e048bb79Snicm } else { 760a489e4deSnicm x = m->x + m->ox; 761a489e4deSnicm y = m->y + m->oy; 762e048bb79Snicm } 763c7de2738Snicm log_debug("%s: x=%u, y=%u%s", __func__, x, y, last ? " (last)" : ""); 764e048bb79Snicm 7653cedae0cSnicm if (m->statusat == 0 && y >= m->statuslines) 7663cedae0cSnicm y -= m->statuslines; 767e048bb79Snicm 768e048bb79Snicm if (x < wp->xoff || x >= wp->xoff + wp->sx) 769e048bb79Snicm return (-1); 770e048bb79Snicm if (y < wp->yoff || y >= wp->yoff + wp->sy) 771e048bb79Snicm return (-1); 772e048bb79Snicm 773ba770029Snicm if (xp != NULL) 774e048bb79Snicm *xp = x - wp->xoff; 775ba770029Snicm if (yp != NULL) 776e048bb79Snicm *yp = y - wp->yoff; 777e048bb79Snicm return (0); 778e048bb79Snicm } 779e048bb79Snicm 780e048bb79Snicm /* Get current mouse window if any. */ 781e048bb79Snicm struct winlink * 782e048bb79Snicm cmd_mouse_window(struct mouse_event *m, struct session **sp) 783e048bb79Snicm { 784e048bb79Snicm struct session *s; 785e048bb79Snicm struct window *w; 7869b9cde72Snicm struct winlink *wl; 787e048bb79Snicm 7889b9cde72Snicm if (!m->valid) 789e048bb79Snicm return (NULL); 7909b9cde72Snicm if (m->s == -1 || (s = session_find_by_id(m->s)) == NULL) 791e048bb79Snicm return (NULL); 7929b9cde72Snicm if (m->w == -1) 7939b9cde72Snicm wl = s->curw; 7949b9cde72Snicm else { 795e048bb79Snicm if ((w = window_find_by_id(m->w)) == NULL) 796e048bb79Snicm return (NULL); 7979b9cde72Snicm wl = winlink_find_by_window(&s->windows, w); 7989b9cde72Snicm } 799e048bb79Snicm if (sp != NULL) 800e048bb79Snicm *sp = s; 8019b9cde72Snicm return (wl); 802e048bb79Snicm } 803e048bb79Snicm 804e048bb79Snicm /* Get current mouse pane if any. */ 805e048bb79Snicm struct window_pane * 806f65f2164Snicm cmd_mouse_pane(struct mouse_event *m, struct session **sp, 807f65f2164Snicm struct winlink **wlp) 808e048bb79Snicm { 809e048bb79Snicm struct winlink *wl; 810e048bb79Snicm struct window_pane *wp; 811e048bb79Snicm 812e048bb79Snicm if ((wl = cmd_mouse_window(m, sp)) == NULL) 813e048bb79Snicm return (NULL); 8145355e038Snicm if (m->wp == -1) 8155355e038Snicm wp = wl->window->active; 8165355e038Snicm else { 817e048bb79Snicm if ((wp = window_pane_find_by_id(m->wp)) == NULL) 818e048bb79Snicm return (NULL); 819e048bb79Snicm if (!window_has_pane(wl->window, wp)) 820e048bb79Snicm return (NULL); 8215355e038Snicm } 822e048bb79Snicm 823e048bb79Snicm if (wlp != NULL) 824e048bb79Snicm *wlp = wl; 825e048bb79Snicm return (wp); 826e048bb79Snicm } 827e048bb79Snicm 828be3558aeSnicm /* Replace the first %% or %idx in template by s. */ 829be3558aeSnicm char * 8301728e860Snicm cmd_template_replace(const char *template, const char *s, int idx) 831be3558aeSnicm { 8321728e860Snicm char ch, *buf; 833abca6b05Snicm const char *ptr, *cp, quote[] = "\"\\$;~"; 83480774623Snicm int replaced, quoted; 835be3558aeSnicm size_t len; 836be3558aeSnicm 837e55ca793Snicm if (strchr(template, '%') == NULL) 838be3558aeSnicm return (xstrdup(template)); 839be3558aeSnicm 840be3558aeSnicm buf = xmalloc(1); 841be3558aeSnicm *buf = '\0'; 842be3558aeSnicm len = 0; 843be3558aeSnicm replaced = 0; 844be3558aeSnicm 845be3558aeSnicm ptr = template; 846be3558aeSnicm while (*ptr != '\0') { 847be3558aeSnicm switch (ch = *ptr++) { 848be3558aeSnicm case '%': 849be3558aeSnicm if (*ptr < '1' || *ptr > '9' || *ptr - '0' != idx) { 850be3558aeSnicm if (*ptr != '%' || replaced) 851be3558aeSnicm break; 852be3558aeSnicm replaced = 1; 853be3558aeSnicm } 854be3558aeSnicm ptr++; 855be3558aeSnicm 85680774623Snicm quoted = (*ptr == '%'); 85780774623Snicm if (quoted) 85880774623Snicm ptr++; 85980774623Snicm 86038a4b723Snicm buf = xrealloc(buf, len + (strlen(s) * 3) + 1); 86180774623Snicm for (cp = s; *cp != '\0'; cp++) { 862785b9721Snicm if (quoted && strchr(quote, *cp) != NULL) 86380774623Snicm buf[len++] = '\\'; 86480774623Snicm buf[len++] = *cp; 86580774623Snicm } 86680774623Snicm buf[len] = '\0'; 867be3558aeSnicm continue; 868be3558aeSnicm } 86964cf113cSnicm buf = xrealloc(buf, len + 2); 870be3558aeSnicm buf[len++] = ch; 871be3558aeSnicm buf[len] = '\0'; 872be3558aeSnicm } 873be3558aeSnicm 874be3558aeSnicm return (buf); 875be3558aeSnicm } 876