root/tclGlob.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. stringList
  2. BraceExpand
  3. Tilde
  4. Tcl_Glob

/* TclGlob.c -
 *
 *      This file contains routines to glob filenames.  It calls
 *      glob routines from GNU.
 *
 */

#ifndef lint
static char rcsid[] = "$Header: /sprite/src/lib/tcl/RCS/tclGlob.c,v 1.2 89/06/12 17:00:39 shirriff Exp $ SPRITE (Berkeley)";
#endif /* not lint */

#include <string.h>
#include <list.h>
#include <pwd.h>
#include <tcl.h>

/*
 * Library imports.
 */

extern int errno;
extern char *sys_errlist[];
extern char *ckalloc(), *sprintf(), *getlogin();
extern char **glob_filename();

/*
 * Structure to hold a list of strings.
 */
typedef struct {
    List_Links links;
    char *str;
} stringList;

/*
 * Free the linked list.  This macro requires stringElt to be defined.
 */
#define FREE(list) \
        while (!List_IsEmpty((list))) { \
            stringElt=(stringList *)List_First((list)); \
            List_Remove((List_Links *)stringElt); \
            if (stringElt->str != NULL) { \
                ckfree(stringElt->str); \
            } \
            ckfree((char *)stringElt); \
        } \
        ckfree((char *)(list))
 
/*
 * ----------------------------------------------------------------------------
 *
 * BraceExpand --
 *
 *      Expands a filename containing {} braces.
 *      If inBrace==0, this routine matches expression E, where
 *              E = string
 *          or  E = [E]{E,...,E}[E]
 *      otherwise this routine matches a similar expression E1, where
 *              E1 = string (stopping at comma or closing brace)
 *          or  E1 = [E1]{E,...,E}[E1]
 *      That is, commas and closing braces will stop the parsing. 
 *      
 * Results:
 *      
 *      The position of the next character in the input is returned.
 *      This position will point to '\0', or possibly ',' if inBrace is true.
 *      The resulting list of strings is returned in strList.
 *      In case of an error, NULL will be returned and the error
 *      message will be returned in the list of strings.
 *
 * Side effects:
 *      Allocates memory for the list of strings.
 *
 * ----------------------------------------------------------------------------
 */
static char*
BraceExpand(str,inBrace,strList)
    char *str;                  /* String to expand. */
    int inBrace;                /* True if inside a brace. */
    List_Links **strList;       /* List of strings. */
{
    List_Links *headerPtr;      /* Header of string list. */
    stringList *stringElt;      /* Element of string list. */
    List_Links *leftHdr;        /* Left part of expanded name. */
    List_Links *rightHdr;       /* Right part of expanded name. */
    char *strPtr;               /* Pointer into the string. */
    char *next;                 /* Next position in string. */
    int len;                    /* String length. */
    List_Links *leftPtr;        /* Pointer to left string element. */
    char *left;                 /* Left string. */
    int leftLen;                /* Length of left string. */
    List_Links *rightPtr;       /* Pointer to right string element. */
    char *right;                /* Right string. */

    headerPtr = (List_Links *)ckalloc(sizeof(List_Links));
    List_Init(headerPtr);
        
    if (inBrace) {
        strPtr = strpbrk(str,"{,}");
    } else {
        strPtr = strchr(str,'{');
    }
    if (strPtr==NULL || *strPtr=='}' || *strPtr==',') {
        /*
         * Return a single element.
         */
        if (strPtr==NULL) {
            len = strlen(str);
            strPtr = str+len;
        }
        else {
            len = strPtr-str;
        }
        stringElt = (stringList *)ckalloc(sizeof(stringList));
        List_InitElement((List_Links *)stringElt);
        stringElt->str = (char *)ckalloc((unsigned)len+1);
        (void) strncpy(stringElt->str,str,len);
        stringElt->str[len] = '\0';
        List_Insert((List_Links *)stringElt,
                LIST_ATFRONT(headerPtr));
        *strList = headerPtr;
        return strPtr;
    }

    len = strPtr-str;
    leftHdr = (List_Links *)ckalloc(sizeof(List_Links));
    List_Init(leftHdr);

    /*
     * The idea is to grab a unit (string or thing in braces) from
     * the left, and put this in leftHdr.  Then recursively expand
     * the remainder, and put this in rightHdr.  Finally merge the
     * two lists
     */

    if (*str=='{') {
        next = strPtr;
        while (1) {
            /*
             * Expand the part in the braces.
             */
            next = BraceExpand(++next,1,&rightHdr);
            if (next==NULL) {
                /*
                 * Error in BraceExpand.
                 */
                *strList = rightHdr;
                return NULL;
            } else if (*next=='\0') {
                /*
                 * Unexpected end of string.
                 */
                FREE(leftHdr);
                FREE(rightHdr);
                stringElt = (stringList *)ckalloc(sizeof(stringList));
                List_InitElement((List_Links *)stringElt);
                stringElt->str = "Missing }.";
                List_Insert((List_Links *)stringElt,
                        LIST_ATREAR(headerPtr));
                *strList = headerPtr;
                return NULL;
            }
            else {
                /*
                 * Add the new list obtained from BraceExpand to the list.
                 */
                List_ListInsert(rightHdr, LIST_ATREAR(leftHdr));
                ckfree((char *)rightHdr);
                if (*next=='}') {
                    strPtr = next+1;
                    break;
                }
            }
        }
    } else {
        /*
         * leftHdr is the part before the braces.
         */
        stringElt = (stringList *)ckalloc(sizeof(stringList));
        List_InitElement((List_Links *)stringElt);
        stringElt->str = (char *)ckalloc((unsigned)len+1);
        (void) strncpy(stringElt->str,str,len);
        stringElt->str[len] = '\0';
        List_Insert((List_Links *)stringElt,
                LIST_ATFRONT(leftHdr));
    }

    /*
     * Now expand the rest of the pattern and put this in rightHdr.
     */

    if (*strPtr=='\0' || (*strPtr==',' && inBrace)) {
        /*
         * We can leave early.
         */
        *strList = leftHdr;
        ckfree((char *)headerPtr);
        return strPtr;
    }
    next = BraceExpand(strPtr,inBrace,&rightHdr);
    if (next==NULL) {
        ckfree((char *)leftHdr);
        ckfree((char *)headerPtr);
        *strList = rightHdr;
        return NULL;
    }

    /*
     * Merge the left and right lists of strings.
     */

    LIST_FORALL(leftHdr,leftPtr) {
        left = ((stringList *)leftPtr)->str;
        leftLen = strlen(left);
        LIST_FORALL(rightHdr,rightPtr) {
            right = ((stringList *)rightPtr)->str;
            stringElt = (stringList *)ckalloc(sizeof(stringList));
            List_InitElement((List_Links *)stringElt);
            stringElt->str = (char *)ckalloc((unsigned)strlen(left)+
                    strlen(right)+1);
            (void)strcpy(stringElt->str,left);
            (void)strcpy(stringElt->str+leftLen,right);
            List_Insert((List_Links *)stringElt,
                    LIST_ATREAR(headerPtr));
        }
    }
    FREE(leftHdr);
    FREE(rightHdr);
    *strList = headerPtr;
    return next;
}

 

#define VALID "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\
0123456789-"

/*
 * ----------------------------------------------------------------------------
 *
 * Tilde --
 *
 *      Expands a string starting with a tilde.
 *      It is assumed that the pattern starts with a tilde.
 *      It is also assumed that we may temporarily modify pattern.
 *      
 * Results:
 *      
 *      If successful, TCL_OK is returned, and result points to the string
 *      containing the expanded filenames.
 *      Otherwise, TCL_ERROR is returned, and interp contains the error.
 *
 * Side effects:
 *      Allocates the result string if successful.
 *
 * ----------------------------------------------------------------------------
 */
static int
Tilde(pattern,interp,result)
    char *pattern;              /* Pattern to expand. */
    Tcl_Interp *interp;         /* Current interpreter. */
    char **result;              /* Result of expansion. */
{
    int len;                    /* Length of tilde name. */
    struct passwd *pwPtr;       /* Password file entry. */
    char *strPtr;               /* String pointer. */
    int ret;

    pattern++;

    len = strspn(pattern,VALID);
    if (len==0) {
        /*
         * Get home directory.
         */
         strPtr = (char *)getlogin();
         if (strPtr==NULL) {
             interp->result = "no home directory";
             return TCL_ERROR;
         }
    }
    else {
         strPtr = (char *)ckalloc((unsigned)len+1);
         strncpy(strPtr,pattern,len);
         strPtr[len] = '\0';
    }
    pwPtr = getpwnam(strPtr);
    if (pwPtr==NULL) {
         sprintf(interp->result,"Unknown user: %s.",strPtr);
         ret = TCL_ERROR;
    }
    else {
        *result = (char *)ckalloc((unsigned)strlen(pattern+len)+
                strlen(pwPtr->pw_dir)+1);
        (void) strcpy(*result,pwPtr->pw_dir);
        (void) strcpy(*result+strlen(pwPtr->pw_dir),pattern+len);
        ret = TCL_OK;
    }
    if (len>0) {
        ckfree(strPtr);
    }
    return ret;
}
 
/*
 * ----------------------------------------------------------------------------
 *
 * Tcl_Glob --
 *
 *      Expands a pattern in a directory using csh rules.
 *      
 * Results:
 *      A standard Tcl result.
 *
 * Side effects:
 *      See the user documentation.
 *
 * ----------------------------------------------------------------------------
 */

int
Tcl_Glob(interp, argc, argv)
    Tcl_Interp *interp;                 /* Current interpreter. */
    int argc;
    char *argv[];
{
    List_Links *stringHdr;      /* Element of string list. */
    stringList *stringElt;      /* Element of string list. */
    List_Links *resultList;     /* Results of expansion. */
    List_Links *linkPtr;        /* Pointer to linked list element. */
    int length = 0;             /* Length of result. */
    char *strPtr;               /* String pointer. */
    char *str2Ptr;              /* String pointer. */
    char **fileList;            /* List of globbed filenames */
    char **fileList1;           /* List of globbed filenames */
    int i;

    resultList = (List_Links *) ckalloc(sizeof(List_Links));
    List_Init(resultList);

    for (i=1;i<argc;i++) {
        /*
         * Expand the braces in each argument and add to resultList.
         */
        if (!strcmp(argv[i],"{") || !strcmp(argv[i],"{}")) {
            /*
             * Patterns "{" and "{}" are special cases.
             */
            stringElt = (stringList *)ckalloc(sizeof(stringList));
            List_InitElement((List_Links *)stringElt);
            stringElt->str = (char *)ckalloc((unsigned)strlen(argv[i])+1);
            (void)strcpy(stringElt->str,argv[i]);
            List_Insert((List_Links *)stringElt,
                    LIST_ATREAR(resultList));
        } else if (BraceExpand(argv[i],0,&stringHdr)==NULL) {
            strcpy(interp->result,
                    ((stringList *)List_First(stringHdr))->str);
            FREE(resultList);
            FREE(stringHdr);
            return TCL_ERROR;
        } else {
            List_ListInsert(stringHdr,LIST_ATREAR(resultList));
            ckfree((char *)stringHdr);
        }
    }

    stringHdr = (List_Links *)ckalloc(sizeof(List_Links));
    List_Init(stringHdr);

    LIST_FORALL(resultList,linkPtr) {
        strPtr = ((stringList *)linkPtr)->str;
        if (*strPtr == '~') {
            /*
             * Expand tildes.
             */
            if (Tilde(strPtr,interp,&str2Ptr) != TCL_OK) {
                FREE(resultList);
                FREE(stringHdr);
                return TCL_ERROR;
            } else {
                ckfree(strPtr);
                ((stringList *)linkPtr)->str = str2Ptr;
                strPtr = str2Ptr;
            }
        }
        if (glob_pattern_p(strPtr)) {
        fileList = (char **)glob_filename(strPtr);
            if ((int)fileList==-1) {
                strcpy(interp->result,sys_errlist[errno]);
                FREE(resultList);
                FREE(stringHdr);
                return TCL_ERROR;
            } else {
                for (fileList1=fileList; *fileList1!='\0'; fileList1++) {
                    length += strlen(*fileList1)+1;
                    stringElt = (stringList *)ckalloc(sizeof(stringList));
                    List_InitElement((List_Links *)stringElt);
                    stringElt->str =
                            (char *)ckalloc((unsigned)strlen(*fileList1)+1);
                    strcpy(stringElt->str,*fileList1);
                    List_Insert((List_Links *)stringElt,
                            LIST_ATREAR(stringHdr));
                    ckfree(*fileList1);
                }
                ckfree((char *)fileList);
            }
        } else {
            length += strlen(strPtr)+1;
            stringElt = (stringList *)ckalloc(sizeof(stringList));
            List_InitElement((List_Links *)stringElt);
            stringElt->str = strPtr;
            ((stringList *)linkPtr)->str = NULL;
            List_Insert((List_Links *)stringElt,LIST_ATREAR(stringHdr));
        }
    }
    FREE(resultList);

    if (List_IsEmpty(stringHdr)) {
        FREE(stringHdr);
        sprintf(interp->result,"%.50s couldn't find file that matches pattern",
                argv[0]);
        return TCL_ERROR;
    }

    strPtr = (char *)ckalloc((unsigned) length);
    interp->result = strPtr;
    interp->dynamic = 1;
    LIST_FORALL(stringHdr,linkPtr) {
        strcpy(strPtr,((stringList *)linkPtr)->str);
        strPtr += strlen(strPtr)+1;
        strPtr[-1] = ' ';
    }
    strPtr[-1] = '\0';
    FREE(stringHdr);
    return TCL_OK;
}

/* [<][>][^][v][top][bottom][index][help] */