/****************************************************************************

	DirMgr.c

	This file contains the C code to implement the DirectoryMgr system.

	This system is intended to manage filtered and sorted directory
	lists.

 ****************************************************************************/

#include "DirMgr.h"

#define	DIR_MGR_FSM_SIZE 1024

/*---------------------------------------------------------------------------*

                   S I M P L E    I N T E R F A C E

 *---------------------------------------------------------------------------*/

DirectoryMgr *DirectoryMgrSimpleOpen
#ifdef UseFunctionPrototypes
	(char *path, int sort_type, char *pattern)
#else
	(path,sort_type,pattern)
char *path;
int sort_type;
char *pattern;

#endif
{
	DirectoryMgr *dm;
	PFI f_func,s_func;
	char *f_data;

	if (pattern == NULL) pattern = "*";
	if (!DirectoryMgrSimpleFilterFunc(pattern,&f_func,&f_data))
	{
		return(NULL);
	}
	if (!DirectoryMgrSimpleSortingFunc(sort_type,&s_func))
	{
		free(f_data);
		return(NULL);
	}
	dm = DirectoryMgrOpen(path,s_func,f_func,f_data,TRUE);
	return(dm);
} /* End DirectoryMgrSimpleOpen */


int DirectoryMgrSimpleRefilter
#ifdef UseFunctionPrototypes
	(DirectoryMgr *dm, char *pattern)
#else
	(dm,pattern)
DirectoryMgr *dm;
char *pattern;

#endif
{
	PFI f_func;
	char *f_data;

	if (!DirectoryMgrSimpleFilterFunc(pattern,&f_func,&f_data))
	{
		return(FALSE);
	}
	DirectoryMgrRefilter(dm,f_func,f_data,TRUE);
	return(TRUE);
} /* End DirectoryMgrSimpleRefilter */


int DirectoryMgrSimpleResort
#ifdef UseFunctionPrototypes
	(DirectoryMgr *dm, int sort_type)
#else
	(dm,sort_type)
DirectoryMgr *dm;
int sort_type;

#endif
{
	PFI c_func;

	if (!DirectoryMgrSimpleSortingFunc(sort_type,&c_func))
	{
		return(FALSE);
	}
	DirectoryMgrResort(dm,c_func);
	return(TRUE);
} /* End DirectoryMgrSimpleResort */


/*---------------------------------------------------------------------------*

                    N O R M A L    I N T E R F A C E

 *---------------------------------------------------------------------------*/

DirectoryMgr *DirectoryMgrOpen
#ifdef UseFunctionPrototypes
	(char *path, PFI c_func, PFI f_func, char *f_data, int free_data)
#else
	(path,c_func,f_func,f_data,free_data)
char *path;
PFI c_func,f_func;
char *f_data;
int free_data;

#endif
{
	DirectoryMgr *dm;

	dm = (DirectoryMgr *)calloc(1,sizeof(DirectoryMgr));
	if (dm == NULL)
	{
		if (free_data && f_data) free(f_data);
		return(NULL);
	}
	if (DirectoryOpen(path,DirectoryMgrDir(dm)) == FALSE)
	{
		free(dm);
		if (free_data && f_data) free(f_data);
		return(NULL);
	}
	DirectoryMgrCompFunc(dm) = c_func;
	DirectoryMgrRefilter(dm,f_func,f_data,free_data);
	return(dm);
} /* End DirectoryMgrOpen */


void DirectoryMgrClose
#ifdef UseFunctionPrototypes
	(DirectoryMgr *dm)
#else
	(dm)
DirectoryMgr *dm;

#endif
{
	free(DirectoryMgrData(dm));
	free(DirectoryMgrSortedPtrs(dm));
	if (DirectoryMgrFilterData(dm) && DirectoryMgrFreeFilterData(dm))
	{
		free(DirectoryMgrFilterData(dm));
	}
	DirectoryClose(DirectoryMgrDir(dm));
	free(dm);
} /* End DirectoryMgrClose */


int DirectoryMgrRefilter
#ifdef UseFunctionPrototypes
	(DirectoryMgr *dm, PFI f_func, char *f_data, int f_free)
#else
	(dm,f_func,f_data,f_free)
DirectoryMgr *dm;
PFI f_func;
char *f_data;
int f_free;

#endif
{
	if (DirectoryMgrFilterData(dm) && DirectoryMgrFreeFilterData(dm))
	{
		free(DirectoryMgrFilterData(dm));
	}
	DirectoryMgrFilterFunc(dm) = f_func;
	DirectoryMgrFilterData(dm) = f_data;
	DirectoryMgrFreeFilterData(dm) = f_free;
	DirectoryMgrRefresh(dm);
} /* End DirectoryMgrRefilter */


int DirectoryMgrRefresh
#ifdef UseFunctionPrototypes
	(DirectoryMgr *dm)
#else
	(dm)
DirectoryMgr *dm;

#endif
{
	int err,data_size,ptrs_size,i;
	DirEntryCons *head,*tail,*cons;
	DirEntry *dm_data,**dm_ptrs;
	PFI f_func;
	char *f_data;
#ifdef DEBUG
	FILE *fp;
	fp = fopen("x.tmp", "w");
#endif /* DEBUG */

	err = TRUE;
	DirectoryMgrTotalCount(dm) = 0;
	DirectoryMgrFilteredCount(dm) = 0;
	DirectoryRestart(DirectoryMgrDir(dm));
	if (DirectoryMgrData(dm)) free(DirectoryMgrData(dm));
	if (DirectoryMgrSortedPtrs(dm)) free(DirectoryMgrSortedPtrs(dm));
	head = NULL;
	f_func = DirectoryMgrFilterFunc(dm);
	f_data = DirectoryMgrFilterData(dm);
	while (1)
	{
		cons = (DirEntryCons *)malloc(sizeof(DirEntryCons));
		if (cons == NULL)
		{
			fprintf(stderr,
				"DirectoryMgrRefresh: Can't Alloc Cons\n");
			exit(-1);
		}
#ifdef vax11c
		if ( !DirectoryMgrTotalCount(dm) && err != FALSE ) {
			/*
			 *	Ignore the possiblity of this
			 *	not being there, esp. if it is
			 *	the root.
			 */
			err = GetParentDirectory(DirectoryMgrDir(dm),
						 &(cons->dir_entry));
			if (err == FALSE) continue;
		} else {
			err = DirectoryReadNextEntry(DirectoryMgrDir(dm),
						     &(cons->dir_entry));
		}
#else
		err = DirectoryReadNextEntry(DirectoryMgrDir(dm),
					     &(cons->dir_entry));
#endif /* vax11c */
		if (err == FALSE)
		{
			free(cons);
			break;
		}
#ifdef DEBUG
		else {
			register DirEntry *de = &(cons->dir_entry);

			fprintf(fp,"Directory Entry for %s\n",
					DirEntryFileName(de));
			fprintf(fp,"\t");
			switch (DirEntryType(de)) {
				case F_TYPE_DIR: fprintf(fp,"Directory (%d)\n", F_TYPE_DIR); break;
				case F_TYPE_FILE: fprintf(fp,"Text (%d)\n", F_TYPE_FILE); break;
				case F_TYPE_CHAR_SPECIAL: fprintf(fp,"Char Special (%d)\n", F_TYPE_CHAR_SPECIAL); break;
				case F_TYPE_BLOCK_SPECIAL: fprintf(fp,"Block Special (%d)\n", F_TYPE_BLOCK_SPECIAL); break;
				default: fprintf(fp, "Unknown (%d)\n", DirEntryType(de)); break;
			}
			fprintf(fp,"\t== File Information ==\n");
			fprintf(fp,"\t  Mode: 0%o\n", FileInfoOrigMode(DirEntryActualInfo(de)));
			fprintf(fp,"\t  Prot: 0%o\n", FileInfoProt(DirEntryActualInfo(de)));
			fprintf(fp,"\t  User: 0%o\n", FileInfoUserID(DirEntryActualInfo(de)));
			fprintf(fp,"\t  Group: 0%o\n", FileInfoGroupID(DirEntryActualInfo(de)));
			fprintf(fp,"\t  Size: %d (bytes)\n", FileInfoFileSize(DirEntryActualInfo(de)));
			fprintf(fp,"\n");
		}
#endif /* DEBUG */

		++ DirectoryMgrTotalCount(dm);
		if ((f_func == NULL) ||
		    (f_func && f_func(&(cons->dir_entry),f_data)))
		{
			cons->next = NULL;
			if (head == NULL)
				head = cons;
			    else
				tail->next = cons;
			tail = cons;
			++ DirectoryMgrFilteredCount(dm);
		}
		    else			/* Filter Failed */
		{
			free(cons);
		}
	}

	data_size = sizeof(DirEntry) * DirectoryMgrFilteredCount(dm);
	ptrs_size = sizeof(DirEntry *) * DirectoryMgrFilteredCount(dm);
	dm_data = (DirEntry *)malloc(data_size);
	dm_ptrs = (DirEntry **)malloc(ptrs_size);
	if ((dm_data == NULL) || (dm_ptrs == NULL))
	{
		fprintf(stderr,"DirectoryMgrRefresh: Out of memory\n");
		exit(1);
	}
	DirectoryMgrData(dm) = dm_data;
	DirectoryMgrSortedPtrs(dm) = dm_ptrs;

	for (i = 0; i < DirectoryMgrFilteredCount(dm); i++)
	{
		DirectoryMgrData(dm)[i] = head->dir_entry;
		DirectoryMgrSortedPtrs(dm)[i] = &(DirectoryMgrData(dm)[i]);
		cons = head->next;
		free(head);
		head = cons;
	}

	DirectoryMgrResort(dm,DirectoryMgrCompFunc(dm));
	DirectoryMgrRestart(dm);
	return(TRUE);
} /* End DirectoryMgrRefresh */


void DirectoryMgrResort
#ifdef UseFunctionPrototypes
	(DirectoryMgr *dm, PFI c_func)
#else
	(dm,c_func)
DirectoryMgr *dm;
PFI c_func;

#endif
{
	DirectoryMgrCompFunc(dm) = c_func;
	if (c_func != NULL)
	{
		qsort(DirectoryMgrSortedPtrs(dm),DirectoryMgrFilteredCount(dm),
		      sizeof(DirEntry *),DirectoryMgrCompFunc(dm));
	}
	DirectoryMgrRestart(dm);
} /* End DirectoryMgrResort */

/*---------------------------------------------------------------------------*

                  I T E R A T I O N    C O M M A N D S

 *---------------------------------------------------------------------------*/

int DirectoryMgrGotoItem
#ifdef UseFunctionPrototypes
	(DirectoryMgr *dm, int i)
#else
	(dm,i)
DirectoryMgr *dm;
int i;

#endif
{
	if (i < 0 || i >= DirectoryMgrFilteredCount(dm)) return(FALSE);
	DirectoryMgrCurrentIndex(dm) = i;
	return(TRUE);
} /* End DirectoryMgrGotoItem */


int DirectoryMgrGotoNamedItem
#ifdef UseFunctionPrototypes
	(DirectoryMgr *dm, char *name)
#else
	(dm,name)
DirectoryMgr *dm;
char *name;

#endif
{
	int i;
	DirEntry *entry;

	for (i = 0; i < DirectoryMgrFilteredCount(dm); i++)
	{
		entry = DirectoryMgrSortedPtrs(dm)[i];
		if (strcmp(DirEntryFileName(entry),name) == 0)
		{
			DirectoryMgrCurrentIndex(dm) = i;
			return(TRUE);
		}
	}
	return(FALSE);
} /* End DirectoryMgrGotoNamedItem */


void DirectoryMgrRestart
#ifdef UseFunctionPrototypes
	(DirectoryMgr *dm)
#else
	(dm)
DirectoryMgr *dm;

#endif
{
	DirectoryMgrCurrentIndex(dm) = 0;
} /* End DirectoryMgrRestart */


DirEntry *DirectoryMgrCurrentEntry
#ifdef UseFunctionPrototypes
	(DirectoryMgr *dm)
#else
	(dm)
DirectoryMgr *dm;

#endif
{
	int index;

	index = DirectoryMgrCurrentIndex(dm);
	if (index < 0 || index >= DirectoryMgrFilteredCount(dm)) return(NULL);
	return(DirectoryMgrSortedPtrs(dm)[index]);
} /* End DirectoryMgrCurrentEntry */


DirEntry *DirectoryMgrNextEntry
#ifdef UseFunctionPrototypes
	(DirectoryMgr *dm)
#else
	(dm)
DirectoryMgr *dm;

#endif
{
	int index;

	index = DirectoryMgrCurrentIndex(dm);
	if (index >= DirectoryMgrFilteredCount(dm)) return(NULL);
	++ DirectoryMgrCurrentIndex(dm);
	return(DirectoryMgrSortedPtrs(dm)[index]);
} /* End DirectoryMgrNextEntry */


DirEntry *DirectoryMgrPrevEntry
#ifdef UseFunctionPrototypes
	(DirectoryMgr *dm)
#else
	(dm)
DirectoryMgr *dm;

#endif
{
	int index;

	index = DirectoryMgrCurrentIndex(dm) - 1;
	if (index < 0) return(NULL);
	-- DirectoryMgrCurrentIndex(dm);
	return(DirectoryMgrSortedPtrs(dm)[index]);
} /* End DirectoryMgrPrevEntry */

/*---------------------------------------------------------------------------*

                   U T I L I T Y    F U N C T I O N S

 *---------------------------------------------------------------------------*/

int DirectoryMgrSimpleFilterFunc
#ifdef UseFunctionPrototypes
	(char *pattern, PFI *ff_ptr, char **fd_ptr)
#else
	(pattern,ff_ptr,fd_ptr)
char *pattern;
PFI *ff_ptr;
char **fd_ptr;

#endif
{
#ifndef	NO_REGEXP
	char regexp[2048];

	*ff_ptr = DirectoryMgrFilterName;
#ifdef vax11c
	*fd_ptr = NULL;
#else
	*fd_ptr = (char *)malloc(sizeof(char) * DIR_MGR_FSM_SIZE);
	if (*fd_ptr == NULL) return(FALSE);
#endif /* vax11c */
	RegExpPatternToRegExp(pattern,regexp);
#ifdef vax11c
	*fd_ptr = RegExpCompile(regexp,*fd_ptr,DIR_MGR_FSM_SIZE);
#else
	RegExpCompile(regexp,*fd_ptr,DIR_MGR_FSM_SIZE);
#endif /* vax11c */

#endif
	return(TRUE);
} /* End DirectoryMgrSimpleFilterFunc */


int DirectoryMgrSimpleSortingFunc
#ifdef UseFunctionPrototypes
	(int sort_type, PFI *sf_ptr)
#else
	(sort_type,sf_ptr)
int sort_type;
PFI *sf_ptr;

#endif
{
	*sf_ptr = NULL;
	switch (sort_type)
	{
	    case DIR_MGR_SORT_NONE:
		break;
	    case DIR_MGR_SORT_NAME:
		*sf_ptr = DirectoryMgrCompareName;
		break;
	    case DIR_MGR_SORT_SIZE_ASCENDING:
		*sf_ptr = DirectoryMgrCompareSizeAscending;
		break;
	    case DIR_MGR_SORT_SIZE_DESCENDING:
		*sf_ptr = DirectoryMgrCompareSizeDescending;
		break;
	    case DIR_MGR_SORT_NAME_DIRS_FIRST:
		*sf_ptr = DirectoryMgrCompareNameDirsFirst;
		break;
	    case DIR_MGR_SORT_ACCESS_ASCENDING:
		*sf_ptr = DirectoryMgrCompareLastAccessAscending;
		break;
	    case DIR_MGR_SORT_ACCESS_DESCENDING:
		*sf_ptr = DirectoryMgrCompareLastAccessDescending;
		break;
	    default:
		fprintf(stderr,"Bad sort type %d\n",sort_type);
		return(FALSE);
	}
	return(TRUE);
} /* End DirectoryMgrSimpleSortingFunc */

/*---------------------------------------------------------------------------*

                    S O R T I N G    R O U T I N E S

 *---------------------------------------------------------------------------*/

int DirectoryMgrCompareName
#ifdef UseFunctionPrototypes
	(DirEntry **e1p, DirEntry **e2p)
#else
	(e1p,e2p)
DirEntry **e1p,**e2p;

#endif
{
#ifdef vax11c
	/*
	 *	Another hack to allow "[-]" to always be the first in
	 *	line.
	 */
	if (!strcmp(DirEntryFileName(*e1p), "[-]"))
		return(-1);
	if (!strcmp(DirEntryFileName(*e2p), "[-]"))
		return(1);
#endif /* vax11c */

	return(strcmp(DirEntryFileName(*e1p),DirEntryFileName(*e2p)));
} /* End DirectoryMgrCompareName */


int DirectoryMgrCompareNameDirsFirst
#ifdef UseFunctionPrototypes
	(DirEntry **e1p, DirEntry **e2p)
#else
	(e1p,e2p)
DirEntry **e1p,**e2p;

#endif
{
#ifdef vax11c
	/*
	 *	Another hack to allow "[-]" to always be the first in
	 *	line.
	 */
	if (!strcmp(DirEntryFileName(*e1p), "[-]"))
		return(-1);
	if (!strcmp(DirEntryFileName(*e2p), "[-]"))
		return(1);
#endif /* vax11c */

	if (DirEntryLeadsToDir(*e1p))
	{
		if (!DirEntryLeadsToDir(*e2p)) return(-1);
	}
	    else if (DirEntryLeadsToDir(*e2p))
	{
		return(1);
	}
	return(strcmp(DirEntryFileName(*e1p),DirEntryFileName(*e2p)));
} /* End DirectoryMgrCompareNameDirsFirst */


int DirectoryMgrCompareSizeAscending
#ifdef UseFunctionPrototypes
	(DirEntry **e1p, DirEntry **e2p)
#else
	(e1p,e2p)
DirEntry **e1p,**e2p;

#endif
{
#ifdef vax11c
	/*
	 *	Another hack to allow "[-]" to always be the first in
	 *	line.
	 */
	if (!strcmp(DirEntryFileName(*e1p), "[-]"))
		return(-1);
	if (!strcmp(DirEntryFileName(*e2p), "[-]"))
		return(1);
#endif /* vax11c */

	if (DirEntryFileSize(*e1p) < DirEntryFileSize(*e2p))
		return (-1);
	    else if (DirEntryFileSize(*e1p) == DirEntryFileSize(*e2p))
		return (0);
	    else
		return (1);
} /* End DirectoryMgrCompareSizeAscending */


int DirectoryMgrCompareSizeDescending
#ifdef UseFunctionPrototypes
	(DirEntry **e1p, DirEntry **e2p)
#else
	(e1p,e2p)
DirEntry **e1p,**e2p;

#endif
{
#ifdef vax11c
	/*
	 *	Another hack to allow "[-]" to always be the first in
	 *	line.
	 */
	if (!strcmp(DirEntryFileName(*e1p), "[-]"))
		return(-1);
	if (!strcmp(DirEntryFileName(*e2p), "[-]"))
		return(1);
#endif /* vax11c */

	if (DirEntryFileSize(*e1p) > DirEntryFileSize(*e2p))
		return (-1);
	    else if (DirEntryFileSize(*e1p) == DirEntryFileSize(*e2p))
		return (0);
	    else
		return (1);
} /* End DirectoryMgrCompareSizeDescending */


int DirectoryMgrCompareLastAccessAscending
#ifdef UseFunctionPrototypes
	(DirEntry **e1p, DirEntry **e2p)
#else
	(e1p,e2p)
DirEntry **e1p,**e2p;

#endif
{
#ifdef vax11c
	/*
	 *	Another hack to allow "[-]" to always be the first in
	 *	line.
	 */
	if (!strcmp(DirEntryFileName(*e1p), "[-]"))
		return(-1);
	if (!strcmp(DirEntryFileName(*e2p), "[-]"))
		return(1);
#endif /* vax11c */

	return((long)DirEntryLastAccess(*e1p) >
	       (long)DirEntryLastAccess(*e2p));
} /* End DirectoryMgrCompareLastAccessAscending */


int DirectoryMgrCompareLastAccessDescending
#ifdef UseFunctionPrototypes
	(DirEntry **e1p, DirEntry **e2p)
#else
	(e1p,e2p)
DirEntry **e1p,**e2p;

#endif
{
#ifdef vax11c
	/*
	 *	Another hack to allow "[-]" to always be the first in
	 *	line.
	 */
	if (!strcmp(DirEntryFileName(*e1p), "[-]"))
		return(-1);
	if (!strcmp(DirEntryFileName(*e2p), "[-]"))
		return(1);
#endif /* vax11c */

	return((long)DirEntryLastAccess(*e1p) <
	       (long)DirEntryLastAccess(*e2p));
} /* End DirectoryMgrCompareLastAccessDescending */

/*---------------------------------------------------------------------------*

                     F I L T E R    R O U T I N E S

 *---------------------------------------------------------------------------*/

int DirectoryMgrFilterName
#ifdef UseFunctionPrototypes
	(DirEntry *de, char *fsm)
#else
	(de,fsm)
DirEntry *de;
char *fsm;

#endif
{
#ifndef	NO_REGEXP
	return(RegExpMatch(DirEntryFileName(de),fsm));
#else
	return(TRUE);
#endif
} /* End DirectoryMgrFilterName */
