/************************************************************************/
/* SISCO SOFTWARE MODULE HEADER *****************************************/
/************************************************************************/
/*   (c) Copyright Systems Integration Specialists Company, Inc.,	*/
/*		1993-2008, All Rights Reserved 				*/
/*									*/
/* MODULE NAME : slog.c							*/
/* PRODUCT(S)  : SLOG							*/
/*									*/
/* MODULE DESCRIPTION : 						*/
/*									*/
/* GLOBAL FUNCTIONS DEFINED IN THIS MODULE :				*/
/*									*/
/* MODIFICATION LOG :							*/
/*  Date     Who   Rev			Comments			*/
/* --------  ---  ------   -------------------------------------------	*/
/* 03/27/08  EJV     61    Use S_MAX_PATH instead of MAX_PATH.		*/
/*                         slog_get_index_file_name: added destLen param*/
/*                         and changed to return result.		*/
/* 02/06/08  EJV     60    Moved MAX_PATH define to sysincs.h		*/
/*			   _slogSetTimeTextElapsed _WIN32 only.		*/
/* 12/14/07  DSF     59    Added sNonStandardLogMode to support .NET	*/
/*			   Logger class					*/
/* 11/16/07  MDE     58    Fixed LINUX compile problems			*/
/* 10/23/07  MDE     57    Added ElapsedTime option for Windows		*/
/* 01/15/07  EJV     56    Chg S_LOCK_RESOURCES to S_LOCK_UTIL_RESOURCES*/
/* 01/08/07  EJV     55    slogIpcStop: added lc param.			*/
/* 09/13/06  DSF     54    No calls to ExceptionReport			*/
/* 08/18/06  JRB     53    Chk _slog_remote_fun along with other ptrs.	*/
/* 08/02/06  EJV     52    Corr: chg logMissed to ST_INT to avoid	*/
/*                           repeated logging when IPC_LOG_EN is set.	*/
/* 06/20/06  RLH     51    Added parameter checks and other robustness  */
/*                         features to _slogXML                         */
/* 03/14/06  CRM     50    Added _slogXML to log XML strings		*/
/* 02/13/06  DSF     49    Migrate to VS.NET 2005			*/
/* 01/23/06  EJV     48    doSlog: clarified buf overrun log for WIN32.	*/
/* 11/01/05  EJV     47    slog_end: added S_LOCK_RESOURCES.		*/
/* 10/31/05  MDE     46    Tweaked slog_end to stop IPC, disable	*/
/* 10/31/05  MDE     45    Default logging now disabled, log missed	*/
/* 10/21/05  MDE     44    Fixed slog_end path size			*/
/* 08/10/05  MDE     43    Added slog_start, slog_end			*/
/* 08/02/05  MDE     42    Fixed compile warning			*/
/* 05/23/05  EJV     40    doSlog corr: replaced LOG_IPC_EN with 	*/
/*			     LOG_IPC_LISTEN_EN || LOG_IPC_CALL_EN	*/
/*			   Moved sock_debug_sel to gensock2.c		*/
/*			   Moved   gs_debug_sel to glbsem.c		*/
/* 04/21/05  EJV     39    doSlog: use vsnprintf on Linux;		*/
/*			    Reworked the code assembling log in msg_buf.*/
/* 02/23/05  JRB     38    slogSetTimeText: fix for VXWORKS.		*/
/* 01/26/05  JRB     37    Move clnp_debug_sel to lean_var.c		*/
/* 01/24/05  MDE     36    Fixed tweak					*/
/* 01/20/05  MDE     35    Minor tweak for fileName & logtype checks	*/
/* 11/22/04  JRB     34    Add & use slog_max_msg_size_set funct.	*/
/*			   Ignore sl_max_msg_size if chged after first log.*/
/*			   Del static slog_buf, use new lc->msg_buf.	*/
/*			   slogDelBuf: add (LOG_CTRL *) arg.		*/
/* 08/04/04  EJV     33    Del slogTime, slogMs global variables.	*/
/* 07/09/04  EJV     32    All systems: one time/date format in slog hdr*/
/* 06/24/04  DSF     31    For Windows, log milliseconds		*/
/* 05/18/04  MDE     30    Removed LOG_IPC_SUPPORT #ifdef's		*/
/* 05/13/04  EJV     29    Added slogSetHdr, slogHdr.			*/
/* 03/11/04  GLB     28    Remove "thisFileName"                    	*/
/* 02/10/04  KCR     27    Added slogDelBuf()                           */
/* 01/12/04  EJV     26    Moved sock_debug_sel from gensock2.c		*/
/* 10/24/03  JRB     25    Move gs_debug_sel from glbsem.c to here.	*/
/*			   Move clnp_debug_sel to here.			*/
/* 10/13/03  EJV     24    Del MSOS2 (old), _WINDOWS.			*/
/* 05/07/03  DSF     23    Added support for sErrLogCtrl		*/
/* 03/28/02  EJV     22    vnsprintf: use it on Tru64 UNIX v5.0 and up	*/
/* 02/11/02  DSF     21    Call ExpRaiseDebugException () to log the 	*/
/*			   call stack					*/
/* 10/18/01  JRB     20    Fix sprintf calls.				*/
/* 09/28/01  EJV     19    Added vnsprintf for systems that support it.	*/
/* 05/18/00  JRB     18    More Lint cleanup.				*/
/* 04/19/00  JRB     17    Lint cleanup.				*/
/* 09/24/99  JRB     16    added:  #include "sysincs.h"                 */
/* 09/13/99  MDE     15    Added SD_CONST modifiers			*/
/* 07/15/99  RKR     14    16-bit version needs stdarg.h		*/
/* 04/14/99  MDE     13    Removed unnecessary include files		*/
/* 11/11/98  DSF     12    Minor changes to _slog_dyn_log_fun		*/
/* 10/08/98  MDE     11    Migrated to updated SLOG interface		*/
/* 08/25/98  IKE     10    Prevent buffer overrun by vsprintf for some	*/
/*			   platforms					*/
/* 08/13/98  MDE     09    Now log buffer overruns			*/
/* 06/15/98  MDE     08    Changes to allow compile under C++		*/
/* 06/02/98  DSF     07    LOG_TIME_EN on by default            	*/
/* 01/09/98  EJV     06    SUN Solaris 2.5 uses now ANSI vsprintf	*/
/* 11/05/97  DSF     05    Added SYSTIME_EN				*/
/* 10/06/97  DSF     04    Added thisFileName				*/
/* 09/12/97  DSF     03    Expose slogSetTimeText ()			*/
/* 08/19/97  DSF     02    Initialize sLogCtrlDefault to default 	*/
/*			   settings					*/
/* 05/27/97  DSF     01    Added IPC logging capability			*/
/* 04/02/97  DTL   7.00    MMSEASE 7.0 release. See MODL70.DOC for	*/
/*			   history.					*/
/************************************************************************/

#if defined(__OS2__)
#pragma data_seg(alldata)
#define INCL_BASE
#define INCL_DOS
#define INCL_DOSMISC
#define INCL_ERRORS
#define INCL_DOSPROCESS
#define INCL_DOSQUEUES
#define INCL_DOSSEMAPHORES
#define INCL_DOSMEMMGR
#define INCL_DOSFILEMGR
#define INCL_DOSDATETIME
#define INCL_DOSDEVICES
#include <os2.h>
#include <stddef.h>
#endif

#include "glbtypes.h"
#include "sysincs.h"
#include "glbsem.h"
#include "slog.h"
#include "stime.h"
#include "str_util.h"
#include "sx_defs.h"

#if defined (_WIN32)
#include <windows.h>
#endif

#define XML_NO_TAG	      0
#define XML_DOCUMENT	      1
#define XML_COMMENT	      2
#define XML_START	      3
#define XML_END		      4
#define XML_EMPTY	      5

ST_CHAR *_slogXMLLogTypeStr = "SLOGXML";

/************************************************************************/
/* Other prototypes.							*/
/************************************************************************/

static ST_VOID doSlog (LOG_CTRL *lc, 
		       SD_CONST ST_INT logType, 
		       SD_CONST ST_CHAR *SD_CONST logTypeStr, 
		       SD_CONST ST_CHAR *SD_CONST sourceFile,
		       SD_CONST ST_INT lineNum, 
		       SD_CONST ST_CHAR *SD_CONST format, va_list ap);
ST_VOID slogSetTimeText (LOG_CTRL *lc);
#if defined (_WIN32)
static ST_VOID _slogSetTimeTextElapsed (LOG_CTRL *lc);
#endif

/************************************************************************/
/* Global variables used by the SLOG library.				*/ 
/************************************************************************/


LOG_CTRL *sErrLogCtrl = NULL;

LOG_CTRL sLogCtrlDefault = {LOG_TIME_EN,
			    {1000000, "mms.log", 
    			     FIL_CTRL_WIPE_EN | 
                   	     FIL_CTRL_WRAP_EN | 
                   	     FIL_CTRL_MSG_HDR_EN,
			     0, 0, NULL}};

LOG_CTRL *sLogCtrl = &sLogCtrlDefault;
ST_CHAR slogTimeText[TIME_BUF_LEN];
ST_INT sl_max_msg_size = MAX_LOG_SIZE;
ST_UINT32 slogRemoteFlags;
ST_BOOLEAN sNonStandardLogMode = SD_FALSE;

ST_VOID (*slog_service_fun) (ST_VOID);

ST_CHAR *_slogAlwaysLogTypeStr = "SLOGALWAYS";

/* dynamic logging function                                              */
ST_VOID (*slog_dyn_log_fun) (LOG_CTRL *lc, 
				SD_CONST ST_INT logType, 
                             	SD_CONST ST_CHAR *SD_CONST sourceFile, 
				SD_CONST ST_INT lineNum,
                             	SD_CONST ST_INT bufLen, 
				SD_CONST ST_CHAR *buf);
ST_VOID (*_slog_dyn_log_fun) (LOG_CTRL *lc, 
				SD_CONST ST_CHAR *timeStr, 
			     	SD_CONST ST_INT logType, 
				SD_CONST ST_CHAR *SD_CONST logTypeStr,
                             	SD_CONST ST_CHAR *SD_CONST sourceFile, 
				SD_CONST ST_INT lineNum,
                             	SD_CONST ST_INT bufLen, 
				SD_CONST ST_CHAR *buf);

/* remote logging function                                              */
/* Assign to this fun pointer a function which handles the logging to	*/
/* remote log file.  For the slog_remote_flags parameter reference the	*/
/* description for slog_remote_fun function.				*/

ST_VOID (*slog_remote_fun) (ST_UINT32 slog_remote_flags,
			  LOG_CTRL *lc, 
			SD_CONST ST_INT logType, 
                        SD_CONST ST_CHAR *SD_CONST sourceFile, 
			SD_CONST ST_INT lineNum,
                        SD_CONST ST_INT bufLen, 
			SD_CONST ST_CHAR *buf);

ST_VOID (*_slog_remote_fun) (ST_UINT32 slog_remote_flags,
			LOG_CTRL *lc, 
			SD_CONST ST_INT logType, 
                        SD_CONST ST_CHAR *SD_CONST logTypeStr, 
                        SD_CONST ST_CHAR *SD_CONST sourceFile, 
			SD_CONST ST_INT lineNum,
                        SD_CONST ST_INT bufLen, 
			SD_CONST ST_CHAR *buf);

/************************************************************************/
/*                               _slog                                  */
/* Main general message logging function, typically called via macro    */
/* Just print the message to be logged, pass to memory & file logging	*/
/* functions if enabled							*/
/************************************************************************/

ST_VOID _slogc (LOG_CTRL *lc, SD_CONST ST_CHAR *format, ...)
  {
va_list	ap;



  va_start (ap, format);
  doSlog (lc, SLOG_CONT, NULL, NULL, 0, format, ap);
  va_end(ap);
  }


ST_VOID _slog (LOG_CTRL *lc, 
	       SD_CONST ST_CHAR *SD_CONST logTypeStr, 
	       SD_CONST ST_CHAR *SD_CONST sourceFile, 
	       SD_CONST ST_INT	lineNum, 
	       SD_CONST ST_CHAR *format, ...)
  {
va_list	ap;

  va_start (ap, format);
  doSlog (lc, SLOG_NORMAL, logTypeStr, sourceFile, lineNum, format, ap);
  va_end(ap);
  }

/************************************************************************/
/*                               slog                                   */
/************************************************************************/

ST_VOID slog (LOG_CTRL *lc, 
	      SD_CONST ST_INT logType, 
	      SD_CONST ST_CHAR *SD_CONST sourceFile, 
	      SD_CONST ST_INT lineNum, 
	      SD_CONST ST_CHAR *format, ...)
  {
va_list	ap;

  va_start (ap, format);
  doSlog (lc, logType, NULL, sourceFile, lineNum, format, ap);
  va_end(ap);
  }

/************************************************************************/
/*                               slogx                                  */
/* Extended message logging function.					*/
/* Same as slog, except one more argument to determine if this type of	*/
/* logging is enabled.  This function allows one macro to be used	*/
/* for any number of arguments.						*/
/* This function is designed so that the first 5 arguments are passed	*/
/* to this function via macro.  For example:				*/
/* #define SLOG_ACSE_IND s_debug_sel & ACSE_IND_PRINT, s_sLogCtrl,\	*/
/*			 ACSE_IND_PRINT_TYPE,THISFILE,__LINE__		*/
/* slogx (SLOG_ACSE_IND, "Indication PDU ptr=" S_FMT_PTR ", len=%d", ptr, len);	*/
/************************************************************************/

ST_VOID slogx (ST_UINT32 doit, LOG_CTRL *lc, 
	       SD_CONST  ST_INT logType, 
	       SD_CONST ST_CHAR *SD_CONST sourceFile, 
	       SD_CONST ST_INT lineNum, 
	       SD_CONST ST_CHAR *format, ...)
  {
va_list	ap;

/* Make sure "doit" flag is set.					*/
  if (!doit)
    return;

  va_start (ap, format);
  doSlog (lc, logType, NULL, sourceFile, lineNum, format, ap);
  va_end(ap);
  }


/************************************************************************/
/*                               doSlog                                 */
/* Main logging function, called from slog or slogx.			*/
/* Just print the message to be logged, pass to memory & file logging	*/
/* functions if enabled							*/
/************************************************************************/

#define SLOG_MISSED_LOG_MSG  "Warning: SLOG Log messages missed"

static ST_VOID doSlog (LOG_CTRL *lc, 
		       SD_CONST ST_INT logType, 
		       SD_CONST ST_CHAR *SD_CONST logTypeStr, 
		       SD_CONST ST_CHAR *SD_CONST sourceFile,
		       SD_CONST ST_INT lineNum, 
		       SD_CONST ST_CHAR *SD_CONST format, va_list ap)
  {
ST_INT 	count;
ST_CHAR tmpBuf[128];
static ST_INT     logMissed = 0;
static ST_CHAR   *missedSourceFile;
static ST_INT     missedLineNum;
	    
  S_GS_INIT ();
  S_LOCK_UTIL_RESOURCES ();

/* Make sure the LOG_CTRL pointer is not NULL				*/
  if (!lc)
    {
    S_UNLOCK_UTIL_RESOURCES ();
    return;
    }

/* Check to see if any logging is enabled AND dynamic user logging	*/
/* function pointer not set						*/

  if (!(lc->logCtrl & (LOG_FILE_EN | LOG_MEM_EN | LOG_IPC_EN)) && 
      !slog_dyn_log_fun && !_slog_dyn_log_fun &&
      !slog_remote_fun && !_slog_remote_fun)	   
    {
    if (logMissed == 0)
      {
      logMissed        = 1;
      missedSourceFile = sourceFile;
      missedLineNum    = lineNum; 
      }
    S_UNLOCK_UTIL_RESOURCES ();
    return;
    }

/* Allocate a slog_buffer the first time this function is called.  This */
/* allows us to make the size of the slog message buffer larger than    */
/* the default.                                                         */

  if (lc->msg_buf == NULL)
    {
    /* NOTE: this code only for backward compatibility with apps that	*/
    /* control buffer size by setting the global var "sl_max_msg_size".	*/
    /* New apps should call "slog_max_msg_size_set" directly.		*/
    /* Use value of sl_max_msg_size NOW. Don't care if user changes it later.*/
    if (slog_max_msg_size_set (lc, sl_max_msg_size) != SD_SUCCESS)
      {
      fprintf(stderr, "\nslog: error setting max msg size");
      S_UNLOCK_UTIL_RESOURCES ();
      return;
      }
    } 
  
/* It is OK to pass in a NULL format string when using the dynamic 	*/
/* logging functions - no vsprintf if so				*/

  if (format == NULL)
    {
    /* make buf a zero length string just in case			*/
    count = 0;
    lc->msg_buf[0] = 0;
    }
  else
    {
#if defined(_WIN32)
    count = _vsnprintf(lc->msg_buf,lc->max_msg_size,format,ap);
#elif  defined(__QNX__) && defined(__WATCOMC__)
    count = _vbprintf(lc->msg_buf,lc->max_msg_size,format,ap);
#elif defined(_AIX) || defined(sun) || defined(_hpux) || defined(__alpha) || defined(linux)
    count = vsnprintf(lc->msg_buf,lc->max_msg_size,format,ap);
#else  /* other systems: VXWORKS, ... 					*/
    count = vsprintf (lc->msg_buf, format, ap);
#endif
    lc->msg_buf[lc->max_msg_size-1] = 0; 	/* terminate the buffer, Win and UNIX 	*/
						/* functions don't behave the same 	*/
    /* NOTE: On _WIN32 count could be negative because of error or too small buffer. 	*/
    /*       On other systems count is negative because of error, too small buffer is	*/
    /*       indicated by return of count larger than buf_size supplied to the function.*/
#if defined(_WIN32)
    if (count < 0)
      {
      sprintf (tmpBuf,"*** LOG ERROR: LOG BUFFER OVERRUN (lc->max_msg_size=%d bytes) or _vsnprintf function error",
               lc->max_msg_size);
      strncpy_safe (lc->msg_buf, tmpBuf, lc->max_msg_size-1);
      count = strlen (lc->msg_buf);		/* count = len of this log message	*/
      }
#else  /* !defined(_WIN32) */
    if (count < 0)
      {
      sprintf (tmpBuf,"*** LOG ERROR: _vbprintf(QNX), vsnprintf(UNIX,LINUX), or vsprintf(other sys) function failed");
      strncpy_safe (lc->msg_buf, tmpBuf, lc->max_msg_size-1);
      count = strlen (lc->msg_buf);		/* count = len of this log message	*/
      }
    else if (count >= lc->max_msg_size)
      {
      sprintf (tmpBuf,"*** LOG ERROR: LOG BUFFER OVERRUN: message len=%d bytes (lc->max_msg_size=%d bytes)",
               count, lc->max_msg_size);
      strncpy_safe (lc->msg_buf, tmpBuf, lc->max_msg_size-1);
      count = strlen (lc->msg_buf);		/* set count to len of this log message */
      }
#endif  /* !defined(_WIN32) */

    count++;					/* allow for null terminator		*/
    }

/* If time stamping is desired, get the time string from the user	*/
  if (lc->logCtrl & LOG_TIME_EN)
    slogSetTimeText (lc);  
  else
    slogTimeText[0] = 0;	/* Init to empty time/date string.	*/

/* Check for type for special logging ('continuation' or dynamic	*/
/* logging format commands) -	    					*/
/* 	All standard log types are >= 0					*/
/*	Continuation logging is SLOG_CONT (-1)	  			*/
/*	Dynamic logging format commands are other negative numbers,	*/
/*	reserved are - 							*/
/*		SLOG_DYN_PAUSE  = -10	 				*/
/*		SLOG_DYN_LF     = -11	 				*/
/*		SLOG_DYN_CLRSCR = -12	 				*/

  if (logType >=0 || logType == SLOG_CONT)
    {
    if (lc->logCtrl & LOG_FILE_EN)	/* File Logging enabled		*/
      {
      if (logMissed == 1)
        {
        logMissed = 2;          /* to prevent logging this msg again	*/
                                /* when slogIpc turns off all masks 	*/
        slogFile (lc, 0, "INTERNAL_SLOG", missedSourceFile, missedLineNum, strlen (SLOG_MISSED_LOG_MSG), SLOG_MISSED_LOG_MSG);
        }
      slogFile (lc, logType, logTypeStr, sourceFile, lineNum, count, lc->msg_buf);
      }
    if (lc->logCtrl & LOG_MEM_EN)	/* File Logging enabled		*/
      slogMem (lc, logType, logTypeStr, sourceFile, lineNum, count, lc->msg_buf);
    }
  if ((lc->logCtrl & LOG_IPC_LISTEN_EN) || (lc->logCtrl & LOG_IPC_CALL_EN))
    /* IPC Logging enabled (listen, calling, or both modes) */
    slogIpc (lc, logType, logTypeStr, sourceFile, lineNum, count, lc->msg_buf);

/* If the user has set up a dynamic log display function, call it	*/
  if (slog_dyn_log_fun)
    (*slog_dyn_log_fun)(lc, logType, sourceFile, lineNum, count, lc->msg_buf);
  if (_slog_dyn_log_fun)
    (*_slog_dyn_log_fun)(lc, slogTimeText, logType, logTypeStr, sourceFile, 
                         lineNum, count, lc->msg_buf);

 /* If the user has set up a remote logging function, call it		*/
  if (slog_remote_fun)
    {
    (*slog_remote_fun)(slogRemoteFlags, lc, logType, sourceFile, lineNum, 
			count, lc->msg_buf);
    }
  if (_slog_remote_fun)
    {
    (*_slog_remote_fun)(slogRemoteFlags, lc, logType, logTypeStr, 
		       sourceFile, lineNum, count, lc->msg_buf);
    }

  S_UNLOCK_UTIL_RESOURCES ();
  }

/************************************************************************/
/*                         slogSetTimeText				*/
/* Set the time string per the lc control information			*/
/************************************************************************/
ST_VOID slogSetTimeText (LOG_CTRL *lc)
  {
  slogTimeText[0] = 0;	/* Init to empty.	*/

  if (lc->logCtrl & LOG_TIME_EN)
    {
#if defined (_WIN32)
    SYSTEMTIME systime;

    if ((lc->logCtrl & LOG_ELAPSEDTIME_EN) == 0)
      {  
      GetLocalTime (&systime);
      sprintf (slogTimeText, "%04d-%02d-%02d %02d:%02d:%02d.%03d",
             systime.wYear, systime.wMonth, systime.wDay,
             systime.wHour, systime.wMinute, systime.wSecond, systime.wMilliseconds);
      }
    else
      _slogSetTimeTextElapsed (lc);

#elif defined (VXWORKS)
    /* Does not support gettimeofday.	*/
    time_t         curTime;
    struct tm     *locTime;

    curTime = time (NULL);
    locTime = localtime (&curTime);
    sprintf (slogTimeText, "%04d-%02d-%02d  %02d:%02d:%02d",
             locTime->tm_year+1900, locTime->tm_mon+1, locTime->tm_mday,
             locTime->tm_hour, locTime->tm_min, locTime->tm_sec);
#else
    /* UNIX, Linux, QNX,... */
    struct timeval tp;
    time_t         curTime;
    struct tm     *locTime;

    gettimeofday (&tp, NULL);
    curTime = (time_t) tp.tv_sec;
    locTime = localtime (&curTime);
    sprintf (slogTimeText, "%04d-%02d-%02d  %02d:%02d:%02d.%03ld",
             locTime->tm_year+1900, locTime->tm_mon+1, locTime->tm_mday,
             locTime->tm_hour, locTime->tm_min, locTime->tm_sec, tp.tv_usec/1000);
#endif  /* UNIX, Linux, QNX,... */	
    }
  }  


/************************************************************************/

#if defined (_WIN32)
static ST_VOID _slogSetTimeTextElapsed (LOG_CTRL *lc)
{
static ST_BOOLEAN firstTime = SD_TRUE;
static LARGE_INTEGER ticksPerSec;
static LARGE_INTEGER startTicks;
static double dTicksPerSec;
LARGE_INTEGER ticks;
double dDeltaTicks;
double dSec;

  if (firstTime == SD_TRUE)
    {
  /* See if supported, and if so how many ticks per second */
    if (QueryPerformanceFrequency(&ticksPerSec))
      {
    /* Mark the start of time for us  */
      QueryPerformanceCounter (&startTicks);
  
    /* We want to work with double precision floating point */
      dTicksPerSec = (double) ticksPerSec.QuadPart;
      }
    else
      {
      strncpy_safe (slogTimeText, "Elapsed Time Not Available", sizeof (slogTimeText) - 1);
      return;
      }
    firstTime = SD_FALSE;
    }


/* Get the current tick count */
  QueryPerformanceCounter (&ticks);

/* Get delta from start of logging */
  dDeltaTicks = (double) (ticks.QuadPart - startTicks.QuadPart);

/* Calculate the elapsed time */
  dSec = dDeltaTicks/dTicksPerSec;

  sprintf (slogTimeText, "%.6f", dSec);
  }
#endif /* defined (_WIN32) */



/************************************************************************/
/*		               slogTrimFileName				*/
/************************************************************************/

ST_VOID slogTrimFileName (ST_CHAR *dest, SD_CONST ST_CHAR *fullName)
  {
ST_INT i,s;
ST_CHAR c;

  s = 0;
  i = 0;
  while ((c = fullName[i]))
    {
    if (c == '\\' || c == '/' || c == ':' || c == ']' )
      s = i+1;	/* remember the last path seperator character position	*/
    i++; 
    }
  strncpy (dest,&fullName[s], SLOG_MAX_FNAME);
  dest[SLOG_MAX_FNAME] = 0;
  }

/************************************************************************/
/*                         slogSetHdr					*/
/* Set the header string per the lc control information.		*/
/* The slogHdr needs to be SLOG_MAX_HDR+1 bytes long (in worst case).	*/
/************************************************************************/

ST_VOID slogSetHdr (LOG_CTRL *lc, SD_CONST ST_INT logType, 
                    SD_CONST ST_CHAR *SD_CONST logTypeStr, 
                    SD_CONST ST_CHAR *SD_CONST sourceFile, 
                    SD_CONST ST_INT lineNum, 
                    ST_CHAR *slogHdr, ST_CHAR *lineBreak)
  {
ST_CHAR fname[SLOG_MAX_FNAME+1];
ST_CHAR tmp[SLOG_MAX_FNAME+14+1];	/* 14 for line nubmer & ()	*/

  sprintf (slogHdr, lineBreak);

  if ((lc->fc.ctrl & FIL_CTRL_MSG_HDR_EN) && (logType != SLOG_CONT))
    {
    if (!(lc->logCtrl & LOG_NO_HEADER_CR))
      strcat (slogHdr, lineBreak);

    if (lc->logCtrl & LOG_TIME_EN)  /* user wants time stamp		*/
      {
      strcat (slogHdr, slogTimeText);
      strcat (slogHdr, " ");
      }

    if (!(lc->logCtrl & LOG_LOGTYPE_SUPPRESS))
      {
      if (logTypeStr != NULL && logTypeStr[0] != 0)
        {
        strncat (slogHdr, logTypeStr, min (strlen(logTypeStr), SLOG_MAX_LOGTYPESTR));
        strcat (slogHdr, " ");
	}
      else
        {
        sprintf (tmp, "LogType:% 2d ", logType);
	strcat (slogHdr, tmp);
	}
      }

    if ((sourceFile != NULL && sourceFile[0] != 0) && !(lc->logCtrl & LOG_FILENAME_SUPPRESS))
      {
      slogTrimFileName (fname, sourceFile);
      sprintf (tmp, "(%s %d) ", fname, lineNum);
      strcat (slogHdr, tmp);
      }
    if (!(lc->logCtrl & LOG_NO_HEADER_CR))
      {
      strcat (slogHdr, lineBreak);
      strcat (slogHdr,"  ");
      }
    }
  if (logType == SLOG_CONT)
    strcat (slogHdr,"  ");
  }

/************************************************************************/
/*                       dumpCallingStack				*/
/************************************************************************/

#if defined(MSDOS) && !defined(_WINDOWS)
ST_VOID main (ST_VOID);
#endif

ST_VOID slogCallStack (LOG_CTRL *lc, SD_CONST ST_CHAR *txt)
  {
#if !defined(CODAN)
#if defined(MSDOS) && !defined(TC)
static ST_UINT32 ptr_to_abs (ST_VOID *ptr);
ST_UINT16 os,sg;
ST_UINT16 os2,sg2;
ST_UINT16 os3,sg3;
ST_UINT16 os4,sg4;
ST_UINT16 os5,sg5;
#endif

/* For DOS we can save the caller's return address		 	*/
#if defined(MSDOS) && !defined(TC)
  _asm {
    push si                     ; save SI
    
    mov si, bp                  ; first BP frame
    mov ax, ss:[si+2]		; get return address "offset" from stack
    mov os, ax			; store in "offset" variable
    mov ax, ss:[si+4]		; get return address "segment" from stack
    mov sg, ax			; store in "segment" variable

    mov si, ss:[si]             ; second BP frame
    mov ax, ss:[si+2]		; get return address "offset" from stack
    mov os2, ax			; store in "offset" variable
    mov ax, ss:[si+4]		; get return address "segment" from stack
    mov sg2, ax			; store in "segment" variable
    
    mov si, ss:[si]             ; third BP frame
    mov ax, ss:[si+2]		; get return address "offset" from stack
    mov os3, ax			; store in "offset" variable
    mov ax, ss:[si+4]		; get return address "segment" from stack
    mov sg3, ax			; store in "segment" variable

    mov si, ss:[si]             ; fourth BP frame
    mov ax, ss:[si+2]		; get return address "offset" from stack
    mov os4, ax			; store in "offset" variable
    mov ax, ss:[si+4]		; get return address "segment" from stack
    mov sg4, ax			; store in "segment" variable
    
    mov si, ss:[si]             ; fifth BP frame
    mov ax, ss:[si+2]		; get return address "offset" from stack
    mov os5, ax			; store in "offset" variable
    mov ax, ss:[si+4]		; get return address "segment" from stack
    mov sg5, ax			; store in "segment" variable
    
    pop si                      ; restore SI
    }
#endif

#endif

  if (txt != NULL)
    slog(lc, 0, NULL, 0, "%s", txt);
    
#if defined(MSDOS) && !defined(TC)
  slog(lc,SLOG_CONT,NULL,0,"Calling Address (1st frame): 0x%04X:%04X (0x%lX)",
  	    sg,os,((ST_UINT32)sg << 4) + (ST_UINT32)os);
  slog(lc,SLOG_CONT,NULL,0,"Calling Address (2nd frame): 0x%04X:%04X (0x%lX)",
  	    sg2,os2,((ST_UINT32)sg2 << 4) + (ST_UINT32)os2);
  slog(lc,SLOG_CONT,NULL,0,"Calling Address (3rd frame): 0x%04X:%04X (0x%lX)",
  	    sg3,os3,((ST_UINT32)sg3 << 4) + (ST_UINT32)os3);
  slog(lc,SLOG_CONT,NULL,0,"Calling Address (4th frame): 0x%04X:%04X (0x%lX)",
  	    sg4,os4,((ST_UINT32)sg4 << 4) + (ST_UINT32)os4);
  slog(lc,SLOG_CONT,NULL,0,"Calling Address (5th frame): 0x%04X:%04X (0x%lX)",
  	    sg5,os5,((ST_UINT32)sg5 << 4) + (ST_UINT32)os5);
  slog(lc,SLOG_CONT,NULL,0,"Main Address : %08lx (0x%lX)",
                                                main,ptr_to_abs ((ST_VOID *)main));

#endif

#if !defined(CODAN)
#endif
  }

/************************************************************************/
/*			 ptr_to_abs (ptr)				*/
/* Convert buffer address to long real address				*/
/************************************************************************/

#if defined(MSDOS) && !defined(TC)
static ST_UINT32 ptr_to_abs (ST_VOID *ptr)
  {
ST_UINT32 temp1;
ST_UINT32 temp2;

  temp1 =  (ST_UINT32) ptr;
  temp2 = temp1 >> 12;			/* Microsoft/Xenix use seg/off	*/
  temp1 &= 0xFFFF;
  temp2 &= 0xFFFF0;
  return (temp1 + temp2);               /* find real address         */
  }
#endif


/************************************************************************/
/*                               slog                                   */
/************************************************************************/

ST_VOID slogDelBuf (LOG_CTRL *lc)
  {
  S_LOCK_UTIL_RESOURCES ();
  if (lc->msg_buf)
    free (lc->msg_buf);
  lc->msg_buf = NULL;
  S_UNLOCK_UTIL_RESOURCES ();
  }

/************************************************************************/
/*			slog_max_msg_size_set				*/
/* Set the maximum message size and allocate a buffer of this size.	*/
/* This function sets "max_msg_size" and "msg_buf" in LOG_CTRL.		*/
/************************************************************************/
ST_RET slog_max_msg_size_set (LOG_CTRL *lc, ST_INT max_msg_size)
  {
ST_RET retcode = SD_SUCCESS;

  S_LOCK_UTIL_RESOURCES ();
  lc->max_msg_size = max_msg_size;	/* save size in LOG_CTRL	*/

  if (lc->msg_buf != NULL)
    free (lc->msg_buf);	/* buf already allocated by previous call, so free it first*/

  /* Alloc "lc->msg_buf". chk_malloc calls slog so use malloc here!	*/
  /* This is the only place "lc->msg_buf" is allocated.			*/
  lc->msg_buf = (ST_CHAR *) malloc (lc->max_msg_size + 1); 
  if (lc->msg_buf == NULL)
    {	/* this should almost never happen	*/
    lc->max_msg_size = 0;	/* no buffer so can't log anything	*/
    retcode = SD_FAILURE;
    }
  S_UNLOCK_UTIL_RESOURCES ();
  return (retcode);
  }

/************************************************************************/
/*			slog_start					*/
/************************************************************************/

ST_RET slog_start (LOG_CTRL *lc, ST_INT max_msg_size)
  {
ST_RET rc;

  rc = slog_max_msg_size_set (lc, max_msg_size);
  rc = slogIpcInit (lc);
  return (rc);
  }

/************************************************************************/
/*			slog_end					*/
/************************************************************************/

ST_RET slog_end (LOG_CTRL *lc)
  {
ST_CHAR idxFileName[S_MAX_PATH];
FILE *fh;
ST_RET rc;

  slogIpcStop (lc);
  rc = slog_get_index_file_name (lc, idxFileName, sizeof(idxFileName));

  S_LOCK_UTIL_RESOURCES ();
  if (rc == SD_SUCCESS)
    {    
    fh = fopen (idxFileName,"w+");
    if (fh)
      {
      fprintf (fh,"%ld", lc->fc.wipeFilePos);
      fclose (fh);
      rc = SD_SUCCESS;
      }
    else
      rc = SD_FAILURE;
    }

/* No more file or IPC logging after end ... */
  lc->logCtrl &= ~(LOG_FILE_EN | LOG_IPC_EN);
  S_UNLOCK_UTIL_RESOURCES ();

  return (rc);
  }

/************************************************************************/
/*                             _slogXML 				*/
/* Formats XML strings and logs them if file logging is enabled.	*/
/************************************************************************/
ST_VOID _slogXML (LOG_CTRL *lc, 
    	      SD_CONST ST_CHAR *SD_CONST logTypeStr, 
    	      SD_CONST ST_CHAR *SD_CONST sourceFile, 
    	      SD_CONST ST_INT lineNum, 
    	      ST_UINT  numBytes,
    	      SD_CONST ST_CHAR *textData)
  {
ST_UINT filCtrlSave;
ST_UINT tabLevel = 0;
ST_UINT index, i, j;
ST_CHAR xmlBuffer[SX_MAX_ELEM_LEN+1];
ST_INT  eleLength[SX_MAX_XML_NEST];
ST_INT  prevTag;
ST_INT  tag = XML_NO_TAG;

 

  S_GS_INIT ();
  S_LOCK_UTIL_RESOURCES ();

  /* Make sure the LOG_CTRL and textData pointers are not NULL          */
  if ((!lc) || (!textData))
    {
    S_UNLOCK_UTIL_RESOURCES ();
    return;
    }

  /* Check to see if any logging is enabled AND dynamic user logging	*/
  /* function pointer not set						*/
  if (!(lc->logCtrl & LOG_FILE_EN) && 
      !slog_dyn_log_fun && !_slog_dyn_log_fun &&
      !slog_remote_fun && !_slog_remote_fun)
    {
    S_UNLOCK_UTIL_RESOURCES ();
    return;
    }

  /* Remember the state of the wipe/wrap/header enable flags		*/
  /* so that we can restore them later					*/
  filCtrlSave = lc->fc.ctrl & (FIL_CTRL_WIPE_EN | 
    			       FIL_CTRL_WRAP_EN |
			       FIL_CTRL_MSG_HDR_EN);

  if (!numBytes)
    {
    lc->fc.ctrl |= (filCtrlSave & FIL_CTRL_WIPE_EN);
    }

  if (lc->logCtrl & LOG_FILE_EN)	/* File Logging enabled				  */
    {
    for (i=0; i<SX_MAX_XML_NEST; i++)
      eleLength[i] = 0;
    
    xmlBuffer[0] = '\0';
   
    index=0;

    /* process at most numBytes of data, or until end of textData string */
  
    for (i=0; (i < numBytes) && (textData[i] != 0); i++)
      {
      if (tabLevel >= SX_MAX_XML_NEST)  /* Log error if maximum nesting level is exceeded */
	{
	slogFile (lc, SLOG_NORMAL, logTypeStr, sourceFile, lineNum, 0, "XML nesting exceeds maximum level");
	break;
	}

      if (eleLength[tabLevel] >= SX_MAX_ELEM_LEN)   /* Log error if maximum element length is exceeded */
	{
	slogFile (lc, SLOG_NORMAL, logTypeStr, sourceFile, lineNum, 0, "XML element exceeds maximum length");
	break;
	}

      eleLength[tabLevel]++;

      /* Skip control characters */
      /* cast the chars as ST_UCHAR so data > 0x7F will not look negative */

      if (iscntrl((ST_UCHAR) textData[i]) || (textData[i] == ' ' && index == 0))
	{
	index = 0;
	continue;
	}

      /* Determine tag value */
      if (textData[i] == '/' && textData[i+1] == '>')
	tag = XML_EMPTY;

      if (textData[i] == '<')
	{
	prevTag = tag;

	if (textData[i] == '<' && textData[i+1] == '?')
	  tag = XML_DOCUMENT;
	else if (textData[i] == '<' && textData[i+1] == '!')
	  tag = XML_COMMENT;
	else if (textData[i] == '<' && textData[i+1] == '/') 
	  tag = XML_END;
	else if(textData[i] == '<')
	  tag = XML_START;

	/* Increment/decrement tabLevel */
	if (prevTag == XML_START && (tag == XML_START  || tag == XML_COMMENT))
	  tabLevel++;
	else if (tag == XML_END && (prevTag == XML_END || prevTag == XML_EMPTY))
	  tabLevel--;

	/* Format tab level */
	if (index == 0)
	  {
	  xmlBuffer[0] = '\0';
      
	  if (tabLevel)
  	    {
  	    for (j=0; j<tabLevel; j++, index+=2)
  	      strcat (xmlBuffer, "  ");
  	    }
	  }
	}
  
      xmlBuffer[index++] = textData[i];
      xmlBuffer[index] = '\0';

      /* Log XML buffer */
      if (textData[i] == '>' && (  
          textData[i+1] == '<' || 
          textData[i+1] == ' ' || 
          iscntrl ((ST_UCHAR) textData[i+1]) || 
          i == numBytes-1 || 
          tag != XML_START))
	{
	slogFile (lc, SLOG_CONT, logTypeStr, sourceFile, lineNum, strlen(xmlBuffer), xmlBuffer);
	index = 0;
	
	/* If the user has set up a dynamic log display function, call it	*/
	if (slog_dyn_log_fun)
	  {
	  (*slog_dyn_log_fun)(lc, SLOG_CONT, NULL, 0, strlen(xmlBuffer), xmlBuffer);
          }
        if (_slog_dyn_log_fun)
          {
          (*_slog_dyn_log_fun)(lc, slogTimeText, SLOG_CONT, NULL, NULL, 0, strlen(xmlBuffer), xmlBuffer);
          }

        /* If the user has set up a remote logging function, call it		*/
	if (slog_remote_fun)
          {
          (*slog_remote_fun)(slogRemoteFlags, lc, SLOG_CONT, NULL, 0, strlen(xmlBuffer), xmlBuffer);
          }
        if (_slog_remote_fun)
          {
          (*_slog_remote_fun)(slogRemoteFlags, lc, SLOG_CONT, NULL, NULL, 0, strlen(xmlBuffer), xmlBuffer);
	  }
	}
      }
    }

  /* All done, restore the flag state bits */
  lc->fc.ctrl |= filCtrlSave;

  S_UNLOCK_UTIL_RESOURCES ();
  }


