Merge lp:~epics-core/epics-base/msi-join into lp:~epics-core/epics-base/3.15

Proposed by Andrew Johnson
Status: Merged
Merged at revision: 12293
Proposed branch: lp:~epics-core/epics-base/msi-join
Merge into: lp:~epics-core/epics-base/3.15
Diff against target: 2715 lines (+2044/-425) (has conflicts)
30 files modified
configure/CONFIG_BASE (+1/-1)
src/ioc/dbtemplate/Makefile (+34/-0)
src/ioc/dbtemplate/dbLoadTemplate.h (+3/-7)
src/ioc/dbtemplate/dbLoadTemplate.html (+0/-137)
src/ioc/dbtemplate/dbLoadTemplate.y (+304/-248)
src/ioc/dbtemplate/dbLoadTemplate_lex.l (+28/-27)
src/ioc/dbtemplate/dbtoolsIocRegister.c (+8/-5)
src/ioc/dbtemplate/msi.c (+841/-0)
src/ioc/dbtemplate/msi.html (+438/-0)
src/ioc/dbtemplate/test/Makefile (+21/-0)
src/ioc/dbtemplate/test/dbltExpand.c (+100/-0)
src/ioc/dbtemplate/test/msi.plt (+48/-0)
src/ioc/dbtemplate/test/t1-include.txt (+5/-0)
src/ioc/dbtemplate/test/t1-result.txt (+21/-0)
src/ioc/dbtemplate/test/t1-template.txt (+14/-0)
src/ioc/dbtemplate/test/t2-result.txt (+6/-0)
src/ioc/dbtemplate/test/t2-substitution.txt (+11/-0)
src/ioc/dbtemplate/test/t2-template.txt (+1/-0)
src/ioc/dbtemplate/test/t3-result.txt (+28/-0)
src/ioc/dbtemplate/test/t3-substitution.txt (+37/-0)
src/ioc/dbtemplate/test/t3-template.txt (+1/-0)
src/ioc/dbtemplate/test/t4-result.txt (+6/-0)
src/ioc/dbtemplate/test/t4-substitution.txt (+11/-0)
src/ioc/dbtemplate/test/t5-result.txt (+20/-0)
src/ioc/dbtemplate/test/t5-substitute.txt (+9/-0)
src/ioc/dbtemplate/test/t5-template.txt (+5/-0)
src/ioc/dbtemplate/test/t6-result.txt (+20/-0)
src/ioc/dbtemplate/test/t6-substitute.txt (+13/-0)
src/ioc/dbtemplate/test/t6-template.txt (+5/-0)
src/ioc/dbtemplate/test/template (+5/-0)
Text conflict in src/ioc/dbtemplate/Makefile
To merge this branch: bzr merge lp:~epics-core/epics-base/msi-join
Reviewer Review Type Date Requested Status
EPICS Core Developers Pending
Review via email: mp+108420@code.launchpad.net

Description of the change

This branch brings an updated version of the msi tool (and its HTML documentation) into Base. Additions to both msi and dbLoadRecords support defining global macros in substition files, and a flag for msi that changes substitution macros from local (the new default) to global scope (the original behavior).

Since msi only uses the macro substitution code from Base, this branch also builds a simple test program called dbltExpand which calls the dbLoadTemplate substitution file parser to help prove that the parser modifications made to both msi and dbLoadTemplate are compatible.

I also added a series of automated tests of msi into the build.

To post a comment you must log in.
Revision history for this message
Andrew Johnson (anj) wrote :

For some reason Launchpad is not showing all of the revisions that this merge will incorporate; the full set of commits on this branch goes back to revno 12104 inclusive. The diff below is also a bit confused by the 3.15 source tree reorganization, but if you ignore the embedded conflict markers it does seem to show all of the changes.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'configure/CONFIG_BASE'
--- configure/CONFIG_BASE 2012-04-04 17:30:27 +0000
+++ configure/CONFIG_BASE 2012-06-01 20:39:23 +0000
@@ -109,7 +109,7 @@
109endif109endif
110110
111ifndef MSI111ifndef MSI
112MSI = msi112MSI = $(EPICS_BASE_HOST_BIN)/msi
113endif113endif
114114
115115
116116
=== modified file 'src/ioc/dbtemplate/Makefile'
--- src/ioc/dbtemplate/Makefile 2011-09-15 19:05:05 +0000
+++ src/ioc/dbtemplate/Makefile 2012-06-01 20:39:23 +0000
@@ -14,9 +14,43 @@
14INC += dbLoadTemplate.h14INC += dbLoadTemplate.h
15INC += dbtoolsIocRegister.h15INC += dbtoolsIocRegister.h
1616
17<<<<<<< TREE
17dbCore_SRCS += dbLoadTemplate.c18dbCore_SRCS += dbLoadTemplate.c
18dbCore_SRCS += dbtoolsIocRegister.c19dbCore_SRCS += dbtoolsIocRegister.c
1920
20HTMLS += dbtemplate/dbLoadTemplate.html21HTMLS += dbtemplate/dbLoadTemplate.html
2122
22CLEANS += dbLoadTemplate_lex.c dbLoadTemplate.c23CLEANS += dbLoadTemplate_lex.c dbLoadTemplate.c
24=======
25LIBRARY_IOC = dbtoolsIoc
26
27LIB_SRCS += dbLoadTemplate.c
28LIB_SRCS += dbtoolsIocRegister.c
29dbtoolsIoc_LIBS = dbIoc dbStaticIoc Com
30dbtoolsIoc_RCS = dbtoolsIoc.rc
31HTMLS += dbLoadTemplate.html
32
33PROD_HOST = msi
34
35msi_SRCS = msi.c
36msi_LIBS += Com
37HTMLS = msi.html
38
39# For R3.13 compatibility only
40ifeq ($(strip $(COMPAT_313)),YES)
41OBJLIB_vxWorks=dbtoolsIoc
42OBJLIB_SRCS = $(LIB_SRCS)
43endif
44
45include $(TOP)/configure/RULES
46
47#
48# These lex sources are included in some C sources,
49# so they have to be created in time:
50#
51dbLoadTemplate.c: dbLoadTemplate_lex.c ../dbLoadTemplate.h
52
53clean::
54 @$(RM) dbLoadTemplate_lex.c dbLoadTemplate.c
55
56>>>>>>> MERGE-SOURCE
2357
=== modified file 'src/ioc/dbtemplate/dbLoadTemplate.h'
--- src/ioc/dbtemplate/dbLoadTemplate.h 2002-07-12 21:35:43 +0000
+++ src/ioc/dbtemplate/dbLoadTemplate.h 2012-06-01 20:39:23 +0000
@@ -3,8 +3,7 @@
3* National Laboratory.3* National Laboratory.
4* Copyright (c) 2002 The Regents of the University of California, as4* Copyright (c) 2002 The Regents of the University of California, as
5* Operator of Los Alamos National Laboratory.5* Operator of Los Alamos National Laboratory.
6* EPICS BASE Versions 3.13.76* EPICS BASE is distributed subject to a Software License Agreement found
7* and higher are distributed subject to a Software License Agreement found
8* in file LICENSE that is included with this distribution. 7* in file LICENSE that is included with this distribution.
9\*************************************************************************/8\*************************************************************************/
10/* dbLoadTemplate.h */9/* dbLoadTemplate.h */
@@ -13,10 +12,7 @@
13#define INCdbLoadTemplateh12#define INCdbLoadTemplateh
1413
15#include "shareLib.h"14#include "shareLib.h"
16epicsShareFunc int epicsShareAPI dbLoadTemplate(char* sub_file);15epicsShareFunc int epicsShareAPI dbLoadTemplate(
16 const char *sub_file, const char *cmd_collect);
1717
18#endif /*INCdbLoadTemplateh*/18#endif /*INCdbLoadTemplateh*/
19
20
21
22
2319
=== removed file 'src/ioc/dbtemplate/dbLoadTemplate.html'
--- src/ioc/dbtemplate/dbLoadTemplate.html 2002-07-12 21:35:43 +0000
+++ src/ioc/dbtemplate/dbLoadTemplate.html 1970-01-01 00:00:00 +0000
@@ -1,137 +0,0 @@
1/*************************************************************************\
2* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
3* National Laboratory.
4* Copyright (c) 2002 The Regents of the University of California, as
5* Operator of Los Alamos National Laboratory.
6* EPICS BASE Versions 3.13.7
7* and higher are distributed subject to a Software License Agreement found
8* in file LICENSE that is included with this distribution.
9\*************************************************************************/
10<HTML>
11<BODY>
12<PRE>
13<!-- Manpage converted by man2html 3.0.1 -->
14
15</PRE>
16<H2>NAME</H2><PRE>
17 dbLoadRecords, dbLoadTemplate - load ascii database records
18 into an IOC
19
20
21</PRE>
22<H2>SYNOPSIS</H2><PRE>
23 dbLoadRecords(char* db_file, char* substitutions)
24
25 dbLoadTemplate(char* template_file)
26
27
28</PRE>
29<H2>DESCRIPTION</H2><PRE>
30 These routines are available from IOC core on the vxWorks
31 command line. Both provide a way to load ascii ".db" files
32 (usually created by <B>gdct(1)</B> ) into the IOC. The ".db" files
33 contain ascii versions of record instances and are described
34 in more detail in <B>dbfile(5)</B>. In addition to loading the
35 ".db" ascii files into the IOC, both routines provide a
36 method of performing variable substitution on record names
37 and field values.
38
39 dbLoadRecords() reads the ".db" file <I>db</I>_<I>file</I> performing sub-
40 stitutions specified in string <I>substitutions</I>. The substitu-
41 tion must be a string specified as follows:
42
43 "var1=sub1,var2=sub3,..."
44
45 Variables are specified in the ".db" file as
46 $(variable_name). If the substitution string
47 "a=1,b=2,c=\"this is a test\"" were used, any variables
48 $(a), $(b), or $(c) would be substituted with the appropri-
49 ate data. See the EXAMPLES section for more details.
50
51 dbLoadTemplate() will read a <I>template</I>_<I>file</I>. The
52 <I>template</I>_<I>file</I> resides in the your IOC boot directory and
53 contains rules about loading ".db" files and performing sub-
54 stitutions. The template_file must be in the form used by
55 an IOC and is described in <B>templatefile(5)</B>. The EXAMPLES
56 section descibes how it can be used.
57
58
59</PRE>
60<H2>EXAMPLES</H2><PRE>
61 The next two examples of dbLoadRecords() and dbLoadTem-
62 plate() will use the following ".db" file named <I>test</I>.<I>db</I> :
63
64 database(test)
65 {
66 record(ai,"$(pre)testrec1")
67 record(ai,"$(pre)testrec2")
68 record(stringout,"$(pre)testrec3")
69 {
70 field(VAL,"$(STRING)")
71 field(SCAN,"$(SCAN)")
72 }
73 }
74 Running dbLoadRecords ("test.db","pre=TEST,STRING=\"this is
75 a test\",SCAN=Passive") will produce the following records
76 in the IOC's database:
77
78 TESTtestrec1
79 TESTtestrec2
80 TESTtestrec3
81
82 The third record will have VAL set to "this is a test" and
83 SCAN set to "Passive".
84
85 Running dbLoadTemplate ("test.template") with test.template
86 containing:
87 file test.db
88 {
89 {pre=TEST1, STRING = "this is a test two", SCAN="1 Second" }
90 {pre=TEST2, STRING = "this is a test one", SCAN=Passive }
91 {pre=TEST3, STRING = "this is a test three", SCAN=Passive }
92 }
93 will produce a total of nine records in the IOC's database:
94 TEST1testrec1
95 TEST1testrec2
96 TEST1testrec3 - (VAL="this is a test two", SCAN="1 Second")
97 TEST2testrec1
98 TEST2testrec2
99 TEST2testrec3 - (VAL="this is a test one", SCAN="Passive")
100 TEST3testrec1
101 TEST3testrec2
102 TEST3testrec3 - (VAL="this is a test three", SCAN="Passive")
103
104
105</PRE>
106<H2>NOTES</H2><PRE>
107 The binary file <I>default</I>.<I>dctsdr</I> must be loaded prior to run-
108 ning either of these routines. This file contains the rules
109 on how to construct records and change field values.
110
111 After the default.dctsdr file is loaded, these routines can
112 be run as many times as desired until iocInit is run.
113
114
115</PRE>
116<H2>SEE ALSO</H2><PRE>
117 <B>gdct(1)</B>, <B>templatefile(5)</B>, <B>dbfile(5)</B>
118
119
120
121
122
123
124
125
126
127
128
129
130</PRE>
131<HR>
132<ADDRESS>
133Man(1) output converted with
134<a href="http://www.oac.uci.edu/indiv/ehood/man2html.html">man2html</a>
135</ADDRESS>
136</BODY>
137</HTML>
1380
=== modified file 'src/ioc/dbtemplate/dbLoadTemplate.y'
--- src/ioc/dbtemplate/dbLoadTemplate.y 2009-05-05 15:19:00 +0000
+++ src/ioc/dbtemplate/dbLoadTemplate.y 2012-06-01 20:39:23 +0000
@@ -22,30 +22,32 @@
22#include "dbLoadTemplate.h"22#include "dbLoadTemplate.h"
2323
24static int line_num;24static int line_num;
25static int yyerror();25static int yyerror(char* str);
2626
27#define VAR_MAX_VAR_STRING 500027#define VAR_MAX_VAR_STRING 5000
28#define VAR_MAX_VARS 10028#define VAR_MAX_VARS 100
2929
30static char *sub_collect = NULL;30static char *sub_collect;
31static char *sub_locals;
31static char** vars = NULL;32static char** vars = NULL;
32static char* db_file_name = NULL;33static char* db_file_name = NULL;
33static int var_count,sub_count;34static int var_count, sub_count;
3435
35%}36%}
3637
37%start template38%start substitution_file
3839
39%token <Str> WORD QUOTE40%token <Str> WORD QUOTE
40%token DBFILE41%token DBFILE
41%token PATTERN42%token PATTERN
43%token GLOBAL
42%token EQUALS COMMA44%token EQUALS COMMA
43%left O_PAREN C_PAREN45%left O_PAREN C_PAREN
44%left O_BRACE C_BRACE46%left O_BRACE C_BRACE
4547
46%union48%union
47{49{
48 int Int;50 int Int;
49 char Char;51 char Char;
50 char *Str;52 char *Str;
51 double Real;53 double Real;
@@ -53,192 +55,242 @@
5355
54%%56%%
5557
56template: templs58substitution_file: global_or_template
57 | subst59 | substitution_file global_or_template
58 ;60 ;
5961
60templs: templs templ62global_or_template: global_definitions
61 | templ63 | template_substitutions
62 ;64 ;
6365
64templ: templ_head O_BRACE subst C_BRACE66global_definitions: GLOBAL O_BRACE C_BRACE
65 | templ_head67 | GLOBAL O_BRACE variable_definitions C_BRACE
66 {68 {
67 if(db_file_name)69 #ifdef ERROR_STUFF
68 dbLoadRecords(db_file_name,NULL);70 fprintf(stderr, "global_definitions: %s\n", sub_collect+1);
69 else71 #endif
70 fprintf(stderr,"Error: no db file name given\n");72 sub_locals += strlen(sub_locals);
71 }73 }
72 ;74 ;
7375
74templ_head: DBFILE WORD76template_substitutions: template_filename O_BRACE C_BRACE
75 {77 {
76 var_count=0;78 #ifdef ERROR_STUFF
77 if(db_file_name) dbmfFree(db_file_name);79 fprintf(stderr, "template_substitutions: %s unused\n", db_file_name);
78 db_file_name = dbmfMalloc(strlen($2)+1);80 #endif
79 strcpy(db_file_name,$2);81 dbmfFree(db_file_name);
80 dbmfFree($2);82 db_file_name = NULL;
81 }83 }
82 | DBFILE QUOTE84 | template_filename O_BRACE substitutions C_BRACE
83 {85 {
84 var_count=0;86 #ifdef ERROR_STUFF
85 if(db_file_name) dbmfFree(db_file_name);87 fprintf(stderr, "template_substitutions: %s finished\n", db_file_name);
86 db_file_name = dbmfMalloc(strlen($2)+1);88 #endif
87 strcpy(db_file_name,$2);89 dbmfFree(db_file_name);
88 dbmfFree($2);90 db_file_name = NULL;
89 }91 }
90 ;92 ;
9193
92subst: PATTERN pattern subs94template_filename: DBFILE WORD
93 | PATTERN pattern95 {
94 | var_subs96 #ifdef ERROR_STUFF
95 ;97 fprintf(stderr, "template_filename: %s\n", $2);
9698 #endif
97pattern: O_BRACE vars C_BRACE99 var_count = 0;
98 { 100 db_file_name = dbmfMalloc(strlen($2)+1);
99#ifdef ERROR_STUFF101 strcpy(db_file_name, $2);
100 int i;102 dbmfFree($2);
101 for(i=0;i<var_count;i++) fprintf(stderr,"variable=(%s)\n",vars[i]);103 }
102 fprintf(stderr,"var_count=%d\n",var_count);104 | DBFILE QUOTE
103#endif105 {
104 }106 #ifdef ERROR_STUFF
105 ;107 fprintf(stderr, "template_filename: \"%s\"\n", $2);
106108 #endif
107vars: vars var109 var_count = 0;
108 | vars COMMA var110 db_file_name = dbmfMalloc(strlen($2)+1);
109 | var111 strcpy(db_file_name, $2);
110 ;112 dbmfFree($2);
111113 }
112var: WORD114 ;
113 {115
114 vars[var_count] = dbmfMalloc(strlen($1)+1);116substitutions: pattern_substitutions
115 strcpy(vars[var_count],$1);117 | variable_substitutions
116 var_count++;118 ;
117 dbmfFree($1);119
118 }120pattern_substitutions: PATTERN O_BRACE C_BRACE
119 ;121 | PATTERN O_BRACE C_BRACE pattern_definitions
120122 | PATTERN O_BRACE pattern_names C_BRACE
121subs: subs sub123 | PATTERN O_BRACE pattern_names C_BRACE pattern_definitions
122 | sub124 ;
123 ;125
124126pattern_names: pattern_name
125sub: WORD O_BRACE vals C_BRACE127 | pattern_names COMMA
126 {128 | pattern_names pattern_name
127 sub_collect[strlen(sub_collect)-1]='\0';129 ;
128#ifdef ERROR_STUFF130
129 fprintf(stderr,"dbLoadRecords(%s)\n",sub_collect);131pattern_name: WORD
130#endif132 {
131 if(db_file_name)133 #ifdef ERROR_STUFF
132 dbLoadRecords(db_file_name,sub_collect);134 fprintf(stderr, "pattern_name: [%d] = %s\n", var_count, $1);
133 else135 #endif
134 fprintf(stderr,"Error: no db file name given\n");136 vars[var_count] = dbmfMalloc(strlen($1)+1);
135 dbmfFree($1);137 strcpy(vars[var_count], $1);
136 sub_collect[0]='\0';138 var_count++;
137 sub_count=0;139 dbmfFree($1);
138 }140 }
139 | O_BRACE vals C_BRACE141 ;
140 {142
141 sub_collect[strlen(sub_collect)-1]='\0';143pattern_definitions: pattern_definition
142#ifdef ERROR_STUFF144 | pattern_definitions pattern_definition
143 fprintf(stderr,"dbLoadRecords(%s)\n",sub_collect);145 ;
144#endif146
145 if(db_file_name)147pattern_definition: global_definitions
146 dbLoadRecords(db_file_name,sub_collect);148 | O_BRACE C_BRACE
147 else149 {
148 fprintf(stderr,"Error: no db file name given\n");150 #ifdef ERROR_STUFF
149 sub_collect[0]='\0';151 fprintf(stderr, "pattern_definition: pattern_values empty\n");
150 sub_count=0;152 fprintf(stderr, " dbLoadRecords(%s)\n", sub_collect+1);
151 }153 #endif
152 ;154 dbLoadRecords(db_file_name, sub_collect+1);
153155 }
154vals: vals val156 | O_BRACE pattern_values C_BRACE
155 | vals COMMA val157 {
156 | val158 #ifdef ERROR_STUFF
157 ;159 fprintf(stderr, "pattern_definition:\n");
158160 fprintf(stderr, " dbLoadRecords(%s)\n", sub_collect+1);
159val: QUOTE161 #endif
160 {162 dbLoadRecords(db_file_name, sub_collect+1);
161 if(sub_count<=var_count)163 *sub_locals = '\0';
162 {164 sub_count = 0;
163 strcat(sub_collect,vars[sub_count]);165 }
164 strcat(sub_collect,"=\"");166 | WORD O_BRACE pattern_values C_BRACE
165 strcat(sub_collect,$1);167 { /* DEPRECATED SYNTAX */
166 strcat(sub_collect,"\",");168 fprintf(stderr,
167 sub_count++;169 "dbLoadTemplate: Substitution file uses deprecated syntax.\n"
168 }170 " the string '%s' on line %d that comes just before the\n"
169 dbmfFree($1);171 " '{' character is extraneous and should be removed.\n",
170 }172 $1, line_num);
171 | WORD173 #ifdef ERROR_STUFF
172 {174 fprintf(stderr, "pattern_definition:\n");
173 if(sub_count<=var_count)175 fprintf(stderr, " dbLoadRecords(%s)\n", sub_collect+1);
174 {176 #endif
175 strcat(sub_collect,vars[sub_count]);177 dbLoadRecords(db_file_name, sub_collect+1);
176 strcat(sub_collect,"=");178 dbmfFree($1);
177 strcat(sub_collect,$1);179 *sub_locals = '\0';
178 strcat(sub_collect,",");180 sub_count = 0;
179 sub_count++;181 }
180 }182 ;
181 dbmfFree($1);183
182 }184pattern_values: pattern_value
183 ;185 | pattern_values COMMA
184186 | pattern_values pattern_value
185var_subs: var_subs var_sub187 ;
186 | var_sub188
187 ;189pattern_value: QUOTE
188190 {
189var_sub: WORD O_BRACE sub_pats C_BRACE191 #ifdef ERROR_STUFF
190 {192 fprintf(stderr, "pattern_value: [%d] = \"%s\"\n", sub_count, $1);
191 sub_collect[strlen(sub_collect)-1]='\0';193 #endif
192#ifdef ERROR_STUFF194 if (sub_count < var_count) {
193 fprintf(stderr,"dbLoadRecords(%s)\n",sub_collect);195 strcat(sub_locals, ",");
194#endif196 strcat(sub_locals, vars[sub_count]);
195 if(db_file_name)197 strcat(sub_locals, "=\"");
196 dbLoadRecords(db_file_name,sub_collect);198 strcat(sub_locals, $1);
197 else199 strcat(sub_locals, "\"");
198 fprintf(stderr,"Error: no db file name given\n");200 sub_count++;
199 dbmfFree($1);201 } else {
200 sub_collect[0]='\0';202 fprintf(stderr, "dbLoadTemplate: Too many values given, line %d.\n",
201 sub_count=0;203 line_num);
202 }204 }
203 | O_BRACE sub_pats C_BRACE205 dbmfFree($1);
204 {206 }
205 sub_collect[strlen(sub_collect)-1]='\0';207 | WORD
206#ifdef ERROR_STUFF208 {
207 fprintf(stderr,"dbLoadRecords(%s)\n",sub_collect);209 #ifdef ERROR_STUFF
208#endif210 fprintf(stderr, "pattern_value: [%d] = %s\n", sub_count, $1);
209 if(db_file_name)211 #endif
210 dbLoadRecords(db_file_name,sub_collect);212 if (sub_count < var_count) {
211 else213 strcat(sub_locals, ",");
212 fprintf(stderr,"Error: no db file name given\n");214 strcat(sub_locals, vars[sub_count]);
213 sub_collect[0]='\0';215 strcat(sub_locals, "=");
214 sub_count=0;216 strcat(sub_locals, $1);
215 }217 sub_count++;
216 ;218 } else {
217219 fprintf(stderr, "dbLoadTemplate: Too many values given, line %d.\n",
218sub_pats: sub_pats sub_pat220 line_num);
219 | sub_pats COMMA sub_pat221 }
220 | sub_pat222 dbmfFree($1);
221 ;223 }
222224 ;
223sub_pat: WORD EQUALS WORD225
224 {226variable_substitutions: variable_substitution
225 strcat(sub_collect,$1);227 | variable_substitutions variable_substitution
226 strcat(sub_collect,"=");228 ;
227 strcat(sub_collect,$3);229
228 strcat(sub_collect,",");230variable_substitution: global_definitions
229 dbmfFree($1); dbmfFree($3);231 | O_BRACE C_BRACE
230 sub_count++;232 {
231 }233 #ifdef ERROR_STUFF
232 | WORD EQUALS QUOTE234 fprintf(stderr, "variable_substitution: variable_definitions empty\n");
233 {235 fprintf(stderr, " dbLoadRecords(%s)\n", sub_collect+1);
234 strcat(sub_collect,$1);236 #endif
235 strcat(sub_collect,"=\"");237 dbLoadRecords(db_file_name, sub_collect+1);
236 strcat(sub_collect,$3);238 }
237 strcat(sub_collect,"\",");239 | O_BRACE variable_definitions C_BRACE
238 dbmfFree($1); dbmfFree($3);240 {
239 sub_count++;241 #ifdef ERROR_STUFF
240 }242 fprintf(stderr, "variable_substitution:\n");
241 ;243 fprintf(stderr, " dbLoadRecords(%s)\n", sub_collect+1);
244 #endif
245 dbLoadRecords(db_file_name, sub_collect+1);
246 *sub_locals = '\0';
247 }
248 | WORD O_BRACE variable_definitions C_BRACE
249 { /* DEPRECATED SYNTAX */
250 fprintf(stderr,
251 "dbLoadTemplate: Substitution file uses deprecated syntax.\n"
252 " the string '%s' on line %d that comes just before the\n"
253 " '{' character is extraneous and should be removed.\n",
254 $1, line_num);
255 #ifdef ERROR_STUFF
256 fprintf(stderr, "variable_substitution:\n");
257 fprintf(stderr, " dbLoadRecords(%s)\n", sub_collect+1);
258 #endif
259 dbLoadRecords(db_file_name, sub_collect+1);
260 dbmfFree($1);
261 *sub_locals = '\0';
262 }
263 ;
264
265variable_definitions: variable_definition
266 | variable_definitions COMMA
267 | variable_definitions variable_definition
268 ;
269
270variable_definition: WORD EQUALS WORD
271 {
272 #ifdef ERROR_STUFF
273 fprintf(stderr, "variable_definition: %s = %s\n", $1, $3);
274 #endif
275 strcat(sub_locals, ",");
276 strcat(sub_locals, $1);
277 strcat(sub_locals, "=");
278 strcat(sub_locals, $3);
279 dbmfFree($1); dbmfFree($3);
280 }
281 | WORD EQUALS QUOTE
282 {
283 #ifdef ERROR_STUFF
284 fprintf(stderr, "variable_definition: %s = \"%s\"\n", $1, $3);
285 #endif
286 strcat(sub_locals, ",");
287 strcat(sub_locals, $1);
288 strcat(sub_locals, "=\"");
289 strcat(sub_locals, $3);
290 strcat(sub_locals, "\"");
291 dbmfFree($1); dbmfFree($3);
292 }
293 ;
242294
243%%295%%
244 296
@@ -247,69 +299,73 @@
247static int yyerror(char* str)299static int yyerror(char* str)
248{300{
249 if (str)301 if (str)
250 fprintf(stderr, "Substitution file error: %s\n", str);302 fprintf(stderr, "Substitution file error: %s\n", str);
251 else303 else
252 fprintf(stderr, "Substitution file error.\n");304 fprintf(stderr, "Substitution file error.\n");
253 fprintf(stderr, "line %d: '%s'\n", line_num, yytext);305 fprintf(stderr, "line %d: '%s'\n", line_num, yytext);
254 return 0;306 return 0;
255}307}
256308
257static int is_not_inited = 1;309static int is_not_inited = 1;
258310
259int epicsShareAPI dbLoadTemplate(char* sub_file)311int epicsShareAPI dbLoadTemplate(const char *sub_file, const char *cmd_collect)
260{312{
261 FILE *fp;313 FILE *fp;
262 int ind;314 int i;
263315
264 line_num=1;316 line_num = 1;
265317
266 if( !sub_file || !*sub_file)318 if (!sub_file || !*sub_file) {
267 {319 fprintf(stderr, "must specify variable substitution file\n");
268 fprintf(stderr,"must specify variable substitution file\n");320 return -1;
269 return -1;321 }
270 }322
271323 fp = fopen(sub_file, "r");
272 if( !(fp=fopen(sub_file,"r")) )324 if (!fp) {
273 {325 fprintf(stderr, "dbLoadTemplate: error opening sub file %s\n", sub_file);
274 fprintf(stderr,"dbLoadTemplate: error opening sub file %s\n",sub_file);326 return -1;
275 return -1;327 }
276 }328
277329 vars = (char**)malloc(VAR_MAX_VARS * sizeof(char*));
278 vars = (char**)malloc(VAR_MAX_VARS * sizeof(char*));330 sub_collect = malloc(VAR_MAX_VAR_STRING);
279 sub_collect = malloc(VAR_MAX_VAR_STRING);331 if (!vars || !sub_collect) {
280 if (!vars || !sub_collect)332 free(vars);
281 {333 free(sub_collect);
282 free(vars);334 fclose(fp);
283 free(sub_collect);335 fprintf(stderr, "dbLoadTemplate: Out of memory!\n");
284 fclose(fp);336 return -1;
285 fprintf(stderr, "dbLoadTemplate: Out of memory!\n");337 }
286 return -1;338 strcpy(sub_collect, ",");
287 }339
288340 if (cmd_collect && *cmd_collect) {
289 sub_collect[0]='\0';341 strcat(sub_collect, cmd_collect);
290 var_count=0;342 sub_locals = sub_collect + strlen(sub_collect);
291 sub_count=0;343 } else {
292344 sub_locals = sub_collect;
293 if(is_not_inited)345 *sub_locals = '\0';
294 {346 }
295 yyin=fp;347 var_count = 0;
296 is_not_inited=0;348 sub_count = 0;
297 }349
298 else350 if (is_not_inited) {
299 {351 yyin = fp;
300 yyrestart(fp);352 is_not_inited = 0;
301 }353 } else {
302354 yyrestart(fp);
303 yyparse();355 }
304 for(ind=0;ind<var_count;ind++) dbmfFree(vars[ind]);356
305 free(vars);357 yyparse();
306 free(sub_collect);358
307 vars = NULL;359 for (i = 0; i < var_count; i++) {
308 fclose(fp);360 dbmfFree(vars[i]);
309 if(db_file_name){361 }
310 dbmfFree((void *)db_file_name);362 free(vars);
311 db_file_name = NULL;363 free(sub_collect);
312 }364 vars = NULL;
313 return 0;365 fclose(fp);
366 if (db_file_name) {
367 dbmfFree(db_file_name);
368 db_file_name = NULL;
369 }
370 return 0;
314}371}
315
316372
=== modified file 'src/ioc/dbtemplate/dbLoadTemplate_lex.l'
--- src/ioc/dbtemplate/dbLoadTemplate_lex.l 2006-11-17 22:18:35 +0000
+++ src/ioc/dbtemplate/dbLoadTemplate_lex.l 2012-06-01 20:39:23 +0000
@@ -18,39 +18,40 @@
1818
19%%19%%
2020
21"pattern" { return(PATTERN); }21"pattern" { return(PATTERN); }
22"file" { return(DBFILE); }22"file" { return(DBFILE); }
23"global" { return(GLOBAL); }
2324
24{doublequote}({dstringchar}|{escape})*{doublequote} |25{doublequote}({dstringchar}|{escape})*{doublequote} |
25{singlequote}({sstringchar}|{escape})*{singlequote} {26{singlequote}({sstringchar}|{escape})*{singlequote} {
26 yylval.Str = dbmfStrdup(yytext+1);27 yylval.Str = dbmfStrdup(yytext+1);
27 yylval.Str[strlen(yylval.Str)-1] = '\0';28 yylval.Str[strlen(yylval.Str)-1] = '\0';
28 return(QUOTE);29 return(QUOTE);
29 }30}
3031
31{bareword}+ {32{bareword}+ {
32 yylval.Str = dbmfStrdup(yytext);33 yylval.Str = dbmfStrdup(yytext);
33 return(WORD);34 return(WORD);
34 }35}
3536
36"=" { return(EQUALS); }37"=" { return(EQUALS); }
37"," { return(COMMA); }38"," { return(COMMA); }
38"{" { return(O_BRACE); }39"{" { return(O_BRACE); }
39"}" { return(C_BRACE); }40"}" { return(C_BRACE); }
4041
41{comment}.* ;42{comment}.* ;
42{whitespace} ;43{whitespace} ;
43{newline} { line_num++; }44{newline} { line_num++; }
4445
45. {46. {
46 char message[40];47 char message[40];
4748
48 sprintf(message,"invalid character '%c'", yytext[0]);49 sprintf(message, "invalid character '%c'", yytext[0]);
49 yyerror(message);50 yyerror(message);
5051
51 /* Suppress compiler warning messages */52 /* Suppress compiler warning messages */
52 if (0) yyunput('c',NULL);53 if (0) yyunput('c',NULL);
53 if (0) yy_switch_to_buffer(NULL);54 if (0) yy_switch_to_buffer(NULL);
54 }55}
5556
56%%57%%
5758
=== modified file 'src/ioc/dbtemplate/dbtoolsIocRegister.c'
--- src/ioc/dbtemplate/dbtoolsIocRegister.c 2007-03-13 17:54:23 +0000
+++ src/ioc/dbtemplate/dbtoolsIocRegister.c 2012-06-01 20:39:23 +0000
@@ -13,17 +13,20 @@
1313
1414
15/* dbLoadTemplate */15/* dbLoadTemplate */
16static const iocshArg dbLoadTemplateArg0 = { "file name",iocshArgString};16static const iocshArg dbLoadTemplateArg0 = {"filename", iocshArgString};
17static const iocshArg * const dbLoadTemplateArgs[1] = {&dbLoadTemplateArg0};17static const iocshArg dbLoadTemplateArg1 = {"var=value", iocshArgString};
18static const iocshArg * const dbLoadTemplateArgs[2] = {
19 &dbLoadTemplateArg0, &dbLoadTemplateArg1
20};
18static const iocshFuncDef dbLoadTemplateFuncDef =21static const iocshFuncDef dbLoadTemplateFuncDef =
19 {"dbLoadTemplate",1,dbLoadTemplateArgs};22 {"dbLoadTemplate", 2, dbLoadTemplateArgs};
20static void dbLoadTemplateCallFunc(const iocshArgBuf *args)23static void dbLoadTemplateCallFunc(const iocshArgBuf *args)
21{24{
22 dbLoadTemplate(args[0].sval);25 dbLoadTemplate(args[0].sval, args[1].sval);
23}26}
2427
2528
26void epicsShareAPI dbtoolsIocRegister(void)29void epicsShareAPI dbtoolsIocRegister(void)
27{30{
28 iocshRegister(&dbLoadTemplateFuncDef,dbLoadTemplateCallFunc);31 iocshRegister(&dbLoadTemplateFuncDef, dbLoadTemplateCallFunc);
29}32}
3033
=== added file 'src/ioc/dbtemplate/msi.c'
--- src/ioc/dbtemplate/msi.c 1970-01-01 00:00:00 +0000
+++ src/ioc/dbtemplate/msi.c 2012-06-01 20:39:23 +0000
@@ -0,0 +1,841 @@
1/*************************************************************************\
2* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne
3* National Laboratory.
4* Copyright (c) 2002 The Regents of the University of California, as
5* Operator of Los Alamos National Laboratory.
6* EPICS Base is distributed subject to a Software License Agreement found
7* in the file LICENSE that is included with this distribution.
8\*************************************************************************/
9
10/* msi - macro substitutions and include */
11
12#include <stdlib.h>
13#include <stddef.h>
14#include <stdio.h>
15#include <string.h>
16#include <ctype.h>
17#include <errno.h>
18
19#include <dbDefs.h>
20#include <macLib.h>
21#include <ellLib.h>
22#include <epicsString.h>
23#include <osiFileName.h>
24
25#define MAX_BUFFER_SIZE 4096
26
27/* Module to read the template files */
28typedef struct inputData inputData;
29
30static void inputConstruct(inputData **ppvt);
31static void inputDestruct(inputData *pvt);
32static void inputAddPath(inputData *pvt, char *pval);
33static void inputBegin(inputData *pvt, char *fileName);
34static char *inputNextLine(inputData *pvt);
35static void inputNewIncludeFile(inputData *pvt, char *name);
36static void inputErrPrint(inputData *pvt);
37
38/* Module to read the substitution file */
39typedef struct subInfo subInfo;
40
41static void substituteOpen(subInfo **ppvt, char *substitutionName);
42static void substituteDestruct(subInfo *pvt);
43static int substituteGetNextSet(subInfo *pvt, char **filename);
44static int substituteGetGlobalSet(subInfo *pvt);
45static char *substituteGetReplacements(subInfo *pvt);
46static char *substituteGetGlobalReplacements(subInfo *pvt);
47
48/* Forward references to local routines */
49static void usageExit(void);
50static void addMacroReplacements(MAC_HANDLE *macPvt, char *pval);
51static void makeSubstitutions(inputData *inputPvt, MAC_HANDLE *macPvt, char *templateName);
52
53/*Global variables */
54static int dontWarnUndef = 1;
55
56
057
58int main(int argc,char **argv)
59{
60 inputData *inputPvt;
61 MAC_HANDLE *macPvt;
62 char *pval;
63 int narg;
64 char *substitutionName=0;
65 char *templateName=0;
66 int i;
67 int localScope = 1;
68
69 inputConstruct(&inputPvt);
70 macCreateHandle(&macPvt,0);
71 macSuppressWarning(macPvt,1);
72 while((argc>1) && (argv[1][0] == '-')) {
73 narg = (strlen(argv[1])==2) ? 2 : 1;
74 pval = (narg==1) ? (argv[1]+2) : argv[2];
75 if(strncmp(argv[1],"-I",2)==0) {
76 inputAddPath(inputPvt,pval);
77 } else if(strncmp(argv[1],"-o",2)==0) {
78 if(freopen(pval,"w",stdout)==NULL) {
79 fprintf(stderr,"msi: Can't open %s for writing: %s\n",
80 pval, strerror(errno));
81 exit(1);
82 }
83 } else if(strncmp(argv[1],"-M",2)==0) {
84 addMacroReplacements(macPvt,pval);
85 } else if(strncmp(argv[1],"-S",2)==0) {
86 substitutionName = epicsStrDup(pval);
87 } else if(strncmp(argv[1],"-V",2)==0) {
88 macSuppressWarning(macPvt,0);
89 dontWarnUndef = 0;
90 narg = 1; /* no argument for this option */
91 } else if(strncmp(argv[1],"-g",2)==0) {
92 localScope = 0;
93 narg = 1; /* no argument for this option */
94 } else {
95 usageExit();
96 }
97 argc -= narg;
98 for(i=1; i<argc; i++) argv[i] = argv[i + narg];
99 }
100 if(argc>2) {
101 fprintf(stderr,"msi: Too many arguments\n");
102 usageExit();
103 }
104 if(argc==2) {
105 templateName = epicsStrDup(argv[1]);
106 }
107 if(!substitutionName) {
108 makeSubstitutions(inputPvt,macPvt,templateName);
109 } else {
110 subInfo *substitutePvt;
111 char *filename = 0;
112 int isGlobal, isFile;
113
114 substituteOpen(&substitutePvt,substitutionName);
115 do {
116 if ((isGlobal = substituteGetGlobalSet(substitutePvt))) {
117 pval = substituteGetGlobalReplacements(substitutePvt);
118 if(pval) {
119 addMacroReplacements(macPvt,pval);
120 }
121 } else if ((isFile = substituteGetNextSet(substitutePvt,&filename))) {
122 if(templateName) filename = templateName;
123 if(!filename) {
124 fprintf(stderr,"msi: No template file\n");
125 usageExit();
126 }
127 while((pval = substituteGetReplacements(substitutePvt))){
128 if (localScope) macPushScope(macPvt);
129 addMacroReplacements(macPvt,pval);
130 makeSubstitutions(inputPvt,macPvt,filename);
131 if (localScope) macPopScope(macPvt);
132 }
133 }
134 } while (isGlobal || isFile);
135 substituteDestruct(substitutePvt);
136 }
137 macDeleteHandle(macPvt);
138 inputDestruct(inputPvt);
139 free(templateName);
140 free(substitutionName);
141 return 0;
142}
143
1144
145void usageExit(void)
146{
147 fprintf(stderr,"usage: msi [options] [template]\n");
148 fprintf(stderr,"stdin is used if neither template nor substitution file is given\n");
149 fprintf(stderr,"options:\n");
150 fprintf(stderr," -V Verbose warnings\n");
151 fprintf(stderr," -g All macros have global scope\n");
152 fprintf(stderr," -o<FILE> Save output to <FILE>\n");
153 fprintf(stderr," -I<DIR> Add <DIR> to include file search path\n");
154 fprintf(stderr," -M<SUBST> Add <SUBST> to (global) macro definitions\n");
155 fprintf(stderr," (<SUBST> takes the form VAR=VALUE,...)\n");
156 fprintf(stderr," -S<FILE> Expand the substitutions in FILE\n");
157 exit(1);
158}
159
160static void addMacroReplacements(MAC_HANDLE *macPvt,char *pval)
161{
162 char **pairs;
163 long status;
164
165 status = macParseDefns(macPvt,pval,&pairs);
166 if(status==-1) {
167 fprintf(stderr,"msi: Error from macParseDefns\n");
168 usageExit();
169 }
170 if(status) {
171 status = macInstallMacros(macPvt,pairs);
172 if(!status) {
173 fprintf(stderr,"Error from macInstallMacros\n");
174 usageExit();
175 }
176 free(pairs);
177 }
178}
179
180typedef enum {cmdInclude,cmdSubstitute} cmdType;
181static const char *cmdNames[] = {"include","substitute"};
182
183static void makeSubstitutions(inputData *inputPvt, MAC_HANDLE *macPvt, char *templateName)
184{
185 char *input;
186 static char buffer[MAX_BUFFER_SIZE];
187 int n;
188
189 inputBegin(inputPvt,templateName);
190 while((input = inputNextLine(inputPvt))) {
191 int expand=1;
192 char *p;
193 char *command = 0;
194
195 p = input;
196 /*skip whitespace at beginning of line*/
197 while(*p && (isspace(*p))) ++p;
198 /*Look for i or s */
199 if(*p && (*p=='i' || *p=='s')) command = p;
200 if(command) {
201 char *pstart;
202 char *pend;
203 char *copy;
204 int cmdind=-1;
205 int i;
206
207 for(i=0; i< NELEMENTS(cmdNames); i++) {
208 if(strstr(command,cmdNames[i])) {
209 cmdind = i;
210 }
211 }
212 if(cmdind<0) goto endif;
213 p = command + strlen(cmdNames[cmdind]);
214 /*skip whitespace after command*/
215 while(*p && (isspace(*p))) ++p;
216 /*Next character must be quote*/
217 if((*p==0) || (*p!='"')) goto endif;
218 pstart = ++p;
219 /*Look for end quote*/
220 while(*p && (*p!='"')) {
221 /*allow escape for imbeded quote*/
222 if((*p=='\\') && *(p+1)=='"') {
223 p += 2; continue;
224 } else {
225 if(*p=='"') break;
226 }
227 ++p;
228 }
229 pend = p;
230 if(*p==0) goto endif;
231 /*skip quote and any trailing blanks*/
232 while(*++p==' ') ;
233 if(*p != '\n' && *p !=0) goto endif;
234 copy = calloc(pend-pstart+1,sizeof(char));
235 strncpy(copy,pstart,pend-pstart);
236 switch(cmdind) {
237 case cmdInclude:
238 inputNewIncludeFile(inputPvt,copy);
239 break;
240 case cmdSubstitute:
241 addMacroReplacements(macPvt,copy);
242 break;
243 default:
244 fprintf(stderr,"msi: Logic error in makeSubstitutions\n");
245 inputErrPrint(inputPvt);
246 exit(1);
247 }
248 free(copy);
249 expand = 0;
250 }
251endif:
252 if (expand) {
253 n = macExpandString(macPvt,input,buffer,MAX_BUFFER_SIZE-1);
254 fputs(buffer,stdout);
255 if (!dontWarnUndef && n<0) {
256 fprintf(stderr,"msi: Warning, undefined macros present\n");
257 dontWarnUndef++;
258 }
259 }
260 }
261}
262
2263
264typedef struct inputFile{
265 ELLNODE node;
266 char *filename;
267 FILE *fp;
268 int lineNum;
269}inputFile;
270
271typedef struct pathNode {
272 ELLNODE node;
273 char *directory;
274} pathNode;
275
276struct inputData {
277 ELLLIST inputFileList;
278 ELLLIST pathList;
279 char inputBuffer[MAX_BUFFER_SIZE];
280};
281
282static void inputOpenFile(inputData *pinputData,char *filename);
283static void inputCloseFile(inputData *pinputData);
284static void inputCloseAllFiles(inputData *pinputData);
285
286static void inputConstruct(inputData **ppvt)
287{
288 inputData *pinputData;
289
290 pinputData = calloc(1,sizeof(inputData));
291 ellInit(&pinputData->inputFileList);
292 ellInit(&pinputData->pathList);
293 *ppvt = pinputData;
294}
295
296static void inputDestruct(inputData *pinputData)
297{
298 pathNode *ppathNode;
299
300 inputCloseAllFiles(pinputData);
301 while((ppathNode = (pathNode *)ellFirst(&pinputData->pathList))) {
302 ellDelete(&pinputData->pathList,&ppathNode->node);
303 free(ppathNode->directory);
304 free(ppathNode);
305 }
306 free(pinputData);
307}
308
3309
310static void inputAddPath(inputData *pinputData, char *path)
311{
312 ELLLIST *ppathList = &pinputData->pathList;
313 pathNode *ppathNode;
314 const char *pcolon;
315 const char *pdir;
316 int len;
317 int emptyName;
318 const char sep = *OSI_PATH_LIST_SEPARATOR;
319
320 pdir = path;
321 /*an empty name at beginning, middle, or end means current directory*/
322 while(pdir && *pdir) {
323 emptyName = ((*pdir == sep) ? 1 : 0);
324 if(emptyName) ++pdir;
325 ppathNode = (pathNode *)calloc(1,sizeof(pathNode));
326 ellAdd(ppathList,&ppathNode->node);
327 if(!emptyName) {
328 pcolon = strchr(pdir,sep);
329 len = (pcolon ? (pcolon - pdir) : strlen(pdir));
330 if(len>0) {
331 ppathNode->directory = (char *)calloc(len+1,sizeof(char));
332 strncpy(ppathNode->directory,pdir,len);
333 pdir = pcolon;
334 /*unless at end skip past first colon*/
335 if(pdir && *(pdir+1)!=0) ++pdir;
336 } else { /*must have been trailing : */
337 emptyName=1;
338 }
339 }
340 if(emptyName) {
341 ppathNode->directory = (char *)calloc(2,sizeof(char));
342 strcpy(ppathNode->directory,".");
343 }
344 }
345 return;
346}
347
4348
349static void inputBegin(inputData *pinputData, char *fileName)
350{
351 inputCloseAllFiles(pinputData);
352 inputOpenFile(pinputData,fileName);
353}
354
355static char *inputNextLine(inputData *pinputData)
356{
357 inputFile *pinputFile;
358 char *pline;
359
360 while((pinputFile = (inputFile *)ellFirst(&pinputData->inputFileList))) {
361 pline = fgets(pinputData->inputBuffer,MAX_BUFFER_SIZE,pinputFile->fp);
362 if(pline) {
363 ++pinputFile->lineNum;
364 return(pline);
365 }
366 inputCloseFile(pinputData);
367 }
368 return(0);
369}
370
371static void inputNewIncludeFile(inputData *pinputData, char *name)
372{
373 inputOpenFile(pinputData,name);
374}
375
376static void inputErrPrint(inputData *pinputData)
377{
378 inputFile *pinputFile;
379
380 fprintf(stderr,"input: '%s' at ",pinputData->inputBuffer);
381 pinputFile = (inputFile *)ellFirst(&pinputData->inputFileList);
382 while(pinputFile) {
383 fprintf(stderr,"line %d of ",pinputFile->lineNum);
384 if(pinputFile->filename) {
385 fprintf(stderr," file %s\n",pinputFile->filename);
386 } else {
387 fprintf(stderr,"stdin:\n");
388 }
389 pinputFile = (inputFile *)ellNext(&pinputFile->node);
390 if(pinputFile) {
391 fprintf(stderr," included from ");
392 } else {
393 fprintf(stderr,"\n");
394 }
395 }
396 fprintf(stderr,"\n");
397}
398
5399
400static void inputOpenFile(inputData *pinputData,char *filename)
401{
402 ELLLIST *ppathList = &pinputData->pathList;
403 pathNode *ppathNode = 0;
404 inputFile *pinputFile;
405 char *fullname = 0;
406 FILE *fp = 0;
407
408 if(!filename) {
409 fp = stdin;
410 } else if((ellCount(ppathList)==0) || strchr(filename,'/')){
411 fp = fopen(filename,"r");
412 } else {
413 ppathNode = (pathNode *)ellFirst(ppathList);
414 while(ppathNode) {
415 fullname = calloc(strlen(filename)+strlen(ppathNode->directory) +2,
416 sizeof(char));
417 strcpy(fullname,ppathNode->directory);
418 strcat(fullname,"/");
419 strcat(fullname,filename);
420 fp = fopen(fullname,"r");
421 if(fp) break;
422 free(fullname);
423 ppathNode = (pathNode *)ellNext(&ppathNode->node);
424 }
425 }
426 if(!fp) {
427 fprintf(stderr,"msi: Can't open file '%s'\n",filename);
428 inputErrPrint(pinputData);
429 exit(1);
430 }
431 pinputFile = calloc(1,sizeof(inputFile));
432 if(ppathNode) {
433 pinputFile->filename = fullname;
434 } else if(filename) {
435 pinputFile->filename = epicsStrDup(filename);
436 } else {
437 pinputFile->filename = epicsStrDup("stdin");
438 }
439 pinputFile->fp = fp;
440 ellInsert(&pinputData->inputFileList,0,&pinputFile->node);
441}
442
443static void inputCloseFile(inputData *pinputData)
444{
445 inputFile *pinputFile;
446
447 pinputFile = (inputFile *)ellFirst(&pinputData->inputFileList);
448 if(!pinputFile) return;
449 ellDelete(&pinputData->inputFileList,&pinputFile->node);
450 if(fclose(pinputFile->fp))
451 fprintf(stderr,"msi: Can't close input file '%s'\n",pinputFile->filename);
452 free(pinputFile->filename);
453 free(pinputFile);
454}
455
456static void inputCloseAllFiles(inputData *pinputData)
457{
458 inputFile *pinputFile;
459
460 while((pinputFile=(inputFile *)ellFirst(&pinputData->inputFileList))){
461 inputCloseFile(pinputData);
462 }
463}
464
6465
466/*start of code that handles substitution file*/
467typedef enum {
468 tokenLBrace,tokenRBrace,tokenSeparater,tokenString,tokenEOF
469}tokenType;
470
471typedef struct subFile {
472 char *substitutionName;
473 FILE *fp;
474 int lineNum;
475 char inputBuffer[MAX_BUFFER_SIZE];
476 char *pnextChar;
477 tokenType token;
478 char string[MAX_BUFFER_SIZE];
479} subFile;
480
481typedef struct patternNode {
482 ELLNODE node;
483 char *var;
484} patternNode;
485
486struct subInfo {
487 subFile *psubFile;
488 int isFile;
489 char *filename;
490 int isPattern;
491 ELLLIST patternList;
492 size_t size;
493 size_t curLength;
494 char *macroReplacements;
495};
496
497static char *subGetNextLine(subFile *psubFile);
498static tokenType subGetNextToken(subFile *psubFile);
499static void subFileErrPrint(subFile *psubFile,char * message);
500static void freeSubFile(subInfo *psubInfo);
501static void freePattern(subInfo *psubInfo);
502static void catMacroReplacements(subInfo *psubInfo,const char *value);
503
504void freeSubFile(subInfo *psubInfo)
505{
506 subFile *psubFile = psubInfo->psubFile;
507 if(psubFile->fp) {
508 if(fclose(psubFile->fp))
509 fprintf(stderr,"msi: Can't close substitution file\n");
510 }
511 free(psubFile);
512 free(psubInfo->filename);
513 psubInfo->psubFile = 0;
514}
515
516void freePattern(subInfo *psubInfo)
517{
518 patternNode *ppatternNode;
519 while((ppatternNode = (patternNode *)ellFirst(&psubInfo->patternList))) {
520 ellDelete(&psubInfo->patternList,&ppatternNode->node);
521 free(ppatternNode->var);
522 free(ppatternNode);
523 }
524 psubInfo->isPattern = 0;
525}
526
7527
528static void substituteDestruct(subInfo *psubInfo)
529{
530 freeSubFile(psubInfo);
531 freePattern(psubInfo);
532 free(psubInfo);
533 return;
534}
535
536static void substituteOpen(subInfo **ppvt,char *substitutionName)
537{
538 subInfo *psubInfo;
539 subFile *psubFile;
540 FILE *fp;
541
542 psubInfo = calloc(1,sizeof(subInfo));
543 *ppvt = psubInfo;
544 psubFile = calloc(1,sizeof(subFile));
545 psubInfo->psubFile = psubFile;
546 ellInit(&psubInfo->patternList);
547 fp = fopen(substitutionName,"r");
548 if(!fp) {
549 fprintf(stderr,"msi: Can't open file '%s'\n",substitutionName);
550 exit(1);
551 }
552 psubFile->substitutionName = substitutionName;
553 psubFile->fp = fp;
554 psubFile->lineNum = 1;
555 psubFile->inputBuffer[0] = 0;
556 psubFile->pnextChar = &psubFile->inputBuffer[0];
557 subGetNextToken(psubFile);
558 return;
559}
560
561static int substituteGetGlobalSet(subInfo *psubInfo)
562{
563 subFile *psubFile = psubInfo->psubFile;
564
565 while(psubFile->token==tokenSeparater) subGetNextToken(psubFile);
566 if(psubFile->token==tokenString && strcmp(psubFile->string,"global")==0) {
567 subGetNextToken(psubFile);
568 return(1);
569 }
570 return(0);
571}
572
573static int substituteGetNextSet(subInfo *psubInfo,char **filename)
574{
575 subFile *psubFile = psubInfo->psubFile;
576 patternNode *ppatternNode;
577
578 *filename = 0;
579 while(psubFile->token==tokenSeparater) subGetNextToken(psubFile);
580 if(psubFile->token==tokenEOF) return(0);
581 if(psubFile->token==tokenString && strcmp(psubFile->string,"file")==0) {
582 psubInfo->isFile = 1;
583 if(subGetNextToken(psubFile)!=tokenString) {
584 subFileErrPrint(psubFile,"Parse error, expecting filename");
585 exit(1);
586 }
587 freePattern(psubInfo);
588 free(psubInfo->filename);
589 if(psubFile->string[0]=='"'&&psubFile->string[strlen(psubFile->string)-1]=='"') {
590 psubFile->string[strlen(psubFile->string)-1]='\0';
591 psubInfo->filename = macEnvExpand(psubFile->string+1);
592 }
593 else {
594 psubInfo->filename = macEnvExpand(psubFile->string);
595 }
596 while(subGetNextToken(psubFile)==tokenSeparater);
597 if(psubFile->token!=tokenLBrace) {
598 subFileErrPrint(psubFile,"Parse error, expecting {");
599 exit(1);
600 }
601 subGetNextToken(psubFile);
602 }
603 *filename = psubInfo->filename;
604 while(psubFile->token==tokenSeparater) subGetNextToken(psubFile);
605 if(psubFile->token==tokenLBrace) return(1);
606 if(psubFile->token==tokenRBrace) return(1);
607 if(psubFile->token!=tokenString
608 || strcmp(psubFile->string,"pattern")!=0) {
609 subFileErrPrint(psubFile,"Parse error, expecting pattern");
610 exit(1);
611 }
612 freePattern(psubInfo);
613 psubInfo->isPattern = 1;
614 while(subGetNextToken(psubFile)==tokenSeparater);
615 if(psubFile->token!=tokenLBrace) {
616 subFileErrPrint(psubFile,"Parse error, expecting {");
617 exit(1);
618 }
619 while(1) {
620 while(subGetNextToken(psubFile)==tokenSeparater);
621 if(psubFile->token!=tokenString) break;
622 ppatternNode = calloc(1,sizeof(patternNode));
623 ellAdd(&psubInfo->patternList,&ppatternNode->node);
624 ppatternNode->var = epicsStrDup(psubFile->string);
625 }
626 if(psubFile->token!=tokenRBrace) {
627 subFileErrPrint(psubFile,"Parse error, expecting }");
628 exit(1);
629 }
630 subGetNextToken(psubFile);
631 return(1);
632}
633
8634
635static char *substituteGetGlobalReplacements(subInfo *psubInfo)
636{
637 subFile *psubFile = psubInfo->psubFile;
638
639 if(psubInfo->macroReplacements) psubInfo->macroReplacements[0] = 0;
640 psubInfo->curLength = 0;
641 while(psubFile->token==tokenSeparater) subGetNextToken(psubFile);
642 if(psubFile->token==tokenRBrace && psubInfo->isFile) {
643 psubInfo->isFile = 0;
644 free(psubInfo->filename);
645 psubInfo->filename = 0;
646 freePattern(psubInfo);
647 subGetNextToken(psubFile);
648 return(0);
649 }
650 if(psubFile->token==tokenEOF) return(0);
651 if(psubFile->token!=tokenLBrace) return(0);
652 while(1) {
653 switch(subGetNextToken(psubFile)) {
654 case tokenRBrace:
655 subGetNextToken(psubFile);
656 if (!psubInfo->macroReplacements) {
657 catMacroReplacements(psubInfo,"");
658 }
659 return(psubInfo->macroReplacements);
660 case tokenSeparater:
661 catMacroReplacements(psubInfo,",");
662 break;
663 case tokenString:
664 catMacroReplacements(psubInfo,psubFile->string);
665 break;
666 default:
667 subFileErrPrint(psubFile,"Parse error, illegal token");
668 exit(1);
669 }
670 }
671}
672
673static char *substituteGetReplacements(subInfo *psubInfo)
674{
675 subFile *psubFile = psubInfo->psubFile;
676 patternNode *ppatternNode;
677
678 if(psubInfo->macroReplacements) psubInfo->macroReplacements[0] = 0;
679 psubInfo->curLength = 0;
680 while(psubFile->token==tokenSeparater) subGetNextToken(psubFile);
681 if(psubFile->token==tokenRBrace && psubInfo->isFile) {
682 psubInfo->isFile = 0;
683 free(psubInfo->filename);
684 psubInfo->filename = 0;
685 freePattern(psubInfo);
686 subGetNextToken(psubFile);
687 return(0);
688 }
689 if(psubFile->token==tokenEOF) return(0);
690 if(psubFile->token!=tokenLBrace) return(0);
691 if(psubInfo->isPattern) {
692 int gotFirstPattern = 0;
693
694 while(subGetNextToken(psubFile)==tokenSeparater);
695 ppatternNode = (patternNode *)ellFirst(&psubInfo->patternList);
696 while(1) {
697 if(psubFile->token==tokenRBrace) {
698 subGetNextToken(psubFile);
699 return(psubInfo->macroReplacements);
700 }
701 if(psubFile->token!=tokenString) {
702 subFileErrPrint(psubFile,"Parse error, illegal token");
703 exit(-1);
704 }
705 if(gotFirstPattern) catMacroReplacements(psubInfo,",");
706 gotFirstPattern = 1;
707 if(ppatternNode) {
708 catMacroReplacements(psubInfo,ppatternNode->var);
709 catMacroReplacements(psubInfo,"=");
710 catMacroReplacements(psubInfo,psubFile->string);
711 ppatternNode = (patternNode *)ellNext(&ppatternNode->node);
712 } else {
713 subFileErrPrint(psubFile,"Warning, too many values given");
714 }
715 while(subGetNextToken(psubFile)==tokenSeparater);
716 }
717 } else while(1) {
718 switch(subGetNextToken(psubFile)) {
719 case tokenRBrace:
720 subGetNextToken(psubFile);
721 if (!psubInfo->macroReplacements) {
722 catMacroReplacements(psubInfo,"");
723 }
724 return(psubInfo->macroReplacements);
725 case tokenSeparater:
726 catMacroReplacements(psubInfo,",");
727 break;
728 case tokenString:
729 catMacroReplacements(psubInfo,psubFile->string);
730 break;
731 default:
732 subFileErrPrint(psubFile,"Parse error, illegal token");
733 exit(1);
734 }
735 }
736}
737
9738
739static char *subGetNextLine(subFile *psubFile)
740{
741 char *pline;
742
743 do {
744 pline = fgets(psubFile->inputBuffer,MAX_BUFFER_SIZE,psubFile->fp);
745 ++psubFile->lineNum;
746 } while(pline && psubFile->inputBuffer[0]=='#');
747 if(!pline) {
748 psubFile->token = tokenEOF;
749 psubFile->inputBuffer[0] = 0;
750 psubFile->pnextChar = 0;
751 return(0);
752 }
753 psubFile->pnextChar = &psubFile->inputBuffer[0];
754 return(&psubFile->inputBuffer[0]);
755}
756
757static void subFileErrPrint(subFile *psubFile,char * message)
758{
759 fprintf(stderr,"msi: %s\n",message);
760 fprintf(stderr," in substitution file '%s' at line %d:\n %s",
761 psubFile->substitutionName,
762 psubFile->lineNum,psubFile->inputBuffer);
763}
764
765
10766
767static tokenType subGetNextToken(subFile *psubFile)
768{
769 char *p;
770 char *pto;
771
772 p = psubFile->pnextChar;
773 if(!p) { psubFile->token = tokenEOF; return(tokenEOF);}
774 if(*p==0 || *p=='\n' || *p=='#') {
775 p = subGetNextLine(psubFile);
776 if(!p) { psubFile->token = tokenEOF; return(tokenEOF);}
777 else { psubFile->token = tokenSeparater; return(tokenSeparater);}
778 }
779 while(isspace(*p)) p++;
780 if(*p=='{') {
781 psubFile->token = tokenLBrace;
782 psubFile->pnextChar = ++p;
783 return(tokenLBrace);
784 }
785 if(*p=='}') {
786 psubFile->token = tokenRBrace;
787 psubFile->pnextChar = ++p;
788 return(tokenRBrace);
789 }
790 if(*p==0 || isspace(*p) || *p==',') {
791 while(isspace(*p) || *p==',') p++;
792 psubFile->token = tokenSeparater;
793 psubFile->pnextChar = p;
794 return(tokenSeparater);
795 }
796 /*now handle quoted strings*/
797 if(*p=='"') {
798 pto = &psubFile->string[0];
799 *pto++ = *p++;
800 while(*p!='"') {
801 if(*p==0 || *p=='\n') {
802 subFileErrPrint(psubFile,"Strings must be on single line\n");
803 exit(1);
804 }
805 /*allow escape for imbeded quote*/
806 if((*p=='\\') && *(p+1)=='"') {
807 *pto++ = *p++;
808 *pto++ = *p++;
809 continue;
810 }
811 *pto++ = *p++;
812 }
813 *pto++ = *p++;
814 psubFile->pnextChar = p;
815 *pto = 0;
816 psubFile->token = tokenString;
817 return(tokenString);
818 }
819 /*Now take anything up to next non String token and not space*/
820 pto = &psubFile->string[0];
821 while(!isspace(*p) && (strspn(p,"\",{}")==0)) *pto++ = *p++;
822 *pto = 0;
823 psubFile->pnextChar = p;
824 psubFile->token = tokenString;
825 return(tokenString);
826}
827
828static void catMacroReplacements(subInfo *psubInfo,const char *value)
829{
830 size_t len = strlen(value);
831
832 if(psubInfo->size <= (psubInfo->curLength + len)) {
833 size_t newsize = psubInfo->size + MAX_BUFFER_SIZE;
834 char *newbuf;
835
836 if(newsize <= psubInfo->curLength + len)
837 newsize = psubInfo->curLength + len + 1;
838 newbuf = calloc(1,newsize);
839 if(!newbuf) {
840 fprintf(stderr,"calloc failed for size %Zu\n",newsize);
841 exit(1);
842 }
843 if(psubInfo->macroReplacements) {
844 memcpy(newbuf,psubInfo->macroReplacements,psubInfo->curLength);
845 free(psubInfo->macroReplacements);
846 }
847 psubInfo->size = newsize;
848 psubInfo->macroReplacements = newbuf;
849 }
850 strcat(psubInfo->macroReplacements,value);
851 psubInfo->curLength += len;
852}
11853
=== added file 'src/ioc/dbtemplate/msi.html'
--- src/ioc/dbtemplate/msi.html 1970-01-01 00:00:00 +0000
+++ src/ioc/dbtemplate/msi.html 2012-06-01 20:39:23 +0000
@@ -0,0 +1,438 @@
1<!DOCTYPE html public "-//w3c//dtd html 4.0 transitional//en">
2<html>
3<head>
4 <title></title>
5 <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
6</head>
7
8<body>
9
10<h1>msi: Macro Substitution and Include Tool</h1>
11
12<h2>Introduction</h2>
13
14<p>msi is a general purpose macro substitution/include tool. It accepts as input
15an ascii template file. It looks for lines containing two reserved command
16names: <tt>include</tt> and <tt>substitute</tt>. It also looks for and performs
17substitutions on macros of the form $(var) and ${var}. It uses the macLib
18routines from EPICS Base to perform the substitutions, so it also accepts the
19default value and value definition syntax that macLib implements.</p>
20
21<p>msi also allows substitutions to be specified via a separate substitution
22file. This substitution file allows the same format as the substitution files
23accepted by the EPICS IOC's dbLoadTemplate command.</p>
24
25<h2>Command Syntax:</h2>
26
27<pre>msi -V -g -o<i>outfile</i> -I<i>dir</i> -M<i>subs</i> -S<i>subfile</i> <i>template</i></pre>
28
29<p>All parameters are optional. The -o, -I, -M, and -S switches may be
30separated from their associated value string by spaces if desired. Output will
31be written to stdout unless the -o option is given.</p>
32
33<p>Switches have the following meanings:</p>
34
35<dl>
36 <dt><tt>-V</tt></dt>
37 <dd>Verbose warnings; if this parameter is specified then any undefined
38 macro discovered in the template file which does not have an associated
39 default value is considered an error. An error message is generated, and
40 when msi terminates it will do so with an exit status of 2.</dd>
41
42 <dt><tt>-g</tt></dt>
43 <dd>When this flag is given all macros defined in a substitution file will
44 have global scope and thus their values will persist until a new value is
45 given for this macro. This flag is provided for backwards compatibility as
46 this was the behavior of previous versions of msi, but it does not follow
47 common scoping rules and is discouraged.</dd>
48
49 <dt><tt>-o</tt> <i>file</i></dt>
50 <dd>Output will be written to the specifed file rather than to the standard
51 output.</dd>
52
53 <dt><tt>-I</tt> <i>dir</i></dt>
54 <dd>This parameter, which may be repeated or contain a colon-separated (or
55 semi-colon separated on Windows) list of directory paths, specifies a search
56 path for include commands. For example:
57
58 <blockquote>
59 <pre>msi -I /home/mrk/examples:. -I.. template</pre>
60 </blockquote>
61
62 specifies that all named files should be searched for in the following
63 locations in the order given:
64
65 <ol>
66 <li><tt>/home/mrk/examples</tt></li>
67 <li><tt>.</tt> (the current directory)</li>
68 <li><tt>..</tt> (the parent of the current directory)</li>
69 </ol>
70 </dd>
71
72 <dt><tt>-M</tt> <i>substitutions</i></dt>
73 <dd>This parameter specifies macro values for the template instance.
74 Multiple macro values can be specified in one substitution parameter, or in
75 multiple <tt>-M</tt> parameters. For example:
76
77 <blockquote>
78 <pre>msi -M "a=aval,b=bval" -Mc=cval template</pre>
79 </blockquote>
80
81 specifies that in the template file each occurrence of:
82
83 <dl>
84 <dd><tt>$(a)</tt> or <tt>${a}</tt> is replaced by <tt>aval</tt></dd>
85 <dd><tt>$(b)</tt> or <tt>${b}</tt> is replaced by <tt>bval</tt></dd>
86 <dd><tt>$(c)</tt> or <tt>${c}</tt> is replaced by <tt>cval</tt></dd>
87 </dl>
88 </dd>
89
90 <dt><tt>-S</tt> <i>subfile</i></dt>
91 <dd>The substitution file. See below for format.</dd>
92
93 <dt><i>template</i></dt>
94 <dd> The input file. If no file is specified then input is taken from
95 stdin, i.e. msi can be used as a filter. See below for a description of
96 commands that can be embedded in the template file.</dd>
97</dl>
98
99<p>It is not possible to display usage by just typing <tt>msi</tt> since
100executing the command with no arguments is a valid command. To show usage
101specify an illegal switch, e.g.</p>
102
103<blockquote>
104<pre>msi -help</pre>
105</blockquote>
106
107<h2>Exit Status</h2>
108
109<dl>
110 <dt>0<dd>Success.
111 <dt>1<dd>Can't open/create file, or other I/O error.
112 <dt>2<dd>Undefined macros encountered with the <tt>-V</tt> option specified.
113</dl>
114
115<h2>Template File Format</h2>
116
117<p>This file contains the text to be read and written to the output after macro
118substitution is performed. If no file is given then input is read from stdin.
119Variable instances to be substituted by macro values are expressed in the
120template using the syntax <tt>$(</tt><i>name</i><tt>)</tt> or
121<tt>${</tt><i>name</i><tt>}</tt>. The template can also provide default values
122to be used when a macro has not been given a value, using the syntax
123<tt>$(</tt><i>name</i><tt>=</tt><i>default</i><tt>)</tt> or
124<tt>${</tt><i>name</i><tt>=</tt><i>default</i><tt>}</tt>.</p>
125
126<p>For example, using the command</p>
127
128<blockquote>
129<pre>msi -M name=Marty template</pre>
130</blockquote>
131
132<p>where the file template contains</p>
133
134<blockquote>
135<pre>My name is $(name)
136My age is $(age=none of your business)</pre>
137</blockquote>
138
139<p>results in this output:</p>
140
141<blockquote>
142<pre>My name is Marty
143My age is none of your business</pre>
144</blockquote>
145
146<p>Macro variables and their default values can be expressed in terms of other
147macros if necessary, to almost any level of complexity. Recursive definitions
148will generate warning messages on stderr and result in undefined output.</p>
149
150<p>The template file is read and processed one line at a time, where the
151maximum length of a line before and/or after macro expansion is 1023 characters
152&mdash; longer input or output lines will cause msi to fail. Within the context
153of a single line, macro expansion does not occur when the variable instance
154appears inside a single-quoted string, or where the dollar sign <tt>$</tt> is
155preceded by a back-slash character <tt>\</tt>, but as with the standard Unix
156shells, variables inside double quoted strings are expanded properly.</p>
157
158<p>However neither back-slash characters nor quotes of either variety are
159removed when generating the output file, so depending on what is being output
160the single quote behaviour may not be useful and may even be a hinderance. It
161cannot be disabled in the current version of msi.</p>
162
163<h3>Template file commands</h3>
164
165<p>In addition to the regular text and variable instances described above, the
166template file may also contain commands which allow the insertion of other
167template files and the ability to set macro values inside the template file
168itself. These commands are:</p>
169
170<blockquote>
171<pre>include "file"
172substitute "var=value,var=value,..."</pre>
173</blockquote>
174
175<p>Lines containing commands must be in one of these forms:</p>
176
177<ul>
178 <li><tt>include "</tt><i>filename</i><tt>"</tt></li>
179 <li><tt>substitute "</tt><i>name1=value1, name2=value2, ...</i><tt>"</tt></li>
180</ul>
181
182<p>White space is allowed before and after the command verb, and after the
183quoted string. If embedded quotes are needed, the backslash character
184<tt>\</tt> can be used as an escape character. For example</p>
185
186<blockquote>
187<pre>substitute "a=\"val\""</pre>
188</blockquote>
189
190<p>specifies that (unless <tt>a</tt> is subsequently redefined) wherever a
191<tt>$(a)</tt> macro appears in the template below this point, the text
192<tt>"val"</tt> (including the double quote characters) will appear in the
193output instead.</p>
194
195<p>If a line does match either syntax above it is just passed to macLib for
196processing without any notification. Thus the input line:</p>
197
198<blockquote>
199<pre>include "myfile" #include file</pre>
200</blockquote>
201
202<p>would just be passed to macLib, i.e. it would <em>not</em> be considered an
203include command.</p>
204
205<p>As an example of these commands, let the Unix command be:</p>
206
207<blockquote>
208<pre>msi template</pre>
209</blockquote>
210
211<p>and file includeFile contain:</p>
212
213<blockquote>
214<pre>first name is ${first}
215family name is ${family}</pre>
216</blockquote>
217
218<p>and template is</p>
219
220<blockquote>
221<pre>substitute "first=Marty,family=Kraimer"
222include "includeFile"
223substitute "first=Irma,family=Kraimer"
224include "includeFile"</pre>
225</blockquote>
226
227<p>then the following is written to the output.</p>
228
229<blockquote>
230<pre>first name is Marty
231family name is Kraimer
232first name is Irma
233family name is Kraimer</pre>
234</blockquote>
235
236<p>Note that the IOC's <tt>dbLoadTemplate</tt> command does not support the
237<tt>substitute</tt> syntax in template files, although the <tt>include</tt>
238syntax is supported.</p>
239
240<h2>Substitution File Format</h2>
241
242<p>The optional substitution file has three formats: regular, pattern, and
243dbTemplate format. We will discuss each separately.</p>
244
245<h3>Regular format</h3>
246
247<blockquote>
248<pre>global {gbl_var1=gbl_val1, gbl_var2=gbl_val2, ...}
249{var1=set1_val1, var2=set1_val2, ...}
250{var2=set2_val2, var1=set2_val1, ...}
251global {gbl_var1=gbl_val3, gbl_var2=gbl_val4, ...}
252{var1=set3_val1, var2=set3_val2, ...}
253{var2=set4_val2, var1=set4_val1, ...}</pre>
254</blockquote>
255
256<p>The template file is output with macro substitutions performed once for each
257set of braces containing macro replacement values.</p>
258
259<h3>Pattern format</h3>
260
261<blockquote>
262<pre>global {gbl_var1=gbl_val1, gbl_var2=gbl_val2, ...}
263pattern {var1, var2, ...}
264{set1_val1, set1_val2, ...}
265{set2_val1, set2_val2, ...}
266pattern {var2, var1, ...}
267global {gbl_var1=gbl_val3, gbl_var2=gbl_val4, ...}
268{set3_val2, set3_val1, ...}
269{set4_val2, set4_val2, ...}</pre>
270</blockquote>
271
272<p>This produces the same result as the regular format example above.</p>
273
274<h3>dbLoadTemplate Format</h3>
275
276<p>This format is an extension of the format accepted by the EPICS IOC command
277<tt>dbLoadTemplate</tt>, and allows templates to be expanded on the host rather
278by using dbLoadTemplate at IOC boot time.</p>
279
280<blockquote>
281<pre>global {gbl_var1=gbl_val1, gbl_var2=gbl_val2, ...}
282file templatefile {
283 <i>pattern format or regular format</i>
284}
285file "${WHERE}/template2" {
286 <i>pattern format or regular format</i>
287}</pre>
288</blockquote>
289
290<p>For the dbTemplate format, the template filename does not have to be given
291on the command line, and is usually specified in the substitutions file
292instead. If a template filename is given on the command line it will override
293the filenames listed in the substitutions files.</p>
294
295<h3>Syntax for all formats</h3>
296
297<p>A comment line may appear anywhere in a substitution file, and will be
298ignored. A comment line is any line beginning with the character <tt>#</tt>,
299which must be the very first character on the line.</p>
300
301<p>Global definitions may supplement or override the macro values supplied on
302the command-line using the <tt>-M</tt> switch, and set default values that will
303survive for the remainder of the file unless another global definition of the
304same macro changes it.</p>
305
306<p>For definitions within braces given in any of the file formats, a separator
307must be given between items. A separator is either a comma, or one or more of
308the standard white space characters (space, formfeed, newline, carriage return,
309tab or vertical tab).</p>
310
311<p>Each item within braces can be an alphanumeric token, or a double-quoted
312string. A back-slash character <tt>\</tt> can be used to escape a quote
313character needed inside a quoted string. These three sets of substitutions are
314all equivalent:</p>
315
316<blockquote>
317<pre>{a=aa b=bb c="\"cc\""}
318{b="bb",a=aa,c="\"cc\""}
319{
320 c="\"cc\""
321 b=bb
322 a="aa"
323}</pre>
324</blockquote>
325
326<p>Within a substitutions file, the file name may appear inside double quotation
327marks; these are required if the name contains certain characters or environment
328variable macros of the form ${ENV_VAR} or $(ENV_VAR), which will be expanded
329before the file is opened.</p>
330
331<h3>Regular substitution example</h3>
332
333<p>Let the command be:</p>
334
335<blockquote>
336<pre>msi -S substitute template</pre>
337</blockquote>
338
339<p>The file <tt>template</tt> contains</p>
340
341<blockquote>
342<pre>first name is ${first}
343family name is ${family}</pre>
344</blockquote>
345
346<p> and the file <tt>substitute</tt> is</p>
347
348<blockquote>
349<pre>global {family=Kraimer}
350{first=Marty}
351{first=Irma}</pre>
352</blockquote>
353
354<p>The following is the output produced:</p>
355
356<blockquote>
357<pre>first name is Marty
358family name is Kraimer
359first name is Irma
360family name is Kraimer</pre>
361</blockquote>
362
363<h3>Pattern substitution example</h3>
364
365<p>Let the command be:</p>
366
367<blockquote>
368<pre>msi -S pattern template</pre>
369</blockquote>
370
371<p>The file <tt>pattern</tt> contains</p>
372
373<blockquote>
374<pre>pattern {first,last}
375{Marty,Kraimer}
376{Irma,Kraimer}</pre>
377</blockquote>
378
379<p>and <tt>template</tt> is the same as the previous example:</p>
380
381<blockquote>
382<pre>first name is ${first}
383family name is ${family}</pre>
384</blockquote>
385
386<p>This is the output:</p>
387
388<blockquote>
389<pre>first name is Marty
390family name is Kraimer
391first name is Irma
392family name is Kraimer</pre>
393</blockquote>
394
395<h3>dbTemplate example</h3>
396Let the command be
397
398<blockquote>
399<pre>msi -S xxx.substitutions</pre>
400</blockquote>
401
402<tt>xxx.substitutions</tt> is
403
404<blockquote>
405<pre>file template {
406pattern {first,last}
407{Marty,Kraimer}
408{Irma,Kraimer}
409pattern {last,first}
410{Smith,Bill}
411{Smith,Mary}
412}
413file template {
414{first=Marty,last=Kraimer}
415{first=Irma,last=Kraimer}
416}</pre>
417</blockquote>
418<tt>template</tt> is the same as in the previous example..
419
420<p>The following is written to the output</p>
421
422<blockquote>
423<pre>first name is Marty
424family name is Kraimer
425first name is Irma
426family name is Kraimer
427first name is Bill
428last name is Smith
429first name is Mary
430last name is Smith
431first name is Marty
432family name is Kraimer
433first name is Irma
434family name is Kraimer</pre>
435</blockquote>
436
437</body>
438</html>
0439
=== added directory 'src/ioc/dbtemplate/test'
=== added file 'src/ioc/dbtemplate/test/Makefile'
--- src/ioc/dbtemplate/test/Makefile 1970-01-01 00:00:00 +0000
+++ src/ioc/dbtemplate/test/Makefile 2012-06-01 20:39:23 +0000
@@ -0,0 +1,21 @@
1#*************************************************************************
2# Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne
3# National Laboratory.
4# EPICS BASE is distributed subject to a Software License Agreement found
5# in the file LICENSE that is included with this distribution.
6#*************************************************************************
7TOP=../../..
8
9include $(TOP)/configure/CONFIG
10
11TESTPROD_HOST_DEFAULT = dbltExpand
12TESTPROD_HOST_WIN32 = -nil-
13dbltExpand_SRCS += dbltExpand.c
14dbltExpand_LIBS += dbtoolsIoc dbStaticHost Com
15
16TESTS += msi
17
18TESTSCRIPTS_HOST += $(TESTS:%=%.t)
19
20include $(TOP)/configure/RULES
21
022
=== added file 'src/ioc/dbtemplate/test/dbltExpand.c'
--- src/ioc/dbtemplate/test/dbltExpand.c 1970-01-01 00:00:00 +0000
+++ src/ioc/dbtemplate/test/dbltExpand.c 2012-06-01 20:39:23 +0000
@@ -0,0 +1,100 @@
1/*************************************************************************\
2* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne
3* National Laboratory.
4* EPICS Base is distributed subject to a Software License Agreement found
5* in the file LICENSE that is included with this distribution.
6\*************************************************************************/
7
8/* This is a simple version of msi for testing the dbLoadTemplate() code.
9 *
10 * It calls dbLoadTemplate() to parse the substitution file, but replaces
11 * dbLoadRecords() with its own version that reads the template file,
12 * expands any macros in the text and prints the result to stdout.
13 *
14 * This technique won't work on Windows, dbLoadRecords() has to be
15 * epicsShare... decorated and loaded from a shared library.
16 */
17
18#include <stdio.h>
19#include <stdlib.h>
20#include <string.h>
21#include <errno.h>
22
23#include "macLib.h"
24#include "dbLoadTemplate.h"
25
26
27#define BUFFER_SIZE 0x10000
28
29static char *input_buffer, *output_buffer;
30
31int dbLoadRecords(const char *file, const char *macros)
32{
33 MAC_HANDLE *macHandle = NULL;
34 char **macPairs;
35 FILE *fp;
36 size_t input_len;
37
38 if (macCreateHandle(&macHandle, NULL)) {
39 fprintf(stderr, "macCreateHandle failed\n");
40 exit(1);
41 }
42
43 macSuppressWarning(macHandle, 1);
44 macParseDefns(macHandle, macros, &macPairs);
45 if (!macPairs) {
46 macDeleteHandle(macHandle);
47 macHandle = NULL;
48 } else {
49 macInstallMacros(macHandle, macPairs);
50 free(macPairs);
51 }
52
53 fp = fopen(file, "r");
54 if (!fp) {
55 fprintf(stderr, "fopen('%s') failed: %s\n", file, strerror(errno));
56 exit(1);
57 }
58
59 input_len = fread(input_buffer, 1, BUFFER_SIZE, fp);
60 if (!feof(fp)) {
61 fprintf(stderr, "input file > 64K!\n");
62 fclose(fp);
63 exit(1);
64 }
65 input_buffer[input_len] = 0;
66
67 if (fclose(fp)) {
68 fprintf(stderr, "fclose('%s') failed: %s\n", file, strerror(errno));
69 exit(1);
70 }
71
72 macExpandString(macHandle, input_buffer, output_buffer, BUFFER_SIZE-1);
73 printf(output_buffer);
74
75 if (macHandle) macDeleteHandle(macHandle);
76
77 return 0;
78}
79
80int main(int argc, char **argv)
81{
82 input_buffer = malloc(BUFFER_SIZE);
83 output_buffer = malloc(BUFFER_SIZE);
84
85 if (!input_buffer || !output_buffer) {
86 fprintf(stderr, "malloc(%d) failed\n", BUFFER_SIZE);
87 exit(1);
88 }
89
90 if (argc != 2) {
91 fprintf(stderr, "Usage: %s file.sub\n", argv[0]);
92 exit(1);
93 }
94
95 dbLoadTemplate(argv[1], NULL);
96
97 free(output_buffer);
98 free(input_buffer);
99 return 0;
100}
0101
=== added file 'src/ioc/dbtemplate/test/msi.plt'
--- src/ioc/dbtemplate/test/msi.plt 1970-01-01 00:00:00 +0000
+++ src/ioc/dbtemplate/test/msi.plt 2012-06-01 20:39:23 +0000
@@ -0,0 +1,48 @@
1#!/usr/bin/perl
2#*************************************************************************
3# Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne
4# National Laboratory.
5# EPICS BASE is distributed subject to a Software License Agreement found
6# in file LICENSE that is included with this distribution.
7#*************************************************************************
8
9# Script to run tests on the msi program
10
11use FindBin qw($Bin); # To find the msi executable
12
13use strict;
14use Test;
15
16BEGIN {plan tests => 7}
17
18ok(msi('-I .. ../t1-template.txt'), slurp('../t1-result.txt'));
19ok(msi('-I.. -S ../t2-substitution.txt'), slurp('../t2-result.txt'));
20ok(msi('-I. -I.. -S ../t3-substitution.txt'), slurp('../t3-result.txt'));
21ok(msi('-g -I.. -S ../t4-substitution.txt'), slurp('../t4-result.txt'));
22ok(msi('-S ../t5-substitute.txt ../t5-template.txt'), slurp('../t5-result.txt'));
23ok(msi('-S ../t6-substitute.txt ../t6-template.txt'), slurp('../t6-result.txt'));
24
25# Check -o works
26my $out = 't7-output.txt';
27unlink $out;
28msi("-I.. -o $out ../t1-template.txt");
29ok(slurp($out), slurp('../t1-result.txt'));
30
31
32# Support routines
33
34sub slurp {
35 my ($file) = @_;
36 open my $in, '<', $file
37 or die "Can't open file $file: $!\n";
38 my $contents = do { local $/; <$in> };
39 return $contents;
40}
41
42sub msi {
43 my ($args) = @_;
44 my $arch = $ENV{EPICS_HOST_ARCH};
45 my $exe = ($^O eq 'MSWin32') || ($^O eq 'cygwin') ? '.exe' : '';
46 my $msi = "$Bin/../../O.$arch/msi$exe";
47 return `$msi $args`;
48}
049
=== added file 'src/ioc/dbtemplate/test/t1-include.txt'
--- src/ioc/dbtemplate/test/t1-include.txt 1970-01-01 00:00:00 +0000
+++ src/ioc/dbtemplate/test/t1-include.txt 2012-06-01 20:39:23 +0000
@@ -0,0 +1,5 @@
1This is t1-include.txt $(include-file-again=)
2 a = $(a=default value used when a is undefined)
3 b = $(b=default value used when b is undefined)
4substitute "include-file-again=again"
5End of t1-include.txt
06
=== added file 'src/ioc/dbtemplate/test/t1-result.txt'
--- src/ioc/dbtemplate/test/t1-result.txt 1970-01-01 00:00:00 +0000
+++ src/ioc/dbtemplate/test/t1-result.txt 2012-06-01 20:39:23 +0000
@@ -0,0 +1,21 @@
1This is t1-template.txt
2
3With $(a,undefined) & $(b,undefined):
4This is t1-include.txt
5 a = default value used when a is undefined
6 b = default value used when b is undefined
7End of t1-include.txt
8
9On defining a=aaa & b=bbb:
10This is t1-include.txt again
11 a = aaa
12 b = bbb
13End of t1-include.txt
14
15On setting a="aa":
16This is t1-include.txt again
17 a = "aa"
18 b = bbb
19End of t1-include.txt
20
21End of t1-template.txt
022
=== added file 'src/ioc/dbtemplate/test/t1-template.txt'
--- src/ioc/dbtemplate/test/t1-template.txt 1970-01-01 00:00:00 +0000
+++ src/ioc/dbtemplate/test/t1-template.txt 2012-06-01 20:39:23 +0000
@@ -0,0 +1,14 @@
1This is t1-template.txt
2
3With $(a) & ${b}:
4include "t1-include.txt"
5
6substitute "a=aaa,b=bbb"
7On defining a=$(a) & b=${b}:
8include "t1-include.txt"
9
10substitute "a=\"aa\""
11On setting a=$(a):
12include "t1-include.txt"
13
14End of t1-template.txt
015
=== added file 'src/ioc/dbtemplate/test/t2-result.txt'
--- src/ioc/dbtemplate/test/t2-result.txt 1970-01-01 00:00:00 +0000
+++ src/ioc/dbtemplate/test/t2-result.txt 2012-06-01 20:39:23 +0000
@@ -0,0 +1,6 @@
1a = va1-a b = def-b c = def-c d = $(d,undefined)
2a = va2-a b = va2-b c = def-c d = $(d,undefined)
3a = va3-a b = va3-b c = va3-c d = $(d,undefined)
4a = va4-a b = va4-b c = def-c d = $(d,undefined)
5a = va5-a b = def-b c = def-c d = $(d,undefined)
6a = pt3-a b = pt3-b c = pt3-c d = $(d,undefined)
07
=== added file 'src/ioc/dbtemplate/test/t2-substitution.txt'
--- src/ioc/dbtemplate/test/t2-substitution.txt 1970-01-01 00:00:00 +0000
+++ src/ioc/dbtemplate/test/t2-substitution.txt 2012-06-01 20:39:23 +0000
@@ -0,0 +1,11 @@
1file t2-template.txt {
2 {a=va1-a}
3 {a=va2-a, b=va2-b}
4 {a=va3-a, b=va3-b, c=va3-c}
5 {a=va4-a, b=va4-b}
6 {a=va5-a}
7}
8file t2-template.txt {
9 pattern {a, b, c}
10 {pt3-a, pt3-b, pt3-c}
11}
012
=== added file 'src/ioc/dbtemplate/test/t2-template.txt'
--- src/ioc/dbtemplate/test/t2-template.txt 1970-01-01 00:00:00 +0000
+++ src/ioc/dbtemplate/test/t2-template.txt 2012-06-01 20:39:23 +0000
@@ -0,0 +1,1 @@
1a = $(a=def-a) b = $(b=def-b) c = $(c=def-c) d = $(d,undef)
02
=== added file 'src/ioc/dbtemplate/test/t3-result.txt'
--- src/ioc/dbtemplate/test/t3-result.txt 1970-01-01 00:00:00 +0000
+++ src/ioc/dbtemplate/test/t3-result.txt 2012-06-01 20:39:23 +0000
@@ -0,0 +1,28 @@
1a = gb1-a b = gb1-b c = def-c d = $(d,undefined)
2a = va1-a b = gb1-b c = def-c d = $(d,undefined)
3a = va2-a b = va2-b c = def-c d = $(d,undefined)
4a = va3-a b = va3-b c = va3-c d = $(d,undefined)
5a = va4-a b = va4-b c = def-c d = $(d,undefined)
6a = va5-a b = gb1-b c = def-c d = $(d,undefined)
7a = gb1-a b = gb1-b c = def-c d = $(d,undefined)
8a = gb2-a b = gb2-b c = def-c d = $(d,undefined)
9a = va1-a b = gb2-b c = def-c d = $(d,undefined)
10a = va2-a b = va2-b c = def-c d = $(d,undefined)
11a = va3-a b = va3-b c = va3-c d = $(d,undefined)
12a = va4-a b = va4-b c = def-c d = $(d,undefined)
13a = va5-a b = gb2-b c = def-c d = $(d,undefined)
14a = gb2-a b = gb2-b c = def-c d = $(d,undefined)
15a = gb3-a b = gb3-b c = def-c d = $(d,undefined)
16a = pt1-a b = gb3-b c = def-c d = $(d,undefined)
17a = pt2-a b = pt2-b c = def-c d = $(d,undefined)
18a = pt3-a b = pt3-b c = pt3-c d = $(d,undefined)
19a = pt4-a b = pt4-b c = def-c d = $(d,undefined)
20a = pt5-a b = gb3-b c = def-c d = $(d,undefined)
21a = gb3-a b = gb3-b c = def-c d = $(d,undefined)
22a = gb4-a b = gb4-b c = def-c d = $(d,undefined)
23a = pt1-a b = gb4-b c = def-c d = $(d,undefined)
24a = pt2-a b = pt2-b c = def-c d = $(d,undefined)
25a = pt3-a b = pt3-b c = pt3-c d = $(d,undefined)
26a = pt4-a b = pt4-b c = def-c d = $(d,undefined)
27a = pt5-a b = gb4-b c = def-c d = $(d,undefined)
28a = gb4-a b = gb4-b c = def-c d = $(d,undefined)
029
=== added file 'src/ioc/dbtemplate/test/t3-substitution.txt'
--- src/ioc/dbtemplate/test/t3-substitution.txt 1970-01-01 00:00:00 +0000
+++ src/ioc/dbtemplate/test/t3-substitution.txt 2012-06-01 20:39:23 +0000
@@ -0,0 +1,37 @@
1global {a=gb1-a, b=gb1-b}
2file t3-template.txt {
3 {}
4 {a=va1-a}
5 {a=va2-a, b=va2-b}
6 {a=va3-a, b=va3-b, c=va3-c}
7 {a=va4-a, b=va4-b}
8 {a=va5-a}
9 {}
10 global {a=gb2-a, b=gb2-b}
11 {}
12 {a=va1-a}
13 {a=va2-a, b=va2-b}
14 {a=va3-a, b=va3-b, c=va3-c}
15 {a=va4-a, b=va4-b}
16 {a=va5-a}
17 {}
18}
19global {b=gb3-b, a=gb3-a}
20file t3-template.txt {
21 pattern {a, b, c}
22 {}
23 {pt1-a}
24 {pt2-a, pt2-b}
25 {pt3-a, pt3-b, pt3-c}
26 {pt4-a, pt4-b}
27 {pt5-a}
28 {}
29 global {b=gb4-b, a=gb4-a}
30 {}
31 {pt1-a}
32 {pt2-a, pt2-b}
33 {pt3-a, pt3-b, pt3-c}
34 {pt4-a, pt4-b}
35 {pt5-a}
36 {}
37}
038
=== added file 'src/ioc/dbtemplate/test/t3-template.txt'
--- src/ioc/dbtemplate/test/t3-template.txt 1970-01-01 00:00:00 +0000
+++ src/ioc/dbtemplate/test/t3-template.txt 2012-06-01 20:39:23 +0000
@@ -0,0 +1,1 @@
1a = $(a=def-a) b = $(b=def-b) c = $(c=def-c) d = $(d,undef)
02
=== added file 'src/ioc/dbtemplate/test/t4-result.txt'
--- src/ioc/dbtemplate/test/t4-result.txt 1970-01-01 00:00:00 +0000
+++ src/ioc/dbtemplate/test/t4-result.txt 2012-06-01 20:39:23 +0000
@@ -0,0 +1,6 @@
1a = va1-a b = def-b c = def-c d = $(d,undefined)
2a = va2-a b = va2-b c = def-c d = $(d,undefined)
3a = va3-a b = va3-b c = va3-c d = $(d,undefined)
4a = va4-a b = va4-b c = va3-c d = $(d,undefined)
5a = va5-a b = va4-b c = va3-c d = $(d,undefined)
6a = pt3-a b = pt3-b c = pt3-c d = $(d,undefined)
07
=== added file 'src/ioc/dbtemplate/test/t4-substitution.txt'
--- src/ioc/dbtemplate/test/t4-substitution.txt 1970-01-01 00:00:00 +0000
+++ src/ioc/dbtemplate/test/t4-substitution.txt 2012-06-01 20:39:23 +0000
@@ -0,0 +1,11 @@
1file t2-template.txt {
2 {a=va1-a}
3 {a=va2-a, b=va2-b}
4 {a=va3-a, b=va3-b, c=va3-c}
5 {a=va4-a, b=va4-b}
6 {a=va5-a}
7}
8file t2-template.txt {
9 pattern {a, b, c}
10 {pt3-a, pt3-b, pt3-c}
11}
012
=== added file 'src/ioc/dbtemplate/test/t5-result.txt'
--- src/ioc/dbtemplate/test/t5-result.txt 1970-01-01 00:00:00 +0000
+++ src/ioc/dbtemplate/test/t5-result.txt 2012-06-01 20:39:23 +0000
@@ -0,0 +1,20 @@
1# comment line
2a = 111
3b = 222
4c = xx
5d = $(d,undefined)
6# comment line
7a = aaa
8b = bbb
9c = ccc
10d = $(d,undefined)
11# comment line
12a = AA
13b = BB
14c = xx
15d = $(d,undefined)
16# comment line
17a = aaa
18b = bbb
19c = yy
20d = $(d,undefined)
021
=== added file 'src/ioc/dbtemplate/test/t5-substitute.txt'
--- src/ioc/dbtemplate/test/t5-substitute.txt 1970-01-01 00:00:00 +0000
+++ src/ioc/dbtemplate/test/t5-substitute.txt 2012-06-01 20:39:23 +0000
@@ -0,0 +1,9 @@
1global {c=xx}
2{a=111,b="222"}
3{ a = aaa , b=bbb , c = ccc}
4{a=AA,b='BB'}
5global { c = yy }
6{
7 a= aaa
8 b= bbb
9}
010
=== added file 'src/ioc/dbtemplate/test/t5-template.txt'
--- src/ioc/dbtemplate/test/t5-template.txt 1970-01-01 00:00:00 +0000
+++ src/ioc/dbtemplate/test/t5-template.txt 2012-06-01 20:39:23 +0000
@@ -0,0 +1,5 @@
1# comment line
2a = $(a)
3b = $(b)
4c = $(c)
5d = $(d)
06
=== added file 'src/ioc/dbtemplate/test/t6-result.txt'
--- src/ioc/dbtemplate/test/t6-result.txt 1970-01-01 00:00:00 +0000
+++ src/ioc/dbtemplate/test/t6-result.txt 2012-06-01 20:39:23 +0000
@@ -0,0 +1,20 @@
1# comment line
2a = 111
3b = 222
4c = xx
5d = $(d,undefined)
6# comment line
7a = aaa
8b = bbb
9c = ccc
10d = $(d,undefined)
11# comment line
12a = AA
13b = BB
14c = xx
15d = $(d,undefined)
16# comment line
17a = aaa
18b = bbb
19c = yy
20d = $(d,undefined)
021
=== added file 'src/ioc/dbtemplate/test/t6-substitute.txt'
--- src/ioc/dbtemplate/test/t6-substitute.txt 1970-01-01 00:00:00 +0000
+++ src/ioc/dbtemplate/test/t6-substitute.txt 2012-06-01 20:39:23 +0000
@@ -0,0 +1,13 @@
1global {c=xx}
2pattern {b,a}
3{"222",111}
4pattern {a b c}
5{ aaa , bbb , ccc}
6pattern { a , b }
7{AA,'BB'}
8global { c = yy }
9pattern { a , b }
10{
11 aaa
12 bbb
13}
014
=== added file 'src/ioc/dbtemplate/test/t6-template.txt'
--- src/ioc/dbtemplate/test/t6-template.txt 1970-01-01 00:00:00 +0000
+++ src/ioc/dbtemplate/test/t6-template.txt 2012-06-01 20:39:23 +0000
@@ -0,0 +1,5 @@
1# comment line
2a = $(a)
3b = $(b)
4c = $(c)
5d = $(d)
06
=== added file 'src/ioc/dbtemplate/test/template'
--- src/ioc/dbtemplate/test/template 1970-01-01 00:00:00 +0000
+++ src/ioc/dbtemplate/test/template 2012-06-01 20:39:23 +0000
@@ -0,0 +1,5 @@
1# comment line
2a = $(a)
3b = $(b)
4c = $(c)
5d = $(d)

Subscribers

People subscribed via source and target branches

to all changes: