#include <ft2build.h>
#include FT_EXCEPT_H


  FT_BASE_DEF( void )
  ft_cleanup_stack_init( FT_CleanupStack  stack,
                         FT_Memory        memory )
  {
    stack->chunk = &stack->chunk_0;
    stack->top   = stack->chunk->items;
    stack->limit = stack->top + FT_CLEANUP_CHUNK_SIZE;
    stack->chunk_0.link = NULL;

    stack->memory = memory;
  }



  FT_BASE_DEF( void )
  ft_cleanup_stack_done( FT_CleanupStack  stack )
  {
    FT_Memory        memory = stack->memory;
    FT_CleanupChunk  chunk, next;

    for (;;)
    {
      chunk = stack->chunk;
      if ( chunk == &stack->chunk_0 )
        break;

      stack->chunk = chunk->link;

      FT_Free( chunk, memory );
    }

    stack->memory = NULL;
  }



  FT_BASE_DEF( void )
  ft_cleanup_stack_push( FT_CleanupStack  stack,
                         FT_Pointer       item,
                         FT_CleanupFunc   item_func,
                         FT_Pointer       item_data )
  {
    FT_CleanupItem  top;


    FT_ASSERT( stack && stack->chunk && stack->top );
    FT_ASSERT( item  && item_func );

    top = stack->top;

    top->item      = item;
    top->item_func = item_func;
    top->item_data = item_data;

    top ++;

    if ( top == stack->limit )
    {
      FT_CleanupChunk  chunk;

      chunk = FT_QAlloc( sizeof(*chunk), stack->memory );

      chunk->link  = stack->chunk;
      stack->chunk = chunk;
      stack->limit = chunk->items + FT_CLEANUP_CHUNK_SIZE;
      top          = chunk->items;
    }

    stack->top = top;
  }



  FT_BASE_DEF( void )
  ft_cleanup_stack_pop( FT_CleanupStack   stack,
                        FT_Int            destroy )
  {
    FT_CleanupItem  top;


    FT_ASSERT( stack && stack->chunk && stack->top );
    top = stack->top;

    if ( top == stack->chunk->items )
    {
      FT_CleanupChunk  chunk;

      chunk = stack->chunk;

      if ( chunk == &stack->chunk_0 )
      {
        FT_ERROR(( "cleanup.pop: empty cleanup stack !!\n" ));
        ft_cleanup_throw( stack, FT_Err_EmptyCleanupStack );
      }

      chunk = chunk->link;
      FT_QFree( stack->chunk, stack->memory );

      stack->chunk = chunk;
      stack->limit = chunk->items + FT_CLEANUP_CHUNK_SIZE;
      top          = stack->limit;
    }

    top --;

    if ( destroy )
      top->item_func( top->item, top->item_data );

    top->item      = NULL;
    top->item_func = NULL;
    top->item_data = NULL;

    stack->top = top;
  }



  FT_BASE_DEF( FT_CleanupItem )
  ft_cleanup_stack_peek( FT_CleanupStack  stack )
  {
    FT_CleanupItem   top;
    FT_CleanupChunk  chunk;


    FT_ASSERT( stack && stack->chunk && stack->top );

    top   = stack->top;
    chunk = stack->chunk;

    if ( top > chunk->items )
      top--;
    else
    {
      chunk = chunk->link;
      top   = NULL;
      if ( chunk != NULL )
        top = chunk->items + FT_CLEANUP_CHUNK_SIZE - 1;
    }
    return top;
  }



  FT_BASE_DEF( void )
  ft_xhandler_enter( FT_XHandler  xhandler,
                     FT_Memory    memory )
  {
    FT_CleanupStack  stack = FT_MEMORY__CLEANUP(memory);

    xhandler->previous = stack->xhandler;
    xhandler->cleanup  = stack->top;
    xhandler->error    = 0;
    stack->xhandler    = xhandler;
  }



  FT_BASE_DEF( void )
  ft_xhandler_exit( FT_XHandler  xhandler )
  {
    FT_CleanupStack  stack = FT_MEMORY__CLEANUP(memory);

    stack->xhandler    = xhandler->previous;
    xhandler->previous = NULL;
    xhandler->error    = error;
    xhandler->cleanup  = NULL;
  }



  FT_BASE_DEF( void )
  ft_cleanup_throw( FT_CleanupStack  stack,
                    FT_Error         error )
  {
    FT_XHandler  xhandler = stack->xhandler;

    if ( xhandler == NULL )
    {
      /* no exception handler was registered. this  */
      /* means that we have an un-handled exception */
      /* the only thing we can do is _PANIC_ and    */
      /* halt the current program..                 */
      /*                                            */
      FT_ERROR(( "FREETYPE PANIC: An un-handled exception occured. Program aborted" ));
      ft_exit(1);
    }

    /* cleanup the stack until we reach the handler's */
    /* starting stack location..                      */

    xhandler->error = error;
    longmp( xhandler->jump_buffer, 1 );
  }                    
