/*************************************************************
 * vt100 terminal emulator - Wild card and Directory support
 *
 *	v2.7 870825 ACS - Use the *InfoMsg*() routines in window.c rather
 *			  than req().
 *	v2.6 870227 DBW - bug fixes for all the stuff in v2.5
 *	v2.5 870214 DBW - more additions (see readme file)
 *	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.)
 *************************************************************/

#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
 *
 *
 * diropen() 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.
 *
 * dirnext() returns 1 until there are no more entries.  The **name and
 * *stat are set.  *stat = 1 if the file is a directory.
 *
 * dirclose() closes a directory channel.
 *
 */

struct DPTR *
diropen(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)) {
      dirclose (dp);
      return (NULL);
   }
   if (dp->fib->fib_DirEntryType >= 0)
      *stat = 1;
   return (dp);
}

dirnext(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);
}


dirclose(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 = diropen (bname, &stat)) == NULL  ||  stat == 0) {
      free (bname);
      free (base);
      free (eav);
      InfoMsg1Line("Could not open directory");
      return (NULL);
   }
   while (dirnext (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;
	 }
      }
   }
   dirclose (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) {
	    InfoMsg1Line("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, (long)ACCESS_READ)) == 0) {
	 InfoMsg2Line("Directory not found:",MyDir);
	 strcpy(MyDir, temp);
	 }
      else {
	 fib = (struct FileInfoBlock *)AllocMem(
		(long)sizeof(struct FileInfoBlock), MEMF_PUBLIC);
	 if (fib) {
	    if (Examine(lock, fib)) {
		if (fib->fib_DirEntryType > 0) {
#ifdef BUGFIXES
                    UnLock(CurrentDir(lock));
#else
		    CurrentDir(lock);
		    if (MyDirLock != NULL) UnLock(MyDirLock);
		    MyDirLock = lock;
#endif
		    if (MyDir[strlen(MyDir)-1] == '/')
			MyDir[strlen(MyDir)-1] = '\000';
		    }
		else {
		    InfoMsg2Line("Not a Directory:",MyDir);
		    strcpy(MyDir,temp);
		    }
		}
	    FreeMem(fib, (long)sizeof(struct FileInfoBlock));
	    }
	else {
	    InfoMsg1Line("Can't change directory...No free memory!");
	    strcpy(MyDir,temp);
	    }
	}
    }
}

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