Merge ~info-martin-konrad/epics-base:clean-up-msi into ~epics-core/epics-base/+git/epics-base:3.15
- Git
- lp:~info-martin-konrad/epics-base
- clean-up-msi
- Merge into 3.15
Status: | Merged |
---|---|
Approved by: | mdavidsaver |
Approved revision: | a9606dbf6ef07f444c2149afd441d0b62dc0c482 |
Merged at revision: | 59cb5ba6a0f66461e1b0efea28699046d44a7a8b |
Proposed branch: | ~info-martin-konrad/epics-base:clean-up-msi |
Merge into: | ~epics-core/epics-base/+git/epics-base:3.15 |
Prerequisite: | ~info-martin-konrad/epics-base:fix-substitution-file-expansion |
Diff against target: |
791 lines (+129/-164) 5 files modified
src/ioc/dbtemplate/msi.cpp (+107/-162) src/ioc/dbtemplate/test/msi.plt (+15/-2) src/ioc/dbtemplate/test/t12-result.txt (+2/-0) src/ioc/dbtemplate/test/t12-substitute.txt (+3/-0) src/ioc/dbtemplate/test/t12-template.txt (+2/-0) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
mdavidsaver | Approve | ||
Review via email:
|
Commit message
Description of the change
Further clean up to simplify code and replace manual memory management by leveraging C++ features (e.g. std::string, std::list instead of potentially unsafe fiddling with raw pointers).
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Ralph Lange (ralph-lange) wrote : | # |
Looks good to me, too.
Re 3. It would feel somewhat weird to introduce a concept that immediately creates deprecation warnings. I know it's a catch 22, just saying.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
mdavidsaver (mdavidsaver) wrote : | # |
On 1/14/19 12:57 AM, Ralph Lange wrote:
> Looks good to me, too.
>
> Re 3. It would feel somewhat weird to introduce a concept that immediately creates deprecation warnings. I know it's a catch 22, just saying.
It feels weird that in 2019 we're still at c++98 with no timetable to moving forward.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Andrew Johnson (anj) wrote : | # |
I'm very happy to see this work being done, thanks Martin. I haven't looked at the code yet and I don't know if/when I'll have time to do so, but since both Ralph and Michael have approved this at least in principle I don't think I need to.
Since msi gets built for host architectures only, its code doesn't have to meet the compiler limitations of our embedded targets. Of course the MSVC C++ compilers have their own limitations so I would like to see this code built against various versions of Visual C++ before merging, but I wouldn't be averse to removing the older versions of that compiler from what we support.
We should probably update and better document our set of pre-requisites anyway. The list in documentation/
- Andrew
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Martin Konrad (info-martin-konrad) wrote : | # |
While making the modifications on this branch I was also wondering if we could relax the C++98 requirement for host-only applications like MSI. Some newer C++ features would in fact come quite handy. Initializer lists, range-based for loops, std::forward_list, const iterators, and the auto keyword are all C++03/11 features - not to mention move semantics and std::regex which might allow us to simplify the code for parsing significantly without adding any new dependencies.
Which C++ version do you think would be realistic?
BTW: I successfully built this branch with MSVC++ 14.16 (a.k.a. Visual Studio 2017 v15.9) - all MSI tests passed.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Andrew Johnson (anj) wrote : | # |
Any kind of 'what should we be supporting' question should really go to tech-talk, explaining what the issues are and what software and targets would be affected. I'm not saying that *anyone* using EPICS has veto power over this kind of decision, but we should let the community know and allow them some input before we do something that might have effects we don't know about.
From the perspective of the APS Controls group I would want to be able to use the standard version of G++ that comes with RHEL-7.4 (or maybe 7.5 but I'd have to check with IT to see what the oldest RHEL version is that we have installed on our systems). Other groups may have other criteria, such as they need PVA and the latest AreaDetector to run IOCs on a Linux or Windows CPU that comes built into some commercial detector box whose manufacturer only provides <whatever>.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Andrew Johnson (anj) wrote : | # |
Core group: Plans for C++11 support will be announced soon.
Back to MSI: We're considering replacing msi with a Perl version, Benjamin Franksen has offered, which would save having to maintain this complex code. However we don't expect it soon, and it might never happen in which case we will revisit this merge request (please don't delete this MR yet, we may add your test case as a TODO to msi.plt).
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Martin Konrad (info-martin-konrad) wrote : | # |
Any news on the Perl version? If we can't retire the C++ version, yet, I would appreciate if we could get this or the smaller version (https:/
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Ben Franksen (bfrk) wrote : | # |
> Any news on the Perl version?
I may have been a bit hasty with my offer to re-write it in Perl. It is less a matter of time and effort and more one of motivation. Perl is fun for quick and dirty data mining, but doing serious reliable parsing with it is not (IME). C/C++ may not be ideal for this task but at least it is statically typed. Anyway, a rewrite in Perl will need serious real-life testing over an extended period before it can replace the existing code, so I see no good reason to hold back on merging your fixes.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
mdavidsaver (mdavidsaver) wrote : | # |
Andrew/Ralph, share we reconsider merging this work? (maybe against 7.0 if you have concerns wrt. 3.15)
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
mdavidsaver (mdavidsaver) wrote : | # |
Having heard no objections, I'm going to merge this into 3.15.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Andrew Johnson (anj) wrote : | # |
While merging 3.15 up to the 7.0 branch I noticed that Martin removed the inclusion of errlog.h and the call to errlogFlush() at the end of the main() routine. While there are no calls to errlog routines in the msi.cpp file, the macLib routines *do* call errlogPrintf() in the event that errors are detected, so the errlogFlush() is still necessary. I will restore it.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Martin Konrad (info-martin-konrad) wrote : | # |
Good catch. Some day I will write a proper C++ wrapper around the EPICS logger that flushes automatically when it gets destroyed. I'm actually using something like that in some of my support modules: https:/
Preview Diff
1 | diff --git a/src/ioc/dbtemplate/msi.cpp b/src/ioc/dbtemplate/msi.cpp |
2 | index 9a370e8..49d8611 100644 |
3 | --- a/src/ioc/dbtemplate/msi.cpp |
4 | +++ b/src/ioc/dbtemplate/msi.cpp |
5 | @@ -10,6 +10,7 @@ |
6 | /* msi - macro substitutions and include */ |
7 | |
8 | #include <string> |
9 | +#include <list> |
10 | |
11 | #include <stdlib.h> |
12 | #include <stddef.h> |
13 | @@ -20,8 +21,6 @@ |
14 | |
15 | #include <dbDefs.h> |
16 | #include <macLib.h> |
17 | -#include <ellLib.h> |
18 | -#include <errlog.h> |
19 | #include <epicsString.h> |
20 | #include <osiFileName.h> |
21 | #include <osiUnistd.h> |
22 | @@ -68,10 +67,10 @@ static void inputErrPrint(const inputData * const pvt); |
23 | /* Module to read the substitution file */ |
24 | typedef struct subInfo subInfo; |
25 | |
26 | -static void substituteOpen(subInfo **ppvt, char * const substitutionName); |
27 | +static void substituteOpen(subInfo **ppvt, const std::string& substitutionName); |
28 | static void substituteDestruct(subInfo * const pvt); |
29 | -static int substituteGetNextSet(subInfo * const pvt, char **filename); |
30 | -static int substituteGetGlobalSet(subInfo * const pvt); |
31 | +static bool substituteGetNextSet(subInfo * const pvt, char **filename); |
32 | +static bool substituteGetGlobalSet(subInfo * const pvt); |
33 | static const char *substituteGetReplacements(subInfo * const pvt); |
34 | static const char *substituteGetGlobalReplacements(subInfo * const pvt); |
35 | |
36 | @@ -86,7 +85,7 @@ static void makeSubstitutions(inputData * const inputPvt, |
37 | |
38 | /*Global variables */ |
39 | static int opt_V = 0; |
40 | -static int opt_D = 0; |
41 | +static bool opt_D = false; |
42 | |
43 | static char *outFile = 0; |
44 | static int numDeps = 0, depHashes[MAX_DEPS]; |
45 | @@ -97,23 +96,21 @@ int main(int argc,char **argv) |
46 | inputData *inputPvt; |
47 | MAC_HANDLE *macPvt; |
48 | char *pval; |
49 | - int narg; |
50 | - char *substitutionName = 0; |
51 | + std::string substitutionName; |
52 | char *templateName = 0; |
53 | - int i; |
54 | - int localScope = 1; |
55 | + bool localScope = true; |
56 | |
57 | inputConstruct(&inputPvt); |
58 | macCreateHandle(&macPvt, 0); |
59 | while ((argc > 1) && (argv[1][0] == '-')) { |
60 | - narg = (strlen(argv[1]) == 2) ? 2 : 1; |
61 | + int narg = (strlen(argv[1]) == 2) ? 2 : 1; |
62 | pval = (narg == 1) ? (argv[1] + 2) : argv[2]; |
63 | |
64 | if (strncmp(argv[1], "-I", 2) == 0) { |
65 | inputAddPath(inputPvt, pval); |
66 | } |
67 | else if (strcmp(argv[1], "-D") == 0) { |
68 | - opt_D = 1; |
69 | + opt_D = true; |
70 | narg = 1; /* no argument for this option */ |
71 | } |
72 | else if(strncmp(argv[1], "-o", 2) == 0) { |
73 | @@ -123,14 +120,14 @@ int main(int argc,char **argv) |
74 | addMacroReplacements(macPvt, pval); |
75 | } |
76 | else if(strncmp(argv[1], "-S", 2) == 0) { |
77 | - substitutionName = epicsStrDup(pval); |
78 | + substitutionName = pval; |
79 | } |
80 | else if (strcmp(argv[1], "-V") == 0) { |
81 | opt_V = 1; |
82 | narg = 1; /* no argument for this option */ |
83 | } |
84 | else if (strcmp(argv[1], "-g") == 0) { |
85 | - localScope = 0; |
86 | + localScope = false; |
87 | narg = 1; /* no argument for this option */ |
88 | } |
89 | else if (strcmp(argv[1], "-h") == 0) { |
90 | @@ -142,7 +139,7 @@ int main(int argc,char **argv) |
91 | } |
92 | |
93 | argc -= narg; |
94 | - for (i = 1; i < argc; i++) |
95 | + for (int i = 1; i < argc; i++) |
96 | argv[i] = argv[i + narg]; |
97 | } |
98 | |
99 | @@ -170,16 +167,16 @@ int main(int argc,char **argv) |
100 | if (argc == 2) |
101 | templateName = epicsStrDup(argv[1]); |
102 | |
103 | - if (!substitutionName) { |
104 | + if (substitutionName.empty()) { |
105 | STEP("Single template+substitutions file"); |
106 | makeSubstitutions(inputPvt, macPvt, templateName); |
107 | } |
108 | else { |
109 | subInfo *substitutePvt; |
110 | char *filename = 0; |
111 | - int isGlobal, isFile; |
112 | + bool isGlobal, isFile; |
113 | |
114 | - STEPS("Substitutions from file", substitutionName); |
115 | + STEPS("Substitutions from file", substitutionName.c_str()); |
116 | substituteOpen(&substitutePvt, substitutionName); |
117 | do { |
118 | isGlobal = substituteGetGlobalSet(substitutePvt); |
119 | @@ -213,14 +210,12 @@ int main(int argc,char **argv) |
120 | } while (isGlobal || isFile); |
121 | substituteDestruct(substitutePvt); |
122 | } |
123 | - errlogFlush(); |
124 | macDeleteHandle(macPvt); |
125 | inputDestruct(inputPvt); |
126 | if (opt_D) { |
127 | printf("\n"); |
128 | } |
129 | free(templateName); |
130 | - free(substitutionName); |
131 | return opt_V & 2; |
132 | } |
133 | |
134 | |
135 | @@ -301,7 +296,6 @@ static void makeSubstitutions(inputData * const inputPvt, |
136 | if (command) { |
137 | char *pstart; |
138 | char *pend; |
139 | - char *copy; |
140 | int cmdind=-1; |
141 | int i; |
142 | |
143 | @@ -334,16 +328,15 @@ static void makeSubstitutions(inputData * const inputPvt, |
144 | /*skip quote and any trailing blanks*/ |
145 | while (*++p == ' ') ; |
146 | if (*p != '\n' && *p != 0) goto endcmd; |
147 | - copy = static_cast<char *>(calloc(pend-pstart + 1, sizeof(char))); |
148 | - strncpy(copy, pstart, pend-pstart); |
149 | + std::string copy = std::string(pstart, pend); |
150 | |
151 | switch(cmdind) { |
152 | case cmdInclude: |
153 | - inputNewIncludeFile(inputPvt,copy); |
154 | + inputNewIncludeFile(inputPvt, copy.c_str()); |
155 | break; |
156 | |
157 | case cmdSubstitute: |
158 | - addMacroReplacements(macPvt,copy); |
159 | + addMacroReplacements(macPvt, copy.c_str()); |
160 | break; |
161 | |
162 | default: |
163 | @@ -351,7 +344,6 @@ static void makeSubstitutions(inputData * const inputPvt, |
164 | inputErrPrint(inputPvt); |
165 | abortExit(1); |
166 | } |
167 | - free(copy); |
168 | expand = 0; |
169 | } |
170 | |
171 | @@ -370,21 +362,16 @@ endcmd: |
172 | } |
173 | |
174 | |
175 | typedef struct inputFile { |
176 | - ELLNODE node; |
177 | - char *filename; |
178 | + std::string filename; |
179 | FILE *fp; |
180 | int lineNum; |
181 | } inputFile; |
182 | |
183 | -typedef struct pathNode { |
184 | - ELLNODE node; |
185 | - char *directory; |
186 | -} pathNode; |
187 | - |
188 | struct inputData { |
189 | - ELLLIST inputFileList; |
190 | - ELLLIST pathList; |
191 | + std::list<inputFile> inputFileList; |
192 | + std::list<std::string> pathList; |
193 | char inputBuffer[MAX_BUFFER_SIZE]; |
194 | + inputData() { memset(inputBuffer, 0, sizeof(inputBuffer) * sizeof(inputBuffer[0])); }; |
195 | }; |
196 | |
197 | static void inputOpenFile(inputData *pinputData, const char * const filename); |
198 | @@ -393,66 +380,49 @@ static void inputCloseAllFiles(inputData *pinputData); |
199 | |
200 | static void inputConstruct(inputData **ppvt) |
201 | { |
202 | - inputData *pinputData; |
203 | - |
204 | - pinputData = static_cast<inputData *>(calloc(1, sizeof(inputData))); |
205 | - ellInit(&pinputData->inputFileList); |
206 | - ellInit(&pinputData->pathList); |
207 | - *ppvt = pinputData; |
208 | + *ppvt = new inputData; |
209 | } |
210 | |
211 | static void inputDestruct(inputData * const pinputData) |
212 | { |
213 | - pathNode *ppathNode; |
214 | - |
215 | inputCloseAllFiles(pinputData); |
216 | - while ((ppathNode = (pathNode *) ellFirst(&pinputData->pathList))) { |
217 | - ellDelete(&pinputData->pathList, &ppathNode->node); |
218 | - free(ppathNode->directory); |
219 | - free(ppathNode); |
220 | - } |
221 | - free(pinputData); |
222 | + delete(pinputData); |
223 | } |
224 | |
225 | |
226 | static void inputAddPath(inputData * const pinputData, const char * const path) |
227 | { |
228 | - ELLLIST *ppathList = &pinputData->pathList; |
229 | - pathNode *ppathNode; |
230 | const char *pcolon; |
231 | const char *pdir; |
232 | size_t len; |
233 | - int emptyName; |
234 | const char sep = *OSI_PATH_LIST_SEPARATOR; |
235 | |
236 | ENTER; |
237 | pdir = path; |
238 | /*an empty name at beginning, middle, or end means current directory*/ |
239 | while (pdir && *pdir) { |
240 | - emptyName = ((*pdir == sep) ? 1 : 0); |
241 | + bool emptyName = (*pdir == sep); |
242 | if (emptyName) ++pdir; |
243 | |
244 | - ppathNode = (pathNode *) calloc(1, sizeof(pathNode)); |
245 | - ellAdd(ppathList, &ppathNode->node); |
246 | - |
247 | + std::string directory; |
248 | if (!emptyName) { |
249 | pcolon = strchr(pdir, sep); |
250 | len = (pcolon ? (pcolon - pdir) : strlen(pdir)); |
251 | if (len > 0) { |
252 | - ppathNode->directory = (char *) calloc(len + 1, sizeof(char)); |
253 | - strncpy(ppathNode->directory, pdir, len); |
254 | + directory = std::string(pdir, len); |
255 | pdir = pcolon; |
256 | /*unless at end skip past first colon*/ |
257 | if (pdir && *(pdir + 1) != 0) ++pdir; |
258 | } |
259 | else { /*must have been trailing : */ |
260 | - emptyName = 1; |
261 | + emptyName = true; |
262 | } |
263 | } |
264 | |
265 | if (emptyName) { |
266 | - ppathNode->directory = (char *) calloc(2, sizeof(char)); |
267 | - strcpy(ppathNode->directory, "."); |
268 | + directory = "."; |
269 | } |
270 | + |
271 | + pinputData->pathList.push_back(directory); |
272 | } |
273 | EXIT; |
274 | } |
275 | @@ -467,14 +437,14 @@ static void inputBegin(inputData * const pinputData, const char * const fileName |
276 | |
277 | static char *inputNextLine(inputData * const pinputData) |
278 | { |
279 | - inputFile *pinputFile; |
280 | - char *pline; |
281 | + std::list<inputFile>& inFileList = pinputData->inputFileList; |
282 | |
283 | ENTER; |
284 | - while ((pinputFile = (inputFile *) ellFirst(&pinputData->inputFileList))) { |
285 | - pline = fgets(pinputData->inputBuffer, MAX_BUFFER_SIZE, pinputFile->fp); |
286 | + while (!inFileList.empty()) { |
287 | + inputFile& inFile = inFileList.front(); |
288 | + char *pline = fgets(pinputData->inputBuffer, MAX_BUFFER_SIZE, inFile.fp); |
289 | if (pline) { |
290 | - ++pinputFile->lineNum; |
291 | + ++inFile.lineNum; |
292 | EXITS(pline); |
293 | return pline; |
294 | } |
295 | @@ -494,23 +464,21 @@ static void inputNewIncludeFile(inputData * const pinputData, |
296 | |
297 | static void inputErrPrint(const inputData *const pinputData) |
298 | { |
299 | - inputFile *pinputFile; |
300 | - |
301 | ENTER; |
302 | fprintf(stderr, "input: '%s' at ", pinputData->inputBuffer); |
303 | - pinputFile = (inputFile *) ellFirst(&pinputData->inputFileList); |
304 | - while (pinputFile) { |
305 | - fprintf(stderr, "line %d of ", pinputFile->lineNum); |
306 | + const std::list<inputFile>& inFileList = pinputData->inputFileList; |
307 | + std::list<inputFile>::const_iterator inFileIt = inFileList.begin(); |
308 | + while (inFileIt != inFileList.end()) { |
309 | + fprintf(stderr, "line %d of ", inFileIt->lineNum); |
310 | |
311 | - if (pinputFile->filename) { |
312 | - fprintf(stderr, " file %s\n", pinputFile->filename); |
313 | + if (!inFileIt->filename.empty()) { |
314 | + fprintf(stderr, " file %s\n", inFileIt->filename.c_str()); |
315 | } |
316 | else { |
317 | fprintf(stderr, "stdin:\n"); |
318 | } |
319 | |
320 | - pinputFile = (inputFile *) ellNext(&pinputFile->node); |
321 | - if (pinputFile) { |
322 | + if (++inFileIt != inFileList.end()) { |
323 | fprintf(stderr, " included from "); |
324 | } |
325 | else { |
326 | @@ -523,10 +491,9 @@ static void inputErrPrint(const inputData *const pinputData) |
327 | |
328 | |
329 | static void inputOpenFile(inputData *pinputData, const char * const filename) |
330 | { |
331 | - ELLLIST *ppathList = &pinputData->pathList; |
332 | - pathNode *ppathNode = 0; |
333 | - inputFile *pinputFile; |
334 | - char *fullname = 0; |
335 | + std::list<std::string>& pathList = pinputData->pathList; |
336 | + std::list<std::string>::iterator pathIt = pathList.end(); |
337 | + std::string fullname; |
338 | FILE *fp = 0; |
339 | |
340 | ENTER; |
341 | @@ -534,24 +501,19 @@ static void inputOpenFile(inputData *pinputData, const char * const filename) |
342 | STEP("Using stdin"); |
343 | fp = stdin; |
344 | } |
345 | - else if ((ellCount(ppathList) == 0) || strchr(filename, '/')){ |
346 | + else if (pathList.empty() || strchr(filename, '/')){ |
347 | STEPS("Opening ", filename); |
348 | fp = fopen(filename, "r"); |
349 | } |
350 | else { |
351 | - ppathNode = (pathNode *) ellFirst(ppathList); |
352 | - while (ppathNode) { |
353 | - fullname = static_cast<char *>(calloc(strlen(filename) + strlen(ppathNode->directory) + 2, |
354 | - sizeof(char))); |
355 | - strcpy(fullname, ppathNode->directory); |
356 | - strcat(fullname, "/"); |
357 | - strcat(fullname, filename); |
358 | + pathIt = pathList.begin(); |
359 | + while(pathIt != pathList.end()) { |
360 | + fullname = *pathIt + "/" + filename; |
361 | STEPS("Trying", filename); |
362 | - fp = fopen(fullname, "r"); |
363 | + fp = fopen(fullname.c_str(), "r"); |
364 | if (fp) |
365 | break; |
366 | - free(fullname); |
367 | - ppathNode = (pathNode *) ellNext(&ppathNode->node); |
368 | + ++pathIt; |
369 | } |
370 | } |
371 | |
372 | @@ -562,20 +524,20 @@ static void inputOpenFile(inputData *pinputData, const char * const filename) |
373 | } |
374 | |
375 | STEP("File opened"); |
376 | - pinputFile = static_cast<inputFile *>(calloc(1, sizeof(inputFile))); |
377 | + inputFile inFile = inputFile(); |
378 | |
379 | - if (ppathNode) { |
380 | - pinputFile->filename = fullname; |
381 | + if (pathIt != pathList.end()) { |
382 | + inFile.filename = fullname; |
383 | } |
384 | else if (filename) { |
385 | - pinputFile->filename = epicsStrDup(filename); |
386 | + inFile.filename = filename; |
387 | } |
388 | else { |
389 | - pinputFile->filename = epicsStrDup("stdin"); |
390 | + inFile.filename = "stdin"; |
391 | } |
392 | |
393 | if (opt_D) { |
394 | - int hash = epicsStrHash(pinputFile->filename, 12345); |
395 | + int hash = epicsStrHash(inFile.filename.c_str(), 12345); |
396 | int i = 0; |
397 | int match = 0; |
398 | |
399 | @@ -588,7 +550,7 @@ static void inputOpenFile(inputData *pinputData, const char * const filename) |
400 | if (!match) { |
401 | const char *wrap = numDeps ? " \\\n" : ""; |
402 | |
403 | - printf("%s %s", wrap, pinputFile->filename); |
404 | + printf("%s %s", wrap, inFile.filename.c_str()); |
405 | if (numDeps < MAX_DEPS) { |
406 | depHashes[numDeps++] = hash; |
407 | } |
408 | @@ -599,33 +561,29 @@ static void inputOpenFile(inputData *pinputData, const char * const filename) |
409 | } |
410 | } |
411 | |
412 | - pinputFile->fp = fp; |
413 | - ellInsert(&pinputData->inputFileList, 0, &pinputFile->node); |
414 | + inFile.fp = fp; |
415 | + pinputData->inputFileList.push_front(inFile); |
416 | EXIT; |
417 | } |
418 | |
419 | static void inputCloseFile(inputData *pinputData) |
420 | { |
421 | - inputFile *pinputFile; |
422 | - |
423 | + std::list<inputFile>& inFileList = pinputData->inputFileList; |
424 | ENTER; |
425 | - pinputFile = (inputFile *) ellFirst(&pinputData->inputFileList); |
426 | - if (pinputFile) { |
427 | - ellDelete(&pinputData->inputFileList, &pinputFile->node); |
428 | - if (fclose(pinputFile->fp)) |
429 | - fprintf(stderr, "msi: Can't close input file '%s'\n", pinputFile->filename); |
430 | - free(pinputFile->filename); |
431 | - free(pinputFile); |
432 | + if(!inFileList.empty()) { |
433 | + inputFile& inFile = inFileList.front(); |
434 | + if (fclose(inFile.fp)) |
435 | + fprintf(stderr, "msi: Can't close input file '%s'\n", inFile.filename.c_str()); |
436 | + inFileList.erase(inFileList.begin()); |
437 | } |
438 | EXIT; |
439 | } |
440 | |
441 | static void inputCloseAllFiles(inputData *pinputData) |
442 | { |
443 | - inputFile *pinputFile; |
444 | - |
445 | ENTER; |
446 | - while ((pinputFile = (inputFile *) ellFirst(&pinputData->inputFileList))) { |
447 | + const std::list<inputFile>& inFileList = pinputData->inputFileList; |
448 | + while(!inFileList.empty()) { |
449 | inputCloseFile(pinputData); |
450 | } |
451 | EXIT; |
452 | @@ -637,7 +595,7 @@ typedef enum { |
453 | } tokenType; |
454 | |
455 | typedef struct subFile { |
456 | - char *substitutionName; |
457 | + std::string substitutionName; |
458 | FILE *fp; |
459 | int lineNum; |
460 | char inputBuffer[MAX_BUFFER_SIZE]; |
461 | @@ -646,19 +604,15 @@ typedef struct subFile { |
462 | char string[MAX_BUFFER_SIZE]; |
463 | } subFile; |
464 | |
465 | -typedef struct patternNode { |
466 | - ELLNODE node; |
467 | - char *var; |
468 | -} patternNode; |
469 | - |
470 | struct subInfo { |
471 | subFile *psubFile; |
472 | - int isFile; |
473 | + bool isFile; |
474 | char *filename; |
475 | - int isPattern; |
476 | - ELLLIST patternList; |
477 | + bool isPattern; |
478 | + std::list<std::string> patternList; |
479 | std::string macroReplacements; |
480 | - subInfo() : psubFile(NULL), isFile(0), filename(NULL), isPattern(0) {}; |
481 | + subInfo() : psubFile(NULL), isFile(false), filename(NULL), |
482 | + isPattern(false) {}; |
483 | }; |
484 | |
485 | static char *subGetNextLine(subFile *psubFile); |
486 | @@ -677,7 +631,7 @@ void freeSubFile(subInfo *psubInfo) |
487 | if (fclose(psubFile->fp)) |
488 | fprintf(stderr, "msi: Can't close substitution file\n"); |
489 | } |
490 | - free(psubFile); |
491 | + delete(psubFile); |
492 | free(psubInfo->filename); |
493 | psubInfo->psubFile = 0; |
494 | EXIT; |
495 | @@ -685,15 +639,9 @@ void freeSubFile(subInfo *psubInfo) |
496 | |
497 | void freePattern(subInfo *psubInfo) |
498 | { |
499 | - patternNode *ppatternNode; |
500 | - |
501 | ENTER; |
502 | - while ((ppatternNode = (patternNode *) ellFirst(&psubInfo->patternList))) { |
503 | - ellDelete(&psubInfo->patternList, &ppatternNode->node); |
504 | - free(ppatternNode->var); |
505 | - free(ppatternNode); |
506 | - } |
507 | - psubInfo->isPattern = 0; |
508 | + psubInfo->patternList.clear(); |
509 | + psubInfo->isPattern = false; |
510 | EXIT; |
511 | } |
512 | |
513 | |
514 | @@ -706,7 +654,7 @@ static void substituteDestruct(subInfo * const psubInfo) |
515 | EXIT; |
516 | } |
517 | |
518 | -static void substituteOpen(subInfo **ppvt, char * const substitutionName) |
519 | +static void substituteOpen(subInfo **ppvt, const std::string& substitutionName) |
520 | { |
521 | subInfo *psubInfo; |
522 | subFile *psubFile; |
523 | @@ -715,13 +663,12 @@ static void substituteOpen(subInfo **ppvt, char * const substitutionName) |
524 | ENTER; |
525 | psubInfo = new subInfo; |
526 | *ppvt = psubInfo; |
527 | - psubFile = static_cast<subFile *>(calloc(1, sizeof(subFile))); |
528 | + psubFile = new subFile; |
529 | psubInfo->psubFile = psubFile; |
530 | - ellInit(&psubInfo->patternList); |
531 | |
532 | - fp = fopen(substitutionName, "r"); |
533 | + fp = fopen(substitutionName.c_str(), "r"); |
534 | if (!fp) { |
535 | - fprintf(stderr, "msi: Can't open file '%s'\n", substitutionName); |
536 | + fprintf(stderr, "msi: Can't open file '%s'\n", substitutionName.c_str()); |
537 | abortExit(1); |
538 | } |
539 | |
540 | @@ -734,7 +681,7 @@ static void substituteOpen(subInfo **ppvt, char * const substitutionName) |
541 | EXIT; |
542 | } |
543 | |
544 | -static int substituteGetGlobalSet(subInfo * const psubInfo) |
545 | +static bool substituteGetGlobalSet(subInfo * const psubInfo) |
546 | { |
547 | subFile *psubFile = psubInfo->psubFile; |
548 | |
549 | @@ -746,17 +693,16 @@ static int substituteGetGlobalSet(subInfo * const psubInfo) |
550 | strcmp(psubFile->string, "global") == 0) { |
551 | subGetNextToken(psubFile); |
552 | EXITD(1); |
553 | - return 1; |
554 | + return true; |
555 | } |
556 | |
557 | EXITD(0); |
558 | - return 0; |
559 | + return false; |
560 | } |
561 | |
562 | -static int substituteGetNextSet(subInfo * const psubInfo,char **filename) |
563 | +static bool substituteGetNextSet(subInfo * const psubInfo,char **filename) |
564 | { |
565 | subFile *psubFile = psubInfo->psubFile; |
566 | - patternNode *ppatternNode; |
567 | |
568 | ENTER; |
569 | *filename = 0; |
570 | @@ -765,7 +711,7 @@ static int substituteGetNextSet(subInfo * const psubInfo,char **filename) |
571 | |
572 | if (psubFile->token == tokenEOF) { |
573 | EXITD(0); |
574 | - return 0; |
575 | + return false; |
576 | } |
577 | |
578 | if (psubFile->token == tokenString && |
579 | @@ -773,7 +719,7 @@ static int substituteGetNextSet(subInfo * const psubInfo,char **filename) |
580 | size_t len; |
581 | |
582 | STEP("Parsed 'file'"); |
583 | - psubInfo->isFile = 1; |
584 | + psubInfo->isFile = true; |
585 | if (subGetNextToken(psubFile) != tokenString) { |
586 | subFileErrPrint(psubFile, "Parse error, expecting a filename"); |
587 | abortExit(1); |
588 | @@ -808,7 +754,7 @@ static int substituteGetNextSet(subInfo * const psubInfo,char **filename) |
589 | |
590 | if (psubFile->token == tokenLBrace) { |
591 | EXITD(1); |
592 | - return 1; |
593 | + return true; |
594 | } |
595 | |
596 | if (psubFile->token == tokenRBrace) { |
597 | @@ -824,7 +770,7 @@ static int substituteGetNextSet(subInfo * const psubInfo,char **filename) |
598 | |
599 | STEP("Parsed 'pattern'"); |
600 | freePattern(psubInfo); |
601 | - psubInfo->isPattern = 1; |
602 | + psubInfo->isPattern = true; |
603 | |
604 | while (subGetNextToken(psubFile) == tokenSeparator); |
605 | |
606 | @@ -834,15 +780,13 @@ static int substituteGetNextSet(subInfo * const psubInfo,char **filename) |
607 | } |
608 | STEP("Parsed '{'"); |
609 | |
610 | - while (1) { |
611 | + while (true) { |
612 | while (subGetNextToken(psubFile) == tokenSeparator); |
613 | |
614 | if (psubFile->token != tokenString) |
615 | break; |
616 | |
617 | - ppatternNode = static_cast<patternNode *>(calloc(1, sizeof(patternNode))); |
618 | - ellAdd(&psubInfo->patternList, &ppatternNode->node); |
619 | - ppatternNode->var = epicsStrDup(psubFile->string); |
620 | + psubInfo->patternList.push_back(psubFile->string); |
621 | } |
622 | |
623 | if (psubFile->token != tokenRBrace) { |
624 | @@ -852,7 +796,7 @@ static int substituteGetNextSet(subInfo * const psubInfo,char **filename) |
625 | |
626 | subGetNextToken(psubFile); |
627 | EXITD(1); |
628 | - return 1; |
629 | + return true; |
630 | } |
631 | |
632 | |
633 | static const char *substituteGetGlobalReplacements(subInfo * const psubInfo) |
634 | @@ -866,7 +810,7 @@ static const char *substituteGetGlobalReplacements(subInfo * const psubInfo) |
635 | subGetNextToken(psubFile); |
636 | |
637 | if (psubFile->token == tokenRBrace && psubInfo->isFile) { |
638 | - psubInfo->isFile = 0; |
639 | + psubInfo->isFile = false; |
640 | free(psubInfo->filename); |
641 | psubInfo->filename = 0; |
642 | freePattern(psubInfo); |
643 | @@ -884,7 +828,7 @@ static const char *substituteGetGlobalReplacements(subInfo * const psubInfo) |
644 | return 0; |
645 | } |
646 | |
647 | - while (1) { |
648 | + while (true) { |
649 | switch(subGetNextToken(psubFile)) { |
650 | case tokenRBrace: |
651 | subGetNextToken(psubFile); |
652 | @@ -912,7 +856,6 @@ static const char *substituteGetGlobalReplacements(subInfo * const psubInfo) |
653 | static const char *substituteGetReplacements(subInfo * const psubInfo) |
654 | { |
655 | subFile *psubFile = psubInfo->psubFile; |
656 | - patternNode *ppatternNode; |
657 | |
658 | ENTER; |
659 | psubInfo->macroReplacements.clear(); |
660 | @@ -921,7 +864,7 @@ static const char *substituteGetReplacements(subInfo * const psubInfo) |
661 | subGetNextToken(psubFile); |
662 | |
663 | if (psubFile->token==tokenRBrace && psubInfo->isFile) { |
664 | - psubInfo->isFile = 0; |
665 | + psubInfo->isFile = false; |
666 | free(psubInfo->filename); |
667 | psubInfo->filename = 0; |
668 | freePattern(psubInfo); |
669 | @@ -941,11 +884,12 @@ static const char *substituteGetReplacements(subInfo * const psubInfo) |
670 | } |
671 | |
672 | if (psubInfo->isPattern) { |
673 | - int gotFirstPattern = 0; |
674 | + bool gotFirstPattern = false; |
675 | |
676 | while (subGetNextToken(psubFile) == tokenSeparator); |
677 | - ppatternNode = (patternNode *) ellFirst(&psubInfo->patternList); |
678 | - while (1) { |
679 | + std::list<std::string>& patternList = psubInfo->patternList; |
680 | + std::list<std::string>::iterator patternIt = patternList.begin(); |
681 | + while (true) { |
682 | if (psubFile->token == tokenRBrace) { |
683 | subGetNextToken(psubFile); |
684 | EXITS(psubInfo->macroReplacements.c_str()); |
685 | @@ -959,13 +903,13 @@ static const char *substituteGetReplacements(subInfo * const psubInfo) |
686 | |
687 | if (gotFirstPattern) |
688 | catMacroReplacements(psubInfo, ","); |
689 | - gotFirstPattern = 1; |
690 | + gotFirstPattern = true; |
691 | |
692 | - if (ppatternNode) { |
693 | - catMacroReplacements(psubInfo, ppatternNode->var); |
694 | + if (patternIt != patternList.end()) { |
695 | + catMacroReplacements(psubInfo, patternIt->c_str()); |
696 | catMacroReplacements(psubInfo, "="); |
697 | catMacroReplacements(psubInfo, psubFile->string); |
698 | - ppatternNode = (patternNode *) ellNext(&ppatternNode->node); |
699 | + ++patternIt; |
700 | } |
701 | else { |
702 | subFileErrPrint(psubFile, "Warning, too many values given"); |
703 | @@ -974,7 +918,7 @@ static const char *substituteGetReplacements(subInfo * const psubInfo) |
704 | while (subGetNextToken(psubFile) == tokenSeparator); |
705 | } |
706 | } |
707 | - else while(1) { |
708 | + else while(true) { |
709 | switch(subGetNextToken(psubFile)) { |
710 | case tokenRBrace: |
711 | subGetNextToken(psubFile); |
712 | @@ -1026,7 +970,8 @@ static void subFileErrPrint(subFile *psubFile, const char * message) |
713 | { |
714 | fprintf(stderr, "msi: %s\n",message); |
715 | fprintf(stderr, " in substitution file '%s' at line %d:\n %s", |
716 | - psubFile->substitutionName, psubFile->lineNum, psubFile->inputBuffer); |
717 | + psubFile->substitutionName.c_str(), psubFile->lineNum, |
718 | + psubFile->inputBuffer); |
719 | } |
720 | |
721 | |
722 | |
723 | diff --git a/src/ioc/dbtemplate/test/msi.plt b/src/ioc/dbtemplate/test/msi.plt |
724 | index 332defb..ca87ca1 100644 |
725 | --- a/src/ioc/dbtemplate/test/msi.plt |
726 | +++ b/src/ioc/dbtemplate/test/msi.plt |
727 | @@ -11,7 +11,7 @@ |
728 | use strict; |
729 | use Test; |
730 | |
731 | -BEGIN {plan tests => 11} |
732 | +BEGIN {plan tests => 12} |
733 | |
734 | # Check include/substitute command model |
735 | ok(msi('-I .. ../t1-template.txt'), slurp('../t1-result.txt')); |
736 | @@ -56,6 +56,8 @@ ok(msi('-I. -I.. -S ../t10-substitute.txt'), slurp('../t10-result.txt')); |
737 | # Substitution file, pattern format, with 0 pattern definitions |
738 | ok(msi('-I. -I.. -S ../t11-substitute.txt'), slurp('../t11-result.txt')); |
739 | |
740 | +# Macros in template-file name populated from environment variable |
741 | +ok(msi('-I. -I.. -S ../t12-substitute.txt', 'TEST_NO=12,PREFIX=t'), slurp('../t12-result.txt')); |
742 | |
743 | # Test support routines |
744 | |
745 | @@ -68,10 +70,16 @@ sub slurp { |
746 | } |
747 | |
748 | sub msi { |
749 | - my ($args) = @_; |
750 | + my ($args, $envstr) = @_; |
751 | my $exe = ($^O eq 'MSWin32') || ($^O eq 'cygwin') ? '.exe' : ''; |
752 | my $msi = "./msi-copy$exe"; |
753 | my $result; |
754 | + my @envs = split(/,/, $envstr); |
755 | + foreach (@envs) |
756 | + { |
757 | + my ($var, $value) = split /=/, $_; |
758 | + $ENV{$var} = $value; |
759 | + } |
760 | if ($args =~ m/-o / && $args !~ m/-D/) { |
761 | # An empty result is expected |
762 | $result = `$msi $args`; |
763 | @@ -85,5 +93,10 @@ sub msi { |
764 | if $result eq ''; |
765 | } while ($result eq '') && (--$count > 0); |
766 | } |
767 | + foreach (@envs) |
768 | + { |
769 | + my ($var, $value) = split /=/, $_; |
770 | + delete $ENV{$var}; |
771 | + } |
772 | return $result; |
773 | } |
774 | diff --git a/src/ioc/dbtemplate/test/t12-result.txt b/src/ioc/dbtemplate/test/t12-result.txt |
775 | new file mode 100644 |
776 | index 0000000..6cfcc57 |
777 | --- /dev/null |
778 | +++ b/src/ioc/dbtemplate/test/t12-result.txt |
779 | @@ -0,0 +1,2 @@ |
780 | +# comment line |
781 | +a=foo |
782 | diff --git a/src/ioc/dbtemplate/test/t12-substitute.txt b/src/ioc/dbtemplate/test/t12-substitute.txt |
783 | new file mode 100644 |
784 | index 0000000..7a26b6f |
785 | --- /dev/null |
786 | +++ b/src/ioc/dbtemplate/test/t12-substitute.txt |
787 | @@ -0,0 +1,3 @@ |
788 | +file $(PREFIX)$(TEST_NO)-template.txt { |
789 | + { a=foo } |
790 | +} |
791 | diff --git a/src/ioc/dbtemplate/test/t12-template.txt b/src/ioc/dbtemplate/test/t12-template.txt |
792 | new file mode 100644 |
793 | index 0000000..7958885 |
794 | --- /dev/null |
795 | +++ b/src/ioc/dbtemplate/test/t12-template.txt |
796 | @@ -0,0 +1,2 @@ |
797 | +# comment line |
798 | +a=$(a) |
The MSI test still passes: https:/ /travis- ci.org/ mdavidsaver/ epics-base/ builds/ 479219104
There is nothing here which I don't like, so I'm inclined to merge this.
There is a more which could be done to eliminate the remaining explicit deletes.
1. The inputData struct is only ever instantiated in main(). So it could be made a local, and the inputCloseAllFi les() call moved to ~inputData.
2. Similarly for subInfo, which is also only instantiated in main().
3. subInfo::psubFile cries out for auto_ptr.