home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Professional / OS2PRO194.ISO / os2 / sysutils / kornshel / eval.c < prev    next >
C/C++ Source or Header  |  1992-09-06  |  20KB  |  937 lines

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