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