diff options
Diffstat (limited to 'usr.bin/tmux/cmd-string.c')
-rw-r--r-- | usr.bin/tmux/cmd-string.c | 309 |
1 files changed, 309 insertions, 0 deletions
diff --git a/usr.bin/tmux/cmd-string.c b/usr.bin/tmux/cmd-string.c new file mode 100644 index 00000000000..d151a397bd8 --- /dev/null +++ b/usr.bin/tmux/cmd-string.c @@ -0,0 +1,309 @@ +/* $OpenBSD: cmd-string.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */ + +/* + * Copyright (c) 2008 Nicholas Marriott <nicm@users.sourceforge.net> + * + * 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 <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include "tmux.h" + +/* + * Parse a command from a string. + */ + +int cmd_string_getc(const char *, size_t *); +void cmd_string_ungetc(const char *, size_t *); +char *cmd_string_string(const char *, size_t *, char, int); +char *cmd_string_variable(const char *, size_t *); + +int +cmd_string_getc(const char *s, size_t *p) +{ + if (s[*p] == '\0') + return (EOF); + return (s[(*p)++]); +} + +void +cmd_string_ungetc(unused const char *s, size_t *p) +{ + (*p)--; +} + +/* + * Parse command string. Returns -1 on error. If returning -1, cause is error + * string, or NULL for empty command. + */ +int +cmd_string_parse(const char *s, struct cmd_list **cmdlist, char **cause) +{ + size_t p; + int ch, argc, rval, have_arg; + char **argv, *buf, *t, *u; + size_t len; + + if ((t = strchr(s, ' ')) == NULL && (t = strchr(s, '\t')) == NULL) + t = strchr(s, '\0'); + if ((u = strchr(s, '=')) != NULL && u < t) { + if (putenv((char *) s) != 0) { + xasprintf(cause, "assignment failed: %s", s); + return (-1); + } + *cmdlist = NULL; + return (0); + } + + argv = NULL; + argc = 0; + + buf = NULL; + len = 0; + + have_arg = 0; + + *cause = NULL; + + *cmdlist = NULL; + rval = -1; + + p = 0; + for (;;) { + ch = cmd_string_getc(s, &p); + switch (ch) { + case '\'': + if ((t = cmd_string_string(s, &p, '\'', 0)) == NULL) + goto error; + buf = xrealloc(buf, 1, len + strlen(t) + 1); + strlcpy(buf + len, t, strlen(t) + 1); + len += strlen(t); + xfree(t); + + have_arg = 1; + break; + case '"': + if ((t = cmd_string_string(s, &p, '"', 1)) == NULL) + goto error; + buf = xrealloc(buf, 1, len + strlen(t) + 1); + strlcpy(buf + len, t, strlen(t) + 1); + len += strlen(t); + xfree(t); + + have_arg = 1; + break; + case '$': + if ((t = cmd_string_variable(s, &p)) == NULL) + goto error; + buf = xrealloc(buf, 1, len + strlen(t) + 1); + strlcpy(buf + len, t, strlen(t) + 1); + len += strlen(t); + + have_arg = 1; + break; + case '#': + /* Comment: discard rest of line. */ + while ((ch = cmd_string_getc(s, &p)) != EOF) + ; + /* FALLTHROUGH */ + case EOF: + case ' ': + case '\t': + if (have_arg) { + buf = xrealloc(buf, 1, len + 1); + buf[len] = '\0'; + + argv = xrealloc(argv, argc + 1, sizeof *argv); + argv[argc++] = buf; + + buf = NULL; + len = 0; + + have_arg = 0; + } + + if (ch != EOF) + break; + if (argc == 0) + goto out; + + *cmdlist = cmd_list_parse(argc, argv, cause); + if (*cmdlist == NULL) + goto out; + + do + xfree(argv[argc - 1]); + while (--argc > 0); + + rval = 0; + goto out; + default: + if (len >= SIZE_MAX - 2) + goto error; + + buf = xrealloc(buf, 1, len + 1); + buf[len++] = ch; + + have_arg = 1; + break; + } + } + +error: + xasprintf(cause, "invalid or unknown command: %s", s); + +out: + if (buf != NULL) + xfree(buf); + + while (--argc >= 0) + xfree(argv[argc]); + if (argv != NULL) + xfree(argv); + + return (rval); +} + +char * +cmd_string_string(const char *s, size_t *p, char endch, int esc) +{ + int ch; + char *buf, *t; + size_t len; + + buf = NULL; + 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 'r': + ch = '\r'; + break; + case 'n': + ch = '\n'; + break; + case 't': + ch = '\t'; + break; + } + break; + case '$': + if (!esc) + break; + if ((t = cmd_string_variable(s, p)) == NULL) + goto error; + buf = xrealloc(buf, 1, len + strlen(t) + 1); + strlcpy(buf + len, t, strlen(t) + 1); + len += strlen(t); + continue; + } + + if (len >= SIZE_MAX - 2) + goto error; + buf = xrealloc(buf, 1, len + 1); + buf[len++] = ch; + } + + buf = xrealloc(buf, 1, len + 1); + buf[len] = '\0'; + return (buf); + +error: + if (buf != NULL) + xfree(buf); + return (NULL); +} + +char * +cmd_string_variable(const char *s, size_t *p) +{ + int ch, fch; + char *buf, *t; + size_t len; + +#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, 1, 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, 1, len + 1); + buf[len++] = ch; + } + } + } + + if (fch == '{' && ch != '}') + goto error; + if (ch != EOF && fch != '{') + cmd_string_ungetc(s, p); /* ch */ + + buf = xrealloc(buf, 1, len + 1); + buf[len] = '\0'; + + if ((t = getenv(buf)) == NULL) { + xfree(buf); + return (xstrdup("")); + } + xfree(buf); + return (xstrdup(t)); + +error: + if (buf != NULL) + xfree(buf); + return (NULL); +} |