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-2022 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 older ':' scope syntax to newer '.' scope syntax,
144 // but leave anything with '/' delimiters untouched
145 bool upgradeScope(word& entryName)
146 {
147  if (!entryName.contains('/') && entryName.contains(':'))
148  {
149  const wordList names(fileName(entryName).components(':'));
150 
151  entryName.resize(0);
152 
153  for (const word& name : names)
154  {
155  if (entryName.size()) entryName.append(".");
156 
157  entryName.append(name);
158  }
159 
160  return true;
161  }
162 
163  // Nothing changed
164  return false;
165 }
166 
167 
168 //- Split into dictionary name and the entry name
169 class dictAndKeyword
170 {
171  word dict_;
172  word key_;
173 
174 public:
175 
176  dictAndKeyword(const word& scopedName)
177  {
178  auto i = scopedName.rfind('/');
179  if (i == string::npos)
180  {
181  i = scopedName.rfind('.');
182  }
183 
184  if (i != string::npos)
185  {
186  dict_ = scopedName.substr(0, i);
187  key_ = scopedName.substr(i+1);
188  }
189  else
190  {
191  key_ = scopedName;
192  }
193  }
194 
195  inline const word& dict() const
196  {
197  return dict_;
198  }
199 
200  inline const word& key() const
201  {
202  return key_;
203  }
204 };
205 
206 
207 const dictionary& lookupScopedDict
208 (
209  const dictionary& dict,
210  const word& subDictName
211 )
212 {
213  if (subDictName.empty())
214  {
215  return dict;
216  }
217 
218  const entry* eptr = dict.findScoped(subDictName, keyType::LITERAL);
219 
220  if (!eptr || !eptr->isDict())
221  {
223  << "'" << subDictName << "' not found in dictionary "
224  << dict.name() << " or is not a dictionary" << nl
225  << "Known entries are " << dict.keys()
226  << exit(FatalIOError);
227  }
228 
229  return eptr->dict();
230 }
231 
232 
233 void removeDict(dictionary& dict, const dictionary& dictToRemove)
234 {
235  for (const entry& refEntry : dictToRemove)
236  {
237  auto finder = dict.search(refEntry.keyword(), keyType::LITERAL);
238 
239  bool purge = false;
240 
241  if (finder.isDict())
242  {
243  if (refEntry.isDict())
244  {
245  removeDict(finder.dict(), refEntry.dict());
246 
247  // Purge if dictionary is empty
248  purge = finder.dict().empty();
249  }
250  }
251  else if (finder.found() && !refEntry.isDict())
252  {
253  // Purge if entries match
254  purge = (finder.ref() == refEntry);
255  }
256 
257  if (purge)
258  {
259  dict.remove(refEntry.keyword());
260  }
261  }
262 }
263 
264 
265 int main(int argc, char *argv[])
266 {
268  (
269  "Interrogate and manipulate dictionaries"
270  );
271 
274  argList::addArgument("dict", "The dictionary file to process");
275  argList::addBoolOption("keywords", "List keywords");
276  argList::addOption("entry", "name", "Report/select the named entry");
278  (
279  "value",
280  "Print entry value"
281  );
283  (
284  "set",
285  "value",
286  "Set entry value or add new entry"
287  );
289  (
290  "add",
291  "value",
292  "Add a new entry"
293  );
295  (
296  "remove",
297  "Remove the entry"
298  );
300  (
301  "diff",
302  "dict",
303  "Write differences with respect to the specified dictionary"
304  );
306  (
307  "diff-etc",
308  "dict",
309  "As per -diff, but locate the file as per foamEtcFile"
310  );
311  argList::addOptionCompat("diff-etc", {"diffEtc", 1712});
313  (
314  "precision",
315  "int",
316  "Set default write precision for IOstreams"
317  );
318 
320  (
321  "includes",
322  "List the #include/#sinclude files to standard output"
323  );
325  (
326  "expand",
327  "Read the specified dictionary file, expand the macros etc. and write "
328  "the resulting dictionary to standard output"
329  );
331  (
332  "disableFunctionEntries",
333  "Disable expansion of dictionary directives - #include, #codeStream etc"
334  );
335  profiling::disable(); // Disable profiling (and its output)
336 
337  argList args(argc, argv);
338 
339  const bool listIncludes = args.found("includes");
340 
341  if (listIncludes)
342  {
344  }
345 
346  const bool disableEntries = args.found("disableFunctionEntries");
347  if (disableEntries)
348  {
349  // Report on stderr (once) to avoid polluting the output
350  if (Pstream::master())
351  {
352  Serr<< "Not expanding variables or dictionary directives" << endl;
353  }
355  }
356 
357  // Set the default output precision
358  {
359  const unsigned prec = args.getOrDefault<unsigned>("precision", 0u);
360  if (prec)
361  {
362  // if (Pstream::master())
363  // {
364  // Serr<< "Output write precision set to " << prec << endl;
365  // }
366 
368  Sout.precision(prec);
369  }
370  }
371 
372  const auto dictFileName = args.get<fileName>(1);
373 
374  autoPtr<IFstream> dictFile(new IFstream(dictFileName));
375  if (!dictFile().good())
376  {
378  << "Cannot open file " << dictFileName
379  << exit(FatalError, 1);
380  }
381 
382 
383  bool changed = false;
384 
385  // Read but preserve headers
386  dictionary dict(dictFile(), true);
387 
388  if (listIncludes)
389  {
390  return 0;
391  }
392  else if (args.found("expand"))
393  {
395  <<"//\n// " << dictFileName << "\n//\n";
396  dict.write(Info, false);
398 
399  return 0;
400  }
401 
402 
403  // Has "diff" or "diff-etc"
404  bool optDiff = false;
405 
406  // Reference dictionary for -diff / -diff-etc
407  dictionary diffDict;
408  {
409  fileName diffFileName;
410  if (args.readIfPresent("diff", diffFileName))
411  {
412  IFstream diffFile(diffFileName);
413  if (!diffFile.good())
414  {
416  << "Cannot open file " << diffFileName
417  << exit(FatalError, 1);
418  }
419 
420  // Read but preserve headers
421  diffDict.read(diffFile, true);
422  optDiff = true;
423  }
424  else if (args.readIfPresent("diff-etc", diffFileName))
425  {
426  fileName foundName = findEtcFile(diffFileName);
427  if (foundName.empty())
428  {
430  << "Cannot find etcFile " << diffFileName
431  << exit(FatalError, 1);
432  }
433 
434  IFstream diffFile(foundName);
435  if (!diffFile.good())
436  {
438  << "Cannot open file " << foundName
439  << exit(FatalError, 1);
440  }
441 
442  // Read but preserve headers
443  diffDict.read(diffFile, true);
444  optDiff = true;
445  }
446  }
447 
448  word scopedName; // Actually fileName, since it can contain '/' scoping
449  if (args.readIfPresent("entry", scopedName))
450  {
451  upgradeScope(scopedName);
452 
453  string newValue;
454  if
455  (
456  args.readIfPresent("set", newValue)
457  || args.readIfPresent("add", newValue)
458  )
459  {
460  const bool overwrite = args.found("set");
461 
462  // Dictionary name and keyword
463  const dictAndKeyword dAk(scopedName);
464 
465  // The context for the action
466  const dictionary& d(lookupScopedDict(dict, dAk.dict()));
467 
468  // Create a new entry
469  IStringStream str(string(dAk.key()) + ' ' + newValue + ';');
470  entry* ePtr(entry::New(str).ptr());
471 
472  if (overwrite)
473  {
474  const_cast<dictionary&>(d).set(ePtr);
475  }
476  else
477  {
478  const_cast<dictionary&>(d).add(ePtr, false);
479  }
480  changed = true;
481 
482  // Print the changed entry
483  const auto finder = dict.csearchScoped(scopedName, keyType::REGEX);
484 
485  if (finder.found())
486  {
487  Info<< finder.ref();
488  }
489  }
490  else if (args.found("remove"))
491  {
492  // Dictionary name and keyword
493  const dictAndKeyword dAk(scopedName);
494 
495  // The context for the action
496  const dictionary& d(lookupScopedDict(dict, dAk.dict()));
497 
498  const_cast<dictionary&>(d).remove(dAk.key());
499  changed = true;
500  }
501  else
502  {
503  // Optionally remove a second dictionary
504  if (optDiff)
505  {
506  // Dictionary name and keyword
507  const dictAndKeyword dAk(scopedName);
508 
509  const dictionary& d1(lookupScopedDict(dict, dAk.dict()));
510  const dictionary& d2(lookupScopedDict(diffDict, dAk.dict()));
511 
512  const entry* e1Ptr = d1.findEntry(dAk.key(), keyType::REGEX);
513  const entry* e2Ptr = d2.findEntry(dAk.key(), keyType::REGEX);
514 
515  if (e1Ptr && e2Ptr)
516  {
517  if (*e1Ptr == *e2Ptr)
518  {
519  const_cast<dictionary&>(d1).remove(dAk.key());
520  }
521  else if (e1Ptr->isDict() && e2Ptr->isDict())
522  {
523  removeDict
524  (
525  const_cast<dictionary&>(e1Ptr->dict()),
526  e2Ptr->dict()
527  );
528  }
529  }
530  }
531 
532  const auto finder = dict.csearchScoped(scopedName, keyType::REGEX);
533 
534  if (!finder.found())
535  {
536  FatalIOErrorInFunction(dictFile())
537  << "Cannot find entry " << scopedName
538  << exit(FatalIOError, 2);
539  }
540  else if (args.found("keywords"))
541  {
542  for (const entry& e : finder.dict())
543  {
544  Info<< e.keyword() << endl;
545  }
546  }
547  else if (args.found("value"))
548  {
549  if (finder.isDict())
550  {
551  Info<< finder.dict();
552  }
553  else if (finder.ref().isStream())
554  {
555  const tokenList& tokens = finder.ref().stream();
556  forAll(tokens, i)
557  {
558  Info<< tokens[i];
559  if (i < tokens.size() - 1)
560  {
561  Info<< token::SPACE;
562  }
563  }
564  Info<< endl;
565  }
566  }
567  else
568  {
569  Info<< finder.ref();
570  }
571  }
572  }
573  else if (args.found("keywords"))
574  {
575  for (const entry& e : dict)
576  {
577  Info<< e.keyword() << endl;
578  }
579  }
580  else if (optDiff)
581  {
582  removeDict(dict, diffDict);
583  dict.write(Info, false);
584  }
585  else
586  {
587  dict.write(Info, false);
588  }
589 
590  if (changed)
591  {
592  dictFile.clear();
593  OFstream os(dictFileName);
596  dict.write(os, false);
598  }
599 
600  return 0;
601 }
602 
603 
604 // ************************************************************************* //
static void noJobInfo()
Suppress JobInfo, overriding controlDict setting.
Definition: argList.C:534
dict_reference dict() const
Reference the found entry as a dictionary.
Definition: dictionary.H:269
dictionary dict
static void addNote(const string &note)
Add extra notes for the usage information.
Definition: argList.C:453
List< word > names(const UPtrList< T > &list, const UnaryMatchPredicate &matcher)
List of names generated by calling name() for each list item and filtered for matches.
A class for handling file names.
Definition: fileName.H:71
bool contains(char c) const noexcept
True if string contains given character (cf. C++23)
Definition: string.H:390
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:125
virtual int precision() const
Get precision of output field.
Definition: OSstream.C:305
bool read(Istream &is)
Read dictionary from Istream. Discards the header.
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:120
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:578
Output to file stream, using an OSstream.
Definition: OFstream.H:49
constexpr char nl
The newline &#39;\n&#39; character (0x0a)
Definition: Ostream.H:49
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:487
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:416
static void addBoolOption(const word &optName, const string &usage="", bool advanced=false)
Add a bool option to validOptions with usage information.
Definition: argList.C:365
static void addOptionCompat(const word &optName, std::pair< const char *, int > compat)
Specify an alias for the option name.
Definition: argList.C:409
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
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:110
static void disable()
Disallow profiling by forcing the InfoSwitch off.
Definition: profiling.C:110
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:413
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
word name(const expressions::valueTypeCode typeCode)
A word representation of a valueTypeCode. Empty for INVALID.
Definition: exprTraits.C:52
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:128
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:376
String literal.
Definition: keyType.H:82
static Ostream & writeEndDivider(Ostream &os)
Write the standard end file divider.
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:619
void write(Ostream &os, const bool subDict=true) const
Write dictionary, normally with sub-dictionary formatting.
Definition: dictionaryIO.C:199
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:607
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 bool master(const label communicator=worldComm)
Am I the master rank.
Definition: UPstream.H:672
static void addArgument(const string &argName, const string &usage="")
Append a (mandatory) argument to validArgs.
Definition: argList.C:342
OSstream Serr
OSstream wrapped stderr (std::cerr)
messageStream Info
Information stream (stdout output on master, null elsewhere)
static void noBanner()
Disable emitting the banner information.
Definition: argList.C:482
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 ...