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