/*
 * Copyright (c) 1993 Spider Systems Limited
 *
 * This Source Code is furnished under Licence, and may not be
 * copied or distributed without express written agreement.
 *
 * All rights reserved.  Made in Scotland.
 *
 * Authors: Angus Grossart
 *
 * construct.c of lmi module
 *
 * SpiderISDN
 * Release 3.0.0 94/12/20
 * 
 * SpiderISDN Release 3 - Official Release.
 */

/* 
 * construct.c - source for constructing LMI request lists
 * =======================================================
 *
 * This file contains some useful utility functions for constructing instance
 * request lists.
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include	"lmi_inc.h"

bool_t lmi_einfo = TRUE;

int construct_inst( classid_t class,   
                    int count,         
                    list_info_t *info )
/*++

Routine Description:

   This function appends a class and AVL count (ie instance identifier) to the 
   current instance list.  It returns updated size and list information in the
   list information (info) structure.

Arguments:

   class - class identifier
   count - AVL count for class instance
   info - the current request list information

Return Value:

--*/
{
	inst_t	inst;

	if ( (info->size + (int)FIXED_ID_SIZE) > info->limit)
	{
		LMI_EINFO("LMI list construction: Out of memory\n");
		LMI_EINFO("Primitive: construct_inst\n");
		return(LMI_ERR);
	}
	inst.i_class = class;
	inst.count = count;
   memmove( info->wptr, (char *)&inst, FIXED_ID_SIZE );
	info->wptr += FIXED_ID_SIZE;
	info->size += FIXED_ID_SIZE;
	return(LMI_OK);
} /* construct_inst */


int lmi_construct_int_AVL( attid_t att,
                           int val,
                           list_info_t *info,
                           int att_op ) 
/*++

Routine Description:

   This function appends an integer AVL to an instance list.
   It updates the size and current list pointer position

Arguments:

   att - an attribute identifier
   val - an integer valued attribute value
   info - the current request list information
   att_op;

Return Value:

   STATUS_SUCCESS

--*/
{
	avl_t	*avlptr;
	int	*intptr;

	if ( (info->size + (int)FIXED_AVL_SIZE + (int)sizeof(int)) > info->limit)
	{
		LMI_EINFO("LMI list construction: Out of memory\n");
		LMI_EINFO("Primitive: construct_int_AVL\n");
		return(LMI_ERR);
	}
	avlptr = (avl_t*)info->wptr;
	avlptr->att = att;
	avlptr->len = sizeof(int);
	avlptr->err = att_op;
	intptr = (int*)avlptr->val;
	*intptr = val;		/* copy int to avl			*/
	info->size += avl_len((octet_t*)avlptr);
	info->wptr += avl_len((octet_t*)avlptr);
	return(LMI_OK);
} /* construct_int_AVL */
 
   
  
int lmi_construct_str_AVL( attid_t att,
                           char *val,
                           list_info_t *info,
                           int att_op)
/*++

Routine Description:

   This function appends a string AVL to an instance list.
   It updates the size and current list pointer position 

Arguments:

   att - an attribute identifier
   val - an string valued attribute value
   info - the current request list information
   att_op -

Return Value:

--*/	
{
	avl_t	*avlptr;
	int	out;
  
	if ( (info->size + (int)FIXED_AVL_SIZE + (int)strlen(val)) > info->limit)
	{
		LMI_EINFO("LMI list construction: Out of memory\n");
		LMI_EINFO("Primitive: construct_str_AVL\n");
		return(LMI_ERR);
	}
	avlptr = (avl_t*)info->wptr;
	avlptr->att = att;
	avlptr->len = strlen(val)+1;
	avlptr->err = att_op;
	strcpy(avlptr->val,val);	/* copy string to avl	*/
	info->size += avl_len((octet_t*)avlptr);
	info->wptr += avl_len((octet_t*)avlptr);
	if ( out = ((int)info->wptr & 3) )
		{
		avlptr->len += (4 - out);
		info->wptr += (4 - out);
		info->size += (4 - out);
		}
	return(LMI_OK);
} /* construct_str_AVL */


int lmi_construct_any_AVL( attid_t att,
                           octet_t *val,
                           int size,
                           list_info_t *info,
                           int att_op )
/*++

Routine Description:

   This function appends a arbitrarily complex AVL to an instance list.
   It updates the size and current list pointer position
   The function performs rounding to the next word boundary.

Arguments:

   att - an attribute identifier
   val - an abitrarily complex attribute value
   size - the size in octets of the attribute value
   info - the current request list information
   att_op;

Return Value:

--*/
{
	avl_t	*avlptr;
	int	out;

	if ( (info->size + (int)FIXED_AVL_SIZE + size) > info->limit)
	{
		LMI_EINFO("LMI list construction: Out of memory\n");
		LMI_EINFO("Primitive: construct_any_AVL\n");
		return(LMI_ERR);
	}
	avlptr = (avl_t*)info->wptr;
	avlptr->att = att;
	avlptr->len = size;
	avlptr->err = att_op;
	memmove( avlptr->val,(char*)val,size ); 
	info->size += avl_len((octet_t*)avlptr);
	info->wptr += avl_len((octet_t*)avlptr);
	if ( out = ((int)info->wptr & 3) )
	{
		avlptr->len += (4 - out);
		info->wptr += (4 - out);
		info->size += (4 - out);
	}
	return(LMI_OK);
} /* construct_any_AVL */


int lmi_construct_pk_AVL( octet_t *val,
                          int size,
                          list_info_t *info,
                          int att_op )
/*++

Routine Description:

   This function appends a arbitrarily complex primary key AVL to an
   instance list.
   It updates the size and current list pointer position
   The function performs rounding to the next word boundary.
   Note this operation is similar to construct_any_AVL except the attid
   is not required.

Arguments:

   val - an abitrarily complex attribute value
   size - the size in octets of the attribute value
   info - the current request list information
   att_op

Return Value:

--*/
{
	avl_t	*avlptr;
	int	out;

	if ( (info->size + (int)FIXED_AVL_SIZE + size) > info->limit)
	{
		LMI_EINFO("LMI list construction: Out of memory\n");
		LMI_EINFO("Primitive: construct_pk_AVL\n");
		return(LMI_ERR);
	}
	avlptr = (avl_t*)info->wptr;
	avlptr->att = AVL_OK; /* this field is ignored for primary keys */
	avlptr->len = size;
	avlptr->err = att_op;
	memmove( avlptr->val,(char*)val,size ); 
	info->size += avl_len((octet_t*)avlptr);
	info->wptr += avl_len((octet_t*)avlptr);
	if ( out = ((int)info->wptr & 3) )
		{
		avlptr->len += (4 - out);
		info->wptr += (4 - out);
		info->size += (4 - out);
		}
	return(LMI_OK);
} /* construct_pk_AVL */


int lmi_construct_dummy_pk_AVL( list_info_t *info,
                                int att_op )
/*++

Routine Description:

   This function appends a dummy primary key AVL to an instance list.
   It updates the size and current list pointer position

Arguments:

   info - the current request list information
   att_op

Return Value:

--*/
{
	avl_t	*avlptr;
	int	*intptr;

	if ( (info->size + (int)FIXED_AVL_SIZE + (int)sizeof(int)) > info->limit)
	{
		LMI_EINFO("LMI list construction: Out of memory\n");
		LMI_EINFO("Primitive: construct_pk_AVL\n");
		return(LMI_ERR);
	}
	avlptr = (avl_t*)info->wptr;
	avlptr->att = AVL_OK; 
	avlptr->len = sizeof(int);
	avlptr->err = att_op;
	intptr = (int*)avlptr->val;
	*intptr = AVL_OK;
	info->size += avl_len((octet_t*)avlptr);
	info->wptr += avl_len((octet_t*)avlptr);
	return(LMI_OK);
} /* construct_dumy_pk_AVL */

 
int lmi_construct_zero_AVL( attid_t att,
                            int size,
                            list_info_t *info,
                            int att_op )
/*++

Routine Description:

   This function appends an arbitrary complex AVL to an instance list.
   The contents (value field) of this newly appended AVL is set to zero
   It updates the size and current list pointer position               
   The function performs rounding to the next word boundary.           
   The error code is also set to zero.                                 

Arguments:

   att -an attribute identifier
   size - the size in octets of the attribute value
   info - the current request list information
   att_op

Return Value:

--*/
{
	avl_t	*avlptr;
	int	out;

	if ( (info->size + (int)FIXED_AVL_SIZE + size) > info->limit)
	{
		LMI_EINFO("LMI list construction: Out of memory\n");
		LMI_EINFO("Primitive: construct_zero_AVL\n");
		return(LMI_ERR);
	}
	avlptr = (avl_t*)info->wptr;
	avlptr->att = att;
	avlptr->len = size;
	avlptr->err = att_op;
	memset(avlptr->val,0,size);
	info->size += avl_len((octet_t*)avlptr);
	info->wptr += avl_len((octet_t*)avlptr);
	if ( out = ((int)info->wptr & 3) )
	{
		avlptr->len += (4 - out);
		info->wptr += (4 - out);
		info->size += (4 - out);
	}
	return(LMI_OK);
} /* construct_zero_AVL */


int set_AVL_op( int op,
                list_info_t *info )
/*++

Routine Description:

   This function set the AVL operation for a downstream request
   Valid operations are: 						
   	AVL_REPLACE					            
   	AVL_ADD						            
   	AVL_REMOVE					            
   	AVL_DEFAULT					            
   The information list write pointer must be pointing at current AVL

Arguments:

   op - the operation desired
   info - the current request list information

Return Value:

--*/
{
	avl_t	*avlptr;
	
	switch(op)
	{
	case AVL_REPLACE:
	case AVL_ADD:
	case AVL_REMOVE:
	case AVL_DEFAULT:
		avlptr = (avl_t*)info->wptr;
		avlptr->err = AVL_REPLACE;
		return(LMI_OK);
	default:
		LMI_EINFO("set_AVL_op: Invalid operation\n");
		return(LMI_ERR);
	}

} /* set_AVL_op */

 
int set_AVL_error( int op,
                   list_info_t *info )
/*++

Routine Description:

   This function set the AVL operation for a downstream request		   
   Valid operations are: 						  
   The information list read pointer must be pointing at current AVL 	

Arguments:

   op - the operation desired
   info - the current request list information

Return Value:

--*/
{
	avl_t	*avlptr;
	
	switch(op)
	{
	case AVL_OK:
	case AVL_E_ATTR:
	case AVL_E_INST:
	case AVL_E_CLASS:
	case AVL_E_RDONLY:
	case AVL_E_WRONLY:
	case AVL_E_SYSERR:
	case AVL_E_TEMP:
		avlptr = (avl_t*)info->rptr;
		avlptr->err = op;
		return(LMI_OK);
	default:
		LMI_EINFO("set_AVL_op: Invalid operation\n");
		return(LMI_ERR);
	}

} /* set_AVL_error */


avl_len( octet_t *list )
/*++

Routine Description:

   This function returns the total size of the AVL currently pointed to by
   list.                                                                  

Arguments:

   list - an arbitrary octet list

Return Value:

--*/
{
	int     size;
	int     *ptr;
	
	list = list + sizeof(classid_t);
	ptr = (int*)list;
	size = (*ptr) + FIXED_AVL_SIZE;
	return(size);
} /* avl_len */


void next_avl( octet_t **list )
/*++

Routine Description:

   This function aligns the list pointer to the start of the next AVL in the
   list.                                                                    

Arguments:

Return Value:

--*/
{
	int     size;
	
	size = avl_len(*list);
	*list = *list + size;
} /* next_avl */


allocate_LMI_request( list_info_t *req,
                      int size )
/*++

Routine Description:

   This function allocates an LMI request list of size, size bytes.       

Arguments:

Return Value:

--*/
{
	req->size = 0;
	req->base = NULL;
	req->limit = 0;
	req->wptr = NULL;
	req->rptr = NULL;
	if ( (req->rptr = (octet_t*)malloc(size)) == (octet_t*)NULL )
	{
		LMI_EINFO("LMI list construction: Heap exhausted\n");
		LMI_EINFO("Primitive: allocate_LMI_request\n");
		return(LMI_ERR);
	}

	req->wptr = req->rptr;
	req->limit = size;
	req->base = req->rptr;
	return(LMI_OK);
} /* allocate_LMI_request */


deallocate_LMI_request( list_info_t *req )
/*++

Routine Description:

   This function deallocates an LMI request list pointed to by start      

Arguments:

Return Value:

--*/
{
	if (req->base == NULL)
	{
		LMI_EINFO("LMI list construction: No space allocated\n");
		LMI_EINFO("Primitive: deallocate_LMI_request\n");
		return(LMI_ERR);
	}

	free(req->base);

	req->size = 0;
	req->base = NULL;
	req->limit = 0;
	req->rptr = NULL;
	req->wptr = NULL;
	return( LMI_OK );
} /* deallocate_LMI_request */


skip_avl( list_info_t *req )
/*++

Routine Description:

   This function skips past the current AVL in the request list             
   It operates on a request list information struture and updates it.       

Arguments:

Return Value:

--*/
{
	int     size;

	if ( (size = avl_len(req->rptr)) + req->rptr > req->wptr)
	{
		LMI_EINFO("LMI list construction: Cannot skip past end of list\n");
		LMI_EINFO("Primitive: skip_avl\n");
		return(LMI_ERR);
	}
	req->rptr += size;
	return(LMI_OK);
} /* skip_avl */


skip_header( list_info_t *req )
/*++

Routine Description:

   This function skip_header skips past the header information in a	    
   request.  It operates on a request list information structure and     
   updates it.                                                           

Arguments:

Return Value:

--*/
{
	if ( (req->rptr + FIXED_ID_SIZE ) > req->wptr )
	{
		LMI_EINFO("LMI list construction: Cannot skip past end of list\n");
		LMI_EINFO("Primitive: skip_header\n");
		return(LMI_ERR);
	}
	req->rptr = req->rptr + FIXED_ID_SIZE;
	return(LMI_OK);
} /* skip_header */


request_has_error( list_info_t *req )
/*++

Routine Description:

   This function traverses an LMI request list for AVL error codes.         
   If there are any errors present it returns LMI_ERR otherwise LMI_OK      
   The function exits on the first error code other than AVL_OK found       

Arguments:

Return Value:

--*/
{
	int count;
	int instance_count;

	req->rptr = req->base; /* reset read pointer */
	while (req->rptr < req->wptr)
	{
		/* process instance */
		instance_count = LMI_COUNT(req);
		if (skip_header(req) != LMI_OK)
			return(LMI_ERR);
		for (count=0;count < instance_count;count++)
			/* process an AVL including primary key */
			{
			if ( LMI_ERROR(req) != AVL_OK )
				return(LMI_ERR);
			if (skip_avl(req) != LMI_OK)
				return(LMI_ERR);
			}
	} /* while */
	req->rptr = req->base; /* reset read pointer */
	return(LMI_OK);
} /* request_has_error */


int request_has_error_type( list_info_t *req,
                            int error )
/*++

Routine Description:

   This function traverses an LMI request list for a specific AVL error code.							                                                 
   Function returns the error type if error found otherwise AVL_OK.         
   If error located, on exit the req->rptr(read pointer) identifies         
   offending AVL tuple.

Arguments:

Return Value:

--*/
{
	int count;
	int instance_count;

	req->rptr = req->base; /* reset read pointer */
	while (req->rptr < req->wptr)
	{
		/* process instance */
		instance_count = LMI_COUNT(req);
		if (skip_header(req) != LMI_OK)
			return(LMI_ERR);
		for (count=0;count < instance_count;count++)
			/* process an AVL including primary key */
			{
			if ( LMI_ERROR(req) == error )
				return(error);
			if (skip_avl(req) != LMI_OK)
				return(LMI_ERR);
			}
	} /* while */
	req->rptr = req->base; /* reset read pointer */
	return(LMI_OK);
} /* request_has_error_type */


lmi_print_error( list_info_t *req )
/*++

Routine Description:

   This function traverses an LMI request list for AVL error codes.  
   If there are any errors present it prints the errro codes on std error
   The function exits on the first error code other than AVL_OK found      

Arguments:

Return Value:

--*/
{
	int count;
	int instance_count;
	

	req->rptr = req->base; /* reset read pointer */
	while (req->rptr < req->wptr)
	{
		/* process instance */
		instance_count = LMI_COUNT(req);
		if (skip_header(req) != LMI_OK)
			return LMI_ERR;
		for (count=0;count < instance_count;count++)
			/* process an AVL including primary key */
		{
			if ( LMI_ERROR(req) != AVL_OK )
			{
				LMI_EINFOV((
					"AVL instance %d Class %d Attribute ID %d Error %d \n",
						count,*LMI_CLASS(req),
						 *LMI_ATTRIBUTE(req),
						  LMI_ERROR(req)))
			}
			if (skip_avl(req) != LMI_OK)
				return LMI_ERR;
		}
	} /* while */
	req->rptr = req->base; /* reset read pointer */
	return(LMI_OK);
} /* lmi_print_error */
