Files
epics-base/src/libCom/arChanIO.c
1992-09-18 13:30:37 +00:00

1639 lines
53 KiB
C
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* $Id$
* Author: Roger A. Cole
* Date: 03-09-90
*
* Experimental Physics and Industrial Control System (EPICS)
*
* Copyright 1991-92, the Regents of the University of California,
* and the University of Chicago Board of Governors.
*
* This software was produced under U.S. Government contracts:
* (W-7405-ENG-36) at the Los Alamos National Laboratory,
* and (W-31-109-ENG-38) at Argonne National Laboratory.
*
* Initial development by:
* The Controls and Automation Group (AT-8)
* Ground Test Accelerator
* Accelerator Technology Division
* Los Alamos National Laboratory
*
* Co-developed with
* The Controls and Computing Group
* Accelerator Systems Division
* Advanced Photon Source
* Argonne National Laboratory
*
* Modification Log:
* -----------------
* .01 03-09-90 rac initial version
* .02 07-31-91 rac installed in SCCS
* .03 09-19-91 rac add a new assert check; fix bug in writing
* index blocks
* .04 09-14-92 rac remove use of special malloc and free routines
*
* make options
* -DvxWorks makes a version for VxWorks
* -DNDEBUG don't compile assert() checking
* -DDEBUG compile various debug code
*/
/*+/mod***********************************************************************
* TITLE arChanIO.c - AR channel data file routines
*
* DESCRIPTION
* `Private' routines for the implementation of AR channel data files.
* These routines are intended for use only by arAccessLib.c routines.
*
* SEE ALSO
* arAccessLib.h and arAccessLib.c for some code which needs to be
* directly accessible to users of the AR access
* library.
*-***************************************************************************/
#define ARACC_TEXT_GLBLSOURCE
#include <arAccessLib.h>
#ifndef INC_arCS_h
# include <arCS.h>
#endif
#ifndef INC_genDefs_h
# include <genDefs.h>
#endif
#ifndef INC_tsDefs_h
# include <tsDefs.h>
#endif
#ifndef INCLcadefh
# include <cadef.h>
#endif
#ifdef vxWorks
# include <vxWorks.h>
# include <ioLib.h> /* for O_RDWR and O_RDONLY definitions */
# include <stdioLib.h>
#else
# include <stdio.h>
# include <sys/file.h> /* for O_RDWR and O_RDONLY definitions */
#endif
/*----------------------------------------------------------------------------
* AR channel file routines
*
* long arCFAsserts( )
* long arCF_DFillRbufAndGetc( pArChanDesc )
* long arCF_DFlushAndFree( pArChanDesc )
* long arCF_DFlushWbufAndPutc( c, pArChanDesc )
* long arCF_DNextBlock( pArChanDesc )
* long arCF_DRead( pArChanDesc, pDBuf, blockNum )
*
* long arCF_GRGetBlock( pArChanDesc )
* long arCF_GRReadBlock( pArCfDesc, pArChanHdr )
*
* long arCF_IFlushAndFree( pArChanDesc )
* long arCF_INextDatInfo( pArChanDesc )
* long arCF_IRead( pArChanDesc, pIBuf, blockNum )
*
* long arCF_MIAddBlock( pArCfDesc )
* long arCF_MIAddChan( pArCfDesc, name, >ppMIBuf, >pIndex )
* long arCF_MIDelChan( pArCfDesc, pMIBuf, index )
* long arCF_MIFindChan( pArCfDesc, name, >ppMIBuf, >pIndex )
*---------------------------------------------------------------------------*/
/*+/subhead-------------------------------------------------------------------
* NAME arCF_malloc/arCF_free
*
* malloc and free routines for arCF routines to use for the various kinds
* of data structures. Should the need arise, these routines will
* be enhanced to maintain lists of free data structures of the various
* kinds, to eliminate malloc()/free() overhead and memory fragmentation.
*
* The following macros are used for calling malloc() and free() for
* some common data structures in arChanIO. They have two versions,
* depending on whether DEBUG is defined. Both versions perform the
* described function. The DEBUG version provides the capability for
* `bounds checking' on allocated buffers, and also for filling buffers
* with a known pattern for use in `un-initialized buffer' bug detection.
*
* AR_MI_BUF *arCF_mallocMI_BUF() void arCF_freeMI_BUF(pMIBuf
* AR_INDEX_BUF *arCF_mallocINDEX_BUF() void arCF_freeINDEX_BUF(pIndexBuf)
* AR_DATA_BUF *arCF_mallocDATA_BUF() void arCF_freeDATA_BUF(pDataBuf)
* AR_GR_BUF *arCF_mallocGR_BUF() void arCF_freeGR_BUF(pDataBuf)
*
*---------------------------------------------------------------------------*/
static int glArMallocDebug=0; /* 1 says print info */
static unsigned long glArNMalloc=0; /* # of successful malloc's */
static unsigned long glArNMallocFailed=0; /* # of failed mallocs */
static unsigned long glArNbytesMalloc=0; /* # of bytes ever malloc'ed */
static unsigned long glArNetNbytesMalloc=0; /* # bytes malloc, not free */
static unsigned long glArNFree=0; /* # of free's */
static unsigned long glArNbytesFree=0; /* # of bytes ever free'ed */
void
arFree(pMem, size, text)
void *pMem;
int size;
char *text;
{
free((char *)pMem);
glArNFree++;
glArNbytesFree += size;
glArNetNbytesMalloc -= size;
if (glArMallocDebug) {
(void)printf("free %s=0x%x sz=%d\n", text, pMem, size);
arMallocStats();
}
}
void *
arMalloc(size, text)
int size;
char *text;
{
void *pMem;
pMem = (void *)malloc(size);
if (pMem != NULL) {
glArNMalloc++;
glArNbytesMalloc += size;
glArNetNbytesMalloc += size;
}
else
glArNMallocFailed++;
if (glArMallocDebug) {
(void)printf("malloc %s=0x%x sz=%d\n", text, pMem, size);
arMallocStats();
}
return pMem;
}
void
arMallocDebug(n)
int n;
{
glArMallocDebug = n;
}
void
arMallocStats()
{
(void)printf("Nmal,Nfree,net= %d %d %d\n",
glArNMalloc, glArNFree, glArNetNbytesMalloc);
}
AR_MI_BUF *
arCF_mallocMI_BUF()
{
#ifdef DEBUG
return (AR_MI_BUF *)arMalloc(sizeof(AR_MI_BUF), "AR_MI_BUF");
#else
return (AR_MI_BUF *)malloc(sizeof(AR_MI_BUF));
#endif
}
void
arCF_freeMI_BUF(ptr)
AR_MI_BUF *ptr;
{
#ifdef DEBUG
arFree(ptr, sizeof(AR_MI_BUF), "AR_MI_BUF");
#else
free(ptr);
#endif
}
AR_INDEX_BUF *
arCF_mallocINDEX_BUF()
{
#ifdef DEBUG
return (AR_INDEX_BUF *)arMalloc(sizeof(AR_INDEX_BUF), "AR_INDEX_BUF");
#else
return (AR_INDEX_BUF *)malloc(sizeof(AR_INDEX_BUF));
#endif
}
void
arCF_freeINDEX_BUF(ptr)
AR_INDEX_BUF *ptr;
{
#ifdef DEBUG
arFree(ptr, sizeof(AR_INDEX_BUF), "AR_INDEX_BUF");
#else
free(ptr);
#endif
}
AR_DATA_BUF *
arCF_mallocDATA_BUF()
{
#ifdef DEBUG
return (AR_DATA_BUF *)arMalloc(sizeof(AR_DATA_BUF), "AR_DATA_BUF");
#else
return (AR_DATA_BUF *)malloc(sizeof(AR_DATA_BUF));
#endif
}
void
arCF_freeDATA_BUF(ptr)
AR_DATA_BUF *ptr;
{
#ifdef DEBUG
arFree(ptr, sizeof(AR_DATA_BUF), "AR_DATA_BUF");
#else
free(ptr);
#endif
}
AR_GR_BUF *
arCF_mallocGR_BUF()
{
#ifdef DEBUG
return (AR_GR_BUF *)arMalloc(sizeof(AR_GR_BUF), "AR_GR_BUF");
#else
return (AR_GR_BUF *)malloc(sizeof(AR_GR_BUF));
#endif
}
void
arCF_freeGR_BUF(ptr)
AR_GR_BUF *ptr;
{
#ifdef DEBUG
arFree(ptr, sizeof(AR_GR_BUF), "AR_GR_BUF");
#else
free(ptr);
#endif
}
/*+/subr**********************************************************************
* NAME arCFAsserts - check some assumptions used in arChanIO routines
*
* DESCRIPTION
* This routine checks a number of hardware and software items
* which influence the proper operation of the arChanIO routines and
* compatibility between various execution platforms. Aside from
* the simple motivation of being able to run, these checks are
* important in avoiding the clobbering of a file full of data.
*
* RETURNS
* OK
*
* BUGS
* o text
*
* SEE ALSO
*
* EXAMPLE
*
*-*/
long
arCFAsserts()
{
static ULONG in[]={0,0,0}, out[]={0,0,0}, i;
char between[4];
TS_STAMP time_s;
assertAlways(4 == sizeof(time_s.secPastEpoch));
/* important for file layout; also
needed to make structures properly
aligned */
assertAlways(4 == sizeof(time_s.nsec));/* important for file layout; also
needed to make structures properly
aligned */
assertAlways(1 == sizeof(char));
assertAlways(2 == sizeof(short));
assertAlways(4 == sizeof(long));
assertAlways(4 == sizeof(float));
assertAlways(8 == sizeof(double));
assertAlways(AR_UNITS_DIM == 8); /* important for file layout */
assertAlways(AR_UNITS_DIM % 8 == 0);/* important for file layout; also
needed to make structures properly
aligned */
assertAlways(db_state_dim == 16); /* important for file layout; also
needed to make structures properly
aligned */
assertAlways(db_state_text_dim == 26);/* important for file layout; also
needed to make structures properly
aligned */
assertAlways(dbr_size[DBF_ENUM] == 2);/* important for file layout; also
needed for proper functioning of
code which copies DBF_ENUM values */
assertAlways(AR_UNITS_DIM % 8 == 0);/* important for file layout; also
needed to make structures properly
aligned */
assertAlways(AR_GR_BUF_PAD >= 1); /* block has to hold state strings */
assertAlways(ERROR < 0); /* must not be a valid block number */
/*----------------------------------------------------------------------------
* make sure that changing long to character stream and back actually works
*---------------------------------------------------------------------------*/
in[1] = 0x87654321L;
for (i=0; i<4; i++) {
between[i] = *(((char *)&in[1])+i);
}
for (i=0; i<4; i++) {
*(((char *)&out[1])+i) = between[i];
}
assertAlways(in[1] == out[1]);
return OK;
}
/*+/subr**********************************************************************
* NAME arCF_DFillRbufAndGetc - get next data buffer and get a character
*
* DESCRIPTION
* Gets the next data buffer for reading, puts the time stamp
* information from datInfo into the chanDesc, and returns the first
* character for reading.
*
* The time stamp information in the chanDesc is initialized from
* the datInfo structure in the index block, if the datInfo
* actually contains a time stamp. (Remember that blocks which
* don't have the beginning of an item have zero time stamp in
* their datInfo.)
*
* RETURNS
* character, or
* ERROR
*
*-*/
long
arCF_DFillRbufAndGetc(pArChanDesc)
AR_CHAN_DESC *pArChanDesc; /* pointer to channel descriptor */
{
if (arCF_DNextBlock(pArChanDesc) != OK)
return ERROR;
if (--pArChanDesc->remainCount >= 0) {
if (ArCDDatInfo(pArChanDesc).stamp.secPastEpoch != 0)
pArChanDesc->timeStamp = ArCDDatInfo(pArChanDesc).stamp;
return ((int)(*(++pArChanDesc->pData))) & 0xff;
}
else
assertAlways(0);
/* NOTREACHED */
}
/*+/subr**********************************************************************
* NAME arCF_DFlushAndFree - flush data block and free buffer
*
* DESCRIPTION
* If the data block has been modified, the `.lastByte' item in the
* block is updated and the block is written.
*
* The block is free'd and the buffer pointer in the chanDesc is
* set to NULL.
*
* RETURNS
* OK, or
* ERROR
*
* NOTES
* 1. This routine depends on the channel descriptor having .remainCount
* and .pData be relevant to the buffer being flushed.
*
* BUGS
* o doesn't provide a mechanism for multiple buffering
*
*-*/
long
arCF_DFlushAndFree(pArChanDesc)
AR_CHAN_DESC *pArChanDesc; /* IO pointer to channel descriptor */
{
int stat; /* status from calls */
int retStat=OK; /* return status to caller */
if (ArCFModifyTestAndReset(pArChanDesc->pArCfDesc, pArChanDesc->pDataBuf)){
assert((pArChanDesc->flags & AR_CDESC_WRITE) != 0);
if (pArChanDesc->pData != NULL) {
pArChanDesc->pDataBuf->bfInfo.lastByte = pArChanDesc->pData -
(char *)&pArChanDesc->pDataBuf->bfInfo;
assert(pArChanDesc->pDataBuf->bfInfo.lastByte > 0);
assert(pArChanDesc->pDataBuf->bfInfo.lastByte < 2000);
}
stat = bfWrite(pArChanDesc->pArCfDesc->pBfDesc,
&pArChanDesc->pDataBuf->bfInfo,
pArChanDesc->pDataBuf->blkNum);
if (stat != OK) {
(void)fprintf(stderr,
"arCF_DFlushAndFree: can't write data block\n");
retStat = ERROR;
}
}
arCF_freeDATA_BUF(pArChanDesc->pDataBuf);
pArChanDesc->pDataBuf = NULL;
return retStat;
}
/*+/subr**********************************************************************
* NAME arCF_DFlushWbufAndPutc - flush data buffer and write a character
*
* DESCRIPTION
* Write the current data block to the file, get a new data block for
* writing, and put the character into the new block. Manipulations
* for index and master index blocks are performed as needed.
*
* RETURNS
* OK, or
* ERROR
*
* BUGS
* o text
*
* EXAMPLE
* for (i=0; i<dataCount; i++) {
* if (--pArChanDesc->remainCount >= 0)
* *(char *)(++(pArChanDesc->pData)) = data[i];
* else
* stat = arCF_DFlushWbufAndPutc(data[i], pArChanDesc);
* }
*
*-*/
long
arCF_DFlushWbufAndPutc(c, pArChanDesc)
char c; /* I character to write */
AR_CHAN_DESC *pArChanDesc; /* IO pointer to channel descriptor */
{
assert(pArChanDesc != NULL);
if (arCF_DNextBlock(pArChanDesc) != OK)
return ERROR;
if (--pArChanDesc->remainCount >= 0)
*(++pArChanDesc->pData) = c;
else
assertAlways(0);
return OK;
}
/*+/subr**********************************************************************
* NAME arCF_DNextBlock - get next data block
*
* DESCRIPTION
* Gets the next data block, as dictated by the next datInfo in the
* index block. If the mode is O_RDWR, a new datInfo is created and
* a new data block is acquired. If the mode is O_RDONLY, then ERROR
* is returned if there are no more datInfo's, with AR_CDESC_EOF set
* in chanDesc.flags .
*
* RETURNS
* OK, or
* ERROR
*
*-*/
long
arCF_DNextBlock(pArChanDesc)
AR_CHAN_DESC *pArChanDesc; /* IO pointer to channel descriptor */
{
int stat; /* status from calls */
AR_DATA_BUF *pDataBufNew; /* pointer to new data buffer */
BF_BLKNUM blockNum; /* block number of new data block */
assert(pArChanDesc != NULL);
assert(pArChanDesc->pMIBuf != NULL);
if ((pArChanDesc->flags & AR_CDESC_WRITE) != 0) {
/*----------------------------------------------------------------------------
* O_RDWR -- write mode processing
*
* first, get a buffer to hold the next data block;
* then, get the next datInfo item.
*---------------------------------------------------------------------------*/
if ((pDataBufNew = arCF_mallocDATA_BUF()) == NULL) {
if ((pArChanDesc->flags & AR_CDESC_SUP) == 0) {
(void)fprintf(stderr,
"arCF_DNextBlock: can't malloc new data buf\n");
}
pArChanDesc->flags |= AR_CDESC_SUP; /* suppress msg */
return ERROR;
}
if (arCF_INextDatInfo(pArChanDesc) != OK) {
if ((pArChanDesc->flags & AR_CDESC_SUP) == 0) {
(void)fprintf(stderr,
"arCF_DNextBlock: error getting new datInfo\n");
}
pArChanDesc->flags |= AR_CDESC_SUP; /* suppress msg */
arCF_freeDATA_BUF(pDataBufNew);
return ERROR;
}
blockNum = ArCDDatInfo(pArChanDesc).dataBlock;
if (pArChanDesc->pDataBuf == NULL) {
/*----------------------------------------------------------------------------
* Case 1:
* this is the first call for the channel following the open. Read the
* last used data block for the channel. If there wasn't a "last used
* data block", acquire a new data block.
*---------------------------------------------------------------------------*/
if (blockNum > 0) {
stat = bfRead(pArChanDesc->pArCfDesc->pBfDesc,
&pDataBufNew->bfInfo, blockNum);
if (stat != OK) {
(void)fprintf(stderr,
"arCF_DNextBlock: error reading old data block\n");
arCF_freeDATA_BUF(pDataBufNew);
return ERROR;
}
pDataBufNew->blkNum = blockNum;
ArCFModifyInit(pDataBufNew);
pDataBufNew->pBlock = &pDataBufNew->bfInfo;
}
else {
blockNum = bfAcquire(pArChanDesc->pArCfDesc->pBfDesc,
&pDataBufNew->bfInfo);
if (blockNum == ERROR) {
if ((pArChanDesc->flags & AR_CDESC_SUP) == 0) {
(void)fprintf(stderr,
"arCF_DNextBlock: error acquiring new data block\n");
}
pArChanDesc->flags |= AR_CDESC_SUP; /* suppress msg */
arCF_freeDATA_BUF(pDataBufNew);
return ERROR;
}
pDataBufNew->blkNum = blockNum;
ArCFModifyInit(pDataBufNew);
pDataBufNew->pBlock = &pDataBufNew->bfInfo;
ArCFModifySet(pArChanDesc->pArCfDesc, pDataBufNew);
ArCDDatInfo(pArChanDesc).dataBlock = pDataBufNew->blkNum;
}
}
else {
/*----------------------------------------------------------------------------
* Case 2:
* this isn't the first call--instead, the caller wants the current
* buffer flushed and a new one started. Acquire a new block, link the
* old one to it, write the old block, and free its buffer.
*---------------------------------------------------------------------------*/
blockNum = bfAcquire(pArChanDesc->pArCfDesc->pBfDesc,
&pDataBufNew->bfInfo);
if (blockNum == ERROR) {
if ((pArChanDesc->flags & AR_CDESC_SUP) == 0) {
(void)fprintf(stderr,
"arCF_DNextBlock: error acquiring new data block\n");
}
pArChanDesc->flags |= AR_CDESC_SUP; /* suppress msg */
arCF_freeDATA_BUF(pDataBufNew);
return ERROR;
}
pDataBufNew->blkNum = blockNum;
ArCFModifyInit(pDataBufNew);
pDataBufNew->pBlock = &pDataBufNew->bfInfo;
ArCFModifySet(pArChanDesc->pArCfDesc, pDataBufNew);
ArCDDatInfo(pArChanDesc).dataBlock = pDataBufNew->blkNum;
pArChanDesc->pDataBuf->bfInfo.flink = blockNum;
if (pArChanDesc->pData != NULL) {
pArChanDesc->pDataBuf->bfInfo.lastByte = pArChanDesc->pData -
(char *)&pArChanDesc->pDataBuf->bfInfo;
assert(pArChanDesc->pDataBuf->bfInfo.lastByte > 0);
assert(pArChanDesc->pDataBuf->bfInfo.lastByte < 2000);
}
ArCFModifySet(pArChanDesc->pArCfDesc, pArChanDesc->pDataBuf);
if (arCF_DFlushAndFree(pArChanDesc) != OK) {
(void)fprintf(stderr,
"arCF_DNextBlock: error writing old block\n");
return ERROR;
}
}
/*----------------------------------------------------------------------------
* wrapup
*
* set up the channel descriptor for writing to the new block
*---------------------------------------------------------------------------*/
pArChanDesc->pDataBuf = pDataBufNew;
pArChanDesc->remainCount =
AR_CF_BLKSIZE - pDataBufNew->bfInfo.lastByte - 1;
pArChanDesc->pData =
(char *)&pDataBufNew->bfInfo + pDataBufNew->bfInfo.lastByte;
return OK;
}
else {
/*----------------------------------------------------------------------------
* O_RDONLY
*
* Case 1:
* this is the first call for the channel following the open. A buffer
* must be obtained, then the first available data block for the
* channel must be read.
*
* Case 2:
* this isn't the first call following open. Simply read the next
* available data block into the same buffer.
*
* NOTES:
* 1. pDataBufNew is used here as the buffer pointer--except for the first
* read for the channel, the `New' is inappropriate; peruse accordingly.
*---------------------------------------------------------------------------*/
if ((pDataBufNew = pArChanDesc->pDataBuf) == NULL) {
if ((pDataBufNew = arCF_mallocDATA_BUF()) == NULL) {
if ((pArChanDesc->flags & AR_CDESC_SUP) == 0) {
(void)fprintf(stderr,
"arCF_DNextBlock: can't malloc new data buf\n");
}
pArChanDesc->flags |= AR_CDESC_SUP; /* suppress msg */
return ERROR;
}
}
if (arCF_INextDatInfo(pArChanDesc) != OK)
return ERROR;
blockNum = ArCDDatInfo(pArChanDesc).dataBlock;
if (blockNum > 0) {
stat = arCF_DRead(pArChanDesc, pDataBufNew, blockNum);
if (stat != OK) {
(void)fprintf(stderr,
"arCF_DNextBlock: error reading next data block\n");
return ERROR;
}
}
else {
pArChanDesc->flags |= AR_CDESC_EOF;
return ERROR; /* no data to read */
}
pArChanDesc->pDataBuf = pDataBufNew;
return OK;
}
}
/*+/subr**********************************************************************
* NAME arCF_DRead - read a data block into a data buffer
*
* DESCRIPTION
*
* RETURNS
* OK, or
* ERROR
*
* BUGS
* o text
*
* SEE ALSO
*
* EXAMPLE
*
*-*/
long
arCF_DRead(pArChanDesc, pDataBuf, blockNum)
AR_CHAN_DESC *pArChanDesc; /* IO pointer to channel descriptor */
AR_DATA_BUF *pDataBuf; /* IO pointer to data buffer */
BF_BLKNUM blockNum; /* I block number to read */
{
if (bfRead(pArChanDesc->pArCfDesc->pBfDesc, &pDataBuf->bfInfo, blockNum)
!= OK)
return ERROR;
pDataBuf->blkNum = blockNum;
ArCFModifyInit(pDataBuf);
pDataBuf->pBlock = &pDataBuf->bfInfo;
/*----------------------------------------------------------------------------
* wrapup
*
* set up the channel descriptor for reading from the new block
* case 1: no bytes in block; set remainCount = 0, return ERROR, EOF;
* case 2: first read after open; use firstByte; this ignores overflow
* bytes in the first block.
* case 3: use BF_BLOCK_DATA, so as to read bytes which overflow from
* one block to the next.
*---------------------------------------------------------------------------*/
if (pDataBuf->bfInfo.lastByte < BF_BLOCK_DATA) {
pArChanDesc->remainCount = 0;
pArChanDesc->pData = (char *)&pDataBuf->bfInfo;
pArChanDesc->flags |= AR_CDESC_EOF;
return ERROR;
}
else {
int i;
if (pArChanDesc->remainCount < -9) { /* 1st read after open */
if ((i = pDataBuf->bfInfo.firstByte) < BF_BLOCK_DATA)
assertAlways(0); /* 1st block can't just have overflow */
}
else
i = BF_BLOCK_DATA;
pArChanDesc->remainCount = pDataBuf->bfInfo.lastByte -i +1;
pArChanDesc->pData = (char *)(&pDataBuf->bfInfo) +i -1;
}
return OK;
}
/*+/subr**********************************************************************
* NAME arCF_GRGetBlock - get a block to hold ENUM graphics info
*
* DESCRIPTION
*
* RETURNS
* OK, or
* ERROR
*
* BUGS
* o
*
*-*/
long
arCF_GRGetBlock(pArChanDesc)
AR_CHAN_DESC *pArChanDesc; /* IO pointer to channel descriptor */
{
int retStat=OK; /* return status to caller */
BF_BLKNUM block; /* number of acquired block */
AR_GR_BUF *pGRBuf; /* pointer to buffer for block */
assert(pArChanDesc != NULL);
assert(ArCDChanHdr(pArChanDesc).chanType == DBF_ENUM);
/*----------------------------------------------------------------------------
* allocate a buffer to hold new block and acquire a new block
*---------------------------------------------------------------------------*/
if ((pGRBuf = arCF_mallocGR_BUF()) == NULL) {
(void)fprintf(stderr,
"arCF_GRAddBlock: can't malloc for new GR block\n");
retStat = ERROR;
}
if (retStat == OK) {
block = bfAcquire(pArChanDesc->pArCfDesc->pBfDesc, &pGRBuf->bfInfo);
if (block == ERROR) {
(void)fprintf(stderr,
"arCF_GRAddBlock: can't acquire block for grInfo\n");
retStat = ERROR;
arCF_freeGR_BUF(pGRBuf);
}
}
if (retStat == OK) {
pGRBuf->blkNum = block;
ArCFModifyInit(pGRBuf);
pGRBuf->pBlock = &pGRBuf->bfInfo;
/*----------------------------------------------------------------------------
* initialize the block file information for the block
*---------------------------------------------------------------------------*/
pGRBuf->bfInfo.flink = 0;
pGRBuf->bfInfo.firstByte = BF_BLOCK_DATA;
pGRBuf->bfInfo.lastByte = BF_BLOCK_DATA - 1;
ArCFModifySet(pArChanDesc->pArCfDesc, pGRBuf);
/*-----------------------------------------------------------------------------
* link the block and buffer into the chanHdr and set the modified
* bit for the MI block
*----------------------------------------------------------------------------*/
ArCDChanHdr(pArChanDesc).graphics.enumGr.block = block;
ArCDChanHdr(pArChanDesc).graphics.enumGr.pGRBuf = pGRBuf;
ArCFModifySet(pArChanDesc->pArCfDesc, pArChanDesc->pMIBuf);
pArChanDesc->pArCfDesc->b0Modified = 1;
}
return retStat;
}
/*+/subr**********************************************************************
* NAME arCF_GRReadBlock - get a buffer and read ENUM graphics info
*
* DESCRIPTION
*
* RETURNS
* OK, or
* ERROR
*
* BUGS
* o
*
*-*/
long
arCF_GRReadBlock(pArCfDesc, pArChanHdr)
AR_CF_DESC *pArCfDesc; /* I pointer to channel file descriptor */
AR_CHAN_HDR *pArChanHdr; /* IO pointer to chanHdr */
{
int retStat=OK; /* return status to caller */
BF_BLKNUM block; /* number of acquired block */
AR_GR_BUF *pGRBuf; /* pointer to buffer for block */
assert(pArCfDesc != NULL);
assert(pArChanHdr != NULL);
assert(pArChanHdr->chanType == DBF_ENUM);
/*----------------------------------------------------------------------------
* allocate a buffer to hold the block and read the block
*---------------------------------------------------------------------------*/
if ((pGRBuf = arCF_mallocGR_BUF()) == NULL) {
(void)fprintf(stderr, "arCF_GRReadBlock: can't malloc for GR block\n");
retStat = ERROR;
}
if (retStat == OK) {
block = bfRead(pArCfDesc->pBfDesc, &pGRBuf->bfInfo,
pArChanHdr->graphics.enumGr.block);
if (block == ERROR) {
(void)fprintf(stderr,
"arCF_GRReadBlock: can't read block for grInfo\n");
retStat = ERROR;
arCF_freeGR_BUF(pGRBuf);
}
}
if (retStat == OK) {
/*-----------------------------------------------------------------------------
* link the block into the chanHdr. (No need to set modified bit for
* MI block, since this is only a run-time change, not for disk.)
*----------------------------------------------------------------------------*/
pArChanHdr->graphics.enumGr.pGRBuf = pGRBuf;
pGRBuf->blkNum = block;
ArCFModifyInit(pGRBuf);
pGRBuf->pBlock = &pGRBuf->bfInfo;
}
return retStat;
}
/*+/subr**********************************************************************
* NAME arCF_IFlushAndFree - flush index block and free buffer
*
* DESCRIPTION
*
* RETURNS
* OK, or
* ERROR
*
* BUGS
* o
*
* SEE ALSO
*
* EXAMPLE
*
*-*/
long
arCF_IFlushAndFree(pArChanDesc)
AR_CHAN_DESC *pArChanDesc; /* IO pointer to channel descriptor */
{
int stat; /* status from calls */
int retStat=OK; /* return status to caller */
if (ArCFModifyTestAndReset(pArChanDesc->pArCfDesc, pArChanDesc->pIndexBuf)){
assert((pArChanDesc->flags & AR_CDESC_WRITE) != 0);
stat = bfWrite(pArChanDesc->pArCfDesc->pBfDesc,
&pArChanDesc->pIndexBuf->bfInfo,
pArChanDesc->pIndexBuf->blkNum);
if (stat != OK) {
(void)fprintf(stderr,
"arCF_IFlushAndFree: can't write index block\n");
retStat = ERROR;
}
}
arCF_freeINDEX_BUF(pArChanDesc->pIndexBuf);
pArChanDesc->pIndexBuf = NULL;
return retStat;
}
/*+/subr**********************************************************************
* NAME arCF_INextDatInfo - get next datInfo item in a channel's index
*
* DESCRIPTION
* Obtains the next datInfo element for a channel; the chanDescr will
* have the element's subscript. 'next' has several possible meanings,
* depending on the present state of the channel descriptor.
*
* If the channel descriptor is open O_RDWR, then the 'next' datInfo is
* either the last used datInfo (if this is the first call following
* opening the descriptor) or else a new datInfo at the tail end of the
* channel's index. When necessary, a new index block is obtained and,
* if necessary, the previous index buffer is written and the buffer
* is free()'d.
*
* In O_RDONLY mode, the 'next' datInfo is either the first available
* datInfo (if this is the first call following opening the descriptor)
* or else the next datInfo in the index. Initially, an index buffer is
* obtained; the same buffer is used for successive index blocks. When
* no more datInfo are available for reading, ERROR is returned, with
* AR_CDESC_EOF set in chanDesc.flags .
*
* On a successful return, the following fields in the chanDesc have
* been set up:
* o .pIndexBuf points to the (possibly new) index buffer
* o .datInfNum is the number of the datInf structure
*
* The chanHdr is updated, if necessary.
*
* RETURNS
* OK, or
* ERROR
*
* BUGS
* o a highly likely race exists between reading and writing: the chanHdr
* has up-to-date information about 'full' write buffers, and the reader
* has access to this information. In an asynchronous situation, it
* is unpredictable whether those write buffers will be available from
* disk or from memory or (depending on the race) from neither.
*
* NOTES
* 1. This routine is written so that it can be repeatedly called when
* an error condition exists and it won't break. This can be exploited
* by having read or write not check status until after the last byte
* has been transferred, which makes the transfer more efficient. This
* feature is implemented using the AR_CDESC_SUP flag in chanDesc .
* 2. The ArCDDatInfo() macro is used here. Use of this macro must FOLLOW
* storing a 'good' value for datInfNum in the channel descriptor.
*
* SEE ALSO
*
* EXAMPLE
*
*-*/
long
arCF_INextDatInfo(pArChanDesc)
AR_CHAN_DESC *pArChanDesc; /* IO pointer to channel descriptor */
{
int stat; /* status from calls */
AR_INDEX_BUF *pIndexBuf; /* pointer to index buffer */
BF_BLKNUM blockNum; /* block number of new index block */
short datInfNum; /* number of datInfo structure element */
assert(pArChanDesc != NULL);
assert(pArChanDesc->pMIBuf != NULL);
assert(pArChanDesc->pArCfDesc != NULL);
assert(pArChanDesc->pArCfDesc->pBfDesc != NULL);
pIndexBuf = pArChanDesc->pIndexBuf;
if ((pArChanDesc->flags & AR_CDESC_WRITE) != 0) {
/*----------------------------------------------------------------------------
* O_RDWR -- write mode processing. There are 4 cases to handle.
*
* increment datInfNum to point to next datInfo to be used.
*
* if the new datInfNum is < 0 or is >= AR_CF_NDATINFO, then a new index
* block (and thus index buffer) is needed. The buffer is obtained here
* and 'filled in' later by bfRead() or bfAcquire().
*---------------------------------------------------------------------------*/
datInfNum = ++pArChanDesc->datInfNum;
if (datInfNum < 0 || datInfNum >= AR_CF_NDATINFO) {
if ((pIndexBuf = arCF_mallocINDEX_BUF()) == NULL) {
if ((pArChanDesc->flags & AR_CDESC_SUP) == 0) {
(void)fprintf(stderr,
"arCF_INextDatInfo: can't malloc index buffer\n");
}
pArChanDesc->flags |= AR_CDESC_SUP; /* suppress msg */
--pArChanDesc->datInfNum;
return ERROR;
}
}
if (datInfNum >= 0 && datInfNum < AR_CF_NDATINFO)
goto rdwrCase1;
else if (datInfNum >= AR_CF_NDATINFO)
goto rdwrCase2;
else {
blockNum = ArCDChanHdr(pArChanDesc).indexTail;
if (blockNum > 0)
goto rdwrCase3;
else
goto rdwrCase4;
}
rdwrCase1:
/*----------------------------------------------------------------------------
* Case 1: the next datInfo in the index block is available for use.
*
* o adjust lastByte in the index block
* o set up chanDesc for the datInfo
* o set up new datInfo[datInfNum]
*---------------------------------------------------------------------------*/
/* datInfNum in descriptor set up by ++ above */
pIndexBuf->bfInfo.lastByte += sizeof(AR_DAT_INFO);
assert(pIndexBuf->bfInfo.lastByte > 0);
assert(pIndexBuf->bfInfo.lastByte < 2000);
ArCDDatInfo(pArChanDesc).dataBlock = (BF_BLKNUM)0;
ArCDDatInfo(pArChanDesc).stamp.secPastEpoch = 0;
ArCDDatInfo(pArChanDesc).stamp.nsec = 0;
ArCFModifySet(pArChanDesc->pArCfDesc, pIndexBuf);
goto rdwrEnd;
rdwrCase3:
/*----------------------------------------------------------------------------
* Case 3: this is the first call after the channel has been opened, and
* the file already contains data for the channel. The chanHdr points
* to an existing index block (indexTail), and that index block already
* has a datInfo structure set up pointing to a data block.
*
* o read the indexTail block into the index buffer
* o set up chanDesc for the indexBuf and datInfo
* o set up chanDesc for secPastEpoch from chanHdr "newest" values
*---------------------------------------------------------------------------*/
stat = arCF_IRead(pArChanDesc, pIndexBuf, blockNum);
if (stat != OK) {
(void)fprintf(stderr,
"arCF_INextDatInfo: error reading index block\n");
arCF_freeINDEX_BUF(pIndexBuf);
--pArChanDesc->datInfNum;
return ERROR;
}
datInfNum = (pIndexBuf->bfInfo.lastByte - BF_BLOCK_DATA) /
sizeof(AR_DAT_INFO);
/* firstByte and lastByte are OK */
pArChanDesc->pIndexBuf = pIndexBuf;
pArChanDesc->datInfNum = datInfNum;
pArChanDesc->timeStamp.secPastEpoch =
ArCDChanHdr(pArChanDesc).newestSecPastEpoch;
pArChanDesc->timeStamp.nsec = ArCDChanHdr(pArChanDesc).newestNsec;
/* the information in chanHdr is OK */
/* the information in datInfo is OK */
goto rdwrEnd;
rdwrCase2:
rdwrCase4:
/*----------------------------------------------------------------------------
* Case 2: the current datInfo uses the last slot in the index block.
*
* Case 4: this is the first call after the channel has been opened, but
* the file doesn't yet contain data for the channel.
*
* A new index block is needed, with datInfo[0] to be the 'next' datInfo.
* Much of the code is common for these two cases; for each action item,
* the case(s) which use the item are indicated.
*
* 2,4 acquire a new index block and install it in the index buffer,
* marking the buffer as MODIFIED.
* 2,4 set up firstByte and lastByte in the index block
* 2 set the flink in the current index block to point to the new one
* and write the current index block, free()ing its buffer
* 4 set chanHdr.indexHead to point to the new index block
* 2,4 install the new block in the chanHdr (in the MI block); mark the
* MI buffer as MODIFIED.
* 2,4 set up chanDesc for the indexBuf and datInfo
* 4 set up chanDesc for secPastEpoch as "none"
* 2,4 set up datInfo[0]
*---------------------------------------------------------------------------*/
blockNum = bfAcquire(pArChanDesc->pArCfDesc->pBfDesc,
&pIndexBuf->bfInfo);
if (blockNum == ERROR) {
if ((pArChanDesc->flags & AR_CDESC_SUP) == 0) {
(void)fprintf(stderr,
"arCF_INextDatInfo: can't acquire index block \n");
}
pArChanDesc->flags |= AR_CDESC_SUP; /* suppress msg */
arCF_freeINDEX_BUF(pIndexBuf);
--pArChanDesc->datInfNum;
return ERROR;
}
datInfNum = 0;
pIndexBuf->blkNum = blockNum;
ArCFModifyInit(pIndexBuf);
pIndexBuf->pBlock = &pIndexBuf->bfInfo;
pIndexBuf->bfInfo.firstByte = BF_BLOCK_DATA;
pIndexBuf->bfInfo.lastByte = BF_BLOCK_DATA + sizeof(AR_DAT_INFO) - 1;
ArCFModifySet(pArChanDesc->pArCfDesc, pIndexBuf);
if (ArCDChanHdr(pArChanDesc).indexHead == 0) { /* case 4 */
ArCDChanHdr(pArChanDesc).indexHead = blockNum;
pArChanDesc->timeStamp.secPastEpoch = 0;
pArChanDesc->timeStamp.nsec = 0;
}
else { /* case 2 */
assert(pArChanDesc->pIndexBuf->blkNum ==
ArCDChanHdr(pArChanDesc).indexTail);
assert(pArChanDesc->pIndexBuf->bfInfo.flink == (BF_BLKNUM)0);
pArChanDesc->pIndexBuf->bfInfo.flink = (BF_BLKNUM)blockNum;
ArCFModifySet(pArChanDesc->pArCfDesc, pArChanDesc->pIndexBuf);
if (arCF_IFlushAndFree(pArChanDesc) != OK) {
(void)fprintf(stderr,
"arCF_INextDatInfo: error writing previous index block\n");
/* ignore this error */
}
}
ArCDChanHdr(pArChanDesc).indexTail = blockNum;
ArCFModifySet(pArChanDesc->pArCfDesc, pArChanDesc->pMIBuf);
pArChanDesc->pArCfDesc->b0Modified = 1;
pArChanDesc->pIndexBuf = pIndexBuf;
pArChanDesc->datInfNum = datInfNum;
ArCDDatInfo(pArChanDesc).dataBlock = (BF_BLKNUM)0;
ArCDDatInfo(pArChanDesc).stamp.secPastEpoch = 0;
ArCDDatInfo(pArChanDesc).stamp.nsec = 0;
rdwrEnd:
;
}
else {
int datInfLast; /* # of last datInfo in block */
/*----------------------------------------------------------------------------
* O_RDONLY -- read mode processing. There are 3 cases to handle.
*
* increment datInfNum to point to next datInfo to be used. If there is
* no index buffer, then datInfNum will have been initialized elsewhere
* to -10 .
*---------------------------------------------------------------------------*/
datInfNum = ++pArChanDesc->datInfNum;
if (pArChanDesc->pIndexBuf == NULL)
datInfLast = -1;
else {
datInfLast = (pArChanDesc->pIndexBuf->bfInfo.lastByte -
pArChanDesc->pIndexBuf->bfInfo.firstByte) / sizeof(AR_DAT_INFO);
}
if (datInfNum >= 0 && datInfNum <= datInfLast) {
/*----------------------------------------------------------------------------
* Case 1: the next datInfo in the index block is available for reading.
*
* o set up chanDesc for the indexBuf and datInfNum
* o set up chanDesc for time stamp from datInfo
*---------------------------------------------------------------------------*/
assert(pArChanDesc->pIndexBuf != NULL);
/* datInfNum in chanDesc set up by ++ above */
if (ArCDDatInfo(pArChanDesc).stamp.secPastEpoch != 0)
pArChanDesc->timeStamp = ArCDDatInfo(pArChanDesc).stamp;
}
else {
/*----------------------------------------------------------------------------
* Case 2: the current datInfo was in the last one in the index block.
* Case 3: this is the first call after the channel was opened.
*
* o if there is no index buffer, allocate one
* o get the blockNum of the next index block, either from 'flink'
* (for case 2) or from indexHead (for case 3)
* o read the next index block
* o set up chanDesc for the indexBuf and datInfNum
* o set up chanDesc for time stamp from datInfo
*---------------------------------------------------------------------------*/
if (pArChanDesc->pIndexBuf == NULL) {
if ((pArChanDesc->pIndexBuf = arCF_mallocINDEX_BUF()) ==
NULL) {
if ((pArChanDesc->flags & AR_CDESC_SUP) == 0) {
(void)fprintf(stderr,
"arCF_INextDatInfo: can't malloc index buffer\n");
}
pArChanDesc->flags |= AR_CDESC_SUP; /* suppress msg */
--pArChanDesc->datInfNum;
return ERROR;
}
}
if (datInfNum < 0)
blockNum = ArCDChanHdr(pArChanDesc).indexHead;
else
blockNum = pArChanDesc->pIndexBuf->bfInfo.flink;
if (blockNum == 0) {
pArChanDesc->flags |= AR_CDESC_EOF;
--pArChanDesc->datInfNum;
return ERROR;
}
stat = arCF_IRead(pArChanDesc, pArChanDesc->pIndexBuf, blockNum);
if (stat != OK) {
(void)fprintf(stderr,
"arCF_INextDatInfo: error reading index block\n");
--pArChanDesc->datInfNum;
return ERROR;
}
if (pArChanDesc->pIndexBuf->bfInfo.firstByte < BF_BLOCK_DATA) {
pArChanDesc->flags |= AR_CDESC_EOF;
--pArChanDesc->datInfNum;
return ERROR;
}
pArChanDesc->datInfNum =
(pArChanDesc->pIndexBuf->bfInfo.firstByte - BF_BLOCK_DATA) /
sizeof(AR_DAT_INFO);
assert(pArChanDesc->datInfNum >= 0);
assert(pArChanDesc->datInfNum < AR_CF_NDATINFO);
if (ArCDDatInfo(pArChanDesc).stamp.secPastEpoch != 0)
pArChanDesc->timeStamp= ArCDDatInfo(pArChanDesc).stamp;
}
}
return OK;
}
/*+/subr**********************************************************************
* NAME arCF_IRead - read an index block into an index buffer
*
* DESCRIPTION
*
* RETURNS
* OK, or
* ERROR
*
* BUGS
* o text
*
* SEE ALSO
*
* EXAMPLE
*
*-*/
long
arCF_IRead(pArChanDesc, pIndexBuf, blockNum)
AR_CHAN_DESC *pArChanDesc; /* IO pointer to channel descriptor */
AR_INDEX_BUF *pIndexBuf; /* IO pointer to index buffer */
BF_BLKNUM blockNum; /* I block number to read */
{
if (bfRead(pArChanDesc->pArCfDesc->pBfDesc, &pIndexBuf->bfInfo, blockNum)
!= OK)
return ERROR;
pIndexBuf->blkNum = blockNum;
ArCFModifyInit(pIndexBuf);
pIndexBuf->pBlock = &pIndexBuf->bfInfo;
return OK;
}
/*+/subr**********************************************************************
* NAME arCF_MIAddBlock - add a new block to the master index
*
* DESCRIPTION
* Acquire a block (either from free block list or by expanding the
* file) and add it to the master index. The new block is formatted
* with 'free channel headers', which are linked into the free chanHdr
* list, at its head.
*
* The MODIFIED flag is set for the new MI block. If there was a 'tail'
* MI block which has been linked to this one, its MODIFIED flag is
* also set.
*
* RETURNS
* OK, or
* ERROR
*
*-*/
long
arCF_MIAddBlock(pArCfDesc)
AR_CF_DESC *pArCfDesc; /* IO pointer to channel file descriptor */
{
int retStat=OK; /* return status to caller */
BF_BLKNUM block; /* number of acquired block */
AR_MI_BUF *pMIBuf; /* pointer to buffer for block */
int i; /* temp for loops */
assert(pArCfDesc != NULL);
assert(pArCfDesc->pBfDesc != NULL);
/*----------------------------------------------------------------------------
* allocate a buffer to hold new block and acquire a new block
*---------------------------------------------------------------------------*/
pMIBuf = arCF_mallocMI_BUF();
if (pMIBuf == NULL) {
(void)fprintf(stderr,
"arCF_MIaddBlock: can't malloc for new MI block\n");
retStat = ERROR;
}
if (retStat == OK) {
block = bfAcquire(pArCfDesc->pBfDesc, &pMIBuf->bfInfo);
if (block == ERROR) {
(void)fprintf(stderr,
"arCF_MIaddBlock: can't acquire block for file\n");
retStat = ERROR;
}
}
if (retStat == OK) {
/*----------------------------------------------------------------------------
* set up miscellaneous fields in new block
*
* install this block as MIHead, if it's the first MI block
* if there was an old MITail, link it to this new block and mark it as
* modified
* install this block as MITail and mark it as modified
* increment count of MI blocks (block0 only)
*
* please pardon the confusion here, but there are two linked lists to
* maintain--the list of blocks on disk and the list of buffers in memory.
* The disk list is rooted in block0; the memory list is rooted in the
* channel file descriptor.
*---------------------------------------------------------------------------*/
pMIBuf->blkNum = block;
ArCFModifyInit(pMIBuf);
pMIBuf->pBlock = &pMIBuf->bfInfo;
ArCFModifySet(pArCfDesc, pMIBuf);
pArCfDesc->b0Modified = 1;
pMIBuf->pNextMI = NULL;
if (pArCfDesc->pMIHead == NULL) {
assert(ArB0MIHead(pArCfDesc) == 0);
assert(pArCfDesc->pMITail == NULL);
assert(ArB0MITail(pArCfDesc) == 0);
pArCfDesc->pMIHead = pMIBuf;
ArB0MIHead(pArCfDesc) = block;
}
else {
assert(pArCfDesc->pMITail != NULL);
assert(ArB0MITail(pArCfDesc) != 0);
pArCfDesc->pMITail->pNextMI = pMIBuf;
pArCfDesc->pMITail->bfInfo.flink = block;
ArCFModifySet(pArCfDesc, pArCfDesc->pMITail);
pArCfDesc->b0Modified = 1;
}
pArCfDesc->pMITail = pMIBuf;
ArB0MITail(pArCfDesc) = block;
ArB0MINblk(pArCfDesc) += 1;
/*----------------------------------------------------------------------------
* 'format' the new block into individual free channel headers and link
* these into the memory (CfDesc) and disk (block0) structures. (This
* block is put at the head of the list of free chanHdr's.) The name in
* each free channel header has its first character set to '\0' .
*---------------------------------------------------------------------------*/
for (i=0; i<AR_CF_NHDR; i++) {
pMIBuf->chanHdr[i].name[0] = '\0';
pMIBuf->chanHdr[i].graphics.nextMIFree.blkNum =
i<AR_CF_NHDR-1 ? block : ArB0MIFree_blkNum(pArCfDesc);
pMIBuf->chanHdr[i].graphics.nextMIFree.hdrNum =
i<AR_CF_NHDR-1 ? i+1 : ArB0MIFree_hdrNum(pArCfDesc);
}
ArB0MIFree_blkNum(pArCfDesc) = block;
ArB0MIFree_hdrNum(pArCfDesc) = 0;
pArCfDesc->pMIFree = pMIBuf;
pArCfDesc->MIFree_hdrNum = 0;
/*----------------------------------------------------------------------------
* initialize the block file information for the block; the file
* flink in the previous block (if there was a previous block) was set above
*---------------------------------------------------------------------------*/
pMIBuf->bfInfo.flink = 0;
pMIBuf->bfInfo.firstByte = BF_BLOCK_DATA;
pMIBuf->bfInfo.lastByte = BF_BLOCK_DATA - 1 +
AR_CF_NHDR * sizeof(AR_CHAN_HDR);
}
return retStat;
}
/*+/subr**********************************************************************
* NAME arCF_MIAddChan - add a channel to the master index
*
* DESCRIPTION
* Gets the next free channel header from the free chanHdr list and
* performs some initialization for the new chanHdr. A new MI block
* is acquired if there were no entries on the free chanHdr list.
*
* The MODIFIED flag is set for the MI block.
*
* This routine isn't intended as the primary user routine for adding
* a channel to a channel file--use arCFChanOpen() instead.
*
* RETURNS
* OK, or
* ERROR
*
* BUGS
* o if the forward link (to the next free chanHdr) in the acquired
* chanHdr points to another block, then a search of the entire MI
* list may be necessary to find the MI buffer pointer to store in
* the channel file descriptor for pMIFree.
* o doesn't add the channel to the hash table (which doesn't
* presently exist)
*
* SEE ALSO
* arCFChanOpen(), arCF_MIDelChan
*
*-*/
long
arCF_MIAddChan(pArCfDesc, name, ppMIBuf, pIndex)
AR_CF_DESC *pArCfDesc; /* IO pointer to channel file descriptor */
char *name; /* I channel name */
AR_MI_BUF **ppMIBuf; /* O ptr to ptr to MI block containing channel */
int *pIndex; /* O pointer to location to store index within
MI block of chanHdr of added channel */
{
int retStat=OK; /* return status to caller */
AR_MI_BUF *pMIBuf; /* ptr to MI block for new chanHdr */
short indx; /* chanHdr index in MI block */
AR_CHAN_HDR *pChanHdr; /* pointer to actual chanHdr */
assert(pArCfDesc != NULL);
assert(pArCfDesc->pBfDesc != NULL);
assert(name != NULL);
assert(ppMIBuf != NULL);
assert(pIndex != NULL);
assert(strlen(name) > 0);
assert(strlen(name) < AR_NAME_DIM);
/*----------------------------------------------------------------------------
* if there aren't any entries on the free chanHdr list (pMIFree), then
* add a new MI block to the file, putting its chanHdr's on the free list
*---------------------------------------------------------------------------*/
if (pArCfDesc->pMIFree == NULL) {
if (arCF_MIAddBlock(pArCfDesc) != OK)
retStat = ERROR;
}
/*----------------------------------------------------------------------------
* get a chanHdr, removing it from the free chanHdr list. Then
* initialize the chanHdr with some default information, including the
* name provided by the caller.
*---------------------------------------------------------------------------*/
if (retStat == OK) {
BF_BLKNUM blkNum; /* block # for next empty chanHdr */
int hdrNum; /* index in block for next empty chanHdr */
/*----------------------------------------------------------------------------
* grab the first entry from the free chanHdr list, getting its block
* address and subscript, and also getting the actual address of the
* chanHdr
*---------------------------------------------------------------------------*/
pMIBuf = pArCfDesc->pMIFree; /* 'address' of this */
indx = pArCfDesc->MIFree_hdrNum; /* chanHdr */
pChanHdr = &pMIBuf->chanHdr[indx];
assert(pChanHdr->name[0] == '\0'); /* make sure it's empty */
/*----------------------------------------------------------------------------
* set the head of the free chanHdr list to point to the next free
* chanHdr. Several special cases exist: no 'next'; 'next' is in the
* same MI block as the chanHdr we just got; 'next' is in the next
* MI block. If none of the special cases are true, then the whole
* MI chain is searched for the MI buffer with the specified block
* number (remember, we don't have a direct way to translate between
* 'disk block' number and 'address of buffer in memory').
*
* the 'free chanHdr list' has two parts to be set: the block number and
* index in block0; and the buffer address and index in the channel file
* descriptor.
*---------------------------------------------------------------------------*/
blkNum = pChanHdr->graphics.nextMIFree.blkNum;
hdrNum = pChanHdr->graphics.nextMIFree.hdrNum;
if (blkNum == 0) { /* no 'next' empty chanHdr */
pArCfDesc->pMIFree = NULL;
pArCfDesc->MIFree_hdrNum = 0;
}
else if (blkNum == pMIBuf->blkNum) { /* in this block? */
pArCfDesc->pMIFree = pMIBuf;
pArCfDesc->MIFree_hdrNum = hdrNum;
}
else { /* search for buffer with next empty chanHdr */
AR_MI_BUF *pMIFree; /* temp during search */
if (pMIBuf->pNextMI != NULL) { /* in next block? */
if (blkNum == pMIBuf->pNextMI->blkNum) {
pMIFree = pMIBuf->pNextMI;
goto searchDone;
}
}
pMIFree = pArCfDesc->pMIHead;
while (pMIFree != NULL) {
if (pMIFree->blkNum == blkNum)
goto searchDone;
pMIFree = pMIFree->pNextMI;
}
searchDone:
assert(pMIFree != NULL); /* MUST find a match! */
assert(pMIFree->chanHdr[hdrNum].name[0] == '\0');
pArCfDesc->pMIFree = pMIFree;
pArCfDesc->MIFree_hdrNum = hdrNum;
}
ArB0MIFree_blkNum(pArCfDesc) = blkNum;
ArB0MIFree_hdrNum(pArCfDesc) = hdrNum;
/*----------------------------------------------------------------------------
* initialize the new chanHdr
*
* mark the MI block as having been altered
*---------------------------------------------------------------------------*/
(void)strcpy(pChanHdr->name, name);
pChanHdr->chanType = TYPENOTCONN;
pChanHdr->elCount = 0;
pChanHdr->indexHead = 0;
pChanHdr->indexTail = 0;
pChanHdr->oldestSecPastEpoch = 0;
pChanHdr->newestSecPastEpoch = 0;
pChanHdr->newestNsec = 0;
pChanHdr->flags = 0;
pChanHdr->count = 0;
ArCFModifySet(pArCfDesc, pMIBuf);
pArCfDesc->b0Modified = 1;
*ppMIBuf = pMIBuf;
*pIndex = indx;
}
return retStat;
}
/*+/subr**********************************************************************
* NAME arCF_MIDelChan - delete a chanHdr from master index
*
* DESCRIPTION
* Deletes a chanHdr from the master index, making it into a
* 'free chanHdr' and placing it at the head of the free chanHdr list.
*
* The MODIFIED bit is set the the MI block.
*
* RETURNS
* OK
*
* BUGS
* o doesn't remove the channel from the hash table (which doesn't
* presently exist)
* o doesn't check for index blocks and data blocks--if either exists,
* memory buffers and disk blocks are essentially 'lost'.
*
* SEE ALSO
* arCF_MIAddChan()
*
*-*/
long
arCF_MIDelChan(pArCfDesc, pMIBuf, indx)
AR_CF_DESC *pArCfDesc; /* IO pointer to channel file descriptor */
AR_MI_BUF *pMIBuf; /* IO pointer to MI block containing channel */
int indx; /* I index within MI block of chanHdr for channel */
{
assert(pArCfDesc != NULL);
assert(pArCfDesc->pBfDesc != NULL);
assert(indx >= 0);
assert(indx < BfB0BlockSize(pArCfDesc->pBfDesc) / sizeof(AR_CHAN_HDR));
/*----------------------------------------------------------------------------
* update disk information in disk block and block0
*---------------------------------------------------------------------------*/
pMIBuf->chanHdr[indx].graphics.nextMIFree.blkNum =
ArB0MIFree_blkNum(pArCfDesc);
pMIBuf->chanHdr[indx].graphics.nextMIFree.hdrNum =
ArB0MIFree_hdrNum(pArCfDesc);
ArB0MIFree_blkNum(pArCfDesc) = pMIBuf->blkNum;
ArB0MIFree_hdrNum(pArCfDesc) = indx;
/*----------------------------------------------------------------------------
* update pointer and index in channel file descriptor
*---------------------------------------------------------------------------*/
pArCfDesc->pMIFree = pMIBuf;
pArCfDesc->MIFree_hdrNum = indx;
/*----------------------------------------------------------------------------
* mark this chanHdr as 'free', and set the MODIFIED bit for the MI block
*---------------------------------------------------------------------------*/
pMIBuf->chanHdr[indx].name[0] = '\0';
ArCFModifySet(pArCfDesc, pMIBuf);
pArCfDesc->b0Modified = 1;
return OK;
}
/*+/subr**********************************************************************
* NAME arCF_MIFindChan - search master index for a specified channel
*
* DESCRIPTION
* Search for the specified channel name in the Master Index blocks
* in memory.
*
* RETURNS
* OK, or
* ERROR if channel name not found
*
* BUGS
* o uses a linear search through all MI blocks
* o all MI blocks must be in memory
*
* EXAMPLE
*
*-*/
long
arCF_MIFindChan(pArCfDesc, name, ppMIBuf, pIndex)
AR_CF_DESC *pArCfDesc; /* I pointer to channel file descriptor */
char *name; /* I channel name */
AR_MI_BUF **ppMIBuf; /* O ptr to ptr to MI block containing channel;
if NULL, no MI block pointer will be
returned */
int *pIndex; /* O pointer to location to store index within
MI block of chanHdr for channel; if NULL, no
index will be returned */
{
AR_MI_BUF *pMIBuf; /* pointer to MI block for search */
int i;
assert(pArCfDesc != NULL);
assert(pArCfDesc->pBfDesc != NULL);
assert(name != NULL);
assert(strlen(name) > 0);
assert(strlen(name) < AR_NAME_DIM);
pMIBuf = pArCfDesc->pMIHead; /* head of MI list in memory */
while (pMIBuf != NULL) {
for (i=0; i<AR_CF_NHDR; i++) {
if (pMIBuf->chanHdr[i].name[0] != '\0') {
if (strcmp(pMIBuf->chanHdr[i].name, name) == 0)
goto done;
}
}
pMIBuf = pMIBuf->pNextMI; /* next MI block in memory */
}
done:
if (ppMIBuf != NULL)
*ppMIBuf = pMIBuf;
if (pIndex != NULL)
*pIndex = i;
return(pMIBuf==NULL ? ERROR : OK);
}
long
arCF_MIFlush(pCfDesc, pMIBuf)
AR_CF_DESC *pCfDesc; /* I pointer to channel file descriptor */
AR_MI_BUF *pMIBuf; /* I pointer to MI block */
{
int stat; /* status from calls */
int retStat=OK; /* return status */
if (ArCFModifyTestAndReset(pCfDesc, pMIBuf)) {
stat = bfWrite(pCfDesc->pBfDesc, &pMIBuf->bfInfo, pMIBuf->blkNum);
if (stat != OK)
retStat = ERROR;
}
if (pCfDesc->b0Modified) {
pCfDesc->b0Modified = 0;
stat = bfWrite(pCfDesc->pBfDesc, pCfDesc->pBfDesc->pBlock0, 0);
if (stat != OK)
retStat = ERROR;
}
return retStat;
}