LCOV - code coverage report
Current view: top level - core - CLTool.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 112 120 93.3 %
Date: 2026-11-05 10:19:04 Functions: 10 10 100.0 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2012-2023 The plumed team
       3             :    (see the PEOPLE file at the root of the distribution for a list of names)
       4             : 
       5             :    See http://www.plumed.org for more information.
       6             : 
       7             :    This file is part of plumed, version 2.
       8             : 
       9             :    plumed is free software: you can redistribute it and/or modify
      10             :    it under the terms of the GNU Lesser General Public License as published by
      11             :    the Free Software Foundation, either version 3 of the License, or
      12             :    (at your option) any later version.
      13             : 
      14             :    plumed is distributed in the hope that it will be useful,
      15             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      16             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17             :    GNU Lesser General Public License for more details.
      18             : 
      19             :    You should have received a copy of the GNU Lesser General Public License
      20             :    along with plumed.  If not, see <http://www.gnu.org/licenses/>.
      21             : +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
      22             : #include "CLTool.h"
      23             : 
      24             : namespace PLMD {
      25             : 
      26             : Keywords CLToolOptions::emptyKeys;
      27             : 
      28        5268 : CLToolOptions::CLToolOptions(const std::string &name):
      29        5268 :   line(1,name),
      30        5268 :   keys(emptyKeys) {
      31        5268 : }
      32             : 
      33        5268 : CLToolOptions::CLToolOptions(const CLToolOptions& co, const Keywords& k):
      34        5268 :   line(co.line),
      35        5268 :   keys(k) {
      36        5268 : }
      37             : 
      38      139105 : void CLTool::registerKeywords( Keywords& keys ) {
      39      139105 :   keys.addFlag("--help/-h",false,"print this help");
      40      139105 : }
      41             : 
      42        5268 : CLTool::CLTool(const CLToolOptions& co ):
      43        5268 :   name(co.line[0]),
      44        5268 :   keywords(co.keys),
      45        5268 :   inputdata(inputType::unset) {
      46        5268 : }
      47             : 
      48       35684 : void CLTool::parseFlag( const std::string&key, bool&t ) {
      49       35684 :   plumed_massert(keywords.exists(key),"keyword " + key + " has not been registered");
      50       71368 :   plumed_massert(keywords.style(key,"flag"),"keyword " + key + " has not been registered as a flag");
      51           0 :   plumed_assert(inputData.count(key)>0);
      52       35684 :   if( inputData[key]=="true") {
      53        3771 :     t=true;
      54       31913 :   } else if( inputData[key]=="false") {
      55       31913 :     t=false;
      56             :   } else {
      57           0 :     plumed_error();
      58             :   }
      59       35684 : }
      60             : 
      61        5134 : bool CLTool::readInput( int argc, char**argv, FILE* in, FILE*out ) {
      62        5134 :   plumed_massert( inputdata!=inputType::unset,"You have not specified where your tool reads its input. "
      63             :                   "If it is from the command line (like driver) add inputdata=inputType::commandline to the "
      64             :                   "tools constructor. If it reads everything from an input file (like simplemd) "
      65             :                   "add inputdata=inputType::ifile to the tools constructor");
      66        5134 :   if(inputdata==inputType::commandline) {
      67        5069 :     return readCommandLineArgs( argc, argv, out );
      68             :   }
      69          65 :   if(inputdata==inputType::ifile) {
      70          65 :     return readInputFile( argc, argv, in, out );
      71             :   }
      72             :   return true;
      73             : }
      74             : 
      75        5069 : bool CLTool::readCommandLineArgs( int argc, char**argv, FILE*out ) {
      76        5069 :   plumed_assert(inputdata==inputType::commandline);
      77        5069 :   std::string prefix(""), a(""), thiskey;
      78             : 
      79             :   // Set all flags to default false
      80       96766 :   for(unsigned k=0; k<keywords.size(); ++k) {
      81       91697 :     thiskey=keywords.getKeyword(k);
      82      183394 :     if( keywords.style(thiskey,"flag") ) {
      83       82816 :       inputData.insert(std::pair<std::string,std::string>(thiskey,"false"));
      84             :     }
      85             :   }
      86             : 
      87             :   // Read command line arguments
      88             :   bool printhelp=false;
      89       20689 :   for(int i=1; i<argc; i++) {
      90       31240 :     a=prefix+argv[i];
      91       15620 :     if(a.length()==0) {
      92           0 :       continue;
      93             :     }
      94       15620 :     if(a=="-h" || a=="--help") {
      95             :       printhelp=true;
      96             :     } else {
      97             :       bool found=false;
      98      514347 :       for(unsigned k=0; k<keywords.size(); ++k) {
      99      498727 :         thiskey=keywords.getKeyword(k);
     100      997454 :         if( keywords.style(thiskey,"flag") ) {
     101      100836 :           if( a==thiskey ) {
     102             :             found=true;
     103        3769 :             inputData[thiskey]="true";
     104             :           }
     105             :         } else {
     106      397891 :           if( a==thiskey ) {
     107        5545 :             prefix=thiskey+"=";
     108             :             found=true;
     109       11090 :             inputData.insert(std::pair<std::string,std::string>(thiskey,""));
     110      784692 :           } else if(Tools::startWith(a,thiskey+"=")) {
     111        6306 :             a.erase(0,a.find("=")+1);
     112             :             prefix="";
     113             :             found=true;
     114             :             if(inputData.count(thiskey)==0) {
     115        1524 :               inputData.insert(std::pair<std::string,std::string>(thiskey,a));
     116             :             } else {
     117        5544 :               inputData[thiskey]=a;
     118             :             }
     119             :           }
     120             :         }
     121             :       }
     122       15620 :       if(!found) {
     123           0 :         std::fprintf(stderr,"ERROR in input for command line tool %s : %s option is unknown \n\n", name.c_str(), a.c_str() );
     124             :         std::fprintf(out,"Usage: %s < inputFile \n", name.c_str() );
     125             :         std::fprintf(out,"inputFile should contain one directive per line.  The directives should come from amongst the following\n\n");
     126           0 :         keywords.print( out );
     127             :         printhelp=true;
     128             :       }
     129             :     }
     130       15620 :     if(printhelp) {
     131             :       break;
     132             :     }
     133             :   }
     134             : 
     135        5069 :   if(!printhelp) {
     136        5069 :     setRemainingToDefault(out);
     137             :   }
     138             : 
     139             :   if(printhelp) {
     140             :     std::fprintf(out,"Usage: %s [options] \n\n", name.c_str() );
     141           0 :     keywords.print( out );
     142             :   }
     143             : 
     144       10138 :   return !printhelp;
     145      397891 : }
     146             : 
     147        5131 : void CLTool::setRemainingToDefault(FILE* out) {
     148             :   std::string def, thiskey;
     149       97981 :   for(unsigned k=0; k<keywords.size(); ++k) {
     150       92851 :     thiskey=keywords.getKeyword(k);
     151      185702 :     if( keywords.style(thiskey,"compulsory") ) {
     152             :       if( inputData.count(thiskey)==0 ) {
     153        2877 :         if( keywords.getDefaultValue(thiskey,def) ) {
     154        2876 :           plumed_assert( def.length()>0 );
     155        5753 :           inputData.insert(std::pair<std::string,std::string>(thiskey,def));
     156             :         } else {
     157             :           std::fprintf(out,"ERROR : argument %s is compulsory. Use --help option for help\n",thiskey.c_str() );
     158           2 :           plumed_error();
     159             :         }
     160             :       }
     161             :     }
     162             :   }
     163        5130 : }
     164             : 
     165          65 : bool CLTool::readInputFile( int argc, char**argv, FILE* in, FILE*out ) {
     166          65 :   plumed_assert(inputdata==inputType::ifile);
     167             : 
     168             :   // Check if use is just asking for help
     169             :   std::string a;
     170         111 :   for(int i=1; i<argc; i++) {
     171          46 :     a=argv[i];
     172          46 :     if(a.length()==0) {
     173           0 :       continue;
     174             :     }
     175          46 :     if(a=="-h" || a=="--help") {
     176             :       std::fprintf(out,"Usage: %s < inputFile \n", name.c_str() );
     177             :       std::fprintf(out,"inputFile should contain one directive per line.  The directives should come from amongst the following\n\n");
     178           0 :       keywords.print( out );
     179             :       return false;
     180             :     }
     181             :   }
     182             : 
     183             :   FILE* mystdin=in;
     184             : // call fclose when fp_deleter goes out of scope
     185             :   auto deleter=[](auto f) {
     186          45 :     std::fclose(f);
     187          45 :   };
     188             :   std::unique_ptr<FILE,decltype(deleter)> fp_deleter(nullptr,deleter);
     189             : 
     190          65 :   if(argc==2) {
     191          46 :     mystdin=std::fopen(argv[1],"r");
     192          46 :     if(!mystdin) {
     193             :       std::fprintf(out,"Usage: %s < inputFile \n", name.c_str() );
     194             :       std::fprintf(out,"inputFile should contain one directive per line.  The directives should come from amongst the following\n\n");
     195           1 :       keywords.print( out );
     196             :       // an error, so that the exit code is not zero
     197           6 :       error(std::string("cannot open file ") + argv[1]);
     198             :     }
     199             :     fp_deleter.reset(mystdin);
     200             :   }
     201             : 
     202          64 :   plumed_assert(mystdin);
     203             : 
     204             :   // Set all flags to default false
     205        1248 :   for(unsigned k=0; k<keywords.size(); ++k) {
     206        1184 :     const std::string & thiskey=keywords.getKeyword(k);
     207        2368 :     if( keywords.style(thiskey,"flag") ) {
     208         148 :       inputData.insert(std::pair<std::string,std::string>(thiskey,"false"));
     209             :     }
     210             :   }
     211             : 
     212             :   std::string line;
     213             :   // lines of any length
     214         936 :   while(Tools::getline(mystdin,line)) {
     215         874 :     auto comment=line.find('#');
     216         874 :     if(comment!=std::string::npos) {
     217          31 :       line.erase(comment);
     218             :     }
     219         874 :     Tools::stripLeadingAndTrailingBlanks( line );
     220         874 :     if(line.length()==0) {
     221          37 :       continue;
     222             :     }
     223         837 :     const std::string keyword=line.substr(0,line.find_first_of(" \t"));
     224             :     bool found=false;
     225       17286 :     for(unsigned i=0; i<keywords.size(); ++i) {
     226       16450 :       std::string thiskey=keywords.getKeyword(i);
     227       16450 :       if(thiskey==keyword) {
     228             :         found=true;
     229         836 :         std::string value=line.substr(keyword.length());
     230         836 :         Tools::stripLeadingAndTrailingBlanks( value );
     231        1672 :         if( keywords.style(thiskey,"flag") ) {
     232             :           // a flag can be given alone or followed by true or false
     233           4 :           if( value.length()==0 ) {
     234             :             value="true";
     235             :           }
     236           4 :           if( value!="true" && value!="false" ) {
     237           2 :             error("flag " + keyword + " should be followed by nothing, true or false");
     238             :           }
     239           3 :           inputData[thiskey]=value;
     240             :         } else {
     241        1665 :           inputData.insert(std::pair<std::string,std::string>(thiskey,value));
     242             :         }
     243             :       }
     244             :     }
     245         836 :     if(!found) {
     246             :       std::fprintf(out,"Usage: %s < inputFile \n", name.c_str() );
     247             :       std::fprintf(out,"inputFile should contain one directive per line.  The directives should come from amongst the following\n\n");
     248           1 :       keywords.print( out );
     249             :       // an error, so that the exit code is not zero
     250           4 :       error("unknown keyword " + keyword + " found in input file");
     251             :     }
     252             :   }
     253             : 
     254          62 :   setRemainingToDefault(out);
     255             :   return true;
     256             : }
     257             : 
     258          16 : [[noreturn]] void CLTool::error( const std::string& msg ) {
     259          16 :   std::fprintf(stderr,"ERROR : in input for command line tool %s : %s\n",name.c_str(),msg.c_str());
     260          32 :   plumed_error();
     261             : }
     262             : 
     263             : }

Generated by: LCOV version 1.16