/************************************************************************/
/* SISCO SOFTWARE MODULE HEADER *****************************************/
/************************************************************************/
/*   (c) Copyright Systems Integration Specialists Company, Inc.,	*/
/*      	  1986 - 2002, All Rights Reserved.		        */
/*									*/
/*		    PROPRIETARY AND CONFIDENTIAL			*/
/*									*/
/* MODULE NAME : mms_rtaa.c						*/
/* PRODUCT(S)  : MMSEASE						*/
/*									*/
/* MODULE DESCRIPTION : 						*/
/* Function to decode ASN.1 type definitions into Runtime typedefs	*/
/*									*/
/* GLOBAL FUNCTIONS DEFINED IN THIS MODULE :				*/
/*			_ms_mk_rt_aa_all				*/
/*			_ms_mk_rt_aa					*/
/*			_ms_get_rtaa_data_size				*/
/*			_ms_log_runtime_aa				*/
/*									*/
/* MODIFICATION LOG :							*/
/*  Date     Who   Rev			Comments			*/
/* --------  ---  ------   -------------------------------------------	*/
/* 07/27/05  MDE     06    if m_max_rt_aa_cltr == 0 calc required size  */
/* 05/11/05  MDE     05    Added _ms_calc_rt_aa_size			*/
/* 02/22/05  JRB     04    Make "sp" local to avoid VXWORKS conflict.	*/
/* 04/02/03  JRB     03    Add UTF8string support (see RT_UTF8_STRING).	*/
/* 03/04/02  JRB     02    Add more "SD_CONST RUNTIME_TYPE *".		*/
/*			   Make sure str_end_rtaa, arr_end_rtaa init'd.	*/
/* 02/06/02  JRB     01    New. Code from mms_alta.c moved here.	*/
/************************************************************************/

#include "glbtypes.h"
#include "sysincs.h"
#include "glbsem.h"

#include "mmsdefs.h"
#include "mms_pvar.h"
#include "mms_vvar.h"
#include "asn1defs.h"

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

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

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

#define COPY_RTAA(dest,src) { dest->rt = src->rt;\
                              dest->offset_to_data = src->offset_to_data; }

/************************************************************************/
/************************************************************************/
/* This code is required for handling real alternate access data 	*/
/* conversion via mmsdataa, and for using the ARB DATA handlers.	*/

/************************************************************************/
/************************************************************************/
/* Runtime AA (RTAA) Handling						*/
/************************************************************************/
/************************************************************************/
static ST_VOID _ms_calc_rt_aa_size (SD_CONST RUNTIME_TYPE *rt_head,  ST_INT rt_num, 
                       	    ST_INT *num_aa_out);

static ST_RET get_struct_comps (AA_APPLY_CTRL *aa_app, RT_AA_CTRL *w_rtaa);
static ST_RET get_arr_elmnts (AA_APPLY_CTRL *aa_app, RT_AA_CTRL *w_rtaa);
static ST_INT count_num_comp (AA_APPLY_CTRL *aa_app, 
                      ALT_ACC_EL *aa, 
                      ST_INT *count_out);

static RT_AA_CTRL *find_end_rtaa (AA_APPLY_CTRL *aa_app,
                                  RT_AA_CTRL *start_rtaa);

static RT_AA_CTRL *find_named_comp (AA_APPLY_CTRL *aa_app, 
                                    ST_CHAR *name, 
                                    RT_AA_CTRL *w_rtaa);

static ST_RET get_component_elmnts (AA_APPLY_CTRL *aa_app, 
                            RT_AA_CTRL *rtaa);

static RT_AA_CTRL *find_arr_el (AA_APPLY_CTRL *aa_app, 
                                RT_AA_CTRL *w_rtaa, 
                                ST_INT el_index);
static ST_VOID ms_rtaa_el_size_calc (RT_AA_CTRL *runtime_aa, ST_INT num_rt_aa);

static ST_INT arr_loop_level;
static ST_INT *arr_loops;

/************************************************************************/
/* Create a RTAA with all elements for the specified RT type            */
/* This table is allocated via chk_calloc, and is sized for the max     */
/* possible AA size. If desired, this may be realloc'd to the correct   */
/* size by the caller.                                                  */

ST_RET _ms_mk_rt_aa_all (SD_CONST RUNTIME_TYPE *rt_head,  ST_INT rt_num, 
                       RT_AA_CTRL **rt_aa_out, ST_INT *num_aa_out)
  {
RT_AA_CTRL *rtaa;
ST_INT data_offset;
SD_CONST RUNTIME_TYPE *rt_end;
SD_CONST RUNTIME_TYPE *rt;
ST_INT arr_loop_buf[ASN1_MAX_LEVEL];
ST_INT _max_rt_aa_ctrl;

  S_LOCK_COMMON_RESOURCES ();

  arr_loop_level = 0;
  arr_loops = arr_loop_buf;

/* Allocate the output array for max size                               */
  _max_rt_aa_ctrl = m_max_rt_aa_ctrl;
  if (_max_rt_aa_ctrl == 0)
    _ms_calc_rt_aa_size (rt_head,  rt_num, &_max_rt_aa_ctrl);
    
  rtaa = (RT_AA_CTRL *) chk_calloc (_max_rt_aa_ctrl, sizeof (RT_AA_CTRL));
  *rt_aa_out = rtaa;
  *num_aa_out = 0;

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

/* Traverse the RT type as though we were doing a ASN.1 to LOCAL, and	*/
/* set the RTAA RT pointer and offset as each element is encountered.	*/

  while (rt < rt_end)
    {
    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; 
        }
      else
        --arr_loop_level;
      }

    (*num_aa_out)++;			/* Count this one		*/
    rtaa->rt = rt;			/* Set the RT pointer		*/
    rtaa->offset_to_data = data_offset; /* Set the offset		*/
    data_offset += rt->el_size;		/* Offset for next		*/

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

    rtaa++;			/* point to next runtime AA element	*/
    rt++;			/* point to next runtime element	*/
    }

/* Post process to calculate the 'rtaa->el_size' values (for packed 	*/
/* local representation).						*/
  ms_rtaa_el_size_calc (*rt_aa_out, *num_aa_out);

  if (mms_debug_sel & MMS_LOG_RTAA)
    _ms_log_runtime_aa (*rt_aa_out, *num_aa_out);

/* save the high water mark						*/
  if (*num_aa_out >  m_hw_rt_aa_ctrl)
    m_hw_rt_aa_ctrl = *num_aa_out;

  S_UNLOCK_COMMON_RESOURCES ();
  return (SD_SUCCESS);
  }

/************************************************************************/
/************************************************************************/
/*			_ms_calc_rt_aa_size				*/
/************************************************************************/

static ST_VOID _ms_calc_rt_aa_size (SD_CONST RUNTIME_TYPE *rt_head,  ST_INT rt_num, 
                       	    ST_INT *num_aa_out)
  {
SD_CONST RUNTIME_TYPE *rt_end;
SD_CONST RUNTIME_TYPE *rt;
ST_INT arr_loop_buf[ASN1_MAX_LEVEL];
ST_INT num_aa;

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

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

/* Traverse the RT type as though we were doing a ASN.1 to LOCAL, and	*/
/* count the RTAA elements						*/
  while (rt < rt_end)
    {
    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; 
        }
      else
        --arr_loop_level;
      }
    ++num_aa;				/* Count this one		*/

    if (rt->el_tag == RT_ARR_START)	/* Set up array looping		*/
      {
      ++arr_loop_level;
      arr_loops[arr_loop_level] = rt->u.arr.num_elmnts;
      }
    rt++;			/* point to next runtime element	*/
    }
  *num_aa_out = num_aa;
  S_UNLOCK_COMMON_RESOURCES ();
  }

/************************************************************************/
/************************************************************************/
/************************************************************************/
/* Create a RTAA with elements selected by a AA for the specified RT 	*/
/* type            							*/
/* This table is allocated via chk_calloc, and is sized for the max     */
/* possible AA size. If desired, this may be realloc'd to the correct   */
/* size by the caller.                                                  */

ST_RET _ms_mk_rt_aa (SD_CONST RUNTIME_TYPE *rt,  ST_INT rt_num, 
                   ALT_ACCESS *alt_acc,
                   RT_AA_CTRL **rt_aa_out, ST_INT *num_aa_out)
  {
ALT_ACC_EL *aa;
ST_INT num_aa;
ST_RET ret;
ST_INT num_all_rtaa;
RT_AA_CTRL *all_rtaa;
RT_AA_CTRL *rtaa;
AA_APPLY_CTRL aa_app_ctrl;
ST_UINT32 mms_log_mask_save;
ST_INT _max_rt_aa_ctrl;

  S_LOCK_COMMON_RESOURCES ();

  aa = alt_acc->aa;
  num_aa = alt_acc->num_aa;

/* Make the 'all' rtaa - has all elements expanded, with offsets.      	*/
/* This is used with the AA to select the elements for the new RTAA	*/

/* Don't allow logging of the 'all' runtime aa table                  	*/
  mms_log_mask_save = mms_debug_sel;
  mms_debug_sel &= ~MMS_LOG_RTAA;
  ret = _ms_mk_rt_aa_all (rt, rt_num, &all_rtaa, &num_all_rtaa);
  mms_debug_sel = mms_log_mask_save;

  if (ret)
    {
    S_UNLOCK_COMMON_RESOURCES ();
    return (ret);
    }

/* Allocate the output (new) RT AA table                                */
  _max_rt_aa_ctrl = m_max_rt_aa_ctrl;
  if (_max_rt_aa_ctrl == 0)
    _ms_calc_rt_aa_size (rt,  rt_num, &_max_rt_aa_ctrl);
    
  rtaa = (RT_AA_CTRL *) chk_calloc (_max_rt_aa_ctrl, sizeof (RT_AA_CTRL));
  *rt_aa_out = rtaa;
  *num_aa_out = 0;

/* Initialize the AA process control structure components                */

/* Alternate Access     */
  aa_app_ctrl.num_aa = num_aa;
  aa_app_ctrl.aa_head = aa;
  aa_app_ctrl.aa_end = aa + num_aa - 1;
  aa_app_ctrl.curr_aa = aa; 

/* Default RTAA         */
  aa_app_ctrl.num_all_rtaa = num_all_rtaa;
  aa_app_ctrl.all_rtaa_head = all_rtaa;
  aa_app_ctrl.all_rtaa_end = all_rtaa + (num_all_rtaa -1);

/* New RTAA                                                             */
  aa_app_ctrl.num_rtaa = 0;
  aa_app_ctrl.rtaa_head = rtaa;
  aa_app_ctrl.curr_rtaa = rtaa;

/* Nest Level */
  aa_app_ctrl.nest = 0;

/* Get the appropriate rt_aa_ctrl elements                              */
/* Start by getting the first nest level elements, based on the RT.	*/
/* Note that these functions will call recursively to get handle 	*/
/* further nest	levels							*/

  if (rt->el_tag == RT_ARR_START)
    {
    ret = get_arr_elmnts (&aa_app_ctrl, all_rtaa);
    }
  else if (rt->el_tag == RT_STR_START)
    {
    ret = get_struct_comps (&aa_app_ctrl, all_rtaa);
    }
  else
    {
    MLOG_NERR0 ("Runtime Type must be a Structure or Array for AA");
    ret = MVE_RT_TYPE;
    }

/* If successful, set the output AA count and log if enabled		*/
  if (ret == SD_SUCCESS)
    {
    *num_aa_out = aa_app_ctrl.num_rtaa;

/* Post process to calculate the 'rtaa->el_size' values (for packed 	*/
/* local representation).						*/
    ms_rtaa_el_size_calc (*rt_aa_out, *num_aa_out);

    if (mms_debug_sel & MMS_LOG_RTAA)
      _ms_log_runtime_aa (*rt_aa_out, *num_aa_out);

/* save the high water mark						*/
    if (*num_aa_out >  m_hw_rt_aa_ctrl)
      m_hw_rt_aa_ctrl = *num_aa_out;
    }
  else
    {
    chk_free (rtaa);		/* If error, need to free the RTAA	*/
    }

  chk_free (all_rtaa);		/* we are done with the 'all' RTAA	*/
  S_UNLOCK_COMMON_RESOURCES ();

  return (ret);
  }

/************************************************************************/
/* We are working on a structure level, and need to copy the selected   */
/* RTAA components for this nest level. This function can be called     */
/* recursively as further nesting levels are encountered.               */
/* The 'w_rtaa' parameter is the 'working' RTAA, and is used to select 	*/
/* the scope (nest) for the operation					*/

static ST_RET get_struct_comps (AA_APPLY_CTRL *aa_app, RT_AA_CTRL *w_rtaa)
  {
ST_RET ret;
RT_AA_CTRL *o_rtaa;
ALT_ACC_EL *aa;
ST_INT num_str_comp;
RT_AA_CTRL *start_rtaa;
RT_AA_CTRL *str_end_rtaa = NULL;
ST_BOOLEAN done;

/* get working pointers                                                 */
  aa = aa_app->curr_aa;                         /* Current AA           */
  o_rtaa = aa_app->curr_rtaa;                   /* Output RTAA          */

/* Check to see if we need to have the outer structure                  */
/* We need this if there are more than 1 named components selected      */

  if (count_num_comp (aa_app, aa, &num_str_comp))
    return (MVE_AA_SELECT);

  if (num_str_comp != 1)
    {   
    str_end_rtaa = find_end_rtaa (aa_app, w_rtaa);
    if (!str_end_rtaa)
      return (MVE_AA_SELECT);

    /* Write the start component of the output rtaa                     */
    COPY_RTAA (o_rtaa,w_rtaa);
    aa_app->num_rtaa++;
    aa_app->curr_rtaa++;
    }

/* Get all selected named components                                  	*/
  ret = SD_SUCCESS;
  done = SD_FALSE;
  while (!done && ret == SD_SUCCESS)
    {
    aa = aa_app->curr_aa;		/* get current AA pointer	*/
    o_rtaa = aa_app->curr_rtaa;		/* get the output RTAA pointer	*/

  /* This AA sel type can be a named component (w/wo further nesting)	*/
  /* or a 'end nest' element.						*/
    switch (aa->sel_type)
      {
      case  AA_COMP :
	/* This AA selects a entire named component. Just find the 	*/
	/* component name in the ALL RTAA and copy it's RTAA elements	*/

        start_rtaa = find_named_comp (aa_app, aa->u.component, w_rtaa);
        if (!start_rtaa)
          {
          return (MVE_AA_SELECT);
          }
        ret = get_component_elmnts (aa_app, start_rtaa);
        ++aa_app->curr_aa;
      break;
  
      case  AA_COMP_NEST  :
	/* This AA selects a named component, with further selection. 	*/
	/* Find the component name in the ALL RTAA and call recursively	*/
	/* to get the components.					*/

        /* This component must be a structure or an array, so that we 	*/
	/* need to call recursively to get the selected RT AA elements	*/

        start_rtaa = find_named_comp (aa_app, aa->u.component, w_rtaa);
        if (!start_rtaa)
          return (MVE_AA_SELECT);

        ++aa_app->nest;		/* Increment the nest level		*/
				/* Point to the next AA 		*/
        if (aa_app->curr_aa < aa_app->aa_end)
          ++aa_app->curr_aa;
        else
          {
          MLOG_NERR0 ("AA table too small");
          return (MVE_AA_SELECT);
          }

	/* Based on the RT type, get the next level with the scope 	*/
	/* defined by the start RTAA.					*/
        if (start_rtaa->rt->el_tag == RT_ARR_START)
          {
          ret = get_arr_elmnts (aa_app, start_rtaa);
          }
        else if (start_rtaa->rt->el_tag == RT_STR_START)
          {
          ret = get_struct_comps (aa_app, start_rtaa);
          }
        else
          {
          MLOG_NERR0 ("Incorrect RT Type for further nesting");
          return (MVE_RT_TYPE);
          }
        --aa_app->nest;		/* Decrement the nest level		*/

       /* curr_aa should now point to our END_NEST, consume it		*/
        if (aa_app->curr_aa->sel_type != AA_END_NEST)
          {
          MLOG_NERR0 ("Required AA_END_NEST not present");
          return (MVE_AA_SELECT);
          }
        else			/* Consume the AA_END_NEST		*/
          ++aa_app->curr_aa;
      break;

      case  AA_END_NEST :
        done = SD_TRUE;            /* This level is complete               */
      break;

      case  AA_ALL :
      case  AA_ALL_NEST   :
      case  AA_INDEX_NEST :
      case  AA_INDEX_RANGE_NEST :
      case  AA_INDEX :
      case  AA_INDEX_RANGE :
        MLOG_NERR0 ("Incorrect AA sel_type for structure component selection");
        return (MVE_AA_SELECT);
      break;

      default :
        MLOG_NERR0 ("Invalid AA sel_type");
        return (MVE_AA_SELECT);
      break;
      }

  /* Prepare to look at next AA, if this was not the last */    
    if (aa_app->curr_aa > aa_app->aa_end)
      done = SD_TRUE;
    } /* End while getting named components at this level     */


/* Don't continue if an error was detected				*/
  if (ret)
    return (ret);


/* Now copy the structure end element if required                       */
  if (num_str_comp != 1)
    {   
    o_rtaa = aa_app->curr_rtaa;
    COPY_RTAA (o_rtaa, str_end_rtaa);
    aa_app->num_rtaa++;
    aa_app->curr_rtaa++;
    }

  return (ret);
  }

/************************************************************************/
/* We are working on a array selection, and need to copy the selected   */
/* RTAA components for this selection. This function can be called      */
/* recusively as further nesting levels are encountered.                */
/* The 'w_rtaa' parameter is the 'working' RTAA, and is used to select 	*/
/* the scope (nest) for the operation					*/


static ST_RET get_arr_elmnts (AA_APPLY_CTRL *aa_app, RT_AA_CTRL *w_rtaa)
  {
ST_RET ret;
ST_INT i;
RT_AA_CTRL *el_rtaa;
RT_AA_CTRL *o_rtaa;
RT_AA_CTRL *arr_end_rtaa = NULL;
ST_INT nest;
ALT_ACC_EL *aa;
ST_INT num_el;
ST_INT start_el;
ST_INT curr_el;


  ret = SD_SUCCESS;

/* get working pointers                                                 */
  aa = aa_app->curr_aa;                         /* Current AA           */
  o_rtaa = aa_app->curr_rtaa;                   /* Output RTAA          */

  /* This AA sel type must be one of the array selection types, 	*/
  /* (w/wo further nesting)						*/

  /* Start by determining the number of elements and the start element	*/
  switch (aa->sel_type)
    {
    case  AA_INDEX :
    case  AA_INDEX_NEST :
      num_el = 1;
      start_el = (ST_INT) aa->u.index;
    break;
  
    case  AA_INDEX_RANGE :
    case  AA_INDEX_RANGE_NEST :
      num_el = (ST_INT) aa->u.ir.num_elmnts;
      start_el = (ST_INT) aa->u.ir.low_index;
      if (!num_el && !start_el)
        {
        num_el = w_rtaa->rt->u.arr.num_elmnts;
        start_el = 0;
        }
    break;

    case  AA_ALL :
    case  AA_ALL_NEST   :
      num_el = w_rtaa->rt->u.arr.num_elmnts;
      start_el = 0;
    break;
  
    case  AA_COMP :
    case  AA_COMP_NEST  :
      MLOG_NERR0 ("Invalid AA Type");
      return (MVE_AA_SELECT);
    break;
  
    default :
      MLOG_NERR0 ("Bad AA Type");
      return (MVE_AA_SELECT);
    break;
    }

/* num_el is the number of elements to get, start_el is the base index  */
/* w_rtaa points to the start of the array in the default RTAA          */

/* Determine whether the array elements have further AA select ...      */
  if (aa->sel_type == AA_INDEX_NEST ||
      aa->sel_type == AA_INDEX_RANGE_NEST ||
      aa->sel_type == AA_ALL_NEST)
    {
    nest = SD_TRUE;
    }
  else
    nest = SD_FALSE;


/* Check to see if we need to have the outer array                      */
/* We need this if there are more than 1 element selected               */

  if (num_el != 1)
    {   
    arr_end_rtaa = find_end_rtaa (aa_app, w_rtaa);
    if (!arr_end_rtaa)
      return (MVE_AA_SELECT);

    /* Write the start component of the output rtaa                     */
    COPY_RTAA (o_rtaa,w_rtaa);
    aa_app->num_rtaa++;
    aa_app->curr_rtaa++;
    }

/* Now we work through the array elements, getting them one at a time   */
  curr_el = start_el;
  for (i = 0; i < num_el && ret == SD_SUCCESS; ++i, ++curr_el)
    {
    aa_app->curr_aa = aa;            /* Restore the working AA position */
    o_rtaa = aa_app->curr_rtaa;      /* Output RTAA                     */
  
    /* Move the working RTAA pointer to the next element to be selected */
    el_rtaa = find_arr_el (aa_app, w_rtaa, curr_el);
    if (!el_rtaa)
      return (MVE_AA_SELECT);

    /* If further selection is to take place, do it                    	*/
    if (nest)
      {
      /* Set up working parameters for the nest level of nesting    	*/
      if (aa_app->curr_aa < aa_app->aa_end)
        ++aa_app->curr_aa;
      else
        {
        MLOG_NERR0 ("AA table too small");
        return (MVE_AA_SELECT);
        }
      ++aa_app->nest;		/* Increment the nest level		*/

      if (el_rtaa->rt->el_tag == RT_ARR_START)
        {
        ret = get_arr_elmnts (aa_app, el_rtaa);
        }
      else if (el_rtaa->rt->el_tag == RT_STR_START)
        {
        ret = get_struct_comps (aa_app, el_rtaa);
        }
      else 		/* Whoops, internal error			*/
        {
        MLOG_NERR0 ("Bad RT Type for further AA selection");
        return (MVE_RT_TYPE);
        }
      --aa_app->nest;		/* Decrement the nest level		*/
      }
    else	/* No further selection, copy the compoment		*/
      {
      ret = get_component_elmnts (aa_app, el_rtaa);
      }
    } /* end for each selected element */
  

/* Now copy the array end element if required                           */
  if (ret == SD_SUCCESS && num_el != 1)
    {   
    o_rtaa = aa_app->curr_rtaa;
    COPY_RTAA (o_rtaa, arr_end_rtaa);
    aa_app->num_rtaa++;
    aa_app->curr_rtaa++;
    }

  if (nest)
    {
   /* curr_aa should now point to our END_NEST, consume it		*/
    if (aa_app->curr_aa->sel_type != AA_END_NEST)
      {
      MLOG_NERR0 ("Required AA_END_NEST not present");
      ret = MVE_AA_SELECT;
      }
    else 			/* Consume the AA_END_NEST		*/
      ++aa_app->curr_aa;
    }
  else
    ++aa_app->curr_aa; 	/* Point to the next AA 		*/

  return (ret);
  }

/************************************************************************/
/************************************************************************/
/* Given the array or structure start RTAA pointer, return a pointer to */
/* the corresponding end element                                        */

static RT_AA_CTRL *find_end_rtaa (AA_APPLY_CTRL *aa_app,
                                         RT_AA_CTRL *start_rtaa)
  {
SD_CONST RUNTIME_TYPE *start_rt;
SD_CONST RUNTIME_TYPE *end_rt;
ST_INT num_rt;
RT_AA_CTRL *rtaa;

  start_rt = start_rtaa->rt;
  if (start_rt->el_tag == RT_ARR_START)
    {
    num_rt = start_rt->u.arr.num_rt_blks;
    }
  else if (start_rt->el_tag == RT_STR_START)
    {
    num_rt = start_rt->u.str.num_rt_blks;
    }
  else
    {
    MLOG_NERR0 ("Bad RT Type");
    return (NULL);
    }

  end_rt = start_rt + (num_rt + 1);

/* Just a trivial sanity check */
  if (end_rt->el_tag != RT_ARR_END &&
      end_rt->el_tag != RT_STR_END)
    {
    MLOG_NERR0 ("Bad RT Type");
    return (NULL);
    }

/* Now find the RTAA that has a reference to the end structure          */
/* within this scope (we hope!)                                         */

  for (rtaa = start_rtaa; rtaa <= aa_app->all_rtaa_end; ++rtaa)
    {
    if (rtaa->rt == end_rt)
      return (rtaa);
    }

  MLOG_NERR0 ("End RT Not Found");
  return (NULL);
  }

/************************************************************************/
/* Copy the current RTAA element, including any sub-elements            */

static ST_RET get_component_elmnts (AA_APPLY_CTRL *aa_app, RT_AA_CTRL *rtaa)
  {
RT_AA_CTRL *last_rtaa;
RT_AA_CTRL *dest_rtaa;

  if (rtaa->rt->el_tag == RT_ARR_START ||
      rtaa->rt->el_tag == RT_STR_START)
    {
    last_rtaa = find_end_rtaa (aa_app, rtaa);
    if (!last_rtaa)
      return (MVE_AA_SELECT);
    }
  else                          /* Simple element, just copy one        */
    last_rtaa = rtaa;

  dest_rtaa = aa_app->curr_rtaa;
  while (rtaa <= last_rtaa)
    {
    COPY_RTAA (dest_rtaa, rtaa);
    aa_app->num_rtaa++;
    aa_app->curr_rtaa++;
    ++dest_rtaa;
    ++rtaa;
    }
  return (SD_SUCCESS);
  }

/************************************************************************/
/************************************************************************/
/* Count the number of named components selected at this level          */
/* by the alternate access select                                       */

static ST_INT count_num_comp (AA_APPLY_CTRL *aa_app, 
                             ALT_ACC_EL *aa, 
                             ST_INT *count_out)
  {
ST_INT count;
ST_INT nest;
ST_INT done;

  done = SD_FALSE;
  nest = 0;
  count = 0;
  while (done == SD_FALSE)
    {
    switch (aa->sel_type)
      {
      case  AA_COMP :
        if (nest == 0)
          ++count;
      break;

      case  AA_COMP_NEST  :
        if (nest == 0)
          ++count;
        nest++;
      break;

      case  AA_INDEX :
      case  AA_INDEX_RANGE :
      case  AA_ALL :
      break;

      case  AA_INDEX_NEST :
      case  AA_INDEX_RANGE_NEST :
      case  AA_ALL_NEST   :
        nest++;     
      break;
    
      case  AA_END_NEST   :
        if (--nest < 0)
          done = SD_TRUE;
      break;
    
      default :
        MLOG_NERR0 ("Bad AA Type");
        return (MVE_AA_SELECT);
      break;
      }
                        
    if (aa >= aa_app->aa_end)   /* If we are done (level 0 only)        */
      done = SD_TRUE;

    aa++;                       /* Point to next                        */
    }

  *count_out = count;
  return (SD_SUCCESS);
  }


/************************************************************************/
/* Given a RTAA pointer, find a named component at the current nest     */
/* The 'w_rtaa' parameter is the 'working' RTAA, and is used to select 	*/
/* the scope (nest) for the operation					*/

static RT_AA_CTRL *find_named_comp (AA_APPLY_CTRL *aa_app, 
                                           ST_CHAR *name, 
                                           RT_AA_CTRL *w_rtaa)
  {
ST_INT nest;
ST_INT err;

  if (w_rtaa->rt->el_tag != RT_STR_START)
    {
    MLOG_NERR0 ("Bad RT Type - must be structure start");
    return (NULL);
    }
  w_rtaa++;

  nest = 0;
  err = SD_FALSE;
  while (!err)
    {
    if (nest == 0 &&
        !strcmp (name, ms_comp_name_find (w_rtaa->rt)))
      {
      return (w_rtaa);
      }
    switch (w_rtaa->rt->el_tag)
      {
      case  RT_ARR_START      :
      case  RT_STR_START      :
        nest++;
      break;

      case  RT_STR_END 	      :
      case  RT_ARR_END 	      :
        if (--nest < 0)
          break;
      break;

      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_UTC_TIME       :
      case  RT_BOOLEANARRAY   :
      case  RT_UTF8_STRING :
      break;

      default :
        err = SD_TRUE;
        MLOG_NERR0 ("Bad RT Type");
      break;
      }

    w_rtaa++;
    if (w_rtaa >= aa_app->all_rtaa_end)
      break;
    }

  MLOG_NERR1 ("AA Named component (%s) not found", name);
  return (NULL);
  }


/************************************************************************/
/************************************************************************/
/* Given a 'working' RTAA pointer to the RT_ARR_START element and the   */
/* element index, return a pointer to the start RTAA for the element    */
/* The 'w_rtaa' parameter is the 'working' RTAA, and is used to select 	*/
/* the scope (nest) for the operation					*/

static RT_AA_CTRL *find_arr_el (AA_APPLY_CTRL *aa_app, 
                                       RT_AA_CTRL *w_rtaa, 
                                       ST_INT el_index)
  {
RT_AA_CTRL *ret_rtaa; 
SD_CONST RUNTIME_TYPE *arr_rt;
SD_CONST RUNTIME_TYPE *start_rt;
ST_INT el;

  arr_rt = w_rtaa->rt;
  if (arr_rt->el_tag != RT_ARR_START)
    {
    MLOG_NERR0 ("RTAA is not RT_ARR_START");
    return (NULL);
    }
  if (el_index >= arr_rt->u.arr.num_elmnts)
    {
    MLOG_NERR0 ("Index out of range");
    return (NULL);
    }

/* Now we need to locate the el_index'th element. To do this, we first	*/
/* get a pointer to the 0'th element's first RT block, then search for 	*/
/* the el_index'th occurance of the RT within the RTAA 			*/

  w_rtaa++;				 /* 0'th element		*/
  start_rt = w_rtaa->rt;		 /* Start RT block		*/
  el = 0;
  while (w_rtaa <= aa_app->all_rtaa_end) /* while still in RTAA		*/
    {
    if (w_rtaa->rt == start_rt)		 /* if start of an element	*/
      {
      if (el == el_index)		 /* if selected element		*/
        return (w_rtaa);		 /* we found it			*/
      else
        ++el;				 /* maybe next time		*/
      }
    w_rtaa++;				 /* look at next RTAA		*/
    }

  ret_rtaa = (w_rtaa+1) + (el_index * arr_rt->u.arr.num_rt_blks);
  return (ret_rtaa);
  }


/************************************************************************/
/* 			ms_rtaa_el_size_calc 				*/
/************************************************************************/

/* Given a RTAA table, calculate the 'el_size' for each element. This	*/
/* is done by using the alignment control table, and is used only for	*/
/* encoding/decoding AA data that is 'packed' in local memory.		*/

static ST_VOID ms_rtaa_el_size_calc (RT_AA_CTRL *runtime_aa, 
			          ST_INT num_rt_aa)
  {
ST_INT ellen;
SD_CONST RUNTIME_TYPE *rt; 
RT_AA_CTRL *rtaa_above; 
ST_INT curr_offset;
ST_INT pad;
ST_INT algn;
ST_INT i;

  curr_offset = 0;		/* Offset from start of data		*/
  rtaa_above = NULL; 
  for (i = 0; i < num_rt_aa; ++i, ++runtime_aa)
    {
    rt = runtime_aa->rt;

    /* Get 'algnment index' and size of the element (excluding padding)	*/
    _ms_m_get_rt_info (rt, &algn, &ellen);

    /* Set the base element size, before padding   			*/
    runtime_aa->el_size = ellen;

    /* Check to see if padding on the element above is required for 	*/
    /* alignment for this element, add it if so.		  	*/
    /* Increment the offset until no bits are set that shouldn't be	*/
    pad = 0;
    while (curr_offset & algn)
      {
      curr_offset++;
      pad++;
      }

    if (pad)
      {
      if (!rtaa_above) /* First element better not require alignment 	*/
        {
        MLOG_ERR0 ("Alignment table error");
        }
      else	/* add pad to element above */
        rtaa_above->el_size += pad;
      }

  /* Save the pointer to the previous RT element (not this one's name)	*/
    rtaa_above = runtime_aa; 

    /* Point past the current element 		 			*/
    curr_offset += ellen;
    }
  }

/************************************************************************/
/*			_ms_get_rtaa_data_size				*/
/************************************************************************/

ST_INT _ms_get_rtaa_data_size (RT_AA_CTRL *runtime_aa, 
			       ST_INT num_rt_aa)
  {
ST_INT i;
ST_INT size;

  size = 0; 
  for (i = 0; i < num_rt_aa; ++i, ++runtime_aa)
    size += runtime_aa->el_size;
  return (size);
  }

/************************************************************************/
/************************************************************************/
/* LOGGING FUNCTIONS							*/
/************************************************************************/
/************************************************************************/
/************************************************************************/
/*			_ms_log_runtime_aa				*/
/************************************************************************/

ST_VOID _ms_log_runtime_aa (RT_AA_CTRL *rt_aa, ST_INT num_aa)
  {
#ifdef DEBUG_SISCO
SD_CONST RUNTIME_TYPE *rt;

ST_INT i;
ST_INT indent;
SD_CONST ST_CHAR *comp_name;
/* Use these strings to keep logging aligned and indented as desired	*/
#define MAX_INDENT 5
SD_CONST ST_CHAR *sp[MAX_INDENT] =
  {
  "  ",
  "    ",
  "      ",
  "        ",
  "          ",
  };
SD_CONST ST_CHAR *sp2[MAX_INDENT] =
  {
  "        ",
  "      ",
  "    ",
  "  ",
  "",
  };

  S_LOCK_COMMON_RESOURCES ();
  indent = 0;
  MLOG_ALWAYS1 ("RTAA : %d elements", num_aa);

  for (i = 0; i < num_aa; ++i, rt_aa++)
    {
    rt = rt_aa->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_aa->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_aa->el_size,
                                       rt->u.arr.num_elmnts,
                                       rt->u.arr.num_rt_blks);
      break;

      case RT_STR_START :
	MLOG_CALWAYS3 ("%sStruct start%s: size %02d", 
				       	sp[indent],
                                       	sp2[indent],
					rt_aa->el_size);
        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_aa->el_size);
      break;

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

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

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

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

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

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

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

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

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

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

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

      case RT_UTF8_STRING :
	MLOG_CALWAYS5 ("%sUTF8 Str %s: size %02d, precision %02d, offset %d",
                                   sp[indent],
                                   sp2[indent],
                                   rt_aa->el_size,
                                   rt->u.p.el_len,
				   rt_aa->offset_to_data); 
      break;

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

#endif
  }

