/************************************************************************/
/* SISCO SOFTWARE MODULE HEADER *****************************************/
/************************************************************************/
/*   (c) Copyright Systems Integration Specialists Company, Inc.,	*/
/*      	  1997 - 2006, All Rights Reserved.			*/
/*									*/
/*		    PROPRIETARY AND CONFIDENTIAL			*/
/*									*/
/* MODULE NAME : mmsdatat.c						*/
/* PRODUCT(S)  : none							*/
/*									*/
/* MODULE DESCRIPTION : 						*/
/*	This module contains the functions employed by the virtual	*/
/*	machine for parsing an ASN.1 data string and creating the	*/
/*	run-time type specification from it.				*/
/*	Adapted from mmsdata.c						*/
/*									*/
/* GLOBAL FUNCTIONS DEFINED IN THIS MODULE :				*/
/*									*/
/* MODIFICATION LOG :							*/
/*  Date     Who   Rev			Comments			*/
/* --------  ---  ------   -------------------------------------------	*/
/* 03/17/08  JRB     19    Del m_use_long_ints flag & assume smallest	*/
/*			   integer is 4 bytes.				*/
/* 02/19/07  JRB     18    Add error log message.			*/
/* 03/22/06  EJV     17    Changed RT_GENERAL_TIME st_size and el_len to*/
/*			     sizeof(time_t) (VS 2005 has 64-bit time_t).*/
/* 01/30/06  GLB     16    Integrated porting changes for VMS           */
/* 10/27/03  JRB     15    Assume all UTF8strings are variable-length.	*/
/*			   Add code to adjust len for array of UTF8str..*/
/* 10/16/03  MDE     14    Fixed leak for data conversion error		*/
/* 05/08/03  JRB     13    Fix TAG_ADD for "array" of RT_UTF8_STRING.	*/
/* 04/02/03  JRB     12    Add UTF8string support (see RT_UTF8_STRING).	*/
/* 12/20/01  JRB     11    Converted to use ASN1R (re-entrant ASN1)	*/
/* 11/15/01  EJV     10    Added support for new MMS type UtcTime.	*/
/* 10/06/00  MDE     09    Now set asn1_decode_method			*/
/* 04/20/00  JRB     08    Undo last chg, fix mms_vvar.h instead. Lint.	*/
/* 10/13/99  RKR     07    Added SD_CONST to function headers		*/
/* 09/13/99  MDE     06    Added SD_CONST modifiers			*/
/* 06/03/98  MDE     05    Allow for 0 length variable length items	*/
/* 03/12/98  MDE     04    Fixed 'loops' change - whoops!		*/
/* 02/10/98  MDE     03    No longer use runtime type 'loops' element	*/
/* 08/15/97  MDE     02    BTOD handling changes			*/
/* 07/03/97  MDE     01    Fixed problem w/structures			*/
/* 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"
#include "mem_chk.h"

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

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

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

static ST_VOID assign_tags (ASN1_DEC_CTXT *aCtx);
static int init_new_RT (ASN1_DEC_CTXT *aCtx, ST_UCHAR tag, ST_INT el_len);
#if 0 	/* Constructed bitstrings and octetstrings not supported 	*/
static ST_VOID get_bstr_cstr   (ASN1_DEC_CTXT *aCtx); 	/* for constructed bitstrings	*/
static ST_VOID get_ostr_cstr   (ASN1_DEC_CTXT *aCtx); 	/* for constructed octetstrings */
static ST_VOID bs_cstr_done    (ASN1_DEC_CTXT *aCtx);
static ST_VOID os_cstr_done    (ASN1_DEC_CTXT *aCtx);
#endif
static ST_VOID get_bool_prim   (ASN1_DEC_CTXT *aCtx); 	/* for boolean primitives	*/
static ST_VOID get_bitstr_prim (ASN1_DEC_CTXT *aCtx); 	/* for bitstring primitives	*/
static ST_VOID get_int_prim    (ASN1_DEC_CTXT *aCtx); 	/* for integer primitives	*/
static ST_VOID get_uint_prim   (ASN1_DEC_CTXT *aCtx); 	/* for unsigned integers	*/
static ST_VOID get_float_prim  (ASN1_DEC_CTXT *aCtx); 	/* for float primitives 	*/
static ST_VOID get_octstr_prim (ASN1_DEC_CTXT *aCtx); 	/* for octet string primitives	*/
static ST_VOID get_vstr_prim   (ASN1_DEC_CTXT *aCtx); 	/* for visible string primitives*/
static ST_VOID get_gtime_prim  (ASN1_DEC_CTXT *aCtx); 	/* for generalized time 	*/
static ST_VOID get_btime_prim  (ASN1_DEC_CTXT *aCtx); 	/* for binary time primitives	*/
static ST_VOID get_bcd_prim    (ASN1_DEC_CTXT *aCtx); 	/* for binary coded decimal	*/
static ST_VOID get_utc_time_prim  (ASN1_DEC_CTXT *aCtx); 	/* for UTC time primitives	*/
static ST_VOID get_utf8_prim  (ASN1_DEC_CTXT *aCtx); 	/* for UTF8string primitives	*/
static ST_VOID arr_start	    (ASN1_DEC_CTXT *aCtx);
static ST_VOID str_start	    (ASN1_DEC_CTXT *aCtx);
static ST_VOID arr_done_fun    (ASN1_DEC_CTXT *aCtx);
static ST_VOID str_done_fun    (ASN1_DEC_CTXT *aCtx);
static ST_VOID err_clean_fun   (ASN1_DEC_CTXT *aCtx, ST_RET err_code);


typedef enum { UNDEF, STRUCT, ARRAY } ConstrType; /* Constructor types */


static RUNTIME_TYPE	*rt_base;	/* Base ptr for runtime array 	*/
static RUNTIME_TYPE	*cur_rt;	/* Current runtime element ptr 	*/
static RUNTIME_TYPE	*nxt_un_rt;	/* Next unused RT el in buffer 	*/
static RUNTIME_TYPE	*rt_EOB_ptr;	/* One past end of RT buffer. 	*/
static int local_alloc_flag;
				    /* Block pointer for arrs & structs */
static RUNTIME_TYPE *rt_blk_start[ASN1_MAX_LEVEL];
static ConstrType constr_type[ASN1_MAX_LEVEL]; /* Cstr type		*/
static ST_INT arr_loop_level;
static ST_INT *arr_loops;

/* The following defines specify the various error codes that are used	*/
/* in ms_asn1_to_locl and its subordinate functions.  These are in ad-	*/
/* dition to the error codes that can be generated by ASN1DE internally,*/
/* and both translate into return values that are one greater than the	*/
/* error codes themselves.						*/

#define 	BAD_DATA	100	/* Data not consistent with	*/
					/* type specification.		*/
#define 	INTERNAL	101	/* Type specif'n bad, not data. */

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

ST_RET ms_asn1_data_to_locl (ST_UCHAR *asn1_data,	   
		  	    ST_INT asn1_data_len, 
			    ST_VOID **data_dest,	   
			    ST_INT *data_dest_len,
			    RUNTIME_TYPE **rt,     
			    ST_INT *t_len)	   
  {
ST_RET rc;
RUNTIME_TYPE *rt_in;

			/* First, build runtime from data string */
  rt_in = *rt;
  rc = ms_asn1_data_to_runtime (rt, t_len, asn1_data, asn1_data_len);
  if (rc != SD_SUCCESS)
    return (rc);

/* verify that the supplied data buffer is big enough			*/
  if (*data_dest && *data_dest_len < (*rt)->offset_to_last)
    {
    if (!rt_in)		/* RT table was chk_calloc'd			*/
      chk_free (*rt);

    return (MVE_DATA_SPACE);
    }

  if (!*data_dest)
    {
    *data_dest_len = (*rt)->offset_to_last;

  /* Variable length items can have a data length of 0 */
    if (*data_dest_len)
      {
      *data_dest = chk_calloc (1, (*rt)->offset_to_last);

		/* OK, go ahead and do the data conversion	 	*/
      rc = ms_asn1_to_local (*rt, *t_len, asn1_data, asn1_data_len, (ST_CHAR *) *data_dest);
      if (rc)
        {
        chk_free (*data_dest);
        if (!rt_in)		/* RT table was chk_calloc'd			*/
          chk_free (*rt);
	}
      }
    else
      *data_dest = chk_calloc (1, 1);
    }
  else		/* OK, go ahead and do the data conversion		*/
    {

    rc = ms_asn1_to_local (*rt, *t_len, asn1_data, asn1_data_len, (ST_CHAR *) *data_dest);
    if (rc && !rt_in)	/* Error and RT table was chk_calloc'd			*/
      chk_free (*rt);
    }
  return (rc);
  }
	

/************************************************************************/
/*			ms_asn1_data_to_runtime				*/
/* Creates a run-time data defn table from the input ASN.1 data string. */
/* Input parameters:							*/
/* RUNTIME_TYPE **tptr	Pointer^2 to runtime buffer.			*/
/* ST_INT *t_len	Length of runtime buffer, in runtime elements.	*/
/* ST_UCHAR *asn1ptr	Pointer to input ASN.1 string			*/
/* ST_INT asn1len	Length of ASN.1 string, in bytes.		*/
/* Returns:	SD_SUCCESS, SD_FAILURE or INTERNAL.  If SD_FAILURE, aCtx->asn1r_pdu_dec_err	*/
/*		has the specific error code.				*/
/* Output globals: aCtx->asn1r_decode_done_fun 					*/
/* Static variables set: rt_base, rt_EOB_ptr, cur_rt, nxt_un_rt		*/
/************************************************************************/


ST_RET ms_asn1_data_to_runtime (RUNTIME_TYPE **tptr,
			ST_INT *t_len,
			ST_UCHAR *asn1ptr,
			ST_INT asn1_len)
  {
ST_RET ret;
ST_INT arr_loop_buf[ASN1_MAX_LEVEL];
ASN1_DEC_CTXT localDecCtx;	/* For readability, use "aCtx" to access this.*/
ASN1_DEC_CTXT *aCtx = &localDecCtx;

  memset (aCtx, 0, sizeof (ASN1_DEC_CTXT));	/* CRITICAL: start clean.	*/

  local_alloc_flag = SD_FALSE;
  if (tptr == NULL)		/* What?  Parameter error, fix that. */
    {
    return (MVE_RT_TYPE);
    }

  S_LOCK_COMMON_RESOURCES ();
  arr_loop_level = 0;
  arr_loops = arr_loop_buf;
  if (*tptr != NULL)
    {				/* Type buffer already allocated, use it. */
    rt_base = *tptr;		/* Init pointer to runtime buffer. */
    if (*t_len <= 0)		/* Check size. */
      {
      S_UNLOCK_COMMON_RESOURCES ();
      return (MVE_RT_TYPE);		/* Parameter error. */
      }
    else
      rt_EOB_ptr = rt_base + *t_len;	/* Init end-of-buffer pointer */
    }
  else				/* Allocate a default-size buffer here. */
    {
    rt_base = (RUNTIME_TYPE *) chk_calloc (m_rt_type_limit, sizeof (RUNTIME_TYPE));
    rt_EOB_ptr = rt_base + m_rt_type_limit;
    local_alloc_flag = SD_TRUE;
    }

  cur_rt = nxt_un_rt = rt_base;
  rt_blk_start[0] = rt_base;	/* Starting block for level 0 is first block. */
  constr_type[0] = UNDEF;	/* Constructed type initializes to UNDEF. */
			/* These variables are used by the tag routines */
			/* to build the runtime type table. */

  aCtx->asn1r_decode_method = ASN1_TAG_METHOD;
  assign_tags (aCtx);		/* Assign tags.  First type can be anything. */

  aCtx->asn1r_decode_done_fun = NULL;	
  aCtx->asn1r_err_fun = err_clean_fun;

  asn1r_decode_asn1 (aCtx, asn1ptr, asn1_len);  /* Build the type table. */

  *tptr = rt_base;		/* Local buffer de-allocated on error. */
  if (rt_base != NULL)		/* Check for error to prevent doing */
    {				/* arithmetic with NULL pointers. */
    *t_len = nxt_un_rt - rt_base;
    ms_rt_size_calc (rt_base, *t_len); /* Fix up element offsets in table */
    }
  else
    *t_len = 0;

  ret =  (aCtx->asn1r_pdu_dec_err == NO_DECODE_ERR) ? SD_SUCCESS : MVE_DATA_TO_RT ;
  S_UNLOCK_COMMON_RESOURCES ();
  return (ret);
  }

/************************************************************************/
/*			     assign_tags				*/
/* Function to assign tags to the ASN1DE tools based on what's expected */
/* in the runtime_type definition table.  At the beginning of the 'Data'*/
/* entity, a DataAccessError type is allowed.  WARNING: This function	*/
/* requires that the current rt_block NOT be an end-of-array or end-of- */
/* structure rt_block.	If so, it will return an error. 		*/
/************************************************************************/

static ST_VOID assign_tags (ASN1_DEC_CTXT *aCtx)
  {	/* There are two possible cases here:				*/
	/* 1.)  We are parsing through terra incognita and don't know	*/
	/*	what to expect (yet).  All possible tags are legal.	*/
	/*	Set up to build type definitions for what we find.	*/
	/* 2.)	We are parsing through the 2nd or later element of an	*/
	/*	array and know what tag we should get next.  If the	*/
	/*	array is a sub-array of an enveloping array, we will	*/
	/*	have an element count for it; check that too.  Verify	*/
	/*	tags against what we expect to see.			*/

  if (cur_rt < nxt_un_rt)	/* if this is the 2nd or later element	*/
    {				/* of an array & types are defined, 	*/
    if (cur_rt->el_tag == RT_ARR_END)	/* treat case of array ending */
      {			/* Only possible if we've seen this array before as */
      			/* an element of an enveloping array, otherwise	*/
			/* there would be no RT_ARR_END runtime record yet. */
      if (--arr_loops[arr_loop_level] > 0) /* if need to do next ar elmnt */
	cur_rt -= cur_rt->u.arr.num_rt_blks;
      }					/* mv cur_rt to start of arr */

    switch (cur_rt->el_tag)
      {
      case RT_ARR_START :
	ASN1R_TAG_ADD (aCtx, CTX|CONSTR,1,arr_start); /* expecting an array cstr	*/
      break;

      case RT_STR_START :
	ASN1R_TAG_ADD (aCtx, CTX|CONSTR,2,str_start); /* expecting a struct cstr	*/
      break;

      case RT_BOOL :
	ASN1R_TAG_ADD (aCtx, CTX,3,get_bool_prim);	
      break;

      case RT_BIT_STRING :			
	ASN1R_TAG_ADD (aCtx, CTX,4,get_bitstr_prim);
#if 0 	/* Constructed bitstrings and octetstrings not supported 	*/
	ASN1R_TAG_ADD (aCtx, CTX|CONSTR,4,get_bstr_cstr); 
#endif
      break;

      case RT_INTEGER :			
	ASN1R_TAG_ADD (aCtx, CTX,5,get_int_prim);
      break;

      case RT_UNSIGNED : 			
	ASN1R_TAG_ADD (aCtx, CTX,6,get_uint_prim);
      break;

#ifdef FLOAT_DATA_SUPPORT
      case RT_FLOATING_POINT : 		
	ASN1R_TAG_ADD (aCtx, CTX,7,get_float_prim);
      break;
#endif

      case RT_OCTET_STRING : 		
	ASN1R_TAG_ADD (aCtx, CTX,9,get_octstr_prim);
#if 0 	/* Constructed bitstrings and octetstrings not supported 	*/
	ASN1R_TAG_ADD (aCtx, CTX|CONSTR,9,get_ostr_cstr); 
#endif
      break;

      case RT_VISIBLE_STRING : 		
	ASN1R_TAG_ADD (aCtx, CTX,10,get_vstr_prim);
      break;

#ifdef TIME_DATA_SUPPORT
      case RT_GENERAL_TIME : 		
	ASN1R_TAG_ADD (aCtx, CTX,11,get_gtime_prim);
      break;
#endif

#ifdef BTOD_DATA_SUPPORT
      case RT_BINARY_TIME : 		
	ASN1R_TAG_ADD (aCtx, CTX,12,get_btime_prim);
      break;
#endif

      case RT_BCD : 			
	ASN1R_TAG_ADD (aCtx, CTX,13,get_bcd_prim);
      break;

      case RT_UTC_TIME : 		
	ASN1R_TAG_ADD (aCtx, CTX,17,get_utc_time_prim);
      break;

      case RT_UTF8_STRING : 		
	ASN1R_TAG_ADD (aCtx, CTX,RT_UTF8_STRING,get_utf8_prim);
      break;

      case RT_ARR_END :			/* array done			*/
	aCtx->asn1r_c_done_fun[aCtx->asn1r_msg_level] = arr_done_fun; /* the arr cstr must be done*/
      break;

      case RT_STR_END :			/* structure done		*/
	aCtx->asn1r_c_done_fun[aCtx->asn1r_msg_level] = str_done_fun; /* the str cstr must be done*/
      break;

      default : 			/* should not be any other tag	*/
        MLOG_ERR0 ("Bad tag in runtime type");
	asn1r_set_dec_err (aCtx, INTERNAL);
	return;
      break;
      }
    }
  else			/* In terra incognita, next element can be anything */
    {
    /* Allow for any type of data in the data string. */
    ASN1R_TAG_ADD (aCtx, CTX|CONSTR,1,arr_start);
    ASN1R_TAG_ADD (aCtx, CTX|CONSTR,2,str_start);
    ASN1R_TAG_ADD (aCtx, CTX,3,get_bool_prim);	
    ASN1R_TAG_ADD (aCtx, CTX,4,get_bitstr_prim);
#if 0 	/* Constructed bitstrings and octetstrings not supported 	*/
    ASN1R_TAG_ADD (aCtx, CTX|CONSTR,4,get_bstr_cstr);
#endif
    ASN1R_TAG_ADD (aCtx, CTX,5,get_int_prim);
    ASN1R_TAG_ADD (aCtx, CTX,6,get_uint_prim);
    ASN1R_TAG_ADD (aCtx, CTX,7,get_float_prim);
    ASN1R_TAG_ADD (aCtx, CTX,9,get_octstr_prim);
#if 0 	/* Constructed bitstrings and octetstrings not supported 	*/
    ASN1R_TAG_ADD (aCtx, CTX|CONSTR,9,get_ostr_cstr);
#endif
    ASN1R_TAG_ADD (aCtx, CTX,10,get_vstr_prim);
    ASN1R_TAG_ADD (aCtx, CTX,11,get_gtime_prim);
    ASN1R_TAG_ADD (aCtx, CTX,12,get_btime_prim);
    ASN1R_TAG_ADD (aCtx, CTX,13,get_bcd_prim);
    ASN1R_TAG_ADD (aCtx, CTX,17,get_utc_time_prim);
    ASN1R_TAG_ADD (aCtx, CTX,RT_UTF8_STRING,get_utf8_prim);
    }
  }



/************************************************************************/
/*			   init_new_RT					*/
/* This is the function called to check to see if a new RT table entry	*/
/* is required and available, and initializes it if so.			*/
/* Parameters:								*/
/*	tag	Tag value for the field type.				*/
/*	el_len	Element length, in its own units.  0 for constructed	*/
/*		types.  Units are bytes except for bitstrings (length	*/
/*		given in bits).						*/
/* Globals input:							*/
/*	cur_rt, nxt_un_rt, rt_EOB_ptr					*/
/* Globals output:							*/
/*	nxt_un_rt							*/
/* Returns:								*/
/*	0 if OK, 1 if error (table overflow).				*/
/************************************************************************/

static int init_new_RT (ASN1_DEC_CTXT *aCtx, ST_UCHAR tag, ST_INT el_len)
  {
ST_BOOLEAN prim;
ST_INT st_size;

  prim = SD_TRUE;
  switch (tag)		/* Calc storage size based on element fundamentals */
    {
    case RT_BOOL:			/* Size must be 1, so ignore it. */
      st_size = 1;
    break;

    case RT_BIT_STRING:			/* el_len was converted to bits */
      st_size = CALC_BIT_LEN (el_len);	/* Calculate byte count. */
    break;

    case RT_INTEGER:			/* Size is in bytes. */
    case RT_UNSIGNED:
      st_size = el_len;			/* already conditioned (1,2,4)	*/
    break;

    case RT_BCD:			/* always put into ST_INT32		*/
      st_size = 4;
      el_len = 8;			/* assume type is 8 BCD digits	*/
    break;

    case RT_FLOATING_POINT:		
	/* Decrement el_len because it's used for storage size later */
      st_size = (--el_len == 4) ? sizeof (float) : sizeof (double);
    break;

    case RT_OCTET_STRING:		/* Size is in bytes. */
      st_size = el_len;			/* No terminating null */
    break;				/* If variable length, size will */
					/* be increased by sizeof (ST_INT16) */

    case RT_VISIBLE_STRING:		/* Size is in bytes. */
      st_size = el_len+1;		/* Add space for terminating null */
    break;				/* No increase for variable length */

    case RT_GENERAL_TIME:		/* Generalized time */
      st_size = sizeof (time_t);
      el_len = sizeof (time_t);
    break;

    case RT_BINARY_TIME:		/* Binary time */
      if (el_len == 6)
        st_size = 2 * sizeof (ST_INT32);
      else
        st_size = sizeof (ST_INT32);
    break;

    case RT_UTC_TIME:			/* UTC time */
      st_size = sizeof (MMS_UTC_TIME);	/* 3 * sizeof (ST_UINT32);	*/
    break;

    case RT_UTF8_STRING:		/* Unicode UTF8string */
#if (UNICODE_LOCAL_FORMAT==UNICODE_UTF8)
      st_size = abs(el_len)*4 + 1;	/* # bytes (4 bytes per char + NULL)*/
#else		/* assume UNICODE_UTF16	*/
      st_size = abs(el_len)*2 + 2;	/* # bytes (2 bytes per char + 2 byte NULL)*/
#endif
    break;

    default:
      prim = SD_FALSE;			/* Not a primitive element. */
      st_size = 0;
    break;
    }

  if (cur_rt == nxt_un_rt)	/* If this element is not already defined, */
    {				/* We need to use another el, do ovrfl check */
    if (nxt_un_rt == rt_EOB_ptr) /* Check against EOB pointer. */
      {
      asn1r_set_dec_err (aCtx, MX_MEMORY_ALLOC); /* Table is full, declare error & return */
      MLOG_ERR1 ("Exceeded %d limit on number of elements in RUNTIME_TYPE array", rt_EOB_ptr - rt_base);
      return SD_FAILURE;		/* Error value. */
      }
    ++nxt_un_rt;			/* Used one more element. */
    				/* Clear the new element. */
    memset ((char *)cur_rt, 0, sizeof (RUNTIME_TYPE));
    cur_rt->el_tag = tag;	/* Set the tag value. */
    cur_rt->el_size = st_size;	/* Set storage size */
    if (prim)
      {
      if (tag == RT_BCD)
        cur_rt->u.p.el_len = 8;
      else
        cur_rt->u.p.el_len = el_len; /* Set initial data size for prim */
      }
    if (tag == RT_ARR_START)
      cur_rt->u.arr.num_rt_blks = -1; /* Flag unfinished constructor */
    else if (tag == RT_STR_START)
      cur_rt->u.str.num_rt_blks = -1; /* Flag unfinished constructor */

    return SD_SUCCESS;			/* New element, done. */
    }

  /* The following code executes if this is an element of an array.	*/
  /* It adjusts sizes if necessary. For example, if the second Vstring	*/
  /* in an array is longer than the first, the el_len and el_size are	*/
  /* increased to handle it.						*/
 
			/* This is an "old" element.  Check data sizes	*/
			/* and update as required.  Check for variable	*/
  switch (tag)		/* length issues in byte and bit strings.	*/
    {
    case RT_BIT_STRING:			/* Size is in bits */
      if (cur_rt->u.p.el_len < 0)	/* Is variable */
        {					/* Re-size if necessary */
	if (-cur_rt->u.p.el_len < el_len)  /* New one is bigger */
	  {
	  cur_rt->u.p.el_len = -el_len;	/* Set new length */
	  cur_rt->el_size = st_size + sizeof (ST_INT16);
	  }
	}
      else if (cur_rt->u.p.el_len != el_len)	/* Different length */
        {					/* Make variable length */
	if (cur_rt->u.p.el_len > el_len) /* Size for biggest seen */
	  el_len = cur_rt->u.p.el_len;

	cur_rt->u.p.el_len = -el_len;
	cur_rt->el_size = CALC_BIT_LEN (el_len) + sizeof (ST_INT16);
	}
    break;

    case RT_INTEGER:			/* Size is in bytes. */
    case RT_UNSIGNED:
      if (st_size > cur_rt->el_size)	/* If this one is bigger, */
        {					/* set both storage size */
        cur_rt->el_size = 		/* and element length. */
	  cur_rt->u.p.el_len = st_size;
	}
    break;

    case RT_OCTET_STRING:		/* Size is in bytes. */
      if (cur_rt->u.p.el_len < 0)	/* Variable size */
        {
	if (-cur_rt->u.p.el_len < el_len)	/* New one is longer */
	  {
	  cur_rt->u.p.el_len = -el_len;	/* Bump size up */
	  cur_rt->el_size = el_len + sizeof (ST_INT16);
	  }
	}
      else if (cur_rt->u.p.el_len != el_len)	/* Different size */
        {
	if (-cur_rt->u.p.el_len > el_len)	/* If old is bigger, */
	  el_len = -cur_rt->u.p.el_len;	/* keep it, otherwise */
	cur_rt->u.p.el_len = -el_len;		/* use this size. */
	cur_rt->el_size = el_len + sizeof (ST_INT16);
	}
    break;

    case RT_VISIBLE_STRING:		/* Size is in bytes. */
      if (cur_rt->u.p.el_len < 0)	/* Variable len */
        {
	if (cur_rt->u.p.el_len > -el_len)  /* New one is bigger. */
	  cur_rt->u.p.el_len = -el_len;
        }
      else if (cur_rt->u.p.el_len != el_len)	/* Length different */
        {					/* Make variable length */
	if (cur_rt->u.p.el_len > el_len)
	  el_len = cur_rt->u.p.el_len;
	cur_rt->u.p.el_len = -el_len;
	}
      cur_rt->el_size = abs (cur_rt->u.p.el_len) + 1;
    break;

    case RT_UTF8_STRING:		/* Unicode UTF8string */
      /* If new len > old len, force string to be "variable-len" & increase len.*/
      if (abs(el_len) > abs(cur_rt->u.p.el_len))  /* New one is bigger. */
        {
        cur_rt->u.p.el_len = -abs(el_len);	/* increase len	*/
      	/* adjust el_size	*/
#if (UNICODE_LOCAL_FORMAT==UNICODE_UTF8)
        cur_rt->el_size = abs(cur_rt->u.p.el_len)*4 + 1;	/* # bytes (4 bytes per char + NULL)*/
#else		/* assume UNICODE_UTF16	*/
        cur_rt->el_size = abs(cur_rt->u.p.el_len)*2 + 2;	/* # bytes (2 bytes per char + 2 byte NULL)*/
#endif
        }
    break;

    default:				/* Other types are fixed length */
    break;
    }

  return SD_SUCCESS;			/* All OK. */
  }



/************************************************************************/
/*				next_rt_index				*/
/* Sets cur_rt to the correct value for the next data element in	*/
/* the parse.  If the current message level is an unfinished array,	*/
/* it increments the u.arr.num_elmnts member of the header.		*/
/* Parameters:	None.							*/
/* Globals input:  cur_rt, nxt_un_rt, constr_type[]			*/
/* Globals output: cur_rt						*/
/* Other variables changed:  cur_rt->u.arr.num_elmnts			*/
/************************************************************************/

static ST_VOID next_rt_index (ASN1_DEC_CTXT *aCtx)
  {
  if (++cur_rt < nxt_un_rt)	/* Next RT element already defined? */
    return;			/* Return, done. */
	/* The current message level isn't complete yet.  Check type. */
  if (constr_type[aCtx->asn1r_msg_level] == ARRAY) /* This is an unfinished array. */
    {
    cur_rt = rt_blk_start[aCtx->asn1r_msg_level]; /* Loop back to array start */
    cur_rt->u.arr.num_elmnts++; /* Inc # of elements. */
    ++cur_rt;				/* Point to contents. */
    }
	/* Done.  No loopback is required for structures, only arrays. */
  }



/************************************************************************/
/*			   arr_start					*/
/* This is the function called when an array cstr is starting		*/
/************************************************************************/

static ST_VOID arr_start (ASN1_DEC_CTXT *aCtx)
  {
/* initialize the loop counter for the array				*/
  MLOG_CDEC0 ("pars_arr_start");

  if (init_new_RT (aCtx, RT_ARR_START, 0))	/* Return if error. */
    return;
		/* Check to see if the end-contructor for this array	*/
		/* has been encountered before.				*/

  ++arr_loop_level;
  if (cur_rt->u.arr.num_rt_blks == -1)
    {
    aCtx->asn1r_c_done_fun[aCtx->asn1r_msg_level] = arr_done_fun;	/* Init now if new array */
    }
  else	/* We've seen this array before as an element of a larger array.  We */
	/* know the size.  Initialize the loop count in the RT_ARR_END block. */
    {
    arr_loops[arr_loop_level] = cur_rt->u.arr.num_elmnts;
    }


  rt_blk_start[aCtx->asn1r_msg_level] = cur_rt;	/* Point to initial element */
  constr_type[aCtx->asn1r_msg_level] = ARRAY;
  cur_rt++;			/* Increment index (no loopback) */
  assign_tags (aCtx);		/* Assign tags for next data element. */
  }


/************************************************************************/
/*			   arr_done_fun 				*/
/* This is the cstr done function called when an array cstr is done	*/
/* Note: aCtx->asn1r_msg_level is one less than the value given to array_start ().	*/
/************************************************************************/

static ST_VOID arr_done_fun (ASN1_DEC_CTXT *aCtx)
  {
  MLOG_CDEC0 ("pars_arr_done_fun");

  if (rt_blk_start[aCtx->asn1r_msg_level+1]->u.arr.num_rt_blks == -1)
    {			  /* Unfinished array, build RT_ARR_END record. */
    cur_rt = nxt_un_rt;			/* Point to one past end */
    if (init_new_RT (aCtx, RT_ARR_END, 0))	/* Init a new RT block */
      return;
    
    cur_rt->u.arr.num_rt_blks =  /* Calc RT block count for array. */
     rt_blk_start[aCtx->asn1r_msg_level+1]->u.arr.num_rt_blks =
      (cur_rt - rt_blk_start[aCtx->asn1r_msg_level+1] - 1);/* Copy count back to start. */

    cur_rt->u.arr.num_elmnts =		/* Copy element count to */
      rt_blk_start[aCtx->asn1r_msg_level+1]->u.arr.num_elmnts;/* end-array element.*/
    }
  else
    {				/* This is the end of a finished array. */
    if (arr_loops[arr_loop_level]) /* Loop count down to 0? */
      {
      MLOG_NERR0 ("Malformed array data");
      asn1r_set_dec_err (aCtx, BAD_DATA);	/* If not exactly 0, error. */
      }
    }
  --arr_loop_level;

  next_rt_index (aCtx);
  assign_tags (aCtx);		/* setup to get next element		*/
  }

/************************************************************************/
/*			   str_start					*/
/* This is the function called when a struct cstr is starting		*/
/************************************************************************/

static ST_VOID str_start (ASN1_DEC_CTXT *aCtx)
  {

  if (init_new_RT (aCtx, RT_STR_START, 0))
    return;

  if (cur_rt->u.str.num_rt_blks == -1)
    aCtx->asn1r_c_done_fun[aCtx->asn1r_msg_level] = str_done_fun;	/* Init now if new struct */

  rt_blk_start[aCtx->asn1r_msg_level] = cur_rt;	/* Point to initial element */
  constr_type[aCtx->asn1r_msg_level] = STRUCT;
  cur_rt++;			/* point to next runtime element always	*/
  assign_tags (aCtx);		/* setup to get next element		*/
  }

/************************************************************************/
/*			   str_done_fun 				*/
/* This is the cstr done function called when a struct cstr is done	*/
/* Note: aCtx->asn1r_msg_level is one less than the value given to str_start ().	*/
/************************************************************************/

static ST_VOID str_done_fun (ASN1_DEC_CTXT *aCtx)
  {
  MLOG_CDEC0 ("pars_str_done_fun");

  if (init_new_RT (aCtx, RT_STR_END, 0))
    return;

  cur_rt->u.str.num_rt_blks = 	/* Calc RT block count for struct. */
   rt_blk_start[aCtx->asn1r_msg_level+1]->u.str.num_rt_blks =
    (cur_rt - rt_blk_start[aCtx->asn1r_msg_level+1] - 1); /* Copy count back to start. */

  next_rt_index (aCtx);		/* point to next runtime element	*/
  assign_tags (aCtx);		/* setup to get next element		*/
  }


/************************************************************************/
/************************************************************************/
#if 0 	/* Constructed bitstrings and octetstrings not supported 	*/
/************************************************************************/
/************************************************************************/
/*			   get_bstr_cstr				*/
/* State function to extract a boolean value from an asn1 data element. */
/************************************************************************/


static ST_VOID get_bstr_cstr (ASN1_DEC_CTXT *aCtx)		&&& This code not implemented.
  {
  MLOG_CDEC0 ("pars_get_bstr_cstr");

  if (init_new_RT (aCtx, RT_STR_END, 0))
    return;

  aCtx->asn1r_c_done_fun [aCtx->asn1r_msg_level] = bs_cstr_done;

  next_rt_index (aCtx);		/* point to next runtime element	*/
  assign_tags (aCtx);		/* setup to get next element		*/
  }


/************************************************************************/
/*			bs_cstr_done					*/
/* This function is called when an bit string constructor is 		*/
/* completed OK. Need to reset the decode state machine.		*/
/* Note: aCtx->asn1r_msg_level is one less than the value given to get_bstr_cstr ().	*/
/************************************************************************/

static ST_VOID bs_cstr_done (ASN1_DEC_CTXT *aCtx)		&&& Not implemented yet
  {
ST_INT abs_len;
ST_INT abs_count;
int i;
ST_CHAR *to_ptr;
ST_CHAR *from_ptr;

  abs_count = abs (cur_rt->u.p.el_len);
  abs_len = CALC_BIT_LEN (abs_count);

/* determine whether octet string is fixed or variable length		*/

  if (cur_rt->u.p.el_len >=0)	/* fixed length				*/
    {
    if (aCtx->asn1r_bitcount != abs_count)
      {
      MLOG_NERR0 ("Fixed length bit string mismatch");
      asn1r_set_dec_err (aCtx, BAD_DATA);
      }
    }
  else		/* variable length bit string				*/
    {		/* need to move down to allow for size ST_INT16		*/
    from_ptr = datptr+abs_len-1;
    to_ptr = from_ptr+sizeof (ST_INT16);
    for (i = 0; i < abs_count; ++i)
      *(to_ptr--) = *(from_ptr--);

    *((ST_INT16 *)datptr) = (ST_INT16) aCtx->asn1r_bitcount;
    }

  datptr += cur_rt->el_size;	/* Adjust data pointer    		*/
  cur_rt++;			/* point to next runtime element	*/
  assign_tags (aCtx);		/* Assign tags for next data elt*/
  }


/************************************************************************/
/*			   get_ostr_cstr				*/
/* State function to extract an octet string from an asn1 data element. */
/************************************************************************/

static ST_VOID get_ostr_cstr (ASN1_DEC_CTXT *aCtx)
  {
  MLOG_CDEC0 ("pars_get_ostr_cstr");

  aCtx->asn1r_c_done_fun [aCtx->asn1r_msg_level] = os_cstr_done;
  asn1r_get_octstr_cstr (aCtx, abs (cur_rt->u.p.el_len), datptr);
  }

/************************************************************************/
/*			os_cstr_done					*/
/* This function is called when an octet string constructor is 		*/
/* completed OK. Need to reset the decode state machine.		*/
/************************************************************************/

static ST_VOID os_cstr_done (ASN1_DEC_CTXT *aCtx)
  {
ST_INT abs_len;
int i;
ST_CHAR *to_ptr;
ST_CHAR *from_ptr;

  abs_len = abs (cur_rt->u.p.el_len);

/* determine whether octect string is fixed or variable length		*/

  if (cur_rt->u.p.el_len >=0)	/* fixed length				*/
    {
    if (aCtx->asn1r_octetcount != abs_len)
      {
      MLOG_NERR0 ("Fixed length octet string mismatch");
      asn1r_set_dec_err (aCtx, BAD_DATA);
      }
    }
  else		/* variable length octet string				*/
    {		/* need to move down to allow for size ST_INT16		*/
    from_ptr = datptr+abs_len-1;
    to_ptr = from_ptr+sizeof (ST_INT16);
    for (i = 0; i < aCtx->asn1r_octetcount; ++i)
      *(to_ptr--) = *(from_ptr--);

    *((ST_INT16 *)datptr) = (ST_INT16) aCtx->asn1r_octetcount;
    }

  datptr += cur_rt->el_size; /* Adjust data pointer    		*/
  cur_rt++;			/* point to next runtime element	*/
  assign_tags (aCtx);		/* Assign tags for next data elt*/
  }

/************************************************************************/
#endif
/************************************************************************/
/************************************************************************/

/************************************************************************/
/*			   get_bool_prim				*/
/* State function to extract a boolean prim from an asn1 data element.	*/
/************************************************************************/

static ST_VOID get_bool_prim (ASN1_DEC_CTXT *aCtx)
  {
  MLOG_CDEC0 ("pars_get_bool_prim");

  if (init_new_RT (aCtx, RT_BOOL, aCtx->asn1r_elmnt_len))
    return;

  if (aCtx->asn1r_elmnt_len != 1)
    {
    MLOG_NERR0 ("Malformed boolean data");
    asn1r_set_dec_err (aCtx, BAD_DATA);		/* checked by get function	*/
    }
  aCtx->asn1r_field_ptr += aCtx->asn1r_elmnt_len;	/* Skip over data */

  next_rt_index (aCtx);		/* point to next runtime element	*/
  assign_tags (aCtx);		/* setup to get next element		*/
  }

/************************************************************************/
/*			   get_bitstr_prim				*/
/* State function to extract a bitstring prim from an asn1 data element	*/
/************************************************************************/

static ST_VOID get_bitstr_prim (ASN1_DEC_CTXT *aCtx)
  {
ST_INT bit_count;

  MLOG_CDEC0 ("pars_get_bitstr_prim");

  bit_count = (aCtx->asn1r_elmnt_len > 1) ? ((aCtx->asn1r_elmnt_len-1)*8 - (*aCtx->asn1r_field_ptr & 7)) : 0;
  if (!aCtx->asn1r_elmnt_len || (!bit_count && *aCtx->asn1r_field_ptr))
    {
    MLOG_NERR0 ("Malformed bitstring data");
    asn1r_set_dec_err (aCtx, BAD_DATA);
    }

  if (init_new_RT (aCtx, RT_BIT_STRING, bit_count))
    return;

  aCtx->asn1r_field_ptr += aCtx->asn1r_elmnt_len;

  next_rt_index (aCtx);		/* point to next runtime element	*/
  assign_tags (aCtx);
  }

/************************************************************************/
/*			   get_int_prim 				*/
/* State function to extract an integer prim from an asn1 data element. */
/************************************************************************/

static ST_VOID get_int_prim (ASN1_DEC_CTXT *aCtx)
  {
ST_INT el_len;

  MLOG_CDEC0 ("pars_get_int_prim");

  switch (aCtx->asn1r_elmnt_len)	/* determine internal length	*/
    {
    case 1:				/* one byte int		*/
      el_len = 1;
    break;

    case 2:				/* two byte int		*/
      el_len = 2;
    break;

    case 3:				/* three byte int, expand to 4	*/
    case 4:				/* four byte integer	*/
      el_len = 4;
    break;

#ifdef INT64_SUPPORT
    case 5:
    case 6:
    case 7:
    case 8:
      el_len = 8;
    break;
#endif

    default :
      MLOG_NERR0 ("Malformed integer data");
      asn1r_set_dec_err (aCtx, BAD_DATA);     /* no other lengths are valid   	*/
      return;
    }

/* Assume all integers at least 4 bytes (i.e. ST_INT32).	*/
  if (el_len < 4)
    el_len = 4;

  if (init_new_RT (aCtx, RT_INTEGER, el_len))
    return;

  aCtx->asn1r_field_ptr += aCtx->asn1r_elmnt_len;
  next_rt_index (aCtx);		/* point to next runtime element	*/
  assign_tags (aCtx);
  }

/************************************************************************/
/*			   get_uint_prim				*/
/* State function to extract an unsigned integer prim from an asn1 data */
/* element.								*/
/************************************************************************/

static ST_VOID get_uint_prim (ASN1_DEC_CTXT *aCtx)
  {
ST_INT el_len;
ST_INT asn1ElmntLen;

  MLOG_CDEC0 ("pars_get_uint_prim");

/* Unsigned data may have a leading 0x00 if it otherwise would be neg	*/
  asn1ElmntLen = aCtx->asn1r_elmnt_len;
  if (aCtx->asn1r_elmnt_len > 1 &&
      *aCtx->asn1r_field_ptr == 0 && 
      *(aCtx->asn1r_field_ptr+1) & 0x80)
    {
    --asn1ElmntLen;
    }

  switch (asn1ElmntLen)		/* determine internal length	*/
    {
    case 1:				/* one byte */
      el_len = 1;
    break;

    case 2:				/* two bytes */
      el_len = 2;
    break;

    case 3:				/* three bytes, expand to 4	*/
    case 4:				/* four bytes	*/
      el_len = 4;
    break;

#ifdef INT64_SUPPORT
    case 5:
    case 6:
    case 7:
    case 8:
      el_len = 8;
    break;
#endif

    default :
      MLOG_NERR0 ("Malformed unsigned data");
      asn1r_set_dec_err (aCtx, BAD_DATA);     /* no other lengths are valid   	*/
      return;
    }
     
/* Assume all unsigned integers at least 4 bytes (i.e. ST_UINT32).	*/
  if (el_len < 4)
    el_len = 4;

  if (init_new_RT (aCtx, RT_UNSIGNED, el_len))
    return;

  aCtx->asn1r_field_ptr += aCtx->asn1r_elmnt_len;
  next_rt_index (aCtx);		/* point to next runtime element	*/
  assign_tags (aCtx);
  }

/************************************************************************/
/*			   get_float_prim				*/
/* State function to extract a floating point primitive from an asn1	*/
/* data element.							*/
/************************************************************************/

#ifdef FLOAT_DATA_SUPPORT

static ST_VOID get_float_prim (ASN1_DEC_CTXT *aCtx)
  {
  MLOG_CDEC0 ("pars_get_float_prim");

  if (init_new_RT (aCtx, RT_FLOATING_POINT, aCtx->asn1r_elmnt_len))
    return;

  aCtx->asn1r_field_ptr += aCtx->asn1r_elmnt_len;
  next_rt_index (aCtx);		/* point to next runtime element	*/
  assign_tags (aCtx);
  }

#endif

/************************************************************************/
/*			   get_octstr_prim				*/
/* State function to extract an octet string primitive from an asn1	*/
/* data element.							*/
/************************************************************************/

static ST_VOID get_octstr_prim (ASN1_DEC_CTXT *aCtx)
  {
  MLOG_CDEC0 ("pars_get_octstr_prim");

  if (init_new_RT (aCtx, RT_OCTET_STRING, aCtx->asn1r_elmnt_len))
    return;

  aCtx->asn1r_field_ptr += aCtx->asn1r_elmnt_len;
  next_rt_index (aCtx);		/* point to next runtime element	*/
  assign_tags (aCtx);
  }

/************************************************************************/
/*			   get_vstr_prim				*/
/* State function to extract a visible string primitive from an asn1	*/
/* data element.							*/
/************************************************************************/

static ST_VOID get_vstr_prim (ASN1_DEC_CTXT *aCtx)
  {
  MLOG_CDEC0 ("pars_get_vstr_prim");

  if (init_new_RT (aCtx, RT_VISIBLE_STRING, aCtx->asn1r_elmnt_len))
    return;

  aCtx->asn1r_field_ptr += aCtx->asn1r_elmnt_len;
  next_rt_index (aCtx);		/* point to next runtime element*/
  assign_tags (aCtx);
  }

/************************************************************************/
/*			   get_gtime_prim				*/
/* State function to extract a generalized time primitive from an asn1	*/
/* data element.							*/
/************************************************************************/

#ifdef TIME_DATA_SUPPORT

static ST_VOID get_gtime_prim (ASN1_DEC_CTXT *aCtx)
  {
  MLOG_CDEC0 ("pars_get_gtime_prim");

  if (init_new_RT (aCtx, RT_GENERAL_TIME, aCtx->asn1r_elmnt_len))
    return;

  aCtx->asn1r_field_ptr += aCtx->asn1r_elmnt_len;
  next_rt_index (aCtx);		/* point to next runtime element	*/
  assign_tags (aCtx);
  }

#endif

/************************************************************************/
/*			   get_btime_prim				*/
/* State function to extract a binary time primitive from an asn1 data	*/
/* element.								*/
/************************************************************************/

#ifdef BTOD_DATA_SUPPORT

static ST_VOID get_btime_prim (ASN1_DEC_CTXT *aCtx)
  {
  MLOG_CDEC0 ("pars_get_btime_prim");

  if (init_new_RT (aCtx, RT_BINARY_TIME, aCtx->asn1r_elmnt_len))
    return;

  aCtx->asn1r_field_ptr += aCtx->asn1r_elmnt_len;
  next_rt_index (aCtx);		/* point to next runtime element	*/
  assign_tags (aCtx);
  }

#endif

/************************************************************************/
/*			   get_bcd_prim 				*/
/* State function to extract a binary coded decimal primitive from an	*/
/* asn1 data element.							*/
/************************************************************************/

static ST_VOID get_bcd_prim (ASN1_DEC_CTXT *aCtx)
  {

  MLOG_CDEC0 ("pars_get_bcd_prim");

  if (aCtx->asn1r_elmnt_len > 4)
    {
    MLOG_NERR0 ("Malformed bcd data");
    asn1r_set_dec_err (aCtx, BAD_DATA);
    return;
    }

  if (init_new_RT (aCtx, RT_BCD, aCtx->asn1r_elmnt_len))
    return;

  aCtx->asn1r_field_ptr += aCtx->asn1r_elmnt_len;	/* Adjust data pointer    		*/
  next_rt_index (aCtx);		/* point to next runtime element	*/
  assign_tags (aCtx);
  }

/************************************************************************/
/*			   get_utc_time_prim				*/
/* State function to extract a utc time primitive from an asn1 data	*/
/* element.								*/
/************************************************************************/

static ST_VOID get_utc_time_prim (ASN1_DEC_CTXT *aCtx)
  {
  MLOG_CDEC0 ("pars_get_utc_time_prim");

  if (init_new_RT (aCtx, RT_UTC_TIME, aCtx->asn1r_elmnt_len))
    return;

  aCtx->asn1r_field_ptr += aCtx->asn1r_elmnt_len;
  next_rt_index (aCtx);		/* point to next runtime element	*/
  assign_tags (aCtx);
  }

/************************************************************************/
/*			   get_utf8_prim				*/
/* State function to extract a Unicode UTF8string primitive from an	*/
/* asn1 data element.							*/
/* Assume string is "variable-length", so pass "negative" len. The len	*/
/* is the number of Unicode characters. Each Unicode character takes	*/
/* at least 1 byte, so the number of characters is ALWAYS <=		*/
/* the ASN.1 encoded size.						*/
/************************************************************************/

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

  /* Pass "negative" len to indicate "variable-length".		*/
  if (init_new_RT (aCtx, RT_UTF8_STRING, -aCtx->asn1r_elmnt_len))
    return;

  aCtx->asn1r_field_ptr += aCtx->asn1r_elmnt_len;
  next_rt_index (aCtx);		/* point to next runtime element	*/
  assign_tags (aCtx);
  }

/************************************************************************/
/*			err_clean_fun 					*/
/************************************************************************/


static ST_VOID err_clean_fun (ASN1_DEC_CTXT *aCtx, ST_RET err_code)
  {
  if (local_alloc_flag)
    {
    chk_free (rt_base);
    rt_base = NULL;
    }
  nxt_un_rt = rt_base;	/* Zero length runtime buffer. */
  }


