/*
 * Routines that support arithmetic/logical operations on
 * AIF objects.
 *
 * 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
 *
 * The _aif_div_int() routine was derived from source under
 * the following copyright:
 *
 *   Use of this program, for any purpose, is granted the author,
 *   Ian Kaplan, as long as this copyright notice is included in
 *   the source code or any source code derived from this program.
 *   The user assumes all responsibility for using this code.
 *
 */

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

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

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

#if defined(WINDOWSNT) || defined(WIN32)
#define BITSPERBYTE     8
#elif defined(__APPLE__)
#include        <sys/types.h>
#define BITSPERBYTE     NBBY
#else
#include        <values.h>
#endif /* WINDOWSNT || WIN32 */

/*
 * Boolean bitwise and.
 */
void
_aif_and_bool(char *rd, char *d1, int l1, char *d2, int l2)
{
	_aif_and_int(rd, d1, l1, d2, l2);
}

/*
 * Boolean bitwise or.
 */
void
_aif_or_bool(char *rd, char *d1, int l1, char *d2, int l2)
{
	_aif_or_int(rd, d1, l1, d2, l2);
}

/*
 * Boolean 2's compliment.
 */
void
_aif_not_bool(char *rd, char *data, int len)
{
	_aif_not_int(rd, data, len);
}

/*
 * Integer 2's compliment.
 */
void
_aif_not_int(char *rd, char *data, int len)
{
	AIFLONGEST	val;

	_aif_to_longest(data, len, &val);
	val = ~val;
	_longest_to_aif(&rd, len, val);
}

/*
 * Integer negation.
 */
void
_aif_neg_int(char *rd, char *data, int len)
{
	AIFLONGEST	val;

	_aif_to_longest(data, len, &val);
	val = -val;
	_longest_to_aif(&rd, len, val);
}

/*
 * Integer addition.
 */
void
_aif_add_int(char *rd, char *d1, int l1, char *d2, int l2)
{
	AIFLONGEST	val1;
	AIFLONGEST	val2;
	AIFLONGEST	val;

	_aif_to_longest(d1, l1, &val1);
	_aif_to_longest(d2, l2, &val2);
	val = val1 + val2;
	_longest_to_aif(&rd, MAX(l1, l2), val);
}

/*
 * Integer bitwise and.
 */
void
_aif_and_int(char *rd, char *d1, int l1, char *d2, int l2)
{
	AIFLONGEST	val1;
	AIFLONGEST	val2;
	AIFLONGEST	val;

	_aif_to_longest(d1, l1, &val1);
	_aif_to_longest(d2, l2, &val2);
	val = val1 & val2;
	_longest_to_aif(&rd, MAX(l1, l2), val);
}

/*
 * Integer bitwise or.
 */
void
_aif_or_int(char *rd, char *d1, int l1, char *d2, int l2)
{
	AIFLONGEST	val1;
	AIFLONGEST	val2;
	AIFLONGEST	val;

	_aif_to_longest(d1, l1, &val1);
	_aif_to_longest(d2, l2, &val2);
	val = val1 | val2;
	_longest_to_aif(&rd, MAX(l1, l2), val);
}

/*
 * Integer subtraction.
 */
void
_aif_sub_int(char *rd, char *d1, int l1, char *d2, int l2)
{
	AIFLONGEST	val1;
	AIFLONGEST	val2;
	AIFLONGEST	val;

	_aif_to_longest(d1, l1, &val1);
	_aif_to_longest(d2, l2, &val2);
	val = val1 - val2;
	_longest_to_aif(&rd, MAX(l1, l2), val);
}

/*
 * Integer multiplication. Result is same size as the largest
 * argument. 
 */
void
_aif_mul_int(char *rd, char *d1, int l1, char *d2, int l2)
{
	AIFLONGEST	val1;
	AIFLONGEST	val2;
	AIFLONGEST	val;

	_aif_to_longest(d1, l1, &val1);
	_aif_to_longest(d2, l2, &val2);
	val = val1 * val2;
	_longest_to_aif(&rd, MAX(l1, l2), val);
} 

/*
 * Compare two integers. Returns:
 *	-1 if d1 < d2
 *	 0 if d1 == d2
 *	 1 if d1 > d2
 */
int
compare_int(char *d1, int l1, char *d2, int l2)
{
	int	s1 = 0;
	int	s2 = 0;

	if ( l2 > l1 )
	{
		for ( s2 = 0 ; s2 < l2-l1; s2++ )
			if ( d2[s2] != 0x0 )
				return -1;
	}
	else
	{
		for ( s1 = 0 ; s1 < l1-l2; s1++ )
			if ( d1[s1] != 0x0 )
				return 1;
	}

	for ( ; s1 < l1 ; s1++, s2++)
	{
		if ( d2[s2] > d1[s1] )
			return -1;

		if ( d2[s2] < d1[s1] )
			return 1;
	}

	return 0;
}

/*
 * Shift d, s bits to the left. s < BITSPERBYTE. Result in r.
 * d and r can overlap, but must be the same size.
 */
void
lshift_int(unsigned char *r, unsigned char *d, int l, int s)
{
	int	i;
	int	s2 = BITSPERBYTE - s;


	for ( i = 0 ; i < l-1 ; i++ )
		r[i] = (d[i] << s) | (d[i+1] >> s2);

	r[i] = d[i] << s;
}

/*
 * Shift d, s bits to the right. s < BITSPERBYTE. Result in r.
 * d and r can overlap, but must be the same size.
 */
void
rshift_int(unsigned char *r, unsigned char *d, int l, int s)
{
	int	i;
	int	s2 = BITSPERBYTE - s;


	for ( i = l-1 ; i > 0 ; i-- )
		r[i] = (d[i] >> s) | (d[i-1] << s2);

	r[0] = d[0] >> s;
}

/*
 * Integer division d1/d2 = rq + rr/d2. Quotient is same size as the largest
 * argument. Note: d2 != 0.
 *
 * XXX need to check remainder for correct sign.
 */
void
_aif_div_int(char *rq, char *rr, char *d1, int l1, char *d2, int l2)
{
	AIFLONGEST	val1;
	AIFLONGEST	val2;
	AIFLONGEST	val;

	_aif_to_longest(d1, l1, &val1);
	_aif_to_longest(d2, l2, &val2);
	val = val1 / val2;
	_longest_to_aif(&rq, MAX(l1, l2), val);
	val = val1 % val2;
	_longest_to_aif(&rr, MAX(l1, l2), val);
} 

/*
 * Perform a binary integer operation.
 */
int
_aif_binary_op_int(aifop op, char **rd, char *d1, int l1, char *d2, int l2)
{
	char *	rt=NULL;


	if ( l1 > sizeof(AIFLONGEST) )
		fprintf(stderr, "binary int operation: arg1 exceeds precision\n");

	if ( l2 > sizeof(AIFLONGEST) )
		fprintf(stderr, "binary int operation: arg2 exceeds precision\n");

	switch ( op )
	{
	case AIFOP_ADD:
	case AIFOP_SUB:
	case AIFOP_MUL:
	case AIFOP_AND:
	case AIFOP_OR:
		if ( *rd == NULL )
			*rd = (char *)_aif_alloc(MAX(l1, l2));
		break;

	case AIFOP_DIV:
	case AIFOP_REM:
		if ( *rd == NULL )
			*rd = (char *)_aif_alloc(l1);

		/*
		** Temp for quotient.
		*/
		rt = (char *)_aif_alloc(l1);
		break;

	default:
		SetAIFError(AIFERR_TYPE, NULL);
		return -1;
	}

	switch ( op )
	{
	case AIFOP_ADD:
		_aif_add_int(*rd, d1, l1, d2, l2);
		break;

	case AIFOP_SUB:
		_aif_sub_int(*rd, d1, l1, d2, l2);
		break;

	case AIFOP_MUL:
		_aif_mul_int(*rd, d1, l1, d2, l2);
		break;

	case AIFOP_AND:
		_aif_and_int(*rd, d1, l1, d2, l2);
		break;

	case AIFOP_OR:
		_aif_or_int(*rd, d1, l1, d2, l2);
		break;

	case AIFOP_DIV:
		if ( _aif_int_is_zero(d2, l2) )
		{
			SetAIFError(AIFERR_ARITH, NULL);
			return -1;
		}

		_aif_div_int(*rd, rt, d1, l1, d2, l2);

		_aif_free(rt);
		break;

	case AIFOP_REM:
		if ( _aif_int_is_zero(d2, l2) )
		{
			SetAIFError(AIFERR_ARITH, NULL);
			return -1;
		}

		_aif_div_int(rt, *rd, d1, l1, d2, l2);

		_aif_free(rt);
		break;

	default:
		SetAIFError(AIFERR_TYPE, NULL);
		return -1;
	}

	return 0;
}

/*
 * Perform a binary boolean operation.
 */
int
_aif_binary_op_bool(aifop op, char **rd, char *d1, int l1, char *d2, int l2)
{
	if ( *rd == NULL )
		*rd = (char *)_aif_alloc(MAX(l1, l2));

	switch ( op )
	{
	case AIFOP_AND:
		_aif_and_bool(*rd, d1, l1, d2, l2);
		break;

	case AIFOP_OR:
		_aif_or_bool(*rd, d1, l1, d2, l2);
		break;

	default:
		SetAIFError(AIFERR_TYPE, NULL);
		return -1;
	}

	return 0;
}

/*
 * Perform a unary boolean operation.
 */
int
_aif_unary_op_bool(aifop op, char **rd, char *d, int l)
{
	if ( *rd == NULL )
		*rd = (char *)_aif_alloc(l);

	switch ( op )
	{
	case AIFOP_NOT:
		_aif_not_bool(*rd, d, l);
		break;

	default:
		SetAIFError(AIFERR_TYPE, NULL);
		return -1;
	}

	return 0;
}

/*
 * Perform a unary integer operation.
 */
int
_aif_unary_op_int(aifop op, char **rd, char *d, int l)
{
	if ( *rd == NULL )
		*rd = (char *)_aif_alloc(l);

	switch ( op )
	{
	case AIFOP_NEG:
		_aif_neg_int(*rd, d, l);
		break;

	case AIFOP_NOT:
		_aif_not_int(*rd, d, l);
		break;

	default:
		SetAIFError(AIFERR_TYPE, NULL);
		return -1;
	}

	return 0;
}

/*
 * Perform a binary floating point operation.
 */
int
_aif_binary_op_float(aifop op, char **rd, char *d1, int l1, char *d2, int l2)
{
	AIFDOUBLEST	v;
	AIFDOUBLEST	v1;
	AIFDOUBLEST	v2;

	ResetAIFError();

	if
	(
		_aif_to_doublest(d1, l1, &v1) < 0
		||
		_aif_to_doublest(d2, l2, &v2) < 0
	)
		return -1;

	switch ( op )
	{
	case AIFOP_ADD:
		v = v1 + v2;
		break;

	case AIFOP_SUB:
		v = v1 - v2;
		break;

	case AIFOP_MUL:
		v = v1 * v2;
		break;

	case AIFOP_DIV:
		/*
		** Let floating point / deal with v2 == 0.
		*/
		v = v1 / v2;
		break;

	default:
		SetAIFError(AIFERR_TYPE, NULL);
		return -1;
	}

	if ( MAX(l1, l2) > sizeof(float) )
		return _doublest_to_aif(rd, MAX(l1, l2), v);
	else
		return _doublest_to_aif(rd, l1, v);
}

/*
 * Perform a unary floating point operation.
 */
int
_aif_unary_op_float(aifop op, char **rd, char *d1, int l1)
{
	AIFDOUBLEST	v;
	AIFDOUBLEST	v1;

	if ( _aif_to_doublest(d1, l1, &v1) < 0 )
		return -1;

	switch ( op )
	{
	case AIFOP_NEG:
		v = -v1;
		break;

	default:
		SetAIFError(AIFERR_TYPE, NULL);
		return -1;
	}

	return _doublest_to_aif(rd, l1, v);
}

/*
 * Perform a binary operation. Arguments can be integer or
 * floating point or structs or arrays resulting in such.
 * Advance the data and format pointers across the data.
 * Return 0 for success, -1 for failure.
 * Result in rd and rf.
 * If rd is null, we generate our own format and data strings.  It is
 * impossible to decide the proper length for the data for structured types; we
 * make wild guesses.  It's a bug.
 */
int
_aif_binary_op(aifop op, 
	char **rf, char **rd, 
	char **f1, char **d1,
	char **f2, char **d2)
{
	int	i;
	int	bytes1;
	int	bytes2;
	int	bytes3;
	char *	d;
	char *	fmt;
	char *	d3 = NULL;
	int res;

	ResetAIFError();

	_fds_resolve(f1);
	_fds_resolve(f2);

	if ( FDSType(*f1) == AIF_REFERENCE )
	{
		fmt = _fds_lookup(f1);
		return _aif_binary_op(op, rf, rd, &fmt, d1, f2, d2);
	}

	if ( FDSType(*f2) == AIF_REFERENCE )
	{
		fmt = _fds_lookup(f2);
		return _aif_binary_op(op, rf, rd, f1, d1, &fmt, d2);
	}

	switch ( FDSType(*f1) )
	{
	case AIF_ENUM:
		bytes1 = _fds_count_bytes_na(f1);

		switch ( FDSType(*f2) )
		{
		case AIF_ENUM:
		case AIF_INTEGER:
			bytes2 = _fds_count_bytes_na(f2);

			if ( _aif_binary_op_int(op, rd, *d1, bytes1, *d2, bytes2) < 0 )
				return -1;

			(*d1) += bytes1;
			(*d2) += bytes2;

			if ( *rf == NULL )
				*rf = strdup(AIF_INTEGER_TYPE(1, MAX(bytes1, bytes2)));
			else
			{
				char *t1, *t2;
				int len;

				t1 = strstr(*rf, *f1);	

				if ( t1 == NULL )
				{
					SetAIFError(AIFERR_CONV, NULL);
					return -1;
				}

				t2 = _fds_base_type(t1);
				len = strlen(t2);
				memmove(t1, t2, len+1);
			}

			_fds_advance(f1);
			_fds_advance(f2);

			return 0;

		case AIF_FLOATING:
			bytes2 = _fds_count_bytes_na(f2);

			if ( (bytes3 = _aif_int_to_aif_float(&d3, *d1, bytes1)) < 0 )
				return -1;

			if ( _aif_binary_op_float(op, rd, d3, bytes3, *d2, bytes2) < 0 )
			{
				_aif_free(d3);
				return -1;
			}

			(*d1) += bytes1;
			(*d2) += bytes2;

			if ( *rf == NULL ) 
				*rf = strdup(AIF_FLOATING_TYPE(MAX(bytes2, bytes3)));
			else
			{
				char *t1, *t2;
				int len;

				t1 = strstr(*rf, *f1);	

				if ( t1 == NULL )
				{
					SetAIFError(AIFERR_CONV, NULL);
					return -1;
				}

				t2 = t1;
				_fds_advance(&t2);
				len = strlen(t2);
				*t1 = FDS_FLOATING;
				*(t1+1) = (char) MAX(bytes2, bytes3);
				memmove(t1+2, t2, len+1);
			}

			_aif_free(d3);

			_fds_advance(f1);
			_fds_advance(f2);

			return 0;

		default:
			SetAIFError(AIFERR_CONV, NULL);
			return -1;
		}
		break;
		/* case AIF_ENUM */

	case AIF_INTEGER:
		bytes1 = _fds_count_bytes_na(f1);

		switch ( FDSType(*f2) )
		{
		case AIF_CHARACTER:
		case AIF_INTEGER:
		case AIF_ENUM:
			bytes2 = _fds_count_bytes_na(f2);

			if ( _aif_binary_op_int(op, rd, *d1, bytes1, *d2, bytes2) < 0 )
				return -1;

			(*d1) += bytes1;
			(*d2) += bytes2;

			if ( *rf == NULL )
				*rf = strdup(AIF_INTEGER_TYPE(
					FDSIsSigned(*f1) || FDSIsSigned(*f2),
					MAX(bytes1, bytes2)));

			_fds_advance(f1);
			_fds_advance(f2);

			return 0;

		case AIF_FLOATING:
			bytes2 = _fds_count_bytes_na(f2);

			if ( (bytes3 = _aif_int_to_aif_float(&d3, *d1, bytes1)) < 0 )
				return -1;

			if ( _aif_binary_op_float(op, rd, d3, bytes3, *d2, bytes2) < 0 )
			{
				_aif_free(d3);
				return -1;
			}

			(*d1) += bytes1;
			(*d2) += bytes2;

			if ( *rf == NULL ) 
				*rf = strdup(AIF_FLOATING_TYPE(MAX(bytes2, bytes3)));

			_aif_free(d3);

			_fds_advance(f1);
			_fds_advance(f2);

			return 0;

		case AIF_VOID:
			d = NULL;
			res = 1;
			if ( _longest_to_aif(&d, sizeof(int), (AIFLONGEST)res) < 0 )
				return -1;

			*rd = d;

			if ( *rf == NULL )
				*rf = strdup(AIF_INTEGER_TYPE(1, sizeof(int)));

			return 0;


		default:
			SetAIFError(AIFERR_CONV, NULL);
			return -1;
		}
		break;
		/* case AIF_INTEGER */

	case AIF_FLOATING:
		bytes1 = _fds_count_bytes_na(f1);

		switch ( FDSType(*f2) )
		{
		case AIF_ENUM:
		case AIF_INTEGER:
			bytes2 = _fds_count_bytes_na(f2);

			if ( (bytes3 = _aif_int_to_aif_float(&d3, *d2, bytes2)) < 0 )
				return -1;

			if ( _aif_binary_op_float(op, rd, *d1, bytes1, d3, bytes3) < 0 )
			{
				_aif_free(d3);
				return -1;
			}

			(*d1) += bytes1;
			(*d2) += bytes2;

			if ( *rf == NULL )
				*rf = strdup(AIF_FLOATING_TYPE(MAX(bytes2, bytes3)));

			_aif_free(d3);

			_fds_advance(f1);
			_fds_advance(f2);

			return 0;
			
		case AIF_FLOATING:
			bytes2 = _fds_count_bytes_na(f2);

			if ( _aif_binary_op_float(op, rd, *d1, bytes1, *d2, bytes2) < 0 )
				return -1;

			(*d1) += bytes1;
			(*d2) += bytes2;

			if ( *rf == NULL )
				*rf = strdup(AIF_FLOATING_TYPE(MAX(bytes1, bytes2)));

			_fds_advance(f1);
			_fds_advance(f2);

			return 0;

		case AIF_VOID:
			d = NULL;
			res = 1;
			if ( _longest_to_aif(&d, sizeof(int), (AIFLONGEST)res) < 0 )
				return -1;

			*rd = d;

			if ( *rf == NULL )
				*rf = strdup(AIF_INTEGER_TYPE(1, sizeof(int)));

			return 0;

		default:
			SetAIFError(AIFERR_CONV, NULL);
			return -1;
		}
		break;

	case AIF_BOOLEAN:
		bytes1 = _fds_count_bytes_na(f1);
		bytes2 = _fds_count_bytes_na(f2);

		switch ( FDSType(*f2) )
		{
		case AIF_BOOLEAN:
			if ( _aif_binary_op_bool(op, rd, *d1, bytes1, *d2, bytes2) < 0 )
				return -1;

			if ( *rf == NULL )
				*rf = strdup(AIF_BOOLEAN_TYPE());

			*d1 += bytes1;
			*d2 += bytes2;

			_fds_advance(f1);
			_fds_advance(f2);

			return 0;

		default:
			SetAIFError(AIFERR_CONV, NULL);
			return -1;
		}
		break;

	case AIF_CHARACTER:
		bytes1 = _fds_count_bytes_na(f1);
		bytes2 = _fds_count_bytes_na(f2);

		switch ( FDSType(*f2) )
		{
		case AIF_INTEGER:
			if ( _aif_binary_op_int(op, rd, *d1, bytes1, *d2, bytes2) < 0 )
				return -1;

			*d1 += bytes1;
			*d2 += bytes2;

			if ( *rf == NULL )
				*rf = strdup(AIF_INTEGER_TYPE(
						FDSIsSigned(*f2),
						MAX(bytes1, bytes2)));

			_fds_advance(f1);
			_fds_advance(f2);

			return 0;

		case AIF_CHARACTER:
			if ( _aif_binary_op_int(op, rd, *d1, bytes1, *d2, bytes2) < 0 )
				return -1;

			if ( *rf == NULL )
				*rf = strdup(AIF_CHARACTER_TYPE());

			*d1 += bytes1;
			*d2 += bytes2;

			_fds_advance(f1);
			_fds_advance(f2);

			return 0;

		case AIF_VOID:
			d = NULL;
			res = 1;
			if ( _longest_to_aif(&d, sizeof(int), (AIFLONGEST)res) < 0 )
				return -1;

			*rd = d;

			if ( *rf == NULL )
				*rf = strdup(AIF_INTEGER_TYPE(1, sizeof(int)));

			return 0;

		default:
			SetAIFError(AIFERR_CONV, NULL);
			return -1;
		}
		break;

	case AIF_ARRAY:
		{
			AIFIndex *	ix1;
			AIFIndex *	ix2;
			char *		fmt1;
			char *		fmt2;
			char *		fres = NULL;
			char *		dres = NULL;

			if ( FDSType(*f2) != AIF_ARRAY )
			{
				if ( FDSType(*f2) == AIF_VOID )
				{
					d = NULL;
					res = 1;
					if ( _longest_to_aif(&d, sizeof(int), (AIFLONGEST)res) < 0 )
						return -1;

					*rd = d;

					if ( *rf == NULL )
						*rf = strdup(AIF_INTEGER_TYPE(1, sizeof(int)));

					return 0;
				}

				SetAIFError(AIFERR_TYPE, NULL);
				return -1;
			}

			ix1 = FDSArrayIndexInit(*f1);
			ix2 = FDSArrayIndexInit(*f2);

			if ( ix1->i_nel != ix2->i_nel )
			{
				SetAIFError(AIFERR_SIZE, NULL);
				AIFArrayIndexFree(ix1);
				AIFArrayIndexFree(ix2);
				return -1;
			}

			if ( *rd == NULL )
				*rd = (char *)_aif_alloc(BUFSIZ); /* no way to know how big! */

			if ( *rf == NULL )
				*rf = strdup(*f1);

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

			/*
			** If nel[12] > MAX_ADDRESS then we are not going to be
			** able to address all the data in the array. What should
			** we do?
			*/

			d = *rd; /* start of our data result region */

			for ( i = 0 ; i < ix1->i_nel ; i++ )
			{
				char * tmp1;
				char * tmp2;

				if ( dres != NULL) 
				{
					_aif_free(dres);
					dres = NULL;
				}

				if ( fres != NULL) 
				{
					_aif_free(fres);
					fres = NULL;
				}

				fmt1 = ix1->i_btype;
				fmt2 = ix2->i_btype;

				if ( _aif_binary_op(op, &fres, &dres, &fmt1, d1, &fmt2, d2 ) < 0 )
				{
					AIFArrayIndexFree(ix1);
					AIFArrayIndexFree(ix2);
					return -1;
				}

				AIFArrayIndexInc(ix1);
				AIFArrayIndexInc(ix2);

				tmp1 = fres;
				tmp2 = dres;
				_fds_skip_data(&tmp1, &tmp2);
				memcpy(d, dres, tmp2-dres);
				d += (tmp2-dres);
			}

			{
				char *t1, *t2;
				int len;

				t1 = _fds_base_type(*rf);
				t2 = t1;
				_fds_advance(&t2);

				len = strlen(fres);
				memmove(t1, fres, len);
				t1 += len;

				if ( *t2 != '\0' )
				{
					len = strlen(t2);
					memmove(t1, t2, len+1);
				}
				else
					*t1 = '\0';
			}

			_aif_free(fres);
			_aif_free(dres);

			fmt = *rf;
			_fds_advance(&fmt);
			*fmt = '\0';

			AIFArrayIndexFree(ix1);
			AIFArrayIndexFree(ix2);
		}

		return 0;

	/* only works with the public section in AIF_STRUCT */
	case AIF_STRUCT:
		if ( FDSType(*f2) != AIF_STRUCT )
		{
			if ( FDSType(*f2) == AIF_VOID )
			{
				d = NULL;
				res = 1;
				if ( _longest_to_aif(&d, sizeof(int), (AIFLONGEST)res) < 0 )
					return -1;

				*rd = d;

				if ( *rf == NULL )
					*rf = strdup(AIF_INTEGER_TYPE(1, sizeof(int)));

				return 0;
			}

			SetAIFError(AIFERR_TYPE, NULL);
			return -1;
		}

		if ( *rd == NULL )
			*rd = (char *)_aif_alloc(BUFSIZ); /* no way to know how big! */

		if ( *rf == NULL )
			*rf = strdup(*f1); /* includes trailing junk.  bug. */

		d = *rd; /* start of our data result region */

		(*f1)++; /* past open brace */
		(*f2)++; /* past open brace */

		_fds_skipid(f1);
		_fds_skipid(f2);

		while ( **f1 != ';' )
		{ /* do one field */

			char * dres = NULL;
			char * fres = NULL;
			char * tmp1;
			char * tmp2;

			if (**f2 == ';') { /* f1 has more fields */
				SetAIFError(AIFERR_TYPE, NULL);
				return -1;
			}

			*f1 = strchr(*f1, FDS_STRUCT_FIELD_NAME_END) + 1; /* to start of field*/
			*f2 = strchr(*f2, FDS_STRUCT_FIELD_NAME_END) + 1; /* to start of field*/
			
			fmt = *f1;

			if ( _aif_binary_op(op, &fres, &dres, f1, d1, f2, d2 ) < 0 )
				return -1; /* non-zero res means no need to look further */

			tmp1 = fres;
			tmp2 = dres;
			_fds_skip_data(&tmp1, &tmp2);
			memcpy(d, dres, tmp2-dres);
			d += (tmp2-dres);

			{
				char *t1, *t2;
				int len;

				t1 = strstr(*rf, fmt);
				t2 = t1;
				_fds_advance(&t2);

				len = strlen(fres);
				memmove(t1, fres, len);
				t1 += len;

				if ( t1 != t2 )
				{
					len = strlen(t2);
					memmove(t1, t2, len+1);
				}
			}

			_aif_free(fres);
			_aif_free(dres);

			if ( **f1 == ',' )
				(*f1)++;

			if ( **f2 == ',' )
				(*f2)++;
		} /* one field */

		if ( **f2 != ';' )
		{ /* f2 has more fields */
			SetAIFError(AIFERR_TYPE, NULL);
			return -1;
		}

		fmt = *rf;
		_fds_advance(&fmt);
		*fmt = '\0';

		*f1 = strchr(*f1, '}') + 1; /* to start of field*/
		*f2 = strchr(*f2, '}') + 1; /* to start of field*/

		return 0;

	case AIF_POINTER:
		if ( FDSType(*f2) != AIF_POINTER )
		{
			SetAIFError(AIFERR_TYPE, NULL);
			return -1;
		}
		else
		{
			int    ret;
			int    len;
			int    fds_type;
			char * tmp1;
			char * tmp2;
			char * ptr_type_1 = *d1;
			char * ptr_type_2 = *d2;

			char * target1 = _find_target(d1, 0);
			char * target2 = _find_target(d2, MAX_VALUES_SEEN/2);

			char * fres;
			char * dres;

			if ( target1 == 0 && target2 == 0 ) /* if it is NULL */
			{
				if ( *rf == NULL )
				{
					tmp2 = *f1;
					tmp1 = tmp2;
					_fds_advance(&tmp1);
					len = tmp1 - tmp2;
					*rf = _aif_alloc(sizeof(char)*len + 1);
					strncpy(*rf, tmp2, len);
					(*rf)[len] = '\0';
				}

				if ( *rd != NULL )
					_aif_free(*rd);
				*rd = _aif_alloc(1);
				**rd = (char) 0;

				_fds_advance(f1);
				_fds_advance(f2);

				return 0;
			}

			if ( *ptr_type_1 == AIF_PTR_REFERENCE ||
			     *ptr_type_2 == AIF_PTR_REFERENCE )
			{
				if ( *rf == NULL )
				{
					tmp2 = (*ptr_type_1==AIF_PTR_REFERENCE)?						 	*f1 : *f2;

					tmp1 = tmp2;
					_fds_advance(&tmp1);
					len = tmp1 - tmp2;
					*rf = _aif_alloc(sizeof(char)*len + 1);
					strncpy(*rf, tmp2, len);
					(*rf)[len] = '\0';
				}

				if ( *rd != NULL )
					_aif_free(*rd);
				*rd = _aif_alloc(5); /* 1 + 4 (ptr name) */
				
				if (*ptr_type_1 == AIF_PTR_REFERENCE)
				{
					int i;
					for ( i = 0; i < 5; i++) (*rd)[i] = ptr_type_1[i];
				}
				else
				{
					int i;
					for ( i = 0; i < 5; i++) (*rd)[i] = ptr_type_2[i];
				}

				_fds_advance(f1);
				_fds_advance(f2);

				return 0;
			}

                        if ( target1 == 0 )     /* target1 is null, make
                                                target2 as the result */
                        {
                                if ( *rf == NULL )
                                {
                                        tmp2 = *f2;
                                        tmp1 = tmp2;
                                        _fds_advance(&tmp1);
                                        len = tmp1 - tmp2;
                                        *rf = _aif_alloc(sizeof(char)*len + 1);
                                        strncpy(*rf, tmp2, len);
                                        (*rf)[len] = '\0';
                                }

                                if ( *rd != NULL )
                                        _aif_free(*rd);

                                {
                                        char * _d1;
                                        char * _d2;

                                        _d2 = ptr_type_2;
                                        _d1 = _d2;
                                        tmp2 = *f2;
                                        tmp1 = tmp2;
                                        _fds_skip_data(&tmp1, &_d1);
                                        len = _d1 - _d2;
                                        *rd = _aif_alloc(sizeof(char)*len);
                                        memcpy(*rd, _d2, len);
                                }

                                _fds_advance(f1);
                                (*f2)++;
                                _fds_skip_data(f2, d2);

                                return 0;
                        }

                        if ( target2 == 0 )     /* target2 is null, make
                                                target1 as the result */
                        {
                                if ( *rf == NULL )
                                {
                                        tmp2 = *f1;
                                        tmp1 = tmp2;
                                        _fds_advance(&tmp1);
                                        len = tmp1 - tmp2;
                                        *rf = _aif_alloc(sizeof(char)*len + 1);
                                        strncpy(*rf, tmp2, len);
                                        (*rf)[len] = '\0';
                                }

                                if ( *rd != NULL )
                                        _aif_free(*rd);

                                {
                                        char * _d1;
                                        char * _d2;

                                        _d2 = ptr_type_1;
                                        _d1 = _d2;
                                        _d1 = _d2;
                                        tmp2 = *f1;
                                        tmp1 = tmp2;
                                        _fds_skip_data(&tmp1, &_d1);
                                        len = _d1 - _d2;
                                        *rd = _aif_alloc(sizeof(char)*len);
                                        memcpy(*rd, _d2, len);
                                }

                                _fds_advance(f2);
                                (*f1)++;
                                _fds_skip_data(f1, d1);

                                return 0;
                        }

			(*f1)++;
			(*f2)++;

			if ( *rf == NULL )
			{
				*rf = _aif_alloc(BUFSIZ);
				**rf = '\0';
				strcat(*rf, "^");
			}

			if ( *rd != NULL )
				_aif_free(*rd);
			*rd = (char *)_aif_alloc(BUFSIZ);

			if ( *ptr_type_1 == AIF_PTR_NAME )
			{
				int i;
				for ( i = 0; i < 5; i++) (*rd)[i] = ptr_type_1[i];
			}
			else
				**rd = (char) AIF_PTR_NORMAL;

			dres = NULL;
			fres = NULL;

			fds_type = FDSType(*f1);
			tmp1 = *f1;

			ret = _aif_binary_op(op, &fres, &dres, f1, d1, f2, d2);

			{
				char * ff = fres;
				char * dd = dres;

				_fds_skip_data(&ff, &dd);

				if ( *ptr_type_1 == AIF_PTR_NAME )
					memcpy((*rd)+5, dres, dd-dres);
				else
					memcpy((*rd)+1, dres, dd-dres);

				if ( fds_type == AIF_REFERENCE )
				{
					memcpy((*rf)+1, tmp1, *f1-tmp1);
					(*rf)[1 + *f1 - tmp1] = '\0';
				}
				else if ( fds_type == AIF_NAME )
				{
					tmp2 = tmp1;
					tmp2++;
					tmp2 = _fds_skipnum(tmp2);
					tmp2++;

					memcpy((*rf)+1, tmp1, tmp2-tmp1);
					(*rf)[1 + tmp2 - tmp1] = '\0';

					strncat((*rf), fres, ff-fres+1);
				}
				else
					strncat((*rf), fres, ff-fres+1);
			}

			return ret;

		}

	case AIF_STRING:
		if ( FDSType(*f2) != AIF_STRING || op != AIFOP_ADD )
		{
			SetAIFError(AIFERR_TYPE, NULL);
			return -1;
		}

		(*f1)++; /* past "s" */
		(*f2)++; /* past "s" */

		bytes1 = (*(*d1)++ & 0xff) << 8;
		bytes1 += *(*d1)++ & 0xff;
		bytes2 = (*(*d2)++ & 0xff) << 8;
		bytes2 += *(*d2)++ & 0xff;

                if ( *rd != NULL )
                        _aif_free(*rd);

		d = _aif_alloc(bytes1 + bytes2 + 2);

		d[0] = ((bytes1 + bytes2) >> 8) & 0xff;
		d[1] = (bytes1 + bytes2) & 0xff;

		memcpy(d+2, *d1, bytes1);
		memcpy(d+2+bytes1, *d2, bytes2);

		*d1 += bytes1;
		*d2 += bytes2;

		*rd = d;

		if ( *rf == NULL )
			*rf = strdup(AIF_STRING_TYPE());

		return 0;

	case AIF_VOID:
		if ( FDSType(*f2) == AIF_VOID )
		{
			d = NULL;
			res = 0;
			if ( _longest_to_aif(&d, sizeof(int), (AIFLONGEST)res) < 0 )
				return -1;

			*rd = d;

			if ( *rf == NULL )
				*rf = strdup(AIF_INTEGER_TYPE(1, sizeof(int)));

			return 0;
		}
		else
		{
			d = NULL;
			res = 1;
			if ( _longest_to_aif(&d, sizeof(int), (AIFLONGEST)res) < 0 )
				return -1;

			*rd = d;

			if ( *rf == NULL )
				*rf = strdup(AIF_INTEGER_TYPE(1, sizeof(int)));

			return 0;
		}
	default:
		SetAIFError(AIFERR_TYPE, NULL);
		return -1;
	}
}

/*
 * Perform a unary operation. Argument can be integer or
 * floating point or structs or arrays resulting in such.
 * Advance the data and format pointers across the data.
 * Return 0 for success, -1 for failure.
 * Result in rd and rf.
 * If rd is null, we generate our own format and data strings.  It is
 * impossible to decide the proper length for the data for structured types; we
 * make wild guesses.  It's a bug.
 */
int
_aif_unary_op(aifop op, char **rf, char **rd, char **fds, char **data)
{
	int	i;
	int	bytes;
	char *	dp;

	switch ( FDSType(*fds) )
	{
	case AIF_BOOLEAN:
		bytes = _fds_count_bytes(fds);

		if ( *rf == NULL )
			*rf = strdup(AIF_BOOLEAN_TYPE());

		if ( _aif_unary_op_bool(op, rd, *data, bytes) < 0 )
			return -1;

		(*data) += bytes;
		return 0;

	case AIF_ENUM:
		dp = *fds;
		bytes = _fds_count_bytes(fds);

		if ( *rf == NULL )
			*rf = strdup(AIF_INTEGER_TYPE(1, bytes));
		else
		{
			char *t1, *t2;
			int len;

			t1 = strstr(*rf, dp);	

			if ( t1 == NULL )
			{
				SetAIFError(AIFERR_CONV, NULL);
				return -1;
			}

			t2 = _fds_base_type(t1);
			len = strlen(t2);
			memmove(t1, t2, len+1);
		}

		if ( _aif_unary_op_int(op, rd, *data, bytes) < 0 )
			return -1;

		(*data) += bytes;

		return 0;

	case AIF_INTEGER:
		bytes = _fds_count_bytes_na(fds);

		if ( *rf == NULL )
			*rf = strdup(AIF_INTEGER_TYPE(FDSIsSigned(*fds), bytes));
		_fds_advance(fds);

		if ( _aif_unary_op_int(op, rd, *data, bytes) < 0 )
			return -1;

		(*data) += bytes;

		return 0;

	case AIF_FLOATING:
		bytes = _fds_count_bytes(fds);

		if ( *rf == NULL )
			*rf = strdup(AIF_FLOATING_TYPE(bytes));

		if ( _aif_unary_op_float(op, rd, *data, bytes) < 0 )
			return -1;

		(*data) += bytes;

		return 0;

	case AIF_ARRAY:
		{
			AIFIndex *	ix;
			char *		fmt;
			char *		fres = NULL;

			ix = FDSArrayIndexInit(*fds);

			if ( *rd == NULL )
			{
				*rd = (char *)_aif_alloc(ix->i_nel * ix->i_bsize); /* works unless we have subordinate lists */
				*rf = strdup(*fds);
			}

			if ( *rf == NULL )
				*rf = strdup(*fds);

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

			dp = *rd; /* start of our data result region */

			for ( i = 0 ; i < ix->i_nel ; i++ )
			{
				if ( fres != NULL )
				{
					_aif_free(fres);
					fres = NULL;
				}

				fmt = ix->i_btype;

				if ( _aif_unary_op(op, &fres, &dp, &fmt, data) < 0 )
				{
					AIFArrayIndexFree(ix);
					return -1;
				}

				dp += ix->i_bsize;

				AIFArrayIndexInc(ix);
			}

                        {
                                char *t1, *t2;
                                int len;

                                t1 = _fds_base_type(*rf);
                                t2 = t1;
                                _fds_advance(&t2);

                                len = strlen(fres);
                                memmove(t1, fres, len);
                                t1 += len;

                                if ( *t2 != '\0' )
                                {
                                        len = strlen(t2);
                                        memmove(t1, t2, len+1);
                                }
                                else
                                        *t1 = '\0';
                        }


			_aif_free(fres);

			AIFArrayIndexFree(ix);

			return 0;
		}

	/* only works with the public section in AIF_STRUCT */
	case AIF_STRUCT:
		if ( *rd == NULL )
		{
			*rd = (char *)_aif_alloc(BUFSIZ); /* no way to know how big! */
			*rf = strdup(*fds); /* will include trailing junk.  Bug */
		}

		if ( *rf == NULL )
			*rf = strdup(*fds);

		(*fds)++; /* past open brace */
		_fds_skipid(fds);

		dp = *rd;

		while ( **fds != ';' )
		{
			char * fres = NULL;
			char * fmt;

			*fds = strchr(*fds, FDS_STRUCT_FIELD_NAME_END) + 1; /*to start of field*/
			fmt = *fds;

			i = _fds_count_bytes_na(fds);

			if ( _aif_unary_op(op, &fres, &dp, fds, data) < 0 )
				return -1; /* non-zero res means no need to look further */

			dp += i;

                        {
                                char *t1, *t2;
                                int len;

                                t1 = strstr(*rf, fmt);
                                t2 = strstr(*rf, *fds);

                                len = strlen(fres);
                                memmove(t1, fres, len);
                                t1 += len;

                                if ( t1 != t2 )
                                {
                                        len = strlen(t2);
                                        memmove(t1, t2, len+1);
                                }
                        }

			_aif_free(fres);

			if (**fds == ',') (*fds)++;
		}

		return 0;

	default:
		SetAIFError(AIFERR_TYPE, NULL);
		return -1;
	}

	/* NOT REACHED */
}

AIF *
_aif_unary(AIF *a, aifop op)
{
	char *	rd;
	char *	rf;
	char *	f;
	char *	d;

	if ( a == (AIF *)NULL )
	{
		SetAIFError(AIFERR_BADARG, NULL);
		return (AIF *)NULL;
	}
	
	f = AIF_FORMAT(a);
	d = AIF_DATA(a);

	rf = NULL;
	rd = _aif_alloc(AIF_LEN(a));

	if ( _aif_unary_op(op, &rf, &rd, &f, &d) < 0 )
		return (AIF *)NULL;

	return MakeAIF(rf, rd);
}

AIF *
AIFNot(AIF *a)
{
	return _aif_unary(a, AIFOP_NOT);
}

AIF *
AIFNeg(AIF *a)
{
	return _aif_unary(a, AIFOP_NEG);
}

AIF *
_aif_binary(AIF *a1, AIF *a2, aifop op)
{
	char *	rd;
	char *	rf;
	char *	f1;
	char *	f2;
	char *	d1;
	char *	d2;

	if ( a1 == (AIF *)NULL || a2 == (AIF *)NULL )
	{
		SetAIFError(AIFERR_BADARG, NULL);
		return NULL;
	}

	f1 = AIF_FORMAT(a1);
	f2 = AIF_FORMAT(a2);
	d1 = AIF_DATA(a1);
	d2 = AIF_DATA(a2);

	rf = NULL; /* we don't know result so let _aif_binary_op work it out */
	rd = NULL;

	if ( _aif_binary_op(op, &rf, &rd, &f1, &d1, &f2, &d2) < 0 )
		return (AIF *)NULL;

	return MakeAIF(rf, rd);
}
AIF *
AIFAdd(AIF *a1, AIF *a2)
{
	return _aif_binary(a1, a2, AIFOP_ADD);
}

AIF *
AIFSub(AIF *a1, AIF *a2)
{
	return _aif_binary(a1, a2, AIFOP_SUB);
}

AIF *
AIFMul(AIF *a1, AIF *a2)
{
	return _aif_binary(a1, a2, AIFOP_MUL);
} 

AIF *
AIFDiv(AIF *a1, AIF *a2)
{
	return _aif_binary(a1, a2, AIFOP_DIV);
} 

AIF *
AIFRem(AIF *a1, AIF *a2)
{
	return _aif_binary(a1, a2, AIFOP_REM);
} 

AIF *
AIFAnd(AIF *a1, AIF *a2)
{
	return _aif_binary(a1, a2, AIFOP_AND);
} 

AIF *
AIFOr(AIF *a1, AIF *a2)
{
	return _aif_binary(a1, a2, AIFOP_OR);
} 



