| /* $Id: cmd-string.c,v 1.3 2008-06-19 21:20:27 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 "tmux.h" |
| |
| /* |
| * Parse a command from a string. |
| */ |
| |
| int cmd_string_getc(const char *, size_t *); |
| char *cmd_string_string(const char *, size_t *, char, int); |
| |
| int |
| cmd_string_getc(const char *s, size_t *p) |
| { |
| if (s[*p] == '\0') |
| return (EOF); |
| return (s[(*p)++]); |
| } |
| |
| /* |
| * Parse command string. Return command or NULL on error. If returning NULL, |
| * cause is error string, or NULL for empty command. |
| */ |
| struct cmd * |
| cmd_string_parse(const char *s, char **cause) |
| { |
| size_t p; |
| int ch, argc; |
| char **argv, *buf, *t; |
| size_t len; |
| struct cmd *cmd; |
| |
| argv = NULL; |
| argc = 0; |
| |
| buf = NULL; |
| len = 0; |
| |
| cmd = NULL; |
| |
| *cause = NULL; |
| |
| p = 0; |
| for (;;) { |
| ch = cmd_string_getc(s, &p); |
| switch (ch) { |
| case '\'': |
| if ((t = cmd_string_string(s, &p, '\'', 0)) == NULL) |
| goto error; |
| argv = xrealloc(argv, argc + 1, sizeof *argv); |
| argv[argc++] = t; |
| break; |
| case '"': |
| if ((t = cmd_string_string(s, &p, '"', 1)) == NULL) |
| goto error; |
| argv = xrealloc(argv, argc + 1, sizeof *argv); |
| argv[argc++] = 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); |
| 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 (cmd); |
| } |
| |
| char * |
| cmd_string_string(const char *s, size_t *p, char endch, int esc) |
| { |
| int ch; |
| char *buf; |
| 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; |
| } |
| |
| 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); |
| } |