mirror of
https://github.com/tmux/tmux.git
synced 2024-11-16 17:39:09 +00:00
3f86d6d460
creating a new state for each group of commands, require the caller to create one and use it for all the commands in the list. This means the current target works even with list with multiple groups (which can happen if they are defined with newlines).
817 lines
19 KiB
C
817 lines
19 KiB
C
/* $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_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_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_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_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;
|
|
|
|
char *alias;
|
|
int argc;
|
|
char **argv;
|
|
|
|
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, 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, 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;
|
|
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);
|
|
}
|
|
|
|
/* 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(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);
|
|
}
|
|
|
|
/* Free a command. */
|
|
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);
|
|
}
|
|
|
|
/* 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);
|
|
}
|
|
|
|
/* Move all commands from one command list to another */
|
|
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++;
|
|
}
|
|
|
|
/* 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);
|
|
}
|
|
|
|
/* Get a command list as a string. */
|
|
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);
|
|
}
|
|
|
|
/* 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);
|
|
}
|