Annotation of embedaddon/tmux/cmd-string.c, revision 1.1.1.1
1.1 misho 1: /* $OpenBSD$ */
2:
3: /*
4: * Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
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>
22: #include <pwd.h>
23: #include <stdio.h>
24: #include <string.h>
25: #include <stdlib.h>
26: #include <unistd.h>
27:
28: #include "tmux.h"
29:
30: /*
31: * Parse a command from a string.
32: */
33:
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 *);
40:
41: static int
42: cmd_string_getc(const char *s, size_t *p)
43: {
44: const u_char *ucs = s;
45:
46: if (ucs[*p] == '\0')
47: return (EOF);
48: return (ucs[(*p)++]);
49: }
50:
51: static void
52: cmd_string_ungetc(size_t *p)
53: {
54: (*p)--;
55: }
56:
57: int
58: cmd_string_split(const char *s, int *rargc, char ***rargv)
59: {
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;
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;
72: cmd_string_copy(&buf, t, &len);
73: break;
74: case '"':
75: if ((t = cmd_string_string(s, &p, '"', 1)) == NULL)
76: goto error;
77: cmd_string_copy(&buf, t, &len);
78: break;
79: case '$':
80: if ((t = cmd_string_variable(s, &p)) == NULL)
81: goto error;
82: cmd_string_copy(&buf, t, &len);
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':
92: if (buf != NULL) {
93: buf = xrealloc(buf, len + 1);
94: buf[len] = '\0';
95:
96: argv = xreallocarray(argv, argc + 1,
97: sizeof *argv);
98: argv[argc++] = buf;
99:
100: buf = NULL;
101: len = 0;
102: }
103:
104: if (ch != EOF)
105: break;
106:
107: while (argc != 0) {
108: equals = strchr(argv[0], '=');
109: whitespace = argv[0] + strcspn(argv[0], " \t");
110: if (equals == NULL || equals > whitespace)
111: break;
112: environ_put(global_environ, argv[0]);
113: argc--;
114: memmove(argv, argv + 1, argc * (sizeof *argv));
115: }
116: goto done;
117: case '~':
118: if (buf != NULL) {
119: append = 1;
120: break;
121: }
122: t = cmd_string_expand_tilde(s, &p);
123: if (t == NULL)
124: goto error;
125: cmd_string_copy(&buf, t, &len);
126: break;
127: default:
128: append = 1;
129: break;
130: }
131: if (append) {
132: if (len >= SIZE_MAX - 2)
133: goto error;
134: buf = xrealloc(buf, len + 1);
135: buf[len++] = ch;
136: }
137: append = 0;
138: }
139:
140: done:
141: *rargc = argc;
142: *rargv = argv;
143:
144: free(buf);
145: return (0);
146:
147: error:
148: if (argv != NULL)
149: cmd_free_argv(argc, argv);
150: free(buf);
151: return (-1);
152: }
153:
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;
160:
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: }
170: }
171: cmd_free_argv(argc, argv);
172: return (cmdlist);
173:
174: error:
175: xasprintf(cause, "invalid or unknown command: %s", s);
176: return (NULL);
177: }
178:
179: static void
180: cmd_string_copy(char **dst, char *src, size_t *len)
181: {
182: size_t srclen;
183:
184: srclen = strlen(src);
185:
186: *dst = xrealloc(*dst, *len + srclen + 1);
187: strlcpy(*dst + *len, src, srclen + 1);
188:
189: *len += srclen;
190: free(src);
191: }
192:
193: static char *
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:
200: buf = NULL;
201: len = 0;
202:
203: while ((ch = cmd_string_getc(s, p)) != endch) {
204: switch (ch) {
205: case EOF:
206: goto error;
207: case '\\':
208: if (!esc)
209: break;
210: switch (ch = cmd_string_getc(s, p)) {
211: case EOF:
212: goto error;
213: case 'e':
214: ch = '\033';
215: break;
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;
227: case '$':
228: if (!esc)
229: break;
230: if ((t = cmd_string_variable(s, p)) == NULL)
231: goto error;
232: cmd_string_copy(&buf, t, &len);
233: continue;
234: }
235:
236: if (len >= SIZE_MAX - 2)
237: goto error;
238: buf = xrealloc(buf, len + 1);
239: buf[len++] = ch;
240: }
241:
242: buf = xrealloc(buf, len + 1);
243: buf[len] = '\0';
244: return (buf);
245:
246: error:
247: free(buf);
248: return (NULL);
249: }
250:
251: static char *
252: cmd_string_variable(const char *s, size_t *p)
253: {
254: int ch, fch;
255: char *buf, *t;
256: size_t len;
257: struct environ_entry *envent;
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:
265: buf = NULL;
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:
285: buf = xrealloc(buf, len + 1);
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;
295: buf = xrealloc(buf, len + 1);
296: buf[len++] = ch;
297: }
298: }
299: }
300:
301: if (fch == '{' && ch != '}')
302: goto error;
303: if (ch != EOF && fch != '{')
304: cmd_string_ungetc(p); /* ch */
305:
306: buf = xrealloc(buf, len + 1);
307: buf[len] = '\0';
308:
309: envent = environ_find(global_environ, buf);
310: free(buf);
311: if (envent == NULL)
312: return (xstrdup(""));
313: return (xstrdup(envent->value));
314:
315: error:
316: free(buf);
317: return (NULL);
318: }
319:
320: static char *
321: cmd_string_expand_tilde(const char *s, size_t *p)
322: {
323: struct passwd *pw;
324: struct environ_entry *envent;
325: char *home, *path, *user, *cp;
326: int last;
327:
328: home = NULL;
329:
330: last = cmd_string_getc(s, p);
331: if (last == EOF || last == '/' || last == ' '|| last == '\t') {
332: envent = environ_find(global_environ, "HOME");
333: if (envent != NULL && *envent->value != '\0')
334: home = envent->value;
335: else if ((pw = getpwuid(getuid())) != NULL)
336: home = pw->pw_dir;
337: } else {
338: cmd_string_ungetc(p);
339:
340: cp = user = xmalloc(strlen(s));
341: for (;;) {
342: last = cmd_string_getc(s, p);
343: if (last == EOF ||
344: last == '/' ||
345: last == ' '||
346: last == '\t')
347: break;
348: *cp++ = last;
349: }
350: *cp = '\0';
351:
352: if ((pw = getpwnam(user)) != NULL)
353: home = pw->pw_dir;
354: free(user);
355: }
356:
357: if (home == NULL)
358: return (NULL);
359:
360: if (last != EOF)
361: xasprintf(&path, "%s%c", home, last);
362: else
363: xasprintf(&path, "%s", home);
364: return (path);
365: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>