/*
 * stack.c --
 *
 * General purpose stack manipulation routines.
 *
 *     *********************************************************************
 *     * Copyright (C) 1985, 1990 Regents of the University of California. *
 *     * Permission to use, copy, modify, and distribute this              *
 *     * software and its documentation for any purpose and without        *
 *     * fee is hereby granted, provided that the above copyright          *
 *     * notice appear in all copies.  The University of California        *
 *     * makes no representations about the suitability of this            *
 *     * software for any purpose.  It is provided "as is" without         *
 *     * express or implied warranty.  Export of this software outside     *
 *     * of the United States of America may require an export license.    *
 *     *********************************************************************
 */

#ifndef	lint
static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/utils/stack.c,v 1.2 2010/06/24 12:37:58 tim Exp $";
#endif	/* not lint */

#include <stdio.h>

#include "utils/magic.h"
#include "utils/utils.h"
#include "utils/stack.h"
#include "utils/malloc.h"

bool stackCopyStr;

/*
 * ----------------------------------------------------------------------------
 * StackNew --
 *
 * Allocate and initialize a new Stack.
 *
 * Results:
 *	Returns a pointer to a newly heap-allocated and initialized
 *	stack, with its growth increment set to the specified
 *	size (in entries).
 *
 * Side effects:
 *	None.
 * ----------------------------------------------------------------------------
 */

Stack *
StackNew(sincr)
    int sincr;		/* Number of entries by which to grow storage area */
{
    Stack *stack;

    stack = (Stack *) mallocMagic((unsigned) (sizeof (Stack)));
    stack->st_incr = sincr;
    stack->st_body = (struct stackBody *) mallocMagic((unsigned) (stackSize(sincr)));
    stack->st_ptr = stack->st_body->sb_data;
    stack->st_body->sb_next = NULL;

    return (stack);
}

/*
 * ----------------------------------------------------------------------------
 * StackFree --
 *
 * Deallocate a Stack.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Deallocates all memory currently assigned to the Stack.
 *
 * ----------------------------------------------------------------------------
 */

void
StackFree(stack)
    Stack *stack;
{
    struct stackBody *stackp, *stacknext;

    for (stackp = stack->st_body; stackp != NULL; stackp = stacknext)
    {
	stacknext = stackp->sb_next;
	freeMagic((char *) stackp);
    }

    freeMagic((char *) stack);
}

/*
 * ----------------------------------------------------------------------------
 * StackPush --
 *
 * Push a new element on to a stack.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The argument stack is updated to reflect the new
 *	item placed upon it.
 *
 * ----------------------------------------------------------------------------
 */
void
StackPush(arg, stack)
    ClientData arg;
    Stack *stack;
{
    struct stackBody *bodyNew;

    if (stack->st_ptr >= &stack->st_body->sb_data[stack->st_incr])
    {
	bodyNew = (struct stackBody *) mallocMagic((unsigned) (stackSize(stack->st_incr)));
	bodyNew->sb_next = stack->st_body;
	stack->st_ptr = bodyNew->sb_data;
	stack->st_body = bodyNew;
    }

    *(stack->st_ptr++) = arg;
}

/*
 * ----------------------------------------------------------------------------
 * StackPop --
 *
 * Pop the top element from a Stack and return it.
 *
 * Results:
 *	Top element from stack.
 *	If the stack is already empty, returns NULL.
 *	Callers should probably avoid popping from an empty
 *	stack.
 *
 * Side effects:
 *	Updates the stack to reflect the result of popping its top.
 *
 * ----------------------------------------------------------------------------
 */

ClientData
StackPop(stack)
    Stack *stack;
{
    struct stackBody *bodyOld;

    if (stackBodyEmpty(stack))
    {
	bodyOld = stack->st_body;
	if (bodyOld->sb_next == NULL)
	    return ((ClientData) NULL);
	stack->st_body = bodyOld->sb_next;
	stack->st_ptr = &stack->st_body->sb_data[stack->st_incr];
	freeMagic((char *) bodyOld);
    }

    return (*--(stack->st_ptr));
}

/*
 * ----------------------------------------------------------------------------
 * StackLook --
 *
 * Return the top element from a Stack, but don't pop it off.
 *
 * Results:
 *	Top element from stack.
 *	If the stack is already empty, returns NULL.
 *
 * Side effects:
 *	None.
 *
 * ----------------------------------------------------------------------------
 */

ClientData
StackLook(stack)
    Stack *stack;
{
    struct stackBody *bodyNext;

    if (stackBodyEmpty(stack))
    {
	bodyNext = stack->st_body->sb_next;
	if (bodyNext == NULL)
	    return ((ClientData) NULL);
	return (bodyNext->sb_data[stack->st_incr - 1]);
    }

    return ( *(stack->st_ptr - 1) );
}

/*
 * ----------------------------------------------------------------------------
 *
 * StackEnum --
 *
 * 	Enumerate all elements on the stack.  Call the supplied function
 *	for each occurrence.
 *
 *	The supplied function is of the form:
 *		int func(stackItem, i, clientData)
 *		    ClientData stackItem;    Item put on the stack
 *		    int i;		     Index of the item on stack
 *		    ClientData cd;	     Points to whatever you want
 *	The function normally returns 0.  The enumeration terminates if it
 *	returns anything else.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	None.
 *
 * ----------------------------------------------------------------------------
 */
void
StackEnum(stack, func, cd)
    Stack    * stack;
    int     (* func)();
    ClientData cd;
{
    int i, j;
    struct stackBody * sb;

    i=1;
    for(sb=stack->st_body; sb!=(struct stackBody *) NULL; sb=sb->sb_next)
    {
	for(j=0; j<=stack->st_incr; j++)
	{
	    if( &(sb->sb_data[j]) == stack->st_ptr ) return;
	    if((*func)(sb->sb_data[j], i, cd)) return;
	    i++;
	}
    }
}

/*
 * ----------------------------------------------------------------------------
 *
 * StackCopy --
 *
 * 	Make a copy of a stack.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Memory may get allocated if the copystr parameter says to copy
 *	strings rather than pointers to them.
 *	dest gets a copy of src.  If dest is non-null, it gets freed.
 *
 * ----------------------------------------------------------------------------
 */
void
StackCopy(src, dest, copystr)
    Stack * src, ** dest;
    bool copystr;
{
    int stackCopyFn();

    stackCopyStr=copystr;
    if(*dest!=(Stack *) NULL)
	StackFree(*dest);
    if(src==(Stack *) NULL)
	*dest = src;
    else
    {
	*dest = StackNew(src->st_incr);
	StackEnum(src, stackCopyFn, (ClientData) * dest);
    }
}

/*ARGSUSED*/
int
stackCopyFn(stackItem, i, cd)
    ClientData stackItem;
    int i;
    ClientData cd;
{
    if(stackCopyStr)
	StackPush((ClientData) StrDup((char **) NULL, (char *)stackItem), (Stack *) cd);
    else
	StackPush(stackItem, (Stack *) cd);
    return(0);
}
