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 |
|
|
} |