/*
** The following module contains functions which permit wildcard
** filename expansion using Amiga 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.
**              Usenet address: ihnp4!tektronix!reed!iscuva!ricks
**
** Placed in public domain 5/10/86 by the author.
** May be used for any purpose.  The author assumes NO responsibility
** for support or for any problems use of this program may cause.
**
** Modifications to convert this program to support Amiga style wildcard
** by Jeff Lydiatt, Vancouver, B.C., Canada, 06Jun86.
*/


#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,*index(),*AllocMem();

    fwk->fp = (struct FileInfoBlock *) AllocMem((long) sizeof(struct FileInfoBlock),0l);
        /* caller must free this space! */
    fwk->flock = 0;
    
    if (strlen(name) > 128)
        return(1); /* file name too long */
    if ((p1 = index(name,':')) != NULL)
        if (index(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,(long) 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)
char   *name,*pattern;
{
    int CmplPat(), Match();

    int aux[ 128 ];
    if ( CmplPat( pattern, aux ) == 0 )
       return 0;
    return (int) Match( pattern, &aux, name );
}

/*
** 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) {
	switch (*s)
	   {
	     case '#':
	     case '?':
	     case '%':
	     case '|':
	     case '(': return 1;
	     default : ;
	   }
        s++;
        }
    return 0;
}

stspfp(name,n)
char *name;
int n[16];
{
	register int i,j;
	int		flag=1;
	char	ch;

	i = 0;
	j = 0;
	while (ch = name[i]) {
		if (ch == '/') {
			if (j >= 15)
				return(-1);
			flag = 1;
			if (i != 0)
				name[i] = 0;
			}
		else {
			if (flag) {
				if (name[i] != 0) {
					n[j++] = i;
					n[j] = -1;
					}
				flag = 0;
				}
			}
		i++;
		}
	return(0);
}
