1/* $OpenBSD: cmd.c,v 1.176 2024/05/18 08:50:11 jsg Exp $ */
2
3/*
4 * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <sys/types.h>
20#include <sys/time.h>
21
22#include <fnmatch.h>
23#include <paths.h>
24#include <pwd.h>
25#include <stdlib.h>
26#include <string.h>
27#include <unistd.h>
28
29#include "tmux.h"
30
31extern const struct cmd_entry cmd_attach_session_entry;
32extern const struct cmd_entry cmd_bind_key_entry;
33extern const struct cmd_entry cmd_break_pane_entry;
34extern const struct cmd_entry cmd_capture_pane_entry;
35extern const struct cmd_entry cmd_choose_buffer_entry;
36extern const struct cmd_entry cmd_choose_client_entry;
37extern const struct cmd_entry cmd_choose_tree_entry;
38extern const struct cmd_entry cmd_clear_history_entry;
39extern const struct cmd_entry cmd_clear_prompt_history_entry;
40extern const struct cmd_entry cmd_clock_mode_entry;
41extern const struct cmd_entry cmd_command_prompt_entry;
42extern const struct cmd_entry cmd_confirm_before_entry;
43extern const struct cmd_entry cmd_copy_mode_entry;
44extern const struct cmd_entry cmd_customize_mode_entry;
45extern const struct cmd_entry cmd_delete_buffer_entry;
46extern const struct cmd_entry cmd_detach_client_entry;
47extern const struct cmd_entry cmd_display_menu_entry;
48extern const struct cmd_entry cmd_display_message_entry;
49extern const struct cmd_entry cmd_display_popup_entry;
50extern const struct cmd_entry cmd_display_panes_entry;
51extern const struct cmd_entry cmd_find_window_entry;
52extern const struct cmd_entry cmd_has_session_entry;
53extern const struct cmd_entry cmd_if_shell_entry;
54extern const struct cmd_entry cmd_join_pane_entry;
55extern const struct cmd_entry cmd_kill_pane_entry;
56extern const struct cmd_entry cmd_kill_server_entry;
57extern const struct cmd_entry cmd_kill_session_entry;
58extern const struct cmd_entry cmd_kill_window_entry;
59extern const struct cmd_entry cmd_last_pane_entry;
60extern const struct cmd_entry cmd_last_window_entry;
61extern const struct cmd_entry cmd_link_window_entry;
62extern const struct cmd_entry cmd_list_buffers_entry;
63extern const struct cmd_entry cmd_list_clients_entry;
64extern const struct cmd_entry cmd_list_commands_entry;
65extern const struct cmd_entry cmd_list_keys_entry;
66extern const struct cmd_entry cmd_list_panes_entry;
67extern const struct cmd_entry cmd_list_sessions_entry;
68extern const struct cmd_entry cmd_list_windows_entry;
69extern const struct cmd_entry cmd_load_buffer_entry;
70extern const struct cmd_entry cmd_lock_client_entry;
71extern const struct cmd_entry cmd_lock_server_entry;
72extern const struct cmd_entry cmd_lock_session_entry;
73extern const struct cmd_entry cmd_move_pane_entry;
74extern const struct cmd_entry cmd_move_window_entry;
75extern const struct cmd_entry cmd_new_session_entry;
76extern const struct cmd_entry cmd_new_window_entry;
77extern const struct cmd_entry cmd_next_layout_entry;
78extern const struct cmd_entry cmd_next_window_entry;
79extern const struct cmd_entry cmd_paste_buffer_entry;
80extern const struct cmd_entry cmd_pipe_pane_entry;
81extern const struct cmd_entry cmd_previous_layout_entry;
82extern const struct cmd_entry cmd_previous_window_entry;
83extern const struct cmd_entry cmd_refresh_client_entry;
84extern const struct cmd_entry cmd_rename_session_entry;
85extern const struct cmd_entry cmd_rename_window_entry;
86extern const struct cmd_entry cmd_resize_pane_entry;
87extern const struct cmd_entry cmd_resize_window_entry;
88extern const struct cmd_entry cmd_respawn_pane_entry;
89extern const struct cmd_entry cmd_respawn_window_entry;
90extern const struct cmd_entry cmd_rotate_window_entry;
91extern const struct cmd_entry cmd_run_shell_entry;
92extern const struct cmd_entry cmd_save_buffer_entry;
93extern const struct cmd_entry cmd_select_layout_entry;
94extern const struct cmd_entry cmd_select_pane_entry;
95extern const struct cmd_entry cmd_select_window_entry;
96extern const struct cmd_entry cmd_send_keys_entry;
97extern const struct cmd_entry cmd_send_prefix_entry;
98extern const struct cmd_entry cmd_server_access_entry;
99extern const struct cmd_entry cmd_set_buffer_entry;
100extern const struct cmd_entry cmd_set_environment_entry;
101extern const struct cmd_entry cmd_set_hook_entry;
102extern const struct cmd_entry cmd_set_option_entry;
103extern const struct cmd_entry cmd_set_window_option_entry;
104extern const struct cmd_entry cmd_show_buffer_entry;
105extern const struct cmd_entry cmd_show_environment_entry;
106extern const struct cmd_entry cmd_show_hooks_entry;
107extern const struct cmd_entry cmd_show_messages_entry;
108extern const struct cmd_entry cmd_show_options_entry;
109extern const struct cmd_entry cmd_show_prompt_history_entry;
110extern const struct cmd_entry cmd_show_window_options_entry;
111extern const struct cmd_entry cmd_source_file_entry;
112extern const struct cmd_entry cmd_split_window_entry;
113extern const struct cmd_entry cmd_start_server_entry;
114extern const struct cmd_entry cmd_suspend_client_entry;
115extern const struct cmd_entry cmd_swap_pane_entry;
116extern const struct cmd_entry cmd_swap_window_entry;
117extern const struct cmd_entry cmd_switch_client_entry;
118extern const struct cmd_entry cmd_unbind_key_entry;
119extern const struct cmd_entry cmd_unlink_window_entry;
120extern const struct cmd_entry cmd_wait_for_entry;
121
122const struct cmd_entry *cmd_table[] = {
123	&cmd_attach_session_entry,
124	&cmd_bind_key_entry,
125	&cmd_break_pane_entry,
126	&cmd_capture_pane_entry,
127	&cmd_choose_buffer_entry,
128	&cmd_choose_client_entry,
129	&cmd_choose_tree_entry,
130	&cmd_clear_history_entry,
131	&cmd_clear_prompt_history_entry,
132	&cmd_clock_mode_entry,
133	&cmd_command_prompt_entry,
134	&cmd_confirm_before_entry,
135	&cmd_copy_mode_entry,
136	&cmd_customize_mode_entry,
137	&cmd_delete_buffer_entry,
138	&cmd_detach_client_entry,
139	&cmd_display_menu_entry,
140	&cmd_display_message_entry,
141	&cmd_display_popup_entry,
142	&cmd_display_panes_entry,
143	&cmd_find_window_entry,
144	&cmd_has_session_entry,
145	&cmd_if_shell_entry,
146	&cmd_join_pane_entry,
147	&cmd_kill_pane_entry,
148	&cmd_kill_server_entry,
149	&cmd_kill_session_entry,
150	&cmd_kill_window_entry,
151	&cmd_last_pane_entry,
152	&cmd_last_window_entry,
153	&cmd_link_window_entry,
154	&cmd_list_buffers_entry,
155	&cmd_list_clients_entry,
156	&cmd_list_commands_entry,
157	&cmd_list_keys_entry,
158	&cmd_list_panes_entry,
159	&cmd_list_sessions_entry,
160	&cmd_list_windows_entry,
161	&cmd_load_buffer_entry,
162	&cmd_lock_client_entry,
163	&cmd_lock_server_entry,
164	&cmd_lock_session_entry,
165	&cmd_move_pane_entry,
166	&cmd_move_window_entry,
167	&cmd_new_session_entry,
168	&cmd_new_window_entry,
169	&cmd_next_layout_entry,
170	&cmd_next_window_entry,
171	&cmd_paste_buffer_entry,
172	&cmd_pipe_pane_entry,
173	&cmd_previous_layout_entry,
174	&cmd_previous_window_entry,
175	&cmd_refresh_client_entry,
176	&cmd_rename_session_entry,
177	&cmd_rename_window_entry,
178	&cmd_resize_pane_entry,
179	&cmd_resize_window_entry,
180	&cmd_respawn_pane_entry,
181	&cmd_respawn_window_entry,
182	&cmd_rotate_window_entry,
183	&cmd_run_shell_entry,
184	&cmd_save_buffer_entry,
185	&cmd_select_layout_entry,
186	&cmd_select_pane_entry,
187	&cmd_select_window_entry,
188	&cmd_send_keys_entry,
189	&cmd_send_prefix_entry,
190	&cmd_server_access_entry,
191	&cmd_set_buffer_entry,
192	&cmd_set_environment_entry,
193	&cmd_set_hook_entry,
194	&cmd_set_option_entry,
195	&cmd_set_window_option_entry,
196	&cmd_show_buffer_entry,
197	&cmd_show_environment_entry,
198	&cmd_show_hooks_entry,
199	&cmd_show_messages_entry,
200	&cmd_show_options_entry,
201	&cmd_show_prompt_history_entry,
202	&cmd_show_window_options_entry,
203	&cmd_source_file_entry,
204	&cmd_split_window_entry,
205	&cmd_start_server_entry,
206	&cmd_suspend_client_entry,
207	&cmd_swap_pane_entry,
208	&cmd_swap_window_entry,
209	&cmd_switch_client_entry,
210	&cmd_unbind_key_entry,
211	&cmd_unlink_window_entry,
212	&cmd_wait_for_entry,
213	NULL
214};
215
216/* Instance of a command. */
217struct cmd {
218	const struct cmd_entry	 *entry;
219	struct args		 *args;
220	u_int			  group;
221
222	char			 *file;
223	u_int			  line;
224
225	TAILQ_ENTRY(cmd)	  qentry;
226};
227TAILQ_HEAD(cmds, cmd);
228
229/* Next group number for new command list. */
230static u_int cmd_list_next_group = 1;
231
232/* Log an argument vector. */
233void printflike(3, 4)
234cmd_log_argv(int argc, char **argv, const char *fmt, ...)
235{
236	char	*prefix;
237	va_list	 ap;
238	int	 i;
239
240	va_start(ap, fmt);
241	xvasprintf(&prefix, fmt, ap);
242	va_end(ap);
243
244	for (i = 0; i < argc; i++)
245		log_debug("%s: argv[%d]=%s", prefix, i, argv[i]);
246	free(prefix);
247}
248
249/* Prepend to an argument vector. */
250void
251cmd_prepend_argv(int *argc, char ***argv, const char *arg)
252{
253	char	**new_argv;
254	int	  i;
255
256	new_argv = xreallocarray(NULL, (*argc) + 1, sizeof *new_argv);
257	new_argv[0] = xstrdup(arg);
258	for (i = 0; i < *argc; i++)
259		new_argv[1 + i] = (*argv)[i];
260
261	free(*argv);
262	*argv = new_argv;
263	(*argc)++;
264}
265
266/* Append to an argument vector. */
267void
268cmd_append_argv(int *argc, char ***argv, const char *arg)
269{
270	*argv = xreallocarray(*argv, (*argc) + 1, sizeof **argv);
271	(*argv)[(*argc)++] = xstrdup(arg);
272}
273
274/* Pack an argument vector up into a buffer. */
275int
276cmd_pack_argv(int argc, char **argv, char *buf, size_t len)
277{
278	size_t	arglen;
279	int	i;
280
281	if (argc == 0)
282		return (0);
283	cmd_log_argv(argc, argv, "%s", __func__);
284
285	*buf = '\0';
286	for (i = 0; i < argc; i++) {
287		if (strlcpy(buf, argv[i], len) >= len)
288			return (-1);
289		arglen = strlen(argv[i]) + 1;
290		buf += arglen;
291		len -= arglen;
292	}
293
294	return (0);
295}
296
297/* Unpack an argument vector from a packed buffer. */
298int
299cmd_unpack_argv(char *buf, size_t len, int argc, char ***argv)
300{
301	int	i;
302	size_t	arglen;
303
304	if (argc == 0)
305		return (0);
306	*argv = xcalloc(argc, sizeof **argv);
307
308	buf[len - 1] = '\0';
309	for (i = 0; i < argc; i++) {
310		if (len == 0) {
311			cmd_free_argv(argc, *argv);
312			return (-1);
313		}
314
315		arglen = strlen(buf) + 1;
316		(*argv)[i] = xstrdup(buf);
317
318		buf += arglen;
319		len -= arglen;
320	}
321	cmd_log_argv(argc, *argv, "%s", __func__);
322
323	return (0);
324}
325
326/* Copy an argument vector, ensuring it is terminated by NULL. */
327char **
328cmd_copy_argv(int argc, char **argv)
329{
330	char	**new_argv;
331	int	  i;
332
333	if (argc == 0)
334		return (NULL);
335	new_argv = xcalloc(argc + 1, sizeof *new_argv);
336	for (i = 0; i < argc; i++) {
337		if (argv[i] != NULL)
338			new_argv[i] = xstrdup(argv[i]);
339	}
340	return (new_argv);
341}
342
343/* Free an argument vector. */
344void
345cmd_free_argv(int argc, char **argv)
346{
347	int	i;
348
349	if (argc == 0)
350		return;
351	for (i = 0; i < argc; i++)
352		free(argv[i]);
353	free(argv);
354}
355
356/* Convert argument vector to a string. */
357char *
358cmd_stringify_argv(int argc, char **argv)
359{
360	char	*buf = NULL, *s;
361	size_t	 len = 0;
362	int	 i;
363
364	if (argc == 0)
365		return (xstrdup(""));
366
367	for (i = 0; i < argc; i++) {
368		s = args_escape(argv[i]);
369		log_debug("%s: %u %s = %s", __func__, i, argv[i], s);
370
371		len += strlen(s) + 1;
372		buf = xrealloc(buf, len);
373
374		if (i == 0)
375			*buf = '\0';
376		else
377			strlcat(buf, " ", len);
378		strlcat(buf, s, len);
379
380		free(s);
381	}
382	return (buf);
383}
384
385/* Get entry for command. */
386const struct cmd_entry *
387cmd_get_entry(struct cmd *cmd)
388{
389	return (cmd->entry);
390}
391
392/* Get arguments for command. */
393struct args *
394cmd_get_args(struct cmd *cmd)
395{
396	return (cmd->args);
397}
398
399/* Get group for command. */
400u_int
401cmd_get_group(struct cmd *cmd)
402{
403	return (cmd->group);
404}
405
406/* Get file and line for command. */
407void
408cmd_get_source(struct cmd *cmd, const char **file, u_int *line)
409{
410	if (file != NULL)
411		*file = cmd->file;
412	if (line != NULL)
413		*line = cmd->line;
414}
415
416/* Look for an alias for a command. */
417char *
418cmd_get_alias(const char *name)
419{
420	struct options_entry		*o;
421	struct options_array_item	*a;
422	union options_value		*ov;
423	size_t				 wanted, n;
424	const char			*equals;
425
426	o = options_get_only(global_options, "command-alias");
427	if (o == NULL)
428		return (NULL);
429	wanted = strlen(name);
430
431	a = options_array_first(o);
432	while (a != NULL) {
433		ov = options_array_item_value(a);
434
435		equals = strchr(ov->string, '=');
436		if (equals != NULL) {
437			n = equals - ov->string;
438			if (n == wanted && strncmp(name, ov->string, n) == 0)
439				return (xstrdup(equals + 1));
440		}
441
442		a = options_array_next(a);
443	}
444	return (NULL);
445}
446
447/* Look up a command entry by name. */
448static const struct cmd_entry *
449cmd_find(const char *name, char **cause)
450{
451	const struct cmd_entry	**loop, *entry, *found = NULL;
452	int			  ambiguous;
453	char			  s[8192];
454
455	ambiguous = 0;
456	for (loop = cmd_table; *loop != NULL; loop++) {
457		entry = *loop;
458		if (entry->alias != NULL && strcmp(entry->alias, name) == 0) {
459			ambiguous = 0;
460			found = entry;
461			break;
462		}
463
464		if (strncmp(entry->name, name, strlen(name)) != 0)
465			continue;
466		if (found != NULL)
467			ambiguous = 1;
468		found = entry;
469
470		if (strcmp(entry->name, name) == 0)
471			break;
472	}
473	if (ambiguous)
474		goto ambiguous;
475	if (found == NULL) {
476		xasprintf(cause, "unknown command: %s", name);
477		return (NULL);
478	}
479	return (found);
480
481ambiguous:
482	*s = '\0';
483	for (loop = cmd_table; *loop != NULL; loop++) {
484		entry = *loop;
485		if (strncmp(entry->name, name, strlen(name)) != 0)
486			continue;
487		if (strlcat(s, entry->name, sizeof s) >= sizeof s)
488			break;
489		if (strlcat(s, ", ", sizeof s) >= sizeof s)
490			break;
491	}
492	s[strlen(s) - 2] = '\0';
493	xasprintf(cause, "ambiguous command: %s, could be: %s", name, s);
494	return (NULL);
495}
496
497/* Parse a single command from an argument vector. */
498struct cmd *
499cmd_parse(struct args_value *values, u_int count, const char *file, u_int line,
500    char **cause)
501{
502	const struct cmd_entry	*entry;
503	struct cmd		*cmd;
504	struct args		*args;
505	char			*error = NULL;
506
507	if (count == 0 || values[0].type != ARGS_STRING) {
508		xasprintf(cause, "no command");
509		return (NULL);
510	}
511	entry = cmd_find(values[0].string, cause);
512	if (entry == NULL)
513		return (NULL);
514
515	args = args_parse(&entry->args, values, count, &error);
516	if (args == NULL && error == NULL) {
517		xasprintf(cause, "usage: %s %s", entry->name, entry->usage);
518		return (NULL);
519	}
520	if (args == NULL) {
521		xasprintf(cause, "command %s: %s", entry->name, error);
522		free(error);
523		return (NULL);
524	}
525
526	cmd = xcalloc(1, sizeof *cmd);
527	cmd->entry = entry;
528	cmd->args = args;
529
530	if (file != NULL)
531		cmd->file = xstrdup(file);
532	cmd->line = line;
533
534	return (cmd);
535}
536
537/* Free a command. */
538void
539cmd_free(struct cmd *cmd)
540{
541	free(cmd->file);
542
543	args_free(cmd->args);
544	free(cmd);
545}
546
547/* Copy a command. */
548struct cmd *
549cmd_copy(struct cmd *cmd, int argc, char **argv)
550{
551	struct cmd	*new_cmd;
552
553	new_cmd = xcalloc(1, sizeof *new_cmd);
554	new_cmd->entry = cmd->entry;
555	new_cmd->args = args_copy(cmd->args, argc, argv);
556
557	if (cmd->file != NULL)
558		new_cmd->file = xstrdup(cmd->file);
559	new_cmd->line = cmd->line;
560
561	return (new_cmd);
562}
563
564/* Get a command as a string. */
565char *
566cmd_print(struct cmd *cmd)
567{
568	char	*out, *s;
569
570	s = args_print(cmd->args);
571	if (*s != '\0')
572		xasprintf(&out, "%s %s", cmd->entry->name, s);
573	else
574		out = xstrdup(cmd->entry->name);
575	free(s);
576
577	return (out);
578}
579
580/* Create a new command list. */
581struct cmd_list *
582cmd_list_new(void)
583{
584	struct cmd_list	*cmdlist;
585
586	cmdlist = xcalloc(1, sizeof *cmdlist);
587	cmdlist->references = 1;
588	cmdlist->group = cmd_list_next_group++;
589	cmdlist->list = xcalloc(1, sizeof *cmdlist->list);
590	TAILQ_INIT(cmdlist->list);
591	return (cmdlist);
592}
593
594/* Append a command to a command list. */
595void
596cmd_list_append(struct cmd_list *cmdlist, struct cmd *cmd)
597{
598	cmd->group = cmdlist->group;
599	TAILQ_INSERT_TAIL(cmdlist->list, cmd, qentry);
600}
601
602/* Append all commands from one list to another.  */
603void
604cmd_list_append_all(struct cmd_list *cmdlist, struct cmd_list *from)
605{
606	struct cmd	*cmd;
607
608	TAILQ_FOREACH(cmd, from->list, qentry)
609		cmd->group = cmdlist->group;
610	TAILQ_CONCAT(cmdlist->list, from->list, qentry);
611}
612
613/* Move all commands from one command list to another. */
614void
615cmd_list_move(struct cmd_list *cmdlist, struct cmd_list *from)
616{
617	TAILQ_CONCAT(cmdlist->list, from->list, qentry);
618	cmdlist->group = cmd_list_next_group++;
619}
620
621/* Free a command list. */
622void
623cmd_list_free(struct cmd_list *cmdlist)
624{
625	struct cmd	*cmd, *cmd1;
626
627	if (--cmdlist->references != 0)
628		return;
629
630	TAILQ_FOREACH_SAFE(cmd, cmdlist->list, qentry, cmd1) {
631		TAILQ_REMOVE(cmdlist->list, cmd, qentry);
632		cmd_free(cmd);
633	}
634	free(cmdlist->list);
635	free(cmdlist);
636}
637
638/* Copy a command list, expanding %s in arguments. */
639struct cmd_list *
640cmd_list_copy(struct cmd_list *cmdlist, int argc, char **argv)
641{
642	struct cmd	*cmd;
643	struct cmd_list	*new_cmdlist;
644	struct cmd	*new_cmd;
645	u_int		 group = cmdlist->group;
646	char		*s;
647
648	s = cmd_list_print(cmdlist, 0);
649	log_debug("%s: %s", __func__, s);
650	free(s);
651
652	new_cmdlist = cmd_list_new();
653	TAILQ_FOREACH(cmd, cmdlist->list, qentry) {
654		if (cmd->group != group) {
655			new_cmdlist->group = cmd_list_next_group++;
656			group = cmd->group;
657		}
658		new_cmd = cmd_copy(cmd, argc, argv);
659		cmd_list_append(new_cmdlist, new_cmd);
660	}
661
662	s = cmd_list_print(new_cmdlist, 0);
663	log_debug("%s: %s", __func__, s);
664	free(s);
665
666	return (new_cmdlist);
667}
668
669/* Get a command list as a string. */
670char *
671cmd_list_print(struct cmd_list *cmdlist, int escaped)
672{
673	struct cmd	*cmd, *next;
674	char		*buf, *this;
675	size_t		 len;
676
677	len = 1;
678	buf = xcalloc(1, len);
679
680	TAILQ_FOREACH(cmd, cmdlist->list, qentry) {
681		this = cmd_print(cmd);
682
683		len += strlen(this) + 6;
684		buf = xrealloc(buf, len);
685
686		strlcat(buf, this, len);
687
688		next = TAILQ_NEXT(cmd, qentry);
689		if (next != NULL) {
690			if (cmd->group != next->group) {
691				if (escaped)
692					strlcat(buf, " \\;\\; ", len);
693				else
694					strlcat(buf, " ;; ", len);
695			} else {
696				if (escaped)
697					strlcat(buf, " \\; ", len);
698				else
699					strlcat(buf, " ; ", len);
700			}
701		}
702
703		free(this);
704	}
705
706	return (buf);
707}
708
709/* Get first command in list. */
710struct cmd *
711cmd_list_first(struct cmd_list *cmdlist)
712{
713	return (TAILQ_FIRST(cmdlist->list));
714}
715
716/* Get next command in list. */
717struct cmd *
718cmd_list_next(struct cmd *cmd)
719{
720	return (TAILQ_NEXT(cmd, qentry));
721}
722
723/* Do all of the commands in this command list have this flag? */
724int
725cmd_list_all_have(struct cmd_list *cmdlist, int flag)
726{
727	struct cmd	*cmd;
728
729	TAILQ_FOREACH(cmd, cmdlist->list, qentry) {
730		if (~cmd->entry->flags & flag)
731			return (0);
732	}
733	return (1);
734}
735
736/* Do any of the commands in this command list have this flag? */
737int
738cmd_list_any_have(struct cmd_list *cmdlist, int flag)
739{
740	struct cmd	*cmd;
741
742	TAILQ_FOREACH(cmd, cmdlist->list, qentry) {
743		if (cmd->entry->flags & flag)
744			return (1);
745	}
746	return (0);
747}
748
749/* Adjust current mouse position for a pane. */
750int
751cmd_mouse_at(struct window_pane *wp, struct mouse_event *m, u_int *xp,
752    u_int *yp, int last)
753{
754	u_int	x, y;
755
756	if (last) {
757		x = m->lx + m->ox;
758		y = m->ly + m->oy;
759	} else {
760		x = m->x + m->ox;
761		y = m->y + m->oy;
762	}
763	log_debug("%s: x=%u, y=%u%s", __func__, x, y, last ? " (last)" : "");
764
765	if (m->statusat == 0 && y >= m->statuslines)
766		y -= m->statuslines;
767
768	if (x < wp->xoff || x >= wp->xoff + wp->sx)
769		return (-1);
770	if (y < wp->yoff || y >= wp->yoff + wp->sy)
771		return (-1);
772
773	if (xp != NULL)
774		*xp = x - wp->xoff;
775	if (yp != NULL)
776		*yp = y - wp->yoff;
777	return (0);
778}
779
780/* Get current mouse window if any. */
781struct winlink *
782cmd_mouse_window(struct mouse_event *m, struct session **sp)
783{
784	struct session	*s;
785	struct window	*w;
786	struct winlink	*wl;
787
788	if (!m->valid)
789		return (NULL);
790	if (m->s == -1 || (s = session_find_by_id(m->s)) == NULL)
791		return (NULL);
792	if (m->w == -1)
793		wl = s->curw;
794	else {
795		if ((w = window_find_by_id(m->w)) == NULL)
796			return (NULL);
797		wl = winlink_find_by_window(&s->windows, w);
798	}
799	if (sp != NULL)
800		*sp = s;
801	return (wl);
802}
803
804/* Get current mouse pane if any. */
805struct window_pane *
806cmd_mouse_pane(struct mouse_event *m, struct session **sp,
807    struct winlink **wlp)
808{
809	struct winlink		*wl;
810	struct window_pane     	*wp;
811
812	if ((wl = cmd_mouse_window(m, sp)) == NULL)
813		return (NULL);
814	if (m->wp == -1)
815		wp = wl->window->active;
816	else {
817		if ((wp = window_pane_find_by_id(m->wp)) == NULL)
818			return (NULL);
819		if (!window_has_pane(wl->window, wp))
820			return (NULL);
821	}
822
823	if (wlp != NULL)
824		*wlp = wl;
825	return (wp);
826}
827
828/* Replace the first %% or %idx in template by s. */
829char *
830cmd_template_replace(const char *template, const char *s, int idx)
831{
832	char		 ch, *buf;
833	const char	*ptr, *cp, quote[] = "\"\\$;~";
834	int		 replaced, quoted;
835	size_t		 len;
836
837	if (strchr(template, '%') == NULL)
838		return (xstrdup(template));
839
840	buf = xmalloc(1);
841	*buf = '\0';
842	len = 0;
843	replaced = 0;
844
845	ptr = template;
846	while (*ptr != '\0') {
847		switch (ch = *ptr++) {
848		case '%':
849			if (*ptr < '1' || *ptr > '9' || *ptr - '0' != idx) {
850				if (*ptr != '%' || replaced)
851					break;
852				replaced = 1;
853			}
854			ptr++;
855
856			quoted = (*ptr == '%');
857			if (quoted)
858				ptr++;
859
860			buf = xrealloc(buf, len + (strlen(s) * 3) + 1);
861			for (cp = s; *cp != '\0'; cp++) {
862				if (quoted && strchr(quote, *cp) != NULL)
863					buf[len++] = '\\';
864				buf[len++] = *cp;
865			}
866			buf[len] = '\0';
867			continue;
868		}
869		buf = xrealloc(buf, len + 2);
870		buf[len++] = ch;
871		buf[len] = '\0';
872	}
873
874	return (buf);
875}
876