/*
 * Routines dealing with format descriptor strings (FDS).
 *
 * Copyright (c) 1996-2002 by Guardsoft Pty Ltd.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 */

#ifdef HAVE_CONFIG_H
#include	<config.h>
#endif /* HAVE_CONFIG_H */

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

#include	<ctype.h>
#include	<stdlib.h>

#include	"aif.h"
#include	"aiferr.h"
#include	"aifint.h"

static char	_fds_type_str[NUM_AIF_TYPES][12] = 
{
	{ FDS_INVALID, '%', 'd', FDS_INVALID, '\0' },

	{ FDS_INTEGER, '%', 'c', '%', 'd', '\0' },

	{ FDS_FLOATING, '%', 'd', '\0' },

	{ FDS_POINTER, '%', 's', '%', 's', '\0' },

	{ FDS_ARRAY_START, '%', 's', FDS_ARRAY_END, '%', 's', '\0' },

	{ FDS_AGGREGATE_START, '%', 's', FDS_TYPENAME_END, ';', ';', ';', FDS_AGGREGATE_END, '\0' },

	{ FDS_UNION_START, '%', 's', FDS_TYPENAME_END, FDS_UNION_END, '\0' },

	{ FDS_FUNCTION, FDS_FUNCTION_ARG_END, '%', 's', '\0' },

	{ FDS_VOID, '%', 'd', '\0' },

	{ FDS_REGION, '%', 'd', '%', 's', '\0' },

	{ '%', FDS_NAME, '%', 'd', FDS_NAME_END, '%', 's', '\0' },

	{ FDS_REFERENCE, '%', 'd', FDS_REFERENCE_END, '\0' },

	{ FDS_STRING, '\0' },

	{ FDS_CHARACTER, '\0' },

	{ FDS_RANGE, '%', 'd', FDS_RANGE_SEP, '%', 'd', '%', 's', '\0' },

	{ FDS_ENUM_START, '%', 's', FDS_TYPENAME_END, FDS_ENUM_END, FDS_INTEGER, '%', 'c', '4', '\0' },

	{ FDS_BOOLEAN, '%', 'd', '\0' },
	
	{ FDS_ADDRESS, '%', 'd', '\0' },

	{ FDS_CHAR_POINTER, '%', 's', '\0' }
};

static char _fds_array_end[] = {FDS_ARRAY_END, '\0'};
static char _fds_function_arg_end[] = {FDS_FUNCTION_ARG_END, '\0'};
static char _fds_name_end[] = {FDS_NAME_END, '\0'};
static char _fds_enum_end[] = {FDS_ENUM_END, '\0'};
static char _fds_enum_const_name_end[] = {FDS_ENUM_SEP, '\0' };
static char _fds_enum_const_end[] = { FDS_ENUM_CONST_SEP, FDS_ENUM_END, '\0' };
static char _fds_aggregate_access_sep[] = {FDS_AGGREGATE_ACCESS_SEP, '\0'};
static char _fds_aggregate_field_name_end[] = {FDS_AGGREGATE_FIELD_NAME_END, '\0' };
static char _fds_aggregate_field_end[] = { FDS_AGGREGATE_ACCESS_SEP, FDS_AGGREGATE_FIELD_SEP, FDS_AGGREGATE_END, '\0' };
static char _fds_union_field_name_end[] = {FDS_UNION_FIELD_NAME_END, '\0' };
static char _fds_union_field_end[] = { FDS_UNION_FIELD_SEP, FDS_UNION_END, '\0' };

#define MAX_CALLS	4

static char *	_fds_buf[MAX_CALLS];
static int		_fds_pos = 0;

char *
_fds_skipnum(char *fds)
{
	if ( fds == NULL )
		return NULL;

	while ( *fds != '\0' && isdigit((int)*fds) )
		fds++;

	return fds;
}

/*
 * Skip the name field. Leaves fds pointing
 * at the first character after the end of name.
 * Does nothing if there is no name field.
 */
void
_fds_skip_name(char **fds)
{
	if (**fds == FDS_NAME) {
		*fds = _fds_skipto(*fds, _fds_name_end);
		(*fds)++;
	}
}

/*
 * Skip to the location of a character from 'set' in the supplied
 * type string.
 *
 * Returns a pointer to the character if located, or
 * to the NULL character if not.
 *
 * Only searches the "top level" of the type. If the type contains
 * nested structured types, these will be skipped.
 */
char *
_fds_skipto(char *fds, char *set)
{
	char *	s;

	if ( fds == NULL )
		return NULL;

	for ( ; *fds != '\0' ; fds++ )
	{
		if
		(
			*fds == FDS_AGGREGATE_START
			|| 
			*fds == FDS_UNION_START
			|| 
			*fds == FDS_ARRAY_START
			|| 
			*fds == FDS_ENUM_START
		)
		{
			fds = _fds_skiptomatch(fds);
			fds++;
		}

		for ( s = set ; *s != '\0' ; s++ )
			if ( *s == *fds )
				return fds;
	}

	return fds;
}

/* 
 * Convert an ascii number to a number. NOTE: silently truncates
 * the number to fit in sizeof int.
 */

int
_fds_getnum(char *str)
{
//	int	n;
//	char *	p;
//	char *	t;
//
//	p = _fds_skipnum(str);
//
//	if ( *p == '\0' )
//		return strtoul(str, NULL, 10);
//
//	n = p - str;
//	t = (char *)_aif_alloc(n + 1);
//	memcpy(t, str, n);
//	t[n] = '\0';
//
//	n = strtoul(t, NULL, 10);
//
//	_aif_free(t);
//
//	return n;
	return strtoul(str, NULL, 10);
}

int
FDSType(char *str)
{
	int	type;

	if 
	(
		str == NULL 
		|| 
		*str == '\0'
	)
		return AIF_INVALID;

	switch ( *str )
	{
	case FDS_BOOLEAN: /* boolean */
		type = AIF_BOOLEAN; 	
		break; 		

	case FDS_CHARACTER: /* character */
		type = AIF_CHARACTER;
		break;

	case FDS_INTEGER: /* integer */
		type = AIF_INTEGER;
		break;

	case FDS_FLOATING: /* floating */
		type = AIF_FLOATING;
		break;

	case FDS_POINTER: /* pointer */
		type = AIF_POINTER;
		break;

	case FDS_ARRAY_START: /* array */
		type = AIF_ARRAY;
		break;

	case FDS_AGGREGATE_START:  /* struct or class */
		type = AIF_AGGREGATE;
		break;

	case FDS_UNION_START:  /* union */
		type = AIF_UNION;
		break;

	case FDS_ENUM_START: /* enumeration */
		type = AIF_ENUM;
		break;

	case FDS_RANGE: /* range */
		type = AIF_INTEGER;
		break;

	case FDS_FUNCTION: /* function */
		type = AIF_FUNCTION;
		break;

	case FDS_STRING: /* string */
		type = AIF_STRING;
		break;

	case FDS_CHAR_POINTER: /* char pointer */
		type = AIF_CHAR_POINTER;
		break;

	case FDS_ADDRESS: /* address */
		type = AIF_ADDRESS;
		break;

	case FDS_VOID: /* void */
		type = AIF_VOID;
		break;

	case FDS_REGION: /* ZPL region */
		type = AIF_REGION;
		break;

	case FDS_NAME: /* named component */
		type = AIF_NAME;
		break;

	case FDS_REFERENCE: /* reference to named component*/
		type = AIF_REFERENCE;
		break;

	default:
		type = AIF_INVALID;
	}

	return type;
}

/* extracts the base type of a pointer or array type, specified by the     */
/* type descriptor.                                                        */

char *
FDSBaseType(char *type)
{
	do
	{
		if ( (type = _fds_base_type(type)) == NULL )
			return NULL;
	}
	while ( FDSType(type) == AIF_ARRAY || FDSType(type) == AIF_POINTER );

	return type;
}

char *
_fds_base_type(char *type)
{
	char *	p;

	if ( type == NULL || *type == '\0' )
		return NULL;

	switch ( *type++ )
	{
	case FDS_FUNCTION: /* function */
		p = _fds_skipto(type, _fds_function_arg_end);
		p++;
		break;

	case FDS_POINTER: /* pointer */
		_fds_advance(&type); /* skip address */
		p = type;
		break;
		
	case FDS_ARRAY_START: /* array */
		p = _fds_skipto(type, _fds_array_end);
		p++;
		break;

	case FDS_ENUM_START: /* enum */
		p = _fds_skipto(type, _fds_enum_end);
		p++;
		break;

	case FDS_RANGE: /* range */
		p = _fds_skipnum(type); /* skip MinValue */
		p += 2;         /* skip '..' */
		p = _fds_skipnum(p); /* skip MaxValue */
		break;

	case FDS_REGION: /* region */
		p = type + 1;	/* skip FDS_REGION Rank */
		break;

	default:
		return NULL;
	}

	return p;
}

/* In AIF, we can find out about the size of the AIF object by looking at the
 * AIF_LEN(), but this method cannot be used to find out about the size
 * of each individual element in a aggregate. To overcome this problem, we can use
 * FDSTypeSize() with the FDS for each element.
 * 
 * However, FDSTypeSize() cannot be used to know the real size if the aggregate
 * contains references or strings (since we also need to access the data to
 * know the size of these data types).
 * 
 * We use FDSDataSize() to overcome the limitation of FDSTypeSize(). It can 
 * access FDS and the data of an AIF object to calculate the (real) size of 
 * the data.
 */

int
FDSDataSize(char *fds, char *data)
{
        char * tmp1 = fds;
        char * tmp2 = data;

		ResetAIFError();

        _fds_skip_data(&tmp1, &tmp2);

        if ( AIFError() == AIFERR_NOERR )
                return (tmp2-data);
        else
                return -1;
}

/*
 * Gets the size of a type in bytes, specified by the type descriptor.
 * If the size cannot fit in a int, then it is silently truncated.
 * returns size if successful, -1 otherwise.
 *
 * Note: we no longer know the size of aggregates or unions so these are
 * always -1.
 */
int
FDSTypeSize(char *type)
{
	int	s;
	int	rank;
	int	size;
	int	subsize;

	if ( type == NULL || *type == '\0' )
		return -1;

	switch ( *type )
	{
	case FDS_ADDRESS:
	case FDS_BOOLEAN: /* boolean */
		return _fds_getnum(type + 1);

	case FDS_CHARACTER: /* character */
		return 1;

	case FDS_INTEGER: /* integer */
		return _fds_getnum(type + 2);

	case FDS_REFERENCE: /* reference use */
		return 0;

	case FDS_NAME: /* named component */
		return FDSTypeSize(strchr(type, FDS_NAME_END) + 1);

	case FDS_FUNCTION: /* function */
		return FDSTypeSize(strchr(type, FDS_FUNCTION_ARG_END) + 1);

	case FDS_POINTER: /* pointer */
		return FDSTypeSize(type+1);

	case FDS_FLOATING: /* floating */
	case FDS_VOID: /* void */
		return _fds_getnum(type + 1);

	case FDS_AGGREGATE_START: /* aggregate */
		type++; /* past open brace */
		size = 0;
		subsize = 0;

		_fds_skip_typename(&type);
		
		while ( *type != FDS_AGGREGATE_END )
		{
			if ( *type == FDS_AGGREGATE_ACCESS_SEP )
			{
				type++;
				continue;
			}

			type = strchr(type, FDS_AGGREGATE_FIELD_NAME_END) + 1;

			/* to start of field */
			if ( (subsize = FDSTypeSize(type)) < 0 )
				return -1;

			size += subsize;

			_fds_advance(&type);

			if ( *type == FDS_AGGREGATE_FIELD_SEP )
				type++;
		}
		return size;

	case FDS_UNION_START:
		type++; /* past open brace */
		size = 0;
		subsize = 0;
		
		_fds_skip_typename(&type);
		
		while ( *type != FDS_UNION_END )
		{
			type = strchr(type, FDS_UNION_FIELD_NAME_END) + 1;

			/* to start of field */
			if ( (subsize = FDSTypeSize(type)) < 0 )
				return -1;

			if ( subsize > size )
				size = subsize;

			_fds_advance(&type);

			if ( *type == FDS_UNION_FIELD_SEP )
				type++;
		}
		return size;

	case FDS_ARRAY_START: /* array */
		if ( (s = FDSTypeSize(_fds_base_type(type))) < 0 )
			return -1;

		return s * _fds_array_size(type);

	case FDS_ENUM_START: /* enumeration */
		return 4; /* all enumerations are based on 4-byte integers */

	case FDS_RANGE: /* range */
		return FDSTypeSize(_fds_base_type(type));

	case FDS_REGION: /* region */
		rank = _fds_getnum(type + 1);
		s = FDSTypeSize(_fds_base_type(type));
		return rank * s * 2;

	case FDS_CHAR_POINTER:
	case FDS_STRING:
	default:
		return -1;
	}

	/* NOT REACHED */
}

int
FDSIsSigned(char *fds)
{
	if ( *fds++ == FDS_INTEGER )
		return (int)(*fds == FDS_INTEGER_SIGNED);

	return 0;
}

/*
 * Create an FDS type description. The result does not
 * need to be freed.
 *
 * Calls to TypeToFDS() may be nested as follows:
 * 
 *    TypeToFDS(AIF_ARRAY, 
 *      TypeToFDS(AIF_RANGE, 0, 10, 
 *        TypeToFDS(AIF_INTEGER, 4)
 *      ),
 *      TypeToFDS(AIF_INTEGER, 10)
 *   );
 *
 * TypeToFDS currently only allows two nested calls. If
 * an FDS type is created requiring more than two, this will
 * need to be increased.
 */
char *
TypeToFDS(int type, ...)
{
	va_list	args;
	int		v1;
	int		v2;
	char *	v3;
	char *	v4;
	char *	buf;

	if ( type < 0 || type >= NUM_AIF_TYPES )
		type = AIF_INVALID;

	va_start(args, type);

	switch ( type )
	{
	case AIF_INTEGER:
		v1 = va_arg(args, int);
		v2 = va_arg(args, int);
		asprintf(&buf, _fds_type_str[type], v1 ? FDS_INTEGER_SIGNED : FDS_INTEGER_UNSIGNED, v2);
		break;

	case AIF_FLOATING:
	case AIF_VOID:
	case AIF_REFERENCE:
		v1 = va_arg(args, int);
		asprintf(&buf, _fds_type_str[type], v1);
		break;

	case AIF_POINTER:
		v3 = va_arg(args, char *);
		v4 = va_arg(args, char *);
		asprintf(&buf, _fds_type_str[type], v3, v4);
		break;

	case AIF_FUNCTION:
		v3 = va_arg(args, char *);
		asprintf(&buf, _fds_type_str[type], v3);
		break;

	case AIF_ENUM:
		v3 = va_arg(args, char *);
		if (v3 == NULL) {
			v3 = "";
		}
		v1 = va_arg(args, int);
		asprintf(&buf, _fds_type_str[type], v3, v1 ? FDS_INTEGER_SIGNED : FDS_INTEGER_UNSIGNED);
		break;

	case AIF_CHAR_POINTER:
		v3 = va_arg(args, char *);
		asprintf(&buf, _fds_type_str[type], v3);
		break;

	case AIF_ADDRESS:
	case AIF_BOOLEAN:
		v1 = va_arg(args, int);
		asprintf(&buf, _fds_type_str[type], v1);
		break;

	case AIF_STRING:
	case AIF_CHARACTER:
		asprintf(&buf, _fds_type_str[type]);
		break;

	case AIF_AGGREGATE:
	case AIF_UNION:
		v3 = va_arg(args, char *);
		if (v3 == NULL) {
			v3 = "";
		}
		asprintf(&buf, _fds_type_str[type], v3);
		break;

	case AIF_RANGE:
		v1 = va_arg(args, int);
		v2 = va_arg(args, int);
		v3 = va_arg(args, char *);
		asprintf(&buf, _fds_type_str[type], v1, v2, v3);
		break;

	case AIF_ARRAY:
		v3 = va_arg(args, char *);
		v4 = va_arg(args, char *);
		asprintf(&buf, _fds_type_str[type], v3, v4);
		break;

	case AIF_NAME:
	case AIF_REGION:
		v1 = va_arg(args, int);
		v3 = va_arg(args, char *);
		if ( v3 == NULL ) {
			v3 = "";
		}
		asprintf(&buf, _fds_type_str[type], v1, v3);
		break;

	case AIF_INVALID:
	default:
		asprintf(&buf, _fds_type_str[AIF_INVALID], 0);
	}

	va_end(args);

	if (_fds_pos >= MAX_CALLS) {
		_fds_pos = 0;
	}
	if (_fds_buf[_fds_pos] != NULL) {
		free(_fds_buf[_fds_pos]);
	}
	_fds_buf[_fds_pos++] = buf;

	return buf;
}

int
FDSTypeCompare(char *f1, char *f2)
{
	int		n;
	int		res;
	char *		n1;
	char *		n2;
	char *		t1;
	char *		t2;
	AIFIndex *	ix1;
	AIFIndex *	ix2;

	if ( *f1 == FDS_NAME )
	{
		f1 = _fds_skipto(f1, _fds_name_end);
		f1++;
	}

	if ( *f2 == FDS_NAME )
	{
		f2 = _fds_skipto(f2, _fds_name_end);
		f2++;
	}

	if ( FDSType(f1) != FDSType(f2) )
		return 0;

	switch ( FDSType(f1) )
	{
	case AIF_ENUM:
		res = strcmp(f1, f2) == 0;
		break;

	case AIF_INTEGER:
	case AIF_FLOATING:
	case AIF_VOID:
	case AIF_REGION:
	case AIF_ADDRESS:
	case AIF_BOOLEAN:
		res = FDSTypeSize(f1) == FDSTypeSize(f2);
		break;

	case AIF_POINTER:
	case AIF_FUNCTION:
		res = FDSTypeCompare(FDSBaseType(f1), FDSBaseType(f2));
		break;

	case AIF_REFERENCE:
		res = FDSType(f2) == FDSType(f1);
		break;

	case AIF_ARRAY:
		ix1 = FDSArrayIndexInit(f1);
		ix2 = FDSArrayIndexInit(f1);

		if ( ix1->i_rank != ix2->i_rank )
		{
			AIFArrayIndexFree(ix1);
			AIFArrayIndexFree(ix2);
			return 0;
		}

		for ( n = 0 ; n < ix1->i_rank ; n++ )
		{
			if ( ix1->i_size[n] != ix2->i_size[n] )
			{
				AIFArrayIndexFree(ix1);
				AIFArrayIndexFree(ix2);
				return 0;
			}
		}

		res = FDSTypeSize(ix1->i_btype) == FDSTypeSize(ix2->i_btype);

		AIFArrayIndexFree(ix1);
		AIFArrayIndexFree(ix2);
		break;

	case AIF_UNION:
	case AIF_AGGREGATE:
		if ( FDSNumFields(f1) != FDSNumFields(f2) )
		{
			res = 0;
			break;
		}

		for ( n = 0 ; n < FDSNumFields(f1) ; n++ )
		{
			if
			(
				FDSAggregateFieldByNumber(f1, n, &n1, &t1) < 0
				||
				FDSAggregateFieldByNumber(f2, n, &n2, &t2) < 0
			)
				return 0;

			_aif_free(n1);
			_aif_free(n2);

			if ( !FDSTypeCompare(t1, t2) )
			{
				_aif_free(t1);
				_aif_free(t2);
				return 0;
			}

			_aif_free(t1);
			_aif_free(t2);
		}

		res = 1;
		break;

	case AIF_CHAR_POINTER:
	case AIF_STRING:
	case AIF_CHARACTER:
		res = 1;
		break;

	default:
		res = 0;
	}

	return res;
}

/************************************************************
 ********************** ARRAY ROUTINES **********************
 ************************************************************/

/* extracts the index type of an array type, specified by the              */
/* type descriptor.                                                        */
/* index type descriptor is returned if a valid (not a NULL) pointer to    */
/* char is passed as parameter.                                            */
/* returns 0 if successful, -1 otherwise.                                  */

char *
FDSArrayIndexType(char *type)
{
	char *	p;
	char *	pi;
	char *	index;

	if
	(
		type == NULL 
		||
		*type == '\0'
		||
		*type != FDS_ARRAY_START
		||
		*++type != FDS_RANGE
	)
		return NULL;

	p = _fds_skipnum(++type);   /* skip MinValue */
	p += 2;                /* skip '..' */
	p = _fds_skipnum(p);        /* skip MaxValue */

	pi = p;
	p = _fds_skipto(p, _fds_array_end);
	*p = 0;

	index = strdup(pi);

	*p = FDS_ARRAY_END;

	return index;
}

int
_fds_array_min_index(char *type)
{
	if ( *++type != FDS_RANGE )
		return -1;

	return _fds_getnum(++type);
}

int
FDSArrayMinIndex(char *fds, int n)
{
	while ( FDSType(fds) == AIF_ARRAY && n > 0 )
	{
		fds = _fds_base_type(fds);
		n--;
	}

	return (n == 0) ? _fds_array_min_index(fds) : -1;
}

int
_fds_array_size(char *type)
{
	char *	p;

	if ( *++type != FDS_RANGE )
		return -1;

	p = _fds_skipnum(++type);	/* skip MinValue */
	p++;						/* skip ',' */

	return _fds_getnum(p);
}

/*
 * Number of elements in dimension n
 */
int
FDSArrayRankSize(char *fds, int n)
{
	while ( FDSType(fds) == AIF_ARRAY && n > 0 )
	{
		fds = _fds_base_type(fds);
		n--;
	}

	return (n == 0) ? _fds_array_size(fds) : -1;
}

/*
 * Calculate the number of dimensions of an array.
 */
int
FDSArrayRank(char *fds)
{
	int	d = 0;

	while ( FDSType(fds) == AIF_ARRAY )
	{
		fds = _fds_base_type(fds);
		d++;
	}

	return d;
}

/*
 * Calculate the total number of elements in an array.
 * This will count elements in nested arrays.
 */
int
FDSArraySize(char *fds)
{
	int	num;
	int	size = 1;

	while ( FDSType(fds) == AIF_ARRAY )
	{
		num = _fds_array_size(fds);
		fds = _fds_base_type(fds);

		size *= num;
	}

	return size;
}

char *
FDSRangeInit(int min, int size)
{
	return strdup(TypeToFDS(AIF_RANGE, min, size, "is4"));
}

char *
FDSArrayInit(int min, int size, char *btype)
{
	char *	res;
	char *	range = FDSRangeInit(min, size);
	
	res = strdup(TypeToFDS(AIF_ARRAY, range, btype));
	_aif_free(range);
	return res;
}

/*
 * Parse array type descriptor and extract the minimum index value and number
 * of elements for each dimension.
 */
void
FDSArrayBounds(char *fds, int rank, int **min, int **size)
{
	int		i;
	int *		mn;
	int *		sz;

	*min = (int *)_aif_alloc(rank*sizeof(int));
	*size = (int *)_aif_alloc(rank*sizeof(int));

	mn = *min;
	sz = *size;

	for ( i = 0 ; i < rank ; i++) {
		*mn++ = _fds_array_min_index(fds);
		*sz++ = _fds_array_size(fds);
		fds = _fds_base_type(fds);
	}
}

/*
 * Extract info about an array. Returns the index type of the
 * first dimension (assumes all dimensions are the same type),
 * the element type of the array, and the number of dimensions
 * of the array. It is the responsibility of the caller to free
 * memory allocated to el.
 * 
 */
void
FDSArrayInfo(char *fds, int *rank, char **el, char **ix)
{
	int	d;

	if ( ix != NULL )
		*ix = FDSArrayIndexType(fds);

	d = 0;

	while ( FDSType(fds) == AIF_ARRAY )
	{
		fds = _fds_base_type(fds);
		d++;
	}

	*el = strdup(fds);
	*rank = d;
}

AIFIndex *
FDSArrayIndexInit(char *fmt)
{
	int		d;
	int		nel = 1;
	int		rank;
	char *		btype;
	AIFIndex *	ix;

	FDSArrayInfo(fmt, &rank, &btype, NULL);

	if ( rank == 0 )
		return (AIFIndex *)NULL;

	ix = (AIFIndex *)_aif_alloc(sizeof(AIFIndex));
	ix->i_finished = 0;
	ix->i_rank = rank;
	ix->i_btype = btype;
	ix->i_bsize = FDSTypeSize(ix->i_btype);
	ix->i_index = (int *)_aif_alloc(ix->i_rank * sizeof(int));

	FDSArrayBounds(fmt, ix->i_rank, &(ix->i_min), &(ix->i_size));

	for ( d = ix->i_rank - 1 ; d >= 0 ; d-- )
	{
		nel *= ix->i_size[d];
		ix->i_index[d] = ix->i_min[d];
	}

	ix->i_nel = nel;

	return ix;
}

/************************************************************
 ********************** AGGREGATE ROUTINES **********************
 ************************************************************/

/*
 * The fds of a aggregate has this format:
 *
 *  {name|entry,...;entry,...;entry,...;entry,...}
 *
 *  name is the name of the aggregate, or empty for an unnamed type
 *  each entry has the format "name=type"
 *  a aggregate comprises 4 sections separated by ';', corresponding
 *    to public, private, protected and package members
 */

#define AGGREGATE_START(fds, res) \
	if (*(fds) == FDS_NAME) { \
		(fds) = _fds_skipto((fds), _fds_name_end); \
		(fds)++; \
	} \
	if ( *(fds++) != FDS_AGGREGATE_START ) \
		return (res); \
	while ( *(fds) != FDS_TYPENAME_END ) \
		(fds)++; \
	(fds)++; \
	if ( *(fds) == '\0' ) \
		return (res); \
	while ( *(fds) == FDS_AGGREGATE_ACCESS_SEP ) \
		(fds)++; \
	if ( *(fds) == FDS_AGGREGATE_END ) \
		return (res);

/*
 * Skip to matching end of type character.
 *
 * If the fds is not a structured type, skip to end of string.
 *
 * Returns pointer to end of type character or end of string
 * if no match can be found.
 */
char *
_fds_skiptomatch(char *fds)
{
	char	ender = 0;

	switch ( *fds )
	{
	case FDS_AGGREGATE_START:
		ender = FDS_AGGREGATE_END;
		break;

	case FDS_ARRAY_START:
		ender = FDS_ARRAY_END;
		break;

	case FDS_ENUM_START:
		ender = FDS_ENUM_END;
		break;

	case FDS_UNION_START:
		ender = FDS_UNION_END;
		break;
	}

	fds++;

	for ( ;; )
	{
		if ( *fds == ender )
			break;

		if
		(
			*fds == FDS_AGGREGATE_START
			|| 
			*fds == FDS_UNION_START 
			|| 
			*fds == FDS_ARRAY_START 
			|| 
			*fds == FDS_ENUM_START
		)
			fds = _fds_skiptomatch(fds);

		fds++;
	}

	return fds;
}

/*
 * The function ignores access specifiers
 */
char *
_fds_skiptofield(char *fds, int n)
{
	AGGREGATE_START(fds, NULL);

	for ( ; *fds != '\0' && *fds != FDS_AGGREGATE_END && n > 0 ; fds++)
	{
		if ( *fds == FDS_AGGREGATE_FIELD_SEP ) {
			n--;
			continue;
		}

		if ( *fds == FDS_AGGREGATE_ACCESS_SEP ) {
			if 
			( 
			 	*(fds+1) != FDS_AGGREGATE_ACCESS_SEP
				&&
				*(fds+1) != FDS_AGGREGATE_END
			) {
				n--;
			}
			continue;
		}

		if
		(
			*fds == FDS_AGGREGATE_START
			|| 
			*fds == FDS_UNION_START 
			|| 
			*fds == FDS_ARRAY_START 
			|| 
			*fds == FDS_ENUM_START
		) {
			fds = _fds_skiptomatch(fds);
		}
	}

	if ( *fds == '\0' || *fds == FDS_AGGREGATE_END )
		return NULL;

	return fds;
}

/*
 * The function ignores access specifiers, ie: total number of fields
 */
int
FDSNumFields(char *fds)
{
	int	n = 0;

	if (*fds == FDS_NAME) 
	{
		fds = _fds_skipto(fds, _fds_name_end);
		fds++;
	}

	if ( *(fds++) != FDS_AGGREGATE_START )
		return -1;

	while ( *(fds) != FDS_TYPENAME_END )
		(fds)++;
	(fds)++;

	if ( *fds == '\0' )
		return -1;

	while ( *fds == FDS_AGGREGATE_ACCESS_SEP )
		(fds)++;

	if ( *fds == FDS_AGGREGATE_END )
		return 0;

	while ( *fds != '\0' && *fds != FDS_AGGREGATE_END )
	{
		if ( *fds == FDS_AGGREGATE_FIELD_SEP )
			n++;
		else if ( *fds == FDS_AGGREGATE_ACCESS_SEP )
		{
			if 
			( 
			 	*(fds+1) != FDS_AGGREGATE_ACCESS_SEP
				&&
				*(fds+1) != FDS_AGGREGATE_END
			)
				n++;
		}
		else if
		(
			*fds == FDS_AGGREGATE_START
			|| 
			*fds == FDS_UNION_START 
			|| 
			*fds == FDS_ARRAY_START 
			|| 
			*fds == FDS_ENUM_START
		)
			fds = _fds_skiptomatch(fds);

		fds++;
	}

	return n + 1;
}

/*
 * Returns a newly alloced string for name and type
 * The function ignores access specifiers
 */
int
FDSAggregateFieldByNumber(char *fds, int n, char **name, char **type)
{
	if ( (fds = _fds_skiptofield(fds, n)) == NULL )
		return -1;

        if ( (*name = _field_attribute(fds, NULL, _fds_aggregate_field_name_end)) == NULL )
		return -1;

        if ( (*type = _field_attribute(fds, _fds_aggregate_field_name_end, _fds_aggregate_field_end)) == NULL )
		return -1;

	return 0;
}

/*
 * Returns a newly alloced string for type
 * The function ignores access specifiers
 */
int
FDSAggregateFieldByName(char *fds, char *name, char **type)
{
	char *	nm;

	AGGREGATE_START(fds, -1);

	while ( *fds != '\0' && *fds != FDS_AGGREGATE_END ) {
		nm = fds;

		fds = _fds_skipto(fds, _fds_aggregate_field_name_end);

		if ( *fds == '\0' ) {
			return -1;
		}

		*fds = '\0'; /* temporarily */

		if ( strcmp(nm, name) == 0 ) {
			*fds = FDS_AGGREGATE_FIELD_NAME_END;

			if ( (*type = _field_attribute(nm,
							_fds_aggregate_field_name_end,
							_fds_aggregate_field_end)) == NULL ) {
				return -1;
			}

			return 0;
		}

		*fds = FDS_AGGREGATE_FIELD_NAME_END;

		fds = _fds_skipto(fds, _fds_aggregate_field_end);
		fds++;
	}

	return -1;
}

/*
 * Arrange the members of AIF aggregate defined by fds and data into a new_fds
 * and a new_data so that the FDS looks like fdsref
 *
 * If number_of_members of fdsref > number_of_members of fds 
 *	returns -1
 * Else
 * 	creates the new_fds and new_data accordingly and returns 0
 *
 * If fds got more members, the rest will be appended at the end, so
 * fdsref can be thought of as a prefix of fds.
 *
 * The function only works with the public section.
 * It does not change fdsref, fds and data.
 * It allocates the memory for new_fds and new_data
 */
int
_fds_aggregate_arrange(char *fdsref, char *fds, char *data, char **new_fds, char **new_data)
{
	char * ptr_fds = fds;
	char * ptr_data = data;
	int len_new_fds;
	int len_new_data;
	int number_fds = 0;	/* number_of_members of fds */
	int number_fdsref = 0;	/* number_of_members of fdsref */

	char * tmp;
	int n;

	char ** fds_members;
	char ** data_members;
	int * fds_members_len;
	int * data_members_len;
	int * fds_members_flag; /* to flag that the member has been copied */

	/* calculate the number_of_members of fds and fdsref
	** we do not use FDSNumFields() since we are only interested in the
	** public section */
	tmp = fds;
	AGGREGATE_START(tmp, -1);
	while ( *tmp != FDS_AGGREGATE_ACCESS_SEP )
	{
		tmp = strchr(tmp, FDS_AGGREGATE_FIELD_NAME_END) + 1;
		if
		(
                        *tmp == FDS_AGGREGATE_START || *tmp == FDS_UNION_START ||
                        *tmp == FDS_ARRAY_START || *tmp == FDS_ENUM_START
                )
                        tmp = _fds_skiptomatch(tmp) + 1;

		tmp = _fds_skipto(tmp, _fds_aggregate_field_end);
		number_fds++;
	}

	tmp = fdsref;
	AGGREGATE_START(tmp, -1);
	while ( *tmp != FDS_AGGREGATE_ACCESS_SEP )
	{
		tmp = strchr(tmp, FDS_AGGREGATE_FIELD_NAME_END) + 1;
		if
		(
                        *tmp == FDS_AGGREGATE_START || *tmp == FDS_UNION_START ||
                        *tmp == FDS_ARRAY_START || *tmp == FDS_ENUM_START
                )
                        tmp = _fds_skiptomatch(tmp) + 1;

		tmp = _fds_skipto(tmp, _fds_aggregate_field_end);
		number_fdsref++;
	}

	/* check for the number_of_member variables */
	if ( number_fdsref > number_fds )
		return -1;
	
	/* allocate new_fds and new_data */
	_fds_skip_data(&ptr_fds, &ptr_data);
	len_new_fds = ptr_fds - fds;
	len_new_data = ptr_data - data;
	if (*new_fds == NULL)
		*new_fds = _aif_alloc(len_new_fds);
	if (*new_data == NULL)
		*new_data = _aif_alloc(len_new_data);

	/* we divide fds and data into several fields/members
	** we store the pointers for easy retrieval */
	fds_members = (char **) _aif_alloc(sizeof(char *) * number_fds);
	fds_members_len = (int *) _aif_alloc(sizeof(int) * number_fds);
	data_members = (char **) _aif_alloc(sizeof(char *) * number_fds);
	data_members_len = (int *) _aif_alloc(sizeof(int) * number_fds);
	fds_members_flag = (int *) _aif_alloc(sizeof(int) * number_fds);

	ptr_fds = fds;
	ptr_data = data;
	AGGREGATE_START(ptr_fds, -1);
	for (n=0; n<number_fds; n++)
	{
		fds_members[n] = ptr_fds;
		data_members[n] = ptr_data;

		ptr_fds = strchr(ptr_fds, FDS_AGGREGATE_FIELD_NAME_END) + 1;
		_fds_skip_data(&ptr_fds, &ptr_data);
		fds_members_len[n] = ptr_fds - fds_members[n];
		data_members_len[n] = ptr_data - data_members[n];

		fds_members_flag[n] = 0;

		ptr_fds++;	
	}

	/* we start building up the new_fds and new_data */
	ptr_fds = fds;
	AGGREGATE_START(ptr_fds, -1);
	memcpy(*new_fds, fds, ptr_fds - fds); /* copy the id to the new_fds */

	ptr_fds = *new_fds + (ptr_fds - fds);
	ptr_data = *new_data;

	tmp = fdsref;
	AGGREGATE_START(tmp, -1);
	while ( *tmp != FDS_AGGREGATE_ACCESS_SEP )
	{
		char endchar, *end;

		if ( *tmp == FDS_AGGREGATE_FIELD_SEP ) tmp++;
		end = tmp;
		end = _fds_skipto(end, _fds_aggregate_field_end);
		endchar = *end; /* we store *end since *end can be ',' or ';' */
		*end = '\0'; /* temporarily */

		for (n=0; n<number_fds; n++)
		{
			/* we check the fds_members_flag so the function can
			** run faster for large data structures */
			if ( fds_members_flag[n] == 0 )
			{
				/* tmp and fds_members[*] contain
				** 'field_name=field_type', but we only want
				** to compare field_name */
				char * temp_a = strchr(tmp, FDS_AGGREGATE_FIELD_NAME_END);
				char * temp_b = strchr(fds_members[n], FDS_AGGREGATE_FIELD_NAME_END);
				int strcmp_res;
				*temp_a = *temp_b = '\0'; /* temporarily */
				strcmp_res = strcmp(tmp, fds_members[n]);
				*temp_a = *temp_b = FDS_AGGREGATE_FIELD_NAME_END;
				if (strcmp_res == 0)
					break;
			}
		}

		if (n == number_fds) /* not found, an error */
		{
			*end = endchar;
			_aif_free(fds_members);
			_aif_free(fds_members_len);
			_aif_free(fds_members_flag);
			_aif_free(data_members);
			_aif_free(data_members_len);
			_aif_free(*new_fds);
			_aif_free(*new_data);
			 return -1;
		}

		memcpy(ptr_data, data_members[n], data_members_len[n]);
		memcpy(ptr_fds, fds_members[n], fds_members_len[n]);
		fds_members_flag[n] = 1;
		ptr_data += data_members_len[n];
		ptr_fds += fds_members_len[n];
		*end = endchar;
		*(ptr_fds++) = FDS_AGGREGATE_FIELD_SEP;
		tmp = end;
	}

	/* copy members which have not been copied */
	if ( number_fds > number_fdsref )
	{
		for (n=0; n<number_fds; n++)
		{
			if ( fds_members_flag[n] == 0 )
			{
				memcpy(ptr_data, data_members[n], data_members_len[n]);
				memcpy(ptr_fds, fds_members[n], fds_members_len[n]);
				ptr_data += data_members_len[n];
				ptr_fds += fds_members_len[n];
				*(ptr_fds++) = FDS_AGGREGATE_FIELD_SEP;
			}
		}
	}

	/* copy the rest of the fds to the new_fds */
	ptr_fds--;
	memcpy(ptr_fds, fds_members[number_fds-1] + fds_members_len[number_fds-1], len_new_fds - (ptr_fds - *new_fds));

	_aif_free(fds_members);
	_aif_free(fds_members_len);
	_aif_free(fds_members_flag);
	_aif_free(data_members);
	_aif_free(data_members_len);
	return 0;
}

int
FDSAggregateFieldSize(char *fds, char *name)
{
	char *type;
	int   size;

	if ( FDSAggregateFieldByName(fds, name, &type) )
	{
                SetAIFError(AIFERR_BADARG, NULL);
                return -1;
	}

	size = FDSTypeSize(type);
	_aif_free(type);

	return size;
}

/*
 * The function ignores access specifiers
 * return value indicates the position of the field
 * the counter starts at 0, ie: first element is at position 0
 * if the field cannot be found, it returns -1
 */
int
FDSAggregateFieldIndex(char *fds, char *name)
{
	int	counter = 0;
	int	len;
	char *	nm;

	AGGREGATE_START(fds, -1);

	while ( *fds != '\0' && *fds != FDS_AGGREGATE_END )
	{
		nm = fds;

		fds = _fds_skipto(fds, _fds_aggregate_field_name_end);

		if ( *fds == '\0' )
			return -1;

		len = fds - nm;

		if ( strncmp(nm, name, len) == 0 )
			return counter;

		counter++;
		fds = _fds_skipto(fds, _fds_aggregate_field_end);
		fds++;
	}

	return -1;
}

char *
FDSAggregateInit(char *id)
{
	return strdup(TypeToFDS(AIF_AGGREGATE, id));
}

/*
 * returns the data length from field 0 until field n (inclusive)
 */
int
_data_len_index(char *fds, int n)
{
	int size = 0;
	int subsize = 0;

	AGGREGATE_START(fds, -1);
		
	while ( *fds != '\0' && *fds != FDS_AGGREGATE_END && n >= 0 )
	{
		if ( *fds == FDS_AGGREGATE_ACCESS_SEP )
		{
			fds++;
			continue;
		}

		fds = _fds_skipto(fds, _fds_aggregate_field_name_end);

		if ( *fds++ == '\0' )
			return 0;

		/* to start of field */
		if ( (subsize = FDSTypeSize(fds)) < 0 )
			return -1;

		size += subsize;

		n--;

		_fds_advance(&fds);

		if ( *fds == FDS_AGGREGATE_FIELD_SEP )
			fds++;
	}

	return size;
}

int
_data_len_public(char *fds)
{
	int size = 0;
	int subsize = 0;

	if (*(fds) == FDS_NAME) { 
        	(fds) = _fds_skipto((fds), _fds_name_end); 
        	(fds)++; 
	} 
	if ( *(fds++) != FDS_AGGREGATE_START )
        	return -1;

        while ( *(fds) != FDS_TYPENAME_END )
               	(fds)++; 
        (fds)++; 

	if ( *(fds) == '\0' ) 
        	return -1;
	if ( *(fds) == FDS_AGGREGATE_ACCESS_SEP )
        	return 0;
		
	while ( *fds != FDS_AGGREGATE_END )
	{
		if ( *fds == FDS_AGGREGATE_ACCESS_SEP )
			break;

		fds = _fds_skipto(fds, _fds_aggregate_field_name_end);

		if ( *fds++ == '\0' )
			return 0;

		/* to start of field */
		if ( (subsize = FDSTypeSize(fds)) < 0 )
			return -1;

		size += subsize;

		_fds_advance(&fds);

		if ( *fds == FDS_AGGREGATE_FIELD_SEP )
			fds++;
	}

	return size;
}

/*
 * returns a pointer to a newly allocated structure
 */
char *
_field_attribute(char *s, char *starter, char *ender)
{
	int	len;
	char *	np;
	char *	_fds_field_attr; /* we alloc as needed */

	if ( starter != NULL )
	{
		s = _fds_skipto(s, starter);

		if ( *s++ == '\0' )
			return NULL;
	}

	np = s;
	s = _fds_skipto(s, ender);

	if ( *s == '\0' )
		return NULL;

	len = s - np;

	_fds_field_attr = _aif_alloc(len+1);

	memcpy(_fds_field_attr, np, len);
	_fds_field_attr[len] = '\0';

	return _fds_field_attr;
}

#define ENUM_START(fds, res) \
	if (*(fds) == FDS_NAME) { \
		(fds) = _fds_skipto((fds), _fds_name_end); \
		(fds)++; \
	} \
	if ( *(fds++) != FDS_ENUM_START ) \
		return (res); \
	while ( *(fds) != FDS_TYPENAME_END ) \
		(fds)++; \
	(fds)++; \
	if ( *(fds) == '\0' || *(fds) == FDS_ENUM_END ) \
		return (res);

/* based on the fds and the enum value,
 * the function gives the enum name (string),
 * it allocs the string for the caller */
int
FDSEnumConstByValue(char *fds, char **name, int val)
{
	int	len;
	char *	nm;

	ENUM_START(fds, -1);

	while ( *fds != '\0' && *fds != FDS_ENUM_END )
	{
		nm = fds;

		fds = _fds_skipto(fds, _fds_enum_const_name_end);

		if ( *fds == '\0' )
			return -1;

		len = fds - nm;

		fds++;
		if ( val == _fds_getnum(fds) )
		{
			*name = _aif_alloc(len + 1);
			**name = '\0';
			strncat(*name, nm, len);
			
			return 0;
		}

		fds = _fds_skipto(fds, _fds_enum_const_end);
		fds++;
	}

	return -1;
}

/* based on the fds and the enum name (string),
 * the function gives the enum value */
int
FDSEnumConstByName(char *fds, char *name, int *val)
{
	int	len;
	char *	nm;

	ENUM_START(fds, -1);

	while ( *fds != '\0' && *fds != FDS_ENUM_END )
	{
		nm = fds;

		fds = _fds_skipto(fds, _fds_enum_const_name_end);

		if ( *fds == '\0' )
			return -1;

		len = fds - nm;

		if ( strncmp(nm, name, len) == 0 )
		{
			fds++;
                        *val = _fds_getnum(fds);

			return 0;
		}

		fds = _fds_skipto(fds, _fds_enum_const_end);
		fds++;
	}

	return -1;
}

char *
FDSEnumInit(char *id)
{
	return strdup(TypeToFDS(AIF_ENUM, id, 1));
}

char *
FDSAddConstToEnum(char *fds, char *name, int val)
{
	int	v;
	char *	nfmt;
	char *	end;
	char	field[BUFSIZ];

	if ( FDSEnumConstByName(fds, name, &v) == 0 )
	{
		SetAIFError(AIFERR_BADARG, NULL);
		return NULL;
	}

	/* XXX TODO
	if ( FDSEnumFieldByValue(fds, name, &nfmt) == 0 )
	{
		SetAIFError(AIFERR_BADARG, NULL);
		_aif_free(nfmt);
		return NULL;
	}
	*/

	snprintf(field, BUFSIZ, "%s%c%d", name, FDS_ENUM_SEP, val);

	nfmt = (char *)_aif_alloc(strlen(fds) + strlen(field) + 2);
	*nfmt = '\0';

	strcpy(nfmt, fds);

	if
	(
		(end = strrchr(nfmt, FDS_ENUM_END)) == NULL
		||
		end - nfmt < 1
	)
	{
		SetAIFError(AIFERR_BADARG, NULL);
		return NULL;
	}

	if ( *(end-1) != FDS_TYPENAME_END )
		*end++ = FDS_ENUM_CONST_SEP;

	sprintf(end, "%s%s", field, strrchr(fds, FDS_ENUM_END));

	ResetAIFError();

	return nfmt;
}

int
FDSEnumAdd(char **fds, char *name, int val)
{
	char * temp;

	temp = FDSAddConstToEnum(*fds, name, val);

	if ( temp == NULL )
		return -1;

	_aif_free(*fds);
	*fds = temp;

	return 0;
}

#define UNION_START(fds, res) \
	if (*(fds) == FDS_NAME) { \
		(fds) = _fds_skipto((fds), _fds_name_end); \
		(fds)++; \
	} \
	if ( *(fds++) != FDS_UNION_START ) \
		return (res); \
	while ( *(fds) != FDS_TYPENAME_END ) \
		(fds)++; \
	(fds)++; \
	if ( *(fds) == '\0' || *(fds) == FDS_UNION_END ) \
		return (res);

int
FDSUnionFieldByName(char *fds, char *name, char **type)
{
	int	len;
	char *	nm;

	UNION_START(fds, -1);

	while ( *fds != '\0' && *fds != FDS_UNION_END )
	{
		nm = fds;

		fds = _fds_skipto(fds, _fds_union_field_name_end);

		if ( *fds == '\0' )
			return -1;

		len = fds - nm;

		if ( strncmp(nm, name, len) == 0 )
		{
			fds++;
			nm = fds;
			fds = _fds_skipto(fds, _fds_union_field_end);

			if ( *fds == '\0' )
				return -1;

			len = fds - nm;
			*type = _aif_alloc(len+1);
			strncpy(*type, nm, len);
			(*type)[len] = '\0';

			return 0;
		}

		fds = _fds_skipto(fds, _fds_union_field_end);
		fds++;
	}

	return -1;
}

char *
FDSUnionInit(char *id)
{
	return strdup(TypeToFDS(AIF_UNION, id));
}

char *
FDSAddFieldToUnion(char *fds, char *name, char *type)
{
	char *	nfmt;
	char *	end;
	char	field[BUFSIZ];
	char *  dummy;

	if ( FDSUnionFieldByName(fds, name, &dummy) == 0 )
	{
		_aif_free(dummy);
		SetAIFError(AIFERR_BADARG, NULL);
		return NULL;
	}

	snprintf(field, BUFSIZ, "%s%c%s", name, FDS_UNION_FIELD_NAME_END, type);

	nfmt = (char *)_aif_alloc(strlen(fds) + strlen(field) + 2);
	*nfmt = '\0';

	strcpy(nfmt, fds);

	if
	(
		(end = strrchr(nfmt, FDS_UNION_END)) == NULL
		||
		end - nfmt < 1
	)
	{
		SetAIFError(AIFERR_BADARG, NULL);
		return NULL;
	}

	if ( *(end-1) != FDS_TYPENAME_END )
		*end++ = FDS_UNION_FIELD_SEP;

	sprintf(end, "%s%c", field, FDS_UNION_END);

	ResetAIFError();

	return nfmt;
}

int
FDSUnionAdd(char **fds, char *name, char *type)
{
	char * temp;

	temp = FDSAddFieldToUnion(*fds, name, type);

	if ( temp == NULL ) 
		return -1;

	_aif_free(*fds);
	*fds = temp;

	return 0;
}

/*
 * Add the field to the end of the section specified by the access
 * qualifier.
 */
char *
FDSAddFieldToAggregate(char *fds, AIFAccess acc, char *name, char *type)
{
	char * 	temp;
	char *	nfmt;
	char *	end;
	char *  rest;

	if (FDSAggregateFieldByName(fds, name, &nfmt) == 0)
	{
		SetAIFError(AIFERR_BADARG, NULL);
		_aif_free(nfmt);
		return NULL;
	}

	nfmt = (char *)_aif_alloc(strlen(fds) + strlen(name) + strlen(type) +3);
	*nfmt = '\0';

	strcpy(nfmt, fds);

	temp = nfmt;

	if (*fds == FDS_NAME) {
        	fds = _fds_skipto(fds, _fds_name_end);
        	fds++;
	} 

	if (*temp == FDS_NAME) {
        	temp = _fds_skipto(temp, _fds_name_end);
        	temp++;
	}

	if
	(
		(end = _fds_skipto(temp+1, _fds_aggregate_access_sep)) == NULL
		||
		end - temp < 1
	)
	{
		SetAIFError(AIFERR_BADARG, NULL);
		_aif_free(nfmt);
		return NULL;
	}

	rest = _fds_skipto(fds+1, _fds_aggregate_access_sep);

	switch ( acc )
	{
		case AIF_ACCESS_PACKAGE:
			end = _fds_skipto(++end, _fds_aggregate_access_sep);
			if ( end == NULL )
			{
				SetAIFError(AIFERR_BADARG, NULL);
				_aif_free(nfmt);
				return NULL;
			}
			rest = _fds_skipto(++rest, _fds_aggregate_access_sep);
			/* fall through */

		case AIF_ACCESS_PRIVATE:
			end = _fds_skipto(++end, _fds_aggregate_access_sep);
			if ( end == NULL )
			{
				SetAIFError(AIFERR_BADARG, NULL);
				_aif_free(nfmt);
				return NULL;
			}
			rest = _fds_skipto(++rest, _fds_aggregate_access_sep);
			/* fall through */

		case AIF_ACCESS_PROTECTED:
			end = _fds_skipto(++end, _fds_aggregate_access_sep);
			if ( end == NULL )
			{
				SetAIFError(AIFERR_BADARG, NULL);
				_aif_free(nfmt);
				return NULL;
			}
			rest = _fds_skipto(++rest, _fds_aggregate_access_sep);
			/* fall through */

		case AIF_ACCESS_PUBLIC:
		case AIF_ACCESS_UNKNOWN:
			break;
	}

	if ( *(end-1) != FDS_TYPENAME_END && *(end-1) != FDS_AGGREGATE_ACCESS_SEP )
		*end++ = FDS_AGGREGATE_FIELD_SEP;

	sprintf(end, "%s%c%s%s",
		name,
		FDS_AGGREGATE_FIELD_NAME_END,
		type,
		rest);

	ResetAIFError();

	return nfmt;
}

int
FDSAggregateAdd(char **fds, AIFAccess acc, char *name, char *type)
{
	char * temp;

	temp = FDSAddFieldToAggregate(*fds, acc, name, type);

	if ( temp == NULL )
		return -1;

	_aif_free(*fds);
	*fds = temp;

	return 0;
}

int
FDSSetIdentifier(char **fds, char *id)
{
	int	id_len;
	char  *	temp;
	char  *	new;

	temp = *fds;
	id_len = strlen(id);

	new = _aif_alloc( strlen(*fds) + strlen(id) + 1 );
	*new = '\0';

	if ( *temp == FDS_NAME ) 
	{
		temp = _fds_skipto(temp, _fds_name_end);
		temp++;
		strncat(new, *fds, temp - *fds);
	}

	if ( *temp == FDS_UNION_START || 
	     *temp == FDS_AGGREGATE_START ||
	     *temp == FDS_ENUM_START )
	{
		if ( *(temp+1) != FDS_TYPENAME_END )
		{
			_aif_free(new);
                	SetAIFError(AIFERR_BADARG, NULL);
			return -1;
		}

		strncat(new, temp, 1);
		strncat(new, id, id_len);
		strcat(new, temp+1);
		_aif_free(*fds);
		*fds = new;
		return 0;
	}
	else
	{
		_aif_free(new);
                SetAIFError(AIFERR_BADARG, NULL);
		return -1;
	}
}

char *
FDSGetIdentifier(char *fds)
{
	char *temp;
	char *id;
	int   id_len;

	if ( *fds == FDS_NAME ) 
	{
		fds = _fds_skipto(fds, _fds_name_end);
		fds++;
	}

	if ( *fds == FDS_UNION_START || 
	     *fds == FDS_AGGREGATE_START ||
	     *fds == FDS_ENUM_START )
	{
		fds++;

		if ( *fds == FDS_TYPENAME_END )
		{
			SetAIFError(AIFERR_BADARG, NULL);
			return NULL;
		}

		temp = fds;
		fds = strchr(fds, FDS_TYPENAME_END);

		id_len = fds - temp;
		id = _aif_alloc( id_len + 1 );
		*id = '\0';
		strncat(id, temp, id_len);
		return id;
	}
	else
	{
		SetAIFError(AIFERR_BADARG, NULL);
		return NULL;
	}
}

void
_fds_advance(char **fds)
{
	switch ( FDSType((*fds)++) )
	{
	case AIF_ADDRESS:
	case AIF_BOOLEAN:
		*fds = _fds_skipnum(*fds);
		break;

	case AIF_ENUM:
		_fds_skip_typename(fds);

		*fds = _fds_skipto(*fds, _fds_enum_end);
		(*fds) += 2;
		/* fall through */

	case AIF_INTEGER:
		*fds += 1; /* u or s */
		*fds = _fds_skipnum(*fds);
		break;

	case AIF_FLOATING:
	case AIF_VOID:
		*fds = _fds_skipnum(*fds);
		break;

	case AIF_FUNCTION:
		while ( *(*fds)++ != FDS_FUNCTION_ARG_END )
			;

		_fds_advance(fds);
		break;

	case AIF_ARRAY:
		while ( *(*fds)++ != FDS_ARRAY_END )
			; /* past the index type */

		_fds_advance(fds); /* past the base type */
		break;

	case AIF_UNION:
	case AIF_AGGREGATE:
		_fds_skip_typename(fds);

		for ( ;; ) {

			if ( **fds == FDS_AGGREGATE_ACCESS_SEP )
			{
				(*fds)++;
				continue;
			}
			
			if ( **fds == FDS_AGGREGATE_END )
				break;

			while ( *(*fds)++ != FDS_AGGREGATE_FIELD_NAME_END )
				; /* past the field name */

			_fds_advance(fds); /* past the field type */

			if ( **fds == FDS_AGGREGATE_FIELD_SEP )
				(*fds)++;
		}
		(*fds)++; /* past closing brace */
		break;

	case AIF_NAME:
		*fds = _fds_skipnum(*fds);
		(*fds)++; /* past closing '/' */
		_fds_advance(fds); /* past the named type */
		break;

	case AIF_POINTER:
		_fds_advance(fds); /* past the address */
		_fds_advance(fds); /* past the pointed-to type */
		break;

	case AIF_CHAR_POINTER:
		_fds_advance(fds); /* past the address */
		break;

	case AIF_REFERENCE:
		*fds = _fds_skipnum(*fds);
		(*fds)++; /* past closing '/' */
		break;

	case AIF_CHARACTER:
	case AIF_STRING:
		break; /* already skipped all we should */

	default:
		fprintf(stderr, "no way to advance ... \"%s\"\n", *fds);
		break;
	}
}

/*
 * **fds is integer, float, or character.  Return how many bytes it
 * has and advance *fds to its end
 */
int
_fds_count_bytes(char **fds)
{
	int bytes;
	
	switch ( **fds )
	{
	case FDS_FLOATING:
		(*fds)++; /* past f */
		bytes = _fds_getnum(*fds);
		*fds = _fds_skipnum(*fds); /* past size indicator */
		break;

	case FDS_ENUM_START:
		*fds = _fds_skipto(++(*fds), _fds_enum_end);
		(*fds)++;
		/* fall through */	

	case FDS_INTEGER:
		*fds += 2; /* past iu or is */
		bytes = _fds_getnum(*fds);
		*fds = _fds_skipnum(*fds); /* past size indicator */
		break;

	case FDS_CHARACTER:
		(*fds)++; /* past c */
		bytes = 1;
		break;

	case FDS_BOOLEAN:
	case FDS_ADDRESS:
		(*fds)++; /* past b or a */
		bytes = _fds_getnum(*fds);
		*fds = _fds_skipnum(*fds); /* past size indicator */
		break;

	case FDS_VOID:
		(*fds)++; /* past v */
		bytes = _fds_getnum(*fds);
		*fds = _fds_skipnum(*fds); /* past size indicator */
		break;

	default:
		bytes = -1; /* internal error */
	}

	return bytes;
}

/*
 * Count bytes but don't advance pointer
 */
int
_fds_count_bytes_na(char **fds)
{
	char *	fmt = *fds;

	return _fds_count_bytes(&fmt);
}

void
_fds_resolve(char **fmt)
{
	int	index;

	while ( **fmt == FDS_NAME )
	{
		(*fmt)++;
		index = (int)strtol(*fmt, NULL, 10);
		*fmt = (char *)_fds_skipnum(*fmt)+1; /* past name, trailing slash */
		_aif_types_seen[index+MAX_TYPES_SEEN/2] = *fmt;
	}
}

char *
_fds_lookup(char **fmt)
{
	int	index;

	(*fmt)++;
	index = (int)strtol(*fmt, NULL, 10);
	*fmt = (char *)_fds_skipnum(*fmt)+1; /* past name, trailing slash */

	return _aif_types_seen[index+MAX_TYPES_SEEN/2];
}

/*
 * Find the target data for a pointer.
 *
 * @param fds format descriptor for pointer (need to calculate address size). At the
 * 		  end of the call, fds will be advanced to the beginning of the target type
 * @param data points to the data part of an AIF. At the end of the call, data
 *        will be advanced to the beggining of the target data
 * @param index a unique index for saving/retrieving values in order
 *        to simultaneously maintain targets for multiple AIFs.
 * @return a pointer to the data region where the target data actually
 *         starts, or 0 if null or invalid pointer
 *
 * Format of the data is:
 * 	<type>[<name>][<address>[<target>]]
 *
 * Where:
 *
 * <type> is a single byte:
 * 		0: null pointer, no data
 *  	1: ordinary pointer, data contains <address> and <target>
 *  	2: named pointer, data contains <name>, <address> and <target>
 *  	3: pointer reference, data contains <name>
 *  	4: invalid pointer, data contains <address>
 *  <name> is an unsigned 4 byte integer
 *  <address> is the address of the pointer
 *	<target> is the target data
 */

char *
_find_target(char **fds, char **data, int index)
{
	int		name;
	char	code = **data;
	char *	res;

	(*fds)++;	/* past the ^ */
	(*data)++;	/* past the code */

	switch ( code )
	{
	case AIF_PTR_NIL:
		_fds_advance(fds); /* skip address type */
		return (char *)0;

	case AIF_PTR_INVALID: /* invalid pointer value */
		_fds_skip_data(fds, data); /* skip address */
		return (char *)0;

	case AIF_PTR_NORMAL: /* normal pointer value */
		_fds_skip_data(fds, data); /* skip address */
		res = *data;
		return res;

	case AIF_PTR_NAME: /* named value */
		_ptrname_to_int(data, &name);
		_fds_skip_data(fds, data); /* skip address type */
		_aif_values_seen[name+index*MAX_VALUES_SEEN/2] = *data;
		return *data;

	case AIF_PTR_REFERENCE: /* reference to named value */
		_ptrname_to_int(data, &name);
		_fds_advance(fds); /* skip address type */
		return _aif_values_seen[name+index*MAX_VALUES_SEEN/2];
	}

	return 0; /* should never fall through */
}

/*
 * Get the pointer type.
 */
int
_get_pointer_type(char *data)
{
	return (int)(*data);
}

/*
 * Get the pointer name.
 */
int
_get_pointer_name(char *data)
{
	int name = 0;
	int type = _get_pointer_type(data);

	if (type == AIF_PTR_NAME || type == AIF_PTR_REFERENCE) {
		data++; /* skip code */
		_ptrname_to_int(&data, &name);
	}

	return name;
}

/* 
 * Skip over one full data unit, specified by the fds,
 * At return, fds should be at its end, and data is advanced
 * to point to the next byte following the end of the data.
 */
void
_fds_skip_data(char **fds, char **data)
{
	int		i;
	int		bytes;
	AIFIndex *	ix;
	char *		fmt;

	_fds_resolve(fds);

	switch ( FDSType(*fds) )
	{
	case AIF_REFERENCE:
		fmt = _fds_lookup(fds); // need a temporary copy
		_fds_skip_data(&fmt, data);
		return;

	case AIF_POINTER:
		if (_find_target(fds, data, 0) == 0)
			_fds_advance(fds);
		else
			_fds_skip_data(fds, data);
		return;

	case AIF_VOID:
	case AIF_CHARACTER:
	case AIF_INTEGER:
	case AIF_BOOLEAN:
	case AIF_FLOATING:
	case AIF_ADDRESS:
	case AIF_ENUM:
		(*data) += _fds_count_bytes(fds);
		return;

	case AIF_ARRAY:
		ix = FDSArrayIndexInit(*fds);

		_fds_advance(fds); /* skip over the entire array fds */

		for ( i = 0 ; i < ix->i_nel ; i++ )
		{
			fmt = ix->i_btype;
			_fds_skip_data(&fmt, data);
		}

		AIFArrayIndexFree(ix);
		return;

	case AIF_AGGREGATE:
		(*fds)++; /* past FDS_AGGREGATE_START */
		_fds_skip_typename(fds);

		while ( **fds != FDS_AGGREGATE_END )
		{
			if ( **fds == FDS_AGGREGATE_ACCESS_SEP )
			{
				(*fds)++;
				continue;
			}

			*fds = strchr(*fds, FDS_AGGREGATE_FIELD_NAME_END) + 1;
				/* to start of field */
			_fds_skip_data(fds, data);

			if ( **fds == FDS_AGGREGATE_FIELD_SEP )
				(*fds)++;
		}

		(*fds)++; /* past FDS_AGGREGATE_END */
		return;

	case AIF_UNION:
		(*fds)++; /* past FDS_UNION_START */
		_fds_skip_typename(fds);

		while ( **fds != FDS_UNION_END )
		{
			*fds = strchr(*fds, FDS_UNION_FIELD_NAME_END) + 1;
				/* to start of field */
			_fds_skip_data(fds, data);

			if ( **fds == FDS_UNION_FIELD_SEP )
				(*fds)++;
		}

		(*fds)++; /* past FDS_UNION_END */
		return;

	case AIF_CHAR_POINTER:
		(*fds)++; /* past p */
		_fds_skip_data(fds, data); /* past address */
		bytes = (*(*data)++ & 0xff) << 8;
		bytes += *(*data)++ & 0xff;
		*data += bytes;
		return;

	case AIF_STRING:
		(*fds)++; /* past s */
		bytes = (*(*data)++ & 0xff) << 8;
		bytes += *(*data)++ & 0xff;
		*data += bytes;
		return;

	case AIF_FUNCTION:
		while ( *(*fds)++ != FDS_FUNCTION_ARG_END )
			;

		_fds_advance(fds);

		while ( **data != '\0' )
			(*data)++;
		(*data)++; /* past null */
		return;
			
	default:
		SetAIFError(AIFERR_TYPE, NULL);
		return;
	}
}

/*
 * Correctly calculate the actual size of a type.
 * Requires the data in order to be able to deal
 * with variable length types.
 */
int
_fds_type_size(char *fds, char *data)
{
	char *	f = fds;
	char *	d = data;

	_fds_skip_data(&f, &d);
	return d - data;
}

/*
 * Skip the typename field. On return, fds will point
 * to the character after '|' or NULL if the
 * '|' is not found.
 */
void
_fds_skip_typename(char **fds)
{
	if (**fds != FDS_TYPENAME_END) {
		*fds = strchr(*fds, FDS_TYPENAME_END);
	}
	if (*fds != NULL) {
		(*fds)++;
	}
}

char *
_fds_add_function_arg(char *fds, char *arg)
{
	char * nfmt;
	char * temp;
	char * rest;

        if ( FDSType(fds) != AIF_FUNCTION )
        {
                SetAIFError(AIFERR_BADARG, NULL);
                return NULL;
        }

        nfmt = (char *)_aif_alloc(strlen(fds) + strlen(arg) + 2);
        *nfmt = '\0';

        strcpy(nfmt, fds);

        if ( (temp = _fds_skipto(nfmt, _fds_function_arg_end)) == NULL )
        {
                SetAIFError(AIFERR_BADARG, NULL);
                _aif_free(nfmt);
                return NULL;
        }

        rest = _fds_skipto(fds, _fds_function_arg_end);

        if ( temp != (nfmt + 1) )
                *temp++ = FDS_FUNCTION_ARG_SEP;

        sprintf(temp, "%s%s",
                arg,
                rest);

        ResetAIFError();

        return nfmt;
}



