xref: /freebsd/contrib/nvi/ex/ex_argv.c (revision 0fcececbac9880b092aeb56a41a16f1ec8ac1ae6)
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