mirror of
https://github.com/tmux/tmux.git
synced 2026-10-09 11:44:23 +00:00
Merge remote-tracking branch 'refs/remotes/tmux-openbsd/master'
* refs/remotes/tmux-openbsd/master: Add commands to copy mode to select, pipe and open OSC 133 command output, from Michael Grant. Fix bugs with empty panes and load-buffer -w, GitHub issue 5701 from Alexandre Fiori. Track allocations in yacc to avoid leaking when parsing commands, GitHub issue 5632 from Jeong, Heon. Fix commands being lost when chained with assignment, GitHub issue 5702 from Jeong, Heon
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: cmd-load-buffer.c,v 1.66 2025/10/28 07:32:26 nicm Exp $ */
|
||||
/* $OpenBSD: cmd-load-buffer.c,v 1.67 2026/10/08 07:06:26 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>
|
||||
@@ -78,9 +78,9 @@ cmd_load_buffer_done(__unused struct client *c, const char *path, int error,
|
||||
tc->session != NULL &&
|
||||
(~tc->flags & CLIENT_DEAD))
|
||||
tty_set_selection(&tc->tty, "", copy, bsize);
|
||||
if (tc != NULL)
|
||||
server_client_unref(tc);
|
||||
}
|
||||
if (tc != NULL)
|
||||
server_client_unref(tc);
|
||||
cmdq_continue(item);
|
||||
|
||||
free(cdata->name);
|
||||
|
||||
325
cmd-parse.y
325
cmd-parse.y
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: cmd-parse.y,v 1.61 2026/10/05 13:45:49 nicm Exp $ */
|
||||
/* $OpenBSD: cmd-parse.y,v 1.63 2026/10/08 06:44:16 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -69,6 +69,22 @@ struct cmd_parse_command {
|
||||
};
|
||||
TAILQ_HEAD(cmd_parse_commands, cmd_parse_command);
|
||||
|
||||
enum cmd_parse_value_type {
|
||||
CMD_PARSE_VALUE_STRING,
|
||||
CMD_PARSE_VALUE_ARGUMENT,
|
||||
CMD_PARSE_VALUE_ARGUMENTS,
|
||||
CMD_PARSE_VALUE_COMMAND,
|
||||
CMD_PARSE_VALUE_COMMANDS
|
||||
};
|
||||
|
||||
struct cmd_parse_owned_value {
|
||||
enum cmd_parse_value_type type;
|
||||
void *value;
|
||||
|
||||
TAILQ_ENTRY(cmd_parse_owned_value) entry;
|
||||
};
|
||||
TAILQ_HEAD(cmd_parse_owned_values, cmd_parse_owned_value);
|
||||
|
||||
struct cmd_parse_state {
|
||||
FILE *f;
|
||||
|
||||
@@ -87,10 +103,16 @@ struct cmd_parse_state {
|
||||
|
||||
struct cmd_parse_scope *scope;
|
||||
TAILQ_HEAD(, cmd_parse_scope) stack;
|
||||
|
||||
struct cmd_parse_owned_values values;
|
||||
};
|
||||
static struct cmd_parse_state parse_state;
|
||||
|
||||
static char *cmd_parse_get_error(const char *, u_int, const char *);
|
||||
static void *cmd_parse_own(enum cmd_parse_value_type, void *);
|
||||
static struct cmd_parse_commands *cmd_parse_own_commands(void);
|
||||
static void *cmd_parse_take(void *);
|
||||
static void cmd_parse_drop(void *);
|
||||
static void cmd_parse_free_command(struct cmd_parse_command *);
|
||||
static struct cmd_parse_commands *cmd_parse_new_commands(void);
|
||||
static void cmd_parse_free_commands(struct cmd_parse_commands *);
|
||||
@@ -139,7 +161,7 @@ lines : /* empty */
|
||||
{
|
||||
struct cmd_parse_state *ps = &parse_state;
|
||||
|
||||
ps->commands = $1;
|
||||
ps->commands = cmd_parse_take($1);
|
||||
}
|
||||
|
||||
statements : statement '\n'
|
||||
@@ -150,18 +172,16 @@ statements : statement '\n'
|
||||
{
|
||||
$$ = $1;
|
||||
TAILQ_CONCAT($$, $2, entry);
|
||||
free($2);
|
||||
cmd_parse_drop($2);
|
||||
}
|
||||
|
||||
statement : /* empty */
|
||||
{
|
||||
$$ = xmalloc (sizeof *$$);
|
||||
TAILQ_INIT($$);
|
||||
$$ = cmd_parse_own_commands();
|
||||
}
|
||||
| hidden_assignment
|
||||
{
|
||||
$$ = xmalloc (sizeof *$$);
|
||||
TAILQ_INIT($$);
|
||||
$$ = cmd_parse_own_commands();
|
||||
}
|
||||
| condition
|
||||
{
|
||||
@@ -170,8 +190,8 @@ statement : /* empty */
|
||||
if (ps->scope == NULL || ps->scope->flag)
|
||||
$$ = $1;
|
||||
else {
|
||||
$$ = cmd_parse_new_commands();
|
||||
cmd_parse_free_commands($1);
|
||||
$$ = cmd_parse_own_commands();
|
||||
cmd_parse_drop($1);
|
||||
}
|
||||
}
|
||||
| commands
|
||||
@@ -181,8 +201,8 @@ statement : /* empty */
|
||||
if (ps->scope == NULL || ps->scope->flag)
|
||||
$$ = $1;
|
||||
else {
|
||||
$$ = cmd_parse_new_commands();
|
||||
cmd_parse_free_commands($1);
|
||||
$$ = cmd_parse_own_commands();
|
||||
cmd_parse_drop($1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,6 +224,7 @@ expanded : format
|
||||
struct cmd_find_state *fsp;
|
||||
struct cmd_find_state fs;
|
||||
int flags = FORMAT_NOJOBS;
|
||||
char *expanded;
|
||||
|
||||
if (cmd_find_valid_state(&pi->fs))
|
||||
fsp = &pi->fs;
|
||||
@@ -214,9 +235,10 @@ expanded : format
|
||||
ft = format_create(c, pi->item, FORMAT_NONE, flags);
|
||||
format_defaults(ft, c, fsp->s, fsp->wl, fsp->wp);
|
||||
|
||||
$$ = format_expand(ft, $1);
|
||||
expanded = format_expand(ft, $1);
|
||||
$$ = cmd_parse_own(CMD_PARSE_VALUE_STRING, expanded);
|
||||
format_free(ft);
|
||||
free($1);
|
||||
cmd_parse_drop($1);
|
||||
}
|
||||
|
||||
optional_assignment : /* empty */
|
||||
@@ -241,7 +263,7 @@ assignment : EQUALS
|
||||
}
|
||||
if ((~flags & CMD_PARSE_PARSEONLY) && flag)
|
||||
environ_put(global_environ, $1, 0);
|
||||
free($1);
|
||||
cmd_parse_drop($1);
|
||||
}
|
||||
|
||||
hidden_assignment : HIDDEN EQUALS
|
||||
@@ -263,7 +285,7 @@ hidden_assignment : HIDDEN EQUALS
|
||||
}
|
||||
if ((~flags & CMD_PARSE_PARSEONLY) && flag)
|
||||
environ_put(global_environ, $2, ENVIRON_HIDDEN);
|
||||
free($2);
|
||||
cmd_parse_drop($2);
|
||||
}
|
||||
|
||||
if_open : IF expanded
|
||||
@@ -274,7 +296,7 @@ if_open : IF expanded
|
||||
scope = xmalloc(sizeof *scope);
|
||||
$$ = scope->flag = format_true($2);
|
||||
scope->taken = scope->flag;
|
||||
free($2);
|
||||
cmd_parse_drop($2);
|
||||
|
||||
if (ps->scope != NULL)
|
||||
TAILQ_INSERT_HEAD(&ps->stack, ps->scope, entry);
|
||||
@@ -303,7 +325,7 @@ if_elif : ELIF expanded
|
||||
scope->flag = !ps->scope->taken && format_true($2);
|
||||
scope->taken = ps->scope->taken || scope->flag;
|
||||
$$ = scope->flag;
|
||||
free($2);
|
||||
cmd_parse_drop($2);
|
||||
|
||||
free(ps->scope);
|
||||
ps->scope = scope;
|
||||
@@ -324,48 +346,48 @@ condition : if_open '\n' statements if_close
|
||||
if ($1)
|
||||
$$ = $3;
|
||||
else {
|
||||
$$ = cmd_parse_new_commands();
|
||||
cmd_parse_free_commands($3);
|
||||
$$ = cmd_parse_own_commands();
|
||||
cmd_parse_drop($3);
|
||||
}
|
||||
}
|
||||
| if_open '\n' statements if_else '\n' statements if_close
|
||||
{
|
||||
if ($1) {
|
||||
$$ = $3;
|
||||
cmd_parse_free_commands($6);
|
||||
cmd_parse_drop($6);
|
||||
} else {
|
||||
$$ = $6;
|
||||
cmd_parse_free_commands($3);
|
||||
cmd_parse_drop($3);
|
||||
}
|
||||
}
|
||||
| if_open '\n' statements elif if_close
|
||||
{
|
||||
if ($1) {
|
||||
$$ = $3;
|
||||
cmd_parse_free_commands($4.commands);
|
||||
cmd_parse_drop($4.commands);
|
||||
} else if ($4.flag) {
|
||||
$$ = $4.commands;
|
||||
cmd_parse_free_commands($3);
|
||||
cmd_parse_drop($3);
|
||||
} else {
|
||||
$$ = cmd_parse_new_commands();
|
||||
cmd_parse_free_commands($3);
|
||||
cmd_parse_free_commands($4.commands);
|
||||
$$ = cmd_parse_own_commands();
|
||||
cmd_parse_drop($3);
|
||||
cmd_parse_drop($4.commands);
|
||||
}
|
||||
}
|
||||
| if_open '\n' statements elif if_else '\n' statements if_close
|
||||
{
|
||||
if ($1) {
|
||||
$$ = $3;
|
||||
cmd_parse_free_commands($4.commands);
|
||||
cmd_parse_free_commands($7);
|
||||
cmd_parse_drop($4.commands);
|
||||
cmd_parse_drop($7);
|
||||
} else if ($4.flag) {
|
||||
$$ = $4.commands;
|
||||
cmd_parse_free_commands($3);
|
||||
cmd_parse_free_commands($7);
|
||||
cmd_parse_drop($3);
|
||||
cmd_parse_drop($7);
|
||||
} else {
|
||||
$$ = $7;
|
||||
cmd_parse_free_commands($3);
|
||||
cmd_parse_free_commands($4.commands);
|
||||
cmd_parse_drop($3);
|
||||
cmd_parse_drop($4.commands);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -376,8 +398,8 @@ elif : if_elif '\n' statements
|
||||
$$.commands = $3;
|
||||
} else {
|
||||
$$.flag = 0;
|
||||
$$.commands = cmd_parse_new_commands();
|
||||
cmd_parse_free_commands($3);
|
||||
$$.commands = cmd_parse_own_commands();
|
||||
cmd_parse_drop($3);
|
||||
}
|
||||
}
|
||||
| if_elif '\n' statements elif
|
||||
@@ -385,16 +407,16 @@ elif : if_elif '\n' statements
|
||||
if ($1) {
|
||||
$$.flag = 1;
|
||||
$$.commands = $3;
|
||||
cmd_parse_free_commands($4.commands);
|
||||
cmd_parse_drop($4.commands);
|
||||
} else if ($4.flag) {
|
||||
$$.flag = 1;
|
||||
$$.commands = $4.commands;
|
||||
cmd_parse_free_commands($3);
|
||||
cmd_parse_drop($3);
|
||||
} else {
|
||||
$$.flag = 0;
|
||||
$$.commands = cmd_parse_new_commands();
|
||||
cmd_parse_free_commands($3);
|
||||
cmd_parse_free_commands($4.commands);
|
||||
$$.commands = cmd_parse_own_commands();
|
||||
cmd_parse_drop($3);
|
||||
cmd_parse_drop($4.commands);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -402,12 +424,13 @@ commands : command
|
||||
{
|
||||
struct cmd_parse_state *ps = &parse_state;
|
||||
|
||||
$$ = cmd_parse_new_commands();
|
||||
$$ = cmd_parse_own_commands();
|
||||
if (!TAILQ_EMPTY(&$1->arguments) &&
|
||||
(ps->scope == NULL || ps->scope->flag))
|
||||
(ps->scope == NULL || ps->scope->flag)) {
|
||||
cmd_parse_take($1);
|
||||
TAILQ_INSERT_TAIL($$, $1, entry);
|
||||
else
|
||||
cmd_parse_free_command($1);
|
||||
} else
|
||||
cmd_parse_drop($1);
|
||||
}
|
||||
| commands ';'
|
||||
{
|
||||
@@ -417,21 +440,19 @@ commands : command
|
||||
{
|
||||
$$ = $1;
|
||||
TAILQ_CONCAT($$, $3, entry);
|
||||
free($3);
|
||||
cmd_parse_drop($3);
|
||||
}
|
||||
| commands ';' command
|
||||
{
|
||||
struct cmd_parse_state *ps = &parse_state;
|
||||
|
||||
$$ = $1;
|
||||
if (!TAILQ_EMPTY(&$3->arguments) &&
|
||||
(ps->scope == NULL || ps->scope->flag)) {
|
||||
$$ = $1;
|
||||
cmd_parse_take($3);
|
||||
TAILQ_INSERT_TAIL($$, $3, entry);
|
||||
} else {
|
||||
$$ = cmd_parse_new_commands();
|
||||
cmd_parse_free_commands($1);
|
||||
cmd_parse_free_command($3);
|
||||
}
|
||||
} else
|
||||
cmd_parse_drop($3);
|
||||
}
|
||||
| condition1
|
||||
{
|
||||
@@ -445,6 +466,7 @@ command : assignment
|
||||
$$ = xcalloc(1, sizeof *$$);
|
||||
$$->line = ps->input->line;
|
||||
TAILQ_INIT(&$$->arguments);
|
||||
cmd_parse_own(CMD_PARSE_VALUE_COMMAND, $$);
|
||||
}
|
||||
| optional_assignment TOKEN
|
||||
{
|
||||
@@ -457,8 +479,9 @@ command : assignment
|
||||
|
||||
arg = xcalloc(1, sizeof *arg);
|
||||
arg->type = CMD_PARSE_STRING;
|
||||
arg->string = $2;
|
||||
arg->string = cmd_parse_take($2);
|
||||
TAILQ_INSERT_HEAD(&$$->arguments, arg, entry);
|
||||
cmd_parse_own(CMD_PARSE_VALUE_COMMAND, $$);
|
||||
}
|
||||
| optional_assignment TOKEN arguments
|
||||
{
|
||||
@@ -470,12 +493,13 @@ command : assignment
|
||||
TAILQ_INIT(&$$->arguments);
|
||||
|
||||
TAILQ_CONCAT(&$$->arguments, $3, entry);
|
||||
free($3);
|
||||
cmd_parse_drop($3);
|
||||
|
||||
arg = xcalloc(1, sizeof *arg);
|
||||
arg->type = CMD_PARSE_STRING;
|
||||
arg->string = $2;
|
||||
arg->string = cmd_parse_take($2);
|
||||
TAILQ_INSERT_HEAD(&$$->arguments, arg, entry);
|
||||
cmd_parse_own(CMD_PARSE_VALUE_COMMAND, $$);
|
||||
}
|
||||
|
||||
condition1 : if_open commands if_close
|
||||
@@ -483,48 +507,48 @@ condition1 : if_open commands if_close
|
||||
if ($1)
|
||||
$$ = $2;
|
||||
else {
|
||||
$$ = cmd_parse_new_commands();
|
||||
cmd_parse_free_commands($2);
|
||||
$$ = cmd_parse_own_commands();
|
||||
cmd_parse_drop($2);
|
||||
}
|
||||
}
|
||||
| if_open commands if_else commands if_close
|
||||
{
|
||||
if ($1) {
|
||||
$$ = $2;
|
||||
cmd_parse_free_commands($4);
|
||||
cmd_parse_drop($4);
|
||||
} else {
|
||||
$$ = $4;
|
||||
cmd_parse_free_commands($2);
|
||||
cmd_parse_drop($2);
|
||||
}
|
||||
}
|
||||
| if_open commands elif1 if_close
|
||||
{
|
||||
if ($1) {
|
||||
$$ = $2;
|
||||
cmd_parse_free_commands($3.commands);
|
||||
cmd_parse_drop($3.commands);
|
||||
} else if ($3.flag) {
|
||||
$$ = $3.commands;
|
||||
cmd_parse_free_commands($2);
|
||||
cmd_parse_drop($2);
|
||||
} else {
|
||||
$$ = cmd_parse_new_commands();
|
||||
cmd_parse_free_commands($2);
|
||||
cmd_parse_free_commands($3.commands);
|
||||
$$ = cmd_parse_own_commands();
|
||||
cmd_parse_drop($2);
|
||||
cmd_parse_drop($3.commands);
|
||||
}
|
||||
}
|
||||
| if_open commands elif1 if_else commands if_close
|
||||
{
|
||||
if ($1) {
|
||||
$$ = $2;
|
||||
cmd_parse_free_commands($3.commands);
|
||||
cmd_parse_free_commands($5);
|
||||
cmd_parse_drop($3.commands);
|
||||
cmd_parse_drop($5);
|
||||
} else if ($3.flag) {
|
||||
$$ = $3.commands;
|
||||
cmd_parse_free_commands($2);
|
||||
cmd_parse_free_commands($5);
|
||||
cmd_parse_drop($2);
|
||||
cmd_parse_drop($5);
|
||||
} else {
|
||||
$$ = $5;
|
||||
cmd_parse_free_commands($2);
|
||||
cmd_parse_free_commands($3.commands);
|
||||
cmd_parse_drop($2);
|
||||
cmd_parse_drop($3.commands);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -535,8 +559,8 @@ elif1 : if_elif commands
|
||||
$$.commands = $2;
|
||||
} else {
|
||||
$$.flag = 0;
|
||||
$$.commands = cmd_parse_new_commands();
|
||||
cmd_parse_free_commands($2);
|
||||
$$.commands = cmd_parse_own_commands();
|
||||
cmd_parse_drop($2);
|
||||
}
|
||||
}
|
||||
| if_elif commands elif1
|
||||
@@ -544,16 +568,16 @@ elif1 : if_elif commands
|
||||
if ($1) {
|
||||
$$.flag = 1;
|
||||
$$.commands = $2;
|
||||
cmd_parse_free_commands($3.commands);
|
||||
cmd_parse_drop($3.commands);
|
||||
} else if ($3.flag) {
|
||||
$$.flag = 1;
|
||||
$$.commands = $3.commands;
|
||||
cmd_parse_free_commands($2);
|
||||
cmd_parse_drop($2);
|
||||
} else {
|
||||
$$.flag = 0;
|
||||
$$.commands = cmd_parse_new_commands();
|
||||
cmd_parse_free_commands($2);
|
||||
cmd_parse_free_commands($3.commands);
|
||||
$$.commands = cmd_parse_own_commands();
|
||||
cmd_parse_drop($2);
|
||||
cmd_parse_drop($3.commands);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -562,10 +586,13 @@ arguments : argument
|
||||
$$ = xcalloc(1, sizeof *$$);
|
||||
TAILQ_INIT($$);
|
||||
|
||||
cmd_parse_take($1);
|
||||
TAILQ_INSERT_HEAD($$, $1, entry);
|
||||
cmd_parse_own(CMD_PARSE_VALUE_ARGUMENTS, $$);
|
||||
}
|
||||
| argument arguments
|
||||
{
|
||||
cmd_parse_take($1);
|
||||
TAILQ_INSERT_HEAD($2, $1, entry);
|
||||
$$ = $2;
|
||||
}
|
||||
@@ -574,19 +601,22 @@ argument : TOKEN
|
||||
{
|
||||
$$ = xcalloc(1, sizeof *$$);
|
||||
$$->type = CMD_PARSE_STRING;
|
||||
$$->string = $1;
|
||||
$$->string = cmd_parse_take($1);
|
||||
cmd_parse_own(CMD_PARSE_VALUE_ARGUMENT, $$);
|
||||
}
|
||||
| EQUALS
|
||||
{
|
||||
$$ = xcalloc(1, sizeof *$$);
|
||||
$$->type = CMD_PARSE_STRING;
|
||||
$$->string = $1;
|
||||
$$->string = cmd_parse_take($1);
|
||||
cmd_parse_own(CMD_PARSE_VALUE_ARGUMENT, $$);
|
||||
}
|
||||
| '{' argument_statements
|
||||
{
|
||||
$$ = xcalloc(1, sizeof *$$);
|
||||
$$->type = CMD_PARSE_COMMANDS;
|
||||
$$->commands = $2;
|
||||
$$->commands = cmd_parse_take($2);
|
||||
cmd_parse_own(CMD_PARSE_VALUE_ARGUMENT, $$);
|
||||
}
|
||||
|
||||
argument_statements : statement '}'
|
||||
@@ -597,7 +627,7 @@ argument_statements : statement '}'
|
||||
{
|
||||
$$ = $1;
|
||||
TAILQ_CONCAT($$, $2, entry);
|
||||
free($2);
|
||||
cmd_parse_drop($2);
|
||||
}
|
||||
|
||||
%%
|
||||
@@ -686,22 +716,116 @@ cmd_parse_free_commands(struct cmd_parse_commands *cmds)
|
||||
free(cmds);
|
||||
}
|
||||
|
||||
static void *
|
||||
cmd_parse_own(enum cmd_parse_value_type type, void *value)
|
||||
{
|
||||
struct cmd_parse_state *ps = &parse_state;
|
||||
struct cmd_parse_owned_value *v;
|
||||
|
||||
v = xmalloc(sizeof *v);
|
||||
v->type = type;
|
||||
v->value = value;
|
||||
TAILQ_INSERT_TAIL(&ps->values, v, entry);
|
||||
return (value);
|
||||
}
|
||||
|
||||
static struct cmd_parse_commands *
|
||||
cmd_parse_own_commands(void)
|
||||
{
|
||||
struct cmd_parse_commands *cmds;
|
||||
|
||||
cmds = cmd_parse_new_commands();
|
||||
return (cmd_parse_own(CMD_PARSE_VALUE_COMMANDS, cmds));
|
||||
}
|
||||
|
||||
static struct cmd_parse_owned_value *
|
||||
cmd_parse_find_value(void *value)
|
||||
{
|
||||
struct cmd_parse_state *ps = &parse_state;
|
||||
struct cmd_parse_owned_value *v;
|
||||
|
||||
TAILQ_FOREACH_REVERSE(v, &ps->values, cmd_parse_owned_values, entry) {
|
||||
if (v->value == value)
|
||||
return (v);
|
||||
}
|
||||
fatalx("parser value not owned");
|
||||
}
|
||||
|
||||
static void *
|
||||
cmd_parse_take(void *value)
|
||||
{
|
||||
struct cmd_parse_state *ps = &parse_state;
|
||||
struct cmd_parse_owned_value *v;
|
||||
|
||||
v = cmd_parse_find_value(value);
|
||||
TAILQ_REMOVE(&ps->values, v, entry);
|
||||
free(v);
|
||||
return (value);
|
||||
}
|
||||
|
||||
static void
|
||||
cmd_parse_free_value(enum cmd_parse_value_type type, void *value)
|
||||
{
|
||||
switch (type) {
|
||||
case CMD_PARSE_VALUE_STRING:
|
||||
free(value);
|
||||
break;
|
||||
case CMD_PARSE_VALUE_ARGUMENT:
|
||||
cmd_parse_free_argument(value);
|
||||
break;
|
||||
case CMD_PARSE_VALUE_ARGUMENTS:
|
||||
cmd_parse_free_arguments(value);
|
||||
free(value);
|
||||
break;
|
||||
case CMD_PARSE_VALUE_COMMAND:
|
||||
cmd_parse_free_command(value);
|
||||
break;
|
||||
case CMD_PARSE_VALUE_COMMANDS:
|
||||
cmd_parse_free_commands(value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
cmd_parse_drop(void *value)
|
||||
{
|
||||
struct cmd_parse_state *ps = &parse_state;
|
||||
struct cmd_parse_owned_value *v;
|
||||
|
||||
v = cmd_parse_find_value(value);
|
||||
TAILQ_REMOVE(&ps->values, v, entry);
|
||||
cmd_parse_free_value(v->type, v->value);
|
||||
free(v);
|
||||
}
|
||||
|
||||
static struct cmd_parse_commands *
|
||||
cmd_parse_run_parser(char **cause)
|
||||
{
|
||||
struct cmd_parse_state *ps = &parse_state;
|
||||
struct cmd_parse_scope *scope, *scope1;
|
||||
int retval;
|
||||
struct cmd_parse_state *ps = &parse_state;
|
||||
struct cmd_parse_scope *scope, *scope1;
|
||||
struct cmd_parse_owned_value *v, *v1;
|
||||
int retval;
|
||||
|
||||
ps->commands = NULL;
|
||||
TAILQ_INIT(&ps->stack);
|
||||
TAILQ_INIT(&ps->values);
|
||||
|
||||
retval = yyparse();
|
||||
TAILQ_FOREACH_SAFE(scope, &ps->stack, entry, scope1) {
|
||||
TAILQ_REMOVE(&ps->stack, scope, entry);
|
||||
free(scope);
|
||||
}
|
||||
free(ps->scope);
|
||||
ps->scope = NULL;
|
||||
|
||||
TAILQ_FOREACH_SAFE(v, &ps->values, entry, v1) {
|
||||
TAILQ_REMOVE(&ps->values, v, entry);
|
||||
cmd_parse_free_value(v->type, v->value);
|
||||
free(v);
|
||||
}
|
||||
if (retval != 0) {
|
||||
if (ps->commands != NULL)
|
||||
cmd_parse_free_commands(ps->commands);
|
||||
*cause = ps->error;
|
||||
return (NULL);
|
||||
}
|
||||
@@ -1332,9 +1456,10 @@ yylex(void)
|
||||
*/
|
||||
next = yylex_getc();
|
||||
if (condition && next == '{') {
|
||||
yylval.token = yylex_format();
|
||||
if (yylval.token == NULL)
|
||||
token = yylval.token = yylex_format();
|
||||
if (token == NULL)
|
||||
return (ERROR);
|
||||
cmd_parse_own(CMD_PARSE_VALUE_STRING, token);
|
||||
return (FORMAT);
|
||||
}
|
||||
while (next != '\n' && next != EOF)
|
||||
@@ -1351,35 +1476,37 @@ yylex(void)
|
||||
* % is a condition unless it is all % or all numbers,
|
||||
* then it is a token.
|
||||
*/
|
||||
yylval.token = yylex_get_word('%');
|
||||
for (cp = yylval.token; *cp != '\0'; cp++) {
|
||||
token = yylval.token = yylex_get_word('%');
|
||||
for (cp = token; *cp != '\0'; cp++) {
|
||||
if (*cp != '%' && !isdigit((u_char)*cp))
|
||||
break;
|
||||
}
|
||||
if (*cp == '\0')
|
||||
if (*cp == '\0') {
|
||||
cmd_parse_own(CMD_PARSE_VALUE_STRING, token);
|
||||
return (TOKEN);
|
||||
}
|
||||
ps->condition = 1;
|
||||
if (strcmp(yylval.token, "%hidden") == 0) {
|
||||
free(yylval.token);
|
||||
if (strcmp(token, "%hidden") == 0) {
|
||||
free(token);
|
||||
return (HIDDEN);
|
||||
}
|
||||
if (strcmp(yylval.token, "%if") == 0) {
|
||||
free(yylval.token);
|
||||
if (strcmp(token, "%if") == 0) {
|
||||
free(token);
|
||||
return (IF);
|
||||
}
|
||||
if (strcmp(yylval.token, "%else") == 0) {
|
||||
free(yylval.token);
|
||||
if (strcmp(token, "%else") == 0) {
|
||||
free(token);
|
||||
return (ELSE);
|
||||
}
|
||||
if (strcmp(yylval.token, "%elif") == 0) {
|
||||
free(yylval.token);
|
||||
if (strcmp(token, "%elif") == 0) {
|
||||
free(token);
|
||||
return (ELIF);
|
||||
}
|
||||
if (strcmp(yylval.token, "%endif") == 0) {
|
||||
free(yylval.token);
|
||||
if (strcmp(token, "%endif") == 0) {
|
||||
free(token);
|
||||
return (ENDIF);
|
||||
}
|
||||
free(yylval.token);
|
||||
free(token);
|
||||
return (ERROR);
|
||||
}
|
||||
|
||||
@@ -1389,7 +1516,7 @@ yylex(void)
|
||||
token = yylex_token(ch);
|
||||
if (token == NULL)
|
||||
return (ERROR);
|
||||
yylval.token = token;
|
||||
yylval.token = cmd_parse_own(CMD_PARSE_VALUE_STRING, token);
|
||||
|
||||
if (strchr(token, '=') != NULL && yylex_is_var(*token, 1)) {
|
||||
for (cp = token + 1; *cp != '='; cp++) {
|
||||
|
||||
207
grid-reader.c
207
grid-reader.c
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: grid-reader.c,v 1.11 2026/07/29 17:42:56 nicm Exp $ */
|
||||
/* $OpenBSD: grid-reader.c,v 1.12 2026/10/08 07:50:05 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2020 Anindya Mukherjee <anindya49@hotmail.com>
|
||||
@@ -436,3 +436,208 @@ grid_reader_cursor_back_to_indentation(struct grid_reader *gr)
|
||||
gr->cx = oldx;
|
||||
gr->cy = oldy;
|
||||
}
|
||||
|
||||
/* Get the end of the last used line. */
|
||||
static void
|
||||
grid_reader_output_end(struct grid *gd, u_int *x, u_int *y)
|
||||
{
|
||||
u_int last = gd->hsize + gd->sy - 1;
|
||||
|
||||
while (last > 0 && grid_get_line(gd, last)->cellused == 0)
|
||||
last--;
|
||||
*x = grid_get_line(gd, last)->cellused;
|
||||
*y = last;
|
||||
}
|
||||
|
||||
/* Find the most recent complete output at or before the cursor. */
|
||||
static int
|
||||
grid_reader_previous_output_range(struct grid_reader *gr, u_int *sx,
|
||||
u_int *sy, u_int *ex, u_int *ey)
|
||||
{
|
||||
struct grid *gd = gr->gd;
|
||||
struct grid_line *gl;
|
||||
struct osc133_data *od;
|
||||
u_int cursor_x = gr->cx, cursor_y = gr->cy;
|
||||
u_int start_x, start_y, y, total;
|
||||
int found = 0, have_prompt = 0;
|
||||
int pending = 0;
|
||||
int has_start, has_end, end_first;
|
||||
int cleared;
|
||||
|
||||
total = gd->hsize + gd->sy;
|
||||
|
||||
/*
|
||||
* Scan from the top because markers arrive in their natural order, C
|
||||
* then D. A backward scan would meet D first and need extra state to
|
||||
* find its C. The last complete output found is the one wanted.
|
||||
*/
|
||||
for (y = 0; y < total && y <= cursor_y; y++) {
|
||||
gl = grid_get_line(gd, y);
|
||||
od = &gl->osc133_data;
|
||||
|
||||
/* On the cursor's line, ignore markers after the cursor. */
|
||||
has_start = has_end = end_first = 0;
|
||||
if (gl->flags & GRID_LINE_START_OUTPUT) {
|
||||
if (y != cursor_y || od->out_start_col <= cursor_x)
|
||||
has_start = 1;
|
||||
}
|
||||
if (gl->flags & GRID_LINE_END_OUTPUT) {
|
||||
if (y != cursor_y || od->out_end_col <= cursor_x)
|
||||
has_end = 1;
|
||||
}
|
||||
|
||||
/* An end before the start ends the previous output. */
|
||||
if (has_start && has_end && od->out_end_col < od->out_start_col)
|
||||
end_first = 1;
|
||||
|
||||
/* A C marker starts an output that is pending until its D. */
|
||||
if (has_start && !end_first) {
|
||||
start_x = od->out_start_col;
|
||||
start_y = y;
|
||||
pending = 1;
|
||||
}
|
||||
|
||||
/* The output may have cleared its C marker from the screen. */
|
||||
if (!pending && !have_prompt &&
|
||||
(gl->flags & GRID_LINE_END_OUTPUT)) {
|
||||
cleared = 1;
|
||||
if ((gl->flags & GRID_LINE_START_PROMPT) &&
|
||||
od->out_end_col > od->prompt_col)
|
||||
cleared = 0;
|
||||
if (cleared) {
|
||||
start_x = start_y = 0;
|
||||
pending = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* A D marker completes the pending output. */
|
||||
if (pending && has_end) {
|
||||
*sx = start_x;
|
||||
*sy = start_y;
|
||||
*ex = od->out_end_col;
|
||||
*ey = y;
|
||||
found = 1;
|
||||
pending = 0;
|
||||
}
|
||||
if (has_start && end_first) {
|
||||
start_x = od->out_start_col;
|
||||
start_y = y;
|
||||
pending = 1;
|
||||
}
|
||||
|
||||
/* A prompt drops an unfinished output. */
|
||||
if (gl->flags & GRID_LINE_START_PROMPT) {
|
||||
if (!has_start || od->out_start_col < od->prompt_col)
|
||||
pending = 0;
|
||||
have_prompt = 1;
|
||||
}
|
||||
}
|
||||
return (found);
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the output range for the command at the cursor, from its C marker to
|
||||
* its D marker. If the prompt has no output yet, use the previous output.
|
||||
* Returns 0 if there is no usable range.
|
||||
*/
|
||||
int
|
||||
grid_reader_output_range(struct grid_reader *gr, u_int *sx, u_int *sy,
|
||||
u_int *ex, u_int *ey)
|
||||
{
|
||||
struct grid *gd = gr->gd;
|
||||
struct grid_line *gl;
|
||||
struct osc133_data *od;
|
||||
u_int cursor_x = gr->cx, cursor_y = gr->cy;
|
||||
u_int prompt_x = 0, prompt_y = UINT_MAX;
|
||||
u_int y, total;
|
||||
int found_start = 0, found_end = 0;
|
||||
int next_prompt, in_range, end_ok;
|
||||
int found;
|
||||
|
||||
/* Find the last prompt at or before the cursor. */
|
||||
total = gd->hsize + gd->sy;
|
||||
for (y = 0; y < total; y++) {
|
||||
gl = grid_get_line(gd, y);
|
||||
od = &gl->osc133_data;
|
||||
if (~gl->flags & GRID_LINE_START_PROMPT)
|
||||
continue;
|
||||
if (y > cursor_y)
|
||||
break;
|
||||
if (y == cursor_y && od->prompt_col > cursor_x)
|
||||
break;
|
||||
prompt_y = y;
|
||||
prompt_x = od->prompt_col;
|
||||
}
|
||||
|
||||
/* With no prompt, its A marker may have left history, so start at 0. */
|
||||
if (prompt_y == UINT_MAX)
|
||||
y = 0;
|
||||
else
|
||||
y = prompt_y;
|
||||
|
||||
/* Walk down from the prompt to its C and D, up to the next prompt. */
|
||||
for (; y < total; y++) {
|
||||
gl = grid_get_line(gd, y);
|
||||
od = &gl->osc133_data;
|
||||
next_prompt = 0;
|
||||
if (y != prompt_y && (gl->flags & GRID_LINE_START_PROMPT))
|
||||
next_prompt = 1;
|
||||
|
||||
/* Output before the next prompt on its line is ours. */
|
||||
if (y == prompt_y)
|
||||
in_range = (od->out_start_col >= prompt_x);
|
||||
else if (next_prompt)
|
||||
in_range = (od->out_start_col < od->prompt_col);
|
||||
else
|
||||
in_range = 1;
|
||||
if (gl->flags & GRID_LINE_START_OUTPUT && in_range) {
|
||||
*sx = od->out_start_col;
|
||||
*sy = y;
|
||||
found_start = 1;
|
||||
}
|
||||
|
||||
/* Both A and C may have left history while D remains. */
|
||||
if (!found_start && prompt_y == UINT_MAX &&
|
||||
(gl->flags & GRID_LINE_END_OUTPUT)) {
|
||||
if (!next_prompt || od->out_end_col <= od->prompt_col) {
|
||||
*sx = *sy = 0;
|
||||
found_start = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* An output may end on the same line or the next prompt's. */
|
||||
if (found_start && (gl->flags & GRID_LINE_END_OUTPUT)) {
|
||||
end_ok = 1;
|
||||
if (y == prompt_y && od->out_end_col < prompt_x)
|
||||
end_ok = 0;
|
||||
if (y == *sy && od->out_end_col < *sx)
|
||||
end_ok = 0;
|
||||
if (end_ok) {
|
||||
*ex = od->out_end_col;
|
||||
*ey = y;
|
||||
found_end = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (next_prompt)
|
||||
break;
|
||||
}
|
||||
|
||||
/* The cursor is on a prompt with no output yet. */
|
||||
if (!found_start) {
|
||||
found = grid_reader_previous_output_range(gr, sx, sy, ex, ey);
|
||||
return (found);
|
||||
}
|
||||
|
||||
/*
|
||||
* Without a D marker the command is still running, so its output runs
|
||||
* to the last used line. If the next prompt came first, it has no
|
||||
* usable end.
|
||||
*/
|
||||
if (!found_end) {
|
||||
if (y != total)
|
||||
return (0);
|
||||
grid_reader_output_end(gd, ex, ey);
|
||||
}
|
||||
return (1);
|
||||
}
|
||||
|
||||
81
grid.c
81
grid.c
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: grid.c,v 1.158 2026/09/01 12:49:49 nicm Exp $ */
|
||||
/* $OpenBSD: grid.c,v 1.159 2026/10/08 07:50:05 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -1347,12 +1347,60 @@ grid_reflow_move(struct grid *gd, struct grid_line *from)
|
||||
return (to);
|
||||
}
|
||||
|
||||
/* Move the OSC 133 markers from one line to another. */
|
||||
static void
|
||||
grid_reflow_move_osc133(struct grid_line *to, struct grid_line *from,
|
||||
u_int to_col, u_int count, int last)
|
||||
{
|
||||
struct osc133_data *src = &from->osc133_data;
|
||||
struct osc133_data *dst = &to->osc133_data;
|
||||
u_short *src_col[] = {
|
||||
&src->prompt_col,
|
||||
&src->cmd_col,
|
||||
&src->out_start_col,
|
||||
&src->out_end_col
|
||||
};
|
||||
u_short *dst_col[] = {
|
||||
&dst->prompt_col,
|
||||
&dst->cmd_col,
|
||||
&dst->out_start_col,
|
||||
&dst->out_end_col
|
||||
};
|
||||
u_int flags[] = {
|
||||
GRID_LINE_START_PROMPT|GRID_LINE_SECOND_PROMPT,
|
||||
GRID_LINE_START_COMMAND,
|
||||
GRID_LINE_START_OUTPUT,
|
||||
GRID_LINE_END_OUTPUT
|
||||
};
|
||||
u_int i, flag;
|
||||
|
||||
if ((from->flags & GRID_LINE_OSC133_FLAGS) == 0)
|
||||
return;
|
||||
for (i = 0; i < nitems(flags); i++) {
|
||||
flag = from->flags & flags[i];
|
||||
if (flag == 0)
|
||||
continue;
|
||||
if (!last && *src_col[i] >= count) {
|
||||
*src_col[i] -= count;
|
||||
continue;
|
||||
}
|
||||
*dst_col[i] = to_col + *src_col[i];
|
||||
to->flags |= flag;
|
||||
from->flags &= ~flag;
|
||||
*src_col[i] = 0;
|
||||
if (flag == GRID_LINE_END_OUTPUT) {
|
||||
dst->exit_status = src->exit_status;
|
||||
src->exit_status = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Join line below onto this one. */
|
||||
static void
|
||||
grid_reflow_join(struct grid *target, struct grid *gd, u_int sx, u_int yy,
|
||||
u_int width, int already)
|
||||
{
|
||||
struct grid_line *gl, *from = NULL;
|
||||
struct grid_line *gl, *from = NULL, *next;
|
||||
struct grid_cell gc;
|
||||
u_int lines, left, i, to, line, want = 0;
|
||||
u_int at;
|
||||
@@ -1383,13 +1431,20 @@ grid_reflow_join(struct grid *target, struct grid *gd, u_int sx, u_int yy,
|
||||
break;
|
||||
line = yy + 1 + lines;
|
||||
|
||||
/* If the next line is empty, skip it. */
|
||||
/* Consume empty lines, including any markers. */
|
||||
if (~gd->linedata[line].flags & GRID_LINE_WRAPPED)
|
||||
wrapped = 0;
|
||||
if (gd->linedata[line].cellused == 0) {
|
||||
next = &gd->linedata[line];
|
||||
if (!wrapped &&
|
||||
(next->flags & GRID_LINE_OSC133_FLAGS) == 0)
|
||||
break;
|
||||
from = next;
|
||||
want = 0;
|
||||
grid_reflow_move_osc133(gl, from, at, 0, 1);
|
||||
lines++;
|
||||
if (!wrapped)
|
||||
break;
|
||||
lines++;
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1416,6 +1471,8 @@ grid_reflow_join(struct grid *target, struct grid *gd, u_int sx, u_int yy,
|
||||
grid_set_cell(target, at, to, &gc);
|
||||
at++;
|
||||
}
|
||||
grid_reflow_move_osc133(gl, from, at - want, want,
|
||||
want == from->cellused);
|
||||
lines++;
|
||||
|
||||
/*
|
||||
@@ -1486,6 +1543,14 @@ grid_reflow_split(struct grid *target, struct grid *gd, u_int sx, u_int yy,
|
||||
line = target->sy + 1;
|
||||
first = grid_reflow_add(target, lines);
|
||||
|
||||
/* The first line keeps only the markers within its first at cells. */
|
||||
memcpy(first, gl, sizeof *first);
|
||||
first->cellsize = first->cellused = at;
|
||||
first->flags &= ~GRID_LINE_OSC133_FLAGS;
|
||||
first->flags |= GRID_LINE_WRAPPED;
|
||||
memset(&first->osc133_data, 0, sizeof first->osc133_data);
|
||||
grid_reflow_move_osc133(first, gl, 0, at, 0);
|
||||
|
||||
/* Copy sections from the original line. */
|
||||
width = 0;
|
||||
xx = 0;
|
||||
@@ -1493,6 +1558,8 @@ grid_reflow_split(struct grid *target, struct grid *gd, u_int sx, u_int yy,
|
||||
grid_get_cell1(gl, i, &gc);
|
||||
if (width + gc.data.width > sx) {
|
||||
target->linedata[line].flags |= GRID_LINE_WRAPPED;
|
||||
grid_reflow_move_osc133(&target->linedata[line], gl, 0,
|
||||
xx, 0);
|
||||
|
||||
line++;
|
||||
width = 0;
|
||||
@@ -1504,11 +1571,7 @@ grid_reflow_split(struct grid *target, struct grid *gd, u_int sx, u_int yy,
|
||||
}
|
||||
if (flags & GRID_LINE_WRAPPED)
|
||||
target->linedata[line].flags |= GRID_LINE_WRAPPED;
|
||||
|
||||
/* Move the remainder of the original line. */
|
||||
gl->cellsize = gl->cellused = at;
|
||||
gl->flags |= GRID_LINE_WRAPPED;
|
||||
memcpy(first, gl, sizeof *first);
|
||||
grid_reflow_move_osc133(&target->linedata[line], gl, 0, xx, 1);
|
||||
grid_reflow_dead(gl);
|
||||
|
||||
/* Adjust the scroll position. */
|
||||
|
||||
3
input.c
3
input.c
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: input.c,v 1.272 2026/09/21 10:22:31 nicm Exp $ */
|
||||
/* $OpenBSD: input.c,v 1.273 2026/10/08 07:50:05 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -3278,7 +3278,6 @@ input_osc_133(struct input_ctx *ictx, const char *p)
|
||||
case 'A':
|
||||
case 'N':
|
||||
if (gl != NULL) {
|
||||
memset(&gl->osc133_data, 0, sizeof gl->osc133_data);
|
||||
gl->osc133_data.prompt_col = s->cx;
|
||||
gl->flags |= GRID_LINE_START_PROMPT;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: key-bindings.c,v 1.193 2026/09/21 20:45:48 nicm Exp $ */
|
||||
/* $OpenBSD: key-bindings.c,v 1.194 2026/10/08 07:50:05 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -53,6 +53,7 @@
|
||||
" '#{?#{m/r:(copy|view)-mode,#{pane_mode}},Go To Bottom,}' '>' {send -X history-bottom}" \
|
||||
" ''" \
|
||||
" '#{?#{==:#{pane_mode},copy-mode},#{?copy_line_numbers,Hide Line Numbers,Show Line Numbers},}' 'L' {send -X line-numbers-toggle}" \
|
||||
" '#{?#{==:#{pane_mode},copy-mode},Edit,}' 'e' {if -F '#{selection_present}' {send -X open-selection} {send -X open-output}}" \
|
||||
" '#{?#{==:#{pane_mode},copy-mode},#{?refresh_active,Refresh Off,Refresh On},}' 'r' {send -X refresh-toggle}" \
|
||||
" ''" \
|
||||
" '#{?#{&&:#{buffer_size},#{!:#{pane_in_mode}}},Paste #[underscore]#{=/9/...:buffer_sample},}' 'p' {paste-buffer}" \
|
||||
@@ -575,6 +576,7 @@ key_bindings_init(void)
|
||||
"bind -Tcopy-mode C-l { send -X recentre-top-bottom }",
|
||||
"bind -Tcopy-mode M-l { send -X cursor-centre-horizontal }",
|
||||
"bind -Tcopy-mode C-n { send -X cursor-down }",
|
||||
"bind -Tcopy-mode C-o { send -X select-output }",
|
||||
"bind -Tcopy-mode C-p { send -X cursor-up }",
|
||||
"bind -Tcopy-mode C-r { command-prompt -P -T search -ip'(search up)' -I'#{pane_search_string}' { send -X search-backward-incremental -- '%%' } }",
|
||||
"bind -Tcopy-mode C-s { command-prompt -P -T search -ip'(search down)' -I'#{pane_search_string}' { send -X search-forward-incremental -- '%%' } }",
|
||||
@@ -592,6 +594,7 @@ key_bindings_init(void)
|
||||
"bind -Tcopy-mode R { send -X rectangle-toggle }",
|
||||
"bind -Tcopy-mode T { command-prompt -P -1p'(jump to backward)' { send -X jump-to-backward -- '%%' } }",
|
||||
"bind -Tcopy-mode X { send -X set-mark }",
|
||||
"bind -Tcopy-mode e { if -F '#{selection_present}' { send -X open-selection } { send -X open-output } }",
|
||||
"bind -Tcopy-mode f { command-prompt -P -1p'(jump forward)' { send -X jump-forward -- '%%' } }",
|
||||
"bind -Tcopy-mode g { command-prompt -P -p'(goto line)' { send -X goto-line -- '%%' } }",
|
||||
"bind -Tcopy-mode n { send -X search-again }",
|
||||
@@ -687,6 +690,8 @@ key_bindings_init(void)
|
||||
"bind -Tcopy-mode-vi K { send -X scroll-up }",
|
||||
"bind -Tcopy-mode-vi L { send -X bottom-line }",
|
||||
"bind -Tcopy-mode-vi M { send -X middle-line }",
|
||||
"bind -Tcopy-mode-vi M-e { if -F '#{selection_present}' { send -X open-selection } { send -X open-output } }",
|
||||
"bind -Tcopy-mode-vi M-o { send -X select-output }",
|
||||
"bind -Tcopy-mode-vi N { send -X search-reverse }",
|
||||
"bind -Tcopy-mode-vi P { send -X toggle-position }",
|
||||
"bind -Tcopy-mode-vi T { command-prompt -P -1p'(jump to backward)' { send -X jump-to-backward -- '%%' } }",
|
||||
|
||||
10
spawn.c
10
spawn.c
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: spawn.c,v 1.55 2026/10/07 12:55:41 nicm Exp $ */
|
||||
/* $OpenBSD: spawn.c,v 1.57 2026/10/08 07:50:05 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -460,6 +460,8 @@ spawn_pane(struct spawn_context *sc, char **cause)
|
||||
/* If the command is empty, don't fork a child process. */
|
||||
if (sc->flags & SPAWN_EMPTY) {
|
||||
new_wp->flags |= PANE_EMPTY;
|
||||
new_wp->pid = 0;
|
||||
*new_wp->tty = '\0';
|
||||
new_wp->base.mode &= ~MODE_CURSOR;
|
||||
new_wp->base.mode |= MODE_CRLF;
|
||||
goto complete;
|
||||
@@ -703,7 +705,7 @@ spawn_editor_finish(struct window_pane *wp)
|
||||
|
||||
struct spawn_editor_state *
|
||||
spawn_editor(struct client *c, const char *buf, size_t len,
|
||||
spawn_finish_edit_cb cb, void *arg)
|
||||
const char *editor, spawn_finish_edit_cb cb, void *arg)
|
||||
{
|
||||
struct spawn_editor_state *es;
|
||||
struct spawn_context sc = { 0 };
|
||||
@@ -717,13 +719,13 @@ spawn_editor(struct client *c, const char *buf, size_t len,
|
||||
FILE *f;
|
||||
char *cmd, *cause = NULL;
|
||||
char path[] = _PATH_TMP "tmux.XXXXXXXX";
|
||||
const char *editor;
|
||||
int fd;
|
||||
|
||||
if (w->modal != NULL)
|
||||
return (NULL);
|
||||
|
||||
editor = options_get_string(global_options, "editor");
|
||||
if (editor == NULL)
|
||||
editor = options_get_string(global_options, "editor");
|
||||
fd = mkstemp(path);
|
||||
if (fd == -1)
|
||||
return (NULL);
|
||||
|
||||
70
tmux.1
70
tmux.1
@@ -1,4 +1,4 @@
|
||||
.\" $OpenBSD: tmux.1,v 1.1180 2026/10/07 13:13:49 nicm Exp $
|
||||
.\" $OpenBSD: tmux.1,v 1.1181 2026/10/08 07:50:05 nicm Exp $
|
||||
.\"
|
||||
.\" Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
.\"
|
||||
@@ -14,7 +14,7 @@
|
||||
.\" IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
.\" OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
.\"
|
||||
.Dd $Mdocdate: October 7 2026 $
|
||||
.Dd $Mdocdate: October 8 2026 $
|
||||
.Dt TMUX 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
@@ -2013,6 +2013,25 @@ Same as
|
||||
.Ic copy\-pipe
|
||||
but also exit copy mode.
|
||||
.It Xo
|
||||
.Ic copy\-pipe\-output
|
||||
.Op Fl aCP
|
||||
.Op Ar command
|
||||
.Op Ar prefix
|
||||
.Xc
|
||||
Copy the current OSC 133 command output, pipe it to
|
||||
.Ar command ,
|
||||
and clear the selection.
|
||||
.Ar prefix
|
||||
is used to name the new paste buffer.
|
||||
.It Xo
|
||||
.Ic copy\-output
|
||||
.Op Fl aCP
|
||||
.Op Ar prefix
|
||||
.Xc
|
||||
Copy the current OSC 133 command output and clear the selection.
|
||||
.Ar prefix
|
||||
is used to name the new paste buffer.
|
||||
.It Xo
|
||||
.Ic copy\-selection
|
||||
.Op Fl CP
|
||||
.Op Ar prefix
|
||||
@@ -2231,6 +2250,31 @@ but treat all non-whitespace characters as part of a word.
|
||||
.Xc
|
||||
Switch at which end of the selection the cursor sits.
|
||||
.It Xo
|
||||
.Ic open\-output
|
||||
.Op Fl a
|
||||
.Op Ar command
|
||||
(vi: M\-e)
|
||||
(emacs: e)
|
||||
.Xc
|
||||
Open the current OSC 133 command output in an editor.
|
||||
If
|
||||
.Ar command
|
||||
is given, it is used as the editor command; otherwise, the
|
||||
.Ic editor
|
||||
option is used.
|
||||
.It Xo
|
||||
.Ic open\-selection
|
||||
.Op Ar command
|
||||
(vi: M\-e)
|
||||
(emacs: e)
|
||||
.Xc
|
||||
Open the current selection in an editor.
|
||||
If
|
||||
.Ar command
|
||||
is given, it is used as the editor command; otherwise, the
|
||||
.Ic editor
|
||||
option is used.
|
||||
.It Xo
|
||||
.Ic page\-down
|
||||
(vi: C\-f)
|
||||
(emacs: PageDown)
|
||||
@@ -2271,6 +2315,21 @@ Same as
|
||||
.Ic pipe
|
||||
but also exit copy mode.
|
||||
.It Xo
|
||||
.Ic pipe\-output
|
||||
.Op Fl a
|
||||
.Op Ar command
|
||||
.Xc
|
||||
Pipe the current OSC 133 command output to
|
||||
.Ar command
|
||||
and clear the selection.
|
||||
.It Xo
|
||||
.Ic pipe\-selection
|
||||
.Op Ar command
|
||||
.Xc
|
||||
Pipe the selected text to
|
||||
.Ar command
|
||||
and clear the selection.
|
||||
.It Xo
|
||||
.Ic previous\-matching\-bracket
|
||||
(emacs: M\-C\-b)
|
||||
.Xc
|
||||
@@ -2460,6 +2519,13 @@ Search forward for the specified plain text.
|
||||
Repeat the last search in the reverse direction (forward becomes backward and
|
||||
backward becomes forward).
|
||||
.It Xo
|
||||
.Ic select\-output
|
||||
.Op Fl a
|
||||
(vi: M\-o)
|
||||
(emacs: C\-o)
|
||||
.Xc
|
||||
Select the current OSC 133 command output.
|
||||
.It Xo
|
||||
.Ic select\-line
|
||||
(vi: V)
|
||||
.Xc
|
||||
|
||||
6
tmux.h
6
tmux.h
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: tmux.h,v 1.1453 2026/10/06 17:49:45 nicm Exp $ */
|
||||
/* $OpenBSD: tmux.h,v 1.1454 2026/10/08 07:50:05 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -3533,6 +3533,8 @@ void grid_reader_start(struct grid_reader *, struct grid *, u_int, u_int);
|
||||
void grid_reader_get_cursor(struct grid_reader *, u_int *, u_int *);
|
||||
u_int grid_reader_line_length(struct grid_reader *);
|
||||
int grid_reader_in_set(struct grid_reader *, const char *);
|
||||
int grid_reader_output_range(struct grid_reader *, u_int *, u_int *,
|
||||
u_int *, u_int *);
|
||||
void grid_reader_cursor_right(struct grid_reader *, int, int, int);
|
||||
void grid_reader_cursor_left(struct grid_reader *, int);
|
||||
void grid_reader_cursor_down(struct grid_reader *);
|
||||
@@ -4222,7 +4224,7 @@ struct winlink *spawn_window(struct spawn_context *, char **);
|
||||
struct window_pane *spawn_pane(struct spawn_context *, char **);
|
||||
typedef void (*spawn_finish_edit_cb)(char *, size_t, void *);
|
||||
struct spawn_editor_state *spawn_editor(struct client *, const char *, size_t,
|
||||
spawn_finish_edit_cb, void *);
|
||||
const char *, spawn_finish_edit_cb, void *);
|
||||
void spawn_cancel_editor(struct spawn_editor_state *);
|
||||
pid_t spawn_get_editor_pid(struct spawn_editor_state *);
|
||||
void spawn_editor_finish(struct window_pane *);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: window-buffer.c,v 1.52 2026/08/24 21:19:40 nicm Exp $ */
|
||||
/* $OpenBSD: window-buffer.c,v 1.53 2026/10/08 07:50:05 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2017 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -606,7 +606,8 @@ window_buffer_start_edit(struct window_buffer_modedata *data,
|
||||
ed->name = xstrdup(paste_buffer_name(pb));
|
||||
ed->pb = pb;
|
||||
|
||||
ed->editor = spawn_editor(c, buf, len, window_buffer_edit_close_cb, ed);
|
||||
ed->editor = spawn_editor(c, buf, len, NULL,
|
||||
window_buffer_edit_close_cb, ed);
|
||||
if (ed->editor == NULL)
|
||||
window_buffer_finish_edit(ed);
|
||||
else {
|
||||
|
||||
399
window-copy.c
399
window-copy.c
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: window-copy.c,v 1.436 2026/10/07 12:59:50 nicm Exp $ */
|
||||
/* $OpenBSD: window-copy.c,v 1.437 2026/10/08 07:50:05 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -121,9 +121,17 @@ static int window_copy_set_selection(struct window_mode_entry *, int, int);
|
||||
static int window_copy_update_selection(struct window_mode_entry *, int,
|
||||
int);
|
||||
static void window_copy_synchronize_cursor(struct window_mode_entry *, int);
|
||||
static int window_copy_get_output_range(struct window_mode_entry *, int,
|
||||
u_int *, u_int *, u_int *, u_int *);
|
||||
static void *window_copy_get_selection(struct window_mode_entry *, size_t *);
|
||||
static void *window_copy_get_output(struct window_mode_entry *, size_t *,
|
||||
int);
|
||||
static void *window_copy_get_grid_range(struct window_mode_entry *, u_int,
|
||||
u_int, u_int, u_int, size_t *);
|
||||
static void window_copy_copy_buffer(struct window_mode_entry *,
|
||||
const char *, void *, size_t, int, int);
|
||||
static void window_copy_pipe_buffer(struct session *, const char *,
|
||||
void *, size_t);
|
||||
static void window_copy_pipe(struct window_mode_entry *,
|
||||
struct session *, const char *);
|
||||
static void window_copy_copy_pipe(struct window_mode_entry *,
|
||||
@@ -133,6 +141,8 @@ static void window_copy_copy_selection(struct window_mode_entry *,
|
||||
const char *, int, int);
|
||||
static void window_copy_append_selection(struct window_mode_entry *);
|
||||
static void window_copy_clear_selection(struct window_mode_entry *);
|
||||
static u_int window_copy_copy_line_length(struct window_mode_entry *,
|
||||
u_int, int *);
|
||||
static void window_copy_copy_line(struct window_mode_entry *, char **,
|
||||
size_t *, u_int, u_int, u_int);
|
||||
static int window_copy_in_set(struct window_mode_entry *, u_int, u_int,
|
||||
@@ -1155,7 +1165,9 @@ window_copy_formats(struct window_mode_entry *wme, struct format_tree *ft)
|
||||
format_add(ft, "selection_active", "1");
|
||||
else
|
||||
format_add(ft, "selection_active", "0");
|
||||
if (data->endselx != data->selx || data->endsely != data->sely)
|
||||
if (data->modekeys == MODEKEY_VI ||
|
||||
data->endselx != data->selx ||
|
||||
data->endsely != data->sely)
|
||||
format_add(ft, "selection_present", "1");
|
||||
else
|
||||
format_add(ft, "selection_present", "0");
|
||||
@@ -1605,6 +1617,84 @@ window_copy_cmd_copy_selection_and_cancel(struct window_copy_cmd_state *cs)
|
||||
return (WINDOW_COPY_CMD_CANCEL);
|
||||
}
|
||||
|
||||
static enum window_copy_cmd_action
|
||||
window_copy_cmd_select_output(struct window_copy_cmd_state *cs)
|
||||
{
|
||||
struct window_mode_entry *wme = cs->wme;
|
||||
struct window_copy_mode_data *data = wme->data;
|
||||
struct grid_reader gr;
|
||||
void *buf;
|
||||
size_t len;
|
||||
u_int sx, sy, ex, ey, last;
|
||||
int all = args_has(cs->wargs, 'a');
|
||||
int wrapped;
|
||||
|
||||
if (!window_copy_get_output_range(wme, all, &sx, &sy, &ex, &ey))
|
||||
return (WINDOW_COPY_CMD_NOTHING);
|
||||
|
||||
/* An empty output selects nothing. */
|
||||
buf = window_copy_get_grid_range(wme, sx, sy, ex, ey, &len);
|
||||
if (buf == NULL)
|
||||
return (WINDOW_COPY_CMD_NOTHING);
|
||||
free(buf);
|
||||
|
||||
window_copy_clear_selection(wme);
|
||||
data->lineflag = LINE_SEL_NONE;
|
||||
data->rectflag = 0;
|
||||
data->selflag = SEL_CHAR;
|
||||
window_copy_scroll_to(wme, sx, sy, 1);
|
||||
window_copy_start_selection(wme);
|
||||
|
||||
/* The end is inclusive in vi mode, so step back onto the last cell. */
|
||||
if (data->modekeys == MODEKEY_VI) {
|
||||
last = window_copy_copy_line_length(wme, ey, NULL);
|
||||
if (ex > last)
|
||||
ex = last;
|
||||
if (ex == 0 && ey > 0) {
|
||||
ey--;
|
||||
ex = window_copy_copy_line_length(wme, ey, &wrapped);
|
||||
if (wrapped && ex > 0)
|
||||
ex--;
|
||||
} else {
|
||||
grid_reader_start(&gr, data->backing->grid, ex, ey);
|
||||
grid_reader_cursor_left(&gr, 1);
|
||||
grid_reader_get_cursor(&gr, &ex, &ey);
|
||||
}
|
||||
}
|
||||
window_copy_scroll_to(wme, ex, ey, 1);
|
||||
return (WINDOW_COPY_CMD_REDRAW);
|
||||
}
|
||||
|
||||
static enum window_copy_cmd_action
|
||||
window_copy_cmd_copy_output(struct window_copy_cmd_state *cs)
|
||||
{
|
||||
struct window_mode_entry *wme = cs->wme;
|
||||
struct client *c = cs->c;
|
||||
struct session *s = cs->s;
|
||||
struct winlink *wl = cs->wl;
|
||||
struct window_pane *wp = wme->wp;
|
||||
void *buf;
|
||||
char *prefix = NULL;
|
||||
const char *arg0 = args_string(cs->wargs, 0);
|
||||
size_t len;
|
||||
int all = args_has(cs->wargs, 'a');
|
||||
int set_paste = !args_has(cs->wargs, 'P');
|
||||
int set_clip = !args_has(cs->wargs, 'C');
|
||||
|
||||
if (arg0 != NULL)
|
||||
prefix = format_single(NULL, arg0, c, s, wl, wp);
|
||||
if (s != NULL) {
|
||||
buf = window_copy_get_output(wme, &len, all);
|
||||
if (buf != NULL) {
|
||||
window_copy_copy_buffer(wme, prefix, buf, len,
|
||||
set_paste, set_clip);
|
||||
}
|
||||
}
|
||||
free(prefix);
|
||||
window_copy_clear_selection(wme);
|
||||
return (WINDOW_COPY_CMD_REDRAW);
|
||||
}
|
||||
|
||||
static enum window_copy_cmd_action
|
||||
window_copy_cmd_cursor_down(struct window_copy_cmd_state *cs)
|
||||
{
|
||||
@@ -2772,6 +2862,127 @@ window_copy_cmd_pipe_and_cancel(struct window_copy_cmd_state *cs)
|
||||
return (WINDOW_COPY_CMD_CANCEL);
|
||||
}
|
||||
|
||||
static enum window_copy_cmd_action
|
||||
window_copy_cmd_copy_pipe_output(struct window_copy_cmd_state *cs)
|
||||
{
|
||||
struct window_mode_entry *wme = cs->wme;
|
||||
struct client *c = cs->c;
|
||||
struct session *s = cs->s;
|
||||
struct winlink *wl = cs->wl;
|
||||
struct window_pane *wp = wme->wp;
|
||||
void *buf;
|
||||
char *command = NULL, *prefix = NULL;
|
||||
const char *arg0 = args_string(cs->wargs, 0);
|
||||
const char *arg1 = args_string(cs->wargs, 1);
|
||||
size_t len;
|
||||
int all = args_has(cs->wargs, 'a');
|
||||
int set_paste = !args_has(cs->wargs, 'P');
|
||||
int set_clip = !args_has(cs->wargs, 'C');
|
||||
|
||||
if (arg1 != NULL)
|
||||
prefix = format_single(NULL, arg1, c, s, wl, wp);
|
||||
if (s != NULL && arg0 != NULL && *arg0 != '\0')
|
||||
command = format_single(NULL, arg0, c, s, wl, wp);
|
||||
if (s != NULL) {
|
||||
buf = window_copy_get_output(wme, &len, all);
|
||||
if (buf != NULL) {
|
||||
window_copy_pipe_buffer(s, command, buf, len);
|
||||
window_copy_copy_buffer(wme, prefix, buf, len,
|
||||
set_paste, set_clip);
|
||||
}
|
||||
}
|
||||
free(command);
|
||||
free(prefix);
|
||||
window_copy_clear_selection(wme);
|
||||
return (WINDOW_COPY_CMD_REDRAW);
|
||||
}
|
||||
|
||||
static enum window_copy_cmd_action
|
||||
window_copy_cmd_pipe_output(struct window_copy_cmd_state *cs)
|
||||
{
|
||||
struct window_mode_entry *wme = cs->wme;
|
||||
struct client *c = cs->c;
|
||||
struct session *s = cs->s;
|
||||
struct winlink *wl = cs->wl;
|
||||
struct window_pane *wp = wme->wp;
|
||||
void *buf;
|
||||
char *command = NULL;
|
||||
const char *arg0 = args_string(cs->wargs, 0);
|
||||
size_t len;
|
||||
int all = args_has(cs->wargs, 'a');
|
||||
|
||||
if (s != NULL && arg0 != NULL && *arg0 != '\0')
|
||||
command = format_single(NULL, arg0, c, s, wl, wp);
|
||||
if (s != NULL) {
|
||||
buf = window_copy_get_output(wme, &len, all);
|
||||
if (buf != NULL) {
|
||||
window_copy_pipe_buffer(s, command, buf, len);
|
||||
free(buf);
|
||||
}
|
||||
}
|
||||
free(command);
|
||||
window_copy_clear_selection(wme);
|
||||
return (WINDOW_COPY_CMD_REDRAW);
|
||||
}
|
||||
|
||||
static void
|
||||
window_copy_open_done(char *buf, __unused size_t len, __unused void *arg)
|
||||
{
|
||||
free(buf);
|
||||
}
|
||||
|
||||
static void
|
||||
window_copy_open_buffer(struct window_copy_cmd_state *cs, void *buf,
|
||||
size_t len)
|
||||
{
|
||||
struct window_mode_entry *wme = cs->wme;
|
||||
struct window_pane *wp = wme->wp;
|
||||
char *editor = NULL;
|
||||
const char *arg0 = args_string(cs->wargs, 0);
|
||||
|
||||
if (arg0 != NULL && *arg0 != '\0')
|
||||
editor = format_single(NULL, arg0, cs->c, cs->s, cs->wl, wp);
|
||||
spawn_editor(cs->c, buf, len, editor, window_copy_open_done, NULL);
|
||||
free(editor);
|
||||
}
|
||||
|
||||
static enum window_copy_cmd_action
|
||||
window_copy_cmd_open_selection(struct window_copy_cmd_state *cs)
|
||||
{
|
||||
struct window_mode_entry *wme = cs->wme;
|
||||
struct window_copy_mode_data *data = wme->data;
|
||||
void *buf;
|
||||
size_t len;
|
||||
|
||||
if (cs->c == NULL)
|
||||
return (WINDOW_COPY_CMD_NOTHING);
|
||||
if (data->screen.sel == NULL && data->lineflag == LINE_SEL_NONE)
|
||||
return (WINDOW_COPY_CMD_NOTHING);
|
||||
buf = window_copy_get_selection(wme, &len);
|
||||
if (buf != NULL) {
|
||||
window_copy_open_buffer(cs, buf, len);
|
||||
free(buf);
|
||||
}
|
||||
return (WINDOW_COPY_CMD_NOTHING);
|
||||
}
|
||||
|
||||
static enum window_copy_cmd_action
|
||||
window_copy_cmd_open_output(struct window_copy_cmd_state *cs)
|
||||
{
|
||||
struct window_mode_entry *wme = cs->wme;
|
||||
void *buf;
|
||||
size_t len;
|
||||
|
||||
if (cs->c == NULL)
|
||||
return (WINDOW_COPY_CMD_NOTHING);
|
||||
buf = window_copy_get_output(wme, &len, args_has(cs->wargs, 'a'));
|
||||
if (buf != NULL) {
|
||||
window_copy_open_buffer(cs, buf, len);
|
||||
free(buf);
|
||||
}
|
||||
return (WINDOW_COPY_CMD_NOTHING);
|
||||
}
|
||||
|
||||
static enum window_copy_cmd_action
|
||||
window_copy_cmd_goto_line(struct window_copy_cmd_state *cs)
|
||||
{
|
||||
@@ -3426,6 +3637,18 @@ static const struct {
|
||||
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
|
||||
.f = window_copy_cmd_copy_pipe_and_cancel
|
||||
},
|
||||
{ .command = "copy-pipe-output",
|
||||
.args = { "aCP", 0, 2, NULL },
|
||||
.flags = 0,
|
||||
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
|
||||
.f = window_copy_cmd_copy_pipe_output
|
||||
},
|
||||
{ .command = "copy-output",
|
||||
.args = { "aCP", 0, 1, NULL },
|
||||
.flags = 0,
|
||||
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
|
||||
.f = window_copy_cmd_copy_output
|
||||
},
|
||||
{ .command = "copy-selection-no-clear",
|
||||
.args = { "CP", 0, 1, NULL },
|
||||
.flags = 0,
|
||||
@@ -3648,6 +3871,18 @@ static const struct {
|
||||
.clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
|
||||
.f = window_copy_cmd_other_end
|
||||
},
|
||||
{ .command = "open-output",
|
||||
.args = { "a", 0, 1, NULL },
|
||||
.flags = 0,
|
||||
.clear = WINDOW_COPY_CMD_CLEAR_NEVER,
|
||||
.f = window_copy_cmd_open_output
|
||||
},
|
||||
{ .command = "open-selection",
|
||||
.args = { "", 0, 1, NULL },
|
||||
.flags = 0,
|
||||
.clear = WINDOW_COPY_CMD_CLEAR_NEVER,
|
||||
.f = window_copy_cmd_open_selection
|
||||
},
|
||||
{ .command = "page-down",
|
||||
.args = { "", 0, 0, NULL },
|
||||
.flags = WINDOW_COPY_CMD_FLAG_READONLY,
|
||||
@@ -3684,6 +3919,18 @@ static const struct {
|
||||
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
|
||||
.f = window_copy_cmd_pipe_and_cancel
|
||||
},
|
||||
{ .command = "pipe-output",
|
||||
.args = { "a", 0, 1, NULL },
|
||||
.flags = 0,
|
||||
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
|
||||
.f = window_copy_cmd_pipe_output
|
||||
},
|
||||
{ .command = "pipe-selection",
|
||||
.args = { "", 0, 1, NULL },
|
||||
.flags = 0,
|
||||
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
|
||||
.f = window_copy_cmd_pipe
|
||||
},
|
||||
{ .command = "previous-matching-bracket",
|
||||
.args = { "", 0, 0, NULL },
|
||||
.flags = WINDOW_COPY_CMD_FLAG_READONLY,
|
||||
@@ -3861,6 +4108,12 @@ static const struct {
|
||||
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
|
||||
.f = window_copy_cmd_search_reverse
|
||||
},
|
||||
{ .command = "select-output",
|
||||
.args = { "a", 0, 0, NULL },
|
||||
.flags = 0,
|
||||
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
|
||||
.f = window_copy_cmd_select_output
|
||||
},
|
||||
{ .command = "select-line",
|
||||
.args = { "", 0, 0, NULL },
|
||||
.flags = 0,
|
||||
@@ -6054,9 +6307,11 @@ window_copy_set_selection(struct window_mode_entry *wme, int may_redraw,
|
||||
window_copy_redraw_lines(wme, cy, sy - cy + 1);
|
||||
} else {
|
||||
if (endsy < cy) {
|
||||
window_copy_redraw_lines(wme, endsy, cy - endsy + 1);
|
||||
window_copy_redraw_lines(wme, endsy,
|
||||
cy - endsy + 1);
|
||||
} else {
|
||||
window_copy_redraw_lines(wme, cy, endsy - cy + 1);
|
||||
window_copy_redraw_lines(wme, cy,
|
||||
endsy - cy + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6064,6 +6319,78 @@ window_copy_set_selection(struct window_mode_entry *wme, int may_redraw,
|
||||
return (1);
|
||||
}
|
||||
|
||||
/* Get the range of the current output, or the whole buffer with all. */
|
||||
static int
|
||||
window_copy_get_output_range(struct window_mode_entry *wme, int all,
|
||||
u_int *sx, u_int *sy, u_int *ex, u_int *ey)
|
||||
{
|
||||
struct window_copy_mode_data *data = wme->data;
|
||||
struct grid *gd = data->backing->grid;
|
||||
struct grid_reader gr;
|
||||
u_int total, cursor_y;
|
||||
int found;
|
||||
|
||||
if (!all) {
|
||||
cursor_y = screen_hsize(data->backing) + data->cy - data->oy;
|
||||
grid_reader_start(&gr, gd, data->cx, cursor_y);
|
||||
found = grid_reader_output_range(&gr, sx, sy, ex, ey);
|
||||
return (found);
|
||||
}
|
||||
total = gd->hsize + gd->sy;
|
||||
*sx = *sy = 0;
|
||||
*ey = total - 1;
|
||||
*ex = grid_get_line(gd, *ey)->cellused;
|
||||
return (1);
|
||||
}
|
||||
|
||||
/* Get current output. */
|
||||
static void *
|
||||
window_copy_get_output(struct window_mode_entry *wme, size_t *len, int all)
|
||||
{
|
||||
void *buf;
|
||||
u_int sx, sy, ex, ey;
|
||||
|
||||
if (!window_copy_get_output_range(wme, all, &sx, &sy, &ex, &ey)) {
|
||||
*len = 0;
|
||||
return (NULL);
|
||||
}
|
||||
buf = window_copy_get_grid_range(wme, sx, sy, ex, ey, len);
|
||||
return (buf);
|
||||
}
|
||||
|
||||
/* Get the text from sx,sy up to ex,ey, or NULL if there is none. */
|
||||
static void *
|
||||
window_copy_get_grid_range(struct window_mode_entry *wme, u_int sx, u_int sy,
|
||||
u_int ex, u_int ey, size_t *len)
|
||||
{
|
||||
struct window_copy_mode_data *data = wme->data;
|
||||
struct grid *gd = data->backing->grid;
|
||||
char *buf;
|
||||
size_t off;
|
||||
u_int i, first, last;
|
||||
|
||||
buf = xmalloc(1);
|
||||
off = 0;
|
||||
for (i = sy; i <= ey; i++) {
|
||||
first = 0;
|
||||
last = gd->sx;
|
||||
if (i == sy)
|
||||
first = sx;
|
||||
if (i == ey)
|
||||
last = ex;
|
||||
window_copy_copy_line(wme, &buf, &off, i, first, last);
|
||||
}
|
||||
if (off != 0 && buf[off - 1] == '\n')
|
||||
off--;
|
||||
if (off == 0) {
|
||||
free(buf);
|
||||
*len = 0;
|
||||
return (NULL);
|
||||
}
|
||||
*len = off;
|
||||
return (buf);
|
||||
}
|
||||
|
||||
static void *
|
||||
window_copy_get_selection(struct window_mode_entry *wme, size_t *len)
|
||||
{
|
||||
@@ -6074,7 +6401,7 @@ window_copy_get_selection(struct window_mode_entry *wme, size_t *len)
|
||||
size_t off;
|
||||
u_int i, xx, yy, sx, sy, ex, ey, ey_last;
|
||||
u_int firstsx, lastex, restex, restsx, selx;
|
||||
int keys;
|
||||
int keys, wrapped;
|
||||
|
||||
if (data->screen.sel == NULL && data->lineflag == LINE_SEL_NONE) {
|
||||
buf = window_copy_match_at_cursor(data);
|
||||
@@ -6107,7 +6434,7 @@ window_copy_get_selection(struct window_mode_entry *wme, size_t *len)
|
||||
}
|
||||
|
||||
/* Trim ex to end of line. */
|
||||
ey_last = window_copy_find_length(wme, ey);
|
||||
ey_last = window_copy_copy_line_length(wme, ey, &wrapped);
|
||||
if (ex > ey_last)
|
||||
ex = ey_last;
|
||||
|
||||
@@ -6178,8 +6505,7 @@ window_copy_get_selection(struct window_mode_entry *wme, size_t *len)
|
||||
}
|
||||
/* Remove final \n (unless at end in vi mode). */
|
||||
if (keys == MODEKEY_EMACS || lastex <= ey_last) {
|
||||
if (~grid_get_line(data->backing->grid, ey)->flags &
|
||||
GRID_LINE_WRAPPED || lastex != ey_last)
|
||||
if (!wrapped || lastex != ey_last)
|
||||
off -= 1;
|
||||
}
|
||||
*len = off;
|
||||
@@ -6220,18 +6546,28 @@ window_copy_pipe_run(struct window_mode_entry *wme, struct session *s,
|
||||
const char *cmd, size_t *len)
|
||||
{
|
||||
void *buf;
|
||||
struct job *job;
|
||||
|
||||
buf = window_copy_get_selection(wme, len);
|
||||
window_copy_pipe_buffer(s, cmd, buf, *len);
|
||||
return (buf);
|
||||
}
|
||||
|
||||
static void
|
||||
window_copy_pipe_buffer(struct session *s, const char *cmd, void *buf,
|
||||
size_t len)
|
||||
{
|
||||
struct job *job;
|
||||
|
||||
if (buf == NULL)
|
||||
return;
|
||||
if (cmd == NULL || *cmd == '\0')
|
||||
cmd = options_get_string(global_options, "copy-command");
|
||||
if (cmd != NULL && *cmd != '\0') {
|
||||
job = job_run(cmd, 0, NULL, NULL, s, NULL, NULL, NULL, NULL,
|
||||
NULL, JOB_NOWAIT, -1, -1);
|
||||
if (job != NULL)
|
||||
bufferevent_write(job_get_event(job), buf, *len);
|
||||
bufferevent_write(job_get_event(job), buf, len);
|
||||
}
|
||||
return (buf);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -6307,6 +6643,29 @@ window_copy_append_selection(struct window_mode_entry *wme)
|
||||
free(bufname);
|
||||
}
|
||||
|
||||
/* Get the length for copying, preserving spaces on wrapped lines. */
|
||||
static u_int
|
||||
window_copy_copy_line_length(struct window_mode_entry *wme, u_int sy,
|
||||
int *wrapped)
|
||||
{
|
||||
struct window_copy_mode_data *data = wme->data;
|
||||
struct grid *gd = data->backing->grid;
|
||||
struct grid_line *gl = grid_get_line(gd, sy);
|
||||
u_int length;
|
||||
int line_wrapped;
|
||||
|
||||
line_wrapped = 0;
|
||||
if ((gl->flags & GRID_LINE_WRAPPED) && gl->cellsize <= gd->sx)
|
||||
line_wrapped = 1;
|
||||
if (wrapped != NULL)
|
||||
*wrapped = line_wrapped;
|
||||
if (line_wrapped)
|
||||
length = gl->cellsize;
|
||||
else
|
||||
length = window_copy_find_length(wme, sy);
|
||||
return (length);
|
||||
}
|
||||
|
||||
static void
|
||||
window_copy_copy_line(struct window_mode_entry *wme, char **buf, size_t *off,
|
||||
u_int sy, u_int sx, u_int ex)
|
||||
@@ -6314,27 +6673,15 @@ window_copy_copy_line(struct window_mode_entry *wme, char **buf, size_t *off,
|
||||
struct window_copy_mode_data *data = wme->data;
|
||||
struct grid *gd = data->backing->grid;
|
||||
struct grid_cell gc;
|
||||
struct grid_line *gl;
|
||||
struct utf8_data ud;
|
||||
u_int i, xx, wrapped = 0;
|
||||
u_int i, xx;
|
||||
const char *s;
|
||||
int wrapped;
|
||||
|
||||
if (sx > ex)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Work out if the line was wrapped at the screen edge and all of it is
|
||||
* on screen.
|
||||
*/
|
||||
gl = grid_get_line(gd, sy);
|
||||
if (gl->flags & GRID_LINE_WRAPPED && gl->cellsize <= gd->sx)
|
||||
wrapped = 1;
|
||||
|
||||
/* If the line was wrapped, don't strip spaces (use the full length). */
|
||||
if (wrapped)
|
||||
xx = gl->cellsize;
|
||||
else
|
||||
xx = window_copy_find_length(wme, sy);
|
||||
xx = window_copy_copy_line_length(wme, sy, &wrapped);
|
||||
if (ex > xx)
|
||||
ex = xx;
|
||||
if (sx > xx)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: window-customize.c,v 1.36 2026/07/28 10:35:31 nicm Exp $ */
|
||||
/* $OpenBSD: window-customize.c,v 1.37 2026/10/08 07:50:05 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2020 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -2011,13 +2011,15 @@ window_customize_edit_close_cb(char *buf, size_t len, void *arg)
|
||||
switch (ed->edit_type) {
|
||||
case WINDOW_CUSTOMIZE_EDIT_OPTION:
|
||||
if (window_customize_option_editable(data, item) &&
|
||||
window_customize_set_option_value(item, value, &cause) != 0) {
|
||||
window_customize_set_option_value(item, value,
|
||||
&cause) != 0) {
|
||||
free(cause);
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
case WINDOW_CUSTOMIZE_EDIT_KEY_COMMAND:
|
||||
if (window_customize_set_command_value(item, value, &cause) != 0) {
|
||||
if (window_customize_set_command_value(item, value,
|
||||
&cause) != 0) {
|
||||
free(cause);
|
||||
goto out;
|
||||
}
|
||||
@@ -2104,8 +2106,8 @@ window_customize_start_edit(struct window_customize_modedata *data,
|
||||
buf = "\n";
|
||||
len = 1;
|
||||
}
|
||||
ed->editor = spawn_editor(c, buf, len, window_customize_edit_close_cb,
|
||||
ed);
|
||||
ed->editor = spawn_editor(c, buf, len, NULL,
|
||||
window_customize_edit_close_cb, ed);
|
||||
free(value);
|
||||
if (ed->editor == NULL)
|
||||
window_customize_finish_edit(ed);
|
||||
|
||||
8
window.c
8
window.c
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: window.c,v 1.384 2026/10/02 12:48:52 nicm Exp $ */
|
||||
/* $OpenBSD: window.c,v 1.385 2026/10/08 07:06:26 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -1592,13 +1592,15 @@ window_pane_destroy(struct window_pane *wp)
|
||||
window_pane_free_modes(wp);
|
||||
screen_write_sync_clear_dirty(wp);
|
||||
|
||||
if (wp->event != NULL) {
|
||||
bufferevent_free(wp->event);
|
||||
wp->event = NULL;
|
||||
}
|
||||
if (wp->fd != -1) {
|
||||
#ifdef HAVE_UTEMPTER
|
||||
utempter_remove_record(wp->fd);
|
||||
kill(getpid(), SIGCHLD);
|
||||
#endif
|
||||
bufferevent_free(wp->event);
|
||||
wp->event = NULL;
|
||||
close(wp->fd);
|
||||
wp->fd = -1;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user