/*
 * nqs_generic/all-systems/debug.c
 * Runtime debugging support for NQS
 * 
 * Adapted from Sheffield-NQS v4 (and hence not as capable)
 * For use with Monsanto-NQS v3.50 or greater
 */

#include <nqs_generic/license.h>
  /* license information */

#include <syslog.h>
  /* syslogd support */
#include <stdio.h>
#include <stdlib.h>
#include <nqs_generic/debug.h>
#include <nqs_generic/autoconf.h>
#include <nqs_generic/proto.h>

/*
 * the debugging level variable ...
 */

unsigned int Debug = CONFIG_DEFAULT_DEBUG;

/*
 * stErrorTable - map UFCD_MESS macros to debugging information
 */

static UFCT_ErrorTable stErrorTable[] =
{
/* Message ---- Show -- Trace - Where - Syslog -------- Show -- Fatal? -*/
/* Prefix  ---- Level - Level - Level - Action -------- Author?        -*/
  { "AUTHOR",	0,	0,	0,	LOG_CRIT,	TRUE,	TRUE 	}, /* UFCD_MESS_AUTHOR */
  { "DATA",	0,	1,	0,	LOG_ERR,	FALSE,	TRUE	}, /* UFCD_MESS_DATA */
  { "RESOURCE", 0,	1,	0,	LOG_ERR,	FALSE,	TRUE	}, /* UFCD_MESS_RESOURCE */
  { "LOG",	0,	9,	4,	LOG_INFO,	FALSE,	FALSE	}, /* UFCD_MESS_INFO */
  { "DEBUG",	2,	6,	2,	LOG_DEBUG,	FALSE,	FALSE   }, /* UFCD_MESS_DEBUGHIGH */
  { "DEBUG",	3,	7,	3,	LOG_DEBUG,	FALSE,	FALSE	}, /* UFCD_MESS_DEBUGMEDIUM */
  { "DEBUG",	4,	8,	4,	LOG_DEBUG,	FALSE,	FALSE	}, /* UFCD_MESS_DEBUGLOW */
  { "TRACE",	5,	9,	5,	LOG_DEBUG,	FALSE,	FALSE	}, /* UFCD_MESS_TRACE */
  { "PANIC",	0,	0,	0,	LOG_CRIT,	FALSE,	TRUE	}  /* should never be seen */
};

/*
 * pstTraceStack - stack object for function trace
 */

static UFCT_TraceHandle *pstTraceStack = NULL;

/*
 * DEBUGGING - log a debugging message
 *
 * szFile		- source code filename
 * uiLine		- line number where the message was logged from
 * uiPriority		- is this a high, medium or low priority message?
 * szMessage		- format string for the message
 * ...			- parameters for the format string
 * 
 * NOTE:
 *   This function is NOT called DEBUG because this is commonly defined as
 *   macro to indicate that extra debugging code should be compiled in.
 */

void DEBUGGING (const char   *szFile,
	        unsigned int  uiLine,
		unsigned int  uiPriority,
	        const char   *szMessage,
	        ...)
{
  va_list vTmp;
  
  va_start(vTmp, szMessage);
  UFCF_DebugMessage(szFile, uiLine, uiPriority, szMessage, vTmp);
    /* handle the message */
  va_end(vTmp);
    /* this should always be reached */
}

/*
 * ERROR_AUTHOR - report an error by the author
 *
 * szFile		- source code file name
 * uiLine		- line number where the error was reported
 * szMessage		- format string for the message
 * ...			- parameters for the message
 */

void ERROR_AUTHOR (const char   *szFile,
		   unsigned int  uiLine,
		   const char   *szMessage,
		   ...)
{
  va_list vTmp;
    /* variable arguement handler */
  
  va_start(vTmp, szMessage);
  UFCF_DebugMessage (szFile, uiLine, UFCD_MESS_AUTHOR, szMessage, vTmp);
    /* handle the message */
  NEVER;
  va_end(vTmp);
    /* this line should never be reached, but it is here for completeness */
}

/*
 * ERROR_DATA - report an error with the program data
 *
 * szFile		- source code filename
 * uiLine		- line number where the error was reported
 * szMessage		- format string for the message
 * ...			- parameters for the message
 */

void ERROR_DATA (const char   *szFile,
		 unsigned int  uiLine,
		 const char   *szMessage,
		 ...)
{
  va_list vTmp;
  
  va_start(vTmp, szMessage);
  UFCF_DebugMessage(szFile, uiLine, UFCD_MESS_DATA, szMessage, vTmp);
    /* handle the message */
  va_end(vTmp);
    /* this should never be called, but we add it for completeness */
}

/*
 * ERROR_RESOURCE - report that something is missing
 *
 * szFile		- source code filename
 * uiLine		- line number where the error was reported
 * szMessage		- format string for the message
 * ...			- parameters for the format string
 */

void ERROR_RESOURCE (const char   *szFile,
		     unsigned int  uiLine,
		     const char   *szMessage,
		     ...)
{
  va_list vTmp;
  
  va_start(vTmp, szMessage);
  UFCF_DebugMessage (szFile, uiLine, UFCD_MESS_RESOURCE, szMessage, vTmp);
    /* handle the message */
  NEVER;
  va_end(vTmp);
}

/*
 * InitLogging - perform setup for the syslog connection
 */

void InitLogging (void)
{
  /*
   * the facility we log as comes from 'make configure'.
   */
  
  openlog("NQS", LOG_CONS | LOG_NDELAY | LOG_PERROR | LOG_PID, CONFIG_SYSLOG_FACILITY);
}

/*
 * LOG - log a message
 *
 * szFile		- source code filename
 * uiLine		- line number where the message was logged from
 * szMessage		- format string for the message
 * ...			- parameters for the format string
 */

void LOG (const char   *szFile,
	  unsigned int	uiLine,
	  const char   *szMessage,
	  ...)
{
  va_list vTmp;
  
  va_start(vTmp, szMessage);
  UFCF_DebugMessage(szFile, uiLine, UFCD_MESS_LOG, szMessage, vTmp);
    /* handle the message */
  va_end(vTmp);
    /* this should always be reached */
}

/*
 * TRACE - report a trace message
 *
 * szFile		- source code filename (__FILE__)
 * uiLine		- line number where the message was TRACE'd'
 * szMessage		- format string for the message
 * ...			- parameters for the format string
 */

void TRACE (const char   *szFile,
	    unsigned int  uiLine,
	    const char   *szMessage,
	    ...)
{
  va_list vTmp;
  
  va_start(vTmp, szMessage);
  UFCF_DebugMessage(szFile, uiLine, UFCD_MESS_TRACE, szMessage, vTmp);
  va_end(vTmp);
}

/*
 * UFCF_DebugMessage - handle a debugging message
 *
 * szFile		- source code filename (__FILE__)
 * uiLine		- line number where the message originated from
 * uiMessType		- what type of message we have
 * szMessage		- format string for the message
 * vParam		- parameters for the message
 */

void UFCF_DebugMessage (const char   *szFile,
			unsigned int  uiLine,
			unsigned int  uiMessType,
			const char   *szMessage,
			va_list       vParam)
{
  
  unsigned int uiSyslog = stErrorTable[uiMessType].uiSyslogAction;
    /* saves a lot of typing, makes the code easier to read */
  
  if (Debug >= stErrorTable[uiMessType].uiShowAtAll)
  {
  
    /* 
     * first off, is this a fatal error?
     * if so, we want to place a highly visible marker in the logs
     * telling the user that this is where the error report begins.
     */
  
    if (Debug >= stErrorTable[uiMessType].boFatal)
      UFCF_DebugSyslog(uiSyslog,GET_MESSAGE(UFCM_DBUG_STARTSHERE));
  
    /* do we want to output file and line number? */
    if ((Debug >= stErrorTable[uiMessType].uiShowWhere) ||
        (Debug >= stErrorTable[uiMessType].uiShowTrace))
    {
      UFCF_DebugSyslog(uiSyslog,"In file : %s\n", szFile);
      UFCF_DebugSyslog(uiSyslog,"At line : %ld\n", uiLine);
    }
  
    /* next, output the error message */
  
    UFCF_DebugSyslog  (uiSyslog,"Message : %s\n",stErrorTable[uiMessType].szPrefix);
    UFCF_DebugSyslog  (uiSyslog,"Content : ");
    UFCF_DebugvSyslog (uiSyslog,szMessage, vParam);
  
    /* do we want a function trace? */
  
    if (Debug >= stErrorTable[uiMessType].uiShowTrace)
    {
      UFCT_TraceHandle *pstHandle = pstTraceStack;
    
      while (pstHandle != NULL)
      {
        UFCF_DebugSyslog (uiSyslog,GET_MESSAGE(UFCM_DBUG_CALLEDFROM), 
			  pstHandle->szFunction, pstHandle->szFile, 
			  pstHandle->uiLine);
        pstHandle = pstHandle->pstNext;
      }
    }

    if (stErrorTable[uiMessType].boShowAuthor)
    {
      UFCF_DebugSyslog (uiSyslog,GET_MESSAGE(UFCM_ERRD_AUTHOR), GET_MESSAGE(UFCM_DBUG_AUTHOR));
    }
  
    if (stErrorTable[uiMessType].boFatal)
    {
      UFCF_DebugSyslog(uiSyslog, GET_MESSAGE(UFCM_DBUG_STOPSHERE));
      abort();
    }
  }
}

/*
 * UFCF_DebugSyslog - handle messages when using syslog
 *
 * uiSyslog		- syslog action value
 * szMessage		- format string for message
 * ...			- parameters for format string
 */

void UFCF_DebugSyslog (unsigned int uiSyslog, const char *szMessage, ...)
{
  va_list vTmp;
  
  va_start(vTmp, szMessage);
  UFCF_DebugvSyslog (uiSyslog, szMessage, vTmp);
  va_end (vTmp);
}

/*
 * UFCF_DebugvSyslog - handle messages when using syslog
 *
 * uiSyslog		- syslog action value
 * szMessage		- format string for message
 * vTmp			- parameter list for message
 */

void UFCF_DebugvSyslog (unsigned int uiSyslog, const char *szMessage, va_list vList)
{
  static char szBuffer[1024];
    /* FIXME: this is a nasty hack, and probably fails under real conditions */
  
  vsprintf(szBuffer, szMessage, vList);
  
  syslog (uiSyslog, szBuffer);
  fprintf(stderr,   szBuffer);
}

/*
 * UFCF_DebugStderr - handle messages when NOT using syslog
 */

void UFCF_DebugStderr (unsigned int uiSyslog, const char *szMessage, ...)
{
  va_list vTmp;
  
  va_start (vTmp, szMessage);
  UFCF_DebugvStderr(uiSyslog, szMessage, vTmp);
  va_end (vTmp);
}

/*
 * UFCF_DebugvStderr - handle messages when NOT using syslog
 */

void UFCF_DebugvStderr (unsigned int uiSyslog, const char *szMessage, va_list vList)
{
  static char szBuffer[1024];
    /* FIXME: another nasty hack */
  
  vsprintf(szBuffer,szMessage,vList);
  fprintf (stderr,  szBuffer);
}

/*
 * UFCF_EnterFunction - add a function to the trace stack
 */
    
void UFCF_EnterFunction (const char    *szFile,
			 unsigned int   uiLine,
			 const char    *szFunction)
{
  UFCT_TraceHandle *pstHandle = (UFCT_TraceHandle *) malloc(sizeof (UFCT_TraceHandle));
  
  Assert(szFile		!= NULL);
  Assert(szFunction	!= NULL);
  Assert(pstHandle	!= NULL);
  
  TRACE (szFile, uiLine, GET_MESSAGE(UFCM_DBUG_FUNCTIONENTER), szFunction);
  
  pstHandle->szFile     = (char *) szFile;
  pstHandle->uiLine     = uiLine;
  pstHandle->szFunction = (char *) szFunction;
    /* fill in the details */
  
  pstHandle->pstNext	= pstTraceStack;
  pstTraceStack		= pstHandle;
}

/*
 * UFCF_ExitFunction - remove a function from the trace stack
 */

void UFCF_ExitFunction(const char *szFile, unsigned long ulLine)
{
  UFCT_TraceHandle *pstHandle;
  
  pstHandle = pstTraceStack;
  Assert(pstHandle != NULL);
  
  pstTraceStack = pstTraceStack->pstNext;
  
  TRACE(szFile, ulLine, GET_MESSAGE(UFCM_DBUG_FUNCTIONEXIT), pstHandle->szFunction);
  
  free(pstHandle);
}
