xref: /openbsd-src/usr.bin/tmux/cmd.c (revision b6124414e332f14e0fa5b90d86530cc270c72412)
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