foamDictionary.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) 2016-2017 OpenFOAM Foundation
9  Copyright (C) 2017-2023 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 Application
28  foamDictionary
29 
30 Description
31  Interrogate and manipulate dictionaries.
32 
33 Usage
34  \b foamDictionary [OPTION] dictionary
35 
36  - \par -entry <name>
37  Selects an entry
38 
39  - \par -keywords
40  Prints the keywords (of the selected entry or of the top level if
41  no entry was selected
42 
43  - \par -add <value>
44  Adds the entry (should not exist yet)
45 
46  - \par -set <value>
47  Adds or replaces the entry
48 
49  - \par -remove
50  Remove the selected entry
51 
52  - \par -diff <dictionary>
53  Write differences with respect to the specified dictionary
54  (or sub entry if -entry specified)
55 
56  - \par -diff-etc <dictionary>
57  Write differences with respect to the specified dictionary
58  (or sub entry if -entry specified)
59 
60  - \par -expand
61  Read the specified dictionary file, expand the macros etc. and write
62  the resulting dictionary to standard output.
63 
64  - \par -includes
65  List the \c \#include and \c \#sinclude files to standard output
66 
67  - \par -disableFunctionEntries
68  Do not expand macros or directives (\#include etc)
69 
70  - \par -precision int
71  Set default write precision for IOstreams
72 
73  Example usage:
74  - Change simulation to run for one timestep only:
75  \verbatim
76  foamDictionary system/controlDict -entry stopAt -set writeNow
77  \endverbatim
78 
79  - Change solver:
80  \verbatim
81  foamDictionary system/fvSolution -entry solvers/p/solver -set PCG
82  \endverbatim
83 
84  - Print bc type:
85  \verbatim
86  foamDictionary 0/U -entry boundaryField/movingWall/type
87  \endverbatim
88 
89  - Change bc parameter:
90  \verbatim
91  foamDictionary 0/U -entry boundaryField/movingWall/value \
92  -set "uniform (2 0 0)"
93  \endverbatim
94 
95  - Change whole bc type:
96  \verbatim
97  foamDictionary 0/U -entry boundaryField/movingWall \
98  -set "{type uniformFixedValue; uniformValue (2 0 0);}"
99  \endverbatim
100 
101  - Write the differences with respect to a template dictionary:
102  \verbatim
103  foamDictionary 0/U -diff-etc templates/closedVolume/0/U
104  \endverbatim
105 
106  - Write the differences in boundaryField with respect to a
107  template dictionary:
108  \verbatim
109  foamDictionary 0/U -diff-etc templates/closedVolume/0/U \
110  -entry boundaryField
111  \endverbatim
112 
113  - Change patch type:
114  \verbatim
115  foamDictionary constant/polyMesh/boundary \
116  -entry entry0/fixedWalls/type -set patch
117  \endverbatim
118  This uses special parsing of Lists which stores these in the
119  dictionary with keyword 'entryDDD' where DDD is the position
120  in the dictionary (after ignoring the FoamFile entry).
121 
122  Notes:
123  - the use of '.' as the scoping symbol might conflict with
124  e.g. file extensions ('.' is not really considered
125  to be a valid word character). Instead use the '/' as a scoping
126  character e.g.
127  foamDictionary system/snappyHexMeshDict \
128  -entry /geometry/motorBike.obj -remove
129 
130 \*---------------------------------------------------------------------------*/
131 
132 #include "argList.H"
133 #include "profiling.H"
134 #include "Time.H"
135 #include "Fstream.H"
136 #include "etcFiles.H"
137 #include "includeEntry.H"
138 
139 using namespace Foam;
140 
141 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
142 
143 //- Convert very old ':' scope syntax to less old '.' scope syntax,
144 // but leave anything with '/' delimiters untouched
145 bool upgradeScope(word& entryName)
146 {
147  if (entryName.contains(':') && !entryName.contains('/'))
148  {
149  InfoErr
150  << "Warning: upgrading very old ':' scope syntax: \""
151  << entryName << '"' << endl;
152 
153  // Make copy - cannot use stringOps::split
154  const wordList cmpts(fileName(entryName).components(':'));
155 
156  entryName.clear();
157 
158  bool addSep = false;
159 
160  for (const word& cmpt : cmpts)
161  {
162  if (addSep) entryName += '.';
163  if (!cmpt.empty())
164  {
165  addSep = true;
166  entryName += cmpt;
167  }
168  }
169 
170  return true;
171  }
172 
173  // Nothing changed
174  return false;
175 }
176 
177 
178 //- Split into dictionary name and the entry name
179 class dictAndKeyword
180 {
181  word dict_;
182  word key_;
183 
184 public:
185 
186  dictAndKeyword(const word& scopedName)
187  {
188  auto i = scopedName.rfind('/');
189  if (i == std::string::npos)
190  {
191  i = scopedName.rfind('.');
192  }
193 
194  if (i != std::string::npos)
195  {
196  dict_ = scopedName.substr(0, i);
197  key_ = scopedName.substr(i+1);
198  }
199  else
200  {
201  key_ = scopedName;
202  }
203  }
204 
205  const word& dict() const noexcept { return dict_; }
206 
207  const word& key() const noexcept { return key_; }
208 };
209 
210 
211 const dictionary& lookupScopedDict
212 (
213  const dictionary& dict,
214  const word& subDictName
215 )
216 {
217  if (subDictName.empty())
218  {
219  return dict;
220  }
221 
222  const entry* eptr = dict.findScoped(subDictName, keyType::LITERAL);
223 
224  if (!eptr || !eptr->isDict())
225  {
227  << "'" << subDictName << "' not found in dictionary "
228  << dict.name() << " or is not a dictionary" << nl
229  << "Known entries are " << dict.keys()
230  << exit(FatalIOError);
231  }
232 
233  return eptr->dict();
234 }
235 
236 
237 void removeDict(dictionary& dict, const dictionary& dictToRemove)
238 {
239  for (const entry& refEntry : dictToRemove)
240  {
241  auto finder = dict.search(refEntry.keyword(), keyType::LITERAL);
242 
243  bool purge = false;
244 
245  if (finder.isDict())
246  {
247  if (refEntry.isDict())
248  {
249  removeDict(finder.dict(), refEntry.dict());
250 
251  // Purge if dictionary is empty
252  purge = finder.dict().empty();
253  }
254  }
255  else if (finder.good() && !refEntry.isDict())
256  {
257  // Purge if entries match
258  purge = (finder.ref() == refEntry);
259  }
260 
261  if (purge)
262  {
263  dict.remove(refEntry.keyword());
264  }
265  }
266 }
267 
268 
269 int main(int argc, char *argv[])
270 {
272  (
273  "Interrogate and manipulate dictionaries"
274  );
275 
278  argList::addArgument("dict", "The dictionary file to process");
279  argList::addBoolOption("keywords", "List keywords");
280  argList::addOption("entry", "name", "Report/select the named entry");
282  (
283  "value",
284  "Print entry value"
285  );
287  (
288  "set",
289  "value",
290  "Set entry value or add new entry"
291  );
293  (
294  "add",
295  "value",
296  "Add a new entry"
297  );
299  (
300  "remove",
301  "Remove the entry"
302  );
304  (
305  "diff",
306  "dict",
307  "Write differences with respect to the specified dictionary"
308  );
310  (
311  "diff-etc",
312  "dict",
313  "As per -diff, but locate the file as per foamEtcFile"
314  );
315  argList::addOptionCompat("diff-etc", {"diffEtc", 1712});
317  (
318  "precision",
319  "int",
320  "Set default write precision for IOstreams"
321  );
322 
324  (
325  "includes",
326  "List the #include/#sinclude files to standard output"
327  );
329  (
330  "expand",
331  "Read the specified dictionary file, expand the macros etc. and write "
332  "the resulting dictionary to standard output"
333  );
335  (
336  "disableFunctionEntries",
337  "Disable expansion of dictionary directives - #include, #codeStream etc"
338  );
339  profiling::disable(); // Disable profiling (and its output)
340 
341  argList args(argc, argv);
342 
343  const bool listIncludes = args.found("includes");
344 
345  if (listIncludes)
346  {
348  }
349 
350  const bool disableEntries = args.found("disableFunctionEntries");
351  if (disableEntries)
352  {
353  // Report on stderr (once) to avoid polluting the output
354  InfoErr<< "Not expanding variables or dictionary directives" << endl;
356  }
357 
358  // Set the default output precision
359  {
360  const unsigned prec = args.getOrDefault<unsigned>("precision", 0u);
361  if (prec)
362  {
363  // InfoErr<< "Output write precision set to " << prec << endl;
365  Sout.precision(prec);
366  }
367  }
368 
369  const auto dictFileName = args.get<fileName>(1);
370 
371  autoPtr<IFstream> dictFile(new IFstream(dictFileName));
372  if (!dictFile().good())
373  {
375  << "Cannot open file " << dictFileName
376  << exit(FatalError, 1);
377  }
378 
379 
380  bool changed = false;
381 
382  // Read but preserve headers
383  dictionary dict(dictFile(), true);
384 
385  if (listIncludes)
386  {
387  return 0;
388  }
389  else if (args.found("expand"))
390  {
392  <<"//\n// " << dictFileName << "\n//\n";
393  dict.write(Info, false);
395 
396  return 0;
397  }
398 
399 
400  // Has "diff" or "diff-etc"
401  bool optDiff = false;
402 
403  // Reference dictionary for -diff / -diff-etc
404  dictionary diffDict;
405  {
406  fileName diffFileName;
407  if (args.readIfPresent("diff", diffFileName))
408  {
409  IFstream diffFile(diffFileName);
410  if (!diffFile.good())
411  {
413  << "Cannot open file " << diffFileName
414  << exit(FatalError, 1);
415  }
416 
417  // Read but preserve headers
418  diffDict.read(diffFile, true);
419  optDiff = true;
420  }
421  else if (args.readIfPresent("diff-etc", diffFileName))
422  {
423  fileName foundName = findEtcFile(diffFileName);
424  if (foundName.empty())
425  {
427  << "Cannot find etcFile " << diffFileName
428  << exit(FatalError, 1);
429  }
430 
431  IFstream diffFile(foundName);
432  if (!diffFile.good())
433  {
435  << "Cannot open file " << foundName
436  << exit(FatalError, 1);
437  }
438 
439  // Read but preserve headers
440  diffDict.read(diffFile, true);
441  optDiff = true;
442  }
443  }
444 
445  word scopedName; // Actually fileName, since it can contain '/' scoping
446  if (args.readIfPresent("entry", scopedName))
447  {
448  upgradeScope(scopedName);
449 
450  string newValue;
451  if
452  (
453  args.readIfPresent("set", newValue)
454  || args.readIfPresent("add", newValue)
455  )
456  {
457  const bool overwrite = args.found("set");
458 
459  // Dictionary name and keyword
460  const dictAndKeyword dAk(scopedName);
461 
462  // The context for the action
463  const dictionary& d(lookupScopedDict(dict, dAk.dict()));
464 
465  // Create a new entry
466  IStringStream str(string(dAk.key()) + ' ' + newValue + ';');
467  entry* ePtr(entry::New(str).ptr());
468 
469  if (overwrite)
470  {
471  const_cast<dictionary&>(d).set(ePtr);
472  }
473  else
474  {
475  const_cast<dictionary&>(d).add(ePtr, false);
476  }
477  changed = true;
478 
479  const auto finder = dict.csearchScoped(scopedName, keyType::REGEX);
480 
481  // Print the changed entry to stderr
482  if (finder.good())
483  {
484  InfoErr<< finder.ref();
485  }
486  }
487  else if (args.found("remove"))
488  {
489  // Dictionary name and keyword
490  const dictAndKeyword dAk(scopedName);
491 
492  // The context for the action
493  const dictionary& d(lookupScopedDict(dict, dAk.dict()));
494 
495  const_cast<dictionary&>(d).remove(dAk.key());
496  changed = true;
497  }
498  else
499  {
500  // Optionally remove a second dictionary
501  if (optDiff)
502  {
503  // Dictionary name and keyword
504  const dictAndKeyword dAk(scopedName);
505 
506  const dictionary& d1(lookupScopedDict(dict, dAk.dict()));
507  const dictionary& d2(lookupScopedDict(diffDict, dAk.dict()));
508 
509  const entry* e1Ptr = d1.findEntry(dAk.key(), keyType::REGEX);
510  const entry* e2Ptr = d2.findEntry(dAk.key(), keyType::REGEX);
511 
512  if (e1Ptr && e2Ptr)
513  {
514  if (*e1Ptr == *e2Ptr)
515  {
516  const_cast<dictionary&>(d1).remove(dAk.key());
517  }
518  else if (e1Ptr->isDict() && e2Ptr->isDict())
519  {
520  removeDict
521  (
522  const_cast<dictionary&>(e1Ptr->dict()),
523  e2Ptr->dict()
524  );
525  }
526  }
527  }
528 
529  const auto finder = dict.csearchScoped(scopedName, keyType::REGEX);
530 
531  if (!finder.good())
532  {
533  FatalIOErrorInFunction(dictFile())
534  << "Cannot find entry " << scopedName
535  << exit(FatalIOError, 2);
536  }
537  else if (args.found("keywords"))
538  {
539  // Report keywords to stdout
540  for (const entry& e : finder.dict())
541  {
542  Info<< e.keyword() << endl;
543  }
544  }
545  else if (args.found("value"))
546  {
547  // Report value to stdout
548  if (finder.isDict())
549  {
550  Info<< finder.dict();
551  }
552  else if (finder.ref().isStream())
553  {
554  bool addSep = false;
555 
556  const tokenList& tokens = finder.ref().stream();
557 
558  for (const token& tok : tokens)
559  {
560  if (addSep) Info<< token::SPACE;
561  addSep = true;
562  Info<< tok;
563  }
564  Info<< endl;
565  }
566  }
567  else
568  {
569  // Report entry to stdout
570  Info<< finder.ref();
571  }
572  }
573  }
574  else if (args.found("keywords"))
575  {
576  // Report keywords to stdout
577  for (const entry& e : dict)
578  {
579  Info<< e.keyword() << endl;
580  }
581  }
582  else if (optDiff)
583  {
584  // Report difference to stdout
585  removeDict(dict, diffDict);
586  dict.write(Info, false);
587  }
588  else
589  {
590  // Report dictionary to stdout
591  dict.write(Info, false);
592  }
593 
594  if (changed)
595  {
596  dictFile.clear();
597  OFstream os(dictFileName);
600  dict.write(os, false);
602  }
603 
604  return 0;
605 }
606 
607 
608 // ************************************************************************* //
static void noJobInfo()
Suppress JobInfo, overriding controlDict setting.
Definition: argList.C:567
dict_reference dict() const
Reference the found entry as a dictionary.
Definition: dictionary.H:278
dictionary dict
static void addNote(const string &note)
Add extra notes for the usage information.
Definition: argList.C:462
A class for handling file names.
Definition: fileName.H:72
bool contains(char c) const noexcept
True if string contains given character (cf. C++23)
Definition: string.H:411
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:125
messageStream InfoErr
Information stream (stderr output on master, null elsewhere)
bool read(Istream &is)
Read dictionary from Istream (discards the header). Reads entries until EOF or when the first token i...
Definition: dictionaryIO.C:134
error FatalError
Error stream (stdout output on all processes), with additional &#39;FOAM FATAL ERROR&#39; header text and sta...
A list of keyword definitions, which are a keyword followed by a number of values (eg...
Definition: dictionary.H:129
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:598
Output to file stream, using an OSstream.
Definition: OFstream.H:49
constexpr char nl
The newline &#39;\n&#39; character (0x0a)
Definition: Ostream.H:50
A token holds an item read from Istream.
Definition: token.H:65
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:531
static Ostream & writeDivider(Ostream &os)
Write the standard file section divider.
virtual const dictionary & dict() const =0
Return dictionary, if entry is a dictionary.
static unsigned int defaultPrecision() noexcept
Return the default precision.
Definition: IOstream.H:423
static void addBoolOption(const word &optName, const string &usage="", bool advanced=false)
Add a bool option to validOptions with usage information.
Definition: argList.C:374
static void addOptionCompat(const word &optName, std::pair< const char *, int > compat)
Specify an alias for the option name.
Definition: argList.C:418
T getOrDefault(const word &optName, const T &deflt) const
Get a value from the named option if present, or return default.
Definition: argListI.H:300
const fileName & name() const noexcept
The dictionary name.
Definition: dictionaryI.H:41
static void disable() noexcept
Disallow profiling - turns the InfoSwitch off.
Definition: profiling.C:113
const entry * findScoped(const word &keyword, enum keyType::option matchOpt=keyType::REGEX) const
Search for a scoped entry (const access) with the given keyword.
Definition: dictionaryI.H:114
bool remove(const word &keyword)
Remove an entry specified by keyword.
const_searcher search(const word &keyword, enum keyType::option matchOpt=keyType::REGEX) const
Search dictionary for given keyword.
static int disableFunctionEntries
Enable or disable use of function entries and variable expansions.
Definition: entry.H:139
const dimensionedScalar e
Elementary charge.
Definition: createFields.H:11
virtual int precision() const override
Get precision of output field.
Definition: OSstream.C:334
A class for handling words, derived from Foam::string.
Definition: word.H:63
Functions to search &#39;etc&#39; directories for configuration files etc.
Extract command arguments and options from the supplied argc and argv parameters. ...
Definition: argList.H:118
Space [isspace].
Definition: token.H:131
OSstream Sout
OSstream wrapped stdout (std::cout)
static void addOption(const word &optName, const string &param="", const string &usage="", bool advanced=false)
Add an option to validOptions with usage information.
Definition: argList.C:385
String literal.
Definition: keyType.H:82
static Ostream & writeEndDivider(Ostream &os)
Write the standard end file divider.
const direction noexcept
Definition: Scalar.H:258
void add(FieldField< Field1, typename typeOfSum< Type1, Type2 >::type > &f, const FieldField< Field1, Type1 > &f1, const FieldField< Field2, Type2 > &f2)
OBJstream os(runTime.globalPath()/outputName)
const_searcher csearchScoped(const word &keyword, enum keyType::option matchOpt) const
Search using scoping.
Input from file stream, using an ISstream.
Definition: IFstream.H:49
List< keyType > keys(bool patterns=false) const
Return the list of available keys or patterns.
Definition: dictionary.C:607
void write(Ostream &os, const bool subDict=true) const
Write dictionary, normally with sub-dictionary formatting.
Definition: dictionaryIO.C:204
T get(const label index) const
Get a value from the argument at index.
Definition: argListI.H:271
fileName findEtcFile(const fileName &name, const bool mandatory=false, unsigned short location=0777)
Search for a single FILE within the etc directories.
Definition: etcFiles.C:439
#define FatalIOErrorInFunction(ios)
Report an error message using Foam::FatalIOError.
Definition: error.H:627
auto key(const Type &t) -> typename std::enable_if< std::is_enum< Type >::value, typename std::underlying_type< Type >::type >::type
Definition: foamGltfBase.H:103
Input from string buffer, using a ISstream. Always UNCOMPRESSED.
Definition: StringStream.H:120
static void addArgument(const string &argName, const string &usage="")
Append a (mandatory) argument to validArgs.
Definition: argList.C:351
messageStream Info
Information stream (stdout output on master, null elsewhere)
static void noBanner()
Disable emitting the banner information.
Definition: argList.C:491
virtual bool isDict() const noexcept
Return true if this entry is a dictionary.
Definition: entry.H:287
static Ostream & writeBanner(Ostream &os, const bool noSyntaxHint=false)
Write the standard OpenFOAM file/dictionary banner.
Pointer management similar to std::unique_ptr, with some additional methods and type checking...
Definition: HashPtrTable.H:48
bool readIfPresent(const word &optName, T &val) const
Read a value from the named option if present.
Definition: argListI.H:316
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:98
static bool log
Report to stdout which file is included.
Definition: includeEntry.H:114
Foam::argList args(argc, argv)
Regular expression.
Definition: keyType.H:83
bool found(const word &optName) const
Return true if the named option is found.
Definition: argListI.H:171
Namespace for OpenFOAM.
A keyword and a list of tokens is an &#39;entry&#39;.
Definition: entry.H:63
IOerror FatalIOError
Error stream (stdout output on all processes), with additional &#39;FOAM FATAL IO ERROR&#39; header text and ...