/*******************************************************************************
*
* MODULE: cterror.c
*
********************************************************************************
*
* DESCRIPTION: Error reporting for the ctlib
*
********************************************************************************
*
* $Project: /Convert-Binary-C $
* $Author: mhx $
* $Date: 2006/01/01 10:38:03 +0100 $
* $Revision: 19 $
* $Source: /ctlib/cterror.c $
*
********************************************************************************
*
* Copyright (c) 2002-2006 Marcus Holland-Moritz. All rights reserved.
* This program is free software; you can redistribute it and/or modify
* it under the same terms as Perl itself.
*
*******************************************************************************/

/*===== GLOBAL INCLUDES ======================================================*/

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>

/*===== LOCAL INCLUDES =======================================================*/

#include "cterror.h"
#include "util/memalloc.h"

#include "ucpp/cpp.h"
#include "ucpp/mem.h"

#include "cppreent.h"


/*===== DEFINES ==============================================================*/

#define INIT_CHECK                                                             \
          do {                                                                 \
            if( !initialized ) {                                               \
              fprintf(stderr, "FATAL: print functions have not been set!\n");  \
              abort();                                                         \
            }                                                                  \
          } while(0)

/*===== TYPEDEFS =============================================================*/

/*===== STATIC FUNCTION PROTOTYPES ===========================================*/

static CTLibError *error_new( enum CTErrorSeverity severity, void *str );
static void error_delete( CTLibError *error );
static void push_str( CParseInfo *pCPI, enum CTErrorSeverity severity, void *str );
static void push_verror( CParseInfo *pCPI, enum CTErrorSeverity severity,
                         const char *fmt, va_list *pap );

/*===== EXTERNAL VARIABLES ===================================================*/

#ifndef UCPP_REENTRANT
extern CParseInfo *g_current_cpi;

#define my_ucpp_ouch    ucpp_ouch
#define my_ucpp_error   ucpp_error
#define my_ucpp_warning ucpp_warning
#endif

/*===== GLOBAL VARIABLES =====================================================*/

/*===== STATIC VARIABLES =====================================================*/

static int initialized = 0;
static PrintFunctions F;

/*===== STATIC FUNCTIONS =====================================================*/

/*******************************************************************************
*
*   ROUTINE: error_new
*
*   WRITTEN BY: Marcus Holland-Moritz             ON: Nov 2003
*   CHANGED BY:                                   ON:
*
********************************************************************************
*
* DESCRIPTION:
*
*   ARGUMENTS:
*
*     RETURNS:
*
*******************************************************************************/

static CTLibError *error_new( enum CTErrorSeverity severity, void *str )
{
  CTLibError *perr;
  const char *string;
  size_t len;

  string = F.cstring( str, &len );
  AllocF( CTLibError *, perr, sizeof(CTLibError) );
  AllocF( char *, perr->string, len+1 );
  perr->severity = severity;
  strncpy( perr->string, string, len );
  perr->string[len] = '\0';

  return perr;
}

/*******************************************************************************
*
*   ROUTINE: error_delete
*
*   WRITTEN BY: Marcus Holland-Moritz             ON: Nov 2003
*   CHANGED BY:                                   ON:
*
********************************************************************************
*
* DESCRIPTION:
*
*   ARGUMENTS:
*
*     RETURNS:
*
*******************************************************************************/

static void error_delete( CTLibError *error )
{
  if( error ) {
    if( error->string )
      Free( error->string );
    Free( error );
  }
}

/*******************************************************************************
*
*   ROUTINE: push_str
*
*   WRITTEN BY: Marcus Holland-Moritz             ON: Nov 2003
*   CHANGED BY:                                   ON:
*
********************************************************************************
*
* DESCRIPTION:
*
*   ARGUMENTS:
*
*     RETURNS:
*
*******************************************************************************/

static void push_str( CParseInfo *pCPI, enum CTErrorSeverity severity, void *str )
{
  if( pCPI == NULL || pCPI->errorStack == NULL )
    F.fatalerr( str );

  LL_push( pCPI->errorStack, error_new( severity, str ) );
}

/*******************************************************************************
*
*   ROUTINE: push_verror
*
*   WRITTEN BY: Marcus Holland-Moritz             ON: Nov 2003
*   CHANGED BY:                                   ON:
*
********************************************************************************
*
* DESCRIPTION:
*
*   ARGUMENTS:
*
*     RETURNS:
*
*******************************************************************************/

static void push_verror( CParseInfo *pCPI, enum CTErrorSeverity severity,
                         const char *fmt, va_list *pap )
{
  void *str = F.newstr();
  F.vscatf( str, fmt, pap );
  push_str( pCPI, severity, str );
  F.destroy( str );
}

/*===== FUNCTIONS ============================================================*/

/*******************************************************************************
*
*   ROUTINE: set_print_functions
*
*   WRITTEN BY: Marcus Holland-Moritz             ON: Mar 2002
*   CHANGED BY:                                   ON:
*
********************************************************************************
*
* DESCRIPTION:
*
*   ARGUMENTS:
*
*     RETURNS:
*
*******************************************************************************/

void set_print_functions( PrintFunctions *pPF )
{
  if( pPF->newstr   == NULL ||
      pPF->destroy  == NULL ||
      pPF->scatf    == NULL ||
      pPF->vscatf   == NULL ||
      pPF->cstring  == NULL ||
      pPF->fatalerr == NULL ) {
    fprintf( stderr, "FATAL: all print functions must be set!\n" );
    abort();
  }

  F = *pPF;
  initialized = 1;
}

/*******************************************************************************
*
*   ROUTINE: pop_all_errors
*
*   WRITTEN BY: Marcus Holland-Moritz             ON: Nov 2003
*   CHANGED BY:                                   ON:
*
********************************************************************************
*
* DESCRIPTION:
*
*   ARGUMENTS:
*
*     RETURNS:
*
*******************************************************************************/

void pop_all_errors( CParseInfo *pCPI )
{
  LL_flush( pCPI->errorStack, (LLDestroyFunc) error_delete );
}

/*******************************************************************************
*
*   ROUTINE: push_error
*
*   WRITTEN BY: Marcus Holland-Moritz             ON: Nov 2003
*   CHANGED BY:                                   ON:
*
********************************************************************************
*
* DESCRIPTION:
*
*   ARGUMENTS:
*
*     RETURNS:
*
*******************************************************************************/

void push_error( CParseInfo *pCPI, const char *fmt, ... )
{
  va_list ap;
  INIT_CHECK;
  va_start( ap, fmt );
  push_verror( pCPI, CTES_ERROR, fmt, &ap );
  va_end( ap );
}

/*******************************************************************************
*
*   ROUTINE: push_warning
*
*   WRITTEN BY: Marcus Holland-Moritz             ON: Nov 2003
*   CHANGED BY:                                   ON:
*
********************************************************************************
*
* DESCRIPTION:
*
*   ARGUMENTS:
*
*     RETURNS:
*
*******************************************************************************/

void push_warning( CParseInfo *pCPI, const char *fmt, ... )
{
  va_list ap;
  INIT_CHECK;
  va_start( ap, fmt );
  push_verror( pCPI, CTES_WARNING, fmt, &ap );
  va_end( ap );
}

/*******************************************************************************
*
*   ROUTINE: fatal_error
*
*   WRITTEN BY: Marcus Holland-Moritz             ON: Nov 2003
*   CHANGED BY:                                   ON:
*
********************************************************************************
*
* DESCRIPTION:
*
*   ARGUMENTS:
*
*     RETURNS:
*
*******************************************************************************/

void fatal_error( const char *fmt, ... )
{
  va_list ap;
  void *str;

  INIT_CHECK;
  va_start( ap, fmt );
  str = F.newstr();
  F.vscatf( str, fmt, &ap );
  va_end( ap );

  F.fatalerr( str );
}

/*******************************************************************************
*
*   ROUTINE: my_ucpp_ouch
*
*   WRITTEN BY: Marcus Holland-Moritz             ON: Mar 2002
*   CHANGED BY:                                   ON:
*
********************************************************************************
*
* DESCRIPTION:
*
*   ARGUMENTS:
*
*     RETURNS:
*
*******************************************************************************/

void my_ucpp_ouch( pUCPP_ char *fmt, ... )
{
  va_list ap;
  void *str;

  INIT_CHECK;

  va_start( ap, fmt );
  str = F.newstr();
  F.scatf( str, "%s: (FATAL) ", r_current_filename );
  F.vscatf( str, fmt, &ap );
  va_end( ap );

  F.fatalerr( str );
}

/*******************************************************************************
*
*   ROUTINE: my_ucpp_error
*
*   WRITTEN BY: Marcus Holland-Moritz             ON: Mar 2002
*   CHANGED BY:                                   ON:
*
********************************************************************************
*
* DESCRIPTION:
*
*   ARGUMENTS:
*
*     RETURNS:
*
*******************************************************************************/

void my_ucpp_error( pUCPP_ long line, char *fmt, ... )
{
  va_list ap;
  void *str;

  INIT_CHECK;

  va_start( ap, fmt );

  str = F.newstr();

  if( line > 0 )
    F.scatf( str, "%s, line %ld: ", r_current_filename, line );
  else if( line == 0 )
    F.scatf( str, "%s: ", r_current_filename );

  F.vscatf( str, fmt, &ap );

  if( line >= 0 ) {
    struct stack_context *sc = report_context( aUCPP );
    size_t i;

    for( i = 0; sc[i].line >= 0; i++ )
      F.scatf( str, "\n\tincluded from %s:%ld",
               sc[i].long_name ? sc[i].long_name : sc[i].name,
               sc[i].line );

    freemem( sc );
  }

  va_end( ap );

#ifdef UCPP_REENTRANT
  push_str( cpp->callback_arg, CTES_ERROR, str );
#else
  push_str( g_current_cpi, CTES_ERROR, str );
#endif

  F.destroy( str );
}

/*******************************************************************************
*
*   ROUTINE: my_ucpp_warning
*
*   WRITTEN BY: Marcus Holland-Moritz             ON: Mar 2002
*   CHANGED BY:                                   ON:
*
********************************************************************************
*
* DESCRIPTION:
*
*   ARGUMENTS:
*
*     RETURNS:
*
*******************************************************************************/

void my_ucpp_warning( pUCPP_ long line, char *fmt, ... )
{
  va_list ap;
  void *str;

  INIT_CHECK;

  va_start( ap, fmt );

  str = F.newstr();

  if( line > 0 )
    F.scatf( str, "%s, line %ld: (warning) ",
             r_current_filename, line);
  else if (line == 0)
    F.scatf( str, "%s: (warning) ", r_current_filename);
  else
    F.scatf( str, "(warning) ");

  F.vscatf( str, fmt, &ap );

  if( line >= 0 ) {
    struct stack_context *sc = report_context( aUCPP );
    size_t i;

    for( i = 0; sc[i].line >= 0; i++ )
      F.scatf( str, "\n\tincluded from %s:%ld",
               sc[i].long_name ? sc[i].long_name : sc[i].name,
               sc[i].line );
    freemem( sc );
  }

  va_end( ap );

#ifdef UCPP_REENTRANT
  push_str( cpp->callback_arg, CTES_WARNING, str );
#else
  push_str( g_current_cpi, CTES_WARNING, str );
#endif

  F.destroy( str );
}