Annotation of src/usr.bin/tmux/cmd-string.c, Revision 1.4
1.4 ! nicm 1: /* $OpenBSD: cmd-string.c,v 1.3 2009/07/08 16:04:56 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:
34: int cmd_string_getc(const char *, size_t *);
35: void cmd_string_ungetc(const char *, size_t *);
36: char *cmd_string_string(const char *, size_t *, char, int);
37: char *cmd_string_variable(const char *, size_t *);
1.4 ! nicm 38: char *cmd_string_expand_tilde(const char *, size_t *);
1.1 nicm 39:
40: int
41: cmd_string_getc(const char *s, size_t *p)
42: {
43: if (s[*p] == '\0')
44: return (EOF);
45: return (s[(*p)++]);
46: }
47:
48: void
49: cmd_string_ungetc(unused const char *s, size_t *p)
50: {
51: (*p)--;
52: }
53:
54: /*
55: * Parse command string. Returns -1 on error. If returning -1, cause is error
56: * string, or NULL for empty command.
57: */
58: int
59: cmd_string_parse(const char *s, struct cmd_list **cmdlist, char **cause)
60: {
61: size_t p;
62: int ch, argc, rval, have_arg;
63: char **argv, *buf, *t, *u;
64: size_t len;
65:
66: if ((t = strchr(s, ' ')) == NULL && (t = strchr(s, '\t')) == NULL)
67: t = strchr(s, '\0');
68: if ((u = strchr(s, '=')) != NULL && u < t) {
1.2 nicm 69: if (putenv(xstrdup(s)) != 0) {
1.1 nicm 70: xasprintf(cause, "assignment failed: %s", s);
71: return (-1);
72: }
73: *cmdlist = NULL;
74: return (0);
75: }
76:
77: argv = NULL;
78: argc = 0;
79:
80: buf = NULL;
81: len = 0;
82:
83: have_arg = 0;
84:
85: *cause = NULL;
86:
87: *cmdlist = NULL;
88: rval = -1;
89:
90: p = 0;
91: for (;;) {
92: ch = cmd_string_getc(s, &p);
93: switch (ch) {
94: case '\'':
95: if ((t = cmd_string_string(s, &p, '\'', 0)) == NULL)
96: goto error;
97: buf = xrealloc(buf, 1, len + strlen(t) + 1);
98: strlcpy(buf + len, t, strlen(t) + 1);
99: len += strlen(t);
100: xfree(t);
101:
102: have_arg = 1;
103: break;
104: case '"':
105: if ((t = cmd_string_string(s, &p, '"', 1)) == NULL)
106: goto error;
107: buf = xrealloc(buf, 1, len + strlen(t) + 1);
108: strlcpy(buf + len, t, strlen(t) + 1);
109: len += strlen(t);
110: xfree(t);
111:
112: have_arg = 1;
113: break;
114: case '$':
115: if ((t = cmd_string_variable(s, &p)) == NULL)
116: goto error;
117: buf = xrealloc(buf, 1, len + strlen(t) + 1);
118: strlcpy(buf + len, t, strlen(t) + 1);
119: len += strlen(t);
1.3 nicm 120: xfree(t);
1.1 nicm 121:
122: have_arg = 1;
123: break;
124: case '#':
125: /* Comment: discard rest of line. */
126: while ((ch = cmd_string_getc(s, &p)) != EOF)
127: ;
128: /* FALLTHROUGH */
129: case EOF:
130: case ' ':
131: case '\t':
132: if (have_arg) {
133: buf = xrealloc(buf, 1, len + 1);
134: buf[len] = '\0';
135:
136: argv = xrealloc(argv, argc + 1, sizeof *argv);
137: argv[argc++] = buf;
138:
139: buf = NULL;
140: len = 0;
141:
142: have_arg = 0;
143: }
144:
145: if (ch != EOF)
146: break;
147: if (argc == 0)
148: goto out;
149:
150: *cmdlist = cmd_list_parse(argc, argv, cause);
151: if (*cmdlist == NULL)
152: goto out;
153:
154: do
155: xfree(argv[argc - 1]);
156: while (--argc > 0);
157:
158: rval = 0;
159: goto out;
1.4 ! nicm 160: case '~':
! 161: if (have_arg == 0) {
! 162: if ((t = cmd_string_expand_tilde(s, &p)) == NULL)
! 163: goto error;
! 164: buf = xrealloc(buf, 1, len + strlen(t) + 1);
! 165: strlcpy(buf + len, t, strlen(t) + 1);
! 166: len += strlen(t);
! 167: xfree(t);
! 168: break;
! 169: }
! 170: /* FALLTHROUGH */
1.1 nicm 171: default:
172: if (len >= SIZE_MAX - 2)
173: goto error;
174:
175: buf = xrealloc(buf, 1, len + 1);
176: buf[len++] = ch;
177:
178: have_arg = 1;
179: break;
180: }
181: }
182:
183: error:
184: xasprintf(cause, "invalid or unknown command: %s", s);
185:
186: out:
187: if (buf != NULL)
188: xfree(buf);
189:
190: while (--argc >= 0)
191: xfree(argv[argc]);
192: if (argv != NULL)
193: xfree(argv);
194:
195: return (rval);
196: }
197:
198: char *
199: cmd_string_string(const char *s, size_t *p, char endch, int esc)
200: {
201: int ch;
202: char *buf, *t;
203: size_t len;
204:
205: buf = NULL;
206: len = 0;
207:
208: while ((ch = cmd_string_getc(s, p)) != endch) {
209: switch (ch) {
210: case EOF:
211: goto error;
212: case '\\':
213: if (!esc)
214: break;
215: switch (ch = cmd_string_getc(s, p)) {
216: case EOF:
217: goto error;
218: case 'r':
219: ch = '\r';
220: break;
221: case 'n':
222: ch = '\n';
223: break;
224: case 't':
225: ch = '\t';
226: break;
227: }
228: break;
229: case '$':
230: if (!esc)
231: break;
232: if ((t = cmd_string_variable(s, p)) == NULL)
233: goto error;
234: buf = xrealloc(buf, 1, len + strlen(t) + 1);
235: strlcpy(buf + len, t, strlen(t) + 1);
236: len += strlen(t);
1.3 nicm 237: xfree(t);
1.1 nicm 238: continue;
239: }
240:
241: if (len >= SIZE_MAX - 2)
242: goto error;
243: buf = xrealloc(buf, 1, len + 1);
244: buf[len++] = ch;
245: }
246:
247: buf = xrealloc(buf, 1, len + 1);
248: buf[len] = '\0';
249: return (buf);
250:
251: error:
252: if (buf != NULL)
253: xfree(buf);
254: return (NULL);
255: }
256:
257: char *
258: cmd_string_variable(const char *s, size_t *p)
259: {
260: int ch, fch;
261: char *buf, *t;
262: size_t len;
263:
264: #define cmd_string_first(ch) ((ch) == '_' || \
265: ((ch) >= 'a' && (ch) <= 'z') || ((ch) >= 'A' && (ch) <= 'Z'))
266: #define cmd_string_other(ch) ((ch) == '_' || \
267: ((ch) >= 'a' && (ch) <= 'z') || ((ch) >= 'A' && (ch) <= 'Z') || \
268: ((ch) >= '0' && (ch) <= '9'))
269:
270: buf = NULL;
271: len = 0;
272:
273: fch = EOF;
274: switch (ch = cmd_string_getc(s, p)) {
275: case EOF:
276: goto error;
277: case '{':
278: fch = '{';
279:
280: ch = cmd_string_getc(s, p);
281: if (!cmd_string_first(ch))
282: goto error;
283: /* FALLTHROUGH */
284: default:
285: if (!cmd_string_first(ch)) {
286: xasprintf(&t, "$%c", ch);
287: return (t);
288: }
289:
290: buf = xrealloc(buf, 1, len + 1);
291: buf[len++] = ch;
292:
293: for (;;) {
294: ch = cmd_string_getc(s, p);
295: if (ch == EOF || !cmd_string_other(ch))
296: break;
297: else {
298: if (len >= SIZE_MAX - 3)
299: goto error;
300: buf = xrealloc(buf, 1, len + 1);
301: buf[len++] = ch;
302: }
303: }
304: }
305:
306: if (fch == '{' && ch != '}')
307: goto error;
308: if (ch != EOF && fch != '{')
309: cmd_string_ungetc(s, p); /* ch */
310:
311: buf = xrealloc(buf, 1, len + 1);
312: buf[len] = '\0';
313:
314: if ((t = getenv(buf)) == NULL) {
315: xfree(buf);
316: return (xstrdup(""));
317: }
318: xfree(buf);
319: return (xstrdup(t));
320:
321: error:
322: if (buf != NULL)
323: xfree(buf);
324: return (NULL);
1.4 ! nicm 325: }
! 326:
! 327: char *
! 328: cmd_string_expand_tilde(const char *s, size_t *p)
! 329: {
! 330: struct passwd *pw;
! 331: char *home, *path, *username;
! 332:
! 333: home = NULL;
! 334: if (cmd_string_getc(s, p) == '/') {
! 335: if ((home = getenv("HOME")) == NULL) {
! 336: if ((pw = getpwuid(getuid())) != NULL)
! 337: home = pw->pw_dir;
! 338: }
! 339: } else {
! 340: cmd_string_ungetc(s, p);
! 341: if ((username = cmd_string_string(s, p, '/', 0)) == NULL)
! 342: return (NULL);
! 343: if ((pw = getpwnam(username)) != NULL)
! 344: home = pw->pw_dir;
! 345: if (username != NULL)
! 346: xfree(username);
! 347: }
! 348: if (home == NULL)
! 349: return (NULL);
! 350:
! 351: xasprintf(&path, "%s/", home);
! 352: return (path);
1.1 nicm 353: }