1476 lines
46 KiB
C
1476 lines
46 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.
|
|
\*************************************************************************/
|
|
|
|
/* Author: Marty Kraimer Date: 13JUL95*/
|
|
|
|
/*The routines in this module are serially reusable NOT reentrant*/
|
|
|
|
#include <ctype.h>
|
|
#include <epicsStdlib.h>
|
|
#include <stddef.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
|
|
#include "dbDefs.h"
|
|
#include "dbmf.h"
|
|
#include "ellLib.h"
|
|
#include "epicsPrint.h"
|
|
#include "epicsString.h"
|
|
#include "errMdef.h"
|
|
#include "freeList.h"
|
|
#include "gpHash.h"
|
|
#include "macLib.h"
|
|
|
|
#include "dbBase.h"
|
|
#include "dbFldTypes.h"
|
|
#include "dbStaticLib.h"
|
|
#include "dbStaticPvt.h"
|
|
#include "epicsExport.h"
|
|
#include "link.h"
|
|
#include "special.h"
|
|
#include "iocInit.h"
|
|
|
|
/* This file is included from dbYacc.y
|
|
* Duplicate some declarations to avoid warnings
|
|
* from analysis tools that don't know about this.
|
|
*/
|
|
static int yyerror(char *str);
|
|
static long pvt_yy_parse(void);
|
|
|
|
/*global declarations*/
|
|
char *makeDbdDepends=0;
|
|
|
|
int dbRecordsOnceOnly=0;
|
|
epicsExportAddress(int,dbRecordsOnceOnly);
|
|
|
|
int dbBptNotMonotonic=0;
|
|
epicsExportAddress(int,dbBptNotMonotonic);
|
|
|
|
int dbQuietMacroWarnings=0;
|
|
epicsExportAddress(int,dbQuietMacroWarnings);
|
|
|
|
int dbRecordsAbcSorted=0;
|
|
epicsExportAddress(int,dbRecordsAbcSorted);
|
|
|
|
/*private routines */
|
|
static void yyerrorAbort(char *str);
|
|
static void allocTemp(void *pvoid);
|
|
static void *popFirstTemp(void);
|
|
static void *getLastTemp(void);
|
|
static int db_yyinput(char *buf,int max_size);
|
|
static void dbIncludePrint(void);
|
|
static void dbPathCmd(char *path);
|
|
static void dbAddPathCmd(char *path);
|
|
static void dbIncludeNew(char *include_file);
|
|
static void dbMenuHead(char *name);
|
|
static void dbMenuChoice(char *name,char *value);
|
|
static void dbMenuBody(void);
|
|
|
|
static void dbRecordtypeHead(char *name);
|
|
static void dbRecordtypeEmpty(void);
|
|
static void dbRecordtypeBody(void);
|
|
static void dbRecordtypeFieldHead(char *name,char *type);
|
|
static void dbRecordtypeFieldItem(char *name,char *value);
|
|
static short findOrAddGuiGroup(const char *name);
|
|
|
|
static void dbDevice(char *recordtype,char *linktype,
|
|
char *dsetname,char *choicestring);
|
|
static void dbDriver(char *name);
|
|
static void dbLinkType(char *name, char *jlif_name);
|
|
static void dbRegistrar(char *name);
|
|
static void dbFunction(char *name);
|
|
static void dbVariable(char *name, char *type);
|
|
|
|
static void dbBreakHead(char *name);
|
|
static void dbBreakItem(char *value);
|
|
static void dbBreakBody(void);
|
|
|
|
static void dbRecordHead(char *recordType,char*name,int visible);
|
|
static void dbRecordField(char *name,char *value);
|
|
static void dbRecordBody(void);
|
|
|
|
/*private declarations*/
|
|
#define MY_BUFFER_SIZE 1024
|
|
static char *my_buffer=NULL;
|
|
static char *mac_input_buffer=NULL;
|
|
static char *my_buffer_ptr=NULL;
|
|
static MAC_HANDLE *macHandle = NULL;
|
|
typedef struct inputFile{
|
|
ELLNODE node;
|
|
const char *path;
|
|
const char *filename;
|
|
FILE *fp;
|
|
int line_num;
|
|
}inputFile;
|
|
static ELLLIST inputFileList = ELLLIST_INIT;
|
|
|
|
static inputFile *pinputFileNow = NULL;
|
|
/* The DBBASE most recently allocated/used by dbReadCOM() */
|
|
static DBBASE *savedPdbbase = NULL;
|
|
|
|
typedef struct tempListNode {
|
|
ELLNODE node;
|
|
void *item;
|
|
}tempListNode;
|
|
|
|
static ELLLIST tempList = ELLLIST_INIT;
|
|
static void *freeListPvt = NULL;
|
|
static int duplicate = FALSE;
|
|
|
|
static void yyerrorAbort(char *str)
|
|
{
|
|
yyerror(str);
|
|
yyAbort = TRUE;
|
|
}
|
|
|
|
static void allocTemp(void *pvoid)
|
|
{
|
|
tempListNode *ptempListNode;
|
|
|
|
ptempListNode = freeListCalloc(freeListPvt);
|
|
ptempListNode->item = pvoid;
|
|
ellAdd(&tempList,&ptempListNode->node);
|
|
}
|
|
|
|
static void *popFirstTemp(void)
|
|
{
|
|
tempListNode *ptempListNode;
|
|
void *ptemp = NULL;
|
|
|
|
ptempListNode = (tempListNode *)ellFirst(&tempList);
|
|
if(ptempListNode) {
|
|
ptemp = ptempListNode->item;
|
|
ellDelete(&tempList,(ELLNODE *)ptempListNode);
|
|
freeListFree(freeListPvt,ptempListNode);
|
|
}
|
|
return(ptemp);
|
|
}
|
|
|
|
static void *getLastTemp(void)
|
|
{
|
|
tempListNode *ptempListNode;
|
|
|
|
ptempListNode = (tempListNode *)ellLast(&tempList);
|
|
return(ptempListNode->item);
|
|
}
|
|
|
|
const char *dbOpenFile(DBBASE *pdbbase,const char *filename,FILE **fp)
|
|
{
|
|
ELLLIST *ppathList = (ELLLIST *)pdbbase->pathPvt;
|
|
dbPathNode *pdbPathNode;
|
|
char *fullfilename;
|
|
|
|
*fp = 0;
|
|
if (!filename) return 0;
|
|
if (!ppathList || ellCount(ppathList) == 0 ||
|
|
strchr(filename, '/') || strchr(filename, '\\')) {
|
|
*fp = fopen(filename, "r");
|
|
if (*fp && makeDbdDepends)
|
|
fprintf(stdout, "%s:%s \n", makeDbdDepends, filename);
|
|
return 0;
|
|
}
|
|
pdbPathNode = (dbPathNode *)ellFirst(ppathList);
|
|
while (pdbPathNode) {
|
|
fullfilename = dbMalloc(strlen(pdbPathNode->directory) +
|
|
strlen(filename) + 2);
|
|
strcpy(fullfilename, pdbPathNode->directory);
|
|
strcat(fullfilename, "/");
|
|
strcat(fullfilename, filename);
|
|
*fp = fopen(fullfilename, "r");
|
|
if (*fp && makeDbdDepends)
|
|
fprintf(stdout, "%s:%s \n", makeDbdDepends, fullfilename);
|
|
free((void *)fullfilename);
|
|
if (*fp) return pdbPathNode->directory;
|
|
pdbPathNode = (dbPathNode *)ellNext(&pdbPathNode->node);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void freeInputFileList(void)
|
|
{
|
|
inputFile *pinputFileNow;
|
|
|
|
while((pinputFileNow=(inputFile *)ellFirst(&inputFileList))) {
|
|
if(fclose(pinputFileNow->fp))
|
|
fprintf(stderr, ERL_WARNING
|
|
": Error closing file '%s': %s\n",
|
|
pinputFileNow->filename, strerror(errno));
|
|
free((void *)pinputFileNow->filename);
|
|
ellDelete(&inputFileList,(ELLNODE *)pinputFileNow);
|
|
free((void *)pinputFileNow);
|
|
}
|
|
}
|
|
|
|
static
|
|
int cmp_dbRecordNode(const ELLNODE *lhs, const ELLNODE *rhs)
|
|
{
|
|
dbRecordNode *LHS = (dbRecordNode*)lhs,
|
|
*RHS = (dbRecordNode*)rhs;
|
|
|
|
return strcmp(LHS->recordname, RHS->recordname);
|
|
}
|
|
|
|
static long dbReadCOM(DBBASE **ppdbbase,const char *filename, FILE *fp,
|
|
const char *path,const char *substitutions)
|
|
{
|
|
long status;
|
|
inputFile *pinputFile = NULL;
|
|
char *penv;
|
|
char **macPairs;
|
|
|
|
if (ellCount(&tempList)) {
|
|
fprintf(stderr, ERL_WARNING
|
|
": dbReadCOM: Parser stack dirty %d\n", ellCount(&tempList));
|
|
}
|
|
|
|
if (getIocState() != iocVoid) {
|
|
status = -2;
|
|
goto cleanup;
|
|
}
|
|
|
|
if(*ppdbbase == 0) *ppdbbase = dbAllocBase();
|
|
savedPdbbase = *ppdbbase;
|
|
if(path && strlen(path)>0) {
|
|
dbPath(savedPdbbase,path);
|
|
} else {
|
|
penv = getenv("EPICS_DB_INCLUDE_PATH");
|
|
if(penv) {
|
|
dbPath(savedPdbbase,penv);
|
|
} else {
|
|
dbPath(savedPdbbase,".");
|
|
}
|
|
}
|
|
my_buffer = dbCalloc(MY_BUFFER_SIZE,sizeof(char));
|
|
freeListInitPvt(&freeListPvt,sizeof(tempListNode),100);
|
|
if (substitutions == NULL)
|
|
substitutions = "";
|
|
if(macCreateHandle(&macHandle,NULL)) {
|
|
fprintf(stderr, ERL_ERROR ": macCreateHandle failed\n");
|
|
status = -1;
|
|
goto cleanup;
|
|
}
|
|
macParseDefns(macHandle,substitutions,&macPairs);
|
|
if(macPairs == NULL) {
|
|
macDeleteHandle(macHandle);
|
|
macHandle = NULL;
|
|
} else {
|
|
macInstallMacros(macHandle,macPairs);
|
|
free(macPairs);
|
|
mac_input_buffer = dbCalloc(MY_BUFFER_SIZE,sizeof(char));
|
|
}
|
|
macSuppressWarning(macHandle,dbQuietMacroWarnings);
|
|
pinputFile = dbCalloc(1,sizeof(inputFile));
|
|
if (filename) {
|
|
pinputFile->filename = macEnvExpand(filename);
|
|
}
|
|
if (!fp) {
|
|
FILE *fp1 = 0;
|
|
|
|
if (pinputFile->filename)
|
|
pinputFile->path = dbOpenFile(savedPdbbase, pinputFile->filename, &fp1);
|
|
if (!pinputFile->filename || !fp1) {
|
|
fprintf(stderr, ERL_ERROR
|
|
": Can't open file '%s'\n", pinputFile->filename);
|
|
free((char*)pinputFile->filename);
|
|
free(pinputFile);
|
|
status = -1;
|
|
goto cleanup;
|
|
}
|
|
pinputFile->fp = fp1;
|
|
} else {
|
|
pinputFile->fp = fp;
|
|
fp = NULL;
|
|
}
|
|
pinputFile->line_num = 0;
|
|
pinputFileNow = pinputFile;
|
|
my_buffer[0] = '\0';
|
|
my_buffer_ptr = my_buffer;
|
|
ellAdd(&inputFileList,&pinputFile->node);
|
|
status = pvt_yy_parse();
|
|
|
|
if (ellCount(&tempList) && !yyAbort)
|
|
fprintf(stderr, ERL_WARNING
|
|
": dbReadCOM: Parser stack dirty w/o error. %d\n",
|
|
ellCount(&tempList));
|
|
while (ellCount(&tempList))
|
|
popFirstTemp(); /* Memory leak on parser failure */
|
|
|
|
dbFreePath(savedPdbbase);
|
|
if(!status) { /*add RTYP and VERS as an attribute */
|
|
DBENTRY dbEntry;
|
|
DBENTRY *pdbEntry = &dbEntry;
|
|
long localStatus;
|
|
|
|
dbInitEntry(savedPdbbase,pdbEntry);
|
|
localStatus = dbFirstRecordType(pdbEntry);
|
|
while(!localStatus) {
|
|
localStatus = dbPutRecordAttribute(pdbEntry,"RTYP",
|
|
dbGetRecordTypeName(pdbEntry));
|
|
if(!localStatus) {
|
|
localStatus = dbPutRecordAttribute(pdbEntry,"VERS",
|
|
"none specified");
|
|
}
|
|
if(localStatus) {
|
|
fprintf(stderr,"dbPutRecordAttribute status %ld\n",status);
|
|
} else {
|
|
localStatus = dbNextRecordType(pdbEntry);
|
|
}
|
|
}
|
|
dbFinishEntry(pdbEntry);
|
|
}
|
|
cleanup:
|
|
if(dbRecordsAbcSorted) {
|
|
ELLNODE *cur;
|
|
for(cur = ellFirst(&savedPdbbase->recordTypeList); cur; cur=ellNext(cur))
|
|
{
|
|
dbRecordType *rtype = CONTAINER(cur, dbRecordType, node);
|
|
|
|
ellSortStable(&rtype->recList, &cmp_dbRecordNode);
|
|
}
|
|
}
|
|
if(macHandle) macDeleteHandle(macHandle);
|
|
macHandle = NULL;
|
|
if(mac_input_buffer) free((void *)mac_input_buffer);
|
|
mac_input_buffer = NULL;
|
|
if(freeListPvt) freeListCleanup(freeListPvt);
|
|
freeListPvt = NULL;
|
|
if(my_buffer) free((void *)my_buffer);
|
|
my_buffer = NULL;
|
|
freeInputFileList();
|
|
if(fp)
|
|
fclose(fp);
|
|
return(status);
|
|
}
|
|
|
|
long dbReadDatabase(DBBASE **ppdbbase,const char *filename,
|
|
const char *path,const char *substitutions)
|
|
{return (dbReadCOM(ppdbbase,filename,0,path,substitutions));}
|
|
|
|
long dbReadDatabaseFP(DBBASE **ppdbbase,FILE *fp,
|
|
const char *path,const char *substitutions)
|
|
{return (dbReadCOM(ppdbbase,0,fp,path,substitutions));}
|
|
|
|
static int db_yyinput(char *buf, int max_size)
|
|
{
|
|
size_t l,n;
|
|
char *fgetsRtn;
|
|
|
|
if(yyAbort) return(0);
|
|
if(*my_buffer_ptr==0) {
|
|
while(TRUE) { /*until we get some input*/
|
|
if(macHandle) {
|
|
fgetsRtn = fgets(mac_input_buffer,MY_BUFFER_SIZE,
|
|
pinputFileNow->fp);
|
|
if(fgetsRtn) {
|
|
int exp = macExpandString(macHandle,mac_input_buffer,
|
|
my_buffer,MY_BUFFER_SIZE);
|
|
if (exp < 0) {
|
|
fprintf(stderr, ERL_WARNING
|
|
": '%s' line %d has undefined macros\n",
|
|
pinputFileNow->filename, pinputFileNow->line_num+1);
|
|
}
|
|
}
|
|
} else {
|
|
fgetsRtn = fgets(my_buffer,MY_BUFFER_SIZE,pinputFileNow->fp);
|
|
}
|
|
if(fgetsRtn) break;
|
|
if(fclose(pinputFileNow->fp))
|
|
fprintf(stderr, ERL_WARNING
|
|
": Error closing file '%s': %s\n",
|
|
pinputFileNow->filename, strerror(errno));
|
|
free((void *)pinputFileNow->filename);
|
|
ellDelete(&inputFileList,(ELLNODE *)pinputFileNow);
|
|
free((void *)pinputFileNow);
|
|
pinputFileNow = (inputFile *)ellLast(&inputFileList);
|
|
if(!pinputFileNow) return(0);
|
|
}
|
|
if(dbStaticDebug) fprintf(stderr,"%s",my_buffer);
|
|
pinputFileNow->line_num++;
|
|
my_buffer_ptr = &my_buffer[0];
|
|
}
|
|
l = strlen(my_buffer_ptr);
|
|
n = (l<=max_size ? l : max_size);
|
|
memcpy(buf,my_buffer_ptr,n);
|
|
my_buffer_ptr += n;
|
|
return (int)n;
|
|
}
|
|
|
|
static void dbIncludePrint(void)
|
|
{
|
|
inputFile *pinputFile = pinputFileNow;
|
|
|
|
while (pinputFile) {
|
|
fprintf(stderr, " in");
|
|
if (pinputFile->path)
|
|
fprintf(stderr, " path \"%s\" ",pinputFile->path);
|
|
if (pinputFile->filename) {
|
|
fprintf(stderr, " file \"%s\"",pinputFile->filename);
|
|
} else {
|
|
fprintf(stderr, " standard input");
|
|
}
|
|
fprintf(stderr, " line %d\n",pinputFile->line_num);
|
|
pinputFile = (inputFile *)ellPrevious(&pinputFile->node);
|
|
}
|
|
return;
|
|
}
|
|
|
|
static void dbPathCmd(char *path)
|
|
{
|
|
dbPath(savedPdbbase,path);
|
|
}
|
|
|
|
static void dbAddPathCmd(char *path)
|
|
{
|
|
dbAddPath(savedPdbbase,path);
|
|
}
|
|
|
|
static void dbIncludeNew(char *filename)
|
|
{
|
|
inputFile *pinputFile;
|
|
FILE *fp;
|
|
|
|
pinputFile = dbCalloc(1,sizeof(inputFile));
|
|
pinputFile->filename = macEnvExpand(filename);
|
|
pinputFile->path = dbOpenFile(savedPdbbase, pinputFile->filename, &fp);
|
|
if (!fp) {
|
|
fprintf(stderr, ERL_ERROR ": Can't open include file '%s'\n", filename);
|
|
yyerror(NULL);
|
|
free((void *)pinputFile->filename);
|
|
free((void *)pinputFile);
|
|
return;
|
|
}
|
|
pinputFile->fp = fp;
|
|
ellAdd(&inputFileList,&pinputFile->node);
|
|
pinputFileNow = pinputFile;
|
|
}
|
|
|
|
static void dbMenuHead(char *name)
|
|
{
|
|
dbMenu *pdbMenu;
|
|
GPHENTRY *pgphentry;
|
|
|
|
if (!*name) {
|
|
yyerrorAbort("dbMenuHead: Menu name can't be empty");
|
|
return;
|
|
}
|
|
pgphentry = gphFind(savedPdbbase->pgpHash,name,&savedPdbbase->menuList);
|
|
if(pgphentry) {
|
|
duplicate = TRUE;
|
|
return;
|
|
}
|
|
if(ellCount(&tempList)) yyerrorAbort("dbMenuHead: tempList not empty");
|
|
pdbMenu = dbCalloc(1,sizeof(dbMenu));
|
|
pdbMenu->name = epicsStrDup(name);
|
|
allocTemp(pdbMenu);
|
|
}
|
|
|
|
static void dbMenuChoice(char *name,char *value)
|
|
{
|
|
if (!*name) {
|
|
yyerror("dbMenuChoice: Menu choice name can't be empty");
|
|
return;
|
|
}
|
|
if(duplicate) return;
|
|
allocTemp(epicsStrDup(name));
|
|
allocTemp(epicsStrDup(value));
|
|
}
|
|
|
|
static void dbMenuBody(void)
|
|
{
|
|
dbMenu *pnewMenu;
|
|
dbMenu *pMenu;
|
|
int nChoice;
|
|
int i;
|
|
GPHENTRY *pgphentry;
|
|
|
|
if(duplicate) {
|
|
duplicate = FALSE;
|
|
return;
|
|
}
|
|
pnewMenu = (dbMenu *)popFirstTemp();
|
|
if(!pnewMenu)
|
|
return;
|
|
pnewMenu->nChoice = nChoice = ellCount(&tempList)/2;
|
|
pnewMenu->papChoiceName = dbCalloc(pnewMenu->nChoice,sizeof(char *));
|
|
pnewMenu->papChoiceValue = dbCalloc(pnewMenu->nChoice,sizeof(char *));
|
|
for(i=0; i<nChoice; i++) {
|
|
pnewMenu->papChoiceName[i] = (char *)popFirstTemp();
|
|
pnewMenu->papChoiceValue[i] = (char *)popFirstTemp();
|
|
if(!pnewMenu->papChoiceName[i] || !pnewMenu->papChoiceValue[i])
|
|
return;
|
|
}
|
|
if(ellCount(&tempList)) yyerrorAbort("dbMenuBody: tempList not empty");
|
|
/* Add menu in sorted order */
|
|
pMenu = (dbMenu *)ellFirst(&savedPdbbase->menuList);
|
|
while(pMenu && strcmp(pMenu->name,pnewMenu->name) >0 )
|
|
pMenu = (dbMenu *)ellNext(&pMenu->node);
|
|
if(pMenu)
|
|
ellInsert(&savedPdbbase->menuList,ellPrevious(&pMenu->node),&pnewMenu->node);
|
|
else
|
|
ellAdd(&savedPdbbase->menuList,&pnewMenu->node);
|
|
pgphentry = gphAdd(savedPdbbase->pgpHash,pnewMenu->name,&savedPdbbase->menuList);
|
|
if(!pgphentry) {
|
|
yyerrorAbort("gphAdd failed");
|
|
} else {
|
|
pgphentry->userPvt = pnewMenu;
|
|
}
|
|
}
|
|
|
|
static void dbRecordtypeHead(char *name)
|
|
{
|
|
dbRecordType *pdbRecordType;
|
|
GPHENTRY *pgphentry;
|
|
|
|
if (!*name) {
|
|
yyerrorAbort("dbRecordtypeHead: Recordtype name can't be empty");
|
|
return;
|
|
}
|
|
pgphentry = gphFind(savedPdbbase->pgpHash,name,&savedPdbbase->recordTypeList);
|
|
if(pgphentry) {
|
|
duplicate = TRUE;
|
|
return;
|
|
}
|
|
pdbRecordType = dbCalloc(1,sizeof(dbRecordType));
|
|
pdbRecordType->name = epicsStrDup(name);
|
|
if (savedPdbbase->loadCdefs) ellInit(&pdbRecordType->cdefList);
|
|
if(ellCount(&tempList))
|
|
yyerrorAbort("dbRecordtypeHead tempList not empty");
|
|
allocTemp(pdbRecordType);
|
|
}
|
|
|
|
static void dbRecordtypeFieldHead(char *name,char *type)
|
|
{
|
|
dbFldDes *pdbFldDes;
|
|
int i;
|
|
|
|
if (!*name) {
|
|
yyerrorAbort("dbRecordtypeFieldHead: Field name can't be empty");
|
|
return;
|
|
}
|
|
if(duplicate) return;
|
|
pdbFldDes = dbCalloc(1,sizeof(dbFldDes));
|
|
allocTemp(pdbFldDes);
|
|
pdbFldDes->name = epicsStrDup(name);
|
|
pdbFldDes->as_level = ASL1;
|
|
pdbFldDes->isDevLink = strcmp(pdbFldDes->name, "INP")==0 ||
|
|
strcmp(pdbFldDes->name, "OUT")==0;
|
|
i = dbFindFieldType(type);
|
|
if (i < 0)
|
|
yyerrorAbort("Invalid Field Type");
|
|
pdbFldDes->field_type = i;
|
|
}
|
|
|
|
static short findOrAddGuiGroup(const char *name)
|
|
{
|
|
dbGuiGroup *pdbGuiGroup;
|
|
GPHENTRY *pgphentry;
|
|
pgphentry = gphFind(savedPdbbase->pgpHash, name, &savedPdbbase->guiGroupList);
|
|
if (!pgphentry) {
|
|
pdbGuiGroup = dbCalloc(1,sizeof(dbGuiGroup));
|
|
pdbGuiGroup->name = epicsStrDup(name);
|
|
ellAdd(&savedPdbbase->guiGroupList, &pdbGuiGroup->node);
|
|
pdbGuiGroup->key = ellCount(&savedPdbbase->guiGroupList);
|
|
pgphentry = gphAdd(savedPdbbase->pgpHash, pdbGuiGroup->name, &savedPdbbase->guiGroupList);
|
|
pgphentry->userPvt = pdbGuiGroup;
|
|
}
|
|
return ((dbGuiGroup *)pgphentry->userPvt)->key;
|
|
}
|
|
|
|
static void dbRecordtypeFieldItem(char *name,char *value)
|
|
{
|
|
dbFldDes *pdbFldDes;
|
|
|
|
if(duplicate) return;
|
|
pdbFldDes = (dbFldDes *)getLastTemp();
|
|
if(strcmp(name,"asl")==0) {
|
|
if(strcmp(value,"ASL0")==0) {
|
|
pdbFldDes->as_level = ASL0;
|
|
} else if(strcmp(value,"ASL1")==0) {
|
|
pdbFldDes->as_level = ASL1;
|
|
} else {
|
|
yyerror("Invalid 'asl' value, must be ASL0 or ASL1");
|
|
}
|
|
return;
|
|
}
|
|
if(strcmp(name,"initial")==0) {
|
|
pdbFldDes->initial = epicsStrDup(value);
|
|
return;
|
|
}
|
|
if(strcmp(name,"promptgroup")==0) {
|
|
pdbFldDes->promptgroup = findOrAddGuiGroup(value);
|
|
return;
|
|
}
|
|
if(strcmp(name,"prompt")==0) {
|
|
pdbFldDes->prompt = epicsStrDup(value);
|
|
return;
|
|
}
|
|
if(strcmp(name,"special")==0) {
|
|
int i;
|
|
for(i=0; i<SPC_NTYPES; i++) {
|
|
if(strcmp(value,pamapspcType[i].strvalue)==0) {
|
|
pdbFldDes->special = pamapspcType[i].value;
|
|
return;
|
|
}
|
|
}
|
|
if(sscanf(value,"%hd",&pdbFldDes->special)==1) {
|
|
return;
|
|
}
|
|
yyerror("Invalid 'special' value.");
|
|
return;
|
|
}
|
|
if(strcmp(name,"pp")==0) {
|
|
if((strcmp(value,"YES")==0) || (strcmp(value,"TRUE")==0)) {
|
|
pdbFldDes->process_passive = TRUE;
|
|
} else if((strcmp(value,"NO")==0) || (strcmp(value,"FALSE")==0)) {
|
|
pdbFldDes->process_passive = FALSE;
|
|
} else {
|
|
yyerror("Invalid 'pp' value, must be YES/NO/TRUE/FALSE");
|
|
}
|
|
return;
|
|
}
|
|
if(strcmp(name,"interest")==0) {
|
|
if(sscanf(value,"%hd",&pdbFldDes->interest)!=1)
|
|
yyerror("Invalid 'interest' value, must be integer");
|
|
return;
|
|
}
|
|
if(strcmp(name,"base")==0) {
|
|
if(strcmp(value,"DECIMAL")==0) {
|
|
pdbFldDes->base = CT_DECIMAL;
|
|
} else if(strcmp(value,"HEX")==0) {
|
|
pdbFldDes->base = CT_HEX;
|
|
} else {
|
|
yyerror("Invalid 'base' value, must be DECIMAL/HEX");
|
|
}
|
|
return;
|
|
}
|
|
if(strcmp(name,"size")==0) {
|
|
if(sscanf(value,"%hd",&pdbFldDes->size)!=1)
|
|
yyerror("Invalid 'size' value, must be integer");
|
|
return;
|
|
}
|
|
if(strcmp(name,"extra")==0) {
|
|
pdbFldDes->extra = epicsStrDup(value);
|
|
return;
|
|
}
|
|
if(strcmp(name,"menu")==0) {
|
|
pdbFldDes->ftPvt = (dbMenu *)dbFindMenu(savedPdbbase,value);
|
|
if(!savedPdbbase->ignoreMissingMenus && !pdbFldDes->ftPvt)
|
|
yyerrorAbort("menu not found");
|
|
return;
|
|
}
|
|
if(strcmp(name,"prop")==0) {
|
|
if(strcmp(value, "YES")==0)
|
|
pdbFldDes->prop = 1;
|
|
else
|
|
pdbFldDes->prop = 0;
|
|
return;
|
|
}
|
|
}
|
|
|
|
static void dbRecordtypeCdef(char *text) {
|
|
dbText *pdbCdef;
|
|
tempListNode *ptempListNode;
|
|
dbRecordType *pdbRecordType;
|
|
|
|
if (!savedPdbbase->loadCdefs || duplicate) return;
|
|
ptempListNode = (tempListNode *)ellFirst(&tempList);
|
|
pdbRecordType = ptempListNode->item;
|
|
|
|
pdbCdef = dbCalloc(1,sizeof(dbText));
|
|
if (text[0] == ' ') text++; /* strip leading space if present */
|
|
pdbCdef->text = epicsStrDup(text);
|
|
ellAdd(&pdbRecordType->cdefList, &pdbCdef->node);
|
|
return;
|
|
}
|
|
|
|
static void dbRecordtypeEmpty(void)
|
|
{
|
|
tempListNode *ptempListNode;
|
|
dbRecordType *pdbRecordType;
|
|
|
|
if (duplicate) {
|
|
duplicate = FALSE;
|
|
return;
|
|
}
|
|
|
|
ptempListNode = (tempListNode *)ellFirst(&tempList);
|
|
pdbRecordType = ptempListNode->item;
|
|
fprintf(stderr, ERL_ERROR
|
|
": Declaration of recordtype(%s) preceeded full definition.\n",
|
|
pdbRecordType->name);
|
|
yyerrorAbort(NULL);
|
|
}
|
|
|
|
static void dbRecordtypeBody(void)
|
|
{
|
|
dbRecordType *pdbRecordType;
|
|
dbFldDes *pdbFldDes;
|
|
int i,j,ilink;
|
|
GPHENTRY *pgphentry;
|
|
int no_fields,no_prompt,no_links;
|
|
dbfType field_type;
|
|
char *psortFldNameTemp;
|
|
short psortFldIndTemp;
|
|
char **papsortFldName;
|
|
short *sortFldInd;
|
|
|
|
if(duplicate) {
|
|
duplicate = FALSE;
|
|
return;
|
|
}
|
|
pdbRecordType= (dbRecordType *)popFirstTemp();
|
|
if(!pdbRecordType)
|
|
return;
|
|
pdbRecordType->no_fields = no_fields = ellCount(&tempList);
|
|
pdbRecordType->papFldDes = dbCalloc(no_fields,sizeof(dbFldDes *));
|
|
pdbRecordType->papsortFldName = dbCalloc(no_fields,sizeof(char *));
|
|
pdbRecordType->sortFldInd = dbCalloc(no_fields,sizeof(short));
|
|
no_prompt = no_links = 0;
|
|
for(i=0; i<no_fields; i++) {
|
|
pdbFldDes = (dbFldDes *)popFirstTemp();
|
|
if(!pdbFldDes)
|
|
return;
|
|
pdbFldDes->pdbRecordType = pdbRecordType;
|
|
pdbFldDes->indRecordType = i;
|
|
pdbRecordType->papFldDes[i] = pdbFldDes;
|
|
if(pdbFldDes->promptgroup) no_prompt++;
|
|
field_type = pdbFldDes->field_type;
|
|
if((field_type>=DBF_INLINK) && (field_type<=DBF_FWDLINK))no_links++;
|
|
if((field_type==DBF_STRING) && (pdbFldDes->size==0))
|
|
fprintf(stderr, ERL_ERROR
|
|
": recordtype(%s).%s size not specified\n",
|
|
pdbRecordType->name,pdbFldDes->name);
|
|
if((field_type==DBF_NOACCESS) && (pdbFldDes->extra==0))
|
|
fprintf(stderr, ERL_ERROR
|
|
": recordtype(%s).%s extra not specified\n",
|
|
pdbRecordType->name,pdbFldDes->name);
|
|
}
|
|
if (ellCount(&tempList))
|
|
yyerrorAbort("dbRecordtypeBody: tempList not empty");
|
|
pdbRecordType->no_prompt = no_prompt;
|
|
pdbRecordType->no_links = no_links;
|
|
pdbRecordType->link_ind = dbCalloc(no_links,sizeof(short));
|
|
ilink = 0;
|
|
for(i=0; i<no_fields; i++) {
|
|
pdbFldDes = pdbRecordType->papFldDes[i];
|
|
/* if prompt is null make it a null string */
|
|
if(!pdbFldDes->prompt) pdbFldDes->prompt = dbCalloc(1,sizeof(char));
|
|
field_type = pdbFldDes->field_type;
|
|
if((field_type>=DBF_INLINK) && (field_type<=DBF_FWDLINK))
|
|
pdbRecordType->link_ind[ilink++] = i;
|
|
if(strcmp(pdbFldDes->name,"VAL")==0) {
|
|
pdbRecordType->pvalFldDes = pdbRecordType->papFldDes[i];
|
|
pdbRecordType->indvalFlddes = i;
|
|
}
|
|
pdbRecordType->papsortFldName[i] = pdbFldDes->name;
|
|
pdbRecordType->sortFldInd[i] = i;
|
|
}
|
|
/*Now sort fields. Sorry dumb sort algorithm */
|
|
papsortFldName = pdbRecordType->papsortFldName;
|
|
sortFldInd = pdbRecordType->sortFldInd;
|
|
for(i=0; i<no_fields; i++) {
|
|
for(j=i+1; j<no_fields; j++) {
|
|
if(strcmp(papsortFldName[j],papsortFldName[i])<0 ) {
|
|
psortFldNameTemp = papsortFldName[j];
|
|
psortFldIndTemp = sortFldInd[j];
|
|
papsortFldName[j] = papsortFldName[i];
|
|
sortFldInd[j] = sortFldInd[i];
|
|
papsortFldName[i] = psortFldNameTemp;
|
|
sortFldInd[i] = psortFldIndTemp;
|
|
}
|
|
}
|
|
}
|
|
/*Initialize lists*/
|
|
ellInit(&pdbRecordType->attributeList);
|
|
ellInit(&pdbRecordType->recList);
|
|
ellInit(&pdbRecordType->devList);
|
|
pgphentry = gphAdd(savedPdbbase->pgpHash,pdbRecordType->name,
|
|
&savedPdbbase->recordTypeList);
|
|
if(!pgphentry) {
|
|
yyerrorAbort("gphAdd failed");
|
|
} else {
|
|
pgphentry->userPvt = pdbRecordType;
|
|
}
|
|
ellAdd(&savedPdbbase->recordTypeList,&pdbRecordType->node);
|
|
}
|
|
|
|
static void dbDevice(char *recordtype,char *linktype,
|
|
char *dsetname,char *choicestring)
|
|
{
|
|
devSup *pdevSup;
|
|
dbRecordType *pdbRecordType;
|
|
GPHENTRY *pgphentry;
|
|
int i,link_type;
|
|
pgphentry = gphFind(savedPdbbase->pgpHash,recordtype,&savedPdbbase->recordTypeList);
|
|
if(!pgphentry) {
|
|
fprintf(stderr, ERL_ERROR
|
|
": Record type '%s' not found for device '%s'\n",
|
|
recordtype, choicestring);
|
|
yyerror(NULL);
|
|
return;
|
|
}
|
|
link_type=-1;
|
|
for(i=0; i<LINK_NTYPES; i++) {
|
|
if(strcmp(pamaplinkType[i].strvalue,linktype)==0) {
|
|
link_type = pamaplinkType[i].value;
|
|
break;
|
|
}
|
|
}
|
|
if(link_type==-1) {
|
|
fprintf(stderr, ERL_ERROR
|
|
": Bad link type '%s' for device '%s'\n",
|
|
linktype, choicestring);
|
|
yyerror(NULL);
|
|
return;
|
|
}
|
|
pdbRecordType = (dbRecordType *)pgphentry->userPvt;
|
|
pgphentry = gphFind(savedPdbbase->pgpHash,choicestring,&pdbRecordType->devList);
|
|
if(pgphentry) {
|
|
return;
|
|
}
|
|
pdevSup = dbCalloc(1,sizeof(devSup));
|
|
pdevSup->name = epicsStrDup(dsetname);
|
|
pdevSup->choice = epicsStrDup(choicestring);
|
|
pdevSup->link_type = link_type;
|
|
pgphentry = gphAdd(savedPdbbase->pgpHash,pdevSup->choice,&pdbRecordType->devList);
|
|
if(!pgphentry) {
|
|
yyerrorAbort("gphAdd failed");
|
|
} else {
|
|
pgphentry->userPvt = pdevSup;
|
|
}
|
|
ellAdd(&pdbRecordType->devList,&pdevSup->node);
|
|
}
|
|
|
|
static void dbDriver(char *name)
|
|
{
|
|
drvSup *pdrvSup;
|
|
GPHENTRY *pgphentry;
|
|
|
|
if (!*name) {
|
|
yyerrorAbort("dbDriver: Driver name can't be empty");
|
|
return;
|
|
}
|
|
pgphentry = gphFind(savedPdbbase->pgpHash,name,&savedPdbbase->drvList);
|
|
if(pgphentry) {
|
|
return;
|
|
}
|
|
pdrvSup = dbCalloc(1,sizeof(drvSup));
|
|
pdrvSup->name = epicsStrDup(name);
|
|
pgphentry = gphAdd(savedPdbbase->pgpHash,pdrvSup->name,&savedPdbbase->drvList);
|
|
if(!pgphentry) {
|
|
yyerrorAbort("gphAdd failed");
|
|
}
|
|
pgphentry->userPvt = pdrvSup;
|
|
ellAdd(&savedPdbbase->drvList,&pdrvSup->node);
|
|
}
|
|
|
|
static void dbLinkType(char *name, char *jlif_name)
|
|
{
|
|
linkSup *pLinkSup;
|
|
GPHENTRY *pgphentry;
|
|
|
|
pgphentry = gphFind(savedPdbbase->pgpHash, name, &savedPdbbase->linkList);
|
|
if (pgphentry) {
|
|
return;
|
|
}
|
|
pLinkSup = dbCalloc(1,sizeof(linkSup));
|
|
pLinkSup->name = epicsStrDup(name);
|
|
pLinkSup->jlif_name = epicsStrDup(jlif_name);
|
|
pgphentry = gphAdd(savedPdbbase->pgpHash, pLinkSup->name, &savedPdbbase->linkList);
|
|
if (!pgphentry) {
|
|
yyerrorAbort("gphAdd failed");
|
|
}
|
|
pgphentry->userPvt = pLinkSup;
|
|
ellAdd(&savedPdbbase->linkList, &pLinkSup->node);
|
|
}
|
|
|
|
static void dbRegistrar(char *name)
|
|
{
|
|
dbText *ptext;
|
|
GPHENTRY *pgphentry;
|
|
|
|
if (!*name) {
|
|
yyerrorAbort("dbRegistrar: Registrar name can't be empty");
|
|
return;
|
|
}
|
|
pgphentry = gphFind(savedPdbbase->pgpHash,name,&savedPdbbase->registrarList);
|
|
if(pgphentry) {
|
|
return;
|
|
}
|
|
ptext = dbCalloc(1,sizeof(dbText));
|
|
ptext->text = epicsStrDup(name);
|
|
pgphentry = gphAdd(savedPdbbase->pgpHash,ptext->text,&savedPdbbase->registrarList);
|
|
if(!pgphentry) {
|
|
yyerrorAbort("gphAdd failed");
|
|
}
|
|
pgphentry->userPvt = ptext;
|
|
ellAdd(&savedPdbbase->registrarList,&ptext->node);
|
|
}
|
|
|
|
static void dbFunction(char *name)
|
|
{
|
|
dbText *ptext;
|
|
GPHENTRY *pgphentry;
|
|
|
|
if (!*name) {
|
|
yyerrorAbort("dbFunction: Function name can't be empty");
|
|
return;
|
|
}
|
|
pgphentry = gphFind(savedPdbbase->pgpHash,name,&savedPdbbase->functionList);
|
|
if(pgphentry) {
|
|
return;
|
|
}
|
|
ptext = dbCalloc(1,sizeof(dbText));
|
|
ptext->text = epicsStrDup(name);
|
|
pgphentry = gphAdd(savedPdbbase->pgpHash,ptext->text,&savedPdbbase->functionList);
|
|
if(!pgphentry) {
|
|
yyerrorAbort("gphAdd failed");
|
|
}
|
|
pgphentry->userPvt = ptext;
|
|
ellAdd(&savedPdbbase->functionList,&ptext->node);
|
|
}
|
|
|
|
static void dbVariable(char *name, char *type)
|
|
{
|
|
dbVariableDef *pvar;
|
|
GPHENTRY *pgphentry;
|
|
|
|
if (!*name) {
|
|
yyerrorAbort("dbVariable: Variable name can't be empty");
|
|
return;
|
|
}
|
|
pgphentry = gphFind(savedPdbbase->pgpHash,name,&savedPdbbase->variableList);
|
|
if(pgphentry) {
|
|
return;
|
|
}
|
|
pvar = dbCalloc(1,sizeof(dbVariableDef));
|
|
pvar->name = epicsStrDup(name);
|
|
pvar->type = epicsStrDup(type);
|
|
pgphentry = gphAdd(savedPdbbase->pgpHash,pvar->name,&savedPdbbase->variableList);
|
|
if(!pgphentry) {
|
|
yyerrorAbort("gphAdd failed");
|
|
}
|
|
pgphentry->userPvt = pvar;
|
|
ellAdd(&savedPdbbase->variableList,&pvar->node);
|
|
}
|
|
|
|
static void dbBreakHead(char *name)
|
|
{
|
|
brkTable *pbrkTable;
|
|
GPHENTRY *pgphentry;
|
|
|
|
if (!*name) {
|
|
yyerrorAbort("dbBreakHead: Breaktable name can't be empty");
|
|
return;
|
|
}
|
|
pgphentry = gphFind(savedPdbbase->pgpHash,name,&savedPdbbase->bptList);
|
|
if(pgphentry) {
|
|
duplicate = TRUE;
|
|
return;
|
|
}
|
|
pbrkTable = dbCalloc(1,sizeof(brkTable));
|
|
pbrkTable->name = epicsStrDup(name);
|
|
if(ellCount(&tempList)) yyerrorAbort("dbBreakHead:tempList not empty");
|
|
allocTemp(pbrkTable);
|
|
}
|
|
|
|
static void dbBreakItem(char *value)
|
|
{
|
|
double dummy;
|
|
if (duplicate) return;
|
|
if (epicsScanDouble(value, &dummy) != 1) {
|
|
yyerrorAbort("Non-numeric value in breaktable");
|
|
}
|
|
allocTemp(epicsStrDup(value));
|
|
}
|
|
|
|
static void dbBreakBody(void)
|
|
{
|
|
brkTable *pnewbrkTable;
|
|
brkInt *paBrkInt;
|
|
brkTable *pbrkTable;
|
|
int number, down=0;
|
|
int i;
|
|
GPHENTRY *pgphentry;
|
|
|
|
if (duplicate) {
|
|
duplicate = FALSE;
|
|
return;
|
|
}
|
|
pnewbrkTable = (brkTable *)popFirstTemp();
|
|
if(!pnewbrkTable)
|
|
return;
|
|
number = ellCount(&tempList);
|
|
if (number % 2) {
|
|
yyerrorAbort("breaktable: Raw value missing");
|
|
return;
|
|
}
|
|
number /= 2;
|
|
if (number < 2) {
|
|
yyerrorAbort("breaktable: Must have at least two points!");
|
|
return;
|
|
}
|
|
pnewbrkTable->number = number;
|
|
pnewbrkTable->paBrkInt = paBrkInt = dbCalloc(number, sizeof(brkInt));
|
|
for (i=0; i<number; i++) {
|
|
char *str;
|
|
|
|
str = (char *)popFirstTemp();
|
|
if(!str)
|
|
return;
|
|
(void) epicsScanDouble(str, &paBrkInt[i].raw);
|
|
free(str);
|
|
|
|
str = (char *)popFirstTemp();
|
|
if(!str)
|
|
return;
|
|
(void) epicsScanDouble(str, &paBrkInt[i].eng);
|
|
free(str);
|
|
}
|
|
/* Compute slopes */
|
|
for (i=0; i<number-1; i++) {
|
|
double slope =
|
|
(paBrkInt[i+1].eng - paBrkInt[i].eng)/
|
|
(paBrkInt[i+1].raw - paBrkInt[i].raw);
|
|
if (!dbBptNotMonotonic && slope == 0) {
|
|
yyerrorAbort("breaktable slope is zero");
|
|
return;
|
|
}
|
|
if (i == 0) {
|
|
down = (slope < 0);
|
|
} else if (!dbBptNotMonotonic && down != (slope < 0)) {
|
|
yyerrorAbort("breaktable slope changes sign");
|
|
return;
|
|
}
|
|
paBrkInt[i].slope = slope;
|
|
}
|
|
/* Continue with last slope beyond the final point */
|
|
paBrkInt[number-1].slope = paBrkInt[number-2].slope;
|
|
/* Add brkTable in sorted order */
|
|
pbrkTable = (brkTable *)ellFirst(&savedPdbbase->bptList);
|
|
while (pbrkTable) {
|
|
if (strcmp(pbrkTable->name, pnewbrkTable->name) > 0) {
|
|
ellInsert(&savedPdbbase->bptList, ellPrevious((ELLNODE *)pbrkTable),
|
|
(ELLNODE *)pnewbrkTable);
|
|
break;
|
|
}
|
|
pbrkTable = (brkTable *)ellNext(&pbrkTable->node);
|
|
}
|
|
if (!pbrkTable) ellAdd(&savedPdbbase->bptList, &pnewbrkTable->node);
|
|
pgphentry = gphAdd(savedPdbbase->pgpHash,pnewbrkTable->name,&savedPdbbase->bptList);
|
|
if (!pgphentry) {
|
|
yyerrorAbort("dbBreakBody: gphAdd failed");
|
|
return;
|
|
}
|
|
pgphentry->userPvt = pnewbrkTable;
|
|
}
|
|
|
|
static
|
|
int dbRecordNameValidate(const char *name)
|
|
{
|
|
size_t i=0u;
|
|
const char *pos = name;
|
|
|
|
if (!*name) {
|
|
yyerrorAbort(ERL_ERROR ": Record/Alias name can't be empty");
|
|
return 1;
|
|
}
|
|
|
|
for(; *pos; i++, pos++) {
|
|
unsigned char c = *pos;
|
|
if(i==0) {
|
|
/* first character restrictions */
|
|
if(c=='-' || c=='+' || c=='[' || c=='{') {
|
|
fprintf(stderr, ERL_WARNING
|
|
": Record/Alias name '%s' should not begin with '%c'\n",
|
|
name, c);
|
|
}
|
|
}
|
|
/* any character restrictions */
|
|
if(c < ' ') {
|
|
fprintf(stderr, ERL_WARNING
|
|
": Record/Alias name '%s' contains non-printable 0x%02x\n",
|
|
name, c);
|
|
|
|
} else if(c==' ' || c=='\t' || c=='"' || c=='\'' || c=='.' || c=='$') {
|
|
fprintf(stderr, ERL_ERROR
|
|
": Bad character '%c' in Record/Alias name \"%s\"\n",
|
|
c, name);
|
|
yyerrorAbort(NULL);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void dbRecordHead(char *recordType, char *name, int visible)
|
|
{
|
|
DBENTRY *pdbentry;
|
|
long status;
|
|
|
|
if(dbRecordNameValidate(name))
|
|
return;
|
|
|
|
pdbentry = dbAllocEntry(savedPdbbase);
|
|
if (ellCount(&tempList))
|
|
yyerrorAbort("dbRecordHead: tempList not empty");
|
|
allocTemp(pdbentry);
|
|
|
|
if (recordType[0] == '*' && recordType[1] == 0) {
|
|
status = dbFindRecord(pdbentry, name);
|
|
if (status == 0)
|
|
return; /* done */
|
|
fprintf(stderr, ERL_ERROR ": Record '%s' not found\n", name);
|
|
yyerror(NULL);
|
|
duplicate = TRUE;
|
|
return;
|
|
}
|
|
|
|
if (recordType[0] == '#' && recordType[1] == 0) {
|
|
status = dbFindRecord(pdbentry, name);
|
|
if (status == 0) {
|
|
dbDeleteRecord(pdbentry);
|
|
} else {
|
|
fprintf(stderr, ERL_WARNING
|
|
": Record '%s' not found, can't delete\n"
|
|
" at file '%s', line %d\n",
|
|
name, pinputFileNow->filename, pinputFileNow->line_num);
|
|
}
|
|
popFirstTemp();
|
|
dbFreeEntry(pdbentry);
|
|
duplicate = TRUE;
|
|
return;
|
|
}
|
|
status = dbFindRecordType(pdbentry, recordType);
|
|
if (status) {
|
|
fprintf(stderr, ERL_ERROR
|
|
": Record type '%s' for record '%s' not found\n",
|
|
recordType, name);
|
|
yyerrorAbort(NULL);
|
|
return;
|
|
}
|
|
|
|
/*Duplicate records are ok if the same type */
|
|
|
|
status = dbCreateRecord(pdbentry,name);
|
|
if (status == S_dbLib_recExists) {
|
|
if (strcmp(recordType, dbGetRecordTypeName(pdbentry)) != 0) {
|
|
fprintf(stderr, ERL_ERROR
|
|
": %s record '%s' already exists, can't load %s record\n",
|
|
recordType, name, dbGetRecordTypeName(pdbentry));
|
|
yyerror(NULL);
|
|
return;
|
|
}
|
|
else if (dbRecordsOnceOnly) {
|
|
fprintf(stderr, ERL_ERROR
|
|
": Record '%s' already defined; dbRecordsOnceOnly is set,\n"
|
|
" so can't modify record.\n", name);
|
|
yyerror(NULL);
|
|
duplicate = TRUE;
|
|
}
|
|
}
|
|
else if (status) {
|
|
fprintf(stderr, ERL_ERROR
|
|
": Can't create %s record '%s'\n",
|
|
recordType, name);
|
|
yyerrorAbort(NULL);
|
|
}
|
|
|
|
if (visible)
|
|
dbVisibleRecord(pdbentry);
|
|
}
|
|
|
|
static const char* dbFieldConfusionMap [] = {
|
|
"INP","OUT",
|
|
"DOL","INP",
|
|
"ZNAM","ZRST",
|
|
"ONAM","ONST",
|
|
"INPA-J","DOL0-9",
|
|
"INPK-P","DOLA-F",
|
|
"INP0-9","INPA-J",
|
|
NULL
|
|
};
|
|
|
|
static void dbRecordField(char *name,char *value)
|
|
{
|
|
DBENTRY *pdbentry;
|
|
tempListNode *ptempListNode;
|
|
long status;
|
|
|
|
if (duplicate) return;
|
|
ptempListNode = (tempListNode *)ellFirst(&tempList);
|
|
pdbentry = ptempListNode->item;
|
|
status = dbFindField(pdbentry,name);
|
|
if (status) {
|
|
fprintf(stderr, ERL_ERROR
|
|
": %s record '%s' doesn't have a field '%s'\n",
|
|
dbGetRecordTypeName(pdbentry), dbGetRecordName(pdbentry), name);
|
|
if(dbGetRecordName(pdbentry)) {
|
|
DBENTRY temp;
|
|
double bestSim = -1.0;
|
|
const dbFldDes *bestFld = NULL;
|
|
int i;
|
|
const char* fld;
|
|
dbCopyEntryContents(pdbentry, &temp);
|
|
for(i = 0; (fld = dbFieldConfusionMap[i]) != NULL; i++) {
|
|
char buf[10];
|
|
const char* guess = NULL;
|
|
size_t l = strlen(fld);
|
|
if (l >= 3 && fld[l-2] == '-' &&
|
|
strncmp(name, fld, l-3) == 0 &&
|
|
name[l-3] >= fld[l-3] &&
|
|
name[l-3] <= fld[l-1])
|
|
{
|
|
/* range map */
|
|
size_t l2 = strlen(dbFieldConfusionMap[i^1]);
|
|
memset(buf, 0, sizeof(buf));
|
|
strncpy(buf, dbFieldConfusionMap[i^1], sizeof(buf)-1);
|
|
buf[l2-3] += name[l-3] - fld[l-3];
|
|
buf[l2-2] = 0;
|
|
guess = buf;
|
|
} else if (strcmp(name, fld) == 0) {
|
|
/* simple map */
|
|
guess = dbFieldConfusionMap[i^1];
|
|
}
|
|
if (guess && dbFindFieldPart(&temp, &guess) == 0) {
|
|
/* guessed field exists */
|
|
bestFld = temp.pflddes;
|
|
break;
|
|
}
|
|
}
|
|
if (!bestFld) {
|
|
/* no map found, use weighted lexical similarity */
|
|
char quote = 0;
|
|
if (*value == '"' || *value == '\'') {
|
|
quote = *value++;
|
|
}
|
|
for (status = dbFirstField(&temp, 0); !status; status = dbNextField(&temp, 0)) {
|
|
if (temp.pflddes->special == SPC_NOMOD ||
|
|
temp.pflddes->special == SPC_DBADDR) /* cannot be configured */
|
|
continue;
|
|
double sim = epicsStrSimilarity(name, temp.pflddes->name);
|
|
if (!temp.pflddes->promptgroup)
|
|
sim *= 0.5; /* no prompt: unlikely */
|
|
if (temp.pflddes->interest)
|
|
sim *= 1.0 - 0.1 * temp.pflddes->interest; /* 10% less likely per interest level */
|
|
|
|
long status = 0;
|
|
char* end = "e;
|
|
if (*value != quote) {
|
|
/* value given, check match to field type */
|
|
switch (temp.pflddes->field_type) {
|
|
epicsAny dummy;
|
|
case DBF_CHAR:
|
|
status = epicsParseInt8(value, &dummy.int8, 0, &end);
|
|
break;
|
|
case DBF_UCHAR:
|
|
status = epicsParseUInt8(value, &dummy.uInt8, 0, &end);
|
|
break;
|
|
case DBF_SHORT:
|
|
status = epicsParseInt16(value, &dummy.int16, 0, &end);
|
|
break;
|
|
case DBF_USHORT:
|
|
status = epicsParseUInt16(value, &dummy.uInt16, 0, &end);
|
|
break;
|
|
case DBF_LONG:
|
|
status = epicsParseInt32(value, &dummy.int32, 0, &end);
|
|
break;
|
|
case DBF_ULONG:
|
|
status = epicsParseUInt32(value, &dummy.uInt32, 0, &end);
|
|
break;
|
|
case DBF_INT64:
|
|
status = epicsParseInt64(value, &dummy.int64, 0, &end);
|
|
break;
|
|
case DBF_UINT64:
|
|
status = epicsParseUInt64(value, &dummy.uInt64, 0, &end);
|
|
break;
|
|
case DBF_FLOAT:
|
|
status = epicsParseFloat(value, &dummy.float32, &end);
|
|
break;
|
|
case DBF_DOUBLE:
|
|
status = epicsParseDouble(value, &dummy.float64, &end);
|
|
break;
|
|
case DBF_ENUM:
|
|
case DBF_MENU:
|
|
case DBF_DEVICE:
|
|
/* TODO */
|
|
default:
|
|
break;
|
|
}
|
|
if (status || *end != quote) {
|
|
sim *= 0.1; /* value type does not match field type: unlikely */
|
|
}
|
|
}
|
|
if (sim > bestSim) {
|
|
bestSim = sim;
|
|
bestFld = temp.pflddes;
|
|
}
|
|
}
|
|
}
|
|
dbFinishEntry(&temp);
|
|
if (bestFld) {
|
|
fprintf(stderr, " Did you mean \"%s\"?", bestFld->name);
|
|
if(bestFld->prompt)
|
|
fprintf(stderr, " (%s)", bestFld->prompt);
|
|
fprintf(stderr, "\n");
|
|
}
|
|
}
|
|
yyerror(NULL);
|
|
return;
|
|
}
|
|
if (pdbentry->indfield == 0) {
|
|
fprintf(stderr, ERL_ERROR
|
|
": Can't set 'NAME' field of record '%s'\n",
|
|
dbGetRecordName(pdbentry));
|
|
yyerror(NULL);
|
|
return;
|
|
}
|
|
|
|
if (*value == '"' || *value == '\'') {
|
|
/* jsonSTRING values still have their quotes */
|
|
value++;
|
|
value[strlen(value) - 1] = 0;
|
|
dbTranslateEscape(value, value); /* in-place; safe & legal */
|
|
}
|
|
|
|
status = dbPutString(pdbentry,value);
|
|
if (status) {
|
|
char msg[128];
|
|
|
|
errSymLookup(status, msg, sizeof(msg));
|
|
fprintf(stderr, ERL_ERROR
|
|
": Can't set '%s.%s' to '%s' %s : %s\n",
|
|
dbGetRecordName(pdbentry), name, value,
|
|
pdbentry->message ? pdbentry->message : "", msg);
|
|
dbPutStringSuggest(pdbentry, value);
|
|
yyerror(NULL);
|
|
return;
|
|
}
|
|
}
|
|
|
|
static void dbRecordInfo(char *name, char *value)
|
|
{
|
|
DBENTRY *pdbentry;
|
|
tempListNode *ptempListNode;
|
|
long status;
|
|
|
|
if (!*name) {
|
|
yyerrorAbort("dbRecordInfo: Info item name can't be empty");
|
|
return;
|
|
}
|
|
if (duplicate) return;
|
|
ptempListNode = (tempListNode *)ellFirst(&tempList);
|
|
pdbentry = ptempListNode->item;
|
|
|
|
if (*value == '"' || *value == '\'') {
|
|
/* jsonSTRING values still have their quotes */
|
|
value++;
|
|
value[strlen(value) - 1] = 0;
|
|
dbTranslateEscape(value, value); /* in-place; safe & legal */
|
|
}
|
|
|
|
status = dbPutInfo(pdbentry,name,value);
|
|
if (status) {
|
|
fprintf(stderr, ERL_ERROR
|
|
": Can't set '%s' info(\"%s\") to '%s'\n",
|
|
dbGetRecordName(pdbentry), name, value);
|
|
yyerror(NULL);
|
|
return;
|
|
}
|
|
}
|
|
|
|
static long createAlias(DBENTRY *pdbentry, const char *alias)
|
|
{
|
|
DBENTRY tempEntry;
|
|
long status;
|
|
dbRecordNode *precnode = pdbentry->precnode;
|
|
|
|
if (precnode->aliasedRecnode) precnode = precnode->aliasedRecnode;
|
|
dbInitEntry(pdbentry->pdbbase, &tempEntry);
|
|
status = dbFindRecord(&tempEntry, alias);
|
|
if (status == 0) {
|
|
if (tempEntry.precnode->aliasedRecnode != precnode) {
|
|
if (tempEntry.precnode->aliasedRecnode)
|
|
fprintf(stderr, ERL_ERROR
|
|
": Alias '%s' for record '%s' already aliases '%s'\n",
|
|
alias, dbGetRecordName(pdbentry),
|
|
tempEntry.precnode->aliasedRecnode->recordname);
|
|
else
|
|
fprintf(stderr, ERL_ERROR
|
|
": Alias '%s' for record '%s' is already a record name.\n",
|
|
alias, dbGetRecordName(pdbentry));
|
|
status = S_dbLib_recExists;
|
|
} else if (dbRecordsOnceOnly) {
|
|
fprintf(stderr, ERL_ERROR
|
|
": Alias '%s' already defined; dbRecordsOnceOnly is set.\n",
|
|
alias);
|
|
status = S_dbLib_recExists;
|
|
}
|
|
} else {
|
|
status = dbCreateAlias(pdbentry, alias);
|
|
}
|
|
dbFinishEntry(&tempEntry);
|
|
return status;
|
|
}
|
|
|
|
static void dbRecordAlias(char *name)
|
|
{
|
|
DBENTRY *pdbentry;
|
|
tempListNode *ptempListNode;
|
|
if(dbRecordNameValidate(name))
|
|
return;
|
|
|
|
if (duplicate) return;
|
|
ptempListNode = (tempListNode *)ellFirst(&tempList);
|
|
pdbentry = ptempListNode->item;
|
|
|
|
if (createAlias(pdbentry, name) != 0) {
|
|
yyerror(NULL);
|
|
}
|
|
}
|
|
|
|
static void dbAlias(char *name, char *alias)
|
|
{
|
|
DBENTRY dbEntry;
|
|
DBENTRY *pdbEntry = &dbEntry;
|
|
|
|
if(dbRecordNameValidate(alias) || dbRecordNameValidate(name))
|
|
return;
|
|
|
|
dbInitEntry(savedPdbbase, pdbEntry);
|
|
if (dbFindRecord(pdbEntry, name)) {
|
|
fprintf(stderr, ERL_ERROR
|
|
": Alias '%s' names an unknown record '%s'\n",
|
|
alias, name);
|
|
yyerror(NULL);
|
|
}
|
|
else if (createAlias(pdbEntry, alias) != 0) {
|
|
yyerror(NULL);
|
|
}
|
|
dbFinishEntry(pdbEntry);
|
|
}
|
|
|
|
static void dbRecordBody(void)
|
|
{
|
|
DBENTRY *pdbentry;
|
|
|
|
if (duplicate) {
|
|
duplicate = FALSE;
|
|
return;
|
|
}
|
|
pdbentry = (DBENTRY *)popFirstTemp();
|
|
if (ellCount(&tempList))
|
|
yyerrorAbort("dbRecordBody: tempList not empty");
|
|
dbFreeEntry(pdbentry);
|
|
}
|