Bug Summary

File:src/usr.bin/vi/build/../ex/ex_argv.c
Warning:line 638, column 9
This function call is prohibited after a successful vfork

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name ex_argv.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/usr.bin/vi/build/obj -resource-dir /usr/local/lib/clang/13.0.0 -I /usr/src/usr.bin/vi/build -I /usr/src/usr.bin/vi/build/../include -I . -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/usr.bin/vi/build/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/vmm/scan-build/2022-01-12-194120-40624-1 -x c /usr/src/usr.bin/vi/build/../ex/ex_argv.c
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
29static int argv_alloc(SCR *, size_t);
30static int argv_comp(const void *, const void *);
31static int argv_fexp(SCR *, EXCMD *,
32 char *, size_t, char *, size_t *, char **, size_t *, int);
33static int argv_lexp(SCR *, EXCMD *, char *);
34static 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 */
42int
43argv_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 */
62int
63argv_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 */
85int
86argv_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
111ret: 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 */
122int
123argv_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))); } }
;
1
Assuming 'sp' is not equal to null
2
'?' condition is false
3
Assuming 'L__gp' is not equal to null
4
Assuming the condition is false
5
Taking false branch
6
Assuming 512 is <= field 'tmp_blen'
7
Taking false branch
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)) {
8
Taking false branch
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))
9
Assuming the condition is false
10
Assuming the condition is false
11
Taking false branch
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)
12
Assuming the condition is true
13
'?' condition is true
14
Assuming the condition is false
15
Loop condition is false. Execution continues on line 170
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') {
16
Assuming the condition is true
17
Taking true branch
173 for (; n > 0; --n, ++p)
18
Loop condition is true. Entering loop body
174 if (strchr(mp, *p) != NULL((void *)0))
19
Assuming the condition is true
20
Taking true branch
175 break;
21
Execution continues on line 196
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) {
22
Control jumps to 'case 1:' at line 202
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 == '*') {
23
Assuming the condition is false
24
Taking false branch
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)) {
25
Calling 'argv_sexp'
211 rval = 1;
212 goto err;
213 }
214 p = bp;
215 rval = argv_exp3(sp, excp, p, len);
216 break;
217 }
218
219err: 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 */
230int
231argv_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 */
295static int
296argv_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:
366ins_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 */
391static int
392argv_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)));
433mem: 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 */
456int
457argv_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 */
484static int
485argv_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 */
562static int
563argv_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 */
573static int
574argv_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)
) {
26
Assuming the condition is false
27
'?' condition is false
28
Assuming the condition is false
29
Taking false branch
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)
;
30
Assuming the condition is false
31
'?' condition is false
591 if ((sh = strrchr(sh_path, '/')) == NULL((void *)0))
32
Assuming the condition is false
33
Taking false branch
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) {
34
Assuming the condition is false
35
Taking false branch
614 msgq(sp, M_SYSERR, "pipe");
615 return (1);
616 }
617 if ((ifp = fdopen(std_output[0], "r")) == NULL((void *)0)) {
36
Assuming the condition is false
37
Taking false branch
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()) {
38
Control jumps to 'case 0:' at line 636
627 case -1: /* Error. */
628 msgq(sp, M_SYSERR, "vfork");
629err: 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);
39
This function call is prohibited after a successful vfork
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)) {
686ioerr: msgq_str(sp, M_ERR, sh, "I/O error: %s");
687alloc_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}