Files
epics-base/modules/database/src/ioc/dbStatic/dbStaticLib.c
Michael Davidsaver 0f428ea334 use DBCORE_API
git ls-files modules/database/src/ioc|egrep '\.[hc]p*$' | xargs sed -i \
 -e 's|epicsShareFunc|DBCORE_API|g' \
 -e 's|epicsShareClass|DBCORE_API|g' \
 -e 's|epicsShareExtern|DBCORE_API extern|g' \
 -e 's|epicsShareDef\s*||g' \
 -e 's|shareLib\.h|dbCoreAPI.h|g' \
 -e 's|epicsShareAPI|epicsStdCall|g' \
 -e '/#define\s*epicsExportSharedSymbols/d'
2021-04-01 10:57:19 -07:00

3636 lines
108 KiB
C

/*************************************************************************\
* Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne
* National Laboratory.
* Copyright (c) 2002 The Regents of the University of California, as
* Operator of Los Alamos National Laboratory.
* SPDX-License-Identifier: EPICS
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
#include <stdio.h>
#include <stddef.h>
#include <errno.h>
#include <limits.h>
#include <string.h>
#include <math.h>
#include <ctype.h>
#include "cantProceed.h"
#include "cvtFast.h"
#include "epicsAssert.h"
#include "dbDefs.h"
#include "dbmf.h"
#include "ellLib.h"
#include "epicsPrint.h"
#include "epicsStdio.h"
#include "epicsStdlib.h"
#include "epicsString.h"
#include "errlog.h"
#include "gpHash.h"
#include "osiFileName.h"
#include "postfix.h"
#define DBFLDTYPES_GBLSOURCE
#define SPECIAL_GBLSOURCE
#include "dbChannel.h"
#include "dbFldTypes.h"
#include "dbStaticLib.h"
#include "dbStaticPvt.h"
#include "devSup.h"
#include "drvSup.h"
#include "link.h"
#include "special.h"
#include "dbCommon.h"
#include "dbJLink.h"
int dbStaticDebug = 0;
static char *pNullString = "";
#define messagesize 276
#define RPCL_LEN INFIX_TO_POSTFIX_SIZE(80)
/* Must be big enough to hold a 64-bit integer in base 10, but in
* the future when fields hold large JSON objects this fixed size
* allocation will probably have to become variable sized.
*/
STATIC_ASSERT(messagesize >= 21);
static char *ppstring[5]={" NPP"," PP"," CA"," CP"," CPP"};
static char *msstring[4]={" NMS"," MS"," MSI"," MSS"};
maplinkType pamaplinkType[LINK_NTYPES] = {
{"CONSTANT",CONSTANT},
{"PV_LINK",PV_LINK},
{"VME_IO",VME_IO},
{"CAMAC_IO",CAMAC_IO},
{"AB_IO",AB_IO},
{"GPIB_IO",GPIB_IO},
{"BITBUS_IO",BITBUS_IO},
{"MACRO_LINK",MACRO_LINK},
{"JSON_LINK",JSON_LINK},
{"PN_LINK",PN_LINK},
{"DB_LINK",DB_LINK},
{"CA_LINK",CA_LINK},
{"INST_IO",INST_IO},
{"BBGPIB_IO",BBGPIB_IO},
{"RF_IO",RF_IO},
{"VXI_IO",VXI_IO}
};
/*forward references for private routines*/
static long dbAddOnePath (DBBASE *pdbbase, const char *path, unsigned length);
/* internal routines*/
static FILE *openOutstream(const char *filename)
{
FILE *stream;
errno = 0;
stream = fopen(filename,"w");
if(!stream) {
fprintf(stderr,"error opening %s %s\n",filename,strerror(errno));
return 0;
}
return stream;
}
static void finishOutstream(FILE *stream)
{
if(stream==stdout) {
fflush(stdout);
} else {
if(fclose(stream)) fprintf(stderr,"fclose error %s\n",strerror(errno));
}
}
void dbFreeLinkContents(struct link *plink)
{
char *parm = NULL;
switch(plink->type) {
case CONSTANT: free((void *)plink->value.constantStr); break;
case MACRO_LINK: free((void *)plink->value.macro_link.macroStr); break;
case PV_LINK: free((void *)plink->value.pv_link.pvname); break;
case JSON_LINK:
dbJLinkFree(plink->value.json.jlink);
parm = plink->value.json.string;
break;
case VME_IO: parm = plink->value.vmeio.parm; break;
case CAMAC_IO: parm = plink->value.camacio.parm; break;
case AB_IO: parm = plink->value.abio.parm; break;
case GPIB_IO: parm = plink->value.gpibio.parm; break;
case BITBUS_IO: parm = plink->value.bitbusio.parm;break;
case INST_IO: parm = plink->value.instio.string; break;
case BBGPIB_IO: parm = plink->value.bbgpibio.parm;break;
case RF_IO: break;
case VXI_IO: parm = plink->value.vxiio.parm; break;
default:
epicsPrintf("dbFreeLink called but link type %d unknown\n", plink->type);
}
if(parm && (parm != pNullString)) free((void *)parm);
if(plink->text) free(plink->text);
plink->lset = NULL;
plink->text = NULL;
memset(&plink->value, 0, sizeof(union value));
}
void dbFreePath(DBBASE *pdbbase)
{
ELLLIST *ppathList;
dbPathNode *pdbPathNode;
if(!pdbbase) return;
ppathList = (ELLLIST *)pdbbase->pathPvt;
if(!ppathList) return;
while((pdbPathNode = (dbPathNode *)ellFirst(ppathList))) {
ellDelete(ppathList,&pdbPathNode->node);
free((void *)pdbPathNode->directory);
free((void *)pdbPathNode);
}
free((void *)ppathList);
pdbbase->pathPvt = 0;
return;
}
static void zeroDbentry(DBENTRY *pdbentry)
{
/*NOTE that pdbbase and message MUST NOT be set to NULL*/
pdbentry->precordType=NULL;
pdbentry->pflddes=NULL;
pdbentry->precnode=NULL;
pdbentry->pfield=NULL;
pdbentry->indfield=0;
}
static char *getpMessage(DBENTRY *pdbentry)
{
char *msg = pdbentry->message;
if (!msg) {
msg = dbCalloc(1, messagesize);
pdbentry->message = msg;
}
else
*msg = '\0';
return msg;
}
static
void dbMsgCpy(DBENTRY *pdbentry, const char *msg)
{
getpMessage(pdbentry);
strncpy(pdbentry->message, msg, messagesize-1);
pdbentry->message[messagesize-1] = '\0';
}
static
void dbMsgNCpy(DBENTRY *pdbentry, const char *msg, size_t len)
{
getpMessage(pdbentry);
if (len >= messagesize)
len = messagesize-1; /* FIXME: Quietly truncates */
strncpy(pdbentry->message, msg, len);
pdbentry->message[len] = '\0';
}
void dbMsgPrint(DBENTRY *pdbentry, const char *fmt, ...)
{
va_list args;
getpMessage(pdbentry);
va_start(args, fmt);
epicsVsnprintf(pdbentry->message, messagesize, fmt, args);
va_end(args);
}
static void ulongToHexString(epicsUInt32 source, char *pdest)
{
static const char hex_digit_to_ascii[16] = "0123456789abcdef";
epicsUInt32 val,temp;
char digit[10];
int i,j;
if (source==0) {
strcpy(pdest,"0x0");
return;
}
*pdest++ = '0'; *pdest++ = 'x';
val = source;
for (i=0; val!=0; i++) {
temp = val/16;
digit[i] = hex_digit_to_ascii[val - temp*16];
val = temp;
}
for (j=i-1; j>=0; j--) {
*pdest++ = digit[j];
}
*pdest = 0;
return;
}
static void realToString(double value, char *preturn, int isdouble)
{
static const double delta[2] = {1e-6, 1e-15};
static const int precision[2] = {6, 14};
double absvalue;
int logval,prec;
size_t end;
char tstr[30];
char *ptstr = &tstr[0];
int round;
int ise = FALSE;
char *loce = NULL;
if (value == 0) {
strcpy(preturn, "0");
return;
}
absvalue = value < 0 ? -value : value;
if (absvalue < (double)INT_MAX) {
epicsInt32 intval = (epicsInt32) value;
double diff = value - intval;
if (diff < 0) diff = -diff;
if (diff < absvalue * delta[isdouble]) {
cvtLongToString(intval, preturn);
return;
}
}
/*Now starts the hard cases*/
if (value < 0) {
*preturn++ = '-';
value = -value;
}
logval = (int)log10(value);
if (logval > 6 || logval < -2) {
int nout;
ise = TRUE;
prec = precision[isdouble];
nout = sprintf(ptstr, "%.*e", prec, value);
loce = strchr(ptstr, 'e');
if (!loce) {
ptstr[nout] = 0;
strcpy(preturn, ptstr);
return;
}
*loce++ = 0;
} else {
prec = precision[isdouble] - logval;
if ( prec < 0) prec = 0;
sprintf(ptstr, "%.*f", prec, value);
}
if (prec > 0) {
end = strlen(ptstr) - 1;
round = FALSE;
while (end > 0) {
if (tstr[end] == '.') {end--; break;}
if (tstr[end] == '0') {end--; continue;}
if (!round && end < precision[isdouble]) break;
if (!round && tstr[end] < '8') break;
if (tstr[end-1] == '.') {
if (round) end = end-2;
break;
}
if (tstr[end-1] != '9') break;
round = TRUE;
end--;
}
tstr[end+1] = 0;
while (round) {
if (tstr[end] < '9') {tstr[end]++; break;}
if (end == 0) { *preturn++ = '1'; tstr[end] = '0'; break;}
tstr[end--] = '0';
}
}
strcpy(preturn, &tstr[0]);
if (ise) {
if (!(strchr(preturn, '.'))) strcat(preturn, ".0");
strcat(preturn, "e");
strcat(preturn, loce);
}
}
static void floatToString(float value, char *preturn)
{
realToString((double)value, preturn, 0);
}
static void doubleToString(double value, char *preturn)
{
realToString(value, preturn, 1);
}
/*Public only for dbStaticNoRun*/
dbDeviceMenu *dbGetDeviceMenu(DBENTRY *pdbentry)
{
dbRecordType *precordType = pdbentry->precordType;
dbFldDes *pflddes = pdbentry->pflddes;
dbDeviceMenu *pdbDeviceMenu;
devSup *pdevSup;
int ind;
int nChoice;
if(!precordType) return(NULL);
if(!pflddes) return(NULL);
if(pflddes->field_type!=DBF_DEVICE) return(NULL);
if(pflddes->ftPvt){
pdbDeviceMenu = (dbDeviceMenu *)pflddes->ftPvt;
if(pdbDeviceMenu->nChoice == ellCount(&precordType->devList))
return(pdbDeviceMenu);
free((void *)pdbDeviceMenu->papChoice);
free((void *)pdbDeviceMenu);
pflddes->ftPvt = NULL;
}
nChoice = ellCount(&precordType->devList);
if(nChoice <= 0) return(NULL);
pdbDeviceMenu = dbCalloc(1,sizeof(dbDeviceMenu));
pdbDeviceMenu->nChoice = nChoice;
pdbDeviceMenu->papChoice = dbCalloc(pdbDeviceMenu->nChoice,sizeof(char *));
pdevSup = (devSup *)ellFirst(&precordType->devList);
ind = 0;
while(pdevSup) {
pdbDeviceMenu->papChoice[ind] = pdevSup->choice;
ind++;
pdevSup = (devSup *)ellNext(&pdevSup->node);
}
pflddes->ftPvt = pdbDeviceMenu;
return(pdbDeviceMenu);
}
/* Beginning of Public Routines */
#define INC_SIZE 256
void dbCatString(char **string,int *stringLength,char *src,char *separator)
{
if((*string==NULL)
|| ((strlen(*string)+strlen(src)+2) > (size_t)*stringLength)) {
char *newString;
size_t size;
size = strlen(src);
if(*string) size += strlen(*string);
/*Make size multiple of INC_SIZE*/
size = ((size + 2 + INC_SIZE)/INC_SIZE) * INC_SIZE;
newString = dbCalloc(size,sizeof(char));
if(*string) {
strcpy(newString,*string);
free((void *)(*string));
}
*string = newString;
}
if(*stringLength>0) {
strcat(*string,separator);
*stringLength += (int) strlen(separator);
}
strcat(*string,src);
*stringLength += (int) strlen(src);
}
dbBase * dbAllocBase(void)
{
dbBase *pdbbase;
pdbbase = dbCalloc(1,sizeof(dbBase));
ellInit(&pdbbase->menuList);
ellInit(&pdbbase->recordTypeList);
ellInit(&pdbbase->drvList);
ellInit(&pdbbase->registrarList);
ellInit(&pdbbase->functionList);
ellInit(&pdbbase->variableList);
ellInit(&pdbbase->bptList);
ellInit(&pdbbase->filterList);
ellInit(&pdbbase->guiGroupList);
gphInitPvt(&pdbbase->pgpHash,256);
dbPvdInitPvt(pdbbase);
return (pdbbase);
}
void dbFreeBase(dbBase *pdbbase)
{
dbMenu *pdbMenu;
dbMenu *pdbMenuNext;
dbRecordType *pdbRecordType;
dbRecordType *pdbRecordTypeNext;
dbFldDes *pdbFldDes;
dbRecordAttribute *pAttribute;
dbRecordAttribute *pAttributeNext;
devSup *pdevSup;
devSup *pdevSupNext;
dbText *ptext;
dbText *ptextNext;
dbVariableDef *pvar;
dbVariableDef *pvarNext;
drvSup *pdrvSup;
drvSup *pdrvSupNext;
linkSup *plinkSup;
brkTable *pbrkTable;
brkTable *pbrkTableNext;
chFilterPlugin *pfilt;
chFilterPlugin *pfiltNext;
dbGuiGroup *pguiGroup;
dbGuiGroup *pguiGroupNext;
int i;
DBENTRY dbentry;
long status;
dbInitEntry(pdbbase,&dbentry);
status = dbFirstRecordType(&dbentry);
while(!status) {
/* dbDeleteRecord() will remove alias or real record node.
* For real record nodes, also removes the nodes of all aliases.
* This complicates safe traversal, so we re-start iteration
* from the first record after each call.
*/
while((status = dbFirstRecord(&dbentry))==0) {
dbDeleteRecord(&dbentry);
}
assert(status==S_dbLib_recNotFound);
status = dbNextRecordType(&dbentry);
}
dbFinishEntry(&dbentry);
pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList);
while(pdbRecordType) {
for(i=0; i<pdbRecordType->no_fields; i++) {
pdbFldDes = pdbRecordType->papFldDes[i];
free((void *)pdbFldDes->prompt);
free((void *)pdbFldDes->name);
free((void *)pdbFldDes->extra);
free((void *)pdbFldDes->initial);
if(pdbFldDes->field_type==DBF_DEVICE && pdbFldDes->ftPvt) {
dbDeviceMenu *pdbDeviceMenu;
pdbDeviceMenu = (dbDeviceMenu *)pdbFldDes->ftPvt;
free((void *)pdbDeviceMenu->papChoice);
free((void *)pdbDeviceMenu);
pdbFldDes->ftPvt=0;
}
free((void *)pdbFldDes);
}
pdevSup = (devSup *)ellFirst(&pdbRecordType->devList);
while(pdevSup) {
pdevSupNext = (devSup *)ellNext(&pdevSup->node);
ellDelete(&pdbRecordType->devList,&pdevSup->node);
free((void *)pdevSup->name);
free((void *)pdevSup->choice);
free((void *)pdevSup);
pdevSup = pdevSupNext;
}
ptext = (dbText *)ellFirst(&pdbRecordType->cdefList);
while(ptext) {
ptextNext = (dbText *)ellNext(&ptext->node);
ellDelete(&pdbRecordType->cdefList,&ptext->node);
free((void *)ptext->text);
free((void *)ptext);
ptext = ptextNext;
}
pAttribute =
(dbRecordAttribute *)ellFirst(&pdbRecordType->attributeList);
while(pAttribute) {
pAttributeNext = (dbRecordAttribute *)ellNext(&pAttribute->node);
ellDelete(&pdbRecordType->attributeList,&pAttribute->node);
free((void *)pAttribute->name);
free((void *)pAttribute->pdbFldDes);
free(pAttribute);
pAttribute = pAttributeNext;
}
pdbRecordTypeNext = (dbRecordType *)ellNext(&pdbRecordType->node);
gphDelete(pdbbase->pgpHash,pdbRecordType->name,&pdbbase->recordTypeList);
ellDelete(&pdbbase->recordTypeList,&pdbRecordType->node);
free((void *)pdbRecordType->name);
free((void *)pdbRecordType->link_ind);
free((void *)pdbRecordType->papsortFldName);
free((void *)pdbRecordType->sortFldInd);
free((void *)pdbRecordType->papFldDes);
free((void *)pdbRecordType);
pdbRecordType = pdbRecordTypeNext;
}
pdbMenu = (dbMenu *)ellFirst(&pdbbase->menuList);
while(pdbMenu) {
pdbMenuNext = (dbMenu *)ellNext(&pdbMenu->node);
gphDelete(pdbbase->pgpHash,pdbMenu->name,&pdbbase->menuList);
ellDelete(&pdbbase->menuList,&pdbMenu->node);
for(i=0; i< pdbMenu->nChoice; i++) {
free((void *)pdbMenu->papChoiceName[i]);
free((void *)pdbMenu->papChoiceValue[i]);
}
free((void *)pdbMenu->papChoiceName);
free((void *)pdbMenu->papChoiceValue);
free((void *)pdbMenu ->name);
free((void *)pdbMenu);
pdbMenu = pdbMenuNext;
}
pdrvSup = (drvSup *)ellFirst(&pdbbase->drvList);
while(pdrvSup) {
pdrvSupNext = (drvSup *)ellNext(&pdrvSup->node);
ellDelete(&pdbbase->drvList,&pdrvSup->node);
free((void *)pdrvSup->name);
free((void *)pdrvSup);
pdrvSup = pdrvSupNext;
}
while ((plinkSup = (linkSup *) ellGet(&pdbbase->linkList))) {
free(plinkSup->jlif_name);
free(plinkSup->name);
free(plinkSup);
}
ptext = (dbText *)ellFirst(&pdbbase->registrarList);
while(ptext) {
ptextNext = (dbText *)ellNext(&ptext->node);
ellDelete(&pdbbase->registrarList,&ptext->node);
free((void *)ptext->text);
free((void *)ptext);
ptext = ptextNext;
}
ptext = (dbText *)ellFirst(&pdbbase->functionList);
while(ptext) {
ptextNext = (dbText *)ellNext(&ptext->node);
ellDelete(&pdbbase->functionList,&ptext->node);
free((void *)ptext->text);
free((void *)ptext);
ptext = ptextNext;
}
pvar = (dbVariableDef *)ellFirst(&pdbbase->variableList);
while(pvar) {
pvarNext = (dbVariableDef *)ellNext(&pvar->node);
ellDelete(&pdbbase->variableList,&pvar->node);
free((void *)pvar->name);
free((void *)pvar->type);
free((void *)pvar);
pvar = pvarNext;
}
pbrkTable = (brkTable *)ellFirst(&pdbbase->bptList);
while(pbrkTable) {
pbrkTableNext = (brkTable *)ellNext(&pbrkTable->node);
gphDelete(pdbbase->pgpHash,pbrkTable->name,&pdbbase->bptList);
ellDelete(&pdbbase->bptList,&pbrkTable->node);
free(pbrkTable->name);
free((void *)pbrkTable->paBrkInt);
free((void *)pbrkTable);
pbrkTable = pbrkTableNext;
}
pfilt = (chFilterPlugin *)ellFirst(&pdbbase->filterList);
while(pfilt) {
pfiltNext = (chFilterPlugin *)ellNext(&pfilt->node);
free((char*)pfilt->name);
if(pfilt->fif->priv_free)
(*pfilt->fif->priv_free)(pfilt->puser);
free(pfilt);
pfilt = pfiltNext;
}
pguiGroup = (dbGuiGroup *)ellFirst(&pdbbase->guiGroupList);
while (pguiGroup) {
pguiGroupNext = (dbGuiGroup *)ellNext(&pguiGroup->node);
gphDelete(pdbbase->pgpHash, pguiGroup->name, &pdbbase->guiGroupList);
ellDelete(&pdbbase->guiGroupList, &pguiGroup->node);
free(pguiGroup->name);
free((void *)pguiGroup);
pguiGroup = pguiGroupNext;
}
gphFreeMem(pdbbase->pgpHash);
dbPvdFreeMem(pdbbase);
dbFreePath(pdbbase);
free((void *)pdbbase);
pdbbase = NULL;
return;
}
DBENTRY * dbAllocEntry(dbBase *pdbbase)
{
DBENTRY *pdbentry;
pdbentry = dbmfMalloc(sizeof(DBENTRY));
memset(pdbentry,'\0',sizeof(DBENTRY));
pdbentry->pdbbase = pdbbase;
return(pdbentry);
}
void dbFreeEntry(DBENTRY *pdbentry)
{
if (!pdbentry)
return;
if (pdbentry->message)
free((void *)pdbentry->message);
dbmfFree(pdbentry);
}
void dbInitEntry(dbBase *pdbbase,DBENTRY *pdbentry)
{
memset((char *)pdbentry,'\0',sizeof(DBENTRY));
pdbentry->pdbbase = pdbbase;
}
void dbFinishEntry(DBENTRY *pdbentry)
{
if(pdbentry->message) {
free((void *)pdbentry->message);
pdbentry->message = NULL;
}
}
DBENTRY * dbCopyEntry(DBENTRY *pdbentry)
{
DBENTRY *pnew;
pnew = dbAllocEntry(pdbentry->pdbbase);
*pnew = *pdbentry;
pnew->message = NULL;
return(pnew);
}
void dbCopyEntryContents(DBENTRY *pfrom,DBENTRY *pto)
{
*pto = *pfrom;
pto->message = NULL;
}
long dbPath(DBBASE *pdbbase,const char *path)
{
if(!pdbbase) return(-1);
dbFreePath(pdbbase);
if(!path || strlen(path)==0) return(dbAddPath(pdbbase,"."));
return(dbAddPath(pdbbase,path));
}
long dbAddPath(DBBASE *pdbbase,const char *path)
{
ELLLIST *ppathList;
const char *pcolon;
const char *plast;
unsigned expectingPath;
unsigned sawMissingPath;
if(!pdbbase) return(-1);
ppathList = (ELLLIST *)pdbbase->pathPvt;
if(!ppathList) {
ppathList = dbCalloc(1,sizeof(ELLLIST));
ellInit(ppathList);
pdbbase->pathPvt = (void *)ppathList;
}
if (!path) return(0); /* Empty path strings are ignored */
/* care is taken to properly deal with white space
* 1) preceding and trailing white space is removed from paths
* 2) white space inbetween path separator counts as an empty name
* (see below)
*/
expectingPath = FALSE;
sawMissingPath = FALSE;
while (*path) {
size_t len;
/* preceding white space is removed */
if (isspace((int)*path)) {
path++;
continue;
}
pcolon = strstr (path, OSI_PATH_LIST_SEPARATOR);
if (pcolon==path) {
sawMissingPath = TRUE;
path += strlen (OSI_PATH_LIST_SEPARATOR);
continue;
}
if (pcolon) {
plast = pcolon - 1;
expectingPath = TRUE;
} else {
plast = strlen (path) + path - 1;
expectingPath = FALSE;
}
/* trailing white space is removed */
while (isspace((int)*plast)) {
plast--;
}
/*
* len is always nonzero because we found something that
* 1) isnt white space
* 2) isnt a path separator
*/
len = (plast - path) + 1;
if (dbAddOnePath (pdbbase, path, (unsigned) len)) return (-1);
path += len;
if (pcolon) {
path += strlen(OSI_PATH_LIST_SEPARATOR);
}
}
/*
* an empty name at beginning, middle, or end of a path string that isnt
* empty means current directory
*/
if (expectingPath||sawMissingPath) {
return dbAddOnePath (pdbbase, ".", 1);
}
return(0);
}
static long dbAddOnePath (DBBASE *pdbbase, const char *path, unsigned length)
{
ELLLIST *ppathList;
dbPathNode *pdbPathNode;
if(!pdbbase) return(-1);
ppathList = (ELLLIST *)pdbbase->pathPvt;
pdbPathNode = (dbPathNode *)dbCalloc(1, sizeof(dbPathNode));
pdbPathNode->directory = (char *)dbCalloc(length+1, sizeof(char));
strncpy(pdbPathNode->directory, path, length);
pdbPathNode->directory[length] = '\0';
ellAdd(ppathList, &pdbPathNode->node);
return 0;
}
char *dbGetPromptGroupNameFromKey(DBBASE *pdbbase, const short key)
{
dbGuiGroup *pdbGuiGroup;
if (!pdbbase) return NULL;
for (pdbGuiGroup = (dbGuiGroup *)ellFirst(&pdbbase->guiGroupList);
pdbGuiGroup; pdbGuiGroup = (dbGuiGroup *)ellNext(&pdbGuiGroup->node)) {
if (pdbGuiGroup->key == key) return pdbGuiGroup->name;
}
return NULL;
}
short dbGetPromptGroupKeyFromName(DBBASE *pdbbase, const char *name)
{
GPHENTRY *pgphentry;
if (!pdbbase) return 0;
pgphentry = gphFind(pdbbase->pgpHash, name, &pdbbase->guiGroupList);
if (!pgphentry) {
return 0;
} else {
return ((dbGuiGroup*)pgphentry->userPvt)->key;
}
}
long dbWriteRecord(DBBASE *ppdbbase,const char *filename,
const char *precordTypename,int level)
{
FILE *stream;
long status;
stream = openOutstream(filename);
if(!stream) return -1;
status = dbWriteRecordFP(ppdbbase,stream,precordTypename,level);
finishOutstream(stream);
return status;
}
long dbWriteRecordFP(
DBBASE *pdbbase,FILE *fp,const char *precordTypename,int level)
{
DBENTRY dbentry;
DBENTRY *pdbentry=&dbentry;
long status;
int dctonly;
dctonly = ((level>1) ? FALSE : TRUE);
dbInitEntry(pdbbase,pdbentry);
if (precordTypename) {
if (*precordTypename == 0 || *precordTypename == '*')
precordTypename = 0;
}
if(!precordTypename) {
status = dbFirstRecordType(pdbentry);
if(status) {
/* No record descriptions, so no record instances */
dbFinishEntry(pdbentry);
return(0);
}
} else {
status = dbFindRecordType(pdbentry,precordTypename);
if(status) {
fprintf(stderr,"dbWriteRecordFP: No record description for %s\n",
precordTypename);
dbFinishEntry(pdbentry);
return(status);
}
}
while(!status) {
status = dbFirstRecord(pdbentry);
while(!status) {
if (dbIsAlias(pdbentry)) {
status = dbNextRecord(pdbentry);
continue;
}
if(dbIsVisibleRecord(pdbentry))
fprintf(fp,"grecord(%s,\"%s\") {\n",
dbGetRecordTypeName(pdbentry),dbGetRecordName(pdbentry));
else
fprintf(fp,"record(%s,\"%s\") {\n",
dbGetRecordTypeName(pdbentry),dbGetRecordName(pdbentry));
status = dbFirstField(pdbentry,dctonly);
while(!status) {
if (!dbIsDefaultValue(pdbentry) || level>0) {
char *pvalstring = dbGetString(pdbentry);
if (!pvalstring) {
fprintf(fp,"\tfield(%s,\"\")\n",
dbGetFieldName(pdbentry));
} else {
fprintf(fp,"\tfield(%s,\"",
dbGetFieldName(pdbentry));
epicsStrPrintEscaped(fp,pvalstring,strlen(pvalstring));
fprintf(fp,"\")\n");
}
} else if(level>0) { /*generate 0 length string*/
fprintf(fp,"\tfield(%s,\"\")\n",dbGetFieldName(pdbentry));
}
status=dbNextField(pdbentry,dctonly);
}
status = dbFirstInfo(pdbentry);
while (!status) {
const char *pinfostr = dbGetInfoString(pdbentry);
fprintf(fp, "\tinfo(\"%s\",\"",
dbGetInfoName(pdbentry));
epicsStrPrintEscaped(fp, pinfostr, strlen(pinfostr));
fprintf(fp, "\")\n");
status = dbNextInfo(pdbentry);
}
fprintf(fp,"}\n");
status = dbNextRecord(pdbentry);
}
status = dbFirstRecord(pdbentry);
while (!status) {
if (!dbIsAlias(pdbentry)) {
status = dbNextRecord(pdbentry);
continue;
}
fprintf(fp, "alias(\"%s\",\"%s\")\n",
dbRecordName(pdbentry), dbGetRecordName(pdbentry));
status = dbNextRecord(pdbentry);
}
if(precordTypename) break;
status = dbNextRecordType(pdbentry);
}
dbFinishEntry(pdbentry);
return(0);
}
long dbWriteMenu(
DBBASE *ppdbbase,const char *filename,const char *menuName)
{
FILE *stream;
long status;
stream = openOutstream(filename);
status = dbWriteMenuFP(ppdbbase,stream,menuName);
finishOutstream(stream);
return status;
}
long dbWriteMenuFP(DBBASE *pdbbase,FILE *fp,const char *menuName)
{
dbMenu *pdbMenu;
int gotMatch;
int i;
if(!pdbbase) {
fprintf(stderr,"pdbbase not specified\n");
return(-1);
}
if (menuName) {
if (*menuName == 0 || *menuName == '*')
menuName = 0;
}
pdbMenu = (dbMenu *)ellFirst(&pdbbase->menuList);
while(pdbMenu) {
if(menuName) {
gotMatch = (strcmp(menuName,pdbMenu->name)==0) ? TRUE : FALSE;
}else {
gotMatch=TRUE;
}
if(gotMatch) {
fprintf(fp,"menu(%s) {\n",pdbMenu->name);
for(i=0; i<pdbMenu->nChoice; i++) {
fprintf(fp,"\tchoice(%s,\"%s\")\n",pdbMenu->papChoiceName[i],
pdbMenu->papChoiceValue[i]);
}
fprintf(fp,"}\n");
if(menuName) break;
}
pdbMenu = (dbMenu *)ellNext(&pdbMenu->node);
}
return(0);
}
long dbWriteRecordType(
DBBASE *pdbbase,const char *filename,const char *recordTypeName)
{
FILE *stream;
long status;
stream = openOutstream(filename);
status = dbWriteRecordTypeFP(pdbbase,stream,recordTypeName);
finishOutstream(stream);
return status;
}
long dbWriteRecordTypeFP(
DBBASE *pdbbase,FILE *fp,const char *recordTypeName)
{
dbRecordType *pdbRecordType;
dbFldDes *pdbFldDes;
int gotMatch;
int i;
if(!pdbbase) {
fprintf(stderr,"pdbbase not specified\n");
return(-1);
}
if (recordTypeName) {
if (*recordTypeName == 0 || *recordTypeName == '*')
recordTypeName = 0;
}
for(pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList);
pdbRecordType; pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node)) {
if(recordTypeName) {
gotMatch = (strcmp(recordTypeName,pdbRecordType->name)==0)
? TRUE : FALSE;
}else {
gotMatch=TRUE;
}
if(!gotMatch) continue;
fprintf(fp,"recordtype(%s) {\n",pdbRecordType->name);
for(i=0; i<pdbRecordType->no_fields; i++) {
int j;
pdbFldDes = pdbRecordType->papFldDes[i];
fprintf(fp,"\tfield(%s,%s) {\n",pdbFldDes->name,
dbGetFieldTypeString(pdbFldDes->field_type));
if(pdbFldDes->prompt)
fprintf(fp,"\t\tprompt(\"%s\")\n",pdbFldDes->prompt);
if(pdbFldDes->initial)
fprintf(fp,"\t\tinitial(\"%s\")\n",pdbFldDes->initial);
if (pdbFldDes->promptgroup) {
fprintf(fp,"\t\tpromptgroup(\"%s\")\n",
dbGetPromptGroupNameFromKey(pdbbase, pdbFldDes->promptgroup));
}
if(pdbFldDes->special) {
if(pdbFldDes->special >= SPC_NTYPES) {
fprintf(fp,"\t\tspecial(%d)\n",pdbFldDes->special);
} else for(j=0; j<SPC_NTYPES; j++) {
if(pamapspcType[j].value == pdbFldDes->special) {
fprintf(fp,"\t\tspecial(%s)\n",
pamapspcType[j].strvalue);
break;
}
}
}
if(pdbFldDes->extra)
fprintf(fp,"\t\textra(\"%s\")\n",pdbFldDes->extra);
if(pdbFldDes->field_type==DBF_MENU) {
if(pdbFldDes->ftPvt)
fprintf(fp,"\t\tmenu(%s)\n",
((dbMenu *)pdbFldDes->ftPvt)->name);
else
fprintf(stderr,"\t\t menu: NOT FOUND\n");
}
if(pdbFldDes->field_type==DBF_STRING) {
fprintf(fp,"\t\tsize(%d)\n",
pdbFldDes->size);
}
if(pdbFldDes->process_passive) fprintf(fp,"\t\tpp(TRUE)\n");
if(pdbFldDes->prop) fprintf(fp,"\t\tprop(YES)\n");
if(pdbFldDes->base) fprintf(fp,"\t\tbase(HEX)\n");
if(pdbFldDes->interest)
fprintf(fp,"\t\tinterest(%d)\n",pdbFldDes->interest);
if(!pdbFldDes->as_level) fprintf(fp,"\t\tasl(ASL0)\n");
fprintf(fp,"\t}\n");
}
fprintf(fp,"}\n");
if(recordTypeName) break;
}
return(0);
}
long dbWriteDevice(DBBASE *pdbbase,const char *filename)
{
FILE *stream;
long status;
stream = openOutstream(filename);
status = dbWriteDeviceFP(pdbbase,stream);
finishOutstream(stream);
return status;
}
long dbWriteDeviceFP(DBBASE *pdbbase,FILE *fp)
{
dbRecordType *pdbRecordType;
devSup *pdevSup;
if(!pdbbase) {
fprintf(stderr,"dbWriteDeviceFP: pdbbase not specified\n");
return(-1);
}
for(pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList);
pdbRecordType; pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node)) {
for(pdevSup = (devSup *)ellFirst(&pdbRecordType->devList);
pdevSup; pdevSup = (devSup *)ellNext(&pdevSup->node)) {
int j;
for(j=0; j< LINK_NTYPES; j++) {
if(pamaplinkType[j].value==pdevSup->link_type) break;
}
if(j>=LINK_NTYPES) {
fprintf(fp,"link_type not valid\n");
continue;
}
fprintf(fp,"device(%s,%s,%s,\"%s\")\n",
pdbRecordType->name,
pamaplinkType[j].strvalue,
pdevSup->name,pdevSup->choice);
}
}
return(0);
}
long dbWriteDriver(DBBASE *pdbbase,const char *filename)
{
FILE *stream;
long status;
stream = openOutstream(filename);
status = dbWriteDriverFP(pdbbase,stream);
finishOutstream(stream);
return status;
}
long dbWriteDriverFP(DBBASE *pdbbase,FILE *fp)
{
drvSup *pdrvSup;
if(!pdbbase) {
fprintf(stderr,"pdbbase not specified\n");
return(-1);
}
for(pdrvSup = (drvSup *)ellFirst(&pdbbase->drvList);
pdrvSup; pdrvSup = (drvSup *)ellNext(&pdrvSup->node)) {
fprintf(fp,"driver(%s)\n",pdrvSup->name);
}
return(0);
}
long dbWriteLinkFP(DBBASE *pdbbase, FILE *fp)
{
linkSup *plinkSup;
if (!pdbbase) {
fprintf(stderr, "pdbbase not specified\n");
return -1;
}
for (plinkSup = (linkSup *) ellFirst(&pdbbase->linkList);
plinkSup; plinkSup = (linkSup *) ellNext(&plinkSup->node)) {
fprintf(fp, "link(%s,%s)\n", plinkSup->name, plinkSup->jlif_name);
}
return 0;
}
long dbWriteRegistrarFP(DBBASE *pdbbase,FILE *fp)
{
dbText *ptext;
if(!pdbbase) {
fprintf(stderr,"pdbbase not specified\n");
return(-1);
}
for(ptext = (dbText *)ellFirst(&pdbbase->registrarList);
ptext; ptext = (dbText *)ellNext(&ptext->node)) {
fprintf(fp,"registrar(%s)\n",ptext->text);
}
return(0);
}
long dbWriteFunctionFP(DBBASE *pdbbase,FILE *fp)
{
dbText *ptext;
if(!pdbbase) {
fprintf(stderr,"pdbbase not specified\n");
return(-1);
}
for(ptext = (dbText *)ellFirst(&pdbbase->functionList);
ptext; ptext = (dbText *)ellNext(&ptext->node)) {
fprintf(fp,"function(%s)\n",ptext->text);
}
return(0);
}
long dbWriteVariableFP(DBBASE *pdbbase,FILE *fp)
{
dbVariableDef *pvar;
if(!pdbbase) {
fprintf(stderr,"pdbbase not specified\n");
return(-1);
}
for(pvar = (dbVariableDef *)ellFirst(&pdbbase->variableList);
pvar; pvar = (dbVariableDef *)ellNext(&pvar->node)) {
fprintf(fp,"variable(%s,%s)\n",pvar->name,pvar->type);
}
return(0);
}
long dbWriteBreaktable(DBBASE *pdbbase,const char *filename)
{
FILE *stream;
long status;
stream = openOutstream(filename);
status = dbWriteBreaktableFP(pdbbase,stream);
finishOutstream(stream);
return status;
}
long dbWriteBreaktableFP(DBBASE *pdbbase,FILE *fp)
{
brkTable *pbrkTable;
brkInt *pbrkInt;
int i;
if (!pdbbase) {
fprintf(stderr,"pdbbase not specified\n");
return(-1);
}
for (pbrkTable = (brkTable *)ellFirst(&pdbbase->bptList);
pbrkTable;
pbrkTable = (brkTable *)ellNext(&pbrkTable->node)) {
fprintf(fp,"breaktable(%s) {\n",pbrkTable->name);
pbrkInt = pbrkTable->paBrkInt;
for(i=0; i < pbrkTable->number; i++) {
fprintf(fp,"\t%e, %e\n",pbrkInt->raw,pbrkInt->eng);
pbrkInt++;
}
fprintf(fp,"}\n");
}
return(0);
}
long dbFindRecordType(DBENTRY *pdbentry,const char *recordType)
{
dbBase *pdbbase = pdbentry->pdbbase;
GPHENTRY *phash;
zeroDbentry(pdbentry);
phash = gphFind(pdbbase->pgpHash,recordType,&pdbbase->recordTypeList);
if(!phash) return(S_dbLib_recordTypeNotFound);
pdbentry->precordType = phash->userPvt;
return(0);
}
long dbFirstRecordType(DBENTRY *pdbentry)
{
dbRecordType *precordType;
zeroDbentry(pdbentry);
precordType = (dbRecordType *)ellFirst(&pdbentry->pdbbase->recordTypeList);
if(!precordType) return(S_dbLib_recordTypeNotFound);
pdbentry->precordType = precordType;
return(0);
}
long dbNextRecordType(DBENTRY *pdbentry)
{
dbRecordType *precordType = pdbentry->precordType;
zeroDbentry(pdbentry);
precordType = (dbRecordType *)ellNext(&precordType->node);
if(!precordType) return(S_dbLib_recordTypeNotFound);
pdbentry->precordType = precordType;
return(0);
}
char * dbGetRecordTypeName(DBENTRY *pdbentry)
{
return(pdbentry->precordType->name);
}
int dbGetNRecordTypes(DBENTRY *pdbentry)
{
return(ellCount(&pdbentry->pdbbase->recordTypeList));
}
long dbPutRecordAttribute(
DBENTRY *pdbentry, const char *name, const char*value)
{
dbRecordType *precordType = pdbentry->precordType;
int createNew = TRUE;
int compare;
dbRecordAttribute *pattribute;
if(!precordType) return(S_dbLib_recordTypeNotFound);
pattribute = (dbRecordAttribute *)ellFirst(&precordType->attributeList);
/*put new attribute name in sort order*/
while(pattribute) {
compare = strcmp(pattribute->name,name);
if(compare==0) {
createNew = FALSE;
}
if(compare>=0) break;
pattribute = (dbRecordAttribute *)ellNext(&pattribute->node);
}
if(createNew) {
dbRecordAttribute *pnew;
dbFldDes *pdbFldDes;
pnew = dbCalloc(1,sizeof(dbRecordAttribute));
if(pattribute) {
ellInsert(&precordType->attributeList,pattribute->node.previous,
&pnew->node);
} else {
ellAdd(&precordType->attributeList,&pnew->node);
}
pattribute = pnew;
pattribute->name = dbCalloc(strlen(name)+1,sizeof(char));
strcpy(pattribute->name,name);
pdbFldDes = dbCalloc(1,sizeof(dbFldDes));
pdbFldDes->name = pattribute->name;
pdbFldDes->pdbRecordType = precordType;
pdbFldDes->special = SPC_ATTRIBUTE;
pdbFldDes->field_type = DBF_STRING;
pdbFldDes->as_level = ASL1;
pdbFldDes->size = MAX_STRING_SIZE;
pattribute->pdbFldDes = pdbFldDes;
}
strncpy(pattribute->value,value,MAX_STRING_SIZE);
pattribute->value[MAX_STRING_SIZE-1] = 0;
return(0);
}
long dbGetAttributePart(DBENTRY *pdbentry, const char **ppname)
{
dbRecordType *precordType = pdbentry->precordType;
const char *pname = *ppname;
dbRecordAttribute *pattribute;
if (!precordType)
return S_dbLib_recordTypeNotFound;
pattribute = (dbRecordAttribute *)ellFirst(&precordType->attributeList);
while (pattribute) {
size_t nameLen = strlen(pattribute->name);
int compare = strncmp(pattribute->name, pname, nameLen);
if (compare == 0) {
int ch = pname[nameLen];
if (ch != '_' && !isalnum(ch)) {
/* Any other character can't be in the attribute name */
pdbentry->pflddes = pattribute->pdbFldDes;
pdbentry->pfield = pattribute->value;
*ppname = &pname[nameLen];
return 0;
}
if (strlen(pname) > nameLen) {
compare = -1;
}
}
if (compare >= 0) break;
pattribute = (dbRecordAttribute *)ellNext(&pattribute->node);
}
return S_dbLib_fieldNotFound;
}
long dbGetRecordAttribute(DBENTRY *pdbentry, const char *pname)
{
return dbGetAttributePart(pdbentry, &pname);
}
long dbFirstField(DBENTRY *pdbentry,int dctonly)
{
pdbentry->indfield = -1;
return(dbNextField(pdbentry,dctonly));
}
long dbNextField(DBENTRY *pdbentry,int dctonly)
{
dbRecordType *precordType = pdbentry->precordType;
dbRecordNode *precnode = pdbentry->precnode;
dbFldDes *pflddes;
short indfield = pdbentry->indfield;
if(!precordType) return(S_dbLib_recordTypeNotFound);
indfield++;
while(TRUE) {
if(indfield>=precordType->no_fields) {
pdbentry->indfield = 0;
pdbentry->pflddes = NULL;
pdbentry->pfield = NULL;
return(S_dbLib_fieldNotFound);
}
pflddes = precordType->papFldDes[indfield];
if(!dctonly || pflddes->promptgroup) {
/*Skip field if dctonly and no device support*/
if(!dctonly || (pflddes->field_type!=DBF_DEVICE)
|| (ellCount(&precordType->devList)>0)) {
pdbentry->indfield = indfield;
pdbentry->pflddes = pflddes;
pdbentry->indfield = indfield;
if(precnode) {
dbGetFieldAddress(pdbentry);
}else {
pdbentry->pfield = NULL;
}
return(0);
}
}
indfield++;
}
}
int dbGetNFields(DBENTRY *pdbentry,int dctonly)
{
dbRecordType *precordType = pdbentry->precordType;
dbFldDes *pflddes;
int indfield,n;
if(!precordType) return(S_dbLib_recordTypeNotFound);
n = 0;
for(indfield=0; indfield<precordType->no_fields; indfield++) {
pflddes = precordType->papFldDes[indfield];
if(dctonly && (pflddes->field_type==DBF_DEVICE)
&& (ellCount(&precordType->devList)==0) ) continue;
if(!dctonly || pflddes->promptgroup) n++;
}
return(n);
}
char * dbGetFieldName(DBENTRY *pdbentry)
{
dbFldDes *pflddes = pdbentry->pflddes;
if(!pflddes) return(NULL);
return(pflddes->name);
}
int dbGetFieldDbfType(DBENTRY *pdbentry)
{
dbFldDes *pflddes = pdbentry->pflddes;
if(!pflddes) return(-1);
return(pflddes->field_type);
}
char * dbGetDefault(DBENTRY *pdbentry)
{
dbFldDes *pflddes = pdbentry->pflddes;
if(!pflddes) return(NULL);
return(pflddes->initial);
}
char * dbGetPrompt(DBENTRY *pdbentry)
{
dbFldDes *pflddes = pdbentry->pflddes;
if(!pflddes) return(NULL);
return(&pflddes->prompt[0]);
}
int dbGetPromptGroup(DBENTRY *pdbentry)
{
dbFldDes *pflddes = pdbentry->pflddes;
if(!pflddes) return(0);
return(pflddes->promptgroup);
}
long dbCreateRecord(DBENTRY *pdbentry,const char *precordName)
{
dbRecordType *precordType = pdbentry->precordType;
dbFldDes *pdbFldDes;
PVDENTRY *ppvd;
ELLLIST *preclist = NULL;
dbRecordNode *pNewRecNode = NULL;
long status = 0;
if(!precordType) return(S_dbLib_recordTypeNotFound);
/*Get size of NAME field*/
pdbFldDes = precordType->papFldDes[0];
if(!pdbFldDes || (strcmp(pdbFldDes->name,"NAME")!=0))
return(S_dbLib_nameLength);
if((int)strlen(precordName)>=pdbFldDes->size) return(S_dbLib_nameLength);
/* clear callers entry */
zeroDbentry(pdbentry);
if(!dbFindRecord(pdbentry,precordName)) return (S_dbLib_recExists);
zeroDbentry(pdbentry);
pdbentry->precordType = precordType;
preclist = &precordType->recList;
/* create a recNode */
pNewRecNode = dbCalloc(1,sizeof(dbRecordNode));
/* create a new record of this record type */
pdbentry->precnode = pNewRecNode;
if((status = dbAllocRecord(pdbentry,precordName))) return(status);
pNewRecNode->recordname = dbRecordName(pdbentry);
ellInit(&pNewRecNode->infoList);
ellAdd(preclist, &pNewRecNode->node);
pdbentry->precnode = pNewRecNode;
ppvd = dbPvdAdd(pdbentry->pdbbase,precordType,pNewRecNode);
if(!ppvd) {errMessage(-1,"Logic Err: Could not add to PVD");return(-1);}
return(0);
}
long dbDeleteAliases(DBENTRY *pdbentry)
{
dbBase *pdbbase = pdbentry->pdbbase;
dbRecordType *precordType = pdbentry->precordType;
dbRecordNode *precnode = pdbentry->precnode;
ELLLIST *preclist = &precordType->recList;
dbRecordNode *pAliasNode, *pAliasNodeNext;
DBENTRY dbentry;
void *precord;
if (!precnode) return S_dbLib_recNotFound;
if (precnode->flags & DBRN_FLAGS_ISALIAS) return S_dbLib_recExists;
precord = precnode->precord;
dbInitEntry(pdbbase, &dbentry);
pAliasNode = (dbRecordNode *)ellFirst(preclist);
while (pAliasNode) {
pAliasNodeNext = (dbRecordNode *)ellNext(&pAliasNode->node);
if (pAliasNode->flags & DBRN_FLAGS_ISALIAS &&
pAliasNode->precord == precord &&
!dbFindRecord(&dbentry, pAliasNode->recordname)) {
dbDeleteRecord(&dbentry);
}
pAliasNode = pAliasNodeNext;
}
precnode->flags &= ~DBRN_FLAGS_HASALIAS;
return 0;
}
long dbDeleteRecord(DBENTRY *pdbentry)
{
dbBase *pdbbase = pdbentry->pdbbase;
dbRecordType *precordType = pdbentry->precordType;
dbRecordNode *precnode = pdbentry->precnode;
ELLLIST *preclist;
long status;
if (!precnode) return S_dbLib_recNotFound;
if (precnode->flags & DBRN_FLAGS_HASALIAS)
dbDeleteAliases(pdbentry);
preclist = &precordType->recList;
ellDelete(preclist, &precnode->node);
dbPvdDelete(pdbbase, precnode);
while (!dbFirstInfo(pdbentry)) {
dbDeleteInfo(pdbentry);
}
if (precnode->flags & DBRN_FLAGS_ISALIAS) {
free(precnode->recordname);
precordType->no_aliases--;
} else {
status = dbFreeRecord(pdbentry);
if (status) return status;
}
free(precnode);
pdbentry->precnode = NULL;
return 0;
}
long dbFreeRecords(DBBASE *pdbbase)
{
DBENTRY dbentry;
dbRecordType *pdbRecordType;
dbRecordNode *pdbRecordNode;
dbRecordNode *pdbRecordNodeNext;
dbInitEntry(pdbbase,&dbentry);
pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList);
while(pdbRecordType) {
pdbRecordNode = (dbRecordNode *)ellFirst(&pdbRecordType->recList);
while(pdbRecordNode) {
pdbRecordNodeNext = (dbRecordNode *)ellNext(&pdbRecordNode->node);
if(!dbFindRecord(&dbentry,pdbRecordNode->recordname))
dbDeleteRecord(&dbentry);
pdbRecordNode = pdbRecordNodeNext;
}
pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node);
}
dbFinishEntry(&dbentry);
return(0);
}
long dbFindRecordPart(DBENTRY *pdbentry, const char **ppname)
{
dbBase *pdbbase = pdbentry->pdbbase;
const char *pname = *ppname;
const char *pfn;
size_t lenName;
PVDENTRY *ppvdNode;
zeroDbentry(pdbentry);
pfn = strchr(pname, '.');
if (pfn) {
lenName = (size_t) (pfn - pname);
} else {
lenName = strlen(pname);
}
ppvdNode = dbPvdFind(pdbbase, pname, lenName);
if (!ppvdNode)
return S_dbLib_recNotFound;
pdbentry->precnode = ppvdNode->precnode;
pdbentry->precordType = ppvdNode->precordType;
*ppname = pname + lenName;
return 0;
}
long dbFindRecord(DBENTRY *pdbentry, const char *pname)
{
long status = dbFindRecordPart(pdbentry, &pname);
if (status) return status;
if (*pname == '.')
return dbFindField(pdbentry, ++pname);
return 0;
}
long dbFirstRecord(DBENTRY *pdbentry)
{
dbRecordType *precordType = pdbentry->precordType;
dbRecordNode *precnode;
zeroDbentry(pdbentry);
if(!precordType) return(S_dbLib_recordTypeNotFound);
pdbentry->precordType = precordType;
precnode = (dbRecordNode *)ellFirst(&precordType->recList);
if(!precnode) return(S_dbLib_recNotFound);
pdbentry->precnode = precnode;
return(0);
}
long dbNextRecord(DBENTRY *pdbentry)
{
dbRecordNode *precnode=pdbentry->precnode;
long status=0;
if(!precnode) return(S_dbLib_recNotFound);
precnode = (dbRecordNode *)ellNext(&precnode->node);
if(!precnode) status = S_dbLib_recNotFound;
pdbentry->precnode = precnode;
pdbentry->pfield = NULL;
return(status);
}
int dbGetNRecords(DBENTRY *pdbentry)
{
dbRecordType *precordType = pdbentry->precordType;
if(!precordType) return(0);
return(ellCount(&precordType->recList));
}
int dbGetNAliases(DBENTRY *pdbentry)
{
dbRecordType *precordType = pdbentry->precordType;
if(!precordType) return(0);
return(precordType->no_aliases);
}
char * dbGetRecordName(DBENTRY *pdbentry)
{
dbRecordType *pdbRecordType = pdbentry->precordType;
dbRecordNode *precnode = pdbentry->precnode;
if(!pdbRecordType) return NULL;
if(!precnode) return NULL;
return precnode->recordname;
}
long dbVisibleRecord(DBENTRY *pdbentry)
{
dbRecordNode *precnode = pdbentry->precnode;
if(!precnode) return(S_dbLib_recNotFound);
precnode->flags |= DBRN_FLAGS_VISIBLE;
return 0;
}
long dbInvisibleRecord(DBENTRY *pdbentry)
{
dbRecordNode *precnode = pdbentry->precnode;
if(!precnode) return(S_dbLib_recNotFound);
precnode->flags &= ~DBRN_FLAGS_VISIBLE;
return 0;
}
int dbIsVisibleRecord(DBENTRY *pdbentry)
{
dbRecordNode *precnode = pdbentry->precnode;
if(!precnode) return 0;
return precnode->flags & DBRN_FLAGS_VISIBLE ? 1 : 0;
}
long dbCreateAlias(DBENTRY *pdbentry, const char *alias)
{
dbRecordType *precordType = pdbentry->precordType;
dbRecordNode *precnode = pdbentry->precnode;
dbRecordNode *pnewnode;
DBENTRY tempEntry;
PVDENTRY *ppvd;
if (!precordType)
return S_dbLib_recordTypeNotFound;
/* alias of alias still references actual record */
while (precnode && (precnode->flags & DBRN_FLAGS_ISALIAS))
precnode = precnode->aliasedRecnode;
if (!precnode)
return S_dbLib_recNotFound;
dbInitEntry(pdbentry->pdbbase, &tempEntry);
if (!dbFindRecord(&tempEntry, alias))
return S_dbLib_recExists;
dbFinishEntry(&tempEntry);
pnewnode = dbCalloc(1, sizeof(dbRecordNode));
pnewnode->recordname = epicsStrDup(alias);
pnewnode->precord = precnode->precord;
pnewnode->aliasedRecnode = precnode;
pnewnode->flags = DBRN_FLAGS_ISALIAS;
precnode->flags |= DBRN_FLAGS_HASALIAS;
ellInit(&pnewnode->infoList);
ellAdd(&precordType->recList, &pnewnode->node);
precordType->no_aliases++;
ppvd = dbPvdAdd(pdbentry->pdbbase, precordType, pnewnode);
if (!ppvd) {
errMessage(-1, "dbCreateAlias: Add to PVD failed");
return -1;
}
return 0;
}
int dbFollowAlias(DBENTRY *pdbentry)
{
if(!pdbentry->precnode)
return S_dbLib_recNotFound;
if(pdbentry->precnode->aliasedRecnode)
pdbentry->precnode = pdbentry->precnode->aliasedRecnode;
return 0;
}
int dbIsAlias(DBENTRY *pdbentry)
{
dbRecordNode *precnode = pdbentry->precnode;
if(!precnode) return 0;
return precnode->flags & DBRN_FLAGS_ISALIAS ? 1 : 0;
}
long dbCopyRecord(DBENTRY *pdbentry,const char *newRecordName,int overWriteOK)
{
dbRecordType *precordType = pdbentry->precordType;
dbFldDes *pdbFldDes;
dbRecordNode *precnode = pdbentry->precnode;
long status;
DBENTRY dbentry;
char *pvalue;
if(!precordType) return(S_dbLib_recordTypeNotFound);
/*Get size of NAME field*/
pdbFldDes = precordType->papFldDes[0];
if(!pdbFldDes || (strcmp(pdbFldDes->name,"NAME")!=0))
return(S_dbLib_nameLength);
if((int)strlen(newRecordName)>=pdbFldDes->size) return(S_dbLib_nameLength);
if (!precnode || dbIsAlias(pdbentry)) return S_dbLib_recNotFound;
dbInitEntry(pdbentry->pdbbase,&dbentry);
status = dbFindRecord(&dbentry,newRecordName);
if(!status) {
if(!overWriteOK) {
dbFinishEntry(&dbentry);
return(S_dbLib_recExists);
}
status = dbDeleteRecord(&dbentry);
if(status) return(status);
}
dbFinishEntry(&dbentry);
if((status = dbFindRecordType(&dbentry,precordType->name))) return(status);
if((status = dbCreateRecord(&dbentry,newRecordName))) return(status);
if((status = dbFirstField(pdbentry,TRUE))) return(status);
if((status = dbFirstField(&dbentry,TRUE))) return(status);
while(!status) {
if(!dbIsDefaultValue(pdbentry)) {
pvalue = dbGetString(pdbentry);
if((status = dbPutString(&dbentry,pvalue))) return(status);
}
status = dbNextField(pdbentry,TRUE);
if(!status) status = dbNextField(&dbentry,TRUE);
if(!status && (pdbentry->pflddes!=dbentry.pflddes)) {
epicsPrintf("dbCopyRecord: Logic Error\n");
return(-1);
}
}
/*Copy the info strings too*/
status = dbFirstInfo(pdbentry);
while (!status) {
status = dbPutInfo(&dbentry, dbGetInfoName(pdbentry), dbGetInfoString(pdbentry));
if (status) return (status);
status = dbNextInfo(pdbentry);
}
/*Leave pdbentry pointing to newRecordName*/
return(dbFindRecord(pdbentry,newRecordName));
}
long dbFindFieldPart(DBENTRY *pdbentry,const char **ppname)
{
dbRecordType *precordType = pdbentry->precordType;
dbRecordNode *precnode = pdbentry->precnode;
const char *pname = *ppname;
short top, bottom, test;
char **papsortFldName;
short *sortFldInd;
int ch;
size_t nameLen;
if (!precordType) return S_dbLib_recordTypeNotFound;
if (!precnode) return S_dbLib_recNotFound;
papsortFldName = precordType->papsortFldName;
sortFldInd = precordType->sortFldInd;
/* Measure field name length; name is a valid C identifier */
nameLen = 0;
if ((ch = *pname) &&
(ch == '_' || isalpha(ch))) {
while ((ch = pname[++nameLen]))
if (!(ch == '_' || isalnum(ch))) break;
}
/* Handle absent field name */
if (nameLen == 0) {
dbFldDes *pflddes = precordType->pvalFldDes;
if (!pflddes)
return S_dbLib_recordTypeNotFound;
pdbentry->pflddes = pflddes;
pdbentry->indfield = precordType->indvalFlddes;
*ppname = &pname[nameLen];
return dbGetFieldAddress(pdbentry);
}
/* binary search through ordered field names */
top = precordType->no_fields - 1;
bottom = 0;
test = (top + bottom) / 2;
while (1) {
int compare = strncmp(papsortFldName[test], pname, nameLen);
if (compare == 0)
compare = (int) (strlen(papsortFldName[test]) - nameLen);
if (compare == 0) {
dbFldDes *pflddes = precordType->papFldDes[sortFldInd[test]];
if (!pflddes)
return S_dbLib_recordTypeNotFound;
pdbentry->pflddes = pflddes;
pdbentry->indfield = sortFldInd[test];
*ppname = &pname[nameLen];
return dbGetFieldAddress(pdbentry);
} else if (compare > 0) {
top = test - 1;
if (top < bottom) break;
test = (top + bottom) / 2;
} else {
bottom = test + 1;
if (top < bottom) break;
test = (top + bottom) / 2;
}
}
return S_dbLib_fieldNotFound;
}
long dbFindField(DBENTRY *pdbentry,const char *pname)
{
long status = dbFindFieldPart(pdbentry, &pname);
int ch;
if (status == S_dbLib_fieldNotFound)
return dbGetRecordAttribute(pdbentry, pname);
if (status) return status;
ch = *pname;
if (ch == 0 || isspace(ch)) return 0;
return S_dbLib_recNotFound;
}
int dbFoundField(DBENTRY *pdbentry)
{ return((pdbentry->pfield) ? TRUE : FALSE); }
char * dbGetString(DBENTRY *pdbentry)
{
dbFldDes *pflddes = pdbentry->pflddes;
void *pfield = pdbentry->pfield;
DBLINK *plink;
if (!pflddes) {
dbMsgCpy(pdbentry, "fldDes not found");
return pdbentry->message;
}
switch (pflddes->field_type) {
case DBF_STRING:
case DBF_INLINK:
case DBF_OUTLINK:
case DBF_FWDLINK:
if (!pfield) {
dbMsgCpy(pdbentry, "Field not allocated (NULL)");
return pdbentry->message;
}
break;
default:
break;
}
switch (pflddes->field_type) {
case DBF_STRING:
/* Protect against a missing nil-terminator */
dbMsgNCpy(pdbentry, (char *)pfield, pflddes->size);
break;
case DBF_CHAR:
case DBF_UCHAR:
case DBF_SHORT:
case DBF_USHORT:
case DBF_ENUM:
case DBF_LONG:
case DBF_ULONG:
case DBF_INT64:
case DBF_UINT64:
case DBF_FLOAT:
case DBF_DOUBLE:
case DBF_MENU:
case DBF_DEVICE:
return(dbGetStringNum(pdbentry));
case DBF_INLINK:
case DBF_OUTLINK:
plink = (DBLINK *)pfield;
switch(plink->type) {
case CONSTANT:
if (plink->value.constantStr) {
dbMsgCpy(pdbentry, plink->value.constantStr);
} else if (plink->text) {
dbMsgCpy(pdbentry, plink->text);
} else {
dbMsgCpy(pdbentry, "");
}
break;
case MACRO_LINK:
if (plink->value.macro_link.macroStr) {
dbMsgCpy(pdbentry, plink->value.macro_link.macroStr);
} else {
dbMsgCpy(pdbentry, "");
}
break;
case JSON_LINK:
dbMsgCpy(pdbentry, plink->value.json.string);
break;
case PN_LINK:
dbMsgPrint(pdbentry, "%s%s",
plink->value.pv_link.pvname ? plink->value.pv_link.pvname : "",
msstring[plink->value.pv_link.pvlMask&pvlOptMsMode]);
break;
case PV_LINK:
case CA_LINK:
case DB_LINK: {
int ppind;
short pvlMask;
pvlMask = plink->value.pv_link.pvlMask;
if (pvlMask&pvlOptPP) ppind=1;
else if(pvlMask&pvlOptCA) ppind=2;
else if(pvlMask&pvlOptCP) ppind=3;
else if(pvlMask&pvlOptCPP) ppind=4;
else ppind=0;
dbMsgPrint(pdbentry, "%s%s%s%s",
plink->value.pv_link.pvname ? plink->value.pv_link.pvname : "",
(plink->flags & DBLINK_FLAG_TSELisTIME) ? ".TIME" : "",
ppstring[ppind],
msstring[plink->value.pv_link.pvlMask&pvlOptMsMode]);
break;
}
case VME_IO:
dbMsgPrint(pdbentry, "#C%d S%d @%s",
plink->value.vmeio.card,plink->value.vmeio.signal,
plink->value.vmeio.parm);
break;
case CAMAC_IO:
dbMsgPrint(pdbentry, "#B%d C%d N%d A%d F%d @%s",
plink->value.camacio.b,plink->value.camacio.c,
plink->value.camacio.n,plink->value.camacio.a,
plink->value.camacio.f,plink->value.camacio.parm);
break;
case RF_IO:
dbMsgPrint(pdbentry, "#R%d M%d D%d E%d",
plink->value.rfio.cryo,
plink->value.rfio.micro,
plink->value.rfio.dataset,
plink->value.rfio.element);
break;
case AB_IO:
dbMsgPrint(pdbentry, "#L%d A%d C%d S%d @%s",
plink->value.abio.link,plink->value.abio.adapter,
plink->value.abio.card,plink->value.abio.signal,
plink->value.abio.parm);
break;
case GPIB_IO:
dbMsgPrint(pdbentry, "#L%d A%d @%s",
plink->value.gpibio.link,plink->value.gpibio.addr,
plink->value.gpibio.parm);
break;
case BITBUS_IO:
dbMsgPrint(pdbentry, "#L%u N%u P%u S%u @%s",
plink->value.bitbusio.link,plink->value.bitbusio.node,
plink->value.bitbusio.port,plink->value.bitbusio.signal,
plink->value.bitbusio.parm);
break;
case BBGPIB_IO:
dbMsgPrint(pdbentry, "#L%u B%u G%u @%s",
plink->value.bbgpibio.link,plink->value.bbgpibio.bbaddr,
plink->value.bbgpibio.gpibaddr,plink->value.bbgpibio.parm);
break;
case INST_IO:
dbMsgPrint(pdbentry, "@%s", plink->value.instio.string);
break;
case VXI_IO :
if (plink->value.vxiio.flag == VXIDYNAMIC)
dbMsgPrint(pdbentry, "#V%d C%d S%d @%s",
plink->value.vxiio.frame,plink->value.vxiio.slot,
plink->value.vxiio.signal,plink->value.vxiio.parm);
else
dbMsgPrint(pdbentry, "#V%d S%d @%s",
plink->value.vxiio.la,plink->value.vxiio.signal,
plink->value.vxiio.parm);
break;
default :
return(NULL);
}
break;
case DBF_FWDLINK: {
DBLINK *plink=(DBLINK *)pfield;
switch(plink->type) {
case CONSTANT:
if (plink->value.constantStr) {
dbMsgCpy(pdbentry, plink->value.constantStr);
} else if (plink->text) {
dbMsgCpy(pdbentry, plink->text);
} else {
dbMsgCpy(pdbentry, "");
}
break;
case MACRO_LINK:
if (plink->value.macro_link.macroStr) {
dbMsgCpy(pdbentry, plink->value.macro_link.macroStr);
} else {
dbMsgCpy(pdbentry, "");
}
break;
case JSON_LINK:
dbMsgCpy(pdbentry, plink->value.json.string);
break;
case PV_LINK:
case CA_LINK:
case DB_LINK: {
int ppind;
short pvlMask;
pvlMask = plink->value.pv_link.pvlMask;
if (pvlMask&pvlOptCA) ppind=2;
else ppind=0;
dbMsgPrint(pdbentry, "%s%s",
plink->value.pv_link.pvname ? plink->value.pv_link.pvname : "",
ppind ? ppstring[ppind] : "");
break;
}
default :
return(NULL);
}
}
break;
default:
return(NULL);
}
return pdbentry->message;
}
char *dbGetStringNum(DBENTRY *pdbentry)
{
dbFldDes *pflddes = pdbentry->pflddes;
void *pfield = pdbentry->pfield;
char *message;
unsigned char cvttype;
/* the following assumes that messagesize is large enough
* to hold the base 10 encoded value of a 32-bit integer.
*/
message = getpMessage(pdbentry);
cvttype = pflddes->base;
switch (pflddes->field_type) {
case DBF_CHAR:
if (cvttype == CT_DECIMAL)
cvtCharToString(*(char *) pfield, message);
else
ulongToHexString(*(char *) pfield, message);
break;
case DBF_UCHAR:
if (cvttype==CT_DECIMAL)
cvtUcharToString(*(epicsUInt8 *) pfield, message);
else
ulongToHexString(*(epicsUInt8 *) pfield, message);
break;
case DBF_SHORT:
if (cvttype==CT_DECIMAL)
cvtShortToString(*(epicsInt16 *) pfield, message);
else
ulongToHexString(*(epicsInt16 *) pfield, message);
break;
case DBF_USHORT:
case DBF_ENUM:
if (cvttype==CT_DECIMAL)
cvtUshortToString(*(epicsUInt16 *) pfield, message);
else
ulongToHexString(*(epicsUInt16 *) pfield, message);
break;
case DBF_LONG:
if (cvttype==CT_DECIMAL)
cvtLongToString(*(epicsInt32 *) pfield, message);
else
ulongToHexString(*(epicsInt32 *) pfield, message);
break;
case DBF_ULONG:
if (cvttype==CT_DECIMAL)
cvtUlongToString(*(epicsUInt32 *) pfield, message);
else
ulongToHexString(*(epicsUInt32 *) pfield, message);
break;
case DBF_INT64:
if (cvttype==CT_DECIMAL)
cvtInt64ToString(*(epicsInt64 *) pfield, message);
else
cvtInt64ToHexString(*(epicsInt64 *) pfield, message);
break;
case DBF_UINT64:
if (cvttype==CT_DECIMAL)
cvtUInt64ToString(*(epicsUInt32 *) pfield, message);
else
cvtUInt64ToHexString(*(epicsUInt32 *) pfield, message);
break;
case DBF_FLOAT:
floatToString(*(epicsFloat32 *) pfield, message);
break;
case DBF_DOUBLE:
doubleToString(*(epicsFloat64 *) pfield, message);
break;
case DBF_MENU:
{
dbMenu *pdbMenu = (dbMenu *)pflddes->ftPvt;
epicsEnum16 choice_ind;
char *pchoice;
if (!pfield) {
dbMsgCpy(pdbentry, "Field not found");
return message;
}
choice_ind = *((epicsEnum16 *) pdbentry->pfield);
if (!pdbMenu || choice_ind < 0 || choice_ind >= pdbMenu->nChoice)
return NULL;
pchoice = pdbMenu->papChoiceValue[choice_ind];
dbMsgCpy(pdbentry, pchoice);
}
break;
case DBF_DEVICE:
{
dbDeviceMenu *pdbDeviceMenu;
epicsEnum16 choice_ind;
char *pchoice;
if (!pfield) {
dbMsgCpy(pdbentry, "Field not found");
return message;
}
pdbDeviceMenu = dbGetDeviceMenu(pdbentry);
if (!pdbDeviceMenu)
return NULL;
choice_ind = *((epicsEnum16 *) pdbentry->pfield);
if (choice_ind<0 || choice_ind>=pdbDeviceMenu->nChoice)
return NULL;
pchoice = pdbDeviceMenu->papChoice[choice_ind];
dbMsgCpy(pdbentry, pchoice);
}
break;
default:
return NULL;
}
return message;
}
long dbInitRecordLinks(dbRecordType *rtyp, struct dbCommon *prec)
{
short i;
for (i=0; i<rtyp->no_links; i++) {
dbLinkInfo link_info;
dbFldDes *pflddes = rtyp->papFldDes[rtyp->link_ind[i]];
DBLINK *plink = (DBLINK *)(((char *)prec) + pflddes->offset);
devSup *devsup = NULL;
plink->precord = prec;
/* link fields are zero'd on allocation.
* so are effectively CONSTANT, but with constantStr==NULL.
* Here we initialize them to have the correct link type,
* with zero values and empty (but non-NULL) strings.
*/
if(pflddes->isDevLink) {
devsup = (devSup *)ellNth(&rtyp->devList, prec->dtyp+1);
}
if(devsup)
plink->type = devsup->link_type;
else
plink->type = CONSTANT;
switch (plink->type) {
/* constantStr is allowed to remain NULL if plink->text==NULL
* constantStr==NULL has special meaning in recGblInitConstantLink()
*/
case CONSTANT: plink->value.constantStr = NULL; break;
case PV_LINK: plink->value.pv_link.pvname = callocMustSucceed(1, 1, "init PV_LINK"); break;
case JSON_LINK: plink->value.json.string = pNullString; break;
case VME_IO: plink->value.vmeio.parm = pNullString; break;
case CAMAC_IO: plink->value.camacio.parm = pNullString; break;
case AB_IO: plink->value.abio.parm = pNullString; break;
case GPIB_IO: plink->value.gpibio.parm = pNullString; break;
case BITBUS_IO: plink->value.bitbusio.parm = pNullString; break;
case INST_IO: plink->value.instio.string = pNullString; break;
case BBGPIB_IO: plink->value.bbgpibio.parm = pNullString; break;
case VXI_IO: plink->value.vxiio.parm = pNullString; break;
}
if(!plink->text)
continue;
if(dbParseLink(plink->text, pflddes->field_type, &link_info)!=0) {
/* This was already parsed once when ->text was set.
* Any syntax error messages were printed at that time.
*/
} else if(dbCanSetLink(plink, &link_info, devsup)!=0) {
errlogPrintf("Error: %s.%s: can't initialize link type %d with \"%s\" (type %d)\n",
prec->name, pflddes->name, plink->type, plink->text, link_info.ltype);
} else if(dbSetLink(plink, &link_info, devsup)) {
errlogPrintf("Error: %s.%s: failed to initialize link type %d with \"%s\" (type %d)\n",
prec->name, pflddes->name, plink->type, plink->text, link_info.ltype);
}
free(plink->text);
plink->text = NULL;
}
return 0;
}
void dbFreeLinkInfo(dbLinkInfo *pinfo)
{
if (pinfo->ltype == JSON_LINK) {
dbJLinkFree(pinfo->jlink);
pinfo->jlink = NULL;
}
free(pinfo->target);
pinfo->target = NULL;
}
long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo)
{
char *pstr;
size_t len;
double value;
memset(pinfo, 0, sizeof(*pinfo));
/* Strip leading white space */
while (*str && isspace((int)*str)) str++;
len = strlen(str);
/* Strip trailing white space */
while (len > 0 && isspace((int)str[len-1])) len--;
pstr = malloc(len + 1);
if (!pstr)
return S_dbLib_outMem;
pinfo->target = pstr;
/* Check for Instrument I/O links */
if (*str == '@') {
pinfo->ltype = INST_IO;
/* Store everything after the '@' */
memcpy(pstr, str+1, --len);
pstr[len] = '\0';
return 0;
}
/* Store the stripped string */
memcpy(pstr, str, len);
pstr[len] = '\0';
/* Check for braces => JSON */
if (*str == '{' && str[len-1] == '}') {
if (dbJLinkParse(str, len, ftype, &pinfo->jlink))
goto fail;
pinfo->ltype = JSON_LINK;
return 0;
}
/* Check for other HW link types */
if (*pstr == '#') {
int ret;
char junk = 0;
char *parm = strchr(pstr, '@'); /* find start of parm string */
if (parm) {
*parm++ = '\0'; /* isolate the parm string for later */
len -= (parm - pstr);
}
/* generalized extraction of ID charactor and integer pairs (eg. "#C15 S14") */
ret = sscanf(pinfo->target, "# %c%d %c%d %c%d %c%d %c%d %c",
&pinfo->hwid[0], &pinfo->hwnums[0],
&pinfo->hwid[1], &pinfo->hwnums[1],
&pinfo->hwid[2], &pinfo->hwnums[2],
&pinfo->hwid[3], &pinfo->hwnums[3],
&pinfo->hwid[4], &pinfo->hwnums[4],
&junk);
/* ret<0 when pattern not matched
* ret==11 when extra non-space before '@'.
* ret is odd when a number is missing
*/
if (ret<0 || ret>10 || ret%2==1) goto fail;
if (strcmp(pinfo->hwid, "CS")==0) pinfo->ltype = VME_IO;
else if (strcmp(pinfo->hwid, "BCN")==0) pinfo->ltype = CAMAC_IO;
else if (strcmp(pinfo->hwid, "BCNA")==0) pinfo->ltype = CAMAC_IO;
else if (strcmp(pinfo->hwid, "BCNF")==0) pinfo->ltype = CAMAC_IO;
else if (strcmp(pinfo->hwid, "BCNAF")==0) pinfo->ltype = CAMAC_IO;
else if (strcmp(pinfo->hwid, "RMDE")==0) pinfo->ltype = RF_IO;
else if (strcmp(pinfo->hwid, "LACS")==0) pinfo->ltype = AB_IO;
else if (strcmp(pinfo->hwid, "LA")==0) pinfo->ltype = GPIB_IO;
else if (strcmp(pinfo->hwid, "LNPS")==0) pinfo->ltype = BITBUS_IO;
else if (strcmp(pinfo->hwid, "LBG")==0) pinfo->ltype = BBGPIB_IO;
else if (strcmp(pinfo->hwid, "VCS")==0) pinfo->ltype = VXI_IO;
else if (strcmp(pinfo->hwid, "VS")==0) pinfo->ltype = VXI_IO;
else goto fail;
if (pinfo->ltype != RF_IO) {
if (!parm) {
pinfo->target[0] = '\0';
} else {
/* move parm string to beginning of buffer */
memmove(pinfo->target, parm, len + 1);
}
} else if (!parm && pinfo->ltype == RF_IO) {
/* RF_IO, the string isn't needed at all */
free(pinfo->target);
pinfo->target = NULL;
}
else goto fail;
return 0;
}
/* Link is a constant if empty or it holds just a number */
if (len == 0 || epicsParseDouble(pstr, &value, NULL) == 0) {
pinfo->ltype = CONSTANT;
return 0;
}
/* Link may be an array constant */
if (pstr[0] == '[' && pstr[len-1] == ']') {
pinfo->ltype = CONSTANT;
return 0;
}
pinfo->ltype = PV_LINK;
pstr = strchr(pstr, ' '); /* find start of link modifiers (can't be seperated by tabs) */
if (pstr) {
*pstr++ = '\0'; /* isolate modifiers. pinfo->target is PV name only for re-use in struct pv_link */
/* Space seperation of modifiers isn't required, and other chars are ignored.
* Order of comparisons resolves ambiguity by checking for
* longer matches first.
* eg. "QQCPPXMSITT" is pvlOptCPP|pvlOptMSI
*/
if (strstr(pstr, "NPP")) pinfo->modifiers = 0;
else if (strstr(pstr, "CPP")) pinfo->modifiers = pvlOptCPP;
else if (strstr(pstr, "PP")) pinfo->modifiers = pvlOptPP;
else if (strstr(pstr, "CA")) pinfo->modifiers = pvlOptCA;
else if (strstr(pstr, "CP")) pinfo->modifiers = pvlOptCP;
if (strstr(pstr, "NMS")) pinfo->modifiers |= pvlOptNMS;
else if (strstr(pstr, "MSI")) pinfo->modifiers |= pvlOptMSI;
else if (strstr(pstr, "MSS")) pinfo->modifiers |= pvlOptMSS;
else if (strstr(pstr, "MS")) pinfo->modifiers |= pvlOptMS;
/* filter modifiers based on link type */
switch(ftype) {
case DBF_INLINK: /* accept all */ break;
case DBF_OUTLINK: pinfo->modifiers &= ~pvlOptCPP; break;
case DBF_FWDLINK: pinfo->modifiers &= pvlOptCA; break;
}
}
return 0;
fail:
dbFreeLinkInfo(pinfo);
return S_dbLib_badField;
}
long dbCanSetLink(DBLINK *plink, dbLinkInfo *pinfo, devSup *devsup)
{
/* Release pinfo resources on failure */
int expected_type = devsup ? devsup->link_type : CONSTANT;
if (pinfo->ltype == expected_type)
return 0;
switch (pinfo->ltype) {
case CONSTANT:
case JSON_LINK:
case PV_LINK:
if (expected_type == CONSTANT ||
expected_type == JSON_LINK ||
expected_type == PV_LINK)
return 0;
default:
dbFreeLinkInfo(pinfo);
return 1;
}
}
static
void dbSetLinkConst(DBLINK *plink, dbLinkInfo *pinfo)
{
plink->type = CONSTANT;
plink->value.constantStr = pinfo->target;
pinfo->target = NULL;
}
static
void dbSetLinkPV(DBLINK *plink, dbLinkInfo *pinfo)
{
plink->type = PV_LINK;
plink->value.pv_link.pvname = pinfo->target;
plink->value.pv_link.pvlMask = pinfo->modifiers;
pinfo->target = NULL;
}
static
void dbSetLinkJSON(DBLINK *plink, dbLinkInfo *pinfo)
{
plink->type = JSON_LINK;
plink->value.json.string = pinfo->target;
plink->value.json.jlink = pinfo->jlink;
pinfo->target = NULL;
pinfo->jlink = NULL;
}
static
void dbSetLinkHW(DBLINK *plink, dbLinkInfo *pinfo)
{
switch(pinfo->ltype) {
case JSON_LINK:
plink->value.json.string = pinfo->target;
break;
case INST_IO:
plink->value.instio.string = pinfo->target;
break;
case VME_IO:
plink->value.vmeio.card = pinfo->hwnums[0];
plink->value.vmeio.signal = pinfo->hwnums[1];
plink->value.vmeio.parm = pinfo->target;
break;
case CAMAC_IO:
plink->value.camacio.b = pinfo->hwnums[0];
plink->value.camacio.c = pinfo->hwnums[1];
plink->value.camacio.n = pinfo->hwnums[2];
plink->value.camacio.a = pinfo->hwnums[3];
plink->value.camacio.f = pinfo->hwnums[4];
plink->value.camacio.parm = pinfo->target;
break;
case RF_IO:
plink->value.rfio.cryo = pinfo->hwnums[0];
plink->value.rfio.micro = pinfo->hwnums[1];
plink->value.rfio.dataset = pinfo->hwnums[2];
plink->value.rfio.element = pinfo->hwnums[3];
break;
case AB_IO:
plink->value.abio.link = pinfo->hwnums[0];
plink->value.abio.adapter = pinfo->hwnums[1];
plink->value.abio.card = pinfo->hwnums[2];
plink->value.abio.signal = pinfo->hwnums[3];
plink->value.abio.parm = pinfo->target;
break;
case GPIB_IO:
plink->value.gpibio.link = pinfo->hwnums[0];
plink->value.gpibio.addr = pinfo->hwnums[1];
plink->value.gpibio.parm = pinfo->target;
break;
case BITBUS_IO:
plink->value.bitbusio.link = pinfo->hwnums[0];
plink->value.bitbusio.node = pinfo->hwnums[1];
plink->value.bitbusio.port = pinfo->hwnums[2];
plink->value.bitbusio.signal = pinfo->hwnums[3];
plink->value.bitbusio.parm = pinfo->target;
break;
case BBGPIB_IO:
plink->value.bbgpibio.link = pinfo->hwnums[0];
plink->value.bbgpibio.bbaddr = pinfo->hwnums[1];
plink->value.bbgpibio.gpibaddr = pinfo->hwnums[2];
plink->value.bbgpibio.parm = pinfo->target;
break;
case VXI_IO:
if(strcmp(pinfo->hwid, "VCS")==0) {
plink->value.vxiio.flag=VXIDYNAMIC;
plink->value.vxiio.frame = pinfo->hwnums[0];
plink->value.vxiio.slot = pinfo->hwnums[1];
plink->value.vxiio.signal = pinfo->hwnums[2];
} else if(strcmp(pinfo->hwid, "VS")==0) {
plink->value.vxiio.flag=VXISTATIC;
plink->value.vxiio.la = pinfo->hwnums[0];
plink->value.vxiio.signal = pinfo->hwnums[1];
} else {
cantProceed("dbSetLinkHW: logic error, unknown VXI_IO variant");
}
plink->value.vxiio.parm = pinfo->target;
break;
default:
cantProceed("dbSetLinkHW: logic error, unhandled link type");
return;
}
plink->type = pinfo->ltype;
pinfo->target = NULL; /* now owned by link field */
}
long dbSetLink(DBLINK *plink, dbLinkInfo *pinfo, devSup *devsup)
{
int expected_type = devsup ? devsup->link_type : CONSTANT;
if (expected_type == CONSTANT ||
expected_type == JSON_LINK ||
expected_type == PV_LINK) {
switch (pinfo->ltype) {
case CONSTANT:
dbFreeLinkContents(plink);
dbSetLinkConst(plink, pinfo);
break;
case PV_LINK:
dbFreeLinkContents(plink);
dbSetLinkPV(plink, pinfo);
break;
case JSON_LINK:
dbFreeLinkContents(plink);
dbSetLinkJSON(plink, pinfo);
break;
default:
errlogMessage("Warning: dbSetLink: forgot to test with dbCanSetLink() or logic error");
goto fail; /* can't assign HW link */
}
}
else if (expected_type == pinfo->ltype) {
dbFreeLinkContents(plink);
dbSetLinkHW(plink, pinfo);
}
else
goto fail;
return 0;
fail:
dbFreeLinkInfo(pinfo);
return S_dbLib_badField;
}
long dbPutString(DBENTRY *pdbentry,const char *pstring)
{
dbFldDes *pflddes = pdbentry->pflddes;
void *pfield = pdbentry->pfield;
long status=0;
int macroIsOk;
int stringHasMacro=FALSE;
if(!pflddes) return(S_dbLib_flddesNotFound);
macroIsOk = dbIsMacroOk(pdbentry);
stringHasMacro = strstr(pstring,"$(") || strstr(pstring,"${");
if(!macroIsOk && stringHasMacro) {
epicsPrintf("%s.%s Has unexpanded macro\n",
dbGetRecordName(pdbentry),dbGetFieldName(pdbentry));
return(S_dbLib_badField);
}
switch (pflddes->field_type) {
case DBF_STRING:
if(!pfield) return(S_dbLib_fieldNotFound);
if(strlen(pstring) >= (size_t)pflddes->size) return S_dbLib_strLen;
strncpy((char *)pfield, pstring, pflddes->size-1);
((char *)pfield)[pflddes->size-1] = 0;
if((pflddes->special == SPC_CALC) && !stringHasMacro) {
char rpcl[RPCL_LEN];
short err;
if (postfix(pstring,rpcl,&err)) {
status = S_dbLib_badField;
errlogPrintf("%s in CALC expression '%s'\n",
calcErrorStr(err), pstring);
}
}
break;
case DBF_CHAR:
case DBF_SHORT:
case DBF_LONG:
case DBF_INT64:
case DBF_UCHAR:
case DBF_USHORT:
case DBF_ULONG:
case DBF_UINT64:
case DBF_ENUM:
case DBF_FLOAT:
case DBF_DOUBLE:
case DBF_MENU:
case DBF_DEVICE:
status = dbPutStringNum(pdbentry,pstring);
break;
case DBF_INLINK:
case DBF_OUTLINK:
case DBF_FWDLINK: {
dbLinkInfo link_info;
DBLINK *plink = (DBLINK *)pfield;
status = dbParseLink(pstring, pflddes->field_type, &link_info);
if (status) break;
if (plink->type==CONSTANT && plink->value.constantStr==NULL) {
/* links not yet initialized by dbInitRecordLinks() */
free(plink->text);
plink->text = epicsStrDup(pstring);
dbFreeLinkInfo(&link_info);
} else {
/* assignment after init (eg. autosave restore) */
struct dbCommon *prec = pdbentry->precnode->precord;
devSup *devsup = (devSup *)ellNth(&pdbentry->precordType->devList, prec->dtyp+1);
status = dbCanSetLink(plink, &link_info, devsup);
if (status == 0)
status = dbSetLink(plink, &link_info, devsup);
}
}
break;
default:
return S_dbLib_badField;
}
if (!status && strcmp(pflddes->name, "VAL") == 0) {
DBENTRY dbentry;
dbCopyEntryContents(pdbentry, &dbentry);
if (!dbFindField(&dbentry, "UDF")) {
dbPutString(&dbentry, "0");
}
dbFinishEntry(&dbentry);
}
return(status);
}
void dbPutStringSuggest(DBENTRY *pdbentry, const char *pstring)
{
dbFldDes *pflddes = pdbentry->pflddes;
switch (pflddes->field_type) {
case DBF_MENU:
case DBF_DEVICE: {
int i, nchoices = 0;
char** choices = NULL;
const char *best = NULL;
double maxdist = 0.0; /* don't offer suggestions which have no similarity */
if(pflddes->field_type==DBF_MENU) {
dbMenu *pdbMenu = (dbMenu *)pflddes->ftPvt;
if(!pdbMenu)
return;
choices = pdbMenu->papChoiceValue;
nchoices = pdbMenu->nChoice;
} else {
dbDeviceMenu *pdbDeviceMenu = dbGetDeviceMenu(pdbentry);
if(!pdbDeviceMenu)
return;
choices = pdbDeviceMenu->papChoice;
nchoices = pdbDeviceMenu->nChoice;
}
for(i=0; i<nchoices; i++) {
double dist = epicsStrSimilarity(pstring, choices[i]);
if(dist>maxdist) {
best = choices[i];
maxdist = dist;
}
}
if(best) {
epicsPrintf(" Did you mean \"%s\"?\n", best);
}
}
break;
default:
break;
}
}
char * dbVerify(DBENTRY *pdbentry, const char *pstring)
{
dbFldDes *pflddes = pdbentry->pflddes;
char *message = getpMessage(pdbentry);
long status;
union {
epicsInt8 i8;
epicsUInt8 u8;
epicsInt16 i16;
epicsUInt16 u16;
epicsInt32 i32;
epicsUInt32 u32;
epicsInt64 i64;
epicsUInt64 u64;
epicsFloat32 f32;
epicsFloat64 f64;
} val;
if (!pflddes) {
strcpy(message, "fldDes not found");
return message;
}
if (strstr(pstring,"$(") || strstr(pstring,"${"))
return NULL;
switch (pflddes->field_type) {
case DBF_STRING:
{
size_t length = strlen(pstring);
if (length >= pflddes->size) {
sprintf(message, "String too long, max %d characters",
pflddes->size - 1);
return message;
}
if (pflddes->special == SPC_CALC) {
char rpcl[RPCL_LEN];
short err;
status = postfix(pstring, rpcl, &err);
if (status) {
sprintf(message,"%s in CALC expression '%s'",
calcErrorStr(err), pstring);
return message;
}
}
return NULL;
}
case DBF_CHAR:
status = epicsParseInt8(pstring, &val.i8, 0, NULL);
break;
case DBF_UCHAR:
status = epicsParseUInt8(pstring, &val.u8, 0, NULL);
break;
case DBF_SHORT:
status = epicsParseInt16(pstring, &val.i16, 0, NULL);
break;
case DBF_ENUM:
case DBF_USHORT:
status = epicsParseUInt16(pstring, &val.u16, 0, NULL);
break;
case DBF_LONG:
status = epicsParseInt32(pstring, &val.i32, 0, NULL);
break;
case DBF_ULONG:
status = epicsParseUInt32(pstring, &val.u32, 0, NULL);
break;
case DBF_INT64:
status = epicsParseInt64(pstring, &val.i64, 0, NULL);
break;
case DBF_UINT64:
status = epicsParseUInt64(pstring, &val.u64, 0, NULL);
break;
case DBF_FLOAT:
status = epicsParseFloat32(pstring, &val.f32, NULL);
break;
case DBF_DOUBLE:
status = epicsParseFloat64(pstring, &val.f64, NULL);
break;
case DBF_MENU:
{
dbMenu *pdbMenu = (dbMenu *)pflddes->ftPvt;
int i;
if (!pdbMenu)
return NULL;
for (i = 0; i < pdbMenu->nChoice; i++) {
const char *pchoice = pdbMenu->papChoiceValue[i];
if (!pchoice)
continue;
if (strcmp(pchoice, pstring) == 0) {
return NULL;
}
}
}
strcpy(message, "Not a valid menu choice");
return message;
case DBF_DEVICE:
{
dbDeviceMenu *pdbDeviceMenu = dbGetDeviceMenu(pdbentry);
int i;
if (!pdbDeviceMenu || pdbDeviceMenu->nChoice == 0)
return NULL;
for (i = 0; i < pdbDeviceMenu->nChoice; i++) {
const char *pchoice = pdbDeviceMenu->papChoice[i];
if (!pchoice)
continue;
if (strcmp(pchoice, pstring) == 0) {
return NULL;
}
}
}
strcpy(message, "Not a valid device type");
return message;
case DBF_INLINK:
case DBF_OUTLINK:
case DBF_FWDLINK:
return NULL;
default:
strcpy(message, "Not a valid field type");
return message;
}
switch (status) {
case 0:
message = NULL;
break;
case S_stdlib_noConversion:
strcpy(message, "Not a valid integer");
break;
case S_stdlib_badBase:
strcpy(message, "Internal error (badBase)");
break;
case S_stdlib_overflow:
strcpy(message, "Number too large for field type");
break;
case S_stdlib_underflow:
strcpy(message, "Number too small for field type");
break;
case S_stdlib_extraneous:
strcpy(message, "Extraneous characters after number");
break;
default:
strcpy(message, "Unknown numeric conversion error");
}
return message;
}
long dbFirstInfo(DBENTRY *pdbentry)
{
dbRecordNode *precnode = pdbentry->precnode;
pdbentry->pinfonode = NULL;
if (!precnode) return (S_dbLib_recNotFound);
pdbentry->pinfonode = (dbInfoNode *)ellFirst(&precnode->infoList);
return (pdbentry->pinfonode ? 0 : S_dbLib_infoNotFound);
}
long dbNextInfo(DBENTRY *pdbentry)
{
dbRecordNode *precnode = pdbentry->precnode;
dbInfoNode *pinfo;
if (!precnode) return (S_dbLib_recNotFound);
pinfo = pdbentry->pinfonode;
if (!pinfo) return (S_dbLib_infoNotFound);
pinfo = (dbInfoNode *)ellNext(&pinfo->node);
pdbentry->pinfonode = pinfo;
return (pinfo ? 0 : S_dbLib_infoNotFound);
}
long dbNextMatchingInfo(DBENTRY *pdbentry, const char *pattern)
{
long status;
if (!pdbentry->precordType)
{
status = dbFirstRecordType(pdbentry);
goto first;
}
while(1) {
status = dbNextInfo(pdbentry);
while (status) {
status = dbNextRecord(pdbentry);
while (status) {
status = dbNextRecordType(pdbentry);
first:
if (status) return status;
status = dbFirstRecord(pdbentry);
}
status = dbFirstInfo(pdbentry);
}
if (!pattern || !*pattern) return 0;
if (epicsStrGlobMatch(dbGetInfoName(pdbentry), pattern)) return 0;
}
}
long dbFindInfo(DBENTRY *pdbentry,const char *name)
{
dbRecordNode *precnode = pdbentry->precnode;
dbInfoNode *pinfo;
pdbentry->pinfonode = NULL;
if (!precnode) return(S_dbLib_recNotFound);
pinfo = (dbInfoNode *)ellFirst(&precnode->infoList);
while (pinfo) {
if (!strcmp(pinfo->name, name)) {
pdbentry->pinfonode = pinfo;
return (0);
}
pinfo = (dbInfoNode *)ellNext(&pinfo->node);
}
return (S_dbLib_infoNotFound);
}
long dbDeleteInfo(DBENTRY *pdbentry)
{
dbRecordNode *precnode = pdbentry->precnode;
dbInfoNode *pinfo = pdbentry->pinfonode;
if (!precnode) return (S_dbLib_recNotFound);
if (!pinfo) return (S_dbLib_infoNotFound);
ellDelete(&precnode->infoList,&pinfo->node);
free(pinfo->name);
free(pinfo->string);
free(pinfo);
pdbentry->pinfonode = NULL;
return (0);
}
const char * dbGetInfoName(DBENTRY *pdbentry)
{
dbInfoNode *pinfo = pdbentry->pinfonode;
if (!pinfo) return (NULL);
return (pinfo->name);
}
const char * dbGetInfoString(DBENTRY *pdbentry)
{
dbInfoNode *pinfo = pdbentry->pinfonode;
if (!pinfo) return (NULL);
return (pinfo->string);
}
long dbPutInfoString(DBENTRY *pdbentry,const char *string)
{
dbInfoNode *pinfo = pdbentry->pinfonode;
char *newstring;
if (!pinfo) return (S_dbLib_infoNotFound);
newstring = realloc(pinfo->string,1+strlen(string));
if (!newstring) return (S_dbLib_outMem);
strcpy(newstring, string);
pinfo->string = newstring;
return (0);
}
long dbPutInfoPointer(DBENTRY *pdbentry, void *pointer)
{
dbInfoNode *pinfo = pdbentry->pinfonode;
if (!pinfo) return (S_dbLib_infoNotFound);
pinfo->pointer = pointer;
return (0);
}
void * dbGetInfoPointer(DBENTRY *pdbentry)
{
dbInfoNode *pinfo = pdbentry->pinfonode;
if (!pinfo) return (NULL);
return (pinfo->pointer);
}
const char * dbGetInfo(DBENTRY *pdbentry,const char *name)
{
if (dbFindInfo(pdbentry, name)) return NULL;
return dbGetInfoString(pdbentry);
}
long dbPutInfo(DBENTRY *pdbentry,const char *name,const char *string)
{
dbInfoNode *pinfo;
dbRecordNode *precnode = pdbentry->precnode;
if (!precnode) return (S_dbLib_recNotFound);
dbFindInfo(pdbentry, name);
pinfo = pdbentry->pinfonode;
if (pinfo) return (dbPutInfoString(pdbentry, string));
/*Create new info node*/
pinfo = calloc(1,sizeof(dbInfoNode));
if (!pinfo) return (S_dbLib_outMem);
pinfo->name = calloc(1,1+strlen(name));
if (!pinfo->name) {
free(pinfo);
return (S_dbLib_outMem);
}
strcpy(pinfo->name, name);
pinfo->string = calloc(1,1+strlen(string));
if (!pinfo->string) {
free(pinfo->name);
free(pinfo);
return (S_dbLib_outMem);
}
strcpy(pinfo->string, string);
ellAdd(&precnode->infoList,&pinfo->node);
pdbentry->pinfonode = pinfo;
return (0);
}
brkTable * dbFindBrkTable(dbBase *pdbbase,const char *name)
{
GPHENTRY *pgph;
pgph = gphFind(pdbbase->pgpHash,name,(void *)&pdbbase->bptList);
if(!pgph) return(NULL);
return((brkTable *)pgph->userPvt);
}
const char * dbGetFieldTypeString(int dbfType)
{
int i;
for (i=0; i < DBF_NTYPES; i++) {
if (pamapdbfType[i].value == dbfType) {
return pamapdbfType[i].strvalue;
}
}
return "BAD_DBF_TYPE";
}
int dbFindFieldType(const char *type)
{
int i;
for (i = 0; i < DBF_NTYPES; i++) {
if (strcmp(type, pamapdbfType[i].strvalue) == 0) {
return pamapdbfType[i].value;
}
}
return -1;
}
dbMenu * dbFindMenu(dbBase *pdbbase,const char *name)
{
GPHENTRY *pgph;
pgph = gphFind(pdbbase->pgpHash,name,(void *)&pdbbase->menuList);
if(!pgph) return(NULL);
return((dbMenu *)pgph->userPvt);
}
char ** dbGetMenuChoices(DBENTRY *pdbentry)
{
dbFldDes *pflddes = pdbentry->pflddes;
if(!pflddes) return(NULL);
switch (pflddes->field_type) {
case DBF_MENU: {
dbMenu *pdbMenu = (dbMenu *)pflddes->ftPvt;
if(!pdbMenu) return(NULL);
return(pdbMenu->papChoiceValue);
}
case DBF_DEVICE: {
dbDeviceMenu *pdbDeviceMenu;
pdbDeviceMenu = dbGetDeviceMenu(pdbentry);
if(!pdbDeviceMenu) return(NULL);
return(pdbDeviceMenu->papChoice);
}
default:
return(NULL);
}
}
int dbGetNMenuChoices(DBENTRY *pdbentry)
{
dbFldDes *pflddes = pdbentry->pflddes;
if(!pflddes) return(-1);
switch (pflddes->field_type) {
case DBF_MENU: {
dbMenu *pdbMenu = (dbMenu *)pflddes->ftPvt;
if(!pdbMenu) return(0);
return(pdbMenu->nChoice);
}
case DBF_DEVICE: {
dbDeviceMenu *pdbDeviceMenu;
pdbDeviceMenu = dbGetDeviceMenu(pdbentry);
if(!pdbDeviceMenu) return(0);
return(pdbDeviceMenu->nChoice);
}
default:
break;
}
return (-1);
}
char * dbGetMenuStringFromIndex(DBENTRY *pdbentry, int index)
{
dbFldDes *pflddes = pdbentry->pflddes;
if(!pflddes) return(NULL);
switch (pflddes->field_type) {
case DBF_MENU: {
dbMenu *pdbMenu = (dbMenu *)pflddes->ftPvt;
if(!pdbMenu) return(NULL);
if(index<0 || index>=pdbMenu->nChoice) return(NULL);
return(pdbMenu->papChoiceValue[index]);
}
case DBF_DEVICE: {
dbDeviceMenu *pdbDeviceMenu;
pdbDeviceMenu = dbGetDeviceMenu(pdbentry);
if(!pdbDeviceMenu) return(NULL);
if(index<0 || index>=pdbDeviceMenu->nChoice) return(NULL);
return(pdbDeviceMenu->papChoice[index]);
}
default:
break;
}
return (NULL);
}
int dbGetMenuIndexFromString(DBENTRY *pdbentry, const char *choice)
{
dbFldDes *pflddes = pdbentry->pflddes;
int ind;
int nChoice = 0;
char **papChoice = NULL;
if(!pflddes) return(-1);
switch (pflddes->field_type) {
case DBF_MENU: {
dbMenu *pdbMenu = (dbMenu *)pflddes->ftPvt;
if(!pdbMenu) return(-1);
papChoice = pdbMenu->papChoiceValue;
nChoice = pdbMenu->nChoice;
break;
}
case DBF_DEVICE: {
dbDeviceMenu *pdbDeviceMenu;
pdbDeviceMenu = dbGetDeviceMenu(pdbentry);
if(!pdbDeviceMenu) return(-1);
papChoice = pdbDeviceMenu->papChoice;
nChoice = pdbDeviceMenu->nChoice;
break;
}
default:
return(-1);
}
if(nChoice<=0 || !papChoice) return(-1);
for(ind=0; ind<nChoice; ind++) {
if(strcmp(choice,papChoice[ind])==0) return(ind);
}
return (-1);
}
drvSup * dbFindDriver(dbBase *pdbbase, const char *name) {
GPHENTRY *pgph = gphFind(pdbbase->pgpHash,name,&pdbbase->drvList);
if (!pgph) return NULL;
return (drvSup *) pgph->userPvt;
}
char * dbGetRelatedField(DBENTRY *psave)
{
DBENTRY dbEntry;
DBENTRY *pdbentry= &dbEntry;
dbFldDes *pflddes;
char *rtnval = NULL;
long status;
pflddes = psave->pflddes;
if(pflddes->field_type !=DBF_DEVICE) return(NULL);
dbCopyEntryContents(psave,pdbentry);
pflddes = pdbentry->pflddes;
status = dbFindField(pdbentry,"INP");
if(status) status = dbFindField(pdbentry,"OUT");
if(!status) rtnval = pdbentry->pflddes->name;
dbFinishEntry(pdbentry);
return(rtnval);
}
linkSup* dbFindLinkSup(dbBase *pdbbase, const char *name) {
GPHENTRY *pgph = gphFind(pdbbase->pgpHash,name,&pdbbase->linkList);
if (!pgph) return NULL;
return (linkSup *) pgph->userPvt;
}
int dbGetNLinks(DBENTRY *pdbentry)
{
dbRecordType *precordType = pdbentry->precordType;
if(!precordType) return(S_dbLib_recordTypeNotFound);
return((int)precordType->no_links);
}
long dbGetLinkField(DBENTRY *pdbentry, int index)
{
dbRecordType *precordType = pdbentry->precordType;
dbFldDes *pflddes;
if (!precordType)
return S_dbLib_recordTypeNotFound;
if (index < 0 || index >= precordType->no_links)
return S_dbLib_badLink;
pdbentry->indfield = precordType->link_ind[index];
pdbentry->pflddes = pflddes = precordType->papFldDes[pdbentry->indfield];
dbGetFieldAddress(pdbentry);
return 0;
}
void dbDumpPath(DBBASE *pdbbase)
{
ELLLIST *ppathList;
dbPathNode *pdbPathNode;
if(!pdbbase) {
fprintf(stderr,"pdbbase not specified\n");
return;
}
ppathList = (ELLLIST *)pdbbase->pathPvt;
if(!ppathList || !(pdbPathNode = (dbPathNode *)ellFirst(ppathList))) {
printf("no path defined\n");
return;
}
while(pdbPathNode) {
printf("%s",pdbPathNode->directory);
pdbPathNode = (dbPathNode *)ellNext(&pdbPathNode->node);
if(pdbPathNode) printf("%s", OSI_PATH_LIST_SEPARATOR);
}
printf("\n");
return;
}
void dbDumpRecord(
dbBase *pdbbase,const char *precordTypename,int level)
{
if(!pdbbase) {
fprintf(stderr,"pdbbase not specified\n");
return;
}
dbWriteRecordFP(pdbbase,stdout,precordTypename,level);
}
void dbDumpMenu(dbBase *pdbbase,const char *menuName)
{
if(!pdbbase) {
fprintf(stderr,"pdbbase not specified\n");
return;
}
dbWriteMenuFP(pdbbase,stdout,menuName);
}
void dbDumpRecordType(DBBASE *pdbbase,const char *recordTypeName)
{
dbRecordType *pdbRecordType;
dbFldDes *pdbFldDes;
int gotMatch;
int i;
if(!pdbbase) {
fprintf(stderr,"pdbbase not specified\n");
return;
}
for(pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList);
pdbRecordType; pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node)) {
if(recordTypeName) {
gotMatch = (strcmp(recordTypeName,pdbRecordType->name)==0)
? TRUE : FALSE;
}else {
gotMatch=TRUE;
}
if(!gotMatch) continue;
printf("name(%s) no_fields(%hd) no_prompt(%hd) no_links(%hd)\n",
pdbRecordType->name,pdbRecordType->no_fields,
pdbRecordType->no_prompt,pdbRecordType->no_links);
printf("index name\tsortind sortname\n");
for(i=0; i<pdbRecordType->no_fields; i++) {
pdbFldDes = pdbRecordType->papFldDes[i];
printf("%5d %s\t%7d %s\n",
i,pdbFldDes->name,
pdbRecordType->sortFldInd[i],pdbRecordType->papsortFldName[i]);
}
printf("link_ind ");
for(i=0; i<pdbRecordType->no_links; i++)
printf(" %hd",pdbRecordType->link_ind[i]);
printf("\n");
printf("indvalFlddes %d name %s\n",pdbRecordType->indvalFlddes,
pdbRecordType->pvalFldDes->name);
printf("rset * %p rec_size %d\n",
(void *)pdbRecordType->prset,pdbRecordType->rec_size);
if(recordTypeName) break;
}
}
void dbDumpField(
DBBASE *pdbbase,const char *recordTypeName,const char *fname)
{
dbRecordType *pdbRecordType;
dbFldDes *pdbFldDes;
int gotMatch;
int i;
dbRecordAttribute *pAttribute;
if(!pdbbase) {
fprintf(stderr,"pdbbase not specified\n");
return;
}
for(pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList);
pdbRecordType; pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node)) {
if(recordTypeName) {
gotMatch = (strcmp(recordTypeName,pdbRecordType->name)==0)
? TRUE : FALSE;
}else {
gotMatch=TRUE;
}
if(!gotMatch) continue;
printf("recordtype(%s) \n",pdbRecordType->name);
for(i=0; i<pdbRecordType->no_fields; i++) {
int j;
pdbFldDes = pdbRecordType->papFldDes[i];
if(fname && strcmp(fname,pdbFldDes->name)!=0) continue;
printf(" %s\n", pdbFldDes->name);
printf("\t prompt: %s\n",
(pdbFldDes->prompt ? pdbFldDes->prompt : ""));
printf("\t extra: %s\n",
(pdbFldDes->extra ? pdbFldDes->extra: ""));
printf("\t indRecordType: %hd\n",pdbFldDes->indRecordType);
printf("\t special: %hd ",pdbFldDes->special);
if(pdbFldDes->special) {
for(j=0; j<SPC_NTYPES; j++) {
if(pamapspcType[j].value == pdbFldDes->special) {
printf("%s",pamapspcType[j].strvalue);
break;
}
}
}
printf("\n");
printf("\t field_type: %s\n",
dbGetFieldTypeString(pdbFldDes->field_type));
printf("\tprocess_passive: %u\n",pdbFldDes->process_passive);
printf("\t property: %u\n",pdbFldDes->prop);
printf("\t base: %d\n",pdbFldDes->base);
if(!pdbFldDes->promptgroup) {
printf("\t promptgroup: %d\n",pdbFldDes->promptgroup);
} else {
printf("\t promptgroup: %s\n",
dbGetPromptGroupNameFromKey(pdbbase, pdbFldDes->promptgroup));
}
printf("\t interest: %hd\n", pdbFldDes->interest);
printf("\t as_level: %d\n",pdbFldDes->as_level);
printf("\t initial: %s\n",
(pdbFldDes->initial ? pdbFldDes->initial : ""));
if(pdbFldDes->field_type==DBF_MENU) {
if(pdbFldDes->ftPvt)
printf("\t\t menu: %s\n",
((dbMenu *)pdbFldDes->ftPvt)->name);
else
printf("\t\t menu: NOT FOUND\n");
}
if(pdbFldDes->field_type==DBF_DEVICE) {
printf("\t ftPvt: %p\n",pdbFldDes->ftPvt);
}
printf("\t size: %hd\n",pdbFldDes->size);
printf("\t offset: %hd\n",pdbFldDes->offset);
}
pAttribute =
(dbRecordAttribute *)ellFirst(&pdbRecordType->attributeList);
while(pAttribute) {
printf("Attribute: name %s value %s\n",
pAttribute->name,pAttribute->value);
pAttribute = (dbRecordAttribute *)ellNext(&pAttribute->node);
}
if(recordTypeName) break;
}
}
void dbDumpDevice(DBBASE *pdbbase,const char *recordTypeName)
{
dbRecordType *pdbRecordType;
devSup *pdevSup;
int gotMatch;
if (recordTypeName) {
if (*recordTypeName == 0 || *recordTypeName == '*')
recordTypeName = 0;
}
if(!pdbbase) {
fprintf(stderr,"pdbbase not specified\n");
return;
}
for(pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList);
pdbRecordType; pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node)) {
if(recordTypeName) {
gotMatch = (strcmp(recordTypeName,pdbRecordType->name)==0)
? TRUE : FALSE;
}else {
gotMatch=TRUE;
}
if(!gotMatch) continue;
printf("recordtype(%s)\n",pdbRecordType->name);
for(pdevSup = (devSup *)ellFirst(&pdbRecordType->devList);
pdevSup; pdevSup = (devSup *)ellNext(&pdevSup->node)) {
printf(" device name: %s\n",pdevSup->name);
printf("\tchoice: %s\n",pdevSup->choice);
printf("\tlink_type: %d\n",pdevSup->link_type);
printf("\tpdset: %p\n",(void *)pdevSup->pdset);
if (pdevSup->pdset) {
static const char *names[] = {
" - report()",
" - init()",
" - init_record()",
" - get_ioint_info()"
};
int i, n = pdevSup->pdset->number;
DEVSUPFUN *pfunc = &pdevSup->pdset->report;
printf("\t number: %d\n", n);
for (i = 0; i < n; ++i, ++pfunc) {
const char *name = (i < NELEMENTS(names)) ? names[i] : "";
printf("\t func %d: %p%s\n", i, (void *)*pfunc, name);
}
}
printf("\tpdsxt: %p\n",(void *)pdevSup->pdsxt);
if (pdevSup->pdsxt) {
printf("\t add_record: %p\n",
(void *)pdevSup->pdsxt->add_record);
printf("\t del_record: %p\n",
(void *)pdevSup->pdsxt->del_record);
}
}
if(recordTypeName) break;
}
}
void dbDumpDriver(DBBASE *pdbbase)
{
if(!pdbbase) {
fprintf(stderr,"pdbbase not specified\n");
return;
}
dbWriteDriverFP(pdbbase,stdout);
}
void dbDumpLink(DBBASE *pdbbase)
{
if(!pdbbase) {
fprintf(stderr,"pdbbase not specified\n");
return;
}
dbWriteLinkFP(pdbbase,stdout);
}
void dbDumpRegistrar(DBBASE *pdbbase)
{
if(!pdbbase) {
fprintf(stderr,"pdbbase not specified\n");
return;
}
dbWriteRegistrarFP(pdbbase,stdout);
}
void dbDumpFunction(DBBASE *pdbbase)
{
if(!pdbbase) {
fprintf(stderr,"pdbbase not specified\n");
return;
}
dbWriteFunctionFP(pdbbase,stdout);
}
void dbDumpVariable(DBBASE *pdbbase)
{
if(!pdbbase) {
fprintf(stderr,"pdbbase not specified\n");
return;
}
dbWriteVariableFP(pdbbase,stdout);
}
void dbDumpBreaktable(DBBASE *pdbbase,const char *name)
{
brkTable *pbrkTable;
brkInt *pbrkInt;
int ind;
if(!pdbbase) {
fprintf(stderr,"pdbbase not specified\n");
return;
}
for(pbrkTable = (brkTable *)ellFirst(&pdbbase->bptList);
pbrkTable; pbrkTable = (brkTable *)ellNext(&pbrkTable->node)) {
if (name && strcmp(name,pbrkTable->name)!=0) continue;
printf("breaktable(%s) {\n",pbrkTable->name);
pbrkInt = pbrkTable->paBrkInt;
for(ind=0; ind < pbrkTable->number; ind++) {
printf("\traw=%f slope=%e eng=%f\n",
pbrkInt->raw, pbrkInt->slope, pbrkInt->eng);
pbrkInt++;
}
printf("}\n");
}
return;
}
static char *bus[LINK_NTYPES] = {
"", /* CONSTANT */
NULL, /* PV_LINK */
"VME",
"CAMAC",
"AB",
"GPIB",
"BITBUS",
NULL, /* MACRO_LINK */
NULL, /* JSON_LINK */
NULL, /* PN_LINK */
NULL, /* DB_LINK */
NULL, /* CA_LINK */
"INST",
"BBGPIB",
"VXI"
};
void dbReportDeviceConfig(dbBase *pdbbase, FILE *report)
{
DBENTRY dbentry, *pdbentry = &dbentry;
long status;
FILE *stream = report ? report : stdout;
if (!pdbbase) {
fprintf(stderr, "dbReportDeviceConfig: pdbbase not specified\n");
return;
}
dbInitEntry(pdbbase,pdbentry);
status = dbFirstRecordType(pdbentry);
while (!status) {
const int nlinks = dbGetNLinks(pdbentry);
status = dbFirstRecord(pdbentry);
while (!status) {
int ilink;
for (ilink=0; ilink<nlinks; ilink++) {
char linkValue[messagesize];
char dtypValue[50];
char cvtValue[40];
struct link *plink;
int linkType;
status = dbGetLinkField(pdbentry, ilink);
if (status)
continue;
plink = pdbentry->pfield;
linkType = plink->type;
if (plink->text) { /* Not yet parsed */
dbLinkInfo linfo;
if (dbParseLink(plink->text, pdbentry->pflddes->field_type, &linfo))
continue;
linkType = linfo.ltype;
if (linkType && bus[linkType])
strncpy(linkValue, plink->text, messagesize-1);
dbFreeLinkInfo(&linfo);
}
else {
strncpy(linkValue, dbGetString(pdbentry), messagesize-1);
}
if (!linkType || !bus[linkType])
continue;
linkValue[messagesize-1] = '\0';
status = dbFindField(pdbentry, "DTYP");
if (status)
break; /* Next record type */
strcpy(dtypValue, dbGetString(pdbentry));
status = dbFindField(pdbentry, "LINR");
if (status) {
cvtValue[0] = 0;
}
else {
if (strcmp(dbGetString(pdbentry), "LINEAR") != 0) {
cvtValue[0] = 0;
}
else {
strcpy(cvtValue,"cvt(");
status = dbFindField(pdbentry, "EGUL");
if (!status)
strcat(cvtValue, dbGetString(pdbentry));
status = dbFindField(pdbentry, "EGUF");
if (!status) {
strcat(cvtValue, ",");
strcat(cvtValue, dbGetString(pdbentry));
}
strcat(cvtValue, ")");
}
}
fprintf(stream,"%-8s %-20s %-20s %-20s %-s\n",
bus[linkType], linkValue, dtypValue,
dbGetRecordName(pdbentry), cvtValue);
break;
}
status = dbNextRecord(pdbentry);
}
status = dbNextRecordType(pdbentry);
}
dbFinishEntry(pdbentry);
finishOutstream(stream);
return;
}