mirror of
https://github.com/tmux/tmux.git
synced 2024-12-04 19:58:48 +00:00
Support multiple occurances of the same argument. Use this for a new
flag -e to new-window, split-window, respawn-window, respawn-pane to pass environment variables into the newly created process. From Steffen Christgau in GitHub issue 1697.
This commit is contained in:
parent
dfb7bb6830
commit
c4b0da5513
157
arguments.c
157
arguments.c
@ -29,9 +29,15 @@
|
||||
* Manipulate command arguments.
|
||||
*/
|
||||
|
||||
struct args_value {
|
||||
char *value;
|
||||
TAILQ_ENTRY(args_value) entry;
|
||||
};
|
||||
TAILQ_HEAD(args_values, args_value);
|
||||
|
||||
struct args_entry {
|
||||
u_char flag;
|
||||
char *value;
|
||||
struct args_values values;
|
||||
RB_ENTRY(args_entry) entry;
|
||||
};
|
||||
|
||||
@ -93,12 +99,18 @@ args_free(struct args *args)
|
||||
{
|
||||
struct args_entry *entry;
|
||||
struct args_entry *entry1;
|
||||
struct args_value *value;
|
||||
struct args_value *value1;
|
||||
|
||||
cmd_free_argv(args->argc, args->argv);
|
||||
|
||||
RB_FOREACH_SAFE(entry, args_tree, &args->tree, entry1) {
|
||||
RB_REMOVE(args_tree, &args->tree, entry);
|
||||
free(entry->value);
|
||||
TAILQ_FOREACH_SAFE(value, &entry->values, entry, value1) {
|
||||
TAILQ_REMOVE(&entry->values, value, entry);
|
||||
free(value->value);
|
||||
free(value);
|
||||
}
|
||||
free(entry);
|
||||
}
|
||||
|
||||
@ -124,22 +136,69 @@ args_print_add(char **buf, size_t *len, const char *fmt, ...)
|
||||
free(s);
|
||||
}
|
||||
|
||||
/* Add value to string. */
|
||||
static void
|
||||
args_print_add_value(char **buf, size_t *len, struct args_entry *entry,
|
||||
struct args_value *value)
|
||||
{
|
||||
static const char quoted[] = " #\"';$";
|
||||
char *escaped;
|
||||
int flags;
|
||||
|
||||
if (**buf != '\0')
|
||||
args_print_add(buf, len, " -%c ", entry->flag);
|
||||
else
|
||||
args_print_add(buf, len, "-%c ", entry->flag);
|
||||
|
||||
flags = VIS_OCTAL|VIS_TAB|VIS_NL;
|
||||
if (value->value[strcspn(value->value, quoted)] != '\0')
|
||||
flags |= VIS_DQ;
|
||||
utf8_stravis(&escaped, value->value, flags);
|
||||
if (flags & VIS_DQ)
|
||||
args_print_add(buf, len, "\"%s\"", escaped);
|
||||
else
|
||||
args_print_add(buf, len, "%s", escaped);
|
||||
free(escaped);
|
||||
}
|
||||
|
||||
/* Add argument to string. */
|
||||
static void
|
||||
args_print_add_argument(char **buf, size_t *len, const char *argument)
|
||||
{
|
||||
static const char quoted[] = " #\"';$";
|
||||
char *escaped;
|
||||
int flags;
|
||||
|
||||
if (**buf != '\0')
|
||||
args_print_add(buf, len, " ");
|
||||
|
||||
flags = VIS_OCTAL|VIS_TAB|VIS_NL;
|
||||
if (argument[strcspn(argument, quoted)] != '\0')
|
||||
flags |= VIS_DQ;
|
||||
utf8_stravis(&escaped, argument, flags);
|
||||
if (flags & VIS_DQ)
|
||||
args_print_add(buf, len, "\"%s\"", escaped);
|
||||
else
|
||||
args_print_add(buf, len, "%s", escaped);
|
||||
free(escaped);
|
||||
}
|
||||
|
||||
/* Print a set of arguments. */
|
||||
char *
|
||||
args_print(struct args *args)
|
||||
{
|
||||
size_t len;
|
||||
char *buf, *escaped;
|
||||
int i, flags;
|
||||
char *buf;
|
||||
int i;
|
||||
struct args_entry *entry;
|
||||
static const char quoted[] = " #\"';$";
|
||||
struct args_value *value;
|
||||
|
||||
len = 1;
|
||||
buf = xcalloc(1, len);
|
||||
|
||||
/* Process the flags first. */
|
||||
RB_FOREACH(entry, args_tree, &args->tree) {
|
||||
if (entry->value != NULL)
|
||||
if (!TAILQ_EMPTY(&entry->values))
|
||||
continue;
|
||||
|
||||
if (*buf == '\0')
|
||||
@ -149,40 +208,13 @@ args_print(struct args *args)
|
||||
|
||||
/* Then the flags with arguments. */
|
||||
RB_FOREACH(entry, args_tree, &args->tree) {
|
||||
if (entry->value == NULL)
|
||||
continue;
|
||||
|
||||
if (*buf != '\0')
|
||||
args_print_add(&buf, &len, " -%c ", entry->flag);
|
||||
else
|
||||
args_print_add(&buf, &len, "-%c ", entry->flag);
|
||||
|
||||
flags = VIS_OCTAL|VIS_TAB|VIS_NL;
|
||||
if (entry->value[strcspn(entry->value, quoted)] != '\0')
|
||||
flags |= VIS_DQ;
|
||||
utf8_stravis(&escaped, entry->value, flags);
|
||||
if (flags & VIS_DQ)
|
||||
args_print_add(&buf, &len, "\"%s\"", escaped);
|
||||
else
|
||||
args_print_add(&buf, &len, "%s", escaped);
|
||||
free(escaped);
|
||||
TAILQ_FOREACH(value, &entry->values, entry)
|
||||
args_print_add_value(&buf, &len, entry, value);
|
||||
}
|
||||
|
||||
/* And finally the argument vector. */
|
||||
for (i = 0; i < args->argc; i++) {
|
||||
if (*buf != '\0')
|
||||
args_print_add(&buf, &len, " ");
|
||||
|
||||
flags = VIS_OCTAL|VIS_TAB|VIS_NL;
|
||||
if (args->argv[i][strcspn(args->argv[i], quoted)] != '\0')
|
||||
flags |= VIS_DQ;
|
||||
utf8_stravis(&escaped, args->argv[i], flags);
|
||||
if (flags & VIS_DQ)
|
||||
args_print_add(&buf, &len, "\"%s\"", escaped);
|
||||
else
|
||||
args_print_add(&buf, &len, "%s", escaped);
|
||||
free(escaped);
|
||||
}
|
||||
for (i = 0; i < args->argc; i++)
|
||||
args_print_add_argument(&buf, &len, args->argv[i]);
|
||||
|
||||
return (buf);
|
||||
}
|
||||
@ -196,22 +228,24 @@ args_has(struct args *args, u_char ch)
|
||||
|
||||
/* Set argument value in the arguments tree. */
|
||||
void
|
||||
args_set(struct args *args, u_char ch, const char *value)
|
||||
args_set(struct args *args, u_char ch, const char *s)
|
||||
{
|
||||
struct args_entry *entry;
|
||||
struct args_value *value;
|
||||
|
||||
/* Replace existing argument. */
|
||||
if ((entry = args_find(args, ch)) != NULL) {
|
||||
free(entry->value);
|
||||
entry->value = NULL;
|
||||
} else {
|
||||
entry = args_find(args, ch);
|
||||
if (entry == NULL) {
|
||||
entry = xcalloc(1, sizeof *entry);
|
||||
entry->flag = ch;
|
||||
TAILQ_INIT(&entry->values);
|
||||
RB_INSERT(args_tree, &args->tree, entry);
|
||||
}
|
||||
|
||||
if (value != NULL)
|
||||
entry->value = xstrdup(value);
|
||||
if (s != NULL) {
|
||||
value = xcalloc(1, sizeof *value);
|
||||
value->value = xstrdup(s);
|
||||
TAILQ_INSERT_TAIL(&entry->values, value, entry);
|
||||
}
|
||||
}
|
||||
|
||||
/* Get argument value. Will be NULL if it isn't present. */
|
||||
@ -222,7 +256,34 @@ args_get(struct args *args, u_char ch)
|
||||
|
||||
if ((entry = args_find(args, ch)) == NULL)
|
||||
return (NULL);
|
||||
return (entry->value);
|
||||
return (TAILQ_LAST(&entry->values, args_values)->value);
|
||||
}
|
||||
|
||||
/* Get first value in argument. */
|
||||
const char *
|
||||
args_first_value(struct args *args, u_char ch, struct args_value **value)
|
||||
{
|
||||
struct args_entry *entry;
|
||||
|
||||
if ((entry = args_find(args, ch)) == NULL)
|
||||
return (NULL);
|
||||
|
||||
*value = TAILQ_FIRST(&entry->values);
|
||||
if (*value == NULL)
|
||||
return (NULL);
|
||||
return ((*value)->value);
|
||||
}
|
||||
|
||||
/* Get next value in argument. */
|
||||
const char *
|
||||
args_next_value(struct args_value **value)
|
||||
{
|
||||
if (*value == NULL)
|
||||
return (NULL);
|
||||
*value = TAILQ_NEXT(*value, entry);
|
||||
if (*value == NULL)
|
||||
return (NULL);
|
||||
return ((*value)->value);
|
||||
}
|
||||
|
||||
/* Convert an argument value to a number. */
|
||||
@ -233,13 +294,15 @@ args_strtonum(struct args *args, u_char ch, long long minval, long long maxval,
|
||||
const char *errstr;
|
||||
long long ll;
|
||||
struct args_entry *entry;
|
||||
struct args_value *value;
|
||||
|
||||
if ((entry = args_find(args, ch)) == NULL) {
|
||||
*cause = xstrdup("missing");
|
||||
return (0);
|
||||
}
|
||||
value = TAILQ_LAST(&entry->values, args_values);
|
||||
|
||||
ll = strtonum(entry->value, minval, maxval, &errstr);
|
||||
ll = strtonum(value->value, minval, maxval, &errstr);
|
||||
if (errstr != NULL) {
|
||||
*cause = xstrdup(errstr);
|
||||
return (0);
|
||||
|
@ -38,9 +38,9 @@ const struct cmd_entry cmd_new_window_entry = {
|
||||
.name = "new-window",
|
||||
.alias = "neww",
|
||||
|
||||
.args = { "ac:dF:kn:Pt:", 0, -1 },
|
||||
.usage = "[-adkP] [-c start-directory] [-F format] [-n window-name] "
|
||||
CMD_TARGET_WINDOW_USAGE " [command]",
|
||||
.args = { "ac:de:F:kn:Pt:", 0, -1 },
|
||||
.usage = "[-adkP] [-c start-directory] [-e environment] [-F format] "
|
||||
"[-n window-name] " CMD_TARGET_WINDOW_USAGE " [command]",
|
||||
|
||||
.target = { 't', CMD_FIND_WINDOW, CMD_FIND_WINDOW_INDEX },
|
||||
|
||||
@ -60,8 +60,9 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
|
||||
int idx = item->target.idx;
|
||||
struct winlink *new_wl;
|
||||
char *cause = NULL, *cp;
|
||||
const char *template;
|
||||
const char *template, *add;
|
||||
struct cmd_find_state fs;
|
||||
struct args_value *value;
|
||||
|
||||
if (args_has(args, 'a') && (idx = winlink_shuffle_up(s, wl)) == -1) {
|
||||
cmdq_error(item, "couldn't get a window index");
|
||||
@ -75,6 +76,13 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
|
||||
sc.name = args_get(args, 'n');
|
||||
sc.argc = args->argc;
|
||||
sc.argv = args->argv;
|
||||
sc.environ = environ_create();
|
||||
|
||||
add = args_first_value(args, 'e', &value);
|
||||
while (add != NULL) {
|
||||
environ_put(sc.environ, add);
|
||||
add = args_next_value(&value);
|
||||
}
|
||||
|
||||
sc.idx = idx;
|
||||
sc.cwd = args_get(args, 'c');
|
||||
@ -107,5 +115,6 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
|
||||
cmd_find_from_winlink(&fs, new_wl, 0);
|
||||
cmdq_insert_hook(s, item, &fs, "after-new-window");
|
||||
|
||||
environ_free(sc.environ);
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
@ -34,9 +34,9 @@ const struct cmd_entry cmd_respawn_pane_entry = {
|
||||
.name = "respawn-pane",
|
||||
.alias = "respawnp",
|
||||
|
||||
.args = { "c:kt:", 0, -1 },
|
||||
.usage = "[-c start-directory] [-k] " CMD_TARGET_PANE_USAGE
|
||||
" [command]",
|
||||
.args = { "c:e:kt:", 0, -1 },
|
||||
.usage = "[-k] [-c start-directory] [-e environment] "
|
||||
CMD_TARGET_PANE_USAGE " [command]",
|
||||
|
||||
.target = { 't', CMD_FIND_PANE, 0 },
|
||||
|
||||
@ -53,6 +53,8 @@ cmd_respawn_pane_exec(struct cmd *self, struct cmdq_item *item)
|
||||
struct winlink *wl = item->target.wl;
|
||||
struct window_pane *wp = item->target.wp;
|
||||
char *cause = NULL;
|
||||
const char *add;
|
||||
struct args_value *value;
|
||||
|
||||
memset(&sc, 0, sizeof sc);
|
||||
sc.item = item;
|
||||
@ -65,6 +67,13 @@ cmd_respawn_pane_exec(struct cmd *self, struct cmdq_item *item)
|
||||
sc.name = NULL;
|
||||
sc.argc = args->argc;
|
||||
sc.argv = args->argv;
|
||||
sc.environ = environ_create();
|
||||
|
||||
add = args_first_value(args, 'e', &value);
|
||||
while (add != NULL) {
|
||||
environ_put(sc.environ, add);
|
||||
add = args_next_value(&value);
|
||||
}
|
||||
|
||||
sc.idx = -1;
|
||||
sc.cwd = args_get(args, 'c');
|
||||
@ -82,5 +91,6 @@ cmd_respawn_pane_exec(struct cmd *self, struct cmdq_item *item)
|
||||
wp->flags |= PANE_REDRAW;
|
||||
server_status_window(wp->window);
|
||||
|
||||
environ_free(sc.environ);
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
@ -34,9 +34,9 @@ const struct cmd_entry cmd_respawn_window_entry = {
|
||||
.name = "respawn-window",
|
||||
.alias = "respawnw",
|
||||
|
||||
.args = { "c:kt:", 0, -1 },
|
||||
.usage = "[-c start-directory] [-k] " CMD_TARGET_WINDOW_USAGE
|
||||
" [command]",
|
||||
.args = { "c:e:kt:", 0, -1 },
|
||||
.usage = "[-k] [-c start-directory] [-e environment] "
|
||||
CMD_TARGET_WINDOW_USAGE " [command]",
|
||||
|
||||
.target = { 't', CMD_FIND_WINDOW, 0 },
|
||||
|
||||
@ -52,6 +52,8 @@ cmd_respawn_window_exec(struct cmd *self, struct cmdq_item *item)
|
||||
struct session *s = item->target.s;
|
||||
struct winlink *wl = item->target.wl;
|
||||
char *cause = NULL;
|
||||
const char *add;
|
||||
struct args_value *value;
|
||||
|
||||
memset(&sc, 0, sizeof sc);
|
||||
sc.item = item;
|
||||
@ -61,6 +63,13 @@ cmd_respawn_window_exec(struct cmd *self, struct cmdq_item *item)
|
||||
sc.name = NULL;
|
||||
sc.argc = args->argc;
|
||||
sc.argv = args->argv;
|
||||
sc.environ = environ_create();
|
||||
|
||||
add = args_first_value(args, 'e', &value);
|
||||
while (add != NULL) {
|
||||
environ_put(sc.environ, add);
|
||||
add = args_next_value(&value);
|
||||
}
|
||||
|
||||
sc.idx = -1;
|
||||
sc.cwd = args_get(args, 'c');
|
||||
@ -77,5 +86,6 @@ cmd_respawn_window_exec(struct cmd *self, struct cmdq_item *item)
|
||||
|
||||
server_redraw_window(wl->window);
|
||||
|
||||
environ_free(sc.environ);
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
@ -40,8 +40,8 @@ const struct cmd_entry cmd_split_window_entry = {
|
||||
.name = "split-window",
|
||||
.alias = "splitw",
|
||||
|
||||
.args = { "bc:dfF:l:hp:Pt:v", 0, -1 },
|
||||
.usage = "[-bdfhvP] [-c start-directory] [-F format] "
|
||||
.args = { "bc:de:fF:l:hp:Pt:v", 0, -1 },
|
||||
.usage = "[-bdefhvP] [-c start-directory] [-e environment] [-F format] "
|
||||
"[-p percentage|-l size] " CMD_TARGET_PANE_USAGE " [command]",
|
||||
|
||||
.target = { 't', CMD_FIND_PANE, 0 },
|
||||
@ -64,8 +64,9 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
|
||||
struct layout_cell *lc;
|
||||
struct cmd_find_state fs;
|
||||
int size, percentage, flags;
|
||||
const char *template;
|
||||
const char *template, *add;
|
||||
char *cause, *cp;
|
||||
struct args_value *value;
|
||||
|
||||
if (args_has(args, 'h'))
|
||||
type = LAYOUT_LEFTRIGHT;
|
||||
@ -117,6 +118,13 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
|
||||
sc.name = NULL;
|
||||
sc.argc = args->argc;
|
||||
sc.argv = args->argv;
|
||||
sc.environ = environ_create();
|
||||
|
||||
add = args_first_value(args, 'e', &value);
|
||||
while (add != NULL) {
|
||||
environ_put(sc.environ, add);
|
||||
add = args_next_value(&value);
|
||||
}
|
||||
|
||||
sc.idx = -1;
|
||||
sc.cwd = args_get(args, 'c');
|
||||
@ -146,5 +154,6 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
|
||||
cmd_find_from_winlink_pane(&fs, wl, new_wp, 0);
|
||||
cmdq_insert_hook(s, item, &fs, "after-split-window");
|
||||
|
||||
environ_free(sc.environ);
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
2
spawn.c
2
spawn.c
@ -293,6 +293,8 @@ spawn_pane(struct spawn_context *sc, char **cause)
|
||||
|
||||
/* Create an environment for this pane. */
|
||||
child = environ_for_session(s, 0);
|
||||
if (sc->environ != NULL)
|
||||
environ_copy(sc->environ, child);
|
||||
environ_set(child, "TMUX_PANE", "%%%u", new_wp->id);
|
||||
|
||||
/*
|
||||
|
28
tmux.1
28
tmux.1
@ -1784,6 +1784,7 @@ option.
|
||||
.It Xo Ic new-window
|
||||
.Op Fl adkP
|
||||
.Op Fl c Ar start-directory
|
||||
.Op Fl e Ar environment
|
||||
.Op Fl F Ar format
|
||||
.Op Fl n Ar window-name
|
||||
.Op Fl t Ar target-window
|
||||
@ -1823,6 +1824,12 @@ See the
|
||||
.Ic remain-on-exit
|
||||
option to change this behaviour.
|
||||
.Pp
|
||||
.Fl e
|
||||
takes the form
|
||||
.Ql VARIABLE=value
|
||||
and sets an environment variable for the newly created window; it may be
|
||||
specified multiple times.
|
||||
.Pp
|
||||
The
|
||||
.Ev TERM
|
||||
environment variable must be set to
|
||||
@ -1835,7 +1842,9 @@ for all programs running
|
||||
New windows will automatically have
|
||||
.Ql TERM=screen
|
||||
added to their environment, but care must be taken not to reset this in shell
|
||||
start-up files.
|
||||
start-up files or by the
|
||||
.Fl e
|
||||
option.
|
||||
.Pp
|
||||
The
|
||||
.Fl P
|
||||
@ -1994,8 +2003,9 @@ This command will automatically set
|
||||
.Ic window-size
|
||||
to manual in the window options.
|
||||
.It Xo Ic respawn-pane
|
||||
.Op Fl c Ar start-directory
|
||||
.Op Fl k
|
||||
.Op Fl c Ar start-directory
|
||||
.Op Fl e Ar environment
|
||||
.Op Fl t Ar target-pane
|
||||
.Op Ar shell-command
|
||||
.Xc
|
||||
@ -2011,9 +2021,15 @@ The pane must be already inactive, unless
|
||||
is given, in which case any existing command is killed.
|
||||
.Fl c
|
||||
specifies a new working directory for the pane.
|
||||
The
|
||||
.Fl e
|
||||
option has the same meaning as for the
|
||||
.Ic new-window
|
||||
command.
|
||||
.It Xo Ic respawn-window
|
||||
.Op Fl c Ar start-directory
|
||||
.Op Fl k
|
||||
.Op Fl c Ar start-directory
|
||||
.Op Fl e Ar environment
|
||||
.Op Fl t Ar target-window
|
||||
.Op Ar shell-command
|
||||
.Xc
|
||||
@ -2029,6 +2045,11 @@ The window must be already inactive, unless
|
||||
is given, in which case any existing command is killed.
|
||||
.Fl c
|
||||
specifies a new working directory for the window.
|
||||
The
|
||||
.Fl e
|
||||
option has the same meaning as for the
|
||||
.Ic new-window
|
||||
command.
|
||||
.It Xo Ic rotate-window
|
||||
.Op Fl DU
|
||||
.Op Fl t Ar target-window
|
||||
@ -2147,6 +2168,7 @@ the command behaves like
|
||||
.It Xo Ic split-window
|
||||
.Op Fl bdfhvP
|
||||
.Op Fl c Ar start-directory
|
||||
.Op Fl e Ar environment
|
||||
.Oo Fl l
|
||||
.Ar size |
|
||||
.Fl p Ar percentage Oc
|
||||
|
4
tmux.h
4
tmux.h
@ -37,6 +37,7 @@
|
||||
extern char **environ;
|
||||
|
||||
struct args;
|
||||
struct args_value;
|
||||
struct client;
|
||||
struct cmd_find_state;
|
||||
struct cmdq_item;
|
||||
@ -1578,6 +1579,7 @@ struct spawn_context {
|
||||
const char *name;
|
||||
char **argv;
|
||||
int argc;
|
||||
struct environ *environ;
|
||||
|
||||
int idx;
|
||||
const char *cwd;
|
||||
@ -1871,6 +1873,8 @@ void args_free(struct args *);
|
||||
char *args_print(struct args *);
|
||||
int args_has(struct args *, u_char);
|
||||
const char *args_get(struct args *, u_char);
|
||||
const char *args_first_value(struct args *, u_char, struct args_value **);
|
||||
const char *args_next_value(struct args_value **);
|
||||
long long args_strtonum(struct args *, u_char, long long, long long,
|
||||
char **);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user