/* 
 * Motif
 *
 * Copyright (c) 1987-2012, The Open Group. All rights reserved.
 *
 * These libraries and programs are free software; you can
 * redistribute them and/or modify them under the terms of the GNU
 * Lesser General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * These libraries and programs are distributed in the hope that
 * they will be useful, but WITHOUT ANY WARRANTY; without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 * PURPOSE. See the GNU Lesser General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with these librararies and programs; if not, write
 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
 * Floor, Boston, MA 02110-1301 USA
 */ 
/* 
 * HISTORY
 */ 
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif


#ifdef REV_INFO
#ifndef lint
static char rcsid[] = "$XConsortium: MrmIrid.c /main/13 1996/11/13 13:58:17 drk $"
#endif
#endif

/* (c) Copyright 1989, 1990, DIGITAL EQUIPMENT CORPORATION, MAYNARD, MASS. */



/*
 *++
 *  FACILITY:
 *
 *      UIL Resource Manager (URM): IDB Facility
 *	Resource ID management routines.
 *
 *  ABSTRACT:
 *
 *	These routines acquire RIDs, allow entering a data block under an RID,
 *	and manage the RID and index map records.
 *
 *--
 */


/*
 *
 *  INCLUDE FILES
 *
 */

#include <Mrm/MrmAppl.h>
#include <Mrm/Mrm.h>
#include <Mrm/IDB.h>
#include "MrmMsgI.h"


/*
 *
 *  TABLE OF CONTENTS
 *
 *	Idb__RID_EnterItem		- Enter a data entry under an RID
 *
 *	Idb__RID_ReturnItem		- Return the data entry for an RID
 *
 *	Idb__RID_NextRID		- Return next available RID
 *
 *	Idb__RID_AddRecord		- Set up a new resource ID record
 *
 */


/*
 *
 *  DEFINE and MACRO DEFINITIONS
 *
 */

/*
 * Macros which validate index records in buffers
 */
#define	Idb__RID_ValidRecord(buffer) \
	(_IdbBufferRecordType(buffer)==IDBrtRIDMap)


/*
 *++
 *
 *  PROCEDURE DESCRIPTION:
 *
 *	Idb__RID_EnterItem makes an entry in the resource record for the
 *	resourc id of the data entry pointer.
 *
 *  FORMAL PARAMETERS:
 *
 *	file_id		Open IDB file in which to write entry
 *	resource_id	Resource ID under which to enter entry
 *	data_entry	Data entry pointer for data
 *
 *  IMPLICIT INPUTS:
 *
 *  IMPLICIT OUTPUTS:
 *
 *  FUNCTION VALUE:
 *
 *	MrmSUCCESS	operation succeeded
 *	MrmFAILURE	some other failure
 *	MrmOUT_OF_RANGE	Record number out of range
 *	MrmBAD_RECORD	not an RID map record
 *	URMBadDataindex	Data index out of range
 *
 *  SIDE EFFECTS:
 *
 *--
 */

Cardinal 
Idb__RID_EnterItem (IDBFile			file_id,
		    IDBResource			resource_id,
		    IDBDataHandle		data_entry)
{

  /*
   *  Local variables
   */
  Cardinal		result;		/* function results */
  IDBRecordBufferPtr	bufptr;		/* header record buffer */
  IDBridMapRecordPtr	recptr;		/* header record in buffer */
  IDBridDesc		resid;		/* CAST resource id */
  IDBResourceIndex	resndx;		/* to check resource index */
  IDBRecordNumber	recno;		/* Map record record number */


  /*
   * Get the resource map record. Let the header handle it if the
   * RID is in the header record.
   */
  resid.external_id = resource_id;
  recno = resid.internal_id.map_rec;
  if ( recno == IDBHeaderRecordNumber )
    return Idb__HDR_EnterItem (file_id, resource_id, data_entry);

  resndx = resid.internal_id.res_index;
  if ( recno > file_id->last_record )
    return Urm__UT_Error ("Idb__RID_EnterItem", _MrmMMsg_0019,
			  file_id, NULL, MrmOUT_OF_RANGE);
  result = Idb__BM_GetRecord (file_id, recno, &bufptr);
  if ( result != MrmSUCCESS ) return result;
  if ( ! Idb__RID_ValidRecord(bufptr) )
    return Urm__UT_Error ("Idb__RID_EnterItem", _MrmMMsg_0010,
			  file_id, NULL, MrmBAD_RECORD);

  recptr = (IDBridMapRecordPtr) bufptr->IDB_record;

  /*
   * Set the data item.
   */
  if ( resndx >= IDBridPtrVecMax )
    return Urm__UT_Error ("Idb__RID_EnterItem", _MrmMMsg_0013,
			  file_id, NULL, MrmBAD_DATA_INDEX);
  recptr->pointers[resndx].internal_id.rec_no = data_entry.rec_no;
  recptr->pointers[resndx].internal_id.item_offs = data_entry.item_offs;
  Idb__BM_MarkActivity (bufptr);

  /*
   * successful entry
   */
  Idb__BM_MarkModified (bufptr);
  return MrmSUCCESS;

}



/*
 *++
 *
 *  PROCEDURE DESCRIPTION:
 *
 *	Idb_RID_ReturnItem returns the data entry pointer for an item
 *	entered in the database under a resource id. Since this routine
 *	is also used to check if an entry exists before writing, it
 *	uses a flag to determine if a null entry should be signalled.
 *
 *  FORMAL PARAMETERS:
 *
 *	file_id		Open IDB file in which to write entry
 *	resource_id	Entry's resource id
 *	signal_null	if TRUE, signal error on null data entry
 *	entry_return	To return data pointer for data
 *
 *  IMPLICIT INPUTS:
 *
 *  IMPLICIT OUTPUTS:
 *
 *  FUNCTION VALUE:
 *
 *	MrmSUCCESS	operation succeeded
 *	MrmOUT_OF_RANGE	Record number out of range
 *	MrmBAD_RECORD	not an RID map record
 *	MrmNULL_DATA	no data for resource id
 *	MrmFAILURE	some other failure
 *	URMBadDataindex	Data index out of range
 *
 *  SIDE EFFECTS:
 *
 *--
 */

Cardinal 
Idb__RID_ReturnItem (IDBFile			file_id,
		     IDBResource		resource_id,
		     Boolean			signal_null,
		     IDBDataHandle		*entry_return)
{

  /*
   *  Local variables
   */
  Cardinal		result;		/* function results */
  IDBRecordBufferPtr	bufptr;		/* RID map record buffer */
  IDBridMapRecordPtr	recptr;		/* RID map record in buffer */
  IDBridDesc		resid;		/* CAST resource id */
  IDBResourceIndex	resndx;		/* to check resource index */
  IDBRecordNumber	recno;		/* Map record record number */


  /*
   * Get the resource map record. Let the header handle it if the RID is
   * in the header.
   */
  resid.external_id = resource_id;
  recno = resid.internal_id.map_rec;
  if ( recno == IDBHeaderRecordNumber )
    return Idb__HDR_ReturnItem
      (file_id, resource_id, signal_null, entry_return);
  resndx = resid.internal_id.res_index;
  if ( recno > file_id->last_record )
    return Urm__UT_Error ("Idb__RID_ReturnItem", _MrmMMsg_0019,
			  file_id, NULL, MrmOUT_OF_RANGE);
  result = Idb__BM_GetRecord (file_id, recno, &bufptr);
  if ( result != MrmSUCCESS ) return result;
  if ( ! Idb__RID_ValidRecord(bufptr) )
    return Urm__UT_Error ("Idb__RID_ReturnItem", _MrmMMsg_0010,
			  file_id, NULL, MrmBAD_RECORD);

  recptr = (IDBridMapRecordPtr) bufptr->IDB_record;

  /*
   * Retrieve the data item.
   */
  if ( resndx >= IDBridPtrVecMax )
    return Urm__UT_Error ("Idb__RID_ReturnItem", _MrmMMsg_0013,
			  file_id, NULL, MrmBAD_DATA_INDEX);
  if ((recptr->pointers[resndx].internal_id.rec_no == 0) &&
      (recptr->pointers[resndx].internal_id.item_offs == 0))
    if ( signal_null )
      return Urm__UT_Error ("Idb__RID_ReturnItem", _MrmMMsg_0014,
			    file_id, NULL, MrmNULL_DATA);
    else return MrmNULL_DATA;
  entry_return->rec_no = recptr->pointers[resndx].internal_id.rec_no;
  entry_return->item_offs = recptr->pointers[resndx].internal_id.item_offs;

  /*
   * Successful retrieval
   */
  Idb__BM_MarkActivity (bufptr);
  return MrmSUCCESS;

}



/*
 *++
 *
 *  PROCEDURE DESCRIPTION:
 *
 *	Idb__RID_NextRID returns the next available resource ID in the
 *	file. It is the internal version of UrmIdbGetResourceId. If a new
 *	resource record is required, this routine:
 *		- Acquires a new resource record, and initializes it
 *		  to all null pointers.
 *		- Adds a new entry to map index vector in the file header
 *
 *  FORMAL PARAMETERS:
 *
 *	file_id		Open IDB file in which to write entry
 *	res_id_return	To return new resource id
 *
 *  IMPLICIT INPUTS:
 *
 *  IMPLICIT OUTPUTS:
 *
 *  FUNCTION VALUE:
 *
 *	MrmSUCCESS	operation succeeded
 *	MrmFAILURE	some other failure
 *
 *  SIDE EFFECTS:
 *
 *--
 */

Cardinal 
Idb__RID_NextRID (IDBFile		file_id,
		  IDBResource		*res_id_return)

{

  /*
   *  Local variables
   */
  Cardinal		result;		/* function results */
  IDBResourceIndex	resndx;		/* to check resource index */

  /*
   * use the next available ID, or acquire a new record. Handle both header
   * record and RID record maps.
   */
  resndx = file_id->next_RID.internal_id.res_index;
  if ( file_id->next_RID.internal_id.map_rec == IDBHeaderRecordNumber )
    {
      if ( resndx >= IDBHeaderRIDMax )
	{
	  result = Idb__RID_AddRecord (file_id);
	  if ( result != MrmSUCCESS ) return result;
	  resndx = file_id->next_RID.internal_id.res_index;
	}
    }

  /*
   * RID comes from a RID record
   */
  if ( resndx >= IDBridPtrVecMax )
    {
      result = Idb__RID_AddRecord (file_id);
      if ( result != MrmSUCCESS ) return result;
      resndx = file_id->next_RID.internal_id.res_index;
    }

  /*
   * Increment resource index, returning current value.
   */
  *res_id_return = (IDBResource) file_id->next_RID.external_id;
  file_id->next_RID.internal_id.res_index++;
  return MrmSUCCESS;

}



/*
 *++
 *
 *  PROCEDURE DESCRIPTION:
 *
 *	Idb__RID_AddRecord initializes a new resource id record and enters
 *	in in the index map in the header.
 *
 *  FORMAL PARAMETERS:
 *
 *	file_id		Open IDB file
 *
 *  IMPLICIT INPUTS:
 *
 *  IMPLICIT OUTPUTS:
 *
 *  FUNCTION VALUE:
 *
 *	MrmSUCCESS	operation succeeded
 *	MrmFAILURE	some other failure
 *
 *  SIDE EFFECTS:
 *
 *--
 */

Cardinal 
Idb__RID_AddRecord (IDBFile		file_id)
{

  /*
   *  Local variables
   */
  Cardinal		result;		/* function results */
  IDBRecordBufferPtr	bufptr;		/* RID map record buffer */
  IDBridMapRecordPtr	recptr;		/* RID map record in buffer */
  int			ndx;		/* loop index */

  /*
   * Acquire a record.
   */
  result = Idb__BM_InitRecord (file_id, 0, IDBrtRIDMap, &bufptr);
  if ( result != MrmSUCCESS ) return result;
  recptr = (IDBridMapRecordPtr) bufptr->IDB_record;

  /*
   * Initialize the record contents
   */
  for (ndx=0 ; ndx<IDBridPtrVecMax ; ndx++)
    {
      recptr->pointers[ndx].internal_id.rec_no = 0;
      recptr->pointers[ndx].internal_id.item_offs = 0;
    }
  Idb__BM_MarkModified (bufptr);

  /*
   * Update the next RID in the file header to point to the first entry in
   * the new record.
   */
  file_id->next_RID.internal_id.map_rec = recptr->map_header.header.record_num;
  file_id->next_RID.internal_id.res_index = 0;

  /*
   * Record successfully created.
   */
  Idb__BM_MarkModified (bufptr);
  return MrmSUCCESS;

}

