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