From 8c60e7e2cb40efc1f1ea300b03bd327b6bd500d0 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 1 Aug 2014 11:28:10 -0400 Subject: [PATCH 01/12] track INP/OUT field in struct dbFldDes avoid string comparisons --- src/ioc/db/dbAccess.c | 3 +-- src/ioc/dbStatic/dbBase.h | 1 + src/ioc/dbStatic/dbLexRoutines.c | 2 ++ src/ioc/dbStatic/dbStaticLib.c | 3 +-- src/ioc/misc/iocInit.c | 3 +-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/ioc/db/dbAccess.c b/src/ioc/db/dbAccess.c index bf0f00dec..2abbe8ea2 100644 --- a/src/ioc/db/dbAccess.c +++ b/src/ioc/db/dbAccess.c @@ -942,8 +942,7 @@ static long dbPutFieldLink(DBADDR *paddr, if (status) goto finish; isDevLink = ellCount(&precord->rdes->devList) > 0 && - (strcmp(pfldDes->name, "INP") == 0 || - strcmp(pfldDes->name, "OUT") == 0); + pfldDes->isDevLink; dbLockSetGblLock(); dbLockSetRecordLock(precord); diff --git a/src/ioc/dbStatic/dbBase.h b/src/ioc/dbStatic/dbBase.h index 691953442..2aa9a6084 100644 --- a/src/ioc/dbStatic/dbBase.h +++ b/src/ioc/dbStatic/dbBase.h @@ -81,6 +81,7 @@ typedef struct dbFldDes{ /* field description */ ctType base; /*base for integer to string conversions*/ short promptgroup; /*prompt, i.e. gui group */ short interest; /*interest level */ + short isDevLink; /* true for INP/OUT fields */ asLevel as_level; /*access security level */ char *initial; /*initial value */ /*If (DBF_MENU,DBF_DEVICE) ftPvt is (pdbMenu,pdbDeviceMenu) */ diff --git a/src/ioc/dbStatic/dbLexRoutines.c b/src/ioc/dbStatic/dbLexRoutines.c index cf2406556..063701751 100644 --- a/src/ioc/dbStatic/dbLexRoutines.c +++ b/src/ioc/dbStatic/dbLexRoutines.c @@ -484,6 +484,8 @@ static void dbRecordtypeFieldHead(char *name,char *type) allocTemp(pdbFldDes); pdbFldDes->name = epicsStrDup(name); pdbFldDes->as_level = ASL1; + pdbFldDes->isDevLink = strcmp(pdbFldDes->name, "INP")==0 || + strcmp(pdbFldDes->name, "OUT")==0; for(i=0; ifield_type = pamapdbfType[i].value; diff --git a/src/ioc/dbStatic/dbStaticLib.c b/src/ioc/dbStatic/dbStaticLib.c index 43a371b7a..9f953a673 100644 --- a/src/ioc/dbStatic/dbStaticLib.c +++ b/src/ioc/dbStatic/dbStaticLib.c @@ -2106,8 +2106,7 @@ long dbPutString(DBENTRY *pdbentry,const char *pstring) goto done; } - if (strcmp(pflddes->name, "INP") == 0 || - strcmp(pflddes->name, "OUT") == 0) { + if (pflddes->isDevLink) { status = setLinkType(pdbentry); /* This uses DTYP to look up and set plink->type, necessary for default DTYP */ if (status) { errMessage(status,"in dbPutString from setLinkType"); diff --git a/src/ioc/misc/iocInit.c b/src/ioc/misc/iocInit.c index 73f8ebd00..1e224d2f5 100644 --- a/src/ioc/misc/iocInit.c +++ b/src/ioc/misc/iocInit.c @@ -484,8 +484,7 @@ static void doResolveLinks(dbRecordType *pdbRecordType, dbCommon *precord, dbFldDes *pdbFldDes = papFldDes[link_ind[j]]; DBLINK *plink = (DBLINK *)((char *)precord + pdbFldDes->offset); - if (ellCount(&precord->rdes->devList) > 0 && - (strcmp(pdbFldDes->name, "INP") == 0 || strcmp(pdbFldDes->name, "OUT") == 0)) { + if (ellCount(&precord->rdes->devList) > 0 && pdbFldDes->isDevLink) { devSup *pdevSup = dbDTYPtoDevSup(pdbRecordType, precord->dtyp); if (pdevSup) { From 99626f03cd30550f3ca822fa1a0a1d4428f7e649 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 1 Aug 2014 11:28:10 -0400 Subject: [PATCH 02/12] dbLink: remove struct lset and dbDb_lset unnecessary exposure of internal API --- src/ioc/db/dbLink.c | 6 ------ src/ioc/db/dbLink.h | 24 ------------------------ 2 files changed, 30 deletions(-) diff --git a/src/ioc/db/dbLink.c b/src/ioc/db/dbLink.c index be0a3b70a..af23d7615 100644 --- a/src/ioc/db/dbLink.c +++ b/src/ioc/db/dbLink.c @@ -379,12 +379,6 @@ static void dbDbScanFwdLink(struct link *plink) dbScanPassive(precord, paddr->precord); } -lset dbDb_lset = { dbDbInitLink, dbDbAddLink, NULL, dbDbRemoveLink, - dbDbIsLinkConnected, dbDbGetDBFtype, dbDbGetElements, dbDbGetValue, - dbDbGetControlLimits, dbDbGetGraphicLimits, dbDbGetAlarmLimits, - dbDbGetPrecision, dbDbGetUnits, dbDbGetAlarm, dbDbGetTimeStamp, - dbDbPutValue, dbDbScanFwdLink }; - /***************************** Generic Link API *****************************/ void dbInitLink(struct dbCommon *precord, struct link *plink, short dbfType) diff --git a/src/ioc/db/dbLink.h b/src/ioc/db/dbLink.h index ff0de3003..ee097757f 100644 --- a/src/ioc/db/dbLink.h +++ b/src/ioc/db/dbLink.h @@ -25,30 +25,6 @@ extern "C" { #endif -typedef struct lset { - long (*initLink)(struct link *plink, short dbfType); - long (*addLink)(struct link *plink, short dbfType); - long (*loadLink)(struct link *plink, short dbrType, void *pbuffer); - void (*removeLink)(struct link *plink); - int (*isLinkConnected)(const struct link *plink); - int (*getDBFtype)(const struct link *plink); - long (*getElements)(const struct link *plink, long *nelements); - long (*getValue)(struct link *plink, short dbrType, void *pbuffer, - epicsEnum16 *pstat, epicsEnum16 *psevr, long *pnRequest); - long (*getControlLimits)(const struct link *plink, double *lo, double *hi); - long (*getGraphicLimits)(const struct link *plink, double *lo, double *hi); - long (*getAlarmLimits)(const struct link *plink, double *lolo, double *lo, - double *hi, double *hihi); - long (*getPrecision)(const struct link *plink, short *precision); - long (*getUnits)(const struct link *plink, char *units, int unitsSize); - long (*getAlarm)(const struct link *plink, epicsEnum16 *status, - epicsEnum16 *severity); - long (*getTimeStamp)(const struct link *plink, epicsTimeStamp *pstamp); - long (*putValue)(struct link *plink, short dbrType, - const void *pbuffer, long nRequest); - void (*scanFwdLink)(struct link *plink); -} lset; - #define dbGetSevr(PLINK, PSEVERITY) \ dbGetAlarm((PLINK), NULL, (PSEVERITY)); From 470a9f9fc975dd6e7ea9383d772e7a66d2de2027 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 1 Aug 2014 11:28:10 -0400 Subject: [PATCH 03/12] Introduce struct dbLinkInfo and dbParseLink() This structure holds the processed form of any link type. It is populated by dbParseLink(), which can be called w/o record locking. dbCanSetLink() checks the the parsed link, and a DBLINK field have compatible types for assignment. Record locking is required, but this does not modify the field. dbSetLink() makes the actual assignment. --- src/ioc/dbStatic/dbStaticLib.c | 295 +++++++++++++++++++++++++++++++++ src/ioc/dbStatic/dbStaticPvt.h | 31 ++++ 2 files changed, 326 insertions(+) diff --git a/src/ioc/dbStatic/dbStaticLib.c b/src/ioc/dbStatic/dbStaticLib.c index 9f953a673..5ca23493b 100644 --- a/src/ioc/dbStatic/dbStaticLib.c +++ b/src/ioc/dbStatic/dbStaticLib.c @@ -2024,6 +2024,301 @@ static void cvtDecimalOrHexToShort(char *from,short *value) } } +long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo) +{ + char *pstr, *pend; + size_t len, i; + + /* Strip leading blanks and tabs */ + while (*str && (*str == ' ' || *str == '\t')) str++; + + memset(pinfo, 0, sizeof(*pinfo)); + + len = strlen(str); + pinfo->target = pstr = malloc(len+1); + if(!pstr) + return S_dbLib_outMem; + strcpy(pstr, str); + + /* Strip trailing blanks and tabs */ + for(i=len; i>0 && (pstr[i-1]==' ' || pstr[i-1]=='\t'); i--) {} + pstr[i]='\0'; + + /* check for HW link types */ + if(pstr[0]=='@') { + pinfo->ltype = INST_IO; /* can only be INST_IO */ + + /* move string up by one to overwrite '@' + * so it can be re-used later in struct instio + */ + ssize_t N = strlen(pstr)-1; + memmove(pstr, pstr+1, N); + pstr[N] = '\0'; + + return 0; + + } else if(pstr[0]=='#') { /* some other HW link type */ + int ret; + char junk = 0; + char *patstr = strchr(pstr, '@'); /* find start of parm string */ + + if(patstr) + *patstr++ = '\0'; /* isolate the parm string for later processing */ + + /* generalized extraction of ID charactor and integer pairs (eg. "#C15 S14") */ + ret = sscanf(pinfo->target, "# %c%d %c%d %c%d %c%d %c%d %c", + &pinfo->hwid[0], &pinfo->hwnums[0], + &pinfo->hwid[1], &pinfo->hwnums[1], + &pinfo->hwid[2], &pinfo->hwnums[2], + &pinfo->hwid[3], &pinfo->hwnums[3], + &pinfo->hwid[4], &pinfo->hwnums[4], + &junk); + + /* ret<0 when pattern not matched + * ret==11 when extra non-space before '@'. + * ret is odd when a number is missing + */ + if(ret<0 || ret>10 || ret%2==1) goto fail; + + if(strcmp(pinfo->hwid, "CS")==0) pinfo->ltype = VME_IO; + else if(strcmp(pinfo->hwid, "BCNAF")==0)pinfo->ltype = CAMAC_IO; + else if(strcmp(pinfo->hwid, "RMDE")==0) pinfo->ltype = RF_IO; + else if(strcmp(pinfo->hwid, "LACS")==0) pinfo->ltype = AB_IO; + else if(strcmp(pinfo->hwid, "LA")==0) pinfo->ltype = GPIB_IO; + else if(strcmp(pinfo->hwid, "LNPS")==0) pinfo->ltype = BITBUS_IO; + else if(strcmp(pinfo->hwid, "LBG")==0) pinfo->ltype = BBGPIB_IO; + else if(strcmp(pinfo->hwid, "VCS")==0) pinfo->ltype = VXI_IO; + else if(strcmp(pinfo->hwid, "VS")==0) pinfo->ltype = VXI_IO; + else goto fail; + + if(patstr && pinfo->ltype!=RF_IO) { + /* move the parm string forward. + * pinfo->target will be re-used as ->parm + */ + memmove(pinfo->target, patstr, strlen(patstr)+1); + } else if(!patstr && pinfo->ltype==RF_IO) { + /* RF_IO only. the string isn't needed anymore */ + free(pinfo->target); + pinfo->target = NULL; + } else { + /* missing parm when required, or found parm when not expected */ + free(pinfo->target); + pinfo->target = NULL; + return S_dbLib_badField; + } + + return 0; + } + + /* A string is a constant if epicsStrtod or strtol consumes entire string */ + (void)epicsStrtod(pstr, &pend); + if(*pend!='\0') + (void)strtol(pstr, &pend, 0); + if(*pend=='\0') { + pinfo->ltype = CONSTANT; + return 0; /* This is a CONSTANT link */ + } + + pinfo->ltype = PV_LINK; + pstr = strchr(pstr, ' '); /* find start of link modifiers (can't be seperated by tabs) */ + if(pstr) { + *pstr++ = '\0'; /* isolate modifiers. pinfo->target is PV name only for re-use in struct pv_link */ + + /* Space seperation of modifiers isn't required, and other chars are ignored. + * Order of comparisons resolves ambiguity by checking for + * longer matches first. + * eg. "QQCPPXMSITT" is pvlOptCPP|pvlOptMS + */ + + if (strstr(pstr, "NPP")) pinfo->modifiers = 0; + else if (strstr(pstr, "CPP")) pinfo->modifiers = pvlOptCPP; + else if (strstr(pstr, "PP")) pinfo->modifiers = pvlOptPP; + else if (strstr(pstr, "CA")) pinfo->modifiers = pvlOptCA; + else if (strstr(pstr, "CP")) pinfo->modifiers = pvlOptCP; + + if (strstr(pstr, "NMS")) pinfo->modifiers |= pvlOptNMS; + else if (strstr(pstr, "MSI")) pinfo->modifiers |= pvlOptMSI; + else if (strstr(pstr, "MSS")) pinfo->modifiers |= pvlOptMSS; + else if (strstr(pstr, "MS")) pinfo->modifiers |= pvlOptMS; + + /* filter modifiers based on link type */ + switch(ftype) { + case DBF_INLINK: /* accept all */ break; + case DBF_OUTLINK: pinfo->modifiers &= ~pvlOptCPP; break; + case DBF_FWDLINK: pinfo->modifiers &= pvlOptCA; break; + } + } + + return 0; +fail: + free(pinfo->target); + return S_dbLib_badField; +} + +long dbCanSetLink(DBLINK *plink, dbLinkInfo *pinfo, devSup *devsup) +{ + /* consume allocated string pinfo->target on failure */ + + int link_type = CONSTANT; + if(devsup) + link_type = devsup->link_type; + if(link_type==pinfo->ltype) + return 0; + switch(pinfo->ltype) { + case CONSTANT: + case PV_LINK: + if(link_type==CONSTANT || link_type==PV_LINK) + return 0; + else if(link_type==INST_IO && pinfo->target[0]!='\0') + /* for compatibility. Invalid non-empty INST_IO is treated as empty string */ + return 0; + default: + free(pinfo->target); + pinfo->target = NULL; + return 1; + } +} + +static +void dbSetLinkConst(DBLINK *plink, dbLinkInfo *pinfo) +{ + plink->type = CONSTANT; + plink->value.constantStr = pinfo->target; + + pinfo->target = NULL; +} + +static +void dbSetLinkPV(DBLINK *plink, dbLinkInfo *pinfo) +{ + plink->type = PV_LINK; + plink->value.pv_link.pvname = pinfo->target; + plink->value.pv_link.pvlMask = pinfo->modifiers; + + pinfo->target = NULL; +} + +static +void dbSetLinkHW(DBLINK *plink, dbLinkInfo *pinfo) +{ + + switch(pinfo->ltype) { + case INST_IO: + plink->value.instio.string = pinfo->target; + break; + case VME_IO: + plink->value.vmeio.card = pinfo->hwnums[0]; + plink->value.vmeio.signal = pinfo->hwnums[1]; + plink->value.vmeio.parm = pinfo->target; + break; + case CAMAC_IO: + plink->value.camacio.b = pinfo->hwnums[0]; + plink->value.camacio.c = pinfo->hwnums[1]; + plink->value.camacio.n = pinfo->hwnums[2]; + plink->value.camacio.a = pinfo->hwnums[3]; + plink->value.camacio.f = pinfo->hwnums[4]; + plink->value.camacio.parm = pinfo->target; + break; + case RF_IO: + plink->value.rfio.cryo = pinfo->hwnums[0]; + plink->value.rfio.micro = pinfo->hwnums[1]; + plink->value.rfio.dataset = pinfo->hwnums[2]; + plink->value.rfio.element = pinfo->hwnums[3]; + break; + case AB_IO: + plink->value.abio.link = pinfo->hwnums[0]; + plink->value.abio.adapter = pinfo->hwnums[1]; + plink->value.abio.card = pinfo->hwnums[2]; + plink->value.abio.signal = pinfo->hwnums[3]; + plink->value.abio.parm = pinfo->target; + break; + case GPIB_IO: + plink->value.gpibio.link = pinfo->hwnums[0]; + plink->value.gpibio.addr = pinfo->hwnums[1]; + plink->value.gpibio.parm = pinfo->target; + break; + case BITBUS_IO: + plink->value.bitbusio.link = pinfo->hwnums[0]; + plink->value.bitbusio.node = pinfo->hwnums[1]; + plink->value.bitbusio.port = pinfo->hwnums[2]; + plink->value.bitbusio.signal = pinfo->hwnums[3]; + plink->value.bitbusio.parm = pinfo->target; + break; + case BBGPIB_IO: + plink->value.bbgpibio.link = pinfo->hwnums[0]; + plink->value.bbgpibio.bbaddr = pinfo->hwnums[1]; + plink->value.bbgpibio.gpibaddr = pinfo->hwnums[2]; + plink->value.bbgpibio.parm = pinfo->target; + break; + case VXI_IO: + if(strcmp(pinfo->hwid, "VCS")==0) { + plink->value.vxiio.flag=VXIDYNAMIC; + plink->value.vxiio.frame = pinfo->hwnums[0]; + plink->value.vxiio.slot = pinfo->hwnums[1]; + plink->value.vxiio.signal = pinfo->hwnums[2]; + } else if(strcmp(pinfo->hwid, "VS")==0) { + plink->value.vxiio.flag=VXISTATIC; + plink->value.vxiio.la = pinfo->hwnums[0]; + plink->value.vxiio.signal = pinfo->hwnums[1]; + } else { + cantProceed("dbSetLinkHW: logic error, unknown VXI_IO variant"); + } + plink->value.vxiio.parm = pinfo->target; + break; + + default: + cantProceed("dbSetLinkHW: logic error, unhandled link type"); + return; + } + + plink->type = pinfo->ltype; + + pinfo->target = NULL; /* now owned by link field */ +} + +long dbSetLink(DBLINK *plink, dbLinkInfo *pinfo, devSup *devsup) +{ + int ret = 0; + int link_type = CONSTANT; + + if(devsup) + link_type = devsup->link_type; + + if(link_type==CONSTANT || link_type==PV_LINK) { + switch(pinfo->ltype) { + case CONSTANT: + dbFreeLinkContents(plink); + dbSetLinkConst(plink, pinfo); break; + case PV_LINK: + dbFreeLinkContents(plink); + dbSetLinkPV(plink, pinfo); break; + default: + errlogMessage("Warning: dbSetLink: forgot to test with dbCanSetLink() or logic error"); + goto fail; /* can't assign HW link */ + } + + } else if(link_type==pinfo->ltype) { + dbFreeLinkContents(plink); + dbSetLinkHW(plink, pinfo); + + } else if(link_type==INST_IO && (pinfo->ltype==CONSTANT || pinfo->ltype==PV_LINK) && pinfo->target[0]!='\0') { + /* for compatibility. Invalid non-empty INST_IO is treated as empty string */ + pinfo->target[0] = '\0'; + pinfo->ltype = INST_IO; + + dbFreeLinkContents(plink); + dbSetLinkHW(plink, pinfo); + + } else + goto fail; + + return ret; +fail: + free(pinfo->target); + pinfo->target = NULL; + return S_dbLib_badField; +} + long dbPutString(DBENTRY *pdbentry,const char *pstring) { dbFldDes *pflddes = pdbentry->pflddes; diff --git a/src/ioc/dbStatic/dbStaticPvt.h b/src/ioc/dbStatic/dbStaticPvt.h index 8f8664dc1..f3e6a932e 100644 --- a/src/ioc/dbStatic/dbStaticPvt.h +++ b/src/ioc/dbStatic/dbStaticPvt.h @@ -36,6 +36,37 @@ char *dbRecordName(DBENTRY *pdbentry); char *dbGetStringNum(DBENTRY *pdbentry); long dbPutStringNum(DBENTRY *pdbentry,const char *pstring); +typedef struct dbLinkInfo { + short ltype; + + /* full link string for CONSTANT and PV_LINK, + * parm string for HW links*/ + char *target; + + /* for PV_LINK */ + short modifiers; + + /* HW links */ + char hwid[6]; /* one extra element for a nil */ + int hwnums[5]; +} dbLinkInfo; + +/* Parse link string. no record locks needed. + * on success caller must free pinfo->target + */ +long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo); +/* Check if link type allow the parsed link value pinfo + * to be assigned to the given link. + * Record containing plink must be locked. + * Frees pinfo->target on failure. + */ +long dbCanSetLink(DBLINK *plink, dbLinkInfo *pinfo, devSup *devsup); +/* Set link field. source record must be locked (target record too + * when a DB_LINK is created) + * Unconditionally takes ownership of pinfo->target + */ +long dbSetLink(DBLINK *plink, dbLinkInfo *pinfo, devSup *dset); + /* The following is for path */ typedef struct dbPathNode { ELLNODE node; From 480797c0e001fb6d27df8b07b5f76de16e0b6b6e Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 1 Aug 2014 11:28:10 -0400 Subject: [PATCH 04/12] db/test: extend dbPutLinkTest to cover dbParseLink() --- src/ioc/db/test/dbPutLinkTest.c | 122 +++++++++++++++++++++++++++++++- 1 file changed, 121 insertions(+), 1 deletion(-) diff --git a/src/ioc/db/test/dbPutLinkTest.c b/src/ioc/db/test/dbPutLinkTest.c index 75e01b2d7..31563466f 100644 --- a/src/ioc/db/test/dbPutLinkTest.c +++ b/src/ioc/db/test/dbPutLinkTest.c @@ -26,10 +26,128 @@ #include "xRecord.h" +#include "dbStaticPvt.h" + #include "testMain.h" +static +int testStrcmp(int expect, const char *A, const char *B) { + static const char op[] = "<=>"; + int ret = strcmp(A,B); + testOk(ret==expect, "\"%s\" %c= \"%s\"", + A, op[expect+1], B); + return ret==expect; +} + void dbTestIoc_registerRecordDeviceDriver(struct dbBase *); +#define TEST_CONSTANT(SET, EXPECT) {SET, {CONSTANT, EXPECT}} +#define TEST_PV_LINK(SET, PV, MOD) {SET, {PV_LINK, PV, MOD}} +#define TEST_HW(SET, TYPE, ID, PARM, ...) {SET, {TYPE, PARM, 0, ID, {__VA_ARGS__}}} + +static const struct testParseDataT { + const char * const str; + dbLinkInfo info; +} testParseData[] = { + TEST_CONSTANT("", ""), + TEST_CONSTANT("0.1", "0.1"), + TEST_CONSTANT(" 0.2\t ", "0.2"), + + TEST_PV_LINK("0.1a", "0.1a", 0), + TEST_PV_LINK(" hello ", "hello", 0), + TEST_PV_LINK(" hellox MSI", "hellox", pvlOptMSI), + TEST_PV_LINK(" world MSICP", "world", pvlOptMSI|pvlOptCP), + + TEST_HW("#C14 S145 @testing", VME_IO, "CS", "testing", 14, 145), + TEST_HW("#B11 C12 N13 A14 F15 @cparam", CAMAC_IO, "BCNAF", "cparam", 11, 12, 13, 14, 15), + TEST_HW("@hello world", INST_IO, "", "hello world"), + {NULL} +}; + +static void testLinkParse(void) +{ + const struct testParseDataT *td = testParseData; + dbLinkInfo info; + testDiag("link parsing"); + testdbPrepare(); + + testdbReadDatabase("dbTestIoc.dbd", NULL, NULL); + + dbTestIoc_registerRecordDeviceDriver(pdbbase); + + testdbReadDatabase("dbPutLinkTest.db", NULL, NULL); + + eltc(0); + testIocInitOk(); + eltc(1); + + for(;td->str; td++) { + int i, N; + testDiag("Parse \"%s\"", td->str); + testOk1(dbParseLink(td->str, DBF_INLINK, &info)==0); + testOk1(info.ltype==td->info.ltype); + if(td->info.target) + testStrcmp(0, info.target, td->info.target); + if(info.ltype==td->info.ltype) { + switch(info.ltype) { + case PV_LINK: + testOk1(info.modifiers==td->info.modifiers); + break; + case VME_IO: + testStrcmp(0, info.hwid, td->info.hwid); + N = strlen(td->info.hwid); + for(i=0; iinfo.hwnums[i], "%d == %d", + info.hwnums[i], td->info.hwnums[i]); + } + } + free(info.target); + } + + testIocShutdownOk(); + + testdbCleanup(); +} + +static const char *testParseFailData[] = { + "#", + "#S", + "#ABC", + "#A0 B", + "#A0 B @", + "#A0 B C @", + "#R1 M2 D3 E4 @oops", /* RF_IO has no parm */ + "#C1 S2", /* VME_IO needs parm */ + NULL +}; + +static void testLinkFailParse(void) +{ + const char * const *td = testParseFailData; + dbLinkInfo info; + testDiag("link parsing of invalid input"); + testdbPrepare(); + + testdbReadDatabase("dbTestIoc.dbd", NULL, NULL); + + dbTestIoc_registerRecordDeviceDriver(pdbbase); + + testdbReadDatabase("dbPutLinkTest.db", NULL, NULL); + + eltc(0); + testIocInitOk(); + eltc(1); + + for(;*td; td++) { + testDiag("Expect failure \"%s\"", *td); + testOk1(dbParseLink(*td, DBF_INLINK, &info)==S_dbLib_badField); + } + + testIocShutdownOk(); + + testdbCleanup(); +} + static const struct testDataT { const char * const linkstring; short linkType; @@ -387,7 +505,9 @@ static void testLinkFail(void) MAIN(dbPutLinkTest) { - testPlan(200); + testPlan(245); + testLinkParse(); + testLinkFailParse(); testCADBSet(); testHWInitSet(); testHWMod(); From 200355bc0030267431161018547180c292378fff Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 1 Aug 2014 11:28:10 -0400 Subject: [PATCH 05/12] dbStatic: re-write dbPutString() to use new link parsing dbPutString() now has two behaviors. If link_type==CONSTANT and constantStr==NULL, the link has not yet been initialized. Just set the ->text field. If the link has been initialized, then use dbSetLink() to make the assignment. Eliminate special handling for DBF_DEVICE. --- src/ioc/dbStatic/dbStaticLib.c | 489 ++------------------------------- 1 file changed, 21 insertions(+), 468 deletions(-) diff --git a/src/ioc/dbStatic/dbStaticLib.c b/src/ioc/dbStatic/dbStaticLib.c index 5ca23493b..e7807bc7a 100644 --- a/src/ioc/dbStatic/dbStaticLib.c +++ b/src/ioc/dbStatic/dbStaticLib.c @@ -44,6 +44,8 @@ #include "dbStaticLib.h" #include "dbStaticPvt.h" +#include "dbCommon.h" + int dbStaticDebug = 0; static char *pNullString = ""; #define messagesize 100 @@ -84,12 +86,9 @@ static int mapDBFtoDCT[DBF_NOACCESS+1] = { /*forward references for private routines*/ static FILE *openOutstream(const char *filename); static void finishOutstream(FILE *stream); -static long setLinkType(DBENTRY *pdbentry); -static long putParmString(char **pparm,const char *pstring); static void entryErrMessage(DBENTRY *pdbentry,long status,char *mess); static void zeroDbentry(DBENTRY *pdbentry); static char *getpMessage(DBENTRY *pdbentry); -static long putPvLink(DBENTRY *pdbentry,short pvlMask,const char *pvname); static long dbAddOnePath (DBBASE *pdbbase, const char *path, unsigned length); /* internal routines*/ @@ -114,103 +113,6 @@ static void finishOutstream(FILE *stream) } } -static long setLinkType(DBENTRY *pdbentry) -{ - DBENTRY dbEntry; - dbRecordType *precordType; - devSup *pdevSup; - DBLINK *plink; - long status=0; - int link_type,ind,type; - - dbCopyEntryContents(pdbentry, &dbEntry); - status = dbFindField(&dbEntry, "DTYP"); - if (status) { - epicsPrintf("field DTYP does not exist for recordtype %s\n", - dbGetRecordTypeName(&dbEntry)); - status = S_dbLib_fieldNotFound; - goto done; - } - - precordType = dbEntry.precordType; - if (!precordType) { - status = S_dbLib_badField; - goto done; - } - - if (ellCount(&precordType->devList) == 0) goto done; - - ind = dbGetMenuIndex(&dbEntry); - if (ind == -1) { - char *pstring; - - pstring = dbGetString(&dbEntry); - if (strstr(pstring, "$(") || strstr(pstring, "${")) { - link_type = MACRO_LINK; - } else { - status = S_dbLib_badField; - goto done; - } - } else { - pdevSup = (devSup *)ellNth(&precordType->devList, ind + 1); - if (!pdevSup) { - status = S_dbLib_badField; - goto done; - } - link_type = pdevSup->link_type; - } - - plink = (DBLINK *)pdbentry->pfield; - if (plink->type == link_type) goto done; - - if (plink->text) - { - /* re-parse link text when DTYP has changed */ - char * link_text; - link_text = plink->text; - plink->text = NULL; - dbFreeLinkContents(plink); - plink->type = link_type; - dbPutString(pdbentry, link_text); - free(link_text); - goto done; - } - - type = plink->type; - if ((type == CONSTANT || type == PV_LINK || - type == PN_LINK || type == DB_LINK || type == CA_LINK) && - (link_type == CONSTANT || link_type == PV_LINK)) goto done; - - dbFreeLinkContents(plink); - plink->type = link_type; - switch (plink->type) { - case VME_IO: plink->value.vmeio.parm = pNullString; break; - case CAMAC_IO: plink->value.camacio.parm = pNullString; break; - case AB_IO: plink->value.abio.parm = pNullString; break; - case GPIB_IO: plink->value.gpibio.parm = pNullString; break; - case BITBUS_IO: plink->value.bitbusio.parm = pNullString; break; - case INST_IO: plink->value.instio.string = pNullString; break; - case BBGPIB_IO: plink->value.bbgpibio.parm = pNullString; break; - case VXI_IO: plink->value.vxiio.parm = pNullString; break; - } -done: - dbFinishEntry(&dbEntry); - return(status); -} - -static long putParmString(char **pparm,const char *pstring) -{ - if (*pparm && *pparm != pNullString) { - free(*pparm); - *pparm = pNullString; - } - if (!pstring) return 0; - pstring = strchr(pstring, '@'); - if (!pstring || !*++pstring) return 0; - *pparm = epicsStrDup(pstring); - return 0; -} - void dbFreeLinkContents(struct link *plink) { char *parm = NULL; @@ -308,35 +210,6 @@ static char *getpMessage(DBENTRY *pdbentry) return msg; } -static long putPvLink(DBENTRY *pdbentry,short pvlMask,const char *pvname) -{ - dbFldDes *pflddes; - DBLINK *plink; - char *pname; - - dbGetFieldAddress(pdbentry); - pflddes = pdbentry->pflddes; - if(!pflddes) return(-1); - plink = (DBLINK *)pdbentry->pfield; - if(!plink) return(-1); - switch (pflddes->field_type) { - case DBF_INLINK: - case DBF_OUTLINK: - case DBF_FWDLINK: - if(plink->type != PV_LINK) return(S_dbLib_badLink); - pname = plink->value.pv_link.pvname; - if(pname) free((void *)pname); - pname = dbCalloc(strlen(pvname)+1,sizeof(char)); - plink->value.pv_link.pvname = pname; - strcpy(pname,pvname); - plink->value.pv_link.pvlMask = pvlMask; - return(0); - default: - errPrintf(-1,__FILE__, __LINE__,"Logic Error\n"); - } - return(S_dbLib_badLink); -} - /*Public only for dbStaticNoRun*/ dbDeviceMenu *dbGetDeviceMenu(DBENTRY *pdbentry) { @@ -2015,15 +1888,6 @@ char * dbGetString(DBENTRY *pdbentry) return (message); } -static void cvtDecimalOrHexToShort(char *from,short *value) -{ - if(strspn(from,"0x")==2 || strspn(from,"0X")==2) { - sscanf(from,"%hi",value); - } else { - sscanf(from,"%hd",value); - } -} - long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo) { char *pstr, *pend; @@ -2362,350 +2226,39 @@ long dbPutString(DBENTRY *pdbentry,const char *pstring) case DBF_FLOAT: case DBF_DOUBLE: case DBF_MENU: + case DBF_DEVICE: status = dbPutStringNum(pdbentry,pstring); break; - case DBF_DEVICE: { - DBENTRY dbEntry; - char *name; - - status = dbPutStringNum(pdbentry, pstring); - if (status) return status; - - name = dbGetRelatedField(pdbentry); - if (!name) return 0; - - dbCopyEntryContents(pdbentry, &dbEntry); - status = dbFindField(&dbEntry, name); - if (!status) - status = setLinkType(&dbEntry); - dbFinishEntry(&dbEntry); - return status; - } - case DBF_INLINK: case DBF_OUTLINK: case DBF_FWDLINK: { - DBLINK *plink; - char string[80]; - char *pstr = string; + dbLinkInfo link_info; + DBLINK *plink = (DBLINK *)pfield; - if (!pfield) - return S_dbLib_fieldNotFound; + status = dbParseLink(pstring, pflddes->field_type, &link_info); - plink = (DBLINK *)pfield; - dbFreeLinkContents(plink); - if (stringHasMacro) { - plink->type = MACRO_LINK; - plink->value.macro_link.macroStr = epicsStrDup(pstring); - goto done; - } + if(status==0 && plink->type==CONSTANT && plink->value.constantStr==NULL) { + /* links not yet initialized by dbInitRecordLinks() */ + free(plink->text); + plink->text = epicsStrDup(pstring); + free(link_info.target); - if (pflddes->isDevLink) { - status = setLinkType(pdbentry); /* This uses DTYP to look up and set plink->type, necessary for default DTYP */ - if (status) { - errMessage(status,"in dbPutString from setLinkType"); - return status; - } - /* store link text in case DTYP changes later */ - plink->text = malloc(strlen(pstring) + 1); - if (plink->text) - strcpy(plink->text, pstring); - } - if (strlen(pstring) >= sizeof(string)) { - status = S_dbLib_badField; - errMessage(status, - "dbPutString received a string that is too long"); - return status; - } - strcpy(pstr, pstring); - /* Strip leading blanks and tabs */ - while (*pstr && (*pstr == ' ' || *pstr == '\t')) pstr++; - /* Strip trailing blanks and tabs */ - if (pstr) { - int ind; + } else if(status==0) { + /* assignment after init (eg. autosave restore) */ + struct dbCommon *prec = pdbentry->precnode->precord; + devSup *devsup = (devSup *)ellNth(&pdbentry->precordType->devList, prec->dtyp+1); + status = dbCanSetLink(plink, &link_info, devsup); + if(status) + status = dbSetLink(plink, &link_info, devsup); + } - for (ind = (int) strlen(pstr) - 1; ind >= 0; ind--) { - if (pstr[ind] != ' ' && pstr[ind] != '\t') break; - pstr[ind] = '\0'; - } - } - if (!pstr || !*pstr) { - if (plink->type == PV_LINK) dbCvtLinkToConstant(pdbentry); - if (plink->type != CONSTANT) return S_dbLib_badField; - free((void *)plink->value.constantStr); - plink->value.constantStr = NULL; - goto done; - } - switch (plink->type) { - case CONSTANT: - case PV_LINK: { - short ppOpt = 0; - short msOpt = 0; - char *end; - char *enddouble; - char *endlong; - - /* Check first to see if string is a constant*/ - /*It is a string if epicsStrtod or strtol eats entire string*/ - /*leading and trailing blanks have already been stripped*/ - (void) epicsStrtod(pstr, &enddouble); - (void) strtol(pstr, &endlong, 0); - - if (*enddouble == 0 || *endlong == 0) { - if (plink->type == PV_LINK) dbCvtLinkToConstant(pdbentry); - if (!plink->value.constantStr || - strlen(plink->value.constantStr) < strlen(pstr)) { - free(plink->value.constantStr); - plink->value.constantStr = - dbCalloc(strlen(pstr) + 1, sizeof(char)); - } - strcpy(plink->value.constantStr, pstr); - goto done; - } - - if (plink->type==CONSTANT) dbCvtLinkToPvlink(pdbentry); - end = strchr(pstr,' '); - if (end) { - switch (pflddes->field_type) { - case DBF_INLINK: { - if (strstr(end, "NPP")) ppOpt = 0; - else if (strstr(end, "CPP")) ppOpt = pvlOptCPP; - else if (strstr(end, "PP")) ppOpt = pvlOptPP; - else if (strstr(end, "CA")) ppOpt = pvlOptCA; - else if (strstr(end, "CP")) ppOpt = pvlOptCP; - else ppOpt = 0; - if (strstr(end, "NMS")) msOpt = pvlOptNMS; - else if (strstr(end, "MSI")) msOpt = pvlOptMSI; - else if (strstr(end, "MSS")) msOpt = pvlOptMSS; -/*must be the last one:*/ else if (strstr(end, "MS")) msOpt = pvlOptMS; - else msOpt = 0; - *end = 0; - } - break; - - case DBF_OUTLINK: { - if (strstr(end, "NPP")) ppOpt = 0; - else if(strstr(end, "PP")) ppOpt = pvlOptPP; - else if(strstr(end, "CA")) ppOpt = pvlOptCA; - else ppOpt = 0; - if (strstr(end, "NMS")) msOpt = pvlOptNMS; - else if(strstr(end, "MSI")) msOpt = pvlOptMSI; - else if(strstr(end, "MSS")) msOpt = pvlOptMSS; -/*must be the last one:*/ else if(strstr(end, "MS")) msOpt = pvlOptMS; - else msOpt = 0; - *end = 0; - } - break; - - case DBF_FWDLINK: { - if (strstr(end, "NPP")) ppOpt = 0; - else if (strstr(end, "CA")) ppOpt = pvlOptCA; - else ppOpt = 0; - msOpt = 0; - *end = 0; - } - break; - - default: - epicsPrintf("dbPutString: Logic Error\n"); - } - } - status = putPvLink(pdbentry,ppOpt|msOpt,pstr); - goto done; - } - /*break; is unnecessary*/ - case VME_IO: { - char *end; - - if (!(end = strchr(pstr,'#'))) return S_dbLib_badField; - pstr = end + 1; - if (!(end = strchr(pstr,'C'))) return S_dbLib_badField; - pstr = end + 1; - cvtDecimalOrHexToShort(pstr,&plink->value.vmeio.card); - if (!(end = strchr(pstr,'S'))) return S_dbLib_badField; - pstr = end + 1; - cvtDecimalOrHexToShort(pstr, &plink->value.vmeio.signal); - status = putParmString(&plink->value.vmeio.parm, pstr); - } - break; - - case CAMAC_IO: { - char *end; - - if (!(end = strchr(pstr,'#'))) return (S_dbLib_badField); - pstr = end + 1; - if (!(end = strchr(pstr,'B'))) return (S_dbLib_badField); - pstr = end + 1; - cvtDecimalOrHexToShort(pstr,&plink->value.camacio.b); - if (!(end = strchr(pstr,'C'))) return (S_dbLib_badField); - pstr = end + 1; - cvtDecimalOrHexToShort(pstr,&plink->value.camacio.c); - if (!(end = strchr(pstr,'N'))) return (S_dbLib_badField); - pstr = end + 1; - cvtDecimalOrHexToShort(pstr,&plink->value.camacio.n); - if (!(end = strchr(pstr,'A'))) { - plink->value.camacio.a = 0; - } else { - pstr = end + 1; - cvtDecimalOrHexToShort(pstr,&plink->value.camacio.a); - } - if (!(end = strchr(pstr,'F'))) { - plink->value.camacio.f = 0; - } else { - pstr = end + 1; - cvtDecimalOrHexToShort(pstr,&plink->value.camacio.f); - } - status = putParmString(&plink->value.camacio.parm,pstr); - } - break; - - case RF_IO: { - char *end; - - if(!(end = strchr(pstr,'#'))) return (S_dbLib_badField); - pstr = end + 1; - if(!(end = strchr(pstr,'R'))) return (S_dbLib_badField); - pstr = end + 1; - cvtDecimalOrHexToShort(pstr,&plink->value.rfio.cryo); - if(!(end = strchr(pstr,'M'))) return (S_dbLib_badField); - pstr = end + 1; - cvtDecimalOrHexToShort(pstr,&plink->value.rfio.micro); - if(!(end = strchr(pstr,'D'))) return (S_dbLib_badField); - pstr = end + 1; - cvtDecimalOrHexToShort(pstr,&plink->value.rfio.dataset); - if(!(end = strchr(pstr,'E'))) return (S_dbLib_badField); - pstr = end + 1; - cvtDecimalOrHexToShort(pstr,&plink->value.rfio.element); - } - break; - case AB_IO: { - char *end; - - if(!(end = strchr(pstr,'#'))) return (S_dbLib_badField); - pstr = end + 1; - if(!(end = strchr(pstr,'L'))) return (S_dbLib_badField); - pstr = end + 1; - cvtDecimalOrHexToShort(pstr,&plink->value.abio.link); - if(!(end = strchr(pstr,'A'))) return (S_dbLib_badField); - pstr = end + 1; - cvtDecimalOrHexToShort(pstr,&plink->value.abio.adapter); - if(!(end = strchr(pstr,'C'))) return (S_dbLib_badField); - pstr = end + 1; - cvtDecimalOrHexToShort(pstr,&plink->value.abio.card); - if(!(end = strchr(pstr,'S'))) return (S_dbLib_badField); - pstr = end + 1; - cvtDecimalOrHexToShort(pstr,&plink->value.abio.signal); - status = putParmString(&plink->value.abio.parm,pstr); - } - break; - case GPIB_IO: { - char *end; - - if(!(end = strchr(pstr,'#'))) return (S_dbLib_badField); - pstr = end + 1; - if(!(end = strchr(pstr,'L'))) return (S_dbLib_badField); - pstr = end + 1; - cvtDecimalOrHexToShort(pstr,&plink->value.gpibio.link); - if(!(end = strchr(pstr,'A'))) return (S_dbLib_badField); - pstr = end + 1; - cvtDecimalOrHexToShort(pstr,&plink->value.gpibio.addr); - status = putParmString(&plink->value.gpibio.parm,pstr); - } - break; - case BITBUS_IO: { - /* jbk - the bbgpib struct uses unsigned char's instead - of short, so read values into short and then convert */ - - char *end; - short tmp_val; - - if(!(end = strchr(pstr,'#'))) return (S_dbLib_badField); - pstr = end + 1; - if(!(end = strchr(pstr,'L'))) return (S_dbLib_badField); - pstr = end + 1; - cvtDecimalOrHexToShort(pstr,&tmp_val); - plink->value.bitbusio.link=(unsigned char)tmp_val; - if(!(end = strchr(pstr,'N'))) return (S_dbLib_badField); - pstr = end + 1; - cvtDecimalOrHexToShort(pstr,&tmp_val); - plink->value.bitbusio.node=(unsigned char)tmp_val; - if(!(end = strchr(pstr,'P'))) return (S_dbLib_badField); - pstr = end + 1; - cvtDecimalOrHexToShort(pstr,&tmp_val); - plink->value.bitbusio.port=(unsigned char)tmp_val; - if(!(end = strchr(pstr,'S'))) return (S_dbLib_badField); - pstr = end + 1; - cvtDecimalOrHexToShort(pstr,&tmp_val); - plink->value.bitbusio.signal=(unsigned char)tmp_val; - status = putParmString(&plink->value.bitbusio.parm,pstr); - } - break; - case BBGPIB_IO: { - /* jbk - the bbgpib struct uses unsigned char's instead - of short, so read values into short and then convert */ - - char *end; - short tmp_val; - - if(!(end = strchr(pstr,'#'))) return (S_dbLib_badField); - pstr = end + 1; - if(!(end = strchr(pstr,'L'))) return (S_dbLib_badField); - pstr = end + 1; - cvtDecimalOrHexToShort(pstr,&tmp_val); - plink->value.bbgpibio.link=(unsigned char)tmp_val; - if(!(end = strchr(pstr,'B'))) return (S_dbLib_badField); - pstr = end + 1; - cvtDecimalOrHexToShort(pstr,&tmp_val); - plink->value.bbgpibio.bbaddr=(unsigned char)tmp_val; - if(!(end = strchr(pstr,'G'))) return (S_dbLib_badField); - pstr = end + 1; - cvtDecimalOrHexToShort(pstr,&tmp_val); - plink->value.bbgpibio.gpibaddr=(unsigned char)tmp_val; - status = putParmString(&plink->value.bbgpibio.parm,pstr); - } - break; - case VXI_IO: { - char *end; - - if(!(end = strchr(pstr,'#'))) return (S_dbLib_badField); - pstr = end + 1; - memset((char *)&plink->value.vxiio,0,sizeof(struct vxiio)); - plink->value.vxiio.parm = pNullString; - if(!((end = strchr(pstr,'C'))&&(end < strchr(pstr,'@')) )) { - plink->value.vxiio.flag = VXISTATIC; - if(!(end = strchr(pstr,'V'))) return (S_dbLib_badField); - pstr = end + 1; - cvtDecimalOrHexToShort(pstr,&plink->value.vxiio.la); - } else { - plink->value.vxiio.flag = VXIDYNAMIC; - if(!(end = strchr(pstr,'V'))) return (S_dbLib_badField); - pstr = end + 1; - cvtDecimalOrHexToShort(pstr,&plink->value.vxiio.frame); - if(!(end = strchr(pstr,'C'))) return (S_dbLib_badField); - pstr = end + 1; - cvtDecimalOrHexToShort(pstr,&plink->value.vxiio.slot); - } - if((end = strchr(pstr,'S'))) { - pstr = end + 1; - cvtDecimalOrHexToShort(pstr,&plink->value.vxiio.signal); - } else { - plink->value.vxiio.signal = 0; - } - status = putParmString(&plink->value.vxiio.parm,pstr); - } - break; - case INST_IO: { - status = putParmString(&plink->value.instio.string, pstr); - } - break; - } - } + } break; default: return S_dbLib_badField; } -done: + if (!status && strcmp(pflddes->name, "VAL") == 0) { DBENTRY dbentry; From 33804bd7ead99c23549b3fa43504366c12900bec Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 1 Aug 2014 11:28:10 -0400 Subject: [PATCH 06/12] iocInit: initialize links in iocBuild Done before any driver, record, or device supports run. Also before the hooks used by autosave. --- src/ioc/dbStatic/dbStaticLib.c | 59 ++++++++++++++++++++++++++++++++++ src/ioc/dbStatic/dbStaticPvt.h | 2 ++ src/ioc/misc/iocInit.c | 23 +++++++++---- 3 files changed, 77 insertions(+), 7 deletions(-) diff --git a/src/ioc/dbStatic/dbStaticLib.c b/src/ioc/dbStatic/dbStaticLib.c index e7807bc7a..ca7dfff83 100644 --- a/src/ioc/dbStatic/dbStaticLib.c +++ b/src/ioc/dbStatic/dbStaticLib.c @@ -1888,6 +1888,65 @@ char * dbGetString(DBENTRY *pdbentry) return (message); } +long dbInitRecordLinks(dbRecordType *rtyp, struct dbCommon *prec) +{ + short i; + + for (i=0; ino_links; i++) { + dbLinkInfo link_info; + dbFldDes *pflddes = rtyp->papFldDes[rtyp->link_ind[i]]; + DBLINK *plink = (DBLINK *)(((char *)prec) + pflddes->offset); + devSup *devsup = NULL; + + /* link fields are zero'd on allocation. + * so are effectively CONSTANT, but with constantStr==NULL. + * Here we initialize them to have the correct link type, + * with zero values and empty (but non-NULL) strings. + */ + + if(pflddes->isDevLink) { + devsup = (devSup *)ellNth(&rtyp->devList, prec->dtyp+1); + } + if(devsup) + plink->type = devsup->link_type; + else + plink->type = CONSTANT; + + switch (plink->type) { + case CONSTANT: plink->value.constantStr = callocMustSucceed(1, 1, "init CONSTANT link"); break; + case PV_LINK: plink->value.pv_link.pvname = callocMustSucceed(1, 1, "init PV_LINK"); break; + case VME_IO: plink->value.vmeio.parm = pNullString; break; + case CAMAC_IO: plink->value.camacio.parm = pNullString; break; + case AB_IO: plink->value.abio.parm = pNullString; break; + case GPIB_IO: plink->value.gpibio.parm = pNullString; break; + case BITBUS_IO: plink->value.bitbusio.parm = pNullString; break; + case INST_IO: plink->value.instio.string = pNullString; break; + case BBGPIB_IO: plink->value.bbgpibio.parm = pNullString; break; + case VXI_IO: plink->value.vxiio.parm = pNullString; break; + } + + if(!plink->text) + continue; + + if(dbParseLink(plink->text, pflddes->field_type, &link_info)!=0) { + /* This was already parsed once when ->text was set. + * Any syntax error messages were printed at that time. + */ + + } else if(dbCanSetLink(plink, &link_info, devsup)!=0) { + errlogPrintf("Error: %s.%s: can't initialize link type %d with \"%s\" (type %d)\n", + prec->name, pflddes->name, plink->type, plink->text, link_info.ltype); + + } else if(dbSetLink(plink, &link_info, devsup)) { + errlogPrintf("Error: %s.%s: failed to initialize link type %d with \"%s\" (type %d)\n", + prec->name, pflddes->name, plink->type, plink->text, link_info.ltype); + } + free(plink->text); + plink->text = NULL; + } + return 0; +} + long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo) { char *pstr, *pend; diff --git a/src/ioc/dbStatic/dbStaticPvt.h b/src/ioc/dbStatic/dbStaticPvt.h index f3e6a932e..c249e73bb 100644 --- a/src/ioc/dbStatic/dbStaticPvt.h +++ b/src/ioc/dbStatic/dbStaticPvt.h @@ -51,6 +51,8 @@ typedef struct dbLinkInfo { int hwnums[5]; } dbLinkInfo; +long dbInitRecordLinks(dbRecordType *rtyp, struct dbCommon *prec); + /* Parse link string. no record locks needed. * on success caller must free pinfo->target */ diff --git a/src/ioc/misc/iocInit.c b/src/ioc/misc/iocInit.c index 1e224d2f5..32b7fc524 100644 --- a/src/ioc/misc/iocInit.c +++ b/src/ioc/misc/iocInit.c @@ -56,6 +56,8 @@ #include "dbCommon.h" #include "dbLock.h" #include "dbAccess.h" +#include "dbStaticLib.h" +#include "dbStaticPvt.h" #include "recGbl.h" #include "dbNotify.h" #include "dbCa.h" @@ -82,7 +84,14 @@ static void initDatabase(void); static void initialProcess(void); static void exitDatabase(void *dummy); - +/* + * Iterate through all record instances (but not aliases), + * calling a function for each one. + */ +typedef void (*recIterFunc)(dbRecordType *rtyp, dbCommon *prec, void *user); + +static void iterateRecords(recIterFunc func, void *user); + /* * Initialize EPICS on the IOC. */ @@ -122,8 +131,14 @@ static int iocBuild_1(void) return 0; } +static void prepareLinks(dbRecordType *rtyp, dbCommon *prec, void *junk) +{ + dbInitRecordLinks(rtyp, prec); +} + static int iocBuild_2(void) { + iterateRecords(prepareLinks, NULL); initHookAnnounce(initHookAfterCaLinkInit); initDrvSup(); @@ -413,12 +428,6 @@ static void finishDevSup(void) } } } - -/* - * Iterate through all record instances (but not aliases), - * calling a function for each one. - */ -typedef void (*recIterFunc)(dbRecordType *rtyp, dbCommon *prec, void *user); static void iterateRecords(recIterFunc func, void *user) { From c5da1e3f89b6a54219e2096aea5f3678cbbf83ec Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 1 Aug 2014 11:28:10 -0400 Subject: [PATCH 07/12] dbAccess: update dbPutFieldLink() to use dbParseLink()/dbSetLink() Now returns early for invalid link strings w/o modifying the field, or detaching device support. --- src/ioc/db/dbAccess.c | 46 +++++++++++++++++++++++++++++++++---------- 1 file changed, 36 insertions(+), 10 deletions(-) diff --git a/src/ioc/db/dbAccess.c b/src/ioc/db/dbAccess.c index 2abbe8ea2..cd69c97ec 100644 --- a/src/ioc/db/dbAccess.c +++ b/src/ioc/db/dbAccess.c @@ -35,6 +35,7 @@ #include "errMdef.h" #define epicsExportSharedSymbols #include "dbStaticLib.h" +#include "dbStaticPvt.h" #include "dbBase.h" #include "link.h" #include "dbFldTypes.h" @@ -909,15 +910,17 @@ devSup* dbDSETtoDevSup(dbRecordType *prdes, struct dset *pdset) { static long dbPutFieldLink(DBADDR *paddr, short dbrType, const void *pbuffer, long nRequest) { + dbLinkInfo link_info; + DBADDR *pdbaddr = NULL; dbCommon *precord = paddr->precord; dbFldDes *pfldDes = paddr->pfldDes; long special = paddr->special; struct link *plink = (struct link *)paddr->pfield; const char *pstring = (const char *)pbuffer; - DBENTRY dbEntry; struct dsxt *old_dsxt = NULL; struct dset *new_dset = NULL; struct dsxt *new_dsxt = NULL; + devSup *new_devsup = NULL; long status; int isDevLink; short scan; @@ -936,16 +939,30 @@ static long dbPutFieldLink(DBADDR *paddr, return S_db_badDbrtype; } - dbInitEntry(pdbbase, &dbEntry); - status = dbFindRecord(&dbEntry, precord->name); - if (!status) status = dbFindField(&dbEntry, pfldDes->name); - if (status) goto finish; + status = dbParseLink(pstring, pfldDes->field_type, &link_info); + if (status) + return status; + + if (link_info.ltype==PV_LINK && !(link_info.modifiers&(pvlOptCA|pvlOptCP|pvlOptCPP))) { + DBADDR tempaddr; + if(dbNameToAddr(link_info.target, &tempaddr)==0) { + /* we will create a DB link. */ + pdbaddr = malloc(sizeof(*pdbaddr)); + if(!pdbaddr) { + status = S_db_noMemory; + goto cleanup; + } + *pdbaddr = tempaddr; /* struct copy */ + } + } isDevLink = ellCount(&precord->rdes->devList) > 0 && pfldDes->isDevLink; dbLockSetGblLock(); dbLockSetRecordLock(precord); + if(pdbaddr) + dbLockSetRecordLock(pdbaddr->precord); scan = precord->scan; @@ -954,10 +971,19 @@ static long dbPutFieldLink(DBADDR *paddr, if (pdevSup) { new_dset = pdevSup->pdset; new_dsxt = pdevSup->pdsxt; + new_devsup = pdevSup; } + } + if (dbCanSetLink(plink, &link_info, new_devsup)) { + /* link type mis-match prevents assignment */ + status = S_dbLib_badField; + goto unlock; + } + + if (isDevLink) { if (precord->dset) { - pdevSup = dbDSETtoDevSup(precord->rdes, precord->dset); + devSup *pdevSup = dbDSETtoDevSup(precord->rdes, precord->dset); if (pdevSup) old_dsxt = pdevSup->pdsxt; } @@ -1005,7 +1031,7 @@ static long dbPutFieldLink(DBADDR *paddr, if (special) status = dbPutSpecial(paddr, 0); - if (!status) status = dbPutString(&dbEntry, pstring); + if (!status) status = dbSetLink(plink, &link_info, new_devsup); if (!status && special) status = dbPutSpecial(paddr, 1); @@ -1032,7 +1058,7 @@ static long dbPutFieldLink(DBADDR *paddr, switch (plink->type) { /* New link type */ case PV_LINK: - dbAddLink(precord, plink, pfldDes->field_type); + dbAddLink(precord, plink, pfldDes->field_type, pdbaddr); break; case CONSTANT: @@ -1063,8 +1089,8 @@ postScanEvent: db_post_events(precord, &precord->scan, DBE_VALUE | DBE_LOG); unlock: dbLockSetGblUnlock(); -finish: - dbFinishEntry(&dbEntry); +cleanup: + free(link_info.target); return status; } From a919d7e09d17bd4a0b49d4af58ee566a5fe58479 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 1 Aug 2014 11:28:10 -0400 Subject: [PATCH 08/12] dbLink: pass DBADDR* to dbAddLink() Now we know whether a DB or CA link is created before calling dbAddLink(). --- src/ioc/db/dbLink.c | 36 +++++++++++------------------------- src/ioc/db/dbLink.h | 3 ++- 2 files changed, 13 insertions(+), 26 deletions(-) diff --git a/src/ioc/db/dbLink.c b/src/ioc/db/dbLink.c index af23d7615..bc4de8ec6 100644 --- a/src/ioc/db/dbLink.c +++ b/src/ioc/db/dbLink.c @@ -123,26 +123,6 @@ static long dbDbInitLink(struct link *plink, short dbfType) return 0; } -static long dbDbAddLink(struct link *plink, short dbfType) -{ - DBADDR dbaddr; - long status; - DBADDR *pdbAddr; - - status = dbNameToAddr(plink->value.pv_link.pvname, &dbaddr); - if (status) - return status; - - plink->type = DB_LINK; - pdbAddr = dbCalloc(1, sizeof(struct dbAddr)); - *pdbAddr = dbaddr; /* structure copy */ - plink->value.pv_link.pvt = pdbAddr; - - dbLockSetRecordLock(pdbAddr->precord); - dbLockSetMerge(plink->value.pv_link.precord, pdbAddr->precord); - return 0; -} - static void dbDbRemoveLink(struct link *plink) { free(plink->value.pv_link.pvt); @@ -411,17 +391,23 @@ void dbInitLink(struct dbCommon *precord, struct link *plink, short dbfType) } } -void dbAddLink(struct dbCommon *precord, struct link *plink, short dbfType) +void dbAddLink(struct dbCommon *precord, struct link *plink, short dbfType, DBADDR *ptargetaddr) { plink->value.pv_link.precord = precord; if (plink == &precord->tsel) recGblTSELwasModified(plink); - if (!(plink->value.pv_link.pvlMask & (pvlOptCA | pvlOptCP | pvlOptCPP))) { - /* Can we make it a DB link? */ - if (!dbDbAddLink(plink, dbfType)) - return; + if (ptargetaddr) { + /* make a DB link */ + + plink->type = DB_LINK; + plink->value.pv_link.pvt = ptargetaddr; + + /* target record is already locked in dbPutFieldLink() */ + dbLockSetMerge(plink->value.pv_link.precord, ptargetaddr->precord); + + return; } /* Make it a CA link */ diff --git a/src/ioc/db/dbLink.h b/src/ioc/db/dbLink.h index ee097757f..68a803dbb 100644 --- a/src/ioc/db/dbLink.h +++ b/src/ioc/db/dbLink.h @@ -20,6 +20,7 @@ #include "shareLib.h" #include "epicsTypes.h" #include "epicsTime.h" +#include "dbAddr.h" #ifdef __cplusplus extern "C" { @@ -31,7 +32,7 @@ extern "C" { epicsShareFunc void dbInitLink(struct dbCommon *precord, struct link *plink, short dbfType); epicsShareFunc void dbAddLink(struct dbCommon *precord, struct link *plink, - short dbfType); + short dbfType, DBADDR *ptargetaddr); epicsShareFunc long dbLoadLink(struct link *plink, short dbrType, void *pbuffer); epicsShareFunc void dbRemoveLink(struct link *plink); From 5a8915c051320c861244bb9041bac86aa30901bd Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 1 Aug 2014 11:28:10 -0400 Subject: [PATCH 09/12] INST_IO no longer accept invalid string as empty --- src/ioc/db/test/dbPutLinkTest.c | 7 +++---- src/ioc/dbStatic/dbStaticLib.c | 11 ----------- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/src/ioc/db/test/dbPutLinkTest.c b/src/ioc/db/test/dbPutLinkTest.c index 31563466f..52ae41fb5 100644 --- a/src/ioc/db/test/dbPutLinkTest.c +++ b/src/ioc/db/test/dbPutLinkTest.c @@ -482,9 +482,8 @@ static void testLinkFail(void) /* INST_IO doesn't accept empty string */ testdbPutFieldFail(S_dbLib_badField, "rINST_IO.INP", DBR_STRING, ""); - /* INST_IO accepts invalid input as empty string */ - testdbPutFieldOk("rINST_IO.INP", DBR_STRING, "abc"); - testdbGetFieldEqual("rINST_IO.INP", DBR_STRING, "@"); + /* INST_IO doesn't accept empty string */ + testdbPutFieldFail(S_dbLib_badField, "rINST_IO.INP", DBR_STRING, "abc"); /* syntax errors */ testdbPutFieldFail(S_dbLib_badField, "rVME_IO.INP", DBR_STRING, "#S201 C200 @another VME_IO"); @@ -505,7 +504,7 @@ static void testLinkFail(void) MAIN(dbPutLinkTest) { - testPlan(245); + testPlan(244); testLinkParse(); testLinkFailParse(); testCADBSet(); diff --git a/src/ioc/dbStatic/dbStaticLib.c b/src/ioc/dbStatic/dbStaticLib.c index ca7dfff83..ac4276e5d 100644 --- a/src/ioc/dbStatic/dbStaticLib.c +++ b/src/ioc/dbStatic/dbStaticLib.c @@ -2092,9 +2092,6 @@ long dbCanSetLink(DBLINK *plink, dbLinkInfo *pinfo, devSup *devsup) case PV_LINK: if(link_type==CONSTANT || link_type==PV_LINK) return 0; - else if(link_type==INST_IO && pinfo->target[0]!='\0') - /* for compatibility. Invalid non-empty INST_IO is treated as empty string */ - return 0; default: free(pinfo->target); pinfo->target = NULL; @@ -2224,14 +2221,6 @@ long dbSetLink(DBLINK *plink, dbLinkInfo *pinfo, devSup *devsup) dbFreeLinkContents(plink); dbSetLinkHW(plink, pinfo); - } else if(link_type==INST_IO && (pinfo->ltype==CONSTANT || pinfo->ltype==PV_LINK) && pinfo->target[0]!='\0') { - /* for compatibility. Invalid non-empty INST_IO is treated as empty string */ - pinfo->target[0] = '\0'; - pinfo->ltype = INST_IO; - - dbFreeLinkContents(plink); - dbSetLinkHW(plink, pinfo); - } else goto fail; From 6ed997aaccfe26aaef5c0413b99e42f35640c88c Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 1 Aug 2014 11:28:10 -0400 Subject: [PATCH 10/12] Invalid HW links no longer partially initialized --- src/ioc/db/test/dbPutLinkTest.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ioc/db/test/dbPutLinkTest.c b/src/ioc/db/test/dbPutLinkTest.c index 52ae41fb5..e6580b0a1 100644 --- a/src/ioc/db/test/dbPutLinkTest.c +++ b/src/ioc/db/test/dbPutLinkTest.c @@ -445,7 +445,7 @@ static void testLinkInitFail(void) testOk1(plink->type==VME_IO); testOk1(plink->value.vmeio.parm!=NULL); - testdbGetFieldEqual("eVME_IO2.INP", DBR_STRING, "#C200 S0 @"); + testdbGetFieldEqual("eVME_IO2.INP", DBR_STRING, "#C0 S0 @"); prec = (xRecord*)testdbRecordPtr("eVME_IO2"); plink = &prec->inp; From 5847f98f8922b5ea17c1dd7403c609a6195f3297 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 1 Aug 2014 13:55:33 -0400 Subject: [PATCH 11/12] oops --- src/ioc/db/test/dbLinkdset.c | 5 +++-- src/ioc/dbStatic/dbStaticLib.c | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/ioc/db/test/dbLinkdset.c b/src/ioc/db/test/dbLinkdset.c index dce258a3f..c1de4cf00 100644 --- a/src/ioc/db/test/dbLinkdset.c +++ b/src/ioc/db/test/dbLinkdset.c @@ -12,9 +12,10 @@ long link_test_extend(struct dbCommon *junk) static dsxt xrecextend = {&link_test_extend, &link_test_extend}; static -long link_test_init(int junk) +long link_test_init(int pass) { - devExtend(&xrecextend); + if(pass==0) + devExtend(&xrecextend); return 0; } diff --git a/src/ioc/dbStatic/dbStaticLib.c b/src/ioc/dbStatic/dbStaticLib.c index ac4276e5d..bf1c65580 100644 --- a/src/ioc/dbStatic/dbStaticLib.c +++ b/src/ioc/dbStatic/dbStaticLib.c @@ -2297,7 +2297,7 @@ long dbPutString(DBENTRY *pdbentry,const char *pstring) struct dbCommon *prec = pdbentry->precnode->precord; devSup *devsup = (devSup *)ellNth(&pdbentry->precordType->devList, prec->dtyp+1); status = dbCanSetLink(plink, &link_info, devsup); - if(status) + if(status==0) status = dbSetLink(plink, &link_info, devsup); } From 4518e8fa7fb6b347d05524119b70c01ed17c5163 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 29 Dec 2014 18:16:26 -0500 Subject: [PATCH 12/12] dbLink: re-intorduce struct lset and dbDb_lset" without initLink, addLink, and loadLink --- src/ioc/db/dbLink.c | 6 ++++++ src/ioc/db/dbLink.h | 21 +++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/src/ioc/db/dbLink.c b/src/ioc/db/dbLink.c index bc4de8ec6..b42504139 100644 --- a/src/ioc/db/dbLink.c +++ b/src/ioc/db/dbLink.c @@ -359,6 +359,12 @@ static void dbDbScanFwdLink(struct link *plink) dbScanPassive(precord, paddr->precord); } +lset dbDb_lset = { dbDbRemoveLink, + dbDbIsLinkConnected, dbDbGetDBFtype, dbDbGetElements, dbDbGetValue, + dbDbGetControlLimits, dbDbGetGraphicLimits, dbDbGetAlarmLimits, + dbDbGetPrecision, dbDbGetUnits, dbDbGetAlarm, dbDbGetTimeStamp, + dbDbPutValue, dbDbScanFwdLink }; + /***************************** Generic Link API *****************************/ void dbInitLink(struct dbCommon *precord, struct link *plink, short dbfType) diff --git a/src/ioc/db/dbLink.h b/src/ioc/db/dbLink.h index 68a803dbb..8a5eb2573 100644 --- a/src/ioc/db/dbLink.h +++ b/src/ioc/db/dbLink.h @@ -26,6 +26,27 @@ extern "C" { #endif +typedef struct lset { + void (*removeLink)(struct link *plink); + int (*isLinkConnected)(const struct link *plink); + int (*getDBFtype)(const struct link *plink); + long (*getElements)(const struct link *plink, long *nelements); + long (*getValue)(struct link *plink, short dbrType, void *pbuffer, + epicsEnum16 *pstat, epicsEnum16 *psevr, long *pnRequest); + long (*getControlLimits)(const struct link *plink, double *lo, double *hi); + long (*getGraphicLimits)(const struct link *plink, double *lo, double *hi); + long (*getAlarmLimits)(const struct link *plink, double *lolo, double *lo, + double *hi, double *hihi); + long (*getPrecision)(const struct link *plink, short *precision); + long (*getUnits)(const struct link *plink, char *units, int unitsSize); + long (*getAlarm)(const struct link *plink, epicsEnum16 *status, + epicsEnum16 *severity); + long (*getTimeStamp)(const struct link *plink, epicsTimeStamp *pstamp); + long (*putValue)(struct link *plink, short dbrType, + const void *pbuffer, long nRequest); + void (*scanFwdLink)(struct link *plink); +} lset; + #define dbGetSevr(PLINK, PSEVERITY) \ dbGetAlarm((PLINK), NULL, (PSEVERITY));