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