merged msi-join branch
This commit is contained in:
@@ -109,7 +109,7 @@ DBST = dbst
|
||||
endif
|
||||
|
||||
ifndef MSI
|
||||
MSI = msi
|
||||
MSI = $(EPICS_BASE_HOST_BIN)/msi
|
||||
endif
|
||||
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,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*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
%%
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
— 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>
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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`;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
a = $(a=def-a) b = $(b=def-b) c = $(c=def-c) d = $(d,undef)
|
||||
@@ -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}
|
||||
{}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
a = $(a=def-a) b = $(b=def-b) c = $(c=def-c) d = $(d,undef)
|
||||
@@ -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}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
# comment line
|
||||
a = $(a)
|
||||
b = $(b)
|
||||
c = $(c)
|
||||
d = $(d)
|
||||
@@ -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,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
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
# comment line
|
||||
a = $(a)
|
||||
b = $(b)
|
||||
c = $(c)
|
||||
d = $(d)
|
||||
@@ -0,0 +1,5 @@
|
||||
# comment line
|
||||
a = $(a)
|
||||
b = $(b)
|
||||
c = $(c)
|
||||
d = $(d)
|
||||
Reference in New Issue
Block a user