/* dirList.c
   routines to manage the data structures for gopher directories */

     /*---------------------------------------------------------------*/
     /* Xgopher        version 1.2     20 November 1991               */
     /*                version 1.1     20 April 1991                  */
     /*                version 1.0     04 March 1991                  */
     /* X window system client for the University of Minnesota        */
     /*                                Internet Gopher System.        */
     /* Allan Tuchman, University of Illinois at Urbana-Champaign     */
     /*                Computing and Communications Services Office   */
     /* Copyright 1992 by                                             */
     /*           the Board of Trustees of the University of Illinois */
     /* Permission is granted to freely copy and redistribute this    */
     /* software with the copyright notice intact.                    */
     /*---------------------------------------------------------------*/



#include <stdio.h>

#include "gopher.h"
#include "dirList.h"
#include "item.h"
#include "listP.h"

static	gopherDirList	unusedDirs = {NULL, NULL};

static	int		firstDirAlloc  = TRUE;


/* Directory list management routines:
	acquireDir()			return ptr to a gopherDir structure
	releaseDir(gopherDirP)		Return directory to the free list
	previousDir(gopherDirP)		return a pointer to previous directory
	nextDir(gopherDirP)		return a pointer to next directory
	freeDirList(gopherDirP)		Free a list of directories
	allocGopherDir(int)		Allocate n new gopher directories

   current directory list management:
	getCurrentDir()			return a ptr to current directory
	pushCurrentDir(gopherDirP)	Push a new directory to be current
	popCurrentDir()			make previous directory current
	getCurrentDirIndex()		return search string of curr directory
	atFirstDir()			T/F; is current directory first one
	checkDirStack()			freeing expired gopher items from stack
*/


/* acquireDir 
	Allocate a gopher directory from the free list */

gopherDirP
acquireDir()
{
	gopherDirP	dir;

	if (unusedDirs.first == NULL) {
		/* out of gopher directories, so allocate more */
		allocGopherDir(firstDirAlloc ? dirStart : dirIncrement);
		firstDirAlloc = FALSE;
	}
	dir = unusedDirs.first;
	unusedDirs.first = dir->next;
	if (unusedDirs.first == NULL) unusedDirs.last = NULL;

	dir->previous = dir->next = NULL;

	return dir;
}


/* releaseDir 
	Return a gopher directory to the free list */

void
releaseDir(dir)
gopherDirP	dir;
{
	dir->next = unusedDirs.first;
	unusedDirs.first = dir;
	if (unusedDirs.last == NULL) unusedDirs.last = dir;

	return;
}


/* previousDir 
	return a pointer to the directory previous to the given one */

gopherDirP
previousDir(dir)
gopherDirP	dir;
{
	if (dir == NULL) return NULL;
	return dir->previous;
}


/* nextDir 
	return a pointer to the directory following to the given one*/

gopherDirP
nextDir(dir)
gopherDirP	dir;
{
	if (dir == NULL) return NULL;
	return dir->next;
}


/* freeDirList
	Free a list of directories */

static void
freeDirList(dir)
gopherDirP	dir;
{
	gopherDirP	p;

	for (p=dir; p->next != NULL; p=p->next) ;

	if (unusedDirs.last == NULL) unusedDirs.last = p;
	p->next = unusedDirs.first;
	unusedDirs.first = dir;

	return;
}


/* allocGopherDir
	Allocate n new gopher directories, adding them to the free list */

static void
allocGopherDir(n)
int	n;
{
	gopherDirP	dirList, p;
	int		i;

	if (n <= 0) return;

	if ((dirList = (gopherDirP) malloc(n * sizeof(gopherDir))) == NULL) {
		/* out of memory */
		fprintf (stderr, "There is not enough memory to continue.\n");
		exit(3);
	}

	for (i=0, p=dirList; i<n-1; i++, p++) {
		p->next = p+1;
	}
	p->next = NULL;
	freeDirList(dirList);

	return;
}

/* -----------------------------------------------------------------------
   Special section for managing Current directory and the directory stack
   ----------------------------------------------------------------------- */

static  gopherDirP      currentDir = NULL;	/* current directory pointer */
static  gopherDirP      firstDir = NULL;	/* start of dir stack */


/* getCurrentDir 
	return a pointer to the current directory */

gopherDirP
getCurrentDir()
{
	return currentDir;
}


/* pushCurrentDir
	Push a new directory to the stack after the current one and
	make the new one current. */

void
pushCurrentDir(dir)
gopherDirP	dir;
{

	/* check for first directory in stack */

	if (currentDir == NULL) {
		dir->previous = NULL;
		firstDir = dir;
	} else {

		/* discard "popped" directories, if any */

		reallyPopFrom(currentDir->next);

		dir->previous = currentDir;
		currentDir->next = dir;
	}

	dir->next = NULL;
	currentDir = dir;

	return;
}


/* popCurrentDir
	Just move back to previous directory.  Don't actually
	delete the subsequent ones until a new push is done. */

void
popCurrentDir()
{
	if (currentDir == NULL) return;
	if (currentDir->previous == NULL) return;

	currentDir = currentDir->previous;

	return;
}


/* reallyPopFrom
   discard and reclaim the directories in the stack starting from
   the given one */
   
static void
reallyPopFrom(d)
gopherDirP	d;
{
	gopherDirP	tmp;

	while (d != NULL){
		tmp = d->next;
		freeDir(d);
		d = tmp;
	}
}


/* getCurrentDirIndex 
	return a pointer to the index search string of the current directory */

char *
getCurrentDirIndex()
{
	return getItemIndex(currentDir->selectorItem);
}


/* atFirstDir 
	return TRUE is current directory is the first one, FALSE otherwise */

BOOLEAN
atFirstDir()
{
	return ((currentDir == NULL) || (currentDir->previous == NULL));
}


/* clearDirStack
   wipe out the cirectory stack; set it back to its initial state */

void
clearDirStack()
{
	reallyPopFrom(firstDir);
	currentDir = NULL;
	firstDir = NULL;
}


/* checkDirStack
	traverse directory stack freeing expired gopher items */

void
checkDirStack()
{
	gopherDirP	d;

	for (d=firstDir; d != NULL; d = d->next) {
		if (d->created != NOT_LOADED) clearDirWhenOld(d);
	}

	return;
}
