Files
epics-base/modules/libcom/src/macLib/macUtil.c
Andrew Johnson 3c99391d93 Added SPDX License ID to all EPICS-original source files
In some cases the license-identification header was missing,
so I added that as well. Replaced the remaining headers that
specifically identified "Versions 3.13.7 and higher".

Makefiles and the build system were deliberately excluded.
2020-08-03 11:53:01 -05:00

285 lines
9.0 KiB
C

/*************************************************************************\
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
* National Laboratory.
* Copyright (c) 2002 The Regents of the University of California, as
* Operator of Los Alamos National Laboratory.
* SPDX-License-Identifier: EPICS
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/*
* 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 "dbDefs.h"
#include "errlog.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)
*/
long /* #defns encountered; <0 = ERROR */
epicsStdCall macParseDefns(
MAC_HANDLE *handle, /* opaque handle; can be NULL if default */
/* special characters are to be used */
const 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 */
{
static const size_t altNumMax = 4;
size_t numMax;
int i;
int num;
int quote;
int escape;
size_t nbytes;
const char **ptr;
const char **end;
int *del;
char *memCp, **memCpp;
const char *c;
char *s, *d, **p;
enum { preName, inName, preValue, inValue } state;
/* debug output */
if ( handle && (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 */
numMax = strlen( defns );
if ( numMax < altNumMax )
numMax = altNumMax;
ptr = (const char **) calloc( numMax, sizeof( char * ) );
end = (const char **) calloc( numMax, sizeof( char * ) );
del = (int *) calloc( numMax, sizeof( int ) );
if ( ptr == NULL || end == NULL || del == NULL ) goto error;
/* go through definitions, noting pointers to starts and ends of macro
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 = (const char *) 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( (int) *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( (int) *( 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( (int) *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( (int) *( 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( (int) *( 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( (int) *( 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( "[%ld] %.*s = [%ld] %.*s (%s) (%s)\n",
(long) (end[i+0] - ptr[i+0]), (int) (end[i+0] - ptr[i+0]), ptr[i+0],
(long) (end[i+1] - ptr[i+1]), (int) (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, (const char *) 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( ( void * ) ptr );
free( ( void * ) end );
free( ( char * ) del );
/* debug output */
if ( handle != NULL && handle->debug & 1 )
printf( "macParseDefns() -> %d\n", num / 2 );
/* success exit; return number of definitions */
return num / 2;
/* error exit */
error:
errlogPrintf( "macParseDefns: failed to allocate memory\n" );
if ( ptr != NULL ) free( ( void * ) ptr );
if ( end != NULL ) free( ( void * ) 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 */
epicsStdCall 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 */
{
int 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;
}