From 9052a05c48c6bb98a82c14713e7d0a90c154cba2 Mon Sep 17 00:00:00 2001 From: Bob Zieman Date: Tue, 28 May 1991 15:55:41 +0000 Subject: [PATCH] Initial revision --- src/libCom/arAccessLib.c | 2748 ++++++++++++++++++++++++++++++++++++++ src/libCom/arCSCheck.c | 120 ++ src/libCom/arChanIO.c | 1635 +++++++++++++++++++++++ 3 files changed, 4503 insertions(+) create mode 100644 src/libCom/arAccessLib.c create mode 100644 src/libCom/arCSCheck.c create mode 100644 src/libCom/arChanIO.c diff --git a/src/libCom/arAccessLib.c b/src/libCom/arAccessLib.c new file mode 100644 index 000000000..24301a766 --- /dev/null +++ b/src/libCom/arAccessLib.c @@ -0,0 +1,2748 @@ +/* $Id$ + * Author: Roger A. Cole + * Date: 03-09-90 + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, 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 mm-dd-yy rac version 2.0, installed in SCCS + * + * make options + * -DvxWorks makes a version for VxWorks + * -DNDEBUG don't compile assert() checking + * -DDEBUG compile various debug code, including checks on + * malloc'd memory + */ +/*+/mod*********************************************************************** +* TITLE arAccessLib.c - AR access library for AR data set files +* +* DESCRIPTION +* +* QUICK REFERENCE +* AR_CHAN_DESC *pCFDesc; pointer to 'AR channel file' descriptor +* +* #include some general use definitions +* #include definitions for time stamps +* #include definitions for Channel Access +* #include definitions for database related items +* #include structures for AR access library routines +* #ifdef vxWorks +* # include obtain mode definitions for VxWorks +* #else +* # include obtain mode definitions for SunOS +* #endif +* +* AR_CHAN_DESC *pChanDesc; pointer to channel descriptor in chan file +* AR_CF_DESC *pCfDesc; pointer to channel file descriptor +* chtype type; one of the DBR_xxx types +* TS_STAMP *pStamp; pointer to a time stamp structure +* int mode; mode to open file or channel--O_RDONLY +* or O_RDWR +* +* long arCFChanClose( >ppChanDesc ) +* long arCFChanDel( >ppChanDesc ) +* long arCFChanFind( pCfDesc, chanName ) +* AR_CHAN_DESC *arCFChanOpen( pCfDesc, chanName, mode ) +* long arCFChanPosition( pChanDesc, pStamp ) +* long arCFChanPurge( pChanDesc, pStamp ) +* long arCFChanRead( pChanDesc, type, >pDbrBuf, count) +* long arCFChanWrite( pChanDesc, pEvHandArg ) +* long arCFChanWriteGR( pChanDesc, pEvHandArg ) +* long arCFClose( >ppCfDesc ) +* AR_CF_DESC *arCFCreate( name, maxBytes ) +* AR_CF_DESC *arCFOpen( name, mode ) +* long arCFSyncRead( pCfDesc ) +* long arCFSyncWrite( pCfDesc ) +* +* int ArCFChanElementCount( pChanDesc ) +* chtype ArCFChanFieldType( pChanDesc ) +* char *ArCFChanName( pChanDesc ) +* int ArCFChanNStates( pChanDesc ) +* int ArCFChanPrec( pChanDesc ) +* char *ArCFChanStates( pChanDesc ) +* char *ArCFChanUnits( pChanDesc ) +* +*-***************************************************************************/ + +#include +#ifndef INC_genDefs_h +# include +#endif +#ifndef INC_tsDefs_h +# include +#endif +#ifndef INC_arCS_h +# include +#endif +#ifndef INCLcadefh +# include +#endif + +#ifdef vxWorks +# include +# include /* for O_RDWR and O_RDONLY definitions */ +# include +# include +#else +# include +# include /* for O_RDWR and O_RDONLY definitions */ +# include +#endif + +/*+/internal------------------------------------------------------------------ +* 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 routines are used for calling malloc() and free() for +* some common data structures in arAccessLib. 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_CF_DESC *arCF_mallocCF_DESC() void arCF_freeCF_DESC(pCFDesc) +* AR_CHAN_DESC *arCF_mallocCHAN_DESC() void arCF_freeCHAN_DESC(pChanDesc) +*---------------------------------------------------------------------------*/ + +AR_CHAN_DESC * +arCF_mallocCHAN_DESC() +{ +#ifdef DEBUG + return (AR_CHAN_DESC *)arMalloc(sizeof(AR_CHAN_DESC), "AR_CHAN_DESC"); +#else + return (AR_CHAN_DESC *)GenMalloc(sizeof(AR_CHAN_DESC)); +#endif +} +void +arCF_freeCHAN_DESC(ptr) +AR_CHAN_DESC *ptr; +{ +#ifdef DEBUG + arFree(ptr, sizeof(AR_CHAN_DESC), "AR_CHAN_DESC"); +#else + GenFree(ptr); +#endif +} + +AR_CF_DESC * +arCF_mallocCF_DESC() +{ +#ifdef DEBUG + return (AR_CF_DESC *)arMalloc(sizeof(AR_CF_DESC), "AR_CF_DESC"); +#else + return (AR_CF_DESC *)GenMalloc(sizeof(AR_CF_DESC)); +#endif +} +void +arCF_freeCF_DESC(ptr) +AR_CF_DESC *ptr; +{ +#ifdef DEBUG + arFree(ptr, sizeof(AR_CF_DESC), "AR_CF_DESC"); +#else + GenFree(ptr); +#endif +} + +/*+/macro********************************************************************* +* NAME ArCFChanXxx - convenience macros for channel files +* +* DESCRIPTION +* These macros provide easy access to information about a channel in +* a channel file. The macros are defined in arAccessLib.h +* +* int ArCFChanElementCount( pArChanDesc ) +* chtype ArCFChanFieldType( pArChanDesc ) +* char *ArCFChanName( pArChanDesc ) +* +* The following marcos for obtaining graphics information bypasses +* the need to do a read with DBR_GR_xxx, and thus simplifies +* programming. However, programs which must run with both +* Channel Access and the AR access library should probably use a +* read to obtain the information. +* +* int ArCFChanNStates( pArChanDesc ) +* int ArCFChanPrec( pArChanDesc ) +* char *ArCFChanStates( pArChanDesc ) +* char *ArCFChanUnits( pArChanDesc ) +* +* RETURNS +* as indicated +* +* NOTES +* 1. These macros assume, but do not check, that the channel descriptor +* is open. +* 2. The macros for obtaining graphics information assume, but do not +* check, that the macro being used is appropriate for the native +* type of the channel. +* +*-*/ + + +/*+/subr********************************************************************** +* NAME arCFChanClose - close a channel of an AR channel data file +* +* DESCRIPTION +* Close a channel descriptor. If any I/O is outstanding for the +* descriptor, that I/O is forced to completion; this may include +* writing data and index blocks to disk and updating oldestStamp and +* newestStamp in the chanHdr. Memory buffers for data and index blocks +* are free()'d. +* +* The count of channel descriptors open to the channel is decremented. +* +* The channel descriptor is removed from the channel descriptor list +* in the channel file descriptor. The channel descriptor is free()'d +* and NULL is stored in caller's pointer to prevent further use +* +* RETURNS +* OK +* +* BUGS +* o doesn't flush the MI buffer +* o primitive error handling +* +* SEE ALSO +* arCFChanOpen(), arCFChanDel() +* +*-*/ +long +arCFChanClose(ppArChanDesc) +AR_CHAN_DESC **ppArChanDesc; /* IO ptr to ptr to channel descriptor; + will be NULL at completion */ +{ + int stat; /* status from calls */ + int retStat=OK; /* return status to caller */ + AR_CHAN_DESC *pArChanDesc; /* pointer to channel descriptor */ + AR_CHAN_HDR *pChanHdr; /* pointer to channel header */ + + assert(*ppArChanDesc != NULL); + pArChanDesc = *ppArChanDesc; + assert(pArChanDesc != NULL); + assert(pArChanDesc->pArCfDesc != NULL); + assert(pArChanDesc->pMIBuf != NULL); + pChanHdr = &ArCDChanHdr(pArChanDesc); + assert(pChanHdr->count > 0); + +/*---------------------------------------------------------------------------- +* flush data and index buffers and free() them. +* if the MI for this channel has been altered, write it +*---------------------------------------------------------------------------*/ + if (pArChanDesc->pDataBuf != NULL) { + stat = arCF_DFlushAndFree(pArChanDesc); + assert(stat == OK); + } + if (pArChanDesc->pIndexBuf != NULL) { + stat = arCF_IFlushAndFree(pArChanDesc); + assert(stat == OK); + } + if (pArChanDesc->pMIBuf != NULL) { + stat = arCF_MIFlush(pArChanDesc->pArCfDesc, pArChanDesc->pMIBuf); + assert(stat == OK); + } +/*---------------------------------------------------------------------------- +* remove this channel descriptor from the list of channel descriptors. +*---------------------------------------------------------------------------*/ + if (pArChanDesc->pNextDesc != NULL) + pArChanDesc->pNextDesc->pPrevDesc = pArChanDesc->pPrevDesc; + else + pArChanDesc->pArCfDesc->pChanDescTail = pArChanDesc->pPrevDesc; + if (pArChanDesc->pPrevDesc != NULL) + pArChanDesc->pPrevDesc->pNextDesc = pArChanDesc->pNextDesc; + else + pArChanDesc->pArCfDesc->pChanDescHead = pArChanDesc->pNextDesc; +/*---------------------------------------------------------------------------- +* decrement count of descriptors open to this chanHdr +* if this descriptor had the chanHdr open in O_RDWR mode, reset the +* flag in the chanHdr +*---------------------------------------------------------------------------*/ + pChanHdr->count -= 1; + if ((pArChanDesc->flags & AR_CDESC_WRITE) != 0) { + assert((pChanHdr->flags & AR_CHDR_WRITE) != 0); + pChanHdr->flags ^= AR_CHDR_WRITE; + } + +/*---------------------------------------------------------------------------- +* free the descriptor and put NULL in the caller's pointer, to prevent +* further use of the descriptor +*---------------------------------------------------------------------------*/ + arCF_freeCHAN_DESC(pArChanDesc); + *ppArChanDesc = NULL; + + return retStat; +} + +/*+/subr********************************************************************** +* NAME arCFChanDel - delete a channel of an AR channel data file +* +* DESCRIPTION +* Deletes all data and index buffers for the channel, deletes the +* chanHdr for the channel, and closes the channel descriptor. If the +* delete is successful, then the channel descriptor has been changed +* to NULL, preventing further use; memory associated with data buffers, +* index buffers, and the channel descriptor has been free()'d. Disk +* blocks which are deleted from the channel are added to the channel +* file's free block list; the chanHdr is added to the free chanHdr list. +* +* The channel descriptor must be open O_RDWR, and there must be no +* other channel descriptors open to the channel. +* +* RETURNS +* OK, or +* ERROR +* +* BUGS +* o text +* +* SEE ALSO +* +* EXAMPLE +* +*-*/ +long +arCFChanDel(ppArChanDesc) +AR_CHAN_DESC **ppArChanDesc; /* IO ptr to ptr to channel descriptor */ +{ + int stat; /* status from calls */ + int retStat=OK; /* return status to caller */ + AR_CHAN_DESC *pArChanDesc; /* pointer to channel descriptor */ + AR_CHAN_HDR *pChanHdr; /* pointer to channel header */ + + assert(*ppArChanDesc != NULL); + pArChanDesc = *ppArChanDesc; + assert(pArChanDesc != NULL); + assert(pArChanDesc->pArCfDesc != NULL); + assert(pArChanDesc->pMIBuf != NULL); + pChanHdr = &ArCDChanHdr(pArChanDesc); + assert(pChanHdr->count > 0); + + if ((pArChanDesc->flags & AR_CDESC_WRITE) == 0) { + (void)fprintf(stderr, "arCFChanDel: %s not open O_RDWR\n", + ArCDChanHdr(pArChanDesc).name); + retStat = ERROR; + } + if (pChanHdr->count > 1) { + (void)fprintf(stderr, "arCFChanDel: others have %s open\n", + ArCDChanHdr(pArChanDesc).name); + retStat = ERROR; + } + +/*---------------------------------------------------------------------------- +* free all the data and index buffers for this channel +*---------------------------------------------------------------------------*/ + if (retStat == OK) { + if ((stat = arCFChanPurge(pArChanDesc, (TS_STAMP *)NULL)) != OK) { + (void)fprintf(stderr, +"arCFChanDel: error deleting data and/or index buffers for %s\n", + ArCDChanHdr(pArChanDesc).name); + retStat = ERROR; + } + } + +/*---------------------------------------------------------------------------- +* remove this channel descriptor from the list of channel descriptors. +*---------------------------------------------------------------------------*/ + if (retStat == OK) { + if (pArChanDesc->pNextDesc != NULL) + pArChanDesc->pNextDesc->pPrevDesc = pArChanDesc->pPrevDesc; + else + pArChanDesc->pArCfDesc->pChanDescTail = pArChanDesc->pPrevDesc; + if (pArChanDesc->pPrevDesc != NULL) + pArChanDesc->pPrevDesc->pNextDesc = pArChanDesc->pNextDesc; + else + pArChanDesc->pArCfDesc->pChanDescHead = pArChanDesc->pNextDesc; + +/*---------------------------------------------------------------------------- +* move the chanHdr to the free chanHdr list and mark the MI block +* as MODIFIED +*---------------------------------------------------------------------------*/ + stat = arCF_MIDelChan(pArChanDesc->pArCfDesc, + pArChanDesc->pMIBuf, pArChanDesc->hdrNum); + ArCFModifySet(pArChanDesc->pArCfDesc, pArChanDesc->pMIBuf); + +/*---------------------------------------------------------------------------- +* free the descriptor and put NULL in the caller's pointer, to prevent +* further use of the descriptor +*---------------------------------------------------------------------------*/ + arCF_freeCHAN_DESC(pArChanDesc); + *ppArChanDesc = NULL; + } + + return retStat; +} + +/*+/subr********************************************************************** +* NAME arCFChanFind - find a channel in an AR channel data file +* +* DESCRIPTION +* Find a channel in a channel data file. This routine is intended +* to be used in cases where it is necessary to know prior to doing +* an arCFChanOpen() whether the file contains a particular channel. +* +* RETURNS +* OK if channel is found, or +* ERROR +* +* BUGS +* o text +* +* EXAMPLE +* +*-*/ +long +arCFChanFind(pArCfDesc, chanName) +AR_CF_DESC *pArCfDesc; /* I pointer to channel file descriptor */ +char *chanName; /* I channel name */ +{ + AR_MI_BUF *pMIBuf; /* ptr to MI block containing chanHdr */ + int indx; /* index in MI block of chanHdr */ + int l; /* length of chanName */ + char name[AR_NAME_DIM]; /* buffer for appending .VAL */ + + assert(pArCfDesc != NULL); + l = strlen(chanName); + + if (index(chanName, '.') == NULL) { + if (l <= 0 || l >= AR_NAME_DIM-5) { + (void)fprintf(stderr, "arCFChanFind: chanName illegal length\n"); + return ERROR; + } + (void)strcpy(name, chanName); + (void)strcat(name, ".VAL"); + chanName = name; + } + else { + if (l <= 0 || l >= AR_NAME_DIM) { + (void)fprintf(stderr, "arCFChanFind: chanName illegal length\n"); + return ERROR; + } + } + return arCF_MIFindChan(pArCfDesc, chanName, &pMIBuf, &indx); +} + +/*+/subr********************************************************************** +* NAME arCFChanOpen - open a channel to an AR channel data file +* +* DESCRIPTION +* Open a channel in a channel data file. If the open is successful, +* a channel descriptor is returned. If the channel doesn't exist, +* one is created if mode is O_RDWR; in this case, the .flags item in +* the channel descriptor will have AR_CDESC_NEW set. +* +* When the open is complete, the relevent index and data blocks will +* be in memory. If the mode is O_RDWR, the position will be at the +* end of the channel's data, ready for writing; for O_RDONLY, at the +* start of the channel's data, ready for reading. +* +* If the channel descriptor isn't closed before the channel data file +* is closed, it will be closed automatically, with updated blocks +* written to the channel data file. +* +* If O_RDWR is specified, the channel data file must have been opened +* with O_RDWR and there must be no other channel descriptor open in +* O_RDWR mode to the desired channel. +* +* The count of channel descriptors open to the desired channel is +* incremented. +* +* RETURNS +* pointer to channel descriptor, or +* NULL +* +* BUGS +* o no exit handler (this will probably be covered by the exit handler +* for the file, which isn't presently done) +* o for reading, the possibility of opening to a position (date/time) +* would potentially avoid wasting a disk read +* +* SEE ALSO +* arCFChanClose(), arCFOpen(), arCFClose() +* +* EXAMPLE +* +*-*/ +AR_CHAN_DESC * +arCFChanOpen(pArCfDesc, chanName, mode) +AR_CF_DESC *pArCfDesc; /* IO pointer to channel file descriptor */ +char *chanName; /* I channel name */ +int mode; /* I mode for open--O_RDONLY or O_RDWR; if mode is + O_RDWR and the channel doesn't exist, it + is created */ +{ + int stat; /* status return from calls */ + int retStat=OK; /* cumulative status in this routine */ + AR_MI_BUF *pMIBuf; /* ptr to MI block containing chanHdr */ + int indx; /* index in MI block of chanHdr */ + AR_CHAN_HDR *pChanHdr; /* pointer to chanHdr for this channel */ + AR_CHAN_DESC *pChanDesc=NULL;/* pointer to channel descriptor */ + int l; /* length of chanName */ + char name[AR_NAME_DIM]; /* buffer for appending .VAL */ + + assert(pArCfDesc != NULL); + assert(mode == O_RDONLY || mode == O_RDWR); + +/*---------------------------------------------------------------------------- +* check chanName to see if it specifies a .field ; if it doesn't, copy +* chanName to 'name' and append .VAL , then change chanName to point to +* 'name' +*---------------------------------------------------------------------------*/ + l = strlen(chanName); + if (index(chanName, '.') == NULL) { + if (l <= 0 || l >= AR_NAME_DIM-5) { + (void)fprintf(stderr, "arCFChanOpen: chanName illegal length\n"); + retStat = ERROR; + } + else { + (void)strcpy(name, chanName); + (void)strcat(name, ".VAL"); + chanName = name; + } + } + else { + if (l <= 0 || l >= AR_NAME_DIM) { + (void)fprintf(stderr, "arCFChanOpen: chanName illegal length\n"); + retStat = ERROR; + } + } + +/*---------------------------------------------------------------------------- +* make sure file is O_RDWR if ChanOpen request is O_RDWR +*---------------------------------------------------------------------------*/ + if ( (mode == O_RDWR) && + ((pArCfDesc->pBfDesc->flags & BF_DESC_WRITE) == 0) ) { + (void)fprintf(stderr, + "arCFChanOpen: can't open %s O_RDWR--file not O_RDWR\n", + chanName); + retStat = ERROR; + } + +/*---------------------------------------------------------------------------- +* allocate a channel descriptor and clear the 'flags' item. If the open +* is with O_RDWR, set the AR_CDESC_WRITE bit in the 'flags' item. +*---------------------------------------------------------------------------*/ + if (retStat == OK) { + if ((pChanDesc = arCF_mallocCHAN_DESC()) == NULL) { + (void)fprintf(stderr, + "arCFChanOpen: can't malloc for chan desc for %s\n", + chanName); + retStat = ERROR; + } + else { + pChanDesc->flags = 0; + if (mode == O_RDWR) + pChanDesc->flags |= AR_CDESC_WRITE; + } + } + +/*---------------------------------------------------------------------------- +* try to locate the chanHdr for the channel. If no chanHdr is found, +* create one, providing that mode is O_RDWR; if mode is O_RDONLY, then +* not finding a chanHdr is an error. If a new chanHdr is created, then +* set AR_CDESC_NEW in the channel descriptor. +*---------------------------------------------------------------------------*/ + if (retStat == OK) { + stat = arCF_MIFindChan(pArCfDesc, chanName, &pMIBuf, &indx); + if (stat != OK) { + if ((pChanDesc->flags & AR_CDESC_WRITE) != 0) { + stat = arCF_MIAddChan(pArCfDesc, chanName, &pMIBuf, &indx); + if (stat == OK) + pChanDesc->flags |= AR_CDESC_NEW; + else { + (void)fprintf(stderr, + "arCFChanOpen: error adding chanHdr for %s\n", + chanName); + retStat = ERROR; + } + } + else { + (void)fprintf(stderr, "arCFChanOpen: %s not found\n", chanName); + retStat = ERROR; + } + } + } + if (retStat == OK) { +/*---------------------------------------------------------------------------- +* compute the actual address of the chanHdr, for easier manipulation. If +* the chanHdr is already open in O_RDWR mode, and if this open is for +* O_RDWR, an error exists. If this is the only request for O_RDWR, flag +* AR_CHDR_WRITE in the chanHdr to disallow other writers. If all is well, +* increment in the chanHdr the count of descriptors open to it. +*---------------------------------------------------------------------------*/ + assert(pMIBuf != NULL); + pChanHdr = &pMIBuf->chanHdr[indx]; + if ((pChanDesc->flags & AR_CDESC_WRITE) != 0) { + if ((pChanHdr->flags & AR_CHDR_WRITE) != 0) { + (void)fprintf(stderr, + "arCFChanOpen: %s already open for writing\n", + chanName); + retStat = ERROR; + } + else { + pChanHdr->flags |= AR_CHDR_WRITE; + pChanHdr->count += 1; + } + } + else + pChanHdr->count += 1; + + } + if (retStat == OK) { +/*---------------------------------------------------------------------------- +* the channel file descriptor has a list of open channel descriptors; +* link this channel descriptor onto the end of that list (which is +* a doubly linked list). +*---------------------------------------------------------------------------*/ + pChanDesc->pNextDesc = NULL; + pChanDesc->pPrevDesc = pArCfDesc->pChanDescTail; + if (pArCfDesc->pChanDescHead != NULL) { + assert(pArCfDesc->pChanDescTail != NULL); + pArCfDesc->pChanDescTail->pNextDesc = pChanDesc; + } + else { + assert(pArCfDesc->pChanDescTail == NULL); + pArCfDesc->pChanDescHead = pChanDesc; + } + pArCfDesc->pChanDescTail = pChanDesc; + +/*---------------------------------------------------------------------------- +* set up the channel descriptor. Part of this setup includes getting +* an index buffer and a data buffer. If the open is O_RDONLY, then +* the position will be at the first index block and first data block; +* for O_RDWR, the last index block and last data block will be used. +*---------------------------------------------------------------------------*/ + pChanDesc->pArCfDesc = pArCfDesc; + pChanDesc->pMIBuf = pMIBuf; + pChanDesc->hdrNum = indx; + pChanDesc->pIndexBuf = NULL; + pChanDesc->pDataBuf = NULL; + pChanDesc->pData = NULL; + pChanDesc->remainCount = -10; + pChanDesc->datInfNum = -10; + pChanDesc->timeStamp.secPastEpoch = 0; + pChanDesc->timeStamp.nsec = 0; + stat = arCF_DNextBlock(pChanDesc); + if (stat == ERROR && (pChanDesc->flags & AR_CDESC_EOF) == 0) + assertAlways(0); + } + else { + if (pChanDesc != NULL) { + arCF_freeCHAN_DESC(pChanDesc); + pChanDesc = NULL; + } + } + + return pChanDesc; +} + +/*+/subr********************************************************************** +* NAME arCFChanPosition - position channel decscriptor based on time stamp +* +* DESCRIPTION +* Position a channel descriptor so it is ready to read the data with +* the specified time stamp. If no data with the specified time stamp +* is available, the positioning is for the first data following. +* +* End of file results in an error return, with the AR_CDESC_EOF flag +* set in chanDesc.flags . +* +* RETURNS +* OK, or +* ERROR +* +* SEE ALSO +* arCFChanRead() +* +* EXAMPLE +* +*-*/ +long +arCFChanPosition(pArChanDesc, pPosStamp) +AR_CHAN_DESC *pArChanDesc;/* IO pointer to channel descriptor */ +TS_STAMP *pPosStamp; /* I stamp for desired data; NULL for `rewind' */ +{ + int stat; /* status from calls */ + int retStat=OK; /* return status to caller */ + AR_CHAN_HDR *pChanHdr; /* pointer to chanHdr */ + AR_INDEX_BUF *pIndexBuf; /* pointer to buffer for index buffer */ + AR_DATA_BUF *pDataBuf; /* pointer to buffer for data buffer */ + BF_BLKNUM indexBlock; /* block # for index block */ + BF_BLKNUM nextIndexBlock; /* block # for next index block */ + BF_BLKNUM dataBlock; /* block # for data block */ + int firstDatInf; /* first datInfo in index block */ + int curDatInf; /* current datInfo in index block */ + int lastDatInf; /* last datInfo in index block */ + int i; /* temp for loops */ + + assert(pArChanDesc != NULL); + assert(pArChanDesc->pArCfDesc != NULL); + pChanHdr = &ArCDChanHdr(pArChanDesc); + assert(pChanHdr != NULL); + + if (pChanHdr->newestSecPastEpoch == 0) { + pArChanDesc->flags |= AR_CDESC_EOF; + return ERROR; + } + else if (pPosStamp == NULL || + pPosStamp->secPastEpoch < pChanHdr->oldestSecPastEpoch) { +/*---------------------------------------------------------------------------- +* rewind +* either a rewind was requested, or else the requested stamp is prior +* to the oldest data for the file +*----------------------------------------------------------------------------*/ + pArChanDesc->datInfNum = -10; + pArChanDesc->remainCount = -10; + stat = arCF_DNextBlock(pArChanDesc); + if (stat == ERROR && (pArChanDesc->flags & AR_CDESC_EOF) == 0) + assertAlways(0); + return stat; + } + +/*---------------------------------------------------------------------------- +* search is required +* It isn't known yet whether the search will be forward or reverse. +* If the desired stamp is less than that of the first datInf, +* then a backward search is required. If the desired stamp +* is greater than that of the first datInf, then a forward +* search (possibly confined to this index block) is required. +* +* A special case occurs when the present index block contains no +* datInf's with time stamps. In this case, a backward search is +* done. (This case arises because data blocks which contain only +* data items don't have a time stamp in their datInf entry.) +*----------------------------------------------------------------------------*/ + pIndexBuf = pArChanDesc->pIndexBuf; + lastDatInf = (pIndexBuf->bfInfo.lastByte - BF_BLOCK_DATA) / + sizeof(AR_DAT_INFO); + while (lastDatInf > 0 && + pIndexBuf->datInfo[lastDatInf].stamp.secPastEpoch == 0) { + lastDatInf--; + } + firstDatInf = (pIndexBuf->bfInfo.firstByte - BF_BLOCK_DATA) / + sizeof(AR_DAT_INFO); + while (firstDatInf < lastDatInf && + pIndexBuf->datInfo[firstDatInf].stamp.secPastEpoch == 0) { + firstDatInf++; + } + if (firstDatInf > lastDatInf || + TsCmpStampsLT(pPosStamp, &pIndexBuf->datInfo[firstDatInf].stamp)) { +/*---------------------------------------------------------------------------- +* reverse search +* Since the file contains extremely little information to allow +* `going backward', a reverse search is accomplished with a rewind +* followed by a forward search. +*----------------------------------------------------------------------------*/ + pArChanDesc->datInfNum = -10; + pArChanDesc->remainCount = -10; + stat = arCF_INextDatInfo(pArChanDesc); + if (stat == ERROR && (pArChanDesc->flags & AR_CDESC_EOF) == 0) + assertAlways(0); + if (stat == ERROR) + return stat; + } + pIndexBuf = pArChanDesc->pIndexBuf; + lastDatInf = (pIndexBuf->bfInfo.lastByte - BF_BLOCK_DATA) / + sizeof(AR_DAT_INFO); + while (lastDatInf > 0 && + pIndexBuf->datInfo[lastDatInf].stamp.secPastEpoch == 0) { + lastDatInf--; + } +/*---------------------------------------------------------------------------- +* forward search +*----------------------------------------------------------------------------*/ + while (lastDatInf >= 0 && + TsCmpStampsGT(pPosStamp, &pIndexBuf->datInfo[lastDatInf].stamp)) { +/*---------------------------------------------------------------------------- +* desired position may be in this index block, or it may be in a +* following one. In order to find out, we'll have to read the next +* one and do an explicit check. +*----------------------------------------------------------------------------*/ + indexBlock = pIndexBuf->blkNum; + if ((nextIndexBlock = pIndexBuf->bfInfo.flink) != 0) { + stat = arCF_IRead(pArChanDesc, pIndexBuf, nextIndexBlock); + if (stat == OK) { + lastDatInf = (pIndexBuf->bfInfo.lastByte - BF_BLOCK_DATA) / + sizeof(AR_DAT_INFO); + while (lastDatInf > 0 && + pIndexBuf->datInfo[lastDatInf].stamp.secPastEpoch == 0) { + lastDatInf--; + } + firstDatInf = (pIndexBuf->bfInfo.firstByte - BF_BLOCK_DATA) / + sizeof(AR_DAT_INFO); + while (firstDatInf < lastDatInf && + pIndexBuf->datInfo[firstDatInf].stamp.secPastEpoch == 0) { + firstDatInf++; + } + if (firstDatInf > lastDatInf || + TsCmpStampsLE(pPosStamp, + &pIndexBuf->datInfo[firstDatInf].stamp)) { + /* woops!!! time to back up! */ + stat = arCF_IRead(pArChanDesc, pIndexBuf, indexBlock); + if (stat != OK) + assertAlways(0); + else + break; + } + } + else { + /* error reading next block; back up and let normal code + handle the error */ + stat = arCF_IRead(pArChanDesc, pIndexBuf, indexBlock); + if (stat != OK) + assertAlways(0); + else + break; + } + } + else + break; + } +/*---------------------------------------------------------------------------- +* desired position may be somewhere in this index block; this is the +* index block where we'll start narrowing down the search. +* +* First, the datInfo whose stamp is greater than or equal to the +* desired stamp will be found. Then reading will start with the +* preceding datInfo until a stamp equal to or greater than the +* desired stamp is found. +*----------------------------------------------------------------------------*/ + indexBlock = pIndexBuf->blkNum; + lastDatInf = (pIndexBuf->bfInfo.lastByte - BF_BLOCK_DATA) / + sizeof(AR_DAT_INFO); + while (lastDatInf > 0 && + pIndexBuf->datInfo[lastDatInf].stamp.secPastEpoch == 0) { + lastDatInf--; + } + assert(lastDatInf >= 0); + firstDatInf = (pIndexBuf->bfInfo.firstByte - BF_BLOCK_DATA) / + sizeof(AR_DAT_INFO); + while (firstDatInf < lastDatInf && + pIndexBuf->datInfo[firstDatInf].stamp.secPastEpoch == 0) { + firstDatInf++; + } + assert(firstDatInf >= 0); + i = curDatInf = firstDatInf; + while (i < lastDatInf) { + if (pIndexBuf->datInfo[i+1].stamp.secPastEpoch == 0) + i++; + else if (TsCmpStampsGE(&pIndexBuf->datInfo[i+1].stamp, pPosStamp)) + break; + else { + i++; + curDatInf = i; + } + } + pArChanDesc->datInfNum = curDatInf; + pArChanDesc->timeStamp= pIndexBuf->datInfo[curDatInf].stamp; + pArChanDesc->remainCount = -10; + stat = arCF_DRead(pArChanDesc, pArChanDesc->pDataBuf, + pIndexBuf->datInfo[curDatInf].dataBlock); + if (stat != OK) + assertAlways(0); + while (1) { + stat = arCFChanReadTs(pArChanDesc); + if (stat != OK) { + if ((pArChanDesc->flags & (AR_CDESC_BEGIN|AR_CDESC_INACT)) == 0) { + retStat = ERROR; + break; + } + } + else if (TsCmpStampsGE(&pArChanDesc->timeStamp, pPosStamp)) + break; + else { + stat = arCFChanRead(pArChanDesc, 0, (void *)NULL, 0); + if (stat != OK) { + retStat = ERROR; + break; + } + } + } + + return retStat; +} + +/*+/subr********************************************************************** +* NAME arCFChanPurge - purge data for a channel of an AR channel data file +* +* DESCRIPTION +* +* RETURNS +* +* BUGS +* o doesn't update oldestSecPastEpoch and newestSecPastEpoch +* o doesn't initialize the read/write items in the chanDesc +* o always does TOTAL purge, ignoring requested time stamp +* o there are some critical sections unprotected +* - between malloc() and storing pointer in descriptor +* - between releasing blocks and updating pointers and links +* o error handling is shakey, at best +* +* SEE ALSO +* arCFChanDel() +* +* EXAMPLE +* +*-*/ +long +arCFChanPurge(pArChanDesc, pKeepStamp) +AR_CHAN_DESC *pArChanDesc;/* IO pointer to channel descriptor */ +TS_STAMP *pKeepStamp; /* I stamp for oldest data to keep; NULL says + delete all data */ +{ + int stat; /* status from calls */ + int retStat=OK; /* return status to caller */ + AR_CHAN_HDR *pChanHdr; /* pointer to chanHdr */ + BF_DESC *pBfDesc; /* pointer to block file descriptor */ + AR_INDEX_BUF *pIndexBuf; /* pointer to buffer for index buffer */ + AR_DATA_BUF *pDataBuf; /* pointer to buffer for data buffer */ + + assert(pArChanDesc != NULL); + assert(pArChanDesc->pArCfDesc != NULL); + pBfDesc = pArChanDesc->pArCfDesc->pBfDesc; + assert(pBfDesc != NULL); + assert(pArChanDesc->pMIBuf != NULL); + pChanHdr = &ArCDChanHdr(pArChanDesc); + assert(pChanHdr->count > 0); + + if ((pArChanDesc->flags & AR_CDESC_WRITE) == 0) { + (void)fprintf(stderr, "arCFChanPurge: %s not open O_RDWR\n", + ArCDChanHdr(pArChanDesc).name); + retStat = ERROR; + } + if (pChanHdr->count > 1) { + (void)fprintf(stderr, "arCFChanPurge: others have %s open\n", + ArCDChanHdr(pArChanDesc).name); + retStat = ERROR; + } + +/*---------------------------------------------------------------------------- +* o if a read block is in memory, free it +* o if a write block is in memory, ???? +* o get buffers for index and data blocks, and put their pointers in +* the channel descriptor +* o traverse the index buffer chain, and all the data buffers, freeing +* the appropriate buffers +*---------------------------------------------------------------------------*/ + if (retStat == OK) { + if ((pIndexBuf = arCF_mallocINDEX_BUF()) == NULL) { + (void)fprintf(stderr, "arCFChanPurge: can't malloc to purge %s\n", + ArCDChanHdr(pArChanDesc).name); + retStat = ERROR; + } + else + pArChanDesc->pIndexBuf = pIndexBuf; + + if (retStat == OK) { + if ((pDataBuf = arCF_mallocDATA_BUF()) == NULL) { + (void)fprintf(stderr, + "arCFChanPurge: can't malloc to purge %s\n", + ArCDChanHdr(pArChanDesc).name); + retStat = ERROR; + } + else + pArChanDesc->pDataBuf = pDataBuf; + } + } + if (retStat == OK) { + BF_BLKNUM indexBlock; /* block # for index block */ + BF_BLKNUM nextIndexBlock; /* block # for next index block */ + BF_BLKNUM dataBlock; /* block # for data block */ + int curDatInf; /* current datInfo in index block */ + int lastDatInf; /* last datInfo in index block */ + + indexBlock = pChanHdr->indexHead; + while (retStat == OK && indexBlock != 0) { + stat = bfRead(pBfDesc, &pIndexBuf->bfInfo, indexBlock); + if (stat == OK) { + nextIndexBlock = pIndexBuf->bfInfo.flink; + pIndexBuf->blkNum = indexBlock; + ArCFModifyInit(pIndexBuf); + pIndexBuf->pBlock = &pIndexBuf->bfInfo; + } + else { + (void)fprintf(stderr, + "arCFChanPurge: bfRead error, indxBlk for %s\n", + ArCDChanHdr(pArChanDesc).name); + retStat = ERROR; + } + if (stat == OK && pIndexBuf->bfInfo.lastByte > BF_BLOCK_DATA) { +/*---------------------------------------------------------------------------- +* march through all the datInfo entries in this index block. Use +* firstByte and lastByte to figure out how many datInfo's there are. +* +* for each datInfo entry, +* o read the block +* o release the block to the free block list +* o adjust firstByte in the index block to be past the datInfo just +* processed (this keeps firstByte valid) +* +* BUGS +* o this section of code should actually compare time stamps in datInfo +* with the caller supplied time stamp. If newestSecPastEpoch is newer +* than the caller's time stamp, delete the whole data block; otherwise, +* either the purge is complete, or only part of the data is to be +* purged (which means keeping the datInfo entry, adjusting its +* time stamp value, and adjusting firstByte in the data block). In +* addition, it must be taken into account that the stamps in some +* datInfo are zero--for the data blocks which don't have a time item. +*---------------------------------------------------------------------------*/ + curDatInf = (pIndexBuf->bfInfo.firstByte - BF_BLOCK_DATA) / + sizeof(AR_DAT_INFO); + lastDatInf = (pIndexBuf->bfInfo.lastByte - BF_BLOCK_DATA) / + sizeof(AR_DAT_INFO); + + while (curDatInf < lastDatInf && retStat == OK) { + dataBlock = pIndexBuf->datInfo[curDatInf].dataBlock; + stat = bfRead(pBfDesc, &pDataBuf->bfInfo, dataBlock); + if (stat == OK) { + pDataBuf->blkNum = dataBlock; + ArCFModifyInit(pDataBuf); + pDataBuf->pBlock = &pDataBuf->bfInfo; + } + else { + (void)fprintf(stderr, + "arCFChanPurge: bfRead error, dataBlk for %s\n", + ArCDChanHdr(pArChanDesc).name); + retStat = ERROR; + } + if (retStat == OK) + stat = bfRelease(pBfDesc, &pDataBuf->bfInfo, dataBlock); + if (stat == OK) { + /* adjust firstByte in index block to indicate that + * this blockInfo entry is no longer valid, just in + * case */ + pIndexBuf->bfInfo.firstByte += sizeof(AR_DAT_INFO); + } + else { + (void)fprintf(stderr, + "arCFChanPurge: bfRelease error, dataBlk for %s\n", + ArCDChanHdr(pArChanDesc).name); + retStat = ERROR; + } + curDatInf++; + } + } + if (retStat == OK) { +/*---------------------------------------------------------------------------- +* now release the index block back to the free block list. +* +* set to process the next index block, using the forward link which +* was saved earlier. Also, set the pointer the the index list in the +* chanHdr to point to the next index block. +* +* BUGS +* o if purging by time stamp is being done, then not all datInfo +* structures may have been deleted from the index block. In that case, +* it would be necessary to write the updated index block back to the +* file and leave it as the head of the index block list. +*---------------------------------------------------------------------------*/ + stat = bfRelease(pBfDesc, &pIndexBuf->bfInfo, indexBlock); + if (stat != OK) { + (void)fprintf(stderr, + "arCFChanPurge: bfRelease error, indexBlk for %s\n", + ArCDChanHdr(pArChanDesc).name); + retStat = ERROR; + } + indexBlock = nextIndexBlock; + pChanHdr->indexHead = indexBlock; + } + } + } +/*---------------------------------------------------------------------------- +* final cleanup +* +* BUGS +* o cleanup assumes total purge +*----------------------------------------------------------------------------*/ + arCF_freeDATA_BUF(pDataBuf); + pArChanDesc->pDataBuf = NULL; + arCF_freeINDEX_BUF(pIndexBuf); + pArChanDesc->pIndexBuf = NULL; + pArChanDesc->pData = NULL; + pArChanDesc->remainCount = -10; + pArChanDesc->datInfNum = -10; + if (pChanHdr->indexHead == 0) + pChanHdr->indexTail = 0; + + ArCFModifySet(pArChanDesc->pArCfDesc, pArChanDesc->pMIBuf); + + return retStat; +} + +/*---------------------------------------------------------------------------- +* macros for use by arCFChanRead() and friends +* +* int ArCRFetch fetch as an int the next character from the current +* read buffer; if there isn't a next character +* (or if there isn't a current read buffer), +* get the next read buffer and fetch the +* first available character from it. On error +* or at end of file, returns ERROR. +*---------------------------------------------------------------------------*/ +#define ArCRFetch \ + ( (--pArChanDesc->remainCount >= 0) \ + ? ((int)*(++pArChanDesc->pData))&0xff \ + : arCF_DFillRbufAndGetc(pArChanDesc) ) + +/*+/subr********************************************************************** +* NAME arCFChanRead - read next value from a channel +* +* DESCRIPTION +* This routine reads the next value for a channel and fills in the +* caller's db_access_val union with the value, time stamp, etc. +* as appropriate. +* +* An ERROR return indicates that a valid value wasn't put into the +* caller's buffer. The .flags item in the channel descriptor must be +* examined to determine the reason: +* +* AR_CDESC_EOF indicates that the stream for the channel is at +* end of file +* AR_CDESC_BEGIN indicates that a new run of data is beginning +* for the channel +* AR_CDESC_INACT indicates that the run of data is done, because +* the archive request was inactivated (or disabled) +* +* RETURNS +* OK, or +* ERROR +* +* BUGS +* o assumes that representation of numbers is the same on `retrieval +* host' as on original `store host' +* o the type of the read request must be the same `flavor' as that of +* the channel--no conversions are provided from short to string, etc. +* o need a way to return to caller number of elements actually read +* +* NOTES +* 1. `count' need not match native count. A 0 value results in skipping a +* record, without storing to caller's buffer. If the value is less +* than the native count, excess values will be discarded. +* +*-*/ +long +arCFChanRead(pArChanDesc, type, pAccessBuf, count) +register +AR_CHAN_DESC *pArChanDesc; /* IO pointer to channel descriptor */ +chtype type; /* I buffer type; DBR_xxx. Ignored if + count == 0 */ +register +union db_access_val *pAccessBuf;/* IO pointer to caller's buffer. Ignored if + count == 0 */ +short count; /* I array element count for caller's buffer */ +{ + int retStat=OK; /* return status to caller */ + int nBytes; /* number of value bytes in file item value */ + int countBytes; /* number of bytes allowed by caller's buf */ + int i; /* temp for loops */ + int byte0, byteN; /* bytes from the entry */ + char *pBuf; /* temporary pointer for moving data */ + + assert(pArChanDesc != NULL); + assert(pArChanDesc->pMIBuf != NULL); + assert(dbr_type_is_valid(type) || count == 0); + assert(pAccessBuf != NULL || count == 0); + assert(count >= 0); + +/*---------------------------------------------------------------------------- +* handle time stamp +* +* (arCFChanReadTs() resets the various flags) +* +* put the proper time stamp information into the access buffer if +* the type is DBR_TIME_xxx. +*----------------------------------------------------------------------------*/ + if ((retStat = arCFChanReadTs(pArChanDesc)) == OK) { + if (dbr_type_is_TIME(type) && count > 0) + pAccessBuf->tstrval.stamp = pArChanDesc->timeStamp; + } + +/*---------------------------------------------------------------------------- +* handle alarm status/severity if this item has it. If the item does +* have it, process it and then exit this section with a make believe +* item code of value without status. +* +* This section assumes that all DBR_ types which have status have +* the status and severity structure items in the same place as +* struct dbr_sts_string . +*---------------------------------------------------------------------------*/ + if (retStat == OK) { + byte0 = ArCRFetch; + if (byte0 == AR_DAT_IC_VAL_STAT) { + byte0 = ArCRFetch; + byteN = ArCRFetch; + if (!dbr_type_is_plain(type) && count > 0) { + pAccessBuf->sstrval.status = byte0; + pAccessBuf->sstrval.severity = byteN; + } + if (byteN == ERROR) + retStat = ERROR; + else + byte0 = AR_DAT_IC_VAL; + } + } + +/*---------------------------------------------------------------------------- +* if this is DBR_GR_xxx, get graphics info from chanHdr +*---------------------------------------------------------------------------*/ + if (retStat == OK && dbr_type_is_GR(type) && count > 0) { + switch (type) { + case DBR_GR_STRING: + break; /* no graphics for string */ + case DBR_GR_SHORT: +#define ArCRShrtGr ArCDChanHdr(pArChanDesc).graphics.shortGr + (void)strcpy(pAccessBuf->gshrtval.units, ArCRShrtGr.units); + pAccessBuf->gshrtval.upper_disp_limit = ArCRShrtGr.upDispLim; + pAccessBuf->gshrtval.lower_disp_limit = ArCRShrtGr.lowDispLim; + pAccessBuf->gshrtval.upper_alarm_limit = ArCRShrtGr.upAlmLim; + pAccessBuf->gshrtval.upper_warning_limit = ArCRShrtGr.upWarnLim; + pAccessBuf->gshrtval.lower_warning_limit = + ArCRShrtGr.lowWarnLim; + pAccessBuf->gshrtval.lower_alarm_limit = ArCRShrtGr.lowAlmLim; + break; + case DBR_GR_FLOAT: +#define ArCRFltGr ArCDChanHdr(pArChanDesc).graphics.floatGr + pAccessBuf->gfltval.precision = ArCRFltGr.precision; + (void)strcpy(pAccessBuf->gfltval.units, ArCRFltGr.units); + pAccessBuf->gfltval.upper_disp_limit = ArCRFltGr.upDispLim; + pAccessBuf->gfltval.lower_disp_limit = ArCRFltGr.lowDispLim; + pAccessBuf->gfltval.upper_alarm_limit = ArCRFltGr.upAlmLim; + pAccessBuf->gfltval.upper_warning_limit = ArCRFltGr.upWarnLim; + pAccessBuf->gfltval.lower_warning_limit = ArCRFltGr.lowWarnLim; + pAccessBuf->gfltval.lower_alarm_limit = ArCRFltGr.lowAlmLim; + break; + case DBR_GR_ENUM: +#define ArCREnmGr ArCDChanHdr(pArChanDesc).graphics.enumGr.pGRBuf + pAccessBuf->genmval.no_str = ArCREnmGr->numStrings; + for (i=0; inumStrings; i++) { + (void)strcpy(pAccessBuf->genmval.strs[i], + ArCREnmGr->states[i]); + } + break; + default: + (void)fprintf(stderr, "arCFChanRead: unimplemented GR type\n"); + retStat = ERROR; + break; + } + } + +/*---------------------------------------------------------------------------- +* now handle the value item. Only the actual values are handled here, since +* status, time stamp, etc. have already been handled. +*---------------------------------------------------------------------------*/ + if (retStat != OK) + ; /* no action */ + else if (byte0 != AR_DAT_IC_VAL) { + (void)fprintf(stderr, "arCFChanRead: unexpected item code %d\n", byte0); + retStat = ERROR; + } + else { + if (ArCDChanHdr(pArChanDesc).chanType == DBF_STRING) { + if (type == DBR_STRING) + pBuf = ((char *)pAccessBuf->strval) - 1; + else if (type == DBR_STS_STRING) + pBuf = ((char *)pAccessBuf->sstrval.value) - 1; + else if (type == DBR_TIME_STRING) + pBuf = ((char *)pAccessBuf->tstrval.value) - 1; + else if (type == DBR_GR_STRING) + pBuf = ((char *)pAccessBuf->gstrval.value) - 1; + else if (count <= 0) + pBuf = NULL; + else + assertAlways(0); + + /* set byte count one byte shy to allow checking status on the + last one. This is done as a time saving measure */ + nBytes = ArCDChanHdr(pArChanDesc).elCount*db_strval_dim - 1; + countBytes = count * db_strval_dim; + for (i=0; i nBytes) + *(++pBuf) = byteN; + if (byteN == ERROR) + retStat = ERROR; + } + else if (ArCDChanHdr(pArChanDesc).chanType == DBF_SHORT) { + if (type == DBR_SHORT) + pBuf = ((char *)&pAccessBuf->shrtval) - 1; + else if (type == DBR_STS_SHORT) + pBuf = ((char *)&pAccessBuf->sshrtval.value) - 1; + else if (type == DBR_TIME_SHORT) + pBuf = ((char *)&pAccessBuf->tshrtval.value) - 1; + else if (type == DBR_GR_SHORT) + pBuf = ((char *)&pAccessBuf->gshrtval.value) - 1; + else if (count <= 0) + pBuf = NULL; + else + assertAlways(0); + + /* set byte count one byte shy to allow checking status on the + last one. This is done as a time saving measure */ + nBytes = ArCDChanHdr(pArChanDesc).elCount*sizeof(short) - 1; + countBytes = count * sizeof(short); + for (i=0; i nBytes) + *(++pBuf) = byteN; + if (byteN == ERROR) + retStat = ERROR; + } + else if (ArCDChanHdr(pArChanDesc).chanType == DBF_FLOAT) { + if (type == DBR_FLOAT) + pBuf = ((char *)&pAccessBuf->fltval) - 1; + else if (type == DBR_STS_FLOAT) + pBuf = ((char *)&pAccessBuf->sfltval.value) - 1; + else if (type == DBR_TIME_FLOAT) + pBuf = ((char *)&pAccessBuf->tfltval.value) - 1; + else if (type == DBR_GR_FLOAT) + pBuf = ((char *)&pAccessBuf->gfltval.value) - 1; + else if (count <= 0) + pBuf = NULL; + else + assertAlways(0); + + /* set byte count one byte shy to allow checking status on the + last one. This is done as a time saving measure */ + nBytes = ArCDChanHdr(pArChanDesc).elCount*sizeof(float) - 1; + countBytes = count * sizeof(float); + for (i=0; i nBytes) + *(++pBuf) = byteN; + if (byteN == ERROR) + retStat = ERROR; + } + else if (ArCDChanHdr(pArChanDesc).chanType == DBF_LONG) { + if (type == DBR_LONG) + pBuf = ((char *)&pAccessBuf->longval) - 1; + else if (type == DBR_STS_LONG) + pBuf = ((char *)&pAccessBuf->slngval.value) - 1; + else if (type == DBR_TIME_LONG) + pBuf = ((char *)&pAccessBuf->tlngval.value) - 1; + else if (type == DBR_GR_LONG) + pBuf = ((char *)&pAccessBuf->glngval.value) - 1; + else if (count <= 0) + pBuf = NULL; + else + assertAlways(0); + + /* set byte count one byte shy to allow checking status on the + last one. This is done as a time saving measure */ + nBytes = ArCDChanHdr(pArChanDesc).elCount*sizeof(long) - 1; + countBytes = count * sizeof(long); + for (i=0; i nBytes) + *(++pBuf) = byteN; + if (byteN == ERROR) + retStat = ERROR; + } + else if (ArCDChanHdr(pArChanDesc).chanType == DBF_DOUBLE) { + if (type == DBR_DOUBLE) + pBuf = ((char *)&pAccessBuf->doubleval) - 1; + else if (type == DBR_STS_DOUBLE) + pBuf = ((char *)&pAccessBuf->sdblval.value) - 1; + else if (type == DBR_TIME_DOUBLE) + pBuf = ((char *)&pAccessBuf->tdblval.value) - 1; + else if (type == DBR_GR_DOUBLE) + pBuf = ((char *)&pAccessBuf->gdblval.value) - 1; + else if (count <= 0) + pBuf = NULL; + else + assertAlways(0); + + /* set byte count one byte shy to allow checking status on the + last one. This is done as a time saving measure */ + nBytes = ArCDChanHdr(pArChanDesc).elCount*sizeof(double) - 1; + countBytes = count * sizeof(double); + for (i=0; i nBytes) + *(++pBuf) = byteN; + if (byteN == ERROR) + retStat = ERROR; + } + else if (ArCDChanHdr(pArChanDesc).chanType == DBF_ENUM) { + if (type == DBR_ENUM) + pBuf = ((char *)&pAccessBuf->enmval) - 1; + else if (type == DBR_STS_ENUM) + pBuf = ((char *)&pAccessBuf->senmval.value) - 1; + else if (type == DBR_TIME_ENUM) + pBuf = ((char *)&pAccessBuf->tenmval.value) - 1; + else if (type == DBR_GR_ENUM) + pBuf = ((char *)&pAccessBuf->genmval.value) - 1; + else if (count <= 0) + pBuf = NULL; + else + assertAlways(0); + + /* set byte count one byte shy to allow checking status on the + last one. This is done as a time saving measure */ + nBytes = ArCDChanHdr(pArChanDesc).elCount*sizeof(short) - 1; + countBytes = count * sizeof(short); + for (i=0; i nBytes) + *(++pBuf) = byteN; + if (byteN == ERROR) + retStat = ERROR; + } + else if (ArCDChanHdr(pArChanDesc).chanType == DBF_CHAR) { + if (type == DBR_CHAR) + pBuf = ((char *)&pAccessBuf->charval) - 1; + else if (type == DBR_STS_CHAR) + pBuf = ((char *)&pAccessBuf->schrval.value) - 1; + else if (type == DBR_TIME_CHAR) + pBuf = ((char *)&pAccessBuf->tchrval.value) - 1; + else if (type == DBR_GR_CHAR) + pBuf = ((char *)&pAccessBuf->gchrval.value) - 1; + else if (count <= 0) + pBuf = NULL; + else + assertAlways(0); + + /* set byte count one byte shy to allow checking status on the + last one. This is done as a time saving measure */ + nBytes = ArCDChanHdr(pArChanDesc).elCount*sizeof(char) - 1; + countBytes = count * sizeof(char); + for (i=0; i nBytes) + *(++pBuf) = byteN; + if (byteN == ERROR) + retStat = ERROR; + } + else { +/*---------------------------------------------------------------------------- +* illegal buffer type +*---------------------------------------------------------------------------*/ + (void)fprintf(stderr, "arCFChanRead: unimplemented buffer type\n"); + } + } + + return retStat; +} + +/*+/internal****************************************************************** +* NAME arCFChanReadTs - read and process time stamp items +* +* DESCRIPTION +* This routine reads successive items until a non-time stamp item +* is found. The chanDesc time stamp is updated appropriately. +* +* End of file results in an error return, with the AR_CDESC_EOF flag +* set in chanDesc.flags . +* +* Encountering one of the AR_DAT_IC_MON_xxx flags results in an error +* return, with an appropriate AR_CDESC_xxx flag set in chanDesc.flags . +* +* RETURNS +* OK, or +* ERROR +* +*-*/ +long +arCFChanReadTs(pArChanDesc) +register +AR_CHAN_DESC *pArChanDesc; /* IO pointer to channel descriptor */ +{ + int retStat=OK; /* return status to caller */ + TS_STAMP timeDel; /* delta time */ + int byte0, byteN; /* bytes from the entry */ + + assert(pArChanDesc != NULL); + assert(pArChanDesc->pMIBuf != NULL); + + /* turn off various flags */ + pArChanDesc->flags &= ~(AR_CDESC_EOF | AR_CDESC_BEGIN | AR_CDESC_INACT | + AR_CDESC_DISCON | AR_CDESC_SUP); + +/*---------------------------------------------------------------------------- +* handle time stamp +* +* if the first byte indicates a time stamp, process it, +* updating the time information in the channel descriptor. +* +* this section of code loops until a non-time item is found, since +* several time related items can follow each other. The chanDesc +* is left so that the next read will start with the non-time item. +* +* this section assumes that all DBR_ types which have time have +* the the time structure items in the same place as +* struct dbr_time_string . +*---------------------------------------------------------------------------*/ + while (retStat == OK) { + timeDel.secPastEpoch = 0; + timeDel.nsec = 0; + if ( (byte0 = ArCRFetch) != ERROR) { + if ((byte0 & 0x80) == 0) { /* delta time, milli-sec */ + if ( (byteN = ArCRFetch) != ERROR) { + if ((timeDel.nsec = ((byte0<<8) + byteN)) < 1000) + timeDel.nsec *= 1000000; + else { + timeDel.secPastEpoch = timeDel.nsec / 1000; + timeDel.nsec = 1000000 * (timeDel.nsec % 1000); + } + TsAddStamps(&pArChanDesc->timeStamp, + &pArChanDesc->timeStamp, &timeDel); + } + else + retStat = ERROR; + } + else if (byte0 == AR_DAT_IC_MON_BEGIN) { /* begin monitor */ + pArChanDesc->flags |= AR_CDESC_BEGIN; + retStat = ERROR; + } + else if (byte0 == AR_DAT_IC_MON_INACT) { /* req inactivated */ + pArChanDesc->flags |= AR_CDESC_INACT; + retStat = ERROR; + } + else if (byte0 == AR_DAT_IC_MON_DISCON) { /* chan disconnected */ + pArChanDesc->flags |= AR_CDESC_DISCON; + retStat = ERROR; + } + else if ((byte0 & 0xf0) != AR_DAT_IC_TIME_XXX) { + pArChanDesc->remainCount++; /* `un-read' the byte */ + pArChanDesc->pData--; + goto timeDone; /* not a time code; bail out */ + } + else if (byte0 == AR_DAT_IC_TIME_STAMP) { + timeDel.secPastEpoch = ArCRFetch <<8; + timeDel.secPastEpoch = (timeDel.secPastEpoch + ArCRFetch)<<8; + timeDel.secPastEpoch = (timeDel.secPastEpoch + ArCRFetch)<<8; + timeDel.secPastEpoch += (byteN = ArCRFetch); + if (byteN != ERROR) + pArChanDesc->timeStamp = timeDel; + else + retStat = ERROR; + } + else if (byte0 == AR_DAT_IC_TIME_USEC) { /* delta time, micro-sec */ + timeDel.nsec = ArCRFetch <<8; + timeDel.nsec = (timeDel.nsec + ArCRFetch)<<8; + if ((timeDel.nsec += (byteN = ArCRFetch)) < 1000000) + timeDel.nsec *= 1000; + else { + timeDel.secPastEpoch = timeDel.nsec / 1000000; + timeDel.nsec = 1000 * (timeDel.nsec % 1000000); + } + if (byteN != ERROR) { + TsAddStamps(&pArChanDesc->timeStamp, + &pArChanDesc->timeStamp, &timeDel); + } + else + retStat = ERROR; + } + else if (byte0 == AR_DAT_IC_TIME_NSEC) { /* delta time, nano-sec */ + timeDel.nsec = ArCRFetch <<8; + timeDel.nsec = (timeDel.nsec + ArCRFetch)<<8; + timeDel.nsec = (timeDel.nsec + ArCRFetch)<<8; + timeDel.nsec += (byteN = ArCRFetch); + if (byteN != ERROR) { + TsAddStamps(&pArChanDesc->timeStamp, + &pArChanDesc->timeStamp, &timeDel); + } + else + retStat = ERROR; + } + else + assertAlways(0); /* not one of the expected codes */ + + } + if (byte0 == ERROR) + retStat = ERROR; + } +timeDone: /* must FOLLOW the while loop */ + return retStat; +} + +/*---------------------------------------------------------------------------- +* macros for use by arCFChanWrite() and friends +* +* int ArCWStore(c) insert character into current write buffer; if +* there isn't room (or if there isn't a current +* buffer) flush current buffer and insert +* character into new buffer +* +* int ArCWFlushAndStore(c) flush current write buffer (if any) and +* insert character into new buffer +*---------------------------------------------------------------------------*/ +#define ArCWFlushAndStore(c) ArCFFlushAndStore((c), pArChanDesc) +#define ArCWStore(c) ArCFStore((c), pArChanDesc) + +/*+/subr********************************************************************** +* NAME arCFChanWrite - write a channel access buffer to a channel +* +* DESCRIPTION +* This routine accepts an event_handler_args structure for a channel and +* writes time stamp, alarm status/severity, and value(s) to the +* channel data file for the channel. The buffer must be one of the +* DBR_TIME_xxx types. +* +* The header information for the channel must exist prior to calling +* this routine. In addition, the buffer type must agree with the +* native type stored in the header (e.g., if buffer type is +* DBR_TIME_SHORT, then the header must indicate DBF_SHORT). If +* any of these requirements are violated, then no write is done and +* ERROR status is returned. +* +* RETURNS +* OK, or +* ERROR +* +* BUGS +* o error handling needs to be validated +* o no attention is paid to issues related to calling this routine from +* Channel Access event handler. In particular, there should be +* queues and tasks for: receiving full buffers from this routine; +* supplying empty buffers to this routine; and supplying free blocks +* to this routine. THIS ROUTINE SHOULD NEVER BLOCK!!! +* +* NOTES +* 1. This routine uses the smallest delta time item consistent with the +* significance present in the .nsec member of the time stamp. E.g., +* if .nsec is a multiple of 1000000, then a milli-sec delta time will +* be used. +* +*-*/ +long +arCFChanWrite(pArChanDesc, pArg) +register +AR_CHAN_DESC *pArChanDesc; /* IO pointer to channel descriptor */ +struct event_handler_args *pArg;/* I pointer to monitor structure */ +{ +#define CW_NSEC ((struct dbr_time_string *)pCaBuf)->stamp.nsec +#define CW_SPE ((struct dbr_time_string *)pCaBuf)->stamp.secPastEpoch + register + void *pCaBuf; /* pointer to buffer */ + + int stat; /* status from calls */ + int deltaCode; /* AR_DAT_IC_TIME_xxx code for delta time */ + int deltaCount; /* count of bytes to represent delta time */ + TS_STAMP diff; /* difference between data stamp and chanDesc */ + ULONG fraction; /* fractional part of second, units vary */ + int fracCode; /* AR_DAT_IC_TIME_xxx code for delta time */ + int fracCount; /* count of bytes to represent delta time */ + register + char *ptr; /* temp for pointer in loops */ + register + char *ptrEnd; /* temp for pointer bound in loops */ + +/*---------------------------------------------------------------------------- +* get some stuff out of the structure supplied as input pArg +*---------------------------------------------------------------------------*/ + pCaBuf = pArg->dbr; + + assert(pArChanDesc != NULL); + assert(pArChanDesc->pMIBuf != NULL); + assert(pCaBuf != NULL); + if (!dbr_type_is_TIME(pArg->type)) + goto error; + if (pArg->count != ArCDChanHdr(pArChanDesc).elCount) + goto error; + + pArChanDesc->flags &= ~AR_CDESC_SUP; /* turn off suppress */ + +/*---------------------------------------------------------------------------- +* time stamp handling +* +* (This is done 'in-line' as an optimization strategy. When done as a +* separate routine, this used as much CPU time as all the rest of +* arCFChanWrite() used for status and data for DBR_TIME_FLOAT.) +* +* NOTES +* 1. A lot of the logic in this routine is in support of the rule that +* when a "time item" is used, the item can't cross a block +* boundary. +*---------------------------------------------------------------------------*/ + + if (TsCmpStampsLT(&((struct dbr_time_string *)pCaBuf)->stamp, + &pArChanDesc->timeStamp)) + goto error; + if (ArCDDatInfo(pArChanDesc).stamp.secPastEpoch == 0) { +/*---------------------------------------------------------------------------- +* case 1 +* this is the first item which has its beginning in this block. A +* time stamp in the datInfo is needed; there must be enough room +* in the block to avoid starting a new block--i.e., to hold the item +* code for the data entry. +* +* Since this is the first item which has its beginning in this block, +* datInfo.stamp must be set. The .firstByte item may also need to be set. +* +* This may also be the first item for the channel. If this is the case, +* then .oldestSecPastEpoch in chanHdr needs to be set. +*---------------------------------------------------------------------------*/ + if (pArChanDesc->remainCount < 1) { + if (ArCWFlushAndStore(AR_DAT_IC_VAL_STAT ) != OK) + goto error; + } + else { + if (ArCWStore( AR_DAT_IC_VAL_STAT ) != OK) + goto error; + } + if (pArChanDesc->pDataBuf->bfInfo.firstByte == 0) { + pArChanDesc->pDataBuf->bfInfo.firstByte = pArChanDesc->pData - + (char *)(&pArChanDesc->pDataBuf->bfInfo); + } + ArCDDatInfo(pArChanDesc).stamp = + ((struct dbr_time_string *)pCaBuf)->stamp; + ArCFModifySet(pArChanDesc->pArCfDesc, pArChanDesc->pIndexBuf); + + if (ArCDChanHdr(pArChanDesc).oldestSecPastEpoch == 0) { + ArCDChanHdr(pArChanDesc).oldestSecPastEpoch = CW_SPE; + ArCFModifySet(pArChanDesc->pArCfDesc, pArChanDesc->pMIBuf); + } + } + else { +/*---------------------------------------------------------------------------- +* not first item beginning in this block. Figure out first what's +* going to be needed to handle the time stamp. The questions are: +* o without loss of significance, what type of delta time item is +* required? For that type of delta time item, is the delta time +* less than the maximum? If so, is there enough space left in the +* block to hold the delta time item and the first byte of a data item? +* o if a delta time item can't be used or if there isn't enough room +* in the block, then a full time stamp is needed. +*---------------------------------------------------------------------------*/ + TsDiffAsStamp(&diff, &((struct dbr_time_string *)pCaBuf)->stamp, + &pArChanDesc->timeStamp); + if (diff.nsec % 1000000 == 0 && diff.secPastEpoch < 30 && + pArChanDesc->remainCount >= 2+1) { +/*---------------------------------------------------------------------------- +* case 2 +* the change can be represented as a delta milli-second (and, there is +* room enough to store the item and the first byte of value) +*----------------------------------------------------------------------------*/ + fraction = diff.secPastEpoch*1000 + diff.nsec/1000000; + (void)ArCWStore( fraction>> 8 ); + (void)ArCWStore( fraction ); + if (ArCWStore( AR_DAT_IC_VAL_STAT ) != OK) + goto error; + } + else if (diff.nsec % 1000 == 0 && diff.secPastEpoch < 15 && + pArChanDesc->remainCount >= 4+1) { +/*---------------------------------------------------------------------------- +* case 3 +* the change can be represented as a delta micro-second (and, there is +* room enough to store the item and the first byte of value) +*----------------------------------------------------------------------------*/ + fraction = diff.secPastEpoch*1000000 + diff.nsec/1000; + (void)ArCWStore( AR_DAT_IC_TIME_USEC ); + (void)ArCWStore( fraction>>16 ); + (void)ArCWStore( fraction>> 8 ); + (void)ArCWStore( fraction ); + if (ArCWStore( AR_DAT_IC_VAL_STAT ) != OK) + goto error; + } + else if (diff.secPastEpoch < 4 && pArChanDesc->remainCount >= 5+1) { +/*---------------------------------------------------------------------------- +* case 4 +* the change can be represented as a delta nano-second (and, there is +* room enough to store the item and the first byte of value) +*----------------------------------------------------------------------------*/ + fraction = diff.secPastEpoch*1000000000 + diff.nsec; + (void)ArCWStore( AR_DAT_IC_TIME_NSEC ); + (void)ArCWStore( fraction>>24 ); + (void)ArCWStore( fraction>>16 ); + (void)ArCWStore( fraction>> 8 ); + (void)ArCWStore( fraction ); + if (ArCWStore( AR_DAT_IC_VAL_STAT ) != OK) + goto error; + } + else { +/*---------------------------------------------------------------------------- +* case 5 +* the change is either too large to be accomodated by a delta time +* item or else there isn't enough room left in the block for a delta +* time item followed by the first byte of a data item. If there isn't +* enough space left in the block for the required items, a new block +* will be started. +*---------------------------------------------------------------------------*/ + deltaCode = AR_DAT_IC_TIME_STAMP; /* full time stamp needed */ + deltaCount = 5+1; /* 5 for stamp, 1 for value code */ + if (CW_NSEC == 0) { + fracCount = 2; + fracCode = AR_DAT_IC_TIME_MSEC; /* delta msec */ + /* don't add to deltaCount, since this item is optional */ + fraction = 0; + } + else if (CW_NSEC % 1000000 == 0) { + fracCount = 2; + fracCode = AR_DAT_IC_TIME_MSEC; /* delta msec */ + deltaCount += fracCount; + fraction = CW_NSEC / 1000000; + } + else if (CW_NSEC % 1000 == 0) { + fracCount = 4; + fracCode = AR_DAT_IC_TIME_USEC; /* delta usec */ + deltaCount += fracCount; + fraction = CW_NSEC / 1000; + } + else { + fracCount = 5; + fracCode = AR_DAT_IC_TIME_NSEC; /* delta nsec */ + deltaCount += fracCount; + fraction = CW_NSEC; + } + + if (pArChanDesc->remainCount < deltaCount) { + if (ArCWFlushAndStore(AR_DAT_IC_VAL_STAT ) != OK) + goto error; + pArChanDesc->pDataBuf->bfInfo.firstByte = pArChanDesc->pData - + (char *)(&pArChanDesc->pDataBuf->bfInfo); + ArCDDatInfo(pArChanDesc).stamp = + ((struct dbr_time_string *)pCaBuf)->stamp; + ArCFModifySet(pArChanDesc->pArCfDesc, pArChanDesc->pIndexBuf); + } + else { + (void)ArCWStore(deltaCode ); + (void)ArCWStore(CW_SPE >>24 ); + (void)ArCWStore(CW_SPE >>16 ); + (void)ArCWStore(CW_SPE >> 8 ); + (void)ArCWStore(CW_SPE ); + if (fraction != 0) { + if (fracCode != AR_DAT_IC_TIME_MSEC) { + (void)ArCWStore(fracCode ); + if (fracCode == AR_DAT_IC_TIME_NSEC) + (void)ArCWStore(fraction>>24 ); + (void)ArCWStore(fraction>>16 ); + } + (void)ArCWStore( fraction>> 8 ); + (void)ArCWStore( fraction ); + } + if (ArCWStore( AR_DAT_IC_VAL_STAT ) != OK) + goto error; + } + } + } + pArChanDesc->timeStamp = ((struct dbr_time_string *)pCaBuf)->stamp; + +/*---------------------------------------------------------------------------- +* now store the status and data. The code above has already stored +* an AR_DAT_IC_VAL_STAT item code in the file. +*----------------------------------------------------------------------------*/ + (void)ArCWStore(((struct dbr_time_string *)pCaBuf)->status); + (void)ArCWStore(((struct dbr_time_string *)pCaBuf)->severity); + if (pArg->type == DBR_TIME_STRING) { +#define ArString ((struct dbr_time_string *)pCaBuf) + assert(ArCDChanHdr(pArChanDesc).chanType == DBF_STRING); + ptr = (char *)(ArString->value); + ptrEnd = ptr + pArg->count*db_strval_dim - 1; + while (ptr < ptrEnd) + (void)ArCWStore(*ptr++); + if (ArCWStore(*ptr++) != OK) goto error; + } + else if (pArg->type == DBR_TIME_SHORT) { +#define ArShort ((struct dbr_time_short *)pCaBuf) + assert(ArCDChanHdr(pArChanDesc).chanType == DBF_SHORT); + ptr = (char *)(&ArShort->value); + ptrEnd = ptr + pArg->count * sizeof(short) - 1; + while (ptr < ptrEnd) + (void)ArCWStore(*ptr++); + if (ArCWStore(*ptr++) != OK) goto error; + } + else if (pArg->type == DBR_TIME_FLOAT) { +#define ArFloat ((struct dbr_time_float *)pCaBuf) + assert(ArCDChanHdr(pArChanDesc).chanType == DBF_FLOAT); + ptr = (char *)(&ArFloat->value); + ptrEnd = ptr + pArg->count * sizeof(float) - 1; + while (ptr < ptrEnd) + (void)ArCWStore(*ptr++); + if (ArCWStore(*ptr++) != OK) goto error; + } + else if (pArg->type == DBR_TIME_LONG) { +#define ArLong ((struct dbr_time_long *)pCaBuf) + assert(ArCDChanHdr(pArChanDesc).chanType == DBF_LONG); + ptr = (char *)(&ArLong->value); + ptrEnd = ptr + pArg->count * sizeof(long) - 1; + while (ptr < ptrEnd) + (void)ArCWStore(*ptr++); + if (ArCWStore(*ptr++) != OK) goto error; + } + else if (pArg->type == DBR_TIME_DOUBLE) { +#define ArDouble ((struct dbr_time_double *)pCaBuf) + assert(ArCDChanHdr(pArChanDesc).chanType == DBF_DOUBLE); + ptr = (char *)(&ArDouble->value); + ptrEnd = ptr + pArg->count * sizeof(double) - 1; + while (ptr < ptrEnd) + (void)ArCWStore(*ptr++); + if (ArCWStore(*ptr++) != OK) goto error; + } + else if (pArg->type == DBR_TIME_ENUM) { +#define ArEnum ((struct dbr_time_enum *)pCaBuf) + assert(ArCDChanHdr(pArChanDesc).chanType == DBF_ENUM); + ptr = (char *)(&ArEnum->value); + ptrEnd = ptr + pArg->count * sizeof(short) - 1; + while (ptr < ptrEnd) + (void)ArCWStore(*ptr++); + if (ArCWStore(*ptr++) != OK) goto error; + } + else if (pArg->type == DBR_TIME_CHAR) { +#define ArChar ((struct dbr_time_char *)pCaBuf) + assert(ArCDChanHdr(pArChanDesc).chanType == DBF_CHAR); + ptr = (char *)(&ArChar->value); + ptrEnd = ptr + pArg->count * sizeof(char) - 1; + while (ptr < ptrEnd) + (void)ArCWStore(*ptr++); + if (ArCWStore(*ptr++) != OK) goto error; + } + else + assertAlways(0); + +/*----------------------------------------------------------------------------- +* update bookkeeping information +* +* .lastByte +* .modified in data buffer +* .newestStamp in MI buffer +* .modified in MI buffer +*----------------------------------------------------------------------------*/ + 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); + ArCDChanHdr(pArChanDesc).newestSecPastEpoch = + pArChanDesc->timeStamp.secPastEpoch; + ArCDChanHdr(pArChanDesc).newestNsec = pArChanDesc->timeStamp.nsec; + ArCFModifySet(pArChanDesc->pArCfDesc, pArChanDesc->pMIBuf); + + return OK; +error: + return ERROR; +} + +/*+/subr********************************************************************** +* NAME arCFChanWriteByte - write byte into a channel's stream +* +* DESCRIPTION +* +* RETURNS +* OK, or +* ERROR +* +* BUGS +* o text +* +*-*/ +long +arCFChanWriteByte(pArChanDesc, value) +AR_CHAN_DESC *pArChanDesc; /* IO pointer to channel descriptor */ +int value; /* I value to write to stream */ +{ + int stat; /* status from calls */ + + assert(pArChanDesc != NULL); + assert(pArChanDesc->pMIBuf != NULL); + + pArChanDesc->flags &= ~AR_CDESC_SUP; /* turn off suppress */ + + if (pArChanDesc->remainCount < 1) { + if (ArCWFlushAndStore(value ) != OK) + goto error; + } + else { + if (ArCWStore( value ) != OK) + goto error; + } + if (pArChanDesc->pDataBuf->bfInfo.firstByte == 0) { + pArChanDesc->pDataBuf->bfInfo.firstByte = pArChanDesc->pData - + (char *)(&pArChanDesc->pDataBuf->bfInfo); + } + 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); + + return OK; +error: + return ERROR; +} + +/*+/subr********************************************************************** +* NAME arCFChanWriteGR - write a channel access DBR_GR buffer to a channel +* +* DESCRIPTION +* This routine writes DBR_GR_xxx information to the chanHdr for a +* channel. +* +* RETURNS +* OK, or +* ERROR +* +* BUGS +* o text +* +* NOTES +* 1. This routine does NOT write a value to the file. +* +*-*/ +long +arCFChanWriteGR(pArChanDesc, pArg) +AR_CHAN_DESC *pArChanDesc; /* IO pointer to channel descriptor */ +struct event_handler_args *pArg;/* I pointer to monitor structure */ +{ + void *pCaBuf; /* pointer to buffer */ + int i; + long stat; + +/*---------------------------------------------------------------------------- +* get some stuff out of the structure supplied as input pArg +*---------------------------------------------------------------------------*/ + pCaBuf = pArg->dbr; + + assert(pArChanDesc != NULL); + assert(pArChanDesc->pMIBuf != NULL); + assert(pCaBuf != NULL); + assert(dbr_type_is_GR(pArg->type)); + + if (pArg->type == DBR_GR_STRING) { + ArCDChanHdr(pArChanDesc).chanType = DBF_STRING; + ArCDChanHdr(pArChanDesc).elCount = pArg->count; + } + else if (pArg->type == DBR_GR_SHORT) { +#define ArCWShrtGr ArCDChanHdr(pArChanDesc).graphics.shortGr +#define ArCWgr_short ((struct dbr_gr_short *)pCaBuf) + + ArCDChanHdr(pArChanDesc).chanType = DBF_SHORT; + ArCDChanHdr(pArChanDesc).elCount = pArg->count; + (void)strcpy(ArCWShrtGr.units, ArCWgr_short->units); + ArCWShrtGr.upDispLim = ArCWgr_short->upper_disp_limit; + ArCWShrtGr.lowDispLim = ArCWgr_short->lower_disp_limit; + ArCWShrtGr.upAlmLim = ArCWgr_short->upper_alarm_limit; + ArCWShrtGr.upWarnLim = ArCWgr_short->upper_warning_limit; + ArCWShrtGr.lowWarnLim = ArCWgr_short->lower_warning_limit; + ArCWShrtGr.lowAlmLim = ArCWgr_short->lower_alarm_limit; + } + else if (pArg->type == DBR_GR_FLOAT) { +#define ArCWFltGr ArCDChanHdr(pArChanDesc).graphics.floatGr +#define ArCWgr_float ((struct dbr_gr_float *)pCaBuf) + + ArCDChanHdr(pArChanDesc).chanType = DBF_FLOAT; + ArCDChanHdr(pArChanDesc).elCount = pArg->count; + ArCWFltGr.precision = ArCWgr_float->precision; + (void)strcpy(ArCWFltGr.units, ArCWgr_float->units); + ArCWFltGr.upDispLim = ArCWgr_float->upper_disp_limit; + ArCWFltGr.lowDispLim = ArCWgr_float->lower_disp_limit; + ArCWFltGr.upAlmLim = ArCWgr_float->upper_alarm_limit; + ArCWFltGr.upWarnLim = ArCWgr_float->upper_warning_limit; + ArCWFltGr.lowWarnLim = ArCWgr_float->lower_warning_limit; + ArCWFltGr.lowAlmLim = ArCWgr_float->lower_alarm_limit; + } + else if (pArg->type == DBR_GR_LONG) { +#define ArCWLngGr ArCDChanHdr(pArChanDesc).graphics.longGr +#define ArCWgr_long ((struct dbr_gr_long *)pCaBuf) + + ArCDChanHdr(pArChanDesc).chanType = DBF_LONG; + ArCDChanHdr(pArChanDesc).elCount = pArg->count; + (void)strcpy(ArCWLngGr.units, ArCWgr_long->units); + ArCWLngGr.upDispLim = ArCWgr_long->upper_disp_limit; + ArCWLngGr.lowDispLim = ArCWgr_long->lower_disp_limit; + ArCWLngGr.upAlmLim = ArCWgr_long->upper_alarm_limit; + ArCWLngGr.upWarnLim = ArCWgr_long->upper_warning_limit; + ArCWLngGr.lowWarnLim = ArCWgr_long->lower_warning_limit; + ArCWLngGr.lowAlmLim = ArCWgr_long->lower_alarm_limit; + } + else if (pArg->type == DBR_GR_DOUBLE) { +#define ArCWDblGr ArCDChanHdr(pArChanDesc).graphics.doubleGr +#define ArCWgr_double ((struct dbr_gr_double *)pCaBuf) + + ArCDChanHdr(pArChanDesc).chanType = DBF_DOUBLE; + ArCDChanHdr(pArChanDesc).elCount = pArg->count; + ArCWDblGr.precision = ArCWgr_double->precision; + (void)strcpy(ArCWDblGr.units, ArCWgr_double->units); + ArCWDblGr.upDispLim = ArCWgr_double->upper_disp_limit; + ArCWDblGr.lowDispLim = ArCWgr_double->lower_disp_limit; + ArCWDblGr.upAlmLim = ArCWgr_double->upper_alarm_limit; + ArCWDblGr.upWarnLim = ArCWgr_double->upper_warning_limit; + ArCWDblGr.lowWarnLim = ArCWgr_double->lower_warning_limit; + ArCWDblGr.lowAlmLim = ArCWgr_double->lower_alarm_limit; + } + else if (pArg->type == DBR_GR_ENUM) { +/*---------------------------------------------------------------------------- +* Store the graphics info into the graphics buffer, set the modified +* bit for the buffer, and also set lastByte for the disk block. +*---------------------------------------------------------------------------*/ +#define ArCWEnmGr ArCDChanHdr(pArChanDesc).graphics.enumGr.pGRBuf +#define ArCWgr_enum ((struct dbr_gr_enum *)pCaBuf) + + if (ArCDChanHdr(pArChanDesc).chanType == DBF_ENUM) { + assert(ArCWEnmGr != NULL); /* better have a buffer */ + assert(ArCWEnmGr->blkNum != 0); /* it must contain a block */ + } + else { + ArCDChanHdr(pArChanDesc).chanType = DBF_ENUM; + if ((stat = arCF_GRGetBlock(pArChanDesc)) != OK) + assertAlways(0); + } + + ArCDChanHdr(pArChanDesc).elCount = pArg->count; + ArCWEnmGr->numStrings = ArCWgr_enum->no_str; + for (i=0; inumStrings; i++) + (void)strcpy(ArCWEnmGr->states[i], ArCWgr_enum->strs[i]); + ArCWEnmGr->bfInfo.lastByte = BF_BLOCK_DATA + 2 + + ArCWEnmGr->numStrings * db_state_text_dim - 1; + stat = bfWrite(pArChanDesc->pArCfDesc->pBfDesc, + &ArCWEnmGr->bfInfo, ArCWEnmGr->blkNum); + if (stat != OK) { + (void)fprintf(stderr, "arCFWriteGR: error writing GR block\n"); + assertAlways(0); + } + } + else if (pArg->type == DBR_GR_CHAR) { +#define ArCWChrGr ArCDChanHdr(pArChanDesc).graphics.charGr +#define ArCWgr_char ((struct dbr_gr_char *)pCaBuf) + + ArCDChanHdr(pArChanDesc).chanType = DBF_CHAR; + ArCDChanHdr(pArChanDesc).elCount = pArg->count; + (void)strcpy(ArCWChrGr.units, ArCWgr_char->units); + ArCWChrGr.upDispLim = ArCWgr_char->upper_disp_limit; + ArCWChrGr.lowDispLim = ArCWgr_char->lower_disp_limit; + ArCWChrGr.upAlmLim = ArCWgr_char->upper_alarm_limit; + ArCWChrGr.upWarnLim = ArCWgr_char->upper_warning_limit; + ArCWChrGr.lowWarnLim = ArCWgr_char->lower_warning_limit; + ArCWChrGr.lowAlmLim = ArCWgr_char->lower_alarm_limit; + } + else { + assertAlways(0); + } + + ArCFModifySet(pArChanDesc->pArCfDesc, pArChanDesc->pMIBuf); + + return OK; +} + +/*+/subr********************************************************************** +* NAME arCFClose - close an AR channel data file +* +* DESCRIPTION +* Closes an AR channel data file. If the file was open in O_RDWR +* mode, then buffers are flushed and the lock file is deleted. +* +* RETURNS +* OK, or +* ERROR +* +* BUGS +* o text +* +* SEE ALSO +* arCFOpen(), arCFCreate() +* +* EXAMPLE +* +*-*/ +long +arCFClose(ppArCfDesc) +AR_CF_DESC **ppArCfDesc; /* IO ptr to ptr to channel file descriptor */ +{ + int stat; /* status from calls */ + int retStat=OK; /* return status */ + AR_CF_DESC *pCfDesc; /* pointer to channel file descriptor */ + AR_CHAN_DESC *pChanDesc; /* pointer to channel descriptor */ + + assert(ppArCfDesc != NULL); + assert(*ppArCfDesc != NULL); + + pCfDesc = *ppArCfDesc; + + if (pCfDesc->pChanDescHead != NULL) { + assert(pCfDesc->pBfDesc != NULL); + assert(pCfDesc->pChanDescTail != NULL); + + /* close all the channel descriptors */ + while ((pChanDesc = pCfDesc->pChanDescHead) != NULL) { + stat = arCFChanClose(&pChanDesc); + } + + } + + assert(pCfDesc->pChanDescHead == NULL); /* should be closed */ + assert(pCfDesc->pChanDescTail == NULL); + + if (pCfDesc->pMIHead != NULL) { + assert(pCfDesc->pBfDesc != NULL); + assert(pCfDesc->pMITail != NULL); + +/*---------------------------------------------------------------------------- +* check all MI blocks for "MODIFIED" bit, and write those with it set; +* ALL MI blocks will be free()'d. When the entire MI block list has +* been traversed, both MIHead and MITail in the channel file descriptor +* will be null. +* +* As part of the traversal, check each chanHdr for DBF_ENUM channels. +* The GR block may need to be written, and the buffer will need to +* be free()'d. +*---------------------------------------------------------------------------*/ + while (pCfDesc->pMIHead != NULL) { + AR_MI_BUF *pMIBuf; /* pointer to MI block */ + AR_GR_BUF *pGRBuf; /* pointer to GR block */ + int i; + + pMIBuf = pCfDesc->pMIHead; + for (i=0; ichanHdr[i].name[0] == '\0') + ; /* no action */ + else if (pMIBuf->chanHdr[i].chanType == DBF_ENUM && + (pGRBuf=pMIBuf->chanHdr[i].graphics.enumGr.pGRBuf) != NULL) { + if (ArCFModifyTestAndReset(pCfDesc, pGRBuf)) { + stat = bfWrite(pCfDesc->pBfDesc, + &pGRBuf->bfInfo, pGRBuf->blkNum); + if (stat != OK) { + (void)fprintf(stderr, + "arCFClose: error writing GR block\n"); + retStat = ERROR; + } + } + arCF_freeGR_BUF(pGRBuf); + pMIBuf->chanHdr[i].graphics.enumGr.pGRBuf = NULL; + } + } + pCfDesc->pMIHead = pMIBuf->pNextMI; + if ((stat = arCF_MIFlush(pCfDesc, pMIBuf)) != OK) { + (void)fprintf(stderr, "arCFClose: error writing MI block\n"); + retStat = ERROR; + } + arCF_freeMI_BUF(pMIBuf); + } + pCfDesc->pMITail = NULL; + } + + assert(pCfDesc->pMIHead == NULL); + assert(pCfDesc->pMITail == NULL); + + if (pCfDesc->pBfDesc != NULL) { + stat = bfClose(&(*ppArCfDesc)->pBfDesc); + if (stat != OK) { + (void)fprintf(stderr, "arCFClose: error closing block file\n"); + retStat = ERROR; + } + } + + arCF_freeCF_DESC(*ppArCfDesc); + *ppArCfDesc = NULL; + + return retStat; +} + +/*+/subr********************************************************************** +* NAME arCFCreate - create an AR channel data file +* +* DESCRIPTION +* Creates an empty 'block file' of type BF_TYPE_CHAN_DATA. The +* maximum size for the file will be set at the number of blocks +* which will fit with the caller specified number of bytes. The +* initial size for the file is 1 block. +* +* If the create is successful, then the file is open in O_RDWR mode +* when control returns to the caller. The channel file descriptor +* and other structures have been set up as would be done by arCFOpen(). +* In addition, a lock file has been created to prevent multiple +* writers. +* +* The block size is forced to be AR_CF_BLKSIZE bytes. +* +* RETURNS +* pointer to channel file descriptor, or +* NULL if the file already exists or if an error occurs on create +* +* BUGS +* o no exit handler is established (see arCFOpen() ) +* +* SEE ALSO +* arCFOpen(), arCFClose(), arCFChanOpen() +* +*-*/ +AR_CF_DESC * +arCFCreate(name, maxBytes) +char *name; /* I file name */ +int maxBytes; /* I maximum number of bytes for file */ +{ + int stat=OK; /* status from calls */ + + BF_BLKNUM maxBlocks; /* size limit */ + BF_DESC *pDesc; /* pointer to block file descriptor */ + AR_CF_DESC *pArCfDesc=NULL;/* pointer to channel file descriptor */ + + assert(strlen(name) < GEN_FNAME_DIM); + assert(strlen(name) > 0); + + +/*---------------------------------------------------------------------------- +* allocate memory for an AR channel file descriptor and initialize the +* descriptor. Then create the block file and put into the channel file +* descriptor a pointer to the block file descriptor. This hides from +* most users the fact that there are actually two descriptors--they +* just use the channel file descriptor. +*---------------------------------------------------------------------------*/ + if ((pArCfDesc = arCF_mallocCF_DESC()) == NULL) { + (void)fprintf(stderr, "arCFCreate: can't malloc for arCfDesc\n"); + stat = ERROR; + } + else { + pArCfDesc->pBfDesc = NULL; + pArCfDesc->pMIHead = NULL; /* no MI buffers yet */ + pArCfDesc->pMITail = NULL; + pArCfDesc->pMIFree = NULL; + pArCfDesc->MIFree_hdrNum = 0; + pArCfDesc->pChanDescHead = NULL; /* no channel desc exist yet */ + pArCfDesc->pChanDescTail = NULL; + pArCfDesc->pWriteHead = NULL; + pArCfDesc->pWriteTail = NULL; + pArCfDesc->b0Modified = 0; + } + + if (maxBytes < AR_CF_BLKSIZE) { + (void)printf("nBytes must be at least %d\n", AR_CF_BLKSIZE); + stat = ERROR; + } + if (stat == OK) { + maxBlocks = maxBytes / AR_CF_BLKSIZE; + + pDesc = bfCreate(name, BF_TYPE_CHAN_DATA, + 1, AR_CF_BLKSIZE, BF_FLAG_GROW, maxBlocks); + if (pDesc == NULL) + stat = ERROR; + else + pArCfDesc->pBfDesc = pDesc; + } + if (stat == OK) { +/*---------------------------------------------------------------------------- +* create has succeeded; initialize userInfo in block0 +*---------------------------------------------------------------------------*/ + ArB0MIHead(pArCfDesc) = 0; + ArB0MITail(pArCfDesc) = 0; + ArB0MINblk(pArCfDesc) = 0; + ArB0MIFree_blkNum(pArCfDesc) = 0; + ArB0MIFree_hdrNum(pArCfDesc) = 0; + ArB0FormatRev(pArCfDesc) = AR_CF_FORMAT_REV; + if ((stat = bfWrite(pDesc, pDesc->pBlock0, 0)) != OK) + (void)printf("arCFCreate: error writing block0\n"); + } + if (stat != OK) { + if (pArCfDesc != NULL) { +/*---------------------------------------------------------------------------- +* ERROR has occurred; clean up whatever structure has been built and close +* the file. +*---------------------------------------------------------------------------*/ + (void)arCFClose(&pArCfDesc); + assert(pArCfDesc == NULL); + } + } + + return pArCfDesc; +} + +/*+/subr********************************************************************** +* NAME arCFOpen - open an AR channel data file +* +* DESCRIPTION +* Opens an existing AR channel data file (a 'block file' of type +* BF_TYPE_CHAN_DATA) in the specified mode, O_RDONLY or O_RDWR. If +* the mode is O_RDWR, then a lock file is created to prevent multiple +* writers of the file. +* +* Upon a successful open, the master index blocks have been read into +* memory (and the channel file descriptor has the appropriate pointers). +* +* The block size in the file must be AR_CF_BLKSIZE. The file +* `format revision number' must match the `format revision number' +* the code was compiled with. +* +* RETURNS +* pointer to channel file descriptor, or +* NULL +* +* BUGS +* o lock file handling is somewhat `ratty', especially under VxWorks; +* there is a slim chance that multiple writers could occur. +* o no exit handler is established, which means that AR_CFClose() +* isn't automatically called. This means that buffers aren't flushed +* and the lock file isn't deleted if the program aborts. +* +* SEE ALSO +* arCFCreate(), arCFClose(), arCFChanOpen() +* +* EXAMPLE +* +*-*/ +AR_CF_DESC * +arCFOpen(name, mode) +char *name; /* I file name */ +int mode; /* I mode to use for open--O_RDONLY or O_RDWR */ +{ + int stat=OK; /* status from calls */ + BF_DESC *pDesc=NULL; /* pointer to block file descriptor */ + AR_CF_DESC *pArCfDesc=NULL;/* pointer to channel file descriptor */ + + assert(strlen(name) < GEN_FNAME_DIM); + assert(strlen(name) > 0); + assert(mode == O_RDONLY || mode == O_RDWR); + (void)arCSCheck(); /* check control system 'compatibility' */ + (void)arCFAsserts(); /* check control system 'compatibility' */ + +/*---------------------------------------------------------------------------- +* allocate memory for an AR channel file descriptor and initialize the +* descriptor. Then open the block file and put into the channel file +* descriptor a pointer to the block file descriptor. This hides from +* most users the fact that there are actually two descriptors--they +* just use the channel file descriptor. +*---------------------------------------------------------------------------*/ + if ((pArCfDesc = arCF_mallocCF_DESC()) == NULL) { + (void)fprintf(stderr, "arCFOpen: can't malloc for arCfDesc\n"); + stat = ERROR; + } + else { + pArCfDesc->pBfDesc = NULL; + pArCfDesc->pMIHead = NULL; /* no MI buffers yet */ + pArCfDesc->pMITail = NULL; + pArCfDesc->pMIFree = NULL; + pArCfDesc->MIFree_hdrNum = 0; + pArCfDesc->pChanDescHead = NULL; /* no channel desc exist yet */ + pArCfDesc->pChanDescTail = NULL; + pArCfDesc->pWriteHead = NULL; + pArCfDesc->pWriteTail = NULL; + pArCfDesc->b0Modified = 0; + } + if (stat == OK) { + if ((pDesc = bfOpen(name, mode, BF_TYPE_CHAN_DATA, 0)) == NULL) + stat = ERROR; + else + pArCfDesc->pBfDesc = pDesc; + } + if (stat == OK) { + if (BfB0BlockSize(pDesc) != AR_CF_BLKSIZE) { + (void)fprintf(stderr, "arCfOpen: block size mismatch\n"); + stat = ERROR; + } + } + if (stat == OK) { + if (ArB0FormatRev(pArCfDesc)/100 != AR_CF_FORMAT_REV/100) { + (void)fprintf(stderr, + "arCfOpen: file format mismatch--file %d, code %d\n", + ArB0FormatRev(pArCfDesc), AR_CF_FORMAT_REV); + stat = ERROR; + } + } + if (stat == OK) { +/*---------------------------------------------------------------------------- +* so far, the open is going OK. Do any additional processing needed. +*---------------------------------------------------------------------------*/ + stat = arCFReadMIBuffers(pArCfDesc); + } + + if (stat != OK) { + if (pArCfDesc != NULL) { +/*---------------------------------------------------------------------------- +* ERROR has occurred; clean up whatever structure has been built and close +* the file. +*---------------------------------------------------------------------------*/ + (void)arCFClose(&pArCfDesc); + assert(pArCfDesc == NULL); + } + } + + return pArCfDesc; +} + +/*+/subr********************************************************************** +* NAME arCFReadMIBuffers - read (or re-read) the MI blocks +* +* DESCRIPTION +* +* RETURNS +* +* BUGS +* o doesn't have any protection against changes in linked lists while +* this routine is running +* o doesn't handle deleting channels or purging data (i.e., deleting +* .datInfo items or entire index blocks) +* o comments are out of date--they don't describe re-read of MI blocks +* +* SEE ALSO +* +* EXAMPLE +* +*-*/ +long +arCFReadMIBuffers(pArCfDesc) +AR_CF_DESC *pArCfDesc; /* I ptr to channel file descriptor */ +{ + int retStat=OK; /* return status to caller */ + int stat; /* status return from calls */ + BF_BLKNUM blockNum; /* block number */ + AR_MI_BUF *pMIBuf; /* ptr to buffer for block */ + int i; + AR_MI_BUF MIBuf; /* temporary buffer for re-read of MI block */ + + if (ArB0MIHead(pArCfDesc) == 0) + return OK; +/*---------------------------------------------------------------------------- +* the file has some Master Index blocks; read ALL OF THEM into memory. +* +* For each MI block, get a buffer, link the buffer into the MI buffer +* list, put the block number into the buffer, reset the buffer's flags, +* and, finally, read the block into the buffer. Once the block is in +* the buffer, do some initialization for each chanHdr. This +* initialization includes reading the blocks for DBF_ENUM channels. +* +* If block0 points to an MIFree, then check each MI block to see if it +* is the one pointed to; when a match is found, initialize the MIFree +* information in the channel file descriptor. +*---------------------------------------------------------------------------*/ + + blockNum = ArB0MIHead(pArCfDesc); + while (blockNum > 0) { + pMIBuf = pArCfDesc->pMIHead; + while (pMIBuf != NULL) { + if (pMIBuf->blkNum == blockNum) + break; + pMIBuf = pMIBuf->pNextMI; + } + if (pMIBuf == NULL) { + if ((pMIBuf = arCF_mallocMI_BUF()) == NULL) { + (void)fprintf(stderr, "arCFOpen: can't malloc for MI block\n"); + stat = ERROR; + goto readMIError; + } + if (pArCfDesc->pMIHead == NULL) /* first block? */ + pArCfDesc->pMIHead = pMIBuf; /* yes; link to head */ + else + pArCfDesc->pMITail->pNextMI = pMIBuf; /* no; link from prev */ + pArCfDesc->pMITail = pMIBuf; /* this is now tail */ + pMIBuf->blkNum = blockNum; + pMIBuf->pNextMI = NULL; + ArCFModifyInit(pMIBuf); + pMIBuf->pBlock = &pMIBuf->bfInfo; + + stat = bfRead(pArCfDesc->pBfDesc, &pMIBuf->bfInfo, blockNum); + if (stat != OK) { + (void)fprintf(stderr, "arCFOpen: error reading MI block\n"); + stat = ERROR; + goto readMIError; + } + + for (i=0; ichanHdr[i].count = 0; + pMIBuf->chanHdr[i].flags = 0; + if (pMIBuf->chanHdr[i].name[0] != '\0' && + pMIBuf->chanHdr[i].chanType == DBF_ENUM) { + pMIBuf->chanHdr[i].graphics.enumGr.pGRBuf = NULL; + stat = arCF_GRReadBlock(pArCfDesc, &pMIBuf->chanHdr[i]); + if (stat != OK) { + (void)fprintf(stderr, + "arCFOpen: error reading GR block for ENUM\n"); + stat = ERROR; + goto readMIError; + } + } + } + } + else { + stat = bfRead(pArCfDesc->pBfDesc, &MIBuf.bfInfo, blockNum); + if (stat != OK) { + (void)fprintf(stderr, "arCFOpen: error reading MI block\n"); + stat = ERROR; + goto readMIError; + } + pMIBuf->bfInfo.flink = MIBuf.bfInfo.flink; + for (i=0; ichanHdr[i].name, + MIBuf.chanHdr[i].name) == 0); + assertAlways(pMIBuf->chanHdr[i].indexHead == + MIBuf.chanHdr[i].indexHead); + pMIBuf->chanHdr[i].indexTail = MIBuf.chanHdr[i].indexTail; + assertAlways(pMIBuf->chanHdr[i].oldestSecPastEpoch == + MIBuf.chanHdr[i].oldestSecPastEpoch); + pMIBuf->chanHdr[i].newestSecPastEpoch = + MIBuf.chanHdr[i].newestSecPastEpoch; + pMIBuf->chanHdr[i].newestNsec = + MIBuf.chanHdr[i].newestNsec; + } + else { + pMIBuf->chanHdr[i].graphics.nextMIFree = + MIBuf.chanHdr[i].graphics.nextMIFree; + } + } + + } + if (ArB0MIFree_blkNum(pArCfDesc) == blockNum) { + pArCfDesc->pMIFree = pMIBuf; + pArCfDesc->MIFree_hdrNum = ArB0MIFree_hdrNum(pArCfDesc); + } + GenBufCheck(pMIBuf); + blockNum = pMIBuf->bfInfo.flink; /* next MI block */ + } + return OK; +readMIError: + return ERROR; +} + +/*+/subr********************************************************************** +* NAME arCFSyncRead - synchronize read buffers in memory with disk +* +* DESCRIPTION +* +* RETURNS +* +* BUGS +* o text +* +* SEE ALSO +* +* EXAMPLE +* +*-*/ +long +arCFSyncRead(pArCfDesc) +AR_CF_DESC *pArCfDesc; /* I ptr to channel file descriptor */ +{ + int retStat=OK; /* return status to caller */ + int stat; /* status return from calls */ + int readOnly; /* channel file is opened read-only */ + AR_CHAN_DESC *pChanDesc; + BF_DESC *pBfDesc; + + assert(pArCfDesc != NULL); + assert(pArCfDesc->pBfDesc != NULL); + pBfDesc = pArCfDesc->pBfDesc; + + readOnly = ((pBfDesc->flags & BF_DESC_WRITE) == 0); + if (readOnly) { + stat = bfRead(pBfDesc, pBfDesc->pBlock0, 0); + assertAlways(stat == OK); + stat = arCFReadMIBuffers(pArCfDesc); + assertAlways(stat == OK); + } + pChanDesc = pArCfDesc->pChanDescHead; + while (pChanDesc != NULL) { + if ((pChanDesc->flags & AR_CDESC_WRITE) == 0) { + if (pChanDesc->pIndexBuf != NULL && + pChanDesc->pIndexBuf->bfInfo.flink == 0) { + stat = bfRead(pBfDesc, &pChanDesc->pIndexBuf->bfInfo, + pChanDesc->pIndexBuf->blkNum); + assertAlways(stat == OK); + } + if (pChanDesc->pDataBuf != NULL && + pChanDesc->pDataBuf->bfInfo.flink == 0) { + stat = bfRead(pBfDesc, &pChanDesc->pDataBuf->bfInfo, + pChanDesc->pDataBuf->blkNum); + assertAlways(stat == OK); + pChanDesc->remainCount = + pChanDesc->pData-(char *)&pChanDesc->pDataBuf->bfInfo - + pChanDesc->pDataBuf->bfInfo.firstByte; + } + } + pChanDesc = pChanDesc->pNextDesc; + } + + return retStat; +} + +/*+/subr********************************************************************** +* NAME arCFSyncWrite - synchronize disk with write buffers in memory +* +* DESCRIPTION +* +* RETURNS +* OK +* +* BUGS +* o doesn't lock for list and write operations +* +* SEE ALSO +* +* EXAMPLE +* +*-*/ +long +arCFSyncWrite(pArCfDesc) +AR_CF_DESC *pArCfDesc; /* I ptr to channel file descriptor */ +{ + int retStat=OK; /* return status to caller */ + int stat; /* status return from calls */ + AR_MI_BUF *pBuf; /* preamble is same for all types of buffer */ + int modified=0; + + assert(pArCfDesc != NULL); + + while ((pBuf = pArCfDesc->pWriteHead) != NULL) { + modified++; + if (ArCFModifyTestAndReset(pArCfDesc, pBuf)) { + stat = bfWrite(pArCfDesc->pBfDesc, &pBuf->bfInfo, pBuf->blkNum); + assertAlways(stat == OK); + } + } + + if (modified) { + pArCfDesc->b0Modified = 0; + stat = bfWrite(pArCfDesc->pBfDesc, pArCfDesc->pBfDesc->pBlock0, 0); + if (stat != OK) + retStat = ERROR; + } + + return retStat; +} diff --git a/src/libCom/arCSCheck.c b/src/libCom/arCSCheck.c new file mode 100644 index 000000000..03683e724 --- /dev/null +++ b/src/libCom/arCSCheck.c @@ -0,0 +1,120 @@ +/**************************************************************************** + GTA PROJECT AT division + + Copyright, 1990, The Regents of the University of California + Los Alamos National Laboratory + + FILE PATH: ~gta/ar/arCSCheck.c + ENVIRONMENT: SunOS, VxWorks + MAKE OPTIONS: + SCCS VERSION: $Id$ +*+/mod*********************************************************************** +* TITLE arCSCheck.c - check assumptions about definitions in arCS.h +* +* DESCRIPTION +* +*- +* Modification History +* version date programmer comments +* ------- -------- ------------ ----------------------------------- +* 1.1 04/01/90 R. Cole initial version +* +*****************************************************************************/ +#ifdef AR_CS_NEW_DBR +# define DB_TEXT_GLBLSOURCE +#endif + +#include +#include +#include +#ifndef INCLcadefh +# include +#endif + +#ifndef INCLdb_accessh +# ifndef AR_CS_NEW_DBR +# include +# else +# include +# endif +#endif + + + +/*+/subr********************************************************************** +* NAME arCSCheck - check assumptions used by AR about the control system +* +* DESCRIPTION +* +* RETURNS +* +* BUGS +* o doesn't actually check against GTACS #define's +* +* SEE ALSO +* arCS.h +* +* EXAMPLE +* +*-*/ +int +arCSCheck() +{ +#ifdef NDEBUG +# define NDEBUG /* force checks even if compiled NDEBUG */ +#endif + +/*---------------------------------------------------------------------------- +* dimensions for text strings must be multiples of 4 for proper alignment +* of structures. In addition, specific dimensions are checked for string +* types which are stored in files--changing these dimensions requires +* reformatting existing files. +* +* Just for documentation purposes, alarm severity and status are checked. +* Again, these are important because of file layout. +*---------------------------------------------------------------------------*/ + assert(AR_NAME_DIM % 4 == 0); + assert(AR_NAME_DIM == 36); + assert(AR_NAME_DIM == db_name_dim); + assert(AR_STRVAL_DIM == 40); + assert(AR_STRVAL_DIM == db_strval_dim); + assert(AR_DESC_DIM % 4 == 0); + assert(AR_DESC_DIM == 24); + assert(AR_DESC_DIM == db_desc_dim); + assert(AR_UNITS_DIM % 4 == 0); + assert(AR_UNITS_DIM == 8); + assert(AR_UNITS_DIM == db_units_dim); + assert(AR_STATE_DIM == 16); + assert(AR_STATE_DIM == db_state_dim); + assert(AR_STATE_TEXT_DIM == 26); + assert(AR_STATE_TEXT_DIM == db_state_text_dim); + assert(sizeof(AR_ALM_SEV) == 1); + assert(sizeof(AR_ALM_STAT) == 1); + +/*---------------------------------------------------------------------------- +* check for incompatibilities between the assumptions used by AR and +* the actual sizes in the control system. +*---------------------------------------------------------------------------*/ + assert(AR_STRVAL_DIM == dbr_size[DBR_STRING]); +#ifndef AR_CS_NEW_DBR + assert(2 == dbr_size[DBR_INT]); +#else + assert(2 == dbr_size[DBR_SHORT]); +#endif + assert(4 == dbr_size[DBR_FLOAT]); + +/*---------------------------------------------------------------------------- +* miscellaneous +*---------------------------------------------------------------------------*/ + assert(4 == sizeof(long)); /* block numbers require 32 bit integer; + also important for file layout */ + assert(2 == sizeof(short)); /* important for file layout */ + assert(4 == sizeof(float)); /* important for file layout */ + assert(1 == sizeof(char)); /* important for file layout */ + assert(2 == sizeof(USHORT));/* important for file layout */ + assert(4 == sizeof(ULONG)); /* important for file layout */ + assert(TS_EPOCH_YEAR == 1990);/* existing file time stamps are + invalidated if epoch year changes*/ + + return OK; +} diff --git a/src/libCom/arChanIO.c b/src/libCom/arChanIO.c new file mode 100644 index 000000000..0a9a7dd08 --- /dev/null +++ b/src/libCom/arChanIO.c @@ -0,0 +1,1635 @@ +/* $Id$ + * Author: Roger A. Cole + * Date: 03-09-90 + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, 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 mm-dd-yy rac version 2.0, installed in SCCS + * + * make options + * -DvxWorks makes a version for VxWorks + * -DNDEBUG don't compile assert() checking + * -DDEBUG compile various debug code, including checks on + * malloc'd memory + */ +/*+/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 +#ifndef INC_arCS_h +# include +#endif +#ifndef INC_genDefs_h +# include +#endif +#ifndef INC_tsDefs_h +# include +#endif +#ifndef INCLcadefh +# include +#endif + +#ifdef vxWorks +# include +# include /* for O_RDWR and O_RDONLY definitions */ +# include +#else +# include +# include /* 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; +{ + GenFree((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 *)GenMalloc(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 *)GenMalloc(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 + GenFree(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 *)GenMalloc(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 + GenFree(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 *)GenMalloc(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 + GenFree(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 *)GenMalloc(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 + GenFree(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 */ + +/*---------------------------------------------------------------------------- +* 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; iremainCount >= 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, 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; ichanHdr[i].name[0] = '\0'; + pMIBuf->chanHdr[i].graphics.nextMIFree.blkNum = + ichanHdr[i].graphics.nextMIFree.hdrNum = + ipMIFree = 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; ichanHdr[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; +}