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