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