mirror of
https://github.com/tmux/tmux.git
synced 2025-04-10 11:08:49 +00:00
Permit shortcut keys in buffer, client, tree modes to be configured with
a format; the default remains the line number. GitHub issue 2636.
This commit is contained in:
parent
73cbe46f8d
commit
cd208c9d72
@ -30,9 +30,9 @@ const struct cmd_entry cmd_choose_tree_entry = {
|
|||||||
.name = "choose-tree",
|
.name = "choose-tree",
|
||||||
.alias = NULL,
|
.alias = NULL,
|
||||||
|
|
||||||
.args = { "F:Gf:NO:rst:wZ", 0, 1 },
|
.args = { "F:f:GK:NO:rst:wZ", 0, 1 },
|
||||||
.usage = "[-GNrswZ] [-F format] [-f filter] [-O sort-order] "
|
.usage = "[-GNrswZ] [-F format] [-f filter] [-K key-format] "
|
||||||
CMD_TARGET_PANE_USAGE " [template]",
|
"[-O sort-order] " CMD_TARGET_PANE_USAGE " [template]",
|
||||||
|
|
||||||
.target = { 't', CMD_FIND_PANE, 0 },
|
.target = { 't', CMD_FIND_PANE, 0 },
|
||||||
|
|
||||||
@ -44,9 +44,9 @@ const struct cmd_entry cmd_choose_client_entry = {
|
|||||||
.name = "choose-client",
|
.name = "choose-client",
|
||||||
.alias = NULL,
|
.alias = NULL,
|
||||||
|
|
||||||
.args = { "F:f:NO:rt:Z", 0, 1 },
|
.args = { "F:f:K:NO:rt:Z", 0, 1 },
|
||||||
.usage = "[-NrZ] [-F format] [-f filter] [-O sort-order] "
|
.usage = "[-NrZ] [-F format] [-f filter] [-K key-format] "
|
||||||
CMD_TARGET_PANE_USAGE " [template]",
|
"[-O sort-order] " CMD_TARGET_PANE_USAGE " [template]",
|
||||||
|
|
||||||
.target = { 't', CMD_FIND_PANE, 0 },
|
.target = { 't', CMD_FIND_PANE, 0 },
|
||||||
|
|
||||||
@ -58,9 +58,9 @@ const struct cmd_entry cmd_choose_buffer_entry = {
|
|||||||
.name = "choose-buffer",
|
.name = "choose-buffer",
|
||||||
.alias = NULL,
|
.alias = NULL,
|
||||||
|
|
||||||
.args = { "F:f:NO:rt:Z", 0, 1 },
|
.args = { "F:f:K:NO:rt:Z", 0, 1 },
|
||||||
.usage = "[-NrZ] [-F format] [-f filter] [-O sort-order] "
|
.usage = "[-NrZ] [-F format] [-f filter] [-K key-format] "
|
||||||
CMD_TARGET_PANE_USAGE " [template]",
|
"[-O sort-order] " CMD_TARGET_PANE_USAGE " [template]",
|
||||||
|
|
||||||
.target = { 't', CMD_FIND_PANE, 0 },
|
.target = { 't', CMD_FIND_PANE, 0 },
|
||||||
|
|
||||||
|
34
format.c
34
format.c
@ -100,6 +100,7 @@ format_job_cmp(struct format_job *fj1, struct format_job *fj2)
|
|||||||
#define FORMAT_QUOTE_STYLE 0x2000
|
#define FORMAT_QUOTE_STYLE 0x2000
|
||||||
#define FORMAT_WINDOW_NAME 0x4000
|
#define FORMAT_WINDOW_NAME 0x4000
|
||||||
#define FORMAT_SESSION_NAME 0x8000
|
#define FORMAT_SESSION_NAME 0x8000
|
||||||
|
#define FORMAT_CHARACTER 0x10000
|
||||||
|
|
||||||
/* Limit on recursion. */
|
/* Limit on recursion. */
|
||||||
#define FORMAT_LOOP_LIMIT 10
|
#define FORMAT_LOOP_LIMIT 10
|
||||||
@ -3522,7 +3523,7 @@ format_build_modifiers(struct format_expand_state *es, const char **s,
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Modifiers are a ; separated list of the forms:
|
* Modifiers are a ; separated list of the forms:
|
||||||
* l,m,C,b,d,n,t,w,q,E,T,S,W,P,<,>
|
* l,m,C,a,b,d,n,t,w,q,E,T,S,W,P,<,>
|
||||||
* =a
|
* =a
|
||||||
* =/a
|
* =/a
|
||||||
* =/a/
|
* =/a/
|
||||||
@ -3539,7 +3540,7 @@ format_build_modifiers(struct format_expand_state *es, const char **s,
|
|||||||
cp++;
|
cp++;
|
||||||
|
|
||||||
/* Check single character modifiers with no arguments. */
|
/* Check single character modifiers with no arguments. */
|
||||||
if (strchr("lbdnwETSWP<>", cp[0]) != NULL &&
|
if (strchr("labdnwETSWP<>", cp[0]) != NULL &&
|
||||||
format_is_end(cp[1])) {
|
format_is_end(cp[1])) {
|
||||||
format_add_modifier(&list, count, cp, 1, NULL, 0);
|
format_add_modifier(&list, count, cp, 1, NULL, 0);
|
||||||
cp++;
|
cp++;
|
||||||
@ -3956,7 +3957,7 @@ format_replace_expression(struct format_modifier *mexp,
|
|||||||
mright = (long long)mright;
|
mright = (long long)mright;
|
||||||
}
|
}
|
||||||
format_log(es, "expression left side is: %.*f", prec, mleft);
|
format_log(es, "expression left side is: %.*f", prec, mleft);
|
||||||
format_log(es, "expression right side is: %.*f", prec, mright);
|
format_log(es, "expression right side is: %.*f", prec, mright);
|
||||||
|
|
||||||
switch (operator) {
|
switch (operator) {
|
||||||
case ADD:
|
case ADD:
|
||||||
@ -4016,10 +4017,10 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
|
|||||||
{
|
{
|
||||||
struct format_tree *ft = es->ft;
|
struct format_tree *ft = es->ft;
|
||||||
struct window_pane *wp = ft->wp;
|
struct window_pane *wp = ft->wp;
|
||||||
const char *errptr, *copy, *cp, *marker = NULL;
|
const char *errstr, *copy, *cp, *marker = NULL;
|
||||||
const char *time_format = NULL;
|
const char *time_format = NULL;
|
||||||
char *copy0, *condition, *found, *new;
|
char *copy0, *condition, *found, *new;
|
||||||
char *value, *left, *right;
|
char *value, *left, *right, c;
|
||||||
size_t valuelen;
|
size_t valuelen;
|
||||||
int modifiers = 0, limit = 0, width = 0;
|
int modifiers = 0, limit = 0, width = 0;
|
||||||
int j;
|
int j;
|
||||||
@ -4063,8 +4064,8 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
|
|||||||
if (fm->argc < 1)
|
if (fm->argc < 1)
|
||||||
break;
|
break;
|
||||||
limit = strtonum(fm->argv[0], INT_MIN, INT_MAX,
|
limit = strtonum(fm->argv[0], INT_MIN, INT_MAX,
|
||||||
&errptr);
|
&errstr);
|
||||||
if (errptr != NULL)
|
if (errstr != NULL)
|
||||||
limit = 0;
|
limit = 0;
|
||||||
if (fm->argc >= 2 && fm->argv[1] != NULL)
|
if (fm->argc >= 2 && fm->argv[1] != NULL)
|
||||||
marker = fm->argv[1];
|
marker = fm->argv[1];
|
||||||
@ -4073,8 +4074,8 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
|
|||||||
if (fm->argc < 1)
|
if (fm->argc < 1)
|
||||||
break;
|
break;
|
||||||
width = strtonum(fm->argv[0], INT_MIN, INT_MAX,
|
width = strtonum(fm->argv[0], INT_MIN, INT_MAX,
|
||||||
&errptr);
|
&errstr);
|
||||||
if (errptr != NULL)
|
if (errstr != NULL)
|
||||||
width = 0;
|
width = 0;
|
||||||
break;
|
break;
|
||||||
case 'w':
|
case 'w':
|
||||||
@ -4088,6 +4089,9 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
|
|||||||
case 'l':
|
case 'l':
|
||||||
modifiers |= FORMAT_LITERAL;
|
modifiers |= FORMAT_LITERAL;
|
||||||
break;
|
break;
|
||||||
|
case 'a':
|
||||||
|
modifiers |= FORMAT_CHARACTER;
|
||||||
|
break;
|
||||||
case 'b':
|
case 'b':
|
||||||
modifiers |= FORMAT_BASENAME;
|
modifiers |= FORMAT_BASENAME;
|
||||||
break;
|
break;
|
||||||
@ -4154,6 +4158,18 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
|
|||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Is this a character? */
|
||||||
|
if (modifiers & FORMAT_CHARACTER) {
|
||||||
|
new = format_expand1(es, copy);
|
||||||
|
c = strtonum(new, 32, 126, &errstr);
|
||||||
|
if (errstr != NULL)
|
||||||
|
value = xstrdup("");
|
||||||
|
else
|
||||||
|
xasprintf(&value, "%c", c);
|
||||||
|
free (new);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
/* Is this a loop, comparison or condition? */
|
/* Is this a loop, comparison or condition? */
|
||||||
if (modifiers & FORMAT_SESSIONS) {
|
if (modifiers & FORMAT_SESSIONS) {
|
||||||
value = format_loop_sessions(es, copy);
|
value = format_loop_sessions(es, copy);
|
||||||
|
68
mode-tree.c
68
mode-tree.c
@ -46,6 +46,7 @@ struct mode_tree_data {
|
|||||||
mode_tree_search_cb searchcb;
|
mode_tree_search_cb searchcb;
|
||||||
mode_tree_menu_cb menucb;
|
mode_tree_menu_cb menucb;
|
||||||
mode_tree_height_cb heightcb;
|
mode_tree_height_cb heightcb;
|
||||||
|
mode_tree_key_cb keycb;
|
||||||
|
|
||||||
struct mode_tree_list children;
|
struct mode_tree_list children;
|
||||||
struct mode_tree_list saved;
|
struct mode_tree_list saved;
|
||||||
@ -74,6 +75,10 @@ struct mode_tree_item {
|
|||||||
void *itemdata;
|
void *itemdata;
|
||||||
u_int line;
|
u_int line;
|
||||||
|
|
||||||
|
key_code key;
|
||||||
|
const char *keystr;
|
||||||
|
size_t keylen;
|
||||||
|
|
||||||
uint64_t tag;
|
uint64_t tag;
|
||||||
const char *name;
|
const char *name;
|
||||||
const char *text;
|
const char *text;
|
||||||
@ -135,6 +140,7 @@ mode_tree_free_item(struct mode_tree_item *mti)
|
|||||||
|
|
||||||
free((void *)mti->name);
|
free((void *)mti->name);
|
||||||
free((void *)mti->text);
|
free((void *)mti->text);
|
||||||
|
free((void *)mti->keystr);
|
||||||
|
|
||||||
free(mti);
|
free(mti);
|
||||||
}
|
}
|
||||||
@ -193,6 +199,26 @@ mode_tree_build_lines(struct mode_tree_data *mtd,
|
|||||||
flat = 0;
|
flat = 0;
|
||||||
if (mti->expanded)
|
if (mti->expanded)
|
||||||
mode_tree_build_lines(mtd, &mti->children, depth + 1);
|
mode_tree_build_lines(mtd, &mti->children, depth + 1);
|
||||||
|
|
||||||
|
if (mtd->keycb != NULL) {
|
||||||
|
mti->key = mtd->keycb(mtd->modedata, mti->itemdata,
|
||||||
|
mti->line);
|
||||||
|
if (mti->key == KEYC_UNKNOWN)
|
||||||
|
mti->key = KEYC_NONE;
|
||||||
|
} else if (mti->line < 10)
|
||||||
|
mti->key = '0' + mti->line;
|
||||||
|
else if (mti->line < 36)
|
||||||
|
mti->key = KEYC_META|('a' + mti->line - 10);
|
||||||
|
else
|
||||||
|
mti->key = KEYC_NONE;
|
||||||
|
if (mti->key != KEYC_NONE) {
|
||||||
|
mti->keystr = xstrdup(key_string_lookup_key(mti->key,
|
||||||
|
0));
|
||||||
|
mti->keylen = strlen(mti->keystr);
|
||||||
|
} else {
|
||||||
|
mti->keystr = NULL;
|
||||||
|
mti->keylen = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
TAILQ_FOREACH(mti, mtl, entry) {
|
TAILQ_FOREACH(mti, mtl, entry) {
|
||||||
for (i = 0; i < mtd->line_size; i++) {
|
for (i = 0; i < mtd->line_size; i++) {
|
||||||
@ -363,7 +389,7 @@ struct mode_tree_data *
|
|||||||
mode_tree_start(struct window_pane *wp, struct args *args,
|
mode_tree_start(struct window_pane *wp, struct args *args,
|
||||||
mode_tree_build_cb buildcb, mode_tree_draw_cb drawcb,
|
mode_tree_build_cb buildcb, mode_tree_draw_cb drawcb,
|
||||||
mode_tree_search_cb searchcb, mode_tree_menu_cb menucb,
|
mode_tree_search_cb searchcb, mode_tree_menu_cb menucb,
|
||||||
mode_tree_height_cb heightcb, void *modedata,
|
mode_tree_height_cb heightcb, mode_tree_key_cb keycb, void *modedata,
|
||||||
const struct menu_item *menu, const char **sort_list, u_int sort_size,
|
const struct menu_item *menu, const char **sort_list, u_int sort_size,
|
||||||
struct screen **s)
|
struct screen **s)
|
||||||
{
|
{
|
||||||
@ -402,6 +428,7 @@ mode_tree_start(struct window_pane *wp, struct args *args,
|
|||||||
mtd->searchcb = searchcb;
|
mtd->searchcb = searchcb;
|
||||||
mtd->menucb = menucb;
|
mtd->menucb = menucb;
|
||||||
mtd->heightcb = heightcb;
|
mtd->heightcb = heightcb;
|
||||||
|
mtd->keycb = keycb;
|
||||||
|
|
||||||
TAILQ_INIT(&mtd->children);
|
TAILQ_INIT(&mtd->children);
|
||||||
|
|
||||||
@ -596,10 +623,10 @@ mode_tree_draw(struct mode_tree_data *mtd)
|
|||||||
struct screen_write_ctx ctx;
|
struct screen_write_ctx ctx;
|
||||||
struct grid_cell gc0, gc;
|
struct grid_cell gc0, gc;
|
||||||
u_int w, h, i, j, sy, box_x, box_y, width;
|
u_int w, h, i, j, sy, box_x, box_y, width;
|
||||||
char *text, *start, key[7];
|
char *text, *start, *key;
|
||||||
const char *tag, *symbol;
|
const char *tag, *symbol;
|
||||||
size_t size, n;
|
size_t size, n;
|
||||||
int keylen;
|
int keylen, pad;
|
||||||
|
|
||||||
if (mtd->line_size == 0)
|
if (mtd->line_size == 0)
|
||||||
return;
|
return;
|
||||||
@ -614,28 +641,30 @@ mode_tree_draw(struct mode_tree_data *mtd)
|
|||||||
screen_write_start(&ctx, s);
|
screen_write_start(&ctx, s);
|
||||||
screen_write_clearscreen(&ctx, 8);
|
screen_write_clearscreen(&ctx, 8);
|
||||||
|
|
||||||
if (mtd->line_size > 10)
|
keylen = 0;
|
||||||
keylen = 6;
|
for (i = 0; i < mtd->line_size; i++) {
|
||||||
else
|
mti = mtd->line_list[i].item;
|
||||||
keylen = 4;
|
if (mti->key == KEYC_NONE)
|
||||||
|
continue;
|
||||||
|
if ((int)mti->keylen + 3 > keylen)
|
||||||
|
keylen = mti->keylen + 3;
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0; i < mtd->line_size; i++) {
|
for (i = 0; i < mtd->line_size; i++) {
|
||||||
if (i < mtd->offset)
|
if (i < mtd->offset)
|
||||||
continue;
|
continue;
|
||||||
if (i > mtd->offset + h - 1)
|
if (i > mtd->offset + h - 1)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
line = &mtd->line_list[i];
|
line = &mtd->line_list[i];
|
||||||
mti = line->item;
|
mti = line->item;
|
||||||
|
|
||||||
screen_write_cursormove(&ctx, 0, i - mtd->offset, 0);
|
screen_write_cursormove(&ctx, 0, i - mtd->offset, 0);
|
||||||
|
|
||||||
if (i < 10)
|
pad = keylen - 2 - mti->keylen;
|
||||||
snprintf(key, sizeof key, "(%c) ", '0' + i);
|
if (mti->key != KEYC_NONE)
|
||||||
else if (i < 36)
|
xasprintf(&key, "(%s)%*s", mti->keystr, pad, "");
|
||||||
snprintf(key, sizeof key, "(M-%c)", 'a' + (i - 10));
|
|
||||||
else
|
else
|
||||||
*key = '\0';
|
key = xstrdup("");
|
||||||
|
|
||||||
if (line->flat)
|
if (line->flat)
|
||||||
symbol = "";
|
symbol = "";
|
||||||
@ -698,6 +727,7 @@ mode_tree_draw(struct mode_tree_data *mtd)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
free(text);
|
free(text);
|
||||||
|
free(key);
|
||||||
|
|
||||||
if (mti->tagged) {
|
if (mti->tagged) {
|
||||||
gc.attr ^= GRID_ATTR_BRIGHT;
|
gc.attr ^= GRID_ATTR_BRIGHT;
|
||||||
@ -951,7 +981,6 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
|
|||||||
struct mode_tree_item *current, *parent, *mti;
|
struct mode_tree_item *current, *parent, *mti;
|
||||||
u_int i, x, y;
|
u_int i, x, y;
|
||||||
int choice;
|
int choice;
|
||||||
key_code tmp;
|
|
||||||
|
|
||||||
if (KEYC_IS_MOUSE(*key) && m != NULL) {
|
if (KEYC_IS_MOUSE(*key) && m != NULL) {
|
||||||
if (cmd_mouse_at(mtd->wp, m, &x, &y, 0) != 0) {
|
if (cmd_mouse_at(mtd->wp, m, &x, &y, 0) != 0) {
|
||||||
@ -993,12 +1022,11 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
|
|||||||
current = line->item;
|
current = line->item;
|
||||||
|
|
||||||
choice = -1;
|
choice = -1;
|
||||||
if (*key >= '0' && *key <= '9')
|
for (i = 0; i < mtd->line_size; i++) {
|
||||||
choice = (*key) - '0';
|
if (*key == mtd->line_list[i].item->key) {
|
||||||
else if (((*key) & KEYC_MASK_MODIFIERS) == KEYC_META) {
|
choice = i;
|
||||||
tmp = (*key) & KEYC_MASK_KEY;
|
break;
|
||||||
if (tmp >= 'a' && tmp <= 'z')
|
}
|
||||||
choice = 10 + (tmp - 'a');
|
|
||||||
}
|
}
|
||||||
if (choice != -1) {
|
if (choice != -1) {
|
||||||
if ((u_int)choice > mtd->line_size - 1) {
|
if ((u_int)choice > mtd->line_size - 1) {
|
||||||
|
34
tmux.1
34
tmux.1
@ -1985,12 +1985,17 @@ The default is to capture only the visible contents of the pane.
|
|||||||
.Op Fl NrZ
|
.Op Fl NrZ
|
||||||
.Op Fl F Ar format
|
.Op Fl F Ar format
|
||||||
.Op Fl f Ar filter
|
.Op Fl f Ar filter
|
||||||
|
.Op Fl K Ar key-format
|
||||||
.Op Fl O Ar sort-order
|
.Op Fl O Ar sort-order
|
||||||
.Op Fl t Ar target-pane
|
.Op Fl t Ar target-pane
|
||||||
.Op Ar template
|
.Op Ar template
|
||||||
.Xc
|
.Xc
|
||||||
Put a pane into client mode, allowing a client to be selected interactively from
|
Put a pane into client mode, allowing a client to be selected interactively from
|
||||||
a list.
|
a list.
|
||||||
|
Each client is shown on one line.
|
||||||
|
A shortcut key is shown on the left in brackets allowing for immediate choice,
|
||||||
|
or the list may be navigated and an item chosen or otherwise manipulated using
|
||||||
|
the keys below.
|
||||||
.Fl Z
|
.Fl Z
|
||||||
zooms the pane.
|
zooms the pane.
|
||||||
The following keys may be used in client mode:
|
The following keys may be used in client mode:
|
||||||
@ -2040,7 +2045,9 @@ specifies an initial filter: the filter is a format - if it evaluates to zero,
|
|||||||
the item in the list is not shown, otherwise it is shown.
|
the item in the list is not shown, otherwise it is shown.
|
||||||
If a filter would lead to an empty list, it is ignored.
|
If a filter would lead to an empty list, it is ignored.
|
||||||
.Fl F
|
.Fl F
|
||||||
specifies the format for each item in the list.
|
specifies the format for each item in the list and
|
||||||
|
.Fl K
|
||||||
|
a format for each shortcut key; both are evaluated once for each line.
|
||||||
.Fl N
|
.Fl N
|
||||||
starts without the preview.
|
starts without the preview.
|
||||||
This command works only if at least one client is attached.
|
This command works only if at least one client is attached.
|
||||||
@ -2049,12 +2056,17 @@ This command works only if at least one client is attached.
|
|||||||
.Op Fl GNrswZ
|
.Op Fl GNrswZ
|
||||||
.Op Fl F Ar format
|
.Op Fl F Ar format
|
||||||
.Op Fl f Ar filter
|
.Op Fl f Ar filter
|
||||||
|
.Op Fl K Ar key-format
|
||||||
.Op Fl O Ar sort-order
|
.Op Fl O Ar sort-order
|
||||||
.Op Fl t Ar target-pane
|
.Op Fl t Ar target-pane
|
||||||
.Op Ar template
|
.Op Ar template
|
||||||
.Xc
|
.Xc
|
||||||
Put a pane into tree mode, where a session, window or pane may be chosen
|
Put a pane into tree mode, where a session, window or pane may be chosen
|
||||||
interactively from a list.
|
interactively from a tree.
|
||||||
|
Each session, window or pane is shown on one line.
|
||||||
|
A shortcut key is shown on the left in brackets allowing for immediate choice,
|
||||||
|
or the tree may be navigated and an item chosen or otherwise manipulated using
|
||||||
|
the keys below.
|
||||||
.Fl s
|
.Fl s
|
||||||
starts with sessions collapsed and
|
starts with sessions collapsed and
|
||||||
.Fl w
|
.Fl w
|
||||||
@ -2113,7 +2125,9 @@ specifies an initial filter: the filter is a format - if it evaluates to zero,
|
|||||||
the item in the list is not shown, otherwise it is shown.
|
the item in the list is not shown, otherwise it is shown.
|
||||||
If a filter would lead to an empty list, it is ignored.
|
If a filter would lead to an empty list, it is ignored.
|
||||||
.Fl F
|
.Fl F
|
||||||
specifies the format for each item in the tree.
|
specifies the format for each item in the tree and
|
||||||
|
.Fl K
|
||||||
|
a format for each shortcut key; both are evaluated once for each line.
|
||||||
.Fl N
|
.Fl N
|
||||||
starts without the preview.
|
starts without the preview.
|
||||||
.Fl G
|
.Fl G
|
||||||
@ -4663,6 +4677,11 @@ For example,
|
|||||||
multiplies 5.5 by 3 for a result with four decimal places and
|
multiplies 5.5 by 3 for a result with four decimal places and
|
||||||
.Ql #{e|%%:7,3}
|
.Ql #{e|%%:7,3}
|
||||||
returns the modulus of 7 and 3.
|
returns the modulus of 7 and 3.
|
||||||
|
.Ql a
|
||||||
|
replaces a numeric argument by its ASCII equivalent, so
|
||||||
|
.Ql #{a:98}
|
||||||
|
results in
|
||||||
|
.Ql b .
|
||||||
.Pp
|
.Pp
|
||||||
A limit may be placed on the length of the resultant string by prefixing it
|
A limit may be placed on the length of the resultant string by prefixing it
|
||||||
by an
|
by an
|
||||||
@ -5681,12 +5700,17 @@ The buffer commands are as follows:
|
|||||||
.Op Fl NZr
|
.Op Fl NZr
|
||||||
.Op Fl F Ar format
|
.Op Fl F Ar format
|
||||||
.Op Fl f Ar filter
|
.Op Fl f Ar filter
|
||||||
|
.Op Fl K Ar key-format
|
||||||
.Op Fl O Ar sort-order
|
.Op Fl O Ar sort-order
|
||||||
.Op Fl t Ar target-pane
|
.Op Fl t Ar target-pane
|
||||||
.Op Ar template
|
.Op Ar template
|
||||||
.Xc
|
.Xc
|
||||||
Put a pane into buffer mode, where a buffer may be chosen interactively from
|
Put a pane into buffer mode, where a buffer may be chosen interactively from
|
||||||
a list.
|
a list.
|
||||||
|
Each buffer is shown on one line.
|
||||||
|
A shortcut key is shown on the left in brackets allowing for immediate choice,
|
||||||
|
or the list may be navigated and an item chosen or otherwise manipulated using
|
||||||
|
the keys below.
|
||||||
.Fl Z
|
.Fl Z
|
||||||
zooms the pane.
|
zooms the pane.
|
||||||
The following keys may be used in buffer mode:
|
The following keys may be used in buffer mode:
|
||||||
@ -5734,7 +5758,9 @@ specifies an initial filter: the filter is a format - if it evaluates to zero,
|
|||||||
the item in the list is not shown, otherwise it is shown.
|
the item in the list is not shown, otherwise it is shown.
|
||||||
If a filter would lead to an empty list, it is ignored.
|
If a filter would lead to an empty list, it is ignored.
|
||||||
.Fl F
|
.Fl F
|
||||||
specifies the format for each item in the list.
|
specifies the format for each item in the list and
|
||||||
|
.Fl K
|
||||||
|
a format for each shortcut key; both are evaluated once for each line.
|
||||||
.Fl N
|
.Fl N
|
||||||
starts without the preview.
|
starts without the preview.
|
||||||
This command works only if at least one client is attached.
|
This command works only if at least one client is attached.
|
||||||
|
3
tmux.h
3
tmux.h
@ -2855,6 +2855,7 @@ typedef void (*mode_tree_draw_cb)(void *, void *, struct screen_write_ctx *,
|
|||||||
typedef int (*mode_tree_search_cb)(void *, void *, const char *);
|
typedef int (*mode_tree_search_cb)(void *, void *, const char *);
|
||||||
typedef void (*mode_tree_menu_cb)(void *, struct client *, key_code);
|
typedef void (*mode_tree_menu_cb)(void *, struct client *, key_code);
|
||||||
typedef u_int (*mode_tree_height_cb)(void *, u_int);
|
typedef u_int (*mode_tree_height_cb)(void *, u_int);
|
||||||
|
typedef key_code (*mode_tree_key_cb)(void *, void *, u_int);
|
||||||
typedef void (*mode_tree_each_cb)(void *, void *, struct client *, key_code);
|
typedef void (*mode_tree_each_cb)(void *, void *, struct client *, key_code);
|
||||||
u_int mode_tree_count_tagged(struct mode_tree_data *);
|
u_int mode_tree_count_tagged(struct mode_tree_data *);
|
||||||
void *mode_tree_get_current(struct mode_tree_data *);
|
void *mode_tree_get_current(struct mode_tree_data *);
|
||||||
@ -2869,7 +2870,7 @@ void mode_tree_up(struct mode_tree_data *, int);
|
|||||||
void mode_tree_down(struct mode_tree_data *, int);
|
void mode_tree_down(struct mode_tree_data *, int);
|
||||||
struct mode_tree_data *mode_tree_start(struct window_pane *, struct args *,
|
struct mode_tree_data *mode_tree_start(struct window_pane *, struct args *,
|
||||||
mode_tree_build_cb, mode_tree_draw_cb, mode_tree_search_cb,
|
mode_tree_build_cb, mode_tree_draw_cb, mode_tree_search_cb,
|
||||||
mode_tree_menu_cb, mode_tree_height_cb, void *,
|
mode_tree_menu_cb, mode_tree_height_cb, mode_tree_key_cb, void *,
|
||||||
const struct menu_item *, const char **, u_int, struct screen **);
|
const struct menu_item *, const char **, u_int, struct screen **);
|
||||||
void mode_tree_zoom(struct mode_tree_data *, struct args *);
|
void mode_tree_zoom(struct mode_tree_data *, struct args *);
|
||||||
void mode_tree_build(struct mode_tree_data *);
|
void mode_tree_build(struct mode_tree_data *);
|
||||||
|
@ -41,6 +41,17 @@ static void window_buffer_key(struct window_mode_entry *,
|
|||||||
#define WINDOW_BUFFER_DEFAULT_FORMAT \
|
#define WINDOW_BUFFER_DEFAULT_FORMAT \
|
||||||
"#{t/p:buffer_created}: #{buffer_sample}"
|
"#{t/p:buffer_created}: #{buffer_sample}"
|
||||||
|
|
||||||
|
#define WINDOW_BUFFER_DEFAULT_KEY_FORMAT \
|
||||||
|
"#{?#{e|<:#{line},10}," \
|
||||||
|
"#{line}" \
|
||||||
|
"," \
|
||||||
|
"#{?#{e|<:#{line},36}," \
|
||||||
|
"M-#{a:#{e|+:97,#{e|-:#{line},10}}}" \
|
||||||
|
"," \
|
||||||
|
"" \
|
||||||
|
"}" \
|
||||||
|
"}"
|
||||||
|
|
||||||
static const struct menu_item window_buffer_menu_items[] = {
|
static const struct menu_item window_buffer_menu_items[] = {
|
||||||
{ "Paste", 'p', NULL },
|
{ "Paste", 'p', NULL },
|
||||||
{ "Paste Tagged", 'P', NULL },
|
{ "Paste Tagged", 'P', NULL },
|
||||||
@ -93,6 +104,7 @@ struct window_buffer_modedata {
|
|||||||
struct mode_tree_data *data;
|
struct mode_tree_data *data;
|
||||||
char *command;
|
char *command;
|
||||||
char *format;
|
char *format;
|
||||||
|
char *key_format;
|
||||||
|
|
||||||
struct window_buffer_itemdata **item_list;
|
struct window_buffer_itemdata **item_list;
|
||||||
u_int item_size;
|
u_int item_size;
|
||||||
@ -232,7 +244,8 @@ window_buffer_draw(__unused void *modedata, void *itemdata,
|
|||||||
while (end != pdata + psize && *end != '\n')
|
while (end != pdata + psize && *end != '\n')
|
||||||
end++;
|
end++;
|
||||||
buf = xreallocarray(buf, 4, end - start + 1);
|
buf = xreallocarray(buf, 4, end - start + 1);
|
||||||
utf8_strvis(buf, start, end - start, VIS_OCTAL|VIS_CSTYLE|VIS_TAB);
|
utf8_strvis(buf, start, end - start,
|
||||||
|
VIS_OCTAL|VIS_CSTYLE|VIS_TAB);
|
||||||
if (*buf != '\0') {
|
if (*buf != '\0') {
|
||||||
screen_write_cursormove(ctx, cx, cy + i, 0);
|
screen_write_cursormove(ctx, cx, cy + i, 0);
|
||||||
screen_write_nputs(ctx, sx, &grid_default_cell, "%s",
|
screen_write_nputs(ctx, sx, &grid_default_cell, "%s",
|
||||||
@ -275,6 +288,41 @@ window_buffer_menu(void *modedata, struct client *c, key_code key)
|
|||||||
window_buffer_key(wme, c, NULL, NULL, key, NULL);
|
window_buffer_key(wme, c, NULL, NULL, key, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static key_code
|
||||||
|
window_buffer_get_key(void *modedata, void *itemdata, u_int line)
|
||||||
|
{
|
||||||
|
struct window_buffer_modedata *data = modedata;
|
||||||
|
struct window_buffer_itemdata *item = itemdata;
|
||||||
|
struct format_tree *ft;
|
||||||
|
struct session *s;
|
||||||
|
struct winlink *wl;
|
||||||
|
struct window_pane *wp;
|
||||||
|
struct paste_buffer *pb;
|
||||||
|
char *expanded;
|
||||||
|
key_code key;
|
||||||
|
|
||||||
|
if (cmd_find_valid_state(&data->fs)) {
|
||||||
|
s = data->fs.s;
|
||||||
|
wl = data->fs.wl;
|
||||||
|
wp = data->fs.wp;
|
||||||
|
}
|
||||||
|
pb = paste_get_name(item->name);
|
||||||
|
if (pb == NULL)
|
||||||
|
return KEYC_NONE;
|
||||||
|
|
||||||
|
ft = format_create(NULL, NULL, FORMAT_NONE, 0);
|
||||||
|
format_defaults(ft, NULL, NULL, 0, NULL);
|
||||||
|
format_defaults(ft, NULL, s, wl, wp);
|
||||||
|
format_defaults_paste_buffer(ft, pb);
|
||||||
|
format_add(ft, "line", "%u", line);
|
||||||
|
|
||||||
|
expanded = format_expand(ft, data->key_format);
|
||||||
|
key = key_string_lookup_string(expanded);
|
||||||
|
free(expanded);
|
||||||
|
format_free(ft);
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
static struct screen *
|
static struct screen *
|
||||||
window_buffer_init(struct window_mode_entry *wme, struct cmd_find_state *fs,
|
window_buffer_init(struct window_mode_entry *wme, struct cmd_find_state *fs,
|
||||||
struct args *args)
|
struct args *args)
|
||||||
@ -291,6 +339,10 @@ window_buffer_init(struct window_mode_entry *wme, struct cmd_find_state *fs,
|
|||||||
data->format = xstrdup(WINDOW_BUFFER_DEFAULT_FORMAT);
|
data->format = xstrdup(WINDOW_BUFFER_DEFAULT_FORMAT);
|
||||||
else
|
else
|
||||||
data->format = xstrdup(args_get(args, 'F'));
|
data->format = xstrdup(args_get(args, 'F'));
|
||||||
|
if (args == NULL || !args_has(args, 'K'))
|
||||||
|
data->key_format = xstrdup(WINDOW_BUFFER_DEFAULT_KEY_FORMAT);
|
||||||
|
else
|
||||||
|
data->key_format = xstrdup(args_get(args, 'K'));
|
||||||
if (args == NULL || args->argc == 0)
|
if (args == NULL || args->argc == 0)
|
||||||
data->command = xstrdup(WINDOW_BUFFER_DEFAULT_COMMAND);
|
data->command = xstrdup(WINDOW_BUFFER_DEFAULT_COMMAND);
|
||||||
else
|
else
|
||||||
@ -298,8 +350,8 @@ window_buffer_init(struct window_mode_entry *wme, struct cmd_find_state *fs,
|
|||||||
|
|
||||||
data->data = mode_tree_start(wp, args, window_buffer_build,
|
data->data = mode_tree_start(wp, args, window_buffer_build,
|
||||||
window_buffer_draw, window_buffer_search, window_buffer_menu, NULL,
|
window_buffer_draw, window_buffer_search, window_buffer_menu, NULL,
|
||||||
data, window_buffer_menu_items, window_buffer_sort_list,
|
window_buffer_get_key, data, window_buffer_menu_items,
|
||||||
nitems(window_buffer_sort_list), &s);
|
window_buffer_sort_list, nitems(window_buffer_sort_list), &s);
|
||||||
mode_tree_zoom(data->data, args);
|
mode_tree_zoom(data->data, args);
|
||||||
|
|
||||||
mode_tree_build(data->data);
|
mode_tree_build(data->data);
|
||||||
@ -324,6 +376,7 @@ window_buffer_free(struct window_mode_entry *wme)
|
|||||||
free(data->item_list);
|
free(data->item_list);
|
||||||
|
|
||||||
free(data->format);
|
free(data->format);
|
||||||
|
free(data->key_format);
|
||||||
free(data->command);
|
free(data->command);
|
||||||
|
|
||||||
free(data);
|
free(data);
|
||||||
|
@ -40,6 +40,17 @@ static void window_client_key(struct window_mode_entry *,
|
|||||||
#define WINDOW_CLIENT_DEFAULT_FORMAT \
|
#define WINDOW_CLIENT_DEFAULT_FORMAT \
|
||||||
"#{t/p:client_activity}: session #{session_name}"
|
"#{t/p:client_activity}: session #{session_name}"
|
||||||
|
|
||||||
|
#define WINDOW_CLIENT_DEFAULT_KEY_FORMAT \
|
||||||
|
"#{?#{e|<:#{line},10}," \
|
||||||
|
"#{line}" \
|
||||||
|
"," \
|
||||||
|
"#{?#{e|<:#{line},36}," \
|
||||||
|
"M-#{a:#{e|+:97,#{e|-:#{line},10}}}" \
|
||||||
|
"," \
|
||||||
|
"" \
|
||||||
|
"}" \
|
||||||
|
"}"
|
||||||
|
|
||||||
static const struct menu_item window_client_menu_items[] = {
|
static const struct menu_item window_client_menu_items[] = {
|
||||||
{ "Detach", 'd', NULL },
|
{ "Detach", 'd', NULL },
|
||||||
{ "Detach Tagged", 'D', NULL },
|
{ "Detach Tagged", 'D', NULL },
|
||||||
@ -87,6 +98,7 @@ struct window_client_modedata {
|
|||||||
|
|
||||||
struct mode_tree_data *data;
|
struct mode_tree_data *data;
|
||||||
char *format;
|
char *format;
|
||||||
|
char *key_format;
|
||||||
char *command;
|
char *command;
|
||||||
|
|
||||||
struct window_client_itemdata **item_list;
|
struct window_client_itemdata **item_list;
|
||||||
@ -252,6 +264,26 @@ window_client_menu(void *modedata, struct client *c, key_code key)
|
|||||||
window_client_key(wme, c, NULL, NULL, key, NULL);
|
window_client_key(wme, c, NULL, NULL, key, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static key_code
|
||||||
|
window_client_get_key(void *modedata, void *itemdata, u_int line)
|
||||||
|
{
|
||||||
|
struct window_client_modedata *data = modedata;
|
||||||
|
struct window_client_itemdata *item = itemdata;
|
||||||
|
struct format_tree *ft;
|
||||||
|
char *expanded;
|
||||||
|
key_code key;
|
||||||
|
|
||||||
|
ft = format_create(NULL, NULL, FORMAT_NONE, 0);
|
||||||
|
format_defaults(ft, item->c, NULL, 0, NULL);
|
||||||
|
format_add(ft, "line", "%u", line);
|
||||||
|
|
||||||
|
expanded = format_expand(ft, data->key_format);
|
||||||
|
key = key_string_lookup_string(expanded);
|
||||||
|
free(expanded);
|
||||||
|
format_free(ft);
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
static struct screen *
|
static struct screen *
|
||||||
window_client_init(struct window_mode_entry *wme,
|
window_client_init(struct window_mode_entry *wme,
|
||||||
__unused struct cmd_find_state *fs, struct args *args)
|
__unused struct cmd_find_state *fs, struct args *args)
|
||||||
@ -267,15 +299,19 @@ window_client_init(struct window_mode_entry *wme,
|
|||||||
data->format = xstrdup(WINDOW_CLIENT_DEFAULT_FORMAT);
|
data->format = xstrdup(WINDOW_CLIENT_DEFAULT_FORMAT);
|
||||||
else
|
else
|
||||||
data->format = xstrdup(args_get(args, 'F'));
|
data->format = xstrdup(args_get(args, 'F'));
|
||||||
|
if (args == NULL || !args_has(args, 'K'))
|
||||||
|
data->key_format = xstrdup(WINDOW_CLIENT_DEFAULT_KEY_FORMAT);
|
||||||
|
else
|
||||||
|
data->key_format = xstrdup(args_get(args, 'K'));
|
||||||
if (args == NULL || args->argc == 0)
|
if (args == NULL || args->argc == 0)
|
||||||
data->command = xstrdup(WINDOW_CLIENT_DEFAULT_COMMAND);
|
data->command = xstrdup(WINDOW_CLIENT_DEFAULT_COMMAND);
|
||||||
else
|
else
|
||||||
data->command = xstrdup(args->argv[0]);
|
data->command = xstrdup(args->argv[0]);
|
||||||
|
|
||||||
data->data = mode_tree_start(wp, args, window_client_build,
|
data->data = mode_tree_start(wp, args, window_client_build,
|
||||||
window_client_draw, NULL, window_client_menu, NULL, data,
|
window_client_draw, NULL, window_client_menu, NULL,
|
||||||
window_client_menu_items, window_client_sort_list,
|
window_client_get_key, data, window_client_menu_items,
|
||||||
nitems(window_client_sort_list), &s);
|
window_client_sort_list, nitems(window_client_sort_list), &s);
|
||||||
mode_tree_zoom(data->data, args);
|
mode_tree_zoom(data->data, args);
|
||||||
|
|
||||||
mode_tree_build(data->data);
|
mode_tree_build(data->data);
|
||||||
@ -300,6 +336,7 @@ window_client_free(struct window_mode_entry *wme)
|
|||||||
free(data->item_list);
|
free(data->item_list);
|
||||||
|
|
||||||
free(data->format);
|
free(data->format);
|
||||||
|
free(data->key_format);
|
||||||
free(data->command);
|
free(data->command);
|
||||||
|
|
||||||
free(data);
|
free(data);
|
||||||
|
@ -890,8 +890,8 @@ window_customize_init(struct window_mode_entry *wme, struct cmd_find_state *fs,
|
|||||||
|
|
||||||
data->data = mode_tree_start(wp, args, window_customize_build,
|
data->data = mode_tree_start(wp, args, window_customize_build,
|
||||||
window_customize_draw, NULL, window_customize_menu,
|
window_customize_draw, NULL, window_customize_menu,
|
||||||
window_customize_height, data, window_customize_menu_items, NULL, 0,
|
window_customize_height, NULL, data, window_customize_menu_items,
|
||||||
&s);
|
NULL, 0, &s);
|
||||||
mode_tree_zoom(data->data, args);
|
mode_tree_zoom(data->data, args);
|
||||||
|
|
||||||
mode_tree_build(data->data);
|
mode_tree_build(data->data);
|
||||||
|
@ -56,6 +56,17 @@ static void window_tree_key(struct window_mode_entry *,
|
|||||||
"}" \
|
"}" \
|
||||||
"}"
|
"}"
|
||||||
|
|
||||||
|
#define WINDOW_TREE_DEFAULT_KEY_FORMAT \
|
||||||
|
"#{?#{e|<:#{line},10}," \
|
||||||
|
"#{line}" \
|
||||||
|
"," \
|
||||||
|
"#{?#{e|<:#{line},36}," \
|
||||||
|
"M-#{a:#{e|+:97,#{e|-:#{line},10}}}" \
|
||||||
|
"," \
|
||||||
|
"" \
|
||||||
|
"}" \
|
||||||
|
"}"
|
||||||
|
|
||||||
static const struct menu_item window_tree_menu_items[] = {
|
static const struct menu_item window_tree_menu_items[] = {
|
||||||
{ "Select", '\r', NULL },
|
{ "Select", '\r', NULL },
|
||||||
{ "Expand", KEYC_RIGHT, NULL },
|
{ "Expand", KEYC_RIGHT, NULL },
|
||||||
@ -117,6 +128,7 @@ struct window_tree_modedata {
|
|||||||
|
|
||||||
struct mode_tree_data *data;
|
struct mode_tree_data *data;
|
||||||
char *format;
|
char *format;
|
||||||
|
char *key_format;
|
||||||
char *command;
|
char *command;
|
||||||
int squash_groups;
|
int squash_groups;
|
||||||
|
|
||||||
@ -856,6 +868,35 @@ window_tree_menu(void *modedata, struct client *c, key_code key)
|
|||||||
window_tree_key(wme, c, NULL, NULL, key, NULL);
|
window_tree_key(wme, c, NULL, NULL, key, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static key_code
|
||||||
|
window_tree_get_key(void *modedata, void *itemdata, u_int line)
|
||||||
|
{
|
||||||
|
struct window_tree_modedata *data = modedata;
|
||||||
|
struct window_tree_itemdata *item = itemdata;
|
||||||
|
struct format_tree *ft;
|
||||||
|
struct session *s;
|
||||||
|
struct winlink *wl;
|
||||||
|
struct window_pane *wp;
|
||||||
|
char *expanded;
|
||||||
|
key_code key;
|
||||||
|
|
||||||
|
ft = format_create(NULL, NULL, FORMAT_NONE, 0);
|
||||||
|
window_tree_pull_item(item, &s, &wl, &wp);
|
||||||
|
if (item->type == WINDOW_TREE_SESSION)
|
||||||
|
format_defaults(ft, NULL, s, NULL, NULL);
|
||||||
|
else if (item->type == WINDOW_TREE_WINDOW)
|
||||||
|
format_defaults(ft, NULL, s, wl, NULL);
|
||||||
|
else
|
||||||
|
format_defaults(ft, NULL, s, wl, wp);
|
||||||
|
format_add(ft, "line", "%u", line);
|
||||||
|
|
||||||
|
expanded = format_expand(ft, data->key_format);
|
||||||
|
key = key_string_lookup_string(expanded);
|
||||||
|
free(expanded);
|
||||||
|
format_free(ft);
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
static struct screen *
|
static struct screen *
|
||||||
window_tree_init(struct window_mode_entry *wme, struct cmd_find_state *fs,
|
window_tree_init(struct window_mode_entry *wme, struct cmd_find_state *fs,
|
||||||
struct args *args)
|
struct args *args)
|
||||||
@ -880,6 +921,10 @@ window_tree_init(struct window_mode_entry *wme, struct cmd_find_state *fs,
|
|||||||
data->format = xstrdup(WINDOW_TREE_DEFAULT_FORMAT);
|
data->format = xstrdup(WINDOW_TREE_DEFAULT_FORMAT);
|
||||||
else
|
else
|
||||||
data->format = xstrdup(args_get(args, 'F'));
|
data->format = xstrdup(args_get(args, 'F'));
|
||||||
|
if (args == NULL || !args_has(args, 'K'))
|
||||||
|
data->key_format = xstrdup(WINDOW_TREE_DEFAULT_KEY_FORMAT);
|
||||||
|
else
|
||||||
|
data->key_format = xstrdup(args_get(args, 'K'));
|
||||||
if (args == NULL || args->argc == 0)
|
if (args == NULL || args->argc == 0)
|
||||||
data->command = xstrdup(WINDOW_TREE_DEFAULT_COMMAND);
|
data->command = xstrdup(WINDOW_TREE_DEFAULT_COMMAND);
|
||||||
else
|
else
|
||||||
@ -887,9 +932,9 @@ window_tree_init(struct window_mode_entry *wme, struct cmd_find_state *fs,
|
|||||||
data->squash_groups = !args_has(args, 'G');
|
data->squash_groups = !args_has(args, 'G');
|
||||||
|
|
||||||
data->data = mode_tree_start(wp, args, window_tree_build,
|
data->data = mode_tree_start(wp, args, window_tree_build,
|
||||||
window_tree_draw, window_tree_search, window_tree_menu, NULL, data,
|
window_tree_draw, window_tree_search, window_tree_menu, NULL,
|
||||||
window_tree_menu_items, window_tree_sort_list,
|
window_tree_get_key, data, window_tree_menu_items,
|
||||||
nitems(window_tree_sort_list), &s);
|
window_tree_sort_list, nitems(window_tree_sort_list), &s);
|
||||||
mode_tree_zoom(data->data, args);
|
mode_tree_zoom(data->data, args);
|
||||||
|
|
||||||
mode_tree_build(data->data);
|
mode_tree_build(data->data);
|
||||||
@ -913,6 +958,7 @@ window_tree_destroy(struct window_tree_modedata *data)
|
|||||||
free(data->item_list);
|
free(data->item_list);
|
||||||
|
|
||||||
free(data->format);
|
free(data->format);
|
||||||
|
free(data->key_format);
|
||||||
free(data->command);
|
free(data->command);
|
||||||
|
|
||||||
free(data);
|
free(data);
|
||||||
|
Loading…
Reference in New Issue
Block a user