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