/*
** The following module contains functions which permit wildcard
** filename expansion using Unix type wildcard characters.  This
** module can be compiled separately and included in the link for
** any programs which desire it's support.  The following functions
** are present:
**
**   findfirst       - find the first occurance of a file matching
**                     the given name which may contain Unix style
**                     wildcards.
**   findnext        - find the next occurance of a file.  Returns
**                     0 if no more matches remain.
**   find_cleanup    - release all storage and locks reserved by findfirst
**                     and/or findnext.  MUST be called before exit!
**   fferror         - given an error code returned by findnext, this
**                     function returns a pointer to a descriptive
**                     error message string.
**   iswild          - given a string, this function returns
**                     TRUE if the string contains a wildcard character
**                     and FALSE if not.
**   wildexp         - given a file name and a pointer to a table of
**                     pointers, this function will fill the table
**                     with a list of file names in the indicated
**                     directory which match the given name.
**
** 
** Written by:  Rick Schaeffer
**              E. 13611 26th Ave.
**              Spokane, Wa.  99216
**              Compuserve ID: 70120,174
**              Bytenet ID: ricks.
*/

#include "wildexp.h"

/*
** Return values for findfirst and findnext are:
**  0 = retval ok, find struct "fname" contains name of file found
**  1 = file name too long
**  2 = error in parsing
**  3 = invalid path name
**  4 = first Examine failed
**  5 = not a directory
*/

/* fferror -- return a meaningful error message for findxxx errors
**  Parameter:
**     errcd - An integer containing the error code returned by findfirst
**             or findnext.
**
**  Returns:
**     msgptr - A pointer to a meaningful error message
*/
char *fferror(errcd)
int errcd;
{
    static char *errmsg[] = {
        "Filename too long",
        "Filename invalid",
        "Pathname invalid",
        "Examine failed",
        "Pathname invalid",
        "Error code invalid"
        };
    
    if (--errcd > 4)
        errcd = 5;
    return(errmsg[errcd]);
}   

/* findfirst -- find the first occurance of a given file name in either
**              the given or the current directory.
**  Parameters:
**    name - A pointer to a filename string.  May contain wildcard
**           characters ('?' and/or '*').  May optionally contain
**           a directory path.  Example: "df0:c/d*" matches all
**           files in directory "df0:c" which begin with the letter
**           "d".
**    fwk  - A pointer to a structure which will be filled in by
**           findfirst and used by findnext.  Must NOT be disturbed
**           between calls!
**
**  Returns:
**    0 = successful completion.  fwk filled in with first matching
**        file.
**   >0 = error code.  use fferror to obtain a meaningful description.
**
**  Example:
**    findfirst("*.c",fwk)
**       The first matching file is in fwk->fname.
*/
findfirst(name,fwk)
char    *name;
struct find     *fwk;
{
    struct Process *tp,*FindTask();
    int             pt[16];
    int             last;
    char            *p1,*strchr();

    fwk->fp = (struct FileInfoBlock *) AllocMem(sizeof(struct FileInfoBlock),0);
        /* caller must free this space! */
    fwk->flock = 0;
    
    if (strlen(name) > 128)
        return(1); /* file name too long */
    if ((p1 = strchr(name,':')) != NULL)
        if (strchr(name,'/') == NULL) {
            *p1 = 0;
            strcpy(fwk->path,name);
            strcat(fwk->path,":/");
            strcat(fwk->path,p1+1);
            strcpy(name,fwk->path);
            }
    strcpy(fwk->path,name);
    if (stspfp(name,pt) == -1)
        return(2);  /* error in parsing */
    for (last=0; last < 16; last++)
        if (pt[last] == -1)
            break;
    last--;     /* now points at file name portion */
    if ((last == 0) && (pt[0] == 0)) {  /* no path */
        if (strlen(name) > 32)
            return(1); /* file name too long */
        strcpy(fwk->name,name);
        fwk->path[0] = 0;
        tp = FindTask(NULL);
        fwk->flock = DupLock(tp->pr_CurrentDir);
        bldfull(fwk);   /* build full path name */
        }
    else {
        if (strlen(&name[pt[last]]) > 32)
            return(1); /* file name too long */
        strcpy(fwk->name,&name[pt[last]]);
        fwk->path[pt[last] - 1] = 0;
        if ((fwk->flock = Lock(fwk->path,ACCESS_READ)) == 0)
            return(3); /* invalid path name */
        bldfull(fwk);
        }
    if (Examine(fwk->flock,fwk->fp)) {  /* get directory name */
        if (fwk->fp->fib_DirEntryType > 0)
            return(findnext(fwk));
        else
            return(5); /* not a directory */
        }
    else
        return(4);  /* first examine failed */
}

/*
** findnext -- find next occurance of a matching file.
**  Parameter:
**    fwk - pointer to a "find" structure which has been filled in
**          by a call to findfirst.
**
**  Returns:
**    0 = Match found.  fwk->fname contains the name.
**   -1 = No more matches.
*/
int findnext(fwk)
struct find *fwk;
{
    while (ExNext(fwk->flock,fwk->fp)) {
        strcpy(fwk->fname,fwk->fp->fib_FileName);
        if (fnmatch(fwk->fname,fwk->name))
            return(0);
        }
    return(-1);
}

bldfull()
{
}

/* find_cleanup -- release any structures and locks used by findfirst.
**   Parameters:
**      fwk - pointer to a "find" structure which has been previously
**            filled in by findfirst.
**   Returns:
**    nothing
*/
find_cleanup(fwk)
struct find *fwk;
{
    if (fwk->flock)
        UnLock(fwk->flock);
    if (fwk->fp)
        FreeMem(fwk->fp,sizeof(struct FileInfoBlock));
}

/* fnmatch -- perform unix style pattern match on a file name
**   usage: result = fnmatch(name,pattern)
**       returns 1 if "name" matches "pattern", 0 otherwise
*/

int fnmatch(name,pattern)
register char   *name,*pattern;
{

    while (*pattern) {
        if (*pattern == '*') {
            while (*pattern == '*')
                pattern++;
            while ((*name) && (tolower(*name) != tolower(*pattern)))
                name++;
            if (*name == 0)
                if (*pattern == 0)
                    return(1);  /* matched */
                else
                    return(0);
            }
        if (*pattern == '?') {
            pattern++;
            name++;
            continue;
            }
        if (tolower(*pattern) != tolower(*name))
            return(0); /* not matched */
        pattern++;
        name++;
        }
    if ((*name == 0) && (*pattern == 0))
        return(1);  /* matched */
    else
        return(0);  /* not matched */
}

/*
** wildexp -- expand a wildcard file name
**  Parameters:
**    name     - Pointer to the file name to be expanded.
**    adtbl    - Pointer to an array of pointers.
**    maxargs  - The maximum number of pointers contained in adtbl.
**
**  Returns:
**    1 = Successful completion.  The adtbl array will contain pointers
**        to all file names found and will be terminated with a NULL
**        pointer.  It's use is exactly like use of the standard C
**        argv array except that the first filename argument is in
**        adtbl[0] whereas argv[0] contains a pointer to the name of
**        the function which was invoked.
**    0 = An error occured.
*/
int wildexp(name,adtbl,maxargs)
char            *name;
register char   **adtbl;
int             maxargs;
{
    struct find f;
    register int i=0;
    char        *malloc();
    int     retval;

    if ((retval = findfirst(name,&f)) > 0) {
        *adtbl = NULL;
        find_cleanup(&f);
        return(0);
        }
    while (retval == 0) {
        if (f.fp->fib_DirEntryType > 0) {
            retval=findnext(&f);
            continue;   /* it's a directory */
            }
        *adtbl = malloc(strlen(f.path)+strlen(f.fname)+1);
        if (*adtbl == NULL) {
            find_cleanup(&f);
            return(0);  /* arena is full */
            }
        strcpy(*adtbl,f.path);
        if ((f.path[0] != 0) && (f.path[strlen(f.path)-1] != ':'))
            strcat(*adtbl,"/");
        strcat(*adtbl++,f.fname);
        if ((++i) >= maxargs)
            return(0);
        retval=findnext(&f);
        }
    *adtbl = NULL;
    find_cleanup(&f);
    return(1);
}

int iswild(s)
char    *s;
{
    while (*s) {
        if ((*s == '*') || (*s == '?'))
            return(1);
        s++;
        }
    return(0);
}

