Merge branch 'obsd-master'

This commit is contained in:
Thomas Adam
2026-02-26 10:18:15 +00:00
12 changed files with 541 additions and 336 deletions

View File

@@ -106,6 +106,7 @@ dist_tmux_SOURCES = \
cmd-kill-window.c \ cmd-kill-window.c \
cmd-list-buffers.c \ cmd-list-buffers.c \
cmd-list-clients.c \ cmd-list-clients.c \
cmd-list-commands.c \
cmd-list-keys.c \ cmd-list-keys.c \
cmd-list-panes.c \ cmd-list-panes.c \
cmd-list-sessions.c \ cmd-list-sessions.c \

107
cmd-list-commands.c Normal file
View File

@@ -0,0 +1,107 @@
/* $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 <stdlib.h>
#include <string.h>
#include "tmux.h"
/*
* List all commands.
*/
#define LIST_COMMANDS_TEMPLATE \
"#{command_list_name}" \
"#{?command_list_alias, (#{command_list_alias}),} " \
"#{command_list_usage}"
static enum cmd_retval cmd_list_commands(struct cmd *, struct cmdq_item *);
const struct cmd_entry cmd_list_commands_entry = {
.name = "list-commands",
.alias = "lscm",
.args = { "F:", 0, 1, NULL },
.usage = "[-F format] [command]",
.flags = CMD_STARTSERVER|CMD_AFTERHOOK,
.exec = cmd_list_commands
};
static void
cmd_list_single_command(const struct cmd_entry *entry, struct format_tree *ft,
const char *template, struct cmdq_item *item)
{
const char *s;
char *line;
format_add(ft, "command_list_name", "%s", entry->name);
if (entry->alias != NULL)
s = entry->alias;
else
s = "";
format_add(ft, "command_list_alias", "%s", s);
if (entry->usage != NULL)
s = entry->usage;
else
s = "";
format_add(ft, "command_list_usage", "%s", s);
line = format_expand(ft, template);
if (*line != '\0')
cmdq_print(item, "%s", line);
free(line);
}
static enum cmd_retval
cmd_list_commands(struct cmd *self, struct cmdq_item *item)
{
struct args *args = cmd_get_args(self);
const struct cmd_entry **entryp;
const struct cmd_entry *entry;
struct format_tree *ft;
const char *template, *command;
char *cause;
if ((template = args_get(args, 'F')) == NULL)
template = LIST_COMMANDS_TEMPLATE;
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
format_defaults(ft, NULL, NULL, NULL, NULL);
command = args_string(args, 0);
if (command == NULL) {
for (entryp = cmd_table; *entryp != NULL; entryp++)
cmd_list_single_command(*entryp, ft, template, item);
} else {
entry = cmd_find(command, &cause);
if (entry != NULL)
cmd_list_single_command(entry, ft, template, item);
else {
cmdq_error(item, "%s", cause);
free(cause);
format_free(ft);
return (CMD_RETURN_ERROR);
}
}
format_free(ft);
return (CMD_RETURN_NORMAL);
}

View File

@@ -27,122 +27,140 @@
* List key bindings. * List key bindings.
*/ */
static enum cmd_retval cmd_list_keys_exec(struct cmd *, struct cmdq_item *); #define LIST_KEYS_TEMPLATE \
"#{?notes_only," \
"#{key_prefix} " \
"#{p|#{key_string_width}:key_string} " \
"#{?key_note,#{key_note},#{key_command}}" \
"," \
"bind-key #{?key_has_repeat,#{?key_repeat,-r, },} " \
"-T #{p|#{key_table_width}:key_table} " \
"#{p|#{key_string_width}:key_string} " \
"#{key_command}}"
static enum cmd_retval cmd_list_keys_commands(struct cmd *, static enum cmd_retval cmd_list_keys_exec(struct cmd *, struct cmdq_item *);
struct cmdq_item *);
const struct cmd_entry cmd_list_keys_entry = { const struct cmd_entry cmd_list_keys_entry = {
.name = "list-keys", .name = "list-keys",
.alias = "lsk", .alias = "lsk",
.args = { "1aNP:T:", 0, 1, NULL }, .args = { "1aF:NO:P:rT:", 0, 1, NULL },
.usage = "[-1aN] [-P prefix-string] [-T key-table] [key]", .usage = "[-1aNr] [-F format] [-O order] [-P prefix-string]"
"[-T key-table] [key]",
.flags = CMD_STARTSERVER|CMD_AFTERHOOK, .flags = CMD_STARTSERVER|CMD_AFTERHOOK,
.exec = cmd_list_keys_exec .exec = cmd_list_keys_exec
}; };
const struct cmd_entry cmd_list_commands_entry = { static char *
.name = "list-commands", cmd_list_keys_get_prefix(struct args *args)
.alias = "lscm", {
key_code prefix;
.args = { "F:", 0, 1, NULL }, if (args_has(args, 'P'))
.usage = "[-F format] [command]", return (xstrdup(args_get(args, 'P')));
.flags = CMD_STARTSERVER|CMD_AFTERHOOK, prefix = options_get_number(global_s_options, "prefix");
.exec = cmd_list_keys_exec if (prefix == KEYC_NONE)
}; return (xstrdup(""));
return (xstrdup(key_string_lookup_key(prefix, 0)));
}
static u_int static u_int
cmd_list_keys_get_width(const char *tablename, key_code only) cmd_list_keys_get_width(struct key_binding **l, u_int n)
{ {
struct key_table *table; u_int i, width, keywidth = 0;
struct key_binding *bd;
u_int width, keywidth = 0;
table = key_bindings_get_table(tablename, 0); for (i = 0; i < n; i++) {
if (table == NULL) width = utf8_cstrwidth(key_string_lookup_key(l[i]->key, 0));
return (0);
bd = key_bindings_first(table);
while (bd != NULL) {
if ((only != KEYC_UNKNOWN && bd->key != only) ||
KEYC_IS_MOUSE(bd->key) ||
bd->note == NULL ||
*bd->note == '\0') {
bd = key_bindings_next(table, bd);
continue;
}
width = utf8_cstrwidth(key_string_lookup_key(bd->key, 0));
if (width > keywidth) if (width > keywidth)
keywidth = width; keywidth = width;
bd = key_bindings_next(table, bd);
} }
return (keywidth); return (keywidth);
} }
static int static u_int
cmd_list_keys_print_notes(struct cmdq_item *item, struct args *args, cmd_list_keys_get_table_width(struct key_binding **l, u_int n)
const char *tablename, u_int keywidth, key_code only, const char *prefix)
{ {
struct client *tc = cmdq_get_target_client(item); u_int i, width, tablewidth = 0;
struct key_table *table;
struct key_binding *bd;
const char *key;
char *tmp, *note;
int found = 0;
table = key_bindings_get_table(tablename, 0); for (i = 0; i < n; i++) {
if (table == NULL) width = utf8_cstrwidth(l[i]->tablename);
return (0); if (width > tablewidth)
bd = key_bindings_first(table); tablewidth = width;
while (bd != NULL) {
if ((only != KEYC_UNKNOWN && bd->key != only) ||
KEYC_IS_MOUSE(bd->key) ||
((bd->note == NULL || *bd->note == '\0') &&
!args_has(args, 'a'))) {
bd = key_bindings_next(table, bd);
continue;
}
found = 1;
key = key_string_lookup_key(bd->key, 0);
if (bd->note == NULL || *bd->note == '\0')
note = cmd_list_print(bd->cmdlist,
CMD_LIST_PRINT_ESCAPED|CMD_LIST_PRINT_NO_GROUPS);
else
note = xstrdup(bd->note);
tmp = utf8_padcstr(key, keywidth + 1);
if (args_has(args, '1') && tc != NULL) {
status_message_set(tc, -1, 1, 0, 0, "%s%s%s", prefix,
tmp, note);
} else
cmdq_print(item, "%s%s%s", prefix, tmp, note);
free(tmp);
free(note);
if (args_has(args, '1'))
break;
bd = key_bindings_next(table, bd);
} }
return (found); return (tablewidth);
} }
static char * static struct key_binding **
cmd_list_keys_get_prefix(struct args *args, key_code *prefix) cmd_get_root_and_prefix(u_int *n, struct sort_criteria *sort_crit)
{ {
char *s; const char *tables[] = { "prefix", "root" };
struct key_table *t;
struct key_binding **lt;
u_int i, ltsz, len = 0, offset = 0;
static struct key_binding **l = NULL;
static u_int lsz = 0;
*prefix = options_get_number(global_s_options, "prefix"); for (i = 0; i < nitems(tables); i++) {
if (!args_has(args, 'P')) { t = key_bindings_get_table(tables[i], 0);
if (*prefix != KEYC_NONE) lt = sort_get_key_bindings_table(t, &ltsz, sort_crit);
xasprintf(&s, "%s ", key_string_lookup_key(*prefix, 0)); len += ltsz;
else if (lsz <= len) {
s = xstrdup(""); lsz = len + 100;
} else l = xreallocarray(l, lsz, sizeof *l);
s = xstrdup(args_get(args, 'P')); }
return (s); memcpy(l + offset, lt, ltsz * sizeof *l);
offset += ltsz;
}
*n = len;
return (l);
}
static void
cmd_filter_key_list(int filter_notes, int filter_key, key_code only,
struct key_binding **l, u_int *n)
{
key_code key;
u_int i, j = 0;
for (i = 0; i < *n; i++) {
key = l[i]->key & (KEYC_MASK_KEY|KEYC_MASK_MODIFIERS);
if (filter_key && only != key)
continue;
if (filter_notes && l[i]->note == NULL)
continue;
l[j++] = l[i];
}
*n = j;
}
static void
cmd_format_add_key_binding(struct format_tree *ft,
const struct key_binding *bd, const char *prefix)
{
const char *s;
if (bd->flags & KEY_BINDING_REPEAT)
format_add(ft, "key_repeat", "1");
else
format_add(ft, "key_repeat", "0");
if (bd->note != NULL)
format_add(ft, "key_note", "%s", bd->note);
else
format_add(ft, "key_note", "%s", "");
format_add(ft, "key_prefix", "%s", prefix);
format_add(ft, "key_table", "%s", bd->tablename);
s = key_string_lookup_key(bd->key, 0);
format_add(ft, "key_string", "%s", s);
s = cmd_list_print(bd->cmdlist,
CMD_LIST_PRINT_ESCAPED|CMD_LIST_PRINT_NO_GROUPS);
format_add(ft, "key_command", "%s", s);
} }
static enum cmd_retval static enum cmd_retval
@@ -150,16 +168,16 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
{ {
struct args *args = cmd_get_args(self); struct args *args = cmd_get_args(self);
struct client *tc = cmdq_get_target_client(item); struct client *tc = cmdq_get_target_client(item);
struct key_table *table; struct format_tree *ft;
struct key_binding *bd; struct key_table *table = NULL;
const char *tablename, *r, *keystr; struct key_binding **l;
char *key, *cp, *tmp, *start, *empty; key_code only = KEYC_UNKNOWN;
key_code prefix, only = KEYC_UNKNOWN; const char *template, *tablename, *keystr;
int repeat, width, tablewidth, keywidth, found = 0; char *line;
size_t tmpsize, tmpused, cplen; char *prefix = NULL;
u_int i, n;
if (cmd_get_entry(self) == &cmd_list_commands_entry) int single, notes_only, filter_notes, filter_key;
return (cmd_list_keys_commands(self, item)); struct sort_criteria sort_crit;
if ((keystr = args_string(args, 0)) != NULL) { if ((keystr = args_string(args, 0)) != NULL) {
only = key_string_lookup_string(keystr); only = key_string_lookup_string(keystr);
@@ -170,219 +188,60 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
only &= (KEYC_MASK_KEY|KEYC_MASK_MODIFIERS); only &= (KEYC_MASK_KEY|KEYC_MASK_MODIFIERS);
} }
tablename = args_get(args, 'T'); sort_crit.order = sort_order_from_string(args_get(args, 'O'));
if (tablename != NULL && key_bindings_get_table(tablename, 0) == NULL) { sort_crit.reversed = args_has(args, 'r');
cmdq_error(item, "table %s doesn't exist", tablename);
return (CMD_RETURN_ERROR);
}
if (args_has(args, 'N')) { prefix = cmd_list_keys_get_prefix(args);
if (tablename == NULL) { single = args_has(args, '1');
start = cmd_list_keys_get_prefix(args, &prefix); notes_only = args_has(args, 'N');
keywidth = cmd_list_keys_get_width("root", only);
if (prefix != KEYC_NONE) {
width = cmd_list_keys_get_width("prefix", only);
if (width == 0)
prefix = KEYC_NONE;
else if (width > keywidth)
keywidth = width;
}
empty = utf8_padcstr("", utf8_cstrwidth(start));
found = cmd_list_keys_print_notes(item, args, "root", if ((tablename = args_get(args, 'T')) != NULL) {
keywidth, only, empty); table = key_bindings_get_table(tablename, 0);
if (prefix != KEYC_NONE) { if (table == NULL) {
if (cmd_list_keys_print_notes(item, args, cmdq_error(item, "table %s doesn't exist", tablename);
"prefix", keywidth, only, start))
found = 1;
}
free(empty);
} else {
if (args_has(args, 'P'))
start = xstrdup(args_get(args, 'P'));
else
start = xstrdup("");
keywidth = cmd_list_keys_get_width(tablename, only);
found = cmd_list_keys_print_notes(item, args, tablename,
keywidth, only, start);
}
free(start);
goto out;
}
repeat = 0;
tablewidth = keywidth = 0;
table = key_bindings_first_table();
while (table != NULL) {
if (tablename != NULL && strcmp(table->name, tablename) != 0) {
table = key_bindings_next_table(table);
continue;
}
bd = key_bindings_first(table);
while (bd != NULL) {
if (only != KEYC_UNKNOWN && bd->key != only) {
bd = key_bindings_next(table, bd);
continue;
}
key = args_escape(key_string_lookup_key(bd->key, 0));
if (bd->flags & KEY_BINDING_REPEAT)
repeat = 1;
width = utf8_cstrwidth(table->name);
if (width > tablewidth)
tablewidth = width;
width = utf8_cstrwidth(key);
if (width > keywidth)
keywidth = width;
free(key);
bd = key_bindings_next(table, bd);
}
table = key_bindings_next_table(table);
}
tmpsize = 256;
tmp = xmalloc(tmpsize);
table = key_bindings_first_table();
while (table != NULL) {
if (tablename != NULL && strcmp(table->name, tablename) != 0) {
table = key_bindings_next_table(table);
continue;
}
bd = key_bindings_first(table);
while (bd != NULL) {
if (only != KEYC_UNKNOWN && bd->key != only) {
bd = key_bindings_next(table, bd);
continue;
}
found = 1;
key = args_escape(key_string_lookup_key(bd->key, 0));
if (!repeat)
r = "";
else if (bd->flags & KEY_BINDING_REPEAT)
r = "-r ";
else
r = " ";
tmpused = xsnprintf(tmp, tmpsize, "%s-T ", r);
cp = utf8_padcstr(table->name, tablewidth);
cplen = strlen(cp) + 1;
while (tmpused + cplen + 1 >= tmpsize) {
tmpsize *= 2;
tmp = xrealloc(tmp, tmpsize);
}
strlcat(tmp, cp, tmpsize);
tmpused = strlcat(tmp, " ", tmpsize);
free(cp);
cp = utf8_padcstr(key, keywidth);
cplen = strlen(cp) + 1;
while (tmpused + cplen + 1 >= tmpsize) {
tmpsize *= 2;
tmp = xrealloc(tmp, tmpsize);
}
strlcat(tmp, cp, tmpsize);
tmpused = strlcat(tmp, " ", tmpsize);
free(cp);
cp = cmd_list_print(bd->cmdlist,
CMD_LIST_PRINT_ESCAPED|CMD_LIST_PRINT_NO_GROUPS);
cplen = strlen(cp);
while (tmpused + cplen + 1 >= tmpsize) {
tmpsize *= 2;
tmp = xrealloc(tmp, tmpsize);
}
strlcat(tmp, cp, tmpsize);
free(cp);
if (args_has(args, '1') && tc != NULL) {
status_message_set(tc, -1, 1, 0, 0,
"bind-key %s", tmp);
} else
cmdq_print(item, "bind-key %s", tmp);
free(key);
if (args_has(args, '1'))
break;
bd = key_bindings_next(table, bd);
}
table = key_bindings_next_table(table);
}
free(tmp);
out:
if (only != KEYC_UNKNOWN && !found) {
cmdq_error(item, "unknown key: %s", args_string(args, 0));
return (CMD_RETURN_ERROR);
}
return (CMD_RETURN_NORMAL);
}
static void
cmd_list_single_command(const struct cmd_entry *entry, struct format_tree *ft,
const char *template, struct cmdq_item *item)
{
const char *s;
char *line;
format_add(ft, "command_list_name", "%s", entry->name);
if (entry->alias != NULL)
s = entry->alias;
else
s = "";
format_add(ft, "command_list_alias", "%s", s);
if (entry->usage != NULL)
s = entry->usage;
else
s = "";
format_add(ft, "command_list_usage", "%s", s);
line = format_expand(ft, template);
if (*line != '\0')
cmdq_print(item, "%s", line);
free(line);
}
static enum cmd_retval
cmd_list_keys_commands(struct cmd *self, struct cmdq_item *item)
{
struct args *args = cmd_get_args(self);
const struct cmd_entry **entryp;
const struct cmd_entry *entry;
struct format_tree *ft;
const char *template, *command;
char *cause;
if ((template = args_get(args, 'F')) == NULL) {
template = "#{command_list_name}"
"#{?command_list_alias, (#{command_list_alias}),} "
"#{command_list_usage}";
}
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
format_defaults(ft, NULL, NULL, NULL, NULL);
command = args_string(args, 0);
if (command == NULL) {
for (entryp = cmd_table; *entryp != NULL; entryp++)
cmd_list_single_command(*entryp, ft, template, item);
} else {
entry = cmd_find(command, &cause);
if (entry != NULL)
cmd_list_single_command(entry, ft, template, item);
else {
cmdq_error(item, "%s", cause);
free(cause);
format_free(ft);
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
} }
if ((template = args_get(args, 'F')) == NULL)
template = LIST_KEYS_TEMPLATE;
if (table)
l = sort_get_key_bindings_table(table, &n, &sort_crit);
else if (notes_only)
l = cmd_get_root_and_prefix(&n, &sort_crit);
else
l = sort_get_key_bindings(&n, &sort_crit);
filter_notes = notes_only && !args_has(args, 'a');
filter_key = only != KEYC_UNKNOWN;
if (filter_notes || filter_key)
cmd_filter_key_list(filter_notes, filter_key, only, l, &n);
if (single)
n = 1;
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
format_defaults(ft, NULL, NULL, NULL, NULL);
format_add(ft, "notes_only", "%d", notes_only);
format_add(ft, "key_has_repeat", "%d", key_bindings_has_repeat(l, n));
format_add(ft, "key_string_width", "%u", cmd_list_keys_get_width(l, n));
format_add(ft, "key_table_width", "%u",
cmd_list_keys_get_table_width(l, n));
for (i = 0; i < n; i++) {
cmd_format_add_key_binding(ft, l[i], prefix);
line = format_expand(ft, template);
if ((single && tc != NULL) || n == 1)
status_message_set(tc, -1, 1, 0, 0, "%s", line);
else if (*line != '\0')
cmdq_print(item, "%s", line);
free(line);
if (single)
break;
}
format_free(ft); format_free(ft);
free(prefix);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }

View File

@@ -33,8 +33,8 @@ const struct cmd_entry cmd_paste_buffer_entry = {
.name = "paste-buffer", .name = "paste-buffer",
.alias = "pasteb", .alias = "pasteb",
.args = { "db:prs:t:", 0, 0, NULL }, .args = { "db:prSs:t:", 0, 0, NULL },
.usage = "[-dpr] [-s separator] " CMD_BUFFER_USAGE " " .usage = "[-dprS] [-s separator] " CMD_BUFFER_USAGE " "
CMD_TARGET_PANE_USAGE, CMD_TARGET_PANE_USAGE,
.target = { 't', CMD_FIND_PANE, 0 }, .target = { 't', CMD_FIND_PANE, 0 },
@@ -43,6 +43,17 @@ const struct cmd_entry cmd_paste_buffer_entry = {
.exec = cmd_paste_buffer_exec .exec = cmd_paste_buffer_exec
}; };
static void
cmd_paste_buffer_paste(struct window_pane *wp, const char *buf, size_t len)
{
char *cp;
size_t n;
n = utf8_stravisx(&cp, buf, len, VIS_SAFE);
bufferevent_write(wp->event, cp, n);
free(cp);
}
static enum cmd_retval static enum cmd_retval
cmd_paste_buffer_exec(struct cmd *self, struct cmdq_item *item) cmd_paste_buffer_exec(struct cmd *self, struct cmdq_item *item)
{ {
@@ -51,7 +62,7 @@ cmd_paste_buffer_exec(struct cmd *self, struct cmdq_item *item)
struct window_pane *wp = target->wp; struct window_pane *wp = target->wp;
struct paste_buffer *pb; struct paste_buffer *pb;
const char *sepstr, *bufname, *bufdata, *bufend, *line; const char *sepstr, *bufname, *bufdata, *bufend, *line;
size_t seplen, bufsize; size_t seplen, bufsize, len;
int bracket = args_has(args, 'p'); int bracket = args_has(args, 'p');
if (window_pane_exited(wp)) { if (window_pane_exited(wp)) {
@@ -93,14 +104,22 @@ cmd_paste_buffer_exec(struct cmd *self, struct cmdq_item *item)
line = memchr(bufdata, '\n', bufend - bufdata); line = memchr(bufdata, '\n', bufend - bufdata);
if (line == NULL) if (line == NULL)
break; break;
len = line - bufdata;
bufferevent_write(wp->event, bufdata, line - bufdata); if (args_has(args, 'S'))
bufferevent_write(wp->event, bufdata, len);
else
cmd_paste_buffer_paste(wp, bufdata, len);
bufferevent_write(wp->event, sepstr, seplen); bufferevent_write(wp->event, sepstr, seplen);
bufdata = line + 1; bufdata = line + 1;
} }
if (bufdata != bufend) if (bufdata != bufend) {
bufferevent_write(wp->event, bufdata, bufend - bufdata); len = bufend - bufdata;
if (args_has(args, 'S'))
bufferevent_write(wp->event, bufdata, len);
else
cmd_paste_buffer_paste(wp, bufdata, len);
}
if (bracket && (wp->screen->mode & MODE_BRACKETPASTE)) if (bracket && (wp->screen->mode & MODE_BRACKETPASTE))
bufferevent_write(wp->event, "\033[201~", 6); bufferevent_write(wp->event, "\033[201~", 6);

View File

@@ -5239,11 +5239,13 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
done: done:
/* Expand again if required. */ /* Expand again if required. */
if (modifiers & FORMAT_EXPAND) { if (modifiers & FORMAT_EXPAND) {
new = format_expand1(es, value); format_copy_state(&next, es, FORMAT_EXPAND_NOJOBS);
new = format_expand1(&next, value);
free(value); free(value);
value = new; value = new;
} else if (modifiers & FORMAT_EXPANDTIME) { } else if (modifiers & FORMAT_EXPANDTIME) {
format_copy_state(&next, es, FORMAT_EXPAND_TIME); format_copy_state(&next, es, FORMAT_EXPAND_TIME|
FORMAT_EXPAND_NOJOBS);
new = format_expand1(&next, value); new = format_expand1(&next, value);
free(value); free(value);
value = new; value = new;

12
input.c
View File

@@ -1625,10 +1625,6 @@ input_csi_dispatch(struct input_ctx *ictx)
} }
input_reply(ictx, 1, "\033[?12;%d$y", n); input_reply(ictx, 1, "\033[?12;%d$y", n);
break; break;
case 2004: /* bracketed paste */
n = (s->mode & MODE_BRACKETPASTE) ? 1 : 2;
input_reply(ictx, 1, "\033[?2004;%d$y", n);
break;
case 1004: /* focus reporting */ case 1004: /* focus reporting */
n = (s->mode & MODE_FOCUSON) ? 1 : 2; n = (s->mode & MODE_FOCUSON) ? 1 : 2;
input_reply(ictx, 1, "\033[?1004;%d$y", n); input_reply(ictx, 1, "\033[?1004;%d$y", n);
@@ -1637,6 +1633,14 @@ input_csi_dispatch(struct input_ctx *ictx)
n = (s->mode & MODE_MOUSE_SGR) ? 1 : 2; n = (s->mode & MODE_MOUSE_SGR) ? 1 : 2;
input_reply(ictx, 1, "\033[?1006;%d$y", n); input_reply(ictx, 1, "\033[?1006;%d$y", n);
break; break;
case 2004: /* bracketed paste */
n = (s->mode & MODE_BRACKETPASTE) ? 1 : 2;
input_reply(ictx, 1, "\033[?2004;%d$y", n);
break;
case 2026: /* synchronized output */
n = (s->mode & MODE_SYNC) ? 1 : 2;
input_reply(ictx, 1, "\033[?2026;%d$y", n);
break;
case 2031: case 2031:
input_reply(ictx, 1, "\033[?2031;2$y"); input_reply(ictx, 1, "\033[?2031;2$y");
break; break;

View File

@@ -215,6 +215,7 @@ key_bindings_add(const char *name, key_code key, const char *note, int repeat,
bd = xcalloc(1, sizeof *bd); bd = xcalloc(1, sizeof *bd);
bd->key = (key & ~KEYC_MASK_FLAGS); bd->key = (key & ~KEYC_MASK_FLAGS);
bd->tablename = table->name;
if (note != NULL) if (note != NULL)
bd->note = xstrdup(note); bd->note = xstrdup(note);
RB_INSERT(key_bindings, &table->key_bindings, bd); RB_INSERT(key_bindings, &table->key_bindings, bd);
@@ -702,3 +703,15 @@ key_bindings_dispatch(struct key_binding *bd, struct cmdq_item *item,
new_item = cmdq_append(c, new_item); new_item = cmdq_append(c, new_item);
return (new_item); return (new_item);
} }
int
key_bindings_has_repeat(struct key_binding **l, u_int n)
{
u_int i;
for (i = 0; i < n; i++) {
if (l[i]->flags & KEY_BINDING_REPEAT)
return (1);
}
return (0);
}

101
sort.c
View File

@@ -72,6 +72,7 @@ sort_buffer_cmp(const void *a0, const void *b0)
break; break;
case SORT_ACTIVITY: case SORT_ACTIVITY:
case SORT_INDEX: case SORT_INDEX:
case SORT_MODIFIER:
case SORT_ORDER: case SORT_ORDER:
case SORT_END: case SORT_END:
break; break;
@@ -117,6 +118,7 @@ sort_client_cmp(const void *a0, const void *b0)
result = 1; result = 1;
break; break;
case SORT_INDEX: case SORT_INDEX:
case SORT_MODIFIER:
case SORT_ORDER: case SORT_ORDER:
case SORT_END: case SORT_END:
break; break;
@@ -167,6 +169,7 @@ sort_session_cmp(const void *a0, const void *b0)
case SORT_NAME: case SORT_NAME:
result = strcmp(sa->name, sb->name); result = strcmp(sa->name, sb->name);
break; break;
case SORT_MODIFIER:
case SORT_ORDER: case SORT_ORDER:
case SORT_SIZE: case SORT_SIZE:
case SORT_END: case SORT_END:
@@ -208,6 +211,7 @@ sort_pane_cmp(const void *a0, const void *b0)
case SORT_NAME: case SORT_NAME:
result = strcmp(a->screen->title, b->screen->title); result = strcmp(a->screen->title, b->screen->title);
break; break;
case SORT_MODIFIER:
case SORT_ORDER: case SORT_ORDER:
case SORT_END: case SORT_END:
break; break;
@@ -263,6 +267,7 @@ sort_winlink_cmp(const void *a0, const void *b0)
case SORT_SIZE: case SORT_SIZE:
result = wa->sx * wa->sy - wb->sx * wb->sy; result = wa->sx * wa->sy - wb->sx * wb->sy;
break; break;
case SORT_MODIFIER:
case SORT_ORDER: case SORT_ORDER:
case SORT_END: case SORT_END:
break; break;
@@ -276,6 +281,41 @@ sort_winlink_cmp(const void *a0, const void *b0)
return (result); return (result);
} }
static int
sort_key_binding_cmp(const void *a0, const void *b0)
{
struct sort_criteria *sort_crit = sort_criteria;
const struct key_binding *a = *(struct key_binding **)a0;
const struct key_binding *b = *(struct key_binding **)b0;
int result = 0;
switch (sort_crit->order) {
case SORT_INDEX:
result = a->key - b->key;
break;
case SORT_MODIFIER:
result = (a->key & KEYC_MASK_MODIFIERS) -
(b->key & KEYC_MASK_MODIFIERS);
break;
case SORT_NAME:
result = strcasecmp(a->tablename, b->tablename) == 0;
break;
case SORT_ACTIVITY:
case SORT_CREATION:
case SORT_ORDER:
case SORT_SIZE:
case SORT_END:
break;
}
if (result == 0)
result = strcasecmp(a->tablename, b->tablename) == 0;
if (sort_crit->reversed)
result = -result;
return (result);
}
void void
sort_next_order(struct sort_criteria *sort_crit) sort_next_order(struct sort_criteria *sort_crit)
{ {
@@ -306,8 +346,11 @@ sort_order_from_string(const char* order)
return (SORT_ACTIVITY); return (SORT_ACTIVITY);
if (strcasecmp(order, "creation") == 0) if (strcasecmp(order, "creation") == 0)
return (SORT_CREATION); return (SORT_CREATION);
if (strcasecmp(order, "index") == 0) if (strcasecmp(order, "index") == 0 ||
strcasecmp(order, "key") == 0)
return (SORT_INDEX); return (SORT_INDEX);
if (strcasecmp(order, "modifier") == 0)
return (SORT_MODIFIER);
if (strcasecmp(order, "name") == 0 || if (strcasecmp(order, "name") == 0 ||
strcasecmp(order, "title") == 0) strcasecmp(order, "title") == 0)
return (SORT_NAME); return (SORT_NAME);
@@ -328,6 +371,8 @@ sort_order_to_string(enum sort_order order)
return "creation"; return "creation";
if (order == SORT_INDEX) if (order == SORT_INDEX)
return "index"; return "index";
if (order == SORT_MODIFIER)
return "modifier";
if (order == SORT_NAME) if (order == SORT_NAME)
return "name"; return "name";
if (order == SORT_ORDER) if (order == SORT_ORDER)
@@ -548,3 +593,57 @@ sort_get_winlinks_session(struct session *s, u_int *n,
return (l); return (l);
} }
struct key_binding **
sort_get_key_bindings(u_int *n, struct sort_criteria *sort_crit)
{
struct key_table *table;
struct key_binding *bd;
u_int i = 0;
static struct key_binding **l = NULL;
static u_int lsz = 0;
table = key_bindings_first_table();
while (table != NULL) {
bd = key_bindings_first(table);
while (bd != NULL) {
if (lsz <= i) {
lsz += 100;
l = xreallocarray(l, lsz, sizeof *l);
}
l[i++] = bd;
bd = key_bindings_next(table, bd);
}
table = key_bindings_next_table(table);
}
sort_qsort(l, i, sizeof *l, sort_key_binding_cmp, sort_crit);
*n = i;
return (l);
}
struct key_binding **
sort_get_key_bindings_table(struct key_table *table, u_int *n,
struct sort_criteria *sort_crit)
{
struct key_binding *bd;
u_int i = 0;
static struct key_binding **l = NULL;
static u_int lsz = 0;
bd = key_bindings_first(table);
while (bd != NULL) {
if (lsz <= i) {
lsz += 100;
l = xreallocarray(l, lsz, sizeof *l);
}
l[i++] = bd;
bd = key_bindings_next(table, bd);
}
sort_qsort(l, i, sizeof *l, sort_key_binding_cmp, sort_crit);
*n = i;
return (l);
}

65
tmux.1
View File

@@ -2247,6 +2247,18 @@ Same as
.Ic scroll-down .Ic scroll-down
but also exit copy mode if the cursor reaches the bottom. but also exit copy mode if the cursor reaches the bottom.
.It Xo .It Xo
.It Xo
.Ic scroll-exit-on
.Xc
Turn on exiting copy mode when scrolling to the end of the buffer.
.It Xo
.Ic scroll-exit-off
.Xc
Turn off exiting copy mode when scrolling to the end of the buffer.
.It Xo
.Ic scroll-exit-toggle
.Xc
Toggle exiting copy mode when scrolling to the end of the buffer.
.Ic scroll-middle .Ic scroll-middle
(vi: z) (vi: z)
.Xc .Xc
@@ -3827,24 +3839,45 @@ To view the default bindings and possible commands, see the
command. command.
.Tg lsk .Tg lsk
.It Xo Ic list-keys .It Xo Ic list-keys
.Op Fl 1aN .Op Fl 1aNr
.Op Fl F Ar format
.Op Fl O Ar sort-order
.Op Fl P Ar prefix-string .Op Fl P Ar prefix-string
.Op Fl T Ar key-table .Op Fl T Ar key-table
.Op Ar key .Op Ar key
.Xc .Xc
.D1 Pq alias: Ic lsk .D1 Pq alias: Ic lsk
List key bindings. List key bindings.
There are two forms: the default lists keys as .Fl F
specifies the format of each line.
See the
.Sx FORMATS
section.
.Fl T
specifies a
.Ar key-table
to list from.
.Fl 1
lists only the first matching key.
.Fl O
specifies the sort order: one of
.Ql key ,
.Ql modifier ,
.Ql name
(table name).
.Fl r
reverses the sort order.
.Pp
If no
.Ar format
is given, there are two forms: the default lists keys as
.Ic bind-key .Ic bind-key
commands; commands;
.Fl N .Fl N
lists only keys with attached notes and shows only the key and note for each lists only keys with attached notes and shows only the key and note for each
key. key.
.Pp .Pp
With the default form, all key tables are listed by default. With the default form, all key tables are listed unless specified otherwise.
.Fl T
lists only keys in
.Ar key-table .
.Pp .Pp
With the With the
.Fl N .Fl N
@@ -3857,9 +3890,7 @@ key tables are listed by default;
also lists only keys in also lists only keys in
.Ar key-table . .Ar key-table .
.Fl P .Fl P
specifies a prefix to print before each key and specifies a prefix to print before each key.
.Fl 1
lists only the first matching key.
.Fl a .Fl a
lists the command for keys that do not have a note rather than skipping them. lists the command for keys that do not have a note rather than skipping them.
.Tg send .Tg send
@@ -6236,6 +6267,15 @@ The following variables are available, where appropriate:
.It Li "host" Ta "#H" Ta "Hostname of local host" .It Li "host" Ta "#H" Ta "Hostname of local host"
.It Li "host_short" Ta "#h" Ta "Hostname of local host (no domain name)" .It Li "host_short" Ta "#h" Ta "Hostname of local host (no domain name)"
.It Li "insert_flag" Ta "" Ta "Pane insert flag" .It Li "insert_flag" Ta "" Ta "Pane insert flag"
.It Li "key_string" Ta "" Ta "String representation of the key binding"
.It Li "key_repeat" Ta "" Ta "1 if key binding is repeatable"
.It Li "key_note" Ta "" Ta "Note of the key binding"
.It Li "key_prefix" Ta "" Ta "Global key prefix"
.It Li "key_table" Ta "" Ta "Table name of the key binding"
.It Li "key_command" Ta "" Ta "Command of the key binding"
.It Li "key_has_repeat" Ta "" Ta "1 if list contain a repeatable key"
.It Li "key_string_width" Ta "" Ta "Maximum key_string width in list"
.It Li "key_table_width" Ta "" Ta "Maximum key_table width in list"
.It Li "keypad_cursor_flag" Ta "" Ta "Pane keypad cursor flag" .It Li "keypad_cursor_flag" Ta "" Ta "Pane keypad cursor flag"
.It Li "keypad_flag" Ta "" Ta "Pane keypad flag" .It Li "keypad_flag" Ta "" Ta "Pane keypad flag"
.It Li "last_window_index" Ta "" Ta "Index of last window in session" .It Li "last_window_index" Ta "" Ta "Index of last window in session"
@@ -7411,7 +7451,7 @@ is
the contents are read from stdin. the contents are read from stdin.
.Tg pasteb .Tg pasteb
.It Xo Ic paste-buffer .It Xo Ic paste-buffer
.Op Fl dpr .Op Fl dprS
.Op Fl b Ar buffer-name .Op Fl b Ar buffer-name
.Op Fl s Ar separator .Op Fl s Ar separator
.Op Fl t Ar target-pane .Op Fl t Ar target-pane
@@ -7419,9 +7459,14 @@ the contents are read from stdin.
.D1 Pq alias: Ic pasteb .D1 Pq alias: Ic pasteb
Insert the contents of a paste buffer into the specified pane. Insert the contents of a paste buffer into the specified pane.
If not specified, paste into the current one. If not specified, paste into the current one.
By default, control characters are sanitized with
.Xr vis 3 ;
.Fl S
disables this.
With With
.Fl d , .Fl d ,
also delete the paste buffer. also delete the paste buffer.
.Pp
When output, any linefeed (LF) characters in the paste buffer are replaced with When output, any linefeed (LF) characters in the paste buffer are replaced with
a separator, by default carriage return (CR). a separator, by default carriage return (CR).
A custom separator may be specified using the A custom separator may be specified using the

13
tmux.h
View File

@@ -2146,6 +2146,7 @@ struct key_binding {
key_code key; key_code key;
struct cmd_list *cmdlist; struct cmd_list *cmdlist;
const char *note; const char *note;
const char *tablename;
int flags; int flags;
#define KEY_BINDING_REPEAT 0x1 #define KEY_BINDING_REPEAT 0x1
@@ -2284,6 +2285,7 @@ enum sort_order {
SORT_ACTIVITY, SORT_ACTIVITY,
SORT_CREATION, SORT_CREATION,
SORT_INDEX, SORT_INDEX,
SORT_MODIFIER,
SORT_NAME, SORT_NAME,
SORT_ORDER, SORT_ORDER,
SORT_SIZE, SORT_SIZE,
@@ -2382,6 +2384,10 @@ struct window_pane **sort_get_panes_window(struct window *, u_int *,
struct winlink **sort_get_winlinks(u_int *, struct sort_criteria *); struct winlink **sort_get_winlinks(u_int *, struct sort_criteria *);
struct winlink **sort_get_winlinks_session(struct session *, u_int *, struct winlink **sort_get_winlinks_session(struct session *, u_int *,
struct sort_criteria *); struct sort_criteria *);
struct key_binding **sort_get_key_bindings(u_int *,
struct sort_criteria *);
struct key_binding **sort_get_key_bindings_table(struct key_table *,
u_int *, struct sort_criteria *);
/* format.c */ /* format.c */
#define FORMAT_STATUS 0x1 #define FORMAT_STATUS 0x1
@@ -2894,6 +2900,7 @@ void key_bindings_reset(const char *, key_code);
void key_bindings_remove_table(const char *); void key_bindings_remove_table(const char *);
void key_bindings_reset_table(const char *); void key_bindings_reset_table(const char *);
void key_bindings_init(void); void key_bindings_init(void);
int key_bindings_has_repeat(struct key_binding **, u_int);
struct cmdq_item *key_bindings_dispatch(struct key_binding *, struct cmdq_item *key_bindings_dispatch(struct key_binding *,
struct cmdq_item *, struct client *, struct key_event *, struct cmdq_item *, struct client *, struct key_event *,
struct cmd_find_state *); struct cmd_find_state *);
@@ -3615,9 +3622,9 @@ void utf8_copy(struct utf8_data *, const struct utf8_data *);
enum utf8_state utf8_open(struct utf8_data *, u_char); enum utf8_state utf8_open(struct utf8_data *, u_char);
enum utf8_state utf8_append(struct utf8_data *, u_char); enum utf8_state utf8_append(struct utf8_data *, u_char);
int utf8_isvalid(const char *); int utf8_isvalid(const char *);
int utf8_strvis(char *, const char *, size_t, int); size_t utf8_strvis(char *, const char *, size_t, int);
int utf8_stravis(char **, const char *, int); size_t utf8_stravis(char **, const char *, int);
int utf8_stravisx(char **, const char *, size_t, int); size_t utf8_stravisx(char **, const char *, size_t, int);
char *utf8_sanitize(const char *); char *utf8_sanitize(const char *);
size_t utf8_strlen(const struct utf8_data *); size_t utf8_strlen(const struct utf8_data *);
u_int utf8_strwidth(const struct utf8_data *, ssize_t); u_int utf8_strwidth(const struct utf8_data *, ssize_t);

10
utf8.c
View File

@@ -652,7 +652,7 @@ utf8_append(struct utf8_data *ud, u_char ch)
* bytes available for each character from src (for \abc or UTF-8) plus space * bytes available for each character from src (for \abc or UTF-8) plus space
* for \0. * for \0.
*/ */
int size_t
utf8_strvis(char *dst, const char *src, size_t len, int flag) utf8_strvis(char *dst, const char *src, size_t len, int flag)
{ {
struct utf8_data ud; struct utf8_data ud;
@@ -690,11 +690,11 @@ utf8_strvis(char *dst, const char *src, size_t len, int flag)
} }
/* Same as utf8_strvis but allocate the buffer. */ /* Same as utf8_strvis but allocate the buffer. */
int size_t
utf8_stravis(char **dst, const char *src, int flag) utf8_stravis(char **dst, const char *src, int flag)
{ {
char *buf; char *buf;
int len; size_t len;
buf = xreallocarray(NULL, 4, strlen(src) + 1); buf = xreallocarray(NULL, 4, strlen(src) + 1);
len = utf8_strvis(buf, src, strlen(src), flag); len = utf8_strvis(buf, src, strlen(src), flag);
@@ -704,11 +704,11 @@ utf8_stravis(char **dst, const char *src, int flag)
} }
/* Same as utf8_strvis but allocate the buffer. */ /* Same as utf8_strvis but allocate the buffer. */
int size_t
utf8_stravisx(char **dst, const char *src, size_t srclen, int flag) utf8_stravisx(char **dst, const char *src, size_t srclen, int flag)
{ {
char *buf; char *buf;
int len; size_t len;
buf = xreallocarray(NULL, 4, srclen + 1); buf = xreallocarray(NULL, 4, srclen + 1);
len = utf8_strvis(buf, src, srclen, flag); len = utf8_strvis(buf, src, srclen, flag);

View File

@@ -2127,6 +2127,36 @@ window_copy_cmd_rectangle_toggle(struct window_copy_cmd_state *cs)
return (WINDOW_COPY_CMD_NOTHING); return (WINDOW_COPY_CMD_NOTHING);
} }
static enum window_copy_cmd_action
window_copy_cmd_scroll_exit_on(struct window_copy_cmd_state *cs)
{
struct window_copy_mode_data *data = cs->wme->data;
data->scroll_exit = 1;
return (WINDOW_COPY_CMD_NOTHING);
}
static enum window_copy_cmd_action
window_copy_cmd_scroll_exit_off(struct window_copy_cmd_state *cs)
{
struct window_copy_mode_data *data = cs->wme->data;
data->scroll_exit = 0;
return (WINDOW_COPY_CMD_NOTHING);
}
static enum window_copy_cmd_action
window_copy_cmd_scroll_exit_toggle(struct window_copy_cmd_state *cs)
{
struct window_copy_mode_data *data = cs->wme->data;
data->scroll_exit = !data->scroll_exit;
return (WINDOW_COPY_CMD_NOTHING);
}
static enum window_copy_cmd_action static enum window_copy_cmd_action
window_copy_cmd_scroll_down(struct window_copy_cmd_state *cs) window_copy_cmd_scroll_down(struct window_copy_cmd_state *cs)
{ {
@@ -2702,16 +2732,20 @@ window_copy_cmd_refresh_from_pane(struct window_copy_cmd_state *cs)
struct window_mode_entry *wme = cs->wme; struct window_mode_entry *wme = cs->wme;
struct window_pane *wp = wme->swp; struct window_pane *wp = wme->swp;
struct window_copy_mode_data *data = wme->data; struct window_copy_mode_data *data = wme->data;
u_int oy_from_top;
if (data->viewmode) if (data->viewmode)
return (WINDOW_COPY_CMD_NOTHING); return (WINDOW_COPY_CMD_NOTHING);
oy_from_top = screen_hsize(data->backing) - data->oy;
screen_free(data->backing); screen_free(data->backing);
free(data->backing); free(data->backing);
data->backing = window_copy_clone_screen(&wp->base, &data->screen, NULL, data->backing = window_copy_clone_screen(&wp->base, &data->screen, NULL,
NULL, wme->swp != wme->wp); NULL, wme->swp != wme->wp);
if (data->oy > screen_hsize(data->backing)) { if (oy_from_top <= screen_hsize(data->backing))
data->oy = screen_hsize(data->backing) - oy_from_top;
else {
data->cy = 0; data->cy = 0;
data->oy = screen_hsize(data->backing); data->oy = screen_hsize(data->backing);
} }
@@ -3073,6 +3107,21 @@ static const struct {
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
.f = window_copy_cmd_scroll_down_and_cancel .f = window_copy_cmd_scroll_down_and_cancel
}, },
{ .command = "scroll-exit-on",
.args = { "", 0, 0, NULL },
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
.f = window_copy_cmd_scroll_exit_on
},
{ .command = "scroll-exit-off",
.args = { "", 0, 0, NULL },
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
.f = window_copy_cmd_scroll_exit_off
},
{ .command = "scroll-exit-toggle",
.args = { "", 0, 0, NULL },
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
.f = window_copy_cmd_scroll_exit_toggle
},
{ .command = "scroll-middle", { .command = "scroll-middle",
.args = { "", 0, 0, NULL }, .args = { "", 0, 0, NULL },
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,