home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / World_Of_Computer_Software-02-385-Vol-1of3.iso / k / ksh48.zip / sh / eval.c < prev    next >
C/C++ Source or Header  |  1993-01-12  |  20KB  |  935 lines

  1. /*
  2.  * Expansion - quoting, separation, substitution, globbing
  3.  */
  4.  
  5. #ifndef lint
  6. static char *RCSid = "$Id: eval.c,v 1.5 1992/12/05 13:15:25 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.         (void) fd_clexec(pv[0]);
  477.         (void) fd_clexec(ofd1);
  478.         execute(t, XFORK|XXCOM|XPIPEO);
  479.         dup2(ofd1, 1);
  480.         close(ofd1);
  481.         xp->split = 1;    /* waitlast() */
  482.     }
  483.  
  484.     if (fi == NULL)
  485.         errorf("cannot open $() input\n");
  486.     setvbuf(fi, (char *)NULL, _IOFBF, BUFSIZ);
  487.     xp->u.file = fi;
  488.     return XCOM;
  489. }
  490.  
  491. /*
  492.  * perform #pattern and %pattern substitution in ${}
  493.  */
  494.  
  495. static char *
  496. trimsub(str, pat, how)
  497.     register char *str;
  498.     char *pat;
  499.     int how;
  500. {
  501.     register char *end = strchr(str, 0);
  502.     register char *p, c, *match;
  503.  
  504.     switch (how&0xff) {    /* UCHAR_MAX maybe? */
  505.     case '#':        /* shortest at begin */
  506.         for (p = str; p <= end; p++) {
  507.             c = *p; *p = '\0';
  508.             if (gmatch(str, pat)) {
  509.                 *p = c;
  510.                 return p;
  511.             }
  512.             *p = c;
  513.         }
  514.         break;
  515.     case '#'|0x80:        /* longest match at begin */
  516.         for (p = end; p >= str; p--) {
  517.             c = *p; *p = '\0';
  518.             if (gmatch(str, pat)) {
  519.                 *p = c;
  520.                 return p;
  521.             }
  522.             *p = c;
  523.         }
  524.         break;
  525.     case '%':        /* shortest match at end */
  526.         for (p = end; p >= str; p--) {
  527.             if (gmatch(p, pat)) {
  528.                 c = *p; *p = '\0';
  529.                 match = strsave( str, APERM );    /* APERM? */
  530.                 *p = c;
  531.                 return match;
  532.             }
  533.         }
  534.         break;
  535.     case '%'|0x80:    /* longest match at end */
  536.         for (p = str; p <= end; p++) {
  537.             if (gmatch(p, pat)) {
  538.                 c = *p; *p = '\0';
  539.                 match = strsave( str, ATEMP );    /* APERM? */
  540.                 *p = c;
  541.                 return match;
  542.             }
  543.         }
  544.         break;
  545.     }
  546.  
  547.     return str;        /* no match, return string */
  548. }
  549.  
  550. #ifdef ALTERNATIONS
  551. /*    (pc@hillside.co.uk)
  552.  *    I have decided to `fudge' alternations by picking up
  553.  *    the compiled command tree and working with it recursively
  554.  *    to generate the set of arguments
  555.  *    This has the advantage of making a single discrete change
  556.  *    to the code
  557.  *
  558.  *    This routine calls itself recursively
  559.  *    a)    scan forward looking for { building the output string
  560.  *        if none found then call expand - and exit
  561.  *    b)    When { found, scan forward finding the end }
  562.  *    c)    add first alternate to output string
  563.  *    d)    scan for the end of the string copying into output
  564.  *    e)    call routine with new string
  565.  *    Major complication is quoting
  566.  */
  567. static int
  568. alt_expand(cp, wp, f)
  569.     char *cp;        /* input word */
  570.     register XPtrV *wp;    /* output words */
  571.     int f;            /* DO* flags */
  572. {
  573.     char *srcp = cp;
  574.     char *left;        /* destination string of left hand side */
  575.     char *leftend;        /* end of left hand side */
  576.     char *alt;        /* start of alterate section */
  577.     char *altend;        /* end of alternate section */
  578.     char *ap;        /* working pointer */
  579.     char *right;        /* right hand side */
  580.     char *rp;        /* used to copy right-hand side */
  581.     int  maxlen;        /* max string length */
  582.  
  583.     leftend = left = alloc((maxlen = alt_count(cp)), ATEMP);
  584.     
  585.     if (alt_scan(&srcp, &leftend, '{', 0) == 0) {
  586.         expand(cp, wp, f&NOALT);
  587.         afree(left, ATEMP);
  588.         return;
  589.     }
  590.  
  591.     /*
  592.      *    we have a alternation section
  593.      */
  594.     alt = altend = alloc(maxlen, ATEMP);
  595.  
  596.     srcp += 2;
  597.     if (alt_scan(&srcp, &altend, '}', 1) == 0) {
  598.         afree(left, ATEMP);
  599.         afree(alt, ATEMP);
  600.         errorf("Missing }.\n");
  601.     }
  602.     *altend++ = CHAR;
  603.     *altend++ = ',';
  604.     *altend = EOS;
  605.     /*
  606.      *    finally we may have a right-hand side
  607.      */
  608.     right = srcp + 2;
  609.  
  610.     /*
  611.      *    glue the bits together making a new string
  612.      */
  613.     for (srcp = alt; *srcp != EOS;) {
  614.  
  615.         ap = leftend;
  616.  
  617.         if (alt_scan(&srcp, &ap, ',', -1) == 0) {
  618.             afree(left, ATEMP);
  619.             afree(alt, ATEMP);
  620.             errorf("Missing comma.\n");
  621.         }
  622.         
  623.         srcp += 2;
  624.  
  625.         rp = right;
  626.         (void) alt_scan(&rp, &ap, EOS, 0);
  627.  
  628.         alt_expand(left, wp, f);
  629.     }
  630.     afree(left, ATEMP);
  631.     afree(alt, ATEMP);
  632. }
  633.  
  634. /*
  635.  * see how much space we need to hold this tree
  636.  */
  637. static int
  638. alt_count(cp)
  639.     register char *cp;
  640. {
  641.     register int sum = 0;
  642.     register char *sp;
  643.  
  644.     while (*cp != EOS) {
  645.         switch(*cp) {
  646.           case CHAR:
  647.           case QCHAR:
  648.             sum += 2;
  649.             cp += 2;
  650.             break;
  651.           case OQUOTE:
  652.           case CQUOTE:
  653.           case CSUBST:
  654.             sum++;
  655.             cp++;
  656.             break;
  657.           case COMSUB:
  658.           case OSUBST:
  659.             sp = cp;
  660.             cp = strchr(sp, 0) + 1;
  661.             sum += cp - sp;
  662.             break;
  663.         }
  664.     }
  665.     return ++sum;
  666. }
  667.  
  668. #ifdef __STDC__
  669. static int
  670. alt_scan(
  671.     char **cpp,        /* address of source pointer */
  672.     char **dpp,        /* address of destination pointer */
  673.     char endc,        /* last character we are looking for */
  674.     int bal)
  675. #else
  676. static int
  677. alt_scan(cpp, dpp, endc, bal)
  678.     char **cpp;        /* address of source pointer */
  679.     char **dpp;        /* address of destination pointer */
  680.     char endc;        /* last character we are looking for */
  681.     int bal;
  682. #endif
  683. {
  684.     register char *cp, *dp;
  685.     int quote = 0;
  686.     int balance = 0;
  687.     int usebalance = 0;
  688.  
  689.     if (bal)
  690.     {    usebalance = 1;
  691.         balance = (bal < 1) ? 0 : 1;
  692.     }
  693.  
  694.     cp = *cpp;
  695.     dp = *dpp;
  696.  
  697.     while (*cp != EOS) {
  698.         switch (*cp) {
  699.           case CHAR:
  700.             if (quote == 1) {
  701.                 if (cp[1] == ']')
  702.                     quote = 0;
  703.             }
  704.             else
  705.             if (quote == 0) {
  706.                 if (cp[1] == '[')
  707.                     quote = 1;
  708.                 else {
  709.                     if (usebalance) {
  710.                         if (cp[1] == '{')
  711.                             balance++;
  712.                         if (cp[1] == '}')
  713.                             balance--;
  714.                         }
  715.                     if (cp[1] == endc && balance == 0) {
  716.                         *dp = EOS;
  717.                         *dpp = dp;
  718.                         *cpp = cp;
  719.                         return 1;
  720.                         }
  721.                 }
  722.             }
  723.           case QCHAR:
  724.             *dp++ = *cp++;
  725.           copy:
  726.             *dp++ = *cp++;
  727.             break;
  728.             
  729.           case OQUOTE:
  730.             quote = 1;
  731.             goto copy;
  732.  
  733.           case CQUOTE:
  734.             quote = 0;
  735.             goto copy;
  736.  
  737.           case COMSUB:
  738.           case OSUBST:
  739.             while (*dp++ = *cp++);
  740.             break;
  741.         }
  742.     }
  743.     *dp = EOS;
  744.     *cpp = cp;
  745.     *dpp = dp;
  746.     return 0;
  747. }
  748. #endif    /* ALTERNATIONS */
  749.  
  750. /*
  751.  * glob
  752.  * Name derived from V6's /etc/glob, the program that expanded filenames.
  753.  */
  754.  
  755. static    char   *debunk();
  756.  
  757. static void
  758. glob(cp, wp)
  759.     char *cp;
  760.     register XPtrV *wp;
  761. {
  762.     char path [PATH];
  763.     register char *sp = cp;
  764.     int oldsize;
  765.  
  766.     oldsize = XPsize(*wp);
  767.     globit(path, path, sp, wp, 0);
  768.  
  769.     if (XPsize(*wp) == oldsize)
  770.         {XPput(*wp, debunk(cp));}
  771.     else
  772.         qsortp(XPptrv(*wp) + oldsize, (size_t)(XPsize(*wp) - oldsize), xstrcmp);
  773. }
  774.  
  775. static void
  776. globit(ds, dp, sp, wp, check)
  777.     char *ds;        /* dest path */
  778.     char *dp;        /* dest end */
  779.     char *sp;        /* source path */
  780.     register XPtrV *wp;    /* output list */
  781.     int check;        /* check dest existence */
  782. {
  783.     register char *np;    /* next source component */
  784.     register char *tsp, *tdp;
  785.  
  786.     if (sp == NULL) {    /* end of source path */
  787.         if (check && eaccess(ds, 0) < 0)
  788.             return;
  789.         XPput(*wp, strsave(ds, ATEMP));
  790.         return;
  791.     }
  792.  
  793.     if (dp > ds)
  794.         *dp++ = DIRSEP;
  795.     while (ISDIRSEP(*sp))
  796.         *dp++ = *sp++;
  797.     np = index_sep(sp);
  798.     if (np != NULL)
  799.         *np++ = 0;
  800.  
  801.     *dp = 0;
  802.     if (strchr(sp, MAGIC) == NULL) { /* contains no pattern? */
  803.         tdp = dp; tsp = sp;
  804.         while ((*tdp++ = *tsp++) != 0)
  805.             ;
  806.         --tdp;
  807.         globit(ds, tdp, np, wp, check);
  808.     } else {
  809.         DIR *dirp;
  810.         struct dirent *d;
  811.  
  812.         /* ToDo:
  813.          * should not attemp to open() special files: /dev/ttyd0/*
  814.          * opendir should do this check, but Doug Gwyn's does not.
  815.          */
  816.         dirp = opendir((*ds == 0) ? "." : ds);
  817.         if (dirp == NULL)
  818.             goto Nodir;
  819.         while ((d = readdir(dirp)) != NULL) {
  820.             tsp = d->d_name;
  821.             if (tsp[0] == '.' &&
  822.                 (tsp[1] == 0 || (tsp[1] == '.' && tsp[2] == 0)))
  823.                 continue; /* always ignore . and .. */
  824.             if ((*tsp == '.' && *sp != '.') || !gmatch(tsp, sp))
  825.                 continue;
  826.  
  827.             tdp = dp;
  828.             while ((*tdp++ = *tsp++) != 0)
  829.                 ;
  830.             --tdp;
  831.             globit(ds, tdp, np, wp, np != NULL);
  832.         }
  833.         closedir(dirp);
  834.       Nodir:;
  835.     }
  836.  
  837.     if (np != NULL)
  838.         *--np = DIRSEP;
  839. }
  840.  
  841. /* remove MAGIC from string */
  842. static char *
  843. debunk(cp)
  844.     char *cp;
  845. {
  846.     register char *dp, *sp;
  847.  
  848.     for (dp = sp = cp; *sp != 0; sp++)
  849.         if (*sp != MAGIC)
  850.             *dp++ = *sp;
  851.     *dp = 0;
  852.     return cp;
  853. }
  854.  
  855. /*
  856.  * tilde expansion
  857.  *
  858.  * based on a version by Arnold Robbins
  859.  */
  860.  
  861. static char *homedir();
  862.  
  863. static char *
  864. tilde(acp)
  865.     char *acp;
  866. {
  867.     register int c;
  868.     char path [PATH+1];
  869.     register char *cp = acp, *wp = path, *dp;
  870.     char userid [16+1];
  871.  
  872.   Again:
  873.     while (1) {
  874.         if ((c = *cp++) == 0) {
  875.             *wp = 0;
  876.             afree((void*)acp, ATEMP);
  877.             return strsave(path, ATEMP);
  878.         } else if (c == MAGIC && *cp == '~')
  879.             break;
  880.         else
  881.             *wp++ = c;
  882.     }
  883.  
  884.     dp = NULL;    /* no output substitution */
  885.     if (cp[1] == 0 || ISDIRSEP(cp[1]) || cp[1] == ':') /* ~ or ~/ */
  886.         dp = strval(global("HOME")), cp += 1;
  887.     else if (cp[1] == '+' && (ISDIRSEP(cp[2]) || cp[2] == ':' || cp[2] == 0))
  888.         dp = strval(global("PWD")), cp += 2;
  889.     else if (cp[1] == '-' && (ISDIRSEP(cp[2]) || cp[2] == ':' || cp[2] == 0))
  890.         dp = strval(global("OLDPWD")), cp += 2;
  891.     else if (letter(cp[1])) {
  892.         char *save = cp;
  893.         for (dp = userid, cp++; letnum(*cp) && dp < userid+16; )
  894.             *dp++ = *cp++;
  895.         *dp = 0;
  896.         dp = homedir(userid);
  897.         if (dp == NULL)
  898.             cp = save;
  899.     }
  900.     /* substitute */
  901.     if (dp != NULL)
  902.         while (*dp != 0)
  903.             *wp++ = *dp++;
  904.     goto Again;
  905. }
  906.  
  907. /*
  908.  * map userid to user's home directory.
  909.  * todo: implement a cache with the "homedirs" table.
  910.  * note that 4.3's getpw adds more than 6K to the shell,
  911.  * and the YP version probably adds much more.
  912.  * we might consider our own version of getpwnam() to keep the size down.
  913.  */
  914.  
  915. static char *
  916. homedir(name)
  917.     char *name;
  918. {
  919. #ifdef OS2
  920.         return getenv("HOME");
  921. #else
  922.     register struct tbl *ap;
  923.     register struct passwd *pw;
  924.     extern struct passwd *getpwnam();
  925.  
  926.     ap = tsearch(&homedirs, name, hash(name));
  927.     if ((ap != NULL && (ap->flag&ISSET)))
  928.         return ap->val.s;
  929.     pw = getpwnam(name);
  930.     if (pw == NULL)
  931.         return NULL;
  932.     return pw->pw_dir;
  933. #endif
  934. }
  935.