|  | /* $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_clear_prompt_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_customize_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_popup_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_prompt_history_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_clear_prompt_history_entry, | 
|  | &cmd_clock_mode_entry, | 
|  | &cmd_command_prompt_entry, | 
|  | &cmd_confirm_before_entry, | 
|  | &cmd_copy_mode_entry, | 
|  | &cmd_customize_mode_entry, | 
|  | &cmd_delete_buffer_entry, | 
|  | &cmd_detach_client_entry, | 
|  | &cmd_display_menu_entry, | 
|  | &cmd_display_message_entry, | 
|  | &cmd_display_popup_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_prompt_history_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 | 
|  | }; | 
|  |  | 
|  | /* Instance of a command. */ | 
|  | struct cmd { | 
|  | const struct cmd_entry	 *entry; | 
|  | struct args		 *args; | 
|  | u_int			  group; | 
|  |  | 
|  | char			 *file; | 
|  | u_int			  line; | 
|  |  | 
|  | TAILQ_ENTRY(cmd)	  qentry; | 
|  | }; | 
|  | TAILQ_HEAD(cmds, cmd); | 
|  |  | 
|  | /* Next group number for new command list. */ | 
|  | static u_int cmd_list_next_group = 1; | 
|  |  | 
|  | /* Log an argument vector. */ | 
|  | 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); | 
|  | } | 
|  |  | 
|  | /* Prepend to an argument vector. */ | 
|  | void | 
|  | cmd_prepend_argv(int *argc, char ***argv, const 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)++; | 
|  | } | 
|  |  | 
|  | /* Append to an argument vector. */ | 
|  | void | 
|  | cmd_append_argv(int *argc, char ***argv, const char *arg) | 
|  | { | 
|  | *argv = xreallocarray(*argv, (*argc) + 1, sizeof **argv); | 
|  | (*argv)[(*argc)++] = xstrdup(arg); | 
|  | } | 
|  |  | 
|  | /* Pack an argument vector up into a buffer. */ | 
|  | 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); | 
|  | } | 
|  |  | 
|  | /* Unpack an argument vector from a packed buffer. */ | 
|  | 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); | 
|  | } | 
|  |  | 
|  | /* Copy an argument vector, ensuring it is terminated by NULL. */ | 
|  | 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); | 
|  | } | 
|  |  | 
|  | /* Free an argument vector. */ | 
|  | 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); | 
|  | } | 
|  |  | 
|  | /* Convert argument vector to a string. */ | 
|  | char * | 
|  | cmd_stringify_argv(int argc, char **argv) | 
|  | { | 
|  | char	*buf = NULL, *s; | 
|  | size_t	 len = 0; | 
|  | int	 i; | 
|  |  | 
|  | if (argc == 0) | 
|  | return (xstrdup("")); | 
|  |  | 
|  | for (i = 0; i < argc; i++) { | 
|  | s = args_escape(argv[i]); | 
|  | log_debug("%s: %u %s = %s", __func__, i, argv[i], s); | 
|  |  | 
|  | len += strlen(s) + 1; | 
|  | buf = xrealloc(buf, len); | 
|  |  | 
|  | if (i == 0) | 
|  | *buf = '\0'; | 
|  | else | 
|  | strlcat(buf, " ", len); | 
|  | strlcat(buf, s, len); | 
|  |  | 
|  | free(s); | 
|  | } | 
|  | return (buf); | 
|  | } | 
|  |  | 
|  | /* Get entry for command. */ | 
|  | const struct cmd_entry * | 
|  | cmd_get_entry(struct cmd *cmd) | 
|  | { | 
|  | return (cmd->entry); | 
|  | } | 
|  |  | 
|  | /* Get arguments for command. */ | 
|  | struct args * | 
|  | cmd_get_args(struct cmd *cmd) | 
|  | { | 
|  | return (cmd->args); | 
|  | } | 
|  |  | 
|  | /* Get group for command. */ | 
|  | u_int | 
|  | cmd_get_group(struct cmd *cmd) | 
|  | { | 
|  | return (cmd->group); | 
|  | } | 
|  |  | 
|  | /* Get file and line for command. */ | 
|  | void | 
|  | cmd_get_source(struct cmd *cmd, const char **file, u_int *line) | 
|  | { | 
|  | if (file != NULL) | 
|  | *file = cmd->file; | 
|  | if (line != NULL) | 
|  | *line = cmd->line; | 
|  | } | 
|  |  | 
|  | /* Look for an alias for a command. */ | 
|  | 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); | 
|  | } | 
|  |  | 
|  | /* Look up a command entry by name. */ | 
|  | 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); | 
|  | } | 
|  |  | 
|  | /* Parse a single command from an argument vector. */ | 
|  | struct cmd * | 
|  | cmd_parse(struct args_value *values, u_int count, const char *file, u_int line, | 
|  | char **cause) | 
|  | { | 
|  | const struct cmd_entry	*entry; | 
|  | struct cmd		*cmd; | 
|  | struct args		*args; | 
|  | char			*error = NULL; | 
|  |  | 
|  | if (count == 0 || values[0].type != ARGS_STRING) { | 
|  | xasprintf(cause, "no command"); | 
|  | return (NULL); | 
|  | } | 
|  | entry = cmd_find(values[0].string, cause); | 
|  | if (entry == NULL) | 
|  | return (NULL); | 
|  |  | 
|  | args = args_parse(&entry->args, values, count, &error); | 
|  | if (args == NULL && error == NULL) { | 
|  | xasprintf(cause, "usage: %s %s", entry->name, entry->usage); | 
|  | return (NULL); | 
|  | } | 
|  | if (args == NULL) { | 
|  | xasprintf(cause, "command %s: %s", entry->name, error); | 
|  | free(error); | 
|  | return (NULL); | 
|  | } | 
|  |  | 
|  | cmd = xcalloc(1, sizeof *cmd); | 
|  | cmd->entry = entry; | 
|  | cmd->args = args; | 
|  |  | 
|  | if (file != NULL) | 
|  | cmd->file = xstrdup(file); | 
|  | cmd->line = line; | 
|  |  | 
|  | return (cmd); | 
|  | } | 
|  |  | 
|  | /* Free a command. */ | 
|  | void | 
|  | cmd_free(struct cmd *cmd) | 
|  | { | 
|  | free(cmd->file); | 
|  |  | 
|  | args_free(cmd->args); | 
|  | free(cmd); | 
|  | } | 
|  |  | 
|  | /* Copy a command. */ | 
|  | struct cmd * | 
|  | cmd_copy(struct cmd *cmd, int argc, char **argv) | 
|  | { | 
|  | struct cmd	*new_cmd; | 
|  |  | 
|  | new_cmd = xcalloc(1, sizeof *new_cmd); | 
|  | new_cmd->entry = cmd->entry; | 
|  | new_cmd->args = args_copy(cmd->args, argc, argv); | 
|  |  | 
|  | if (cmd->file != NULL) | 
|  | new_cmd->file = xstrdup(cmd->file); | 
|  | new_cmd->line = cmd->line; | 
|  |  | 
|  | return (new_cmd); | 
|  | } | 
|  |  | 
|  | /* Get a command as a string. */ | 
|  | 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); | 
|  | } | 
|  |  | 
|  | /* Create a new command list. */ | 
|  | 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++; | 
|  | cmdlist->list = xcalloc(1, sizeof *cmdlist->list); | 
|  | TAILQ_INIT(cmdlist->list); | 
|  | return (cmdlist); | 
|  | } | 
|  |  | 
|  | /* Append a command to a command list. */ | 
|  | void | 
|  | cmd_list_append(struct cmd_list *cmdlist, struct cmd *cmd) | 
|  | { | 
|  | cmd->group = cmdlist->group; | 
|  | TAILQ_INSERT_TAIL(cmdlist->list, cmd, qentry); | 
|  | } | 
|  |  | 
|  | /* Append all commands from one list to another.  */ | 
|  | void | 
|  | cmd_list_append_all(struct cmd_list *cmdlist, struct cmd_list *from) | 
|  | { | 
|  | struct cmd	*cmd; | 
|  |  | 
|  | TAILQ_FOREACH(cmd, from->list, qentry) | 
|  | cmd->group = cmdlist->group; | 
|  | TAILQ_CONCAT(cmdlist->list, from->list, qentry); | 
|  | } | 
|  |  | 
|  | /* Move all commands from one command list to another. */ | 
|  | void | 
|  | cmd_list_move(struct cmd_list *cmdlist, struct cmd_list *from) | 
|  | { | 
|  | TAILQ_CONCAT(cmdlist->list, from->list, qentry); | 
|  | cmdlist->group = cmd_list_next_group++; | 
|  | } | 
|  |  | 
|  | /* Free a command list. */ | 
|  | 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->list); | 
|  | free(cmdlist); | 
|  | } | 
|  |  | 
|  | /* Copy a command list, expanding %s in arguments. */ | 
|  | struct cmd_list * | 
|  | cmd_list_copy(struct cmd_list *cmdlist, int argc, char **argv) | 
|  | { | 
|  | struct cmd	*cmd; | 
|  | struct cmd_list	*new_cmdlist; | 
|  | struct cmd	*new_cmd; | 
|  | u_int		 group = cmdlist->group; | 
|  | char		*s; | 
|  |  | 
|  | s = cmd_list_print(cmdlist, 0); | 
|  | log_debug("%s: %s", __func__, s); | 
|  | free(s); | 
|  |  | 
|  | new_cmdlist = cmd_list_new(); | 
|  | TAILQ_FOREACH(cmd, cmdlist->list, qentry) { | 
|  | if (cmd->group != group) { | 
|  | new_cmdlist->group = cmd_list_next_group++; | 
|  | group = cmd->group; | 
|  | } | 
|  | new_cmd = cmd_copy(cmd, argc, argv); | 
|  | cmd_list_append(new_cmdlist, new_cmd); | 
|  | } | 
|  |  | 
|  | s = cmd_list_print(new_cmdlist, 0); | 
|  | log_debug("%s: %s", __func__, s); | 
|  | free(s); | 
|  |  | 
|  | return (new_cmdlist); | 
|  | } | 
|  |  | 
|  | /* Get a command list as a string. */ | 
|  | char * | 
|  | cmd_list_print(struct cmd_list *cmdlist, int escaped) | 
|  | { | 
|  | struct cmd	*cmd, *next; | 
|  | 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) + 6; | 
|  | buf = xrealloc(buf, len); | 
|  |  | 
|  | strlcat(buf, this, len); | 
|  |  | 
|  | next = TAILQ_NEXT(cmd, qentry); | 
|  | if (next != NULL) { | 
|  | if (cmd->group != next->group) { | 
|  | if (escaped) | 
|  | strlcat(buf, " \\;\\; ", len); | 
|  | else | 
|  | strlcat(buf, " ;; ", len); | 
|  | } else { | 
|  | if (escaped) | 
|  | strlcat(buf, " \\; ", len); | 
|  | else | 
|  | strlcat(buf, " ; ", len); | 
|  | } | 
|  | } | 
|  |  | 
|  | free(this); | 
|  | } | 
|  |  | 
|  | return (buf); | 
|  | } | 
|  |  | 
|  | /* Get first command in list. */ | 
|  | struct cmd * | 
|  | cmd_list_first(struct cmd_list *cmdlist) | 
|  | { | 
|  | return (TAILQ_FIRST(cmdlist->list)); | 
|  | } | 
|  |  | 
|  | /* Get next command in list. */ | 
|  | struct cmd * | 
|  | cmd_list_next(struct cmd *cmd) | 
|  | { | 
|  | return (TAILQ_NEXT(cmd, qentry)); | 
|  | } | 
|  |  | 
|  | /* Do all of the commands in this command list have this flag? */ | 
|  | int | 
|  | cmd_list_all_have(struct cmd_list *cmdlist, int flag) | 
|  | { | 
|  | struct cmd	*cmd; | 
|  |  | 
|  | TAILQ_FOREACH(cmd, cmdlist->list, qentry) { | 
|  | if (~cmd->entry->flags & flag) | 
|  | return (0); | 
|  | } | 
|  | return (1); | 
|  | } | 
|  |  | 
|  | /* Do any of the commands in this command list have this flag? */ | 
|  | int | 
|  | cmd_list_any_have(struct cmd_list *cmdlist, int flag) | 
|  | { | 
|  | struct cmd	*cmd; | 
|  |  | 
|  | TAILQ_FOREACH(cmd, cmdlist->list, qentry) { | 
|  | if (cmd->entry->flags & flag) | 
|  | return (1); | 
|  | } | 
|  | return (0); | 
|  | } | 
|  |  | 
|  | /* 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); | 
|  | } |