dbtools: Join msi repository into ../src/dbtools

This commit is contained in:
Ralph Lange
2010-08-23 14:36:23 -04:00
10 changed files with 1348 additions and 0 deletions

64
src/dbtools/msi/LICENSE Normal file
View File

@@ -0,0 +1,64 @@
Copyright (c) 2002 University of Chicago. All rights reserved.
msi is distributed subject to the following license conditions:
SOFTWARE LICENSE AGREEMENT
Software: Macro Substitution and Include Tool (msi)
1. The "Software", below, refers to msi (in either source code, or
binary form and accompanying documentation). Each licensee is
addressed as "you" or "Licensee."
2. The copyright holders shown above and their third-party licensors
hereby grant Licensee a royalty-free nonexclusive license, subject to
the limitations stated herein and U.S. Government license rights.
3. You may modify and make a copy or copies of the Software for use
within your organization, if you meet the following conditions:
a. Copies in source code must include the copyright notice and this
Software License Agreement.
b. Copies in binary form must include the copyright notice and this
Software License Agreement in the documentation and/or other
materials provided with the copy.
4. You may modify a copy or copies of the Software or any portion of it,
thus forming a work based on the Software, and distribute copies of
such work outside your organization, if you meet all of the following
conditions:
a. Copies in source code must include the copyright notice and this
Software License Agreement;
b. Copies in binary form must include the copyright notice and this
Software License Agreement in the documentation and/or other
materials provided with the copy;
c. Modified copies and works based on the Software must carry
prominent notices stating that you changed specified portions of
the Software.
5. Portions of the Software resulted from work developed under a U.S.
Government contract and are subject to the following license: the
Government is granted for itself and others acting on its behalf a
paid-up, nonexclusive, irrevocable worldwide license in this computer
software to reproduce, prepare derivative works, and perform publicly
and display publicly.
6. WARRANTY DISCLAIMER. THE SOFTWARE IS SUPPLIED "AS IS" WITHOUT WARRANTY
OF ANY KIND. THE COPYRIGHT HOLDERS, THEIR THIRD PARTY LICENSORS, THE
UNITED STATES, THE UNITED STATES DEPARTMENT OF ENERGY, AND THEIR
EMPLOYEES: (1) DISCLAIM ANY WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
BUT NOT LIMITED TO ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE, TITLE OR NON-INFRINGEMENT, (2) DO NOT ASSUME
ANY LEGAL LIABILITY OR RESPONSIBILITY FOR THE ACCURACY, COMPLETENESS,
OR USEFULNESS OF THE SOFTWARE, (3) DO NOT REPRESENT THAT USE OF THE
SOFTWARE WOULD NOT INFRINGE PRIVATELY OWNED RIGHTS, (4) DO NOT WARRANT
THAT THE SOFTWARE WILL FUNCTION UNINTERRUPTED, THAT IT IS ERROR-FREE
OR THAT ANY ERRORS WILL BE CORRECTED.
7. LIMITATION OF LIABILITY. IN NO EVENT WILL THE COPYRIGHT HOLDERS, THEIR
THIRD PARTY LICENSORS, THE UNITED STATES, THE UNITED STATES DEPARTMENT
OF ENERGY, OR THEIR EMPLOYEES: BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
CONSEQUENTIAL, SPECIAL OR PUNITIVE DAMAGES OF ANY KIND OR NATURE,
INCLUDING BUT NOT LIMITED TO LOSS OF PROFITS OR LOSS OF DATA, FOR ANY
REASON WHATSOEVER, WHETHER SUCH LIABILITY IS ASSERTED ON THE BASIS OF
CONTRACT, TORT (INCLUDING NEGLIGENCE OR STRICT LIABILITY), OR
OTHERWISE, EVEN IF ANY OF SAID PARTIES HAS BEEN WARNED OF THE
POSSIBILITY OF SUCH LOSS OR DAMAGES.

22
src/dbtools/msi/Makefile Normal file
View File

@@ -0,0 +1,22 @@
#*************************************************************************
# 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.
# This file is distributed subject to a Software License Agreement found
# in the file LICENSE that is included with this distribution.
#*************************************************************************
#
# $Id$
#
TOP = ../..
include $(TOP)/configure/CONFIG
PROD_HOST = msi
HTMLS = msi.html
msi_SRCS = msi.c
PROD_LIBS += Com
include $(TOP)/configure/RULES

View File

@@ -0,0 +1,2 @@
The extension distribution file should be unziped and untared
in the extensions/src directory.

792
src/dbtools/msi/msi.c Normal file
View File

@@ -0,0 +1,792 @@
/*************************************************************************\
* 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.
* This file is distributed subject to a Software License Agreement found
* in the file LICENSE that is included with this distribution.
\*************************************************************************/
/*msi - macro sunstitutions and include */
/*
* Modification Log:
* -----------------
* .01 08DEC97 mrk Original version
*/
#include <stdlib.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <epicsVersion.h>
#include <dbDefs.h>
#include <macLib.h>
#include <ellLib.h>
#define MAX_BUFFER_SIZE 4096
#if ((EPICS_VERSION <= 3) && (EPICS_REVISION <= 13))
#define macEnvExpand(x) strdup(x)
#endif
/*Forward references to local routines*/
static void usageExit(void);
static void addMacroReplacements(MAC_HANDLE *macPvt,char *pval);
static void makeSubstitutions(void *inputPvt,void *macPvt,char *templateName);
/*Routines that read the template files */
static void inputConstruct(void **inputPvt);
static void inputDestruct(void *inputPvt);
static void inputAddPath(void *inputPvt, char *pval);
static void inputBegin(void *inputPvt,char *fileName);
static char *inputNextLine(void *inputPvt);
static void inputNewIncludeFile(void *inputPvt,char *name);
static void inputErrPrint(void *inputPvt);
/*Routines that read the substitution file */
static void substituteDestruct(void *substitutePvt);
static void substituteOpen(void **substitutePvt,char *substitutionName);
static int substituteGetNextSet(void *substitutePvt,char **filename);
static char *substituteGetReplacements(void *substitutePvt);
/*Exit status*/
static int exitStatus = 0;
int opt_V = 0;
int main(int argc,char **argv)
{
void *inputPvt;
MAC_HANDLE *macPvt;
char *pval;
int narg;
char *substitutionName=0;
char *templateName=0;
int i;
inputConstruct(&inputPvt);
macCreateHandle(&macPvt,0);
macSuppressWarning(macPvt,1);
while((argc>1) && (argv[1][0] == '-')) {
narg = (strlen(argv[1])==2) ? 2 : 1;
pval = (narg==1) ? (argv[1]+2) : argv[2];
if(strncmp(argv[1],"-I",2)==0) {
inputAddPath(inputPvt,pval);
} else if(strncmp(argv[1],"-o",2)==0) {
if(freopen(pval,"w",stdout)==NULL) {
fprintf(stderr,"Can't open %s for writing: %s\n", pval, strerror(errno));
exit(1);
}
} else if(strncmp(argv[1],"-M",2)==0) {
addMacroReplacements(macPvt,pval);
} else if(strncmp(argv[1],"-S",2)==0) {
substitutionName = calloc(strlen(pval)+1,sizeof(char));
strcpy(substitutionName,pval);
} else if(strncmp(argv[1],"-V",2)==0) {
macSuppressWarning(macPvt,0);
opt_V = 1;
narg = 1; /* no argument for this option */
} else {
usageExit();
}
argc -= narg;
for(i=1; i<argc; i++) argv[i] = argv[i + narg];
}
if(argc>2) {
fprintf(stderr,"too many filename arguments\n");
usageExit();
}
if(argc==2) {
templateName = calloc(strlen(argv[1])+1,sizeof(char));
strcpy(templateName,argv[1]);
}
if(!substitutionName) {
makeSubstitutions(inputPvt,macPvt,templateName);
} else {
void *substitutePvt;
char *filename = 0;
substituteOpen(&substitutePvt,substitutionName);
while(substituteGetNextSet(substitutePvt,&filename)) {
if(templateName) filename = templateName;
if(!filename) {
fprintf(stderr,"no template file\n");
usageExit();
}
macPushScope(macPvt);
while((pval = substituteGetReplacements(substitutePvt))){
addMacroReplacements(macPvt,pval);
makeSubstitutions(inputPvt,macPvt,filename);
}
macPopScope(macPvt);
}
substituteDestruct(substitutePvt);
}
inputDestruct(inputPvt);
free((void *)templateName);
free((void *)substitutionName);
return(exitStatus);
}
void usageExit(void)
{
fprintf(stderr,"usage: msi -V -opath -Ipath ... -Msub ... -Ssubfile template\n");
fprintf(stderr," Specifying path will replace the default '.'\n");
fprintf(stderr," stdin is used if template is not given\n");
exit(1);
}
static void addMacroReplacements(MAC_HANDLE *macPvt,char *pval)
{
char **pairs;
long status;
status = macParseDefns(macPvt,pval,&pairs);
if(!status) {
fprintf(stderr,"Error macParseDefns error\n");
usageExit();
}
status = macInstallMacros(macPvt,pairs);
if(!status) {
fprintf(stderr,"Error macInstallMacros error\n");
usageExit();
}
free((void *)pairs);
}
typedef enum {cmdInclude,cmdSubstitute} cmdType;
static const char *cmdNames[] = {"include","substitute"};
static void makeSubstitutions(void *inputPvt,void *macPvt,char *templateName)
{
char *input;
static char buffer[MAX_BUFFER_SIZE];
int n;
static int unexpWarned = 0;
inputBegin(inputPvt,templateName);
while((input = inputNextLine(inputPvt))) {
int expand=1;
char *p;
char *command = 0;
p = input;
/*skip whitespace at beginning of line*/
while(*p && (isspace(*p))) ++p;
/*Look for i or s */
if(*p && (*p=='i' || *p=='s')) command = p;
if(command) {
char *pstart;
char *pend;
char *copy;
int cmdind=-1;
int i;
for(i=0; i< NELEMENTS(cmdNames); i++) {
if(strstr(command,cmdNames[i])) {
cmdind = i;
}
}
if(cmdind<0) goto endif;
p = command + strlen(cmdNames[cmdind]);
/*skip whitespace after command*/
while(*p && (isspace(*p))) ++p;
/*Next character must be quote*/
if((*p==0) || (*p!='"')) goto endif;
pstart = ++p;
/*Look for end quote*/
while(*p && (*p!='"')) {
/*allow escape for imbeded quote*/
if((*p=='\\') && *(p+1)=='"') {
p += 2; continue;
} else {
if(*p=='"') break;
}
++p;
}
pend = p;
if(*p==0) goto endif;
/*skip quote and any trailing blanks*/
while(*++p==' ') ;
if(*p != '\n' && *p !=0) goto endif;
copy = calloc(pend-pstart+1,sizeof(char));
strncpy(copy,pstart,pend-pstart);
switch(cmdind) {
case cmdInclude:
inputNewIncludeFile(inputPvt,copy);
break;
case cmdSubstitute:
addMacroReplacements(macPvt,copy);
break;
default:
fprintf(stderr,"Logic Error: makeSubstitutions\n");
inputErrPrint(inputPvt);
exit(1);
}
free(copy);
expand = 0;
}
endif:
if (expand) {
n = macExpandString(macPvt,input,buffer,MAX_BUFFER_SIZE-1);
fputs(buffer,stdout);
if (!unexpWarned && n<0) {
fprintf(stderr, "Warning: Undefined macros present%s\n",
opt_V ? "" : ", use msi -V to list");
unexpWarned++;
}
}
}
}
typedef struct inputFile{
ELLNODE node;
char *filename;
FILE *fp;
int lineNum;
}inputFile;
typedef struct pathNode {
ELLNODE node;
char *directory;
} pathNode;
typedef struct inputData {
ELLLIST inputFileList;
ELLLIST pathList;
char inputBuffer[MAX_BUFFER_SIZE];
}inputData;
static void inputOpenFile(inputData *pinputData,char *filename);
static void inputCloseFile(inputData *pinputData);
static void inputCloseAllFiles(inputData *pinputData);
static void inputConstruct(void **ppvt)
{
inputData *pinputData;
pinputData = calloc(1,sizeof(inputData));
ellInit(&pinputData->inputFileList);
ellInit(&pinputData->pathList);
*ppvt = pinputData;
}
static void inputDestruct(void *pvt)
{
inputData *pinputData = (inputData *)pvt;
pathNode *ppathNode;
inputCloseAllFiles(pinputData);
while((ppathNode = (pathNode *)ellFirst(&pinputData->pathList))) {
ellDelete(&pinputData->pathList,&ppathNode->node);
free((void *)ppathNode->directory);
free((void *)ppathNode);
}
free(pvt);
}
static void inputAddPath(void *pvt, char *path)
{
inputData *pinputData = (inputData *)pvt;
ELLLIST *ppathList = &pinputData->pathList;
pathNode *ppathNode;
const char *pcolon;
const char *pdir;
int len;
int emptyName;
pdir = path;
/*an empty name at beginning, middle, or end means current directory*/
while(pdir && *pdir) {
emptyName = ((*pdir == ':') ? 1 : 0);
if(emptyName) ++pdir;
ppathNode = (pathNode *)calloc(1,sizeof(pathNode));
ellAdd(ppathList,&ppathNode->node);
if(!emptyName) {
pcolon = strchr(pdir,':');
len = (pcolon ? (pcolon - pdir) : strlen(pdir));
if(len>0) {
ppathNode->directory = (char *)calloc(len+1,sizeof(char));
strncpy(ppathNode->directory,pdir,len);
pdir = pcolon;
/*unless at end skip past first colon*/
if(pdir && *(pdir+1)!=0) ++pdir;
} else { /*must have been trailing : */
emptyName=1;
}
}
if(emptyName) {
ppathNode->directory = (char *)calloc(2,sizeof(char));
strcpy(ppathNode->directory,".");
}
}
return;
}
static void inputBegin(void *pvt,char *fileName)
{
inputData *pinputData = (inputData *)pvt;
inputCloseAllFiles(pinputData);
inputOpenFile(pinputData,fileName);
}
static char *inputNextLine(void *pvt)
{
inputData *pinputData = (inputData *)pvt;
inputFile *pinputFile;
char *pline;
while((pinputFile = (inputFile *)ellFirst(&pinputData->inputFileList))) {
pline = fgets(pinputData->inputBuffer,MAX_BUFFER_SIZE,pinputFile->fp);
if(pline) {
++pinputFile->lineNum;
return(pline);
}
inputCloseFile(pinputData);
}
return(0);
}
static void inputNewIncludeFile(void *pvt,char *name)
{
inputData *pinputData = (inputData *)pvt;
inputOpenFile(pinputData,name);
}
static void inputErrPrint(void *pvt)
{
inputData *pinputData = (inputData *)pvt;
inputFile *pinputFile;
fprintf(stderr,"input: %s which is ",pinputData->inputBuffer);
pinputFile = (inputFile *)ellFirst(&pinputData->inputFileList);
while(pinputFile) {
fprintf(stderr,"line %d of ",pinputFile->lineNum);
if(pinputFile->filename) {
fprintf(stderr," file %s\n",pinputFile->filename);
} else {
fprintf(stderr,"stdin:\n");
}
pinputFile = (inputFile *)ellNext(&pinputFile->node);
if(pinputFile) {
fprintf(stderr," which is included from ");
} else {
fprintf(stderr,"\n");
}
}
fprintf(stderr,"\n");
}
static void inputOpenFile(inputData *pinputData,char *filename)
{
ELLLIST *ppathList = &pinputData->pathList;
pathNode *ppathNode = 0;
inputFile *pinputFile;
char *fullname = 0;
FILE *fp = 0;
if(!filename) {
fp = stdin;
} else if((ellCount(ppathList)==0) || strchr(filename,'/')){
fp = fopen(filename,"r");
} else {
ppathNode = (pathNode *)ellFirst(ppathList);
while(ppathNode) {
fullname = calloc(strlen(filename)+strlen(ppathNode->directory) +2,
sizeof(char));
strcpy(fullname,ppathNode->directory);
strcat(fullname,"/");
strcat(fullname,filename);
fp = fopen(fullname,"r");
if(fp) break;
free((void *)fullname);
ppathNode = (pathNode *)ellNext(&ppathNode->node);
}
}
if(!fp) {
fprintf(stderr,"Could not open %s\n",filename);
inputErrPrint((void *)pinputData);
exit(1);
}
pinputFile = calloc(1,sizeof(inputFile));
if(ppathNode) {
pinputFile->filename = calloc(1,strlen(fullname)+1);
strcpy(pinputFile->filename,fullname);
free((void *)fullname);
} else if(filename) {
pinputFile->filename = calloc(1,strlen(filename)+1);
strcpy(pinputFile->filename,filename);
} else {
pinputFile->filename = calloc(1,strlen("stdin")+1);
strcpy(pinputFile->filename,"stdin");
}
pinputFile->fp = fp;
ellInsert(&pinputData->inputFileList,0,&pinputFile->node);
}
static void inputCloseFile(inputData *pinputData)
{
inputFile *pinputFile;
pinputFile = (inputFile *)ellFirst(&pinputData->inputFileList);
if(!pinputFile) return;
ellDelete(&pinputData->inputFileList,&pinputFile->node);
if(fclose(pinputFile->fp))
fprintf(stderr,"fclose failed: file %s\n",pinputFile->filename);
free(pinputFile->filename);
free(pinputFile);
}
static void inputCloseAllFiles(inputData *pinputData)
{
inputFile *pinputFile;
while((pinputFile=(inputFile *)ellFirst(&pinputData->inputFileList))){
inputCloseFile(pinputData);
}
}
/*start of code that handles substitution file*/
typedef enum {
tokenLBrace,tokenRBrace,tokenSeparater,tokenString,tokenEOF
}tokenType;
typedef struct subFile {
char *substitutionName;
FILE *fp;
int lineNum;
char inputBuffer[MAX_BUFFER_SIZE];
char *pnextChar;
tokenType token;
char string[MAX_BUFFER_SIZE];
} subFile;
typedef struct patternNode {
ELLNODE node;
char *var;
}patternNode;
typedef struct subInfo {
subFile *psubFile;
int isFile;
char *filename;
int isPattern;
ELLLIST patternList;
size_t size;
size_t curLength;
char *macroReplacements;
}subInfo;
static char *subGetNextLine(subFile *psubFile);
static tokenType subGetNextToken(subFile *psubFile);
static void subFileErrPrint(subFile *psubFile,char * message);
static void freeSubFile(subInfo *psubInfo);
static void freePattern(subInfo *psubInfo);
static void catMacroReplacements(subInfo *psubInfo,const char *value);
void freeSubFile(subInfo *psubInfo)
{
subFile *psubFile = psubInfo->psubFile;
if(psubFile->fp) {
if(fclose(psubFile->fp))
fprintf(stderr,"fclose failed on substitution file\n");
}
free((void *)psubFile);
free((void *)psubInfo->filename);
psubInfo->psubFile = 0;
}
void freePattern(subInfo *psubInfo)
{
patternNode *ppatternNode;
while((ppatternNode = (patternNode *)ellFirst(&psubInfo->patternList))) {
ellDelete(&psubInfo->patternList,&ppatternNode->node);
free(ppatternNode->var);
free(ppatternNode);
}
psubInfo->isPattern = 0;
}
static void substituteDestruct(void *pvt)
{
subInfo *psubInfo = (subInfo *)pvt;
freeSubFile(psubInfo);
freePattern(psubInfo);
free((void *)psubInfo);
return;
}
static void substituteOpen(void **ppvt,char *substitutionName)
{
subInfo *psubInfo;
subFile *psubFile;
FILE *fp;
psubInfo = calloc(1,sizeof(subInfo));
*ppvt = (void *)psubInfo;
psubFile = calloc(1,sizeof(subFile));
psubInfo->psubFile = psubFile;
ellInit(&psubInfo->patternList);
fp = fopen(substitutionName,"r");
if(!fp) {
fprintf(stderr,"Could not open %s\n",substitutionName);
exit(1);
}
psubFile->substitutionName = substitutionName;
psubFile->fp = fp;
psubFile->lineNum = 0;
psubFile->inputBuffer[0] = 0;
psubFile->pnextChar = &psubFile->inputBuffer[0];
subGetNextToken(psubFile);
return;
}
static int substituteGetNextSet(void *pvt,char **filename)
{
subInfo *psubInfo = (subInfo *)pvt;
subFile *psubFile = psubInfo->psubFile;
patternNode *ppatternNode;
*filename = 0;
while(psubFile->token==tokenSeparater) subGetNextToken(psubFile);
if(psubFile->token==tokenEOF) return(0);
if(psubFile->token==tokenString && strcmp(psubFile->string,"file")==0) {
psubInfo->isFile = 1;
if(subGetNextToken(psubFile)!=tokenString) {
subFileErrPrint(psubFile,"Expecting filename");
exit(1);
}
freePattern(psubInfo);
free((void *)psubInfo->filename);
if(psubFile->string[0]=='"'&&psubFile->string[strlen(psubFile->string)-1]=='"') {
psubFile->string[strlen(psubFile->string)-1]='\0';
psubInfo->filename = macEnvExpand(psubFile->string+1);
}
else {
psubInfo->filename = macEnvExpand(psubFile->string);
}
while(subGetNextToken(psubFile)==tokenSeparater);
if(psubFile->token!=tokenLBrace) {
subFileErrPrint(psubFile,"Expecting {");
exit(1);
}
subGetNextToken(psubFile);
}
*filename = psubInfo->filename;
while(psubFile->token==tokenSeparater) subGetNextToken(psubFile);
if(psubFile->token==tokenLBrace) return(1);
if(psubFile->token==tokenRBrace) return(0);
if(psubFile->token!=tokenString
|| strcmp(psubFile->string,"pattern")!=0) {
subFileErrPrint(psubFile,"Expecting pattern");
exit(1);
}
freePattern(psubInfo);
psubInfo->isPattern = 1;
while(subGetNextToken(psubFile)==tokenSeparater);
if(psubFile->token!=tokenLBrace) {
subFileErrPrint(psubFile,"Expecting {");
exit(1);
}
while(1) {
while(subGetNextToken(psubFile)==tokenSeparater);
if(psubFile->token!=tokenString) break;
ppatternNode = calloc(1,sizeof(patternNode));
ellAdd(&psubInfo->patternList,&ppatternNode->node);
ppatternNode->var = calloc(strlen(psubFile->string)+1,sizeof(char));
strcpy(ppatternNode->var,psubFile->string);
}
if(psubFile->token!=tokenRBrace) {
subFileErrPrint(psubFile,"Expecting }");
exit(1);
}
subGetNextToken(psubFile);
return(1);
}
static char *substituteGetReplacements(void *pvt)
{
subInfo *psubInfo = (subInfo *)pvt;
subFile *psubFile = psubInfo->psubFile;
patternNode *ppatternNode;
if(psubInfo->macroReplacements) psubInfo->macroReplacements[0] = 0;
psubInfo->curLength = 0;
while(psubFile->token==tokenSeparater) subGetNextToken(psubFile);
if(psubFile->token==tokenRBrace && psubInfo->isFile) {
psubInfo->isFile = 0;
free((void *)psubInfo->filename);
psubInfo->filename = 0;
freePattern(psubInfo);
subGetNextToken(psubFile);
return(0);
}
if(psubFile->token==tokenEOF) return(0);
if(psubFile->token!=tokenLBrace) return(0);
if(psubInfo->isPattern) {
int gotFirstPattern = 0;
while(subGetNextToken(psubFile)==tokenSeparater);
ppatternNode = (patternNode *)ellFirst(&psubInfo->patternList);
while(1) {
if(psubFile->token==tokenRBrace) {
if(ppatternNode)
subFileErrPrint(psubFile,"less values than patterns");
subGetNextToken(psubFile);
return(psubInfo->macroReplacements);
}
if(psubFile->token!=tokenString) {
subFileErrPrint(psubFile,"Illegal token");
exit(-1);
}
if(gotFirstPattern) catMacroReplacements(psubInfo,",");
gotFirstPattern = 1;
if(ppatternNode) {
catMacroReplacements(psubInfo,ppatternNode->var);
catMacroReplacements(psubInfo,"=");
catMacroReplacements(psubInfo,psubFile->string);
ppatternNode = (patternNode *)ellNext(&ppatternNode->node);
} else {
subFileErrPrint(psubFile,"more values than patterns");
}
while(subGetNextToken(psubFile)==tokenSeparater);
}
} else while(1) {
switch(subGetNextToken(psubFile)) {
case tokenRBrace:
subGetNextToken(psubFile);
return(psubInfo->macroReplacements);
case tokenSeparater:
catMacroReplacements(psubInfo,",");
break;
case tokenString:
catMacroReplacements(psubInfo,psubFile->string);
break;
default:
subFileErrPrint(psubFile,"Illegal token");
exit(1);
}
}
}
static char *subGetNextLine(subFile *psubFile)
{
char *pline;
pline = fgets(psubFile->inputBuffer,MAX_BUFFER_SIZE,psubFile->fp);
++psubFile->lineNum;
while(pline && psubFile->inputBuffer[0]=='#') {
pline = fgets(psubFile->inputBuffer,MAX_BUFFER_SIZE,psubFile->fp);
++psubFile->lineNum;
}
if(!pline) {
psubFile->token = tokenEOF;
psubFile->inputBuffer[0] = 0;
psubFile->pnextChar = 0;
return(0);
}
psubFile->pnextChar = &psubFile->inputBuffer[0];
return(&psubFile->inputBuffer[0]);
}
static void subFileErrPrint(subFile *psubFile,char * message)
{
fprintf(stderr,"substitution file %s line %d: %s",
psubFile->substitutionName,
psubFile->lineNum,psubFile->inputBuffer);
fprintf(stderr,"%s\n",message);
}
static tokenType subGetNextToken(subFile *psubFile)
{
char *p;
char *pto;
p = psubFile->pnextChar;
if(!p) { psubFile->token = tokenEOF; return(tokenEOF);}
if(*p==0 || *p=='\n' || *p=='#') {
p = subGetNextLine(psubFile);
if(!p) { psubFile->token = tokenEOF; return(tokenEOF);}
else { psubFile->token = tokenSeparater; return(tokenSeparater);}
}
while(isspace(*p)) p++;
if(*p=='{') {
psubFile->token = tokenLBrace;
psubFile->pnextChar = ++p;
return(tokenLBrace);
}
if(*p=='}') {
psubFile->token = tokenRBrace;
psubFile->pnextChar = ++p;
return(tokenRBrace);
}
if(*p==0 || isspace(*p) || *p==',') {
while(isspace(*p) || *p==',') p++;
psubFile->token = tokenSeparater;
psubFile->pnextChar = p;
return(tokenSeparater);
}
/*now handle quoted strings*/
if(*p=='"') {
pto = &psubFile->string[0];
*pto++ = *p++;
while(*p!='"') {
if(*p==0 || *p=='\n') {
subFileErrPrint(psubFile,"Strings must be on single line\n");
exit(1);
}
/*allow escape for imbeded quote*/
if((*p=='\\') && *(p+1)=='"') {
*pto++ = *p++;
*pto++ = *p++;
continue;
}
*pto++ = *p++;
}
*pto++ = *p++;
psubFile->pnextChar = p;
*pto = 0;
psubFile->token = tokenString;
return(tokenString);
}
/*Now take anything up to next non String token and not space*/
pto = &psubFile->string[0];
while(!isspace(*p) && (strspn(p,"\",{}")==0)) *pto++ = *p++;
*pto = 0;
psubFile->pnextChar = p;
psubFile->token = tokenString;
return(tokenString);
}
static void catMacroReplacements(subInfo *psubInfo,const char *value)
{
size_t len = strlen(value);
if(psubInfo->size <= (psubInfo->curLength + len)) {
size_t newsize = psubInfo->size + MAX_BUFFER_SIZE;
char *newbuf;
if(newsize <= psubInfo->curLength + len)
newsize = psubInfo->curLength + len + 1;
newbuf = calloc(1,newsize);
if(!newbuf) {
fprintf(stderr,"calloc failed for size %Zu\n",newsize);
exit(1);
}
if(psubInfo->macroReplacements) {
memcpy(newbuf,psubInfo->macroReplacements,psubInfo->curLength);
free(psubInfo->macroReplacements);
}
psubInfo->size = newsize;
psubInfo->macroReplacements = newbuf;
}
strcat(psubInfo->macroReplacements,value);
psubInfo->curLength += len;
}

433
src/dbtools/msi/msi.html Normal file
View File

@@ -0,0 +1,433 @@
<!DOCTYPE html public "-//w3c//dtd html 4.0 transitional//en">
<html>
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
</head>
<body>
<h1>msi: Macro Substitution and Include Tool</h1>
<blockquote>
<p>Version 1.5, 10th November, 2008</p>
</blockquote>
<h2>Introduction</h2>
<p>msi is a general purpose macro substitution/include tool. It accepts as input
an ascii template file. It looks for lines containing two reserved command
names: <tt>include</tt> and <tt>substitute</tt>. It also looks for and performs
substitutions on macros of the form $(var) and ${var}. It uses the macLib
routines from EPICS Base to perform the substitutions, so it also accepts the
default value and value definition syntax that macLib implements.</p>
<p>msi also allows substitutions to be specified via a separate substitution
file. This substitution file allows the same format as the substitution files
accepted by the EPICS IOC's dbLoadTemplate command.</p>
<h2>Command Syntax:</h2>
<pre>msi -V -o<i>outfile</i> -I<i>dir</i> -M<i>subs</i> -S<i>subfile</i> <i>template</i></pre>
<p>All parameters are optional. The -o, -I, -M, and -S switches may be
separated from their associated value string by spaces if desired. Output will
be written to stdout unless the -o option is given.</p>
<p>Switches have the following meanings:</p>
<dl>
<dt><tt>-V</tt></dt>
<dd>If this parameter is specified then any undefined macro discovered in
the template file which does not have an associated default value is
considered an error. An error message is generated and when msi terminates
it will do so with an exit status of 2.</dd>
<dt><tt>-o</tt> <i>file</i></dt>
<dd>Output will be written to the specifed file rather than to the standard
output.</dd>
<dt><tt>-I</tt> <i>dir</i></dt>
<dd>This parameter, which may be repeated, specifies a search path for
include commands. For example:
<blockquote>
<pre>msi -I /home/mrk/examples:. -I.. template</pre>
</blockquote>
specifies that all named files should be searched for in the following
locations in the order given:
<ol>
<li><tt>/home/mrk/examples</tt></li>
<li><tt>.</tt> (the current directory)</li>
<li><tt>..</tt> (the parent of the current directory)</li>
</ol>
</dd>
<dt><tt>-M</tt> <i>substitutions</i></dt>
<dd>This parameter specifies macro values for the template instance.
Multiple macro values can be specified in one substitution parameter, or in
multiple <tt>-M</tt> parameters. For example:
<blockquote>
<pre>msi -M "a=aval,b=bval" -Mc=cval template</pre>
</blockquote>
specifies that in the template file each occurrence of:
<dl>
<dd><tt>$(a)</tt> or <tt>${a}</tt> is replaced by <tt>aval</tt></dd>
<dd><tt>$(b)</tt> or <tt>${b}</tt> is replaced by <tt>bval</tt></dd>
<dd><tt>$(c)</tt> or <tt>${c}</tt> is replaced by <tt>cval</tt></dd>
</dl>
</dd>
<dt><tt>-S</tt> <i>subfile</i></dt>
<dd>The substitution file. See below for format.</dd>
<dt><i>template</i></dt>
<dd> The input file. If no file is specified then input is taken from
stdin, i.e. msi can be used as a filter. See below for a description of
commands that can be embedded in the template file.</dd>
</dl>
<p>It is not possible to display usage by just typing <tt>msi</tt> since
executing the command with no arguments is a valid command. To show usage
specify an illegal switch, e.g.</p>
<blockquote>
<pre>msi -help</pre>
</blockquote>
<h2>Exit Status</h2>
<dl>
<dt>0<dd>Success.
<dt>1<dd>Can't open/create file, or other I/O error.
<dt>2<dd>Undefined macros encountered with the <tt>-V</tt> option specified.
</dl>
<h2>Template File Format</h2>
<p>This file contains the text to be read and written to the output after macro
substitution is performed. If no file is given then input is read from stdin.
Variable instances to be substituted by macro values are expressed in the
template using the syntax <tt>$(</tt><i>name</i><tt>)</tt> or
<tt>${</tt><i>name</i><tt>}</tt>. If msi has been built with EPICS Base version
3.14.7 or later, the template can also provide default values to be used when a
macro has not been given a value, using the syntax
<tt>$(</tt><i>name</i><tt>=</tt><i>default</i><tt>)</tt> or
<tt>${</tt><i>name</i><tt>=</tt><i>default</i><tt>}</tt>.</p>
<p>For example, using the command</p>
<blockquote>
<pre>msi -M name=Marty template</pre>
</blockquote>
<p>where the file template contains</p>
<blockquote>
<pre>My name is $(name)
My age is $(age=none of your business)</pre>
</blockquote>
<p>results in this output:</p>
<blockquote>
<pre>My name is Marty
My age is none of your business</pre>
</blockquote>
<p>Macro variables and their default values can be expressed in terms of other
macros if necessary, to almost any level of complexity. Recursive definitions
will generate warning messages on stderr and result in undefined output.</p>
<p>The template file is read and processed one line at a time, where the
maximum length of a line before and/or after macro expansion is 1023 characters
&mdash; longer input or output lines will cause msi to fail. Within the context
of a single line, macro expansion does not occur when the variable instance
appears inside a single-quoted string, or where the dollar sign <tt>$</tt> is
preceded by a back-slash character <tt>\</tt>, but as with the standard Unix
shells, variables inside double quoted strings are expanded properly.</p>
<p>However neither back-slash characters nor quotes of either variety are
removed when generating the output file, so depending on what is being output
the single quote behaviour may not be useful and may even be a hinderance. It
cannot be disabled in the current version of msi.</p>
<h3>Template file commands</h3>
<p>In addition to the regular text and variable instances described above, the
template file may also contain commands which allow the insertion of other
template files and the ability to set macro values inside the template file
itself. These commands are:</p>
<blockquote>
<pre>include "file"
substitute "var=value,var=value,..."</pre>
</blockquote>
<p>Lines containing commands must be in one of these forms:</p>
<ul>
<li><tt>include "</tt><i>filename</i><tt>"</tt></li>
<li><tt>substitute "</tt><i>name1=value1, name2=value2, ...</i><tt>"</tt></li>
</ul>
<p>White space is allowed before and after the command verb, and after the
quoted string. If embedded quotes are needed, the backslash character
<tt>\</tt> can be used as an escape character. For example</p>
<blockquote>
<pre>substitute "a=\"val\""</pre>
</blockquote>
<p>specifies that (unless <tt>a</tt> is subsequently redefined) wherever a
<tt>$(a)</tt> macro appears in the template below this point, the text
<tt>"val"</tt> (including the double quote characters) will appear in the
output instead.</p>
<p>If a line does match either syntax above it is just passed to macLib for
processing without any notification. Thus the input line:</p>
<blockquote>
<pre>include "myfile" #include file</pre>
</blockquote>
<p>would just be passed to macLib, i.e. it would <em>not</em> be considered an
include command.</p>
<p>As an example of these commands, let the Unix command be:</p>
<blockquote>
<pre>msi template</pre>
</blockquote>
<p>and file includeFile contain:</p>
<blockquote>
<pre>first name is ${first}
family name is ${family}</pre>
</blockquote>
<p>and template is</p>
<blockquote>
<pre>substitute "first=Marty,family=Kraimer"
include "includeFile"
substitute "first=Irma,family=Kraimer"
include "includeFile"</pre>
</blockquote>
<p>then the following is written to the output.</p>
<blockquote>
<pre>first name is Marty
family name is Kraimer
first name is Irma
family name is Kraimer</pre>
</blockquote>
<p>Note that on an IOC when using the <tt>dbLoadTemplate</tt> command, the
template file does not support the <tt>substitute</tt> command, although
<tt>include</tt> is supported.</p>
<h2>Substitution File Format</h2>
<p>The optional substitution file has three formats: regular, pattern, and
dbTemplate format. We will discuss each separately.</p>
<h3>Regular format</h3>
<blockquote>
<pre>{var1=set1_val1, var2=set1_val2, ...}
{var2=set2_val2, var1=set2_val1, ...}
{var1=set3_val1, var2=set3_val2, ...}
{var2=set4_val2, var1=set4_val1, ...}</pre>
</blockquote>
<p>The template file is output with macro substitutions performed once for each
set of braces containing macro replacement values.</p>
<h3>Pattern format</h3>
<blockquote>
<pre>pattern {var1, var2, ...}
{set1_val1, set1_val2, ...}
{set2_val1, set2_val2, ...}
pattern {var2, var1, ...}
{set3_val2, set3_val1, ...}
{set4_val2, set4_val2, ...}</pre>
</blockquote>
<p>This produces the same result as the regular format example above.</p>
<h3>dbTemplate Format</h3>
<p>This format is an extension of the format accepted by the EPICS IOC command
<tt>dbLoadTemplate</tt>, and allows templates to be expanded on the host rather
by using dbLoadTemplate at IOC boot time.</p>
<blockquote>
<pre>file template {
<i>pattern format or regular format</i>
}
file "${WHERE}/template2" {
<i>pattern format or regular format</i>
}</pre>
</blockquote>
<p>For the dbTemplate format, the template filename does not have to be given
on the command line, and is usually specified in the substitutions file
instead. If a template filename is given on the command line it will override
the filenames listed in the substitutions files.</p>
<h3>Syntax for all formats</h3>
<p>A comment line may appear anywhere in a substitution file, and will be
ignored. A comment line is any line beginning with the character <tt>#</tt>,
which must be the very first character on the line.</p>
<p>For definitions within braces given in any of the file formats, a separator
must be given between items. A separator is either a comma, or one or more of
the standard white space characters (space, formfeed, newline, carriage return,
tab or vertical tab).</p>
<p>Each item within braces can be an alphanumeric token, or a double-quoted
string. A back-slash character <tt>\</tt> can be used to escape a quote
character needed inside a quoted string. These three sets of substitutions are
all equivalent:</p>
<blockquote>
<pre>{a=aa b=bb c="\"cc\""}
{b="bb",a=aa,c="\"cc\""}
{
c="\"cc\""
b=bb
a="aa"
}</pre>
</blockquote>
<p>Within a substitutions file, the file name may appear inside double
quotation marks; these are only required if the name contains environment
variable macros of the form ${ENV_VAR} or $(ENV_VAR), which will be expanded
before the file is opened. Environment variable macro expansion is only
available when msi has been built using EPICS base R3.14.3 or newer.</p>
<h3>Regular substitution example</h3>
<p>Let the command be:</p>
<blockquote>
<pre>msi -S substitute template</pre>
</blockquote>
<p>The file <tt>template</tt> contains</p>
<blockquote>
<pre>first name is ${first}
family name is ${family}</pre>
</blockquote>
<p> and the file <tt>substitute</tt> is</p>
<blockquote>
<pre>{first=Marty,family=Kraimer}
{first=Irma,family=Kraimer}</pre>
</blockquote>
<p>The following is the output produced:</p>
<blockquote>
<pre>first name is Marty
family name is Kraimer
first name is Irma
family name is Kraimer</pre>
</blockquote>
<h3>Pattern substitution example</h3>
<p>Let the command be:</p>
<blockquote>
<pre>msi -S pattern template</pre>
</blockquote>
<p>The file <tt>pattern</tt> contains</p>
<blockquote>
<pre>pattern {first,last}
{Marty,Kraimer}
{Irma,Kraimer}</pre>
</blockquote>
<p>and <tt>template</tt> is the same as the previous example:</p>
<blockquote>
<pre>first name is ${first}
family name is ${family}</pre>
</blockquote>
<p>This is the output:</p>
<blockquote>
<pre>first name is Marty
family name is Kraimer
first name is Irma
family name is Kraimer</pre>
</blockquote>
<h3>dbTemplate example</h3>
Let the command be
<blockquote>
<pre>msi -S xxx.substitutions</pre>
</blockquote>
<tt>xxx.substitutions</tt> is
<blockquote>
<pre>file template {
pattern {first,last}
{Marty,Kraimer}
{Irma,Kraimer}
pattern {last,first}
{Smith,Bill}
{Smith,Mary}
}
file template {
{first=Marty,last=Kraimer}
{first=Irma,last=Kraimer}
}</pre>
</blockquote>
<tt>template</tt> is the same as in the previous example..
<p>The following is written to the output</p>
<blockquote>
<pre>first name is Marty
family name is Kraimer
first name is Irma
family name is Kraimer
first name is Bill
last name is Smith
first name is Mary
last name is Smith
first name is Marty
family name is Kraimer
first name is Irma
family name is Kraimer</pre>
</blockquote>
<h2>Building msi</h2>
<p>msi is built as a normal extensions product, and can be built with any
version of EPICS Base from 3.13.0beta11 onwards. Base version 3.14.3 or later
is required if the ability to expand environment variables in filenames is
needed. Base version 3.14.7 or later is required to allow the use of default
macro values in template files.</p>
</body>
</html>

9
src/dbtools/msi/pattern Normal file
View File

@@ -0,0 +1,9 @@
#test of a pattern
pattern {a,b}
{xxx,"yyy"}
{zzz , ttt}
{
vvv
zzz
}

View File

@@ -0,0 +1,5 @@
{a=111,b="222"}
{ a = aaa , b=bbb}
{a= aaa
b= bbb
}

4
src/dbtools/msi/template Normal file
View File

@@ -0,0 +1,4 @@
# comment line
$(a)
this is a test $(b)
$(d)

13
src/dbtools/msi/testfile Normal file
View File

@@ -0,0 +1,13 @@
This is a test file
This is second line
include "testfile1"
a=${a}
b=$(b)
substitute "a=aaa,b=bbb"
a=${a}
b=$(b)
substitute "a=\"aa\""
${a}
$(a)
This is last line
%report

View File

@@ -0,0 +1,4 @@
This is testfile1
in testfile1 $(a)=a
in testfile1 $(b)=b
end of testfile1