File: | src/usr.bin/vi/build/../ex/ex_argv.c |
Warning: | line 626, column 16 Call to function 'vfork' is insecure as it can lead to denial of service situations in the parent process. Replace calls to vfork with calls to the safer 'posix_spawn' function |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* $OpenBSD: ex_argv.c,v 1.20 2016/05/27 09:18:12 martijn Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 1993, 1994 |
5 | * The Regents of the University of California. All rights reserved. |
6 | * Copyright (c) 1993, 1994, 1995, 1996 |
7 | * Keith Bostic. All rights reserved. |
8 | * |
9 | * See the LICENSE file for redistribution information. |
10 | */ |
11 | |
12 | #include "config.h" |
13 | |
14 | #include <sys/types.h> |
15 | #include <sys/queue.h> |
16 | |
17 | #include <bitstring.h> |
18 | #include <ctype.h> |
19 | #include <dirent.h> |
20 | #include <errno(*__errno()).h> |
21 | #include <limits.h> |
22 | #include <stdio.h> |
23 | #include <stdlib.h> |
24 | #include <string.h> |
25 | #include <unistd.h> |
26 | |
27 | #include "../common/common.h" |
28 | |
29 | static int argv_alloc(SCR *, size_t); |
30 | static int argv_comp(const void *, const void *); |
31 | static int argv_fexp(SCR *, EXCMD *, |
32 | char *, size_t, char *, size_t *, char **, size_t *, int); |
33 | static int argv_lexp(SCR *, EXCMD *, char *); |
34 | static int argv_sexp(SCR *, char **, size_t *, size_t *); |
35 | |
36 | /* |
37 | * argv_init -- |
38 | * Build a prototype arguments list. |
39 | * |
40 | * PUBLIC: int argv_init(SCR *, EXCMD *); |
41 | */ |
42 | int |
43 | argv_init(SCR *sp, EXCMD *excp) |
44 | { |
45 | EX_PRIVATE *exp; |
46 | |
47 | exp = EXP(sp)((EX_PRIVATE *)((sp)->ex_private)); |
48 | exp->argsoff = 0; |
49 | argv_alloc(sp, 1); |
50 | |
51 | excp->argv = exp->args; |
52 | excp->argc = exp->argsoff; |
53 | return (0); |
54 | } |
55 | |
56 | /* |
57 | * argv_exp0 -- |
58 | * Append a string to the argument list. |
59 | * |
60 | * PUBLIC: int argv_exp0(SCR *, EXCMD *, char *, size_t); |
61 | */ |
62 | int |
63 | argv_exp0(SCR *sp, EXCMD *excp, char *cmd, size_t cmdlen) |
64 | { |
65 | EX_PRIVATE *exp; |
66 | |
67 | exp = EXP(sp)((EX_PRIVATE *)((sp)->ex_private)); |
68 | argv_alloc(sp, cmdlen); |
69 | memcpy(exp->args[exp->argsoff]->bp, cmd, cmdlen); |
70 | exp->args[exp->argsoff]->bp[cmdlen] = '\0'; |
71 | exp->args[exp->argsoff]->len = cmdlen; |
72 | ++exp->argsoff; |
73 | excp->argv = exp->args; |
74 | excp->argc = exp->argsoff; |
75 | return (0); |
76 | } |
77 | |
78 | /* |
79 | * argv_exp1 -- |
80 | * Do file name expansion on a string, and append it to the |
81 | * argument list. |
82 | * |
83 | * PUBLIC: int argv_exp1(SCR *, EXCMD *, char *, size_t, int); |
84 | */ |
85 | int |
86 | argv_exp1(SCR *sp, EXCMD *excp, char *cmd, size_t cmdlen, int is_bang) |
87 | { |
88 | size_t blen, len; |
89 | char *bp, *p, *t; |
90 | |
91 | GET_SPACE_RET(sp, bp, blen, 512){ GS *L__gp = (sp) == ((void *)0) ? ((void *)0) : (sp)->gp ; if (L__gp == ((void *)0) || (((L__gp)->flags) & ((0x0100 )))) { (bp) = ((void *)0); (blen) = 0; { void *L__bincp; if ( ((512)) > ((blen))) { if ((L__bincp = binc(((sp)), ((bp)), &((blen)), ((512)))) == ((void *)0)) return (1); ((bp)) = L__bincp; } }; } else { { void *L__bincp; if (((512)) > ( L__gp->tmp_blen)) { if ((L__bincp = binc(((sp)), (L__gp-> tmp_bp), &(L__gp->tmp_blen), ((512)))) == ((void *)0)) return (1); (L__gp->tmp_bp) = L__bincp; } }; (bp) = L__gp ->tmp_bp; (blen) = L__gp->tmp_blen; (((L__gp)->flags ) |= ((0x0100))); } }; |
92 | |
93 | len = 0; |
94 | if (argv_fexp(sp, excp, cmd, cmdlen, bp, &len, &bp, &blen, is_bang)) { |
95 | FREE_SPACE(sp, bp, blen){ GS *L__gp = (sp) == ((void *)0) ? ((void *)0) : (sp)->gp ; if (L__gp != ((void *)0) && (bp) == L__gp->tmp_bp ) (((L__gp)->flags) &= ~((0x0100))); else free(bp); }; |
96 | return (1); |
97 | } |
98 | |
99 | /* If it's empty, we're done. */ |
100 | if (len != 0) { |
101 | for (p = bp, t = bp + len; p < t; ++p) |
102 | if (!isblank(*p)) |
103 | break; |
104 | if (p == t) |
105 | goto ret; |
106 | } else |
107 | goto ret; |
108 | |
109 | (void)argv_exp0(sp, excp, bp, len); |
110 | |
111 | ret: FREE_SPACE(sp, bp, blen){ GS *L__gp = (sp) == ((void *)0) ? ((void *)0) : (sp)->gp ; if (L__gp != ((void *)0) && (bp) == L__gp->tmp_bp ) (((L__gp)->flags) &= ~((0x0100))); else free(bp); }; |
112 | return (0); |
113 | } |
114 | |
115 | /* |
116 | * argv_exp2 -- |
117 | * Do file name and shell expansion on a string, and append it to |
118 | * the argument list. |
119 | * |
120 | * PUBLIC: int argv_exp2(SCR *, EXCMD *, char *, size_t); |
121 | */ |
122 | int |
123 | argv_exp2(SCR *sp, EXCMD *excp, char *cmd, size_t cmdlen) |
124 | { |
125 | size_t blen, len, n; |
126 | int rval; |
127 | char *bp, *mp, *p; |
128 | |
129 | GET_SPACE_RET(sp, bp, blen, 512){ GS *L__gp = (sp) == ((void *)0) ? ((void *)0) : (sp)->gp ; if (L__gp == ((void *)0) || (((L__gp)->flags) & ((0x0100 )))) { (bp) = ((void *)0); (blen) = 0; { void *L__bincp; if ( ((512)) > ((blen))) { if ((L__bincp = binc(((sp)), ((bp)), &((blen)), ((512)))) == ((void *)0)) return (1); ((bp)) = L__bincp; } }; } else { { void *L__bincp; if (((512)) > ( L__gp->tmp_blen)) { if ((L__bincp = binc(((sp)), (L__gp-> tmp_bp), &(L__gp->tmp_blen), ((512)))) == ((void *)0)) return (1); (L__gp->tmp_bp) = L__bincp; } }; (bp) = L__gp ->tmp_bp; (blen) = L__gp->tmp_blen; (((L__gp)->flags ) |= ((0x0100))); } }; |
130 | |
131 | #define SHELLECHO"echo " "echo " |
132 | #define SHELLOFFSET(sizeof("echo ") - 1) (sizeof(SHELLECHO"echo ") - 1) |
133 | memcpy(bp, SHELLECHO"echo ", SHELLOFFSET(sizeof("echo ") - 1)); |
134 | p = bp + SHELLOFFSET(sizeof("echo ") - 1); |
135 | len = SHELLOFFSET(sizeof("echo ") - 1); |
136 | |
137 | #if defined(DEBUG) && 0 |
138 | TRACE(sp, "file_argv: {%.*s}\n", (int)cmdlen, cmd); |
139 | #endif |
140 | |
141 | if (argv_fexp(sp, excp, cmd, cmdlen, p, &len, &bp, &blen, 0)) { |
142 | rval = 1; |
143 | goto err; |
144 | } |
145 | |
146 | #if defined(DEBUG) && 0 |
147 | TRACE(sp, "before shell: %d: {%s}\n", len, bp); |
148 | #endif |
149 | |
150 | /* |
151 | * Do shell word expansion -- it's very, very hard to figure out what |
152 | * magic characters the user's shell expects. Historically, it was a |
153 | * union of v7 shell and csh meta characters. We match that practice |
154 | * by default, so ":read \%" tries to read a file named '%'. It would |
155 | * make more sense to pass any special characters through the shell, |
156 | * but then, if your shell was csh, the above example will behave |
157 | * differently in nvi than in vi. If you want to get other characters |
158 | * passed through to your shell, change the "meta" option. |
159 | * |
160 | * To avoid a function call per character, we do a first pass through |
161 | * the meta characters looking for characters that aren't expected |
162 | * to be there, and then we can ignore them in the user's argument. |
163 | */ |
164 | if (opts_empty(sp, O_SHELL, 1) || opts_empty(sp, O_SHELLMETA, 1)) |
165 | n = 0; |
166 | else { |
167 | for (p = mp = O_STR(sp, O_SHELLMETA)((((&((sp))->opts[((O_SHELLMETA))])->flags) & ( (0x01))) ? ((sp))->gp->opts[((sp))->opts[((O_SHELLMETA ))].o_cur.val].o_cur.str : ((sp))->opts[((O_SHELLMETA))].o_cur .str); *p != '\0'; ++p) |
168 | if (isblank(*p) || isalnum(*p)) |
169 | break; |
170 | p = bp + SHELLOFFSET(sizeof("echo ") - 1); |
171 | n = len - SHELLOFFSET(sizeof("echo ") - 1); |
172 | if (*p != '\0') { |
173 | for (; n > 0; --n, ++p) |
174 | if (strchr(mp, *p) != NULL((void *)0)) |
175 | break; |
176 | } else |
177 | for (; n > 0; --n, ++p) |
178 | if (!isblank(*p) && |
179 | !isalnum(*p) && strchr(mp, *p) != NULL((void *)0)) |
180 | break; |
181 | } |
182 | |
183 | /* |
184 | * If we found a meta character in the string, fork a shell to expand |
185 | * it. Unfortunately, this is comparatively slow. Historically, it |
186 | * didn't matter much, since users don't enter meta characters as part |
187 | * of pathnames that frequently. The addition of filename completion |
188 | * broke that assumption because it's easy to use. As a result, lots |
189 | * folks have complained that the expansion code is too slow. So, we |
190 | * detect filename completion as a special case, and do it internally. |
191 | * Note that this code assumes that the <asterisk> character is the |
192 | * match-anything meta character. That feels safe -- if anyone writes |
193 | * a shell that doesn't follow that convention, I'd suggest giving them |
194 | * a festive hot-lead enema. |
195 | */ |
196 | switch (n) { |
197 | case 0: |
198 | p = bp + SHELLOFFSET(sizeof("echo ") - 1); |
199 | len -= SHELLOFFSET(sizeof("echo ") - 1); |
200 | rval = argv_exp3(sp, excp, p, len); |
201 | break; |
202 | case 1: |
203 | if (*p == '*') { |
204 | *p = '\0'; |
205 | rval = argv_lexp(sp, excp, bp + SHELLOFFSET(sizeof("echo ") - 1)); |
206 | break; |
207 | } |
208 | /* FALLTHROUGH */ |
209 | default: |
210 | if (argv_sexp(sp, &bp, &blen, &len)) { |
211 | rval = 1; |
212 | goto err; |
213 | } |
214 | p = bp; |
215 | rval = argv_exp3(sp, excp, p, len); |
216 | break; |
217 | } |
218 | |
219 | err: FREE_SPACE(sp, bp, blen){ GS *L__gp = (sp) == ((void *)0) ? ((void *)0) : (sp)->gp ; if (L__gp != ((void *)0) && (bp) == L__gp->tmp_bp ) (((L__gp)->flags) &= ~((0x0100))); else free(bp); }; |
220 | return (rval); |
221 | } |
222 | |
223 | /* |
224 | * argv_exp3 -- |
225 | * Take a string and break it up into an argv, which is appended |
226 | * to the argument list. |
227 | * |
228 | * PUBLIC: int argv_exp3(SCR *, EXCMD *, char *, size_t); |
229 | */ |
230 | int |
231 | argv_exp3(SCR *sp, EXCMD *excp, char *cmd, size_t cmdlen) |
232 | { |
233 | EX_PRIVATE *exp; |
234 | size_t len; |
235 | int ch, off; |
236 | char *ap, *p; |
237 | |
238 | for (exp = EXP(sp)((EX_PRIVATE *)((sp)->ex_private)); cmdlen > 0; ++exp->argsoff) { |
239 | /* Skip any leading whitespace. */ |
240 | for (; cmdlen > 0; --cmdlen, ++cmd) { |
241 | ch = *cmd; |
242 | if (!isblank(ch)) |
243 | break; |
244 | } |
245 | if (cmdlen == 0) |
246 | break; |
247 | |
248 | /* |
249 | * Determine the length of this whitespace delimited |
250 | * argument. |
251 | * |
252 | * QUOTING NOTE: |
253 | * |
254 | * Skip any character preceded by the user's quoting |
255 | * character. |
256 | */ |
257 | for (ap = cmd, len = 0; cmdlen > 0; ++cmd, --cmdlen, ++len) { |
258 | ch = *cmd; |
259 | if (IS_ESCAPE(sp, excp, ch)(((((excp))->flags) & ((0x00020000))) ? (ch) == '\026' : ((unsigned char)((ch)) <= 254 ? ((sp))->gp->special_key [(unsigned char)((ch))] : (unsigned char)((ch)) > ((sp))-> gp->max_special ? 0 : v_key_val(((sp)),((ch)))) == K_VLNEXT ) && cmdlen > 1) { |
260 | ++cmd; |
261 | --cmdlen; |
262 | } else if (isblank(ch)) |
263 | break; |
264 | } |
265 | |
266 | /* |
267 | * Copy the argument into place. |
268 | * |
269 | * QUOTING NOTE: |
270 | * |
271 | * Lose quote chars. |
272 | */ |
273 | argv_alloc(sp, len); |
274 | off = exp->argsoff; |
275 | exp->args[off]->len = len; |
276 | for (p = exp->args[off]->bp; len > 0; --len, *p++ = *ap++) |
277 | if (IS_ESCAPE(sp, excp, *ap)(((((excp))->flags) & ((0x00020000))) ? (*ap) == '\026' : ((unsigned char)((*ap)) <= 254 ? ((sp))->gp->special_key [(unsigned char)((*ap))] : (unsigned char)((*ap)) > ((sp)) ->gp->max_special ? 0 : v_key_val(((sp)),((*ap)))) == K_VLNEXT )) |
278 | ++ap; |
279 | *p = '\0'; |
280 | } |
281 | excp->argv = exp->args; |
282 | excp->argc = exp->argsoff; |
283 | |
284 | #if defined(DEBUG) && 0 |
285 | for (cnt = 0; cnt < exp->argsoff; ++cnt) |
286 | TRACE(sp, "arg %d: {%s}\n", cnt, exp->argv[cnt]); |
287 | #endif |
288 | return (0); |
289 | } |
290 | |
291 | /* |
292 | * argv_fexp -- |
293 | * Do file name and bang command expansion. |
294 | */ |
295 | static int |
296 | argv_fexp(SCR *sp, EXCMD *excp, char *cmd, size_t cmdlen, char *p, |
297 | size_t *lenp, char **bpp, size_t *blenp, int is_bang) |
298 | { |
299 | EX_PRIVATE *exp; |
300 | char *bp, *t; |
301 | size_t blen, len, off, tlen; |
302 | |
303 | /* Replace file name characters. */ |
304 | for (bp = *bpp, blen = *blenp, len = *lenp; cmdlen > 0; --cmdlen, ++cmd) |
305 | switch (*cmd) { |
306 | case '!': |
307 | if (!is_bang) |
308 | goto ins_ch; |
309 | exp = EXP(sp)((EX_PRIVATE *)((sp)->ex_private)); |
310 | if (exp->lastbcomm == NULL((void *)0)) { |
311 | msgq(sp, M_ERR, |
312 | "No previous command to replace \"!\""); |
313 | return (1); |
314 | } |
315 | len += tlen = strlen(exp->lastbcomm); |
316 | off = p - bp; |
317 | ADD_SPACE_RET(sp, bp, blen, len){ GS *L__gp = (sp) == ((void *)0) ? ((void *)0) : (sp)->gp ; if (L__gp != ((void *)0) && (bp) == L__gp->tmp_bp ) { (((L__gp)->flags) &= ~((0x0100))); { void *L__bincp ; if (((len)) > (L__gp->tmp_blen)) { if ((L__bincp = binc (((sp)), (L__gp->tmp_bp), &(L__gp->tmp_blen), ((len )))) == ((void *)0)) return (1); (L__gp->tmp_bp) = L__bincp ; } }; (bp) = L__gp->tmp_bp; (blen) = L__gp->tmp_blen; ( ((L__gp)->flags) |= ((0x0100))); } else { void *L__bincp; if (((len)) > ((blen))) { if ((L__bincp = binc(((sp)), ((bp) ), &((blen)), ((len)))) == ((void *)0)) return (1); ((bp) ) = L__bincp; } }; }; |
318 | p = bp + off; |
319 | memcpy(p, exp->lastbcomm, tlen); |
320 | p += tlen; |
321 | F_SET(excp, E_MODIFY)(((excp)->flags) |= ((0x00200000))); |
322 | break; |
323 | case '%': |
324 | if ((t = sp->frp->name) == NULL((void *)0)) { |
325 | msgq(sp, M_ERR, |
326 | "No filename to substitute for %%"); |
327 | return (1); |
328 | } |
329 | tlen = strlen(t); |
330 | len += tlen; |
331 | off = p - bp; |
332 | ADD_SPACE_RET(sp, bp, blen, len){ GS *L__gp = (sp) == ((void *)0) ? ((void *)0) : (sp)->gp ; if (L__gp != ((void *)0) && (bp) == L__gp->tmp_bp ) { (((L__gp)->flags) &= ~((0x0100))); { void *L__bincp ; if (((len)) > (L__gp->tmp_blen)) { if ((L__bincp = binc (((sp)), (L__gp->tmp_bp), &(L__gp->tmp_blen), ((len )))) == ((void *)0)) return (1); (L__gp->tmp_bp) = L__bincp ; } }; (bp) = L__gp->tmp_bp; (blen) = L__gp->tmp_blen; ( ((L__gp)->flags) |= ((0x0100))); } else { void *L__bincp; if (((len)) > ((blen))) { if ((L__bincp = binc(((sp)), ((bp) ), &((blen)), ((len)))) == ((void *)0)) return (1); ((bp) ) = L__bincp; } }; }; |
333 | p = bp + off; |
334 | memcpy(p, t, tlen); |
335 | p += tlen; |
336 | F_SET(excp, E_MODIFY)(((excp)->flags) |= ((0x00200000))); |
337 | break; |
338 | case '#': |
339 | if ((t = sp->alt_name) == NULL((void *)0)) { |
340 | msgq(sp, M_ERR, |
341 | "No filename to substitute for #"); |
342 | return (1); |
343 | } |
344 | len += tlen = strlen(t); |
345 | off = p - bp; |
346 | ADD_SPACE_RET(sp, bp, blen, len){ GS *L__gp = (sp) == ((void *)0) ? ((void *)0) : (sp)->gp ; if (L__gp != ((void *)0) && (bp) == L__gp->tmp_bp ) { (((L__gp)->flags) &= ~((0x0100))); { void *L__bincp ; if (((len)) > (L__gp->tmp_blen)) { if ((L__bincp = binc (((sp)), (L__gp->tmp_bp), &(L__gp->tmp_blen), ((len )))) == ((void *)0)) return (1); (L__gp->tmp_bp) = L__bincp ; } }; (bp) = L__gp->tmp_bp; (blen) = L__gp->tmp_blen; ( ((L__gp)->flags) |= ((0x0100))); } else { void *L__bincp; if (((len)) > ((blen))) { if ((L__bincp = binc(((sp)), ((bp) ), &((blen)), ((len)))) == ((void *)0)) return (1); ((bp) ) = L__bincp; } }; }; |
347 | p = bp + off; |
348 | memcpy(p, t, tlen); |
349 | p += tlen; |
350 | F_SET(excp, E_MODIFY)(((excp)->flags) |= ((0x00200000))); |
351 | break; |
352 | case '\\': |
353 | /* |
354 | * QUOTING NOTE: |
355 | * |
356 | * Strip any backslashes that protected the file |
357 | * expansion characters. |
358 | */ |
359 | if (cmdlen > 1 && |
360 | (cmd[1] == '%' || cmd[1] == '#' || cmd[1] == '!')) { |
361 | ++cmd; |
362 | --cmdlen; |
363 | } |
364 | /* FALLTHROUGH */ |
365 | default: |
366 | ins_ch: ++len; |
367 | off = p - bp; |
368 | ADD_SPACE_RET(sp, bp, blen, len){ GS *L__gp = (sp) == ((void *)0) ? ((void *)0) : (sp)->gp ; if (L__gp != ((void *)0) && (bp) == L__gp->tmp_bp ) { (((L__gp)->flags) &= ~((0x0100))); { void *L__bincp ; if (((len)) > (L__gp->tmp_blen)) { if ((L__bincp = binc (((sp)), (L__gp->tmp_bp), &(L__gp->tmp_blen), ((len )))) == ((void *)0)) return (1); (L__gp->tmp_bp) = L__bincp ; } }; (bp) = L__gp->tmp_bp; (blen) = L__gp->tmp_blen; ( ((L__gp)->flags) |= ((0x0100))); } else { void *L__bincp; if (((len)) > ((blen))) { if ((L__bincp = binc(((sp)), ((bp) ), &((blen)), ((len)))) == ((void *)0)) return (1); ((bp) ) = L__bincp; } }; }; |
369 | p = bp + off; |
370 | *p++ = *cmd; |
371 | } |
372 | |
373 | /* Nul termination. */ |
374 | ++len; |
375 | off = p - bp; |
376 | ADD_SPACE_RET(sp, bp, blen, len){ GS *L__gp = (sp) == ((void *)0) ? ((void *)0) : (sp)->gp ; if (L__gp != ((void *)0) && (bp) == L__gp->tmp_bp ) { (((L__gp)->flags) &= ~((0x0100))); { void *L__bincp ; if (((len)) > (L__gp->tmp_blen)) { if ((L__bincp = binc (((sp)), (L__gp->tmp_bp), &(L__gp->tmp_blen), ((len )))) == ((void *)0)) return (1); (L__gp->tmp_bp) = L__bincp ; } }; (bp) = L__gp->tmp_bp; (blen) = L__gp->tmp_blen; ( ((L__gp)->flags) |= ((0x0100))); } else { void *L__bincp; if (((len)) > ((blen))) { if ((L__bincp = binc(((sp)), ((bp) ), &((blen)), ((len)))) == ((void *)0)) return (1); ((bp) ) = L__bincp; } }; }; |
377 | p = bp + off; |
378 | *p = '\0'; |
379 | |
380 | /* Return the new string length, buffer, buffer length. */ |
381 | *lenp = len - 1; |
382 | *bpp = bp; |
383 | *blenp = blen; |
384 | return (0); |
385 | } |
386 | |
387 | /* |
388 | * argv_alloc -- |
389 | * Make more space for arguments. |
390 | */ |
391 | static int |
392 | argv_alloc(SCR *sp, size_t len) |
393 | { |
394 | ARGS *ap; |
395 | EX_PRIVATE *exp; |
396 | int cnt, off; |
397 | |
398 | /* |
399 | * Allocate room for another argument, always leaving |
400 | * enough room for an ARGS structure with a length of 0. |
401 | */ |
402 | #define INCREMENT20 20 |
403 | exp = EXP(sp)((EX_PRIVATE *)((sp)->ex_private)); |
404 | off = exp->argsoff; |
405 | if (exp->argscnt == 0 || off + 2 >= exp->argscnt - 1) { |
406 | cnt = exp->argscnt + INCREMENT20; |
407 | REALLOCARRAY(sp, exp->args, cnt, sizeof(ARGS *)){ void *tmpp; if (((tmpp) = (reallocarray((exp->args), (cnt ), (sizeof(ARGS *))))) == ((void *)0)) { msgq((sp), M_SYSERR, ((void *)0)); free(exp->args); } exp->args = tmpp; }; |
408 | if (exp->args == NULL((void *)0)) { |
409 | (void)argv_free(sp); |
410 | goto mem; |
411 | } |
412 | memset(&exp->args[exp->argscnt], 0, INCREMENT20 * sizeof(ARGS *)); |
413 | exp->argscnt = cnt; |
414 | } |
415 | |
416 | /* First argument. */ |
417 | if (exp->args[off] == NULL((void *)0)) { |
418 | CALLOC(sp, exp->args[off], 1, sizeof(ARGS)){ if (((exp->args[off]) = calloc((1), (sizeof(ARGS)))) == ( (void *)0)) msgq((sp), M_SYSERR, ((void *)0)); }; |
419 | if (exp->args[off] == NULL((void *)0)) |
420 | goto mem; |
421 | } |
422 | |
423 | /* First argument buffer. */ |
424 | ap = exp->args[off]; |
425 | ap->len = 0; |
426 | if (ap->blen < len + 1) { |
427 | ap->blen = len + 1; |
428 | REALLOCARRAY(sp, ap->bp, ap->blen, sizeof(CHAR_T)){ void *tmpp; if (((tmpp) = (reallocarray((ap->bp), (ap-> blen), (sizeof(CHAR_T))))) == ((void *)0)) { msgq((sp), M_SYSERR , ((void *)0)); free(ap->bp); } ap->bp = tmpp; }; |
429 | if (ap->bp == NULL((void *)0)) { |
430 | ap->bp = NULL((void *)0); |
431 | ap->blen = 0; |
432 | F_CLR(ap, A_ALLOCATED)(((ap)->flags) &= ~((0x01))); |
433 | mem: msgq(sp, M_SYSERR, NULL((void *)0)); |
434 | return (1); |
435 | } |
436 | F_SET(ap, A_ALLOCATED)(((ap)->flags) |= ((0x01))); |
437 | } |
438 | |
439 | /* Second argument. */ |
440 | if (exp->args[++off] == NULL((void *)0)) { |
441 | CALLOC(sp, exp->args[off], 1, sizeof(ARGS)){ if (((exp->args[off]) = calloc((1), (sizeof(ARGS)))) == ( (void *)0)) msgq((sp), M_SYSERR, ((void *)0)); }; |
442 | if (exp->args[off] == NULL((void *)0)) |
443 | goto mem; |
444 | } |
445 | /* 0 length serves as end-of-argument marker. */ |
446 | exp->args[off]->len = 0; |
447 | return (0); |
448 | } |
449 | |
450 | /* |
451 | * argv_free -- |
452 | * Free up argument structures. |
453 | * |
454 | * PUBLIC: int argv_free(SCR *); |
455 | */ |
456 | int |
457 | argv_free(SCR *sp) |
458 | { |
459 | EX_PRIVATE *exp; |
460 | int off; |
461 | |
462 | exp = EXP(sp)((EX_PRIVATE *)((sp)->ex_private)); |
463 | if (exp->args != NULL((void *)0)) { |
464 | for (off = 0; off < exp->argscnt; ++off) { |
465 | if (exp->args[off] == NULL((void *)0)) |
466 | continue; |
467 | if (F_ISSET(exp->args[off], A_ALLOCATED)(((exp->args[off])->flags) & ((0x01)))) |
468 | free(exp->args[off]->bp); |
469 | free(exp->args[off]); |
470 | } |
471 | free(exp->args); |
472 | } |
473 | exp->args = NULL((void *)0); |
474 | exp->argscnt = 0; |
475 | exp->argsoff = 0; |
476 | return (0); |
477 | } |
478 | |
479 | /* |
480 | * argv_lexp -- |
481 | * Find all file names matching the prefix and append them to the |
482 | * buffer. |
483 | */ |
484 | static int |
485 | argv_lexp(SCR *sp, EXCMD *excp, char *path) |
486 | { |
487 | struct dirent *dp; |
488 | DIR *dirp; |
489 | EX_PRIVATE *exp; |
490 | int off; |
491 | size_t dlen, nlen; |
492 | char *dname, *name, *p; |
493 | |
494 | exp = EXP(sp)((EX_PRIVATE *)((sp)->ex_private)); |
495 | |
496 | /* Set up the name and length for comparison. */ |
497 | if ((p = strrchr(path, '/')) == NULL((void *)0)) { |
498 | dname = "."; |
499 | dlen = 0; |
500 | name = path; |
501 | } else { |
502 | if (p == path) { |
503 | dname = "/"; |
504 | dlen = 1; |
505 | } else { |
506 | *p = '\0'; |
507 | dname = path; |
508 | dlen = strlen(path); |
509 | } |
510 | name = p + 1; |
511 | } |
512 | nlen = strlen(name); |
513 | |
514 | if ((dirp = opendir(dname)) == NULL((void *)0)) { |
515 | msgq_str(sp, M_SYSERR, dname, "%s"); |
516 | return (1); |
517 | } |
518 | for (off = exp->argsoff; (dp = readdir(dirp)) != NULL((void *)0);) { |
519 | if (nlen == 0) { |
520 | if (dp->d_name[0] == '.') |
521 | continue; |
522 | } else { |
523 | if (dp->d_namlen < nlen || |
524 | memcmp(dp->d_name, name, nlen)) |
525 | continue; |
526 | } |
527 | |
528 | /* Directory + name + slash + null. */ |
529 | argv_alloc(sp, dlen + dp->d_namlen + 2); |
530 | p = exp->args[exp->argsoff]->bp; |
531 | if (dlen != 0) { |
532 | memcpy(p, dname, dlen); |
533 | p += dlen; |
534 | if (dlen > 1 || dname[0] != '/') |
535 | *p++ = '/'; |
536 | } |
537 | memcpy(p, dp->d_name, dp->d_namlen + 1); |
538 | exp->args[exp->argsoff]->len = dlen + dp->d_namlen + 1; |
539 | ++exp->argsoff; |
540 | excp->argv = exp->args; |
541 | excp->argc = exp->argsoff; |
542 | } |
543 | closedir(dirp); |
544 | |
545 | if (off == exp->argsoff) { |
546 | /* |
547 | * If we didn't find a match, complain that the expansion |
548 | * failed. We can't know for certain that's the error, but |
549 | * it's a good guess, and it matches historic practice. |
550 | */ |
551 | msgq(sp, M_ERR, "Shell expansion failed"); |
552 | return (1); |
553 | } |
554 | qsort(exp->args + off, exp->argsoff - off, sizeof(ARGS *), argv_comp); |
555 | return (0); |
556 | } |
557 | |
558 | /* |
559 | * argv_comp -- |
560 | * Alphabetic comparison. |
561 | */ |
562 | static int |
563 | argv_comp(const void *a, const void *b) |
564 | { |
565 | return (strcmp((char *)(*(ARGS **)a)->bp, (char *)(*(ARGS **)b)->bp)); |
566 | } |
567 | |
568 | /* |
569 | * argv_sexp -- |
570 | * Fork a shell, pipe a command through it, and read the output into |
571 | * a buffer. |
572 | */ |
573 | static int |
574 | argv_sexp(SCR *sp, char **bpp, size_t *blenp, size_t *lenp) |
575 | { |
576 | enum { SEXP_ERR, SEXP_EXPANSION_ERR, SEXP_OK } rval; |
577 | FILE *ifp; |
578 | pid_t pid; |
579 | size_t blen, len; |
580 | int ch, std_output[2]; |
581 | char *bp, *p, *sh, *sh_path; |
582 | |
583 | /* Secure means no shell access. */ |
584 | if (O_ISSET(sp, O_SECURE)((((&(((sp)))->opts[(((O_SECURE)))])->flags) & ( (0x01))) ? (((sp)))->gp->opts[(((sp)))->opts[(((O_SECURE )))].o_cur.val].o_cur.val : (((sp)))->opts[(((O_SECURE)))] .o_cur.val)) { |
585 | msgq(sp, M_ERR, |
586 | "Shell expansions not supported when the secure edit option is set"); |
587 | return (1); |
588 | } |
589 | |
590 | sh_path = O_STR(sp, O_SHELL)((((&((sp))->opts[((O_SHELL))])->flags) & ((0x01 ))) ? ((sp))->gp->opts[((sp))->opts[((O_SHELL))].o_cur .val].o_cur.str : ((sp))->opts[((O_SHELL))].o_cur.str); |
591 | if ((sh = strrchr(sh_path, '/')) == NULL((void *)0)) |
592 | sh = sh_path; |
593 | else |
594 | ++sh; |
595 | |
596 | /* Local copies of the buffer variables. */ |
597 | bp = *bpp; |
598 | blen = *blenp; |
599 | |
600 | /* |
601 | * There are two different processes running through this code, named |
602 | * the utility (the shell) and the parent. The utility reads standard |
603 | * input and writes standard output and standard error output. The |
604 | * parent writes to the utility, reads its standard output and ignores |
605 | * its standard error output. Historically, the standard error output |
606 | * was discarded by vi, as it produces a lot of noise when file patterns |
607 | * don't match. |
608 | * |
609 | * The parent reads std_output[0], and the utility writes std_output[1]. |
610 | */ |
611 | ifp = NULL((void *)0); |
612 | std_output[0] = std_output[1] = -1; |
613 | if (pipe(std_output) < 0) { |
614 | msgq(sp, M_SYSERR, "pipe"); |
615 | return (1); |
616 | } |
617 | if ((ifp = fdopen(std_output[0], "r")) == NULL((void *)0)) { |
618 | msgq(sp, M_SYSERR, "fdopen"); |
619 | goto err; |
620 | } |
621 | |
622 | /* |
623 | * Do the minimal amount of work possible, the shell is going to run |
624 | * briefly and then exit. We sincerely hope. |
625 | */ |
626 | switch (pid = vfork()) { |
Call to function 'vfork' is insecure as it can lead to denial of service situations in the parent process. Replace calls to vfork with calls to the safer 'posix_spawn' function | |
627 | case -1: /* Error. */ |
628 | msgq(sp, M_SYSERR, "vfork"); |
629 | err: if (ifp != NULL((void *)0)) |
630 | (void)fclose(ifp); |
631 | else if (std_output[0] != -1) |
632 | close(std_output[0]); |
633 | if (std_output[1] != -1) |
634 | close(std_output[0]); |
635 | return (1); |
636 | case 0: /* Utility. */ |
637 | /* Redirect stdout to the write end of the pipe. */ |
638 | (void)dup2(std_output[1], STDOUT_FILENO1); |
639 | |
640 | /* Close the utility's file descriptors. */ |
641 | (void)close(std_output[0]); |
642 | (void)close(std_output[1]); |
643 | (void)close(STDERR_FILENO2); |
644 | |
645 | /* |
646 | * XXX |
647 | * Assume that all shells have -c. |
648 | */ |
649 | execl(sh_path, sh, "-c", bp, (char *)NULL((void *)0)); |
650 | msgq_str(sp, M_SYSERR, sh_path, "Error: execl: %s"); |
651 | _exit(127); |
652 | default: /* Parent. */ |
653 | /* Close the pipe ends the parent won't use. */ |
654 | (void)close(std_output[1]); |
655 | break; |
656 | } |
657 | |
658 | /* |
659 | * Copy process standard output into a buffer. |
660 | * |
661 | * !!! |
662 | * Historic vi apparently discarded leading \n and \r's from |
663 | * the shell output stream. We don't on the grounds that any |
664 | * shell that does that is broken. |
665 | */ |
666 | for (p = bp, len = 0, ch = EOF(-1); |
667 | (ch = getc(ifp)(!__isthreaded ? (--(ifp)->_r < 0 ? __srget(ifp) : (int )(*(ifp)->_p++)) : (getc)(ifp))) != EOF(-1); *p++ = ch, --blen, ++len) |
668 | if (blen < 5) { |
669 | ADD_SPACE_GOTO(sp, bp, *blenp, *blenp * 2){ GS *L__gp = (sp) == ((void *)0) ? ((void *)0) : (sp)->gp ; if (L__gp != ((void *)0) && (bp) == L__gp->tmp_bp ) { (((L__gp)->flags) &= ~((0x0100))); { void *L__bincp ; if (((*blenp * 2)) > (L__gp->tmp_blen)) { if ((L__bincp = binc(((sp)), (L__gp->tmp_bp), &(L__gp->tmp_blen) , ((*blenp * 2)))) == ((void *)0)) goto alloc_err; (L__gp-> tmp_bp) = L__bincp; } }; (bp) = L__gp->tmp_bp; (*blenp) = L__gp ->tmp_blen; (((L__gp)->flags) |= ((0x0100))); } else { void *L__bincp; if (((*blenp * 2)) > ((*blenp))) { if ((L__bincp = binc(((sp)), ((bp)), &((*blenp)), ((*blenp * 2)))) == ( (void *)0)) goto alloc_err; ((bp)) = L__bincp; } }; }; |
670 | p = bp + len; |
671 | blen = *blenp - len; |
672 | } |
673 | |
674 | /* Delete the final newline, nul terminate the string. */ |
675 | if (p > bp && (p[-1] == '\n' || p[-1] == '\r')) { |
676 | --p; |
677 | --len; |
678 | } |
679 | *p = '\0'; |
680 | *lenp = len; |
681 | *bpp = bp; /* *blenp is already updated. */ |
682 | |
683 | if (ferror(ifp)(!__isthreaded ? (((ifp)->_flags & 0x0040) != 0) : (ferror )(ifp))) |
684 | goto ioerr; |
685 | if (fclose(ifp)) { |
686 | ioerr: msgq_str(sp, M_ERR, sh, "I/O error: %s"); |
687 | alloc_err: rval = SEXP_ERR; |
688 | } else |
689 | rval = SEXP_OK; |
690 | |
691 | /* |
692 | * Wait for the process. If the shell process fails (e.g., "echo $q" |
693 | * where q wasn't a defined variable) or if the returned string has |
694 | * no characters or only blank characters, (e.g., "echo $5"), complain |
695 | * that the shell expansion failed. We can't know for certain that's |
696 | * the error, but it's a good guess, and it matches historic practice. |
697 | * This won't catch "echo foo_$5", but that's not a common error and |
698 | * historic vi didn't catch it either. |
699 | */ |
700 | if (proc_wait(sp, pid, sh, 1, 0)) |
701 | rval = SEXP_EXPANSION_ERR; |
702 | |
703 | for (p = bp; len; ++p, --len) |
704 | if (!isblank(*p)) |
705 | break; |
706 | if (len == 0) |
707 | rval = SEXP_EXPANSION_ERR; |
708 | |
709 | if (rval == SEXP_EXPANSION_ERR) |
710 | msgq(sp, M_ERR, "Shell expansion failed"); |
711 | |
712 | return (rval == SEXP_OK ? 0 : 1); |
713 | } |