#include "copyright.h"
/****************************************************************** 
 *
 * errclass.c --
 *
 *     Report errors in a consistent manner.
 *
 *
 *
 * $Author: mstoufer $
 * $Source: /home/g2/src/projects/NetLogger/src/nlv/errclass.c,v $
 * $RCSfile: errclass.c,v $
 * $Revision: 1.1.1.1 $
 * $Locker:  $
 * $Date: 2000/12/05 19:52:32 $
 *
 * Modification history
 * --------------------
 * 8/26/1999 : Made the messages a little less intimidating by
 *             removing the [x/y] and error codes.
 *
 ****************************************************************** 
 */

#include "errclass.h"

/* RCS identification */
static char rcsid[] = "$Id: errclass.c,v 1.1.1.1 2000/12/05 19:52:32 mstoufer Exp $";

/*========= MACROS ===================================================*/

#define LOCK_MUTEX(m) if ( 0 != pthread_mutex_lock( (m) ) ) \
			fprintf( stderr, "Locking mutex failed\n" );
#define UNLOCK_MUTEX(m) if ( 0 != pthread_mutex_unlock( (m) ) ) \
			fprintf( stderr, "Unlocking mutex failed\n" );

/*========= DATA =====================================================*/

#define T Nlv_err
struct T {
  int *hadErr;
  int numErr;
  int erange[2];
  int wrange[2];
  int dbg;
  pthread_mutex_t mutex;
};

#define FILE_ERR_CODE 700

/*========= FUNCTIONS ================================================*/

/*
 *----------------------------------------------------------------------
 *
 * New -- Allocate new object.
 *
 * Calls    :
 *
 * Algorithm: A default error-space is used:
 *             num = 4000
 *             0..num/2-1   = Errors
 *             num/2..num-1 = Warnings
 *
 *----------------------------------------------------------------------
 */
T Nlv_errNew()
{
  T this = (T)Nlv_Malloc( sizeof(struct Nlv_err) );
  this->numErr = 4000;
  this->hadErr = (int*)malloc( this->numErr * sizeof(int));
  memset( this->hadErr, 0, ( this->numErr * sizeof(int)));
  this->erange[0] = 0;
  this->erange[1] = this->numErr / 2;
  this->wrange[0] = NLV_WARN;
  this->wrange[1] = NLV_WARN + this->numErr/2;
  this->dbg       = 0;
  if ( pthread_mutex_init( &this->mutex, NULL ) != 0 ) {
    fprintf( stderr,"Error: While allocating new error reporting object, "
                    "pthread_mutex_init() failed. Aborting.\n");
    exit( -1 );
  }
  return this;
}

/*
 *----------------------------------------------------------------------
 *
 * PrintString -- Wrapper that handles common-case error reporting
 *                with a single function call.
 *
 *                Line number, level, repetitions, and code are all
 *                set to a default.
 *
 * Calls  : Nlv_errReport
 *
 *----------------------------------------------------------------------
 */

void  Nlv_errPrintString ( T           this,
                           const char *func,
                           char       *msg )
{
  Nlv_EInfo errInfo;

  errInfo.code = 100;
  errInfo.reps = 0;
  errInfo.file = func;
  errInfo.lineNum = 1;
  Nlv_errReport( this, &errInfo, msg );
}

/*
 *----------------------------------------------------------------------
 *
 * FatalTcl -- Report a fatal Tcl error and exit.
 *
 * Calls  : Nlv_errReport, exit
 *
 *----------------------------------------------------------------------
 */

void Nlv_errFatalTcl( T this, Tcl_Interp *interp, char *command )
{
  char buf[1024];
  Nlv_EInfo errInfo;

  if ( ! command ) {
    strcpy(buf,"Tcl command result");
  }
  else {
    sprintf( buf, "Tcl command '%s' result",command);
  }
  errInfo.code = 1000;
  errInfo.reps = 0;
  errInfo.file = buf;
  errInfo.lineNum = 1;
  Nlv_errReport( this, &errInfo, Tcl_GetStringResult(interp) );

  exit(1);
}

  
  

/*
 *----------------------------------------------------------------------
 *
 * Report -- Report errors or warnings.
 *
 * Calls    :  va_start, va_end
 *
 * Algorithm: Warnings and errors are positive codes,
 *            Debugging msgs are negative codes.
 *
 *            Printf section borrowed from 'minprintf' example
 *            from K&R's "C Programming Language", p.156
 *
 *----------------------------------------------------------------------
 */

void Nlv_errReport( T this, Nlv_EInfo *info, char *fmtMsg, ... )
{
  va_list ap;
  int   code_idx;
  int   c;
  int   dbg;
  char  *p;
  char  *sval;
  int    ival;
  double dval;
  long   lval;

  LOCK_MUTEX( &(this->mutex) );

  dbg = info->code < 0 ? 1 : 0;

  /* Above current debug level? */
  if ( dbg && info->code >= this->dbg ) {
    goto bottom;
  }
  
  if ( dbg ) {
   fprintf(stderr, "Debug: " );
  }
  else if ( info->code >= this->erange[0] &&
    info->code <= this->erange[1] ) {
    code_idx = info->code - this->erange[0];
    c = ( code_idx < this->numErr ) ? code_idx : 0;
    if ( info->reps > 0 && this->hadErr[c] >= info->reps )
       goto bottom;
    /* fprintf(stderr, "[%d/%d] Error %d: ", this->hadErr[c]+1, info->reps,c ); */
    fprintf(stderr, "Error: ");
  }
  else if ( info->code >= this->wrange[0] &&
      info->code <= this->wrange[1] ) {
     code_idx = info->code - this->wrange[0] + 
                 ( this->wrange[1] - this->wrange[0] );
      c = ( code_idx < this->numErr ) ? code_idx : 0;
      if ( info->reps > 0 && this->hadErr[c] >= info->reps )
        goto bottom;
      /* fprintf(stderr, "[%d/%d] Warning %d: ",this->hadErr[c]+1, info->reps,c); */
      fprintf(stderr, "Warning: ");
  }
  else {
     fprintf( stderr, "Unknown error (%d):\n", info->code );
     c = -1;
  }
  va_start( ap, fmtMsg );
  for ( p = fmtMsg; *p; p++ ) {
      if ( *p != '%' ) {
	fputc( *p, stderr );
	continue;
      }
      switch( *++p ) {
      case 'd' :
	ival = va_arg( ap, int );
	fprintf( stderr, "%d", ival );
	break;
      case 'f' :
	dval = va_arg( ap, double );
	fprintf( stderr, "%f", dval );
	break;
      case 'l' :
	lval = va_arg( ap, long );
	fprintf( stderr, "%ld", lval );
	break;
      case 's' :
	for ( sval = va_arg( ap, char * ); *sval; sval++ )
	  fputc( *sval, stderr );
	break;
      default:
	fputc( *p, stderr );
	break;
      }
    }
    va_end( ap );
    if ( dbg &&
         info->file    != NULL &&
	 info->lineNum >= 0 ) {
       fprintf( stderr, " -- %s:%d\n", info->file, info->lineNum );
    }
    else
      fprintf( stderr,"\n" );

    if ( !dbg &&
	 c >= 0 )
      this->hadErr[c]++;
  bottom:
    UNLOCK_MUTEX( &(this->mutex) );
}

/*
 *----------------------------------------------------------------------
 *
 * SetDebug -- Set debug level. 0 = All, < 0 = increasingly less.
 *
 * Calls    :
 *
 * Algorithm: Non-negative numbers are ignored.
 *
 *----------------------------------------------------------------------
 */
void Nlv_errSetDebug( T this, int lvl )
{
  if ( lvl <= 0 )
    this->dbg = lvl;
}

#undef T
#undef LOCK_MUTEX
#undef UNLOCK_MUTEX
#undef FILE_ERR_CODE
