/************************************************************************/
/* SISCO SOFTWARE MODULE HEADER *****************************************/
/************************************************************************/
/*   (c) Copyright Systems Integration Specialists Company, Inc.,       */
/*	2004-2006, All Rights Reserved					*/
/*                                                                      */
/* MODULE NAME : sclparse.c                                             */
/* PRODUCT(S)  :                                                        */
/*                                                                      */
/* MODULE DESCRIPTION : This routine parses XML files conforming to the */
/*                      SCL object model.			        */
/*                                                                      */
/* GLOBAL FUNCTIONS DEFINED IN THIS MODULE :                            */
/*                                                                      */
/* MODIFICATION LOG :                                                   */
/*  Date     Who   Rev          Comments                                */
/* --------  ---  ------   -------------------------------------------  */
/* 05/06/08  JRB    23     Chk ReportControl indexed attr (must be true)*/
/* 04/23/08  JRB    22     Parse & save "SettingControl" info (SGCB).	*/
/*			   Save dchg, qchg, dupd values in SCL_DA struct*/
/* 01/18/08  JRB    21     Allow max LN prefix+inst=11. IEC 61850 max=7.*/
/*			   Must also reduce max suffix in mvlu_rt.c to	*/
/*			   avoid overflow.				*/
/* 01/18/08  JRB    20     Chg datSet in GSEControl & LogControl to	*/
/*			   optional to match Schema (see tControl def).	*/
/* 07/10/07  JRB    19     Check length of lnClass, prefix, & inst.	*/
/* 06/12/07  JRB    18     Save VLANID in SCL_GSE & SCL_SMV.		*/
/* 09/08/06  JRB    17     Fix dstlen type in convert_mac.		*/
/* 07/26/06 LWP/JRB 16     Parse & save GOOSE & SMV addressing info from*/
/*			   "Communication" section.			*/
/* 04/19/06  JRB    15     Make datSet in ReportControl "optional".	*/
/* 04/19/06  JRB    14     Parse & save "RptEnabled" info.		*/
/* 04/04/06  JRB    13     Parse & save "SampledValueControl" info.	*/
/* 03/25/06  JRB    12     Change order of operations: call create/add	*/
/*			   functions first, then copy data directly to	*/
/*			   SCL structs (eliminates intermediate copy).	*/
/*			   Repl scl_get_attr (could overflow caller buf)*/
/*			   with scl_get_attr_ptr & scl_get_attr_copy.	*/
/*			   Replace CONTROL_BLOCK with SCL_RCB & SCL_LCB,*/
/*			   Fix _DA_SEFun setting of dchg, qchg, & dupd.	*/
/*			   Fix sx_get_string calls to prevent overflow.	*/
/*			   Fix "*_Val_*" functions.			*/
/*			   Chg desc to ptr (allocated during parse).	*/
/* 07/25/05  JRB    11     Make nameStructure optional, default=IEDName.*/
/* 06/24/05  JRB    10     Call sx_parseExx_mt (faster).		*/
/*			   Chg "ord" from unsigned to signed value.	*/
/* 05/17/05  DWL    09     Use sx_push instead of SX_PUSH, and force	*/
/*			   user code to call sx_pop.  Eliminated some	*/
/*			   unnecessary debug logging.			*/
/* 05/17/05  JRB    08     Avoid allocating struct in scl_parse.	*/
/* 02/15/05  JRB    07     Save iedName in scl_info.			*/
/*			   Parse Header & save in scl_info.		*/
/*			   Fix some log messages.			*/
/* 12/07/04  JRB    06     Del unneeded memsets after callocs.		*/
/* 08/23/04  JRB    05     Ret err if iedName or accessPoint.. not found*/
/* 08/06/04  JRB    04     Del "sclparse.h", use "scl.h".		*/
/* 07/23/04  JRB    03     Del unused static functions.			*/
/* 07/15/04  DWL    02     Added LogControl & GSEControl elements	*/
/*			   Handle LN and LN0 differently.		*/
/* 06/10/04  DWL    01     Created					*/
/************************************************************************/

#include "glbtypes.h"
#include "sysincs.h"
#include "mem_chk.h"
#include "sx_defs.h"
#include "sx_log.h"
#include "str_util.h"
#include "slog.h"
#include "scl.h"	/* SCL file processing structs & functions	*/
#include "mvl_uca.h"
#include "smpval.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

#define SCL_ATTR_OPTIONAL 	0	/* attribute is optional	*/
#define SCL_ATTR_REQUIRED 	1	/* attribute is required	*/

typedef struct scl_dec_ctrl
  {
  ST_CHAR *iedName;
  ST_CHAR *accessPointName;
  ST_BOOLEAN accessPointFound;	/* SD_TRUE if IED and AccessPoint found	*/
  ST_BOOLEAN iedNameMatched;	
  ST_BOOLEAN accessPointMatched;
  SCL_INFO *sclInfo;
  SCL_GSE *scl_gse;	/* Used for "GSE" in "Communication" section	*/
  SCL_SMV *scl_smv;	/* Used for "SMV" in "Communication" section	*/
  SCL_LD *scl_ld;	/* Used for "LDevice"				*/
  SCL_LN *scl_ln;	/* Used for "LN" (Logical Node)			*/
  SCL_RCB *scl_rcb;	/* alloc to store ReportControl info		*/
  SCL_LCB *scl_lcb;	/* alloc to store LogControl info		*/
  ST_UINT8 TrgOps[1];	/* Used for ReportControl or LogControl.	*/
			/* Copied to SCL_RCB or SCL_LCB.		*/
  SCL_SVCB *scl_svcb;	/* Used for "SampledValueControl".	*/
  SCL_ENUMVAL *scl_enumval;	/* Used for "EnumVal".			*/
  SCL_DAI *scl_dai;	/* Used for "DAI".				*/
  SCL_DA *scl_da;	/* Used for "DA".				*/
  SCL_BDA *scl_bda;	/* Used for "BDA".				*/
  ST_CHAR flattened[MAX_FLAT_LEN + 1];	/* Created by concatenating values*/
					/* from DOI, SDI, and DAI elements*/
  } SCL_DEC_CTRL;

/************************************************************************/
static ST_VOID _SCL_SEFun (SX_DEC_CTRL *sxDecCtrl);
static ST_VOID _Header_SFun (SX_DEC_CTRL *sxDecCtrl);

static ST_VOID _Communication_SEFun (SX_DEC_CTRL *sxDecCtrl);
static ST_VOID _SubNetwork_SEFun (SX_DEC_CTRL *sxDecCtrl);
static ST_VOID _ConnectedAP_SEFun (SX_DEC_CTRL *sxDecCtrl);
static ST_VOID _GSE_SEFun (SX_DEC_CTRL *sxDecCtrl);
static ST_VOID _GSE_Address_SEFun (SX_DEC_CTRL *sxDecCtrl);
static ST_VOID _GSE_Address_P_SEFun (SX_DEC_CTRL *sxDecCtrl);
static ST_VOID _SMV_SEFun (SX_DEC_CTRL *sxDecCtrl);
static ST_VOID _SMV_Address_SEFun (SX_DEC_CTRL *sxDecCtrl);
static ST_VOID _SMV_Address_P_SEFun (SX_DEC_CTRL *sxDecCtrl);
/*renxiaobao GSE ½âÎö*/
static ST_VOID _GSE_MinTime_SEFun (SX_DEC_CTRL *sxDecCtrl);
static ST_VOID _GSE_MaxTime_SEFun (SX_DEC_CTRL *sxDecCtrl);

static ST_VOID _IED_SEFun (SX_DEC_CTRL *sxDecCtrl);
static ST_VOID _AccessPoint_SEFun (SX_DEC_CTRL *sxDecCtrl);
static ST_VOID _Server_SEFun (SX_DEC_CTRL *sxDecCtrl);
static ST_VOID _LDevice_SEFun (SX_DEC_CTRL *sxDecCtrl);
static ST_VOID _LN_SEFun (SX_DEC_CTRL *sxDecCtrl);
static ST_VOID _DataSet_SEFun (SX_DEC_CTRL *sxDecCtrl);
static ST_VOID _FCDA_SFun (SX_DEC_CTRL *sxDecCtrl);
static ST_VOID _ReportControl_SEFun (SX_DEC_CTRL *sxDecCtrl);
static ST_VOID _LogControl_SEFun (SX_DEC_CTRL *sxDecCtrl);
static ST_VOID _GSEControl_SFun (SX_DEC_CTRL *sxDecCtrl);
static ST_VOID _SettingControl_SFun (SX_DEC_CTRL *sxDecCtrl);
static ST_VOID _TrgOps_SFun (SX_DEC_CTRL *sxDecCtrl);
static ST_VOID _OptFlds_SFun (SX_DEC_CTRL *sxDecCtrl);
static ST_VOID _RptEnabled_SFun (SX_DEC_CTRL *sxDecCtrl);
static ST_VOID _DOI_SEFun (SX_DEC_CTRL *sxDecCtrl);
static ST_VOID _SDI_SEFun (SX_DEC_CTRL *sxDecCtrl);
static ST_VOID _DAI_SEFun (SX_DEC_CTRL *sxDecCtrl);
static ST_VOID _DAI_Val_SEFun (SX_DEC_CTRL *sxDecCtrl);
static ST_VOID _DataTypeTemplates_SEFun (SX_DEC_CTRL *sxDecCtrl);
static ST_VOID _LNodeType_SEFun (SX_DEC_CTRL *sxDecCtrl);
static ST_VOID _DO_SFun (SX_DEC_CTRL *sxDecCtrl);
static ST_VOID _DOType_SEFun (SX_DEC_CTRL *sxDecCtrl);
static ST_VOID _DA_SEFun (SX_DEC_CTRL *sxDecCtrl);
static ST_VOID _SDO_SFun (SX_DEC_CTRL *sxDecCtrl);
static ST_VOID _DAType_SEFun (SX_DEC_CTRL *sxDecCtrl);
static ST_VOID _DA_Val_SEFun (SX_DEC_CTRL *sxDecCtrl);
static ST_VOID _BDA_SEFun (SX_DEC_CTRL *sxDecCtrl);
static ST_VOID _BDA_Val_SEFun (SX_DEC_CTRL *sxDecCtrl);
static ST_VOID _EnumType_SEFun (SX_DEC_CTRL *sxDecCtrl);
static ST_VOID _EnumVal_SEFun (SX_DEC_CTRL *sxDecCtrl);
static ST_RET _scl_unknown_el_start (SX_DEC_CTRL *sxDecCtrl, ST_CHAR *tag);
static ST_RET _scl_unknown_el_end (SX_DEC_CTRL *sxDecCtrl, ST_CHAR *tag);
static ST_VOID _SampledValueControl_SEFun (SX_DEC_CTRL *sxDecCtrl);
static ST_VOID _SampledValueControl_SFun (SX_DEC_CTRL *sxDecCtrl);
static ST_VOID _SampledValueControl_EFun (SX_DEC_CTRL *sxDecCtrl);
static ST_VOID _SmvOpts_SFun (SX_DEC_CTRL *sxDecCtrl);

/************************************************************************/
/************************************************************************/
/* Only the elements we need to extract are listed here.		*/
/* The rest are handled by "unknown" element handler.			*/
SX_ELEMENT sclStartElements[] = 
{
  {"SCL", 		SX_ELF_CSTARTEND,		_SCL_SEFun, NULL, 0}
};

SX_ELEMENT SCLElements[] = 
{
  {"Header",           	SX_ELF_CSTART|SX_ELF_OPT, 	_Header_SFun, NULL, 0},
  {"Communication",    	SX_ELF_CSTARTEND|SX_ELF_OPT, 	_Communication_SEFun, NULL, 0},
  {"IED",            	SX_ELF_CSTARTEND|SX_ELF_OPTRPT,	_IED_SEFun, NULL, 0},
  {"DataTypeTemplates", SX_ELF_CSTARTEND|SX_ELF_OPT, 	_DataTypeTemplates_SEFun, NULL, 0}
};

/************************************************************************/
/* Tables for mapping "Communication" elements.				*/
/************************************************************************/
SX_ELEMENT CommunicationElements[] = 
{
  {"SubNetwork",      	SX_ELF_CSTARTEND|SX_ELF_OPTRPT,	_SubNetwork_SEFun, NULL, 0}
};

SX_ELEMENT SubNetworkElements[] = 
{
  /* NOTE: "bitRate" and "Text" elements ignored.	*/
  {"ConnectedAP",      	SX_ELF_CSTARTEND|SX_ELF_OPTRPT,	_ConnectedAP_SEFun, NULL, 0}
};

SX_ELEMENT ConnectedAPElements[] = 
{
  /* DEBUG: add "Address". Ignore "PhyConn".	*/
  {"GSE",	      	SX_ELF_CSTARTEND|SX_ELF_OPTRPT,	_GSE_SEFun, NULL, 0},
  {"SMV",	      	SX_ELF_CSTARTEND|SX_ELF_OPTRPT,	_SMV_SEFun, NULL, 0}
};

SX_ELEMENT GSEElements[] = 
{
  {"Address",      	SX_ELF_CSTARTEND|SX_ELF_OPT, 	_GSE_Address_SEFun, NULL, 0},
  /*renxiaobao GSE ½âÎö*/
  {"MinTime",      	SX_ELF_CSTARTEND|SX_ELF_OPTRPT, _GSE_MinTime_SEFun, NULL, 0},
  {"MaxTime",      	SX_ELF_CSTARTEND|SX_ELF_OPTRPT, _GSE_MaxTime_SEFun, NULL, 0}
};
SX_ELEMENT GSEAddressElements[] = 
{
  {"P",      		SX_ELF_CSTARTEND|SX_ELF_OPTRPT,	_GSE_Address_P_SEFun, NULL, 0}
};

SX_ELEMENT SMVElements[] = 
{
  {"Address",      	SX_ELF_CSTARTEND|SX_ELF_OPT, 	_SMV_Address_SEFun, NULL, 0}
};

SX_ELEMENT SMVAddressElements[] = 
{
  {"P",      		SX_ELF_CSTARTEND|SX_ELF_OPTRPT,	_SMV_Address_P_SEFun, NULL, 0}
};
/************************************************************************/
/* Tables for mapping "IED" elements.					*/
/************************************************************************/
SX_ELEMENT IEDElements[] = 
{
  {"AccessPoint",      	SX_ELF_CSTARTEND|SX_ELF_RPT, 	_AccessPoint_SEFun, NULL, 0}
};

SX_ELEMENT AccessPointElements[] = 
{
  {"Server",      	SX_ELF_CSTARTEND, 		_Server_SEFun, NULL, 0}
};

SX_ELEMENT ServerElements[] = 
{
  {"LDevice",      	SX_ELF_CSTARTEND|SX_ELF_RPT,	_LDevice_SEFun, NULL, 0}
};

SX_ELEMENT LDeviceElements[] = 
{
  {"LN0",      		SX_ELF_CSTARTEND,		_LN_SEFun, NULL, 0},
  {"LN",      		SX_ELF_CSTARTEND|SX_ELF_OPTRPT,	_LN_SEFun, NULL, 0}
};

SX_ELEMENT LN0Elements[] = 
{
  {"DataSet",  		SX_ELF_CSTARTEND|SX_ELF_OPTRPT,	_DataSet_SEFun, NULL, 0},
  {"ReportControl",	SX_ELF_CSTARTEND|SX_ELF_OPTRPT,	_ReportControl_SEFun, NULL, 0},
  {"DOI",		SX_ELF_CSTARTEND|SX_ELF_OPTRPT,	_DOI_SEFun, NULL, 0}, 
  {"SampledValueControl",	SX_ELF_CSTARTEND|SX_ELF_OPTRPT,	_SampledValueControl_SEFun, NULL, 0},
  {"LogControl",	SX_ELF_CSTARTEND|SX_ELF_OPTRPT,	_LogControl_SEFun, NULL, 0},
  {"SettingControl",	SX_ELF_CSTART|SX_ELF_OPTRPT,	_SettingControl_SFun, NULL, 0},
  {"GSEControl",	SX_ELF_CSTART|SX_ELF_OPTRPT,	_GSEControl_SFun, NULL, 0}
};

SX_ELEMENT LNElements[] = 
{
  {"DataSet",  		SX_ELF_CSTARTEND|SX_ELF_OPTRPT,	_DataSet_SEFun, NULL, 0},
  {"ReportControl",	SX_ELF_CSTARTEND|SX_ELF_OPTRPT,	_ReportControl_SEFun, NULL, 0},
  {"DOI",		SX_ELF_CSTARTEND|SX_ELF_OPTRPT,	_DOI_SEFun, NULL, 0}, 
  {"SampledValueControl",	SX_ELF_CSTARTEND|SX_ELF_OPTRPT,	_SampledValueControl_SEFun, NULL, 0},
  {"LogControl",	SX_ELF_CSTARTEND|SX_ELF_OPTRPT,	_LogControl_SEFun, NULL, 0}
};

SX_ELEMENT DataSetElements[] = 
{
  {"FCDA",  		SX_ELF_CSTART|SX_ELF_RPT,	_FCDA_SFun, NULL, 0}
};

SX_ELEMENT ReportControlElements[] = 
{
  {"TrgOps",  		SX_ELF_CSTART|SX_ELF_OPT,	_TrgOps_SFun, NULL, 0},
  {"OptFields",		SX_ELF_CSTART,			_OptFlds_SFun, NULL, 0},
  {"RptEnabled",	SX_ELF_CSTART|SX_ELF_OPT,	_RptEnabled_SFun, NULL, 0}
};

SX_ELEMENT LogControlElements[] = 
{
  {"TrgOps",  		SX_ELF_CSTART|SX_ELF_OPT,	_TrgOps_SFun, NULL, 0}
};

SX_ELEMENT SampledValueControlElements[] = 
{
  {"SmvOpts",  		SX_ELF_CSTART,			_SmvOpts_SFun, NULL, 0}
};

SX_ELEMENT DOIElements[] = 
{
  {"SDI",  		SX_ELF_CSTARTEND|SX_ELF_OPTRPT,	_SDI_SEFun, NULL, 0},
  {"DAI",  		SX_ELF_CSTARTEND|SX_ELF_OPTRPT,	_DAI_SEFun, NULL, 0}
};

/* SDI can be nested under itself indefinitely */
SX_ELEMENT SDIElements[] = 
{
  {"SDI",  		SX_ELF_CSTARTEND|SX_ELF_OPTRPT,	_SDI_SEFun, NULL, 0},
  {"DAI",  		SX_ELF_CSTARTEND|SX_ELF_OPTRPT,	_DAI_SEFun, NULL, 0}
};

SX_ELEMENT DAIElements[] = 
{
  {"Val",  		SX_ELF_CSTARTEND|SX_ELF_OPT,	_DAI_Val_SEFun, NULL, 0}
};

/************************************************************************/
/* Tables for mapping "DataTypeTemplates" elements.			*/
/************************************************************************/
SX_ELEMENT DataTypeTemplatesElements[] = 
{
  {"LNodeType",  	SX_ELF_CSTARTEND|SX_ELF_RPT,	_LNodeType_SEFun, NULL, 0},
  {"DOType",  		SX_ELF_CSTARTEND|SX_ELF_RPT,	_DOType_SEFun, NULL, 0},
  {"DAType",  		SX_ELF_CSTARTEND|SX_ELF_OPTRPT,	_DAType_SEFun, NULL, 0},
  {"EnumType", 		SX_ELF_CSTARTEND|SX_ELF_OPTRPT,	_EnumType_SEFun, NULL, 0}
};

SX_ELEMENT LNodeTypeElements[] = 
{
  {"DO",  		SX_ELF_CSTART|SX_ELF_RPT,	_DO_SFun, NULL, 0}
};

SX_ELEMENT DOTypeElements[] = 
{
  {"DA",  		SX_ELF_CSTARTEND|SX_ELF_OPTRPT,	_DA_SEFun, NULL, 0},
  {"SDO",  		SX_ELF_CSTART|SX_ELF_OPTRPT,	_SDO_SFun, NULL, 0}
};

SX_ELEMENT DAElements[] = 
{
  {"Val",  		SX_ELF_CSTARTEND|SX_ELF_OPTRPT,	_DA_Val_SEFun, NULL, 0}
};

SX_ELEMENT DATypeElements[] = 
{
  {"BDA",  		SX_ELF_CSTARTEND|SX_ELF_RPT,	_BDA_SEFun, NULL, 0}
};

SX_ELEMENT BDAElements[] = 
{
  {"Val",  		SX_ELF_CSTARTEND|SX_ELF_OPTRPT,	_BDA_Val_SEFun, NULL, 0}
};

SX_ELEMENT EnumTypeElements[] = 
{
  {"EnumVal",  		SX_ELF_CSTARTEND|SX_ELF_RPT,	_EnumVal_SEFun, NULL, 0}
};

/************************************************************************/
/*			convert_mac					*/
/* Converts MAC string read from SCL file (like 01-02-03-04-05-06)	*/
/* to 6 byte hex MAC address.						*/
/************************************************************************/
#define MAX_MAC_STRING_LEN	60
static ST_RET convert_mac (ST_UCHAR *dst, ST_CHAR *src)
  {
ST_RET retcode;
ST_CHAR tmpbuf [MAX_MAC_STRING_LEN+1];
ST_CHAR *tmpptr;
ST_UINT dstlen;

  /* Input string may include extra blanks, so allow for fairly long string.*/
  if (strlen (src) > MAX_MAC_STRING_LEN)
    retcode = SD_FAILURE;
  else
    {
    /* Just replace each '-' with ' '. Then use ascii_to_hex_str to convert.*/
    tmpptr = tmpbuf;
    /* Copy until NULL terminator but ignore '-' and spaces.	*/
    for ( ;  *src;  src++)
      {
      if (*src != '-' && (!isspace(*src)))
        *tmpptr++ = *src;
      }
    *tmpptr = '\0';	/* NULL terminate temp buffer	*/
    retcode = ascii_to_hex_str (dst, &dstlen, 6, tmpbuf);
    if (retcode == SD_SUCCESS && dstlen != 6)
      retcode = SD_FAILURE; 
    }
  return (retcode);
  }
    
/************************************************************************/
/*			log_notfound_attr				*/
/************************************************************************/

static ST_VOID log_notfound_attr (ST_CHAR *attrName)
  {
  SXLOG_ERR1 ("SCL PARSE: Required attribute '%s' not found.", 
  			attrName);
  }

/************************************************************************/
/*			log_attr_str					*/
/************************************************************************/

static ST_VOID log_attr_str (ST_CHAR *name, ST_CHAR *value)
  {
  SXLOG_DEC2 ("SCL PARSE: Found attribute '%s', value is '%s'", name, value);
  }

/************************************************************************/
/*			log_attr_int					*/
/************************************************************************/

static ST_VOID log_attr_int (ST_CHAR *name, ST_INT value)
  {
  SXLOG_DEC2 ("SCL PARSE: Found attribute '%s', value is '%d'", name, value);
  }

/************************************************************************/
/*			log_attr_uint					*/
/************************************************************************/

static ST_VOID log_attr_uint (ST_CHAR *name, ST_UINT value)
  {
  SXLOG_DEC2 ("SCL PARSE: Found attribute '%s', value is '%u'", name, value);
  }

/************************************************************************/
/*			log_returned_failure				*/
/************************************************************************/

static ST_VOID log_returned_failure (ST_CHAR *funcName, ST_LONG ret)
  {
  SXLOG_ERR2 ("SCL PARSE: %s returned failure, err=%ld", funcName, ret);
  }

/************************************************************************/
/*			scl_stop_parsing				*/
/************************************************************************/

static ST_VOID scl_stop_parsing (SX_DEC_CTRL *sxDecCtrl, ST_CHAR *offender,
				 ST_RET errCode)
  {
  sxDecCtrl->errCode = errCode;
  sxDecCtrl->termFlag = SD_TRUE;
  if (errCode == SX_REQUIRED_TAG_NOT_FOUND)
    {
    log_notfound_attr (offender);
    }
  else /* SX_USER_ERROR */
    {
    log_returned_failure (offender, errCode);
    }
  }

/************************************************************************/
/*			scl_get_attr_ptr				*/
/* Get a pointer to an attr string stored in SX_DEC_CTRL.		*/
/* If attr found, SD_SUCCESS returned & "*value" points to string.	*/
/* NOTE: The pointer returned in "*value" might not be valid later	*/
/*	when parsing continues.						*/
/************************************************************************/

static ST_RET scl_get_attr_ptr (SX_DEC_CTRL *sxDecCtrl, ST_CHAR *name,
			        ST_CHAR **value, ST_BOOLEAN required)
  {
ST_RET ret;

  ret = sx_get_attr_ptr (sxDecCtrl, value, name);
  if (ret != SD_SUCCESS)
    {
    *value = NULL;	/* make sure ptr is NULL on error (better than garbage)*/
    if (required)
      scl_stop_parsing (sxDecCtrl, name, SX_REQUIRED_TAG_NOT_FOUND);
    }
  else
    log_attr_str (name, *value);

  return (ret);
  }

/************************************************************************/
/*			scl_get_attr_copy				*/
/* Get a pointer to an attr string stored in SX_DEC_CTRL.		*/
/* If strlen <= maxValueLen, copy string, else return error.		*/
/************************************************************************/

static ST_RET scl_get_attr_copy (SX_DEC_CTRL *sxDecCtrl, ST_CHAR *name,
			        ST_CHAR *value, ST_UINT maxValueLen, ST_BOOLEAN required)
  {
ST_RET ret;
ST_CHAR *pValue;

  ret = scl_get_attr_ptr (sxDecCtrl, name, &pValue, required);
  if (ret == SD_SUCCESS)
    {
    if (strlen (pValue) <= maxValueLen)
      strcpy (value, pValue);	/* copy string to caller's buffer	*/
    else
      {
      SXLOG_ERR3 ("Attribute Value='%s' exceeds max len '%d' for attribute '%s'", pValue, maxValueLen, name);
      scl_stop_parsing (sxDecCtrl, name, SX_USER_ERROR);
      ret = SD_FAILURE;
      }
    }

  return (ret);
  }

/************************************************************************/
/*			scl_get_int_attr				*/
/************************************************************************/

static ST_RET scl_get_int_attr (SX_DEC_CTRL *sxDecCtrl, ST_CHAR *name,
			        ST_INT *value, ST_BOOLEAN required)
  {
ST_RET ret;

  ret = sx_get_int_attr (sxDecCtrl, name, value);
  if (ret != SD_SUCCESS)
    {
    if (required)
      scl_stop_parsing (sxDecCtrl, name, SX_REQUIRED_TAG_NOT_FOUND);
    }
  else
    log_attr_int (name, *value);

  return (ret);
  }

/************************************************************************/
/*			scl_get_uint_attr				*/
/************************************************************************/

static ST_RET scl_get_uint_attr (SX_DEC_CTRL *sxDecCtrl, ST_CHAR *name,
			        ST_UINT *value, ST_BOOLEAN required)
  {
ST_RET ret;

  ret = sx_get_uint_attr (sxDecCtrl, name, value);
  if (ret != SD_SUCCESS)
    {
    if (required)
      scl_stop_parsing (sxDecCtrl, name, SX_REQUIRED_TAG_NOT_FOUND);
    }
  else
    log_attr_uint (name, *value);

  return (ret);
  }

/************************************************************************/
/*			 construct_flattened				*/
/* Construct a flattened variable name from DOI, SDI, DAI names.	*/
/************************************************************************/
ST_RET construct_flattened (ST_CHAR *flattened, size_t maxlen, ST_CHAR *name, ST_CHAR *ix)
  {
size_t ixlen;
ST_RET retCode;

  /* Calc space needed for optional [ix]	*/
  if (ix)
    ixlen = strlen(ix)+2;	/* string plus brackets	*/
  else
    ixlen = 0;
  /* Make sure there is room for [ix] and "$"	*/
  if (strlen (flattened) + strlen(name) + ixlen + 1 <= maxlen)
    {
    /* If flattened is now empty, just copy name, else add "$" then name.*/
    if (strlen(flattened) == 0)
      strcpy (flattened, name);
    else
      {
      strcat (flattened, "$");
      strcat (flattened, name);
      }
    if (ix)
      {      /* Add 'ix' to flattened if necessary.	*/
      strcat (flattened, "[");
      strcat (flattened, ix);
      strcat (flattened, "]");
      }
    retCode = SD_SUCCESS;
    }
  else
    {	/* flattened is big, so this error should never occur with normal SCL.*/
    SXLOG_ERR2 ("ERROR: not enough space to add name '%s' to flattened name '%s'", name, flattened);
    retCode = SD_FAILURE;
    }
  return (retCode);
  }

/************************************************************************/
/*			scl_parse					*/
/************************************************************************/

ST_RET scl_parse (ST_CHAR *xmlFileName, ST_CHAR *iedName, 
		  ST_CHAR *accessPointName, SCL_INFO *sclInfo)
  {
ST_RET ret;
SCL_DEC_CTRL sclDecCtrl = {0};	/* start with clean struct.	*/

  memset (sclInfo, 0, sizeof (SCL_INFO));    /* CRITICAL: start with clean struct*/

  /* Save iedName in sclInfo for later use.	*/
  strncpy_safe (sclInfo->iedName, iedName, MAX_IDENT_LEN);

  sclDecCtrl.iedName = iedName;
  sclDecCtrl.accessPointName = accessPointName;
  sclDecCtrl.accessPointFound = SD_FALSE;
  sclDecCtrl.sclInfo = sclInfo;

  ret = sx_parseExx_mt (xmlFileName, 
          sizeof (sclStartElements)/sizeof(SX_ELEMENT), sclStartElements,
          &sclDecCtrl, _scl_unknown_el_start, _scl_unknown_el_end);

  /* NOTE: sx_parseEx_mt doesn't log error if file open fails, so log here*/
  /* It may not log some other errors, so log any other error here too.	*/
  if (ret == SX_FILE_NOT_FOUND)
    SXLOG_ERR1 ("XML File (%s) Open Error",xmlFileName);
  else if (ret != SD_SUCCESS)
    SXLOG_ERR2 ("Error 0x%X parsing SCL file (%s)", ret, xmlFileName);

  /* If parsing successful, check if requested AccessPoint was found.	*/
  if (ret == SD_SUCCESS  &&  sclDecCtrl.accessPointFound == SD_FALSE)
    {
    SXLOG_ERR3 ("IED='%s' or AccessPoint='%s' not found in input file (%s)",
                iedName, accessPointName, xmlFileName);
    ret = SX_REQUIRED_TAG_NOT_FOUND;	/* new error code could be clearer*/
    }

  return (ret);
  }

/************************************************************************/
/************************************************************************/
/*			SCL_SEFun					*/
/************************************************************************/

static ST_VOID _SCL_SEFun (SX_DEC_CTRL *sxDecCtrl)
  {
  if (sxDecCtrl->reason == SX_ELEMENT_START)
    sx_push (sxDecCtrl, sizeof(SCLElements)/sizeof(SX_ELEMENT), SCLElements, SD_FALSE);
    
  else
    {
    while (sxDecCtrl->itemStackLevel > 0)
      sx_pop (sxDecCtrl);
    }
  }

/************************************************************************/
/*			_Header_SFun					*/
/************************************************************************/

static ST_VOID _Header_SFun (SX_DEC_CTRL *sxDecCtrl)
  {
SCL_DEC_CTRL *sclDecCtrl;
ST_CHAR *nameStructure;
ST_RET ret;
SCL_INFO *sclInfo;

  sclDecCtrl = (SCL_DEC_CTRL *) sxDecCtrl->usr;
  sclInfo = sclDecCtrl->sclInfo;

  /* Get required attributes	*/
  ret = scl_get_attr_copy (sxDecCtrl, "id", sclInfo->Header.id,
            (sizeof(sclInfo->Header.id)-1), SCL_ATTR_REQUIRED);
  if (ret != SD_SUCCESS)
    return;	/* At least one required attr not found. Stop now.	*/

  /* Handle optional "nameStructure".	*/
  if (scl_get_attr_ptr (sxDecCtrl, "nameStructure", &nameStructure, SCL_ATTR_OPTIONAL) == SD_SUCCESS)
    {
    if (strcmp (nameStructure, "IEDName") != 0)
      SXLOG_ERR1 ("Header attribute nameStructure='%s' not allowed. Assuming nameStructure='IEDName' (i.e. 'Product Naming')", nameStructure);
    }
  /* Always assume nameStructure="IEDName" (i.e. "Product Naming")	*/
  sclInfo->Header.nameStructure = SCL_NAMESTRUCTURE_IEDNAME;
  }

/************************************************************************/
/*			_Communication_SEFun				*/
/************************************************************************/
static ST_VOID _Communication_SEFun (SX_DEC_CTRL *sxDecCtrl)
  {
  if (sxDecCtrl->reason == SX_ELEMENT_START)
    {
    sx_push (sxDecCtrl, sizeof(CommunicationElements)/sizeof(SX_ELEMENT), 
             CommunicationElements, SD_FALSE);
    }
  else
    {
    sx_pop (sxDecCtrl);
    }
  }
/************************************************************************/
/*			_SubNetwork_SEFun				*/
/************************************************************************/
static ST_VOID _SubNetwork_SEFun (SX_DEC_CTRL *sxDecCtrl)
  {
SCL_DEC_CTRL *sclDecCtrl = (SCL_DEC_CTRL *) sxDecCtrl->usr;
ST_RET ret;
ST_CHAR *desc;

  if (sxDecCtrl->reason == SX_ELEMENT_START)
    {
    SCL_SUBNET *scl_subnet;
    scl_subnet = scl_subnet_add (sclDecCtrl->sclInfo);
    if (scl_subnet == NULL)
      {
      scl_stop_parsing (sxDecCtrl, "scl_subnet_add", SX_USER_ERROR);
      return;
      }
    /* Get required attributes.	*/
    ret = scl_get_attr_copy (sxDecCtrl, "name", scl_subnet->name, (sizeof(scl_subnet->name)-1), SCL_ATTR_REQUIRED);
    if (ret)
      {
      scl_stop_parsing (sxDecCtrl, "SubNetwork", SX_USER_ERROR);
      return;
      }
    /* Get optional attributes.	*/
    ret = scl_get_attr_ptr (sxDecCtrl, "desc", &desc, SCL_ATTR_OPTIONAL);
    if (ret == SD_SUCCESS)
      scl_subnet->desc = chk_strdup (desc);	/* Alloc & copy desc string	*/
    ret = scl_get_attr_copy (sxDecCtrl, "type", scl_subnet->type, (sizeof(scl_subnet->type)-1), SCL_ATTR_OPTIONAL);

    sx_push (sxDecCtrl, sizeof(SubNetworkElements)/sizeof(SX_ELEMENT), 
             SubNetworkElements, SD_FALSE);
    }
  else
    {
    sx_pop (sxDecCtrl);
    }
  }
/************************************************************************/
/*			_ConnectedAP_SEFun				*/
/************************************************************************/
static ST_VOID _ConnectedAP_SEFun (SX_DEC_CTRL *sxDecCtrl)
  {
SCL_DEC_CTRL *sclDecCtrl = (SCL_DEC_CTRL *) sxDecCtrl->usr;
ST_RET ret;
ST_CHAR *desc;

  if (sxDecCtrl->reason == SX_ELEMENT_START)
    {
    SCL_CAP *scl_cap;
    scl_cap = scl_cap_add (sclDecCtrl->sclInfo);
    if (scl_cap == NULL)
      {
      scl_stop_parsing (sxDecCtrl, "scl_cap_add", SX_USER_ERROR);
      return;
      }
    /* Get required attributes	*/
    ret = scl_get_attr_copy (sxDecCtrl, "iedName", scl_cap->iedName, (sizeof(scl_cap->iedName)-1), SCL_ATTR_REQUIRED);
    ret |= scl_get_attr_copy (sxDecCtrl, "apName", scl_cap->apName, (sizeof(scl_cap->apName)-1), SCL_ATTR_REQUIRED);
    if (ret)
      {
      scl_stop_parsing (sxDecCtrl, "ConnectedAP", SX_USER_ERROR);
      return;
      }
    /* Get optional attributes.	*/
    ret = scl_get_attr_ptr (sxDecCtrl, "desc", &desc, SCL_ATTR_OPTIONAL);
    if (ret == SD_SUCCESS)
      scl_cap->desc = chk_strdup (desc);	/* Alloc & copy desc string	*/

    sx_push (sxDecCtrl, sizeof(ConnectedAPElements)/sizeof(SX_ELEMENT), 
             ConnectedAPElements, SD_FALSE);
    }
  else
    {
    sx_pop (sxDecCtrl);
    }
  }
/************************************************************************/
/*			_GSE_SEFun					*/
/************************************************************************/
static ST_VOID _GSE_SEFun (SX_DEC_CTRL *sxDecCtrl)
  {
SCL_DEC_CTRL *sclDecCtrl = (SCL_DEC_CTRL *) sxDecCtrl->usr;
ST_RET ret;

  if (sxDecCtrl->reason == SX_ELEMENT_START)
    {
    /* NOTE: save ptr in sclDecCtrl->scl_gse to use later in parsing.	*/
    sclDecCtrl->scl_gse = scl_gse_add (sclDecCtrl->sclInfo);
    if (sclDecCtrl->scl_gse == NULL)
      {
      scl_stop_parsing (sxDecCtrl, "scl_gse_add", SX_USER_ERROR);
      return;
      }
    ret = scl_get_attr_copy (sxDecCtrl, "ldInst", sclDecCtrl->scl_gse->ldInst, (sizeof(sclDecCtrl->scl_gse->ldInst)-1), SCL_ATTR_REQUIRED);    
    ret |= scl_get_attr_copy (sxDecCtrl, "cbName", sclDecCtrl->scl_gse->cbName, (sizeof(sclDecCtrl->scl_gse->cbName)-1), SCL_ATTR_REQUIRED);    
    if (ret)
      {
      scl_stop_parsing (sxDecCtrl, "GSE", SX_USER_ERROR);
      return;
      }
    else
      sx_push (sxDecCtrl, sizeof(GSEElements)/sizeof(SX_ELEMENT), 
             GSEElements, SD_FALSE);
    }
  else
    {
    sx_pop (sxDecCtrl);
    }
  }
/************************************************************************/
/*			_GSE_Address_SEFun				*/
/************************************************************************/
static ST_VOID _GSE_Address_SEFun (SX_DEC_CTRL *sxDecCtrl)
  {
  if (sxDecCtrl->reason == SX_ELEMENT_START)
    {
    sx_push (sxDecCtrl, sizeof(GSEAddressElements)/sizeof(SX_ELEMENT), 
             GSEAddressElements, SD_FALSE);
    }
  else
    {
    sx_pop (sxDecCtrl);
    }
  }
/************************************************************************/
/*			_GSE_Address_P_SEFun				*/
/************************************************************************/
static ST_VOID _GSE_Address_P_SEFun (SX_DEC_CTRL *sxDecCtrl)
  {
SCL_DEC_CTRL *sclDecCtrl = (SCL_DEC_CTRL *) sxDecCtrl->usr;
ST_CHAR *str;
ST_RET ret;
ST_BOOLEAN required = SD_FALSE;
ST_CHAR *strOut;
ST_INT strLen;

  if (sxDecCtrl->reason == SX_ELEMENT_END)
    {
    ret = scl_get_attr_ptr (sxDecCtrl, "type", &str, required);
    if (!strcmpi(str,"MAC-Address"))
      {
      ret = sx_get_string_ptr (sxDecCtrl, &strOut, &strLen);
      if (ret == SD_SUCCESS)
        {
        if (convert_mac (sclDecCtrl->scl_gse->MAC,strOut))
          {
          SXLOG_ERR1 ("Illegal MAC Address '%s'", strOut);
          scl_stop_parsing (sxDecCtrl, "_GSE_Address_P_SEFun", SX_USER_ERROR);
          }
        }
      }
    else if (!strcmpi(str,"APPID"))
      {
      ret = sx_get_string_ptr (sxDecCtrl, &strOut, &strLen);
      if (ret == SD_SUCCESS)
        sclDecCtrl->scl_gse->APPID = atoi(strOut);
      }
    else if (!strcmpi(str,"VLAN-PRIORITY"))
      {
      ret = sx_get_string_ptr (sxDecCtrl, &strOut, &strLen);
      if (ret == SD_SUCCESS)
        sclDecCtrl->scl_gse->VLANPRI = atoi(strOut);
      }
    else if (!strcmpi(str,"VLAN-ID"))
      {
      ret = sx_get_string_ptr (sxDecCtrl, &strOut, &strLen);
      if (ret == SD_SUCCESS)
        sclDecCtrl->scl_gse->VLANID = atoi(strOut);
      }
    }
  }

/*renxiaobao GSE ½âÎö*/
/************************************************************************/
/*			_GSE_MinTime_SEFun				*/
/************************************************************************/
static ST_VOID _GSE_MinTime_SEFun (SX_DEC_CTRL *sxDecCtrl)
{
	SCL_DEC_CTRL *sclDecCtrl = (SCL_DEC_CTRL *) sxDecCtrl->usr;
	ST_CHAR *str;
	ST_RET ret;
	ST_BOOLEAN required = SD_FALSE;
	ST_CHAR *strOut;
	ST_INT strLen;
	if (sxDecCtrl->reason == SX_ELEMENT_END)
	{
		ret = scl_get_attr_ptr (sxDecCtrl, "unit", &str, required);
		/*if (!strcmpi(str,"s"))*/
		{
			ret = sx_get_string_ptr (sxDecCtrl, &strOut, &strLen);
			if (ret == SD_SUCCESS)
				sclDecCtrl->scl_gse->MinTime = atoi(strOut);
		}
	}
}
static ST_VOID _GSE_MaxTime_SEFun (SX_DEC_CTRL *sxDecCtrl)
{
	SCL_DEC_CTRL *sclDecCtrl = (SCL_DEC_CTRL *) sxDecCtrl->usr;
	ST_CHAR *str;
	ST_RET ret;
	ST_BOOLEAN required = SD_FALSE;
	ST_CHAR *strOut;
	ST_INT strLen;
	if (sxDecCtrl->reason == SX_ELEMENT_END)
	{
		ret = scl_get_attr_ptr (sxDecCtrl, "unit", &str, required);
		/*if (!strcmpi(str,"s"))*/
		{
			ret = sx_get_string_ptr (sxDecCtrl, &strOut, &strLen);
			if (ret == SD_SUCCESS)
				sclDecCtrl->scl_gse->MaxTime = atoi(strOut);
		}
	}
}
/************************************************************************/
/*			_SMV_SEFun					*/
/************************************************************************/
static ST_VOID _SMV_SEFun (SX_DEC_CTRL *sxDecCtrl)
  {
SCL_DEC_CTRL *sclDecCtrl = (SCL_DEC_CTRL *) sxDecCtrl->usr;
ST_RET ret;

  if (sxDecCtrl->reason == SX_ELEMENT_START)
    {
    /* NOTE: save ptr in sclDecCtrl->scl_smv to use later in parsing.	*/
    sclDecCtrl->scl_smv = scl_smv_add (sclDecCtrl->sclInfo);
    if (sclDecCtrl->scl_smv == NULL)
      {
      scl_stop_parsing (sxDecCtrl, "scl_smv_add", SX_USER_ERROR);
      return;
      }
    ret = scl_get_attr_copy (sxDecCtrl, "ldInst", sclDecCtrl->scl_smv->ldInst, (sizeof(sclDecCtrl->scl_smv->ldInst)-1), SCL_ATTR_REQUIRED);    
    ret |= scl_get_attr_copy (sxDecCtrl, "cbName", sclDecCtrl->scl_smv->cbName, (sizeof(sclDecCtrl->scl_smv->cbName)-1), SCL_ATTR_REQUIRED);    
    if (ret)
      {
      scl_stop_parsing (sxDecCtrl, "SMV", SX_USER_ERROR);
      return;
      }
    else
      sx_push (sxDecCtrl, sizeof(SMVElements)/sizeof(SX_ELEMENT), 
             SMVElements, SD_FALSE);
    }
  else
    {
    sx_pop (sxDecCtrl);
    }
  }
/************************************************************************/
/*			_SMV_Address_SEFun				*/
/************************************************************************/
static ST_VOID _SMV_Address_SEFun (SX_DEC_CTRL *sxDecCtrl)
  {
  if (sxDecCtrl->reason == SX_ELEMENT_START)
    {
    sx_push (sxDecCtrl, sizeof(SMVAddressElements)/sizeof(SX_ELEMENT), 
             SMVAddressElements, SD_FALSE);
    }
  else
    {
    sx_pop (sxDecCtrl);
    }
  }
/************************************************************************/
/*			_SMV_Address_P_SEFun				*/
/************************************************************************/
static ST_VOID _SMV_Address_P_SEFun (SX_DEC_CTRL *sxDecCtrl)
  {
SCL_DEC_CTRL *sclDecCtrl = (SCL_DEC_CTRL *) sxDecCtrl->usr;
ST_CHAR *str;
ST_RET ret;
ST_BOOLEAN required = SD_FALSE;
ST_CHAR *strOut;
ST_INT strLen;

  if (sxDecCtrl->reason == SX_ELEMENT_END)
    {
    ret = scl_get_attr_ptr (sxDecCtrl, "type", &str, required);
    if (!strcmpi(str,"MAC-Address"))
      {
      ret = sx_get_string_ptr (sxDecCtrl, &strOut, &strLen);
      if (ret == SD_SUCCESS)
        {
        if (convert_mac (sclDecCtrl->scl_smv->MAC,strOut))
          {
          SXLOG_ERR1 ("Illegal MAC Address '%s'", strOut);
          scl_stop_parsing (sxDecCtrl, "_SMV_Address_P_SEFun", SX_USER_ERROR);
          }
        }
      }
    else if (!strcmpi(str,"APPID"))
      {
      ret = sx_get_string_ptr (sxDecCtrl, &strOut, &strLen);
      if (ret == SD_SUCCESS)
        sclDecCtrl->scl_smv->APPID = atoi(strOut);
      }
    else if (!strcmpi(str,"VLAN-PRIORITY"))
      {
      ret = sx_get_string_ptr (sxDecCtrl, &strOut, &strLen);
      if (ret == SD_SUCCESS)
        sclDecCtrl->scl_smv->VLANPRI = atoi(strOut);
      }
    else if (!strcmpi(str,"VLAN-ID"))
      {
      ret = sx_get_string_ptr (sxDecCtrl, &strOut, &strLen);
      if (ret == SD_SUCCESS)
        sclDecCtrl->scl_smv->VLANID = atoi(strOut);
      }
    }
  }
/************************************************************************/
/*			IED_SEFun					*/
/************************************************************************/
char icd_ied_type[65];
static ST_VOID _IED_SEFun (SX_DEC_CTRL *sxDecCtrl)
  {
SCL_DEC_CTRL *sclDecCtrl;
ST_CHAR *str;	/* ptr set by scl_get_attr_ptr	*/
ST_RET ret;
ST_BOOLEAN required = SD_FALSE;

  sclDecCtrl = (SCL_DEC_CTRL *) sxDecCtrl->usr;

  if (sxDecCtrl->reason == SX_ELEMENT_START)
    {
    /* start required attributes */
    required = SD_TRUE;
    ret = scl_get_attr_ptr (sxDecCtrl, "name", &str, required);
    if (ret != SD_SUCCESS)
      {
      return;
      }
    if (strcmp(str, sclDecCtrl->iedName) == 0)
      {
    	icd_ied_type[0] = 0;
    	scl_get_attr_copy (sxDecCtrl, "type", &icd_ied_type[0], 64, 0); /*renxiaobao add 20151218*/
    	SXLOG_CDEC1 ("SCL PARSE: IED 'name' match found: %s", str);
    	sclDecCtrl->iedNameMatched = SD_TRUE;
    	sx_push (sxDecCtrl, sizeof(IEDElements)/sizeof(SX_ELEMENT), IEDElements, SD_FALSE);
      }
    else
      {
      SXLOG_CDEC1 ("SCL PARSE: IED 'name' found (%s), not a match", str);
      }
    /* end required attributes */
    }
  else
    {
    if (sclDecCtrl->iedNameMatched == SD_TRUE)
      {
      sclDecCtrl->iedNameMatched = SD_FALSE;
      sx_pop (sxDecCtrl);
      }
    }
  }

/************************************************************************/
/*			_AccessPoint_SEFun				*/
/************************************************************************/

static ST_VOID _AccessPoint_SEFun (SX_DEC_CTRL *sxDecCtrl)
  {
SCL_DEC_CTRL *sclDecCtrl;
ST_CHAR *str;	/* ptr set by scl_get_attr_ptr	*/
ST_RET ret;
ST_BOOLEAN required = SD_FALSE;

  sclDecCtrl = (SCL_DEC_CTRL *) sxDecCtrl->usr;

  if (sxDecCtrl->reason == SX_ELEMENT_START)
    {
    /* start required attributes */
    required = SD_TRUE;
    ret = scl_get_attr_ptr (sxDecCtrl, "name", &str, required);
    if (ret != SD_SUCCESS)
      {
      return;
      }
    if (strcmp(str, sclDecCtrl->accessPointName) == 0)
      {
      SXLOG_CDEC1 ("SCL PARSE: AccessPoint 'name' match found: %s", str);
      sclDecCtrl->accessPointFound = SD_TRUE;	/*NOTE: only get here if IED also found*/
      sclDecCtrl->accessPointMatched = SD_TRUE;
      sx_push (sxDecCtrl, sizeof(AccessPointElements)/sizeof(SX_ELEMENT), AccessPointElements, SD_FALSE);
      }
    else
      {
      SXLOG_CDEC1 ("SCL PARSE: AccessPoint 'name' found (%s), not a match", str);
      }
    /* end required attributes */
    }
  else
    {
    if (sclDecCtrl->accessPointMatched == SD_TRUE)
      {
      sclDecCtrl->accessPointMatched = SD_FALSE;
      sx_pop (sxDecCtrl);
      }
    }
  }

/************************************************************************/
/*			_Server_SEFun					*/
/************************************************************************/

static ST_VOID _Server_SEFun (SX_DEC_CTRL *sxDecCtrl)
  {
  if (sxDecCtrl->reason == SX_ELEMENT_START)
    {
    sx_push (sxDecCtrl, sizeof(ServerElements)/sizeof(SX_ELEMENT), ServerElements, SD_FALSE);
    }
  else
    sx_pop (sxDecCtrl);
  }

/************************************************************************/
/*			_LDevice_SEFun					*/
/************************************************************************/
static ST_VOID _LDevice_SEFun (SX_DEC_CTRL *sxDecCtrl)
  {
SCL_DEC_CTRL *sclDecCtrl;
ST_RET ret;
ST_BOOLEAN required = SD_FALSE;
SCL_INFO *scl_info;
SCL_LD *scl_ld;
ST_CHAR *desc;

  if (sxDecCtrl->reason == SX_ELEMENT_START)
    {
    sclDecCtrl = (SCL_DEC_CTRL *) sxDecCtrl->usr;

    scl_ld = sclDecCtrl->scl_ld = scl_ld_create (sclDecCtrl->sclInfo);
    if (scl_ld == NULL)
      {
      scl_stop_parsing (sxDecCtrl, "scl_ld_create", SX_USER_ERROR);
      return;
      }

    /* start optional attributes */
    ret = scl_get_attr_ptr (sxDecCtrl, "desc", &desc, required);
    if (ret == SD_SUCCESS)
      scl_ld->desc = chk_strdup (desc);	/* Alloc & copy desc string	*/
    /* end optional attributes */

    /* start required attributes */
    required = SD_TRUE;
    ret = scl_get_attr_copy (sxDecCtrl, "inst", scl_ld->inst, (sizeof(scl_ld->inst)-1), required);
    if (ret != SD_SUCCESS)
      return;	/* At least one required attr not found. Stop now.	*/
    /* end required attributes */

    sx_push (sxDecCtrl, sizeof(LDeviceElements)/sizeof(SX_ELEMENT), LDeviceElements, SD_FALSE);
    }
  else
    {		/* reason == SX_ELEMENT_END	*/
    sclDecCtrl = (SCL_DEC_CTRL *) sxDecCtrl->usr;
    scl_info = sclDecCtrl->sclInfo;
    scl_ld = sclDecCtrl->scl_ld;
    /* Construct MMS Domain name from scl info.	*/
    /* ASSUME nameStructure="IEDName" (domain name = IED name + LDevice inst)*/
    /* nameStructure="FuncName" is OBSOLETE.				*/
    if (strlen(scl_info->iedName) + strlen(scl_ld->inst) <= MAX_IDENT_LEN)
      {
      strcpy (scl_ld->domName, scl_info->iedName);	/* construct domain name*/
      strcat (scl_ld->domName, scl_ld->inst);
      }
    else
      {
      SXLOG_ERR0 ("Cannot create LD: constructed domain name too long");
      scl_stop_parsing (sxDecCtrl, "_LDevice_SEFun", SX_USER_ERROR);
      }
    sx_pop (sxDecCtrl);
    }
  }

/************************************************************************/
/*			_LN_SEFun					*/
/************************************************************************/

static ST_VOID _LN_SEFun (SX_DEC_CTRL *sxDecCtrl)
  {
SCL_DEC_CTRL *sclDecCtrl;
ST_RET ret,ret1;
ST_BOOLEAN required = SD_FALSE;
SCL_LN *scl_ln;
ST_CHAR *desc;

  if (sxDecCtrl->reason == SX_ELEMENT_START)
    {
    sclDecCtrl = (SCL_DEC_CTRL *) sxDecCtrl->usr;

    scl_ln = sclDecCtrl->scl_ln = scl_ln_add (sclDecCtrl->sclInfo);
    if (scl_ln == NULL)
      {
      scl_stop_parsing (sxDecCtrl, "scl_ln_add", SX_USER_ERROR);
      return;
      }

    /* start optional attributes */
    ret = scl_get_attr_ptr (sxDecCtrl, "desc", &desc, required);
    if (ret == SD_SUCCESS)
      scl_ln->desc = chk_strdup (desc);	/* Alloc & copy desc string	*/
    ret = scl_get_attr_copy (sxDecCtrl, "prefix", scl_ln->prefix, (sizeof(scl_ln->prefix)-1), required);
    /* end optional attributes */

    /* start required attributes */
    required = SD_TRUE;
    ret = scl_get_attr_copy (sxDecCtrl, "lnType", scl_ln->lnType, (sizeof(scl_ln->lnType)-1), required);


 /* ret |= scl_get_attr_copy (sxDecCtrl, "inst", scl_ln->inst, (sizeof(scl_ln->inst)-1), required); renxiaobao remove*/
/*	scl_ln->inst ="";*/
/*	ret |= */
		scl_get_attr_copy (sxDecCtrl, "inst", scl_ln->inst, (sizeof(scl_ln->inst)-1), SD_FALSE);


    ret |= scl_get_attr_copy (sxDecCtrl, "lnClass", scl_ln->lnClass, (sizeof(scl_ln->lnClass)-1), required);

    if (ret != SD_SUCCESS)
      return;

    if (stricmp(sxDecCtrl->sxDecElInfo.tag, "LN0") == 0 && 
	stricmp(scl_ln->lnClass, "LLN0") != 0)
      {
      sxDecCtrl->errCode = SX_USER_ERROR;
      sxDecCtrl->termFlag = SD_TRUE;
      SXLOG_ERR0 ("SCL PARSE: Attribute 'lnClass' of element 'LN0' has a value other then 'LLN0' (schema violation).");
      return;
      }
    /* end required attributes */

    if (stricmp(sxDecCtrl->sxDecElInfo.tag, "LN0") == 0)
      sx_push (sxDecCtrl, sizeof(LN0Elements)/sizeof(SX_ELEMENT), LN0Elements, SD_FALSE);
    else
      sx_push (sxDecCtrl, sizeof(LNElements)/sizeof(SX_ELEMENT), LNElements, SD_FALSE);
    }
  else
    {		/* reason == SX_ELEMENT_END	*/
    sclDecCtrl = (SCL_DEC_CTRL *) sxDecCtrl->usr;
    scl_ln = sclDecCtrl->scl_ln;
    /* Construct MMS Variable name from scl info.	*/
    if (strlen (scl_ln->lnClass) != 4)
      {
      SXLOG_ERR1 ("Illegal lnClass='%s'. Must be exactly 4 char",
                scl_ln->lnClass);
      scl_stop_parsing (sxDecCtrl, "_LN_SEFun", SX_USER_ERROR);
      }
    else if (strlen (scl_ln->prefix) + strlen (scl_ln->inst) > 11)
      {
      /* NOTE: standard only allows max=7, but we want to be more forgiving.*/
      SXLOG_ERR3 ("Illegal definition for lnClass='%s': prefix (%s) plus inst (%s) > 11 char.",
                scl_ln->lnClass, scl_ln->prefix, scl_ln->inst);
      scl_stop_parsing (sxDecCtrl, "_LN_SEFun", SX_USER_ERROR);
      }
    else
      {
      strcpy (scl_ln->varName, scl_ln->prefix);
      strcat (scl_ln->varName, scl_ln->lnClass);
      strcat (scl_ln->varName, scl_ln->inst);
      }
    sx_pop (sxDecCtrl);
    }
  }

/************************************************************************/
/*			_DataSet_SEFun					*/
/************************************************************************/

static ST_VOID _DataSet_SEFun (SX_DEC_CTRL *sxDecCtrl)
  {
SCL_DEC_CTRL *sclDecCtrl;
ST_RET ret;
ST_BOOLEAN required = SD_FALSE;
SCL_DATASET *scl_dataset;
ST_CHAR *desc;

  if (sxDecCtrl->reason == SX_ELEMENT_START)
    {
    sclDecCtrl = (SCL_DEC_CTRL *) sxDecCtrl->usr;

    scl_dataset = scl_dataset_add (sclDecCtrl->sclInfo);
    if (scl_dataset == NULL)
      {
      scl_stop_parsing (sxDecCtrl, "scl_dataset_add", SX_USER_ERROR);
      return;
      }

    /* start optional attributes */
    ret = scl_get_attr_ptr (sxDecCtrl, "desc", &desc, required);
    if (ret == SD_SUCCESS)
      scl_dataset->desc = chk_strdup (desc);	/* Alloc & copy desc string	*/
    /* end optional attributes */

    /* start required attributes */
    required = SD_TRUE;
    ret = scl_get_attr_copy (sxDecCtrl, "name", scl_dataset->name, (sizeof(scl_dataset->name)-1), required);
    if (ret != SD_SUCCESS)
      return;
    /* end required attributes */

    sx_push (sxDecCtrl, sizeof(DataSetElements)/sizeof(SX_ELEMENT), DataSetElements, SD_FALSE);
    }
  else
    sx_pop (sxDecCtrl);
  }

/************************************************************************/
/*			_FCDA_SFun					*/
/************************************************************************/

static ST_VOID _FCDA_SFun (SX_DEC_CTRL *sxDecCtrl)
  {
SCL_DEC_CTRL *sclDecCtrl;
ST_RET ret;
ST_BOOLEAN required = SD_FALSE;
SCL_INFO *scl_info;
SCL_FCDA *scl_fcda;

  sclDecCtrl = (SCL_DEC_CTRL *) sxDecCtrl->usr;
  scl_info = sclDecCtrl->sclInfo;

  scl_fcda = scl_fcda_add (scl_info);
  if (scl_fcda == NULL)
    {
    scl_stop_parsing (sxDecCtrl, "scl_fcda_add", SX_USER_ERROR);
    return;
    }

  /* start optional attributes */
  ret = scl_get_attr_copy (sxDecCtrl, "ldInst",  scl_fcda->ldInst,  (sizeof(scl_fcda->ldInst)-1), required);
  ret = scl_get_attr_copy (sxDecCtrl, "prefix",  scl_fcda->prefix,  (sizeof(scl_fcda->prefix)-1), required);
  ret = scl_get_attr_copy (sxDecCtrl, "lnInst",  scl_fcda->lnInst,  (sizeof(scl_fcda->lnInst)-1), required);
  ret = scl_get_attr_copy (sxDecCtrl, "lnClass", scl_fcda->lnClass, (sizeof(scl_fcda->lnClass)-1), required);
  ret = scl_get_attr_copy (sxDecCtrl, "doName",  scl_fcda->doName,  (sizeof(scl_fcda->doName)-1), required);
  ret = scl_get_attr_copy (sxDecCtrl, "daName",  scl_fcda->daName,  (sizeof(scl_fcda->daName)-1), required);
  /* end optional attributes */

  /* start required attributes */
  required = SD_TRUE;
  ret = scl_get_attr_copy (sxDecCtrl, "fc", scl_fcda->fc, (sizeof(scl_fcda->fc)-1), required);
  if (ret != SD_SUCCESS)
    return;
  /* end required attributes */

  /* Construct domain name from SCL info	*/
  /* ASSUME nameStructure="IEDName" (domain name = IED name + LDevice inst)*/
  /* nameStructure="FuncName" is OBSOLETE.				*/
  if (strlen(scl_info->iedName) + strlen(scl_fcda->ldInst) <= MAX_IDENT_LEN)
    {
    strcpy (scl_fcda->domName, scl_info->iedName);
    strcat (scl_fcda->domName, scl_fcda->ldInst);
    }
  else
    {
    SXLOG_ERR0 ("Cannot add FCDA: constructed domain name too long");
    scl_stop_parsing (sxDecCtrl, "_FCDA_SFun", SX_USER_ERROR);
    }
  }

/************************************************************************/
/*			_ReportControl_SEFun				*/
/************************************************************************/

static ST_VOID _ReportControl_SEFun (SX_DEC_CTRL *sxDecCtrl)
  {
SCL_DEC_CTRL *sclDecCtrl;
ST_CHAR *str;	/* ptr set by scl_get_attr_ptr	*/
ST_RET ret;
ST_BOOLEAN required = SD_FALSE;
ST_CHAR *desc;

  sclDecCtrl = (SCL_DEC_CTRL *) sxDecCtrl->usr;

  if (sxDecCtrl->reason == SX_ELEMENT_START)
    {
    /* Alloc struct, save ptr in sclDecCtrl, & set local ptr to it.	*/
    SCL_RCB *scl_rcb = sclDecCtrl->scl_rcb = scl_rcb_add (sclDecCtrl->sclInfo);
    if (scl_rcb == NULL)
      {
      scl_stop_parsing (sxDecCtrl, "scl_rcb_add", SX_USER_ERROR);
      return;
      }
    
    /* start optional attributes */
    ret = scl_get_attr_ptr (sxDecCtrl, "desc", &desc, required);
    if (ret == SD_SUCCESS)
      scl_rcb->desc = chk_strdup (desc);	/* Alloc & copy desc string	*/
    ret = scl_get_attr_copy (sxDecCtrl, "datSet", scl_rcb->datSet, (sizeof(scl_rcb->datSet)-1), required);
    ret = scl_get_uint_attr (sxDecCtrl, "intgPd", &scl_rcb->intgPd, required);
    ret = scl_get_uint_attr (sxDecCtrl, "bufTime", &scl_rcb->bufTime, required);
    ret = scl_get_attr_ptr (sxDecCtrl, "buffered", &str, required);

    scl_rcb->buffered = SD_FALSE;  /* default */
    if (ret == SD_SUCCESS)
      {
      if (stricmp(str, "true") == 0)
        scl_rcb->buffered = SD_TRUE;
      }
    /* NOTE: we only accept default value of indexed="true".	*/
    ret = scl_get_attr_ptr (sxDecCtrl, "indexed", &str, required);
    if (ret == SD_SUCCESS && stricmp(str, "false") == 0)
      {
      SXLOG_ERR0 ("ReportControl attribute indexed='false' not supported");
      scl_stop_parsing (sxDecCtrl, "scl_rcb_add", SX_USER_ERROR);
      return;
      }
    /* end optional attributes */
    
    /* start required attributes */
    required = SD_TRUE;
    ret = scl_get_attr_copy (sxDecCtrl, "name", scl_rcb->name, (sizeof(scl_rcb->name)-1), required);
    ret |= scl_get_attr_copy (sxDecCtrl, "rptID", scl_rcb->rptID, (sizeof(scl_rcb->rptID)-1), required);
    ret |= scl_get_uint_attr (sxDecCtrl, "confRev", &scl_rcb->confRev, required);
    if (ret != SD_SUCCESS)
      return;
    /* end required attributes */

    sx_push (sxDecCtrl, sizeof(ReportControlElements)/sizeof(SX_ELEMENT), ReportControlElements, SD_FALSE);
    }
  else /* reason = SX_ELEMENT_END */
    {
    /* CRITICAL: Copy TrgOps to scl_rcb.	*/
    sclDecCtrl->scl_rcb->TrgOps[0] = sclDecCtrl->TrgOps[0];
    /* If "RptEnabled max" not configured, set default value*/
    if (sclDecCtrl->scl_rcb->maxClient == 0)
      sclDecCtrl->scl_rcb->maxClient = 1;	/* default	*/
    /* NOTE: scl_rcb is all filled in now	*/
    sx_pop (sxDecCtrl);
    }
  }

/************************************************************************/
/*			_LogControl_SEFun				*/
/************************************************************************/

static ST_VOID _LogControl_SEFun (SX_DEC_CTRL *sxDecCtrl)
  {
SCL_DEC_CTRL *sclDecCtrl;
ST_CHAR *str;	/* ptr set by scl_get_attr_ptr	*/
ST_RET ret;
ST_BOOLEAN required = SD_FALSE;
ST_CHAR *desc;

  sclDecCtrl = (SCL_DEC_CTRL *) sxDecCtrl->usr;

  if (sxDecCtrl->reason == SX_ELEMENT_START)
    {
    /* Alloc struct, save ptr in sclDecCtrl, & set local ptr to it.	*/
    SCL_LCB *scl_lcb = sclDecCtrl->scl_lcb = scl_lcb_add (sclDecCtrl->sclInfo);
    if (scl_lcb == NULL)
      {
      scl_stop_parsing (sxDecCtrl, "scl_lcb_add", SX_USER_ERROR);
      return;
      }
    
    /* start optional attributes */
    ret = scl_get_attr_ptr (sxDecCtrl, "desc", &desc, required);
    if (ret == SD_SUCCESS)
      scl_lcb->desc = chk_strdup (desc);	/* Alloc & copy desc string	*/
    ret = scl_get_uint_attr (sxDecCtrl, "intgPd", &scl_lcb->intgPd, required);
    ret = scl_get_attr_copy (sxDecCtrl, "datSet", scl_lcb->datSet, (sizeof(scl_lcb->datSet)-1), required);
    ret = scl_get_attr_ptr (sxDecCtrl, "logEna", &str, required);
    scl_lcb->logEna = SD_FALSE;  /* default */
    if (ret == SD_SUCCESS)
      {
      if (stricmp(str, "true") == 0)
        scl_lcb->logEna = SD_TRUE;
      }

    ret = scl_get_attr_ptr (sxDecCtrl, "reasonCode", &str, required);
    scl_lcb->reasonCode = SD_FALSE;  /* default */
    if (ret == SD_SUCCESS)
      {
      if (stricmp(str, "true") == 0)
        scl_lcb->reasonCode = SD_TRUE;
      }
    /* end optional attributes */
    
    /* start required attributes */
    required = SD_TRUE;
    ret = scl_get_attr_copy (sxDecCtrl, "name", scl_lcb->name, (sizeof(scl_lcb->name)-1), required);
    ret |= scl_get_attr_copy (sxDecCtrl, "logName", scl_lcb->logName, (sizeof(scl_lcb->logName)-1), required);
    if (ret != SD_SUCCESS)
      return;
    /* end required attributes */

    sx_push (sxDecCtrl, sizeof(LogControlElements)/sizeof(SX_ELEMENT), LogControlElements, SD_FALSE);
    }
  else /* reason = SX_ELEMENT_END */
    {
    /* CRITICAL: Copy TrgOps to scl_lcb.	*/
    sclDecCtrl->scl_lcb->TrgOps[0] = sclDecCtrl->TrgOps[0];
    /* NOTE: scl_lcb is all filled in now	*/
    sx_pop (sxDecCtrl);
    }
  }

/************************************************************************/
/*			_GSEControl_SFun				*/
/************************************************************************/

static ST_VOID _GSEControl_SFun (SX_DEC_CTRL *sxDecCtrl)
  {
SCL_DEC_CTRL *sclDecCtrl;
ST_RET ret;
ST_BOOLEAN required = SD_FALSE;
ST_CHAR *type;	/* ptr set by scl_get_attr_ptr	*/
SCL_GCB *scl_gcb;
ST_CHAR *desc;

  sclDecCtrl = (SCL_DEC_CTRL *) sxDecCtrl->usr;

  scl_gcb = scl_gcb_add (sclDecCtrl->sclInfo);
  if (scl_gcb == NULL)
    {
    scl_stop_parsing (sxDecCtrl, "scl_gcb_add", SX_USER_ERROR);
    return;
    }

  /* start optional attributes */
  ret = scl_get_attr_ptr (sxDecCtrl, "desc", &desc, required);
  if (ret == SD_SUCCESS)
    scl_gcb->desc = chk_strdup (desc);	/* Alloc & copy desc string	*/
  ret = scl_get_uint_attr (sxDecCtrl, "confRev", &scl_gcb->confRev, required);
  ret = scl_get_attr_copy (sxDecCtrl, "datSet", scl_gcb->datSet, (sizeof(scl_gcb->datSet)-1), required);
  ret = scl_get_attr_ptr (sxDecCtrl, "type", &type, required);
  if (ret == SD_SUCCESS && strcmp(type, "GSSE") == 0)
    scl_gcb->isGoose = SD_FALSE;
  else
    scl_gcb->isGoose = SD_TRUE;
  /* end optional attributes */
    
  /* start required attributes */
  required = SD_TRUE;
  ret = scl_get_attr_copy  (sxDecCtrl, "name",   scl_gcb->name,   (sizeof(scl_gcb->name)-1), required);
  ret |= scl_get_attr_copy (sxDecCtrl, "appID",  scl_gcb->appID,  (sizeof(scl_gcb->appID)-1), required);
  if (ret != SD_SUCCESS)
    return;
  /* end required attributes */
  }

/************************************************************************/
/*			_SettingControl_SFun				*/
/************************************************************************/

static ST_VOID _SettingControl_SFun (SX_DEC_CTRL *sxDecCtrl)
  {
SCL_DEC_CTRL *sclDecCtrl;
ST_RET ret;
ST_BOOLEAN required;
SCL_SGCB *scl_sgcb;
ST_CHAR *desc;

  sclDecCtrl = (SCL_DEC_CTRL *) sxDecCtrl->usr;

  scl_sgcb = scl_sgcb_add (sclDecCtrl->sclInfo);
  if (scl_sgcb == NULL)
    {
    scl_stop_parsing (sxDecCtrl, "scl_sgcb_add", SX_USER_ERROR);
    return;
    }

  /* start optional attributes */
  required = SD_FALSE;
  ret = scl_get_attr_ptr (sxDecCtrl, "desc", &desc, required);
  if (ret == SD_SUCCESS)
    scl_sgcb->desc = chk_strdup (desc);	/* Alloc & copy desc string	*/
  ret = scl_get_uint_attr (sxDecCtrl, "actSG", &scl_sgcb->actSG, required);
  if (ret)
    scl_sgcb->actSG = 1;  /* default value */
  /* end optional attributes */
    
  /* start required attributes */
  required = SD_TRUE;
  ret = scl_get_uint_attr (sxDecCtrl, "numOfSGs", &scl_sgcb->numOfSGs, required);
  /* end required attributes */
  }

/************************************************************************/
/*			_TrgOps_SFun					*/
/* Save all TrgOps bits in sclDecCtrl->TrgOps.				*/
/************************************************************************/

static ST_VOID _TrgOps_SFun (SX_DEC_CTRL *sxDecCtrl)
  {
SCL_DEC_CTRL *sclDecCtrl;
ST_CHAR *str;	/* ptr set by scl_get_attr_ptr	*/
ST_RET ret;
ST_BOOLEAN required = SD_FALSE;

  sclDecCtrl = (SCL_DEC_CTRL *) sxDecCtrl->usr;
  
  sclDecCtrl->TrgOps[0] = 0;	/* Start with all bits=0	*/

  /* start optional attributes */
  ret = scl_get_attr_ptr (sxDecCtrl, "dchg", &str, required);
  if (ret == SD_SUCCESS)
    {
    if (stricmp(str, "true") == 0)
      {
      BSTR_BIT_SET_ON(sclDecCtrl->TrgOps, TRGOPS_BITNUM_DATA_CHANGE);
      }
    }

  ret = scl_get_attr_ptr (sxDecCtrl, "qchg", &str, required);
  if (ret == SD_SUCCESS)
    {
    if (stricmp(str, "true") == 0)
      {
      BSTR_BIT_SET_ON(sclDecCtrl->TrgOps, TRGOPS_BITNUM_QUALITY_CHANGE);
      }
    }

  ret = scl_get_attr_ptr (sxDecCtrl, "dupd", &str, required);
  if (ret == SD_SUCCESS)
    {
    if (stricmp(str, "true") == 0)
      {
      BSTR_BIT_SET_ON(sclDecCtrl->TrgOps, TRGOPS_BITNUM_DATA_UPDATE);
      }
    }

  ret = scl_get_attr_ptr (sxDecCtrl, "period", &str, required);
  if (ret == SD_SUCCESS)
    {
    if (stricmp(str, "true") == 0)
      {
      BSTR_BIT_SET_ON(sclDecCtrl->TrgOps, TRGOPS_BITNUM_INTEGRITY);
      }
    }
  /* end optional attributes */
  }

/************************************************************************/
/*			_OptFlds_SFun					*/
/* Save all OptFlds bits in sclDecCtrl->scl_rcb->OptFlds.		*/
/************************************************************************/

static ST_VOID _OptFlds_SFun (SX_DEC_CTRL *sxDecCtrl)
  {
SCL_DEC_CTRL *sclDecCtrl;
ST_CHAR *str;	/* ptr set by scl_get_attr_ptr	*/
ST_RET ret;
SCL_RCB *scl_rcb;
ST_BOOLEAN required = SD_FALSE;

  sclDecCtrl = (SCL_DEC_CTRL *) sxDecCtrl->usr;
  scl_rcb = sclDecCtrl->scl_rcb;

  /* start optional attributes */
  ret = scl_get_attr_ptr (sxDecCtrl, "seqNum", &str, required);
  if (ret == SD_SUCCESS)
    {
    if (stricmp(str, "true") == 0)
      {
      BSTR_BIT_SET_ON(scl_rcb->OptFlds, OPTFLD_BITNUM_SQNUM);
      }
    }

  ret = scl_get_attr_ptr (sxDecCtrl, "timeStamp", &str, required);
  if (ret == SD_SUCCESS)
    {
    if (stricmp(str, "true") == 0)
      {
      BSTR_BIT_SET_ON(scl_rcb->OptFlds, OPTFLD_BITNUM_TIMESTAMP);
      }
    }

  ret = scl_get_attr_ptr (sxDecCtrl, "dataSet", &str, required);
  if (ret == SD_SUCCESS)
    {
    if (stricmp(str, "true") == 0)
      {
      BSTR_BIT_SET_ON(scl_rcb->OptFlds, OPTFLD_BITNUM_DATSETNAME);
      }
    }

  ret = scl_get_attr_ptr (sxDecCtrl, "reasonCode", &str, required);
  if (ret == SD_SUCCESS)
    {
    if (stricmp(str, "true") == 0)
      {
      BSTR_BIT_SET_ON(scl_rcb->OptFlds, OPTFLD_BITNUM_REASON);
      }
    }

  ret = scl_get_attr_ptr (sxDecCtrl, "dataRef", &str, required);
  if (ret == SD_SUCCESS)
    {
    if (stricmp(str, "true") == 0)
      {
      BSTR_BIT_SET_ON(scl_rcb->OptFlds, OPTFLD_BITNUM_DATAREF);
      }
    }

  ret = scl_get_attr_ptr (sxDecCtrl, "bufOvfl", &str, required);
  if (ret == SD_SUCCESS)
    {
    if (stricmp(str, "true") == 0)
      {
      BSTR_BIT_SET_ON(scl_rcb->OptFlds, OPTFLD_BITNUM_BUFOVFL);
      }
    }

  ret = scl_get_attr_ptr (sxDecCtrl, "entryID", &str, required);
  if (ret == SD_SUCCESS)
    {
    if (stricmp(str, "true") == 0)
      {
      BSTR_BIT_SET_ON(scl_rcb->OptFlds, OPTFLD_BITNUM_ENTRYID);
      }
    }

  ret = scl_get_attr_ptr (sxDecCtrl, "configRef", &str, required);
  if (ret == SD_SUCCESS)
    {
    if (stricmp(str, "true") == 0)
      {
      BSTR_BIT_SET_ON(scl_rcb->OptFlds, OPTFLD_BITNUM_CONFREV);
      }
    }
  /* end optional attributes */
  }

/************************************************************************/
/*			_RptEnabled_SFun				*/
/* Save RptEnabled info in sclDecCtrl->scl_rcb.				*/
/************************************************************************/
static ST_VOID _RptEnabled_SFun (SX_DEC_CTRL *sxDecCtrl)
  {
SCL_DEC_CTRL *sclDecCtrl = (SCL_DEC_CTRL *) sxDecCtrl->usr;
ST_RET ret;
SCL_RCB *scl_rcb;

  assert (sxDecCtrl->reason == SX_ELEMENT_START);
  scl_rcb = sclDecCtrl->scl_rcb;

  /* start optional attributes */
  ret = scl_get_uint_attr (sxDecCtrl, "max", &scl_rcb->maxClient, SCL_ATTR_OPTIONAL);
  /* If configured, check for legal value.	*/
  if (ret == SD_SUCCESS)
    {
    if (scl_rcb->maxClient <= 0 || scl_rcb->maxClient > 99)
      {
      SXLOG_ERR1 ("RptEnabled max=%d is not valid. Must be value between 1 and 99", scl_rcb->maxClient);
      scl_stop_parsing (sxDecCtrl, "RptEnabled", SX_USER_ERROR);
      scl_rcb->maxClient = 1;	/* set to default just in case user ignores error*/
      }
    }
  }

/************************************************************************/
/*			_DOI_SEFun					*/
/************************************************************************/

static ST_VOID _DOI_SEFun (SX_DEC_CTRL *sxDecCtrl)
  {
SCL_DEC_CTRL *sclDecCtrl;
ST_CHAR *ix;
ST_CHAR *name;
ST_RET ret;
ST_BOOLEAN required = SD_FALSE;

  sclDecCtrl = (SCL_DEC_CTRL *) sxDecCtrl->usr;

  if (sxDecCtrl->reason == SX_ELEMENT_START)
    {
    /* start optional attributes */
    ret = scl_get_attr_ptr (sxDecCtrl, "ix", &ix, required);
    if (ret)
      ix = NULL;
    /* end optional attributes */

    /* start required attributes */
    required = SD_TRUE;
    ret = scl_get_attr_ptr (sxDecCtrl, "name", &name, required);
    if (ret != SD_SUCCESS)
      {
      return;
      }
    /* end required attributes */

    /* Start creation of flattened name */
    sclDecCtrl->flattened[0] = '\0';	/* CRITICAL: start with empty flatname*/
    if (construct_flattened (sclDecCtrl->flattened, sizeof(sclDecCtrl->flattened), name, ix)
        != SD_SUCCESS)
      {	/* error already logged.	*/
      scl_stop_parsing (sxDecCtrl, "_DOI_SEFun", SX_USER_ERROR);
      return;
      }


    SXLOG_CDEC1 ("SCL PARSE: Created flattened variable: '%s'", sclDecCtrl->flattened);

    sx_push (sxDecCtrl, sizeof(DOIElements)/sizeof(SX_ELEMENT), DOIElements, SD_FALSE);    
    }
  else
    {
    sx_pop (sxDecCtrl);
    }
  }

/************************************************************************/
/*			_SDI_SEFun					*/
/************************************************************************/

static ST_VOID _SDI_SEFun (SX_DEC_CTRL *sxDecCtrl)
  {
SCL_DEC_CTRL *sclDecCtrl;
ST_CHAR *ix;
ST_CHAR *name;
ST_RET ret;
ST_CHAR *p;
ST_BOOLEAN required = SD_FALSE;

  sclDecCtrl = (SCL_DEC_CTRL *) sxDecCtrl->usr;

  if (sxDecCtrl->reason == SX_ELEMENT_START)
    {
    /* start optional attributes */
    ret = scl_get_attr_ptr (sxDecCtrl, "ix", &ix, required);
    if (ret)
      ix = NULL;
    /* end optional attributes */

    /* start required attributes */
    required = SD_TRUE;
    ret = scl_get_attr_ptr (sxDecCtrl, "name", &name, required);
    if (ret != SD_SUCCESS)
      {
      return;
      }
    /* end required attributes */

    /* Continue creation of flattened name */
    if (construct_flattened (sclDecCtrl->flattened, sizeof(sclDecCtrl->flattened), name, ix)
        != SD_SUCCESS)
      {	/* error already logged.	*/
      scl_stop_parsing (sxDecCtrl, "_SDI_SEFun", SX_USER_ERROR);
      return;
      }

    SXLOG_CDEC1 ("SCL PARSE: Appended to flattened variable: '%s'", sclDecCtrl->flattened);

    sx_push (sxDecCtrl, sizeof(SDIElements)/sizeof(SX_ELEMENT), SDIElements, SD_FALSE);    
    }
  else /* reason = SX_ELEMENT_END */
    {
    /* Remove the last item from the flattened string */
    p = strrchr(sclDecCtrl->flattened, '$');
    if (p != NULL)
      *p = 0;
    SXLOG_CDEC1 ("SCL PARSE: Removed last item from flattened variable: '%s'", sclDecCtrl->flattened);
    sx_pop (sxDecCtrl);
    }
  }

/************************************************************************/
/*			_DAI_SEFun					*/
/************************************************************************/

static ST_VOID _DAI_SEFun (SX_DEC_CTRL *sxDecCtrl)
  {
SCL_DEC_CTRL *sclDecCtrl;
ST_CHAR *ix;
ST_CHAR *name;
ST_RET ret;
ST_CHAR *p;
ST_BOOLEAN required = SD_FALSE;

  sclDecCtrl = (SCL_DEC_CTRL *) sxDecCtrl->usr;

  if (sxDecCtrl->reason == SX_ELEMENT_START)
    {
    SCL_DAI *scl_dai;
    if ((scl_dai = sclDecCtrl->scl_dai = scl_dai_add (sclDecCtrl->sclInfo)) == NULL)
      {
      scl_stop_parsing (sxDecCtrl, "scl_enumtype_create", SX_USER_ERROR);
      return;
      }

    /* start optional attributes */
    ret = scl_get_attr_ptr (sxDecCtrl, "ix", &ix, required);
    if (ret)
      ix = NULL;
    ret = scl_get_attr_copy (sxDecCtrl, "sAddr", scl_dai->sAddr, (sizeof(scl_dai->sAddr)-1), required);
    ret = scl_get_attr_copy (sxDecCtrl, "valKind", scl_dai->valKind, (sizeof(scl_dai->valKind)-1), required);
    if (ret)
      strcpy (scl_dai->valKind, "Set"); /* default */
    /* end optional attributes */

    /* start required attributes */
    required = SD_TRUE;
    ret = scl_get_attr_ptr (sxDecCtrl, "name", &name, required);
    if (ret != SD_SUCCESS)
      {
      return;
      }
    /* end required attributes */

    /* Continue creation of flattened name */
    if (construct_flattened (sclDecCtrl->flattened, sizeof(sclDecCtrl->flattened), name, ix)
        != SD_SUCCESS)
      {	/* error already logged.	*/
      scl_stop_parsing (sxDecCtrl, "_DAI_SEFun", SX_USER_ERROR);
      return;
      }

    SXLOG_CDEC1 ("SCL PARSE: Appended to flattened variable: '%s'", sclDecCtrl->flattened);
    strcpy (scl_dai->flattened, sclDecCtrl->flattened);
    sx_push (sxDecCtrl, sizeof(DAIElements)/sizeof(SX_ELEMENT), DAIElements, SD_FALSE);    
    }
  else /* reason = SX_ELEMENT_END */
    {
    /* Remove the last item from the flattened string */
    p = strrchr(sclDecCtrl->flattened, '$');
    if (p != NULL)
      *p = 0;
    SXLOG_CDEC1 ("SCL PARSE: Removed last item from flattened variable: '%s'", sclDecCtrl->flattened);
    sx_pop (sxDecCtrl);
    }
  }

/************************************************************************/
/*			_DAI_Val_SEFun					*/
/* Sets "sGroup" and "Val" in sclDecCtrl->scl_dai.			*/
/************************************************************************/

static ST_VOID _DAI_Val_SEFun (SX_DEC_CTRL *sxDecCtrl)
  {
  if (sxDecCtrl->reason == SX_ELEMENT_START)
    {
    SCL_DEC_CTRL *sclDecCtrl = (SCL_DEC_CTRL *) sxDecCtrl->usr;
    /* start optional attributes (don't care about return)	*/
    scl_get_uint_attr (sxDecCtrl, "sGroup", &sclDecCtrl->scl_dai->sGroup, SCL_ATTR_OPTIONAL);
    /* end optional attributes */
    }
  else /* reason = SX_ELEMENT_END */
    {
    SCL_DEC_CTRL *sclDecCtrl = (SCL_DEC_CTRL *) sxDecCtrl->usr;
    ST_RET ret;
    ST_INT strLen;
    ST_CHAR *Val;
    ret = sx_get_string_ptr (sxDecCtrl, &Val, &strLen);
    if (ret==SD_SUCCESS)
      sclDecCtrl->scl_dai->Val = chk_strdup (Val);	/* alloc & store Val*/
    else
      scl_stop_parsing (sxDecCtrl, "DAI Val", SX_USER_ERROR);
    }
  }

/************************************************************************/
/*			_DataTypeTemplates_SEFun			*/
/************************************************************************/

static ST_VOID _DataTypeTemplates_SEFun (SX_DEC_CTRL *sxDecCtrl)
  {
  if (sxDecCtrl->reason == SX_ELEMENT_START)
    sx_push (sxDecCtrl, sizeof(DataTypeTemplatesElements)/sizeof(SX_ELEMENT), DataTypeTemplatesElements, SD_FALSE);    
  else
    sx_pop (sxDecCtrl);
  }

/************************************************************************/
/*			_LNodeType_SEFun				*/
/************************************************************************/

static ST_VOID _LNodeType_SEFun (SX_DEC_CTRL *sxDecCtrl)
  {
SCL_DEC_CTRL *sclDecCtrl;
ST_RET ret;
ST_BOOLEAN required = SD_FALSE;
SCL_LNTYPE *scl_lntype;

  if (sxDecCtrl->reason == SX_ELEMENT_START)
    {
    sclDecCtrl = (SCL_DEC_CTRL *) sxDecCtrl->usr;

    scl_lntype = scl_lntype_create (sclDecCtrl->sclInfo);
    if (scl_lntype == NULL)
      {
      scl_stop_parsing (sxDecCtrl, "scl_lntype_create", SX_USER_ERROR);
      return;
      }

    /* start required attributes */
    required = SD_TRUE;
    ret = scl_get_attr_copy (sxDecCtrl, "id", scl_lntype->id, (sizeof(scl_lntype->id)-1), required);
    ret |= scl_get_attr_copy (sxDecCtrl, "lnClass", scl_lntype->lnClass, (sizeof(scl_lntype->lnClass)-1), required);
    if (ret != SD_SUCCESS)
      {
      return;
      }
    /* end required attributes */

    sx_push (sxDecCtrl, sizeof(LNodeTypeElements)/sizeof(SX_ELEMENT), LNodeTypeElements, SD_FALSE);    
    }
  else
    sx_pop (sxDecCtrl);
  }

/************************************************************************/
/*			_DO_SFun					*/
/************************************************************************/

static ST_VOID _DO_SFun (SX_DEC_CTRL *sxDecCtrl)
  {
SCL_DEC_CTRL *sclDecCtrl;
ST_RET ret;
ST_BOOLEAN required = SD_FALSE;
SCL_DO *scl_do;

  sclDecCtrl = (SCL_DEC_CTRL *) sxDecCtrl->usr;

  scl_do = scl_lntype_add_do (sclDecCtrl->sclInfo);
  if (scl_do == NULL)
    {
    scl_stop_parsing (sxDecCtrl, "scl_lntype_add_do", SX_USER_ERROR);
    return;
    }

  /* start required attributes */
  required = SD_TRUE;
  ret = scl_get_attr_copy (sxDecCtrl, "name", scl_do->name, (sizeof(scl_do->name)-1), required);
  ret |= scl_get_attr_copy (sxDecCtrl, "type", scl_do->type, (sizeof(scl_do->type)-1), required);
  if (ret != SD_SUCCESS)
    {
    return;
    }
  /* end required attributes */
  }

/************************************************************************/
/*			_DOType_SEFun					*/
/************************************************************************/

static ST_VOID _DOType_SEFun (SX_DEC_CTRL *sxDecCtrl)
  {
SCL_DEC_CTRL *sclDecCtrl;
ST_RET ret;
ST_BOOLEAN required = SD_FALSE;
SCL_DOTYPE *scl_dotype;

  if (sxDecCtrl->reason == SX_ELEMENT_START)
    {
    sclDecCtrl = (SCL_DEC_CTRL *) sxDecCtrl->usr;

    scl_dotype = scl_dotype_create (sclDecCtrl->sclInfo);
    if (scl_dotype == NULL)
      {
      scl_stop_parsing (sxDecCtrl, "scl_dotype_create", SX_USER_ERROR);
      return;
      }

    /* start required attributes */
    required = SD_TRUE;
    ret = scl_get_attr_copy (sxDecCtrl, "id", scl_dotype->id, (sizeof(scl_dotype->id)-1), required);
    ret |= scl_get_attr_copy (sxDecCtrl, "cdc", scl_dotype->cdc, (sizeof(scl_dotype->cdc)-1), required);
    if (ret != SD_SUCCESS)
      {
      return;
      }
    /* end required attributes */

    sx_push (sxDecCtrl, sizeof(DOTypeElements)/sizeof(SX_ELEMENT), DOTypeElements, SD_FALSE);    
    }
  else
    sx_pop (sxDecCtrl);
  }

/************************************************************************/
/*			_DA_SEFun					*/
/************************************************************************/

static ST_VOID _DA_SEFun (SX_DEC_CTRL *sxDecCtrl)
  {
  if (sxDecCtrl->reason == SX_ELEMENT_START)
    {
    SCL_DEC_CTRL *sclDecCtrl;
    ST_CHAR *str;	/* use for dchg, qchg, dupd	*/
    ST_RET ret;
    ST_BOOLEAN required = SD_FALSE;
    SCL_DA *scl_da;
    ST_CHAR *desc;

    sclDecCtrl = (SCL_DEC_CTRL *) sxDecCtrl->usr;

    scl_da = sclDecCtrl->scl_da = scl_dotype_add_da (sclDecCtrl->sclInfo);
    if (scl_da == NULL)
      {
      scl_stop_parsing (sxDecCtrl, "scl_dotype_add_da", SX_USER_ERROR);
      return;
      }
  
    /* start optional attributes */
    ret = scl_get_attr_ptr (sxDecCtrl, "desc", &desc, required);
    if (ret == SD_SUCCESS)
      scl_da->desc = chk_strdup (desc);	/* Alloc & copy desc string	*/
    ret = scl_get_attr_copy (sxDecCtrl, "sAddr", scl_da->sAddr, (sizeof(scl_da->sAddr)-1), required);
    ret = scl_get_attr_copy (sxDecCtrl, "valKind", scl_da->valKind, (sizeof(scl_da->valKind)-1), required);
    if (ret)
      strcpy (scl_da->valKind, "Set"); /* default */
    ret = scl_get_attr_copy (sxDecCtrl, "type", scl_da->type, (sizeof(scl_da->type)-1), required);
    ret = scl_get_uint_attr (sxDecCtrl, "count", &scl_da->count, required);
    ret = scl_get_attr_ptr (sxDecCtrl, "dchg", &str, required);
    if (ret == SD_SUCCESS  &&  stricmp(str, "true") == 0)
      scl_da->dchg = SD_TRUE;
  
    ret = scl_get_attr_ptr (sxDecCtrl, "qchg", &str, required);
    if (ret == SD_SUCCESS  &&  stricmp(str, "true") == 0)
      scl_da->qchg = SD_TRUE;
  
    ret = scl_get_attr_ptr (sxDecCtrl, "dupd", &str, required);
    if (ret == SD_SUCCESS  &&  stricmp(str, "true") == 0)
      scl_da->dupd = SD_TRUE;
    /* end optional attributes */
  
    /* start required attributes */
    required = SD_TRUE;
    ret = scl_get_attr_copy (sxDecCtrl, "name", scl_da->name, (sizeof(scl_da->name)-1), required);
    ret |= scl_get_attr_copy (sxDecCtrl, "bType", scl_da->bType, (sizeof(scl_da->bType)-1), required);
    ret |= scl_get_attr_copy (sxDecCtrl, "fc", scl_da->fc, (sizeof(scl_da->fc)-1), required);
    if (ret != SD_SUCCESS)
      {
      return;
      }
    /* end required attributes */
    sx_push (sxDecCtrl, sizeof(DAElements)/sizeof(SX_ELEMENT), DAElements, SD_FALSE);
    }
  else
    sx_pop (sxDecCtrl);
  }

/************************************************************************/
/*			_SDO_SFun					*/
/************************************************************************/

static ST_VOID _SDO_SFun (SX_DEC_CTRL *sxDecCtrl)
  {
SCL_DEC_CTRL *sclDecCtrl;
ST_RET ret;
ST_BOOLEAN required = SD_FALSE;
SCL_DA *scl_da;
ST_CHAR *desc;

  sclDecCtrl = (SCL_DEC_CTRL *) sxDecCtrl->usr;

  scl_da = scl_dotype_add_sdo (sclDecCtrl->sclInfo);
  if (scl_da == NULL)
    {
    scl_stop_parsing (sxDecCtrl, "scl_dotype_add_sdo", SX_USER_ERROR);
    return;
    }

  /* start optional attributes */
  ret = scl_get_attr_ptr (sxDecCtrl, "desc", &desc, required);
  if (ret == SD_SUCCESS)
    scl_da->desc = chk_strdup (desc);	/* Alloc & copy desc string	*/
  /* end optional attributes */

  /* start required attributes */
  required = SD_TRUE;
  ret = scl_get_attr_copy (sxDecCtrl, "name", scl_da->name, (sizeof(scl_da->name)-1), required);
  ret |= scl_get_attr_copy (sxDecCtrl, "type", scl_da->type, (sizeof(scl_da->type)-1), required);
  if (ret != SD_SUCCESS)
    {
    return;
    }
  /* end required attributes */
  }

/************************************************************************/
/*			_DA_Val_SEFun					*/
/************************************************************************/

static ST_VOID _DA_Val_SEFun (SX_DEC_CTRL *sxDecCtrl)
  {
  if (sxDecCtrl->reason == SX_ELEMENT_START)
    {
    SCL_DEC_CTRL *sclDecCtrl = (SCL_DEC_CTRL *) sxDecCtrl->usr;
    /* start optional attributes (don't care about return)	*/
    scl_get_uint_attr (sxDecCtrl, "sGroup", &sclDecCtrl->scl_da->sGroup, SCL_ATTR_OPTIONAL);
    /* end optional attributes */
    }
  else /* reason = SX_ELEMENT_END */
    {
    SCL_DEC_CTRL *sclDecCtrl = (SCL_DEC_CTRL *) sxDecCtrl->usr;
    ST_RET ret;
    ST_INT strLen;
    ST_CHAR *Val;
    ret = sx_get_string_ptr (sxDecCtrl, &Val, &strLen);
    if (ret==SD_SUCCESS)
      sclDecCtrl->scl_da->Val = chk_strdup (Val);	/* alloc & store Val*/
    else
      scl_stop_parsing (sxDecCtrl, "DA Val", SX_USER_ERROR);
    }
  }

/************************************************************************/
/*			_DAType_SEFun					*/
/************************************************************************/

static ST_VOID _DAType_SEFun (SX_DEC_CTRL *sxDecCtrl)
  {
SCL_DEC_CTRL *sclDecCtrl;
ST_RET ret;
ST_BOOLEAN required = SD_FALSE;
SCL_DATYPE *scl_datype;

  if (sxDecCtrl->reason == SX_ELEMENT_START)
    {
    sclDecCtrl = (SCL_DEC_CTRL *) sxDecCtrl->usr;

    scl_datype = scl_datype_create (sclDecCtrl->sclInfo);
    if (scl_datype == NULL)
      {
      scl_stop_parsing (sxDecCtrl, "scl_datype_create", SX_USER_ERROR);
      return;
      }

    /* start required attributes */
    required = SD_TRUE;
    ret = scl_get_attr_copy (sxDecCtrl, "id", scl_datype->id, (sizeof(scl_datype->id)-1), required);
    if (ret != SD_SUCCESS)
      {
      return;
      }
    /* end required attributes */

    sx_push (sxDecCtrl, sizeof(DATypeElements)/sizeof(SX_ELEMENT), DATypeElements, SD_FALSE);    
    }
  else
    sx_pop (sxDecCtrl);
  }

/************************************************************************/
/*			_BDA_SEFun					*/
/************************************************************************/

static ST_VOID _BDA_SEFun (SX_DEC_CTRL *sxDecCtrl)
  {
  if (sxDecCtrl->reason == SX_ELEMENT_START)
    {
    SCL_DEC_CTRL *sclDecCtrl;
    ST_RET ret;
    ST_BOOLEAN required = SD_FALSE;
    SCL_BDA *scl_bda;
    ST_CHAR *desc;

    sclDecCtrl = (SCL_DEC_CTRL *) sxDecCtrl->usr;

    scl_bda = sclDecCtrl->scl_bda = scl_datype_add_bda (sclDecCtrl->sclInfo);
    if (scl_bda == NULL)
      {
      scl_stop_parsing (sxDecCtrl, "scl_datype_add_bda", SX_USER_ERROR);
      return;
      }

    /* start optional attributes */
    ret = scl_get_attr_ptr (sxDecCtrl, "desc", &desc, required);
    if (ret == SD_SUCCESS)
      scl_bda->desc = chk_strdup (desc);	/* Alloc & copy desc string	*/
    ret = scl_get_attr_copy (sxDecCtrl, "sAddr", scl_bda->sAddr, (sizeof(scl_bda->sAddr)-1), required);
    ret = scl_get_attr_copy (sxDecCtrl, "valKind", scl_bda->valKind, (sizeof(scl_bda->valKind)-1), required);
    if (ret)
      strcpy (scl_bda->valKind, "Set"); /* default */
    ret = scl_get_attr_copy (sxDecCtrl, "type", scl_bda->type, (sizeof(scl_bda->type)-1), required);
    ret = scl_get_uint_attr (sxDecCtrl, "count", &scl_bda->count, required);
    /* end optional attributes */

    /* start required attributes */
    required = SD_TRUE;
    ret = scl_get_attr_copy (sxDecCtrl, "name", scl_bda->name, (sizeof(scl_bda->name)-1), required);
    ret |= scl_get_attr_copy (sxDecCtrl, "bType", scl_bda->bType, (sizeof(scl_bda->bType)-1), required);
    if (ret != SD_SUCCESS)
      {
      return;
      }
    /* end required attributes */
    sx_push (sxDecCtrl, sizeof(BDAElements)/sizeof(SX_ELEMENT), BDAElements, SD_FALSE);
    }
  else
    sx_pop (sxDecCtrl);
  }

/************************************************************************/
/*			_BDA_Val_SEFun					*/
/************************************************************************/

static ST_VOID _BDA_Val_SEFun (SX_DEC_CTRL *sxDecCtrl)
  {
  if (sxDecCtrl->reason == SX_ELEMENT_START)
    {
    SCL_DEC_CTRL *sclDecCtrl = (SCL_DEC_CTRL *) sxDecCtrl->usr;
    /* start optional attributes (don't care about return)	*/
    scl_get_uint_attr (sxDecCtrl, "sGroup", &sclDecCtrl->scl_bda->sGroup, SCL_ATTR_OPTIONAL);
    /* end optional attributes */
    }
  else /* reason = SX_ELEMENT_END */
    {
    SCL_DEC_CTRL *sclDecCtrl = (SCL_DEC_CTRL *) sxDecCtrl->usr;
    ST_RET ret;
    ST_INT strLen;
    ST_CHAR *Val;
    ret = sx_get_string_ptr (sxDecCtrl, &Val, &strLen);
    if (ret==SD_SUCCESS)
      sclDecCtrl->scl_bda->Val = chk_strdup (Val);	/* alloc & store Val*/
    else
      scl_stop_parsing (sxDecCtrl, "BDA Val", SX_USER_ERROR);
    }
  }

/************************************************************************/
/*			_EnumType_SEFun					*/
/************************************************************************/

static ST_VOID _EnumType_SEFun (SX_DEC_CTRL *sxDecCtrl)
  {
SCL_DEC_CTRL *sclDecCtrl;
ST_RET ret;
ST_BOOLEAN required = SD_FALSE;
SCL_ENUMTYPE *scl_enumtype;

  if (sxDecCtrl->reason == SX_ELEMENT_START)
    {
    sclDecCtrl = (SCL_DEC_CTRL *) sxDecCtrl->usr;

    if ((scl_enumtype = scl_enumtype_create (sclDecCtrl->sclInfo)) == NULL)
      {
      scl_stop_parsing (sxDecCtrl, "scl_enumtype_create", SX_USER_ERROR);
      return;
      }

    /* start required attributes */
    required = SD_TRUE;
    ret = scl_get_attr_copy (sxDecCtrl, "id", scl_enumtype->id, (sizeof(scl_enumtype->id)-1), required);
    if (ret != SD_SUCCESS)
      {
      return;
      }
    /* end required attributes */

    sx_push (sxDecCtrl, sizeof(EnumTypeElements)/sizeof(SX_ELEMENT), EnumTypeElements, SD_FALSE);    
    }
  else
    sx_pop (sxDecCtrl);
  }

/************************************************************************/
/*			_EnumVal_SEFun					*/
/************************************************************************/

static ST_VOID _EnumVal_SEFun (SX_DEC_CTRL *sxDecCtrl)
  {
SCL_DEC_CTRL *sclDecCtrl;
ST_RET ret;
ST_INT strLen;
ST_BOOLEAN required = SD_FALSE;
SCL_ENUMVAL *scl_enumval;

  sclDecCtrl = (SCL_DEC_CTRL *) sxDecCtrl->usr;

  if (sxDecCtrl->reason == SX_ELEMENT_START)
    {
    if ((scl_enumval = sclDecCtrl->scl_enumval = scl_enumtype_add_enumval (sclDecCtrl->sclInfo)) == NULL)
      {
      scl_stop_parsing (sxDecCtrl, "scl_enumtype_add_enumval", SX_USER_ERROR);
      return;
      }

    /* start required attributes */
    required = SD_TRUE;
    ret = scl_get_int_attr (sxDecCtrl, "ord", &scl_enumval->ord, required);
    if (ret != SD_SUCCESS)
      {
      return;
      }
    /* end required attributes */
    }
  else /* reason = SX_ELEMENT_END */
    {
    scl_enumval = sclDecCtrl->scl_enumval;
    /* CRITICAL: Init strLen = max len. After sx_get_string, strLen = actual len*/
    strLen = sizeof(scl_enumval->EnumVal)-1;
    ret = sx_get_string (sxDecCtrl, scl_enumval->EnumVal, &strLen);
    if (ret != SD_SUCCESS)
      scl_stop_parsing (sxDecCtrl, "_EnumVal_SEFun", SX_USER_ERROR);
    }
  }

/************************************************************************/
/*			_scl_unknown_el_start				*/
/************************************************************************/

static ST_RET _scl_unknown_el_start (SX_DEC_CTRL *sxDecCtrl, ST_CHAR *tag)
  {
  SXLOG_DEC1 ("SCL PARSE: Unneeded or unknown element '%s'", tag);
  return (SD_SUCCESS);
  }

/************************************************************************/
/*			_scl_unknown_el_end				*/
/************************************************************************/

static ST_RET _scl_unknown_el_end (SX_DEC_CTRL *sxDecCtrl, ST_CHAR *tag)
  {
  return (SD_SUCCESS);
  }

/************************************************************************/
/*			_SampledValueControl_SEFun			*/
/* DEBUG: if parser called separate start and end functions, the lower	*/
/*   functs could be called directly & this funct would not be needed.	*/
/************************************************************************/
static ST_VOID _SampledValueControl_SEFun (SX_DEC_CTRL *sxDecCtrl)
  {
  if (sxDecCtrl->reason == SX_ELEMENT_START)
    _SampledValueControl_SFun (sxDecCtrl);
  else
    _SampledValueControl_EFun (sxDecCtrl);
  }
/************************************************************************/
/*			_SampledValueControl_SFun			*/
/* Handle Start tag							*/
/************************************************************************/
static ST_VOID _SampledValueControl_SFun (SX_DEC_CTRL *sxDecCtrl)
  {
SCL_DEC_CTRL *sclDecCtrl;
ST_CHAR *str;		/* ptr set by scl_get_attr_ptr	*/
ST_RET ret;
SCL_SVCB *scl_svcb;
ST_CHAR *desc;

  sclDecCtrl = (SCL_DEC_CTRL *) sxDecCtrl->usr;

  /* Alloc struct, save in sclDecCtrl, & set local ptr to it.	*/
  scl_svcb = sclDecCtrl->scl_svcb = scl_svcb_add (sclDecCtrl->sclInfo);
  if (scl_svcb == NULL)
    {
    scl_stop_parsing (sxDecCtrl, "scl_svcb_add", SX_USER_ERROR);
    return;
    }
  
  /* start required attributes */
  ret = scl_get_attr_copy (sxDecCtrl, "name", scl_svcb->name, (sizeof(scl_svcb->name)-1), SCL_ATTR_REQUIRED);
  ret |= scl_get_attr_copy (sxDecCtrl, "smvID", scl_svcb->smvID, (sizeof(scl_svcb->smvID)-1), SCL_ATTR_REQUIRED);
  ret |= scl_get_uint_attr (sxDecCtrl, "smpRate", &scl_svcb->smpRate, SCL_ATTR_REQUIRED);
  ret |= scl_get_uint_attr (sxDecCtrl, "nofASDU", &scl_svcb->nofASDU, SCL_ATTR_REQUIRED);
  if (ret != SD_SUCCESS)
    return;	/* At least one required attr not found. Stop now.	*/
  /* end required attributes */

  /* start optional attributes */
  ret = scl_get_attr_ptr (sxDecCtrl, "desc", &desc, SCL_ATTR_OPTIONAL);
  if (ret == SD_SUCCESS)
    scl_svcb->desc = chk_strdup (desc);	/* Alloc & copy desc string	*/
  ret = scl_get_attr_copy (sxDecCtrl, "datSet", scl_svcb->datSet, (sizeof(scl_svcb->datSet)-1), SCL_ATTR_OPTIONAL);
  ret = scl_get_uint_attr (sxDecCtrl, "confRev", &scl_svcb->confRev, SCL_ATTR_OPTIONAL);
  ret = scl_get_attr_ptr (sxDecCtrl, "multicast", &str, SCL_ATTR_OPTIONAL);	/* chk "str" below*/
  if (ret == SD_SUCCESS  &&  stricmp(str, "false") == 0)
    scl_svcb->multicast = SD_FALSE;
  else
    scl_svcb->multicast = SD_TRUE;  /* default value */
  /* end optional attributes */
  
  sx_push (sxDecCtrl, sizeof(SampledValueControlElements)/sizeof(SX_ELEMENT), SampledValueControlElements, SD_FALSE);
  }
/************************************************************************/
/*			_SampledValueControl_EFun			*/
/* Handle End tag							*/
/************************************************************************/
static ST_VOID _SampledValueControl_EFun (SX_DEC_CTRL *sxDecCtrl)
  {
  sx_pop (sxDecCtrl);
  }

/************************************************************************/
/*			_SmvOpts_SFun					*/
/************************************************************************/
static ST_VOID _SmvOpts_SFun (SX_DEC_CTRL *sxDecCtrl)
  {
SCL_DEC_CTRL *sclDecCtrl;
ST_CHAR *str;		/* ptr set by scl_get_attr_ptr	*/
ST_RET ret;
SCL_SVCB *scl_svcb;

  sclDecCtrl = (SCL_DEC_CTRL *) sxDecCtrl->usr;
  scl_svcb = sclDecCtrl->scl_svcb;
  
  /* start optional attributes */
  ret = scl_get_attr_ptr (sxDecCtrl, "sampleRate", &str, SCL_ATTR_OPTIONAL);
  if (ret == SD_SUCCESS  &&  stricmp(str, "true") == 0)
    {
    BSTR_BIT_SET_ON(scl_svcb->OptFlds, SVOPT_BITNUM_SMPRATE);
    }

  ret = scl_get_attr_ptr (sxDecCtrl, "refreshTime", &str, SCL_ATTR_OPTIONAL);
  if (ret == SD_SUCCESS  &&  stricmp(str, "true") == 0)
    {
    BSTR_BIT_SET_ON(scl_svcb->OptFlds, SVOPT_BITNUM_REFRTM);
    }

  ret = scl_get_attr_ptr (sxDecCtrl, "sampleSynchronized", &str, SCL_ATTR_OPTIONAL);
  if (ret == SD_SUCCESS  &&  stricmp(str, "true") == 0)
    {
    BSTR_BIT_SET_ON(scl_svcb->OptFlds, SVOPT_BITNUM_SMPSYNCH);
    }

  ret = scl_get_attr_ptr (sxDecCtrl, "security", &str, SCL_ATTR_OPTIONAL);
  if (ret == SD_SUCCESS  &&  stricmp(str, "true") == 0)
    scl_svcb->securityPres = SD_TRUE;	/* scl_svcb calloced so init val is FALSE*/

  /* NOTE: SCL calls this "dataRef", but 7-2 & 9-2 call it "DatSet".	*/
  ret = scl_get_attr_ptr (sxDecCtrl, "dataRef", &str, SCL_ATTR_OPTIONAL);
  if (ret == SD_SUCCESS  &&  stricmp(str, "true") == 0)
    scl_svcb->dataRefPres = SD_TRUE;	/* scl_svcb calloced so init val is FALSE*/
  /* end optional attributes */
  }
