home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / bsd_srcs / usr.bin / rdist / expand.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-04-18  |  11.7 KB  |  650 lines

  1. /*
  2.  * Copyright (c) 1983 Regents of the University of California.
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  * 1. Redistributions of source code must retain the above copyright
  9.  *    notice, this list of conditions and the following disclaimer.
  10.  * 2. Redistributions in binary form must reproduce the above copyright
  11.  *    notice, this list of conditions and the following disclaimer in the
  12.  *    documentation and/or other materials provided with the distribution.
  13.  * 3. All advertising materials mentioning features or use of this software
  14.  *    must display the following acknowledgement:
  15.  *    This product includes software developed by the University of
  16.  *    California, Berkeley and its contributors.
  17.  * 4. Neither the name of the University nor the names of its contributors
  18.  *    may be used to endorse or promote products derived from this software
  19.  *    without specific prior written permission.
  20.  *
  21.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  22.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  23.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  24.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  25.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  26.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  27.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  28.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  29.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  30.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  31.  * SUCH DAMAGE.
  32.  */
  33.  
  34. #ifndef lint
  35. static char sccsid[] = "@(#)expand.c    5.6 (Berkeley) 6/1/90";
  36. #endif /* not lint */
  37.  
  38. #include "defs.h"
  39.  
  40. #define    GAVSIZ    NCARGS / 6
  41. #define LC '{'
  42. #define RC '}'
  43.  
  44. static char    shchars[] = "${[*?";
  45.  
  46. int    which;        /* bit mask of types to expand */
  47. int    eargc;        /* expanded arg count */
  48. char    **eargv;    /* expanded arg vectors */
  49. char    *path;
  50. char    *pathp;
  51. char    *lastpathp;
  52. char    *tilde;        /* "~user" if not expanding tilde, else "" */
  53. char    *tpathp;
  54. int    nleft;
  55.  
  56. int    expany;        /* any expansions done? */
  57. char    *entp;
  58. char    **sortbase;
  59.  
  60. char    *index();
  61. int    argcmp();
  62.  
  63. #define sort()    qsort((char *)sortbase, &eargv[eargc] - sortbase, \
  64.               sizeof(*sortbase), argcmp), sortbase = &eargv[eargc]
  65.  
  66. /*
  67.  * Take a list of names and expand any macros, etc.
  68.  * wh = E_VARS if expanding variables.
  69.  * wh = E_SHELL if expanding shell characters.
  70.  * wh = E_TILDE if expanding `~'.
  71.  * or any of these or'ed together.
  72.  *
  73.  * Major portions of this were snarfed from csh/sh.glob.c.
  74.  */
  75. struct namelist *
  76. expand(list, wh)
  77.     struct namelist *list;
  78.     int wh;
  79. {
  80.     register struct namelist *nl, *prev;
  81.     register int n;
  82.     char pathbuf[BUFSIZ];
  83.     char *argvbuf[GAVSIZ];
  84.  
  85.     if (debug) {
  86.         printf("expand(%x, %d)\nlist = ", list, wh);
  87.         prnames(list);
  88.     }
  89.  
  90.     if (wh == 0) {
  91.         register char *cp;
  92.  
  93.         for (nl = list; nl != NULL; nl = nl->n_next)
  94.             for (cp = nl->n_name; *cp; cp++)
  95.                 *cp = *cp & TRIM;
  96.         return(list);
  97.     }
  98.  
  99.     which = wh;
  100.     path = tpathp = pathp = pathbuf;
  101.     *pathp = '\0';
  102.     lastpathp = &path[sizeof pathbuf - 2];
  103.     tilde = "";
  104.     eargc = 0;
  105.     eargv = sortbase = argvbuf;
  106.     *eargv = 0;
  107.     nleft = NCARGS - 4;
  108.     /*
  109.      * Walk the name list and expand names into eargv[];
  110.      */
  111.     for (nl = list; nl != NULL; nl = nl->n_next)
  112.         expstr(nl->n_name);
  113.     /*
  114.      * Take expanded list of names from eargv[] and build a new list.
  115.      */
  116.     list = prev = NULL;
  117.     for (n = 0; n < eargc; n++) {
  118.         nl = makenl(NULL);
  119.         nl->n_name = eargv[n];
  120.         if (prev == NULL)
  121.             list = prev = nl;
  122.         else {
  123.             prev->n_next = nl;
  124.             prev = nl;
  125.         }
  126.     }
  127.     if (debug) {
  128.         printf("expanded list = ");
  129.         prnames(list);
  130.     }
  131.     return(list);
  132. }
  133.  
  134. expstr(s)
  135.     char *s;
  136. {
  137.     register char *cp, *cp1;
  138.     register struct namelist *tp;
  139.     char *tail;
  140.     char buf[BUFSIZ];
  141.     int savec, oeargc;
  142.     extern char homedir[];
  143.  
  144.     if (s == NULL || *s == '\0')
  145.         return;
  146.  
  147.     if ((which & E_VARS) && (cp = index(s, '$')) != NULL) {
  148.         *cp++ = '\0';
  149.         if (*cp == '\0') {
  150.             yyerror("no variable name after '$'");
  151.             return;
  152.         }
  153.         if (*cp == LC) {
  154.             cp++;
  155.             if ((tail = index(cp, RC)) == NULL) {
  156.                 yyerror("unmatched '{'");
  157.                 return;
  158.             }
  159.             *tail++ = savec = '\0';
  160.             if (*cp == '\0') {
  161.                 yyerror("no variable name after '$'");
  162.                 return;
  163.             }
  164.         } else {
  165.             tail = cp + 1;
  166.             savec = *tail;
  167.             *tail = '\0';
  168.         }
  169.         tp = lookup(cp, NULL, 0);
  170.         if (savec != '\0')
  171.             *tail = savec;
  172.         if (tp != NULL) {
  173.             for (; tp != NULL; tp = tp->n_next) {
  174.                 sprintf(buf, "%s%s%s", s, tp->n_name, tail);
  175.                 expstr(buf);
  176.             }
  177.             return;
  178.         }
  179.         sprintf(buf, "%s%s", s, tail);
  180.         expstr(buf);
  181.         return;
  182.     }
  183.     if ((which & ~E_VARS) == 0 || !strcmp(s, "{") || !strcmp(s, "{}")) {
  184.         Cat(s, "");
  185.         sort();
  186.         return;
  187.     }
  188.     if (*s == '~') {
  189.         cp = ++s;
  190.         if (*cp == '\0' || *cp == '/') {
  191.             tilde = "~";
  192.             cp1 = homedir;
  193.         } else {
  194.             tilde = cp1 = buf;
  195.             *cp1++ = '~';
  196.             do
  197.                 *cp1++ = *cp++;
  198.             while (*cp && *cp != '/');
  199.             *cp1 = '\0';
  200.             if (pw == NULL || strcmp(pw->pw_name, buf+1) != 0) {
  201.                 if ((pw = getpwnam(buf+1)) == NULL) {
  202.                     strcat(buf, ": unknown user name");
  203.                     yyerror(buf+1);
  204.                     return;
  205.                 }
  206.             }
  207.             cp1 = pw->pw_dir;
  208.             s = cp;
  209.         }
  210.         for (cp = path; *cp++ = *cp1++; )
  211.             ;
  212.         tpathp = pathp = cp - 1;
  213.     } else {
  214.         tpathp = pathp = path;
  215.         tilde = "";
  216.     }
  217.     *pathp = '\0';
  218.     if (!(which & E_SHELL)) {
  219.         if (which & E_TILDE)
  220.             Cat(path, s);
  221.         else
  222.             Cat(tilde, s);
  223.         sort();
  224.         return;
  225.     }
  226.     oeargc = eargc;
  227.     expany = 0;
  228.     expsh(s);
  229.     if (eargc == oeargc)
  230.         Cat(s, "");        /* "nonomatch" is set */
  231.     sort();
  232. }
  233.  
  234. static
  235. argcmp(a1, a2)
  236.     char **a1, **a2;
  237. {
  238.  
  239.     return (strcmp(*a1, *a2));
  240. }
  241.  
  242. /*
  243.  * If there are any Shell meta characters in the name,
  244.  * expand into a list, after searching directory
  245.  */
  246. expsh(s)
  247.     char *s;
  248. {
  249.     register char *cp;
  250.     register char *spathp, *oldcp;
  251.     struct stat stb;
  252.  
  253.     spathp = pathp;
  254.     cp = s;
  255.     while (!any(*cp, shchars)) {
  256.         if (*cp == '\0') {
  257.             if (!expany || stat(path, &stb) >= 0) {
  258.                 if (which & E_TILDE)
  259.                     Cat(path, "");
  260.                 else
  261.                     Cat(tilde, tpathp);
  262.             }
  263.             goto endit;
  264.         }
  265.         addpath(*cp++);
  266.     }
  267.     oldcp = cp;
  268.     while (cp > s && *cp != '/')
  269.         cp--, pathp--;
  270.     if (*cp == '/')
  271.         cp++, pathp++;
  272.     *pathp = '\0';
  273.     if (*oldcp == '{') {
  274.         execbrc(cp, NULL);
  275.         return;
  276.     }
  277.     matchdir(cp);
  278. endit:
  279.     pathp = spathp;
  280.     *pathp = '\0';
  281. }
  282.  
  283. matchdir(pattern)
  284.     char *pattern;
  285. {
  286.     struct stat stb;
  287.     register struct direct *dp;
  288.     DIR *dirp;
  289.  
  290.     dirp = opendir(path);
  291.     if (dirp == NULL) {
  292.         if (expany)
  293.             return;
  294.         goto patherr2;
  295.     }
  296.     if (fstat(dirp->dd_fd, &stb) < 0)
  297.         goto patherr1;
  298.     if (!ISDIR(stb.st_mode)) {
  299.         errno = ENOTDIR;
  300.         goto patherr1;
  301.     }
  302.     while ((dp = readdir(dirp)) != NULL)
  303.         if (match(dp->d_name, pattern)) {
  304.             if (which & E_TILDE)
  305.                 Cat(path, dp->d_name);
  306.             else {
  307.                 strcpy(pathp, dp->d_name);
  308.                 Cat(tilde, tpathp);
  309.                 *pathp = '\0';
  310.             }
  311.         }
  312.     closedir(dirp);
  313.     return;
  314.  
  315. patherr1:
  316.     closedir(dirp);
  317. patherr2:
  318.     strcat(path, ": ");
  319.     strcat(path, strerror(errno));
  320.     yyerror(path);
  321. }
  322.  
  323. execbrc(p, s)
  324.     char *p, *s;
  325. {
  326.     char restbuf[BUFSIZ + 2];
  327.     register char *pe, *pm, *pl;
  328.     int brclev = 0;
  329.     char *lm, savec, *spathp;
  330.  
  331.     for (lm = restbuf; *p != '{'; *lm++ = *p++)
  332.         continue;
  333.     for (pe = ++p; *pe; pe++)
  334.         switch (*pe) {
  335.  
  336.         case '{':
  337.             brclev++;
  338.             continue;
  339.  
  340.         case '}':
  341.             if (brclev == 0)
  342.                 goto pend;
  343.             brclev--;
  344.             continue;
  345.  
  346.         case '[':
  347.             for (pe++; *pe && *pe != ']'; pe++)
  348.                 continue;
  349.             if (!*pe)
  350.                 yyerror("Missing ']'");
  351.             continue;
  352.         }
  353. pend:
  354.     if (brclev || !*pe) {
  355.         yyerror("Missing '}'");
  356.         return (0);
  357.     }
  358.     for (pl = pm = p; pm <= pe; pm++)
  359.         switch (*pm & (QUOTE|TRIM)) {
  360.  
  361.         case '{':
  362.             brclev++;
  363.             continue;
  364.  
  365.         case '}':
  366.             if (brclev) {
  367.                 brclev--;
  368.                 continue;
  369.             }
  370.             goto doit;
  371.  
  372.         case ',':
  373.             if (brclev)
  374.                 continue;
  375. doit:
  376.             savec = *pm;
  377.             *pm = 0;
  378.             strcpy(lm, pl);
  379.             strcat(restbuf, pe + 1);
  380.             *pm = savec;
  381.             if (s == 0) {
  382.                 spathp = pathp;
  383.                 expsh(restbuf);
  384.                 pathp = spathp;
  385.                 *pathp = 0;
  386.             } else if (amatch(s, restbuf))
  387.                 return (1);
  388.             sort();
  389.             pl = pm + 1;
  390.             continue;
  391.  
  392.         case '[':
  393.             for (pm++; *pm && *pm != ']'; pm++)
  394.                 continue;
  395.             if (!*pm)
  396.                 yyerror("Missing ']'");
  397.             continue;
  398.         }
  399.     return (0);
  400. }
  401.  
  402. match(s, p)
  403.     char *s, *p;
  404. {
  405.     register int c;
  406.     register char *sentp;
  407.     char sexpany = expany;
  408.  
  409.     if (*s == '.' && *p != '.')
  410.         return (0);
  411.     sentp = entp;
  412.     entp = s;
  413.     c = amatch(s, p);
  414.     entp = sentp;
  415.     expany = sexpany;
  416.     return (c);
  417. }
  418.  
  419. amatch(s, p)
  420.     register char *s, *p;
  421. {
  422.     register int scc;
  423.     int ok, lc;
  424.     char *spathp;
  425.     struct stat stb;
  426.     int c, cc;
  427.  
  428.     expany = 1;
  429.     for (;;) {
  430.         scc = *s++ & TRIM;
  431.         switch (c = *p++) {
  432.  
  433.         case '{':
  434.             return (execbrc(p - 1, s - 1));
  435.  
  436.         case '[':
  437.             ok = 0;
  438.             lc = 077777;
  439.             while (cc = *p++) {
  440.                 if (cc == ']') {
  441.                     if (ok)
  442.                         break;
  443.                     return (0);
  444.                 }
  445.                 if (cc == '-') {
  446.                     if (lc <= scc && scc <= *p++)
  447.                         ok++;
  448.                 } else
  449.                     if (scc == (lc = cc))
  450.                         ok++;
  451.             }
  452.             if (cc == 0) {
  453.                 yyerror("Missing ']'");
  454.                 return (0);
  455.             }
  456.             continue;
  457.  
  458.         case '*':
  459.             if (!*p)
  460.                 return (1);
  461.             if (*p == '/') {
  462.                 p++;
  463.                 goto slash;
  464.             }
  465.             for (s--; *s; s++)
  466.                 if (amatch(s, p))
  467.                     return (1);
  468.             return (0);
  469.  
  470.         case '\0':
  471.             return (scc == '\0');
  472.  
  473.         default:
  474.             if ((c & TRIM) != scc)
  475.                 return (0);
  476.             continue;
  477.  
  478.         case '?':
  479.             if (scc == '\0')
  480.                 return (0);
  481.             continue;
  482.  
  483.         case '/':
  484.             if (scc)
  485.                 return (0);
  486. slash:
  487.             s = entp;
  488.             spathp = pathp;
  489.             while (*s)
  490.                 addpath(*s++);
  491.             addpath('/');
  492.             if (stat(path, &stb) == 0 && ISDIR(stb.st_mode))
  493.                 if (*p == '\0') {
  494.                     if (which & E_TILDE)
  495.                         Cat(path, "");
  496.                     else
  497.                         Cat(tilde, tpathp);
  498.                 } else
  499.                     expsh(p);
  500.             pathp = spathp;
  501.             *pathp = '\0';
  502.             return (0);
  503.         }
  504.     }
  505. }
  506.  
  507. smatch(s, p)
  508.     register char *s, *p;
  509. {
  510.     register int scc;
  511.     int ok, lc;
  512.     int c, cc;
  513.  
  514.     for (;;) {
  515.         scc = *s++ & TRIM;
  516.         switch (c = *p++) {
  517.  
  518.         case '[':
  519.             ok = 0;
  520.             lc = 077777;
  521.             while (cc = *p++) {
  522.                 if (cc == ']') {
  523.                     if (ok)
  524.                         break;
  525.                     return (0);
  526.                 }
  527.                 if (cc == '-') {
  528.                     if (lc <= scc && scc <= *p++)
  529.                         ok++;
  530.                 } else
  531.                     if (scc == (lc = cc))
  532.                         ok++;
  533.             }
  534.             if (cc == 0) {
  535.                 yyerror("Missing ']'");
  536.                 return (0);
  537.             }
  538.             continue;
  539.  
  540.         case '*':
  541.             if (!*p)
  542.                 return (1);
  543.             for (s--; *s; s++)
  544.                 if (smatch(s, p))
  545.                     return (1);
  546.             return (0);
  547.  
  548.         case '\0':
  549.             return (scc == '\0');
  550.  
  551.         default:
  552.             if ((c & TRIM) != scc)
  553.                 return (0);
  554.             continue;
  555.  
  556.         case '?':
  557.             if (scc == 0)
  558.                 return (0);
  559.             continue;
  560.  
  561.         }
  562.     }
  563. }
  564.  
  565. Cat(s1, s2)
  566.     register char *s1, *s2;
  567. {
  568.     int len = strlen(s1) + strlen(s2) + 1;
  569.     register char *s;
  570.  
  571.     nleft -= len;
  572.     if (nleft <= 0 || ++eargc >= GAVSIZ)
  573.         yyerror("Arguments too long");
  574.     eargv[eargc] = 0;
  575.     eargv[eargc - 1] = s = malloc(len);
  576.     if (s == NULL)
  577.         fatal("ran out of memory\n");
  578.     while (*s++ = *s1++ & TRIM)
  579.         ;
  580.     s--;
  581.     while (*s++ = *s2++ & TRIM)
  582.         ;
  583. }
  584.  
  585. addpath(c)
  586.     char c;
  587. {
  588.  
  589.     if (pathp >= lastpathp)
  590.         yyerror("Pathname too long");
  591.     else {
  592.         *pathp++ = c & TRIM;
  593.         *pathp = '\0';
  594.     }
  595. }
  596.  
  597. /*
  598.  * Expand file names beginning with `~' into the
  599.  * user's home directory path name. Return a pointer in buf to the
  600.  * part corresponding to `file'.
  601.  */
  602. char *
  603. exptilde(buf, file)
  604.     char buf[];
  605.     register char *file;
  606. {
  607.     register char *s1, *s2, *s3;
  608.     extern char homedir[];
  609.  
  610.     if (*file != '~') {
  611.         strcpy(buf, file);
  612.         return(buf);
  613.     }
  614.     if (*++file == '\0') {
  615.         s2 = homedir;
  616.         s3 = NULL;
  617.     } else if (*file == '/') {
  618.         s2 = homedir;
  619.         s3 = file;
  620.     } else {
  621.         s3 = file;
  622.         while (*s3 && *s3 != '/')
  623.             s3++;
  624.         if (*s3 == '/')
  625.             *s3 = '\0';
  626.         else
  627.             s3 = NULL;
  628.         if (pw == NULL || strcmp(pw->pw_name, file) != 0) {
  629.             if ((pw = getpwnam(file)) == NULL) {
  630.                 error("%s: unknown user name\n", file);
  631.                 if (s3 != NULL)
  632.                     *s3 = '/';
  633.                 return(NULL);
  634.             }
  635.         }
  636.         if (s3 != NULL)
  637.             *s3 = '/';
  638.         s2 = pw->pw_dir;
  639.     }
  640.     for (s1 = buf; *s1++ = *s2++; )
  641.         ;
  642.     s2 = --s1;
  643.     if (s3 != NULL) {
  644.         s2++;
  645.         while (*s1++ = *s3++)
  646.             ;
  647.     }
  648.     return(s2);
  649. }
  650.