3d70e70640
Reverse of dbRec2Pvt() pacify -D_FORTIFY_SOURCE=3 and __builtin_object_size() as "&precord->common" does not know than common as actually the prefix of a variable sized struct.
579 lines
17 KiB
C
579 lines
17 KiB
C
/*************************************************************************\
|
|
* Copyright (c) 2002 The University of Chicago, 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.
|
|
\*************************************************************************/
|
|
/*dbStaticLibRun.c*/
|
|
|
|
#include <stddef.h>
|
|
#include <stdio.h>
|
|
#include <limits.h>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
|
|
#include "cvtFast.h"
|
|
#include "dbDefs.h"
|
|
#include "ellLib.h"
|
|
#include "epicsPrint.h"
|
|
#include "epicsStdlib.h"
|
|
#include "epicsTypes.h"
|
|
#include "errMdef.h"
|
|
|
|
#include "dbBase.h"
|
|
#include "dbCommonPvt.h"
|
|
#include "dbStaticLib.h"
|
|
#include "dbStaticPvt.h"
|
|
#include "dbAccess.h"
|
|
#include "devSup.h"
|
|
#include "special.h"
|
|
#include "epicsExport.h"
|
|
|
|
int dbConvertStrict = 0;
|
|
epicsExportAddress(int, dbConvertStrict);
|
|
|
|
static long do_nothing(struct dbCommon *precord) { return 0; }
|
|
|
|
/* Dummy DSXT used for soft device supports */
|
|
struct dsxt devSoft_DSXT = {
|
|
do_nothing,
|
|
do_nothing
|
|
};
|
|
|
|
static devSup *pthisDevSup = NULL;
|
|
|
|
void dbInitDevSup(devSup *pdevSup, dset *pdset)
|
|
{
|
|
pdevSup->pdset = pdset;
|
|
if (pdevSup->link_type == CONSTANT)
|
|
pdevSup->pdsxt = &devSoft_DSXT;
|
|
|
|
if (pdset->init) {
|
|
pthisDevSup = pdevSup;
|
|
pdset->init(0);
|
|
pthisDevSup = NULL;
|
|
}
|
|
}
|
|
|
|
void devExtend(dsxt *pdsxt)
|
|
{
|
|
if (!pthisDevSup)
|
|
errlogPrintf("devExtend() called outside of dbInitDevSup()\n");
|
|
else {
|
|
pthisDevSup->pdsxt = pdsxt;
|
|
}
|
|
}
|
|
|
|
/* Ensure that: sizeof(dbCommonPvt) + pdbRecordType->rec_size
|
|
* accounts for necessary alignment of record type struct
|
|
*/
|
|
typedef struct {
|
|
dbCommonPvt prefix;
|
|
/* ensure no padding needed... */
|
|
dbCommon suffix;
|
|
} dbCommonPvtAlignmentTest;
|
|
STATIC_ASSERT(offsetof(dbCommonPvtAlignmentTest, suffix)==sizeof(dbCommonPvt));
|
|
|
|
long dbAllocRecord(DBENTRY *pdbentry,const char *precordName)
|
|
{
|
|
dbRecordType *pdbRecordType = pdbentry->precordType;
|
|
dbRecordNode *precnode = pdbentry->precnode;
|
|
dbFldDes *pflddes;
|
|
int i;
|
|
dbCommonPvt *ppvt;
|
|
dbCommon *precord;
|
|
char *pfield;
|
|
|
|
if(!pdbRecordType) return(S_dbLib_recordTypeNotFound);
|
|
if(!precnode) return(S_dbLib_recNotFound);
|
|
if(pdbRecordType->rec_size == 0) {
|
|
printf("\t*** Did you run x_RegisterRecordDeviceDriver(pdbbase) yet? ***\n");
|
|
epicsPrintf("dbAllocRecord(%s) with %s rec_size = 0\n",
|
|
precordName, pdbRecordType->name);
|
|
return(S_dbLib_noRecSup);
|
|
} else if(pdbRecordType->rec_size<sizeof(*precord)) {
|
|
printf("\t*** Recordtype %s must include \"dbCommon.dbd\"\n", pdbRecordType->name);
|
|
epicsPrintf("dbAllocRecord(%s) with %s rec_size = %d\n",
|
|
precordName, pdbRecordType->name, pdbRecordType->rec_size);
|
|
return(S_dbLib_noRecSup);
|
|
}
|
|
ppvt = dbCalloc(1, sizeof(dbCommonPvt) + pdbRecordType->rec_size);
|
|
precord = dbPvt2Rec(ppvt);
|
|
ppvt->recnode = precnode;
|
|
precord->rdes = pdbRecordType;
|
|
precnode->precord = precord;
|
|
pflddes = pdbRecordType->papFldDes[0];
|
|
if(!pflddes) {
|
|
epicsPrintf("dbAllocRecord pflddes for NAME not found\n");
|
|
return(S_dbLib_flddesNotFound);
|
|
}
|
|
assert(pflddes->offset == 0);
|
|
assert(pflddes->size == sizeof(precord->name));
|
|
if(strlen(precordName) >= sizeof(precord->name)) {
|
|
epicsPrintf("dbAllocRecord: NAME(%s) too long\n",precordName);
|
|
return(S_dbLib_nameLength);
|
|
}
|
|
strcpy(precord->name, precordName);
|
|
for(i=1; i<pdbRecordType->no_fields; i++) {
|
|
|
|
pflddes = pdbRecordType->papFldDes[i];
|
|
if(!pflddes) continue;
|
|
pfield = (char*)precord + pflddes->offset;
|
|
pdbentry->pfield = (void *)pfield;
|
|
pdbentry->pflddes = pflddes;
|
|
pdbentry->indfield = i;
|
|
switch(pflddes->field_type) {
|
|
case DBF_STRING:
|
|
if(pflddes->initial) {
|
|
if(strlen(pflddes->initial) >= pflddes->size) {
|
|
epicsPrintf("initial size > size for %s.%s\n",
|
|
pdbRecordType->name,pflddes->name);
|
|
} else {
|
|
strcpy(pfield,pflddes->initial);
|
|
}
|
|
}
|
|
break;
|
|
case DBF_CHAR:
|
|
case DBF_UCHAR:
|
|
case DBF_SHORT:
|
|
case DBF_USHORT:
|
|
case DBF_LONG:
|
|
case DBF_ULONG:
|
|
case DBF_INT64:
|
|
case DBF_UINT64:
|
|
case DBF_FLOAT:
|
|
case DBF_DOUBLE:
|
|
case DBF_ENUM:
|
|
case DBF_MENU:
|
|
if(pflddes->initial) {
|
|
long status;
|
|
|
|
status = dbPutStringNum(pdbentry,pflddes->initial);
|
|
if(status)
|
|
epicsPrintf(ERL_ERROR " initializing %s.%s initial %s\n",
|
|
pdbRecordType->name,pflddes->name,pflddes->initial);
|
|
}
|
|
break;
|
|
case DBF_DEVICE:
|
|
if(!pflddes->ftPvt) dbGetDeviceMenu(pdbentry);
|
|
break;
|
|
case DBF_INLINK:
|
|
case DBF_OUTLINK:
|
|
case DBF_FWDLINK: {
|
|
DBLINK *plink = (DBLINK *)pfield;
|
|
|
|
plink->type = CONSTANT;
|
|
if(pflddes->initial) {
|
|
plink->text =
|
|
dbCalloc(strlen(pflddes->initial)+1,sizeof(char));
|
|
strcpy(plink->text,pflddes->initial);
|
|
}
|
|
}
|
|
break;
|
|
case DBF_NOACCESS:
|
|
break;
|
|
default:
|
|
epicsPrintf("dbAllocRecord: Illegal field type\n");
|
|
}
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
long dbFreeRecord(DBENTRY *pdbentry)
|
|
{
|
|
dbRecordType *pdbRecordType = pdbentry->precordType;
|
|
dbRecordNode *precnode = pdbentry->precnode;
|
|
|
|
if(!pdbRecordType) return(S_dbLib_recordTypeNotFound);
|
|
if(!precnode) return(S_dbLib_recNotFound);
|
|
if(!precnode->precord) return(S_dbLib_recNotFound);
|
|
free(dbRec2Pvt(precnode->precord));
|
|
precnode->precord = NULL;
|
|
return(0);
|
|
}
|
|
|
|
long dbGetFieldAddress(DBENTRY *pdbentry)
|
|
{
|
|
dbRecordType *pdbRecordType = pdbentry->precordType;
|
|
dbRecordNode *precnode = pdbentry->precnode;
|
|
dbFldDes *pflddes = pdbentry->pflddes;
|
|
|
|
if(!pdbRecordType) return(S_dbLib_recordTypeNotFound);
|
|
if(!precnode) return(S_dbLib_recNotFound);
|
|
if(!pflddes) return(S_dbLib_flddesNotFound);
|
|
if(!precnode->precord) return(0);
|
|
pdbentry->pfield = ((char *)precnode->precord) + pflddes->offset;
|
|
return(0);
|
|
}
|
|
|
|
char *dbRecordName(DBENTRY *pdbentry)
|
|
{
|
|
dbRecordType *pdbRecordType = pdbentry->precordType;
|
|
dbRecordNode *precnode = pdbentry->precnode;
|
|
dbFldDes *pflddes;
|
|
char *precord;
|
|
|
|
if(!pdbRecordType) return(0);
|
|
if(!precnode) return(0);
|
|
if(!precnode->precord) return(0);
|
|
precord = (char *)precnode->precord;
|
|
pflddes = pdbRecordType->papFldDes[0];
|
|
if(!pflddes) return(NULL);
|
|
return(precord + pflddes->offset);
|
|
}
|
|
|
|
int dbIsMacroOk(DBENTRY *pdbentry) { return(FALSE); }
|
|
|
|
DBCORE_API int dbIsDefaultValue(DBENTRY *pdbentry)
|
|
{
|
|
dbFldDes *pflddes = pdbentry->pflddes;
|
|
void *pfield = pdbentry->pfield;
|
|
|
|
if (!pflddes || !pfield)
|
|
return FALSE;
|
|
|
|
switch (pflddes->field_type) {
|
|
case DBF_STRING: {
|
|
char *p = (char *)pfield;
|
|
|
|
return pflddes->initial ? ! strcmp(pflddes->initial, p)
|
|
: ! strlen(p);
|
|
}
|
|
case DBF_CHAR: {
|
|
epicsInt8 field = *(epicsInt8 *)pfield;
|
|
epicsInt8 def;
|
|
|
|
if (!pflddes->initial)
|
|
return field == 0;
|
|
|
|
return ! epicsParseInt8(pflddes->initial, &def, 0, NULL)
|
|
&& field == def;
|
|
}
|
|
case DBF_UCHAR: {
|
|
epicsUInt8 field = *(epicsUInt8 *)pfield;
|
|
epicsUInt8 def;
|
|
|
|
if (!pflddes->initial)
|
|
return field == 0;
|
|
|
|
return ! epicsParseUInt8(pflddes->initial, &def, 0, NULL)
|
|
&& field == def;
|
|
}
|
|
case DBF_SHORT: {
|
|
epicsInt16 field = *(epicsInt16 *)pfield;
|
|
epicsInt16 def;
|
|
|
|
if (!pflddes->initial)
|
|
return field == 0;
|
|
|
|
return ! epicsParseInt16(pflddes->initial, &def, 0, NULL)
|
|
&& field == def;
|
|
}
|
|
case DBF_ENUM:
|
|
case DBF_USHORT: {
|
|
epicsUInt16 field = *(epicsUInt16 *)pfield;
|
|
epicsUInt16 def;
|
|
|
|
if (!pflddes->initial)
|
|
return field == 0;
|
|
|
|
return ! epicsParseUInt16(pflddes->initial, &def, 0, NULL)
|
|
&& field == def;
|
|
}
|
|
case DBF_LONG: {
|
|
epicsInt32 field = *(epicsInt32 *)pfield;
|
|
epicsInt32 def;
|
|
|
|
if (!pflddes->initial)
|
|
return field == 0;
|
|
|
|
return ! epicsParseInt32(pflddes->initial, &def, 0, NULL)
|
|
&& field == def;
|
|
}
|
|
case DBF_ULONG: {
|
|
epicsUInt32 field = *(epicsUInt32 *)pfield;
|
|
epicsUInt32 def;
|
|
|
|
if (!pflddes->initial)
|
|
return field == 0;
|
|
|
|
return ! epicsParseUInt32(pflddes->initial, &def, 0, NULL)
|
|
&& field == def;
|
|
}
|
|
case DBF_INT64: {
|
|
epicsInt64 field = *(epicsInt64 *)pfield;
|
|
epicsInt64 def;
|
|
|
|
if (!pflddes->initial)
|
|
return field == 0;
|
|
|
|
return ! epicsParseInt64(pflddes->initial, &def, 0, NULL)
|
|
&& field == def;
|
|
}
|
|
case DBF_UINT64: {
|
|
epicsUInt64 field = *(epicsUInt64 *)pfield;
|
|
epicsUInt64 def;
|
|
|
|
if (!pflddes->initial)
|
|
return field == 0;
|
|
|
|
return ! epicsParseUInt64(pflddes->initial, &def, 0, NULL)
|
|
&& field == def;
|
|
}
|
|
case DBF_FLOAT: {
|
|
epicsFloat32 field = *(epicsFloat32 *)pfield;
|
|
epicsFloat32 def;
|
|
|
|
if (!pflddes->initial)
|
|
return field == 0;
|
|
|
|
return ! epicsParseFloat32(pflddes->initial, &def, NULL)
|
|
&& field == def;
|
|
}
|
|
case DBF_DOUBLE: {
|
|
epicsFloat64 field = *(epicsFloat64 *)pfield;
|
|
epicsFloat64 def;
|
|
|
|
if (!pflddes->initial)
|
|
return field == 0;
|
|
|
|
return ! epicsParseFloat64(pflddes->initial, &def, NULL)
|
|
&& field == def;
|
|
}
|
|
case DBF_MENU: {
|
|
epicsEnum16 field = *(epicsEnum16 *)pfield;
|
|
epicsEnum16 def;
|
|
int index;
|
|
|
|
if (!pflddes->initial)
|
|
return field == 0;
|
|
|
|
index = dbGetMenuIndexFromString(pdbentry, pflddes->initial);
|
|
if (index < 0) {
|
|
if (epicsParseUInt16(pflddes->initial, &def, 0, NULL))
|
|
return FALSE;
|
|
}
|
|
else
|
|
def = index;
|
|
return field == def;
|
|
}
|
|
case DBF_DEVICE: {
|
|
dbRecordType *precordType = pdbentry->precordType;
|
|
|
|
if (!precordType) {
|
|
epicsPrintf("dbIsDefaultValue: pdbRecordType is NULL??\n");
|
|
return FALSE;
|
|
}
|
|
return ellCount(&precordType->devList) == 0;
|
|
}
|
|
case DBF_INLINK:
|
|
case DBF_OUTLINK:
|
|
case DBF_FWDLINK: {
|
|
struct link *plink = (struct link *)pfield;
|
|
|
|
if (!plink || plink->type != CONSTANT)
|
|
return FALSE;
|
|
|
|
/* These conditions don't make a lot of sense... */
|
|
if (!plink->value.constantStr)
|
|
return TRUE;
|
|
|
|
if (!pflddes->initial) /* Default value for a link field? */
|
|
return FALSE;
|
|
|
|
return !strcmp(plink->value.constantStr, pflddes->initial);
|
|
}
|
|
default:
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
long dbPutStringNum(DBENTRY *pdbentry, const char *pstring)
|
|
{
|
|
dbFldDes *pflddes = pdbentry->pflddes;
|
|
void *pfield = pdbentry->pfield;
|
|
long status;
|
|
epicsUInt64 u64;
|
|
epicsInt64 i64;
|
|
|
|
if (!pfield)
|
|
return S_dbLib_fieldNotFound;
|
|
|
|
/* empty string is the same as writing numeric zero */
|
|
if (pstring[0] == '\0')
|
|
pstring = "0";
|
|
|
|
switch (pflddes->field_type) {
|
|
case DBF_CHAR:
|
|
if (dbConvertStrict)
|
|
return epicsParseInt8(pstring, pfield, 0, NULL);
|
|
goto lax_signed;
|
|
|
|
case DBF_SHORT:
|
|
if (dbConvertStrict)
|
|
return epicsParseInt16(pstring, pfield, 0, NULL);
|
|
goto lax_signed;
|
|
|
|
case DBF_LONG:
|
|
if (dbConvertStrict)
|
|
return epicsParseInt32(pstring, pfield, 0, NULL);
|
|
goto lax_signed;
|
|
|
|
case DBF_INT64:
|
|
if (dbConvertStrict)
|
|
return epicsParseInt64(pstring, pfield, 0, NULL);
|
|
|
|
lax_signed:
|
|
status = epicsParseInt64(pstring, &i64, 0, NULL);
|
|
if (status)
|
|
return status;
|
|
|
|
switch (pflddes->field_type) {
|
|
case DBF_CHAR: *(epicsInt8 *)pfield = (epicsInt8) i64; break;
|
|
case DBF_SHORT: *(epicsInt16*)pfield = (epicsInt16)i64; break;
|
|
case DBF_LONG: *(epicsInt32*)pfield = (epicsInt32)i64; break;
|
|
case DBF_INT64: *(epicsInt64*)pfield = (epicsInt64)i64; break;
|
|
default: break;
|
|
}
|
|
return status;
|
|
|
|
case DBF_UCHAR:
|
|
if (dbConvertStrict)
|
|
return epicsParseUInt8(pstring, pfield, 0, NULL);
|
|
goto lax_unsigned;
|
|
|
|
case DBF_ENUM:
|
|
case DBF_USHORT:
|
|
if (dbConvertStrict)
|
|
return epicsParseUInt16(pstring, pfield, 0, NULL);
|
|
goto lax_unsigned;
|
|
|
|
case DBF_ULONG:
|
|
if (dbConvertStrict)
|
|
return epicsParseUInt32(pstring, pfield, 0, NULL);
|
|
goto lax_unsigned;
|
|
|
|
case DBF_UINT64:
|
|
if (dbConvertStrict)
|
|
return epicsParseUInt64(pstring, pfield, 0, NULL);
|
|
|
|
lax_unsigned:
|
|
status = epicsParseUInt64(pstring, &u64, 0, NULL);
|
|
if (status)
|
|
return status;
|
|
|
|
switch (pflddes->field_type) {
|
|
case DBF_UCHAR: *(epicsUInt8 *)pfield = (epicsInt8) u64; break;
|
|
case DBF_ENUM:
|
|
case DBF_USHORT: *(epicsUInt16*)pfield = (epicsInt16)u64; break;
|
|
case DBF_ULONG: *(epicsUInt32*)pfield = (epicsInt32)u64; break;
|
|
case DBF_UINT64: *(epicsUInt64*)pfield = (epicsInt64)u64; break;
|
|
default: break;
|
|
}
|
|
return status;
|
|
|
|
case DBF_FLOAT:
|
|
return epicsParseFloat32(pstring, pfield, NULL);
|
|
|
|
case DBF_DOUBLE:
|
|
return epicsParseFloat64(pstring, pfield, NULL);
|
|
|
|
case DBF_MENU:
|
|
case DBF_DEVICE: {
|
|
epicsEnum16 *field = (epicsEnum16 *) pfield;
|
|
int index = dbGetMenuIndexFromString(pdbentry, pstring);
|
|
|
|
if (index < 0) {
|
|
epicsEnum16 value;
|
|
long status = epicsParseUInt16(pstring, &value, 0, NULL);
|
|
|
|
if (status) {
|
|
status = S_db_badChoice;
|
|
if(pflddes->field_type==DBF_MENU) {
|
|
dbMenu *pdbMenu = (dbMenu *)pflddes->ftPvt;
|
|
dbMsgPrint(pdbentry, "using menu %s", pdbMenu->name);
|
|
|
|
} else if(pflddes->field_type==DBF_DEVICE) {
|
|
dbMsgPrint(pdbentry, "no such device support for '%s' record type", pdbentry->precordType->name);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
index = dbGetNMenuChoices(pdbentry);
|
|
if (value > index && index > 0 && value < USHRT_MAX)
|
|
return S_dbLib_badField;
|
|
|
|
*field = value;
|
|
}
|
|
else
|
|
*field = index;
|
|
return 0;
|
|
}
|
|
|
|
default:
|
|
return S_dbLib_badField;
|
|
}
|
|
}
|
|
|
|
DBCORE_API int dbGetMenuIndex(DBENTRY *pdbentry)
|
|
{
|
|
dbFldDes *pflddes = pdbentry->pflddes;
|
|
void *pfield = pdbentry->pfield;
|
|
|
|
if (!pflddes || !pfield)
|
|
return -1;
|
|
|
|
switch (pflddes->field_type) {
|
|
case DBF_MENU:
|
|
case DBF_DEVICE:
|
|
return * (epicsEnum16 *) pfield;
|
|
default:
|
|
epicsPrintf("dbGetMenuIndex: Called for field type %d\n",
|
|
pflddes->field_type);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
DBCORE_API long dbPutMenuIndex(DBENTRY *pdbentry, int index)
|
|
{
|
|
dbFldDes *pflddes = pdbentry->pflddes;
|
|
epicsEnum16 *pfield = pdbentry->pfield;
|
|
|
|
if (!pflddes)
|
|
return S_dbLib_flddesNotFound;
|
|
if (!pfield)
|
|
return S_dbLib_fieldNotFound;
|
|
|
|
switch (pflddes->field_type) {
|
|
case DBF_MENU: {
|
|
dbMenu *pdbMenu = (dbMenu *) pflddes->ftPvt;
|
|
|
|
if (!pdbMenu)
|
|
return S_dbLib_menuNotFound;
|
|
if (index < 0 || index >= pdbMenu->nChoice)
|
|
return S_dbLib_badField;
|
|
|
|
*pfield = index;
|
|
return 0;
|
|
}
|
|
|
|
case DBF_DEVICE: {
|
|
dbDeviceMenu *pdbDeviceMenu = dbGetDeviceMenu(pdbentry);
|
|
|
|
if (!pdbDeviceMenu)
|
|
return S_dbLib_menuNotFound;
|
|
if (index < 0 || index >= pdbDeviceMenu->nChoice)
|
|
return S_dbLib_badField;
|
|
|
|
return dbPutString(pdbentry, pdbDeviceMenu->papChoice[index]);
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return S_dbLib_badField;
|
|
}
|