home *** CD-ROM | disk | FTP | other *** search
/ Il CD di internet / CD.iso / SOURCE / AP / ASH / ASH-LINU.2 / ASH-LINU / ash-linux-0.2 / expand.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-08-24  |  22.0 KB  |  1,112 lines

  1. /*-
  2.  * Copyright (c) 1991 The Regents of the University of California.
  3.  * All rights reserved.
  4.  *
  5.  * This code is derived from software contributed to Berkeley by
  6.  * Kenneth Almquist.
  7.  *
  8.  * Redistribution and use in source and binary forms, with or without
  9.  * modification, are permitted provided that the following conditions
  10.  * are met:
  11.  * 1. Redistributions of source code must retain the above copyright
  12.  *    notice, this list of conditions and the following disclaimer.
  13.  * 2. Redistributions in binary form must reproduce the above copyright
  14.  *    notice, this list of conditions and the following disclaimer in the
  15.  *    documentation and/or other materials provided with the distribution.
  16.  * 3. All advertising materials mentioning features or use of this software
  17.  *    must display the following acknowledgement:
  18.  *    This product includes software developed by the University of
  19.  *    California, Berkeley and its contributors.
  20.  * 4. Neither the name of the University nor the names of its contributors
  21.  *    may be used to endorse or promote products derived from this software
  22.  *    without specific prior written permission.
  23.  *
  24.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  25.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  26.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  27.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  28.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  29.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  30.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  31.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  32.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  33.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  34.  * SUCH DAMAGE.
  35.  */
  36.  
  37. #ifndef lint
  38. /*static char sccsid[] = "from: @(#)expand.c    5.1 (Berkeley) 3/7/91";*/
  39. static char rcsid[] = "expand.c,v 1.5 1993/08/01 18:58:16 mycroft Exp";
  40. #endif /* not lint */
  41.  
  42. /*
  43.  * Routines to expand arguments to commands.  We have to deal with
  44.  * backquotes, shell variables, and file metacharacters.
  45.  */
  46.  
  47. #include "shell.h"
  48. #include "main.h"
  49. #include "nodes.h"
  50. #include "eval.h"
  51. #include "expand.h"
  52. #include "syntax.h"
  53. #include "parser.h"
  54. #include "jobs.h"
  55. #include "options.h"
  56. #include "var.h"
  57. #include "input.h"
  58. #include "output.h"
  59. #include "memalloc.h"
  60. #include "error.h"
  61. #include "mystring.h"
  62. #include <sys/types.h>
  63. #include <sys/stat.h>
  64. #include <errno.h>
  65. #include <dirent.h>
  66.  
  67. /*
  68.  * Structure specifying which parts of the string should be searched
  69.  * for IFS characters.
  70.  */
  71.  
  72. struct ifsregion {
  73.     struct ifsregion *next;    /* next region in list */
  74.     int begoff;        /* offset of start of region */
  75.     int endoff;        /* offset of end of region */
  76.     int nulonly;        /* search for nul bytes only */
  77. };
  78.  
  79.  
  80. char *expdest;            /* output of current string */
  81. struct nodelist *argbackq;    /* list of back quote expressions */
  82. struct ifsregion ifsfirst;    /* first struct in list of ifs regions */
  83. struct ifsregion *ifslastp;    /* last struct in list */
  84. struct arglist exparg;        /* holds expanded arg list */
  85. #if UDIR
  86. /*
  87.  * Set if the last argument processed had /u/logname expanded.  This
  88.  * variable is read by the cd command.
  89.  */
  90. int didudir;
  91. #endif
  92.  
  93. #ifdef __STDC__
  94. STATIC void argstr(char *, int);
  95. STATIC void expbackq(union node *, int, int);
  96. STATIC char *evalvar(char *, int);
  97. STATIC int varisset(int);
  98. STATIC void varvalue(int, int, int);
  99. STATIC void recordregion(int, int, int);
  100. STATIC void ifsbreakup(char *, struct arglist *);
  101. STATIC void expandmeta(struct strlist *);
  102. STATIC void expmeta(char *, char *);
  103. STATIC void addfname(char *);
  104. STATIC struct strlist *expsort(struct strlist *);
  105. STATIC struct strlist *msort(struct strlist *, int);
  106. STATIC int pmatch(char *, char *);
  107. #else
  108. STATIC void argstr();
  109. STATIC void expbackq();
  110. STATIC char *evalvar();
  111. STATIC int varisset();
  112. STATIC void varvalue();
  113. STATIC void recordregion();
  114. STATIC void ifsbreakup();
  115. STATIC void expandmeta();
  116. STATIC void expmeta();
  117. STATIC void addfname();
  118. STATIC struct strlist *expsort();
  119. STATIC struct strlist *msort();
  120. STATIC int pmatch();
  121. #endif
  122. #if UDIR
  123. #ifdef __STDC__
  124. STATIC char *expudir(char *);
  125. #else
  126. STATIC char *expudir();
  127. #endif
  128. #endif /* UDIR */
  129.  
  130.  
  131.  
  132. /*
  133.  * Expand shell variables and backquotes inside a here document.
  134.  */
  135.  
  136. void
  137. expandhere(arg, fd)
  138.     union node *arg;    /* the document */
  139.     int fd;            /* where to write the expanded version */
  140.     {
  141.     herefd = fd;
  142.     expandarg(arg, (struct arglist *)NULL, 0);
  143.     xwrite(fd, stackblock(), expdest - stackblock());
  144. }
  145.  
  146.  
  147. /*
  148.  * Perform variable substitution and command substitution on an argument,
  149.  * placing the resulting list of arguments in arglist.  If full is true,
  150.  * perform splitting and file name expansion.  When arglist is NULL, perform
  151.  * here document expansion.
  152.  */
  153.  
  154. void
  155. expandarg(arg, arglist, full)
  156.     union node *arg;
  157.     struct arglist *arglist;
  158.     {
  159.     struct strlist *sp;
  160.     char *p;
  161.  
  162. #if UDIR
  163.     didudir = 0;
  164. #endif
  165.     argbackq = arg->narg.backquote;
  166.     STARTSTACKSTR(expdest);
  167.     ifsfirst.next = NULL;
  168.     ifslastp = NULL;
  169.     argstr(arg->narg.text, full);
  170.     if (arglist == NULL)
  171.         return;            /* here document expanded */
  172.     STPUTC('\0', expdest);
  173.     p = grabstackstr(expdest);
  174.     exparg.lastp = &exparg.list;
  175.     if (full) {
  176.         ifsbreakup(p, &exparg);
  177.         *exparg.lastp = NULL;
  178.         exparg.lastp = &exparg.list;
  179.         expandmeta(exparg.list);
  180.     } else {
  181.         sp = (struct strlist *)stalloc(sizeof (struct strlist));
  182.         sp->text = p;
  183.         *exparg.lastp = sp;
  184.         exparg.lastp = &sp->next;
  185.     }
  186.     while (ifsfirst.next != NULL) {
  187.         struct ifsregion *ifsp;
  188.         INTOFF;
  189.         ifsp = ifsfirst.next->next;
  190.         ckfree(ifsfirst.next);
  191.         ifsfirst.next = ifsp;
  192.         INTON;
  193.     }
  194.     *exparg.lastp = NULL;
  195.     if (exparg.list) {
  196.         *arglist->lastp = exparg.list;
  197.         arglist->lastp = exparg.lastp;
  198.     }
  199. }
  200.  
  201.  
  202.  
  203. /*
  204.  * Perform variable and command substitution.  If full is set, output CTLESC
  205.  * characters to allow for further processing.  If full is not set, treat
  206.  * $@ like $* since no splitting will be performed.
  207.  */
  208.  
  209. STATIC void
  210. argstr(p, full)
  211.     register char *p;
  212.     {
  213.     char c;
  214.  
  215.     for (;;) {
  216.         switch (c = *p++) {
  217.         case '\0':
  218.         case CTLENDVAR:
  219.             goto breakloop;
  220.         case CTLESC:
  221.             if (full)
  222.                 STPUTC(c, expdest);
  223.             c = *p++;
  224.             STPUTC(c, expdest);
  225.             break;
  226.         case CTLVAR:
  227.             p = evalvar(p, full);
  228.             break;
  229.         case CTLBACKQ:
  230.         case CTLBACKQ|CTLQUOTE:
  231.             expbackq(argbackq->n, c & CTLQUOTE, full);
  232.             argbackq = argbackq->next;
  233.             break;
  234.         default:
  235.             STPUTC(c, expdest);
  236.         }
  237.     }
  238. breakloop:;
  239. }
  240.  
  241.  
  242. /*
  243.  * Expand stuff in backwards quotes.
  244.  */
  245.  
  246. STATIC void
  247. expbackq(cmd, quoted, full)
  248.     union node *cmd;
  249.     {
  250.     struct backcmd in;
  251.     int i;
  252.     char buf[128];
  253.     char *p;
  254.     char *dest = expdest;
  255.     struct ifsregion saveifs, *savelastp;
  256.     struct nodelist *saveargbackq;
  257.     char lastc;
  258.     int startloc = dest - stackblock();
  259.     char const *syntax = quoted? DQSYNTAX : BASESYNTAX;
  260.     int saveherefd;
  261.  
  262.     INTOFF;
  263.     saveifs = ifsfirst;
  264.     savelastp = ifslastp;
  265.     saveargbackq = argbackq;
  266.     saveherefd = herefd;      
  267.     herefd = -1;
  268.     p = grabstackstr(dest);
  269.     evalbackcmd(cmd, &in);
  270.     ungrabstackstr(p, dest);
  271.     ifsfirst = saveifs;
  272.     ifslastp = savelastp;
  273.     argbackq = saveargbackq;
  274.     herefd = saveherefd;
  275.  
  276.     p = in.buf;
  277.     lastc = '\0';
  278.     for (;;) {
  279.         if (--in.nleft < 0) {
  280.             if (in.fd < 0)
  281.                 break;
  282.             while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR);
  283.             TRACE(("expbackq: read returns %d\n", i));
  284.             if (i <= 0)
  285.                 break;
  286.             p = buf;
  287.             in.nleft = i - 1;
  288.         }
  289.         lastc = *p++;
  290.         if (lastc != '\0') {
  291.             if (full && syntax[lastc] == CCTL)
  292.                 STPUTC(CTLESC, dest);
  293.             STPUTC(lastc, dest);
  294.         }
  295.     }
  296.     if (lastc == '\n') {
  297.         STUNPUTC(dest);
  298.     }
  299.     if (in.fd >= 0)
  300.         close(in.fd);
  301.     if (in.buf)
  302.         ckfree(in.buf);
  303.     if (in.jp)
  304.         exitstatus = waitforjob(in.jp);
  305.     if (quoted == 0)
  306.         recordregion(startloc, dest - stackblock(), 0);
  307.     TRACE(("evalbackq: size=%d: \"%.*s\"\n",
  308.         (dest - stackblock()) - startloc,
  309.         (dest - stackblock()) - startloc,
  310.         stackblock() + startloc));
  311.     expdest = dest;
  312.     INTON;
  313. }
  314.  
  315.  
  316.  
  317. /*
  318.  * Expand a variable, and return a pointer to the next character in the
  319.  * input string.
  320.  */
  321.  
  322. STATIC char *
  323. evalvar(p, full)
  324.     char *p;
  325.     {
  326.     int subtype;
  327.     int flags;
  328.     char *var;
  329.     char *val;
  330.     int c;
  331.     int set;
  332.     int special;
  333.     int startloc;
  334.  
  335.     flags = *p++;
  336.     subtype = flags & VSTYPE;
  337.     var = p;
  338.     special = 0;
  339.     if (! is_name(*p))
  340.         special = 1;
  341.     p = strchr(p, '=') + 1;
  342. again: /* jump here after setting a variable with ${var=text} */
  343.     if (special) {
  344.         set = varisset(*var);
  345.         val = NULL;
  346.     } else {
  347.         val = lookupvar(var);
  348.         if (val == NULL || (flags & VSNUL) && val[0] == '\0') {
  349.             val = NULL;
  350.             set = 0;
  351.         } else
  352.             set = 1;
  353.     }
  354.     startloc = expdest - stackblock();
  355.     if (set && subtype != VSPLUS) {
  356.         /* insert the value of the variable */
  357.         if (special) {
  358.             varvalue(*var, flags & VSQUOTE, full);
  359.         } else {
  360.             char const *syntax = (flags & VSQUOTE)? DQSYNTAX : BASESYNTAX;
  361.  
  362.             while (*val) {
  363.                 if (full && syntax[*val] == CCTL)
  364.                     STPUTC(CTLESC, expdest);
  365.                 STPUTC(*val++, expdest);
  366.             }
  367.         }
  368.     }
  369.     if (subtype == VSPLUS)
  370.         set = ! set;
  371.     if (((flags & VSQUOTE) == 0 || (*var == '@' && shellparam.nparam != 1))
  372.      && (set || subtype == VSNORMAL))
  373.         recordregion(startloc, expdest - stackblock(), flags & VSQUOTE);
  374.     if (! set && subtype != VSNORMAL) {
  375.         if (subtype == VSPLUS || subtype == VSMINUS) {
  376.             argstr(p, full);
  377.         } else {
  378.             char *startp;
  379.             int saveherefd = herefd;
  380.             herefd = -1;
  381.             argstr(p, 0);
  382.             STACKSTRNUL(expdest);
  383.             herefd = saveherefd;
  384.             startp = stackblock() + startloc;
  385.             if (subtype == VSASSIGN) {
  386.                 setvar(var, startp, 0);
  387.                 STADJUST(startp - expdest, expdest);
  388.                 flags &=~ VSNUL;
  389.                 goto again;
  390.             }
  391.             /* subtype == VSQUESTION */
  392.             if (*p != CTLENDVAR) {
  393.                 outfmt(&errout, "%s\n", startp);
  394.                 error((char *)NULL);
  395.             }
  396.             error("%.*s: parameter %snot set", p - var - 1,
  397.                 var, (flags & VSNUL)? "null or " : nullstr);
  398.         }
  399.     }
  400.     if (subtype != VSNORMAL) {    /* skip to end of alternative */
  401.         int nesting = 1;
  402.         for (;;) {
  403.             if ((c = *p++) == CTLESC)
  404.                 p++;
  405.             else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
  406.                 if (set)
  407.                     argbackq = argbackq->next;
  408.             } else if (c == CTLVAR) {
  409.                 if ((*p++ & VSTYPE) != VSNORMAL)
  410.                     nesting++;
  411.             } else if (c == CTLENDVAR) {
  412.                 if (--nesting == 0)
  413.                     break;
  414.             }
  415.         }
  416.     }
  417.     return p;
  418. }
  419.  
  420.  
  421.  
  422. /*
  423.  * Test whether a specialized variable is set.
  424.  */
  425.  
  426. STATIC int
  427. varisset(name)
  428.     char name;
  429.     {
  430.     char **ap;
  431.  
  432.     if (name == '!') {
  433.         if (backgndpid == -1)
  434.             return 0;
  435.     } else if (name == '@' || name == '*') {
  436.         if (*shellparam.p == NULL)
  437.             return 0;
  438.     } else if ((unsigned)(name -= '1') <= '9' - '1') {
  439.         ap = shellparam.p;
  440.         do {
  441.             if (*ap++ == NULL)
  442.                 return 0;
  443.         } while (--name >= 0);
  444.     }
  445.     return 1;
  446. }
  447.  
  448.  
  449.  
  450. /*
  451.  * Add the value of a specialized variable to the stack string.
  452.  */
  453.  
  454. STATIC void
  455. varvalue(name, quoted, allow_split)
  456.     char name;
  457.     {
  458.     int num;
  459.     char temp[32];
  460.     char *p;
  461.     int i;
  462.     extern int exitstatus;
  463.     char sep;
  464.     char **ap;
  465.     char const *syntax;
  466.  
  467.     switch (name) {
  468.     case '$':
  469.         num = rootpid;
  470.         goto numvar;
  471.     case '?':
  472.         num = exitstatus;
  473.         goto numvar;
  474.     case '#':
  475.         num = shellparam.nparam;
  476.         goto numvar;
  477.     case '!':
  478.         num = backgndpid;
  479. numvar:
  480.         p = temp + 31;
  481.         temp[31] = '\0';
  482.         do {
  483.             *--p = num % 10 + '0';
  484.         } while ((num /= 10) != 0);
  485.         while (*p)
  486.             STPUTC(*p++, expdest);
  487.         break;
  488.     case '-':
  489.         for (i = 0 ; optchar[i] ; i++) {
  490.             if (optval[i])
  491.                 STPUTC(optchar[i], expdest);
  492.         }
  493.         break;
  494.     case '@':
  495.         if (allow_split) {
  496.             sep = '\0';
  497.             goto allargs;
  498.         }
  499.         /* fall through */            
  500.     case '*':
  501.         sep = ' ';
  502. allargs:
  503.         /* Only emit CTLESC if we will do further processing,
  504.            i.e. if allow_split is set.  */
  505.         syntax = quoted && allow_split ? DQSYNTAX : BASESYNTAX;
  506.         for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
  507.             /* should insert CTLESC characters */
  508.             while (*p) {
  509.                 if (syntax[*p] == CCTL)
  510.                     STPUTC(CTLESC, expdest);
  511.                 STPUTC(*p++, expdest);
  512.             }
  513.             if (*ap)
  514.                 STPUTC(sep, expdest);
  515.         }
  516.         break;
  517.     case '0':
  518.         p = arg0;
  519. string:
  520.         /* Only emit CTLESC if we will do further processing,
  521.            i.e. if allow_split is set.  */
  522.         syntax = quoted && allow_split ? DQSYNTAX : BASESYNTAX;
  523.         while (*p) {
  524.             if (syntax[*p] == CCTL)
  525.                 STPUTC(CTLESC, expdest);
  526.             STPUTC(*p++, expdest);
  527.         }
  528.         break;
  529.     default:
  530.         if ((unsigned)(name -= '1') <= '9' - '1') {
  531.             p = shellparam.p[name];
  532.             goto string;
  533.         }
  534.         break;
  535.     }
  536. }
  537.  
  538.  
  539.  
  540. /*
  541.  * Record the the fact that we have to scan this region of the
  542.  * string for IFS characters.
  543.  */
  544.  
  545. STATIC void
  546. recordregion(start, end, nulonly) {
  547.     register struct ifsregion *ifsp;
  548.  
  549.     if (ifslastp == NULL) {
  550.         ifsp = &ifsfirst;
  551.     } else {
  552.         ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion));
  553.         ifslastp->next = ifsp;
  554.     }
  555.     ifslastp = ifsp;
  556.     ifslastp->next = NULL;
  557.     ifslastp->begoff = start;
  558.     ifslastp->endoff = end;
  559.     ifslastp->nulonly = nulonly;
  560. }
  561.  
  562.  
  563.  
  564. /*
  565.  * Break the argument string into pieces based upon IFS and add the
  566.  * strings to the argument list.  The regions of the string to be
  567.  * searched for IFS characters have been stored by recordregion.
  568.  */
  569.  
  570. STATIC void
  571. ifsbreakup(string, arglist)
  572.     char *string;
  573.     struct arglist *arglist;
  574.     {
  575.     struct ifsregion *ifsp;
  576.     struct strlist *sp;
  577.     char *start;
  578.     register char *p;
  579.     char *q;
  580.     char *ifs;
  581.  
  582.     start = string;
  583.     if (ifslastp != NULL) {
  584.         ifsp = &ifsfirst;
  585.         do {
  586.             p = string + ifsp->begoff;
  587.             ifs = ifsp->nulonly? nullstr : ifsval();
  588.             while (p < string + ifsp->endoff) {
  589.                 q = p;
  590.                 if (*p == CTLESC)
  591.                     p++;
  592.                 if (strchr(ifs, *p++)) {
  593.                     if (q > start || *ifs != ' ') {
  594.                         *q = '\0';
  595.                         sp = (struct strlist *)stalloc(sizeof *sp);
  596.                         sp->text = start;
  597.                         *arglist->lastp = sp;
  598.                         arglist->lastp = &sp->next;
  599.                     }
  600.                     if (*ifs == ' ') {
  601.                         for (;;) {
  602.                             if (p >= string + ifsp->endoff)
  603.                                 break;
  604.                             q = p;
  605.                             if (*p == CTLESC)
  606.                                 p++;
  607.                             if (strchr(ifs, *p++) == NULL) {
  608.                                 p = q;
  609.                                 break;
  610.                             }
  611.                         }
  612.                     }
  613.                     start = p;
  614.                 }
  615.             }
  616.         } while ((ifsp = ifsp->next) != NULL);
  617.         if (*start || (*ifs != ' ' && start > string)) {
  618.             sp = (struct strlist *)stalloc(sizeof *sp);
  619.             sp->text = start;
  620.             *arglist->lastp = sp;
  621.             arglist->lastp = &sp->next;
  622.         }
  623.     } else {
  624.         sp = (struct strlist *)stalloc(sizeof *sp);
  625.         sp->text = start;
  626.         *arglist->lastp = sp;
  627.         arglist->lastp = &sp->next;
  628.     }
  629. }
  630.  
  631.  
  632.  
  633. /*
  634.  * Expand shell metacharacters.  At this point, the only control characters
  635.  * should be escapes.  The results are stored in the list exparg.
  636.  */
  637.  
  638. char *expdir;
  639.  
  640.  
  641. STATIC void
  642. expandmeta(str)
  643.     struct strlist *str;
  644.     {
  645.     char *p;
  646.     struct strlist **savelastp;
  647.     struct strlist *sp;
  648.     char c;
  649.  
  650.     while (str) {
  651.         if (fflag)
  652.             goto nometa;
  653.         p = str->text;
  654. #if UDIR
  655.         if (p[0] == '/' && p[1] == 'u' && p[2] == '/')
  656.             str->text = p = expudir(p);
  657. #endif
  658.         for (;;) {            /* fast check for meta chars */
  659.             if ((c = *p++) == '\0')
  660.                 goto nometa;
  661.             if (c == '*' || c == '?' || c == '[' || c == '!')
  662.                 break;
  663.         }
  664.         savelastp = exparg.lastp;
  665.         INTOFF;
  666.         if (expdir == NULL)
  667.             expdir = ckmalloc(1024); /* I hope this is big enough */
  668.         expmeta(expdir, str->text);
  669.         ckfree(expdir);
  670.         expdir = NULL;
  671.         INTON;
  672.         if (exparg.lastp == savelastp) {
  673.             if (! zflag) {
  674. nometa:
  675.                 *exparg.lastp = str;
  676.                 rmescapes(str->text);
  677.                 exparg.lastp = &str->next;
  678.             }
  679.         } else {
  680.             *exparg.lastp = NULL;
  681.             *savelastp = sp = expsort(*savelastp);
  682.             while (sp->next != NULL)
  683.                 sp = sp->next;
  684.             exparg.lastp = &sp->next;
  685.         }
  686.         str = str->next;
  687.     }
  688. }
  689.  
  690.  
  691. #if UDIR
  692. /*
  693.  * Expand /u/username into the home directory for the specified user.
  694.  * We could use the getpw stuff here, but then we would have to load
  695.  * in stdio and who knows what else.
  696.  */
  697.  
  698. #define MAXLOGNAME 32
  699. #define MAXPWLINE 128
  700.  
  701. char *pfgets();
  702.  
  703.  
  704. STATIC char *
  705. expudir(path)
  706.     char *path;
  707.     {
  708.     register char *p, *q, *r;
  709.     char name[MAXLOGNAME];
  710.     char line[MAXPWLINE];
  711.     int i;
  712.  
  713.     r = path;                /* result on failure */
  714.     p = r + 3;            /* the 3 skips "/u/" */
  715.     q = name;
  716.     while (*p && *p != '/') {
  717.         if (q >= name + MAXLOGNAME - 1)
  718.             return r;        /* fail, name too long */
  719.         *q++ = *p++;
  720.     }
  721.     *q = '\0';
  722.     setinputfile("/etc/passwd", 1);
  723.     q = line + strlen(name);
  724.     while (pfgets(line, MAXPWLINE) != NULL) {
  725.         if (line[0] == name[0] && prefix(name, line) && *q == ':') {
  726.             /* skip to start of home directory */
  727.             i = 4;
  728.             do {
  729.                 while (*++q && *q != ':');
  730.             } while (--i > 0);
  731.             if (*q == '\0')
  732.                 break;        /* fail, corrupted /etc/passwd */
  733.             q++;
  734.             for (r = q ; *r && *r != '\n' && *r != ':' ; r++);
  735.             *r = '\0';        /* nul terminate home directory */
  736.             i = r - q;        /* i = strlen(q) */
  737.             r = stalloc(i + strlen(p) + 1);
  738.             scopy(q, r);
  739.             scopy(p, r + i);
  740.             TRACE(("expudir converts %s to %s\n", path, r));
  741.             didudir = 1;
  742.             path = r;        /* succeed */
  743.             break;
  744.         }
  745.     }
  746.     popfile();
  747.     return r;
  748. }
  749. #endif
  750.  
  751.  
  752. /*
  753.  * Do metacharacter (i.e. *, ?, [...]) expansion.
  754.  */
  755.  
  756. STATIC void
  757. expmeta(enddir, name)
  758.     char *enddir;
  759.     char *name;
  760.     {
  761.     register char *p;
  762.     char *q;
  763.     char *start;
  764.     char *endname;
  765.     int metaflag;
  766.     struct stat statb;
  767.     DIR *dirp;
  768.     struct dirent *dp;
  769.     int atend;
  770.     int matchdot;
  771.  
  772.     metaflag = 0;
  773.     start = name;
  774.     for (p = name ; ; p++) {
  775.         if (*p == '*' || *p == '?')
  776.             metaflag = 1;
  777.         else if (*p == '[') {
  778.             q = p + 1;
  779.             if (*q == '!')
  780.                 q++;
  781.             for (;;) {
  782.                 if (*q == CTLESC)
  783.                     q++;
  784.                 if (*q == '/' || *q == '\0')
  785.                     break;
  786.                 if (*++q == ']') {
  787.                     metaflag = 1;
  788.                     break;
  789.                 }
  790.             }
  791.         } else if (*p == '!' && p[1] == '!'    && (p == name || p[-1] == '/')) {
  792.             metaflag = 1;
  793.         } else if (*p == '\0')
  794.             break;
  795.         else if (*p == CTLESC)
  796.             p++;
  797.         if (*p == '/') {
  798.             if (metaflag)
  799.                 break;
  800.             start = p + 1;
  801.         }
  802.     }
  803.     if (metaflag == 0) {    /* we've reached the end of the file name */
  804.         if (enddir != expdir)
  805.             metaflag++;
  806.         for (p = name ; ; p++) {
  807.             if (*p == CTLESC)
  808.                 p++;
  809.             *enddir++ = *p;
  810.             if (*p == '\0')
  811.                 break;
  812.         }
  813.         if (metaflag == 0 || stat(expdir, &statb) >= 0)
  814.             addfname(expdir);
  815.         return;
  816.     }
  817.     endname = p;
  818.     if (start != name) {
  819.         p = name;
  820.         while (p < start) {
  821.             if (*p == CTLESC)
  822.                 p++;
  823.             *enddir++ = *p++;
  824.         }
  825.     }
  826.     if (enddir == expdir) {
  827.         p = ".";
  828.     } else if (enddir == expdir + 1 && *expdir == '/') {
  829.         p = "/";
  830.     } else {
  831.         p = expdir;
  832.         enddir[-1] = '\0';
  833.     }
  834.     if ((dirp = opendir(p)) == NULL)
  835.         return;
  836.     if (enddir != expdir)
  837.         enddir[-1] = '/';
  838.     if (*endname == 0) {
  839.         atend = 1;
  840.     } else {
  841.         atend = 0;
  842.         *endname++ = '\0';
  843.     }
  844.     matchdot = 0;
  845.     if (start[0] == '.' || start[0] == CTLESC && start[1] == '.')
  846.         matchdot++;
  847.     while (! int_pending() && (dp = readdir(dirp)) != NULL) {
  848.         if (dp->d_name[0] == '.' && ! matchdot)
  849.             continue;
  850.         if (patmatch(start, dp->d_name)) {
  851.             if (atend) {
  852.                 scopy(dp->d_name, enddir);
  853.                 addfname(expdir);
  854.             } else {
  855.                 char *q;
  856.                 for (p = enddir, q = dp->d_name ; *p++ = *q++ ;);
  857.                 p[-1] = '/';
  858.                 expmeta(p, endname);
  859.             }
  860.         }
  861.     }
  862.     closedir(dirp);
  863.     if (! atend)
  864.         endname[-1] = '/';
  865. }
  866.  
  867.  
  868. /*
  869.  * Add a file name to the list.
  870.  */
  871.  
  872. STATIC void
  873. addfname(name)
  874.     char *name;
  875.     {
  876.     char *p;
  877.     struct strlist *sp;
  878.  
  879.     p = stalloc(strlen(name) + 1);
  880.     scopy(name, p);
  881.     sp = (struct strlist *)stalloc(sizeof *sp);
  882.     sp->text = p;
  883.     *exparg.lastp = sp;
  884.     exparg.lastp = &sp->next;
  885. }
  886.  
  887.  
  888. /*
  889.  * Sort the results of file name expansion.  It calculates the number of
  890.  * strings to sort and then calls msort (short for merge sort) to do the
  891.  * work.
  892.  */
  893.  
  894. STATIC struct strlist *
  895. expsort(str)
  896.     struct strlist *str;
  897.     {
  898.     int len;
  899.     struct strlist *sp;
  900.  
  901.     len = 0;
  902.     for (sp = str ; sp ; sp = sp->next)
  903.         len++;
  904.     return msort(str, len);
  905. }
  906.  
  907.  
  908. STATIC struct strlist *
  909. msort(list, len)
  910.     struct strlist *list;
  911.     {
  912.     struct strlist *p, *q;
  913.     struct strlist **lpp;
  914.     int half;
  915.     int n;
  916.  
  917.     if (len <= 1)
  918.         return list;
  919.     half = len >> 1;      
  920.     p = list;
  921.     for (n = half ; --n >= 0 ; ) {
  922.         q = p;
  923.         p = p->next;
  924.     }
  925.     q->next = NULL;            /* terminate first half of list */
  926.     q = msort(list, half);        /* sort first half of list */
  927.     p = msort(p, len - half);        /* sort second half */
  928.     lpp = &list;
  929.     for (;;) {
  930.         if (strcmp(p->text, q->text) < 0) {
  931.             *lpp = p;
  932.             lpp = &p->next;
  933.             if ((p = *lpp) == NULL) {
  934.                 *lpp = q;
  935.                 break;
  936.             }
  937.         } else {
  938.             *lpp = q;
  939.             lpp = &q->next;
  940.             if ((q = *lpp) == NULL) {
  941.                 *lpp = p;
  942.                 break;
  943.             }
  944.         }
  945.     }
  946.     return list;
  947. }
  948.  
  949.  
  950.  
  951. /*
  952.  * Returns true if the pattern matches the string.
  953.  */
  954.  
  955. int
  956. patmatch(pattern, string)
  957.     char *pattern;
  958.     char *string;
  959.     {
  960.     if (pattern[0] == '!' && pattern[1] == '!')
  961.         return 1 - pmatch(pattern + 2, string);
  962.     else
  963.         return pmatch(pattern, string);
  964. }
  965.  
  966.  
  967. STATIC int
  968. pmatch(pattern, string)
  969.     char *pattern;
  970.     char *string;
  971.     {
  972.     register char *p, *q;
  973.     register char c;
  974.  
  975.     p = pattern;
  976.     q = string;
  977.     for (;;) {
  978.         switch (c = *p++) {
  979.         case '\0':
  980.             goto breakloop;
  981.         case CTLESC:
  982.             if (*q++ != *p++)
  983.                 return 0;
  984.             break;
  985.         case '?':
  986.             if (*q++ == '\0')
  987.                 return 0;
  988.             break;
  989.         case '*':
  990.             c = *p;
  991.             if (c != CTLESC && c != '?' && c != '*' && c != '[') {
  992.                 while (*q != c) {
  993.                     if (*q == '\0')
  994.                         return 0;
  995.                     q++;
  996.                 }
  997.             }
  998.             do {
  999.                 if (pmatch(p, q))
  1000.                     return 1;
  1001.             } while (*q++ != '\0');
  1002.             return 0;
  1003.         case '[': {
  1004.             char *endp;
  1005.             int invert, found;
  1006.             char chr;
  1007.  
  1008.             endp = p;
  1009.             if (*endp == '!')
  1010.                 endp++;
  1011.             for (;;) {
  1012.                 if (*endp == '\0')
  1013.                     goto dft;        /* no matching ] */
  1014.                 if (*endp == CTLESC)
  1015.                     endp++;
  1016.                 if (*++endp == ']')
  1017.                     break;
  1018.             }
  1019.             invert = 0;
  1020.             if (*p == '!') {
  1021.                 invert++;
  1022.                 p++;
  1023.             }
  1024.             found = 0;
  1025.             chr = *q++;
  1026.             c = *p++;
  1027.             do {
  1028.                 if (c == CTLESC)
  1029.                     c = *p++;
  1030.                 if (*p == '-' && p[1] != ']') {
  1031.                     p++;
  1032.                     if (*p == CTLESC)
  1033.                         p++;
  1034.                     if (chr >= c && chr <= *p)
  1035.                         found = 1;
  1036.                     p++;
  1037.                 } else {
  1038.                     if (chr == c)
  1039.                         found = 1;
  1040.                 }
  1041.             } while ((c = *p++) != ']');
  1042.             if (found == invert)
  1043.                 return 0;
  1044.             break;
  1045.         }
  1046. dft:        default:
  1047.             if (*q++ != c)
  1048.                 return 0;
  1049.             break;
  1050.         }
  1051.     }
  1052. breakloop:
  1053.     if (*q != '\0')
  1054.         return 0;
  1055.     return 1;
  1056. }
  1057.  
  1058.  
  1059.  
  1060. /*
  1061.  * Remove any CTLESC characters from a string.
  1062.  */
  1063.  
  1064. void
  1065. rmescapes(str)
  1066.     char *str;
  1067.     {
  1068.     register char *p, *q;
  1069.  
  1070.     p = str;
  1071.     while (*p != CTLESC) {
  1072.         if (*p++ == '\0')
  1073.             return;
  1074.     }
  1075.     q = p;
  1076.     while (*p) {
  1077.         if (*p == CTLESC)
  1078.             p++;
  1079.         *q++ = *p++;
  1080.     }
  1081.     *q = '\0';
  1082. }
  1083.  
  1084.  
  1085.  
  1086. /*
  1087.  * See if a pattern matches in a case statement.
  1088.  */
  1089.  
  1090. int
  1091. casematch(pattern, val)
  1092.     union node *pattern;
  1093.     char *val;
  1094.     {
  1095.     struct stackmark smark;
  1096.     int result;
  1097.     char *p;
  1098.  
  1099.     setstackmark(&smark);
  1100.     argbackq = pattern->narg.backquote;
  1101.     STARTSTACKSTR(expdest);
  1102.     ifslastp = NULL;
  1103.     /* Preserve any CTLESC characters inserted previously, so that
  1104.        we won't expand reg exps which are inside strings.  */
  1105.     argstr(pattern->narg.text, 1);
  1106.     STPUTC('\0', expdest);
  1107.     p = grabstackstr(expdest);
  1108.     result = patmatch(p, val);
  1109.     popstackmark(&smark);
  1110.     return result;
  1111. }
  1112.