macLib cleanup, added support for scoped macros.

New test program for regular macros as well as the environment variable tests.
This commit is contained in:
Andrew Johnson
2008-03-31 19:30:17 +00:00
parent 2079c128d5
commit 37b371e956
7 changed files with 536 additions and 237 deletions

View File

@@ -1,28 +1,15 @@
/*************************************************************************\
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
* Copyright (c) 2007 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 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.
\*************************************************************************/
/* $Id$
*
* Implementation of core macro substitution library (macLib)
*
* Just about all functionality proposed in tech-talk on 19-Feb-96 has
* been implemented with only minor departures. Missing features are:
*
* 1. All the attribute-setting routines (for setting and getting
* special characters etc)
*
* 2. Error message verbosity control (although a debug level is
* supported)
*
* 3. The macro substitution tool (although the supplied macTest
* program is in fact a simple version of this)
*
* The implementation is fairly unsophisticated and linked lists are
* used to store macro values. Typically there will will be only a
* small number of macros and performance won't be a problem. Special
@@ -31,14 +18,7 @@
* modified or deleted, a "dirty" flag is set; this causes a full
* expansion of all macros the next time a macro value is read
*
* See the files README (original specification from tech-talk) and
* NOTES (valid input file for the macTest program) for further
* information
*
* Testing has been primarily under SunOS, but the code has been built
* and macTest <NOTES run under vxWorks
*
* William Lupton, W. M. Keck Observatory
* Original Author: William Lupton, W. M. Keck Observatory
*/
#include <ctype.h>
@@ -47,27 +27,62 @@
#include <string.h>
#define epicsExportSharedSymbols
#include "dbDefs.h"
#include "errlog.h"
#include "dbmf.h"
#include "macLib.h"
/*** Local structure definitions ***/
/*
* Static function prototypes (these static functions provide an low-level
* library operating on macro entries)
* Entry in linked list of macro definitions
*/
typedef struct mac_entry {
ELLNODE node; /* prev and next pointers */
char *name; /* entry name */
char *type; /* entry type */
char *rawval; /* raw (unexpanded) value */
char *value; /* expanded macro value */
size_t length; /* length of value */
int error; /* error expanding value? */
int visited; /* ever been visited? */
int special; /* special (internal) entry? */
int level; /* scoping level */
} MAC_ENTRY;
/*** Local function prototypes ***/
/*
* These static functions peform low-level operations on macro entries
*/
static MAC_ENTRY *first ( MAC_HANDLE *handle );
static MAC_ENTRY *last ( MAC_HANDLE *handle );
static MAC_ENTRY *next ( MAC_ENTRY *entry );
static MAC_ENTRY *previous( MAC_ENTRY *entry );
static MAC_ENTRY *create( MAC_HANDLE *handle, const char *name, long special );
static MAC_ENTRY *lookup( MAC_HANDLE *handle, const char *name, long special );
static MAC_ENTRY *create( MAC_HANDLE *handle, const char *name, int special );
static MAC_ENTRY *lookup( MAC_HANDLE *handle, const char *name, int special );
static char *rawval( MAC_HANDLE *handle, MAC_ENTRY *entry, const char *value );
static void delete( MAC_HANDLE *handle, MAC_ENTRY *entry );
static long expand( MAC_HANDLE *handle );
static void trans ( MAC_HANDLE *handle, MAC_ENTRY *entry, long level,
char *term, const char **rawval, char **value, char *valend );
static void cpy2val(const char *src, char **value, char *valend);
static void trans ( MAC_HANDLE *handle, MAC_ENTRY *entry, int level,
const char *term, const char **rawval, char **value,
char *valend );
static void refer ( MAC_HANDLE *handle, MAC_ENTRY *entry, int level,
const char **rawval, char **value, char *valend );
static void cpy2val( const char *src, char **value, char *valend );
static char *Strdup( const char *string );
/*** Constants ***/
/*
* Magic number for validating context.
*/
#define MAC_MAGIC 0xbadcafe /* ...sells sub-standard coffee? */
/*
* Flag bits
@@ -75,19 +90,8 @@ static void cpy2val(const char *src, char **value, char *valend);
#define FLAG_SUPPRESS_WARNINGS 0x1
#define FLAG_USE_ENVIRONMENT 0x80
static char *Strdup( const char *string );
/*
* Static variables
*/
static MAC_CHARS macDefaultChars = {
MAC_STARTCHARS,
MAC_LEFTCHARS,
MAC_RIGHTCHARS,
MAC_SEPARCHARS,
MAC_ASSIGNCHARS,
MAC_ESCAPECHARS
};
/*** Library routines ***/
/*
* Create a new macro substitution context and return an opaque handle
@@ -118,7 +122,6 @@ epicsShareAPI macCreateHandle(
/* initialize context */
handle->magic = MAC_MAGIC;
handle->chars = macDefaultChars;
handle->dirty = FALSE;
handle->level = 0;
handle->debug = 0;
@@ -144,13 +147,21 @@ epicsShareAPI macCreateHandle(
return 0;
}
void epicsShareAPI macSuppressWarning(
/*
* Turn on/off suppression of printed warnings from macLib calls
* for the given handle
*/
void
epicsShareAPI macSuppressWarning(
MAC_HANDLE *handle, /* opaque handle */
int falseTrue /*0 means issue, 1 means suppress*/
int suppress /* 0 means issue, 1 means suppress */
)
{
if(handle) handle->flags = (handle->flags & ~FLAG_SUPPRESS_WARNINGS) |
(falseTrue ? FLAG_SUPPRESS_WARNINGS : 0);
if ( handle && handle->magic == MAC_MAGIC ) {
handle->flags = (handle->flags & ~FLAG_SUPPRESS_WARNINGS) |
(suppress ? FLAG_SUPPRESS_WARNINGS : 0);
}
}
/*
@@ -239,7 +250,7 @@ epicsShareAPI macPutValue(
/* handle NULL value case: if name was found, delete entry (may be
several entries at different scoping levels) */
if ( value == NULL ) {
/* FIXME: don't loop or delete entries from any lower scopes */
/* FIXME: shouldn't be able to delete entries from lower scopes */
while ( ( entry = lookup( handle, name, FALSE ) ) != NULL )
delete( handle, entry );
return 0;
@@ -519,7 +530,7 @@ static MAC_ENTRY *previous( MAC_ENTRY *entry )
/*
* Create new macro entry (can assume it doesn't exist)
*/
static MAC_ENTRY *create( MAC_HANDLE *handle, const char *name, long special )
static MAC_ENTRY *create( MAC_HANDLE *handle, const char *name, int special )
{
ELLLIST *list = &handle->list;
MAC_ENTRY *entry = ( MAC_ENTRY * ) dbmfMalloc( sizeof( MAC_ENTRY ) );
@@ -550,10 +561,14 @@ static MAC_ENTRY *create( MAC_HANDLE *handle, const char *name, long special )
/*
* Look up macro entry with matching "special" attribute by name
*/
static MAC_ENTRY *lookup( MAC_HANDLE *handle, const char *name, long special )
static MAC_ENTRY *lookup( MAC_HANDLE *handle, const char *name, int special )
{
MAC_ENTRY *entry;
if ( handle->debug & 2 )
printf( "lookup-> level = %d, name = %s, special = %d\n",
handle->level, name, special );
/* search backwards so scoping works */
for ( entry = last( handle ); entry != NULL; entry = previous( entry ) ) {
if ( entry->special != special )
@@ -574,6 +589,10 @@ static MAC_ENTRY *lookup( MAC_HANDLE *handle, const char *name, long special )
}
}
if ( handle->debug & 2 )
printf( "<-lookup level = %d, name = %s, result = %p\n",
handle->level, name, entry );
return entry;
}
@@ -655,20 +674,16 @@ static long expand( MAC_HANDLE *handle )
* Translate raw macro value (recursive). This is by far the most complicated
* of the macro routines and calls itself recursively both to translate any
* macros referenced in the name and to translate the resulting name
*
* For now, use default special characters
*/
static void trans( MAC_HANDLE *handle, MAC_ENTRY *entry, long level,
char *term, const char **rawval, char **value, char *valend )
static void trans( MAC_HANDLE *handle, MAC_ENTRY *entry, int level,
const char *term, const char **rawval, char **value,
char *valend )
{
char quote;
const char *r, *r2;
char *v, *n2;
char name2[MAC_SIZE + 1];
long discard;
long macRef;
char *macEnd;
MAC_ENTRY *entry2;
const char *r;
char *v;
int discard;
int macRef;
/* return immediately if raw value is NULL */
if ( *rawval == NULL ) return;
@@ -679,8 +694,8 @@ static void trans( MAC_HANDLE *handle, MAC_ENTRY *entry, long level,
/* debug output */
if ( handle->debug & 2 )
printf( "trans-> level = %ld, maxlen = %4u, discard = %s, "
"rawval = %s\n", level, (unsigned int)(valend - *value), discard ? "T" : "F", *rawval );
printf( "trans-> entry = %p, level = %d, maxlen = %u, discard = %s, "
"rawval = %s\n", entry, level, (unsigned int)(valend - *value), discard ? "T" : "F", *rawval );
/* initially not in quotes */
quote = 0;
@@ -705,9 +720,13 @@ static void trans( MAC_HANDLE *handle, MAC_ENTRY *entry, long level,
*( r + 1 ) != '\0' &&
strchr( "({", *( r + 1 ) ) != NULL );
/* if not macro reference (macros are not expanded in single quotes) */
if ( quote == '\'' || !macRef ) {
/* macros are not expanded in single quotes */
if ( macRef && quote != '\'' ) {
/* Handle macro reference */
refer ( handle, entry, level, &r, &v, valend );
}
else {
/* handle escaped characters (escape is discarded if in name) */
if ( *r == '\\' && *( r + 1 ) != '\0' ) {
if ( v < valend && !discard ) *v++ = '\\';
@@ -722,98 +741,11 @@ static void trans( MAC_HANDLE *handle, MAC_ENTRY *entry, long level,
/* ensure string remains properly terminated */
if ( v <= valend ) *v = '\0';
}
/* macro reference */
else {
/* step over '$(' or '${' */
r++;
macEnd = ( *r == '(' ) ? "=)" : "=}";
r++;
/* translate name (may contain macro references); truncated
quietly if too long but always guaranteed zero-terminated */
n2 = name2;
*n2 = '\0';
trans( handle, entry, level + 1, macEnd, &r, &n2, n2 + MAC_SIZE );
name2[MAC_SIZE] = '\0';
/* look up the translated name */
if ( ( entry2 = lookup( handle, name2, FALSE ) ) == NULL ) {
/* not found */
if ( *r == '=' ) {
/* there is a default value, translate that instead */
++r;
trans( handle, entry, level + 1, ++macEnd, &r, &v, valend );
} else {
/* flag the warning and insert $(name) */
if ( (handle->flags & FLAG_SUPPRESS_WARNINGS) == 0 ) {
entry->error = TRUE;
errlogPrintf( "macLib: macro %s is undefined (expanding %s %s)\n",
name2, entry->type, entry->name );
}
if ( v < valend ) *v++ = '$';
if ( v < valend ) *v++ = '(';
cpy2val( name2, &v, valend );
if ( v < valend ) *v++ = ')';
/* ensure string remains properly terminated */
if ( v <= valend ) *v = '\0';
}
continue;
}
/* if reference is recursive, flag an error and insert $(name) */
if ( entry2->visited ) {
if ( (handle->flags & FLAG_SUPPRESS_WARNINGS) == 0 ) {
entry->error = TRUE;
errlogPrintf( "macLib: %s %s is recursive (expanding %s %s)\n",
entry->type, entry->name,
entry2->type, entry2->name );
}
if ( v < valend ) *v++ = '$';
if ( v < valend ) *v++ = '(';
cpy2val( name2, &v, valend );
if ( v < valend ) *v++ = ')';
/* ensure string remains properly terminated */
if ( v <= valend ) *v = '\0';
continue;
}
/* translate but discard any default value, supressing warnings */
if ( *r == '=' ) {
MAC_ENTRY dflt;
int flags = handle->flags;
++r;
handle->flags |= FLAG_SUPPRESS_WARNINGS;
dflt.name = name2;
dflt.type = "default value";
dflt.error = FALSE;
trans( handle, &dflt, level + 1, ++macEnd, &r, &v, v);
handle->flags = flags;
}
/* if all expanded values are valid (not "dirty") copy the
value directly */
if ( !handle->dirty ) {
cpy2val( entry2->value, &v, valend );
}
/* otherwise, translate raw value */
else {
r2 = entry2->rawval;
entry2->visited = TRUE;
trans( handle, entry, level + 1, "", &r2, &v, valend );
entry2->visited = FALSE;
}
}
}
/* debug output */
if ( handle->debug & 2 )
printf( "<-trans level = %ld, length = %4u, value = %s\n",
printf( "<-trans level = %d, length = %4u, value = %s\n",
level, (unsigned int)(v - *value), *value );
/* update pointers to next characters to scan in raw value and to fill
@@ -825,6 +757,162 @@ static void trans( MAC_HANDLE *handle, MAC_ENTRY *entry, long level,
return;
}
/*
* Expand a macro reference, handling default values and defining scoped
* macros as encountered. This code used to part of trans(), but was
* pulled out for ease of understanding.
*/
static void refer ( MAC_HANDLE *handle, MAC_ENTRY *entry, int level,
const char **rawval, char **value, char *valend )
{
const char *r = *rawval;
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;
/* debug output */
if ( handle->debug & 2 )
printf( "refer-> entry = %p, level = %d, maxlen = %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 );
refname[MAC_SIZE] = '\0';
/* Is there a default value? */
if ( *r == '=' ) {
MAC_ENTRY dflt;
int flags = handle->flags;
handle->flags |= FLAG_SUPPRESS_WARNINGS;
/* store its location in case we need it */
defval = ++r;
/* Find the end, discarding its value */
dflt.name = refname;
dflt.type = "default value";
dflt.error = FALSE;
trans( handle, &dflt, level + 1, macEnd+1, &r, &v, v);
handle->flags = flags;
}
/* extract and set values for any scoped macros */
if ( *r == ',' ) {
MAC_ENTRY subs;
int flags = handle->flags;
handle->flags |= FLAG_SUPPRESS_WARNINGS;
subs.type = "scoped macro";
subs.error = FALSE;
macPushScope( handle );
pop = TRUE;
while ( *r == ',' ) {
char subname[MAC_SIZE + 1] = {'\0'};
char subval[MAC_SIZE + 1] = {'\0'};
char *sn = subname;
char *sv = subval;
/* translate the macro name */
++r;
subs.name = refname;
trans( handle, &subs, level + 1, macEnd, &r, &sn, sn + MAC_SIZE );
subname[MAC_SIZE] = '\0';
/* If it has a value, translate that and assign it */
if ( *r == '=' ) {
++r;
subs.name = subname;
trans( handle, &subs, level + 1, macEnd+1, &r, &sv,
sv + MAC_SIZE);
subval[MAC_SIZE] = '\0';
macPutValue( handle, subname, subval );
handle->dirty = TRUE; /* re-expand with new macro values */
}
}
handle->flags = flags;
}
/* Now we can look up the translated name */
refentry = lookup( handle, refname, FALSE );
if ( refentry ) {
if ( !refentry->visited ) {
/* reference is good, use it */
if ( !handle->dirty ) {
/* copy the already-expanded value, and its error status! */
cpy2val( refentry->value, &v, valend );
entry->error = refentry->error;
} else {
/* translate raw value */
const char *rv = refentry->rawval;
refentry->visited = TRUE;
trans( handle, entry, level + 1, "", &rv, &v, valend );
refentry->visited = FALSE;
}
goto cleanup;
}
/* reference is recursive */
entry->error = TRUE;
errval = ",recursive)";
if ( (handle->flags & FLAG_SUPPRESS_WARNINGS) == 0 ) {
errlogPrintf( "macLib: %s %s is recursive (expanding %s %s)\n",
entry->type, entry->name,
refentry->type, refentry->name );
}
} else {
/* no macro found by this name */
if ( defval ) {
/* there was a default value, translate that instead */
trans( handle, entry, level + 1, macEnd+1, &defval, &v, valend );
goto cleanup;
}
entry->error = TRUE;
errval = ",undefined)";
if ( (handle->flags & FLAG_SUPPRESS_WARNINGS) == 0 ) {
errlogPrintf( "macLib: macro %s is undefined (expanding %s %s)\n",
refname, entry->type, entry->name );
}
}
/* Bad reference, insert $(name,errval) */
if ( v < valend ) *v++ = '$';
if ( v < valend ) *v++ = '(';
cpy2val( refname, &v, valend );
cpy2val( errval, &v, valend );
cleanup:
if (pop) {
macPopScope( handle );
}
/* debug output */
if ( handle->debug & 2 )
printf( "<-refer level = %d, length = %4u, value = %s\n",
level, (unsigned int)(v - *value), *value );
*rawval = r;
*value = v;
return;
}
/*
* Copy a string, honoring the 'end of destination string' pointer
* Returns with **value pointing to the '\0' terminator

View File

@@ -1,10 +1,9 @@
/*************************************************************************\
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
* Copyright (c) 2007 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 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.
\*************************************************************************/
/* $Id$
@@ -27,76 +26,24 @@
extern "C" {
#endif
/*
* Standard FALSE and TRUE macros
*/
#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE 1
#endif
/*
* Magic number for validating context.
*/
#define MAC_MAGIC 0xbadcafe /* ...sells sub-standard coffee? */
/*
* Maximum size of macro name or value string (simpler to make fixed)
*/
#define MAC_SIZE 256
/*
* Special characters
*/
#define MAC_STARTCHARS "$" /* characters that indicate macro ref */
#define MAC_LEFTCHARS "{(" /* characters to left of macro name */
#define MAC_RIGHTCHARS "})" /* characters to right of macro name */
#define MAC_SEPARCHARS "," /* characters separating macro defns */
#define MAC_ASSIGNCHARS "=" /* characters denoting values assignment */
#define MAC_ESCAPECHARS "\\" /* characters escaping the next char */
typedef struct {
char *start;
char *left;
char *right;
char *separ;
char *assign;
char *escape;
} MAC_CHARS;
/*
* Macro substitution context. One of these contexts is allocated each time
* macCreateHandle() is called
*/
typedef struct {
long magic; /* magic number (used for authentication) */
MAC_CHARS chars; /* special characters */
long dirty; /* values need expanding from raw values? */
long level; /* scoping level */
long debug; /* debugging level */
int dirty; /* values need expanding from raw values? */
int level; /* scoping level */
int debug; /* debugging level */
ELLLIST list; /* macro name / value list */
int flags; /* operating mode flags */
} MAC_HANDLE;
/*
* Entry in linked list of macro definitions
*/
typedef struct mac_entry {
ELLNODE node; /* prev and next pointers */
char *name; /* entry name */
char *type; /* entry type */
char *rawval; /* raw (unexpanded) value */
char *value; /* expanded macro value */
long length; /* length of value */
long error; /* error expanding value? */
long visited; /* ever been visited? */
long special; /* special (internal) entry? */
long level; /* scoping level */
} MAC_ENTRY;
/*
* Function prototypes (core library)
*/

View File

@@ -20,6 +20,7 @@
#include <string.h>
#define epicsExportSharedSymbols
#include "dbDefs.h"
#include "errlog.h"
#include "macLib.h"
@@ -29,8 +30,6 @@
* name and value strings, terminated with two NULL pointers. Quotes
* and escapes are honored but only removed from macro names (not
* values)
*
* Doesn't yet use special characters (so uses default special characters)
*/
long /* #defns encountered; <0 = ERROR */
epicsShareAPI macParseDefns(
@@ -47,16 +46,16 @@ epicsShareAPI macParseDefns(
{
static const size_t altNumMax = 4;
size_t numMax;
long i;
long num;
long quote;
long escape;
long nbytes;
const char **ptr;
const char **end;
long *del;
int i;
int num;
int quote;
int escape;
size_t nbytes;
const unsigned char **ptr;
const unsigned char **end;
int *del;
char *memCp, **memCpp;
const char *c;
const unsigned char *c;
char *s, *d, **p;
enum { preName, inName, preValue, inValue } state;
@@ -69,9 +68,9 @@ epicsShareAPI macParseDefns(
numMax = strlen( defns );
if ( numMax < altNumMax )
numMax = altNumMax;
ptr = (const char ** ) malloc( numMax * sizeof( char * ) );
end = (const char ** ) malloc( numMax * sizeof( char * ) );
del = ( long * ) malloc( numMax * sizeof( long ) );
ptr = (const unsigned char **) calloc( numMax, sizeof( char * ) );
end = (const unsigned char **) calloc( numMax, sizeof( char * ) );
del = (int *) calloc( numMax, sizeof( int ) );
if ( ptr == NULL || end == NULL || del == NULL ) goto error;
/* go through definitions, noting pointers to starts and ends of macro
@@ -81,7 +80,7 @@ epicsShareAPI macParseDefns(
del[0] = FALSE;
quote = 0;
state = preName;
for ( c = defns; *c != '\0'; c++ ) {
for ( c = (const unsigned char *) defns; *c != '\0'; c++ ) {
/* handle quotes */
if ( quote )
@@ -187,7 +186,7 @@ epicsShareAPI macParseDefns(
*memCpp++ = memCp;
/* copy value regardless of the above */
strncpy( memCp, ptr[i], end[i] - ptr[i] );
strncpy( memCp, (const char *) ptr[i], end[i] - ptr[i] );
memCp += end[i] - ptr[i];
*memCp++ = '\0';
}
@@ -233,7 +232,7 @@ epicsShareAPI macParseDefns(
/* debug output */
if ( handle->debug & 1 )
printf( "macParseDefns() -> %ld\n", num / 2 );
printf( "macParseDefns() -> %d\n", num / 2 );
/* success exit; return number of definitions */
return num / 2;
@@ -262,7 +261,7 @@ epicsShareAPI macInstallMacros(
/* value implies undefined; a NULL */
/* argument implies no macros */
{
long n;
int n;
char **p;
/* debug output */
@@ -279,7 +278,7 @@ epicsShareAPI macInstallMacros(
/* debug output */
if ( handle->debug & 1 )
printf( "macInstallMacros() -> %ld\n", n );
printf( "macInstallMacros() -> %d\n", n );
/* return number of macros defined */
return n;

View File

@@ -102,6 +102,11 @@ macEnvExpandTest_SRCS += macEnvExpandTest.c
testHarness_SRCS += macEnvExpandTest.c
TESTS += macEnvExpandTest
TESTPROD_HOST += macLibTest
macLibTest_SRCS += macLibTest.c
testHarness_SRCS += macLibTest.c
TESTS += macLibTest
TESTPROD_HOST += blockingSockTest
blockingSockTest_SRCS += blockingSockTest.cpp
testHarness_SRCS += blockingSockTest.cpp

View File

@@ -29,6 +29,7 @@ int epicsStringTest(void);
int epicsThreadPriorityTest(void);
int epicsThreadPrivateTest(void);
int epicsTimeTest(void);
int macLibTest(void);
int macEnvExpandTest(void);
int ringPointerTest(void);
int blockingSockTest(void);
@@ -99,6 +100,10 @@ epicsRunLibComTests(void)
epicsTimeTest ();
epicsThreadSleep (1.0);
printf("\n****** Macro Library Test *****\n");
macLibTest ();
epicsThreadSleep (1.0);
printf("\n****** Macro Environment Variable Expansion Test *****\n");
macEnvExpandTest ();
epicsThreadSleep (1.0);

View File

@@ -21,10 +21,15 @@
#include "epicsUnitTest.h"
#include "testMain.h"
void check(const char *str, const char *expect)
int warn;
static void check(const char *str, const char *expect)
{
char *got = macEnvExpand(str);
int pass = -1;
if (expect == NULL) ++warn;
if (expect && !got) {
testDiag("Got NULL, expected \"%s\".\n", expect);
pass = 0;
@@ -42,33 +47,83 @@ void check(const char *str, const char *expect)
MAIN(macEnvExpandTest)
{
int warn = 0;
testPlan(30);
warn = 0;
testPlan(71);
check("FOO", "FOO");
check("${FOO}", NULL); warn++;
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("${BAR=${FOO}}", "GLEEP");
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");
@@ -83,7 +138,7 @@ MAIN(macEnvExpandTest)
check("${FOO}","GLEEP");
epicsEnvSet("BAR","${BAZ}");
check("${FOO}", NULL); warn++;
check("${FOO}", NULL);
epicsEnvSet("BAR","${BAZ=GRIBBLE}");
check("${FOO}", "GRIBBLE");
@@ -97,7 +152,10 @@ MAIN(macEnvExpandTest)
check("${FOO}", "VAL2");
epicsEnvSet("BAR","${FOO}");
check("${FOO}", NULL); warn++;
check("${FOO}", NULL);
check("${FOO,FOO=$(FOO)}", NULL);
check("${FOO=$(FOO)}", NULL);
check("${FOO=$(BAR),BAR=$(FOO)}", NULL);
errlogFlush();
testDiag("%d warning messages from macLib were expected above.\n", warn);

View File

@@ -0,0 +1,197 @@
/*************************************************************************\
* Copyright (c) 2007 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.
\*************************************************************************/
/*
* $Id$
*/
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "macLib.h"
#include "dbDefs.h"
#include "envDefs.h"
#include "errlog.h"
#include "epicsUnitTest.h"
#include "testMain.h"
MAC_HANDLE *h;
static void check(const char *str, const char *expect)
{
char output[MAC_SIZE] = {'\0'};
long status = macExpandString(h, str, output, MAC_SIZE);
long expect_len = strlen(expect+1);
int expect_error = (expect[0] == '!');
int statBad = expect_error ^ (status < 0);
int strBad = strcmp(output, expect+1);
testOk(!statBad && !strBad, "%s => %s", str, output);
if (strBad) {
testDiag("Got \"%s\", expected \"%s\"", output, expect+1);
}
if (statBad) {
testDiag("Return status was %ld, expected %ld",
status, expect_error ? -expect_len : expect_len);
}
}
MAIN(macLibTest)
{
testPlan(81);
if (macCreateHandle(&h, NULL))
testAbort("macCreateHandle() failed");
macSuppressWarning(h, TRUE);
check("FOO", " FOO");
check("$(FOO)", "!$(FOO,undefined)");
check("${FOO}", "!$(FOO,undefined)");
check("${FOO=${FOO}}", "!$(FOO,undefined)");
check("${FOO,FOO}", "!$(FOO,undefined)");
check("${FOO,FOO=${FOO}}", "!$(FOO,recursive)");
check("${FOO,BAR}", "!$(FOO,undefined)");
check("${FOO,BAR=baz}", "!$(FOO,undefined)");
check("${FOO,BAR=${FOO}}", "!$(FOO,undefined)");
check("${FOO,BAR=baz,FUM}", "!$(FOO,undefined)");
check("${FOO=${BAR},BAR=${FOO}}", "!$(FOO,undefined)");
check("${FOO,FOO=${BAR},BAR=${FOO}}", "!$(BAR,recursive)");
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");
macPutValue(h, "FOO", "BLETCH");
/* 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("${N=${FOO}/${BAR},BAR=GLEEP}", " BLETCH/GLEEP");
macPutValue(h, "BAR","GLEEP");
/* FOO = "BLETCH" */
/* 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");
check("${N=${FOO}/${BAR}}", " BLETCH/GLEEP");
macPutValue(h, "BLETCH","BAR");
/* FOO = "BLETCH" */
/* BLETCH = "BAR" */
check("${${FOO}}", " BAR");
check("x${${FOO}}y", " xBARy");
check("${${FOO}=GRIBBLE}", " BAR");
check("x${${FOO}=GRIBBLE}y", " xBARy");
macPutValue(h, "BLETCH","${BAR}");
/* FOO = "BLETCH" */
/* BLETCH = "${BAR}" */
/* BAR = "GLEEP" */
check("${${FOO}}", " GLEEP");
check("${BLETCH=${FOO}}", " GLEEP");
macPutValue(h, "FOO","${BAR}");
/* FOO = "${BAR}" */
/* BAR = "GLEEP" */
check("${FOO}", " GLEEP");
check("${FOO=BLETCH,BAR=BAZ}", " BAZ");
macPutValue(h, "BAR","${BAZ}");
/* FOO = "${BAR}" */
/* BAR = "${BAZ}" */
check("${FOO}", "!$(BAZ,undefined)");
macPutValue(h, "BAR","${BAZ=GRIBBLE}");
/* FOO = "${BAR}" */
/* BAR = "${BAZ=GRIBBLE}" */
check("${FOO}", " GRIBBLE");
check("${FOO,BAZ=GEEK}", " GEEK");
macPutValue(h, "BAR","${STR1}");
macPutValue(h, "STR1","VAL1");
macPutValue(h, "STR2","VAL2");
/* FOO = "${BAR}" */
/* BAR = "${STR1}" */
/* STR1 = "VAL1" */
/* STR2 = "VAL2" */
check("${FOO}", " VAL1");
macPutValue(h, "BAR","${STR2}");
/* FOO = "${BAR}" */
/* BAR = "${STR2}" */
/* STR1 = "VAL1" */
/* STR2 = "VAL2" */
check("${FOO}", " VAL2");
macPutValue(h, "BAR","${FOO}");
/* FOO = "${BAR}" */
/* BAR = "${FOO}" */
check("${FOO}", "!$(BAR,recursive)");
check("${FOO=GRIBBLE}", "!$(BAR,recursive)");
check("${FOO=GRIBBLE,BAR=${FOO}}", "!$(BAR,recursive)");
check("${FOO,FOO=${FOO}}", "!$(FOO,recursive)");
check("${FOO=GRIBBLE,FOO=${FOO}}", "!$(FOO,recursive)");
return testDone();
}