aboutsummaryrefslogtreecommitdiff
path: root/cmd-string.c
diff options
context:
space:
mode:
authornicm <nicm>2019-05-23 11:13:30 +0000
committernicm <nicm>2019-05-23 11:13:30 +0000
commit723010ba72e337832402f8e44981c02caa30b476 (patch)
treea7fa1089f4d5dc368a44702489cf176afbcfec55 /cmd-string.c
parent5571d7a21c5dc920eb9d25336b5bb7b04d72fc9d (diff)
downloadrtmux-723010ba72e337832402f8e44981c02caa30b476.tar.gz
rtmux-723010ba72e337832402f8e44981c02caa30b476.tar.bz2
rtmux-723010ba72e337832402f8e44981c02caa30b476.zip
Replace the split parser code (cfg.c and cmd-string.c) with a single
parser using yacc(1). This is a major change but is clearer and simpler and allows some edge cases to be made more consistent, as well as tidying up how aliases are handled. It will also allow some further improvements later. Entirely the same parser is now used for parsing the configuration file and for string commands. This means that constructs previously only available in .tmux.conf, such as %if, can now be used in string commands (for example, those given to if-shell - not commands invoked from the shell, they are still parsed by the shell itself). The only syntax change I am aware of is that #{} outside quotes or a comment is now considered a format and not a comment, so #{ is now a syntax error (notably, if it is at the start of a line). This also adds two new sections to the man page documenting the syntax and outlining how parsing and command execution works. Thanks to everyone who sent me test configs (they still all parse without errors - but this doesn't mean they still work as intended!). Thanks to Avi Halachmi for testing and man page improvements, also to jmc@ for reviewing the man page changes.
Diffstat (limited to 'cmd-string.c')
-rw-r--r--cmd-string.c393
1 files changed, 0 insertions, 393 deletions
diff --git a/cmd-string.c b/cmd-string.c
deleted file mode 100644
index 058f997c..00000000
--- a/cmd-string.c
+++ /dev/null
@@ -1,393 +0,0 @@
-/* $OpenBSD$ */
-
-/*
- * Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
- * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
- * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <sys/types.h>
-
-#include <errno.h>
-#include <pwd.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <unistd.h>
-
-#include "tmux.h"
-
-/*
- * Parse a command from a string.
- */
-
-static int cmd_string_getc(const char *, size_t *);
-static void cmd_string_ungetc(size_t *);
-static void cmd_string_copy(char **, char *, size_t *);
-static char *cmd_string_string(const char *, size_t *, char, int);
-static char *cmd_string_variable(const char *, size_t *);
-static char *cmd_string_expand_tilde(const char *, size_t *);
-
-static int
-cmd_string_getc(const char *s, size_t *p)
-{
- const u_char *ucs = s;
-
- if (ucs[*p] == '\0')
- return (EOF);
- return (ucs[(*p)++]);
-}
-
-static void
-cmd_string_ungetc(size_t *p)
-{
- (*p)--;
-}
-
-static int
-cmd_string_unicode(wchar_t *wc, const char *s, size_t *p, char ch)
-{
- int size = (ch == 'u') ? 4 : 8;
- u_int tmp;
-
- if (size == 4 && sscanf(s + *p, "%4x", &tmp) != 1)
- return (-1);
- if (size == 8 && sscanf(s + *p, "%8x", &tmp) != 1)
- return (-1);
- *p += size;
-
- *wc = (wchar_t)tmp;
- return (0);
-}
-
-int
-cmd_string_split(const char *s, int *rargc, char ***rargv)
-{
- size_t p = 0;
- int ch, argc = 0, append = 0;
- char **argv = NULL, *buf = NULL, *t;
- const char *whitespace, *equals;
- size_t len = 0;
-
- for (;;) {
- ch = cmd_string_getc(s, &p);
- switch (ch) {
- case '\'':
- if ((t = cmd_string_string(s, &p, '\'', 0)) == NULL)
- goto error;
- cmd_string_copy(&buf, t, &len);
- break;
- case '"':
- if ((t = cmd_string_string(s, &p, '"', 1)) == NULL)
- goto error;
- cmd_string_copy(&buf, t, &len);
- break;
- case '$':
- if ((t = cmd_string_variable(s, &p)) == NULL)
- goto error;
- cmd_string_copy(&buf, t, &len);
- break;
- case '#':
- /* Comment: discard rest of line. */
- while ((ch = cmd_string_getc(s, &p)) != EOF)
- ;
- /* FALLTHROUGH */
- case EOF:
- case ' ':
- case '\t':
- if (buf != NULL) {
- buf = xrealloc(buf, len + 1);
- buf[len] = '\0';
-
- argv = xreallocarray(argv, argc + 1,
- sizeof *argv);
- argv[argc++] = buf;
-
- buf = NULL;
- len = 0;
- }
-
- if (ch != EOF)
- break;
-
- while (argc != 0) {
- equals = strchr(argv[0], '=');
- whitespace = argv[0] + strcspn(argv[0], " \t");
- if (equals == NULL || equals > whitespace)
- break;
- environ_put(global_environ, argv[0]);
- argc--;
- memmove(argv, argv + 1, argc * (sizeof *argv));
- }
- goto done;
- case '~':
- if (buf != NULL) {
- append = 1;
- break;
- }
- t = cmd_string_expand_tilde(s, &p);
- if (t == NULL)
- goto error;
- cmd_string_copy(&buf, t, &len);
- break;
- default:
- append = 1;
- break;
- }
- if (append) {
- if (len >= SIZE_MAX - 2)
- goto error;
- buf = xrealloc(buf, len + 1);
- buf[len++] = ch;
- }
- append = 0;
- }
-
-done:
- *rargc = argc;
- *rargv = argv;
-
- free(buf);
- return (0);
-
-error:
- if (argv != NULL)
- cmd_free_argv(argc, argv);
- free(buf);
- return (-1);
-}
-
-struct cmd_list *
-cmd_string_parse(const char *s, const char *file, u_int line, char **cause)
-{
- struct cmd_list *cmdlist = NULL;
- int argc;
- char **argv;
-
- if (cause != NULL)
- *cause = NULL;
- log_debug ("%s: %s", __func__, s);
-
- if (cmd_string_split(s, &argc, &argv) != 0) {
- xasprintf(cause, "invalid or unknown command: %s", s);
- return (NULL);
- }
- if (argc != 0) {
- cmdlist = cmd_list_parse(argc, argv, file, line, cause);
- if (cmdlist == NULL) {
- cmd_free_argv(argc, argv);
- return (NULL);
- }
- }
- cmd_free_argv(argc, argv);
- return (cmdlist);
-}
-
-static void
-cmd_string_copy(char **dst, char *src, size_t *len)
-{
- size_t srclen;
-
- srclen = strlen(src);
-
- *dst = xrealloc(*dst, *len + srclen + 1);
- strlcpy(*dst + *len, src, srclen + 1);
-
- *len += srclen;
- free(src);
-}
-
-static char *
-cmd_string_string(const char *s, size_t *p, char endch, int esc)
-{
- int ch;
- wchar_t wc;
- struct utf8_data ud;
- char *buf = NULL, *t;
- size_t len = 0;
-
- while ((ch = cmd_string_getc(s, p)) != endch) {
- switch (ch) {
- case EOF:
- goto error;
- case '\\':
- if (!esc)
- break;
- switch (ch = cmd_string_getc(s, p)) {
- case EOF:
- goto error;
- case 'e':
- ch = '\033';
- break;
- case 'r':
- ch = '\r';
- break;
- case 'n':
- ch = '\n';
- break;
- case 't':
- ch = '\t';
- break;
- case 'u':
- case 'U':
- if (cmd_string_unicode(&wc, s, p, ch) != 0)
- goto error;
- if (utf8_split(wc, &ud) != UTF8_DONE)
- goto error;
- if (len >= SIZE_MAX - ud.size - 1)
- goto error;
- buf = xrealloc(buf, len + ud.size);
- memcpy(buf + len, ud.data, ud.size);
- len += ud.size;
- continue;
- }
- break;
- case '$':
- if (!esc)
- break;
- if ((t = cmd_string_variable(s, p)) == NULL)
- goto error;
- cmd_string_copy(&buf, t, &len);
- continue;
- }
-
- if (len >= SIZE_MAX - 2)
- goto error;
- buf = xrealloc(buf, len + 1);
- buf[len++] = ch;
- }
-
- buf = xrealloc(buf, len + 1);
- buf[len] = '\0';
- return (buf);
-
-error:
- free(buf);
- return (NULL);
-}
-
-static char *
-cmd_string_variable(const char *s, size_t *p)
-{
- int ch, fch;
- char *buf, *t;
- size_t len;
- struct environ_entry *envent;
-
-#define cmd_string_first(ch) ((ch) == '_' || \
- ((ch) >= 'a' && (ch) <= 'z') || ((ch) >= 'A' && (ch) <= 'Z'))
-#define cmd_string_other(ch) ((ch) == '_' || \
- ((ch) >= 'a' && (ch) <= 'z') || ((ch) >= 'A' && (ch) <= 'Z') || \
- ((ch) >= '0' && (ch) <= '9'))
-
- buf = NULL;
- len = 0;
-
- fch = EOF;
- switch (ch = cmd_string_getc(s, p)) {
- case EOF:
- goto error;
- case '{':
- fch = '{';
-
- ch = cmd_string_getc(s, p);
- if (!cmd_string_first(ch))
- goto error;
- /* FALLTHROUGH */
- default:
- if (!cmd_string_first(ch)) {
- xasprintf(&t, "$%c", ch);
- return (t);
- }
-
- buf = xrealloc(buf, len + 1);
- buf[len++] = ch;
-
- for (;;) {
- ch = cmd_string_getc(s, p);
- if (ch == EOF || !cmd_string_other(ch))
- break;
- else {
- if (len >= SIZE_MAX - 3)
- goto error;
- buf = xrealloc(buf, len + 1);
- buf[len++] = ch;
- }
- }
- }
-
- if (fch == '{' && ch != '}')
- goto error;
- if (ch != EOF && fch != '{')
- cmd_string_ungetc(p); /* ch */
-
- buf = xrealloc(buf, len + 1);
- buf[len] = '\0';
-
- envent = environ_find(global_environ, buf);
- free(buf);
- if (envent == NULL)
- return (xstrdup(""));
- return (xstrdup(envent->value));
-
-error:
- free(buf);
- return (NULL);
-}
-
-static char *
-cmd_string_expand_tilde(const char *s, size_t *p)
-{
- struct passwd *pw;
- struct environ_entry *envent;
- char *home, *path, *user, *cp;
- int last;
-
- home = NULL;
-
- last = cmd_string_getc(s, p);
- if (last == EOF || last == '/' || last == ' '|| last == '\t') {
- envent = environ_find(global_environ, "HOME");
- if (envent != NULL && *envent->value != '\0')
- home = envent->value;
- else if ((pw = getpwuid(getuid())) != NULL)
- home = pw->pw_dir;
- } else {
- cmd_string_ungetc(p);
-
- cp = user = xmalloc(strlen(s));
- for (;;) {
- last = cmd_string_getc(s, p);
- if (last == EOF ||
- last == '/' ||
- last == ' '||
- last == '\t')
- break;
- *cp++ = last;
- }
- *cp = '\0';
-
- if ((pw = getpwnam(user)) != NULL)
- home = pw->pw_dir;
- free(user);
- }
-
- if (home == NULL)
- return (NULL);
-
- if (last != EOF)
- xasprintf(&path, "%s%c", home, last);
- else
- xasprintf(&path, "%s", home);
- return (path);
-}