/* 
   Tzong-Yow hwu
   need changes to make:
   1. keep the number of deleted nodes of the NameTree for reconstruction
      the NameTree
*/
#include "Scian.h"
#include "ScianTypes.h"
#include "ScianIDs.h"
#include "ScianLists.h"
#include "ScianSymbols.h"
#include "ScianGarbageMan.h"
#include "ScianDatabase.h"

#define DB_MAXKEYS  30 /* maximum number of keys allowed in the search key list */

struct LListNode 
{
  struct LListNode *next;
  ObjPtr object;
};
struct NameTreeNode
{
  struct NameTreeNode *left;
  struct NameTreeNode *right;
  struct LListNode *list;
  char *name;   
  int linkLength;
 /* the maximum number of link length of its left and right child */
  Bool deleteFlag;
};
typedef struct LListNode llistNode;
typedef struct NameTreeNode nameTreeNode;
typedef enum {FAILED = 0, SUCCESSANDNOMORE = 1, SUCCESSANDMORE = 2} Triad;

/* a dummy object to be associated with the database system */
static ObjPtr gcObject;
static nameTreeNode *NameTree; 
static llistNode *ClassList[N_CLASS_IDS + 1];  
/* class id starting from 1 
   Use 0th array element for objects whose class var is NULLOBJ */

#ifdef PROTO

static Triad addToNameTree(nameTreeNode **root, llistNode *listNode, char *name);
static Bool deleteFromNameTree(ObjPtr object, char *name);
static ObjPtr searchNameTree(char *name, int nKeys, NameTyp *keyId, ObjPtr *keyObj);
static nameTreeNode *createNameTreeNode(llistNode *node, char *objname);
static void freeNameTreeNode(nameTreeNode *node);

static Bool addToClassArray(llistNode *node, int classid);
static Bool deleteFromClassArray(ObjPtr object, int classid);
static ObjPtr searchClassArray(int classid, int nKeys, NameTyp *keyId, ObjPtr *keyObj);
static ObjPtr dumbSearch(int nKeys, NameTyp *keyId, ObjPtr *keyObj);

static llistNode *createLListNode(ObjPtr object);
static void freeLListNode(llistNode *node);
static void addToLList(llistNode **list, llistNode *node);
static Bool deleteFromLList(llistNode **list, ObjPtr object);
static ObjPtr searchLList(llistNode *list, int nKeys, NameTyp *keyId, ObjPtr *keyObj);

static Bool matched(ObjPtr object, int nKeys, NameTyp *keyId, ObjPtr *keyObj);

#else

static Triad addToNameTree();
static Bool deleteFromNameTree();
static ObjPtr searchNameTree();
static nameTreeNode *createNameTreeNode();
static void freeNameTreeNode();

static Bool addToClassArray();
static Bool deleteFromClassArray();
static ObjPtr searchClassArray();
static ObjPtr dumbSearch();

static llistNode *createLListNode();
static void freeLListNode();
static void addToLList();
static Bool deleteFromLList();
static ObjPtr searchLList();

static Bool matched();

#endif

/**************************** DATABASE OPERATIONS ***************************/
#ifdef PROTO
Bool AddObjToDatabase(ObjPtr object)
#else
Bool AddObjToDatabase(object)
ObjPtr object;
#endif
{
  ObjPtr var;
  char *name;
  char empty[] = "";
  int noclass = 0;
  int classid;
  llistNode *nameTreeListNode, *classArrayListNode;

  if (!object)
    return false;

  /* figure out the name of the object */
  MakeVar(object, NAME);
  var = GetVar(object, NAME);
  if (!var)
    {
      /* var is a NULLOBJ */
      name = empty;
    }
  else
    {
      name = GetString(var);
    }

  /* figure out the classid of the object */
  MakeVar(object, CLASSID);
  var = GetVar(object, CLASSID);
  if (!var)
    {
      classid = noclass;
    }
  else
    {
      classid = GetInt(var);
    }

  /* make a ClassArrayListNode and a NameTreeListNode and insert them
     into the NameTree and ClassArray respectively */
  if (!(nameTreeListNode = createLListNode(object)))
    {
      return false;
    }
  if (!(classArrayListNode = createLListNode(object)))
    {
      freeLListNode(nameTreeListNode);
      return false;
    }
  if (FAILED == addToNameTree(&NameTree, nameTreeListNode, name) || 
      false == addToClassArray(classArrayListNode, classid))
    {
      freeLListNode(nameTreeListNode);
      freeLListNode(classArrayListNode);
      return false;
    }
  return true;
} /* AddObjToDatabase */

#ifdef PROTO
Bool DeleteObjFromDatabase(ObjPtr object)
#else
Bool DeleteObjFromDatabase(object)
ObjPtr object;
#endif
/* The deleteFlag of a Nametreenode is turned on if it has an empty
   list, i.e., the list ptr points to null */
{
  char *name;
  char empty[] = "";
  ObjPtr var;
  int classid;
  int noclass = 0;
  llistNode *treeListNode, classArrayListNode;

  if (!object)
    return false;

  MakeVar(object, NAME);
  var = GetVar(object, NAME);
  if (!var)
    {
      /* var is a NULLOBJ */
      name = empty;
    }
  else
    {
      name = GetString(var);
    }

  MakeVar(object, CLASSID);
  var = GetVar(object, CLASSID);
  if (!var)
    {
      classid = noclass;
    }
  else
    {
      classid = GetInt(var);
    }

  /* if the object is not in the name tree, it should not be in the 
     class array either */
  if (!deleteFromNameTree(object, name))
    {
      if (deleteFromClassArray(object, classid))
	{
	  ReportError("DeleteObjFromDatabase", "database is in inconsistent state");
	}
      else
	{
          ReportError("DeleteObjFromDatabase", "object not found");
        }
      return false;
    }
  if (!deleteFromClassArray(object, classid))
    {
      ReportError("DeleteObjFromDatabase", "database is in inconsistent state");
      return false;
    }

  return true;
} /* DeleteObjFromDatabase */

#ifdef PROTO
ObjPtr SearchDatabase(ObjPtr keylist)
#else
ObjPtr SearchDatabase(keylist)
ObjPtr keylist;
#endif
/* if there is Name primarily search by NAME,
   else if there is CLASSID primarily search by CLASSID,
   else performs a linear search on the class array by the keys present */
/* returns NULLOBJ if error, an emptylist if not found, a list otherwise */
{
  char *emptyname = "";
  Bool isName = false;       /* is name there as a key in the list */
  char *name;

  ObjPtr classObj;
  Bool isClassid = false;       /* is class id there as a key in the list */
  int classid;
  int noclass = 0;

  /* construct an array of name types corresponding to keylist */
  NameTyp keyId[DB_MAXKEYS]; 
  /* construct an array of objptr from keylist */
  ObjPtr keyObj[DB_MAXKEYS];

  int index;
  int keyIndex;
  int nKeys;
  int nElements;

  if (!keylist)
    {
      /* keylist is a NULLOBJ */
      return dumbSearch(0, (NameTyp *) 0, (ObjPtr *) 0);
    }
  else if (!IsList(keylist) || ((nElements=ListCount(keylist)) % 2))
    {
      /* keylist must be a nonempty list and has even number of elements */
      ReportError("SearchDatabase", "Improper key list");
      return NULLOBJ;
    }
  else if (nElements == 0)
    {
      /* empty key list, return the whole database */
      return dumbSearch(0, (NameTyp *) 0, (ObjPtr *) 0);
    }
  else if ((nKeys = nElements / 2) > DB_MAXKEYS)
    {
      ReportError("SearchDatabase", "Exceeds the maximum number of allowed keys");
      return NULLOBJ;
    }

  keyIndex = 0;
  for (index = 0; index < nKeys; index++)
    {
      ObjPtr symbol;
      ObjPtr obj;
      NameTyp id;

      symbol = GetListElem(keylist, 2 * index);
      obj = GetListElem(keylist, 2 * index + 1);
      if (!IsSymbol(symbol))
	{
          ReportError("SearchDatabase", "Improper key list");
          return NULLOBJ;
	}
      id = GetSymbolID(symbol);
      
      if (id == NAME)
	{
          if (isName == true)
	    {
	      ReportError("SearchDatabase", "Double NAME keys in keylist");
	      return NULLOBJ;
	    }
	  if (!obj)
	    {
	      name = emptyname;
            }
          else if (!IsString(obj))
	    {
	      ReportError("SearchDatabase", "non-string obj after NAME symbol");
	      return NULLOBJ;
	    }
          else
	    {
	      name = GetString(obj);
	    }
          if (!name)
	    name = emptyname;
	  isName = true;  
	}
      else if (id == CLASSID)
	{
	  if (isClassid == true)
	    {
	      ReportError("SearchDatabase", "Double CLASSID keys in keylist");
	      return NULLOBJ;
	    }
	  if (!obj)
	    {
	      classid = noclass;
	    }
	  else if (!IsInt(obj))
	    {
	      ReportError("SearchDatabase", "non-class obj after CLASS symbol");
	      return NULLOBJ;
	    }
          else
	    {
	      classid = GetInt(obj);
	    }
          if (classid > N_CLASS_IDS || classid < 0)
	    {
	      ReportError("SearchDatabase", "Class id exceeds range");
	      return NULLOBJ;
	    }
	  classObj = obj;
          isClassid = true;
	}
      else
	{
          keyId[keyIndex] = id;
          keyObj[keyIndex] = obj;
	  keyIndex++;
	}
    }
  /* the search routines always return a list obj or NULLOBJ if failed */
  if (isName)
    {
      if (isClassid)
	{
	  keyId[keyIndex] = CLASSID;
	  keyObj[keyIndex] = classObj;
	}
	     /* NAME key is not included */
      return searchNameTree(name, nKeys - 1, keyId, keyObj);
    }
  else if (isClassid)
    {
	     /* CLASSID key is not included */
      return searchClassArray(classid, nKeys - 1, keyId, keyObj);
    }
  else
    {
      return dumbSearch(nKeys, keyId, keyObj);
    }
}

/* method use only old way of c function definition */
ObjPtr MarkDatabase(gcObject)
ObjPtr gcObject;
{
  /* it's sufficient to mark all the objects in the Class Array */
  /* easier to traverse */
  int i;
  llistNode *runner;

  for (i = 0; i <= N_CLASS_IDS; i++)
     for (runner = ClassList[i]; runner; runner = runner->next)
	{
	  MarkObject(runner->object);
	}
  return ObjTrue;
} /* MarkDatabase */

#ifdef PROTO
void InitDatabase(void)
#else
void InitDatabase()
#endif
{
  int i;

  gcObject = NewObject(NULLOBJ, 0);
  AddToReferenceList(gcObject);
  SetMethod(gcObject, MARK, MarkDatabase);

  /* empty database, do an initialization first */
  NameTree = (nameTreeNode *) 0;
  for (i = 0; i <= N_CLASS_IDS; i++)
     {
       ClassList[i] = (llistNode *) 0;
     }
  return;
} /* InitDatabase */

#ifdef PROTO
void KillDatabase(void)
#else
void KillDatabase()
#endif
{
  RemoveFromReferenceList(gcObject);
  return;
} /* KillDatabase */


/**************************** NameTree Operations ****************************/
/* 
   This is a recursive algorithm for adding a node into a binary tree and
   maintain its balance.  Balance in this case means the difference between
   any node's left and right link length is not greater than 1.

   When adding a linked-list node into the binary NameTree, three possible
   results may occur:
   0. Error iff an name tree node is not able be allocated or name is a 
      null pointer.
   1. Success and the list node is inserted into the linked list of an
      already existing tree node. 
   2. Success and a new tree node is created to accomdate this list node.

      For case 2, the flow of op is like this: 
      When the length of the current node changed, we let the parent
      to determine whether it needs to balance its subtree by returning
      SUCCESSANDMORE value.  Upon receiving the return value of 
      SUCCESSANDMORE, the current node look into its rightlength and
      leftlength for determining successive actions. ? possible actions: 

      1. When the length of left or right child changed and their difference 
         is greater than 1, we need to balance the subtree rooted at the
         current node.  After a balancing op, the length of the current node
         may be different.  Return accordingly.
      2. When their difference is not greater than 1, the current node needs 
         to determine whether it needs to increment its length by 1.  
         If YES, return SUCCESSANDMORE.
         If NO, return SUCCESSANDNOMORE.
*/
#ifdef PROTO
static Triad addToNameTree(nameTreeNode **root, llistNode *listNode, char *name)
#else
static Triad addToNameTree(root, listNode, name)
nameTreeNode *root;
llistNode *listNode;
char *name;
#endif
{
  int direction;
  Triad retVal;

  if (!name)
    return FAILED;

  if (!*root)
    {
      if ( !(*root = createNameTreeNode(listNode, name)) )
        return FAILED;
      return SUCCESSANDMORE;
    }

  direction = strcmp2((*root)->name, name);

  if (!direction)
    {
      if ((*root)->deleteFlag == true)
        {
          (*root)->deleteFlag = false;
        }
      addToLList(&(*root)->list, listNode);
      return SUCCESSANDNOMORE;
    }
  else if (direction < 0)
    {
      if ( SUCCESSANDMORE == 
            (retVal = addToNameTree(&((*root)->right), listNode, name)) )
        { 
          /* The root's right link length is just incremented by 1, see
             if we need to do the balancing task, or changing the link length
             of the root and returns SUCCESSANDMORE */

          int leftLength, rightLength;

          /* determine the left and right length */ 
          leftLength = (*root)->left ? (*root)->left->linkLength : 0;
          /* there must be a right child since we just added one */
          rightLength = (*root)->right->linkLength; 

          /* Check the length difference first and balancing accordingly */ 
          /* the length diff may only one of three possible values 0, 1, 2 */
          switch(rightLength - leftLength)
            {
              case 0:
                    /* Before adding the node, the situation is that
                       leftLength is 1 greater than rightLength, 
                       the link length of the root must be leftLength + 1 */
                    /* balanced and no need to change link length, do nothing */
                     retVal = SUCCESSANDNOMORE;
                     break;
              case 1: 
                     /* Before adding the node, the situation is that
                        rightLength is the same as leftLength */
                     /* rightLength - leftLength must be 1 after adding
                        the node to the right and get its rightlength
                        incremented by 1. */
                     (*root)->linkLength += 1;
                     retVal = SUCCESSANDMORE;
                     break;
              case 2:
                  {
                     /* Before adding the node, the situation is that
                        rightLength = leftLength + 1, the link length of the 
                        root must be old rightLength + 1 that is the current
                        value of rightlength */
                     /* needs balancing */
                     nameTreeNode *temp;  
                     int newRightLength;
                      
                     temp = *root;
                     *root = (*root)->right;
                     temp->right = (*root)->left;
                     (*root)->left = temp;
                     
                     /* after the switching, we need calculate the linklength
                        of the node currently pointed by temp, which is the
                        left child of the *root now.  */
                     /* The result of newRightLength - leftLength is either
                        0 or 1.  That's what the balancing for. */
                     newRightLength = temp->right ? temp->right->linkLength : 0;
                     if (newRightLength == leftLength)
                       {
                         temp->linkLength -= 1;
                         retVal = SUCCESSANDNOMORE;
                       }
                     else
                       {
                         (*root)->linkLength += 1;
                         retVal = SUCCESSANDMORE;
                       } 
                  }
                     break;
              default: 
                     /* dummy case */
                     break;
            }
        } 
      return retVal;
    }
  else
    {
      if ( SUCCESSANDMORE == 
            (retVal = addToNameTree(&((*root)->left), listNode, name)) )
        {
          int leftLength, rightLength;

          rightLength = (*root)->right ? (*root)->right->linkLength : 0;
          leftLength = (*root)->left->linkLength; 

          switch(leftLength - rightLength)
            {
              case 0:
                     retVal = SUCCESSANDNOMORE;
                     break;
              case 1: 
                     (*root)->linkLength += 1;
                     retVal = SUCCESSANDMORE;
                     break;
              case 2:
                  {
                     nameTreeNode *temp;  
                     int newLeftLength;
                      
                     temp = *root;
                     *root = (*root)->left;
                     temp->left = (*root)->right;
                     (*root)->right = temp;
                     
                     newLeftLength = temp->left ? temp->left->linkLength : 0;
                     if (newLeftLength == rightLength)
                       {
                         temp->linkLength -= 1;
                         retVal = SUCCESSANDNOMORE;
                       }
                     else
                       {
                         (*root)->linkLength += 1;
                         retVal = SUCCESSANDMORE;
                       } 
                  }
                     break;
              default: 
                     /* dummy case */
                     break;
            }
        } 
      return retVal;
    }
} /* addToNameTree */

/* This is an iteration algorithm for pseudo deleting a node from the 
   name tree.  A node is pseudo deleted iff it has an empty linked list.
   Pseudo deletion is done by turning on the deleteFlag of the tree node. */
#ifdef PROTO
static Bool deleteFromNameTree(ObjPtr object, char *name)
#else
static Bool deleteFromNameTree(object, name)
ObjPtr object;
char *name;
#endif
{
  nameTreeNode *runner;
  Bool retVal;

  runner = NameTree;
  while(runner)
    {
      int direction;

      direction = strcmp2(runner->name, name);

      if (!direction)
	{
          if (runner->deleteFlag == true)
	    {
              ReportError("deleteFromNameTree", "object not found");
	      return false;
            }
	  retVal = deleteFromLList(&runner->list, object);
	  if (!runner->list)
	    {
	      /* if there is no more nodes in the list */
	      runner->deleteFlag = true;
	    }
	  return retVal;
	}
      else if (direction < 0)
	{
	  runner = runner->right; 
	}
      else
	{
	  runner = runner->left; 
	}
    }/* while loop */

  ReportError("deleteFromNameTree", "object not found");
  return false;
} /* deleteFromNameTree */

#ifdef PROTO
static ObjPtr searchNameTree(char *name, int nKeys, NameTyp *keyId, ObjPtr *keyObj)
#else
static ObjPtr searchNameTree(name, nKeys, keyId, keyObj)
char *name;
int nKeys;
NameTyp *keyId;
ObjPtr *keyObj;
#endif
{
  nameTreeNode *runner;

  runner = NameTree;
  while(runner)
    {
      int direction;

      direction = strcmp2(runner->name, name);

      if (!direction)
	{
          if (runner->deleteFlag == false)
	    {
	      return searchLList(runner->list, nKeys, keyId, keyObj);
	    }
	  else
	    {
	      return NewList();
	    }
        }
      else if (direction < 0)
	{
	  runner = runner->right; 
	}
      else
	{
	  runner = runner->left; 
	}
    }/* while loop */

  return NewList();
} /* searchNameTree */

#ifdef PROTO
static nameTreeNode *createNameTreeNode(llistNode *node, char *objname)
#else
static nameTreeNode *createNameTreeNode(node, objname)
llistNode *node;
char *objname;
#endif
/* Construct a new tree node and add node at the beginning of the tree node list */
{
  nameTreeNode *retVal;

  if (!(retVal = (nameTreeNode *) malloc(sizeof(nameTreeNode))))
    {
      ReportError("createNameTreeNode", "out of memory");
      return (nameTreeNode *) 0;
    }

  if (!(retVal->name = (char *) malloc((strlen(objname) + 1) * sizeof(char))))
    {
      free(retVal);
      ReportError("createNameTreeNode", "out of memory");
      return (nameTreeNode *) 0;
    }

  strcpy(retVal->name, objname);
  retVal->left = (nameTreeNode *) 0;
  retVal->right = (nameTreeNode *) 0;
  retVal->list = node;
  retVal->deleteFlag = false;
  retVal->linkLength = 1; /* a newly created tree node is going to be 
                             at the leaf position which is of link length
                             1 */

  return retVal;
} /* createNameTreeNode */

#ifdef PROTO
static void freeNameTreeNode(nameTreeNode *node)
#else
static void freeNameTreeNode(node)
nameTreeNode *node;
#endif
{
  free(node->name);
  free(node);
  return;
}


/**************************** ClassArray Operations ****************************/
#ifdef PROTO
static Bool addToClassArray(llistNode *node, int classid)
#else
static Bool addToClassArray(node, classid)
llistNode *node;
int classid;
#endif
{
  if (classid < 0 || classid > N_CLASS_IDS)
    {
      ReportError("addToClassArray", "Class id exceeds range");
      return false;
    }
  else
    {
      addToLList(ClassList + classid, node);
      return true;
    }
} /* addToClassArray */

#ifdef PROTO
static Bool deleteFromClassArray(ObjPtr object, int classid)
#else
static Bool deleteFromClassArray(object, classid)
ObjPtr object;
int classid;
#endif
{
  if (classid < 0 || classid > N_CLASS_IDS)
    {
      ReportError("deleteFromClassArray", "Class id exceeds range");
      return false;
    }
  else
    {
      return deleteFromLList(ClassList + classid, object);
    }
} /* deleteFromClassArray */

#ifdef PROTO
static ObjPtr searchClassArray(int classid, int nKeys, NameTyp *keyId, ObjPtr *keyObj)
#else
static ObjPtr searchClassArray(classid, nKeys, keyId, keyObj)
int classid;
int nKeys;
NameTyp *keyId;
ObjPtr *keyObj;
#endif
{
  if (classid < 0 || classid > N_CLASS_IDS)
    {
      ReportError("searchClassArray", "Class id exceeds range");
      return NULLOBJ;
    }
  else
    {
      return searchLList(ClassList[classid], nKeys, keyId, keyObj);
    }
}

#ifdef PROTO
static ObjPtr dumbSearch(int nKeys, NameTyp *keyId, ObjPtr *keyObj)
#else
static ObjPtr dumbSearch(nKeys, keyId, keyObj)
int nKeys;
NameTyp *keyId;
ObjPtr *keyObj;
#endif
{
  int runner;
  ObjPtr newlist;
  ObjPtr retVal = NULLOBJ; 

  for (runner = 0; runner <= N_CLASS_IDS; runner++)
     {
       newlist = searchLList(ClassList[runner], nKeys, keyId, keyObj);
       if (!newlist)
	 {
	   return NULLOBJ;
	 }
       else
	 {
	   if (ListCount(newlist) > 0)
	     {
	       if (!retVal)
		 {
		   if (!(retVal = NewList()))
		     {
		       ReportError("dumbSearch", "Unable to get a new list");
		       return NULLOBJ;
		     }
		 }
               AppendList(retVal, newlist); 
	     }
	 }
     }
  return retVal;
}

/****************************** LList Operations *******************************/

#ifdef PROTO
static llistNode *createLListNode(ObjPtr object)
#else
static llistNode *createLListNode(object)
ObjPtr object;
#endif
{
  llistNode *retVal;

  if (!(retVal = (llistNode *)malloc(sizeof(llistNode))))
    {
      ReportError("createLListNode", "out of memory");
      return (llistNode *) 0;
    }
  else
    {
      retVal->object = object;
      retVal->next = (llistNode *) 0;
      return retVal;
    }
} /* createLListNode */

#ifdef PROTO
static void freeLListNode(llistNode *node)
#else
static void freeLListNode(node)
llistNode *node;
#endif
{
  free(node);
  return;
} /* freeLListNode */

#ifdef PROTO
static void addToLList(llistNode **list, llistNode *node)
#else
static void addToLList(list, node)
llistNode **list;
llistNode *node;
#endif
{
  node->next = *list;
  *list = node;

  return;
}

#ifdef PROTO
static Bool deleteFromLList(llistNode **list, ObjPtr object)
#else
static Bool deleteFromLList(list, object)
llistNode **list;
ObjPtr object;
#endif
{
  llistNode *current, *previous;

  current = *list;

  while(current)
    {
      if (current->object == object)
	/* object found */
	{
	  /* at the beggining of the list */
	  if (current == *list)
	    {
	      *list = current->next;
	    }
	  /* somewhere after first node in the list */
          else
	    {
	      previous->next = current->next;  
	    }
          freeLListNode(current);
	  return true;
	}
      else
	{
	  previous = current;
	  current = current->next;
	}
    }

  /* the object is not found within the list */
  ReportError("deleteFromLList", "object not found");
  return false;
}

#ifdef PROTO
static ObjPtr searchLList(llistNode *list, int nKeys, NameTyp *keyId, ObjPtr *keyObj)
#else
static ObjPtr searchLList(list, nKeys, keyId, keyObj)
llistNode *list;
int nKeys;
NameTyp *keyId;
ObjPtr *keyObj;
#endif
{
  llistNode *runner;
  ObjPtr retVal;

  if (!(retVal = NewList()))
    {
      ReportError("searchLList", "Unable to get a new list");
      return NULLOBJ;
    }

  runner = list;
  while(runner)
    {
      if (true == matched(runner->object, nKeys, keyId, keyObj))
        {
          if (!PrefixList(retVal, runner->object))
            {
	      ReportError("searchLList", "Unable to Prefix to list");
              return NULLOBJ;
            }
	}
      runner = runner->next;
    }

  return retVal;
}

/************************** matching operation ******************************/

#ifdef PROTO
static Bool matched(ObjPtr object, int nKeys, NameTyp *keyId, ObjPtr *keyObj)
#else
static Bool matched(object, nKeys, keyId, keyObj)
ObjPtr object;
int nKeys;
NameTyp *keyId;
ObjPtr *keyObj;
#endif
{
  Bool matched = true;
  int i;

  for (i = 0; matched && i < nKeys; i++)
    {
      ObjPtr varObj;

      MakeVar(object, keyId[i]);
      varObj = GetVar(object, keyId[i]);

      if (IsString(keyObj[i]))
	{
	  if (!IsString(varObj))
	    {
	      return false; 
	    }
          else
	    {
	      char *keyString, *varString;

	      keyString = GetString(keyObj[i]);
	      varString = GetString(varObj);
	      matched = strcmp2(keyString, varString) ? false : true;
	    }
	}
      else
	{
	  matched = Equal(varObj, keyObj[i]);
	}
    }

  return matched;
}
