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