Blob Blame Raw


/*----------------------------------------------------------------------------*
 |                                                                            |
 |                                   avl.c                                    |
 |                                                                            |
 |                by W.T.  11-MAR-91, last revision 11-OCT-96                 |
 |                                                                            |
 *----------------------------------------------------------------------------*/

#ifdef WIN32
#pragma warning(push)
#pragma warning(disable : 4113)
#pragma warning(disable : 4996)
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define AVL_C
#include "avl.h"
#include "tcm.h"
#define CASE \
	break;   \
	case
#define __OR case
#define DEFAULT \
	break;      \
	default

#define LOOP for (;;)

#ifndef TRUE
#define TRUE 1
#define FALSE 0
#endif

//#define NIL ((void *)0)

typedef unsigned long ulong;

#ifndef MACOSX

typedef unsigned int uint;
typedef unsigned short ushort;

#endif

typedef struct avl_node NODE;
typedef struct avl_path PATH;

#define MBR_KEY (AVL_NODUP_MBR >> 1)
#define PTR_KEY (AVL_NODUP_PTR >> 1)
#define STR_KEY (AVL_NODUP_STR >> 1)
#define LNG_KEY (AVL_NODUP_LNG >> 1)
#define INT_KEY (AVL_NODUP_INT >> 1)
#define SHT_KEY (AVL_NODUP_SHT >> 1)
#define ULN_KEY (AVL_NODUP_ULN >> 1)
#define UIN_KEY (AVL_NODUP_UIN >> 1)
#define USH_KEY (AVL_NODUP_USH >> 1)
#define CHR_KEY (AVL_NODUP_CHR >> 1)

#define USR_CMP 0
#define STR_CMP 1
#define VAL_CMP 2
#define COR_CMP 3

#define NODUP 0
#define DUP 1

#define USR_NODUP (USR_CMP | NODUP << 2)
#define STR_NODUP (STR_CMP | NODUP << 2)
#define VAL_NODUP (VAL_CMP | NODUP << 2)
#define COR_NODUP (COR_CMP | NODUP << 2)
#define USR_DUP (USR_CMP | DUP << 2)
#define STR_DUP (STR_CMP | DUP << 2)
#define VAL_DUP (VAL_CMP | DUP << 2)
#define COR_DUP (COR_CMP | DUP << 2)

/* keyinfo: bits 6-3: KEYTYPE, bit 2: DUP, bits 1-0: CMPTYPE */
/*                bits 6-2: TREETYPE, bits 2-0: LOCTYPE      */

#define KEYTYPE(keyinfo) ((keyinfo) >> 3)
#define TREETYPE(keyinfo) ((keyinfo) >> 2)
#define CMPTYPE(keyinfo) ((keyinfo)&0x3)
#define LOCTYPE(keyinfo) ((keyinfo)&0x7)

#define MAXUSHORT (((unsigned short)(~0)) >> 1)

#define MINLONG ((long)~(((unsigned long)(~0L)) >> 1))

#define CORRECT(u) ((long)(u) + MINLONG)

#ifdef VAX
#define SET_STRCMP(cmp, str1, str2)                                   \
	{                                                                 \
		register char *p1, *p2;                                       \
		for (p1 = (str1), p2 = (str2); *p1 && *p1 == *p2; p1++, p2++) \
			;                                                         \
		(cmp) = *(unsigned char *)p1 - *(unsigned char *)p2;          \
	}
#else
#define SET_STRCMP(cmp, str1, str2)     \
	{                                   \
		(cmp) = strcmp((str1), (str2)); \
	}
#endif

#define SET_PTRCMP(cmp, usrcmp, ptr1, ptr2)                   \
	{                                                         \
		if (usrcmp)                                           \
			(cmp) = (*(usrcmp))((ptr1), (ptr2));              \
		else                                                  \
			SET_STRCMP((cmp), (char *)(ptr1), (char *)(ptr2)) \
	}

#define ERROR (-1)

#define BAL 0
#define LEFT 1
#define RIGHT 2
#define LEFTUNBAL 3
#define RIGHTUNBAL 4

#define LESS 3
#define SAME 4

#define NOTINS 0
#define INS 1
#define DEEPER 2

#define NODE_LIST 0
#define TREE_LIST (NODE_LIST + (sizeof(NODE) != sizeof(TREE)))
#define PATH_LIST (TREE_LIST + 1)
#define FREE_LISTS (PATH_LIST + 1)

#define LONG_ALIGNED_SIZE(obj) ((sizeof(obj) + sizeof(long) - 1) & ~(sizeof(long) - 1))

#define SIZEOF_NODE LONG_ALIGNED_SIZE(NODE)
#define SIZEOF_TREE LONG_ALIGNED_SIZE(TREE)
#define SIZEOF_PATH LONG_ALIGNED_SIZE(PATH)
#define SIZEOF_LONG sizeof(long)

#define PREALLOC_LONGS (0x7FF0 / SIZEOF_LONG)
#define PREALLOC_SIZE (PREALLOC_LONGS * SIZEOF_LONG)
#define MALLOC_LONGS (0x7FE0 / SIZEOF_LONG)
#define MALLOC_SIZE (MALLOC_LONGS * SIZEOF_LONG)

static long Prealloc[PREALLOC_LONGS];

static void *Free_List[FREE_LISTS]; /* init to 0 by C */
static char *Avail_Base = (char *)Prealloc;
static uint Avail_Size = PREALLOC_SIZE;

/*===========================================================================*/

static void *new_memory(uint size)
{
	char *base;

	while (Avail_Size >= SIZEOF_NODE) {
		base = Avail_Base + (Avail_Size -= SIZEOF_NODE);
		*(void **)base = Free_List[NODE_LIST];
		Free_List[NODE_LIST] = (void *)base;
	}
	if (Avail_Base = (char *)malloc(MALLOC_SIZE)) {
		Avail_Size = MALLOC_SIZE - size;
		return (void *)(Avail_Base + Avail_Size);
	} else {
		Avail_Size = 0;
		return NIL;
	}
}

/*---------------------------------------------------------------------------*/

#define ALLOC_OBJ(ptr, ptr_type, size, list)          \
	{                                                 \
		if (Free_List[list]) {                        \
			(ptr) = (ptr_type)Free_List[list];        \
			Free_List[list] = *(void **)(ptr);        \
		} else if (Avail_Size >= size)                \
			(ptr) = (ptr_type)(Avail_Base +           \
							   (Avail_Size -= size)); \
		else                                          \
			(ptr) = (ptr_type)new_memory(size);       \
	}

#define FREE_OBJ(ptr, list)                \
	{                                      \
		*(void **)(ptr) = Free_List[list]; \
		Free_List[list] = (void *)(ptr);   \
	}

#define ALLOC_NODE(node) ALLOC_OBJ(node, NODE *, SIZEOF_NODE, NODE_LIST)
#define ALLOC_TREE(tree) ALLOC_OBJ(tree, TREE *, SIZEOF_TREE, TREE_LIST)
#define ALLOC_PATH(path) ALLOC_OBJ(path, PATH *, SIZEOF_PATH, PATH_LIST)

#define FREE_NODE(node) FREE_OBJ(node, NODE_LIST)
#define FREE_TREE(tree) FREE_OBJ(tree, TREE_LIST)
#define FREE_PATH(path) FREE_OBJ(path, PATH_LIST)

/*===========================================================================*/

TREE *avl__tree(int treetype, uint keyoffs, int (*usrcmp)(void *, void *))
{
	TREE *tree;
	int keyinfo;

	keyinfo = treetype << 2;
	switch (treetype) {
		CASE AVL_NODUP_MBR : __OR AVL_DUP_MBR : keyinfo |= USR_CMP;
		CASE AVL_NODUP_PTR : __OR AVL_DUP_PTR : keyinfo |= USR_CMP;
		CASE AVL_NODUP_STR : __OR AVL_DUP_STR : keyinfo |= STR_CMP;
		CASE AVL_NODUP_LNG : __OR AVL_DUP_LNG : keyinfo |= VAL_CMP;
		CASE AVL_NODUP_INT : __OR AVL_DUP_INT : keyinfo |= VAL_CMP;
		CASE AVL_NODUP_SHT : __OR AVL_DUP_SHT : keyinfo |= VAL_CMP;
		CASE AVL_NODUP_ULN : __OR AVL_DUP_ULN : keyinfo |= COR_CMP;
		CASE AVL_NODUP_UIN : __OR AVL_DUP_UIN : keyinfo |= COR_CMP;
		CASE AVL_NODUP_USH : __OR AVL_DUP_USH : keyinfo |= VAL_CMP;
		CASE AVL_NODUP_CHR : __OR AVL_DUP_CHR : keyinfo |= VAL_CMP;
	DEFAULT:
		return NIL;
	}
	ALLOC_TREE(tree)
	if (!tree)
		return NIL;
	tree->root = NIL;
	tree->keyinfo = (ushort)keyinfo;
	tree->keyoffs = (ushort)keyoffs;
	tree->usrcmp = usrcmp;
	tree->nodes = 0L;
	tree->path = NIL;

	return tree;
}

/*===========================================================================*/

static int rebalance(NODE **rootaddr)
{
	NODE *root = *rootaddr;
	NODE *newroot;
	NODE *half;

	switch (root->bal) {
		CASE LEFTUNBAL : switch (root->left->bal)
		{
			CASE LEFT : /* simple rotation, tree depth decreased */
						newroot = root->left;
			root->left = newroot->right;
			root->bal = BAL;
			newroot->right = root;
			newroot->bal = BAL;
			*rootaddr = newroot;
			return LESS;

			CASE BAL : /* simple rotation, tree depth unchanged */
					   newroot = root->left;
			root->left = newroot->right;
			root->bal = LEFT;
			newroot->right = root;
			newroot->bal = RIGHT;
			*rootaddr = newroot;
			return SAME;

			CASE RIGHT : /* double rotation */
						 half = root->left;
			newroot = half->right;
			root->left = newroot->right;
			half->right = newroot->left;
			switch (newroot->bal) {
				CASE BAL : root->bal = BAL;
				half->bal = BAL;
				CASE LEFT : root->bal = RIGHT;
				half->bal = BAL;
				CASE RIGHT : root->bal = BAL;
				half->bal = LEFT;
			}
			newroot->left = half;
			newroot->right = root;
			newroot->bal = BAL;
			*rootaddr = newroot;
			return LESS;
		}
		CASE RIGHTUNBAL : switch (root->right->bal)
		{
			CASE RIGHT : /* simple rotation, tree depth decreased */
						 newroot = root->right;
			root->right = newroot->left;
			root->bal = BAL;
			newroot->left = root;
			newroot->bal = BAL;
			*rootaddr = newroot;
			return LESS;

			CASE BAL : /* simple rotation, tree depth unchanged */
					   newroot = root->right;
			root->right = newroot->left;
			root->bal = RIGHT;
			newroot->left = root;
			newroot->bal = LEFT;
			*rootaddr = newroot;
			return SAME;

			CASE LEFT : /* double rotation */
						half = root->right;
			newroot = half->left;
			root->right = newroot->left;
			half->left = newroot->right;
			switch (newroot->bal) {
				CASE BAL : root->bal = BAL;
				half->bal = BAL;
				CASE RIGHT : root->bal = LEFT;
				half->bal = BAL;
				CASE LEFT : root->bal = BAL;
				half->bal = RIGHT;
			}
			newroot->right = half;
			newroot->left = root;
			newroot->bal = BAL;
			*rootaddr = newroot;
			return LESS;
		}
	DEFAULT:
		return SAME;
	}
	return ERROR;
}

/*===========================================================================*/

static int insert_ptr(NODE **rootaddr,
					  NODE *node, int (*usrcmp)(void *, void *), int dup)
{
	NODE *root = *rootaddr;
	int cmp;
	int ins;

	SET_PTRCMP(cmp, usrcmp, node->key.ptr, root->key.ptr)
	if (cmp < 0) {
		if (root->left)
			ins = insert_ptr(&root->left, node, usrcmp, dup);
		else {
			root->left = node;
			ins = DEEPER;
		}
		switch (ins) {
			CASE DEEPER : switch (root->bal)
			{
				CASE RIGHT : root->bal = BAL;
				return INS;
				CASE BAL : root->bal = LEFT;
				return DEEPER;
				CASE LEFT : root->bal = LEFTUNBAL;
				return rebalance(rootaddr) == LESS ? INS : DEEPER;
			}
			CASE INS : return INS;
			CASE NOTINS : return NOTINS;
		}
	} else if (cmp > 0 || dup) {
		if (root->right)
			ins = insert_ptr(&root->right, node, usrcmp, dup);
		else {
			root->right = node;
			ins = DEEPER;
		}
		switch (ins) {
			CASE DEEPER : switch (root->bal)
			{
				CASE LEFT : root->bal = BAL;
				return INS;
				CASE BAL : root->bal = RIGHT;
				return DEEPER;
				CASE RIGHT : root->bal = RIGHTUNBAL;
				return rebalance(rootaddr) == LESS ? INS : DEEPER;
			}
			CASE INS : return INS;
			CASE NOTINS : return NOTINS;
		}
	}
	return NOTINS;
}

/*---------------------------------------------------------------------------*/

static int insert_val(NODE **rootaddr, NODE *node, int dup)
{
	NODE *root = *rootaddr;
	int ins;

	if (node->key.val < root->key.val) {
		if (root->left)
			ins = insert_val(&root->left, node, dup);
		else {
			root->left = node;
			ins = DEEPER;
		}
		switch (ins) {
			CASE DEEPER : switch (root->bal)
			{
				CASE RIGHT : root->bal = BAL;
				return INS;
				CASE BAL : root->bal = LEFT;
				return DEEPER;
				CASE LEFT : root->bal = LEFTUNBAL;
				return rebalance(rootaddr) == LESS ? INS : DEEPER;
			}
			CASE INS : return INS;
			CASE NOTINS : return NOTINS;
		}
	} else if (node->key.val > root->key.val || dup) {
		if (root->right)
			ins = insert_val(&root->right, node, dup);
		else {
			root->right = node;
			ins = DEEPER;
		}
		switch (ins) {
			CASE DEEPER : switch (root->bal)
			{
				CASE LEFT : root->bal = BAL;
				return INS;
				CASE BAL : root->bal = RIGHT;
				return DEEPER;
				CASE RIGHT : root->bal = RIGHTUNBAL;
				return rebalance(rootaddr) == LESS ? INS : DEEPER;
			}
			CASE INS : return INS;
			CASE NOTINS : return NOTINS;
		}
	}
	return NOTINS;
}

/*---------------------------------------------------------------------------*/

int avl_insert(TREE *tree, void *data)
{
	NODE *node;
	int keyinfo;
	int ins;

	ALLOC_NODE(node)
	if (!node)
		return FALSE;
	node->data = data;
	node->left = NIL;
	node->right = NIL;
	node->bal = BAL;
	keyinfo = tree->keyinfo;

	switch (KEYTYPE(keyinfo)) {
		CASE MBR_KEY : node->key.ptr = (void *)((char *)data + tree->keyoffs);
		CASE PTR_KEY : node->key.ptr = *(void **)((char *)data + tree->keyoffs);
		CASE STR_KEY : node->key.ptr = *(void **)((char *)data + tree->keyoffs);
		CASE LNG_KEY : node->key.val = *(long *)((char *)data + tree->keyoffs);
		CASE INT_KEY : node->key.val = *(int *)((char *)data + tree->keyoffs);
		CASE SHT_KEY : node->key.val = *(short *)((char *)data + tree->keyoffs);
		CASE ULN_KEY : node->key.val =
			CORRECT(*(ulong *)((char *)data + tree->keyoffs));
		CASE UIN_KEY : node->key.val =
			CORRECT(*(uint *)((char *)data + tree->keyoffs));
		CASE USH_KEY : node->key.val = *(ushort *)((char *)data + tree->keyoffs);
		CASE CHR_KEY : node->key.val = *((char *)data + tree->keyoffs);
	DEFAULT:
		FREE_NODE(node)
		return FALSE;
	}
	if (tree->root) {
		switch (LOCTYPE(keyinfo)) {
			CASE USR_NODUP : ins = insert_ptr(&tree->root, node, tree->usrcmp, NODUP);
			CASE STR_NODUP : ins = insert_ptr(&tree->root, node, AVL_AVLCMP, NODUP);
			CASE COR_NODUP : __OR VAL_NODUP : ins = insert_val(&tree->root, node, NODUP);
			CASE USR_DUP : ins = insert_ptr(&tree->root, node, tree->usrcmp, DUP);
			CASE STR_DUP : ins = insert_ptr(&tree->root, node, AVL_AVLCMP, DUP);
			CASE COR_DUP : __OR VAL_DUP : ins = insert_val(&tree->root, node, DUP);
		}
		if (ins == NOTINS) {
			FREE_NODE(node)
			return FALSE;
		}
	} else {
		tree->root = node;
	}
	tree->nodes++;
	if (tree->path) {
		FREE_PATH(tree->path)
		tree->path = NIL;
	}
	return TRUE;
}

/*===========================================================================*/

#define SELECT_EQ_NODUP(node, val, this) \
	{                                    \
		if (this < val)                  \
			node = node->right;          \
		else if (val < this)             \
			node = node->left;           \
		else                             \
			return node->data;           \
	}

#define SELECT_EQ_DUP(node, val, this, save) \
	{                                        \
		if (this < val)                      \
			node = node->right;              \
		else if (val < this)                 \
			node = node->left;               \
		else {                               \
			save = node;                     \
			node = node->left;               \
		}                                    \
	}

#define SELECT_GE_NODUP(node, val, this, save) \
	{                                          \
		if (val < this) {                      \
			save = node;                       \
			node = node->left;                 \
		} else if (this < val)                 \
			node = node->right;                \
		else                                   \
			return node->data;                 \
	}

#define SELECT_GE_DUP(node, val, this, save) \
	{                                        \
		if (this < val)                      \
			node = node->right;              \
		else {                               \
			save = node;                     \
			node = node->left;               \
		}                                    \
	}

#define SELECT_GT(node, val, this, save) \
	{                                    \
		if (val < this) {                \
			save = node;                 \
			node = node->left;           \
		} else                           \
			node = node->right;          \
	}

#define SELECT_LE_NODUP(node, val, this, save) \
	{                                          \
		if (this < val) {                      \
			save = node;                       \
			node = node->right;                \
		} else if (val < this)                 \
			node = node->left;                 \
		else                                   \
			return node->data;                 \
	}

#define SELECT_LE_DUP(node, val, this, save) \
	{                                        \
		if (val < this)                      \
			node = node->left;               \
		else {                               \
			save = node;                     \
			node = node->right;              \
		}                                    \
	}

#define SELECT_LT(node, val, this, save) \
	{                                    \
		if (this < val) {                \
			save = node;                 \
			node = node->right;          \
		} else                           \
			node = node->left;           \
	}

/*---------------------------------------------------------------------------*/

void *avl__locate(TREE *tree, long keyval)
{
	NODE *node;
	NODE *save;
	int (*usrcmp)(void *, void *);
	int cmp;

	node = tree->root;
	switch (LOCTYPE(tree->keyinfo)) {
		CASE USR_NODUP : usrcmp = tree->usrcmp;
		while (node) {
			cmp = (*usrcmp)((void *)keyval, node->key.ptr);
			SELECT_EQ_NODUP(node, cmp, 0)
		}
		CASE STR_NODUP : while (node)
		{
			SET_STRCMP(cmp, (char *)keyval, (char *)node->key.ptr)
			SELECT_EQ_NODUP(node, cmp, 0)
		}
		CASE COR_NODUP : keyval = CORRECT(keyval);
		__OR VAL_NODUP : while (node)
							 SELECT_EQ_NODUP(node, keyval, node->key.val)

								 CASE USR_DUP : save = NIL;
		usrcmp = tree->usrcmp;
		while (node) {
			cmp = (*usrcmp)((void *)keyval, node->key.ptr);
			SELECT_EQ_DUP(node, cmp, 0, save)
		}
		if (save)
			return save->data;

		CASE STR_DUP : save = NIL;
		while (node) {
			SET_STRCMP(cmp, (char *)keyval, (char *)node->key.ptr)
			SELECT_EQ_DUP(node, cmp, 0, save)
		}
		if (save)
			return save->data;

		CASE COR_DUP : keyval = CORRECT(keyval);
		__OR VAL_DUP : save = NIL;
		while (node)
			SELECT_EQ_DUP(node, keyval, node->key.val, save)
		if (save)
			return save->data;
	}
	return NIL;
}

/*---------------------------------------------------------------------------*/

void *avl__locate_ge(TREE *tree, long keyval)
{
	NODE *node;
	NODE *save;
	int (*usrcmp)(void *, void *);
	int cmp;

	node = tree->root;
	save = NIL;
	switch (LOCTYPE(tree->keyinfo)) {
		CASE USR_NODUP : usrcmp = tree->usrcmp;
		while (node) {
			cmp = (*usrcmp)((void *)keyval, node->key.ptr);
			SELECT_GE_NODUP(node, cmp, 0, save)
		}
		CASE STR_NODUP : while (node)
		{
			SET_STRCMP(cmp, (char *)keyval, (char *)node->key.ptr)
			SELECT_GE_NODUP(node, cmp, 0, save)
		}
		CASE COR_NODUP : keyval = CORRECT(keyval);
		__OR VAL_NODUP : while (node)
							 SELECT_GE_NODUP(node, keyval, node->key.val, save)

								 CASE USR_DUP : usrcmp = tree->usrcmp;
		while (node) {
			cmp = (*usrcmp)((void *)keyval, node->key.ptr);
			SELECT_GE_DUP(node, cmp, 0, save)
		}
		CASE STR_DUP : while (node)
		{
			SET_STRCMP(cmp, (char *)keyval, (char *)node->key.ptr)
			SELECT_GE_DUP(node, cmp, 0, save)
		}
		CASE COR_DUP : keyval = CORRECT(keyval);
		__OR VAL_DUP : while (node)
						   SELECT_GE_DUP(node, keyval, node->key.val, save)
	}
	if (save)
		return save->data;
	else
		return NIL;
}

/*---------------------------------------------------------------------------*/

void *avl__locate_gt(TREE *tree, long keyval)
{
	NODE *node;
	NODE *save;
	int (*usrcmp)(void *, void *);
	int cmp;

	node = tree->root;
	save = NIL;
	switch (CMPTYPE(tree->keyinfo)) {
		CASE USR_CMP : usrcmp = tree->usrcmp;
		while (node) {
			cmp = (*usrcmp)((void *)keyval, node->key.ptr);
			SELECT_GT(node, cmp, 0, save)
		}
		CASE STR_CMP : while (node)
		{
			SET_STRCMP(cmp, (char *)keyval, (char *)node->key.ptr)
			SELECT_GT(node, cmp, 0, save)
		}
		CASE COR_CMP : keyval = CORRECT(keyval);
		__OR VAL_CMP : while (node)
						   SELECT_GT(node, keyval, node->key.val, save)
	}
	if (save)
		return save->data;
	else
		return NIL;
}

/*---------------------------------------------------------------------------*/

void *avl__locate_le(TREE *tree, long keyval)
{
	NODE *node;
	NODE *save;
	int (*usrcmp)(void *, void *);
	int cmp;

	node = tree->root;
	save = NIL;
	switch (LOCTYPE(tree->keyinfo)) {
		CASE USR_NODUP : usrcmp = tree->usrcmp;
		while (node) {
			cmp = (*usrcmp)((void *)keyval, node->key.ptr);
			SELECT_LE_NODUP(node, cmp, 0, save)
		}
		CASE STR_NODUP : while (node)
		{
			SET_STRCMP(cmp, (char *)keyval, (char *)node->key.ptr)
			SELECT_LE_NODUP(node, cmp, 0, save)
		}
		CASE COR_NODUP : keyval = CORRECT(keyval);
		__OR VAL_NODUP : while (node)
							 SELECT_LE_NODUP(node, keyval, node->key.val, save)

								 CASE USR_DUP : usrcmp = tree->usrcmp;
		while (node) {
			cmp = (*usrcmp)((void *)keyval, node->key.ptr);
			SELECT_LE_DUP(node, cmp, 0, save)
		}
		CASE STR_DUP : while (node)
		{
			SET_STRCMP(cmp, (char *)keyval, (char *)node->key.ptr)
			SELECT_LE_DUP(node, cmp, 0, save)
		}
		CASE COR_DUP : keyval = CORRECT(keyval);
		__OR VAL_DUP : while (node)
						   SELECT_LE_DUP(node, keyval, node->key.val, save)
	}
	if (save)
		return save->data;
	else
		return NIL;
}

/*---------------------------------------------------------------------------*/

void *avl__locate_lt(TREE *tree, long keyval)
{
	NODE *node;
	NODE *save;
	int (*usrcmp)(void *, void *);
	int cmp;

	node = tree->root;
	save = NIL;
	switch (CMPTYPE(tree->keyinfo)) {
		CASE USR_CMP : usrcmp = tree->usrcmp;
		while (node) {
			cmp = (*usrcmp)((void *)keyval, node->key.ptr);
			SELECT_LT(node, cmp, 0, save)
		}
		CASE STR_CMP : while (node)
		{
			SET_STRCMP(cmp, (char *)keyval, (char *)node->key.ptr)
			SELECT_LT(node, cmp, 0, save)
		}
		CASE COR_CMP : keyval = CORRECT(keyval);
		__OR VAL_CMP : while (node)
						   SELECT_LT(node, keyval, node->key.val, save)
	}
	if (save)
		return save->data;
	else
		return NIL;
}

/*---------------------------------------------------------------------------*/

void *avl_locate_first(TREE *tree)
{
	NODE *node, *leftnode;

	node = tree->root;
	if (!node)
		return NIL;
	while (leftnode = node->left)
		node = leftnode;
	return node->data;
}

/*---------------------------------------------------------------------------*/

void *avl_locate_last(TREE *tree)
{
	NODE *node, *rightnode;

	node = tree->root;
	if (!node)
		return NIL;
	while (rightnode = node->right)
		node = rightnode;
	return node->data;
}

/*===========================================================================*/

static NODE *leftmost(NODE **rootaddr)
{
	NODE *root = *rootaddr;
	NODE *node;

	if (root) {
		if (root->left) {
			node = leftmost(&root->left);
			if (node->bal == LESS) {
				/* left subtree depth decreased */
				switch (root->bal) {
					CASE LEFT : root->bal = BAL;
					CASE BAL : root->bal = RIGHT;
					node->bal = SAME;
					CASE RIGHT : root->bal = RIGHTUNBAL;
					node->bal = rebalance(rootaddr);
				}
			}
			return node;
		} else {
			*rootaddr = root->right;
			root->bal = LESS;
			return root;
		}
	} else
		return NIL;
}

/*---------------------------------------------------------------------------*/

static NODE *remove_ptr(NODE **rootaddr,
						void *keyptr, int (*usrcmp)(void *, void *), int dup)
{
	NODE *root = *rootaddr;
	NODE *node;
	int cmp;

	SET_PTRCMP(cmp, usrcmp, keyptr, root->key.ptr)
	if (cmp < 0) {
		if (!root->left)
			return NIL;
		node = remove_ptr(&root->left, keyptr, usrcmp, dup);
		if (node && node->bal == LESS) {
			/* left subtree depth decreased */
			switch (root->bal) {
				CASE LEFT : root->bal = BAL;
				CASE BAL : root->bal = RIGHT;
				node->bal = SAME;
				CASE RIGHT : root->bal = RIGHTUNBAL;
				node->bal = rebalance(rootaddr);
			}
		}
	} else if (cmp > 0) {
		if (!root->right)
			return NIL;
		node = remove_ptr(&root->right, keyptr, usrcmp, dup);
		if (node && node->bal == LESS) {
			/* right subtree depth decreased */
			switch (root->bal) {
				CASE RIGHT : root->bal = BAL;
				CASE BAL : root->bal = LEFT;
				node->bal = SAME;
				CASE LEFT : root->bal = LEFTUNBAL;
				node->bal = rebalance(rootaddr);
			}
		}
	} else {
		if (dup && root->left && (node = remove_ptr(&root->left, keyptr, usrcmp, dup))) {
			if (node->bal == LESS) {
				/* left subtree depth decreased */
				switch (root->bal) {
					CASE LEFT : root->bal = BAL;
					CASE BAL : root->bal = RIGHT;
					node->bal = SAME;
					CASE RIGHT : root->bal = RIGHTUNBAL;
					node->bal = rebalance(rootaddr);
				}
			}
		} else {
			node = root;
			if (!node->right) {
				*rootaddr = node->left;
				node->bal = LESS;
			} else if (!node->left) {
				*rootaddr = node->right;
				node->bal = LESS;
			} else /* replace by the leftmost node of the right subtree */
			{
				root = leftmost(&node->right);
				root->left = node->left;
				root->right = node->right;
				if (root->bal == LESS) {
					/* right subtree depth decreased */
					switch (node->bal) {
						CASE RIGHT : root->bal = BAL;
						node->bal = LESS;
						CASE BAL : root->bal = LEFT;
						node->bal = SAME;
						CASE LEFT : root->bal = LEFTUNBAL;
						node->bal = rebalance(&root);
					}
				} else {
					root->bal = node->bal;
					node->bal = SAME;
				}
				*rootaddr = root;
			}
		}
	}
	return node;
}

/*---------------------------------------------------------------------------*/

static NODE *remove_val(NODE **rootaddr, long keyval, int dup)
{
	NODE *root = *rootaddr;
	NODE *node;

	if (keyval < root->key.val) {
		if (!root->left)
			return NIL;
		node = remove_val(&root->left, keyval, dup);
		if (node && node->bal == LESS) {
			/* left subtree depth decreased */
			switch (root->bal) {
				CASE LEFT : root->bal = BAL;
				CASE BAL : root->bal = RIGHT;
				node->bal = SAME;
				CASE RIGHT : root->bal = RIGHTUNBAL;
				node->bal = rebalance(rootaddr);
			}
		}
	} else if (keyval > root->key.val) {
		if (!root->right)
			return NIL;
		node = remove_val(&root->right, keyval, dup);
		if (node && node->bal == LESS) {
			/* right subtree depth decreased */
			switch (root->bal) {
				CASE RIGHT : root->bal = BAL;
				CASE BAL : root->bal = LEFT;
				node->bal = SAME;
				CASE LEFT : root->bal = LEFTUNBAL;
				node->bal = rebalance(rootaddr);
			}
		}
	} else {
		if (dup && root->left && (node = remove_val(&root->left, keyval, dup))) {
			if (node->bal == LESS) {
				/* left subtree depth decreased */
				switch (root->bal) {
					CASE LEFT : root->bal = BAL;
					CASE BAL : root->bal = RIGHT;
					node->bal = SAME;
					CASE RIGHT : root->bal = RIGHTUNBAL;
					node->bal = rebalance(rootaddr);
				}
			}
		} else {
			node = root;
			if (!node->right) {
				*rootaddr = node->left;
				node->bal = LESS;
			} else if (!node->left) {
				*rootaddr = node->right;
				node->bal = LESS;
			} else /* replace by the leftmost node of the right subtree */
			{
				root = leftmost(&node->right);
				root->left = node->left;
				root->right = node->right;
				if (root->bal == LESS) {
					/* right subtree depth decreased */
					switch (node->bal) {
						CASE RIGHT : root->bal = BAL;
						node->bal = LESS;
						CASE BAL : root->bal = LEFT;
						node->bal = SAME;
						CASE LEFT : root->bal = LEFTUNBAL;
						node->bal = rebalance(&root);
					}
				} else {
					root->bal = node->bal;
					node->bal = SAME;
				}
				*rootaddr = root;
			}
		}
	}
	return node;
}

/*---------------------------------------------------------------------------*/

void *avl__remove(TREE *tree, long keyval)
{
	NODE *node;
	void *data;

	if (tree->root) {
		switch (LOCTYPE(tree->keyinfo)) {
			CASE USR_NODUP : node = remove_ptr(&tree->root, (void *)keyval, tree->usrcmp, NODUP);
			CASE STR_NODUP : node = remove_ptr(&tree->root, (void *)keyval, AVL_AVLCMP, NODUP);
			CASE COR_NODUP : keyval = CORRECT(keyval);
			__OR VAL_NODUP : node = remove_val(&tree->root, keyval, NODUP);
			CASE USR_DUP : node = remove_ptr(&tree->root, (void *)keyval, tree->usrcmp, DUP);
			CASE STR_DUP : node = remove_ptr(&tree->root, (void *)keyval, AVL_AVLCMP, DUP);
			CASE COR_DUP : keyval = CORRECT(keyval);
			__OR VAL_DUP : node = remove_val(&tree->root, keyval, DUP);
		}
		if (node) {
			tree->nodes--;
			if (tree->path) {
				FREE_PATH(tree->path)
				tree->path = NIL;
			}
			data = node->data;
			FREE_NODE(node)
			return data;
		}
	}
	return NIL;
}

/*===========================================================================*/

static void scan_subtree(NODE *root, void (*usrfun)(void *))
{
	if (root->left)
		scan_subtree(root->left, usrfun);
	(*usrfun)(root->data);
	if (root->right)
		scan_subtree(root->right, usrfun);
}

/*---------------------------------------------------------------------------*/

static void backscan_subtree(NODE *root, void (*usrfun)(void *))
{
	if (root->right)
		backscan_subtree(root->right, usrfun);
	(*usrfun)(root->data);
	if (root->left)
		backscan_subtree(root->left, usrfun);
}

/*---------------------------------------------------------------------------*/

void avl__scan(TREE *tree, void (*usrfun)(void *), int back)
{
	if (tree->root) {
		if (back)
			backscan_subtree(tree->root, usrfun);
		else
			scan_subtree(tree->root, usrfun);
	}
}

/*===========================================================================*/

void *avl_first(TREE *tree)
{
	PATH *path;
	char *pathright;
	NODE **pathnode;
	NODE *node;

	if (!tree->root)
		return NIL;
	if (!tree->path) {
		ALLOC_PATH(path)
		if (!path)
			return NIL;
		tree->path = path;
	} else {
		path = tree->path;
	}
	pathnode = &path->node[0];
	pathright = &path->right[1];
	*pathnode = NIL; /* sentinels */
	*pathright = TRUE;
	*++pathnode = NIL;
	*++pathright = FALSE;
	*++pathnode = node = tree->root;
	while (node = node->left) {
		*++pathright = FALSE;
		*++pathnode = node;
	}
	path->pathright = pathright;
	path->pathnode = pathnode;
	return (*pathnode)->data;
}

/*---------------------------------------------------------------------------*/

void *avl_last(TREE *tree)
{
	PATH *path;
	char *pathright;
	NODE **pathnode;
	NODE *node;

	if (!tree->root)
		return NIL;
	if (!tree->path) {
		ALLOC_PATH(path)
		if (!path)
			return NIL;
		tree->path = path;
	} else {
		path = tree->path;
	}
	pathnode = &path->node[0];
	pathright = &path->right[1];
	*pathnode = NIL; /* sentinels */
	*pathright = FALSE;
	*++pathnode = NIL;
	*++pathright = TRUE;
	*++pathnode = node = tree->root;
	while (node = node->right) {
		*++pathright = TRUE;
		*++pathnode = node;
	}
	path->pathright = pathright;
	path->pathnode = pathnode;
	return (*pathnode)->data;
}

/*---------------------------------------------------------------------------*/

#define DOWN_RIGHT_OR_BREAK(node, pathright, pathnode) \
	{                                                  \
		if (node = node->right) {                      \
			*++pathright = TRUE;                       \
			*++pathnode = node;                        \
		} else                                         \
			break;                                     \
	}

#define DOWN_LEFT_OR_BREAK(node, pathright, pathnode) \
	{                                                 \
		if (node = node->left) {                      \
			*++pathright = FALSE;                     \
			*++pathnode = node;                       \
		} else                                        \
			break;                                    \
	}

#define START_OK_AND_RETURN(path, _pathright, _pathnode) \
	{                                                    \
		path->pathright = _pathright;                    \
		path->pathnode = _pathnode;                      \
		return (*_pathnode)->data;                       \
	}

/*---------------------------------------------------------------------------*/

void *avl__start(TREE *tree, long keyval, int back)
{
	PATH *path;
	char *pathright;
	NODE **pathnode;
	NODE *node;
	char *saveright;
	NODE **savenode;
	int (*usrcmp)(void *, void *);
	int cmp;

	if (!tree->root)
		return NIL;
	if (!tree->path) {
		ALLOC_PATH(path)
		if (!path)
			return NIL;
		tree->path = path;
	} else {
		path = tree->path;
	}
	pathnode = &path->node[0];
	pathright = &path->right[1];
	saveright = NIL;
	savenode = NIL;
	*pathnode = NIL; /* sentinels */
	*pathright = !back;
	*++pathnode = NIL;
	*++pathright = back;
	*++pathnode = node = tree->root;
	switch (LOCTYPE(tree->keyinfo)) {
		CASE USR_NODUP : __OR STR_NODUP : usrcmp = tree->usrcmp;
		if (back)
			LOOP
			{
				SET_PTRCMP(cmp, usrcmp, (void *)keyval, node->key.ptr)
				if (cmp > 0) {
					saveright = pathright;
					savenode = pathnode;
					DOWN_RIGHT_OR_BREAK(node, pathright, pathnode)
				} else if (cmp < 0)
					DOWN_LEFT_OR_BREAK(node, pathright, pathnode)
				else
					START_OK_AND_RETURN(path, pathright, pathnode)
			}
		else
			LOOP
			{
				SET_PTRCMP(cmp, usrcmp, (void *)keyval, node->key.ptr)
				if (cmp < 0) {
					saveright = pathright;
					savenode = pathnode;
					DOWN_LEFT_OR_BREAK(node, pathright, pathnode)
				} else if (cmp > 0)
					DOWN_RIGHT_OR_BREAK(node, pathright, pathnode)
				else
					START_OK_AND_RETURN(path, pathright, pathnode)
			}
		CASE COR_NODUP : keyval = CORRECT(keyval);
		__OR VAL_NODUP : if (back)
							 LOOP
		{
			if (keyval > node->key.val) {
				saveright = pathright;
				savenode = pathnode;
				DOWN_RIGHT_OR_BREAK(node, pathright, pathnode)
			} else if (keyval < node->key.val)
				DOWN_LEFT_OR_BREAK(node, pathright, pathnode)
			else
				START_OK_AND_RETURN(path, pathright, pathnode)
		}
		else LOOP
		{
			if (keyval < node->key.val) {
				saveright = pathright;
				savenode = pathnode;
				DOWN_LEFT_OR_BREAK(node, pathright, pathnode)
			} else if (keyval > node->key.val)
				DOWN_RIGHT_OR_BREAK(node, pathright, pathnode)
			else
				START_OK_AND_RETURN(path, pathright, pathnode)
		}
		CASE USR_DUP : __OR STR_DUP : usrcmp = tree->usrcmp;
		if (back)
			LOOP
			{
				SET_PTRCMP(cmp, usrcmp, (void *)keyval, node->key.ptr)
				if (cmp >= 0) {
					saveright = pathright;
					savenode = pathnode;
					DOWN_RIGHT_OR_BREAK(node, pathright, pathnode)
				} else
					DOWN_LEFT_OR_BREAK(node, pathright, pathnode)
			}
		else
			LOOP
			{
				SET_PTRCMP(cmp, usrcmp, (void *)keyval, node->key.ptr)
				if (cmp <= 0) {
					saveright = pathright;
					savenode = pathnode;
					DOWN_LEFT_OR_BREAK(node, pathright, pathnode)
				} else
					DOWN_RIGHT_OR_BREAK(node, pathright, pathnode)
			}
		CASE COR_DUP : keyval = CORRECT(keyval);
		__OR VAL_DUP : if (back)
						   LOOP
		{
			if (keyval >= node->key.val) {
				saveright = pathright;
				savenode = pathnode;
				DOWN_RIGHT_OR_BREAK(node, pathright, pathnode)
			} else
				DOWN_LEFT_OR_BREAK(node, pathright, pathnode)
		}
		else LOOP
		{
			if (keyval <= node->key.val) {
				saveright = pathright;
				savenode = pathnode;
				DOWN_LEFT_OR_BREAK(node, pathright, pathnode)
			} else
				DOWN_RIGHT_OR_BREAK(node, pathright, pathnode)
		}
	}
	if (savenode) {
		path->pathright = saveright;
		path->pathnode = savenode;
		return (*savenode)->data;
	} else {
		FREE_PATH(path)
		tree->path = NIL;
		return NIL;
	}
}

/*---------------------------------------------------------------------------*/

void *avl_next(TREE *tree)
{
	PATH *path;
	char *pathright;
	NODE **pathnode;
	NODE *node;

	path = tree->path;
	if (!path)
		return NIL;
	pathright = path->pathright;
	pathnode = path->pathnode;
	if (node = (*pathnode)->right) {
		*++pathright = TRUE;
		*++pathnode = node;
		while (node = node->left) {
			*++pathright = FALSE;
			*++pathnode = node;
		}
	} else {
		while (*pathright) {
			--pathright;
			--pathnode;
		}
		--pathright;
		--pathnode;
		if (!*pathnode) {
			FREE_PATH(path)
			tree->path = NIL;
			return NIL;
		}
	}
	path->pathright = pathright;
	path->pathnode = pathnode;
	return (*pathnode)->data;
}

/*---------------------------------------------------------------------------*/

void *avl_prev(TREE *tree)
{
	PATH *path;
	char *pathright;
	NODE **pathnode;
	NODE *node;

	path = tree->path;
	if (!path)
		return NIL;
	pathright = path->pathright;
	pathnode = path->pathnode;
	if (node = (*pathnode)->left) {
		*++pathright = FALSE;
		*++pathnode = node;
		while (node = node->right) {
			*++pathright = TRUE;
			*++pathnode = node;
		}
	} else {
		while (!*pathright) {
			--pathright;
			--pathnode;
		}
		--pathright;
		--pathnode;
		if (!*pathnode) {
			FREE_PATH(path)
			tree->path = NIL;
			return NIL;
		}
	}
	path->pathright = pathright;
	path->pathnode = pathnode;
	return (*pathnode)->data;
}

/*---------------------------------------------------------------------------*/

void avl_stop(TREE *tree)
{
	if (tree->path) {
		FREE_PATH(tree->path)
		tree->path = NIL;
	}
}

/*===========================================================================*/

static uint Offset;
static void *Save;

/*---------------------------------------------------------------------------*/

static void link_subtree(NODE *node)
{
	if (node->right)
		link_subtree(node->right);
	*(void **)((char *)node->data + Offset) = Save;
	Save = node->data;
	if (node->left)
		link_subtree(node->left);
}

/*---------------------------------------------------------------------------*/

static void backlink_subtree(NODE *node)
{
	if (node->left)
		backlink_subtree(node->left);
	*(void **)((char *)node->data + Offset) = Save;
	Save = node->data;
	if (node->right)
		backlink_subtree(node->right);
}

/*---------------------------------------------------------------------------*/

void *avl__link(TREE *tree, uint ptroffs, int back)
{
	Offset = ptroffs;
	Save = NIL;
	if (tree->root) {
		if (back)
			backlink_subtree(tree->root);
		else
			link_subtree(tree->root);
	}
	return Save;
}

/*===========================================================================*/

static void release_subtree(NODE *root, void (*usrfun)(void *))
{
	if (root->left)
		release_subtree(root->left, usrfun);
	if (root->right)
		release_subtree(root->right, usrfun);
	(*usrfun)(root->data);
	FREE_NODE(root)
}

/*---------------------------------------------------------------------------*/

static void reset_subtree(NODE *root)
{
	if (root->left)
		reset_subtree(root->left);
	if (root->right)
		reset_subtree(root->right);
	FREE_NODE(root)
}

/*---------------------------------------------------------------------------*/

void avl_release(TREE *tree, void (*usrfun)(void *))
{
	if (tree->root)
		release_subtree(tree->root, usrfun);
	tree->root = NIL;
	tree->nodes = 0;
	if (tree->path) {
		FREE_PATH(tree->path)
		tree->path = NIL;
	}
}

/*---------------------------------------------------------------------------*/

void avl_reset(TREE *tree)
{
	if (tree->root)
		reset_subtree(tree->root);
	tree->root = NIL;
	tree->nodes = 0;
	if (tree->path) {
		FREE_PATH(tree->path)
		tree->path = NIL;
	}
}

/*---------------------------------------------------------------------------*/

void avl_close(TREE *tree)
{
	if (tree->root)
		reset_subtree(tree->root);
	if (tree->path)
		FREE_PATH(tree->path)
	tree->keyinfo = (ushort)ERROR;
	FREE_TREE(tree)
}

/*===========================================================================*/

static int copy_subtree(NODE *newroot, NODE *root)
{
	newroot->key = root->key;
	newroot->data = root->data;
	newroot->bal = root->bal;

	if (root->left) {
		ALLOC_NODE(newroot->left)
		if (!newroot->left)
			return FALSE;
		if (!copy_subtree(newroot->left, root->left)) {
			FREE_NODE(newroot->left);
			return FALSE;
		}
	} else
		newroot->left = NIL;

	if (root->right) {
		ALLOC_NODE(newroot->right)
		if (!newroot->right)
			return FALSE;
		if (!copy_subtree(newroot->right, root->right)) {
			FREE_NODE(newroot->right);
			return FALSE;
		}
	} else
		newroot->right = NIL;

	return TRUE;
}

/*---------------------------------------------------------------------------*/

TREE *avl_copy(TREE *tree)
{
	TREE *newtree;

	ALLOC_TREE(newtree)
	if (!newtree)
		return NIL;
	newtree->keyinfo = tree->keyinfo;
	newtree->keyoffs = tree->keyoffs;
	newtree->usrcmp = tree->usrcmp;
	newtree->nodes = tree->nodes;
	newtree->path = NIL;

	if (tree->root) {
		ALLOC_NODE(newtree->root)
		if (!copy_subtree(newtree->root, tree->root)) {
			FREE_NODE(newtree->root)
			avl_close(newtree);
			return NIL;
		}
	} else {
		newtree->root = NIL;
	}
	return newtree;
}

/*===========================================================================*/

static void **Dat;
static int *Lev;
static int *Pos;
static int Nod;
static int Max_Lev;

/*---------------------------------------------------------------------------*/

static void dump_subtree(struct avl_node *root, int lev, int pos)
{
	if (root->left)
		dump_subtree(root->left, lev + 1, pos * 2);
	Dat[Nod] = root->data;
	Lev[Nod] = lev;
	Pos[Nod] = pos;
	if (lev > Max_Lev)
		Max_Lev = lev;
	Nod++;
	if (root->right)
		dump_subtree(root->right, lev + 1, pos * 2 + 1);
}

/*---------------------------------------------------------------------------*/

int avl_dump(TREE *tree, void **dat_vect, int *lev_vect, int *pos_vect)
{
	Dat = dat_vect;
	Lev = lev_vect;
	Pos = pos_vect;
	Nod = 0;
	Max_Lev = -1;
	if (tree->root)
		dump_subtree(tree->root, 0, 0);
	return Max_Lev + 1;
}

/*===========================================================================*/
#ifdef LEVO
int avl_porting_problems(void)
{
	long lng1, lng2;
	ushort ush1, ush2;
	char chr1, chr2;
	void *ptr1, *ptr2;
	struct s {
		char c[4];
	} * ps;
	int problems;

#define PROBLEM(n) (problems |= 1 | (1 << (n)))

	problems = 0;

	Avl_Dummy[0] = 0.0;
	Avl_Dummy[1] = 0.0;
	((char *)Avl_Dummy)[1] = 0x15;
	ptr1 = (void *)((char *)Avl_Dummy + 1);
	lng1 = (long)ptr1;
	ptr2 = (void *)lng1;
	if (*(char *)ptr2 != (char)0x15)
		PROBLEM(1);

	ps = (struct s *)malloc(sizeof(struct s));
	ps->c[0] = 0;
	ps->c[1] = 0x15;
	ps->c[2] = 0;
	ps->c[3] = 0;
	ptr1 = (void *)&ps->c[1];
	lng1 = (long)ptr1;
	ptr2 = (void *)lng1;
	if (*(char *)ptr2 != (char)0x15)
		PROBLEM(2);
	free(ps);

	chr1 = 1;
	chr2 = 250;
	lng1 = chr1;
	lng2 = chr2;
	if ((chr1 > chr2) && (lng1 < lng2) || (chr1 < chr2) && (lng1 > lng2))
		PROBLEM(3);

	if (sizeof(long) == sizeof(short))
		PROBLEM(4);

	if (sizeof(char) != 1)
		PROBLEM(5);

	ush1 = (ushort)-1;
	ush2 = (ushort)-2;
	lng1 = CORRECT(ush1);
	lng2 = CORRECT(ush2);
	if (!(lng1 > lng2))
		PROBLEM(6);

	lng1 = CORRECT(0L);
	lng2 = lng1 - 1;
	if (!((lng1 < 0) && (lng2 > 0)))
		PROBLEM(7);

	return problems;
}
#endif
//-----------------------------------------------------------------------------------------------

#define CDB_TCALLOC(ptr, elem)                          \
	{                                                   \
		(ptr) = (void *)calloc((elem), sizeof(*(ptr))); \
	}
#define CDB_TMALLOC(ptr, elem)                           \
	{                                                    \
		(ptr) = (void *)malloc((elem) * sizeof(*(ptr))); \
	}
#define CDB_TREALLOC(ptr, elem)                                  \
	{                                                            \
		(ptr) = (void *)realloc((ptr), (elem) * sizeof(*(ptr))); \
	}

#define GET_STRING_FIELD(P, FIELD) \
	(P ? (P->FIELD ? P->FIELD : "") : "")

#define AVL_FOR(tree, p) for ((p) = avl_first(tree); (p); (p) = avl_next(tree))
typedef struct
	{
	int index;
	char *name;
} COLOR_NAME;

typedef struct
	{
	char *name;
	char *value;
} CDB_PARAM;

typedef struct
	{
	char *name;
	int num_params;
	CDB_PARAM *param;
} CDB_EFFECT;

char *strsave(const char *str)
{
	char *neww;
	if (!str)
		return 0;
	neww = malloc(strlen(str) + 1);

	strcpy(neww, str);
	return neww;
}

/*---------------------------------------------------------------------------*/

char *strnsave(const char *str, int n)
{
	char *neww;

	neww = malloc(n + 1);

	strncpy(neww, str, n);
	neww[n] = '\0';
	return neww;
}

TREE *cdb_decode_all(char *names_in_plt_file, TCM_INFO tcm)
{
	TREE *tree;
	char *c1, *c2, *endcol, *endstr, *ct;
	char tmp[100];
	int i, p;
	CDB_TREE_ITEM *item;
	CDB_EFFECT *effect;

	tree = avl_tree_nodup_int(CDB_TREE_ITEM, index);

	item = NIL;
	endstr = strchr(names_in_plt_file, '\0');
	c1 = names_in_plt_file;
	c2 = NIL;
	for (i = 0; i < tcm.n_colors + tcm.n_pencils; i++) {
		CDB_TCALLOC(item, 1)
		item->index = i;
		//if (i < tcm.n_colors)
		//  item->index = TCM_COLOR_INDEX (tcm, i);
		//else
		//  item->index = TCM_PENCIL_INDEX (tcm, i - tcm.n_colors);

		endcol = strchr(c1, '|');
		if (!endcol)
			endcol = endstr;
		c2 = strchr(c1, '\t');
		if (!c2)
			c2 = endcol;
		if (*c2 == '\t') {
			if (c2 - c1 == 1 && *c1 == '*')
				item->group = strsave("");
			else
				item->group = strnsave(c1, c2 - c1);
			c1 = c2 + 1;
			c2 = strchr(c1, '\t');
			if (!c2)
				c2 = endcol;
		} else {
			item->group = strsave("");
		}
		while (*c1 == '<' && c1[1] == '!') {
			c1 += 2;
			ct = strstr(c1, "!>");
			if (!ct)
				goto error;
			if (*c1 == 'A') {
				c1++;
				item->accelerator = strnsave(c1, ct - c1);
			}
			/* else if (*c1 == 'to be defined')... */
			c1 = ct + 2;
		}
		if (c2 - c1 == 1 && *c1 == '*')
			item->name = strsave("");
		else
			item->name = strnsave(c1, c2 - c1);

		if (*c2 == '\t')
			c1 = c2 + 1;
		else
			c1 = c2;
		if (!*c1) {
		} else if (*c1 == '|')
			c1++;
		else {
			item->effects = avl_tree_nodup_str(CDB_EFFECT, name);
			for (;;) {
				ct = strstr(c1, " ! ");
				if (ct)
					NOT_MORE_THAN(ct, endcol)
				ct = strstr(c1, "#! ");
				if (ct)
					NOT_MORE_THAN(ct, endcol)
				c2 = strchr(c1, '#');
				if (!c2 || c2 >= endcol)
					break;
				CDB_TCALLOC(effect, 1)
				effect->name = strnsave(c1, c2 - c1);

				c1 = c2 + 1;
				c2 = strchr(c1, '#');
				if (!c2)
					goto error;
				strncpy(tmp, c1, c2 - c1);
				tmp[c2 - c1] = '\0';
				effect->num_params = atoi(tmp);
				if (effect->num_params)
					CDB_TCALLOC(effect->param, effect->num_params)
				c1 = c2 + 1;
				for (p = 0; p < effect->num_params; p++) {
					c2 = strchr(c1, '#');
					if (!c2)
						goto error;
					effect->param[p].name = strnsave(c1, c2 - c1);
					c1 = c2 + 1;
					c2 = (p == effect->num_params - 1) ? endcol : strstr(c1, " : ");
					if (!c2)
						goto error;
					effect->param[p].value = strnsave(c1, c2 - c1);
					ct = effect->param[p].value + strlen(effect->param[p].value) - 1;
					while (ct >= effect->param[p].value && (*ct == ' ' || *ct == '\t'))
						*ct-- = '\0';
					c1 = c2 + 3;
				}
				avl_insert(item->effects, effect);
				item->num_effects++;
				if (*endcol != '|')
					c1 = endcol + 3;
				else {
					c1 = endcol + 1;
					break;
				}
			}
		}
		avl_insert(tree, item);
	}
	return tree;

error:

	printf("error parsing color names and fx\n");

	return tree;
}