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