/*********************************************************************/
/*  bibView: Administration of BibTeX-Databases                      */
/*           (Verwaltung von BibTeX-Literaturdatenbanken)            */
/*                                                                   */
/*  Module:  db_tree.c                                               */
/*                                                                   */
/*             - AVL-Baeume                                          */
/*             - String-Listen                                       */
/*             - Karten-Listen                                       */
/*                                                                   */
/*  Author:  Holger Martin,  martinh@informatik.tu-muenchen.de       */
/*           Peter M. Urban, urban@informatik.tu-muenchen.de         */
/*                                                                   */
/*  History:                                                         */
/*    22.11.91   HBM  created                                        */
/*    18.01.92   HBM  UserFld-Funktionen                             */
/*    05.26.92       Version 1.0 released                            */
/*                                                                   */
/*  Copyright 1992 TU MUENCHEN                                       */
/*    See ./Copyright for complete rights and liability information. */
/*                                                                   */
/*********************************************************************/


#include <stdio.h>
#include <X11/Intrinsic.h> 

#ifndef __hpux
#define _WCHAR_T
#endif
/*
 #include <string.h>
*/
#include "bibview.h"

#include <regex.h>

/*
 * Name    : avltree
 * Purpose : Header file for avltree.c
 * Author  : Lee Hollingworth
 */
/*
 * datatype: balances
 * purpose : mainatin a balance field in the current node
 */
typedef enum {EVEN, L_HEAVY, R_HEAVY} balances;


/*
 * datatype: node
 * purpose : tree node structure
 */
typedef struct node {
    CardData *data;                 /* search key */
    balances balance;               /* level of balance of children */
    struct node *left;          /* left sub-tree */
    struct node *right;         /* right sub-tree */
} AvlNode;

/*********************************************************************/
/*  Include / Define                                                 */
/*********************************************************************/

#define KEY_EQUAL    0
#define KEY_GREATER  1
#define KEY_LESS    -1

extern int max_fields;
extern char sort_field[];


/*****************************************
 * Array mit Pointern auf versch. Baeume *
 *****************************************/
AvlNode *TreeRoots[MAXTREEROOTS];



/*********************
 * Lokale Funktionen *
 *********************/
Errcode CopyCard(CardData **c, CardData *c2);
Errcode DupCard(CardData **c, CardData *c2, int type);
int     KeyCompare(char *s1, char *s2);
int     Compare(CardData *s1, CardData *s2, int type);
int     authorcmp(CardData *c1, CardData *c2);
int     titlecmp(CardData *c1, CardData *c2);
int     yearcmp(CardData *c1, CardData *c2);
int     mystrcmp(char *s1, char *s2);
char    *mystrcat(char *s1, char *s2);
int     dbtSearchAllUserFld(UserFld **list, regex_t *str);
int     dbtSearchUserFld(UserFld **list, UserFld **reglist);
Boolean Insert(int treeIdx, AvlNode **tree, CardData *card, int *h);
Boolean Search(AvlNode *tree, CardData **card);
Boolean Delete(int treeIdx, AvlNode **tree, CardData *card);
Errcode MakeListNode(ListNode **list);
Errcode ListAppend(ListNode **list, String word);
Errcode MakeCardListNode(CardListNode **list);
Errcode CardListAppend(CardListNode **list, CardData *card);
Errcode SearchList(AvlNode *baum, CardData *reg, CardListNode **list,
		   int sortedby); 
Errcode BuildList(AvlNode *baum, CardListNode **list, int sortedby);
Errcode RegCompCard(CardData *card, CardData *reg);
Errcode MakeRegCard(CardData *card, CardData *reg);
Errcode DeleteTree(AvlNode *baum);

static Errcode checkCards (AvlNode *tree, CardListNode **list);


/*
 * Name:    rotate_left
 * Purpose: To re-arrange node pointers resulting in a LL rotation
 * Passed:  tree:   address of pointer the rotation root
 * Returns: tree:   pointer to new root node
 */
void rotate_left(int treeIdx, AvlNode **tree)
{  
    Boolean isroot = FALSE;
    AvlNode *left = (*tree)->left;         /* left hand child */

    
    if (*tree == TreeRoots[treeIdx])
       isroot = TRUE;
    (*tree)->left = left->right;        /* re-arrange pointers */
    left->right = *tree;
    *tree = left;                   /* change root to top node of rotation */
    if (isroot)
       TreeRoots[treeIdx] = *tree;
}

/*
 * Name:    rotate_right
 * Purpose: To re-arrange node pointers resulting in a RR rotation
 * Passed:  tree:   address of pointer to the rotation node
 * Returns: tree:   pointer to new root node
 */
void rotate_right(int treeIdx, AvlNode **tree)
{
    Boolean isroot = FALSE;
    AvlNode *right = (*tree)->right;       /* right hand child */

    if (*tree == TreeRoots[treeIdx])
       isroot = TRUE;
    (*tree)->right = right->left;       /* re-arrange pointers */
    right->left = *tree;
    *tree = right;                  /* change root to top node of rotation */
    if (isroot)
       TreeRoots[treeIdx] = *tree;
}

/*
 * Name:    rot_left_balance
 * Purpose: Balance the root node and it's right child after a LL rotation
 * Passed:  tree:   pointer to root node of rotation after the rotation has
 *                   taken place
 */
void rot_left_balance(AvlNode *tree)
{
    tree->balance = EVEN;
    if ((tree->right->left && tree->right->right) ||
            (!tree->right->left && !tree->right->right)) {
        tree->right->balance = EVEN;
    }
    else {
        tree->right->balance = tree->right->right ? R_HEAVY : L_HEAVY;
    }
}

/*
 * Name:    rot_right_balance
 * Purpose: Balance the root node and it's left child after a RR rotation
 * Passed:  tree:   pointer to root node of rotation after the rotation has
 *                   taken place
 */
void rot_right_balance(AvlNode *tree)
{
    tree->balance = EVEN;
    if ((tree->left->left && tree->left->right) ||
            (!tree->left->left && !tree->left->right)) {
        tree->left->balance = EVEN;
    }
    else {
        tree->left->balance = tree->left->right ? R_HEAVY : L_HEAVY;
    }
}

/*
 * Name:    left_balance
 * Purpose: Take care of left rotations, either LL or LR after a node has
 *           been inserted
 * Passed:  tree:   pointer to pointer to root of rotation point which has
 *                   violated the tree balance prior to any rotation taking
 *                   place
 *          key:    the new insertion value
 * Returns: tree:   new root node
 */
void left_balance(int treeIdx, AvlNode **tree, CardData *card)
{
    AvlNode **child = &(*tree)->left;

    if (KeyCompare((*tree)->left->data->mainkey, card->mainkey) > 0) {
        /* LL rotation */
        rotate_left(treeIdx, tree);
        rot_left_balance(*tree);
    }
    else {                                  /* LR rotation */
        rotate_right(treeIdx, child);
        rot_right_balance(*child);
        rotate_left(treeIdx, tree);
        rot_left_balance(*tree);
    }
}

/*
 * Name:    right_balance
 * Purpose: Take care of right rotations, either RR or RL after a node has
 *           been inserted
 * Passed:  tree:   pointer to pointer to root of rotation point which has
 *                   violated the tree balance prior to any rotation taking
 *                   place
 *          key:    the new insertion value
 * Returns: tree:   new root node
 */
void right_balance(int treeIdx, AvlNode **tree, CardData *card)
{
    AvlNode **child = &(*tree)->right;

    if (KeyCompare((*tree)->right->data->mainkey, card->mainkey) < 0) {   
        /* RR rotation */
        rotate_right(treeIdx, tree);
        rot_right_balance(*tree);
    }
    else {                                  /* RL rotation */
        rotate_left(treeIdx, child);
        rot_left_balance(*child);
        rotate_right(treeIdx, tree);
        rot_right_balance(*tree);
    }
}

/*
 * Name:    Insert
 * Purpose: To search for and insert if necessary a key in a
 *           binary search tree.
 * Passed:  new_key: the key to be inserted
 *          tree:    the tree
 * Returns: tree:    possibly modified tree containing new_key
 *          TRUE     a new node was added so check the balance
 *          FALSE    balance is okay or node already in tree
 *           (maybe should return something else for duplicate key???)
 */
Boolean Insert(int treeIdx, AvlNode **tree, CardData *card, int *h)
{
    int result;
    
    *h = DBT_OK;
    if ((*tree == NULL) || (*tree == (AvlNode *) 1)) {
        /*
         * at leaf, so key was not in the tree and must be added
         */
        *tree = (AvlNode *)XtMalloc(sizeof(**tree));
        (*tree)->balance = EVEN;
        (*tree)->left = (*tree)->right = NULL;
        (*tree)->data = NULL;

        CopyCard(&((*tree)->data), card);
        if (TreeRoots[treeIdx]==NULL || TreeRoots[treeIdx]==(AvlNode *)1) {
           TreeRoots[treeIdx] = *tree;
           }
        return TRUE;
    }
    else if ((result = KeyCompare(card->mainkey, (*tree)->data->mainkey)) < 0) {
        if (Insert(treeIdx, &((*tree)->left), card, h)) {
            switch ((*tree)->balance) {
                case EVEN:
                    (*tree)->balance = L_HEAVY;
                    return TRUE;

                case R_HEAVY:
                    (*tree)->balance = EVEN;
                    return FALSE;                   /* tree is balanced */

                case L_HEAVY:
                    left_balance(treeIdx, tree, card);
                    return FALSE;                   /* tree is balanced */
            }

        }
        return FALSE;
    }
    else if (result > 0) {
        if (Insert(treeIdx, &((*tree)->right), card, h)) {
            switch ((*tree)->balance) {
                case EVEN:
                    (*tree)->balance = R_HEAVY;
                    return TRUE;

                case L_HEAVY:
                    (*tree)->balance = EVEN;
                    return FALSE;                   /* tree is balanced */

                case R_HEAVY:
                    right_balance(treeIdx, tree, card);
                    return FALSE;                   /* tree is balanced */
            }
        }
        return FALSE;
    }
    else {
        /*
         * key already in tree so return FALSE
         */
        *h = DBT_EDUPKEY;
        return FALSE;
    }
}

/*
 * Name:    del_ll
 * Purpose: Balance the tree after a deletion has taken place and a LL
 *           rotation is required
 * Passed:  tree:   pointer to pointer to root rotation node
 * Returns: tree:   pointer to pointer to new root node after rotation
 */
void del_ll(int treeIdx, AvlNode **tree)
{
    if ((*tree)->balance == L_HEAVY && (*tree)->left->balance == EVEN) {
        (*tree)->left->balance = R_HEAVY;
    }
    else {
        (*tree)->balance = EVEN;
        (*tree)->left->balance = R_HEAVY;
    }

    rotate_left(treeIdx, tree);
}

/*
 * Name:    del_rr
 * Purpose: Balance the tree after a deletion has taken place and a RR
 *           rotation is required
 * Passed:  tree:   pointer to pointer to root rotation node
 * Returns: tree:   pointer to pointer to new root node after rotation
 */
void del_rr(int treeIdx, AvlNode **tree)
{
    if ((*tree)->balance == R_HEAVY && (*tree)->right->balance == EVEN) {
        (*tree)->right->balance = L_HEAVY;
    }
    else {
        (*tree)->balance = EVEN;
        (*tree)->right->balance = L_HEAVY;
    }
    rotate_right(treeIdx, tree);
}

/*
 * Name:    del_lr
 * Purpose: Balance the tree after a deletion has taken place and a LR
 *           rotation is required
 * Passed:  tree:   pointer to pointer to root rotation node
 * Returns: tree:   pointer to pointer to new root node after rotation
 */
void del_lr(int treeIdx, AvlNode **tree)
{
    AvlNode **child = &(*tree)->left;

    /*
     * (*tree)->balance == L_HEAVY in all cases
     * (*child)->balance == R_HEAVY in all cases
     */

    switch ((*child)->right->balance) {
        case R_HEAVY:
            (*child)->balance = L_HEAVY;
            (*child)->right->balance = EVEN;        /* new root */
            (*tree)->balance = EVEN;
            break;

        case EVEN:
            (*child)->balance = EVEN;
            (*tree)->balance = EVEN;
            break;

        case L_HEAVY:
            (*child)->balance = EVEN;
            (*tree)->balance = R_HEAVY;
            (*child)->right->balance = EVEN;
            break;
    }

    rotate_right(treeIdx, child);
    rotate_left(treeIdx, tree);
}

/*
 * Name:    del_rl
 * Purpose: Balance the tree after a deletion has taken place and a RL
 *           rotation is required
 * Passed:  tree:   pointer to pointer to root rotation node
 * Returns: tree:   pointer to pointer to new root node after rotation
 */
void del_rl(int treeIdx, AvlNode **tree)
{
    AvlNode **child = &(*tree)->right;

    /*
     * (*tree)->balance == R_HEAVY in all cases
     * (*child)->balance == L_HEAVY in all cases
     */

    switch ((*child)->left->balance) {
        case L_HEAVY:
            (*child)->balance = R_HEAVY;
            (*child)->left->balance = EVEN;         /* new root */
            (*tree)->balance = EVEN;
            break;

        case EVEN:
            (*child)->balance = EVEN;
            (*tree)->balance = EVEN;
            break;

        case R_HEAVY:
            (*child)->balance = EVEN;
            (*tree)->balance = L_HEAVY;
            (*child)->left->balance = EVEN;
            break;
    }

    rotate_left(treeIdx, child);
    rotate_right(treeIdx, tree);
}

/*
 * Name:    left_path_balance
 * Purpose: If returning up the left path, check the current nodes balance
 *           and adjust if required
 * Passed:  pointer to pointer to the root node
 * Returns: FALSE no further balancing required above this point
 *          TRUE continue to balance up the tree
 */
Boolean left_path_balance(int treeIdx, AvlNode **tree)
{
    switch ((*tree)->balance) {
        case EVEN:          /* no further balancing req'd */
            (*tree)->balance = R_HEAVY;
            return FALSE;

        case L_HEAVY:       /* check further up... */
            (*tree)->balance = EVEN;
            break;

        case R_HEAVY:       /* tree is unbalanced rotations req'd */
            /*
             * three possible cases now exist
             * 1. right child is EVEN
             * 2. right child is R_HEAVY
             * 3. right child is L_HEAVY
             * 1 = RR and change balances according to current
             *      balances, tree is now balanced
             * 2 = set both balances to EVEN and RR
             * 3 = RL and change balances according to current
             *      balances
             */
            switch ((*tree)->right->balance) {
                case EVEN:
                    del_rr(treeIdx, tree);
                    return FALSE;

                case R_HEAVY:
                    (*tree)->balance = EVEN;
                    (*tree)->right->balance = EVEN;
                    rotate_right(treeIdx, tree);
                    break;

                case L_HEAVY:
                    del_rl(treeIdx, tree);
                    break;
            }
    }
    return TRUE;
}

/*
 * Name:    right_path_balance
 * Purpose: If returning up the right path, check the current nodes balance
 *           and adjust if required
 * Passed:  pointer to pointer to the root node
 * Returns: FALSE no further balancing required above this point
 *          TRUE continue to balance up the tree
 */
Boolean right_path_balance(int treeIdx, AvlNode **tree)
{
    switch ((*tree)->balance) {
        case EVEN:      /* no further balancing req'd */
            (*tree)->balance = L_HEAVY;
            return FALSE;

        case R_HEAVY:   /* continue to check on the way up */
            (*tree)->balance = EVEN;
            break;

        case L_HEAVY:
            /*
             * three possible cases now exist
             * 1. left child is EVEN
             * 2. left child is L_HEAVY
             * 3. left child is R_HEAVY
             * 1 = LL and change balances according to current
             *      balances, tree is now balanced
             * 2 = set both balances to EVEN and LL
             * 3 = LR and change balances according to current
             *      balances
             */
            switch ((*tree)->left->balance) {
                case EVEN:
                    del_ll(treeIdx, tree);
                    return FALSE;

                case L_HEAVY:
                    (*tree)->balance = EVEN;
                    (*tree)->left->balance = EVEN;
                    rotate_left(treeIdx, tree);
                    break;

                case R_HEAVY:
                    del_lr(treeIdx, tree);
                    break;
            }
    }
    return TRUE;
}

/*
 * Name:    left_most
 * Purpose: To retrieve the left most descendent of the tree,
 *            and delete this leaf node.
 * Passed:  tree: the tree to be searched
 * Returns: card:    the left most card in this tree
 *          tree:    the tree minus the leftmost node
 *          TRUE until point of balance is reached
 *          FALSE if no deletion or balance acheived
 */
Boolean left_most(int treeIdx, AvlNode **tree, CardData **card)
{
    AvlNode *p;               /* node to be disposed of */

    /*
     * first make sure there is something there to find
     */
    if ((*tree) != NULL) {
        /*
         * see if we are already at the bottom of the tree
         */
        if ((*tree)->left == NULL) {
            /*
             * return this left most node, and delete it, tree path is now
             * shorter so return TRUE
             */
            dbtDeleteCard(card);
            *card = (*tree)->data;
            p = (*tree);
            (*tree) = (*tree)->right;
            XtFree((char *)p);
            return TRUE;
        }
        else {
            /*
             * recurse down the left sub-tree, return of TRUE means a node
             * was removed from the left path so check balance of tree on
             * way up and adjust accordingly
             */
            if (left_most(treeIdx, &((*tree)->left), card)) {
                return left_path_balance(treeIdx, tree);
            }
            return FALSE;
        }
    }
    return FALSE;
}

/*
 * Name:    del_item
 * Purpose: To delete the item in the root of the tree.
 * Passed:  tree:    the tree whose root must be deleted
 * Returns: tree:    the tree with its root deleted
 *          TRUE until point of balance is reached
 *          FALSE if no deletion or balance acheived
 */
Boolean del_item(int treeIdx, AvlNode **tree)
{
    AvlNode *p;                /* node to be disposed of */
    Boolean isroot = FALSE;
    
    /*
     * first check that there is something to de deleted
     */
    if ((*tree) != NULL) {
        if (*tree == TreeRoots[treeIdx])
           isroot = TRUE;
        if (((*tree)->left == NULL) && ((*tree)->right == NULL)) {
            /*
             * if tree is a leaf, then it can be deleted easily, path
             *  has been shortened so return TRUE
             */
            dbtDeleteCard(&((*tree)->data));
            XtFree((char *)(*tree));
            (*tree) = NULL;
            if (isroot)
               TreeRoots[treeIdx] = NULL;
            return TRUE;
        }
        else if ((*tree)->left == NULL) {
            /*
             * if the tree only has a right child, then the root
             *  can be replaced by the right child
             * path has been shortened so return TRUE
             */
            p = (*tree);
            (*tree) = (*tree)->right;
            if (isroot)
               TreeRoots[treeIdx] = *tree;
            dbtDeleteCard(&(p->data));
            XtFree((char *)p);
            return TRUE;
        }
        else if ((*tree)->right == NULL) {
            /*
             * if the tree only has a left child, then the root
             *  can be replaced by the left child
             * path has been shortened so return TRUE
             */
            p = (*tree);
            (*tree) = (*tree)->left;
            if (isroot)
               TreeRoots[treeIdx] = *tree;
            dbtDeleteCard(&(p->data));
            XtFree((char *)p);
            return TRUE;
        }
        else {
            /*
             * if the tree has two children, then recurse to find
             *  the smallest key larger than the root (i.e. the
             *  left most key to the right of the root)
             * the path from where the replacement node has been
             *  taken is shortened, so the tree must be re-balanced
             *  accordingly
             */
            if (left_most(treeIdx, &((*tree)->right), &(*tree)->data)) {
                return right_path_balance(treeIdx, tree);
            }
            return FALSE;
        }
    }
    return FALSE;
}

/*
 * Name:    Delete
 * Purpose: To delete an item from the tree.
 * Passed:  tree:    the tree to be deleted from
 *          key:     the key value of the item to be deleted
 * Returns: tree:    the tree with the item deleted
 *          TRUE  balancing required
 *          FALSE no further balancing required
 */
Boolean Delete(int treeIdx, AvlNode **tree, CardData *card)
{
    int result;

    if ((*tree) == NULL) {
        /*
         * key not in tree
         */
        return FALSE;
    }
    else if ((result = 
              KeyCompare(card->mainkey, (*tree)->data->mainkey)) == 0) {
        /*
         * key found
         */
        return del_item(treeIdx, tree);
    }
    else if (result < 0) {
        /*
         * key could be in left sub-tree
         */
        if (Delete(treeIdx, &((*tree)->left), card)) {
            return left_path_balance(treeIdx, tree);
        }
        return FALSE;
    }
    else {
        /*
         * key could be in right sub-tree
         */
        if (Delete(treeIdx, &((*tree)->right), card)) {
            return right_path_balance(treeIdx, tree);
        }
        return FALSE;
    }
}

/*
 * Name:    Search
 * Purpose: To search for a key in the binary search tree.
 * Passed:  key: the key to be found
 *          tree:    the tree
 * Returns: TRUE     the node was found
 *          FALSE    the node was not found
 */
Boolean Search(AvlNode *tree, CardData **card)
{
    int result;

    if (tree == NULL) {
        /*
         * at leaf, so key was not in the tree
         */
        return FALSE;
    }
    else if 
    ((result = KeyCompare((*card)->mainkey, tree->data->mainkey)) < 0) {
        return (Search(tree->left, card));
    }
    else if (result > 0) {
        return (Search(tree->right, card));
    }
    else {
        /*
         * key found
         */
        CopyCard(card, tree->data);
        return TRUE;
    }
}


/*********************************************************************/
/*  Errcode dbtGetFreeTreeIdx(int *treeIdx):                         */
/*  liefert naechsten freien Listenindex zurueck und markiert diesen */
/*  mit 1 als besetzt.                                               */
/*********************************************************************/

Errcode dbtGetFreeTreeIdx(int *treeIdx)
{
 int h;

 h = 0;
 while (TreeRoots[h] && h < MAXTREEROOTS) h++;
 if (h == MAXTREEROOTS) return DBT_ROOTSFULL;
 *treeIdx = h;
 TreeRoots[h] = (AvlNode *)1;   /* Markierung */
 return DBT_OK;
}


/**************************************************************************/
/* Errcode dbtMakeUserFld(UserFld **list):                                */
/* neuen Knoten fuer Benutzerfeld-Liste erstellen.                        */
/**************************************************************************/

Errcode dbtMakeUserFld(UserFld **list)
{
  UserFld *hlist;

  hlist = *list;
  if ( hlist == NULL) {
    hlist = (UserFld *)XtCalloc(1,sizeof(UserFld));
    if (hlist == NULL) return DBT_ECALLOC; 
    hlist->next = NULL;
    *list = hlist;
    return DBT_OK; 
  }
  return DBT_OK; 
}



/**************************************************************************/
/* Errcode dbtDeleteAllUserFld(UserFld **list):                           */
/* Benutzerfeld-Liste loeschen.                                           */
/**************************************************************************/

Errcode dbtDeleteAllUserFld(UserFld **list)
{
 UserFld *h;

 h = *list;
 if (h) {
   if (h->fldName != NULL) XtFree((char *)h->fldName);
   if (h->fldData != NULL) XtFree((char *)h->fldData);
   if (h->next != NULL) dbtDeleteAllUserFld(&h->next);
   XtFree((char *)h);
   *list = NULL; 
   return DBT_OK;
 }
 return DBT_OK; 
}


/**************************************************************************/
/* Errcode dbtDeleteRegUserFld(UserFld **list):                           */
/* Benutzerfeld-Liste loeschen.                                           */
/**************************************************************************/

Errcode dbtDeleteRegUserFld(UserFld **list)
{
 UserFld *h;

 h = *list;
 if (h) {
   if (h->fldName != NULL) XtFree((char *)h->fldName);
   if (h->fldData != NULL) regfree((regex_t *)h->fldData);
   if (h->next != NULL) dbtDeleteRegUserFld(&h->next);
   XtFree((char *)h);
   *list = NULL; 
   return DBT_OK;
 }
 return DBT_OK; 
}


/**************************************************************************/
/*                                                                        */
/* int   dbtSearchUserFld(UserFld **list, UserFld **reglist)              */
/*                                                                        */
/**************************************************************************/
int   dbtSearchUserFld(UserFld **list, UserFld **reglist)
{
 UserFld *h, *hreg;
 size_t nmatch = 0;
 int eflags = 0;
 int gef;

 hreg = *reglist;
 gef = 0;
 while (hreg) {
  gef = 0;
  h = *list;
  while (h) {
   if (strcmp(hreg->fldName, h->fldName)==0){
     if (regexec((regex_t *)hreg->fldData, h->fldData,
         nmatch, NULL, eflags) != 0) 
        return 0;
     else 
	gef = 1;
     }
   h = h->next;
   }
  if (!gef)
    return 0;
  hreg = hreg->next;
  }
 return 1;
 }



/**************************************************************************/
/* int dbtSearchAllUserFld(UserFld **list, regex_t *str):                 */
/* Search for string in user field list.                                  */
/**************************************************************************/

int dbtSearchAllUserFld(UserFld **list, regex_t *str)
{
 UserFld *h;
 size_t nmatch = 0;
 int eflags = 0;

 h = *list;
 while (h) {
   if (regexec(str, h->fldData,
       nmatch, NULL, eflags) == 0) 
     return 1;
   h = h->next;
 }
 return 0; 
}




/**************************************************************************/
/* Errcode dbtAppendUserFld(UserFld **list, UserFld *obj):                */
/* obj an list anhaengen (kopieren).                                      */
/**************************************************************************/

Errcode dbtAppendUserFld(UserFld **list, UserFld *obj)
{
  UserFld *hlist; 
  Errcode erg;

  hlist = *list;
  if (hlist == NULL) {
    erg = dbtMakeUserFld(&hlist);
    Scalloc(&hlist->fldName, obj->fldName); 
    Scalloc(&hlist->fldData, obj->fldData); 
    hlist->fldLabel =  obj->fldLabel;
    hlist->fldText = obj->fldText;
    *list = hlist; 
    return erg;
  }
  else return(dbtAppendUserFld(&hlist->next, obj));
}


/**************************************************************************/
/* Errcode dbtNewUserFld(UserFld **list, UserFld *obj):                   */
/* obj an list anhaengen (kopieren).                                      */
/**************************************************************************/

Errcode dbtNewUserFld(UserFld **list, String fldName, String fldData)
{
  UserFld *hlist; 
  Errcode erg;

  hlist = *list;
  if ((fldData==NULL) || (*fldData == '\0'))
    return DBT_OK;
  if (hlist == NULL) {
    erg = dbtMakeUserFld(&hlist);
    Scalloc(&hlist->fldName, fldName); 
    Scalloc(&hlist->fldData, fldData); 
    *list = hlist; 
    return erg;
  }
  else return(dbtNewUserFld(&hlist->next, fldName, fldData));
}


/**************************************************************************/
/* Errcode dbtDupUserFld(CardData *c1, UserFld *olist):                   */
/* olist nach nlist kopieren.                                             */
/**************************************************************************/

Errcode dbtDupUserFld(CardData *c1, UserFld *olist)
{
 UserFld *h, *hnode;
 Errcode erg;
 int inserted;
 FieldName i;

 erg = DBT_OK;
 
 h = olist;
 hnode = NULL;            /* wegen dbtAppendUserFld */
 while (h != NULL) {
   inserted = 0;
   for (i=0;i<max_fields;i++){
      if (isstandardfield(i, c1->cardtype)){
         if (!strcmp(h->fldName, glbFldToName(i))) {
            Scalloc(&c1->field[i], h->fldData); 
            inserted = 1;
	    }
      }
   }
   if (!inserted)
      erg = dbtAppendUserFld(&hnode, h);
   h = h->next;
 }
 c1->ufield = hnode;
 return(erg);
}



/**************************************************************************/
/* Errcode dbtMakeAndCopyUserFld(UserFld **nlist, UserFld *olist):        */
/* olist nach nlist kopieren.                                             */
/**************************************************************************/

Errcode dbtMakeAndCopyUserFld(UserFld **nlist, UserFld *olist)
{
 Errcode erg;
 UserFld *h, *hnode;

 erg = DBT_OK;
 h = olist;
 hnode = NULL;            /* wegen dbtAppendUserFld */
 while (h != NULL) {
   erg = dbtAppendUserFld(&hnode, h);
   h = h->next;
 }
 *nlist = hnode;
 return(erg);
}




/*********************************************************************/
/* Errcode dbtMakeCard(CardData **card):                             */
/* Anlegen einer neuen Karte                                         */
/*********************************************************************/

Errcode dbtMakeCard(CardData **card)
{
  CardData *h;
  int i;

  h = (CardData *)XtCalloc(1,sizeof(CardData));
  if (h == NULL) return DBT_ECALLOC;
  h->cardtypestr = NULL;
  h->mainkey = NULL;   
  h->authorsort = NULL;
  h->sorted = -1;
  for (i=0; i<MAX_FIELDS; i++) 
     h->field[i] = NULL;
  h->ufield = NULL;
  *card = h; 
  return DBT_OK;
} 


/*********************************************************************/
/* Errcode dbtDeleteCard(CardData **card):                           */
/* Speicherplatz fuer eine Karte freigeben                           */
/*********************************************************************/

Errcode dbtDeleteCard(CardData **card)
{
   CardData *h;
   FieldName i;
 
   h = *card;
   if (h) {
     if (h->mainkey != NULL)
	XtFree((char *) h->mainkey);
     if (h->authorsort != NULL)
	XtFree((char *) h->authorsort);
     if (h->cardtypestr != NULL)
	XtFree((char *) h->cardtypestr);
     for (i=0; i<MAX_FIELDS; i++) {
       if (h->field[i] != NULL)
          XtFree((char *)h->field[i]);
       }
     if (h->ufield != NULL) dbtDeleteAllUserFld(&h->ufield); 
     XtFree((char *)h); 
     *card = NULL;
     return DBT_OK;
   }
   return DBT_OK;
}



/*********************************************************************/
/* Errcode dbtDeleteRegCard(CardData **card):                        */
/* Speicherplatz fuer eine Karte mit reg. Ausdruecken freigeben      */
/*********************************************************************/

Errcode dbtDeleteRegCard(CardData **card)
{
   CardData *h;
   FieldName i;
  
   h = *card;
   if (h) {
     if (h->mainkey != NULL)
	regfree((regex_t *) h->mainkey);
     if (h->authorsort != NULL)
	regfree((regex_t *) h->authorsort);
     if (h->cardtypestr != NULL)
	regfree((regex_t *) h->cardtypestr);
     for (i=0;i<max_fields;i++) {
       if (h->field[i] != NULL)
          regfree((regex_t *) h->field[i]);
       }
     if (h->ufield != NULL) dbtDeleteRegUserFld(&h->ufield); 
     XtFree((char *)h); 
     *card = NULL;
     return DBT_OK;
   }
   return DBT_OK;
}



/*********************************************************************/
/* Errcode dbtDeleteTree(int treeIdx):                               */
/* Speicherplatz von einem Baum freigeben.                           */
/*********************************************************************/
Errcode dbtDeleteTree(int treeIdx) 
{
 Errcode erg;

 if (TreeRoots[treeIdx] == (AvlNode *)1) {
   TreeRoots[treeIdx] = 0;
   return DBT_OK;
 }
 erg = DeleteTree(TreeRoots[treeIdx]);
 if (erg == DBT_OK) TreeRoots[treeIdx] = 0;
 return erg;
}
                              

/*********************************************************************/
/* Errcode DeleteTree(AvlNode *baum):                                */
/* Speicherplatz von einem Baum freigeben.                           */
/*********************************************************************/
Errcode DeleteTree(AvlNode *baum)
{
  Errcode erg, ende;
  CardData *tree;

  if (baum) {  

    tree = baum->data;
    if (baum->left) {
       if ((erg = DeleteTree(baum->left)) != DBT_OK) return erg; 
    }
    ende = dbtDeleteCard(&tree); 
    if (ende != DBT_OK) { 
       return ende;
    } 
    if (baum->right) {
       if ((erg = DeleteTree(baum->right)) != DBT_OK) return erg; 
    }
    return DBT_OK;
  }
  return DBT_OK;
}


/*********************************************************************/
/* CHECK FUNCTIONS                                                   */
/*********************************************************************/

/*********************************************************************/
/* Errcode dbtTestCard(CardData *c):                                 */
/* prueft, ob die zwingenden Felder ausgefuellt sind.                */
/*********************************************************************/
Errcode 
dbtTestCard (CardData *c)
{
 int i;

 if (c == NULL) return DBT_ENOCARD;
 if (!glbIsStringEmpty(c->field[ncrossref])) 
    return DBT_OK;
 
 switch (c->cardtype) {
         case book:
               if (glbIsStringEmpty(c->field[nauthor]) &&
                   glbIsStringEmpty(c->field[neditor])) 
		   return DBT_EAUTHOR_EDITOR;
               if (glbIsStringEmpty(c->field[ntitle])) return (200+ntitle);
               if (glbIsStringEmpty(c->field[npublisher]))
		  return (200+npublisher);
               if (glbIsStringEmpty(c->field[nyear])) return (200+nyear);
               break;
         case inbook:
               if (glbIsStringEmpty(c->field[nauthor]) &&
                   glbIsStringEmpty(c->field[neditor])) 
		   return DBT_EAUTHOR_EDITOR;
               if (glbIsStringEmpty(c->field[ntitle])) return (200+ntitle);
               if (glbIsStringEmpty(c->field[nchapter]) &&
                   glbIsStringEmpty(c->field[npages])) 
		   return DBT_ECHAPTER_PAGES;
               if (glbIsStringEmpty(c->field[npublisher])) 
		   return (200+npublisher); 
               if (glbIsStringEmpty(c->field[nyear])) return (200+nyear);
               break;
         default:
	       for (i=0; i<max_fields; i++){
		  if (isrequiredfield(i, c->cardtype))
		     if (glbIsStringEmpty(c->field[i]))
			return (200+i);
               }
               break;
    }
    return DBT_OK;
}


/*********************************************************************/
/* checkCards:                                                       */
/*    Descends tree and appends illegal cards to list                */
/*********************************************************************/
static Errcode
checkCards (AvlNode *tree, CardListNode **list)
{
Errcode status;

   if ((tree !=NULL) && (tree != (AvlNode *)1)) {                 
      if (tree->left) {
         if ((status = checkCards(tree->left, list)) != DBT_OK)
	    return(status);
      }

      if (dbtTestCard(tree->data) != DBT_OK) { 
         if ((status = CardListAppend(list, tree->data)) != DBT_OK)
	    return(status);
      }

      if (tree->right) { 
         if ((status = checkCards(tree->right, list)) != DBT_OK)
	    return(status);
      }
   }
   return(DBT_OK);
}


/*********************************************************************/
/* dbtCheckAllCards:                                                 */
/*    Checks tree and returns list of illegal cards                  */
/*********************************************************************/
Errcode 
dbtCheckAllCards (int treeIdx, CardListNode **list)
{
   return(checkCards(TreeRoots[treeIdx], list));
}



/*********************************************************************/
/* AVL - TREE FUNCTIONS                                              */
/*********************************************************************/

/*********************************************************************/
/* Errcode DupCard(CardData **c, CardData *c2, int type):            */
/* Inhalt von c2 nach c kopieren                                     */
/*********************************************************************/
Errcode DupCard(CardData **c, CardData *c2, int type)
{
  CardData *c1;
  FieldName i;

  if (*c == NULL) dbtMakeCard(c);
  else {                  /* NEU: 16.02.92 */
    dbtDeleteCard(c); 
    dbtMakeCard(c);
  }
  c1 = *c;
  c1->cardtype = type;
  c1->sorted = c2->sorted;
  c1->cardtypestr = glbNewString(glbTypeToName(type));
  c1->mainkey = NULL;
  dbtDupUserFld(c1, c2->ufield);
  for (i=0; i<max_fields; i++) {
    if (isstandardfield(i, c1->cardtype))
       Scalloc(&c1->field[i], c2->field[i]);
    else 
       dbtNewUserFld(&c1->ufield, glbFldToName(i), c2->field[i]);
    }
  Scalloc(&c1->authorsort, c2->authorsort);
  return DBT_OK;
}
            
/*********************************************************************/
/* Errcode CopyCard(CardData **c, CardData *c2):                        */
/* Inhalt von c2 nach c kopieren                                     */
/*********************************************************************/
Errcode CopyCard(CardData **c, CardData *c2)
{
  CardData *c1;
  FieldName i;

  if (*c == NULL) dbtMakeCard(c);
  else {                  /* NEU: 16.02.92 */
    dbtDeleteCard(c); 
    dbtMakeCard(c);
  }
  c1 = *c;
  c1->cardtype = c2->cardtype;
  c1->sorted = c2->sorted;
  Scalloc(&c1->mainkey, c2->mainkey);
  Scalloc(&c1->authorsort, c2->authorsort);
  Scalloc(&c1->cardtypestr, c2->cardtypestr);
  dbtMakeAndCopyUserFld(&c1->ufield, c2->ufield);
  for (i=0; i<max_fields; i++) 
     Scalloc(&c1->field[i], c2->field[i]);
  return DBT_OK;
}

int istrenner(char c)
   {int erg;
    erg=(isspace(c) || (c=='.') || (c=='~'));
    return(erg);
    }

/*********************************************************************/
/*    char *mystrcat(char *str1, char *str2)                         */
/*    String s2 wird an String s1 angehaengt, wobei Leerzeichen      */
/*    und Klammern und '\' und '"' entfernt werden                   */
/*********************************************************************/

char *mystrcat(char *str1, char *str2)
   {char *hstr1=str1;
    while ((*hstr1)!='\0')
       hstr1++;
    while (*str2)
      {if ((!isspace(*str2)) && (*str2 != '{') && (*str2 != '}')
	    && (*str2 != '\"') && (*str2!='\\'))
	  *hstr1++ = *str2++;
       else str2++;
       }
    *hstr1 = '\0';
    return(str1);
    }

/*********************************************************************/
/* void get_name(CardData *card, int fieldnr)                        */
/* card->authorsort wird besetzt, und zwar im Format nnvv            */
/*********************************************************************/
void get_name(CardData  *card, int fieldnr)
   
   {
    char *semicolon = ";";
    char *comma = ",";
    char *andstr = "and";
    char *name[10];
    char *entry, *p_surname, *p_firstname, *hname;
    int  i,j,k;
    int inbracket = 0;
   
    if (card->field[fieldnr]==NULL)
       return;
    else {
       entry = XtCalloc(1, strlen(card->field[fieldnr])+1);
       strcpy(entry, card->field[fieldnr]);
       }
    card->authorsort = XtCalloc(1, strlen(entry)+1); 
    card->sorted = fieldnr;
    strcpy(card->authorsort, "");
    for (i=0; i<strlen(entry); i++){
      if (entry[i] == '{'){
	 inbracket++;
	 }
      else if (entry[i] == '}')
	 inbracket--;
      else if (entry[i] == ';')
         entry[i] = ' ';
      else if ((entry[i] == ',') && (inbracket>0))
         entry[i] = ' ';
      else if (!strncmp(&entry[i],andstr,3) && inbracket==0)
       if ((i>=1) && (i<(strlen(entry)-3)) && isspace(entry[i-1]) && 
	    isspace(entry[i+3])){
	entry[i++] = ' ';
	entry[i++] = ';';
	entry[i] = ' ';
	}
    }

    i = 0;
    inbracket=0;
    name[i] = strtok(entry, semicolon);
    while ((name[i] != NULL) && (i<10))
       {
        i++;
        name[i] = strtok(NULL, semicolon);
       }
    for (j=0; j<i; j++)
      {
       if (strchr (name[j], ',') == NULL)
          {
           /* there is NO comma at all */
	   hname = name[j];
           for (k=strlen(hname)-1; isspace(hname[k])&&(k>=0); k--);
           for (; k>=0; k--)
	     {if (hname[k] == '}') 
		inbracket++;
	      else if (hname[k] == '{') 
		inbracket--;
              else if (inbracket>0);
	      else if (istrenner(hname[k]) && (inbracket==0)) break;
	      }
           mystrcat (card->authorsort, &hname[k+1]);
	   if (k>0){
             hname[k] = '\0';
             mystrcat (card->authorsort, hname);
	     }
           }
       else
          {
           /* get firstname and surname in right order */
           p_surname = strtok (name[j], comma);
           p_firstname = strtok (NULL, comma);
           mystrcat (card->authorsort, p_surname);
           mystrcat (card->authorsort, p_firstname);
          }
       }
   XtFree(entry);
   }


/*********************************************************************/
/* int namecmp(CardData *c1, CardData *c2):                          */
/* Die Author-Strings der Karten c1 und c2 werden verglichen         */
/*********************************************************************/

int namecmp(CardData *c1, CardData *c2, int fieldnr)
{
 int erg;

 if (c1->sorted == fieldnr){
    if (c1->authorsort == NULL)
       get_name(c1, fieldnr);
    }
 else {
    XtFree((char *) c1->authorsort);
    c1->authorsort = NULL;
    get_name(c1, fieldnr);
    c1->sorted = fieldnr;
    }
 if (c2->sorted == fieldnr){
    if (c2->authorsort == NULL)
       get_name(c2, fieldnr);
    }
 else {
    XtFree((char *) c2->authorsort);
    c2->authorsort = NULL;
    get_name(c2, fieldnr);
    c2->sorted = fieldnr;
    }

 if (c1->authorsort==NULL) return -1;
 if (c2->authorsort==NULL) return 1;

 erg = strcmp(c1->authorsort, c2->authorsort);
 return erg;
}

/*********************************************************************/
/* int datecmp(CardData *c1, CardData *c2, int fieldnr):             */
/* The cards c1 and c2 are compared.                                 */
/* Format of a date is dd.mm.yyyy                                    */
/*********************************************************************/

int datecmp(CardData *c1, CardData *c2, int fieldnr)
{
 int erg;
 
 char *hs1, *hs2;

 hs1 = c1->field[fieldnr];
 if (hs1==NULL) return -1;
 hs2 = c2->field[fieldnr];
 if (hs2==NULL) return 1;
 while (isspace(*hs1)) hs1++;
 while (isspace(*hs2)) hs2++;
 if (strlen(hs1) < 10) return -1;
 if (strlen(hs2) < 10) return 1;
 erg = strcmp(hs1+6, hs2+6);
 if (erg!=0) return erg;
 erg = strncmp(hs1+3, hs2+3, 2);
 if (erg!=0) return erg;
 erg = strncmp(hs1, hs2, 2);
 return erg;
}


/*********************************************************************/
/* int titlecmp(CardData *c1, CardData *c2):                         */
/* Die Title-Strings der Karten c1 und c2 werden verglichen          */
/*********************************************************************/

int titlecmp(CardData *c1, CardData *c2)
{
 int erg;
 
 if (c1->field[ntitle] == NULL) return -1;
 if (c2->field[ntitle] == NULL) return 1;
 erg = strcmp(c1->field[ntitle], c2->field[ntitle]);
 return erg;
}


/*********************************************************************/
/* int yearcmp(CardData c1, CardData c2):                            */
/* Die Year-Strings der Karten c1 und c2 werden verglichen           */
/*********************************************************************/

int yearcmp(CardData *c1, CardData *c2)
{
 int erg;

 char *hs1, *hs2;

 if (c1->field[nyear] == NULL) return -1;
 if (c2->field[nyear] == NULL) return 1;
 hs1 = c1->field[nyear];
 hs2 = c2->field[nyear];
 if (*hs1=='@') hs1++;
 if (*hs2=='@') hs2++;
 erg = strcmp(hs1, hs2);
 return erg;
}


/*********************************************************************/
/* int mystrcmp(String s1, String s2):                               */
/* Die Strings s1 und s2 werden verglichen                           */
/*********************************************************************/

int mystrcmp(String s1, String s2)
{
 int erg;
 
 if (s1==NULL) return -1;
 if (s2==NULL) return 1;
 erg = strcmp(s1, s2);
 return erg;
}



/*********************************************************************/
/* int Compare(CardData *c1, CardData *c2, int type):                */
/* Die Karten c1 und c2 werden verglichen                            */
/*********************************************************************/

int Compare(CardData *c1, CardData *c2, int type)
{
 int erg;
 
 if (type == SORT_MAINKEY)
    erg = mystrcmp(c1->mainkey, c2->mainkey);
 else if (type == SORT_UNSRT)
    erg = 0;
 else if (type == SORT_TYPE)
    erg = mystrcmp(c1->cardtypestr, c2->cardtypestr);
 else if (type == nyear)
    erg = yearcmp(c1, c2);
 else if (type == ntitle)
    erg = titlecmp(c1, c2);
 else if (sort_field[type] == 'n')
    erg = namecmp(c1, c2, type);
 else if (sort_field[type] == 'd')
    erg = datecmp(c1, c2, type);
 else 
    erg = mystrcmp(c1->field[type], c2->field[type]);
 return erg;
}

/*********************************************************************/
/* int KeyCompare(char *s1, char *s2):                               */
/* Die Strings s1 und s2 werden verglichen                           */
/*********************************************************************/

int KeyCompare(char *s1, char *s2)
{
 int erg;

 erg = mystrcmp(s1, s2);
 if (erg > 0) return KEY_GREATER;
 if (erg < 0) return KEY_LESS;
 return KEY_EQUAL;
}


/*********************************************************************/
/* Errcode dbtInsert(int treeIdx, CardData *card):                   */
/* card wird in den AVL-Baum mit dem Index treeIdx eingefuegt        */
/*********************************************************************/

Errcode dbtInsert(BibPtr bp, CardData *card)
{
  int h;
  char *sp;
  int len;
  char app;

  if (glbIsStringEmpty(card->mainkey) == 1) {
    if (card->mainkey != NULL) XtFree(card->mainkey);
    if (glbIsStringEmpty(card->field[nkey]) == 0) {
       Scalloc(&card->mainkey, card->field[nkey]);  
       }
    else {
      card->mainkey = (String)XtCalloc(1,25);
      if (glbIsStringEmpty(card->field[ntitle]) == 1) {
        if (glbIsStringEmpty(card->field[nauthor]) == 0) {
          strncpy(card->mainkey, card->field[nauthor], 10);
        }
	else
          strcpy(card->mainkey, "UNKNOWN");
      }
      else {
            strncpy(card->mainkey, card->field[ntitle], 10);
      }
      strcat(card->mainkey, ":");
      if (glbIsStringEmpty(card->field[nyear]) == 0) 
	 strcat(card->mainkey, card->field[nyear]);
      strlower(card->mainkey);
      for (sp = card->mainkey; *sp!='\0'; sp++)
        if (*sp == ' ')
           *sp = '-';
    }
  }
  Insert(bp->treeIdx, &TreeRoots[bp->treeIdx], card, &h);
  if (h == DBT_EDUPKEY){
     card->mainkey = XtRealloc(card->mainkey, strlen(card->mainkey)+2);
     card->mainkey = strcat(card->mainkey, "a");
     len = strlen(card->mainkey);
     app = 'a';
     Insert(bp->treeIdx, &TreeRoots[bp->treeIdx], card, &h);
     while (h == DBT_EDUPKEY){
       if (app == 'z')
         app = 'A';
       else
         app = app+1;
       card->mainkey[len-1] = app;
       card->mainkey[len] = '\0';
       Insert(bp->treeIdx, &TreeRoots[bp->treeIdx], card, &h);
     }
  }
  return DBT_OK;
  
}


/*********************************************************************/
/* Errcode dbtDelete(int treeIdx, CardData *card):                   */
/* card wird aus dem AVL-Baum mit dem Index treeIdx geloescht        */
/*********************************************************************/

Errcode dbtDelete(int treeIdx, CardData *card)
{
  if (TreeRoots[treeIdx] == (AvlNode *)1) 
     return DBT_OK;    /* nur vorerst */
  Delete(treeIdx, &TreeRoots[treeIdx], card);
  return DBT_OK;   /* nur vorerst */
}



/*********************************************************************/
/* Errcode MakeRegCard(CardData *card, CardData *reg):               */
/* Die einzelnen reg. Ausdruecke in card werden compiliert und in    */
/* reg gespeichert                                                   */
/*********************************************************************/

Errcode MakeRegCard(CardData *card, CardData *reg)
{
 int status;
 int cflags;
 UserFld *helpufield = NULL;
 UserFld *ufzeiger = NULL;
 FieldName i;

if (cotIgnoreCase())
  cflags = REG_EXTENDED ^ REG_ICASE ^ REG_NOSUB;
else
  cflags = REG_EXTENDED ^ REG_NOSUB;

if (card->mainkey != NULL) {
   reg->mainkey = XtCalloc(1,sizeof(regex_t));
   status = regcomp((regex_t *) reg->mainkey, card->mainkey, cflags);
   if (status != 0) return DBT_ECOMP;
   }
if (card->cardtypestr != NULL) {
   reg->cardtypestr = XtCalloc(1,sizeof(regex_t));
   status = regcomp((regex_t *) reg->cardtypestr, card->cardtypestr, cflags);
   if (status != 0) return DBT_ECOMP;
   }
if (card->authorsort != NULL) {
   reg->authorsort = XtCalloc(1,sizeof(regex_t));
   status = regcomp((regex_t *) reg->authorsort, card->authorsort, cflags);
   if (status != 0) return DBT_ECOMP;
   }
for (i=0; i<max_fields; i++){
   if (card->field[i] != NULL) {
      reg->field[i]  = XtCalloc(1,sizeof(regex_t));
      status = regcomp((regex_t *)reg->field[i], card->field[i], cflags);
      if (status != 0) return DBT_ECOMP;
   }
}

if (card->ufield != NULL) {
    ufzeiger = card->ufield;
    dbtMakeUserFld(&helpufield);
    dbtAppendUserFld(&reg->ufield, helpufield);
    reg->ufield->fldName = glbNewString(ufzeiger->fldName);
    reg->ufield->fldData = (String)XtCalloc(1,sizeof(regex_t));
    status = regcomp((regex_t *)reg->ufield->fldData, 
	      ufzeiger->fldData, cflags);
    if (status != 0) return DBT_ECOMP;
    ufzeiger = ufzeiger->next;
    }

if (ufzeiger != NULL) {
    dbtMakeUserFld(&helpufield);
    dbtAppendUserFld(&reg->ufield, helpufield);
    reg->ufield->next->fldName = glbNewString(ufzeiger->fldName);
    reg->ufield->next->fldData = (String)XtCalloc(1,sizeof(regex_t));
    status = regcomp((regex_t *)reg->ufield->next->fldData, 
	      ufzeiger->fldData, cflags);
    if (status != 0) return DBT_ECOMP;
    }

return DBT_OK;
}

/*********************************************************************/
/* Errcode RegCompCard(CardData *card, CardData *reg):               */
/* Die einzelnen Strings aus card werden mit den jeweiligen          */
/* compilierten reg. Ausdruecken aus reg verglichen                  */
/*********************************************************************/
Errcode RegCompCard(CardData *card, CardData *reg)
{
 int flag, nbOfComp;
 size_t nmatch = 0;
 int eflags = 0;
 FieldName i;

 nbOfComp = 0;
 if (reg->authorsort) { 
   flag = 0;
   if (!flag && (card->mainkey)) {
     if (regexec((regex_t *)reg->authorsort, card->mainkey,
        nmatch, NULL, eflags) == 0) 
       flag = 1;
     nbOfComp++;
     }
   if (!flag && (card->cardtypestr)) {
     if (regexec((regex_t *)reg->authorsort, card->cardtypestr,
        nmatch, NULL, eflags) == 0) 
       flag = 1;
     nbOfComp++;
     }
   for (i=0; i<max_fields;i++) {
      if (!flag && (card->field[i])) {
        if (regexec((regex_t *)reg->authorsort, card->field[i],
           nmatch, NULL, eflags) == 0) 
          flag = 1;
        nbOfComp++;
        }
   }
   if (!flag)
     flag = dbtSearchAllUserFld(&card->ufield, (regex_t *)reg->authorsort);
   if (!flag)
     return DBT_ENOMATCH;
   }
 else
    flag = 1;

 if (flag  && (reg->mainkey)) { 
    if (!card->mainkey) return DBT_ENOMATCH;
    else {
     if (regexec((regex_t *)reg->mainkey, card->mainkey,
         nmatch, NULL, eflags) == 0) 
        flag = 1; 
     else flag = 0;
     nbOfComp++;
     }
 }
 if (flag  && (reg->cardtypestr)) { 
    if (!card->cardtypestr) return DBT_ENOMATCH;
    else {
     if (regexec((regex_t *)reg->cardtypestr, card->cardtypestr,
         nmatch, NULL, eflags) == 0) 
        flag = 1; 
     else flag = 0;
     nbOfComp++;
     }
 }
 for (i=0; i<max_fields; i++) { 
    if (flag  && reg->field[i]) { 
       if (!card->field[i]) return DBT_ENOMATCH;
       else {
        if (regexec((regex_t *)reg->field[i], card->field[i],
            nmatch, NULL, eflags) == 0) 
           flag = 1; 
        else flag = 0;
        nbOfComp++;
        }
    }
 } 
 if (flag && reg->ufield) {
    flag = dbtSearchUserFld(&card->ufield, &reg->ufield);
    nbOfComp++;
    }
 if (flag == 1 && nbOfComp > 0) return DBT_OK;
 return DBT_ENOMATCH;
}


/*********************************************************************/
/* Errcode dbtSearch(int treeIdx, CardData **card)                   */
/* eine Karte mit bestimmten Schluessel suchen                       */
/*********************************************************************/
Errcode dbtSearch(int treeIdx, CardData **card)
{
  Boolean erg;

  erg = Search(TreeRoots[treeIdx], card);
  if (erg) return DBT_OK;
  else     return DBT_ENONODE;
} 



/**************************************************************************/
/* Errcode dbtSearchList(int treeIdx, CardData *card,                     */
/*       CardListNode **list, int sortedby)                               */
/* die reg. Ausd. in card werden compiliert; jede Karte im Baum wird mit  */
/* dieser Karte verglichen und ggf. an list angefuegt.                    */ 
/**************************************************************************/
Errcode dbtSearchList(int treeIdx, CardData *card, CardListNode **list, 
		      int sortedby)
{
  CardData *reg;
  Errcode erg;

  if (TreeRoots[treeIdx] == (AvlNode *)1) {
    TreeRoots[treeIdx] = 0;
    return DBT_OK;
  }
  if ((erg = dbtMakeCard(&reg)) != DBT_OK) return erg;  
  if ((erg = MakeRegCard(card, reg)) != DBT_OK) return erg; 
  erg = SearchList(TreeRoots[treeIdx], reg, list, sortedby);
  dbtDeleteRegCard(&reg);
  return erg;
}


/**************************************************************************/
/* Errcode dbtBuildList(int treeIdx, CardListNode **list, int sortedby)   */
/* eine Liste mit allen Eintraegen des AVL-Baums wird aufgebaut           */
/**************************************************************************/
Errcode dbtBuildList(int treeIdx, CardListNode **list, int sortedby)
{
  Errcode erg;

  if (TreeRoots[treeIdx] == (AvlNode *)1) {
    TreeRoots[treeIdx] = 0;
    return DBT_OK;
  }
  erg = BuildList(TreeRoots[treeIdx], list, sortedby);
  return erg;
}




/**************************************************************************/
/* Errcode SearchList(AvlNode *baum, CardData *reg,                       */
/*       CardListNode **list, int sortedby)                               */
/* jede Karte im Baum wird mit reg verglichen und ggf. an list angefuegt. */
/**************************************************************************/
Errcode SearchList(AvlNode *baum, CardData *reg, CardListNode **list, 
		   int sortedby)
{
  CardData *tree;

  if (baum) {                 
    tree = baum->data;
    if (baum->left) {
       SearchList(baum->left, reg, list, sortedby); 
    }
    if (RegCompCard(tree, reg) == DBT_OK) { 
       dbtCardListInsert(list, tree, sortedby);  
    }
    if (baum->right) {
       SearchList(baum->right, reg, list, sortedby);
    }
  }
  return DBT_OK;
}



/**************************************************************************/
/* Errcode BuildList(AvlNode *baum, CardListNode **list, int sortedby)    */
/* jede Karte im Baum wird an list angefuegt.                             */
/**************************************************************************/
Errcode BuildList(AvlNode *baum, CardListNode **list, int sortedby)
{
  CardData *tree;

  if (baum) {                 
    tree = baum->data;
    if (baum->left) {
       BuildList(baum->left, list, sortedby); 
    }
    dbtCardListInsert(list, tree, sortedby);  
    if (baum->right) {
       BuildList(baum->right, list, sortedby);
    }
  }
  return DBT_OK;
}


/**************************************************************************/
/* Errcode MakeCardListNode(CardListNode **list):                         */
/* neue Wurzel fuer Karten-Liste erstellen.                               */
/**************************************************************************/

Errcode MakeCardListNode(CardListNode **list)
{
  CardListNode *hlist;

  hlist = *list;
  if ( hlist == NULL) {
    hlist = (CardListNode *)XtCalloc(1,sizeof(CardListNode));
    if (hlist == NULL) return DBT_ECALLOC; 
    hlist->next = NULL;
    *list = hlist;
    return DBT_OK; 
  }
  return DBT_OK; 
}


/**************************************************************************/
/* Errcode CardListAppend(CardListNode **list, CardData *card):           */
/* card an list anhaengen.                                                */
/**************************************************************************/

Errcode CardListAppend(CardListNode **list, CardData *card)
{
  CardListNode *hlist;
  Errcode erg;

  hlist = *list;
  if (hlist == NULL) {
    erg = MakeCardListNode(&hlist);
    CopyCard(&hlist->data, card);
    *list = hlist; 
    return erg;
  }
  else CardListAppend(&hlist->next, card);
  return DBT_OK;
}



/**************************************************************************/
/* Errcode dbtCardListSortIns(CardListNode **list, CardData *card,        */
/*           int sortedby):                                               */
/* card in list einfuegen.                                                */
/**************************************************************************/

Errcode dbtCardListSortIns(CardListNode **list, CardData *card, int sortedby)
{
  CardListNode *hlist, *vorg, *newlist;
  Errcode erg;

  hlist = *list;
  newlist = NULL;
  if (hlist == NULL) {
    erg = MakeCardListNode(&hlist);
    hlist->data = card;
    *list = hlist; 
    return erg;
  }
  else {
    vorg = NULL;
    while  (hlist != NULL)
       if (Compare(hlist->data, card, sortedby)<=0){
          vorg = hlist;
          hlist = hlist->next;
	  }
       else
	  break;
    erg = MakeCardListNode(&newlist);
    newlist->data = card;
    newlist->next = hlist;
    if (vorg == NULL)
      *list = newlist; 
    else
      vorg->next = newlist;
    return erg;
    }
}



/**************************************************************************/
/* Errcode dbtCardListInsert(CardListNode **list, CardData *card,         */
/*           int sortedby):                                               */
/* card in list einfuegen. card wird kopiert!                             */
/**************************************************************************/

Errcode dbtCardListInsert(CardListNode **list, CardData *card, int sortedby)
{
  CardListNode *hlist, *vorg, *newlist;
  Errcode erg;

  hlist = *list;
  newlist = NULL;
  if (hlist == NULL) {
    erg = MakeCardListNode(&hlist);
    CopyCard(&hlist->data, card);
    *list = hlist; 
    return erg;
  }
  else {
    vorg = NULL;
    while  (hlist != NULL)
       if (Compare(hlist->data, card, sortedby)<=0){
          vorg = hlist;
          hlist = hlist->next;
	  }
       else
	  break;
    erg = MakeCardListNode(&newlist);
    CopyCard(&newlist->data, card);
    newlist->next = hlist;
    if (vorg == NULL)
      *list = newlist; 
    else
      vorg->next = newlist;
    return erg;
    } 
} 


/**************************************************************************/
/* Errcode dbtCardListDelete(CardListNode **list):                        */
/* list wird geloescht.                                                   */
/**************************************************************************/

Errcode dbtCardListDelete(CardListNode **list) 
{
 CardListNode *delList, *elem;
 
 delList = *list;
 while (delList != NULL) {
   dbtDeleteCard(&delList->data);
   elem = delList;
   delList = delList->next;
   XtFree((char *)elem);
   *list = NULL;
 }
 return DBT_OK;
}                       


/*********************************************************************/
/* LINKED LIST FUNCTIONS                                             */
/*********************************************************************/

/*********************************************************************/
/* Errcode MakeListNode(ListNode **list):                            */
/* neue Wurzel fuer String-Liste erstellen.                          */
/*********************************************************************/
Errcode 
dbtMakeListNode (ListNode **list)
{
  ListNode *hlist;

  hlist = *list;
  if ( hlist == NULL) {
    hlist = (ListNode *)XtCalloc(1,sizeof(ListNode));
    if (hlist == NULL) return DBT_ECALLOC; 
    hlist->next = NULL;
    *list = hlist;
    return DBT_OK; 
  }
  return DBT_OK; 
}


/*********************************************************************/
/* Errcode ListAppend(ListNode **list, String word):                 */
/* word an list anhaengen.                                           */
/*********************************************************************/
Errcode 
dbtListAppend (ListNode **list, String word)
{
  ListNode *hlist;
  Errcode erg;

  hlist = *list;
  if (hlist == NULL) {
    erg = dbtMakeListNode(&hlist);
    hlist->data = (String)XtCalloc(1,strlen(word)+1);
    strcpy(hlist->data, word);
    *list = hlist; 
    return erg;
  }
  else if (strcmp(hlist->data, word)==0)
    return DBT_OK; 
  else return(dbtListAppend(&hlist->next, word));
}
