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