added macLib
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
749
src/libCom/macCore.c
Normal file
749
src/libCom/macCore.c
Normal file
@@ -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 <NOTES run under vxWorks
|
||||
*
|
||||
* William Lupton, W. M. Keck Observatory
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#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 = "<string>";
|
||||
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 "<scope>" */
|
||||
entry = create( handle, "<scope>", 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, "<scope>", 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
|
||||
*
|
||||
*/
|
||||
198
src/libCom/macLib.h
Normal file
198
src/libCom/macLib.h
Normal file
@@ -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
|
||||
*
|
||||
*/
|
||||
749
src/libCom/macLib/macCore.c
Normal file
749
src/libCom/macLib/macCore.c
Normal file
@@ -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 <NOTES run under vxWorks
|
||||
*
|
||||
* William Lupton, W. M. Keck Observatory
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#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 = "<string>";
|
||||
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 "<scope>" */
|
||||
entry = create( handle, "<scope>", 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, "<scope>", 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
|
||||
*
|
||||
*/
|
||||
198
src/libCom/macLib/macLib.h
Normal file
198
src/libCom/macLib/macLib.h
Normal file
@@ -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
|
||||
*
|
||||
*/
|
||||
160
src/libCom/macLib/macLibNOTES
Normal file
160
src/libCom/macLib/macLibNOTES
Normal file
@@ -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?
|
||||
288
src/libCom/macLib/macLibREADME
Normal file
288
src/libCom/macLib/macLibREADME
Normal file
@@ -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, <Xxxx-specific> );
|
||||
long macPutXxxx( MAC_HANDLE *handle, <Xxxx-specific> );
|
||||
|
||||
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)
|
||||
<arbitrary text containing macro references>
|
||||
|
||||
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).
|
||||
275
src/libCom/macLib/macUtil.c
Normal file
275
src/libCom/macLib/macUtil.c
Normal file
@@ -0,0 +1,275 @@
|
||||
/* $Id$
|
||||
*
|
||||
* Implementation of utility macro substitution library (macLib)
|
||||
*
|
||||
* William Lupton, W. M. Keck Observatory
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#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
|
||||
*
|
||||
*/
|
||||
160
src/libCom/macLibNOTES
Normal file
160
src/libCom/macLibNOTES
Normal file
@@ -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?
|
||||
288
src/libCom/macLibREADME
Normal file
288
src/libCom/macLibREADME
Normal file
@@ -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, <Xxxx-specific> );
|
||||
long macPutXxxx( MAC_HANDLE *handle, <Xxxx-specific> );
|
||||
|
||||
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)
|
||||
<arbitrary text containing macro references>
|
||||
|
||||
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).
|
||||
275
src/libCom/macUtil.c
Normal file
275
src/libCom/macUtil.c
Normal file
@@ -0,0 +1,275 @@
|
||||
/* $Id$
|
||||
*
|
||||
* Implementation of utility macro substitution library (macLib)
|
||||
*
|
||||
* William Lupton, W. M. Keck Observatory
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#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
|
||||
*
|
||||
*/
|
||||
Reference in New Issue
Block a user