Release Manager
- Tag the module in Git using these tag conventions:
+ Tag the module in Git, using these tag conventions:
R7.1.1-pren
@@ -143,7 +143,7 @@ made.
R7.1.1-rcn
— release candidate tag
-
+
cd base-7.1
git tag -m 'ANJ: Tagged for 7.1.1-rc1' R7.1.1-rc1
diff --git a/modules/ca/src/client/udpiiu.cpp b/modules/ca/src/client/udpiiu.cpp
index 73d4ee4cb..00d764d7c 100644
--- a/modules/ca/src/client/udpiiu.cpp
+++ b/modules/ca/src/client/udpiiu.cpp
@@ -193,7 +193,7 @@ udpiiu::udpiiu (
#ifdef IP_MULTICAST_TTL
{
- int ttl;
+ osiSockOptMcastTTL_t ttl;
long val;
if(envGetLongConfigParam(&EPICS_CA_MCAST_TTL, &val))
val =1;
diff --git a/modules/ca/src/template/top/caPerlApp/caget.pl b/modules/ca/src/template/top/caPerlApp/caget.pl
index 5e7be9f79..0d9af37a1 100644
--- a/modules/ca/src/template/top/caPerlApp/caget.pl
+++ b/modules/ca/src/template/top/caPerlApp/caget.pl
@@ -143,6 +143,10 @@ sub display {
printf " Lo ctrl limit: %g\n", $data->{lower_ctrl_limit};
printf " Hi ctrl limit: %g\n", $data->{upper_ctrl_limit};
}
+ if (exists $data->{ackt}) {
+ printf " Ack transients: %s\n", $data->{ackt} ? 'YES' : 'NO';
+ printf " Ack severity: %s\n", $data->{acks};
+ }
} else {
my $value = format_number($data, $type);
if ($opt_t) {
diff --git a/modules/database/src/ioc/db/dbAccess.c b/modules/database/src/ioc/db/dbAccess.c
index cd7535194..143c427da 100644
--- a/modules/database/src/ioc/db/dbAccess.c
+++ b/modules/database/src/ioc/db/dbAccess.c
@@ -34,7 +34,7 @@
#include "errlog.h"
#include "errMdef.h"
-#define epicsExportSharedSymbols
+#include "epicsExport.h" /* #define epicsExportSharedSymbols */
#include "caeventmask.h"
#include "callback.h"
#include "dbAccessDefs.h"
@@ -65,6 +65,9 @@
epicsShareDef struct dbBase *pdbbase = 0;
epicsShareDef volatile int interruptAccept=FALSE;
+epicsShareDef int dbAccessDebugPUTF = 0;
+epicsExportAddress(int, dbAccessDebugPUTF);
+
/* Hook Routines */
epicsShareDef DB_LOAD_RECORDS_HOOK_ROUTINE dbLoadRecordsHook = NULL;
@@ -446,22 +449,6 @@ int dbGetFieldIndex(const struct dbAddr *paddr)
return paddr->pfldDes->indRecordType;
}
-/*
- * Process a record if its scan field is passive.
- * Will notify if processing is complete by callback.
- * (only if you are interested in completion)
- */
-long dbScanPassive(dbCommon *pfrom, dbCommon *pto)
-{
- /* if not passive just return success */
- if (pto->scan != 0)
- return 0;
-
- if (pfrom && pfrom->ppn)
- dbNotifyAdd(pfrom,pto);
- return dbProcess(pto);
-}
-
/*
* Process the record.
* 1. Check for breakpoints.
@@ -527,7 +514,8 @@ long dbProcess(dbCommon *precord)
unsigned short monitor_mask;
if (*ptrace)
- printf("%s: Active %s\n", context, precord->name);
+ printf("%s: dbProcess of Active '%s' with RPRO=%d\n",
+ context, precord->name, precord->rpro);
/* raise scan alarm after MAX_LOCK times */
if ((precord->stat == SCAN_ALARM) ||
@@ -556,7 +544,8 @@ long dbProcess(dbCommon *precord)
/* if disabled check disable alarm severity and return success */
if (precord->disa == precord->disv) {
if (*ptrace)
- printf("%s: Disabled %s\n", context, precord->name);
+ printf("%s: dbProcess of Disabled '%s'\n",
+ context, precord->name);
/*take care of caching and notifyCompletion*/
precord->rpro = FALSE;
@@ -593,7 +582,7 @@ long dbProcess(dbCommon *precord)
}
if (*ptrace)
- printf("%s: Process %s\n", context, precord->name);
+ printf("%s: dbProcess of '%s'\n", context, precord->name);
/* process record */
status = prset->process(precord);
@@ -713,6 +702,18 @@ void dbInitEntryFromRecord(struct dbCommon *prec, DBENTRY *pdbentry)
pdbentry->precnode = ppvt->recnode;
}
+struct link* dbGetDevLink(struct dbCommon* prec)
+{
+ DBLINK *plink = 0;
+ DBENTRY entry;
+ dbInitEntryFromRecord(prec, &entry);
+ if(dbFindField(&entry, "INP")==0 || dbFindField(&entry, "OUT")==0) {
+ plink = (DBLINK*)entry.pfield;
+ }
+ dbFinishEntry(&entry);
+ return plink;
+}
+
long dbValueSize(short dbr_type)
{
/* sizes for value associated with each DBR request type */
@@ -1040,7 +1041,7 @@ static long dbPutFieldLink(DBADDR *paddr,
return S_db_badDbrtype;
}
- status = dbParseLink(pstring, pfldDes->field_type, &link_info, 0);
+ status = dbParseLink(pstring, pfldDes->field_type, &link_info);
if (status)
return status;
@@ -1213,8 +1214,8 @@ long dbPutField(DBADDR *paddr, short dbrType,
precord->scan == 0 &&
dbrType < DBR_PUT_ACKT)) {
if (precord->pact) {
- if (precord->tpro)
- printf("%s: Active %s\n",
+ if (dbAccessDebugPUTF && precord->tpro)
+ printf("%s: dbPutField to Active '%s', setting RPRO=1\n",
epicsThreadGetNameSelf(), precord->name);
precord->rpro = TRUE;
} else {
@@ -1347,4 +1348,3 @@ done:
paddr->pfield = pfieldsave;
return status;
}
-
diff --git a/modules/database/src/ioc/db/dbAccessDefs.h b/modules/database/src/ioc/db/dbAccessDefs.h
index cc45b17fe..5cf0b26ab 100644
--- a/modules/database/src/ioc/db/dbAccessDefs.h
+++ b/modules/database/src/ioc/db/dbAccessDefs.h
@@ -34,6 +34,7 @@ extern "C" {
epicsShareExtern struct dbBase *pdbbase;
epicsShareExtern volatile int interruptAccept;
+epicsShareExtern int dbAccessDebugPUTF;
/* The database field and request types are defined in dbFldTypes.h*/
/* Data Base Request Options */
diff --git a/modules/database/src/ioc/db/dbCa.c b/modules/database/src/ioc/db/dbCa.c
index e844ad826..65a8327cf 100644
--- a/modules/database/src/ioc/db/dbCa.c
+++ b/modules/database/src/ioc/db/dbCa.c
@@ -842,6 +842,7 @@ static void eventCallback(struct event_handler_args arg)
struct dbr_time_double *pdbr_time_double;
dbCaCallback monitor = 0;
void *userPvt = 0;
+ int doScan = 1;
assert(pca);
epicsMutexMustLock(pca->lock);
@@ -872,10 +873,13 @@ static void eventCallback(struct event_handler_args arg)
memcpy(pca->pgetString, dbr_value_ptr(arg.dbr, arg.type), size);
pca->gotInString = TRUE;
} else switch (arg.type){
+ case DBR_TIME_ENUM:
+ /* Disable the record scan if we also have a string monitor */
+ doScan = !(plink->value.pv_link.pvlMask & pvlOptInpString);
+ /* fall through */
case DBR_TIME_STRING:
case DBR_TIME_SHORT:
case DBR_TIME_FLOAT:
- case DBR_TIME_ENUM:
case DBR_TIME_CHAR:
case DBR_TIME_LONG:
case DBR_TIME_DOUBLE:
@@ -893,7 +897,7 @@ static void eventCallback(struct event_handler_args arg)
pca->sevr = pdbr_time_double->severity;
pca->stat = pdbr_time_double->status;
memcpy(&pca->timeStamp, &pdbr_time_double->stamp, sizeof(epicsTimeStamp));
- if (precord) {
+ if (doScan && precord) {
struct pv_link *ppv_link = &plink->value.pv_link;
if ((ppv_link->pvlMask & pvlOptCP) ||
diff --git a/modules/database/src/ioc/db/dbDbLink.c b/modules/database/src/ioc/db/dbDbLink.c
index 105799876..ce0110a64 100644
--- a/modules/database/src/ioc/db/dbDbLink.c
+++ b/modules/database/src/ioc/db/dbDbLink.c
@@ -12,6 +12,29 @@
* Current Author: Andrew Johnson
*/
+/* The PUTF and RPRO fields in dbCommon are flags that indicate when a record
+ * is being processed as a result of an external put (i.e. some server process
+ * calling dbPutField()), ensuring that the record and its successors will
+ * eventually get processed even if they happen to be busy at the time of the
+ * put. From Base-3.16.2 and 7.0.2 the code ensures that all records downstream
+ * from the original are processed even if a busy asynchronous device appears
+ * in the processing chain (this breaks the chain in older versions).
+ *
+ * PUTF - This field is set in dbPutField() prior to it calling dbProcess().
+ * It is normally cleared at the end of processing in recGblFwdLink().
+ * It may also be cleared in dbProcess() if DISA==DISV (scan disabled),
+ * or by the processTarget() function below.
+ *
+ * If PUTF is TRUE before a call to dbProcess(prec), then after it returns
+ * either PACT is TRUE, or PUTF will be FALSE.
+ *
+ * RPRO - This field is set by dbPutField() or by the processTarget() function
+ * below when a record to be processed is found to be busy (PACT==1).
+ * It is normally cleared in recGblFwdLink() when the record is queued
+ * for re-processing, or in dbProcess() if DISA==DISV (scan disabled).
+ */
+
+
#include
#include
#include
@@ -43,17 +66,22 @@
#include "dbNotify.h"
#include "dbScan.h"
#include "dbStaticLib.h"
+#include "dbServer.h"
#include "devSup.h"
#include "link.h"
#include "recGbl.h"
#include "recSup.h"
#include "special.h"
+#include "dbDbLink.h"
+
/***************************** Database Links *****************************/
-/* Forward definition */
+/* Forward definitions */
static lset dbDb_lset;
+static long processTarget(dbCommon *psrc, dbCommon *pdst);
+
long dbDbInitLink(struct link *plink, short dbfType)
{
DBADDR dbaddr;
@@ -138,11 +166,7 @@ static long dbDbGetValue(struct link *plink, short dbrType, void *pbuffer,
/* scan passive records if link is process passive */
if (ppv_link->pvlMask & pvlOptPP) {
- unsigned char pact = precord->pact;
-
- precord->pact = TRUE;
status = dbScanPassive(precord, paddr->precord);
- precord->pact = pact;
if (status)
return status;
}
@@ -311,22 +335,10 @@ static long dbDbPutValue(struct link *plink, short dbrType,
return status;
if (paddr->pfield == (void *) &pdest->proc ||
- (ppv_link->pvlMask & pvlOptPP && pdest->scan == 0)) {
- /* if dbPutField caused asyn record to process */
- /* ask for reprocessing*/
- if (pdest->putf) {
- pdest->rpro = TRUE;
- } else { /* process dest record with source's PACT true */
- unsigned char pact;
-
- if (psrce && psrce->ppn)
- dbNotifyAdd(psrce, pdest);
- pact = psrce->pact;
- psrce->pact = TRUE;
- status = dbProcess(pdest);
- psrce->pact = pact;
- }
+ (ppv_link->pvlMask & pvlOptPP && pdest->scan == 0)) {
+ status = processTarget(psrce, pdest);
}
+
return status;
}
@@ -356,3 +368,75 @@ static lset dbDb_lset = {
dbDbPutValue, NULL,
dbDbScanFwdLink, doLocked
};
+
+
+/*
+ * Process a record if its scan field is passive.
+ */
+long dbScanPassive(dbCommon *pfrom, dbCommon *pto)
+{
+ /* if not passive we're done */
+ if (pto->scan != 0)
+ return 0;
+
+ return processTarget(pfrom, pto);
+}
+
+static long processTarget(dbCommon *psrc, dbCommon *pdst)
+{
+ char context[40] = "";
+ int trace = dbAccessDebugPUTF && *dbLockSetAddrTrace(psrc);
+ long status;
+ epicsUInt8 pact = psrc->pact;
+
+ psrc->pact = TRUE;
+
+ if (psrc && psrc->ppn)
+ dbNotifyAdd(psrc, pdst);
+
+ if (trace && dbServerClient(context, sizeof(context))) {
+ /* No client, use thread name */
+ strncpy(context, epicsThreadGetNameSelf(), sizeof(context));
+ context[sizeof(context) - 1] = 0;
+ }
+
+ if (!pdst->pact) {
+ /* Normal propagation of PUTF from src to dst */
+ if (trace)
+ printf("%s: '%s' -> '%s' with PUTF=%u\n",
+ context, psrc->name, pdst->name, psrc->putf);
+
+ if (pdst->putf)
+ errlogPrintf("Warning: '%s.PUTF' found true with PACT false\n",
+ pdst->name);
+
+ pdst->putf = psrc->putf;
+ }
+ else if (psrc->putf) {
+ /* The dst record is busy (awaiting async reprocessing) and
+ * we were originally triggered by a call to dbPutField(),
+ * so we mark the dst record for reprocessing once the async
+ * completion is over.
+ */
+ if (trace)
+ printf("%s: '%s' -> Active '%s', setting RPRO=1\n",
+ context, psrc->name, pdst->name);
+
+ pdst->putf = FALSE;
+ pdst->rpro = TRUE;
+ }
+ else {
+ /* The dst record is busy, but we weren't triggered by a call
+ * to dbPutField(). Do nothing.
+ */
+ if (trace)
+ printf("%s: '%s' -> Active '%s', done\n",
+ context, psrc->name, pdst->name);
+ }
+
+ status = dbProcess(pdst);
+
+ psrc->pact = pact;
+
+ return status;
+}
diff --git a/modules/database/src/ioc/db/dbJLink.c b/modules/database/src/ioc/db/dbJLink.c
index ea054eee9..3acf75660 100644
--- a/modules/database/src/ioc/db/dbJLink.c
+++ b/modules/database/src/ioc/db/dbJLink.c
@@ -2,7 +2,7 @@
* Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne
* National Laboratory.
* EPICS BASE is distributed subject to a Software License Agreement found
-* in file LICENSE that is included with this distribution.
+* in file LICENSE that is included with this distribution.
\*************************************************************************/
/* dbJLink.c */
@@ -25,19 +25,33 @@
#include "dbLock.h"
#include "dbStaticLib.h"
#include "link.h"
+#include "epicsExport.h"
-#define IFDEBUG(n) if(parser->parse_debug)
+epicsShareDef int dbJLinkDebug = 0;
+epicsExportAddress(int, dbJLinkDebug);
+
+#define IFDEBUG(n) if (dbJLinkDebug >= (n))
typedef struct parseContext {
jlink *pjlink;
jlink *product;
short dbfType;
short jsonDepth;
- unsigned key_is_link:1;
- unsigned parse_debug:1;
- unsigned lset_debug:1;
} parseContext;
+epicsShareDef const char *jlif_result_name[2] = {
+ "jlif_stop",
+ "jlif_continue",
+};
+
+epicsShareDef const char *jlif_key_result_name[5] = {
+ "jlif_key_stop",
+ "jlif_key_continue",
+ "jlif_key_child_inlink",
+ "jlif_key_child_outlink",
+ "jlif_key_child_fwdlink"
+};
+
#define CALL_OR_STOP(routine) !(routine) ? jlif_stop : (routine)
static int dbjl_return(parseContext *parser, jlif_result result) {
@@ -45,8 +59,8 @@ static int dbjl_return(parseContext *parser, jlif_result result) {
IFDEBUG(10) {
printf("dbjl_return(%s@%p, %d)\t", pjlink ? pjlink->pif->name : "", pjlink, result);
- printf(" jsonDepth=%d, parseDepth=%d, key_is_link=%d\n",
- parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->key_is_link);
+ printf(" jsonDepth=%d, parseDepth=%d, dbfType=%d\n",
+ parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->dbfType);
}
if (result == jlif_stop && pjlink) {
@@ -59,6 +73,9 @@ static int dbjl_return(parseContext *parser, jlif_result result) {
pjlink->pif->free_jlink(pjlink);
}
+ IFDEBUG(10)
+ printf(" returning %d %s\n", result,
+ result == jlif_stop ? "*** STOP ***" : "Continue");
return result;
}
@@ -68,8 +85,8 @@ static int dbjl_value(parseContext *parser, jlif_result result) {
IFDEBUG(10) {
printf("dbjl_value(%s@%p, %d)\t", pjlink ? pjlink->pif->name : "", pjlink, result);
- printf(" jsonDepth=%d, parseDepth=%d, key_is_link=%d\n",
- parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->key_is_link);
+ printf(" jsonDepth=%d, parseDepth=%d, dbfType=%d\n",
+ parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->dbfType);
}
if (result == jlif_stop || pjlink->parseDepth > 0)
@@ -81,7 +98,6 @@ static int dbjl_value(parseContext *parser, jlif_result result) {
} else if (parent->pif->end_child) {
parent->pif->end_child(parent, pjlink);
}
- pjlink->debug = 0;
parser->pjlink = parent;
@@ -159,29 +175,46 @@ static int dbjl_start_map(void *ctx) {
if (!pjlink) {
IFDEBUG(10) {
printf("dbjl_start_map(NULL)\t");
- printf(" jsonDepth=%d, parseDepth=00, key_is_link=%d\n",
- parser->jsonDepth, parser->key_is_link);
+ printf(" jsonDepth=%d, parseDepth=00, dbfType=%d\n",
+ parser->jsonDepth, parser->dbfType);
}
assert(parser->jsonDepth == 0);
parser->jsonDepth++;
- parser->key_is_link = 1;
return jlif_continue; /* Opening '{' */
}
IFDEBUG(10) {
printf("dbjl_start_map(%s@%p)\t", pjlink ? pjlink->pif->name : "", pjlink);
- printf(" jsonDepth=%d, parseDepth=%d, key_is_link=%d\n",
- parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->key_is_link);
+ printf(" jsonDepth=%d, parseDepth=%d, dbfType=%d\n",
+ parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->dbfType);
}
pjlink->parseDepth++;
parser->jsonDepth++;
result = CALL_OR_STOP(pjlink->pif->parse_start_map)(pjlink);
- if (result == jlif_key_child_link) {
- parser->key_is_link = 1;
+ switch (result) {
+ case jlif_key_child_inlink:
+ parser->dbfType = DBF_INLINK;
result = jlif_continue;
+ break;
+ case jlif_key_child_outlink:
+ parser->dbfType = DBF_OUTLINK;
+ result = jlif_continue;
+ break;
+ case jlif_key_child_fwdlink:
+ parser->dbfType = DBF_FWDLINK;
+ result = jlif_continue;
+ break;
+ case jlif_key_stop:
+ case jlif_key_continue:
+ break;
+ default:
+ errlogPrintf("dbJLinkInit: Bad return %d from '%s'::parse_start_map()\n",
+ result, pjlink->pif->name);
+ result = jlif_stop;
+ break;
}
IFDEBUG(10)
@@ -196,8 +229,9 @@ static int dbjl_map_key(void *ctx, const unsigned char *key, size_t len) {
char *link_name;
linkSup *linkSup;
jlif *pjlif;
+ jlink *child;
- if (!parser->key_is_link) {
+ if (parser->dbfType == 0) {
if (!pjlink) {
errlogPrintf("dbJLinkInit: Illegal second link key '%.*s'\n",
(int) len, key);
@@ -207,8 +241,8 @@ static int dbjl_map_key(void *ctx, const unsigned char *key, size_t len) {
IFDEBUG(10) {
printf("dbjl_map_key(%s@%p, \"%.*s\")\t",
pjlink->pif->name, pjlink, (int) len, key);
- printf(" jsonDepth=%d, parseDepth=%d, key_is_link=%d\n",
- parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->key_is_link);
+ printf(" jsonDepth=%d, parseDepth=%d, dbfType=%d\n",
+ parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->dbfType);
}
assert(pjlink->parseDepth > 0);
@@ -219,8 +253,8 @@ static int dbjl_map_key(void *ctx, const unsigned char *key, size_t len) {
IFDEBUG(10) {
printf("dbjl_map_key(NULL, \"%.*s\")\t", (int) len, key);
- printf(" jsonDepth=%d, parseDepth=00, key_is_link=%d\n",
- parser->jsonDepth, parser->key_is_link);
+ printf(" jsonDepth=%d, parseDepth=00, dbfType=%d\n",
+ parser->jsonDepth, parser->dbfType);
}
link_name = dbmfStrndup((const char *) key, len);
@@ -241,27 +275,35 @@ static int dbjl_map_key(void *ctx, const unsigned char *key, size_t len) {
return dbjl_return(parser, jlif_stop);
}
- dbmfFree(link_name);
-
- pjlink = pjlif->alloc_jlink(parser->dbfType);
- if (!pjlink) {
- errlogPrintf("dbJLinkInit: Out of memory\n");
+ child = pjlif->alloc_jlink(parser->dbfType);
+ if (!child) {
+ errlogPrintf("dbJLinkInit: Link type '%s' allocation failed. \n",
+ link_name);
+ dbmfFree(link_name);
return dbjl_return(parser, jlif_stop);
}
- pjlink->pif = pjlif;
- pjlink->parent = NULL;
- pjlink->parseDepth = 0;
- pjlink->debug = !!parser->lset_debug;
+
+ child->pif = pjlif;
+ child->parseDepth = 0;
+ child->debug = 0;
if (parser->pjlink) {
/* We're starting a child link, save its parent */
- pjlink->parent = parser->pjlink;
+ child->parent = pjlink;
+
+ if (pjlink->pif->start_child)
+ pjlink->pif->start_child(pjlink, child);
}
- parser->pjlink = pjlink;
- parser->key_is_link = 0;
+ else
+ child->parent = NULL;
+
+ parser->pjlink = child;
+ parser->dbfType = 0;
+
+ dbmfFree(link_name);
IFDEBUG(8)
- printf("dbjl_map_key: New %s@%p\n", pjlink ? pjlink->pif->name : "", pjlink);
+ printf("dbjl_map_key: New %s@%p\n", child ? child->pif->name : "", child);
return jlif_continue;
}
@@ -274,9 +316,9 @@ static int dbjl_end_map(void *ctx) {
IFDEBUG(10) {
printf("dbjl_end_map(%s@%p)\t",
pjlink ? pjlink->pif->name : "NULL", pjlink);
- printf(" jsonDepth=%d, parseDepth=%d, key_is_link=%d\n",
+ printf(" jsonDepth=%d, parseDepth=%d, dbfType=%d\n",
parser->jsonDepth, pjlink ? pjlink->parseDepth : 0,
- parser->key_is_link);
+ parser->dbfType);
}
parser->jsonDepth--;
@@ -298,8 +340,8 @@ static int dbjl_start_array(void *ctx) {
IFDEBUG(10) {
printf("dbjl_start_array(%s@%p)\t", pjlink ? pjlink->pif->name : "", pjlink);
- printf(" jsonDepth=%d, parseDepth=%d, key_is_link=%d\n",
- parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->key_is_link);
+ printf(" jsonDepth=%d, parseDepth=%d, dbfType=%d\n",
+ parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->dbfType);
}
assert(pjlink);
@@ -316,8 +358,8 @@ static int dbjl_end_array(void *ctx) {
IFDEBUG(10) {
printf("dbjl_end_array(%s@%p)\t", pjlink ? pjlink->pif->name : "", pjlink);
- printf(" jsonDepth=%d, parseDepth=%d, key_is_link=%d\n",
- parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->key_is_link);
+ printf(" jsonDepth=%d, parseDepth=%d, dbfType=%d\n",
+ parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->dbfType);
}
assert(pjlink);
@@ -335,7 +377,7 @@ static yajl_callbacks dbjl_callbacks = {
};
long dbJLinkParse(const char *json, size_t jlen, short dbfType,
- jlink **ppjlink, unsigned opts)
+ jlink **ppjlink)
{
parseContext context, *parser = &context;
yajl_alloc_funcs dbjl_allocs;
@@ -347,17 +389,14 @@ long dbJLinkParse(const char *json, size_t jlen, short dbfType,
parser->product = NULL;
parser->dbfType = dbfType;
parser->jsonDepth = 0;
- parser->key_is_link = 0;
- parser->parse_debug = !!(opts&LINK_DEBUG_JPARSE);
- parser->lset_debug = !!(opts&LINK_DEBUG_LSET);
IFDEBUG(10)
printf("dbJLinkInit(\"%.*s\", %d, %p)\n",
(int) jlen, json, dbfType, ppjlink);
IFDEBUG(10)
- printf("dbJLinkInit: jsonDepth=%d, key_is_link=%d\n",
- parser->jsonDepth, parser->key_is_link);
+ printf("dbJLinkInit: jsonDepth=%d, dbfType=%d\n",
+ parser->jsonDepth, parser->dbfType);
yajl_set_default_alloc_funcs(&dbjl_allocs);
yh = yajl_alloc(&dbjl_callbacks, &dbjl_allocs, parser);
@@ -365,8 +404,14 @@ long dbJLinkParse(const char *json, size_t jlen, short dbfType,
return S_db_noMemory;
ys = yajl_parse(yh, (const unsigned char *) json, jlen);
- if (ys == yajl_status_ok)
+ IFDEBUG(10)
+ printf("dbJLinkInit: yajl_parse() returned %d\n", ys);
+
+ if (ys == yajl_status_ok) {
ys = yajl_complete_parse(yh);
+ IFDEBUG(10)
+ printf("dbJLinkInit: yajl_complete_parse() returned %d\n", ys);
+ }
switch (ys) {
unsigned char *err;
@@ -378,6 +423,9 @@ long dbJLinkParse(const char *json, size_t jlen, short dbfType,
break;
case yajl_status_error:
+ IFDEBUG(10)
+ printf(" jsonDepth=%d, product=%p, pjlink=%p\n",
+ parser->jsonDepth, parser->product, parser->pjlink);
err = yajl_get_error(yh, 1, (const unsigned char *) json, jlen);
errlogPrintf("dbJLinkInit: %s\n", err);
yajl_free_error(yh, err);
@@ -389,18 +437,24 @@ long dbJLinkParse(const char *json, size_t jlen, short dbfType,
}
yajl_free(yh);
+
+ IFDEBUG(10)
+ printf("dbJLinkInit: returning status=0x%lx\n\n",
+ status);
+
return status;
}
long dbJLinkInit(struct link *plink)
{
- jlink *pjlink;
-
assert(plink);
- pjlink = plink->value.json.jlink;
- if (pjlink)
- plink->lset = pjlink->pif->get_lset(pjlink);
+ if (plink->type == JSON_LINK) {
+ jlink *pjlink = plink->value.json.jlink;
+
+ if (pjlink)
+ plink->lset = pjlink->pif->get_lset(pjlink);
+ }
dbLinkOpen(plink);
return 0;
diff --git a/modules/database/src/ioc/db/dbJLink.h b/modules/database/src/ioc/db/dbJLink.h
index 61b59670b..bd1a6c8a2 100644
--- a/modules/database/src/ioc/db/dbJLink.h
+++ b/modules/database/src/ioc/db/dbJLink.h
@@ -2,7 +2,7 @@
* Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne
* National Laboratory.
* EPICS BASE is distributed subject to a Software License Agreement found
-* in file LICENSE that is included with this distribution.
+* in file LICENSE that is included with this distribution.
\*************************************************************************/
/* dbJLink.h */
@@ -21,12 +21,16 @@ typedef enum {
jlif_continue = 1
} jlif_result;
+epicsShareExtern const char *jlif_result_name[2];
+
typedef enum {
jlif_key_stop = jlif_stop,
jlif_key_continue = jlif_continue,
- jlif_key_child_link
+ jlif_key_child_inlink, jlif_key_child_outlink, jlif_key_child_fwdlink
} jlif_key_result;
+epicsShareExtern const char *jlif_key_result_name[5];
+
struct link;
struct lset;
struct jlif;
@@ -35,7 +39,7 @@ typedef struct jlink {
struct jlif *pif; /* Link methods */
struct jlink *parent; /* NULL for top-level links */
int parseDepth; /* Used by parser, unused afterwards */
- unsigned debug:1; /* set by caller of jlif operations to request debug output to console */
+ unsigned debug:1; /* Set to request debug output to console */
/* Link types extend or embed this structure for private storage */
} jlink;
@@ -72,8 +76,9 @@ typedef struct jlif {
/* Optional, parser saw a string value */
jlif_key_result (*parse_start_map)(jlink *);
- /* Optional, parser saw an open-brace '{'. Return jlif_key_child_link
- * to expect a child link next (extra key/value pairs may follow).
+ /* Optional, parser saw an open-brace '{'. Return jlif_key_child_inlink,
+ * jlif_key_child_outlink, or jlif_key_child_fwdlink to expect a child
+ * link next (extra key/value pairs may follow)
*/
jlif_result (*parse_map_key)(jlink *, const char *key, size_t len);
@@ -90,7 +95,8 @@ typedef struct jlif {
void (*end_child)(jlink *parent, jlink *child);
/* Optional, called with pointer to the new child link after
- * parse_start_map() returned jlif_key_child_link */
+ * the child link has finished parsing successfully
+ */
struct lset* (*get_lset)(const jlink *);
/* Required, return lset for this link instance */
@@ -98,7 +104,7 @@ typedef struct jlif {
void (*report)(const jlink *, int level, int indent);
/* Optional, print status information about this link instance, then
* if (level > 0) print a link identifier (at indent+2) and call
- * dbJLinkReport(child, level-1, indent+4)
+ * dbJLinkReport(child, level-1, indent+4)
* for each child.
*/
@@ -107,13 +113,19 @@ typedef struct jlif {
* Stop immediately and return status if non-zero.
*/
+ void (*start_child)(jlink *parent, jlink *child);
+ /* Optional, called with pointer to the new child link after
+ * parse_start_map() returned a jlif_key_child_link value and
+ * the child link has been allocated (but not parsed yet)
+ */
+
/* Link types must NOT extend this table with their own routines,
* this space is reserved for extensions to the jlink interface.
*/
} jlif;
epicsShareFunc long dbJLinkParse(const char *json, size_t len, short dbfType,
- jlink **ppjlink, unsigned opts);
+ jlink **ppjlink);
epicsShareFunc long dbJLinkInit(struct link *plink);
epicsShareFunc void dbJLinkFree(jlink *);
@@ -130,4 +142,3 @@ epicsShareFunc long dbJLinkMapAll(char *recname, jlink_map_fn rtn, void *ctx);
#endif
#endif /* INC_dbJLink_H */
-
diff --git a/modules/database/src/ioc/db/dbLink.c b/modules/database/src/ioc/db/dbLink.c
index bcdbec473..7c37058b0 100644
--- a/modules/database/src/ioc/db/dbLink.c
+++ b/modules/database/src/ioc/db/dbLink.c
@@ -265,8 +265,19 @@ int dbIsLinkConnected(const struct link *plink)
{
lset *plset = plink->lset;
- if (!plset || !plset->isConnected)
+ if (!plset)
return FALSE;
+ if (!plset->isVolatile)
+ return TRUE;
+
+ if (!plset->isConnected) {
+ struct dbCommon *precord = plink->precord;
+
+ errlogPrintf("dbLink: Link type for '%s.%s' is volatile but has no"
+ " lset::isConnected() method\n",
+ precord->name, dbLinkFieldName(plink));
+ return FALSE;
+ }
return plset->isConnected(plink);
}
diff --git a/modules/database/src/ioc/db/dbLink.h b/modules/database/src/ioc/db/dbLink.h
index 16066e0d5..94e1f32a3 100644
--- a/modules/database/src/ioc/db/dbLink.h
+++ b/modules/database/src/ioc/db/dbLink.h
@@ -27,54 +27,337 @@ extern "C" {
struct dbLocker;
+/** @file dbLink.h
+ * @brief Link Support API
+ *
+ * Link support run-time API, all link types provide an lset which is used by
+ * the IOC database to control and operate the link. This file also declares the
+ * dbLink routines that IOC, record and device code can call to perform link
+ * operations.
+ */
+
+/** @brief callback routine for locked link operations
+ *
+ * Called by the lset::doLocked method to permit multiple link operations
+ * while the link instance is locked.
+ *
+ * @param plink the link
+ * @param priv context for the callback routine
+ */
typedef long (*dbLinkUserCallback)(struct link *plink, void *priv);
+/** @brief Link Support Entry Table
+ *
+ * This structure provides information about and methods for an individual link
+ * type. A pointer to this structure is included in every link's lset field, and
+ * is used to perform operations on the link. For JSON links the pointer is
+ * obtained by calling pjlink->pif->get_lset() at link initialization time,
+ * immediately before calling dbLinkOpen() to activate the link.
+ */
typedef struct lset {
/* Characteristics of the link type */
+
+ /** @brief link constancy
+ *
+ * 1 means this is a constant link type whose value doesn't change.
+ * The link's value will be obtained using one of the methods loadScalar,
+ * loadLS or loadArray.
+ */
const unsigned isConstant:1;
+
+ /** @brief link volatility
+ *
+ * 0 means the link is always connected.
+ */
const unsigned isVolatile:1;
- /* Activation */
+ /** @brief activate link
+ *
+ * Optional, called whenever a JSON link is initialized or added at runtime.
+ *
+ * @param plink the link
+ */
void (*openLink)(struct link *plink);
- /* Destructor */
+ /** @brief deactivate link
+ *
+ * Optional, called whenever a link address is changed at runtime, or the
+ * IOC is shutting down.
+ *
+ * @param locker
+ * @param plink the link
+ */
void (*removeLink)(struct dbLocker *locker, struct link *plink);
- /* Const init, data type hinting */
+ /* Constant link initialization and data type hinting */
+
+ /** @brief load constant scalar from link type
+ *
+ * Usually called during IOC initialization, constant link types must copy a
+ * scalar value of the indicated data type to the buffer provided and return
+ * 0. A non-constant link type can use this method call as an early hint
+ * that subsequent calls to dbGetLink() will request scalar data of the
+ * indicated type, although the type might change.
+ *
+ * @param plink the link
+ * @param dbrType data type code
+ * @param pbuffer where to put the value
+ * @returns 0 if a value was loaded, non-zero otherwise
+ */
long (*loadScalar)(struct link *plink, short dbrType, void *pbuffer);
+
+ /** @brief load constant long string from link type
+ *
+ * Usually called during IOC initialization, constant link types must copy a
+ * nil-terminated string up to size characters long to the buffer provided,
+ * and write the length of that string to the plen location. A non-constant
+ * link type can use this as an early hint that subsequent calls to
+ * dbGetLink() will request long string data, although this might change.
+ *
+ * @param plink the link
+ * @param pbuffer where to put the string
+ * @param size length of pbuffer in chars
+ * @param plen set to number of chars written
+ * @returns status value
+ */
long (*loadLS)(struct link *plink, char *pbuffer, epicsUInt32 size,
epicsUInt32 *plen);
+
+ /** @brief load constant array from link type
+ *
+ * Usually called during IOC initialization, constant link types must copy
+ * an array value of the indicated data type to the buffer provided, update
+ * the pnRequest location to indicate how many elements were loaded, and
+ * return 0. A non-constant link type can use this method call as an early
+ * hint that subsequent calls to dbGetLink() will request array data of the
+ * indicated type and max size, although the request might change.
+ *
+ * @param plink the link
+ * @param dbrType data type code
+ * @param pbuffer where to put the value
+ * @param pnRequest Max elements on entry, actual on exit
+ * @returns 0 if elements were loaded, non-zero otherwise
+ */
long (*loadArray)(struct link *plink, short dbrType, void *pbuffer,
long *pnRequest);
/* Metadata */
+
+ /** @brief return link connection status
+ *
+ * Return an indication whether this link is connected or not. This routine
+ * is polled by the calcout and some external record types. Not required for
+ * non-volatile link types, which are by definition always connected.
+ *
+ * @param plink the link
+ * @returns 1 if connected, 0 if disconnected
+ */
int (*isConnected)(const struct link *plink);
+
+ /** @brief get data type of link destination
+ *
+ * Called on both input and output links by long string support code to
+ * decide whether to use DBR_CHAR/DBR_UCHAR or DBR_STRING for a subsequent
+ * dbPutLink() or dbGetLink() call. Optional, but if not provided long
+ * strings cannot be transported over this link type, and no warning or
+ * error will appear to explain why. Not required for constant link types.
+ *
+ * @param plink the link
+ * @returns DBF_* type code, or -1 on error/disconnected link
+ */
int (*getDBFtype)(const struct link *plink);
- long (*getElements)(const struct link *plink, long *nelements);
/* Get data */
+
+ /** @brief get array size of an input link
+ *
+ * Called on input links by the compress record type for memory allocation
+ * purposes, before using the dbGetLink() routine to fetch the actual
+ * array data.
+ *
+ * @param plink the link
+ * @param pnElements where to put the answer
+ * @returns status value
+ */
+ long (*getElements)(const struct link *plink, long *pnElements);
+
+ /** @brief get value from an input link
+ *
+ * Called to fetch data from the link, which must be converted into the
+ * given data type and placed in the buffer indicated. The actual number of
+ * elements retrieved should be updated in the pnRequest location. If this
+ * method returns an error status value, the link's record will be placed
+ * into an Invalid severity / Link Alarm state by the dbGetLink() routine
+ * that calls this method.
+ *
+ * @param plink the link
+ * @param dbrType data type code
+ * @param pbuffer where to put the value
+ * @param pnRequest max elements on entry, actual on exit
+ * @returns status value
+ */
long (*getValue)(struct link *plink, short dbrType, void *pbuffer,
long *pnRequest);
+
+ /** @brief get the control range for an output link
+ *
+ * Called to fetch the control range for the link target, as a pair of
+ * double values for the lowest and highest values that the target will
+ * accept. This method is not used at all by the IOC or built-in record
+ * types, although external record types may require it.
+ *
+ * @param plink the link
+ * @param lo lowest accepted value
+ * @param hi highest accepted value
+ * @returns status value
+ */
long (*getControlLimits)(const struct link *plink, double *lo, double *hi);
+
+ /** @brief get the display range from an input link
+ *
+ * Called to fetch the display range for an input link target, as a pair of
+ * double values for the lowest and highest values that the PV expects to
+ * return. This method is used by several built-in record types to obtain
+ * the display range for their generic input links.
+ *
+ * @param plink the link
+ * @param lo lowest accepted value
+ * @param hi highest accepted value
+ * @returns status value
+ */
long (*getGraphicLimits)(const struct link *plink, double *lo, double *hi);
+
+ /** @brief get the alarm limits from an input link
+ *
+ * Called to fetch the alarm limits for an input link target, as four
+ * double values for the warning and alarm levels that the PV checks its
+ * value against. This method is used by several built-in record types to
+ * obtain the alarm limits for their generic input links.
+ *
+ * @param plink the link
+ * @param lolo low alarm value
+ * @param lo low warning value
+ * @param hi high warning value
+ * @param hihi high alarm value
+ * @returns status value
+ */
long (*getAlarmLimits)(const struct link *plink, double *lolo, double *lo,
double *hi, double *hihi);
+
+ /** @brief get the precision from an input link
+ *
+ * Called to fetch the precision for an input link target. This method is
+ * used by several built-in record types to obtain the precision for their
+ * generic input links.
+ *
+ * @param plink the link
+ * @param precision where to put the answer
+ * @returns status value
+ */
long (*getPrecision)(const struct link *plink, short *precision);
+
+ /** @brief get the units string from an input link
+ *
+ * Called to fetch the units string for an input link target. This method is
+ * used by several built-in record types to obtain the units string for
+ * their generic input links.
+ *
+ * @param plink the link
+ * @param units where to put the answer
+ * @param unitsSize buffer size for the answer
+ * @returns status value
+ */
long (*getUnits)(const struct link *plink, char *units, int unitsSize);
+
+ /** @brief get the alarm condition from an input link
+ *
+ * Called to fetch the alarm status and severity for an input link target.
+ * Either status or severity pointers may be NULL when that value is not
+ * needed by the calling code. This method is used by several built-in
+ * record types to obtain the alarm condition for their generic input links.
+ *
+ * @param plink the link
+ * @param status where to put the alarm status (or NULL)
+ * @param severity where to put the severity (or NULL)
+ * @returns status value
+ */
long (*getAlarm)(const struct link *plink, epicsEnum16 *status,
epicsEnum16 *severity);
+
+ /** @brief get the time-stamp from an input link
+ *
+ * Called to fetch the time-stamp for an input link target. This method is
+ * used by many built-in device supports to obtain the precision for their
+ * generic input links.
+ *
+ * @param plink the link
+ * @param pstamp where to put the answer
+ * @returns status value
+ */
long (*getTimeStamp)(const struct link *plink, epicsTimeStamp *pstamp);
/* Put data */
+
+ /** @brief put a value to an output link
+ *
+ * Called to send nRequest elements of type dbrType found at pbuffer to an
+ * output link target.
+ *
+ * @param plink the link
+ * @param dbrType data type code
+ * @param pbuffer where to put the value
+ * @param nRequest number of elements to send
+ * @returns status value
+ */
long (*putValue)(struct link *plink, short dbrType,
const void *pbuffer, long nRequest);
+
+ /** @brief put a value to an output link with asynchronous completion
+ *
+ * Called to send nRequest elements of type dbrType found at pbuffer to an
+ * output link target. If the return status is zero, the link type will
+ * later indicate the put has completed by calling dbLinkAsyncComplete()
+ * from a background thread, which will be used to continue the record
+ * process operation from where it left off.
+ *
+ * @param plink the link
+ * @param dbrType data type code
+ * @param pbuffer where to put the value
+ * @param nRequest number of elements to send
+ * @returns status value
+ */
long (*putAsync)(struct link *plink, short dbrType,
const void *pbuffer, long nRequest);
/* Process */
+
+ /** @brief trigger processing of a forward link
+ *
+ * Called to trigger processing of the record pointed to by a forward link.
+ * This routine is optional, but if not provided no warning message will be
+ * shown when called by dbScanFwdLink(). JSON link types that do not support
+ * this operation should return NULL from their jlif::alloc_jlink() method
+ * if it gets called with a dbfType of DBF_FWDLINK.
+ *
+ * @param plink the link
+ */
void (*scanForward)(struct link *plink);
/* Atomicity */
+
+ /** @brief execute a callback routine with link locked
+ *
+ * Called on an input link when multiple link attributes need to be fetched
+ * in an atomic fashion. The link type must call the callback routine and
+ * prevent any background I/O from updating any cached link data until that
+ * routine returns. This method is used by most input device support to
+ * fetch the timestamp along with the value when the record's TSE field is
+ * set to epicsTimeEventDeviceTime.
+ *
+ * @param plink the link
+ * @param rtn routine to execute
+ * @returns status value
+ */
long (*doLocked)(struct link *plink, dbLinkUserCallback rtn, void *priv);
} lset;
@@ -99,7 +382,7 @@ epicsShareFunc long dbLoadLink(struct link *plink, short dbrType,
epicsShareFunc long dbLoadLinkArray(struct link *, short dbrType, void *pbuffer,
long *pnRequest);
-epicsShareFunc long dbGetNelements(const struct link *plink, long *nelements);
+epicsShareFunc long dbGetNelements(const struct link *plink, long *pnElements);
epicsShareFunc int dbIsLinkConnected(const struct link *plink); /* 0 or 1 */
epicsShareFunc int dbGetLinkDBFtype(const struct link *plink);
epicsShareFunc long dbTryGetLink(struct link *, short dbrType, void *pbuffer,
diff --git a/modules/database/src/ioc/db/dbLock.h b/modules/database/src/ioc/db/dbLock.h
index 1d6388ed3..e15ddd0e9 100644
--- a/modules/database/src/ioc/db/dbLock.h
+++ b/modules/database/src/ioc/db/dbLock.h
@@ -13,6 +13,8 @@
#ifndef INCdbLockh
#define INCdbLockh
+#include
+
#include "ellLib.h"
#include "shareLib.h"
diff --git a/modules/database/src/ioc/db/dbScan.h b/modules/database/src/ioc/db/dbScan.h
index 028d09ec8..d483a0c30 100644
--- a/modules/database/src/ioc/db/dbScan.h
+++ b/modules/database/src/ioc/db/dbScan.h
@@ -19,6 +19,7 @@
#include "menuScan.h"
#include "shareLib.h"
#include "compilerDependencies.h"
+#include "devSup.h"
#ifdef __cplusplus
extern "C" {
@@ -33,9 +34,7 @@ extern "C" {
#define MIN_PHASE SHRT_MIN
/*definitions for I/O Interrupt Scanning */
-struct ioscan_head;
-
-typedef struct ioscan_head *IOSCANPVT;
+/* IOSCANPVT now defined in devSup.h */
typedef struct event_list *EVENTPVT;
struct dbCommon;
diff --git a/modules/database/src/ioc/db/dbState.h b/modules/database/src/ioc/db/dbState.h
index abd23259e..c7cd81c52 100644
--- a/modules/database/src/ioc/db/dbState.h
+++ b/modules/database/src/ioc/db/dbState.h
@@ -15,6 +15,10 @@
#include "shareLib.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/** @file dbState.h
* @brief Generic IOC state facility
*
@@ -89,4 +93,9 @@ epicsShareFunc void dbStateShow(dbStateId id, unsigned int level);
*/
epicsShareFunc void dbStateShowAll(unsigned int level);
+
+#ifdef __cplusplus
+}
+#endif
+
#endif // INCdbStateH
diff --git a/modules/database/src/ioc/db/recGbl.c b/modules/database/src/ioc/db/recGbl.c
index 12c0a0e0f..5ff4730fe 100644
--- a/modules/database/src/ioc/db/recGbl.c
+++ b/modules/database/src/ioc/db/recGbl.c
@@ -19,6 +19,7 @@
#include "alarm.h"
#include "dbDefs.h"
+#include "alarm.h"
#include "epicsMath.h"
#include "epicsPrint.h"
#include "epicsStdlib.h"
@@ -180,6 +181,9 @@ unsigned short recGblResetAlarms(void *precord)
epicsEnum16 val_mask = 0;
epicsEnum16 stat_mask = 0;
+ if (new_sevr > INVALID_ALARM)
+ new_sevr = INVALID_ALARM;
+
pdbc->stat = new_stat;
pdbc->sevr = new_sevr;
pdbc->nsta = 0;
diff --git a/modules/database/src/ioc/dbStatic/dbStaticLib.c b/modules/database/src/ioc/dbStatic/dbStaticLib.c
index 1a3d9883f..7cc2645c1 100644
--- a/modules/database/src/ioc/dbStatic/dbStaticLib.c
+++ b/modules/database/src/ioc/dbStatic/dbStaticLib.c
@@ -2241,7 +2241,7 @@ long dbInitRecordLinks(dbRecordType *rtyp, struct dbCommon *prec)
if(!plink->text)
continue;
- if(dbParseLink(plink->text, pflddes->field_type, &link_info, 0)!=0) {
+ 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.
*/
@@ -2270,7 +2270,7 @@ void dbFreeLinkInfo(dbLinkInfo *pinfo)
pinfo->target = NULL;
}
-long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo, unsigned opts)
+long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo)
{
char *pstr;
size_t len;
@@ -2306,7 +2306,7 @@ long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo, unsigned opts)
/* Check for braces => JSON */
if (*str == '{' && str[len-1] == '}') {
- if (dbJLinkParse(str, len, ftype, &pinfo->jlink, opts))
+ if (dbJLinkParse(str, len, ftype, &pinfo->jlink))
goto fail;
pinfo->ltype = JSON_LINK;
@@ -2353,9 +2353,13 @@ long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo, unsigned opts)
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);
+ if (pinfo->ltype != RF_IO) {
+ if (!parm) {
+ pinfo->target[0] = '\0';
+ } else {
+ /* 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);
@@ -2641,21 +2645,8 @@ long dbPutString(DBENTRY *pdbentry,const char *pstring)
case DBF_FWDLINK: {
dbLinkInfo link_info;
DBLINK *plink = (DBLINK *)pfield;
- DBENTRY infoentry;
- unsigned opts = 0;
- if(pdbentry->precnode && ellCount(&pdbentry->precnode->infoList)) {
- dbCopyEntryContents(pdbentry, &infoentry);
-
- if(dbFindInfo(&infoentry, "base:lsetDebug")==0 && epicsStrCaseCmp(dbGetInfoString(&infoentry), "YES")==0)
- opts |= LINK_DEBUG_LSET;
- if(dbFindInfo(&infoentry, "base:jlinkDebug")==0 && epicsStrCaseCmp(dbGetInfoString(&infoentry), "YES")==0)
- opts |= LINK_DEBUG_JPARSE;
-
- dbFinishEntry(&infoentry);
- }
-
- status = dbParseLink(pstring, pflddes->field_type, &link_info, opts);
+ status = dbParseLink(pstring, pflddes->field_type, &link_info);
if (status) break;
if (plink->type==CONSTANT && plink->value.constantStr==NULL) {
diff --git a/modules/database/src/ioc/dbStatic/dbStaticPvt.h b/modules/database/src/ioc/dbStatic/dbStaticPvt.h
index 58d32c28c..85fc02217 100644
--- a/modules/database/src/ioc/dbStatic/dbStaticPvt.h
+++ b/modules/database/src/ioc/dbStatic/dbStaticPvt.h
@@ -5,7 +5,7 @@
* Operator of Los Alamos National Laboratory.
* EPICS BASE Versions 3.13.7
* and higher are distributed subject to a Software License Agreement found
-* in file LICENSE that is included with this distribution.
+* in file LICENSE that is included with this distribution.
\*************************************************************************/
/* dbStaticPvt.h */
/*
@@ -59,13 +59,10 @@ typedef struct dbLinkInfo {
long dbInitRecordLinks(dbRecordType *rtyp, struct dbCommon *prec);
-#define LINK_DEBUG_LSET 1
-#define LINK_DEBUG_JPARSE 2
-
/* Parse link string. no record locks needed.
* on success caller must free pinfo->target
*/
-epicsShareFunc long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo, unsigned opts);
+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.
diff --git a/modules/database/src/ioc/dbStatic/devSup.h b/modules/database/src/ioc/dbStatic/devSup.h
index bd900cae4..73dba198c 100644
--- a/modules/database/src/ioc/dbStatic/devSup.h
+++ b/modules/database/src/ioc/dbStatic/devSup.h
@@ -6,7 +6,11 @@
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
-/* devSup.h Device Support */
+/** @file devSup.h
+ *
+ * @brief Device support routines
+ */
+
/*
* Author: Marty Kraimer
* Date: 6-1-90
@@ -21,6 +25,111 @@
/* structures defined elsewhere */
struct dbCommon;
struct devSup;
+typedef struct ioscan_head *IOSCANPVT;
+struct link; /* aka DBLINK */
+
+/** Type safe version of 'struct dset'
+ *
+ * Recommended usage:
+ *
+ * In Makefile:
+ @code
+ USR_CFLAGS += -DUSE_TYPED_RSET -DUSE_TYPED_DSET
+ @endcode
+ *
+ * In C source file:
+ @code
+ #include
+ #include // For IOCSCANPVT
+ ...
+ #include // defines epicsExportSharedSymbols
+ ...
+ static long init_record(dbCommon *prec);
+ static long get_iointr_info(int detach, dbCommon *prec, IOCSCANPVT* pscan);
+ static long longin_read(longinRecord *prec);
+
+ const struct {
+ dset common;
+ long (*read)(longinRecord *prec);
+ } devLiDevName = {
+ {
+ 5, // 4 from dset + 1 from longinRecord
+ NULL,
+ NULL,
+ &init_record,
+ &get_iointr_info
+ },
+ &longin_read
+ };
+ epicsExportAddress(dset, devLiDevName);
+ @endcode
+ */
+typedef struct typed_dset {
+ /** Number of function pointers which follow.
+ * The value depends on the recordtype, but must be >=4 */
+ long number;
+ /** Called from dbior() */
+ long (*report)(int lvl);
+ /** Called twice during iocInit().
+ * First with @a after = 0 before init_record() or array field allocation.
+ * Again with @a after = 1 after init_record() has finished.
+ */
+ long (*init)(int after);
+ /** Called once per record instance */
+ long (*init_record)(struct dbCommon *prec);
+ /** Called when SCAN="I/O Intr" on startup, or after SCAN is changed.
+ *
+ * Caller must assign the third arguement (IOCSCANPVT*). eg.
+ @code
+ struct mpvt {
+ IOSCANPVT drvlist;
+ };
+ ...
+ // init_record() routine calls
+ scanIoInit(&pvt->drvlist);
+ ...
+ static long get_ioint_info(int detach, struct dbCommon *prec, IOCSCANPVT* pscan) {
+ if(prec->dpvt)
+ *pscan = &((mypvt*)prec->dpvt)->drvlist;
+ @endcode
+ *
+ * When a particular record instance can/will only used a single scan list,
+ * the @a detach argument can be ignored.
+ *
+ * If this is not the case, then the following should be noted.
+ * + get_ioint_info() is called with @a detach = 0 to fetch the scan list to
+ * which this record will be added.
+ * + get_ioint_info() is called later with @a detach = 1 to fetch the scan
+ * list from which this record should be removed.
+ * + Calls will be balanced, so a call with @a detach = 0 will be followed
+ * by one with @a detach = 1.
+ *
+ * @note get_ioint_info() will be called during IOC shutdown if the
+ * dsxt::del_record() extended callback is defined. (from 3.15.0.1)
+ */
+ long (*get_ioint_info)(int detach, struct dbCommon *prec, IOSCANPVT* pscan);
+ /* Any further functions are specified by the record type. */
+} typed_dset;
+
+/** Device support extension table.
+ *
+ * Optional routines to allow run-time address modifications to be communicated
+ * to device support, which must register a struct dsxt by calling devExtend()
+ * from its init() routine.
+ */
+typedef struct dsxt {
+ /** Optional, called to offer device support a new record to control.
+ *
+ * Routine may return a non-zero error code to refuse record.
+ */
+ long (*add_record)(struct dbCommon *precord);
+ /** Optional, called to remove record from device support control.
+ *
+ * Routine return a non-zero error code to refuse record removal.
+ */
+ long (*del_record)(struct dbCommon *precord);
+ /* Only future Base releases may extend this table. */
+} dsxt;
#ifdef __cplusplus
extern "C" {
@@ -29,6 +138,8 @@ extern "C" {
typedef long (*DEVSUPFUN)(); /* ptr to device support function*/
#endif
+#ifndef USE_TYPED_DSET
+
typedef struct dset { /* device support entry table */
long number; /*number of support routines*/
DEVSUPFUN report; /*print report*/
@@ -38,11 +149,15 @@ typedef struct dset { /* device support entry table */
/*other functions are record dependent*/
} dset;
-typedef struct dsxt { /* device support extension table */
- long (*add_record)(struct dbCommon *precord);
- long (*del_record)(struct dbCommon *precord);
- /* Recordtypes are *not* allowed to extend this table */
-} dsxt;
+#else
+typedef typed_dset dset;
+#endif /* USE_TYPED_DSET */
+
+/** Fetch INP or OUT link (or NULL if record type has neither).
+ *
+ * Recommended for use in device support init_record()
+ */
+epicsShareFunc struct link* dbGetDevLink(struct dbCommon* prec);
epicsShareExtern dsxt devSoft_DSXT; /* Allow anything table */
diff --git a/modules/database/src/ioc/dbStatic/drvSup.h b/modules/database/src/ioc/dbStatic/drvSup.h
index 5778038e7..193d57482 100644
--- a/modules/database/src/ioc/dbStatic/drvSup.h
+++ b/modules/database/src/ioc/dbStatic/drvSup.h
@@ -6,7 +6,10 @@
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
-/* drvSup.h Driver Support */
+/** @file drvSup.h
+ *
+ * @brief Driver support routines.
+ */
/*
* Author: Marty Kraimer
@@ -18,16 +21,38 @@
#include "errMdef.h"
-typedef long (*DRVSUPFUN) (); /* ptr to driver support function*/
+/** Driver entry table */
+typedef struct typed_drvet {
+ /** Number of function pointers which follow. Must be >=2 */
+ long number;
+ /** Called from dbior() */
+ long (*report)(int lvl);
+ /** Called during iocInit() */
+ long (*init)(void);
+ /* Any further functions are driver-specific */
+} typed_drvet;
+
+#ifdef USE_TYPED_DRVET
+
+typedef typed_drvet drvet;
+
+#else
+
+/* These interfaces may eventually get deprecated */
+
+typedef long (*DRVSUPFUN) (); /* ptr to driver support function */
+
+typedef struct drvet { /* driver entry table */
+ long number; /* number of support routines */
+ DRVSUPFUN report; /* print report */
+ DRVSUPFUN init; /* init support */
+ /* Any further functions are driver-specific */
+} drvet;
-typedef struct drvet { /* driver entry table */
- long number; /*number of support routines*/
- DRVSUPFUN report; /*print report*/
- DRVSUPFUN init; /*init support*/
- /*other functions are device dependent*/
-}drvet;
#define DRVETNUMBER ( (sizeof(struct drvet) -sizeof(long))/sizeof(DRVSUPFUN) )
+#endif /* USE_TYPED_DRVET */
+
#define S_drv_noDrvSup (M_drvSup| 1) /*SDR_DRVSUP: Driver support missing*/
#define S_drv_noDrvet (M_drvSup| 3) /*Missing driver support entry table*/
diff --git a/modules/database/src/ioc/dbtemplate/msi.c b/modules/database/src/ioc/dbtemplate/msi.c
index 5a5023163..69680e17e 100644
--- a/modules/database/src/ioc/dbtemplate/msi.c
+++ b/modules/database/src/ioc/dbtemplate/msi.c
@@ -4,7 +4,7 @@
* Copyright (c) 2002 The Regents of the University of California, as
* Operator of Los Alamos National Laboratory.
* EPICS Base is distributed subject to a Software License Agreement found
-* in the file LICENSE that is included with this distribution.
+* in the file LICENSE that is included with this distribution.
\*************************************************************************/
/* msi - macro substitutions and include */
@@ -22,10 +22,36 @@
#include
#include
#include
+#include
#define MAX_BUFFER_SIZE 4096
#define MAX_DEPS 1024
+#if 0
+/* Debug Tracing */
+int din = 0;
+#define ENTER fprintf(stderr, "%*sEntering %s\n", 2*din++, "", __FUNCTION__)
+
+#define STEP(s) fprintf(stderr, "%*s%s: %s\n", 2*din, "", __FUNCTION__, s)
+#define STEPS(s, v) fprintf(stderr, "%*s%s: %s '%s'\n", 2*din, "", __FUNCTION__, s, v)
+#define STEPD(s, v) fprintf(stderr, "%*s%s: %s %d\n", 2*din, "", __FUNCTION__, s, v)
+
+#define EXIT fprintf(stderr, "%*s%s: Returning\n", 2*din--, "", __FUNCTION__)
+#define EXITD(r) fprintf(stderr, "%*s%s: Returning %d\n", 2*din--, "", __FUNCTION__, r)
+#define EXITS(r) fprintf(stderr, "%*s%s: Returning '%s'\n", 2*din--, "", __FUNCTION__, r)
+#else
+#define ENTER
+
+#define STEP(s)
+#define STEPS(s, v)
+#define STEPD(s, v)
+
+#define EXIT
+#define EXITD(r)
+#define EXITS(r)
+#endif
+
+
/* Module to read the template files */
typedef struct inputData inputData;
@@ -49,6 +75,7 @@ static char *substituteGetGlobalReplacements(subInfo *pvt);
/* Forward references to local routines */
static void usageExit(int status);
+static void abortExit(int status);
static void addMacroReplacements(MAC_HANDLE *macPvt, char *pval);
static void makeSubstitutions(inputData *inputPvt, MAC_HANDLE *macPvt, char *templateName);
@@ -66,48 +93,62 @@ int main(int argc,char **argv)
MAC_HANDLE *macPvt;
char *pval;
int narg;
- char *substitutionName=0;
- char *templateName=0;
+ char *substitutionName = 0;
+ char *templateName = 0;
int i;
int localScope = 1;
inputConstruct(&inputPvt);
- macCreateHandle(&macPvt,0);
- while((argc>1) && (argv[1][0] == '-')) {
- narg = (strlen(argv[1])==2) ? 2 : 1;
- pval = (narg==1) ? (argv[1]+2) : argv[2];
- if(strncmp(argv[1],"-I",2)==0) {
- inputAddPath(inputPvt,pval);
- } else if (strcmp(argv[1], "-D") == 0) {
+ macCreateHandle(&macPvt, 0);
+ while ((argc > 1) && (argv[1][0] == '-')) {
+ narg = (strlen(argv[1]) == 2) ? 2 : 1;
+ pval = (narg == 1) ? (argv[1] + 2) : argv[2];
+
+ if (strncmp(argv[1], "-I", 2) == 0) {
+ inputAddPath(inputPvt, pval);
+ }
+ else if (strcmp(argv[1], "-D") == 0) {
opt_D = 1;
narg = 1; /* no argument for this option */
- } else if(strncmp(argv[1],"-o",2)==0) {
+ }
+ else if(strncmp(argv[1], "-o", 2) == 0) {
outFile = epicsStrDup(pval);
- } else if(strncmp(argv[1],"-M",2)==0) {
- addMacroReplacements(macPvt,pval);
- } else if(strncmp(argv[1],"-S",2)==0) {
+ }
+ else if(strncmp(argv[1], "-M", 2) == 0) {
+ addMacroReplacements(macPvt, pval);
+ }
+ else if(strncmp(argv[1], "-S", 2) == 0) {
substitutionName = epicsStrDup(pval);
- } else if (strcmp(argv[1], "-V") == 0) {
+ }
+ else if (strcmp(argv[1], "-V") == 0) {
opt_V = 1;
narg = 1; /* no argument for this option */
- } else if (strcmp(argv[1], "-g") == 0) {
+ }
+ else if (strcmp(argv[1], "-g") == 0) {
localScope = 0;
narg = 1; /* no argument for this option */
- } else if (strcmp(argv[1], "-h") == 0) {
+ }
+ else if (strcmp(argv[1], "-h") == 0) {
usageExit(0);
- } else {
+ }
+ else {
fprintf(stderr, "msi: Bad argument \"%s\"\n", argv[1]);
usageExit(1);
}
+
argc -= narg;
- for(i=1; i2) {
- fprintf(stderr,"msi: Too many arguments\n");
+ macSuppressWarning(macPvt, 1);
+
+ if (argc > 2) {
+ fprintf(stderr, "msi: Too many arguments\n");
usageExit(1);
}
+
if (opt_D) {
if (!outFile) {
fprintf(stderr, "msi: Option -D requires -o for Makefile target\n");
@@ -120,34 +161,47 @@ int main(int argc,char **argv)
outFile, strerror(errno));
exit(1);
}
- if(argc==2) {
+
+ if (argc == 2)
templateName = epicsStrDup(argv[1]);
+
+ if (!substitutionName) {
+ STEP("Single template+substitutions file");
+ makeSubstitutions(inputPvt, macPvt, templateName);
}
- if(!substitutionName) {
- makeSubstitutions(inputPvt,macPvt,templateName);
- } else {
+ else {
subInfo *substitutePvt;
char *filename = 0;
int isGlobal, isFile;
- substituteOpen(&substitutePvt,substitutionName);
+ STEPS("Substitutions from file", substitutionName);
+ substituteOpen(&substitutePvt, substitutionName);
do {
- if ((isGlobal = substituteGetGlobalSet(substitutePvt))) {
+ isGlobal = substituteGetGlobalSet(substitutePvt);
+ if (isGlobal) {
+ STEP("Handling global macros");
pval = substituteGetGlobalReplacements(substitutePvt);
- if(pval) {
- addMacroReplacements(macPvt,pval);
- }
- } else if ((isFile = substituteGetNextSet(substitutePvt,&filename))) {
- if(templateName) filename = templateName;
- if(!filename) {
- fprintf(stderr,"msi: No template file\n");
+ if (pval)
+ addMacroReplacements(macPvt, pval);
+ }
+ else if ((isFile = substituteGetNextSet(substitutePvt, &filename))) {
+ if (templateName)
+ filename = templateName;
+ if (!filename) {
+ fprintf(stderr, "msi: No template file\n");
usageExit(1);
}
- while((pval = substituteGetReplacements(substitutePvt))){
- if (localScope) macPushScope(macPvt);
- addMacroReplacements(macPvt,pval);
- makeSubstitutions(inputPvt,macPvt,filename);
- if (localScope) macPopScope(macPvt);
+
+ STEPS("Handling template file", filename);
+ while ((pval = substituteGetReplacements(substitutePvt))) {
+ if (localScope)
+ macPushScope(macPvt);
+
+ addMacroReplacements(macPvt, pval);
+ makeSubstitutions(inputPvt, macPvt, filename);
+
+ if (localScope)
+ macPopScope(macPvt);
}
}
} while (isGlobal || isFile);
@@ -182,20 +236,29 @@ void usageExit(int status)
exit(status);
}
-static void addMacroReplacements(MAC_HANDLE *macPvt,char *pval)
+void abortExit(int status)
+{
+ if (outFile) {
+ fclose(stdout);
+ unlink(outFile);
+ }
+ exit(status);
+}
+
+static void addMacroReplacements(MAC_HANDLE *macPvt, char *pval)
{
char **pairs;
long status;
- status = macParseDefns(macPvt,pval,&pairs);
- if(status==-1) {
- fprintf(stderr,"msi: Error from macParseDefns\n");
+ status = macParseDefns(macPvt, pval, &pairs);
+ if (status == -1) {
+ fprintf(stderr, "msi: Error from macParseDefns\n");
usageExit(1);
}
- if(status) {
- status = macInstallMacros(macPvt,pairs);
- if(!status) {
- fprintf(stderr,"Error from macInstallMacros\n");
+ if (status) {
+ status = macInstallMacros(macPvt, pairs);
+ if (!status) {
+ fprintf(stderr, "Error from macInstallMacros\n");
usageExit(1);
}
free(pairs);
@@ -211,86 +274,98 @@ static void makeSubstitutions(inputData *inputPvt, MAC_HANDLE *macPvt, char *tem
static char buffer[MAX_BUFFER_SIZE];
int n;
- inputBegin(inputPvt,templateName);
- while((input = inputNextLine(inputPvt))) {
+ ENTER;
+ inputBegin(inputPvt, templateName);
+ while ((input = inputNextLine(inputPvt))) {
int expand=1;
char *p;
char *command = 0;
- p = input;
+ p = input;
/*skip whitespace at beginning of line*/
- while(*p && (isspace((int) *p))) ++p;
+ while (*p && (isspace((int) *p))) ++p;
+
/*Look for i or s */
- if(*p && (*p=='i' || *p=='s')) command = p;
- if(command) {
+ if (*p && (*p=='i' || *p=='s'))
+ command = p;
+
+ if (command) {
char *pstart;
char *pend;
char *copy;
int cmdind=-1;
int i;
-
- for(i=0; i< NELEMENTS(cmdNames); i++) {
- if(strstr(command,cmdNames[i])) {
+
+ for (i = 0; i < NELEMENTS(cmdNames); i++) {
+ if (strstr(command, cmdNames[i])) {
cmdind = i;
}
}
- if(cmdind<0) goto endif;
+ if (cmdind < 0) goto endcmd;
p = command + strlen(cmdNames[cmdind]);
/*skip whitespace after command*/
- while(*p && (isspace((int) *p))) ++p;
+ while (*p && (isspace((int) *p))) ++p;
/*Next character must be quote*/
- if((*p==0) || (*p!='"')) goto endif;
+ if ((*p == 0) || (*p != '"')) goto endcmd;
pstart = ++p;
/*Look for end quote*/
- while(*p && (*p!='"')) {
- /*allow escape for imbeded quote*/
- if((*p=='\\') && *(p+1)=='"') {
- p += 2; continue;
- } else {
- if(*p=='"') break;
+ while (*p && (*p != '"')) {
+ /*allow escape for embeded quote*/
+ if ((p[0] == '\\') && p[1] == '"') {
+ p += 2;
+ continue;
+ }
+ else {
+ if (*p == '"') break;
}
++p;
}
pend = p;
- if(*p==0) goto endif;
+ if (*p == 0) goto endcmd;
/*skip quote and any trailing blanks*/
- while(*++p==' ') ;
- if(*p != '\n' && *p !=0) goto endif;
- copy = calloc(pend-pstart+1,sizeof(char));
- strncpy(copy,pstart,pend-pstart);
+ while (*++p == ' ') ;
+ if (*p != '\n' && *p != 0) goto endcmd;
+ copy = calloc(pend-pstart + 1, sizeof(char));
+ strncpy(copy, pstart, pend-pstart);
+
switch(cmdind) {
case cmdInclude:
inputNewIncludeFile(inputPvt,copy);
break;
+
case cmdSubstitute:
addMacroReplacements(macPvt,copy);
break;
+
default:
- fprintf(stderr,"msi: Logic error in makeSubstitutions\n");
+ fprintf(stderr, "msi: Logic error in makeSubstitutions\n");
inputErrPrint(inputPvt);
- exit(1);
+ abortExit(1);
}
free(copy);
expand = 0;
}
-endif:
+
+endcmd:
if (expand && !opt_D) {
- n = macExpandString(macPvt,input,buffer,MAX_BUFFER_SIZE-1);
- fputs(buffer,stdout);
+ STEP("Expanding to output stream");
+ n = macExpandString(macPvt, input, buffer, MAX_BUFFER_SIZE - 1);
+ fputs(buffer, stdout);
if (opt_V == 1 && n < 0) {
- fprintf(stderr,"msi: Error - undefined macros present\n");
+ fprintf(stderr, "msi: Error - undefined macros present\n");
opt_V++;
}
}
}
+ EXIT;
}
-typedef struct inputFile{
+typedef struct inputFile {
ELLNODE node;
char *filename;
FILE *fp;
int lineNum;
-}inputFile;
+} inputFile;
typedef struct pathNode {
ELLNODE node;
@@ -303,15 +378,15 @@ struct inputData {
char inputBuffer[MAX_BUFFER_SIZE];
};
-static void inputOpenFile(inputData *pinputData,char *filename);
+static void inputOpenFile(inputData *pinputData, char *filename);
static void inputCloseFile(inputData *pinputData);
static void inputCloseAllFiles(inputData *pinputData);
static void inputConstruct(inputData **ppvt)
{
- inputData *pinputData;
+ inputData *pinputData;
- pinputData = calloc(1,sizeof(inputData));
+ pinputData = calloc(1, sizeof(inputData));
ellInit(&pinputData->inputFileList);
ellInit(&pinputData->pathList);
*ppvt = pinputData;
@@ -319,11 +394,11 @@ static void inputConstruct(inputData **ppvt)
static void inputDestruct(inputData *pinputData)
{
- pathNode *ppathNode;
+ pathNode *ppathNode;
inputCloseAllFiles(pinputData);
- while((ppathNode = (pathNode *)ellFirst(&pinputData->pathList))) {
- ellDelete(&pinputData->pathList,&ppathNode->node);
+ while ((ppathNode = (pathNode *) ellFirst(&pinputData->pathList))) {
+ ellDelete(&pinputData->pathList, &ppathNode->node);
free(ppathNode->directory);
free(ppathNode);
}
@@ -340,38 +415,45 @@ static void inputAddPath(inputData *pinputData, char *path)
int emptyName;
const char sep = *OSI_PATH_LIST_SEPARATOR;
+ ENTER;
pdir = path;
/*an empty name at beginning, middle, or end means current directory*/
- while(pdir && *pdir) {
+ while (pdir && *pdir) {
emptyName = ((*pdir == sep) ? 1 : 0);
- if(emptyName) ++pdir;
- ppathNode = (pathNode *)calloc(1,sizeof(pathNode));
- ellAdd(ppathList,&ppathNode->node);
- if(!emptyName) {
- pcolon = strchr(pdir,sep);
+ if (emptyName) ++pdir;
+
+ ppathNode = (pathNode *) calloc(1, sizeof(pathNode));
+ ellAdd(ppathList, &ppathNode->node);
+
+ if (!emptyName) {
+ pcolon = strchr(pdir, sep);
len = (pcolon ? (pcolon - pdir) : strlen(pdir));
- if(len>0) {
- ppathNode->directory = (char *)calloc(len+1,sizeof(char));
- strncpy(ppathNode->directory,pdir,len);
+ if (len > 0) {
+ ppathNode->directory = (char *) calloc(len + 1, sizeof(char));
+ strncpy(ppathNode->directory, pdir, len);
pdir = pcolon;
/*unless at end skip past first colon*/
- if(pdir && *(pdir+1)!=0) ++pdir;
- } else { /*must have been trailing : */
- emptyName=1;
+ if (pdir && *(pdir + 1) != 0) ++pdir;
+ }
+ else { /*must have been trailing : */
+ emptyName = 1;
}
}
- if(emptyName) {
- ppathNode->directory = (char *)calloc(2,sizeof(char));
- strcpy(ppathNode->directory,".");
+
+ if (emptyName) {
+ ppathNode->directory = (char *) calloc(2, sizeof(char));
+ strcpy(ppathNode->directory, ".");
}
}
- return;
+ EXIT;
}
static void inputBegin(inputData *pinputData, char *fileName)
{
+ ENTER;
inputCloseAllFiles(pinputData);
- inputOpenFile(pinputData,fileName);
+ inputOpenFile(pinputData, fileName);
+ EXIT;
}
static char *inputNextLine(inputData *pinputData)
@@ -379,43 +461,54 @@ static char *inputNextLine(inputData *pinputData)
inputFile *pinputFile;
char *pline;
- while((pinputFile = (inputFile *)ellFirst(&pinputData->inputFileList))) {
- pline = fgets(pinputData->inputBuffer,MAX_BUFFER_SIZE,pinputFile->fp);
- if(pline) {
+ ENTER;
+ while ((pinputFile = (inputFile *) ellFirst(&pinputData->inputFileList))) {
+ pline = fgets(pinputData->inputBuffer, MAX_BUFFER_SIZE, pinputFile->fp);
+ if (pline) {
++pinputFile->lineNum;
- return(pline);
+ EXITS(pline);
+ return pline;
}
inputCloseFile(pinputData);
}
- return(0);
+ EXITD(0);
+ return 0;
}
static void inputNewIncludeFile(inputData *pinputData, char *name)
{
+ ENTER;
inputOpenFile(pinputData,name);
+ EXIT;
}
static void inputErrPrint(inputData *pinputData)
{
inputFile *pinputFile;
- fprintf(stderr,"input: '%s' at ",pinputData->inputBuffer);
- pinputFile = (inputFile *)ellFirst(&pinputData->inputFileList);
- while(pinputFile) {
- fprintf(stderr,"line %d of ",pinputFile->lineNum);
- if(pinputFile->filename) {
- fprintf(stderr," file %s\n",pinputFile->filename);
- } else {
- fprintf(stderr,"stdin:\n");
+ ENTER;
+ fprintf(stderr, "input: '%s' at ", pinputData->inputBuffer);
+ pinputFile = (inputFile *) ellFirst(&pinputData->inputFileList);
+ while (pinputFile) {
+ fprintf(stderr, "line %d of ", pinputFile->lineNum);
+
+ if (pinputFile->filename) {
+ fprintf(stderr, " file %s\n", pinputFile->filename);
}
- pinputFile = (inputFile *)ellNext(&pinputFile->node);
- if(pinputFile) {
- fprintf(stderr," included from ");
- } else {
+ else {
+ fprintf(stderr, "stdin:\n");
+ }
+
+ pinputFile = (inputFile *) ellNext(&pinputFile->node);
+ if (pinputFile) {
+ fprintf(stderr, " included from ");
+ }
+ else {
fprintf(stderr,"\n");
}
}
fprintf(stderr,"\n");
+ EXIT;
}
static void inputOpenFile(inputData *pinputData,char *filename)
@@ -426,35 +519,48 @@ static void inputOpenFile(inputData *pinputData,char *filename)
char *fullname = 0;
FILE *fp = 0;
- if(!filename) {
+ ENTER;
+ if (!filename) {
+ STEP("Using stdin");
fp = stdin;
- } else if((ellCount(ppathList)==0) || strchr(filename,'/')){
- fp = fopen(filename,"r");
- } else {
- ppathNode = (pathNode *)ellFirst(ppathList);
- while(ppathNode) {
- fullname = calloc(strlen(filename)+strlen(ppathNode->directory) +2,
+ }
+ else if ((ellCount(ppathList) == 0) || strchr(filename, '/')){
+ STEPS("Opening ", filename);
+ fp = fopen(filename, "r");
+ }
+ else {
+ ppathNode = (pathNode *) ellFirst(ppathList);
+ while (ppathNode) {
+ fullname = calloc(strlen(filename) + strlen(ppathNode->directory) + 2,
sizeof(char));
- strcpy(fullname,ppathNode->directory);
- strcat(fullname,"/");
- strcat(fullname,filename);
- fp = fopen(fullname,"r");
- if(fp) break;
+ strcpy(fullname, ppathNode->directory);
+ strcat(fullname, "/");
+ strcat(fullname, filename);
+ STEPS("Trying", filename);
+ fp = fopen(fullname, "r");
+ if (fp)
+ break;
free(fullname);
- ppathNode = (pathNode *)ellNext(&ppathNode->node);
+ ppathNode = (pathNode *) ellNext(&ppathNode->node);
}
}
- if(!fp) {
- fprintf(stderr,"msi: Can't open file '%s'\n",filename);
+
+ if (!fp) {
+ fprintf(stderr, "msi: Can't open file '%s'\n", filename);
inputErrPrint(pinputData);
- exit(1);
+ abortExit(1);
}
- pinputFile = calloc(1,sizeof(inputFile));
- if(ppathNode) {
+
+ STEP("File opened");
+ pinputFile = calloc(1, sizeof(inputFile));
+
+ if (ppathNode) {
pinputFile->filename = fullname;
- } else if(filename) {
+ }
+ else if (filename) {
pinputFile->filename = epicsStrDup(filename);
- } else {
+ }
+ else {
pinputFile->filename = epicsStrDup("stdin");
}
@@ -484,35 +590,41 @@ static void inputOpenFile(inputData *pinputData,char *filename)
}
pinputFile->fp = fp;
- ellInsert(&pinputData->inputFileList,0,&pinputFile->node);
+ ellInsert(&pinputData->inputFileList, 0, &pinputFile->node);
+ EXIT;
}
static void inputCloseFile(inputData *pinputData)
{
inputFile *pinputFile;
- pinputFile = (inputFile *)ellFirst(&pinputData->inputFileList);
- if(!pinputFile) return;
- ellDelete(&pinputData->inputFileList,&pinputFile->node);
- if(fclose(pinputFile->fp))
- fprintf(stderr,"msi: Can't close input file '%s'\n",pinputFile->filename);
- free(pinputFile->filename);
- free(pinputFile);
+ ENTER;
+ pinputFile = (inputFile *) ellFirst(&pinputData->inputFileList);
+ if (pinputFile) {
+ ellDelete(&pinputData->inputFileList, &pinputFile->node);
+ if (fclose(pinputFile->fp))
+ fprintf(stderr, "msi: Can't close input file '%s'\n", pinputFile->filename);
+ free(pinputFile->filename);
+ free(pinputFile);
+ }
+ EXIT;
}
static void inputCloseAllFiles(inputData *pinputData)
{
inputFile *pinputFile;
- while((pinputFile=(inputFile *)ellFirst(&pinputData->inputFileList))){
+ ENTER;
+ while ((pinputFile = (inputFile *) ellFirst(&pinputData->inputFileList))) {
inputCloseFile(pinputData);
}
+ EXIT;
}
/*start of code that handles substitution file*/
typedef enum {
- tokenLBrace,tokenRBrace,tokenSeparater,tokenString,tokenEOF
-}tokenType;
+ tokenLBrace, tokenRBrace, tokenSeparator, tokenString, tokenEOF
+} tokenType;
typedef struct subFile {
char *substitutionName;
@@ -549,70 +661,87 @@ static void catMacroReplacements(subInfo *psubInfo,const char *value);
void freeSubFile(subInfo *psubInfo)
{
- subFile *psubFile = psubInfo->psubFile;
- if(psubFile->fp) {
- if(fclose(psubFile->fp))
- fprintf(stderr,"msi: Can't close substitution file\n");
+ subFile *psubFile = psubInfo->psubFile;
+
+ ENTER;
+ if (psubFile->fp) {
+ if (fclose(psubFile->fp))
+ fprintf(stderr, "msi: Can't close substitution file\n");
}
free(psubFile);
free(psubInfo->filename);
psubInfo->psubFile = 0;
+ EXIT;
}
void freePattern(subInfo *psubInfo)
{
patternNode *ppatternNode;
- while((ppatternNode = (patternNode *)ellFirst(&psubInfo->patternList))) {
- ellDelete(&psubInfo->patternList,&ppatternNode->node);
+
+ ENTER;
+ while ((ppatternNode = (patternNode *) ellFirst(&psubInfo->patternList))) {
+ ellDelete(&psubInfo->patternList, &ppatternNode->node);
free(ppatternNode->var);
free(ppatternNode);
}
psubInfo->isPattern = 0;
+ EXIT;
}
static void substituteDestruct(subInfo *psubInfo)
{
+ ENTER;
freeSubFile(psubInfo);
freePattern(psubInfo);
free(psubInfo);
- return;
+ EXIT;
}
-static void substituteOpen(subInfo **ppvt,char *substitutionName)
+static void substituteOpen(subInfo **ppvt, char *substitutionName)
{
subInfo *psubInfo;
subFile *psubFile;
FILE *fp;
- psubInfo = calloc(1,sizeof(subInfo));
+ ENTER;
+ psubInfo = calloc(1, sizeof(subInfo));
*ppvt = psubInfo;
- psubFile = calloc(1,sizeof(subFile));
+ psubFile = calloc(1, sizeof(subFile));
psubInfo->psubFile = psubFile;
ellInit(&psubInfo->patternList);
- fp = fopen(substitutionName,"r");
- if(!fp) {
- fprintf(stderr,"msi: Can't open file '%s'\n",substitutionName);
- exit(1);
+
+ fp = fopen(substitutionName, "r");
+ if (!fp) {
+ fprintf(stderr, "msi: Can't open file '%s'\n", substitutionName);
+ abortExit(1);
}
+
psubFile->substitutionName = substitutionName;
psubFile->fp = fp;
psubFile->lineNum = 1;
psubFile->inputBuffer[0] = 0;
psubFile->pnextChar = &psubFile->inputBuffer[0];
subGetNextToken(psubFile);
- return;
+ EXIT;
}
static int substituteGetGlobalSet(subInfo *psubInfo)
{
- subFile *psubFile = psubInfo->psubFile;
+ subFile *psubFile = psubInfo->psubFile;
- while(psubFile->token==tokenSeparater) subGetNextToken(psubFile);
- if(psubFile->token==tokenString && strcmp(psubFile->string,"global")==0) {
+ ENTER;
+ while (psubFile->token == tokenSeparator)
subGetNextToken(psubFile);
- return(1);
+
+ if (psubFile->token == tokenString &&
+ strcmp(psubFile->string, "global") == 0) {
+ subGetNextToken(psubFile);
+ EXITD(1);
+ return 1;
}
- return(0);
+
+ EXITD(0);
+ return 0;
}
static int substituteGetNextSet(subInfo *psubInfo,char **filename)
@@ -620,96 +749,155 @@ static int substituteGetNextSet(subInfo *psubInfo,char **filename)
subFile *psubFile = psubInfo->psubFile;
patternNode *ppatternNode;
+ ENTER;
*filename = 0;
- while(psubFile->token==tokenSeparater) subGetNextToken(psubFile);
- if(psubFile->token==tokenEOF) return(0);
- if(psubFile->token==tokenString && strcmp(psubFile->string,"file")==0) {
+ while (psubFile->token == tokenSeparator)
+ subGetNextToken(psubFile);
+
+ if (psubFile->token == tokenEOF) {
+ EXITD(0);
+ return 0;
+ }
+
+ if (psubFile->token == tokenString &&
+ strcmp(psubFile->string, "file") == 0) {
+ size_t len;
+
+ STEP("Parsed 'file'");
psubInfo->isFile = 1;
- if(subGetNextToken(psubFile)!=tokenString) {
- subFileErrPrint(psubFile,"Parse error, expecting filename");
- exit(1);
+ if (subGetNextToken(psubFile) != tokenString) {
+ subFileErrPrint(psubFile, "Parse error, expecting a filename");
+ abortExit(1);
}
+
freePattern(psubInfo);
free(psubInfo->filename);
- if(psubFile->string[0]=='"'&&psubFile->string[strlen(psubFile->string)-1]=='"') {
- psubFile->string[strlen(psubFile->string)-1]='\0';
- psubInfo->filename = macEnvExpand(psubFile->string+1);
+
+ len = strlen(psubFile->string);
+ if (psubFile->string[0] == '"' &&
+ psubFile->string[len - 1] == '"') {
+ psubFile->string[len - 1] = '\0';
+ psubInfo->filename = macEnvExpand(psubFile->string + 1);
}
- else {
+ else
psubInfo->filename = macEnvExpand(psubFile->string);
+ STEPS("Parsed filename", psubInfo->filename);
+
+ while (subGetNextToken(psubFile) == tokenSeparator);
+
+ if (psubFile->token != tokenLBrace) {
+ subFileErrPrint(psubFile, "Parse error, expecting '{'");
+ abortExit(1);
}
- while(subGetNextToken(psubFile)==tokenSeparater);
- if(psubFile->token!=tokenLBrace) {
- subFileErrPrint(psubFile,"Parse error, expecting {");
- exit(1);
- }
+ STEP("Parsed '{'");
subGetNextToken(psubFile);
}
*filename = psubInfo->filename;
- while(psubFile->token==tokenSeparater) subGetNextToken(psubFile);
- if(psubFile->token==tokenLBrace) return(1);
- if(psubFile->token==tokenRBrace) return(1);
- if(psubFile->token!=tokenString
- || strcmp(psubFile->string,"pattern")!=0) {
- subFileErrPrint(psubFile,"Parse error, expecting pattern");
- exit(1);
+
+ while (psubFile->token == tokenSeparator)
+ subGetNextToken(psubFile);
+
+ if (psubFile->token == tokenLBrace) {
+ EXITD(1);
+ return 1;
}
+
+ if (psubFile->token == tokenRBrace) {
+ subFileErrPrint(psubFile, "Parse error, unexpected '}'");
+ abortExit(1);
+ }
+
+ if (psubFile->token != tokenString ||
+ strcmp(psubFile->string, "pattern") != 0) {
+ subFileErrPrint(psubFile, "Parse error, expecting 'pattern'");
+ abortExit(1);
+ }
+
+ STEP("Parsed 'pattern'");
freePattern(psubInfo);
psubInfo->isPattern = 1;
- while(subGetNextToken(psubFile)==tokenSeparater);
- if(psubFile->token!=tokenLBrace) {
- subFileErrPrint(psubFile,"Parse error, expecting {");
- exit(1);
+
+ while (subGetNextToken(psubFile) == tokenSeparator);
+
+ if (psubFile->token != tokenLBrace) {
+ subFileErrPrint(psubFile, "Parse error, expecting '{'");
+ abortExit(1);
}
- while(1) {
- while(subGetNextToken(psubFile)==tokenSeparater);
- if(psubFile->token!=tokenString) break;
- ppatternNode = calloc(1,sizeof(patternNode));
- ellAdd(&psubInfo->patternList,&ppatternNode->node);
+ STEP("Parsed '{'");
+
+ while (1) {
+ while (subGetNextToken(psubFile) == tokenSeparator);
+
+ if (psubFile->token != tokenString)
+ break;
+
+ ppatternNode = calloc(1, sizeof(patternNode));
+ ellAdd(&psubInfo->patternList, &ppatternNode->node);
ppatternNode->var = epicsStrDup(psubFile->string);
}
- if(psubFile->token!=tokenRBrace) {
- subFileErrPrint(psubFile,"Parse error, expecting }");
- exit(1);
+
+ if (psubFile->token != tokenRBrace) {
+ subFileErrPrint(psubFile, "Parse error, expecting '}'");
+ abortExit(1);
}
+
subGetNextToken(psubFile);
- return(1);
+ EXITD(1);
+ return 1;
}
static char *substituteGetGlobalReplacements(subInfo *psubInfo)
{
subFile *psubFile = psubInfo->psubFile;
- if(psubInfo->macroReplacements) psubInfo->macroReplacements[0] = 0;
+ ENTER;
+ if (psubInfo->macroReplacements)
+ psubInfo->macroReplacements[0] = 0;
psubInfo->curLength = 0;
- while(psubFile->token==tokenSeparater) subGetNextToken(psubFile);
- if(psubFile->token==tokenRBrace && psubInfo->isFile) {
+
+ while (psubFile->token == tokenSeparator)
+ subGetNextToken(psubFile);
+
+ if (psubFile->token == tokenRBrace && psubInfo->isFile) {
psubInfo->isFile = 0;
free(psubInfo->filename);
psubInfo->filename = 0;
freePattern(psubInfo);
subGetNextToken(psubFile);
- return(0);
+ EXITD(0);
+ return 0;
}
- if(psubFile->token==tokenEOF) return(0);
- if(psubFile->token!=tokenLBrace) return(0);
- while(1) {
+
+ if (psubFile->token == tokenEOF) {
+ EXITD(0);
+ return 0;
+ }
+ if (psubFile->token != tokenLBrace) {
+ EXITD(0);
+ return 0;
+ }
+
+ while (1) {
switch(subGetNextToken(psubFile)) {
case tokenRBrace:
subGetNextToken(psubFile);
- if (!psubInfo->macroReplacements) {
- catMacroReplacements(psubInfo,"");
- }
- return(psubInfo->macroReplacements);
- case tokenSeparater:
- catMacroReplacements(psubInfo,",");
+ EXITS(psubInfo->macroReplacements);
+ return psubInfo->macroReplacements;
+
+ case tokenSeparator:
+ catMacroReplacements(psubInfo, ",");
break;
+
case tokenString:
- catMacroReplacements(psubInfo,psubFile->string);
+ catMacroReplacements(psubInfo, psubFile->string);
break;
- default:
- subFileErrPrint(psubFile,"Parse error, illegal token");
- exit(1);
+
+ case tokenLBrace:
+ subFileErrPrint(psubFile, "Parse error, unexpected '{'");
+ abortExit(1);
+ case tokenEOF:
+ subFileErrPrint(psubFile, "Parse error, incomplete file?");
+ abortExit(1);
}
}
}
@@ -719,62 +907,89 @@ static char *substituteGetReplacements(subInfo *psubInfo)
subFile *psubFile = psubInfo->psubFile;
patternNode *ppatternNode;
- if(psubInfo->macroReplacements) psubInfo->macroReplacements[0] = 0;
+ ENTER;
+ if (psubInfo->macroReplacements)
+ psubInfo->macroReplacements[0] = 0;
psubInfo->curLength = 0;
- while(psubFile->token==tokenSeparater) subGetNextToken(psubFile);
- if(psubFile->token==tokenRBrace && psubInfo->isFile) {
+
+ while (psubFile->token == tokenSeparator)
+ subGetNextToken(psubFile);
+
+ if (psubFile->token==tokenRBrace && psubInfo->isFile) {
psubInfo->isFile = 0;
free(psubInfo->filename);
psubInfo->filename = 0;
freePattern(psubInfo);
subGetNextToken(psubFile);
- return(0);
+ EXITD(0);
+ return 0;
}
- if(psubFile->token==tokenEOF) return(0);
- if(psubFile->token!=tokenLBrace) return(0);
- if(psubInfo->isPattern) {
+
+ if (psubFile->token == tokenEOF) {
+ EXITD(0);
+ return 0;
+ }
+
+ if (psubFile->token != tokenLBrace) {
+ EXITD(0);
+ return 0;
+ }
+
+ if (psubInfo->isPattern) {
int gotFirstPattern = 0;
- while(subGetNextToken(psubFile)==tokenSeparater);
- ppatternNode = (patternNode *)ellFirst(&psubInfo->patternList);
- while(1) {
- if(psubFile->token==tokenRBrace) {
+ while (subGetNextToken(psubFile) == tokenSeparator);
+ ppatternNode = (patternNode *) ellFirst(&psubInfo->patternList);
+ while (1) {
+ if (psubFile->token == tokenRBrace) {
subGetNextToken(psubFile);
- return(psubInfo->macroReplacements);
+ EXITS(psubInfo->macroReplacements);
+ return psubInfo->macroReplacements;
}
- if(psubFile->token!=tokenString) {
- subFileErrPrint(psubFile,"Parse error, illegal token");
- exit(-1);
+
+ if (psubFile->token != tokenString) {
+ subFileErrPrint(psubFile,"Parse error, expecting macro value");
+ abortExit(1);
}
- if(gotFirstPattern) catMacroReplacements(psubInfo,",");
+
+ if (gotFirstPattern)
+ catMacroReplacements(psubInfo, ",");
gotFirstPattern = 1;
- if(ppatternNode) {
- catMacroReplacements(psubInfo,ppatternNode->var);
- catMacroReplacements(psubInfo,"=");
- catMacroReplacements(psubInfo,psubFile->string);
- ppatternNode = (patternNode *)ellNext(&ppatternNode->node);
- } else {
- subFileErrPrint(psubFile,"Warning, too many values given");
+
+ if (ppatternNode) {
+ catMacroReplacements(psubInfo, ppatternNode->var);
+ catMacroReplacements(psubInfo, "=");
+ catMacroReplacements(psubInfo, psubFile->string);
+ ppatternNode = (patternNode *) ellNext(&ppatternNode->node);
}
- while(subGetNextToken(psubFile)==tokenSeparater);
+ else {
+ subFileErrPrint(psubFile, "Warning, too many values given");
+ }
+
+ while (subGetNextToken(psubFile) == tokenSeparator);
}
- } else while(1) {
+ }
+ else while(1) {
switch(subGetNextToken(psubFile)) {
case tokenRBrace:
subGetNextToken(psubFile);
- if (!psubInfo->macroReplacements) {
- catMacroReplacements(psubInfo,"");
- }
- return(psubInfo->macroReplacements);
- case tokenSeparater:
- catMacroReplacements(psubInfo,",");
+ EXITS(psubInfo->macroReplacements);
+ return psubInfo->macroReplacements;
+
+ case tokenSeparator:
+ catMacroReplacements(psubInfo, ",");
break;
+
case tokenString:
- catMacroReplacements(psubInfo,psubFile->string);
+ catMacroReplacements(psubInfo, psubFile->string);
break;
- default:
- subFileErrPrint(psubFile,"Parse error, illegal token");
- exit(1);
+
+ case tokenLBrace:
+ subFileErrPrint(psubFile, "Parse error, unexpected '{'");
+ abortExit(1);
+ case tokenEOF:
+ subFileErrPrint(psubFile, "Parse error, incomplete file?");
+ abortExit(1);
}
}
}
@@ -783,69 +998,84 @@ static char *subGetNextLine(subFile *psubFile)
{
char *pline;
+ ENTER;
do {
- pline = fgets(psubFile->inputBuffer,MAX_BUFFER_SIZE,psubFile->fp);
+ pline = fgets(psubFile->inputBuffer, MAX_BUFFER_SIZE, psubFile->fp);
++psubFile->lineNum;
- } while(pline && psubFile->inputBuffer[0]=='#');
- if(!pline) {
+ } while (pline && psubFile->inputBuffer[0] == '#');
+
+ if (!pline) {
psubFile->token = tokenEOF;
psubFile->inputBuffer[0] = 0;
psubFile->pnextChar = 0;
- return(0);
+ EXITD(0);
+ return 0;
}
+
psubFile->pnextChar = &psubFile->inputBuffer[0];
- return(&psubFile->inputBuffer[0]);
+ EXITS(&psubFile->inputBuffer[0]);
+ return &psubFile->inputBuffer[0];
}
static void subFileErrPrint(subFile *psubFile,char * message)
{
- fprintf(stderr,"msi: %s\n",message);
- fprintf(stderr," in substitution file '%s' at line %d:\n %s",
- psubFile->substitutionName,
- psubFile->lineNum,psubFile->inputBuffer);
+ fprintf(stderr, "msi: %s\n",message);
+ fprintf(stderr, " in substitution file '%s' at line %d:\n %s",
+ psubFile->substitutionName, psubFile->lineNum, psubFile->inputBuffer);
}
static tokenType subGetNextToken(subFile *psubFile)
{
- char *p;
- char *pto;
+ char *p, *pto;
+ ENTER;
p = psubFile->pnextChar;
- if(!p) { psubFile->token = tokenEOF; return(tokenEOF);}
- if(*p==0 || *p=='\n' || *p=='#') {
- p = subGetNextLine(psubFile);
- if(!p) { psubFile->token = tokenEOF; return(tokenEOF);}
- else { psubFile->token = tokenSeparater; return(tokenSeparater);}
+ if (!p) {
+ STEP("Got EOF");
+ psubFile->token = tokenEOF;
+ goto done;
}
- while(isspace((int) *p)) p++;
- if(*p=='{') {
+
+ if (*p == 0 || *p == '\n' || *p == '#') {
+ STEP("Got newline/comment");
+ p = subGetNextLine(psubFile);
+ psubFile->token = p ? tokenSeparator : tokenEOF;
+ goto done;
+ }
+
+ while (isspace((int) *p)) p++;
+ if (*p == '{') {
+ STEP("Got '{'");
psubFile->token = tokenLBrace;
psubFile->pnextChar = ++p;
- return(tokenLBrace);
+ goto done;
}
- if(*p=='}') {
+ if (*p == '}') {
+ STEP("Got '}'");
psubFile->token = tokenRBrace;
psubFile->pnextChar = ++p;
- return(tokenRBrace);
+ goto done;
}
- if(*p==0 || isspace((int) *p) || *p==',') {
- while (isspace((int) *p) || *p==',') p++;
- psubFile->token = tokenSeparater;
+ if (*p == 0 || isspace((int) *p) || *p == ',') {
+ STEP("Got space/comma");
+ while (isspace((int) *p) || *p == ',') p++;
+ psubFile->token = tokenSeparator;
psubFile->pnextChar = p;
- return(tokenSeparater);
+ goto done;
}
/*now handle quoted strings*/
- if(*p=='"') {
+ if (*p == '"') {
+ STEP("Got '\"'");
pto = &psubFile->string[0];
*pto++ = *p++;
- while(*p!='"') {
- if(*p==0 || *p=='\n') {
- subFileErrPrint(psubFile,"Strings must be on single line\n");
- exit(1);
+ while (*p != '"') {
+ if (*p == 0 || *p == '\n') {
+ subFileErrPrint(psubFile, "Strings must be on single line\n");
+ abortExit(1);
}
- /*allow escape for imbeded quote*/
- if((*p=='\\') && *(p+1)=='"') {
+ /*allow escape for embeded quote*/
+ if ((p[0] == '\\') && p[1] == '"') {
*pto++ = *p++;
*pto++ = *p++;
continue;
@@ -856,40 +1086,53 @@ static tokenType subGetNextToken(subFile *psubFile)
psubFile->pnextChar = p;
*pto = 0;
psubFile->token = tokenString;
- return(tokenString);
+ goto done;
}
+
/*Now take anything up to next non String token and not space*/
pto = &psubFile->string[0];
- while(!isspace((int) *p) && (strspn(p,"\",{}")==0)) *pto++ = *p++;
+
+ while (!isspace((int) *p) && (strspn(p, "\",{}") == 0))
+ *pto++ = *p++;
*pto = 0;
+ STEPS("Got bareword", psubFile->string);
+
psubFile->pnextChar = p;
psubFile->token = tokenString;
- return(tokenString);
+
+done:
+ EXITD(psubFile->token);
+ return psubFile->token;
}
-static void catMacroReplacements(subInfo *psubInfo,const char *value)
+static void catMacroReplacements(subInfo *psubInfo, const char *value)
{
- size_t len = strlen(value);
+ size_t len = strlen(value);
- if(psubInfo->size <= (psubInfo->curLength + len)) {
+ ENTER;
+ if (psubInfo->size <= (psubInfo->curLength + len)) {
size_t newsize = psubInfo->size + MAX_BUFFER_SIZE;
char *newbuf;
- if(newsize <= psubInfo->curLength + len)
+ STEP("Enlarging buffer");
+ if (newsize <= psubInfo->curLength + len)
newsize = psubInfo->curLength + len + 1;
- newbuf = calloc(1,newsize);
- if(!newbuf) {
- fprintf(stderr,"calloc failed for size %lu\n",
- (unsigned long) newsize);
- exit(1);
+ newbuf = calloc(1, newsize);
+ if (!newbuf) {
+ fprintf(stderr, "calloc failed for size %lu\n",
+ (unsigned long) newsize);
+ abortExit(1);
}
- if(psubInfo->macroReplacements) {
- memcpy(newbuf,psubInfo->macroReplacements,psubInfo->curLength);
+ if (psubInfo->macroReplacements) {
+ memcpy(newbuf, psubInfo->macroReplacements, psubInfo->curLength);
free(psubInfo->macroReplacements);
}
psubInfo->size = newsize;
psubInfo->macroReplacements = newbuf;
}
- strcat(psubInfo->macroReplacements,value);
+
+ STEPS("Appending", value);
+ strcat(psubInfo->macroReplacements, value);
psubInfo->curLength += len;
+ EXIT;
}
diff --git a/modules/database/src/ioc/dbtemplate/msi.html b/modules/database/src/ioc/dbtemplate/msi.html
index ff4341ead..b5929fc79 100644
--- a/modules/database/src/ioc/dbtemplate/msi.html
+++ b/modules/database/src/ioc/dbtemplate/msi.html
@@ -34,10 +34,10 @@ be written to stdout unless the -o option is given.
-V
- Verbose warnings; if this parameter is specified then any undefined
- macro discovered in the template file which does not have an associated
- default value is considered an error. An error message is generated, and
- when msi terminates it will do so with an exit status of 2.
+ Verbose warnings; if this parameter is specified then any undefined or
+ recursive macros discovered in the template will be considered an error and
+ will be marked in the output file. An error message will be shown, and when
+ msi terminates it will do so with an exit status of 2.
-g
When this flag is given all macros defined in a substitution file will
diff --git a/modules/database/src/ioc/misc/Makefile b/modules/database/src/ioc/misc/Makefile
index e97efe15a..ef70941c2 100644
--- a/modules/database/src/ioc/misc/Makefile
+++ b/modules/database/src/ioc/misc/Makefile
@@ -26,3 +26,4 @@ dbCore_SRCS += miscIocRegister.c
dbCore_SRCS += dlload.c
dbCore_SRCS += iocshRegisterCommon.c
+miscIocRegister_CFLAGS_iOS = -DSYSTEM_UNAVAILABLE
diff --git a/modules/database/src/ioc/misc/dbCore.dbd b/modules/database/src/ioc/misc/dbCore.dbd
index 921b3818e..51f9c9640 100644
--- a/modules/database/src/ioc/misc/dbCore.dbd
+++ b/modules/database/src/ioc/misc/dbCore.dbd
@@ -12,6 +12,9 @@ variable(asCaDebug,int)
# CA server debug flag (very verbose) range[0,5]
variable(CASDEBUG,int)
+# Link parsing debug
+variable(dbJLinkDebug,int)
+
# Static database access variables
variable(dbRecordsOnceOnly,int)
variable(dbRecordsAbcSorted,int)
@@ -19,8 +22,12 @@ variable(dbBptNotMonotonic,int)
variable(dbQuietMacroWarnings,int)
variable(dbConvertStrict,int)
+# PUTF/RPRO tracing; set TPRO on records to trace
+variable(dbAccessDebugPUTF,int)
+
# dbLoadTemplate settings
variable(dbTemplateMaxVars,int)
+
# Default number of parallel callback threads
variable(callbackParallelThreadsDefault,int)
diff --git a/modules/database/src/ioc/misc/miscIocRegister.c b/modules/database/src/ioc/misc/miscIocRegister.c
index 233852ed2..6c08ef0c9 100644
--- a/modules/database/src/ioc/misc/miscIocRegister.c
+++ b/modules/database/src/ioc/misc/miscIocRegister.c
@@ -66,10 +66,12 @@ void miscIocRegister(void)
/* system -- escape to system command interpreter.
*
- * Disabled by default, for security reasons. To enable this command, add
+ * Disabled by default for security reasons, not available on all OSs.
+ * To enable this command, add
* registrar(iocshSystemCommand)
- * to an application dbd file.
+ * to an application dbd file, or include system.dbd
*/
+#ifndef SYSTEM_UNAVAILABLE
static const iocshArg systemArg0 = { "command string",iocshArgString};
static const iocshArg * const systemArgs[] = {&systemArg0};
static const iocshFuncDef systemFuncDef = {"system",1,systemArgs};
@@ -77,12 +79,15 @@ static void systemCallFunc(const iocshArgBuf *args)
{
system(args[0].sval);
}
+#endif
static void iocshSystemCommand(void)
{
+#ifndef SYSTEM_UNAVAILABLE
if (system(NULL))
iocshRegister(&systemFuncDef, systemCallFunc);
else
+#endif
errlogPrintf ("Can't register 'system' command -- no command interpreter available.\n");
}
epicsExportRegistrar(iocshSystemCommand);
diff --git a/modules/database/src/ioc/rsrv/caservertask.c b/modules/database/src/ioc/rsrv/caservertask.c
index 0b7a068a5..5ef746faa 100644
--- a/modules/database/src/ioc/rsrv/caservertask.c
+++ b/modules/database/src/ioc/rsrv/caservertask.c
@@ -324,7 +324,7 @@ void rsrv_build_addr_lists(void)
#ifdef IP_MULTICAST_TTL
{
- int ttl;
+ osiSockOptMcastTTL_t ttl;
long val;
if(envGetLongConfigParam(&EPICS_CA_MCAST_TTL, &val))
val =1;
diff --git a/modules/database/src/std/dev/devAaiSoft.c b/modules/database/src/std/dev/devAaiSoft.c
index d2a9875ee..cb4aa0213 100644
--- a/modules/database/src/std/dev/devAaiSoft.c
+++ b/modules/database/src/std/dev/devAaiSoft.c
@@ -23,6 +23,7 @@
#include "dbDefs.h"
#include "dbAccess.h"
#include "dbConstLink.h"
+#include "dbEvent.h"
#include "recGbl.h"
#include "devSup.h"
#include "cantProceed.h"
@@ -96,11 +97,15 @@ static long readLocked(struct link *pinp, void *dummy)
static long read_aai(aaiRecord *prec)
{
+ epicsUInt32 nord = prec->nord;
struct link *pinp = prec->simm == menuYesNoYES ? &prec->siol : &prec->inp;
long status = dbLinkDoLocked(pinp, readLocked, NULL);
if (status == S_db_noLSET)
status = readLocked(pinp, NULL);
+ if (!status && nord != prec->nord)
+ db_post_events(prec, &prec->nord, DBE_VALUE | DBE_LOG);
+
return status;
}
diff --git a/modules/database/src/std/dev/devSASoft.c b/modules/database/src/std/dev/devSASoft.c
index 01a076e6d..69894dd89 100644
--- a/modules/database/src/std/dev/devSASoft.c
+++ b/modules/database/src/std/dev/devSASoft.c
@@ -4,7 +4,7 @@
* Copyright (c) 2002 Lawrence Berkeley Laboratory,The Control Systems
* Group, Systems Engineering Department
* EPICS BASE is distributed subject to a Software License Agreement found
-* in file LICENSE that is included with this distribution.
+* in file LICENSE that is included with this distribution.
\*************************************************************************/
/*
@@ -19,6 +19,7 @@
#include "alarm.h"
#include "dbDefs.h"
#include "dbAccess.h"
+#include "dbEvent.h"
#include "recGbl.h"
#include "devSup.h"
#include "subArrayRecord.h"
@@ -101,6 +102,7 @@ static long read_sa(subArrayRecord *prec)
{
long status;
struct sart rt;
+ epicsUInt32 nord = prec->nord;
rt.nRequest = prec->indx + prec->nelm;
if (rt.nRequest > prec->malm)
@@ -123,8 +125,12 @@ static long read_sa(subArrayRecord *prec)
status = readLocked(&prec->inp, &rt);
}
- if (!status && rt.nRequest > 0)
+ if (!status && rt.nRequest > 0) {
subset(prec, rt.nRequest);
+ if (nord != prec->nord)
+ db_post_events(prec, &prec->nord, DBE_VALUE | DBE_LOG);
+ }
+
return status;
}
diff --git a/modules/database/src/std/dev/devWfSoft.c b/modules/database/src/std/dev/devWfSoft.c
index 8d8295696..5b521518d 100644
--- a/modules/database/src/std/dev/devWfSoft.c
+++ b/modules/database/src/std/dev/devWfSoft.c
@@ -4,7 +4,7 @@
* Copyright (c) 2002 The Regents of the University of California, as
* Operator of Los Alamos National Laboratory.
* EPICS BASE is distributed subject to a Software License Agreement found
-* in file LICENSE that is included with this distribution.
+* in file LICENSE that is included with this distribution.
\*************************************************************************/
/*
@@ -81,6 +81,7 @@ static long read_wf(waveformRecord *prec)
{
long status;
struct wfrt rt;
+ epicsUInt32 nord = prec->nord;
rt.nRequest = prec->nelm;
rt.ptime = (dbLinkIsConstant(&prec->tsel) &&
@@ -93,6 +94,8 @@ static long read_wf(waveformRecord *prec)
if (!status && rt.nRequest > 0) {
prec->nord = rt.nRequest;
prec->udf = FALSE;
+ if (nord != prec->nord)
+ db_post_events(prec, &prec->nord, DBE_VALUE | DBE_LOG);
}
return status;
diff --git a/modules/database/src/std/link/Makefile b/modules/database/src/std/link/Makefile
index 31d14b825..9b6abd705 100644
--- a/modules/database/src/std/link/Makefile
+++ b/modules/database/src/std/link/Makefile
@@ -2,7 +2,7 @@
# Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne
# National Laboratory.
# EPICS BASE is distributed subject to a Software License Agreement found
-# in file LICENSE that is included with this distribution.
+# in file LICENSE that is included with this distribution.
#*************************************************************************
# This is a Makefile fragment, see src/std/Makefile.
@@ -13,6 +13,7 @@ DBD += links.dbd
dbRecStd_SRCS += lnkConst.c
dbRecStd_SRCS += lnkCalc.c
+dbRecStd_SRCS += lnkState.c
+dbRecStd_SRCS += lnkDebug.c
HTMLS += links.html
-
diff --git a/modules/database/src/std/link/links.dbd.pod b/modules/database/src/std/link/links.dbd.pod
index d94e9e8f0..b5ed582f2 100644
--- a/modules/database/src/std/link/links.dbd.pod
+++ b/modules/database/src/std/link/links.dbd.pod
@@ -13,6 +13,12 @@ The following additional link types are available in this release:
=item * L
+=item * L
+
+=item * L
+
+=item * L
+
=back
=head2 Using JSON Links
@@ -31,15 +37,16 @@ database file syntax.
=cut
+
link(const, lnkConstIf)
=head3 Constant Link C<"const">
-Constant links provide one or more values at link initalization time, but do not
-return any data when their C routine is called. Most record types
-support the use of constant links by calling C at
-record initialization, which results in the constant value being loaded into the
-target field at that time.
+Constant links are input links that provide literal values at link initalization
+time, but do not return any data when their C routine is called.
+Most record types support the use of constant links on their input links by
+calling C at record initialization, which results in
+the constant value being loaded into the target field at that time.
Note that for most record types (the C and C records are the
main exceptions) it is pointless to set an input link to a constant link at
@@ -70,14 +77,35 @@ converted to the desired double value at initialization, for example:
=cut
+
link(calc, lnkCalcIf)
=head3 Calculation Link C<"calc">
-Calculation links can perform simple mathematical expressions on scalar
-(double-precision floating-point) values obtained from other link types and
-return a single double-precision floating-point result. The expressions are
-evaluated by the EPICS Calc engine, and up to 12 inputs can be provided.
+A calculation link is an input link that can evaluate mathematical expressions
+on scalar (double-precision floating-point) values obtained from up to 12 child
+input links, and returns a double-precision floating-point result. The
+expression is evaluated by the EPICS Calc engine, and the result is returned as
+the value of the link.
+
+Two additional expressions may also be provided and are evaluated to determine
+whether the record owning the link should be placed in alarm state. In both
+cases the result of the main calculation is available to these expressions as
+C (attempts to assign to C inside either expression will have no
+lasting effect). If the C expression evaluates to a non-zero value the
+record will be placed in C alarm. If not and the C expression
+evaluates to non-zero the record will be placed in C alarm state.
+
+A calculation link can also be an output link, with the scalar output value
+being converted to a double and provided to the expression as C. Up to 12
+additional input links can also be read and provided to the expression as above.
+The result of the calculation is forwarded to a child output link specified in
+the link's C parameter.
+
+For an output link the main expression is actually optional; if not provided the
+converted value will be forwarded to the output link unchanged. The two alarm
+expressions may still be used to put the output link into alarm state as
+described above.
=head4 Parameters
@@ -88,6 +116,7 @@ The link address is a JSON map with the following keys:
=item expr
The primary expression to be evaluated, given as a string.
+This is optional for output links, required for input links.
=item major
@@ -104,6 +133,12 @@ to the inputs C, C, C, ... C. Each input argument may be either a
numeric literal or an embedded JSON link inside C<{}> braces. The same input
values are provided to the two alarm expressions as to the primary expression.
+=item out
+
+A JSON link inside C<{}> braces which specifies the destination of C
+operations after any expressions have been evaluated.
+This key is required for output links, not used by input links.
+
=item units
An optional string specifying the engineering units for the result of the
@@ -129,3 +164,72 @@ atomically with the value of the input argument.
{calc: {expr:"A*B", args:[{db:"record.VAL"}, 1.5], prec:3}}
=cut
+
+
+link(state, lnkStateIf)
+
+=head3 dbState Link C<"state">
+
+A dbState link is one that gets or puts a boolean value from/to a named global
+flag as implemented by the dbState facility in C. The link type can
+invert the sense of the dbState flag during the get or put if desired.
+
+The value of the named flag is read or written at the time of the link I/O
+operation. When reading a flag, the value returned by the link will be zero or
+one converted to the requested data type. When writing to a flag the boolean
+value of the data written is determined in the originating data type. All
+strings are regarded as true other than C<""> and C<"0"> which are both false.
+
+A link can be configured to invert the sense of the flag data by putting an
+exclamation mark C before the first character of the flag's name in the link
+address.
+
+These dbState flags can be accessed from the IOC Shell with various dbState
+commands, and are also used by the C<"sync"> Channel-Access server-side filter
+mechanism.
+
+=head4 Parameters
+
+The link takes a single parameter which must be a string, providing the name of
+the dbState object, with an optional leading C character to indicate that the
+flag's value should be inverted. The dbState object will be created when the
+link is initialized if it doesn't already exist.
+
+=head4 Examples
+
+ {state:"redBeam"}
+ {state:"!simEnable"}
+
+=cut
+
+
+link(debug, lnkDebugIf)
+variable(lnkDebug_debug, int)
+
+=head3 Debug Link C<"debug">
+
+The debug link type exists to enable debugging of other link types; it provides
+no functionality itself other than to turn on the debug flag for the child link
+that is its only parameter and pass all link operations down to that link.
+
+=head4 Example
+
+ {debug:{state:"redBeam"}}
+
+=cut
+
+
+link(trace, lnkTraceIf)
+
+=head3 Trace Link C<"trace">
+
+The trace link type is a relative of the debug link type that also traces the
+operation of its child link. At creation it turns on the debug flag of its child
+link, then it prints the method arguments and return values of all link
+operations before / after passing control down to the child link.
+
+=head4 Example
+
+ {trace:{state:"redBeam"}}
+
+=cut
diff --git a/modules/database/src/std/link/lnkCalc.c b/modules/database/src/std/link/lnkCalc.c
index e370c36d2..6f525059b 100644
--- a/modules/database/src/std/link/lnkCalc.c
+++ b/modules/database/src/std/link/lnkCalc.c
@@ -6,13 +6,9 @@
\*************************************************************************/
/* lnkCalc.c */
-/* Current usage
- * {calc:{expr:"A", args:[{...}, ...]}}
+/* Usage
+ * {calc:{expr:"A*B", args:[{...}, ...], units:"mm"}}
* First link in 'args' is 'A', second is 'B', and so forth.
- *
- * TODO:
- * Support setting individual input links instead of the args list.
- * {calc:{expr:"K", K:{...}}}
*/
#include
@@ -26,6 +22,7 @@
#include "epicsAssert.h"
#include "epicsString.h"
#include "epicsTypes.h"
+#include "epicsTime.h"
#include "dbAccessDefs.h"
#include "dbCommon.h"
#include "dbConvertFast.h"
@@ -40,15 +37,14 @@
typedef long (*FASTCONVERT)();
-#define IFDEBUG(n) if(clink->jlink.debug)
-
typedef struct calc_link {
jlink jlink; /* embedded object */
int nArgs;
+ short dbfType;
enum {
ps_init,
ps_expr, ps_major, ps_minor,
- ps_args,
+ ps_args, ps_out,
ps_prec,
ps_units,
ps_time,
@@ -66,7 +62,9 @@ typedef struct calc_link {
char *units;
short tinp;
struct link inp[CALCPERFORM_NARGS];
+ struct link out;
double arg[CALCPERFORM_NARGS];
+ epicsTimeStamp time;
double val;
} calc_link;
@@ -77,19 +75,25 @@ static lset lnkCalc_lset;
static jlink* lnkCalc_alloc(short dbfType)
{
- calc_link *clink = calloc(1, sizeof(struct calc_link));
+ calc_link *clink;
- IFDEBUG(10)
- printf("lnkCalc_alloc()\n");
+ if (dbfType == DBF_FWDLINK) {
+ errlogPrintf("lnkCalc: No support for forward links\n");
+ return NULL;
+ }
+
+ clink = calloc(1, sizeof(struct calc_link));
+ if (!clink) {
+ errlogPrintf("lnkCalc: calloc() failed.\n");
+ return NULL;
+ }
clink->nArgs = 0;
+ clink->dbfType = dbfType;
clink->pstate = ps_init;
clink->prec = 15; /* standard value for a double */
clink->tinp = -1;
- IFDEBUG(10)
- printf("lnkCalc_alloc -> calc@%p\n", clink);
-
return &clink->jlink;
}
@@ -98,12 +102,11 @@ static void lnkCalc_free(jlink *pjlink)
calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink);
int i;
- IFDEBUG(10)
- printf("lnkCalc_free(calc@%p)\n", clink);
-
for (i = 0; i < clink->nArgs; i++)
dbJLinkFree(clink->inp[i].value.json.jlink);
+ dbJLinkFree(clink->out.value.json.jlink);
+
free(clink->expr);
free(clink->major);
free(clink->minor);
@@ -118,9 +121,6 @@ static jlif_result lnkCalc_integer(jlink *pjlink, long long num)
{
calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink);
- IFDEBUG(10)
- printf("lnkCalc_integer(calc@%p, %lld)\n", clink, num);
-
if (clink->pstate == ps_prec) {
clink->prec = num;
return jlif_continue;
@@ -146,9 +146,6 @@ static jlif_result lnkCalc_double(jlink *pjlink, double num)
{
calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink);
- IFDEBUG(10)
- printf("lnkCalc_double(calc@%p, %g)\n", clink, num);
-
if (clink->pstate != ps_args) {
return jlif_stop;
errlogPrintf("lnkCalc: Unexpected double %g\n", num);
@@ -171,9 +168,6 @@ static jlif_result lnkCalc_string(jlink *pjlink, const char *val, size_t len)
char *inbuf, *postbuf;
short err;
- IFDEBUG(10)
- printf("lnkCalc_string(calc@%p, \"%.*s\")\n", clink, (int) len, val);
-
if (clink->pstate == ps_units) {
clink->units = epicsStrnDup(val, len);
return jlif_continue;
@@ -181,7 +175,8 @@ static jlif_result lnkCalc_string(jlink *pjlink, const char *val, size_t len)
if (clink->pstate == ps_time) {
char tinp;
- if (len != 1 || (tinp = toupper(val[0])) < 'A' || tinp > 'L') {
+
+ if (len != 1 || (tinp = toupper((int) val[0])) < 'A' || tinp > 'L') {
errlogPrintf("lnkCalc: Bad 'time' parameter \"%.*s\"\n", (int) len, val);
return jlif_stop;
}
@@ -234,11 +229,10 @@ static jlif_key_result lnkCalc_start_map(jlink *pjlink)
{
calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink);
- IFDEBUG(10)
- printf("lnkCalc_start_map(calc@%p)\n", clink);
-
if (clink->pstate == ps_args)
- return jlif_key_child_link;
+ return jlif_key_child_inlink;
+ if (clink->pstate == ps_out)
+ return jlif_key_child_outlink;
if (clink->pstate != ps_init) {
errlogPrintf("lnkCalc: Unexpected map\n");
@@ -252,10 +246,21 @@ static jlif_result lnkCalc_map_key(jlink *pjlink, const char *key, size_t len)
{
calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink);
- IFDEBUG(10)
- printf("lnkCalc_map_key(calc@%p, \"%.*s\")\n", pjlink, (int) len, key);
+ /* FIXME: These errors messages are wrong when a key is duplicated.
+ * The key is known, we just don't allow it more than once.
+ */
- if (len == 4) {
+ if (len == 3) {
+ if (!strncmp(key, "out", len) &&
+ clink->dbfType == DBF_OUTLINK &&
+ clink->out.type == 0)
+ clink->pstate = ps_out;
+ else {
+ errlogPrintf("lnkCalc: Unknown key \"%.3s\"\n", key);
+ return jlif_stop;
+ }
+ }
+ else if (len == 4) {
if (!strncmp(key, "expr", len) && !clink->post_expr)
clink->pstate = ps_expr;
else if (!strncmp(key, "args", len) && !clink->nArgs)
@@ -293,13 +298,16 @@ static jlif_result lnkCalc_end_map(jlink *pjlink)
{
calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink);
- IFDEBUG(10)
- printf("lnkCalc_end_map(calc@%p)\n", clink);
-
if (clink->pstate == ps_error)
return jlif_stop;
- else if (!clink->post_expr) {
- errlogPrintf("lnkCalc: no expression ('expr' key)\n");
+ else if (clink->dbfType == DBF_INLINK &&
+ !clink->post_expr) {
+ errlogPrintf("lnkCalc: No expression ('expr' key)\n");
+ return jlif_stop;
+ }
+ else if (clink->dbfType == DBF_OUTLINK &&
+ clink->out.type != JSON_LINK) {
+ errlogPrintf("lnkCalc: No output link ('out' key)\n");
return jlif_stop;
}
@@ -310,9 +318,6 @@ static jlif_result lnkCalc_start_array(jlink *pjlink)
{
calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink);
- IFDEBUG(10)
- printf("lnkCalc_start_array(calc@%p)\n", clink);
-
if (clink->pstate != ps_args) {
errlogPrintf("lnkCalc: Unexpected array\n");
return jlif_stop;
@@ -325,9 +330,6 @@ static jlif_result lnkCalc_end_array(jlink *pjlink)
{
calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink);
- IFDEBUG(10)
- printf("lnkCalc_end_array(calc@%p)\n", clink);
-
if (clink->pstate == ps_error)
return jlif_stop;
@@ -339,15 +341,27 @@ static void lnkCalc_end_child(jlink *parent, jlink *child)
calc_link *clink = CONTAINER(parent, struct calc_link, jlink);
struct link *plink;
- if (clink->nArgs == CALCPERFORM_NARGS) {
- dbJLinkFree(child);
- errlogPrintf("lnkCalc: Too many input args, limit is %d\n",
- CALCPERFORM_NARGS);
+ if (clink->pstate == ps_args) {
+ if (clink->nArgs == CALCPERFORM_NARGS) {
+ errlogPrintf("lnkCalc: Too many input args, limit is %d\n",
+ CALCPERFORM_NARGS);
+ goto errOut;
+ }
+
+ plink = &clink->inp[clink->nArgs++];
+ }
+ else if (clink->pstate == ps_out) {
+ plink = &clink->out;
+ }
+ else {
+ errlogPrintf("lnkCalc: Unexpected child link, parser state = %d\n",
+ clink->pstate);
+errOut:
clink->pstate = ps_error;
+ dbJLinkFree(child);
return;
}
- plink = &clink->inp[clink->nArgs++];
plink->type = JSON_LINK;
plink->value.json.string = NULL;
plink->value.json.jlink = child;
@@ -355,11 +369,6 @@ static void lnkCalc_end_child(jlink *parent, jlink *child)
static struct lset* lnkCalc_get_lset(const jlink *pjlink)
{
- calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink);
-
- IFDEBUG(10)
- printf("lnkCalc_get_lset(calc@%p)\n", pjlink);
-
return &lnkCalc_lset;
}
@@ -368,9 +377,6 @@ static void lnkCalc_report(const jlink *pjlink, int level, int indent)
calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink);
int i;
- IFDEBUG(10)
- printf("lnkCalc_report(calc@%p)\n", clink);
-
printf("%*s'calc': \"%s\" = %.*g %s\n", indent, "",
clink->expr, clink->prec, clink->val,
clink->units ? clink->units : "");
@@ -388,9 +394,13 @@ static void lnkCalc_report(const jlink *pjlink, int level, int indent)
printf("%*s Minor expression: \"%s\"\n", indent, "",
clink->minor);
- if (clink->tinp >= 0 && clink->tinp < clink->nArgs)
- printf("%*s Timestamp input \"%c\"\n", indent, "",
- clink->tinp + 'A');
+ if (clink->tinp >= 0) {
+ char timeStr[40];
+ epicsTimeToStrftime(timeStr, 40, "%Y-%m-%d %H:%M:%S.%09f",
+ &clink->time);
+ printf("%*s Timestamp input %c: %s\n", indent, "",
+ clink->tinp + 'A', timeStr);
+ }
for (i = 0; i < clink->nArgs; i++) {
struct link *plink = &clink->inp[i];
@@ -403,17 +413,20 @@ static void lnkCalc_report(const jlink *pjlink, int level, int indent)
if (child)
dbJLinkReport(child, level - 1, indent + 4);
}
+
+ if (clink->out.type == JSON_LINK) {
+ printf("%*s Output:\n", indent, "");
+
+ dbJLinkReport(clink->out.value.json.jlink, level - 1, indent + 4);
+ }
}
}
-long lnkCalc_map_children(jlink *pjlink, jlink_map_fn rtn, void *ctx)
+static long lnkCalc_map_children(jlink *pjlink, jlink_map_fn rtn, void *ctx)
{
calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink);
int i;
- IFDEBUG(10)
- printf("lnkCalc_map_children(calc@%p)\n", clink);
-
for (i = 0; i < clink->nArgs; i++) {
struct link *child = &clink->inp[i];
long status = dbJLinkMapChildren(child, rtn, ctx);
@@ -421,6 +434,10 @@ long lnkCalc_map_children(jlink *pjlink, jlink_map_fn rtn, void *ctx)
if (status)
return status;
}
+
+ if (clink->out.type == JSON_LINK) {
+ return dbJLinkMapChildren(&clink->out, rtn, ctx);
+ }
return 0;
}
@@ -432,9 +449,6 @@ static void lnkCalc_open(struct link *plink)
struct calc_link, jlink);
int i;
- IFDEBUG(10)
- printf("lnkCalc_open(calc@%p)\n", clink);
-
for (i = 0; i < clink->nArgs; i++) {
struct link *child = &clink->inp[i];
@@ -442,6 +456,10 @@ static void lnkCalc_open(struct link *plink)
dbJLinkInit(child);
dbLoadLink(child, DBR_DOUBLE, &clink->arg[i]);
}
+
+ if (clink->out.type == JSON_LINK) {
+ dbJLinkInit(&clink->out);
+ }
}
static void lnkCalc_remove(struct dbLocker *locker, struct link *plink)
@@ -450,15 +468,16 @@ static void lnkCalc_remove(struct dbLocker *locker, struct link *plink)
struct calc_link, jlink);
int i;
- IFDEBUG(10)
- printf("lnkCalc_remove(calc@%p)\n", clink);
-
for (i = 0; i < clink->nArgs; i++) {
struct link *child = &clink->inp[i];
dbRemoveLink(locker, child);
}
+ if (clink->out.type == JSON_LINK) {
+ dbRemoveLink(locker, &clink->out);
+ }
+
free(clink->expr);
free(clink->major);
free(clink->minor);
@@ -477,9 +496,6 @@ static int lnkCalc_isConn(const struct link *plink)
int connected = 1;
int i;
- IFDEBUG(10)
- printf("lnkCalc_isConn(calc@%p)\n", clink);
-
for (i = 0; i < clink->nArgs; i++) {
struct link *child = &clink->inp[i];
@@ -488,37 +504,24 @@ static int lnkCalc_isConn(const struct link *plink)
connected = 0;
}
+ if (clink->out.type == JSON_LINK) {
+ struct link *child = &clink->out;
+
+ if (dbLinkIsVolatile(child) &&
+ !dbIsLinkConnected(child))
+ connected = 0;
+ }
+
return connected;
}
static int lnkCalc_getDBFtype(const struct link *plink)
{
- calc_link *clink = CONTAINER(plink->value.json.jlink,
- struct calc_link, jlink);
-
- IFDEBUG(10) {
- calc_link *clink = CONTAINER(plink->value.json.jlink,
- struct calc_link, jlink);
-
- printf("lnkCalc_getDBFtype(calc@%p)\n", clink);
- }
-
return DBF_DOUBLE;
}
static long lnkCalc_getElements(const struct link *plink, long *nelements)
{
- calc_link *clink = CONTAINER(plink->value.json.jlink,
- struct calc_link, jlink);
-
- IFDEBUG(10) {
- calc_link *clink = CONTAINER(plink->value.json.jlink,
- struct calc_link, jlink);
-
- printf("lnkCalc_getElements(calc@%p, (%ld))\n",
- clink, *nelements);
- }
-
*nelements = 1;
return 0;
}
@@ -551,23 +554,22 @@ static long lnkCalc_getValue(struct link *plink, short dbrType, void *pbuffer,
long status;
FASTCONVERT conv = dbFastPutConvertRoutine[DBR_DOUBLE][dbrType];
- IFDEBUG(10)
- printf("lnkCalc_getValue(calc@%p, %d, ...)\n",
- clink, dbrType);
-
/* Any link errors will trigger a LINK/INVALID alarm in the child link */
for (i = 0; i < clink->nArgs; i++) {
struct link *child = &clink->inp[i];
long nReq = 1;
- if (i == clink->tinp &&
- dbLinkIsConstant(&prec->tsel) &&
- prec->tse == epicsTimeEventDeviceTime) {
- struct lcvt vt = {&clink->arg[i], &prec->time};
+ if (i == clink->tinp) {
+ struct lcvt vt = {&clink->arg[i], &clink->time};
status = dbLinkDoLocked(child, readLocked, &vt);
if (status == S_db_noLSET)
status = readLocked(child, &vt);
+
+ if (dbLinkIsConstant(&prec->tsel) &&
+ prec->tse == epicsTimeEventDeviceTime) {
+ prec->time = clink->time;
+ }
}
else
dbGetLink(child, DBR_DOUBLE, &clink->arg[i], NULL, &nReq);
@@ -599,7 +601,7 @@ static long lnkCalc_getValue(struct link *plink, short dbrType, void *pbuffer,
}
}
- if (!status && clink->post_minor) {
+ if (!status && !clink->sevr && clink->post_minor) {
double alval = clink->val;
status = calcPerform(clink->arg, &alval, clink->post_minor);
@@ -613,14 +615,79 @@ static long lnkCalc_getValue(struct link *plink, short dbrType, void *pbuffer,
return status;
}
+static long lnkCalc_putValue(struct link *plink, short dbrType,
+ const void *pbuffer, long nRequest)
+{
+ calc_link *clink = CONTAINER(plink->value.json.jlink,
+ struct calc_link, jlink);
+ dbCommon *prec = plink->precord;
+ int i;
+ long status;
+ FASTCONVERT conv = dbFastGetConvertRoutine[dbrType][DBR_DOUBLE];
+
+ /* Any link errors will trigger a LINK/INVALID alarm in the child link */
+ for (i = 0; i < clink->nArgs; i++) {
+ struct link *child = &clink->inp[i];
+ long nReq = 1;
+
+ if (i == clink->tinp) {
+ struct lcvt vt = {&clink->arg[i], &clink->time};
+
+ status = dbLinkDoLocked(child, readLocked, &vt);
+ if (status == S_db_noLSET)
+ status = readLocked(child, &vt);
+
+ if (dbLinkIsConstant(&prec->tsel) &&
+ prec->tse == epicsTimeEventDeviceTime) {
+ prec->time = clink->time;
+ }
+ }
+ else
+ dbGetLink(child, DBR_DOUBLE, &clink->arg[i], NULL, &nReq);
+ }
+ clink->stat = 0;
+ clink->sevr = 0;
+
+ /* Get the value being output as VAL */
+ status = conv(pbuffer, &clink->val, NULL);
+
+ if (!status && clink->post_expr)
+ status = calcPerform(clink->arg, &clink->val, clink->post_expr);
+
+ if (!status && clink->post_major) {
+ double alval = clink->val;
+
+ status = calcPerform(clink->arg, &alval, clink->post_major);
+ if (!status && alval) {
+ clink->stat = LINK_ALARM;
+ clink->sevr = MAJOR_ALARM;
+ recGblSetSevr(prec, clink->stat, clink->sevr);
+ }
+ }
+
+ if (!status && !clink->sevr && clink->post_minor) {
+ double alval = clink->val;
+
+ status = calcPerform(clink->arg, &alval, clink->post_minor);
+ if (!status && alval) {
+ clink->stat = LINK_ALARM;
+ clink->sevr = MINOR_ALARM;
+ recGblSetSevr(prec, clink->stat, clink->sevr);
+ }
+ }
+
+ if (!status) {
+ status = dbPutLink(&clink->out, DBR_DOUBLE, &clink->val, 1);
+ }
+
+ return status;
+}
+
static long lnkCalc_getPrecision(const struct link *plink, short *precision)
{
calc_link *clink = CONTAINER(plink->value.json.jlink,
struct calc_link, jlink);
- IFDEBUG(10)
- printf("lnkCalc_getPrecision(calc@%p)\n", clink);
-
*precision = clink->prec;
return 0;
}
@@ -630,9 +697,6 @@ static long lnkCalc_getUnits(const struct link *plink, char *units, int len)
calc_link *clink = CONTAINER(plink->value.json.jlink,
struct calc_link, jlink);
- IFDEBUG(10)
- printf("lnkCalc_getUnits(calc@%p)\n", clink);
-
if (clink->units) {
strncpy(units, clink->units, --len);
units[len] = '\0';
@@ -648,9 +712,6 @@ static long lnkCalc_getAlarm(const struct link *plink, epicsEnum16 *status,
calc_link *clink = CONTAINER(plink->value.json.jlink,
struct calc_link, jlink);
- IFDEBUG(10)
- printf("lnkCalc_getAlarm(calc@%p)\n", clink);
-
if (status)
*status = clink->stat;
if (severity)
@@ -659,6 +720,19 @@ static long lnkCalc_getAlarm(const struct link *plink, epicsEnum16 *status,
return 0;
}
+static long lnkCalc_getTimestamp(const struct link *plink, epicsTimeStamp *pstamp)
+{
+ calc_link *clink = CONTAINER(plink->value.json.jlink,
+ struct calc_link, jlink);
+
+ if (clink->tinp >= 0) {
+ *pstamp = clink->time;
+ return 0;
+ }
+
+ return -1;
+}
+
static long doLocked(struct link *plink, dbLinkUserCallback rtn, void *priv)
{
return rtn(plink, priv);
@@ -675,8 +749,8 @@ static lset lnkCalc_lset = {
lnkCalc_getValue,
NULL, NULL, NULL,
lnkCalc_getPrecision, lnkCalc_getUnits,
- lnkCalc_getAlarm, NULL,
- NULL, NULL,
+ lnkCalc_getAlarm, lnkCalc_getTimestamp,
+ lnkCalc_putValue, NULL,
NULL, doLocked
};
@@ -686,6 +760,6 @@ static jlif lnkCalcIf = {
lnkCalc_start_map, lnkCalc_map_key, lnkCalc_end_map,
lnkCalc_start_array, lnkCalc_end_array,
lnkCalc_end_child, lnkCalc_get_lset,
- lnkCalc_report, lnkCalc_map_children
+ lnkCalc_report, lnkCalc_map_children, NULL
};
epicsExportAddress(jlif, lnkCalcIf);
diff --git a/modules/database/src/std/link/lnkConst.c b/modules/database/src/std/link/lnkConst.c
index c2eb032c1..cb948fcb3 100644
--- a/modules/database/src/std/link/lnkConst.c
+++ b/modules/database/src/std/link/lnkConst.c
@@ -2,7 +2,7 @@
* Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne
* National Laboratory.
* EPICS BASE is distributed subject to a Software License Agreement found
-* in file LICENSE that is included with this distribution.
+* in file LICENSE that is included with this distribution.
\*************************************************************************/
/* lnkConst.c */
@@ -22,8 +22,6 @@
#include "epicsExport.h"
-#define IFDEBUG(n) if (clink->jlink.debug)
-
typedef long (*FASTCONVERT)();
typedef struct const_link {
@@ -48,18 +46,23 @@ static lset lnkConst_lset;
static jlink* lnkConst_alloc(short dbfType)
{
- const_link *clink = calloc(1, sizeof(*clink));
+ const_link *clink;
- IFDEBUG(10)
- printf("lnkConst_alloc()\n");
+ if (dbfType != DBF_INLINK) {
+ errlogPrintf("lnkConst: Only works with input links\n");
+ return NULL;
+ }
+
+ clink = calloc(1, sizeof(*clink));
+ if (!clink) {
+ errlogPrintf("lnkConst: calloc() failed.\n");
+ return NULL;
+ }
clink->type = s0;
clink->nElems = 0;
clink->value.pmem = NULL;
- IFDEBUG(10)
- printf("lnkConst_alloc -> const@%p\n", clink);
-
return &clink->jlink;
}
@@ -67,9 +70,6 @@ static void lnkConst_free(jlink *pjlink)
{
const_link *clink = CONTAINER(pjlink, const_link, jlink);
- IFDEBUG(10)
- printf("lnkConst_free(const@%p) type=%d\n", pjlink, clink->type);
-
switch (clink->type) {
int i;
case ac40:
@@ -95,16 +95,13 @@ static jlif_result lnkConst_integer(jlink *pjlink, long long num)
const_link *clink = CONTAINER(pjlink, const_link, jlink);
int newElems = clink->nElems + 1;
- IFDEBUG(10)
- printf("lnkConst_integer(const@%p, %lld)\n", pjlink, num);
-
switch (clink->type) {
void *buf;
case s0:
clink->type = si64;
clink->value.scalar_integer = num;
- IFDEBUG(12)
+ if (pjlink->debug)
printf(" si64 := %lld\n", num);
break;
@@ -118,7 +115,7 @@ static jlif_result lnkConst_integer(jlink *pjlink, long long num)
clink->value.pmem = buf;
clink->value.pintegers[clink->nElems] = num;
- IFDEBUG(12)
+ if (pjlink->debug)
printf(" ai64 += %lld\n", num);
break;
@@ -129,7 +126,7 @@ static jlif_result lnkConst_integer(jlink *pjlink, long long num)
clink->value.pmem = buf;
clink->value.pdoubles[clink->nElems] = num;
- IFDEBUG(12)
+ if (pjlink->debug)
printf(" af64 += %lld\n", num);
break;
@@ -146,10 +143,6 @@ static jlif_result lnkConst_integer(jlink *pjlink, long long num)
static jlif_result lnkConst_boolean(jlink *pjlink, int val)
{
- const_link *clink = CONTAINER(pjlink, const_link, jlink);
- IFDEBUG(10)
- printf("lnkConst_boolean(const@%p, %d)\n", pjlink, val);
-
return lnkConst_integer(pjlink, val);
}
@@ -158,9 +151,6 @@ static jlif_result lnkConst_double(jlink *pjlink, double num)
const_link *clink = CONTAINER(pjlink, const_link, jlink);
int newElems = clink->nElems + 1;
- IFDEBUG(10)
- printf("lnkConst_double(const@%p, %g)\n", pjlink, num);
-
switch (clink->type) {
epicsFloat64 *f64buf;
int i;
@@ -212,9 +202,6 @@ static jlif_result lnkConst_string(jlink *pjlink, const char *val, size_t len)
const_link *clink = CONTAINER(pjlink, const_link, jlink);
int newElems = clink->nElems + 1;
- IFDEBUG(10)
- printf("lnkConst_string(const@%p, \"%.*s\")\n", clink, (int) len, val);
-
switch (clink->type) {
char **vec, *str;
@@ -262,9 +249,6 @@ static jlif_result lnkConst_start_array(jlink *pjlink)
{
const_link *clink = CONTAINER(pjlink, const_link, jlink);
- IFDEBUG(10)
- printf("lnkConst_start_array(const@%p)\n", pjlink);
-
if (clink->type != s0) {
errlogPrintf("lnkConst: Embedded array value\n");
return jlif_stop;
@@ -276,21 +260,11 @@ static jlif_result lnkConst_start_array(jlink *pjlink)
static jlif_result lnkConst_end_array(jlink *pjlink)
{
- const_link *clink = CONTAINER(pjlink, const_link, jlink);
-
- IFDEBUG(10)
- printf("lnkConst_end_array(const@%p)\n", pjlink);
-
return jlif_continue;
}
static struct lset* lnkConst_get_lset(const jlink *pjlink)
{
- const_link *clink = CONTAINER(pjlink, const_link, jlink);
-
- IFDEBUG(10)
- printf("lnkConst_get_lset(const@%p)\n", pjlink);
-
return &lnkConst_lset;
}
@@ -318,9 +292,6 @@ static void lnkConst_report(const jlink *pjlink, int level, int indent)
};
const char * const dtype = type_names[clink->type & 3];
- IFDEBUG(10)
- printf("lnkConst_report(const@%p)\n", clink);
-
if (clink->type > a0) {
const char * const plural = clink->nElems > 1 ? "s" : "";
@@ -382,11 +353,6 @@ static void lnkConst_report(const jlink *pjlink, int level, int indent)
static void lnkConst_remove(struct dbLocker *locker, struct link *plink)
{
- const_link *clink = CONTAINER(plink->value.json.jlink, const_link, jlink);
-
- IFDEBUG(10)
- printf("lnkConst_remove(const@%p)\n", clink);
-
lnkConst_free(plink->value.json.jlink);
}
@@ -395,55 +361,51 @@ static long lnkConst_loadScalar(struct link *plink, short dbrType, void *pbuffer
const_link *clink = CONTAINER(plink->value.json.jlink, const_link, jlink);
long status;
- IFDEBUG(10)
- printf("lnkConst_loadScalar(const@%p, %d, %p)\n",
- clink, dbrType, pbuffer);
-
switch (clink->type) {
case si64:
- IFDEBUG(12)
+ if (clink->jlink.debug)
printf(" si64 %lld\n", clink->value.scalar_integer);
status = dbFastPutConvertRoutine[DBF_INT64][dbrType]
(&clink->value.scalar_integer, pbuffer, NULL);
break;
case sf64:
- IFDEBUG(12)
+ if (clink->jlink.debug)
printf(" sf64 %g\n", clink->value.scalar_double);
status = dbFastPutConvertRoutine[DBF_DOUBLE][dbrType]
(&clink->value.scalar_double, pbuffer, NULL);
break;
case sc40:
- IFDEBUG(12)
+ if (clink->jlink.debug)
printf(" sc40 '%s'\n", clink->value.scalar_string);
status = dbFastPutConvertRoutine[DBF_STRING][dbrType]
(clink->value.scalar_string, pbuffer, NULL);
break;
case ai64:
- IFDEBUG(12)
+ if (clink->jlink.debug)
printf(" ai64 [%lld, ...]\n", clink->value.pintegers[0]);
status = dbFastPutConvertRoutine[DBF_INT64][dbrType]
(clink->value.pintegers, pbuffer, NULL);
break;
case af64:
- IFDEBUG(12)
+ if (clink->jlink.debug)
printf(" af64 [%g, ...]\n", clink->value.pdoubles[0]);
status = dbFastPutConvertRoutine[DBF_DOUBLE][dbrType]
(clink->value.pdoubles, pbuffer, NULL);
break;
case ac40:
- IFDEBUG(12)
+ if (clink->jlink.debug)
printf(" ac40 ['%s', ...]\n", clink->value.pstrings[0]);
status = dbFastPutConvertRoutine[DBF_STRING][dbrType]
(clink->value.pstrings[0], pbuffer, NULL);
break;
default:
- IFDEBUG(12)
+ if (clink->jlink.debug)
printf(" Bad type %d\n", clink->type);
status = S_db_badField;
break;
@@ -458,27 +420,23 @@ static long lnkConst_loadLS(struct link *plink, char *pbuffer, epicsUInt32 size,
const_link *clink = CONTAINER(plink->value.json.jlink, const_link, jlink);
const char *pstr;
- IFDEBUG(10)
- printf("lnkConst_loadLS(const@%p, %p, %d, %d)\n",
- clink, pbuffer, size, *plen);
-
if(!size) return 0;
switch (clink->type) {
case sc40:
- IFDEBUG(12)
+ if (clink->jlink.debug)
printf(" sc40 '%s'\n", clink->value.scalar_string);
pstr = clink->value.scalar_string;
break;
case ac40:
- IFDEBUG(12)
+ if (clink->jlink.debug)
printf(" ac40 ['%s', ...]\n", clink->value.pstrings[0]);
pstr = clink->value.pstrings[0];
break;
default:
- IFDEBUG(12)
+ if (clink->jlink.debug)
printf(" Bad type %d\n", clink->type);
return S_db_badField;
}
@@ -499,10 +457,6 @@ static long lnkConst_loadArray(struct link *plink, short dbrType, void *pbuffer,
FASTCONVERT conv;
long status;
- IFDEBUG(10)
- printf("lnkConst_loadArray(const@%p, %d, %p, (%ld))\n",
- clink, dbrType, pbuffer, *pnReq);
-
if (nElems > *pnReq)
nElems = *pnReq;
@@ -510,28 +464,28 @@ static long lnkConst_loadArray(struct link *plink, short dbrType, void *pbuffer,
int i;
case si64:
- IFDEBUG(12)
+ if (clink->jlink.debug)
printf(" si64 %lld\n", clink->value.scalar_integer);
status = dbFastPutConvertRoutine[DBF_INT64][dbrType]
(&clink->value.scalar_integer, pdest, NULL);
break;
case sf64:
- IFDEBUG(12)
+ if (clink->jlink.debug)
printf(" sf64 %g\n", clink->value.scalar_double);
status = dbFastPutConvertRoutine[DBF_DOUBLE][dbrType]
(&clink->value.scalar_double, pdest, NULL);
break;
case sc40:
- IFDEBUG(12)
+ if (clink->jlink.debug)
printf(" sc40 '%s'\n", clink->value.scalar_string);
status = dbFastPutConvertRoutine[DBF_STRING][dbrType]
(clink->value.scalar_string, pbuffer, NULL);
break;
case ai64:
- IFDEBUG(12)
+ if (clink->jlink.debug)
printf(" ai64 [%lld, ...]\n", clink->value.pintegers[0]);
conv = dbFastPutConvertRoutine[DBF_INT64][dbrType];
for (i = 0; i < nElems; i++) {
@@ -542,7 +496,7 @@ static long lnkConst_loadArray(struct link *plink, short dbrType, void *pbuffer,
break;
case af64:
- IFDEBUG(12)
+ if (clink->jlink.debug)
printf(" af64 [%g, ...]\n", clink->value.pdoubles[0]);
conv = dbFastPutConvertRoutine[DBF_DOUBLE][dbrType];
for (i = 0; i < nElems; i++) {
@@ -553,7 +507,7 @@ static long lnkConst_loadArray(struct link *plink, short dbrType, void *pbuffer,
break;
case ac40:
- IFDEBUG(12)
+ if (clink->jlink.debug)
printf(" ac40 ['%s', ...]\n", clink->value.pstrings[0]);
conv = dbFastPutConvertRoutine[DBF_STRING][dbrType];
for (i = 0; i < nElems; i++) {
@@ -564,7 +518,7 @@ static long lnkConst_loadArray(struct link *plink, short dbrType, void *pbuffer,
break;
default:
- IFDEBUG(12)
+ if (clink->jlink.debug)
printf(" Bad type %d\n", clink->type);
status = S_db_badField;
}
@@ -574,12 +528,6 @@ static long lnkConst_loadArray(struct link *plink, short dbrType, void *pbuffer,
static long lnkConst_getNelements(const struct link *plink, long *nelements)
{
- const_link *clink = CONTAINER(plink->value.json.jlink, const_link, jlink);
-
- IFDEBUG(10)
- printf("lnkConst_getNelements(const@%p, (%ld))\n",
- plink->value.json.jlink, *nelements);
-
*nelements = 0;
return 0;
}
@@ -587,13 +535,6 @@ static long lnkConst_getNelements(const struct link *plink, long *nelements)
static long lnkConst_getValue(struct link *plink, short dbrType, void *pbuffer,
long *pnRequest)
{
- const_link *clink = CONTAINER(plink->value.json.jlink, const_link, jlink);
-
- IFDEBUG(10)
- printf("lnkConst_getValue(const@%p, %d, %p, ... (%ld))\n",
- plink->value.json.jlink, dbrType, pbuffer,
- pnRequest ? *pnRequest : 0);
-
if (pnRequest)
*pnRequest = 0;
return 0;
@@ -626,7 +567,6 @@ static jlif lnkConstIf = {
NULL, NULL, NULL,
lnkConst_start_array, lnkConst_end_array,
NULL, lnkConst_get_lset,
- lnkConst_report, NULL
+ lnkConst_report, NULL, NULL
};
epicsExportAddress(jlif, lnkConstIf);
-
diff --git a/modules/database/src/std/link/lnkDebug.c b/modules/database/src/std/link/lnkDebug.c
new file mode 100644
index 000000000..bd8f84e5a
--- /dev/null
+++ b/modules/database/src/std/link/lnkDebug.c
@@ -0,0 +1,1045 @@
+/*************************************************************************\
+* Copyright (c) 2018 UChicago Argonne LLC, as Operator of Argonne
+* National Laboratory.
+* EPICS BASE is distributed subject to a Software License Agreement found
+* in file LICENSE that is included with this distribution.
+\*************************************************************************/
+/* lnkDebug.c */
+
+/* Usage
+ * {debug:{...:...}}
+ */
+
+#include
+#include
+#include
+
+#include "alarm.h"
+#include "dbDefs.h"
+#include "dbAccessDefs.h"
+#include "dbLink.h"
+#include "dbJLink.h"
+#include "dbStaticLib.h"
+#include "errlog.h"
+#include "epicsTime.h"
+
+#include "epicsExport.h"
+
+/* This is for debugging the debug link-type */
+int lnkDebug_debug;
+epicsExportAddress(int, lnkDebug_debug);
+
+#define IFDEBUG(n) if (lnkDebug_debug >= (n))
+
+typedef struct debug_link {
+ jlink jlink; /* embedded object */
+ short dbfType;
+ unsigned trace:1;
+ const jlif *child_jlif;
+ const lset *child_lset;
+ jlif jlif;
+ lset lset;
+ struct link child_link;
+} debug_link;
+
+
+/********************* Delegating jlif Routines *********************/
+
+static void delegate_free(jlink *pjlink)
+{
+ debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink);
+ const jlif *pif = dlink->child_jlif;
+ struct link *plink = &dlink->child_link;
+
+ if (dlink->trace)
+ printf("Link trace: Calling %s::free_jlink(%p)\n",
+ pif->name, pjlink);
+
+ pif->free_jlink(pjlink);
+ plink->type = 0;
+ plink->value.json.jlink = NULL;
+
+ if (dlink->trace)
+ printf("Link trace: %s::free_jlink(%p) returned\n",
+ pif->name, pjlink);
+}
+
+static jlif_result delegate_null(jlink *pjlink)
+{
+ debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink);
+ const jlif *pif = dlink->child_jlif;
+ jlif_result res;
+
+ if (dlink->trace)
+ printf("Link trace: Calling %s::parse_null(%p)\n",
+ pif->name, pjlink);
+
+ res = pif->parse_null(pjlink);
+
+ if (dlink->trace)
+ printf("Link trace: %s::parse_null(%p) returned %s\n",
+ pif->name, pjlink, jlif_result_name[res]);
+
+ return res;
+}
+
+static jlif_result delegate_boolean(jlink *pjlink, int val)
+{
+ debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink);
+ const jlif *pif = dlink->child_jlif;
+ jlif_result res;
+
+ if (dlink->trace)
+ printf("Link trace: Calling %s::parse_boolean(%p, %d)\n",
+ pif->name, pjlink, val);
+
+ res = pif->parse_boolean(pjlink, val);
+
+ if (dlink->trace)
+ printf("Link trace: %s::parse_boolean(%p) returned %s\n",
+ pif->name, pjlink, jlif_result_name[res]);
+
+ return res;
+}
+
+static jlif_result delegate_integer(jlink *pjlink, long long num)
+{
+ debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink);
+ const jlif *pif = dlink->child_jlif;
+ jlif_result res;
+
+ if (dlink->trace)
+ printf("Link trace: Calling %s::parse_integer(%p, %lld)\n",
+ pif->name, pjlink, num);
+
+ res = pif->parse_integer(pjlink, num);
+
+ if (dlink->trace)
+ printf("Link trace: %s::parse_integer(%p) returned %s\n",
+ pif->name, pjlink, jlif_result_name[res]);
+
+ return res;
+}
+
+static jlif_result delegate_double(jlink *pjlink, double num)
+{
+ debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink);
+ const jlif *pif = dlink->child_jlif;
+ jlif_result res;
+
+ if (dlink->trace)
+ printf("Link trace: Calling %s::parse_double(%p, %g)\n",
+ pif->name, pjlink, num);
+
+ res = pif->parse_double(pjlink, num);
+
+ if (dlink->trace)
+ printf("Link trace: %s::parse_double(%p) returned %s\n",
+ pif->name, pjlink, jlif_result_name[res]);
+
+ return res;
+}
+
+static jlif_result delegate_string(jlink *pjlink, const char *val, size_t len)
+{
+ debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink);
+ const jlif *pif = dlink->child_jlif;
+ jlif_result res;
+
+ if (dlink->trace)
+ printf("Link trace: Calling %s::parse_string(%p, \"%.*s\")\n",
+ pif->name, pjlink, (int) len, val);
+
+ res = pif->parse_string(pjlink, val, len);
+
+ if (dlink->trace)
+ printf("Link trace: %s::parse_string(%p) returned %s\n",
+ pif->name, pjlink, jlif_result_name[res]);
+
+ return res;
+}
+
+static jlif_key_result delegate_start_map(jlink *pjlink)
+{
+ debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink);
+ const jlif *pif = dlink->child_jlif;
+ jlif_key_result res;
+
+ if (dlink->trace)
+ printf("Link trace: Calling %s::parse_start_map(%p)\n",
+ pif->name, pjlink);
+
+ res = pif->parse_start_map(pjlink);
+
+ if (dlink->trace)
+ printf("Link trace: %s::parse_start_map(%p) returned %s\n",
+ pif->name, pjlink, jlif_key_result_name[res]);
+
+ return res;
+}
+
+static jlif_result delegate_map_key(jlink *pjlink, const char *key, size_t len)
+{
+ debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink);
+ const jlif *pif = dlink->child_jlif;
+ jlif_result res;
+
+ if (dlink->trace)
+ printf("Link trace: Calling %s::parse_map_key(%p, \"%.*s\")\n",
+ pif->name, pjlink, (int) len, key);
+
+ res = pif->parse_map_key(pjlink, key, len);
+
+ if (dlink->trace)
+ printf("Link trace: %s::parse_map_key(%p) returned %s\n",
+ pif->name, pjlink, jlif_result_name[res]);
+
+ return res;
+}
+
+static jlif_result delegate_end_map(jlink *pjlink)
+{
+ debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink);
+ const jlif *pif = dlink->child_jlif;
+ jlif_result res;
+
+ if (dlink->trace)
+ printf("Link trace: Calling %s::parse_end_map(%p)\n",
+ pif->name, pjlink);
+
+ res = pif->parse_end_map(pjlink);
+
+ if (dlink->trace)
+ printf("Link trace: %s::parse_end_map(%p) returned %s\n",
+ pif->name, pjlink, jlif_result_name[res]);
+
+ return res;
+}
+
+static jlif_result delegate_start_array(jlink *pjlink)
+{
+ debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink);
+ const jlif *pif = dlink->child_jlif;
+ jlif_result res;
+
+ if (dlink->trace)
+ printf("Link trace: Calling %s::parse_(%p)\n",
+ pif->name, pjlink);
+
+ res = pif->parse_start_array(pjlink);
+
+ if (dlink->trace)
+ printf("Link trace: %s::parse_start_array(%p) returned %s\n",
+ pif->name, pjlink, jlif_result_name[res]);
+
+ return res;
+}
+
+static jlif_result delegate_end_array(jlink *pjlink)
+{
+ debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink);
+ const jlif *pif = dlink->child_jlif;
+ jlif_result res;
+
+ if (dlink->trace)
+ printf("Link trace: Calling %s::parse_end_array(%p)\n",
+ pif->name, pjlink);
+
+ res = pif->parse_end_array(pjlink);
+
+ if (dlink->trace)
+ printf("Link trace: %s::parse_end_array(%p) returned %s\n",
+ pif->name, pjlink, jlif_result_name[res]);
+
+ return res;
+}
+
+static void delegate_start_child(jlink *pjlink, jlink *child)
+{
+ debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink);
+ const jlif *pif = dlink->child_jlif;
+
+ if (dlink->trace)
+ printf("Link trace: Calling %s::start_child(%p, %p)\n",
+ pif->name, pjlink, child);
+
+ pif->start_child(pjlink, child);
+
+ if (dlink->trace)
+ printf("Link trace: %s::start_child(%p) returned\n",
+ pif->name, pjlink);
+}
+
+static void delegate_end_child(jlink *pjlink, jlink *child)
+{
+ debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink);
+ const jlif *pif = dlink->child_jlif;
+
+ if (dlink->trace)
+ printf("Link trace: Calling %s::end_child(%p, %p)\n",
+ pif->name, pjlink, child);
+
+ pif->end_child(pjlink, child);
+
+ if (dlink->trace)
+ printf("Link trace: %s::end_child(%p) returned\n",
+ pif->name, pjlink);
+}
+
+static struct lset* delegate_get_lset(const jlink *pjlink)
+{
+ debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink);
+
+ if (dlink->trace)
+ printf("Link trace: NOT calling %s::get_lset(%p)\n",
+ dlink->child_jlif->name, pjlink);
+
+ return &dlink->lset;
+}
+
+static void delegate_report(const jlink *pjlink, int level, int indent)
+{
+ debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink);
+ const jlif *pif = dlink->child_jlif;
+
+ if (dlink->trace)
+ printf("Link trace: Calling %s::report(%p, %d, %d)\n",
+ pif->name, pjlink, level, indent);
+
+ pif->report(pjlink, level, indent);
+
+ if (dlink->trace)
+ printf("Link trace: %s::report(%p) returned\n",
+ pif->name, pjlink);
+}
+
+static long delegate_map_children(jlink *pjlink, jlink_map_fn rtn, void *ctx)
+{
+ debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink);
+ const jlif *pif = dlink->child_jlif;
+ long res;
+
+ if (dlink->trace)
+ printf("Link trace: Calling %s::map_children(%p, %p, %p)\n",
+ pif->name, pjlink, rtn, ctx);
+
+ res = pif->map_children(pjlink, rtn, ctx);
+
+ if (dlink->trace)
+ printf("Link trace: %s::map_children(%p) returned %ld\n",
+ pif->name, pjlink, res);
+
+ return res;
+}
+
+
+/********************* Delegating lset Routines *********************/
+
+static void delegate_openLink(struct link *plink)
+{
+ debug_link *dlink = CONTAINER(plink->value.json.jlink,
+ struct debug_link, jlink);
+ struct link *clink = &dlink->child_link;
+ const lset *clset = dlink->child_lset;
+
+ if (dlink->trace)
+ printf("Link trace: Calling %s::openLink(%p = jlink %p)\n",
+ dlink->child_jlif->name, clink, clink->value.json.jlink);
+
+ clink->precord = plink->precord;
+ clset->openLink(clink);
+
+ if (dlink->trace)
+ printf("Link trace: %s::openLink(%p) returned\n",
+ dlink->child_jlif->name, clink);
+}
+
+static void delegate_removeLink(struct dbLocker *locker, struct link *plink)
+{
+ debug_link *dlink = CONTAINER(plink->value.json.jlink,
+ struct debug_link, jlink);
+ struct link *clink = &dlink->child_link;
+ const lset *clset = dlink->child_lset;
+
+ if (dlink->trace)
+ printf("Link trace: Calling %s::removeLink(%p, %p)\n",
+ dlink->child_jlif->name, locker, clink);
+
+ clset->removeLink(locker, clink);
+
+ if (dlink->trace)
+ printf("Link trace: %s::removeLink(%p) returned\n",
+ dlink->child_jlif->name, clink);
+}
+
+static long delegate_loadScalar(struct link *plink, short dbrType, void *pbuffer)
+{
+ debug_link *dlink = CONTAINER(plink->value.json.jlink,
+ struct debug_link, jlink);
+ struct link *clink = &dlink->child_link;
+ const lset *clset = dlink->child_lset;
+ long res;
+
+ if (dlink->trace)
+ printf("Link trace: Calling %s::loadScalar(%p, %s, %p)\n",
+ dlink->child_jlif->name, clink,
+ dbGetFieldTypeString(dbrType), pbuffer);
+
+ res = clset->loadScalar(clink, dbrType, pbuffer);
+
+ if (dlink->trace)
+ printf("Link trace: %s::loadScalar(%p) returned %ld (0x%lx)\n",
+ dlink->child_jlif->name, clink, res, res);
+
+ return res;
+}
+
+static long delegate_loadLS(struct link *plink, char *pbuffer, epicsUInt32 size,
+ epicsUInt32 *plen)
+{
+ debug_link *dlink = CONTAINER(plink->value.json.jlink,
+ struct debug_link, jlink);
+ struct link *clink = &dlink->child_link;
+ const lset *clset = dlink->child_lset;
+ long res;
+
+ if (dlink->trace)
+ printf("Link trace: Calling %s::loadLS(%p, %p, %u)\n",
+ dlink->child_jlif->name, clink, pbuffer, size);
+
+ res = clset->loadLS(clink, pbuffer, size, plen);
+
+ if (dlink->trace) {
+ printf("Link trace: %s::loadLS(%p) returned %ld (0x%lx)\n",
+ dlink->child_jlif->name, clink, res, res);
+ if (res == 0)
+ printf(" Loaded: %u byte(s) \"%s\"\n", *plen, pbuffer);
+ }
+
+ return res;
+}
+
+static long delegate_loadArray(struct link *plink, short dbrType, void *pbuffer,
+ long *pnRequest)
+{
+ debug_link *dlink = CONTAINER(plink->value.json.jlink,
+ struct debug_link, jlink);
+ struct link *clink = &dlink->child_link;
+ const lset *clset = dlink->child_lset;
+ long res;
+
+ if (dlink->trace)
+ printf("Link trace: Calling %s::loadArray(%p, %s, %p, %ld)\n",
+ dlink->child_jlif->name, clink,
+ dbGetFieldTypeString(dbrType), pbuffer, pnRequest ? *pnRequest : 0l);
+
+ res = clset->loadArray(clink, dbrType, pbuffer, pnRequest);
+
+ if (dlink->trace) {
+ printf("Link trace: %s::loadArray(%p) returned %ld (0x%lx)\n",
+ dlink->child_jlif->name, clink, res, res);
+ if (res == 0)
+ printf(" Loaded: %ld element(s)\n", pnRequest ? *pnRequest : 1l);
+ }
+
+ return res;
+}
+
+static int delegate_isConnected(const struct link *plink)
+{
+ debug_link *dlink = CONTAINER(plink->value.json.jlink,
+ struct debug_link, jlink);
+ struct link *clink = &dlink->child_link;
+ const lset *clset = dlink->child_lset;
+ int res;
+
+ if (dlink->trace)
+ printf("Link trace: Calling %s::isConnected(%p)\n",
+ dlink->child_jlif->name, clink);
+
+ res = clset->isConnected(clink);
+
+ if (dlink->trace)
+ printf("Link trace: %s::isConnected(%p) returned %d (%s)\n",
+ dlink->child_jlif->name, clink, res,
+ res == 0 ? "No" : res == 1 ? "Yes" : "Bad value");
+
+ return res;
+}
+
+static int delegate_getDBFtype(const struct link *plink)
+{
+ debug_link *dlink = CONTAINER(plink->value.json.jlink,
+ struct debug_link, jlink);
+ struct link *clink = &dlink->child_link;
+ const lset *clset = dlink->child_lset;
+ int res;
+
+ if (dlink->trace)
+ printf("Link trace: Calling %s::getDBFtype(%p)\n",
+ dlink->child_jlif->name, clink);
+
+ res = clset->getDBFtype(clink);
+
+ if (dlink->trace)
+ printf("Link trace: %s::getDBFtype(%p) returned %d (%s)\n",
+ dlink->child_jlif->name, clink, res,
+ res == -1 ? "Link disconnected" : dbGetFieldTypeString(res));
+
+ return res;
+}
+
+static long delegate_getElements(const struct link *plink, long *pnElements)
+{
+ debug_link *dlink = CONTAINER(plink->value.json.jlink,
+ struct debug_link, jlink);
+ struct link *clink = &dlink->child_link;
+ const lset *clset = dlink->child_lset;
+ long res;
+
+ if (dlink->trace)
+ printf("Link trace: Calling %s::getElements(%p)\n",
+ dlink->child_jlif->name, clink);
+
+ res = clset->getElements(clink, pnElements);
+
+ if (dlink->trace) {
+ printf("Link trace: %s::getElements(%p) returned %ld (0x%lx)\n",
+ dlink->child_jlif->name, clink, res, res);
+ if (res == 0)
+ printf(" Result: %ld element(s)\n", *pnElements);
+ }
+
+ return res;
+}
+
+static long delegate_getValue(struct link *plink, short dbrType, void *pbuffer,
+ long *pnRequest)
+{
+ debug_link *dlink = CONTAINER(plink->value.json.jlink,
+ struct debug_link, jlink);
+ struct link *clink = &dlink->child_link;
+ const lset *clset = dlink->child_lset;
+ long res;
+
+ if (dlink->trace)
+ printf("Link trace: Calling %s::getValue(%p, %s, %p, %ld)\n",
+ dlink->child_jlif->name, clink,
+ dbGetFieldTypeString(dbrType), pbuffer, pnRequest ? *pnRequest : 0l);
+
+ res = clset->getValue(clink, dbrType, pbuffer, pnRequest);
+
+ if (dlink->trace) {
+ printf("Link trace: %s::getValue(%p) returned %ld (0x%lx)\n",
+ dlink->child_jlif->name, clink, res, res);
+ if (res == 0)
+ printf(" Got: %ld element(s)\n", pnRequest ? *pnRequest : 1l);
+ }
+
+ return res;
+}
+
+static long delegate_getControlLimits(const struct link *plink,
+ double *lo, double *hi)
+{
+ debug_link *dlink = CONTAINER(plink->value.json.jlink,
+ struct debug_link, jlink);
+ struct link *clink = &dlink->child_link;
+ const lset *clset = dlink->child_lset;
+ long res;
+
+ if (dlink->trace)
+ printf("Link trace: Calling %s::getControlLimits(%p)\n",
+ dlink->child_jlif->name, clink);
+
+ res = clset->getControlLimits(clink, lo, hi);
+
+ if (dlink->trace) {
+ printf("Link trace: %s::getControlLimits(%p) returned %ld (0x%lx)\n",
+ dlink->child_jlif->name, clink, res, res);
+ if (res == 0)
+ printf(" Got: Lo = %g, Hi = %g\n", *lo, *hi);
+ }
+
+ return res;
+}
+
+static long delegate_getGraphicLimits(const struct link *plink,
+ double *lo, double *hi)
+{
+ debug_link *dlink = CONTAINER(plink->value.json.jlink,
+ struct debug_link, jlink);
+ struct link *clink = &dlink->child_link;
+ const lset *clset = dlink->child_lset;
+ long res;
+
+ if (dlink->trace)
+ printf("Link trace: Calling %s::getGraphicLimits(%p)\n",
+ dlink->child_jlif->name, clink);
+
+ res = clset->getGraphicLimits(clink, lo, hi);
+
+ if (dlink->trace) {
+ printf("Link trace: %s::getGraphicLimits(%p) returned %ld (0x%lx)\n",
+ dlink->child_jlif->name, clink, res, res);
+ if (res == 0)
+ printf(" Got: Lo = %g, Hi = %g\n", *lo, *hi);
+ }
+
+ return res;
+}
+
+static long delegate_getAlarmLimits(const struct link *plink,
+ double *lolo, double *lo, double *hi, double *hihi)
+{
+ debug_link *dlink = CONTAINER(plink->value.json.jlink,
+ struct debug_link, jlink);
+ struct link *clink = &dlink->child_link;
+ const lset *clset = dlink->child_lset;
+ long res;
+
+ if (dlink->trace)
+ printf("Link trace: Calling %s::getAlarmLimits(%p)\n",
+ dlink->child_jlif->name, clink);
+
+ res = clset->getAlarmLimits(clink, lolo, lo, hi, hihi);
+
+ if (dlink->trace) {
+ printf("Link trace: %s::getAlarmLimits(%p) returned %ld (0x%lx)\n",
+ dlink->child_jlif->name, clink, res, res);
+ if (res == 0)
+ printf(" Got: Lolo = %g, Lo = %g, Hi = %g, Hihi = %g\n",
+ *lolo, *lo, *hi, *hihi);
+ }
+
+ return res;
+}
+
+static long delegate_getPrecision(const struct link *plink, short *precision)
+{
+ debug_link *dlink = CONTAINER(plink->value.json.jlink,
+ struct debug_link, jlink);
+ struct link *clink = &dlink->child_link;
+ const lset *clset = dlink->child_lset;
+ long res;
+
+ if (dlink->trace)
+ printf("Link trace: Calling %s::getPrecision(%p)\n",
+ dlink->child_jlif->name, clink);
+
+ res = clset->getPrecision(clink, precision);
+
+ if (dlink->trace) {
+ printf("Link trace: %s::getPrecision(%p) returned %ld (0x%lx)\n",
+ dlink->child_jlif->name, clink, res, res);
+ if (res == 0)
+ printf(" Got: prec = %d\n", *precision);
+ }
+
+ return res;
+}
+
+static long delegate_getUnits(const struct link *plink, char *units, int unitsSize)
+{
+ debug_link *dlink = CONTAINER(plink->value.json.jlink,
+ struct debug_link, jlink);
+ struct link *clink = &dlink->child_link;
+ const lset *clset = dlink->child_lset;
+ long res;
+
+ if (dlink->trace)
+ printf("Link trace: Calling %s::getUnits(%p)\n",
+ dlink->child_jlif->name, clink);
+
+ res = clset->getUnits(clink, units, unitsSize);
+
+ if (dlink->trace) {
+ printf("Link trace: %s::getUnits(%p) returned %ld (0x%lx)\n",
+ dlink->child_jlif->name, clink, res, res);
+ if (res == 0)
+ printf(" Got: Units = '%s'\n", units);
+ }
+
+ return res;
+}
+
+static long delegate_getAlarm(const struct link *plink, epicsEnum16 *stat,
+ epicsEnum16 *sevr)
+{
+ debug_link *dlink = CONTAINER(plink->value.json.jlink,
+ struct debug_link, jlink);
+ struct link *clink = &dlink->child_link;
+ const lset *clset = dlink->child_lset;
+ long res;
+
+ if (dlink->trace)
+ printf("Link trace: Calling %s::getAlarm(%p)\n",
+ dlink->child_jlif->name, clink);
+
+ res = clset->getAlarm(clink, stat, sevr);
+
+ if (dlink->trace) {
+ printf("Link trace: %s::getAlarm(%p) returned %ld (0x%lx)\n",
+ dlink->child_jlif->name, clink, res, res);
+ if (res == 0)
+ printf(" Got:%s%s%s%s\n",
+ stat ? " Status = " : "",
+ stat && (*stat < ALARM_NSTATUS) ?
+ epicsAlarmConditionStrings[*stat] : "Bad-status",
+ sevr ? " Severity = " : "",
+ sevr && (*sevr < ALARM_NSEV) ?
+ epicsAlarmSeverityStrings[*sevr] : "Bad-severity"
+ );
+ }
+
+ return res;
+}
+
+static long delegate_getTimeStamp(const struct link *plink, epicsTimeStamp *pstamp)
+{
+ debug_link *dlink = CONTAINER(plink->value.json.jlink,
+ struct debug_link, jlink);
+ struct link *clink = &dlink->child_link;
+ const lset *clset = dlink->child_lset;
+ long res;
+
+ if (dlink->trace)
+ printf("Link trace: Calling %s::getTimeStamp(%p)\n",
+ dlink->child_jlif->name, clink);
+
+ res = clset->getTimeStamp(clink, pstamp);
+
+ if (dlink->trace) {
+ printf("Link trace: %s::getTimeStamp(%p) returned %ld (0x%lx)\n",
+ dlink->child_jlif->name, clink, res, res);
+ if (res == 0) {
+ char timeStr[32];
+
+ epicsTimeToStrftime(timeStr, sizeof(timeStr),
+ "%Y-%m-%d %H:%M:%S.%09f", pstamp);
+ printf(" Got: Timestamp = '%s'\n", timeStr);
+ }
+ }
+
+ return res;
+}
+
+static long delegate_putValue(struct link *plink, short dbrType,
+ const void *pbuffer, long nRequest)
+{
+ debug_link *dlink = CONTAINER(plink->value.json.jlink,
+ struct debug_link, jlink);
+ struct link *clink = &dlink->child_link;
+ const lset *clset = dlink->child_lset;
+ long res;
+
+ if (dlink->trace)
+ printf("Link trace: Calling %s::putValue(%p, %s, %p, %ld)\n",
+ dlink->child_jlif->name, clink,
+ dbGetFieldTypeString(dbrType), pbuffer, nRequest);
+
+ res = clset->putValue(clink, dbrType, pbuffer, nRequest);
+
+ if (dlink->trace)
+ printf("Link trace: %s::putValue(%p) returned %ld (0x%lx)\n",
+ dlink->child_jlif->name, clink, res, res);
+
+ return res;
+}
+
+static long delegate_putAsync(struct link *plink, short dbrType,
+ const void *pbuffer, long nRequest)
+{
+ debug_link *dlink = CONTAINER(plink->value.json.jlink,
+ struct debug_link, jlink);
+ struct link *clink = &dlink->child_link;
+ const lset *clset = dlink->child_lset;
+ long res;
+
+ if (dlink->trace)
+ printf("Link trace: Calling %s::putAsync(%p, %s, %p, %ld)\n",
+ dlink->child_jlif->name, clink,
+ dbGetFieldTypeString(dbrType), pbuffer, nRequest);
+
+ res = clset->putAsync(clink, dbrType, pbuffer, nRequest);
+
+ if (dlink->trace)
+ printf("Link trace: %s::putAsync(%p) returned %ld (0x%lx)\n",
+ dlink->child_jlif->name, clink, res, res);
+
+ return res;
+}
+
+static void delegate_scanForward(struct link *plink)
+{
+ debug_link *dlink = CONTAINER(plink->value.json.jlink,
+ struct debug_link, jlink);
+ struct link *clink = &dlink->child_link;
+ const lset *clset = dlink->child_lset;
+
+ if (dlink->trace)
+ printf("Link trace: Calling %s::scanForward(%p)\n",
+ dlink->child_jlif->name, clink);
+
+ clset->scanForward(clink);
+
+ if (dlink->trace)
+ printf("Link trace: %s::scanForward(%p) returned\n",
+ dlink->child_jlif->name, clink);
+}
+
+static long delegate_doLocked(struct link *plink, dbLinkUserCallback rtn, void *priv)
+{
+ debug_link *dlink = CONTAINER(plink->value.json.jlink,
+ struct debug_link, jlink);
+ struct link *clink = &dlink->child_link;
+ const lset *clset = dlink->child_lset;
+ long res;
+
+ if (dlink->trace)
+ printf("Link trace: Calling %s::doLocked(%p, %p, %p)\n",
+ dlink->child_jlif->name, clink, rtn, priv);
+
+ res = clset->doLocked(clink, rtn, priv);
+
+ if (dlink->trace)
+ printf("Link trace: %s::doLocked(%p) returned %ld (0x%lx)\n",
+ dlink->child_jlif->name, clink, res, res);
+
+ return res;
+}
+
+
+/************************ Debug jlif Routines ***********************/
+
+static jlink* lnkDebug_alloc(short dbfType)
+{
+ debug_link *dlink;
+
+ IFDEBUG(10)
+ printf("lnkDebug_alloc(%s)\n", dbGetFieldTypeString(dbfType));
+
+ dlink = calloc(1, sizeof(struct debug_link));
+ if (!dlink) {
+ errlogPrintf("lnkDebug: calloc() failed.\n");
+ return NULL;
+ }
+
+ dlink->dbfType = dbfType;
+
+ IFDEBUG(10)
+ printf("lnkDebug_alloc -> debug@%p\n", dlink);
+
+ return &dlink->jlink;
+}
+
+static jlink* lnkTrace_alloc(short dbfType)
+{
+ debug_link *dlink;
+
+ IFDEBUG(10)
+ printf("lnkTrace_alloc(%s)\n", dbGetFieldTypeString(dbfType));
+
+ dlink = calloc(1, sizeof(struct debug_link));
+ if (!dlink) {
+ errlogPrintf("lnkTrace: calloc() failed.\n");
+ return NULL;
+ }
+
+ dlink->dbfType = dbfType;
+ dlink->trace = 1;
+
+ IFDEBUG(10)
+ printf("lnkTrace_alloc -> debug@%p\n", dlink);
+
+ return &dlink->jlink;
+}
+
+static void lnkDebug_free(jlink *pjlink)
+{
+ debug_link *dlink = CONTAINER(pjlink, struct debug_link, jlink);
+
+ IFDEBUG(10)
+ printf("lnkDebug_free(debug@%p)\n", dlink);
+
+ dbJLinkFree(dlink->child_link.value.json.jlink);
+
+ free(dlink);
+}
+
+static jlif_key_result lnkDebug_start_map(jlink *pjlink)
+{
+ debug_link *dlink = CONTAINER(pjlink, struct debug_link, jlink);
+
+ IFDEBUG(10)
+ printf("lnkDebug_start_map(debug@%p)\n", dlink);
+
+ switch (dlink->dbfType) {
+ case DBF_INLINK:
+ return jlif_key_child_inlink;
+ case DBF_OUTLINK:
+ return jlif_key_child_outlink;
+ case DBF_FWDLINK:
+ return jlif_key_child_outlink;
+ }
+ return jlif_key_stop;
+}
+
+static jlif_result lnkDebug_end_map(jlink *pjlink)
+{
+ debug_link *dlink = CONTAINER(pjlink, struct debug_link, jlink);
+
+ IFDEBUG(10)
+ printf("lnkDebug_end_map(debug@%p)\n", dlink);
+
+ return jlif_continue;
+}
+
+static void lnkDebug_start_child(jlink *parent, jlink *child)
+{
+ debug_link *dlink = CONTAINER(parent, struct debug_link, jlink);
+ const jlif *pif = child->pif;
+ const jlif delegate_jlif = {
+ pif->name,
+ pif->alloc_jlink, /* Never used */
+ delegate_free,
+ pif->parse_null ? delegate_null : NULL,
+ pif->parse_boolean ? delegate_boolean : NULL,
+ pif->parse_integer ? delegate_integer : NULL,
+ pif->parse_double ? delegate_double : NULL,
+ pif->parse_string ? delegate_string : NULL,
+ pif->parse_start_map ? delegate_start_map : NULL,
+ pif->parse_map_key ? delegate_map_key : NULL,
+ pif->parse_end_map ? delegate_end_map : NULL,
+ pif->parse_start_array ? delegate_start_array : NULL,
+ pif->parse_end_array ? delegate_end_array : NULL,
+ pif->end_child ? delegate_end_child : NULL,
+ delegate_get_lset,
+ pif->report ? delegate_report : NULL,
+ pif->map_children ? delegate_map_children : NULL,
+ pif->start_child ? delegate_start_child : NULL
+ };
+
+ IFDEBUG(10)
+ printf("lnkDebug_start_child(debug@%p, %s@%p)\n", dlink,
+ child->pif->name, child);
+
+ dlink->child_jlif = pif;
+ memcpy(&dlink->jlif, &delegate_jlif, sizeof(jlif));
+
+ child->debug = 1;
+ child->pif = &dlink->jlif; /* Replace Child's interface table */
+
+ IFDEBUG(15)
+ printf("lnkDebug_start_child: pif %p => %p\n", pif, child->pif);
+
+ if (dlink->trace)
+ printf("Link trace: %s::alloc_jlink(%s) returned %p\n",
+ pif->name, dbGetFieldTypeString(dlink->dbfType), child);
+}
+
+static void lnkDebug_end_child(jlink *parent, jlink *child)
+{
+ debug_link *dlink = CONTAINER(parent, struct debug_link, jlink);
+ struct link *plink = &dlink->child_link;
+ const lset *plset = dlink->child_jlif->get_lset(child);
+ lset delegate_lset = {
+ plset->isConstant,
+ plset->isVolatile,
+ plset->openLink ? delegate_openLink : NULL,
+ plset->removeLink ? delegate_removeLink : NULL,
+ plset->loadScalar ? delegate_loadScalar : NULL,
+ plset->loadLS ? delegate_loadLS : NULL,
+ plset->loadArray ? delegate_loadArray : NULL,
+ plset->isConnected ? delegate_isConnected : NULL,
+ plset->getDBFtype ? delegate_getDBFtype : NULL,
+ plset->getElements ? delegate_getElements : NULL,
+ plset->getValue ? delegate_getValue : NULL,
+ plset->getControlLimits ? delegate_getControlLimits : NULL,
+ plset->getGraphicLimits ? delegate_getGraphicLimits : NULL,
+ plset->getAlarmLimits ? delegate_getAlarmLimits : NULL,
+ plset->getPrecision ? delegate_getPrecision : NULL,
+ plset->getUnits ? delegate_getUnits : NULL,
+ plset->getAlarm ? delegate_getAlarm : NULL,
+ plset->getTimeStamp ? delegate_getTimeStamp : NULL,
+ plset->putValue ? delegate_putValue : NULL,
+ plset->putAsync ? delegate_putAsync : NULL,
+ plset->scanForward ? delegate_scanForward : NULL,
+ plset->doLocked ? delegate_doLocked : NULL
+ };
+
+ IFDEBUG(10)
+ printf("lnkDebug_end_child(debug@%p, %s@%p)\n", dlink,
+ child->pif->name, child);
+
+ plink->type = JSON_LINK;
+ plink->value.json.string = NULL;
+ plink->value.json.jlink = child;
+
+ dlink->child_lset = plset;
+ memcpy(&dlink->lset, &delegate_lset, sizeof(lset));
+
+ IFDEBUG(15)
+ printf("lnkDebug_end_child: lset %p => %p\n", plset, &dlink->lset);
+}
+
+static struct lset* lnkDebug_get_lset(const jlink *pjlink)
+{
+ debug_link *dlink = CONTAINER(pjlink, struct debug_link, jlink);
+
+ IFDEBUG(10)
+ printf("lnkDebug_get_lset(debug@%p)\n", pjlink);
+
+ return &dlink->lset;
+}
+
+static void lnkDebug_report(const jlink *pjlink, int level, int indent)
+{
+ debug_link *dlink = CONTAINER(pjlink, struct debug_link, jlink);
+
+ IFDEBUG(10)
+ printf("lnkDebug_report(debug@%p)\n", dlink);
+
+ if (dlink->trace)
+ printf("%*s'trace':\n", indent, "");
+ else
+ printf("%*s'debug':\n", indent, "");
+
+ if (dlink->child_link.type == JSON_LINK) {
+ dbJLinkReport(dlink->child_link.value.json.jlink, level, indent + 2);
+ }
+}
+
+long lnkDebug_map_children(jlink *pjlink, jlink_map_fn rtn, void *ctx)
+{
+ debug_link *dlink = CONTAINER(pjlink, struct debug_link, jlink);
+
+ IFDEBUG(10)
+ printf("lnkDebug_map_children(debug@%p)\n", dlink);
+
+ if (dlink->child_link.type == JSON_LINK) {
+ return dbJLinkMapChildren(&dlink->child_link, rtn, ctx);
+ }
+ return 0;
+}
+
+
+/************************* Interface Tables *************************/
+
+static jlif lnkDebugIf = {
+ "debug", lnkDebug_alloc, lnkDebug_free,
+ NULL, NULL, NULL, NULL, NULL,
+ lnkDebug_start_map, NULL, lnkDebug_end_map,
+ NULL, NULL, lnkDebug_end_child, lnkDebug_get_lset,
+ lnkDebug_report, lnkDebug_map_children, lnkDebug_start_child
+};
+epicsExportAddress(jlif, lnkDebugIf);
+
+static jlif lnkTraceIf = {
+ "trace", lnkTrace_alloc, lnkDebug_free,
+ NULL, NULL, NULL, NULL, NULL,
+ lnkDebug_start_map, NULL, lnkDebug_end_map,
+ NULL, NULL, lnkDebug_end_child, lnkDebug_get_lset,
+ lnkDebug_report, lnkDebug_map_children, lnkDebug_start_child
+};
+epicsExportAddress(jlif, lnkTraceIf);
diff --git a/modules/database/src/std/link/lnkState.c b/modules/database/src/std/link/lnkState.c
new file mode 100644
index 000000000..b8791bd3b
--- /dev/null
+++ b/modules/database/src/std/link/lnkState.c
@@ -0,0 +1,230 @@
+/*************************************************************************\
+* Copyright (c) 2017 UChicago Argonne LLC, as Operator of Argonne
+* National Laboratory.
+* EPICS BASE is distributed subject to a Software License Agreement found
+* in file LICENSE that is included with this distribution.
+\*************************************************************************/
+/* lnkState.c */
+
+/* Usage:
+ * {state:green}
+ */
+
+#include
+#include
+#include
+#include
+
+#include "alarm.h"
+#include "dbDefs.h"
+#include "errlog.h"
+#include "epicsAssert.h"
+#include "epicsString.h"
+#include "epicsTypes.h"
+#include "dbAccessDefs.h"
+#include "dbCommon.h"
+#include "dbConvertFast.h"
+#include "dbLink.h"
+#include "dbJLink.h"
+#include "dbStaticLib.h"
+#include "dbStaticPvt.h"
+#include "dbState.h"
+#include "recGbl.h"
+#include "epicsExport.h"
+
+
+typedef long (*FASTCONVERT)();
+
+typedef struct state_link {
+ jlink jlink; /* embedded object */
+ char *name;
+ short val;
+ short invert;
+ dbStateId state;
+} state_link;
+
+static lset lnkState_lset;
+
+
+/*************************** jlif Routines **************************/
+
+static jlink* lnkState_alloc(short dbfType)
+{
+ state_link *slink;
+
+ if (dbfType == DBF_FWDLINK) {
+ errlogPrintf("lnkState: DBF_FWDLINK not supported\n");
+ return NULL;
+ }
+
+ slink = calloc(1, sizeof(struct state_link));
+ if (!slink) {
+ errlogPrintf("lnkState: calloc() failed.\n");
+ return NULL;
+ }
+
+ slink->name = NULL;
+ slink->state = NULL;
+ slink->invert = 0;
+ slink->val = 0;
+
+ return &slink->jlink;
+}
+
+static void lnkState_free(jlink *pjlink)
+{
+ state_link *slink = CONTAINER(pjlink, struct state_link, jlink);
+
+ free(slink->name);
+ free(slink);
+}
+
+static jlif_result lnkState_string(jlink *pjlink, const char *val, size_t len)
+{
+ state_link *slink = CONTAINER(pjlink, struct state_link, jlink);
+
+ if (len > 1 && val[0] == '!') {
+ slink->invert = 1;
+ val++; len--;
+ }
+
+ slink->name = epicsStrnDup(val, len);
+ return jlif_continue;
+}
+
+static struct lset* lnkState_get_lset(const jlink *pjlink)
+{
+ return &lnkState_lset;
+}
+
+static void lnkState_report(const jlink *pjlink, int level, int indent)
+{
+ state_link *slink = CONTAINER(pjlink, struct state_link, jlink);
+
+ printf("%*s'state': \"%s\" = %s%s\n", indent, "",
+ slink->name, slink->invert ? "! " : "", slink->val ? "TRUE" : "FALSE");
+}
+
+/*************************** lset Routines **************************/
+
+static void lnkState_open(struct link *plink)
+{
+ state_link *slink = CONTAINER(plink->value.json.jlink,
+ struct state_link, jlink);
+
+ slink->state = dbStateCreate(slink->name);
+}
+
+static void lnkState_remove(struct dbLocker *locker, struct link *plink)
+{
+ state_link *slink = CONTAINER(plink->value.json.jlink,
+ struct state_link, jlink);
+
+ free(slink->name);
+ free(slink);
+
+ plink->value.json.jlink = NULL;
+}
+
+static int lnkState_getDBFtype(const struct link *plink)
+{
+ return DBF_SHORT;
+}
+
+static long lnkState_getElements(const struct link *plink, long *nelements)
+{
+ *nelements = 1;
+ return 0;
+}
+
+static long lnkState_getValue(struct link *plink, short dbrType, void *pbuffer,
+ long *pnRequest)
+{
+ state_link *slink = CONTAINER(plink->value.json.jlink,
+ struct state_link, jlink);
+ FASTCONVERT conv = dbFastPutConvertRoutine[DBR_SHORT][dbrType];
+
+ slink->val = slink->invert ^ dbStateGet(slink->state);
+ return conv(&slink->val, pbuffer, NULL);
+}
+
+static long lnkState_putValue(struct link *plink, short dbrType,
+ const void *pbuffer, long nRequest)
+{
+ state_link *slink = CONTAINER(plink->value.json.jlink,
+ struct state_link, jlink);
+ short val;
+ const char *pstr;
+
+ if (nRequest == 0)
+ return 0;
+
+ switch(dbrType) {
+ case DBR_CHAR:
+ case DBR_UCHAR:
+ val = !! *(const epicsInt8 *) pbuffer;
+ break;
+
+ case DBR_SHORT:
+ case DBR_USHORT:
+ val = !! *(const epicsInt16 *) pbuffer;
+ break;
+
+ case DBR_LONG:
+ case DBR_ULONG:
+ val = !! *(const epicsInt32 *) pbuffer;
+ break;
+
+ case DBR_INT64:
+ case DBR_UINT64:
+ val = !! *(const epicsInt64 *) pbuffer;
+ break;
+
+ case DBR_FLOAT:
+ val = !! *(const epicsFloat32 *) pbuffer;
+ break;
+
+ case DBR_DOUBLE:
+ val = !! *(const epicsFloat64 *) pbuffer;
+ break;
+
+ case DBR_STRING: /* Only "" and "0" are FALSE */
+ pstr = (const char *) pbuffer;
+ val = (pstr[0] != 0) && ((pstr[0] != '0') || (pstr[1] != 0));
+ break;
+
+ default:
+ return S_db_badDbrtype;
+ }
+ slink->val = val;
+
+ val ^= slink->invert;
+ (val ? dbStateSet : dbStateClear)(slink->state);
+
+ return 0;
+}
+
+/************************* Interface Tables *************************/
+
+static lset lnkState_lset = {
+ 0, 0, /* not constant, always connected */
+ lnkState_open, lnkState_remove,
+ NULL, NULL, NULL,
+ NULL, lnkState_getDBFtype, lnkState_getElements,
+ lnkState_getValue,
+ NULL, NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ lnkState_putValue, NULL,
+ NULL, NULL
+};
+
+static jlif lnkStateIf = {
+ "state", lnkState_alloc, lnkState_free,
+ NULL, NULL, NULL, NULL, lnkState_string,
+ NULL, NULL, NULL,
+ NULL, NULL,
+ NULL, lnkState_get_lset,
+ lnkState_report, NULL, NULL
+};
+epicsExportAddress(jlif, lnkStateIf);
diff --git a/modules/database/src/std/rec/aaiRecord.c b/modules/database/src/std/rec/aaiRecord.c
index 230c060c4..183769c2e 100644
--- a/modules/database/src/std/rec/aaiRecord.c
+++ b/modules/database/src/std/rec/aaiRecord.c
@@ -2,7 +2,7 @@
* Copyright (c) 2002 Southeastern Universities Research Association, as
* Operator of Thomas Jefferson National Accelerator Facility.
* EPICS BASE is distributed subject to a Software License Agreement found
-* in file LICENSE that is included with this distribution.
+* in file LICENSE that is included with this distribution.
\*************************************************************************/
/* recAai.c */
@@ -11,7 +11,7 @@
* Original Author: Dave Barker
*
* C E B A F
- *
+ *
* Continuous Electron Beam Accelerator Facility
* Newport News, Virginia, USA.
*
@@ -225,10 +225,14 @@ static long get_array_info(DBADDR *paddr, long *no_elements, long *offset)
static long put_array_info(DBADDR *paddr, long nNew)
{
aaiRecord *prec = (aaiRecord *)paddr->precord;
+ epicsUInt32 nord = prec->nord;
prec->nord = nNew;
if (prec->nord > prec->nelm)
prec->nord = prec->nelm;
+
+ if (nord != prec->nord)
+ db_post_events(prec, &prec->nord, DBE_VALUE | DBE_LOG);
return 0;
}
@@ -241,7 +245,7 @@ static long get_units(DBADDR *paddr, char *units)
switch (dbGetFieldIndex(paddr)) {
case indexof(VAL):
if (prec->ftvl == DBF_STRING || prec->ftvl == DBF_ENUM)
- break;
+ break;
case indexof(HOPR):
case indexof(LOPR):
strncpy(units,prec->egu,DB_UNITS_SIZE);
@@ -336,33 +340,22 @@ static void monitor(aaiRecord *prec)
static long readValue(aaiRecord *prec)
{
struct aaidset *pdset = (struct aaidset *) prec->dset;
- long status = 0;
+ long status;
- if (!prec->pact) {
- status = recGblGetSimm((dbCommon *)prec, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml);
- if (status) return status;
- }
+ /* NB: Device support must post updates to NORD */
- switch (prec->simm) {
- case menuYesNoNO:
- status = pdset->read_aai(prec);
- break;
+ if (prec->pact)
+ goto do_read;
- case menuYesNoYES: {
+ status = recGblGetSimm((dbCommon *)prec, &prec->sscn, &prec->oldsimm,
+ &prec->simm, &prec->siml);
+ if (status)
+ return status;
+
+ if (prec->simm == menuYesNoYES) {
recGblSetSevr(prec, SIMM_ALARM, prec->sims);
- if (prec->pact || (prec->sdly < 0.)) {
- /* Device suport is responsible for buffer
- which might be read-only so we may not be
- allowed to call dbGetLink on it.
- Maybe also device support has an advanced
- simulation mode.
- Thus call device now.
- Reading through SIOL is handled in Soft Channel Device Support
- */
- status = pdset->read_aai(prec);
- prec->pact = FALSE;
- } else { /* !prec->pact && delay >= 0. */
+ if (prec->sdly >= 0) {
CALLBACK *pvt = prec->simpvt;
if (!pvt) {
pvt = calloc(1, sizeof(CALLBACK)); /* very lazy allocation of callback structure */
@@ -370,14 +363,14 @@ static long readValue(aaiRecord *prec)
}
if (pvt) callbackRequestProcessCallbackDelayed(pvt, prec->prio, prec, prec->sdly);
prec->pact = TRUE;
+ return 0;
}
- break;
}
-
- default:
+ else if (prec->simm != menuYesNoNO) {
recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM);
- status = -1;
+ return -1;
}
- return status;
+do_read:
+ return pdset->read_aai(prec);
}
diff --git a/modules/database/src/std/rec/aaoRecord.c b/modules/database/src/std/rec/aaoRecord.c
index 68a76a66a..b71750052 100644
--- a/modules/database/src/std/rec/aaoRecord.c
+++ b/modules/database/src/std/rec/aaoRecord.c
@@ -2,7 +2,7 @@
* Copyright (c) 2002 Southeastern Universities Research Association, as
* Operator of Thomas Jefferson National Accelerator Facility.
* EPICS BASE is distributed subject to a Software License Agreement found
-* in file LICENSE that is included with this distribution.
+* in file LICENSE that is included with this distribution.
\*************************************************************************/
/* recAao.c */
@@ -11,7 +11,7 @@
* Original Author: Dave Barker
*
* C E B A F
- *
+ *
* Continuous Electron Beam Accelerator Facility
* Newport News, Virginia, USA.
*
@@ -225,10 +225,14 @@ static long get_array_info(DBADDR *paddr, long *no_elements, long *offset)
static long put_array_info(DBADDR *paddr, long nNew)
{
aaoRecord *prec = (aaoRecord *)paddr->precord;
+ epicsUInt32 nord = prec->nord;
prec->nord = nNew;
if (prec->nord > prec->nelm)
prec->nord = prec->nelm;
+
+ if (nord != prec->nord)
+ db_post_events(prec, &prec->nord, DBE_VALUE | DBE_LOG);
return 0;
}
@@ -241,7 +245,7 @@ static long get_units(DBADDR *paddr, char *units)
switch (dbGetFieldIndex(paddr)) {
case indexof(VAL):
if (prec->ftvl == DBF_STRING || prec->ftvl == DBF_ENUM)
- break;
+ break;
case indexof(HOPR):
case indexof(LOPR):
strncpy(units,prec->egu,DB_UNITS_SIZE);
diff --git a/modules/database/src/std/rec/compressRecord.c b/modules/database/src/std/rec/compressRecord.c
index b123dcf09..e1164d81f 100644
--- a/modules/database/src/std/rec/compressRecord.c
+++ b/modules/database/src/std/rec/compressRecord.c
@@ -4,12 +4,12 @@
* Copyright (c) 2002 The Regents of the University of California, as
* Operator of Los Alamos National Laboratory.
* EPICS BASE is distributed subject to a Software License Agreement found
-* in file LICENSE that is included with this distribution.
+* in file LICENSE that is included with this distribution.
\*************************************************************************/
/*
* Original Author: Bob Dalesio
- * Date: 7-14-89
+ * Date: 7-14-89
*/
#include
@@ -439,12 +439,16 @@ static long get_array_info(DBADDR *paddr, long *no_elements, long *offset)
static long put_array_info(DBADDR *paddr, long nNew)
{
compressRecord *prec = (compressRecord *) paddr->precord;
+ epicsUInt32 nuse = prec->nuse;
if (prec->balg == bufferingALG_FIFO)
prec->off = (prec->off + nNew) % prec->nsam;
prec->nuse += nNew;
if (prec->nuse > prec->nsam)
prec->nuse = prec->nsam;
+
+ if (nuse != prec->nuse)
+ db_post_events(prec, &prec->nuse, DBE_VALUE | DBE_LOG);
return 0;
}
diff --git a/modules/database/src/std/rec/subArrayRecord.c b/modules/database/src/std/rec/subArrayRecord.c
index d9d1c33e1..6de514766 100644
--- a/modules/database/src/std/rec/subArrayRecord.c
+++ b/modules/database/src/std/rec/subArrayRecord.c
@@ -2,17 +2,17 @@
* Copyright (c) 2002 Lawrence Berkeley Laboratory,The Control Systems
* Group, Systems Engineering Department
* EPICS BASE is distributed subject to a Software License Agreement found
-* in file LICENSE that is included with this distribution.
+* in file LICENSE that is included with this distribution.
\*************************************************************************/
-/* recSubArray.c - Record Support Routines for SubArray records
+/* recSubArray.c - Record Support Routines for SubArray records
*
*
* Author: Carl Lionberger
* Date: 090293
*
* NOTES:
- * Derived from waveform record.
+ * Derived from waveform record.
* Modification Log:
* -----------------
*/
@@ -126,7 +126,7 @@ static long init_record(struct dbCommon *pcommon, int pass)
}
if (pdset->init_record)
- return (*pdset->init_record)(prec);
+ return pdset->init_record(prec);
return 0;
}
@@ -194,11 +194,14 @@ static long get_array_info(DBADDR *paddr, long *no_elements, long *offset)
static long put_array_info(DBADDR *paddr, long nNew)
{
subArrayRecord *prec = (subArrayRecord *) paddr->precord;
+ epicsUInt32 nord = prec->nord;
- if (nNew > prec->malm)
- nNew = prec->malm;
prec->nord = nNew;
+ if (prec->nord > prec->malm)
+ prec->nord = prec->malm;
+ if (nord != prec->nord)
+ db_post_events(prec, &prec->nord, DBE_VALUE | DBE_LOG);
return 0;
}
@@ -211,7 +214,7 @@ static long get_units(DBADDR *paddr, char *units)
switch (dbGetFieldIndex(paddr)) {
case indexof(VAL):
if (prec->ftvl == DBF_STRING || prec->ftvl == DBF_ENUM)
- break;
+ break;
case indexof(HOPR):
case indexof(LOPR):
strncpy(units,prec->egu,DB_UNITS_SIZE);
@@ -321,4 +324,3 @@ static long readValue(subArrayRecord *prec)
return status;
}
-
diff --git a/modules/database/src/std/rec/waveformRecord.c b/modules/database/src/std/rec/waveformRecord.c
index c987cfbc4..ece675b27 100644
--- a/modules/database/src/std/rec/waveformRecord.c
+++ b/modules/database/src/std/rec/waveformRecord.c
@@ -4,7 +4,7 @@
* Copyright (c) 2002 The Regents of the University of California, as
* Operator of Los Alamos National Laboratory.
* EPICS BASE is distributed subject to a Software License Agreement found
-* in file LICENSE that is included with this distribution.
+* in file LICENSE that is included with this distribution.
\*************************************************************************/
/* recWaveform.c - Record Support Routines for Waveform records */
@@ -205,12 +205,14 @@ static long get_array_info(DBADDR *paddr, long *no_elements, long *offset)
static long put_array_info(DBADDR *paddr, long nNew)
{
waveformRecord *prec = (waveformRecord *) paddr->precord;
+ epicsUInt32 nord = prec->nord;
prec->nord = nNew;
if (prec->nord > prec->nelm)
prec->nord = prec->nelm;
- db_post_events(prec, &prec->nord, DBE_VALUE | DBE_LOG);
+ if (nord != prec->nord)
+ db_post_events(prec, &prec->nord, DBE_VALUE | DBE_LOG);
return 0;
}
@@ -223,7 +225,7 @@ static long get_units(DBADDR *paddr, char *units)
switch (dbGetFieldIndex(paddr)) {
case indexof(VAL):
if (prec->ftvl == DBF_STRING || prec->ftvl == DBF_ENUM)
- break;
+ break;
case indexof(HOPR):
case indexof(LOPR):
strncpy(units,prec->egu,DB_UNITS_SIZE);
@@ -346,15 +348,18 @@ static long readValue(waveformRecord *prec)
case menuYesNoYES: {
long nRequest = prec->nelm;
+ epicsUInt32 nord = prec->nord;
recGblSetSevr(prec, SIMM_ALARM, prec->sims);
if (prec->pact || (prec->sdly < 0.)) {
status = dbGetLink(&prec->siol, prec->ftvl, prec->bptr, 0, &nRequest);
- if (status == 0) prec->udf = FALSE;
- /* nord set only for db links: needed for old db_access */
+ if (status == 0)
+ prec->udf = FALSE;
+
if (!dbLinkIsConstant(&prec->siol)) {
prec->nord = nRequest;
- db_post_events(prec, &prec->nord, DBE_VALUE | DBE_LOG);
+ if (nord != prec->nord)
+ db_post_events(prec, &prec->nord, DBE_VALUE | DBE_LOG);
}
prec->pact = FALSE;
} else { /* !prec->pact && delay >= 0. */
diff --git a/modules/database/src/std/softIoc/makeInstallDir.pl b/modules/database/src/std/softIoc/makeInstallDir.pl
index 61f271f15..b090280eb 100644
--- a/modules/database/src/std/softIoc/makeInstallDir.pl
+++ b/modules/database/src/std/softIoc/makeInstallDir.pl
@@ -1,5 +1,4 @@
-eval 'exec perl -S $0 ${1+"$@"}' # -*- Mode: perl -*-
- if $running_under_some_shell;
+#!/usr/bin/env perl
#*************************************************************************
# Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne
# National Laboratory.
diff --git a/modules/database/test/ioc/db/Makefile b/modules/database/test/ioc/db/Makefile
index 840fd7394..e1f06cd3b 100644
--- a/modules/database/test/ioc/db/Makefile
+++ b/modules/database/test/ioc/db/Makefile
@@ -180,6 +180,8 @@ testHarness_SRCS += epicsRunDbTests.c
dbTestHarness_SRCS += $(testHarness_SRCS)
dbTestHarness_SRCS_RTEMS += rtemsTestHarness.c
+PROD_SRCS_RTEMS += rtemsTestData.c
+
PROD_vxWorks = dbTestHarness
PROD_RTEMS = dbTestHarness
@@ -187,6 +189,10 @@ TESTSPEC_vxWorks = dbTestHarness.munch; epicsRunDbTests
TESTSPEC_RTEMS = dbTestHarness.boot; epicsRunDbTests
TESTSCRIPTS_HOST += $(TESTS:%=%.t)
+ifneq ($(filter $(T_A),$(CROSS_COMPILER_RUNTEST_ARCHS)),)
+TESTPROD_RTEMS = $(TESTPROD_HOST)
+TESTSCRIPTS_RTEMS += $(TESTS:%=%.t)
+endif
include $(TOP)/configure/RULES
@@ -197,3 +203,6 @@ dbStressLock$(DEP): $(COMMON_DIR)/xRecord.h
devx$(DEP): $(COMMON_DIR)/xRecord.h
scanIoTest$(DEP): $(COMMON_DIR)/xRecord.h
xRecord$(DEP): $(COMMON_DIR)/xRecord.h
+
+rtemsTestData.c : $(TESTFILES) $(TOOLS)/epicsMakeMemFs.pl
+ $(PERL) $(TOOLS)/epicsMakeMemFs.pl $@ epicsRtemsFSImage $(TESTFILES)
diff --git a/modules/database/test/ioc/db/dbBadLink.db b/modules/database/test/ioc/db/dbBadLink.db
index 6c41e854b..b38d2ccef 100644
--- a/modules/database/test/ioc/db/dbBadLink.db
+++ b/modules/database/test/ioc/db/dbBadLink.db
@@ -14,3 +14,8 @@ record(x, "eINST_IO") {
field(DTYP, "Unit Test INST_IO")
field(INP, "hello")
}
+
+record(x, "eINST_IO2") {
+ field(DTYP, "Unit Test INST_IO")
+ field(INP, "RL @no")
+}
diff --git a/modules/database/test/ioc/db/dbPutLinkTest.c b/modules/database/test/ioc/db/dbPutLinkTest.c
index d6748a9eb..179b913ce 100644
--- a/modules/database/test/ioc/db/dbPutLinkTest.c
+++ b/modules/database/test/ioc/db/dbPutLinkTest.c
@@ -60,7 +60,9 @@ static const struct testParseDataT {
TEST_PV_LINK(" world MSICP", "world", pvlOptMSI|pvlOptCP),
{"#C14 S145 @testing", {VME_IO, "testing", 0, "CS", {14, 145}}},
+ {"#C14 S145", {VME_IO, "", 0, "CS", {14, 145}}},
{"#B11 C12 N13 A14 F15 @cparam", {CAMAC_IO, "cparam", 0, "BCNAF", {11, 12, 13, 14, 15}}},
+ {"#B11 C12 N13 A14 F15", {CAMAC_IO, "", 0, "BCNAF", {11, 12, 13, 14, 15}}},
{" #B111 C112 N113 @cparam", {CAMAC_IO, "cparam", 0, "BCN", {111, 112, 113}}},
{" @hello world ", {INST_IO, "hello world", 0, "", /*{}*/}},
{" {\"x\":true} ", {JSON_LINK, "{\"x\":true}", 0, "", /*{}*/}},
@@ -87,11 +89,15 @@ static void testLinkParse(void)
for (;td->str; td++) {
int i, N;
testDiag("Parsing \"%s\"", td->str);
- testOk(dbParseLink(td->str, DBF_INLINK, &info, 0) == 0, "Parser returned OK");
+ testOk(dbParseLink(td->str, DBF_INLINK, &info) == 0, "Parser returned OK");
if (!testOk(info.ltype == td->info.ltype, "Link type value"))
testDiag("Expected %d, got %d", td->info.ltype, info.ltype);
- if (td->info.target)
+ if (td->info.target && info.target)
testStrcmp(0, info.target, td->info.target);
+ else if(!!td->info.target ^ !!info.target)
+ testFail("info target NULL mis-match %s %s", info.target, td->info.target);
+ else
+ testPass("info target NULL as expected");
if (info.ltype == td->info.ltype) {
switch (info.ltype) {
case PV_LINK:
@@ -125,7 +131,6 @@ static const char *testParseFailData[] = {
"#A0 B @",
"#A0 B C @",
"#R1 M2 D3 E4 @oops", /* RF_IO has no parm */
- "#C1 S2", /* VME_IO needs parm */
NULL
};
@@ -147,7 +152,7 @@ static void testLinkFailParse(void)
eltc(1);
for(;*td; td++) {
- testOk(dbParseLink(*td, DBF_INLINK, &info, 0) == S_dbLib_badField,
+ testOk(dbParseLink(*td, DBF_INLINK, &info) == S_dbLib_badField,
"dbParseLink correctly rejected \"%s\"", *td);
}
@@ -480,6 +485,13 @@ static void testLinkInitFail(void)
testOk1(plink->type == INST_IO);
testOk1(plink->value.instio.string != NULL);
+ testdbGetFieldEqual("eINST_IO2.INP", DBR_STRING, "@");
+
+ prec = (xRecord *) testdbRecordPtr("eINST_IO2");
+ plink = &prec->inp;
+ testOk1(plink->type == INST_IO);
+ testOk1(plink->value.instio.string != NULL);
+
testIocShutdownOk();
testdbCleanup();
@@ -684,7 +696,7 @@ void testTSEL(void)
MAIN(dbPutLinkTest)
{
- testPlan(320);
+ testPlan(337);
testLinkParse();
testLinkFailParse();
testCADBSet();
diff --git a/modules/database/test/ioc/db/dbStressLock.c b/modules/database/test/ioc/db/dbStressLock.c
index aed0116e0..827464840 100644
--- a/modules/database/test/ioc/db/dbStressLock.c
+++ b/modules/database/test/ioc/db/dbStressLock.c
@@ -219,6 +219,11 @@ MAIN(dbStressTest)
testPlan(80+nworkers*3);
+#if defined(__rtems__)
+ testSkip(80+nworkers*3, "Test assumes time sliced preempting scheduling");
+ return testDone();
+#endif
+
priv = callocMustSucceed(nworkers, sizeof(*priv), "no memory");
testDiag("lock set stress test");
diff --git a/modules/database/test/ioc/db/jlinkz.c b/modules/database/test/ioc/db/jlinkz.c
index dd6919ffb..fc98ff3b3 100644
--- a/modules/database/test/ioc/db/jlinkz.c
+++ b/modules/database/test/ioc/db/jlinkz.c
@@ -246,7 +246,8 @@ static jlif jlifZ = {
NULL, /* end child */
&z_lset,
NULL, /* report */
- NULL /* map child */
+ NULL, /* map child */
+ NULL /* start child */
};
epicsExportAddress(jlif, jlifZ);
diff --git a/modules/database/test/ioc/db/xLink.c b/modules/database/test/ioc/db/xLink.c
index 12175f44e..2d7c3c043 100644
--- a/modules/database/test/ioc/db/xLink.c
+++ b/modules/database/test/ioc/db/xLink.c
@@ -2,7 +2,7 @@
* Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne
* National Laboratory.
* EPICS BASE is distributed subject to a Software License Agreement found
-* in file LICENSE that is included with this distribution.
+* in file LICENSE that is included with this distribution.
\*************************************************************************/
/* xLink.c */
@@ -82,7 +82,6 @@ static jlif xlinkIf = {
NULL, NULL, NULL,
NULL, NULL,
NULL, xlink_get_lset,
- NULL, NULL
+ NULL, NULL, NULL
};
epicsExportAddress(jlif, xlinkIf);
-
diff --git a/modules/database/test/ioc/dbtemplate/msi.plt b/modules/database/test/ioc/dbtemplate/msi.plt
index ab49fd9cd..54cc56aa2 100644
--- a/modules/database/test/ioc/dbtemplate/msi.plt
+++ b/modules/database/test/ioc/dbtemplate/msi.plt
@@ -31,18 +31,18 @@ ok(msi('-S ../t5-substitute.txt ../t5-template.txt'), slurp('../t5-result.txt'))
# Substitution file, pattern format
ok(msi('-S../t6-substitute.txt ../t6-template.txt'), slurp('../t6-result.txt'));
-# Output option -o
+# Output option -o and verbose option -V
my $out = 't7-output.txt';
my $count = 5; # Try up to 5 times...
my $result;
do {
unlink $out;
- msi("-I.. -o $out ../t1-template.txt");
+ msi("-I.. -V -o $out ../t1-template.txt");
$result = slurp($out);
print "# msi output file empty, retrying\n"
if $result eq '';
} while ($result eq '') && (--$count > 0);
-ok($result, slurp('../t1-result.txt'));
+ok($result, slurp('../t7-result.txt'));
# Dependency generation, include/substitute model
ok(msi('-I.. -D -o t8.txt ../t1-template.txt'), slurp('../t8-result.txt'));
diff --git a/modules/database/test/ioc/dbtemplate/t1-result.txt b/modules/database/test/ioc/dbtemplate/t1-result.txt
index fcd065585..abe94ebf7 100644
--- a/modules/database/test/ioc/dbtemplate/t1-result.txt
+++ b/modules/database/test/ioc/dbtemplate/t1-result.txt
@@ -1,6 +1,6 @@
This is t1-template.txt
-With $(a,undefined) & $(b,undefined):
+With $(a) & $(b):
This is t1-include.txt
a = default value used when a is undefined
b = default value used when b is undefined
diff --git a/modules/database/test/ioc/dbtemplate/t2-result.txt b/modules/database/test/ioc/dbtemplate/t2-result.txt
index 5239e6a05..123d2d92f 100644
--- a/modules/database/test/ioc/dbtemplate/t2-result.txt
+++ b/modules/database/test/ioc/dbtemplate/t2-result.txt
@@ -1,6 +1,6 @@
-a = va1-a b = def-b c = def-c d = $(d,undefined)
-a = va2-a b = va2-b c = def-c d = $(d,undefined)
-a = va3-a b = va3-b c = va3-c d = $(d,undefined)
-a = va4-a b = va4-b c = def-c d = $(d,undefined)
-a = va5-a b = def-b c = def-c d = $(d,undefined)
-a = pt3-a b = pt3-b c = pt3-c d = $(d,undefined)
+a = va1-a b = def-b c = def-c d = $(d)
+a = va2-a b = va2-b c = def-c d = $(d)
+a = va3-a b = va3-b c = va3-c d = $(d)
+a = va4-a b = va4-b c = def-c d = $(d)
+a = va5-a b = def-b c = def-c d = $(d)
+a = pt3-a b = pt3-b c = pt3-c d = $(d)
diff --git a/modules/database/test/ioc/dbtemplate/t3-result.txt b/modules/database/test/ioc/dbtemplate/t3-result.txt
index c6961507f..d7823e541 100644
--- a/modules/database/test/ioc/dbtemplate/t3-result.txt
+++ b/modules/database/test/ioc/dbtemplate/t3-result.txt
@@ -1,28 +1,28 @@
-a = gb1-a b = gb1-b c = def-c d = $(d,undefined)
-a = va1-a b = gb1-b c = def-c d = $(d,undefined)
-a = va2-a b = va2-b c = def-c d = $(d,undefined)
-a = va3-a b = va3-b c = va3-c d = $(d,undefined)
-a = va4-a b = va4-b c = def-c d = $(d,undefined)
-a = va5-a b = gb1-b c = def-c d = $(d,undefined)
-a = gb1-a b = gb1-b c = def-c d = $(d,undefined)
-a = gb2-a b = gb2-b c = def-c d = $(d,undefined)
-a = va1-a b = gb2-b c = def-c d = $(d,undefined)
-a = va2-a b = va2-b c = def-c d = $(d,undefined)
-a = va3-a b = va3-b c = va3-c d = $(d,undefined)
-a = va4-a b = va4-b c = def-c d = $(d,undefined)
-a = va5-a b = gb2-b c = def-c d = $(d,undefined)
-a = gb2-a b = gb2-b c = def-c d = $(d,undefined)
-a = gb3-a b = gb3-b c = def-c d = $(d,undefined)
-a = pt1-a b = gb3-b c = def-c d = $(d,undefined)
-a = pt2-a b = pt2-b c = def-c d = $(d,undefined)
-a = pt3-a b = pt3-b c = pt3-c d = $(d,undefined)
-a = pt4-a b = pt4-b c = def-c d = $(d,undefined)
-a = pt5-a b = gb3-b c = def-c d = $(d,undefined)
-a = gb3-a b = gb3-b c = def-c d = $(d,undefined)
-a = gb4-a b = gb4-b c = def-c d = $(d,undefined)
-a = pt1-a b = gb4-b c = def-c d = $(d,undefined)
-a = pt2-a b = pt2-b c = def-c d = $(d,undefined)
-a = pt3-a b = pt3-b c = pt3-c d = $(d,undefined)
-a = pt4-a b = pt4-b c = def-c d = $(d,undefined)
-a = pt5-a b = gb4-b c = def-c d = $(d,undefined)
-a = gb4-a b = gb4-b c = def-c d = $(d,undefined)
+a = gb1-a b = gb1-b c = def-c d = $(d)
+a = va1-a b = gb1-b c = def-c d = $(d)
+a = va2-a b = va2-b c = def-c d = $(d)
+a = va3-a b = va3-b c = va3-c d = $(d)
+a = va4-a b = va4-b c = def-c d = $(d)
+a = va5-a b = gb1-b c = def-c d = $(d)
+a = gb1-a b = gb1-b c = def-c d = $(d)
+a = gb2-a b = gb2-b c = def-c d = $(d)
+a = va1-a b = gb2-b c = def-c d = $(d)
+a = va2-a b = va2-b c = def-c d = $(d)
+a = va3-a b = va3-b c = va3-c d = $(d)
+a = va4-a b = va4-b c = def-c d = $(d)
+a = va5-a b = gb2-b c = def-c d = $(d)
+a = gb2-a b = gb2-b c = def-c d = $(d)
+a = gb3-a b = gb3-b c = def-c d = $(d)
+a = pt1-a b = gb3-b c = def-c d = $(d)
+a = pt2-a b = pt2-b c = def-c d = $(d)
+a = pt3-a b = pt3-b c = pt3-c d = $(d)
+a = pt4-a b = pt4-b c = def-c d = $(d)
+a = pt5-a b = gb3-b c = def-c d = $(d)
+a = gb3-a b = gb3-b c = def-c d = $(d)
+a = gb4-a b = gb4-b c = def-c d = $(d)
+a = pt1-a b = gb4-b c = def-c d = $(d)
+a = pt2-a b = pt2-b c = def-c d = $(d)
+a = pt3-a b = pt3-b c = pt3-c d = $(d)
+a = pt4-a b = pt4-b c = def-c d = $(d)
+a = pt5-a b = gb4-b c = def-c d = $(d)
+a = gb4-a b = gb4-b c = def-c d = $(d)
diff --git a/modules/database/test/ioc/dbtemplate/t4-result.txt b/modules/database/test/ioc/dbtemplate/t4-result.txt
index 664872013..6ee8aa0f3 100644
--- a/modules/database/test/ioc/dbtemplate/t4-result.txt
+++ b/modules/database/test/ioc/dbtemplate/t4-result.txt
@@ -1,6 +1,6 @@
-a = va1-a b = def-b c = def-c d = $(d,undefined)
-a = va2-a b = va2-b c = def-c d = $(d,undefined)
-a = va3-a b = va3-b c = va3-c d = $(d,undefined)
-a = va4-a b = va4-b c = va3-c d = $(d,undefined)
-a = va5-a b = va4-b c = va3-c d = $(d,undefined)
-a = pt3-a b = pt3-b c = pt3-c d = $(d,undefined)
+a = va1-a b = def-b c = def-c d = $(d)
+a = va2-a b = va2-b c = def-c d = $(d)
+a = va3-a b = va3-b c = va3-c d = $(d)
+a = va4-a b = va4-b c = va3-c d = $(d)
+a = va5-a b = va4-b c = va3-c d = $(d)
+a = pt3-a b = pt3-b c = pt3-c d = $(d)
diff --git a/modules/database/test/ioc/dbtemplate/t5-result.txt b/modules/database/test/ioc/dbtemplate/t5-result.txt
index 19a57c87c..2ae214948 100644
--- a/modules/database/test/ioc/dbtemplate/t5-result.txt
+++ b/modules/database/test/ioc/dbtemplate/t5-result.txt
@@ -2,19 +2,19 @@
a = 111
b = 222
c = xx
-d = $(d,undefined)
+d = $(d)
# comment line
a = aaa
b = bbb
c = ccc
-d = $(d,undefined)
+d = $(d)
# comment line
a = AA
b = BB
c = xx
-d = $(d,undefined)
+d = $(d)
# comment line
a = aaa
b = bbb
c = yy
-d = $(d,undefined)
+d = $(d)
diff --git a/modules/database/test/ioc/dbtemplate/t6-result.txt b/modules/database/test/ioc/dbtemplate/t6-result.txt
index 19a57c87c..2ae214948 100644
--- a/modules/database/test/ioc/dbtemplate/t6-result.txt
+++ b/modules/database/test/ioc/dbtemplate/t6-result.txt
@@ -2,19 +2,19 @@
a = 111
b = 222
c = xx
-d = $(d,undefined)
+d = $(d)
# comment line
a = aaa
b = bbb
c = ccc
-d = $(d,undefined)
+d = $(d)
# comment line
a = AA
b = BB
c = xx
-d = $(d,undefined)
+d = $(d)
# comment line
a = aaa
b = bbb
c = yy
-d = $(d,undefined)
+d = $(d)
diff --git a/modules/database/test/ioc/dbtemplate/t7-result.txt b/modules/database/test/ioc/dbtemplate/t7-result.txt
new file mode 100644
index 000000000..fcd065585
--- /dev/null
+++ b/modules/database/test/ioc/dbtemplate/t7-result.txt
@@ -0,0 +1,21 @@
+This is t1-template.txt
+
+With $(a,undefined) & $(b,undefined):
+This is t1-include.txt
+ a = default value used when a is undefined
+ b = default value used when b is undefined
+End of t1-include.txt
+
+On defining a=aaa & b=bbb:
+This is t1-include.txt again
+ a = aaa
+ b = bbb
+End of t1-include.txt
+
+On setting a="aa":
+This is t1-include.txt again
+ a = "aa"
+ b = bbb
+End of t1-include.txt
+
+End of t1-template.txt
diff --git a/modules/database/test/std/filters/Makefile b/modules/database/test/std/filters/Makefile
index 540a4d76c..718b8d523 100644
--- a/modules/database/test/std/filters/Makefile
+++ b/modules/database/test/std/filters/Makefile
@@ -63,6 +63,8 @@ testHarness_SRCS += epicsRunFilterTests.c
filterTestHarness_SRCS += $(testHarness_SRCS)
filterTestHarness_SRCS_RTEMS += rtemsTestHarness.c
+PROD_SRCS_RTEMS += rtemsTestData.c
+
PROD_vxWorks = filterTestHarness
PROD_RTEMS = filterTestHarness
@@ -70,6 +72,10 @@ TESTSPEC_vxWorks = filterTestHarness.munch; epicsRunFilterTests
TESTSPEC_RTEMS = filterTestHarness.boot; epicsRunFilterTests
TESTSCRIPTS_HOST += $(TESTS:%=%.t)
+ifneq ($(filter $(T_A),$(CROSS_COMPILER_RUNTEST_ARCHS)),)
+TESTPROD_RTEMS = $(TESTPROD_HOST)
+TESTSCRIPTS_RTEMS += $(TESTS:%=%.t)
+endif
include $(TOP)/configure/RULES
@@ -79,3 +85,6 @@ dbndTest$(DEP): $(COMMON_DIR)/xRecord.h
syncTest$(DEP): $(COMMON_DIR)/xRecord.h
arrRecord$(DEP): $(COMMON_DIR)/arrRecord.h
arrTest$(DEP): $(COMMON_DIR)/arrRecord.h
+
+rtemsTestData.c : $(TESTFILES) $(TOOLS)/epicsMakeMemFs.pl
+ $(PERL) $(TOOLS)/epicsMakeMemFs.pl $@ epicsRtemsFSImage $(TESTFILES)
diff --git a/modules/database/test/std/link/Makefile b/modules/database/test/std/link/Makefile
new file mode 100644
index 000000000..8540cdf84
--- /dev/null
+++ b/modules/database/test/std/link/Makefile
@@ -0,0 +1,60 @@
+#*************************************************************************
+# Copyright (c) 2018 UChicago Argonne LLC, as Operator of Argonne
+# National Laboratory.
+# EPICS BASE is distributed subject to a Software License Agreement found
+# in the file LICENSE that is included with this distribution.
+#*************************************************************************
+TOP=../../..
+
+include $(TOP)/configure/CONFIG
+
+TESTLIBRARY = Recs
+
+Recs_SRCS += ioRecord.c
+Recs_LIBS += dbCore ca Com
+
+PROD_LIBS = Recs dbRecStd dbCore ca Com
+
+DBDDEPENDS_FILES += linkTest.dbd$(DEP)
+TARGETS += $(COMMON_DIR)/linkTest.dbd
+linkTest_DBD += menuGlobal.dbd
+linkTest_DBD += menuConvert.dbd
+linkTest_DBD += menuScan.dbd
+linkTest_DBD += links.dbd
+linkTest_DBD += ioRecord.dbd
+TESTFILES += $(COMMON_DIR)/linkTest.dbd
+TESTFILES += ../ioRecord.db
+
+testHarness_SRCS += linkTest_registerRecordDeviceDriver.cpp
+
+TESTPROD_HOST += lnkStateTest
+lnkStateTest_SRCS += lnkStateTest.c
+lnkStateTest_SRCS += linkTest_registerRecordDeviceDriver.cpp
+testHarness_SRCS += lnkStateTest.c
+TESTS += lnkStateTest
+
+TESTPROD_HOST += lnkCalcTest
+lnkCalcTest_SRCS += lnkCalcTest.c
+lnkCalcTest_SRCS += linkTest_registerRecordDeviceDriver.cpp
+testHarness_SRCS += lnkCalcTest.c
+TESTS += lnkCalcTest
+
+# epicsRunLinkTests runs all the test programs in a known working order.
+testHarness_SRCS += epicsRunLinkTests.c
+
+linkTestHarness_SRCS += $(testHarness_SRCS)
+linkTestHarness_SRCS_RTEMS += rtemsTestHarness.c
+
+PROD_vxWorks = linkTestHarness
+PROD_RTEMS = linkTestHarness
+
+TESTSPEC_vxWorks = linkTestHarness.munch; epicsRunLinkTests
+TESTSPEC_RTEMS = linkTestHarness.boot; epicsRunLinkTests
+
+TESTSCRIPTS_HOST += $(TESTS:%=%.t)
+
+include $(TOP)/configure/RULES
+
+ioRecord$(DEP): $(COMMON_DIR)/ioRecord.h
+lnkStateTest$(DEP): $(COMMON_DIR)/ioRecord.h
+lnkCalcTest$(DEP): $(COMMON_DIR)/ioRecord.h
diff --git a/modules/database/test/std/link/epicsRunLinkTests.c b/modules/database/test/std/link/epicsRunLinkTests.c
new file mode 100644
index 000000000..a0f3b7c72
--- /dev/null
+++ b/modules/database/test/std/link/epicsRunLinkTests.c
@@ -0,0 +1,29 @@
+/*************************************************************************\
+* Copyright (c) 2018 UChicago Argonne LLC, as Operator of Argonne
+* National Laboratory.
+* EPICS BASE is distributed subject to a Software License Agreement found
+* in the file LICENSE that is included with this distribution.
+\*************************************************************************/
+
+/*
+ * Run filter tests as a batch.
+ */
+
+#include "epicsUnitTest.h"
+#include "epicsExit.h"
+#include "dbmf.h"
+
+int lnkStateTest(void);
+int lnkCalcTest(void);
+
+void epicsRunLinkTests(void)
+{
+ testHarness();
+
+ runTest(lnkStateTest);
+ runTest(lnkCalcTest);
+
+ dbmfFreeChunks();
+
+ epicsExit(0); /* Trigger test harness */
+}
diff --git a/modules/database/test/std/link/ioRecord.c b/modules/database/test/std/link/ioRecord.c
new file mode 100644
index 000000000..1807c9171
--- /dev/null
+++ b/modules/database/test/std/link/ioRecord.c
@@ -0,0 +1,22 @@
+/*************************************************************************\
+* Copyright (c) 2018 UChicago Argonne LLC, as Operator of Argonne
+* National Laboratory.
+* EPICS BASE is distributed subject to a Software License Agreement found
+* in the file LICENSE that is included with this distribution.
+ \*************************************************************************/
+
+/*
+ * Author: Andrew Johnson
+ */
+
+#include
+#include
+
+#define GEN_SIZE_OFFSET
+#include "ioRecord.h"
+#undef GEN_SIZE_OFFSET
+
+#include
+
+static rset ioRSET;
+epicsExportAddress(rset,ioRSET);
diff --git a/modules/database/test/std/link/ioRecord.db b/modules/database/test/std/link/ioRecord.db
new file mode 100644
index 000000000..7a95a1025
--- /dev/null
+++ b/modules/database/test/std/link/ioRecord.db
@@ -0,0 +1 @@
+record(io, io) {}
diff --git a/modules/database/test/std/link/ioRecord.dbd b/modules/database/test/std/link/ioRecord.dbd
new file mode 100644
index 000000000..efaec10a0
--- /dev/null
+++ b/modules/database/test/std/link/ioRecord.dbd
@@ -0,0 +1,14 @@
+# This is a soft record type with both input and output links
+
+recordtype(io) {
+ include "dbCommon.dbd"
+ field(VAL, DBF_LONG) {
+ prompt("Value")
+ }
+ field(INPUT, DBF_INLINK) {
+ prompt("Input Link")
+ }
+ field(OUTPUT, DBF_OUTLINK) {
+ prompt("Output Link")
+ }
+}
diff --git a/modules/database/test/std/link/lnkCalcTest.c b/modules/database/test/std/link/lnkCalcTest.c
new file mode 100644
index 000000000..3fe8382d4
--- /dev/null
+++ b/modules/database/test/std/link/lnkCalcTest.c
@@ -0,0 +1,164 @@
+/*************************************************************************\
+* Copyright (c) 2018 Andrew Johnson
+* EPICS BASE is distributed subject to a Software License Agreement found
+* in the file LICENSE that is included with this distribution.
+\*************************************************************************/
+
+#include
+
+#include "dbAccess.h"
+#include "alarm.h"
+#include "dbUnitTest.h"
+#include "errlog.h"
+#include "epicsThread.h"
+#include "dbLink.h"
+#include "dbState.h"
+#include "recGbl.h"
+#include "testMain.h"
+#include "ioRecord.h"
+
+#define testPutLongStr(PV, VAL) \
+ testdbPutArrFieldOk(PV, DBF_CHAR, sizeof(VAL), VAL);
+
+void linkTest_registerRecordDeviceDriver(struct dbBase *);
+
+static void startTestIoc(const char *dbfile)
+{
+ testdbPrepare();
+ testdbReadDatabase("linkTest.dbd", NULL, NULL);
+ linkTest_registerRecordDeviceDriver(pdbbase);
+ testdbReadDatabase(dbfile, NULL, NULL);
+
+ eltc(0);
+ testIocInitOk();
+ eltc(1);
+}
+
+static void testCalc()
+{
+ ioRecord *pio;
+ DBLINK *pinp, *pout;
+ long status;
+ epicsFloat64 f64;
+
+ startTestIoc("ioRecord.db");
+
+ pio = (ioRecord *) testdbRecordPtr("io");
+ pinp = &pio->input;
+ pout = &pio->output;
+
+ testDiag("testing lnkCalc input");
+
+ {
+ dbStateId red;
+
+ testPutLongStr("io.INPUT", "{\"calc\":{"
+ "\"expr\":\"a\","
+ "\"args\":[{\"state\":\"red\"}]"
+ "}}");
+ if (testOk1(pinp->type == JSON_LINK))
+ testDiag("Link was set to '%s'", pinp->value.json.string);
+ red = dbStateFind("red");
+ testOk(!!red, "State red was created");
+
+ dbStateSet(red);
+ status = dbGetLink(pinp, DBF_DOUBLE, &f64, NULL, NULL);
+ testOk(!status, "dbGetLink succeeded (status = %ld)", status);
+ testOk(f64, "Got TRUE (%g)", f64);
+
+ dbStateClear(red);
+ status = dbGetLink(pinp, DBF_DOUBLE, &f64, NULL, NULL);
+ testOk(!status, "dbGetLink succeeded (status = %ld)", status);
+ testOk(!f64, "Got FALSE (%g)", f64);
+ }
+
+ {
+ dbStateId major = dbStateCreate("major");
+ dbStateId minor = dbStateCreate("minor");
+ epicsEnum16 stat, sevr;
+
+ testPutLongStr("io.INPUT", "{\"calc\":{"
+ "\"expr\":\"0\","
+ "\"major\":\"A\","
+ "\"minor\":\"B\","
+ "\"args\":[{\"state\":\"major\"},{\"state\":\"minor\"}]"
+ "}}");
+ if (testOk1(pinp->type == JSON_LINK))
+ testDiag("Link was set to '%s'", pinp->value.json.string);
+
+ dbStateSet(major);
+ dbStateSet(minor);
+ status = dbGetLink(pinp, DBF_DOUBLE, &f64, NULL, NULL);
+ testOk(!status, "dbGetLink succeeded (status = %ld)", status);
+ testOk(f64 == 0.0, "Got zero (%g)", f64);
+ testOk(recGblResetAlarms(pio) & DBE_ALARM, "Record alarm was raised");
+ status = dbGetAlarm(pinp, &stat, &sevr);
+ testOk(!status, "dbGetAlarm succeeded (status = %ld)", status);
+ testOk(stat == LINK_ALARM, "Alarm status = LINK (%d)", stat);
+ testOk(sevr == MAJOR_ALARM, "Alarm severity = MAJOR (%d)", sevr);
+
+ dbStateClear(major);
+ status = dbGetLink(pinp, DBF_DOUBLE, &f64, NULL, NULL);
+ testOk(!status, "dbGetLink succeeded (status = %ld)", status);
+ testOk(recGblResetAlarms(pio) & DBE_ALARM, "Record alarm was raised");
+ status = dbGetAlarm(pinp, &stat, &sevr);
+ testOk(!status, "dbGetAlarm succeeded (status = %ld)", status);
+ testOk(stat == LINK_ALARM, "Alarm status = LINK (%d)", stat);
+ testOk(sevr == MINOR_ALARM, "Alarm severity = MINOR (%d)", sevr);
+ }
+
+ testDiag("testing lnkCalc output");
+
+ {
+ dbStateId red = dbStateFind("red");
+ dbStateId out = dbStateCreate("out");
+
+ testPutLongStr("io.OUTPUT", "{\"calc\":{"
+ "\"expr\":\"!a\","
+ "\"out\":{\"state\":\"out\"},"
+ "\"args\":[{\"state\":\"red\"}],"
+ "\"units\":\"things\","
+ "\"prec\":3"
+ "}}");
+ if (testOk1(pout->type == JSON_LINK))
+ testDiag("Link was set to '%s'", pout->value.json.string);
+
+ dbStateSet(red);
+ f64 = 1.0;
+ status = dbPutLink(pout, DBF_DOUBLE, &f64, 1);
+ testOk(!status, "dbPutLink succeeded (status = %ld)", status);
+ testOk(!dbStateGet(out), "output was cleared");
+
+ dbStateClear(red);
+ status = dbPutLink(pout, DBF_DOUBLE, &f64, 1);
+ testOk(!status, "dbGetLink succeeded (status = %ld)", status);
+ testOk(dbStateGet(out), "output was set");
+ }
+
+ {
+ char units[20] = {0};
+ short prec = 0;
+
+ status = dbGetUnits(pout, units, sizeof(units));
+ testOk(!status, "dbGetUnits succeeded (status = %ld)", status);
+ testOk(!strcmp(units, "things"), "Units string correct (%s)", units);
+
+ status = dbGetPrecision(pout, &prec);
+ testOk(!status, "dbGetPrecision succeeded (status = %ld)", status);
+ testOk(prec == 3, "Precision correct (%d)", prec);
+ }
+
+ testIocShutdownOk();
+
+ testdbCleanup();
+}
+
+
+MAIN(lnkCalcTest)
+{
+ testPlan(0);
+
+ testCalc();
+
+ return testDone();
+}
diff --git a/modules/database/test/std/link/lnkStateTest.c b/modules/database/test/std/link/lnkStateTest.c
new file mode 100644
index 000000000..d3da06f5c
--- /dev/null
+++ b/modules/database/test/std/link/lnkStateTest.c
@@ -0,0 +1,130 @@
+/*************************************************************************\
+* Copyright (c) 2018 Andrew Johnson
+* EPICS BASE is distributed subject to a Software License Agreement found
+* in the file LICENSE that is included with this distribution.
+\*************************************************************************/
+
+#include
+
+#include "dbAccess.h"
+#include "alarm.h"
+#include "dbUnitTest.h"
+#include "errlog.h"
+#include "epicsThread.h"
+#include "dbLink.h"
+#include "dbState.h"
+#include "ioRecord.h"
+
+#include "testMain.h"
+
+void linkTest_registerRecordDeviceDriver(struct dbBase *);
+
+static void startTestIoc(const char *dbfile)
+{
+ testdbPrepare();
+ testdbReadDatabase("linkTest.dbd", NULL, NULL);
+ linkTest_registerRecordDeviceDriver(pdbbase);
+ testdbReadDatabase(dbfile, NULL, NULL);
+
+ eltc(0);
+ testIocInitOk();
+ eltc(1);
+}
+
+static void testState()
+{
+ dbStateId red;
+ ioRecord *pio;
+ DBLINK *pinp, *pout;
+ long status;
+
+ testDiag("testing lnkState");
+
+ startTestIoc("ioRecord.db");
+
+ pio = (ioRecord *) testdbRecordPtr("io");
+ pinp = &pio->input;
+ pout = &pio->output;
+
+ red = dbStateFind("red");
+ testOk(!red, "No state red exists");
+
+ testdbPutFieldOk("io.INPUT", DBF_STRING, "{\"state\":\"red\"}");
+ if (testOk1(pinp->type == JSON_LINK))
+ testDiag("Link was set to '%s'", pinp->value.json.string);
+ red = dbStateFind("red");
+ testOk(!!red, "state red exists");
+
+ {
+ epicsInt16 i16;
+
+ dbStateSet(red);
+ status = dbGetLink(pinp, DBF_SHORT, &i16, NULL, NULL);
+ testOk(!status, "dbGetLink succeeded (status = %ld)", status);
+ testOk(i16, "Got TRUE");
+
+ testdbPutFieldOk("io.INPUT", DBF_STRING, "{\"state\":\"!red\"}");
+ if (testOk1(pinp->type == JSON_LINK))
+ testDiag("Link was set to '%s'", pinp->value.json.string);
+
+ status = dbGetLink(pinp, DBF_SHORT, &i16, NULL, NULL);
+ testOk(!status, "dbGetLink succeeded (status = %ld)", status);
+ testOk(!i16, "Got FALSE");
+
+ testdbPutFieldOk("io.OUTPUT", DBF_STRING, "{\"state\":\"red\"}");
+ if (testOk1(pout->type == JSON_LINK))
+ testDiag("Link was set to '%s'", pout->value.json.string);
+
+ i16 = 0;
+ status = dbPutLink(pout, DBF_SHORT, &i16, 1);
+ testOk(!status, "dbPutLink %d succeeded (status = %ld)", i16, status);
+ testOk(!dbStateGet(red), "state was cleared");
+
+ i16 = 0x8000;
+ status = dbPutLink(pout, DBF_SHORT, &i16, 1);
+ testOk(!status, "dbPutLink 0x%hx succeeded (status = %ld)", i16, status);
+ testOk(dbStateGet(red), "state was set");
+ }
+
+ status = dbPutLink(pout, DBF_STRING, "", 1);
+ testOk(!status, "dbPutLink '' succeeded (status = %ld)", status);
+ testOk(!dbStateGet(red), "state was cleared");
+
+ status = dbPutLink(pout, DBF_STRING, "FALSE", 1); /* Not really... */
+ testOk(!status, "dbPutLink 'FALSE' succeeded (status = %ld)", status);
+ testOk(dbStateGet(red), "state was set");
+
+ status = dbPutLink(pout, DBF_STRING, "0", 1);
+ testOk(!status, "dbPutLink '0' succeeded (status = %ld)", status);
+ testOk(!dbStateGet(red), "state was cleared");
+
+ {
+ epicsFloat64 f64 = 0.1;
+
+ status = dbPutLink(pout, DBF_DOUBLE, &f64, 1);
+ testOk(!status, "dbPutLink %g succeeded (status = %ld)", f64, status);
+ testOk(dbStateGet(red), "state was set");
+
+ testdbPutFieldOk("io.OUTPUT", DBF_STRING, "{\"state\":\"!red\"}");
+ if (testOk1(pout->type == JSON_LINK))
+ testDiag("Link was set to '%s'", pout->value.json.string);
+
+ status = dbPutLink(pout, DBF_DOUBLE, &f64, 1);
+ testOk(!status, "dbPutLink %g succeeded (status = %ld)", f64, status);
+ testOk(!dbStateGet(red), "state was cleared");
+ }
+
+ testIocShutdownOk();
+
+ testdbCleanup();
+}
+
+
+MAIN(lnkStateTest)
+{
+ testPlan(28);
+
+ testState();
+
+ return testDone();
+}
diff --git a/modules/database/test/std/link/rtemsTestHarness.c b/modules/database/test/std/link/rtemsTestHarness.c
new file mode 100644
index 000000000..7397f74d6
--- /dev/null
+++ b/modules/database/test/std/link/rtemsTestHarness.c
@@ -0,0 +1,14 @@
+/*************************************************************************\
+* Copyright (c) 2018 UChicago Argonne LLC, as Operator of Argonne
+* National Laboratory.
+* EPICS BASE is distributed subject to a Software License Agreement found
+* in the file LICENSE that is included with this distribution.
+\*************************************************************************/
+
+extern void epicsRunLinkTests(void);
+
+int main(int argc, char **argv)
+{
+ epicsRunLinkTests(); /* calls epicsExit(0) */
+ return 0;
+}
diff --git a/modules/database/test/std/rec/Makefile b/modules/database/test/std/rec/Makefile
index 7c78ac93c..ae3741977 100644
--- a/modules/database/test/std/rec/Makefile
+++ b/modules/database/test/std/rec/Makefile
@@ -136,12 +136,23 @@ testHarness_SRCS += mbbioDirectTest.c
TESTFILES += ../mbbioDirectTest.db
TESTS += mbbioDirectTest
+TARGETS += $(COMMON_DIR)/asyncproctest.dbd
+DBDDEPENDS_FILES += asyncproctest.dbd$(DEP)
+asyncproctest_DBD += base.dbd
+TESTPROD_HOST += asyncproctest
+asyncproctest_SRCS += asyncproctest.c
+asyncproctest_SRCS += asyncproctest_registerRecordDeviceDriver.cpp
+TESTFILES += $(COMMON_DIR)/asyncproctest.dbd ../asyncproctest.db
+TESTS += asyncproctest
+
# epicsRunRecordTests runs all the test programs in a known working order.
testHarness_SRCS += epicsRunRecordTests.c
recordTestHarness_SRCS += $(testHarness_SRCS)
recordTestHarness_SRCS_RTEMS += rtemsTestHarness.c
+PROD_SRCS_RTEMS += rtemsTestData.c
+
PROD_vxWorks = recordTestHarness
PROD_RTEMS = recordTestHarness
@@ -149,5 +160,12 @@ TESTSPEC_vxWorks = recordTestHarness.munch; epicsRunRecordTests
TESTSPEC_RTEMS = recordTestHarness.boot; epicsRunRecordTests
TESTSCRIPTS_HOST += $(TESTS:%=%.t)
+ifneq ($(filter $(T_A),$(CROSS_COMPILER_RUNTEST_ARCHS)),)
+TESTPROD_RTEMS = $(TESTPROD_HOST)
+TESTSCRIPTS_RTEMS += $(TESTS:%=%.t)
+endif
include $(TOP)/configure/RULES
+
+rtemsTestData.c : $(TESTFILES) $(TOOLS)/epicsMakeMemFs.pl
+ $(PERL) $(TOOLS)/epicsMakeMemFs.pl $@ epicsRtemsFSImage $(TESTFILES)
diff --git a/modules/database/test/std/rec/asyncproctest.c b/modules/database/test/std/rec/asyncproctest.c
new file mode 100644
index 000000000..a8b09a928
--- /dev/null
+++ b/modules/database/test/std/rec/asyncproctest.c
@@ -0,0 +1,107 @@
+/*************************************************************************\
+* Copyright (c) 2018 Michael Davidsaver
+* EPICS BASE is distributed subject to a Software License Agreement found
+* in file LICENSE that is included with this distribution.
+\*************************************************************************/
+/* This test covers some situations where asynchronous records are
+ * dbProcess()'d while busy (PACT==1).
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "registryFunction.h"
+#include
+
+epicsEventId done;
+static int waitFor;
+
+static
+long doneSubr(subRecord *prec)
+{
+ if (--waitFor <= 0)
+ epicsEventMustTrigger(done);
+ return 0;
+}
+
+void asyncproctest_registerRecordDeviceDriver(struct dbBase *);
+
+MAIN(asyncproctest)
+{
+ testPlan(21);
+
+ done = epicsEventMustCreate(epicsEventEmpty);
+
+ testdbPrepare();
+
+ testdbReadDatabase("asyncproctest.dbd", NULL, NULL);
+ asyncproctest_registerRecordDeviceDriver(pdbbase);
+ registryFunctionAdd("doneSubr", (REGISTRYFUNCTION) doneSubr);
+ testdbReadDatabase("asyncproctest.db", NULL, "TPRO=0");
+
+ dbAccessDebugPUTF = 1;
+
+ testIocInitOk();
+ testDiag("===== Chain 1 ======");
+
+ waitFor = 2;
+ testdbPutFieldOk("chain1.B", DBF_LONG, 6);
+ testdbPutFieldOk("chain1.B", DBF_LONG, 7);
+
+ if (epicsEventWaitWithTimeout(done, 10.0) != epicsEventOK)
+ testAbort("Processing timed out");
+
+ testdbGetFieldEqual("chain1", DBF_LONG, 7);
+ testdbGetFieldEqual("chain1.A", DBF_LONG, 2);
+
+ testDiag("===== Chain 2 ======");
+
+ waitFor = 2;
+ testdbPutFieldOk("chain2:1.B", DBF_LONG, 6);
+ testdbPutFieldOk("chain2:1.B", DBF_LONG, 7);
+
+ if (epicsEventWaitWithTimeout(done, 10.0) != epicsEventOK)
+ testAbort("Processing timed out");
+
+ testdbGetFieldEqual("chain2:1", DBF_LONG, 7);
+ testdbGetFieldEqual("chain2:2", DBF_LONG, 7);
+ testdbGetFieldEqual("chain2:1.A", DBF_LONG, 2);
+ testdbGetFieldEqual("chain2:2.A", DBF_LONG, 2);
+
+ testDiag("===== Chain 2 again ======");
+
+ waitFor = 2;
+ testdbPutFieldOk("chain2:1.B", DBF_LONG, 6);
+ testdbPutFieldOk("chain2:1.B", DBF_LONG, 7);
+ testdbPutFieldOk("chain2:1.B", DBF_LONG, 8);
+
+ if (epicsEventWaitWithTimeout(done, 10.0) != epicsEventOK)
+ testAbort("Processing timed out");
+
+ testdbGetFieldEqual("chain2:1", DBF_LONG, 8);
+ testdbGetFieldEqual("chain2:2", DBF_LONG, 8);
+ testdbGetFieldEqual("chain2:1.A", DBF_LONG, 5);
+ testdbGetFieldEqual("chain2:2.A", DBF_LONG, 4);
+
+ testDiag("===== Chain 3 ======");
+
+ waitFor = 2;
+ testdbPutFieldOk("chain3.B", DBF_LONG, 6);
+ testdbPutFieldOk("chain3.B", DBF_LONG, 7);
+
+ if (epicsEventWaitWithTimeout(done, 10.0) != epicsEventOK)
+ testAbort("Processing timed out");
+
+ testdbGetFieldEqual("chain3", DBF_LONG, 7);
+ testdbGetFieldEqual("chain3.A", DBF_LONG, 2);
+
+ testIocShutdownOk();
+
+ testdbCleanup();
+
+ return testDone();
+}
diff --git a/modules/database/test/std/rec/asyncproctest.db b/modules/database/test/std/rec/asyncproctest.db
new file mode 100644
index 000000000..352b6ca56
--- /dev/null
+++ b/modules/database/test/std/rec/asyncproctest.db
@@ -0,0 +1,55 @@
+
+# simple case
+# stand alone async record
+record(calcout, "chain1") {
+ field(CALC, "A:=A+1;B")
+ field(ODLY, "0.1")
+ field(TPRO, "$(TPRO=)")
+ field(FLNK, "done1")
+}
+record(sub, "done1") {
+ field(SNAM, "doneSubr")
+ field(TPRO, "$(TPRO=)")
+}
+
+
+# original problem case
+# async record chained after syncronous record
+record(calcout, "chain2:1") {
+ field(CALC, "A:=A+1;B")
+ # ODLY=0 synchronous
+ field(OUT , "chain2:2.B PP")
+ field(TPRO, "$(TPRO=)")
+}
+
+record(ai, "chain2:3") {
+ field(INP, "chain2:1 CPP")
+ field(FLNK, "chain2:2")
+ field(TPRO, "$(TPRO=)")
+}
+
+record(calcout, "chain2:2") {
+ field(CALC, "A:=A+1;B")
+ field(ODLY, "0.5")
+ field(TPRO, "$(TPRO=)")
+ field(FLNK, "done2")
+}
+record(sub, "done2") {
+ field(SNAM, "doneSubr")
+ field(TPRO, "$(TPRO=)")
+}
+
+
+# ANJ's error case
+# async record FLNK's to itself (via done3)
+record(calcout, "chain3") {
+ field(CALC, "A:=A+1;B")
+ field(ODLY, "0.1")
+ field(FLNK, "done3")
+ field(TPRO, "$(TPRO=)")
+}
+record(sub, "done3") {
+ field(SNAM, "doneSubr")
+ field(FLNK, "chain3")
+ field(TPRO, "$(TPRO=)")
+}
diff --git a/modules/database/test/std/rec/linkInitTest.c b/modules/database/test/std/rec/linkInitTest.c
index 7225beb1c..09ff40942 100644
--- a/modules/database/test/std/rec/linkInitTest.c
+++ b/modules/database/test/std/rec/linkInitTest.c
@@ -7,11 +7,14 @@
#include
#include "dbAccess.h"
+#include "devSup.h"
#include "alarm.h"
#include "dbUnitTest.h"
#include "errlog.h"
#include "epicsThread.h"
+#include "longinRecord.h"
+
#include "testMain.h"
void recTestIoc_registerRecordDeviceDriver(struct dbBase *);
@@ -28,12 +31,21 @@ static void startTestIoc(const char *dbfile)
eltc(1);
}
+/* testing here instead of ioc/db/test as xRecord has no INP/OUT */
+static void testdbGetDevLink(void)
+{
+ longinRecord *rec = (longinRecord*)testdbRecordPtr("li1");
+ testOk1(dbGetDevLink((dbCommon*)rec) == &rec->inp);
+}
+
static void testLongStringInit()
{
testDiag("testLongStringInit");
startTestIoc("linkInitTest.db");
+ testdbGetDevLink();
+
{
const char buf[] = "!----------------------------------------------!";
testdbGetArrFieldEqual("longstr1.VAL$", DBF_CHAR, NELEMENTS(buf)+2, NELEMENTS(buf), buf);
@@ -230,7 +242,7 @@ void testInt64Inputs(void)
MAIN(linkInitTest)
{
- testPlan(77);
+ testPlan(78);
testLongStringInit();
testCalcInit();
diff --git a/modules/libcom/RTEMS/Makefile b/modules/libcom/RTEMS/Makefile
index b0f3b6fd9..311c25024 100644
--- a/modules/libcom/RTEMS/Makefile
+++ b/modules/libcom/RTEMS/Makefile
@@ -11,8 +11,14 @@ TOP = ..
include $(TOP)/configure/CONFIG
include $(TOP)/configure/CONFIG_LIBCOM_VERSION
-INC += epicsRtemsInitHooks.h
+PERL_SCRIPTS += epicsMakeMemFs.pl
+INC += epicsRtemsInitHooks.h
+INC += epicsMemFs.h
+
+ifeq ($(RTEMS_QEMU_FIXUPS),YES)
+rtems_init_CPPFLAGS += -DQEMU_FIXUPS
+endif
rtemsCom_SRCS += rtems_init.c
rtemsCom_SRCS += rtems_config.c
rtemsCom_SRCS += rtems_netconfig.c
@@ -20,6 +26,11 @@ rtemsCom_SRCS += rtems_util.c
rtemsCom_SRCS += setBootConfigFromNVRAM.c
rtemsCom_SRCS += epicsRtemsInitHookPre.c
rtemsCom_SRCS += epicsRtemsInitHookPost.c
+rtemsCom_SRCS += epicsMemFs.c
+
+ifeq ($(RTEMS_BSP),pc386)
+rtemsCom_SRCS += ne2kpci.c
+endif
LIBRARY_RTEMS = rtemsCom
diff --git a/modules/libcom/RTEMS/epicsMakeMemFs.pl b/modules/libcom/RTEMS/epicsMakeMemFs.pl
new file mode 100644
index 000000000..a97074e0e
--- /dev/null
+++ b/modules/libcom/RTEMS/epicsMakeMemFs.pl
@@ -0,0 +1,83 @@
+#!/usr/bin/env perl
+#
+
+use File::Basename;
+
+use strict;
+
+my $outfile = shift;
+my $varname = shift;
+
+open(my $DST, '>', $outfile) or die "Failed to open $outfile";
+
+print $DST <
+EOF
+
+my $N = 0;
+
+for my $fname (@ARGV) {
+ print "<- $fname\n";
+ my $realfname = $fname;
+
+ # strip leading "../" "./" or "/"
+ while ($fname =~ /^\.*\/(.*)$/) { $fname = $1; }
+
+ my $file = basename($fname);
+ my @dirs = split('/', dirname($fname));
+
+ print $DST "/* begin $realfname */\nstatic const char * const file_${N}_dir[] = {";
+ for my $dpart (@dirs) {
+ print $DST "\"$dpart\", ";
+ }
+ print $DST "NULL};\nstatic const char file_${N}_data[] = {\n ";
+
+ open(my $SRC, '<', $realfname) or die "Failed to open $realfname";
+ binmode($SRC);
+
+ my $buf;
+ my $total = 0;
+ while (my $num = read($SRC, $buf, 32)) {
+ if($total != 0) {
+ print $DST ",\n ";
+ }
+ $total += $num;
+ my $out = join(",",map(ord,split(//,$buf)));
+ print $DST "$out";
+ }
+
+ close($SRC);
+
+ print $DST <
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#include "epicsMemFs.h"
+
+#ifndef PATH_MAX
+# define PATH_MAX 100
+#endif
+
+int epicsMemFsLoad(const epicsMemFS *fs)
+{
+ char initdir[PATH_MAX];
+ const epicsMemFile * const *fileptr = fs->files;
+
+ if(getcwd(initdir, sizeof(initdir)-1)==NULL) {
+ perror("getcwd");
+ return errno;
+ }
+ initdir[sizeof(initdir)-1] = '\0';
+
+ for(;*fileptr; fileptr++) {
+ const epicsMemFile *curfile = *fileptr;
+ int fd;
+ ssize_t ret;
+ size_t sofar;
+ const char * const *dir = curfile->directory;
+ /* jump back to the root each time,
+ * slow but simple.
+ */
+ if(chdir(initdir)) {
+ perror("chdir");
+ return errno;
+ }
+
+ printf("-> /");
+
+ /* traverse directory tree, creating as necessary */
+ for(;*dir; dir++) {
+ int ret;
+ if(**dir=='.') continue; /* ignore '.' and '..' */
+ printf("%s/", *dir);
+ ret = chdir(*dir);
+ if(ret==-1 && errno==ENOENT) {
+ /* this directory doesn't exist */
+ if(mkdir(*dir,0744)==-1) {
+ printf("\n");
+ perror("mkdir");
+ return errno;
+ }
+ if(chdir(*dir)==-1) {
+ printf("\n");
+ perror("chdir2");
+ return errno;
+ }
+ } else if(ret==-1) {
+ printf("\n");
+ perror("chdir1");
+ return errno;
+ }
+ }
+
+ /* no file name creates an empty directory */
+ if(!curfile->name) {
+ printf("\n");
+ continue;
+ }
+ printf("%s", curfile->name);
+
+ /* create or overwrite */
+ fd = open(curfile->name, O_WRONLY|O_CREAT|O_TRUNC, 0644);
+
+ if(fd==-1) {
+ printf("\n");
+ perror("open");
+ return errno;
+ }
+
+ sofar = 0;
+
+ while(sofarsize) {
+ ret = write(fd, curfile->data+sofar, curfile->size-sofar);
+ if(ret<=0) {
+ printf("\n");
+ perror("write");
+ return errno;
+ }
+ sofar += ret;
+ }
+
+ close(fd);
+ printf(" - ok\n");
+ }
+
+ if(chdir(initdir))
+ perror("chdir");
+
+ return 0;
+}
diff --git a/modules/libcom/RTEMS/epicsMemFs.h b/modules/libcom/RTEMS/epicsMemFs.h
new file mode 100644
index 000000000..c57f4d793
--- /dev/null
+++ b/modules/libcom/RTEMS/epicsMemFs.h
@@ -0,0 +1,24 @@
+/*************************************************************************\
+* Copyright (c) 2014 Brookhaven National Laboratory.
+* EPICS BASE is distributed subject to a Software License Agreement found
+* in file LICENSE that is included with this distribution.
+\*************************************************************************/
+#ifndef EPICSMEMFS_H
+#define EPICSMEMFS_H
+
+#include
+
+typedef struct {
+ const char * const *directory; /* NULL terminated list of directories */
+ const char *name; /* file name */
+ const char *data; /* file contents */
+ size_t size; /* size of file contents in bytes */
+} epicsMemFile;
+
+typedef struct {
+ const epicsMemFile * const *files;
+} epicsMemFS;
+
+int epicsMemFsLoad(const epicsMemFS *fs);
+
+#endif // EPICSMEMFS_H
diff --git a/modules/libcom/RTEMS/epicsRtemsInitHooks.h b/modules/libcom/RTEMS/epicsRtemsInitHooks.h
index b7f09c100..4313f19e3 100644
--- a/modules/libcom/RTEMS/epicsRtemsInitHooks.h
+++ b/modules/libcom/RTEMS/epicsRtemsInitHooks.h
@@ -21,3 +21,5 @@ extern char *env_nfsMountPoint;
*/
int epicsRtemsInitPreSetBootConfigFromNVRAM(struct rtems_bsdnet_config *config);
int epicsRtemsInitPostSetBootConfigFromNVRAM(struct rtems_bsdnet_config *config);
+/* Return 0 if local file system was setup, or non-zero (will fall back to network */
+int epicsRtemsMountLocalFilesystem(char **argv);
diff --git a/modules/libcom/RTEMS/ne2kpci.c b/modules/libcom/RTEMS/ne2kpci.c
new file mode 100644
index 000000000..909d885f1
--- /dev/null
+++ b/modules/libcom/RTEMS/ne2kpci.c
@@ -0,0 +1,58 @@
+/*************************************************************************\
+* Copyright (c) 2015 Brookhaven Science Associates, as Operator of
+* Brookhaven National Laboratory.
+* EPICS BASE is distributed subject to a Software License Agreement found
+* in file LICENSE that is included with this distribution.
+\*************************************************************************/
+/*
+ * Wrapper around the ISA ne2000 driver to support detection of the PCI variant.
+ * Can be used with the ne2k_pci device provided by QEMU.
+ *
+ * eg. "qemu-system-i386 ... -net nic,model=ne2k_pci"
+ */
+
+#include
+#include
+#include
+#include
+#include
+
+/* The plain ISA driver attach()
+ * which doesn't (can't?) do any test to see if
+ * the HW is really present
+ */
+extern int
+rtems_ne_driver_attach (struct rtems_bsdnet_ifconfig *config, int attach);
+
+int
+rtems_ne2kpci_driver_attach (struct rtems_bsdnet_ifconfig *config, int attach)
+{
+ uint8_t irq;
+ uint32_t bar0;
+ int B, D, F, ret;
+ printk("Probing for NE2000 on PCI (aka. Realtek 8029)\n");
+
+ if(pci_find_device(PCI_VENDOR_ID_REALTEK, PCI_DEVICE_ID_REALTEK_8029, 0, &B, &D, &F))
+ {
+ printk("Not found\n");
+ return 0;
+ }
+
+ printk("Found %d:%d.%d\n", B, D, F);
+
+ ret = pci_read_config_dword(B, D, F, PCI_BASE_ADDRESS_0, &bar0);
+ ret|= pci_read_config_byte(B, D, F, PCI_INTERRUPT_LINE, &irq);
+
+ if(ret || (bar0&PCI_BASE_ADDRESS_SPACE)!=PCI_BASE_ADDRESS_SPACE_IO)
+ {
+ printk("Failed reading card config\n");
+ return 0;
+ }
+
+ config->irno = irq;
+ config->port = bar0&PCI_BASE_ADDRESS_IO_MASK;
+
+ printk("Using port=0x%x irq=%u\n", (unsigned)config->port, config->irno);
+
+ return rtems_ne_driver_attach(config, attach);
+}
diff --git a/modules/libcom/RTEMS/rtems_init.c b/modules/libcom/RTEMS/rtems_init.c
index 82871da0d..7dbf2594f 100644
--- a/modules/libcom/RTEMS/rtems_init.c
+++ b/modules/libcom/RTEMS/rtems_init.c
@@ -33,6 +33,7 @@
#include
#include
+#include "epicsVersion.h"
#include "epicsThread.h"
#include "epicsTime.h"
#include "epicsExit.h"
@@ -42,9 +43,12 @@
#include "osiUnistd.h"
#include "iocsh.h"
#include "osdTime.h"
+#include "epicsMemFs.h"
#include "epicsRtemsInitHooks.h"
+#define RTEMS_VERSION_INT VERSION_INT(__RTEMS_MAJOR__, __RTEMS_MINOR__, 0, 0)
+
/*
* Prototypes for some functions not in header files
*/
@@ -138,6 +142,31 @@ mustMalloc(int size, const char *msg)
# include
#endif
+const epicsMemFS *epicsRtemsFSImage __attribute__((weak));
+const epicsMemFS *epicsRtemsFSImage = (void*)&epicsRtemsFSImage;
+
+/* hook to allow app specific FS setup */
+int
+epicsRtemsMountLocalFilesystem(char **argv) __attribute__((weak));
+int
+epicsRtemsMountLocalFilesystem(char **argv)
+{
+ if(epicsRtemsFSImage==(void*)&epicsRtemsFSImage)
+ return -1; /* no FS image provided. */
+ else if(epicsRtemsFSImage==NULL)
+ return 0; /* no FS image provided, but none is needed. */
+ else {
+ printf("***** Using compiled in file data *****\n");
+ if (epicsMemFsLoad(epicsRtemsFSImage) != 0) {
+ printf("Can't unpack tar filesystem\n");
+ return -1;
+ } else {
+ argv[1] = "/";
+ return 0;
+ }
+ }
+}
+
static int
initialize_local_filesystem(char **argv)
{
@@ -146,7 +175,9 @@ initialize_local_filesystem(char **argv)
extern char _FlashSize[] __attribute__((weak));
argv[0] = rtems_bsdnet_bootp_boot_file_name;
- if (_FlashSize && (_DownloadLocation || _FlashBase)) {
+ if (epicsRtemsMountLocalFilesystem(argv)==0) {
+ return 1; /* FS setup successful */
+ } else if (_FlashSize && (_DownloadLocation || _FlashBase)) {
extern char _edata[];
size_t flashIndex = _edata - _DownloadLocation;
char *header = _FlashBase + flashIndex;
@@ -596,6 +627,7 @@ Init (rtems_task_argument ignored)
}
printf("\n***** Initializing network *****\n");
rtems_bsdnet_initialize_network();
+ printf("\n***** Setting up file system *****\n");
initialize_remote_filesystem(argv, initialize_local_filesystem(argv));
fixup_hosts();
@@ -666,3 +698,37 @@ Init (rtems_task_argument ignored)
epicsThreadSleep(1.0);
epicsExit(result);
}
+
+#if defined(QEMU_FIXUPS)
+/* Override some hooks (weak symbols)
+ * if BSP defaults aren't configured for running tests.
+ */
+
+
+/* Ensure that stdio goes to serial (so it can be captured) */
+#if defined(__i386__) && !USE_COM1_AS_CONSOLE
+#include
+extern int BSPPrintkPort;
+void bsp_predriver_hook(void)
+{
+ BSPConsolePort = BSP_CONSOLE_PORT_COM1;
+ BSPPrintkPort = BSP_CONSOLE_PORT_COM1;
+}
+#endif
+
+/* reboot immediately when done. */
+#if defined(__i386__) && BSP_PRESS_KEY_FOR_RESET
+void bsp_cleanup(void)
+{
+#if RTEMS_VERSION_INT>=VERSION_INT(4,10,0,0)
+ void bsp_reset();
+ bsp_reset();
+#else
+ rtemsReboot();
+#endif
+}
+#endif
+
+#endif /* QEMU_FIXUPS */
+
+int cexpdebug __attribute__((weak));
diff --git a/modules/libcom/RTEMS/rtems_netconfig.c b/modules/libcom/RTEMS/rtems_netconfig.c
index 832a6646b..d0c500c92 100644
--- a/modules/libcom/RTEMS/rtems_netconfig.c
+++ b/modules/libcom/RTEMS/rtems_netconfig.c
@@ -36,11 +36,19 @@ static struct rtems_bsdnet_ifconfig loopback_config = {
* application directory and make the appropriate changes.
*/
#if defined(__i386__)
+
+extern int
+rtems_ne2kpci_driver_attach (struct rtems_bsdnet_ifconfig *config, int attach);
+static struct rtems_bsdnet_ifconfig ne2k_driver_config = {
+ "ne2", /* name */
+ rtems_ne2kpci_driver_attach, /* attach function */
+ &loopback_config, /* link to next interface */
+};
extern int rtems_fxp_attach (struct rtems_bsdnet_ifconfig *, int);
static struct rtems_bsdnet_ifconfig fxp_driver_config = {
"fxp1", /* name */
rtems_fxp_attach, /* attach function */
- &loopback_config, /* link to next interface */
+ &ne2k_driver_config, /* link to next interface */
};
extern int rtems_3c509_driver_attach (struct rtems_bsdnet_ifconfig *, int);
static struct rtems_bsdnet_ifconfig e3c509_driver_config = {
diff --git a/modules/libcom/src/env/envDefs.h b/modules/libcom/src/env/envDefs.h
index 13df94733..8be00a9ea 100644
--- a/modules/libcom/src/env/envDefs.h
+++ b/modules/libcom/src/env/envDefs.h
@@ -99,6 +99,7 @@ epicsShareFunc long epicsShareAPI
envGetBoolConfigParam(const ENV_PARAM *pParam, int *pBool);
epicsShareFunc long epicsShareAPI epicsPrtEnvParams(void);
epicsShareFunc void epicsShareAPI epicsEnvSet (const char *name, const char *value);
+epicsShareFunc void epicsShareAPI epicsEnvUnset (const char *name);
epicsShareFunc void epicsShareAPI epicsEnvShow (const char *name);
#ifdef __cplusplus
diff --git a/modules/libcom/src/flex/flex.c b/modules/libcom/src/flex/flex.c
index 196f4e4cc..8d0400c2b 100644
--- a/modules/libcom/src/flex/flex.c
+++ b/modules/libcom/src/flex/flex.c
@@ -4,7 +4,7 @@
* Copyright (c) 2002 The Regents of the University of California, as
* Operator of Los Alamos National Laboratory.
* EPICS BASE is distributed subject to a Software License Agreement found
-* in file LICENSE that is included with this distribution.
+* in file LICENSE that is included with this distribution.
\*************************************************************************/
/* flex - tool to generate fast lexical analyzers */
@@ -14,7 +14,7 @@
*
* This code is derived from software contributed to Berkeley by
* Vern Paxson.
- *
+ *
* The United States Government has rights in this work pursuant
* to contract no. DE-AC03-76SF00098 between the United States
* Department of Energy and the University of California.
@@ -495,6 +495,13 @@ void flexinit(int argc, char **argv)
/* stupid do-nothing deprecated option */
break;
+ case 'o':
+ if ( i != 1 )
+ flexerror( "-o flag must be given separately" );
+
+ outfile = arg + i + 1;
+ goto get_next_arg;
+
case 'p':
performance_report = true;
break;
diff --git a/modules/libcom/src/iocsh/libComRegister.c b/modules/libcom/src/iocsh/libComRegister.c
index d3a5cfac4..d8429fa9f 100644
--- a/modules/libcom/src/iocsh/libComRegister.c
+++ b/modules/libcom/src/iocsh/libComRegister.c
@@ -110,6 +110,21 @@ static void epicsEnvSetCallFunc(const iocshArgBuf *args)
epicsEnvSet (name, value);
}
+/* epicsEnvUnset */
+static const iocshArg epicsEnvUnsetArg0 = { "name",iocshArgString};
+static const iocshArg * const epicsEnvUnsetArgs[1] = {&epicsEnvUnsetArg0};
+static const iocshFuncDef epicsEnvUnsetFuncDef = {"epicsEnvUnset",1,epicsEnvUnsetArgs};
+static void epicsEnvUnsetCallFunc(const iocshArgBuf *args)
+{
+ char *name = args[0].sval;
+
+ if (name == NULL) {
+ fprintf(stderr, "Missing environment variable name argument.\n");
+ return;
+ }
+ epicsEnvUnset (name);
+}
+
/* epicsParamShow */
static const iocshFuncDef epicsParamShowFuncDef = {"epicsParamShow",0,NULL};
static void epicsParamShowCallFunc(const iocshArgBuf *args)
@@ -367,6 +382,7 @@ void epicsShareAPI libComRegister(void)
iocshRegister(&pwdFuncDef, pwdCallFunc);
iocshRegister(&epicsEnvSetFuncDef, epicsEnvSetCallFunc);
+ iocshRegister(&epicsEnvUnsetFuncDef, epicsEnvUnsetCallFunc);
iocshRegister(&epicsParamShowFuncDef, epicsParamShowCallFunc);
iocshRegister(&epicsPrtEnvParamsFuncDef, epicsPrtEnvParamsCallFunc);
iocshRegister(&epicsEnvShowFuncDef, epicsEnvShowCallFunc);
diff --git a/modules/libcom/src/log/iocLogServer.c b/modules/libcom/src/log/iocLogServer.c
index f5694fc0f..f42aa4563 100644
--- a/modules/libcom/src/log/iocLogServer.c
+++ b/modules/libcom/src/log/iocLogServer.c
@@ -347,11 +347,6 @@ static int openLogFile (struct ioc_log_server *pserver)
{
enum TF_RETURN ret;
- if (ioc_log_file_limit==0u) {
- pserver->poutfile = stderr;
- return IOCLS_ERROR;
- }
-
if (pserver->poutfile && pserver->poutfile != stderr){
fclose (pserver->poutfile);
pserver->poutfile = NULL;
@@ -627,7 +622,7 @@ static void writeMessagesToLog (struct iocLogClient *pclient)
strlen(pclient->ascii_time) + nchar + 3u;
assert (nTotChar <= INT_MAX);
ntci = (int) nTotChar;
- if ( pclient->pserver->filePos+ntci >= pclient->pserver->max_file_size ) {
+ if ( pclient->pserver->max_file_size && pclient->pserver->filePos+ntci >= pclient->pserver->max_file_size ) {
if ( pclient->pserver->max_file_size >= pclient->pserver->filePos ) {
unsigned nPadChar;
/*
@@ -771,7 +766,7 @@ static int getConfig(void)
&EPICS_IOC_LOG_FILE_LIMIT,
&ioc_log_file_limit);
if(status>=0){
- if (ioc_log_file_limit<=0) {
+ if (ioc_log_file_limit < 0) {
envFailureNotify (&EPICS_IOC_LOG_FILE_LIMIT);
return IOCLS_ERROR;
}
diff --git a/modules/libcom/src/macLib/macCore.c b/modules/libcom/src/macLib/macCore.c
index 55d31719e..6f84d6b84 100644
--- a/modules/libcom/src/macLib/macCore.c
+++ b/modules/libcom/src/macLib/macCore.c
@@ -902,11 +902,16 @@ static void refer ( MAC_HANDLE *handle, MAC_ENTRY *entry, int level,
}
}
- /* Bad reference, insert $(name,errval) */
+ /* Bad reference, insert either $(name,) or $(name) */
if ( v < valend ) *v++ = '$';
if ( v < valend ) *v++ = '(';
cpy2val( refname, &v, valend );
- cpy2val( errval, &v, valend );
+ if (handle->flags & FLAG_SUPPRESS_WARNINGS) {
+ if ( v < valend ) *v++ = ')';
+ *v = '\0';
+ }
+ else
+ cpy2val( errval, &v, valend );
cleanup:
if (pop) {
diff --git a/modules/libcom/src/misc/testMain.h b/modules/libcom/src/misc/testMain.h
index 4db72c39e..7c4d9b29b 100644
--- a/modules/libcom/src/misc/testMain.h
+++ b/modules/libcom/src/misc/testMain.h
@@ -27,7 +27,13 @@
* }
*/
-#if defined(vxWorks) || defined(__rtems__)
+#if defined(__rtems__)
+ #ifdef __cplusplus
+ #define MAIN(prog) extern "C" int prog(void); extern "C" int main() __attribute__((weak, alias(#prog))); extern "C" int prog(void)
+ #else
+ #define MAIN(prog) int prog(); int main() __attribute__((weak, alias(#prog))); int prog()
+ #endif
+#elif defined(vxWorks)
#ifdef __cplusplus
#define MAIN(prog) extern "C" int prog(void)
#else
diff --git a/modules/libcom/src/osi/epicsStdio.h b/modules/libcom/src/osi/epicsStdio.h
index 172d5cf87..9ef0b2239 100644
--- a/modules/libcom/src/osi/epicsStdio.h
+++ b/modules/libcom/src/osi/epicsStdio.h
@@ -34,20 +34,22 @@ extern "C" {
/* Make printf, puts and putchar use *our* version of stdout */
-#ifdef printf
-# undef printf
-#endif /* printf */
-#define printf epicsStdoutPrintf
+#ifndef epicsStdioStdPrintfEtc
+# ifdef printf
+# undef printf
+# endif
+# define printf epicsStdoutPrintf
-#ifdef puts
-# undef puts
-#endif /* puts */
-#define puts epicsStdoutPuts
+# ifdef puts
+# undef puts
+# endif
+# define puts epicsStdoutPuts
-#ifdef putchar
-# undef putchar
-#endif /* putchar */
-#define putchar epicsStdoutPutchar
+# ifdef putchar
+# undef putchar
+# endif
+# define putchar epicsStdoutPutchar
+#endif
epicsShareFunc int epicsShareAPI epicsSnprintf(
char *str, size_t size, const char *format, ...) EPICS_PRINTF_STYLE(3,4);
@@ -87,6 +89,19 @@ epicsShareFunc int epicsShareAPI epicsStdoutPutchar(int c);
#ifdef __cplusplus
}
-#endif
+
+/* Also pull functions into the std namespace (see lp:1786927) */
+#if !defined(__GNUC__) || (__GNUC__ > 2)
+namespace std {
+using ::epicsGetStdin;
+using ::epicsGetStdout;
+using ::epicsGetStderr;
+using ::epicsStdoutPrintf;
+using ::epicsStdoutPuts;
+using ::epicsStdoutPutchar;
+}
+#endif /* __GNUC__ > 2 */
+
+#endif /* __cplusplus */
#endif /* epicsStdioh */
diff --git a/modules/libcom/src/osi/os/Darwin/osdEnv.c b/modules/libcom/src/osi/os/Darwin/osdEnv.c
index ab3f93644..f7ff12ced 100644
--- a/modules/libcom/src/osi/os/Darwin/osdEnv.c
+++ b/modules/libcom/src/osi/os/Darwin/osdEnv.c
@@ -39,6 +39,16 @@ epicsShareFunc void epicsShareAPI epicsEnvSet (const char *name, const char *val
setenv(name, value, 1);
}
+/*
+ * Unset an environment variable
+ */
+
+epicsShareFunc void epicsShareAPI epicsEnvUnset (const char *name)
+{
+ iocshEnvClear(name);
+ unsetenv(name);
+}
+
/*
* Show the value of the specified, or all, environment variables
*/
diff --git a/modules/libcom/src/osi/os/Darwin/osdSock.h b/modules/libcom/src/osi/os/Darwin/osdSock.h
index e7c344062..0122d16de 100644
--- a/modules/libcom/src/osi/os/Darwin/osdSock.h
+++ b/modules/libcom/src/osi/os/Darwin/osdSock.h
@@ -32,6 +32,7 @@ typedef int SOCKET;
typedef int osiSockIoctl_t;
typedef socklen_t osiSocklen_t;
typedef int osiSockOptMcastLoop_t;
+typedef unsigned char osiSockOptMcastTTL_t;
#define FD_IN_FDSET(FD) ((FD)
+#include
+
+#define epicsExportSharedSymbols
+#include "epicsStdio.h"
+#include "envDefs.h"
+#include "osiUnistd.h"
+#include "iocsh.h"
+
+/*
+ * Set the value of an environment variable
+ */
+epicsShareFunc void epicsShareAPI epicsEnvSet (const char *name, const char *value)
+{
+ iocshEnvClear(name);
+ setenv(name, value, 1);
+}
+
+/*
+ * Unset an environment variable
+ */
+
+epicsShareFunc void epicsShareAPI epicsEnvUnset (const char *name)
+{
+ iocshEnvClear(name);
+ unsetenv(name);
+}
+
+/*
+ * Show the value of the specified, or all, environment variables
+ */
+epicsShareFunc void epicsShareAPI epicsEnvShow (const char *name)
+{
+ if (name == NULL) {
+ extern char **environ;
+ char **sp;
+
+ for (sp = environ ; (sp != NULL) && (*sp != NULL) ; sp++)
+ printf ("%s\n", *sp);
+ }
+ else {
+ const char *cp = getenv (name);
+ if (cp == NULL)
+ printf ("%s is not an environment variable.\n", name);
+ else
+ printf ("%s=%s\n", name, cp);
+ }
+}
diff --git a/modules/libcom/src/osi/os/RTEMS/osdSock.h b/modules/libcom/src/osi/os/RTEMS/osdSock.h
index 6177c3033..ec43b72ef 100644
--- a/modules/libcom/src/osi/os/RTEMS/osdSock.h
+++ b/modules/libcom/src/osi/os/RTEMS/osdSock.h
@@ -43,6 +43,7 @@ typedef int SOCKET;
typedef int osiSockIoctl_t;
typedef socklen_t osiSocklen_t;
typedef char osiSockOptMcastLoop_t;
+typedef unsigned char osiSockOptMcastTTL_t;
#define FD_IN_FDSET(FD) ((FD)
+#include
+#include
+#include
+#include
+
+#define epicsExportSharedSymbols
+#include "epicsStdio.h"
+#include "errlog.h"
+#include "cantProceed.h"
+#include "envDefs.h"
+#include "osiUnistd.h"
+#include "epicsFindSymbol.h"
+#include "iocsh.h"
+
+/*
+ * Set the value of an environment variable
+ * Leaks memory, but the assumption is that this routine won't be
+ * called often enough for the leak to be a problem.
+ */
+epicsShareFunc void epicsShareAPI epicsEnvSet (const char *name, const char *value)
+{
+ char *cp;
+
+ iocshEnvClear(name);
+
+ cp = mallocMustSucceed (strlen (name) + strlen (value) + 2, "epicsEnvSet");
+ strcpy (cp, name);
+ strcat (cp, "=");
+ strcat (cp, value);
+ if (putenv (cp) < 0) {
+ errPrintf(
+ -1L,
+ __FILE__,
+ __LINE__,
+ "Failed to set environment parameter \"%s\" to \"%s\": %s\n",
+ name,
+ value,
+ strerror (errno));
+ free (cp);
+ }
+}
+
+/*
+ * Unset an environment variable
+ * Using putenv with a an existing name plus "=" (without value) deletes
+ */
+
+epicsShareFunc void epicsShareAPI epicsEnvUnset (const char *name)
+{
+ iocshEnvClear(name);
+ if (getenv(name) != NULL)
+ epicsEnvSet((char*)name, "");
+}
+
+/*
+ * Show the value of the specified, or all, environment variables
+ */
+epicsShareFunc void epicsShareAPI epicsEnvShow (const char *name)
+{
+ if (name == NULL) {
+ extern char **environ;
+ char **sp;
+
+ for (sp = environ ; (sp != NULL) && (*sp != NULL) ; sp++)
+ printf ("%s\n", *sp);
+ }
+ else {
+ const char *cp = getenv (name);
+ if (cp == NULL)
+ printf ("%s is not an environment variable.\n", name);
+ else
+ printf ("%s=%s\n", name, cp);
+ }
+}
diff --git a/modules/libcom/src/osi/os/WIN32/osdProcess.c b/modules/libcom/src/osi/os/WIN32/osdProcess.c
index 6e69c0485..fbe68ba91 100644
--- a/modules/libcom/src/osi/os/WIN32/osdProcess.c
+++ b/modules/libcom/src/osi/os/WIN32/osdProcess.c
@@ -19,13 +19,13 @@
#endif
#include
+#include
#define STRICT
#include
#define epicsExportSharedSymbols
#include "osiProcess.h"
-#include "errlog.h"
epicsShareFunc osiGetUserNameReturn epicsShareAPI osiGetUserName (char *pBuf, unsigned bufSizeIn)
{
@@ -94,16 +94,11 @@ epicsShareFunc osiSpawnDetachedProcessReturn epicsShareAPI osiSpawnDetachedProce
pFmtArgs[1] = (char *) pBaseExecutableName;
pFmtArgs[2] = errStrMsgBuf;
pFmtArgs[3] = "Changes may be required in your \"path\" environment variable.";
- pFmtArgs[4] = "PATH = ";
- pFmtArgs[5] = getenv ("path");
- if ( pFmtArgs[5] == NULL ) {
- pFmtArgs[5] = "";
- }
W32status = FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_STRING |
FORMAT_MESSAGE_ARGUMENT_ARRAY | 80,
- "%1 \"%2\". %3 %4 %5 \"%6\"",
+ "%1 \"%2\". %3 %4",
0,
MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default language */
(LPTSTR) &complteMsgBuf,
@@ -111,24 +106,20 @@ epicsShareFunc osiSpawnDetachedProcessReturn epicsShareAPI osiSpawnDetachedProce
pFmtArgs
);
if (W32status) {
- /* Display the string. */
- MessageBox (NULL, complteMsgBuf, "Configuration Problem",
- MB_OK | MB_ICONINFORMATION);
+ fprintf (stderr, "%s\n", (char *) complteMsgBuf);
LocalFree (complteMsgBuf);
}
else {
- /* Display the string. */
- MessageBox (NULL, errStrMsgBuf, "Failed to start executable",
- MB_OK | MB_ICONINFORMATION);
+ fprintf (stderr, "%s\n", (char *) errStrMsgBuf);
}
/* Free the buffer. */
LocalFree (errStrMsgBuf);
}
else {
- errlogPrintf ("!!WARNING!!\n");
- errlogPrintf ("Unable to locate executable \"%s\".\n", pBaseExecutableName);
- errlogPrintf ("You may need to modify your \"path\" environment variable.\n");
+ fprintf (stderr, "!!WARNING!!\n");
+ fprintf (stderr, "Unable to locate executable \"%s\".\n", pBaseExecutableName);
+ fprintf (stderr, "You may need to modify your \"path\" environment variable.\n");
}
return osiSpawnDetachedProcessFail;
}
diff --git a/modules/libcom/src/osi/os/WIN32/osdSock.h b/modules/libcom/src/osi/os/WIN32/osdSock.h
index 2ff23c4a6..c1bc1d9b1 100644
--- a/modules/libcom/src/osi/os/WIN32/osdSock.h
+++ b/modules/libcom/src/osi/os/WIN32/osdSock.h
@@ -29,6 +29,7 @@
typedef u_long FAR osiSockIoctl_t;
typedef int osiSocklen_t;
typedef BOOL osiSockOptMcastLoop_t;
+typedef DWORD osiSockOptMcastTTL_t;
#ifndef SHUT_RD
# define SHUT_RD SD_RECEIVE
diff --git a/modules/libcom/src/osi/os/cygwin32/osdSock.h b/modules/libcom/src/osi/os/cygwin32/osdSock.h
index 75288c865..123f2404f 100644
--- a/modules/libcom/src/osi/os/cygwin32/osdSock.h
+++ b/modules/libcom/src/osi/os/cygwin32/osdSock.h
@@ -35,6 +35,8 @@ typedef int SOCKET;
typedef int osiSockIoctl_t;
typedef int osiSocklen_t;
typedef int osiSockOptMcastLoop_t;
+typedef int osiSockOptMcastTTL_t;
+
#define FD_IN_FDSET(FD) ((FD)=0)
#ifndef SHUT_RD
#define SHUT_RD 0
diff --git a/modules/libcom/src/osi/os/default/osdEnv.c b/modules/libcom/src/osi/os/default/osdEnv.c
index 682bcc934..0f6fb250d 100644
--- a/modules/libcom/src/osi/os/default/osdEnv.c
+++ b/modules/libcom/src/osi/os/default/osdEnv.c
@@ -55,6 +55,18 @@ epicsShareFunc void epicsShareAPI epicsEnvSet (const char *name, const char *val
}
}
+/*
+ * Unset an environment variable
+ * Using putenv with a an existing name but without "=..." deletes
+ */
+
+epicsShareFunc void epicsShareAPI epicsEnvUnset (const char *name)
+{
+ iocshEnvClear(name);
+ if (getenv(name) != NULL)
+ putenv((char*)name);
+}
+
/*
* Show the value of the specified, or all, environment variables
*/
diff --git a/modules/libcom/src/osi/os/freebsd/osdSock.h b/modules/libcom/src/osi/os/freebsd/osdSock.h
index b402ec120..2e6951399 100644
--- a/modules/libcom/src/osi/os/freebsd/osdSock.h
+++ b/modules/libcom/src/osi/os/freebsd/osdSock.h
@@ -37,6 +37,7 @@ typedef int SOCKET;
typedef int osiSockIoctl_t;
typedef socklen_t osiSocklen_t;
typedef int osiSockOptMcastLoop_t;
+typedef unsigned char osiSockOptMcastTTL_t;
#define FD_IN_FDSET(FD) ((FD)
+#include
+
+#define epicsExportSharedSymbols
+#include "epicsStdio.h"
+#include "envDefs.h"
+#include "osiUnistd.h"
+#include "iocsh.h"
+
+/*
+ * Set the value of an environment variable
+ */
+epicsShareFunc void epicsShareAPI epicsEnvSet (const char *name, const char *value)
+{
+ iocshEnvClear(name);
+ setenv(name, value, 1);
+}
+
+/*
+ * Unset an environment variable
+ */
+
+epicsShareFunc void epicsShareAPI epicsEnvUnset (const char *name)
+{
+ iocshEnvClear(name);
+ unsetenv(name);
+}
+
+/*
+ * Show the value of the specified, or all, environment variables
+ */
+epicsShareFunc void epicsShareAPI epicsEnvShow (const char *name)
+{
+ if (name == NULL) {
+ extern char **environ;
+ char **sp;
+
+ for (sp = environ ; (sp != NULL) && (*sp != NULL) ; sp++)
+ printf ("%s\n", *sp);
+ }
+ else {
+ const char *cp = getenv (name);
+ if (cp == NULL)
+ printf ("%s is not an environment variable.\n", name);
+ else
+ printf ("%s=%s\n", name, cp);
+ }
+}
diff --git a/modules/libcom/src/osi/os/solaris/osdSock.h b/modules/libcom/src/osi/os/solaris/osdSock.h
index 96ea2a983..adf3d9bad 100644
--- a/modules/libcom/src/osi/os/solaris/osdSock.h
+++ b/modules/libcom/src/osi/os/solaris/osdSock.h
@@ -43,6 +43,7 @@ typedef int osiSockIoctl_t;
typedef int osiSocklen_t;
#endif
typedef char osiSockOptMcastLoop_t;
+typedef unsigned char osiSockOptMcastTTL_t;
#define DOES_NOT_ACCEPT_ZERO_LENGTH_UDP
diff --git a/modules/libcom/src/osi/os/vxWorks/osdEnv.c b/modules/libcom/src/osi/os/vxWorks/osdEnv.c
index c81f49316..88e0ba078 100644
--- a/modules/libcom/src/osi/os/vxWorks/osdEnv.c
+++ b/modules/libcom/src/osi/os/vxWorks/osdEnv.c
@@ -51,6 +51,25 @@ epicsShareFunc void epicsShareAPI epicsEnvSet (const char *name, const char *val
}
}
+/*
+ * Unset an environment variable
+ * Basically destroy the name of that variable because vxWorks does not
+ * support to really unset an environment variable.
+ */
+
+epicsShareFunc void epicsShareAPI epicsEnvUnset (const char *name)
+{
+ char* var;
+
+ if (!name) return;
+ iocshEnvClear(name);
+ var = getenv(name);
+ if (!var) return;
+ var -= strlen(name);
+ var --;
+ *var = 0;
+}
+
/*
* Show the value of the specified, or all, environment variables
*/
diff --git a/modules/libcom/src/osi/os/vxWorks/osdSock.h b/modules/libcom/src/osi/os/vxWorks/osdSock.h
index d949b25f1..272371523 100644
--- a/modules/libcom/src/osi/os/vxWorks/osdSock.h
+++ b/modules/libcom/src/osi/os/vxWorks/osdSock.h
@@ -66,6 +66,7 @@ typedef int SOCKET;
typedef int osiSockIoctl_t;
typedef int osiSocklen_t;
typedef int osiSockOptMcastLoop_t;
+typedef char osiSockOptMcastTTL_t;
#define FD_IN_FDSET(FD) ((FD)=0)
diff --git a/modules/libcom/src/osi/osiClockTime.c b/modules/libcom/src/osi/osiClockTime.c
index 408a93fc0..fb9d1532f 100644
--- a/modules/libcom/src/osi/osiClockTime.c
+++ b/modules/libcom/src/osi/osiClockTime.c
@@ -40,17 +40,19 @@ static struct {
static epicsThreadOnceId onceId = EPICS_THREAD_ONCE_INIT;
-#if defined(CLOCK_REALTIME) && !defined(_WIN32)
-/* This code is not used on systems without Posix CLOCK_REALTIME such
- * as Darwin, but the only way to detect that is from the OS headers,
- * so the Makefile can't exclude building this file on those systems.
+#ifdef CLOCK_REALTIME
+/* This code is not used on systems without Posix CLOCK_REALTIME,
+ * but the only way to detect that is from the OS headers, so the
+ * Makefile can't exclude compiling this file on those systems.
*/
/* Forward references */
static int ClockTimeGetCurrent(epicsTimeStamp *pDest);
-static void ClockTimeSync(void *dummy);
+#if defined(vxWorks) || defined(__rtems__)
+static void ClockTimeSync(void *dummy);
+#endif
/* ClockTime_Report iocsh command */
static const iocshArg ReportArg0 = { "interest_level", iocshArgArgv};
@@ -98,12 +100,18 @@ void ClockTime_Init(int synchronize)
if (synchronize == CLOCKTIME_SYNC) {
if (ClockTimePvt.synchronize == CLOCKTIME_NOSYNC) {
+
+#if defined(vxWorks) || defined(__rtems__)
/* Start synchronizing */
ClockTimePvt.synchronize = synchronize;
epicsThreadCreate("ClockTimeSync", epicsThreadPriorityHigh,
epicsThreadGetStackSize(epicsThreadStackSmall),
ClockTimeSync, NULL);
+#else
+ errlogPrintf("Clock synchronization must be performed by the OS\n");
+#endif
+
}
else {
/* No change, sync thread should already be running */
@@ -139,6 +147,7 @@ void ClockTime_GetProgramStart(epicsTimeStamp *pDest)
/* Synchronization thread */
+#if defined(vxWorks) || defined(__rtems__)
static void ClockTimeSync(void *dummy)
{
taskwdInsert(0, NULL, NULL);
@@ -177,6 +186,7 @@ static void ClockTimeSync(void *dummy)
ClockTimePvt.synchronized = 0;
taskwdRemove(0);
}
+#endif
/* Time Provider Routine */
@@ -188,6 +198,7 @@ static int ClockTimeGetCurrent(epicsTimeStamp *pDest)
/* If a Hi-Res clock is available and works, use it */
#ifdef CLOCK_REALTIME_HR
clock_gettime(CLOCK_REALTIME_HR, &clockNow) &&
+ /* Note: Uses the lo-res clock below if the above call fails */
#endif
clock_gettime(CLOCK_REALTIME, &clockNow);
@@ -195,9 +206,15 @@ static int ClockTimeGetCurrent(epicsTimeStamp *pDest)
clockNow.tv_sec < POSIX_TIME_AT_EPICS_EPOCH) {
clockNow.tv_sec = POSIX_TIME_AT_EPICS_EPOCH + 86400;
clockNow.tv_nsec = 0;
+
+#if defined(vxWorks) || defined(__rtems__)
clock_settime(CLOCK_REALTIME, &clockNow);
errlogPrintf("WARNING: OS Clock time was read before being set.\n"
"Using 1990-01-02 00:00:00.000000 UTC\n");
+#else
+ errlogPrintf("WARNING: OS Clock pre-dates the EPICS epoch!\n"
+ "Using 1990-01-02 00:00:00.000000 UTC\n");
+#endif
}
epicsTimeFromTimespec(pDest, &clockNow);
diff --git a/modules/libcom/test/Makefile b/modules/libcom/test/Makefile
index e72c46c61..429d446c7 100755
--- a/modules/libcom/test/Makefile
+++ b/modules/libcom/test/Makefile
@@ -14,11 +14,18 @@ PROD_LIBS += Com
PROD_SYS_LIBS_WIN32 += ws2_32 advapi32 user32
PROD_SYS_LIBS_solaris += socket nsl
+PROD_SRCS_RTEMS += rtemsTestData.c
+
+ifeq ($(EPICS_HOST_ARCH),$(T_A))
+# skip except for host arch due to custom .plt
+
TESTPROD_HOST += epicsUnitTestTest
epicsUnitTestTest_SRCS += epicsUnitTestTest.c
# Not much point running this on vxWorks or RTEMS...
TESTS += epicsUnitTestTest
+endif
+
TESTPROD_HOST += epicsTypesTest
epicsTypesTest_SRCS += epicsTypesTest.cpp
testHarness_SRCS += epicsTypesTest.cpp
@@ -62,6 +69,11 @@ epicsEnvTest_SRCS += epicsEnvTest.c
testHarness_SRCS += epicsEnvTest.c
TESTS += epicsEnvTest
+TESTPROD_HOST += epicsEnvUnsetTest
+epicsEnvUnsetTest_SRCS += epicsEnvUnsetTest.c
+testHarness_SRCS += epicsEnvUnsetTest.c
+TESTS += epicsEnvUnsetTest
+
TESTPROD_HOST += epicsErrlogTest
epicsErrlogTest_SRCS += epicsErrlogTest.c
testHarness_SRCS += epicsErrlogTest.c
@@ -215,12 +227,14 @@ osiSockTest_SRCS += osiSockTest.c
testHarness_SRCS += osiSockTest.c
TESTS += osiSockTest
+ifeq ($(BUILD_CLASS),HOST)
ifneq ($(OS_CLASS),WIN32)
# This test can only be run on a build host, and is broken on Windows
TESTPROD_HOST += yajl_test
yajl_test_SRCS += yajl_test.c
TESTS += yajlTest
endif
+endif
# The testHarness runs all the test programs in a known working order.
testHarness_SRCS += epicsRunLibComTests.c
@@ -235,6 +249,10 @@ TESTSPEC_vxWorks = libComTestHarness.munch; epicsRunLibComTests
TESTSPEC_RTEMS = libComTestHarness.boot; epicsRunLibComTests
TESTSCRIPTS_HOST += $(TESTS:%=%.t)
+ifneq ($(filter $(T_A),$(CROSS_COMPILER_RUNTEST_ARCHS)),)
+TESTPROD_RTEMS = $(TESTPROD_HOST)
+TESTSCRIPTS_RTEMS += $(filter-out epicsUnitTestTest.t, $(TESTS:%=%.t))
+endif
# The following are not test programs, they measure performance.
diff --git a/modules/libcom/test/epicsEnvUnsetTest.c b/modules/libcom/test/epicsEnvUnsetTest.c
new file mode 100644
index 000000000..e9d0f8603
--- /dev/null
+++ b/modules/libcom/test/epicsEnvUnsetTest.c
@@ -0,0 +1,37 @@
+#include
+#include
+#include
+#include
+
+#include "macLib.h"
+#include "envDefs.h"
+#include "errlog.h"
+#include "epicsUnitTest.h"
+#include "testMain.h"
+
+static void check(const char* variable, const char* expected)
+{
+ const char* value;
+
+ value = getenv(variable);
+ if (!testOk((!expected && !value) || (expected && value && strcmp(expected, value) == 0),
+ "%s = \"%s\"", variable, value))
+ {
+ testDiag("should have been \"%s\"", expected);
+ }
+}
+
+MAIN(epicsEnvUnsetTest)
+{
+ eltc(0);
+ testPlan(3);
+
+ check("TEST_VAR_A",NULL);
+ epicsEnvSet("TEST_VAR_A","test value");
+ check("TEST_VAR_A","test value");
+ epicsEnvUnset("TEST_VAR_A");
+ check("TEST_VAR_A",NULL);
+
+ testDone();
+ return 0;
+}
diff --git a/modules/libcom/test/macLibTest.c b/modules/libcom/test/macLibTest.c
index 7f1e2c544..742fc79e2 100644
--- a/modules/libcom/test/macLibTest.c
+++ b/modules/libcom/test/macLibTest.c
@@ -65,11 +65,11 @@ static void ovcheck(void)
MAIN(macLibTest)
{
- testPlan(91);
+ testPlan(93);
if (macCreateHandle(&h, NULL))
testAbort("macCreateHandle() failed");
- macSuppressWarning(h, TRUE);
+ eltc(0);
check("FOO", " FOO");
@@ -215,6 +215,10 @@ MAIN(macLibTest)
check("${FOO,FOO=${FOO}}", "!$(FOO,recursive)");
check("${FOO=GRIBBLE,FOO=${FOO}}", "!$(FOO,recursive)");
+ macSuppressWarning(h, TRUE);
+ check("$(CRUX)", "!$(CRUX)");
+ check("${FOO}", "!$(BAR)");
+
ovcheck();
return testDone();
diff --git a/modules/libcom/test/osiSockTest.c b/modules/libcom/test/osiSockTest.c
index 39eb0ba2b..6672cdbd3 100644
--- a/modules/libcom/test/osiSockTest.c
+++ b/modules/libcom/test/osiSockTest.c
@@ -42,6 +42,21 @@ void multiCastLoop(SOCKET s, int put)
"getsockopt MULTICAST_LOOP => %d", (int) flag);
}
+void multiCastTTL(SOCKET s, int put)
+{
+ int status;
+ osiSockOptMcastTTL_t flag = put;
+ osiSocklen_t len = sizeof(flag);
+
+ status = setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL,
+ (char *)&flag, len);
+ testOk(status >= 0, "setsockopt IP_MULTICAST_TTL := %d", put);
+
+ status = getsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, (char *)&flag, &len);
+ testOk(status >= 0 && len == sizeof(flag) && !flag == !put,
+ "getsockopt IP_MULTICAST_TTL => %d", (int) flag);
+}
+
void udpSockTest(void)
{
SOCKET s;
@@ -55,6 +70,9 @@ void udpSockTest(void)
multiCastLoop(s, 1);
multiCastLoop(s, 0);
+ multiCastTTL(s, 1);
+ multiCastTTL(s, 0);
+
epicsSocketDestroy(s);
}
@@ -62,7 +80,7 @@ void udpSockTest(void)
MAIN(osiSockTest)
{
int status;
- testPlan(10);
+ testPlan(14);
status = osiSockAttach();
testOk(status, "osiSockAttach");
diff --git a/modules/libcom/test/rtemsTestData.c b/modules/libcom/test/rtemsTestData.c
new file mode 100644
index 000000000..7b68976c5
--- /dev/null
+++ b/modules/libcom/test/rtemsTestData.c
@@ -0,0 +1,6 @@
+#include "epicsMemFs.h"
+
+/* no local files needed for these tests,
+ * so skip local FS setup
+ */
+const epicsMemFS *epicsRtemsFSImage = NULL;
diff --git a/src/tools/EpicsHostArch.pl b/src/tools/EpicsHostArch.pl
new file mode 100644
index 000000000..e8e49bc5e
--- /dev/null
+++ b/src/tools/EpicsHostArch.pl
@@ -0,0 +1,55 @@
+#!/usr/bin/env perl
+#*************************************************************************
+# Copyright (c) 2018 UChicago Argonne LLC, as Operator of Argonne
+# National Laboratory.
+# EPICS BASE is distributed subject to a Software License Agreement found
+# in file LICENSE that is included with this distribution.
+#*************************************************************************
+
+# Returns an architecture name for EPICS_HOST_ARCH that should be
+# appropriate for the CPU that this version of Perl was built for.
+# Any arguments to the program will be appended with separator '-'
+# to allow flags like -gnu -debug and/or -static to be added.
+
+# Before Base has been built, use a command like this:
+# bash$ export EPICS_HOST_ARCH=`perl src/tools/EpicsHostArch.pl`
+#
+# If Base is already built, use
+# tcsh% setenv EPICS_HOST_ARCH `perl base/lib/perl/EpicsHostArch.pl`
+
+# If your architecture is not recognized by this script, please send
+# the output from running 'perl --version' to the EPICS tech-talk
+# mailing list to have it added.
+
+use strict;
+
+use Config;
+use POSIX;
+
+print join('-', HostArch(), @ARGV), "\n";
+
+sub HostArch {
+ my $arch = $Config{archname};
+ for ($arch) {
+ return 'linux-x86_64' if m/^x86_64-linux/;
+ return 'linux-x86' if m/^i[3-6]86-linux/;
+ return 'linux-arm' if m/^arm-linux/;
+ return 'windows-x64' if m/^MSWin32-x64/;
+ return 'win32-x86' if m/^MSWin32-x86/;
+ return "cygwin-x86_64" if m/^x86_64-cygwin/;
+ return "cygwin-x86" if m/^i[3-6]86-cygwin/;
+ return 'solaris-sparc' if m/^sun4-solaris/;
+ return 'solaris-x86' if m/^i86pc-solaris/;
+
+ my ($kernel, $hostname, $release, $version, $cpu) = uname;
+ if (m/^darwin/) {
+ for ($cpu) {
+ return 'darwin-x86' if m/^(i386|x86_64)/;
+ return 'darwin-ppc' if m/Power Macintosh/;
+ }
+ die "$0: macOS CPU type '$cpu' not recognized\n";
+ }
+
+ die "$0: Architecture '$arch' not recognized\n";
+ }
+}
diff --git a/src/tools/Makefile b/src/tools/Makefile
index b7d6582cb..39497a417 100644
--- a/src/tools/Makefile
+++ b/src/tools/Makefile
@@ -17,6 +17,9 @@ PERL_MODULES += EPICS/Release.pm
PERL_MODULES += EPICS/Readfile.pm
PERL_MODULES += EPICS/Getopts.pm
+# This goes into lib/perl, not bin/
+PERL_MODULES += EpicsHostArch.pl
+
PERL_SCRIPTS += assembleSnippets.pl
PERL_SCRIPTS += convertRelease.pl
PERL_SCRIPTS += cvsclean.pl
diff --git a/src/tools/genVersionHeader.pl b/src/tools/genVersionHeader.pl
index c61f14c98..24e08abf9 100644
--- a/src/tools/genVersionHeader.pl
+++ b/src/tools/genVersionHeader.pl
@@ -23,18 +23,20 @@ my $tfmt = '%Y-%m-%dT%H:%M';
$tfmt .= '%z' unless $^O eq 'MSWin32'; # %z returns zone name on Windows
my $now = strftime($tfmt, localtime);
-our ($opt_h, $opt_v, $opt_q);
+our ($opt_d, $opt_h, $opt_i, $opt_v, $opt_q);
our $opt_t = '.';
our $opt_N = 'VCSVERSION';
our $opt_V = $now;
my $vcs;
-getopts('hvqt:N:V:') && @ARGV == 1
+getopts('dhivqt:N:V:') && @ARGV == 1
or HELP_MESSAGE();
my ($outfile) = @ARGV;
+if ($opt_d) { exit 0 } # exit if make is run in dry-run mode
+
if (!$vcs && -d "$opt_t/_darcs") { # Darcs
print "== Found /_darcs directory\n" if $opt_v;
# v1-4-dirty
@@ -135,19 +137,36 @@ if (open($DST, '+<', $outfile)) {
print "== Current:\n$actual==\n" if $opt_v;
if ($actual eq $output) {
- print "Keeping VCS header $outfile\n $opt_N = \"$opt_V\"\n"
+ close $DST;
+ print "Keeping VCS header $outfile\n",
+ " $opt_N = \"$opt_V\"\n"
unless $opt_q;
exit 0;
}
- print "Updating VCS header $outfile\n $opt_N = \"$opt_V\"\n"
- unless $opt_q;
+
+ # This regexp must match the #define in $output above:
+ $actual =~ m/#define (\w+) ("[^"]*")\n/;
+ if ($opt_i) {
+ print "Outdated VCS header $outfile\n",
+ " has: $1 = $2\n",
+ " needs: $opt_N = \"$opt_V\"\n";
+ }
+ else {
+ print "Updating VCS header $outfile\n",
+ " from: $1 = $2\n",
+ " to: $opt_N = \"$opt_V\"\n"
+ unless $opt_q;
+ }
} else {
- print "Creating VCS header $outfile\n $opt_N = \"$opt_V\"\n"
+ print "Creating VCS header $outfile\n",
+ " $opt_N = \"$opt_V\"\n"
unless $opt_q;
open($DST, '>', $outfile)
or die "Can't create $outfile: $!\n";
}
+if ($opt_i) { exit 1 }; # exit if make is run in "question" mode
+
seek $DST, 0, 0;
truncate $DST, 0;
print $DST $output;
@@ -158,9 +177,11 @@ sub HELP_MESSAGE {
Usage:
genVersionHeader.pl -h
Display this Usage message
- genVersionHeader.pl [-v] [-q] [-t top] [-N NAME] [-V version] output.h";
+ genVersionHeader.pl [-v] [-d] [-q] [-t top] [-N NAME] [-V version] output.h";
Generate or update the header file output.h
-v - Verbose (debugging messages)
+ -d - Dry-run
+ -i - Question mode
-q - Quiet
-t top - Path to the module's top (default '$opt_t')
-N NAME - Macro name to be defined (default '$opt_N')
diff --git a/src/tools/makeTestfile.pl b/src/tools/makeTestfile.pl
index be4648258..7d298768f 100644
--- a/src/tools/makeTestfile.pl
+++ b/src/tools/makeTestfile.pl
@@ -15,18 +15,43 @@
# If the script is given an argument -tap it sets HARNESS_ACTIVE in the
# environment to make the epicsUnitTest code generate strict TAP output.
-# Usage: makeTestfile.pl target.t executable
+# Usage: makeTestfile.pl target.t executable
+# target-arch and host-arch are EPICS build target names (eg. linux-x86)
# target.t is the name of the Perl script to generate
# executable is the name of the file the script runs
use strict;
-my ($target, $exe) = @ARGV;
+my ($TA, $HA, $target, $exe) = @ARGV;
+my $exec;
+
+# Use WINE to run windows target executables on non-windows host
+if( $TA =~ /^win32-x86/ && $HA !~ /^win/ ) {
+ # new deb. derivatives have wine32 and wine64
+ # older have wine and wine64
+ # prefer wine32 if present
+ my $wine32 = "/usr/bin/wine32";
+ $wine32 = "/usr/bin/wine" if ! -x $wine32;
+ $exec = "$wine32 $exe";
+} elsif( $TA =~ /^windows-x64/ && $HA !~ /^win/ ) {
+ $exec = "wine64 $exe";
+
+# Run pc386 test harness w/ QEMU
+} elsif( $TA =~ /^RTEMS-pc386-qemu$/ ) {
+ $exec = "qemu-system-i386 -m 64 -no-reboot -serial stdio -display none -net nic,model=ne2k_pci -net user,restrict=yes -kernel $exe";
+
+# Explicitly fail for other RTEMS targets
+} elsif( $TA =~ /^RTEMS-/ ) {
+ die "$0: I don't know how to create scripts for testing $TA on $HA\n";
+
+} else {
+ $exec = "./$exe";
+}
open(my $OUT, '>', $target) or die "Can't create $target: $!\n";
print $OUT <&2
+exit 1
diff --git a/startup/EpicsHostArch.pl b/startup/EpicsHostArch.pl
deleted file mode 100755
index 09f7ffddd..000000000
--- a/startup/EpicsHostArch.pl
+++ /dev/null
@@ -1,47 +0,0 @@
-eval 'exec perl -S $0 ${1+"$@"}' # -*- Mode: perl -*-
- if $running_under_some_shell; # EpicsHostArch.pl
-#*************************************************************************
-# Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne
-# National Laboratory.
-# Copyright (c) 2002 The Regents of the University of California, as
-# Operator of Los Alamos National Laboratory.
-# EPICS BASE is distributed subject to a Software License Agreement found
-# in file LICENSE that is included with this distribution.
-#*************************************************************************
-
-# Returns the Epics host architecture suitable
-# for assigning to the EPICS_HOST_ARCH variable
-
-use Config;
-use POSIX;
-
-$suffix="";
-$suffix="-".$ARGV[0] if ($ARGV[0] ne "");
-
-$EpicsHostArch = GetEpicsHostArch();
-print "$EpicsHostArch$suffix";
-
-sub GetEpicsHostArch { # no args
- $arch=$Config{'archname'};
- if ($arch =~ /sun4-solaris/) { return "solaris-sparc";
- } elsif ($arch =~ m/i86pc-solaris/) { return "solaris-x86";
- } elsif ($arch =~ m/i[3-6]86-linux/){ return "linux-x86";
- } elsif ($arch =~ m/x86_64-linux/) { return "linux-x86_64";
- } elsif ($arch =~ m/arm-linux/) { return "linux-arm";
- } elsif ($arch =~ m/MSWin32-x86/) { return "win32-x86";
- } elsif ($arch =~ m/MSWin32-x64/) { return "windows-x64";
- } elsif ($arch =~ m/cygwin/) {
- my($kernel, $hostname, $release, $version, $cpu) = POSIX::uname();
- if ($cpu =~ m/x86_64/) { return "cygwin-x86_64"; }
- return "cygwin-x86";
- } elsif ($arch =~ m/darwin/) {
- my($kernel, $hostname, $release, $version, $cpu) = POSIX::uname();
- if ($cpu =~ m/Power Macintosh/) { return "darwin-ppc"; }
- elsif ($cpu =~ m/i386/) { return "darwin-x86"; }
- elsif ($cpu =~ m/x86_64/) { return "darwin-x86"; }
- else { return "unsupported"; }
- } else { return "unsupported"; }
-}
-
-#EOF EpicsHostArch.pl
-
diff --git a/startup/Site.cshrc b/startup/Site.cshrc
deleted file mode 100755
index 24f34abb9..000000000
--- a/startup/Site.cshrc
+++ /dev/null
@@ -1,83 +0,0 @@
-#!/bin/csh -f
-#*************************************************************************
-# Copyright (c) 2002 The University of Chicago, as Operator of Argonne
-# National Laboratory.
-# Copyright (c) 2002 The Regents of the University of California, as
-# Operator of Los Alamos National Laboratory.
-# EPICS BASE Versions 3.13.7
-# and higher are distributed subject to a Software License Agreement found
-# in file LICENSE that is included with this distribution.
-#*************************************************************************
-# Site-specific EPICS environment settings
-#
-# sites should modify these definitions
-
-# Location of epics base
-if ( ! $?EPICS_BASE ) then
- set EPICS_BASE=/usr/local/epics/base
-endif
-
-# Location of epics extensions
-if ( ! $?EPICS_EXTENSIONS ) then
- setenv EPICS_EXTENSIONS /usr/local/epics/extensions
-endif
-
-# Postscript printer definition needed by some extensions (eg medm, dp, dm, ...)
-if ( ! $?PSPRINTTER ) then
- setenv PSPRINTER lp
-endif
-
-# Needed only by medm extension
-#setenv EPICS_DISPLAY_PATH
-# Needed only by medm extension
-setenv BROWSER firefox
-
-# Needed only by orbitscreen extension
-if ( ! $?ORBITSCREENHOME ) then
- setenv ORBITSCREENHOME $EPICS_EXTENSIONS/src/orbitscreen
-endif
-
-# Needed only by adt extension
-if ( ! $?ADTHOME ) then
- setenv ADTHOME /usr/local/oag/apps/src/appconfig/adt
- echo $ADTHOME
-endif
-
-# Needed only by ar extension (archiver)
-setenv EPICS_AR_PORT 7002
-
-# Needed for java extensions
-if ( $?CLASSPATH ) then
- setenv CLASSPATH "${CLASSPATH}:${EPICS_EXTENSIONS}/javalib"
-else
- setenv CLASSPATH "${EPICS_EXTENSIONS}/javalib"
-endif
-
-# Allow private versions of extensions without a bin subdir
-if ( $?EPICS_EXTENSIONS_PVT ) then
- set path = ( $path $EPICS_EXTENSIONS_PVT)
-endif
-
-##################################################################
-
-# Start of set R3.14 environment variables
-
-setenv EPICS_HOST_ARCH `$EPICS_BASE/startup/EpicsHostArch.pl`
-
-# Allow private versions of base
-if ( $?EPICS_BASE_PVT ) then
- if ( -e $EPICS_BASE_PVT/bin/$EPICS_HOST_ARCH ) then
- set path = ( $path $EPICS_BASE_PVT/bin/$EPICS_HOST_ARCH)
- endif
-endif
-
-# Allow private versions of extensions
-if ( $?EPICS_EXTENSIONS_PVT ) then
- if ( -e $EPICS_EXTENSIONS_PVT/bin/$EPICS_HOST_ARCH ) then
- set path = ( $path $EPICS_EXTENSIONS_PVT/bin/$EPICS_HOST_ARCH)
- endif
-endif
-set path = ( $path $EPICS_EXTENSIONS/bin/$EPICS_HOST_ARCH )
-
-# End of set R3.14 environment variables
-##################################################################
diff --git a/startup/Site.profile b/startup/Site.profile
deleted file mode 100755
index 677001401..000000000
--- a/startup/Site.profile
+++ /dev/null
@@ -1,88 +0,0 @@
-#!/bin/sh
-#*************************************************************************
-# Copyright (c) 2002 The University of Chicago, as Operator of Argonne
-# National Laboratory.
-# Copyright (c) 2002 The Regents of the University of California, as
-# Operator of Los Alamos National Laboratory.
-# EPICS BASE Versions 3.13.7
-# and higher are distributed subject to a Software License Agreement found
-# in file LICENSE that is included with this distribution.
-#*************************************************************************
-# Site-specific EPICS environment settings
-#
-# sites should modify these definitions
-
-# Location of epics base
-if [ -z "${MY_EPICS_BASE}" ] ; then
- MY_EPICS_BASE=/usr/local/epics/base
-fi
-
-# Location of epics extensions (medm, msi, etc.)
-if [ -z "${EPICS_EXTENSIONS}" ] ; then
- EPICS_EXTENSIONS=/usr/local/epics/extensions
-fi
-
-# Postscript printer definition needed by some extensions (eg medm, dp, dm, ...)
-if [ -z "${PSPRINTER}" ] ; then
- export PSPRINTER=lp
-fi
-
-#Needed only by the idl and ezcaIDL extensions.
-#export EPICS_EXTENSIONS
-
-# Needed only by medm extension
-#export EPICS_DISPLAY_PATH=/path/to/adl/files
-export BROWSER=firefox
-
-# Needed only by orbitscreen extension
-#if [ -z "${ORBITSCREENHOME}" ] ; then
-# export "ORBITSCREENHOME=${EPICS_EXTENSIONS/src/orbitscreen}"
-#fi
-
-# Needed only by adt extension
-#if [ -z "${ADTHOME}" ] ; then
-# ADTHOME=
-# export ADTHOME
-#fi
-
-# Needed only by ar extension (archiver)
-#EPICS_AR_PORT=7002
-#export EPICS_AR_PORT
-
-# Needed for java extensions
-if [ -z "${CLASSPATH}" ] ; then
- CLASSPATH="${EPICS_EXTENSIONS}/javalib"
-else
- CLASSPATH="${CLASSPATH}:${EPICS_EXTENSIONS}/javalib"
-fi
-export CLASSPATH
-
-# Allow private versions of extensions without a bin subdir
-if [ -n "${EPICS_EXTENSIONS_PVT}" ] ; then
- PATH="${PATH}:${EPICS_EXTENSIONS_PVT}"
-fi
-
-#---------------------------------------------------------------
-# Start of set R3.14 environment variables
-#
-EPICS_HOST_ARCH=`"${MY_EPICS_BASE}"/startup/EpicsHostArch.pl`
-export EPICS_HOST_ARCH
-
-# Allow private versions of base
-if [ -n "${EPICS_BASE_PVT}" ] ; then
- if [ -d "${EPICS_BASE_PVT}/bin/${EPICS_HOST_ARCH}" ]; then
- PATH="${PATH}:${EPICS_BASE_PVT}/bin/${EPICS_HOST_ARCH}"
- fi
-fi
-
-# Allow private versions of extensions
-if [ -n "${EPICS_EXTENSIONS_PVT}" ] ; then
- if [ -d "${EPICS_EXTENSIONS_PVT}/bin/${EPICS_HOST_ARCH}" ]; then
- PATH="${PATH}:${EPICS_EXTENSIONS_PVT}/bin/${EPICS_HOST_ARCH}"
- fi
-fi
-PATH="${PATH}:${EPICS_EXTENSIONS}/bin/${EPICS_HOST_ARCH}"
-
-# End of set R3.14 environment variables
-
-#---------------------------------------------------------------
diff --git a/startup/cygwin.bat b/startup/cygwin.bat
deleted file mode 100755
index ff75b5335..000000000
--- a/startup/cygwin.bat
+++ /dev/null
@@ -1,122 +0,0 @@
-@ECHO OFF
-REM *************************************************************************
-REM Copyright (c) 2002 The University of Chicago, as Operator of Argonne
-REM National Laboratory.
-REM Copyright (c) 2002 The Regents of the University of California, as
-REM Operator of Los Alamos National Laboratory.
-REM EPICS BASE Versions 3.13.7
-REM and higher are distributed subject to a Software License Agreement found
-REM in file LICENSE that is included with this distribution.
-REM *************************************************************************
-REM
-REM Site-specific EPICS environment settings
-REM
-REM sites should modify these definitions
-
-REM ======================================================
-REM ====== REQUIRED ENVIRONMENT VARIABLES FOLLOW ======
-REM ======================================================
-
-REM ======================================================
-REM ---------------- WINDOWS ---------------------------
-REM ======================================================
-REM ----- WIN95 -----
-REM set PATH=C:\WINDOWS;C:\WINDOWS\COMMAND
-REM ----- WINNT, WIN2000 -----
-REM set PATH=C:\WINNT;C:\WINNT\SYSTEM32
-REM ----- WINXP, Vista, Windows 7 -----
-set PATH=C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\SYSTEM32\Wbem
-
-REM ======================================================
-REM ---------------- make and perl ---------------------
-REM ======================================================
-
-REM --------------- ActiveState perl -------------------
-set PATH=C:\Perl\bin;%PATH%
-
-REM --------------- mingw make ------------------------
-REM set PATH=C:\mingw-make\bin;%PATH%
-REM set PATH=C:\mingw-make82-3\bin;%PATH%
-
-REM --------------- gnuwin32 make ----------------------
-set PATH=C:\gnuwin32\bin;%PATH%
-
-REM ======================================================
-REM ---------------- cygwin tools ------------------------
-REM ======================================================
-REM (make & perl if above perl and make are REMs)
-REM Dont use cygwin GNU make and Perl!
-REM cygwin contains tk/tcl, vim, perl, and many unix tools
-REM need grep from here NOT from cvs directory
-set PATH=%PATH%;.;..
-set PATH=%PATH%;c:\cygwin\bin
-
-REM ======================================================
-REM --------------- EPICS --------------------------------
-REM ======================================================
-set EPICS_HOST_ARCH=cygwin-x86
-set PATH=%PATH%;G:\epics\base\bin\%EPICS_HOST_ARCH%
-set PATH=%PATH%;G:\epics\extensions\bin\%EPICS_HOST_ARCH%
-
-REM ======================================================
-REM ------- OPTIONAL ENVIRONMENT VARIABLES FOLLOW --------
-REM ======================================================
-
-REM ======================================================
-REM ----------------- remote CVS -------------------------
-REM ======================================================
-REM set CVS_RSH=c:/cygwin/bin/ssh.exe
-REM set CVSROOT=:ext:jba@aps.anl.gov:/usr/local/epicsmgr/cvsroot
-REM set HOME=c:/users/%USERNAME%
-REM set HOME=c:/users/jba
-
-REM ======================================================
-REM ------------------- Bazaar ---------------------------
-REM ======================================================
-set PATH=%PATH%;C:\Program files\Bazaar
-
-REM ======================================================
-REM ----------------- GNU make flags ---------------------
-REM ======================================================
-set MAKEFLAGS=-w
-
-REM ======================================================
-REM -------------- vim (use cygwin vim ) -----------------
-REM ======================================================
-REM HOME needed by vim to write .viminfo file.
-REM VIM needed by vim to find _vimrc file.
-REM set VIM=c:\cygwin
-
-REM ======================================================
-REM --------------- Epics Channel Access -----------------
-REM Modify and uncomment the following lines
-REM to override the base/configure/CONFIG_ENV defaults
-REM ======================================================
-REM set EPICS_CA_ADDR_LIST=n.n.n.n n.n.n.n
-REM set EPICS_CA_AUTO_ADDR_LIST=YES
-
-REM set EPICS_CA_CONN_TMO=30.0
-REM set EPICS_CA_BEACON_PERIOD=15.0
-REM set EPICS_CA_REPEATER_PORT=5065
-REM set EPICS_CA_SERVER_PORT=5064
-REM set EPICS_TS_MIN_WEST=420
-
-REM ======================================================
-REM --------------- JAVA ---------------------------------
-REM ======================================================
-REM Needed for java extensions
-REM set CLASSPATH=G:\epics\extensions\javalib
-REM set PATH=%PATH%;C:\j2sdk1.4.1_01\bin
-REM set CLASSPATH=%CLASSPATH%;C:\j2sdk1.4.1_01\lib\tools.jar
-
-REM ======================================================
-REM --------------- Exceed -------------------------------
-REM Needed for X11 extensions
-REM ======================================================
-REM set EX_VER=7.10
-REM set EX_VER=12.00
-REM set EX_VER=14.00
-REM set PATH=%PATH%;C:\Exceed%EX_VER%\XDK\
-REM set PATH=%PATH%;C:\Program Files\Hummingbird\Connectivity\%EX_VER%\Exceed\
-
-
diff --git a/startup/unix.csh b/startup/unix.csh
new file mode 100644
index 000000000..788a639e6
--- /dev/null
+++ b/startup/unix.csh
@@ -0,0 +1,96 @@
+#*************************************************************************
+# Copyright (c) 2002 The University of Chicago, as Operator of Argonne
+# National Laboratory.
+# Copyright (c) 2002 The Regents of the University of California, as
+# Operator of Los Alamos National Laboratory.
+# EPICS BASE Versions 3.13.7
+# and higher are distributed subject to a Software License Agreement found
+# in file LICENSE that is included with this distribution.
+#*************************************************************************
+#
+# Site-specific EPICS environment settings
+#
+# Attempts to set EPICS_HOST_ARCH. Optionally, adds the EPICS Base
+# install host architecture bin directory to PATH.
+#
+
+#-----------------------------------------------------------------------
+# Site serviceable parts (These definitions may be modified)
+#-----------------------------------------------------------------------
+
+# Automatically set up the environment when possible ("yes" or "no").
+# If set to yes, as much of the environment will be set up as possible.
+# If set to no, just the minimum environment will be set up. More
+# specific _auto_* definitions take precedence over this definition.
+set _auto=no
+
+# Automatically append to PATH ("yes" or "no"). If set to yes, the
+# EPICS Base install host architecture bin directory will be added to
+# PATH if possible. If set to no, the bin directory will not be added
+# to PATH.
+set _auto_path_append=$_auto
+
+# The program used to run Perl scripts (pathname).
+set _perl_prog=perl
+
+# The EPICS host architecture specification for EPICS_HOST_ARCH
+# (-[-] as defined in configure/CONFIG_SITE). If
+# nonempty, the value will be used as the value of EPICS_HOST_ARCH. If
+# empty, an attempt will be made to automatically determine the value
+# with EpicsHostArch.pl.
+set _epics_host_arch=
+
+# The install location of EPICS Base (pathname). If nonempty, the
+# EpicsHostArch.pl script from it, if it exists, will be used to
+# determine EPICS_HOST_ARCH. If nonempty and EPICS_HOST_ARCH was
+# determined successfully, it will be used to add the host architecture
+# bin directory to PATH if _auto_path_append is yes.
+set _epics_base=
+
+# The source location of EPICS Base (pathname). If nonempty, the
+# EpicsHostArch.pl script from it, if it exists and _epics_base is empty
+# or it did not exist in the _epics_base location, will be used to
+# determine EPICS_HOST_ARCH.
+set _epics_base_src=
+
+#-----------------------------------------------------------------------
+# Internal parts (There is typically no need to modify these)
+#-----------------------------------------------------------------------
+
+# Define the possible locations of EpicsHostArch.pl
+set _epics_host_arch_pl=
+set _src_epics_host_arch_pl=
+if ("$_epics_base" != '') then
+ set _epics_host_arch_pl="$_epics_base/lib/perl/EpicsHostArch.pl"
+endif
+if ("$_epics_base_src" != '') then
+ set _src_epics_host_arch_pl="$_epics_base_src/src/tools/EpicsHostArch.pl"
+endif
+
+# Set the EPICS host architecture specification
+if ("$_epics_host_arch" != '') then
+ setenv EPICS_HOST_ARCH "$_epics_host_arch"
+else if (-e "$_epics_host_arch_pl") then
+ set _epics_host_arch=`"$_perl_prog" "$_epics_host_arch_pl"`
+ setenv EPICS_HOST_ARCH "$_epics_host_arch"
+else if (-e "$_src_epics_host_arch_pl") then
+ set _epics_host_arch=`"$_perl_prog" "$_src_epics_host_arch_pl"`
+ setenv EPICS_HOST_ARCH "$_epics_host_arch"
+endif
+
+# Add the EPICS Base host architecture bin directory to PATH
+if ("$_auto_path_append" == yes) then
+ if ("$_epics_base" != '' && "$_epics_host_arch" != '') then
+ setenv PATH "${PATH}:$_epics_base/bin/$_epics_host_arch"
+ endif
+endif
+
+# Don't leak variables into the environment
+unset _auto
+unset _auto_path_append
+unset _perl_prog
+unset _epics_host_arch
+unset _epics_base
+unset _epics_base_src
+unset _epics_host_arch_pl
+unset _src_epics_host_arch_pl
diff --git a/startup/unix.sh b/startup/unix.sh
new file mode 100644
index 000000000..a8d8328ec
--- /dev/null
+++ b/startup/unix.sh
@@ -0,0 +1,100 @@
+#*************************************************************************
+# Copyright (c) 2002 The University of Chicago, as Operator of Argonne
+# National Laboratory.
+# Copyright (c) 2002 The Regents of the University of California, as
+# Operator of Los Alamos National Laboratory.
+# EPICS BASE Versions 3.13.7
+# and higher are distributed subject to a Software License Agreement found
+# in file LICENSE that is included with this distribution.
+#*************************************************************************
+#
+# Site-specific EPICS environment settings
+#
+# Attempts to set EPICS_HOST_ARCH. Optionally, adds the EPICS Base
+# install host architecture bin directory to PATH.
+#
+
+#-----------------------------------------------------------------------
+# Site serviceable parts (These definitions may be modified)
+#-----------------------------------------------------------------------
+
+# Automatically set up the environment when possible ("yes" or "no").
+# If set to yes, as much of the environment will be set up as possible.
+# If set to no, just the minimum environment will be set up. More
+# specific _auto_* definitions take precedence over this definition.
+_auto=no
+
+# Automatically append to PATH ("yes" or "no"). If set to yes, the
+# EPICS Base install host architecture bin directory will be added to
+# PATH if possible. If set to no, the bin directory will not be added
+# to PATH.
+_auto_path_append=$_auto
+
+# The program used to run Perl scripts (pathname).
+_perl_prog=perl
+
+# The EPICS host architecture specification for EPICS_HOST_ARCH
+# (-[-] as defined in configure/CONFIG_SITE). If
+# nonempty, the value will be used as the value of EPICS_HOST_ARCH. If
+# empty, an attempt will be made to automatically determine the value
+# with EpicsHostArch.pl.
+_epics_host_arch=
+
+# The install location of EPICS Base (pathname). If nonempty, the
+# EpicsHostArch.pl script from it, if it exists, will be used to
+# determine EPICS_HOST_ARCH. If nonempty and EPICS_HOST_ARCH was
+# determined successfully, it will be used to add the host architecture
+# bin directory to PATH if _auto_path_append is yes.
+_epics_base=
+
+# The source location of EPICS Base (pathname). If nonempty, the
+# EpicsHostArch.pl script from it, if it exists and _epics_base is empty
+# or it did not exist in the _epics_base location, will be used to
+# determine EPICS_HOST_ARCH.
+_epics_base_src=
+
+#-----------------------------------------------------------------------
+# Internal parts (There is typically no need to modify these)
+#-----------------------------------------------------------------------
+
+# Define the possible locations of EpicsHostArch.pl
+_epics_host_arch_pl=
+_src_epics_host_arch_pl=
+if [ -n "$_epics_base" ]; then
+ _epics_host_arch_pl="$_epics_base/lib/perl/EpicsHostArch.pl"
+fi
+if [ -n "$_epics_base_src" ]; then
+ _src_epics_host_arch_pl="$_epics_base_src/src/tools/EpicsHostArch.pl"
+fi
+
+# Set the EPICS host architecture specification
+if [ -n "$_epics_host_arch" ]; then
+ EPICS_HOST_ARCH=$_epics_host_arch
+ export EPICS_HOST_ARCH
+elif [ -e "$_epics_host_arch_pl" ]; then
+ _epics_host_arch=$("$_perl_prog" "$_epics_host_arch_pl")
+ EPICS_HOST_ARCH=$_epics_host_arch
+ export EPICS_HOST_ARCH
+elif [ -e "$_src_epics_host_arch_pl" ]; then
+ _epics_host_arch=$("$_perl_prog" "$_src_epics_host_arch_pl")
+ EPICS_HOST_ARCH=$_epics_host_arch
+ export EPICS_HOST_ARCH
+fi
+
+# Add the EPICS Base host architecture bin directory to PATH
+if [ "$_auto_path_append" = yes ]; then
+ if [ -n "$_epics_base" ] && [ -n "$_epics_host_arch" ]; then
+ PATH="$PATH:$_epics_base/bin/$_epics_host_arch"
+ export PATH
+ fi
+fi
+
+# Don't leak variables into the environment
+unset _auto
+unset _auto_path_append
+unset _perl_prog
+unset _epics_host_arch
+unset _epics_base
+unset _epics_base_src
+unset _epics_host_arch_pl
+unset _src_epics_host_arch_pl
diff --git a/startup/win32.bat b/startup/win32.bat
index af9155dd7..6652fc97c 100755
--- a/startup/win32.bat
+++ b/startup/win32.bat
@@ -1,147 +1,105 @@
-@ECHO OFF
-REM *************************************************************************
-REM Copyright (c) 2002 The University of Chicago, as Operator of Argonne
-REM National Laboratory.
-REM Copyright (c) 2002 The Regents of the University of California, as
-REM Operator of Los Alamos National Laboratory.
-REM EPICS BASE Versions 3.13.7
-REM and higher are distributed subject to a Software License Agreement found
-REM in file LICENSE that is included with this distribution.
-REM *************************************************************************
-REM
-REM Site-specific EPICS environment settings
-REM
-REM sites should modify these definitions
-
-REM ======================================================
-REM ====== REQUIRED ENVIRONMENT VARIABLES FOLLOW ======
-REM ======================================================
-
-REM ======================================================
-REM ---------------- WINDOWS ---------------------------
-REM ======================================================
-REM ----- WIN95 -----
-REM set PATH=C:\WINDOWS;C:\WINDOWS\COMMAND
-REM ----- WINNT, WIN2000 -----
-REM set PATH=C:\WINNT;C:\WINNT\SYSTEM32
-REM ----- WINXP, Vista, Windows 7 -----
-set PATH=C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\SYSTEM32\Wbem
-
-REM ======================================================
-REM ---------------- make and perl ---------------------
-REM ======================================================
-
-REM --------------- ActiveState perl -------------------
-set PATH=C:\Perl\bin;%PATH%
-
-REM --------------- mingw make ------------------------
-REM set PATH=C:\mingw-make\bin;%PATH%
-REM set PATH=C:\mingw-make82-3\bin;%PATH%
-
-REM --------------- gnuwin32 make ----------------------
-set PATH=C:\gnuwin32\bin;%PATH%
-
-REM ======================================================
-REM ---------------- cygwin tools ------------------------
-REM ======================================================
-REM (make & perl if above perl and make are REMs)
-REM Dont use cygwin GNU make and Perl!
-REM cygwin contains tk/tcl, vim, perl, and many unix tools
-REM need grep from here NOT from cvs directory
-REM set PATH=%PATH%;.;..
-REM set PATH=%PATH%;c:\cygwin\bin
-
-REM ======================================================
-REM --------------- Visual c++ -------------------------
-REM ======================================================
-
-REM ------ Microsoft Visual Studio 2005 ------
-REM call "C:\Program files\Microsoft Visual Studio 8\VC\vcvarsall.bat" x86_amd64
-REM set PATH=%PATH%;C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin
-REM set INCLUDE=%INCLUDE%;C:\Program Files\Microsoft SDKs\Windows\v6.0A\include
-REM REM set LIBPATH=%LIBPATH%;C:\Program Files\Microsoft SDKs\Windows\v6.0A\lib
-REM set LIB=%LIB%;C:\Program Files\Microsoft SDKs\Windows\v6.0A\lib
-
-REM ------ Microsoft Visual Studio 2008 ------
-REM call "C:\Program files\Microsoft Visual Studio 9.0\VC\bin\vcvars32.bat"
-REM call "C:\Program files\Microsoft Visual Studio 9.0\VC\vcvarsall.bat" x86_amd64
-REM set PATH=C:\Program Files\Microsoft SDKs\Windows\v7.0\bin;%PATH%
-REM set INCLUDE=C:\Program Files\Microsoft SDKs\Windows\v7.0\include;%INCLUDE%
-REM set LIBPATH=C:\Program Files\Microsoft SDKs\Windows\v7.0\lib;%LIBPATH%
-REM set LIB=C:\Program Files\Microsoft SDKs\Windows\v7.0\lib;%LIB%
-
-REM ----- Visual Studion 2010 -----
-REM -- windows-x64 ---
-REM call "C:\Program files\Microsoft Visual Studio 10.0\VC\vcvarsall.bat" x64
-REM -- win32-x86 ---
-call "C:\Program files\Microsoft Visual Studio 10.0\VC\vcvarsall.bat" x86
-
-REM ======================================================
-REM --------------- EPICS --------------------------------
-REM ======================================================
-REM set EPICS_HOST_ARCH=windows-x64
-set EPICS_HOST_ARCH=win32-x86
-set PATH=%PATH%;G:\epics\base\bin\%EPICS_HOST_ARCH%
-set PATH=%PATH%;G:\epics\extensions\bin\%EPICS_HOST_ARCH%
-
-REM ======================================================
-REM ------- OPTIONAL ENVIRONMENT VARIABLES FOLLOW --------
-REM ======================================================
-
-REM ======================================================
-REM ----------------- remote CVS -------------------------
-REM ======================================================
-REM set CVS_RSH=c:/cygwin/bin/ssh.exe
-REM set CVSROOT=:ext:jba@aps.anl.gov:/usr/local/epicsmgr/cvsroot
-REM set HOME=c:/users/%USERNAME%
-REM set HOME=c:/users/jba
-
-REM ======================================================
-REM ------------------- Bazaar ---------------------------
-REM ======================================================
-set PATH=%PATH%;C:\Program files\Bazaar
-
-REM ======================================================
-REM ----------------- GNU make flags ---------------------
-REM ======================================================
-set MAKEFLAGS=-w
-
-REM ======================================================
-REM -------------- vim (use cygwin vim ) -----------------
-REM ======================================================
-REM HOME needed by vim to write .viminfo file.
-REM VIM needed by vim to find _vimrc file.
-REM set VIM=c:\cygwin
-
-REM ======================================================
-REM --------------- Epics Channel Access -----------------
-REM Modify and uncomment the following lines
-REM to override the base/configure/CONFIG_ENV defaults
-REM ======================================================
-REM set EPICS_CA_ADDR_LIST=n.n.n.n n.n.n.n
-REM set EPICS_CA_AUTO_ADDR_LIST=YES
-
-REM set EPICS_CA_CONN_TMO=30.0
-REM set EPICS_CA_BEACON_PERIOD=15.0
-REM set EPICS_CA_REPEATER_PORT=5065
-REM set EPICS_CA_SERVER_PORT=5064
-REM set EPICS_TS_MIN_WEST=420
-
-REM ======================================================
-REM --------------- JAVA ---------------------------------
-REM ======================================================
-REM Needed for java extensions
-REM set CLASSPATH=G:\epics\extensions\javalib
-REM set PATH=%PATH%;C:\j2sdk1.4.1_01\bin
-REM set CLASSPATH=%CLASSPATH%;C:\j2sdk1.4.1_01\lib\tools.jar
-
-REM ======================================================
-REM --------------- Exceed -------------------------------
-REM Needed for X11 extensions
-REM ======================================================
-REM set EX_VER=7.10
-REM set EX_VER=12.00
-REM set EX_VER=14.00
-REM set PATH=%PATH%;C:\Exceed%EX_VER%\XDK\
-REM set PATH=%PATH%;C:\Program Files\Hummingbird\Connectivity\%EX_VER%\Exceed\
-
+@echo off
+rem *************************************************************************
+rem Copyright (c) 2017 UChicago Argonne LLC, as Operator of Argonne
+rem National Laboratory.
+rem Copyright (c) 2002 The Regents of the University of California, as
+rem Operator of Los Alamos National Laboratory.
+rem EPICS BASE is distributed subject to a Software License Agreement found
+rem in file LICENSE that is included with this distribution.
+rem *************************************************************************
+rem
+rem Site-specific EPICS environment settings
+rem
+rem Sets EPICS_HOST_ARCH and the environment for Microsoft Visual Studio.
+rem Optionally, resets PATH, adds Strawberry Perl to PATH, and adds the
+rem EPICS Base install host architecture bin directory to PATH.
+rem
+
+rem ----------------------------------------------------------------------
+rem Site serviceable parts (These definitions may be modified)
+rem ----------------------------------------------------------------------
+
+rem The values of the definitions in this section must not contain
+rem double-quotes.
+rem
+rem * Right: set _foo=C:\foo
+rem * Right: set "_foo=C:\foo"
+rem * Wrong: set _foo="C:\foo"
+
+rem Automatically set up the environment when possible ("yes" or "no").
+rem If set to yes, as much of the environment will be set up as possible.
+rem If set to no, just the minimum environment will be set up. More
+rem specific _auto_* definitions take precedence over this definition.
+set _auto=no
+
+rem Automatically reset PATH ("yes" or "no"). If set to yes, PATH will
+rem be reset to the value of _path_new. If set to no, PATH will not be
+rem reset.
+set _auto_path_reset=%_auto%
+
+rem Automatically append to PATH ("yes" or "no"). If set to yes, the
+rem EPICS Base install host architecture bin directory will be added to
+rem PATH if possible. If set to no, the bin directory will not be added
+rem to PATH.
+set _auto_path_append=%_auto%
+
+rem The new value for PATH. If _auto_path_reset is yes, PATH will be set
+rem to it.
+set _path_new=C:\Windows\System32;C:\Windows;C:\Windows\System32\wbem
+
+rem The location of Strawberry Perl (pathname). If empty, Strawberry Perl
+rem is assumed to already be in PATH and will not be added. If nonempty,
+rem Strawberry Perl will be added to PATH.
+set _strawberry_perl_home=C:\Strawberry
+
+rem The location of Microsoft Visual Studio (pathname).
+set _visual_studio_home=C:\Program Files (x86)\Microsoft Visual Studio 14.0
+
+rem The EPICS host architecture specification for EPICS_HOST_ARCH
+rem (-[-] as defined in configure/CONFIG_SITE).
+set _epics_host_arch=win32-x86
+
+rem The install location of EPICS Base (pathname). If nonempty and
+rem _auto_path_append is yes, it will be used to add the host architecture
+rem bin directory to PATH.
+set _epics_base=
+
+rem ----------------------------------------------------------------------
+rem Internal parts (There is typically no need to modify these)
+rem ----------------------------------------------------------------------
+
+rem Reset PATH
+if "%_auto_path_reset%" == "yes" (
+ set "PATH=%_path_new%"
+)
+
+rem Add Strawberry Perl to PATH
+if "%_strawberry_perl_home%" == "" goto after_add_strawberry_perl
+rem Can't do this inside parentheses because PATH would be read only once
+set "PATH=%PATH%;%_strawberry_perl_home%\c\bin"
+set "PATH=%PATH%;%_strawberry_perl_home%\perl\site\bin"
+set "PATH=%PATH%;%_strawberry_perl_home%\perl\bin"
+:after_add_strawberry_perl
+
+rem Set the environment for Microsoft Visual Studio
+call "%_visual_studio_home%\VC\vcvarsall.bat" x86
+
+rem Set the EPICS host architecture specification
+set "EPICS_HOST_ARCH=%_epics_host_arch%"
+
+rem Add the EPICS Base host architecture bin directory to PATH
+if "%_auto_path_append%" == "yes" (
+ if not "%_epics_base%" == "" (
+ set "PATH=%PATH%;%_epics_base%\bin\%_epics_host_arch%"
+ )
+)
+
+rem Don't leak variables into the environment
+set _auto=
+set _auto_path_reset=
+set _auto_path_append=
+set _path_new=
+set _strawberry_perl_home=
+set _visual_studio_home=
+set _epics_host_arch=
+set _epics_base=
diff --git a/startup/windows.bat b/startup/windows.bat
new file mode 100644
index 000000000..877c0d5a9
--- /dev/null
+++ b/startup/windows.bat
@@ -0,0 +1,105 @@
+@echo off
+rem *************************************************************************
+rem Copyright (c) 2017 UChicago Argonne LLC, as Operator of Argonne
+rem National Laboratory.
+rem Copyright (c) 2002 The Regents of the University of California, as
+rem Operator of Los Alamos National Laboratory.
+rem EPICS BASE is distributed subject to a Software License Agreement found
+rem in file LICENSE that is included with this distribution.
+rem *************************************************************************
+rem
+rem Site-specific EPICS environment settings
+rem
+rem Sets EPICS_HOST_ARCH and the environment for Microsoft Visual Studio.
+rem Optionally, resets PATH, adds Strawberry Perl to PATH, and adds the
+rem EPICS Base install host architecture bin directory to PATH.
+rem
+
+rem ----------------------------------------------------------------------
+rem Site serviceable parts (These definitions may be modified)
+rem ----------------------------------------------------------------------
+
+rem The values of the definitions in this section must not contain
+rem double-quotes.
+rem
+rem * Right: set _foo=C:\foo
+rem * Right: set "_foo=C:\foo"
+rem * Wrong: set _foo="C:\foo"
+
+rem Automatically set up the environment when possible ("yes" or "no").
+rem If set to yes, as much of the environment will be set up as possible.
+rem If set to no, just the minimum environment will be set up. More
+rem specific _auto_* definitions take precedence over this definition.
+set _auto=no
+
+rem Automatically reset PATH ("yes" or "no"). If set to yes, PATH will
+rem be reset to the value of _path_new. If set to no, PATH will not be
+rem reset.
+set _auto_path_reset=%_auto%
+
+rem Automatically append to PATH ("yes" or "no"). If set to yes, the
+rem EPICS Base install host architecture bin directory will be added to
+rem PATH if possible. If set to no, the bin directory will not be added
+rem to PATH.
+set _auto_path_append=%_auto%
+
+rem The new value for PATH. If _auto_path_reset is yes, PATH will be set
+rem to it.
+set _path_new=C:\Windows\System32;C:\Windows;C:\Windows\System32\wbem
+
+rem The location of Strawberry Perl (pathname). If empty, Strawberry Perl
+rem is assumed to already be in PATH and will not be added. If nonempty,
+rem Strawberry Perl will be added to PATH.
+set _strawberry_perl_home=C:\Strawberry
+
+rem The location of Microsoft Visual Studio (pathname).
+set _visual_studio_home=C:\Program Files (x86)\Microsoft Visual Studio 14.0
+
+rem The EPICS host architecture specification for EPICS_HOST_ARCH
+rem (-[-] as defined in configure/CONFIG_SITE).
+set _epics_host_arch=windows-x64
+
+rem The install location of EPICS Base (pathname). If nonempty and
+rem _auto_path_append is yes, it will be used to add the host architecture
+rem bin directory to PATH.
+set _epics_base=
+
+rem ----------------------------------------------------------------------
+rem Internal parts (There is typically no need to modify these)
+rem ----------------------------------------------------------------------
+
+rem Reset PATH
+if "%_auto_path_reset%" == "yes" (
+ set "PATH=%_path_new%"
+)
+
+rem Add Strawberry Perl to PATH
+if "%_strawberry_perl_home%" == "" goto after_add_strawberry_perl
+rem Can't do this inside parentheses because PATH would be read only once
+set "PATH=%PATH%;%_strawberry_perl_home%\c\bin"
+set "PATH=%PATH%;%_strawberry_perl_home%\perl\site\bin"
+set "PATH=%PATH%;%_strawberry_perl_home%\perl\bin"
+:after_add_strawberry_perl
+
+rem Set the environment for Microsoft Visual Studio
+call "%_visual_studio_home%\VC\vcvarsall.bat" x64
+
+rem Set the EPICS host architecture specification
+set "EPICS_HOST_ARCH=%_epics_host_arch%"
+
+rem Add the EPICS Base host architecture bin directory to PATH
+if "%_auto_path_append%" == "yes" (
+ if not "%_epics_base%" == "" (
+ set "PATH=%PATH%;%_epics_base%\bin\%_epics_host_arch%"
+ )
+)
+
+rem Don't leak variables into the environment
+set _auto=
+set _auto_path_reset=
+set _auto_path_append=
+set _path_new=
+set _strawberry_perl_home=
+set _visual_studio_home=
+set _epics_host_arch=
+set _epics_base=