Annotation of src/usr.bin/tmux/cmd-string.c, Revision 1.26
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.26 ! nicm 57: int
! 58: cmd_string_split(const char *s, int *rargc, char ***rargv)
1.1 nicm 59: {
1.26 ! 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.26 ! nicm 116: goto done;
1.4 nicm 117: case '~':
1.26 ! nicm 118: if (buf != NULL) {
! 119: append = 1;
1.4 nicm 120: break;
121: }
1.26 ! 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.26 ! 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.26 ! nicm 137: append = 0;
1.1 nicm 138: }
139:
1.26 ! nicm 140: done:
! 141: *rargc = argc;
! 142: *rargv = argv;
! 143:
! 144: free(buf);
! 145: return (0);
! 146:
1.1 nicm 147: error:
1.26 ! nicm 148: if (argv != NULL)
! 149: cmd_free_argv(argc, argv);
! 150: free(buf);
! 151: return (-1);
! 152: }
1.1 nicm 153:
1.26 ! 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.1 nicm 160:
1.26 ! nicm 161: *cause = NULL;
! 162: if (cmd_string_split(s, &argc, &argv) != 0)
! 163: goto error;
! 164: if (argc != 0) {
! 165: cmdlist = cmd_list_parse(argc, argv, file, line, cause);
! 166: if (cmdlist == NULL) {
! 167: cmd_free_argv(argc, argv);
! 168: goto error;
! 169: }
1.8 nicm 170: }
1.26 ! nicm 171: cmd_free_argv(argc, argv);
! 172: return (cmdlist);
1.1 nicm 173:
1.26 ! nicm 174: error:
! 175: xasprintf(cause, "invalid or unknown command: %s", s);
! 176: return (NULL);
1.1 nicm 177: }
178:
1.23 nicm 179: static void
1.17 nicm 180: cmd_string_copy(char **dst, char *src, size_t *len)
181: {
182: size_t srclen;
183:
184: srclen = strlen(src);
185:
1.20 nicm 186: *dst = xrealloc(*dst, *len + srclen + 1);
1.17 nicm 187: strlcpy(*dst + *len, src, srclen + 1);
188:
189: *len += srclen;
190: free(src);
191: }
192:
1.23 nicm 193: static char *
1.1 nicm 194: cmd_string_string(const char *s, size_t *p, char endch, int esc)
195: {
196: int ch;
197: char *buf, *t;
198: size_t len;
199:
1.7 deraadt 200: buf = NULL;
1.1 nicm 201: len = 0;
202:
1.7 deraadt 203: while ((ch = cmd_string_getc(s, p)) != endch) {
204: switch (ch) {
1.1 nicm 205: case EOF:
206: goto error;
1.7 deraadt 207: case '\\':
1.1 nicm 208: if (!esc)
209: break;
1.7 deraadt 210: switch (ch = cmd_string_getc(s, p)) {
1.1 nicm 211: case EOF:
212: goto error;
1.5 nicm 213: case 'e':
214: ch = '\033';
215: break;
1.7 deraadt 216: case 'r':
217: ch = '\r';
218: break;
219: case 'n':
220: ch = '\n';
221: break;
222: case 't':
223: ch = '\t';
224: break;
225: }
226: break;
1.1 nicm 227: case '$':
228: if (!esc)
229: break;
230: if ((t = cmd_string_variable(s, p)) == NULL)
231: goto error;
1.17 nicm 232: cmd_string_copy(&buf, t, &len);
1.1 nicm 233: continue;
1.7 deraadt 234: }
1.1 nicm 235:
236: if (len >= SIZE_MAX - 2)
237: goto error;
1.20 nicm 238: buf = xrealloc(buf, len + 1);
1.7 deraadt 239: buf[len++] = ch;
240: }
1.1 nicm 241:
1.20 nicm 242: buf = xrealloc(buf, len + 1);
1.1 nicm 243: buf[len] = '\0';
244: return (buf);
245:
246: error:
1.16 nicm 247: free(buf);
1.1 nicm 248: return (NULL);
249: }
250:
1.23 nicm 251: static char *
1.1 nicm 252: cmd_string_variable(const char *s, size_t *p)
253: {
1.15 nicm 254: int ch, fch;
255: char *buf, *t;
256: size_t len;
257: struct environ_entry *envent;
1.1 nicm 258:
259: #define cmd_string_first(ch) ((ch) == '_' || \
260: ((ch) >= 'a' && (ch) <= 'z') || ((ch) >= 'A' && (ch) <= 'Z'))
261: #define cmd_string_other(ch) ((ch) == '_' || \
262: ((ch) >= 'a' && (ch) <= 'z') || ((ch) >= 'A' && (ch) <= 'Z') || \
263: ((ch) >= '0' && (ch) <= '9'))
264:
1.7 deraadt 265: buf = NULL;
1.1 nicm 266: len = 0;
267:
268: fch = EOF;
269: switch (ch = cmd_string_getc(s, p)) {
270: case EOF:
271: goto error;
272: case '{':
273: fch = '{';
274:
275: ch = cmd_string_getc(s, p);
276: if (!cmd_string_first(ch))
277: goto error;
278: /* FALLTHROUGH */
279: default:
280: if (!cmd_string_first(ch)) {
281: xasprintf(&t, "$%c", ch);
282: return (t);
283: }
284:
1.20 nicm 285: buf = xrealloc(buf, len + 1);
1.1 nicm 286: buf[len++] = ch;
287:
288: for (;;) {
289: ch = cmd_string_getc(s, p);
290: if (ch == EOF || !cmd_string_other(ch))
291: break;
292: else {
293: if (len >= SIZE_MAX - 3)
294: goto error;
1.20 nicm 295: buf = xrealloc(buf, len + 1);
1.1 nicm 296: buf[len++] = ch;
297: }
298: }
299: }
300:
301: if (fch == '{' && ch != '}')
302: goto error;
303: if (ch != EOF && fch != '{')
1.11 nicm 304: cmd_string_ungetc(p); /* ch */
1.1 nicm 305:
1.20 nicm 306: buf = xrealloc(buf, len + 1);
1.1 nicm 307: buf[len] = '\0';
308:
1.21 nicm 309: envent = environ_find(global_environ, buf);
1.16 nicm 310: free(buf);
1.15 nicm 311: if (envent == NULL)
1.1 nicm 312: return (xstrdup(""));
1.15 nicm 313: return (xstrdup(envent->value));
1.1 nicm 314:
315: error:
1.16 nicm 316: free(buf);
1.1 nicm 317: return (NULL);
1.4 nicm 318: }
319:
1.23 nicm 320: static char *
1.4 nicm 321: cmd_string_expand_tilde(const char *s, size_t *p)
322: {
1.15 nicm 323: struct passwd *pw;
324: struct environ_entry *envent;
1.19 nicm 325: char *home, *path, *user, *cp;
326: int last;
1.4 nicm 327:
328: home = NULL;
1.19 nicm 329:
330: last = cmd_string_getc(s, p);
331: if (last == EOF || last == '/' || last == ' '|| last == '\t') {
1.21 nicm 332: envent = environ_find(global_environ, "HOME");
1.15 nicm 333: if (envent != NULL && *envent->value != '\0')
334: home = envent->value;
335: else if ((pw = getpwuid(getuid())) != NULL)
336: home = pw->pw_dir;
1.4 nicm 337: } else {
1.11 nicm 338: cmd_string_ungetc(p);
1.19 nicm 339:
340: cp = user = xmalloc(strlen(s));
341: for (;;) {
342: last = cmd_string_getc(s, p);
1.23 nicm 343: if (last == EOF ||
344: last == '/' ||
345: last == ' '||
346: last == '\t')
1.19 nicm 347: break;
348: *cp++ = last;
349: }
350: *cp = '\0';
351:
352: if ((pw = getpwnam(user)) != NULL)
1.4 nicm 353: home = pw->pw_dir;
1.19 nicm 354: free(user);
1.4 nicm 355: }
1.19 nicm 356:
1.4 nicm 357: if (home == NULL)
358: return (NULL);
359:
1.19 nicm 360: if (last != EOF)
361: xasprintf(&path, "%s%c", home, last);
362: else
363: xasprintf(&path, "%s", home);
1.4 nicm 364: return (path);
1.1 nicm 365: }