GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/tmux/cmd-string.c Lines: 0 150 0.0 %
Date: 2017-11-07 Branches: 0 114 0.0 %

Line Branch Exec Source
1
/* $OpenBSD: cmd-string.c,v 1.29 2017/06/14 07:42:41 nicm Exp $ */
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
		xasprintf(cause, "invalid or unknown command: %s", s);
164
		return (NULL);
165
	}
166
	if (argc != 0) {
167
		cmdlist = cmd_list_parse(argc, argv, file, line, cause);
168
		if (cmdlist == NULL) {
169
			cmd_free_argv(argc, argv);
170
			return (NULL);
171
		}
172
	}
173
	cmd_free_argv(argc, argv);
174
	return (cmdlist);
175
}
176
177
static void
178
cmd_string_copy(char **dst, char *src, size_t *len)
179
{
180
	size_t srclen;
181
182
	srclen = strlen(src);
183
184
	*dst = xrealloc(*dst, *len + srclen + 1);
185
	strlcpy(*dst + *len, src, srclen + 1);
186
187
	*len += srclen;
188
	free(src);
189
}
190
191
static char *
192
cmd_string_string(const char *s, size_t *p, char endch, int esc)
193
{
194
	int	ch;
195
	char   *buf, *t;
196
	size_t	len;
197
198
	buf = NULL;
199
	len = 0;
200
201
	while ((ch = cmd_string_getc(s, p)) != endch) {
202
		switch (ch) {
203
		case EOF:
204
			goto error;
205
		case '\\':
206
			if (!esc)
207
				break;
208
			switch (ch = cmd_string_getc(s, p)) {
209
			case EOF:
210
				goto error;
211
			case 'e':
212
				ch = '\033';
213
				break;
214
			case 'r':
215
				ch = '\r';
216
				break;
217
			case 'n':
218
				ch = '\n';
219
				break;
220
			case 't':
221
				ch = '\t';
222
				break;
223
			}
224
			break;
225
		case '$':
226
			if (!esc)
227
				break;
228
			if ((t = cmd_string_variable(s, p)) == NULL)
229
				goto error;
230
			cmd_string_copy(&buf, t, &len);
231
			continue;
232
		}
233
234
		if (len >= SIZE_MAX - 2)
235
			goto error;
236
		buf = xrealloc(buf, len + 1);
237
		buf[len++] = ch;
238
	}
239
240
	buf = xrealloc(buf, len + 1);
241
	buf[len] = '\0';
242
	return (buf);
243
244
error:
245
	free(buf);
246
	return (NULL);
247
}
248
249
static char *
250
cmd_string_variable(const char *s, size_t *p)
251
{
252
	int			ch, fch;
253
	char		       *buf, *t;
254
	size_t			len;
255
	struct environ_entry   *envent;
256
257
#define cmd_string_first(ch) ((ch) == '_' || \
258
	((ch) >= 'a' && (ch) <= 'z') || ((ch) >= 'A' && (ch) <= 'Z'))
259
#define cmd_string_other(ch) ((ch) == '_' || \
260
	((ch) >= 'a' && (ch) <= 'z') || ((ch) >= 'A' && (ch) <= 'Z') || \
261
	((ch) >= '0' && (ch) <= '9'))
262
263
	buf = NULL;
264
	len = 0;
265
266
	fch = EOF;
267
	switch (ch = cmd_string_getc(s, p)) {
268
	case EOF:
269
		goto error;
270
	case '{':
271
		fch = '{';
272
273
		ch = cmd_string_getc(s, p);
274
		if (!cmd_string_first(ch))
275
			goto error;
276
		/* FALLTHROUGH */
277
	default:
278
		if (!cmd_string_first(ch)) {
279
			xasprintf(&t, "$%c", ch);
280
			return (t);
281
		}
282
283
		buf = xrealloc(buf, len + 1);
284
		buf[len++] = ch;
285
286
		for (;;) {
287
			ch = cmd_string_getc(s, p);
288
			if (ch == EOF || !cmd_string_other(ch))
289
				break;
290
			else {
291
				if (len >= SIZE_MAX - 3)
292
					goto error;
293
				buf = xrealloc(buf, len + 1);
294
				buf[len++] = ch;
295
			}
296
		}
297
	}
298
299
	if (fch == '{' && ch != '}')
300
		goto error;
301
	if (ch != EOF && fch != '{')
302
		cmd_string_ungetc(p); /* ch */
303
304
	buf = xrealloc(buf, len + 1);
305
	buf[len] = '\0';
306
307
	envent = environ_find(global_environ, buf);
308
	free(buf);
309
	if (envent == NULL)
310
		return (xstrdup(""));
311
	return (xstrdup(envent->value));
312
313
error:
314
	free(buf);
315
	return (NULL);
316
}
317
318
static char *
319
cmd_string_expand_tilde(const char *s, size_t *p)
320
{
321
	struct passwd		*pw;
322
	struct environ_entry	*envent;
323
	char			*home, *path, *user, *cp;
324
	int			 last;
325
326
	home = NULL;
327
328
	last = cmd_string_getc(s, p);
329
	if (last == EOF || last == '/' || last == ' '|| last == '\t') {
330
		envent = environ_find(global_environ, "HOME");
331
		if (envent != NULL && *envent->value != '\0')
332
			home = envent->value;
333
		else if ((pw = getpwuid(getuid())) != NULL)
334
			home = pw->pw_dir;
335
	} else {
336
		cmd_string_ungetc(p);
337
338
		cp = user = xmalloc(strlen(s));
339
		for (;;) {
340
			last = cmd_string_getc(s, p);
341
			if (last == EOF ||
342
			    last == '/' ||
343
			    last == ' '||
344
			    last == '\t')
345
				break;
346
			*cp++ = last;
347
		}
348
		*cp = '\0';
349
350
		if ((pw = getpwnam(user)) != NULL)
351
			home = pw->pw_dir;
352
		free(user);
353
	}
354
355
	if (home == NULL)
356
		return (NULL);
357
358
	if (last != EOF)
359
		xasprintf(&path, "%s%c", home, last);
360
	else
361
		xasprintf(&path, "%s", home);
362
	return (path);
363
}