diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
new file mode 100644
index 000000000..9b588f5c2
--- /dev/null
+++ b/.github/CODEOWNERS
@@ -0,0 +1,2 @@
+/.github/CODEOWNERS @epics-base/core-devs
+/documentation/ @epics-base/documentation
diff --git a/configure/RULES_ARCHS b/configure/RULES_ARCHS
index 0d4d944d4..2346e1b13 100644
--- a/configure/RULES_ARCHS
+++ b/configure/RULES_ARCHS
@@ -64,6 +64,9 @@ $(BUILD_ARCHS) : % : O.% O.Common
$(ACTIONS) : % : $(foreach arch, $(BUILD_ARCHS), %$(DIVIDER)$(arch))
$(buildDirs):
+ $(if $(wildcard $(CONFIG)/os/CONFIG.Common.$(archPart) \
+ $(CONFIG)/os/CONFIG.$(EPICS_HOST_ARCH).$(archPart)),, \
+ $(error Base not configured to build target $(archPart)))
$(PERL) $(TOOLS)/makeMakefile.pl $@ $(TOP)/..
O.Common:
diff --git a/documentation/Makefile.headers b/documentation/Makefile.headers
index bc14cf525..136a6dffc 100644
--- a/documentation/Makefile.headers
+++ b/documentation/Makefile.headers
@@ -83,6 +83,7 @@ database_HEADERS += dbUnitTest
database_HEADERS += devSup
database_HEADERS += drvSup
database_HEADERS += initHooks
+database_HEADERS += iocInit
record_HEADERS += aaiRecord
record_HEADERS += aaoRecord
diff --git a/documentation/new-notes/PR-812.md b/documentation/new-notes/PR-812.md
new file mode 100644
index 000000000..a15eabcca
--- /dev/null
+++ b/documentation/new-notes/PR-812.md
@@ -0,0 +1,16 @@
+### Expose `dbCreateRecord` in IOC shell
+
+Add a new IOC shell command `dbCreateRecord` allowing for records to be created
+from the shell.
+
+Only works when called before `iocInit` and will fail if called afterwards.
+
+The pdbbase variable, a record type, and the new record's name should be passed
+as arguments.
+
+Example usage:
+
+```sh
+dbCreateRecord(pdbbase, "ai", "example:ai")
+iocInit()
+```
diff --git a/modules/ca/src/client/CAref.html b/modules/ca/src/client/CAref.html
index 9b5c95bb3..3d8568e9b 100644
--- a/modules/ca/src/client/CAref.html
+++ b/modules/ca/src/client/CAref.html
@@ -2751,7 +2751,7 @@ time.
Arguments
PVNAME
- - A nil terminated process variable name string. EPICS process control
+
- A null terminated process variable name string. EPICS process control
function block database variable names are of the form "<record
name>.<field name>". If the field name and the period separator
are omitted then the "VAL" field is implicit. For example "RFHV01" and
diff --git a/modules/ca/src/client/caRepeater.cpp b/modules/ca/src/client/caRepeater.cpp
index c8385ba3b..5d7695057 100644
--- a/modules/ca/src/client/caRepeater.cpp
+++ b/modules/ca/src/client/caRepeater.cpp
@@ -47,19 +47,21 @@
static void usage(char* argv[])
{
- fprintf(stderr, "Usage: %s -hv\n"
+ fprintf(stderr, "Usage: %s -hdv\n"
"\n"
" -h - Print this message\n"
- " -v - Do not replace stdin/out/err with /dev/null\n",
+ " -d - Increment debug level and set verbose\n"
+ " -v - Verbose, don't suppress messages\n",
argv[0]);
}
int main(int argc, char* argv[])
{
+ int debug = 0;
bool detachinout = true;
int opt;
- while ((opt = getopt(argc, argv, "hv")) != -1) {
+ while ((opt = getopt(argc, argv, "dhv")) != -1) {
switch (opt) {
default:
usage(argv);
@@ -68,6 +70,9 @@ int main(int argc, char* argv[])
case 'h':
usage(argv);
return 0;
+ case 'd':
+ debug++;
+ // Fall through
case 'v':
detachinout = false;
break;
@@ -91,7 +96,7 @@ int main(int argc, char* argv[])
#endif
(void)! chdir ( "/" );
- ca_repeater ();
+ ca_repeater ( debug );
return ( 0 );
}
diff --git a/modules/ca/src/client/cadef.h b/modules/ca/src/client/cadef.h
index d03328ed1..65afb5fee 100644
--- a/modules/ca/src/client/cadef.h
+++ b/modules/ca/src/client/cadef.h
@@ -465,7 +465,7 @@ typedef unsigned capri;
*
* See `caExample.c` in the example application created by `makeBaseApp.pl`.
*
- * \param[in] pChanName A nil terminated process variable name string.
+ * \param[in] pChanName A null terminated process variable name string.
* \parblock
* EPICS process control * function block database variable names are of the
* form `.`. If the field name and the period
diff --git a/modules/ca/src/client/db_access.h b/modules/ca/src/client/db_access.h
index 62d693bea..75debf0b5 100644
--- a/modules/ca/src/client/db_access.h
+++ b/modules/ca/src/client/db_access.h
@@ -128,7 +128,7 @@ LIBCA_API extern const int epicsTypeToDBR_XXXX [lastEpicsType+1];
LIBCA_API extern const epicsType DBR_XXXXToEpicsType [LAST_BUFFER_TYPE+1];
/* values returned for each field type
- * DBR_STRING returns a NULL terminated string
+ * DBR_STRING returns a null terminated string
* DBR_SHORT returns an unsigned short
* DBR_INT returns an unsigned short
* DBR_FLOAT returns an IEEE floating point value
diff --git a/modules/ca/src/client/repeater.cpp b/modules/ca/src/client/repeater.cpp
index c7b019051..9ccf071e3 100644
--- a/modules/ca/src/client/repeater.cpp
+++ b/modules/ca/src/client/repeater.cpp
@@ -70,6 +70,7 @@
#include "taskwd.h"
#include "errlog.h"
+#define DEBUG // Tell iocinf.h to define debugPrint
#include "iocinf.h"
#include "caProto.h"
#include "udpiiu.h"
@@ -85,6 +86,8 @@ static tsDLList < repeaterClient > client_list;
static const unsigned short PORT_ANY = 0u;
+static int debug = 0;
+
/*
* makeSocket()
*/
@@ -128,10 +131,10 @@ static int makeSocket ( unsigned short port, bool reuseAddr, SOCKET * pSock )
repeaterClient::repeaterClient ( const osiSockAddr &fromIn ) :
from ( fromIn ), sock ( INVALID_SOCKET )
{
-#ifdef DEBUG
- unsigned port = ntohs ( from.ia.sin_port );
- debugPrintf ( ( "new client %u\n", port ) );
-#endif
+ if ( debug ) {
+ unsigned port = ntohs ( from.ia.sin_port );
+ debugPrintf ( ( "New client on port %u\n", port ) );
+ }
}
bool repeaterClient::connect ()
@@ -193,19 +196,19 @@ bool repeaterClient::sendMessage ( const void *pBuf, unsigned bufSize )
status = send ( this->sock, (char *) pBuf, bufSize, 0 );
if ( status >= 0 ) {
assert ( static_cast ( status ) == bufSize );
-#ifdef DEBUG
- epicsUInt16 port = ntohs ( this->from.ia.sin_port );
- debugPrintf ( ("Sent to %u\n", port ) );
-#endif
+ if ( debug > 1 ) {
+ epicsUInt16 port = ntohs ( this->from.ia.sin_port );
+ debugPrintf ( ("Sent to port %u\n", port ) );
+ }
return true;
}
else {
int errnoCpy = SOCKERRNO;
if ( errnoCpy == SOCK_ECONNREFUSED ) {
-#ifdef DEBUG
- epicsUInt16 port = ntohs ( this->from.ia.sin_port );
- debugPrintf ( ("Client refused message %u\n", port ) );
-#endif
+ if ( debug ) {
+ epicsUInt16 port = ntohs ( this->from.ia.sin_port );
+ debugPrintf ( ("Client on port %u refused message\n", port ) );
+ }
}
else {
char sockErrBuf[64];
@@ -221,10 +224,10 @@ repeaterClient::~repeaterClient ()
if ( this->sock != INVALID_SOCKET ) {
epicsSocketDestroy ( this->sock );
}
-#ifdef DEBUG
- epicsUInt16 port = ntohs ( this->from.ia.sin_port );
- debugPrintf ( ( "Deleted client %u\n", port ) );
-#endif
+ if ( debug ) {
+ epicsUInt16 port = ntohs ( this->from.ia.sin_port );
+ debugPrintf ( ( "Deleted client on port %u\n", port ) );
+ }
}
void repeaterClient::operator delete ( void * )
@@ -286,6 +289,10 @@ bool repeaterClient::verify ()
if ( sockerrno == SOCK_EADDRINUSE ) {
// Normal result, client using port
+ if ( debug > 1 ) {
+ epicsUInt16 port = ntohs ( this->from.ia.sin_port );
+ debugPrintf ( ("Client on port %u is alive\n", port ) );
+ }
return true;
}
@@ -321,6 +328,9 @@ static void verifyClients ( tsFreeList < repeaterClient, 0x20 > & freeList )
freeList.release ( pclient );
}
}
+ if ( debug ) {
+ debugPrintf ( ( "Verified %u active clients\n", theClients.count() ) );
+ }
client_list.add ( theClients );
}
@@ -444,11 +454,11 @@ static void register_new_client ( osiSockAddr & from,
client_list.remove ( *pNewClient );
pNewClient->~repeaterClient ();
freeList.release ( pNewClient );
-# ifdef DEBUG
+ if ( debug ) {
epicsUInt16 port = ntohs ( from.ia.sin_port );
- debugPrintf ( ( "Deleted repeater client=%u (error while sending ack)\n",
+ debugPrintf ( ( "Deleted repeater client on port %u, error sending ack\n",
port ) );
-# endif
+ }
}
/*
@@ -480,7 +490,7 @@ static void register_new_client ( osiSockAddr & from,
/*
* ca_repeater ()
*/
-void ca_repeater ()
+void ca_repeater ( int setDebug )
{
tsFreeList < repeaterClient, 0x20 > freeList;
int size;
@@ -489,6 +499,8 @@ void ca_repeater ()
unsigned short port;
char * pBuf;
+ debug = setDebug;
+
pBuf = new char [MAX_UDP_RECV];
{
@@ -504,7 +516,7 @@ void ca_repeater ()
*/
if ( sockerrno == SOCK_EADDRINUSE ) {
osiSockRelease ();
- debugPrintf ( ( "CA Repeater: exiting because a repeater is already running\n" ) );
+ debugPrintf ( ( "CA Repeater: Exiting, a repeater is already running\n" ) );
delete [] pBuf;
return;
}
diff --git a/modules/ca/src/client/udpiiu.h b/modules/ca/src/client/udpiiu.h
index 3b1177ece..f7964c5ab 100644
--- a/modules/ca/src/client/udpiiu.h
+++ b/modules/ca/src/client/udpiiu.h
@@ -59,7 +59,7 @@ LIBCA_API void epicsStdCall caRepeaterRegistrationMessage (
SOCKET sock, unsigned repeaterPort, unsigned attemptNumber );
extern "C" LIBCA_API void caRepeaterThread (
void * pDummy );
-LIBCA_API void ca_repeater ( void );
+LIBCA_API void ca_repeater ( int setDebug = 0 );
class cac;
class cacContextNotify;
diff --git a/modules/database/src/ioc/db/dbChannel.h b/modules/database/src/ioc/db/dbChannel.h
index 56ce8c4d6..0daed5a9c 100644
--- a/modules/database/src/ioc/db/dbChannel.h
+++ b/modules/database/src/ioc/db/dbChannel.h
@@ -206,7 +206,7 @@ typedef struct chFilterIf {
*
* Optional.
* \param filter Pointer to instance data.
- * \param stringVal Value, not zero-terminated.
+ * \param stringVal Value, not null terminated.
* \param stringLen Number of chars in \p stringVal.
* \returns \p parse_stop on error, or \p parse_continue
*/
@@ -226,7 +226,7 @@ typedef struct chFilterIf {
*
* Optional.
* \param filter Pointer to instance data.
- * \param key Value not zero-terminated.
+ * \param key Value not null terminated.
* \param stringLen Number of chars in \p key
* \returns \p parse_stop on error, or \p parse_continue
*/
diff --git a/modules/database/src/ioc/db/dbConvert.c b/modules/database/src/ioc/db/dbConvert.c
index 2b17358c4..2592c90fc 100644
--- a/modules/database/src/ioc/db/dbConvert.c
+++ b/modules/database/src/ioc/db/dbConvert.c
@@ -444,7 +444,7 @@ static long getCharChar(const dbAddr *paddr,
if (paddr->pfldDes && paddr->pfldDes->field_type == DBF_STRING) {
/* This is a DBF_STRING field being read as a long string.
- * The buffer we return must be zero-terminated.
+ * The buffer we return must be null terminated.
*/
pdst[--nRequest] = 0;
if (nRequest == 0)
@@ -466,7 +466,7 @@ static long getCharUchar(const dbAddr *paddr,
if (paddr->pfldDes && paddr->pfldDes->field_type == DBF_STRING) {
/* This is a DBF_STRING field being read as a long string.
- * The buffer we return must be zero-terminated.
+ * The buffer we return must be null terminated.
*/
pdst[--nRequest] = 0;
if (nRequest == 0)
diff --git a/modules/database/src/ioc/db/dbLink.h b/modules/database/src/ioc/db/dbLink.h
index e53133541..3c374655b 100644
--- a/modules/database/src/ioc/db/dbLink.h
+++ b/modules/database/src/ioc/db/dbLink.h
@@ -111,7 +111,7 @@ typedef struct lset {
/** @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,
+ * null 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.
diff --git a/modules/database/src/ioc/dbStatic/dbStaticIocRegister.c b/modules/database/src/ioc/dbStatic/dbStaticIocRegister.c
index 26493f67a..c3dce5885 100644
--- a/modules/database/src/ioc/dbStatic/dbStaticIocRegister.c
+++ b/modules/database/src/ioc/dbStatic/dbStaticIocRegister.c
@@ -9,6 +9,7 @@
\*************************************************************************/
#include "iocsh.h"
+#include "iocInit.h"
#include "errSymTbl.h"
#include "errlog.h"
@@ -260,6 +261,54 @@ static void dbCreateAliasCallFunc(const iocshArgBuf *args)
}
}
+static const iocshArg dbCreateRecordArg0 = { "recordType", iocshArgString};
+static const iocshArg dbCreateRecordArg1 = { "recordName", iocshArgString};
+static const iocshArg * const dbCreateRecordArgs[] = {&argPdbbase, &dbCreateRecordArg0, &dbCreateRecordArg1};
+static const iocshFuncDef dbCreateRecordFuncDef = {
+ "dbCreateRecord",
+ 3,
+ dbCreateRecordArgs,
+ "Add a new record to the database.\n"
+ "\n"
+ "Example: dbCreateRecord pdbbase ai record:name\n",
+};
+
+/**
+ * @brief Call function for dbCreateRecord iocsh command.
+ * Creates a new record in the database with the specified record type and record name.
+ * @param args The arguments passed from iocsh, where args[0] is the pointer to the database,
+ * args[1] is the record type name, and args[2] is the record name.
+ */
+static void dbCreateRecordCallFunc(const iocshArgBuf *args)
+{
+ DBENTRY ent;
+ long status;
+ enum iocStateEnum iocState;
+
+ iocState = getIocState();
+ if(iocState != iocVoid) {
+ status = S_dbLib_postInitRecRegister;
+ }
+ else {
+ dbInitEntry(*iocshPpdbbase, &ent);
+ if(!args[2].sval || (args[2].sval[0] == '\0')) {
+ status = S_dbLib_recordNameMissing;
+ } else if(!args[1].sval) {
+ status = S_dbLib_recordTypeNotFound;
+ } else {
+ status = dbFindRecordType(&ent, args[1].sval);
+ if(!status) {
+ status = dbCreateRecord(&ent, args[2].sval);
+ }
+ }
+ dbFinishEntry(&ent);
+ }
+ if(status) {
+ fprintf(stderr, ERL_ERROR ": %ld %s\n", status, errSymMsg(status));
+ iocshSetError(1);
+ }
+}
+
void dbStaticIocRegister(void)
{
iocshRegister(&dbDumpPathFuncDef, dbDumpPathCallFunc);
@@ -278,4 +327,5 @@ void dbStaticIocRegister(void)
iocshRegister(&dbPvdTableSizeFuncDef,dbPvdTableSizeCallFunc);
iocshRegister(&dbReportDeviceConfigFuncDef, dbReportDeviceConfigCallFunc);
iocshRegister(&dbCreateAliasFuncDef, dbCreateAliasCallFunc);
+ iocshRegister(&dbCreateRecordFuncDef, dbCreateRecordCallFunc);
}
diff --git a/modules/database/src/ioc/dbStatic/dbStaticLib.h b/modules/database/src/ioc/dbStatic/dbStaticLib.h
index 6f772f27d..48f770dd5 100644
--- a/modules/database/src/ioc/dbStatic/dbStaticLib.h
+++ b/modules/database/src/ioc/dbStatic/dbStaticLib.h
@@ -252,20 +252,22 @@ DBCORE_API void dbCatString(char **string, int *stringLength,
extern int dbStaticDebug;
extern int dbConvertStrict;
-#define S_dbLib_recordTypeNotFound (M_dbLib|1) /* Record Type does not exist */
-#define S_dbLib_recExists (M_dbLib|3) /* Record Already exists */
-#define S_dbLib_recNotFound (M_dbLib|5) /* Record Not Found */
-#define S_dbLib_flddesNotFound (M_dbLib|7) /* Field Description Not Found */
-#define S_dbLib_fieldNotFound (M_dbLib|9) /* Field Not Found */
-#define S_dbLib_badField (M_dbLib|11) /* Bad Field value */
-#define S_dbLib_menuNotFound (M_dbLib|13) /* Menu not found */
-#define S_dbLib_badLink (M_dbLib|15) /* Bad Link Field */
-#define S_dbLib_nameLength (M_dbLib|17) /* Record Name is too long */
-#define S_dbLib_noRecSup (M_dbLib|19) /* Record support not found */
-#define S_dbLib_strLen (M_dbLib|21) /* String is too long */
-#define S_dbLib_noSizeOffset (M_dbLib|23) /* Missing SizeOffset Routine - No record support? */
-#define S_dbLib_outMem (M_dbLib|27) /* Out of memory */
-#define S_dbLib_infoNotFound (M_dbLib|29) /* Info item Not Found */
+#define S_dbLib_recordTypeNotFound (M_dbLib|1) /* Record Type does not exist */
+#define S_dbLib_recExists (M_dbLib|3) /* Record Already exists */
+#define S_dbLib_recNotFound (M_dbLib|5) /* Record Not Found */
+#define S_dbLib_flddesNotFound (M_dbLib|7) /* Field Description Not Found */
+#define S_dbLib_fieldNotFound (M_dbLib|9) /* Field Not Found */
+#define S_dbLib_badField (M_dbLib|11) /* Bad Field value */
+#define S_dbLib_menuNotFound (M_dbLib|13) /* Menu not found */
+#define S_dbLib_badLink (M_dbLib|15) /* Bad Link Field */
+#define S_dbLib_nameLength (M_dbLib|17) /* Record Name is too long */
+#define S_dbLib_noRecSup (M_dbLib|19) /* Record support not found */
+#define S_dbLib_strLen (M_dbLib|21) /* String is too long */
+#define S_dbLib_noSizeOffset (M_dbLib|23) /* Missing SizeOffset Routine - No record support? */
+#define S_dbLib_outMem (M_dbLib|27) /* Out of memory */
+#define S_dbLib_infoNotFound (M_dbLib|29) /* Info item Not Found */
+#define S_dbLib_postInitRecRegister (M_dbLib|31) /* IOC already initialized - No new records can be added */
+#define S_dbLib_recordNameMissing (M_dbLib|33) /* Record name is required */
#ifdef __cplusplus
}
diff --git a/modules/database/src/ioc/misc/iocInit.h b/modules/database/src/ioc/misc/iocInit.h
index 1acd8ee8f..cf7cda3f7 100644
--- a/modules/database/src/ioc/misc/iocInit.h
+++ b/modules/database/src/ioc/misc/iocInit.h
@@ -7,7 +7,8 @@
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
-/* iocInit.h ioc initialization */
+/** @file iocInit.h
+ * @brief ioc initialization */
#ifndef INCiocInith
#define INCiocInith
@@ -22,15 +23,30 @@ enum iocStateEnum {
extern "C" {
#endif
-/** Query present IOC run state
+/** Query the present IOC run state
* @since 3.15.8
*/
DBCORE_API enum iocStateEnum getIocState(void);
+
+/** @brief Initalizes the IOC in two distinct parts - iocBuild and iocRun
+ */
DBCORE_API int iocInit(void);
+
+/** @brief Puts the IOC into a quiescent state without allowing the various internal threads it starts to actually run */
DBCORE_API int iocBuild(void);
+/** @brief Allows to start an IOC without its Channel Access parts. It can then be shutdown cleanly using iocShutdown.
+ * Feature only intended to be used for test programs, not production IOCs. */
DBCORE_API int iocBuildIsolated(void);
+/** @brief Brings the IOC online after iocBuild or iocPause*/
DBCORE_API int iocRun(void);
+
+/** @brief Freezes all internal operations
+ *
+ * the iocRun command can restart it from where it left off or the ioc can be shut down. Pausing may not always be safe as not all drivers and device support may have been written with the possibility of pausing an IOC in mind.
+ */
DBCORE_API int iocPause(void);
+
+/** @brief Exits the program, shutsdown the IOC */
DBCORE_API int iocShutdown(void);
#ifdef __cplusplus
diff --git a/modules/libcom/src/calc/calcPerform.c b/modules/libcom/src/calc/calcPerform.c
index 340078f82..14bc73bf8 100644
--- a/modules/libcom/src/calc/calcPerform.c
+++ b/modules/libcom/src/calc/calcPerform.c
@@ -20,6 +20,7 @@
#include "osiUnistd.h"
#include "dbDefs.h"
#include "epicsMath.h"
+#include "epicsTime.h"
#include "epicsTypes.h"
#include "errlog.h"
#include "postfix.h"
@@ -505,22 +506,18 @@ calcArgUsage(const char *pinst, unsigned long *pinputs, unsigned long *pstores)
return 0;
}
-/* Generate a random number between 0 and 1 using the algorithm
- * seed = (multy * seed) + addy Random Number Generator by Knuth
- * SemiNumerical Algorithms
- * Chapter 1
- * randy = seed / 65535.0 To normalize the number between 0 - 1
- */
-static unsigned short seed = 0xa3bf;
-static unsigned short multy = 191 * 8 + 5; /* 191 % 8 == 5 */
-static unsigned short addy = 0x3141;
-
static double calcRandom(void)
{
- seed = (seed * multy) + addy;
-
+ static int seeded = 0;
+ static struct epicsTimeStamp ts;
+ if(!seeded)
+ {
+ epicsTimeGetMonotonic(&ts);
+ srand((unsigned) ts.nsec);
+ seeded = 1;
+ }
/* between 0 - 1 */
- return (double) seed / 65535.0;
+ return (double) rand() / RAND_MAX;
}
/* Search the instruction stream for a matching operator, skipping any
diff --git a/modules/libcom/src/dbmf/dbmf.h b/modules/libcom/src/dbmf/dbmf.h
index f1df3d31c..dd62b988d 100644
--- a/modules/libcom/src/dbmf/dbmf.h
+++ b/modules/libcom/src/dbmf/dbmf.h
@@ -64,7 +64,7 @@ LIBCOM_API void * dbmfMalloc(size_t bytes);
* \brief Duplicate a string.
*
* Create a copy of the input string.
- * \param str Pointer to the null-terminated string to be copied.
+ * \param str Pointer to the null terminated string to be copied.
* \return A pointer to the new copy, or NULL on failure.
*/
LIBCOM_API char * dbmfStrdup(const char *str);
@@ -73,7 +73,7 @@ LIBCOM_API char * dbmfStrdup(const char *str);
*
* Copy at most len bytes of the input string into a new buffer. If no nil
* terminator is seen in the first \c len bytes a nil terminator is added.
- * \param str Pointer to the null-terminated string to be copied.
+ * \param str Pointer to the null terminated string to be copied.
* \param len Max number of bytes to copy.
* \return A pointer to the new string, or NULL on failure.
*/
@@ -81,7 +81,7 @@ LIBCOM_API char * dbmfStrndup(const char *str, size_t len);
/**
* \brief Concatenate three strings.
- * Returns a pointer to a null-terminated string made by concatenating the
+ * Returns a pointer to a null terminated string made by concatenating the
* three input strings.
* \param lhs Start string to which the others get concatenated to (left part).
* \param mid Next string to be concatenated to the lhs (mid part).
diff --git a/modules/libcom/src/env/envDefs.h b/modules/libcom/src/env/envDefs.h
index cee902b73..a68b9ead3 100644
--- a/modules/libcom/src/env/envDefs.h
+++ b/modules/libcom/src/env/envDefs.h
@@ -105,7 +105,7 @@ struct in_addr;
* \return Pointer to the environment variable value string, or
* NULL if no parameter value and default value was empty.
*
- * \post A terminating nil will be written to pBuf.
+ * \post A terminating '\0' will be written to pBuf to ensure it is null terminated.
*/
LIBCOM_API char * epicsStdCall
envGetConfigParam(const ENV_PARAM *pParam, int bufDim, char *pBuf);
diff --git a/modules/libcom/src/iocsh/iocsh.cpp b/modules/libcom/src/iocsh/iocsh.cpp
index d1b18bff4..ec3fabddb 100644
--- a/modules/libcom/src/iocsh/iocsh.cpp
+++ b/modules/libcom/src/iocsh/iocsh.cpp
@@ -609,6 +609,7 @@ char** iocsh_attempt_completion(const char* word, int start, int end)
struct ReadlineContext {
void *context;
+ FILE *context_fp;
#ifdef USE_READLINE
// readline on BSD/OSX missing 'rl_completion_func_t' typedef, and 'const'
char* prev_rl_readline_name;
@@ -622,12 +623,14 @@ struct ReadlineContext {
ReadlineContext()
:context(NULL)
+ ,context_fp(NULL)
{}
bool setup(FILE *fp) {
+ context_fp = fp;
context = epicsReadlineBegin(fp);
#ifdef USE_READLINE
- if(context) {
+ if(context && !fp) {
prev_rl_readline_name = (char*)rl_readline_name;
prev_rl_basic_word_break_characters = (char*)rl_basic_word_break_characters;
prev_rl_completer_word_break_characters = (char*)rl_completer_word_break_characters;
@@ -644,6 +647,7 @@ struct ReadlineContext {
rl_bind_key('\t', rl_complete);
compute_hist_file();
if(!hist_file.empty()) {
+ clear_history(); // clear as read_history() will append
if(int err = read_history(hist_file.c_str())) {
if(err!=ENOENT)
fprintf(epicsGetStderr(),
@@ -660,21 +664,23 @@ struct ReadlineContext {
~ReadlineContext() {
if(context) {
#ifdef USE_READLINE
- if(!hist_file.empty()) {
- if(int err = write_history(hist_file.c_str())) {
- fprintf(epicsGetStderr(),
- ERL_ERROR " %s (%d) writing '%s'\n",
- strerror(err), err, hist_file.c_str());
+ if(!context_fp) {
+ if(!hist_file.empty()) {
+ if(int err = write_history(hist_file.c_str())) {
+ fprintf(epicsGetStderr(),
+ ERL_ERROR " %s (%d) writing '%s'\n",
+ strerror(err), err, hist_file.c_str());
+ }
}
+ rl_readline_name = prev_rl_readline_name;
+ rl_basic_word_break_characters = prev_rl_basic_word_break_characters;
+ rl_completer_word_break_characters = prev_rl_completer_word_break_characters;
+ rl_basic_quote_characters = prev_rl_basic_quote_characters;
+ rl_completer_quote_characters = prev_rl_completer_quote_characters;
+ rl_attempted_completion_function = prev_rl_attempted_completion_function;
+ // cf. osdReadlineBegin() in gnuReadline.c
+ rl_bind_key('\t', rl_insert);
}
- rl_readline_name = prev_rl_readline_name;
- rl_basic_word_break_characters = prev_rl_basic_word_break_characters;
- rl_completer_word_break_characters = prev_rl_completer_word_break_characters;
- rl_basic_quote_characters = prev_rl_basic_quote_characters;
- rl_completer_quote_characters = prev_rl_completer_quote_characters;
- rl_attempted_completion_function = prev_rl_attempted_completion_function;
- // cf. osdReadlineBegin() in gnuReadline.c
- rl_bind_key('\t', rl_insert);
#endif
epicsReadlineEnd(context);
}
diff --git a/modules/libcom/src/macLib/macCore.c b/modules/libcom/src/macLib/macCore.c
index bf3ca35cf..5931807cd 100644
--- a/modules/libcom/src/macLib/macCore.c
+++ b/modules/libcom/src/macLib/macCore.c
@@ -958,13 +958,14 @@ static void trans( MAC_HANDLE *handle, MAC_ENTRY *entry, int level,
static void refer ( MAC_HANDLE *handle, MAC_ENTRY *entry, int level,
const char **rawval, char **value, char *valend )
{
- const char *r = *rawval;
+ const char *r = *rawval + 1; /* step over '$' */
+ const char *macEnd = (*r++ == '(') ? "=,)" : "=,}";
+ const char close = macEnd[2]; /* Matching ')' or '}' */
char *v = *value;
char refname[MAC_SIZE + 1] = {'\0'};
char *rn = refname;
MAC_ENTRY *refentry;
const char *defval = NULL;
- const char *macEnd;
const char *errval = NULL;
int pop = FALSE;
@@ -973,14 +974,14 @@ static void refer ( MAC_HANDLE *handle, MAC_ENTRY *entry, int level,
printf( "refer-> entry = %p, level = %d, capacity = %u, rawval = %s\n",
entry, level, (unsigned int)(valend - *value), *rawval );
- /* step over '$(' or '${' */
- r++;
- macEnd = ( *r == '(' ) ? "=,)" : "=,}";
- r++;
-
/* translate name (may contain macro references); truncated
- quietly if too long but always guaranteed zero-terminated */
- trans( handle, entry, level + 1, macEnd, &r, &rn, rn + MAC_SIZE );
+ quietly if too long but always guaranteed to be null terminated */
+ {
+ int flags = handle->flags;
+ handle->flags |= FLAG_SUPPRESS_WARNINGS;
+ trans( handle, entry, level + 1, macEnd, &r, &rn, rn + MAC_SIZE );
+ handle->flags = flags;
+ }
refname[MAC_SIZE] = '\0';
/* Is there a default value? */
@@ -1043,6 +1044,21 @@ static void refer ( MAC_HANDLE *handle, MAC_ENTRY *entry, int level,
handle->flags = flags;
}
+ /* If the closing delimiter doesn't match the opener, pass through verbatim */
+ if ( *r != close ) {
+ const char *p;
+ v = *value;
+ for ( p = *rawval; p <= r && v < valend; p++ )
+ *v++ = *p;
+ *v = '\0';
+ entry->error = TRUE;
+ if ( (handle->flags & FLAG_SUPPRESS_WARNINGS) == 0 )
+ errlogPrintf( "macLib: " ANSI_MAGENTA("unterminated")
+ " macro reference in %s %s\n",
+ entry->type, entry->name );
+ goto cleanup;
+ }
+
/* Now we can look up the translated name */
refentry = lookup( handle, refname, FALSE );
@@ -1066,7 +1082,8 @@ static void refer ( MAC_HANDLE *handle, MAC_ENTRY *entry, int level,
entry->error = TRUE;
errval = ",recursive)";
if ( (handle->flags & FLAG_SUPPRESS_WARNINGS) == 0 ) {
- errlogPrintf( "macLib: %s %s is recursive (expanding %s %s)\n",
+ errlogPrintf( "macLib: %s %s is " ANSI_MAGENTA("recursive")
+ " (expanding %s %s)\n",
entry->type, entry->name,
refentry->type, refentry->name );
}
@@ -1080,8 +1097,9 @@ static void refer ( MAC_HANDLE *handle, MAC_ENTRY *entry, int level,
entry->error = TRUE;
errval = ",undefined)";
if ( (handle->flags & FLAG_SUPPRESS_WARNINGS) == 0 ) {
- errlogPrintf( "macLib: macro %s is " ANSI_MAGENTA("undefined") " (expanding %s %s)\n",
- refname, entry->type, entry->name );
+ errlogPrintf( "macLib: macro %s is " ANSI_MAGENTA("undefined")
+ " (expanding %s %s)\n",
+ refname, entry->type, entry->name );
}
}
diff --git a/modules/libcom/src/macLib/macLib.h b/modules/libcom/src/macLib/macLib.h
index 4ed7ead3c..45c8ad984 100644
--- a/modules/libcom/src/macLib/macLib.h
+++ b/modules/libcom/src/macLib/macLib.h
@@ -126,7 +126,7 @@ epicsStdCall macPutValue(
* \brief Returns the value of a macro
* \return Returns the length of the value string, <0 if undefined
*
- * \c value will be zero-terminated if the length of the value is less than
+ * \c value will be null terminated if the length of the value is less than
* \c capacity. The return value is the number of characters copied to
* \c value (see below for behavior if the macro is undefined). If \c capacity
* is zero, no characters will be copied to \c value (which may be NULL)
@@ -277,7 +277,7 @@ epicsStdCall macInstallMacros(
* environment variables. It parses the string looking for such
* references and passes them to macGetValue() for translation. It uses
* malloc() to allocate space for the expanded string and returns a
- * pointer to this null-terminated string. It returns NULL if the source
+ * pointer to this null terminated string. It returns NULL if the source
* string contains any undefined references.
*/
LIBCOM_API char *
diff --git a/modules/libcom/src/macLib/macLibREADME b/modules/libcom/src/macLib/macLibREADME
index 9efcb9a20..66fc95ea5 100644
--- a/modules/libcom/src/macLib/macLibREADME
+++ b/modules/libcom/src/macLib/macLibREADME
@@ -36,7 +36,7 @@ c) long macGetValue ( MAC_HANDLE *handle, char *name,
char *value, long maxlen );
Returns up to maxlen characters of the value of macro "name". "value"
- will be zero-terminated if the length of the value is less than
+ will be null terminated if the length of the value is less than
maxlen. The function value will be the number of characters copied to
"value" (see below for behavior if the macro is undefined). If maxlen
is zero, no characters will be copied to "value" (which can be NULL)
@@ -161,7 +161,7 @@ d) char *macEnvExpand( char *src );
environment variables. It parses the string looking for such
references and passes them to macGetValue() for translation. It uses
malloc() to allocate space for the expanded string and returns a
- pointer to this null-terminated string. It returns NULL if the source
+ pointer to this null terminated string. It returns NULL if the source
string contains any undefined references.
e) char *macDefExpand( char *src, MAC_HANDLE *macros );
diff --git a/modules/libcom/src/macLib/macUtil.c b/modules/libcom/src/macLib/macUtil.c
index 920ac006f..5c04c9075 100644
--- a/modules/libcom/src/macLib/macUtil.c
+++ b/modules/libcom/src/macLib/macUtil.c
@@ -62,8 +62,11 @@ epicsStdCall macParseDefns(
printf( "macParseDefns( %s )\n", defns );
/* allocate temporary pointer arrays; in worst case they need to have
- as many entries as the length of the defns string */
- numMax = strlen( defns );
+ as many entries as the length of the defns string + 2
+ for defns="1,3,5" give 3 pairs so num will be 6 (strlen(defns)+1)
+ but as num is used as an index e.g. del[num] we need
+ numMax to be strlen(defns) + 2 */
+ numMax = strlen( defns ) + 2;
if ( numMax < altNumMax )
numMax = altNumMax;
ptr = (const char **) calloc( numMax, sizeof( char * ) );
diff --git a/modules/libcom/src/misc/epicsString.h b/modules/libcom/src/misc/epicsString.h
index 8f7f2b001..eee26553a 100644
--- a/modules/libcom/src/misc/epicsString.h
+++ b/modules/libcom/src/misc/epicsString.h
@@ -37,7 +37,7 @@ extern "C" {
* \p inbuf and \p outbuf to point to the same buffer and \p outsize and \p inlen
* be equal, thus performing the character translation in-place.
*
- * \param outbuf buffer to copy string to. The resulting string will be zero-terminated as long as
+ * \param outbuf buffer to copy string to. The resulting string will be null terminated as long as
* \p outsize is non-zero.
* \param outsize length of output buffer not including the null-terminator.
* \param inbuf buffer to copy from. Null byte terminates the string.
@@ -59,7 +59,7 @@ LIBCOM_API int epicsStrnRawFromEscaped(char *outbuf, size_t outsize,
* All other non-printable characters appear in form `\xHH` where HH are two hex digits.
* Non-printable characters are determined by the C runtime library’s isprint() function.
*
- * \param outbuf buffer to copy string to. The resulting string will be zero-terminated as long as
+ * \param outbuf buffer to copy string to. The resulting string will be null terminated as long as
* @p outsize is non-zero.
* \param outsize length of output buffer not including the null-terminator.
* \param inbuf buffer to copy from. Null byte will not terminates the string.
@@ -159,12 +159,12 @@ LIBCOM_API int epicsStrnGlobMatch(const char *str, size_t len, const char *patte
*/
LIBCOM_API char * epicsStrtok_r(char *s, const char *delim, char **lasts);
-/** \brief Calculates a hash of a null-terminated string
+/** \brief Calculates a hash of a null terminated string
*
- * Calculates a hash of a null-terminated string. Initial seed may be provided which
+ * Calculates a hash of a null terminated string. Initial seed may be provided which
* permits multiple strings to be combined into a single hash result.
*
- *\param str null-terminated string
+ *\param str null terminated string
*\param seed Optionally provide seed to combine multiple strings in a single hash. Otherwise
* set to 0.
*
diff --git a/modules/libcom/src/osi/epicsStdio.h b/modules/libcom/src/osi/epicsStdio.h
index b2bda61a3..1dd560040 100644
--- a/modules/libcom/src/osi/epicsStdio.h
+++ b/modules/libcom/src/osi/epicsStdio.h
@@ -125,7 +125,7 @@ extern "C" {
* correctly return the number of characters that would have been written when
* the output gets truncated, it is not worth trying to fix this as long as
* they return `size-1` instead; the resulting string must always be correctly
- * terminated with a zero byte.
+ * null terminated.
*
* In some scenarios the epicsSnprintf() implementation may not provide the
* correct C99 semantics for the return value when `size` is given as zero.
diff --git a/modules/libcom/src/yajl/yajl_gen.h b/modules/libcom/src/yajl/yajl_gen.h
index 50b163cd0..974e364f8 100644
--- a/modules/libcom/src/yajl/yajl_gen.h
+++ b/modules/libcom/src/yajl/yajl_gen.h
@@ -196,7 +196,7 @@ extern "C" {
/** Finish generating a JSON array. */
YAJL_API yajl_gen_status yajl_gen_array_close(yajl_gen hand);
- /** Access the zero-terminated generator buffer. If incrementally
+ /** Access the null terminated generator buffer. If incrementally
* outputting JSON, one should call yajl_gen_clear() to clear the
* buffer. This allows stream generation. */
YAJL_API yajl_gen_status yajl_gen_get_buf(yajl_gen hand,
diff --git a/modules/libcom/src/yajl/yajl_parse.h b/modules/libcom/src/yajl/yajl_parse.h
index 2e5c2e34c..428e405a4 100644
--- a/modules/libcom/src/yajl/yajl_parse.h
+++ b/modules/libcom/src/yajl/yajl_parse.h
@@ -79,7 +79,7 @@ extern "C" {
size_t numberLen);
/** Strings are returned as pointers into the JSON text when
- * possible. As a result they are _not_ zero-terminated. */
+ * possible. As a result they are _not_ null terminated. */
int (* yajl_string)(void * ctx, const unsigned char * stringVal,
size_t stringLen);
diff --git a/modules/libcom/test/epicsStringTest.c b/modules/libcom/test/epicsStringTest.c
index 2bb5ac7ed..c78830125 100644
--- a/modules/libcom/test/epicsStringTest.c
+++ b/modules/libcom/test/epicsStringTest.c
@@ -313,13 +313,13 @@ MAIN(epicsStringTest)
memset(result, 'x', sizeof(result));
status = epicsStrnEscapedFromRaw(result, 1, ABCD, 4);
testOk(status == 4, "epicsStrnEscapedFromRaw(out, 1) -> %d (exp. 4)", status);
- testOk(result[0] == 0, " 0-terminated");
+ testOk(result[0] == 0, " null terminated");
testOk(result[1] == 'x', " No overrun");
memset(result, 'x', sizeof(result));
status = epicsStrnRawFromEscaped(result, 1, ABCD, 4);
testOk(status == 0, "epicsStrnRawFromEscaped(out, 1) -> %d (exp. 0)", status);
- testOk(result[0] == 0, " 0-terminated");
+ testOk(result[0] == 0, " null terminated");
testOk(result[1] == 'x', " No overrun");
testDiag("Testing size = epicsStrnEscapedFromRawSize");
@@ -336,19 +336,19 @@ MAIN(epicsStringTest)
memset(result, 'x', sizeof(result));
status = epicsStrnEscapedFromRaw(result, 4, ABCD, 3);
testOk(status == 3, "esc(\"ABCD\", 3) -> %d (exp. 3)", status);
- testOk(result[3] == 0, " 0-terminated");
+ testOk(result[3] == 0, " null terminated");
testOk(result[4] == 'x', " No overrun");
memset(result, 'x', sizeof(result));
status = epicsStrnEscapedFromRaw(result, 4, ABCD, 4);
testOk(status == 4, "esc(\"ABCD\", 4) -> %d (exp. 4)", status);
- testOk(result[3] == 0, " 0-terminated");
+ testOk(result[3] == 0, " null terminated");
testOk(result[4] == 'x', " No overrun");
memset(result, 'x', sizeof(result));
status = epicsStrnEscapedFromRaw(result, 4, ABCD, 5);
testOk(status == 6, "esc(\"ABCD\", 5) -> %d (exp. 8)", status);
- testOk(result[3] == 0, " 0-terminated");
+ testOk(result[3] == 0, " null terminated");
testOk(result[4] == 'x', " No overrun");
testDiag("Testing raw = epicsStrnRawFromEscaped(out, 4, ...)");
@@ -356,20 +356,20 @@ MAIN(epicsStringTest)
memset(result, 'x', sizeof(result));
status = epicsStrnRawFromEscaped(result, 4, ABCD, 0);
testOk(status == 0, "raw(\"ABCD\", 0) -> %d (exp. 0)", status);
- testOk(result[0] == 0, " 0-terminated");
+ testOk(result[0] == 0, " null terminated");
memset(result, 'x', sizeof(result));
status = epicsStrnRawFromEscaped(result, 4, ABCD, 1);
testOk(status == 1, "raw(\"ABCD\", 1) -> %d (exp. 1)", status);
testOk(result[0] == 'A', " Char '%c' (exp. 'A')", result[0]);
- testOk(result[1] == 0, " 0-terminated");
+ testOk(result[1] == 0, " null terminated");
memset(result, 'x', sizeof(result));
status = epicsStrnRawFromEscaped(result, 4, ABCD, 2);
testOk(status == 2, "raw(\"ABCD\", 2) -> %d (exp. 2)", status);
testOk(result[0] == 'A', " Char '%c' (exp. 'A')", result[0]);
testOk(result[1] == 'B', " Char '%c' (exp. 'B')", result[1]);
- testOk(result[2] == 0, " 0-terminated");
+ testOk(result[2] == 0, " null terminated");
memset(result, 'x', sizeof(result));
status = epicsStrnRawFromEscaped(result, 4, ABCD, 3);
@@ -377,7 +377,7 @@ MAIN(epicsStringTest)
testOk(result[0] == 'A', " Char '%c' (exp. 'A')", result[0]);
testOk(result[1] == 'B', " Char '%c' (exp. 'B')", result[1]);
testOk(result[2] == 'C', " Char '%c' (exp. 'C')", result[2]);
- testOk(result[3] == 0, " 0-terminated");
+ testOk(result[3] == 0, " null terminated");
testOk(result[4] == 'x', " No write outside buffer");
memset(result, 'x', sizeof(result));
@@ -386,7 +386,7 @@ MAIN(epicsStringTest)
testOk(result[0] == 'A', " Char '%c' (exp. 'A')", result[0]);
testOk(result[1] == 'B', " Char '%c' (exp. 'B')", result[1]);
testOk(result[2] == 'C', " Char '%c' (exp. 'C')", result[2]);
- testOk(result[3] == 0, " 0-terminated");
+ testOk(result[3] == 0, " null terminated");
testOk(result[4] == 'x', " No write outside buffer");
memset(result, 'x', sizeof(result));
@@ -395,49 +395,49 @@ MAIN(epicsStringTest)
testOk(result[0] == 'A', " Char '%c' (exp. 'A')", result[0]);
testOk(result[1] == 'B', " Char '%c' (exp. 'B')", result[1]);
testOk(result[2] == 'C', " Char '%c' (exp. 'C')", result[2]);
- testOk(result[3] == 0, " 0-terminated");
+ testOk(result[3] == 0, " null terminated");
testOk(result[4] == 'x', " No write outside buffer");
memset(result, 'x', sizeof(result));
status = epicsStrnRawFromEscaped(result, 4, "A", 2);
testOk(status == 1, "raw(\"A\", 2) -> %d (exp. 1)", status);
testOk(result[0] == 'A', " Char '%c' (exp. 'A')", result[0]);
- testOk(result[status] == 0, " 0-terminated");
+ testOk(result[status] == 0, " null terminated");
memset(result, 'x', sizeof(result));
status = epicsStrnRawFromEscaped(result, 4, "\\x12", 1);
testOk(status == 0, "raw(\"\\x12\", 1) -> %d (exp. 0)", status);
- testOk(result[status] == 0, " 0-terminated");
+ testOk(result[status] == 0, " null terminated");
memset(result, 'x', sizeof(result));
status = epicsStrnRawFromEscaped(result, 4, "\\x12", 2);
testOk(status == 0, "raw(\"\\x12\", 2) -> %d (exp. 0)", status);
- testOk(result[status] == 0, " 0-terminated");
+ testOk(result[status] == 0, " null terminated");
memset(result, 'x', sizeof(result));
status = epicsStrnRawFromEscaped(result, 4, "\\x12", 3);
testOk(status == 1, "raw(\"\\x12\", 3) -> %d (exp. 1)", status);
testOk(result[0] == 1, " Hex escape (got \\x%x)", result[0]);
- testOk(result[status] == 0, " 0-terminated");
+ testOk(result[status] == 0, " null terminated");
memset(result, 'x', sizeof(result));
status = epicsStrnRawFromEscaped(result, 4, "\\x12", 4);
testOk(status == 1, "raw(\"\\x12\", 4) -> %d (exp. 1)", status);
testOk(result[0] == 0x12," Hex escape (got \\x%x)", result[0]);
- testOk(result[status] == 0, " 0-terminated");
+ testOk(result[status] == 0, " null terminated");
memset(result, 'x', sizeof(result));
status = epicsStrnRawFromEscaped(result, 4, "\\xaF", 4);
testOk(status == 1, "raw(\"\\xaF\", 4) -> %d (exp. 1)", status);
testOk(result[0] == '\xaF'," Hex escape (got \\x%x)", result[0] & 0xFF);
- testOk(result[status] == 0, " 0-terminated");
+ testOk(result[status] == 0, " null terminated");
memset(result, 'x', sizeof(result));
status = epicsStrnRawFromEscaped(result, 4, "\\x012", 5);
testOk(status == 2, "raw(\"\\x012\", 5) -> %d (exp. 2)", status);
testOk(result[0] == 0x1," Hex escape (got \\x%x)", result[0]);
testOk(result[1] == '2', " Terminator char got '%c'", result[1]);
- testOk(result[status] == 0, " 0-terminated");
+ testOk(result[status] == 0, " null terminated");
memset(result, 'x', sizeof(result));
status = epicsStrnRawFromEscaped(result, 4, "\\x0012", 6);
@@ -445,14 +445,14 @@ MAIN(epicsStringTest)
testOk(result[0] == 0," Hex escape (got \\x%x)", result[0]);
testOk(result[1] == '1', " Terminator char got '%c'", result[1]);
testOk(result[2] == '2', " Following char got '%c'", result[2]);
- testOk(result[status] == 0, " 0-terminated");
+ testOk(result[status] == 0, " null terminated");
memset(result, 'x', sizeof(result));
status = epicsStrnRawFromEscaped(result, 4, "\\x1g", 4);
testOk(status == 2, "raw(\"\\x1g\", 4) -> %d (exp. 2)", status);
testOk(result[0] == 1, " Hex escape (got \\x%x)", result[0]);
testOk(result[1] == 'g', " Terminator char got '%c'", result[1]);
- testOk(result[status] == 0, " 0-terminated");
+ testOk(result[status] == 0, " null terminated");
testDistance();
testStrTok();
diff --git a/modules/libcom/test/epicsTimeZoneTest.c b/modules/libcom/test/epicsTimeZoneTest.c
index f10688765..8437a33ad 100644
--- a/modules/libcom/test/epicsTimeZoneTest.c
+++ b/modules/libcom/test/epicsTimeZoneTest.c
@@ -89,10 +89,10 @@ MAIN(epicsTimeZoneTest)
* Mon Oct 19 13:00:16 2015 UTC
*/
testDiag("POSIX 1445259616");
- setTZ("EST5EDT");
+ setTZ("EST5EDT,M3.2.0/2,M11.1.0/2");
test_localtime(1445259616ul, 16, 0, 9, 19, 9, 2015, 1, 291, 1);
test_gmtime (1445259616ul, 16, 0, 13, 19, 9, 2015, 1, 291, 0);
- setTZ("CST6CDT");
+ setTZ("CST6CDT,M3.2.0/2,M11.1.0/2");
test_localtime(1445259616ul, 16, 0, 8, 19, 9, 2015, 1, 291, 1);
test_gmtime (1445259616ul, 16, 0, 13, 19, 9, 2015, 1, 291, 0);
#if defined(__rtems__)
@@ -112,10 +112,10 @@ MAIN(epicsTimeZoneTest)
* Wed Jan 14 14:15:31 2015 UTC
*/
testDiag("POSIX 1421244931");
- setTZ("EST5EDT");
+ setTZ("EST5EDT,M3.2.0/2,M11.1.0/2");
test_localtime(1421244931ul, 31, 15, 9, 14, 0, 2015, 3, 13, 0);
test_gmtime (1421244931ul, 31, 15, 14, 14, 0, 2015, 3, 13, 0);
- setTZ("CST6CDT");
+ setTZ("CST6CDT,M3.2.0/2,M11.1.0/2");
test_localtime(1421244931ul, 31, 15, 8, 14, 0, 2015, 3, 13, 0);
test_gmtime (1421244931ul, 31, 15, 14, 14, 0, 2015, 3, 13, 0);
#if defined(__rtems__)
diff --git a/modules/libcom/test/macDefExpandTest.c b/modules/libcom/test/macDefExpandTest.c
index 37f11f9f4..a93727a58 100644
--- a/modules/libcom/test/macDefExpandTest.c
+++ b/modules/libcom/test/macDefExpandTest.c
@@ -61,6 +61,27 @@ static void macEnvScope(void)
checkMac(handle, "E", "15");
checkMac(handle, "F", NULL);
+ {
+ /* test worst case for short definitions
+ * https://github.com/epics-base/epics-base/pull/838
+ */
+ macParseDefns(NULL, "1,3,5", &defines); /* smoke */
+
+#define CASE(I,V) \
+ testOk(defines[I] && strcmp(defines[I], V)==0, "defines[%u] \"%s\" == \"" V "\"", I, defines[I])
+ CASE(0,"1");
+ testOk1(defines[1]==NULL);
+ CASE(2,"3");
+ testOk1(defines[3]==NULL);
+ CASE(4,"5");
+ testOk1(defines[5]==NULL);
+ testOk1(defines[6]==NULL);
+#undef CASE
+
+ free(defines);
+ defines = NULL;
+ }
+
{
macPushScope(handle);
@@ -148,7 +169,7 @@ static void check(const char *str, const char *macros, const char *expect)
MAIN(macDefExpandTest)
{
eltc(0);
- testPlan(97);
+ testPlan(104);
check("FOO", "", "FOO");
diff --git a/modules/libcom/test/macLibTest.c b/modules/libcom/test/macLibTest.c
index eea6268dd..4c5f6824c 100644
--- a/modules/libcom/test/macLibTest.c
+++ b/modules/libcom/test/macLibTest.c
@@ -66,13 +66,25 @@ static void ovcheck(void)
MAIN(macLibTest)
{
- testPlan(93);
+ testPlan(105);
if (macCreateHandle(&h, NULL))
testAbort("macCreateHandle() failed");
eltc(0);
check("FOO", " FOO");
+ check("F(O){O}", " F(O){O}");
+ check("F(O}{O)", " F(O}{O)");
+
+ check("x${FOO)", "!x${FOO)");
+ check("x${FOO=bar)", "!x${FOO=bar)");
+ check("x${FOO,bar)", "!x${FOO,bar)");
+ check("x${FOO ${BAR", "!x${FOO ${BAR");
+
+ check("x$(FOO}", "!x$(FOO}");
+ check("x$(FOO=bar}", "!x$(FOO=bar}");
+ check("x$(FOO,bar}", "!x$(FOO,bar}");
+ check("x$(FOO $(BAR", "!x$(FOO $(BAR");
check("$(FOO)", "!$(FOO,undefined)");
check("${FOO}", "!$(FOO,undefined)");
@@ -115,6 +127,8 @@ MAIN(macLibTest)
check("${=BAR}", " BAR");
check("x${=BAR}y", " xBARy");
+ check("x(${=)}y", " x()y");
+ check("x{$(=})y", " x{}y");
check("${FOO=BAR}", " BAR");
check("x${FOO=BAR}y", " xBARy");