Initial commit for version 2.0.x patch release
[OpenFOAM-2.0.x.git] / src / OpenFOAM / global / argList / argList.C
blob75e129e271480fe0b33eda47e26db7af840c890c
1 /*---------------------------------------------------------------------------*\
2   =========                 |
3   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
4    \\    /   O peration     |
5     \\  /    A nd           | Copyright (C) 2004-2011 OpenCFD Ltd.
6      \\/     M anipulation  |
7 -------------------------------------------------------------------------------
8 License
9     This file is part of OpenFOAM.
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.
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.
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/>.
24 \*---------------------------------------------------------------------------*/
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"
37 #include <cctype>
39 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
41 bool Foam::argList::bannerEnabled = true;
42 Foam::SLList<Foam::string>    Foam::argList::validArgs;
43 Foam::HashTable<Foam::string> Foam::argList::validOptions;
44 Foam::HashTable<Foam::string> Foam::argList::validParOptions;
45 Foam::HashTable<Foam::string> Foam::argList::optionUsage;
46 Foam::SLList<Foam::string>    Foam::argList::notes;
47 Foam::string::size_type Foam::argList::usageMin = 20;
48 Foam::string::size_type Foam::argList::usageMax = 80;
51 Foam::argList::initValidTables::initValidTables()
53     argList::addOption
54     (
55         "case", "dir",
56         "specify alternate case directory, default is the cwd"
57     );
58     argList::addBoolOption("parallel", "run in parallel");
59     validParOptions.set("parallel", "");
60     argList::addOption
61     (
62         "roots", "(dir1 .. dirN)",
63         "slave root directories for distributed running"
64     );
65     validParOptions.set("roots", "(dir1 .. dirN)");
67     argList::addBoolOption
68     (
69         "noFunctionObjects",
70         "do not execute functionObjects"
71     );
73     Pstream::addValidParOptions(validParOptions);
77 Foam::argList::initValidTables dummyInitValidTables;
80 // * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
82 void Foam::argList::addBoolOption
84     const word& opt,
85     const string& usage
88     addOption(opt, "", usage);
92 void Foam::argList::addOption
94     const word& opt,
95     const string& param,
96     const string& usage
99     validOptions.set(opt, param);
100     if (!usage.empty())
101     {
102         optionUsage.set(opt, usage);
103     }
107 void Foam::argList::addUsage
109     const word& opt,
110     const string& usage
113     if (usage.empty())
114     {
115         optionUsage.erase(opt);
116     }
117     else
118     {
119         optionUsage.set(opt, usage);
120     }
124 void Foam::argList::addNote(const string& note)
126     if (!note.empty())
127     {
128         notes.append(note);
129     }
133 void Foam::argList::removeOption(const word& opt)
135     validOptions.erase(opt);
136     optionUsage.erase(opt);
140 void Foam::argList::noBanner()
142     bannerEnabled = false;
146 void Foam::argList::noParallel()
148     removeOption("parallel");
149     removeOption("roots");
150     validParOptions.clear();
154 void Foam::argList::printOptionUsage
156     const label location,
157     const string& str
160     const string::size_type textWidth = usageMax - usageMin;
161     const string::size_type strLen = str.size();
163     if (strLen)
164     {
165         // minimum of 2 spaces between option and usage:
166         if (string::size_type(location) + 2 <= usageMin)
167         {
168             for (string::size_type i = location; i < usageMin; ++i)
169             {
170                 Info<<' ';
171             }
172         }
173         else
174         {
175             // or start a new line
176             Info<< nl;
177             for (string::size_type i = 0; i < usageMin; ++i)
178             {
179                 Info<<' ';
180             }
181         }
183         // text wrap
184         string::size_type pos = 0;
185         while (pos != string::npos && pos + textWidth < strLen)
186         {
187             // potential end point and next point
188             string::size_type curr = pos + textWidth - 1;
189             string::size_type next = string::npos;
191             if (isspace(str[curr]))
192             {
193                 // we were lucky: ended on a space
194                 next = str.find_first_not_of(" \t\n", curr);
195             }
196             else if (isspace(str[curr+1]))
197             {
198                 // the next one is a space - so we are okay
199                 curr++;  // otherwise the length is wrong
200                 next = str.find_first_not_of(" \t\n", curr);
201             }
202             else
203             {
204                 // search for end of a previous word break
205                 string::size_type prev = str.find_last_of(" \t\n", curr);
207                 // reposition to the end of previous word if possible
208                 if (prev != string::npos && prev > pos)
209                 {
210                     curr = prev;
211                 }
212             }
214             if (next == string::npos)
215             {
216                 next = curr + 1;
217             }
219             // indent following lines (not the first one)
220             if (pos)
221             {
222                 for (string::size_type i = 0; i < usageMin; ++i)
223                 {
224                     Info<<' ';
225                 }
226             }
228             Info<< str.substr(pos, (curr - pos)).c_str() << nl;
229             pos = next;
230         }
232         // output the remainder of the string
233         if (pos != string::npos)
234         {
235             // indent following lines (not the first one)
236             if (pos)
237             {
238                 for (string::size_type i = 0; i < usageMin; ++i)
239                 {
240                     Info<<' ';
241                 }
242             }
244             Info<< str.substr(pos).c_str() << nl;
245         }
246     }
247     else
248     {
249         Info<< nl;
250     }
254 // * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
256 // convert argv -> args_
257 // transform sequences with "(" ... ")" into string lists in the process
258 bool Foam::argList::regroupArgv(int& argc, char**& argv)
260     int nArgs = 0;
261     int listDepth = 0;
262     string tmpString;
264     // note: we also re-write directly into args_
265     // and use a second pass to sort out args/options
266     for (int argI = 0; argI < argc; ++argI)
267     {
268         if (strcmp(argv[argI], "(") == 0)
269         {
270             ++listDepth;
271             tmpString += "(";
272         }
273         else if (strcmp(argv[argI], ")") == 0)
274         {
275             if (listDepth)
276             {
277                 --listDepth;
278                 tmpString += ")";
279                 if (listDepth == 0)
280                 {
281                     args_[nArgs++] = tmpString;
282                     tmpString.clear();
283                 }
284             }
285             else
286             {
287                 args_[nArgs++] = argv[argI];
288             }
289         }
290         else if (listDepth)
291         {
292             // quote each string element
293             tmpString += "\"";
294             tmpString += argv[argI];
295             tmpString += "\"";
296         }
297         else
298         {
299             args_[nArgs++] = argv[argI];
300         }
301     }
303     if (tmpString.size())
304     {
305         args_[nArgs++] = tmpString;
306     }
308     args_.setSize(nArgs);
310     return nArgs < argc;
314 void Foam::argList::getRootCase()
316     fileName casePath;
318     // [-case dir] specified
319     HashTable<string>::const_iterator iter = options_.find("case");
321     if (iter != options_.end())
322     {
323         casePath = iter();
324         casePath.clean();
326         if (casePath.empty() || casePath == ".")
327         {
328             // handle degenerate form and '-case .' like no -case specified
329             casePath = cwd();
330             options_.erase("case");
331         }
332         else if (!casePath.isAbsolute() && casePath.name() == "..")
333         {
334             // avoid relative cases ending in '..' - makes for very ugly names
335             casePath = cwd()/casePath;
336             casePath.clean();
337         }
338     }
339     else
340     {
341         // nothing specified, use the current dir
342         casePath = cwd();
343     }
345     rootPath_   = casePath.path();
346     globalCase_ = casePath.name();
347     case_       = globalCase_;
350     // Set the case and case-name as an environment variable
351     if (rootPath_.isAbsolute())
352     {
353         // absolute path - use as-is
354         setEnv("FOAM_CASE", rootPath_/globalCase_, true);
355         setEnv("FOAM_CASENAME", globalCase_, true);
356     }
357     else
358     {
359         // qualify relative path
360         casePath = cwd()/rootPath_/globalCase_;
361         casePath.clean();
363         setEnv("FOAM_CASE", casePath, true);
364         setEnv("FOAM_CASENAME", casePath.name(), true);
365     }
369 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
371 Foam::argList::argList
373     int& argc,
374     char**& argv,
375     bool checkArgs,
376     bool checkOpts
379     args_(argc),
380     options_(argc)
382     // Check if this run is a parallel run by searching for any parallel option
383     // If found call runPar which might filter argv
384     for (int argI = 0; argI < argc; ++argI)
385     {
386         if (argv[argI][0] == '-')
387         {
388             const char *optionName = &argv[argI][1];
390             if (validParOptions.found(optionName))
391             {
392                 parRunControl_.runPar(argc, argv);
393                 break;
394             }
395         }
396     }
398     // convert argv -> args_ and capture ( ... ) lists
399     // for normal arguments and for options
400     regroupArgv(argc, argv);
402     // Get executable name
403     args_[0]    = fileName(argv[0]);
404     executable_ = fileName(argv[0]).name();
406     // Check arguments and options, we already have argv[0]
407     int nArgs = 1;
408     string argListString = args_[0];
410     for (int argI = 1; argI < args_.size(); ++argI)
411     {
412         argListString += ' ';
413         argListString += args_[argI];
415         if (args_[argI][0] == '-')
416         {
417             const char *optionName = &args_[argI][1];
419             if
420             (
421                 (
422                     validOptions.found(optionName)
423                  && !validOptions[optionName].empty()
424                 )
425              || (
426                     validParOptions.found(optionName)
427                  && !validParOptions[optionName].empty()
428                 )
429             )
430             {
431                 ++argI;
432                 if (argI >= args_.size())
433                 {
434                     FatalError
435                         <<"Option '-" << optionName
436                         << "' requires an argument" << endl;
437                     printUsage();
438                     FatalError.exit();
439                 }
441                 argListString += ' ';
442                 argListString += args_[argI];
443                 options_.insert(optionName, args_[argI]);
444             }
445             else
446             {
447                 options_.insert(optionName, "");
448             }
449         }
450         else
451         {
452             if (nArgs != argI)
453             {
454                 args_[nArgs] = args_[argI];
455             }
456             ++nArgs;
457         }
458     }
460     args_.setSize(nArgs);
462     // Help/documentation options:
463     //   -help    print the usage
464     //   -doc     display application documentation in browser
465     //   -srcDoc  display source code in browser
466     if
467     (
468         options_.found("help")
469      || options_.found("doc")
470      || options_.found("srcDoc")
471     )
472     {
473         if (options_.found("help"))
474         {
475             printUsage();
476         }
478         // only display one or the other
479         if (options_.found("srcDoc"))
480         {
481             displayDoc(true);
482         }
483         else if (options_.found("doc"))
484         {
485             displayDoc(false);
486         }
488         ::exit(0);
489     }
491     // Print the usage message and exit if the number of arguments is incorrect
492     if (!check(checkArgs, checkOpts))
493     {
494         FatalError.exit();
495     }
498     string dateString = clock::date();
499     string timeString = clock::clockTime();
501     // Print the banner once only for parallel runs
502     if (Pstream::master() && bannerEnabled)
503     {
504         IOobject::writeBanner(Info, true)
505             << "Build  : " << Foam::FOAMbuild << nl
506             << "Exec   : " << argListString.c_str() << nl
507             << "Date   : " << dateString.c_str() << nl
508             << "Time   : " << timeString.c_str() << nl
509             << "Host   : " << hostName() << nl
510             << "PID    : " << pid() << endl;
511     }
513     jobInfo.add("startDate", dateString);
514     jobInfo.add("startTime", timeString);
515     jobInfo.add("userName", userName());
516     jobInfo.add("foamVersion", word(FOAMversion));
517     jobInfo.add("code", executable_);
518     jobInfo.add("argList", argListString);
519     jobInfo.add("currentDir", cwd());
520     jobInfo.add("PPID", ppid());
521     jobInfo.add("PGID", pgid());
523     // add build information - only use the first word
524     {
525         std::string build(Foam::FOAMbuild);
526         std::string::size_type found = build.find(' ');
527         if (found != std::string::npos)
528         {
529             build.resize(found);
530         }
531         jobInfo.add("foamBuild", build);
532     }
535     // Case is a single processor run unless it is running parallel
536     int nProcs = 1;
538     // Roots if running distributed
539     fileNameList roots;
542     // If this actually is a parallel run
543     if (parRunControl_.parRun())
544     {
545         // For the master
546         if (Pstream::master())
547         {
548             // establish rootPath_/globalCase_/case_ for master
549             getRootCase();
551             // See if running distributed (different roots for different procs)
552             label dictNProcs = -1;
553             fileName source;
555             if (options_.found("roots"))
556             {
557                 source = "-roots";
558                 IStringStream is(options_["roots"]);
559                 roots = readList<fileName>(is);
561                 if (roots.size() != 1)
562                 {
563                     dictNProcs = roots.size()+1;
564                 }
565             }
566             else
567             {
568                 source = rootPath_/globalCase_/"system/decomposeParDict";
569                 IFstream decompDictStream(source);
571                 if (!decompDictStream.good())
572                 {
573                     FatalError
574                         << "Cannot read "
575                         << decompDictStream.name()
576                         << exit(FatalError);
577                 }
579                 dictionary decompDict(decompDictStream);
581                 dictNProcs = readLabel
582                 (
583                     decompDict.lookup("numberOfSubdomains")
584                 );
586                 if (decompDict.lookupOrDefault("distributed", false))
587                 {
588                     decompDict.lookup("roots") >> roots;
589                 }
590             }
592             // convenience:
593             // when a single root is specified, use it for all processes
594             if (roots.size() == 1)
595             {
596                 const fileName rootName(roots[0]);
597                 roots.setSize(Pstream::nProcs()-1, rootName);
599                 // adjust dictNProcs for command-line '-roots' option
600                 if (dictNProcs < 0)
601                 {
602                     dictNProcs = roots.size()+1;
603                 }
604             }
607             // Check number of processors.
608             // nProcs     => number of actual procs
609             // dictNProcs => number of procs specified in decompositionDict
610             // nProcDirs  => number of processor directories
611             //               (n/a when running distributed)
612             //
613             // - normal running : nProcs = dictNProcs = nProcDirs
614             // - decomposition to more  processors : nProcs = dictNProcs
615             // - decomposition to fewer processors : nProcs = nProcDirs
616             if (dictNProcs > Pstream::nProcs())
617             {
618                 FatalError
619                     << source
620                     << " specifies " << dictNProcs
621                     << " processors but job was started with "
622                     << Pstream::nProcs() << " processors."
623                     << exit(FatalError);
624             }
627             // distributed data
628             if (roots.size())
629             {
630                 if (roots.size() != Pstream::nProcs()-1)
631                 {
632                     FatalError
633                         << "number of entries in roots "
634                         << roots.size()
635                         << " is not equal to the number of slaves "
636                         << Pstream::nProcs()-1
637                         << exit(FatalError);
638                 }
640                 forAll(roots, i)
641                 {
642                     roots[i].expand();
643                 }
645                 // Distribute the master's argument list (with new root)
646                 bool hadCaseOpt = options_.found("case");
647                 for
648                 (
649                     int slave = Pstream::firstSlave();
650                     slave <= Pstream::lastSlave();
651                     slave++
652                 )
653                 {
654                     options_.set("case", roots[slave-1]/globalCase_);
656                     OPstream toSlave(Pstream::scheduled, slave);
657                     toSlave << args_ << options_;
658                 }
659                 options_.erase("case");
661                 // restore [-case dir]
662                 if (hadCaseOpt)
663                 {
664                     options_.set("case", rootPath_/globalCase_);
665                 }
666             }
667             else
668             {
669                 // Possibly going to fewer processors.
670                 // Check if all procDirs are there.
671                 if (dictNProcs < Pstream::nProcs())
672                 {
673                     label nProcDirs = 0;
674                     while
675                     (
676                         isDir
677                         (
678                             rootPath_/globalCase_/"processor"
679                           + name(++nProcDirs)
680                         )
681                     )
682                     {}
684                     if (nProcDirs != Pstream::nProcs())
685                     {
686                         FatalError
687                             << "number of processor directories = "
688                             << nProcDirs
689                             << " is not equal to the number of processors = "
690                             << Pstream::nProcs()
691                             << exit(FatalError);
692                     }
693                 }
695                 // Distribute the master's argument list (unaltered)
696                 for
697                 (
698                     int slave = Pstream::firstSlave();
699                     slave <= Pstream::lastSlave();
700                     slave++
701                 )
702                 {
703                     OPstream toSlave(Pstream::scheduled, slave);
704                     toSlave << args_ << options_;
705                 }
706             }
707         }
708         else
709         {
710             // Collect the master's argument list
711             IPstream fromMaster(Pstream::scheduled, Pstream::masterNo());
712             fromMaster >> args_ >> options_;
714             // establish rootPath_/globalCase_/case_ for slave
715             getRootCase();
716         }
718         nProcs = Pstream::nProcs();
719         case_ = globalCase_/(word("processor") + name(Pstream::myProcNo()));
720     }
721     else
722     {
723         // establish rootPath_/globalCase_/case_
724         getRootCase();
725         case_ = globalCase_;
726     }
729     wordList slaveProcs;
731     // collect slave machine/pid
732     if (parRunControl_.parRun())
733     {
734         if (Pstream::master())
735         {
736             slaveProcs.setSize(Pstream::nProcs() - 1);
737             word  slaveMachine;
738             label slavePid;
740             label procI = 0;
741             for
742             (
743                 int slave = Pstream::firstSlave();
744                 slave <= Pstream::lastSlave();
745                 slave++
746             )
747             {
748                 IPstream fromSlave(Pstream::scheduled, slave);
749                 fromSlave >> slaveMachine >> slavePid;
751                 slaveProcs[procI++] = slaveMachine + "." + name(slavePid);
752             }
753         }
754         else
755         {
756             OPstream toMaster(Pstream::scheduled, Pstream::masterNo());
757             toMaster << hostName() << pid();
758         }
759     }
762     if (Pstream::master() && bannerEnabled)
763     {
764         Info<< "Case   : " << (rootPath_/globalCase_).c_str() << nl
765             << "nProcs : " << nProcs << endl;
767         if (parRunControl_.parRun())
768         {
769             Info<< "Slaves : " << slaveProcs << nl;
770             if (roots.size())
771             {
772                 Info<< "Roots  : " << roots << nl;
773             }
774             Info<< "Pstream initialized with:" << nl
775                 << "    floatTransfer     : " << Pstream::floatTransfer << nl
776                 << "    nProcsSimpleSum   : " << Pstream::nProcsSimpleSum << nl
777                 << "    commsType         : "
778                 << Pstream::commsTypeNames[Pstream::defaultCommsType]
779                 << endl;
780         }
781     }
783     jobInfo.add("root", rootPath_);
784     jobInfo.add("case", globalCase_);
785     jobInfo.add("nProcs", nProcs);
786     if (slaveProcs.size())
787     {
788         jobInfo.add("slaves", slaveProcs);
789     }
790     if (roots.size())
791     {
792         jobInfo.add("roots", roots);
793     }
794     jobInfo.write();
796     // Switch on signal trapping. We have to wait until after Pstream::init
797     // since this sets up its own ones.
798     sigFpe_.set(bannerEnabled);
799     sigInt_.set(bannerEnabled);
800     sigQuit_.set(bannerEnabled);
801     sigSegv_.set(bannerEnabled);
803     if (bannerEnabled)
804     {
805         Info<< "fileModificationChecking : "
806             << "Monitoring run-time modified files using "
807             << regIOobject::fileCheckTypesNames
808                 [
809                     regIOobject::fileModificationChecking
810                 ]
811             << endl;
813         Info<< "allowSystemOperations : ";
814         if (dynamicCode::allowSystemOperations)
815         {
816             Info<< "Allowing user-supplied system call operations" << endl;
817         }
818         else
819         {
820             Info<< "Disallowing user-supplied system call operations" << endl;
821         }
822     }
824     if (Pstream::master() && bannerEnabled)
825     {
826         Info<< endl;
827         IOobject::writeDivider(Info);
828     }
832 // * * * * * * * * * * * * * * * * Destructors * * * * * * * * * * * * * * * //
834 Foam::argList::~argList()
836     jobInfo.end();
840 // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
842 bool Foam::argList::setOption(const word& opt, const string& param)
844     bool changed = false;
846     // only allow valid options
847     if (validOptions.found(opt))
848     {
849         // some options are to be protected
850         if
851         (
852             opt == "case"
853          || opt == "parallel"
854          || opt == "roots"
855         )
856         {
857             FatalError
858                 <<"used argList::setOption on a protected option: '"
859                 << opt << "'" << endl;
860             FatalError.exit();
861         }
863         if (validOptions[opt].empty())
864         {
865             // bool option
866             if (!param.empty())
867             {
868                 // disallow change of type
869                 FatalError
870                     <<"used argList::setOption to change bool to non-bool: '"
871                     << opt << "'" << endl;
872                 FatalError.exit();
873             }
874             else
875             {
876                 // did not previously exist
877                 changed = !options_.found(opt);
878             }
879         }
880         else
881         {
882             // non-bool option
883             if (param.empty())
884             {
885                 // disallow change of type
886                 FatalError
887                     <<"used argList::setOption to change non-bool to bool: '"
888                     << opt << "'" << endl;
889                 FatalError.exit();
890             }
891             else
892             {
893                 // existing value needs changing, or did not previously exist
894                 changed = options_.found(opt) ? options_[opt] != param : true;
895             }
896         }
897     }
898     else
899     {
900         FatalError
901             <<"used argList::setOption on an invalid option: '"
902             << opt << "'" << nl << "allowed are the following:"
903             << validOptions << endl;
904         FatalError.exit();
905     }
907     // set/change the option as required
908     if (changed)
909     {
910         options_.set(opt, param);
911     }
913     return changed;
917 bool Foam::argList::unsetOption(const word& opt)
919     // only allow valid options
920     if (validOptions.found(opt))
921     {
922         // some options are to be protected
923         if
924         (
925             opt == "case"
926          || opt == "parallel"
927          || opt == "roots"
928         )
929         {
930             FatalError
931                 <<"used argList::unsetOption on a protected option: '"
932                 << opt << "'" << endl;
933             FatalError.exit();
934         }
936         // remove the option, return true if state changed
937         return options_.erase(opt);
938     }
939     else
940     {
941         FatalError
942             <<"used argList::unsetOption on an invalid option: '"
943             << opt << "'" << nl << "allowed are the following:"
944             << validOptions << endl;
945         FatalError.exit();
946     }
948     return false;
952 void Foam::argList::printNotes() const
954     // output notes directly - no automatic text wrapping
955     if (!notes.empty())
956     {
957         Info<< nl;
958         forAllConstIter(SLList<string>, notes, iter)
959         {
960             Info<< iter().c_str() << nl;
961         }
962     }
966 void Foam::argList::printUsage() const
968     Info<< "\nUsage: " << executable_ << " [OPTIONS]";
970     forAllConstIter(SLList<string>, validArgs, iter)
971     {
972         Info<< " <" << iter().c_str() << '>';
973     }
975     Info<< "\noptions:\n";
977     wordList opts = validOptions.sortedToc();
978     forAll(opts, optI)
979     {
980         const word& optionName = opts[optI];
982         HashTable<string>::const_iterator iter = validOptions.find(optionName);
983         Info<< "  -" << optionName;
984         label len = optionName.size() + 3;  // length includes leading '  -'
986         if (iter().size())
987         {
988             // length includes space and between option/param and '<>'
989             len += iter().size() + 3;
990             Info<< " <" << iter().c_str() << '>';
991         }
993         HashTable<string>::const_iterator usageIter =
994             optionUsage.find(optionName);
996         if (usageIter != optionUsage.end())
997         {
998             printOptionUsage
999             (
1000                 len,
1001                 usageIter()
1002             );
1003         }
1004         else
1005         {
1006             Info<< nl;
1007         }
1008     }
1010     //
1011     // place srcDoc/doc/help options at the end
1012     //
1013     Info<< "  -srcDoc";
1014     printOptionUsage
1015     (
1016         9,
1017         "display source code in browser"
1018     );
1020     Info<< "  -doc";
1021     printOptionUsage
1022     (
1023         6,
1024         "display application documentation in browser"
1025     );
1027     Info<< "  -help";
1028     printOptionUsage
1029     (
1030         7,
1031         "print the usage"
1032     );
1035     printNotes();
1037     Info<< nl
1038         <<"Using: OpenFOAM-" << Foam::FOAMversion
1039         << " (see www.OpenFOAM.com)" << nl
1040         <<"Build: " << Foam::FOAMbuild << nl
1041         << endl;
1045 void Foam::argList::displayDoc(bool source) const
1047     const dictionary& docDict = debug::controlDict().subDict("Documentation");
1048     List<fileName> docDirs(docDict.lookup("doxyDocDirs"));
1049     List<fileName> docExts(docDict.lookup("doxySourceFileExts"));
1051     // for source code: change foo_8C.html to foo_8C_source.html
1052     if (source)
1053     {
1054         forAll(docExts, extI)
1055         {
1056             docExts[extI].replace(".", "_source.");
1057         }
1058     }
1060     fileName docFile;
1061     bool found = false;
1063     forAll(docDirs, dirI)
1064     {
1065         forAll(docExts, extI)
1066         {
1067             docFile = docDirs[dirI]/executable_ + docExts[extI];
1068             docFile.expand();
1070             if (isFile(docFile))
1071             {
1072                 found = true;
1073                 break;
1074             }
1075         }
1076         if (found)
1077         {
1078             break;
1079         }
1080     }
1082     if (found)
1083     {
1084         string docBrowser = getEnv("FOAM_DOC_BROWSER");
1085         if (docBrowser.empty())
1086         {
1087             docDict.lookup("docBrowser") >> docBrowser;
1088         }
1089         // can use FOAM_DOC_BROWSER='application file://%f' if required
1090         docBrowser.replaceAll("%f", docFile);
1092         Info<< "Show documentation: " << docBrowser.c_str() << endl;
1094         system(docBrowser);
1095     }
1096     else
1097     {
1098         Info<< nl
1099             << "No documentation found for " << executable_
1100             << ", but you can use -help to display the usage\n" << endl;
1101     }
1105 bool Foam::argList::check(bool checkArgs, bool checkOpts) const
1107     bool ok = true;
1109     if (Pstream::master())
1110     {
1111         if (checkArgs && args_.size() - 1 != validArgs.size())
1112         {
1113             FatalError
1114                 << "Wrong number of arguments, expected " << validArgs.size()
1115                 << " found " << args_.size() - 1 << endl;
1116             ok = false;
1117         }
1119         if (checkOpts)
1120         {
1121             forAllConstIter(HashTable<string>, options_, iter)
1122             {
1123                 if
1124                 (
1125                     !validOptions.found(iter.key())
1126                  && !validParOptions.found(iter.key())
1127                 )
1128                 {
1129                     FatalError
1130                         << "Invalid option: -" << iter.key() << endl;
1131                     ok = false;
1132                 }
1133             }
1134         }
1136         if (!ok)
1137         {
1138             printUsage();
1139         }
1140     }
1142     return ok;
1146 bool Foam::argList::checkRootCase() const
1148     if (!isDir(rootPath()))
1149     {
1150         FatalError
1151             << executable_
1152             << ": cannot open root directory " << rootPath()
1153             << endl;
1155         return false;
1156     }
1158     if (!isDir(path()) && Pstream::master())
1159     {
1160         // Allow slaves on non-existing processor directories, created later
1161         FatalError
1162             << executable_
1163             << ": cannot open case directory " << path()
1164             << endl;
1166         return false;
1167     }
1169     return true;
1173 // ************************************************************************* //