argList.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration | Website: https://openfoam.org
5  \\ / A nd | Copyright (C) 2011-2018 OpenFOAM Foundation
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8 License
9  This file is part of OpenFOAM.
10 
11  OpenFOAM is free software: you can redistribute it and/or modify it
12  under the terms of the GNU General Public License as published by
13  the Free Software Foundation, either version 3 of the License, or
14  (at your option) any later version.
15 
16  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
17  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19  for more details.
20 
21  You should have received a copy of the GNU General Public License
22  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
23 
24 \*---------------------------------------------------------------------------*/
25 
26 #include "argList.H"
27 #include "OSspecific.H"
28 #include "clock.H"
29 #include "IFstream.H"
30 #include "dictionary.H"
31 #include "IOobject.H"
32 #include "jobInfo.H"
33 #include "labelList.H"
34 #include "regIOobject.H"
35 #include "dynamicCode.H"
36 #include "fileOperation.H"
38 #include "stringListOps.H"
39 
40 #include <cctype>
41 
42 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
43 
52 
54 {
56  (
57  "case", "dir",
58  "specify alternate case directory, default is the cwd"
59  );
60  argList::addBoolOption("parallel", "run in parallel");
61  validParOptions.set("parallel", "");
63  (
64  "roots", "(dir1 .. dirN)",
65  "slave root directories for distributed running"
66  );
67  validParOptions.set("roots", "(dir1 .. dirN)");
68 
70  (
71  "hostRoots", "(((host1 dir1) .. (hostN dirN))",
72  "slave root directories (per host) for distributed running"
73  );
74  validParOptions.set("hostRoots", "((host1 dir1) .. (hostN dirN))");
75 
77  (
78  "noFunctionObjects",
79  "do not execute functionObjects"
80  );
81 
83  (
84  "fileHandler",
85  "handler",
86  "override the fileHandler"
87  );
88 
90 }
91 
92 
94 {
95  argList::removeOption("case");
96  argList::removeOption("parallel");
97  argList::removeOption("roots");
98  argList::removeOption("hostRoots");
99  argList::removeOption("noFunctionObjects");
100  argList::removeOption("fileHandler");
101 }
102 
103 
105 
106 
107 // * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
108 
110 (
111  const word& opt,
112  const string& usage
113 )
114 {
115  addOption(opt, "", usage);
116 }
117 
118 
120 (
121  const word& opt,
122  const string& param,
123  const string& usage
124 )
125 {
126  validOptions.set(opt, param);
127  if (!usage.empty())
128  {
129  optionUsage.set(opt, usage);
130  }
131 }
132 
133 
135 (
136  const word& opt,
137  const string& usage
138 )
139 {
140  if (usage.empty())
141  {
142  optionUsage.erase(opt);
143  }
144  else
145  {
146  optionUsage.set(opt, usage);
147  }
148 }
149 
150 
151 void Foam::argList::addNote(const string& note)
152 {
153  if (!note.empty())
154  {
155  notes.append(note);
156  }
157 }
158 
159 
161 {
162  validOptions.erase(opt);
163  optionUsage.erase(opt);
164 }
165 
166 
168 {
169  removeOption("parallel");
170  removeOption("roots");
171  removeOption("hostRoots");
172  validParOptions.clear();
173 }
174 
175 
176 void Foam::argList::printOptionUsage
177 (
178  const label location,
179  const string& str
180 )
181 {
182  const string::size_type textWidth = usageMax - usageMin;
183  const string::size_type strLen = str.size();
184 
185  if (strLen)
186  {
187  // Minimum of 2 spaces between option and usage:
188  if (string::size_type(location) + 2 <= usageMin)
189  {
190  for (string::size_type i = location; i < usageMin; ++i)
191  {
192  Info<<' ';
193  }
194  }
195  else
196  {
197  // or start a new line
198  Info<< nl;
199  for (string::size_type i = 0; i < usageMin; ++i)
200  {
201  Info<<' ';
202  }
203  }
204 
205  // Text wrap
207  while (pos != string::npos && pos + textWidth < strLen)
208  {
209  // Potential end point and next point
210  string::size_type curr = pos + textWidth - 1;
211  string::size_type next = string::npos;
212 
213  if (isspace(str[curr]))
214  {
215  // We were lucky: ended on a space
216  next = str.find_first_not_of(" \t\n", curr);
217  }
218  else if (isspace(str[curr+1]))
219  {
220  // The next one is a space - so we are okay
221  curr++; // otherwise the length is wrong
222  next = str.find_first_not_of(" \t\n", curr);
223  }
224  else
225  {
226  // Search for end of a previous word break
227  string::size_type prev = str.find_last_of(" \t\n", curr);
228 
229  // Reposition to the end of previous word if possible
230  if (prev != string::npos && prev > pos)
231  {
232  curr = prev;
233  }
234  }
235 
236  if (next == string::npos)
237  {
238  next = curr + 1;
239  }
240 
241  // Indent following lines (not the first one)
242  if (pos)
243  {
244  for (string::size_type i = 0; i < usageMin; ++i)
245  {
246  Info<<' ';
247  }
248  }
249 
250  Info<< str.substr(pos, (curr - pos)).c_str() << nl;
251  pos = next;
252  }
253 
254  // Output the remainder of the string
255  if (pos != string::npos)
256  {
257  // Indent following lines (not the first one)
258  if (pos)
259  {
260  for (string::size_type i = 0; i < usageMin; ++i)
261  {
262  Info<<' ';
263  }
264  }
265 
266  Info<< str.substr(pos).c_str() << nl;
267  }
268  }
269  else
270  {
271  Info<< nl;
272  }
273 }
274 
275 
276 bool Foam::argList::postProcess(int argc, char *argv[])
277 {
278  bool postProcessOption = false;
279 
280  for (int i=1; i<argc; i++)
281  {
282  postProcessOption = argv[i] == '-' + postProcessOptionName;
283  if (postProcessOption) break;
284  }
285 
286  return postProcessOption;
287 }
288 
289 
290 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
291 
292 // Convert argv -> args_
293 // Transform sequences with "(" ... ")" into string lists in the process
294 bool Foam::argList::regroupArgv(int& argc, char**& argv)
295 {
296  int nArgs = 0;
297  int listDepth = 0;
298  string tmpString;
299 
300  // Note: we also re-write directly into args_
301  // and use a second pass to sort out args/options
302  for (int argI = 0; argI < argc; ++argI)
303  {
304  if (strcmp(argv[argI], "(") == 0)
305  {
306  ++listDepth;
307  tmpString += "(";
308  }
309  else if (strcmp(argv[argI], ")") == 0)
310  {
311  if (listDepth)
312  {
313  --listDepth;
314  tmpString += ")";
315  if (listDepth == 0)
316  {
317  args_[nArgs++] = tmpString;
318  tmpString.clear();
319  }
320  }
321  else
322  {
323  args_[nArgs++] = argv[argI];
324  }
325  }
326  else if (listDepth)
327  {
328  // Quote each string element
329  tmpString += "\"";
330  tmpString += argv[argI];
331  tmpString += "\"";
332  }
333  else
334  {
335  args_[nArgs++] = argv[argI];
336  }
337  }
338 
339  if (tmpString.size())
340  {
341  args_[nArgs++] = tmpString;
342  }
343 
344  args_.setSize(nArgs);
345 
346  return nArgs < argc;
347 }
348 
349 
350 void Foam::argList::getRootCase()
351 {
352  fileName casePath;
353 
354  // [-case dir] specified
355  HashTable<string>::const_iterator iter = options_.find("case");
356 
357  if (iter != options_.end())
358  {
359  casePath = iter();
360  casePath.clean();
361 
362  if (casePath.empty() || casePath == ".")
363  {
364  // Handle degenerate form and '-case .' like no -case specified
365  casePath = cwd();
366  options_.erase("case");
367  }
368  else if (!casePath.isAbsolute() && casePath.name() == "..")
369  {
370  // Avoid relative cases ending in '..' - makes for very ugly names
371  casePath = cwd()/casePath;
372  casePath.clean();
373  }
374  }
375  else
376  {
377  // Nothing specified, use the current dir
378  casePath = cwd();
379  }
380 
381  rootPath_ = casePath.path();
382  globalCase_ = casePath.name();
383  case_ = globalCase_;
384 
385 
386  // Set the case and case-name as an environment variable
387  if (rootPath_.isAbsolute())
388  {
389  // Absolute path - use as-is
390  setEnv("FOAM_CASE", rootPath_/globalCase_, true);
391  setEnv("FOAM_CASENAME", globalCase_, true);
392  }
393  else
394  {
395  // Qualify relative path
396  casePath = cwd()/rootPath_/globalCase_;
397  casePath.clean();
398 
399  setEnv("FOAM_CASE", casePath, true);
400  setEnv("FOAM_CASENAME", casePath.name(), true);
401  }
402 }
403 
404 
405 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
406 
408 (
409  int& argc,
410  char**& argv,
411  bool checkArgs,
412  bool checkOpts,
413  const bool initialise
414 )
415 :
416  args_(argc),
417  options_(argc)
418 {
419  // Check for fileHandler
420  word handlerType(getEnv("FOAM_FILEHANDLER"));
421  for (int argI = 0; argI < argc; ++argI)
422  {
423  if (argv[argI][0] == '-')
424  {
425  const char *optionName = &argv[argI][1];
426  if (string(optionName) == "fileHandler")
427  {
428  handlerType = argv[argI+1];
429  break;
430  }
431  }
432  }
433  if (handlerType.empty())
434  {
435  handlerType = fileOperation::defaultFileHandler;
436  }
437 
438  // Detect any parallel options
440  (
441  handlerType,
442  argc,
443  argv
444  )().needsThreading();
445 
446 
447  // Check if this run is a parallel run by searching for any parallel option
448  // If found call runPar which might filter argv
449  for (int argI = 0; argI < argc; ++argI)
450  {
451  if (argv[argI][0] == '-')
452  {
453  const char *optionName = &argv[argI][1];
454 
455  if (validParOptions.found(optionName))
456  {
457  parRunControl_.runPar(argc, argv, needsThread);
458  break;
459  }
460  }
461  }
462 
463  // Convert argv -> args_ and capture ( ... ) lists
464  // for normal arguments and for options
465  regroupArgv(argc, argv);
466 
467  // Get executable name
468  args_[0] = fileName(argv[0]);
469  executable_ = fileName(argv[0]).name();
470 
471  // Check arguments and options, we already have argv[0]
472  int nArgs = 1;
473  argListStr_ = args_[0];
474 
475  for (int argI = 1; argI < args_.size(); ++argI)
476  {
477  argListStr_ += ' ';
478  argListStr_ += args_[argI];
479 
480  if (args_[argI][0] == '-')
481  {
482  const char *optionName = &args_[argI][1];
483 
484  if
485  (
486  (
487  validOptions.found(optionName)
488  && !validOptions[optionName].empty()
489  )
490  || (
491  validParOptions.found(optionName)
492  && !validParOptions[optionName].empty()
493  )
494  )
495  {
496  ++argI;
497  if (argI >= args_.size())
498  {
499  FatalError
500  <<"Option '-" << optionName
501  << "' requires an argument" << endl;
502  printUsage();
503  FatalError.exit();
504  }
505 
506  argListStr_ += ' ';
507  argListStr_ += args_[argI];
508  options_.insert(optionName, args_[argI]);
509  }
510  else
511  {
512  options_.insert(optionName, "");
513  }
514  }
515  else
516  {
517  if (nArgs != argI)
518  {
519  args_[nArgs] = args_[argI];
520  }
521  ++nArgs;
522  }
523  }
524 
525  args_.setSize(nArgs);
526 
527  parse(checkArgs, checkOpts, initialise);
528 }
529 
530 
532 (
533  const argList& args,
534  const HashTable<string>& options,
535  bool checkArgs,
536  bool checkOpts,
537  bool initialise
538 )
539 :
540  parRunControl_(args.parRunControl_),
541  args_(args.args_),
542  options_(options),
543  executable_(args.executable_),
544  rootPath_(args.rootPath_),
545  globalCase_(args.globalCase_),
546  case_(args.case_),
547  argListStr_(args.argListStr_)
548 {
549  parse(checkArgs, checkOpts, initialise);
550 }
551 
552 
554 (
555  bool checkArgs,
556  bool checkOpts,
557  bool initialise
558 )
559 {
560  // Help/documentation options:
561  // -help print the usage
562  // -doc display application documentation in browser
563  // -srcDoc display source code in browser
564  if
565  (
566  options_.found("help")
567  || options_.found("doc")
568  || options_.found("srcDoc")
569  )
570  {
571  if (options_.found("help"))
572  {
573  printUsage();
574  }
575 
576  // Only display one or the other
577  if (options_.found("srcDoc"))
578  {
579  displayDoc(true);
580  }
581  else if (options_.found("doc"))
582  {
583  displayDoc(false);
584  }
585 
586  ::exit(0);
587  }
588 
589  // Print the usage message and exit if the number of arguments is incorrect
590  if (!check(checkArgs, checkOpts))
591  {
592  FatalError.exit();
593  }
594 
595 
596  if (initialise)
597  {
598  string dateString = clock::date();
599  string timeString = clock::clockTime();
600 
601  // Print the banner once only for parallel runs
603  {
605  << "Build : " << Foam::FOAMbuild << nl
606  << "Exec : " << argListStr_.c_str() << nl
607  << "Date : " << dateString.c_str() << nl
608  << "Time : " << timeString.c_str() << nl
609  << "Host : " << hostName() << nl
610  << "PID : " << pid() << endl;
611  }
612 
613  jobInfo_.add("startDate", dateString);
614  jobInfo_.add("startTime", timeString);
615  jobInfo_.add("userName", userName());
616  jobInfo_.add("foamVersion", word(FOAMversion));
617  jobInfo_.add("code", executable_);
618  jobInfo_.add("argList", argListStr_);
619  jobInfo_.add("currentDir", cwd());
620  jobInfo_.add("PPID", ppid());
621  jobInfo_.add("PGID", pgid());
622 
623  // Add build information - only use the first word
624  {
625  std::string build(Foam::FOAMbuild);
626  std::string::size_type found = build.find(' ');
627  if (found != std::string::npos)
628  {
629  build.resize(found);
630  }
631  jobInfo_.add("foamBuild", build);
632  }
633  }
634 
635 
636  // Set fileHandler. In increasing order of priority:
637  // 1. default = uncollated
638  // 2. environment var FOAM_FILEHANDLER
639  // 3. etc/controlDict optimisationSwitches 'fileHandler'
640  // 4. system/controlDict 'fileHandler' (not handled here; done in TimeIO.C)
641 
642  {
643  word handlerType(getEnv("FOAM_FILEHANDLER"));
644  HashTable<string>::const_iterator iter = options_.find("fileHandler");
645  if (iter != options_.end())
646  {
647  handlerType = iter();
648  }
649 
650  if (handlerType.empty())
651  {
652  handlerType = fileOperation::defaultFileHandler;
653  }
654 
655  autoPtr<fileOperation> handler
656  (
658  (
659  handlerType,
661  )
662  );
663  Foam::fileHandler(handler);
664  }
665 
666 
667  stringList slaveMachine;
668  stringList slaveProcs;
669 
670  // Collect slave machine/pid
671  if (parRunControl_.parRun())
672  {
673  if (Pstream::master())
674  {
675  slaveMachine.setSize(Pstream::nProcs() - 1);
676  slaveProcs.setSize(Pstream::nProcs() - 1);
677  label proci = 0;
678  for
679  (
680  int slave = Pstream::firstSlave();
681  slave <= Pstream::lastSlave();
682  slave++
683  )
684  {
685  IPstream fromSlave(Pstream::commsTypes::scheduled, slave);
686 
687  string slaveBuild;
688  label slavePid;
689  fromSlave >> slaveBuild >> slaveMachine[proci] >> slavePid;
690 
691  slaveProcs[proci] = slaveMachine[proci]+"."+name(slavePid);
692  proci++;
693 
694  // Check build string to make sure all processors are running
695  // the same build
696  if (slaveBuild != Foam::FOAMbuild)
697  {
699  << "Master is running version " << Foam::FOAMbuild
700  << "; slave " << proci << " is running version "
701  << slaveBuild
702  << exit(FatalError);
703  }
704  }
705  }
706  else
707  {
708  OPstream toMaster
709  (
712  );
713  toMaster << string(Foam::FOAMbuild) << hostName() << pid();
714  }
715  }
716 
717 
718  // Case is a single processor run unless it is running parallel
719  int nProcs = 1;
720 
721  // Roots if running distributed
722  fileNameList roots;
723 
724  // If this actually is a parallel run
725  if (parRunControl_.parRun())
726  {
727  // For the master
728  if (Pstream::master())
729  {
730  // Establish rootPath_/globalCase_/case_ for master
731  getRootCase();
732 
733  // See if running distributed (different roots for different procs)
734  label dictNProcs = -1;
735  fileName source;
736 
737  if (options_.found("roots"))
738  {
739  source = "-roots";
740  IStringStream is(options_["roots"]);
741  roots = readList<fileName>(is);
742 
743  if (roots.size() != 1)
744  {
745  dictNProcs = roots.size()+1;
746  }
747  }
748  else if (options_.found("hostRoots"))
749  {
750  source = "-hostRoots";
751  IStringStream is(options_["hostRoots"]);
752  List<Tuple2<wordRe, fileName>> hostRoots(is);
753 
754  roots.setSize(Pstream::nProcs()-1);
755  forAll(hostRoots, i)
756  {
757  const Tuple2<wordRe, fileName>& hostRoot = hostRoots[i];
758  const wordRe& re = hostRoot.first();
759  labelList matchedRoots(findStrings(re, slaveMachine));
760  forAll(matchedRoots, matchi)
761  {
762  label slavei = matchedRoots[matchi];
763  if (roots[slavei] != wordRe())
764  {
766  << "Slave " << slaveMachine[slavei]
767  << " has multiple matching roots in "
768  << hostRoots << exit(FatalError);
769  }
770  else
771  {
772  roots[slavei] = hostRoot.second();
773  }
774  }
775  }
776 
777  // Check
778  forAll(roots, slavei)
779  {
780  if (roots[slavei] == wordRe())
781  {
783  << "Slave " << slaveMachine[slavei]
784  << " has no matching roots in "
785  << hostRoots << exit(FatalError);
786  }
787  }
788 
789  if (roots.size() != 1)
790  {
791  dictNProcs = roots.size()+1;
792  }
793  }
794  else
795  {
796  source = rootPath_/globalCase_/"system/decomposeParDict";
797  IFstream decompDictStream(source);
798 
799  if (!decompDictStream.good())
800  {
801  FatalError
802  << "Cannot read "
803  << decompDictStream.name()
804  << exit(FatalError);
805  }
806 
807  dictionary decompDict(decompDictStream);
808 
809  dictNProcs = readLabel
810  (
811  decompDict.lookup("numberOfSubdomains")
812  );
813 
814  if (decompDict.lookupOrDefault("distributed", false))
815  {
816  decompDict.lookup("roots") >> roots;
817  }
818  }
819 
820  // Convenience:
821  // when a single root is specified, use it for all processes
822  if (roots.size() == 1)
823  {
824  const fileName rootName(roots[0]);
825  roots.setSize(Pstream::nProcs()-1, rootName);
826 
827  // adjust dictNProcs for command-line '-roots' option
828  if (dictNProcs < 0)
829  {
830  dictNProcs = roots.size()+1;
831  }
832  }
833 
834 
835  // Check number of processors.
836  // nProcs => number of actual procs
837  // dictNProcs => number of procs specified in decompositionDict
838  // nProcDirs => number of processor directories
839  // (n/a when running distributed)
840  //
841  // - normal running : nProcs = dictNProcs = nProcDirs
842  // - decomposition to more processors : nProcs = dictNProcs
843  // - decomposition to fewer processors : nProcs = nProcDirs
844  if (dictNProcs > Pstream::nProcs())
845  {
846  FatalError
847  << source
848  << " specifies " << dictNProcs
849  << " processors but job was started with "
850  << Pstream::nProcs() << " processors."
851  << exit(FatalError);
852  }
853 
854 
855  // Distributed data
856  if (roots.size())
857  {
858  if (roots.size() != Pstream::nProcs()-1)
859  {
860  FatalError
861  << "number of entries in roots "
862  << roots.size()
863  << " is not equal to the number of slaves "
864  << Pstream::nProcs()-1
865  << exit(FatalError);
866  }
867 
868  forAll(roots, i)
869  {
870  roots[i].expand();
871  }
872 
873  // Distribute the master's argument list (with new root)
874  bool hadCaseOpt = options_.found("case");
875  for
876  (
877  int slave = Pstream::firstSlave();
878  slave <= Pstream::lastSlave();
879  slave++
880  )
881  {
882  options_.set("case", roots[slave-1]/globalCase_);
883 
885  toSlave << args_ << options_;
886  }
887  options_.erase("case");
888 
889  // Restore [-case dir]
890  if (hadCaseOpt)
891  {
892  options_.set("case", rootPath_/globalCase_);
893  }
894  }
895  else
896  {
897  // Possibly going to fewer processors.
898  // Check if all procDirs are there.
899  if (dictNProcs < Pstream::nProcs())
900  {
901  label nProcDirs = 0;
902  while
903  (
904  isDir
905  (
906  rootPath_/globalCase_/"processor"
907  + name(++nProcDirs)
908  )
909  )
910  {}
911 
912  if (nProcDirs != Pstream::nProcs())
913  {
914  FatalError
915  << "number of processor directories = "
916  << nProcDirs
917  << " is not equal to the number of processors = "
918  << Pstream::nProcs()
919  << exit(FatalError);
920  }
921  }
922 
923  // Distribute the master's argument list (unaltered)
924  for
925  (
926  int slave = Pstream::firstSlave();
927  slave <= Pstream::lastSlave();
928  slave++
929  )
930  {
932  toSlave << args_ << options_;
933  }
934  }
935  }
936  else
937  {
938  // Collect the master's argument list
939  IPstream fromMaster
940  (
943  );
944  fromMaster >> args_ >> options_;
945 
946  // Establish rootPath_/globalCase_/case_ for slave
947  getRootCase();
948  }
949 
950  nProcs = Pstream::nProcs();
951  case_ = globalCase_/(word("processor") + name(Pstream::myProcNo()));
952  }
953  else
954  {
955  // Establish rootPath_/globalCase_/case_
956  getRootCase();
957  case_ = globalCase_;
958  }
959 
961  {
962  Info<< "Case : " << (rootPath_/globalCase_).c_str() << nl
963  << "nProcs : " << nProcs << endl;
964 
965  if (parRunControl_.parRun())
966  {
967  Info<< "Slaves : " << slaveProcs << nl;
968  if (roots.size())
969  {
970  Info<< "Roots : " << roots << nl;
971  }
972  Info<< "Pstream initialized with:" << nl
973  << " floatTransfer : " << Pstream::floatTransfer << nl
974  << " nProcsSimpleSum : " << Pstream::nProcsSimpleSum << nl
975  << " commsType : "
977  << " polling iterations : " << Pstream::nPollProcInterfaces
978  << endl;
979  }
980  }
981 
982  if (initialise)
983  {
984  jobInfo_.add("root", rootPath_);
985  jobInfo_.add("case", globalCase_);
986  jobInfo_.add("nProcs", nProcs);
987  if (slaveProcs.size())
988  {
989  jobInfo_.add("slaves", slaveProcs);
990  }
991  if (roots.size())
992  {
993  jobInfo_.add("roots", roots);
994  }
995  jobInfo_.write(executable_, rootPath_/globalCase_);
996 
997  // Switch on signal trapping. We have to wait until after Pstream::init
998  // since this sets up its own ones.
999  sigFpe_.set(writeInfoHeader);
1000  sigInt_.set(writeInfoHeader);
1001  sigQuit_.set(writeInfoHeader);
1002  sigSegv_.set(writeInfoHeader);
1003 
1004  if (writeInfoHeader)
1005  {
1006  Info<< "fileModificationChecking : "
1007  << "Monitoring run-time modified files using "
1009  [
1011  ];
1012  if
1013  (
1014  (
1017  )
1018  || (
1021  )
1022  )
1023  {
1024  Info<< " (fileModificationSkew "
1026  }
1027  Info<< endl;
1028 
1029  Info<< "allowSystemOperations : ";
1031  {
1032  Info<< "Allowing user-supplied system call operations" << endl;
1033  }
1034  else
1035  {
1036  Info<< "Disallowing user-supplied system call operations"
1037  << endl;
1038  }
1039  }
1040 
1042  {
1043  Info<< endl;
1045  }
1046  }
1047 }
1048 
1049 
1050 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
1051 
1053 {
1054  jobInfo_.end();
1055 
1056  // Delete file handler to flush any remaining IO
1057  autoPtr<fileOperation> dummy(nullptr);
1058  fileHandler(dummy);
1059 }
1060 
1061 
1062 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
1063 
1064 bool Foam::argList::setOption(const word& opt, const string& param)
1065 {
1066  bool changed = false;
1067 
1068  // Only allow valid options
1069  if (validOptions.found(opt))
1070  {
1071  // Some options are to be protected
1072  if
1073  (
1074  opt == "case"
1075  || opt == "parallel"
1076  || opt == "roots"
1077  || opt == "hostRoots"
1078  )
1079  {
1080  FatalError
1081  <<"used argList::setOption on a protected option: '"
1082  << opt << "'" << endl;
1083  FatalError.exit();
1084  }
1085 
1086  if (validOptions[opt].empty())
1087  {
1088  // Bool option
1089  if (!param.empty())
1090  {
1091  // Disallow change of type
1092  FatalError
1093  <<"used argList::setOption to change bool to non-bool: '"
1094  << opt << "'" << endl;
1095  FatalError.exit();
1096  }
1097  else
1098  {
1099  // Did not previously exist
1100  changed = !options_.found(opt);
1101  }
1102  }
1103  else
1104  {
1105  // Non-bool option
1106  if (param.empty())
1107  {
1108  // Disallow change of type
1109  FatalError
1110  <<"used argList::setOption to change non-bool to bool: '"
1111  << opt << "'" << endl;
1112  FatalError.exit();
1113  }
1114  else
1115  {
1116  // Existing value needs changing, or did not previously exist
1117  changed = options_.found(opt) ? options_[opt] != param : true;
1118  }
1119  }
1120  }
1121  else
1122  {
1123  FatalError
1124  <<"used argList::setOption on an invalid option: '"
1125  << opt << "'" << nl << "allowed are the following:"
1126  << validOptions << endl;
1127  FatalError.exit();
1128  }
1129 
1130  // Set/change the option as required
1131  if (changed)
1132  {
1133  options_.set(opt, param);
1134  }
1135 
1136  return changed;
1137 }
1138 
1139 
1141 {
1142  // Only allow valid options
1143  if (validOptions.found(opt))
1144  {
1145  // Some options are to be protected
1146  if
1147  (
1148  opt == "case"
1149  || opt == "parallel"
1150  || opt == "roots"
1151  || opt == "hostRoots"
1152  )
1153  {
1154  FatalError
1155  <<"used argList::unsetOption on a protected option: '"
1156  << opt << "'" << endl;
1157  FatalError.exit();
1158  }
1159 
1160  // Remove the option, return true if state changed
1161  return options_.erase(opt);
1162  }
1163  else
1164  {
1165  FatalError
1166  <<"used argList::unsetOption on an invalid option: '"
1167  << opt << "'" << nl << "allowed are the following:"
1168  << validOptions << endl;
1169  FatalError.exit();
1170  }
1171 
1172  return false;
1173 }
1174 
1175 
1177 {
1178  // Output notes directly - no automatic text wrapping
1179  if (!notes.empty())
1180  {
1181  Info<< nl;
1183  {
1184  Info<< iter().c_str() << nl;
1185  }
1186  }
1187 }
1188 
1189 
1191 {
1192  Info<< "\nUsage: " << executable_ << " [OPTIONS]";
1193 
1195  {
1196  Info<< " <" << iter().c_str() << '>';
1197  }
1198 
1199  Info<< "\noptions:\n";
1200 
1201  wordList opts = validOptions.sortedToc();
1202  forAll(opts, optI)
1203  {
1204  const word& optionName = opts[optI];
1205 
1206  HashTable<string>::const_iterator iter = validOptions.find(optionName);
1207  Info<< " -" << optionName;
1208  label len = optionName.size() + 3; // Length includes leading ' -'
1209 
1210  if (iter().size())
1211  {
1212  // Length includes space and between option/param and '<>'
1213  len += iter().size() + 3;
1214  Info<< " <" << iter().c_str() << '>';
1215  }
1216 
1218  optionUsage.find(optionName);
1219 
1220  if (usageIter != optionUsage.end())
1221  {
1222  printOptionUsage
1223  (
1224  len,
1225  usageIter()
1226  );
1227  }
1228  else
1229  {
1230  Info<< nl;
1231  }
1232  }
1233 
1234  // Place srcDoc/doc/help options at the end
1235  Info<< " -srcDoc";
1236  printOptionUsage
1237  (
1238  9,
1239  "display source code in browser"
1240  );
1241 
1242  Info<< " -doc";
1243  printOptionUsage
1244  (
1245  6,
1246  "display application documentation in browser"
1247  );
1248 
1249  Info<< " -help";
1250  printOptionUsage
1251  (
1252  7,
1253  "print the usage"
1254  );
1255 
1256 
1257  printNotes();
1258 
1259  Info<< nl
1260  <<"Using: OpenFOAM-" << Foam::FOAMversion
1261  << " (see www.OpenFOAM.org)" << nl
1262  <<"Build: " << Foam::FOAMbuild << nl
1263  << endl;
1264 }
1265 
1266 
1267 void Foam::argList::displayDoc(bool source) const
1268 {
1269  const dictionary& docDict = debug::controlDict().subDict("Documentation");
1270  List<fileName> docDirs(docDict.lookup("doxyDocDirs"));
1271  fileName docExt(docDict.lookup("doxySourceFileExt"));
1272 
1273  // For source code: change foo_8C.html to foo_8C_source.html
1274  if (source)
1275  {
1276  docExt.replace(".", "_source.");
1277  }
1278 
1279  fileName docFile;
1280  fileName httpServer;
1281  bool found = false;
1282 
1283  forAll(docDirs, dirI)
1284  {
1285  // An HTTP server is treated as a special case ...
1286  if (docDirs[dirI].component(0) == "http:")
1287  {
1288  httpServer = docDirs[dirI]/executable_ + docExt;
1289  }
1290  else
1291  {
1292  // ... all other entries are treated as local directories
1293 
1294  // Remove the optional "file://"
1295  if (docDirs[dirI].component(0) == "file:")
1296  {
1297  docDirs[dirI].replace("file://", string::null);
1298  }
1299 
1300 
1301  // Expand the file name
1302  docFile = docDirs[dirI]/executable_ + docExt;
1303  docFile.expand();
1304 
1305  // Check the existence of the file
1306  if (isFile(docFile))
1307  {
1308  found = true;
1309  break;
1310  }
1311  }
1312  }
1313 
1314  if (found || httpServer != fileName::null)
1315  {
1316  string docBrowser = getEnv("FOAM_DOC_BROWSER");
1317  if (docBrowser.empty())
1318  {
1319  docDict.lookup("docBrowser") >> docBrowser;
1320  }
1321 
1322  if (found)
1323  {
1324  docBrowser += " file://" + docFile;
1325  }
1326  else
1327  {
1328  docBrowser += " " + httpServer;
1329  }
1330 
1331  Info<< "Show documentation: " << docBrowser.c_str() << endl;
1332 
1333  system(docBrowser);
1334  }
1335  else
1336  {
1337  Info<< nl
1338  << "No documentation found for " << executable_
1339  << ", but you can use -help to display the usage\n" << endl;
1340  }
1341 }
1342 
1343 
1344 bool Foam::argList::check(bool checkArgs, bool checkOpts) const
1345 {
1346  bool ok = true;
1347 
1348  if (Pstream::master())
1349  {
1350  if (checkArgs && args_.size() - 1 != validArgs.size())
1351  {
1352  FatalError
1353  << "Wrong number of arguments, expected " << validArgs.size()
1354  << " found " << args_.size() - 1 << endl;
1355  ok = false;
1356  }
1357 
1358  if (checkOpts)
1359  {
1360  forAllConstIter(HashTable<string>, options_, iter)
1361  {
1362  if
1363  (
1364  !validOptions.found(iter.key())
1365  && !validParOptions.found(iter.key())
1366  )
1367  {
1368  FatalError
1369  << "Invalid option: -" << iter.key() << endl;
1370  ok = false;
1371  }
1372  }
1373  }
1374 
1375  if (!ok)
1376  {
1377  printUsage();
1378  }
1379  }
1380 
1381  return ok;
1382 }
1383 
1384 
1386 {
1387  if (!fileHandler().isDir(rootPath()))
1388  {
1389  FatalError
1390  << executable_
1391  << ": cannot open root directory " << rootPath()
1392  << endl;
1393 
1394  return false;
1395  }
1396 
1397  fileName pathDir(fileHandler().filePath(path()));
1398 
1399  if (pathDir.empty() && Pstream::master())
1400  {
1401  // Allow slaves on non-existing processor directories, created later
1402  // (e.g. redistributePar)
1403  FatalError
1404  << executable_
1405  << ": cannot open case directory " << path()
1406  << endl;
1407 
1408  return false;
1409  }
1410 
1411  return true;
1412 }
1413 
1414 
1415 // ************************************************************************* //
static bool floatTransfer
Should compact transfer be used in which floats replace doubles.
Definition: UPstream.H:265
string getEnv(const word &)
Return environment variable of given name.
Definition: POSIX.C:96
bool checkRootCase() const
Check root path and case path.
Definition: argList.C:1385
const word & executable() const
Name of executable without the path.
Definition: argListI.H:30
bool setOption(const word &opt, const string &param="")
Set option directly (use with caution)
Definition: argList.C:1064
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:428
static void removeOption(const word &opt)
Remove option from validOptions and from optionUsage.
Definition: argList.C:160
intWM_LABEL_SIZE_t label
A label is an int32_t or int64_t as specified by the pre-processor macro WM_LABEL_SIZE.
Definition: label.H:59
static int masterNo()
Process index of the master.
Definition: UPstream.H:415
A class for handling file names.
Definition: fileName.H:69
static Stream & writeBanner(Stream &os, bool noHint=false)
Write the standard OpenFOAM file/dictionary banner.
Definition: IOobjectI.H:45
An STL-conforming const_iterator.
Definition: HashTable.H:481
static word postProcessOptionName
Standard name for the post-processing option.
Definition: argList.H:174
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:124
bool unsetOption(const word &opt)
Unset option directly (use with caution)
Definition: argList.C:1140
static void addUsage(const word &opt, const string &usage)
Add option usage information to optionUsage.
Definition: argList.C:135
error FatalError
A list of keyword definitions, which are a keyword followed by any number of values (e...
Definition: dictionary.H:137
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:319
A 2-tuple for storing two objects of different types.
Definition: HashTable.H:66
bool writeInfoHeader
bool clean()
Cleanup file name.
Definition: fileName.C:77
argList(int &argc, char **&argv, bool checkArgs=true, bool checkOpts=true, bool initialise=true)
Construct from argc and argv.
Definition: argList.C:408
const fileName & rootPath() const
Return root path.
Definition: argListI.H:36
static int firstSlave()
Process index of first slave.
Definition: UPstream.H:444
const Type1 & first() const
Return first.
Definition: Tuple2.H:94
Template class for non-intrusive linked lists.
Definition: LList.H:51
pid_t ppid()
Return the parent PID of this process.
Definition: POSIX.C:78
static Stream & writeDivider(Stream &os)
Write the standard file section divider.
Definition: IOobjectI.H:93
const dimensionedScalar re
Classical electron radius: default SI units: [m].
void size(const label)
Override size to be inconsistent with allocated storage.
Definition: ListI.H:163
static const fileName null
An empty fileName.
Definition: fileName.H:97
static int myProcNo(const label communicator=0)
Number of this process (starting from masterNo() = 0)
Definition: UPstream.H:427
static int nProcsSimpleSum
Number of processors at which the sum algorithm changes from linear.
Definition: UPstream.H:269
static SLList< string > notes
Additional notes for usage.
Definition: argList.H:165
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:256
static bool master(const label communicator=0)
Am I the master process.
Definition: UPstream.H:421
Foam::argList::initValidTables dummyInitValidTables
Definition: argList.C:104
static void noParallel()
Remove the parallel options.
Definition: argList.C:167
static SLList< string > validArgs
A list of valid (mandatory) arguments.
Definition: argList.H:153
static bool postProcess(int argc, char *argv[])
Return true if the post-processing option is specified.
Definition: argList.C:276
Operations on lists of strings.
const char *const FOAMversion
jobInfo jobInfo_
Definition: jobInfo.C:44
virtual ~argList()
Destructor.
Definition: argList.C:1052
bool good() const
Return true if next operation might succeed.
Definition: IOstream.H:333
void exit(const int errNo=1)
Exit : can be called for any error to exit program.
Definition: error.C:168
bool add(entry *, bool mergeEntry=false)
Add a new entry.
Definition: dictionary.C:814
static autoPtr< fileOperation > New(const word &type, const bool verbose)
Select type.
bool findStrings(const wordReListMatcher &matcher, const std::string &str)
Return true if string matches one of the regular expressions.
Definition: stringListOps.H:52
const dictionary & subDict(const word &) const
Find and return a sub-dictionary.
Definition: dictionary.C:692
void parse(bool checkArgs, bool checkOpts, bool initialise)
Parse.
Definition: argList.C:554
Input inter-processor communications stream.
Definition: IPstream.H:50
static autoPtr< fileOperationInitialise > New(const word &type, int &argc, char **&argv)
Select type.
Functions used by OpenFOAM that are specific to POSIX compliant operating systems and need to be repl...
dimensionedScalar pos(const dimensionedScalar &ds)
void displayDoc(bool source=false) const
Display documentation in browser.
Definition: argList.C:1267
static int nPollProcInterfaces
Number of polling cycles in processor updates.
Definition: UPstream.H:275
string hostName(const bool full=false)
Return the system&#39;s host name, as per hostname(1)
Definition: POSIX.C:124
static HashTable< string > validParOptions
A list of valid parallel options.
Definition: argList.H:159
void printNotes() const
Print notes (if any)
Definition: argList.C:1176
bool isAbsolute() const
Return true if file name is absolute.
Definition: fileName.C:57
bool isDir(const fileName &, const bool followLink=true)
Does the name exist as a DIRECTORY in the file system?
Definition: POSIX.C:528
A class for handling words, derived from string.
Definition: word.H:59
Extract command arguments and options from the supplied argc and argv parameters. ...
Definition: argList.H:102
const fileName & name() const
Return the name of the stream.
Definition: IFstream.H:116
static int allowSystemOperations
Flag if system operations are allowed.
Definition: dynamicCode.H:161
static void addOption(const word &opt, const string &param="", const string &usage="")
Add to an option to validOptions with usage information.
Definition: argList.C:120
word name() const
Return file name (part beyond last /)
Definition: fileName.C:179
string userName()
Return the user&#39;s login name.
Definition: POSIX.C:164
label size() const
Return the number of arguments.
Definition: argListI.H:84
const fileOperation & fileHandler()
Get current file handler.
static HashTable< string > optionUsage
Short usage information for validOptions.
Definition: argList.H:162
graph_traits< Graph >::vertices_size_type size_type
Definition: SloanRenumber.C:73
forAllConstIter(PtrDictionary< phaseModel >, mixture.phases(), phase)
Definition: pEqn.H:29
label readLabel(Istream &is)
Definition: label.H:64
A wordRe is a word, but can also have a regular expression for matching words.
Definition: wordRe.H:74
static string clockTime()
Return the current wall-clock time as a string.
Definition: clock.C:93
static const string null
An empty string.
Definition: string.H:86
pid_t pid()
Return the PID of this process.
Definition: POSIX.C:72
bool isFile(const fileName &, const bool checkGzip=true, const bool followLink=true)
Does the name exist as a FILE in the file system?
Definition: POSIX.C:543
static const char nl
Definition: Ostream.H:265
static fileCheckTypes fileModificationChecking
Type of file modification checking.
Definition: IOobject.H:214
Input from file stream.
Definition: IFstream.H:81
static const NamedEnum< commsTypes, 3 > commsTypeNames
Definition: UPstream.H:71
Output inter-processor communications stream.
Definition: OPstream.H:50
pid_t pgid()
Return the group PID of this process.
Definition: POSIX.C:84
word name(const complex &)
Return a string representation of a complex.
Definition: complex.C:47
fileName path() const
Return the path to the caseName.
Definition: argListI.H:60
static string date()
Return the current wall-clock date as a string.
Definition: clock.C:77
static const NamedEnum< fileCheckTypes, 4 > fileCheckTypesNames
Definition: IOobject.H:131
static commsTypes defaultCommsType
Default commsType.
Definition: UPstream.H:272
void setSize(const label)
Reset size of List.
Definition: List.C:281
string & replace(const string &oldStr, const string &newStr, size_type start=0)
Replace first occurrence of sub-string oldStr with newStr.
Definition: string.C:56
static label nProcs(const label communicator=0)
Number of processes in parallel run.
Definition: UPstream.H:409
bool isspace(char c)
Definition: char.H:53
static word defaultFileHandler
Default fileHandler.
T lookupOrDefault(const word &, const T &, bool recursive=false, bool patternMatch=true) const
Find and return a T,.
string & expand(const bool allowEmpty=false)
Expand initial tildes and all occurrences of environment variables.
Definition: string.C:95
Input from memory buffer stream.
Definition: IStringStream.H:49
static void clear()
Clear the options table.
Definition: argList.C:93
fileName cwd()
Return current working directory path name.
Definition: POSIX.C:240
static float fileModificationSkew
Definition: regIOobject.H:124
messageStream Info
bool check(bool checkArgs=true, bool checkOpts=true) const
Check argument list.
Definition: argList.C:1344
const Type2 & second() const
Return second.
Definition: Tuple2.H:106
fileName path() const
Return directory path name (part before last /)
Definition: fileName.C:249
An auto-pointer similar to the STL auto_ptr but with automatic casting to a reference to the type and...
Definition: PtrList.H:52
static void addBoolOption(const word &opt, const string &usage="")
Add to a bool option to validOptions with usage information.
Definition: argList.C:110
static void addNote(const string &)
Add extra notes for the usage information.
Definition: argList.C:151
static string::size_type usageMin
Min offset for displaying usage (default: 20)
Definition: argList.H:168
const char *const FOAMbuild
dictionary & controlDict()
The central control dictionary.
Definition: debug.C:94
void printUsage() const
Print usage.
Definition: argList.C:1190
A class for handling character strings derived from std::string.
Definition: string.H:74
void component(FieldField< Field, typename FieldField< Field, Type >::cmptType > &sf, const FieldField< Field, Type > &f, const direction d)
#define FatalErrorIn(functionName)
Report an error message using Foam::FatalError.
Definition: error.H:314
static string::size_type usageMax
Max screen width for displaying usage (default: 80)
Definition: argList.H:171
bool found
static HashTable< string > validOptions
A list of valid options.
Definition: argList.H:156
bool setEnv(const word &name, const std::string &value, const bool overwrite)
Set an environment variable.
Definition: POSIX.C:114
static void addValidParOptions(HashTable< string > &validParOptions)
Add the valid option this type of communications library.
Definition: UPstream.C:31
int system(const std::string &command)
Execute the specified command.
Definition: POSIX.C:1217
ITstream & lookup(const word &, bool recursive=false, bool patternMatch=true) const
Find and return an entry data stream.
Definition: dictionary.C:576
static int lastSlave(const label communicator=0)
Process index of last slave.
Definition: UPstream.H:450