home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 0 / 0988 / tclGlob.c < prev    next >
C/C++ Source or Header  |  1990-12-28  |  12KB  |  436 lines

  1. /* TclGlob.c -
  2.  *
  3.  *         This file contains routines to glob filenames.  It calls
  4.  *    glob routines from GNU.
  5.  *
  6.  */
  7.  
  8. #ifndef lint
  9. static char rcsid[] = "$Header: /sprite/src/lib/tcl/RCS/tclGlob.c,v 1.2 89/06/12 17:00:39 shirriff Exp $ SPRITE (Berkeley)";
  10. #endif /* not lint */
  11.  
  12. #include <string.h>
  13. #include <list.h>
  14. #include <pwd.h>
  15. #include <tcl.h>
  16.  
  17. /*
  18.  * Library imports.
  19.  */
  20.  
  21. extern int errno;
  22. extern char *sys_errlist[];
  23. extern char *ckalloc(), *sprintf(), *getlogin();
  24. extern char **glob_filename();
  25.  
  26. /*
  27.  * Structure to hold a list of strings.
  28.  */
  29. typedef struct {
  30.     List_Links links;
  31.     char *str;
  32. } stringList;
  33.  
  34. /*
  35.  * Free the linked list.  This macro requires stringElt to be defined.
  36.  */
  37. #define FREE(list) \
  38.     while (!List_IsEmpty((list))) { \
  39.         stringElt=(stringList *)List_First((list)); \
  40.         List_Remove((List_Links *)stringElt); \
  41.         if (stringElt->str != NULL) { \
  42.         ckfree(stringElt->str); \
  43.         } \
  44.         ckfree((char *)stringElt); \
  45.     } \
  46.     ckfree((char *)(list))
  47.  
  48. /*
  49.  * ----------------------------------------------------------------------------
  50.  *
  51.  * BraceExpand --
  52.  *
  53.  *      Expands a filename containing {} braces.
  54.  *    If inBrace==0, this routine matches expression E, where
  55.  *        E = string
  56.  *        or    E = [E]{E,...,E}[E]
  57.  *    otherwise this routine matches a similar expression E1, where
  58.  *        E1 = string (stopping at comma or closing brace)
  59.  *        or    E1 = [E1]{E,...,E}[E1]
  60.  *    That is, commas and closing braces will stop the parsing. 
  61.  *    
  62.  * Results:
  63.  *    
  64.  *    The position of the next character in the input is returned.
  65.  *    This position will point to '\0', or possibly ',' if inBrace is true.
  66.  *      The resulting list of strings is returned in strList.
  67.  *    In case of an error, NULL will be returned and the error
  68.  *    message will be returned in the list of strings.
  69.  *
  70.  * Side effects:
  71.  *      Allocates memory for the list of strings.
  72.  *
  73.  * ----------------------------------------------------------------------------
  74.  */
  75. static char*
  76. BraceExpand(str,inBrace,strList)
  77.     char *str;            /* String to expand. */
  78.     int inBrace;        /* True if inside a brace. */
  79.     List_Links **strList;    /* List of strings. */
  80. {
  81.     List_Links *headerPtr;    /* Header of string list. */
  82.     stringList *stringElt;    /* Element of string list. */
  83.     List_Links *leftHdr;    /* Left part of expanded name. */
  84.     List_Links *rightHdr;    /* Right part of expanded name. */
  85.     char *strPtr;        /* Pointer into the string. */
  86.     char *next;            /* Next position in string. */
  87.     int len;            /* String length. */
  88.     List_Links *leftPtr;    /* Pointer to left string element. */
  89.     char *left;            /* Left string. */
  90.     int leftLen;        /* Length of left string. */
  91.     List_Links *rightPtr;    /* Pointer to right string element. */
  92.     char *right;        /* Right string. */
  93.  
  94.     headerPtr = (List_Links *)ckalloc(sizeof(List_Links));
  95.     List_Init(headerPtr);
  96.     
  97.     if (inBrace) {
  98.     strPtr = strpbrk(str,"{,}");
  99.     } else {
  100.     strPtr = strchr(str,'{');
  101.     }
  102.     if (strPtr==NULL || *strPtr=='}' || *strPtr==',') {
  103.     /*
  104.      * Return a single element.
  105.      */
  106.     if (strPtr==NULL) {
  107.         len = strlen(str);
  108.         strPtr = str+len;
  109.     }
  110.     else {
  111.         len = strPtr-str;
  112.     }
  113.     stringElt = (stringList *)ckalloc(sizeof(stringList));
  114.     List_InitElement((List_Links *)stringElt);
  115.     stringElt->str = (char *)ckalloc((unsigned)len+1);
  116.     (void) strncpy(stringElt->str,str,len);
  117.     stringElt->str[len] = '\0';
  118.     List_Insert((List_Links *)stringElt,
  119.         LIST_ATFRONT(headerPtr));
  120.     *strList = headerPtr;
  121.     return strPtr;
  122.     }
  123.  
  124.     len = strPtr-str;
  125.     leftHdr = (List_Links *)ckalloc(sizeof(List_Links));
  126.     List_Init(leftHdr);
  127.  
  128.     /*
  129.      * The idea is to grab a unit (string or thing in braces) from
  130.      * the left, and put this in leftHdr.  Then recursively expand
  131.      * the remainder, and put this in rightHdr.  Finally merge the
  132.      * two lists
  133.      */
  134.  
  135.     if (*str=='{') {
  136.     next = strPtr;
  137.     while (1) {
  138.         /*
  139.          * Expand the part in the braces.
  140.          */
  141.         next = BraceExpand(++next,1,&rightHdr);
  142.         if (next==NULL) {
  143.         /*
  144.          * Error in BraceExpand.
  145.          */
  146.         *strList = rightHdr;
  147.         return NULL;
  148.         } else if (*next=='\0') {
  149.         /*
  150.          * Unexpected end of string.
  151.          */
  152.         FREE(leftHdr);
  153.         FREE(rightHdr);
  154.         stringElt = (stringList *)ckalloc(sizeof(stringList));
  155.         List_InitElement((List_Links *)stringElt);
  156.         stringElt->str = "Missing }.";
  157.         List_Insert((List_Links *)stringElt,
  158.             LIST_ATREAR(headerPtr));
  159.         *strList = headerPtr;
  160.         return NULL;
  161.         }
  162.         else {
  163.         /*
  164.          * Add the new list obtained from BraceExpand to the list.
  165.          */
  166.         List_ListInsert(rightHdr, LIST_ATREAR(leftHdr));
  167.         ckfree((char *)rightHdr);
  168.         if (*next=='}') {
  169.             strPtr = next+1;
  170.             break;
  171.         }
  172.         }
  173.     }
  174.     } else {
  175.     /*
  176.      * leftHdr is the part before the braces.
  177.      */
  178.     stringElt = (stringList *)ckalloc(sizeof(stringList));
  179.     List_InitElement((List_Links *)stringElt);
  180.     stringElt->str = (char *)ckalloc((unsigned)len+1);
  181.     (void) strncpy(stringElt->str,str,len);
  182.     stringElt->str[len] = '\0';
  183.     List_Insert((List_Links *)stringElt,
  184.         LIST_ATFRONT(leftHdr));
  185.     }
  186.  
  187.     /*
  188.      * Now expand the rest of the pattern and put this in rightHdr.
  189.      */
  190.  
  191.     if (*strPtr=='\0' || (*strPtr==',' && inBrace)) {
  192.     /*
  193.      * We can leave early.
  194.      */
  195.     *strList = leftHdr;
  196.     ckfree((char *)headerPtr);
  197.     return strPtr;
  198.     }
  199.     next = BraceExpand(strPtr,inBrace,&rightHdr);
  200.     if (next==NULL) {
  201.     ckfree((char *)leftHdr);
  202.     ckfree((char *)headerPtr);
  203.     *strList = rightHdr;
  204.     return NULL;
  205.     }
  206.  
  207.     /*
  208.      * Merge the left and right lists of strings.
  209.      */
  210.  
  211.     LIST_FORALL(leftHdr,leftPtr) {
  212.     left = ((stringList *)leftPtr)->str;
  213.     leftLen = strlen(left);
  214.     LIST_FORALL(rightHdr,rightPtr) {
  215.         right = ((stringList *)rightPtr)->str;
  216.         stringElt = (stringList *)ckalloc(sizeof(stringList));
  217.         List_InitElement((List_Links *)stringElt);
  218.         stringElt->str = (char *)ckalloc((unsigned)strlen(left)+
  219.             strlen(right)+1);
  220.         (void)strcpy(stringElt->str,left);
  221.         (void)strcpy(stringElt->str+leftLen,right);
  222.         List_Insert((List_Links *)stringElt,
  223.             LIST_ATREAR(headerPtr));
  224.     }
  225.     }
  226.     FREE(leftHdr);
  227.     FREE(rightHdr);
  228.     *strList = headerPtr;
  229.     return next;
  230. }
  231.  
  232.  
  233.  
  234. #define VALID "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\
  235. 0123456789-"
  236.  
  237. /*
  238.  * ----------------------------------------------------------------------------
  239.  *
  240.  * Tilde --
  241.  *
  242.  *      Expands a string starting with a tilde.
  243.  *    It is assumed that the pattern starts with a tilde.
  244.  *    It is also assumed that we may temporarily modify pattern.
  245.  *    
  246.  * Results:
  247.  *    
  248.  *    If successful, TCL_OK is returned, and result points to the string
  249.  *    containing the expanded filenames.
  250.  *    Otherwise, TCL_ERROR is returned, and interp contains the error.
  251.  *
  252.  * Side effects:
  253.  *      Allocates the result string if successful.
  254.  *
  255.  * ----------------------------------------------------------------------------
  256.  */
  257. static int
  258. Tilde(pattern,interp,result)
  259.     char *pattern;        /* Pattern to expand. */
  260.     Tcl_Interp *interp;         /* Current interpreter. */
  261.     char **result;        /* Result of expansion. */
  262. {
  263.     int len;            /* Length of tilde name. */
  264.     struct passwd *pwPtr;    /* Password file entry. */
  265.     char *strPtr;        /* String pointer. */
  266.     int ret;
  267.  
  268.     pattern++;
  269.  
  270.     len = strspn(pattern,VALID);
  271.     if (len==0) {
  272.     /*
  273.      * Get home directory.
  274.      */
  275.      strPtr = (char *)getlogin();
  276.      if (strPtr==NULL) {
  277.          interp->result = "no home directory";
  278.          return TCL_ERROR;
  279.      }
  280.     }
  281.     else {
  282.      strPtr = (char *)ckalloc((unsigned)len+1);
  283.      strncpy(strPtr,pattern,len);
  284.      strPtr[len] = '\0';
  285.     }
  286.     pwPtr = getpwnam(strPtr);
  287.     if (pwPtr==NULL) {
  288.      sprintf(interp->result,"Unknown user: %s.",strPtr);
  289.      ret = TCL_ERROR;
  290.     }
  291.     else {
  292.     *result = (char *)ckalloc((unsigned)strlen(pattern+len)+
  293.         strlen(pwPtr->pw_dir)+1);
  294.     (void) strcpy(*result,pwPtr->pw_dir);
  295.     (void) strcpy(*result+strlen(pwPtr->pw_dir),pattern+len);
  296.     ret = TCL_OK;
  297.     }
  298.     if (len>0) {
  299.     ckfree(strPtr);
  300.     }
  301.     return ret;
  302. }
  303.  
  304. /*
  305.  * ----------------------------------------------------------------------------
  306.  *
  307.  * Tcl_Glob --
  308.  *
  309.  *      Expands a pattern in a directory using csh rules.
  310.  *    
  311.  * Results:
  312.  *    A standard Tcl result.
  313.  *
  314.  * Side effects:
  315.  *      See the user documentation.
  316.  *
  317.  * ----------------------------------------------------------------------------
  318.  */
  319.  
  320. int
  321. Tcl_Glob(interp, argc, argv)
  322.     Tcl_Interp *interp;                 /* Current interpreter. */
  323.     int argc;
  324.     char *argv[];
  325. {
  326.     List_Links *stringHdr;    /* Element of string list. */
  327.     stringList *stringElt;    /* Element of string list. */
  328.     List_Links *resultList;    /* Results of expansion. */
  329.     List_Links *linkPtr;    /* Pointer to linked list element. */
  330.     int length = 0;        /* Length of result. */
  331.     char *strPtr;        /* String pointer. */
  332.     char *str2Ptr;        /* String pointer. */
  333.     char **fileList;        /* List of globbed filenames */
  334.     char **fileList1;        /* List of globbed filenames */
  335.     int i;
  336.  
  337.     resultList = (List_Links *) ckalloc(sizeof(List_Links));
  338.     List_Init(resultList);
  339.  
  340.     for (i=1;i<argc;i++) {
  341.     /*
  342.      * Expand the braces in each argument and add to resultList.
  343.      */
  344.     if (!strcmp(argv[i],"{") || !strcmp(argv[i],"{}")) {
  345.         /*
  346.          * Patterns "{" and "{}" are special cases.
  347.          */
  348.         stringElt = (stringList *)ckalloc(sizeof(stringList));
  349.         List_InitElement((List_Links *)stringElt);
  350.         stringElt->str = (char *)ckalloc((unsigned)strlen(argv[i])+1);
  351.         (void)strcpy(stringElt->str,argv[i]);
  352.         List_Insert((List_Links *)stringElt,
  353.             LIST_ATREAR(resultList));
  354.     } else if (BraceExpand(argv[i],0,&stringHdr)==NULL) {
  355.         strcpy(interp->result,
  356.             ((stringList *)List_First(stringHdr))->str);
  357.         FREE(resultList);
  358.         FREE(stringHdr);
  359.         return TCL_ERROR;
  360.     } else {
  361.         List_ListInsert(stringHdr,LIST_ATREAR(resultList));
  362.         ckfree((char *)stringHdr);
  363.     }
  364.     }
  365.  
  366.     stringHdr = (List_Links *)ckalloc(sizeof(List_Links));
  367.     List_Init(stringHdr);
  368.  
  369.     LIST_FORALL(resultList,linkPtr) {
  370.     strPtr = ((stringList *)linkPtr)->str;
  371.     if (*strPtr == '~') {
  372.         /*
  373.          * Expand tildes.
  374.          */
  375.         if (Tilde(strPtr,interp,&str2Ptr) != TCL_OK) {
  376.         FREE(resultList);
  377.         FREE(stringHdr);
  378.         return TCL_ERROR;
  379.         } else {
  380.         ckfree(strPtr);
  381.         ((stringList *)linkPtr)->str = str2Ptr;
  382.         strPtr = str2Ptr;
  383.         }
  384.     }
  385.     if (glob_pattern_p(strPtr)) {
  386.     fileList = (char **)glob_filename(strPtr);
  387.         if ((int)fileList==-1) {
  388.         strcpy(interp->result,sys_errlist[errno]);
  389.         FREE(resultList);
  390.         FREE(stringHdr);
  391.         return TCL_ERROR;
  392.         } else {
  393.         for (fileList1=fileList; *fileList1!='\0'; fileList1++) {
  394.             length += strlen(*fileList1)+1;
  395.             stringElt = (stringList *)ckalloc(sizeof(stringList));
  396.             List_InitElement((List_Links *)stringElt);
  397.             stringElt->str =
  398.                 (char *)ckalloc((unsigned)strlen(*fileList1)+1);
  399.             strcpy(stringElt->str,*fileList1);
  400.             List_Insert((List_Links *)stringElt,
  401.                 LIST_ATREAR(stringHdr));
  402.             ckfree(*fileList1);
  403.         }
  404.         ckfree((char *)fileList);
  405.         }
  406.     } else {
  407.         length += strlen(strPtr)+1;
  408.         stringElt = (stringList *)ckalloc(sizeof(stringList));
  409.         List_InitElement((List_Links *)stringElt);
  410.         stringElt->str = strPtr;
  411.         ((stringList *)linkPtr)->str = NULL;
  412.         List_Insert((List_Links *)stringElt,LIST_ATREAR(stringHdr));
  413.     }
  414.     }
  415.     FREE(resultList);
  416.  
  417.     if (List_IsEmpty(stringHdr)) {
  418.     FREE(stringHdr);
  419.     sprintf(interp->result,"%.50s couldn't find file that matches pattern",
  420.         argv[0]);
  421.     return TCL_ERROR;
  422.     }
  423.  
  424.     strPtr = (char *)ckalloc((unsigned) length);
  425.     interp->result = strPtr;
  426.     interp->dynamic = 1;
  427.     LIST_FORALL(stringHdr,linkPtr) {
  428.     strcpy(strPtr,((stringList *)linkPtr)->str);
  429.     strPtr += strlen(strPtr)+1;
  430.     strPtr[-1] = ' ';
  431.     }
  432.     strPtr[-1] = '\0';
  433.     FREE(stringHdr);
  434.     return TCL_OK;
  435. }
  436.