home *** CD-ROM | disk | FTP | other *** search
/ Fresh Fish 5 / FreshFish_July-August1994.bin / bbs / util / pdksh-4.5.lha / pdksh-4.5 / sh / eval.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-12-01  |  14.1 KB  |  681 lines

  1. /*
  2.  * Expansion - quoting, separation, substitution, globbing
  3.  */
  4.  
  5. #ifndef lint
  6. static char *RCSid = "$Id: eval.c,v 1.2 1992/04/25 08:33:28 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.  
  52. int    ifs0 = ' ';        /* todo: first char of $IFS */
  53.  
  54. /* compile and expand word */
  55. char *
  56. substitute(cp, f)
  57.     char const *cp;
  58.     int f;
  59. {
  60.     struct source *s, *sold;
  61.  
  62.     sold = source;
  63.     s = pushs(SWSTR);
  64.     s->str = (char *) cp;
  65.     source = s;
  66.     if (yylex(ONEWORD) != LWORD)
  67.         errorf("eval:substitute error\n");
  68.     source = sold;
  69.     return evalstr(yylval.cp, f);
  70. }
  71.  
  72. /*
  73.  * expand arg-list
  74.  */
  75. char **
  76. eval(ap, f)
  77.     register char **ap;
  78. {
  79.     XPtrV w;
  80.  
  81.     if (*ap == NULL)
  82.         return ap;
  83.     XPinit(w, 32);
  84.     XPput(w, NULL);        /* space for shell name */
  85. #ifdef    SHARPBANG
  86.     XPput(w, NULL);        /* and space for one arg */
  87. #endif
  88.     while (*ap != NULL)
  89.         expand(*ap++, &w, f);
  90.     XPput(w, NULL);
  91. #ifdef    SHARPBANG
  92.     return (char **) XPclose(w) + 2;
  93. #else
  94.     return (char **) XPclose(w) + 1;
  95. #endif
  96. }
  97.  
  98. /*
  99.  * expand string
  100.  */
  101. char *
  102. evalstr(cp, f)
  103.     register char *cp;
  104.     int f;
  105. {
  106.     XPtrV w;
  107.  
  108.     XPinit(w, 1);
  109.     expand(cp, &w, f);
  110.     cp = (XPsize(w) == 0) ? "" : (char*) *XPptrv(w);
  111.     XPfree(w);
  112.     return cp;
  113. }
  114.  
  115. /* for nested substitution: ${var:=$var2} */
  116. typedef struct SubType {
  117.     short    type;        /* [=+-?%#] action after expanded word */
  118.     short    base;        /* begin position of expanded word */
  119.     char   *name;        /* name for ${var=word} */
  120. } SubType;
  121.  
  122. static void
  123. expand(cp, wp, f)
  124.     char *cp;        /* input word */
  125.     register XPtrV *wp;    /* output words */
  126.     int f;            /* DO* flags */
  127. {
  128.     register int c;
  129.     register int type = XBASE; /* expansion type */
  130.     register int quote = 0;    /* quoted */
  131.     int quotestack[11];    /* Keep this bigger than the subtype stack */
  132.     register int *qst = quotestack + 11;    /* This too, of course */
  133.     XString ds;        /* destination string */
  134.     register char *dp, *sp;    /* dest., source */
  135.     int fdo, word, combase;    /* second pass flags; have word */
  136.     Expand x;        /* expansion variables */
  137.     SubType subtype [10];    /* substitution type stack */
  138.     register SubType *st = subtype + 10;
  139.     int newlines;        /* For trailing newlines in COMSUB */
  140.     int trimming = 0;    /* flag if expanding ${var#pat} or ${var%pat} */
  141.  
  142.     if (cp == NULL)
  143.         errorf("eval:expand(NULL)\n");
  144.     if (flag[FNOGLOB])
  145.         f &= ~ DOGLOB;
  146.  
  147.     Xinit(ds, dp, 128);    /* init dest. string */
  148.     type = XBASE;
  149.     sp = cp;
  150.     fdo = 0;
  151.     word = !(f&DOBLANK);
  152.  
  153.     while (1) {
  154.         Xcheck(ds, dp);
  155.  
  156.         switch (type) {
  157.           case XBASE:    /* original prefixed string */
  158.             c = *sp++;
  159.             switch (c) {
  160.               case EOS:
  161.                 c = 0;
  162.                 break;
  163.               case CHAR:
  164.                 c = *sp++;
  165.                 break;
  166.               case QCHAR:
  167.                 quote |= 2; /* temporary quote */
  168.                 c = *sp++;
  169.                 break;
  170.               case OQUOTE:
  171.                 word = quote = 1;
  172.                 continue;
  173.               case CQUOTE:
  174.                 quote = 0;
  175.                 continue;
  176.               case COMSUB:
  177.                 type = comsub(&x, sp);
  178.                 sp = strchr(sp, 0) + 1;
  179.                 combase = Xsavepos(ds, dp);
  180.                 newlines = 0;
  181.                 continue;
  182.               case OSUBST: /* ${var{:}[=+-?]word} */
  183.                 cp = sp;         /* variable */
  184.                 sp = strchr(sp, 0) + 1;    /* skip variable */
  185.                 c = (*sp == CSUBST) ? 0 : *sp++;
  186.                 if ((c&0x7F) == '#' || (c&0x7F) == '%') {
  187.                     if (flag[FNOUNSET] &&
  188.                         strval(global(cp)) == null)
  189.                         errorf("%s: unset variable\n", cp);
  190.                     trimming++;
  191.                     type = XBASE;
  192.                     *--qst = quote;
  193.                     quote = 0;
  194.                 } else
  195.                     type = varsub(&x, cp, c);
  196.                 if (type == XBASE) {    /* expand? */
  197.                     if (st == subtype)
  198.                         errorf("ridiculous ${} nesting\n");
  199.                     --st;
  200.                     st->type = c;
  201.                     st->base = Xsavepos(ds, dp);
  202.                     st->name = cp;
  203.                 } else
  204.                     sp = wdscan(sp, CSUBST); /* skip word */
  205.                 continue;
  206.               case CSUBST: /* only get here if expanding word */
  207.                 *dp = 0;
  208.                 if (f&DOGLOB)
  209.                     f &= ~DOPAT;
  210.                 switch (st->type&0x7F) {
  211.                   case '#':
  212.                   case '%':
  213.                     *dp = 0;
  214.                     dp = Xrestpos(ds, dp, st->base);
  215.                     quote = *qst++;
  216.                     x.str = trimsub(strval(global(st->name)),
  217.                         dp, st->type);
  218.                     type = XSUB;
  219.                     trimming--;
  220.                     continue;
  221.                   case '=':
  222. #if 0
  223.                     if ((x.u.vp->flag&RDONLY))
  224.                         errorf("cannot set readonly %s\n", cp);
  225. #endif
  226.                     setstr(global(st->name), Xrestpos(ds, dp, st->base));
  227.                     break;
  228.                   case '?':
  229.                     if (dp == Xrestpos(ds, dp, st->base))
  230.                         errorf("missing value for %s\n", cp);
  231.                     else
  232.                         errorf("%s\n", Xrestpos(ds, dp, st->base));
  233.                 }
  234.                 st++;
  235.                 type = XBASE;
  236.                 continue;
  237.             }
  238.             break;
  239.  
  240.           case XSUB:
  241.             if ((c = *x.str++) == 0) {
  242.                 type = XBASE;
  243.                 continue;
  244.             }
  245.             break;
  246.  
  247.           case XARGSEP:
  248.             type = XARG;
  249.             quote = 1;
  250.           case XARG:
  251.             if ((c = *x.str++) == 0) {
  252.                 if ((x.str = *x.u.strv++) == NULL) {
  253.                     type = XBASE;
  254.                     continue;
  255.                 } else if (quote && x.split) {
  256.                     /* terminate word for "$@" */
  257.                     type = XARGSEP;
  258.                     quote = 0;
  259.                 }
  260.                 c = ifs0;
  261.             }
  262.             break;
  263.  
  264.           case XCOM:
  265.             if (newlines) {        /* Spit out saved nl's */
  266.                 c = '\n';
  267.                 --newlines;
  268.             } else {
  269.                 while ((c = getc(x.u.file)) == '\n')
  270.                     newlines++;    /* Save newlines */
  271.                 if (newlines && c != EOF) {
  272.                     ungetc(c, x.u.file);
  273.                     c = '\n';
  274.                     --newlines;
  275.                 }
  276.             }
  277.             if (c == EOF) {
  278.                 cp = Xrestpos(ds, sp, combase);
  279.                 newlines = 0;
  280.                 fclose(x.u.file);
  281.                 if (x.split)
  282.                     waitlast();
  283.                 type = XBASE;
  284.                 continue;
  285.             }
  286.             break;
  287.         }
  288.  
  289.         /* check for end of word or IFS separation */
  290.         if (c == 0 || (!quote && (f&DOBLANK) && ctype(c, C_IFS))) {
  291.             if (word) {
  292.                 *dp++ = 0;
  293.                 cp = Xclose(ds, dp);
  294.                 if (fdo&DOTILDE)
  295.                     cp = tilde(cp);
  296.                 if (fdo&DOGLOB)
  297.                     glob(cp, wp);
  298.                 else
  299.                     {XPput(*wp, cp);}
  300.                 fdo = word = 0;
  301.                 if (c != 0)
  302.                     Xinit(ds, dp, 128);
  303.             } else
  304.                 ; /* ignore IFS */
  305.             if (c == 0)
  306.                 return;
  307.         } else {
  308.             /* mark any special second pass chars */
  309.             if (!quote)
  310.                 switch (c) {
  311.                   case '*':
  312.                   case '?':
  313.                   case '[':
  314.                     if (f&(DOPAT|DOGLOB) || trimming) {
  315.                         fdo |= (f&DOGLOB);
  316.                         *dp++ = MAGIC;
  317.                     }
  318.                     break;
  319.                   case NOT:
  320.                     if ((f&(DOPAT|DOGLOB) || trimming) &&
  321.                         dp[-1] == '[' && dp[-2] == MAGIC) {
  322.                         *dp++ = MAGIC;
  323.                     }
  324.                     break;
  325.                   case '~':
  326.                     if (((f&DOTILDE) &&
  327.                          dp == Xstring(ds, dp)) ||
  328.                         (!(f&DOBLANK) && 
  329.                         (dp[-1] == '=' || dp[-1] == ':'))) {
  330.                         fdo |= DOTILDE;
  331.                         *dp++ = MAGIC;
  332.                     }
  333.                     break;
  334.                 }
  335.             else
  336.                 quote &= ~2; /* undo temporary */
  337.  
  338.             word = 1;
  339.             *dp++ = c; /* save output char */
  340.         }
  341.     }
  342. }
  343.  
  344. /*
  345.  * Prepare to generate the string returned by ${} substitution.
  346.  */
  347. static int
  348. varsub(xp, sp, stype)
  349.     register Expand *xp;
  350.     register char *sp;
  351.     int stype;
  352. {
  353.     register int c;
  354.     int type;
  355.  
  356.     /* ${#var}, string length or argc */
  357.     if (sp[0] == '#' && (c = sp[1]) != 0) {
  358.         c = (c == '*' || c == '@') ? e.loc->argc :
  359.             strlen(strval(global(sp+1)));
  360.         xp->str = strsave(ulton((unsigned long)c, 10), ATEMP);
  361.         return XSUB;
  362.     }
  363.  
  364.     c = sp[0];
  365.     if (c == '*' || c == '@') {
  366.         if (e.loc->argc == 0) {
  367.             xp->str = null;
  368.             type = XSUB;
  369.         } else {
  370.             xp->u.strv = e.loc->argv + 1;
  371.             xp->str = *xp->u.strv++;
  372.             xp->split = c == '@'; /* $@ */
  373.             type = XARG;
  374.         }
  375.     } else {
  376.         xp->str = strval(global(sp));
  377.         type = XSUB;
  378.     }
  379.  
  380.     c = stype&0x7F;
  381.     /* test the compiler's code generator */
  382.     if (c == '%' || c == '#' ||
  383.         (((stype&0x80) ? *xp->str=='\0' : xp->str==null) ? /* undef? */
  384.          c == '=' || c == '-' || c == '?' : c == '+'))
  385.         type = XBASE;    /* expand word instead of variable value */
  386.     if (type != XBASE && flag[FNOUNSET] && xp->str == null && c != '+')
  387.         errorf("%s: unset variable\n", sp);
  388.     return type;
  389. }
  390.  
  391. /*
  392.  * Run the command in $(...) and read its output.
  393.  */
  394. static int
  395. comsub(xp, cp)
  396.     register Expand *xp;
  397.     char *cp;
  398. {
  399.     Source *s;
  400.     register struct op *t;
  401.     FILE *fi;
  402.  
  403.     s = pushs(SSTRING);
  404.     s->str = cp;
  405.     t = compile(s);
  406.  
  407.     if (t != NULL && t->type == TCOM && /* $(<file) */
  408.         *t->args == NULL && *t->vars == NULL && t->ioact != NULL) {
  409.         register struct ioword *io = *t->ioact;
  410.  
  411.         if ((io->flag&IOTYPE) != IOREAD)
  412.             errorf("funny $() command\n");
  413.         fi = fopen(evalstr(io->name, DOTILDE), "r");
  414.         if (fi != NULL)
  415.             fileno(fi) = savefd(fileno(fi));
  416.         xp->split = 0;    /* no waitlast() */
  417.     } else {
  418.         int ofd1, pv[2];
  419.         openpipe(pv);
  420.         fi = fdopen(pv[0], "r");
  421.         ofd1 = savefd(1);
  422.         dup2(pv[1], 1);
  423.         close(pv[1]);
  424. #if 0
  425.         exchild(t, XXCOM|XPIPEO);
  426. #else
  427.         execute(t, XFORK|XXCOM|XPIPEO);
  428. #endif
  429.         dup2(ofd1, 1);
  430.         close(ofd1);
  431.         xp->split = 1;    /* waitlast() */
  432.     }    
  433.  
  434.     if (fi == NULL)
  435.         errorf("cannot open $() input\n");
  436.     setvbuf(fi, (char *)NULL, _IOFBF, BUFSIZ);
  437.     xp->u.file = fi;
  438.     return XCOM;
  439. }
  440.  
  441. /*
  442.  * perform #pattern and %pattern substitution in ${}
  443.  */
  444.  
  445. static char *
  446. trimsub(str, pat, how)
  447.     register char *str;
  448.     char *pat;
  449.     int how;
  450. {
  451.     register char *end = strchr(str, 0);
  452.     register char *p, c, *match;
  453.  
  454.     switch (how&0xff) {    /* UCHAR_MAX maybe? */
  455.     case '#':        /* shortest at begin */
  456.         for (p = str; p <= end; p++) {
  457.             c = *p; *p = '\0';
  458.             if (gmatch(str, pat)) {
  459.                 *p = c;
  460.                 return p;
  461.             }
  462.             *p = c;
  463.         }
  464.         break;
  465.     case '#'|0x80:        /* longest match at begin */
  466.         for (p = end; p >= str; p--) {
  467.             c = *p; *p = '\0';
  468.             if (gmatch(str, pat)) {
  469.                 *p = c;
  470.                 return p;
  471.             }
  472.             *p = c;
  473.         }
  474.         break;
  475.     case '%':        /* shortest match at end */
  476.         for (p = end; p >= str; p--) {
  477.             if (gmatch(p, pat)) {
  478.                 c = *p; *p = '\0';
  479.                 match = strsave( str, APERM );    /* APERM? */
  480.                 *p = c;
  481.                 return match;
  482.             }
  483.         }
  484.         break;
  485.     case '%'|0x80:    /* longest match at end */
  486.         for (p = str; p <= end; p++) {
  487.             if (gmatch(p, pat)) {
  488.                 c = *p; *p = '\0';
  489.                 match = strsave( str, ATEMP );    /* APERM? */
  490.                 *p = c;
  491.                 return match;
  492.             }
  493.         }
  494.         break;
  495.     }
  496.  
  497.     return str;        /* no match, return string */
  498. }
  499.  
  500. /*
  501.  * glob
  502.  * Name derived from V6's /etc/glob, the program that expanded filenames.
  503.  */
  504.  
  505. static    char   *debunk();
  506.  
  507. static void 
  508. glob(cp, wp)
  509.     char *cp;
  510.     register XPtrV *wp;
  511. {
  512.     char path [PATH];
  513.     register char *sp = cp;
  514.     int oldsize;
  515.  
  516.     oldsize = XPsize(*wp);
  517.     globit(path, path, sp, wp, 0);
  518.  
  519.     if (XPsize(*wp) == oldsize)
  520.         {XPput(*wp, debunk(cp));}
  521.     else
  522.         qsortp(XPptrv(*wp) + oldsize, (size_t)(XPsize(*wp) - oldsize), xstrcmp);
  523. }
  524.  
  525. static void
  526. globit(ds, dp, sp, wp, check)
  527.     char *ds;        /* dest path */
  528.     char *dp;        /* dest end */
  529.     char *sp;        /* source path */
  530.     register XPtrV *wp;    /* output list */
  531.     int check;        /* check dest existence */
  532. {
  533.     register char *np;    /* next source component */
  534.     register char *tsp, *tdp;
  535.  
  536.     if (sp == NULL) {    /* end of source path */
  537.         if (check && eaccess(ds, 0) < 0)
  538.             return;
  539.         XPput(*wp, strsave(ds, ATEMP));
  540.         return;
  541.     }
  542.  
  543.     if (dp > ds)
  544.         *dp++ = '/';
  545.     while (*sp == '/')
  546.         *dp++ = *sp++;
  547.     np = strchr(sp, '/');
  548.     if (np != NULL)
  549.         *np++ = 0;
  550.  
  551.     *dp = 0;
  552.     if (strchr(sp, MAGIC) == NULL) { /* contains no pattern? */
  553.         tdp = dp; tsp = sp;
  554.         while ((*tdp++ = *tsp++) != 0)
  555.             ;
  556.         --tdp;
  557.         globit(ds, tdp, np, wp, check);
  558.     } else {
  559.         DIR *dirp;
  560.         struct dirent *d;
  561.  
  562.         /* ToDo:
  563.          * should not attemp to open() special files: /dev/ttyd0/*
  564.          * opendir should do this check, but Doug Gwyn's does not.
  565.          */
  566.         dirp = opendir((*ds == 0) ? "." : ds);
  567.         if (dirp == NULL)
  568.             goto Nodir;
  569.         while ((d = readdir(dirp)) != NULL) {
  570.             tsp = d->d_name;
  571.             if (tsp[0] == '.' &&
  572.                 (tsp[1] == 0 || (tsp[1] == '.' && tsp[2] == 0)))
  573.                 continue; /* always ignore . and .. */
  574.             if ((*tsp == '.' && *sp != '.') || !gmatch(tsp, sp))
  575.                 continue;
  576.  
  577.             tdp = dp;
  578.             while ((*tdp++ = *tsp++) != 0)
  579.                 ;
  580.             --tdp;
  581.             globit(ds, tdp, np, wp, np != NULL);
  582.         }
  583.         closedir(dirp);
  584.       Nodir:;
  585.     }
  586.  
  587.     if (np != NULL)
  588.         *--np = '/';
  589. }
  590.  
  591. /* remove MAGIC from string */
  592. static char *
  593. debunk(cp)
  594.     char *cp;
  595. {
  596.     register char *dp, *sp;
  597.  
  598.     for (dp = sp = cp; *sp != 0; sp++)
  599.         if (*sp != MAGIC)
  600.             *dp++ = *sp;
  601.     *dp = 0;
  602.     return cp;
  603. }
  604.  
  605. /*
  606.  * tilde expansion
  607.  *
  608.  * based on a version by Arnold Robbins
  609.  */
  610.  
  611. static char *homedir();
  612.  
  613. static char *
  614. tilde(acp)
  615.     char *acp;
  616. {
  617.     register int c;
  618.     char path [PATH+1];
  619.     register char *cp = acp, *wp = path, *dp;
  620.     char userid [16+1];
  621.  
  622.   Again:
  623.     while (1) {
  624.         if ((c = *cp++) == 0) {
  625.             *wp = 0;
  626.             afree((void*)acp, ATEMP);
  627.             return strsave(path, ATEMP);
  628.         } else if (c == MAGIC && *cp == '~')
  629.             break;
  630.         else
  631.             *wp++ = c;
  632.     }
  633.  
  634.     dp = NULL;    /* no output substitution */
  635.     if (cp[1] == 0 || cp[1] == '/' || cp[1] == ':') /* ~ or ~/ */
  636.         dp = strval(global("HOME")), cp += 1;
  637.     else if (cp[1] == '+' && (cp[2] == '/' || cp[2] == ':' || cp[2] == 0))
  638.         dp = strval(global("PWD")), cp += 2;
  639.     else if (cp[1] == '-' && (cp[2] == '/' || cp[2] == ':' || cp[2] == 0))
  640.         dp = strval(global("OLDPWD")), cp += 2;
  641.     else if (letter(cp[1])) {
  642.         char *save = cp;
  643.         for (dp = userid, cp++; letnum(*cp) && dp < userid+16; )
  644.             *dp++ = *cp++;
  645.         *dp = 0;
  646.         dp = homedir(userid);
  647.         if (dp == NULL)
  648.             cp = save;
  649.     }
  650.     /* substitute */
  651.     if (dp != NULL)
  652.         while (*dp != 0)
  653.             *wp++ = *dp++;
  654.     goto Again;
  655. }
  656.  
  657. /*
  658.  * map userid to user's home directory.
  659.  * todo: implement a cache with the "homedirs" table.
  660.  * note that 4.3's getpw adds more than 6K to the shell,
  661.  * and the YP version probably adds much more.
  662.  * we might consider our own version of getpwnam() to keep the size down.
  663.  */
  664.  
  665. static char *
  666. homedir(name)
  667.     char *name;
  668. {
  669.     register struct tbl *ap;
  670.     register struct passwd *pw;
  671.     extern struct passwd *getpwnam();
  672.  
  673.     ap = tsearch(&homedirs, name, hash(name));
  674.     if ((ap != NULL && (ap->flag&ISSET)))
  675.         return ap->val.s;
  676.     pw = getpwnam(name);
  677.     if (pw == NULL)
  678.         return NULL;
  679.     return pw->pw_dir;
  680. }
  681.