Implemented "global" variables in substitution files.

Rewrote the YACC grammar, with help from Benjamin Franksen in formally
defining the substitution file format in EBNF.  This version also
changes the way in which variables are added to the sub_collect
string; previously it appended a ',' after every entry, and removed it
off the end before calling dbLoadRecords(), but now we put the ',' at
the beginning of each entry, and just offer dbLoadRecords() the string
starting at the second character (not that this really matters, macLib
will quite happily ignore either a leading or a trailing comma in the
variable definition string).

We now warn if there are substitution values for which we have no
name, or if the file uses the deprecated syntax which permitted a
bareword token in front of the variable substitution or pattern
definition braces.
This commit is contained in:
Andrew Johnson
2010-08-24 19:01:19 -05:00
parent 4a637c04cf
commit e2464c8572
2 changed files with 171 additions and 262 deletions
-137
View File
@@ -1,137 +0,0 @@
/*************************************************************************\
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
* National Laboratory.
* Copyright (c) 2002 The Regents of the University of California, as
* Operator of Los Alamos National Laboratory.
* EPICS BASE Versions 3.13.7
* and higher are distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
<HTML>
<BODY>
<PRE>
<!-- Manpage converted by man2html 3.0.1 -->
</PRE>
<H2>NAME</H2><PRE>
dbLoadRecords, dbLoadTemplate - load ascii database records
into an IOC
</PRE>
<H2>SYNOPSIS</H2><PRE>
dbLoadRecords(char* db_file, char* substitutions)
dbLoadTemplate(char* template_file)
</PRE>
<H2>DESCRIPTION</H2><PRE>
These routines are available from IOC core on the vxWorks
command line. Both provide a way to load ascii ".db" files
(usually created by <B>gdct(1)</B> ) into the IOC. The ".db" files
contain ascii versions of record instances and are described
in more detail in <B>dbfile(5)</B>. In addition to loading the
".db" ascii files into the IOC, both routines provide a
method of performing variable substitution on record names
and field values.
dbLoadRecords() reads the ".db" file <I>db</I>_<I>file</I> performing sub-
stitutions specified in string <I>substitutions</I>. The substitu-
tion must be a string specified as follows:
"var1=sub1,var2=sub3,..."
Variables are specified in the ".db" file as
$(variable_name). If the substitution string
"a=1,b=2,c=\"this is a test\"" were used, any variables
$(a), $(b), or $(c) would be substituted with the appropri-
ate data. See the EXAMPLES section for more details.
dbLoadTemplate() will read a <I>template</I>_<I>file</I>. The
<I>template</I>_<I>file</I> resides in the your IOC boot directory and
contains rules about loading ".db" files and performing sub-
stitutions. The template_file must be in the form used by
an IOC and is described in <B>templatefile(5)</B>. The EXAMPLES
section descibes how it can be used.
</PRE>
<H2>EXAMPLES</H2><PRE>
The next two examples of dbLoadRecords() and dbLoadTem-
plate() will use the following ".db" file named <I>test</I>.<I>db</I> :
database(test)
{
record(ai,"$(pre)testrec1")
record(ai,"$(pre)testrec2")
record(stringout,"$(pre)testrec3")
{
field(VAL,"$(STRING)")
field(SCAN,"$(SCAN)")
}
}
Running dbLoadRecords ("test.db","pre=TEST,STRING=\"this is
a test\",SCAN=Passive") will produce the following records
in the IOC's database:
TESTtestrec1
TESTtestrec2
TESTtestrec3
The third record will have VAL set to "this is a test" and
SCAN set to "Passive".
Running dbLoadTemplate ("test.template") with test.template
containing:
file test.db
{
{pre=TEST1, STRING = "this is a test two", SCAN="1 Second" }
{pre=TEST2, STRING = "this is a test one", SCAN=Passive }
{pre=TEST3, STRING = "this is a test three", SCAN=Passive }
}
will produce a total of nine records in the IOC's database:
TEST1testrec1
TEST1testrec2
TEST1testrec3 - (VAL="this is a test two", SCAN="1 Second")
TEST2testrec1
TEST2testrec2
TEST2testrec3 - (VAL="this is a test one", SCAN="Passive")
TEST3testrec1
TEST3testrec2
TEST3testrec3 - (VAL="this is a test three", SCAN="Passive")
</PRE>
<H2>NOTES</H2><PRE>
The binary file <I>default</I>.<I>dctsdr</I> must be loaded prior to run-
ning either of these routines. This file contains the rules
on how to construct records and change field values.
After the default.dctsdr file is loaded, these routines can
be run as many times as desired until iocInit is run.
</PRE>
<H2>SEE ALSO</H2><PRE>
<B>gdct(1)</B>, <B>templatefile(5)</B>, <B>dbfile(5)</B>
</PRE>
<HR>
<ADDRESS>
Man(1) output converted with
<a href="http://www.oac.uci.edu/indiv/ehood/man2html.html">man2html</a>
</ADDRESS>
</BODY>
</HTML>
+171 -125
View File
@@ -27,15 +27,15 @@ static int yyerror(char* str);
#define VAR_MAX_VAR_STRING 5000
#define VAR_MAX_VARS 100
static char *sub_collect = NULL;
static char *gbl_collect;
static char *sub_collect;
static char *sub_locals;
static char** vars = NULL;
static char* db_file_name = NULL;
static int var_count, sub_count;
%}
%start template
%start substitution_file
%token <Str> WORD QUOTE
%token DBFILE
@@ -55,67 +55,83 @@ 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
global_definitions: GLOBAL O_BRACE C_BRACE
| GLOBAL O_BRACE variable_definitions C_BRACE
{
if (db_file_name)
dbLoadRecords(db_file_name, NULL);
else
fprintf(stderr, "Error: no db file name given\n");
#ifdef ERROR_STUFF
fprintf(stderr, "global_definitions: %s\n", sub_collect+1);
#endif
sub_locals += strlen(sub_locals);
}
;
templ_head: DBFILE WORD
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;
}
;
template_filename: DBFILE WORD
{
#ifdef ERROR_STUFF
fprintf(stderr, "template_filename: %s\n", $2);
#endif
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
{
#ifdef ERROR_STUFF
fprintf(stderr, "template_filename: \"%s\"\n", $2);
#endif
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);
}
;
subst: PATTERN pattern subs
| PATTERN pattern
| var_subs
substitutions: pattern_substitutions
| variable_substitutions
;
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
}
;
vars: vars var
| vars COMMA var
| var
;
var: WORD
pattern_substitutions: PATTERN O_BRACE C_BRACE
| PATTERN O_BRACE pattern_names C_BRACE
| PATTERN O_BRACE pattern_names C_BRACE pattern_definitions
;
pattern_names: pattern_name
| pattern_names COMMA
| pattern_names pattern_name
;
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++;
@@ -123,123 +139,153 @@ var: WORD
}
;
subs: subs sub
| sub
pattern_definitions: pattern_definition
| pattern_definitions pattern_definition
;
sub: WORD O_BRACE vals C_BRACE
pattern_definition: O_BRACE C_BRACE
{
gbl_collect[strlen(gbl_collect) - 1] = '\0'; /* drop ',' */
#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");
#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);
*gbl_collect = '\0';
sub_count = 0;
}
| O_BRACE vals C_BRACE
{
gbl_collect[strlen(gbl_collect) - 1] = '\0'; /* drop ',' */
#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");
*gbl_collect = '\0';
*sub_locals = '\0';
sub_count = 0;
}
;
vals: vals val
| vals COMMA val
| val
pattern_values: pattern_value
| pattern_values COMMA
| pattern_values pattern_value
;
val: QUOTE
pattern_value: QUOTE
{
if (sub_count <= var_count) {
strcat(gbl_collect, vars[sub_count]);
strcat(gbl_collect, "=\"");
strcat(gbl_collect, $1);
strcat(gbl_collect, "\",");
#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
{
if (sub_count <= var_count) {
strcat(gbl_collect, vars[sub_count]);
strcat(gbl_collect, "=");
strcat(gbl_collect, $1);
strcat(gbl_collect, ",");
#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_subs: var_subs var_sub
| var_sub
variable_substitutions: variable_substitution
| variable_substitutions variable_substitution
;
var_sub: WORD O_BRACE sub_pats C_BRACE
variable_substitution: O_BRACE C_BRACE
{
gbl_collect[strlen(gbl_collect) - 1] = '\0'; /* drop ',' */
#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");
#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);
*gbl_collect = '\0';
sub_count = 0;
}
| O_BRACE sub_pats C_BRACE
{
gbl_collect[strlen(gbl_collect) - 1] = '\0'; /* drop ',' */
#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");
*gbl_collect = '\0';
sub_count = 0;
*sub_locals = '\0';
}
;
sub_pats: sub_pats sub_pat
| sub_pats COMMA sub_pat
| sub_pat
variable_definitions: variable_definition
| variable_definitions COMMA
| variable_definitions variable_definition
;
sub_pat: WORD EQUALS WORD
variable_definition: WORD EQUALS WORD
{
strcat(gbl_collect, $1);
strcat(gbl_collect, "=");
strcat(gbl_collect, $3);
strcat(gbl_collect, ",");
#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);
sub_count++;
}
| WORD EQUALS QUOTE
{
strcat(gbl_collect, $1);
strcat(gbl_collect, "=\"");
strcat(gbl_collect, $3);
strcat(gbl_collect, "\",");
#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);
sub_count++;
}
;
@@ -262,7 +308,7 @@ static int is_not_inited = 1;
int epicsShareAPI dbLoadTemplate(const char *sub_file, const char *cmd_collect)
{
FILE *fp;
int ind;
int i;
line_num = 1;
@@ -286,14 +332,14 @@ int epicsShareAPI dbLoadTemplate(const char *sub_file, const char *cmd_collect)
fprintf(stderr, "dbLoadTemplate: Out of memory!\n");
return -1;
}
strcpy(sub_collect, ",");
if (cmd_collect && *cmd_collect) {
strcpy(sub_collect, cmd_collect);
strcat(sub_collect, ",");
gbl_collect = sub_collect + strlen(sub_collect);
strcat(sub_collect, cmd_collect);
sub_locals = sub_collect + strlen(sub_collect);
} else {
gbl_collect = sub_collect;
*gbl_collect = '\0';
sub_locals = sub_collect;
*sub_locals = '\0';
}
var_count = 0;
sub_count = 0;
@@ -307,8 +353,8 @@ int epicsShareAPI dbLoadTemplate(const char *sub_file, const char *cmd_collect)
yyparse();
for (ind = 0; ind < var_count; ind++) {
dbmfFree(vars[ind]);
for (i = 0; i < var_count; i++) {
dbmfFree(vars[i]);
}
free(vars);
free(sub_collect);