1308 lines
40 KiB
C
1308 lines
40 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 "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 which 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))
|
||
errPrintf(0,__FILE__, __LINE__,
|
||
"Closing file %s",pinputFileNow->filename);
|
||
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)) {
|
||
epicsPrintf("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) {
|
||
if(macCreateHandle(&macHandle,NULL)) {
|
||
epicsPrintf("macCreateHandle error\n");
|
||
status = -1;
|
||
goto cleanup;
|
||
}
|
||
macParseDefns(macHandle,(char *)substitutions,&macPairs);
|
||
if(macPairs ==NULL) {
|
||
macDeleteHandle(macHandle);
|
||
macHandle = NULL;
|
||
} else {
|
||
macInstallMacros(macHandle,macPairs);
|
||
free((void *)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) {
|
||
errPrintf(0, __FILE__, __LINE__,
|
||
"dbRead opening 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)
|
||
epicsPrintf("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, "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))
|
||
errPrintf(0,__FILE__, __LINE__,
|
||
"Closing file %s",pinputFileNow->filename);
|
||
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) {
|
||
epicsPrintf(" in");
|
||
if (pinputFile->path)
|
||
epicsPrintf(" path \"%s\" ",pinputFile->path);
|
||
if (pinputFile->filename) {
|
||
epicsPrintf(" file \"%s\"",pinputFile->filename);
|
||
} else {
|
||
epicsPrintf(" standard input");
|
||
}
|
||
epicsPrintf(" 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) {
|
||
epicsPrintf("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("Illegal 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("Illegal Access Security 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("Illegal '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("Illegal 'pp' value, must be YES/NO/TRUE/FALSE");
|
||
}
|
||
return;
|
||
}
|
||
if(strcmp(name,"interest")==0) {
|
||
if(sscanf(value,"%hd",&pdbFldDes->interest)!=1)
|
||
yyerror("Illegal '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("Illegal 'base' value, must be DECIMAL/HEX");
|
||
}
|
||
return;
|
||
}
|
||
if(strcmp(name,"size")==0) {
|
||
if(sscanf(value,"%hd",&pdbFldDes->size)!=1)
|
||
yyerror("Illegal '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;
|
||
epicsPrintf("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,"recordtype(%s).%s size not specified\n",
|
||
pdbRecordType->name,pdbFldDes->name);
|
||
if((field_type==DBF_NOACCESS) && (pdbFldDes->extra==0))
|
||
fprintf(stderr,"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) {
|
||
epicsPrintf("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) {
|
||
epicsPrintf("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=='{') {
|
||
errlogPrintf("Warning: Record/Alias name '%s' should not begin with '%c'\n", name, c);
|
||
}
|
||
}
|
||
/* any character restrictions */
|
||
if(c < ' ') {
|
||
errlogPrintf("Warning: Record/Alias name '%s' should not contain non-printable 0x%02x\n",
|
||
name, c);
|
||
|
||
} else if(c==' ' || c=='\t' || c=='"' || c=='\'' || c=='.' || c=='$') {
|
||
epicsPrintf(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 */
|
||
epicsPrintf(ERL_ERROR ": Record \"%s\" not found\n", name);
|
||
yyerror(NULL);
|
||
duplicate = TRUE;
|
||
return;
|
||
}
|
||
|
||
status = dbFindRecordType(pdbentry, recordType);
|
||
if (status) {
|
||
epicsPrintf("Record \"%s\" is of unknown type \"%s\"\n",
|
||
name, recordType);
|
||
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) {
|
||
epicsPrintf(ERL_ERROR ": Record \"%s\" of type \"%s\" redefined with new type "
|
||
"\"%s\"\n", name, dbGetRecordTypeName(pdbentry), recordType);
|
||
yyerror(NULL);
|
||
duplicate = TRUE;
|
||
return;
|
||
}
|
||
else if (dbRecordsOnceOnly) {
|
||
epicsPrintf(ERL_ERROR ": Record \"%s\" already defined and dbRecordsOnceOnly set.\n"
|
||
"Used record type \"*\" to append.\n",
|
||
name);
|
||
yyerror(NULL);
|
||
duplicate = TRUE;
|
||
}
|
||
}
|
||
else if (status) {
|
||
epicsPrintf("Can't create record \"%s\" of type \"%s\"\n",
|
||
name, recordType);
|
||
yyerrorAbort(NULL);
|
||
}
|
||
|
||
if (visible)
|
||
dbVisibleRecord(pdbentry);
|
||
}
|
||
|
||
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) {
|
||
epicsPrintf("%s Record \"%s\" does not have a field \"%s\"\n",
|
||
dbGetRecordTypeName(pdbentry), dbGetRecordName(pdbentry), name);
|
||
if(dbGetRecordName(pdbentry)) {
|
||
DBENTRY temp;
|
||
double bestSim = -1.0;
|
||
const dbFldDes *bestFld = NULL;
|
||
dbCopyEntryContents(pdbentry, &temp);
|
||
for(status = dbFirstField(&temp, 0); !status; status = dbNextField(&temp, 0)) {
|
||
double sim = epicsStrSimilarity(name, temp.pflddes->name);
|
||
if(!bestFld || sim > bestSim) {
|
||
bestSim = sim;
|
||
bestFld = temp.pflddes;
|
||
}
|
||
}
|
||
dbFinishEntry(&temp);
|
||
if(bestSim>0.0) {
|
||
epicsPrintf(" Did you mean \"%s\"?", bestFld->name);
|
||
if(bestFld->prompt)
|
||
epicsPrintf(" (%s)", bestFld->prompt);
|
||
epicsPrintf("\n");
|
||
}
|
||
}
|
||
yyerror(NULL);
|
||
return;
|
||
}
|
||
if (pdbentry->indfield == 0) {
|
||
epicsPrintf("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));
|
||
epicsPrintf("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) {
|
||
epicsPrintf("Can't set \"%s\" info \"%s\" to \"%s\"\n",
|
||
dbGetRecordName(pdbentry), name, value);
|
||
yyerror(NULL);
|
||
return;
|
||
}
|
||
}
|
||
|
||
static void dbRecordAlias(char *name)
|
||
{
|
||
DBENTRY *pdbentry;
|
||
tempListNode *ptempListNode;
|
||
long status;
|
||
|
||
if(dbRecordNameValidate(name))
|
||
return;
|
||
|
||
if (duplicate) return;
|
||
ptempListNode = (tempListNode *)ellFirst(&tempList);
|
||
pdbentry = ptempListNode->item;
|
||
status = dbCreateAlias(pdbentry, name);
|
||
if (status) {
|
||
epicsPrintf("Can't create alias \"%s\" for \"%s\"\n",
|
||
name, dbGetRecordName(pdbentry));
|
||
yyerror(NULL);
|
||
return;
|
||
}
|
||
}
|
||
|
||
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)) {
|
||
epicsPrintf("Alias \"%s\" refers to unknown record \"%s\"\n",
|
||
alias, name);
|
||
yyerror(NULL);
|
||
}
|
||
else if (dbCreateAlias(pdbEntry, alias)) {
|
||
epicsPrintf("Can't create alias \"%s\" referring to \"%s\"\n",
|
||
alias, name);
|
||
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);
|
||
}
|