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 -------------------------------------------------------------------------------
11 License
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 
39 bool 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 
74 bool 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 
104 bool Foam::entry::New
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 // ************************************************************************* //
Foam::entry
A keyword and a list of tokens is an 'entry'.
Definition: entry.H:67
Foam::dictionaryEntry
A keyword and a list of tokens is a 'dictionaryEntry'.
Definition: dictionaryEntry.H:65
Foam::entry::New
static bool New(dictionary &parentDict, Istream &is, const inputMode inpMode=inputMode::GLOBAL, const int endChar=0)
Construct from an Istream and insert into the dictionary.
Definition: entryIO.C:105
Foam::primitiveEntry
A keyword and a list of tokens comprise a primitiveEntry. A primitiveEntry can be read,...
Definition: primitiveEntry.H:63
Foam::token::isLabel
bool isLabel() const noexcept
Token is LABEL.
Definition: tokenI.H:497
primitiveEntry.H
Foam::word
A class for handling words, derived from Foam::string.
Definition: word.H:65
Foam::fileName::path
fileName path() const
Return directory path name (part before last /)
Definition: fileNameI.H:193
Foam::glTF::key
auto key(const Type &t) -> typename std::enable_if< std::is_enum< Type >::value, typename std::underlying_type< Type >::type >::type
Definition: foamGltfBase.H:108
Foam::IOstream::fatalCheck
bool fatalCheck(const char *operation) const
Check IOstream status for given operation.
Definition: IOstream.C:64
Foam::dictionary::searchScoped
const_searcher searchScoped(const word &keyword, enum keyType::option) const
Search using dot or slash scoping.
Definition: dictionarySearch.C:355
Foam::word::validate
static word validate(const std::string &s, const bool prefix=false)
Construct validated word (no invalid characters).
Definition: word.C:45
Foam::entry::keyword
const keyType & keyword() const noexcept
Return keyword.
Definition: entry.H:195
Foam::FatalIOError
IOerror FatalIOError
Foam::endl
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:369
Foam::token
A token holds an item read from Istream.
Definition: token.H:68
functionEntry.H
Foam::entry::inputMode
inputMode
The input mode options.
Definition: entry.H:76
entry.H
Foam::fileName::name
word name() const
Return basename (part beyond last /), including its extension.
Definition: fileNameI.H:212
Foam::dictionary::null
static const dictionary null
An empty dictionary, which is also the parent for all dictionaries.
Definition: dictionary.H:392
Foam::mode
mode_t mode(const fileName &name, const bool followLink=true)
Return the file mode, normally following symbolic links.
Definition: MSwindows.C:564
Foam::operator<<
Ostream & operator<<(Ostream &, const boundaryPatch &p)
Write boundaryPatch as dictionary entries (without surrounding braces)
Definition: boundaryPatch.C:83
Foam::keyType
A class for handling keywords in dictionaries.
Definition: keyType.H:68
Foam::stringOps::inplaceExpand
void inplaceExpand(std::string &s, const HashTable< string > &mapping, const char sigil='$')
Definition: stringOps.C:731
Foam::token::info
InfoProxy< token > info() const
Return info proxy for printing token information to a stream.
Definition: token.H:586
Foam::Istream
An Istream is an abstract base class for all input systems (streams, files, token lists etc)....
Definition: Istream.H:61
Foam::dictionary::csearchScoped
const_searcher csearchScoped(const word &keyword, enum keyType::option) const
Search using scoping.
Definition: dictionarySearch.C:325
Foam::dictionary::makeScopedDict
dictionary * makeScopedDict(const fileName &dictPath)
Locate existing or create sub-dictionary using slash-scoping.
Definition: dictionarySearch.C:482
Foam::IOstream::eof
bool eof() const noexcept
True if end of input seen.
Definition: IOstream.H:239
dictionaryListEntry.H
Foam::token::isPunctuation
bool isPunctuation() const noexcept
Token is PUNCTUATION.
Definition: tokenI.H:459
Foam::token::END_STATEMENT
End entry [isseparator].
Definition: token.H:154
Foam::token::END_BLOCK
End block [isseparator].
Definition: token.H:160
Foam::token::DOLLAR
Dollar - start variable or expression.
Definition: token.H:131
Foam::dictionary
A list of keyword definitions, which are a keyword followed by a number of values (eg,...
Definition: dictionary.H:123
os
OBJstream os(runTime.globalPath()/outputName)
Foam::token::HASH
Hash - directive or start verbatim string.
Definition: token.H:130
Foam::entry::disableFunctionEntries
static int disableFunctionEntries
Enable or disable use of function entries and variable expansions.
Definition: entry.H:127
dictionaryEntry.H
Foam::token::BEGIN_BLOCK
Begin block [isseparator].
Definition: token.H:159
Foam::functionEntry
A functionEntry causes entries to be added/manipulated on the specified dictionary given an input str...
Definition: functionEntry.H:66
Foam::exit
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:130
Foam::autoPtr::reset
void reset(autoPtr< T > &&other) noexcept
Delete managed object and set to new given pointer.
Definition: autoPtrI.H:117
Foam::autoPtr
Pointer management similar to std::unique_ptr, with some additional methods and type checking.
Definition: HashPtrTable.H:53
Foam::nl
constexpr char nl
Definition: Ostream.H:404
Foam::Istream::putBack
void putBack(const token &tok)
Put back a token. Only a single put back is permitted.
Definition: Istream.C:70
includeEntry.H
Foam::constant::electromagnetic::e
const dimensionedScalar e
Elementary charge.
Definition: createFields.H:11
Foam::keyType::REGEX
Regular expression.
Definition: keyType.H:82
Foam::dictionary::search
const_searcher search(const word &keyword, enum keyType::option=keyType::REGEX) const
Search dictionary for given keyword.
Definition: dictionarySearch.C:303
Foam::dictionary::substituteScopedKeyword
bool substituteScopedKeyword(const word &keyword, bool mergeEntry=false)
Substitute the given scoped keyword (which is prefixed by '$')
Definition: dictionary.C:428
FUNCTION_NAME
#define FUNCTION_NAME
Definition: messageStream.H:295
Foam::token::END_LIST
End list [isseparator].
Definition: token.H:156
Foam::dictionary::add
entry * add(entry *entryPtr, bool mergeEntry=false)
Add a new entry.
Definition: dictionary.C:640
FatalIOErrorInFunction
#define FatalIOErrorInFunction(ios)
Report an error message using Foam::FatalIOError.
Definition: error.H:473
Foam::fileName::clean
bool clean()
Cleanup filename (inplace)
Definition: fileName.C:390
Foam::dictionaryListEntry
Read/write List of dictionaries.
Definition: dictionaryListEntry.H:64
Foam::keyType::LITERAL
String literal.
Definition: keyType.H:81
Foam::Ostream
An Ostream is an abstract base class for all output systems (streams, files, token lists,...
Definition: Ostream.H:56
Foam::token::BEGIN_LIST
Begin list [isseparator].
Definition: token.H:155
stringOps.H
Foam::functionEntry::execute
static bool execute(const word &functionName, dictionary &parentDict, Istream &is)
Execute the functionEntry in a sub-dict context.
Definition: functionEntry.C:98
Foam::keyType::REGEX_RECURSIVE
Definition: keyType.H:87