1e0b8e63eSJohn Marino /*-
2e0b8e63eSJohn Marino * Copyright (c) 1993, 1994
3e0b8e63eSJohn Marino * The Regents of the University of California. All rights reserved.
4e0b8e63eSJohn Marino * Copyright (c) 1993, 1994, 1995, 1996
5e0b8e63eSJohn Marino * Keith Bostic. All rights reserved.
6e0b8e63eSJohn Marino *
7e0b8e63eSJohn Marino * See the LICENSE file for redistribution information.
8e0b8e63eSJohn Marino */
9e0b8e63eSJohn Marino
10e0b8e63eSJohn Marino #include "config.h"
11e0b8e63eSJohn Marino
12e0b8e63eSJohn Marino #include <sys/types.h>
13e0b8e63eSJohn Marino #include <sys/queue.h>
14e0b8e63eSJohn Marino #include <sys/time.h>
15e0b8e63eSJohn Marino
16e0b8e63eSJohn Marino #include <bitstring.h>
17e0b8e63eSJohn Marino #include <ctype.h>
18e0b8e63eSJohn Marino #include <dirent.h>
19e0b8e63eSJohn Marino #include <errno.h>
20e0b8e63eSJohn Marino #include <limits.h>
21e0b8e63eSJohn Marino #include <pwd.h>
22e0b8e63eSJohn Marino #include <stdio.h>
23e0b8e63eSJohn Marino #include <stdlib.h>
24e0b8e63eSJohn Marino #include <string.h>
25e0b8e63eSJohn Marino #include <unistd.h>
26e0b8e63eSJohn Marino
27e0b8e63eSJohn Marino #include "../common/common.h"
28e0b8e63eSJohn Marino
29e0b8e63eSJohn Marino static int argv_alloc(SCR *, size_t);
30e0b8e63eSJohn Marino static int argv_comp(const void *, const void *);
31e0b8e63eSJohn Marino static int argv_fexp(SCR *, EXCMD *,
32e0b8e63eSJohn Marino CHAR_T *, size_t, CHAR_T *, size_t *, CHAR_T **, size_t *, int);
33e0b8e63eSJohn Marino static int argv_sexp(SCR *, CHAR_T **, size_t *, size_t *);
34e0b8e63eSJohn Marino static int argv_flt_user(SCR *, EXCMD *, CHAR_T *, size_t);
35e0b8e63eSJohn Marino
36e0b8e63eSJohn Marino /*
37e0b8e63eSJohn Marino * argv_init --
38e0b8e63eSJohn Marino * Build a prototype arguments list.
39e0b8e63eSJohn Marino *
40e0b8e63eSJohn Marino * PUBLIC: int argv_init(SCR *, EXCMD *);
41e0b8e63eSJohn Marino */
42e0b8e63eSJohn Marino int
argv_init(SCR * sp,EXCMD * excp)43e0b8e63eSJohn Marino argv_init(SCR *sp, EXCMD *excp)
44e0b8e63eSJohn Marino {
45e0b8e63eSJohn Marino EX_PRIVATE *exp;
46e0b8e63eSJohn Marino
47e0b8e63eSJohn Marino exp = EXP(sp);
48e0b8e63eSJohn Marino exp->argsoff = 0;
49e0b8e63eSJohn Marino argv_alloc(sp, 1);
50e0b8e63eSJohn Marino
51e0b8e63eSJohn Marino excp->argv = exp->args;
52e0b8e63eSJohn Marino excp->argc = exp->argsoff;
53e0b8e63eSJohn Marino return (0);
54e0b8e63eSJohn Marino }
55e0b8e63eSJohn Marino
56e0b8e63eSJohn Marino /*
57e0b8e63eSJohn Marino * argv_exp0 --
58e0b8e63eSJohn Marino * Append a string to the argument list.
59e0b8e63eSJohn Marino *
60e0b8e63eSJohn Marino * PUBLIC: int argv_exp0(SCR *, EXCMD *, CHAR_T *, size_t);
61e0b8e63eSJohn Marino */
62e0b8e63eSJohn Marino int
argv_exp0(SCR * sp,EXCMD * excp,CHAR_T * cmd,size_t cmdlen)63e0b8e63eSJohn Marino argv_exp0(SCR *sp, EXCMD *excp, CHAR_T *cmd, size_t cmdlen)
64e0b8e63eSJohn Marino {
65e0b8e63eSJohn Marino EX_PRIVATE *exp;
66e0b8e63eSJohn Marino
67e0b8e63eSJohn Marino exp = EXP(sp);
68e0b8e63eSJohn Marino argv_alloc(sp, cmdlen);
69e0b8e63eSJohn Marino MEMCPY(exp->args[exp->argsoff]->bp, cmd, cmdlen);
70e0b8e63eSJohn Marino exp->args[exp->argsoff]->bp[cmdlen] = '\0';
71e0b8e63eSJohn Marino exp->args[exp->argsoff]->len = cmdlen;
72e0b8e63eSJohn Marino ++exp->argsoff;
73e0b8e63eSJohn Marino excp->argv = exp->args;
74e0b8e63eSJohn Marino excp->argc = exp->argsoff;
75e0b8e63eSJohn Marino return (0);
76e0b8e63eSJohn Marino }
77e0b8e63eSJohn Marino
78e0b8e63eSJohn Marino /*
79e0b8e63eSJohn Marino * argv_exp1 --
80e0b8e63eSJohn Marino * Do file name expansion on a string, and append it to the
81e0b8e63eSJohn Marino * argument list.
82e0b8e63eSJohn Marino *
83e0b8e63eSJohn Marino * PUBLIC: int argv_exp1(SCR *, EXCMD *, CHAR_T *, size_t, int);
84e0b8e63eSJohn Marino */
85e0b8e63eSJohn Marino int
argv_exp1(SCR * sp,EXCMD * excp,CHAR_T * cmd,size_t cmdlen,int is_bang)86e0b8e63eSJohn Marino argv_exp1(SCR *sp, EXCMD *excp, CHAR_T *cmd, size_t cmdlen, int is_bang)
87e0b8e63eSJohn Marino {
88e0b8e63eSJohn Marino EX_PRIVATE *exp;
89e0b8e63eSJohn Marino size_t blen, len;
90e0b8e63eSJohn Marino CHAR_T *p, *t, *bp;
91e0b8e63eSJohn Marino
92e0b8e63eSJohn Marino GET_SPACE_RETW(sp, bp, blen, 512);
93e0b8e63eSJohn Marino
94e0b8e63eSJohn Marino len = 0;
95e0b8e63eSJohn Marino exp = EXP(sp);
96e0b8e63eSJohn Marino if (argv_fexp(sp, excp, cmd, cmdlen, bp, &len, &bp, &blen, is_bang)) {
97e0b8e63eSJohn Marino FREE_SPACEW(sp, bp, blen);
98e0b8e63eSJohn Marino return (1);
99e0b8e63eSJohn Marino }
100e0b8e63eSJohn Marino
101e0b8e63eSJohn Marino /* If it's empty, we're done. */
102e0b8e63eSJohn Marino if (len != 0) {
103e0b8e63eSJohn Marino for (p = bp, t = bp + len; p < t; ++p)
104e0b8e63eSJohn Marino if (!cmdskip(*p))
105e0b8e63eSJohn Marino break;
106e0b8e63eSJohn Marino if (p == t)
107e0b8e63eSJohn Marino goto ret;
108e0b8e63eSJohn Marino } else
109e0b8e63eSJohn Marino goto ret;
110e0b8e63eSJohn Marino
111e0b8e63eSJohn Marino (void)argv_exp0(sp, excp, bp, len);
112e0b8e63eSJohn Marino
113e0b8e63eSJohn Marino ret: FREE_SPACEW(sp, bp, blen);
114e0b8e63eSJohn Marino return (0);
115e0b8e63eSJohn Marino }
116e0b8e63eSJohn Marino
117e0b8e63eSJohn Marino /*
118e0b8e63eSJohn Marino * argv_exp2 --
119e0b8e63eSJohn Marino * Do file name and shell expansion on a string, and append it to
120e0b8e63eSJohn Marino * the argument list.
121e0b8e63eSJohn Marino *
122e0b8e63eSJohn Marino * PUBLIC: int argv_exp2(SCR *, EXCMD *, CHAR_T *, size_t);
123e0b8e63eSJohn Marino */
124e0b8e63eSJohn Marino int
argv_exp2(SCR * sp,EXCMD * excp,CHAR_T * cmd,size_t cmdlen)125e0b8e63eSJohn Marino argv_exp2(SCR *sp, EXCMD *excp, CHAR_T *cmd, size_t cmdlen)
126e0b8e63eSJohn Marino {
127e0b8e63eSJohn Marino size_t blen, len, n;
128e0b8e63eSJohn Marino int rval;
129e0b8e63eSJohn Marino CHAR_T *bp, *p;
130e0b8e63eSJohn Marino
131e0b8e63eSJohn Marino GET_SPACE_RETW(sp, bp, blen, 512);
132e0b8e63eSJohn Marino
133e0b8e63eSJohn Marino #define SHELLECHO L("echo ")
134e0b8e63eSJohn Marino #define SHELLOFFSET (SIZE(SHELLECHO) - 1)
135e0b8e63eSJohn Marino MEMCPY(bp, SHELLECHO, SHELLOFFSET);
136e0b8e63eSJohn Marino p = bp + SHELLOFFSET;
137e0b8e63eSJohn Marino len = SHELLOFFSET;
138e0b8e63eSJohn Marino
139e0b8e63eSJohn Marino #if defined(DEBUG) && 0
140e0b8e63eSJohn Marino TRACE(sp, "file_argv: {%.*s}\n", (int)cmdlen, cmd);
141e0b8e63eSJohn Marino #endif
142e0b8e63eSJohn Marino
143e0b8e63eSJohn Marino if (argv_fexp(sp, excp, cmd, cmdlen, p, &len, &bp, &blen, 0)) {
144e0b8e63eSJohn Marino rval = 1;
145e0b8e63eSJohn Marino goto err;
146e0b8e63eSJohn Marino }
147e0b8e63eSJohn Marino
148e0b8e63eSJohn Marino #if defined(DEBUG) && 0
149e0b8e63eSJohn Marino TRACE(sp, "before shell: %d: {%s}\n", len, bp);
150e0b8e63eSJohn Marino #endif
151e0b8e63eSJohn Marino
152e0b8e63eSJohn Marino /*
153e0b8e63eSJohn Marino * Do shell word expansion -- it's very, very hard to figure out what
154e0b8e63eSJohn Marino * magic characters the user's shell expects. Historically, it was a
155e0b8e63eSJohn Marino * union of v7 shell and csh meta characters. We match that practice
156e0b8e63eSJohn Marino * by default, so ":read \%" tries to read a file named '%'. It would
157e0b8e63eSJohn Marino * make more sense to pass any special characters through the shell,
158e0b8e63eSJohn Marino * but then, if your shell was csh, the above example will behave
159e0b8e63eSJohn Marino * differently in nvi than in vi. If you want to get other characters
160e0b8e63eSJohn Marino * passed through to your shell, change the "meta" option.
161e0b8e63eSJohn Marino */
162e0b8e63eSJohn Marino if (opts_empty(sp, O_SHELL, 1) || opts_empty(sp, O_SHELLMETA, 1))
163e0b8e63eSJohn Marino n = 0;
164e0b8e63eSJohn Marino else {
165e0b8e63eSJohn Marino p = bp + SHELLOFFSET;
166e0b8e63eSJohn Marino n = len - SHELLOFFSET;
167e0b8e63eSJohn Marino for (; n > 0; --n, ++p)
168e0b8e63eSJohn Marino if (IS_SHELLMETA(sp, *p))
169e0b8e63eSJohn Marino break;
170e0b8e63eSJohn Marino }
171e0b8e63eSJohn Marino
172e0b8e63eSJohn Marino /*
173e0b8e63eSJohn Marino * If we found a meta character in the string, fork a shell to expand
174e0b8e63eSJohn Marino * it. Unfortunately, this is comparatively slow. Historically, it
175e0b8e63eSJohn Marino * didn't matter much, since users don't enter meta characters as part
176e0b8e63eSJohn Marino * of pathnames that frequently. The addition of filename completion
177e0b8e63eSJohn Marino * broke that assumption because it's easy to use. To increase the
178e0b8e63eSJohn Marino * completion performance, nvi used to have an internal routine to
179e0b8e63eSJohn Marino * handle "filename*". However, the shell special characters does not
180e0b8e63eSJohn Marino * limit to "shellmeta", so such a hack breaks historic practice.
181e0b8e63eSJohn Marino * After it all, we split the completion logic out from here.
182e0b8e63eSJohn Marino */
183e0b8e63eSJohn Marino switch (n) {
184e0b8e63eSJohn Marino case 0:
185e0b8e63eSJohn Marino p = bp + SHELLOFFSET;
186e0b8e63eSJohn Marino len -= SHELLOFFSET;
187e0b8e63eSJohn Marino rval = argv_exp3(sp, excp, p, len);
188e0b8e63eSJohn Marino break;
189e0b8e63eSJohn Marino default:
190e0b8e63eSJohn Marino if (argv_sexp(sp, &bp, &blen, &len)) {
191e0b8e63eSJohn Marino rval = 1;
192e0b8e63eSJohn Marino goto err;
193e0b8e63eSJohn Marino }
194e0b8e63eSJohn Marino p = bp;
195e0b8e63eSJohn Marino rval = argv_exp3(sp, excp, p, len);
196e0b8e63eSJohn Marino break;
197e0b8e63eSJohn Marino }
198e0b8e63eSJohn Marino
199e0b8e63eSJohn Marino err: FREE_SPACEW(sp, bp, blen);
200e0b8e63eSJohn Marino return (rval);
201e0b8e63eSJohn Marino }
202e0b8e63eSJohn Marino
203e0b8e63eSJohn Marino /*
204e0b8e63eSJohn Marino * argv_exp3 --
205e0b8e63eSJohn Marino * Take a string and break it up into an argv, which is appended
206e0b8e63eSJohn Marino * to the argument list.
207e0b8e63eSJohn Marino *
208e0b8e63eSJohn Marino * PUBLIC: int argv_exp3(SCR *, EXCMD *, CHAR_T *, size_t);
209e0b8e63eSJohn Marino */
210e0b8e63eSJohn Marino int
argv_exp3(SCR * sp,EXCMD * excp,CHAR_T * cmd,size_t cmdlen)211e0b8e63eSJohn Marino argv_exp3(SCR *sp, EXCMD *excp, CHAR_T *cmd, size_t cmdlen)
212e0b8e63eSJohn Marino {
213e0b8e63eSJohn Marino EX_PRIVATE *exp;
214e0b8e63eSJohn Marino size_t len;
215e0b8e63eSJohn Marino int ch, off;
216e0b8e63eSJohn Marino CHAR_T *ap, *p;
217e0b8e63eSJohn Marino
218e0b8e63eSJohn Marino for (exp = EXP(sp); cmdlen > 0; ++exp->argsoff) {
219e0b8e63eSJohn Marino /* Skip any leading whitespace. */
220e0b8e63eSJohn Marino for (; cmdlen > 0; --cmdlen, ++cmd) {
221e0b8e63eSJohn Marino ch = *cmd;
222e0b8e63eSJohn Marino if (!cmdskip(ch))
223e0b8e63eSJohn Marino break;
224e0b8e63eSJohn Marino }
225e0b8e63eSJohn Marino if (cmdlen == 0)
226e0b8e63eSJohn Marino break;
227e0b8e63eSJohn Marino
228e0b8e63eSJohn Marino /*
229e0b8e63eSJohn Marino * Determine the length of this whitespace delimited
230e0b8e63eSJohn Marino * argument.
231e0b8e63eSJohn Marino *
232e0b8e63eSJohn Marino * QUOTING NOTE:
233e0b8e63eSJohn Marino *
234e0b8e63eSJohn Marino * Skip any character preceded by the user's quoting
235e0b8e63eSJohn Marino * character.
236e0b8e63eSJohn Marino */
237e0b8e63eSJohn Marino for (ap = cmd, len = 0; cmdlen > 0; ++cmd, --cmdlen, ++len) {
238e0b8e63eSJohn Marino ch = *cmd;
239e0b8e63eSJohn Marino if (IS_ESCAPE(sp, excp, ch) && cmdlen > 1) {
240e0b8e63eSJohn Marino ++cmd;
241e0b8e63eSJohn Marino --cmdlen;
242e0b8e63eSJohn Marino } else if (cmdskip(ch))
243e0b8e63eSJohn Marino break;
244e0b8e63eSJohn Marino }
245e0b8e63eSJohn Marino
246e0b8e63eSJohn Marino /*
247e0b8e63eSJohn Marino * Copy the argument into place.
248e0b8e63eSJohn Marino *
249e0b8e63eSJohn Marino * QUOTING NOTE:
250e0b8e63eSJohn Marino *
251e0b8e63eSJohn Marino * Lose quote chars.
252e0b8e63eSJohn Marino */
253e0b8e63eSJohn Marino argv_alloc(sp, len);
254e0b8e63eSJohn Marino off = exp->argsoff;
255e0b8e63eSJohn Marino exp->args[off]->len = len;
256e0b8e63eSJohn Marino for (p = exp->args[off]->bp; len > 0; --len, *p++ = *ap++)
257e0b8e63eSJohn Marino if (IS_ESCAPE(sp, excp, *ap))
258e0b8e63eSJohn Marino ++ap;
259e0b8e63eSJohn Marino *p = '\0';
260e0b8e63eSJohn Marino }
261e0b8e63eSJohn Marino excp->argv = exp->args;
262e0b8e63eSJohn Marino excp->argc = exp->argsoff;
263e0b8e63eSJohn Marino
264e0b8e63eSJohn Marino #if defined(DEBUG) && 0
265e0b8e63eSJohn Marino for (cnt = 0; cnt < exp->argsoff; ++cnt)
266e0b8e63eSJohn Marino TRACE(sp, "arg %d: {%s}\n", cnt, exp->argv[cnt]);
267e0b8e63eSJohn Marino #endif
268e0b8e63eSJohn Marino return (0);
269e0b8e63eSJohn Marino }
270e0b8e63eSJohn Marino
271e0b8e63eSJohn Marino /*
272e0b8e63eSJohn Marino * argv_flt_ex --
273e0b8e63eSJohn Marino * Filter the ex commands with a prefix, and append the results to
274e0b8e63eSJohn Marino * the argument list.
275e0b8e63eSJohn Marino *
276e0b8e63eSJohn Marino * PUBLIC: int argv_flt_ex(SCR *, EXCMD *, CHAR_T *, size_t);
277e0b8e63eSJohn Marino */
278e0b8e63eSJohn Marino int
argv_flt_ex(SCR * sp,EXCMD * excp,CHAR_T * cmd,size_t cmdlen)279e0b8e63eSJohn Marino argv_flt_ex(SCR *sp, EXCMD *excp, CHAR_T *cmd, size_t cmdlen)
280e0b8e63eSJohn Marino {
281e0b8e63eSJohn Marino EX_PRIVATE *exp;
282e0b8e63eSJohn Marino EXCMDLIST const *cp;
283e0b8e63eSJohn Marino int off;
284e0b8e63eSJohn Marino size_t len;
285e0b8e63eSJohn Marino
286e0b8e63eSJohn Marino exp = EXP(sp);
287e0b8e63eSJohn Marino
288e0b8e63eSJohn Marino for (off = exp->argsoff, cp = cmds; cp->name != NULL; ++cp) {
289e0b8e63eSJohn Marino len = STRLEN(cp->name);
290e0b8e63eSJohn Marino if (cmdlen > 0 &&
291e0b8e63eSJohn Marino (cmdlen > len || MEMCMP(cmd, cp->name, cmdlen)))
292e0b8e63eSJohn Marino continue;
293e0b8e63eSJohn Marino
294e0b8e63eSJohn Marino /* Copy the matched ex command name. */
295e0b8e63eSJohn Marino argv_alloc(sp, len + 1);
296e0b8e63eSJohn Marino MEMCPY(exp->args[exp->argsoff]->bp, cp->name, len + 1);
297e0b8e63eSJohn Marino exp->args[exp->argsoff]->len = len;
298e0b8e63eSJohn Marino ++exp->argsoff;
299e0b8e63eSJohn Marino excp->argv = exp->args;
300e0b8e63eSJohn Marino excp->argc = exp->argsoff;
301e0b8e63eSJohn Marino }
302e0b8e63eSJohn Marino
303e0b8e63eSJohn Marino return (0);
304e0b8e63eSJohn Marino }
305e0b8e63eSJohn Marino
306e0b8e63eSJohn Marino /*
307e0b8e63eSJohn Marino * argv_flt_user --
308e0b8e63eSJohn Marino * Filter the ~user list on the system with a prefix, and append
309e0b8e63eSJohn Marino * the results to the argument list.
310e0b8e63eSJohn Marino */
311e0b8e63eSJohn Marino static int
argv_flt_user(SCR * sp,EXCMD * excp,CHAR_T * uname,size_t ulen)312e0b8e63eSJohn Marino argv_flt_user(SCR *sp, EXCMD *excp, CHAR_T *uname, size_t ulen)
313e0b8e63eSJohn Marino {
314e0b8e63eSJohn Marino EX_PRIVATE *exp;
315e0b8e63eSJohn Marino struct passwd *pw;
316e0b8e63eSJohn Marino int off;
317e0b8e63eSJohn Marino char *np;
318e0b8e63eSJohn Marino size_t len, nlen;
319e0b8e63eSJohn Marino
320e0b8e63eSJohn Marino exp = EXP(sp);
321e0b8e63eSJohn Marino off = exp->argsoff;
322e0b8e63eSJohn Marino
323e0b8e63eSJohn Marino /* The input must come with a leading '~'. */
324e0b8e63eSJohn Marino INT2CHAR(sp, uname + 1, ulen - 1, np, nlen);
325e0b8e63eSJohn Marino if ((np = v_strdup(sp, np, nlen)) == NULL)
326e0b8e63eSJohn Marino return (1);
327e0b8e63eSJohn Marino
328e0b8e63eSJohn Marino setpwent();
329e0b8e63eSJohn Marino while ((pw = getpwent()) != NULL) {
330e0b8e63eSJohn Marino len = strlen(pw->pw_name);
331e0b8e63eSJohn Marino if (nlen > 0 &&
332e0b8e63eSJohn Marino (nlen > len || memcmp(np, pw->pw_name, nlen)))
333e0b8e63eSJohn Marino continue;
334e0b8e63eSJohn Marino
335e0b8e63eSJohn Marino /* Copy '~' + the matched user name. */
336e0b8e63eSJohn Marino CHAR2INT(sp, pw->pw_name, len + 1, uname, ulen);
337e0b8e63eSJohn Marino argv_alloc(sp, ulen + 1);
338e0b8e63eSJohn Marino exp->args[exp->argsoff]->bp[0] = '~';
339e0b8e63eSJohn Marino MEMCPY(exp->args[exp->argsoff]->bp + 1, uname, ulen);
340e0b8e63eSJohn Marino exp->args[exp->argsoff]->len = ulen;
341e0b8e63eSJohn Marino ++exp->argsoff;
342e0b8e63eSJohn Marino excp->argv = exp->args;
343e0b8e63eSJohn Marino excp->argc = exp->argsoff;
344e0b8e63eSJohn Marino }
345e0b8e63eSJohn Marino endpwent();
346e0b8e63eSJohn Marino free(np);
347e0b8e63eSJohn Marino
348e0b8e63eSJohn Marino qsort(exp->args + off, exp->argsoff - off, sizeof(ARGS *), argv_comp);
349e0b8e63eSJohn Marino return (0);
350e0b8e63eSJohn Marino }
351e0b8e63eSJohn Marino
352e0b8e63eSJohn Marino /*
353e0b8e63eSJohn Marino * argv_fexp --
354e0b8e63eSJohn Marino * Do file name and bang command expansion.
355e0b8e63eSJohn Marino */
356e0b8e63eSJohn Marino static int
argv_fexp(SCR * sp,EXCMD * excp,CHAR_T * cmd,size_t cmdlen,CHAR_T * p,size_t * lenp,CHAR_T ** bpp,size_t * blenp,int is_bang)357e0b8e63eSJohn Marino argv_fexp(SCR *sp, EXCMD *excp, CHAR_T *cmd, size_t cmdlen, CHAR_T *p, size_t *lenp, CHAR_T **bpp, size_t *blenp, int is_bang)
358e0b8e63eSJohn Marino {
359e0b8e63eSJohn Marino EX_PRIVATE *exp;
360e0b8e63eSJohn Marino char *t;
361e0b8e63eSJohn Marino size_t blen, len, off, tlen;
362e0b8e63eSJohn Marino CHAR_T *bp;
363e0b8e63eSJohn Marino CHAR_T *wp;
364e0b8e63eSJohn Marino size_t wlen;
365e0b8e63eSJohn Marino
366e0b8e63eSJohn Marino /* Replace file name characters. */
367e0b8e63eSJohn Marino for (bp = *bpp, blen = *blenp, len = *lenp; cmdlen > 0; --cmdlen, ++cmd)
368e0b8e63eSJohn Marino switch (*cmd) {
369e0b8e63eSJohn Marino case '!':
370e0b8e63eSJohn Marino if (!is_bang)
371e0b8e63eSJohn Marino goto ins_ch;
372e0b8e63eSJohn Marino exp = EXP(sp);
373e0b8e63eSJohn Marino if (exp->lastbcomm == NULL) {
374e0b8e63eSJohn Marino msgq(sp, M_ERR,
375e0b8e63eSJohn Marino "115|No previous command to replace \"!\"");
376e0b8e63eSJohn Marino return (1);
377e0b8e63eSJohn Marino }
378e0b8e63eSJohn Marino len += tlen = STRLEN(exp->lastbcomm);
379e0b8e63eSJohn Marino off = p - bp;
380e0b8e63eSJohn Marino ADD_SPACE_RETW(sp, bp, blen, len);
381e0b8e63eSJohn Marino p = bp + off;
382e0b8e63eSJohn Marino MEMCPY(p, exp->lastbcomm, tlen);
383e0b8e63eSJohn Marino p += tlen;
384e0b8e63eSJohn Marino F_SET(excp, E_MODIFY);
385e0b8e63eSJohn Marino break;
386e0b8e63eSJohn Marino case '%':
387e0b8e63eSJohn Marino if ((t = sp->frp->name) == NULL) {
388e0b8e63eSJohn Marino msgq(sp, M_ERR,
389e0b8e63eSJohn Marino "116|No filename to substitute for %%");
390e0b8e63eSJohn Marino return (1);
391e0b8e63eSJohn Marino }
392e0b8e63eSJohn Marino tlen = strlen(t);
393e0b8e63eSJohn Marino len += tlen;
394e0b8e63eSJohn Marino off = p - bp;
395e0b8e63eSJohn Marino ADD_SPACE_RETW(sp, bp, blen, len);
396e0b8e63eSJohn Marino p = bp + off;
397e0b8e63eSJohn Marino CHAR2INT(sp, t, tlen, wp, wlen);
398e0b8e63eSJohn Marino MEMCPY(p, wp, wlen);
399e0b8e63eSJohn Marino p += wlen;
400e0b8e63eSJohn Marino F_SET(excp, E_MODIFY);
401e0b8e63eSJohn Marino break;
402e0b8e63eSJohn Marino case '#':
403e0b8e63eSJohn Marino if ((t = sp->alt_name) == NULL) {
404e0b8e63eSJohn Marino msgq(sp, M_ERR,
405e0b8e63eSJohn Marino "117|No filename to substitute for #");
406e0b8e63eSJohn Marino return (1);
407e0b8e63eSJohn Marino }
408e0b8e63eSJohn Marino len += tlen = strlen(t);
409e0b8e63eSJohn Marino off = p - bp;
410e0b8e63eSJohn Marino ADD_SPACE_RETW(sp, bp, blen, len);
411e0b8e63eSJohn Marino p = bp + off;
412e0b8e63eSJohn Marino CHAR2INT(sp, t, tlen, wp, wlen);
413e0b8e63eSJohn Marino MEMCPY(p, wp, wlen);
414e0b8e63eSJohn Marino p += wlen;
415e0b8e63eSJohn Marino F_SET(excp, E_MODIFY);
416e0b8e63eSJohn Marino break;
417e0b8e63eSJohn Marino case '\\':
418e0b8e63eSJohn Marino /*
419e0b8e63eSJohn Marino * QUOTING NOTE:
420e0b8e63eSJohn Marino *
421e0b8e63eSJohn Marino * Strip any backslashes that protected the file
422e0b8e63eSJohn Marino * expansion characters.
423e0b8e63eSJohn Marino */
424e0b8e63eSJohn Marino if (cmdlen > 1 &&
425e0b8e63eSJohn Marino (cmd[1] == '%' || cmd[1] == '#' || cmd[1] == '!')) {
426e0b8e63eSJohn Marino ++cmd;
427e0b8e63eSJohn Marino --cmdlen;
428e0b8e63eSJohn Marino }
429e0b8e63eSJohn Marino /* FALLTHROUGH */
430e0b8e63eSJohn Marino default:
431e0b8e63eSJohn Marino ins_ch: ++len;
432e0b8e63eSJohn Marino off = p - bp;
433e0b8e63eSJohn Marino ADD_SPACE_RETW(sp, bp, blen, len);
434e0b8e63eSJohn Marino p = bp + off;
435e0b8e63eSJohn Marino *p++ = *cmd;
436e0b8e63eSJohn Marino }
437e0b8e63eSJohn Marino
438e0b8e63eSJohn Marino /* Nul termination. */
439e0b8e63eSJohn Marino ++len;
440e0b8e63eSJohn Marino off = p - bp;
441e0b8e63eSJohn Marino ADD_SPACE_RETW(sp, bp, blen, len);
442e0b8e63eSJohn Marino p = bp + off;
443e0b8e63eSJohn Marino *p = '\0';
444e0b8e63eSJohn Marino
445e0b8e63eSJohn Marino /* Return the new string length, buffer, buffer length. */
446e0b8e63eSJohn Marino *lenp = len - 1;
447e0b8e63eSJohn Marino *bpp = bp;
448e0b8e63eSJohn Marino *blenp = blen;
449e0b8e63eSJohn Marino return (0);
450e0b8e63eSJohn Marino }
451e0b8e63eSJohn Marino
452e0b8e63eSJohn Marino /*
453e0b8e63eSJohn Marino * argv_alloc --
454e0b8e63eSJohn Marino * Make more space for arguments.
455e0b8e63eSJohn Marino */
456e0b8e63eSJohn Marino static int
argv_alloc(SCR * sp,size_t len)457e0b8e63eSJohn Marino argv_alloc(SCR *sp, size_t len)
458e0b8e63eSJohn Marino {
459e0b8e63eSJohn Marino ARGS *ap;
460e0b8e63eSJohn Marino EX_PRIVATE *exp;
461e0b8e63eSJohn Marino int cnt, off;
462e0b8e63eSJohn Marino
463e0b8e63eSJohn Marino /*
464e0b8e63eSJohn Marino * Allocate room for another argument, always leaving
465e0b8e63eSJohn Marino * enough room for an ARGS structure with a length of 0.
466e0b8e63eSJohn Marino */
467e0b8e63eSJohn Marino #define INCREMENT 20
468e0b8e63eSJohn Marino exp = EXP(sp);
469e0b8e63eSJohn Marino off = exp->argsoff;
470e0b8e63eSJohn Marino if (exp->argscnt == 0 || off + 2 >= exp->argscnt - 1) {
471e0b8e63eSJohn Marino cnt = exp->argscnt + INCREMENT;
472e0b8e63eSJohn Marino REALLOC(sp, exp->args, ARGS **, cnt * sizeof(ARGS *));
473e0b8e63eSJohn Marino if (exp->args == NULL) {
474e0b8e63eSJohn Marino (void)argv_free(sp);
475e0b8e63eSJohn Marino goto mem;
476e0b8e63eSJohn Marino }
477e0b8e63eSJohn Marino memset(&exp->args[exp->argscnt], 0, INCREMENT * sizeof(ARGS *));
478e0b8e63eSJohn Marino exp->argscnt = cnt;
479e0b8e63eSJohn Marino }
480e0b8e63eSJohn Marino
481e0b8e63eSJohn Marino /* First argument. */
482e0b8e63eSJohn Marino if (exp->args[off] == NULL) {
483*b1ac2ebbSDaniel Fojt CALLOC(sp, exp->args[off], 1, sizeof(ARGS));
484e0b8e63eSJohn Marino if (exp->args[off] == NULL)
485e0b8e63eSJohn Marino goto mem;
486e0b8e63eSJohn Marino }
487e0b8e63eSJohn Marino
488e0b8e63eSJohn Marino /* First argument buffer. */
489e0b8e63eSJohn Marino ap = exp->args[off];
490e0b8e63eSJohn Marino ap->len = 0;
491e0b8e63eSJohn Marino if (ap->blen < len + 1) {
492e0b8e63eSJohn Marino ap->blen = len + 1;
493e0b8e63eSJohn Marino REALLOC(sp, ap->bp, CHAR_T *, ap->blen * sizeof(CHAR_T));
494e0b8e63eSJohn Marino if (ap->bp == NULL) {
495e0b8e63eSJohn Marino ap->bp = NULL;
496e0b8e63eSJohn Marino ap->blen = 0;
497e0b8e63eSJohn Marino F_CLR(ap, A_ALLOCATED);
498e0b8e63eSJohn Marino mem: msgq(sp, M_SYSERR, NULL);
499e0b8e63eSJohn Marino return (1);
500e0b8e63eSJohn Marino }
501e0b8e63eSJohn Marino F_SET(ap, A_ALLOCATED);
502e0b8e63eSJohn Marino }
503e0b8e63eSJohn Marino
504e0b8e63eSJohn Marino /* Second argument. */
505e0b8e63eSJohn Marino if (exp->args[++off] == NULL) {
506*b1ac2ebbSDaniel Fojt CALLOC(sp, exp->args[off], 1, sizeof(ARGS));
507e0b8e63eSJohn Marino if (exp->args[off] == NULL)
508e0b8e63eSJohn Marino goto mem;
509e0b8e63eSJohn Marino }
510e0b8e63eSJohn Marino /* 0 length serves as end-of-argument marker. */
511e0b8e63eSJohn Marino exp->args[off]->len = 0;
512e0b8e63eSJohn Marino return (0);
513e0b8e63eSJohn Marino }
514e0b8e63eSJohn Marino
515e0b8e63eSJohn Marino /*
516e0b8e63eSJohn Marino * argv_free --
517e0b8e63eSJohn Marino * Free up argument structures.
518e0b8e63eSJohn Marino *
519e0b8e63eSJohn Marino * PUBLIC: int argv_free(SCR *);
520e0b8e63eSJohn Marino */
521e0b8e63eSJohn Marino int
argv_free(SCR * sp)522e0b8e63eSJohn Marino argv_free(SCR *sp)
523e0b8e63eSJohn Marino {
524e0b8e63eSJohn Marino EX_PRIVATE *exp;
525e0b8e63eSJohn Marino int off;
526e0b8e63eSJohn Marino
527e0b8e63eSJohn Marino exp = EXP(sp);
528e0b8e63eSJohn Marino if (exp->args != NULL) {
529e0b8e63eSJohn Marino for (off = 0; off < exp->argscnt; ++off) {
530e0b8e63eSJohn Marino if (exp->args[off] == NULL)
531e0b8e63eSJohn Marino continue;
532e0b8e63eSJohn Marino if (F_ISSET(exp->args[off], A_ALLOCATED))
533e0b8e63eSJohn Marino free(exp->args[off]->bp);
534e0b8e63eSJohn Marino free(exp->args[off]);
535e0b8e63eSJohn Marino }
536e0b8e63eSJohn Marino free(exp->args);
537e0b8e63eSJohn Marino }
538e0b8e63eSJohn Marino exp->args = NULL;
539e0b8e63eSJohn Marino exp->argscnt = 0;
540e0b8e63eSJohn Marino exp->argsoff = 0;
541e0b8e63eSJohn Marino return (0);
542e0b8e63eSJohn Marino }
543e0b8e63eSJohn Marino
544e0b8e63eSJohn Marino /*
545e0b8e63eSJohn Marino * argv_flt_path --
546e0b8e63eSJohn Marino * Find all file names matching the prefix and append them to the
547e0b8e63eSJohn Marino * argument list.
548e0b8e63eSJohn Marino *
549e0b8e63eSJohn Marino * PUBLIC: int argv_flt_path(SCR *, EXCMD *, CHAR_T *, size_t);
550e0b8e63eSJohn Marino */
551e0b8e63eSJohn Marino int
argv_flt_path(SCR * sp,EXCMD * excp,CHAR_T * path,size_t plen)552e0b8e63eSJohn Marino argv_flt_path(SCR *sp, EXCMD *excp, CHAR_T *path, size_t plen)
553e0b8e63eSJohn Marino {
554e0b8e63eSJohn Marino struct dirent *dp;
555e0b8e63eSJohn Marino DIR *dirp;
556e0b8e63eSJohn Marino EX_PRIVATE *exp;
557e0b8e63eSJohn Marino int off;
558e0b8e63eSJohn Marino size_t dlen, len, nlen;
559e0b8e63eSJohn Marino CHAR_T *dname;
560e0b8e63eSJohn Marino CHAR_T *p, *np, *n;
561e0b8e63eSJohn Marino char *name, *tp, *epd = NULL;
562e0b8e63eSJohn Marino CHAR_T *wp;
563e0b8e63eSJohn Marino size_t wlen;
564e0b8e63eSJohn Marino
565e0b8e63eSJohn Marino exp = EXP(sp);
566e0b8e63eSJohn Marino
567e0b8e63eSJohn Marino /* Set up the name and length for comparison. */
568e0b8e63eSJohn Marino if ((path = v_wstrdup(sp, path, plen)) == NULL)
569e0b8e63eSJohn Marino return (1);
570e0b8e63eSJohn Marino if ((p = STRRCHR(path, '/')) == NULL) {
571e0b8e63eSJohn Marino if (*path == '~') {
572e0b8e63eSJohn Marino int rc;
573e0b8e63eSJohn Marino
574e0b8e63eSJohn Marino /* Filter ~user list instead. */
575e0b8e63eSJohn Marino rc = argv_flt_user(sp, excp, path, plen);
576e0b8e63eSJohn Marino free(path);
577e0b8e63eSJohn Marino return (rc);
578e0b8e63eSJohn Marino }
579e0b8e63eSJohn Marino dname = L(".");
580e0b8e63eSJohn Marino dlen = 0;
581e0b8e63eSJohn Marino np = path;
582e0b8e63eSJohn Marino } else {
583e0b8e63eSJohn Marino if (p == path) {
584e0b8e63eSJohn Marino dname = L("/");
585e0b8e63eSJohn Marino dlen = 1;
586e0b8e63eSJohn Marino } else {
587e0b8e63eSJohn Marino *p = '\0';
588e0b8e63eSJohn Marino dname = path;
589e0b8e63eSJohn Marino dlen = p - path;
590e0b8e63eSJohn Marino }
591e0b8e63eSJohn Marino np = p + 1;
592e0b8e63eSJohn Marino }
593e0b8e63eSJohn Marino
594e0b8e63eSJohn Marino INT2CHAR(sp, dname, dlen + 1, tp, nlen);
595e0b8e63eSJohn Marino if ((epd = expanduser(tp)) != NULL)
596e0b8e63eSJohn Marino tp = epd;
597e0b8e63eSJohn Marino if ((dirp = opendir(tp)) == NULL) {
598e0b8e63eSJohn Marino free(epd);
599e0b8e63eSJohn Marino free(path);
600e0b8e63eSJohn Marino return (1);
601e0b8e63eSJohn Marino }
602e0b8e63eSJohn Marino free(epd);
603e0b8e63eSJohn Marino
604e0b8e63eSJohn Marino INT2CHAR(sp, np, STRLEN(np), tp, nlen);
605e0b8e63eSJohn Marino if ((name = v_strdup(sp, tp, nlen)) == NULL) {
606e0b8e63eSJohn Marino free(path);
607e0b8e63eSJohn Marino return (1);
608e0b8e63eSJohn Marino }
609e0b8e63eSJohn Marino
610e0b8e63eSJohn Marino for (off = exp->argsoff; (dp = readdir(dirp)) != NULL;) {
611e0b8e63eSJohn Marino if (nlen == 0) {
612e0b8e63eSJohn Marino if (dp->d_name[0] == '.')
613e0b8e63eSJohn Marino continue;
614*b1ac2ebbSDaniel Fojt #ifdef HAVE_DIRENT_D_NAMLEN
615e0b8e63eSJohn Marino len = dp->d_namlen;
616*b1ac2ebbSDaniel Fojt #else
617*b1ac2ebbSDaniel Fojt len = strlen(dp->d_name);
618*b1ac2ebbSDaniel Fojt #endif
619e0b8e63eSJohn Marino } else {
620*b1ac2ebbSDaniel Fojt #ifdef HAVE_DIRENT_D_NAMLEN
621e0b8e63eSJohn Marino len = dp->d_namlen;
622*b1ac2ebbSDaniel Fojt #else
623*b1ac2ebbSDaniel Fojt len = strlen(dp->d_name);
624*b1ac2ebbSDaniel Fojt #endif
625e0b8e63eSJohn Marino if (len < nlen || memcmp(dp->d_name, name, nlen))
626e0b8e63eSJohn Marino continue;
627e0b8e63eSJohn Marino }
628e0b8e63eSJohn Marino
629e0b8e63eSJohn Marino /* Directory + name + slash + null. */
630e0b8e63eSJohn Marino CHAR2INT(sp, dp->d_name, len + 1, wp, wlen);
631e0b8e63eSJohn Marino argv_alloc(sp, dlen + wlen + 1);
632e0b8e63eSJohn Marino n = exp->args[exp->argsoff]->bp;
633e0b8e63eSJohn Marino if (dlen != 0) {
634e0b8e63eSJohn Marino MEMCPY(n, dname, dlen);
635e0b8e63eSJohn Marino n += dlen;
636e0b8e63eSJohn Marino if (dlen > 1 || dname[0] != '/')
637e0b8e63eSJohn Marino *n++ = '/';
638e0b8e63eSJohn Marino exp->args[exp->argsoff]->len = dlen + 1;
639e0b8e63eSJohn Marino }
640e0b8e63eSJohn Marino MEMCPY(n, wp, wlen);
641e0b8e63eSJohn Marino exp->args[exp->argsoff]->len += wlen - 1;
642e0b8e63eSJohn Marino ++exp->argsoff;
643e0b8e63eSJohn Marino excp->argv = exp->args;
644e0b8e63eSJohn Marino excp->argc = exp->argsoff;
645e0b8e63eSJohn Marino }
646e0b8e63eSJohn Marino closedir(dirp);
647e0b8e63eSJohn Marino free(name);
648e0b8e63eSJohn Marino free(path);
649e0b8e63eSJohn Marino
650e0b8e63eSJohn Marino qsort(exp->args + off, exp->argsoff - off, sizeof(ARGS *), argv_comp);
651e0b8e63eSJohn Marino return (0);
652e0b8e63eSJohn Marino }
653e0b8e63eSJohn Marino
654e0b8e63eSJohn Marino /*
655e0b8e63eSJohn Marino * argv_comp --
656e0b8e63eSJohn Marino * Alphabetic comparison.
657e0b8e63eSJohn Marino */
658e0b8e63eSJohn Marino static int
argv_comp(const void * a,const void * b)659e0b8e63eSJohn Marino argv_comp(const void *a, const void *b)
660e0b8e63eSJohn Marino {
661e0b8e63eSJohn Marino return (STRCMP((*(ARGS **)a)->bp, (*(ARGS **)b)->bp));
662e0b8e63eSJohn Marino }
663e0b8e63eSJohn Marino
664e0b8e63eSJohn Marino /*
665e0b8e63eSJohn Marino * argv_sexp --
666e0b8e63eSJohn Marino * Fork a shell, pipe a command through it, and read the output into
667e0b8e63eSJohn Marino * a buffer.
668e0b8e63eSJohn Marino */
669e0b8e63eSJohn Marino static int
argv_sexp(SCR * sp,CHAR_T ** bpp,size_t * blenp,size_t * lenp)670e0b8e63eSJohn Marino argv_sexp(SCR *sp, CHAR_T **bpp, size_t *blenp, size_t *lenp)
671e0b8e63eSJohn Marino {
672e0b8e63eSJohn Marino enum { SEXP_ERR, SEXP_EXPANSION_ERR, SEXP_OK } rval;
673e0b8e63eSJohn Marino FILE *ifp;
674e0b8e63eSJohn Marino pid_t pid;
675e0b8e63eSJohn Marino size_t blen, len;
676e0b8e63eSJohn Marino int ch, std_output[2];
677e0b8e63eSJohn Marino CHAR_T *bp, *p;
678e0b8e63eSJohn Marino char *sh, *sh_path;
679e0b8e63eSJohn Marino char *np;
680e0b8e63eSJohn Marino size_t nlen;
681e0b8e63eSJohn Marino
682e0b8e63eSJohn Marino /* Secure means no shell access. */
683e0b8e63eSJohn Marino if (O_ISSET(sp, O_SECURE)) {
684e0b8e63eSJohn Marino msgq(sp, M_ERR,
685e0b8e63eSJohn Marino "289|Shell expansions not supported when the secure edit option is set");
686e0b8e63eSJohn Marino return (1);
687e0b8e63eSJohn Marino }
688e0b8e63eSJohn Marino
689e0b8e63eSJohn Marino sh_path = O_STR(sp, O_SHELL);
690e0b8e63eSJohn Marino if ((sh = strrchr(sh_path, '/')) == NULL)
691e0b8e63eSJohn Marino sh = sh_path;
692e0b8e63eSJohn Marino else
693e0b8e63eSJohn Marino ++sh;
694e0b8e63eSJohn Marino
695e0b8e63eSJohn Marino /* Local copies of the buffer variables. */
696e0b8e63eSJohn Marino bp = *bpp;
697e0b8e63eSJohn Marino blen = *blenp;
698e0b8e63eSJohn Marino
699e0b8e63eSJohn Marino /*
700e0b8e63eSJohn Marino * There are two different processes running through this code, named
701e0b8e63eSJohn Marino * the utility (the shell) and the parent. The utility reads standard
702e0b8e63eSJohn Marino * input and writes standard output and standard error output. The
703e0b8e63eSJohn Marino * parent writes to the utility, reads its standard output and ignores
704e0b8e63eSJohn Marino * its standard error output. Historically, the standard error output
705e0b8e63eSJohn Marino * was discarded by vi, as it produces a lot of noise when file patterns
706e0b8e63eSJohn Marino * don't match.
707e0b8e63eSJohn Marino *
708e0b8e63eSJohn Marino * The parent reads std_output[0], and the utility writes std_output[1].
709e0b8e63eSJohn Marino */
710e0b8e63eSJohn Marino ifp = NULL;
711e0b8e63eSJohn Marino std_output[0] = std_output[1] = -1;
712e0b8e63eSJohn Marino if (pipe(std_output) < 0) {
713e0b8e63eSJohn Marino msgq(sp, M_SYSERR, "pipe");
714e0b8e63eSJohn Marino return (1);
715e0b8e63eSJohn Marino }
716e0b8e63eSJohn Marino if ((ifp = fdopen(std_output[0], "r")) == NULL) {
717e0b8e63eSJohn Marino msgq(sp, M_SYSERR, "fdopen");
718e0b8e63eSJohn Marino goto err;
719e0b8e63eSJohn Marino }
720e0b8e63eSJohn Marino
721e0b8e63eSJohn Marino /*
722e0b8e63eSJohn Marino * Do the minimal amount of work possible, the shell is going to run
723e0b8e63eSJohn Marino * briefly and then exit. We sincerely hope.
724e0b8e63eSJohn Marino */
725e0b8e63eSJohn Marino switch (pid = vfork()) {
726e0b8e63eSJohn Marino case -1: /* Error. */
727e0b8e63eSJohn Marino msgq(sp, M_SYSERR, "vfork");
728e0b8e63eSJohn Marino err: if (ifp != NULL)
729e0b8e63eSJohn Marino (void)fclose(ifp);
730e0b8e63eSJohn Marino else if (std_output[0] != -1)
731e0b8e63eSJohn Marino close(std_output[0]);
732e0b8e63eSJohn Marino if (std_output[1] != -1)
733e0b8e63eSJohn Marino close(std_output[0]);
734e0b8e63eSJohn Marino return (1);
735e0b8e63eSJohn Marino case 0: /* Utility. */
736e0b8e63eSJohn Marino /* Redirect stdout to the write end of the pipe. */
737e0b8e63eSJohn Marino (void)dup2(std_output[1], STDOUT_FILENO);
738e0b8e63eSJohn Marino
739e0b8e63eSJohn Marino /* Close the utility's file descriptors. */
740e0b8e63eSJohn Marino (void)close(std_output[0]);
741e0b8e63eSJohn Marino (void)close(std_output[1]);
742e0b8e63eSJohn Marino (void)close(STDERR_FILENO);
743e0b8e63eSJohn Marino
744e0b8e63eSJohn Marino /*
745e0b8e63eSJohn Marino * XXX
746e0b8e63eSJohn Marino * Assume that all shells have -c.
747e0b8e63eSJohn Marino */
748e0b8e63eSJohn Marino INT2CHAR(sp, bp, STRLEN(bp)+1, np, nlen);
749e0b8e63eSJohn Marino execl(sh_path, sh, "-c", np, (char *)NULL);
750e0b8e63eSJohn Marino msgq_str(sp, M_SYSERR, sh_path, "118|Error: execl: %s");
751e0b8e63eSJohn Marino _exit(127);
752e0b8e63eSJohn Marino default: /* Parent. */
753e0b8e63eSJohn Marino /* Close the pipe ends the parent won't use. */
754e0b8e63eSJohn Marino (void)close(std_output[1]);
755e0b8e63eSJohn Marino break;
756e0b8e63eSJohn Marino }
757e0b8e63eSJohn Marino
758e0b8e63eSJohn Marino /*
759e0b8e63eSJohn Marino * Copy process standard output into a buffer.
760e0b8e63eSJohn Marino *
761e0b8e63eSJohn Marino * !!!
762e0b8e63eSJohn Marino * Historic vi apparently discarded leading \n and \r's from
763e0b8e63eSJohn Marino * the shell output stream. We don't on the grounds that any
764e0b8e63eSJohn Marino * shell that does that is broken.
765e0b8e63eSJohn Marino */
766e0b8e63eSJohn Marino for (p = bp, len = 0, ch = EOF;
767e0b8e63eSJohn Marino (ch = GETC(ifp)) != EOF; *p++ = ch, blen-=sizeof(CHAR_T), ++len)
768e0b8e63eSJohn Marino if (blen < 5) {
769e0b8e63eSJohn Marino ADD_SPACE_GOTOW(sp, bp, *blenp, *blenp * 2);
770e0b8e63eSJohn Marino p = bp + len;
771e0b8e63eSJohn Marino blen = *blenp - len;
772e0b8e63eSJohn Marino }
773e0b8e63eSJohn Marino
774e0b8e63eSJohn Marino /* Delete the final newline, nul terminate the string. */
775e0b8e63eSJohn Marino if (p > bp && (p[-1] == '\n' || p[-1] == '\r')) {
776e0b8e63eSJohn Marino --p;
777e0b8e63eSJohn Marino --len;
778e0b8e63eSJohn Marino }
779e0b8e63eSJohn Marino *p = '\0';
780e0b8e63eSJohn Marino *lenp = len;
781e0b8e63eSJohn Marino *bpp = bp; /* *blenp is already updated. */
782e0b8e63eSJohn Marino
783e0b8e63eSJohn Marino if (ferror(ifp))
784e0b8e63eSJohn Marino goto ioerr;
785e0b8e63eSJohn Marino if (fclose(ifp)) {
786e0b8e63eSJohn Marino ioerr: msgq_str(sp, M_ERR, sh, "119|I/O error: %s");
787e0b8e63eSJohn Marino alloc_err: rval = SEXP_ERR;
788e0b8e63eSJohn Marino } else
789e0b8e63eSJohn Marino rval = SEXP_OK;
790e0b8e63eSJohn Marino
791e0b8e63eSJohn Marino /*
792e0b8e63eSJohn Marino * Wait for the process. If the shell process fails (e.g., "echo $q"
793e0b8e63eSJohn Marino * where q wasn't a defined variable) or if the returned string has
794e0b8e63eSJohn Marino * no characters or only blank characters, (e.g., "echo $5"), complain
795e0b8e63eSJohn Marino * that the shell expansion failed. We can't know for certain that's
796e0b8e63eSJohn Marino * the error, but it's a good guess, and it matches historic practice.
797e0b8e63eSJohn Marino * This won't catch "echo foo_$5", but that's not a common error and
798e0b8e63eSJohn Marino * historic vi didn't catch it either.
799e0b8e63eSJohn Marino */
800e0b8e63eSJohn Marino if (proc_wait(sp, (long)pid, sh, 1, 0))
801e0b8e63eSJohn Marino rval = SEXP_EXPANSION_ERR;
802e0b8e63eSJohn Marino
803e0b8e63eSJohn Marino for (p = bp; len; ++p, --len)
804e0b8e63eSJohn Marino if (!cmdskip(*p))
805e0b8e63eSJohn Marino break;
806e0b8e63eSJohn Marino if (len == 0)
807e0b8e63eSJohn Marino rval = SEXP_EXPANSION_ERR;
808e0b8e63eSJohn Marino
809e0b8e63eSJohn Marino if (rval == SEXP_EXPANSION_ERR)
810e0b8e63eSJohn Marino msgq(sp, M_ERR, "304|Shell expansion failed");
811e0b8e63eSJohn Marino
812e0b8e63eSJohn Marino return (rval == SEXP_OK ? 0 : 1);
813e0b8e63eSJohn Marino }
814e0b8e63eSJohn Marino
815e0b8e63eSJohn Marino /*
816e0b8e63eSJohn Marino * argv_esc --
817e0b8e63eSJohn Marino * Escape a string into an ex and shell argument.
818e0b8e63eSJohn Marino *
819e0b8e63eSJohn Marino * PUBLIC: CHAR_T *argv_esc(SCR *, EXCMD *, CHAR_T *, size_t);
820e0b8e63eSJohn Marino */
821e0b8e63eSJohn Marino CHAR_T *
argv_esc(SCR * sp,EXCMD * excp,CHAR_T * str,size_t len)822e0b8e63eSJohn Marino argv_esc(SCR *sp, EXCMD *excp, CHAR_T *str, size_t len)
823e0b8e63eSJohn Marino {
824e0b8e63eSJohn Marino size_t blen, off;
825e0b8e63eSJohn Marino CHAR_T *bp, *p;
826e0b8e63eSJohn Marino int ch;
827e0b8e63eSJohn Marino
828e0b8e63eSJohn Marino GET_SPACE_GOTOW(sp, bp, blen, len + 1);
829e0b8e63eSJohn Marino
830e0b8e63eSJohn Marino /*
831e0b8e63eSJohn Marino * Leaving the first '~' unescaped causes the user to need a
832e0b8e63eSJohn Marino * "./" prefix to edit a file which really starts with a '~'.
833e0b8e63eSJohn Marino * However, the file completion happens to not work for these
834e0b8e63eSJohn Marino * files without the prefix.
835e0b8e63eSJohn Marino *
836e0b8e63eSJohn Marino * All ex expansion characters, "!%#", are double escaped.
837e0b8e63eSJohn Marino */
838e0b8e63eSJohn Marino for (p = bp; len > 0; ++str, --len) {
839e0b8e63eSJohn Marino ch = *str;
840e0b8e63eSJohn Marino off = p - bp;
841e0b8e63eSJohn Marino if (blen / sizeof(CHAR_T) - off < 3) {
842e0b8e63eSJohn Marino ADD_SPACE_GOTOW(sp, bp, blen, off + 3);
843e0b8e63eSJohn Marino p = bp + off;
844e0b8e63eSJohn Marino }
845e0b8e63eSJohn Marino if (cmdskip(ch) || ch == '\n' ||
846e0b8e63eSJohn Marino IS_ESCAPE(sp, excp, ch)) /* Ex. */
847e0b8e63eSJohn Marino *p++ = CH_LITERAL;
848e0b8e63eSJohn Marino else switch (ch) {
849e0b8e63eSJohn Marino case '~': /* ~user. */
850e0b8e63eSJohn Marino if (p != bp)
851e0b8e63eSJohn Marino *p++ = '\\';
852e0b8e63eSJohn Marino break;
853e0b8e63eSJohn Marino case '+': /* Ex +cmd. */
854e0b8e63eSJohn Marino if (p == bp)
855e0b8e63eSJohn Marino *p++ = '\\';
856e0b8e63eSJohn Marino break;
857e0b8e63eSJohn Marino case '!': case '%': case '#': /* Ex exp. */
858e0b8e63eSJohn Marino *p++ = '\\';
859e0b8e63eSJohn Marino *p++ = '\\';
860e0b8e63eSJohn Marino break;
861e0b8e63eSJohn Marino case ',': case '-': case '.': case '/': /* Safe. */
862e0b8e63eSJohn Marino case ':': case '=': case '@': case '_':
863e0b8e63eSJohn Marino break;
864e0b8e63eSJohn Marino default: /* Unsafe. */
865e0b8e63eSJohn Marino if (isascii(ch) && !isalnum(ch))
866e0b8e63eSJohn Marino *p++ = '\\';
867e0b8e63eSJohn Marino }
868e0b8e63eSJohn Marino *p++ = ch;
869e0b8e63eSJohn Marino }
870e0b8e63eSJohn Marino *p = '\0';
871e0b8e63eSJohn Marino
872e0b8e63eSJohn Marino return bp;
873e0b8e63eSJohn Marino
874e0b8e63eSJohn Marino alloc_err:
875e0b8e63eSJohn Marino return NULL;
876e0b8e63eSJohn Marino }
877e0b8e63eSJohn Marino
878e0b8e63eSJohn Marino /*
879e0b8e63eSJohn Marino * argv_uesc --
880e0b8e63eSJohn Marino * Unescape an escaped ex and shell argument.
881e0b8e63eSJohn Marino *
882e0b8e63eSJohn Marino * PUBLIC: CHAR_T *argv_uesc(SCR *, EXCMD *, CHAR_T *, size_t);
883e0b8e63eSJohn Marino */
884e0b8e63eSJohn Marino CHAR_T *
argv_uesc(SCR * sp,EXCMD * excp,CHAR_T * str,size_t len)885e0b8e63eSJohn Marino argv_uesc(SCR *sp, EXCMD *excp, CHAR_T *str, size_t len)
886e0b8e63eSJohn Marino {
887e0b8e63eSJohn Marino size_t blen;
888e0b8e63eSJohn Marino CHAR_T *bp, *p;
889e0b8e63eSJohn Marino
890e0b8e63eSJohn Marino GET_SPACE_GOTOW(sp, bp, blen, len + 1);
891e0b8e63eSJohn Marino
892e0b8e63eSJohn Marino for (p = bp; len > 0; ++str, --len) {
893e0b8e63eSJohn Marino if (IS_ESCAPE(sp, excp, *str)) {
894e0b8e63eSJohn Marino if (--len < 1)
895e0b8e63eSJohn Marino break;
896e0b8e63eSJohn Marino ++str;
897e0b8e63eSJohn Marino } else if (*str == '\\') {
898e0b8e63eSJohn Marino if (--len < 1)
899e0b8e63eSJohn Marino break;
900e0b8e63eSJohn Marino ++str;
901e0b8e63eSJohn Marino
902e0b8e63eSJohn Marino /* Check for double escaping. */
903e0b8e63eSJohn Marino if (*str == '\\' && len > 1)
904e0b8e63eSJohn Marino switch (str[1]) {
905e0b8e63eSJohn Marino case '!': case '%': case '#':
906e0b8e63eSJohn Marino ++str;
907e0b8e63eSJohn Marino --len;
908e0b8e63eSJohn Marino }
909e0b8e63eSJohn Marino }
910e0b8e63eSJohn Marino *p++ = *str;
911e0b8e63eSJohn Marino }
912e0b8e63eSJohn Marino *p = '\0';
913e0b8e63eSJohn Marino
914e0b8e63eSJohn Marino return bp;
915e0b8e63eSJohn Marino
916e0b8e63eSJohn Marino alloc_err:
917e0b8e63eSJohn Marino return NULL;
918e0b8e63eSJohn Marino }
919