/************************************************************************/
/* SISCO SOFTWARE MODULE HEADER *****************************************/
/************************************************************************/
/*   (c) Copyright Systems Integration Specialists Company, Inc.,	*/
/*      	  1986 - 2006, All Rights Reserved.		        */
/*									*/
/*		    PROPRIETARY AND CONFIDENTIAL			*/
/*									*/
/* MODULE NAME : mms_alta.c						*/
/* PRODUCT(S)  : MMSEASE						*/
/*									*/
/* MODULE DESCRIPTION : 						*/
/* Function to decode ASN.1 type definitions into Runtime typedefs	*/
/*									*/
/* GLOBAL FUNCTIONS DEFINED IN THIS MODULE :				*/
/*									*/
/* MODIFICATION LOG :							*/
/*  Date     Who   Rev			Comments			*/
/* --------  ---  ------   -------------------------------------------	*/
/* 08/09/06  JRB     20    Add ms_aa_to_asn1_2.				*/
/* 02/22/05  JRB     19    Make "sp" local to avoid VXWORKS conflict.	*/
/* 02/06/02  JRB     18    Move ms_mk_rt_aa* to new mms_rtaa.c.		*/
/* 12/20/01  JRB     17    Converted to use ASN1R (re-entrant ASN1)	*/
/* 11/15/01  EJV     16    Added support for new MMS type UtcTime:	*/
/*			   find_named_comp: added case for RT_UTC_TIME;	*/
/*			   _ms_log_runtime_aa: add case for RT_UTC_TIME;*/
/* 09/26/00  JRB     15    Add SD_CONST on ms_mk_rt_aa*			*/
/* 07/28/00  JRB     14    Use ms_comp_name_find for everyone.		*/
/* 07/13/00  JRB     13    Use new ms_comp_name_find for MMS LITE.	*/
/* 04/28/00  JRB     12    Lint cleanup					*/
/* 09/13/99  RKR     11    Respaced a macro for the install utility     */
/* 09/13/99  MDE     10    Added SD_CONST modifiers			*/
/* 07/30/99  MDE     09    Replaced CSTR_DONE_FUN macro use		*/
/* 09/21/98  MDE     08    Minor lint cleanup				*/
/* 09/10/98  MDE     07    Changes to work better with MMS-LITE		*/
/* 06/15/98  MDE     06    Changes to allow compile under C++		*/
/* 03/12/98  MDE     05    '_ms_m_get_rt_info' use change		*/
/* 03/11/98  MDE     04    Removed NEST_RT_TYPES			*/
/* 02/10/98  MDE     03    No longer use runtime type 'loops' element	*/
/*			   rearranged for MMS-LITE (MMS_ALTA_DATA)	*/
/* 12/29/97  MDE     02    Corrected LOCK/UNLOCK problems		*/
/* 06/09/97  MDE     01    Modified Runtime Type handling		*/
/* 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"

/************************************************************************/
/* 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_RET wr_altAcc (ASN1_ENC_CTXT *aCtx, ALT_ACC_EL **aap, ALT_ACC_EL *last_aa);
static ST_RET wr_altAccSel (ASN1_ENC_CTXT *aCtx, ALT_ACC_EL **aap, ALT_ACC_EL *last_aa);
static ALT_ACC_EL *find_start_aa_nest (ALT_ACC_EL *aa, ALT_ACC_EL *last_aa);
static ST_VOID alt_acc_seq_start (ASN1_DEC_CTXT *aCtx);
static ST_VOID alt_acc_seq_cstr_done (ASN1_DEC_CTXT *aCtx);
static ST_VOID add_alt_acc_tags (ASN1_DEC_CTXT *aCtx);
static ST_VOID named_start (ASN1_DEC_CTXT *aCtx);
static ST_VOID get_component_name (ASN1_DEC_CTXT *aCtx);
static ST_VOID named_cstr_done (ASN1_DEC_CTXT *aCtx);
static ST_VOID sel_acc_comp (ASN1_DEC_CTXT *aCtx);
static ST_VOID sel_acc_index (ASN1_DEC_CTXT *aCtx);
static ST_VOID sel_acc_index_range (ASN1_DEC_CTXT *aCtx);
static ST_VOID sel_acc_ir_low (ASN1_DEC_CTXT *aCtx);
static ST_VOID sel_acc_ir_num (ASN1_DEC_CTXT *aCtx);
static ST_VOID sel_acc_ir_cstr_done (ASN1_DEC_CTXT *aCtx);
static ST_VOID sel_acc_all (ASN1_DEC_CTXT *aCtx);
static ST_VOID sel_alt_acc (ASN1_DEC_CTXT *aCtx);
static ST_VOID sel_alt_acc_cstr_done (ASN1_DEC_CTXT *aCtx);
static ST_VOID sel_alt_acc_comp (ASN1_DEC_CTXT *aCtx);
static ST_VOID sel_alt_acc_index (ASN1_DEC_CTXT *aCtx);
static ST_VOID sel_alt_acc_index_range (ASN1_DEC_CTXT *aCtx);
static ST_VOID sel_alt_acc_ir_low (ASN1_DEC_CTXT *aCtx);
static ST_VOID sel_alt_acc_ir_num (ASN1_DEC_CTXT *aCtx);
static ST_VOID sel_alt_acc_ir_cstr_done (ASN1_DEC_CTXT *aCtx);
static ST_VOID sel_alt_acc_all (ASN1_DEC_CTXT *aCtx);
static ST_VOID alt_acc_dec_done_ok (ASN1_DEC_CTXT *aCtx);
static ST_VOID inc_dest_aa (ASN1_DEC_CTXT *aCtx);

/************************************************************************/
/************************************************************************/
/* Static internal variables, used during decode			*/

static ALT_ACC_EL *dest_aa;
static ALT_ACC_EL *end_dest_aa;

/************************************************************************/
/************************************************************************/
/*                       ms_asn1_to_aa					*/
/* Convert an ASN.1 encoded AA to an array of 'ALT_ACC_EL'		*/
/* Allocates an array of 'm_max_dec_aa' elements, using chk_calloc,	*/
/* does not realloc to correct size.					*/
/************************************************************************/


ST_RET ms_asn1_to_aa (ST_UCHAR *asn1ptr, ST_INT asn1len, ALT_ACCESS *alt_acc_out)
  {
ALT_ACC_EL *dest_aa_head;
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.	*/

  S_LOCK_COMMON_RESOURCES ();

/* Allocate the output AA array						*/
  dest_aa = (ALT_ACC_EL *) chk_calloc (m_max_dec_aa, sizeof (ALT_ACC_EL));
  end_dest_aa = dest_aa + (m_max_dec_aa -1);
  dest_aa_head = dest_aa;

/* Set up ASN.1 decode tools						*/
  aCtx->asn1r_decode_method = ASN1_TAG_METHOD;		/* select tag method			*/
  aCtx->asn1r_decode_done_fun = asn1r_done_err;/* not legal to be done at this time	*/
  aCtx->asn1r_err_fun = NULL;

/* We are using an alternate entry point into the ASN.1 decode, where	*/
/* the outer tag is not required.					*/

  add_alt_acc_tags (aCtx);
  aCtx->asn1r_c_done_fun[0] = NULL;
  aCtx->asn1r_decode_done_fun = alt_acc_dec_done_ok;
  asn1r_decode_asn1_seq (aCtx, asn1ptr, asn1len); /* decode ASN.1 'Data' entity  */

  if (aCtx->asn1r_pdu_dec_err != NO_DECODE_ERR)	/* check for sucess or return error	*/
    {
    chk_free (dest_aa_head);
    MLOG_NERR0 ("ASN.1 to AA Decode Error");
    S_UNLOCK_COMMON_RESOURCES ();
    return (MVE_DATA_CONVERT);
    }

  alt_acc_out->aa = dest_aa_head;
  alt_acc_out->num_aa = dest_aa - dest_aa_head;

/* Log the alternate access, if selected				*/
  if (mms_debug_sel & MMS_LOG_AA)
    ms_log_alt_access (alt_acc_out);

/* save the high water mark						*/
  if (alt_acc_out->num_aa > m_hw_dec_aa)
    m_hw_dec_aa = alt_acc_out->num_aa;

  S_UNLOCK_COMMON_RESOURCES ();
  return (SD_SUCCESS);
  }

/************************************************************************/
/*			alt_acc_seq_start				*/
/* This function is called whe the outer UNI SEQ constructor of the	*/
/* AlternateAcess is found. Used only for continuing the decode for a	*/
/* nested AA spec, as the outer constructor is stripped 		*/
/************************************************************************/

static ST_VOID alt_acc_seq_start (ASN1_DEC_CTXT *aCtx)
  {
  add_alt_acc_tags (aCtx);
  aCtx->asn1r_c_done_fun[aCtx->asn1r_msg_level] = alt_acc_seq_cstr_done;
  }


/************************************************************************/
/*			alt_acc_seq_cstr_done				*/
/************************************************************************/

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

/************************************************************************/
/************************************************************************/
/*			add_alt_acc_tags				*/
/* Add appropriate tags for the outer 'AlternateAccess'			*/
/************************************************************************/

static ST_VOID  add_alt_acc_tags (ASN1_DEC_CTXT *aCtx)
  {
  ASN1R_TAG_ADD (aCtx, CTX | CONSTR, 0, sel_alt_acc);
  ASN1R_TAG_ADD (aCtx, CTX,          1, sel_acc_comp);
  ASN1R_TAG_ADD (aCtx, CTX,          2, sel_acc_index);
  ASN1R_TAG_ADD (aCtx, CTX | CONSTR, 3, sel_acc_index_range);
  ASN1R_TAG_ADD (aCtx, CTX,          4, sel_acc_all);
  ASN1R_TAG_ADD (aCtx, CTX | CONSTR, 5, named_start);
  }

/************************************************************************/
/*			named_start					*/
/* Constructor for the 'named' element of the 'AlternateAccess'		*/
/************************************************************************/

static ST_VOID named_start (ASN1_DEC_CTXT *aCtx)
  {
  MLOG_CDEC0 ("named_start");
  ASN1R_TAG_ADD (aCtx, CTX, 0, get_component_name);
  aCtx->asn1r_c_done_fun[aCtx->asn1r_msg_level] = named_cstr_done;
  }

/************************************************************************/
/*			get_component_name 				*/
/* The 'componentName' element of the 'named' element			*/
/************************************************************************/

static ST_VOID get_component_name (ASN1_DEC_CTXT *aCtx)
  {
  MLOG_CDEC0 ("get_component_name");
  if (asn1r_get_identifier (aCtx, dest_aa->comp_name))
    asn1r_set_dec_err (aCtx, RESP_BAD_VALUE);      
  else
    {
    dest_aa->comp_name_pres = SD_TRUE;
    ASN1R_TAG_ADD (aCtx, CTX | CONSTR, 0, sel_alt_acc);
    ASN1R_TAG_ADD (aCtx, CTX,          1, sel_acc_comp);
    ASN1R_TAG_ADD (aCtx, CTX,          2, sel_acc_index);
    ASN1R_TAG_ADD (aCtx, CTX | CONSTR, 3, sel_acc_index_range);
    ASN1R_TAG_ADD (aCtx, CTX,          4, sel_acc_all);
    }
  }

/************************************************************************/
/*			named_cstr_done					*/
/* End of 'named' constructor						*/
/************************************************************************/

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


/************************************************************************/
/*			sel_acc_comp					*/
/* The 'component' of the 'selectAccess' has been encountered		*/
/************************************************************************/

static ST_VOID sel_acc_comp (ASN1_DEC_CTXT *aCtx)
  {
  MLOG_CDEC0 ("sel_acc_comp");
  if (asn1r_get_identifier (aCtx, dest_aa->u.component))
    asn1r_set_dec_err (aCtx, RESP_BAD_VALUE);      
  else
    {
    dest_aa->sel_type = AA_COMP;
    inc_dest_aa (aCtx);
    add_alt_acc_tags (aCtx);
    }
  }

/************************************************************************/
/*			sel_acc_index					*/
/* The 'index' of the 'selectAccess' has been encountered		*/
/************************************************************************/

static ST_VOID sel_acc_index (ASN1_DEC_CTXT *aCtx)
  {
  MLOG_CDEC0 ("sel_acc_index");
  if (asn1r_get_u32 (aCtx, &dest_aa->u.index))
    asn1r_set_dec_err (aCtx, RESP_BAD_VALUE);      
  else
    {
    dest_aa->sel_type = AA_INDEX;
    inc_dest_aa (aCtx);
    add_alt_acc_tags (aCtx);
    }
  }

/************************************************************************/
/*			sel_acc_index_range				*/
/* The 'indexRange' of the 'selectAccess' has been encountered		*/
/************************************************************************/

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

  dest_aa->sel_type = AA_INDEX_RANGE;
  ASN1R_TAG_ADD (aCtx, CTX,0, sel_acc_ir_low);
  }


/************************************************************************/
/*			sel_acc_ir_low					*/
/* The 'low_index' of the 'indexRange' has been encountered		*/
/************************************************************************/

static ST_VOID sel_acc_ir_low (ASN1_DEC_CTXT *aCtx)
  {
  MLOG_CDEC0 ("sel_acc_ir_low");
  if (asn1r_get_u32 (aCtx, &dest_aa->u.ir.low_index))
    asn1r_set_dec_err (aCtx, RESP_BAD_VALUE);      
  else
    {
    ASN1R_TAG_ADD (aCtx, CTX, 1, sel_acc_ir_num);
    }
  }

/************************************************************************/
/*			sel_acc_ir_num					*/
/* The 'numberOfElements' of the 'indexRange' has been encountered 	*/
/************************************************************************/

static ST_VOID sel_acc_ir_num (ASN1_DEC_CTXT *aCtx)
  {
  MLOG_CDEC0 ("sel_acc_ir_num");
  if (asn1r_get_u32 (aCtx, &dest_aa->u.ir.num_elmnts))
    asn1r_set_dec_err (aCtx, RESP_BAD_VALUE);      
  else
    {
    aCtx->asn1r_c_done_fun[aCtx->asn1r_msg_level] = sel_acc_ir_cstr_done;
    }
  }

/************************************************************************/
/*			sel_acc_ir_cstr_done				*/
/* End of the 'indexRange' constructor					*/
/************************************************************************/

static ST_VOID sel_acc_ir_cstr_done (ASN1_DEC_CTXT *aCtx)
  {
  MLOG_CDEC0 ("sel_acc_ir_cstr_done");
  inc_dest_aa (aCtx);
  add_alt_acc_tags (aCtx);
  }


/************************************************************************/
/*			sel_acc_all					*/
/* The 'allElements' of the 'selectAccess' has been encountered		*/
/************************************************************************/

static ST_VOID sel_acc_all (ASN1_DEC_CTXT *aCtx)
  {
  MLOG_CDEC0 ("sel_acc_all");
  dest_aa->sel_type = AA_ALL;
  inc_dest_aa (aCtx);
  add_alt_acc_tags (aCtx);
  }


/************************************************************************/
/************************************************************************/
/*			sel_alt_acc					*/
/* The 'selectAlternateAccess' constructor has been encountered		*/
/************************************************************************/

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

  ASN1R_TAG_ADD (aCtx, CTX,          0, sel_alt_acc_comp);
  ASN1R_TAG_ADD (aCtx, CTX,          1, sel_alt_acc_index);
  ASN1R_TAG_ADD (aCtx, CTX | CONSTR, 2, sel_alt_acc_index_range);
  ASN1R_TAG_ADD (aCtx, CTX,          3, sel_alt_acc_all);
  aCtx->asn1r_c_done_fun[aCtx->asn1r_msg_level] = sel_alt_acc_cstr_done;
  }


/************************************************************************/
/*			sel_alt_acc_cstr_done				*/
/* End of the 'selectAlternateAccess' constructor			*/
/************************************************************************/

static ST_VOID sel_alt_acc_cstr_done (ASN1_DEC_CTXT *aCtx)
  {
  MLOG_CDEC0 ("sel_alt_acc_cstr_done");
  dest_aa->sel_type = AA_END_NEST;
  inc_dest_aa (aCtx);
  add_alt_acc_tags (aCtx);
  }

/************************************************************************/
/*			sel_alt_acc_comp				*/
/* The 'component' of the 'selectAlternateAccesss' has been encountered	*/
/************************************************************************/

static ST_VOID sel_alt_acc_comp (ASN1_DEC_CTXT *aCtx)
  {
  MLOG_CDEC0 ("sel_alt_acc_comp");
  if (asn1r_get_identifier (aCtx, dest_aa->u.component))
    asn1r_set_dec_err (aCtx, RESP_BAD_VALUE);      
  else
    {
    dest_aa->sel_type = AA_COMP_NEST;
    inc_dest_aa (aCtx);
    ASN1R_TAG_ADD (aCtx, UNI | CONSTR, SEQ_CODE, alt_acc_seq_start);
    }
  }

/************************************************************************/
/*			sel_alt_acc_index				*/
/* The 'index' of the 'selectAlternateAccesss' has been encountered   	*/
/************************************************************************/

static ST_VOID sel_alt_acc_index (ASN1_DEC_CTXT *aCtx)
  {
  MLOG_CDEC0 ("sel_alt_acc_index");
  if (asn1r_get_u32 (aCtx, &dest_aa->u.index))
    asn1r_set_dec_err (aCtx, RESP_BAD_VALUE);      
  else
    {
    dest_aa->sel_type = AA_INDEX_NEST;
    inc_dest_aa (aCtx);
    ASN1R_TAG_ADD (aCtx, UNI | CONSTR, SEQ_CODE, alt_acc_seq_start);
    }
  }

/************************************************************************/
/*			sel_alt_acc_index_range				*/
/* The 'indexRange' of 'selectAlternateAccesss' has been encountered	*/
/************************************************************************/

static ST_VOID sel_alt_acc_index_range (ASN1_DEC_CTXT *aCtx)
  {
  MLOG_CDEC0 ("sel_alt_acc_index_range");
  dest_aa->sel_type = AA_INDEX_RANGE_NEST;
  ASN1R_TAG_ADD (aCtx, CTX,0, sel_alt_acc_ir_low);
  }


/************************************************************************/
/*			sel_alt_acc_ir_low				*/
/* The 'low_index' of the 'indexRange' has been encountered		*/
/************************************************************************/

static ST_VOID sel_alt_acc_ir_low (ASN1_DEC_CTXT *aCtx)
  {
  MLOG_CDEC0 ("sel_alt_acc_ir_low");
  if (asn1r_get_u32 (aCtx, &dest_aa->u.ir.low_index))
    asn1r_set_dec_err (aCtx, RESP_BAD_VALUE);      
  else
    {
    ASN1R_TAG_ADD (aCtx, CTX, 1, sel_alt_acc_ir_num);
    }
  }

/************************************************************************/
/*			sel_alt_acc_ir_num				*/
/* The 'numberOfElements' of the 'indexRange' has been encountered 	*/
/************************************************************************/

static ST_VOID sel_alt_acc_ir_num (ASN1_DEC_CTXT *aCtx)
  {
  MLOG_CDEC0 ("sel_alt_acc_ir_num");
  if (asn1r_get_u32 (aCtx, &dest_aa->u.ir.num_elmnts))
    asn1r_set_dec_err (aCtx, RESP_BAD_VALUE);      
  else
    {
    aCtx->asn1r_c_done_fun[aCtx->asn1r_msg_level] = sel_alt_acc_ir_cstr_done;
    }
  }

/************************************************************************/
/*			sel_alt_acc_ir_cstr_done			*/
/* End of the 'indexRange' constructor					*/
/************************************************************************/

static ST_VOID sel_alt_acc_ir_cstr_done (ASN1_DEC_CTXT *aCtx)
  {
  MLOG_CDEC0 ("sel_alt_acc_ir_cstr_done");
  inc_dest_aa (aCtx);
  ASN1R_TAG_ADD (aCtx, UNI | CONSTR, SEQ_CODE, alt_acc_seq_start);
  }


/************************************************************************/
/*			sel_alt_acc_all					*/
/* The 'allElements' of 'selectAlternateAccesss' has been encountered	*/
/************************************************************************/

static ST_VOID sel_alt_acc_all (ASN1_DEC_CTXT *aCtx)
  {
  MLOG_CDEC0 ("sel_alt_acc_all");
  dest_aa->sel_type = AA_ALL_NEST;
  inc_dest_aa (aCtx);
  ASN1R_TAG_ADD (aCtx, UNI | CONSTR, SEQ_CODE, alt_acc_seq_start);
  }


/************************************************************************/
/*			inc_dest_aa					*/
/* Increment the destination AA pointer, verify no overrun		*/
/************************************************************************/

static ST_VOID inc_dest_aa (ASN1_DEC_CTXT *aCtx)
  {
  if (++dest_aa > end_dest_aa)
    asn1r_set_dec_err (aCtx, RESP_BAD_VALUE);      
  }

/************************************************************************/
/*			alt_acc_dec_done_ok				*/
/* 'decode done fun' for the ASN.1 to AA decode				*/
/************************************************************************/

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

/************************************************************************/
/************************************************************************/
/************************************************************************/
/************************************************************************/
/*                       ms_aa_to_asn1					*/
/* Encode Alternate Access, assume that the encode tools have been 	*/
/* initalized								*/
/* NOTE: The higher level function "ms_aa_to_asn1_2" is more convenient.*/
/*   Call this directly only if "AA" encode is in the middle of a	*/
/*   larger encode.							*/
/************************************************************************/

static ST_INT encodeDone;

ST_RET ms_aa_to_asn1 (ASN1_ENC_CTXT *aCtx, ALT_ACCESS *alt_acc)
  {
ALT_ACC_EL *aa;
ALT_ACC_EL *last_aa;
ST_RET ret;

  S_LOCK_COMMON_RESOURCES ();
  aa = alt_acc->aa;
  last_aa = aa;				/* save pointer to aa[0]	*/
  aa += (alt_acc->num_aa-1);		/* go from bottom to top	*/

/* Write the AlternateAccess 'Seq Of Choice' elements until we have 	*/
/* gone through all AA elements						*/
  ret = SD_SUCCESS;
  encodeDone = SD_FALSE;
  while (!encodeDone && ret == SD_SUCCESS)
    {
    if (aa == last_aa)
      encodeDone = SD_TRUE;
    ret = wr_altAcc (aCtx, &aa, last_aa);
    }

/* Verify no asn.1 encode buffer overrun				*/
  if (aCtx->asn1r_encode_overrun)
    {
    MLOG_NERR0 ("AA to ASN.1 : buffer overrun");
    ret = ME_ASN1_ENCODE_OVERRUN;
    }

  S_UNLOCK_COMMON_RESOURCES ();
  return (ret);
  }

/************************************************************************/
/*			 	wr_altAcc 				*/
/************************************************************************/

/* NOTE : This function is entered recursively to encode parts of the	*/
/* AlternateAccess							*/

static ST_RET wr_altAcc (ASN1_ENC_CTXT *aCtx, ALT_ACC_EL **aap, ALT_ACC_EL *last_aa)
  {
ALT_ACC_EL *aa;
ALT_ACC_EL *aa_start_nest;
ST_RET ret;

/* get a working pointer						*/
  aa = *aap;

/* Write the AlternateAccess sequence of choice element			*/

/* First need to see if this is the start of a named component, so 	*/
/* we can start the constructor if so					*/
  
/* If this is a nested AA element, need to find the start element 	*/
  if (aa->sel_type == AA_END_NEST)
    {
    aa_start_nest = find_start_aa_nest (aa, last_aa);
    if (!aa_start_nest)
      {
      MLOG_NERR0 ("AA to ASN.1 : Bad AA");
      return (MVE_AA_SELECT);
      }

    if (aa_start_nest->comp_name_pres)
      asn1r_strt_constr (aCtx);			/* Start 'named' seq cstr 	*/
    }
  else					/* not the start of a nested	*/
    {
    if (aa->comp_name_pres)		
      asn1r_strt_constr (aCtx);			/* Start 'named' seq cstr 	*/
    }

/* OK, if this was a named component, the constructor has been started	*/
/* Go ahead and write the AlternateAccessSelection element		*/

  ret = wr_altAccSel (aCtx, &aa, last_aa); 	/* write the altAccSel		*/

  *aap = aa;
  return (ret);
  }


/************************************************************************/
/*			wr_altAccSel					*/
/************************************************************************/

/* NOTE : This function is entered recursively to encode parts of the	*/
/* AlternateAccess							*/

static ST_RET wr_altAccSel (ASN1_ENC_CTXT *aCtx, ALT_ACC_EL **aap, ALT_ACC_EL *last_aa)
  {
ALT_ACC_EL *aa;
ALT_ACC_EL *aa_start_nest;
ST_RET ret;

  ret = SD_SUCCESS;
  aa = *aap;				/* get working pointer		*/
  switch (aa->sel_type)
    {
    case AA_COMP :
      asn1r_wr_vstr (aCtx, aa->u.component);
      asn1r_fin_prim (aCtx, 1,CTX);
    break;
  
    case AA_INDEX :
      asn1r_wr_u32 (aCtx, aa->u.index);
      asn1r_fin_prim (aCtx, 2,CTX);
    break;
  
    case AA_INDEX_RANGE :
      asn1r_strt_constr (aCtx);			
      asn1r_wr_u32 (aCtx, aa->u.ir.num_elmnts);
      asn1r_fin_prim (aCtx, 1,CTX);
      asn1r_wr_u32 (aCtx, aa->u.ir.low_index);
      asn1r_fin_prim (aCtx, 0,CTX);
      asn1r_fin_constr (aCtx, 3,CTX,DEF);
    break;
  
    case AA_ALL :
      asn1r_fin_prim (aCtx, 4,CTX);
    break;
  
    case AA_END_NEST :

    /* First we need to find the NEST START element so that we can	*/
    /* encode all elements on this level before returning, so that we 	*/
    /* can add the outer constructor					*/

      aa_start_nest = find_start_aa_nest (aa, last_aa);
      if (!aa_start_nest)
        {
        MLOG_NERR0 ("AA to ASN.1 : Bad AA");
        return (MVE_AA_SELECT);
        }
      
      asn1r_strt_constr (aCtx); 	/* Start SelectAlternateAccess constructor	*/
      asn1r_strt_constr (aCtx); 	/* Start outer AlternateAccess constructor	*/
      --aa;		/* Begin with next element			*/
      while (aa != aa_start_nest)
        {		
        ret = wr_altAcc (aCtx, &aa, last_aa);	/* wr_altAcc will decrement aa	*/
        if (aa == last_aa)
          encodeDone = SD_TRUE;

        if (ret != SD_SUCCESS)
          return (ret);
        }
      asn1r_fin_constr (aCtx, SEQ_CODE,UNI,DEF); /* Outer AlternateAccess cstr	*/

    /* Now write the accessSelection					*/
      switch (aa->sel_type)
        {
        case AA_COMP_NEST :
          asn1r_wr_vstr (aCtx, aa->u.component);
          asn1r_fin_prim (aCtx, 0,CTX);
        break;
      
        case AA_INDEX_NEST :
          asn1r_wr_u32 (aCtx, aa->u.index);
          asn1r_fin_prim (aCtx, 1,CTX);
        break;
      
        case AA_INDEX_RANGE_NEST :
          asn1r_strt_constr (aCtx);			
          asn1r_wr_u32 (aCtx, aa->u.ir.num_elmnts);
          asn1r_fin_prim (aCtx, 1,CTX);
          asn1r_wr_u32 (aCtx, aa->u.ir.low_index);
          asn1r_fin_prim (aCtx, 0,CTX);
          asn1r_fin_constr (aCtx, 2,CTX,DEF);
        break;	   
      
        case AA_ALL_NEST :
          asn1r_fin_prim (aCtx, 3,CTX);
        break;	
        }

    /* Finish the accessSelection constructor				*/
      asn1r_fin_constr (aCtx, 0,CTX,DEF);
    break;

  /* These should never be encountered at this level			*/
    case AA_COMP_NEST :
    case AA_INDEX_NEST :
    case AA_INDEX_RANGE_NEST :
    case AA_ALL_NEST :
    default :
      MLOG_NERR0 ("AA to ASN.1 : Bad AA sel_type value");
      return SD_FAILURE;
    break;
    }


/* For the elements that are not 'end nest', add the 'named' element 	*/
/* as required								*/

  if (aa->sel_type != AA_END_NEST)
    {
    if (aa->comp_name_pres)
      {
      asn1r_wr_vstr (aCtx, aa->comp_name);		/* Write the name 		*/
      asn1r_fin_prim (aCtx, 0,CTX);
      asn1r_fin_constr (aCtx, 5,CTX,DEF);		/* Finish the 'named' cstr	*/
      }
    }

  if (aa > last_aa)
    --aa;					
  *aap = aa;
  return (ret);
  }


/************************************************************************/
/*			 find_start_aa_nest				*/
/************************************************************************/

static ALT_ACC_EL *find_start_aa_nest (ALT_ACC_EL *aa, 
			   	       	     ALT_ACC_EL *last_aa)
  {
ALT_ACC_EL *aa2;
ST_INT nest;

  aa2 = aa-1;     		/* Start with the element above		*/
  nest = 0;			/* keep track of nesting level		*/
  while (SD_TRUE)
    {
    if (aa2->sel_type == AA_COMP_NEST ||
        aa2->sel_type == AA_INDEX_NEST ||
        aa2->sel_type == AA_INDEX_RANGE_NEST ||
        aa2->sel_type == AA_ALL_NEST)
      {
      if (nest == 0)		/* we found the matching start nest	*/
        break;   
    
      --nest;			/* point to the next above element	*/
      if (nest < 0)		/* sanity check - users make this tbl	*/
        {
        return (NULL);
        }
      }
    else 			/* not a start nest element, check to	*/
      {			/* see if we are nesting further	*/
      if (aa2->sel_type == AA_END_NEST)
        nest++;			
      }
    
  /* OK, we did not find the start element for the end nest, look 	*/
  /* some more								*/
    if (aa2 == last_aa)		/* sanity check - users make this tbl	*/
      {
      MLOG_NERR0 ("AA to ASN.1 : Bad AA");
      return (NULL);
      }
    --aa2;
    }
  return (aa2);
  }

/************************************************************************/
/*			ms_log_alt_access  				*/
/************************************************************************/

ST_VOID ms_log_alt_access (ALT_ACCESS *alt_acc)
  {
#ifdef DEBUG_SISCO
ALT_ACC_EL *aa;
ST_INT num_aa;
ST_INT i;
ST_INT indent;
/* 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 ();
  aa = alt_acc->aa;
  num_aa = alt_acc->num_aa;
  indent = 0;
  MLOG_ALWAYS1 ("Alternate Access : %d elements", num_aa);

  for (i = 0; i < num_aa; ++i, ++aa)
    {
    if (aa->comp_name_pres)
      {
      MLOG_CALWAYS3   ("%sNamed      %s: %s", 
					sp[indent], 
                                        sp2[indent],
					aa->comp_name);
      }

    switch (aa->sel_type)
      {
      case AA_COMP :
	MLOG_CALWAYS3 ("%sComp       %s: %s", 
					sp[indent], 
                                        sp2[indent],
					aa->u.component);
      break;
      case AA_INDEX :
	MLOG_CALWAYS3 ("%sIndex      %s: %ld", 
					sp[indent], 
                                        sp2[indent],
					aa->u.index);
        if (--indent < 0)
          indent++;
      break;
      case AA_INDEX_RANGE :
	MLOG_CALWAYS4 ("%sIndex Range%s: Low = %ld, Num = %ld", 
					sp[indent], 
                                        sp2[indent],
					aa->u.ir.low_index,
					aa->u.ir.num_elmnts);
        if (--indent < 0)
          indent++;
      break;
      case AA_ALL :
	MLOG_CALWAYS2 ("%sAll        %s:", 
					sp[indent], 
                                        sp2[indent]);
        if (--indent < 0)
          indent++;
      break;
      case AA_ALL_NEST :
	MLOG_CALWAYS2 ("%sAll Nest   %s:", 
					sp[indent], 
                                        sp2[indent]);
        if (++indent >= MAX_INDENT)
          indent--;
      break;
      case AA_COMP_NEST :
        MLOG_CALWAYS3 ("%sComp Nest  %s: %s", 
					sp[indent], 
                                        sp2[indent],
					aa->u.component);
        if (++indent >= MAX_INDENT)
          indent--;
      break;
      case AA_INDEX_NEST :
	MLOG_CALWAYS3 ("%sIndex Nest %s: %ld", 
					sp[indent], 
                                        sp2[indent],
					aa->u.index);
        if (++indent >= MAX_INDENT)
          indent--;
      break;
      case AA_INDEX_RANGE_NEST :
	MLOG_CALWAYS4 ("%sRange Nest %s: Low = %ld, Num = %ld", 
					sp[indent], 
                                        sp2[indent],
					aa->u.ir.low_index,
					aa->u.ir.num_elmnts);
        if (++indent >= MAX_INDENT)
          indent--;
      break;
      case AA_END_NEST :
	MLOG_CALWAYS2 ("%sEnd Nest   %s:", 
					sp[indent], 
                                        sp2[indent]);
        if (--indent < 0)
          indent++;
      break;

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

/************************************************************************/
/*			ms_aa_to_asn1_2					*/
/************************************************************************/
ST_RET ms_aa_to_asn1_2 (ALT_ACCESS *alt_acc,	/* info to be encoded	*/
	ST_UCHAR *buf_ptr,		/* buffer in which to encode	*/
	ST_INT buf_len,			/* buffer length		*/
	ST_UCHAR **asn1_ptr_out,	/* ptr to ptr to encoded data	*/
	ST_INT *asn1_len_out)		/* ptr to encoded length	*/
  {
ASN1_ENC_CTXT aCtx;
ST_RET retcode;

  asn1r_strt_asn1_bld (&aCtx, buf_ptr, buf_len);	/* start build*/
  retcode = ms_aa_to_asn1 (&aCtx, alt_acc);
  if (retcode == SD_SUCCESS)
    {
    *asn1_ptr_out = ASN1_ENC_PTR(&aCtx);
    *asn1_len_out = ASN1_ENC_LEN(&aCtx);
    }
  return (retcode);
  }
