merged msi-join branch

This commit is contained in:
Andrew Johnson
2012-06-22 17:53:33 -05:00
32 changed files with 1995 additions and 401 deletions
+1 -1
View File
@@ -109,7 +109,7 @@ DBST = dbst
endif
ifndef MSI
MSI = msi
MSI = $(EPICS_BASE_HOST_BIN)/msi
endif
+1
View File
@@ -21,6 +21,7 @@ vpath %.cc $(USR_VPATH) $(ALL_SRC_DIRS)
vpath %.cpp $(USR_VPATH) $(ALL_SRC_DIRS)
vpath %.rc $(USR_VPATH) $(ALL_SRC_DIRS)
vpath %.h $(USR_VPATH) $(ALL_SRC_DIRS)
vpath %.html $(USR_VPATH) $(ALL_SRC_DIRS)
vpath %.skel.static $(USR_VPATH) $(ALL_SRC_DIRS)
vpath %.y $(USR_VPATH) $(ALL_SRC_DIRS)
vpath %.l $(USR_VPATH) $(ALL_SRC_DIRS)
+3
View File
@@ -64,6 +64,9 @@ ioc_DEPEND_DIRS = libCom ca/client
DIRS += ioc/db/test
ioc/db/test_DEPEND_DIRS = ioc libCom/RTEMS
DIRS += ioc/dbtemplate/test
ioc/dbtemplate/test_DEPEND_DIRS = ioc
# PDB Standard Record Definitions
DIRS += std
+7 -2
View File
@@ -11,12 +11,17 @@
SRC_DIRS += $(IOCDIR)/dbtemplate
PROD_HOST += msi
msi_SRCS = msi.c
msi_LIBS += Com
HTMLS += msi.html
INC += dbLoadTemplate.h
INC += dbtoolsIocRegister.h
dbCore_SRCS += dbLoadTemplate.c
dbCore_SRCS += dbtoolsIocRegister.c
HTMLS += dbtemplate/dbLoadTemplate.html
CLEANS += dbLoadTemplate_lex.c dbLoadTemplate.c
+3 -7
View File
@@ -3,8 +3,7 @@
* National Laboratory.
* Copyright (c) 2002 The Regents of the University of California, as
* Operator of Los Alamos National Laboratory.
* EPICS BASE Versions 3.13.7
* and higher are distributed subject to a Software License Agreement found
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/* dbLoadTemplate.h */
@@ -13,10 +12,7 @@
#define INCdbLoadTemplateh
#include "shareLib.h"
epicsShareFunc int epicsShareAPI dbLoadTemplate(char* sub_file);
epicsShareFunc int epicsShareAPI dbLoadTemplate(
const char *sub_file, const char *cmd_collect);
#endif /*INCdbLoadTemplateh*/
-137
View File
@@ -1,137 +0,0 @@
/*************************************************************************\
* 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.
* EPICS BASE Versions 3.13.7
* and higher are distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
<HTML>
<BODY>
<PRE>
<!-- Manpage converted by man2html 3.0.1 -->
</PRE>
<H2>NAME</H2><PRE>
dbLoadRecords, dbLoadTemplate - load ascii database records
into an IOC
</PRE>
<H2>SYNOPSIS</H2><PRE>
dbLoadRecords(char* db_file, char* substitutions)
dbLoadTemplate(char* template_file)
</PRE>
<H2>DESCRIPTION</H2><PRE>
These routines are available from IOC core on the vxWorks
command line. Both provide a way to load ascii ".db" files
(usually created by <B>gdct(1)</B> ) into the IOC. The ".db" files
contain ascii versions of record instances and are described
in more detail in <B>dbfile(5)</B>. In addition to loading the
".db" ascii files into the IOC, both routines provide a
method of performing variable substitution on record names
and field values.
dbLoadRecords() reads the ".db" file <I>db</I>_<I>file</I> performing sub-
stitutions specified in string <I>substitutions</I>. The substitu-
tion must be a string specified as follows:
"var1=sub1,var2=sub3,..."
Variables are specified in the ".db" file as
$(variable_name). If the substitution string
"a=1,b=2,c=\"this is a test\"" were used, any variables
$(a), $(b), or $(c) would be substituted with the appropri-
ate data. See the EXAMPLES section for more details.
dbLoadTemplate() will read a <I>template</I>_<I>file</I>. The
<I>template</I>_<I>file</I> resides in the your IOC boot directory and
contains rules about loading ".db" files and performing sub-
stitutions. The template_file must be in the form used by
an IOC and is described in <B>templatefile(5)</B>. The EXAMPLES
section descibes how it can be used.
</PRE>
<H2>EXAMPLES</H2><PRE>
The next two examples of dbLoadRecords() and dbLoadTem-
plate() will use the following ".db" file named <I>test</I>.<I>db</I> :
database(test)
{
record(ai,"$(pre)testrec1")
record(ai,"$(pre)testrec2")
record(stringout,"$(pre)testrec3")
{
field(VAL,"$(STRING)")
field(SCAN,"$(SCAN)")
}
}
Running dbLoadRecords ("test.db","pre=TEST,STRING=\"this is
a test\",SCAN=Passive") will produce the following records
in the IOC's database:
TESTtestrec1
TESTtestrec2
TESTtestrec3
The third record will have VAL set to "this is a test" and
SCAN set to "Passive".
Running dbLoadTemplate ("test.template") with test.template
containing:
file test.db
{
{pre=TEST1, STRING = "this is a test two", SCAN="1 Second" }
{pre=TEST2, STRING = "this is a test one", SCAN=Passive }
{pre=TEST3, STRING = "this is a test three", SCAN=Passive }
}
will produce a total of nine records in the IOC's database:
TEST1testrec1
TEST1testrec2
TEST1testrec3 - (VAL="this is a test two", SCAN="1 Second")
TEST2testrec1
TEST2testrec2
TEST2testrec3 - (VAL="this is a test one", SCAN="Passive")
TEST3testrec1
TEST3testrec2
TEST3testrec3 - (VAL="this is a test three", SCAN="Passive")
</PRE>
<H2>NOTES</H2><PRE>
The binary file <I>default</I>.<I>dctsdr</I> must be loaded prior to run-
ning either of these routines. This file contains the rules
on how to construct records and change field values.
After the default.dctsdr file is loaded, these routines can
be run as many times as desired until iocInit is run.
</PRE>
<H2>SEE ALSO</H2><PRE>
<B>gdct(1)</B>, <B>templatefile(5)</B>, <B>dbfile(5)</B>
</PRE>
<HR>
<ADDRESS>
Man(1) output converted with
<a href="http://www.oac.uci.edu/indiv/ehood/man2html.html">man2html</a>
</ADDRESS>
</BODY>
</HTML>
+282 -226
View File
@@ -22,30 +22,32 @@
#include "dbLoadTemplate.h"
static int line_num;
static int yyerror();
static int yyerror(char* str);
#define VAR_MAX_VAR_STRING 5000
#define VAR_MAX_VARS 100
static char *sub_collect = NULL;
static char *sub_collect;
static char *sub_locals;
static char** vars = NULL;
static char* db_file_name = NULL;
static int var_count,sub_count;
static int var_count, sub_count;
%}
%start template
%start substitution_file
%token <Str> WORD QUOTE
%token DBFILE
%token PATTERN
%token GLOBAL
%token EQUALS COMMA
%left O_PAREN C_PAREN
%left O_BRACE C_BRACE
%union
{
int Int;
int Int;
char Char;
char *Str;
double Real;
@@ -53,192 +55,242 @@ static int var_count,sub_count;
%%
template: templs
| subst
;
substitution_file: global_or_template
| substitution_file global_or_template
;
templs: templs templ
| templ
;
global_or_template: global_definitions
| template_substitutions
;
templ: templ_head O_BRACE subst C_BRACE
| templ_head
{
if(db_file_name)
dbLoadRecords(db_file_name,NULL);
else
fprintf(stderr,"Error: no db file name given\n");
}
;
global_definitions: GLOBAL O_BRACE C_BRACE
| GLOBAL O_BRACE variable_definitions C_BRACE
{
#ifdef ERROR_STUFF
fprintf(stderr, "global_definitions: %s\n", sub_collect+1);
#endif
sub_locals += strlen(sub_locals);
}
;
templ_head: DBFILE WORD
{
var_count=0;
if(db_file_name) dbmfFree(db_file_name);
db_file_name = dbmfMalloc(strlen($2)+1);
strcpy(db_file_name,$2);
dbmfFree($2);
}
| DBFILE QUOTE
{
var_count=0;
if(db_file_name) dbmfFree(db_file_name);
db_file_name = dbmfMalloc(strlen($2)+1);
strcpy(db_file_name,$2);
dbmfFree($2);
}
;
template_substitutions: template_filename O_BRACE C_BRACE
{
#ifdef ERROR_STUFF
fprintf(stderr, "template_substitutions: %s unused\n", db_file_name);
#endif
dbmfFree(db_file_name);
db_file_name = NULL;
}
| template_filename O_BRACE substitutions C_BRACE
{
#ifdef ERROR_STUFF
fprintf(stderr, "template_substitutions: %s finished\n", db_file_name);
#endif
dbmfFree(db_file_name);
db_file_name = NULL;
}
;
subst: PATTERN pattern subs
| PATTERN pattern
| var_subs
;
template_filename: DBFILE WORD
{
#ifdef ERROR_STUFF
fprintf(stderr, "template_filename: %s\n", $2);
#endif
var_count = 0;
db_file_name = dbmfMalloc(strlen($2)+1);
strcpy(db_file_name, $2);
dbmfFree($2);
}
| DBFILE QUOTE
{
#ifdef ERROR_STUFF
fprintf(stderr, "template_filename: \"%s\"\n", $2);
#endif
var_count = 0;
db_file_name = dbmfMalloc(strlen($2)+1);
strcpy(db_file_name, $2);
dbmfFree($2);
}
;
pattern: O_BRACE vars C_BRACE
{
#ifdef ERROR_STUFF
int i;
for(i=0;i<var_count;i++) fprintf(stderr,"variable=(%s)\n",vars[i]);
fprintf(stderr,"var_count=%d\n",var_count);
#endif
}
;
substitutions: pattern_substitutions
| variable_substitutions
;
vars: vars var
| vars COMMA var
| var
;
pattern_substitutions: PATTERN O_BRACE C_BRACE
| PATTERN O_BRACE C_BRACE pattern_definitions
| PATTERN O_BRACE pattern_names C_BRACE
| PATTERN O_BRACE pattern_names C_BRACE pattern_definitions
;
var: WORD
{
vars[var_count] = dbmfMalloc(strlen($1)+1);
strcpy(vars[var_count],$1);
var_count++;
dbmfFree($1);
}
;
pattern_names: pattern_name
| pattern_names COMMA
| pattern_names pattern_name
;
subs: subs sub
| sub
;
pattern_name: WORD
{
#ifdef ERROR_STUFF
fprintf(stderr, "pattern_name: [%d] = %s\n", var_count, $1);
#endif
vars[var_count] = dbmfMalloc(strlen($1)+1);
strcpy(vars[var_count], $1);
var_count++;
dbmfFree($1);
}
;
sub: WORD O_BRACE vals C_BRACE
{
sub_collect[strlen(sub_collect)-1]='\0';
#ifdef ERROR_STUFF
fprintf(stderr,"dbLoadRecords(%s)\n",sub_collect);
#endif
if(db_file_name)
dbLoadRecords(db_file_name,sub_collect);
else
fprintf(stderr,"Error: no db file name given\n");
dbmfFree($1);
sub_collect[0]='\0';
sub_count=0;
}
| O_BRACE vals C_BRACE
{
sub_collect[strlen(sub_collect)-1]='\0';
#ifdef ERROR_STUFF
fprintf(stderr,"dbLoadRecords(%s)\n",sub_collect);
#endif
if(db_file_name)
dbLoadRecords(db_file_name,sub_collect);
else
fprintf(stderr,"Error: no db file name given\n");
sub_collect[0]='\0';
sub_count=0;
}
;
pattern_definitions: pattern_definition
| pattern_definitions pattern_definition
;
vals: vals val
| vals COMMA val
| val
;
pattern_definition: global_definitions
| O_BRACE C_BRACE
{
#ifdef ERROR_STUFF
fprintf(stderr, "pattern_definition: pattern_values empty\n");
fprintf(stderr, " dbLoadRecords(%s)\n", sub_collect+1);
#endif
dbLoadRecords(db_file_name, sub_collect+1);
}
| O_BRACE pattern_values C_BRACE
{
#ifdef ERROR_STUFF
fprintf(stderr, "pattern_definition:\n");
fprintf(stderr, " dbLoadRecords(%s)\n", sub_collect+1);
#endif
dbLoadRecords(db_file_name, sub_collect+1);
*sub_locals = '\0';
sub_count = 0;
}
| WORD O_BRACE pattern_values C_BRACE
{ /* DEPRECATED SYNTAX */
fprintf(stderr,
"dbLoadTemplate: Substitution file uses deprecated syntax.\n"
" the string '%s' on line %d that comes just before the\n"
" '{' character is extraneous and should be removed.\n",
$1, line_num);
#ifdef ERROR_STUFF
fprintf(stderr, "pattern_definition:\n");
fprintf(stderr, " dbLoadRecords(%s)\n", sub_collect+1);
#endif
dbLoadRecords(db_file_name, sub_collect+1);
dbmfFree($1);
*sub_locals = '\0';
sub_count = 0;
}
;
val: QUOTE
{
if(sub_count<=var_count)
{
strcat(sub_collect,vars[sub_count]);
strcat(sub_collect,"=\"");
strcat(sub_collect,$1);
strcat(sub_collect,"\",");
sub_count++;
}
dbmfFree($1);
}
| WORD
{
if(sub_count<=var_count)
{
strcat(sub_collect,vars[sub_count]);
strcat(sub_collect,"=");
strcat(sub_collect,$1);
strcat(sub_collect,",");
sub_count++;
}
dbmfFree($1);
}
;
pattern_values: pattern_value
| pattern_values COMMA
| pattern_values pattern_value
;
var_subs: var_subs var_sub
| var_sub
;
pattern_value: QUOTE
{
#ifdef ERROR_STUFF
fprintf(stderr, "pattern_value: [%d] = \"%s\"\n", sub_count, $1);
#endif
if (sub_count < var_count) {
strcat(sub_locals, ",");
strcat(sub_locals, vars[sub_count]);
strcat(sub_locals, "=\"");
strcat(sub_locals, $1);
strcat(sub_locals, "\"");
sub_count++;
} else {
fprintf(stderr, "dbLoadTemplate: Too many values given, line %d.\n",
line_num);
}
dbmfFree($1);
}
| WORD
{
#ifdef ERROR_STUFF
fprintf(stderr, "pattern_value: [%d] = %s\n", sub_count, $1);
#endif
if (sub_count < var_count) {
strcat(sub_locals, ",");
strcat(sub_locals, vars[sub_count]);
strcat(sub_locals, "=");
strcat(sub_locals, $1);
sub_count++;
} else {
fprintf(stderr, "dbLoadTemplate: Too many values given, line %d.\n",
line_num);
}
dbmfFree($1);
}
;
var_sub: WORD O_BRACE sub_pats C_BRACE
{
sub_collect[strlen(sub_collect)-1]='\0';
#ifdef ERROR_STUFF
fprintf(stderr,"dbLoadRecords(%s)\n",sub_collect);
#endif
if(db_file_name)
dbLoadRecords(db_file_name,sub_collect);
else
fprintf(stderr,"Error: no db file name given\n");
dbmfFree($1);
sub_collect[0]='\0';
sub_count=0;
}
| O_BRACE sub_pats C_BRACE
{
sub_collect[strlen(sub_collect)-1]='\0';
#ifdef ERROR_STUFF
fprintf(stderr,"dbLoadRecords(%s)\n",sub_collect);
#endif
if(db_file_name)
dbLoadRecords(db_file_name,sub_collect);
else
fprintf(stderr,"Error: no db file name given\n");
sub_collect[0]='\0';
sub_count=0;
}
;
variable_substitutions: variable_substitution
| variable_substitutions variable_substitution
;
sub_pats: sub_pats sub_pat
| sub_pats COMMA sub_pat
| sub_pat
;
variable_substitution: global_definitions
| O_BRACE C_BRACE
{
#ifdef ERROR_STUFF
fprintf(stderr, "variable_substitution: variable_definitions empty\n");
fprintf(stderr, " dbLoadRecords(%s)\n", sub_collect+1);
#endif
dbLoadRecords(db_file_name, sub_collect+1);
}
| O_BRACE variable_definitions C_BRACE
{
#ifdef ERROR_STUFF
fprintf(stderr, "variable_substitution:\n");
fprintf(stderr, " dbLoadRecords(%s)\n", sub_collect+1);
#endif
dbLoadRecords(db_file_name, sub_collect+1);
*sub_locals = '\0';
}
| WORD O_BRACE variable_definitions C_BRACE
{ /* DEPRECATED SYNTAX */
fprintf(stderr,
"dbLoadTemplate: Substitution file uses deprecated syntax.\n"
" the string '%s' on line %d that comes just before the\n"
" '{' character is extraneous and should be removed.\n",
$1, line_num);
#ifdef ERROR_STUFF
fprintf(stderr, "variable_substitution:\n");
fprintf(stderr, " dbLoadRecords(%s)\n", sub_collect+1);
#endif
dbLoadRecords(db_file_name, sub_collect+1);
dbmfFree($1);
*sub_locals = '\0';
}
;
sub_pat: WORD EQUALS WORD
{
strcat(sub_collect,$1);
strcat(sub_collect,"=");
strcat(sub_collect,$3);
strcat(sub_collect,",");
dbmfFree($1); dbmfFree($3);
sub_count++;
}
| WORD EQUALS QUOTE
{
strcat(sub_collect,$1);
strcat(sub_collect,"=\"");
strcat(sub_collect,$3);
strcat(sub_collect,"\",");
dbmfFree($1); dbmfFree($3);
sub_count++;
}
;
variable_definitions: variable_definition
| variable_definitions COMMA
| variable_definitions variable_definition
;
variable_definition: WORD EQUALS WORD
{
#ifdef ERROR_STUFF
fprintf(stderr, "variable_definition: %s = %s\n", $1, $3);
#endif
strcat(sub_locals, ",");
strcat(sub_locals, $1);
strcat(sub_locals, "=");
strcat(sub_locals, $3);
dbmfFree($1); dbmfFree($3);
}
| WORD EQUALS QUOTE
{
#ifdef ERROR_STUFF
fprintf(stderr, "variable_definition: %s = \"%s\"\n", $1, $3);
#endif
strcat(sub_locals, ",");
strcat(sub_locals, $1);
strcat(sub_locals, "=\"");
strcat(sub_locals, $3);
strcat(sub_locals, "\"");
dbmfFree($1); dbmfFree($3);
}
;
%%
@@ -247,69 +299,73 @@ sub_pat: WORD EQUALS WORD
static int yyerror(char* str)
{
if (str)
fprintf(stderr, "Substitution file error: %s\n", str);
fprintf(stderr, "Substitution file error: %s\n", str);
else
fprintf(stderr, "Substitution file error.\n");
fprintf(stderr, "Substitution file error.\n");
fprintf(stderr, "line %d: '%s'\n", line_num, yytext);
return 0;
}
static int is_not_inited = 1;
int epicsShareAPI dbLoadTemplate(char* sub_file)
int epicsShareAPI dbLoadTemplate(const char *sub_file, const char *cmd_collect)
{
FILE *fp;
int ind;
FILE *fp;
int i;
line_num=1;
line_num = 1;
if( !sub_file || !*sub_file)
{
fprintf(stderr,"must specify variable substitution file\n");
return -1;
}
if (!sub_file || !*sub_file) {
fprintf(stderr, "must specify variable substitution file\n");
return -1;
}
if( !(fp=fopen(sub_file,"r")) )
{
fprintf(stderr,"dbLoadTemplate: error opening sub file %s\n",sub_file);
return -1;
}
fp = fopen(sub_file, "r");
if (!fp) {
fprintf(stderr, "dbLoadTemplate: error opening sub file %s\n", sub_file);
return -1;
}
vars = (char**)malloc(VAR_MAX_VARS * sizeof(char*));
sub_collect = malloc(VAR_MAX_VAR_STRING);
if (!vars || !sub_collect)
{
free(vars);
free(sub_collect);
fclose(fp);
fprintf(stderr, "dbLoadTemplate: Out of memory!\n");
return -1;
}
vars = (char**)malloc(VAR_MAX_VARS * sizeof(char*));
sub_collect = malloc(VAR_MAX_VAR_STRING);
if (!vars || !sub_collect) {
free(vars);
free(sub_collect);
fclose(fp);
fprintf(stderr, "dbLoadTemplate: Out of memory!\n");
return -1;
}
strcpy(sub_collect, ",");
sub_collect[0]='\0';
var_count=0;
sub_count=0;
if (cmd_collect && *cmd_collect) {
strcat(sub_collect, cmd_collect);
sub_locals = sub_collect + strlen(sub_collect);
} else {
sub_locals = sub_collect;
*sub_locals = '\0';
}
var_count = 0;
sub_count = 0;
if(is_not_inited)
{
yyin=fp;
is_not_inited=0;
}
else
{
yyrestart(fp);
}
if (is_not_inited) {
yyin = fp;
is_not_inited = 0;
} else {
yyrestart(fp);
}
yyparse();
for(ind=0;ind<var_count;ind++) dbmfFree(vars[ind]);
free(vars);
free(sub_collect);
vars = NULL;
fclose(fp);
if(db_file_name){
dbmfFree((void *)db_file_name);
db_file_name = NULL;
}
return 0;
yyparse();
for (i = 0; i < var_count; i++) {
dbmfFree(vars[i]);
}
free(vars);
free(sub_collect);
vars = NULL;
fclose(fp);
if (db_file_name) {
dbmfFree(db_file_name);
db_file_name = NULL;
}
return 0;
}
+24 -23
View File
@@ -18,39 +18,40 @@ bareword [a-zA-Z0-9_\-+:./\\\[\]<>;]
%%
"pattern" { return(PATTERN); }
"file" { return(DBFILE); }
"pattern" { return(PATTERN); }
"file" { return(DBFILE); }
"global" { return(GLOBAL); }
{doublequote}({dstringchar}|{escape})*{doublequote} |
{singlequote}({sstringchar}|{escape})*{singlequote} {
yylval.Str = dbmfStrdup(yytext+1);
yylval.Str[strlen(yylval.Str)-1] = '\0';
return(QUOTE);
}
yylval.Str = dbmfStrdup(yytext+1);
yylval.Str[strlen(yylval.Str)-1] = '\0';
return(QUOTE);
}
{bareword}+ {
yylval.Str = dbmfStrdup(yytext);
return(WORD);
}
yylval.Str = dbmfStrdup(yytext);
return(WORD);
}
"=" { return(EQUALS); }
"," { return(COMMA); }
"{" { return(O_BRACE); }
"}" { return(C_BRACE); }
"=" { return(EQUALS); }
"," { return(COMMA); }
"{" { return(O_BRACE); }
"}" { return(C_BRACE); }
{comment}.* ;
{whitespace} ;
{newline} { line_num++; }
{comment}.* ;
{whitespace} ;
{newline} { line_num++; }
. {
char message[40];
char message[40];
sprintf(message,"invalid character '%c'", yytext[0]);
yyerror(message);
sprintf(message, "invalid character '%c'", yytext[0]);
yyerror(message);
/* Suppress compiler warning messages */
if (0) yyunput('c',NULL);
if (0) yy_switch_to_buffer(NULL);
}
/* Suppress compiler warning messages */
if (0) yyunput('c',NULL);
if (0) yy_switch_to_buffer(NULL);
}
%%
+8 -5
View File
@@ -13,17 +13,20 @@
/* dbLoadTemplate */
static const iocshArg dbLoadTemplateArg0 = { "file name",iocshArgString};
static const iocshArg * const dbLoadTemplateArgs[1] = {&dbLoadTemplateArg0};
static const iocshArg dbLoadTemplateArg0 = {"filename", iocshArgString};
static const iocshArg dbLoadTemplateArg1 = {"var=value", iocshArgString};
static const iocshArg * const dbLoadTemplateArgs[2] = {
&dbLoadTemplateArg0, &dbLoadTemplateArg1
};
static const iocshFuncDef dbLoadTemplateFuncDef =
{"dbLoadTemplate",1,dbLoadTemplateArgs};
{"dbLoadTemplate", 2, dbLoadTemplateArgs};
static void dbLoadTemplateCallFunc(const iocshArgBuf *args)
{
dbLoadTemplate(args[0].sval);
dbLoadTemplate(args[0].sval, args[1].sval);
}
void epicsShareAPI dbtoolsIocRegister(void)
{
iocshRegister(&dbLoadTemplateFuncDef,dbLoadTemplateCallFunc);
iocshRegister(&dbLoadTemplateFuncDef, dbLoadTemplateCallFunc);
}
+841
View File
@@ -0,0 +1,841 @@
/*************************************************************************\
* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne
* National Laboratory.
* Copyright (c) 2002 The Regents of the University of California, as
* Operator of Los Alamos National Laboratory.
* EPICS Base is distributed subject to a Software License Agreement found
* in the file LICENSE that is included with this distribution.
\*************************************************************************/
/* msi - macro substitutions and include */
#include <stdlib.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <dbDefs.h>
#include <macLib.h>
#include <ellLib.h>
#include <epicsString.h>
#include <osiFileName.h>
#define MAX_BUFFER_SIZE 4096
/* Module to read the template files */
typedef struct inputData inputData;
static void inputConstruct(inputData **ppvt);
static void inputDestruct(inputData *pvt);
static void inputAddPath(inputData *pvt, char *pval);
static void inputBegin(inputData *pvt, char *fileName);
static char *inputNextLine(inputData *pvt);
static void inputNewIncludeFile(inputData *pvt, char *name);
static void inputErrPrint(inputData *pvt);
/* Module to read the substitution file */
typedef struct subInfo subInfo;
static void substituteOpen(subInfo **ppvt, char *substitutionName);
static void substituteDestruct(subInfo *pvt);
static int substituteGetNextSet(subInfo *pvt, char **filename);
static int substituteGetGlobalSet(subInfo *pvt);
static char *substituteGetReplacements(subInfo *pvt);
static char *substituteGetGlobalReplacements(subInfo *pvt);
/* Forward references to local routines */
static void usageExit(void);
static void addMacroReplacements(MAC_HANDLE *macPvt, char *pval);
static void makeSubstitutions(inputData *inputPvt, MAC_HANDLE *macPvt, char *templateName);
/*Global variables */
static int dontWarnUndef = 1;
int main(int argc,char **argv)
{
inputData *inputPvt;
MAC_HANDLE *macPvt;
char *pval;
int narg;
char *substitutionName=0;
char *templateName=0;
int i;
int localScope = 1;
inputConstruct(&inputPvt);
macCreateHandle(&macPvt,0);
macSuppressWarning(macPvt,1);
while((argc>1) && (argv[1][0] == '-')) {
narg = (strlen(argv[1])==2) ? 2 : 1;
pval = (narg==1) ? (argv[1]+2) : argv[2];
if(strncmp(argv[1],"-I",2)==0) {
inputAddPath(inputPvt,pval);
} else if(strncmp(argv[1],"-o",2)==0) {
if(freopen(pval,"w",stdout)==NULL) {
fprintf(stderr,"msi: Can't open %s for writing: %s\n",
pval, strerror(errno));
exit(1);
}
} else if(strncmp(argv[1],"-M",2)==0) {
addMacroReplacements(macPvt,pval);
} else if(strncmp(argv[1],"-S",2)==0) {
substitutionName = epicsStrDup(pval);
} else if(strncmp(argv[1],"-V",2)==0) {
macSuppressWarning(macPvt,0);
dontWarnUndef = 0;
narg = 1; /* no argument for this option */
} else if(strncmp(argv[1],"-g",2)==0) {
localScope = 0;
narg = 1; /* no argument for this option */
} else {
usageExit();
}
argc -= narg;
for(i=1; i<argc; i++) argv[i] = argv[i + narg];
}
if(argc>2) {
fprintf(stderr,"msi: Too many arguments\n");
usageExit();
}
if(argc==2) {
templateName = epicsStrDup(argv[1]);
}
if(!substitutionName) {
makeSubstitutions(inputPvt,macPvt,templateName);
} else {
subInfo *substitutePvt;
char *filename = 0;
int isGlobal, isFile;
substituteOpen(&substitutePvt,substitutionName);
do {
if ((isGlobal = substituteGetGlobalSet(substitutePvt))) {
pval = substituteGetGlobalReplacements(substitutePvt);
if(pval) {
addMacroReplacements(macPvt,pval);
}
} else if ((isFile = substituteGetNextSet(substitutePvt,&filename))) {
if(templateName) filename = templateName;
if(!filename) {
fprintf(stderr,"msi: No template file\n");
usageExit();
}
while((pval = substituteGetReplacements(substitutePvt))){
if (localScope) macPushScope(macPvt);
addMacroReplacements(macPvt,pval);
makeSubstitutions(inputPvt,macPvt,filename);
if (localScope) macPopScope(macPvt);
}
}
} while (isGlobal || isFile);
substituteDestruct(substitutePvt);
}
macDeleteHandle(macPvt);
inputDestruct(inputPvt);
free(templateName);
free(substitutionName);
return 0;
}
void usageExit(void)
{
fprintf(stderr,"usage: msi [options] [template]\n");
fprintf(stderr,"stdin is used if neither template nor substitution file is given\n");
fprintf(stderr,"options:\n");
fprintf(stderr," -V Verbose warnings\n");
fprintf(stderr," -g All macros have global scope\n");
fprintf(stderr," -o<FILE> Save output to <FILE>\n");
fprintf(stderr," -I<DIR> Add <DIR> to include file search path\n");
fprintf(stderr," -M<SUBST> Add <SUBST> to (global) macro definitions\n");
fprintf(stderr," (<SUBST> takes the form VAR=VALUE,...)\n");
fprintf(stderr," -S<FILE> Expand the substitutions in FILE\n");
exit(1);
}
static void addMacroReplacements(MAC_HANDLE *macPvt,char *pval)
{
char **pairs;
long status;
status = macParseDefns(macPvt,pval,&pairs);
if(status==-1) {
fprintf(stderr,"msi: Error from macParseDefns\n");
usageExit();
}
if(status) {
status = macInstallMacros(macPvt,pairs);
if(!status) {
fprintf(stderr,"Error from macInstallMacros\n");
usageExit();
}
free(pairs);
}
}
typedef enum {cmdInclude,cmdSubstitute} cmdType;
static const char *cmdNames[] = {"include","substitute"};
static void makeSubstitutions(inputData *inputPvt, MAC_HANDLE *macPvt, char *templateName)
{
char *input;
static char buffer[MAX_BUFFER_SIZE];
int n;
inputBegin(inputPvt,templateName);
while((input = inputNextLine(inputPvt))) {
int expand=1;
char *p;
char *command = 0;
p = input;
/*skip whitespace at beginning of line*/
while(*p && (isspace(*p))) ++p;
/*Look for i or s */
if(*p && (*p=='i' || *p=='s')) command = p;
if(command) {
char *pstart;
char *pend;
char *copy;
int cmdind=-1;
int i;
for(i=0; i< NELEMENTS(cmdNames); i++) {
if(strstr(command,cmdNames[i])) {
cmdind = i;
}
}
if(cmdind<0) goto endif;
p = command + strlen(cmdNames[cmdind]);
/*skip whitespace after command*/
while(*p && (isspace(*p))) ++p;
/*Next character must be quote*/
if((*p==0) || (*p!='"')) goto endif;
pstart = ++p;
/*Look for end quote*/
while(*p && (*p!='"')) {
/*allow escape for imbeded quote*/
if((*p=='\\') && *(p+1)=='"') {
p += 2; continue;
} else {
if(*p=='"') break;
}
++p;
}
pend = p;
if(*p==0) goto endif;
/*skip quote and any trailing blanks*/
while(*++p==' ') ;
if(*p != '\n' && *p !=0) goto endif;
copy = calloc(pend-pstart+1,sizeof(char));
strncpy(copy,pstart,pend-pstart);
switch(cmdind) {
case cmdInclude:
inputNewIncludeFile(inputPvt,copy);
break;
case cmdSubstitute:
addMacroReplacements(macPvt,copy);
break;
default:
fprintf(stderr,"msi: Logic error in makeSubstitutions\n");
inputErrPrint(inputPvt);
exit(1);
}
free(copy);
expand = 0;
}
endif:
if (expand) {
n = macExpandString(macPvt,input,buffer,MAX_BUFFER_SIZE-1);
fputs(buffer,stdout);
if (!dontWarnUndef && n<0) {
fprintf(stderr,"msi: Warning, undefined macros present\n");
dontWarnUndef++;
}
}
}
}
typedef struct inputFile{
ELLNODE node;
char *filename;
FILE *fp;
int lineNum;
}inputFile;
typedef struct pathNode {
ELLNODE node;
char *directory;
} pathNode;
struct inputData {
ELLLIST inputFileList;
ELLLIST pathList;
char inputBuffer[MAX_BUFFER_SIZE];
};
static void inputOpenFile(inputData *pinputData,char *filename);
static void inputCloseFile(inputData *pinputData);
static void inputCloseAllFiles(inputData *pinputData);
static void inputConstruct(inputData **ppvt)
{
inputData *pinputData;
pinputData = calloc(1,sizeof(inputData));
ellInit(&pinputData->inputFileList);
ellInit(&pinputData->pathList);
*ppvt = pinputData;
}
static void inputDestruct(inputData *pinputData)
{
pathNode *ppathNode;
inputCloseAllFiles(pinputData);
while((ppathNode = (pathNode *)ellFirst(&pinputData->pathList))) {
ellDelete(&pinputData->pathList,&ppathNode->node);
free(ppathNode->directory);
free(ppathNode);
}
free(pinputData);
}
static void inputAddPath(inputData *pinputData, char *path)
{
ELLLIST *ppathList = &pinputData->pathList;
pathNode *ppathNode;
const char *pcolon;
const char *pdir;
int len;
int emptyName;
const char sep = *OSI_PATH_LIST_SEPARATOR;
pdir = path;
/*an empty name at beginning, middle, or end means current directory*/
while(pdir && *pdir) {
emptyName = ((*pdir == sep) ? 1 : 0);
if(emptyName) ++pdir;
ppathNode = (pathNode *)calloc(1,sizeof(pathNode));
ellAdd(ppathList,&ppathNode->node);
if(!emptyName) {
pcolon = strchr(pdir,sep);
len = (pcolon ? (pcolon - pdir) : strlen(pdir));
if(len>0) {
ppathNode->directory = (char *)calloc(len+1,sizeof(char));
strncpy(ppathNode->directory,pdir,len);
pdir = pcolon;
/*unless at end skip past first colon*/
if(pdir && *(pdir+1)!=0) ++pdir;
} else { /*must have been trailing : */
emptyName=1;
}
}
if(emptyName) {
ppathNode->directory = (char *)calloc(2,sizeof(char));
strcpy(ppathNode->directory,".");
}
}
return;
}
static void inputBegin(inputData *pinputData, char *fileName)
{
inputCloseAllFiles(pinputData);
inputOpenFile(pinputData,fileName);
}
static char *inputNextLine(inputData *pinputData)
{
inputFile *pinputFile;
char *pline;
while((pinputFile = (inputFile *)ellFirst(&pinputData->inputFileList))) {
pline = fgets(pinputData->inputBuffer,MAX_BUFFER_SIZE,pinputFile->fp);
if(pline) {
++pinputFile->lineNum;
return(pline);
}
inputCloseFile(pinputData);
}
return(0);
}
static void inputNewIncludeFile(inputData *pinputData, char *name)
{
inputOpenFile(pinputData,name);
}
static void inputErrPrint(inputData *pinputData)
{
inputFile *pinputFile;
fprintf(stderr,"input: '%s' at ",pinputData->inputBuffer);
pinputFile = (inputFile *)ellFirst(&pinputData->inputFileList);
while(pinputFile) {
fprintf(stderr,"line %d of ",pinputFile->lineNum);
if(pinputFile->filename) {
fprintf(stderr," file %s\n",pinputFile->filename);
} else {
fprintf(stderr,"stdin:\n");
}
pinputFile = (inputFile *)ellNext(&pinputFile->node);
if(pinputFile) {
fprintf(stderr," included from ");
} else {
fprintf(stderr,"\n");
}
}
fprintf(stderr,"\n");
}
static void inputOpenFile(inputData *pinputData,char *filename)
{
ELLLIST *ppathList = &pinputData->pathList;
pathNode *ppathNode = 0;
inputFile *pinputFile;
char *fullname = 0;
FILE *fp = 0;
if(!filename) {
fp = stdin;
} else if((ellCount(ppathList)==0) || strchr(filename,'/')){
fp = fopen(filename,"r");
} else {
ppathNode = (pathNode *)ellFirst(ppathList);
while(ppathNode) {
fullname = calloc(strlen(filename)+strlen(ppathNode->directory) +2,
sizeof(char));
strcpy(fullname,ppathNode->directory);
strcat(fullname,"/");
strcat(fullname,filename);
fp = fopen(fullname,"r");
if(fp) break;
free(fullname);
ppathNode = (pathNode *)ellNext(&ppathNode->node);
}
}
if(!fp) {
fprintf(stderr,"msi: Can't open file '%s'\n",filename);
inputErrPrint(pinputData);
exit(1);
}
pinputFile = calloc(1,sizeof(inputFile));
if(ppathNode) {
pinputFile->filename = fullname;
} else if(filename) {
pinputFile->filename = epicsStrDup(filename);
} else {
pinputFile->filename = epicsStrDup("stdin");
}
pinputFile->fp = fp;
ellInsert(&pinputData->inputFileList,0,&pinputFile->node);
}
static void inputCloseFile(inputData *pinputData)
{
inputFile *pinputFile;
pinputFile = (inputFile *)ellFirst(&pinputData->inputFileList);
if(!pinputFile) return;
ellDelete(&pinputData->inputFileList,&pinputFile->node);
if(fclose(pinputFile->fp))
fprintf(stderr,"msi: Can't close input file '%s'\n",pinputFile->filename);
free(pinputFile->filename);
free(pinputFile);
}
static void inputCloseAllFiles(inputData *pinputData)
{
inputFile *pinputFile;
while((pinputFile=(inputFile *)ellFirst(&pinputData->inputFileList))){
inputCloseFile(pinputData);
}
}
/*start of code that handles substitution file*/
typedef enum {
tokenLBrace,tokenRBrace,tokenSeparater,tokenString,tokenEOF
}tokenType;
typedef struct subFile {
char *substitutionName;
FILE *fp;
int lineNum;
char inputBuffer[MAX_BUFFER_SIZE];
char *pnextChar;
tokenType token;
char string[MAX_BUFFER_SIZE];
} subFile;
typedef struct patternNode {
ELLNODE node;
char *var;
} patternNode;
struct subInfo {
subFile *psubFile;
int isFile;
char *filename;
int isPattern;
ELLLIST patternList;
size_t size;
size_t curLength;
char *macroReplacements;
};
static char *subGetNextLine(subFile *psubFile);
static tokenType subGetNextToken(subFile *psubFile);
static void subFileErrPrint(subFile *psubFile,char * message);
static void freeSubFile(subInfo *psubInfo);
static void freePattern(subInfo *psubInfo);
static void catMacroReplacements(subInfo *psubInfo,const char *value);
void freeSubFile(subInfo *psubInfo)
{
subFile *psubFile = psubInfo->psubFile;
if(psubFile->fp) {
if(fclose(psubFile->fp))
fprintf(stderr,"msi: Can't close substitution file\n");
}
free(psubFile);
free(psubInfo->filename);
psubInfo->psubFile = 0;
}
void freePattern(subInfo *psubInfo)
{
patternNode *ppatternNode;
while((ppatternNode = (patternNode *)ellFirst(&psubInfo->patternList))) {
ellDelete(&psubInfo->patternList,&ppatternNode->node);
free(ppatternNode->var);
free(ppatternNode);
}
psubInfo->isPattern = 0;
}
static void substituteDestruct(subInfo *psubInfo)
{
freeSubFile(psubInfo);
freePattern(psubInfo);
free(psubInfo);
return;
}
static void substituteOpen(subInfo **ppvt,char *substitutionName)
{
subInfo *psubInfo;
subFile *psubFile;
FILE *fp;
psubInfo = calloc(1,sizeof(subInfo));
*ppvt = psubInfo;
psubFile = calloc(1,sizeof(subFile));
psubInfo->psubFile = psubFile;
ellInit(&psubInfo->patternList);
fp = fopen(substitutionName,"r");
if(!fp) {
fprintf(stderr,"msi: Can't open file '%s'\n",substitutionName);
exit(1);
}
psubFile->substitutionName = substitutionName;
psubFile->fp = fp;
psubFile->lineNum = 1;
psubFile->inputBuffer[0] = 0;
psubFile->pnextChar = &psubFile->inputBuffer[0];
subGetNextToken(psubFile);
return;
}
static int substituteGetGlobalSet(subInfo *psubInfo)
{
subFile *psubFile = psubInfo->psubFile;
while(psubFile->token==tokenSeparater) subGetNextToken(psubFile);
if(psubFile->token==tokenString && strcmp(psubFile->string,"global")==0) {
subGetNextToken(psubFile);
return(1);
}
return(0);
}
static int substituteGetNextSet(subInfo *psubInfo,char **filename)
{
subFile *psubFile = psubInfo->psubFile;
patternNode *ppatternNode;
*filename = 0;
while(psubFile->token==tokenSeparater) subGetNextToken(psubFile);
if(psubFile->token==tokenEOF) return(0);
if(psubFile->token==tokenString && strcmp(psubFile->string,"file")==0) {
psubInfo->isFile = 1;
if(subGetNextToken(psubFile)!=tokenString) {
subFileErrPrint(psubFile,"Parse error, expecting filename");
exit(1);
}
freePattern(psubInfo);
free(psubInfo->filename);
if(psubFile->string[0]=='"'&&psubFile->string[strlen(psubFile->string)-1]=='"') {
psubFile->string[strlen(psubFile->string)-1]='\0';
psubInfo->filename = macEnvExpand(psubFile->string+1);
}
else {
psubInfo->filename = macEnvExpand(psubFile->string);
}
while(subGetNextToken(psubFile)==tokenSeparater);
if(psubFile->token!=tokenLBrace) {
subFileErrPrint(psubFile,"Parse error, expecting {");
exit(1);
}
subGetNextToken(psubFile);
}
*filename = psubInfo->filename;
while(psubFile->token==tokenSeparater) subGetNextToken(psubFile);
if(psubFile->token==tokenLBrace) return(1);
if(psubFile->token==tokenRBrace) return(1);
if(psubFile->token!=tokenString
|| strcmp(psubFile->string,"pattern")!=0) {
subFileErrPrint(psubFile,"Parse error, expecting pattern");
exit(1);
}
freePattern(psubInfo);
psubInfo->isPattern = 1;
while(subGetNextToken(psubFile)==tokenSeparater);
if(psubFile->token!=tokenLBrace) {
subFileErrPrint(psubFile,"Parse error, expecting {");
exit(1);
}
while(1) {
while(subGetNextToken(psubFile)==tokenSeparater);
if(psubFile->token!=tokenString) break;
ppatternNode = calloc(1,sizeof(patternNode));
ellAdd(&psubInfo->patternList,&ppatternNode->node);
ppatternNode->var = epicsStrDup(psubFile->string);
}
if(psubFile->token!=tokenRBrace) {
subFileErrPrint(psubFile,"Parse error, expecting }");
exit(1);
}
subGetNextToken(psubFile);
return(1);
}
static char *substituteGetGlobalReplacements(subInfo *psubInfo)
{
subFile *psubFile = psubInfo->psubFile;
if(psubInfo->macroReplacements) psubInfo->macroReplacements[0] = 0;
psubInfo->curLength = 0;
while(psubFile->token==tokenSeparater) subGetNextToken(psubFile);
if(psubFile->token==tokenRBrace && psubInfo->isFile) {
psubInfo->isFile = 0;
free(psubInfo->filename);
psubInfo->filename = 0;
freePattern(psubInfo);
subGetNextToken(psubFile);
return(0);
}
if(psubFile->token==tokenEOF) return(0);
if(psubFile->token!=tokenLBrace) return(0);
while(1) {
switch(subGetNextToken(psubFile)) {
case tokenRBrace:
subGetNextToken(psubFile);
if (!psubInfo->macroReplacements) {
catMacroReplacements(psubInfo,"");
}
return(psubInfo->macroReplacements);
case tokenSeparater:
catMacroReplacements(psubInfo,",");
break;
case tokenString:
catMacroReplacements(psubInfo,psubFile->string);
break;
default:
subFileErrPrint(psubFile,"Parse error, illegal token");
exit(1);
}
}
}
static char *substituteGetReplacements(subInfo *psubInfo)
{
subFile *psubFile = psubInfo->psubFile;
patternNode *ppatternNode;
if(psubInfo->macroReplacements) psubInfo->macroReplacements[0] = 0;
psubInfo->curLength = 0;
while(psubFile->token==tokenSeparater) subGetNextToken(psubFile);
if(psubFile->token==tokenRBrace && psubInfo->isFile) {
psubInfo->isFile = 0;
free(psubInfo->filename);
psubInfo->filename = 0;
freePattern(psubInfo);
subGetNextToken(psubFile);
return(0);
}
if(psubFile->token==tokenEOF) return(0);
if(psubFile->token!=tokenLBrace) return(0);
if(psubInfo->isPattern) {
int gotFirstPattern = 0;
while(subGetNextToken(psubFile)==tokenSeparater);
ppatternNode = (patternNode *)ellFirst(&psubInfo->patternList);
while(1) {
if(psubFile->token==tokenRBrace) {
subGetNextToken(psubFile);
return(psubInfo->macroReplacements);
}
if(psubFile->token!=tokenString) {
subFileErrPrint(psubFile,"Parse error, illegal token");
exit(-1);
}
if(gotFirstPattern) catMacroReplacements(psubInfo,",");
gotFirstPattern = 1;
if(ppatternNode) {
catMacroReplacements(psubInfo,ppatternNode->var);
catMacroReplacements(psubInfo,"=");
catMacroReplacements(psubInfo,psubFile->string);
ppatternNode = (patternNode *)ellNext(&ppatternNode->node);
} else {
subFileErrPrint(psubFile,"Warning, too many values given");
}
while(subGetNextToken(psubFile)==tokenSeparater);
}
} else while(1) {
switch(subGetNextToken(psubFile)) {
case tokenRBrace:
subGetNextToken(psubFile);
if (!psubInfo->macroReplacements) {
catMacroReplacements(psubInfo,"");
}
return(psubInfo->macroReplacements);
case tokenSeparater:
catMacroReplacements(psubInfo,",");
break;
case tokenString:
catMacroReplacements(psubInfo,psubFile->string);
break;
default:
subFileErrPrint(psubFile,"Parse error, illegal token");
exit(1);
}
}
}
static char *subGetNextLine(subFile *psubFile)
{
char *pline;
do {
pline = fgets(psubFile->inputBuffer,MAX_BUFFER_SIZE,psubFile->fp);
++psubFile->lineNum;
} while(pline && psubFile->inputBuffer[0]=='#');
if(!pline) {
psubFile->token = tokenEOF;
psubFile->inputBuffer[0] = 0;
psubFile->pnextChar = 0;
return(0);
}
psubFile->pnextChar = &psubFile->inputBuffer[0];
return(&psubFile->inputBuffer[0]);
}
static void subFileErrPrint(subFile *psubFile,char * message)
{
fprintf(stderr,"msi: %s\n",message);
fprintf(stderr," in substitution file '%s' at line %d:\n %s",
psubFile->substitutionName,
psubFile->lineNum,psubFile->inputBuffer);
}
static tokenType subGetNextToken(subFile *psubFile)
{
char *p;
char *pto;
p = psubFile->pnextChar;
if(!p) { psubFile->token = tokenEOF; return(tokenEOF);}
if(*p==0 || *p=='\n' || *p=='#') {
p = subGetNextLine(psubFile);
if(!p) { psubFile->token = tokenEOF; return(tokenEOF);}
else { psubFile->token = tokenSeparater; return(tokenSeparater);}
}
while(isspace(*p)) p++;
if(*p=='{') {
psubFile->token = tokenLBrace;
psubFile->pnextChar = ++p;
return(tokenLBrace);
}
if(*p=='}') {
psubFile->token = tokenRBrace;
psubFile->pnextChar = ++p;
return(tokenRBrace);
}
if(*p==0 || isspace(*p) || *p==',') {
while(isspace(*p) || *p==',') p++;
psubFile->token = tokenSeparater;
psubFile->pnextChar = p;
return(tokenSeparater);
}
/*now handle quoted strings*/
if(*p=='"') {
pto = &psubFile->string[0];
*pto++ = *p++;
while(*p!='"') {
if(*p==0 || *p=='\n') {
subFileErrPrint(psubFile,"Strings must be on single line\n");
exit(1);
}
/*allow escape for imbeded quote*/
if((*p=='\\') && *(p+1)=='"') {
*pto++ = *p++;
*pto++ = *p++;
continue;
}
*pto++ = *p++;
}
*pto++ = *p++;
psubFile->pnextChar = p;
*pto = 0;
psubFile->token = tokenString;
return(tokenString);
}
/*Now take anything up to next non String token and not space*/
pto = &psubFile->string[0];
while(!isspace(*p) && (strspn(p,"\",{}")==0)) *pto++ = *p++;
*pto = 0;
psubFile->pnextChar = p;
psubFile->token = tokenString;
return(tokenString);
}
static void catMacroReplacements(subInfo *psubInfo,const char *value)
{
size_t len = strlen(value);
if(psubInfo->size <= (psubInfo->curLength + len)) {
size_t newsize = psubInfo->size + MAX_BUFFER_SIZE;
char *newbuf;
if(newsize <= psubInfo->curLength + len)
newsize = psubInfo->curLength + len + 1;
newbuf = calloc(1,newsize);
if(!newbuf) {
fprintf(stderr,"calloc failed for size %Zu\n",newsize);
exit(1);
}
if(psubInfo->macroReplacements) {
memcpy(newbuf,psubInfo->macroReplacements,psubInfo->curLength);
free(psubInfo->macroReplacements);
}
psubInfo->size = newsize;
psubInfo->macroReplacements = newbuf;
}
strcat(psubInfo->macroReplacements,value);
psubInfo->curLength += len;
}
+438
View File
@@ -0,0 +1,438 @@
<!DOCTYPE html public "-//w3c//dtd html 4.0 transitional//en">
<html>
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
</head>
<body>
<h1>msi: Macro Substitution and Include Tool</h1>
<h2>Introduction</h2>
<p>msi is a general purpose macro substitution/include tool. It accepts as input
an ascii template file. It looks for lines containing two reserved command
names: <tt>include</tt> and <tt>substitute</tt>. It also looks for and performs
substitutions on macros of the form $(var) and ${var}. It uses the macLib
routines from EPICS Base to perform the substitutions, so it also accepts the
default value and value definition syntax that macLib implements.</p>
<p>msi also allows substitutions to be specified via a separate substitution
file. This substitution file allows the same format as the substitution files
accepted by the EPICS IOC's dbLoadTemplate command.</p>
<h2>Command Syntax:</h2>
<pre>msi -V -g -o<i>outfile</i> -I<i>dir</i> -M<i>subs</i> -S<i>subfile</i> <i>template</i></pre>
<p>All parameters are optional. The -o, -I, -M, and -S switches may be
separated from their associated value string by spaces if desired. Output will
be written to stdout unless the -o option is given.</p>
<p>Switches have the following meanings:</p>
<dl>
<dt><tt>-V</tt></dt>
<dd>Verbose warnings; if this parameter is specified then any undefined
macro discovered in the template file which does not have an associated
default value is considered an error. An error message is generated, and
when msi terminates it will do so with an exit status of 2.</dd>
<dt><tt>-g</tt></dt>
<dd>When this flag is given all macros defined in a substitution file will
have global scope and thus their values will persist until a new value is
given for this macro. This flag is provided for backwards compatibility as
this was the behavior of previous versions of msi, but it does not follow
common scoping rules and is discouraged.</dd>
<dt><tt>-o</tt> <i>file</i></dt>
<dd>Output will be written to the specifed file rather than to the standard
output.</dd>
<dt><tt>-I</tt> <i>dir</i></dt>
<dd>This parameter, which may be repeated or contain a colon-separated (or
semi-colon separated on Windows) list of directory paths, specifies a search
path for include commands. For example:
<blockquote>
<pre>msi -I /home/mrk/examples:. -I.. template</pre>
</blockquote>
specifies that all named files should be searched for in the following
locations in the order given:
<ol>
<li><tt>/home/mrk/examples</tt></li>
<li><tt>.</tt> (the current directory)</li>
<li><tt>..</tt> (the parent of the current directory)</li>
</ol>
</dd>
<dt><tt>-M</tt> <i>substitutions</i></dt>
<dd>This parameter specifies macro values for the template instance.
Multiple macro values can be specified in one substitution parameter, or in
multiple <tt>-M</tt> parameters. For example:
<blockquote>
<pre>msi -M "a=aval,b=bval" -Mc=cval template</pre>
</blockquote>
specifies that in the template file each occurrence of:
<dl>
<dd><tt>$(a)</tt> or <tt>${a}</tt> is replaced by <tt>aval</tt></dd>
<dd><tt>$(b)</tt> or <tt>${b}</tt> is replaced by <tt>bval</tt></dd>
<dd><tt>$(c)</tt> or <tt>${c}</tt> is replaced by <tt>cval</tt></dd>
</dl>
</dd>
<dt><tt>-S</tt> <i>subfile</i></dt>
<dd>The substitution file. See below for format.</dd>
<dt><i>template</i></dt>
<dd> The input file. If no file is specified then input is taken from
stdin, i.e. msi can be used as a filter. See below for a description of
commands that can be embedded in the template file.</dd>
</dl>
<p>It is not possible to display usage by just typing <tt>msi</tt> since
executing the command with no arguments is a valid command. To show usage
specify an illegal switch, e.g.</p>
<blockquote>
<pre>msi -help</pre>
</blockquote>
<h2>Exit Status</h2>
<dl>
<dt>0<dd>Success.
<dt>1<dd>Can't open/create file, or other I/O error.
<dt>2<dd>Undefined macros encountered with the <tt>-V</tt> option specified.
</dl>
<h2>Template File Format</h2>
<p>This file contains the text to be read and written to the output after macro
substitution is performed. If no file is given then input is read from stdin.
Variable instances to be substituted by macro values are expressed in the
template using the syntax <tt>$(</tt><i>name</i><tt>)</tt> or
<tt>${</tt><i>name</i><tt>}</tt>. The template can also provide default values
to be used when a macro has not been given a value, using the syntax
<tt>$(</tt><i>name</i><tt>=</tt><i>default</i><tt>)</tt> or
<tt>${</tt><i>name</i><tt>=</tt><i>default</i><tt>}</tt>.</p>
<p>For example, using the command</p>
<blockquote>
<pre>msi -M name=Marty template</pre>
</blockquote>
<p>where the file template contains</p>
<blockquote>
<pre>My name is $(name)
My age is $(age=none of your business)</pre>
</blockquote>
<p>results in this output:</p>
<blockquote>
<pre>My name is Marty
My age is none of your business</pre>
</blockquote>
<p>Macro variables and their default values can be expressed in terms of other
macros if necessary, to almost any level of complexity. Recursive definitions
will generate warning messages on stderr and result in undefined output.</p>
<p>The template file is read and processed one line at a time, where the
maximum length of a line before and/or after macro expansion is 1023 characters
&mdash; longer input or output lines will cause msi to fail. Within the context
of a single line, macro expansion does not occur when the variable instance
appears inside a single-quoted string, or where the dollar sign <tt>$</tt> is
preceded by a back-slash character <tt>\</tt>, but as with the standard Unix
shells, variables inside double quoted strings are expanded properly.</p>
<p>However neither back-slash characters nor quotes of either variety are
removed when generating the output file, so depending on what is being output
the single quote behaviour may not be useful and may even be a hinderance. It
cannot be disabled in the current version of msi.</p>
<h3>Template file commands</h3>
<p>In addition to the regular text and variable instances described above, the
template file may also contain commands which allow the insertion of other
template files and the ability to set macro values inside the template file
itself. These commands are:</p>
<blockquote>
<pre>include "file"
substitute "var=value,var=value,..."</pre>
</blockquote>
<p>Lines containing commands must be in one of these forms:</p>
<ul>
<li><tt>include "</tt><i>filename</i><tt>"</tt></li>
<li><tt>substitute "</tt><i>name1=value1, name2=value2, ...</i><tt>"</tt></li>
</ul>
<p>White space is allowed before and after the command verb, and after the
quoted string. If embedded quotes are needed, the backslash character
<tt>\</tt> can be used as an escape character. For example</p>
<blockquote>
<pre>substitute "a=\"val\""</pre>
</blockquote>
<p>specifies that (unless <tt>a</tt> is subsequently redefined) wherever a
<tt>$(a)</tt> macro appears in the template below this point, the text
<tt>"val"</tt> (including the double quote characters) will appear in the
output instead.</p>
<p>If a line does match either syntax above it is just passed to macLib for
processing without any notification. Thus the input line:</p>
<blockquote>
<pre>include "myfile" #include file</pre>
</blockquote>
<p>would just be passed to macLib, i.e. it would <em>not</em> be considered an
include command.</p>
<p>As an example of these commands, let the Unix command be:</p>
<blockquote>
<pre>msi template</pre>
</blockquote>
<p>and file includeFile contain:</p>
<blockquote>
<pre>first name is ${first}
family name is ${family}</pre>
</blockquote>
<p>and template is</p>
<blockquote>
<pre>substitute "first=Marty,family=Kraimer"
include "includeFile"
substitute "first=Irma,family=Kraimer"
include "includeFile"</pre>
</blockquote>
<p>then the following is written to the output.</p>
<blockquote>
<pre>first name is Marty
family name is Kraimer
first name is Irma
family name is Kraimer</pre>
</blockquote>
<p>Note that the IOC's <tt>dbLoadTemplate</tt> command does not support the
<tt>substitute</tt> syntax in template files, although the <tt>include</tt>
syntax is supported.</p>
<h2>Substitution File Format</h2>
<p>The optional substitution file has three formats: regular, pattern, and
dbTemplate format. We will discuss each separately.</p>
<h3>Regular format</h3>
<blockquote>
<pre>global {gbl_var1=gbl_val1, gbl_var2=gbl_val2, ...}
{var1=set1_val1, var2=set1_val2, ...}
{var2=set2_val2, var1=set2_val1, ...}
global {gbl_var1=gbl_val3, gbl_var2=gbl_val4, ...}
{var1=set3_val1, var2=set3_val2, ...}
{var2=set4_val2, var1=set4_val1, ...}</pre>
</blockquote>
<p>The template file is output with macro substitutions performed once for each
set of braces containing macro replacement values.</p>
<h3>Pattern format</h3>
<blockquote>
<pre>global {gbl_var1=gbl_val1, gbl_var2=gbl_val2, ...}
pattern {var1, var2, ...}
{set1_val1, set1_val2, ...}
{set2_val1, set2_val2, ...}
pattern {var2, var1, ...}
global {gbl_var1=gbl_val3, gbl_var2=gbl_val4, ...}
{set3_val2, set3_val1, ...}
{set4_val2, set4_val2, ...}</pre>
</blockquote>
<p>This produces the same result as the regular format example above.</p>
<h3>dbLoadTemplate Format</h3>
<p>This format is an extension of the format accepted by the EPICS IOC command
<tt>dbLoadTemplate</tt>, and allows templates to be expanded on the host rather
by using dbLoadTemplate at IOC boot time.</p>
<blockquote>
<pre>global {gbl_var1=gbl_val1, gbl_var2=gbl_val2, ...}
file templatefile {
<i>pattern format or regular format</i>
}
file "${WHERE}/template2" {
<i>pattern format or regular format</i>
}</pre>
</blockquote>
<p>For the dbTemplate format, the template filename does not have to be given
on the command line, and is usually specified in the substitutions file
instead. If a template filename is given on the command line it will override
the filenames listed in the substitutions files.</p>
<h3>Syntax for all formats</h3>
<p>A comment line may appear anywhere in a substitution file, and will be
ignored. A comment line is any line beginning with the character <tt>#</tt>,
which must be the very first character on the line.</p>
<p>Global definitions may supplement or override the macro values supplied on
the command-line using the <tt>-M</tt> switch, and set default values that will
survive for the remainder of the file unless another global definition of the
same macro changes it.</p>
<p>For definitions within braces given in any of the file formats, a separator
must be given between items. A separator is either a comma, or one or more of
the standard white space characters (space, formfeed, newline, carriage return,
tab or vertical tab).</p>
<p>Each item within braces can be an alphanumeric token, or a double-quoted
string. A back-slash character <tt>\</tt> can be used to escape a quote
character needed inside a quoted string. These three sets of substitutions are
all equivalent:</p>
<blockquote>
<pre>{a=aa b=bb c="\"cc\""}
{b="bb",a=aa,c="\"cc\""}
{
c="\"cc\""
b=bb
a="aa"
}</pre>
</blockquote>
<p>Within a substitutions file, the file name may appear inside double quotation
marks; these are required if the name contains certain characters or environment
variable macros of the form ${ENV_VAR} or $(ENV_VAR), which will be expanded
before the file is opened.</p>
<h3>Regular substitution example</h3>
<p>Let the command be:</p>
<blockquote>
<pre>msi -S substitute template</pre>
</blockquote>
<p>The file <tt>template</tt> contains</p>
<blockquote>
<pre>first name is ${first}
family name is ${family}</pre>
</blockquote>
<p> and the file <tt>substitute</tt> is</p>
<blockquote>
<pre>global {family=Kraimer}
{first=Marty}
{first=Irma}</pre>
</blockquote>
<p>The following is the output produced:</p>
<blockquote>
<pre>first name is Marty
family name is Kraimer
first name is Irma
family name is Kraimer</pre>
</blockquote>
<h3>Pattern substitution example</h3>
<p>Let the command be:</p>
<blockquote>
<pre>msi -S pattern template</pre>
</blockquote>
<p>The file <tt>pattern</tt> contains</p>
<blockquote>
<pre>pattern {first,last}
{Marty,Kraimer}
{Irma,Kraimer}</pre>
</blockquote>
<p>and <tt>template</tt> is the same as the previous example:</p>
<blockquote>
<pre>first name is ${first}
family name is ${family}</pre>
</blockquote>
<p>This is the output:</p>
<blockquote>
<pre>first name is Marty
family name is Kraimer
first name is Irma
family name is Kraimer</pre>
</blockquote>
<h3>dbTemplate example</h3>
Let the command be
<blockquote>
<pre>msi -S xxx.substitutions</pre>
</blockquote>
<tt>xxx.substitutions</tt> is
<blockquote>
<pre>file template {
pattern {first,last}
{Marty,Kraimer}
{Irma,Kraimer}
pattern {last,first}
{Smith,Bill}
{Smith,Mary}
}
file template {
{first=Marty,last=Kraimer}
{first=Irma,last=Kraimer}
}</pre>
</blockquote>
<tt>template</tt> is the same as in the previous example..
<p>The following is written to the output</p>
<blockquote>
<pre>first name is Marty
family name is Kraimer
first name is Irma
family name is Kraimer
first name is Bill
last name is Smith
first name is Mary
last name is Smith
first name is Marty
family name is Kraimer
first name is Irma
family name is Kraimer</pre>
</blockquote>
</body>
</html>
+21
View File
@@ -0,0 +1,21 @@
#*************************************************************************
# Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne
# National Laboratory.
# EPICS BASE is distributed subject to a Software License Agreement found
# in the file LICENSE that is included with this distribution.
#*************************************************************************
TOP=../../../..
include $(TOP)/configure/CONFIG
TESTPROD_HOST_DEFAULT = dbltExpand
TESTPROD_HOST_WIN32 = -nil-
dbltExpand_SRCS += dbltExpand.c
dbltExpand_LIBS += dbCore Com
TESTS += msi
TESTSCRIPTS_HOST += $(TESTS:%=%.t)
include $(TOP)/configure/RULES
+100
View File
@@ -0,0 +1,100 @@
/*************************************************************************\
* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne
* National Laboratory.
* EPICS Base is distributed subject to a Software License Agreement found
* in the file LICENSE that is included with this distribution.
\*************************************************************************/
/* This is a simple version of msi for testing the dbLoadTemplate() code.
*
* It calls dbLoadTemplate() to parse the substitution file, but replaces
* dbLoadRecords() with its own version that reads the template file,
* expands any macros in the text and prints the result to stdout.
*
* This technique won't work on Windows, dbLoadRecords() has to be
* epicsShare... decorated and loaded from a shared library.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "macLib.h"
#include "dbLoadTemplate.h"
#define BUFFER_SIZE 0x10000
static char *input_buffer, *output_buffer;
int dbLoadRecords(const char *file, const char *macros)
{
MAC_HANDLE *macHandle = NULL;
char **macPairs;
FILE *fp;
size_t input_len;
if (macCreateHandle(&macHandle, NULL)) {
fprintf(stderr, "macCreateHandle failed\n");
exit(1);
}
macSuppressWarning(macHandle, 1);
macParseDefns(macHandle, macros, &macPairs);
if (!macPairs) {
macDeleteHandle(macHandle);
macHandle = NULL;
} else {
macInstallMacros(macHandle, macPairs);
free(macPairs);
}
fp = fopen(file, "r");
if (!fp) {
fprintf(stderr, "fopen('%s') failed: %s\n", file, strerror(errno));
exit(1);
}
input_len = fread(input_buffer, 1, BUFFER_SIZE, fp);
if (!feof(fp)) {
fprintf(stderr, "input file > 64K!\n");
fclose(fp);
exit(1);
}
input_buffer[input_len] = 0;
if (fclose(fp)) {
fprintf(stderr, "fclose('%s') failed: %s\n", file, strerror(errno));
exit(1);
}
macExpandString(macHandle, input_buffer, output_buffer, BUFFER_SIZE-1);
printf(output_buffer);
if (macHandle) macDeleteHandle(macHandle);
return 0;
}
int main(int argc, char **argv)
{
input_buffer = malloc(BUFFER_SIZE);
output_buffer = malloc(BUFFER_SIZE);
if (!input_buffer || !output_buffer) {
fprintf(stderr, "malloc(%d) failed\n", BUFFER_SIZE);
exit(1);
}
if (argc != 2) {
fprintf(stderr, "Usage: %s file.sub\n", argv[0]);
exit(1);
}
dbLoadTemplate(argv[1], NULL);
free(output_buffer);
free(input_buffer);
return 0;
}
+48
View File
@@ -0,0 +1,48 @@
#!/usr/bin/perl
#*************************************************************************
# Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne
# National Laboratory.
# EPICS BASE is distributed subject to a Software License Agreement found
# in file LICENSE that is included with this distribution.
#*************************************************************************
# Script to run tests on the msi program
use FindBin qw($Bin); # To find the msi executable
use strict;
use Test;
BEGIN {plan tests => 7}
ok(msi('-I .. ../t1-template.txt'), slurp('../t1-result.txt'));
ok(msi('-I.. -S ../t2-substitution.txt'), slurp('../t2-result.txt'));
ok(msi('-I. -I.. -S ../t3-substitution.txt'), slurp('../t3-result.txt'));
ok(msi('-g -I.. -S ../t4-substitution.txt'), slurp('../t4-result.txt'));
ok(msi('-S ../t5-substitute.txt ../t5-template.txt'), slurp('../t5-result.txt'));
ok(msi('-S ../t6-substitute.txt ../t6-template.txt'), slurp('../t6-result.txt'));
# Check -o works
my $out = 't7-output.txt';
unlink $out;
msi("-I.. -o $out ../t1-template.txt");
ok(slurp($out), slurp('../t1-result.txt'));
# Support routines
sub slurp {
my ($file) = @_;
open my $in, '<', $file
or die "Can't open file $file: $!\n";
my $contents = do { local $/; <$in> };
return $contents;
}
sub msi {
my ($args) = @_;
my $arch = $ENV{EPICS_HOST_ARCH};
my $exe = ($^O eq 'MSWin32') || ($^O eq 'cygwin') ? '.exe' : '';
my $msi = "$Bin/../../../O.$arch/msi$exe";
return `$msi $args`;
}
+5
View File
@@ -0,0 +1,5 @@
This is t1-include.txt $(include-file-again=)
a = $(a=default value used when a is undefined)
b = $(b=default value used when b is undefined)
substitute "include-file-again=again"
End of t1-include.txt
+21
View File
@@ -0,0 +1,21 @@
This is t1-template.txt
With $(a,undefined) & $(b,undefined):
This is t1-include.txt
a = default value used when a is undefined
b = default value used when b is undefined
End of t1-include.txt
On defining a=aaa & b=bbb:
This is t1-include.txt again
a = aaa
b = bbb
End of t1-include.txt
On setting a="aa":
This is t1-include.txt again
a = "aa"
b = bbb
End of t1-include.txt
End of t1-template.txt
+14
View File
@@ -0,0 +1,14 @@
This is t1-template.txt
With $(a) & ${b}:
include "t1-include.txt"
substitute "a=aaa,b=bbb"
On defining a=$(a) & b=${b}:
include "t1-include.txt"
substitute "a=\"aa\""
On setting a=$(a):
include "t1-include.txt"
End of t1-template.txt
+6
View File
@@ -0,0 +1,6 @@
a = va1-a b = def-b c = def-c d = $(d,undefined)
a = va2-a b = va2-b c = def-c d = $(d,undefined)
a = va3-a b = va3-b c = va3-c d = $(d,undefined)
a = va4-a b = va4-b c = def-c d = $(d,undefined)
a = va5-a b = def-b c = def-c d = $(d,undefined)
a = pt3-a b = pt3-b c = pt3-c d = $(d,undefined)
@@ -0,0 +1,11 @@
file t2-template.txt {
{a=va1-a}
{a=va2-a, b=va2-b}
{a=va3-a, b=va3-b, c=va3-c}
{a=va4-a, b=va4-b}
{a=va5-a}
}
file t2-template.txt {
pattern {a, b, c}
{pt3-a, pt3-b, pt3-c}
}
+1
View File
@@ -0,0 +1 @@
a = $(a=def-a) b = $(b=def-b) c = $(c=def-c) d = $(d,undef)
+28
View File
@@ -0,0 +1,28 @@
a = gb1-a b = gb1-b c = def-c d = $(d,undefined)
a = va1-a b = gb1-b c = def-c d = $(d,undefined)
a = va2-a b = va2-b c = def-c d = $(d,undefined)
a = va3-a b = va3-b c = va3-c d = $(d,undefined)
a = va4-a b = va4-b c = def-c d = $(d,undefined)
a = va5-a b = gb1-b c = def-c d = $(d,undefined)
a = gb1-a b = gb1-b c = def-c d = $(d,undefined)
a = gb2-a b = gb2-b c = def-c d = $(d,undefined)
a = va1-a b = gb2-b c = def-c d = $(d,undefined)
a = va2-a b = va2-b c = def-c d = $(d,undefined)
a = va3-a b = va3-b c = va3-c d = $(d,undefined)
a = va4-a b = va4-b c = def-c d = $(d,undefined)
a = va5-a b = gb2-b c = def-c d = $(d,undefined)
a = gb2-a b = gb2-b c = def-c d = $(d,undefined)
a = gb3-a b = gb3-b c = def-c d = $(d,undefined)
a = pt1-a b = gb3-b c = def-c d = $(d,undefined)
a = pt2-a b = pt2-b c = def-c d = $(d,undefined)
a = pt3-a b = pt3-b c = pt3-c d = $(d,undefined)
a = pt4-a b = pt4-b c = def-c d = $(d,undefined)
a = pt5-a b = gb3-b c = def-c d = $(d,undefined)
a = gb3-a b = gb3-b c = def-c d = $(d,undefined)
a = gb4-a b = gb4-b c = def-c d = $(d,undefined)
a = pt1-a b = gb4-b c = def-c d = $(d,undefined)
a = pt2-a b = pt2-b c = def-c d = $(d,undefined)
a = pt3-a b = pt3-b c = pt3-c d = $(d,undefined)
a = pt4-a b = pt4-b c = def-c d = $(d,undefined)
a = pt5-a b = gb4-b c = def-c d = $(d,undefined)
a = gb4-a b = gb4-b c = def-c d = $(d,undefined)
@@ -0,0 +1,37 @@
global {a=gb1-a, b=gb1-b}
file t3-template.txt {
{}
{a=va1-a}
{a=va2-a, b=va2-b}
{a=va3-a, b=va3-b, c=va3-c}
{a=va4-a, b=va4-b}
{a=va5-a}
{}
global {a=gb2-a, b=gb2-b}
{}
{a=va1-a}
{a=va2-a, b=va2-b}
{a=va3-a, b=va3-b, c=va3-c}
{a=va4-a, b=va4-b}
{a=va5-a}
{}
}
global {b=gb3-b, a=gb3-a}
file t3-template.txt {
pattern {a, b, c}
{}
{pt1-a}
{pt2-a, pt2-b}
{pt3-a, pt3-b, pt3-c}
{pt4-a, pt4-b}
{pt5-a}
{}
global {b=gb4-b, a=gb4-a}
{}
{pt1-a}
{pt2-a, pt2-b}
{pt3-a, pt3-b, pt3-c}
{pt4-a, pt4-b}
{pt5-a}
{}
}
+1
View File
@@ -0,0 +1 @@
a = $(a=def-a) b = $(b=def-b) c = $(c=def-c) d = $(d,undef)
+6
View File
@@ -0,0 +1,6 @@
a = va1-a b = def-b c = def-c d = $(d,undefined)
a = va2-a b = va2-b c = def-c d = $(d,undefined)
a = va3-a b = va3-b c = va3-c d = $(d,undefined)
a = va4-a b = va4-b c = va3-c d = $(d,undefined)
a = va5-a b = va4-b c = va3-c d = $(d,undefined)
a = pt3-a b = pt3-b c = pt3-c d = $(d,undefined)
@@ -0,0 +1,11 @@
file t2-template.txt {
{a=va1-a}
{a=va2-a, b=va2-b}
{a=va3-a, b=va3-b, c=va3-c}
{a=va4-a, b=va4-b}
{a=va5-a}
}
file t2-template.txt {
pattern {a, b, c}
{pt3-a, pt3-b, pt3-c}
}
+20
View File
@@ -0,0 +1,20 @@
# comment line
a = 111
b = 222
c = xx
d = $(d,undefined)
# comment line
a = aaa
b = bbb
c = ccc
d = $(d,undefined)
# comment line
a = AA
b = BB
c = xx
d = $(d,undefined)
# comment line
a = aaa
b = bbb
c = yy
d = $(d,undefined)
@@ -0,0 +1,9 @@
global {c=xx}
{a=111,b="222"}
{ a = aaa , b=bbb , c = ccc}
{a=AA,b='BB'}
global { c = yy }
{
a= aaa
b= bbb
}
+5
View File
@@ -0,0 +1,5 @@
# comment line
a = $(a)
b = $(b)
c = $(c)
d = $(d)
+20
View File
@@ -0,0 +1,20 @@
# comment line
a = 111
b = 222
c = xx
d = $(d,undefined)
# comment line
a = aaa
b = bbb
c = ccc
d = $(d,undefined)
# comment line
a = AA
b = BB
c = xx
d = $(d,undefined)
# comment line
a = aaa
b = bbb
c = yy
d = $(d,undefined)
+13
View File
@@ -0,0 +1,13 @@
global {c=xx}
pattern {b,a}
{"222",111}
pattern {a b c}
{ aaa , bbb , ccc}
pattern { a , b }
{AA,'BB'}
global { c = yy }
pattern { a , b }
{
aaa
bbb
}
+5
View File
@@ -0,0 +1,5 @@
# comment line
a = $(a)
b = $(b)
c = $(c)
d = $(d)
+5
View File
@@ -0,0 +1,5 @@
# comment line
a = $(a)
b = $(b)
c = $(c)
d = $(d)