/************************************************************************/
/* SISCO SOFTWARE MODULE HEADER *****************************************/
/************************************************************************/
/*   (c) Copyright Systems Integration Specialists Company, Inc.,	*/
/*      	  1986 - 1997, All Rights Reserved.		        */			
/*									*/
/*		    PROPRIETARY AND CONFIDENTIAL			*/
/*									*/
/* MODULE NAME : mms_adl.c						*/
/* PRODUCT(S)  : MMSEASE						*/
/*									*/
/* MODULE DESCRIPTION : 						*/
/* Function to convert ADL (Alternate Access Definition Language) into 	*/
/* alternate access structure						*/
/*									*/
/* GLOBAL FUNCTIONS DEFINED IN THIS MODULE :				*/
/*									*/
/* MODIFICATION LOG :							*/
/*  Date     Who   Rev			Comments			*/
/* --------  ---  ------   -------------------------------------------	*/
/* 08/09/06  JRB     07    Added ms_adl_to_asn1_2.			*/
/* 09/13/99  MDE     06    Added SD_CONST modifiers			*/
/* 06/15/98  MDE     05    Changes to allow compile under C++		*/
/* 02/20/98  MDE     04    Minor variable initialization changes	*/
/* 12/29/97  MDE     03    Corrected LOCK/UNLOCK problems		*/
/* 12/10/97  MDE     02    Added logging for invalid sel_type		*/
/* 05/21/97  IKE     01	   Used '[' instead of ':' for nesting for an	*/
/* 			   array of 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 <ctype.h>
#include "mmsdefs.h"
#include "mms_pvar.h"
#include "mms_vvar.h"

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

#define MAX_NESTING_LEVEL	50
#define MAX_NUM_AA	       	100	

#define AA_STACK_EMPTY(a)	(!((a)->brace_number)) /* is stack empty*/
#define AA_STACK_FULL(a)	((a)->brace_number == MAX_NESTING_LEVEL)

#define L_ANGLE_BRACKET		'<'
#define R_ANGLE_BRACKET		'>'
#define	L_BRACE			'['
#define	R_BRACE			']'
#define	COMMA			','
#define	COLON			':'
#define	ASTERISK		'*'
#define NULL_CHARACTER		'\0'

#define ARRAY_ACCESS		0
#define STRUCTURE_ACCESS	1
#define L_BRACE_PARSE		2
#define R_BRACE_PARSE		3
#define COMMA_PARSE		4
#define COLON_PARSE		5
#define FINISH_PARSE		6

/* The following data structure is used to keep track of information	*/
/* needed to handle ADL string internally. 				*/

struct adl_info
  {
  ST_BOOLEAN comp_name_exists; /* is it a named alternate acces exist ?	*/
  ST_INT named_index;	    /* where exactly is the index of the name ?	*/
  ST_CHAR comp_name[MAX_IDENT_LEN+1]; /* what is the name of the component?*/
  ST_INT nest_count[MAX_NESTING_LEVEL]; /* nesting level per brace	*/
  ST_INT comp_count[MAX_NESTING_LEVEL]; /* comp nesting level		*/
  ST_INT brace_number;
  ST_INT access_state;		/* what to be parsed next ?  		*/
  ST_BOOLEAN started_with_structure_access;/*start could be array or struct*/
  ST_BOOLEAN named_comp_allowed;  /* are named components allowed ?  	*/
  ST_BOOLEAN left_brace_allowed;  /* is left brace allowed ?		*/
  };
typedef struct adl_info ADL_INFO;


/************************************************************************/
/* static variables used in this module					*/
/************************************************************************/

SD_CONST static ST_CHAR seps[] = " []<>\t\n:,";
SD_CONST static ST_CHAR ch_seps[] = "[]<>:,";

/************************************************************************/
/* Internal functions for this module					*/
/************************************************************************/
static ST_RET get_alternate_access (ST_CHAR *pString,
			           ALT_ACCESS *alt_acc,
				   ADL_INFO *info);
static ST_RET get_structure_access (ST_CHAR **ppString,
				   ALT_ACCESS *alt_acc,
			           ADL_INFO *info);
static ST_RET get_array_access (ST_CHAR **ppString,
			       ALT_ACCESS *alt_acc,
			       ADL_INFO *info);
static ST_RET process_r_brace (ST_CHAR **ppString,
			      ALT_ACCESS *alt_acc,
			      ADL_INFO *info);
static ST_RET process_comma (ST_CHAR **ppString,
			      ALT_ACCESS *alt_acc,
			      ADL_INFO *info);
static ST_RET process_l_brace (ST_CHAR **ppString,ADL_INFO *info);
static ST_RET process_colon (ST_CHAR **ppString,ADL_INFO *info);
static ST_RET end_of_parsing (ALT_ACCESS *alt_acc,ADL_INFO *info);
static ST_RET get_named_component (ST_CHAR **ppString,ST_CHAR *token);
static ST_RET get_index_identifier (ST_CHAR **ppString,ALT_ACCESS *alt_acc);
static ST_RET get_sel_type (ST_CHAR **ppString,ST_INT type);
static ST_VOID copy_component_name (ALT_ACCESS *alt_acc,ADL_INFO *info);
static ST_RET isnumber (ST_CHAR *pString);
static ST_RET is_iso_identifier (ST_CHAR *pString);
static ST_VOID point_to_next_character (ST_INT length,ST_CHAR **ppString);
static ST_RET token_copy (ST_CHAR *token,ST_CHAR *pString,ST_INT max_token_length);
static ST_RET traverse_array (ALT_ACCESS *, ST_INT, ST_CHAR *,ST_INT);
static ST_RET get_alt_acc (ALT_ACCESS *);
static ST_RET separator_copy (ST_CHAR *adl,ST_INT *i,ST_CHAR separator, ST_INT max_adl_len);
static ST_RET comp_copy (ST_CHAR *adl,ST_INT *i,ST_CHAR *comp, ST_INT max_adl_len);

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

/************************************************************************/
/*			ms_adl_to_aa					*/
/* Function to parse ADL (Alternate Access Definition Language) and	*/
/* put into alternate structure						*/
/* Input : adl_str User allocates and frees it				*/
/* Output : alt_acc User allocate and frees it				*/
/* Output : alt_acc->aa : This function allocates and user frees it	*/
/* The grammar of ADL is as follows					*/
/*									*/
/* <Alternate Access> ::= <Structure Access> | <Array Access>		*/
/* <Structure Access> ::= <Named Struct. Access> | 			*/
/*			  <Unnamed Struct. Access>			*/
/* <Named Struct. Access> ::= <Named Component><Unnamed Struct. Access>	*/
/* <Unnamed Struct. Access> ::=<Component Access> |			*/
/*		        <Component Access><Comma><Structure Access>	*/
/* <Component Access> ::= <Component> | <Component><Alternate Access>	*/
/* <Array Access> ::="["<Index Identifier>{<Colon><Structure Access>}"]"*/ 
/*			| "["<Index Identifier>{<Array Access>}"]"	*/
/* <Component> ::= ISO Identifier					*/
/* <Named Component> ::= "<"<Component>">"				*/
/* <Index Identifier> ::= <Index> | <Index Range> | <All Elements>	*/
/* <Index Range> ::= <Low Index><Comma><Num of Elements>		*/
/* <Low Index> ::= <Index>						*/
/* <Num of Elements> ::= <Index>					*/
/* <Index> ::= <Digit> | <Digit><Index>					*/
/* <Digit> ::= "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"*/
/* <All Elements> ::= "*"						*/
/* <Comma> ::= ","							*/
/* <Colon> ::= ":"							*/
/************************************************************************/

ST_RET ms_adl_to_aa (ST_CHAR *adl_str,ALT_ACCESS *alt_acc)
  {
ST_RET ret_value;
ST_CHAR *string;
ST_INT str_length;
ADL_INFO *info;

  S_LOCK_COMMON_RESOURCES ();

/* initialize local variables			*/
  ret_value = MVE_INVALID_ADL;
  str_length = strlen (adl_str);
  string = (ST_CHAR *) chk_calloc (1,str_length+1);
  info = (ADL_INFO *) chk_calloc (1,sizeof (ADL_INFO));

/* initialize alt_acc 				*/
  alt_acc->num_aa = 0;
  alt_acc->aa = (ALT_ACC_EL *) chk_calloc (MAX_NUM_AA,sizeof (ALT_ACC_EL));

/* make the duplicate copy of the adl_string and use it in the function	*/
/* calls in case any function corrupts the string			*/
  strcpy (string,adl_str);

/* initialize some state control variables				*/
  info->comp_name_exists = SD_FALSE;
  info->named_comp_allowed = SD_FALSE;
  info->left_brace_allowed = SD_TRUE;

/* start parsing							*/
  ret_value = get_alternate_access (string,alt_acc,info);

/* free what we allocated in this function				*/
  chk_free (info);
  chk_free (string);
  if (ret_value != SD_SUCCESS)
    chk_free (alt_acc->aa);

/* Take care of housekeeping						*/
  if (ret_value == SD_SUCCESS)
    {
  /* Log the alternate access, if selected				*/
    if (mms_debug_sel & MMS_LOG_AA)
      ms_log_alt_access (alt_acc);
  /* save the high water mark						*/
    if (alt_acc->num_aa > m_hw_dec_aa)
      m_hw_dec_aa = alt_acc->num_aa;
    }
  S_UNLOCK_COMMON_RESOURCES ();
  return (ret_value);
  }

/************************************************************************/
/*                           ms_aa_to_adl                         	*/
/* Function to extract information from alternate access structure and	*/
/* make ADL string as output						*/	
/* Input : alt_acc user allocates and frees it				*/
/* Output : adl_str : user is responsible and frees it			*/
/************************************************************************/
ST_RET ms_aa_to_adl (ALT_ACCESS *alt_acc, ST_CHAR *adl_str,ST_INT max_adl_len)
  {
ST_INT num_of_els;
ST_RET ret;

  S_LOCK_COMMON_RESOURCES ();
  num_of_els = get_alt_acc (alt_acc);
  if (num_of_els == 0)
    ret = MVE_INVALID_ADL; 
  else
    ret = traverse_array (alt_acc, num_of_els, adl_str,max_adl_len);

  S_UNLOCK_COMMON_RESOURCES ();
    return (ret);
  }

/************************************************************************/
/* 			get_alternate_access				*/
/* This function parses the passed string pString sequentially. While	*/
/* parsing, this function works on state basis and calls the appropriate*/
/* function on the basis. The called functions set the next state	*/
/************************************************************************/

static ST_RET get_alternate_access (ST_CHAR *pString,
				   ALT_ACCESS *alt_acc,
				   ADL_INFO *info)
  {
ST_INT ret_value;
ST_BOOLEAN all_done;

  ret_value = SD_SUCCESS;
  all_done = SD_FALSE;
  point_to_next_character (0,&pString);
  if (*pString == L_BRACE) 
    {
    info->brace_number++;
    info->access_state = ARRAY_ACCESS;
    info->started_with_structure_access = SD_FALSE;
    point_to_next_character (1,&pString);
    }
  else
    {
    info->access_state = STRUCTURE_ACCESS;
    info->started_with_structure_access = SD_TRUE;
    }

  while ((all_done==SD_FALSE) && (ret_value == SD_SUCCESS))
    {
    switch (info->access_state)
      {
      case STRUCTURE_ACCESS :	
        ret_value = get_structure_access (&pString,alt_acc,info);
      break;

      case ARRAY_ACCESS :
        ret_value = get_array_access (&pString,alt_acc,info);
      break;	

      case L_BRACE_PARSE :
	ret_value = process_l_brace (&pString,info);
      break;   
	
      case R_BRACE_PARSE :
	ret_value = process_r_brace (&pString,alt_acc,info);	
      break;

      case COMMA_PARSE :
	ret_value = process_comma (&pString,alt_acc,info);
      break;		

      case COLON_PARSE :
	ret_value = process_colon (&pString,info);
      break;	

      case FINISH_PARSE :
	ret_value = end_of_parsing (alt_acc,info);	
        all_done = SD_TRUE;
      break;

      default:
	MLOG_NERR1("Unexpected %s",pString);
        ret_value = MVE_INVALID_ADL;
      break;	

      }
    }

  if (ret_value != SD_SUCCESS)
    return (MVE_INVALID_ADL);

/* Stack should be empty because all braces should be matched by now	*/
  if (info->brace_number)
    {
    MLOG_NERR0("Braces not matched");
    return (MVE_INVALID_ADL);
    }

  return (SD_SUCCESS);
  }

/************************************************************************/
/*			get_structure_access				*/
/* This function expects the syntax like <name1>comp1 or comp1 in the 	*/
/* beginning. It parses until the component ,checks the next character	*/
/* and sets the next state						*/
/************************************************************************/

static ST_RET get_structure_access (ST_CHAR **ppString,
				   ALT_ACCESS *alt_acc,
				   ADL_INFO *info)
  {
ST_CHAR token[MAX_IDENT_LEN+1];
ST_INT token_length;
ST_RET ret_value;
ST_CHAR *pString;
ALT_ACC_EL *aa;

  ret_value = SD_SUCCESS;
  pString = *ppString;
  aa = alt_acc->aa+alt_acc->num_aa;
  if (*pString == L_ANGLE_BRACKET) /* alternate access has a name	*/
    {
    if (info->comp_name_exists == SD_TRUE) /* name already defined	*/
      {
      MLOG_NERR1("component name is allowed only once.Error at %s",pString);
      return (MVE_INVALID_ADL);
      }
    info->comp_name_exists = SD_TRUE;
    if (get_named_component (&pString,info->comp_name) != SD_SUCCESS)
      return (MVE_INVALID_ADL);
    }
  if (!(token_length = token_copy (token,pString,MAX_IDENT_LEN)))
    {
    MLOG_NERR1("Expected component at %s",pString);
    return (MVE_INVALID_ADL);
    }
  if (is_iso_identifier (token) == SD_FALSE)
    {
    MLOG_NERR1("Component %s is not a valid identifier",token);
    return (MVE_INVALID_ADL);
    }
/* set the aa pointer to the current structure to be filled in		*/
  aa = alt_acc->aa+alt_acc->num_aa; 

/* start filling the structure						*/
  aa->comp_name_pres = info->comp_name_exists;
  if (aa->comp_name_pres)
    strcpy (aa->comp_name,info->comp_name);
  strcpy (aa->u.component,token);
  aa->sel_type = get_sel_type (&pString,AA_COMP);

  if ((info->comp_name_exists == SD_TRUE) && (aa->sel_type == AA_COMP || aa->sel_type == AA_COMP_NEST))
    info->named_index = alt_acc->num_aa;
  if (++alt_acc->num_aa > MAX_NUM_AA)
    {
    MLOG_NERR0("Number of elements in the structure exceeded the limit");	
    return (MVE_INVALID_ADL);
    }
  point_to_next_character (token_length,&pString);  				

/* Setting the next access state					*/
  switch (*pString)
    {
    case L_BRACE :
      info->access_state = L_BRACE_PARSE;
    break;

    case R_BRACE : 
      info->access_state = R_BRACE_PARSE;
    break;

    case COMMA :  
      info->access_state = COMMA_PARSE;
    break;	

    case NULL_CHARACTER :
      info->access_state = FINISH_PARSE;
    break;		

    default :
      MLOG_NERR1("Unexpected %s",pString);
      ret_value = MVE_INVALID_ADL;		
      info->access_state = -1;
    break;
    }
/* update argumet with the current position of the pointer to string	*/
  *ppString = pString;
  return (ret_value);
  }	

/************************************************************************/
/*			get_array_access				*/
/* This function expects the syntax like 2:comp1 or 2[3 or 2,3 in the	*/
/* beginning. It parses to get the index identifier, checks the next 	*/
/* character and sets the next state					*/
/************************************************************************/

static ST_RET get_array_access (ST_CHAR **ppString,
				   ALT_ACCESS *alt_acc,
				   ADL_INFO *info)
 {
ST_CHAR *pString;
ST_RET ret_value;

  ret_value = SD_SUCCESS;
  pString = *ppString;
  if (info->left_brace_allowed == SD_FALSE)
    {
    MLOG_NERR1("Syntax error at %s",pString);
    return (MVE_INVALID_ADL);
    }
  if (get_index_identifier (&pString,alt_acc) != SD_SUCCESS)
    return (MVE_INVALID_ADL);
  if (++alt_acc->num_aa > MAX_NUM_AA)
    {
    MLOG_NERR0("Number of elements in the structure exceeded the limit");	
    return (MVE_INVALID_ADL);
    }
/* Setting the next access  state		*/						

  switch (*pString)
    {
    case L_BRACE :
      info->access_state = L_BRACE_PARSE;
    break;

    case R_BRACE : 
      info->access_state = R_BRACE_PARSE;
    break;

    case COLON :
      info->access_state = COLON_PARSE;	
    break;

    default :
      MLOG_NERR1("Unexpected %s",pString);
      ret_value = MVE_INVALID_ADL;	
      info->access_state = -1;
    break;
    }
/* update argumet with the current position of the pointer to string	*/
  *ppString = pString;
  return (ret_value);
  }

/************************************************************************/
/*			process_l_brace					*/
/* If the left brace is encountered then we expect array access.We also	*/
/* keep track of nesting associated with this brace 			*/
/************************************************************************/

static ST_RET process_l_brace (ST_CHAR **ppString,ADL_INFO *info)
  {
ST_CHAR *pString;

  pString = *ppString;
  if (AA_STACK_FULL (info))
    {
    MLOG_NERR0("Nesting Level exceeded");
    return (MVE_INVALID_ADL);
    }
/* we are nesting one level now	associated with this brace		*/
  info -> nest_count[info->brace_number++] = 1;
  point_to_next_character (1,&pString);	
  if (isdigit (*pString) || (*pString == ASTERISK))
    info->access_state = ARRAY_ACCESS;
  else 
    info->access_state = STRUCTURE_ACCESS;
  *ppString = pString;
  return (SD_SUCCESS);
  }

/************************************************************************/
/*			process_r_brace					*/
/* Right brace is used to end the array access. This function removes 	*/
/* the right brace from the passed string, checks the next character	*/
/* and sets the next state						*/
/************************************************************************/

static ST_RET process_r_brace (ST_CHAR **ppString,
			      ALT_ACCESS *alt_acc,
			      ADL_INFO *info)
  {
ST_CHAR *pString;
ST_RET ret_value;
ST_INT i;
ST_INT brace_number;
ALT_ACC_EL *aa;

  ret_value = SD_SUCCESS;
  aa = alt_acc->aa+alt_acc->num_aa;
  pString = *ppString;

  info->left_brace_allowed = SD_FALSE;
  if (AA_STACK_EMPTY (info))
    {
    MLOG_NERR1("Syntax Error due to right brace at %s",pString);
    return (MVE_INVALID_ADL);
    }
  brace_number = -- info->brace_number;
  for (i=0;i<info->nest_count[brace_number];i++,aa++)
    {
    aa->sel_type = AA_END_NEST;
    if (++alt_acc->num_aa > MAX_NUM_AA)
      {
      MLOG_NERR0("Number of elements in the structure exceeded the limit");	
      return (MVE_INVALID_ADL);
      }
    }
  point_to_next_character (1,&pString);	
  switch (*pString)
    {
    case R_BRACE : 
      info->access_state = R_BRACE_PARSE;
    break;

    case COMMA :  
      /* comma is used to separate between different alternate accesses	*/
      /* it is invalid between between two array accesses like [2],[3]	*/
      if ((info->started_with_structure_access == SD_TRUE) || (info->brace_number))	
	{
        info->access_state = COMMA_PARSE;
	}
      else
	{
	info->access_state = -1;	
	MLOG_NERR1("Comma invalid at %s",pString);
	ret_value = MVE_INVALID_ADL;
	}
    break;	

    case NULL_CHARACTER :
      info->access_state = FINISH_PARSE;
    break;

    default :
      info->access_state = -1;
      MLOG_NERR1("Unexpected %s",pString);
      ret_value = MVE_INVALID_ADL;
    break;
    }
  *ppString = pString;
  return (ret_value);
  }

/************************************************************************/
/*				process_comma				*/
/* If the comma is encountered then we expect structure access		*/
/************************************************************************/

static ST_RET process_comma (ST_CHAR **ppString,
			      ALT_ACCESS *alt_acc,
			      ADL_INFO *info)
  {
  info->named_comp_allowed = SD_TRUE;
  info->access_state = STRUCTURE_ACCESS;
  info->left_brace_allowed = SD_TRUE;
/* it is time to copy the name of the alternate access if there is any	*/
  if (info->comp_name_exists)
    copy_component_name (alt_acc,info);
  point_to_next_character (1,ppString);
  if (**ppString == NULL_CHARACTER)
    {
    MLOG_NERR0("Comma unexpected at the end");
    return (MVE_INVALID_ADL);
    }
  return (SD_SUCCESS);
  }

/************************************************************************/
/*				process_colon				*/		
/* Colon means array index is nested with structure elements so we	*/
/* increment the nesting level of the brace before colon by one		*/
/************************************************************************/

static ST_RET process_colon (ST_CHAR **ppString,ADL_INFO *info)
  {
ST_RET brace_number;

  if (AA_STACK_EMPTY (info))
    {
    MLOG_NERR1("Syntax Error due to colon at %s",*ppString);
    return (MVE_INVALID_ADL);
    }
  brace_number = info->brace_number - 1;
  info -> nest_count[brace_number] ++;
  info->access_state = STRUCTURE_ACCESS;
  point_to_next_character (1,ppString);	
  return (SD_SUCCESS);
  }

/************************************************************************/
/*			end_of_parsing					*/
/* At the end of parsing it checks if the name of the alternate access	*/
/* needs to be copied into the structure				*/
/************************************************************************/

static ST_RET end_of_parsing (ALT_ACCESS *alt_acc,ADL_INFO *info)
  {
ALT_ACC_EL *aa;

  aa = alt_acc->aa+alt_acc->num_aa;
  if ((info->comp_name_exists) && (info->named_comp_allowed))
    copy_component_name (alt_acc,info);
  return (SD_SUCCESS);
  }

/************************************************************************/
/*			get_named_component				*/
/* This function extracts "name1" from the syntax <name1> and points	*/
/* to first non white space character after ">"				*/
/************************************************************************/

static ST_RET get_named_component (ST_CHAR **ppString,ST_CHAR *token)
  {
ST_INT token_length;
ST_CHAR *pString;

  pString = *ppString;
  point_to_next_character (1,&pString);  				
  if (!(token_length = token_copy (token,pString,MAX_IDENT_LEN)))
    {
    MLOG_NERR1("Expected name for the Alternate Access at %s",pString);
    return (MVE_INVALID_ADL);
    }
  if (is_iso_identifier (token) == SD_FALSE)
    {
    MLOG_NERR1("not a valid ISO identifier %s",token);
    return (MVE_INVALID_ADL);
    }
  point_to_next_character (token_length,&pString); 	
  if (*pString != R_ANGLE_BRACKET)/* expect R_ANGLE_BRACKET	*/
    {
    MLOG_NERR1("Right Angle Bracket expected at %s",pString);
    return (MVE_INVALID_ADL);
    }
  point_to_next_character (1,&pString);  /* remove white spaces between R_ANGLE_BRACKET*/
  *ppString = pString;
  return (SD_SUCCESS);
  }

/************************************************************************/
/*			get_index_identifier				*/
/* This function is called by the function get_array_access		*/
/* Index Identifier could be one of these three types		 	*/
/* <Index> 								*/
/* <Index Range> ::= <Low Index><Comma><Num of Elements>		*/
/* <All Elements> ::= "*"						*/
/* where <Index>, <Low Index> and <Num of Elements> are decimal numbers	*/  
/************************************************************************/

static ST_RET get_index_identifier (ST_CHAR **ppString,ALT_ACCESS *alt_acc)
  {
ST_CHAR *pString;
ST_INT token_length;
ST_CHAR token[MAX_IDENT_LEN+1];
ALT_ACC_EL *aa;

  aa = alt_acc->aa+alt_acc->num_aa;
  pString = *ppString;
  if (!(token_length = token_copy (token,pString,MAX_IDENT_LEN)))
    {
    MLOG_NERR1("Expected an index after [ at %s",pString);
    return (MVE_INVALID_ADL);
    }
  point_to_next_character (token_length,&pString);
  switch (*pString)
    {
    case L_BRACE :
    case R_BRACE :
    case COLON	 :
      if ((*token == ASTERISK) && (token_length == 1))
        {
        aa->sel_type = get_sel_type (&pString,AA_ALL);
        }
      else 
        {
        if (isnumber (token) == SD_FALSE)
	  {
          MLOG_NERR1("Expected a number or '*' at %s",token);
          return (MVE_INVALID_ADL);
	  }
	aa->u.index = atol (token);
        aa->sel_type = get_sel_type (&pString,AA_INDEX);
        }
    break;

    case COMMA :
      if (isnumber (token) == SD_FALSE)
	{
        MLOG_NERR1("Expected a number at %s",token);
        return (MVE_INVALID_ADL);
	}
      aa->u.ir.low_index = atol (token);
      point_to_next_character (1,&pString);
      if (!(token_length = token_copy (token,pString,MAX_IDENT_LEN+1)))
        {
        MLOG_NERR1("Expected an index at %s",pString);
        return (MVE_INVALID_ADL);
        }
      point_to_next_character (token_length,&pString);
      if ((*pString != L_BRACE) && (*pString != R_BRACE)
				   && (*pString != COLON))
	{
        MLOG_NERR2("Unexpected character %c at %s", *pString,pString);
        MLOG_NERR0("Expected characters  []:");
        return (MVE_INVALID_ADL);
	}
      if (isnumber (token) == SD_FALSE) 
	{
        MLOG_NERR1("Expected a number at %s",token);
        return (MVE_INVALID_ADL);
	}
      aa->u.ir.num_elmnts = atol (token);
      aa->sel_type = get_sel_type (&pString,AA_INDEX_RANGE);
    break;

    default : 
      MLOG_NERR2("Unexpected character %c at %s ",*pString,pString);
      MLOG_NERR0("Expected characters :  [],:");
      return (MVE_INVALID_ADL);
    break;
    }
  *ppString = pString;
  return (SD_SUCCESS);
  }

/************************************************************************/
/*				get_sel_type				*/
/* This function determines sel_type for Alternate Access if it nested	*/
/* or not depending on the next separator. If type is nested it		*/
/* increments a static variable nesting_count which is used to fill the	*/
/* structure with sel_type of AA_END_NEST at the end of parsing. If type*/
/* is not nested, it returns the passed type. This function does not 	*/
/* alter the passed string						*/
/************************************************************************/

static ST_RET get_sel_type (ST_CHAR **ppString, ST_INT type)
  {
ST_CHAR *next_separator;

  next_separator = strpbrk (*ppString,ch_seps);
  if (!next_separator)
    return (type);

  switch (type)
    {
    case AA_COMP :
      if (*next_separator == L_BRACE)
        type = AA_COMP_NEST;
    break;

    case AA_INDEX :
      if ((*next_separator == L_BRACE) || (*next_separator == COLON))
        type = AA_INDEX_NEST;	
    break;

    case AA_INDEX_RANGE :
      if ((*next_separator == L_BRACE) || (*next_separator == COLON))
        type = AA_INDEX_RANGE_NEST;
    break;

    case AA_ALL :
      if ((*next_separator == L_BRACE) || (*next_separator == COLON))
        type = AA_ALL_NEST;
    break;
    }
  return (type);
  }

/************************************************************************/
/*			copy_component_name				*/
/* This function copies the name of the alternate access to the 	*/
/* structure at the right place						*/
/************************************************************************/

static ST_VOID copy_component_name (ALT_ACCESS *alt_acc,ADL_INFO *info)
  {
ALT_ACC_EL *aa;
  aa = alt_acc -> aa + info->named_index;
  strcpy (aa->comp_name,info->comp_name);
  aa->comp_name_pres = SD_TRUE;
  info->comp_name_exists = SD_FALSE;
  }

/************************************************************************/
/*				isnumber				*/
/* This function checks if the passed string contains only digits	*/
/************************************************************************/

static ST_RET isnumber (ST_CHAR *pString)
  {
  while (*pString)
    {
    if (isdigit (*pString++)==0)
      return (SD_FALSE);
    }
  return (SD_TRUE);
  }

/************************************************************************/
/*			is_iso_identifier				*/
/* This function checks the health of ISO identifier. ISO identifier	*/
/* can not start with a number and it can have alphanumeric characters	*/
/* underscore '_' and dollar sign '$'					*/
/************************************************************************/

static ST_RET is_iso_identifier (ST_CHAR *pString)
  {
  if (isdigit (*pString))	 /* the first letter may not be a digit  */
    return (SD_FALSE);
  while (*pString)
    {
    if ((!(isalnum (*pString))) &&
	 (!(*pString == '_')) &&
	 (!(*pString == '$')))
      return (SD_FALSE);
    pString++;
    }
  return (SD_TRUE);
  }  
	
/************************************************************************/
/*			point_to_next_character				*/
/* This function wacks all the white spaces and pointer will  be	*/
/* pointing to the next character in the string or null character if 	*/
/* there are no more characters in the string				*/
/************************************************************************/

static ST_VOID point_to_next_character (ST_INT length,ST_CHAR **ppString)
  {
  *ppString += length;
  while (isspace (**ppString))
   (*ppString)++;
  }

/************************************************************************/
/*			token_copy					*/
/* This funtion gets the token from the string and returns the strlen	*/
/************************************************************************/

static ST_RET token_copy (ST_CHAR *token,ST_CHAR *pString,ST_INT max_token_length)
  {
ST_INT counter;

  counter = 0;
  while ((strchr (seps,*pString)==NULL) && (counter < max_token_length))
    {
    *token++ = *pString++;
    counter++;
    }
  *token = '\0';
  return (counter);
  }
  
/************************************************************************/
/*			get_alt_acc					*/
/* This function is called by ms_adl_to_string				*/
/************************************************************************/
static ST_RET get_alt_acc (ALT_ACCESS *alt_acc)
  {
ALT_ACC_EL *aa;
ST_INT el_count;
ST_INT error;
ST_INT index_count;	     /*keep track of nesting */
ST_INT comp_nest_count[MAX_NESTING_LEVEL];
ST_INT x;

  error = SD_FALSE;
  x = -1;
  index_count = 0;
  aa = alt_acc->aa;
  for (el_count = 0; el_count < alt_acc->num_aa; ++el_count, ++aa)
    {
    switch (aa->sel_type)
      {
      case AA_COMP_NEST :
	  index_count++;
	  comp_nest_count[++x] = index_count;
      case AA_COMP :
      break;

      case AA_INDEX_NEST :
        index_count++;
      case AA_INDEX :
      break;

      case AA_INDEX_RANGE_NEST :
        index_count++;
      case AA_INDEX_RANGE :
      break;

      case AA_ALL_NEST :
        index_count++;
      case AA_ALL :
      break;
      case AA_END_NEST :
	if (index_count == comp_nest_count[x] && x >= 0)
	  comp_nest_count[x--] = 0;
        index_count--;
      break;

      default : 			/* should not be any other tag	*/
	MLOG_NERR0("Bad sel_type");
        error = SD_TRUE; 
      break;
      }
    if (error)
      break;
    }

  if (!error && el_count && !index_count)
    {
    alt_acc->num_aa = el_count;
    return (el_count);
    }
  return (0);
  }

/************************************************************************/
/*                         traverse_array                               */
/*  Interprets each element of AA structure and puts correct ADL syntax */
/*  into an array.  This function is called through aa_to_adl.		*/ 
/************************************************************************/
static ST_RET traverse_array (ALT_ACCESS *alt_acc, 
			    ST_INT count, 
			    ST_CHAR *adl,
			    ST_INT max_adl_len)
  {
ADL_INFO info;
ALT_ACC_EL *aa;
ST_INT loop_count;
ST_INT temp_count;
ST_INT prev_sel_type;   /* Initialize to meaningless value	*/
ST_INT lbrace_count;
ST_INT rbrace_count;
ST_INT x;
ST_INT y;
ST_INT len, len2;
ST_INT i;
ST_CHAR ch[MAX_IDENT_LEN];		    /* Max length for unsigned long integer */
ST_CHAR *pCount;
ST_CHAR *temp_ptr;

  prev_sel_type = 99;   /* Initialize to meaningless value	*/
  x = -1;
  y = 0;
  i = 0;

  pCount = adl;
  temp_ptr = adl;
  memset (&info, 0, sizeof (ADL_INFO));
  aa = alt_acc->aa;
  info.nest_count[0] = 0;
  info.comp_count[0] = 0;
  
  switch (aa->sel_type)
    {
    case AA_INDEX :
    case AA_INDEX_RANGE	:
    case AA_ALL	:
    case AA_ALL_NEST :
    case AA_INDEX_NEST :
    case AA_INDEX_RANGE_NEST :
      info.access_state = ARRAY_ACCESS;
      info.started_with_structure_access = SD_FALSE;
      if (separator_copy (adl, &i, L_BRACE, max_adl_len) != SD_SUCCESS)
        return (MVE_INVALID_ADL);
      break;
    default:  
      info.access_state = STRUCTURE_ACCESS;
      info.started_with_structure_access = SD_TRUE;
      break;
    }

  for (loop_count = 0; loop_count < count; loop_count++, aa++)
    {
    switch (aa->sel_type)
      {
      case AA_COMP:
	if ((prev_sel_type == AA_COMP) && (info.nest_count[0]))
          {
	  i--;
          if (separator_copy (adl, &i, COMMA, max_adl_len) != SD_SUCCESS)
            return (MVE_INVALID_ADL);
	  }
        else if ((prev_sel_type == AA_END_NEST) || (prev_sel_type == AA_COMP))
	  {
          if (separator_copy (adl, &i, COMMA, max_adl_len) != SD_SUCCESS)
            return (MVE_INVALID_ADL);
	  }
	if ((prev_sel_type == AA_INDEX_RANGE_NEST) || (prev_sel_type == AA_INDEX_NEST))
	  {
	  i--;
          if (separator_copy (adl, &i, L_BRACE, max_adl_len) != SD_SUCCESS)
            return (MVE_INVALID_ADL);
	  }
	else if ((prev_sel_type == AA_INDEX) || (prev_sel_type == AA_INDEX_RANGE)
                || (prev_sel_type == AA_ALL_NEST))
	  {
          if (separator_copy (adl, &i, L_BRACE, max_adl_len) != SD_SUCCESS)
            return (MVE_INVALID_ADL);
	  }
	if (prev_sel_type == AA_COMP_NEST)
	  {
          if (separator_copy (adl, &i, L_BRACE, max_adl_len) != SD_SUCCESS)
            return (MVE_INVALID_ADL);
	  }
	if ((aa->comp_name_pres) && (!info.comp_count[0]))
	  {
          if (separator_copy (adl, &i, L_ANGLE_BRACKET, max_adl_len) != SD_SUCCESS)
            return (MVE_INVALID_ADL);
          if (comp_copy (adl, &i, aa->comp_name, max_adl_len) != SD_SUCCESS)
	    return (MVE_INVALID_ADL);
          if (separator_copy (adl, &i, R_ANGLE_BRACKET, max_adl_len) != SD_SUCCESS)
            return (MVE_INVALID_ADL);
	  }
        if (comp_copy (adl, &i, aa->u.component, max_adl_len) != SD_SUCCESS)
	   return (MVE_INVALID_ADL);
	len = strlen (aa->u.component);
        len2 = strlen (aa->comp_name);
	if (adl[i - len - 1] == L_BRACE || prev_sel_type == AA_COMP_NEST ||
	   (aa->comp_name_pres && adl[i - len - len2 - 3] == L_BRACE))
	  if (separator_copy (adl, &i, R_BRACE, max_adl_len) != SD_SUCCESS)
            return (MVE_INVALID_ADL);

        info.comp_name_exists = SD_TRUE;
	prev_sel_type = AA_COMP;
        break;

      case AA_COMP_NEST:
        if (prev_sel_type == AA_END_NEST)
	  {
          if (separator_copy (adl, &i, COMMA, max_adl_len) != SD_SUCCESS)
            return (MVE_INVALID_ADL);
	  }

	if ((prev_sel_type == AA_INDEX_NEST) && adl[i - 1] == R_BRACE)
	  {
	  i--;
          if (separator_copy (adl, &i, L_BRACE, max_adl_len) != SD_SUCCESS)
            return (MVE_INVALID_ADL);
	  }
        else if ((prev_sel_type == AA_INDEX_NEST) || (prev_sel_type == AA_INDEX_RANGE_NEST)
                      || (prev_sel_type == AA_ALL_NEST))
	  {
          if (separator_copy (adl, &i, L_BRACE, max_adl_len) != SD_SUCCESS)
            return (MVE_INVALID_ADL);
	  }

	if (prev_sel_type == AA_COMP)
	  {
          if (separator_copy (adl, &i, COMMA, max_adl_len) != SD_SUCCESS)
            return (MVE_INVALID_ADL);
	  }

	if (prev_sel_type == AA_COMP_NEST)
	  {
          if (separator_copy (adl, &i, L_BRACE, max_adl_len) != SD_SUCCESS)
            return (MVE_INVALID_ADL);
	  }

	if ((aa->comp_name_pres) && (!info.comp_count[0]))
	  {
          if (separator_copy (adl, &i, L_ANGLE_BRACKET, max_adl_len) != SD_SUCCESS)
            return (MVE_INVALID_ADL);
          if (comp_copy (adl, &i, aa->comp_name, max_adl_len) != SD_SUCCESS)
	   return (MVE_INVALID_ADL);
          if (separator_copy (adl, &i, R_ANGLE_BRACKET, max_adl_len) != SD_SUCCESS)
            return (MVE_INVALID_ADL);
	  }
        if (comp_copy (adl, &i, aa->u.component, max_adl_len) != SD_SUCCESS)
	   return (MVE_INVALID_ADL);
        info.comp_name_exists = SD_TRUE;
	prev_sel_type = AA_COMP_NEST;
        info.nest_count[y] = AA_COMP_NEST;
	info.comp_count[++x] = info.nest_count[y++];
        break;


      case AA_INDEX_NEST:
        info.nest_count[y++] = AA_INDEX_NEST;
        if (prev_sel_type == AA_END_NEST)
	  {
          if (separator_copy (adl, &i, COMMA, max_adl_len) != SD_SUCCESS)
            return (MVE_INVALID_ADL);
	  }

	if ((prev_sel_type == AA_INDEX_RANGE_NEST) || (prev_sel_type == AA_COMP_NEST) ||
	                  (prev_sel_type == AA_ALL_NEST))
	  {
          if (separator_copy (adl, &i, L_BRACE, max_adl_len) != SD_SUCCESS)
            return (MVE_INVALID_ADL);
	  }

	if (prev_sel_type == AA_INDEX_NEST)
          {
	  i--;
          if (separator_copy (adl, &i, L_BRACE, max_adl_len) != SD_SUCCESS)
            return (MVE_INVALID_ADL);
	  }
	sprintf (ch, "%lu", aa->u.index);
	if (strlen (ch) > MAX_IDENT_LEN)
	  return (MVE_INVALID_ADL);
        if (comp_copy (adl, &i, ch, max_adl_len) != SD_SUCCESS)
          return (MVE_INVALID_ADL);
        if (separator_copy (adl, &i, R_BRACE, max_adl_len) != SD_SUCCESS)
          return (MVE_INVALID_ADL);
	prev_sel_type = AA_INDEX_NEST;
        break;

      case AA_INDEX:
        if (prev_sel_type == AA_END_NEST)
	  {
          if (separator_copy (adl, &i, COMMA, max_adl_len) != SD_SUCCESS)
            return (MVE_INVALID_ADL);
	  }

	if (prev_sel_type == AA_INDEX_RANGE_NEST)
	  {
	  --i;
          if (separator_copy (adl, &i, L_BRACE, max_adl_len) != SD_SUCCESS)
            return (MVE_INVALID_ADL);
	  }
	if ((prev_sel_type == AA_COMP_NEST) ||(prev_sel_type == AA_ALL_NEST))
	  {
          if (separator_copy (adl, &i, L_BRACE, max_adl_len) != SD_SUCCESS)
            return (MVE_INVALID_ADL);
	  }

	if (prev_sel_type == AA_INDEX_NEST)
          {
	  i--;
          if (separator_copy (adl, &i, L_BRACE, max_adl_len) != SD_SUCCESS)
            return (MVE_INVALID_ADL);
	  }
	sprintf (ch, "%lu", aa->u.index);
	if (strlen (ch) > MAX_IDENT_LEN)
	  return (MVE_INVALID_ADL);
        if (comp_copy (adl, &i, ch, max_adl_len) != SD_SUCCESS)
          return (MVE_INVALID_ADL);
        if (separator_copy (adl, &i, R_BRACE, max_adl_len) != SD_SUCCESS)
          return (MVE_INVALID_ADL);
	prev_sel_type = AA_INDEX_NEST;
        break;

      case AA_INDEX_RANGE_NEST:
        info.nest_count[y++] = AA_INDEX_RANGE_NEST;
      case AA_INDEX_RANGE:
	if (prev_sel_type == AA_INDEX_NEST)
          {
	  i--;
          if (separator_copy (adl, &i, L_BRACE, max_adl_len) != SD_SUCCESS)
            return (MVE_INVALID_ADL);
	  }
        if (prev_sel_type == AA_END_NEST)
	  {
          if (separator_copy (adl, &i, COMMA, max_adl_len) != SD_SUCCESS)
            return (MVE_INVALID_ADL);
	  }
        if (prev_sel_type == AA_COMP_NEST) 
	  {
          if (separator_copy (adl, &i, L_BRACE, max_adl_len) != SD_SUCCESS)
            return (MVE_INVALID_ADL);
	  }
	if (prev_sel_type == AA_INDEX_RANGE_NEST)
	  {
	  i--;
          if (separator_copy (adl, &i, L_BRACE, max_adl_len) != SD_SUCCESS)
            return (MVE_INVALID_ADL);
	  }
	sprintf (ch, "%lu", aa->u.ir.low_index);
	if (strlen (ch) > MAX_IDENT_LEN)
	  return (MVE_INVALID_ADL);
        if (comp_copy (adl, &i, ch, max_adl_len) != SD_SUCCESS)
          return (MVE_INVALID_ADL);
        if (separator_copy (adl, &i, COMMA, max_adl_len) != SD_SUCCESS)
          return (MVE_INVALID_ADL);
	sprintf (ch, "%lu", aa->u.ir.num_elmnts);
	if (strlen (ch) > MAX_IDENT_LEN)
	  return (MVE_INVALID_ADL);
        if (comp_copy (adl, &i, ch, max_adl_len) != SD_SUCCESS)
          return (MVE_INVALID_ADL);
        if (separator_copy (adl, &i, R_BRACE, max_adl_len) != SD_SUCCESS)
          return (MVE_INVALID_ADL);
	prev_sel_type = AA_INDEX_RANGE_NEST;
        break;	

      case AA_ALL_NEST:
        info.nest_count[y++] = AA_ALL_NEST;
      case AA_ALL:
        if (prev_sel_type == AA_END_NEST)
	  {
          if (separator_copy (adl, &i, COMMA, max_adl_len) != SD_SUCCESS)
            return (MVE_INVALID_ADL);
	  }
        if ((prev_sel_type == AA_COMP_NEST) || (prev_sel_type == AA_ALL_NEST))
	  {
          if (separator_copy (adl, &i, L_BRACE, max_adl_len) != SD_SUCCESS)
            return (MVE_INVALID_ADL);
	  }

        if (separator_copy (adl, &i, ASTERISK, max_adl_len) != SD_SUCCESS)
          return (MVE_INVALID_ADL);
	prev_sel_type = AA_ALL_NEST;
        break;

      case AA_END_NEST:
	if ((prev_sel_type != AA_END_NEST) && (adl[i - 1] == R_BRACE))
	  {
	  i--;
	  adl[i] = 0;
	  }
	y--;
	if (info.nest_count[y] == info.comp_count[x] && x >= 0)
	  info.comp_count[x--] = 0;

        lbrace_count = rbrace_count = 0;

        if (separator_copy (adl, &i, R_BRACE, max_adl_len) != SD_SUCCESS)
          return (MVE_INVALID_ADL);

	if (y == 0)
	  {
	  for (temp_count = 0; (temp_count <= (&adl[i] - temp_ptr)) && 
	                       (*pCount != 0); temp_count++, pCount++)
            { 
            if (*pCount == L_BRACE)
              lbrace_count++;
            if (*pCount == R_BRACE)
              rbrace_count++;
            }
	  }

	adl[i] = 0;
        prev_sel_type = AA_END_NEST;
        break;
      default:
        MLOG_NERR1("Invalid sel_type : %d", aa->sel_type);
        return (MVE_INVALID_ADL);
	break;
      }
    }

  pCount = adl;              
  lbrace_count = rbrace_count = 0;
 
  for (temp_count = 0; (temp_count <= i) && (*pCount != 0); temp_count++, pCount++)
    { 
    if (*pCount == L_BRACE)
      lbrace_count++;
    if (*pCount == R_BRACE)
      rbrace_count++;
    }
  rbrace_count = lbrace_count - rbrace_count;
  for (temp_count = 0; temp_count < rbrace_count; temp_count++)
    {
    *pCount = R_BRACE;
    pCount++;
    }
  *pCount = 0;  
  return (SD_SUCCESS);
  }

/************************************************************************/
/* 			separator_copy					*/
/* This function takes start of string pointer, current string pointer	*/
/* the string we want to copy and max len allowed, Before we copy str	*/
/* into adl[*i] we validate for the overrun				*/
/************************************************************************/
static ST_RET separator_copy (ST_CHAR *adl,ST_INT *i,ST_CHAR sep, ST_INT max_adl_len)
  { 
  if (*i + 1 >= max_adl_len)
    return (SD_FAILURE);
  adl[*i] = sep;
  *i += 1;
  adl[*i] = 0;	/* Insure end of string is recognized */
  return (SD_SUCCESS);
  }
/************************************************************************/

/************************************************************************/
static ST_RET comp_copy (ST_CHAR *adl,ST_INT *i,ST_CHAR *comp, ST_INT max_adl_len)
  { 
ST_INT comp_len;

  comp_len = strlen (comp);
  if (*i + comp_len + 1 >= max_adl_len)
    return (SD_FAILURE);
  strcpy (&adl[*i], comp);
  *i += comp_len;
  return (SD_SUCCESS);
  }

/************************************************************************/
/*			ms_adl_to_asn1_2				*/
/* Function to parse ADL (Alternate Access Definition Language) and	*/
/* generate the ASN.1 encoding of the Alternate Access Definition.	*/
/* NOTE: Suffix (_2) is just to avoid naming conflict with older funct.	*/
/* NOTE: this function is thread-safe as long as the encode buffer	*/
/*   passed as arg (buf_ptr) is not being used by another thread.	*/
/************************************************************************/
ST_RET ms_adl_to_asn1_2 (ST_CHAR *pAdl,	/* ADL string 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	*/
  {
ALT_ACCESS stAltAcc;
ST_RET retcode;

  /* First, convert from ADL string to ALT_ACCESS structure.		*/
  /* NOTE: if successful, this allocates "stAltAcc.aa". Be sure to free it.*/
  retcode = ms_adl_to_aa(pAdl, &stAltAcc); 	/* get ALT_ACCESS 	*/
  if (retcode == SD_SUCCESS)
    {
    /* Next, convert from ALT_ACCESS structure to ASN.1 encoding.	*/
    retcode = ms_aa_to_asn1_2 (&stAltAcc, buf_ptr, buf_len,
              asn1_ptr_out, asn1_len_out);
    chk_free (stAltAcc.aa);	/* free array allocated by ms_adl_to_aa	*/
    }
  return (retcode);
  }
