/*++

Copyright (c) 2004, Intel Corporation                                                         
All rights reserved. This program and the accompanying materials                          
are licensed and made available under the terms and conditions of the BSD License         
which accompanies this distribution.  The full text of the license may be found at        
http://opensource.org/licenses/bsd-license.php                                            
                                                                                          
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,                     
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.             

Module Name:

  Exceptions.c

Abstract:

  Exception logging routines.

--*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h> // for memset()
#include "Exceptions.h"

//
// Max length of a saved exception message
//
#define MAX_EXCEPTION_MSG 200

//
// We use this structure to track exceptions thrown. We nest deeper on
// TryException() calls, and come back out on CatchException() calls.
// We save off the first exception message for a given exception level,
// but we save the count of how many were thrown.
//
typedef struct {
  int   ExceptionCount;
  char  ExceptionMsg[MAX_EXCEPTION_MSG];
} EXCEPTION_LOG;

static EXCEPTION_LOG  ExceptionLog[MAX_EXCEPTION_NESTING + 1];
static int            ExceptionLevel;

//
// Initialize our data and structures for tracking exceptions.
//
int
InitExceptions (
  VOID
  )
{
  ExceptionLevel = -1;
  memset ((char *) &ExceptionLog, 0, sizeof (ExceptionLog));
  return 0;
}
//
// This function replaces the _try() exception macro. It sets the
// nesting level.
//
int
TryException (
  VOID
  )
{
  //
  // Boost our exception level if we would not go out of range
  //
  ExceptionLevel++;
  if (ExceptionLevel >= MAX_EXCEPTION_NESTING) {
    fprintf (stderr, "ERROR: Max exception nesting level exceeded\n");
    ExceptionLevel--;
    return 1;
  }

  return 0;
}
//
// This function replaces the _catch() exception macro. It's used to decrement
// the nesting level and return any exeption error messages that were
// thrown at the current nesting level.
//
char *
CatchException (
  VOID
  )
{
  //
  // Return a pointer to exception message. NULL if no exceptions at this level
  //
  if (ExceptionLevel >= 0) {
    ExceptionLevel--;
    if (ExceptionLog[ExceptionLevel + 1].ExceptionMsg[0]) {
      return ExceptionLog[ExceptionLevel + 1].ExceptionMsg;
    } else {
      return NULL;
    }
  } else {
    fprintf (stderr, "ERROR: Invalid nesting level call to CatchException()\n");
    return NULL;
  }
}
//
// This function can be used to test for exceptions between the TryException()
// and CatchException() calls in a given function.
//
int
ExceptionThrown (
  VOID
  )
{
  return ExceptionLog[ExceptionLevel].ExceptionCount;
}
//
// This function replaces the _throw() exception macro. It saves off the
// given error message at the current exeption level nesting.
//
int
ThrowException (
  char *Msg
  )
{
  if (ExceptionLevel < 0) {
    //
    // fprintf (stderr, "ERROR: Exception thrown out of scope");
    // Haven't yet enabled handling of exceptions, so just emit the message.
    //
    fprintf (stderr, Msg);
    return 1;
  }
  //
  // Only log the first
  //
  if (ExceptionLog[ExceptionLevel].ExceptionMsg[0] == 0) {
    strncpy (ExceptionLog[ExceptionLevel].ExceptionMsg, Msg, MAX_EXCEPTION_MSG);
  }

  ExceptionLog[ExceptionLevel].ExceptionCount++;
  return 0;
}
