www.pudn.com > gandalf.1.zip > gan_err.c


/**
 *
 * File:                $RCSfile: gan_err.c,v $
 * Module:        Exception module
 * Part of:        Gandalf Library
 *
 * Revision:        $Revision: 1.11 $
 * Last edited:        $Date: 2005/10/18 16:30:46 $
 * Author:        $Author: pm $
 * Copyright:        (c) 2000 Industrial Research Limited
 */

/* This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with this library; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include 
#include 
#include 
#ifdef _MSC_VER
#include 
#endif /* #ifdef _MSC_VER */

/**
 * \addtogroup Common
 * \{
 */

/**
 * \defgroup CommonError Error Handling
 * \{
 */

/*
 * \brief Default error reporter.
 * \return No value.
 *
 * Typically the application should provide its own error reporter.
 */
static void
 gan_err_default_reporter(void)
{
   int i, n;                                /* Loop counter */
    
   const char * func_name;
   int          err_code;
   const char * file_name;
   int          line_number;
   const char * message;
   int          number;

   n = gan_err_get_error_count();
   for ( i = 1; i<=n; i++ )
   {
      if ((gan_err_get_error(i, &func_name, &err_code, &file_name, &line_number, &message, &number ) != GAN_EC_OK))
         fprintf(stderr, "**** gan_err_get_error unsuccessful\n");
        
      fprintf(stderr, "\nError number %d\n", i );
      fprintf(stderr, "func_name   = %s\n", func_name );
      fprintf(stderr, "err_code    = %d\n", err_code );
      fprintf(stderr, "file_name   = %s\n", file_name );
      fprintf(stderr, "line_number = %d\n", line_number );
      fprintf(stderr, "message     = %s\n", message );
   }

   fprintf(stderr, "gan_err_default_reporter()\n");
}

/*  Pointer to current error reporting function. This is setup using
 * gan_err_set_reporter(), and typically invoked using gan_err_report().
 *
 * Default reporter is gan_err_default_reporter().
 */
static Gan_ErrorReporterFunc
               gan_err_current_reporter = gan_err_default_reporter;


/* Flag indicating Trace Mode - see GAN_ERR_TRACE_X in gan_exception.h for
 * details. Default action is to set trace off. Permitted values are:
 * GAN_ERR_TRACE_OFF or GAN_ERR_TRACE_ON.
 */
static Gan_TraceMode gan_err_trace_mode = GAN_ERR_TRACE_OFF; 


/* The error trace */
/*  Statically allocate last and 2nd to last records for error trace */
static Gan_ErrorTrace record_last = { NULL, GAN_ET_YES, GAN_ET_NO,
                                      GAN_ET_YES, NULL,
                                      GAN_EC_DFT_SPARE, NULL, 0, NULL };
static Gan_ErrorTrace record_2nd_last = { &record_last,  GAN_ET_YES, GAN_ET_NO,
                                          GAN_ET_YES, NULL,
                                          GAN_EC_DFT_SPARE, NULL, 0, NULL };

/* Address of error trace (i.e. top of LIFO stack) */
static Gan_ErrorTrace * gan_err_trace_top = &record_2nd_last;

/**
 * \brief Installs an error reporter
 * \param app_error_reporter Pointer to an application defined function
 * \return Pointer to previously installed error reporter, if successful.
 *          \c NULL otherwise.
 *
 * This exception module allows an application defined function to be called
 * when an error is reported using gan_err_report().
 * gan_err_set_reporter() installs this error reporter.
 * \a app_error_reporter should be a pointer to an application defined function
 * to access error details, or the macros:
 *    - GAN_ERR_DFL To set default error reporter
 *    - GAN_ERR_IGN To ignore error reporting
 *
 * If GAN_ERR_DFL is provided, then the default error reporter (see below) is
 * installed.
 * \warning The default error reporter simply writes a message to \c stderr.
 *          The exception module is initialised to use the default error
 *          reporter.
 * \sa gan_err_get_reporter().
 * \note Other functions that invoke the current error handler must check first
 *       if it is set to #GAN_ERR_IGN. If so, ignore the invocation.
 *       gan_err_current_reporter() is module scope variable.
 */
Gan_ErrorReporterFunc gan_err_set_reporter(
                                    Gan_ErrorReporterFunc app_error_reporter )
{
   /*  Buffer old handler, so that it can be returned */
   Gan_ErrorReporterFunc gan_err_temp_reporter = gan_err_current_reporter; 
    
   /* Set reporter, noting default (GAN_ERR_DFL) or ignore
      (GAN_ERR_IGN) modes */
   if ( app_error_reporter == GAN_ERR_DFL )
      gan_err_current_reporter = gan_err_default_reporter;
   else if ( app_error_reporter == GAN_ERR_IGN )
      gan_err_current_reporter = GAN_ERR_IGN;        
   else
      gan_err_current_reporter = app_error_reporter; /* Set reporter */

   return gan_err_temp_reporter;
}

/**
 * \brief Returns current error reporter.
 * \return Pointer to current error reporter, or #GAN_ERR_DFL or #GAN_ERR_IGN.
 *
 * Returns current error reporter.
 *
 * \sa gan_err_set_reporter().
 */
Gan_ErrorReporterFunc
 gan_err_get_reporter(void)
{
   return gan_err_current_reporter;
}


#ifdef _MSC_VER
static CRITICAL_SECTION lpCriticalSection;
static Gan_Bool bInitialisedCriticalSection = GAN_FALSE;
#endif /* #ifdef _MSC_VER */


/**
 * \brief Registers occurence of an error.
 * \param func_name    Name of function in which error occurs
 * \param err_code     Numeric code of error
 * \param file_name    Name of file in which error occurs
 * \param line_number  Line in file at which error occurs
 * \param message      Message string describing error
 * \parem number       Number to attach to error, default 0
 *
 * \return The error number of error registered. #GAN_EC_DFT_DEEP_ERROR if a
 *         deep error occurs, \a err_code otherwise.
 *                
 * Registers occurence of an error. Intended to be called at the lowest
 * function level immediately after the occurence of an error, and called at
 * every level of the function call stack during unwinding, until the error is
 * handled, or it unwinds into a function level where a different error
 * handling mechanism is used.
 *
 * If the trace mode is #GAN_ERR_TRACE_OFF, this function causes the current
 * error reporter to called immediately. If the trace mode is
 * #GAN_ERR_TRACE_ON, this function causes the details of the error to be
 * placed onto an error trace. The error details are reported in "batch" at a
 * later time upon invokation of gan_err_report().
 *
 * \warning #GAN_EC_DFT_DEEP_ERROR is registered in the top record of the error
 *          trace if a deep error occurs. The error requested to be registered
 *          is placed in the second top record of the error trace. This error
 *          may be missing the message string, because it may have been the
 *          process of allocating memory for this string that caused the deep
 *          error to occur.
 *
 * This function is typically called using the macro gan_err_register().
 *
 * \sa gan_err_register() (macro), gan_err_set_trace().
 * \note If trace mode is off then call error reporter immediately, otherwise
 *       push error details onto error trace.
 */
int
 gan_err_register_fileline ( const char *func_name,
                             int         err_code,
                             const char *file_name,
                             int         line_number,
                             const char *message,
                             int         number )
{
   Gan_ErrorTrace *atrace = NULL;
   int             the_err_code = GAN_EC_FAIL; /* Registered error code */
#ifdef _MSC_VER
   /* use thread-safe access to static global gan_err_trace_top */
   if(!bInitialisedCriticalSection)
   {
	   InitializeCriticalSection(&lpCriticalSection);
	   bInitialisedCriticalSection = GAN_TRUE;
   }

   EnterCriticalSection(&lpCriticalSection);
#endif /* #ifdef _MSC_VER */

   /* When trace mode is off, still use the trace, but flush it before and
    * after use.
    */
   if ( gan_err_trace_mode == GAN_ERR_TRACE_OFF )
      gan_err_trace_top = gan_et_flush_trace(gan_err_trace_top);

   gan_err_trace_top = gan_et_push ( gan_err_trace_top, func_name, err_code, file_name, line_number, message, number );

   /*  Note the registered error code. Could be a deep error */
   if ( (atrace = gan_et_get_record_first(gan_err_trace_top)) != NULL )
      the_err_code = atrace->err_code;

   /*  Report immediately. Use trace as interim storage. Flush afterwards */
   if ( gan_err_trace_mode == GAN_ERR_TRACE_OFF )
   {
      gan_err_report();
      gan_err_trace_top = gan_et_flush_trace(gan_err_trace_top);
      /*  This flush is belts and braces. The primary flush is at the head
       * of this function.
       */
   }

#ifdef _MSC_VER
   LeaveCriticalSection(&lpCriticalSection);
#endif /* #ifdef _MSC_VER */
   return the_err_code;
}

/**
 * \brief Invokes current error reporter.
 * \return No value.
 *
 * Invokes current error reporter. That's all. It is the responsibility of the
 * error reporter to traverse the error trace, read the details of each error,
 * and to report those details in whatever application specific manner it
 * chooses.
 *
 * This function is automatically called from gan_err_register_fileline() when
 * trace mode is OFF.
 *               
 * \warning The error reporter will not be called if
 *          gan_err_set_reporter(#GAN_ERR_IGN) has been called.
 *          The default error reporter is called when
 *           gan_err_set_reporter(#GAN_ERR_DFL);
 * \sa gan_err_set_trace(), gan_err_set_reporter().
 */
void
 gan_err_report(void)
{
   if (gan_err_current_reporter == GAN_ERR_DFL )
      gan_err_default_reporter();
   else if ( gan_err_current_reporter != GAN_ERR_IGN )
      gan_err_current_reporter();
    
   return;
} /* gan_err_report() */

/**
 * \brief Enable or disable use of trace to store error details.
 * \param trace_mode Whether to switch trace mode on or off
 * \return No value.
 *
 * A trace is a data structure that stacks error details for subsequent
 * reporting (activated by gan_err_report()). If trace is disabled by
 * passing \a trace_mode as #GAN_ERR_TRACE_OFF, then errors are reported
 * immediately upon being registered i.e. when gan_err_register() is called.
 * Otherwise trace mode is switched on by passing #GAN_ERR_TRACE_ON.
 *
 * \warning Trace mode is initialised to #GAN_ERR_TRACE_OFF.
 *          Any non-zero trace_mode is assumed equivalent to
 *          #GAN_ERR_TRACE_OFF.
 *          When trace is turned off, the trace is flushed immediately.
 * \sa gan_err_report(), gan_err_register().
 */
void
 gan_err_set_trace( Gan_TraceMode trace_mode )
{
#ifdef _MSC_VER
   /* use thread-safe access to static global gan_err_trace_top */
   if(!bInitialisedCriticalSection)
   {
	   InitializeCriticalSection(&lpCriticalSection);
	   bInitialisedCriticalSection = GAN_TRUE;
   }

   EnterCriticalSection(&lpCriticalSection);
#endif /* #ifdef _MSC_VER */

   if ( trace_mode == GAN_ERR_TRACE_OFF )
      gan_err_trace_top = gan_et_flush_trace(gan_err_trace_top);
    
   gan_err_trace_mode = trace_mode;

#ifdef _MSC_VER
   LeaveCriticalSection(&lpCriticalSection);
#endif /* #ifdef _MSC_VER */

   return;
}


/**
 * \brief Flush all errors in trace.
 * \return No value.
 *
 * Flush all errors in error trace
 * \note Traverse error trace from start to finish deleting all error records
 *       (except two reserved ones which are transparent to this module).
 */
void
 gan_err_flush_trace( void )
{
#ifdef _MSC_VER
   /* use thread-safe access to static global gan_err_trace_top */
   if(!bInitialisedCriticalSection)
   {
	   InitializeCriticalSection(&lpCriticalSection);
	   bInitialisedCriticalSection = GAN_TRUE;
   }

   EnterCriticalSection(&lpCriticalSection);
#endif /* #ifdef _MSC_VER */

   gan_err_trace_top = gan_et_flush_trace(gan_err_trace_top);

#ifdef _MSC_VER
   LeaveCriticalSection(&lpCriticalSection);
#endif /* #ifdef _MSC_VER */
   return;
} /*  gan_err_flush_trace() */


/**
 * \brief Gets the number of errors in error trace.
 * \return Number of errors in error trace.
 *
 * Gets the number of errors in error trace.
 *
 * \sa gan_err_get_error().
 */
int
 gan_err_get_error_count( void )
{
   int iErrorCount;

#ifdef _MSC_VER
   /* use thread-safe access to static global gan_err_trace_top */
   if(!bInitialisedCriticalSection)
   {
	   InitializeCriticalSection(&lpCriticalSection);
	   bInitialisedCriticalSection = GAN_TRUE;
   }

   EnterCriticalSection(&lpCriticalSection);
#endif /* #ifdef _MSC_VER */

   iErrorCount = gan_et_get_record_count(gan_err_trace_top);

#ifdef _MSC_VER
   LeaveCriticalSection(&lpCriticalSection);
#endif /* #ifdef _MSC_VER */

   return iErrorCount;
} /* gan_err_get_error_count() */

/**
 * \brief Gets details of n-th error stored in the error trace.
 * \param n            Index of requested error [1..N]
 * \param func_name    Name of function in which error occurs
 * \param err_code     Numeric code of error
 * \param file_name    Name of file in which error occurs
 * \param line_number  Line in file at which error occurs
 * \param message      Message string describing error
 * \param number       Number attached to error
 * \return Status of n-th error.
 *
 * If any of above pointers are \c NULL, then those details are not returned.
 *
 * Gets details of \a n-th error. \a n=1 refers to the most recent error
 * registered in error trace. Usually gan_err_get_error_count() is called to
 * obtain the number of error records in the trace. Return values:
 *     - #GAN_EC_DFT_BAD_N Index '\a n' out of bounds,
 *     - #GAN_EC_DFT_EMPTY Error trace is empty (regardless of requested n)
 *     - #GAN_EC_OK        Otherwise.
 *
 * \warning The returned strings (\a func_name, \a file_name, \a message) are
 *          not guaranteed to exist at a later time, nor should they be
 *          modified in place. Therefore the calling function must either
 *          use the returned strings immediately or make copies.
 *
 *          Because the stack is numbered from the top (1=most
 *          recent) an arbitrary index i may refer to different
 *          error records at different times. However, index 1
 *          always refers to the most recent error.
 *            
 * \sa gan_err_get_error_count().
 */
int
 gan_err_get_error ( int          n,
                     const char **func_name,
                     int         *err_code,
                     const char **file_name,
                     int         *line_number,
                     const char **message,
                     int         *number )
{
   int count;
   int i;                                /* Loop counter */
   Gan_ErrorTrace *a_record = NULL;

#ifdef _MSC_VER
   /* use thread-safe access to static global gan_err_trace_top */
   if(!bInitialisedCriticalSection)
   {
	   InitializeCriticalSection(&lpCriticalSection);
	   bInitialisedCriticalSection = GAN_TRUE;
   }

   EnterCriticalSection(&lpCriticalSection);
#endif /* #ifdef _MSC_VER */

   if ( (count = gan_et_get_record_count(gan_err_trace_top)) < 1 )
      return GAN_EC_DFT_EMPTY;                /* Error trace is empty */
    
   /*  Boundary check on n */
   if ( (n < 1) || (n > count) )
      return GAN_EC_DFT_BAD_N;                /* Out of bounds, bye */

   for ( a_record = gan_et_get_record_first(gan_err_trace_top), i = 1;
         i < n; i++ )
      a_record = gan_et_get_record_next(a_record); /* Step to N-th error */

   if ( func_name != NULL )
      *func_name = a_record->func_name;

   if ( err_code != NULL )
      *err_code = a_record->err_code;

   if ( file_name != NULL )
      *file_name = a_record->file_name;
    
   if ( line_number != NULL )
      *line_number = a_record->line_number;
    
   if ( message != NULL )
      *message = a_record->message;

   if ( number != NULL )
      *number = a_record->number;

#ifdef _MSC_VER
   LeaveCriticalSection(&lpCriticalSection);
#endif /* #ifdef _MSC_VER */

   return GAN_EC_OK;
} /* gan_err_get_error() */

/**
 * \}
 */

/**
 * \}
 */