/************************************************************************/
/* SISCO SOFTWARE MODULE HEADER *****************************************/
/************************************************************************/
/*   (c) Copyright Systems Integration Specialists Company, Inc.,	*/
/*            	  1986 - 2006, All Rights Reserved.             	*/
/*									*/
/*		    PROPRIETARY AND CONFIDENTIAL			*/
/*									*/
/* MODULE NAME : mms_tdef.c						*/
/* PRODUCT(S)  : MMSEASE						*/
/*									*/
/* MODULE DESCRIPTION : 						*/
/* Function to decode ASN.1 type definitions into Runtime typedefs	*/
/*									*/
/* GLOBAL FUNCTIONS DEFINED IN THIS MODULE :				*/
/*			ms_runtime_create				*/
/*			ms_runtime_destroy				*/
/*			ms_is_rt_prim					*/
/*			ms_rt_el_tag_text				*/
/*									*/
/* MODIFICATION LOG :							*/
/*  Date     Who   Rev			Comments			*/
/* --------  ---  ------   -------------------------------------------	*/
/* 08/06/07  JRB     59    Chg ms_rt_bld_* to avoid realloc (reduces	*/
/*			   max memory usage and fragmentation).		*/
/* 04/17/07  JRB     58    ms_get_blocked_length: set also prim_count.	*/
/* 02/02/07  JRB     57    Fix log message in ms_rt_bld_chk_state.	*/
/* 10/30/06  JRB     56    Add ms_rt_bld_* functions.			*/
/* 03/22/06  EJV     55    Chg RT_GENERAL_TIME to use time_t len & algn.*/
/* 01/30/06  GLB     54    Integrated porting changes for VMS           */
/* 08/01/05  JRB     53    Avoid risky cast to remove const.		*/
/* 04/07/05  MDE     52    Changed ERR log to NERR			*/
/* 03/10/05  JRB     51    Add ms_rt_el_tag_text funct.			*/
/* 02/23/05  JRB     50    ms_is_rt_prim: add SD_CONST to arg.		*/
/* 02/21/05  JRB     49    Del m_struct_* for Lite, created by FOUNDRY.	*/
/*			   Del asserts checking m_struct_*.		*/
/* 01/27/05  JRB     48    Move m_lite_data.. extern to mms_vvar.h.	*/
/*			   Init m_data_algn_tbl=NULL if FOUNDRY.	*/
/*			   Add ; to one more MLOG* call.		*/
/* 11/02/04  JRB     47    For MMS_LITE, use m_lite_data_algn_tbl	*/
/*			   generated by Foundry for target platform.	*/
/* 11/01/04  JRB     46    Add ; on MLOG* calls to work with new macros.*/
/* 06/29/04  JRB     45    str_done: chk for empty structure.		*/
/* 12/09/03  JRB     44    Add LYNX support.				*/
/* 11/07/03  JRB     43    Fix misleading log message when decode done.	*/
/* 10/13/03  EJV     42    Removed defined(__VAX).			*/
/* 06/05/03  JRB     41    type_name_cstr_done: copy component names	*/
/*			   for USE_RT_TYPE_2.				*/
/* 04/28/03  JRB     40    Eliminate compiler warnings.			*/
/* 04/28/03  JRB     39    Add UTF8string support (see RT_UTF8_STRING).	*/
/* 03/13/03  JRB     38    u_ml_get_rt_type: chg from "func ptr" to func.*/
/* 02/20/03  JRB     37    get_str_comp_name ret err if name index	*/
/*			   can't be found for USE_RT_TYPE_3.		*/
/*			   type_name_cstr_done save & restore comp name	*/
/*			   for USE_RT_TYPE_2/USE_RT_TYPE_3.		*/
/* 02/20/03  JRB     36	   Del PSOS code.				*/
/* 01/20/03  CRM     35    Added "defined(linux)" code.			*/
/* 01/03/03  JRB     34    Move arr_loop* globals to locals in ms_get..	*/
/* 12/31/02  MDE     33    Eliminate use of m_calc_rt_size global.	*/
/* 12/26/02  JRB     32    Add ms_asn1_to_runtime_x for backward compat.*/
/* 12/16/02  JRB     31    Add ms_is_rt_prim funct.			*/
/* 11/11/02  JRB     30    Chg ms_runtime_create/destroy args.		*/
/*			   Make ms_asn1_to_runtime static, must call	*/
/*			   ms_runtime_create instead.			*/
/*			   Use DEC_INFO struct to avoid global vars.	*/
/*			   Pass aCtx to ms_asn1_to_runtime & pass	*/
/*			   DEC_INFO in aCtx->usr_info[0].		*/
/* 04/16/02  MDE     29    Now set name index for named struct elements	*/
/* 03/04/02  JRB     28    Add more "SD_CONST RUNTIME_TYPE *".		*/
/* 12/20/01  JRB     27    Converted to use ASN1R (re-entrant ASN1)	*/
/* 11/14/01  EJV     26    Added support for new MMS type UtcTime:	*/
/*			   Added fun get_utc_time_el;			*/
/*			   set_type_tags: added code for tag 17;	*/
/*			   _ms_m_get_rt_info: added case for RT_UTC_TIME*/
/*			   _ms_log_runtime: added case for RT_UTC_TIME;	*/
/* 10/18/01  JRB     25    Eliminate warning.				*/
/* 08/29/00  JRB     24    Del ms_comp_name_find function. Use macro.	*/
/* 07/28/00  JRB     23    Simplify RT_TYPE ifdefs.			*/
/*			   Fix SD_CONST usage & lint warnings.		*/
/*			   Make ms_type_nam* static. Only used here.	*/
/* 07/13/00  JRB     22    Add ms_runtime_create, ms_runtime_destroy,	*/
/*			     ms_comp_name_find.				*/
/* 06/29/00  JRB     21    Make ms_asn1_to_runtime work for MMS LITE.	*/
/*			     Must def USE_RT_TYPE_2 to save comp names.	*/
/* 09/13/99  MDE     20    Added SD_CONST modifiers			*/
/* 04/14/99  MDE     19    Removed unnecessary include files		*/
/* 02/23/99  JRB     18    MMS_LITE: "m_data_algn.. = m_def_data_algn.."*/
/*			   Don't need packed for backward compatibility.*/
/* 09/10/98  MDE     17    Changes to work better with MMS-LITE		*/
/* 08/21/98  EJV     16    __hpux STRSTRT_ALGN set from 3 to 0, and 	*/
/* 07/23/98  RKR     15	   OPEN_VMS STRSTRT_ALGN set from 3 to 0	*/
/* 06/26/98  EJV     14    m_def_data_algn_tbl only for __alpha DECUNIX.*/
/* 06/03/98  MDE     13    Corrected structure end alignment handling	*/
/* 03/23/98  MDE     12    Tweaks to structure alignment		*/
/* 03/12/98  MDE     11    Worked on structure alignment		*/
/* 02/10/98  MDE     10    Runtime type changes for LITE		*/
/* 11/04/97  DSF     09    Fixed alignment table for WIN32		*/
/* 10/30/97  MDE     08    Minor logging change				*/
/* 09/29/97  MDE     07    Minor logging change				*/
/* 08/27/97  MDE     06    Fixed problem with log runtime w/numrt = 0	*/
/* 08/15/97  MDE     05    BTOD handling changes			*/
/* 08/04/97  MDE     04    Minor cleanup				*/
/* 06/19/97  MDE     03    Added 64 bit integer support			*/
/* 06/09/97  MDE     02    Fixed MMS-LITE compile problems, other minor	*/
/* 04/08/97  MDE     01    Fixed 'RT Table too small prob w/named types	*/
/* 04/02/97  DTL   7.00    MMSEASE 7.0 release. See MODL70.DOC for	*/
/*			   history.					*/
/************************************************************************/

#include "glbtypes.h"

#include "sysincs.h"
#include "glbsem.h"
#include "mmsdefs.h"
#include "mms_pvar.h"
#include "mms_vvar.h"
#include "asn1defs.h"
#if defined(MMS_LITE)
#include "mvl_defs.h"
#endif

/************************************************************************/
/************************************************************************/
/* 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


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

#ifdef MMS_LITE
ST_VOID (*u_rt_type_process) (SD_CONST RUNTIME_TYPE *rt, ST_INT num_rt);
#endif

static ST_VOID _ms_log_runtime (ST_INT indent, SD_CONST RUNTIME_TYPE *rt, 
				ST_INT num_rt);

static ST_VOID _do_padding (RUNTIME_TYPE *padDest, 
			    ST_INT *curr_offset, 
			    ST_INT algn);
static ST_VOID _get_struct_start_align (SD_CONST RUNTIME_TYPE *rt, 
				 ST_INT *algn_out,
				 ST_INT *ellen_out);
static ST_VOID _get_struct_end_align (SD_CONST RUNTIME_TYPE *rt, 
				 ST_INT *algn_out,
				 ST_INT *ellen_out);
static ST_VOID _get_arr_start_align (SD_CONST RUNTIME_TYPE *rt, 
				 ST_INT *algn_out,
				 ST_INT *ellen_out);

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

static ST_VOID str_comp_seq_start (ASN1_DEC_CTXT *aCtx);	/* CTX 1 CSTR - seq of comp	*/
static ST_VOID get_num_arr_el (ASN1_DEC_CTXT *aCtx);
static ST_VOID array_done (ASN1_DEC_CTXT *aCtx);
static ST_VOID arr_el_cstr_done (ASN1_DEC_CTXT *aCtx);
static ST_VOID get_arr_el (ASN1_DEC_CTXT *aCtx);
static ST_VOID float_cstr_done (ASN1_DEC_CTXT *aCtx);
static ST_VOID get_float_exp (ASN1_DEC_CTXT *aCtx);
static ST_VOID get_float_fract (ASN1_DEC_CTXT *aCtx);
static ST_VOID get_str_packed (ASN1_DEC_CTXT *aCtx);   	/* CTX 0 BOOL - packed		*/
static ST_VOID get_arr_packed (ASN1_DEC_CTXT *aCtx);
static ST_VOID type_name_cstr_done (ASN1_DEC_CTXT *aCtx);
static ST_VOID type_name_done (ASN1_DEC_CTXT *aCtx);
static ST_VOID get_named_type (ASN1_DEC_CTXT *aCtx);
static ST_VOID get_array_el (ASN1_DEC_CTXT *aCtx);
static ST_VOID get_struct_el (ASN1_DEC_CTXT *aCtx);
static ST_VOID get_bool_el (ASN1_DEC_CTXT *aCtx);
static ST_VOID get_bitstr_el (ASN1_DEC_CTXT *aCtx);
static ST_VOID get_int_el (ASN1_DEC_CTXT *aCtx);
static ST_VOID get_uint_el (ASN1_DEC_CTXT *aCtx);
static ST_VOID get_float_el (ASN1_DEC_CTXT *aCtx);
static ST_VOID get_octstr_el (ASN1_DEC_CTXT *aCtx);
static ST_VOID get_vstr_el (ASN1_DEC_CTXT *aCtx);
static ST_VOID get_gtime_el (ASN1_DEC_CTXT *aCtx);
static ST_VOID get_btime_el (ASN1_DEC_CTXT *aCtx);
static ST_VOID get_bcd_el (ASN1_DEC_CTXT *aCtx);
static ST_VOID get_utc_time_el (ASN1_DEC_CTXT *aCtx);
static ST_VOID get_utf8_el (ASN1_DEC_CTXT *aCtx);
static ST_VOID set_type_tags (ASN1_DEC_CTXT *aCtx);
static ST_VOID td_done (ASN1_DEC_CTXT *aCtx);
static ST_VOID td_err (ASN1_DEC_CTXT *aCtx, ST_RET err);

/************************************************************************/
/************************************************************************/
/* Variables and definitions used in this module			*/

static RUNTIME_TYPE  *rt_ptr;	/* current runtime element	*/

static ST_INT  rt_count; 	/* total elements in runtime table	*/
				/* (also index) 			*/
static ST_INT  rt_limit; 	/* max # runtime elements		*/


/* defines used during decode for various types of errors		*/
#define PDU_ERR 		2
#define LENGTH_ERR		3
#define UNSUPPORTED		4
#define UNKNOWN_TYPENAME	5
#define RUNTIME_LIMIT		6

static ST_INT arr_index[ASN1_MAX_LEVEL]; /* index to array element start */
static RUNTIME_TYPE *struct_tracking[ASN1_MAX_LEVEL];

static OBJECT_NAME type_name;	/* temp storage 		*/


/************************************************************************/
/*			ms_asn1_to_runtime				*/
/************************************************************************/

static ST_INT _ms_asn1_to_runtime (ASN1_DEC_CTXT *aCtx,
			   ST_UCHAR *asn1ptr, ST_UINT asn1len,
			   RUNTIME_TYPE *rt_table, ST_INT rt_len)
  {
MMSDEC_INFO dummy_rslt;
ASN1_TO_RT_DEC_INFO *decInfo = (ASN1_TO_RT_DEC_INFO *) aCtx->usr_info[0];

  S_LOCK_COMMON_RESOURCES ();

  _mmsdec_rslt = &dummy_rslt;	/* give _ms_get_mms_objname a place to point */
				/* to					*/
  rt_ptr  = rt_table;		/* initialize the element pointer	*/
  decInfo->rt_start  = rt_table;/* initialize the element pointer	*/
  rt_limit = rt_len;		/* save the limit			*/

  rt_count = 0; 		/* set element count = 0		*/

#if defined(USE_RT_TYPE_2)
  decInfo->comp_name_used = 0;
  decInfo->comp_name_ptr = decInfo->comp_name_buf;
#endif

  aCtx->asn1r_decode_method = ASN1_TAG_METHOD;

  aCtx->asn1r_decode_done_fun = td_done;	/* set up done function 		*/
  aCtx->asn1r_err_fun = td_err;		/* set up error detected fun		*/

  set_type_tags (aCtx);		/* setup the legal tags 		*/

  asn1r_decode_asn1 (aCtx, asn1ptr,asn1len);	/* do message decode and	*/

/* If decode was OK and we are producing a real RT table, need to set	*/
/* the data element sizes.						*/
  if (rt_count && !decInfo->calc_only)
    {
    ms_rt_size_calc (rt_table,rt_count);

/* Also log the runtime type, if selected				*/
    if (mms_debug_sel & MMS_LOG_RT)
      ms_log_runtime (rt_table, rt_count);
    }

  S_UNLOCK_COMMON_RESOURCES ();
  return (rt_count);			/* return # elements used	*/
  }


/************************************************************************/
/*			set_type_tags					*/
/* setup tags for main TypeSpecfication decode				*/
/************************************************************************/

static ST_VOID set_type_tags (ASN1_DEC_CTXT *aCtx)
  {
  ASN1R_TAG_ADD (aCtx, CTX|CONSTR,0,get_named_type);
  ASN1R_TAG_ADD (aCtx, CTX|CONSTR,1,get_array_el);
  ASN1R_TAG_ADD (aCtx, CTX|CONSTR,2,get_struct_el);
  ASN1R_TAG_ADD (aCtx, CTX,3,get_bool_el);
  ASN1R_TAG_ADD (aCtx, CTX,4,get_bitstr_el);
  ASN1R_TAG_ADD (aCtx, CTX,5,get_int_el);
  ASN1R_TAG_ADD (aCtx, CTX,6,get_uint_el);
  ASN1R_TAG_ADD (aCtx, CTX|CONSTR,7,get_float_el);
  ASN1R_TAG_ADD (aCtx, CTX,9,get_octstr_el);
  ASN1R_TAG_ADD (aCtx, CTX,10,get_vstr_el);
  ASN1R_TAG_ADD (aCtx, CTX,11,get_gtime_el);
  ASN1R_TAG_ADD (aCtx, CTX,12,get_btime_el);
  ASN1R_TAG_ADD (aCtx, CTX,13,get_bcd_el);
  ASN1R_TAG_ADD (aCtx, CTX,17,get_utc_time_el);
  ASN1R_TAG_ADD (aCtx, CTX,RT_UTF8_STRING,get_utf8_el);
  }

/************************************************************************/
/*			rt_ptr_inc					*/
/* move the rt_ptr to next element and inc count. Checks vs limit.	*/
/************************************************************************/

static ST_VOID rt_ptr_inc (ASN1_DEC_CTXT *aCtx)
  {
  if (++rt_count >= rt_limit)	/* move the runtime pointer		*/
    {
    MLOG_CDEC0 ("decode_done");
    aCtx->asn1r_decode_done = SD_TRUE;
    }
  else
    rt_ptr++;			/* point to next runtime element	*/
  }

/************************************************************************/
/************************************************************************/
/* TypeSpecification element get start functions			*/
/************************************************************************/
/************************************************************************/
/*			get_named_type					*/
/* named type element, check if in database, copy if so.		*/
/************************************************************************/


static ST_VOID get_named_type (ASN1_DEC_CTXT *aCtx)
  {
  MLOG_CDEC0 ("get_named_type");

/* execute 'type_name_done' when type name has been extracted    */
  _ms_get_mms_objname (aCtx, &type_name,type_name_done);
  }

/************************************************************************/
/*			type_name_done					*/
/* The type_name objectName has been read, and is now in 'type_name'	*/
/* That means that the type_name constructor now can be complete.	*/
/************************************************************************/

static ST_VOID type_name_done (ASN1_DEC_CTXT *aCtx)
  {
  MLOG_CDEC0 ("type_name_done");

  aCtx->asn1r_c_done_fun[aCtx->asn1r_msg_level] = type_name_cstr_done;
  }


/************************************************************************/
/*			get_array_el					*/
/* array element, setup to get array parameters.			*/
/************************************************************************/

static ST_VOID get_array_el (ASN1_DEC_CTXT *aCtx)
  {
ASN1_TO_RT_DEC_INFO *decInfo = (ASN1_TO_RT_DEC_INFO *) aCtx->usr_info[0];

  MLOG_CDEC0 ("get_array_el");

  if (!decInfo->calc_only)
    rt_ptr->el_tag = RT_ARR_START; 	/* write the tag		*/

  arr_index[aCtx->asn1r_msg_level] = rt_count;	/* save index to this element	*/

  ASN1R_TAG_ADD (aCtx, CTX,0,get_arr_packed);/* set up to get next items	*/
  ASN1R_TAG_ADD (aCtx, CTX,1,get_num_arr_el);
  }

/************************************************************************/
/*			get_struct_el					*/
/* structure element, setup to get struct parameters.			*/
/************************************************************************/

static ST_VOID get_struct_el (ASN1_DEC_CTXT *aCtx)
  {
ASN1_TO_RT_DEC_INFO *decInfo = (ASN1_TO_RT_DEC_INFO *) aCtx->usr_info[0];

  MLOG_CDEC0 ("get_struct_el");

  if (++rt_count >= rt_limit)
    {
    MLOG_NERR0 ("ASN.1 to RT Error : RT Table too small");
    asn1r_set_dec_err (aCtx, RUNTIME_LIMIT);
    }
  else
    {
    if (!decInfo->calc_only)
      {
      rt_ptr->el_tag = RT_STR_START; 	/* write the tag		*/
      struct_tracking[aCtx->asn1r_msg_level] = rt_ptr;
      rt_ptr++;				/* point to next runtime element*/
      }
    }

  ASN1R_TAG_ADD (aCtx, CTX,0,get_str_packed);
  ASN1R_TAG_ADD (aCtx, CTX|CONSTR,1,str_comp_seq_start);
  }

/************************************************************************/
/* Simple type get functions						*/
/************************************************************************/
/************************************************************************/
/*			get_bool_el					*/
/* boolean element encountered. 					*/
/************************************************************************/

static ST_VOID get_bool_el (ASN1_DEC_CTXT *aCtx)
  {
ASN1_TO_RT_DEC_INFO *decInfo = (ASN1_TO_RT_DEC_INFO *) aCtx->usr_info[0];

  MLOG_CDEC0 ("get_bool_el");

  if (!decInfo->calc_only)
    {
    rt_ptr->el_tag = RT_BOOL;			/* set data type tag		*/
    rt_ptr->u.p.el_len = 1;		/* set max data length		*/
    }
  rt_ptr_inc (aCtx);			/* move rt ptr, inc count	*/
  }

/************************************************************************/
/*			get_bitstr_el					*/
/* bitstring element encountered.					*/
/************************************************************************/

static ST_VOID get_bitstr_el (ASN1_DEC_CTXT *aCtx)
  {
ASN1_TO_RT_DEC_INFO *decInfo = (ASN1_TO_RT_DEC_INFO *) aCtx->usr_info[0];
ST_INT32 size;

  MLOG_CDEC0 ("get_bitstr_el");

  if (asn1r_get_i32 (aCtx, &size))
    asn1r_set_dec_err (aCtx, PDU_ERR);			/* too long		*/
  else
    {
    if (!decInfo->calc_only)
      {
      rt_ptr->el_tag = RT_BIT_STRING; 	  	/* set data type tag	*/
      rt_ptr->u.p.el_len = (ST_INT) size; 	/* save size		*/
      if (rt_ptr->u.p.el_len != (ST_INT) size)	/* make sure OK cast	*/
        { 
        MLOG_NERR0 ("Bitstring size too big");
        asn1r_set_dec_err (aCtx, UNSUPPORTED);	 /* too long for MMS-EASE	*/
        } 
      } 
    rt_ptr_inc (aCtx);			/* move rt ptr, inc count	*/
    }
  }
/************************************************************************/
/*			get_int_el					*/
/* integer element encountered. 					*/
/************************************************************************/

static ST_VOID get_int_el (ASN1_DEC_CTXT *aCtx)
  {
ASN1_TO_RT_DEC_INFO *decInfo = (ASN1_TO_RT_DEC_INFO *) aCtx->usr_info[0];
ST_UCHAR size;

  MLOG_CDEC0 ("get_int_el");

  if (asn1r_get_u8 (aCtx, &size) || size %8)	/* read integer 		*/
    asn1r_set_dec_err (aCtx, LENGTH_ERR);		/* if too long or not byte size */
  else
    {
    if (!decInfo->calc_only)
      {
      rt_ptr->el_tag = RT_INTEGER;		/* set data type tag		*/
      rt_ptr->u.p.el_len = 1;   		/* set max data length		*/

      rt_ptr->u.p.el_len = (ST_INT) size/8;	/* looks OK so far		*/

      if ((rt_ptr->u.p.el_len != sizeof (ST_CHAR) &&
	  rt_ptr->u.p.el_len != sizeof (ST_INT16) &&
	  rt_ptr->u.p.el_len != sizeof (ST_INT) && /*** decunix use 1,2,4,8 bytes ***/
#ifdef INT64_SUPPORT
      	  rt_ptr->u.p.el_len != sizeof (ST_INT64) &&
#endif
      	  rt_ptr->u.p.el_len != sizeof (ST_INT32)) ||
          size % 8)
        {
        MLOG_NERR0 ("Integer length not supported");
        asn1r_set_dec_err (aCtx, UNSUPPORTED);		/* not standard size int	*/
        }
      }
    rt_ptr_inc (aCtx);			/* move rt ptr, inc count	*/
    }
  }

/************************************************************************/
/*			get_uint_el					*/
/* unsigned element encountered.					*/
/************************************************************************/

static ST_VOID get_uint_el (ASN1_DEC_CTXT *aCtx)
  {
ASN1_TO_RT_DEC_INFO *decInfo = (ASN1_TO_RT_DEC_INFO *) aCtx->usr_info[0];
ST_UCHAR size;

  MLOG_CDEC0 ("get_uint_el");

  if (asn1r_get_u8 (aCtx, &size) || size %8)	/* read integer 		*/
    asn1r_set_dec_err (aCtx, LENGTH_ERR);		/* if too long or not byte size */
  else
    {
    if (!decInfo->calc_only)
      {
      rt_ptr->el_tag = RT_UNSIGNED; 		/* set data type tag		*/
      rt_ptr->u.p.el_len = (ST_INT) size/8;	/* looks OK so far		*/
      if ((rt_ptr->u.p.el_len != sizeof (ST_CHAR) &&
	  rt_ptr->u.p.el_len != sizeof (ST_INT16) &&
	  rt_ptr->u.p.el_len != sizeof (ST_INT) && /*** decunix use 1,2,4,8 bytes ***/
#ifdef INT64_SUPPORT
      	  rt_ptr->u.p.el_len != sizeof (ST_INT64) &&
#endif
      	  rt_ptr->u.p.el_len != sizeof (ST_INT32)) ||
          size % 8)
        {
        MLOG_NERR0 ("Integer length not supported");
        asn1r_set_dec_err (aCtx, UNSUPPORTED);	/* not standard size int	*/
        }
      }
    rt_ptr_inc (aCtx);			/* move rt ptr, inc count	*/
    }
  }

/************************************************************************/
/************************************************************************/
/*			get_float_el					*/
/* float element encountered.						*/
/************************************************************************/

static ST_UCHAR float_f;
static ST_UCHAR float_e;

static ST_VOID get_float_el (ASN1_DEC_CTXT *aCtx)
  {
  MLOG_CDEC0 ("get_float_el");

  ASN1R_TAG_ADD (aCtx, UNI,INT_CODE,get_float_fract);
  }

/************************************************************************/
/*			get_float_fract					*/
/* float element fractional positions (unsigned 8) encountered		*/
/************************************************************************/

static ST_VOID get_float_fract (ASN1_DEC_CTXT *aCtx)
  {
  MLOG_CDEC0 ("get_float_fract");

  if (asn1r_get_u8 (aCtx, &float_f))
    asn1r_set_dec_err (aCtx, UNSUPPORTED);
  else
    ASN1R_TAG_ADD (aCtx, UNI,INT_CODE,get_float_exp);
  }

/************************************************************************/
/*			get_float_exp					*/
/* float element fractional positions (unsigned 8) encountered		*/
/************************************************************************/


static ST_VOID get_float_exp (ASN1_DEC_CTXT *aCtx)
  {
ASN1_TO_RT_DEC_INFO *decInfo = (ASN1_TO_RT_DEC_INFO *) aCtx->usr_info[0];

  MLOG_CDEC0 ("get_float_exp");

  if (asn1r_get_u8 (aCtx, &float_e))
    asn1r_set_dec_err (aCtx, UNSUPPORTED);
  else
    {					/* IEEE 754 Single		*/
    if (!decInfo->calc_only)
      {
      rt_ptr->el_tag = RT_FLOATING_POINT; /* set data type tag		*/

/* Since DIS & IS differ on the values used to represent floating point	*/
/* types, allow either.							*/

      if ((float_f == SNGL_FRACT || float_f == SNGL_WHOLE) && 
            float_e == SNGL_EXP)
        rt_ptr->u.p.el_len = 4;		/* single precision		*/
      else
        {					 /* IEEE 754 Double		*/
        if ((float_f == DOUBL_FRACT || float_f == DOUBL_WHOLE) && 
            float_e == DOUBL_EXP)
   	  rt_ptr->u.p.el_len = 8; 	  /* double precision		*/
        else
          {
          MLOG_NERR0 ("Float format not supported");
          asn1r_set_dec_err (aCtx, UNSUPPORTED);		/* not standard size int	*/
          }
        }
      }
    }
/* Float element constructor can now be done				*/

  aCtx->asn1r_c_done_fun[aCtx->asn1r_msg_level] = float_cstr_done;
  }

/************************************************************************/
/*			float_cstr_done					*/
/* float element cstr done function, allow type decode to continue 	*/
/************************************************************************/

static ST_VOID float_cstr_done (ASN1_DEC_CTXT *aCtx)
  {
  MLOG_CDEC0 ("float_cstr_done");
  rt_ptr_inc (aCtx);			/* move rt ptr, inc count	*/
  }


/************************************************************************/
/*			get_octstr_el					*/
/* octet string element encountered.					*/
/************************************************************************/

static ST_VOID get_octstr_el (ASN1_DEC_CTXT *aCtx)
  {
ASN1_TO_RT_DEC_INFO *decInfo = (ASN1_TO_RT_DEC_INFO *) aCtx->usr_info[0];
ST_INT32 size;

  MLOG_CDEC0 ("get_octstr_el");

  if (asn1r_get_i32 (aCtx, &size))
    asn1r_set_dec_err (aCtx, UNSUPPORTED);		/* too long for MMS-EASE	*/
  else
    {
    if (!decInfo->calc_only)
      {
      rt_ptr->el_tag = RT_OCTET_STRING;		/* set data type tag		*/
      rt_ptr->u.p.el_len = (ST_INT) size;   	/* save precision	*/
      if (rt_ptr->u.p.el_len != (ST_INT) size)	/* make sure OK cast	*/
        {
        MLOG_NERR0 ("Octet string too big");
        asn1r_set_dec_err (aCtx, UNSUPPORTED);		/* too long for MMS-EASE	*/
        }
      }
    rt_ptr_inc (aCtx);			/* move rt ptr, inc count	*/
    }
  }

/************************************************************************/
/*			get_vstr_el					*/
/* visible string element encountered.					*/
/************************************************************************/

static ST_VOID get_vstr_el (ASN1_DEC_CTXT *aCtx)
  {
ASN1_TO_RT_DEC_INFO *decInfo = (ASN1_TO_RT_DEC_INFO *) aCtx->usr_info[0];
ST_INT32 size;

  MLOG_CDEC0 ("get_vstr_el");

  if (asn1r_get_i32 (aCtx, &size))
    asn1r_set_dec_err (aCtx, UNSUPPORTED);		/* too long for MMS-EASE	*/
  else
    {
    if (!decInfo->calc_only)
      {
      rt_ptr->el_tag = RT_VISIBLE_STRING;       /* set data type tag   	*/
      rt_ptr->u.p.el_len = (ST_INT) size;   	/* save precision	*/
      if (rt_ptr->u.p.el_len != (ST_INT) size)	/* make sure OK cast	*/
        {
        MLOG_NERR0 ("Vis string too big");
        asn1r_set_dec_err (aCtx, UNSUPPORTED); 	/* too long for MMS-EASE	*/
        }
      }
    rt_ptr_inc (aCtx);			/* move rt ptr, inc count	*/
    }
  }

/************************************************************************/
/*			get_gtime_el					*/
/* generalized time element encountered.				*/
/************************************************************************/

static ST_VOID get_gtime_el (ASN1_DEC_CTXT *aCtx)
  {
ASN1_TO_RT_DEC_INFO *decInfo = (ASN1_TO_RT_DEC_INFO *) aCtx->usr_info[0];

  MLOG_CDEC0 ("get_gtime_el");

  if (!decInfo->calc_only)
    {
    rt_ptr->el_tag = RT_GENERAL_TIME;	/* set data type tag		*/
    rt_ptr->u.p.el_len = sizeof(time_t);/* gen time precision		*/
    }
  rt_ptr_inc (aCtx);			/* move rt ptr, inc count	*/
  }

/************************************************************************/
/*			get_btime_el					*/
/* binary time element encountered.					*/
/************************************************************************/

static ST_VOID get_btime_el (ASN1_DEC_CTXT *aCtx)
  {
ASN1_TO_RT_DEC_INFO *decInfo = (ASN1_TO_RT_DEC_INFO *) aCtx->usr_info[0];
ST_BOOLEAN dateFlag;

  MLOG_CDEC0 ("get_btime_el");

  if (asn1r_get_bool (aCtx, &dateFlag))		/* get the 4/6 flag		*/
    asn1r_set_dec_err (aCtx, PDU_ERR);
  else
    {
    if (!decInfo->calc_only)
      {
      rt_ptr->el_tag = RT_BINARY_TIME; 	/* set data type tag		*/
      if (dateFlag) 			/* if date included		*/
        rt_ptr->u.p.el_len = 6;		/* expect 6 bytes		*/
      else				/* no date			*/
        rt_ptr->u.p.el_len = 4;		/* expect 4 bytes		*/
      }
    rt_ptr_inc (aCtx);			/* move rt ptr, inc count	*/
    }
  }

/************************************************************************/
/*			get_bcd_el					*/
/* bcd element encountered.						*/
/************************************************************************/

static ST_VOID get_bcd_el (ASN1_DEC_CTXT *aCtx)
  {
ASN1_TO_RT_DEC_INFO *decInfo = (ASN1_TO_RT_DEC_INFO *) aCtx->usr_info[0];
ST_UCHAR size;

  MLOG_CDEC0 ("get_bcd_el");

  if (asn1r_get_u8 (aCtx, &size))			/* get the precision		*/
    asn1r_set_dec_err (aCtx, PDU_ERR);		/* too long			*/
  else					/* find # bytes 		*/
    {
    if (!decInfo->calc_only)
      {
      rt_ptr->el_tag = RT_BCD;		/* set data type tag		*/
      rt_ptr->u.p.el_len = (ST_INT) size;/* save precision		*/
      if (size > 8)
        {
        MLOG_NERR0 ("BCD size too big");
        asn1r_set_dec_err (aCtx, UNSUPPORTED);      	/* too long for MMS-EASE	*/
        } 
      } 
    }
  rt_ptr_inc (aCtx);			/* move rt ptr, inc count	*/
  }

/************************************************************************/
/*			get_utc_time_el					*/
/* UTC Time element encountered.					*/
/************************************************************************/

static ST_VOID get_utc_time_el (ASN1_DEC_CTXT *aCtx)
  {
ASN1_TO_RT_DEC_INFO *decInfo = (ASN1_TO_RT_DEC_INFO *) aCtx->usr_info[0];

  MLOG_CDEC0 ("get_utc_time_el");

    if (!decInfo->calc_only)
      {
      rt_ptr->el_tag = RT_UTC_TIME; 	/* set data type tag		*/
      rt_ptr->u.p.el_len = 8;		/* expect 8 bytes		*/
      }
    rt_ptr_inc (aCtx);			/* move rt ptr, inc count	*/
  }


/************************************************************************/
/*			get_utf8_el					*/
/* Unicode UTF8string element encountered.				*/
/************************************************************************/

static ST_VOID get_utf8_el (ASN1_DEC_CTXT *aCtx)
  {
ASN1_TO_RT_DEC_INFO *decInfo = (ASN1_TO_RT_DEC_INFO *) aCtx->usr_info[0];
ST_INT32 size;

  MLOG_CDEC0 ("get_utf8_el");

  if (asn1r_get_i32 (aCtx, &size))
    asn1r_set_dec_err (aCtx, UNSUPPORTED);	/* too long for MMS-EASE*/
  else
    {
    if (!decInfo->calc_only)
      {
      rt_ptr->el_tag = RT_UTF8_STRING;		/* set data type tag   	*/
      rt_ptr->u.p.el_len = (ST_RTINT) size;	/* save size		*/
      if (rt_ptr->u.p.el_len != size)
        {					/* lost precision in cast*/
        MLOG_NERR0 ("Unicode UTF8 string too long");
        asn1r_set_dec_err (aCtx, UNSUPPORTED);	/* too long for MMS-EASE*/
        }
      }
    rt_ptr_inc (aCtx);			/* move rt ptr, inc count	*/
    }
  }

/************************************************************************/
/************************************************************************/
/* STRUCTURE COMPONENT DECODE						*/
/************************************************************************/
/* function declarations used in structure parsing			*/

static ST_VOID get_str_comp_seq (ASN1_DEC_CTXT *aCtx);	/* UNI seq - comp		*/
static ST_VOID get_str_comp_name (ASN1_DEC_CTXT *aCtx);	/* CTX 0 comp ID		*/
static ST_VOID get_str_comp_type (ASN1_DEC_CTXT *aCtx);	/* CTX 1 CSTR - typedef		*/
static ST_VOID str_comp_type_done (ASN1_DEC_CTXT *aCtx);
static ST_VOID str_comp_done (ASN1_DEC_CTXT *aCtx);
static ST_VOID str_comp_seq_done (ASN1_DEC_CTXT *aCtx);
static ST_VOID str_done (ASN1_DEC_CTXT *aCtx);		/* main struct cstr done	*/

/************************************************************************/
/*			get_str_packed					*/
/************************************************************************/

static ST_VOID get_str_packed (ASN1_DEC_CTXT *aCtx)
  {
#if !defined(USE_RT_TYPE_2) && !defined(USE_RT_TYPE_3)
ASN1_TO_RT_DEC_INFO *decInfo = (ASN1_TO_RT_DEC_INFO *) aCtx->usr_info[0];
#endif
ST_UCHAR m_packed;

  MLOG_CDEC0 ("get_str_packed");

  if (asn1r_get_u8 (aCtx, &m_packed)) 	/* get the boolean value		*/
    asn1r_set_dec_err (aCtx, PDU_ERR);	/* too long				*/
  else
    {
#if !defined(USE_RT_TYPE_2) && !defined(USE_RT_TYPE_3)
    if (!decInfo->calc_only)
      rt_ptr->u.str.packd = m_packed;
#endif
    ASN1R_TAG_ADD (aCtx, CTX|CONSTR,1,str_comp_seq_start);
    }
  }

/************************************************************************/
/*			str_comp_seq_start					*/
/* This is a 'seq' component , of the main struct 'seq of'		*/
/* Next is an optional 'packed' or the components sequence		*/
/************************************************************************/

static ST_VOID str_comp_seq_start (ASN1_DEC_CTXT *aCtx)
  {
  MLOG_CDEC0 ("str_comp_seq_start");

  ASN1R_TAG_ADD (aCtx, UNI|CONSTR,SEQ_CODE,get_str_comp_seq);
  }

/************************************************************************/
/*			get_str_comp_seq				*/
/* This is a 'seq' component , of the main struct 'seq of'		*/
/* Next is an optional 'packed' or the components sequence		*/
/************************************************************************/

static ST_VOID get_str_comp_seq (ASN1_DEC_CTXT *aCtx)
  {
  MLOG_CDEC0 ("get_str_comp_seq");

  ASN1R_TAG_ADD (aCtx, CTX,0,get_str_comp_name);
  ASN1R_TAG_ADD (aCtx, CTX|CONSTR,1,get_str_comp_type);
  }


/************************************************************************/
/*			get_str_comp_name				*/
/* structure component name here					*/
/* NOTE: USE_RT_TYPE_2 must be defined or this function will not save	*/
/*       component names (needs rt_ptr->comp_name_ptr).			*/
/************************************************************************/

#if defined(USE_RT_TYPE_3)
static ST_INT _mvl_rt_name_to_index (ST_CHAR *name)
  {
ST_INT i;

  for (i = 0; i < numMvlRtNames; ++i)
    {
    if (!strcmp (mvlRtNames[i], name))
      return (i);
    }
  return (-1);
  }
#endif


static ST_VOID get_str_comp_name (ASN1_DEC_CTXT *aCtx)
  {
ST_CHAR name[MAX_IDENT_LEN+1]; /* for component name element		*/
ASN1_TO_RT_DEC_INFO *decInfo = (ASN1_TO_RT_DEC_INFO *) aCtx->usr_info[0];
#if defined(USE_RT_TYPE_2)
ST_INT name_len;
#endif

  MLOG_CDEC0 ("get_str_comp_name");

  if (asn1r_get_identifier (aCtx, name))		/* read the identifier		*/
    asn1r_set_dec_err (aCtx, PDU_ERR);		/* too long			*/
  else
    {
#ifdef USE_RT_TYPE_2
    name_len = strlen (name) + 1;	/* make space for NULL		*/
    decInfo->comp_name_used += name_len;/* compute total len BEFORE copy*/
    if (!decInfo->calc_only)
      {
      if (decInfo->comp_name_ptr && 
          decInfo->comp_name_used <= decInfo->comp_name_size)	/* enough space?*/
        {
        strcpy (decInfo->comp_name_ptr, name);
        rt_ptr->comp_name_ptr = decInfo->comp_name_ptr;	/* Store name ptr*/
        decInfo->comp_name_ptr += name_len;
        }
      else
        {	/* Buffer too small. Should never happen.	*/
        MLOG_ERR0 ("ASN.1 to RT Error : Component name buffer overflow.");
        asn1r_set_dec_err (aCtx, RUNTIME_LIMIT);
        return;
        }
      }
#elif defined(USE_RT_TYPE_3)
    if (!decInfo->calc_only)
      {
      rt_ptr->name_index =  _mvl_rt_name_to_index (name);
      if (rt_ptr->name_index < 0)
        {
        MLOG_NERR1 ("ASN.1 to RT Error : Component name '%s'cannot be converted to index", name);
        asn1r_set_dec_err (aCtx, UNKNOWN_TYPENAME);
        return;
        }
      }
#else	/* Default	*/
    if (!decInfo->calc_only)
      strcpy (rt_ptr->name, name);
#endif	/* Default	*/

    ASN1R_TAG_ADD (aCtx, CTX|CONSTR,1,get_str_comp_type); /* need to get type next	*/
    }
  }


/************************************************************************/
/*			get_str_comp_type				*/
/* structure component, is a type definition				*/
/* This constructor contains the type_definition element		*/
/************************************************************************/

static ST_VOID get_str_comp_type (ASN1_DEC_CTXT *aCtx)
  {
  MLOG_CDEC0 ("get_str_comp_type");

  set_type_tags (aCtx);		/* set up to get the type spec. 	*/
  aCtx->asn1r_c_done_fun[aCtx->asn1r_msg_level] = str_comp_type_done;
  }

/************************************************************************/
/*			str_comp_type_done 				*/
/* structure component typedef constructor complete			*/
/* This just means that is is OK for this component to be done.		*/
/************************************************************************/

static ST_VOID str_comp_type_done (ASN1_DEC_CTXT *aCtx)
  {
  MLOG_CDEC0 ("str_comp_type_done");

  aCtx->asn1r_c_done_fun[aCtx->asn1r_msg_level] = str_comp_done;
  }

/************************************************************************/
/*			str_comp_done					*/
/* structure component constructor complete				*/
/* set up to get the next						*/
/************************************************************************/

static ST_VOID str_comp_done (ASN1_DEC_CTXT *aCtx)
  {
  MLOG_CDEC0 ("str_comp_done");

				/* setup to get next struct component	*/
  ASN1R_TAG_ADD (aCtx, UNI|CONSTR,SEQ_CODE,get_str_comp_seq);

/* since a struct comp has been read, OK for this seq to be done	*/
  aCtx->asn1r_c_done_fun[aCtx->asn1r_msg_level] = str_comp_seq_done;
  }

/************************************************************************/
/*			str_comp_seq_done				*/
/* structure component seq complete					*/
/* set up to get the next seq, or could be done with this seq		*/
/************************************************************************/

static ST_VOID str_comp_seq_done (ASN1_DEC_CTXT *aCtx)
  {
  MLOG_CDEC0 ("str_comp_seq_done");

/* since a struct comp has been read, OK for this seq to be done	*/
  aCtx->asn1r_c_done_fun[aCtx->asn1r_msg_level] = str_done;   /* ok for this struct to be done	*/
  }

/************************************************************************/
/*			str_done					*/
/* structure complete							*/
/* set up to get the next element					*/
/************************************************************************/

static ST_VOID str_done (ASN1_DEC_CTXT *aCtx)
  {
ASN1_TO_RT_DEC_INFO *decInfo = (ASN1_TO_RT_DEC_INFO *) aCtx->usr_info[0];
RUNTIME_TYPE *start_rt_blk;
ST_INT num_rt;

  MLOG_CDEC0 ("str_done");

  if (!decInfo->calc_only)
    {
    rt_ptr->el_tag = RT_STR_END;	/* write the tag		*/

/* calculate where the corresponding STR_START elment is		*/
    start_rt_blk = struct_tracking[aCtx->asn1r_msg_level+1];

/* Set num_rt, for use in AA processing                                 */
    num_rt = (rt_ptr - start_rt_blk - 1);
   
/* If everything else working correctly, this error should never occur.	*/
    if (num_rt <= 0)
      {
      MLOG_ERR0 ("ASN.1 to RT Error : Structure empty.");
      asn1r_set_dec_err (aCtx, LENGTH_ERR);
      }

    start_rt_blk->u.str.num_rt_blks = num_rt;
    rt_ptr->u.str.num_rt_blks = num_rt;

#if !defined(USE_RT_TYPE_2) && !defined(USE_RT_TYPE_3)
/* copy the packed flag from the start block to the end block 		*/
    rt_ptr->u.str.packd = start_rt_blk->u.str.packd;
#endif
    }
  rt_ptr_inc (aCtx);			/* move rt ptr, inc count	*/
  }


/************************************************************************/
/************************************************************************/
/* ARRAY COMPONENT DECODE						*/
/************************************************************************/
/*			get_arr_packed					*/
/* packed flag received here, just ignore for now			*/
/************************************************************************/

static ST_VOID get_arr_packed (ASN1_DEC_CTXT *aCtx)
  {
#if !defined(USE_RT_TYPE_2) && !defined(USE_RT_TYPE_3)
ASN1_TO_RT_DEC_INFO *decInfo = (ASN1_TO_RT_DEC_INFO *) aCtx->usr_info[0];
#endif
ST_UCHAR m_packed;

  MLOG_CDEC0 ("get_arr_packed");

  if (asn1r_get_u8 (aCtx, &m_packed))	      /* get the boolean value		*/
    asn1r_set_dec_err (aCtx, PDU_ERR);	      /* too long			*/
  else
    {
#if !defined(USE_RT_TYPE_2) && !defined(USE_RT_TYPE_3)
    if (!decInfo->calc_only)
      rt_ptr->u.arr.packd = m_packed;
#endif   
    ASN1R_TAG_ADD (aCtx, CTX,1,get_num_arr_el);	/* next must be number of elmnts*/
    }
  }

/************************************************************************/
/*			get_num_arr_el					*/
/* number of array elements						*/
/************************************************************************/

static ST_VOID get_num_arr_el (ASN1_DEC_CTXT *aCtx)
  {
ASN1_TO_RT_DEC_INFO *decInfo = (ASN1_TO_RT_DEC_INFO *) aCtx->usr_info[0];
ST_UINT32 num;

  MLOG_CDEC0 ("get_num_arr_el");

  if (asn1r_get_u32 (aCtx, &num))		/* get the number of elements		*/
    asn1r_set_dec_err (aCtx, PDU_ERR);	/* too long				*/
  else
    {
    if (!decInfo->calc_only)
      {
      rt_ptr->u.arr.num_elmnts = (ST_INT) num;
      if (rt_ptr->u.arr.num_elmnts != (ST_INT) num) /* verify cast 	*/
        {
        MLOG_NERR0 ("Array size too big");
        asn1r_set_dec_err (aCtx, UNSUPPORTED);		/* too long				*/
        }      
      }      
    ASN1R_TAG_ADD (aCtx, CTX|CONSTR,2,get_arr_el); /* need to get type next	*/

    if (++rt_count >= rt_limit) 	/* check runtime table limit	*/
      {
      MLOG_NERR0 ("ASN.1 to RT Error : RT Table too small");
      asn1r_set_dec_err (aCtx, RUNTIME_LIMIT);
      }
    else
      rt_ptr++; 		/* move rt ptr to next element		*/
    }
  }

/************************************************************************/
/*			get_arr_el					*/
/* structure component, is a type definition				*/
/* This constructor contains the type_definition element		*/
/************************************************************************/

static ST_VOID get_arr_el (ASN1_DEC_CTXT *aCtx)
  {
  MLOG_CDEC0 ("get_arr_el");

  set_type_tags (aCtx);		/* set up to get the type spec. 	*/
  aCtx->asn1r_c_done_fun[aCtx->asn1r_msg_level] = arr_el_cstr_done;
  }

/************************************************************************/
/*			arr_el_cstr_done				*/
/* array element constructor complete					*/
/* This means that the array sequence is complete			*/
/************************************************************************/

static ST_VOID arr_el_cstr_done (ASN1_DEC_CTXT *aCtx)
  {
  MLOG_CDEC0 ("arr_el_cstr_done");

  aCtx->asn1r_c_done_fun[aCtx->asn1r_msg_level] = array_done;	/* set done function for array	*/
  }

/************************************************************************/
/*			array_done					*/
/* array constructor complete						*/
/* calculate the # rt blocks is each element, update start/end of array */
/************************************************************************/

static ST_VOID array_done (ASN1_DEC_CTXT *aCtx)
  {
ST_INT num_rt;
RUNTIME_TYPE *start_rt_blk;
ASN1_TO_RT_DEC_INFO *decInfo = (ASN1_TO_RT_DEC_INFO *) aCtx->usr_info[0];

  MLOG_CDEC0 ("array_done");

  if (!decInfo->calc_only)
    {
    rt_ptr->el_tag = RT_ARR_END;  	/* write the tag		*/

/* calculate and write rt count for end, start blocks			*/
    start_rt_blk = decInfo->rt_start + arr_index[aCtx->asn1r_msg_level+1];
    num_rt = rt_count - arr_index[aCtx->asn1r_msg_level+1] -1; /* rt_count not inc'd yet*/

    rt_ptr->u.arr.num_rt_blks = num_rt;
    start_rt_blk->u.arr.num_rt_blks = num_rt;

/* copy the element count from the start block to the end block 	*/
    rt_ptr->u.arr.num_elmnts = start_rt_blk->u.arr.num_elmnts;
    }
  rt_ptr_inc (aCtx);			/* move rt ptr, inc count	*/
  }


/************************************************************************/
/************************************************************************/
/* NAMED TYPE								*/
/************************************************************************/
/*			type_name_cstr_done				*/
/* type_name constructor complete					*/
/* need to find in local database, copy the runtime elements		*/
/************************************************************************/

static ST_VOID type_name_cstr_done (ASN1_DEC_CTXT *aCtx)
  {
ASN1_TO_RT_DEC_INFO *decInfo = (ASN1_TO_RT_DEC_INFO *) aCtx->usr_info[0];
RUNTIME_TYPE *sub_rt_head;	/* ptr to first rt in sub-type	*/
ST_INT sub_rt_num;		/* num of rt in sub-type	*/
#if !defined(MMS_LITE)
NAMED_TYPE *tptr;
#endif

#if defined (USE_RT_TYPE_2)
ST_CHAR *name_ptr_save;
ST_INT j;
ST_INT comp_name_len;
#elif defined(USE_RT_TYPE_3)
ST_RTINT name_index_save;
#else
ST_CHAR name_save[MAX_IDENT_LEN+1];
#endif

  MLOG_CDEC0 ("type_name_cstr_done");

#if !defined(MMS_LITE)
  tptr = ms_find_named_type_obj (&type_name,0);
  if (!tptr)
    {
    MLOG_NERR1 ("Named type %s not found", type_name.obj_name.vmd_spec);
    asn1r_set_dec_err (aCtx, UNKNOWN_TYPENAME);
    return;
    }
  sub_rt_num = tptr->rt_num;
  sub_rt_head = tptr->rt_head;
#else
  /* This function finds previously defined types. The default function	*/
  /* should be in the library, but user may replace it with customized funct.*/
  if (u_ml_get_rt_type (aCtx, &type_name, &sub_rt_head, &sub_rt_num) != SD_SUCCESS)
    {
    MLOG_NERR1 ("Named type %s not found", type_name.obj_name.vmd_spec);
    asn1r_set_dec_err (aCtx, UNKNOWN_TYPENAME);
    return;
    }

#endif

/* OK, we have a pointer to the referenced runtime type and the number	*/
/* of elements. We can either insert the referenced table into the 	*/
/* new one, OR we can just add a reference.				*/

  if ((rt_count += sub_rt_num) > rt_limit)
    {
    MLOG_NERR0 ("ASN.1 to RT Error : RT Table too small");
    asn1r_set_dec_err (aCtx, RUNTIME_LIMIT);
    return;
    }
  if (decInfo->calc_only)
    {
#if defined(USE_RT_TYPE_2)	/* make room to copy comp names from sub-type*/
    for (j = 0; j < sub_rt_num; ++j)
      {
      if (sub_rt_head[j].comp_name_ptr)
        decInfo->comp_name_used += (strlen (sub_rt_head[j].comp_name_ptr) + 1);
      }
#endif
    }
  else
    {
#if defined(USE_RT_TYPE_2)
    name_ptr_save = rt_ptr->comp_name_ptr;
#elif defined(USE_RT_TYPE_3)
    name_index_save = rt_ptr->name_index;
#else
    strcpy (name_save, rt_ptr->name);
#endif

    /* Copy array of RUNTIME_TYPE structs.	*/
    memcpy ((ST_CHAR *)rt_ptr,(ST_CHAR *)sub_rt_head, sizeof(RUNTIME_TYPE)*sub_rt_num);

#if defined(USE_RT_TYPE_2)
    /* Copy component names from sub-type into new type.	*/
    /* Must update comp_name_ptr in each rt.			*/
    for (j = 0; j < sub_rt_num; ++j)
      {
      if (sub_rt_head[j].comp_name_ptr)
        {
        /* check for buffer space BEFORE strcpy.	*/
        comp_name_len = (strlen(sub_rt_head[j].comp_name_ptr) + 1);
        decInfo->comp_name_used += comp_name_len;
        if (decInfo->comp_name_used <= decInfo->comp_name_size)	/* enough space?*/
          {
          strcpy (decInfo->comp_name_ptr, sub_rt_head[j].comp_name_ptr);
          rt_ptr[j].comp_name_ptr = decInfo->comp_name_ptr;
          decInfo->comp_name_ptr += comp_name_len;
          }
        else
          {	/* Buffer too small. Should never happen.	*/
          MLOG_ERR0 ("ASN.1 to RT Error : Component name buffer overflow.");
          asn1r_set_dec_err (aCtx, RUNTIME_LIMIT);
          return;
          }
        }
      }
    rt_ptr->comp_name_ptr = name_ptr_save;
#elif defined(USE_RT_TYPE_3)
    rt_ptr->name_index = name_index_save;
#else
    strcpy (rt_ptr->name, name_save);
#endif
    rt_ptr += sub_rt_num;	/* chg global rt_ptr to point after inserted sub-type*/
    }
  }


/************************************************************************/
/************************************************************************/
/* DECODE DONE FUNCTIONS						*/
/************************************************************************/
/*			td_done 					*/
/************************************************************************/


static ST_VOID td_done (ASN1_DEC_CTXT *aCtx)
  {
  MLOG_CDEC0 ("td_done");
  }

/************************************************************************/
/*			td_err						*/
/************************************************************************/

static ST_VOID td_err (ASN1_DEC_CTXT *aCtx, ST_RET err)
  {
  MLOG_CDEC0 ("td_err");

  rt_count = 0; 			/* error, set count = 0 	*/
  mms_op_err = aCtx->asn1r_pdu_dec_err;		/* save error code		*/
  }

/************************************************************************/
/* DATA ALIGNMENT TABLE DEFINITIONS					*/
/************************************************************************/

/* This is the 'packed' data alignment table, which is used to emulate	*/
/* the alignment used by previous versions of the data conversion 	*/
/* functions.								*/

SD_CONST ST_INT m_packed_data_algn_tbl[NUM_ALGN_TYPES] =
  {
  0x0000,	/* ARRSTRT_ALGN	00  */
  0x0000,	/* ARREND_ALGN	01  */
  0x0000,	/* STRSTRT_ALGN	02  */
  0x0000,	/* STREND_ALGN	03  */
  0x0000,	/* INT8_ALGN	04  */
  0x0000,	/* INT16_ALGN	05  */
  0x0000,	/* INT32_ALGN	06  */
  0x0000,	/* INT64_ALGN	07  */
  0x0000,	/* FLOAT_ALGN	08  */
  0x0000,	/* DOUBLE_ALGN	09  */
  0x0000,	/* OCT_ALGN	10  */
  0x0000,	/* BOOL_ALGN	11  */
  0x0000,	/* BCD1_ALGN	12  */
  0x0000,	/* BCD2_ALGN	13  */
  0x0000,	/* BCD4_ALGN	14  */
  0x0000,	/* BIT_ALGN	15  */
  0x0000	/* VIS_ALGN	16  */
  };

/* CRITICAL: this global variable "m_data_algn_tbl" must be set before	*/
/*           any types are created.					*/
#ifdef MMS_LITE
#if defined(FOUNDRY)
/* For some strange reason on "linux", the linker tries to get this	*/
/* module again from the library (causing duplicates) if		*/
/* m_data_algn_tbl is not initialized here.				*/
SD_CONST ST_INT *m_data_algn_tbl=NULL;	/* Foundry sets it as needed	*/
#else
/* Use m_lite_data_algn_tbl generated by Foundry for target platform.	*/
/* This assures that Foundry types & dynamic types have same alignment.	*/
SD_CONST ST_INT *m_data_algn_tbl = m_lite_data_algn_tbl;
#endif
#else	/* !MMS_LITE	*/
/* Should use "m_def_data_algn_tbl" for all systems, but must		*/
/* default to "m_packed_data_algn_tbl" for backward compatiblity with	*/
/* very old MMS-EASE products.						*/
#if !defined(NO_GLB_VAR_INIT)
SD_CONST ST_INT *m_data_algn_tbl = m_packed_data_algn_tbl;
#endif
#endif	/* !MMS_LITE	*/

/* Here is where we create the 'default' alignment tables. These should	*/
/* allow natural access to components.					*/

/* This data alignment table provides word alignment per the following 	*/
/* simple rule :							*/
/*   	Align on the smaller of the size of the data type OR 2 bytes.	*/
/* To do this we set the low bit for every type that is greater than	*/
/* or equal to 2 bytes in size.						*/

#if !defined(MMS_LITE)	/* m_def_data_algn_tbl NOT used for MMS_LITE	*/
#if defined(MSDOS)

#define M_STRSTART_MODE 	M_STRSTART_MODE_LARGEST
#define M_STREND_MODE 		M_STREND_MODE_LARGEST

SD_CONST ST_INT m_def_data_algn_tbl[NUM_ALGN_TYPES] =
  {
  0x0000,	/* ARRSTRT_ALGN	00  */
  0x0000,	/* ARREND_ALGN	01  */
  0x0000,	/* STRSTRT_ALGN	02  */
  0x0000,	/* STREND_ALGN	03  */
  0x0000,	/* INT8_ALGN	04  */
  0x0001,	/* INT16_ALGN	05  */
  0x0001,	/* INT32_ALGN	06  */
  0x0001,	/* INT64_ALGN	07  */
  0x0001,	/* FLOAT_ALGN	08  */
  0x0001,	/* DOUBLE_ALGN	09  */
  0x0000,	/* OCT_ALGN	10  */
  0x0000,	/* BOOL_ALGN	11  */
  0x0000,	/* BCD1_ALGN	12  */
  0x0001,	/* BCD2_ALGN	13  */
  0x0001,	/* BCD4_ALGN	14  */
  0x0000,	/* BIT_ALGN	15  */
  0x0000	/* VIS_ALGN	16  */
  };
#endif

/* This data alignment table provides word alignment per the following 	*/
/* simple rule :							*/
/*   	Align on the smaller of the size of the data type OR 4 bytes.	*/
/* To do this we set the low bit for every type that is  equal to 2 	*/
/* bytes in size, and the low two bits for  every type that is greater 	*/
/* than	or equal to 4 bytes in size.			     		*/

#if 0

#define M_STRSTART_MODE 	M_STRSTART_MODE_LARGEST
#define M_STREND_MODE 		M_STREND_MODE_LARGEST

SD_CONST ST_INT m_def_data_algn_tbl[NUM_ALGN_TYPES] =
  {
  0x0000,	/* ARRSTRT_ALGN	00  */
  0x0000,	/* ARREND_ALGN	01  */
  0x0000,	/* STRSTRT_ALGN	02  */
  0x0000,	/* STREND_ALGN	03  */
  0x0000,	/* INT8_ALGN	04  */
  0x0001,	/* INT16_ALGN	05  */
  0x0003,	/* INT32_ALGN	06  */
  0x0007,	/* INT64_ALGN	07  */
  0x0003,	/* FLOAT_ALGN	08  */
  0x0003,	/* DOUBLE_ALGN	09  */
  0x0000,	/* OCT_ALGN	10  */
  0x0000,	/* BOOL_ALGN	11  */
  0x0000,	/* BCD1_ALGN	12  */
  0x0000,	/* BCD2_ALGN	13  */
  0x0001,	/* BCD4_ALGN	14  */
  0x0000,	/* BIT_ALGN	15  */
  0x0000	/* VIS_ALGN	16  */
  };   
#endif

#ifdef _AIX

#define M_STRSTART_MODE 	M_STRSTART_MODE_LARGEST
#define M_STREND_MODE 		M_STREND_MODE_LARGEST

SD_CONST ST_INT m_def_data_algn_tbl[NUM_ALGN_TYPES] =
  {
  0x0000,	/* ARRSTRT_ALGN	00  */
  0x0000,	/* ARREND_ALGN	01  */
  0x0000,	/* STRSTRT_ALGN	02  */
  0x0000,	/* STREND_ALGN	03  */
  0x0000,	/* INT8_ALGN	04  */
  0x0001,	/* INT16_ALGN	05  */
  0x0003,	/* INT32_ALGN	06  */
  0x0007,	/* INT64_ALGN	07  */
  0x0003,	/* FLOAT_ALGN	08  */
  0x0003,	/* DOUBLE_ALGN	09  */
  0x0000,	/* OCT_ALGN	10  */
  0x0000,	/* BOOL_ALGN	11  */
  0x0000,	/* BCD1_ALGN	12  */
  0x0001,	/* BCD2_ALGN	13  */
  0x0003,	/* BCD4_ALGN	14  */
  0x0000,	/* BIT_ALGN	15  */
  0x0000	/* VIS_ALGN	16  */
  };   
#endif

#if defined (__hpux)

#define M_STRSTART_MODE 	M_STRSTART_MODE_LARGEST
#define M_STREND_MODE 		M_STREND_MODE_LARGEST

SD_CONST ST_INT m_def_data_algn_tbl[NUM_ALGN_TYPES] =
  {
  0x0000,	/* ARRSTRT_ALGN	00  */
  0x0000,	/* ARREND_ALGN	01  */
  0x0000,	/* STRSTRT_ALGN	02  */
  0x0000,	/* STREND_ALGN	03  */
  0x0000,	/* INT8_ALGN	04  */
  0x0001,	/* INT16_ALGN	05  */
  0x0003,	/* INT32_ALGN	06  */
  0x0007,	/* INT64_ALGN	07  */
  0x0003,	/* FLOAT_ALGN	08  */
  0x0007,	/* DOUBLE_ALGN	09  */
  0x0000,	/* OCT_ALGN	10  */
  0x0000,	/* BOOL_ALGN	11  */
  0x0000,	/* BCD1_ALGN	12  */
  0x0001,	/* BCD2_ALGN	13  */
  0x0003,	/* BCD4_ALGN	14  */
  0x0000,	/* BIT_ALGN	15  */
  0x0000	/* VIS_ALGN	16  */
  };   
#endif

#ifdef __OS2__

#define M_STRSTART_MODE 	M_STRSTART_MODE_LARGEST
#define M_STREND_MODE 		M_STREND_MODE_LARGEST

SD_CONST ST_INT m_def_data_algn_tbl[NUM_ALGN_TYPES] =
  {
  0x0000,	/* ARRSTRT_ALGN	00  */
  0x0000,	/* ARREND_ALGN	01  */
  0x0000,	/* STRSTRT_ALGN	02  */
  0x0000,	/* STREND_ALGN	03  */
  0x0000,	/* INT8_ALGN	04  */
  0x0001,	/* INT16_ALGN	05  */
  0x0003,	/* INT32_ALGN	06  */
  0x0007,	/* INT64_ALGN	07  */
  0x0003,	/* FLOAT_ALGN	08  */
  0x0003,	/* DOUBLE_ALGN	09  */
  0x0000,	/* OCT_ALGN	10  */
  0x0000,	/* BOOL_ALGN	11  */
  0x0000,	/* BCD1_ALGN	12  */
  0x0001,	/* BCD2_ALGN	13  */
  0x0003,	/* BCD4_ALGN	14  */
  0x0000,	/* BIT_ALGN	15  */
  0x0000	/* VIS_ALGN	16  */
  };   
#endif

#ifdef sun

#define M_STRSTART_MODE 	M_STRSTART_MODE_LARGEST
#define M_STREND_MODE 		M_STREND_MODE_LARGEST

SD_CONST ST_INT m_def_data_algn_tbl[NUM_ALGN_TYPES] =
  {
  0x0000,	/* ARRSTRT_ALGN	00  */
  0x0000,	/* ARREND_ALGN	01  */
  0x0000,	/* STRSTRT_ALGN	02  */
  0x0000,	/* STREND_ALGN	03  */
  0x0000,	/* INT8_ALGN	04  */
  0x0001,	/* INT16_ALGN	05  */
  0x0003,	/* INT32_ALGN	06  */
  0x0007,	/* INT64_ALGN	07  */
  0x0003,	/* FLOAT_ALGN	08  */
  0x0007,	/* DOUBLE_ALGN	09  */
  0x0000,	/* OCT_ALGN	10  */
  0x0000,	/* BOOL_ALGN	11  */
  0x0000,	/* BCD1_ALGN	12  */
  0x0001,	/* BCD2_ALGN	13  */
  0x0003,	/* BCD4_ALGN	14  */
  0x0000,	/* BIT_ALGN	15  */
  0x0000	/* VIS_ALGN	16  */
  };   
#endif

#if (defined(__ALPHA) && defined(__VMS))

#define M_STRSTART_MODE 	M_STRSTART_MODE_LARGEST
#define M_STREND_MODE 		M_STREND_MODE_LARGEST

SD_CONST ST_INT m_def_data_algn_tbl[NUM_ALGN_TYPES] =
  {
  0x0000,	/* ARRSTRT_ALGN	00  */
  0x0000,	/* ARREND_ALGN	01  */
  0x0000,	/* STRSTRT_ALGN	02  */
  0x0000,	/* STREND_ALGN	03  */
  0x0000,	/* INT8_ALGN	04  */
  0x0001,	/* INT16_ALGN	05  */
  0x0003,	/* INT32_ALGN	06  */
  0x0007,	/* INT64_ALGN	07  */
  0x0003,	/* FLOAT_ALGN	08  */
  0x0007,	/* DOUBLE_ALGN	09  */
  0x0000,	/* OCT_ALGN	10  */
  0x0000,	/* BOOL_ALGN	11  */
  0x0000,	/* BCD1_ALGN	12  */
  0x0001,	/* BCD2_ALGN	13  */
  0x0003,	/* BCD4_ALGN	14  */
  0x0000,	/* BIT_ALGN	15  */
  0x0000	/* VIS_ALGN	16  */
  };   
#endif

#if defined(__alpha) && !defined(__VMS)

#define M_STRSTART_MODE 	M_STRSTART_MODE_LARGEST
#define M_STREND_MODE 		M_STREND_MODE_LARGEST

SD_CONST ST_INT m_def_data_algn_tbl[NUM_ALGN_TYPES] =
  {
  0x0000,	/* ARRSTRT_ALGN	00  */
  0x0000,	/* ARREND_ALGN	01  */
  0x0000,	/* STRSTRT_ALGN	02  */
  0x0000,	/* STREND_ALGN	03  */
  0x0000,	/* INT8_ALGN	04  */
  0x0001,	/* INT16_ALGN	05  */
  0x0003,	/* INT32_ALGN	06  */
  0x0007,	/* INT64_ALGN	07  */
  0x0003,	/* FLOAT_ALGN	08  */
  0x0007,	/* DOUBLE_ALGN	09  */
  0x0000,	/* OCT_ALGN	10  */
  0x0000,	/* BOOL_ALGN	11  */
  0x0000,	/* BCD1_ALGN	12  */
  0x0001,	/* BCD2_ALGN	13  */
  0x0003,	/* BCD4_ALGN	14  */
  0x0000,	/* BIT_ALGN	15  */
  0x0000	/* VIS_ALGN	16  */
  };   
#endif

#ifdef _WIN32

#define M_STRSTART_MODE 	M_STRSTART_MODE_LARGEST
#define M_STREND_MODE 		M_STREND_MODE_LARGEST

SD_CONST ST_INT m_def_data_algn_tbl[NUM_ALGN_TYPES] =
  {
  0x0000,	/* ARRSTRT_ALGN	00  */
  0x0000,	/* ARREND_ALGN	01  */
  0x0000,       /* STRSTRT_ALGN 02  */
  0x0000,	/* STREND_ALGN	03  */
  0x0000,	/* INT8_ALGN	04  */
  0x0001,	/* INT16_ALGN	05  */
  0x0003,	/* INT32_ALGN	06  */
  0x0007,	/* INT64_ALGN	07  */
  0x0003,	/* FLOAT_ALGN	08  */
  0x0007,	/* DOUBLE_ALGN	09  */
  0x0000,	/* OCT_ALGN	10  */
  0x0000,	/* BOOL_ALGN	11  */
  0x0000,	/* BCD1_ALGN	12  */
  0x0001,	/* BCD2_ALGN	13  */
  0x0003,	/* BCD4_ALGN	14  */
  0x0000,	/* BIT_ALGN	15  */
  0x0000	/* VIS_ALGN	16  */
  };   
#endif

#ifdef VXWORKS

#define M_STRSTART_MODE 	M_STRSTART_MODE_LARGEST
#define M_STREND_MODE 		M_STREND_MODE_LARGEST

SD_CONST ST_INT m_def_data_algn_tbl[NUM_ALGN_TYPES] =
  {
  0x0000,	/* ARRSTRT_ALGN	00  */
  0x0000,	/* ARREND_ALGN	01  */
  0x0000,	/* STRSTRT_ALGN	02  */
  0x0000,	/* STREND_ALGN	03  */
  0x0000,	/* INT8_ALGN	04  */
  0x0001,	/* INT16_ALGN	05  */
  0x0001,	/* INT32_ALGN	06  */
  0x0001,	/* INT64_ALGN	07  */
  0x0001,	/* FLOAT_ALGN	08  */
  0x0001,	/* DOUBLE_ALGN	09  */
  0x0000,	/* OCT_ALGN	10  */
  0x0000,	/* BOOL_ALGN	11  */
  0x0000,	/* BCD1_ALGN	12  */
  0x0001,	/* BCD2_ALGN	13  */
  0x0001,	/* BCD4_ALGN	14  */
  0x0000,	/* BIT_ALGN	15  */
  0x0000	/* VIS_ALGN	16  */
  };   
#endif

#if defined (__QNX__)		/* packed	*/

#define M_STRSTART_MODE 	M_STRSTART_MODE_LARGEST
#define M_STREND_MODE 		M_STREND_MODE_LARGEST

SD_CONST ST_INT m_def_data_algn_tbl[NUM_ALGN_TYPES] =
  {
  0x0000,	/* ARRSTRT_ALGN	00  */
  0x0000,	/* ARREND_ALGN	01  */
  0x0000,	/* STRSTRT_ALGN	02  */
  0x0000,	/* STREND_ALGN	03  */
  0x0000,	/* INT8_ALGN	04  */
  0x0000,	/* INT16_ALGN	05  */
  0x0000,	/* INT32_ALGN	06  */
  0x0000,	/* INT64_ALGN	07  */
  0x0000,	/* FLOAT_ALGN	08  */
  0x0000,	/* DOUBLE_ALGN	09  */
  0x0000,	/* OCT_ALGN	10  */
  0x0000,	/* BOOL_ALGN	11  */
  0x0000,	/* BCD1_ALGN	12  */
  0x0000,	/* BCD2_ALGN	13  */
  0x0000,	/* BCD4_ALGN	14  */
  0x0000,	/* BIT_ALGN	15  */
  0x0000	/* VIS_ALGN	16  */
  };
#endif	/* __QNX__	*/

#if defined(linux)
#define M_STRSTART_MODE 	M_STRSTART_MODE_LARGEST
#define M_STREND_MODE 		M_STREND_MODE_LARGEST


SD_CONST ST_INT m_def_data_algn_tbl[NUM_ALGN_TYPES] =
  {
  0x0000,	/* ARRSTRT_ALGN	00  */
  0x0000,	/* ARREND_ALGN	01  */
  0x0000,	/* STRSTRT_ALGN	02  */
  0x0000,	/* STREND_ALGN	03  */
  0x0000,	/* INT8_ALGN	04  */
  0x0001,	/* INT16_ALGN	05  */
  0x0003,	/* INT32_ALGN	06  */
  0x0003,	/* INT64_ALGN	07  */
  0x0003,	/* FLOAT_ALGN	08  */
  0x0003,	/* DOUBLE_ALGN	09  */
  0x0000,	/* OCT_ALGN	10  */
  0x0000,	/* BOOL_ALGN	11  */
  0x0000,	/* BCD1_ALGN	12  */
  0x0001,	/* BCD2_ALGN	13  */
  0x0003,	/* BCD4_ALGN	14  */
  0x0000,	/* BIT_ALGN	15  */
  0x0000	/* VIS_ALGN	16  */
  };
#endif	/* linux	*/

#if defined (__LYNX)

#define M_STRSTART_MODE 	M_STRSTART_MODE_LARGEST
#define M_STREND_MODE 		M_STREND_MODE_LARGEST

SD_CONST ST_INT m_def_data_algn_tbl[NUM_ALGN_TYPES] =
  {
  0x0000,	/* ARRSTRT_ALGN	00  */
  0x0000,	/* ARREND_ALGN	01  */
  0x0000,	/* STRSTRT_ALGN	02  */
  0x0000,	/* STREND_ALGN	03  */
  0x0000,	/* INT8_ALGN	04  */
  0x0001,	/* INT16_ALGN	05  */
  0x0003,	/* INT32_ALGN	06  */
  0x0003,	/* INT64_ALGN	07  */
  0x0003,	/* FLOAT_ALGN	08  */
  0x0003,	/* DOUBLE_ALGN	09  */
  0x0000,	/* OCT_ALGN	10  */
  0x0000,	/* BOOL_ALGN	11  */
  0x0000,	/* BCD1_ALGN	12  */
  0x0001,	/* BCD2_ALGN	13  */
  0x0003,	/* BCD4_ALGN	14  */
  0x0000,	/* BIT_ALGN	15  */
  0x0000	/* VIS_ALGN	16  */
  };
#endif	/* __LYNX	*/
#endif	/* !defined(MMS_LITE)	*/



/************************************************************************/
/************************************************************************/
/*			ms_rt_size_calc					*/
/************************************************************************/
/*									*/
/* ****** PRIMITIVE elements alignment is well known. 			*/
/*   Element above == primitive  : Add padding to element above.	*/
/*     {								*/
/*     prim yy << pad							*/
/*     prim xx								*/
/*     }								*/
/*   Element above == struct end : Add padding to struct end above.	*/
/*     {								*/
/*     struct yy							*/
/*	 {								*/
/*       ...								*/
/*	 } << pad							*/
/*     prim xx								*/
/*     }								*/
/*   Element above == arr end    : Add padding to array end above.	*/
/*     {								*/
/*     arr yy[zz];							*/
/*	 arr_end << pad							*/
/*     prim xx								*/
/*     }								*/
/*									*/
/* ****** STRUCTURE START elements alignment is derived from elements	*/
/*   Element above == primitive  : Add padding to element above.	*/
/*     {								*/
/*     prim yy 								*/
/*     struct  << pad							*/
/*       {								*/
/*       ...								*/
/*       } xx								*/
/*     }								*/
/*   Element above == struct end : Add padding to struct end above.	*/
/*     {								*/
/*     struct yy							*/
/*	 {								*/
/*       ...								*/
/*	 } 								*/
/*     struct  << pad							*/
/*       {								*/
/*       ...								*/
/*       } xx								*/
/*     }								*/
/*   Element above == arr end    : Add padding to array end above.	*/
/*     {								*/
/*     arr yy[zz];							*/
/*     struct  << pad							*/
/*       {								*/
/*       ...								*/
/*       } xx								*/
/*     }								*/
/*									*/
/* ****** ARRAY START elements alignment is derived from element	*/
/*   Element above == primitive  : Add padding to element above.	*/
/*     {								*/
/*     prim yy << pad							*/
/*     arr xx[zz];							*/
/*     }								*/
/*   Element above == struct end : Add padding to struct end above.	*/
/*     {								*/
/*     struct yy							*/
/*	 {								*/
/*       ...								*/
/*	 } << pad							*/
/*     arr xx[zz];							*/
/*     }								*/
/*   Element above == arr end    : Add padding to array end above.	*/
/*     {								*/
/*     arr yy[zz];							*/
/*	 arr_end << pad							*/
/*     arr xx[zz];							*/
/*     }								*/
/*									*/
/* STRUCTURE SIZE PADDING - struct end					*/
/*   struct								*/
/*     {								*/
/*     xx								*/
/*     yy  << Pad for structure size  					*/
/*     }								*/
/*									*/
/* ARRAYS OF STRUCTURES - array of structure end			*/
/*   struct ss								*/
/*     {								*/
/*     xx								*/
/*     yy  								*/
/*     } << Pad to accomadate ss and xx alignment requirements		*/
/*	[xx];								*/
/*									*/
/*									*/

#if !defined(MMS_LITE)
/* NOTE: For MMS_LITE, these variables are created by Foundry and placed*/
/*       in the Foundry output 'c' file.				*/
ST_INT m_struct_start_algn_mode = M_STRSTART_MODE;
ST_INT m_struct_end_algn_mode = M_STREND_MODE;
#endif

ST_VOID ms_rt_size_calc (RUNTIME_TYPE *rt_head, ST_INT rt_num)
  {
ST_INT ellen;
RUNTIME_TYPE *arr1_rt; 
RUNTIME_TYPE *rt; 
RUNTIME_TYPE *rt_end; 
RUNTIME_TYPE *rt_pad_dest; 
ST_INT curr_offset;
ST_INT algn;
ST_INT arr_level;
ST_INT arr_start_offset[ASN1_MAX_LEVEL];
ST_INT arr_element_size;

  S_LOCK_COMMON_RESOURCES ();

  rt = rt_head;		  	  /* set runtime ptr			*/
  rt_end = rt_head + rt_num;  	  /* end block				*/

  arr_level = 0;
  curr_offset = 0;		/* Offset from start of data		*/

  rt_pad_dest = NULL;
  while (rt < rt_end)
    {
    if (rt->el_tag == RT_ARR_END)	
      {
      /* Special handling for an array of structures			*/
      /* We need to check if the structure end needs to be padded	*/
      arr1_rt = rt - rt->u.arr.num_rt_blks;
      if (arr1_rt->el_tag == RT_STR_START)
        {
        _get_struct_start_align (arr1_rt, &algn, &ellen);
        _do_padding (rt_pad_dest, &curr_offset, algn);
        }

    /* Now we need to multiply the size of this array element by the	*/
    /* number of elements in the array					*/
      --arr_level;
      arr_element_size = curr_offset - arr_start_offset[arr_level];
      curr_offset += (arr_element_size * (rt->u.arr.num_elmnts-1));
      }

  /* Get the current RT element's alignment requirements and do any	*/
  /* required padding on the element above us.				*/
    _ms_m_get_rt_info (rt, &algn, &ellen);
     _do_padding (rt_pad_dest, &curr_offset, algn);

  /* If we are starting an array, we must remember the start offset	*/
    if (rt->el_tag == RT_ARR_START)
      {
      arr_start_offset[arr_level] = curr_offset;
      ++arr_level;
      }

  /* Set the current element's nominal memory requirements		*/
    rt->el_size = ellen;

  /* Set the offset for the next element				*/
    curr_offset += ellen;

  /* Usually we pad the element directly above us ...			*/
    rt_pad_dest = rt;
    rt++;			
    }

/* We are all done with adjusting padding for our type.			*/
/* Get the offset past the last element, for encoding			*/
  rt_head->offset_to_last = ms_get_blocked_length (rt_head,rt_num);

/* Let the user have a shot at the runtime type if they want to		*/
  if (u_rt_type_process)
    (*u_rt_type_process) (rt_head, rt_num);

  S_UNLOCK_COMMON_RESOURCES ();
  }

/************************************************************************/
/*				_ms_m_get_rt_info			*/
/************************************************************************/
/* WARNING: Recursive routine */

ST_RET _ms_m_get_rt_info (SD_CONST RUNTIME_TYPE *rt, ST_INT *algn_out,
			  ST_INT *ellen_out)
  {
ST_INT algn_index = NUM_ALGN_TYPES;	/* init to ILLEGAL value	*/
ST_INT ellen;

  switch (rt->el_tag)
    {
    case RT_STR_START :			/* structure starting 		*/
      _get_struct_start_align (rt, algn_out, ellen_out);
      return (SD_SUCCESS);
    break;
    
    case RT_STR_END :			/* structure done  		*/
      _get_struct_end_align (rt, algn_out, ellen_out);
      return (SD_SUCCESS);
    break;
    
    case RT_ARR_START :			/* array starting	      	*/
      _get_arr_start_align (rt, algn_out, ellen_out);
      return (SD_SUCCESS);
    break;
    
    case RT_ARR_END :			/* array done		      	*/
      ellen = 0;
      algn_index = ARREND_ALGN;
    break;
    
    case RT_GENERAL_TIME : 		/* generalized time		*/
      ellen = sizeof(time_t);
      algn_index = GENERAL_TIME_ALGN;
    break;

    case RT_BINARY_TIME :	   /* binary time is one or 2 ST_INT32	*/
      if (rt->u.p.el_len == 6)
        ellen = 2 * sizeof(ST_INT32);
      else
        ellen = sizeof(ST_INT32);
      algn_index = INT32_ALGN;
    break;

    case RT_UTC_TIME :	   	/* UTC Time is 3 ST_UINT32 in struct	*/
      ellen = sizeof (MMS_UTC_TIME);	/* 3 * sizeof(ST_UINT32); 	*/
      algn_index = INT32_ALGN;
    break;

    case RT_INTEGER  :			/* integer			*/
    case RT_UNSIGNED :			/* integer			*/
      ellen = rt->u.p.el_len; 	/* # bytes used			*/
      switch (ellen)
        {
        case 1 :
          algn_index = INT8_ALGN;
        break;
    
        case 2 :
          algn_index = INT16_ALGN;
        break;
    
        case 4 :
          algn_index = INT32_ALGN;
        break;

#ifdef INT64_SUPPORT
        case 8 :
          algn_index = INT64_ALGN;
        break;
#endif
        }
    break;
    
    case RT_FLOATING_POINT : 		/* float			*/
      ellen = rt->u.p.el_len; 	/* # bytes used			*/
      if (ellen == 4)
        algn_index = FLOAT_ALGN;
      else
        algn_index = DOUBLE_ALGN;
    break;
    
    case RT_OCTET_STRING :		/* octet string 		*/
      ellen = abs (rt->u.p.el_len); 	/* max # bytes used    		*/
      if (rt->u.p.el_len < 0)		/* if variable, allow for size	*/
        {
        algn_index = INT16_ALGN;
        ellen += sizeof (ST_INT16);
        }
      else
        algn_index = OCT_ALGN;
    break;
    
    case RT_BOOL :			/* boolean			*/
        ellen = rt->u.p.el_len; 	/* # bytes used			*/
        algn_index = BOOL_ALGN;
    break;
    
    case RT_BCD :			/* bcd				*/
        if (rt->u.p.el_len <= 2)
          {
          ellen = 1;
          algn_index = BCD1_ALGN;
          }
        else if (rt->u.p.el_len <= 4) 
          {
          ellen = 2;
          algn_index = BCD2_ALGN;
          }
        else 
          {
          ellen = 4;
          algn_index = BCD4_ALGN;
          }
    break;
    
    case RT_BIT_STRING :  		/* bit string			*/
      ellen = abs (rt->u.p.el_len)/8; 	/* # bytes used			*/
      if (rt->u.p.el_len % 8)	 	/* if not a multiple of 8	*/
        ellen++;			/* add one to take spare bits	*/

      if (rt->u.p.el_len < 0)		/* if variable, allow for size	*/
        {
        algn_index = INT16_ALGN;
        ellen += sizeof (ST_INT16);
        }
      else
        algn_index = BIT_ALGN;
    break;
    
    case RT_VISIBLE_STRING : 		/* visible string		*/
      ellen = abs (rt->u.p.el_len) + 1;	/* # bytes used			*/
      algn_index = VIS_ALGN;
    break;

    case RT_UTF8_STRING : 		/* Unicode UTF8 string		*/
#if (UNICODE_LOCAL_FORMAT==UNICODE_UTF8)
      ellen = abs(rt->u.p.el_len)*4 + 1;/* # bytes (4 bytes per char + NULL)*/
      algn_index = VIS_ALGN;		/* Same alignment as visible string*/
#else		/* assume UNICODE_UTF16	*/
      ellen = abs(rt->u.p.el_len)*2 + 2;/* # bytes (2 bytes per char + 2 byte NULL)*/
      algn_index = INT16_ALGN;
#endif
    break;

    default :				/* should not be any other tag	*/
      MLOG_NERR0 ("Bad el_tag in RT");
      return (SD_FAILURE);
    break;
    }

  if (algn_index == NUM_ALGN_TYPES)	/* ILLEGAL value	*/
    {
    MLOG_ERR0 ("algn_index never set");	/* THIS SHOULD NEVER HAPPEN	*/
    return (SD_FAILURE);
    }

  *algn_out = m_data_algn_tbl[algn_index];
  *ellen_out = ellen;
  return (SD_SUCCESS);
  }

/************************************************************************/
/*			_get_struct_start_align				*/
/************************************************************************/
/* WARNING: Recursive routine */

static ST_VOID _get_struct_start_align (SD_CONST RUNTIME_TYPE *rt, 
				 ST_INT *algn_out,
				 ST_INT *ellen_out)
  {
ST_INT i;
ST_INT curr_algn;
ST_INT algn;
ST_INT ellen;
ST_INT num_rt;

  algn = m_data_algn_tbl[STRSTRT_ALGN];
  num_rt = rt->u.str.num_rt_blks;
  ++rt;		/* Point to first structure member */
  if (m_struct_start_algn_mode == M_STRSTART_MODE_LARGEST)
    {
    for (i = 0; i < num_rt; ++i, ++rt)
      {
      _ms_m_get_rt_info (rt, &curr_algn, &ellen);
      if (curr_algn > algn)
        algn = curr_algn;
      }
    }
  else if (m_struct_start_algn_mode == M_STRSTART_MODE_FIRST)
    _ms_m_get_rt_info (rt, &algn, &ellen);


  *algn_out = algn;
  *ellen_out = 0;
  }

/************************************************************************/
/*				_get_struct_end_align			*/
/************************************************************************/
/* WARNING: Recursive routine */

static ST_VOID _get_struct_end_align (SD_CONST RUNTIME_TYPE *rt, 
				 ST_INT *algn_out,
				 ST_INT *ellen_out)
  {
ST_INT i;
ST_INT algn;
ST_INT curr_algn;
ST_INT ellen;
ST_INT num_rt;

  num_rt = rt->u.str.num_rt_blks; 
  rt -= rt->u.str.num_rt_blks; 
  algn = m_data_algn_tbl[STREND_ALGN];
  if (m_struct_end_algn_mode == M_STREND_MODE_LARGEST)
    {
    for (i = 0; i < num_rt; ++i, ++rt)
      {
      _ms_m_get_rt_info (rt, &curr_algn, &ellen);
      if (curr_algn > algn)
        algn = curr_algn;
      }
    }

  *algn_out = algn;
  *ellen_out = 0;
  }

/************************************************************************/
/*			_get_arr_start_align				*/
/************************************************************************/
/* WARNING: Recursive routine */

static ST_VOID _get_arr_start_align (SD_CONST RUNTIME_TYPE *rt, 
				 ST_INT *algn_out,
				 ST_INT *ellen_out)
  {
ST_INT i;
ST_INT algn;
ST_INT ellen;
ST_INT num_rt;

  algn = m_data_algn_tbl[ARRSTRT_ALGN];
  num_rt = rt->u.arr.num_rt_blks;
  ++rt;
  for (i = 0; i < num_rt; ++i, ++rt)
    {
    if (rt->el_tag != RT_ARR_START)
      {
      _ms_m_get_rt_info (rt, &algn, &ellen);
      break;
      }
    }
  *algn_out = algn;
  *ellen_out = 0;
  }

/************************************************************************/
/*				_do_padding				*/
/************************************************************************/

static ST_VOID _do_padding (RUNTIME_TYPE *padDest, 
			    ST_INT *curr_offset, 
			    ST_INT algn)
  {
ST_INT pad;

/*Increment the offset until no bits are set that shouldn't be	*/
  pad = 0;
  while ((*curr_offset) & algn)
    {
    (*curr_offset)++;
    pad++;
    }
  
  /* If pad bytes are needed, add to the end struct element */
  if (pad)
    {
    if (!padDest)
      {
      MLOG_ERR0 ("Alignment table error");
      }
    else
      padDest->el_size += pad;
    }
  }


/************************************************************************/
/************************************************************************/
/************************************************************************/
/*			ms_get_blocked_len 				*/
/* Function to return the size of buffer required for blocked data for	*/
/* the data type selected. This is used when type definitions come over */
/* the wire, and in general, when a type is created.			*/
/************************************************************************/

#ifndef MMS_LITE

ST_INT ms_get_blocked_len (NAMED_TYPE *tptr)
  {
  return (ms_get_blocked_length (tptr->rt_head,tptr->rt_num));
  }

#endif

/************************************************************************/
/*			ms_get_blocked_length 				*/
/* If MMS_LITE, this function also sets rt_head->mvluTypeInfo.prim_count*/
/************************************************************************/

ST_INT ms_get_blocked_length (SD_CONST RUNTIME_TYPE *rt_head, ST_INT rt_num)
  {
ST_INT total_size;
SD_CONST RUNTIME_TYPE *rt;	/* current runtime element	*/
SD_CONST RUNTIME_TYPE *rt_end;
ST_INT arr_loop_buf[ASN1_MAX_LEVEL];
ST_INT arr_loop_level;
ST_INT *arr_loops;
ST_RTINT prim_count;

  S_LOCK_COMMON_RESOURCES ();
  arr_loop_level = 0;
  arr_loops = arr_loop_buf;

  rt = rt_head;		  /* set runtime ptr		*/
  rt_end = rt_head + rt_num;  	  /* end block			*/
  total_size = 0;
  prim_count = 0;	/* initialize	*/

  while (rt < rt_end)
    {
    if (rt->el_tag == RT_ARR_START)	/* Set up array looping		*/
      {
      ++arr_loop_level;
      arr_loops[arr_loop_level] = rt->u.arr.num_elmnts;
      }

    if (rt->el_tag == RT_ARR_END)	/* treat case of array ending	*/
      {
      if (--arr_loops[arr_loop_level] > 0) /* if need to do next ar elmnt	*/
        {
        rt -= rt->u.arr.num_rt_blks; /* mv rt to start of arr	*/
        continue; 
        }
      --arr_loop_level;
      }

    total_size += rt->el_size;
    if (ms_is_rt_prim (rt))
      prim_count++;
    rt++;			/* point to next runtime element*/
    }
    
#if defined(MMS_LITE)
  rt_head->mvluTypeInfo.prim_count = prim_count;
#endif
  S_UNLOCK_COMMON_RESOURCES ();
  return (total_size);
  }


/************************************************************************/
/*			 ms_log_runtime 				*/
/************************************************************************/
/* list the elements in a runtime type def table			*/


#define MAX_INDENT 5

ST_VOID ms_log_runtime (SD_CONST RUNTIME_TYPE *rt, ST_INT num_rt)
  {
  _ms_log_runtime (0, rt, num_rt);
  }


static ST_VOID _ms_log_runtime (ST_INT indent, SD_CONST RUNTIME_TYPE *rt, ST_INT num_rt)
  {
#if defined(DEBUG_SISCO)

ST_INT i;
const ST_CHAR *comp_name;
ST_CHAR *sp[MAX_INDENT] =
  {
  "  ",
  "    ",
  "      ",
  "        ",
  "          ",
  };
ST_CHAR *sp2[MAX_INDENT] =
  {
  "        ",
  "      ",
  "    ",
  "  ",
  "",
  };

  S_LOCK_COMMON_RESOURCES ();

  if (num_rt == 0)
    {
    MLOG_ALWAYS0 ("Runtime Type : 0 elements");
    }
  else if (indent == 0)
    {
    MLOG_ALWAYS2 ("Runtime Type : %d elements,  Offset past last : %d",
				num_rt, rt->offset_to_last);
    }

  for (i = 0; i < num_rt; ++i, rt++)
    {
    comp_name = ms_comp_name_find (rt);
    if (strlen (comp_name))
      {
      MLOG_CALWAYS3 ("%sNamed comp  %s: %s", 
    				sp[indent], 
                                sp2[indent],
    				comp_name);
      }

    switch (rt->el_tag)
      {
      case RT_ARR_START :
	MLOG_CALWAYS5 ("%sArray start %s: size %02d, %02d elmnts, %02d rt", 
                                       sp[indent],
                                       sp2[indent],
                                       rt->el_size,
                                       rt->u.arr.num_elmnts,
                                       rt->u.arr.num_rt_blks);
        if (++indent >= MAX_INDENT)
          indent--;
      break;

      case RT_ARR_END :
        if (--indent < 0)
          indent++;
	MLOG_CALWAYS5 ("%sArray end   %s: size %02d, %02d elmnts, %02d rt", 
                                       sp[indent],
                                       sp2[indent],
                                       rt->el_size,
                                       rt->u.arr.num_elmnts,
                                       rt->u.arr.num_rt_blks);
      break;

      case RT_STR_START :
	MLOG_CALWAYS4 ("%sStruct start%s: size %02d, %02d rt", 
				       	sp[indent],
                                       	sp2[indent],
					rt->el_size,
                                        rt->u.str.num_rt_blks);
        if (++indent >= MAX_INDENT)
          indent--;
      break;

      case RT_STR_END :
        if (--indent < 0)
          indent++;
	MLOG_CALWAYS3 ("%sStruct end  %s: size %02d", 
				       	sp[indent],
                                       	sp2[indent],
					rt->el_size);
      break;

      case RT_BOOL :
	MLOG_CALWAYS4 ("%sBoolean     %s: size %02d, precision %02d",
                                   sp[indent],
                                   sp2[indent],
                                   rt->el_size,
                                   rt->u.p.el_len); 
      break;

      case RT_BIT_STRING :
	MLOG_CALWAYS4 ("%sBitstring   %s: size %02d, precision %02d",
                                   sp[indent],
                                   sp2[indent],
                                   rt->el_size,
                                   rt->u.p.el_len); 
      break;

      case RT_INTEGER : 
	MLOG_CALWAYS4 ("%sInteger     %s: size %02d, precision %02d",
                                   sp[indent],
                                   sp2[indent],
                                   rt->el_size,
                                   rt->u.p.el_len); 
      break;

      case RT_UNSIGNED : 
	MLOG_CALWAYS4 ("%sUnsigned    %s: size %02d, precision %02d",
                                   sp[indent],
                                   sp2[indent],
                                   rt->el_size,
                                   rt->u.p.el_len); 
      break;

      case RT_FLOATING_POINT :
	MLOG_CALWAYS4 ("%sFloat       %s: size %02d, precision %02d",
                                   sp[indent],
                                   sp2[indent],
                                   rt->el_size,
                                   rt->u.p.el_len); 
      break;

      case RT_OCTET_STRING :
	MLOG_CALWAYS4  ("%sOctet Str  %s: size %02d, precision %02d",
                                   sp[indent],
                                   sp2[indent],
                                   rt->el_size,
                                   rt->u.p.el_len); 
      break;

      case RT_VISIBLE_STRING :
	MLOG_CALWAYS4 ("%sVisible Str %s: size %02d, precision %02d",
                                   sp[indent],
                                   sp2[indent],
                                   rt->el_size,
                                   rt->u.p.el_len); 
      break;

      case RT_GENERAL_TIME :
	MLOG_CALWAYS4 ("%sGen Time    %s: size %02d, precision %02d",
                                   sp[indent],
                                   sp2[indent],
                                   rt->el_size,
                                   rt->u.p.el_len); 
      break;

      case RT_BINARY_TIME :
	MLOG_CALWAYS4 ("%sBin Time    %s: size %02d, precision %02d",
                                   sp[indent],
                                   sp2[indent],
                                   rt->el_size,
                                   rt->u.p.el_len); 
      break;

      case RT_BCD :
	MLOG_CALWAYS4 ("%sBCD         %s: size %02d, precision %02d",
                                   sp[indent],
                                   sp2[indent],
                                   rt->el_size,
                                   rt->u.p.el_len); 
      break;

      case RT_UTC_TIME :
	MLOG_CALWAYS4 ("%sUTC Time    %s: size %02d, precision %02d",
                                   sp[indent],
                                   sp2[indent],
                                   rt->el_size,
                                   rt->u.p.el_len); 
      break;

      case RT_UTF8_STRING :
	MLOG_CALWAYS4 ("%sUTF8string  %s: size %02d, precision %02d",
                                   sp[indent],
                                   sp2[indent],
                                   rt->el_size,
                                   rt->u.p.el_len); 
      break;

      default : 			/* should not be any other tag	*/
	MLOG_CALWAYS1 (" **** UNKNOWN ELEMENT %02d ",i);
      break;
      }
    }

  S_UNLOCK_COMMON_RESOURCES ();

#endif
  }

/************************************************************************/
/*			ms_runtime_create				*/
/* This function parses an ASN.1 type definition, and creates a Runtime	*/
/* Type Table from it. It allocates memory for the table and fills it in.*/
/* The table should be destroyed by calling "ms_runtime_destroy".	*/
/************************************************************************/
RUNTIME_CTRL *ms_runtime_create (ST_UCHAR *asn1_data,
			ST_UINT asn1_len,
			ST_INT max_num_rt)
  {
RUNTIME_CTRL *rt_ctrl = NULL;	/* assume failure	*/
ST_INT num_rt;		/* num entries in RUNTIME_TYPE array	*/
ASN1_DEC_CTXT aCtx;	/* Main decode struct	*/
ASN1_TO_RT_DEC_INFO decInfo;	/* temporary storage for extra decode info.	*/
ST_UCHAR *asn1_tmp;		/* temporary copy of asn1_data.		*/

  assert (m_data_algn_tbl != NULL);	/* ptr MUST be set before now.	*/

  /* First decode only to compute "num_rt" (RUNTIME_TYPE array NOT alloc'd)*/
  memset (&aCtx, 0, sizeof (ASN1_DEC_CTXT));	/* CRITICAL: start clean.*/
  aCtx.usr_info[0] = &decInfo;	/* save ptr to decInfo in context.	*/

  /* set global variables to parse the type for size  */
  decInfo.calc_only = SD_TRUE;		/* flag to calculate num rt blocks*/

  /* If USE_COMPACT_MMS_STRUCTS defined & ObjectNames included in ASN.1,*/
  /* (maybe other cases) the ASN1 buffer gets corrupted by decode.	*/
  /* Copy to tmp buffer & use tmp buffer on 1st decode.			*/
  asn1_tmp = (ST_UCHAR *) chk_calloc (1, asn1_len);
  memcpy (asn1_tmp, asn1_data, asn1_len);
  num_rt = _ms_asn1_to_runtime (&aCtx, asn1_tmp, asn1_len, NULL, max_num_rt);
  chk_free (asn1_tmp);		/* done with temporary copy	*/

  /* Second decode to alloc & init array of RUNTIME_TYPE structs.	*/
  if (num_rt)
    {
    memset (&aCtx, 0, sizeof (ASN1_DEC_CTXT));	/* CRITICAL: start clean.*/
    aCtx.usr_info[0] = &decInfo;	/* save ptr to decInfo in context.*/
    /* Allocate RUNTIME_CTRL struct plus array of RUNTIME_TYPE structs.	*/
    rt_ctrl = (RUNTIME_CTRL *) chk_calloc (1, sizeof (RUNTIME_CTRL) + num_rt * sizeof (RUNTIME_TYPE));
    rt_ctrl->rt_first = (RUNTIME_TYPE *)(rt_ctrl + 1);

    /* parse the type encoding a second time to get an RT TYPE */
    decInfo.calc_only = SD_FALSE;
#if defined(USE_RT_TYPE_2)
    decInfo.comp_name_size = decInfo.comp_name_used;	/* Save running count.*/
    if (decInfo.comp_name_size)
      decInfo.comp_name_buf = (ST_CHAR *) chk_calloc (1, decInfo.comp_name_size);
    else
      decInfo.comp_name_buf = NULL;	/* There is no buffer.	*/
#endif	/* USE_RT_TYPE_2	*/
    rt_ctrl->rt_num = _ms_asn1_to_runtime (&aCtx, asn1_data, asn1_len, rt_ctrl->rt_first, num_rt);
    if (rt_ctrl->rt_num == 0)
      {	/* 2nd decode failed. Should almost NEVER happen. Cleanup.	*/
#if defined(USE_RT_TYPE_2)
      if (decInfo.comp_name_buf)
        chk_free (decInfo.comp_name_buf);
#endif
      chk_free (rt_ctrl);
      rt_ctrl = NULL;
      }
    }
  /* rt_first & rt_num should now be set in rt_ctrl	*/
  return (rt_ctrl);
  }

/************************************************************************/
/*			ms_runtime_destroy				*/
/* This function frees up all the memory for a Runtime Type Table	*/
/* created by "ms_runtime_create".					*/
/* It should not be called until all references to the Rumtime Type	*/
/* (i.e. Variable associations, etc.) have been destroyed.		*/ 
/************************************************************************/
ST_VOID ms_runtime_destroy (RUNTIME_CTRL *rt_ctrl)
  {
#if defined(USE_RT_TYPE_2)
RUNTIME_TYPE *rt_table = rt_ctrl->rt_first;
ST_INT j;
  /* Component names only saved if USE_RT_TYPE_2 defined. Must free them.*/
  /* Find the first non-NULL comp_name_ptr. This should be the pointer	*/
  /* to the buffer allocated for "component names". Free the buffer.	*/
  for (j=0; j < rt_ctrl->rt_num; j++)
    {
    if (rt_table[j].comp_name_ptr != 0)
      {
      chk_free (rt_table[j].comp_name_ptr);	/* Free the buffer of names*/
      break;
      }
    }
#endif	/* USE_RT_TYPE_2	*/

  /* Free the table.	*/
  chk_free (rt_ctrl);
  }

/************************************************************************/
/*			ms_is_rt_prim					*/
/************************************************************************/
ST_BOOLEAN ms_is_rt_prim (SD_CONST RUNTIME_TYPE *rt)
  {
  switch (rt->el_tag)
    {
    case RT_BOOL : 
    case RT_BIT_STRING : 
    case RT_INTEGER :
    case RT_UNSIGNED :
    case RT_FLOATING_POINT :
    case RT_OCTET_STRING : 
    case RT_VISIBLE_STRING :
    case RT_GENERAL_TIME : 
    case RT_BINARY_TIME : 
    case RT_BCD : 
    case RT_BOOLEANARRAY : 
    case RT_UTC_TIME : 
    case RT_UTF8_STRING : 
      return (SD_TRUE);
    break;
    }
  return (SD_FALSE);
  }

/************************************************************************/
/*			ms_rt_el_tag_text				*/
/* Gets "el_tag" from RUNTIME_TYPE struct and converts is to text.	*/
/************************************************************************/
ST_CHAR *ms_rt_el_tag_text (SD_CONST RUNTIME_TYPE *rt_type)
  {
ST_CHAR *el_tag_text;

  switch (rt_type->el_tag)
    {
    case RT_ARR_START:      el_tag_text="RT_ARR_START";		break;
    case RT_STR_START:      el_tag_text="RT_STR_START";		break;
    case RT_BOOL:           el_tag_text="RT_BOOL";		break;
    case RT_BIT_STRING:     el_tag_text="RT_BIT_STRING";	break;
    case RT_INTEGER:        el_tag_text="RT_INTEGER";		break;
    case RT_UNSIGNED:       el_tag_text="RT_UNSIGNED";		break;
    case RT_FLOATING_POINT: el_tag_text="RT_FLOATING_POINT";	break;
    case RT_OCTET_STRING:   el_tag_text="RT_OCTET_STRING";	break;
    case RT_VISIBLE_STRING: el_tag_text="RT_VISIBLE_STRING";	break;
    case RT_GENERAL_TIME:   el_tag_text="RT_GENERAL_TIME";	break;
    case RT_BINARY_TIME:    el_tag_text="RT_BINARY_TIME";	break;
    case RT_BCD:            el_tag_text="RT_BCD";		break;
    case RT_BOOLEANARRAY:   el_tag_text="RT_BOOLEANARRAY";	break;
    case RT_UTC_TIME:       el_tag_text="RT_UTC_TIME";		break;
    case RT_UTF8_STRING:    el_tag_text="RT_UTF8_STRING";	break;
    case RT_STR_END:        el_tag_text="RT_STR_END";		break;
    case RT_ARR_END:        el_tag_text="RT_ARR_END";		break;
    default:		    el_tag_text="RT_UNSUPPORTED";	break;	/* should never happen*/
    }
  return (el_tag_text);
  }

/************************************************************************/
/*			ms_asn1_to_runtime_x				*/
/* For backward compatibility only. Does NOT work with USE_RT_TYPE_2.	*/
/* New programs should use ms_runtime_create.				*/
/************************************************************************/

#if !defined(MMS_LITE)
ST_INT ms_asn1_to_runtime_x (ST_UCHAR *asn1_data, ST_UINT asn1_len,
   		            RUNTIME_TYPE *rt_table, ST_INT rt_len, 
   		            ST_BOOLEAN calc_only)
  {
ST_INT rt_num;		/* num entries in RUNTIME_TYPE array	*/
ASN1_DEC_CTXT aCtx;	/* Main decode struct	*/
ASN1_TO_RT_DEC_INFO decInfo;	/* temporary storage for extra decode info.	*/

  memset (&aCtx, 0, sizeof (ASN1_DEC_CTXT));	/* CRITICAL: start clean.*/
  aCtx.usr_info[0] = &decInfo;	/* save ptr to decInfo in context.*/
  decInfo.calc_only = calc_only;

  rt_num = _ms_asn1_to_runtime (&aCtx, asn1_data, asn1_len, rt_table, rt_len);

  /* rt_first & rt_num should now be set in rt_ctrl	*/
  return (rt_num);
  }

#endif	/* !MMS_LITE	*/

#if defined(USE_RT_TYPE_2)
/* Currently, the ms_rt_bld*" functions work only if USE_RT_TYPE_2	*/
/* defined. Fix to work with other options, or eliminate them.		*/

/************************************************************************/
/*			ms_rt_bld_set_name				*/
/* Copy the comp_name into the buffer and set the comp_name_ptr in the	*/
/* current RUNTIME_TYPE pointing to it.					*/
/************************************************************************/
static ST_VOID ms_rt_bld_set_name (RUNTIME_BUILD_CTXT *ctxt, ST_CHAR *comp_name)
  {
  if (comp_name)
    {
    ctxt->rt_type->comp_name_ptr = ctxt->comp_name_buf + (ctxt->rt_ctrl->rt_num * (MAX_IDENT_LEN + 1));
    strcpy (ctxt->rt_type->comp_name_ptr, comp_name);
    }
  }
/************************************************************************/
/*			ms_rt_bld_compress				*/
/* NOTE: don't need to save pointer to component name buffer allocated	*/
/*       here. This buf is freed in "ms_runtime_destroy" by using the 	*/
/*       "comp_name_ptr" of the first RUNTIME_TYPE.			*/
/************************************************************************/
static ST_VOID ms_rt_bld_compress (RUNTIME_BUILD_CTXT *ctxt)
  {
RUNTIME_CTRL *rt_ctrl = ctxt->rt_ctrl;
RUNTIME_TYPE *rt_type;
ST_CHAR *compressed_buf;/* allocated buf to store all component names.	*/
ST_CHAR *cur_ptr;	/* ptr to current position in buffer.		*/
ST_INT j;
size_t total_len;

  /* Compute total len of all component names (including 1 byte NULL terminator).*/
  for (j = 0, rt_type = rt_ctrl->rt_first, total_len = 0; j< rt_ctrl->rt_num; j++, rt_type++)
    {
    if (rt_type->comp_name_ptr)
      total_len += (strlen(rt_type->comp_name_ptr) + 1);
    }
  /* Allocate new buffer. Copy all component names to new buffer	*/
  /* and update each component name pointer.				*/
  compressed_buf = chk_malloc (total_len);
  for (j = 0, rt_type = rt_ctrl->rt_first, cur_ptr = compressed_buf;
       j< rt_ctrl->rt_num;
       j++, rt_type++)
    {
    if (rt_type->comp_name_ptr)
      {
      strcpy (cur_ptr, rt_type->comp_name_ptr);
      rt_type->comp_name_ptr = cur_ptr;
      cur_ptr += (strlen(rt_type->comp_name_ptr) + 1);
      }
    }
  assert (cur_ptr == compressed_buf + total_len);	/* make sure we counted right*/
  /* Done with old buffer, so free it, and set pointer back to NULL.	*/
  chk_free (ctxt->comp_name_buf);
  ctxt->comp_name_buf = NULL;
  }

/************************************************************************/
/*			ms_rt_bld_start					*/
/* Start building a type.						*/
/* If max_rt_num == 0:							*/
/*  Start computing the number of RUNTIME_TYPE elements			*/
/*  by setting "do_count=SD_TRUE" (used by all lower ms_rt_bld* functs).*/
/*  Call ms_rt_bld_get_count when build is done to get the count.	*/
/*  DO NOT need to call ..bld_finish or ..bld_cancel when build is done.*/
/* If max_rt_num != 0:							*/
/*  Alloc that number of RUNTIME_TYPE elements and prepare to fill them in.*/
/*  MUST call ..bld_finish or ..bld_cancel to clean up when build is done.*/
/************************************************************************/
ST_VOID ms_rt_bld_start (RUNTIME_BUILD_CTXT *rt_ctxt, ST_INT max_rt_num)
  {
RUNTIME_CTRL *rt_ctrl;

  if (max_rt_num==0)
    {			/* set up to compute number of RUNTIME_TYPE	*/
    /* NOTE: this memset effectively sets these paramters as we want:	*/
    /*       rt_ctxt->curr_count = 0 (lower functions increment it).	*/
    /*       rt_ctxt->rt_ctrl = NULL (must not write to it).		*/
    memset (rt_ctxt, 0, sizeof (RUNTIME_BUILD_CTXT));	/* start clean	*/
    rt_ctxt->do_count = SD_TRUE;
    return;
    }

  /* Allocate RUNTIME_CTRL struct plus array of RUNTIME_TYPE structs.	*/
  rt_ctrl = (RUNTIME_CTRL *) chk_calloc (1, sizeof (RUNTIME_CTRL) + max_rt_num * sizeof (RUNTIME_TYPE));
  rt_ctrl->rt_first = (RUNTIME_TYPE *)(rt_ctrl + 1);
  /* NOTE: rt_ctrl->rt_num = 0 now. Incremented as types added.	*/

  /* Initialize the runtime build context structure.	*/
  /* NOTE: this memset effectively sets these paramters as we want:	*/
  /*       rt_ctxt->nest_level = 0;					*/
  /*       rt_ctxt->errcode = SD_SUCCESS;				*/
  /*       rt_ctxt->do_count = SD_FALSE;				*/
  memset (rt_ctxt, 0, sizeof (RUNTIME_BUILD_CTXT));	/* start clean	*/
  rt_ctxt->rt_ctrl = rt_ctrl;
  rt_ctxt->rt_type = rt_ctrl->rt_first;	/* point to first. This ptr	*/
					/* incremented as types added	*/
  rt_ctxt->max_rt_type = rt_ctrl->rt_first + max_rt_num;
  /* Allocate buffer to store component names. Compress it later using	*/
  /* ms_rt_bld_compress.						*/
  rt_ctxt->comp_name_buf = chk_calloc (max_rt_num , (MAX_IDENT_LEN+1));
  memset (rt_ctxt->nest_start_ptr, 0, sizeof (rt_ctxt->nest_start_ptr));
  }
/************************************************************************/
/************************************************************************/
ST_VOID ms_rt_bld_cancel (RUNTIME_BUILD_CTXT *rt_ctxt)
  {
  if (rt_ctxt->do_count)
    return;	/* no cleanup necessary	*/

  if (rt_ctxt->comp_name_buf)
    {
    chk_free (rt_ctxt->comp_name_buf);
    rt_ctxt->comp_name_buf = NULL;
    }
  chk_free (rt_ctxt->rt_ctrl);
  }
/************************************************************************/
/************************************************************************/
ST_VOID ms_rt_bld_finish (RUNTIME_BUILD_CTXT *rt_ctxt)
  {
  if (rt_ctxt->do_count)
    return;	/* no cleanup necessary	*/

  /* This assert verifies rt_num & rt_type incremented/decremented correctly.*/
  assert (&rt_ctxt->rt_ctrl->rt_first[rt_ctxt->rt_ctrl->rt_num] == rt_ctxt->rt_type);
  ms_rt_bld_compress (rt_ctxt);

  /* CRITICAL: this funct sets "el_size" in every RUNTIME_TYPE.*/
  ms_rt_size_calc (rt_ctxt->rt_ctrl->rt_first, rt_ctxt->rt_ctrl->rt_num);
  }

/************************************************************************/
/************************************************************************/
ST_RET ms_rt_bld_remove_last (RUNTIME_BUILD_CTXT *ctxt)
  {
RUNTIME_TYPE *rt_type;

  if (ctxt->do_count)
    {
    assert (ctxt->curr_count > 0);	/* this should never be called otherwise*/
    ctxt->curr_count--;
    return (SD_SUCCESS);	/* just counting, nothing else to do	*/
    }

  if (ctxt->rt_ctrl->rt_num > 0)
    {
    ctxt->rt_ctrl->rt_num--;
    ctxt->rt_type--;	/* always decrement this & rt_num at same time*/
    rt_type = ctxt->rt_type;
    /* If removing STR_START or ARR_START must adjust nest level*/  
    if (rt_type->el_tag==RT_STR_START || rt_type->el_tag== RT_ARR_START)
      ctxt->nest_level--;
    memset (rt_type, 0, sizeof (RUNTIME_TYPE));	/* clean struct	*/
    return (SD_SUCCESS);
    }
  else
    return (SD_FAILURE);	/* already empty. Nothing to remove.*/
  }
/************************************************************************/
/* Check if state is valid to add another type.				*/
/* CRITICAL: DO NOT use when (ctxt->do_count==SD_TRUE).			*/
/************************************************************************/
ST_RET ms_rt_bld_chk_state (RUNTIME_BUILD_CTXT *ctxt)
  {
  if (ctxt->errcode)
    return (ctxt->errcode);	/* err already set. Just return it.	*/

  if (ctxt->rt_type >= ctxt->max_rt_type)
    {
    /* NOTE: if this error occurs, try calling ms_rt_bld_start with larger num*/
    /* NOTE: we want to log the "max_rt_num" passed to "ms_rt_bld_start"*/
    /*   but we didn't save it, so use pointer arithmetic to compute it.*/
    MLOG_ERR1 ("Error building type. Exceeds max number of elements '%d'",
               ctxt->max_rt_type - ctxt->rt_ctrl->rt_first);
    ctxt->errcode = SD_FAILURE;	/* set flag so all subsequent calls fail too.*/
    return (SD_FAILURE);
    }
  return (SD_SUCCESS);
  }
/************************************************************************/
/* Get current count.							*/
/* CRITICAL: maximum count may be slightly more than current count	*/
/* because RT_STR_START or RT_ARR_START may be temporarily added, then	*/
/* removed later if they are empty (see ms_rt_bld_remove_last).		*/
/************************************************************************/
ST_INT ms_rt_bld_get_count (RUNTIME_BUILD_CTXT *ctxt)
  {
  if (ctxt->do_count)
    return (ctxt->curr_count);
  else
    return (ctxt->rt_ctrl->rt_num);	/* usually same as curr_count,	*/
					/* but may be different on error*/
  }
/************************************************************************/
/* Get maximum count.							*/
/* CRITICAL: maximum count may be slightly more than current count	*/
/* because RT_STR_START or RT_ARR_START may be temporarily added, then	*/
/* removed later if they are empty (see ms_rt_bld_remove_last).		*/
/************************************************************************/
ST_INT ms_rt_bld_get_max_count (RUNTIME_BUILD_CTXT *ctxt)
  {
  assert (ctxt->do_count!=SD_FALSE);	/* only valid if do_count is TRUE.*/
  return (ctxt->max_count);
  }
/************************************************************************/
/* Increment the count and adjust the max count, if necessary.		*/
/************************************************************************/
static ST_VOID inc_count (RUNTIME_BUILD_CTXT *ctxt)
  {
  ctxt->curr_count++;
  if (ctxt->curr_count > ctxt->max_count)
    ctxt->max_count = ctxt->curr_count;	/* update max	*/
  }
/************************************************************************/
/************************************************************************/
ST_RET ms_rt_bld_add_str_start (RUNTIME_BUILD_CTXT *ctxt, ST_CHAR *comp_name)
  {
RUNTIME_TYPE *rt_type = ctxt->rt_type;
ST_RET ret;
  if (ctxt->do_count)
    {
    inc_count (ctxt);
    return (SD_SUCCESS);	/* just counting, nothing else to do	*/
    }
  if ((ret = ms_rt_bld_chk_state (ctxt)) != SD_SUCCESS)
    return (ret);	

  rt_type->el_tag = RT_STR_START;
  ms_rt_bld_set_name (ctxt, comp_name);
  ctxt->rt_ctrl->rt_num++;
  ctxt->rt_type++;	/* always increment this & rt_num at same time*/
  assert (ctxt->nest_level < ASN1_MAX_LEVEL);
  ctxt->nest_start_ptr[ctxt->nest_level++] = rt_type;
  return (SD_SUCCESS);
  }
/************************************************************************/
/************************************************************************/
ST_RET ms_rt_bld_add_str_end (RUNTIME_BUILD_CTXT *ctxt)
  {
RUNTIME_TYPE *rt_type = ctxt->rt_type;
ST_INT num_rt;
RUNTIME_TYPE *rt_start;
ST_RET ret;
  if (ctxt->do_count)
    {
    inc_count (ctxt);
    return (SD_SUCCESS);	/* just counting, nothing else to do	*/
    }
  if ((ret = ms_rt_bld_chk_state (ctxt)) != SD_SUCCESS)
    return (ret);	

  if (--ctxt->nest_level < 0)
    {
    MLOG_ERR0 ("STR_END does not have corresponding STR_START");
    ctxt->errcode = SD_FAILURE;	/* DEBUG: use different errcode?*/
    return (ctxt->errcode);
    }
  rt_start = ctxt->nest_start_ptr[ctxt->nest_level];
  if (rt_start->el_tag != RT_STR_START)
    {
    MLOG_ERR0 ("STR_END does not have corresponding STR_START");
    ctxt->errcode = SD_FAILURE;	/* DEBUG: use different errcode?*/
    return (ctxt->errcode);
    }
  num_rt = (rt_type - rt_start - 1);
  rt_start->u.str.num_rt_blks = num_rt;
  rt_type->u.str.num_rt_blks = num_rt;
  rt_type->el_tag = RT_STR_END;
  ctxt->rt_ctrl->rt_num++;
  ctxt->rt_type++;	/* always increment this & rt_num at same time*/
  return (SD_SUCCESS);
  }
/************************************************************************/
/************************************************************************/
ST_RET ms_rt_bld_add_arr_start (RUNTIME_BUILD_CTXT *ctxt, ST_CHAR *comp_name, ST_INT array_count)
  {
RUNTIME_TYPE *rt_type = ctxt->rt_type;
ST_RET ret;
  if (ctxt->do_count)
    {
    inc_count (ctxt);
    return (SD_SUCCESS);	/* just counting, nothing else to do	*/
    }
  if ((ret = ms_rt_bld_chk_state (ctxt)) != SD_SUCCESS)
    return (ret);	

  rt_type->el_tag = RT_ARR_START;
  ms_rt_bld_set_name (ctxt, comp_name);
  rt_type->u.arr.num_elmnts = array_count;
  ctxt->rt_ctrl->rt_num++;
  ctxt->rt_type++;	/* always increment this & rt_num at same time*/
  ctxt->nest_start_ptr[ctxt->nest_level++] = rt_type;
  return (SD_SUCCESS);
  }
/************************************************************************/
/************************************************************************/
ST_RET ms_rt_bld_add_arr_end (RUNTIME_BUILD_CTXT *ctxt)
  {
RUNTIME_TYPE *rt_type = ctxt->rt_type;
ST_INT num_rt;
RUNTIME_TYPE *rt_start;
ST_RET ret;
  if (ctxt->do_count)
    {
    inc_count (ctxt);
    return (SD_SUCCESS);	/* just counting, nothing else to do	*/
    }
  if ((ret = ms_rt_bld_chk_state (ctxt)) != SD_SUCCESS)
    return (ret);	

  if (--ctxt->nest_level < 0)
    {
    MLOG_ERR0 ("ARR_END does not have corresponding ARR_START");
    ctxt->errcode = SD_FAILURE;	/* DEBUG: use different errcode?*/
    return (ctxt->errcode);
    }
  rt_start = ctxt->nest_start_ptr[ctxt->nest_level];
  if (rt_start->el_tag != RT_ARR_START)
    {
    MLOG_ERR0 ("ARR_END does not have corresponding ARR_START");
    ctxt->errcode = SD_FAILURE;	/* DEBUG: use different errcode?*/
    return (ctxt->errcode);
    }
  num_rt = (rt_type - rt_start - 1);
  rt_type->u.arr.num_rt_blks = num_rt;
  rt_start->u.arr.num_rt_blks = num_rt;

/* copy the element count from the start block to the end block 	*/
  rt_type->u.arr.num_elmnts = rt_start->u.arr.num_elmnts;

  rt_type->el_tag = RT_ARR_END;
  ctxt->rt_ctrl->rt_num++;
  ctxt->rt_type++;	/* always increment this & rt_num at same time*/
  return (SD_SUCCESS);
  }
/************************************************************************/
/************************************************************************/
ST_RET ms_rt_bld_add_bool (RUNTIME_BUILD_CTXT *ctxt, ST_CHAR *comp_name)
  {
RUNTIME_TYPE *rt_type = ctxt->rt_type;
ST_RET ret;
  if (ctxt->do_count)
    {
    inc_count (ctxt);
    return (SD_SUCCESS);	/* just counting, nothing else to do	*/
    }
  if ((ret = ms_rt_bld_chk_state (ctxt)) != SD_SUCCESS)
    return (ret);	

  rt_type->el_tag = RT_BOOL;
  rt_type->u.p.el_len = 1;
  ms_rt_bld_set_name (ctxt, comp_name);
  ctxt->rt_ctrl->rt_num++;
  ctxt->rt_type++;	/* always increment this & rt_num at same time*/
  return (SD_SUCCESS);
  }
/************************************************************************/
/************************************************************************/
ST_RET ms_rt_bld_add_int (RUNTIME_BUILD_CTXT *ctxt, ST_CHAR *comp_name, ST_INT el_len)
  {
RUNTIME_TYPE *rt_type = ctxt->rt_type;
ST_RET ret;
  if (ctxt->do_count)
    {
    inc_count (ctxt);
    return (SD_SUCCESS);	/* just counting, nothing else to do	*/
    }
  if ((ret = ms_rt_bld_chk_state (ctxt)) != SD_SUCCESS)
    return (ret);	

  rt_type->el_tag = RT_INTEGER;
  rt_type->u.p.el_len = el_len;
  ms_rt_bld_set_name (ctxt, comp_name);
  ctxt->rt_ctrl->rt_num++;
  ctxt->rt_type++;	/* always increment this & rt_num at same time*/
  return (SD_SUCCESS);
  }
/************************************************************************/
/************************************************************************/
ST_RET ms_rt_bld_add_uint (RUNTIME_BUILD_CTXT *ctxt, ST_CHAR *comp_name, ST_INT el_len)
  {
RUNTIME_TYPE *rt_type = ctxt->rt_type;
ST_RET ret;
  if (ctxt->do_count)
    {
    inc_count (ctxt);
    return (SD_SUCCESS);	/* just counting, nothing else to do	*/
    }
  if ((ret = ms_rt_bld_chk_state (ctxt)) != SD_SUCCESS)
    return (ret);	

  rt_type->el_tag = RT_UNSIGNED;
  rt_type->u.p.el_len = el_len;
  ms_rt_bld_set_name (ctxt, comp_name);
  ctxt->rt_ctrl->rt_num++;
  ctxt->rt_type++;	/* always increment this & rt_num at same time*/
  return (SD_SUCCESS);
  }
/************************************************************************/
/************************************************************************/
ST_RET ms_rt_bld_add_float (RUNTIME_BUILD_CTXT *ctxt, ST_CHAR *comp_name, ST_INT el_len)
  {
RUNTIME_TYPE *rt_type = ctxt->rt_type;
ST_RET ret;
  if (ctxt->do_count)
    {
    inc_count (ctxt);
    return (SD_SUCCESS);	/* just counting, nothing else to do	*/
    }
  if ((ret = ms_rt_bld_chk_state (ctxt)) != SD_SUCCESS)
    return (ret);	

  rt_type->el_tag = RT_FLOATING_POINT;
  rt_type->u.p.el_len = el_len;
  ms_rt_bld_set_name (ctxt, comp_name);
  ctxt->rt_ctrl->rt_num++;
  ctxt->rt_type++;	/* always increment this & rt_num at same time*/
  return (SD_SUCCESS);
  }
/************************************************************************/
/************************************************************************/
ST_RET ms_rt_bld_add_bstring (RUNTIME_BUILD_CTXT *ctxt, ST_CHAR *comp_name, ST_INT el_len)
  {
RUNTIME_TYPE *rt_type = ctxt->rt_type;
ST_RET ret;
  if (ctxt->do_count)
    {
    inc_count (ctxt);
    return (SD_SUCCESS);	/* just counting, nothing else to do	*/
    }
  if ((ret = ms_rt_bld_chk_state (ctxt)) != SD_SUCCESS)
    return (ret);	

  rt_type->el_tag = RT_BIT_STRING;
  rt_type->u.p.el_len = el_len;
  ms_rt_bld_set_name (ctxt, comp_name);
  ctxt->rt_ctrl->rt_num++;
  ctxt->rt_type++;	/* always increment this & rt_num at same time*/
  return (SD_SUCCESS);
  }
/************************************************************************/
/************************************************************************/
ST_RET ms_rt_bld_add_bvstring (RUNTIME_BUILD_CTXT *ctxt, ST_CHAR *comp_name, ST_INT el_len)
  {
RUNTIME_TYPE *rt_type = ctxt->rt_type;
ST_RET ret;
  if (ctxt->do_count)
    {
    inc_count (ctxt);
    return (SD_SUCCESS);	/* just counting, nothing else to do	*/
    }
  if ((ret = ms_rt_bld_chk_state (ctxt)) != SD_SUCCESS)
    return (ret);	

  rt_type->el_tag = RT_BIT_STRING;
  rt_type->u.p.el_len = -el_len;
  ms_rt_bld_set_name (ctxt, comp_name);
  ctxt->rt_ctrl->rt_num++;
  ctxt->rt_type++;	/* always increment this & rt_num at same time*/
  return (SD_SUCCESS);
  }
/************************************************************************/
/************************************************************************/
ST_RET ms_rt_bld_add_vstring (RUNTIME_BUILD_CTXT *ctxt, ST_CHAR *comp_name, ST_INT el_len)
  {
RUNTIME_TYPE *rt_type = ctxt->rt_type;
ST_RET ret;
  if (ctxt->do_count)
    {
    inc_count (ctxt);
    return (SD_SUCCESS);	/* just counting, nothing else to do	*/
    }
  if ((ret = ms_rt_bld_chk_state (ctxt)) != SD_SUCCESS)
    return (ret);	

  rt_type->el_tag = RT_VISIBLE_STRING;
  rt_type->u.p.el_len = -el_len;
  ms_rt_bld_set_name (ctxt, comp_name);
  ctxt->rt_ctrl->rt_num++;
  ctxt->rt_type++;	/* always increment this & rt_num at same time*/
  return (SD_SUCCESS);
  }
/************************************************************************/
/************************************************************************/
ST_RET ms_rt_bld_add_utctime (RUNTIME_BUILD_CTXT *ctxt, ST_CHAR *comp_name)
  {
RUNTIME_TYPE *rt_type = ctxt->rt_type;
ST_RET ret;
  if (ctxt->do_count)
    {
    inc_count (ctxt);
    return (SD_SUCCESS);	/* just counting, nothing else to do	*/
    }
  if ((ret = ms_rt_bld_chk_state (ctxt)) != SD_SUCCESS)
    return (ret);	

  rt_type->el_tag = RT_UTC_TIME;
  rt_type->u.p.el_len = 8;
  ms_rt_bld_set_name (ctxt, comp_name);
  ctxt->rt_ctrl->rt_num++;
  ctxt->rt_type++;	/* always increment this & rt_num at same time*/
  return (SD_SUCCESS);
  }
/************************************************************************/
/************************************************************************/
ST_RET ms_rt_bld_add_btime6 (RUNTIME_BUILD_CTXT *ctxt, ST_CHAR *comp_name)
  {
RUNTIME_TYPE *rt_type = ctxt->rt_type;
ST_RET ret;
  if (ctxt->do_count)
    {
    inc_count (ctxt);
    return (SD_SUCCESS);	/* just counting, nothing else to do	*/
    }
  if ((ret = ms_rt_bld_chk_state (ctxt)) != SD_SUCCESS)
    return (ret);	

  rt_type->el_tag = RT_BINARY_TIME;
  rt_type->u.p.el_len = 6;
  ms_rt_bld_set_name (ctxt, comp_name);
  ctxt->rt_ctrl->rt_num++;
  ctxt->rt_type++;	/* always increment this & rt_num at same time*/
  return (SD_SUCCESS);
  }
/************************************************************************/
/************************************************************************/
ST_RET ms_rt_bld_add_ostring (RUNTIME_BUILD_CTXT *ctxt, ST_CHAR *comp_name, ST_INT el_len)
  {
RUNTIME_TYPE *rt_type = ctxt->rt_type;
ST_RET ret;
  if (ctxt->do_count)
    {
    inc_count (ctxt);
    return (SD_SUCCESS);	/* just counting, nothing else to do	*/
    }
  if ((ret = ms_rt_bld_chk_state (ctxt)) != SD_SUCCESS)
    return (ret);	

  rt_type->el_tag = RT_OCTET_STRING;
  rt_type->u.p.el_len = el_len;
  ms_rt_bld_set_name (ctxt, comp_name);
  ctxt->rt_ctrl->rt_num++;
  ctxt->rt_type++;	/* always increment this & rt_num at same time*/
  return (SD_SUCCESS);
  }
/************************************************************************/
/************************************************************************/
ST_RET ms_rt_bld_add_ovstring (RUNTIME_BUILD_CTXT *ctxt, ST_CHAR *comp_name, ST_INT el_len)
  {
RUNTIME_TYPE *rt_type = ctxt->rt_type;
ST_RET ret;
  if (ctxt->do_count)
    {
    inc_count (ctxt);
    return (SD_SUCCESS);	/* just counting, nothing else to do	*/
    }
  if ((ret = ms_rt_bld_chk_state (ctxt)) != SD_SUCCESS)
    return (ret);	

  rt_type->el_tag = RT_OCTET_STRING;
  rt_type->u.p.el_len = -el_len;
  ms_rt_bld_set_name (ctxt, comp_name);
  ctxt->rt_ctrl->rt_num++;
  ctxt->rt_type++;	/* always increment this & rt_num at same time*/
  return (SD_SUCCESS);
  }
/************************************************************************/
/************************************************************************/
ST_RET ms_rt_bld_add_utf8vstring (RUNTIME_BUILD_CTXT *ctxt, ST_CHAR *comp_name, ST_INT el_len)
  {
RUNTIME_TYPE *rt_type = ctxt->rt_type;
ST_RET ret;
  if (ctxt->do_count)
    {
    inc_count (ctxt);
    return (SD_SUCCESS);	/* just counting, nothing else to do	*/
    }
  if ((ret = ms_rt_bld_chk_state (ctxt)) != SD_SUCCESS)
    return (ret);	

  rt_type->el_tag = RT_UTF8_STRING;
  rt_type->u.p.el_len = -el_len;
  ms_rt_bld_set_name (ctxt, comp_name);
  ctxt->rt_ctrl->rt_num++;
  ctxt->rt_type++;	/* always increment this & rt_num at same time*/
  return (SD_SUCCESS);
  }
/************************************************************************/
/* Use info from caller's RUNTIME_TYPE struct.				*/
/************************************************************************/
ST_RET ms_rt_bld_add_special (RUNTIME_BUILD_CTXT *ctxt, ST_CHAR *comp_name, RUNTIME_TYPE *special)
  {
RUNTIME_TYPE *rt_type = ctxt->rt_type;
ST_RET ret;
  if (ctxt->do_count)
    {
    inc_count (ctxt);
    return (SD_SUCCESS);	/* just counting, nothing else to do	*/
    }
  if ((ret = ms_rt_bld_chk_state (ctxt)) != SD_SUCCESS)
    return (ret);	

  /* NOTE: only need el_tag & el_len	*/	
  rt_type->el_tag = special->el_tag;
  rt_type->u.p.el_len = special->u.p.el_len;
  ms_rt_bld_set_name (ctxt, comp_name);
  ctxt->rt_ctrl->rt_num++;
  ctxt->rt_type++;	/* always increment this & rt_num at same time*/
  return (SD_SUCCESS);
  }
#endif	/* defined(USE_RT_TYPE_2)	*/
