| /* $Id: cmd-string.c,v 1.6 2008-08-01 20:25:13 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 **cmd, char **cause) |
| { |
| size_t p; |
| int ch, argc, rval; |
| 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(s) != 0) { |
| xasprintf(cause, "assignment failed: %s", s); |
| return (-1); |
| } |
| *cmd = NULL; |
| return (0); |
| } |
| |
| argv = NULL; |
| argc = 0; |
| |
| buf = NULL; |
| len = 0; |
| |
| *cause = NULL; |
| |
| *cmd = 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); |
| 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); |
| 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); |
| break; |
| case '#': |
| /* Comment: discard rest of line. */ |
| while ((ch = cmd_string_getc(s, &p)) != EOF) |
| ; |
| /* FALLTHROUGH */ |
| case EOF: |
| case ' ': |
| case '\t': |
| if (len != 0) { |
| buf = xrealloc(buf, 1, len + 1); |
| buf[len] = '\0'; |
| |
| argv = xrealloc(argv, argc + 1, sizeof *argv); |
| argv[argc++] = buf; |
| |
| buf = NULL; |
| len = 0; |
| } |
| |
| if (ch != EOF) |
| break; |
| if (argc == 0) |
| goto out; |
| |
| *cmd = cmd_parse(argc, argv, cause); |
| rval = 0; |
| goto out; |
| default: |
| if (len >= SIZE_MAX - 2) |
| goto error; |
| |
| buf = xrealloc(buf, 1, len + 1); |
| buf[len++] = ch; |
| break; |
| } |
| } |
| |
| error: |
| xasprintf(cause, "bad 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); |
| } |