/*************************************************************
 * vt100 terminal emulator - Wild card and Directory support
 *
 *	v2.4 861214 DBW - lots of fixes/additions (see readme file)
 *	v2.3 861101 DBW - minor bug fixes
 *	v2.2 861012 DBW - more of the same
 *	v2.1 860915 DBW	- new features (see README)
 *           860830 Steve Drew Added Wild card support,
 *		    features(expand.c)
 *	v2.0 860809 DBW - Major rewrite
 *	v1.1 860720 DBW	- Switches, 80 cols, colors, bug fixes
 *	v1.0 860712 DBW	- First version released
 *
 *      Much of the code from this module extracted from
 *      Matt Dillons Shell program. (Thanxs Matt.)
 *************************************************************/

#define MODULE_EXPAND 1
#include "vt100.h"

struct DPTR {                    /* Format of directory fetch ptr */
   struct FileLock *lock;        /* lock on directory   */
   struct FileInfoBlock *fib;    /* mod'd fib for entry */
};

/*
 * Disk directory routines
 *
 *
 * dopen() returns a struct DPTR, or NULL if the given file does not
 * exist.  stat will be set to 1 if the file is a directory.  If the
 * name is "", then the current directory is openned.
 *
 * dnext() returns 1 until there are no more entries.  The **name and
 * *stat are set.  *stat = 1 if the file is a directory.
 *
 * dclose() closes a directory channel.
 *
 */

struct DPTR *
dopen(name, stat)
char *name;
int *stat;
{
   struct DPTR *dp;
   int namelen, endslash = 0;
   struct FileLock *MyLock;
   
   MyLock = (struct FileLock *)( (ULONG) ((struct Process *)
                                 (FindTask(NULL)))->pr_CurrentDir);
   namelen = strlen(name);
   if (namelen && name[namelen - 1] == '/') {
      name[namelen - 1] = '\0';
      endslash = 1;
   }
   *stat = 0;
   dp = (struct DPTR *)malloc(sizeof(struct DPTR));
   if (*name == '\0') 
      dp->lock = (struct FileLock *)DupLock (MyLock);
   else
      dp->lock = (struct FileLock *)Lock (name, ACCESS_READ);
   if (endslash)
      name[namelen - 1] = '/';
   if (dp->lock == NULL) {
      free (dp);
      return (NULL);
   }
   dp->fib = (struct FileInfoBlock *)
         AllocMem((long)sizeof(struct FileInfoBlock), MEMF_PUBLIC);
   if (!Examine (dp->lock, dp->fib)) {
      dclose (dp);
      return (NULL);
   }
   if (dp->fib->fib_DirEntryType >= 0)
      *stat = 1;
   return (dp);
}

dnext(dp, pname, stat)
struct DPTR *dp;
char **pname;
int *stat;
{
   if (dp == NULL)
      return (0);
   if (ExNext (dp->lock, dp->fib)) {
      *stat = (dp->fib->fib_DirEntryType < 0) ? 0 : 1;
      *pname = dp->fib->fib_FileName;
      return (1);
   }
   return (0);
}


dclose(dp)
struct DPTR *dp;
{
   if (dp == NULL)
      return (1);
   if (dp->fib)
      FreeMem (dp->fib, (long)sizeof(*dp->fib));
   if (dp->lock)
      UnLock (dp->lock);
   free (dp);
   return (1);
}

free_expand(av)
char **av;
{
   char **base = av;

   if (av) {
      while (*av) {
         free (*av);
         ++av;
      }
      free (base);
   }
}

/*
 * EXPAND(wild_name, pac)
 *    wild_name      - char * (example: "df0:*.c")
 *    pac            - int  *  will be set to # of arguments.
 *
 */


char **
expand(base, pac)
char *base;
int *pac;
{
   char **eav = (char **)malloc (sizeof(char *));
   int  eleft, eac;

   char *ptr, *name;
   char *bname, *ename, *tail;
   int stat, scr;
   struct DPTR *dp;

   *pac = eleft = eac = 0;
   base = strcpy(malloc(strlen(base)+1), base);
   for (ptr = base; *ptr && *ptr != '?' && *ptr != '*'; ++ptr);
   for (; ptr >= base && !(*ptr == '/' || *ptr == ':'); --ptr);
   if (ptr < base) {
      bname = strcpy (malloc(1), "");
   } else {
      scr = ptr[1];
      ptr[1] = '\0';
      bname = strcpy (malloc(strlen(base)+1), base);
      ptr[1] = scr;
   }
   ename = ptr + 1;
   for (ptr = ename; *ptr && *ptr != '/'; ++ptr);
   scr = *ptr;
   *ptr = '\0';
   tail = (scr) ? ptr + 1 : NULL;

   if ((dp = dopen (bname, &stat)) == NULL  ||  stat == 0) {
      free (bname);
      free (base);
      free (eav);
      emits ("Could not open directory");
      return (NULL);
   }
   while (dnext (dp, &name, &stat)) {
      if (compare_ok(ename, name)) {
         if (tail) {
            int alt_ac;
            char *search, **alt_av, **scrav;
            struct FileLock *lock;

            if (!stat)      /* expect more dirs, but this not a dir */
               continue;
            lock = (struct FileLock *)CurrentDir (dp->lock);
            search = malloc(strlen(name)+strlen(tail)+2);
            strcpy (search, name);
            strcat (search, "/");
            strcat (search, tail);
            scrav = alt_av = expand (search, &alt_ac);
            CurrentDir (lock);
            if (scrav) {
               while (*scrav) {
                  if (eleft < 2) {
                     char **scrav = (char **)
			malloc(sizeof(char *) * (eac + 10));
                     movmem (eav, scrav, sizeof(char *) * (eac + 1));
                     free (eav);
                     eav = scrav;
                     eleft = 10;
                  }
                  eav[eac] = malloc(strlen(bname)+strlen(*scrav)+1);
                  strcpy(eav[eac], bname);
                  strcat(eav[eac], *scrav);
                  free (*scrav);
                  ++scrav;
                  --eleft, ++eac;
               }
               free (alt_av);
            }
         } else {
            if (eleft < 2) {
               char **scrav = (char **)
		    malloc(sizeof(char *) * (eac + 10));
               movmem (eav, scrav, sizeof(char *) * (eac + 1));
               free (eav);
               eav = scrav;
               eleft = 10;
            }
            eav[eac] = malloc (strlen(bname)+strlen(name)+1);
            eav[eac] = strcpy(eav[eac], bname);
            strcat(eav[eac], name);
            --eleft, ++eac;
         }
      }
   }
   dclose (dp);
   *pac = eac;
   eav[eac] = NULL;
   free (bname);
   free (base);
   if (eac)
      return (eav);
   free (eav);
   return (NULL);
}

/*
 * Compare a wild card name with a normal name
 */

#define MAXB   8

compare_ok(wild, name)
char *wild, *name;
{
   char *w = wild;
   char *n = name;
   char *back[MAXB][2];
   int  bi = 0;

   while (*n || *w) {
      switch (*w) {
      case '*':
         if (bi == MAXB) {
            emits ("Too many levels of '*'");
            return (0);
         }
         back[bi][0] = w;
         back[bi][1] = n;
         ++bi;
         ++w;
         continue;
goback:
         --bi;
         while (bi >= 0 && *back[bi][1] == '\0')
            --bi;
         if (bi < 0)
            return (0);
         w = back[bi][0] + 1;
         n = ++back[bi][1];
         ++bi;
         continue;
      case '?':
         if (!*n) {
            if (bi)
               goto goback;
            return (0);
         }
         break;
      default:
         if (toupper(*n) != toupper(*w)) {
            if (bi)
               goto goback;
            return (0);
         }
         break;
      }
      if (*n)  ++n;
      if (*w)  ++w;
   }
   return (1);
}

set_dir(new)
char *new;
{
   register 	char 		*s;
   int   			i;
   struct 	FileLock 	*lock;
   char 			temp[60];
   struct       FileInfoBlock   fib;
  
   if (*new != '\000') {
      strcpy(temp, MyDir);
      s = new;
      if (*s == '/') {
         s++;
         for (i=strlen(MyDir);
              MyDir[i] != '/' && MyDir[i] != ':';
              i--);
         MyDir[i+1] = '\0';
           strcat(MyDir, s);
         }
      else if (exists(s, ':') == 0) {
         if (MyDir[strlen(MyDir)-1] != ':')
            strcat(MyDir, "/");
         strcat(MyDir, s);
         }
      else
         strcpy(MyDir, s);

      if ((lock = (struct FileLock *)Lock(MyDir)) == 0) {
         emits("Directory not found\n");
         strcpy(MyDir, temp);
         }
      else {
      	 if (Examine(lock, &fib)) {
      	    if (fib.fib_DirEntryType > 0) {
               if (lock = (struct FileLock *)CurrentDir(lock))
                  UnLock(lock);
               if (MyDir[strlen(MyDir)-1] == '/')
                  MyDir[strlen(MyDir)-1] = '\0';
               }
            else {
            	emits("Not a Directory\n");               
            	strcpy(MyDir,temp);
               }
            }
         }
      }
}

exists(s,c)
char *s,c;
    {
    while (*s != '\000')
	if (*s++ == c) return(1);
    return(0);
    }
