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-2019 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.name() << nl
96  << " found " << keyToken << nl
97  << " expected either " << token::END_BLOCK << " or EOF"
98  << std::endl;
99  return false;
100 }
101 
102 
103 bool Foam::entry::New
104 (
105  dictionary& parentDict,
106  Istream& is,
107  const entry::inputMode inpMode,
108  const int endChar
109 )
110 {
111  // The inputMode for dealing with duplicate entries
112  const entry::inputMode mode =
113  (
114  inpMode == inputMode::GLOBAL
115  ? globalInputMode
116  : inpMode
117  );
118 
119  // If somehow the global itself is 'global' - this is a severe logic error.
120  if (mode == inputMode::GLOBAL)
121  {
123  << "Cannot use 'GLOBAL' as an inputMode"
124  << exit(FatalIOError);
125  }
126 
128 
129  keyType keyword;
130  token keyToken;
131 
132  // Get the next keyword and if a valid keyword return true
133  const bool valid = getKeyword(keyword, keyToken, is);
134 
135  // Can accept a list of entries too
136  if
137  (
138  keyToken.isLabel()
139  || (keyToken.isPunctuation() && keyToken.pToken() == token::BEGIN_LIST)
140  )
141  {
142  is.putBack(keyToken);
143  return parentDict.add
144  (
145  new dictionaryListEntry(parentDict, is),
146  false
147  );
148  }
149 
150  if (!valid)
151  {
152  // Error processing for invalid or unexpected input
153 
154  // Do some more checking
155  if (keyToken == token::END_BLOCK)
156  {
157  if (token::END_BLOCK != endChar)
158  {
160  << "Unexpected '}' while reading dictionary entry"
161  << exit(FatalIOError);
162  }
163  return false;
164  }
165  if (is.eof())
166  {
167  if (endChar)
168  {
170  << "Unexpected EOF while reading dictionary entry"
171  << exit(FatalIOError);
172  }
173  return false;
174  }
175 
176 
177  if (endChar)
178  {
180  << "Found " << keyToken
181  << " but expected " << char(endChar)
182  << exit(FatalIOError);
183  }
184  else
185  {
187  << "Found " << keyToken
188  << " but expected EOF, or perhaps a '}' char"
189  << exit(FatalIOError);
190  }
191 
192  return false;
193  }
194 
195 
196  if (keyword[0] == token::HASH)
197  {
198  // Function entry - #function
199 
200  if (disableFunctionEntries)
201  {
202  return parentDict.add
203  (
204  new functionEntry
205  (
206  keyword,
207  parentDict,
208  is
209  ),
210  false
211  );
212  }
213 
214  const word functionName(keyword.substr(1), false);
215  return functionEntry::execute(functionName, parentDict, is);
216  }
217 
218 
219  if (!disableFunctionEntries && keyword[0] == token::DOLLAR)
220  {
221  // Substitution entry - $variable
222 
223  token nextToken(is);
224  is.putBack(nextToken);
225 
226  if (keyword.size() > 2 && keyword[1] == token::BEGIN_BLOCK)
227  {
228  // Recursive substitution mode.
229  // Content between {} is replaced with expansion.
230  // Then let standard variable expansion deal with rest.
231  string expanded = keyword.substr(2, keyword.size()-3);
232 
233  // Substitute dictionary and environment variables.
234  // Do not allow empty substitutions.
235  stringOps::inplaceExpand(expanded, parentDict, true, false);
236 
237  // Restore the '$' prefix.
238  // Use replace since operator= is private
239  keyword.std::string::replace(1, keyword.size()-1, expanded);
240  }
241 
242  if (nextToken == token::BEGIN_BLOCK)
243  {
244  const word varName = keyword.substr(1);
245 
246  // Lookup the variable name in the given dictionary
247  const auto finder =
248  parentDict.csearchScoped(varName, keyType::REGEX_RECURSIVE);
249 
250  if (finder.good())
251  {
252  // Read as primitiveEntry
253  const keyType newKeyword(finder.ptr()->stream());
254 
255  return parentDict.add
256  (
257  new dictionaryEntry(newKeyword, parentDict, is),
258  false
259  );
260  }
261 
263  << "Attempt to use undefined variable " << varName
264  << " as keyword"
265  << exit(FatalIOError);
266  return false;
267  }
268  else
269  {
270  // Deal with duplicate entries (at least partially)
271  const bool mergeEntry =
272  (
273  mode == inputMode::MERGE
274  || mode == inputMode::OVERWRITE
275  );
276 
277  parentDict.substituteScopedKeyword(keyword, mergeEntry);
278  }
279 
280  return true;
281  }
282 
283 
284  // Normal or scoped entry
285  {
286  token nextToken(is);
287  is.putBack(nextToken);
288 
289  if (nextToken == token::END_LIST)
290  {
292  << "Unexpected token encountered for "
293  << keyword << " - " << nextToken.info()
294  << exit(FatalIOError);
295  return false;
296  }
297 
298  const bool scoped =
299  (
300  !disableFunctionEntries
301  && (keyword.find('/') != string::npos)
302  );
303 
304  // See (using exact match) if entry already present
305  auto finder =
306  (
307  scoped
308  ? parentDict.searchScoped(keyword, keyType::LITERAL)
309  : parentDict.search(keyword, keyType::LITERAL)
310  );
311 
312  // How to manage duplicate entries
313  bool mergeEntry = false;
314 
315  if (finder.good())
316  {
317  // Use keyword from the found entry (ie, eliminate scoping chars)
318  const keyType key = finder.ref().keyword();
319 
320  if (mode == inputMode::PROTECT || keyword == "FoamFile")
321  {
322  // Read and discard if existing element should be protected,
323  // or would potentially alter the "FoamFile" header.
324 
325  // Disable function/variable expansion to avoid side-effects
326  const int oldFlag = entry::disableFunctionEntries;
328 
329  if (nextToken == token::BEGIN_BLOCK)
330  {
331  dictionaryEntry dummy("dummy", finder.context(), is);
332  }
333  else
334  {
335  primitiveEntry dummy("dummy", finder.context(), is);
336  }
337 
339  return true;
340  }
341 
342  if (mode == inputMode::ERROR)
343  {
345  << "duplicate entry: " << key
346  << exit(FatalIOError);
347 
348  return false;
349  }
350 
351  if (mode == inputMode::MERGE)
352  {
353  mergeEntry = true;
354  }
355  else if (mode == inputMode::OVERWRITE)
356  {
357  // Clear existing dictionary so merge acts like overwrite
358  if (finder.isDict())
359  {
360  finder.dict().clear();
361  }
362  mergeEntry = true;
363  }
364 
365  // Merge/overwrite data entry
366 
367  if (nextToken == token::BEGIN_BLOCK)
368  {
369  return finder.context().add
370  (
371  new dictionaryEntry(key, finder.context(), is),
372  mergeEntry
373  );
374  }
375  else
376  {
377  return finder.context().add
378  (
379  new primitiveEntry(key, finder.context(), is),
380  mergeEntry
381  );
382  }
383  }
384  else if (scoped)
385  {
386  // A slash-scoped entry - did not previously exist
387 
388  string fullPath(keyword);
389  fileName::clean(fullPath);
390 
391  // Get or create the dictionary-path.
392  // fileName::path == dictionary-path
393  dictionary* subDictPtr =
394  parentDict.makeScopedDict(fileName::path(fullPath));
395 
396  if (subDictPtr)
397  {
398  // fileName::name == keyword-name
399  string keyName = fileName::name(fullPath);
400  keyType key;
401 
402  // Patterns allowed for the final element.
403  // - use if key name begins with a (single|double) quote
404 
405  if (keyName.find_first_of("\"'") == 0)
406  {
407  // Begins with a quote - treat as pattern
408  key = keyType
409  (
410  string::validate<keyType>(keyName),
412  );
413  }
414  else
415  {
416  // Treat as a word
417  key = word::validate(keyName, false);
418  }
419 
420  if (nextToken == token::BEGIN_BLOCK)
421  {
422  return subDictPtr->add
423  (
424  new dictionaryEntry(key, *subDictPtr, is),
425  mergeEntry
426  );
427  }
428  else
429  {
430  return subDictPtr->add
431  (
432  new primitiveEntry(key, *subDictPtr, is),
433  mergeEntry
434  );
435  }
436  }
437 
438  // Some error finding/creating intermediate dictionaries
439  return false;
440  }
441  else
442  {
443  // A non-scoped entry - did not previously exist
444 
445  if (nextToken == token::BEGIN_BLOCK)
446  {
447  return parentDict.add
448  (
449  new dictionaryEntry(keyword, parentDict, is),
450  mergeEntry
451  );
452  }
453  else
454  {
455  return parentDict.add
456  (
457  new primitiveEntry(keyword, parentDict, is),
458  mergeEntry
459  );
460  }
461  }
462  }
463 }
464 
465 
467 {
469 
470  autoPtr<entry> ptr;
471 
472  // Get the next keyword and if invalid return false
473  keyType keyword;
474  if (getKeyword(keyword, is))
475  {
476  // Keyword starts entry ...
477  token nextToken(is);
478  is.putBack(nextToken);
479 
480  if (nextToken == token::BEGIN_BLOCK)
481  {
482  // A sub-dictionary
483  ptr.reset(new dictionaryEntry(keyword, dictionary::null, is));
484  }
485  else
486  {
487  ptr.reset(new primitiveEntry(keyword, is));
488  }
489  }
490 
491  return ptr;
492 }
493 
494 
495 // * * * * * * * * * * * * * Ostream operator * * * * * * * * * * * * * * * //
496 
498 {
499  e.write(os);
500  return os;
501 }
502 
503 
504 // ************************************************************************* //
Foam::entry
A keyword and a list of tokens is an 'entry'.
Definition: entry.H:67
Foam::autoPtr::reset
void reset(T *p=nullptr) noexcept
Delete managed object and set to new given pointer.
Definition: autoPtrI.H:109
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:104
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:481
primitiveEntry.H
Foam::word
A class for handling words, derived from Foam::string.
Definition: word.H:62
Foam::entry::keyword
const keyType & keyword() const
Return keyword.
Definition: entry.H:187
Foam::IOstream::eof
bool eof() const
Return true if end of input seen.
Definition: IOstream.H:230
Foam::fileName::path
fileName path() const
Return directory path name (part before last /)
Definition: fileNameI.H:203
Foam::IOstream::fatalCheck
bool fatalCheck(const char *operation) const
Check IOstream status for given operation.
Definition: IOstream.C:57
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::FatalIOError
IOerror FatalIOError
Foam::endl
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:350
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::token::pToken
punctuationToken pToken() const
Return punctuation character.
Definition: tokenI.H:459
Foam::fileName::name
word name() const
Return basename (part beyond last /), including its extension.
Definition: fileNameI.H:222
Foam::dictionary::null
static const dictionary null
An empty dictionary, which is also the parent for all dictionaries.
Definition: dictionary.H:385
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:60
Foam::token::info
InfoProxy< token > info() const
Return info proxy for printing token information to a stream.
Definition: token.H:551
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:480
dictionaryListEntry.H
Foam::stringOps::inplaceExpand
void inplaceExpand(std::string &s, const HashTable< string, word, string::hash > &mapping, const char sigil='$')
Definition: stringOps.C:733
Foam::token::isPunctuation
bool isPunctuation() const noexcept
Token is PUNCTUATION.
Definition: tokenI.H:453
Foam::token::END_STATEMENT
End entry [isseparator].
Definition: token.H:121
Foam::token::END_BLOCK
End block [isseparator].
Definition: token.H:127
Foam::token::DOLLAR
Dollar - start variable.
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:121
Foam::token::HASH
Hash - directive or 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:119
dictionaryEntry.H
Foam::token::BEGIN_BLOCK
Begin block [isseparator].
Definition: token.H:126
Foam::functionEntry
A functionEntry causes entries to be added/manipulated on the specified dictionary given an input str...
Definition: functionEntry.H:65
Foam::exit
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:130
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:385
Foam::Istream::putBack
void putBack(const token &tok)
Put back token.
Definition: Istream.C:53
includeEntry.H
Foam::constant::electromagnetic::e
const dimensionedScalar e
Elementary charge.
Definition: createFields.H:11
Foam::keyType::REGEX
Regular expression.
Definition: keyType.H:74
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:466
FUNCTION_NAME
#define FUNCTION_NAME
Definition: messageStream.H:270
Foam::token::END_LIST
End list [isseparator].
Definition: token.H:123
Foam::dictionary::add
entry * add(entry *entryPtr, bool mergeEntry=false)
Add a new entry.
Definition: dictionary.C:708
FatalIOErrorInFunction
#define FatalIOErrorInFunction(ios)
Report an error message using Foam::FatalIOError.
Definition: error.H:401
Foam::fileName::clean
bool clean()
Cleanup filename inplace.
Definition: fileName.C:388
Foam::dictionaryListEntry
Read/write List of dictionaries.
Definition: dictionaryListEntry.H:64
Foam::keyType::LITERAL
String literal.
Definition: keyType.H:73
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:122
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:83
Foam::keyType::REGEX_RECURSIVE
Definition: keyType.H:79