/*
FILE:     ndb_cifparse_util.C
*/
/*
VERSION:  3.101
*/
/*
DATE:     8/27/2004
*/
/*
  Comments and Questions to: sw-help@rcsb.rutgers.edu
*/
/*
COPYRIGHT 1999-2004 Rutgers - The State University of New Jersey

This software is provided WITHOUT WARRANTY OF MERCHANTABILITY OR
FITNESS FOR A PARTICULAR PURPOSE OR ANY OTHER WARRANTY, EXPRESS OR
IMPLIED.  RUTGERS MAKE NO REPRESENTATION OR WARRANTY THAT THE
SOFTWARE WILL NOT INFRINGE ANY PATENT, COPYRIGHT OR OTHER
PROPRIETARY RIGHT.

The user of this software shall indemnify, hold harmless and defend
Rutgers, its governors, trustees, officers, employees, students,
agents and the authors against any and all claims, suits,
losses, liabilities, damages, costs, fees, and expenses including
reasonable attorneys' fees resulting from or arising out of the
use of this software.  This indemnification shall include, but is
not limited to, any and all claims alleging products liability.
*/
/*
               PDB SOFTWARE LICENSE AGREEMENT

BY CLICKING THE ACCEPTANCE BUTTON OR INSTALLING OR USING 
THIS "SOFTWARE, THE INDIVIDUAL OR ENTITY LICENSING THE  
SOFTWARE ("LICENSEE") IS CONSENTING TO BE BOUND BY AND IS 
BECOMING A PARTY TO THIS AGREEMENT.  IF LICENSEE DOES NOT 
AGREE TO ALL OF THE TERMS OF THIS AGREEMENT
THE LICENSEE MUST NOT INSTALL OR USE THE SOFTWARE.

1. LICENSE AGREEMENT

This is a license between you ("Licensee") and the Protein Data Bank (PDB) 
at Rutgers, The State University of New Jersey (hereafter referred to 
as "RUTGERS").   The software is owned by RUTGERS and protected by 
copyright laws, and some elements are protected by laws governing 
trademarks, trade dress and trade secrets, and may be protected by 
patent laws. 

2. LICENSE GRANT

RUTGERS grants you, and you hereby accept, non-exclusive, royalty-free 
perpetual license to install, use, modify, prepare derivative works, 
incorporate into other computer software, and distribute in binary 
and source code format, or any derivative work thereof, together with 
any associated media, printed materials, and on-line or electronic 
documentation (if any) provided by RUTGERS (collectively, the "SOFTWARE"), 
subject to the following terms and conditions: (i) any distribution 
of the SOFTWARE shall bind the receiver to the terms and conditions 
of this Agreement; (ii) any distribution of the SOFTWARE in modified 
form shall clearly state that the SOFTWARE has been modified from 
the version originally obtained from RUTGERS.  

2. COPYRIGHT; RETENTION OF RIGHTS.  

The above license grant is conditioned on the following: (i) you must 
reproduce all copyright notices and other proprietary notices on any 
copies of the SOFTWARE and you must not remove such notices; (ii) in 
the event you compile the SOFTWARE, you will include the copyright 
notice with the binary in such a manner as to allow it to be easily 
viewable; (iii) if you incorporate the SOFTWARE into other code, you 
must provide notice that the code contains the SOFTWARE and include 
a copy of the copyright notices and other proprietary notices.  All 
copies of the SOFTWARE shall be subject to the terms of this Agreement.  

3. NO MAINTENANCE OR SUPPORT; TREATMENT OF ENHANCEMENTS 

RUTGERS is under no obligation whatsoever to: (i) provide maintenance 
or support for the SOFTWARE; or (ii) to notify you of bug fixes, patches, 
or upgrades to the features, functionality or performance of the 
SOFTWARE ("Enhancements") (if any), whether developed by RUTGERS 
or third parties.  If, in its sole discretion, RUTGERS makes an 
Enhancement available to you and RUTGERS does not separately enter 
into a written license agreement with you relating to such bug fix, 
patch or upgrade, then it shall be deemed incorporated into the SOFTWARE 
and subject to this Agreement. You are under no obligation whatsoever 
to provide any Enhancements to RUTGERS or the public that you may 
develop over time; however, if you choose to provide your Enhancements 
to RUTGERS, or if you choose to otherwise publish or distribute your 
Enhancements, in source code form without contemporaneously requiring 
end users or RUTGERS to enter into a separate written license agreement 
for such Enhancements, then you hereby grant RUTGERS a non-exclusive,
royalty-free perpetual license to install, use, modify, prepare
derivative works, incorporate into the SOFTWARE or other computer
software, distribute, and sublicense your Enhancements or derivative
works thereof, in binary and source code form.

4. FEES.  There is no license fee for the SOFTWARE.  If Licensee
wishes to receive the SOFTWARE on media, there may be a small charge
for the media and for shipping and handling.  Licensee is
responsible for any and all taxes.

5. TERMINATION.  Without prejudice to any other rights, Licensor
may terminate this Agreement if Licensee breaches any of its terms
and conditions.  Upon termination, Licensee shall destroy all
copies of the SOFTWARE.

6. PROPRIETARY RIGHTS.  Title, ownership rights, and intellectual
property rights in the Product shall remain with RUTGERS.  Licensee 
acknowledges such ownership and intellectual property rights and will 
not take any action to jeopardize, limit or interfere in any manner 
with RUTGERS' ownership of or rights with respect to the SOFTWARE.  
The SOFTWARE is protected by copyright and other intellectual 
property laws and by international treaties.  Title and related 
rights in the content accessed through the SOFTWARE is the property 
of the applicable content owner and is protected by applicable law.  
The license granted under this Agreement gives Licensee no rights to such
content.

7. DISCLAIMER OF WARRANTY.  THE SOFTWARE IS PROVIDED FREE OF 
CHARGE, AND, THEREFORE, ON AN "AS IS" BASIS, WITHOUT WARRANTY OF 
ANY KIND, INCLUDING WITHOUT LIMITATION THE WARRANTIES THAT IT 
IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE 
OR NON-INFRINGING.  THE ENTIRE RISK AS TO THE QUALITY AND 
PERFORMANCE OF THE SOFTWARE IS BORNE BY LICENSEE.  SHOULD THE 
SOFTWARE PROVE DEFECTIVE IN ANY RESPECT, THE LICENSEE AND NOT 
LICENSOR ASSUMES THE ENTIRE COST OF ANY SERVICE AND REPAIR.  
THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF 
THIS AGREEMENT.  NO USE OF THE PRODUCT IS AUTHORIZED HEREUNDER 
EXCEPT UNDER THIS DISCLAIMER.

8. LIMITATION OF LIABILITY.  TO THE MAXIMUM EXTENT PERMITTED BY
APPLICABLE LAW,  IN NO EVENT WILL LICENSOR BE LIABLE FOR ANY 
INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 
OUT OF THE USE OF OR INABILITY TO USE THE SOFTWARE, INCLUDING, 
WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK 
STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL 
OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF ADVISED OF THE
POSSIBILITY THEREOF. 
*/
/* 
  PURPOSE:       This collection of routines provides a simple interface 
                 to the DDL 2.1 compliant CIF files.
*/


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

#include "ndb_cifparse.h"

/* General rules for return values:
 * Return values to user 
 *
 * 0 :  Failure
 * 1 :  Success  or
 * >=1: Index of that has been assigned to the request or current index
 *      This index starts from 1 (which is one more than C-based index)
 */


/* ----------------------------------------------------------------
   initialization
   ---------------------------------------------------------------*/

int ndb_cif_init(CifHandle *cf)
{
  int i;
 
  (*cf).numDatablock = 0;
  (*cf).curDatablock = -1;
  (*cf).allDatablock = CifDefaultSpace ;
  (*cf).datablocks = (NdbCifDatablockFormat *)
    calloc((*cf).allDatablock, sizeof(NdbCifDatablockFormat));
  for (i = 0; i <   (*cf).allDatablock; i++) {
      (*cf).datablocks[i].allCategory = 0;
      (*cf).datablocks[i].numCategory = 0;      
      (*cf).datablocks[i].curCategory = -1;      
      memset((*cf).datablocks[i].datablockName,0,MxNameLen);
  }
  return 1;
}

/* ----------------------------------------------------------------
   close
   ---------------------------------------------------------------*/
int ndb_cif_close(CifHandle *cf)
{
  int i, j, k, l;
  if ((*cf).datablocks !=NULL) {
    for (i=(*cf).numDatablock-1; i>=0; i--) {
      for (j=(*cf).datablocks[i].allCategory-1; j>=0; j--) {
	if ((*cf).datablocks[i].categories[j].rows != NULL) {
	  for (k= (*cf).datablocks[i].categories[j].allRow-1; k>=0; k--) {
	    if ((*cf).datablocks[i].categories[j].rows[k].columns != NULL) {
	      for (l= (*cf).datablocks[i].categories[j].allCol-1; l>=0; l--) {
		if ((*cf).datablocks[i].categories[j].rows[k].columns[l] != NULL)
		  free((*cf).datablocks[i].categories[j].rows[k].columns[l]);
		(*cf).datablocks[i].categories[j].rows[k].columns[l] = NULL;
	      }
	      free((*cf).datablocks[i].categories[j].rows[k].columns);
	      (*cf).datablocks[i].categories[j].rows[k].columns = NULL;
	    }
	  }
	  free((*cf).datablocks[i].categories[j].rows);
	  (*cf).datablocks[i].categories[j].rows = NULL;

	  for (l= (*cf).datablocks[i].categories[j].allCol-1; l>=0; l--) {
	    if ((*cf).datablocks[i].categories[j].colNames[l] != NULL)
	      free((*cf).datablocks[i].categories[j].colNames[l]);
	    (*cf).datablocks[i].categories[j].colNames[l] = NULL;
	  }
	  free((*cf).datablocks[i].categories[j].colNames);
	  (*cf).datablocks[i].categories[j].colNames = NULL;
	}
      }
      free((*cf).datablocks[i].categories);
      (*cf).datablocks[i].categories = NULL;
    }
    free((*cf).datablocks);
    (*cf).datablocks = NULL;
  }
    
  (*cf).numDatablock = 0;
  (*cf).curDatablock = -1;
  (*cf).allDatablock = 0;

  return 1;
}
/* ----------------------------------------------------------------
   allocation 
   ---------------------------------------------------------------*/
int ndb_cif_new_datablock(CifHandle *cf,const char *datablockName)
{
  int i;
  if ((*cf).numDatablock ==  (*cf).allDatablock) {
    (*cf).allDatablock *= 2;
    (*cf).datablocks = (NdbCifDatablockFormat *)
      realloc((*cf).datablocks, 
	      (*cf).allDatablock * sizeof(NdbCifDatablockFormat));
    for (i=(*cf).numDatablock; i < (*cf).allDatablock; i++) {
      (*cf).datablocks[i].categories = NULL;
      (*cf).datablocks[i].allCategory = 0;
      (*cf).datablocks[i].numCategory = 0;
      (*cf).datablocks[i].curCategory = -1;
      memset((*cf).datablocks[i].datablockName,0,MxNameLen);
    }
  }
  (*cf).curDatablock = (*cf).numDatablock;
  (*cf).numDatablock++;
  strcpy((*cf).datablocks[(*cf).curDatablock].datablockName, datablockName);

  /* Allocate spaces for category */
  (*cf).datablocks[(*cf).curDatablock].allCategory = CifDefaultSpace ;
  (*cf).datablocks[(*cf).curDatablock].categories = (NdbCifCategoryFormat *)
    calloc((*cf).datablocks[(*cf).curDatablock].allCategory,
	   sizeof(NdbCifCategoryFormat));

  for (i=0; i< (*cf).datablocks[(*cf).curDatablock].allCategory; i++) {
    (*cf).datablocks[(*cf).curDatablock].categories[i].rows = NULL;
    (*cf).datablocks[(*cf).curDatablock].categories[i].colNames = NULL;
  }

  (*cf).datablocks[(*cf).curDatablock].numCategory = 0;
  (*cf).datablocks[(*cf).curDatablock].curCategory = -1;

  return (*cf).curDatablock +1;
}

int  ndb_cif_put_datablock_name(CifHandle *cf,const char *datablockName)
{
  if ((*cf).numDatablock == 0)
    ndb_cif_new_datablock(cf,datablockName);
  else
    strcpy((*cf).datablocks[(*cf).curDatablock].datablockName,datablockName);
  return(1);
}


int ndb_cif_new_category(CifHandle *cf,const char *categoryName)
{
  int categoryNo,i;



  if ((*cf).numDatablock == 0)
    ndb_cif_new_datablock(cf,"");


  if ((*cf).datablocks[(*cf).curDatablock].numCategory ==  
      (*cf).datablocks[(*cf).curDatablock].allCategory) {

    (*cf).datablocks[(*cf).curDatablock].allCategory *=2;
    (*cf).datablocks[(*cf).curDatablock].categories = 
      (NdbCifCategoryFormat *)
      realloc((*cf).datablocks[(*cf).curDatablock].categories, 
	      (*cf).datablocks[(*cf).curDatablock].allCategory * 
	      sizeof(NdbCifCategoryFormat));

    for (i=(*cf).datablocks[(*cf).curDatablock].numCategory; 
	 i < (*cf).datablocks[(*cf).curDatablock].allCategory; i++) {
      (*cf).datablocks[(*cf).curDatablock].categories[i].rows = NULL;
      (*cf).datablocks[(*cf).curDatablock].categories[i].colNames = NULL;
    }
      
  }
  categoryNo = (*cf).datablocks[(*cf).curDatablock].numCategory;
  (*cf).datablocks[(*cf).curDatablock].curCategory = categoryNo;
  (*cf).datablocks[(*cf).curDatablock].numCategory++;
  (*cf).datablocks[(*cf).curDatablock].categories[categoryNo].numRow = 0;
  (*cf).datablocks[(*cf).curDatablock].categories[categoryNo].numCol = 0;

  /* Allocate space for Rows */
  (*cf).datablocks[(*cf).curDatablock].categories[categoryNo].allRow = 
    CifDefaultSpace;
  (*cf).datablocks[(*cf).curDatablock].categories[categoryNo].rows= 
    (NdbCifRowFormat *) calloc(CifDefaultSpace, sizeof(NdbCifRowFormat));

  (*cf).datablocks[(*cf).curDatablock].categories[categoryNo].curRow = -1;

  /* Allocate space for Column Names */
  (*cf).datablocks[(*cf).curDatablock].categories[categoryNo].allCol = 
    CifDefaultSpace;
  for (i=0; i<CifDefaultSpace; i++) {
    (*cf).datablocks[(*cf).curDatablock].categories[categoryNo].rows[i].columns
      = (char **)calloc(CifDefaultSpace,sizeof(char *));
  }

  (*cf).datablocks[(*cf).curDatablock].categories[categoryNo].colNames = 
    (char **) calloc(CifDefaultSpace, sizeof(char *));
  for (i=0; i<CifDefaultSpace; i++)
    (*cf).datablocks[(*cf).curDatablock].categories[categoryNo].colNames[i] = NULL;
  (*cf).datablocks[(*cf).curDatablock].categories[categoryNo].curCol = -1;
  
  strcpy((*cf).datablocks[(*cf).curDatablock].
	 categories[categoryNo].categoryName, categoryName);

  return (*cf).datablocks[(*cf).curDatablock].curCategory +1;
}

int ndb_cif_new_row(CifHandle *cf)
{
  int curCategory, i, curRow, curDatablock;
  NdbCifCategoryFormat *pCategory;
  NdbCifRowFormat *pRow;
  
  if ((*cf).numDatablock == 0 || (*cf).curDatablock < 0)
    return 0;

  if ((*cf).datablocks[(*cf).curDatablock].numCategory == 0)
    return 0;

  curDatablock = (*cf).curDatablock;
  curCategory = (*cf).datablocks[curDatablock].curCategory;
  if (curCategory < 0) return 0;
  pCategory = &(*cf).datablocks[curDatablock].categories[curCategory];
  if (pCategory == NULL) return 0;

  if (pCategory->numRow == pCategory->allRow) {
    pCategory->allRow *=2;
    pCategory->rows = (NdbCifRowFormat *) 
      realloc(pCategory->rows, pCategory->allRow* sizeof(NdbCifRowFormat));
    for (i = pCategory->numRow ; i< pCategory->allRow ; i++)
      pCategory->rows[i].columns = NULL;
  }
  pCategory->curRow = pCategory->numRow;
  curRow = pCategory->curRow;
  pRow = &pCategory->rows[curRow];
  pCategory->numRow ++;
  if ( pRow->columns == NULL) {
    pRow->columns = (char **) calloc(pCategory->allCol, sizeof(char *));
  }
    for (i=0; i< pCategory->allCol; i++)
      pRow->columns[i] = (char *) calloc(1, sizeof(char));
  
  return pCategory->curRow +1;
}

int ndb_cif_insert_new_row(CifHandle *cf,const int rowNo)
{
  int curCategory, i, j, curDatablock, curRow;
  NdbCifCategoryFormat *pCategory;
  NdbCifRowFormat pRow;

  curDatablock = (*cf).curDatablock;
  if ((*cf).numDatablock == 0 || (*cf).curDatablock < 0)
    return 0;

  if ((*cf).datablocks[curDatablock].numCategory == 0 )
    return 0;

  curCategory = (*cf).datablocks[curDatablock].curCategory;
  if (curCategory < 0) return 0;
  pCategory = &(*cf).datablocks[curDatablock].categories[curCategory];
  if (pCategory == NULL) return 0;
  else if (rowNo > pCategory->numRow)
    return 0;

  curRow = ndb_cif_new_row(cf);

  /* 
   * Move the new allocated to correct space
   */
  memcpy(&pRow, &pCategory->rows[curRow-1], sizeof(NdbCifRowFormat));
  for (i=pCategory->numRow-1; i >= rowNo; i--)
    for (j=0 ; j< pCategory->numCol; j++) 
      pCategory->rows[i].columns[j] = pCategory->rows[i-1].columns[j];

  memcpy(&pCategory->rows[rowNo-1], &pRow, sizeof(NdbCifRowFormat));
  pCategory->curRow = rowNo;
  return pCategory->curRow +1;
}

/*
 * Rewind file, rewind to the first datablock.
 */
int ndb_cif_rewind_file(CifHandle *cf)
{
  int curDatablock;
  (*cf).curDatablock=0;
  do {
    ndb_cif_rewind_datablock(cf);
    curDatablock = ndb_cif_next_datablock(cf);
  } while (curDatablock);
  (*cf).curDatablock=0;
  
  return (*cf).curDatablock+1;

}


/*
 * Rewind datablock, category, row or field to first one.
 */
int ndb_cif_rewind_datablock(CifHandle *cf)
{
  int curCategory;

  if ((*cf).numDatablock <= 0) return 0;

  (*cf).datablocks[(*cf).curDatablock].curCategory = 0;
  do {
    ndb_cif_rewind_category(cf);
    curCategory = ndb_cif_next_category(cf);
  } while (curCategory);
  (*cf).datablocks[(*cf).curDatablock].curCategory = 0;

  return (*cf).curDatablock+1;

}

int ndb_cif_rewind_category(CifHandle *cf)
{
  if ((*cf).numDatablock == 0 ||
      (*cf).datablocks[(*cf).curDatablock].numCategory == 0)
    return 0;
  ndb_cif_rewind_row(cf);
  ndb_cif_rewind_column(cf);
  return (*cf).datablocks[(*cf).curDatablock].curCategory +1;
}

int ndb_cif_rewind_row(CifHandle *cf)
{
  if ((*cf).numDatablock == 0 ||
      (*cf).datablocks[(*cf).curDatablock].numCategory == 0 ||
      (*cf).datablocks[(*cf).curDatablock].
      categories[(*cf).datablocks[(*cf).curDatablock].curCategory].numRow == 0)
    return 0;
  (*cf).datablocks[(*cf).curDatablock].
    categories[(*cf).datablocks[(*cf).curDatablock].curCategory].curRow = 0;
  ndb_cif_rewind_column(cf);
  return 1;
}

int ndb_cif_rewind_column(CifHandle *cf)
{
  int curCategory;
  if ((*cf).numDatablock == 0 ||
      (*cf).datablocks[(*cf).curDatablock].numCategory == 0 ||
      (*cf).datablocks[(*cf).curDatablock].curCategory < 0 ||
      (*cf).datablocks[(*cf).curDatablock].curCategory >=
      (*cf).datablocks[(*cf).curDatablock].numCategory)
    return 0;
  curCategory = (*cf).datablocks[(*cf).curDatablock].curCategory;
  if ((*cf).datablocks[(*cf).curDatablock].categories[curCategory].numRow == 0)
    return 0;
  (*cf).datablocks[(*cf).curDatablock].categories[curCategory].curCol = 0;
  return (*cf).datablocks[(*cf).curDatablock].categories[curCategory].curCol+ 1;
}

/*
 * Reset/clear up datablock, category
 */
int ndb_cif_reset_datablocks(CifHandle *cf)
{
  int ret;
  ret = ndb_cif_rewind_datablock(cf);
  while (ret) {
    ret = ndb_cif_next_datablock(cf);
    if (ret) ndb_cif_reset_datablock(cf);
  } 
  return ret;
}

int ndb_cif_reset_datablock(CifHandle *cf)
{
  int ret;
  int i, num;
  
  if ((*cf).numDatablock == 0) return 0;

  ret = ndb_cif_rewind_datablock(cf);
  num = (*cf).datablocks[(*cf).curDatablock].numCategory;
  for (i=0; i<num; i++){
    ret = ndb_cif_reset_category_by_id(cf, (*cf).curDatablock+1,i+1);
  }
  return ret;
}

int ndb_cif_reset_datablock_by_id(CifHandle *cf,const int datablockId)
{
  int i, ret;

  if (datablockId <=0 ||
      (*cf).numDatablock == 0 ||
      (*cf).numDatablock < datablockId) return 0;

  for (i=0; i<(*cf).datablocks[datablockId-1].numCategory; i++)
    ret  = ndb_cif_reset_category_by_id(cf,datablockId, i+1);
  return ret;

}

int ndb_cif_reset_category(CifHandle *cf)
{
  int i, nrows;
  
  if ((*cf).numDatablock == 0 ||
      (*cf).curDatablock >= (*cf).numDatablock ||
      (*cf).datablocks[(*cf).curDatablock].numCategory == 0  ||
      (*cf).datablocks[(*cf).curDatablock].curCategory >=
      (*cf).datablocks[(*cf).curDatablock].numCategory )
    return 0;
  

  nrows = (*cf).datablocks[(*cf).curDatablock].
    categories[(*cf).datablocks[(*cf).curDatablock].curCategory].numRow;
  for (i=nrows-1; i >=0 ; i--) 
    ndb_cif_remove_row_by_id(cf,(*cf).curDatablock+1,
				   (*cf).datablocks[(*cf).curDatablock].
									  curCategory+1, i+1);
  
  return 1;
}

int ndb_cif_reset_category_by_id(CifHandle *cf,const int datablockId, const int categoryId)
{
  int ret;
  
  if (datablockId <=0 || categoryId <=0 || 
      (*cf).numDatablock < datablockId ||
      (*cf).datablocks[datablockId-1].numCategory < categoryId)
    return 0;
  
  do {
    ret = ndb_cif_remove_row_by_id(cf,datablockId, categoryId, 1);
  } while (ret);
  return 1;
}

/*
 * Remove datablock, category, row or field to first one.
 */

int ndb_cif_remove_datablock(CifHandle *cf)
{
  int j;
  int datablockId;

  if ((*cf).numDatablock == 0) return 0;

  /* Reset free space allocated to this datablock, and memset only copy stuff, not the
   * stuff come with the pointers
   */
  datablockId = (*cf).curDatablock+1;
  for (j=(*cf).datablocks[datablockId-1].allCategory-1; j>=0; j--) {
    if((*cf).datablocks[datablockId-1].categories[j].rows!=NULL) {
      ndb_cif_remove_category_by_id(cf,datablockId,j+1);
    }
  }
  free((*cf).datablocks[datablockId-1].categories);
  (*cf).datablocks[datablockId-1].categories=NULL;

  if ((*cf).numDatablock > (*cf).curDatablock) {
    memcpy(&(*cf).datablocks[(*cf).curDatablock], 
	   &(*cf).datablocks[(*cf).curDatablock+1], 
	   sizeof(NdbCifDatablockFormat) * 
	   ((*cf).numDatablock - (*cf).curDatablock - 1));
  }
  (*cf).numDatablock --;

  if ((*cf).numDatablock == (*cf).curDatablock)   (*cf).curDatablock --;

  return (*cf).curDatablock+1;
}

int ndb_cif_remove_datablock_by_id(CifHandle *cf,const int datablockId)
{
  int j;

  if (datablockId <= 0 ||
      datablockId > (*cf).numDatablock)
    return 0;
  if (datablockId-1 == (*cf).curDatablock)
    return ndb_cif_remove_datablock(cf);

  /* Reset free spaces with this datablock, and memset only copy stuff, not the
   * stuff come with the pointers
   */
  for (j=(*cf).datablocks[datablockId-1].allCategory-1; j>=0; j--) {
    if((*cf).datablocks[datablockId-1].categories[j].rows!=NULL) {
      ndb_cif_remove_category_by_id(cf,datablockId,j+1);
    }
  }
  free((*cf).datablocks[datablockId-1].categories);
  (*cf).datablocks[datablockId-1].categories=NULL;

  if ((*cf).numDatablock > (*cf).curDatablock) {
    memcpy(&(*cf).datablocks[datablockId-1], 
	   &(*cf).datablocks[datablockId], 
	   sizeof(NdbCifDatablockFormat) * 
	   ((*cf).numDatablock - datablockId));
  }
  (*cf).numDatablock --;
  return (*cf).curDatablock +1;
}

int ndb_cif_remove_datablock_by_name(CifHandle *cf,const char *datablockName)
{
  int datablockId;

  datablockId = ndb_cif_get_datablock_id(cf,datablockName);
  if (datablockId == 0) return 0;
  else 
    return (ndb_cif_remove_datablock_by_id(cf,datablockId));
}


int ndb_cif_remove_category(CifHandle *cf)
{
  int datablockId;
  int categoryId;
  int k,l;
  
  if ((*cf).numDatablock == 0 ||
      (*cf).datablocks[(*cf).curDatablock].numCategory == 0) 
    return 0;
  
  datablockId = (*cf).curDatablock;
  categoryId = (*cf).datablocks[(*cf).curDatablock].curCategory+1;
  for (k= (*cf).datablocks[datablockId].categories[categoryId-1].allRow-1; k>=0; k--) {
    if ((*cf).datablocks[datablockId].categories[categoryId-1].rows[k].columns != NULL) {
      for (l= (*cf).datablocks[datablockId].categories[categoryId-1].allCol-1; l>=0; l--) {
		  if ((*cf).datablocks[datablockId].categories[categoryId-1].rows[k].columns[l] != NULL)
			 free((*cf).datablocks[datablockId].categories[categoryId-1].rows[k].columns[l]);
		  (*cf).datablocks[datablockId].categories[categoryId-1].rows[k].columns[l] = NULL;
      }
      free((*cf).datablocks[datablockId].categories[categoryId-1].rows[k].columns);
      (*cf).datablocks[datablockId].categories[categoryId-1].rows[k].columns = NULL;
    }
  }
  free((*cf).datablocks[datablockId].categories[categoryId-1].rows);
  (*cf).datablocks[datablockId].categories[categoryId-1].rows = NULL;
  
  for (l= (*cf).datablocks[datablockId].categories[categoryId-1].allCol-1; l>=0; l--) {
    if ((*cf).datablocks[datablockId].categories[categoryId-1].colNames[l] != NULL)
      free((*cf).datablocks[datablockId].categories[categoryId-1].colNames[l]);
    (*cf).datablocks[datablockId].categories[categoryId-1].colNames[l] = NULL;
  }
  free((*cf).datablocks[datablockId].categories[categoryId-1].colNames);
  (*cf).datablocks[datablockId].categories[categoryId-1].colNames = NULL;
  
  memcpy(&(*cf).datablocks[(*cf).curDatablock].categories[categoryId-1],
			&(*cf).datablocks[(*cf).curDatablock].categories[categoryId],
			sizeof(NdbCifCategoryFormat) * 
			((*cf).datablocks[(*cf).curDatablock].numCategory - categoryId+1));
  
  (*cf).datablocks[(*cf).curDatablock].numCategory --;
  
  if ((*cf).datablocks[(*cf).curDatablock].numCategory == 
      (*cf).datablocks[(*cf).curDatablock].curCategory)
    (*cf).datablocks[(*cf).curDatablock].curCategory--;
  
  return (*cf).datablocks[(*cf).curDatablock].curCategory +1;
}

int ndb_cif_remove_category_by_id(CifHandle *cf,const int datablockId, const int categoryId)
{
  int k,l;
  if (datablockId <=0 || categoryId <=0 ||
      datablockId > (*cf).numDatablock ||
      categoryId > (*cf).datablocks[(*cf).curDatablock].numCategory) 
    return 0;

  if (datablockId == (*cf).curDatablock &&
      categoryId  == (*cf).datablocks[(*cf).curDatablock].curCategory)
    return ndb_cif_remove_category(cf);

  for (k= (*cf).datablocks[datablockId-1].categories[categoryId-1].allRow-1; k>=0; k--) {
    if ((*cf).datablocks[datablockId-1].categories[categoryId-1].rows[k].columns != NULL) {
      for (l= (*cf).datablocks[datablockId-1].categories[categoryId-1].allCol-1; l>=0; l--) {
		  if ((*cf).datablocks[datablockId-1].categories[categoryId-1].rows[k].columns[l] != NULL)
			 free((*cf).datablocks[datablockId-1].categories[categoryId-1].rows[k].columns[l]);
		  (*cf).datablocks[datablockId-1].categories[categoryId-1].rows[k].columns[l] = NULL;
      }
      free((*cf).datablocks[datablockId-1].categories[categoryId-1].rows[k].columns);
      (*cf).datablocks[datablockId-1].categories[categoryId-1].rows[k].columns = NULL;
    }
  }
  free((*cf).datablocks[datablockId-1].categories[categoryId-1].rows);
  (*cf).datablocks[datablockId-1].categories[categoryId-1].rows = NULL;
  
  for (l= (*cf).datablocks[datablockId-1].categories[categoryId-1].allCol-1; l>=0; l--) {
    if ((*cf).datablocks[datablockId-1].categories[categoryId-1].colNames[l] != NULL)
      free((*cf).datablocks[datablockId-1].categories[categoryId-1].colNames[l]);
    (*cf).datablocks[datablockId-1].categories[categoryId-1].colNames[l] = NULL;
  }
  free((*cf).datablocks[datablockId-1].categories[categoryId-1].colNames);
  (*cf).datablocks[datablockId-1].categories[categoryId-1].colNames = NULL;
  
  memcpy(&(*cf).datablocks[(*cf).curDatablock].
			categories[categoryId-1],
			&(*cf).datablocks[(*cf).curDatablock].
			categories[categoryId],
			sizeof(NdbCifCategoryFormat) * 
			((*cf).datablocks[(*cf).curDatablock].numCategory - categoryId));
  (*cf).datablocks[(*cf).curDatablock].numCategory --;
  return (*cf).datablocks[(*cf).curDatablock].curCategory +1;
}

int ndb_cif_remove_category_by_name(CifHandle *cf,const char *datablockName, const char *categoryName)
{
  int datablockId, categoryId;
  
  datablockId = ndb_cif_get_datablock_id(cf,datablockName);
  if (datablockId == 0) 
    return 0;
  categoryId = ndb_cif_get_category_id(cf,datablockName, categoryName);
  if (categoryId == 0) return 0;
  else return ndb_cif_remove_category_by_id(cf,datablockId, categoryId);
}

int ndb_cif_remove_row(CifHandle *cf)
{
  int datablockId, categoryId, rowId;

  if ((*cf).numDatablock <= 0 ||
      (*cf).curDatablock < 0 ||
      (*cf).curDatablock >= (*cf).numDatablock) 
    return 0;
  datablockId = (*cf).curDatablock;

  if ((*cf).datablocks[datablockId].numCategory <=0 ||
      (*cf).datablocks[datablockId].curCategory < 0 ||
      (*cf).datablocks[datablockId].curCategory >=
      (*cf).datablocks[datablockId].numCategory )
    return 0;

  categoryId  = (*cf).datablocks[datablockId].curCategory;
  rowId = (*cf).datablocks[datablockId].categories[categoryId].curRow; 
    
  return ndb_cif_remove_row_by_id(cf,datablockId+1, categoryId+1, rowId+1);
				 
}

int ndb_cif_remove_row_by_id(CifHandle *cf,const int datablockId, const int categoryId, const int rowId)
{

  NdbCifCategoryFormat  *pCategory;
  int i, j;

  if (datablockId <=0 || categoryId <=0 || rowId <=0 ||
      (*cf).numDatablock < datablockId ||
      (*cf).datablocks[datablockId-1].numCategory < categoryId ||
      (*cf).datablocks[datablockId-1].categories[categoryId-1].numRow < rowId)
    return 0;
  
  pCategory = &(*cf).datablocks[datablockId-1].categories[categoryId-1];

  
  /* 
   * Move the new allocated to correct space
   */
  for (i= 0; i < pCategory->numCol; i++)
    if (pCategory->rows[rowId-1].columns[i] != NULL) {
      free(pCategory->rows[rowId-1].columns[i]);
      pCategory->rows[rowId-1].columns[i] = NULL;
    }
  
  for (i= rowId-1; i < pCategory->numRow-1; i++) {
    for (j=0 ; j< pCategory->numCol; j++) 
      pCategory->rows[i].columns[j] = pCategory->rows[i+1].columns[j];
  }
  for (j=0 ; j< pCategory->numCol; j++) 
    pCategory->rows[pCategory->numRow-1].columns[j] = NULL;
  pCategory->numRow--;
  if (pCategory->curRow == pCategory->numRow)
    pCategory->curRow --;
  return pCategory->curRow+1;
}



int ndb_cif_next_datablock(CifHandle *cf)
{
  if ((*cf).curDatablock < (*cf).numDatablock-1) {
    (*cf).curDatablock++;
    (*cf).datablocks[(*cf).curDatablock].curCategory = 0;
    ndb_cif_rewind_category(cf);
    return (*cf).curDatablock+1;  
  }  else 
    return 0;
}

int ndb_cif_next_category(CifHandle *cf)
{
  if ((*cf).datablocks == NULL) return 0;
  
  if ((*cf).datablocks[(*cf).curDatablock].curCategory < 
      (*cf).datablocks[(*cf).curDatablock].numCategory-1) {

    (*cf).datablocks[(*cf).curDatablock].curCategory++;
    ndb_cif_rewind_row(cf);
    return (*cf).datablocks[(*cf).curDatablock].curCategory+1;
  }  else 
    return 0;
}

int ndb_cif_next_row(CifHandle *cf)
{
  NdbCifCategoryFormat *pCategory;
  int curCategory;

  if ((*cf).numDatablock == 0)  return 0;
  
  if ((*cf).datablocks[(*cf).curDatablock].curCategory >=
      (*cf).datablocks[(*cf).curDatablock].numCategory) 
    return 0;

  curCategory = (*cf).datablocks[(*cf).curDatablock].curCategory;
  pCategory = &(*cf).datablocks[(*cf).curDatablock].categories[curCategory];

  if (pCategory->curRow >= 0 && pCategory->curRow < pCategory->numRow - 1) {
    ++pCategory->curRow;
    return pCategory->curRow+1;
  }
  else return 0;
}

int ndb_cif_next_column(CifHandle *cf)
{
  int curCategory;
  if ((*cf).datablocks == NULL)
    return 0;
  
  if ((*cf).datablocks[(*cf).curDatablock].curCategory >=
      (*cf).datablocks[(*cf).curDatablock].numCategory) 
    return 0;
  curCategory = (*cf).datablocks[(*cf).curDatablock].curCategory;

  if ((*cf).datablocks[(*cf).curDatablock].categories[curCategory].curCol <
      (*cf).datablocks[(*cf).curDatablock].categories[curCategory].numCol-1) {
    (*cf).datablocks[(*cf).curDatablock].categories[curCategory].curCol++;
    return (*cf).datablocks[(*cf).curDatablock].categories[curCategory].curCol+1;
  }
  else return 0;
}

int ndb_cif_move_datablock(CifHandle *cf,const char *datablockName)
{
  int i;
  if ((*cf).datablocks == NULL)
    return 0;
		 
  for (i=0; i< (*cf).numDatablock; i++)
    if (!STRCMP((*cf).datablocks[i].datablockName,datablockName)) break;
  
  if (i != (*cf).numDatablock) {
    (*cf).curDatablock = i;
    return i+1;
  }
  else 
    return 0;
}

int ndb_cif_move_datablock_by_id(CifHandle *cf,const int datablockId)
{

  if (datablockId < 0 ||
      datablockId > (*cf).numDatablock)
    return 0;
		 
  (*cf).curDatablock = datablockId-1;
  return datablockId;
}

int ndb_cif_move_category_by_name(CifHandle *cf,const char *datablockName, const char *categoryName)
{
  int i, curDatablock;

  if ((curDatablock = ndb_cif_move_datablock(cf,datablockName)) == 0) 
    return 0;

  for (i=0; i< (*cf).datablocks[curDatablock-1].numCategory; i++)
    if (!STRCMP((*cf).datablocks[curDatablock-1].categories[i].categoryName,
		categoryName)) break;
  
  if (i != (*cf).datablocks[curDatablock-1].numCategory) {
    (*cf).datablocks[(*cf).curDatablock].curCategory = i;
    return i+1;
  }
  else 
    return 0;
}

int ndb_cif_move_category_by_id(CifHandle *cf,const int datablockId, const int categoryId)
{
  int  curDatablock;

  if ((curDatablock = ndb_cif_move_datablock_by_id(cf,datablockId)) == 0) 
    return 0;

  if (categoryId <0 || categoryId > (*cf).datablocks[curDatablock-1].numCategory)
    return 0;


  (*cf).datablocks[curDatablock-1].curCategory = categoryId-1;
  return categoryId;

}

int ndb_cif_move_category(CifHandle *cf,const int categoryId)
{

  if (categoryId <0 || categoryId > (*cf).datablocks[(*cf).curDatablock].numCategory)
    return 0;


  (*cf).datablocks[(*cf).curDatablock].curCategory = categoryId-1;
  return categoryId;

}

int ndb_cif_move_row_by_name(CifHandle *cf,const char *datablockName, const char *categoryName, 
			     const int rowId)
{
  int curCategory, curDatablock;

  if (curCategory = ndb_cif_move_category_by_name(cf,datablockName, categoryName) == 0) 
    return 0;
  curDatablock = (*cf).curDatablock;
  
  if (rowId < 0 ||
      rowId > (*cf).datablocks[curDatablock].categories[curCategory].numRow)
    return 0;

  (*cf).datablocks[curDatablock].categories[curCategory].curRow = rowId-1;
  return rowId+1;
}

int ndb_cif_move_row_by_id(CifHandle *cf,const int datablockId, const int categoryId, const int rowId)
{
  if (datablockId <=0 || categoryId <=0 || rowId <=0 ||
      ndb_cif_move_category_by_id(cf,datablockId, categoryId) == 0) 
    return 0;

  if (rowId <0 || 
      rowId > (*cf).datablocks[datablockId-1].categories[categoryId-1].numRow)
    return 0;

  (*cf).datablocks[datablockId-1].categories[categoryId-1].curRow = rowId -1;
  return rowId;

}

int ndb_cif_move_row(CifHandle *cf,const int rowId)
{
  int  curDatablock, curCategory;

  curDatablock = (*cf).curDatablock;
  curCategory= (*cf).datablocks[curDatablock].curCategory;

  return  ndb_cif_move_row_by_id(cf,curDatablock+1, curCategory+1, rowId);

}

int ndb_cif_count_datablock(CifHandle *cf)
{
  return (*cf).numDatablock;
}

int ndb_cif_count_category(CifHandle *cf)
{
  if ((*cf).curDatablock >= (*cf).numDatablock)
    return 0;

  return (*cf).datablocks[(*cf).curDatablock].numCategory;
}


int ndb_cif_count_row(CifHandle *cf)
{
  int curDatablock, curCategory;

  if ((*cf).curDatablock >= (*cf).numDatablock ||
      (*cf).datablocks[(*cf).curDatablock].curCategory >=
      (*cf).datablocks[(*cf).curDatablock].numCategory )
    return 0;

  curDatablock = (*cf).curDatablock;
  curCategory  = (*cf).datablocks[(*cf).curDatablock].curCategory;
  return (*cf).datablocks[curDatablock].categories[curCategory].numRow;
}


int ndb_cif_count_column(CifHandle *cf)
{
  int curDatablock, curCategory;

  if ((*cf).curDatablock < 0 ||
      (*cf).curDatablock >= (*cf).numDatablock ||
      (*cf).datablocks[(*cf).curDatablock].curCategory < 0 ||
      (*cf).datablocks[(*cf).curDatablock].curCategory >=
      (*cf).datablocks[(*cf).curDatablock].numCategory )
    return 0;

  curDatablock = (*cf).curDatablock;
  curCategory  = (*cf).datablocks[(*cf).curDatablock].curCategory;
  return (*cf).datablocks[curDatablock].categories[curCategory].numCol;
}


int ndb_cif_current_datablock(CifHandle *cf)
{
  return (*cf).curDatablock+1;
}

int ndb_cif_current_datablock_name(CifHandle *cf,char *datablockName)
{
  if ((*cf).curDatablock < 0 ||
      (*cf).curDatablock >= (*cf).numDatablock)
    return 0;
  strcpy(datablockName, (*cf).datablocks[(*cf).curDatablock].datablockName);
  return (*cf).curDatablock+1;
}

int ndb_cif_current_category(CifHandle *cf)
{
  if ((*cf).curDatablock >= (*cf).numDatablock)
    return 0;

  return (*cf).datablocks[(*cf).curDatablock].curCategory+1;
}

int ndb_cif_current_category_name(CifHandle *cf,char *categoryName)
{
  if ((*cf).curDatablock >= (*cf).numDatablock)
    return 0;

  strcpy(categoryName, (*cf).datablocks[(*cf).curDatablock].categories
	 [(*cf).datablocks[(*cf).curDatablock].curCategory].categoryName);
  return (*cf).datablocks[(*cf).curDatablock].curCategory+1;
}

int ndb_cif_current_row(CifHandle *cf)
{

  int curDatablock, curCategory;

  if ((*cf).curDatablock >= (*cf).numDatablock ||
      (*cf).datablocks[(*cf).curDatablock].curCategory >=
      (*cf).datablocks[(*cf).curDatablock].numCategory )
    return 0;

  curDatablock = (*cf).curDatablock;
  curCategory  = (*cf).datablocks[(*cf).curDatablock].curCategory;
  return (*cf).datablocks[curDatablock].categories[curCategory].curRow+1;
}

int ndb_cif_current_col(CifHandle *cf)
{
  int curDatablock, curCategory;

  if ((*cf).curDatablock >= (*cf).numDatablock ||
      (*cf).datablocks[(*cf).curDatablock].curCategory >=
      (*cf).datablocks[(*cf).curDatablock].numCategory )
    return 0;

  curDatablock = (*cf).curDatablock;
  curCategory  = (*cf).datablocks[(*cf).curDatablock].curCategory;
  return (*cf).datablocks[curDatablock].categories[curCategory].curCol+1;
}

int ndb_cif_put_item_keyword(CifHandle *cf,const char *itemKeyword)
{ 
  NdbCifCategoryFormat *pCategory;
  int curDatablock, curCategory, i, j;

  if ((*cf).curDatablock >= (*cf).numDatablock ||
      (*cf).datablocks[(*cf).curDatablock].curCategory >=
      (*cf).datablocks[(*cf).curDatablock].numCategory )
    return 0;

  curDatablock = (*cf).curDatablock;
  curCategory  = (*cf).datablocks[curDatablock].curCategory;
  pCategory  = &(*cf).datablocks[curDatablock].categories[curCategory];

  if (pCategory->numCol == pCategory->allCol) {
    pCategory->allCol *=2;
    pCategory->colNames = (char **) 
      realloc(pCategory->colNames, pCategory->allCol* sizeof(char *));
    for (i=pCategory->numCol; i<pCategory->allCol; i++)
      pCategory->colNames[i] = NULL;
    for (i=0; i<pCategory->allRow; i++) {
      pCategory->rows[i].columns = (char **)
	realloc(pCategory->rows[i].columns, pCategory->allCol * sizeof(char**));
      for (j=pCategory->numCol; j<pCategory->allCol; j++) {
	pCategory->rows[i].columns[j] = NULL;
      }
    }
  }
  pCategory->curCol = pCategory->numCol;
  pCategory->numCol++;
      
  pCategory->colNames[pCategory->curCol] = (char *)
    realloc(pCategory->colNames[pCategory->curCol], 
	    sizeof(char) * (strlen(itemKeyword)+1));
  strcpy(pCategory->colNames[pCategory->curCol], itemKeyword);
  return pCategory->curCol+1;
}

int ndb_cif_remove_item_keyword
(CifHandle *cf,const char *itemKeyword)     
/* Given the name of a column, remove the keyword and all of
   the values from the current category.
   */     
{ 
  NdbCifCategoryFormat *pCategory;
  int curDatablock, curCategory, i, j;
  int ind;
  
  if ((*cf).curDatablock >= (*cf).numDatablock ||
      (*cf).datablocks[(*cf).curDatablock].curCategory >=
      (*cf).datablocks[(*cf).curDatablock].numCategory )
    return 0;

  curDatablock = (*cf).curDatablock;
  curCategory  = (*cf).datablocks[curDatablock].curCategory;
  pCategory  = &(*cf).datablocks[curDatablock].categories[curCategory];

  for (i = 0; i < 	pCategory->numCol; i++)
    if (!STRCMP(pCategory->colNames[i], itemKeyword)) break;
  if (i == pCategory->numCol)
    return 0;

  ind = i;
  free(pCategory->colNames[ind]);
  for (j = ind; j < pCategory->numCol - 1; j++)
    pCategory->colNames[j] = pCategory->colNames[j + 1]; 
  pCategory->colNames[pCategory->numCol - 1] = NULL;
  
  for (i=0; i<pCategory->numRow; i++) 
    {
      for (j = ind; j < pCategory->numCol - 1; j++) 
	pCategory->rows[i].columns[j] = pCategory->rows[i].columns[j + 1];
      pCategory->rows[i].columns[pCategory->numCol - 1] = NULL;
    }
  return pCategory->numCol--; 
}


int ndb_cif_get_item_name(CifHandle *cf,const int colId, char *itemName)
{
  int curDatablock, curCategory;
  NdbCifCategoryFormat *pCategory;
  if ((*cf).curDatablock >= (*cf).numDatablock ||
      (*cf).datablocks[(*cf).curDatablock].curCategory >=
      (*cf).datablocks[(*cf).curDatablock].numCategory)
    return 0;
  curDatablock = (*cf).curDatablock;
  curCategory  = (*cf).datablocks[(*cf).curDatablock].curCategory;

  if (colId > (*cf).datablocks[curDatablock].categories[curCategory].numCol ||
      (*cf).datablocks[curDatablock].categories[curCategory].curRow >=
      (*cf).datablocks[curDatablock].categories[curCategory].numRow)
    return 0;

  pCategory = & (*cf).datablocks[curDatablock].categories[curCategory];
  sprintf(itemName, "_%s.%s", pCategory->categoryName, 
	  pCategory->colNames[colId-1]);
  return TRUE;
}

int ndb_cif_get_item_shname(CifHandle *cf,const int colId, char *itemName)
     /* Return the name minus the category name (the short name. 
      */     
{
  int curDatablock, curCategory;
  NdbCifCategoryFormat *pCategory;
  if ((*cf).curDatablock >= (*cf).numDatablock ||
      (*cf).datablocks[(*cf).curDatablock].curCategory >=
      (*cf).datablocks[(*cf).curDatablock].numCategory)
    return 0;
  curDatablock = (*cf).curDatablock;
  curCategory  = (*cf).datablocks[(*cf).curDatablock].curCategory;

  if (colId > (*cf).datablocks[curDatablock].categories[curCategory].numCol ||
      (*cf).datablocks[curDatablock].categories[curCategory].curRow >=
      (*cf).datablocks[curDatablock].categories[curCategory].numRow)
    return 0;

  pCategory = & (*cf).datablocks[curDatablock].categories[curCategory];
  sprintf(itemName, "%s", pCategory->colNames[colId-1]);
  return TRUE;
}

int ndb_cif_get_item_value(CifHandle *cf,const int colId, char *fieldValue, const int maxFieldLen)
{
  NdbCifRowFormat *pRow;
  int curDatablock, curCategory, curRow;

  fieldValue[0] = 0;
  if ((*cf).curDatablock >= (*cf).numDatablock ||
      (*cf).datablocks[(*cf).curDatablock].curCategory >=
      (*cf).datablocks[(*cf).curDatablock].numCategory)
    return 0;
  curDatablock = (*cf).curDatablock;
  curCategory  = (*cf).datablocks[(*cf).curDatablock].curCategory;
  if (colId > (*cf).datablocks[curDatablock].categories[curCategory].numCol ||
      (*cf).datablocks[curDatablock].categories[curCategory].numRow == 0 ||
      (*cf).datablocks[curDatablock].categories[curCategory].curRow >=
      (*cf).datablocks[curDatablock].categories[curCategory].numRow)
    return 0;

  curRow  = (*cf).datablocks[(*cf).curDatablock].categories[curCategory].curRow;
  if (curRow == -1) return 0;
  pRow = &(*cf).datablocks[curDatablock].categories[curCategory].rows[curRow];

  if (pRow->columns[colId-1] != NULL) 
    strncpy(fieldValue, pRow->columns[colId-1], maxFieldLen-1);
  fieldValue[maxFieldLen-1] = 0;
  return TRUE;
}

int ndb_cif_get_item_value_length(CifHandle *cf,const int colId)
{
  NdbCifRowFormat *pRow;
  int ilen,curDatablock, curCategory, curRow;
  ilen=0;
  if ((*cf).curDatablock >= (*cf).numDatablock ||
      (*cf).datablocks[(*cf).curDatablock].curCategory >=
      (*cf).datablocks[(*cf).curDatablock].numCategory)
    return 0;
  curDatablock = (*cf).curDatablock;
  curCategory  = (*cf).datablocks[(*cf).curDatablock].curCategory;
  if (colId > (*cf).datablocks[curDatablock].categories[curCategory].numCol ||
      (*cf).datablocks[curDatablock].categories[curCategory].numRow == 0 ||
      (*cf).datablocks[curDatablock].categories[curCategory].curRow >=
      (*cf).datablocks[curDatablock].categories[curCategory].numRow)
    return 0;

  curRow  = (*cf).datablocks[(*cf).curDatablock].categories[curCategory].curRow;
  if (curRow == -1) return 0;
  pRow = &(*cf).datablocks[curDatablock].categories[curCategory].rows[curRow];
  if (pRow->columns[colId-1] != NULL) ilen= strlen(pRow->columns[colId-1]);
  return ilen;
}


char *ndb_cif_copy_item_value(CifHandle *cf,const int colId)
  /* In the current row,return a newly allocated string that
     contains a copy of the valule in  colID.
   */
{
 NdbCifRowFormat *pRow;
 int len, curDatablock, curCategory, curRow;
 char *ret = NULL;
 
 if ((*cf).curDatablock >= (*cf).numDatablock ||
     (*cf).datablocks[(*cf).curDatablock].curCategory >=
     (*cf).datablocks[(*cf).curDatablock].numCategory)
   return NULL;
 curDatablock = (*cf).curDatablock;
 curCategory  = (*cf).datablocks[(*cf).curDatablock].curCategory;
 if (colId > (*cf).datablocks[curDatablock].categories[curCategory].numCol ||
     (*cf).datablocks[curDatablock].categories[curCategory].numRow == 0 ||
     (*cf).datablocks[curDatablock].categories[curCategory].curRow >=
     (*cf).datablocks[curDatablock].categories[curCategory].numRow)
   return NULL;
 
 curRow  = (*cf).datablocks[(*cf).curDatablock].categories[curCategory].curRow;
 if (curRow == -1) return NULL;
 pRow = &(*cf).datablocks[curDatablock].categories[curCategory].rows[curRow];
 
 if (pRow->columns[colId-1] != NULL) 
   {
    len = strlen(pRow->columns[colId-1]);
    ret = (char *) calloc(len + 1, sizeof(char));
    strcpy(ret, pRow->columns[colId-1]);
    return ret;
   }
 return NULL;
}


int ndb_cif_output_item(CifHandle *cf,FILE *fp, const int colId)
  /* In the current row, output the value in colID to port fp.
   */
{
  int i;
 NdbCifRowFormat *pRow;
 int curDatablock, curCategory, curRow;
 
 if ((*cf).curDatablock >= (*cf).numDatablock ||
     (*cf).datablocks[(*cf).curDatablock].curCategory >=
     (*cf).datablocks[(*cf).curDatablock].numCategory)
   return 0;
 curDatablock = (*cf).curDatablock;
 curCategory  = (*cf).datablocks[(*cf).curDatablock].curCategory;
 if (colId > (*cf).datablocks[curDatablock].categories[curCategory].numCol ||
     (*cf).datablocks[curDatablock].categories[curCategory].numRow == 0 ||
     (*cf).datablocks[curDatablock].categories[curCategory].curRow >=
     (*cf).datablocks[curDatablock].categories[curCategory].numRow)
   return 0;
 
 curRow  = (*cf).datablocks[(*cf).curDatablock].categories[curCategory].curRow;
 if (curRow == -1) return 0;
 pRow = &(*cf).datablocks[curDatablock].categories[curCategory].rows[curRow];
 
 if (pRow->columns[colId-1] != NULL) 
   {
    i = 0;
    while (pRow->columns[colId-1][i] != '\0')
      fputc(pRow->columns[colId-1][i++], fp);
    return TRUE;
   }
 else 
   return 0;
}

int ndb_cif_item_value_strncmp(CifHandle *cf,const int catId, const int colId, 
										 const int rowId,const int startPos,
										 char *fieldValue, const int maxFieldLen)
{
  NdbCifRowFormat *pRow;
  int curDatablock;
  
  if ((*cf).curDatablock >= (*cf).numDatablock ||
		(*cf).datablocks[(*cf).curDatablock].curCategory >=
		(*cf).datablocks[(*cf).curDatablock].numCategory)
	 return -1;
  curDatablock = (*cf).curDatablock;
 
  if (catId <= 0 || catId > (*cf).datablocks[curDatablock].numCategory)
	 return -1;
  if (colId <= 0 ||
     colId > (*cf).datablocks[curDatablock].categories[catId-1].numCol ||
		rowId <= 0 ||
		rowId > (*cf).datablocks[curDatablock].categories[catId-1].numRow )
	 return -1;
  
 pRow = &(*cf).datablocks[curDatablock].categories[catId-1].rows[rowId-1];
 
 if (pRow->columns[colId-1] != NULL) 
   return (strncmp(fieldValue, &pRow->columns[colId-1][startPos-1], maxFieldLen));
 return(-1);
}

int ndb_cif_item_values_strncmp(CifHandle *cf,const int catId1, const int colId1, 
										  const int rowId1, const int startPos1, 
										  const int catId2, const int colId2,  
										  const int rowId2,  const int startPos2,
										  const int maxFieldLen)
{
  NdbCifRowFormat *pRow1, *pRow2;
  int curDatablock;
  
  if ((*cf).curDatablock >= (*cf).numDatablock ||
      (*cf).datablocks[(*cf).curDatablock].curCategory >=
      (*cf).datablocks[(*cf).curDatablock].numCategory)
    return -1;
  curDatablock = (*cf).curDatablock;
  
  if (catId1 <= 0 || catId1 > (*cf).datablocks[curDatablock].numCategory ||
      catId2 <= 0 || catId2 > (*cf).datablocks[curDatablock].numCategory )
    return -1;
  if (colId1 <= 0 ||
      colId1 > (*cf).datablocks[curDatablock].categories[catId1-1].numCol ||
      rowId1 <= 0 ||
      rowId1 > (*cf).datablocks[curDatablock].categories[catId1-1].numRow ||
      colId2 <= 0 ||
      colId2 > (*cf).datablocks[curDatablock].categories[catId2-1].numCol ||
      rowId2 <= 0 ||
      rowId2 > (*cf).datablocks[curDatablock].categories[catId2-1].numRow)
    return -1;

  pRow1 = &(*cf).datablocks[curDatablock].categories[catId1-1].rows[rowId1-1];
  pRow2 = &(*cf).datablocks[curDatablock].categories[catId2-1].rows[rowId2-1];
  
  if (!pRow1->columns || !pRow2->columns) return -1;
  if (!pRow1->columns[colId1-1] || !pRow2->columns[colId2-1]) return -1;
  return strncmp(&pRow1->columns[colId1-1][startPos1-1],
		 &pRow2->columns[colId2-1][startPos2-1], maxFieldLen);
}

/*
   The following two functions are like ndb_cif_item_values_strncmp
   but return 0 or i if the string(s) are found in the given 
   columns of the given row,using strcmp for comparison, and not specifying
   the starting column of the string searched against.
   */

int ndb_cif_item_row_1_key(CifHandle *cf,const int colId, char *fieldValue)
  /* In the current category, examine all the rows and return the row number if fieldValue
     is found, and also also set the current row to i. If the row is not found return 0.
  */
{
  int i;
  int curDatablock, curCategory;
  NdbCifRowFormat *pRow;
  NdbCifCategoryFormat *pCategory;

  if ((*cf).numDatablock == 0)  return 0;
  if ((*cf).curDatablock >= (*cf).numDatablock ||
		((*cf).datablocks[(*cf).curDatablock].curCategory >=
		 (*cf).datablocks[(*cf).curDatablock].numCategory))
	 return 0;
  
  curDatablock = (*cf).curDatablock;
 curCategory = (*cf).datablocks[curDatablock].curCategory;
 pCategory = &(*cf).datablocks[curDatablock].categories[curCategory];
 
 if (colId <= 0 ||
     colId >  pCategory->numCol)
   return 0;
 
 for (i = 0; i < pCategory->numRow; i++)
   {
	  pRow = &pCategory->rows[i];
	  if ((pRow->columns[colId-1] != NULL) 
			&& !STRCMPV(fieldValue, pRow->columns[colId-1]))
		 {
			pCategory->curRow = i;    
			return i + 1;
		 }
   }
 return 0;
}

int ndb_cif_item_row_2_keys(CifHandle *cf,const int colId1, 
									 const char *fieldValue1, const int colId2, 
									 const char *fieldValue2)
  /* In the current category, examine all the rows and return the row number if fieldValue1 
     and 2 are found in the respecive columns of row i, and also also set the current
     row to i. If the row is not found, return 0.
  */
{
  int i;
  int curDatablock, curCategory;
  NdbCifRowFormat *pRow;
  NdbCifCategoryFormat *pCategory;
  
  if ((*cf).numDatablock == 0)  return 0;
  if ((*cf).curDatablock >= (*cf).numDatablock ||
		((*cf).datablocks[(*cf).curDatablock].curCategory >=
		 (*cf).datablocks[(*cf).curDatablock].numCategory))
   return 0;
  
  curDatablock = (*cf).curDatablock;
  curCategory = (*cf).datablocks[curDatablock].curCategory;
  pCategory = &(*cf).datablocks[curDatablock].categories[curCategory];
 
  if ((colId1 <= 0) || (colId1 >  pCategory->numCol) || 
		(colId2 <= 0) || (colId2 >  pCategory->numCol))
	 return 0;
  
  for (i = 0; i < pCategory->numRow; i++)
	 {
		pRow = &pCategory->rows[i];
		if ((pRow->columns[colId1-1] != NULL) && (pRow->columns[colId2-1] != NULL))
		  if (!STRCMPV(fieldValue1, pRow->columns[colId1-1]))
			 if (!STRCMPV(fieldValue2, pRow->columns[colId2-1]))
				{
				  pCategory->curRow = i;    
				  return i + 1;
				}
	 }
  return 0;
}

int ndb_cif_item_row_3_keys(CifHandle *cf,const int colId1, const char *fieldValue1, 
									 const int colId2, const char *fieldValue2,
									 const int colId3, const char *fieldValue3)
  /* In the current category, examine all the rows and return the row number if fieldValue1 
     2 and 3 are found in the respective columns of row i, and also also set the current
     row to i. If the row is not found, return 0.
  */
{
  int i;
  int curDatablock, curCategory;
  NdbCifRowFormat *pRow;
  NdbCifCategoryFormat *pCategory;
  
  if ((*cf).numDatablock == 0)  return 0;
  if ((*cf).curDatablock >= (*cf).numDatablock ||
		((*cf).datablocks[(*cf).curDatablock].curCategory >=
		 (*cf).datablocks[(*cf).curDatablock].numCategory))
	 return 0;
  
  curDatablock = (*cf).curDatablock;
  curCategory = (*cf).datablocks[curDatablock].curCategory;
  pCategory = &(*cf).datablocks[curDatablock].categories[curCategory];
  
  if ((colId1 <= 0) || (colId1 >  pCategory->numCol) || 
		(colId2 <= 0) || (colId2 >  pCategory->numCol) ||
		(colId3 <= 0) || (colId3 >  pCategory->numCol))
	 return 0;
  
  for (i = 0; i < pCategory->numRow; i++)
	 {
		pRow = &pCategory->rows[i];
		if ((pRow->columns[colId1-1] != NULL) && (pRow->columns[colId2-1] != NULL))
		  if (!STRCMPV(fieldValue1, pRow->columns[colId1-1]))
			 if (!STRCMPV(fieldValue2, pRow->columns[colId2-1]))
				if (!STRCMPV(fieldValue3, pRow->columns[colId3-1]))
				  {
					 pCategory->curRow = i;    
					 return i + 1;
				  }
	 }
  return 0;
}

int ndb_cif_item_row_4_keys(CifHandle *cf,const int colId1, const char *fieldValue1, 
									 const int colId2, const char *fieldValue2,
									 const int colId3, const char *fieldValue3,
									 const int colId4, const char *fieldValue4)
  /* In the current category, examine all the rows and return the row number if fieldValue1 
     2, 3 and 4 are found in the respective columns of row i, and also also set the current
     row to i. If the row is not found, return 0.
  */
{
  int i;
  int curDatablock, curCategory;
  NdbCifRowFormat *pRow;
  NdbCifCategoryFormat *pCategory;
  
  if ((*cf).numDatablock == 0)  return 0;
  if ((*cf).curDatablock >= (*cf).numDatablock ||
		((*cf).datablocks[(*cf).curDatablock].curCategory >=
		 (*cf).datablocks[(*cf).curDatablock].numCategory))
	 return 0;
  
  curDatablock = (*cf).curDatablock;
  curCategory = (*cf).datablocks[curDatablock].curCategory;
  pCategory = &(*cf).datablocks[curDatablock].categories[curCategory];
  
  if ((colId1 <= 0) || (colId1 >  pCategory->numCol) || 
		(colId2 <= 0) || (colId2 >  pCategory->numCol) ||
		(colId3 <= 0) || (colId3 >  pCategory->numCol) ||
		(colId4 <= 0) || (colId4 >  pCategory->numCol))
	 return 0;
  
  for (i = 0; i < pCategory->numRow; i++)
	 {
		pRow = &pCategory->rows[i];
		if ((pRow->columns[colId1-1] != NULL) && (pRow->columns[colId2-1] != NULL))
		  if (!STRCMPV(fieldValue1, pRow->columns[colId1-1]))
			 if (!STRCMPV(fieldValue2, pRow->columns[colId2-1]))
				if (!STRCMPV(fieldValue3, pRow->columns[colId3-1]))
				  if (!STRCMPV(fieldValue4, pRow->columns[colId4-1]))
					 {
						pCategory->curRow = i;    
						return i + 1;
					 }
	 }
  return 0;
}

int ndb_cif_put_item_value(CifHandle *cf,const int colId, const char *fieldValue)
{
  NdbCifRowFormat *pRow;
  int curDatablock, curCategory, curRow, str_len;
  if (colId <= 0 ){
    fprintf(stderr, "Error: index of %d passed to ndb_cif_put_item_value.\n",
				colId);
    exit(1);
  }
  if ((*cf).curDatablock< 0 ||
      (*cf).curDatablock >= (*cf).numDatablock ||
      (*cf).datablocks[(*cf).curDatablock].curCategory <0 ||
      (*cf).datablocks[(*cf).curDatablock].curCategory >=
      (*cf).datablocks[(*cf).curDatablock].numCategory)
    return 0;
  curDatablock = (*cf).curDatablock;
  curCategory  = (*cf).datablocks[(*cf).curDatablock].curCategory;
  if (colId > (*cf).datablocks[curDatablock].categories[curCategory].numCol ||
      (*cf).datablocks[curDatablock].categories[curCategory].curRow >=
      (*cf).datablocks[curDatablock].categories[curCategory].numRow)
    return 0;
  
  curRow  = (*cf).datablocks[(*cf).curDatablock].categories[curCategory].curRow;
  if (curRow == -1) {
    curRow = ndb_cif_new_row(cf);
    curRow --;
  }
  pRow = &(*cf).datablocks[curDatablock].categories[curCategory].rows[curRow];
  
  str_len = strlen(fieldValue);
  if (pRow->columns[colId-1] == NULL || str_len > strlen(pRow->columns[colId-1]))
    pRow->columns[colId-1] = (char *)
      realloc( pRow->columns[colId-1], (str_len+1)*sizeof(char));
  strcpy(pRow->columns[colId-1], fieldValue);
  return TRUE;
}

int ndb_cif_get_category_name_from_item_name(CifHandle *cf,char *categoryName, const char *itemName)
{
  int i, str_len;
  
  str_len = strlen(itemName);
  for (i=0; i<str_len; i++)
	 if (itemName[i] == '.') break;
  if (i == str_len)  {
    categoryName[0] = '\0';
    return 0;
  }
  strncpy(categoryName, &itemName[1],i-1);    
  categoryName[i-1] = '\0';
  if ((*cf).curDatablock >= (*cf).numDatablock) return 0;

  for (i=0; i< (*cf).datablocks[(*cf).curDatablock].numCategory; i++)
    if (!STRCMP(categoryName, 
		(*cf).datablocks[(*cf).curDatablock].categories[i].categoryName))
      break;
  if (i == (*cf).datablocks[(*cf).curDatablock].numCategory) return 0;
  else return i+1;

}

int ndb_cif_get_item_keyword_from_item_name(CifHandle *cf,char *itemKeyword, const char *itemName)
{
  int i, str_len, curCategory;
  
  str_len = strlen(itemName);
  for (i=0; i<str_len; i++)
	 if (itemName[i] == '.') break;
  if (i == str_len)  {
    itemKeyword[0] = '\0';
    return 0;
  }
  strcpy(itemKeyword, &itemName[i+1]);    
  if ((*cf).curDatablock <= (*cf).numDatablock ||
      (*cf).datablocks[(*cf).curDatablock].curCategory <=
      (*cf).datablocks[(*cf).curDatablock].numCategory)
    return 0;
  
  curCategory = (*cf).datablocks[(*cf).curDatablock].curCategory;
  for (i=0; i< (*cf).datablocks[(*cf).curDatablock].categories[curCategory].numCol; i++)
    if (!STRCMP(itemKeyword, 
					 (*cf).datablocks[(*cf).curDatablock].categories[curCategory].
					 colNames[i]))
      break;
  if (i == (*cf).datablocks[(*cf).curDatablock].categories[curCategory].numCol) 
    return 0;
  else return i+1;
  
}

int ndb_cif_get_datablock_id(CifHandle *cf,const char *datablockName)
{
  int i;

  if ((*cf).datablocks == NULL ||
      (*cf).curDatablock >= (*cf).numDatablock)
    return 0;
		 
 
  for (i=0; i< (*cf).numDatablock; i++)
    if (!STRCMP((*cf).datablocks[i].datablockName, datablockName)) break;
  if (i != (*cf).numDatablock)
    return i +1;
  else 
    return 0;
}

int ndb_cif_get_category_id(CifHandle *cf,const char *datablockName, const char *categoryName)
{
  int i, datablockId;

  datablockId = ndb_cif_get_datablock_id(cf,datablockName);
  if (datablockId != 0) {
    for (i=0; i< (*cf).datablocks[datablockId-1].numCategory; i++)
      if (!STRCMP((*cf).datablocks[datablockId-1].categories[i].categoryName,
		  categoryName)) break;
    if (i != (*cf).datablocks[datablockId-1].numCategory) 
      return i+1;
    else 
      return 0;
  }
  else return 0;
}

int ndb_cif_get_column_id(CifHandle *cf,const char *datablockName, const char *categoryName,
			  const char *itemKeyword)
{
  int i, datablockId, catId;
  
  datablockId = ndb_cif_get_datablock_id(cf,datablockName);
  if (datablockId != 0)
	 catId = ndb_cif_get_category_id(cf,datablockName, categoryName);
  if (datablockId != 0 && catId != 0) {
    for (i=0; i< (*cf).datablocks[datablockId-1].categories[catId-1].numCol; i++)
      if (!STRCMP((*cf).datablocks[datablockId-1].categories[catId-1].colNames[i],
						itemKeyword)) break;
    if (i != (*cf).datablocks[datablockId-1].categories[catId-1].numCol)
      return i+1;
    else 
      return 0;
  }
  else return 0;
}


void ndb_cif_print_datablock(CifHandle *cf,FILE *fp)
{
    int i, j, k;
    if ((*cf).numDatablock == 0 ||
	(*cf).curDatablock < 0 || 
	(*cf).curDatablock >= (*cf).numDatablock)
	return;

    fprintf(fp, "Data Block: %s\n", (*cf).datablocks[(*cf).curDatablock].datablockName);

    for (i=0; i< (*cf).datablocks[(*cf).curDatablock].numCategory; i++) {
		fprintf(fp,"Category: %s\n", (*cf).datablocks[(*cf).curDatablock].categories[i].categoryName);
		
		for (j=0; j< (*cf).datablocks[(*cf).curDatablock].categories[i].numCol; j++) 
		  fprintf(fp, "%s  ", (*cf).datablocks[(*cf).curDatablock].categories[i].colNames[j]);
		fprintf(fp, "\n");
		for (j=0; j< (*cf).datablocks[(*cf).curDatablock].categories[i].numCol; j++) 
		  fprintf(fp, "-------");
		fprintf(fp, "\n");
		for (k=0; k< (*cf).datablocks[(*cf).curDatablock].categories[i].numRow; k++)  {
		  for (j=0; j< (*cf).datablocks[(*cf).curDatablock].categories[i].numCol; j++) { 
			 if ((*cf).datablocks[(*cf).curDatablock].categories[i].rows[k].columns[j] == NULL ||
				  !STRCMPV((*cf).datablocks[(*cf).curDatablock].categories[i].rows[k].columns[j],""))
				
				fprintf(fp, "%s ", "(null)");
			 else
				fprintf(fp, "%s ", (*cf).datablocks[(*cf).curDatablock].categories[i].rows[k].columns[j]);
		  }
		  fprintf(fp, "\n");
		}
    }
}


void ndb_cif_pretty_print_datablock(CifHandle *cf,FILE *fp)
{
  int i, j, k, l, len;
  int *cwidth;
  
  if ((*cf).numDatablock == 0 ||
		(*cf).curDatablock < 0 || 
		(*cf).curDatablock >= (*cf).numDatablock)
	 return;
  
  
  fprintf(fp, "\n\n--------------------------------------------------------\n");
  fprintf(fp, "Data Block: %s\n", (*cf).datablocks[(*cf).curDatablock].datablockName);
  
  
  for (i=0; i< (*cf).datablocks[(*cf).curDatablock].numCategory; i++) {
	 cwidth = (int *) calloc((*cf).datablocks[(*cf).curDatablock].categories[i].numCol, sizeof(int));
	 for (j=0; j< (*cf).datablocks[(*cf).curDatablock].categories[i].numCol; j++) {
		cwidth[j] = strlen((*cf).datablocks[(*cf).curDatablock].categories[i].colNames[j]);
		if (cwidth[j] < 10) cwidth[j] = 10;
	 }
	 for (k=0; k< (*cf).datablocks[(*cf).curDatablock].categories[i].numRow; k++)  {
		for (j=0; j< (*cf).datablocks[(*cf).curDatablock].categories[i].numCol; j++) { 
		  if (((*cf).datablocks[(*cf).curDatablock].categories[i].rows[k].columns[j] != NULL) &&
				(strlen((*cf).datablocks[(*cf).curDatablock].categories[i].rows[k].columns[j]) > 0)) {
		    if ((strlen((*cf).datablocks[(*cf).curDatablock].categories[i].rows[k].columns[j]) > cwidth[j]) &&
				  (strlen((*cf).datablocks[(*cf).curDatablock].categories[i].rows[k].columns[j])  > 10)) { 
				cwidth[j] = strlen((*cf).datablocks[(*cf).curDatablock].categories[i].rows[k].columns[j]);
		    }
		  }
		}
	 }
	 for (j=0; j< (*cf).datablocks[(*cf).curDatablock].categories[i].numCol; j++) { 
		cwidth[j] += 4;
	 }
	 fprintf(fp, "\n\n--------------------------------------------------------\n");	
	 fprintf(fp,"Category: %s\n", (*cf).datablocks[(*cf).curDatablock].categories[i].categoryName);
	 
	 for (j=0; j< (*cf).datablocks[(*cf).curDatablock].categories[i].numCol; j++) {
		fprintf(fp, "%s", (*cf).datablocks[(*cf).curDatablock].categories[i].colNames[j]);
		len = cwidth[j] - strlen((*cf).datablocks[(*cf).curDatablock].categories[i].colNames[j]);
		for (l=0; l < len; l++)
		  fprintf(fp," ");
	 }
	 fprintf(fp, "\n");
	 for (j=0; j< (*cf).datablocks[(*cf).curDatablock].categories[i].numCol; j++)  {
		for (l=0; l < cwidth[j]-4; l++)
		  fprintf(fp,"-");
		for (l=0; l < 4; l++)
		  fprintf(fp," ");
	 }
	 fprintf(fp, "\n");
	 
	 for (k=0; k< (*cf).datablocks[(*cf).curDatablock].categories[i].numRow; k++)  {
		for (j=0; j< (*cf).datablocks[(*cf).curDatablock].categories[i].numCol; j++) { 
		  if ((*cf).datablocks[(*cf).curDatablock].categories[i].rows[k].columns[j] == NULL ||
				!STRCMPV((*cf).datablocks[(*cf).curDatablock].categories[i].rows[k].columns[j],"")) {
		    
		    fprintf(fp, "%s", "(null)");
		    len = cwidth[j] - 6;
		    for (l=0; l < len; l++)
				fprintf(fp," ");
		  } else {
		    fprintf(fp, "%s", (*cf).datablocks[(*cf).curDatablock].categories[i].rows[k].columns[j]);
		    len = cwidth[j] - strlen((*cf).datablocks[(*cf).curDatablock].categories[i].rows[k].columns[j]);
		    for (l=0; l < len; l++)
				fprintf(fp," ");
		  }
		}
		fprintf(fp, "\n");
	 }
	 free(cwidth);
  }
}


void ndb_cif_print_category(CifHandle *cf,FILE *fp, char *category)
{
  int i, j, k;
  if ((*cf).numDatablock == 0 ||
      (*cf).curDatablock < 0 || 
      (*cf).curDatablock >= (*cf).numDatablock)
    return;
  for (i=0; i< (*cf).datablocks[(*cf).curDatablock].numCategory; i++)
    if (!strcasecmp((*cf).datablocks[(*cf).curDatablock].categories[i].categoryName, category))
      break;
  if (i == (*cf).datablocks[(*cf).curDatablock].numCategory) return;
  fprintf(fp,"Category: %s\n", (*cf).datablocks[(*cf).curDatablock].categories[i].categoryName);
  
  for (j=0; j< (*cf).datablocks[(*cf).curDatablock].categories[i].numCol; j++)   
    fprintf(fp, "%s  ", (*cf).datablocks[(*cf).curDatablock].categories[i].colNames[j]);
  fprintf(fp, "\n");
  for (j=0; j< (*cf).datablocks[(*cf).curDatablock].categories[i].numCol; j++) 
    fprintf(fp, "-------");
  fprintf(fp, "\n");
  for (k=0; k< (*cf).datablocks[(*cf).curDatablock].categories[i].numRow; k++)  {
    for (j=0; j< (*cf).datablocks[(*cf).curDatablock].categories[i].numCol; j++) { 
      fprintf(fp, "*%s* ", (*cf).datablocks[(*cf).curDatablock].categories[i].rows[k].columns[j]);
    }
    fprintf(fp, "\n");
  }
}


void ndb_cif_print_datablocks(CifHandle *cf,FILE *fp)
{
  int i, j, k, n;
  if ((*cf).numDatablock == 0 ||
      (*cf).curDatablock < 0 || 
      (*cf).curDatablock >= (*cf).numDatablock)
    return;

  for (n=0; n < (*cf).numDatablock; n++) {
    fprintf(fp, "Data Block: %d %s\n", n, (*cf).datablocks[n].datablockName);
    
    for (i=0; i< (*cf).datablocks[n].numCategory; i++) {
      fprintf(fp,"Category: %s\n", (*cf).datablocks[n].categories[i].categoryName);
      
      for (j=0; j< (*cf).datablocks[n].categories[i].numCol; j++) 
	fprintf(fp, "%s  ", (*cf).datablocks[n].categories[i].colNames[j]);
      fprintf(fp, "\n");
      for (j=0; j< (*cf).datablocks[n].categories[i].numCol; j++) 
	fprintf(fp, "-------");
      fprintf(fp, "\n");
      for (k=0; k< (*cf).datablocks[n].categories[i].numRow; k++)  {
	for (j=0; j< (*cf).datablocks[n].categories[i].numCol; j++) { 
	  fprintf(fp, "%s ", (*cf).datablocks[n].categories[i].rows[k].columns[j]);
	}
	fprintf(fp, "\n");
      }
    }
  }
}  







int ndb_cif_compare(CifHandle cif1,char*name1,CifHandle cif2,char*name2,FILE*msg)
{
  int dbno1, dbno2;
  int catno1, catno2;
  int colno1, colno2;
  int rowno1, rowno2;
  int i,j,k,l,m,n;
  int iblock, icat, icol;
  char dbname[MxNameLen];
  char catname[MxNameLen];
  char itemname[MxNameLen];
  int *db2;
  int *cat2;
  int *col1,*col2;
  int *coleq1, *coleq2;
  int *row1,*row2;
  int len;
  char *value;
  int curD1,curC1,curD2,curC2;
  int diff1,diff2;
  int ret=0;
  ndb_cif_rewind_file(&cif1); 

  dbno1=ndb_cif_count_datablock(&cif1);
  dbno2=ndb_cif_count_datablock(&cif2);
  db2=(int *)calloc(dbno2,sizeof(int));
  for (i=0; i<dbno2; i++)
	 db2[i]=0;
  for (i=1; i<=dbno1; i++) {
	 ndb_cif_current_datablock_name(&cif1,dbname);
	 fprintf(msg,"\nDATABLOCK --> %s\n",dbname);
	 /* Looking the datablock in the second cif file by dbname */ 
	 iblock=ndb_cif_move_datablock(&cif2,dbname);
	 if (iblock==0) {
		fprintf(msg,"   is present in %s\n",name1);
		ret=1;
	 }
	 else {
		fprintf(msg,"   is present in %s and %s\n",name1,name2);
		db2[iblock-1]=1;
		catno1=ndb_cif_count_category(&cif1);
		catno2=ndb_cif_count_category(&cif2);
		cat2=(int *)calloc(catno2,sizeof(int));
		for (j=0;j<catno2;j++)
		  cat2[j]=0;
		for (j=1; j<=catno1;j++) {
		  ndb_cif_current_category_name(&cif1,catname);
		  fprintf(msg,"\n   CATEGORY --> %s\n",catname);
		  icat=ndb_cif_move_category_by_name(&cif2,dbname,catname);
		  if (icat==0) {
			 fprintf(msg,"      is present in %s\n",name1);
			 ret=1;
		  }
		  else {
			 fprintf(msg,"      is present in %s and %s\n",name1,name2);
			 cat2[icat-1]=1;
			 /* comparing the tables for same categories in two cif files */
			 colno1=ndb_cif_count_column(&cif1);
			 colno2=ndb_cif_count_column(&cif2);
			 col1=(int*)calloc(colno1,sizeof(int));
			 col2=(int*)calloc(colno2,sizeof(int));
			 coleq1=(int*)calloc(colno1,sizeof(int));
			 coleq2=(int*)calloc(colno2,sizeof(int));
			 for (k=0; k<colno1; k++){
				col1[k]=0;
			 }
			 for (k=0; k<colno2; k++){
				col2[k]=0;
			 }
			 l=0;
			 fprintf(msg,"\n      ITEMS of category %s\n",catname);
			 for (k=0; k<colno1; k++) {
				ndb_cif_get_item_shname(&cif1,k+1,itemname);
				icol=ndb_cif_get_column_id(&cif2,dbname,catname,itemname);
				if (icol==0) {
				  fprintf(msg,"         -%s is present %s\n",itemname,name1);
				  ret=1;
				}
				else {
				  fprintf(msg,"         -%s is present in %s and %s\n",itemname,name1,name2);
				  
				  col1[k]=1;
				  col2[icol-1]=1;
				  
				  coleq1[l]=k;
				  coleq2[l]=icol-1;
				  
				  l++;
				}
			 } 
			 for (k=0; k<colno2; k++)
				if (col2[k]==0) {
				  ndb_cif_get_item_shname(&cif2,k+1,itemname);
				  fprintf(msg,"         -%s is present in %s\n",itemname,name2);
				  ret=1;
				}
			 /* For all rows in first category, looking for 
				 identical row in second categorry      */
			 fprintf(msg,"\n      ROWS in category %s for the same item names\n",catname);
			 rowno1=ndb_cif_count_row(&cif1);
			 rowno2=ndb_cif_count_row(&cif2);
			 row1=(int *)calloc(rowno1,sizeof(int));
			 row2=(int *)calloc(rowno2,sizeof(int));
			 for (k=0;k<rowno1;k++)
				row1[k]=0;
			 for (k=0;k<rowno2;k++)
				row2[k]=0;
			 curD1=(cif1).curDatablock;
			 curC1=(cif1).datablocks[curD1].curCategory;
			 curD2=(cif2).curDatablock;
			 curC2=(cif2).datablocks[curD2].curCategory;

			 for (k=0; k<rowno1; k++) {
				m=0;
				while (m<rowno2 && row1[k]==0){
				  n=0;
				  while(n<l && strcmp((cif1).datablocks[curD1].categories[curC1].rows[k].columns[coleq1[n]],(cif2).datablocks[curD2].categories[curC2].rows[m].columns[coleq2[n]]) ==0) {
					 n++;
				  }
				  if (n==l && (strcmp((cif1).datablocks[curD1].categories[curC1].rows[k].columns[coleq1[n-1]],(cif2).datablocks[curD2].categories[curC2].rows[m].columns[coleq2[n-1]])==0)){
					 row1[k]=1;
					 row2[m]=1;
				  }
				  m++;
				}
				
				ndb_cif_next_row(&cif1);
			 }
			 diff1=1; diff2=1;
			 k=0;
			 while ( k<rowno1 && diff2==1) {
				if (row1[k]==0) {
				  diff1=0;
				}
				k++;
			 }

			 k=0;
			 while ( k<rowno2 && diff2==1) {
				if (row2[k]==0) {
				  diff2=0;
				}
				k++;
			 }
			 if (diff1==0){
				fprintf(msg,"\n         %d rows in %s, followed are the extra:\n",rowno1,name1);
				ret=1;
				fprintf(msg,"          ");m=1;
				for (k=0; k<rowno1; k++) {
				  if (row1[k]==0) {
					 fprintf(msg,"%5d ",k);
					 if (m==10){
						fprintf(msg,"\n          ");
						m=0;
					 }
					 m++;
				  }
				}
				if (m<=10)
				  fprintf(msg,"\n");
			 }
			 else {
				if (diff2==0)
				  fprintf(msg,"\n         %d rows in %s, and there are no extra rows\n",rowno1,name1);
			 }
			 
			 if (diff2==0){
				fprintf(msg,"\n         %d rows in %s, followed are the extra:\n",rowno2,name2);
				ret=1;
				fprintf(msg,"          ");m=1;
				for (k=0; k<rowno2; k++) {
				  if (row2[k]==0) {
					 fprintf(msg,"%5d ",k);
					 if (m==10){
						fprintf(msg,"\n          ");
						m=0;
					 }
					 m++;
				  }
				}
				if (m<=10)
				  fprintf(msg,"\n");
			 }
			 else {
				if (diff1==0)
				  fprintf(msg,"\n         %d rows in %s, and there are no extra rows\n",rowno2,name2);
			 }
			 
			 if (diff1==1 && diff2==1) {
				fprintf(msg,"         Category %s has %d rows in both files and all are the same\n",catname,rowno2);
			 }
			 if (row1!=NULL) free(row1);	
			 if (row2!=NULL) free(row2);	
			
			 if (col2!=NULL) free(col2);
			 if (col1!=NULL) free(col1);		
			 if (coleq2!=NULL) free(coleq2);
			 if (coleq1!=NULL) free(coleq1);
			  
			 
		  }
		  ndb_cif_next_category(&cif1);
		}
		for(j=0; j<catno2; j++)
		  if (cat2[j]==0) {
			 ndb_cif_move_category(&cif2,j+1);
			 ndb_cif_current_category_name(&cif2,catname);
			 fprintf(msg,"\n   CATEGORY --> %s\n",catname);
			 fprintf(msg,"      is present in %s\n",name2);
			 ret=1;
		  }
		if (cat2!=NULL) free(cat2);	
	 }
	 ndb_cif_next_datablock(&cif1);

  }
  for(i=0; i<dbno2; i++){
	 if (db2[i]==0) {
		ndb_cif_move_datablock_by_id(&cif2,i+1);
		ndb_cif_current_datablock_name(&cif2,dbname);
		fprintf(msg,"DATABLOCK --> %s\n",dbname);
		fprintf(msg,"   is present in %s\n",name2);
		ret=1;
	 }
  }

   
  if (db2!=NULL) free(db2);
  return ret;
}




void ndb_cif_compress_file(CifHandle *cif,CifHandle *ddl)
{
  int curDatablock;
  (*cif).curDatablock=0;
  do {
	 ndb_cif_compress_datablock(cif,ddl);
    curDatablock = ndb_cif_next_datablock(cif);
  } while (curDatablock);
  (*cif).curDatablock=0;

}

void ndb_cif_compress_datablock(CifHandle *cif,CifHandle *ddl)
{
  int curCategory;

  (*cif).datablocks[(*cif).curDatablock].curCategory = 0;
  do {
    ndb_cif_compress_category(cif,ddl);
    curCategory = ndb_cif_next_category(cif);
  } while (curCategory);
  (*cif).datablocks[(*cif).curDatablock].curCategory = 0;

}

void ndb_cif_compress_category(CifHandle *cif,CifHandle *ddl)
/*  delete redudant rows from CifHandle structure. If the cif structure is
	 dictionary, then necessary informations for compressing is read from
	 DDL. If the structure is cif file than the informatin is read from
	 dictionary
*/
{
  int i,k;
  int keyNo;
  int lenValue;
  int duplicate;
  char datablockName[MxNameLen],dbNameddl[MxNameLen];
  char categoryName[MxNameLen];
  int numRow;
  int Index1,Index2;
  int datablockId,categoryId;
  tree btree; 
  char *itemValue=(char *)malloc(1);
  char *keyString=(char*)malloc(1);
  int **deleteMe;
  int colId;
  int lenValueCode;
  char *itemValueCode=(char*)malloc(1);
  int index;


  int *keyIndex=(int*)malloc(sizeof(int));
  char **key;
  char **keyCode;
  key=(char**)malloc(sizeof(char*));
  keyCode=(char**)malloc(sizeof(char*));

  InitBTree(&btree);

  /* looking what is the key */
  ndb_cif_current_category_name(cif,categoryName);
  categoryId=ndb_cif_current_category(cif);
  ndb_cif_current_datablock_name(cif,datablockName);
  datablockId=ndb_cif_current_datablock(cif);

  ndb_cif_rewind_category(ddl);
  ndb_cif_current_datablock_name(ddl,dbNameddl);
  ndb_cif_move_category_by_name(ddl,dbNameddl,"category_key");
  Index1=ndb_cif_get_column_id(ddl,dbNameddl,"category_key","id");
  Index2=ndb_cif_get_column_id(ddl,dbNameddl,"category_key","name");
  numRow=ndb_cif_count_row(ddl);
  keyNo=0;
  for (k=0;k<numRow;k++) {
    lenValue=ndb_cif_get_item_value_length(ddl,Index1);
    itemValue=(char *)realloc(itemValue,lenValue+1);
    ndb_cif_get_item_value(ddl,Index1,itemValue,lenValue+1);
    if (STRCMPV(itemValue,categoryName)==0) {
      key=(char**)realloc(key,(keyNo+1)*sizeof(char*));
      key[keyNo]=(char*)malloc(MxNameLen);
      keyCode=(char**)realloc(keyCode,(keyNo+1)*sizeof(char*));
      keyCode[keyNo]=(char*)malloc(MxNameLen);
      lenValue=ndb_cif_get_item_value_length(ddl,Index2);
      itemValue=(char *)realloc(itemValue,lenValue+1);
      ndb_cif_get_item_value(ddl,Index2,itemValue,lenValue+1);
      ndb_cif_get_item_keyword_from_item_name(ddl,key[keyNo],itemValue);
      
      /* determine does appropiate itemValue case sensitive or not         */
      /* if primitive type code is uchar, then the value is case sensitive */
      ndb_cif_move_category_by_name(ddl,dbNameddl,"item_type");
      colId=ndb_cif_get_column_id(ddl,dbNameddl,"item_type","name");
      ndb_cif_item_row_1_key(ddl,colId, itemValue);
      colId=ndb_cif_get_column_id(ddl,dbNameddl,"item_type","code");		
      lenValueCode=ndb_cif_get_item_value_length(ddl,colId);
      itemValueCode=(char *)realloc(itemValueCode,lenValueCode+1);
      ndb_cif_get_item_value(ddl,colId,itemValueCode,lenValueCode+1);
      ndb_cif_move_category_by_name(ddl,dbNameddl,"item_type_list");
      colId=ndb_cif_get_column_id(ddl,dbNameddl,"item_type_list","code");
      ndb_cif_item_row_1_key(ddl,colId, itemValueCode);
      colId=ndb_cif_get_column_id(ddl,dbNameddl,"item_type_list","primitive_code");		
      lenValueCode=ndb_cif_get_item_value_length(ddl,colId);
      itemValueCode=(char *)realloc(itemValueCode,lenValueCode+1);
      ndb_cif_get_item_value(ddl,colId,itemValueCode,lenValueCode+1);
      strcpy(keyCode[keyNo],itemValueCode);
      keyNo++;
      ndb_cif_move_category_by_name(ddl,dbNameddl,"category_key");
    }
    ndb_cif_next_row(ddl);
  }
  ndb_cif_move_category_by_name(cif,datablockName,categoryName);
  ndb_cif_rewind_category(cif); 
  numRow=ndb_cif_count_row(cif);
  for (k=0;k<keyNo;k++){
    keyIndex=(int*)realloc(keyIndex,(k+1)*sizeof(int));
    keyIndex[k]=ndb_cif_get_column_id(cif,datablockName,categoryName,key[k]);
  }
  deleteMe=(int **)malloc((numRow)*sizeof(int*));
  for (i=0;i<numRow;i++) {
    keyString[0]='\0';
    deleteMe[i]=(int *)malloc(2*sizeof(int));
    for (k=0;k<keyNo;k++){
      lenValue=ndb_cif_get_item_value_length(cif,keyIndex[k]);
      itemValue=(char *)realloc(itemValue,lenValue+1);
      ndb_cif_get_item_value(cif,keyIndex[k],itemValue,lenValue+1);
      keyString=(char*)realloc(keyString,strlen(keyString)+lenValue+1);
      if (strcmp(keyCode[k],"uchar")==0) {
		  strcat(keyString,uppercase(itemValue));
      }
      else {
		  strcat(keyString,itemValue);
      }
    }
    
    index=SeekNode(&btree,keyString,1);
    if (index==-1) {
      InsertNode(&btree,keyString,i,1);
    }
    else {
      deleteMe[index][1]=0;
      btree.current->index=index;
    }
    deleteMe[i][0]=i;
    deleteMe[i][1]=1;
    ndb_cif_next_row(cif);
  }
  duplicate=0;
  ndb_cif_rewind_category(cif);
  for (i=0;i<numRow;i++) {
    if (deleteMe[i][1]==0){
      ndb_cif_remove_row_by_id(cif,datablockId,categoryId,deleteMe[i][0]+1-duplicate);
      duplicate++;
      ndb_cif_next_row(cif);
    }
  }
  if (itemValue != NULL) free(itemValue);
  if (keyString != NULL) free(keyString);
  for(k=0; k<numRow; k++)
    free(deleteMe[k]);
  free(deleteMe);
  for(k=0; k<keyNo; k++){
    free(key[k]);
    free(keyCode[k]);
  }
  free(key);
  free(keyCode);
  free(keyIndex);
  free(itemValueCode);
  FreeBTree(&btree);
}
