expand.c revision 320510
1/*-
2 * Copyright (c) 1991, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 * Copyright (c) 1997-2005
5 *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * Kenneth Almquist.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 * 4. Neither the name of the University nor the names of its contributors
19 *    may be used to endorse or promote products derived from this software
20 *    without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35#ifndef lint
36#if 0
37static char sccsid[] = "@(#)expand.c	8.5 (Berkeley) 5/15/95";
38#endif
39#endif /* not lint */
40#include <sys/cdefs.h>
41__FBSDID("$FreeBSD: stable/10/bin/sh/expand.c 320510 2017-06-30 21:32:48Z jilles $");
42
43#include <sys/types.h>
44#include <sys/time.h>
45#include <sys/stat.h>
46#include <dirent.h>
47#include <errno.h>
48#include <inttypes.h>
49#include <limits.h>
50#include <pwd.h>
51#include <stdio.h>
52#include <stdlib.h>
53#include <string.h>
54#include <unistd.h>
55#include <wchar.h>
56#include <wctype.h>
57
58/*
59 * Routines to expand arguments to commands.  We have to deal with
60 * backquotes, shell variables, and file metacharacters.
61 */
62
63#include "shell.h"
64#include "main.h"
65#include "nodes.h"
66#include "eval.h"
67#include "expand.h"
68#include "syntax.h"
69#include "parser.h"
70#include "jobs.h"
71#include "options.h"
72#include "var.h"
73#include "input.h"
74#include "output.h"
75#include "memalloc.h"
76#include "error.h"
77#include "mystring.h"
78#include "arith.h"
79#include "show.h"
80#include "builtins.h"
81
82/*
83 * Structure specifying which parts of the string should be searched
84 * for IFS characters.
85 */
86
87struct ifsregion {
88	struct ifsregion *next;	/* next region in list */
89	int begoff;		/* offset of start of region */
90	int endoff;		/* offset of end of region */
91	int inquotes;		/* search for nul bytes only */
92};
93
94
95static char *expdest;			/* output of current string */
96static struct nodelist *argbackq;	/* list of back quote expressions */
97static struct ifsregion ifsfirst;	/* first struct in list of ifs regions */
98static struct ifsregion *ifslastp;	/* last struct in list */
99static struct arglist exparg;		/* holds expanded arg list */
100
101static char *argstr(char *, int);
102static char *exptilde(char *, int);
103static char *expari(char *);
104static void expbackq(union node *, int, int);
105static int subevalvar(char *, char *, int, int, int, int, int);
106static char *evalvar(char *, int);
107static int varisset(const char *, int);
108static void strtodest(const char *, int, int, int);
109static void varvalue(const char *, int, int, int);
110static void recordregion(int, int, int);
111static void removerecordregions(int);
112static void ifsbreakup(char *, struct arglist *);
113static void expandmeta(struct strlist *);
114static void expmeta(char *, char *);
115static void addfname(char *);
116static struct strlist *expsort(struct strlist *);
117static struct strlist *msort(struct strlist *, int);
118static int patmatch(const char *, const char *, int);
119static char *cvtnum(int, char *);
120static int collate_range_cmp(wchar_t, wchar_t);
121
122static int
123collate_range_cmp(wchar_t c1, wchar_t c2)
124{
125	static wchar_t s1[2], s2[2];
126
127	s1[0] = c1;
128	s2[0] = c2;
129	return (wcscoll(s1, s2));
130}
131
132static char *
133stputs_quotes(const char *data, const char *syntax, char *p)
134{
135	while (*data) {
136		CHECKSTRSPACE(2, p);
137		if (syntax[(int)*data] == CCTL)
138			USTPUTC(CTLESC, p);
139		USTPUTC(*data++, p);
140	}
141	return (p);
142}
143#define STPUTS_QUOTES(data, syntax, p) p = stputs_quotes((data), syntax, p)
144
145/*
146 * Perform expansions on an argument, placing the resulting list of arguments
147 * in arglist.  Parameter expansion, command substitution and arithmetic
148 * expansion are always performed; additional expansions can be requested
149 * via flag (EXP_*).
150 * The result is left in the stack string.
151 * When arglist is NULL, perform here document expansion.
152 *
153 * Caution: this function uses global state and is not reentrant.
154 * However, a new invocation after an interrupted invocation is safe
155 * and will reset the global state for the new call.
156 */
157void
158expandarg(union node *arg, struct arglist *arglist, int flag)
159{
160	struct strlist *sp;
161	char *p;
162
163	argbackq = arg->narg.backquote;
164	STARTSTACKSTR(expdest);
165	ifsfirst.next = NULL;
166	ifslastp = NULL;
167	argstr(arg->narg.text, flag);
168	if (arglist == NULL) {
169		STACKSTRNUL(expdest);
170		return;			/* here document expanded */
171	}
172	STPUTC('\0', expdest);
173	p = grabstackstr(expdest);
174	exparg.lastp = &exparg.list;
175	if (flag & EXP_FULL) {
176		ifsbreakup(p, &exparg);
177		*exparg.lastp = NULL;
178		exparg.lastp = &exparg.list;
179		expandmeta(exparg.list);
180	} else {
181		sp = (struct strlist *)stalloc(sizeof (struct strlist));
182		sp->text = p;
183		*exparg.lastp = sp;
184		exparg.lastp = &sp->next;
185	}
186	while (ifsfirst.next != NULL) {
187		struct ifsregion *ifsp;
188		INTOFF;
189		ifsp = ifsfirst.next->next;
190		ckfree(ifsfirst.next);
191		ifsfirst.next = ifsp;
192		INTON;
193	}
194	*exparg.lastp = NULL;
195	if (exparg.list) {
196		*arglist->lastp = exparg.list;
197		arglist->lastp = exparg.lastp;
198	}
199}
200
201
202
203/*
204 * Perform parameter expansion, command substitution and arithmetic
205 * expansion, and tilde expansion if requested via EXP_TILDE/EXP_VARTILDE.
206 * Processing ends at a CTLENDVAR or CTLENDARI character as well as '\0'.
207 * This is used to expand word in ${var+word} etc.
208 * If EXP_FULL or EXP_CASE are set, keep and/or generate CTLESC
209 * characters to allow for further processing.
210 * If EXP_FULL is set, also preserve CTLQUOTEMARK characters.
211 */
212static char *
213argstr(char *p, int flag)
214{
215	char c;
216	int quotes = flag & (EXP_FULL | EXP_CASE);	/* do CTLESC */
217	int firsteq = 1;
218	int split_lit;
219	int lit_quoted;
220
221	split_lit = flag & EXP_SPLIT_LIT;
222	lit_quoted = flag & EXP_LIT_QUOTED;
223	flag &= ~(EXP_SPLIT_LIT | EXP_LIT_QUOTED);
224	if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE)))
225		p = exptilde(p, flag);
226	for (;;) {
227		CHECKSTRSPACE(2, expdest);
228		switch (c = *p++) {
229		case '\0':
230			return (p - 1);
231		case CTLENDVAR:
232		case CTLENDARI:
233			return (p);
234		case CTLQUOTEMARK:
235			lit_quoted = 1;
236			/* "$@" syntax adherence hack */
237			if (p[0] == CTLVAR && p[2] == '@' && p[3] == '=')
238				break;
239			if ((flag & EXP_FULL) != 0)
240				USTPUTC(c, expdest);
241			break;
242		case CTLQUOTEEND:
243			lit_quoted = 0;
244			break;
245		case CTLESC:
246			if (quotes)
247				USTPUTC(c, expdest);
248			c = *p++;
249			USTPUTC(c, expdest);
250			if (split_lit && !lit_quoted)
251				recordregion(expdest - stackblock() -
252				    (quotes ? 2 : 1),
253				    expdest - stackblock(), 0);
254			break;
255		case CTLVAR:
256			p = evalvar(p, flag);
257			break;
258		case CTLBACKQ:
259		case CTLBACKQ|CTLQUOTE:
260			expbackq(argbackq->n, c & CTLQUOTE, flag);
261			argbackq = argbackq->next;
262			break;
263		case CTLARI:
264			p = expari(p);
265			break;
266		case ':':
267		case '=':
268			/*
269			 * sort of a hack - expand tildes in variable
270			 * assignments (after the first '=' and after ':'s).
271			 */
272			USTPUTC(c, expdest);
273			if (split_lit && !lit_quoted)
274				recordregion(expdest - stackblock() - 1,
275				    expdest - stackblock(), 0);
276			if (flag & EXP_VARTILDE && *p == '~' &&
277			    (c != '=' || firsteq)) {
278				if (c == '=')
279					firsteq = 0;
280				p = exptilde(p, flag);
281			}
282			break;
283		default:
284			USTPUTC(c, expdest);
285			if (split_lit && !lit_quoted)
286				recordregion(expdest - stackblock() - 1,
287				    expdest - stackblock(), 0);
288		}
289	}
290}
291
292/*
293 * Perform tilde expansion, placing the result in the stack string and
294 * returning the next position in the input string to process.
295 */
296static char *
297exptilde(char *p, int flag)
298{
299	char c, *startp = p;
300	struct passwd *pw;
301	char *home;
302
303	for (;;) {
304		c = *p;
305		switch(c) {
306		case CTLESC: /* This means CTL* are always considered quoted. */
307		case CTLVAR:
308		case CTLBACKQ:
309		case CTLBACKQ | CTLQUOTE:
310		case CTLARI:
311		case CTLENDARI:
312		case CTLQUOTEMARK:
313			return (startp);
314		case ':':
315			if ((flag & EXP_VARTILDE) == 0)
316				break;
317			/* FALLTHROUGH */
318		case '\0':
319		case '/':
320		case CTLENDVAR:
321			*p = '\0';
322			if (*(startp+1) == '\0') {
323				home = lookupvar("HOME");
324			} else {
325				pw = getpwnam(startp+1);
326				home = pw != NULL ? pw->pw_dir : NULL;
327			}
328			*p = c;
329			if (home == NULL || *home == '\0')
330				return (startp);
331			strtodest(home, flag, VSNORMAL, 1);
332			return (p);
333		}
334		p++;
335	}
336}
337
338
339static void
340removerecordregions(int endoff)
341{
342	if (ifslastp == NULL)
343		return;
344
345	if (ifsfirst.endoff > endoff) {
346		while (ifsfirst.next != NULL) {
347			struct ifsregion *ifsp;
348			INTOFF;
349			ifsp = ifsfirst.next->next;
350			ckfree(ifsfirst.next);
351			ifsfirst.next = ifsp;
352			INTON;
353		}
354		if (ifsfirst.begoff > endoff)
355			ifslastp = NULL;
356		else {
357			ifslastp = &ifsfirst;
358			ifsfirst.endoff = endoff;
359		}
360		return;
361	}
362
363	ifslastp = &ifsfirst;
364	while (ifslastp->next && ifslastp->next->begoff < endoff)
365		ifslastp=ifslastp->next;
366	while (ifslastp->next != NULL) {
367		struct ifsregion *ifsp;
368		INTOFF;
369		ifsp = ifslastp->next->next;
370		ckfree(ifslastp->next);
371		ifslastp->next = ifsp;
372		INTON;
373	}
374	if (ifslastp->endoff > endoff)
375		ifslastp->endoff = endoff;
376}
377
378/*
379 * Expand arithmetic expression.
380 * Note that flag is not required as digits never require CTLESC characters.
381 */
382static char *
383expari(char *p)
384{
385	char *q, *start;
386	arith_t result;
387	int begoff;
388	int quoted;
389	int adj;
390
391	quoted = *p++ == '"';
392	begoff = expdest - stackblock();
393	p = argstr(p, 0);
394	removerecordregions(begoff);
395	STPUTC('\0', expdest);
396	start = stackblock() + begoff;
397
398	q = grabstackstr(expdest);
399	result = arith(start);
400	ungrabstackstr(q, expdest);
401
402	start = stackblock() + begoff;
403	adj = start - expdest;
404	STADJUST(adj, expdest);
405
406	CHECKSTRSPACE((int)(DIGITS(result) + 1), expdest);
407	fmtstr(expdest, DIGITS(result), ARITH_FORMAT_STR, result);
408	adj = strlen(expdest);
409	STADJUST(adj, expdest);
410	if (!quoted)
411		recordregion(begoff, expdest - stackblock(), 0);
412	return p;
413}
414
415
416/*
417 * Perform command substitution.
418 */
419static void
420expbackq(union node *cmd, int quoted, int flag)
421{
422	struct backcmd in;
423	int i;
424	char buf[128];
425	char *p;
426	char *dest = expdest;
427	struct ifsregion saveifs, *savelastp;
428	struct nodelist *saveargbackq;
429	char lastc;
430	int startloc = dest - stackblock();
431	char const *syntax = quoted? DQSYNTAX : BASESYNTAX;
432	int quotes = flag & (EXP_FULL | EXP_CASE);
433	size_t nnl;
434
435	INTOFF;
436	saveifs = ifsfirst;
437	savelastp = ifslastp;
438	saveargbackq = argbackq;
439	p = grabstackstr(dest);
440	evalbackcmd(cmd, &in);
441	ungrabstackstr(p, dest);
442
443	p = in.buf;
444	lastc = '\0';
445	nnl = 0;
446	/* Don't copy trailing newlines */
447	for (;;) {
448		if (--in.nleft < 0) {
449			if (in.fd < 0)
450				break;
451			while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR);
452			TRACE(("expbackq: read returns %d\n", i));
453			if (i <= 0)
454				break;
455			p = buf;
456			in.nleft = i - 1;
457		}
458		lastc = *p++;
459		if (lastc != '\0') {
460			if (lastc == '\n') {
461				nnl++;
462			} else {
463				CHECKSTRSPACE(nnl + 2, dest);
464				while (nnl > 0) {
465					nnl--;
466					USTPUTC('\n', dest);
467				}
468				if (quotes && syntax[(int)lastc] == CCTL)
469					USTPUTC(CTLESC, dest);
470				USTPUTC(lastc, dest);
471			}
472		}
473	}
474
475	if (in.fd >= 0)
476		close(in.fd);
477	if (in.buf)
478		ckfree(in.buf);
479	if (in.jp) {
480		p = grabstackstr(dest);
481		exitstatus = waitforjob(in.jp, (int *)NULL);
482		ungrabstackstr(p, dest);
483	}
484	TRACE(("expbackq: size=%td: \"%.*s\"\n",
485		((dest - stackblock()) - startloc),
486		(int)((dest - stackblock()) - startloc),
487		stackblock() + startloc));
488	ifsfirst = saveifs;
489	ifslastp = savelastp;
490	if (quoted == 0)
491		recordregion(startloc, dest - stackblock(), 0);
492	argbackq = saveargbackq;
493	expdest = dest;
494	INTON;
495}
496
497
498
499static void
500recordleft(const char *str, const char *loc, char *startp)
501{
502	int amount;
503
504	amount = ((str - 1) - (loc - startp)) - expdest;
505	STADJUST(amount, expdest);
506	while (loc != str - 1)
507		*startp++ = *loc++;
508}
509
510static int
511subevalvar(char *p, char *str, int strloc, int subtype, int startloc,
512  int varflags, int quotes)
513{
514	char *startp;
515	char *loc = NULL;
516	char *q;
517	int c = 0;
518	struct nodelist *saveargbackq = argbackq;
519	int amount;
520
521	argstr(p, (subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX ||
522	    subtype == VSTRIMRIGHT || subtype == VSTRIMRIGHTMAX ?
523	    EXP_CASE : 0) | EXP_TILDE);
524	STACKSTRNUL(expdest);
525	argbackq = saveargbackq;
526	startp = stackblock() + startloc;
527	if (str == NULL)
528	    str = stackblock() + strloc;
529
530	switch (subtype) {
531	case VSASSIGN:
532		setvar(str, startp, 0);
533		amount = startp - expdest;
534		STADJUST(amount, expdest);
535		varflags &= ~VSNUL;
536		return 1;
537
538	case VSQUESTION:
539		if (*p != CTLENDVAR) {
540			outfmt(out2, "%s\n", startp);
541			error((char *)NULL);
542		}
543		error("%.*s: parameter %snot set", (int)(p - str - 1),
544		      str, (varflags & VSNUL) ? "null or "
545					      : nullstr);
546		return 0;
547
548	case VSTRIMLEFT:
549		for (loc = startp; loc < str; loc++) {
550			c = *loc;
551			*loc = '\0';
552			if (patmatch(str, startp, quotes)) {
553				*loc = c;
554				recordleft(str, loc, startp);
555				return 1;
556			}
557			*loc = c;
558			if (quotes && *loc == CTLESC)
559				loc++;
560		}
561		return 0;
562
563	case VSTRIMLEFTMAX:
564		for (loc = str - 1; loc >= startp;) {
565			c = *loc;
566			*loc = '\0';
567			if (patmatch(str, startp, quotes)) {
568				*loc = c;
569				recordleft(str, loc, startp);
570				return 1;
571			}
572			*loc = c;
573			loc--;
574			if (quotes && loc > startp && *(loc - 1) == CTLESC) {
575				for (q = startp; q < loc; q++)
576					if (*q == CTLESC)
577						q++;
578				if (q > loc)
579					loc--;
580			}
581		}
582		return 0;
583
584	case VSTRIMRIGHT:
585		for (loc = str - 1; loc >= startp;) {
586			if (patmatch(str, loc, quotes)) {
587				amount = loc - expdest;
588				STADJUST(amount, expdest);
589				return 1;
590			}
591			loc--;
592			if (quotes && loc > startp && *(loc - 1) == CTLESC) {
593				for (q = startp; q < loc; q++)
594					if (*q == CTLESC)
595						q++;
596				if (q > loc)
597					loc--;
598			}
599		}
600		return 0;
601
602	case VSTRIMRIGHTMAX:
603		for (loc = startp; loc < str - 1; loc++) {
604			if (patmatch(str, loc, quotes)) {
605				amount = loc - expdest;
606				STADJUST(amount, expdest);
607				return 1;
608			}
609			if (quotes && *loc == CTLESC)
610				loc++;
611		}
612		return 0;
613
614
615	default:
616		abort();
617	}
618}
619
620
621/*
622 * Expand a variable, and return a pointer to the next character in the
623 * input string.
624 */
625
626static char *
627evalvar(char *p, int flag)
628{
629	int subtype;
630	int varflags;
631	char *var;
632	const char *val;
633	int patloc;
634	int c;
635	int set;
636	int special;
637	int startloc;
638	int varlen;
639	int varlenb;
640	int easy;
641	int quotes = flag & (EXP_FULL | EXP_CASE);
642	int record = 0;
643
644	varflags = (unsigned char)*p++;
645	subtype = varflags & VSTYPE;
646	var = p;
647	special = 0;
648	if (! is_name(*p))
649		special = 1;
650	p = strchr(p, '=') + 1;
651again: /* jump here after setting a variable with ${var=text} */
652	if (varflags & VSLINENO) {
653		set = 1;
654		special = 1;
655		val = NULL;
656	} else if (special) {
657		set = varisset(var, varflags & VSNUL);
658		val = NULL;
659	} else {
660		val = bltinlookup(var, 1);
661		if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) {
662			val = NULL;
663			set = 0;
664		} else
665			set = 1;
666	}
667	varlen = 0;
668	startloc = expdest - stackblock();
669	if (!set && uflag && *var != '@' && *var != '*') {
670		switch (subtype) {
671		case VSNORMAL:
672		case VSTRIMLEFT:
673		case VSTRIMLEFTMAX:
674		case VSTRIMRIGHT:
675		case VSTRIMRIGHTMAX:
676		case VSLENGTH:
677			error("%.*s: parameter not set", (int)(p - var - 1),
678			    var);
679		}
680	}
681	if (set && subtype != VSPLUS) {
682		/* insert the value of the variable */
683		if (special) {
684			if (varflags & VSLINENO)
685				STPUTBIN(var, p - var - 1, expdest);
686			else
687				varvalue(var, varflags & VSQUOTE, subtype, flag);
688			if (subtype == VSLENGTH) {
689				varlenb = expdest - stackblock() - startloc;
690				varlen = varlenb;
691				if (localeisutf8) {
692					val = stackblock() + startloc;
693					for (;val != expdest; val++)
694						if ((*val & 0xC0) == 0x80)
695							varlen--;
696				}
697				STADJUST(-varlenb, expdest);
698			}
699		} else {
700			if (subtype == VSLENGTH) {
701				for (;*val; val++)
702					if (!localeisutf8 ||
703					    (*val & 0xC0) != 0x80)
704						varlen++;
705			}
706			else
707				strtodest(val, flag, subtype,
708				    varflags & VSQUOTE);
709		}
710	}
711
712	if (subtype == VSPLUS)
713		set = ! set;
714
715	easy = ((varflags & VSQUOTE) == 0 ||
716		(*var == '@' && shellparam.nparam != 1));
717
718
719	switch (subtype) {
720	case VSLENGTH:
721		expdest = cvtnum(varlen, expdest);
722		record = 1;
723		break;
724
725	case VSNORMAL:
726		record = easy;
727		break;
728
729	case VSPLUS:
730	case VSMINUS:
731		if (!set) {
732			argstr(p, flag | (flag & EXP_FULL ? EXP_SPLIT_LIT : 0) |
733			    (varflags & VSQUOTE ? EXP_LIT_QUOTED : 0));
734			break;
735		}
736		record = easy;
737		break;
738
739	case VSTRIMLEFT:
740	case VSTRIMLEFTMAX:
741	case VSTRIMRIGHT:
742	case VSTRIMRIGHTMAX:
743		if (!set) {
744			set = 1;
745			break;
746		}
747		/*
748		 * Terminate the string and start recording the pattern
749		 * right after it
750		 */
751		STPUTC('\0', expdest);
752		patloc = expdest - stackblock();
753		if (subevalvar(p, NULL, patloc, subtype,
754		    startloc, varflags, quotes) == 0) {
755			int amount = (expdest - stackblock() - patloc) + 1;
756			STADJUST(-amount, expdest);
757		}
758		/* Remove any recorded regions beyond start of variable */
759		removerecordregions(startloc);
760		record = 1;
761		break;
762
763	case VSASSIGN:
764	case VSQUESTION:
765		if (!set) {
766			if (subevalvar(p, var, 0, subtype, startloc, varflags,
767			    quotes)) {
768				varflags &= ~VSNUL;
769				/*
770				 * Remove any recorded regions beyond
771				 * start of variable
772				 */
773				removerecordregions(startloc);
774				goto again;
775			}
776			break;
777		}
778		record = easy;
779		break;
780
781	case VSERROR:
782		c = p - var - 1;
783		error("${%.*s%s}: Bad substitution", c, var,
784		    (c > 0 && *p != CTLENDVAR) ? "..." : "");
785
786	default:
787		abort();
788	}
789
790	if (record)
791		recordregion(startloc, expdest - stackblock(),
792		    varflags & VSQUOTE || (ifsset() && ifsval()[0] == '\0' &&
793		    (*var == '@' || *var == '*')));
794
795	if (subtype != VSNORMAL) {	/* skip to end of alternative */
796		int nesting = 1;
797		for (;;) {
798			if ((c = *p++) == CTLESC)
799				p++;
800			else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
801				if (set)
802					argbackq = argbackq->next;
803			} else if (c == CTLVAR) {
804				if ((*p++ & VSTYPE) != VSNORMAL)
805					nesting++;
806			} else if (c == CTLENDVAR) {
807				if (--nesting == 0)
808					break;
809			}
810		}
811	}
812	return p;
813}
814
815
816
817/*
818 * Test whether a specialized variable is set.
819 */
820
821static int
822varisset(const char *name, int nulok)
823{
824
825	if (*name == '!')
826		return backgndpidset();
827	else if (*name == '@' || *name == '*') {
828		if (*shellparam.p == NULL)
829			return 0;
830
831		if (nulok) {
832			char **av;
833
834			for (av = shellparam.p; *av; av++)
835				if (**av != '\0')
836					return 1;
837			return 0;
838		}
839	} else if (is_digit(*name)) {
840		char *ap;
841		long num;
842
843		errno = 0;
844		num = strtol(name, NULL, 10);
845		if (errno != 0 || num > shellparam.nparam)
846			return 0;
847
848		if (num == 0)
849			ap = arg0;
850		else
851			ap = shellparam.p[num - 1];
852
853		if (nulok && (ap == NULL || *ap == '\0'))
854			return 0;
855	}
856	return 1;
857}
858
859static void
860strtodest(const char *p, int flag, int subtype, int quoted)
861{
862	if (flag & (EXP_FULL | EXP_CASE) && subtype != VSLENGTH)
863		STPUTS_QUOTES(p, quoted ? DQSYNTAX : BASESYNTAX, expdest);
864	else
865		STPUTS(p, expdest);
866}
867
868/*
869 * Add the value of a specialized variable to the stack string.
870 */
871
872static void
873varvalue(const char *name, int quoted, int subtype, int flag)
874{
875	int num;
876	char *p;
877	int i;
878	char sep;
879	char **ap;
880
881	switch (*name) {
882	case '$':
883		num = rootpid;
884		goto numvar;
885	case '?':
886		num = oexitstatus;
887		goto numvar;
888	case '#':
889		num = shellparam.nparam;
890		goto numvar;
891	case '!':
892		num = backgndpidval();
893numvar:
894		expdest = cvtnum(num, expdest);
895		break;
896	case '-':
897		for (i = 0 ; i < NOPTS ; i++) {
898			if (optlist[i].val)
899				STPUTC(optlist[i].letter, expdest);
900		}
901		break;
902	case '@':
903		if (flag & EXP_FULL && quoted) {
904			for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
905				strtodest(p, flag, subtype, quoted);
906				if (*ap)
907					STPUTC('\0', expdest);
908			}
909			break;
910		}
911		/* FALLTHROUGH */
912	case '*':
913		if (ifsset())
914			sep = ifsval()[0];
915		else
916			sep = ' ';
917		for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
918			strtodest(p, flag, subtype, quoted);
919			if (!*ap)
920				break;
921			if (sep || (flag & EXP_FULL && !quoted && **ap != '\0'))
922				STPUTC(sep, expdest);
923		}
924		break;
925	case '0':
926		p = arg0;
927		strtodest(p, flag, subtype, quoted);
928		break;
929	default:
930		if (is_digit(*name)) {
931			num = atoi(name);
932			if (num > 0 && num <= shellparam.nparam) {
933				p = shellparam.p[num - 1];
934				strtodest(p, flag, subtype, quoted);
935			}
936		}
937		break;
938	}
939}
940
941
942
943/*
944 * Record the fact that we have to scan this region of the
945 * string for IFS characters.
946 */
947
948static void
949recordregion(int start, int end, int inquotes)
950{
951	struct ifsregion *ifsp;
952
953	INTOFF;
954	if (ifslastp == NULL) {
955		ifsp = &ifsfirst;
956	} else {
957		if (ifslastp->endoff == start
958		    && ifslastp->inquotes == inquotes) {
959			/* extend previous area */
960			ifslastp->endoff = end;
961			INTON;
962			return;
963		}
964		ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion));
965		ifslastp->next = ifsp;
966	}
967	ifslastp = ifsp;
968	ifslastp->next = NULL;
969	ifslastp->begoff = start;
970	ifslastp->endoff = end;
971	ifslastp->inquotes = inquotes;
972	INTON;
973}
974
975
976
977/*
978 * Break the argument string into pieces based upon IFS and add the
979 * strings to the argument list.  The regions of the string to be
980 * searched for IFS characters have been stored by recordregion.
981 * CTLESC characters are preserved but have little effect in this pass
982 * other than escaping CTL* characters.  In particular, they do not escape
983 * IFS characters: that should be done with the ifsregion mechanism.
984 * CTLQUOTEMARK characters are used to preserve empty quoted strings.
985 * This pass treats them as a regular character, making the string non-empty.
986 * Later, they are removed along with the other CTL* characters.
987 */
988static void
989ifsbreakup(char *string, struct arglist *arglist)
990{
991	struct ifsregion *ifsp;
992	struct strlist *sp;
993	char *start;
994	char *p;
995	char *q;
996	const char *ifs;
997	const char *ifsspc;
998	int had_param_ch = 0;
999
1000	start = string;
1001
1002	if (ifslastp == NULL) {
1003		/* Return entire argument, IFS doesn't apply to any of it */
1004		sp = (struct strlist *)stalloc(sizeof *sp);
1005		sp->text = start;
1006		*arglist->lastp = sp;
1007		arglist->lastp = &sp->next;
1008		return;
1009	}
1010
1011	ifs = ifsset() ? ifsval() : " \t\n";
1012
1013	for (ifsp = &ifsfirst; ifsp != NULL; ifsp = ifsp->next) {
1014		p = string + ifsp->begoff;
1015		while (p < string + ifsp->endoff) {
1016			q = p;
1017			if (*p == CTLESC)
1018				p++;
1019			if (ifsp->inquotes) {
1020				/* Only NULs (should be from "$@") end args */
1021				had_param_ch = 1;
1022				if (*p != 0) {
1023					p++;
1024					continue;
1025				}
1026				ifsspc = NULL;
1027			} else {
1028				if (!strchr(ifs, *p)) {
1029					had_param_ch = 1;
1030					p++;
1031					continue;
1032				}
1033				ifsspc = strchr(" \t\n", *p);
1034
1035				/* Ignore IFS whitespace at start */
1036				if (q == start && ifsspc != NULL) {
1037					p++;
1038					start = p;
1039					continue;
1040				}
1041				had_param_ch = 0;
1042			}
1043
1044			/* Save this argument... */
1045			*q = '\0';
1046			sp = (struct strlist *)stalloc(sizeof *sp);
1047			sp->text = start;
1048			*arglist->lastp = sp;
1049			arglist->lastp = &sp->next;
1050			p++;
1051
1052			if (ifsspc != NULL) {
1053				/* Ignore further trailing IFS whitespace */
1054				for (; p < string + ifsp->endoff; p++) {
1055					q = p;
1056					if (*p == CTLESC)
1057						p++;
1058					if (strchr(ifs, *p) == NULL) {
1059						p = q;
1060						break;
1061					}
1062					if (strchr(" \t\n", *p) == NULL) {
1063						p++;
1064						break;
1065					}
1066				}
1067			}
1068			start = p;
1069		}
1070	}
1071
1072	/*
1073	 * Save anything left as an argument.
1074	 * Traditionally we have treated 'IFS=':'; set -- x$IFS' as
1075	 * generating 2 arguments, the second of which is empty.
1076	 * Some recent clarification of the Posix spec say that it
1077	 * should only generate one....
1078	 */
1079	if (had_param_ch || *start != 0) {
1080		sp = (struct strlist *)stalloc(sizeof *sp);
1081		sp->text = start;
1082		*arglist->lastp = sp;
1083		arglist->lastp = &sp->next;
1084	}
1085}
1086
1087
1088static char expdir[PATH_MAX];
1089#define expdir_end (expdir + sizeof(expdir))
1090
1091/*
1092 * Perform pathname generation and remove control characters.
1093 * At this point, the only control characters should be CTLESC and CTLQUOTEMARK.
1094 * The results are stored in the list exparg.
1095 */
1096static void
1097expandmeta(struct strlist *str)
1098{
1099	char *p;
1100	struct strlist **savelastp;
1101	struct strlist *sp;
1102	char c;
1103
1104	while (str) {
1105		if (fflag)
1106			goto nometa;
1107		p = str->text;
1108		for (;;) {			/* fast check for meta chars */
1109			if ((c = *p++) == '\0')
1110				goto nometa;
1111			if (c == '*' || c == '?' || c == '[')
1112				break;
1113		}
1114		savelastp = exparg.lastp;
1115		INTOFF;
1116		expmeta(expdir, str->text);
1117		INTON;
1118		if (exparg.lastp == savelastp) {
1119			/*
1120			 * no matches
1121			 */
1122nometa:
1123			*exparg.lastp = str;
1124			rmescapes(str->text);
1125			exparg.lastp = &str->next;
1126		} else {
1127			*exparg.lastp = NULL;
1128			*savelastp = sp = expsort(*savelastp);
1129			while (sp->next != NULL)
1130				sp = sp->next;
1131			exparg.lastp = &sp->next;
1132		}
1133		str = str->next;
1134	}
1135}
1136
1137
1138/*
1139 * Do metacharacter (i.e. *, ?, [...]) expansion.
1140 */
1141
1142static void
1143expmeta(char *enddir, char *name)
1144{
1145	const char *p;
1146	const char *q;
1147	const char *start;
1148	char *endname;
1149	int metaflag;
1150	struct stat statb;
1151	DIR *dirp;
1152	struct dirent *dp;
1153	int atend;
1154	int matchdot;
1155	int esc;
1156	int namlen;
1157
1158	metaflag = 0;
1159	start = name;
1160	for (p = name; esc = 0, *p; p += esc + 1) {
1161		if (*p == '*' || *p == '?')
1162			metaflag = 1;
1163		else if (*p == '[') {
1164			q = p + 1;
1165			if (*q == '!' || *q == '^')
1166				q++;
1167			for (;;) {
1168				while (*q == CTLQUOTEMARK)
1169					q++;
1170				if (*q == CTLESC)
1171					q++;
1172				if (*q == '/' || *q == '\0')
1173					break;
1174				if (*++q == ']') {
1175					metaflag = 1;
1176					break;
1177				}
1178			}
1179		} else if (*p == '\0')
1180			break;
1181		else if (*p == CTLQUOTEMARK)
1182			continue;
1183		else {
1184			if (*p == CTLESC)
1185				esc++;
1186			if (p[esc] == '/') {
1187				if (metaflag)
1188					break;
1189				start = p + esc + 1;
1190			}
1191		}
1192	}
1193	if (metaflag == 0) {	/* we've reached the end of the file name */
1194		if (enddir != expdir)
1195			metaflag++;
1196		for (p = name ; ; p++) {
1197			if (*p == CTLQUOTEMARK)
1198				continue;
1199			if (*p == CTLESC)
1200				p++;
1201			*enddir++ = *p;
1202			if (*p == '\0')
1203				break;
1204			if (enddir == expdir_end)
1205				return;
1206		}
1207		if (metaflag == 0 || lstat(expdir, &statb) >= 0)
1208			addfname(expdir);
1209		return;
1210	}
1211	endname = name + (p - name);
1212	if (start != name) {
1213		p = name;
1214		while (p < start) {
1215			while (*p == CTLQUOTEMARK)
1216				p++;
1217			if (*p == CTLESC)
1218				p++;
1219			*enddir++ = *p++;
1220			if (enddir == expdir_end)
1221				return;
1222		}
1223	}
1224	if (enddir == expdir) {
1225		p = ".";
1226	} else if (enddir == expdir + 1 && *expdir == '/') {
1227		p = "/";
1228	} else {
1229		p = expdir;
1230		enddir[-1] = '\0';
1231	}
1232	if ((dirp = opendir(p)) == NULL)
1233		return;
1234	if (enddir != expdir)
1235		enddir[-1] = '/';
1236	if (*endname == 0) {
1237		atend = 1;
1238	} else {
1239		atend = 0;
1240		*endname = '\0';
1241		endname += esc + 1;
1242	}
1243	matchdot = 0;
1244	p = start;
1245	while (*p == CTLQUOTEMARK)
1246		p++;
1247	if (*p == CTLESC)
1248		p++;
1249	if (*p == '.')
1250		matchdot++;
1251	while (! int_pending() && (dp = readdir(dirp)) != NULL) {
1252		if (dp->d_name[0] == '.' && ! matchdot)
1253			continue;
1254		if (patmatch(start, dp->d_name, 0)) {
1255			namlen = dp->d_namlen;
1256			if (enddir + namlen + 1 > expdir_end)
1257				continue;
1258			memcpy(enddir, dp->d_name, namlen + 1);
1259			if (atend)
1260				addfname(expdir);
1261			else {
1262				if (dp->d_type != DT_UNKNOWN &&
1263				    dp->d_type != DT_DIR &&
1264				    dp->d_type != DT_LNK)
1265					continue;
1266				if (enddir + namlen + 2 > expdir_end)
1267					continue;
1268				enddir[namlen] = '/';
1269				enddir[namlen + 1] = '\0';
1270				expmeta(enddir + namlen + 1, endname);
1271			}
1272		}
1273	}
1274	closedir(dirp);
1275	if (! atend)
1276		endname[-esc - 1] = esc ? CTLESC : '/';
1277}
1278
1279
1280/*
1281 * Add a file name to the list.
1282 */
1283
1284static void
1285addfname(char *name)
1286{
1287	char *p;
1288	struct strlist *sp;
1289
1290	p = stsavestr(name);
1291	sp = (struct strlist *)stalloc(sizeof *sp);
1292	sp->text = p;
1293	*exparg.lastp = sp;
1294	exparg.lastp = &sp->next;
1295}
1296
1297
1298/*
1299 * Sort the results of file name expansion.  It calculates the number of
1300 * strings to sort and then calls msort (short for merge sort) to do the
1301 * work.
1302 */
1303
1304static struct strlist *
1305expsort(struct strlist *str)
1306{
1307	int len;
1308	struct strlist *sp;
1309
1310	len = 0;
1311	for (sp = str ; sp ; sp = sp->next)
1312		len++;
1313	return msort(str, len);
1314}
1315
1316
1317static struct strlist *
1318msort(struct strlist *list, int len)
1319{
1320	struct strlist *p, *q = NULL;
1321	struct strlist **lpp;
1322	int half;
1323	int n;
1324
1325	if (len <= 1)
1326		return list;
1327	half = len >> 1;
1328	p = list;
1329	for (n = half ; --n >= 0 ; ) {
1330		q = p;
1331		p = p->next;
1332	}
1333	q->next = NULL;			/* terminate first half of list */
1334	q = msort(list, half);		/* sort first half of list */
1335	p = msort(p, len - half);		/* sort second half */
1336	lpp = &list;
1337	for (;;) {
1338		if (strcmp(p->text, q->text) < 0) {
1339			*lpp = p;
1340			lpp = &p->next;
1341			if ((p = *lpp) == NULL) {
1342				*lpp = q;
1343				break;
1344			}
1345		} else {
1346			*lpp = q;
1347			lpp = &q->next;
1348			if ((q = *lpp) == NULL) {
1349				*lpp = p;
1350				break;
1351			}
1352		}
1353	}
1354	return list;
1355}
1356
1357
1358
1359static wchar_t
1360get_wc(const char **p)
1361{
1362	wchar_t c;
1363	int chrlen;
1364
1365	chrlen = mbtowc(&c, *p, 4);
1366	if (chrlen == 0)
1367		return 0;
1368	else if (chrlen == -1)
1369		c = 0;
1370	else
1371		*p += chrlen;
1372	return c;
1373}
1374
1375
1376/*
1377 * See if a character matches a character class, starting at the first colon
1378 * of "[:class:]".
1379 * If a valid character class is recognized, a pointer to the next character
1380 * after the final closing bracket is stored into *end, otherwise a null
1381 * pointer is stored into *end.
1382 */
1383static int
1384match_charclass(const char *p, wchar_t chr, const char **end)
1385{
1386	char name[20];
1387	const char *nameend;
1388	wctype_t cclass;
1389
1390	*end = NULL;
1391	p++;
1392	nameend = strstr(p, ":]");
1393	if (nameend == NULL || (size_t)(nameend - p) >= sizeof(name) ||
1394	    nameend == p)
1395		return 0;
1396	memcpy(name, p, nameend - p);
1397	name[nameend - p] = '\0';
1398	*end = nameend + 2;
1399	cclass = wctype(name);
1400	/* An unknown class matches nothing but is valid nevertheless. */
1401	if (cclass == 0)
1402		return 0;
1403	return iswctype(chr, cclass);
1404}
1405
1406
1407/*
1408 * Returns true if the pattern matches the string.
1409 */
1410
1411static int
1412patmatch(const char *pattern, const char *string, int squoted)
1413{
1414	const char *p, *q, *end;
1415	const char *bt_p, *bt_q;
1416	char c;
1417	wchar_t wc, wc2;
1418
1419	p = pattern;
1420	q = string;
1421	bt_p = NULL;
1422	bt_q = NULL;
1423	for (;;) {
1424		switch (c = *p++) {
1425		case '\0':
1426			if (*q != '\0')
1427				goto backtrack;
1428			return 1;
1429		case CTLESC:
1430			if (squoted && *q == CTLESC)
1431				q++;
1432			if (*q++ != *p++)
1433				goto backtrack;
1434			break;
1435		case CTLQUOTEMARK:
1436			continue;
1437		case '?':
1438			if (squoted && *q == CTLESC)
1439				q++;
1440			if (*q == '\0')
1441				return 0;
1442			if (localeisutf8) {
1443				wc = get_wc(&q);
1444				/*
1445				 * A '?' does not match invalid UTF-8 but a
1446				 * '*' does, so backtrack.
1447				 */
1448				if (wc == 0)
1449					goto backtrack;
1450			} else
1451				wc = (unsigned char)*q++;
1452			break;
1453		case '*':
1454			c = *p;
1455			while (c == CTLQUOTEMARK || c == '*')
1456				c = *++p;
1457			/*
1458			 * If the pattern ends here, we know the string
1459			 * matches without needing to look at the rest of it.
1460			 */
1461			if (c == '\0')
1462				return 1;
1463			/*
1464			 * First try the shortest match for the '*' that
1465			 * could work. We can forget any earlier '*' since
1466			 * there is no way having it match more characters
1467			 * can help us, given that we are already here.
1468			 */
1469			bt_p = p;
1470			bt_q = q;
1471			break;
1472		case '[': {
1473			const char *savep, *saveq;
1474			int invert, found;
1475			wchar_t chr;
1476
1477			savep = p, saveq = q;
1478			invert = 0;
1479			if (*p == '!' || *p == '^') {
1480				invert++;
1481				p++;
1482			}
1483			found = 0;
1484			if (squoted && *q == CTLESC)
1485				q++;
1486			if (*q == '\0')
1487				return 0;
1488			if (localeisutf8) {
1489				chr = get_wc(&q);
1490				if (chr == 0)
1491					goto backtrack;
1492			} else
1493				chr = (unsigned char)*q++;
1494			c = *p++;
1495			do {
1496				if (c == '\0') {
1497					p = savep, q = saveq;
1498					c = '[';
1499					goto dft;
1500				}
1501				if (c == CTLQUOTEMARK)
1502					continue;
1503				if (c == '[' && *p == ':') {
1504					found |= match_charclass(p, chr, &end);
1505					if (end != NULL)
1506						p = end;
1507				}
1508				if (c == CTLESC)
1509					c = *p++;
1510				if (localeisutf8 && c & 0x80) {
1511					p--;
1512					wc = get_wc(&p);
1513					if (wc == 0) /* bad utf-8 */
1514						return 0;
1515				} else
1516					wc = (unsigned char)c;
1517				if (*p == '-' && p[1] != ']') {
1518					p++;
1519					while (*p == CTLQUOTEMARK)
1520						p++;
1521					if (*p == CTLESC)
1522						p++;
1523					if (localeisutf8) {
1524						wc2 = get_wc(&p);
1525						if (wc2 == 0) /* bad utf-8 */
1526							return 0;
1527					} else
1528						wc2 = (unsigned char)*p++;
1529					if (   collate_range_cmp(chr, wc) >= 0
1530					    && collate_range_cmp(chr, wc2) <= 0
1531					   )
1532						found = 1;
1533				} else {
1534					if (chr == wc)
1535						found = 1;
1536				}
1537			} while ((c = *p++) != ']');
1538			if (found == invert)
1539				goto backtrack;
1540			break;
1541		}
1542dft:	        default:
1543			if (squoted && *q == CTLESC)
1544				q++;
1545			if (*q == '\0')
1546				return 0;
1547			if (*q++ == c)
1548				break;
1549backtrack:
1550			/*
1551			 * If we have a mismatch (other than hitting the end
1552			 * of the string), go back to the last '*' seen and
1553			 * have it match one additional character.
1554			 */
1555			if (bt_p == NULL)
1556				return 0;
1557			if (squoted && *bt_q == CTLESC)
1558				bt_q++;
1559			if (*bt_q == '\0')
1560				return 0;
1561			bt_q++;
1562			p = bt_p;
1563			q = bt_q;
1564			break;
1565		}
1566	}
1567}
1568
1569
1570
1571/*
1572 * Remove any CTLESC and CTLQUOTEMARK characters from a string.
1573 */
1574
1575void
1576rmescapes(char *str)
1577{
1578	char *p, *q;
1579
1580	p = str;
1581	while (*p != CTLESC && *p != CTLQUOTEMARK && *p != CTLQUOTEEND) {
1582		if (*p++ == '\0')
1583			return;
1584	}
1585	q = p;
1586	while (*p) {
1587		if (*p == CTLQUOTEMARK || *p == CTLQUOTEEND) {
1588			p++;
1589			continue;
1590		}
1591		if (*p == CTLESC)
1592			p++;
1593		*q++ = *p++;
1594	}
1595	*q = '\0';
1596}
1597
1598
1599
1600/*
1601 * See if a pattern matches in a case statement.
1602 */
1603
1604int
1605casematch(union node *pattern, const char *val)
1606{
1607	struct stackmark smark;
1608	int result;
1609	char *p;
1610
1611	setstackmark(&smark);
1612	argbackq = pattern->narg.backquote;
1613	STARTSTACKSTR(expdest);
1614	ifslastp = NULL;
1615	argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
1616	STPUTC('\0', expdest);
1617	p = grabstackstr(expdest);
1618	result = patmatch(p, val, 0);
1619	popstackmark(&smark);
1620	return result;
1621}
1622
1623/*
1624 * Our own itoa().
1625 */
1626
1627static char *
1628cvtnum(int num, char *buf)
1629{
1630	char temp[32];
1631	int neg = num < 0;
1632	char *p = temp + 31;
1633
1634	temp[31] = '\0';
1635
1636	do {
1637		*--p = num % 10 + '0';
1638	} while ((num /= 10) != 0);
1639
1640	if (neg)
1641		*--p = '-';
1642
1643	STPUTS(p, buf);
1644	return buf;
1645}
1646
1647/*
1648 * Do most of the work for wordexp(3).
1649 */
1650
1651int
1652wordexpcmd(int argc, char **argv)
1653{
1654	size_t len;
1655	int i;
1656
1657	out1fmt("%08x", argc - 1);
1658	for (i = 1, len = 0; i < argc; i++)
1659		len += strlen(argv[i]);
1660	out1fmt("%08x", (int)len);
1661	for (i = 1; i < argc; i++)
1662		outbin(argv[i], strlen(argv[i]) + 1, out1);
1663        return (0);
1664}
1665
1666/*
1667 * Do most of the work for wordexp(3), new version.
1668 */
1669
1670int
1671freebsd_wordexpcmd(int argc __unused, char **argv __unused)
1672{
1673	struct arglist arglist;
1674	union node *args, *n;
1675	struct strlist *sp;
1676	size_t count, len;
1677	int ch;
1678	int protected = 0;
1679	int fd = -1;
1680
1681	while ((ch = nextopt("f:p")) != '\0') {
1682		switch (ch) {
1683		case 'f':
1684			fd = number(shoptarg);
1685			break;
1686		case 'p':
1687			protected = 1;
1688			break;
1689		}
1690	}
1691	if (*argptr != NULL)
1692		error("wrong number of arguments");
1693	if (fd < 0)
1694		error("missing fd");
1695	INTOFF;
1696	setinputfd(fd, 1);
1697	INTON;
1698	args = parsewordexp();
1699	popfile(); /* will also close fd */
1700	if (protected)
1701		for (n = args; n != NULL; n = n->narg.next) {
1702			if (n->narg.backquote != NULL) {
1703				outcslow('C', out1);
1704				error("command substitution disabled");
1705			}
1706		}
1707	outcslow(' ', out1);
1708	arglist.lastp = &arglist.list;
1709	for (n = args; n != NULL; n = n->narg.next)
1710		expandarg(n, &arglist, EXP_FULL | EXP_TILDE);
1711	*arglist.lastp = NULL;
1712	for (sp = arglist.list, count = len = 0; sp; sp = sp->next)
1713		count++, len += strlen(sp->text);
1714	out1fmt("%016zx %016zx", count, len);
1715	for (sp = arglist.list; sp; sp = sp->next)
1716		outbin(sp->text, strlen(sp->text) + 1, out1);
1717	return (0);
1718}
1719