/*
FILE:     ciflib_interface.C
*/
/*
VERSION:  4
*/
/*
DATE:     6/17/2002
*/
/*
  Comments and Questions to: sw-help@rcsb.rutgers.edu
*/
/*
COPYRIGHT 1999-2002 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 file contains function prototypes to access 
                 ciflib class via a C API.

   A NOTE ON THE DOCUMENTATION: Steven Schirripa

     In many cases, the parameters datablockName, categoryName, itemKeyword,
     itemName, rowIndex, datablockIndex, etc. are not explained because they
     occur so frequently. They merely locate data in a "table" called
     categoryName (or having an index categoryIndex), which is in the datablock
     datablockName (or having the index dataBlockIndex). The table's row is
     identified with a rowIndex, while the table's columns are identified by
     an itemKeyword. itemName sometimes replaces a categoryName and
     itemKeyword, since it contains both pieces of information.

 * ***************************************************************** */

#include <stdlib.h>
#include "CifItemValue.h"
#include "CifFile.h"
#include "CifFileIo.h"
#include "ciflib_interface.h"
#include "ciflib_structure.h"
#include "CifErrorCodes.h"
#include "CifAnon.h"
#include "regex.h"
#ifdef GNU
#include "cif_template.h"
#endif
#include "ciflib.h"

CifFile *theCifFiles = NULL;
static int verify, verbose;

/*  
 *  Functions which construct and manipulate schema ...
 */

CifSchema  *cifConstructSchema(const int dataBlockIndex)
/* --------------------------------------------------------------------------
   cifConstructSchema() constructs a cifSchema for a cif Datablock
   Parameters: dataBlockIndex
   Return ItemValue: *schema or NULL for failure
  ------------------------------------------------------------------------- */
{
  int i, j, numCategory, numItem;
  CifSchema *schema;
  char ** categoryList, **itemList;
  
  // Initialization
  schema = NULL;
  
  if (dataBlockIndex < 0 || dataBlockIndex >= theCifFiles->CountDataBlocks()) 
    return NULL;
  // Allocate space and construct column list for each category
  schema = (CifSchema *)  malloc(sizeof(CifSchema));
  categoryList = cifGetCategoryNamesByIndex(dataBlockIndex, &numCategory);
  strcpy(schema->datablockName, theCifFiles->GetDataBlockName(dataBlockIndex)->Text());
  if (categoryList != (char **) NULL && numCategory != 0) {
    schema->numCategory = numCategory;
    schema->categories = (CategorySchema *) 
      calloc(schema->numCategory +1, sizeof(CategorySchema));
    for (i=0; i< schema->numCategory; i++) {
      schema->categories[i].category = categoryList[i];
      itemList = cifGetItemKeywordsByIndex(dataBlockIndex, i, &numItem);
      if (itemList != (char **) NULL && numItem != 0) {
	schema->categories[i].numItem = numItem;
	schema->categories[i].items = 
	  (ItemSchema *)calloc(numItem+1, sizeof(ItemSchema));
	for (j=0; j< numItem; j++) {
	  schema->categories[i].items[j].item = itemList[j];
	  schema->categories[i].items[j].presentationOrder = j+1;
	}
      }
    }
  }
  return schema;
}

CifSchema **cifConstructSchemas(int *numDataBLock)
/* --------------------------------------------------------------------------
   cifConstructSchemas() constructs cifSchemas for all the cif Datablocks
   Parameters: *numDataBLock is the number of Datablocks
   Return ItemValue: *schema or NULL for failure
  ------------------------------------------------------------------------- */
{
  CifSchema **schema;
  int i;
  
  // Initialization
  schema = NULL;
  
  // Allocate space and construct column list for each category
  *numDataBLock = theCifFiles->CountDataBlocks() ;
  if (theCifFiles != (CifFile *) NULL && *numDataBLock != 0 ) {
    schema = (CifSchema **) 
      calloc(*numDataBLock, sizeof(CifSchema*));
    for (i=0; i < *numDataBLock ; i++) 
      schema[i] = cifConstructSchema(i);
  }
  return schema;
}

CifSchema  *cifConstructSchemaByAlias(const int dataBlockIndex)
/* --------------------------------------------------------------------------
   cifConstructSchemaByAlias() constructs a cifSchema with alias 
                                       for a cif Datablock
   Parametrers: dataBlockIndex
   Return ItemValue: *schema or NULL for failure
   ------------------------------------------------------------------------- */
{
  int i, j, numCategory;
  CifSchema *schema;
  char ** categoryList;
  CifString *aliasName;
  
  // Initialization
  schema = NULL;
  
  // Allocate space and construct keyword list for each category
  if (dataBlockIndex < 0 || dataBlockIndex >= theCifFiles->CountDataBlocks()) 
    return NULL;
  schema = (CifSchema *) malloc(sizeof(CifSchema));
  strcpy(schema->datablockName, theCifFiles->GetDataBlockName(dataBlockIndex)->Text());
  categoryList = cifGetCategoryNamesByIndex(dataBlockIndex, &numCategory);
  if (categoryList != (char **) NULL && numCategory != 0) {
    schema->numCategory = numCategory;
    schema->categories = (CategorySchema *) 
      calloc(schema->numCategory +1, sizeof(CategorySchema));
    for (i=0; i< schema->numCategory; i++) {
      schema->categories[i].category = categoryList[i];
      schema->categories[i].numItem = theCifFiles->CountItem(dataBlockIndex, i);
      schema->categories[i].items = 
	(ItemSchema *)calloc(schema->categories[i].numItem+1, sizeof(ItemSchema));
      for (j=0; j< schema->categories[i].numItem; j++) {
	schema->categories[i].items[j].presentationOrder = j+1;
	aliasName = theCifFiles->GetItemAliasName(dataBlockIndex, i, j);
	if (aliasName == NULL) 
	  aliasName = theCifFiles->GetItemKeyword(dataBlockIndex, i, j);
	if (aliasName != NULL) {
	  schema->categories[i].items[j].item = (char *)
	    calloc(aliasName->Length() +1, sizeof(char));
	  strcpy(schema->categories[i].items[j].item, aliasName->Text());
	}
      }
    }
  }
  return schema;
}

CifSchema **cifConstructSchemasByAlias(int *numDataBLock)
/* --------------------------------------------------------------------------
   cifConstructSchemasByAlias() constructs cifSchemas with alias 
                                       for all cif Datablocks
   Parametrers: int *numDataBLock: number of schema (number of Datablocks)
   Return ItemValue: *schema or NULL for failure
   ------------------------------------------------------------------------- */
{
  CifSchema **schema;
  int i;
  
  // Initialization
  schema = NULL;
  
  // Allocate space and construct keyword list for each category
  *numDataBLock = theCifFiles->CountDataBlocks();
  if (theCifFiles != (CifFile *) NULL && *numDataBLock != 0 ) {
    schema = (CifSchema **) 
      calloc(*numDataBLock, sizeof(CifSchema*));
    for (i=0; i< *numDataBLock; i++) 
      schema[i] = cifConstructSchemaByAlias(i);
  }
  return schema;
}

void cifFreeSchema(CifSchema *schema)
/* --------------------------------------------------------------------------
   cifFreeSchema() frees space for given schema pointer
   Parametrers: schema, a pointer to the CifSchema to be freeed
   Return ItemValue: None; schema will be set to NULL
   ------------------------------------------------------------------------- */
{
  for (unsigned i = 0; i < schema->numCategory; i++) {
    for (unsigned j = 0; j < schema->categories[i].numItem; j++)
      free ( schema->categories[i].items[j].item);
    free ( schema->categories[i].category);
    free ( schema->categories[i].items);
  }
  free (schema->categories);
  free(schema);
  schema = NULL;
}

void cifFreeSchemas(CifSchema **schema, const int numDataBLock)
/* --------------------------------------------------------------------------
   cifFreeSchemas() frees space for given array of schema pointers
   Parametrers: schema, a pointer to CifSchema *'s to be freeed
   Return ItemValue: None; schema will be set to NULL
   ------------------------------------------------------------------------- */
{
  for (unsigned i = 0; i < numDataBLock; i++) 
    cifFreeSchema(schema[i]);
  free(schema);
  schema = NULL;
}

void cifPrintSchema(const CifSchema *schema)
/* --------------------------------------------------------------------------
   cifPrintSchema() prints schema details
   Parametrers: cifSchema *schema: the schema to be printed
   ------------------------------------------------------------------------- */
{
  int i, j;
  
  if (schema != NULL) {
    cout << "Datablock: " << schema->datablockName << endl;
    for (i=0; i< schema->numCategory; i++) {
      cout << i << ' ' << schema->categories[i].category << endl;
      for  (j=0; j< schema->categories[i].numItem; j++) 
       cout << "\t" << j << ' ' << schema->categories[i].items[j].item << endl;
    }
    cout.flush();
  }
}

void cifPrintSchemas(CifSchema **schema, const int numDataBLock)

/* --------------------------------------------------------------------------
   cifPrintSchemas() print schemas' details
   Parametrers: cifSchema **schema: poniter to the CifSchema *'s to be printed
                int numDataBLock: number of schemas
   ------------------------------------------------------------------------- */
{
  int i;
  
  if (schema != NULL) 
    for (i = 0; i < numDataBLock; i++) 
      cifPrintSchema(schema[i]);
}


/*
 *  Functions which perform file operations, initialization and housekeeping ..
 */
int cifInit(const char *fileName, const int inVerify, const int inVerbose)
/* -------------------------------------------------------------------------
   cifInit() initializes CifFile class, and also set regular expression
                  options
   ------------------------------------------------------------------------- */
{
  verbose = inVerbose;
  verify  = inVerify;

  // regular expression options
//  re_syntax_options =  RE_DOT_NEWLINE | RE_DOT_NOT_NULL | RE_NO_BK_VBAR 
//    | RE_NO_BK_PARENS;
  
  if (theCifFiles != NULL)
    cifFree();
  theCifFiles = new CifFile;
  if (theCifFiles != NULL)
    return theCifFiles->ReadFile(fileName, (char *) NULL, verify);
  else {
    cerr << "Allocation space error" << endl;
    return CIF_ALLOCATION_FAILURE;
  }
  
}
void cifFree()
/* -------------------------------------------------------------------------
   cifFree() frees up the CifFile class
   ------------------------------------------------------------------------- */
{
  if (theCifFiles != NULL) delete theCifFiles;
  theCifFiles = NULL;
}

int cifReadFile(const char *fileName, const char *dicDatablockName)
/* -------------------------------------------------------------------------
   cifReadFile() Reads in a cif file and builds CifFile class
   Parameters: const char *fileName : input file name
               const char *dicDatablockName: its dictionary Datablock name 
   Return ItemValue: SUCCEED of FAILURE
   ------------------------------------------------------------------------- */
{
  return theCifFiles->ReadFile(fileName, dicDatablockName, verify);
  
}

int cifCloseFile(const char *fileName)
/*-------------------------------------------------------------------------
  cifCloseFile() closes the CifFile and removes the Datablocks from CifFile
  Parameters: char *fileName : file name of the file to be closed
  Return ItemValue: SUCCEED of FAILURE
  ------------------------------------------------------------------------- */
{
  char *file;
  int ret = FALSE;
  if (theCifFiles == (CifFile *) NULL) return FALSE;
  for (int i= cifCountDataBlocks()-1; i >= 0; i--) {
    file = cifGetDataBlockFileNameByIndex(i);
    if (file != (char *) NULL && !strcmp(file, fileName)) {
      cifDeleteDataBlockByIndex(i);
      ret = TRUE;
    }
  }
  return ret;
}

int cifWriteFile(const char *fileName, const char *outputFileName,
		 const char mode, const int templateFlag, const int esdFlag,
		 const char *dictionary, const char *version)
/* -------------------------------------------------------------------------
   cifWriteFile() Writes in a dictionary file and builds CifFile class
   Parameters: fileName : input file name
               outputFileName : output file name
               mode: 'a' for append or 'w' for overwrite
               templateFlag: write comment or not
               esdFlag: write precision estimates to data items with
                        _item_conditions.code = esd or not
               dictionary and version: controls the use of alias names
   Return ItemValue: SUCCEED of FAILURE
   ------------------------------------------------------------------------ */
{
  if (fileName == NULL)
    return theCifFiles->WriteFile(outputFileName, outputFileName, mode, 
				  templateFlag, esdFlag, dictionary, version);
  else if (outputFileName  == NULL)
    return theCifFiles->WriteFile(fileName, fileName, mode, templateFlag, esdFlag, 
				  dictionary, version);
  else
    return theCifFiles->WriteFile(fileName, outputFileName, mode, templateFlag,
				  esdFlag, dictionary, version);
}

void cifSaveFile(const char *fileName)
/* ------------------------------------------------------------------------
   cifSaveFile() saves the CIFLIB internal representation of the contents of
     the input file into an auxiliary file.
   Parameters: fileName, the file to be saved
   Return Value: None
   ------------------------------------------------------------------------ */
{
  theCifFiles->PermanentWrite(fileName);
}

int cifWriteDataBlock(const char *fileName, const char mode,
		      const char *dataBlockName, const int templateFlag, 
		      const int esdFlag,
		      const char *dictionary, const char *version)
/* -------------------------------------------------------------------------
   cifWriteDataBlock() Writes a data block
   Parameters: filename : output file name
               datablockName: the Datablock block to write out
               mode: 'a' for append or 'w' for overwrite
               const int templateFlag: write comment or not
               esdFlag: write precision estimates to data items with
                        _item_conditions.code = esd or not
               dictionary and version: controls the use of alias names
   Return ItemValue: SUCCEED of FAILURE
   ------------------------------------------------------------------------- */
{
  return theCifFiles->WriteDataBlock(fileName, mode, dataBlockName,
                        templateFlag, esdFlag, dictionary, version);
}


/* 
 *    Functions which provide information or operate on data blocks... 
 */

int cifCountDataBlocks()
/* -------------------------------------------------------------------------
   cifCountDataBlocks(): Gets the number of the Datablocks 
   ------------------------------------------------------------------------- */
{
  return theCifFiles->CountDataBlocks();
}

char **cifGetDataBlockNames(int *numDataBLock)
/* -------------------------------------------------------------------------
   cifGetDataBlockNames(): Gets all the Datablock names fom 
                                 dictionary
   Parameters: numDataBLock: Address of an integer to get number of 
                                  data blocks
   Return ItemValue: NULL or the labels of Datablocks
   ------------------------------------------------------------------------- */
{
  char **datablockNames;
  *numDataBLock = theCifFiles->CountDataBlocks();
  
  if (*numDataBLock == 0) return NULL;
  if (*numDataBLock > 0) {
    datablockNames = (char **) calloc(*numDataBLock+1, sizeof(char *));
  
    for (unsigned i=0; i<*numDataBLock; i++) 
      datablockNames[i] = cifGetDataBlockName(i);
  }
  else datablockNames = (char **) NULL;
  return datablockNames;
}

char *cifGetDataBlockName(const int dataBlockIndex)
/* -------------------------------------------------------------------------
   cifGetDataBlockName() : Find the name of the given dataBlockIndex
   Parameters: dataBlockIndex
   Return ItemValue: The label of the Datablock or NULL if fails
   ------------------------------------------------------------------------- */
{
  CifString *datablock; 
  char *datablockName;
  datablock = theCifFiles->GetDataBlockName(dataBlockIndex);
  if (datablock == NULL) return NULL;
  else {
    datablockName = (char *) calloc(datablock->Length()+1, sizeof(char));
    strcpy(datablockName, datablock->Text());
    return datablockName;
  }
}

int cifGetDataBlockIndex(const char *datablockName) 
/* -------------------------------------------------------------------------
   cifGetDataBlockIndex() : Find the index of the given DatablockName
   Parameters: datablockName
   Return ItemValue: index or -1 if fails
   ------------------------------------------------------------------------- */
{
  //  CifString message;
  int dataBlockIndex = theCifFiles->GetIndex(datablockName);

  return dataBlockIndex;
}

int cifGetDataBlockDictionaryIndex(const char *dataBlockName)
/* -------------------------------------------------------------------------
   cifGetDataBlockDictionaryIndex() : Find the dictionary index of the given 
                                     dataBlockName
   Parameters: dataBlockName
   Return ItemValue: index or -1 if fails
   ------------------------------------------------------------------------- */
{
  int dataBlockIndex = cifGetDataBlockIndex(dataBlockName);
  return cifGetDataBlockDictionaryIndexByIndex(dataBlockIndex);
}

int cifGetDataBlockDictionaryIndexByIndex(const int dataBlockIndex)
/* -------------------------------------------------------------------------
   cifGetDataBlockDictionaryIndexByIndex() : Find the dictionary index of the
                                            given dataBlockIndex
   Parameters: dataBlockIndex
   Return ItemValue: index or -1 if fails
   ------------------------------------------------------------------------- */
{
  return theCifFiles->GetDataBlockDictionaryIndex(dataBlockIndex);
}

char *cifGetDataBlockDictionaryName(const char *dataBlockName)
/* -------------------------------------------------------------------------
   cifGetDataBlockDictionaryName() : Find the dictionary index of the given 
                                     dataBlockName
   Parameters: dataBlockName
   Return ItemValue: index or -1 if fails
   ------------------------------------------------------------------------- */
{
  int dataBlockIndex = cifGetDataBlockIndex(dataBlockName);
  return cifGetDataBlockDictionaryNameByIndex(dataBlockIndex);
}

char *cifGetDataBlockDictionaryNameByIndex(const int dataBlockIndex)
/* -------------------------------------------------------------------------
  cifGetDataBlockDictionaryNameByIndex() : Find the dictionary index of the
                                           given dataBlockIndex
  Parameters: dataBlockIndex
  Return ItemValue: index or -1 if fails
   ------------------------------------------------------------------------- */
{
  int dicDataBlockIndex;

  dicDataBlockIndex = theCifFiles->GetDataBlockDictionaryIndex(dataBlockIndex);
  return cifGetDataBlockName(dicDataBlockIndex);
}

char *cifGetDataBlockFileName(const char *dataBlockName)
/* -------------------------------------------------------------------------
   cifGetDataBlockFileName() : Get the filename of a given datablockName
   Parameters: dataBlockName
   Return ItemValue: the datablock name or NULL if failed
   ------------------------------------------------------------------------- */
{
  int dataBlockIndex = cifGetDataBlockIndex(dataBlockName);
  return cifGetDataBlockFileNameByIndex(dataBlockIndex);
}

char *cifGetDataBlockFileNameByIndex(const int dataBlockIndex)
/* -------------------------------------------------------------------------
   cifGetDataBlockFileNameByIndex(): Get the filename of a given datablockIndex
   Parameters: dataBlockIndex
   Return ItemValue: the datablock name or NULL if failed
   ------------------------------------------------------------------------- */
{
  CifString *fileName;
  fileName = theCifFiles->GetDataBlockFileName(dataBlockIndex);
  if (fileName  == (CifString *) NULL) return (char *) NULL;
  else               return fileName->Text();
}

long cifGetDataBlockFileOffset(const char *dataBlockName)
/* -------------------------------------------------------------------------
   cifGetDataBlockFileOffset(): Get the file offset for a datablockName
   Parameters: dataBlockName
   Return ItemValue: file offset or -1 for failure
   ------------------------------------------------------------------------- */
{
  int dataBlockIndex = cifGetDataBlockIndex(dataBlockName);
  return cifGetDataBlockFileOffsetByIndex(dataBlockIndex);
  
}

long cifGetDataBlockFileOffsetByIndex(const int dataBlockIndex)
/* -------------------------------------------------------------------------
   cifGetDataBlockFileOffsetByIndex() : Find the file offset of the given
                                        dataBlockIndex
   Parameters: dataBlockIndex
   Return ItemValue: file offset or -1 if fails
   ------------------------------------------------------------------------- */
{
  return  theCifFiles->GetDataBlockFileOffset(dataBlockIndex);
}

int cifMergeDataBlocks(const char *newDataBlockName, 
			const char *dataBlockName1, 
			const char *dataBlockName2)
/* -------------------------------------------------------------------------
   cifMergeDataBlocks() : Merges two datablocks
   Parameters: newDataBlockName, the new datablock's name
              dataBlockName1, the first datablock's name
              dataBlockName2, the second datablock's name
   Return ItemValue: new Datablock index or -1 if fails
   ------------------------------------------------------------------------- */
{
  int dataBlockIndex1 = cifGetDataBlockIndex(dataBlockName1);
  int dataBlockIndex2 = cifGetDataBlockIndex(dataBlockName2);
  return cifMergeDataBlocksByIndex(newDataBlockName, dataBlockIndex1, dataBlockIndex2);
  
}
int cifMergeDataBlocksByIndex(const char *newDataBlockName, 
			      const int dataBlockIndex1, 
			      const int dataBlockIndex2)
/* -------------------------------------------------------------------------
   cifMergeDataBlockByIndex(): Merge Datablocks 
   Parameters: newDataBlockName, the new datablock's name
              dataBlockName1, the first datablock's name
              dataBlockName2, the second datablock's name
   Return: new Datablock index, or -1 if fails
   ------------------------------------------------------------------------- */
{
  return theCifFiles->MergeDataBlock(newDataBlockName,
				      dataBlockIndex1, dataBlockIndex2);
}

void cifDeleteDataBlock(const char *dataBlockName)
/* -------------------------------------------------------------------------
   cifDeleteDataBlock(): Remove a Datablock
   Parameters: dataBlockName, the Datablock to be removed
   Return: None
   ------------------------------------------------------------------------- */
{
  int dataBlockIndex = cifGetDataBlockIndex(dataBlockName);
  cifDeleteDataBlockByIndex(dataBlockIndex);
}

void cifDeleteDataBlockByIndex(const int dataBlockIndex)
/* -------------------------------------------------------------------------
   cifDeleteDataBlockByIndex(): Remove a Datablock
   Parameters: dataBlockIndex: the index of the Datablock to be removed
   Return: None
   ------------------------------------------------------------------------- */
{
  theCifFiles->DeleteDataBlock(dataBlockIndex);
}

void cifCheckDataBlock(const char *dataBlockName)
/* -------------------------------------------------------------------------
   cifCheckDataBlock(): Checks a Datablock
   Parameters: dataBlockName, the Datablock to be checked
   Return: None
   ------------------------------------------------------------------------- */
{
  int dataBlockIndex = cifGetDataBlockIndex(dataBlockName);
  cifCheckDataBlockByIndex(dataBlockIndex);
}


void cifCheckDataBlockByIndex(const int dataBlockIndex)
/* -------------------------------------------------------------------------
   cifCheckDataBlock(): Check a Datablock
   Parameters: dataBlockIndex, the index of the Datablock to be removed
   Return: None
   ------------------------------------------------------------------------- */
{
  theCifFiles->CheckDataBlock(dataBlockIndex);
}


/* 
 *   Functions which provide information or operate on categories...
 */

int cifCountCategories(const char *datablockName)
/* -------------------------------------------------------------------------
   cifCountCategories(): counts the number of categories in a datablock
   Parameters: datablockName
   Return: the number of categories or -1 if failure
   ------------------------------------------------------------------------- */
{
  int dataBlockIndex;

  dataBlockIndex = cifGetDataBlockIndex(datablockName);
  return cifCountCategoriesByIndex(dataBlockIndex);
}

int cifCountCategoriesByIndex(const int dataBlockIndex)
/* -------------------------------------------------------------------------
   cifCountCategoriesByIndex(): counts the number of categories in a datablock
   Parameters: datablockIndex
   Return: the number of categories or -1 if failure
   ------------------------------------------------------------------------- */
{
  return theCifFiles->CountCategories(dataBlockIndex);
}

char * cifGetCategoryName(const char *datablockName, const int categoryIndex)
/* -------------------------------------------------------------------------
   cifGetCategoryName(): Gets the name of a category given its index
   Parameters: datablockName, the datablock in which the category is
               categoryIndex, the index of the category
   Return: the name of the category or NULL if failure
   ------------------------------------------------------------------------- */
{
  int dataBlockIndex;
  dataBlockIndex = cifGetDataBlockIndex(datablockName);

  if (dataBlockIndex != -1)
    return cifGetCategoryNameByIndex(dataBlockIndex, categoryIndex);
  else return (char *) NULL;
}

char *cifGetCategoryNameByIndex(const int dataBlockIndex, const int categoryIndex)
/* -------------------------------------------------------------------------
   cifGetCategoryNameByIndex(): Gets the name of a category given its index
   Parameters: datablockIndex, the datablock in which the category is
               categoryIndex, the index of the category
   Return: the name of the category or NULL if failure
   ------------------------------------------------------------------------- */
{
  CifString *category;
  char *categoryCifString;

  categoryCifString = NULL; 
  category = theCifFiles->GetCategoryName(dataBlockIndex, categoryIndex);
  if (category != NULL) {
    categoryCifString = (char *) calloc(category->Length()+1, sizeof(char));
    strcpy(categoryCifString ,category->Text());
  }
  return categoryCifString;
}

char ** cifGetCategoryNames(const char *datablockName, int *numCategory)
/* -------------------------------------------------------------------------
   cifGetCategoryNames(): Gets the names of a categories in a datablock
   Parameters: datablockName, the datablock in which the categories are
               numCategory, a pointer to a int which will have the number of
                 categories placed in it
   Return: the names of the categories or NULL for failure. numCategory also
           will contain the number of categories
   ------------------------------------------------------------------------- */
{
  int dataBlockIndex;
  dataBlockIndex = cifGetDataBlockIndex(datablockName);

  *numCategory = 0;
  if (dataBlockIndex != -1)
    return cifGetCategoryNamesByIndex(dataBlockIndex, numCategory);
  else return (char **) NULL;
}

char ** cifGetCategoryNamesByIndex(const int dataBlockIndex, int *numCategory)
/* -------------------------------------------------------------------------
   cifGetCategoryNamesByIndex(): Get list of categories for a Datablock
   Parameters: dataBlockIndex: which datablock to get the category list from
               numCategory: Address of a integer to get number of 
                               categories
   Return: The list of categories or NULL if fails. numCategory also contains
           the number of categories
   ------------------------------------------------------------------------- */
{
  char ** categories;
  int i;
  
  *numCategory = 0;
  categories = NULL;
  if (theCifFiles != (CifFile *) NULL && theCifFiles->CountDataBlocks() != 0 ) {
    *numCategory = theCifFiles->CountCategories(dataBlockIndex);
    if (*numCategory > 0) {
      categories = (char **) calloc(*numCategory +1, sizeof(char *));
      for (i=0; i<*numCategory; i++) 
	categories[i] = cifGetCategoryNameByIndex(dataBlockIndex, i);
    }
  }
  return categories;
}

int cifGetCategoryIndexByIndex(const int dataBlockIndex, const char *categoryName)
/* -------------------------------------------------------------------------
   cifGetCategoryIndexByIndex(): Get the index of a category
   Parameters: dataBlockIndex, the index of the datablock
               categoryName, the name of the category for which the index is
                 requested
   Return: The index of the category or -1 for failure
   ------------------------------------------------------------------------- */
{
  int categoryIndex;
  CifString message;

  categoryIndex = theCifFiles->GetCategoryIndex(dataBlockIndex, categoryName);
  if (categoryIndex == -1) {
    message.Clear();
    message += "Category \'";
    message += categoryName;
    message += "\' does not exist";
    theCifFiles->ErrorMessage(dataBlockIndex, CIF_ERROR, message.Text());
  }
  return categoryIndex;
}

int cifGetCategoryIndex(const char *dataBlockName, const char *categoryName)
/* -------------------------------------------------------------------------
   cifGetCategoryIndex(): Get the index of a category
   Parameters: dataBlockName, the name of the datablock
               categoryName, the name of the category for which the index is
                 requested
   Return: The index of the category or -1 for failure
   ------------------------------------------------------------------------- */
{
  int dataBlockIndex;
  
  dataBlockIndex = cifGetDataBlockIndex(dataBlockName);
  return cifGetCategoryIndexByIndex(dataBlockIndex, categoryName);
}

char **cifGetCategoryKeys(const char *dataBlockName, 
			  const char *categoryName,
			  int *numKey)
/* -------------------------------------------------------------------------
   cifGetCategoryKeys(): Gets a list of keys for a category
   Parameters: datablockName, categoryName, and numKey, the address in which
               the number of keys will be stored
   Return: the list of keys or NULL if failed. Number of keys returned in
           numKey 
   ------------------------------------------------------------------------- */
{
  int categoryIndex, dataBlockIndex;
  
  dataBlockIndex = cifGetDataBlockIndex(dataBlockName);
  categoryIndex = cifGetCategoryIndexByIndex(dataBlockIndex, categoryName);
  return cifGetCategoryKeysByIndex(dataBlockIndex, categoryIndex, numKey);
}

char **cifGetCategoryKeysByIndex(const int dataBlockIndex, 
				 const int categoryIndex,
				 int *numKey)
/* -------------------------------------------------------------------------
   cifGetCategoryKeysByIndex(): Gets a list of keys for a category
   Parameters: datablockIndex, categoryIndex, and numKey, the address in which
               the number of keys will be stored
   Return: the list of keys or NULL if failed. Number of keys returned in
           numKey 
   ------------------------------------------------------------------------- */
{
  char **keys;
  ReVarCifArray <CifString> *keylist;
  int i;

  keys = NULL;
  *numKey = 0;
  keylist = theCifFiles->GetCategoryKeys(dataBlockIndex,
					 categoryIndex);
  if (keylist != NULL && keylist->Length() > 0) {
    keys = (char **) calloc(keylist->Length()+1, sizeof(char *));
    *numKey = keylist->Length();
    for (i=0; i<keylist->Length(); i++) {
      keys[i] = (char *) calloc((*keylist)[i].Length()+1, sizeof(char));
      strcpy(keys[i], (*keylist)[i].Text());
    }
  }
  return keys;
}

int cifCountRows(const char *datablockName, const char *categoryName)
/* -------------------------------------------------------------------------
   cifCountRows(): Get number of rows in a category
   Parametrers: dataBlockIndex: which datablock to get the item list
                categoryIndex: which category to get the item list
   Return: Number of rows or -1 for failure
   ------------------------------------------------------------------------- */
{
  int dataBlockIndex, categoryIndex;
  dataBlockIndex = cifGetDataBlockIndex(datablockName);
  if (dataBlockIndex != -1)
    categoryIndex = cifGetCategoryIndexByIndex(dataBlockIndex, categoryName);
  if (dataBlockIndex != -1 && categoryIndex != -1)
    return cifCountRowsByIndex(dataBlockIndex, categoryIndex);
  else return 0;
}

int cifCountRowsByIndex(const int dataBlockIndex, const int categoryIndex)
/* -------------------------------------------------------------------------
   cifCountRowsByIndex(): Get number of rows from a category
   Parametrers: dataBlockIndex, which datablock to get the item list
                categoryIndex, which category to get the item list
   Return ItemValue: Number of rows or -1 for failure
   ------------------------------------------------------------------------- */
{
  return theCifFiles->CountRows(dataBlockIndex, categoryIndex);
}

int cifCountColumns(const char *datablockName, const char *categoryName)
/* -------------------------------------------------------------------------
   cifCountColumns(): Get number of columns in a category
   Parametrers: dataBlockIndex: which datablock to get the item list
                categoryIndex: which category to get the item list
   Return: Number of columns or -1 for failure
   ------------------------------------------------------------------------- */
{
  int dataBlockIndex, categoryIndex;

  dataBlockIndex = cifGetDataBlockIndex(datablockName);
  categoryIndex = cifGetCategoryIndexByIndex(dataBlockIndex, categoryName);
  return cifCountColumnsByIndex(dataBlockIndex, categoryIndex);
}

int cifCountColumnsByIndex(const int dataBlockIndex, const int categoryIndex)
/* -------------------------------------------------------------------------
   cifCountColumnsByIndex(): Get number of columns from a category
   Parametrers: dataBlockIndex, which datablock to get the column from
                categoryIndex, which category to get the column from
   Return ItemValue: Number of columns or -1 for failure
   ------------------------------------------------------------------------- */
{
  return theCifFiles->CountItem(dataBlockIndex, categoryIndex);
}


/*
 *   Functions which provide information of operate on category groups...
 */

char ** cifGetCategoryGroups(const char *datablockName, int *numGroups)
/* -------------------------------------------------------------------------
   cifGetCategoryGroups(): Gets category group list
   Parameters: datablockName
               numGroups: The address to keep number of values
   Return: The list of category groups in the datablock or NULL if fails
   ------------------------------------------------------------------------- */
{
  char ** values;

  values = NULL;
  cifGetColumn(&values, datablockName,
			 CIF_DDL_CATEGORY_CATEGORY_GROUP_LIST,
			 CIF_DDL_ITEM_ID, numGroups);
  return values;
}

char ** cifGetCategoryGroupsByIndex(const int dataBlockIndex, int *numGroups)
/* -------------------------------------------------------------------------
   cifGetCategoryGroupsByIndex():  Gets category group list
   Parameters: dataBlockIndex
               numGroups: The address to keep number of values
   Return: The list of category groups in the datablock or NULL if fails
   ------------------------------------------------------------------------- */
{
  char **list;
  CifCategoryIndex *tableIndex;

  *numGroups = 0;
  list = NULL;
  tableIndex = theCifFiles->GetTableIndex(dataBlockIndex,
					  CIF_DDL_CATEGORY_CATEGORY_GROUP_LIST,
					  CIF_DDL_ITEM_ID); 
  if (tableIndex == NULL) return NULL;
  else {
    cifGetColumnByIndex(&list, dataBlockIndex,
				  tableIndex->GetCategoryIndex() ,
				  tableIndex->GetItemIndex(), numGroups);
    delete tableIndex;
    return list;
  }
}

char ** cifGetCategoriesInCategoryGroup(const char *datablockName, 
			       	   const char *groupName, int *numCategory)
/* -------------------------------------------------------------------------
   cifGetCategoriesInCategoryGroup():  Gets a list of category names for
                                       a particular categoryGroup
   Parameters: datablockName
               groupName, the category group name
               numCategory, the address of the number of categories
   Return: The list of category names or NULL if fails. numCategory will
           contain the number of categories
   ------------------------------------------------------------------------- */
{

  int i, idIndex, ret;
  CifItemValue  *matchedValue;
  CifString *itemValue, categoryId(CIF_DDL_ITEM_CATEGORY_ID), id(CIF_DDL_ITEM_ID);
  CifString categoryGroup(CIF_DDL_CATEGORY_CATEGORY_GROUP), name;
  CifCategory *categoryNameTable;

  char **list = NULL;;
  *numCategory = 0;
			      
  matchedValue = new CifItemValue(CIF_STRING_VALUE, FALSE, CIF_DATA_IS_VALID, 
				  groupName);
  categoryNameTable = theCifFiles->GetItemAttribute(datablockName,
						    matchedValue, 
						    id,
						    categoryGroup,
						    categoryId);
  if (categoryNameTable != NULL) {
    *numCategory = categoryNameTable->CountRows();
    list = (char **) calloc(*numCategory, sizeof(char *));
    JoinCategoryItemName(CIF_DDL_CATEGORY_CATEGORY_GROUP, CIF_DDL_ITEM_CATEGORY_ID,
			 &name);
    idIndex = categoryNameTable->GetIndex(name);
    for (i=0; i< *numCategory; i++) {
      ret = categoryNameTable->GetItemValue(&itemValue, idIndex, i);
      if (ret == CIF_DATA_IS_VALID) {
	list[i] = (char *) calloc(itemValue->Length()+1, sizeof(char));
	strcpy(list[i], itemValue->Text());
      }
      else  list[i] = (char *) calloc(1, sizeof(char));
    }
    delete categoryNameTable;
  }
  return list;
}


/*
 *   Functions which provide information or operate on subcategories ... 
 */

char ** cifGetSubcategories(const char *dataBlockName, 
			    const char *categoryName,
			    int *numSubGroups)
/* -------------------------------------------------------------------------
   cifGetSubcategories(): Gets a list of subcategory names for
                                  a particular category
   Parameters: dataBlockName
               categoryName
               numSubGroups: The address to keep number of subcategories
   Return: The list of subcategories or NULL if fails. numSubGroups will also
           return the number of subcategories
   ------------------------------------------------------------------------- */
{
  char **list;
  int i, j, ret, nrows, dataBlockIndex;
  CifString *idValue, *nameValue;
  CifCategoryIndex *tableIndex1, *tableIndex2;

  list = NULL;
  *numSubGroups = 0;

  if (categoryName == NULL) return NULL;
  dataBlockIndex = cifGetDataBlockIndex(dataBlockName);
  tableIndex1 = theCifFiles->GetTableIndex(dataBlockIndex,
					   CIF_DDL_CATEGORY_ITEM_SUB_CATEGORY,
					   CIF_DDL_ITEM_ID); 
  tableIndex2 = theCifFiles->GetTableIndex(dataBlockIndex,
					   CIF_DDL_CATEGORY_ITEM_SUB_CATEGORY,
					   CIF_DDL_ITEM_NAME); 
  
  *numSubGroups = 1;
  list = (char **) calloc(1, sizeof(char *));
  list[0] = (char *) calloc(strlen(categoryName)+1, sizeof(char));
  strcpy(list[0] , categoryName);
  if (tableIndex1 != NULL && tableIndex2 != NULL) {
    nrows = theCifFiles->CountRows(dataBlockIndex, tableIndex1->GetCategoryIndex() );
    for (i = 0; i < nrows; i++) {
      ret = theCifFiles->GetItemValue(&nameValue, dataBlockIndex,
				      tableIndex2->GetCategoryIndex(),
				      tableIndex2->GetItemIndex(), i);
      
      if (ret == CIF_DATA_IS_VALID && 
	  nameValue->Length() > strlen(categoryName) + 2 &&
	  !CompareNoCase(categoryName, &nameValue->Text()[1], 
			 strlen(categoryName)) &&
	  nameValue->Text()[1+strlen(categoryName)] == '.'  ) {
	ret = theCifFiles->GetItemValue(&idValue, dataBlockIndex,
					tableIndex1->GetCategoryIndex() ,
					tableIndex1->GetItemIndex(), i);
	for (j=0; j<*numSubGroups; j++) 
	  if (!strcmp(idValue->Text(), list[j])) break;
	if (j == *numSubGroups) {
	  (*numSubGroups)++;
	  list = (char **) realloc(list, *numSubGroups*sizeof(char *));
	  list[j] =(char *) calloc( idValue->Length()+1, sizeof(char));
	  strcpy(list[j], idValue->Text());
	}
      }
    }
  }
  if (tableIndex1 != NULL) delete tableIndex1;
  if (tableIndex2 != NULL) delete tableIndex2;
  return list;
}
char ** cifGetItemNamesInSubcategory(const char *dataBlockName,
				       const char *categoryName,
				       const char *subCategory,
				       int *numItemNames)
/* -------------------------------------------------------------------------
   cifGetItemNamesInSubcategoryByIndex():  Gets a list of item names for 
                        a particular category and sub category group
                        if categoryName and subCateGroup name are the same,
                        give the list of whole categoryName
   Parameters: const int dataBlockIndex,
               const char *categoryName,
               const char *subCategory,
               *numGroups: The address to keep number of numGroups
   Return itemValue: The list of item names or NULL if fails
   ------------------------------------------------------------------------- */
{
  char **list;
  int i, ret, nrows, dataBlockIndex;
  CifString *idValue, *nameValue;
  CifCategoryIndex *tableIndex1, *tableIndex2;


  *numItemNames= 0;
  list = NULL;
  if (categoryName == NULL) 
    return NULL;
  dataBlockIndex = cifGetDataBlockIndex(dataBlockName);

  tableIndex1 = theCifFiles->GetTableIndex(dataBlockIndex,
					   CIF_DDL_CATEGORY_ITEM_SUB_CATEGORY,
					   CIF_DDL_ITEM_ID); 

  tableIndex2 = theCifFiles->GetTableIndex(dataBlockIndex,
					   CIF_DDL_CATEGORY_ITEM_SUB_CATEGORY,
					   CIF_DDL_ITEM_NAME); 
  if (tableIndex1 != NULL && tableIndex2 != NULL) {
    nrows = theCifFiles->CountRows(dataBlockIndex, tableIndex1->GetCategoryIndex() );
    for (i=0; i<nrows; i++) {
      ret = theCifFiles->GetItemValue(&idValue, dataBlockIndex,
				      tableIndex1->GetCategoryIndex() ,
				      tableIndex1->GetItemIndex(), i);
      
      if (ret == CIF_DATA_IS_VALID && 
	  (!strcmp(categoryName, subCategory) ||
	   !Compare(subCategory, idValue->Text()))) {
	ret = theCifFiles->GetItemValue(&nameValue, dataBlockIndex,
					tableIndex2->GetCategoryIndex() ,
					tableIndex2->GetItemIndex(), i);
	if (ret == CIF_DATA_IS_VALID &&
	    nameValue->Length() > strlen(categoryName)+2 &&
	    !CompareNoCase(categoryName, &nameValue->Text()[1], 
			   strlen(categoryName)) &&
	    nameValue->Text()[1+strlen(categoryName)] == '.'  ) {
	  list = (char **) realloc(list, (*numItemNames+1)*sizeof(char *));
	  list[*numItemNames] =(char *) calloc( nameValue->Length()+1, sizeof(char));
	  strcpy(list[*numItemNames], nameValue->Text());
	  (*numItemNames)++;
	}
      }
    }
  }
  if (tableIndex1 != NULL) delete tableIndex1;
  if (tableIndex2 != NULL) delete tableIndex2;
  return list;
}

char ** cifGetItemKeywordsInSubcategory(const char *dataBlockName,
					const char *categoryName,
					const char *subCategory,
					int *numItemName)
/* -------------------------------------------------------------------------
   cifGetItemNamesInSubcategoryByIndex():  Gets a list of item names for 
                        a particular category and sub category group
                        if categoryName and subCateGroup name are the same,
                        give the list of whole categoryName
   Parameters: const int dataBlockIndex,
               const char *categoryName,
               const char *subCategory,
               *numGroups: The address to keep number of numGroups
   Return itemValue: The list of item names or NULL if fails
   ------------------------------------------------------------------------- */
{
  char **list;
  int i, ret, nrows, dataBlockIndex;
  CifString *idValue, *nameValue;
  CifCategoryIndex *tableIndex1, *tableIndex2;

  *numItemName= 0;
  list = NULL;

  if (categoryName == NULL) 
    return NULL;

  dataBlockIndex = cifGetDataBlockIndex(dataBlockName);
  tableIndex1 = theCifFiles->GetTableIndex(dataBlockIndex,
					   CIF_DDL_CATEGORY_ITEM_SUB_CATEGORY,
					   CIF_DDL_ITEM_ID); 

  tableIndex2 = theCifFiles->GetTableIndex(dataBlockIndex,
					   CIF_DDL_CATEGORY_ITEM_SUB_CATEGORY,
					   CIF_DDL_ITEM_NAME); 
  if (tableIndex1 != NULL && tableIndex2 != NULL) {
    nrows = theCifFiles->CountRows(dataBlockIndex, tableIndex1->GetCategoryIndex() );
    for (i=0; i<nrows; i++) {
      ret = theCifFiles->GetItemValue(&idValue, dataBlockIndex,
				      tableIndex1->GetCategoryIndex() ,
				      tableIndex1->GetItemIndex(), i);
      
      if (ret == CIF_DATA_IS_VALID && 
	  (!strcmp(categoryName, subCategory) ||
	   !Compare(subCategory, idValue->Text()))) {
	ret = theCifFiles->GetItemValue(&nameValue, dataBlockIndex,
					tableIndex2->GetCategoryIndex() ,
					tableIndex2->GetItemIndex(), i);
	if (ret == CIF_DATA_IS_VALID &&
	    nameValue->Length() > strlen(categoryName)+2 &&
	    !CompareNoCase(categoryName, &nameValue->Text()[1], 
			   strlen(categoryName)) &&
	    nameValue->Text()[1+strlen(categoryName)] == '.'  ) {
	  list = (char **) realloc(list, (*numItemName+1)*sizeof(char *));
	  list[*numItemName] =(char *) calloc( nameValue->Length(), sizeof(char));
	  strcpy(list[*numItemName], &nameValue->Text()[2+strlen(categoryName)]);
	  (*numItemName)++;
	}
      }
    }
  }
  if (tableIndex1 != NULL) delete tableIndex1;
  if (tableIndex2 != NULL) delete tableIndex2;
  return list;
}


/*
 *  Functions which provide information about items ..
 */
char * cifGetItemKeyword(const char *datablockName, const char *categoryName, 
			 const int itemKeywordIndex)
/* -------------------------------------------------------------------------
   cifGetItemKeyword(): returns the item keyword identified by its index
                     within the target category within the target datablock
   Parameters: datablockName and categoryNames
   Return: the item keyword of NULL if fails
   ------------------------------------------------------------------------- */
{
  int dataBlockIndex, categoryIndex;
  dataBlockIndex = cifGetDataBlockIndex(datablockName);
  if (dataBlockIndex != -1)
    categoryIndex = cifGetCategoryIndexByIndex(dataBlockIndex, categoryName);
  if (dataBlockIndex != -1 && categoryIndex != -1)
    return cifGetItemKeywordByIndex(dataBlockIndex, categoryIndex, itemKeywordIndex);
  else return (char *) NULL;
}

char * cifGetItemKeywordByIndex(const int dataBlockIndex,
                        const int categoryIndex, const int itemKeywordIndex)
/* -------------------------------------------------------------------------
   cifGetItemKeywordByIndex(): returns the item keyword identified by its index
                     within the target category within the target datablock
   Parameters: datablockIndex and categoryIndex
   Return: the item keyword of NULL if fails
   ------------------------------------------------------------------------- */
{
  CifString *itemKeyword;
  char *keyword;

  keyword = NULL;
  itemKeyword = theCifFiles->GetItemKeyword(dataBlockIndex, categoryIndex, itemKeywordIndex);
  if (itemKeyword != (CifString *) NULL ) {
    keyword = (char *) calloc(itemKeyword->Length()+1, sizeof(char));
    strcpy(keyword ,itemKeyword->Text());
  }
  return keyword;
}

char ** cifGetItemKeywords(const char *datablockName,
                           const char *categoryName, int *numItemKeyword)
/* -------------------------------------------------------------------------
   cifGetItemKeywords(): returns the item keywords identified by the target
                         category within the target datablock
   Parameters: datablockName, categoryName, and numItemKeyword, an address
               where the number of keywords can be put
   Return: the item keywords of NULL if fails. Number of keywords is returned 
           via numItemKeyword
   ------------------------------------------------------------------------- */
{
  int dataBlockIndex, categoryIndex;
  dataBlockIndex = cifGetDataBlockIndex(datablockName);
  if (dataBlockIndex != -1)
    categoryIndex = cifGetCategoryIndexByIndex(dataBlockIndex, categoryName);
  if (dataBlockIndex != -1 && categoryIndex != -1)
    return cifGetItemKeywordsByIndex(dataBlockIndex, categoryIndex, numItemKeyword);
  else return (char **) NULL;
}

char ** cifGetItemKeywordsByIndex(const int dataBlockIndex, const int 
                                  categoryIndex, int *numItemKeywords)
/* -------------------------------------------------------------------------
   cifGetItemKeywordsByIndex(): returns the item keywords identified by the
                                target category within the target datablock
   Parameters: datablockIndex, categoryIndex, and numItemKeywords, an address
               where the number of keywords can be put
   Return: the item keywords of NULL if fails. Number of keywords is returned 
           via numItemKeywords
   ------------------------------------------------------------------------- */
{
  char **itemKeywords;
  int i;
  
  *numItemKeywords = 0;
  itemKeywords = NULL;
  if (theCifFiles != (CifFile *) NULL && theCifFiles->CountDataBlocks() != 0)
  {
    *numItemKeywords = theCifFiles->CountItem(dataBlockIndex, categoryIndex);
    if (*numItemKeywords > 0) {
      itemKeywords = (char **) calloc(*numItemKeywords+1, sizeof(char *));
      for (i = 0; i < *numItemKeywords; i++) 
	itemKeywords[i] = cifGetItemKeywordByIndex(dataBlockIndex,
                          categoryIndex, i);
    }
  }
  return itemKeywords;
}

int cifGetItemIndexByIndex(const int dataBlockIndex, const int categoryIndex, 
			   const char *itemKeyword)
/* -------------------------------------------------------------------------
   cifGetItemIndexByIndex(): Gets the index of the given item in the given
                             category, in the given datablock
   Parameters: dataBlockIndex, categoryIndex, and itemKeyword, which is the
               keyword of the item.
   Return: item index or -1 if failure
   ------------------------------------------------------------------------- */
{
  return theCifFiles->GetItemIndex(dataBlockIndex, categoryIndex, itemKeyword);
    
}

int cifGetItemIndexByName(const char *dataBlockName, const char *itemName)
/* -------------------------------------------------------------------------
   cifGetItemIndexByName(): Gets the index of the given item in the given
                            datablock
   Parameters: dataBlockName, and itemName, which is the complete item name,
               including category prefix
   Return: item index or -1 if failure
   ------------------------------------------------------------------------- */
{
  CifCategoryIndex *tableIndex;

  int dataBlockIndex = cifGetDataBlockIndex(dataBlockName);
  tableIndex = theCifFiles->GetTableIndex(dataBlockIndex, itemName);
  if (tableIndex == NULL) return -1;
  else return (tableIndex->GetItemIndex());
}

int cifGetItemIndex(const char *dataBlockName,
		    const char *categoryName,
		    const char *itemKeyword)
/* -------------------------------------------------------------------------
   cifGetItemIndex(): Gets the index of the given item in the given
                      category in the given datablock
   Parameters: dataBlockName, catgoryName, and the itemKeyword, which is the
               keyword of the item (column name)
   Return: item index or -1 if failure
   ------------------------------------------------------------------------- */
{
  CifCategoryIndex *tableIndex;

  int dataBlockIndex = cifGetDataBlockIndex(dataBlockName);
  tableIndex = theCifFiles->GetTableIndex(dataBlockIndex, categoryName, itemKeyword);
  if (tableIndex == NULL) return -1;
  else return  (tableIndex->GetItemIndex());
}

char *cifGetItemName(const char *datablockName, const char *categoryName, 
		      const int itemNameIndex)
/* -------------------------------------------------------------------------
   cifGetItemName(): Gets the full name of the given item in the given
                      category in the given datablock
   Parameters: dataBlockName, catgoryName, and the itemNameIndex, which is the
               index of the item
   Return: item index or -1 if failure
   ------------------------------------------------------------------------- */
{
  int dataBlockIndex, categoryIndex;
  dataBlockIndex = cifGetDataBlockIndex(datablockName);
  if (dataBlockIndex != -1)
    categoryIndex = cifGetCategoryIndexByIndex(dataBlockIndex, categoryName);
  if (dataBlockIndex != -1 && categoryIndex != -1)
    return cifGetItemNameByIndex(dataBlockIndex, categoryIndex, itemNameIndex);
  else return (char *) NULL;
}

char * cifGetItemNameByIndex(const int dataBlockIndex, const int
                             categoryIndex, const int itemNameIndex)
/* -------------------------------------------------------------------------
   cifGetItemNameByIndex(): Gets the full name of the given item in the given
                      category in the given datablock
   Parameters: dataBlockIndex, catgoryIndex, and the itemNameIndex, which is
               the index of the item
   Return: item name or NULL if failure
   ------------------------------------------------------------------------- */
{
  CifString *itemName;
  char *name;
  name = NULL;

  itemName = theCifFiles->GetItemName(dataBlockIndex, categoryIndex, itemNameIndex);
  if (itemName != (CifString *) NULL ) {
    name = (char *) calloc(itemName->Length()+1, sizeof(char));
    strcpy(name, itemName->Text());
  }
  return name;
}

char ** cifGetItemNames(const char *datablockName, const char *categoryName, 
			int *numItemName)
/* -------------------------------------------------------------------------
   cifGetItemNames(): Gets a list of the full names of the given list of items
                      in the given category in the given datablock
   Parameters: dataBlockName, catgoryName, and the numItemName, which will
               point to the number of items.
   Return: item names or NULL if failure. numItemName will contain the number
           of items
   ------------------------------------------------------------------------- */
{
  int dataBlockIndex, categoryIndex;
  dataBlockIndex = cifGetDataBlockIndex(datablockName);
  if (dataBlockIndex != -1)
    categoryIndex = cifGetCategoryIndexByIndex(dataBlockIndex, categoryName);
  if (dataBlockIndex != -1 && categoryIndex != -1)
    return cifGetItemNamesByIndex(dataBlockIndex, categoryIndex, numItemName);
  else return (char **) NULL;
}

char ** cifGetItemNamesByIndex(const int dataBlockIndex, const int
                               categoryIndex, int *numItemName)
/* -------------------------------------------------------------------------
   cifGetItemNamesByIndex(): Get list of item names for a certain category
                             in a certain data block
   Parameters: dataBlockIndex, which data block to get the list from
               categoryIndex, which category to get the list from
               *numItemName, address of an integer to get number of keywords
   Return: the list of itemNames or NULL if fails
   ------------------------------------------------------------------------- */
{
  char **itemNames;
  int i;
  
  *numItemName = 0;
  itemNames = NULL;
  if (theCifFiles != (CifFile *) NULL && theCifFiles->CountDataBlocks() != 0)
  {
    *numItemName = theCifFiles->CountItem(dataBlockIndex, categoryIndex);
    if (*numItemName > 0 ) {
      itemNames = (char **) calloc(*numItemName+1, sizeof(char *));
      for (i = 0; i < *numItemName; i++) 
	itemNames[i] = cifGetItemNameByIndex(dataBlockIndex, categoryIndex, i);
    }
  }
  return itemNames;
}

char * cifGetItemAliasName(const char *datablockName, const char *
                           categoryName, const char *itemKeyword)
/* -------------------------------------------------------------------------
   cifGetItemAliasName(): Get an alias name for a certain item
   Parameters: dataBlockName, which data block to get the alias from
               categoryName, which category to get the alias from
               itemKeyword, the item keyword (column name)
   Return: the alias name or NULL for failure
   ------------------------------------------------------------------------- */
{
  int dataBlockIndex;
  CifCategoryIndex *tableIndex;
  
  dataBlockIndex = cifGetDataBlockIndex(datablockName);
  if (dataBlockIndex != -1)
    tableIndex = theCifFiles->GetTableIndex(dataBlockIndex, categoryName, itemKeyword);
  if (dataBlockIndex != -1 && tableIndex != NULL) 
    return cifGetItemAliasNameByIndex(dataBlockIndex, 
				      tableIndex->GetCategoryIndex(), 
				      tableIndex->GetItemIndex());
  
  else return (char *) NULL;
}

char * cifGetItemAliasNameByIndex(const int dataBlockIndex, const int
                                  categoryIndex, const int itemNameIndex)
/* -------------------------------------------------------------------------
   cifGetItemAliasNameByIndex(): Get an alias name for a certain item
   Parameters: dataBlockIndex, which data block to get the alias from
               categoryIndex, which category to get the alias from
               itemNameIndex, which item to get the alias for 
   Return: the alias name or NULL for failure
   ------------------------------------------------------------------------- */
{
  CifString *itemAliasName;
  char *aliasName;

  aliasName = NULL;

  itemAliasName = theCifFiles->GetItemAliasName(dataBlockIndex, categoryIndex, itemNameIndex);
  if (itemAliasName == NULL)
    itemAliasName = theCifFiles->GetItemName(dataBlockIndex, categoryIndex, itemNameIndex);
  if (itemAliasName != (CifString *) NULL ) {
    aliasName = (char *) calloc(itemAliasName->Length()+1, sizeof(char));
    strcpy(aliasName, itemAliasName->Text());
  }
  return aliasName;
}

char ** cifGetItemAliasNames(const char *datablockName, const char *
                             categoryName, int *numItemKeyword)
/* -------------------------------------------------------------------------
   cifGetItemAliasNames(): Get a list of alias names for a certain category
   Parameters: dataBlockName, which data block to get the alias list from
               categoryName, which category to get the alias list from
               numItemKeyword, address to store the number of aliases
               (keywords) 
   Return: the list of alias names or NULL for failure
   ------------------------------------------------------------------------- */
{
  int dataBlockIndex, categoryIndex;
  dataBlockIndex = cifGetDataBlockIndex(datablockName);
  if (dataBlockIndex != -1)
    categoryIndex = cifGetCategoryIndexByIndex(dataBlockIndex, categoryName);
  if (dataBlockIndex != -1 && categoryIndex != -1)
    return cifGetItemAliasNamesByIndex(dataBlockIndex, categoryIndex, numItemKeyword);
  else return (char **) NULL;
}

char ** cifGetItemAliasNamesByIndex(const int dataBlockIndex, 
				    const int categoryIndex, int *numAliasName)
/* -------------------------------------------------------------------------
   cifGetItemAliasNamesByIndex(): Get alias names of item list for a 
                 certain category in a certain datablock
   Parameters: dataBlockIndex, the datablock to get the keyword list
               categoryIndex, the category to get the keyword list
               numAliasName, address of an integer to get number of keywords
   Return: The list of keywords or NULL if fails
   ------------------------------------------------------------------------- */
{
  char **itemAliasNames;
  int i;
  
  *numAliasName = theCifFiles->CountItem(dataBlockIndex, categoryIndex);
  itemAliasNames = (char **) NULL;
  if (*numAliasName != 0) {
    itemAliasNames = (char **) calloc(*numAliasName+1, sizeof(char *));
    for (i=0; i< *numAliasName; i++) 
      itemAliasNames[i] = cifGetItemAliasNameByIndex(dataBlockIndex, categoryIndex, i);
  }
  return itemAliasNames;
}


/* 
 * Functions which get item values ... 
 */

int cifGetItemValueByIndex(char **itemValue, const int dataBlockIndex,
			const int categoryIndex, const int itemIndex, 
			const int rowIndex)
/* -------------------------------------------------------------------------
   cifGetItemValueByIndex() Gets an itemValue by giving indices
   Parameters: itemValue the address of the value
               dataBlockIndex, categoryIndex, itemIndex (column#), rowIndex 
   Return: SUCCEED or error code if failed
   ------------------------------------------------------------------------- */
{
  CifItemValue *cifItemValue;
  char *tmpItemValue = NULL;
  int ret;

  *itemValue = NULL;  
  ret = theCifFiles->GetItemValue(&cifItemValue, dataBlockIndex,
                                  categoryIndex, itemIndex, rowIndex);

  if (cifItemValue != NULL) {
    tmpItemValue = (char *) calloc(cifItemValue->_buf.Length() +1, sizeof(char));
    strcpy(tmpItemValue, cifItemValue->_buf.Text());
    *itemValue = tmpItemValue;
  }
  return ret;
}

int cifGetItemValue(char **itemValue, const char *DatablockName, const char *
                    categoryName, const char *itemKeyword, const int rowIndex)
/* -------------------------------------------------------------------------
   cifGetItemValue() Gets an itemValue by datablock, category, and item names
   Parameters: itemValue, the address of the value
               DatablockName and categoryName
               itemKeyword, the name of the column (keyword)
               rowIndex, the row number
   Return: SUCCEED or error code if failed
   ------------------------------------------------------------------------- */
{
  CifString *matrixItemValue;
  char *tmpItemValue;
  int ret, StructureNumValue, dataBlockIndex;
  CifString item(itemKeyword), category(categoryName);
  CifCategoryIndex *tableIndex;
  
  *itemValue = NULL;
  dataBlockIndex = cifGetDataBlockIndex(DatablockName);
  tableIndex = theCifFiles->GetTableIndex(dataBlockIndex, categoryName, itemKeyword);
  
  if (tableIndex == NULL) return CIF_KEYWORD_NOT_EXIST;
  else {
    StructureNumValue = 
      theCifFiles->GetItemStructureNumValue(dataBlockIndex, 
					     tableIndex->GetCategoryIndex(), 
					     tableIndex->GetItemIndex());
    if (StructureNumValue == 1) 
      return cifGetItemValueByIndex(itemValue, dataBlockIndex, 
				tableIndex->GetCategoryIndex(),
				tableIndex->GetItemIndex(), rowIndex);
    // For indexed multi-dimension itemValues 
    // Does not make sense yet, need more work here
    else { 
      ret = theCifFiles->GetItemValue(&matrixItemValue, dataBlockIndex, category, item, rowIndex);
      if (ret == CIF_DATA_IS_VALID) {
	tmpItemValue =  (char *) calloc(matrixItemValue->Length() +1, sizeof(char));
	strcpy(tmpItemValue,  matrixItemValue->Text());
	*itemValue = tmpItemValue;
      }
    }
  }
  return ret;
}

int  cifGetItemValueByAlias(char **itemValue, const int dataBlockIndex,
			    const char *aliasName, const int rowIndex)
/* -------------------------------------------------------------------------
   cifGetItemValueByAlias() Gets an itemValue by alias name
   Parameters: itemValue, the address of the item value
               dataBlockIndex, rowIndex
               aliasName, can be an item name or alias name
   Return: SUCCEED or error code if failed
   ------------------------------------------------------------------------- */
{
  CifCategoryIndex *tableIndex;
  
  tableIndex = theCifFiles->GetTableIndex(dataBlockIndex, aliasName);
  if (tableIndex == NULL)
    return CIF_ALIAS_NOT_EXIST;
  else 
    return cifGetItemValueByIndex(itemValue, dataBlockIndex, 
				  tableIndex->GetCategoryIndex(),
				  tableIndex->GetItemIndex(), rowIndex);
}

int* cifGetRowByIndex(char ***itemValue, const int dataBlockIndex,
			     const int categoryIndex, 
			     const int rowIndex, int *numItemValue)
/* -------------------------------------------------------------------------
   cifGetRowByIndex() Gets a row of itemValues by indices
   Parameters: itemValue, the address of the destination of the values
               dataBlockIndex, categoryIndex, rowIndex 
               numItemValue : the address of the number of items in the
                              itemValues array
   Return: array of SUCCEEDs or error codes 
   ------------------------------------------------------------------------- */
{
  CifItemValue *cifItemValue;
  char *tmpItemValue, **val;
  int *ret, tmpRet, i;
  
  
  *itemValue = NULL;
  ret = NULL;
  *numItemValue = theCifFiles->CountItem(dataBlockIndex, categoryIndex);
  if (*numItemValue > 0) {
    val = (char **) calloc(*numItemValue+1, sizeof(char *));
    ret = (int *) calloc(*numItemValue+1, sizeof(int));
    for (i=0; i< *numItemValue; i++) {
      tmpRet = theCifFiles->GetItemValue(&cifItemValue, dataBlockIndex,
                                          categoryIndex, i, rowIndex);

      tmpItemValue = (char *) calloc(cifItemValue->_buf.Length() +1, sizeof(char));
      strcpy(tmpItemValue, cifItemValue->_buf.Text());
      val[i] = tmpItemValue;
      ret[i] = tmpRet;
    }
  }
  *itemValue = val;
  return ret;
}

int* cifGetRow(char ***itemValue, const char *DatablockName,
		      const char *categoryName,
		      const int rowIndex, int *numItemValue)
/* -------------------------------------------------------------------------
   cifGetRow() Gets a row of itemValues from given category
   Parameters: itemValue, the address of the destination of the values
              dataBlockIndex, rowIndex, categoryName
              numItemValue : the address of the number of items in the
                              itemValues array
   Return: an array of SUCCEEDs or error codes 
   ------------------------------------------------------------------------- */
{
  int categoryIndex, dataBlockIndex;
  
  *numItemValue = 0;
  dataBlockIndex = cifGetDataBlockIndex(DatablockName);
  categoryIndex = cifGetCategoryIndexByIndex(dataBlockIndex, categoryName);
  return cifGetRowByIndex(itemValue, dataBlockIndex, categoryIndex, rowIndex, 
			     numItemValue);
}

int* cifGetColumn(char ***itemValue, const char *datablockName, const char *
                          categoryName, const char *itemName, int *numValue)
/* -------------------------------------------------------------------------
   cifGetColumn(): Get a column of values from a category table
   Parameters: datablockName, categoryName, itemName
               numValue, the address to keep number of values
   Return: array of SUCCEEDS or NULL
   ------------------------------------------------------------------------- */
{
  int dataBlockIndex, *rets;
  CifCategoryIndex *tableIndex;

  *numValue = 0;
  *itemValue = NULL;
  dataBlockIndex = cifGetDataBlockIndex(datablockName);
  tableIndex = theCifFiles->GetTableIndex(dataBlockIndex, categoryName, itemName);
  if (tableIndex == NULL) return NULL;
  else {
    rets = cifGetColumnByIndex(itemValue, dataBlockIndex, 
			       tableIndex->GetCategoryIndex(),
			       tableIndex->GetItemIndex(),  numValue);
    delete tableIndex;
    return rets;
  }
}

int* cifGetColumnByIndex(char ***itemValues, const int dataBlockIndex, const
                         int categoryIndex, const int itemIndex, int *numValue)
/* -------------------------------------------------------------------------
   cifGetColumnByIndex(): Get a column of values from a category table
   Parameters: dataBlockIndex, categoryIndex, itemIndex
               numValue, the address to keep number of values
   Return: The list of values or NULL if fails
   ------------------------------------------------------------------------- */
{
  ReVarCifArray <CifItemValue> *values;
  int i, *rets;
  *numValue = 0;
  *itemValues = NULL;

  values = theCifFiles->GetColumnItemValues(dataBlockIndex,
					    categoryIndex,
					    itemIndex);
  if (values == NULL) 
    return NULL;
  else {
    *numValue = values->Length();
    *itemValues = (char **) calloc(*numValue, sizeof(char *));
    rets = (int *) calloc(*numValue, sizeof(int));
    for (i = 0; i< *numValue; i++) {
      (*itemValues)[i] = (char *) calloc( (*values)[i]._buf.Length()+1, sizeof(char));
      strcpy((*itemValues)[i], (*values)[i]._buf.Text());
      rets[i] = (*values)[i]._status;
    }
  }
  return rets;
}


/*  
 * Functions which update item values ...
 */

int cifUpdateItemValueByIndex(const char *itemValue, const int dataBlockIndex,
			       const int categoryIndex, const int itemIndex, 
			       const int rowIndex)
/* -------------------------------------------------------------------------
   cifUpdateItemValueByIndex() Updates an item value by giving indices
   Parameters: itemValue, the new value
               dataBlockIndex, categoryIndex, itemIndex, rowIndex  
   Return: SUCCEED or error code if failed
   ------------------------------------------------------------------------- */
{
  CifItemValue *cifItemValue = NULL;
  int ret, caseSensitivity, dataType;
  
  dataType = theCifFiles->GetItemDataType(dataBlockIndex, categoryIndex, itemIndex);
  caseSensitivity = 
    theCifFiles->GetItemCaseSensitivity(dataBlockIndex, categoryIndex, itemIndex);
  if (itemValue == (char *) NULL || !strcmp(itemValue,""))
    cifItemValue = new CifItemValue(dataType,  caseSensitivity, CIF_DATA_IS_NULL, NULL);
  else if (dataType == CIF_INTEGER_VALUE)
    cifItemValue = new CifItemValue(CIF_DOUBLE_VALUE, caseSensitivity, CIF_DATA_IS_VALID, itemValue);
  else if (dataType == CIF_DOUBLE_VALUE)
    cifItemValue = new CifItemValue(CIF_DOUBLE_VALUE, caseSensitivity, CIF_DATA_IS_VALID, itemValue);
  else if (dataType == CIF_STRING_VALUE) {
    if (!strcmp(itemValue,"."))
      cifItemValue = new CifItemValue(CIF_STRING_VALUE, caseSensitivity, 
				      CIF_DATA_NOT_APPROPRIATE, itemValue);
    else
      cifItemValue = new CifItemValue(CIF_STRING_VALUE, caseSensitivity, 
				      CIF_DATA_IS_VALID, itemValue);
  }
  else return CIF_DATA_TYPE_MISMATCH;
  
  ret =theCifFiles->UpdateItemValue(cifItemValue, dataBlockIndex, categoryIndex, itemIndex, rowIndex, TRUE);
  delete cifItemValue;
  return ret;
}

int cifUpdateItemValue(const char *itemValue, const char *datablockName, 
		       const char *categoryName, const char *itemKeyword,
                       const int rowIndex)
/* -------------------------------------------------------------------------
   cifUpdateItemValue() Updates an item value by names
   Parameters: itemValue, the new value
              dataBlockName, categoryname, itemKeyword, rowIndex
   Return: SUCCEED or error code if failed
   ------------------------------------------------------------------------- */
{
  CifCategoryIndex *tableIndex;
  int dataBlockIndex;
  
  dataBlockIndex =  cifGetDataBlockIndex(datablockName);
  
  tableIndex = theCifFiles->GetTableIndex(dataBlockIndex, categoryName, itemKeyword);
  if (tableIndex == NULL)
    return CIF_KEYWORD_NOT_EXIST;
  else 
    return cifUpdateItemValueByIndex(itemValue, dataBlockIndex, 
				     tableIndex->GetCategoryIndex(),
				     tableIndex->GetItemIndex(), rowIndex);
}

int  cifUpdateItemValueByAlias(char *itemValue, 
			       const int dataBlockIndex, 
			       const char *aliasName, 
			       const int rowIndex)
/* -------------------------------------------------------------------------
   cifUpdateItemValueByAlias() Updates an item value by an alias name
   Parameters: itemValue, the new value
              dataBlockIndex, rowIndex, aliasName
   Return: SUCCEED or error code if failed
   ------------------------------------------------------------------------- */
{
  CifCategoryIndex *tableIndex;
  
  tableIndex = theCifFiles->GetTableIndex(dataBlockIndex, aliasName);
  if (tableIndex == NULL) return CIF_ALIAS_NOT_EXIST;
  else
    return cifUpdateItemValueByIndex(itemValue, dataBlockIndex, 
				  tableIndex->GetCategoryIndex(),
				  tableIndex->GetItemIndex(), rowIndex);
}


/* 
 *  Item convenience functions ...
 */

long cifGetItemFileOffset(const int dataBlockIndex, const char *itemName)
/* -------------------------------------------------------------------------
   cifGetItemFileOffset(): returns the file offset in the source file of the
                           item 
   Parameters: dataBlockIndex, the datablock
               itemName, the item name
   Return: the file offset, or -1 if fails
   ------------------------------------------------------------------------- */
{
  return theCifFiles->GetItemFileOffset(dataBlockIndex, itemName);
}

int cifGetItemCaseSensitivityByIndex(const int dataBlockIndex, const int
                                     categoryIndex, const int itemIndex)
/* -------------------------------------------------------------------------
   cifGetItemCaseSensitivityByIndex(): gets the case sensitivity of the item
   Parametrers: dataBlockIndex, categoryIndex, itemIndex
   Return: the case sensitivity or -1 if failure
   ------------------------------------------------------------------------- */
{
  return theCifFiles->GetItemCaseSensitivity(dataBlockIndex, categoryIndex, itemIndex);
}

int cifGetItemCaseSensitivity(const char *DatablockName, const char *
                              categoryName, const char *itemKeyword)
/* -------------------------------------------------------------------------
   cifGetItemCaseSensitivity(): gets the case sensitivity of the item
   Parametrers: dataBlockName, categoryName, itemKeyword
   Return: the case sensitivity or -1 if failure
   ------------------------------------------------------------------------- */
{
  CifString category(categoryName), item(itemKeyword);
  int dataBlockIndex;

  dataBlockIndex = cifGetDataBlockIndex(DatablockName);
  return theCifFiles->GetItemCaseSensitivity(dataBlockIndex, category, item);
}

char *cifGetItemDefaultValueByIndex(const int dataBlockIndex, const int
                                    categoryIndex, const int itemIndex)
/* -------------------------------------------------------------------------
   cifGetItemDefaultValueByIndex(): retrieves the default value for an item
   Parametrers: dataBlockIndex, categoryIndex, itemIndex
   Return: default item value or NULL if fails
   ------------------------------------------------------------------------- */
{
  CifItemValue *defaultValue;
  char *itemValue;
  
  itemValue = NULL;
  defaultValue =theCifFiles->GetItemDefaultValue(dataBlockIndex, categoryIndex, itemIndex);
  if (defaultValue != NULL) {
    itemValue = (char *) calloc(defaultValue->_buf.Length()+1, sizeof(char));
    strcpy(itemValue, defaultValue->_buf.Text());
  }
  return itemValue;
}

char *cifGetItemDefaultValue(const char *datablockName, const char *
                             categoryName, const char *itemKeyword)
/* -------------------------------------------------------------------------
   cifGetItemDefaultValue(): retrieves the default value for an item
   Parametrers: dataBlockName, const char *categoryName, itemKeyword
   Return: default item value or NULL if fails
   ------------------------------------------------------------------------- */
{
  int dataBlockIndex;
  CifCategoryIndex *tableIndex;
  
  dataBlockIndex = cifGetDataBlockIndex(datablockName);
  tableIndex = theCifFiles->GetTableIndex(dataBlockIndex, categoryName, itemKeyword);
  if (tableIndex == NULL) return NULL;
  else return cifGetItemDefaultValueByIndex(dataBlockIndex,  
				       tableIndex->GetCategoryIndex(),
				       tableIndex->GetItemIndex()); 
}


/* 
 *  Functions which provide information about parent/child relationships...
 */

char **cifGetParentItemNames(const char *DatablockName, const char *
                             categoryName, const char *itemKeyword,
                             int *numParents)
/* -------------------------------------------------------------------------
   cifGetParentItemNames(): retrieves an array of parent names of an item 
   Parameters: DatablockName, categoryName, itemKeyword
               numparents, the address to keep the number of parent names
    Return: parent names or NULL if failure
   ------------------------------------------------------------------------- */
{
  ReVarCifArray <CifCategoryIndex> *parents = NULL;
  CifString category(categoryName), keyword(itemKeyword);
  char **parentsList = NULL; 
  int dataBlockIndex;

  dataBlockIndex = cifGetDataBlockIndex(DatablockName);
 
  *numParents = 0;
  parents = theCifFiles->GetLinkParents(dataBlockIndex, category, keyword);
  
  if (parents == NULL)
    return NULL;
  
  parentsList = (char **) calloc(parents->Length() +1, sizeof(char *));
  *numParents = parents->Length();
  for (unsigned i = 0; i< parents->Length(); i++) {
    parentsList[i] = (char *) 
      calloc( (*parents)[i].GetItemName()->Length()+1, sizeof(char));
    strcpy(parentsList[i],  (*parents)[i].GetItemName()->Text());
  }
  return parentsList;
}

int cifGetParentIndexList(const char *DatablockName, const char *categoryName, 
				 const char *itemKeyword,  
				 int **parentsCategoryIndex, 
				 int **parentsItemIndex, int *numParents)
/* -------------------------------------------------------------------------
   cifGetParentIndexList(): retrieves two lists, one of category indices, one
                  of item indices, both referring to the parent of an item 
   Parameters: dataBlockname, categoryName, itemKeyword 
               parentCategoryIndex, the address to keep the categeory indices
               parentItemIndex, the address to keep the item indexes
	       numParents, the address to keep the number of parents
   Return: SUCCEED or FAILURE
   ------------------------------------------------------------------------- */
{
  ReVarCifArray <CifCategoryIndex> *parents = NULL;
  CifString category(categoryName), item(itemKeyword);
  int dataBlockIndex;

  dataBlockIndex = cifGetDataBlockIndex(DatablockName);
  
  *numParents = 0;
  parents = theCifFiles->GetLinkParents(dataBlockIndex, category, item);
  
  if (parents == NULL)
    return FAILURE;
  
  *parentsCategoryIndex = (int *) calloc(parents->Length() +1, sizeof(int));
  *parentsItemIndex = (int *) calloc(parents->Length() +1, sizeof(int));
  *numParents = parents->Length();
  for (unsigned i = 0; i< parents->Length(); i++) {
    (*parentsCategoryIndex)[i] =  (*parents)[i].GetCategoryIndex();
    (*parentsItemIndex)[i] =  (*parents)[i].GetItemIndex();
  }
  return SUCCEED;
}

char **cifGetChildItemNames(const char *DatablockName, const char *
                            categoryName, const char *itemKeyword,
                            int *nchildren)
/* -------------------------------------------------------------------------
   cifGetChildItemNames(): retrieves a list of child item names for an item 
   Parameters: DatablockName, categoryName, itemKeyword
               nchildren, the address to keep the number of child items
   Return: a list of child names or NULL if fails
   ------------------------------------------------------------------------- */
{
  ReVarCifArray <CifCategoryIndex> *children = NULL;
  CifString rel(categoryName), att(itemKeyword);
  char **childrenList = NULL; 
  
  int dataBlockIndex;

  dataBlockIndex = cifGetDataBlockIndex(DatablockName);
  
  *nchildren = 0;
  children = theCifFiles->GetLinkChildren(dataBlockIndex, rel, att);
  
  if (children == NULL)
    return NULL;
  
  childrenList = (char **) calloc(children->Length() +1, sizeof(char *));
  *nchildren = children->Length();
  for (unsigned i = 0; i< children->Length(); i++) {
    childrenList[i] = (char *) 
      calloc( (*children)[i].GetItemName()->Length()+1, sizeof(char));
    strcpy(childrenList[i],  (*children)[i].GetItemName()->Text());
  }
  return childrenList;
}

int cifGetChildIndexList(const char *DatablockName, const char *categoryName, 
			 const char *itemKeyword, int **childrenCategoryIndex, 
			 int **childrenItemIndex, int *nchildren)
     
/* -------------------------------------------------------------------------
   cifGetChildIndexList(): retrieves two lists, one of category indices, one
                  of item indices, both referring to the child of an item
   Parameters: DatablockName, categoryName, itemKeyword
               childrenCategoryIndex, the address to keep the categeory indices
               childrenItemIndex, the address to keep the item indices
   Return: SUCCEED or FAILURE
   ------------------------------------------------------------------------- */
{
  ReVarCifArray <CifCategoryIndex> *children = NULL;
  CifString category(categoryName), item(itemKeyword);
  
  int dataBlockIndex;

  dataBlockIndex = cifGetDataBlockIndex(DatablockName);
  
  *nchildren = 0;
  children = theCifFiles->GetLinkChildren(dataBlockIndex, category, item);
  
  if (children == NULL)
    return FAILURE;
  
  *childrenCategoryIndex = (int *) calloc(children->Length() +1, sizeof(int));
  *childrenItemIndex = (int *) calloc(children->Length() +1, sizeof(int));
  *nchildren = children->Length();
  for (unsigned i = 0; i< children->Length(); i++) {
    (*childrenCategoryIndex)[i] =  (*children)[i].GetCategoryIndex();
    (*childrenItemIndex)[i] =  (*children)[i].GetItemIndex();
  }
  return SUCCEED; 
} 

int cifGetRelatedItemIndexList(const char *DatablockName, const char *
                               categoryName, const char *itemKeyword,  
			       int **relatedItemsCategoryIndex, 
			       int **relatedItemsItemIndex, int *nRelatedItems)
     
/* -------------------------------------------------------------------------
   cifGetRelatedItemIndexList(): retrieves two lists, one of category indices,
                                 one of item indices, both referring to a
                                 related item of an item 
   Parameters: DataBlockName, categoryName, itemKeyword
               relatedItemsCategoryIndex, the address to keep the categeory
                                          indices
               relatedItemsItemIndex, the address to keep the item indices 
   Return: SUCCEED or FAILURE
   ------------------------------------------------------------------------- */
{
  ReVarCifArray <CifRelatedItem> *RelatedItems = NULL;
  CifString category(categoryName), item(itemKeyword);
  
  int dataBlockIndex;

  dataBlockIndex = cifGetDataBlockIndex(DatablockName);
  
  *nRelatedItems = 0;
  RelatedItems = theCifFiles->GetRelatedItems(dataBlockIndex, category, item);
  
  if (RelatedItems == NULL)
    return FAILURE;
  
  *relatedItemsCategoryIndex = (int *) calloc(RelatedItems->Length() +1, sizeof(int));
  *relatedItemsItemIndex = (int *) calloc(RelatedItems->Length() +1, sizeof(int));
  *nRelatedItems = RelatedItems->Length();
  for (unsigned i =0; i< RelatedItems->Length(); i++) {
    (*relatedItemsCategoryIndex)[i] =  (*RelatedItems)[i].GetCategoryIndex();
    (*relatedItemsItemIndex)[i] =  (*RelatedItems)[i].GetItemIndex();
  }
  return SUCCEED; 
} 

char **cifGetRelatedItemNames(const char *DatablockName, const char *
                              categoryName, const char *itemKeyword,
                              int *nRelatedItems)
/* -------------------------------------------------------------------------
    cifGetRelatedItemNames(): retrieves a list of the related items of an item
    Parameters: DataBlockName, categoryName, itemKeyword
                nRelatedItems, the address to keep the number of items in the
                               itemValues array
    Return ItemValue: a list of related item names or NULL if failure
   ------------------------------------------------------------------------- */
{
  ReVarCifArray <CifRelatedItem> *relatedItems = NULL;
  CifString rel(categoryName), att(itemKeyword);
  char **relatedItemsList = NULL; 
  
  int dataBlockIndex;

  dataBlockIndex = cifGetDataBlockIndex(DatablockName);
  
  *nRelatedItems = 0;
  relatedItems = theCifFiles->GetRelatedItems(dataBlockIndex, rel, att);
  
  if (relatedItems == NULL)
    return NULL;
  
  relatedItemsList = (char **) calloc(relatedItems->Length() +1, sizeof(char *));
  *nRelatedItems = relatedItems->Length();
  for (unsigned i = 0; i< relatedItems->Length(); i++) {
    relatedItemsList[i] = (char *) 
      calloc( (*relatedItems)[i].GetItemName()->Length()+1, sizeof(char));
    strcpy(relatedItemsList[i],  (*relatedItems)[i].GetItemName()->Text());
  }
  return relatedItemsList;
}

int cifGetDependentItemIndexList(const char *DatablockName, const char *
                                 categoryName, const char *itemKeyword,  
				 int **dependentItemCategoryIndex, 
				 int **dependentItemItemIndex,
                                 int *nDependentItem)
/* -------------------------------------------------------------------------
   cifGetDependentItemIndexList(): retrieves two lists, one of category
                                   indices, one of item indices, both
                                   referring to a dependent item of an item 
   Parameters: DataBlockName, categoryName, itemKeyword
               dependentItemsCategoryIndex, the address to keep the categeory
                                            indices
               dependentItemsItemIndex, the address to keep the item indices 
   Return: SUCCEED or FAILURE
   ------------------------------------------------------------------------- */
{
  ReVarCifArray <CifCategoryIndex> *dependentItem = NULL;
  CifString category(categoryName), item(itemKeyword);
  
  int dataBlockIndex;

  dataBlockIndex = cifGetDataBlockIndex(DatablockName);
  
  *nDependentItem = 0;
  dependentItem = theCifFiles->GetDependentItems(dataBlockIndex, category, item);
  
  if (dependentItem == NULL)
    return FAILURE;
  
  *dependentItemCategoryIndex = (int *) calloc(dependentItem->Length() +1, sizeof(int));
  *dependentItemItemIndex = (int *) calloc(dependentItem->Length() +1, sizeof(int));
  *nDependentItem = dependentItem->Length();
  for (unsigned i = 0; i < dependentItem->Length(); i++) {
    (*dependentItemCategoryIndex)[i] =  (*dependentItem)[i].GetCategoryIndex();
    (*dependentItemItemIndex)[i] =  (*dependentItem)[i].GetItemIndex();
  }
  return SUCCEED; 
} 

char **cifGetDependentItemNames(const char *DatablockName, const char *
                                categoryName, const char *itemKeyword,
                                int *nDependentItem)
/* -------------------------------------------------------------------------
   cifGetDependentItemNames() 
    Parameters: DataBlockName, categoryName, itemKeyword
                nDependentItem, the address to keep the number of dependent
                items
    Return: list of dependent item names or NULL if failure
   ------------------------------------------------------------------------- */
{
  ReVarCifArray <CifCategoryIndex> *dependentItem = NULL;
  CifString rel(categoryName), att(itemKeyword);
  char **dependentItemList = NULL; 
  
  int dataBlockIndex;

  dataBlockIndex = cifGetDataBlockIndex(DatablockName);
  
  *nDependentItem = 0;
  dependentItem = theCifFiles->GetDependentItems(dataBlockIndex, rel, att);
  
  if (dependentItem == NULL)
    return NULL;
  
  dependentItemList = (char **) calloc(dependentItem->Length() +1, sizeof(char *));
  *nDependentItem = dependentItem->Length();
  for (unsigned i = 0; i < dependentItem->Length(); i++) {
    dependentItemList[i] = (char *) 
      calloc( (*dependentItem)[i].GetItemName()->Length()+1, sizeof(char));
    strcpy(dependentItemList[i],  (*dependentItem)[i].GetItemName()->Text());
  }
  return dependentItemList;
}

char *cifGetRelatedItemFunctionCode(const char *dataBlockName,
				    const char *categoryName, 
				    const char *itemKeyword,
				    const char *relatedItemName)
/* -------------------------------------------------------------------------
   cifGetRelatedItemFunctionCode(): retrieves the code associated with a
                                    related item of an item
   Parameters: dataBlockName, categoryName, itemKeyword
               relatedItemName, the name of the related item
   Return: a string of code or NULL for failure
   ------------------------------------------------------------------------- */
{
  ReVarCifArray <CifRelatedItem> *relatedItems = NULL;
  CifString category(categoryName), item(itemKeyword);
  char *code;
  int i;
  int dataBlockIndex = cifGetDataBlockIndex(dataBlockName);

  code = NULL;
  relatedItems = theCifFiles->GetRelatedItems(dataBlockIndex, category, item);
  if (relatedItems != NULL) {
    for (i = 0; i < relatedItems->Length(); i++)
      if (!CompareNoCase((*relatedItems)[i].GetItemName()->Text(), relatedItemName))
	break;
    if (i != relatedItems->Length()) {
      code = (char *) calloc((*relatedItems)[i].GetItemName()->Length()+1, sizeof(char));
      strcpy(code, (*relatedItems)[i].GetItemName()->Text());
    }
  }
  return code;
}

char *cifGetRelatedItemFunctionCodeByIndex(const int dataBlockIndex,
					   const int categoryIndex, 
					   const int itemIndex,
					   const int relatedItemIndex)
/* -------------------------------------------------------------------------
   cifGetRelatedItemFunctionCodeByIndex(): retrieves the code associated with
                                           a related item of an item
   Parameters: dataBlockIndex, categoryIndex, itemIndex
               relatedItemIndex, the index of the related item
   Return: a string of code or NULL for failure
   ------------------------------------------------------------------------- */
{
  ReVarCifArray <CifRelatedItem> *relatedItems = NULL;
  char *code;

  code = NULL;
  if (relatedItemIndex < 0) return NULL;
  relatedItems = theCifFiles->GetRelatedItems(dataBlockIndex, categoryIndex, itemIndex);
  if (relatedItems != NULL && relatedItemIndex < relatedItems->Length()) {
    code = (char *) calloc((*relatedItems)[relatedItemIndex].GetItemName()->Length()+1, sizeof(char));
    strcpy(code, (*relatedItems)[relatedItemIndex].GetItemName()->Text());
  }
  return code;
}


/* 
 * Functions that print the details of CIFLIB structures...
 */

void cifPrintDataBlockByIndex(const int dataBlockIndex) 
/* -------------------------------------------------------------------------
   cifPrintDataBlockByIndex(): prints the contents of the datablock given
                               by dataBlockIndex, for debugging purposes
   ------------------------------------------------------------------------- */
{
  theCifFiles->PrintDataBlock(dataBlockIndex);
  cout << endl << endl;
}

void cifPrintDataBlock(const char *dataBlockName) 
/* -------------------------------------------------------------------------
   cifPrintDataBlock(): prints the contents of the datablock given by
                        dataBlockName, for debugging purposes
   ------------------------------------------------------------------------- */
{
  int dataBlockIndex = cifGetDataBlockIndex(dataBlockName);
  cifPrintDataBlockByIndex(dataBlockIndex);
}

void cifPrintDataBlockTable(const int dataBlockIndex) 
/* -------------------------------------------------------------------------
   cifPrintDataBlockTable(): prints the contents of the tables of the
                             datablock given by dataBlockName, for debugging
                             purposes
   ------------------------------------------------------------------------- */
{
  cout << endl << endl;
  theCifFiles->PrintDataBlockTable(dataBlockIndex);
  cout << endl << endl;
  cout.flush();
}

void cifPrintDataBlockDetails(const int dataBlockIndex) 
/* -------------------------------------------------------------------------
   cifPrintDataBlockDetails(): prints the details of the datablock given by
                               dataBlockName, for debugging purposes
   ------------------------------------------------------------------------- */
{
  theCifFiles->PrintDataBlockDetails(dataBlockIndex);
  cout << endl << endl;
}


/*
 *  Functions which extend the schema ... 
 */

int cifAddDataBlock(const char *newDatablockName, const char *dicDatablockName)
/* -------------------------------------------------------------------------
   cifAddDatablock(): Add a new datablock
   ------------------------------------------------------------------------- */
{
  int dicDataBlockIndex;

  dicDataBlockIndex = cifGetDataBlockIndex(dicDatablockName);
  return cifAddDataBlockByIndex(newDatablockName, dicDataBlockIndex);
}

int cifAddCategory(const char *newCategoryName, const char *datablockName)
/* -------------------------------------------------------------------------
   cifAddCategory(): Add a new Category
   ------------------------------------------------------------------------- */
{
  int dataBlockIndex;

  dataBlockIndex = cifGetDataBlockIndex(datablockName);
  return cifAddCategoryByIndex(newCategoryName, dataBlockIndex);
}

int cifAddDataItem(const char *newItemKeyword, const char *datablockName,
                   const char *categoryName)
/* -------------------------------------------------------------------------
   cifAddDataItem(): Add a new item column
   ------------------------------------------------------------------------- */
{
  int dataBlockIndex, categoryIndex;

  dataBlockIndex = cifGetDataBlockIndex(datablockName);
  categoryIndex = cifGetCategoryIndexByIndex(dataBlockIndex, categoryName);
  return cifAddDataItemByIndex(newItemKeyword, dataBlockIndex, categoryIndex);
}

int cifAddRow(const char *datablockName, const char *categoryName)
/* -------------------------------------------------------------------------
   cifAddDataItem(): Add a new row to a table
   ------------------------------------------------------------------------- */
{
  int dataBlockIndex, categoryIndex;

  dataBlockIndex = cifGetDataBlockIndex(datablockName);
  categoryIndex = cifGetCategoryIndexByIndex(dataBlockIndex, categoryName);
  return cifAddRowByIndex(dataBlockIndex, categoryIndex);
}


/*
 * Functions that evaluate the number and type of warnings and errors
 */

int cifCountDataBlockErrors(const char *dataBlockName)
{
  int dataBlockIndex = cifGetDataBlockIndex(dataBlockName);
  return cifCountDataBlockErrorsByIndex(dataBlockIndex);
}

int cifCountDataBlockErrorsByIndex(const int dataBlockIndex)
{
  return theCifFiles->CountDataBlockErrors(dataBlockIndex);
}

char *cifGetDataBlockError(const char *dataBlockName, const int errorIndex)
{
  int dataBlockIndex = cifGetDataBlockIndex(dataBlockName);
  return cifGetDataBlockErrorByIndex(dataBlockIndex, errorIndex);
}

char *cifGetDataBlockErrorByIndex(const int dataBlockIndex,
                                  const int errorIndex)
{
  CifString *errorTmp;
  char *error;

  errorTmp = theCifFiles->GetDataBlockError(dataBlockIndex, errorIndex);
  if (errorTmp != NULL && errorTmp->Length() != 0) {
    error = (char *) calloc(errorTmp->Length()+1, sizeof(char));
    strcpy(error, errorTmp->Text());
    return error;
  }
  else return (char *) NULL;
}

char **cifGetDataBlockErrors(const char *dataBlockName, int *numError) 
{
  int dataBlockIndex = cifGetDataBlockIndex(dataBlockName);
  return cifGetDataBlockErrorsByIndex(dataBlockIndex, numError);
}

char **cifGetDataBlockErrorsByIndex(const int dataBlockIndex, int *numError)
{
  ReVarCifArray <CifString> *tmpErrors;
  char **errors;
  *numError = 0;
  tmpErrors = theCifFiles->GetDataBlockErrors(dataBlockIndex);
  if (tmpErrors != NULL && tmpErrors->Length() != 0) {
    errors = (char **) calloc(tmpErrors->Length()+1, sizeof(char *));
    for (unsigned i=0; i< tmpErrors->Length(); i++) {
      errors[i] = (char *) calloc((*tmpErrors)[i].Length()+1, sizeof(char));
      strcpy(errors[i], (*tmpErrors)[i].Text());
    }
    *numError = tmpErrors->Length();
    return errors;
  }
  else return (char **) NULL;
}

void cifFreeDataBlockError(const char *dataBlockName, const int errorIndex)
{
  int dataBlockIndex = cifGetDataBlockIndex(dataBlockName);
  cifFreeDataBlockErrorByIndex(dataBlockIndex, errorIndex);
}

void cifFreeDataBlockErrorByIndex(const int dataBlockIndex,
                                  const int errorIndex)
{
  theCifFiles->FreeDataBlockError(dataBlockIndex, errorIndex);
}

void cifFreeDataBlockErrors(const char *dataBlockName)
{
  int dataBlockIndex = cifGetDataBlockIndex(dataBlockName);
  cifFreeDataBlockErrorsByIndex(dataBlockIndex);
}

void cifFreeDataBlockErrorsByIndex(const int dataBlockIndex)
{
  theCifFiles->FreeDataBlockErrors(dataBlockIndex);
}

int cifCountDataBlockWarnings(const char *dataBlockName)
{
  int dataBlockIndex = cifGetDataBlockIndex(dataBlockName); 
  return cifCountDataBlockWarningsByIndex(dataBlockIndex);
}

int cifCountDataBlockWarningsByIndex(const int dataBlockIndex)
{
  return theCifFiles->CountDataBlockWarnings(dataBlockIndex);
}

char *cifGetDataBlockWarning(const char *dataBlockName, const int WarningIndex)
{
  int dataBlockIndex = cifGetDataBlockIndex(dataBlockName);
  return cifGetDataBlockWarningByIndex(dataBlockIndex, WarningIndex);
}

char *cifGetDataBlockWarningByIndex(const int dataBlockIndex,
                                    const int WarningIndex)
{
  CifString *tmpWarning;
  char *warning;

  tmpWarning = theCifFiles->GetDataBlockWarning(dataBlockIndex, WarningIndex);
  if (tmpWarning != NULL && tmpWarning->Length() != 0) {
    warning = (char *) calloc(tmpWarning->Length()+1, sizeof(char));
    strcpy(warning, tmpWarning->Text());
    return warning;
  }
  else return (char *) NULL;
}

char **cifGetDataBlockWarnings(const char *dataBlockName, int *numWarning) 
{
  int dataBlockIndex = cifGetDataBlockIndex(dataBlockName);
  return cifGetDataBlockWarningsByIndex(dataBlockIndex, numWarning);
}

char **cifGetDataBlockWarningsByIndex(const int dataBlockIndex,
                                      int *numWarning)
{
  ReVarCifArray <CifString> *tmpWarnings;
  char **warnings;
  *numWarning = 0;
  tmpWarnings = theCifFiles->GetDataBlockWarnings(dataBlockIndex);
  if (tmpWarnings != NULL && tmpWarnings->Length() != 0) {
    warnings = (char **) calloc(tmpWarnings->Length()+1, sizeof(char *));
    for (unsigned i=0; i< tmpWarnings->Length(); i++) {
      warnings[i] = (char *) calloc((*tmpWarnings)[i].Length()+1, sizeof(char));
      strcpy(warnings[i], (*tmpWarnings)[i].Text());
    }
    *numWarning = tmpWarnings->Length();
    return warnings;
  }
  else return (char **) NULL;
}

void cifFreeDataBlockWarning(const char *dataBlockName, const int WarningIndex)
{
  int dataBlockIndex = cifGetDataBlockIndex(dataBlockName);
  cifFreeDataBlockWarningByIndex(dataBlockIndex, WarningIndex);
}

void cifFreeDataBlockWarningByIndex(const int dataBlockIndex, const int warningIndex)
{
  theCifFiles->FreeDataBlockWarning(dataBlockIndex, warningIndex);
}

void cifFreeDataBlockWarnings(const char *dataBlockName)
{
  int dataBlockIndex = cifGetDataBlockIndex(dataBlockName);
  cifFreeDataBlockWarningsByIndex(dataBlockIndex);
}

void cifFreeDataBlockWarningsByIndex(const int dataBlockIndex)
{
  theCifFiles->FreeDataBlockWarnings(dataBlockIndex);
}
