| /* $OpenBSD$ */ | 
 |  | 
 | /* | 
 |  * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> | 
 |  * | 
 |  * Permission to use, copy, modify, and distribute this software for any | 
 |  * purpose with or without fee is hereby granted, provided that the above | 
 |  * copyright notice and this permission notice appear in all copies. | 
 |  * | 
 |  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | 
 |  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | 
 |  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | 
 |  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | 
 |  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER | 
 |  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING | 
 |  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | 
 |  */ | 
 |  | 
 | #include <sys/types.h> | 
 | #include <sys/time.h> | 
 |  | 
 | #include <fnmatch.h> | 
 | #include <paths.h> | 
 | #include <pwd.h> | 
 | #include <stdlib.h> | 
 | #include <string.h> | 
 | #include <unistd.h> | 
 |  | 
 | #include "tmux.h" | 
 |  | 
 | extern const struct cmd_entry cmd_attach_session_entry; | 
 | extern const struct cmd_entry cmd_bind_key_entry; | 
 | extern const struct cmd_entry cmd_break_pane_entry; | 
 | extern const struct cmd_entry cmd_capture_pane_entry; | 
 | extern const struct cmd_entry cmd_choose_buffer_entry; | 
 | extern const struct cmd_entry cmd_choose_client_entry; | 
 | extern const struct cmd_entry cmd_choose_tree_entry; | 
 | extern const struct cmd_entry cmd_clear_history_entry; | 
 | extern const struct cmd_entry cmd_clock_mode_entry; | 
 | extern const struct cmd_entry cmd_command_prompt_entry; | 
 | extern const struct cmd_entry cmd_confirm_before_entry; | 
 | extern const struct cmd_entry cmd_copy_mode_entry; | 
 | extern const struct cmd_entry cmd_delete_buffer_entry; | 
 | extern const struct cmd_entry cmd_detach_client_entry; | 
 | extern const struct cmd_entry cmd_display_menu_entry; | 
 | extern const struct cmd_entry cmd_display_message_entry; | 
 | extern const struct cmd_entry cmd_display_panes_entry; | 
 | extern const struct cmd_entry cmd_down_pane_entry; | 
 | extern const struct cmd_entry cmd_find_window_entry; | 
 | extern const struct cmd_entry cmd_has_session_entry; | 
 | extern const struct cmd_entry cmd_if_shell_entry; | 
 | extern const struct cmd_entry cmd_join_pane_entry; | 
 | extern const struct cmd_entry cmd_kill_pane_entry; | 
 | extern const struct cmd_entry cmd_kill_server_entry; | 
 | extern const struct cmd_entry cmd_kill_session_entry; | 
 | extern const struct cmd_entry cmd_kill_window_entry; | 
 | extern const struct cmd_entry cmd_last_pane_entry; | 
 | extern const struct cmd_entry cmd_last_window_entry; | 
 | extern const struct cmd_entry cmd_link_window_entry; | 
 | extern const struct cmd_entry cmd_list_buffers_entry; | 
 | extern const struct cmd_entry cmd_list_clients_entry; | 
 | extern const struct cmd_entry cmd_list_commands_entry; | 
 | extern const struct cmd_entry cmd_list_keys_entry; | 
 | extern const struct cmd_entry cmd_list_panes_entry; | 
 | extern const struct cmd_entry cmd_list_sessions_entry; | 
 | extern const struct cmd_entry cmd_list_windows_entry; | 
 | extern const struct cmd_entry cmd_load_buffer_entry; | 
 | extern const struct cmd_entry cmd_lock_client_entry; | 
 | extern const struct cmd_entry cmd_lock_server_entry; | 
 | extern const struct cmd_entry cmd_lock_session_entry; | 
 | extern const struct cmd_entry cmd_move_pane_entry; | 
 | extern const struct cmd_entry cmd_move_window_entry; | 
 | extern const struct cmd_entry cmd_new_session_entry; | 
 | extern const struct cmd_entry cmd_new_window_entry; | 
 | extern const struct cmd_entry cmd_next_layout_entry; | 
 | extern const struct cmd_entry cmd_next_window_entry; | 
 | extern const struct cmd_entry cmd_paste_buffer_entry; | 
 | extern const struct cmd_entry cmd_pipe_pane_entry; | 
 | extern const struct cmd_entry cmd_previous_layout_entry; | 
 | extern const struct cmd_entry cmd_previous_window_entry; | 
 | extern const struct cmd_entry cmd_refresh_client_entry; | 
 | extern const struct cmd_entry cmd_rename_session_entry; | 
 | extern const struct cmd_entry cmd_rename_window_entry; | 
 | extern const struct cmd_entry cmd_resize_pane_entry; | 
 | extern const struct cmd_entry cmd_resize_window_entry; | 
 | extern const struct cmd_entry cmd_respawn_pane_entry; | 
 | extern const struct cmd_entry cmd_respawn_window_entry; | 
 | extern const struct cmd_entry cmd_rotate_window_entry; | 
 | extern const struct cmd_entry cmd_run_shell_entry; | 
 | extern const struct cmd_entry cmd_save_buffer_entry; | 
 | extern const struct cmd_entry cmd_select_layout_entry; | 
 | extern const struct cmd_entry cmd_select_pane_entry; | 
 | extern const struct cmd_entry cmd_select_window_entry; | 
 | extern const struct cmd_entry cmd_send_keys_entry; | 
 | extern const struct cmd_entry cmd_send_prefix_entry; | 
 | extern const struct cmd_entry cmd_set_buffer_entry; | 
 | extern const struct cmd_entry cmd_set_environment_entry; | 
 | extern const struct cmd_entry cmd_set_hook_entry; | 
 | extern const struct cmd_entry cmd_set_option_entry; | 
 | extern const struct cmd_entry cmd_set_window_option_entry; | 
 | extern const struct cmd_entry cmd_show_buffer_entry; | 
 | extern const struct cmd_entry cmd_show_environment_entry; | 
 | extern const struct cmd_entry cmd_show_hooks_entry; | 
 | extern const struct cmd_entry cmd_show_messages_entry; | 
 | extern const struct cmd_entry cmd_show_options_entry; | 
 | extern const struct cmd_entry cmd_show_window_options_entry; | 
 | extern const struct cmd_entry cmd_source_file_entry; | 
 | extern const struct cmd_entry cmd_split_window_entry; | 
 | extern const struct cmd_entry cmd_start_server_entry; | 
 | extern const struct cmd_entry cmd_suspend_client_entry; | 
 | extern const struct cmd_entry cmd_swap_pane_entry; | 
 | extern const struct cmd_entry cmd_swap_window_entry; | 
 | extern const struct cmd_entry cmd_switch_client_entry; | 
 | extern const struct cmd_entry cmd_unbind_key_entry; | 
 | extern const struct cmd_entry cmd_unlink_window_entry; | 
 | extern const struct cmd_entry cmd_up_pane_entry; | 
 | extern const struct cmd_entry cmd_wait_for_entry; | 
 |  | 
 | const struct cmd_entry *cmd_table[] = { | 
 | 	&cmd_attach_session_entry, | 
 | 	&cmd_bind_key_entry, | 
 | 	&cmd_break_pane_entry, | 
 | 	&cmd_capture_pane_entry, | 
 | 	&cmd_choose_buffer_entry, | 
 | 	&cmd_choose_client_entry, | 
 | 	&cmd_choose_tree_entry, | 
 | 	&cmd_clear_history_entry, | 
 | 	&cmd_clock_mode_entry, | 
 | 	&cmd_command_prompt_entry, | 
 | 	&cmd_confirm_before_entry, | 
 | 	&cmd_copy_mode_entry, | 
 | 	&cmd_delete_buffer_entry, | 
 | 	&cmd_detach_client_entry, | 
 | 	&cmd_display_menu_entry, | 
 | 	&cmd_display_message_entry, | 
 | 	&cmd_display_panes_entry, | 
 | 	&cmd_find_window_entry, | 
 | 	&cmd_has_session_entry, | 
 | 	&cmd_if_shell_entry, | 
 | 	&cmd_join_pane_entry, | 
 | 	&cmd_kill_pane_entry, | 
 | 	&cmd_kill_server_entry, | 
 | 	&cmd_kill_session_entry, | 
 | 	&cmd_kill_window_entry, | 
 | 	&cmd_last_pane_entry, | 
 | 	&cmd_last_window_entry, | 
 | 	&cmd_link_window_entry, | 
 | 	&cmd_list_buffers_entry, | 
 | 	&cmd_list_clients_entry, | 
 | 	&cmd_list_commands_entry, | 
 | 	&cmd_list_keys_entry, | 
 | 	&cmd_list_panes_entry, | 
 | 	&cmd_list_sessions_entry, | 
 | 	&cmd_list_windows_entry, | 
 | 	&cmd_load_buffer_entry, | 
 | 	&cmd_lock_client_entry, | 
 | 	&cmd_lock_server_entry, | 
 | 	&cmd_lock_session_entry, | 
 | 	&cmd_move_pane_entry, | 
 | 	&cmd_move_window_entry, | 
 | 	&cmd_new_session_entry, | 
 | 	&cmd_new_window_entry, | 
 | 	&cmd_next_layout_entry, | 
 | 	&cmd_next_window_entry, | 
 | 	&cmd_paste_buffer_entry, | 
 | 	&cmd_pipe_pane_entry, | 
 | 	&cmd_previous_layout_entry, | 
 | 	&cmd_previous_window_entry, | 
 | 	&cmd_refresh_client_entry, | 
 | 	&cmd_rename_session_entry, | 
 | 	&cmd_rename_window_entry, | 
 | 	&cmd_resize_pane_entry, | 
 | 	&cmd_resize_window_entry, | 
 | 	&cmd_respawn_pane_entry, | 
 | 	&cmd_respawn_window_entry, | 
 | 	&cmd_rotate_window_entry, | 
 | 	&cmd_run_shell_entry, | 
 | 	&cmd_save_buffer_entry, | 
 | 	&cmd_select_layout_entry, | 
 | 	&cmd_select_pane_entry, | 
 | 	&cmd_select_window_entry, | 
 | 	&cmd_send_keys_entry, | 
 | 	&cmd_send_prefix_entry, | 
 | 	&cmd_set_buffer_entry, | 
 | 	&cmd_set_environment_entry, | 
 | 	&cmd_set_hook_entry, | 
 | 	&cmd_set_option_entry, | 
 | 	&cmd_set_window_option_entry, | 
 | 	&cmd_show_buffer_entry, | 
 | 	&cmd_show_environment_entry, | 
 | 	&cmd_show_hooks_entry, | 
 | 	&cmd_show_messages_entry, | 
 | 	&cmd_show_options_entry, | 
 | 	&cmd_show_window_options_entry, | 
 | 	&cmd_source_file_entry, | 
 | 	&cmd_split_window_entry, | 
 | 	&cmd_start_server_entry, | 
 | 	&cmd_suspend_client_entry, | 
 | 	&cmd_swap_pane_entry, | 
 | 	&cmd_swap_window_entry, | 
 | 	&cmd_switch_client_entry, | 
 | 	&cmd_unbind_key_entry, | 
 | 	&cmd_unlink_window_entry, | 
 | 	&cmd_wait_for_entry, | 
 | 	NULL | 
 | }; | 
 |  | 
 | static u_int cmd_list_next_group = 1; | 
 |  | 
 | void printflike(3, 4) | 
 | cmd_log_argv(int argc, char **argv, const char *fmt, ...) | 
 | { | 
 | 	char	*prefix; | 
 | 	va_list	 ap; | 
 | 	int	 i; | 
 |  | 
 | 	va_start(ap, fmt); | 
 | 	xvasprintf(&prefix, fmt, ap); | 
 | 	va_end(ap); | 
 |  | 
 | 	for (i = 0; i < argc; i++) | 
 | 		log_debug("%s: argv[%d]=%s", prefix, i, argv[i]); | 
 | 	free(prefix); | 
 | } | 
 |  | 
 | void | 
 | cmd_prepend_argv(int *argc, char ***argv, char *arg) | 
 | { | 
 | 	char	**new_argv; | 
 | 	int	  i; | 
 |  | 
 | 	new_argv = xreallocarray(NULL, (*argc) + 1, sizeof *new_argv); | 
 | 	new_argv[0] = xstrdup(arg); | 
 | 	for (i = 0; i < *argc; i++) | 
 | 		new_argv[1 + i] = (*argv)[i]; | 
 |  | 
 | 	free(*argv); | 
 | 	*argv = new_argv; | 
 | 	(*argc)++; | 
 | } | 
 |  | 
 | void | 
 | cmd_append_argv(int *argc, char ***argv, char *arg) | 
 | { | 
 | 	*argv = xreallocarray(*argv, (*argc) + 1, sizeof **argv); | 
 | 	(*argv)[(*argc)++] = xstrdup(arg); | 
 | } | 
 |  | 
 | int | 
 | cmd_pack_argv(int argc, char **argv, char *buf, size_t len) | 
 | { | 
 | 	size_t	arglen; | 
 | 	int	i; | 
 |  | 
 | 	if (argc == 0) | 
 | 		return (0); | 
 | 	cmd_log_argv(argc, argv, "%s", __func__); | 
 |  | 
 | 	*buf = '\0'; | 
 | 	for (i = 0; i < argc; i++) { | 
 | 		if (strlcpy(buf, argv[i], len) >= len) | 
 | 			return (-1); | 
 | 		arglen = strlen(argv[i]) + 1; | 
 | 		buf += arglen; | 
 | 		len -= arglen; | 
 | 	} | 
 |  | 
 | 	return (0); | 
 | } | 
 |  | 
 | int | 
 | cmd_unpack_argv(char *buf, size_t len, int argc, char ***argv) | 
 | { | 
 | 	int	i; | 
 | 	size_t	arglen; | 
 |  | 
 | 	if (argc == 0) | 
 | 		return (0); | 
 | 	*argv = xcalloc(argc, sizeof **argv); | 
 |  | 
 | 	buf[len - 1] = '\0'; | 
 | 	for (i = 0; i < argc; i++) { | 
 | 		if (len == 0) { | 
 | 			cmd_free_argv(argc, *argv); | 
 | 			return (-1); | 
 | 		} | 
 |  | 
 | 		arglen = strlen(buf) + 1; | 
 | 		(*argv)[i] = xstrdup(buf); | 
 |  | 
 | 		buf += arglen; | 
 | 		len -= arglen; | 
 | 	} | 
 | 	cmd_log_argv(argc, *argv, "%s", __func__); | 
 |  | 
 | 	return (0); | 
 | } | 
 |  | 
 | char ** | 
 | cmd_copy_argv(int argc, char **argv) | 
 | { | 
 | 	char	**new_argv; | 
 | 	int	  i; | 
 |  | 
 | 	if (argc == 0) | 
 | 		return (NULL); | 
 | 	new_argv = xcalloc(argc + 1, sizeof *new_argv); | 
 | 	for (i = 0; i < argc; i++) { | 
 | 		if (argv[i] != NULL) | 
 | 			new_argv[i] = xstrdup(argv[i]); | 
 | 	} | 
 | 	return (new_argv); | 
 | } | 
 |  | 
 | void | 
 | cmd_free_argv(int argc, char **argv) | 
 | { | 
 | 	int	i; | 
 |  | 
 | 	if (argc == 0) | 
 | 		return; | 
 | 	for (i = 0; i < argc; i++) | 
 | 		free(argv[i]); | 
 | 	free(argv); | 
 | } | 
 |  | 
 | char * | 
 | cmd_stringify_argv(int argc, char **argv) | 
 | { | 
 | 	char	*buf; | 
 | 	int	 i; | 
 | 	size_t	 len; | 
 |  | 
 | 	if (argc == 0) | 
 | 		return (xstrdup("")); | 
 |  | 
 | 	len = 0; | 
 | 	buf = NULL; | 
 |  | 
 | 	for (i = 0; i < argc; i++) { | 
 | 		len += strlen(argv[i]) + 1; | 
 | 		buf = xrealloc(buf, len); | 
 |  | 
 | 		if (i == 0) | 
 | 			*buf = '\0'; | 
 | 		else | 
 | 			strlcat(buf, " ", len); | 
 | 		strlcat(buf, argv[i], len); | 
 | 	} | 
 | 	return (buf); | 
 | } | 
 |  | 
 | char * | 
 | cmd_get_alias(const char *name) | 
 | { | 
 | 	struct options_entry		*o; | 
 | 	struct options_array_item	*a; | 
 | 	union options_value		*ov; | 
 | 	size_t				 wanted, n; | 
 | 	const char			*equals; | 
 |  | 
 | 	o = options_get_only(global_options, "command-alias"); | 
 | 	if (o == NULL) | 
 | 		return (NULL); | 
 | 	wanted = strlen(name); | 
 |  | 
 | 	a = options_array_first(o); | 
 | 	while (a != NULL) { | 
 | 		ov = options_array_item_value(a); | 
 |  | 
 | 		equals = strchr(ov->string, '='); | 
 | 		if (equals != NULL) { | 
 | 			n = equals - ov->string; | 
 | 			if (n == wanted && strncmp(name, ov->string, n) == 0) | 
 | 				return (xstrdup(equals + 1)); | 
 | 		} | 
 |  | 
 | 		a = options_array_next(a); | 
 | 	} | 
 | 	return (NULL); | 
 | } | 
 |  | 
 | static const struct cmd_entry * | 
 | cmd_find(const char *name, char **cause) | 
 | { | 
 | 	const struct cmd_entry	**loop, *entry, *found = NULL; | 
 | 	int			  ambiguous; | 
 | 	char			  s[8192]; | 
 |  | 
 | 	ambiguous = 0; | 
 | 	for (loop = cmd_table; *loop != NULL; loop++) { | 
 | 		entry = *loop; | 
 | 		if (entry->alias != NULL && strcmp(entry->alias, name) == 0) { | 
 | 			ambiguous = 0; | 
 | 			found = entry; | 
 | 			break; | 
 | 		} | 
 |  | 
 | 		if (strncmp(entry->name, name, strlen(name)) != 0) | 
 | 			continue; | 
 | 		if (found != NULL) | 
 | 			ambiguous = 1; | 
 | 		found = entry; | 
 |  | 
 | 		if (strcmp(entry->name, name) == 0) | 
 | 			break; | 
 | 	} | 
 | 	if (ambiguous) | 
 | 		goto ambiguous; | 
 | 	if (found == NULL) { | 
 | 		xasprintf(cause, "unknown command: %s", name); | 
 | 		return (NULL); | 
 | 	} | 
 | 	return (found); | 
 |  | 
 | ambiguous: | 
 | 	*s = '\0'; | 
 | 	for (loop = cmd_table; *loop != NULL; loop++) { | 
 | 		entry = *loop; | 
 | 		if (strncmp(entry->name, name, strlen(name)) != 0) | 
 | 			continue; | 
 | 		if (strlcat(s, entry->name, sizeof s) >= sizeof s) | 
 | 			break; | 
 | 		if (strlcat(s, ", ", sizeof s) >= sizeof s) | 
 | 			break; | 
 | 	} | 
 | 	s[strlen(s) - 2] = '\0'; | 
 | 	xasprintf(cause, "ambiguous command: %s, could be: %s", name, s); | 
 | 	return (NULL); | 
 | } | 
 |  | 
 | struct cmd * | 
 | cmd_parse(int argc, char **argv, const char *file, u_int line, char **cause) | 
 | { | 
 | 	const struct cmd_entry	*entry; | 
 | 	const char		*name; | 
 | 	struct cmd		*cmd; | 
 | 	struct args		*args; | 
 |  | 
 | 	if (argc == 0) { | 
 | 		xasprintf(cause, "no command"); | 
 | 		return (NULL); | 
 | 	} | 
 | 	name = argv[0]; | 
 |  | 
 | 	entry = cmd_find(name, cause); | 
 | 	if (entry == NULL) | 
 | 		return (NULL); | 
 | 	cmd_log_argv(argc, argv, "%s: %s", __func__, entry->name); | 
 |  | 
 | 	args = args_parse(entry->args.template, argc, argv); | 
 | 	if (args == NULL) | 
 | 		goto usage; | 
 | 	if (entry->args.lower != -1 && args->argc < entry->args.lower) | 
 | 		goto usage; | 
 | 	if (entry->args.upper != -1 && args->argc > entry->args.upper) | 
 | 		goto usage; | 
 |  | 
 | 	cmd = xcalloc(1, sizeof *cmd); | 
 | 	cmd->entry = entry; | 
 | 	cmd->args = args; | 
 |  | 
 | 	if (file != NULL) | 
 | 		cmd->file = xstrdup(file); | 
 | 	cmd->line = line; | 
 |  | 
 | 	cmd->alias = NULL; | 
 | 	cmd->argc = argc; | 
 | 	cmd->argv = cmd_copy_argv(argc, argv); | 
 |  | 
 | 	return (cmd); | 
 |  | 
 | usage: | 
 | 	if (args != NULL) | 
 | 		args_free(args); | 
 | 	xasprintf(cause, "usage: %s %s", entry->name, entry->usage); | 
 | 	return (NULL); | 
 | } | 
 |  | 
 | void | 
 | cmd_free(struct cmd *cmd) | 
 | { | 
 | 	free(cmd->alias); | 
 | 	cmd_free_argv(cmd->argc, cmd->argv); | 
 |  | 
 | 	free(cmd->file); | 
 |  | 
 | 	args_free(cmd->args); | 
 | 	free(cmd); | 
 | } | 
 |  | 
 | char * | 
 | cmd_print(struct cmd *cmd) | 
 | { | 
 | 	char	*out, *s; | 
 |  | 
 | 	s = args_print(cmd->args); | 
 | 	if (*s != '\0') | 
 | 		xasprintf(&out, "%s %s", cmd->entry->name, s); | 
 | 	else | 
 | 		out = xstrdup(cmd->entry->name); | 
 | 	free(s); | 
 |  | 
 | 	return (out); | 
 | } | 
 |  | 
 | struct cmd_list * | 
 | cmd_list_new(void) | 
 | { | 
 | 	struct cmd_list	*cmdlist; | 
 |  | 
 | 	cmdlist = xcalloc(1, sizeof *cmdlist); | 
 | 	cmdlist->references = 1; | 
 | 	cmdlist->group = cmd_list_next_group++; | 
 | 	TAILQ_INIT(&cmdlist->list); | 
 | 	return (cmdlist); | 
 | } | 
 |  | 
 | void | 
 | cmd_list_append(struct cmd_list *cmdlist, struct cmd *cmd) | 
 | { | 
 | 	cmd->group = cmdlist->group; | 
 | 	TAILQ_INSERT_TAIL(&cmdlist->list, cmd, qentry); | 
 | } | 
 |  | 
 | void | 
 | cmd_list_move(struct cmd_list *cmdlist, struct cmd_list *from) | 
 | { | 
 | 	struct cmd	*cmd, *cmd1; | 
 |  | 
 | 	TAILQ_FOREACH_SAFE(cmd, &from->list, qentry, cmd1) { | 
 | 		TAILQ_REMOVE(&from->list, cmd, qentry); | 
 | 		TAILQ_INSERT_TAIL(&cmdlist->list, cmd, qentry); | 
 | 	} | 
 | 	cmdlist->group = cmd_list_next_group++; | 
 | } | 
 |  | 
 | void | 
 | cmd_list_free(struct cmd_list *cmdlist) | 
 | { | 
 | 	struct cmd	*cmd, *cmd1; | 
 |  | 
 | 	if (--cmdlist->references != 0) | 
 | 		return; | 
 |  | 
 | 	TAILQ_FOREACH_SAFE(cmd, &cmdlist->list, qentry, cmd1) { | 
 | 		TAILQ_REMOVE(&cmdlist->list, cmd, qentry); | 
 | 		cmd_free(cmd); | 
 | 	} | 
 |  | 
 | 	free(cmdlist); | 
 | } | 
 |  | 
 | char * | 
 | cmd_list_print(struct cmd_list *cmdlist, int escaped) | 
 | { | 
 | 	struct cmd	*cmd; | 
 | 	char		*buf, *this; | 
 | 	size_t		 len; | 
 |  | 
 | 	len = 1; | 
 | 	buf = xcalloc(1, len); | 
 |  | 
 | 	TAILQ_FOREACH(cmd, &cmdlist->list, qentry) { | 
 | 		this = cmd_print(cmd); | 
 |  | 
 | 		len += strlen(this) + 4; | 
 | 		buf = xrealloc(buf, len); | 
 |  | 
 | 		strlcat(buf, this, len); | 
 | 		if (TAILQ_NEXT(cmd, qentry) != NULL) { | 
 | 			if (escaped) | 
 | 				strlcat(buf, " \\; ", len); | 
 | 			else | 
 | 				strlcat(buf, " ; ", len); | 
 | 		} | 
 |  | 
 | 		free(this); | 
 | 	} | 
 |  | 
 | 	return (buf); | 
 | } | 
 |  | 
 | /* Adjust current mouse position for a pane. */ | 
 | int | 
 | cmd_mouse_at(struct window_pane *wp, struct mouse_event *m, u_int *xp, | 
 |     u_int *yp, int last) | 
 | { | 
 | 	u_int	x, y; | 
 |  | 
 | 	if (last) { | 
 | 		x = m->lx + m->ox; | 
 | 		y = m->ly + m->oy; | 
 | 	} else { | 
 | 		x = m->x + m->ox; | 
 | 		y = m->y + m->oy; | 
 | 	} | 
 | 	log_debug("%s: x=%u, y=%u%s", __func__, x, y, last ? " (last)" : ""); | 
 |  | 
 | 	if (m->statusat == 0 && y >= m->statuslines) | 
 | 		y -= m->statuslines; | 
 |  | 
 | 	if (x < wp->xoff || x >= wp->xoff + wp->sx) | 
 | 		return (-1); | 
 | 	if (y < wp->yoff || y >= wp->yoff + wp->sy) | 
 | 		return (-1); | 
 |  | 
 | 	if (xp != NULL) | 
 | 		*xp = x - wp->xoff; | 
 | 	if (yp != NULL) | 
 | 		*yp = y - wp->yoff; | 
 | 	return (0); | 
 | } | 
 |  | 
 | /* Get current mouse window if any. */ | 
 | struct winlink * | 
 | cmd_mouse_window(struct mouse_event *m, struct session **sp) | 
 | { | 
 | 	struct session	*s; | 
 | 	struct window	*w; | 
 | 	struct winlink	*wl; | 
 |  | 
 | 	if (!m->valid) | 
 | 		return (NULL); | 
 | 	if (m->s == -1 || (s = session_find_by_id(m->s)) == NULL) | 
 | 		return (NULL); | 
 | 	if (m->w == -1) | 
 | 		wl = s->curw; | 
 | 	else { | 
 | 		if ((w = window_find_by_id(m->w)) == NULL) | 
 | 			return (NULL); | 
 | 		wl = winlink_find_by_window(&s->windows, w); | 
 | 	} | 
 | 	if (sp != NULL) | 
 | 		*sp = s; | 
 | 	return (wl); | 
 | } | 
 |  | 
 | /* Get current mouse pane if any. */ | 
 | struct window_pane * | 
 | cmd_mouse_pane(struct mouse_event *m, struct session **sp, | 
 |     struct winlink **wlp) | 
 | { | 
 | 	struct winlink		*wl; | 
 | 	struct window_pane     	*wp; | 
 |  | 
 | 	if ((wl = cmd_mouse_window(m, sp)) == NULL) | 
 | 		return (NULL); | 
 | 	if ((wp = window_pane_find_by_id(m->wp)) == NULL) | 
 | 		return (NULL); | 
 | 	if (!window_has_pane(wl->window, wp)) | 
 | 		return (NULL); | 
 |  | 
 | 	if (wlp != NULL) | 
 | 		*wlp = wl; | 
 | 	return (wp); | 
 | } | 
 |  | 
 | /* Replace the first %% or %idx in template by s. */ | 
 | char * | 
 | cmd_template_replace(const char *template, const char *s, int idx) | 
 | { | 
 | 	char		 ch, *buf; | 
 | 	const char	*ptr, *cp, quote[] = "\"\\$;~"; | 
 | 	int		 replaced, quoted; | 
 | 	size_t		 len; | 
 |  | 
 | 	if (strchr(template, '%') == NULL) | 
 | 		return (xstrdup(template)); | 
 |  | 
 | 	buf = xmalloc(1); | 
 | 	*buf = '\0'; | 
 | 	len = 0; | 
 | 	replaced = 0; | 
 |  | 
 | 	ptr = template; | 
 | 	while (*ptr != '\0') { | 
 | 		switch (ch = *ptr++) { | 
 | 		case '%': | 
 | 			if (*ptr < '1' || *ptr > '9' || *ptr - '0' != idx) { | 
 | 				if (*ptr != '%' || replaced) | 
 | 					break; | 
 | 				replaced = 1; | 
 | 			} | 
 | 			ptr++; | 
 |  | 
 | 			quoted = (*ptr == '%'); | 
 | 			if (quoted) | 
 | 				ptr++; | 
 |  | 
 | 			buf = xrealloc(buf, len + (strlen(s) * 3) + 1); | 
 | 			for (cp = s; *cp != '\0'; cp++) { | 
 | 				if (quoted && strchr(quote, *cp) != NULL) | 
 | 					buf[len++] = '\\'; | 
 | 				buf[len++] = *cp; | 
 | 			} | 
 | 			buf[len] = '\0'; | 
 | 			continue; | 
 | 		} | 
 | 		buf = xrealloc(buf, len + 2); | 
 | 		buf[len++] = ch; | 
 | 		buf[len] = '\0'; | 
 | 	} | 
 |  | 
 | 	return (buf); | 
 | } |