home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / pdksh-4.9-src.tgz / tar.out / contrib / pdksh / sh / eval.c < prev    next >
C/C++ Source or Header  |  1996-09-28  |  19KB  |  929 lines

  1. /*
  2.  * Expansion - quoting, separation, substitution, globbing
  3.  */
  4.  
  5. #ifndef lint
  6. static char *RCSid = "$Id: eval.c,v 1.7 93/05/22 22:47:11 sjg Exp $";
  7. #endif
  8.  
  9. #include "stdh.h"
  10. #include <errno.h>
  11. #include <setjmp.h>
  12. #include <unistd.h>
  13. #include <dirent.h>
  14. #include <pwd.h>
  15. #include "sh.h"
  16. #include "expand.h"
  17.  
  18. /*
  19.  * string expansion
  20.  *
  21.  * first pass: quoting, IFS separation, ${} and $() substitution.
  22.  * second pass: filename expansion (*?[]~).
  23.  */
  24.  
  25. /* expansion generator state */
  26. typedef struct Expand {
  27.     /* int  type; */    /* see expand() */
  28.     char   *str;        /* string */
  29.     union {
  30.         char  **strv;    /* string[] */
  31.         FILE   *file;    /* file */
  32.     } u;            /* source */
  33.     short    split;        /* split "$@"*/
  34. } Expand;
  35.  
  36. #define    XBASE    0        /* scanning original */
  37. #define    XSUB    1        /* expanding ${} string */
  38. #define    XARGSEP    2        /* ifs0 between "$@" */
  39. #define    XARG    3        /* expanding $*, $@ */
  40. #define    XCOM    4        /* expanding $() */
  41.  
  42. static void     expand      ARGS((char *cp, XPtrV *wp, int f));
  43. static int      varsub      ARGS((Expand *xp, char *sp, int stype));
  44. static int      comsub      ARGS((Expand *xp, char *cp));
  45. static char *   trimsub     ARGS((char *str, char *pat, int how));
  46. static void     glob        ARGS((char *cp, XPtrV *wp));
  47. static void     globit      ARGS((char *ds, char *dp, char *sp, XPtrV *wp, int check));
  48. static char *   debunk      ARGS((char *cp));
  49. static char *   tilde       ARGS((char *acp));
  50. static char *   homedir     ARGS((char *name));
  51. #ifdef ALTERNATIONS
  52. static    int    alt_expand  ARGS((char *, XPtrV *, int));
  53. static    int    alt_count   ARGS((char *));
  54. static    int    alt_scan    ARGS((char **, char **, char,     int));
  55. #endif
  56.  
  57. int    ifs0 = ' ';        /* todo: first char of $IFS */
  58.  
  59. /* compile and expand word */
  60. char *
  61. substitute(cp, f)
  62.     char const *cp;
  63.     int f;
  64. {
  65.     struct source *s, *sold;
  66.  
  67.     sold = source;
  68.     s = pushs(SWSTR);
  69.     s->str = (char *) cp;
  70.     source = s;
  71.     if (yylex(ONEWORD) != LWORD)
  72.         errorf("eval:substitute error\n");
  73.     source = sold;
  74.     return evalstr(yylval.cp, f);
  75. }
  76.  
  77. /*
  78.  * expand arg-list
  79.  */
  80. char **
  81. eval(ap, f)
  82.     register char **ap;
  83. {
  84.     XPtrV w;
  85.  
  86.     if (*ap == NULL)
  87.         return ap;
  88.     XPinit(w, 32);
  89.     XPput(w, NULL);        /* space for shell name */
  90. #ifdef    SHARPBANG
  91.     XPput(w, NULL);        /* and space for one arg */
  92. #endif
  93.     while (*ap != NULL)
  94.         expand(*ap++, &w, f);
  95.     XPput(w, NULL);
  96. #ifdef    SHARPBANG
  97.     return (char **) XPclose(w) + 2;
  98. #else
  99.     return (char **) XPclose(w) + 1;
  100. #endif
  101. }
  102.  
  103. /*
  104.  * expand string
  105.  */
  106. char *
  107. evalstr(cp, f)
  108.     register char *cp;
  109.     int f;
  110. {
  111.     XPtrV w;
  112.  
  113.     XPinit(w, 1);
  114.     expand(cp, &w, f);
  115.     cp = (XPsize(w) == 0) ? "" : (char*) *XPptrv(w);
  116.     XPfree(w);
  117.     return cp;
  118. }
  119.  
  120. /*
  121.  * expand string - return only one component
  122.  * used from iosetup to expand redirection files
  123.  */
  124. char *
  125. evalonestr(cp, f)
  126.     register char *cp;
  127.     int f;
  128. {
  129.     XPtrV w;
  130.  
  131.     XPinit(w, 1);
  132.     expand(cp, &w, f);
  133.     switch (XPsize(w)) {
  134.     case 0:
  135.         cp = "";
  136.         break;
  137.     case 1:
  138.         cp = (char*) *XPptrv(w);
  139.         break;
  140.     default:
  141.         cp = evalstr(cp, f&~DOGLOB);
  142.         break;
  143.     }
  144.     XPfree(w);
  145.     return cp;
  146. }
  147.  
  148. /* for nested substitution: ${var:=$var2} */
  149. typedef struct SubType {
  150.     short    type;        /* [=+-?%#] action after expanded word */
  151.     short    base;        /* begin position of expanded word */
  152.     char   *name;        /* name for ${var=word} */
  153. } SubType;
  154.  
  155. static void
  156. expand(cp, wp, f)
  157.     char *cp;        /* input word */
  158.     register XPtrV *wp;    /* output words */
  159.     int f;            /* DO* flags */
  160. {
  161.     register int c;
  162.     register int type = XBASE; /* expansion type */
  163.     register int quote = 0;    /* quoted */
  164.     int quotestack[11];    /* Keep this bigger than the subtype stack */
  165.     register int *qst = quotestack + 11;    /* This too, of course */
  166.     XString ds;        /* destination string */
  167.     register char *dp, *sp;    /* dest., source */
  168.     int fdo, word, combase;    /* second pass flags; have word */
  169.     Expand x;        /* expansion variables */
  170.     SubType subtype [10];    /* substitution type stack */
  171.     register SubType *st = subtype + 10;
  172.     int newlines;        /* For trailing newlines in COMSUB */
  173.     int trimming = 0;    /* flag if expanding ${var#pat} or ${var%pat} */
  174.  
  175.     if (cp == NULL)
  176.         errorf("eval:expand(NULL)\n");
  177.     if (flag[FNOGLOB])
  178.         f &= ~ DOGLOB;
  179.  
  180. #ifdef ALTERNATIONS
  181. #define NOALT    BIT(8)        /* internal to this file */
  182.                 /* prevent endless recursion */
  183.     /* look for '{' in the input word */
  184.     if (((f & NOALT) == 0) && (f & DOGLOB) &&
  185.         (dp = strchr(cp, '{')) != NULL &&
  186.         (dp[-1] == CHAR) &&
  187.             !(dp[1] == CHAR && dp[2] == '}')) {
  188.         if (alt_expand(cp, wp, f))
  189.             return;
  190.     }
  191.     f &= ~NOALT;
  192. #endif
  193.  
  194.     Xinit(ds, dp, 128);    /* init dest. string */
  195.     type = XBASE;
  196.     sp = cp;
  197.     fdo = 0;
  198.     word = !(f&DOBLANK);
  199.  
  200.     while (1) {
  201.         Xcheck(ds, dp);
  202.  
  203.         switch (type) {
  204.           case XBASE:    /* original prefixed string */
  205.             c = *sp++;
  206.             switch (c) {
  207.               case EOS:
  208.                 c = 0;
  209.                 break;
  210.               case CHAR:
  211.                 c = *sp++;
  212.                 break;
  213.               case QCHAR:
  214.                 quote |= 2; /* temporary quote */
  215.                 c = *sp++;
  216.                 break;
  217.               case OQUOTE:
  218.                 word = quote = 1;
  219.                 continue;
  220.               case CQUOTE:
  221.                 quote = 0;
  222.                 continue;
  223.               case COMSUB:
  224.                 type = comsub(&x, sp);
  225.                 sp = strchr(sp, 0) + 1;
  226.                 combase = Xsavepos(ds, dp);
  227.                 newlines = 0;
  228.                 continue;
  229.               case OSUBST: /* ${var{:}[=+-?]word} */
  230.                 cp = sp;         /* variable */
  231.                 sp = strchr(sp, 0) + 1;    /* skip variable */
  232.                 c = (*sp == CSUBST) ? 0 : *sp++;
  233.                 if ((c&0x7F) == '#' || (c&0x7F) == '%') {
  234.                     if (flag[FNOUNSET] &&
  235.                         strval(global(cp)) == null)
  236.                         errorf("%s: unset variable\n", cp);
  237.                     trimming++;
  238.                     type = XBASE;
  239.                     *--qst = quote;
  240.                     quote = 0;
  241.                 } else
  242.                     type = varsub(&x, cp, c);
  243.                 if (type == XBASE) {    /* expand? */
  244.                     if (st == subtype)
  245.                         errorf("ridiculous ${} nesting\n");
  246.                     --st;
  247.                     st->type = c;
  248.                     st->base = Xsavepos(ds, dp);
  249.                     st->name = cp;
  250.                 } else
  251.                     sp = wdscan(sp, CSUBST); /* skip word */
  252.                 continue;
  253.               case CSUBST: /* only get here if expanding word */
  254.                 *dp = 0;
  255.                 if (f&DOGLOB)
  256.                     f &= ~DOPAT;
  257.                 switch (st->type&0x7F) {
  258.                   case '#':
  259.                   case '%':
  260.                     *dp = 0;
  261.                     dp = Xrestpos(ds, dp, st->base);
  262.                     quote = *qst++;
  263.                     x.str = trimsub(strval(global(st->name)),
  264.                         dp, st->type);
  265.                     type = XSUB;
  266.                     trimming--;
  267.                     continue;
  268.                   case '=':
  269. #if 0
  270.                     if ((x.u.vp->flag&RDONLY))
  271.                         errorf("cannot set readonly %s\n", cp);
  272. #endif
  273.                     setstr(global(st->name), Xrestpos(ds, dp, st->base));
  274.                     break;
  275.                   case '?':
  276.                     if (dp == Xrestpos(ds, dp, st->base))
  277.                         errorf("missing value for %s\n", cp);
  278.                     else
  279.                         errorf("%s\n", Xrestpos(ds, dp, st->base));
  280.                 }
  281.                 st++;
  282.                 type = XBASE;
  283.                 continue;
  284.             }
  285.             break;
  286.  
  287.           case XSUB:
  288.             if ((c = *x.str++) == 0) {
  289.                 type = XBASE;
  290.                 continue;
  291.             }
  292.             break;
  293.  
  294.           case XARGSEP:
  295.             type = XARG;
  296.             quote = 1;
  297.           case XARG:
  298.             if ((c = *x.str++) == 0) {
  299.                 if ((x.str = *x.u.strv++) == NULL) {
  300.                     type = XBASE;
  301.                     continue;
  302.                 } else if (quote && x.split) {
  303.                     /* terminate word for "$@" */
  304.                     type = XARGSEP;
  305.                     quote = 0;
  306.                 }
  307.                 c = ifs0;
  308.             }
  309.             break;
  310.  
  311.           case XCOM:
  312.             if (newlines) {        /* Spit out saved nl's */
  313.                 c = '\n';
  314.                 --newlines;
  315.             } else {
  316.                 while ((c = getc(x.u.file)) == '\n')
  317.                     newlines++;    /* Save newlines */
  318.                 if (newlines && c != EOF) {
  319.                     ungetc(c, x.u.file);
  320.                     c = '\n';
  321.                     --newlines;
  322.                 }
  323.             }
  324.             if (c == EOF) {
  325.                 cp = Xrestpos(ds, sp, combase);
  326.                 newlines = 0;
  327.                 fclose(x.u.file);
  328.                 if (x.split)
  329.                     waitlast();
  330.                 type = XBASE;
  331.                 continue;
  332.             }
  333.             break;
  334.         }
  335.  
  336.         /* check for end of word or IFS separation */
  337.         if (c == 0 || (!quote && (f&DOBLANK) && ctype(c, C_IFS))) {
  338.             if (word) {
  339.                 *dp++ = 0;
  340.                 cp = Xclose(ds, dp);
  341.                 if (fdo&DOTILDE)
  342.                     cp = tilde(cp);
  343.                 if (fdo&DOGLOB)
  344.                     glob(cp, wp);
  345.                 else
  346.                     {XPput(*wp, cp);}
  347.                 fdo = word = 0;
  348.                 if (c != 0)
  349.                     Xinit(ds, dp, 128);
  350.             } else
  351.                 ; /* ignore IFS */
  352.             if (c == 0)
  353.                 return;
  354.         } else {
  355.             /* mark any special second pass chars */
  356.             if (!quote)
  357.                 switch (c) {
  358.                   case '*':
  359.                   case '?':
  360.                   case '[':
  361.                     if (f&(DOPAT|DOGLOB) || trimming) {
  362.                         fdo |= (f&DOGLOB);
  363.                         *dp++ = MAGIC;
  364.                     }
  365.                     break;
  366.                   case NOT:
  367.                     if ((f&(DOPAT|DOGLOB) || trimming) &&
  368.                         dp[-1] == '[' && dp[-2] == MAGIC) {
  369.                         *dp++ = MAGIC;
  370.                     }
  371.                     break;
  372.                   case '~':
  373.                     if (((f&DOTILDE) &&
  374.                          dp == Xstring(ds, dp)) ||
  375.                         (!(f&DOBLANK) && 
  376.                         (dp[-1] == '=' || dp[-1] == ':'))) {
  377.                         fdo |= DOTILDE;
  378.                         *dp++ = MAGIC;
  379.                     }
  380.                     break;
  381.                 }
  382.             else
  383.                 quote &= ~2; /* undo temporary */
  384.  
  385.             word = 1;
  386.             *dp++ = c; /* save output char */
  387.         }
  388.     }
  389. }
  390.  
  391. /*
  392.  * Prepare to generate the string returned by ${} substitution.
  393.  */
  394. static int
  395. varsub(xp, sp, stype)
  396.     register Expand *xp;
  397.     register char *sp;
  398.     int stype;
  399. {
  400.     register int c;
  401.     int type;
  402.  
  403.     /* ${#var}, string length or argc */
  404.     if (sp[0] == '#' && (c = sp[1]) != 0) {
  405.         c = (c == '*' || c == '@') ? e.loc->argc :
  406.             strlen(strval(global(sp+1)));
  407.         xp->str = strsave(ulton((unsigned long)c, 10), ATEMP);
  408.         return XSUB;
  409.     }
  410.  
  411.     c = sp[0];
  412.     if (c == '*' || c == '@') {
  413.         if (e.loc->argc == 0) {
  414.             xp->str = null;
  415.             type = XSUB;
  416.         } else {
  417.             xp->u.strv = e.loc->argv + 1;
  418.             xp->str = *xp->u.strv++;
  419.             xp->split = c == '@'; /* $@ */
  420.             type = XARG;
  421.         }
  422.     } else {
  423.         if ((xp->str = strval(global(sp))) == NULL)
  424.           xp->str = null;
  425.         type = XSUB;
  426.     }
  427.  
  428.     c = stype&0x7F;
  429.     /* test the compiler's code generator */
  430.     if (c == '%' || c == '#' ||
  431.         (((stype&0x80) ? *xp->str=='\0' : xp->str==null) ? /* undef? */
  432.          c == '=' || c == '-' || c == '?' : c == '+'))
  433.         type = XBASE;    /* expand word instead of variable value */
  434.     if (type != XBASE && flag[FNOUNSET] && xp->str == null && c != '+')
  435.         errorf("%s: unset variable\n", sp);
  436.     return type;
  437. }
  438.  
  439. /*
  440.  * Run the command in $(...) and read its output.
  441.  */
  442. static int
  443. comsub(xp, cp)
  444.     register Expand *xp;
  445.     char *cp;
  446. {
  447.     Source *s;
  448.     register struct op *t;
  449.     FILE *fi;
  450.  
  451.     s = pushs(SSTRING);
  452.     s->str = cp;
  453.     t = compile(s);
  454.  
  455.     if (t != NULL && t->type == TCOM && /* $(<file) */
  456.         *t->args == NULL && *t->vars == NULL && t->ioact != NULL) {
  457.         register struct ioword *io = *t->ioact;
  458.  
  459.         if ((io->flag&IOTYPE) != IOREAD)
  460.             errorf("funny $() command\n");
  461.         fi = fopen(evalstr(io->name, DOTILDE), "r");
  462.         if (fi != NULL)
  463.             fileno(fi) = savefd(fileno(fi));
  464.         xp->split = 0;    /* no waitlast() */
  465.     } else {
  466.         int ofd1, pv[2];
  467.         openpipe(pv);
  468.         fi = fdopen(pv[0], "r");
  469.         ofd1 = savefd(1);
  470.         dup2(pv[1], 1);
  471.         close(pv[1]);
  472.         (void) fd_clexec(pv[0]);
  473.         (void) fd_clexec(ofd1);
  474.         execute(t, XFORK|XXCOM|XPIPEO);
  475.         dup2(ofd1, 1);
  476.         close(ofd1);
  477.         xp->split = 1;    /* waitlast() */
  478.     }    
  479.  
  480.     if (fi == NULL)
  481.         errorf("cannot open $() input\n");
  482.     setvbuf(fi, (char *)NULL, _IOFBF, BUFSIZ);
  483.     xp->u.file = fi;
  484.     return XCOM;
  485. }
  486.  
  487. /*
  488.  * perform #pattern and %pattern substitution in ${}
  489.  */
  490.  
  491. static char *
  492. trimsub(str, pat, how)
  493.     register char *str;
  494.     char *pat;
  495.     int how;
  496. {
  497.     register char *end = strchr(str, 0);
  498.     register char *p, c, *match;
  499.  
  500.     switch (how&0xff) {    /* UCHAR_MAX maybe? */
  501.     case '#':        /* shortest at begin */
  502.         for (p = str; p <= end; p++) {
  503.             c = *p; *p = '\0';
  504.             if (gmatch(str, pat)) {
  505.                 *p = c;
  506.                 return p;
  507.             }
  508.             *p = c;
  509.         }
  510.         break;
  511.     case '#'|0x80:        /* longest match at begin */
  512.         for (p = end; p >= str; p--) {
  513.             c = *p; *p = '\0';
  514.             if (gmatch(str, pat)) {
  515.                 *p = c;
  516.                 return p;
  517.             }
  518.             *p = c;
  519.         }
  520.         break;
  521.     case '%':        /* shortest match at end */
  522.         for (p = end; p >= str; p--) {
  523.             if (gmatch(p, pat)) {
  524.                 c = *p; *p = '\0';
  525.                 match = strsave( str, APERM );    /* APERM? */
  526.                 *p = c;
  527.                 return match;
  528.             }
  529.         }
  530.         break;
  531.     case '%'|0x80:    /* longest match at end */
  532.         for (p = str; p <= end; p++) {
  533.             if (gmatch(p, pat)) {
  534.                 c = *p; *p = '\0';
  535.                 match = strsave( str, ATEMP );    /* APERM? */
  536.                 *p = c;
  537.                 return match;
  538.             }
  539.         }
  540.         break;
  541.     }
  542.  
  543.     return str;        /* no match, return string */
  544. }
  545.  
  546. #ifdef ALTERNATIONS
  547. /*    (pc@hillside.co.uk)
  548.  *    I have decided to `fudge' alternations by picking up
  549.  *    the compiled command tree and working with it recursively
  550.  *    to generate the set of arguments
  551.  *    This has the advantage of making a single discrete change
  552.  *    to the code
  553.  *
  554.  *    This routine calls itself recursively
  555.  *    a)    scan forward looking for { building the output string
  556.  *        if none found then call expand - and exit
  557.  *    b)    When { found, scan forward finding the end }
  558.  *    c)    add first alternate to output string
  559.  *    d)    scan for the end of the string copying into output
  560.  *    e)    call routine with new string
  561.  *    Major complication is quoting
  562.  */
  563. static int
  564. alt_expand(cp, wp, f)
  565.     char *cp;        /* input word */
  566.     register XPtrV *wp;    /* output words */
  567.     int f;            /* DO* flags */
  568. {
  569.     char *srcp = cp;
  570.     char *left;        /* destination string of left hand side */
  571.     char *leftend;        /* end of left hand side */
  572.     char *alt;        /* start of alterate section */
  573.     char *altend;        /* end of alternate section */
  574.     char *ap;        /* working pointer */
  575.     char *right;        /* right hand side */
  576.     char *rp;        /* used to copy right-hand side */
  577.     int  maxlen;        /* max string length */
  578.  
  579.     leftend = left = alloc((maxlen = alt_count(cp)), ATEMP);
  580.     
  581.     if (alt_scan(&srcp, &leftend, '{', 0) == 0) {
  582.         expand(cp, wp, f&NOALT);
  583.         afree(left, ATEMP);
  584.         return;
  585.     }
  586.  
  587.     /*
  588.      *    we have a alternation section
  589.      */
  590.     alt = altend = alloc(maxlen, ATEMP);
  591.  
  592.     srcp += 2;
  593.     if (alt_scan(&srcp, &altend, '}', 1) == 0) {
  594.         afree(left, ATEMP);
  595.         afree(alt, ATEMP);
  596.         errorf("Missing }.\n");
  597.     }
  598.     *altend++ = CHAR;
  599.     *altend++ = ',';
  600.     *altend = EOS;
  601.     /*
  602.      *    finally we may have a right-hand side
  603.      */
  604.     right = srcp + 2;
  605.  
  606.     /*
  607.      *    glue the bits together making a new string
  608.      */
  609.     for (srcp = alt; *srcp != EOS;) {
  610.  
  611.         ap = leftend;
  612.  
  613.         if (alt_scan(&srcp, &ap, ',', -1) == 0) {
  614.             afree(left, ATEMP);
  615.             afree(alt, ATEMP);
  616.             errorf("Missing comma.\n");
  617.         }
  618.         
  619.         srcp += 2;
  620.  
  621.         rp = right;
  622.         (void) alt_scan(&rp, &ap, EOS, 0);
  623.  
  624.         alt_expand(left, wp, f);
  625.     }
  626.     afree(left, ATEMP);
  627.     afree(alt, ATEMP);
  628. }
  629.  
  630. /*
  631.  * see how much space we need to hold this tree
  632.  */
  633. static int
  634. alt_count(cp)
  635.     register char *cp;
  636. {
  637.     register int sum = 0;
  638.     register char *sp;
  639.  
  640.     while (*cp != EOS) {
  641.         switch(*cp) {
  642.           case CHAR:
  643.           case QCHAR:
  644.             sum += 2;
  645.             cp += 2;
  646.             break;
  647.           case OQUOTE:
  648.           case CQUOTE:
  649.           case CSUBST:
  650.             sum++;
  651.             cp++;
  652.             break;
  653.           case COMSUB:
  654.           case OSUBST:
  655.             sp = cp;
  656.             cp = strchr(sp, 0) + 1;
  657.             sum += cp - sp;
  658.             break;
  659.         }
  660.     }
  661.     return ++sum;
  662. }
  663.  
  664. #ifdef __STDC__
  665. static int
  666. alt_scan(
  667.     char **cpp,        /* address of source pointer */
  668.     char **dpp,        /* address of destination pointer */
  669.     char endc,        /* last character we are looking for */
  670.     int bal)
  671. #else
  672. static int
  673. alt_scan(cpp, dpp, endc, bal)
  674.     char **cpp;        /* address of source pointer */
  675.     char **dpp;        /* address of destination pointer */
  676.     char endc;        /* last character we are looking for */
  677.     int bal;
  678. #endif
  679. {
  680.     register char *cp, *dp;
  681.     int quote = 0;
  682.     int range = 0;
  683.     int balance = 0;
  684.     int usebalance = 0;
  685.  
  686.     if (bal)
  687.     {    usebalance = 1;
  688.         balance = (bal < 1) ? 0 : 1;
  689.     }
  690.  
  691.     cp = *cpp;
  692.     dp = *dpp;
  693.  
  694.     while (*cp != EOS) {
  695.         switch (*cp) {
  696.           case CHAR:
  697.             if (quote == 0) {
  698.                 if (range == 1) {
  699.                     if (cp[1] == ']')
  700.                         range = 0;
  701.                 }
  702.                 else
  703.                 if (cp[1] == '[')
  704.                     range = 1;
  705.                 else {
  706.                     if (usebalance) {
  707.                         if (cp[1] == '{')
  708.                             balance++;
  709.                         if (cp[1] == '}')
  710.                             balance--;
  711.                         }
  712.                     if (cp[1] == endc && balance == 0) {
  713.                         *dp = EOS;
  714.                         *dpp = dp;
  715.                         *cpp = cp;
  716.                         return 1;
  717.                         }
  718.                 }
  719.             }
  720.           case QCHAR:
  721.             *dp++ = *cp++;
  722.           case CSUBST:
  723.           copy:
  724.             *dp++ = *cp++;
  725.             break;
  726.             
  727.           case OQUOTE:
  728.             quote = 1;
  729.             goto copy;
  730.  
  731.           case CQUOTE:
  732.             quote = 0;
  733.             goto copy;
  734.  
  735.           case COMSUB:
  736.           case OSUBST:
  737.             while (*dp++ = *cp++);
  738.             break;
  739.         }
  740.     }
  741.     *dp = EOS;
  742.     *cpp = cp;
  743.     *dpp = dp;
  744.     return 0;
  745. }
  746. #endif    /* ALTERNATIONS */
  747.  
  748. /*
  749.  * glob
  750.  * Name derived from V6's /etc/glob, the program that expanded filenames.
  751.  */
  752.  
  753. static    char   *debunk();
  754.  
  755. static void 
  756. glob(cp, wp)
  757.     char *cp;
  758.     register XPtrV *wp;
  759. {
  760.     char path [PATH];
  761.     register char *sp = cp;
  762.     int oldsize;
  763.  
  764.     oldsize = XPsize(*wp);
  765.     globit(path, path, sp, wp, 0);
  766.  
  767.     if (XPsize(*wp) == oldsize)
  768.         {XPput(*wp, debunk(cp));}
  769.     else
  770.         qsortp(XPptrv(*wp) + oldsize, (size_t)(XPsize(*wp) - oldsize), xstrcmp);
  771. }
  772.  
  773. static void
  774. globit(ds, dp, sp, wp, check)
  775.     char *ds;        /* dest path */
  776.     char *dp;        /* dest end */
  777.     char *sp;        /* source path */
  778.     register XPtrV *wp;    /* output list */
  779.     int check;        /* check dest existence */
  780. {
  781.     register char *np;    /* next source component */
  782.     register char *tsp, *tdp;
  783.  
  784.     if (sp == NULL) {    /* end of source path */
  785.         if (check && eaccess(ds, 0) < 0)
  786.             return;
  787.         XPput(*wp, strsave(ds, ATEMP));
  788.         return;
  789.     }
  790.  
  791.     if (dp > ds)
  792.         *dp++ = '/';
  793.     while (*sp == '/')
  794.         *dp++ = *sp++;
  795.     np = strchr(sp, '/');
  796.     if (np != NULL)
  797.         *np++ = 0;
  798.  
  799.     *dp = 0;
  800.     if (strchr(sp, MAGIC) == NULL) { /* contains no pattern? */
  801.         tdp = dp; tsp = sp;
  802.         while ((*tdp++ = *tsp++) != 0)
  803.             ;
  804.         --tdp;
  805.         globit(ds, tdp, np, wp, check);
  806.     } else {
  807.         DIR *dirp;
  808.         struct dirent *d;
  809.  
  810.         /* ToDo:
  811.          * should not attemp to open() special files: /dev/ttyd0/*
  812.          * opendir should do this check, but Doug Gwyn's does not.
  813.          */
  814.         dirp = opendir((*ds == 0) ? "." : ds);
  815.         if (dirp == NULL)
  816.             goto Nodir;
  817.         while ((d = readdir(dirp)) != NULL) {
  818.             tsp = d->d_name;
  819.             if (tsp[0] == '.' &&
  820.                 (tsp[1] == 0 || (tsp[1] == '.' && tsp[2] == 0)))
  821.                 continue; /* always ignore . and .. */
  822.             if ((*tsp == '.' && *sp != '.') || !gmatch(tsp, sp))
  823.                 continue;
  824.  
  825.             tdp = dp;
  826.             while ((*tdp++ = *tsp++) != 0)
  827.                 ;
  828.             --tdp;
  829.             globit(ds, tdp, np, wp, np != NULL);
  830.         }
  831.         closedir(dirp);
  832.       Nodir:;
  833.     }
  834.  
  835.     if (np != NULL)
  836.         *--np = '/';
  837. }
  838.  
  839. /* remove MAGIC from string */
  840. static char *
  841. debunk(cp)
  842.     char *cp;
  843. {
  844.     register char *dp, *sp;
  845.  
  846.     for (dp = sp = cp; *sp != 0; sp++)
  847.         if (*sp != MAGIC)
  848.             *dp++ = *sp;
  849.     *dp = 0;
  850.     return cp;
  851. }
  852.  
  853. /*
  854.  * tilde expansion
  855.  *
  856.  * based on a version by Arnold Robbins
  857.  */
  858.  
  859. static char *homedir();
  860.  
  861. static char *
  862. tilde(acp)
  863.     char *acp;
  864. {
  865.     register int c;
  866.     char path [PATH+1];
  867.     register char *cp = acp, *wp = path, *dp;
  868.     char userid [16+1];
  869.  
  870.   Again:
  871.     while (1) {
  872.         if ((c = *cp++) == 0) {
  873.             *wp = 0;
  874.             afree((void*)acp, ATEMP);
  875.             return strsave(path, ATEMP);
  876.         } else if (c == MAGIC && *cp == '~')
  877.             break;
  878.         else
  879.             *wp++ = c;
  880.     }
  881.  
  882.     dp = NULL;    /* no output substitution */
  883.     if (cp[1] == 0 || cp[1] == '/' || cp[1] == ':') /* ~ or ~/ */
  884.         dp = strval(global("HOME")), cp += 1;
  885.     else if (cp[1] == '+' && (cp[2] == '/' || cp[2] == ':' || cp[2] == 0))
  886.         dp = strval(global("PWD")), cp += 2;
  887.     else if (cp[1] == '-' && (cp[2] == '/' || cp[2] == ':' || cp[2] == 0))
  888.         dp = strval(global("OLDPWD")), cp += 2;
  889.     else if (letter(cp[1])) {
  890.         char *save = cp;
  891.         for (dp = userid, cp++; letnum(*cp) && dp < userid+16; )
  892.             *dp++ = *cp++;
  893.         *dp = 0;
  894.         dp = homedir(userid);
  895.         if (dp == NULL)
  896.             cp = save;
  897.     }
  898.     /* substitute */
  899.     if (dp != NULL)
  900.         while (*dp != 0)
  901.             *wp++ = *dp++;
  902.     goto Again;
  903. }
  904.  
  905. /*
  906.  * map userid to user's home directory.
  907.  * todo: implement a cache with the "homedirs" table.
  908.  * note that 4.3's getpw adds more than 6K to the shell,
  909.  * and the YP version probably adds much more.
  910.  * we might consider our own version of getpwnam() to keep the size down.
  911.  */
  912.  
  913. static char *
  914. homedir(name)
  915.     char *name;
  916. {
  917.     register struct tbl *ap;
  918.     register struct passwd *pw;
  919.     extern struct passwd *getpwnam();
  920.  
  921.     ap = tsearch(&homedirs, name, hash(name));
  922.     if ((ap != NULL && (ap->flag&ISSET)))
  923.         return ap->val.s;
  924.     pw = getpwnam(name);
  925.     if (pw == NULL)
  926.         return NULL;
  927.     return pw->pw_dir;
  928. }
  929.