diff options
author | Nicholas Marriott <nicm@cvs.openbsd.org> | 2019-05-27 12:16:28 +0000 |
---|---|---|
committer | Nicholas Marriott <nicm@cvs.openbsd.org> | 2019-05-27 12:16:28 +0000 |
commit | e52990e8cd5c494c43baefe2263a7be6d2b64b35 (patch) | |
tree | 6287759ee64e717b3d5a06850b0a086729a35dd5 | |
parent | e83d6e71d661bb60ab9e411023ef755ccf52217f (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.c | 4 | ||||
-rw-r--r-- | usr.bin/tmux/cmd-bind-key.c | 21 | ||||
-rw-r--r-- | usr.bin/tmux/cmd-parse.y | 102 | ||||
-rw-r--r-- | usr.bin/tmux/key-bindings.c | 14 | ||||
-rw-r--r-- | usr.bin/tmux/tmux.1 | 49 |
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 |