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