/************************************************************************/
/* SISCO SOFTWARE MODULE HEADER *****************************************/
/************************************************************************/
/*   (c) Copyright Systems Integration Specialists Company, Inc.,	*/
/*      	1994 - 2002, All Rights Reserved		        */
/*									*/
/* MODULE NAME : mvl_var.c    						*/
/* PRODUCT(S)  : MMSEASE-LITE						*/
/*									*/
/* MODULE DESCRIPTION : 						*/
/*									*/
/* GLOBAL FUNCTIONS DEFINED IN THIS MODULE :				*/
/*	NONE								*/
/*									*/
/* MODIFICATION LOG :							*/
/*  Date     Who   Rev			Comments			*/
/* --------  ---  ------   -------------------------------------------	*/
/* 10/30/06  JRB     31    Use new mvl_vmd_* object handling functions.	*/
/*			   _mvl_objname_to_va: add args.		*/
/*			   u_mvl_get_va_aa: add args.			*/
/*			   Elim use of global var "_mvl_curr_net_info".	*/
/* 01/30/06  GLB     30    Integrated porting changes for VMS           */
/* 02/27/04  MDE     29    Log message correct				*/
/* 03/13/03  JRB     28    Add mvl_scope_set function.			*/
/* 12/12/02  JRB     27    Add net_info arg to mvl_get_va_asn1... and	*/
/*			   elim it's use of global _mvl_curr_net...	*/
/* 02/25/02  MDE     26    Now get max PDU size from mvl_cfg_info	*/
/* 01/02/02  JRB     25    Converted to use ASN1R (re-entrant ASN1)	*/
/* 10/25/00  JRB     24    Del u_mvl funct ptrs. Call functs directly.	*/
/*			   Control with #ifdefs.			*/
/* 08/17/00  JRB     23    Don't clear va_to_free. Need value later.	*/
/* 03/08/00  JRB     22    Del #ifdef MVL_REQ_BUF_ENABLED: not used.	*/
/* 01/21/00  MDE     21    Now use MEM_SMEM for dynamic memory		*/
/* 01/21/00  MDE     20    Add '_mvl_get_req_buf' for MVL_REQ_BUF_EN..	*/
/* 11/19/99  NAV     19	   Add #ifdef MVL_REQ_BUF_ENABLED		*/
/* 10/22/99  NAV     18    Add a few utilities to copy structs		*/
/* 09/13/99  MDE     17    Added SD_CONST modifiers			*/
/* 09/07/99  MDE     16    Changed MVL_VA_SCOPE to MVL_SCOPE		*/
/* 01/20/99  JRB     15    Del mvl_find_*. Use new "bsearch" versions.	*/
/*			   Del _mvl_skip_uca_prefix (use va->flags).	*/
/*			   Del _mvl_destroy_nvl_ent.. (see mvl_nvl_de..)*/
/* 12/11/98  MDE     14    Removed scope references from VA		*/
/* 12/08/98  MDE     13    Added client alternate access support	*/
/* 11/16/98  MDE     12    Added '_mvl_destroy_nvl_entries'		*/
/* 11/16/98  MDE     11    Renamed internal functions (prefix '_')	*/
/* 11/10/98  MDE     10    Added 'mvl_get_va_asn1_data' (was in s_read)	*/
/* 09/21/98  MDE     09    Minor lint cleanup				*/
/* 07/21/98  MDE     08    Moved 'mvl_insert_nvlist' to mvl_dnvl.c	*/
/* 07/13/98  MDE     07    Fixed mvl_insert_nvlist bug when adding 1st	*/
/* 06/30/98  MDE     06    Corrected insert order for "_UCA_" names	*/
/* 06/15/98  MDE     05    Changes to allow compile under C++		*/
/* 06/05/98  MDE     04    Added 'mvl_insert_nvlist'			*/
/* 01/06/98  RWM     03    Added Binary Search Capability               */
/* 12/12/97  MDE     02    Moved mvl_vmd to rt_types.c			*/
/* 09/10/97  MDE     01    MMS-LITE V4.0 Release			*/
/************************************************************************/

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

#include "mmsdefs.h"
#include "mms_pvar.h"
#include "mms_vvar.h"
#include "mvl_acse.h"
#include "mvl_log.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

/************************************************************************/
/************************************************************************/
/*			_mvl_objname_to_va				*/
/************************************************************************/

MVL_VAR_ASSOC *_mvl_objname_to_va (MVL_VMD_CTRL *vmd_ctrl,
	MVL_NET_INFO *net_info,
	ST_INT service,
	OBJECT_NAME *obj, 
	MVL_SCOPE *va_scope_out,
	ST_BOOLEAN alt_access_pres,
	ALT_ACCESS *alt_acc,
	ST_BOOLEAN *alt_access_done_out)
  {    
MVL_VAR_ASSOC *va;

/* By default, we don't modify the VA to handle alternate access 	*/
  if (alt_access_done_out != NULL)
    *alt_access_done_out = SD_FALSE;

/* Let's find the variable association */
  va = mvl_vmd_find_var (vmd_ctrl, obj, net_info);
  if ((!va) || (va->flags & MVL_VAR_FLAG_UCA))
    {
  /* Could not find variable association, see if we can manufacture it	*/
#if defined(MVL_UCA) || defined(USE_MANUFACTURED_OBJS)
    /* May be able to manufacture it	*/
    va = u_mvl_get_va_aa (vmd_ctrl, service, obj, net_info, 
    				 alt_access_pres, alt_acc, 
    				 alt_access_done_out);
    if (va != NULL)		/* Manufactured it, set flag ...	*/
      va->va_to_free = va;
#endif	/* defined(MVL_UCA) || defined(USE_MANUFACTURED_OBJS)	*/
    }

/* OK, assuming we have the VA, set it's scope appropriately		*/
  if (va && va_scope_out)
    {
    va_scope_out->scope = obj->object_tag;
    if (va_scope_out->scope == DOM_SPEC)
      {
      va_scope_out->dom = mvl_vmd_find_dom (vmd_ctrl, obj->domain_id);
      if (va_scope_out->dom == NULL)
        {
        MVL_LOG_NERR1 ("Warning : Variable object references non-existant domain '%s'", 
						obj->domain_id);
        }
      }
    }
  return (va);
  }

/************************************************************************/
/*			mvl_scope_set					*/
/* Use the OBJECT_NAME info to set the scope for any MMS object.	*/
/* This function fills in the struct pointed to by mvl_scope_out.	*/
/************************************************************************/
ST_VOID mvl_scope_set (OBJECT_NAME *obj, MVL_SCOPE *mvl_scope_out)
  {
  mvl_scope_out->scope = obj->object_tag;
  if (mvl_scope_out->scope == DOM_SPEC)
    {
    mvl_scope_out->dom = mvl_vmd_find_dom (&mvl_vmd, obj->domain_id);
    if (mvl_scope_out->dom == NULL)
      MVL_LOG_NERR1 ("Warning : MMS object references nonexistent domain '%s'", 
					obj->domain_id);
    }
  }

/************************************************************************/
/*			mvl_get_va_asn1_data				*/
/************************************************************************/

ST_RET mvl_get_va_asn1_data (MVL_NET_INFO *net_info, MVL_VAR_ASSOC *va,
			 ST_BOOLEAN alt_access_pres,
			 ALT_ACCESS *alt_acc,
                         ST_UCHAR *asn1_dest_buffer,
                         ST_INT asn1_buffer_len,
                         ST_INT *asn1_len_out)
  {
RUNTIME_TYPE *rt;
ST_INT num_rt;
ST_RET rc;
ST_UCHAR *asn1_start;
ST_INT asn1_len;
ST_INT aa_mode;
ASN1_ENC_CTXT localEncCtx;	/* For readability, use "aCtx" to access this.*/
ASN1_ENC_CTXT *aCtx = &localEncCtx;

  aa_mode = MVL_USE_UNPACKED_AA;
  if (va->proc && va->proc->pre_read_aa)
    {
    rc = (*va->proc->pre_read_aa)(&va, alt_access_pres, alt_acc, 
  			    net_info, &aa_mode);

    if (rc != SD_SUCCESS)
      {
      MVL_LOG_NERR1 ("Read: pre_read_aa returned %d", rc);
      return (SD_FAILURE);
      }
    }

  rc = mvl_get_runtime (va->type_id, &rt, &num_rt);
  if (rc != SD_SUCCESS)
    {
    MVL_LOG_NERR1 ("Read: mvl_get_runtime failed, rc = %x", rc);
    return (rc);			     /* bad type id		*/
    }

/* If we got here, we have a valid runtime and have resolved local	*/
/* data pointer and size. Go ahead and build the ASN.1 data element.	*/

/* First we need to initialize the ASN.1 encode tools, then do the 	*/
/* encode							   	*/
  asn1r_strt_asn1_bld (aCtx, asn1_dest_buffer, asn1_buffer_len);

#if defined(MVL_AA_SUPP)
  if (alt_access_pres == SD_FALSE || aa_mode == MVL_USE_NO_AA)
    {
    rc = ms_local_to_asn1 (aCtx, rt, num_rt, (ST_CHAR *) va->data);
    }
  else
    {
    if (aa_mode == MVL_USE_PACKED_AA)
      m_alt_acc_packed = SD_TRUE;
    else
      m_alt_acc_packed = SD_FALSE;

    rc = ms_local_to_asn1_aa (aCtx, rt, num_rt, alt_acc, (ST_CHAR *) va->data);
    }
#else
  rc = ms_local_to_asn1 (aCtx, rt, num_rt, (ST_CHAR *) va->data);
#endif

/* When the ASN.1 encode is complete, 'asn1_field_ptr' points 1 byte ahead 	*/
/* of the start of the message. The ASN.1 message is build from back to	*/
/* front, so that is now we calculate the length of the PDU		*/
  if (rc == SD_SUCCESS)
    {
    asn1_start = aCtx->asn1r_field_ptr+1;
    asn1_len = (asn1_dest_buffer + asn1_buffer_len) - asn1_start;
    memmove (asn1_dest_buffer,asn1_start,asn1_len);
    *asn1_len_out = asn1_len;
  
    if (va->proc && va->proc->post_read_aa)
      {
      (*va->proc->post_read_aa)(va, alt_access_pres, alt_acc, 
    			             net_info);
      }
    }
  else
    {
    MVL_LOG_NERR1 ("Local to ASN1 data conversion error: 0x%04x", rc);
    }
  return (rc);
  }


/************************************************************************/
/************************************************************************/
#if defined(MVL_AA_SUPP)
/************************************************************************/

/************************************************************************/
/************************************************************************/
/*			_mvl_get_asn1_aa 				*/
/************************************************************************/

ST_RET _mvl_get_asn1_aa (ALT_ACCESS *alt_acc, VARIABLE_LIST *vl)
  {
ST_UCHAR *aa_buf;
ST_INT aa_buf_size;
ST_UCHAR *asn1_start;
ST_INT asn1_len;
ASN1_ENC_CTXT localEncCtx;	/* For readability, use "aCtx" to access this.*/
ASN1_ENC_CTXT *aCtx = &localEncCtx;

  aa_buf = _m_get_aa_asn1_buf (&aa_buf_size);
  asn1r_strt_asn1_bld (aCtx, aa_buf, aa_buf_size);
  if (ms_aa_to_asn1 (aCtx, alt_acc))
    {
    _m_free_aa_asn1_buf ();
    return (SD_FAILURE);
    }
  else
    {
    asn1_start = aCtx->asn1r_field_ptr+1;
    asn1_len = (aa_buf + aa_buf_size) - asn1_start;
    vl->alt_access.len = asn1_len;
    vl->alt_access.data = asn1_start;
    vl->alt_access_pres = SD_TRUE;
    _m_set_aa_asn1_buf_end (asn1_start -1);
    }
  return (SD_SUCCESS);
  }

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

static ST_UCHAR *aa_buffer_head;
static ST_UCHAR *aa_buffer_end;

/************************************************************************/
/*			_m_get_aa_asn1_buf 				*/
/************************************************************************/

ST_UCHAR *_m_get_aa_asn1_buf (ST_INT *size_out)
  {
ST_UCHAR *ret;

  if (!aa_buffer_head)
    {
    aa_buffer_head = (ST_UCHAR *) M_MALLOC (MSMEM_AA_ENCODE, mvl_cfg_info->max_msg_size);
    aa_buffer_end = aa_buffer_head + mvl_cfg_info->max_msg_size -1;
    }

  *size_out = aa_buffer_end - aa_buffer_head + 1;
  ret = aa_buffer_head;
  return (ret);
  }

/************************************************************************/
/*			_m_set_aa_asn1_buf_end 				*/
/************************************************************************/

ST_VOID _m_set_aa_asn1_buf_end (ST_UCHAR *new_end)
  {
  aa_buffer_end = new_end;
  }

/************************************************************************/
/*			_m_free_aa_asn1_buf 				*/
/************************************************************************/

ST_VOID _m_free_aa_asn1_buf (ST_VOID)
  {
  if (aa_buffer_head)
    {
    M_FREE (MSMEM_AA_ENCODE, aa_buffer_head);
    aa_buffer_head = NULL;
    }
  }

#endif /* #if defined(MVL_AA_SUPP) */


/************************************************************************/
/*			mvl_get_descr_local_addr			*/
/* DESCRIBED VARIABLE ASSOCIATION					*/
/* Addresses are in the form :						*/
/*              "baseaddr:xxx"						*/
/* where xxx is the element offset from the base address.		*/
/************************************************************************/

/************************************************************************/
#if defined (MVL_DESCR_SUPP)
/************************************************************************/

ST_RET mvl_get_descr_local_addr (VAR_ACC_ADDR *addr,
                            RUNTIME_TYPE *rt,
                            ST_INT num_rt,
                            ST_CHAR **data_out,
                            ST_INT *data_size_out)
  {
ST_INT i;
MVL_DESCR_ADDR_ASSOC *d;
ST_INT base_name_len;
ST_INT offset;
ST_INT num_offset;
ST_INT num_elmnts;

/* We only support symbolic addresses					*/
  if (addr->addr_tag != SYM_ADDR)
    {
    MVL_LOG_NERR0 ("Get Described Local Address : Only Symbolic Addresses are supported");
    return (MVL_ERR_NOT_SYM_ADDR);
    }

/* Find the matching described variable association			*/
  d = mvl_vmd.descr_addr_assoc_tbl;
  for (i = 0; i < mvl_vmd.num_descr_addr; ++i, ++d)
    {
    base_name_len = strlen (d->base_name);
    if (!strncmp (d->base_name, addr->addr.sym_addr, base_name_len))
      {
      /* Check for offset from base element				*/
      if (base_name_len == strlen (addr->addr.sym_addr))
        offset = 0;
      else
        {
        num_offset = atoi (&addr->addr.sym_addr[base_name_len + 1]);
        if (num_offset)         /* :1 is first element			*/
          num_offset--;
        offset = d->data_size * num_offset;
        }

      if (rt->el_tag != RT_ARR_START) /* if not array			*/
        {
        *data_out = (ST_CHAR *) d->data + offset;
        *data_size_out = d->data_size;
        return (SD_SUCCESS);
        }
      else      /* this is an array					*/
        {
        num_elmnts = rt->u.arr.num_elmnts; /* get number of elements	*/
        if (num_elmnts + num_offset > d->max_elmnts)
	  {
	  MVL_LOG_NERR0 ("Get Described Local Address : Invalid array element count");
	  return (MVL_ERR_ARRAY_ELEMENT_CNT);
	  }

        *data_out = (ST_CHAR *) d->data + offset;
        *data_size_out = d->data_size * num_elmnts;
        return (SD_SUCCESS);
        }
      break;
      }
    }
  MVL_LOG_NERR0 ("Get Described Local Address : Could not get Local Address");
  return (MVL_ERR_LOCAL_ADDRESS);
  }
/************************************************************************/
#endif /* #if defined (MVL_DESCR_SUPP) */
/************************************************************************/

