summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicholas Marriott <nicm@cvs.openbsd.org>2019-05-27 12:16:28 +0000
committerNicholas Marriott <nicm@cvs.openbsd.org>2019-05-27 12:16:28 +0000
commite52990e8cd5c494c43baefe2263a7be6d2b64b35 (patch)
tree6287759ee64e717b3d5a06850b0a086729a35dd5
parente83d6e71d661bb60ab9e411023ef755ccf52217f (diff)
Add an additional {} syntax for defining strings in the configuration
file, making it much tidier to define commands that contain other tmux or shell commands (like if-shell). Also tweak bind-key to expect a string if it is only given one argument, so {} can be used with it as well. From Avi Halachmi.
-rw-r--r--usr.bin/tmux/arguments.c4
-rw-r--r--usr.bin/tmux/cmd-bind-key.c21
-rw-r--r--usr.bin/tmux/cmd-parse.y102
-rw-r--r--usr.bin/tmux/key-bindings.c14
-rw-r--r--usr.bin/tmux/tmux.149
5 files changed, 163 insertions, 27 deletions
diff --git a/usr.bin/tmux/arguments.c b/usr.bin/tmux/arguments.c
index 143089e28b8..e00ea617da4 100644
--- a/usr.bin/tmux/arguments.c
+++ b/usr.bin/tmux/arguments.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: arguments.c,v 1.22 2019/05/23 14:03:44 nicm Exp $ */
+/* $OpenBSD: arguments.c,v 1.23 2019/05/27 12:16:27 nicm Exp $ */
/*
* Copyright (c) 2010 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -207,7 +207,7 @@ args_print(struct args *args)
char *
args_escape(const char *s)
{
- static const char quoted[] = " #\"';$";
+ static const char quoted[] = " #\"';${}";
char *escaped, *result;
int flags;
diff --git a/usr.bin/tmux/cmd-bind-key.c b/usr.bin/tmux/cmd-bind-key.c
index 6fec275e2cc..e5c6ca2878b 100644
--- a/usr.bin/tmux/cmd-bind-key.c
+++ b/usr.bin/tmux/cmd-bind-key.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: cmd-bind-key.c,v 1.33 2019/05/25 07:18:20 nicm Exp $ */
+/* $OpenBSD: cmd-bind-key.c,v 1.34 2019/05/27 12:16:27 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -44,14 +44,16 @@ const struct cmd_entry cmd_bind_key_entry = {
static enum cmd_retval
cmd_bind_key_exec(struct cmd *self, struct cmdq_item *item)
{
- struct args *args = self->args;
- key_code key;
- const char *tablename;
- struct cmd_parse_result *pr;
+ struct args *args = self->args;
+ key_code key;
+ const char *tablename;
+ struct cmd_parse_result *pr;
+ char **argv = args->argv;
+ int argc = args->argc;
- key = key_string_lookup_string(args->argv[0]);
+ key = key_string_lookup_string(argv[0]);
if (key == KEYC_NONE || key == KEYC_UNKNOWN) {
- cmdq_error(item, "unknown key: %s", args->argv[0]);
+ cmdq_error(item, "unknown key: %s", argv[0]);
return (CMD_RETURN_ERROR);
}
@@ -62,7 +64,10 @@ cmd_bind_key_exec(struct cmd *self, struct cmdq_item *item)
else
tablename = "prefix";
- pr = cmd_parse_from_arguments(args->argc - 1, args->argv + 1, NULL);
+ if (argc == 2)
+ pr = cmd_parse_from_string(argv[1], NULL);
+ else
+ pr = cmd_parse_from_arguments(argc - 1, argv + 1, NULL);
switch (pr->status) {
case CMD_PARSE_EMPTY:
cmdq_error(item, "empty command");
diff --git a/usr.bin/tmux/cmd-parse.y b/usr.bin/tmux/cmd-parse.y
index 53c5a3f22ce..0180a0fe125 100644
--- a/usr.bin/tmux/cmd-parse.y
+++ b/usr.bin/tmux/cmd-parse.y
@@ -1,4 +1,4 @@
-/* $OpenBSD: cmd-parse.y,v 1.5 2019/05/26 10:08:50 nicm Exp $ */
+/* $OpenBSD: cmd-parse.y,v 1.6 2019/05/27 12:16:27 nicm Exp $ */
/*
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -1236,6 +1236,99 @@ yylex_token_tilde(char **buf, size_t *len)
return (1);
}
+static int
+yylex_token_brace(char **buf, size_t *len)
+{
+ struct cmd_parse_state *ps = &parse_state;
+ int ch, nesting = 1, escape = 0, quote = '\0';
+ int lines = 0;
+
+ /*
+ * Extract a string up to the matching unquoted '}', including newlines
+ * and handling nested braces.
+ *
+ * To detect the final and intermediate braces which affect the nesting
+ * depth, we scan the input as if it was a tmux config file, and ignore
+ * braces which would be considered quoted, escaped, or in a comment.
+ *
+ * The result is verbatim copy of the input excluding the final brace.
+ */
+
+ for (ch = yylex_getc1(); ch != EOF; ch = yylex_getc1()) {
+ yylex_append1(buf, len, ch);
+ if (ch == '\n')
+ lines++;
+
+ /*
+ * If the previous character was a backslash (escape is set),
+ * escape anything if unquoted or in double quotes, otherwise
+ * escape only '\n' and '\\'.
+ */
+ if (escape &&
+ (quote == '\0' ||
+ quote == '"' ||
+ ch == '\n' ||
+ ch == '\\')) {
+ escape = 0;
+ continue;
+ }
+
+ /*
+ * The character is not escaped. If it is a backslash, set the
+ * escape flag.
+ */
+ if (ch == '\\') {
+ escape = 1;
+ continue;
+ }
+ escape = 0;
+
+ /* A newline always resets to unquoted. */
+ if (ch == '\n') {
+ quote = 0;
+ continue;
+ }
+
+ if (quote) {
+ /*
+ * Inside quotes or comment. Check if this is the
+ * closing quote.
+ */
+ if (ch == quote && quote != '#')
+ quote = 0;
+ } else {
+ /* Not inside quotes or comment. */
+ switch (ch) {
+ case '"':
+ case '\'':
+ case '#':
+ /* Beginning of quote or comment. */
+ quote = ch;
+ break;
+ case '{':
+ nesting++;
+ break;
+ case '}':
+ nesting--;
+ if (nesting == 0) {
+ (*len)--; /* remove closing } */
+ ps->input->line += lines;
+ return (1);
+ }
+ break;
+ }
+ }
+ }
+
+ /*
+ * Update line count after error as reporting the opening line
+ * is more useful than EOF.
+ */
+ yyerror("unterminated brace string");
+ ps->input->line += lines;
+ return (0);
+}
+
static char *
yylex_token(int ch)
{
@@ -1282,6 +1375,13 @@ yylex_token(int ch)
goto error;
goto skip;
}
+ if (ch == '{' && state == NONE) {
+ if (!yylex_token_brace(&buf, &len))
+ goto error;
+ goto skip;
+ }
+ if (ch == '}' && state == NONE)
+ goto error; /* unmatched (matched ones were handled) */
/*
* ' and " starts or end quotes (and is consumed).
diff --git a/usr.bin/tmux/key-bindings.c b/usr.bin/tmux/key-bindings.c
index 71603654a68..de5fd64f481 100644
--- a/usr.bin/tmux/key-bindings.c
+++ b/usr.bin/tmux/key-bindings.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: key-bindings.c,v 1.93 2019/05/23 11:13:30 nicm Exp $ */
+/* $OpenBSD: key-bindings.c,v 1.94 2019/05/27 12:16:27 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -242,8 +242,8 @@ key_bindings_init(void)
"bind w choose-tree -Zw",
"bind x confirm-before -p\"kill-pane #P? (y/n)\" kill-pane",
"bind z resize-pane -Z",
- "bind { swap-pane -U",
- "bind } swap-pane -D",
+ "bind '{' swap-pane -U",
+ "bind '}' swap-pane -D",
"bind '~' show-messages",
"bind PPage copy-mode -u",
"bind -r Up select-pane -U",
@@ -347,8 +347,8 @@ key_bindings_init(void)
"bind -Tcopy-mode M-r send -X middle-line",
"bind -Tcopy-mode M-v send -X page-up",
"bind -Tcopy-mode M-w send -X copy-selection-and-cancel",
- "bind -Tcopy-mode M-{ send -X previous-paragraph",
- "bind -Tcopy-mode M-} send -X next-paragraph",
+ "bind -Tcopy-mode 'M-{' send -X previous-paragraph",
+ "bind -Tcopy-mode 'M-}' send -X next-paragraph",
"bind -Tcopy-mode M-Up send -X halfpage-up",
"bind -Tcopy-mode M-Down send -X halfpage-down",
"bind -Tcopy-mode C-Up send -X scroll-up",
@@ -413,8 +413,8 @@ key_bindings_init(void)
"bind -Tcopy-mode-vi t command-prompt -1p'(jump to forward)' 'send -X jump-to-forward \"%%%\"'",
"bind -Tcopy-mode-vi v send -X rectangle-toggle",
"bind -Tcopy-mode-vi w send -X next-word",
- "bind -Tcopy-mode-vi { send -X previous-paragraph",
- "bind -Tcopy-mode-vi } send -X next-paragraph",
+ "bind -Tcopy-mode-vi '{' send -X previous-paragraph",
+ "bind -Tcopy-mode-vi '}' send -X next-paragraph",
"bind -Tcopy-mode-vi % send -X next-matching-bracket",
"bind -Tcopy-mode-vi MouseDown1Pane select-pane",
"bind -Tcopy-mode-vi MouseDrag1Pane select-pane\\; send -X begin-selection",
diff --git a/usr.bin/tmux/tmux.1 b/usr.bin/tmux/tmux.1
index c37b6854a01..872d9a3bd92 100644
--- a/usr.bin/tmux/tmux.1
+++ b/usr.bin/tmux/tmux.1
@@ -1,4 +1,4 @@
-.\" $OpenBSD: tmux.1,v 1.654 2019/05/26 17:34:45 nicm Exp $
+.\" $OpenBSD: tmux.1,v 1.655 2019/05/27 12:16:27 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: May 26 2019 $
+.Dd $Mdocdate: May 27 2019 $
.Dt TMUX 1
.Os
.Sh NAME
@@ -490,11 +490,13 @@ line (the \e and the newline are completely removed).
This is called line continuation and applies both inside and outside quoted
strings and in comments.
.Pp
-Command arguments may be specified as strings surrounded by either single (')
-or double quotes (").
+Command arguments may be specified as strings surrounded by single (') quotes,
+double quotes (") or braces ({}).
.\" "
This is required when the argument contains any special character.
-Strings cannot span multiple lines except with line continuation.
+Single and double quoted strings cannot span multiple lines except with line
+continuation.
+Braces can span multiple lines.
.Pp
Outside of quotes and inside double quotes, these replacements are performed:
.Bl -dash -offset indent
@@ -520,6 +522,34 @@ is removed) and are not treated as having any special meaning - so for example
variable.
.El
.Pp
+Braces are similar to single quotes in that the text inside is taken literally
+without replacements, but they can span multiple lines.
+They are designed to avoid the need for additional escaping when passing a group
+of
+.Nm
+or shell commands as an argument (for example to
+.Ic if-shell
+or
+.Ic pipe-pane ) .
+These two examples produce an identical command - note that no escaping is
+needed when using {}:
+.Bd -literal -offset indent
+if-shell true {
+ display -p 'brace-dollar-foo: }$foo'
+}
+
+if-shell true "\en display -p 'brace-dollar-foo: }\e$foo'\en"
+.Ed
+.Pp
+Braces may be enclosed inside braces, for example:
+.Bd -literal -offset indent
+bind x if-shell "true" {
+ if-shell "true" {
+ display "true!"
+ }
+}
+.Ed
+.Pp
Environment variables may be set by using the syntax
.Ql name=value ,
for example
@@ -820,15 +850,16 @@ directly without invoking the shell.
.Op Ar arguments
refers to a
.Nm
-command, passed with the command and arguments separately, for example:
+command, either passed with the command and arguments separately, for example:
.Bd -literal -offset indent
bind-key F1 set-option status off
.Ed
.Pp
-Or if using
-.Xr sh 1 :
+Or passed as a single string argument in
+.Pa .tmux.conf ,
+for example:
.Bd -literal -offset indent
-$ tmux bind-key F1 set-option status off
+bind-key F1 { set-option status off }
.Ed
.Pp
Example