From 236cf1ddf8c73bb08d67f4960900e9daba26fda1 Mon Sep 17 00:00:00 2001 From: Marty Kraimer Date: Wed, 10 Jul 1996 14:49:55 +0000 Subject: [PATCH] added macLib --- src/libCom/Makefile.Unix | 5 + src/libCom/Makefile.Vx | 4 + src/libCom/macCore.c | 749 +++++++++++++++++++++++++++++++++ src/libCom/macLib.h | 198 +++++++++ src/libCom/macLib/macCore.c | 749 +++++++++++++++++++++++++++++++++ src/libCom/macLib/macLib.h | 198 +++++++++ src/libCom/macLib/macLibNOTES | 160 +++++++ src/libCom/macLib/macLibREADME | 288 +++++++++++++ src/libCom/macLib/macUtil.c | 275 ++++++++++++ src/libCom/macLibNOTES | 160 +++++++ src/libCom/macLibREADME | 288 +++++++++++++ src/libCom/macUtil.c | 275 ++++++++++++ 12 files changed, 3349 insertions(+) create mode 100644 src/libCom/macCore.c create mode 100644 src/libCom/macLib.h create mode 100644 src/libCom/macLib/macCore.c create mode 100644 src/libCom/macLib/macLib.h create mode 100644 src/libCom/macLib/macLibNOTES create mode 100644 src/libCom/macLib/macLibREADME create mode 100644 src/libCom/macLib/macUtil.c create mode 100644 src/libCom/macLibNOTES create mode 100644 src/libCom/macLibREADME create mode 100644 src/libCom/macUtil.c diff --git a/src/libCom/Makefile.Unix b/src/libCom/Makefile.Unix index 2c7521bc0..2d8d77cb7 100644 --- a/src/libCom/Makefile.Unix +++ b/src/libCom/Makefile.Unix @@ -21,6 +21,7 @@ INC += pal.h INC += fdMgr.h INC += osiTime.h INC += osiTimer.h +INC += macLib.h SRCS.c += ../bucketLib.c SRCS.c += ../calcPerform.c @@ -44,6 +45,8 @@ SRCS.c += ../assertUNIX.c SRCS.c += ../fdMgr.cc SRCS.c += ../osiTimer.cc SRCS.c += ../osiTimeOSD.cc +SRCS.c += ../macCore.c +SRCS.c += ../macUtil.c LIBOBJS += bucketLib.o LIBOBJS += calcPerform.o @@ -67,6 +70,8 @@ LIBOBJS += assertUNIX.o LIBOBJS += fdMgr.o LIBOBJS += osiTimer.o LIBOBJS += osiTimeOSD.o +LIBOBJS += macCore.o +LIBOBJS += macUtil.o LIBNAME = libCom.a diff --git a/src/libCom/Makefile.Vx b/src/libCom/Makefile.Vx index dec2386f3..1ac84fec6 100644 --- a/src/libCom/Makefile.Vx +++ b/src/libCom/Makefile.Vx @@ -19,6 +19,8 @@ SRCS.c += ../paldef.c SRCS.c += errSymTbl.c SRCS.c += ../errPrintfVX.c SRCS.c += ../assertVX.c +SRCS.c += ../macCore.c +SRCS.c += ../macUtil.c LIBOBJS += calcPerform.o LIBOBJS += cvtFast.o @@ -37,6 +39,8 @@ LIBOBJS += pal.o LIBOBJS += paldef.o LIBOBJS += errPrintfVX.o LIBOBJS += assertVX.o +LIBOBJS += macCore.o +LIBOBJS += macUtil.o LIBNAME = libCom diff --git a/src/libCom/macCore.c b/src/libCom/macCore.c new file mode 100644 index 000000000..b1ff3a718 --- /dev/null +++ b/src/libCom/macCore.c @@ -0,0 +1,749 @@ +/* $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 + * measures are taken to avoid unnecessary expansion of macros whose + * definitions reference other macros. Whenever a macro is created, + * 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 +#include +#include +#include + +#include "macLib.h" + +/* + * Static function prototypes (these static functions provide an low-level + * library operating 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, char *name, long special ); +static MAC_ENTRY *lookup( MAC_HANDLE *handle, char *name, long special ); +static char *rawval( MAC_HANDLE *handle, MAC_ENTRY *entry, 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, char **rawval, char **value, char *valend ); + +static char *Strdup( char *string ); + +/* + * Static variables + */ +static MAC_CHARS macDefaultChars = { + MAC_STARTCHARS, + MAC_LEFTCHARS, + MAC_RIGHTCHARS, + MAC_SEPARCHARS, + MAC_ASSIGNCHARS, + MAC_ESCAPECHARS +}; + +/* + * Create a new macro substitution context and return an opaque handle + * associated with the new context. Also optionally load an initial set + * of macro definitions + */ +long /* 0 = OK; <0 = ERROR */ +macCreateHandle( + MAC_HANDLE **pHandle, /* address of variable to receive pointer */ + /* to new macro substitution context */ + + char *pairs[] ) /* pointer to NULL-terminated array of */ + /* {name,value} pair strings; a NULL */ + /* value implies undefined; a NULL */ + /* argument implies no macros */ +{ + MAC_HANDLE *handle; /* pointer to macro substitution context */ + + /* ensure NULL handle pointer is returned on error */ + *pHandle = NULL; + + /* allocate macro substitution context */ + handle = ( MAC_HANDLE * ) malloc( sizeof( MAC_HANDLE ) ); + if ( handle == NULL ) { + macErrMessage0( -1, "macCreateHandle: failed to allocate context" ); + return -1; + } + + /* initialize context */ + handle->magic = MAC_MAGIC; + handle->chars = macDefaultChars; + handle->dirty = FALSE; + handle->level = 0; + handle->debug = 0; + ellInit( &handle->list ); + + /* if supplied, load macro definitions */ + for ( ; pairs && pairs[0]; pairs += 2 ) { + if ( macPutValue( handle, pairs[0], pairs[1] ) < 0 ) { + free( handle ); + return -1; + } + } + + /* set returned handle pointer */ + *pHandle = handle; + + return 0; +} + +/* + * Expand a string that may contain macro references and return the + * expanded string + * + * This is a very basic and powerful routine. It's basically a wrapper + * around the the translation "engine" trans() + */ +long /* #chars copied, <0 if any macros are */ + /* undefined */ +macExpandString( + MAC_HANDLE *handle, /* opaque handle */ + + char *src, /* source string */ + + char *dest, /* destination string */ + + long maxlen ) /* maximum number of characters to copy */ + /* to destination string */ +{ + MAC_ENTRY entry; + char *s, *d; + long length; + + /* check handle */ + if ( handle == NULL || handle->magic != MAC_MAGIC ) { + macErrMessage0( -1, "macExpandString: NULL or invalid handle\n" ); + return -1; + } + + /* debug output */ + if ( handle->debug & 1 ) + printf( "macExpandString( %s, maxlen = %d )\n", src, maxlen ); + + /* expand raw values if necessary */ + if ( expand( handle ) < 0 ) + macErrMessage0( -1, "macExpandString: failed to expand raw values" ); + + /* fill in necessary fields in fake macro entry struncture */ + entry.name = ""; + entry.error = FALSE; + + /* expand the string */ + s = src; + d = dest; + *d = '\0'; + trans( handle, &entry, 0, "", &s, &d, d + maxlen ); + + /* return +/- #chars copied depending on successful expansion */ + length = d - dest; + length = ( entry.error ) ? -length : length; + + /* debug output */ + if ( handle->debug & 1 ) + printf( "macExpandString() -> %d\n", length ); + + return length; +} + +/* + * Define the value of a macro. A NULL value deletes the macro if it + * already existed + */ +long /* length of value */ +macPutValue( + MAC_HANDLE *handle, /* opaque handle */ + + char *name, /* macro name */ + + char *value ) /* macro value */ +{ + MAC_ENTRY *entry; /* pointer to this macro's entry structure */ + + /* check handle */ + if ( handle == NULL || handle->magic != MAC_MAGIC ) { + macErrMessage0( -1, "macPutValue: NULL or invalid handle\n" ); + return -1; + } + + if ( handle->debug & 1 ) + epicsPrintf( "macPutValue( %s, %s )\n", name, value ? value : "NULL" ); + + /* handle NULL value case: if name was found, delete entry (may be + several entries at different scoping levels) */ + if ( value == NULL ) { + while ( ( entry = lookup( handle, name, FALSE ) ) != NULL ) + delete( handle, entry ); + return 0; + } + + /* look up macro name */ + entry = lookup( handle, name, FALSE ); + + /* new entry must be created if macro doesn't exist or if it only + exists at a lower scoping level */ + if ( entry == NULL || entry->level < handle->level ) { + entry = create( handle, name, FALSE ); + if ( entry == NULL ) { + macErrMessage2( -1, "macPutValue: failed to create macro %s = %s", + name, value ); + return -1; + } + } + + /* copy raw value */ + if ( rawval( handle, entry, value ) == NULL ) { + macErrMessage2( -1, "macPutValue: failed to copy macro %s = %s", + name, value ) ; + return -1; + } + + /* return length of value */ + return strlen( value ); +} + +/* + * Return the value of a macro + */ +long /* #chars copied (<0 if undefined) */ +macGetValue( + MAC_HANDLE *handle, /* opaque handle */ + + char *name, /* macro name or reference */ + + char *value, /* string to receive macro value or name */ + /* argument if macro is undefined */ + + long maxlen ) /* maximum number of characters to copy */ + /* to value */ +{ + MAC_ENTRY *entry; /* pointer to this macro's entry structure */ + long length; /* number of characters returned */ + + /* check handle */ + if ( handle == NULL || handle->magic != MAC_MAGIC ) { + macErrMessage0( -1, "macGetValue: NULL or invalid handle\n" ); + return -1; + } + + /* debug output */ + if ( handle->debug & 1 ) + epicsPrintf( "macGetValue( %s )\n", name ); + + /* look up macro name */ + entry = lookup( handle, name, FALSE ); + + /* if maxlen <= 0 or VALUE == NULL just return -1 / 0 for undefined / + defined macro */ + if ( maxlen <= 0 || value == NULL ) { + return ( entry == NULL ) ? -1 : 0; + } + + /* if not found, copy name to value and return minus #chars copied */ + if ( entry == NULL ) { + strncpy( value, name, maxlen ); + return ( value[maxlen-1] == '\0' ) ? -strlen( name ) : -maxlen; + } + + /* expand raw values if necessary; if fail (can only fail because of + memory allocation failure), return same as if not found */ + if ( expand( handle ) < 0 ) { + macErrMessage0( -1, "macGetValue: failed to expand raw values" ); + strncpy( value, name, maxlen ); + return ( value[maxlen-1] == '\0' ) ? -strlen( name ) : -maxlen; + } + + /* copy value and return +/- #chars copied depending on successful + expansion */ + strncpy( value, entry->value, maxlen ); + length = ( value[maxlen-1] == '\0' ) ? entry->length : maxlen; + + return ( entry->error ) ? -length : length; +} + +/* + * Free up all storage associated with and delete a macro substitution + * context + */ +long /* 0 = OK; <0 = ERROR */ +macDeleteHandle( + MAC_HANDLE *handle ) /* opaque handle */ +{ + MAC_ENTRY *entry, *nextEntry; + + /* check handle */ + if ( handle == NULL || handle->magic != MAC_MAGIC ) { + macErrMessage0( -1, "macDeleteHandle: NULL or invalid handle\n" ); + return -1; + } + + /* debug output */ + if ( handle->debug & 1 ) + printf( "macDeleteHandle()\n" ); + + /* delete all entries */ + for ( entry = first( handle ); entry != NULL; entry = nextEntry ) { + nextEntry = next( entry ); + delete( handle, entry ); + } + + /* clear magic field and free context structure */ + handle->magic = 0; + free( handle ); + + return 0; +} + +/* + * Mark the start of a new scoping level + */ +long /* 0 = OK; <0 = ERROR */ +macPushScope( + MAC_HANDLE *handle ) /* opaque handle */ +{ + MAC_ENTRY *entry; + + /* check handle */ + if ( handle == NULL || handle->magic != MAC_MAGIC ) { + macErrMessage0( -1, "macPushScope: NULL or invalid handle\n" ); + return -1; + } + + /* debug output */ + if ( handle->debug & 1 ) + printf( "macPushScope()\n" ); + + /* increment scoping level */ + handle->level++; + + /* create new "special" entry of name "" */ + entry = create( handle, "", TRUE ); + if ( entry == NULL ) { + handle->level--; + macErrMessage0( -1, "macPushScope: failed to push scope" ); + return -1; + } + + return 0; +} + +/* + * Pop all macros defined since the last call to macPushScope() + */ +long /* 0 = OK; <0 = ERROR */ +macPopScope( + MAC_HANDLE *handle ) /* opaque handle */ +{ + MAC_ENTRY *entry, *nextEntry; + + /* check handle */ + if ( handle == NULL || handle->magic != MAC_MAGIC ) { + macErrMessage0( -1, "macPopScope: NULL or invalid handle\n" ); + return -1; + } + + /* debug output */ + if ( handle->debug & 1 ) + printf( "macPopScope()\n" ); + + /* check scoping level isn't already zero */ + if ( handle->level == 0 ) { + macErrMessage0( -1, "macPopScope: no scope to pop" ); + return -1; + } + + /* look up most recent scope entry */ + entry = lookup( handle, "", TRUE ); + if ( entry == NULL ) { + macErrMessage0( -1, "macPopScope: no scope to pop" ); + return -1; + } + + /* delete scope entry and all macros defined since it */ + for ( ; entry != NULL; entry = nextEntry ) { + nextEntry = next( entry ); + delete( handle, entry ); + } + + /* decrement scoping level */ + handle->level--; + + return 0; +} + +/* + * Report macro details to standard output + */ +long /* 0 = OK; <0 = ERROR */ +macReportMacros( + MAC_HANDLE *handle ) /* opaque handle */ +{ + char *format = "%-1s %-16s %-16s %s\n"; + MAC_ENTRY *entry; + + /* check handle */ + if ( handle == NULL || handle->magic != MAC_MAGIC ) { + macErrMessage0( -1, "macReportMacros: NULL or invalid handle\n" ); + return -1; + } + + /* expand raw values if necessary; report but ignore failure */ + if ( expand( handle ) < 0 ) + macErrMessage0( -1, "macGetValue: failed to expand raw values" ); + + /* loop through macros, reporting names and values */ + epicsPrintf( format, "e", "name", "rawval", "value" ); + epicsPrintf( format, "-", "----", "------", "-----" ); + for ( entry = first( handle ); entry != NULL; entry = next( entry ) ) { + + /* differentiate between "special" (scope marker) and ordinary + entries */ + if ( entry->special ) + epicsPrintf( format, "s", "----", "------", "-----" ); + else + epicsPrintf( format, entry->error ? "*" : " ", entry->name, + entry->rawval ? entry->rawval : "", + entry->value ? entry->value : ""); + } + + return 0; +} + +/******************** beginning of static functions ********************/ + +/* + * Return pointer to first macro entry (could be preprocessor macro) + */ +static MAC_ENTRY *first( MAC_HANDLE *handle ) +{ + return ( MAC_ENTRY * ) ellFirst( &handle->list ); +} + +/* + * Return pointer to last macro entry (could be preprocessor macro) + */ +static MAC_ENTRY *last( MAC_HANDLE *handle ) +{ + return ( MAC_ENTRY * ) ellLast( &handle->list ); +} + +/* + * Return pointer to next macro entry (could be preprocessor macro) + */ +static MAC_ENTRY *next( MAC_ENTRY *entry ) +{ + return ( MAC_ENTRY * ) ellNext( ( ELLNODE * ) entry ); +} + +/* + * Return pointer to previous macro entry (could be preprocessor macro) + */ +static MAC_ENTRY *previous( MAC_ENTRY *entry ) +{ + return ( MAC_ENTRY * ) ellPrevious( ( ELLNODE * ) entry ); +} + +/* + * Create new macro entry (can assume it doesn't exist) + */ +static MAC_ENTRY *create( MAC_HANDLE *handle, char *name, long special ) +{ + ELLLIST *list = &handle->list; + MAC_ENTRY *entry = ( MAC_ENTRY * ) malloc( sizeof( MAC_ENTRY ) ); + + if ( entry != NULL ) { + entry->name = Strdup( name ); + if ( entry->name == NULL ) { + free( entry ); + entry = NULL; + } + else { + entry->rawval = NULL; + entry->value = NULL; + entry->length = 0; + entry->error = FALSE; + entry->visited = FALSE; + entry->special = special; + entry->level = handle->level; + + ellAdd( list, ( ELLNODE * ) entry ); + } + } + + return entry; +} + +/* + * Look up macro entry with matching "special" attribute by name + */ +static MAC_ENTRY *lookup( MAC_HANDLE *handle, char *name, long special ) +{ + MAC_ENTRY *entry; + + /* search backwards so scoping works */ + for ( entry = last( handle ); entry != NULL; entry = previous( entry ) ) { + if ( entry->special != special ) + continue; + if ( strcmp( name, entry->name ) == 0 ) + break; + } + + return entry; +} + +/* + * Copy raw value to macro entry + */ +static char *rawval( MAC_HANDLE *handle, MAC_ENTRY *entry, char *value ) +{ + if ( entry->rawval != NULL ) + free( entry->rawval ); + entry->rawval = Strdup( value ); + + handle->dirty = TRUE; + + return entry->rawval; +} + +/* + * Delete a macro entry; requires re-expansion of macro values since this + * macro may be referenced by another one + */ +static void delete( MAC_HANDLE *handle, MAC_ENTRY *entry ) +{ + ELLLIST *list = &handle->list; + + ellDelete( list, ( ELLNODE * ) entry ); + + free( entry->name ); + if ( entry->rawval != NULL ) + free( entry->rawval ); + if ( entry->value != NULL ) + free( entry->value ); + free( entry ); + + handle->dirty = TRUE; +} + +/* + * Expand macro definitions (expensive but done very infrequently) + */ +static long expand( MAC_HANDLE *handle ) +{ + MAC_ENTRY *entry; + char *rawval; + char *value; + + if ( !handle->dirty ) + return 0; + + for ( entry = first( handle ); entry != NULL; entry = next( entry ) ) { + + if ( handle->debug & 2 ) + epicsPrintf( "\nexpand %s = %s\n", entry->name, + entry->rawval ? entry->rawval : "" ); + + if ( entry->value == NULL ) { + if ( ( entry->value = malloc( MAC_SIZE + 1 ) ) == NULL ) { + return -1; + } + } + + /* start at level 1 so quotes and escapes will be removed from + expanded value */ + rawval = entry->rawval; + value = entry->value; + *value = '\0'; + entry->error = FALSE; + trans( handle, entry, 1, "", &rawval, &value, entry->value + MAC_SIZE ); + entry->length = value - entry->value; + entry->value[MAC_SIZE] = '\0'; + } + + handle->dirty = FALSE; + + return 0; +} + +/* + * 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, char **rawval, char **value, char *valend ) +{ + char quote; + char *r, *v, *n2, *r2; + char name2[MAC_SIZE + 1]; + long discard; + long macRef; + char *macEnd; + MAC_ENTRY *entry2; + + /* return immediately if raw value is NULL */ + if ( *rawval == NULL ) return; + + /* discard quotes and escapes if level is > 0 (i.e. if these aren't + the user's quotes and escapes) */ + discard = ( level > 0 ); + + /* debug output */ + if ( handle->debug & 2 ) + epicsPrintf( "trans-> level = %d, maxlen = %4d, discard = %s, " + "rawval = %s\n", level, valend - *value, discard ? "T" : "F", *rawval ); + + /* initially not in quotes */ + quote = 0; + + /* scan characters until hit terminator or end of string */ + for ( r = *rawval, v = *value; strchr( term, *r ) == NULL; r++ ) { + + /* handle quoted characters (quotes are discarded if in name) */ + if ( quote ) { + if ( *r == quote ) { + quote = 0; + if ( discard ) continue; + } + } + else if ( *r == '"' || *r == '\'' ) { + quote = *r; + if ( discard ) continue; + } + + /* macro reference if '$' followed by '(' or '{' */ + macRef = ( *r == '$' && *( r + 1 ) != '\0' && + strchr( "({", *( r + 1 ) ) != NULL ); + + /* if not macro reference (macros are not expanded in single quotes) */ + if ( quote == '\'' || !macRef ) { + + /* handle escaped characters (escape is discarded if in name) */ + if ( *r == '\\' && *( r + 1 ) != '\0' ) { + if ( v < valend && !discard ) *v++ = '\\'; + if ( v < valend ) *v++ = *++r; + } + + /* copy character to output */ + else { + if ( v < valend ) *v++ = *r; + } + + /* 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 resultant name; if not there, set error flag and + copy reference */ + if ( ( entry2 = lookup( handle, name2, FALSE ) ) == NULL ) { + entry->error = TRUE; + macErrMessage2( -1, "%s: %s referenced but undefined", + entry->name, name2 ); + sprintf( v, "$(%s)", name2 ); v += strlen( v ); + } + + /* check for recursive reference; if so, set error flag and + copy reference */ + else if ( entry2->visited ) { + entry->error = TRUE; + macErrMessage2( -1, "%s: %s referenced recursively", + entry->name, entry2->name ); + sprintf( v, "$(%s)", name2 ); v += strlen( v ); + } + + /* if all expanded values are valid (not "dirty") copy the + value directly */ + else if ( !handle->dirty ) { + strcpy( v, entry2->value ); v += strlen( v ); + } + + /* 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 ) + epicsPrintf( "<-trans level = %d, length = %4d, value = %s\n", + level, v - *value, *value ); + + /* update pointers to next characters to scan in raw value and to fill + in in output value (if at end of input, step back so terminator is + still there to be seen) */ + *rawval = ( *r == '\0' ) ? r - 1 : r; + *value = v; + + return; +} + +/* + * strdup() implementation (because it's not always available) + */ +char *Strdup( char *string ) +{ + char *copy = malloc( strlen( string ) + 1 ); + + if ( copy != NULL ) + strcpy( copy, string ); + + return copy; +} + +/* $Log$ + * Revision 1.9 1996/06/26 09:43:14 wlupton + * first released version + * + */ diff --git a/src/libCom/macLib.h b/src/libCom/macLib.h new file mode 100644 index 000000000..393e74e0f --- /dev/null +++ b/src/libCom/macLib.h @@ -0,0 +1,198 @@ +/* $Id$ + * + * Definitions for macro substitution library (macLib) + * + * William Lupton, W. M. Keck Observatory + */ + +/* + * EPICS include files needed by this file + */ +#include "ellLib.h" +#include "epicsPrint.h" +#include "errMdef.h" + +/* + * 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 0xabadcafe /* ...sells sub-standard coffee? */ + +/* + * Maximum size of macro name or value string (simpler to make fixed) + */ +#define MAC_SIZE 256 + +/* + * Macros passing __FILE__ and __LINE__ to what will be macErrPrintf() + */ +#define macErrMessage0( _status, _format ) \ + errPrintf( _status, __FILE__, __LINE__, _format ) + +#define macErrMessage1( _status, _format, _arg1 ) \ + errPrintf( _status, __FILE__, __LINE__, _format, _arg1 ) + +#define macErrMessage2( _status, _format, _arg1, _arg2 ) \ + errPrintf( _status, __FILE__, __LINE__, _format, _arg1, _arg2 ) + +#define macErrMessage3( _status, _format, _arg1, _arg2, _arg3 ) \ + errPrintf( _status, __FILE__, __LINE__, _format, _arg1, _arg2, _arg3 ) + +/* + * 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 */ + ELLLIST list; /* macro name / value list */ +} MAC_HANDLE; + +/* + * Entry in linked list of macro definitions + */ +typedef struct mac_entry { + ELLNODE node; /* prev and next pointers */ + char *name; /* macro name */ + 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) + */ +long /* 0 = OK; <0 = ERROR */ +macCreateHandle( + MAC_HANDLE **handle, /* address of variable to receive pointer */ + /* to new macro substitution context */ + + char *pairs[] /* pointer to NULL-terminated array of */ + /* {name,value} pair strings; a NULL */ + /* value implies undefined; a NULL */ + /* argument implies no macros */ +); + +long /* #chars copied, <0 if any macros are */ + /* undefined */ +macExpandString( + MAC_HANDLE *handle, /* opaque handle */ + + char *src, /* source string */ + + char *dest, /* destination string */ + + long maxlen /* maximum number of characters to copy */ + /* to destination string */ +); + +long /* length of value */ +macPutValue( + MAC_HANDLE *handle, /* opaque handle */ + + char *name, /* macro name */ + + char *value /* macro value */ +); + +long /* #chars copied (<0 if undefined) */ +macGetValue( + MAC_HANDLE *handle, /* opaque handle */ + + char *name, /* macro name or reference */ + + char *value, /* string to receive macro value or name */ + /* argument if macro is undefined */ + + long maxlen /* maximum number of characters to copy */ + /* to value */ +); + +long /* 0 = OK; <0 = ERROR */ +macDeleteHandle( + MAC_HANDLE *handle /* opaque handle */ +); + +long /* 0 = OK; <0 = ERROR */ +macPushScope( + MAC_HANDLE *handle /* opaque handle */ +); + +long /* 0 = OK; <0 = ERROR */ +macPopScope( + MAC_HANDLE *handle /* opaque handle */ +); + +long /* 0 = OK; <0 = ERROR */ +macReportMacros( + MAC_HANDLE *handle /* opaque handle */ +); + +/* + * Function prototypes (utility library) + */ +long /* #defns encountered; <0 = ERROR */ +macParseDefns( + MAC_HANDLE *handle, /* opaque handle; can be NULL if default */ + /* special characters are to be used */ + + char *defns, /* macro definitions in "a=xxx,b=yyy" */ + /* format */ + + char **pairs[] /* address of variable to receive pointer */ + /* to NULL-terminated array of {name, */ + /* value} pair strings; all storage is */ + /* allocated contiguously */ +); + +long /* #macros defined; <0 = ERROR */ +macInstallMacros( + MAC_HANDLE *handle, /* opaque handle */ + + char *pairs[] /* pointer to NULL-terminated array of */ + /* {name,value} pair strings; a NULL */ + /* value implies undefined; a NULL */ + /* argument implies no macros */ +); + +/* $Log$ + * Revision 1.8 1996/06/26 09:43:16 wlupton + * first released version + * + */ diff --git a/src/libCom/macLib/macCore.c b/src/libCom/macLib/macCore.c new file mode 100644 index 000000000..b1ff3a718 --- /dev/null +++ b/src/libCom/macLib/macCore.c @@ -0,0 +1,749 @@ +/* $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 + * measures are taken to avoid unnecessary expansion of macros whose + * definitions reference other macros. Whenever a macro is created, + * 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 +#include +#include +#include + +#include "macLib.h" + +/* + * Static function prototypes (these static functions provide an low-level + * library operating 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, char *name, long special ); +static MAC_ENTRY *lookup( MAC_HANDLE *handle, char *name, long special ); +static char *rawval( MAC_HANDLE *handle, MAC_ENTRY *entry, 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, char **rawval, char **value, char *valend ); + +static char *Strdup( char *string ); + +/* + * Static variables + */ +static MAC_CHARS macDefaultChars = { + MAC_STARTCHARS, + MAC_LEFTCHARS, + MAC_RIGHTCHARS, + MAC_SEPARCHARS, + MAC_ASSIGNCHARS, + MAC_ESCAPECHARS +}; + +/* + * Create a new macro substitution context and return an opaque handle + * associated with the new context. Also optionally load an initial set + * of macro definitions + */ +long /* 0 = OK; <0 = ERROR */ +macCreateHandle( + MAC_HANDLE **pHandle, /* address of variable to receive pointer */ + /* to new macro substitution context */ + + char *pairs[] ) /* pointer to NULL-terminated array of */ + /* {name,value} pair strings; a NULL */ + /* value implies undefined; a NULL */ + /* argument implies no macros */ +{ + MAC_HANDLE *handle; /* pointer to macro substitution context */ + + /* ensure NULL handle pointer is returned on error */ + *pHandle = NULL; + + /* allocate macro substitution context */ + handle = ( MAC_HANDLE * ) malloc( sizeof( MAC_HANDLE ) ); + if ( handle == NULL ) { + macErrMessage0( -1, "macCreateHandle: failed to allocate context" ); + return -1; + } + + /* initialize context */ + handle->magic = MAC_MAGIC; + handle->chars = macDefaultChars; + handle->dirty = FALSE; + handle->level = 0; + handle->debug = 0; + ellInit( &handle->list ); + + /* if supplied, load macro definitions */ + for ( ; pairs && pairs[0]; pairs += 2 ) { + if ( macPutValue( handle, pairs[0], pairs[1] ) < 0 ) { + free( handle ); + return -1; + } + } + + /* set returned handle pointer */ + *pHandle = handle; + + return 0; +} + +/* + * Expand a string that may contain macro references and return the + * expanded string + * + * This is a very basic and powerful routine. It's basically a wrapper + * around the the translation "engine" trans() + */ +long /* #chars copied, <0 if any macros are */ + /* undefined */ +macExpandString( + MAC_HANDLE *handle, /* opaque handle */ + + char *src, /* source string */ + + char *dest, /* destination string */ + + long maxlen ) /* maximum number of characters to copy */ + /* to destination string */ +{ + MAC_ENTRY entry; + char *s, *d; + long length; + + /* check handle */ + if ( handle == NULL || handle->magic != MAC_MAGIC ) { + macErrMessage0( -1, "macExpandString: NULL or invalid handle\n" ); + return -1; + } + + /* debug output */ + if ( handle->debug & 1 ) + printf( "macExpandString( %s, maxlen = %d )\n", src, maxlen ); + + /* expand raw values if necessary */ + if ( expand( handle ) < 0 ) + macErrMessage0( -1, "macExpandString: failed to expand raw values" ); + + /* fill in necessary fields in fake macro entry struncture */ + entry.name = ""; + entry.error = FALSE; + + /* expand the string */ + s = src; + d = dest; + *d = '\0'; + trans( handle, &entry, 0, "", &s, &d, d + maxlen ); + + /* return +/- #chars copied depending on successful expansion */ + length = d - dest; + length = ( entry.error ) ? -length : length; + + /* debug output */ + if ( handle->debug & 1 ) + printf( "macExpandString() -> %d\n", length ); + + return length; +} + +/* + * Define the value of a macro. A NULL value deletes the macro if it + * already existed + */ +long /* length of value */ +macPutValue( + MAC_HANDLE *handle, /* opaque handle */ + + char *name, /* macro name */ + + char *value ) /* macro value */ +{ + MAC_ENTRY *entry; /* pointer to this macro's entry structure */ + + /* check handle */ + if ( handle == NULL || handle->magic != MAC_MAGIC ) { + macErrMessage0( -1, "macPutValue: NULL or invalid handle\n" ); + return -1; + } + + if ( handle->debug & 1 ) + epicsPrintf( "macPutValue( %s, %s )\n", name, value ? value : "NULL" ); + + /* handle NULL value case: if name was found, delete entry (may be + several entries at different scoping levels) */ + if ( value == NULL ) { + while ( ( entry = lookup( handle, name, FALSE ) ) != NULL ) + delete( handle, entry ); + return 0; + } + + /* look up macro name */ + entry = lookup( handle, name, FALSE ); + + /* new entry must be created if macro doesn't exist or if it only + exists at a lower scoping level */ + if ( entry == NULL || entry->level < handle->level ) { + entry = create( handle, name, FALSE ); + if ( entry == NULL ) { + macErrMessage2( -1, "macPutValue: failed to create macro %s = %s", + name, value ); + return -1; + } + } + + /* copy raw value */ + if ( rawval( handle, entry, value ) == NULL ) { + macErrMessage2( -1, "macPutValue: failed to copy macro %s = %s", + name, value ) ; + return -1; + } + + /* return length of value */ + return strlen( value ); +} + +/* + * Return the value of a macro + */ +long /* #chars copied (<0 if undefined) */ +macGetValue( + MAC_HANDLE *handle, /* opaque handle */ + + char *name, /* macro name or reference */ + + char *value, /* string to receive macro value or name */ + /* argument if macro is undefined */ + + long maxlen ) /* maximum number of characters to copy */ + /* to value */ +{ + MAC_ENTRY *entry; /* pointer to this macro's entry structure */ + long length; /* number of characters returned */ + + /* check handle */ + if ( handle == NULL || handle->magic != MAC_MAGIC ) { + macErrMessage0( -1, "macGetValue: NULL or invalid handle\n" ); + return -1; + } + + /* debug output */ + if ( handle->debug & 1 ) + epicsPrintf( "macGetValue( %s )\n", name ); + + /* look up macro name */ + entry = lookup( handle, name, FALSE ); + + /* if maxlen <= 0 or VALUE == NULL just return -1 / 0 for undefined / + defined macro */ + if ( maxlen <= 0 || value == NULL ) { + return ( entry == NULL ) ? -1 : 0; + } + + /* if not found, copy name to value and return minus #chars copied */ + if ( entry == NULL ) { + strncpy( value, name, maxlen ); + return ( value[maxlen-1] == '\0' ) ? -strlen( name ) : -maxlen; + } + + /* expand raw values if necessary; if fail (can only fail because of + memory allocation failure), return same as if not found */ + if ( expand( handle ) < 0 ) { + macErrMessage0( -1, "macGetValue: failed to expand raw values" ); + strncpy( value, name, maxlen ); + return ( value[maxlen-1] == '\0' ) ? -strlen( name ) : -maxlen; + } + + /* copy value and return +/- #chars copied depending on successful + expansion */ + strncpy( value, entry->value, maxlen ); + length = ( value[maxlen-1] == '\0' ) ? entry->length : maxlen; + + return ( entry->error ) ? -length : length; +} + +/* + * Free up all storage associated with and delete a macro substitution + * context + */ +long /* 0 = OK; <0 = ERROR */ +macDeleteHandle( + MAC_HANDLE *handle ) /* opaque handle */ +{ + MAC_ENTRY *entry, *nextEntry; + + /* check handle */ + if ( handle == NULL || handle->magic != MAC_MAGIC ) { + macErrMessage0( -1, "macDeleteHandle: NULL or invalid handle\n" ); + return -1; + } + + /* debug output */ + if ( handle->debug & 1 ) + printf( "macDeleteHandle()\n" ); + + /* delete all entries */ + for ( entry = first( handle ); entry != NULL; entry = nextEntry ) { + nextEntry = next( entry ); + delete( handle, entry ); + } + + /* clear magic field and free context structure */ + handle->magic = 0; + free( handle ); + + return 0; +} + +/* + * Mark the start of a new scoping level + */ +long /* 0 = OK; <0 = ERROR */ +macPushScope( + MAC_HANDLE *handle ) /* opaque handle */ +{ + MAC_ENTRY *entry; + + /* check handle */ + if ( handle == NULL || handle->magic != MAC_MAGIC ) { + macErrMessage0( -1, "macPushScope: NULL or invalid handle\n" ); + return -1; + } + + /* debug output */ + if ( handle->debug & 1 ) + printf( "macPushScope()\n" ); + + /* increment scoping level */ + handle->level++; + + /* create new "special" entry of name "" */ + entry = create( handle, "", TRUE ); + if ( entry == NULL ) { + handle->level--; + macErrMessage0( -1, "macPushScope: failed to push scope" ); + return -1; + } + + return 0; +} + +/* + * Pop all macros defined since the last call to macPushScope() + */ +long /* 0 = OK; <0 = ERROR */ +macPopScope( + MAC_HANDLE *handle ) /* opaque handle */ +{ + MAC_ENTRY *entry, *nextEntry; + + /* check handle */ + if ( handle == NULL || handle->magic != MAC_MAGIC ) { + macErrMessage0( -1, "macPopScope: NULL or invalid handle\n" ); + return -1; + } + + /* debug output */ + if ( handle->debug & 1 ) + printf( "macPopScope()\n" ); + + /* check scoping level isn't already zero */ + if ( handle->level == 0 ) { + macErrMessage0( -1, "macPopScope: no scope to pop" ); + return -1; + } + + /* look up most recent scope entry */ + entry = lookup( handle, "", TRUE ); + if ( entry == NULL ) { + macErrMessage0( -1, "macPopScope: no scope to pop" ); + return -1; + } + + /* delete scope entry and all macros defined since it */ + for ( ; entry != NULL; entry = nextEntry ) { + nextEntry = next( entry ); + delete( handle, entry ); + } + + /* decrement scoping level */ + handle->level--; + + return 0; +} + +/* + * Report macro details to standard output + */ +long /* 0 = OK; <0 = ERROR */ +macReportMacros( + MAC_HANDLE *handle ) /* opaque handle */ +{ + char *format = "%-1s %-16s %-16s %s\n"; + MAC_ENTRY *entry; + + /* check handle */ + if ( handle == NULL || handle->magic != MAC_MAGIC ) { + macErrMessage0( -1, "macReportMacros: NULL or invalid handle\n" ); + return -1; + } + + /* expand raw values if necessary; report but ignore failure */ + if ( expand( handle ) < 0 ) + macErrMessage0( -1, "macGetValue: failed to expand raw values" ); + + /* loop through macros, reporting names and values */ + epicsPrintf( format, "e", "name", "rawval", "value" ); + epicsPrintf( format, "-", "----", "------", "-----" ); + for ( entry = first( handle ); entry != NULL; entry = next( entry ) ) { + + /* differentiate between "special" (scope marker) and ordinary + entries */ + if ( entry->special ) + epicsPrintf( format, "s", "----", "------", "-----" ); + else + epicsPrintf( format, entry->error ? "*" : " ", entry->name, + entry->rawval ? entry->rawval : "", + entry->value ? entry->value : ""); + } + + return 0; +} + +/******************** beginning of static functions ********************/ + +/* + * Return pointer to first macro entry (could be preprocessor macro) + */ +static MAC_ENTRY *first( MAC_HANDLE *handle ) +{ + return ( MAC_ENTRY * ) ellFirst( &handle->list ); +} + +/* + * Return pointer to last macro entry (could be preprocessor macro) + */ +static MAC_ENTRY *last( MAC_HANDLE *handle ) +{ + return ( MAC_ENTRY * ) ellLast( &handle->list ); +} + +/* + * Return pointer to next macro entry (could be preprocessor macro) + */ +static MAC_ENTRY *next( MAC_ENTRY *entry ) +{ + return ( MAC_ENTRY * ) ellNext( ( ELLNODE * ) entry ); +} + +/* + * Return pointer to previous macro entry (could be preprocessor macro) + */ +static MAC_ENTRY *previous( MAC_ENTRY *entry ) +{ + return ( MAC_ENTRY * ) ellPrevious( ( ELLNODE * ) entry ); +} + +/* + * Create new macro entry (can assume it doesn't exist) + */ +static MAC_ENTRY *create( MAC_HANDLE *handle, char *name, long special ) +{ + ELLLIST *list = &handle->list; + MAC_ENTRY *entry = ( MAC_ENTRY * ) malloc( sizeof( MAC_ENTRY ) ); + + if ( entry != NULL ) { + entry->name = Strdup( name ); + if ( entry->name == NULL ) { + free( entry ); + entry = NULL; + } + else { + entry->rawval = NULL; + entry->value = NULL; + entry->length = 0; + entry->error = FALSE; + entry->visited = FALSE; + entry->special = special; + entry->level = handle->level; + + ellAdd( list, ( ELLNODE * ) entry ); + } + } + + return entry; +} + +/* + * Look up macro entry with matching "special" attribute by name + */ +static MAC_ENTRY *lookup( MAC_HANDLE *handle, char *name, long special ) +{ + MAC_ENTRY *entry; + + /* search backwards so scoping works */ + for ( entry = last( handle ); entry != NULL; entry = previous( entry ) ) { + if ( entry->special != special ) + continue; + if ( strcmp( name, entry->name ) == 0 ) + break; + } + + return entry; +} + +/* + * Copy raw value to macro entry + */ +static char *rawval( MAC_HANDLE *handle, MAC_ENTRY *entry, char *value ) +{ + if ( entry->rawval != NULL ) + free( entry->rawval ); + entry->rawval = Strdup( value ); + + handle->dirty = TRUE; + + return entry->rawval; +} + +/* + * Delete a macro entry; requires re-expansion of macro values since this + * macro may be referenced by another one + */ +static void delete( MAC_HANDLE *handle, MAC_ENTRY *entry ) +{ + ELLLIST *list = &handle->list; + + ellDelete( list, ( ELLNODE * ) entry ); + + free( entry->name ); + if ( entry->rawval != NULL ) + free( entry->rawval ); + if ( entry->value != NULL ) + free( entry->value ); + free( entry ); + + handle->dirty = TRUE; +} + +/* + * Expand macro definitions (expensive but done very infrequently) + */ +static long expand( MAC_HANDLE *handle ) +{ + MAC_ENTRY *entry; + char *rawval; + char *value; + + if ( !handle->dirty ) + return 0; + + for ( entry = first( handle ); entry != NULL; entry = next( entry ) ) { + + if ( handle->debug & 2 ) + epicsPrintf( "\nexpand %s = %s\n", entry->name, + entry->rawval ? entry->rawval : "" ); + + if ( entry->value == NULL ) { + if ( ( entry->value = malloc( MAC_SIZE + 1 ) ) == NULL ) { + return -1; + } + } + + /* start at level 1 so quotes and escapes will be removed from + expanded value */ + rawval = entry->rawval; + value = entry->value; + *value = '\0'; + entry->error = FALSE; + trans( handle, entry, 1, "", &rawval, &value, entry->value + MAC_SIZE ); + entry->length = value - entry->value; + entry->value[MAC_SIZE] = '\0'; + } + + handle->dirty = FALSE; + + return 0; +} + +/* + * 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, char **rawval, char **value, char *valend ) +{ + char quote; + char *r, *v, *n2, *r2; + char name2[MAC_SIZE + 1]; + long discard; + long macRef; + char *macEnd; + MAC_ENTRY *entry2; + + /* return immediately if raw value is NULL */ + if ( *rawval == NULL ) return; + + /* discard quotes and escapes if level is > 0 (i.e. if these aren't + the user's quotes and escapes) */ + discard = ( level > 0 ); + + /* debug output */ + if ( handle->debug & 2 ) + epicsPrintf( "trans-> level = %d, maxlen = %4d, discard = %s, " + "rawval = %s\n", level, valend - *value, discard ? "T" : "F", *rawval ); + + /* initially not in quotes */ + quote = 0; + + /* scan characters until hit terminator or end of string */ + for ( r = *rawval, v = *value; strchr( term, *r ) == NULL; r++ ) { + + /* handle quoted characters (quotes are discarded if in name) */ + if ( quote ) { + if ( *r == quote ) { + quote = 0; + if ( discard ) continue; + } + } + else if ( *r == '"' || *r == '\'' ) { + quote = *r; + if ( discard ) continue; + } + + /* macro reference if '$' followed by '(' or '{' */ + macRef = ( *r == '$' && *( r + 1 ) != '\0' && + strchr( "({", *( r + 1 ) ) != NULL ); + + /* if not macro reference (macros are not expanded in single quotes) */ + if ( quote == '\'' || !macRef ) { + + /* handle escaped characters (escape is discarded if in name) */ + if ( *r == '\\' && *( r + 1 ) != '\0' ) { + if ( v < valend && !discard ) *v++ = '\\'; + if ( v < valend ) *v++ = *++r; + } + + /* copy character to output */ + else { + if ( v < valend ) *v++ = *r; + } + + /* 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 resultant name; if not there, set error flag and + copy reference */ + if ( ( entry2 = lookup( handle, name2, FALSE ) ) == NULL ) { + entry->error = TRUE; + macErrMessage2( -1, "%s: %s referenced but undefined", + entry->name, name2 ); + sprintf( v, "$(%s)", name2 ); v += strlen( v ); + } + + /* check for recursive reference; if so, set error flag and + copy reference */ + else if ( entry2->visited ) { + entry->error = TRUE; + macErrMessage2( -1, "%s: %s referenced recursively", + entry->name, entry2->name ); + sprintf( v, "$(%s)", name2 ); v += strlen( v ); + } + + /* if all expanded values are valid (not "dirty") copy the + value directly */ + else if ( !handle->dirty ) { + strcpy( v, entry2->value ); v += strlen( v ); + } + + /* 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 ) + epicsPrintf( "<-trans level = %d, length = %4d, value = %s\n", + level, v - *value, *value ); + + /* update pointers to next characters to scan in raw value and to fill + in in output value (if at end of input, step back so terminator is + still there to be seen) */ + *rawval = ( *r == '\0' ) ? r - 1 : r; + *value = v; + + return; +} + +/* + * strdup() implementation (because it's not always available) + */ +char *Strdup( char *string ) +{ + char *copy = malloc( strlen( string ) + 1 ); + + if ( copy != NULL ) + strcpy( copy, string ); + + return copy; +} + +/* $Log$ + * Revision 1.9 1996/06/26 09:43:14 wlupton + * first released version + * + */ diff --git a/src/libCom/macLib/macLib.h b/src/libCom/macLib/macLib.h new file mode 100644 index 000000000..393e74e0f --- /dev/null +++ b/src/libCom/macLib/macLib.h @@ -0,0 +1,198 @@ +/* $Id$ + * + * Definitions for macro substitution library (macLib) + * + * William Lupton, W. M. Keck Observatory + */ + +/* + * EPICS include files needed by this file + */ +#include "ellLib.h" +#include "epicsPrint.h" +#include "errMdef.h" + +/* + * 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 0xabadcafe /* ...sells sub-standard coffee? */ + +/* + * Maximum size of macro name or value string (simpler to make fixed) + */ +#define MAC_SIZE 256 + +/* + * Macros passing __FILE__ and __LINE__ to what will be macErrPrintf() + */ +#define macErrMessage0( _status, _format ) \ + errPrintf( _status, __FILE__, __LINE__, _format ) + +#define macErrMessage1( _status, _format, _arg1 ) \ + errPrintf( _status, __FILE__, __LINE__, _format, _arg1 ) + +#define macErrMessage2( _status, _format, _arg1, _arg2 ) \ + errPrintf( _status, __FILE__, __LINE__, _format, _arg1, _arg2 ) + +#define macErrMessage3( _status, _format, _arg1, _arg2, _arg3 ) \ + errPrintf( _status, __FILE__, __LINE__, _format, _arg1, _arg2, _arg3 ) + +/* + * 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 */ + ELLLIST list; /* macro name / value list */ +} MAC_HANDLE; + +/* + * Entry in linked list of macro definitions + */ +typedef struct mac_entry { + ELLNODE node; /* prev and next pointers */ + char *name; /* macro name */ + 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) + */ +long /* 0 = OK; <0 = ERROR */ +macCreateHandle( + MAC_HANDLE **handle, /* address of variable to receive pointer */ + /* to new macro substitution context */ + + char *pairs[] /* pointer to NULL-terminated array of */ + /* {name,value} pair strings; a NULL */ + /* value implies undefined; a NULL */ + /* argument implies no macros */ +); + +long /* #chars copied, <0 if any macros are */ + /* undefined */ +macExpandString( + MAC_HANDLE *handle, /* opaque handle */ + + char *src, /* source string */ + + char *dest, /* destination string */ + + long maxlen /* maximum number of characters to copy */ + /* to destination string */ +); + +long /* length of value */ +macPutValue( + MAC_HANDLE *handle, /* opaque handle */ + + char *name, /* macro name */ + + char *value /* macro value */ +); + +long /* #chars copied (<0 if undefined) */ +macGetValue( + MAC_HANDLE *handle, /* opaque handle */ + + char *name, /* macro name or reference */ + + char *value, /* string to receive macro value or name */ + /* argument if macro is undefined */ + + long maxlen /* maximum number of characters to copy */ + /* to value */ +); + +long /* 0 = OK; <0 = ERROR */ +macDeleteHandle( + MAC_HANDLE *handle /* opaque handle */ +); + +long /* 0 = OK; <0 = ERROR */ +macPushScope( + MAC_HANDLE *handle /* opaque handle */ +); + +long /* 0 = OK; <0 = ERROR */ +macPopScope( + MAC_HANDLE *handle /* opaque handle */ +); + +long /* 0 = OK; <0 = ERROR */ +macReportMacros( + MAC_HANDLE *handle /* opaque handle */ +); + +/* + * Function prototypes (utility library) + */ +long /* #defns encountered; <0 = ERROR */ +macParseDefns( + MAC_HANDLE *handle, /* opaque handle; can be NULL if default */ + /* special characters are to be used */ + + char *defns, /* macro definitions in "a=xxx,b=yyy" */ + /* format */ + + char **pairs[] /* address of variable to receive pointer */ + /* to NULL-terminated array of {name, */ + /* value} pair strings; all storage is */ + /* allocated contiguously */ +); + +long /* #macros defined; <0 = ERROR */ +macInstallMacros( + MAC_HANDLE *handle, /* opaque handle */ + + char *pairs[] /* pointer to NULL-terminated array of */ + /* {name,value} pair strings; a NULL */ + /* value implies undefined; a NULL */ + /* argument implies no macros */ +); + +/* $Log$ + * Revision 1.8 1996/06/26 09:43:16 wlupton + * first released version + * + */ diff --git a/src/libCom/macLib/macLibNOTES b/src/libCom/macLib/macLibNOTES new file mode 100644 index 000000000..d0a396215 --- /dev/null +++ b/src/libCom/macLib/macLibNOTES @@ -0,0 +1,160 @@ +# $Id$ +# +# Test input file for macTest filter, doubling as notes on usage of the +# macro library. Some special strings at start of line are supported: +# +# 1. '#' indicates a comment and is ignored +# +# 2. '%set' is followed by "a=b, c=d" style macro definitions which are +# passed through macParseDefs() and macInstallMacros() +# +# 3. '%push' pushes a scoping level by calling macPushScope() +# +# 4. '%pop' pops a scoping level by calling macPopScope() +# +# 5. '%report' reports macro definitions by calling macReportMacros() +# +# 6. all other lines are expanded by callins macExpandString() +# +introduction +------------ + +See the README file for the library specification and see the header +comments of macCore.c (the core library) for notes on what has been +implemented. + +This file contains tutorial information and examples. It's the +best documentation that there is. + + +simple examples +--------------- + +To define the a, b, c and d macros to be 1, 2, 3 and 4 (note optional +white space is ignored around '=' and ',' characters): + +%set a=1, b=2, c = 3 , d = 4 + +These macros can be dereferenced using '$(xxx)' or '${xxx}' notation: +a = $(a), b = $(b), c = ${c}, d = ${d}. + +Macro values can reference other macros in an arbitrarily complex way. +The only current restrictions are that a macro name or value cannot +exceed 256 characters in length. This restriction could fairly easily +be lifted. + +Here's an example: + +%set x = ${$(y$(z))} + +If this is expanded now: $(x), it won't work because the other macros +aren't defined yet. So: + +%set cash=lucre, ywork=cash, z=work + +Now expansion yields "$(x)" (work -> ywork -> cash -> lucre). + +One can inadvertently set up circular references. For example: + +%set mac1=$(mac2), mac2=$(mac3), mac3=$(mac1) + +An attempt to dereference mac1 gives $(mac1). When a macro expansion +fails, the translation that failed is replaced with the text that could +not be expanded. + +You can get a report of current macro definitions by doing +%report + +The '*' character in the first column indicates a problem expanding that +macro. We'll get rid of these problem macros: +%set mac1, mac2, mac3 + +You can also push a new scoping level by doing +%push + +and pop back to it by doing +%pop + +For example: +%set level = 0 +%push +%set level = 1 +%push +%set level = 2 +%push +%set level = 3 +%report +Level is $(level) +%pop +Level is $(level) +%pop +Level is $(level) +%pop +Level is $(level) +%pop +(That last error was deliberate) + + +quote and escape handling +------------------------- + +Both single and double quotes are supported, as are escapes. Some of the +implications are quite subtle and some of the design choices made may not +meet with universal approval. The basic idea is that a string in which +macro references have been expanded should look like the source string +apart from the where macros have been expanded. This implies that quote +and escape characters should not be removed from the string. + +Single and double quotes are different in that (as in most shells), +macros are substituted within double quotes but not within single +quotes. Back quotes are not special characters. Missing quotes at the +end of a string are automatically and quietly supplied. + +We've already seen some examples but what happens here: $(x)? +Should not that have been expanded? Why wasn't it? The answer is given +later on. As a clue, this is: $(x). This isn't: $(x)! + +Characters may be escaped by preceding them with a \ (back slash). +Escapes work even within quotes, which is more like C than most shells. +Thus, '\'' works, and it doesn't with the C shell. + +Quotes and escapes can also be used in "a=b, c=d" style assignments +but they are not part of macro names. For example: + +%set \= = equal, \' = quote + +defines macros called "=" and "'", not "\=" and "\'". To reference +these macros, "$(=)" ('$(=)') works because "=" is not special in +this context. However the quote must be escaped because quotes are +always special, as in "$(\')" ('$(\')'). + + +miscellaneous notes +------------------- + +In the "a=b, c=d" strings that are parsed by macParseDefns(), a macro +can be set to an empty value by putting nothing after the equals +sign, as in + +%set empty = +resulting in empty = "$(empty)". Omitting the equal sign gives the +macro a NULL rather than empty value (with the result that the macro +is deleted since this is how macPutValue() interprets a NULL value), so + +%set empty +deletes the empty macro, as can be seen when we try to de-reference +it: $(empty). + +The only special characters in these "a=b, c=d" strings are "=", "," +and white space surrounding these characters (plus quotes and escapes +of course). This means that + +%set fred 2 +actually sets a macro called "fred 2" to a NULL value (i.e. it deletes +it if it existed). This is probably wrong and the space should be +taken as an implicit '='. However, to do this and still to support +ignored white space around '=' and ',' characters is a bigger change +than I am prepared to make at present. + +What was the problem expanding "$(x)" before? It was single quotes +from words like "wasn't"! Is this a problem or a feature? diff --git a/src/libCom/macLib/macLibREADME b/src/libCom/macLib/macLibREADME new file mode 100644 index 000000000..0979aef4c --- /dev/null +++ b/src/libCom/macLib/macLibREADME @@ -0,0 +1,288 @@ +README file for etc/macLib directory +------------------------------------ + +This directory contains all files necessary to build and test the +EPICS macro library macLib. The specification for this library +appears later in this file. + +Files in this release are: + +-rw-r--r-- 1 wlupton 141 Sep 22 1995 Makefile +-rw-r--r-- 1 wlupton 522 Jun 23 20:42 Makefile.Unix +-rw-r--r-- 1 wlupton 473 Jun 23 20:43 Makefile.Vx + +-rw-r--r-- 1 wlupton 5052 Jun 25 23:17 NOTES +-rw-r--r-- 1 wlupton 10429 Jun 25 23:34 README + +-rw-r--r-- 1 wlupton 20158 Jun 25 23:40 macCore.c +-rw-r--r-- 1 wlupton 5056 Jun 25 21:33 macLib.h +-rw-r--r-- 1 wlupton 2050 Jun 25 23:18 macTest.c +-rw-r--r-- 1 wlupton 7307 Jun 25 23:13 macUtil.c + +25-Jun-96, William Lupton (wlupton@keck.hawaii.edu), CVS tag: macLib-1-0 +------------------------------------------------------------------------ + +first release + +------------------------------------------------------------------------ + +This is the original final specification for the EPICS macro library: + +From wlupton Mon Feb 19 14:10:08 1996 +To: tech-talk@aps.anl.gov +Subject: Re: macro substitution +Content-Length: 9716 +X-Lines: 251 +Status: RO + +Dear all, + + Here's yet another proposal for the macro library. There has been some +private discussion between myself, Jim and Marty, and I hope that this +is nearly final (or final enough to be implemented). After all the +discussion, the actual implementation is going to be a breeze! + + The biggest changes are a move a way from the "filesystem-like" +routine names and a reduction in the emphasis placed on the standalone +tool. + + I'm afraid there are still a few minor issues to be resolved but +probably nothing that can't wait for an alpha release. + + William + +------------------------------------------------------------------------ + +1. Macro substitution library +----------------------------- + +This library could be used directly by applications which need to +support macro substitution. It will be implemented on all platforms. + +1.1 Core library +---------------- + +The core library provides a minimal set of basic operations. Some +utility routines, described later, use core routines to provide a more +convenient interface for some purposes. + +a) long macCreateHandle( MAC_HANDLE **handle, char *pairs[] ); + + Creates a new macro substitution context and returns an opaque handle + to that context. An application can, if it desires, have several + active contexts, although most will not. + + If desired, an initial set of macro definitions may be loaded + ("pairs" is set to NULL to avoid this). The definitions are in the + "standard" pairs format, as described under macParseDefns(). + + Note that MAC_HANDLE is a typedef for the context structure. The + opaque handle is of type "MAC_HANDLE *", which is a pointer to the + context structure. The memory for this context is allocated by this + routine. + +b) long macGetXxxx( MAC_HANDLE *handle, ); + long macPutXxxx( MAC_HANDLE *handle, ); + + These routines replace the earlier-proposed macIoctl(). "Xxxx" is the + name of the attribute in question. An initial list (which can grow) + might be: + + Attribute default value + --------- ------------- + ErrRoutine errPrintf + Verbose TRUE + + StartChars "$" + LeftChars "{(" + RightChars "})" + SeparChars "," + AssignChars "=" + + A NULL handle can be used to set or get global values (used by + macParseDefns()). + +c) long macGetValue ( MAC_HANDLE *handle, char *name, + char *value, long maxlen ); + + Returns up to maxlen characters of the value of macro "name". "value" + will be zero-terminated if the length of the value is less than + maxlen. The function value will be the number of characters copied to + "value" (see below for behavior if the macro is undefined). If maxlen + is zero, no characters will be copied to "value" (which can be NULL) + and the call can be used to check whether the macro is defined. Note + that truncation of the value is therefore not reported. Is this a + problem? + + If the macro is undefined, the macro reference will be returned in + the value string (if permitted by maxlen) and the function value will + be _minus_ the number of characters copied. Note that treatment of + maxlen is intended to be consistent with what people are used to with + strncpy(). + + "name" can either be just the macro name or can have the syntax of a + macro reference (e.g. it can be "fred" or "$(fred)"). In the latter + case, any characters after the logical end of the macro reference + will be ignored (this is to ease expansion of a string which may + contain many macro references), but no means of returning the number + of characters scanned in "name" is provided. [It may prove expedient + to make macGetValue() simpler and provide a separate routine with + more parameters which returns this information.] + + If the name or value contains a macro reference then the reference + will be expanded recursively. This expansion will detect a direct or + indirect self reference. + + Macro references begin with a "$" immediately followed by either a + "(" or a "{" character. The reference is terminated by the matching + ")" or "}" character. These characters can be changed via + macPut{Start,Left,Right}Chars() above. + +d) long macPutValue( MAC_HANDLE *handle, char *name, char *value ); + + Sets the value of the macro "name". If "value" is NULL, undefines + "name" (it's not an error if "name" was already undefined). Macros + referenced by "value" need not be defined at this point. + + The function value is the length of the value. + +e) long macDeleteHandle( MAC_HANDLE *handle ); + + Marks a handle invalid, and frees all storage associated with it. + + Note that this does not free any strings into which macro values have + been returned. Macro values are always returned into strings which + were pre-allocated by the caller. + +f) long macPushScope( MAC_HANDLE *handle ); + + Marks the start of a new scoping level such that all definitions made + up until the next macPopScope() call will be lost on macPopScope() + and those current at macPushScope() will be re-instated. + + May not be implemented first time round. + +g) long macPopScope( MAC_HANDLE *handle ); + + See above. + +h) Error handling + + These routines conform to 0 (=OK) for success, -1 (=ERROR) for + failure, and small positive values for extra info. I contravened this + for macGetValue() and macExpandString() because I felt that it was + worth returning information both on success / failure and on value + length. + + Errors are reported using an errPrintf()-compatible routine. + + A "verbose" variable will control error reporting. + + +1.2 Utility library +------------------- + +These are convenience functions. If other useful functions emerge during +implementation, the list may grow. + +a) macParseDefns( char *defns, char **pairs[] ); + + This takes a set of macro definitions in "a=xxx,b=yyy" format and + converts them into an array of pointers to character strings which + are, in order, "first name", "first value", "second name", "second + value" etc. The array is terminated with two NULL pointers and all + storage is allocated contiguously so that it can be freed with a + single call to free(). + + This routine is independent of any handle and provides a generally + useful service which may be used elsewhere. Any macro references in + values are not expanded at this point since the referenced macros may + be defined or redefined before the macro actually has to be + translated. + + Shell-style escapes and quotes are supported, as are things like + "A=B,B=$(C$(A)),CA=CA,CB=CB" (sets B to "CB"). White space is + significant within values but ignored elsewhere (i.e. surrounding "=" + and "," characters). + + Probably noone will ever want to, but the special meanings of "$", + "{", "}", "(", ")", "=" and "," can all be changed via macPutXxxx() + calls. This routine does not have a handle argument, so they must be + changed globally for it to use the new definitions. Should it have a + handle argument? This makes it more of a pain to use but guarantees + that there will be no conflicts. I think it should really. + + The function value is the number of definitions encountered, or -1 if + the supplied string is invalid. + +b) long macInstallMacros( MAC_HANDLE *handle, char *pairs[] ); + + This takes an array of pairs as defined above and installs them as + definitions by calling macPutValue(). The pairs array is terminated + by a NULL pointer. + + The function value is the number of macros defined. + +c) long macExpandString( MAC_HANDLE *handle, char *src, + char *dest, long maxlen ); + + This operates on a string which may contain macro references. It + parses the string looking for such references and passes them to + macGetValue() for translation. It returns the expanded string in the + supplied argument. + + The function value is similar to that of macGetValue(). Its absolute + value is the number of characters copied. If negative, at least one + undefined macro has been left unexpanded. + +d) long macReportMacros( MAC_HANDLE *handle ); + + This reports details of current definitions to standard output (or + standard error?). It's purely for debugging purposes. + + +2. Macro substitution tool +-------------------------- + +This section is hardly changed from the previous version. The library will +be implemented first. + +A "macsub" (or "mactool"?) application will be written which would use the +above library and use a command like: + +macsub a=b,c=d e=f -Iaaa -Ibbb + +to parse a file like: + +# comment +%include filename +%xxx = ppp +%yyy = $(xxx) + + +There will also be options to read and write specified files, control the +comment character, control the command prefix, change the macro substitution +characters etc. + +Syntax will be a loose subset of corresponding shell syntax. + +The use of a command prefix should be optional and perhaps it should default +to an empty string. However there will be cases where the file in question +has a syntax over which we have no control, which may include assignments, +"set" commands, "define" commands etc. This is why I proposed a command +prefix, which might be an arbitrary string such as "macro " or might just +be "%" as in the above example. + +The tool can also support the following syntax (or perhaps some equivalent +syntax which will sit more easily inside a file that may have a completely +different syntax from it?): + +file gizmo_database.db +{ + instance1{ pre=part1,parm=big, alarm_limit=20 } + instance2{ pre=part2,parm=medium,alarm_limit=30 } +} + +as an implied multiple include of "gizmo_database.db" with the specified +macro definitions for each include (here's a good application for +macPushScope() and macPopScope() actually). diff --git a/src/libCom/macLib/macUtil.c b/src/libCom/macLib/macUtil.c new file mode 100644 index 000000000..2a6a4af5c --- /dev/null +++ b/src/libCom/macLib/macUtil.c @@ -0,0 +1,275 @@ +/* $Id$ + * + * Implementation of utility macro substitution library (macLib) + * + * William Lupton, W. M. Keck Observatory + */ + +#include +#include +#include +#include + +#include "macLib.h" + +/* + * Parse macros definitions in "a=xxx,b=yyy" format and convert them to + * a contiguously allocated array pointers to names and values, and the + * 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 handle (so uses default special characters) + */ +long /* #defns encountered; <0 = ERROR */ +macParseDefns( + MAC_HANDLE *handle, /* opaque handle; can be NULL if default */ + /* special characters are to be used */ + + char *defns, /* macro definitions in "a=xxx,b=yyy" */ + /* format */ + + char **pairs[] ) /* address of variable to receive pointer */ + /* to NULL-terminated array of {name, */ + /* value} pair strings; all storage is */ + /* allocated contiguously */ +{ + long i; + long num; + long quote; + long escape; + long nbytes; + char **ptr; + char **end; + long *del; + char *memCp, **memCpp; + char *c, *s, *d, **p; + enum { preName, inName, preValue, inValue } state; + + /* debug output */ + if ( handle->debug & 1 ) + printf( "macParseDefns( %s )\n", defns ); + + /* allocate temporary pointer arrays; in worst case they need to have + as many entries as the length of the defns string */ + ptr = ( char ** ) malloc( strlen( defns ) * sizeof( char * ) ); + end = ( char ** ) malloc( strlen( defns ) * sizeof( char * ) ); + del = ( long * ) malloc( strlen( defns ) * sizeof( long ) ); + if ( ptr == NULL || end == NULL || del == NULL ) goto error; + + /* go through definitions, noting pointers to starts and ends of macro + names and values; honor quotes and escapes; ignore white space + around assignment and separator characters */ + num = 0; + del[0] = FALSE; + quote = 0; + state = preName; + for ( c = defns; *c != '\0'; c++ ) { + + /* handle quotes */ + if ( quote ) + quote = ( *c == quote ) ? 0 : quote; + else if ( *c == '\'' || *c == '"' ) + quote = *c; + + /* handle escapes (pointer incremented below) */ + escape = ( *c == '\\' && *( c + 1 ) != '\0' ); + + switch ( state ) { + case preName: + if ( !quote && !escape && ( isspace( *c ) || *c == ',' ) ) break; + ptr[num] = c; + state = inName; + /* fall through (may be empty name) */ + + case inName: + if ( quote || escape || ( *c != '=' && *c != ',' ) ) break; + end[num] = c; + while ( end[num] > ptr[num] && isspace( *( end[num] - 1 ) ) ) + end[num]--; + num++; + del[num] = FALSE; + state = preValue; + if ( *c != ',' ) break; + del[num] = TRUE; + /* fall through (','; will delete) */ + + case preValue: + if ( !quote && !escape && isspace( *c ) ) break; + ptr[num] = c; + state = inValue; + /* fall through (may be empty value) */ + + case inValue: + if ( quote || escape || *c != ',' ) break; + end[num] = c; + while ( end[num] > ptr[num] && isspace( *( end[num] - 1 ) ) ) + end[num]--; + num++; + del[num] = FALSE; + state = preName; + break; + } + + /* if this was escape, increment pointer now (couldn't do + before because could have ignored escape at start of name + or value) */ + if ( escape ) c++; + } + + /* tidy up from state at end of string */ + switch ( state ) { + case preName: + break; + case inName: + end[num] = c; + while ( end[num] > ptr[num] && isspace( *( end[num] - 1 ) ) ) + end[num]--; + num++; + del[num] = TRUE; + case preValue: + ptr[num] = c; + case inValue: + end[num] = c; + while ( end[num] > ptr[num] && isspace( *( end[num] - 1 ) ) ) + end[num]--; + num++; + del[num] = FALSE; + } + + /* debug output */ + if ( handle != NULL && handle->debug & 4 ) + for ( i = 0; i < num; i += 2 ) + printf( "[%d] %.*s = [%d] %.*s (%s) (%s)\n", + end[i+0] - ptr[i+0], end[i+0] - ptr[i+0], ptr[i+0], + end[i+1] - ptr[i+1], end[i+1] - ptr[i+1], ptr[i+1], + del[i+0] ? "del" : "nodel", + del[i+1] ? "del" : "nodel" ); + + /* calculate how much memory to allocate: pointers followed by + strings */ + nbytes = ( num + 2 ) * sizeof( char * ); + for ( i = 0; i < num; i++ ) + nbytes += end[i] - ptr[i] + 1; + + /* allocate memory and set returned pairs pointer */ + memCp = malloc( nbytes ); + if ( memCp == NULL ) goto error; + memCpp = ( char ** ) memCp; + *pairs = memCpp; + + /* copy pointers and strings (memCpp accesses the pointer section + and memCp accesses the string section) */ + memCp += ( num + 2 ) * sizeof( char * ); + for ( i = 0; i < num; i++ ) { + + /* if no '=' followed the name, macro will be deleted */ + if ( del[i] ) + *memCpp++ = NULL; + else + *memCpp++ = memCp; + + /* copy value regardless of the above */ + strncpy( memCp, ptr[i], end[i] - ptr[i] ); + memCp += end[i] - ptr[i]; + *memCp++ = '\0'; + } + + /* add two NULL pointers */ + *memCpp++ = NULL; + *memCpp++ = NULL; + + /* remove quotes and escapes from names in place (unlike values, they + will not be re-parsed) */ + for ( p = *pairs; *p != NULL; p += 2 ) { + quote = 0; + for ( s = d = *p; *s != '\0'; s++ ) { + + /* quotes are not copied */ + if ( quote ) { + if ( *s == quote ) { + quote = 0; + continue; + } + } + else if ( *s == '\'' || *s == '"' ) { + quote = *s; + continue; + } + + /* escapes are not copied but next character is */ + if ( *s == '\\' && *( s + 1 ) != '\0' ) + s++; + + /* others are copied */ + *d++ = *s; + } + + /* need to terminate destination */ + *d++ = '\0'; + } + + /* free workspace */ + free( ptr ); + free( end ); + free( ( char * ) del ); + + /* debug output */ + if ( handle->debug & 1 ) + printf( "macParseDefns() -> %d\n", num / 2 ); + + /* success exit; return number of definitions */ + return num / 2; + + /* error exit */ +error: + macErrMessage0( -1, "macParseDefns: failed to allocate memory" ); + if ( ptr != NULL ) free( ptr ); + if ( end != NULL ) free( end ); + if ( del != NULL ) free( ( char * ) del ); + *pairs = NULL; + return -1; +} + +/* + * Install an array of name / value pairs as macro definitions. The + * array should have an even number of elements followed by at least + * one (preferably two) NULL pointers + */ +long /* #macros defined; <0 = ERROR */ +macInstallMacros( + MAC_HANDLE *handle, /* opaque handle */ + + char *pairs[] ) /* pointer to NULL-terminated array of */ + /* {name,value} pair strings; a NULL */ + /* value implies undefined; a NULL */ + /* argument implies no macros */ +{ + long n; + char **p; + + /* debug output */ + if ( handle->debug & 1 ) + printf( "macInstallMacros( %s, %s, ... )\n", + pairs && pairs[0] ? pairs[0] : "NULL", + pairs && pairs[1] ? pairs[1] : "NULL" ); + + /* go through array defining macros */ + for ( n = 0, p = pairs; p != NULL && p[0] != NULL; n++, p += 2 ) { + if ( macPutValue( handle, p[0], p[1] ) < 0 ) + return -1; + } + + /* debug output */ + if ( handle->debug & 1 ) + printf( "macInstallMacros() -> %d\n", n ); + + /* return number of macros defined */ + return n; +} + +/* $Log$ + * Revision 1.6 1996/06/26 09:43:19 wlupton + * first released version + * + */ diff --git a/src/libCom/macLibNOTES b/src/libCom/macLibNOTES new file mode 100644 index 000000000..d0a396215 --- /dev/null +++ b/src/libCom/macLibNOTES @@ -0,0 +1,160 @@ +# $Id$ +# +# Test input file for macTest filter, doubling as notes on usage of the +# macro library. Some special strings at start of line are supported: +# +# 1. '#' indicates a comment and is ignored +# +# 2. '%set' is followed by "a=b, c=d" style macro definitions which are +# passed through macParseDefs() and macInstallMacros() +# +# 3. '%push' pushes a scoping level by calling macPushScope() +# +# 4. '%pop' pops a scoping level by calling macPopScope() +# +# 5. '%report' reports macro definitions by calling macReportMacros() +# +# 6. all other lines are expanded by callins macExpandString() +# +introduction +------------ + +See the README file for the library specification and see the header +comments of macCore.c (the core library) for notes on what has been +implemented. + +This file contains tutorial information and examples. It's the +best documentation that there is. + + +simple examples +--------------- + +To define the a, b, c and d macros to be 1, 2, 3 and 4 (note optional +white space is ignored around '=' and ',' characters): + +%set a=1, b=2, c = 3 , d = 4 + +These macros can be dereferenced using '$(xxx)' or '${xxx}' notation: +a = $(a), b = $(b), c = ${c}, d = ${d}. + +Macro values can reference other macros in an arbitrarily complex way. +The only current restrictions are that a macro name or value cannot +exceed 256 characters in length. This restriction could fairly easily +be lifted. + +Here's an example: + +%set x = ${$(y$(z))} + +If this is expanded now: $(x), it won't work because the other macros +aren't defined yet. So: + +%set cash=lucre, ywork=cash, z=work + +Now expansion yields "$(x)" (work -> ywork -> cash -> lucre). + +One can inadvertently set up circular references. For example: + +%set mac1=$(mac2), mac2=$(mac3), mac3=$(mac1) + +An attempt to dereference mac1 gives $(mac1). When a macro expansion +fails, the translation that failed is replaced with the text that could +not be expanded. + +You can get a report of current macro definitions by doing +%report + +The '*' character in the first column indicates a problem expanding that +macro. We'll get rid of these problem macros: +%set mac1, mac2, mac3 + +You can also push a new scoping level by doing +%push + +and pop back to it by doing +%pop + +For example: +%set level = 0 +%push +%set level = 1 +%push +%set level = 2 +%push +%set level = 3 +%report +Level is $(level) +%pop +Level is $(level) +%pop +Level is $(level) +%pop +Level is $(level) +%pop +(That last error was deliberate) + + +quote and escape handling +------------------------- + +Both single and double quotes are supported, as are escapes. Some of the +implications are quite subtle and some of the design choices made may not +meet with universal approval. The basic idea is that a string in which +macro references have been expanded should look like the source string +apart from the where macros have been expanded. This implies that quote +and escape characters should not be removed from the string. + +Single and double quotes are different in that (as in most shells), +macros are substituted within double quotes but not within single +quotes. Back quotes are not special characters. Missing quotes at the +end of a string are automatically and quietly supplied. + +We've already seen some examples but what happens here: $(x)? +Should not that have been expanded? Why wasn't it? The answer is given +later on. As a clue, this is: $(x). This isn't: $(x)! + +Characters may be escaped by preceding them with a \ (back slash). +Escapes work even within quotes, which is more like C than most shells. +Thus, '\'' works, and it doesn't with the C shell. + +Quotes and escapes can also be used in "a=b, c=d" style assignments +but they are not part of macro names. For example: + +%set \= = equal, \' = quote + +defines macros called "=" and "'", not "\=" and "\'". To reference +these macros, "$(=)" ('$(=)') works because "=" is not special in +this context. However the quote must be escaped because quotes are +always special, as in "$(\')" ('$(\')'). + + +miscellaneous notes +------------------- + +In the "a=b, c=d" strings that are parsed by macParseDefns(), a macro +can be set to an empty value by putting nothing after the equals +sign, as in + +%set empty = +resulting in empty = "$(empty)". Omitting the equal sign gives the +macro a NULL rather than empty value (with the result that the macro +is deleted since this is how macPutValue() interprets a NULL value), so + +%set empty +deletes the empty macro, as can be seen when we try to de-reference +it: $(empty). + +The only special characters in these "a=b, c=d" strings are "=", "," +and white space surrounding these characters (plus quotes and escapes +of course). This means that + +%set fred 2 +actually sets a macro called "fred 2" to a NULL value (i.e. it deletes +it if it existed). This is probably wrong and the space should be +taken as an implicit '='. However, to do this and still to support +ignored white space around '=' and ',' characters is a bigger change +than I am prepared to make at present. + +What was the problem expanding "$(x)" before? It was single quotes +from words like "wasn't"! Is this a problem or a feature? diff --git a/src/libCom/macLibREADME b/src/libCom/macLibREADME new file mode 100644 index 000000000..0979aef4c --- /dev/null +++ b/src/libCom/macLibREADME @@ -0,0 +1,288 @@ +README file for etc/macLib directory +------------------------------------ + +This directory contains all files necessary to build and test the +EPICS macro library macLib. The specification for this library +appears later in this file. + +Files in this release are: + +-rw-r--r-- 1 wlupton 141 Sep 22 1995 Makefile +-rw-r--r-- 1 wlupton 522 Jun 23 20:42 Makefile.Unix +-rw-r--r-- 1 wlupton 473 Jun 23 20:43 Makefile.Vx + +-rw-r--r-- 1 wlupton 5052 Jun 25 23:17 NOTES +-rw-r--r-- 1 wlupton 10429 Jun 25 23:34 README + +-rw-r--r-- 1 wlupton 20158 Jun 25 23:40 macCore.c +-rw-r--r-- 1 wlupton 5056 Jun 25 21:33 macLib.h +-rw-r--r-- 1 wlupton 2050 Jun 25 23:18 macTest.c +-rw-r--r-- 1 wlupton 7307 Jun 25 23:13 macUtil.c + +25-Jun-96, William Lupton (wlupton@keck.hawaii.edu), CVS tag: macLib-1-0 +------------------------------------------------------------------------ + +first release + +------------------------------------------------------------------------ + +This is the original final specification for the EPICS macro library: + +From wlupton Mon Feb 19 14:10:08 1996 +To: tech-talk@aps.anl.gov +Subject: Re: macro substitution +Content-Length: 9716 +X-Lines: 251 +Status: RO + +Dear all, + + Here's yet another proposal for the macro library. There has been some +private discussion between myself, Jim and Marty, and I hope that this +is nearly final (or final enough to be implemented). After all the +discussion, the actual implementation is going to be a breeze! + + The biggest changes are a move a way from the "filesystem-like" +routine names and a reduction in the emphasis placed on the standalone +tool. + + I'm afraid there are still a few minor issues to be resolved but +probably nothing that can't wait for an alpha release. + + William + +------------------------------------------------------------------------ + +1. Macro substitution library +----------------------------- + +This library could be used directly by applications which need to +support macro substitution. It will be implemented on all platforms. + +1.1 Core library +---------------- + +The core library provides a minimal set of basic operations. Some +utility routines, described later, use core routines to provide a more +convenient interface for some purposes. + +a) long macCreateHandle( MAC_HANDLE **handle, char *pairs[] ); + + Creates a new macro substitution context and returns an opaque handle + to that context. An application can, if it desires, have several + active contexts, although most will not. + + If desired, an initial set of macro definitions may be loaded + ("pairs" is set to NULL to avoid this). The definitions are in the + "standard" pairs format, as described under macParseDefns(). + + Note that MAC_HANDLE is a typedef for the context structure. The + opaque handle is of type "MAC_HANDLE *", which is a pointer to the + context structure. The memory for this context is allocated by this + routine. + +b) long macGetXxxx( MAC_HANDLE *handle, ); + long macPutXxxx( MAC_HANDLE *handle, ); + + These routines replace the earlier-proposed macIoctl(). "Xxxx" is the + name of the attribute in question. An initial list (which can grow) + might be: + + Attribute default value + --------- ------------- + ErrRoutine errPrintf + Verbose TRUE + + StartChars "$" + LeftChars "{(" + RightChars "})" + SeparChars "," + AssignChars "=" + + A NULL handle can be used to set or get global values (used by + macParseDefns()). + +c) long macGetValue ( MAC_HANDLE *handle, char *name, + char *value, long maxlen ); + + Returns up to maxlen characters of the value of macro "name". "value" + will be zero-terminated if the length of the value is less than + maxlen. The function value will be the number of characters copied to + "value" (see below for behavior if the macro is undefined). If maxlen + is zero, no characters will be copied to "value" (which can be NULL) + and the call can be used to check whether the macro is defined. Note + that truncation of the value is therefore not reported. Is this a + problem? + + If the macro is undefined, the macro reference will be returned in + the value string (if permitted by maxlen) and the function value will + be _minus_ the number of characters copied. Note that treatment of + maxlen is intended to be consistent with what people are used to with + strncpy(). + + "name" can either be just the macro name or can have the syntax of a + macro reference (e.g. it can be "fred" or "$(fred)"). In the latter + case, any characters after the logical end of the macro reference + will be ignored (this is to ease expansion of a string which may + contain many macro references), but no means of returning the number + of characters scanned in "name" is provided. [It may prove expedient + to make macGetValue() simpler and provide a separate routine with + more parameters which returns this information.] + + If the name or value contains a macro reference then the reference + will be expanded recursively. This expansion will detect a direct or + indirect self reference. + + Macro references begin with a "$" immediately followed by either a + "(" or a "{" character. The reference is terminated by the matching + ")" or "}" character. These characters can be changed via + macPut{Start,Left,Right}Chars() above. + +d) long macPutValue( MAC_HANDLE *handle, char *name, char *value ); + + Sets the value of the macro "name". If "value" is NULL, undefines + "name" (it's not an error if "name" was already undefined). Macros + referenced by "value" need not be defined at this point. + + The function value is the length of the value. + +e) long macDeleteHandle( MAC_HANDLE *handle ); + + Marks a handle invalid, and frees all storage associated with it. + + Note that this does not free any strings into which macro values have + been returned. Macro values are always returned into strings which + were pre-allocated by the caller. + +f) long macPushScope( MAC_HANDLE *handle ); + + Marks the start of a new scoping level such that all definitions made + up until the next macPopScope() call will be lost on macPopScope() + and those current at macPushScope() will be re-instated. + + May not be implemented first time round. + +g) long macPopScope( MAC_HANDLE *handle ); + + See above. + +h) Error handling + + These routines conform to 0 (=OK) for success, -1 (=ERROR) for + failure, and small positive values for extra info. I contravened this + for macGetValue() and macExpandString() because I felt that it was + worth returning information both on success / failure and on value + length. + + Errors are reported using an errPrintf()-compatible routine. + + A "verbose" variable will control error reporting. + + +1.2 Utility library +------------------- + +These are convenience functions. If other useful functions emerge during +implementation, the list may grow. + +a) macParseDefns( char *defns, char **pairs[] ); + + This takes a set of macro definitions in "a=xxx,b=yyy" format and + converts them into an array of pointers to character strings which + are, in order, "first name", "first value", "second name", "second + value" etc. The array is terminated with two NULL pointers and all + storage is allocated contiguously so that it can be freed with a + single call to free(). + + This routine is independent of any handle and provides a generally + useful service which may be used elsewhere. Any macro references in + values are not expanded at this point since the referenced macros may + be defined or redefined before the macro actually has to be + translated. + + Shell-style escapes and quotes are supported, as are things like + "A=B,B=$(C$(A)),CA=CA,CB=CB" (sets B to "CB"). White space is + significant within values but ignored elsewhere (i.e. surrounding "=" + and "," characters). + + Probably noone will ever want to, but the special meanings of "$", + "{", "}", "(", ")", "=" and "," can all be changed via macPutXxxx() + calls. This routine does not have a handle argument, so they must be + changed globally for it to use the new definitions. Should it have a + handle argument? This makes it more of a pain to use but guarantees + that there will be no conflicts. I think it should really. + + The function value is the number of definitions encountered, or -1 if + the supplied string is invalid. + +b) long macInstallMacros( MAC_HANDLE *handle, char *pairs[] ); + + This takes an array of pairs as defined above and installs them as + definitions by calling macPutValue(). The pairs array is terminated + by a NULL pointer. + + The function value is the number of macros defined. + +c) long macExpandString( MAC_HANDLE *handle, char *src, + char *dest, long maxlen ); + + This operates on a string which may contain macro references. It + parses the string looking for such references and passes them to + macGetValue() for translation. It returns the expanded string in the + supplied argument. + + The function value is similar to that of macGetValue(). Its absolute + value is the number of characters copied. If negative, at least one + undefined macro has been left unexpanded. + +d) long macReportMacros( MAC_HANDLE *handle ); + + This reports details of current definitions to standard output (or + standard error?). It's purely for debugging purposes. + + +2. Macro substitution tool +-------------------------- + +This section is hardly changed from the previous version. The library will +be implemented first. + +A "macsub" (or "mactool"?) application will be written which would use the +above library and use a command like: + +macsub a=b,c=d e=f -Iaaa -Ibbb + +to parse a file like: + +# comment +%include filename +%xxx = ppp +%yyy = $(xxx) + + +There will also be options to read and write specified files, control the +comment character, control the command prefix, change the macro substitution +characters etc. + +Syntax will be a loose subset of corresponding shell syntax. + +The use of a command prefix should be optional and perhaps it should default +to an empty string. However there will be cases where the file in question +has a syntax over which we have no control, which may include assignments, +"set" commands, "define" commands etc. This is why I proposed a command +prefix, which might be an arbitrary string such as "macro " or might just +be "%" as in the above example. + +The tool can also support the following syntax (or perhaps some equivalent +syntax which will sit more easily inside a file that may have a completely +different syntax from it?): + +file gizmo_database.db +{ + instance1{ pre=part1,parm=big, alarm_limit=20 } + instance2{ pre=part2,parm=medium,alarm_limit=30 } +} + +as an implied multiple include of "gizmo_database.db" with the specified +macro definitions for each include (here's a good application for +macPushScope() and macPopScope() actually). diff --git a/src/libCom/macUtil.c b/src/libCom/macUtil.c new file mode 100644 index 000000000..2a6a4af5c --- /dev/null +++ b/src/libCom/macUtil.c @@ -0,0 +1,275 @@ +/* $Id$ + * + * Implementation of utility macro substitution library (macLib) + * + * William Lupton, W. M. Keck Observatory + */ + +#include +#include +#include +#include + +#include "macLib.h" + +/* + * Parse macros definitions in "a=xxx,b=yyy" format and convert them to + * a contiguously allocated array pointers to names and values, and the + * 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 handle (so uses default special characters) + */ +long /* #defns encountered; <0 = ERROR */ +macParseDefns( + MAC_HANDLE *handle, /* opaque handle; can be NULL if default */ + /* special characters are to be used */ + + char *defns, /* macro definitions in "a=xxx,b=yyy" */ + /* format */ + + char **pairs[] ) /* address of variable to receive pointer */ + /* to NULL-terminated array of {name, */ + /* value} pair strings; all storage is */ + /* allocated contiguously */ +{ + long i; + long num; + long quote; + long escape; + long nbytes; + char **ptr; + char **end; + long *del; + char *memCp, **memCpp; + char *c, *s, *d, **p; + enum { preName, inName, preValue, inValue } state; + + /* debug output */ + if ( handle->debug & 1 ) + printf( "macParseDefns( %s )\n", defns ); + + /* allocate temporary pointer arrays; in worst case they need to have + as many entries as the length of the defns string */ + ptr = ( char ** ) malloc( strlen( defns ) * sizeof( char * ) ); + end = ( char ** ) malloc( strlen( defns ) * sizeof( char * ) ); + del = ( long * ) malloc( strlen( defns ) * sizeof( long ) ); + if ( ptr == NULL || end == NULL || del == NULL ) goto error; + + /* go through definitions, noting pointers to starts and ends of macro + names and values; honor quotes and escapes; ignore white space + around assignment and separator characters */ + num = 0; + del[0] = FALSE; + quote = 0; + state = preName; + for ( c = defns; *c != '\0'; c++ ) { + + /* handle quotes */ + if ( quote ) + quote = ( *c == quote ) ? 0 : quote; + else if ( *c == '\'' || *c == '"' ) + quote = *c; + + /* handle escapes (pointer incremented below) */ + escape = ( *c == '\\' && *( c + 1 ) != '\0' ); + + switch ( state ) { + case preName: + if ( !quote && !escape && ( isspace( *c ) || *c == ',' ) ) break; + ptr[num] = c; + state = inName; + /* fall through (may be empty name) */ + + case inName: + if ( quote || escape || ( *c != '=' && *c != ',' ) ) break; + end[num] = c; + while ( end[num] > ptr[num] && isspace( *( end[num] - 1 ) ) ) + end[num]--; + num++; + del[num] = FALSE; + state = preValue; + if ( *c != ',' ) break; + del[num] = TRUE; + /* fall through (','; will delete) */ + + case preValue: + if ( !quote && !escape && isspace( *c ) ) break; + ptr[num] = c; + state = inValue; + /* fall through (may be empty value) */ + + case inValue: + if ( quote || escape || *c != ',' ) break; + end[num] = c; + while ( end[num] > ptr[num] && isspace( *( end[num] - 1 ) ) ) + end[num]--; + num++; + del[num] = FALSE; + state = preName; + break; + } + + /* if this was escape, increment pointer now (couldn't do + before because could have ignored escape at start of name + or value) */ + if ( escape ) c++; + } + + /* tidy up from state at end of string */ + switch ( state ) { + case preName: + break; + case inName: + end[num] = c; + while ( end[num] > ptr[num] && isspace( *( end[num] - 1 ) ) ) + end[num]--; + num++; + del[num] = TRUE; + case preValue: + ptr[num] = c; + case inValue: + end[num] = c; + while ( end[num] > ptr[num] && isspace( *( end[num] - 1 ) ) ) + end[num]--; + num++; + del[num] = FALSE; + } + + /* debug output */ + if ( handle != NULL && handle->debug & 4 ) + for ( i = 0; i < num; i += 2 ) + printf( "[%d] %.*s = [%d] %.*s (%s) (%s)\n", + end[i+0] - ptr[i+0], end[i+0] - ptr[i+0], ptr[i+0], + end[i+1] - ptr[i+1], end[i+1] - ptr[i+1], ptr[i+1], + del[i+0] ? "del" : "nodel", + del[i+1] ? "del" : "nodel" ); + + /* calculate how much memory to allocate: pointers followed by + strings */ + nbytes = ( num + 2 ) * sizeof( char * ); + for ( i = 0; i < num; i++ ) + nbytes += end[i] - ptr[i] + 1; + + /* allocate memory and set returned pairs pointer */ + memCp = malloc( nbytes ); + if ( memCp == NULL ) goto error; + memCpp = ( char ** ) memCp; + *pairs = memCpp; + + /* copy pointers and strings (memCpp accesses the pointer section + and memCp accesses the string section) */ + memCp += ( num + 2 ) * sizeof( char * ); + for ( i = 0; i < num; i++ ) { + + /* if no '=' followed the name, macro will be deleted */ + if ( del[i] ) + *memCpp++ = NULL; + else + *memCpp++ = memCp; + + /* copy value regardless of the above */ + strncpy( memCp, ptr[i], end[i] - ptr[i] ); + memCp += end[i] - ptr[i]; + *memCp++ = '\0'; + } + + /* add two NULL pointers */ + *memCpp++ = NULL; + *memCpp++ = NULL; + + /* remove quotes and escapes from names in place (unlike values, they + will not be re-parsed) */ + for ( p = *pairs; *p != NULL; p += 2 ) { + quote = 0; + for ( s = d = *p; *s != '\0'; s++ ) { + + /* quotes are not copied */ + if ( quote ) { + if ( *s == quote ) { + quote = 0; + continue; + } + } + else if ( *s == '\'' || *s == '"' ) { + quote = *s; + continue; + } + + /* escapes are not copied but next character is */ + if ( *s == '\\' && *( s + 1 ) != '\0' ) + s++; + + /* others are copied */ + *d++ = *s; + } + + /* need to terminate destination */ + *d++ = '\0'; + } + + /* free workspace */ + free( ptr ); + free( end ); + free( ( char * ) del ); + + /* debug output */ + if ( handle->debug & 1 ) + printf( "macParseDefns() -> %d\n", num / 2 ); + + /* success exit; return number of definitions */ + return num / 2; + + /* error exit */ +error: + macErrMessage0( -1, "macParseDefns: failed to allocate memory" ); + if ( ptr != NULL ) free( ptr ); + if ( end != NULL ) free( end ); + if ( del != NULL ) free( ( char * ) del ); + *pairs = NULL; + return -1; +} + +/* + * Install an array of name / value pairs as macro definitions. The + * array should have an even number of elements followed by at least + * one (preferably two) NULL pointers + */ +long /* #macros defined; <0 = ERROR */ +macInstallMacros( + MAC_HANDLE *handle, /* opaque handle */ + + char *pairs[] ) /* pointer to NULL-terminated array of */ + /* {name,value} pair strings; a NULL */ + /* value implies undefined; a NULL */ + /* argument implies no macros */ +{ + long n; + char **p; + + /* debug output */ + if ( handle->debug & 1 ) + printf( "macInstallMacros( %s, %s, ... )\n", + pairs && pairs[0] ? pairs[0] : "NULL", + pairs && pairs[1] ? pairs[1] : "NULL" ); + + /* go through array defining macros */ + for ( n = 0, p = pairs; p != NULL && p[0] != NULL; n++, p += 2 ) { + if ( macPutValue( handle, p[0], p[1] ) < 0 ) + return -1; + } + + /* debug output */ + if ( handle->debug & 1 ) + printf( "macInstallMacros() -> %d\n", n ); + + /* return number of macros defined */ + return n; +} + +/* $Log$ + * Revision 1.6 1996/06/26 09:43:19 wlupton + * first released version + * + */