This is Unofficial EPICS BASE Doxygen Site
msi.cpp
Go to the documentation of this file.
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 <string>
13 #include <list>
14 
15 #include <stdlib.h>
16 #include <stddef.h>
17 #include <stdio.h>
18 #include <string.h>
19 #include <ctype.h>
20 #include <errno.h>
21 
22 #include <dbDefs.h>
23 #include <macLib.h>
24 #include <errlog.h>
25 #include <epicsString.h>
26 #include <osiFileName.h>
27 #include <osiUnistd.h>
28 
29 #define MAX_BUFFER_SIZE 4096
30 #define MAX_DEPS 1024
31 
32 #if 0
33 /* Debug Tracing */
34 int din = 0;
35 #define ENTER fprintf(stderr, "%*sEntering %s\n", 2*din++, "", __FUNCTION__)
36 
37 #define STEP(s) fprintf(stderr, "%*s%s: %s\n", 2*din, "", __FUNCTION__, s)
38 #define STEPS(s, v) fprintf(stderr, "%*s%s: %s '%s'\n", 2*din, "", __FUNCTION__, s, v)
39 #define STEPD(s, v) fprintf(stderr, "%*s%s: %s %d\n", 2*din, "", __FUNCTION__, s, v)
40 
41 #define EXIT fprintf(stderr, "%*s%s: Returning\n", 2*din--, "", __FUNCTION__)
42 #define EXITD(r) fprintf(stderr, "%*s%s: Returning %d\n", 2*din--, "", __FUNCTION__, r)
43 #define EXITS(r) fprintf(stderr, "%*s%s: Returning '%s'\n", 2*din--, "", __FUNCTION__, r)
44 #else
45 #define ENTER
46 
47 #define STEP(s)
48 #define STEPS(s, v)
49 #define STEPD(s, v)
50 
51 #define EXIT
52 #define EXITD(r)
53 #define EXITS(r)
54 #endif
55 
56 
57 /* Module to read the template files */
58 typedef struct inputData inputData;
59 
60 static void inputConstruct(inputData **ppvt);
61 static void inputDestruct(inputData * const pvt);
62 static void inputAddPath(inputData * const pvt, const char * const pval);
63 static void inputBegin(inputData * const pvt, const char * const fileName);
64 static char *inputNextLine(inputData * const pvt);
65 static void inputNewIncludeFile(inputData * const pvt, const char * const name);
66 static void inputErrPrint(const inputData * const pvt);
67 
68 /* Module to read the substitution file */
69 typedef struct subInfo subInfo;
70 
71 static void substituteOpen(subInfo **ppvt, const std::string& substitutionName);
72 static void substituteDestruct(subInfo * const pvt);
73 static bool substituteGetNextSet(subInfo * const pvt, char **filename);
74 static bool substituteGetGlobalSet(subInfo * const pvt);
75 static const char *substituteGetReplacements(subInfo * const pvt);
76 static const char *substituteGetGlobalReplacements(subInfo * const pvt);
77 
78 /* Forward references to local routines */
79 static void usageExit(const int status);
80 static void abortExit(const int status);
81 static void addMacroReplacements(MAC_HANDLE * const macPvt,
82  const char * const pval);
83 static void makeSubstitutions(inputData * const inputPvt,
84  MAC_HANDLE * const macPvt,
85  const char * const templateName);
86 
87 /*Global variables */
88 static int opt_V = 0;
89 static bool opt_D = false;
90 
91 static char *outFile = 0;
92 static int numDeps = 0, depHashes[MAX_DEPS];
93 
94 
95 int main(int argc,char **argv)
96 {
97  inputData *inputPvt;
98  MAC_HANDLE *macPvt;
99  char *pval;
100  std::string substitutionName;
101  char *templateName = 0;
102  bool localScope = true;
103 
104  inputConstruct(&inputPvt);
105  macCreateHandle(&macPvt, 0);
106  while ((argc > 1) && (argv[1][0] == '-')) {
107  int narg = (strlen(argv[1]) == 2) ? 2 : 1;
108  pval = (narg == 1) ? (argv[1] + 2) : argv[2];
109 
110  if (strncmp(argv[1], "-I", 2) == 0) {
111  inputAddPath(inputPvt, pval);
112  }
113  else if (strcmp(argv[1], "-D") == 0) {
114  opt_D = true;
115  narg = 1; /* no argument for this option */
116  }
117  else if(strncmp(argv[1], "-o", 2) == 0) {
118  outFile = epicsStrDup(pval);
119  }
120  else if(strncmp(argv[1], "-M", 2) == 0) {
121  addMacroReplacements(macPvt, pval);
122  }
123  else if(strncmp(argv[1], "-S", 2) == 0) {
124  substitutionName = pval;
125  }
126  else if (strcmp(argv[1], "-V") == 0) {
127  opt_V = 1;
128  narg = 1; /* no argument for this option */
129  }
130  else if (strcmp(argv[1], "-g") == 0) {
131  localScope = false;
132  narg = 1; /* no argument for this option */
133  }
134  else if (strcmp(argv[1], "-h") == 0) {
135  usageExit(0);
136  }
137  else {
138  fprintf(stderr, "msi: Bad argument \"%s\"\n", argv[1]);
139  usageExit(1);
140  }
141 
142  argc -= narg;
143  for (int i = 1; i < argc; i++)
144  argv[i] = argv[i + narg];
145  }
146 
147  if (!opt_V)
148  macSuppressWarning(macPvt, 1);
149 
150  if (argc > 2) {
151  fprintf(stderr, "msi: Too many arguments\n");
152  usageExit(1);
153  }
154 
155  if (opt_D) {
156  if (!outFile) {
157  fprintf(stderr, "msi: Option -D requires -o for Makefile target\n");
158  exit(1);
159  }
160  printf("%s:", outFile);
161  }
162  else if (outFile && freopen(outFile, "w", stdout) == NULL) {
163  fprintf(stderr, "msi: Can't open %s for writing: %s\n",
164  outFile, strerror(errno));
165  exit(1);
166  }
167 
168  if (argc == 2)
169  templateName = epicsStrDup(argv[1]);
170 
171  if (substitutionName.empty()) {
172  STEP("Single template+substitutions file");
173  makeSubstitutions(inputPvt, macPvt, templateName);
174  }
175  else {
176  subInfo *substitutePvt;
177  char *filename = 0;
178  bool isGlobal, isFile;
179 
180  STEPS("Substitutions from file", substitutionName.c_str());
181  substituteOpen(&substitutePvt, substitutionName);
182  do {
183  isGlobal = substituteGetGlobalSet(substitutePvt);
184  if (isGlobal) {
185  STEP("Handling global macros");
186  const char *macStr = substituteGetGlobalReplacements(substitutePvt);
187  if (macStr)
188  addMacroReplacements(macPvt, macStr);
189  }
190  else if ((isFile = substituteGetNextSet(substitutePvt, &filename))) {
191  if (templateName)
192  filename = templateName;
193  if (!filename) {
194  fprintf(stderr, "msi: No template file\n");
195  usageExit(1);
196  }
197 
198  STEPS("Handling template file", filename);
199  const char *macStr;
200  while ((macStr = substituteGetReplacements(substitutePvt))) {
201  if (localScope)
202  macPushScope(macPvt);
203 
204  addMacroReplacements(macPvt, macStr);
205  makeSubstitutions(inputPvt, macPvt, filename);
206 
207  if (localScope)
208  macPopScope(macPvt);
209  }
210  }
211  } while (isGlobal || isFile);
212  substituteDestruct(substitutePvt);
213  }
214  macDeleteHandle(macPvt);
215  errlogFlush(); // macLib calls errlogPrintf()
216  inputDestruct(inputPvt);
217  if (opt_D) {
218  printf("\n");
219  }
220  fflush(stdout);
221  free(templateName);
222  return opt_V & 2;
223 }
224 
225 void usageExit(const int status)
226 {
227  fprintf(stderr,
228  "Usage: msi [options] [template]\n"
229  " stdin is used if neither template nor substitution file is given\n"
230  " options:\n"
231  " -h Print this help message\n"
232  " -D Output file dependencies, not substitutions\n"
233  " -V Undefined macros generate an error\n"
234  " -g All macros have global scope\n"
235  " -o<FILE> Send output to <FILE>\n"
236  " -I<DIR> Add <DIR> to include file search path\n"
237  " -M<SUBST> Add <SUBST> to (global) macro definitions\n"
238  " (<SUBST> takes the form VAR=VALUE,...)\n"
239  " -S<FILE> Expand the substitutions in FILE\n");
240  exit(status);
241 }
242 
243 void abortExit(const int status)
244 {
245  if (outFile) {
246  fclose(stdout);
247  unlink(outFile);
248  }
249  exit(status);
250 }
251 
252 static void addMacroReplacements(MAC_HANDLE * const macPvt,
253  const char * const pval)
254 {
255  char **pairs;
256  long status;
257 
258  status = macParseDefns(macPvt, pval, &pairs);
259  if (status == -1) {
260  fprintf(stderr, "msi: Error from macParseDefns\n");
261  usageExit(1);
262  }
263  if (status) {
264  status = macInstallMacros(macPvt, pairs);
265  if (!status) {
266  fprintf(stderr, "Error from macInstallMacros\n");
267  usageExit(1);
268  }
269  free(pairs);
270  }
271 }
272 
274 static const char *cmdNames[] = {"include","substitute"};
275 
276 static void makeSubstitutions(inputData * const inputPvt,
277  MAC_HANDLE * const macPvt,
278  const char * const templateName)
279 {
280  char *input;
281  static char buffer[MAX_BUFFER_SIZE];
282  int n;
283 
284  ENTER;
285  inputBegin(inputPvt, templateName);
286  while ((input = inputNextLine(inputPvt))) {
287  int expand=1;
288  char *p;
289  char *command = 0;
290 
291  p = input;
292  /*skip whitespace at beginning of line*/
293  while (*p && (isspace((int) *p))) ++p;
294 
295  /*Look for i or s */
296  if (*p && (*p=='i' || *p=='s'))
297  command = p;
298 
299  if (command) {
300  char *pstart;
301  char *pend;
302  int cmdind=-1;
303  int i;
304 
305  for (i = 0; i < NELEMENTS(cmdNames); i++) {
306  if (strstr(command, cmdNames[i])) {
307  cmdind = i;
308  }
309  }
310  if (cmdind < 0) goto endcmd;
311  p = command + strlen(cmdNames[cmdind]);
312  /*skip whitespace after command*/
313  while (*p && (isspace((int) *p))) ++p;
314  /*Next character must be quote*/
315  if ((*p == 0) || (*p != '"')) goto endcmd;
316  pstart = ++p;
317  /*Look for end quote*/
318  while (*p && (*p != '"')) {
319  /*allow escape for embeded quote*/
320  if ((p[0] == '\\') && p[1] == '"') {
321  p += 2;
322  continue;
323  }
324  else {
325  if (*p == '"') break;
326  }
327  ++p;
328  }
329  pend = p;
330  if (*p == 0) goto endcmd;
331  /*skip quote and any trailing blanks*/
332  while (*++p == ' ') ;
333  if (*p != '\n' && *p != 0) goto endcmd;
334  std::string copy = std::string(pstart, pend);
335 
336  switch(cmdind) {
337  case cmdInclude:
338  inputNewIncludeFile(inputPvt, copy.c_str());
339  break;
340 
341  case cmdSubstitute:
342  addMacroReplacements(macPvt, copy.c_str());
343  break;
344 
345  default:
346  fprintf(stderr, "msi: Logic error in makeSubstitutions\n");
347  inputErrPrint(inputPvt);
348  abortExit(1);
349  }
350  expand = 0;
351  }
352 
353 endcmd:
354  if (expand && !opt_D) {
355  STEP("Expanding to output stream");
356  n = macExpandString(macPvt, input, buffer, MAX_BUFFER_SIZE - 1);
357  fputs(buffer, stdout);
358  if (opt_V == 1 && n < 0) {
359  fprintf(stderr, "msi: Error - undefined macros present\n");
360  opt_V++;
361  }
362  }
363  }
364  EXIT;
365 }
366 
367 typedef struct inputFile {
368  std::string filename;
369  FILE *fp;
370  int lineNum;
371 } inputFile;
372 
373 struct inputData {
374  std::list<inputFile> inputFileList;
375  std::list<std::string> pathList;
376  char inputBuffer[MAX_BUFFER_SIZE];
377  inputData() { memset(inputBuffer, 0, sizeof(inputBuffer) * sizeof(inputBuffer[0])); };
378 };
379 
380 static void inputOpenFile(inputData *pinputData, const char * const filename);
381 static void inputCloseFile(inputData *pinputData);
382 static void inputCloseAllFiles(inputData *pinputData);
383 
384 static void inputConstruct(inputData **ppvt)
385 {
386  *ppvt = new inputData;
387 }
388 
389 static void inputDestruct(inputData * const pinputData)
390 {
391  inputCloseAllFiles(pinputData);
392  delete(pinputData);
393 }
394 
395 static void inputAddPath(inputData * const pinputData, const char * const path)
396 {
397  const char *pcolon;
398  const char *pdir;
399  size_t len;
400  const char sep = *OSI_PATH_LIST_SEPARATOR;
401 
402  ENTER;
403  pdir = path;
404  /*an empty name at beginning, middle, or end means current directory*/
405  while (pdir && *pdir) {
406  bool emptyName = (*pdir == sep);
407  if (emptyName) ++pdir;
408 
409  std::string directory;
410  if (!emptyName) {
411  pcolon = strchr(pdir, sep);
412  len = (pcolon ? (pcolon - pdir) : strlen(pdir));
413  if (len > 0) {
414  directory = std::string(pdir, len);
415  pdir = pcolon;
416  /*unless at end skip past first colon*/
417  if (pdir && *(pdir + 1) != 0) ++pdir;
418  }
419  else { /*must have been trailing : */
420  emptyName = true;
421  }
422  }
423 
424  if (emptyName) {
425  directory = ".";
426  }
427 
428  pinputData->pathList.push_back(directory);
429  }
430  EXIT;
431 }
432 
433 static void inputBegin(inputData * const pinputData, const char * const fileName)
434 {
435  ENTER;
436  inputCloseAllFiles(pinputData);
437  inputOpenFile(pinputData, fileName);
438  EXIT;
439 }
440 
441 static char *inputNextLine(inputData * const pinputData)
442 {
443  std::list<inputFile>& inFileList = pinputData->inputFileList;
444 
445  ENTER;
446  while (!inFileList.empty()) {
447  inputFile& inFile = inFileList.front();
448  char *pline = fgets(pinputData->inputBuffer, MAX_BUFFER_SIZE, inFile.fp);
449  if (pline) {
450  ++inFile.lineNum;
451  EXITS(pline);
452  return pline;
453  }
454  inputCloseFile(pinputData);
455  }
456  EXITD(0);
457  return 0;
458 }
459 
460 static void inputNewIncludeFile(inputData * const pinputData,
461  const char * const name)
462 {
463  ENTER;
464  inputOpenFile(pinputData,name);
465  EXIT;
466 }
467 
468 static void inputErrPrint(const inputData *const pinputData)
469 {
470  ENTER;
471  fprintf(stderr, "input: '%s' at ", pinputData->inputBuffer);
472  const std::list<inputFile>& inFileList = pinputData->inputFileList;
473  std::list<inputFile>::const_iterator inFileIt = inFileList.begin();
474  while (inFileIt != inFileList.end()) {
475  fprintf(stderr, "line %d of ", inFileIt->lineNum);
476 
477  if (!inFileIt->filename.empty()) {
478  fprintf(stderr, " file %s\n", inFileIt->filename.c_str());
479  }
480  else {
481  fprintf(stderr, "stdin:\n");
482  }
483 
484  if (++inFileIt != inFileList.end()) {
485  fprintf(stderr, " included from ");
486  }
487  else {
488  fprintf(stderr,"\n");
489  }
490  }
491  fprintf(stderr,"\n");
492  EXIT;
493 }
494 
495 static void inputOpenFile(inputData *pinputData, const char * const filename)
496 {
497  std::list<std::string>& pathList = pinputData->pathList;
498  std::list<std::string>::iterator pathIt = pathList.end();
499  std::string fullname;
500  FILE *fp = 0;
501 
502  ENTER;
503  if (!filename) {
504  STEP("Using stdin");
505  fp = stdin;
506  }
507  else if (pathList.empty() || strchr(filename, '/')){
508  STEPS("Opening ", filename);
509  fp = fopen(filename, "r");
510  }
511  else {
512  pathIt = pathList.begin();
513  while(pathIt != pathList.end()) {
514  fullname = *pathIt + "/" + filename;
515  STEPS("Trying", filename);
516  fp = fopen(fullname.c_str(), "r");
517  if (fp)
518  break;
519  ++pathIt;
520  }
521  }
522 
523  if (!fp) {
524  fprintf(stderr, "msi: Can't open file '%s'\n", filename);
525  inputErrPrint(pinputData);
526  abortExit(1);
527  }
528 
529  STEP("File opened");
530  inputFile inFile = inputFile();
531 
532  if (pathIt != pathList.end()) {
533  inFile.filename = fullname;
534  }
535  else if (filename) {
536  inFile.filename = filename;
537  }
538  else {
539  inFile.filename = "stdin";
540  }
541 
542  if (opt_D) {
543  int hash = epicsStrHash(inFile.filename.c_str(), 12345);
544  int i = 0;
545  int match = 0;
546 
547  while (i < numDeps) {
548  if (hash == depHashes[i++]) {
549  match = 1;
550  break;
551  }
552  }
553  if (!match) {
554  const char *wrap = numDeps ? " \\\n" : "";
555 
556  printf("%s %s", wrap, inFile.filename.c_str());
557  if (numDeps < MAX_DEPS) {
558  depHashes[numDeps++] = hash;
559  }
560  else {
561  fprintf(stderr, "msi: More than %d dependencies!\n", MAX_DEPS);
562  depHashes[0] = hash;
563  }
564  }
565  }
566 
567  inFile.fp = fp;
568  pinputData->inputFileList.push_front(inFile);
569  EXIT;
570 }
571 
572 static void inputCloseFile(inputData *pinputData)
573 {
574  std::list<inputFile>& inFileList = pinputData->inputFileList;
575  ENTER;
576  if(!inFileList.empty()) {
577  inputFile& inFile = inFileList.front();
578  if (fclose(inFile.fp))
579  fprintf(stderr, "msi: Can't close input file '%s'\n", inFile.filename.c_str());
580  inFileList.erase(inFileList.begin());
581  }
582  EXIT;
583 }
584 
585 static void inputCloseAllFiles(inputData *pinputData)
586 {
587  ENTER;
588  const std::list<inputFile>& inFileList = pinputData->inputFileList;
589  while(!inFileList.empty()) {
590  inputCloseFile(pinputData);
591  }
592  EXIT;
593 }
594 
595 /*start of code that handles substitution file*/
596 typedef enum {
598 } tokenType;
599 
600 typedef struct subFile {
601  std::string substitutionName;
602  FILE *fp;
603  int lineNum;
604  char inputBuffer[MAX_BUFFER_SIZE];
605  char *pnextChar;
607  char string[MAX_BUFFER_SIZE];
608 } subFile;
609 
610 struct subInfo {
612  bool isFile;
613  char *filename;
614  bool isPattern;
615  std::list<std::string> patternList;
616  std::string macroReplacements;
617  subInfo() : psubFile(NULL), isFile(false), filename(NULL),
618  isPattern(false) {};
619 };
620 
621 static char *subGetNextLine(subFile *psubFile);
622 static tokenType subGetNextToken(subFile *psubFile);
623 static void subFileErrPrint(subFile *psubFile, const char * message);
624 static void freeSubFile(subInfo *psubInfo);
625 static void freePattern(subInfo *psubInfo);
626 static void catMacroReplacements(subInfo *psubInfo,const char *value);
627 
628 void freeSubFile(subInfo *psubInfo)
629 {
630  subFile *psubFile = psubInfo->psubFile;
631 
632  ENTER;
633  if (psubFile->fp) {
634  if (fclose(psubFile->fp))
635  fprintf(stderr, "msi: Can't close substitution file\n");
636  }
637  delete(psubFile);
638  free(psubInfo->filename);
639  psubInfo->psubFile = 0;
640  EXIT;
641 }
642 
643 void freePattern(subInfo *psubInfo)
644 {
645  ENTER;
646  psubInfo->patternList.clear();
647  psubInfo->isPattern = false;
648  EXIT;
649 }
650 
651 static void substituteDestruct(subInfo * const psubInfo)
652 {
653  ENTER;
654  freeSubFile(psubInfo);
655  freePattern(psubInfo);
656  delete(psubInfo);
657  EXIT;
658 }
659 
660 static void substituteOpen(subInfo **ppvt, const std::string& substitutionName)
661 {
662  subInfo *psubInfo;
663  subFile *psubFile;
664  FILE *fp;
665 
666  ENTER;
667  psubInfo = new subInfo;
668  *ppvt = psubInfo;
669  psubFile = new subFile;
670  psubInfo->psubFile = psubFile;
671 
672  fp = fopen(substitutionName.c_str(), "r");
673  if (!fp) {
674  fprintf(stderr, "msi: Can't open file '%s'\n", substitutionName.c_str());
675  abortExit(1);
676  }
677 
678  psubFile->substitutionName = substitutionName;
679  psubFile->fp = fp;
680  psubFile->lineNum = 1;
681  psubFile->inputBuffer[0] = 0;
682  psubFile->pnextChar = &psubFile->inputBuffer[0];
683  subGetNextToken(psubFile);
684  EXIT;
685 }
686 
687 static bool substituteGetGlobalSet(subInfo * const psubInfo)
688 {
689  subFile *psubFile = psubInfo->psubFile;
690 
691  ENTER;
692  while (psubFile->token == tokenSeparator)
693  subGetNextToken(psubFile);
694 
695  if (psubFile->token == tokenString &&
696  strcmp(psubFile->string, "global") == 0) {
697  subGetNextToken(psubFile);
698  EXITD(1);
699  return true;
700  }
701 
702  EXITD(0);
703  return false;
704 }
705 
706 static bool substituteGetNextSet(subInfo * const psubInfo,char **filename)
707 {
708  subFile *psubFile = psubInfo->psubFile;
709 
710  ENTER;
711  *filename = 0;
712  while (psubFile->token == tokenSeparator)
713  subGetNextToken(psubFile);
714 
715  if (psubFile->token == tokenEOF) {
716  EXITD(0);
717  return false;
718  }
719 
720  if (psubFile->token == tokenString &&
721  strcmp(psubFile->string, "file") == 0) {
722  size_t len;
723 
724  STEP("Parsed 'file'");
725  psubInfo->isFile = true;
726  if (subGetNextToken(psubFile) != tokenString) {
727  subFileErrPrint(psubFile, "Parse error, expecting a filename");
728  abortExit(1);
729  }
730 
731  freePattern(psubInfo);
732  free(psubInfo->filename);
733 
734  len = strlen(psubFile->string);
735  if (psubFile->string[0] == '"' &&
736  psubFile->string[len - 1] == '"') {
737  psubFile->string[len - 1] = '\0';
738  psubInfo->filename = macEnvExpand(psubFile->string + 1);
739  }
740  else
741  psubInfo->filename = macEnvExpand(psubFile->string);
742  STEPS("Parsed filename", psubInfo->filename);
743 
744  while (subGetNextToken(psubFile) == tokenSeparator);
745 
746  if (psubFile->token != tokenLBrace) {
747  subFileErrPrint(psubFile, "Parse error, expecting '{'");
748  abortExit(1);
749  }
750  STEP("Parsed '{'");
751  subGetNextToken(psubFile);
752  }
753  *filename = psubInfo->filename;
754 
755  while (psubFile->token == tokenSeparator)
756  subGetNextToken(psubFile);
757 
758  if (psubFile->token == tokenLBrace) {
759  EXITD(1);
760  return true;
761  }
762 
763  if (psubFile->token == tokenRBrace) {
764  subFileErrPrint(psubFile, "Parse error, unexpected '}'");
765  abortExit(1);
766  }
767 
768  if (psubFile->token != tokenString ||
769  strcmp(psubFile->string, "pattern") != 0) {
770  subFileErrPrint(psubFile, "Parse error, expecting 'pattern'");
771  abortExit(1);
772  }
773 
774  STEP("Parsed 'pattern'");
775  freePattern(psubInfo);
776  psubInfo->isPattern = true;
777 
778  while (subGetNextToken(psubFile) == tokenSeparator);
779 
780  if (psubFile->token != tokenLBrace) {
781  subFileErrPrint(psubFile, "Parse error, expecting '{'");
782  abortExit(1);
783  }
784  STEP("Parsed '{'");
785 
786  while (true) {
787  while (subGetNextToken(psubFile) == tokenSeparator);
788 
789  if (psubFile->token != tokenString)
790  break;
791 
792  psubInfo->patternList.push_back(psubFile->string);
793  }
794 
795  if (psubFile->token != tokenRBrace) {
796  subFileErrPrint(psubFile, "Parse error, expecting '}'");
797  abortExit(1);
798  }
799 
800  subGetNextToken(psubFile);
801  EXITD(1);
802  return true;
803 }
804 
805 static const char *substituteGetGlobalReplacements(subInfo * const psubInfo)
806 {
807  subFile *psubFile = psubInfo->psubFile;
808 
809  ENTER;
810  psubInfo->macroReplacements.clear();
811 
812  while (psubFile->token == tokenSeparator)
813  subGetNextToken(psubFile);
814 
815  if (psubFile->token == tokenRBrace && psubInfo->isFile) {
816  psubInfo->isFile = false;
817  free(psubInfo->filename);
818  psubInfo->filename = 0;
819  freePattern(psubInfo);
820  subGetNextToken(psubFile);
821  EXITD(0);
822  return 0;
823  }
824 
825  if (psubFile->token == tokenEOF) {
826  EXITD(0);
827  return 0;
828  }
829  if (psubFile->token != tokenLBrace) {
830  EXITD(0);
831  return 0;
832  }
833 
834  while (true) {
835  switch(subGetNextToken(psubFile)) {
836  case tokenRBrace:
837  subGetNextToken(psubFile);
838  EXITS(psubInfo->macroReplacements.c_str());
839  return psubInfo->macroReplacements.c_str();
840 
841  case tokenSeparator:
842  catMacroReplacements(psubInfo, ",");
843  break;
844 
845  case tokenString:
846  catMacroReplacements(psubInfo, psubFile->string);
847  break;
848 
849  case tokenLBrace:
850  subFileErrPrint(psubFile, "Parse error, unexpected '{'");
851  abortExit(1);
852  case tokenEOF:
853  subFileErrPrint(psubFile, "Parse error, incomplete file?");
854  abortExit(1);
855  }
856  }
857 }
858 
859 static const char *substituteGetReplacements(subInfo * const psubInfo)
860 {
861  subFile *psubFile = psubInfo->psubFile;
862 
863  ENTER;
864  psubInfo->macroReplacements.clear();
865 
866  while (psubFile->token == tokenSeparator)
867  subGetNextToken(psubFile);
868 
869  if (psubFile->token==tokenRBrace && psubInfo->isFile) {
870  psubInfo->isFile = false;
871  free(psubInfo->filename);
872  psubInfo->filename = 0;
873  freePattern(psubInfo);
874  subGetNextToken(psubFile);
875  EXITD(0);
876  return 0;
877  }
878 
879  if (psubFile->token == tokenEOF) {
880  EXITD(0);
881  return 0;
882  }
883 
884  if (psubFile->token != tokenLBrace) {
885  EXITD(0);
886  return 0;
887  }
888 
889  if (psubInfo->isPattern) {
890  bool gotFirstPattern = false;
891 
892  while (subGetNextToken(psubFile) == tokenSeparator);
893  std::list<std::string>& patternList = psubInfo->patternList;
894  std::list<std::string>::iterator patternIt = patternList.begin();
895  while (true) {
896  if (psubFile->token == tokenRBrace) {
897  subGetNextToken(psubFile);
898  EXITS(psubInfo->macroReplacements.c_str());
899  return psubInfo->macroReplacements.c_str();
900  }
901 
902  if (psubFile->token != tokenString) {
903  subFileErrPrint(psubFile,"Parse error, expecting macro value");
904  abortExit(1);
905  }
906 
907  if (gotFirstPattern)
908  catMacroReplacements(psubInfo, ",");
909  gotFirstPattern = true;
910 
911  if (patternIt != patternList.end()) {
912  catMacroReplacements(psubInfo, patternIt->c_str());
913  catMacroReplacements(psubInfo, "=");
914  catMacroReplacements(psubInfo, psubFile->string);
915  ++patternIt;
916  }
917  else {
918  subFileErrPrint(psubFile, "Warning, too many values given");
919  }
920 
921  while (subGetNextToken(psubFile) == tokenSeparator);
922  }
923  }
924  else while(true) {
925  switch(subGetNextToken(psubFile)) {
926  case tokenRBrace:
927  subGetNextToken(psubFile);
928  EXITS(psubInfo->macroReplacements.c_str());
929  return psubInfo->macroReplacements.c_str();
930 
931  case tokenSeparator:
932  catMacroReplacements(psubInfo, ",");
933  break;
934 
935  case tokenString:
936  catMacroReplacements(psubInfo, psubFile->string);
937  break;
938 
939  case tokenLBrace:
940  subFileErrPrint(psubFile, "Parse error, unexpected '{'");
941  abortExit(1);
942  case tokenEOF:
943  subFileErrPrint(psubFile, "Parse error, incomplete file?");
944  abortExit(1);
945  }
946  }
947 }
948 
949 static char *subGetNextLine(subFile *psubFile)
950 {
951  char *pline;
952 
953  ENTER;
954  do {
955  pline = fgets(psubFile->inputBuffer, MAX_BUFFER_SIZE, psubFile->fp);
956  ++psubFile->lineNum;
957  } while (pline && psubFile->inputBuffer[0] == '#');
958 
959  if (!pline) {
960  psubFile->token = tokenEOF;
961  psubFile->inputBuffer[0] = 0;
962  psubFile->pnextChar = 0;
963  EXITD(0);
964  return 0;
965  }
966 
967  psubFile->pnextChar = &psubFile->inputBuffer[0];
968  EXITS(&psubFile->inputBuffer[0]);
969  return &psubFile->inputBuffer[0];
970 }
971 
972 static void subFileErrPrint(subFile *psubFile, const char * message)
973 {
974  fprintf(stderr, "msi: %s\n",message);
975  fprintf(stderr, " in substitution file '%s' at line %d:\n %s",
976  psubFile->substitutionName.c_str(), psubFile->lineNum,
977  psubFile->inputBuffer);
978 }
979 
980 
981 static tokenType subGetNextToken(subFile *psubFile)
982 {
983  char *p, *pto;
984 
985  ENTER;
986  p = psubFile->pnextChar;
987  if (!p) {
988  STEP("Got EOF");
989  psubFile->token = tokenEOF;
990  goto done;
991  }
992 
993  if (*p == 0 || *p == '\n' || *p == '#') {
994  STEP("Got newline/comment");
995  p = subGetNextLine(psubFile);
996  psubFile->token = p ? tokenSeparator : tokenEOF;
997  goto done;
998  }
999 
1000  while (isspace((int) *p)) p++;
1001  if (*p == '{') {
1002  STEP("Got '{'");
1003  psubFile->token = tokenLBrace;
1004  psubFile->pnextChar = ++p;
1005  goto done;
1006  }
1007  if (*p == '}') {
1008  STEP("Got '}'");
1009  psubFile->token = tokenRBrace;
1010  psubFile->pnextChar = ++p;
1011  goto done;
1012  }
1013  if (*p == 0 || isspace((int) *p) || *p == ',') {
1014  STEP("Got space/comma");
1015  while (isspace((int) *p) || *p == ',') p++;
1016  psubFile->token = tokenSeparator;
1017  psubFile->pnextChar = p;
1018  goto done;
1019  }
1020  /*now handle quoted strings*/
1021  if (*p == '"') {
1022  STEP("Got '\"'");
1023  pto = &psubFile->string[0];
1024  *pto++ = *p++;
1025  while (*p != '"') {
1026  if (*p == 0 || *p == '\n') {
1027  subFileErrPrint(psubFile, "Strings must be on single line\n");
1028  abortExit(1);
1029  }
1030  /*allow escape for embeded quote*/
1031  if ((p[0] == '\\') && p[1] == '"') {
1032  *pto++ = *p++;
1033  *pto++ = *p++;
1034  continue;
1035  }
1036  *pto++ = *p++;
1037  }
1038  *pto++ = *p++;
1039  psubFile->pnextChar = p;
1040  *pto = 0;
1041  psubFile->token = tokenString;
1042  goto done;
1043  }
1044 
1045  /*Now take anything up to next non String token and not space*/
1046  pto = &psubFile->string[0];
1047 
1048  while (!isspace((int) *p) && (strspn(p, "\",{}") == 0))
1049  *pto++ = *p++;
1050  *pto = 0;
1051  STEPS("Got bareword", psubFile->string);
1052 
1053  psubFile->pnextChar = p;
1054  psubFile->token = tokenString;
1055 
1056 done:
1057  EXITD(psubFile->token);
1058  return psubFile->token;
1059 }
1060 
1061 static void catMacroReplacements(subInfo *psubInfo, const char *value)
1062 {
1063  ENTER;
1064  STEPS("Appending", value);
1065  psubInfo->macroReplacements += value;
1066  EXIT;
1067 }
#define STEPS(s, v)
Definition: msi.cpp:48
void errlogFlush(void)
Definition: errlog.c:529
unsigned int epicsStrHash(const char *str, unsigned int seed)
Definition: epicsString.c:356
std::list< std::string > patternList
Definition: msi.cpp:615
Definition: link.h:174
char * filename
LIBCOM_API long epicsStdCall macParseDefns(MAC_HANDLE *handle, const char *defns, char **pairs[])
Parse macro definitions into an array of {name, value} pairs.
Definition: macUtil.c:32
bool isFile
Definition: msi.cpp:612
char inputBuffer[MAX_BUFFER_SIZE]
Definition: msi.cpp:604
pvd::Status status
int lineNum
Definition: msi.cpp:370
FILE * fp
Definition: msi.cpp:602
int i
Definition: scan.c:967
#define OSI_PATH_LIST_SEPARATOR
Definition: unixFileName.h:23
std::string substitutionName
Definition: msi.cpp:601
#define printf
Definition: epicsStdio.h:41
struct inputData inputData
Definition: msi.cpp:58
#define MAX_DEPS
Definition: msi.cpp:30
#define NULL
Definition: catime.c:38
tokenType token
Definition: msi.cpp:606
char inputBuffer[MAX_BUFFER_SIZE]
Definition: msi.cpp:376
Miscellaneous macro definitions.
char * pnextChar
Definition: msi.cpp:605
void copy(PVValueArray< T > &pvFrom, size_t fromOffset, size_t fromStride, PVValueArray< T > &pvTo, size_t toOffset, size_t toStride, size_t count)
Copy a subarray from one scalar array to another.
long epicsStdCall macExpandString(MAC_HANDLE *handle, const char *src, char *dest, long capacity)
Expand a string which may contain macro references.
Definition: macCore.c:174
std::string filename
Definition: msi.cpp:368
char *epicsStdCall macEnvExpand(const char *str)
Expand environment variables in a string.
Definition: macEnv.c:20
std::list< std::string > pathList
Definition: msi.cpp:375
Definition: msi.cpp:600
#define STEP(s)
Definition: msi.cpp:47
struct inputFile inputFile
Macro substitution context, for use by macLib routines only.
Definition: macLib.h:42
void epicsStdCall macSuppressWarning(MAC_HANDLE *handle, int suppress)
Disable or enable warning messages.
Definition: macCore.c:154
inputData()
Definition: msi.cpp:377
int lineNum
Definition: msi.cpp:603
#define EXITD(r)
Definition: msi.cpp:52
#define EXITS(r)
Definition: msi.cpp:53
#define NELEMENTS(A)
Definition: aToIPAddr.c:21
#define stdout
Definition: epicsStdio.h:30
cmdType
Definition: msi.cpp:273
char * filename
Definition: msi.cpp:613
Definition: msi.cpp:610
tokenType
Definition: msi.cpp:596
char * epicsStrDup(const char *s)
Definition: epicsString.c:233
long epicsStdCall macDeleteHandle(MAC_HANDLE *handle)
Marks a handle invalid, and frees all storage associated with it.
Definition: macCore.c:360
#define EXIT
Definition: msi.cpp:51
long epicsStdCall macPopScope(MAC_HANDLE *handle)
Retrieve the last pushed scope (like stack operations)
Definition: macCore.c:427
#define stdin
Definition: epicsStdio.h:28
long epicsStdCall macCreateHandle(MAC_HANDLE **pHandle, const char *pairs[])
Creates a new macro substitution context.
Definition: macCore.c:100
void done(int k)
Definition: antelope.c:77
struct subInfo subInfo
Definition: msi.cpp:69
bool isPattern
Definition: msi.cpp:614
std::string macroReplacements
Definition: msi.cpp:616
Text macro substitution routines.
char * path
#define MAX_BUFFER_SIZE
Definition: msi.cpp:29
int main(int argc, char **argv)
Definition: msi.cpp:95
#define stderr
Definition: epicsStdio.h:32
std::list< inputFile > inputFileList
Definition: msi.cpp:374
#define ENTER
Definition: msi.cpp:45
struct subFile subFile
LIBCOM_API long epicsStdCall macInstallMacros(MAC_HANDLE *handle, char *pairs[])
Install set of {name, value} pairs as definitions.
Definition: macUtil.c:253
char string[MAX_BUFFER_SIZE]
Definition: msi.cpp:607
#define false
Definition: flexdef.h:85
subFile * psubFile
Definition: msi.cpp:611
long epicsStdCall macPushScope(MAC_HANDLE *handle)
Marks the start of a new scoping level.
Definition: macCore.c:392
subInfo()
Definition: msi.cpp:617