This commit was manufactured by cvs2svn to create tag 'TMUX_0_8'.
[tmux.git] / cmd-string.c
blob089f303ea89700bee2e5db722d8a460923f73595
1 /* $Id: cmd-string.c,v 1.13 2009-02-16 19:29:17 nicm Exp $ */
3 /*
4 * Copyright (c) 2008 Nicholas Marriott <nicm@users.sourceforge.net>
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 #include <sys/types.h>
21 #include <errno.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <stdlib.h>
26 #include "tmux.h"
29 * Parse a command from a string.
32 int cmd_string_getc(const char *, size_t *);
33 void cmd_string_ungetc(const char *, size_t *);
34 char *cmd_string_string(const char *, size_t *, char, int);
35 char *cmd_string_variable(const char *, size_t *);
37 int
38 cmd_string_getc(const char *s, size_t *p)
40 if (s[*p] == '\0')
41 return (EOF);
42 return (s[(*p)++]);
45 void
46 cmd_string_ungetc(unused const char *s, size_t *p)
48 (*p)--;
52 * Parse command string. Returns -1 on error. If returning -1, cause is error
53 * string, or NULL for empty command.
55 int
56 cmd_string_parse(const char *s, struct cmd_list **cmdlist, char **cause)
58 size_t p;
59 int ch, argc, rval, have_arg;
60 char **argv, *buf, *t, *u;
61 size_t len;
63 if ((t = strchr(s, ' ')) == NULL && (t = strchr(s, '\t')) == NULL)
64 t = strchr(s, '\0');
65 if ((u = strchr(s, '=')) != NULL && u < t) {
66 if (putenv(s) != 0) {
67 xasprintf(cause, "assignment failed: %s", s);
68 return (-1);
70 *cmdlist = NULL;
71 return (0);
74 argv = NULL;
75 argc = 0;
77 buf = NULL;
78 len = 0;
80 have_arg = 0;
82 *cause = NULL;
84 *cmdlist = NULL;
85 rval = -1;
87 p = 0;
88 for (;;) {
89 ch = cmd_string_getc(s, &p);
90 switch (ch) {
91 case '\'':
92 if ((t = cmd_string_string(s, &p, '\'', 0)) == NULL)
93 goto error;
94 buf = xrealloc(buf, 1, len + strlen(t) + 1);
95 strlcpy(buf + len, t, strlen(t) + 1);
96 len += strlen(t);
97 xfree(t);
99 have_arg = 1;
100 break;
101 case '"':
102 if ((t = cmd_string_string(s, &p, '"', 1)) == NULL)
103 goto error;
104 buf = xrealloc(buf, 1, len + strlen(t) + 1);
105 strlcpy(buf + len, t, strlen(t) + 1);
106 len += strlen(t);
107 xfree(t);
109 have_arg = 1;
110 break;
111 case '$':
112 if ((t = cmd_string_variable(s, &p)) == NULL)
113 goto error;
114 buf = xrealloc(buf, 1, len + strlen(t) + 1);
115 strlcpy(buf + len, t, strlen(t) + 1);
116 len += strlen(t);
118 have_arg = 1;
119 break;
120 case '#':
121 /* Comment: discard rest of line. */
122 while ((ch = cmd_string_getc(s, &p)) != EOF)
124 /* FALLTHROUGH */
125 case EOF:
126 case ' ':
127 case '\t':
128 if (have_arg) {
129 buf = xrealloc(buf, 1, len + 1);
130 buf[len] = '\0';
132 argv = xrealloc(argv, argc + 1, sizeof *argv);
133 argv[argc++] = buf;
135 buf = NULL;
136 len = 0;
138 have_arg = 0;
141 if (ch != EOF)
142 break;
143 if (argc == 0)
144 goto out;
146 *cmdlist = cmd_list_parse(argc, argv, cause);
147 if (*cmdlist == NULL)
148 goto out;
151 xfree(argv[argc - 1]);
152 while (--argc > 0);
154 rval = 0;
155 goto out;
156 default:
157 if (len >= SIZE_MAX - 2)
158 goto error;
160 buf = xrealloc(buf, 1, len + 1);
161 buf[len++] = ch;
163 have_arg = 1;
164 break;
168 error:
169 xasprintf(cause, "invalid or unknown command: %s", s);
171 out:
172 if (buf != NULL)
173 xfree(buf);
175 while (--argc >= 0)
176 xfree(argv[argc]);
177 if (argv != NULL)
178 xfree(argv);
180 return (rval);
183 char *
184 cmd_string_string(const char *s, size_t *p, char endch, int esc)
186 int ch;
187 char *buf, *t;
188 size_t len;
190 buf = NULL;
191 len = 0;
193 while ((ch = cmd_string_getc(s, p)) != endch) {
194 switch (ch) {
195 case EOF:
196 goto error;
197 case '\\':
198 if (!esc)
199 break;
200 switch (ch = cmd_string_getc(s, p)) {
201 case EOF:
202 goto error;
203 case 'r':
204 ch = '\r';
205 break;
206 case 'n':
207 ch = '\n';
208 break;
209 case 't':
210 ch = '\t';
211 break;
213 break;
214 case '$':
215 if (!esc)
216 break;
217 if ((t = cmd_string_variable(s, p)) == NULL)
218 goto error;
219 buf = xrealloc(buf, 1, len + strlen(t) + 1);
220 strlcpy(buf + len, t, strlen(t) + 1);
221 len += strlen(t);
222 continue;
225 if (len >= SIZE_MAX - 2)
226 goto error;
227 buf = xrealloc(buf, 1, len + 1);
228 buf[len++] = ch;
231 buf = xrealloc(buf, 1, len + 1);
232 buf[len] = '\0';
233 return (buf);
235 error:
236 if (buf != NULL)
237 xfree(buf);
238 return (NULL);
241 char *
242 cmd_string_variable(const char *s, size_t *p)
244 int ch, fch;
245 char *buf, *t;
246 size_t len;
248 #define cmd_string_first(ch) ((ch) == '_' || \
249 ((ch) >= 'a' && (ch) <= 'z') || ((ch) >= 'A' && (ch) <= 'Z'))
250 #define cmd_string_other(ch) ((ch) == '_' || \
251 ((ch) >= 'a' && (ch) <= 'z') || ((ch) >= 'A' && (ch) <= 'Z') || \
252 ((ch) >= '0' && (ch) <= '9'))
254 buf = NULL;
255 len = 0;
257 fch = EOF;
258 switch (ch = cmd_string_getc(s, p)) {
259 case EOF:
260 goto error;
261 case '{':
262 fch = '{';
264 ch = cmd_string_getc(s, p);
265 if (!cmd_string_first(ch))
266 goto error;
267 /* FALLTHROUGH */
268 default:
269 if (!cmd_string_first(ch)) {
270 xasprintf(&t, "$%c", ch);
271 return (t);
274 buf = xrealloc(buf, 1, len + 1);
275 buf[len++] = ch;
277 for(;;) {
278 ch = cmd_string_getc(s, p);
279 if (ch == EOF || !cmd_string_other(ch))
280 break;
281 else {
282 if (len >= SIZE_MAX - 3)
283 goto error;
284 buf = xrealloc(buf, 1, len + 1);
285 buf[len++] = ch;
290 if (fch == '{' && ch != '}')
291 goto error;
292 if (ch != EOF && fch != '{')
293 cmd_string_ungetc(s, p); /* ch */
295 buf = xrealloc(buf, 1, len + 1);
296 buf[len] = '\0';
298 if ((t = getenv(buf)) == NULL) {
299 xfree(buf);
300 return (xstrdup(""));
302 xfree(buf);
303 return (xstrdup(t));
305 error:
306 if (buf != NULL)
307 xfree(buf);
308 return (NULL);