Bug Summary

File:src/usr.bin/vi/build/../ex/ex_argv.c
Warning:line 666, column 24
Although the value stored to 'ch' is used in the enclosing expression, the value is never actually read from 'ch'

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))); } }
;
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
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)
) {
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()) {
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);
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);
Although the value stored to 'ch' is used in the enclosing expression, the value is never actually read from 'ch'
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}