Subversion Repositories HelenOS

Rev

Rev 2714 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

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