diff --git a/src/libCom/iocsh/iocsh.cpp b/src/libCom/iocsh/iocsh.cpp index 671239781..d8701532e 100644 --- a/src/libCom/iocsh/iocsh.cpp +++ b/src/libCom/iocsh/iocsh.cpp @@ -56,7 +56,8 @@ static struct iocshVariable *iocshVariableHead; static char iocshVarID[] = "iocshVar"; extern "C" { static void varCallFunc(const iocshArgBuf *); } static epicsMutexId iocshTableMutex; -static epicsThreadOnceId iocshTableOnceId = EPICS_THREAD_ONCE_INIT; +static epicsThreadOnceId iocshOnceId = EPICS_THREAD_ONCE_INIT; +static epicsThreadPrivateId iocshMacroHandleId; /* * I/O redirection @@ -71,11 +72,17 @@ struct iocshRedirect { }; /* - * Set up command table mutex + * Set up module variables */ -static void iocshTableOnce (void *) +static void iocshOnce (void *) { iocshTableMutex = epicsMutexMustCreate (); + iocshMacroHandleId = epicsThreadPrivateCreate(); +} + +static void iocshInit (void) +{ + epicsThreadOnce (&iocshOnceId, iocshOnce, NULL); } /* @@ -84,7 +91,7 @@ static void iocshTableOnce (void *) static void iocshTableLock (void) { - epicsThreadOnce (&iocshTableOnceId, iocshTableOnce, NULL); + iocshInit(); epicsMutexMustLock (iocshTableMutex); } @@ -94,7 +101,6 @@ iocshTableLock (void) static void iocshTableUnlock (void) { - epicsThreadOnce (&iocshTableOnceId, iocshTableOnce, NULL); epicsMutexUnlock (iocshTableMutex); } @@ -475,7 +481,7 @@ static void helpCallFunc(const iocshArgBuf *args) * The body of the command interpreter */ static int -iocshBody (const char *pathname, const char *commandLine) +iocshBody (const char *pathname, const char *commandLine, const char *macros) { FILE *fp = NULL; const char *filename = NULL; @@ -498,7 +504,12 @@ iocshBody (const char *pathname, const char *commandLine) struct iocshCommand *found; void *readlineContext = NULL; int wasOkToBlock; + static const char * pairs[] = {"", "environ", NULL, NULL}; + MAC_HANDLE *handle; + char ** defines = NULL; + iocshInit(); + /* * See if command interpreter is interactive */ @@ -540,7 +551,35 @@ iocshBody (const char *pathname, const char *commandLine) fprintf(epicsGetStderr(), "Out of memory!\n"); return -1; } - + + /* + * Parse macro definitions, this check occurs before creating the + * macro handle to simplify cleanup. + */ + + if (macros) { + if (macParseDefns(NULL, macros, &defines) < 0) { + return -1; + } + } + + /* + * Check for existing macro context or construct a new one. + */ + handle = (MAC_HANDLE *) epicsThreadPrivateGet(iocshMacroHandleId); + + if (handle == NULL) { + if (macCreateHandle(&handle, pairs)) { + errlogMessage("iocsh: macCreateHandle failed."); + return -1; + } + + epicsThreadPrivateSet(iocshMacroHandleId, (void *) handle); + } + + macPushScope(handle); + macInstallMacros(handle, defines); + /* * Read commands till EOF or exit */ @@ -585,7 +624,7 @@ iocshBody (const char *pathname, const char *commandLine) * Expand macros */ free(line); - if ((line = macEnvExpand(raw)) == NULL) + if ((line = macDefExpand(raw, handle)) == NULL) continue; /* @@ -744,7 +783,7 @@ iocshBody (const char *pathname, const char *commandLine) if (openRedirect(filename, lineno, redirects) < 0) continue; startRedirect(filename, lineno, redirects); - iocshBody(commandFile, NULL); + iocshBody(commandFile, NULL, macros); stopRedirect(filename, lineno, redirects); continue; } @@ -811,6 +850,12 @@ iocshBody (const char *pathname, const char *commandLine) } stopRedirect(filename, lineno, redirects); } + macPopScope(handle); + + if (handle->level == 0) { + macDeleteHandle(handle); + epicsThreadPrivateSet(iocshMacroHandleId, NULL); + } if (fp && (fp != stdin)) fclose (fp); if (redirects != NULL) { @@ -833,17 +878,58 @@ iocshBody (const char *pathname, const char *commandLine) int epicsShareAPI iocsh (const char *pathname) { - if (pathname) - epicsEnvSet("IOCSH_STARTUP_SCRIPT", pathname); - return iocshBody(pathname, NULL); + return iocshLoad(pathname, NULL); } int epicsShareAPI iocshCmd (const char *cmd) +{ + return iocshRun(cmd, NULL); +} + +int epicsShareAPI +iocshLoad(const char *pathname, const char *macros) +{ + if (pathname) + epicsEnvSet("IOCSH_STARTUP_SCRIPT", pathname); + return iocshBody(pathname, NULL, macros); +} + +int epicsShareAPI +iocshRun(const char *cmd, const char *macros) { if (cmd == NULL) return 0; - return iocshBody(NULL, cmd); + return iocshBody(NULL, cmd, macros); +} + +/* + * Needed to work around the necessary limitations of macLib and + * environment variables. In every other case of macro expansion + * it is the expected outcome that defined macros override any + * environment variables. + * + * iocshLoad/Run turn this on its head as it is very likely that + * an epicsEnvSet command may be run within the context of their + * calls. Thus, it would be expected that the new value would be + * returned in any future macro expansion. + * + * To do so, the epicsEnvSet command needs to be able to access + * and update the shared MAC_HANDLE that the iocsh uses. Which is + * what this function is provided for. + */ +void epicsShareAPI +iocshEnvClear(const char *name) +{ + MAC_HANDLE *handle; + + if (iocshMacroHandleId) { + handle = (MAC_HANDLE *) epicsThreadPrivateGet(iocshMacroHandleId); + + if (handle != NULL) { + macPutValue(handle, name, NULL); + } + } } /* @@ -926,6 +1012,26 @@ static void iocshCmdCallFunc(const iocshArgBuf *args) iocshCmd(args[0].sval); } +/* iocshLoad */ +static const iocshArg iocshLoadArg0 = { "pathname",iocshArgString}; +static const iocshArg iocshLoadArg1 = { "macros", iocshArgString}; +static const iocshArg *iocshLoadArgs[2] = {&iocshLoadArg0, &iocshLoadArg1}; +static const iocshFuncDef iocshLoadFuncDef = {"iocshLoad",2,iocshLoadArgs}; +static void iocshLoadCallFunc(const iocshArgBuf *args) +{ + iocshLoad(args[0].sval, args[1].sval); +} + +/* iocshRun */ +static const iocshArg iocshRunArg0 = { "command",iocshArgString}; +static const iocshArg iocshRunArg1 = { "macros", iocshArgString}; +static const iocshArg *iocshRunArgs[2] = {&iocshRunArg0, &iocshRunArg1}; +static const iocshFuncDef iocshRunFuncDef = {"iocshRun",2,iocshRunArgs}; +static void iocshRunCallFunc(const iocshArgBuf *args) +{ + iocshRun(args[0].sval, args[1].sval); +} + /* * Dummy internal commands -- register and install in command table * so they show up in the help display @@ -953,6 +1059,8 @@ static void localRegister (void) iocshRegister(&exitFuncDef,exitCallFunc); iocshRegister(&helpFuncDef,helpCallFunc); iocshRegister(&iocshCmdFuncDef,iocshCmdCallFunc); + iocshRegister(&iocshLoadFuncDef,iocshLoadCallFunc); + iocshRegister(&iocshRunFuncDef,iocshRunCallFunc); } } /* extern "C" */ diff --git a/src/libCom/iocsh/iocsh.h b/src/libCom/iocsh/iocsh.h index d89991050..e31756479 100644 --- a/src/libCom/iocsh/iocsh.h +++ b/src/libCom/iocsh/iocsh.h @@ -70,6 +70,11 @@ epicsShareFunc void epicsShareAPI iocshFree(void); epicsShareFunc int epicsShareAPI iocsh(const char *pathname); epicsShareFunc int epicsShareAPI iocshCmd(const char *cmd); +epicsShareFunc int epicsShareAPI iocshLoad(const char *pathname, const char* macros); +epicsShareFunc int epicsShareAPI iocshRun(const char *cmd, const char* macros); + +/* Makes macros that shadow environment variables work correctly with epicsEnvSet */ +epicsShareFunc void epicsShareAPI iocshEnvClear(const char *name); /* 'weak' link to pdbbase */ epicsShareExtern struct dbBase **iocshPpdbbase; diff --git a/src/libCom/macLib/macCore.c b/src/libCom/macLib/macCore.c index da5244dfd..02c8da765 100644 --- a/src/libCom/macLib/macCore.c +++ b/src/libCom/macLib/macCore.c @@ -103,7 +103,7 @@ epicsShareAPI macCreateHandle( MAC_HANDLE **pHandle, /* address of variable to receive pointer */ /* to new macro substitution context */ - char *pairs[] ) /* pointer to NULL-terminated array of */ + const char * pairs[] ) /* pointer to NULL-terminated array of */ /* {name,value} pair strings; a NULL */ /* value implies undefined; a NULL */ /* argument implies no macros */ @@ -253,9 +253,18 @@ epicsShareAPI macPutValue( /* handle NULL value case: if name was found, delete entry (may be several entries at different scoping levels) */ if ( value == NULL ) { - /* FIXME: shouldn't be able to delete entries from lower scopes */ - while ( ( entry = lookup( handle, name, FALSE ) ) != NULL ) + /* + * FIXME: shouldn't be able to delete entries from lower scopes + * NOTE: when this is changed, this functionality of removing + * a macro from all scopes will still be needed by iocshEnvClear + */ + while ( ( entry = lookup( handle, name, FALSE ) ) != NULL ) { delete( handle, entry ); + + if (strcmp(entry->type, "environment variable") == 0) + break; + } + return 0; } diff --git a/src/libCom/macLib/macEnv.c b/src/libCom/macLib/macEnv.c index 35872d14c..8cdf8bd94 100644 --- a/src/libCom/macLib/macEnv.c +++ b/src/libCom/macLib/macEnv.c @@ -20,16 +20,26 @@ char * epicsShareAPI macEnvExpand(const char *str) +{ + return macDefExpand(str, NULL); +} + +char * epicsShareAPI +macDefExpand(const char *str, MAC_HANDLE *macros) { MAC_HANDLE *handle; - static char *pairs[] = { "", "environ", NULL, NULL }; + static const char * pairs[] = { "", "environ", NULL, NULL }; long destCapacity = 128; char *dest = NULL; int n; - - if (macCreateHandle(&handle, pairs)){ - errlogMessage("macEnvExpand: macCreateHandle failed."); - return NULL; + + if (macros) { + handle = macros; + } else { + if (macCreateHandle(&handle, pairs)){ + errlogMessage("macDefExpand: macCreateHandle failed."); + return NULL; + } } do { @@ -57,7 +67,10 @@ macEnvExpand(const char *str) } done: - if (macDeleteHandle(handle)) - errlogMessage("macEnvExpand: macDeleteHandle failed."); + if (macros == NULL) { + if (macDeleteHandle(handle)) { + errlogMessage("macDefExpand: macDeleteHandle failed."); + } + } return dest; } diff --git a/src/libCom/macLib/macLib.h b/src/libCom/macLib/macLib.h index a99007e9a..c06e67d9e 100644 --- a/src/libCom/macLib/macLib.h +++ b/src/libCom/macLib/macLib.h @@ -52,7 +52,7 @@ epicsShareAPI macCreateHandle( MAC_HANDLE **handle, /* address of variable to receive pointer */ /* to new macro substitution context */ - char *pairs[] /* pointer to NULL-terminated array of */ + const char * pairs[] /* pointer to NULL-terminated array of */ /* {name,value} pair strings; a NULL */ /* value implies undefined; a NULL */ /* argument implies no macros */ @@ -151,6 +151,13 @@ epicsShareAPI macEnvExpand( const char *str /* string to be expanded */ ); +epicsShareFunc char * /* expanded string; NULL if any undefined macros */ +epicsShareAPI macDefExpand( + const char *str, /* string to be expanded */ + MAC_HANDLE *macros /* opaque handle; can be NULL if default */ + /* special characters are to be used */ +); + #ifdef __cplusplus } #endif diff --git a/src/libCom/macLib/macLibREADME b/src/libCom/macLib/macLibREADME index ec521d8a6..f3cda53c2 100644 --- a/src/libCom/macLib/macLibREADME +++ b/src/libCom/macLib/macLibREADME @@ -164,7 +164,14 @@ d) char *macEnvExpand( char *src ); pointer to this null-terminated string. It returns NULL if the source string contains any undefined references. -e) long macReportMacros( MAC_HANDLE *handle ); +e) char *macDefExpand( char *src, MAC_HANDLE *macros ); + + This operates in the same manner as macEnvExpand, but takes an + optional macro handle that can contain a set of macro definitions. + These macros are appended to the set of macros from environment + variables when expanding the string. + +f) long macReportMacros( MAC_HANDLE *handle ); This reports details of current definitions to standard output, and is intended purely for debugging purposes. diff --git a/src/libCom/osi/os/Darwin/osdEnv.c b/src/libCom/osi/os/Darwin/osdEnv.c index 44c0afa0a..35517c1e9 100644 --- a/src/libCom/osi/os/Darwin/osdEnv.c +++ b/src/libCom/osi/os/Darwin/osdEnv.c @@ -1,7 +1,6 @@ /*************************************************************************\ * Copyright (c) 2002 The University of Saskatchewan -* EPICS BASE Versions 3.13.7 -* and higher are distributed subject to a Software License Agreement found +* EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* osdEnv.c */ @@ -30,13 +29,15 @@ #define epicsExportSharedSymbols #include "epicsStdio.h" -#include +#include "envDefs.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); } diff --git a/src/libCom/osi/os/default/osdEnv.c b/src/libCom/osi/os/default/osdEnv.c index ff1675fda..6c67a6999 100644 --- a/src/libCom/osi/os/default/osdEnv.c +++ b/src/libCom/osi/os/default/osdEnv.c @@ -1,7 +1,6 @@ /*************************************************************************\ * Copyright (c) 2002 The University of Saskatchewan -* EPICS BASE Versions 3.13.7 -* and higher are distributed subject to a Software License Agreement found +* EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* osdEnv.c */ @@ -23,11 +22,12 @@ #define epicsExportSharedSymbols #include "epicsStdio.h" -#include -#include -#include -#include +#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 @@ -38,6 +38,8 @@ epicsShareFunc void epicsShareAPI epicsEnvSet (const char *name, const char *val { char *cp; + iocshEnvClear(name); + cp = mallocMustSucceed (strlen (name) + strlen (value) + 2, "epicsEnvSet"); strcpy (cp, name); strcat (cp, "="); diff --git a/src/libCom/osi/os/iOS/osdEnv.c b/src/libCom/osi/os/iOS/osdEnv.c index f55b174a8..c631ec4f8 100644 --- a/src/libCom/osi/os/iOS/osdEnv.c +++ b/src/libCom/osi/os/iOS/osdEnv.c @@ -28,32 +28,15 @@ #include #include #include "epicsFindSymbol.h" - +#include /* * 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; - - 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); - } + iocshEnvClear(name); + setenv(name, value, 1); } /* diff --git a/src/libCom/osi/os/vxWorks/osdEnv.c b/src/libCom/osi/os/vxWorks/osdEnv.c index e41d57440..085611935 100644 --- a/src/libCom/osi/os/vxWorks/osdEnv.c +++ b/src/libCom/osi/os/vxWorks/osdEnv.c @@ -23,12 +23,12 @@ #include #include -#include "epicsStdio.h" -#include -#include - #define epicsExportSharedSymbols +#include "cantProceed.h" #include "epicsFindSymbol.h" +#include "epicsStdio.h" +#include "errlog.h" +#include "iocsh.h" /* * Set the value of an environment variable @@ -39,21 +39,18 @@ epicsShareFunc void epicsShareAPI epicsEnvSet (const char *name, const char *val { char *cp; - 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)); + 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); - } + } } /* diff --git a/src/libCom/test/Makefile b/src/libCom/test/Makefile index 62f73c3e2..3a3d32a8f 100755 --- a/src/libCom/test/Makefile +++ b/src/libCom/test/Makefile @@ -163,10 +163,10 @@ epicsExceptionTest_SRCS += epicsExceptionTest.cpp testHarness_SRCS += epicsExceptionTest.cpp TESTS += epicsExceptionTest -TESTPROD_HOST += macEnvExpandTest -macEnvExpandTest_SRCS += macEnvExpandTest.c -testHarness_SRCS += macEnvExpandTest.c -TESTS += macEnvExpandTest +TESTPROD_HOST += macDefExpandTest +macDefExpandTest_SRCS += macDefExpandTest.c +testHarness_SRCS += macDefExpandTest.c +TESTS += macDefExpandTest TESTPROD_HOST += macLibTest macLibTest_SRCS += macLibTest.c diff --git a/src/libCom/test/epicsRunLibComTests.c b/src/libCom/test/epicsRunLibComTests.c index cf4a2dd7f..5bf6cafd1 100644 --- a/src/libCom/test/epicsRunLibComTests.c +++ b/src/libCom/test/epicsRunLibComTests.c @@ -44,7 +44,7 @@ int epicsThreadTest(void); int epicsTimerTest(void); int epicsTimeTest(void); int epicsTypesTest(void); -int macEnvExpandTest(void); +int macDefExpandTest(void); int macLibTest(void); int ringBytesTest(void); int ringPointerTest(void); @@ -92,7 +92,7 @@ void epicsRunLibComTests(void) runTest(epicsThreadPrivateTest); runTest(epicsTimeTest); runTest(epicsTypesTest); - runTest(macEnvExpandTest); + runTest(macDefExpandTest); runTest(macLibTest); runTest(ringBytesTest); runTest(ringPointerTest); diff --git a/src/libCom/test/macDefExpandTest.c b/src/libCom/test/macDefExpandTest.c new file mode 100644 index 000000000..6114e92a0 --- /dev/null +++ b/src/libCom/test/macDefExpandTest.c @@ -0,0 +1,256 @@ +/*************************************************************************\ +* Copyright (c) 2014 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. +\*************************************************************************/ +/* + * $Revision-Id$ + */ + +#include +#include +#include +#include + +#include "macLib.h" +#include "envDefs.h" +#include "errlog.h" +#include "epicsUnitTest.h" +#include "testMain.h" + + +static void checkMac(MAC_HANDLE *handle, const char *name, const char *value) +{ + char buf[20]; + + buf[19]='\0'; + if(macGetValue(handle, name, buf, 19)<0) { + if(value) + testFail("Macro %s undefined, expected %s", name, value); + else + testPass("Macro %s undefined", name); + } else { + if(!value) + testFail("Macro %s is %s, expected undefined", name, buf); + else if(strcmp(value, buf)==0) + testPass("Macro %s is %s", name, value); + else + testFail("Macro %s is %s, expected %s", name, buf, value); + } +} + +static void macEnvScope(void) +{ + MAC_HANDLE *handle; + char **defines; + static const char *pairs[] = { "", "environ", NULL, NULL }; + + epicsEnvSet("C","3"); + epicsEnvSet("D","4"); + epicsEnvSet("E","5"); + + macCreateHandle(&handle, pairs); + macParseDefns(NULL, "A=1,B=2,E=15", &defines); + macInstallMacros(handle, defines); + + checkMac(handle, "A", "1"); + checkMac(handle, "B", "2"); + checkMac(handle, "C", "3"); + checkMac(handle, "D", "4"); + checkMac(handle, "E", "15"); + checkMac(handle, "F", NULL); + + { + macPushScope(handle); + + macParseDefns(NULL, "A=11,C=13,D=14,G=7", &defines); + macInstallMacros(handle, defines); + + checkMac(handle, "A", "11"); + checkMac(handle, "B", "2"); + checkMac(handle, "C", "13"); + checkMac(handle, "D", "14"); + checkMac(handle, "E", "15"); + checkMac(handle, "F", NULL); + checkMac(handle, "G", "7"); + + epicsEnvSet("D", "24"); + macPutValue(handle, "D", NULL); /* implicit when called through in iocshBody */ + epicsEnvSet("F", "6"); + macPutValue(handle, "F", NULL); /* implicit */ + epicsEnvSet("G", "17"); + macPutValue(handle, "G", NULL); /* implicit */ + + checkMac(handle, "D", "24"); + checkMac(handle, "F", "6"); + checkMac(handle, "G", "17"); + + macPopScope(handle); + } + + checkMac(handle, "A", "1"); + checkMac(handle, "B", "2"); + checkMac(handle, "C", "3"); + checkMac(handle, "D", "24"); + checkMac(handle, "E", "15"); + checkMac(handle, "F", "6"); + checkMac(handle, "G", "17"); + + { + macPushScope(handle); + + macParseDefns(NULL, "D=34,G=27", &defines); + macInstallMacros(handle, defines); + + checkMac(handle, "D", "34"); + checkMac(handle, "G", "27"); + + macPopScope(handle); + } + + checkMac(handle, "D", "24"); + + macDeleteHandle(handle); +} + +static void check(const char *str, const char *macros, const char *expect) +{ + MAC_HANDLE *handle; + char **defines; + static const char *pairs[] = { "", "environ", NULL, NULL }; + + macCreateHandle(&handle, pairs); + macParseDefns(NULL, macros, &defines); + macInstallMacros(handle, defines); + + char *got = macDefExpand(str, handle); + int pass = -1; + + if (expect && !got) { + testDiag("Got NULL, expected \"%s\".\n", expect); + pass = 0; + } + else if (!expect && got) { + testDiag("Got \"%s\", expected NULL.\n", got); + pass = 0; + } + else if (expect && got && strcmp(got, expect)) { + testDiag("Got \"%s\", expected \"%s\".\n", got, expect); + pass = 0; + } + testOk(pass, "%s", str); + + macDeleteHandle(handle); +} + +MAIN(macDefExpandTest) +{ + eltc(0); + testPlan(97); + + check("FOO", "", "FOO"); + + check("${FOO}", "", NULL); + check("${FOO,BAR}", "", NULL); + check("${FOO,BAR=baz}", "", NULL); + check("${FOO,BAR=$(FOO)}", "", NULL); + check("${FOO,FOO}", "", NULL); + check("${FOO,FOO=$(FOO)}", "", NULL); + check("${FOO,BAR=baz,FUM}", "", NULL); + + check("${=}", "", ""); + check("x${=}y", "", "xy"); + + check("${,=}", "", ""); + check("x${,=}y", "", "xy"); + + check("${FOO=}", "", ""); + check("x${FOO=}y", "", "xy"); + + check("${FOO=,}", "", ""); + check("x${FOO=,}y", "", "xy"); + + check("${FOO,FOO=}", "", ""); + check("x${FOO,FOO=}y", "", "xy"); + + check("${FOO=,BAR}", "", ""); + check("x${FOO=,BAR}y", "", "xy"); + + check("${FOO=$(BAR=)}", "", ""); + check("x${FOO=$(BAR=)}y", "", "xy"); + + check("${FOO=,BAR=baz}", "", ""); + check("x${FOO=,BAR=baz}y", "", "xy"); + + check("${FOO=$(BAR),BAR=}", "", ""); + check("x${FOO=$(BAR),BAR=}y", "", "xy"); + + check("${=BAR}", "", "BAR"); + check("x${=BAR}y", "", "xBARy"); + + check("${FOO=BAR}", "", "BAR"); + check("x${FOO=BAR}y", "", "xBARy"); + + epicsEnvSet("FOO","BLETCH"); + check("${FOO}", "", "BLETCH"); + check("${FOO,FOO}", "", "BLETCH"); + check("x${FOO}y", "", "xBLETCHy"); + check("x${FOO}y${FOO}z", "", "xBLETCHyBLETCHz"); + check("${FOO=BAR}", "", "BLETCH"); + check("x${FOO=BAR}y", "", "xBLETCHy"); + check("${FOO=${BAZ}}", "", "BLETCH"); + check("${FOO=${BAZ},BAR=$(BAZ)}", "", "BLETCH"); + check("x${FOO=${BAZ}}y", "", "xBLETCHy"); + check("x${FOO=${BAZ},BAR=$(BAZ)}y", "", "xBLETCHy"); + check("${BAR=${FOO}}", "", "BLETCH"); + check("x${BAR=${FOO}}y", "", "xBLETCHy"); + check("w${BAR=x${FOO}y}z", "", "wxBLETCHyz"); + + check("${FOO,FOO=BAR}", "", "BAR"); + check("x${FOO,FOO=BAR}y", "", "xBARy"); + check("${BAR,BAR=$(FOO)}", "", "BLETCH"); + check("x${BAR,BAR=$(FOO)}y", "", "xBLETCHy"); + check("${BAR,BAR=$($(FOO)),BLETCH=GRIBBLE}", "", "GRIBBLE"); + check("x${BAR,BAR=$($(FOO)),BLETCH=GRIBBLE}y", "", "xGRIBBLEy"); + check("${$(BAR,BAR=$(FOO)),BLETCH=GRIBBLE}", "", "GRIBBLE"); + check("x${$(BAR,BAR=$(FOO)),BLETCH=GRIBBLE}y", "", "xGRIBBLEy"); + + check("${FOO}/${BAR}", "BAR=GLEEP", "BLETCH/GLEEP"); + check("x${FOO}/${BAR}y", "BAR=GLEEP", "xBLETCH/GLEEPy"); + check("${FOO,BAR}/${BAR}", "BAR=GLEEP", "BLETCH/GLEEP"); + check("${FOO,BAR=x}/${BAR}", "BAR=GLEEP", "BLETCH/GLEEP"); + check("${BAZ=BLETCH,BAR}/${BAR}", "BAR=GLEEP", "BLETCH/GLEEP"); + check("${BAZ=BLETCH,BAR=x}/${BAR}", "BAR=GLEEP", "BLETCH/GLEEP"); + + check("${${FOO}}", "BAR=GLEEP,BLETCH=BAR", "BAR"); + check("x${${FOO}}y", "BAR=GLEEP,BLETCH=BAR", "xBARy"); + check("${${FOO}=GRIBBLE}", "BAR=GLEEP,BLETCH=BAR", "BAR"); + check("x${${FOO}=GRIBBLE}y", "BAR=GLEEP,BLETCH=BAR", "xBARy"); + + check("${${FOO}}", "BAR=GLEEP,BLETCH=${BAR}", "GLEEP"); + + epicsEnvSet("FOO","${BAR}"); + check("${FOO}", "BAR=GLEEP,BLETCH=${BAR}" ,"GLEEP"); + + check("${FOO}", "BAR=${BAZ},BLETCH=${BAR}", NULL); + + check("${FOO}", "BAR=${BAZ=GRIBBLE},BLETCH=${BAR}", "GRIBBLE"); + + check("${FOO}", "BAR=${STR1},BLETCH=${BAR},STR1=VAL1,STR2=VAL2", "VAL1"); + + check("${FOO}", "BAR=${STR2},BLETCH=${BAR},STR1=VAL1,STR2=VAL2", "VAL2"); + + check("${FOO}", "BAR=${FOO},BLETCH=${BAR},STR1=VAL1,STR2=VAL2", NULL); + check("${FOO,FOO=$(FOO)}", "BAR=${FOO},BLETCH=${BAR},STR1=VAL1,STR2=VAL2", NULL); + check("${FOO=$(FOO)}", "BAR=${FOO},BLETCH=${BAR},STR1=VAL1,STR2=VAL2", NULL); + check("${FOO=$(BAR),BAR=$(FOO)}", "BAR=${FOO},BLETCH=${BAR},STR1=VAL1,STR2=VAL2", NULL); + + macEnvScope(); + + errlogFlush(); + eltc(1); + return testDone(); +} diff --git a/src/libCom/test/macEnvExpandTest.c b/src/libCom/test/macEnvExpandTest.c deleted file mode 100644 index b56cc366a..000000000 --- a/src/libCom/test/macEnvExpandTest.c +++ /dev/null @@ -1,159 +0,0 @@ -/*************************************************************************\ -* Copyright (c) 2006 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. -\*************************************************************************/ -/* - * $Revision-Id$ - */ - -#include -#include -#include -#include - -#include "macLib.h" -#include "envDefs.h" -#include "errlog.h" -#include "epicsUnitTest.h" -#include "testMain.h" - -static void check(const char *str, const char *expect) -{ - char *got = macEnvExpand(str); - int pass = -1; - - if (expect && !got) { - testDiag("Got NULL, expected \"%s\".\n", expect); - pass = 0; - } - else if (!expect && got) { - testDiag("Got \"%s\", expected NULL.\n", got); - pass = 0; - } - else if (expect && got && strcmp(got, expect)) { - testDiag("Got \"%s\", expected \"%s\".\n", got, expect); - pass = 0; - } - testOk(pass, "%s", str); -} - -MAIN(macEnvExpandTest) -{ - eltc(0); - testPlan(71); - - check("FOO", "FOO"); - - check("${FOO}", NULL); - check("${FOO,BAR}", NULL); - check("${FOO,BAR=baz}", NULL); - check("${FOO,BAR=$(FOO)}", NULL); - check("${FOO,FOO}", NULL); - check("${FOO,FOO=$(FOO)}", NULL); - check("${FOO,BAR=baz,FUM}", NULL); - - check("${=}", ""); - check("x${=}y", "xy"); - - check("${,=}", ""); - check("x${,=}y", "xy"); - - check("${FOO=}", ""); - check("x${FOO=}y", "xy"); - - check("${FOO=,}", ""); - check("x${FOO=,}y", "xy"); - - check("${FOO,FOO=}", ""); - check("x${FOO,FOO=}y", "xy"); - - check("${FOO=,BAR}", ""); - check("x${FOO=,BAR}y", "xy"); - - check("${FOO=$(BAR=)}", ""); - check("x${FOO=$(BAR=)}y", "xy"); - - check("${FOO=,BAR=baz}", ""); - check("x${FOO=,BAR=baz}y", "xy"); - - check("${FOO=$(BAR),BAR=}", ""); - check("x${FOO=$(BAR),BAR=}y", "xy"); - - check("${=BAR}", "BAR"); - check("x${=BAR}y", "xBARy"); - - check("${FOO=BAR}", "BAR"); - check("x${FOO=BAR}y", "xBARy"); - - epicsEnvSet("FOO","BLETCH"); - check("${FOO}", "BLETCH"); - check("${FOO,FOO}", "BLETCH"); - check("x${FOO}y", "xBLETCHy"); - check("x${FOO}y${FOO}z", "xBLETCHyBLETCHz"); - check("${FOO=BAR}", "BLETCH"); - check("x${FOO=BAR}y", "xBLETCHy"); - check("${FOO=${BAZ}}", "BLETCH"); - check("${FOO=${BAZ},BAR=$(BAZ)}", "BLETCH"); - check("x${FOO=${BAZ}}y", "xBLETCHy"); - check("x${FOO=${BAZ},BAR=$(BAZ)}y", "xBLETCHy"); - check("${BAR=${FOO}}", "BLETCH"); - check("x${BAR=${FOO}}y", "xBLETCHy"); - check("w${BAR=x${FOO}y}z", "wxBLETCHyz"); - - check("${FOO,FOO=BAR}", "BAR"); - check("x${FOO,FOO=BAR}y", "xBARy"); - check("${BAR,BAR=$(FOO)}", "BLETCH"); - check("x${BAR,BAR=$(FOO)}y", "xBLETCHy"); - check("${BAR,BAR=$($(FOO)),BLETCH=GRIBBLE}", "GRIBBLE"); - check("x${BAR,BAR=$($(FOO)),BLETCH=GRIBBLE}y", "xGRIBBLEy"); - check("${$(BAR,BAR=$(FOO)),BLETCH=GRIBBLE}", "GRIBBLE"); - check("x${$(BAR,BAR=$(FOO)),BLETCH=GRIBBLE}y", "xGRIBBLEy"); - - epicsEnvSet("BAR","GLEEP"); - check("${FOO}/${BAR}", "BLETCH/GLEEP"); - check("x${FOO}/${BAR}y", "xBLETCH/GLEEPy"); - check("${FOO,BAR}/${BAR}", "BLETCH/GLEEP"); - check("${FOO,BAR=x}/${BAR}", "BLETCH/GLEEP"); - check("${BAZ=BLETCH,BAR}/${BAR}", "BLETCH/GLEEP"); - check("${BAZ=BLETCH,BAR=x}/${BAR}", "BLETCH/GLEEP"); - - epicsEnvSet("BLETCH","BAR"); - check("${${FOO}}", "BAR"); - check("x${${FOO}}y", "xBARy"); - check("${${FOO}=GRIBBLE}", "BAR"); - check("x${${FOO}=GRIBBLE}y", "xBARy"); - - epicsEnvSet("BLETCH","${BAR}"); - check("${${FOO}}", "GLEEP"); - - epicsEnvSet("FOO","${BAR}"); - check("${FOO}","GLEEP"); - - epicsEnvSet("BAR","${BAZ}"); - check("${FOO}", NULL); - - epicsEnvSet("BAR","${BAZ=GRIBBLE}"); - check("${FOO}", "GRIBBLE"); - - epicsEnvSet("BAR","${STR1}"); - epicsEnvSet("STR1","VAL1"); - epicsEnvSet("STR2","VAL2"); - check("${FOO}", "VAL1"); - - epicsEnvSet("BAR","${STR2}"); - check("${FOO}", "VAL2"); - - epicsEnvSet("BAR","${FOO}"); - check("${FOO}", NULL); - check("${FOO,FOO=$(FOO)}", NULL); - check("${FOO=$(FOO)}", NULL); - check("${FOO=$(BAR),BAR=$(FOO)}", NULL); - - errlogFlush(); - eltc(1); - return testDone(); -}