Annotation of src/usr.bin/tmux/cmd-string.c, Revision 1.11
1.11 ! nicm 1: /* $OpenBSD: cmd-string.c,v 1.10 2009/11/21 17:52:18 nicm Exp $ */
1.1 nicm 2:
3: /*
4: * Copyright (c) 2008 Nicholas Marriott <nicm@users.sourceforge.net>
5: *
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.
9: *
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.
17: */
18:
19: #include <sys/types.h>
20:
21: #include <errno.h>
1.4 nicm 22: #include <pwd.h>
1.1 nicm 23: #include <stdio.h>
24: #include <string.h>
25: #include <stdlib.h>
1.4 nicm 26: #include <unistd.h>
1.1 nicm 27:
28: #include "tmux.h"
29:
30: /*
31: * Parse a command from a string.
32: */
33:
34: int cmd_string_getc(const char *, size_t *);
1.11 ! nicm 35: void cmd_string_ungetc(size_t *);
1.1 nicm 36: char *cmd_string_string(const char *, size_t *, char, int);
37: char *cmd_string_variable(const char *, size_t *);
1.4 nicm 38: char *cmd_string_expand_tilde(const char *, size_t *);
1.1 nicm 39:
40: int
41: cmd_string_getc(const char *s, size_t *p)
42: {
1.11 ! nicm 43: const u_char *ucs = s;
! 44:
! 45: if (ucs[*p] == '\0')
1.1 nicm 46: return (EOF);
1.11 ! nicm 47: return (ucs[(*p)++]);
1.1 nicm 48: }
49:
50: void
1.11 ! nicm 51: cmd_string_ungetc(size_t *p)
1.1 nicm 52: {
53: (*p)--;
54: }
55:
56: /*
57: * Parse command string. Returns -1 on error. If returning -1, cause is error
58: * string, or NULL for empty command.
59: */
60: int
61: cmd_string_parse(const char *s, struct cmd_list **cmdlist, char **cause)
62: {
63: size_t p;
1.6 nicm 64: int ch, i, argc, rval, have_arg;
65: char **argv, *buf, *t;
66: const char *whitespace, *equals;
1.1 nicm 67: size_t len;
68:
69: argv = NULL;
70: argc = 0;
71:
72: buf = NULL;
73: len = 0;
74:
75: have_arg = 0;
76:
77: *cause = NULL;
78:
79: *cmdlist = NULL;
80: rval = -1;
81:
82: p = 0;
83: for (;;) {
84: ch = cmd_string_getc(s, &p);
85: switch (ch) {
86: case '\'':
87: if ((t = cmd_string_string(s, &p, '\'', 0)) == NULL)
88: goto error;
89: buf = xrealloc(buf, 1, len + strlen(t) + 1);
90: strlcpy(buf + len, t, strlen(t) + 1);
91: len += strlen(t);
92: xfree(t);
93:
94: have_arg = 1;
95: break;
96: case '"':
97: if ((t = cmd_string_string(s, &p, '"', 1)) == NULL)
98: goto error;
99: buf = xrealloc(buf, 1, len + strlen(t) + 1);
100: strlcpy(buf + len, t, strlen(t) + 1);
101: len += strlen(t);
102: xfree(t);
103:
104: have_arg = 1;
105: break;
106: case '$':
107: if ((t = cmd_string_variable(s, &p)) == NULL)
108: goto error;
109: buf = xrealloc(buf, 1, len + strlen(t) + 1);
110: strlcpy(buf + len, t, strlen(t) + 1);
111: len += strlen(t);
1.3 nicm 112: xfree(t);
1.1 nicm 113:
114: have_arg = 1;
115: break;
116: case '#':
117: /* Comment: discard rest of line. */
118: while ((ch = cmd_string_getc(s, &p)) != EOF)
119: ;
120: /* FALLTHROUGH */
121: case EOF:
122: case ' ':
123: case '\t':
124: if (have_arg) {
125: buf = xrealloc(buf, 1, len + 1);
126: buf[len] = '\0';
127:
128: argv = xrealloc(argv, argc + 1, sizeof *argv);
129: argv[argc++] = buf;
130:
131: buf = NULL;
132: len = 0;
133:
134: have_arg = 0;
135: }
136:
137: if (ch != EOF)
138: break;
1.6 nicm 139:
1.8 nicm 140: while (argc != 0) {
141: equals = strchr(argv[0], '=');
142: whitespace = argv[0] + strcspn(argv[0], " \t");
1.6 nicm 143: if (equals == NULL || equals > whitespace)
144: break;
1.8 nicm 145: environ_put(&global_environ, argv[0]);
1.6 nicm 146: argc--;
1.8 nicm 147: memmove(argv, argv + 1, argc * (sizeof *argv));
1.6 nicm 148: }
1.1 nicm 149: if (argc == 0)
150: goto out;
151:
152: *cmdlist = cmd_list_parse(argc, argv, cause);
153: if (*cmdlist == NULL)
154: goto out;
155:
156: rval = 0;
157: goto out;
1.4 nicm 158: case '~':
159: if (have_arg == 0) {
160: if ((t = cmd_string_expand_tilde(s, &p)) == NULL)
161: goto error;
162: buf = xrealloc(buf, 1, len + strlen(t) + 1);
163: strlcpy(buf + len, t, strlen(t) + 1);
164: len += strlen(t);
165: xfree(t);
166: break;
167: }
168: /* FALLTHROUGH */
1.1 nicm 169: default:
170: if (len >= SIZE_MAX - 2)
171: goto error;
172:
173: buf = xrealloc(buf, 1, len + 1);
174: buf[len++] = ch;
175:
176: have_arg = 1;
177: break;
178: }
179: }
180:
181: error:
182: xasprintf(cause, "invalid or unknown command: %s", s);
183:
184: out:
185: if (buf != NULL)
186: xfree(buf);
187:
1.8 nicm 188: if (argv != NULL) {
189: for (i = 0; i < argc; i++)
1.9 nicm 190: xfree(argv[i]);
1.1 nicm 191: xfree(argv);
1.8 nicm 192: }
1.1 nicm 193:
194: return (rval);
195: }
196:
197: char *
198: cmd_string_string(const char *s, size_t *p, char endch, int esc)
199: {
200: int ch;
201: char *buf, *t;
202: size_t len;
203:
1.7 deraadt 204: buf = NULL;
1.1 nicm 205: len = 0;
206:
1.7 deraadt 207: while ((ch = cmd_string_getc(s, p)) != endch) {
208: switch (ch) {
1.1 nicm 209: case EOF:
210: goto error;
1.7 deraadt 211: case '\\':
1.1 nicm 212: if (!esc)
213: break;
1.7 deraadt 214: switch (ch = cmd_string_getc(s, p)) {
1.1 nicm 215: case EOF:
216: goto error;
1.5 nicm 217: case 'e':
218: ch = '\033';
219: break;
1.7 deraadt 220: case 'r':
221: ch = '\r';
222: break;
223: case 'n':
224: ch = '\n';
225: break;
226: case 't':
227: ch = '\t';
228: break;
229: }
230: break;
1.1 nicm 231: case '$':
232: if (!esc)
233: break;
234: if ((t = cmd_string_variable(s, p)) == NULL)
235: goto error;
236: buf = xrealloc(buf, 1, len + strlen(t) + 1);
237: strlcpy(buf + len, t, strlen(t) + 1);
238: len += strlen(t);
1.3 nicm 239: xfree(t);
1.1 nicm 240: continue;
1.7 deraadt 241: }
1.1 nicm 242:
243: if (len >= SIZE_MAX - 2)
244: goto error;
245: buf = xrealloc(buf, 1, len + 1);
1.7 deraadt 246: buf[len++] = ch;
247: }
1.1 nicm 248:
249: buf = xrealloc(buf, 1, len + 1);
250: buf[len] = '\0';
251: return (buf);
252:
253: error:
254: if (buf != NULL)
255: xfree(buf);
256: return (NULL);
257: }
258:
259: char *
260: cmd_string_variable(const char *s, size_t *p)
261: {
262: int ch, fch;
263: char *buf, *t;
264: size_t len;
265:
266: #define cmd_string_first(ch) ((ch) == '_' || \
267: ((ch) >= 'a' && (ch) <= 'z') || ((ch) >= 'A' && (ch) <= 'Z'))
268: #define cmd_string_other(ch) ((ch) == '_' || \
269: ((ch) >= 'a' && (ch) <= 'z') || ((ch) >= 'A' && (ch) <= 'Z') || \
270: ((ch) >= '0' && (ch) <= '9'))
271:
1.7 deraadt 272: buf = NULL;
1.1 nicm 273: len = 0;
274:
275: fch = EOF;
276: switch (ch = cmd_string_getc(s, p)) {
277: case EOF:
278: goto error;
279: case '{':
280: fch = '{';
281:
282: ch = cmd_string_getc(s, p);
283: if (!cmd_string_first(ch))
284: goto error;
285: /* FALLTHROUGH */
286: default:
287: if (!cmd_string_first(ch)) {
288: xasprintf(&t, "$%c", ch);
289: return (t);
290: }
291:
292: buf = xrealloc(buf, 1, len + 1);
293: buf[len++] = ch;
294:
295: for (;;) {
296: ch = cmd_string_getc(s, p);
297: if (ch == EOF || !cmd_string_other(ch))
298: break;
299: else {
300: if (len >= SIZE_MAX - 3)
301: goto error;
302: buf = xrealloc(buf, 1, len + 1);
303: buf[len++] = ch;
304: }
305: }
306: }
307:
308: if (fch == '{' && ch != '}')
309: goto error;
310: if (ch != EOF && fch != '{')
1.11 ! nicm 311: cmd_string_ungetc(p); /* ch */
1.1 nicm 312:
313: buf = xrealloc(buf, 1, len + 1);
314: buf[len] = '\0';
315:
316: if ((t = getenv(buf)) == NULL) {
317: xfree(buf);
318: return (xstrdup(""));
319: }
320: xfree(buf);
321: return (xstrdup(t));
322:
323: error:
324: if (buf != NULL)
325: xfree(buf);
326: return (NULL);
1.4 nicm 327: }
328:
329: char *
330: cmd_string_expand_tilde(const char *s, size_t *p)
331: {
332: struct passwd *pw;
333: char *home, *path, *username;
334:
335: home = NULL;
336: if (cmd_string_getc(s, p) == '/') {
1.10 nicm 337: if ((home = getenv("HOME")) == NULL || *home == '\0') {
1.4 nicm 338: if ((pw = getpwuid(getuid())) != NULL)
339: home = pw->pw_dir;
340: }
341: } else {
1.11 ! nicm 342: cmd_string_ungetc(p);
1.4 nicm 343: if ((username = cmd_string_string(s, p, '/', 0)) == NULL)
344: return (NULL);
345: if ((pw = getpwnam(username)) != NULL)
346: home = pw->pw_dir;
347: if (username != NULL)
348: xfree(username);
349: }
350: if (home == NULL)
351: return (NULL);
352:
353: xasprintf(&path, "%s/", home);
354: return (path);
1.1 nicm 355: }