diff --git a/src/ioc/db/dbAccess.c b/src/ioc/db/dbAccess.c index 5c86678f7..5b5854f6c 100644 --- a/src/ioc/db/dbAccess.c +++ b/src/ioc/db/dbAccess.c @@ -55,6 +55,7 @@ #include "dbScan.h" #include "dbServer.h" #include "dbStaticLib.h" +#include "dbStaticPvt.h" #include "devSup.h" #include "epicsEvent.h" #include "link.h" @@ -935,15 +936,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; @@ -962,31 +965,55 @@ 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)) == 0) { + DBADDR tempaddr; + + if (dbNameToAddr(link_info.target, &tempaddr)==0) { + /* This will become 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 && - (strcmp(pfldDes->name, "INP") == 0 || - strcmp(pfldDes->name, "OUT") == 0); + pfldDes->isDevLink; dbLockSetGblLock(); dbLockSetRecordLock(precord); + if (pdbaddr) + dbLockSetRecordLock(pdbaddr->precord); scan = precord->scan; if (isDevLink) { - devSup *pdevSup = dbDTYPtoDevSup(precord->rdes, precord->dtyp); - if (pdevSup) { - new_dset = pdevSup->pdset; - new_dsxt = pdevSup->pdsxt; + new_devsup = dbDTYPtoDevSup(precord->rdes, precord->dtyp); + if (new_devsup) { + new_dset = new_devsup->pdset; + new_dsxt = new_devsup->pdsxt; } + } + 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); - if (pdevSup) - old_dsxt = pdevSup->pdsxt; + devSup *old_devsup = dbDSETtoDevSup(precord->rdes, precord->dset); + + if (old_devsup) + old_dsxt = old_devsup->pdsxt; } if (new_dsxt == NULL || @@ -1012,7 +1039,7 @@ static long dbPutFieldLink(DBADDR *paddr, switch (plink->type) { /* Old link type */ case DB_LINK: case CA_LINK: - dbRemoveLink(plink); + dbRemoveLink(plink); break; case PV_LINK: @@ -1032,7 +1059,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); @@ -1059,7 +1086,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: @@ -1090,8 +1117,8 @@ postScanEvent: db_post_events(precord, &precord->scan, DBE_VALUE | DBE_LOG); unlock: dbLockSetGblUnlock(); -finish: - dbFinishEntry(&dbEntry); +cleanup: + free(link_info.target); return status; } diff --git a/src/ioc/db/dbLink.c b/src/ioc/db/dbLink.c index ac9354a3b..2ad9c6606 100644 --- a/src/ioc/db/dbLink.c +++ b/src/ioc/db/dbLink.c @@ -144,26 +144,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); @@ -400,7 +380,7 @@ static void dbDbScanFwdLink(struct link *plink) dbScanPassive(precord, paddr->precord); } -lset dbDb_lset = { dbDbInitLink, dbDbAddLink, NULL, dbDbRemoveLink, +lset dbDb_lset = { dbDbRemoveLink, dbDbIsLinkConnected, dbDbGetDBFtype, dbDbGetElements, dbDbGetValue, dbDbGetControlLimits, dbDbGetGraphicLimits, dbDbGetAlarmLimits, dbDbGetPrecision, dbDbGetUnits, dbDbGetAlarm, dbDbGetTimeStamp, @@ -442,17 +422,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 1a4524b14..f96f8ece1 100644 --- a/src/ioc/db/dbLink.h +++ b/src/ioc/db/dbLink.h @@ -20,15 +20,13 @@ #include "shareLib.h" #include "epicsTypes.h" #include "epicsTime.h" +#include "dbAddr.h" #ifdef __cplusplus 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); @@ -55,7 +53,7 @@ typedef struct lset { 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); diff --git a/src/ioc/db/test/dbPutLinkTest.c b/src/ioc/db/test/dbPutLinkTest.c index 001b28c35..6e7f56ce6 100644 --- a/src/ioc/db/test/dbPutLinkTest.c +++ b/src/ioc/db/test/dbPutLinkTest.c @@ -20,6 +20,7 @@ #include "dbAccess.h" #include "registry.h" #include "dbStaticLib.h" +#include "dbStaticPvt.h" #include "osiFileName.h" #include "dbmf.h" #include "errlog.h" @@ -28,8 +29,125 @@ #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(" #B111 C112 N113 @cparam", CAMAC_IO, "BCN", "cparam", 111, 112, 113), + 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; @@ -39,6 +157,7 @@ static const struct testDataT { {"", CONSTANT, 0}, {"0", CONSTANT, 0}, {"42", CONSTANT, 0}, + {"0x1", CONSTANT, 0}, {"x1", DB_LINK, 0, "x1 NPP NMS"}, {"x1.VAL", DB_LINK, 0, "x1.VAL NPP NMS"}, @@ -77,17 +196,17 @@ static void testCADBSet(void) prec = (xRecord*)testdbRecordPtr("x1"); plink = &prec->lnk; - for(;td->linkstring;td++) { + for (;td->linkstring;td++) { testDiag("x1.LNK <- \"%s\"", td->linkstring); testdbPutFieldOk("x1.LNK", DBF_STRING, td->linkstring); - if(td->linkback) + if (td->linkback) testdbGetFieldEqual("x1.LNK", DBF_STRING, td->linkback); else testdbGetFieldEqual("x1.LNK", DBF_STRING, td->linkstring); testOk1(plink->type==td->linkType); - if(plink->type==td->linkType) { + if (plink->type==td->linkType) { switch(td->linkType) { case CONSTANT: if(plink->value.constantStr) @@ -327,7 +446,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; @@ -364,9 +483,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"); @@ -387,7 +505,9 @@ static void testLinkFail(void) MAIN(dbPutLinkTest) { - testPlan(200); + testPlan(251); + testLinkParse(); + testLinkFailParse(); testCADBSet(); testHWInitSet(); testHWMod(); diff --git a/src/ioc/dbStatic/dbBase.h b/src/ioc/dbStatic/dbBase.h index 691953442..adf5f1d60 100644 --- a/src/ioc/dbStatic/dbBase.h +++ b/src/ioc/dbStatic/dbBase.h @@ -76,8 +76,9 @@ typedef struct dbFldDes{ /* field description */ short indRecordType; /*within dbRecordType.papFldDes */ short special; /*Special processing requirements */ dbfType field_type; /*Field type as defined in dbFldTypes.h */ - unsigned int process_passive:1;/*should dbPutField process passive */ - unsigned int prop:1;/*field is a metadata, post DBE_PROPERTY on change*/ + unsigned int process_passive:1;/*should dbPutField process passive */ + unsigned int prop:1;/*field is a metadata, post DBE_PROPERTY on change*/ + unsigned int isDevLink:1; /* true for INP/OUT fields */ ctType base; /*base for integer to string conversions*/ short promptgroup; /*prompt, i.e. gui group */ short interest; /*interest level */ diff --git a/src/ioc/dbStatic/dbLexRoutines.c b/src/ioc/dbStatic/dbLexRoutines.c index 6c3d94561..1674408ad 100644 --- a/src/ioc/dbStatic/dbLexRoutines.c +++ b/src/ioc/dbStatic/dbLexRoutines.c @@ -486,6 +486,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 e1af4c720..9f13c8803 100644 --- a/src/ioc/dbStatic/dbStaticLib.c +++ b/src/ioc/dbStatic/dbStaticLib.c @@ -45,6 +45,8 @@ #include "link.h" #include "special.h" +#include "dbCommon.h" + int dbStaticDebug = 0; static char *pNullString = ""; #define messagesize 100 @@ -85,12 +87,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*/ @@ -115,103 +114,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; @@ -309,35 +211,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) { @@ -2017,13 +1890,348 @@ char * dbGetString(DBENTRY *pdbentry) return (message); } -static void cvtDecimalOrHexToShort(char *from,short *value) +long dbInitRecordLinks(dbRecordType *rtyp, struct dbCommon *prec) { - if(strspn(from,"0x")==2 || strspn(from,"0X")==2) { - sscanf(from,"%hi",value); - } else { - sscanf(from,"%hd",value); + 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; + size_t len; + double value; + + memset(pinfo, 0, sizeof(*pinfo)); + + /* Strip leading white space */ + while (*str && isspace((int)*str)) str++; + + len = strlen(str); + /* Strip trailing white space */ + while (len > 0 && isspace((int)str[len-1])) len--; + + pstr = malloc(len + 1); + if (!pstr) + return S_dbLib_outMem; + pinfo->target = pstr; + + /* Check for Instrument I/O links */ + if (*str == '@') { + pinfo->ltype = INST_IO; + + /* Store everything after the '@' */ + memcpy(pstr, str+1, --len); + pstr[len] = '\0'; + return 0; + } + + /* Store the stripped string */ + memcpy(pstr, str, len); + pstr[len] = '\0'; + + /* Check for other HW link types */ + if (*pstr == '#') { + int ret; + char junk = 0; + char *parm = strchr(pstr, '@'); /* find start of parm string */ + + if (parm) { + *parm++ = '\0'; /* isolate the parm string for later */ + len -= (parm - pstr); + } + + /* generalized extraction of ID charactor and integer pairs (eg. "#C15 S14") */ + ret = sscanf(pinfo->target, "# %c%d %c%d %c%d %c%d %c%d %c", + &pinfo->hwid[0], &pinfo->hwnums[0], + &pinfo->hwid[1], &pinfo->hwnums[1], + &pinfo->hwid[2], &pinfo->hwnums[2], + &pinfo->hwid[3], &pinfo->hwnums[3], + &pinfo->hwid[4], &pinfo->hwnums[4], + &junk); + + /* ret<0 when pattern not matched + * ret==11 when extra non-space before '@'. + * ret is odd when a number is missing + */ + if (ret<0 || ret>10 || ret%2==1) goto fail; + + if (strcmp(pinfo->hwid, "CS")==0) pinfo->ltype = VME_IO; + else if (strcmp(pinfo->hwid, "BCN")==0) pinfo->ltype = CAMAC_IO; + else if (strcmp(pinfo->hwid, "BCNA")==0) pinfo->ltype = CAMAC_IO; + else if (strcmp(pinfo->hwid, "BCNF")==0) pinfo->ltype = CAMAC_IO; + else if (strcmp(pinfo->hwid, "BCNAF")==0) pinfo->ltype = CAMAC_IO; + else if (strcmp(pinfo->hwid, "RMDE")==0) pinfo->ltype = RF_IO; + else if (strcmp(pinfo->hwid, "LACS")==0) pinfo->ltype = AB_IO; + else if (strcmp(pinfo->hwid, "LA")==0) pinfo->ltype = GPIB_IO; + else if (strcmp(pinfo->hwid, "LNPS")==0) pinfo->ltype = BITBUS_IO; + else if (strcmp(pinfo->hwid, "LBG")==0) pinfo->ltype = BBGPIB_IO; + else if (strcmp(pinfo->hwid, "VCS")==0) pinfo->ltype = VXI_IO; + else if (strcmp(pinfo->hwid, "VS")==0) pinfo->ltype = VXI_IO; + else goto fail; + + if (parm && pinfo->ltype != RF_IO) { + /* move parm string to beginning of buffer */ + memmove(pinfo->target, parm, len + 1); + } else if (!parm && pinfo->ltype == RF_IO) { + /* RF_IO, the string isn't needed at all */ + free(pinfo->target); + pinfo->target = NULL; + } else { + /* missing parm when required, or found parm when not expected */ + free(pinfo->target); + pinfo->target = NULL; + return S_dbLib_badField; + } + return 0; + } + + /* Link is a constant if empty or it holds just a number */ + if (len == 0 || epicsParseDouble(pstr, &value, NULL) == 0) { + pinfo->ltype = CONSTANT; + return 0; + } + + pinfo->ltype = PV_LINK; + pstr = strchr(pstr, ' '); /* find start of link modifiers (can't be seperated by tabs) */ + if (pstr) { + *pstr++ = '\0'; /* isolate modifiers. pinfo->target is PV name only for re-use in struct pv_link */ + + /* Space seperation of modifiers isn't required, and other chars are ignored. + * Order of comparisons resolves ambiguity by checking for + * longer matches first. + * eg. "QQCPPXMSITT" is pvlOptCPP|pvlOptMSI + */ + + if (strstr(pstr, "NPP")) pinfo->modifiers = 0; + else if (strstr(pstr, "CPP")) pinfo->modifiers = pvlOptCPP; + else if (strstr(pstr, "PP")) pinfo->modifiers = pvlOptPP; + else if (strstr(pstr, "CA")) pinfo->modifiers = pvlOptCA; + else if (strstr(pstr, "CP")) pinfo->modifiers = pvlOptCP; + + if (strstr(pstr, "NMS")) pinfo->modifiers |= pvlOptNMS; + else if (strstr(pstr, "MSI")) pinfo->modifiers |= pvlOptMSI; + else if (strstr(pstr, "MSS")) pinfo->modifiers |= pvlOptMSS; + else if (strstr(pstr, "MS")) pinfo->modifiers |= pvlOptMS; + + /* filter modifiers based on link type */ + switch(ftype) { + case DBF_INLINK: /* accept all */ break; + case DBF_OUTLINK: pinfo->modifiers &= ~pvlOptCPP; break; + case DBF_FWDLINK: pinfo->modifiers &= pvlOptCA; break; + } + } + + return 0; +fail: + 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; + 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 + goto fail; + + return ret; +fail: + free(pinfo->target); + pinfo->target = NULL; + return S_dbLib_badField; } long dbPutString(DBENTRY *pdbentry,const char *pstring) @@ -2069,351 +2277,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 (strcmp(pflddes->name, "INP") == 0 || - strcmp(pflddes->name, "OUT") == 0) { - 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==0) + 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; diff --git a/src/ioc/dbStatic/dbStaticPvt.h b/src/ioc/dbStatic/dbStaticPvt.h index 8f8664dc1..8e58e9535 100644 --- a/src/ioc/dbStatic/dbStaticPvt.h +++ b/src/ioc/dbStatic/dbStaticPvt.h @@ -36,6 +36,39 @@ 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; + +long dbInitRecordLinks(dbRecordType *rtyp, struct dbCommon *prec); + +/* Parse link string. no record locks needed. + * on success caller must free pinfo->target + */ +epicsShareFunc 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; diff --git a/src/ioc/misc/iocInit.c b/src/ioc/misc/iocInit.c index a8dca3c9a..a66de064a 100644 --- a/src/ioc/misc/iocInit.c +++ b/src/ioc/misc/iocInit.c @@ -86,7 +86,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. */ @@ -126,8 +133,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(); @@ -419,12 +432,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) { @@ -490,8 +497,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) {