Ralph Lange has proposed merging lp:~ralph-lange/epics-base/msi-join into lp:epics-base.
Requested reviews:
EPICS Core Developers (epics-core)
Andrew converted the extensions msi CVS repo into a Bazaar repo, I included it in base/src/dbtools.
This branch builds msi as part of dbtools and - in CONFIG_BASE - points MSI to it as a default.
I moved the files that obviously are intended for testing into a test subdir, but I have no idea what tests should be run with them.
I tested that msi basically works with a slightly modified example app.
It still is msi as converted from extensions CVS - none of the recently suggested changes have been applied yet, as I thought that should better be done under Bazaar.
Remaining issues:
Should host-side blow up be included in the example app?
What about the documentation? The App-Dev-Guide? Is that still officially a Frame document?
--
https://code.launchpad.net/~ralph-lange/epics-base/msi-join/+merge/33439
Your team EPICS Core Developers is requested to review the proposed merge of lp:~ralph-lange/epics-base/msi-join into lp:epics-base.
=== modified file 'configure/CONFIG_BASE'
--- configure/CONFIG_BASE 2008-08-29 21:07:04 +0000
+++ configure/CONFIG_BASE 2010-08-23 19:50:55 +0000
@@ -110,7 +110,7 @@
endif
ifndef MSI
-MSI = msi
+MSI = $(EPICS_BASE_HOST_BIN)/msi
endif
=== modified file 'src/dbtools/Makefile'
--- src/dbtools/Makefile 2009-03-11 19:11:21 +0000
+++ src/dbtools/Makefile 2010-08-23 19:50:55 +0000
@@ -14,17 +14,20 @@
INC += dbLoadTemplate.h
INC += dbtoolsIocRegister.h
+LIBRARY_IOC = dbtoolsIoc
+
LIB_SRCS += dbLoadTemplate.c
LIB_SRCS += dbtoolsIocRegister.c
-
-LIBRARY_IOC = dbtoolsIoc
-
dbtoolsIoc_LIBS = dbIoc dbStaticIoc Com
-
dbtoolsIoc_RCS = dbtoolsIoc.rc
-
HTMLS += dbLoadTemplate.html
+PROD_HOST = msi
+
+msi_SRCS = msi.c
+msi_LIBS += Com
+HTMLS = msi.html
+
# For R3.13 compatibility only
ifeq ($(strip $(COMPAT_313)),YES)
OBJLIB_vxWorks=dbtoolsIoc
=== added file 'src/dbtools/msi.c'
--- src/dbtools/msi.c 1970-01-01 00:00:00 +0000
+++ src/dbtools/msi.c 2010-08-23 19:50:55 +0000
@@ -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;
+}
=== added file 'src/dbtools/msi.html'
--- src/dbtools/msi.html 1970-01-01 00:00:00 +0000
+++ src/dbtools/msi.html 2010-08-23 19:50:55 +0000
@@ -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
+— 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>
=== added directory 'src/dbtools/test'
=== added file 'src/dbtools/test/pattern'
--- src/dbtools/test/pattern 1970-01-01 00:00:00 +0000
+++ src/dbtools/test/pattern 2010-08-23 19:50:55 +0000
@@ -0,0 +1,9 @@
+#test of a pattern
+ pattern {a,b}
+{xxx,"yyy"}
+ {zzz , ttt}
+
+{
+ vvv
+ zzz
+}
=== added file 'src/dbtools/test/substitute'
--- src/dbtools/test/substitute 1970-01-01 00:00:00 +0000
+++ src/dbtools/test/substitute 2010-08-23 19:50:55 +0000
@@ -0,0 +1,5 @@
+{a=111,b="222"}
+{ a = aaa , b=bbb}
+{a= aaa
+b= bbb
+}
=== added file 'src/dbtools/test/template'
--- src/dbtools/test/template 1970-01-01 00:00:00 +0000
+++ src/dbtools/test/template 2010-08-23 19:50:55 +0000
@@ -0,0 +1,4 @@
+# comment line
+$(a)
+this is a test $(b)
+$(d)
=== added file 'src/dbtools/test/testfile'
--- src/dbtools/test/testfile 1970-01-01 00:00:00 +0000
+++ src/dbtools/test/testfile 2010-08-23 19:50:55 +0000
@@ -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
=== added file 'src/dbtools/test/testfile1'
--- src/dbtools/test/testfile1 1970-01-01 00:00:00 +0000
+++ src/dbtools/test/testfile1 2010-08-23 19:50:55 +0000
@@ -0,0 +1,4 @@
+This is testfile1
+in testfile1 $(a)=a
+in testfile1 $(b)=b
+end of testfile1
- Navigate by Date:
- Prev:
Re: Fixed: Segfaults in 3.14 branch since merging ca-over-tcp Ralph Lange
- Next:
[Merge] lp:~ralph-lange/epics-base/cac-dtor-racecond-fix into lp:epics-base Ralph Lange
- Index:
2002
2003
2004
2005
2006
2007
2008
2009
<2010>
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
- Navigate by Thread:
- Prev:
devLib extensions review and testing request Davidsaver, Michael
- Next:
[Merge] lp:~ralph-lange/epics-base/cac-dtor-racecond-fix into lp:epics-base Ralph Lange
- Index:
2002
2003
2004
2005
2006
2007
2008
2009
<2010>
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
|