diff options
author | nicm <nicm> | 2019-05-27 12:16:27 +0000 |
---|---|---|
committer | nicm <nicm> | 2019-05-27 12:16:27 +0000 |
commit | 6b332127cae97914d34c39575881fbc87205f4e0 (patch) | |
tree | e451d78a0ec378c4b58b3a401a88532c27a25ae0 /cmd-parse.y | |
parent | 65e5e1456179d68f36602a5976184b38cc4b636c (diff) | |
download | rtmux-6b332127cae97914d34c39575881fbc87205f4e0.tar.gz rtmux-6b332127cae97914d34c39575881fbc87205f4e0.tar.bz2 rtmux-6b332127cae97914d34c39575881fbc87205f4e0.zip |
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.
Diffstat (limited to 'cmd-parse.y')
-rw-r--r-- | cmd-parse.y | 100 |
1 files changed, 100 insertions, 0 deletions
diff --git a/cmd-parse.y b/cmd-parse.y index 43ec8a94..f347280b 100644 --- a/cmd-parse.y +++ b/cmd-parse.y @@ -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). |