/*---------------------------------------------------------------------------*\ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | Copyright (C) 2011-2017 OpenFOAM Foundation \\/ M anipulation | Copyright (C) 2015-2018 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. OpenFOAM is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. OpenFOAM is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenFOAM. If not, see . \*---------------------------------------------------------------------------*/ #include "argList.H" #include "OSspecific.H" #include "clock.H" #include "IFstream.H" #include "dictionary.H" #include "IOobject.H" #include "JobInfo.H" #include "labelList.H" #include "regIOobject.H" #include "dynamicCode.H" #include "sigFpe.H" #include "sigInt.H" #include "sigQuit.H" #include "sigSegv.H" #include "foamVersion.H" #include "stringOps.H" #include "CStringList.H" #include "stringListOps.H" #include "fileOperation.H" #include "fileOperationInitialise.H" #include // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // bool Foam::argList::argsMandatory_ = true; bool Foam::argList::checkProcessorDirectories_ = true; Foam::SLList Foam::argList::validArgs; Foam::HashSet Foam::argList::advancedOptions; Foam::HashTable Foam::argList::validOptions; Foam::HashTable Foam::argList::validParOptions; Foam::HashTable Foam::argList::optionUsage; Foam::HashTable> Foam::argList::validOptionsCompat; Foam::HashTable> Foam::argList::ignoreOptionsCompat; Foam::SLList Foam::argList::notes; Foam::word Foam::argList::postProcessOptionName("postProcess"); std::string::size_type Foam::argList::usageMin = 20; std::string::size_type Foam::argList::usageMax = 80; Foam::argList::initValidTables::initValidTables() { argList::addOption ( "case", "dir", "Specify case directory to use (instead of the cwd)" ); argList::addBoolOption("parallel", "Run in parallel"); validParOptions.set("parallel", ""); argList::addOption ( "roots", "(dir1 .. dirN)", "Slave root directories for distributed running", true // advanced option ); validParOptions.set("roots", "(dir1 .. dirN)"); argList::addOption ( "decomposeParDict", "file", "Use specified file for decomposePar dictionary" ); argList::addOption ( "hostRoots", "(((host1 dir1) .. (hostN dirN))", "slave root directories (per host) for distributed running. " "The host specification can use a regex.", true // advanced option ); validParOptions.set("hostRoots", "((host1 dir1) .. (hostN dirN))"); argList::addBoolOption ( "noFunctionObjects", "Do not execute function objects" ); argList::addOption ( "fileHandler", "handler", "Override the file handler type", true // advanced option ); // Some standard option aliases (with or without version warnings) // argList::addOptionCompat // ( // "noFunctionObjects", {"no-function-objects", 0} // ); Pstream::addValidParOptions(validParOptions); } Foam::argList::initValidTables dummyInitValidTables; // * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * // namespace { // Should issue warning if there is +ve versioning (+ve version number) // and the this version number is older than the current OpenFOAM version // as conveyed by the OPENFOAM compiler define. static inline constexpr bool shouldWarnVersion(const int version) { return (version > 0 && version < OPENFOAM); } } // End anonymous namespace namespace Foam { // Counted per machine name // Does not include any sorting since we wish to know the ordering according to // mpi rank. // // Always include the master too. // This provides a better overview of the subscription static void printHostsSubscription(const UList& slaveProcs) { Info<< "Hosts :" << nl << "(" << nl; std::string prev = hostName(); int count = 1; for (const auto& str : slaveProcs) { std::string curr(str.substr(0, str.rfind('.'))); if (prev != curr) { if (count) { // Finish previous Info<<" (" << prev.c_str() << " " << count << ")" << nl; count = 0; } prev = std::move(curr); } ++count; } if (count) { // Finished last one Info<<" (" << prev.c_str() << " " << count << ")" << nl; } Info<< ")" << nl; } } // End namespace Foam // * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * // void Foam::argList::checkITstream(const ITstream& is, const label index) { const label remaining = is.nRemainingTokens(); if (remaining) { std::cerr << nl << "--> FOAM WARNING:" << nl << "Argument " << index << " has " << remaining << " excess tokens" << nl << nl; } else if (!is.size()) { std::cerr << nl << "--> FOAM WARNING:" << nl << "Argument " << index << " had no tokens" << nl << nl; } } void Foam::argList::checkITstream(const ITstream& is, const word& optName) { const label remaining = is.nRemainingTokens(); if (remaining) { std::cerr << nl << "--> FOAM WARNING:" << nl << "Option -" << optName << " has " << remaining << " excess tokens" << nl << nl; } else if (!is.size()) { std::cerr << nl << "--> FOAM WARNING:" << nl << "Option -" << optName << " had no tokens" << nl << nl; } } void Foam::argList::addArgument(const string& argName) { validArgs.append(argName); } void Foam::argList::addBoolOption ( const word& optName, const string& usage, bool advanced ) { addOption(optName, "", usage, advanced); } void Foam::argList::addOption ( const word& optName, const string& param, const string& usage, bool advanced ) { validOptions.set(optName, param); if (!usage.empty()) { optionUsage.set(optName, usage); } if (advanced) { advancedOptions.set(optName); } } void Foam::argList::setAdvanced(const word& optName, bool advanced) { if (advanced && validOptions.found(optName)) { advancedOptions.set(optName); } else { advancedOptions.erase(optName); } } void Foam::argList::addOptionCompat ( const word& optName, std::pair compat ) { validOptionsCompat.insert ( compat.first, std::pair(optName, compat.second) ); } void Foam::argList::ignoreOptionCompat ( std::pair compat, bool expectArg ) { ignoreOptionsCompat.insert ( compat.first, std::pair(expectArg, compat.second) ); } void Foam::argList::addUsage ( const word& optName, const string& usage ) { if (usage.empty()) { optionUsage.erase(optName); } else { optionUsage.set(optName, usage); } } void Foam::argList::addNote(const string& note) { if (!note.empty()) { notes.append(note); } } void Foam::argList::removeOption(const word& optName) { validOptions.erase(optName); optionUsage.erase(optName); advancedOptions.erase(optName); } void Foam::argList::nonMandatoryArgs() { argsMandatory_ = false; } void Foam::argList::noBanner() { ::Foam::infoDetailLevel = 0; } bool Foam::argList::bannerEnabled() { return (::Foam::infoDetailLevel > 0); } void Foam::argList::noFunctionObjects(bool addWithOption) { removeOption("noFunctionObjects"); // Ignore this bool option without warning // - cannot tie to any particular version anyhow ignoreOptionCompat({"noFunctionObjects", 0}, false); if (addWithOption) { addBoolOption ( "withFunctionObjects", "Execute functionObjects" ); } } void Foam::argList::noJobInfo() { JobInfo::writeJobInfo = false; } void Foam::argList::noLibs() { addBoolOption ( "no-libs", "Disable use of the controlDict libs entry", true // advanced ); } void Foam::argList::noParallel() { removeOption("parallel"); removeOption("roots"); removeOption("decomposeParDict"); removeOption("hostRoots"); validParOptions.clear(); } void Foam::argList::noCheckProcessorDirectories() { checkProcessorDirectories_ = false; } bool Foam::argList::postProcess(int argc, char *argv[]) { for (int i=1; i FOAM IOWarning :" << nl << " Found [v" << iter.second << "] '" << optName << "' instead of '-" << iter.first << "' option" << nl << std::endl; error::warnAboutAge("option", iter.second); } return "-" + iter.first; } } // Nothing found - pass through the original input return optName; } int Foam::argList::optionIgnore(const word& optName) { // NB: optName is without the leading '-' if (!ignoreOptionsCompat.empty()) { const auto fnd = ignoreOptionsCompat.cfind(optName); if (fnd.found()) { const auto& iter = *fnd; // Number to skip (including the option itself) // '-option ARG' or '-option' const int nskip = (iter.first ? 2 : 1); if (shouldWarnVersion(iter.second)) { std::cerr << "--> FOAM IOWarning :" << nl << " Ignoring [v" << iter.second << "] '-" << optName << (nskip > 1 ? " ARG" : "") << "' option" << nl << std::endl; error::warnAboutAge("option", iter.second); } return nskip; } } return 0; // Do not skip } bool Foam::argList::regroupArgv(int& argc, char**& argv) { int nArgs = 1; int ignore = 0; unsigned depth = 0; string group; // For grouping ( ... ) arguments // Note: we rewrite directly into args_ // and use a second pass to sort out args/options args_[0] = fileName(argv[0]); for (int argi = 1; argi < argc; ++argi) { if (strcmp(argv[argi], "(") == 0) { ++depth; group += '('; } else if (strcmp(argv[argi], ")") == 0) { if (depth) { --depth; group += ')'; if (!depth) { args_[nArgs++] = group; group.clear(); } } else { args_[nArgs++] = argv[argi]; } } else if (depth) { // Quote each string element group += '"'; group += argv[argi]; group += '"'; } else if (argv[argi][0] == '-') { // Appears to be an option const char *optName = &argv[argi][1]; if (validOptions.found(optName)) { // Known option name args_[nArgs++] = argv[argi]; } else if ((ignore = optionIgnore(optName)) > 0) { // Option to be ignored (with/without an argument) if (ignore > 1) { ++argi; } } else { // Try alias for the option name args_[nArgs++] = optionCompat(argv[argi]); } } else { args_[nArgs++] = argv[argi]; } } if (group.size()) { // Group(s) not closed, but flush anything still pending args_[nArgs++] = group; } args_.resize(nArgs); std::string::size_type len = (nArgs-1); // Spaces between args for (const auto& s : args_) { len += s.length(); } // Length needed for regrouped command-line commandLine_.reserve(len); return nArgs < argc; } void Foam::argList::setCasePaths() { fileName caseDir; const auto optIter = options_.cfind("case"); // [-case dir] specified? if (optIter.found()) { caseDir = optIter.object(); caseDir.clean(); if (caseDir.empty() || caseDir == ".") { // Treat "", "." and "./" as if -case was not specified caseDir = cwd(); options_.erase("case"); } else { caseDir.toAbsolute(); } } else { // Nothing specified, use the current dir caseDir = cwd(); } // The caseDir is a cleaned, absolute path rootPath_ = caseDir.path(); globalCase_ = caseDir.name(); case_ = globalCase_; // The (processor) local case name // Global case (directory) and case-name as environment variables setEnv("FOAM_CASE", caseDir, true); setEnv("FOAM_CASENAME", globalCase_, true); // Executable name, unless already present in the environment setEnv("FOAM_EXECUTABLE", executable_, false); } // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // Foam::argList::argList ( int& argc, char**& argv, bool checkArgs, bool checkOpts, bool initialise ) : args_(argc), options_(argc) { // Check for -fileHandler, which requires an argument. word handlerType(getEnv("FOAM_FILEHANDLER")); for (int argi = argc-2; argi > 0; --argi) { if (argv[argi][0] == '-') { const char *optName = &argv[argi][1]; if (strcmp(optName, "fileHandler") == 0) { handlerType = argv[argi+1]; break; } } } if (handlerType.empty()) { handlerType = fileOperation::defaultFileHandler; } // Detect any parallel options bool needsThread = fileOperations::fileOperationInitialise::New ( handlerType, argc, argv )().needsThreading(); // Check if this run is a parallel run by searching for any parallel option // If found call runPar which might filter argv for (int argi = 1; argi < argc; ++argi) { if (argv[argi][0] == '-') { const char *optName = &argv[argi][1]; if (validParOptions.found(optName)) { parRunControl_.runPar(argc, argv, needsThread); break; } } } // Convert argv -> args_ and capture ( ... ) lists regroupArgv(argc, argv); commandLine_ += args_[0]; // Set executable name immediately - useful when emitting errors. executable_ = fileName(args_[0]).name(); // Check arguments and options, argv[0] was already handled int nArgs = 1; for (int argi = 1; argi < args_.size(); ++argi) { commandLine_ += ' '; commandLine_ += args_[argi]; if (args_[argi][0] == '-') { const char *optName = &args_[argi][1]; if (!*optName) { Warning <<"Ignoring lone '-' on the command-line" << endl; } else if ( validOptions.lookup(optName, "").size() || validParOptions.lookup(optName, "").size() ) { // If the option is known to require an argument, // get it or emit a FatalError. ++argi; if (argi >= args_.size()) { foamVersion::printBuildInfo(false); Info<& options, bool checkArgs, bool checkOpts, bool initialise ) : parRunControl_(args.parRunControl_), args_(args.args_), options_(options), executable_(args.executable_), rootPath_(args.rootPath_), globalCase_(args.globalCase_), case_(args.case_), commandLine_(args.commandLine_) { parse(checkArgs, checkOpts, initialise); } void Foam::argList::parse ( bool checkArgs, bool checkOpts, bool initialise ) { // Help/documentation options: // -doc Display documentation in browser // -doc-source Display source code in browser // -help Display short help and exit // -help-compat Display compatibility options // -help-full Display full help and exit { bool quickExit = false; // Display either application or source documentation, not both if (options_.found("doc")) { displayDoc(false); quickExit = true; } else if ( options_.found("doc-source") || options_.found("srcDoc") // Compat 1706 ) { displayDoc(true); quickExit = true; } // Display either short or full help, not both if (options_.found("help-full")) { printUsage(true); quickExit = true; } else if (options_.found("help")) { printUsage(false); quickExit = true; } else if (options_.found("help-man")) { printMan(); quickExit = true; } // Allow independent display of compatibility information if (options_.found("help-compat")) { printCompat(); quickExit = true; } if (quickExit) { ::exit(0); } } // Print the collected error messages and exit if check fails if (!check(checkArgs, checkOpts)) { foamVersion::printBuildInfo(false); FatalError.write(Info, false); Pstream::exit(1); // works for serial and parallel } if (initialise) { const string dateString = clock::date(); const string timeString = clock::clockTime(); // Print the banner once only for parallel runs if (Pstream::master() && bannerEnabled()) { IOobject::writeBanner(Info, true) << "Build : "; if (foamVersion::build.size()) { Info<< foamVersion::build.c_str() << ' '; } Info<< "OPENFOAM=" << foamVersion::api; if (foamVersion::patched()) { // Patch-level, when defined Info<< " patch=" << foamVersion::patch.c_str(); } Info<< nl << "Arch : " << foamVersion::buildArch << nl << "Exec : " << commandLine_.c_str() << nl << "Date : " << dateString.c_str() << nl << "Time : " << timeString.c_str() << nl << "Host : " << hostName().c_str() << nl << "PID : " << pid() << endl; } jobInfo.add("startDate", dateString); jobInfo.add("startTime", timeString); jobInfo.add("userName", userName()); jobInfo.add("foamVersion", word(foamVersion::version)); jobInfo.add("code", executable_); jobInfo.add("argList", commandLine_); jobInfo.add("currentDir", cwd()); jobInfo.add("PPID", ppid()); jobInfo.add("PGID", pgid()); // Add build information - only use the first word { std::string build(foamVersion::build); const auto space = build.find(' '); if (space != std::string::npos) { build.resize(space); } jobInfo.add("foamBuild", build); } } // Set fileHandler. In increasing order of priority: // 1. default = uncollated // 2. environment var FOAM_FILEHANDLER // 3. etc/controlDict optimisationSwitches 'fileHandler' // 4. system/controlDict 'fileHandler' (not handled here; done in TimeIO.C) // 5. '-fileHandler' commmand-line option { word fileHandlerName = options_.lookup("fileHandler", getEnv("FOAM_FILEHANDLER")); if (fileHandlerName.empty()) { fileHandlerName = fileOperation::defaultFileHandler; } auto handler = fileOperation::New(fileHandlerName, bannerEnabled()); Foam::fileHandler(handler); } stringList slaveProcs; stringList slaveMachine; const int writeHostsSwitch = debug::infoSwitch("writeHosts", 1); // Collect slave machine/pid, and check that the build is identical if (parRunControl_.parRun()) { if (Pstream::master()) { slaveProcs.resize(Pstream::nProcs()-1); slaveMachine.resize(Pstream::nProcs()-1); label proci = 0; for ( int slave = Pstream::firstSlave(); slave <= Pstream::lastSlave(); slave++ ) { IPstream fromSlave(Pstream::commsTypes::scheduled, slave); string slaveBuild; label slavePid; fromSlave >> slaveBuild >> slaveMachine[proci] >> slavePid; slaveProcs[proci] = slaveMachine[proci] + "." + name(slavePid); proci++; // Verify that all processors are running the same build if (slaveBuild != foamVersion::build) { FatalErrorIn(executable()) << "Master is running version " << foamVersion::build << "; slave " << proci << " is running version " << slaveBuild << exit(FatalError); } } } else { OPstream toMaster ( Pstream::commsTypes::scheduled, Pstream::masterNo() ); toMaster << foamVersion::build << hostName() << pid(); } } // Case is a single processor run unless it is running parallel int nProcs = 1; // Roots if running distributed fileNameList roots; // If this actually is a parallel run if (parRunControl_.parRun()) { // For the master if (Pstream::master()) { // Establish rootPath_/globalCase_/case_ for master setCasePaths(); // Establish location of decomposeParDict, allow override with // the -decomposeParDict option. fileName source = rootPath_/globalCase_/"system"/"decomposeParDict"; if (options_.found("decomposeParDict")) { bool adjustOpt = false; source = options_["decomposeParDict"]; if (isDir(source)) { source /= "decomposeParDict"; adjustOpt = true; } // Case-relative if not absolute and not "./" etc if (!source.isAbsolute() && !source.startsWith(".")) { source = rootPath_/globalCase_/source; adjustOpt = true; } // Could also check for absolute path, but shouldn't be needed if (adjustOpt) { source.clean(); options_.set("decomposeParDict", source); } } // If running distributed (different roots for different procs) label dictNProcs = -1; if (this->readListIfPresent("roots", roots)) { parRunControl_.distributed(true); source = "-roots"; if (roots.size() != 1) { dictNProcs = roots.size()+1; } } else if (options_.found("hostRoots")) { roots.resize(Pstream::nProcs()-1, fileName::null); source = "-hostRoots"; ITstream is(source, options_["hostRoots"]); List> hostRoots(is); checkITstream(is, "hostRoots"); for (const auto& hostRoot : hostRoots) { labelList matched ( findStrings(hostRoot.first(), slaveMachine) ); for (const label slavei : matched) { if (!roots[slavei].empty()) { FatalErrorInFunction << "Slave " << slaveMachine[slavei] << " has multiple matching roots in " << hostRoots << exit(FatalError); } roots[slavei] = hostRoot.second(); } } // Check forAll(roots, slavei) { if (roots[slavei].empty()) { FatalErrorInFunction << "Slave " << slaveMachine[slavei] << " has no matching roots in " << hostRoots << exit(FatalError); } } if (roots.size() != 1) { dictNProcs = roots.size()+1; } } else if (checkProcessorDirectories_) { // Use values from decomposeParDict, the location was already // established above. IFstream decompDictStream(source); if (!decompDictStream.good()) { FatalError << "Cannot read decomposeParDict from " << decompDictStream.name() << exit(FatalError); } dictionary decompDict(decompDictStream); decompDict.readEntry("numberOfSubdomains", dictNProcs); if (decompDict.lookupOrDefault("distributed", false)) { parRunControl_.distributed(true); decompDict.readEntry("roots", roots); } } // Convenience: // when a single root is specified, use it for all processes if (roots.size() == 1) { const fileName rootName(roots[0]); roots.resize(Pstream::nProcs()-1, rootName); // adjust dictNProcs for command-line '-roots' option if (dictNProcs < 0) { dictNProcs = roots.size()+1; } } // Check number of processors. // nProcs => number of actual procs // dictNProcs => number of procs specified in decompositionDict // nProcDirs => number of processor directories // (n/a when running distributed) // // - normal running : nProcs = dictNProcs = nProcDirs // - decomposition to more processors : nProcs = dictNProcs // - decomposition to fewer processors : nProcs = nProcDirs if (checkProcessorDirectories_ && dictNProcs > Pstream::nProcs()) { FatalError << source << " specifies " << dictNProcs << " processors but job was started with " << Pstream::nProcs() << " processors." << exit(FatalError); } // Distributed data if (roots.size()) { if (roots.size() != Pstream::nProcs()-1) { FatalError << "number of entries in roots " << roots.size() << " is not equal to the number of slaves " << Pstream::nProcs()-1 << exit(FatalError); } for (fileName& dir : roots) { dir.expand(); } // Distribute the master's argument list (with new root) const bool hadCaseOpt = options_.found("case"); for ( int slave = Pstream::firstSlave(); slave <= Pstream::lastSlave(); slave++ ) { options_.set("case", roots[slave-1]/globalCase_); OPstream toSlave(Pstream::commsTypes::scheduled, slave); toSlave << args_ << options_ << roots.size(); } options_.erase("case"); // Restore [-case dir] if (hadCaseOpt) { options_.set("case", rootPath_/globalCase_); } } else { // Possibly going to fewer processors. // Check if all procDirs are there. if ( checkProcessorDirectories_ && dictNProcs < Pstream::nProcs() ) { label nProcDirs = 0; while ( isDir ( rootPath_/globalCase_ / ("processor" + Foam::name(++nProcDirs)) ) ) {} if (nProcDirs != Pstream::nProcs()) { FatalError << "number of processor directories = " << nProcDirs << " is not equal to the number of processors = " << Pstream::nProcs() << exit(FatalError); } } // Distribute the master's argument list (unaltered) for ( int slave = Pstream::firstSlave(); slave <= Pstream::lastSlave(); slave++ ) { OPstream toSlave(Pstream::commsTypes::scheduled, slave); toSlave << args_ << options_ << roots.size(); } } } else { // Collect the master's argument list label nroots; IPstream fromMaster ( Pstream::commsTypes::scheduled, Pstream::masterNo() ); fromMaster >> args_ >> options_ >> nroots; parRunControl_.distributed(nroots); // Establish rootPath_/globalCase_/case_ for slave setCasePaths(); } nProcs = Pstream::nProcs(); case_ = globalCase_/("processor" + Foam::name(Pstream::myProcNo())); } else { // Establish rootPath_/globalCase_/case_ setCasePaths(); case_ = globalCase_; // Redundant, but extra safety? } // Keep or discard slave and root information for reporting: if (Pstream::master() && parRunControl_.parRun()) { if (!writeHostsSwitch) { // Clear here to ensures it doesn't show in the jobInfo slaveProcs.clear(); } if (!debug::infoSwitch("writeRoots", 1)) { roots.clear(); } } if (Pstream::master() && bannerEnabled()) { Info<< "Case : " << (rootPath_/globalCase_).c_str() << nl << "nProcs : " << nProcs << endl; if (parRunControl_.parRun()) { if (slaveProcs.size()) { if (writeHostsSwitch == 1) { // Compact output (see etc/controlDict) printHostsSubscription(slaveProcs); } else { // Full output of "slave.pid" Info<< "Slaves : " << slaveProcs << nl; } } if (roots.size()) { Info<< "Roots : " << roots << nl; } Info<< "Pstream initialized with:" << nl << " floatTransfer : " << Pstream::floatTransfer << nl << " nProcsSimpleSum : " << Pstream::nProcsSimpleSum << nl << " commsType : " << Pstream::commsTypeNames[Pstream::defaultCommsType] << nl << " polling iterations : " << Pstream::nPollProcInterfaces << endl; } } if (initialise) { jobInfo.add("root", rootPath_); jobInfo.add("case", globalCase_); jobInfo.add("nProcs", nProcs); if (slaveProcs.size()) { jobInfo.add("slaves", slaveProcs); } if (roots.size()) { jobInfo.add("roots", roots); } jobInfo.write(); // Switch on signal trapping. We have to wait until after Pstream::init // since this sets up its own ones. sigFpe::set(bannerEnabled()); sigInt::set(bannerEnabled()); sigQuit::set(bannerEnabled()); sigSegv::set(bannerEnabled()); if (Pstream::master() && bannerEnabled()) { Info<< "fileModificationChecking : " << "Monitoring run-time modified files using " << regIOobject::fileCheckTypesNames [ regIOobject::fileModificationChecking ]; if ( ( regIOobject::fileModificationChecking == regIOobject::timeStamp ) || ( regIOobject::fileModificationChecking == regIOobject::timeStampMaster ) ) { Info<< " (fileModificationSkew " << regIOobject::fileModificationSkew << ")"; } Info<< nl; Info<< "allowSystemOperations : "; if (dynamicCode::allowSystemOperations) { Info<< "Allowing"; } else { Info<< "Disallowing"; } Info<< " user-supplied system call operations" << nl << endl; IOobject::writeDivider(Info); } } } // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * // Foam::argList::~argList() { jobInfo.end(); // Delete file handler to flush any remaining IO autoPtr dummy(nullptr); fileHandler(dummy); } // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // Foam::label Foam::argList::count(const UList& optionNames) const { label n = 0; for (const word& optName : optionNames) { if (options_.found(optName)) { ++n; } } return n; } Foam::label Foam::argList::count ( std::initializer_list optionNames ) const { label n = 0; for (const word& optName : optionNames) { if (options_.found(optName)) { ++n; } } return n; } bool Foam::argList::setOption(const word& optName, const string& param) { // Some options are always protected if ( optName == "case" || optName == "parallel" || optName == "roots" ) { FatalErrorInFunction <<"Option: '" << optName << "' is protected" << nl << exit(FatalError); return false; } if (options_.found(optName) ? (options_[optName] != param) : true) { options_.set(optName, param); return true; } return false; } bool Foam::argList::unsetOption(const word& optName) { // Some options are always protected if ( optName == "case" || optName == "parallel" || optName == "roots" || optName == "hostRoots" ) { FatalErrorInFunction <<"Option: '" << optName << "' is protected" << nl << exit(FatalError); return false; } // Remove the option, return true if state changed return options_.erase(optName); } void Foam::argList::displayDoc(bool source) const { const dictionary& docDict = debug::controlDict().subDict("Documentation"); fileNameList docDirs(docDict.get("doxyDocDirs")); fileName docExt(docDict.get("doxySourceFileExt")); // For source code: change xxx_8C.html to xxx_8C_source.html if (source) { docExt.replace(".", "_source."); } fileName url; for (const fileName& dir : docDirs) { // http protocols are last in the list if (dir.startsWith("http:") || dir.startsWith("https:")) { url = dir/executable_ + docExt; break; } fileName docFile = stringOps::expand(dir/executable_ + docExt); if ( docFile.startsWith("file://") ? isFile(docFile.substr(7)) // check part after "file://" : isFile(docFile) ) { url = std::move(docFile); break; } } if (url.empty()) { Info<< nl << "No documentation found for " << executable_ << ", but you can use -help to display the usage\n" << endl; return; } string docBrowser = getEnv("FOAM_DOC_BROWSER"); if (docBrowser.empty()) { docDict.readEntry("docBrowser", docBrowser); } // Can use FOAM_DOC_BROWSER='application file://%f' if required if (docBrowser.find("%f") != std::string::npos) { docBrowser.replace("%f", url); } else { docBrowser += " " + url; } // Split on whitespace to use safer version of Foam::system() CStringList command(stringOps::splitSpace(docBrowser)); Info << "OpenFOAM" #if OPENFOAM << ' ' << OPENFOAM #endif << " documentation:" << nl << " " << command << nl << endl; Foam::system(command, true); } bool Foam::argList::check(bool checkArgs, bool checkOpts) const { bool ok = true; if (Pstream::master()) { const label nargs = args_.size()-1; if (checkArgs && nargs != validArgs.size()) { FatalError << "Expected " << validArgs.size() << " arguments but found " << nargs << endl; ok = false; } if (checkOpts) { forAllConstIters(options_, iter) { const word& optName = iter.key(); if ( !validOptions.found(optName) && !validParOptions.found(optName) ) { FatalError << "Invalid option: -" << optName << endl; ok = false; } } } if (!ok) { FatalError << nl << "See '" << executable_ << " -help' for usage" << nl << nl; } } return ok; } bool Foam::argList::checkRootCase() const { if (!fileHandler().isDir(rootPath())) { FatalError << executable_ << ": cannot open root directory " << rootPath() << endl; return false; } const fileName pathDir(fileHandler().filePath(path())); if (checkProcessorDirectories_ && pathDir.empty() && Pstream::master()) { // Allow slaves on non-existing processor directories, created later // (e.g. redistributePar) FatalError << executable_ << ": cannot open case directory " << path() << endl; return false; } return true; } // ************************************************************************* //