xref: /minix/external/bsd/tmux/dist/cmd-string.c (revision 0a6a1f1d)
1 /* Id */
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 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 *);
40 
41 int
cmd_string_getc(const char * s,size_t * p)42 cmd_string_getc(const char *s, size_t *p)
43 {
44 	const char	*ucs = s;
45 
46 	if (ucs[*p] == '\0')
47 		return (EOF);
48 	return (u_char)(ucs[(*p)++]);
49 }
50 
51 void
cmd_string_ungetc(size_t * p)52 cmd_string_ungetc(size_t *p)
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
cmd_string_parse(const char * s,struct cmd_list ** cmdlist,const char * file,u_int line,char ** cause)62 cmd_string_parse(const char *s, struct cmd_list **cmdlist, const char *file,
63     u_int line, char **cause)
64 {
65 	size_t		p;
66 	int		ch, i, argc, rval;
67 	char	      **argv, *buf, *t;
68 	const char     *whitespace, *equals;
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;
89 			cmd_string_copy(&buf, t, &len);
90 			break;
91 		case '"':
92 			if ((t = cmd_string_string(s, &p, '"', 1)) == NULL)
93 				goto error;
94 			cmd_string_copy(&buf, t, &len);
95 			break;
96 		case '$':
97 			if ((t = cmd_string_variable(s, &p)) == NULL)
98 				goto error;
99 			cmd_string_copy(&buf, t, &len);
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':
109 			if (buf != NULL) {
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;
122 
123 			while (argc != 0) {
124 				equals = strchr(argv[0], '=');
125 				whitespace = argv[0] + strcspn(argv[0], " \t");
126 				if (equals == NULL || equals > whitespace)
127 					break;
128 				environ_put(&global_environ, argv[0]);
129 				argc--;
130 				memmove(argv, argv + 1, argc * (sizeof *argv));
131 			}
132 			if (argc == 0)
133 				goto out;
134 
135 			*cmdlist = cmd_list_parse(argc, argv, file, line, cause);
136 			if (*cmdlist == NULL)
137 				goto out;
138 
139 			rval = 0;
140 			goto out;
141 		case '~':
142 			if (buf == NULL) {
143 				t = cmd_string_expand_tilde(s, &p);
144 				if (t == NULL)
145 					goto error;
146 				cmd_string_copy(&buf, t, &len);
147 				break;
148 			}
149 			/* FALLTHROUGH */
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:
164 	free(buf);
165 
166 	if (argv != NULL) {
167 		for (i = 0; i < argc; i++)
168 			free(argv[i]);
169 		free(argv);
170 	}
171 
172 	return (rval);
173 }
174 
175 void
cmd_string_copy(char ** dst,char * src,size_t * len)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 
189 char *
cmd_string_string(const char * s,size_t * p,char endch,int esc)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 
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 			cmd_string_copy(&buf, t, &len);
229 			continue;
230 		}
231 
232 		if (len >= SIZE_MAX - 2)
233 			goto error;
234 		buf = xrealloc(buf, 1, len + 1);
235 		buf[len++] = ch;
236 	}
237 
238 	buf = xrealloc(buf, 1, len + 1);
239 	buf[len] = '\0';
240 	return (buf);
241 
242 error:
243 	free(buf);
244 	return (NULL);
245 }
246 
247 char *
cmd_string_variable(const char * s,size_t * p)248 cmd_string_variable(const char *s, size_t *p)
249 {
250 	int			ch, fch;
251 	char		       *buf, *t;
252 	size_t			len;
253 	struct environ_entry   *envent;
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 
261 	buf = NULL;
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 != '{')
300 		cmd_string_ungetc(p); /* ch */
301 
302 	buf = xrealloc(buf, 1, len + 1);
303 	buf[len] = '\0';
304 
305 	envent = environ_find(&global_environ, buf);
306 	free(buf);
307 	if (envent == NULL)
308 		return (xstrdup(""));
309 	return (xstrdup(envent->value));
310 
311 error:
312 	free(buf);
313 	return (NULL);
314 }
315 
316 char *
cmd_string_expand_tilde(const char * s,size_t * p)317 cmd_string_expand_tilde(const char *s, size_t *p)
318 {
319 	struct passwd		*pw;
320 	struct environ_entry	*envent;
321 	char			*home, *path, *user, *cp;
322 	int			 last;
323 
324 	home = NULL;
325 
326 	last = cmd_string_getc(s, p);
327 	if (last == EOF || last == '/' || last == ' '|| last == '\t') {
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;
333 	} else {
334 		cmd_string_ungetc(p);
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)
346 			home = pw->pw_dir;
347 		free(user);
348 	}
349 
350 	if (home == NULL)
351 		return (NULL);
352 
353 	if (last != EOF)
354 		xasprintf(&path, "%s%c", home, last);
355 	else
356 		xasprintf(&path, "%s", home);
357 	return (path);
358 }
359