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