entryIO.C
Go to the documentation of this file.
1/*---------------------------------------------------------------------------*\
2 ========= |
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4 \\ / O peration |
5 \\ / A nd | www.openfoam.com
6 \\/ M anipulation |
7-------------------------------------------------------------------------------
8 Copyright (C) 2011-2016 OpenFOAM Foundation
9 Copyright (C) 2018-2021 OpenCFD Ltd.
10-------------------------------------------------------------------------------
11License
12 This file is part of OpenFOAM.
13
14 OpenFOAM is free software: you can redistribute it and/or modify it
15 under the terms of the GNU General Public License as published by
16 the Free Software Foundation, either version 3 of the License, or
17 (at your option) any later version.
18
19 OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
20 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22 for more details.
23
24 You should have received a copy of the GNU General Public License
25 along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
26
27\*---------------------------------------------------------------------------*/
28
29#include "entry.H"
30#include "primitiveEntry.H"
31#include "dictionaryEntry.H"
32#include "functionEntry.H"
33#include "includeEntry.H"
34#include "stringOps.H"
35#include "dictionaryListEntry.H"
36
37// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
38
39bool Foam::entry::getKeyword(keyType& keyword, token& keyToken, Istream& is)
40{
41 // Read the next valid token discarding spurious ';'s
42 do
43 {
44 if
45 (
46 is.read(keyToken).bad()
47 || is.eof()
48 || !keyToken.good()
49 )
50 {
51 return false;
52 }
53 }
54 while (keyToken == token::END_STATEMENT);
55
56 // If the token is a valid keyword set 'keyword' return true...
57 if (keyToken.isWord())
58 {
59 keyword = keyToken.wordToken();
60 return true;
61 }
62
63 if (keyToken.isString())
64 {
65 // Enable wildcards
66 keyword = keyToken.stringToken();
67 return true;
68 }
69
70 return false;
71}
72
73
74bool Foam::entry::getKeyword(keyType& keyword, Istream& is)
75{
76 token keyToken;
77 const bool valid = getKeyword(keyword, keyToken, is);
78
79 if (valid)
80 {
81 return true;
82 }
83
84 // Mark as invalid, but allow for some more checking
85 if (keyToken == token::END_BLOCK || is.eof())
86 {
87 return false;
88 }
89
90 // Otherwise the token is invalid
91 std::cerr
92 << "--> FOAM Warning :" << nl
93 << " From function " << FUNCTION_NAME << nl
94 << " in file " << __FILE__ << " at line " << __LINE__ << nl
95 << " Reading " << is.relativeName() << nl
96 << " found " << keyToken << nl
97 << " expected either " << token::END_BLOCK << " or EOF"
98 << std::endl;
99
100 return false;
101}
102
103
105(
106 dictionary& parentDict,
107 Istream& is,
108 const entry::inputMode inpMode,
109 const int endChar
110)
111{
112 // The inputMode for dealing with duplicate entries
113 const entry::inputMode mode =
114 (
115 inpMode == inputMode::GLOBAL
116 ? globalInputMode
117 : inpMode
118 );
119
120 // If somehow the global itself is 'global' - this is a severe logic error.
121 if (mode == inputMode::GLOBAL)
122 {
124 << "Cannot use 'GLOBAL' as an inputMode"
125 << exit(FatalIOError);
126 }
127
129
130 keyType keyword;
131 token keyToken;
132
133 // Get the next keyword and if a valid keyword return true
134 const bool valid = getKeyword(keyword, keyToken, is);
135
136 // Can accept a list of entries too
137 if (keyToken.isLabel() || keyToken.isPunctuation(token::BEGIN_LIST))
138 {
139 is.putBack(keyToken);
140 return parentDict.add
141 (
142 new dictionaryListEntry(parentDict, is),
143 false
144 );
145 }
146
147 if (!valid)
148 {
149 // Error processing for invalid or unexpected input
150
151 // Do some more checking
152 if (keyToken == token::END_BLOCK)
153 {
154 if (token::END_BLOCK != endChar)
155 {
157 << "Unexpected '}' while reading dictionary entry"
158 << exit(FatalIOError);
159 }
160 return false;
161 }
162 if (is.eof())
163 {
164 if (endChar)
165 {
167 << "Unexpected EOF while reading dictionary entry"
168 << exit(FatalIOError);
169 }
170 return false;
171 }
172
173
174 if (endChar)
175 {
177 << "Found " << keyToken
178 << " but expected " << char(endChar)
179 << exit(FatalIOError);
180 }
181 else
182 {
184 << "Found " << keyToken
185 << " but expected EOF, or perhaps a '}' char"
186 << exit(FatalIOError);
187 }
188
189 return false;
190 }
191
192
193 if (keyword[0] == token::HASH)
194 {
195 // Function entry - #function
196
197 if (disableFunctionEntries)
198 {
199 return parentDict.add
200 (
201 new functionEntry
202 (
203 keyword,
204 parentDict,
205 is
206 ),
207 false
208 );
209 }
210
211 const word functionName(keyword.substr(1), false);
212 return functionEntry::execute(functionName, parentDict, is);
213 }
214
215
216 if (!disableFunctionEntries && keyword[0] == token::DOLLAR)
217 {
218 // Substitution entry - $variable
219
220 token nextToken(is);
221 is.putBack(nextToken);
222
223 if (keyword.size() > 2 && keyword[1] == token::BEGIN_BLOCK)
224 {
225 // Recursive substitution mode.
226 // Content between {} is replaced with expansion.
227 // Then let standard variable expansion deal with rest.
228 string expanded = keyword.substr(2, keyword.size()-3);
229
230 // Substitute dictionary and environment variables.
231 // Do not allow empty substitutions.
232 stringOps::inplaceExpand(expanded, parentDict, true, false);
233
234 // Restore the '$' prefix.
235 // Use replace since operator= is private
236 keyword.std::string::replace(1, keyword.size()-1, expanded);
237 }
238
239 if (nextToken == token::BEGIN_BLOCK)
240 {
241 const word varName = keyword.substr(1);
242
243 // Lookup the variable name in the given dictionary
244 const auto finder =
245 parentDict.csearchScoped(varName, keyType::REGEX_RECURSIVE);
246
247 if (finder.good())
248 {
249 // Read as primitiveEntry
250 const keyType newKeyword(finder.ptr()->stream());
251
252 return parentDict.add
253 (
254 new dictionaryEntry(newKeyword, parentDict, is),
255 false
256 );
257 }
258
260 << "Attempt to use undefined variable " << varName
261 << " as keyword"
262 << exit(FatalIOError);
263 return false;
264 }
265 else
266 {
267 // Deal with duplicate entries (at least partially)
268 const bool mergeEntry =
269 (
270 mode == inputMode::MERGE
271 || mode == inputMode::OVERWRITE
272 );
273
274 parentDict.substituteScopedKeyword(keyword, mergeEntry);
275 }
276
277 return true;
278 }
279
280
281 // Normal or scoped entry
282 {
283 token nextToken(is);
284 is.putBack(nextToken);
285
286 if (nextToken == token::END_LIST)
287 {
289 << "Unexpected token encountered for "
290 << keyword << " - " << nextToken.info()
291 << exit(FatalIOError);
292 return false;
293 }
294
295 const bool scoped =
296 (
297 !disableFunctionEntries
298 && (keyword.find('/') != string::npos)
299 );
300
301 // See (using exact match) if entry already present
302 auto finder =
303 (
304 scoped
305 ? parentDict.searchScoped(keyword, keyType::LITERAL)
306 : parentDict.search(keyword, keyType::LITERAL)
307 );
308
309 // How to manage duplicate entries
310 bool mergeEntry = false;
311
312 if (finder.good())
313 {
314 // Use keyword from the found entry (ie, eliminate scoping chars)
315 const keyType key = finder.ref().keyword();
316
317 if (mode == inputMode::PROTECT || keyword == "FoamFile")
318 {
319 // Read and discard if existing element should be protected,
320 // or would potentially alter the "FoamFile" header.
321
322 // Disable function/variable expansion to avoid side-effects
323 const int oldFlag = entry::disableFunctionEntries;
325
326 if (nextToken == token::BEGIN_BLOCK)
327 {
328 dictionaryEntry dummy("dummy", finder.context(), is);
329 }
330 else
331 {
332 primitiveEntry dummy("dummy", finder.context(), is);
333 }
334
336 return true;
337 }
338
339 if (mode == inputMode::ERROR)
340 {
342 << "duplicate entry: " << key
343 << exit(FatalIOError);
344
345 return false;
346 }
347
348 if (mode == inputMode::MERGE)
349 {
350 mergeEntry = true;
351 }
352 else if (mode == inputMode::OVERWRITE)
353 {
354 // Clear existing dictionary so merge acts like overwrite
355 if (finder.isDict())
356 {
357 finder.dict().clear();
358 }
359 mergeEntry = true;
360 }
361
362 // Merge/overwrite data entry
363
364 if (nextToken == token::BEGIN_BLOCK)
365 {
366 return finder.context().add
367 (
368 new dictionaryEntry(key, finder.context(), is),
369 mergeEntry
370 );
371 }
372 else
373 {
374 return finder.context().add
375 (
376 new primitiveEntry(key, finder.context(), is),
377 mergeEntry
378 );
379 }
380 }
381 else if (scoped)
382 {
383 // A slash-scoped entry - did not previously exist
384
385 string fullPath(keyword);
386 fileName::clean(fullPath);
387
388 // Get or create the dictionary-path.
389 // fileName::path == dictionary-path
390 dictionary* subDictPtr =
391 parentDict.makeScopedDict(fileName::path(fullPath));
392
393 if (subDictPtr)
394 {
395 // fileName::name == keyword-name
396 string keyName = fileName::name(fullPath);
397 keyType key;
398
399 // Patterns allowed for the final element.
400 // - use if key name begins with a (single|double) quote
401
402 if (keyName.find_first_of("\"'") == 0)
403 {
404 // Begins with a quote - treat as pattern
405 key = keyType
406 (
407 string::validate<keyType>(keyName),
409 );
410 }
411 else
412 {
413 // Treat as a word
414 key = word::validate(keyName, false);
415 }
416
417 if (nextToken == token::BEGIN_BLOCK)
418 {
419 return subDictPtr->add
420 (
421 new dictionaryEntry(key, *subDictPtr, is),
422 mergeEntry
423 );
424 }
425 else
426 {
427 return subDictPtr->add
428 (
429 new primitiveEntry(key, *subDictPtr, is),
430 mergeEntry
431 );
432 }
433 }
434
435 // Some error finding/creating intermediate dictionaries
436 return false;
437 }
438 else
439 {
440 // A non-scoped entry - did not previously exist
441
442 if (nextToken == token::BEGIN_BLOCK)
443 {
444 return parentDict.add
445 (
446 new dictionaryEntry(keyword, parentDict, is),
447 mergeEntry
448 );
449 }
450 else
451 {
452 return parentDict.add
453 (
454 new primitiveEntry(keyword, parentDict, is),
455 mergeEntry
456 );
457 }
458 }
459 }
460}
461
462
464{
466
467 autoPtr<entry> ptr;
468
469 // Get the next keyword and if invalid return false
470 keyType keyword;
471 if (getKeyword(keyword, is))
472 {
473 // Keyword starts entry ...
474 token nextToken(is);
475 is.putBack(nextToken);
476
477 if (nextToken == token::BEGIN_BLOCK)
478 {
479 // A sub-dictionary
480 ptr.reset(new dictionaryEntry(keyword, dictionary::null, is));
481 }
482 else
483 {
484 ptr.reset(new primitiveEntry(keyword, is));
485 }
486 }
487
488 return ptr;
489}
490
491
492// * * * * * * * * * * * * * Ostream operator * * * * * * * * * * * * * * * //
493
495{
496 e.write(os);
497 return os;
498}
499
500
501// ************************************************************************* //
bool fatalCheck(const char *operation) const
Check IOstream status for given operation.
Definition: IOstream.C:64
bool eof() const noexcept
True if end of input seen.
Definition: IOstream.H:239
An Istream is an abstract base class for all input systems (streams, files, token lists etc)....
Definition: Istream.H:64
void putBack(const token &tok)
Put back a token. Only a single put back is permitted.
Definition: Istream.C:70
An Ostream is an abstract base class for all output systems (streams, files, token lists,...
Definition: Ostream.H:62
static autoPtr< Time > New()
Construct (dummy) Time - no functionObjects or libraries.
Definition: Time.C:717
Pointer management similar to std::unique_ptr, with some additional methods and type checking.
Definition: autoPtr.H:66
void reset(autoPtr< T > &&other) noexcept
Delete managed object and set to new given pointer.
Definition: autoPtrI.H:117
A keyword and a list of tokens is a 'dictionaryEntry'.
Read/write List of dictionaries.
A list of keyword definitions, which are a keyword followed by a number of values (eg,...
Definition: dictionary.H:126
const_searcher csearchScoped(const word &keyword, enum keyType::option matchOpt) const
Search using scoping.
dictionary * makeScopedDict(const fileName &dictPath)
Locate existing or create sub-dictionary using slash-scoping.
const_searcher search(const word &keyword, enum keyType::option matchOpt=keyType::REGEX) const
Search dictionary for given keyword.
bool substituteScopedKeyword(const word &keyword, bool mergeEntry=false)
Substitute the given scoped keyword (which is prefixed by '$')
Definition: dictionary.C:428
entry * add(entry *entryPtr, bool mergeEntry=false)
Add a new entry.
Definition: dictionary.C:640
const_searcher searchScoped(const word &keyword, enum keyType::option matchOpt) const
Search using dot or slash scoping.
A keyword and a list of tokens is an 'entry'.
Definition: entry.H:70
inputMode
The input mode options.
Definition: entry.H:77
const keyType & keyword() const noexcept
Return keyword.
Definition: entry.H:195
static int disableFunctionEntries
Enable or disable use of function entries and variable expansions.
Definition: entry.H:127
bool clean()
Cleanup filename (inplace)
Definition: fileName.C:390
word name() const
Return basename (part beyond last /), including its extension.
Definition: fileNameI.H:212
fileName path() const
Return directory path name (part before last /)
Definition: fileNameI.H:193
A functionEntry causes entries to be added/manipulated on the specified dictionary given an input str...
Definition: functionEntry.H:69
virtual bool execute()
Calculate the output fields.
virtual void validate()
Validate the turbulence fields after construction.
Definition: kkLOmega.C:597
A class for handling keywords in dictionaries.
Definition: keyType.H:71
@ LITERAL
String literal.
Definition: keyType.H:81
@ REGEX_RECURSIVE
Definition: keyType.H:87
@ REGEX
Regular expression.
Definition: keyType.H:82
A keyword and a list of tokens comprise a primitiveEntry. A primitiveEntry can be read,...
virtual bool write(const bool valid=true) const
Write using setting from DB.
A token holds an item read from Istream.
Definition: token.H:69
bool isPunctuation() const noexcept
Token is PUNCTUATION.
Definition: tokenI.H:459
@ BEGIN_BLOCK
Begin block [isseparator].
Definition: token.H:159
@ END_BLOCK
End block [isseparator].
Definition: token.H:160
@ HASH
Hash - directive or start verbatim string.
Definition: token.H:130
@ END_STATEMENT
End entry [isseparator].
Definition: token.H:154
@ BEGIN_LIST
Begin list [isseparator].
Definition: token.H:155
@ DOLLAR
Dollar - start variable or expression.
Definition: token.H:131
@ END_LIST
End list [isseparator].
Definition: token.H:156
bool isLabel() const noexcept
Token is LABEL.
Definition: tokenI.H:497
InfoProxy< token > info() const
Return info proxy for printing token information to a stream.
Definition: token.H:586
static constexpr uint64_t npos
Out of range position or size.
A class for handling words, derived from Foam::string.
Definition: word.H:68
#define FatalIOErrorInFunction(ios)
Report an error message using Foam::FatalIOError.
Definition: error.H:473
OBJstream os(runTime.globalPath()/outputName)
#define FUNCTION_NAME
void inplaceExpand(std::string &s, const HashTable< string > &mapping, const char sigil='$')
Definition: stringOps.C:731
mode_t mode(const fileName &name, const bool followLink=true)
Return the file mode, normally following symbolic links.
Definition: MSwindows.C:572
Ostream & operator<<(Ostream &, const boundaryPatch &p)
Write boundaryPatch as dictionary entries (without surrounding braces)
Definition: boundaryPatch.C:83
IOerror FatalIOError
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:130
constexpr char nl
The newline '\n' character (0x0a)
Definition: Ostream.H:53
volScalarField & e
Definition: createFields.H:11