Changed the naming convention for the source HTML files to correspond to that generated
[OpenFOAM-1.6.x.git] / src / OpenFOAM / global / argList / argList.C
blobbbb3d0ea8239bf67164dd549a7575a324f14fcd4
1 /*---------------------------------------------------------------------------*\
2   =========                 |
3   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
4    \\    /   O peration     |
5     \\  /    A nd           | Copyright (C) 1991-2009 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 the
13     Free Software Foundation; either version 2 of the License, or (at your
14     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, write to the Free Software Foundation,
23     Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25 \*---------------------------------------------------------------------------*/
27 #include "argList.H"
28 #include "OSspecific.H"
29 #include "clock.H"
30 #include "IFstream.H"
31 #include "dictionary.H"
32 #include "Switch.H"
33 #include "IOobject.H"
34 #include "JobInfo.H"
35 #include "labelList.H"
38 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
40 Foam::SLList<Foam::string>    Foam::argList::validArgs;
41 Foam::HashTable<Foam::string> Foam::argList::validOptions;
42 Foam::HashTable<Foam::string> Foam::argList::validParOptions;
43 bool Foam::argList::bannerEnabled(true);
46 Foam::argList::initValidTables::initValidTables()
48     validOptions.set("case", "dir");
49     validOptions.set("parallel", "");
50     validParOptions.set("parallel", "");
52     Pstream::addValidParOptions(validParOptions);
56 Foam::argList::initValidTables dummyInitValidTables;
59 // convert argv -> args_
60 // transform sequences with "(" ... ")" into string lists in the process
61 bool Foam::argList::regroupArgv(int& argc, char**& argv)
63     int nArgs = 0;
64     int listDepth = 0;
65     string tmpString;
67     // note: we also re-write directly into args_
68     // and use a second pass to sort out args/options
69     for (int argI = 0; argI < argc; argI++)
70     {
71         if (strcmp(argv[argI], "(") == 0)
72         {
73             listDepth++;
74             tmpString += "(";
75         }
76         else if (strcmp(argv[argI], ")") == 0)
77         {
78             if (listDepth)
79             {
80                 listDepth--;
81                 tmpString += ")";
82                 if (listDepth == 0)
83                 {
84                     args_[nArgs++] = tmpString;
85                     tmpString.clear();
86                 }
87             }
88             else
89             {
90                 args_[nArgs++] = argv[argI];
91             }
92         }
93         else if (listDepth)
94         {
95             // quote each string element
96             tmpString += "\"";
97             tmpString += argv[argI];
98             tmpString += "\"";
99         }
100         else
101         {
102             args_[nArgs++] = argv[argI];
103         }
104     }
106     if (tmpString.size())
107     {
108         args_[nArgs++] = tmpString;
109     }
111     args_.setSize(nArgs);
113     return nArgs < argc;
117 // get rootPath_ / globalCase_ from one of the following forms
118 //   * [-case dir]
119 //   * cwd
121 // Also export FOAM_CASE and FOAM_CASENAME environment variables
122 // so they can be used immediately (eg, in decomposeParDict)
124 void Foam::argList::getRootCase()
126     fileName casePath;
128     // [-case dir] specified
129     HashTable<string>::iterator iter = options_.find("case");
131     if (iter != options_.end())
132     {
133         casePath = iter();
134         casePath.clean();
136         if (casePath.empty() || casePath == ".")
137         {
138             // handle degenerate form and '-case .' like no -case specified
139             casePath = cwd();
140             options_.erase("case");
141         }
142         else if (casePath[0] != '/' && casePath.name() == "..")
143         {
144             // avoid relative cases ending in '..' - makes for very ugly names
145             casePath = cwd()/casePath;
146             casePath.clean();
147         }
148     }
149     else
150     {
151         // nothing specified, use the current dir
152         casePath = cwd();
153     }
155     rootPath_   = casePath.path();
156     globalCase_ = casePath.name();
157     case_       = globalCase_;
160     // Set the case and case-name as an environment variable
161     if (rootPath_[0] == '/')
162     {
163         // absolute path - use as-is
164         setEnv("FOAM_CASE", rootPath_/globalCase_, true);
165         setEnv("FOAM_CASENAME", globalCase_, true);
166     }
167     else
168     {
169         // qualify relative path
170         fileName casePath = cwd()/rootPath_/globalCase_;
171         casePath.clean();
173         setEnv("FOAM_CASE", casePath, true);
174         setEnv("FOAM_CASENAME", casePath.name(), true);
175     }
181 Foam::stringList::subList Foam::argList::additionalArgs() const
183     return stringList::subList(args_, args_.size() - 1, 1);
187 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
189 Foam::argList::argList
191     int& argc,
192     char**& argv,
193     bool checkArgs,
194     bool checkOpts
197     args_(argc),
198     options_(argc)
200     // Check if this run is a parallel run by searching for any parallel option
201     // If found call runPar (might filter argv)
202     for (int argI = 0; argI < argc; argI++)
203     {
204         if (argv[argI][0] == '-')
205         {
206             const char *optionName = &argv[argI][1];
208             if (validParOptions.found(optionName))
209             {
210                 parRunControl_.runPar(argc, argv);
211                 break;
212             }
213         }
214     }
216     // convert argv -> args_ and capture ( ... ) lists
217     // for normal arguments and for options
218     regroupArgv(argc, argv);
220     // Get executable name
221     args_[0]    = fileName(argv[0]);
222     executable_ = fileName(argv[0]).name();
224     // Check arguments and options, we already have argv[0]
225     int nArgs = 1;
226     string argListString = args_[0];
228     for (int argI = 1; argI < args_.size(); argI++)
229     {
230         argListString += ' ';
231         argListString += args_[argI];
233         if (args_[argI][0] == '-')
234         {
235             const char *optionName = &args_[argI][1];
237             if
238             (
239                 (
240                     validOptions.found(optionName)
241                  && validOptions[optionName] != ""
242                 )
243              || (
244                     validParOptions.found(optionName)
245                  && validParOptions[optionName] != ""
246                 )
247             )
248             {
249                 argI++;
250                 if (argI >= args_.size())
251                 {
252                     FatalError
253                         << "option " << "'-" << optionName << '\''
254                         << " requires an argument"
255                         << exit(FatalError);
256                 }
258                 argListString += ' ';
259                 argListString += args_[argI];
260                 options_.insert(optionName, args_[argI]);
261             }
262             else
263             {
264                 options_.insert(optionName, "");
265             }
266         }
267         else
268         {
269             if (nArgs != argI)
270             {
271                 args_[nArgs] = args_[argI];
272             }
273             nArgs++;
274         }
275     }
277     args_.setSize(nArgs);
279     // Help/documentation options:
280     //   -help    print the usage
281     //   -doc     display application documentation in browser
282     //   -srcDoc  display source code in browser
283     if
284     (
285         options_.found("help")
286      || options_.found("doc")
287      || options_.found("srcDoc")
288     )
289     {
290         if (options_.found("help"))
291         {
292             printUsage();
293         }
295         // only display one or the other
296         if (options_.found("srcDoc"))
297         {
298             displayDoc(true);
299         }
300         else if (options_.found("doc"))
301         {
302             displayDoc(false);
303         }
305         ::exit(0);
306     }
308     // Print the usage message and exit if the number of arguments is incorrect
309     if (!check(checkArgs, checkOpts))
310     {
311         FatalError.exit();
312     }
315     string dateString = clock::date();
316     string timeString = clock::clockTime();
318     // Print the banner once only for parallel runs
319     if (Pstream::master() && bannerEnabled)
320     {
321         IOobject::writeBanner(Info, true)
322             << "Build  : " << Foam::FOAMbuild << nl
323             << "Exec   : " << argListString.c_str() << nl
324             << "Date   : " << dateString.c_str() << nl
325             << "Time   : " << timeString.c_str() << nl
326             << "Host   : " << hostName() << nl
327             << "PID    : " << pid() << endl;
328     }
330     jobInfo.add("startDate", dateString);
331     jobInfo.add("startTime", timeString);
332     jobInfo.add("userName", userName());
333     jobInfo.add("foamVersion", word(FOAMversion));
334     jobInfo.add("foamBuild", Foam::FOAMbuild);
335     jobInfo.add("code", executable_);
336     jobInfo.add("argList", argListString);
337     jobInfo.add("currentDir", cwd());
338     jobInfo.add("PPID", ppid());
339     jobInfo.add("PGID", pgid());
342     // Case is a single processor run unless it is running parallel
343     int nProcs = 1;
345     // If this actually is a parallel run
346     if (parRunControl_.parRun())
347     {
348         // For the master
349         if (Pstream::master())
350         {
351             // establish rootPath_/globalCase_/case_ for master
352             getRootCase();
354             IFstream decompDictStream
355             (
356                 rootPath_/globalCase_/"system/decomposeParDict"
357             );
359             if (!decompDictStream.good())
360             {
361                 FatalError
362                     << "Cannot read "
363                     << decompDictStream.name()
364                     << exit(FatalError);
365             }
367             dictionary decompDict(decompDictStream);
369             label dictNProcs
370             (
371                 readLabel
372                 (
373                     decompDict.lookup("numberOfSubdomains")
374                 )
375             );
377             // Check number of processors.
378             // nProcs     => number of actual procs
379             // dictNProcs => number of procs specified in decompositionDict
380             // nProcDirs  => number of processor directories
381             //               (n/a when running distributed)
382             //
383             // - normal running : nProcs = dictNProcs = nProcDirs
384             // - decomposition to more  processors : nProcs = dictNProcs
385             // - decomposition to fewer processors : nProcs = nProcDirs
386             if (dictNProcs > Pstream::nProcs())
387             {
388                 FatalError
389                     << decompDictStream.name()
390                     << " specifies " << dictNProcs
391                     << " processors but job was started with "
392                     << Pstream::nProcs() << " processors."
393                     << exit(FatalError);
394             }
396             // distributed data
397             if (decompDict.lookupOrDefault<Switch>("distributed", false))
398             {
399                 fileNameList roots;
400                 decompDict.lookup("roots") >> roots;
402                 if (roots.size() != Pstream::nProcs()-1)
403                 {
404                     FatalError
405                         << "number of entries in decompositionDict::roots"
406                         << " is not equal to the number of slaves "
407                         << Pstream::nProcs()-1
408                         << exit(FatalError);
409                 }
411                 // Distribute the master's argument list (with new root)
412                 bool hadCaseOpt = options_.found("case");
413                 for
414                 (
415                     int slave=Pstream::firstSlave();
416                     slave<=Pstream::lastSlave();
417                     slave++
418                 )
419                 {
420                     options_.set
421                     (
422                         "case",
423                         fileName(roots[slave-1])/globalCase_
424                     );
426                     OPstream toSlave(Pstream::scheduled, slave);
427                     toSlave << args_ << options_;
428                 }
429                 options_.erase("case");
431                 // restore [-case dir]
432                 if (hadCaseOpt)
433                 {
434                     options_.set("case", rootPath_/globalCase_);
435                 }
436             }
437             else
438             {
439                 // Possibly going to fewer processors.
440                 // Check if all procDirs are there.
441                 if (dictNProcs < Pstream::nProcs())
442                 {
443                     label nProcDirs = 0;
444                     while
445                     (
446                         isDir
447                         (
448                             rootPath_/globalCase_/"processor"
449                           + name(++nProcDirs)
450                         )
451                     )
452                     {}
454                     if (nProcDirs != Pstream::nProcs())
455                     {
456                         FatalError
457                             << "number of processor directories = "
458                             << nProcDirs
459                             << " is not equal to the number of processors = "
460                             << Pstream::nProcs()
461                             << exit(FatalError);
462                     }
463                 }
465                 // Distribute the master's argument list (unaltered)
466                 for
467                 (
468                     int slave=Pstream::firstSlave();
469                     slave<=Pstream::lastSlave();
470                     slave++
471                 )
472                 {
473                     OPstream toSlave(Pstream::scheduled, slave);
474                     toSlave << args_ << options_;
475                 }
476             }
477         }
478         else
479         {
480             // Collect the master's argument list
481             IPstream fromMaster(Pstream::scheduled, Pstream::masterNo());
482             fromMaster >> args_ >> options_;
484             // establish rootPath_/globalCase_/case_ for slave
485             getRootCase();
486         }
488         nProcs = Pstream::nProcs();
489         case_ = globalCase_/(word("processor") + name(Pstream::myProcNo()));
490     }
491     else
492     {
493         // establish rootPath_/globalCase_/case_
494         getRootCase();
495         case_ = globalCase_;
496     }
499     wordList slaveProcs;
501     // collect slave machine/pid
502     if (parRunControl_.parRun())
503     {
504         if (Pstream::master())
505         {
506             slaveProcs.setSize(Pstream::nProcs() - 1);
507             word  slaveMachine;
508             label slavePid;
510             label procI = 0;
511             for
512             (
513                 int slave=Pstream::firstSlave();
514                 slave<=Pstream::lastSlave();
515                 slave++
516             )
517             {
518                 IPstream fromSlave(Pstream::scheduled, slave);
519                 fromSlave >> slaveMachine >> slavePid;
521                 slaveProcs[procI++] = slaveMachine + "." + name(slavePid);
522             }
523         }
524         else
525         {
526             OPstream toMaster(Pstream::scheduled, Pstream::masterNo());
527             toMaster << hostName() << pid();
528         }
529     }
532     if (Pstream::master() && bannerEnabled)
533     {
534         Info<< "Case   : " << (rootPath_/globalCase_).c_str() << nl
535             << "nProcs : " << nProcs << endl;
537         if (parRunControl_.parRun())
538         {
539             Info<< "Slaves : " << slaveProcs << nl
540                 << "Pstream initialized with:" << nl
541                 << "    floatTransfer     : " << Pstream::floatTransfer << nl
542                 << "    nProcsSimpleSum   : " << Pstream::nProcsSimpleSum << nl
543                 << "    commsType         : "
544                 << Pstream::commsTypeNames[Pstream::defaultCommsType]
545                 << endl;
546         }
547     }
549     jobInfo.add("root", rootPath_);
550     jobInfo.add("case", globalCase_);
551     jobInfo.add("nProcs", nProcs);
552     if (slaveProcs.size())
553     {
554         jobInfo.add("slaves", slaveProcs);
555     }
556     jobInfo.write();
558     // Switch on signal trapping. We have to wait until after Pstream::init
559     // since this sets up its own ones.
560     sigFpe_.set(bannerEnabled);
561     sigInt_.set(bannerEnabled);
562     sigQuit_.set(bannerEnabled);
563     sigSegv_.set(bannerEnabled);
565     if (Pstream::master() && bannerEnabled)
566     {
567         Info<< endl;
568         IOobject::writeDivider(Info);
569     }
573 // * * * * * * * * * * * * * * * * Destructors * * * * * * * * * * * * * * * //
575 Foam::argList::~argList()
577     jobInfo.end();
581 // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
583 void Foam::argList::noBanner()
585     bannerEnabled = false;
589 void Foam::argList::noParallel()
591     validOptions.erase("parallel");
595 void Foam::argList::printUsage() const
597     Info<< nl
598         << "Usage: " << executable_;
600     for
601     (
602         SLList<string>::iterator iter = validArgs.begin();
603         iter != validArgs.end();
604         ++iter
605     )
606     {
607         Info<< " <" << iter().c_str() << '>';
608     }
610     for
611     (
612         HashTable<string>::iterator iter = validOptions.begin();
613         iter != validOptions.end();
614         ++iter
615     )
616     {
617         Info<< " [-" << iter.key();
619         if (iter().size())
620         {
621             Info<< ' ' << iter().c_str();
622         }
624         Info<< ']';
625     }
627     // place help/doc/srcDoc options of the way at the end,
628     // but with an extra space to separate it a little
629     Info<< "  [-help] [-doc] [-srcDoc]\n" << endl;
633 void Foam::argList::displayDoc(bool source) const
635     const dictionary& docDict = debug::controlDict().subDict("Documentation");
636     List<fileName> docDirs(docDict.lookup("doxyDocDirs"));
637     List<fileName> docExts(docDict.lookup("doxySourceFileExts"));
639     // for source code: change foo_8C.html to foo_8C_source.html
640     if (source)
641     {
642         forAll(docExts, extI)
643         {
644             docExts[extI].replace(".", "_source.");
645         }
646     }
648     fileName docFile;
649     bool found = false;
651     forAll(docDirs, dirI)
652     {
653         forAll(docExts, extI)
654         {
655             docFile = docDirs[dirI]/executable_ + docExts[extI];
656             docFile.expand();
658             if (isFile(docFile))
659             {
660                 found = true;
661                 break;
662             }
663         }
664         if (found)
665         {
666             break;
667         }
668     }
670     if (found)
671     {
672         string docBrowser(docDict.lookup("docBrowser"));
673         docBrowser.replaceAll("%f", docFile);
675         Info<< "Show documentation: " << docBrowser.c_str() << endl;
677         system(docBrowser);
678     }
679     else
680     {
681         Info<< nl
682             << "No documentation found for " << executable_
683             << ", but you can use -help to display the usage\n" << endl;
684     }
688 bool Foam::argList::check(bool checkArgs, bool checkOpts) const
690     bool ok = true;
692     if (Pstream::master())
693     {
694         if (checkArgs && args_.size() - 1 != validArgs.size())
695         {
696             FatalError
697                 << "Wrong number of arguments, expected " << validArgs.size()
698                 << " found " << args_.size() - 1 << endl;
699             ok = false;
700         }
702         if (checkOpts)
703         {
704             forAllConstIter(HashTable<string>, options_, iter)
705             {
706                 if
707                 (
708                     !validOptions.found(iter.key())
709                  && !validParOptions.found(iter.key())
710                 )
711                 {
712                     FatalError
713                         << "Invalid option: -" << iter.key() << endl;
714                     ok = false;
715                 }
716             }
717         }
719         if (!ok)
720         {
721             printUsage();
722         }
723     }
725     return ok;
729 bool Foam::argList::checkRootCase() const
731     if (!isDir(rootPath()))
732     {
733         FatalError
734             << executable_
735             << ": cannot open root directory " << rootPath()
736             << endl;
738         return false;
739     }
741     if (!isDir(path()) && Pstream::master())
742     {
743         // Allow slaves on non-existing processor directories, created later
744         FatalError
745             << executable_
746             << ": cannot open case directory " << path()
747             << endl;
749         return false;
750     }
752     return true;
756 // ************************************************************************* //