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