From 37b371e9567ada7c069373ae804ecc6d64aff409 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Mon, 31 Mar 2008 19:30:17 +0000 Subject: [PATCH] macLib cleanup, added support for scoped macros. New test program for regular macros as well as the environment variable tests. --- src/libCom/macLib/macCore.c | 394 ++++++++++++++++---------- src/libCom/macLib/macLib.h | 63 +--- src/libCom/macLib/macUtil.c | 37 ++- src/libCom/test/Makefile | 5 + src/libCom/test/epicsRunLibComTests.c | 5 + src/libCom/test/macEnvExpandTest.c | 72 ++++- src/libCom/test/macLibTest.c | 197 +++++++++++++ 7 files changed, 536 insertions(+), 237 deletions(-) create mode 100644 src/libCom/test/macLibTest.c diff --git a/src/libCom/macLib/macCore.c b/src/libCom/macLib/macCore.c index 6464b734b..bb2407483 100644 --- a/src/libCom/macLib/macCore.c +++ b/src/libCom/macLib/macCore.c @@ -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 @@ -47,27 +27,62 @@ #include #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 diff --git a/src/libCom/macLib/macLib.h b/src/libCom/macLib/macLib.h index c71d6c821..79f505f82 100644 --- a/src/libCom/macLib/macLib.h +++ b/src/libCom/macLib/macLib.h @@ -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) */ diff --git a/src/libCom/macLib/macUtil.c b/src/libCom/macLib/macUtil.c index 65bfd2aed..55246a725 100644 --- a/src/libCom/macLib/macUtil.c +++ b/src/libCom/macLib/macUtil.c @@ -20,6 +20,7 @@ #include #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; diff --git a/src/libCom/test/Makefile b/src/libCom/test/Makefile index 198937699..e644a978d 100644 --- a/src/libCom/test/Makefile +++ b/src/libCom/test/Makefile @@ -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 diff --git a/src/libCom/test/epicsRunLibComTests.c b/src/libCom/test/epicsRunLibComTests.c index 7fa0391aa..8b74c24bc 100644 --- a/src/libCom/test/epicsRunLibComTests.c +++ b/src/libCom/test/epicsRunLibComTests.c @@ -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); diff --git a/src/libCom/test/macEnvExpandTest.c b/src/libCom/test/macEnvExpandTest.c index 5bd3a3845..d0b7f1af4 100644 --- a/src/libCom/test/macEnvExpandTest.c +++ b/src/libCom/test/macEnvExpandTest.c @@ -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); diff --git a/src/libCom/test/macLibTest.c b/src/libCom/test/macLibTest.c new file mode 100644 index 000000000..d2997e4e9 --- /dev/null +++ b/src/libCom/test/macLibTest.c @@ -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 +#include +#include +#include + +#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(); +}