/************************************************************************/
/* SISCO SOFTWARE MODULE HEADER *****************************************/
/************************************************************************/
/*   (c) Copyright Systems Integration Specialists Company, Inc.,	*/
/*     		1999 - 2008, All Rights Reserved		        */
/*									*/
/* MODULE NAME : sx_dec.c						*/
/* PRODUCT(S)  : 							*/
/*									*/
/* MODULE DESCRIPTION : 						*/
/*									*/
/* GLOBAL FUNCTIONS DEFINED IN THIS MODULE :				*/
/*									*/
/* MODIFICATION LOG :							*/
/*  Date     Who   Rev			Comments			*/
/* --------  ---  ------   -------------------------------------------	*/
/* 02/06/08  EJV     88    Added SX_PARSING_OK, sx_err_str* (for slogs).*/
/* 01/14/08  MDE     87    Fixed numOcc multithread problem		*/
/* 10/04/07  MDE     86    Tweaked LOGCFG_VALUE_GROUP/LOGCFGX_VALUE_MAP	*/
/* For early MODLOGS see previous revision of this file on PVCS.	*/
/************************************************************************/

#if defined (_WIN32)
#pragma warning(disable : 4996)
#endif

#include "glbtypes.h"
#include "sysincs.h"
#include "mem_chk.h"
#include "sx_defs.h"
#include "sx_log.h"
#include "time_str.h"
#include "str_util.h"

/************************************************************************/
/* For debug version, use a static pointer to avoid duplication of 	*/
/* __FILE__ strings.							*/

#ifdef DEBUG_SISCO
SD_CONST static ST_CHAR *SD_CONST thisFileName = __FILE__;
#endif

#if defined(USE_EXPAT)
/* Assume linking to Expat "static" library. Must define XML_STATIC	*/
/* before including "expat.h".						*/
#define XML_STATIC
#include "expat.h"
#define EXPAT_BUF_SIZE	8192	/* parse buffer size if reading from file*/
#define ENTITY_BUF_SIZE	2000	/* initial size to alloc for entity buffer*/
				/* reallocate larger if needed		*/

static void XMLCALL expatHandlerStartSkip(void *userData, const char *el, const char **attr);
static void XMLCALL expatHandlerEndSkip(void *userData, const char *el);
#endif
/************************************************************************/

SD_CONST ST_CHAR *SD_CONST _sx_log_dec_logstr = "SX_LOG_DEC";
SD_CONST ST_CHAR *SD_CONST _sx_log_enc_logstr = "SX_LOG_ENC";
SD_CONST ST_CHAR *SD_CONST _sx_log_flow_logstr = "SX_LOG_FLOW";
SD_CONST ST_CHAR *SD_CONST _sx_log_debug_logstr = "SX_LOG_DEBUG";
SD_CONST ST_CHAR *SD_CONST _sx_log_err_logstr = "SX_LOG_ERR";
SD_CONST ST_CHAR *SD_CONST _sx_log_nerr_logstr = "SX_LOG_NERR";

ST_UINT sx_debug_sel = SX_LOG_ERR | SX_LOG_NERR;
ST_UINT sx_debug_sel_cfg = SX_LOG_ERR | SX_LOG_NERR;	/* saves SX masks while logging configuration is parsed */

#ifdef DEBUG_SISCO
LOGCFGX_VALUE_MAP sxLogMaskMaps[] =
  {
    {"SX_LOG_ERR",	SX_LOG_ERR,	&sx_debug_sel_cfg, _LOGCFG_DATATYPE_UINT_MASK, NULL, "Error"},
    {"SX_LOG_NERR",	SX_LOG_NERR,	&sx_debug_sel_cfg, _LOGCFG_DATATYPE_UINT_MASK, NULL, "Notice"},
    {"SX_LOG_FLOW",	SX_LOG_FLOW,	&sx_debug_sel_cfg, _LOGCFG_DATATYPE_UINT_MASK, NULL, "Flow"},
    {"SX_LOG_DEC",	SX_LOG_DEC,	&sx_debug_sel_cfg, _LOGCFG_DATATYPE_UINT_MASK, NULL, "Decode"},
    {"SX_LOG_ENC",	SX_LOG_ENC,	&sx_debug_sel_cfg, _LOGCFG_DATATYPE_UINT_MASK, NULL, "Encode"},
    {"SX_LOG_DEBUG",	SX_LOG_DEBUG,	&sx_debug_sel_cfg, _LOGCFG_DATATYPE_UINT_MASK, NULL, "Debug"},
  };

LOGCFG_VALUE_GROUP sxLogMaskMapCtrl =
  {
  {NULL,NULL},
  "SxLogMasks",	     /* Parent Tag */
  sizeof(sxLogMaskMaps)/sizeof(LOGCFGX_VALUE_MAP),
  sxLogMaskMaps
  };

/* tbl of error strings, used in slogs */
ST_CHAR *sx_err_str[] =
  {
  "SX_PARSING_OK",              /*  0 */
  "SX_USER_ERROR",              /*  1 */
  "SX_STRUCT_NOT_FOUND",        /*  2 */
  "SX_REQUIRED_TAG_NOT_FOUND",  /*  3 */
  "SX_DUPLICATE_NOT_ALLOWED",   /*  4 */
  "SX_EMPTY_TAG_NOT_ALLOWED",   /*  5 */
  "SX_XML_NEST_TOO_DEEP",       /*  6 */
  "SX_XML_BUFFER_OVER_MAX",     /*  7 */
  "SX_XML_MALFORMED",           /*  8 */
  "SX_FILE_NOT_FOUND",	        /*  9 */
  "SX_END_PARSING",             /* 10 */
  "SX_ELEMENT_TBL_TOO_BIG"      /* 11 */
  };
ST_INT sx_err_str_cnt = sizeof (sx_err_str)/sizeof (ST_CHAR *);
#endif /* DEBUG_SISCO */



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

ST_BOOLEAN sxUseSax;
ST_BOOLEAN sxIgnoreNS;

ST_RET sx_rip_xml (SX_DEC_CTRL *sxDecCtrl);
ST_RET sx_rip_xml_file (SX_DEC_CTRL *sxDecCtrl);
ST_RET sx_rip_xml_mem (SX_DEC_CTRL *sxDecCtrl);

SX_ELEMENT *_uibed_find_element (SX_DEC_CTRL *sxDecCtrl, ST_CHAR *tag, ST_INT **numOccOut);
static ST_VOID _sx_pop (SX_DEC_CTRL *sxDecCtrl, ST_BOOLEAN auto_pop);

/************************************************************************/
/*				sx_parseExx_mt 				*/
/* Note: when making changes/correction to this function revise also	*/
/*       other sx_parse functions.					*/
/* Decode the XML located in file by loading the whole thing into	*/
/* memory first ...							*/
/************************************************************************/

ST_RET sx_parseExx_mt (ST_CHAR *fileName, ST_INT numItems, 
		   SX_ELEMENT *itemTbl, ST_VOID *usr,
                   ST_RET (*u_sx_el_start_fun) (SX_DEC_CTRL *sxDecCtrl, ST_CHAR *tag),
                   ST_RET (*u_sx_el_end_fun) (SX_DEC_CTRL *sxDecCtrl, ST_CHAR *tag))
  {
ST_RET rc;
ST_LONG fileSize;
ST_LONG bytesRead;
ST_CHAR *cfgData;
struct stat buf;
int result;
FILE *fp;

/* Get the size of the file */
  fp = fopen (fileName, "r");
  if (fp == NULL)
    {
	  /*
    SXLOG_FLOW2 ("XML File (%s) Open Error (errno=%d)", fileName, errno);
	printf ("XML File (%s) Open Error (errno=%d)\n", fileName, errno);
	*/
    return (SX_FILE_NOT_FOUND);
    }
  result = fstat (fileno (fp), &buf);
  if (result != 0)
    return (SD_FAILURE);

/* Allocate a buffer and read all into memory */
  fileSize = buf.st_size;
  cfgData = (ST_CHAR *) chk_malloc (fileSize);

  bytesRead = fread (cfgData, 1, fileSize, fp);
  fclose (fp);
  if (bytesRead <= 0)
    {
    chk_free (cfgData);
    SXLOG_FLOW1 ("Error: Could not read from '%s'", fileName);
    return (SX_FILE_NOT_FOUND);
    }

  rc = sx_parse_mt (bytesRead, cfgData, numItems, itemTbl, usr, 
  		    u_sx_el_start_fun, u_sx_el_end_fun); 
  if (rc != SD_SUCCESS)
    {
    SXLOG_FLOW1 ("ERROR: parsing failed, return code: '%d'", rc);
    }

  chk_free (cfgData);
  return (rc);
  }
  
/************************************************************************/
/*				sx_parse_mt				*/
/* Note: when making changes/correction to this function revise also	*/
/*       other sx_parse functions.					*/
/* Decode the XML located in memory (thread safe version).		*/
/************************************************************************/
ST_RET sx_parse_mt (ST_LONG lMsgLen, ST_CHAR *xml, ST_INT numItems, 
		 SX_ELEMENT *itemTbl, ST_VOID *usr,
                 ST_RET (*u_sx_el_start_fun) (SX_DEC_CTRL *sxDecCtrl, ST_CHAR *tag),
                 ST_RET (*u_sx_el_end_fun) (SX_DEC_CTRL *sxDecCtrl, ST_CHAR *tag))
{
SX_DEC_CTRL *sxDecCtrl;
ST_RET rc;

  SXLOG_FLOW0 ("Start Decoding XML");
  SXLOG_FLOWH ((ST_INT) lMsgLen, xml);

  sxDecCtrl = (SX_DEC_CTRL *) calloc (1, sizeof (SX_DEC_CTRL));
			/* !we need to use here the system calloc */

  sx_push (sxDecCtrl, numItems, itemTbl, SD_FALSE);
  sxDecCtrl->xmlStart = xml;
  sxDecCtrl->xmlLen = lMsgLen;
  sxDecCtrl->ignoreNS = sxIgnoreNS;
  sxDecCtrl->useFp = SD_FALSE;
  sxDecCtrl->usr = usr;
  sxDecCtrl->u_sx_el_start = u_sx_el_start_fun;
  sxDecCtrl->u_sx_el_end   = u_sx_el_end_fun;
 
#if defined(USE_EXPAT)
  sx_rip_xml_mem (sxDecCtrl);
#else
  sx_rip_xml (sxDecCtrl);
#endif

  if (sxDecCtrl->xmlNestLevel != 0 && sxDecCtrl->errCode == SD_SUCCESS)
    {
    sxDecCtrl->errCode = SX_XML_MALFORMED;
    SXLOG_NERR0 ("Invalid XML nesting");
    }

  rc = sxDecCtrl->errCode;

  free (sxDecCtrl);

  return (rc);
}

/************************************************************************/
/*				sx_parseEx_mt				*/
/* Note: when making changes/correction to this function revise also	*/
/*       other sx_parse functions.					*/
/* Decode the XML located in file (thread safe version).		*/
/************************************************************************/
ST_RET sx_parseEx_mt (ST_CHAR *fileName, ST_INT numItems, 
                 SX_ELEMENT *itemTbl, ST_VOID *usr,
                 ST_RET (*u_sx_el_start_fun) (SX_DEC_CTRL *sxDecCtrl, ST_CHAR *tag),
                 ST_RET (*u_sx_el_end_fun) (SX_DEC_CTRL *sxDecCtrl, ST_CHAR *tag))
{
SX_DEC_CTRL *sxDecCtrl;
ST_RET rc;
FILE *fp;

  SXLOG_FLOW1 ("Start Decoding XML file %s", fileName);
  if ((fp = fopen (fileName,"r"))==NULL)
    {
    SXLOG_FLOW1 ("XML File (%s) Open Error",fileName);
    return (SX_FILE_NOT_FOUND);
    }
  
  sxDecCtrl = (SX_DEC_CTRL *) calloc (1, sizeof (SX_DEC_CTRL));
			/* !we need to use here the system calloc */

  sx_push (sxDecCtrl, numItems, itemTbl, SD_FALSE);
  sxDecCtrl->fp = fp;
  sxDecCtrl->ignoreNS = sxIgnoreNS;
  sxDecCtrl->useFp = SD_TRUE;
  sxDecCtrl->usr = usr;
  sxDecCtrl->u_sx_el_start = u_sx_el_start_fun;
  sxDecCtrl->u_sx_el_end   = u_sx_el_end_fun;
 
#if defined(USE_EXPAT)
  sx_rip_xml_file (sxDecCtrl);
#else
  sx_rip_xml (sxDecCtrl);
#endif

  if (sxDecCtrl->errCode == 0)
    {
    if (sxDecCtrl->xmlNestLevel != 0)
      {
      sxDecCtrl->errCode = SX_XML_MALFORMED;
      SXLOG_NERR0 ("Invalid XML nesting");
      }
    }

  rc = sxDecCtrl->errCode;

  free (sxDecCtrl);

  fclose (fp);
  return (rc);
}


/************************************************************************/
/*			sxStartElement 					*/
/************************************************************************/

ST_VOID sxStartElement (SX_DEC_CTRL *sxDecCtrl) 
  {
SX_ELEMENT *item;
SX_DEC_ELEMENT_INFO *sxDecElInfo;
ST_CHAR *tag;
ST_RET rc = SD_FAILURE;
ST_INT stackLevelSave;
ST_INT *numOccPtr;

  sxDecElInfo = &sxDecCtrl->sxDecElInfo;
  tag = sxDecElInfo->tag;
  SXLOG_DEC1 ("Start element '%s'", tag);
  if (sxDecCtrl->errCode != SD_SUCCESS && sxDecCtrl->errCode != SX_ERR_CONVERT)
    {
    return;
    }

  item = _uibed_find_element (sxDecCtrl, tag, &numOccPtr);
  
  stackLevelSave = sxDecCtrl->itemStackLevel;
  while (item == NULL && sxDecCtrl->itemStackLevel > 0)
    {
    if (sxDecCtrl->auto_pop[sxDecCtrl->itemStackLevel-1] == SD_TRUE)
      {
      _sx_pop (sxDecCtrl, SD_TRUE);
      item = _uibed_find_element (sxDecCtrl, tag, &numOccPtr);
      }
    else
      break;
    }

  if (item != NULL)
    {
    if (*numOccPtr != 0 && ((item->elementFlags & SX_ELF_RPT) == 0))
      {
      sxDecCtrl->errCode = SX_DUPLICATE_NOT_ALLOWED;
      SXLOG_NERR1 ("Duplicate of element '%s' not allowed",  tag);
      return;       
      }
    ++(*numOccPtr);
    if (*numOccPtr > 1)
      {
      SXLOG_CDEC1 ("Number occurences: %d", *numOccPtr);
      }

  /* Save the item for later */
    ++sxDecCtrl->xmlNestLevel;
    sxDecCtrl->elTbl[sxDecCtrl->xmlNestLevel] = item;

  /* Call the user function, if there is one ... */
    sxDecCtrl->item = item;
    sxDecCtrl->reason = SX_ELEMENT_START;
    if ((item->elementFlags & SX_ELF_CSTART) != 0)
      {
      if (item->funcPtr != NULL)
        {
	sxDecCtrl->elUser = item->user;
        (item->funcPtr)(sxDecCtrl);
	}
      else
        {
        SXLOG_CDEC0 ("No state function for this element");
        }
      }
    }
  else if (sxDecCtrl->u_sx_el_start != NULL)
    {
    sxDecCtrl->itemStackLevel = stackLevelSave;

    if (sxDecCtrl->u_sx_el_start != NULL)
      rc = (*(sxDecCtrl->u_sx_el_start)) (sxDecCtrl, tag);
    if (rc == SD_SUCCESS)
      {
      ++sxDecCtrl->xmlNestLevel;
      sxDecCtrl->elTbl[sxDecCtrl->xmlNestLevel] = NULL;
      }
    else
      {
      sxDecCtrl->errCode = SX_STRUCT_NOT_FOUND;
      SXLOG_CDEC1 ("u_sx_el_start failed for element '%s'", tag);
      }
    }
  else
    {
    sxDecCtrl->errCode = SX_STRUCT_NOT_FOUND;
    SXLOG_NERR1 ("Could not find element '%s' in element table", tag);
    }
  }

/************************************************************************/
/*			sxEndElement 					*/
/************************************************************************/

ST_VOID sxEndElement (SX_DEC_CTRL *sxDecCtrl)
  {
SX_DEC_ELEMENT_INFO *sxDecElInfo;
SX_ELEMENT *item;
ST_CHAR *tag;
ST_RET rc = SD_FAILURE;

  sxDecElInfo = &sxDecCtrl->sxDecElInfo;
  tag = sxDecElInfo->tag;
  SXLOG_DEC1 ("End element   '%s'", tag);
  if (sxDecCtrl->errCode != SD_SUCCESS && sxDecCtrl->errCode != SX_ERR_CONVERT)
    {
    return;
    }

  item = sxDecCtrl->elTbl[sxDecCtrl->xmlNestLevel];
  --sxDecCtrl->xmlNestLevel;

  if (item != NULL)
    {
    if (strcmp (tag, item->tag) != 0) /* verify end tag */
      {
      sxDecCtrl->errCode = SX_XML_MALFORMED;
      SXLOG_NERR2 ("XML malformed: found </%s>, expected </%s>", tag, item->tag);
      }
    else
      {
      if ((item->elementFlags & SX_ELF_CEND) != 0)
        {
        sxDecCtrl->item = item;
        sxDecCtrl->reason = SX_ELEMENT_END;
        if (item->funcPtr != NULL)
	  {
  	  sxDecCtrl->elUser = item->user;
          (item->funcPtr)(sxDecCtrl);
	  }
        else
          {
          SXLOG_CDEC0 ("No state function for this element");
          }
        }
      }
    }
  else
    {
    if (sxDecCtrl->u_sx_el_end != NULL)
      {
      rc = (*(sxDecCtrl->u_sx_el_end)) (sxDecCtrl, sxDecCtrl->sxDecElInfo.tag);
      if (rc != SD_SUCCESS)
        {
        sxDecCtrl->errCode = SX_STRUCT_NOT_FOUND;
        SXLOG_NERR1 ("u_sx_el_end failed for element '%s'", tag);
        }
      }
    }
  }

/************************************************************************/
/************************************************************************/
/*				sx_push 				*/
/************************************************************************/

ST_VOID sx_push (SX_DEC_CTRL *sxDecCtrl, ST_INT numItems, SX_ELEMENT *itemTbl,
		ST_BOOLEAN auto_pop)
  {
ST_INT i;
SX_ELEMENT_TBL_CTRL *itemTblCtrl;
ST_INT *numOccTbl;

/* Do some sanity checks first */
  if (sxDecCtrl->itemStackLevel >= SX_MAX_STACK_LEVEL)
    {
    sxDecCtrl->errCode = SX_XML_NEST_TOO_DEEP;
    return;
    }
  if (numItems > SX_MAX_ITEMS_PER_TABLE)
    {
    sxDecCtrl->errCode = SX_ELEMENT_TBL_TOO_BIG;
    return;
    }

  itemTblCtrl = &sxDecCtrl->items[sxDecCtrl->itemStackLevel];
  numOccTbl = itemTblCtrl->numOccTbl;
  
  itemTblCtrl->itemTbl= itemTbl;
  itemTblCtrl->numItems= numItems;
  sxDecCtrl->auto_pop[sxDecCtrl->itemStackLevel] = auto_pop;
  ++sxDecCtrl->itemStackLevel;

/* reset the numOCc elements */
  for (i = 0; i < numItems; ++i)
    numOccTbl[i] = 0;
  }

/************************************************************************/
/*				sx_pop 					*/
/************************************************************************/

ST_VOID sx_pop (SX_DEC_CTRL *sxDecCtrl)
  {
  _sx_pop (sxDecCtrl, SD_FALSE);
  }


/************************************************************************/
/*			_sx_pop						*/
/************************************************************************/

static ST_VOID _sx_pop (SX_DEC_CTRL *sxDecCtrl, ST_BOOLEAN auto_pop)
  {
SX_ELEMENT_TBL_CTRL *itemTblCtrl;
SX_ELEMENT *item;
ST_INT i;
ST_INT *numOccTbl;

/* Check for mandatory elements */
  if (sxDecCtrl->itemStackLevel > 0)
    {
    --sxDecCtrl->itemStackLevel;
  /* If auto-popping AND have a unknown element handler, don't check mandatory */
    if (auto_pop && sxDecCtrl->u_sx_el_start != NULL)
      return;

    itemTblCtrl = &sxDecCtrl->items[sxDecCtrl->itemStackLevel];
    numOccTbl = itemTblCtrl->numOccTbl;
    for (i = 0; i < itemTblCtrl->numItems; ++i)
      {
      item = &itemTblCtrl->itemTbl[i];
      if (numOccTbl[i] == 0 && ((item->elementFlags & SX_ELF_OPT) == 0))
        {
        sxDecCtrl->errCode = SX_REQUIRED_TAG_NOT_FOUND;
        SXLOG_NERR1 ("Mandatory element '%s' not found",  item->tag);
        break;       
        }
      }
    }
  }


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

SX_ELEMENT *_uibed_find_element (SX_DEC_CTRL *sxDecCtrl, ST_CHAR *tag, ST_INT **numOccPtrOut)
  {
SX_ELEMENT_TBL_CTRL *itemTblCtrl;
ST_INT numItems;
SX_ELEMENT *item;
ST_INT i;

  itemTblCtrl = &sxDecCtrl->items[sxDecCtrl->itemStackLevel-1];
  item = itemTblCtrl->itemTbl;
  numItems = itemTblCtrl->numItems;

/* See if this element is in our table */
  for (i = 0; i < numItems; ++i, ++item)
    {
    if (strcmp (tag, item->tag) == 0)
      {
      *numOccPtrOut = &itemTblCtrl->numOccTbl[i];
      return (item);
      }
    }
  return (NULL);
  }



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

ST_RET sx_get_element_contents (SX_DEC_CTRL *sxDecCtrl, 
		      ST_CHAR *destBuf, ST_INT destLen,
		      ST_INT *lenOut)
  {
ST_CHAR *src;
ST_INT len;
ST_INT rc;

  rc = sx_find_element_contents (sxDecCtrl, &src, &len);
  if (rc != SD_SUCCESS)
    return (rc);

  if (destLen < len)
    {
    SXLOG_NERR0("sx_get_element_contents: dest too small");
    SXLOG_NERR1 ("Error: sx_get_element_contents: dest too small for tag '%s'", sxDecCtrl->sxDecElInfo.tag);
    sxDecCtrl->errCode = SD_FAILURE;
    return (SD_FAILURE);
    }

  memcpy (destBuf, src, len);
  *lenOut = len;
  return (SD_SUCCESS);
  }

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

ST_RET sx_find_element_contents (SX_DEC_CTRL *sxDecCtrl, 
		      ST_CHAR **elStartOut, ST_INT *lenOut)
  {
SX_DEC_ELEMENT_INFO *sxDecElInfo;
ST_CHAR *start;
ST_CHAR *end;
ST_CHAR endTagBuf[100];
ST_INT len;
#if defined(USE_EXPAT)
int offset;
int size;
const char *ptr;

  /* Doesn't work if reading one buffer at a time from a file.	*/
  if (sxDecCtrl->fp)
    {
    SXLOG_ERR0 ("sx_find_element_contents only works if entire XML is in memory. Use sx_parseExx_mt or sx_parse_mt.");
    return (SD_FAILURE);
    }

  /* Set "start" ptr to point after end of this start tag.	*/
  ptr = XML_GetInputContext(sxDecCtrl->parser, &offset, &size);
  ptr += offset;	/* point to current position in XML buffer	*/
			/* should be beginning of start tag		*/
  start = strchr (ptr, '>') + 1;	/* point after end of start tag	*/

  /* save nest level to help find corresponding end tag	*/
  sxDecCtrl->skipNestLevel = sxDecCtrl->xmlNestLevel;
  /* Change handlers to skip to end of this element (look for end tag).	*/
  XML_SetCharacterDataHandler(sxDecCtrl->parser, NULL);	/* ignore element data	*/
  XML_SetElementHandler(sxDecCtrl->parser, expatHandlerStartSkip, expatHandlerEndSkip);
  sxDecElInfo = &sxDecCtrl->sxDecElInfo;
#else
  sxDecElInfo = &sxDecCtrl->sxDecElInfo;
  start = sxDecCtrl->xmlPos;
#endif
/* Check to see if this is an empty element */
  if (*(start - 2) == '/')
    {
    *lenOut = 0;
    return (SD_SUCCESS);
    }
    
  *elStartOut = start;
  len = sxDecCtrl->xmlLen - (start - sxDecCtrl->xmlStart);

/* We need to find the closing element for this start element */
  endTagBuf[0] = '<';
  endTagBuf[1] = '/';
  strcpy (&endTagBuf[2], sxDecElInfo->tag);
  strcat (endTagBuf, ">");

  end = strnstr (start, endTagBuf, len);
  if (end == NULL)
    {
    SXLOG_NERR1("Can't find end tag '%s'", sxDecElInfo->tag);
    sxDecCtrl->errCode = SD_FAILURE;
    return (SD_FAILURE);
    }
  *lenOut = end - start;

#if !defined(USE_EXPAT)		/* with Expat, can't set or use xmlPos	*/
  sxDecCtrl->xmlPos = end;
#endif

  SXLOG_CDEC0("sx_find_element_contents got data:");
  SXLOG_DECH (*lenOut, *elStartOut);
  return (SD_SUCCESS);
  }

/************************************************************************/
/************************************************************************/
/*			sx_get_entity 					*/
/************************************************************************/

ST_RET sx_get_entity (SX_DEC_CTRL *sxDecCtrl, 
		      ST_CHAR *destBuf, ST_INT destLen,
		      ST_INT *lenOut)
  {
#if defined(USE_EXPAT)
  if (destLen < sxDecCtrl->entityLen)
    {
    SXLOG_NERR2 ("Error: sx_get_entity: dest too small (max %d bytes expected) for tag '%s'", destLen, sxDecCtrl->sxDecElInfo.tag);
    sxDecCtrl->errCode = SD_FAILURE;
    *lenOut = 0;	/* return empty buf	*/
    return (SD_FAILURE);
    }
  else
    {
    memcpy (destBuf, sxDecCtrl->entityBuf, sxDecCtrl->entityLen);
    *lenOut = sxDecCtrl->entityLen;
    }
  if (*lenOut == 0)
    SXLOG_CDEC0("sx_get_entity (no data)");
  else
    {
    SXLOG_CDEC0("sx_get_entity got data:");
    SXLOG_DECH (*lenOut, destBuf);
    }
  return (SD_SUCCESS);
#else	/* !USE_EXPAT	*/
SX_DEC_ELEMENT_INFO *sxDecElInfo;
ST_INT i;
ST_CHAR *dest;
ST_CHAR *src;
ST_CHAR *end;

  sxDecElInfo = &sxDecCtrl->sxDecElInfo;
  src = sxDecElInfo->entityStart;
  dest = destBuf;
  end = sxDecElInfo->entityEnd;
  for (i = 0; i < destLen && src < end; ++i, ++src, ++dest)
    *dest = *src;

  *lenOut = i;

  if (src < end)
    {
    SXLOG_NERR2 ("Error: sx_get_entity: dest too small (max %d bytes expected) for tag '%s'", destLen, sxDecCtrl->sxDecElInfo.tag);
    sxDecCtrl->errCode = SD_FAILURE;
    return (SD_FAILURE);
    }

  SXLOG_CDEC0("sx_get_entity got data:");
  SXLOG_DECH (i, destBuf);
  return (SD_SUCCESS);
#endif	/* !USE_EXPAT	*/
  }


/************************************************************************/
/*                       sx_get_bool					*/
/************************************************************************/

ST_RET sx_get_bool (SX_DEC_CTRL *sxDecCtrl, ST_BOOLEAN *out_ptr)
  {
ST_INT d;
ST_RET rc;

  rc = sx_get_value (sxDecCtrl, "%d", &d);
  if (rc != SD_SUCCESS)
    return (rc);

  if (d == 0)
    *out_ptr = SD_FALSE;
  else
    *out_ptr = SD_TRUE;

  return (SD_SUCCESS);
  }

/************************************************************************/
/*                       sx_get_int					*/
/************************************************************************/

ST_RET sx_get_int (SX_DEC_CTRL *sxDecCtrl, ST_INT *out_ptr)
  {
  return (sx_get_value (sxDecCtrl, "%d", out_ptr));
  }

/************************************************************************/
/*                       sx_get_float					*/
/************************************************************************/

ST_RET sx_get_float (SX_DEC_CTRL *sxDecCtrl, ST_FLOAT *out_ptr)
  {
  return (sx_get_value (sxDecCtrl, "%f", out_ptr));
  }

/************************************************************************/
/*                       sx_get_double					*/
/************************************************************************/

ST_RET sx_get_double (SX_DEC_CTRL *sxDecCtrl, ST_DOUBLE *out_ptr)
  {
  /* Note: must use the '%le' format specifier for double */
  return (sx_get_value (sxDecCtrl, "%le", out_ptr));
  }

/************************************************************************/
/*                       sx_get_uchar					*/
/************************************************************************/

ST_RET sx_get_uchar (SX_DEC_CTRL *sxDecCtrl, ST_UCHAR *out_ptr)
  {
  return (sx_get_value (sxDecCtrl, "%c", out_ptr));
  }

/************************************************************************/
/*                       sx_get_int16					*/
/************************************************************************/

ST_RET sx_get_int16 (SX_DEC_CTRL *sxDecCtrl, ST_INT16 *out_ptr)
  {
ST_INT i;
ST_RET rc;

  if ((rc = sx_get_value (sxDecCtrl, "%d", &i)) == SD_SUCCESS)
    *out_ptr = (ST_INT16) i;
  return (rc);
  }

/************************************************************************/
/*                       sx_get_uint16					*/
/************************************************************************/

ST_RET sx_get_uint16 (SX_DEC_CTRL *sxDecCtrl, ST_UINT16 *out_ptr)
  {
  return (sx_get_value (sxDecCtrl, "%hu", out_ptr));
  }

/************************************************************************/
/*                       sx_get_uint32					*/
/************************************************************************/

ST_RET sx_get_uint32 (SX_DEC_CTRL *sxDecCtrl, ST_UINT32 *out_ptr)
  {
ST_ULONG ul;
ST_RET   rc;

  if ((rc = sx_get_value (sxDecCtrl, "%lu", &ul)) == SD_SUCCESS)
    *out_ptr = (ST_UINT32) ul;
  return (rc);
  }

/************************************************************************/
/*                       sx_get_uint32_hex				*/
/************************************************************************/

ST_RET sx_get_uint32_hex (SX_DEC_CTRL *sxDecCtrl, ST_UINT32 *out_ptr)
  {
ST_ULONG ul;
ST_RET   rc;

  if ((rc = sx_get_value (sxDecCtrl, "0x%lx", &ul)) == SD_SUCCESS)
    *out_ptr = (ST_UINT32) ul;
  return (rc);
  }
/************************************************************************/
/*                       sx_get_int32					*/
/************************************************************************/

ST_RET sx_get_int32 (SX_DEC_CTRL *sxDecCtrl, ST_INT32 *out_ptr)
  {
ST_LONG sl;
ST_RET  rc;

  if ((rc = sx_get_value (sxDecCtrl, "%ld", &sl)) == SD_SUCCESS)
    *out_ptr = (ST_INT32) sl;
  return (rc);
  }

/************************************************************************/
/*                       sx_get_long					*/
/************************************************************************/

ST_RET sx_get_long (SX_DEC_CTRL *sxDecCtrl, ST_LONG *out_ptr)
  {
  return (sx_get_value (sxDecCtrl, "%ld", out_ptr));
  }

/************************************************************************/
/*                       sx_get_ulong					*/
/************************************************************************/

ST_RET sx_get_ulong (SX_DEC_CTRL *sxDecCtrl, ST_ULONG *out_ptr)
  {
  return (sx_get_value (sxDecCtrl, "%lu", out_ptr));
  }

/************************************************************************/
/*                       sx_get_uint					*/
/************************************************************************/

ST_RET sx_get_uint (SX_DEC_CTRL *sxDecCtrl, ST_UINT *out_ptr)
  {
  return (sx_get_value (sxDecCtrl, "%u", out_ptr));
  }

/************************************************************************/
/*                       sx_get_value					*/
/************************************************************************/

ST_RET sx_get_value (SX_DEC_CTRL *sxDecCtrl, ST_CHAR *format_string, ST_VOID *out_ptr)
  {
ST_CHAR *str;
ST_INT strLen;
ST_RET rc;

  strLen = 0;
  rc = sx_get_string_ptr (sxDecCtrl, &str, &strLen);
  if (rc != SD_SUCCESS)
    return (rc);

/* Convert to desired data format*/
/* Note: sscanf may return 'bad' value if the number in the str	*/
/*       exceeds the max value in format_string.		*/
  if (sscanf (str, format_string, out_ptr) != 1)
  /* we just want to get one value out of the string, any other ret is an error */
    {
    SXLOG_NERR1 ("Data Conversion Error for tag '%s'", sxDecCtrl->sxDecElInfo.tag);
    sxDecCtrl->errCode = SD_FAILURE;
    return (SD_FAILURE);
    }
  return (SD_SUCCESS);
  }

/************************************************************************/
/*                       *sx_get_alloc_string				*/
/************************************************************************/

ST_RET sx_get_alloc_string (SX_DEC_CTRL *sxDecCtrl, ST_CHAR **strOut)
  {
ST_CHAR *str;
ST_INT strLen;
ST_RET rc;

  strLen = 0;
  rc = sx_get_string_ptr (sxDecCtrl, &str, &strLen);
  if (rc != SD_SUCCESS)
    return (rc);

  *strOut = M_STRDUP(NULL,str);
  return (SD_SUCCESS);
  }

/************************************************************************/
/*                       *sx_get_string					*/
/************************************************************************/

ST_RET sx_get_string (SX_DEC_CTRL *sxDecCtrl, ST_CHAR *dest, ST_INT *lenOut)
  {
ST_CHAR *str;
ST_INT strLen;
ST_RET rc;

  strLen = 0;
  rc = sx_get_string_ptr (sxDecCtrl, &str, &strLen);
  if (rc != SD_SUCCESS)
    return (rc);

  if (*lenOut != 0)
    {
    if (strLen > *lenOut)
      {
      SXLOG_NERR1 ("Error: String too long for tag '%s'", sxDecCtrl->sxDecElInfo.tag);
      sxDecCtrl->errCode = SD_FAILURE;
      return (SD_FAILURE);
      }
    }

  sx_format_string_dec (dest, str);
  *lenOut = strLen;
  return (SD_SUCCESS);
  }

/************************************************************************/
/*                       sx_format_string_dec			        */
/************************************************************************/

ST_VOID sx_format_string_dec (ST_CHAR *dest, ST_CHAR *src)
  {
ST_CHAR *srcPtr;
ST_CHAR *srcPtrLast;
ST_CHAR *dstPtr;
ST_INT  diff;

  dstPtr = dest;
  srcPtrLast = src;
  srcPtr = strchr (src, '&');

  if (srcPtr == NULL)
    {
    strcpy (dest, src);
    return;
    }

  diff = srcPtr - srcPtrLast;
  strncpy (dstPtr, srcPtrLast, diff);
  dstPtr += diff;

  /* parse the source string and generate the dest string */
  while (srcPtr != NULL)
    {
    /* srcPtr points at a & */
    /* lets find out if the following characters are what we are looking for */
    if (strncmp (srcPtr, CODE_APOS, CODE_APOS_LEN) == 0)
      {
      *dstPtr++ = CHAR_APOS;
      srcPtr += CODE_APOS_LEN;
      }
    else if (strncmp (srcPtr, CODE_QUOT, CODE_QUOT_LEN) == 0)
      {
      *dstPtr++ = CHAR_QUOT;
      srcPtr += CODE_QUOT_LEN;
      }
    else if (strncmp (srcPtr, CODE_AMP, CODE_AMP_LEN) == 0)
      {
      *dstPtr++ = CHAR_AMP;
      srcPtr += CODE_AMP_LEN;
      }
    else if (strncmp (srcPtr, CODE_LT, CODE_LT_LEN) == 0)
      {
      *dstPtr++ = CHAR_LT;
      srcPtr += CODE_LT_LEN;
      }
    else if (strncmp (srcPtr, CODE_GT, CODE_GT_LEN) == 0)
      {
      *dstPtr++ = CHAR_GT;
      srcPtr += CODE_GT_LEN;
      }
    else
      {
      *dstPtr++ = '&';
      srcPtr++;
      }

    srcPtrLast = srcPtr;
    srcPtr = strchr (srcPtr, '&');

    if (srcPtr == NULL)
      {
      /* copy the remaining section of the string */
      strcpy (dstPtr, srcPtrLast);
      }
    else
      {
      diff = srcPtr - srcPtrLast;
      strncpy (dstPtr, srcPtrLast, diff);
      dstPtr += diff;
      }
    }
  }


/************************************************************************/
/************************************************************************/
/************************************************************************/
/*                       sx_get_bool_attr				*/
/************************************************************************/

ST_RET sx_get_bool_attr (SX_DEC_CTRL *sxDecCtrl, ST_CHAR *name, ST_BOOLEAN *out_ptr)
  {
ST_INT d;
ST_RET rc;

  rc = sx_get_attr_value (sxDecCtrl, name, "%d", &d);
  if (rc != SD_SUCCESS)
    return (rc);

  if (d == 0)
    *out_ptr = SD_FALSE;
  else
    *out_ptr = SD_TRUE;

  return (SD_SUCCESS);
  }

/************************************************************************/
/*                       sx_get_int_attr 				*/
/************************************************************************/

ST_RET sx_get_int_attr (SX_DEC_CTRL *sxDecCtrl, ST_CHAR *name, ST_INT *out_ptr)
  {
  return (sx_get_attr_value (sxDecCtrl, name, "%d", out_ptr));
  }

/************************************************************************/
/*                       sx_get_float_attr 				*/
/************************************************************************/

ST_RET sx_get_float_attr (SX_DEC_CTRL *sxDecCtrl, ST_CHAR *name, ST_FLOAT *out_ptr)
  {
  return (sx_get_attr_value (sxDecCtrl, name, "%f", out_ptr));
  }

/************************************************************************/
/*                       sx_get_double_attr 				*/
/************************************************************************/

ST_RET sx_get_double_attr (SX_DEC_CTRL *sxDecCtrl, ST_CHAR *name, ST_DOUBLE *out_ptr)
  {
  return (sx_get_attr_value (sxDecCtrl, name, "%le", out_ptr));
  }

/************************************************************************/
/*                       sx_get_uchar_attr 				*/
/************************************************************************/

ST_RET sx_get_uchar_attr (SX_DEC_CTRL *sxDecCtrl, ST_CHAR *name, ST_UCHAR *out_ptr)
  {
  return (sx_get_attr_value (sxDecCtrl, name, "%c", out_ptr));
  }

/************************************************************************/
/*                       sx_get_int16_attr 				*/
/************************************************************************/

ST_RET sx_get_int16_attr (SX_DEC_CTRL *sxDecCtrl, ST_CHAR *name, ST_INT16 *out_ptr)
  {
ST_INT i;
ST_RET rc;

  if ((rc = sx_get_attr_value (sxDecCtrl, name, "%d", &i)) == SD_SUCCESS)
    *out_ptr = (ST_INT16) i;
  return (rc);
  }

/************************************************************************/
/*                       sx_get_uint16_attr 				*/
/************************************************************************/

ST_RET sx_get_uint16_attr (SX_DEC_CTRL *sxDecCtrl, ST_CHAR *name, ST_UINT16 *out_ptr)
  {
ST_UINT i;
ST_RET rc;

  if ((rc = sx_get_attr_value (sxDecCtrl, name, "%u", &i)) == SD_SUCCESS)
    *out_ptr = (ST_UINT16) i;
  return (rc);
  }

/************************************************************************/
/*                       sx_get_uint32_attr				*/
/************************************************************************/

ST_RET sx_get_uint32_attr (SX_DEC_CTRL *sxDecCtrl, ST_CHAR *name, ST_UINT32 *out_ptr)
  {
ST_ULONG ul;
ST_RET   rc;

  if ((rc = sx_get_attr_value (sxDecCtrl, name, "%lu", &ul)) == SD_SUCCESS)
    *out_ptr = (ST_UINT32) ul;
  return (rc);
  }

/************************************************************************/
/*                       sx_get_int32_attr				*/
/************************************************************************/

ST_RET sx_get_int32_attr (SX_DEC_CTRL *sxDecCtrl, ST_CHAR *name, ST_INT32 *out_ptr)
  {
ST_LONG sl;
ST_RET  rc;

  if ((rc = sx_get_attr_value (sxDecCtrl, name, "%ld", &sl)) == SD_SUCCESS)
    *out_ptr = (ST_INT32) sl;
  return (rc);
  }

/************************************************************************/
/*                       sx_get_long_attr					*/
/************************************************************************/

ST_RET sx_get_long_attr (SX_DEC_CTRL *sxDecCtrl, ST_CHAR *name, ST_LONG *out_ptr)
  {
  return (sx_get_attr_value (sxDecCtrl, name, "%ld", out_ptr));
  }

/************************************************************************/
/*                       sx_get_ulong_attr				*/
/************************************************************************/

ST_RET sx_get_ulong_attr (SX_DEC_CTRL *sxDecCtrl, ST_CHAR *name, ST_ULONG *out_ptr)
  {
  return (sx_get_attr_value (sxDecCtrl, name, "%lu", out_ptr));
  }

/************************************************************************/
/*                       sx_get_uint_attr 				*/
/************************************************************************/

ST_RET sx_get_uint_attr (SX_DEC_CTRL *sxDecCtrl, ST_CHAR *name, ST_UINT *out_ptr)
  {
  return (sx_get_attr_value (sxDecCtrl, name, "%u", out_ptr));
  }

/************************************************************************/
/*                       XmlStringToDuration 				*/
/************************************************************************/

ST_RET XmlStringToDuration (ST_CHAR *src, SX_DURATION *out_ptr)
  {
ST_CHAR *p;
ST_CHAR temp[50]; /* arbitrary length */
ST_CHAR temp2[50]; /* arbitrary length */
ST_BOOLEAN foundP = SD_FALSE;
ST_BOOLEAN foundT = SD_FALSE;
ST_BOOLEAN foundDot = SD_FALSE;
ST_UINT i;
ST_BOOLEAN something = SD_FALSE;  /* Make sure we got SOMETHING out of */
				  /* this, any of the fields can be missing */

  out_ptr->inUse = SD_TRUE;
  p = src;
  memset (temp, 0, sizeof(temp));
  memset (temp2, 0, sizeof(temp2));
  for (i = 0; i < strlen(src); i++, p++)
    {
    /* check all error conditions I can think of */
    if ((*p == '-' && foundP) || 
        (isdigit(*p) && !foundP) ||
        (*p == 'Y' && !foundP) ||
        (*p == 'M' && !foundP) ||
        (*p == 'D' && !foundP) ||
        (!foundP && foundT) ||
        (*p == 'H' && !foundT) ||
        (*p == 'S' && !foundT) ||
	(*p != 'P' && *p != 'T' && isalpha(*p) && strlen(temp) == 0))
      return (SD_FAILURE);
      
    /* check for negative */
    else if (*p == '-')
      out_ptr->negative = SD_TRUE;
    else if (*p == '.')
      foundDot = SD_TRUE;
    /* We found a number */
    else if (isdigit(*p))
      {
      if (foundDot)
	strncat (temp2, p, 1);
      else
	strncat (temp, p, 1);
      }
    /* We found a letter */
    else if (isalpha(*p))
      {
      switch (*p)
	{
	case 'P':
	  foundP = SD_TRUE;
	  break;
	case 'T':
	  foundT = SD_TRUE;
	  break;
	case 'Y':
	  sscanf(temp, "%d", &out_ptr->years);
	  temp[0] = 0;
	  something = SD_TRUE;
	  break;
	case 'M':
	  if (foundT)
	    sscanf(temp, "%d", &out_ptr->minutes);
	  else
	    sscanf(temp, "%d", &out_ptr->months);
	  temp[0] = 0;
  	  something = SD_TRUE;
	  break;
	case 'D':
	  sscanf(temp, "%d", &out_ptr->days);
	  temp[0] = 0;
  	  something = SD_TRUE;
	  break;
	case 'H':
	  sscanf(temp, "%d", &out_ptr->hours);
	  temp[0] = 0;
  	  something = SD_TRUE;
	  break;
	case 'S':
	  sscanf(temp, "%d", &out_ptr->seconds);
	  temp[0] = 0;
	  sscanf(temp2, "%ld", &out_ptr->microseconds);
	  temp2[0] = 0;
  	  something = SD_TRUE;
	  break;
	default:
          return (SD_FAILURE);
	}
      }
    }
  if (something == SD_FALSE)
    return (SD_FAILURE);

  return (SD_SUCCESS);
  }

/************************************************************************/
/*                       sx_get_duration_attr 				*/
/************************************************************************/

ST_RET sx_get_duration_attr (SX_DEC_CTRL *sxDecCtrl, ST_CHAR *name, SX_DURATION *out_ptr)
  {
ST_RET rc;
ST_CHAR *sxDuration;

  memset (out_ptr, 0, sizeof (*out_ptr));
  
  rc = sx_get_attr_ptr (sxDecCtrl, &sxDuration, name);
  if (rc)
    return (rc);

  rc = XmlStringToDuration (sxDuration, out_ptr);
  if (rc != SD_SUCCESS)
    {
    SXLOG_NERR1 ("Error: String to Duration conversion for tag '%s'", sxDecCtrl->sxDecElInfo.tag);
    sxDecCtrl->errCode = SD_FAILURE;
    return (SD_FAILURE);
    }
      
  return (SD_SUCCESS);
  }

/************************************************************************/
/*                       sx_get_xtime_attr 				*/
/************************************************************************/

ST_RET sx_get_xtime_attr (SX_DEC_CTRL *sxDecCtrl, ST_CHAR *name, SX_DATE_TIME *out_ptr)
  {
ST_RET rc;
ST_CHAR *sxDateTime;

  memset (out_ptr, 0, sizeof (*out_ptr));
  
  rc = sx_get_attr_ptr (sxDecCtrl, &sxDateTime, name);
  if (rc)
    return (rc);
    
  rc = XmlStringToUtcValue (sxDateTime, out_ptr);
  if (rc != SD_SUCCESS)
    {
    SXLOG_NERR1 ("Error: String to Value date/time conversion for tag '%s'", sxDecCtrl->sxDecElInfo.tag);
    sxDecCtrl->errCode = SD_FAILURE;
    return (rc);
    }

  return (SD_SUCCESS);
  }

/************************************************************************/
/*                       sx_get_tm_attr 				*/
/************************************************************************/

ST_RET sx_get_tm_attr (SX_DEC_CTRL *sxDecCtrl, ST_CHAR *name, struct tm *out_ptr)
  {
ST_RET rc;
ST_CHAR *theTime;

  memset (out_ptr, 0, sizeof (*out_ptr));
  
  rc = sx_get_attr_ptr (sxDecCtrl, &theTime, name);
  if (rc)
    return (rc);
    
  rc = tstrStringToTm (theTime, out_ptr);
  if (rc != SD_SUCCESS)
    {
    SXLOG_NERR1 ("Error: String to Value (struct tm) date/time conversion for tag '%s'", sxDecCtrl->sxDecElInfo.tag);
    sxDecCtrl->errCode = SD_FAILURE;
    return (rc);
    }

  return (SD_SUCCESS);
  }

/************************************************************************/
/*			sx_get_attr_value 				*/
/************************************************************************/

ST_RET sx_get_attr_value (SX_DEC_CTRL *sxDecCtrl, ST_CHAR *name, 
			  ST_CHAR *format_string, ST_VOID *out_ptr)
  {
ST_UINT i;

  for (i = 0; i < sxDecCtrl->sxDecElInfo.attrCount; ++i)
    {
    if (strcmp (sxDecCtrl->sxDecElInfo.attr[i].name, name) == 0)
      {
    /* Convert to desired data format*/
      if (!sscanf (sxDecCtrl->sxDecElInfo.attr[i].value, 
			format_string, out_ptr))
        {
        SXLOG_NERR1 ("Error: Attribute Data Conversion Error for tag '%s'", sxDecCtrl->sxDecElInfo.tag);
        sxDecCtrl->errCode = SD_FAILURE;
        return (SD_FAILURE);
        }
      return (SD_SUCCESS);
      }
    }
  return (SD_FAILURE);
  }

/************************************************************************/
/*                       sx_get_attr					*/
/************************************************************************/

ST_RET sx_get_attr (SX_DEC_CTRL *sxDecCtrl, ST_CHAR *value, ST_CHAR *name)
  {
ST_UINT i;

  for (i = 0; i < sxDecCtrl->sxDecElInfo.attrCount; ++i)
    {
    if (strcmp (sxDecCtrl->sxDecElInfo.attr[i].name, name) == 0)
      {
      strcpy (value, sxDecCtrl->sxDecElInfo.attr[i].value);
      return (SD_SUCCESS);
      }
    }
  return (SD_FAILURE);
  }

/************************************************************************/
/*                       sx_get_attr_ptr				*/
/************************************************************************/

ST_RET sx_get_attr_ptr (SX_DEC_CTRL *sxDecCtrl, ST_CHAR **ptrOut, ST_CHAR *name)
  {
ST_UINT i;

  for (i = 0; i < sxDecCtrl->sxDecElInfo.attrCount; ++i)
    {
    if (strcmp (sxDecCtrl->sxDecElInfo.attr[i].name, name) == 0)
      {
      *ptrOut = sxDecCtrl->sxDecElInfo.attr[i].value;
      return (SD_SUCCESS);
      }
    }
  return (SD_FAILURE);
  }

/************************************************************************/
/*                       *sx_get_bitstring				*/
/************************************************************************/

ST_RET sx_get_bitstring (SX_DEC_CTRL *sxDecCtrl, ST_INT *dest, ST_INT *lenOut)
  {
ST_CHAR *str;
ST_INT strLen;
ST_INT *bits;
ST_CHAR *pStr;
ST_RET rc;
ST_INT i;

  strLen = 0;
  bits = dest; 
  rc = sx_get_string_ptr (sxDecCtrl, &str, &strLen);
  if (rc != SD_SUCCESS)
    return (rc);

  if (*lenOut != 0)
    {
    if (strLen > *lenOut)
      {
      SXLOG_NERR1 ("Error: String too long for tag '%s'", sxDecCtrl->sxDecElInfo.tag);
      sxDecCtrl->errCode = SD_FAILURE;
      return (SD_FAILURE);
      }
    }

  pStr = str;
  pStr += strlen(str) - 1;
  for (i=0;i<(ST_INT)strlen(str);++i,--pStr)
    {
    if (strncmp(pStr, "0", 1) != 0 &&
        strncmp(pStr, "1", 1) != 0)
      {
      SXLOG_NERR1 ("Error: Not a bitstring for tag '%s'", sxDecCtrl->sxDecElInfo.tag);
      sxDecCtrl->errCode = SD_FAILURE;
      return (SD_FAILURE);
      }

    if (strncmp(pStr, "1", 1) == 0)
      {
      *bits |= (1 << i);
      }
    }

  *lenOut = strLen;
  return (SD_SUCCESS);
  }

/************************************************************************/
/*                       sx_get_string_ptr   				*/
/************************************************************************/

ST_RET sx_get_string_ptr (SX_DEC_CTRL *sxDecCtrl, ST_CHAR **strOut, ST_INT *lenOut)
  {
ST_INT vLen;
ST_RET rc;

  rc = sx_get_entity (sxDecCtrl, sxDecCtrl->elemBuf, sizeof(sxDecCtrl->elemBuf), &vLen);
  if (rc != SD_SUCCESS)
    return (SD_FAILURE);

/* Convert to desired data format*/
  sxDecCtrl->elemBuf[vLen] = 0;
  *strOut = sxDecCtrl->elemBuf;
  *lenOut = vLen;
  return (SD_SUCCESS);
  }

/************************************************************************/
/*                       sx_get_string_YesNo   				*/
/*----------------------------------------------------------------------*/
/* This user helper function parses element value for the string	*/
/* "Yes"/"No" and sets the dest to SD_TRUE/SD_FALSE respectively.	*/
/* The string comparison is not case sensitive.				*/
/* Parameters:								*/
/*   SX_DEC_CTRL *sxDecCtrl	pointer to SX decoding control		*/
/*   ST_BOOLEAN  *dest		pointer to dest to SD_TRUE/SD_FALSE	*/
/*   ST_RET       errCode       SX err code to set in sxDecCtrl->errCode*/
/*			          in case of invalid element value,	*/
/*			        SX_ERR_CONVERT allows continue parsing.	*/
/* Return:								*/
/*	SD_SUCCESS		if function successful			*/
/*	SD_FAILURE		otherwise,and the sxDecCtrl->errCode is	*/
/*                              set to errCode.				*/
/************************************************************************/
ST_RET sx_get_string_YesNo (SX_DEC_CTRL *sxDecCtrl, ST_BOOLEAN *dest,
                            ST_RET errCode)
  {
ST_RET   rc;
ST_CHAR *str;
ST_INT   strLen;

  rc = sx_get_string_ptr (sxDecCtrl, &str, &strLen);
  if (rc == SD_SUCCESS)
    {
    if (stricmp (str, "Yes") == 0)
      *dest = SD_TRUE;
    else if (stricmp (str, "No") == 0)
      *dest = SD_FALSE;
    else
      {
      SXLOG_ERR2 ("SX DEC ERROR: invalid %s value '%s' (Yes/No expected)",
                  sxDecCtrl->sxDecElInfo.tag, str);
      sxDecCtrl->errCode = errCode;
      rc = SD_FAILURE;
      }
    }

  return (rc);
  }


/************************************************************************/
/*			sx_get_string_OnOff_mask			*/
/*----------------------------------------------------------------------*/
/* This user helper function parses element value for the string	*/
/* "On"/"Off" and sets/resets a maskBit in the mask.			*/
/* The string comparison is not case sensitive.				*/
/* Parameters:								*/
/*   SX_DEC_CTRL *sxDecCtrl	pointer to SX decoding control		*/
/*   ST_UINT     *mask		pointer to mask to set/reset a bit	*/
/*   ST_UINT      maskBit       mask bit to set/reset if ON/OFF	found	*/
/*   ST_RET       errCode       SX err code to set in sxDecCtrl->errCode*/
/*			          in case of invalid element value,	*/
/*			        SX_ERR_CONVERT allows continue parsing.	*/
/* Return:								*/
/*	SD_SUCCESS		if function successful			*/
/*	SD_FAILURE		otherwise,and the sxDecCtrl->errCode is	*/
/*                              set to errCode				*/
/************************************************************************/

ST_RET sx_get_string_OnOff_mask (SX_DEC_CTRL *sxDecCtrl, ST_UINT *mask,
                                ST_UINT maskBit, ST_RET errCode)
  {
ST_RET   rc;
ST_CHAR *str;
ST_INT   strLen;

  rc = sx_get_string_ptr (sxDecCtrl, &str, &strLen);
  if (rc == SD_SUCCESS)
    {
    if (stricmp (str, "On") == 0)
      {
      *mask |= maskBit;		/* turn on this bit in the mask		*/
      }						       
    else if (stricmp (str, "Off") == 0)
      {						       
      *mask &= ~maskBit;	/* turn off this bit in the mask	*/
      }
    else
      {
      SXLOG_ERR2 ("SX DEC ERROR: invalid %s value '%s' (On/Off expected)",
                  sxDecCtrl->sxDecElInfo.tag, str);
      sxDecCtrl->errCode = errCode;
      rc = SD_FAILURE;
      }
    }
  return (rc);
  }

/************************************************************************/
/*			sx_get_string_OnOff_bool			*/
/*----------------------------------------------------------------------*/
/* This user helper function parses element value for the string	*/
/* "On"/"Off" and sets the dest to SD_TRUE/SD_FALSE respectively.	*/
/* The string comparison is not case sensitive.				*/
/* Parameters:								*/
/*   SX_DEC_CTRL *sxDecCtrl	pointer to SX decoding control		*/
/*   ST_BOOLEAN  *dest		pointer to dest to SD_TRUE/SD_FALSE	*/
/*   ST_RET       errCode       SX err code to set in sxDecCtrl->errCode*/
/*			          in case of invalid element value,	*/
/*			        SX_ERR_CONVERT allows continue parsing.	*/
/* Return:								*/
/*	SD_SUCCESS		if function successful			*/
/*	SD_FAILURE		otherwise,and the sxDecCtrl->errCode is	*/
/*                              set to errCode				*/
/************************************************************************/

ST_RET sx_get_string_OnOff_bool (SX_DEC_CTRL *sxDecCtrl, ST_BOOLEAN *dest,
                                ST_RET errCode)
  {
ST_RET   rc;
ST_CHAR *str;
ST_INT   strLen;

  rc = sx_get_string_ptr (sxDecCtrl, &str, &strLen);
  if (rc == SD_SUCCESS)
    {
    if (stricmp (str, "On") == 0)
      *dest = SD_TRUE;
    else if (stricmp (str, "Off") == 0)
      *dest = SD_FALSE;
    else
      {
      SXLOG_ERR2 ("SX DEC ERROR: invalid %s value '%s' (On/Off expected)",
                  sxDecCtrl->sxDecElInfo.tag, str);
      sxDecCtrl->errCode = errCode;
      rc = SD_FAILURE;
      }
    }
  return (rc);
  }

/************************************************************************/
/*                       sx_get_time					*/
/************************************************************************/
ST_RET sx_get_time (SX_DEC_CTRL *sxDecCtrl, time_t *out_ptr)
  {
ST_CHAR *str;
ST_RET rc;
time_t t;
ST_INT strLen;

  strLen = 0;
  rc = sx_get_string_ptr (sxDecCtrl, &str, &strLen);
  if (rc != SD_SUCCESS)
    return (rc);


  rc = tstrStringToTime (str, &t);
  if (rc != SD_SUCCESS)
    {
    SXLOG_NERR2 ("Error: Time conversion (0x%04x) for tag '%s'", rc, sxDecCtrl->sxDecElInfo.tag);
    sxDecCtrl->errCode = SD_FAILURE;
    return (rc);
    }

  *out_ptr = t;
  return (SD_SUCCESS);
  }

/************************************************************************/
/*                       sx_get_tm					*/
/************************************************************************/
ST_RET sx_get_tm (SX_DEC_CTRL *sxDecCtrl, struct tm *out_ptr)
  {
ST_CHAR *str;
ST_RET rc;
ST_INT strLen;

  strLen = 0;
  rc = sx_get_string_ptr (sxDecCtrl, &str, &strLen);
  if (rc != SD_SUCCESS)
    return (rc);

  rc = tstrStringToTm (str, out_ptr);
  if (rc != SD_SUCCESS)
    {
    SXLOG_NERR2 ("Error: Time conversion (0x%04x) for tag '%s'", rc, sxDecCtrl->sxDecElInfo.tag);
    sxDecCtrl->errCode = SD_FAILURE;
    return (rc);
    }

  return (SD_SUCCESS);
  }

/************************************************************************/
/*                       sx_get_tm_ex					*/
/************************************************************************/
ST_RET sx_get_tm_ex (SX_DEC_CTRL *sxDecCtrl, struct tm *out_ptr, ST_LONG *microseconds)
  {
ST_CHAR *str;
ST_RET rc;
ST_INT strLen;
ST_CHAR *strMicro = NULL;	/* init to invalid value*/
char *periodLoc;

  strLen = 0;
  rc = sx_get_string_ptr (sxDecCtrl, &str, &strLen);
  if (rc != SD_SUCCESS)
    return (rc);

  periodLoc = strchr (str, '.');
  if (periodLoc != NULL)
    {
    if ((periodLoc + 1) == '\0')
      {
      SXLOG_NERR2 ("Error: Time conversion (0x%04x) for tag '%s'", rc, sxDecCtrl->sxDecElInfo.tag);
      sxDecCtrl->errCode = SD_FAILURE;
      return (rc);
      }
    *periodLoc = '\0';
    strMicro = periodLoc + 1;
    }

  rc = tstrStringToTm (str, out_ptr);
  if (rc != SD_SUCCESS)
    {
    SXLOG_NERR2 ("Error: Time conversion (0x%04x) for tag '%s'", rc, sxDecCtrl->sxDecElInfo.tag);
    sxDecCtrl->errCode = SD_FAILURE;
    return (rc);
    }

  if (strMicro != NULL)
    {
    while (strlen(strMicro) < 6)
      strcat (strMicro, "0");
    while (strlen(strMicro) > 6)
      strMicro[strlen(strMicro)-1] = '\0';
    *microseconds = atol(strMicro);
    }
  else
    *microseconds = 0;

  return (SD_SUCCESS);
  }

/************************************************************************/
/*                       sx_get_duration				*/
/************************************************************************/
/* Store duration string found in an xml file into the structure	*/
/* "SX_DURATION".                               			*/
/************************************************************************/

ST_RET sx_get_duration (SX_DEC_CTRL *sxDecCtrl, SX_DURATION *sxDuration)
  {
ST_CHAR *str;
ST_RET rc;
ST_INT strLen;

  memset (sxDuration, 0, sizeof (*sxDuration));
  		
  strLen = 0;
  rc = sx_get_string_ptr (sxDecCtrl, &str, &strLen);
  if (rc != SD_SUCCESS)
    return (rc);

  rc = XmlStringToDuration (str, sxDuration);
  if (rc != SD_SUCCESS)
    {
    SXLOG_NERR1 ("Error: String to Duration for tag '%s'", sxDecCtrl->sxDecElInfo.tag);
    sxDecCtrl->errCode = SD_FAILURE;
    return (rc);
    }

  return (SD_SUCCESS);
  }

/************************************************************************/
/*                       sx_get_xtime					*/
/************************************************************************/
/* Store date and time string found in an xml file into the structure   */
/* "SX_DATE_TIME" specifying the number of seconds from                 */
/* 1/1/1970 (UTC time), the number of microseconds in a decimal         */
/* fraction if it is specified and the number of minutes in the time    */
/* zone offset if it is specified.                                      */
/************************************************************************/

ST_RET sx_get_xtime (SX_DEC_CTRL *sxDecCtrl, SX_DATE_TIME *sxDateTime)
  {
ST_CHAR *str;
ST_RET rc;
ST_INT strLen;

  memset (sxDateTime, 0, sizeof (*sxDateTime));
  		
  strLen = 0;
  rc = sx_get_string_ptr (sxDecCtrl, &str, &strLen);
  if (rc != SD_SUCCESS)
    return (rc);

  /* convert input xml string to date and time */
  /* store data in "SX_DATE_TIME" structure    */
  rc = XmlStringToUtcValue (str, sxDateTime);
  if (rc != SD_SUCCESS)
    {
    SXLOG_NERR1 ("Error: String to Value date/time conversion for tag '%s'", sxDecCtrl->sxDecElInfo.tag);
    sxDecCtrl->errCode = SD_FAILURE;
    return (rc);
    }

  return (SD_SUCCESS);
  }

/************************************************************************/
/*                       sx_get_time_ex					*/
/************************************************************************/
ST_RET sx_get_time_ex (SX_DEC_CTRL *sxDecCtrl, time_t *out_ptr, ST_LONG *microseconds)
  {
ST_CHAR *str;
ST_CHAR strMicro[10];
char *periodLoc;
ST_RET rc;
time_t t;
ST_INT strLen;

  strLen = 0;
  rc = sx_get_string_ptr (sxDecCtrl, &str, &strLen);
  if (rc != SD_SUCCESS)
    return (rc);

  periodLoc = strchr (str, '.');
  if (periodLoc != NULL)
    {
    if ((periodLoc + 1) == '\0')
      {
      SXLOG_NERR2 ("Error: Time conversion (0x%04x) for tag '%s'", rc, sxDecCtrl->sxDecElInfo.tag);
      sxDecCtrl->errCode = SD_FAILURE;
      return (rc);
      }
    strcpy(strMicro, periodLoc + 1);
    *periodLoc = '\0';
    }

  rc = tstrStringToTime (str, &t);
  if (rc != SD_SUCCESS)
    {
    SXLOG_NERR2 ("Error: Time conversion (0x%04x) for tag '%s'", rc, sxDecCtrl->sxDecElInfo.tag);
    sxDecCtrl->errCode = SD_FAILURE;
    return (rc);
    }

  *out_ptr = t;
  if (periodLoc != NULL)
    {
    while (strlen(strMicro) < 6)
      strcat (strMicro, "0");
    while (strlen(strMicro) > 6)
      strMicro[strlen(strMicro)-1] = '\0';
    *microseconds = atol(strMicro);
    }
  else
    *microseconds = 0;

  return (SD_SUCCESS);
  }

/************************************************************************/
/************************************************************************/
#define MAX_BYTES_IN_CHUNK  (2*SX_MAX_ELEM_LEN)	/* MUST BE EVEN */

#define SX_LOAD_CHAR(a)  {\
                         if (sxDecCtrl->useFp == SD_TRUE)\
                           {\
                           sx_load_characters (lineBuf, &eof, sxDecCtrl->fp, a);\
                           sxDecElInfo->entityStart-= a;\
                           sxDecElInfo->entityEnd-= a;\
                           }\
                         else\
                           {\
                           xml+= a;\
                           }\
                         }
                         
#define SX_RIP_NOT_DONE (!sxDecCtrl->termFlag && (((sxDecCtrl->useFp == SD_FALSE) && (xml < xmlEnd)) || ((sxDecCtrl->useFp == SD_TRUE) && (*xml != '\0'))))

#define SX_RIP_DONE (sxDecCtrl->termFlag || ((sxDecCtrl->useFp == SD_FALSE) && (xml >= xmlEnd)) || ((sxDecCtrl->useFp == SD_TRUE) && (*xml == '\0')))


#if !defined(USE_EXPAT)
/************************************************************************/
/*			sx_rip_xml 					*/
/************************************************************************/

ST_RET sx_rip_xml (SX_DEC_CTRL *sxDecCtrl)
  {
SX_DEC_ELEMENT_INFO *sxDecElInfo;
ST_CHAR *xml;
ST_INT  nestLevel;
ST_CHAR *tagDest;
ST_CHAR *attribNameDest;
ST_CHAR *attribValDest;
ST_CHAR attribValCopy[SX_MAX_ATTR_VALUE];
ST_INT len;
ST_INT midBuff;
ST_INT numChRead;
ST_LONG xmlLen;
ST_CHAR *xmlEnd = NULL;		/* init to avoid compiler warning	*/
ST_CHAR c;
ST_BOOLEAN bEmptyTag;
ST_BOOLEAN eof;
ST_BOOLEAN sawStartTag = SD_FALSE;
ST_CHAR lineBuf[MAX_BYTES_IN_CHUNK];

  sxDecElInfo = &sxDecCtrl->sxDecElInfo;
  
  bEmptyTag = SD_FALSE;
  
  if (sxDecCtrl->useFp == SD_TRUE)
    {
    memset (lineBuf, 0, MAX_BYTES_IN_CHUNK);
    eof = SD_FALSE;
    midBuff = MAX_BYTES_IN_CHUNK / 2;
  
    /* get the first (MAX_BTYES_IN_CHUNK / 2) chunk from the file */
    numChRead = fread(lineBuf + midBuff, 1, MAX_BYTES_IN_CHUNK - midBuff, sxDecCtrl->fp);
    if (numChRead > 0)
      xml = lineBuf + midBuff;
    else
      {
      SXLOG_NERR0 ("SX decode error: could not read xml from file");
      sxDecCtrl->errCode = SD_FAILURE;
      return (SD_FAILURE);
      }
    }
  else
    {
    sxDecElInfo = &sxDecCtrl->sxDecElInfo;
    xml = sxDecCtrl->xmlStart;
    xmlLen = sxDecCtrl->xmlLen;
    xmlEnd = xml + xmlLen;
    }

  while (SX_RIP_NOT_DONE)
    {
/* Find a begin or end tag */
    while (*xml != '<' && SX_RIP_NOT_DONE)
      SX_LOAD_CHAR (1);

    if (SX_RIP_DONE)
      break;
   
   sawStartTag = SD_TRUE;

   sxDecElInfo->tagStart = xml;
   
/* OK, this should be the start of a start tag, an end tag, or a comment */
/* or block of binary CDATA						 */
    SX_LOAD_CHAR (1);
    if (strncmp (xml, "!--", 3) == 0 || *xml == '?') /* Comment */
      {
      while (strncmp (xml, "-->", 3) != 0 && 
           strncmp (xml, "?>", 2)  != 0 &&
           SX_RIP_NOT_DONE)
        {
        SX_LOAD_CHAR (1);
        }
      }
    else if (strncmp (xml, "![CDATA[", 8) == 0) /* CDATA */
      {
      while (strncmp (xml, "]]>", 3) != 0 &&
	     SX_RIP_NOT_DONE)
	{
	SX_LOAD_CHAR (1);
	}
      }
    else if (strncmp (xml, "!DOCTYPE", 8) == 0) /* DocType */
      {
      nestLevel = 0;
      while (SD_TRUE)
        {
        if (*xml == '>')
          {
          if (nestLevel == 0)
            break;
          else
            --nestLevel;
          }
        if (*xml == '<')
          ++nestLevel;

        SX_LOAD_CHAR (1);
        if (SX_RIP_DONE)
          {
          SXLOG_NERR0 ("SX decode error: could not find DOCTYPE end");
          sxDecCtrl->errCode = SD_FAILURE;
          return (SD_FAILURE);
	  }
	}
      SX_LOAD_CHAR (1);
      }
    else if (*xml != '/')	/* Begin tag */
      {
    /* We have a element tag start, get the tag  first  */
      tagDest = sxDecElInfo->tag;
      len = 0;
      while (SX_RIP_NOT_DONE)
        {
        c = *xml;
        if (c == '>' || c == ' ' || c == '/' || c == 10 || c == 9 || c == 13) 	/* Found the end of the tag */
          break;

        *(tagDest++) = c;
        ++len;
        if (len >= SX_MAX_TAG_LEN)
          {
          SXLOG_NERR0 ("SX decode error: tag too long");
          sxDecCtrl->errCode = SD_FAILURE;
          return (SD_FAILURE);
          }
        SX_LOAD_CHAR (1);
	if (sxDecCtrl->ignoreNS && c == ':')
	  {
          tagDest = sxDecElInfo->tag;
      	  len = 0;
	  }
        }
      if (SX_RIP_DONE)
        {
        SXLOG_NERR0 ("SX decode error: could not find tag end");
        sxDecCtrl->errCode = SD_FAILURE;
        return (SD_FAILURE);
        }
      *tagDest = 0;		/* terminate the tag */

    /* Now look for attributes */         
      sxDecElInfo->attrCount = 0;
      while (*xml != '>' && *xml != '/') /* we could have attributes! */
        {
      /* skip any whitespace before the start of the attribute name */
        while (*xml == ' ' && SX_RIP_NOT_DONE)
          SX_LOAD_CHAR (1);

        if (SX_RIP_DONE)
          {
          SXLOG_NERR0 ("SX decode error: could not find attribute name");
          sxDecCtrl->errCode = SD_FAILURE;
          return (SD_FAILURE);
          }

        if (*xml != '>' && *xml != '/')
          {
          if (sxDecElInfo->attrCount >= SX_MAX_ATTRIB)
            {
            SXLOG_NERR0 ("SX decode error: too many attributes. Look at SX_MAX_ATTRIB define");
            sxDecCtrl->errCode = SD_FAILURE;
            return (SD_FAILURE);
            }

        /* This should be the start of an attribute name */ 
          attribNameDest = sxDecElInfo->attr[sxDecElInfo->attrCount].name;
          len = 0;
          while (SX_RIP_NOT_DONE)
            {
            c = *xml;
            if (c == '=' || c == ' ')	/* Found the end of the name */
              break;

            if (c == '>') /* this isn't really an attribute like we first thought */
              break;
       
            if (c != 9 && c != 10 && c != 13) /* dont include these characters */
              {
              *(attribNameDest++) = c;
              ++len;
              }

            if (len >= SX_MAX_ATTR_NAME)
              {
              SXLOG_NERR0 ("SX decode error: attribute name too long");
              sxDecCtrl->errCode = SD_FAILURE;
              return (SD_FAILURE);
              }
            SX_LOAD_CHAR (1);
	    if (sxDecCtrl->ignoreNS && c == ':')
	      {
              attribNameDest = sxDecElInfo->attr[sxDecElInfo->attrCount].name;
              len = 0;
	      }
            }

          if (SX_RIP_DONE)
            {
            SXLOG_NERR0 ("SX decode error: could not find attribute name end");
            sxDecCtrl->errCode = SD_FAILURE;
            return (SD_FAILURE);
            }

          if (*xml == '>') /* this isn't really an attribute like we first thought */
            break;

          *attribNameDest = 0;		/* terminate the attrib name */

        /* skip to the attribute '=' */
          while (*xml != '=' && SX_RIP_NOT_DONE)
            SX_LOAD_CHAR (1);
          if (SX_RIP_DONE)
            {
            SXLOG_NERR0 ("SX decode error: could not find attribute '='");
            sxDecCtrl->errCode = SD_FAILURE;
            return (SD_FAILURE);
            }
          SX_LOAD_CHAR (1);
	  
        /* skip white space */
          while ((*xml == ' ' || *xml == '\t') && SX_RIP_NOT_DONE)
            SX_LOAD_CHAR (1);
	    
          /* OK, get the attrib value */
          if (*(xml) != '"' && *(xml) != '\'')		/* skip the opening " */
            {
            SXLOG_NERR0 ("SX decode error: could not find leading attribute value '\"'");
            sxDecCtrl->errCode = SD_FAILURE;
            return (SD_FAILURE);
            }

          SX_LOAD_CHAR (1);
        
           /* At the start of the attribute value */ 
          attribValDest = sxDecElInfo->attr[sxDecElInfo->attrCount].value;
          len = 0;
          while (SX_RIP_NOT_DONE)
            {
            c = *xml;
            if (c == '"' || c == '\'')	/* Found the end of the attrib */
              break;
          
            if (c != 9 && c != 10 && c != 13) /* dont include these characters */
              {
              *(attribValDest++) = c;
              ++len;
              }

            if (len >= SX_MAX_ATTR_VALUE)
              {
              SXLOG_NERR0 ("SX decode error: attribute value too long");
              sxDecCtrl->errCode = SD_FAILURE;
              return (SD_FAILURE);
              }
            SX_LOAD_CHAR (1);
            }
          if (SX_RIP_DONE)
            {
            SXLOG_NERR0 ("SX decode error: could not find closing attribute value '\"'");
            sxDecCtrl->errCode = SD_FAILURE;
            return (SD_FAILURE);
            }

          if (*(xml) != '"' && *(xml) != '\'')		/* skip the closing " */
            {
            SXLOG_NERR0 ("SX decode error: could not find closing attribute value '\"'");
            sxDecCtrl->errCode = SD_FAILURE;
            return (SD_FAILURE);
            }
          SX_LOAD_CHAR (1);

          *attribValDest = 0;	/* terminate the attrib value */
          strcpy (attribValCopy, sxDecElInfo->attr[sxDecElInfo->attrCount].value);
          sx_format_string_dec (sxDecElInfo->attr[sxDecElInfo->attrCount].value, attribValCopy);
          ++sxDecElInfo->attrCount;
          }

        if (SX_RIP_DONE)
          {
          SXLOG_NERR0 ("SX decode error: could not find tag end");
          sxDecCtrl->errCode = SD_FAILURE;
          return (SD_FAILURE);
          }
        }

      /* Could be empty tag */
      if (*xml == '/')
	{
	bEmptyTag = SD_TRUE;
	while (*xml != '>')
          SX_LOAD_CHAR (1);
	}
      SX_LOAD_CHAR (1); /* skip the '>' */

    /* OK, now call the element start function */
      sxDecElInfo->entityStart = xml;
      sxDecElInfo->entityEnd = xml;
      sxDecCtrl->xmlPos  = xml;	/* Save current dec position 	*/

      sxStartElement (sxDecCtrl); 
      /* Fail on any error except convert error */
      if (sxDecCtrl->errCode != SD_SUCCESS && sxDecCtrl->errCode != SX_ERR_CONVERT)
	{
	return (SD_FAILURE);
	}

      if (bEmptyTag)
	{
        /* OK, now call the element end function */
	sxEndElement (sxDecCtrl);
	bEmptyTag = SD_FALSE;
	}
      SX_LOAD_CHAR (sxDecCtrl->xmlPos - xml);
      sxDecCtrl->xmlPos  = xml;	/* Save current dec position 	*/
      }
    else		/* End tag */
      {
      sxDecElInfo->entityEnd = xml - 1;
      SX_LOAD_CHAR (1);
      tagDest = sxDecElInfo->tag;
      len = 0;
      while (SX_RIP_NOT_DONE)
        {
        c = *xml;
        if (c == '>' || c == ' ')	/* Found the end of the tag */
          break;

        *(tagDest++) = c;
        ++len;
        if (len >= SX_MAX_TAG_LEN)
          {
          SXLOG_NERR0 ("SX decode error: tag too long");
          sxDecCtrl->errCode = SD_FAILURE;
          return (SD_FAILURE);
          }
        SX_LOAD_CHAR (1);
	if (sxDecCtrl->ignoreNS && c == ':')
	  {
          tagDest = sxDecElInfo->tag;
          len = 0;
	  }
        }
      if (SX_RIP_DONE)
        {
        SXLOG_NERR0 ("SX decode error: could not find tag end");
        sxDecCtrl->errCode = SD_FAILURE;
        return (SD_FAILURE);
        }
      *tagDest = 0;		/* terminate the tag */


      /* any white space up to the end of the tag name */
      while (*xml != '>' && SX_RIP_NOT_DONE)
        SX_LOAD_CHAR (1);
      if (SX_RIP_DONE)
        {
        SXLOG_NERR0 ("SX decode error: could not find tag end");
        sxDecCtrl->errCode = SD_FAILURE;
        return (SD_FAILURE);
        }
      SX_LOAD_CHAR (1);

      sxDecCtrl->xmlPos  = xml;	/* Save current dec position 	*/
      
    /* OK, now call the element end function */
      sxEndElement (sxDecCtrl);
      /* Fail on any error except convert error */
      if (sxDecCtrl->errCode != SD_SUCCESS && sxDecCtrl->errCode != SX_ERR_CONVERT)
	{
	return (SD_FAILURE);
	}
      }
    }
    
  if (!sawStartTag)
    {
    SXLOG_ERR0 ("SX decode error: could not find start tag");
    sxDecCtrl->errCode = SD_FAILURE;
    return (SD_FAILURE);
    }
  else
    return (SD_SUCCESS);
  }

ST_VOID sx_load_characters (ST_CHAR *lineBuf, ST_BOOLEAN *eof, FILE *fp, ST_INT numToRead)
{
ST_LONG numChRead;
ST_LONG i;

  if (numToRead > 0)
    {
    /* first shift the characters by numToRead */
    memmove (lineBuf, &lineBuf[numToRead], MAX_BYTES_IN_CHUNK - numToRead);
  
    if (*eof != SD_TRUE)
      {
      numChRead = fread(lineBuf + (MAX_BYTES_IN_CHUNK - numToRead), 1, numToRead, fp);
      if (numChRead == 0)
        {
        *eof = SD_TRUE;
        for (i = 1; i < numToRead + 1; i++)
          {
          lineBuf[MAX_BYTES_IN_CHUNK - i] = '\0';
          }
        }
      }
    }
  }
#else	/* USE_EXPAT	*/


/************************************************************************/
/*			expatHandlerStart				*/
/* Normal 'start tag' handler.						*/
/* Log and set "errCode" if any string is too long to be stored.	*/
/************************************************************************/
static void XMLCALL expatHandlerStart(void *userData, const char *el, const char **attr)
  {
  SX_DEC_CTRL *sxDecCtrl = (SX_DEC_CTRL *) userData;
  int i;
  const ST_CHAR *ptr;

  if (!sxDecCtrl->termFlag)
    {
    /* Copy tag to sxDecCtrl	*/
    /* If ignoreNS flag set, ignore namespace prefix on tag.	*/
    if (sxDecCtrl->ignoreNS)
      {
      /* If ':' in the string, copy only text after ':'	*/
      if ((ptr = strrchr (el, ':')) != NULL)
        ptr += 1;	/* point after ':'	*/
      else
        ptr = el;
      }
    else
      ptr = el;
  
    if (strlen (ptr) < SX_MAX_TAG_LEN)
      strcpy (sxDecCtrl->sxDecElInfo.tag, ptr);
    else
      {
      SXLOG_ERR1 ("start tag '%s' too long. Can't be stored.", ptr);
      sxDecCtrl->errCode = SD_FAILURE;
      }
  
    /* Copy attributes to sxDecCtrl	*/
    for (i = 0; attr[i]; i += 2)
      {
      if (strlen (attr[i]) < SX_MAX_ATTR_NAME)
        strcpy (sxDecCtrl->sxDecElInfo.attr[i/2].name, attr[i]);
      else
        {
        SXLOG_ERR1 ("attr name '%s' too long. Can't be stored.", ptr);
        sxDecCtrl->errCode = SD_FAILURE;
        }
  
      if (strlen (attr[i+1]) < SX_MAX_ATTR_VALUE)
        strcpy (sxDecCtrl->sxDecElInfo.attr[i/2].value, attr[i+1]);
      else
        {
        SXLOG_ERR1 ("attr value '%s' too long. Can't be stored.", ptr);
        sxDecCtrl->errCode = SD_FAILURE;
        }
      }
    sxDecCtrl->sxDecElInfo.attrCount = i/2;
    sxDecCtrl->entityLen = 0;	/* reset entityLen	*/
    sxStartElement (sxDecCtrl);
    }	/* !termFlag	*/
  }

/************************************************************************/
/*			expatHandlerEnd					*/
/* Normal 'end tag' handler.						*/
/************************************************************************/
static void XMLCALL expatHandlerEnd(void *userData, const char *el)
  {
  SX_DEC_CTRL *sxDecCtrl = (SX_DEC_CTRL *) userData;

  if (!sxDecCtrl->termFlag)
    {
    strcpy (sxDecCtrl->sxDecElInfo.tag, el);
    sxEndElement (sxDecCtrl);
    sxDecCtrl->entityLen = 0;	/* reset entityLen	*/
    }	/* !termFlag	*/
  }

/************************************************************************/
/*			expatHandlerData				*/
/* Normal data handler.							*/
/* Log and set "errCode" if data is too long to be stored.		*/
/************************************************************************/
static void XMLCALL expatHandlerData(void *userData,
                                    const XML_Char *s,
                                    int len)
  {
  SX_DEC_CTRL *sxDecCtrl = (SX_DEC_CTRL *) userData;
  ST_INT sizeNeeded;	/* buffer size needed to store this data	*/

  if (!sxDecCtrl->termFlag)
    {
    sizeNeeded = len + sxDecCtrl->entityLen;

    /* Save data to access with "sx_get_entity" later.	*/
    if (sizeNeeded > sxDecCtrl->entityBufSize)
      {
      /* Reallocate buffer twice as big as currently needed.	*/
      sxDecCtrl->entityBufSize = sizeNeeded * 2;
      SXLOG_FLOW1 ("entity buffer too small. Reallocating entity buffer size = %d", sxDecCtrl->entityBufSize);
      sxDecCtrl->entityBuf = chk_realloc (sxDecCtrl->entityBuf, sxDecCtrl->entityBufSize);
      }

    memcpy (&(sxDecCtrl->entityBuf [sxDecCtrl->entityLen]), s, len);
    sxDecCtrl->entityLen += len;
    }	/* !termFlag	*/
  }

/************************************************************************/
/*			expatHandlerStartSkip				*/
/* The 'start tag' handler when tags are being skipped.			*/
/************************************************************************/
static void XMLCALL expatHandlerStartSkip(void *userData, const char *el, const char **attr)
  {
  SX_DEC_CTRL *sxDecCtrl = (SX_DEC_CTRL *) userData;

  if (!sxDecCtrl->termFlag)
    {
    /* Not calling normal start funct, so must increment nest level here*/
    sxDecCtrl->xmlNestLevel++;
    }	/* !termFlag	*/
  }

/************************************************************************/
/*			expatHandlerEndSkip				*/
/* The 'end tag' handler when tags are being skipped.			*/
/************************************************************************/
static void XMLCALL expatHandlerEndSkip(void *userData, const char *el)
  {
  SX_DEC_CTRL *sxDecCtrl = (SX_DEC_CTRL *) userData;

  if (!sxDecCtrl->termFlag)
    {
    if (sxDecCtrl->xmlNestLevel == sxDecCtrl->skipNestLevel)
      {
      SXLOG_DEC1 ("End element   '%s' (contents skipped)", el);
      /* Go back to normal handlers.	*/
      XML_SetCharacterDataHandler(sxDecCtrl->parser, expatHandlerData);
      XML_SetElementHandler(sxDecCtrl->parser, expatHandlerStart, expatHandlerEnd);
      }

    /* Not calling normal end funct, so must decrement nest level here*/
    sxDecCtrl->xmlNestLevel--;
    }	/* !termFlag	*/
  }
/************************************************************************/
/*			setup_expat					*/
/* NOTE: If "sxDecCtrl->termFlag" is set by user (i.e. error found),	*/
/*       let parse complete, but ignore data (see "expatHandler*").	*/
/************************************************************************/
static XML_Parser setup_expat(SX_DEC_CTRL *sxDecCtrl)
  {
  XML_Parser parser = XML_ParserCreate(NULL);
  if (! parser)
    {
    SXLOG_ERR0 ("Couldn't allocate memory for XML parser");
    return (parser);
    }
  XML_SetUserData (parser, sxDecCtrl);	/* passes sxDecCtrl to handlers	*/
  XML_SetCharacterDataHandler(parser, expatHandlerData);
  XML_SetElementHandler(parser, expatHandlerStart, expatHandlerEnd);
  sxDecCtrl->parser = parser;	/* CRITICAL: save parser to use in callbacks*/

  return (parser);
  }
/************************************************************************/
/*			sx_rip_xml_file					*/
/* Parse XML from a file. The caller must open a file			*/
/* and store the file ptr in "sxDecCtrl->fp". It reads one segment	*/
/* at a time from the file and passes it to XML_Parse.			*/
/************************************************************************/
ST_RET sx_rip_xml_file (SX_DEC_CTRL *sxDecCtrl)
  {
  XML_Parser parser;		/* Expat parser control structure	*/
  ST_RET retcode = SD_SUCCESS;
  char *parseBuf;	/* temporary buffer to store text read from input file	*/

  if (!(parser = setup_expat (sxDecCtrl)))
    retcode = SD_FAILURE;	/* error already logged	in setup_expat	*/
  else
    {
    /* Allocate a reasonable size entity buffer.		*/
    /* This may be reallocated later if it is too small.	*/
    sxDecCtrl->entityBufSize = ENTITY_BUF_SIZE;
    sxDecCtrl->entityBuf = chk_malloc (sxDecCtrl->entityBufSize);
  
    parseBuf = chk_malloc (EXPAT_BUF_SIZE);

    /* In a loop, read the file one segment at a time & pass to parser.	*/
    for (;;)
      {
      int done;
      int len;
  
      len = fread(parseBuf, 1, EXPAT_BUF_SIZE, sxDecCtrl->fp);
      if (ferror(sxDecCtrl->fp))
        {
        fprintf(stderr, "Read error\n");
        retcode = SD_FAILURE;
        break;	/* stop now	*/
        }
      done = feof(sxDecCtrl->fp);
  
      if (XML_Parse(parser, parseBuf, len, done) == XML_STATUS_ERROR)
        {
        SXLOG_ERR2 ("XML parse error at line %d: %s\n",
                XML_GetCurrentLineNumber(parser),
                XML_ErrorString(XML_GetErrorCode(parser)));
        retcode = SD_FAILURE;
        break;	/* stop now	*/
        }
  
      if (done)
        break;
      }	/* end main loop	*/
    chk_free (parseBuf);
    chk_free (sxDecCtrl->entityBuf);
    }
  return (retcode);
  }
/************************************************************************/
/*			sx_rip_xml_mem					*/
/* Parse XML from a memory buffer. The caller must set			*/
/* the following members of sxDecCtrl before calling this function:	*/
/*   sxDecCtrl->xmlStart points to the buffer.				*/
/*   sxDecCtrl->xmlLen contains the length of the buffer.		*/
/* It simply sets up the parse and passes the buffer to XML_Parse.	*/
/************************************************************************/
ST_RET sx_rip_xml_mem (SX_DEC_CTRL *sxDecCtrl)
  {
  XML_Parser parser;		/* Expat parser control structure	*/
  int done = SD_TRUE;	/* indicates to parser that all data is being passed*/
  ST_RET retcode = SD_SUCCESS;

  if (!(parser = setup_expat (sxDecCtrl)))
    retcode = SD_FAILURE;	/* error already logged	in setup_expat	*/
  else
    {
    /* Allocate a reasonable size entity buffer.		*/
    /* This may be reallocated later if it is too small.	*/
    sxDecCtrl->entityBufSize = ENTITY_BUF_SIZE;
    sxDecCtrl->entityBuf = chk_malloc (sxDecCtrl->entityBufSize);

    /* Just pass data all at once to XML_Parse.	*/
    if (XML_Parse(parser, sxDecCtrl->xmlStart, sxDecCtrl->xmlLen, done) == XML_STATUS_ERROR)
      {
      SXLOG_ERR2 ("XML parse error at line %d: %s\n",
                XML_GetCurrentLineNumber(parser),
                XML_ErrorString(XML_GetErrorCode(parser)));
      retcode = SD_FAILURE;
      }
    chk_free (sxDecCtrl->entityBuf);
    }
  
  return (retcode);
  }
#endif	/* USE_EXPAT	*/

