GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/tmux/cmd-string.c Lines: 0 162 0.0 %
Date: 2016-12-06 Branches: 0 106 0.0 %

Line Branch Exec Source
1
/* $OpenBSD: cmd-string.c,v 1.22 2016/01/19 15:59:12 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
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
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
void
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
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, len + 1);
111
				buf[len] = '\0';
112
113
				argv = xreallocarray(argv, argc + 1,
114
				    sizeof *argv);
115
				argv[argc++] = buf;
116
117
				buf = NULL;
118
				len = 0;
119
			}
120
121
			if (ch != EOF)
122
				break;
123
124
			while (argc != 0) {
125
				equals = strchr(argv[0], '=');
126
				whitespace = argv[0] + strcspn(argv[0], " \t");
127
				if (equals == NULL || equals > whitespace)
128
					break;
129
				environ_put(global_environ, argv[0]);
130
				argc--;
131
				memmove(argv, argv + 1, argc * (sizeof *argv));
132
			}
133
			if (argc == 0)
134
				goto out;
135
136
			*cmdlist = cmd_list_parse(argc, argv, file, line, cause);
137
			if (*cmdlist == NULL)
138
				goto out;
139
140
			rval = 0;
141
			goto out;
142
		case '~':
143
			if (buf == NULL) {
144
				t = cmd_string_expand_tilde(s, &p);
145
				if (t == NULL)
146
					goto error;
147
				cmd_string_copy(&buf, t, &len);
148
				break;
149
			}
150
			/* FALLTHROUGH */
151
		default:
152
			if (len >= SIZE_MAX - 2)
153
				goto error;
154
155
			buf = xrealloc(buf, len + 1);
156
			buf[len++] = ch;
157
			break;
158
		}
159
	}
160
161
error:
162
	xasprintf(cause, "invalid or unknown command: %s", s);
163
164
out:
165
	free(buf);
166
167
	if (argv != NULL) {
168
		for (i = 0; i < argc; i++)
169
			free(argv[i]);
170
		free(argv);
171
	}
172
173
	return (rval);
174
}
175
176
void
177
cmd_string_copy(char **dst, char *src, size_t *len)
178
{
179
	size_t srclen;
180
181
	srclen = strlen(src);
182
183
	*dst = xrealloc(*dst, *len + srclen + 1);
184
	strlcpy(*dst + *len, src, srclen + 1);
185
186
	*len += srclen;
187
	free(src);
188
}
189
190
char *
191
cmd_string_string(const char *s, size_t *p, char endch, int esc)
192
{
193
	int	ch;
194
	char   *buf, *t;
195
	size_t	len;
196
197
	buf = NULL;
198
	len = 0;
199
200
	while ((ch = cmd_string_getc(s, p)) != endch) {
201
		switch (ch) {
202
		case EOF:
203
			goto error;
204
		case '\\':
205
			if (!esc)
206
				break;
207
			switch (ch = cmd_string_getc(s, p)) {
208
			case EOF:
209
				goto error;
210
			case 'e':
211
				ch = '\033';
212
				break;
213
			case 'r':
214
				ch = '\r';
215
				break;
216
			case 'n':
217
				ch = '\n';
218
				break;
219
			case 't':
220
				ch = '\t';
221
				break;
222
			}
223
			break;
224
		case '$':
225
			if (!esc)
226
				break;
227
			if ((t = cmd_string_variable(s, p)) == NULL)
228
				goto error;
229
			cmd_string_copy(&buf, t, &len);
230
			continue;
231
		}
232
233
		if (len >= SIZE_MAX - 2)
234
			goto error;
235
		buf = xrealloc(buf, len + 1);
236
		buf[len++] = ch;
237
	}
238
239
	buf = xrealloc(buf, len + 1);
240
	buf[len] = '\0';
241
	return (buf);
242
243
error:
244
	free(buf);
245
	return (NULL);
246
}
247
248
char *
249
cmd_string_variable(const char *s, size_t *p)
250
{
251
	int			ch, fch;
252
	char		       *buf, *t;
253
	size_t			len;
254
	struct environ_entry   *envent;
255
256
#define cmd_string_first(ch) ((ch) == '_' || \
257
	((ch) >= 'a' && (ch) <= 'z') || ((ch) >= 'A' && (ch) <= 'Z'))
258
#define cmd_string_other(ch) ((ch) == '_' || \
259
	((ch) >= 'a' && (ch) <= 'z') || ((ch) >= 'A' && (ch) <= 'Z') || \
260
	((ch) >= '0' && (ch) <= '9'))
261
262
	buf = NULL;
263
	len = 0;
264
265
	fch = EOF;
266
	switch (ch = cmd_string_getc(s, p)) {
267
	case EOF:
268
		goto error;
269
	case '{':
270
		fch = '{';
271
272
		ch = cmd_string_getc(s, p);
273
		if (!cmd_string_first(ch))
274
			goto error;
275
		/* FALLTHROUGH */
276
	default:
277
		if (!cmd_string_first(ch)) {
278
			xasprintf(&t, "$%c", ch);
279
			return (t);
280
		}
281
282
		buf = xrealloc(buf, len + 1);
283
		buf[len++] = ch;
284
285
		for (;;) {
286
			ch = cmd_string_getc(s, p);
287
			if (ch == EOF || !cmd_string_other(ch))
288
				break;
289
			else {
290
				if (len >= SIZE_MAX - 3)
291
					goto error;
292
				buf = xrealloc(buf, len + 1);
293
				buf[len++] = ch;
294
			}
295
		}
296
	}
297
298
	if (fch == '{' && ch != '}')
299
		goto error;
300
	if (ch != EOF && fch != '{')
301
		cmd_string_ungetc(p); /* ch */
302
303
	buf = xrealloc(buf, len + 1);
304
	buf[len] = '\0';
305
306
	envent = environ_find(global_environ, buf);
307
	free(buf);
308
	if (envent == NULL)
309
		return (xstrdup(""));
310
	return (xstrdup(envent->value));
311
312
error:
313
	free(buf);
314
	return (NULL);
315
}
316
317
char *
318
cmd_string_expand_tilde(const char *s, size_t *p)
319
{
320
	struct passwd		*pw;
321
	struct environ_entry	*envent;
322
	char			*home, *path, *user, *cp;
323
	int			 last;
324
325
	home = NULL;
326
327
	last = cmd_string_getc(s, p);
328
	if (last == EOF || last == '/' || last == ' '|| last == '\t') {
329
		envent = environ_find(global_environ, "HOME");
330
		if (envent != NULL && *envent->value != '\0')
331
			home = envent->value;
332
		else if ((pw = getpwuid(getuid())) != NULL)
333
			home = pw->pw_dir;
334
	} else {
335
		cmd_string_ungetc(p);
336
337
		cp = user = xmalloc(strlen(s));
338
		for (;;) {
339
			last = cmd_string_getc(s, p);
340
			if (last == EOF || last == '/' || last == ' '|| last == '\t')
341
				break;
342
			*cp++ = last;
343
		}
344
		*cp = '\0';
345
346
		if ((pw = getpwnam(user)) != NULL)
347
			home = pw->pw_dir;
348
		free(user);
349
	}
350
351
	if (home == NULL)
352
		return (NULL);
353
354
	if (last != EOF)
355
		xasprintf(&path, "%s%c", home, last);
356
	else
357
		xasprintf(&path, "%s", home);
358
	return (path);
359
}