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