LCOV - code coverage report
Current view: top level - core - CLToolMain.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 117 145 80.7 %
Date: 2026-03-30 13:16:06 Functions: 5 5 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 "CLToolMain.h"
      23             : #include "config/Config.h"
      24             : #include "tools/Exception.h"
      25             : #include "tools/Communicator.h"
      26             : #include "CLTool.h"
      27             : #include "CLToolRegister.h"
      28             : #include "tools/Tools.h"
      29             : #include "tools/DLLoader.h"
      30             : #include <string>
      31             : #include <cstdlib>
      32             : #include <cstdio>
      33             : #include <iostream>
      34             : #include <algorithm>
      35             : #include <memory>
      36             : #include <unordered_map>
      37             : 
      38             : namespace PLMD {
      39             : 
      40        4643 : CLToolMain::CLToolMain():
      41        4643 :   argc(0),
      42        4643 :   in(stdin),
      43        4643 :   out(stdout) {
      44        4643 : }
      45             : 
      46        9286 : CLToolMain::~CLToolMain() {
      47             : // empty destructor to delete unique_ptr
      48        9286 : }
      49             : 
      50             : #define CHECK_NULL(val,word) plumed_massert(val,"NULL pointer received in cmd(\"CLTool " + word + "\")");
      51             : 
      52       14193 : void CLToolMain::cmd(const std::string& word,const TypesafePtr & val) {
      53             : 
      54             : // Enumerate all possible commands:
      55             :   enum {
      56             : #include "CLToolMainEnum.inc"
      57             :   };
      58             : 
      59             : // Static object (initialized once) containing the map of commands:
      60             :   const static std::unordered_map<std::string, int> word_map = {
      61             : #include "CLToolMainMap.inc"
      62       55251 :   };
      63             : 
      64       14193 :   std::vector<std::string> words=Tools::getWords(word);
      65       14193 :   unsigned nw=words.size();
      66       14193 :   if(nw==0) {
      67             :     // do nothing
      68             :   } else {
      69             :     int iword=-1;
      70             :     const char*const*v;
      71             :     const char*vv;
      72             :     const auto it=word_map.find(words[0]);
      73       14193 :     if(it!=word_map.end()) {
      74       14193 :       iword=it->second;
      75             :     }
      76       14193 :     switch(iword) {
      77             :     case cmd_setArgc:
      78        4559 :       CHECK_NULL(val,word);
      79        4559 :       argc=val.get<int>();
      80        4559 :       break;
      81             :     case cmd_setArgv:
      82        4559 :       CHECK_NULL(val,word);
      83        4559 :       v=val.get<const char*const*>(argc);
      84       29738 :       for(int i=0; i<argc; ++i) {
      85       50358 :         argv.push_back(std::string(v[i]));
      86             :       }
      87             :       break;
      88             :     case cmd_setArgvLine:
      89          84 :       CHECK_NULL(val,word);
      90             :       vv=val.get<const char*>();
      91          84 :       argv=Tools::getWords(vv);
      92          84 :       break;
      93             :     case cmd_setIn:
      94           0 :       CHECK_NULL(val,word);
      95           0 :       in=val.get<FILE*>();
      96           0 :       break;
      97             :     case cmd_setOut:
      98           1 :       CHECK_NULL(val,word);
      99           1 :       out=val.get<FILE*>();
     100           1 :       break;
     101         347 :     case cmd_setMPIComm:
     102         347 :       comm.Set_comm(val);
     103             :       break;
     104           0 :     case cmd_setMPIFComm:
     105           0 :       comm.Set_fcomm(val);
     106             :       break;
     107             :     case cmd_run:
     108        4643 :       CHECK_NULL(val,word);
     109        4643 :       argc=argv.size();
     110             :       {
     111             :         int n=0;
     112       30310 :         for(int i=0; i<argc; ++i) {
     113       25667 :           n+=argv[i].length()+1;
     114             :         }
     115        4643 :         std::vector<char> args(n);
     116        4643 :         std::vector<char*> vvv(argc);
     117             :         char* ptr=&args[0];
     118       30310 :         for(int i=0; i<argc; ++i) {
     119       25667 :           vvv[i]=ptr;
     120      224256 :           for(unsigned c=0; c<argv[i].length(); ++c) {
     121      198589 :             *ptr=argv[i][c];
     122      198589 :             ptr++;
     123             :           }
     124       25667 :           *ptr=0;
     125       25667 :           ptr++;
     126             :         }
     127        4643 :         val.set(int(run(argc,&vvv[0],in,out,comm)));
     128             :       }
     129        4643 :       break;
     130           0 :     default:
     131           0 :       plumed_merror("cannot interpret cmd(\"CLTool " + word + "\"). check plumed developers manual to see the available commands.");
     132             :       break;
     133             :     }
     134             :   }
     135       14193 : }
     136             : 
     137             : /**
     138             : This is the entry point to the command line tools
     139             : included in the plumed library.
     140             : */
     141             : 
     142        4643 : int CLToolMain::run(int argc, char **argv,FILE*in,FILE*out,Communicator& pc) {
     143             :   int i;
     144             :   bool printhelp=false;
     145             : 
     146        4643 :   DLLoader dlloader;
     147             : 
     148        4643 :   std::string root=config::getPlumedRoot();
     149             : 
     150             :   bool standalone_executable=false;
     151             : 
     152             : // Start parsing options
     153        4643 :   std::string prefix("");
     154        4643 :   std::string a("");
     155        8855 :   for(i=1; i<argc; i++) {
     156       17710 :     a=prefix+argv[i];
     157        8855 :     if(a.length()==0) {
     158           0 :       continue;
     159             :     }
     160       26565 :     if(a=="help" || a=="-h" || a=="--help") {
     161             :       printhelp=true;
     162             :       break;
     163        8851 :     } else if(a=="--has-mpi") {
     164           0 :       if(Communicator::initialized()) {
     165             :         return 0;
     166             :       } else {
     167           0 :         return 1;
     168             :       }
     169        8851 :     } else if(a=="--has-cregex") {
     170           0 :       return (config::hasCregex()?0:1);
     171        8851 :     } else if(a=="--has-dlopen") {
     172           0 :       return (config::hasDlopen()?0:1);
     173        8851 :     } else if(a=="--has-molfile") {
     174           0 :       return (config::hasMolfile()?0:1);
     175        8851 :     } else if(a=="--has-external-molfile") {
     176           0 :       return (config::hasExternalMolfile()?0:1);
     177        8851 :     } else if(a=="--has-zlib") {
     178           0 :       return (config::hasZlib()?0:1);
     179        8851 :     } else if(a=="--has-xdrfile") {
     180             :       return 0; // always ok
     181        8851 :     } else if(a=="--is-installed") {
     182         661 :       return (config::isInstalled()?0:1);
     183        8190 :     } else if(a=="--no-mpi") {
     184             : // this is ignored, as it is parsed in main
     185        4212 :       continue;
     186        3978 :     } else if(a=="--mpi") {
     187             : // this is ignored, as it is parsed in main
     188           0 :       continue;
     189        3978 :     } else if(a=="--standalone-executable") {
     190             :       standalone_executable=true;
     191        7956 :     } else if(Tools::startWith(a,"--load=")) {
     192           0 :       a.erase(0,a.find("=")+1);
     193             :       prefix="";
     194           0 :       void *p=dlloader.load(a);
     195           0 :       if(!p) {
     196           0 :         std::fprintf(stderr,"ERROR: cannot load library %s\n",a.c_str());
     197           0 :         std::fprintf(stderr,"ERROR: %s\n",dlloader.error().c_str());
     198             :         return 1;
     199             :       }
     200        3978 :     } else if(a=="--load") {
     201             :       prefix="--load=";
     202        3978 :     } else if(a[0]=='-') {
     203           0 :       std::string msg="ERROR: Unknown option " +a;
     204           0 :       std::fprintf(stderr,"%s\n",msg.c_str());
     205             :       return 1;
     206             :     } else {
     207             :       break;
     208             :     }
     209             :   }
     210             : 
     211             : // Check if plumedRoot/patches/ directory exists (as a further check)
     212        3982 :   if(!standalone_executable) {
     213        3982 :     std::vector<std::string> files=Tools::ls(root);
     214        3982 :     if(find(files.begin(),files.end(),"patches")==files.end()) {
     215             :       std::string msg=
     216           0 :         "WARNING: I cannot find "+root+"/patches/ directory. Set PLUMED_ROOT or reinstall PLUMED\n\n";
     217           0 :       std::fprintf(stderr,"%s",msg.c_str());
     218             :     }
     219        3982 :   }
     220             : 
     221             : // Build list of available C++ tools:
     222        3982 :   std::vector<std::string> availableCxx=cltoolRegister().list();
     223             : // Build list of available shell tools:
     224             :   std::vector<std::string> availableShell;
     225        3982 :   if(!standalone_executable) {
     226             :     std::vector<std::string> tmp;
     227        7964 :     tmp=Tools::ls(std::string(root+"/scripts"));
     228       31856 :     for(unsigned j=0; j<tmp.size(); ++j) {
     229       27874 :       size_t ff=tmp[j].find(".sh");
     230       27874 :       if(ff==std::string::npos) {
     231           0 :         tmp[j].erase();
     232             :       } else {
     233       27874 :         tmp[j].erase(ff);
     234             :       }
     235             :     }
     236       31856 :     for(unsigned j=0; j<tmp.size(); ++j)
     237       27874 :       if(tmp[j].length()>0) {
     238       27874 :         availableShell.push_back(tmp[j]);
     239             :       }
     240        3982 :   }
     241             : 
     242        3982 :   if(printhelp) {
     243             :     std::string msg=
     244             :       "Usage: plumed [options] [command] [command options]\n"
     245             :       "  plumed [command] -h|--help: to print help for a specific command\n"
     246             :       "Options:\n"
     247             :       "  [help|-h|--help]          : to print this help\n"
     248             :       "  [--is-installed]          : fails if plumed is not installed\n"
     249             :       "  [--has-mpi]               : fails if plumed is running without MPI\n"
     250             :       "  [--has-dlopen]            : fails if plumed is compiled without dlopen\n"
     251             :       "  [--load LIB]              : loads a shared object (typically a plugin library)\n"
     252             :       "  [--standalone-executable] : tells plumed not to look for commands implemented as scripts\n"
     253           4 :       "Commands:\n";
     254             :     std::fprintf(out,"%s",msg.c_str());
     255          68 :     for(unsigned j=0; j<availableCxx.size(); ++j) {
     256         128 :       auto cl=cltoolRegister().create(CLToolOptions(availableCxx[j]));
     257          64 :       plumed_assert(cl);
     258         128 :       std::string manual=availableCxx[j]+" : "+cl->description();
     259             :       std::fprintf(out,"  plumed %s\n", manual.c_str());
     260          64 :     }
     261          32 :     for(unsigned j=0; j<availableShell.size(); ++j) {
     262             :       std::string manual;
     263             : #ifdef __PLUMED_HAS_POPEN
     264          56 :       std::string cmd=config::getEnvCommand()+" \""+root+"/scripts/"+availableShell[j]+".sh\" --description";
     265          28 :       FILE *fp=popen(cmd.c_str(),"r");
     266             :       std::string line;
     267          56 :       while(Tools::getline(fp,line)) {
     268             :         manual+=line;
     269             :       }
     270          28 :       pclose(fp);
     271             : #else
     272             :       manual="(doc not avail)";
     273             : #endif
     274          56 :       manual= availableShell[j]+" : "+manual;
     275             :       std::fprintf(out,"  plumed %s\n", manual.c_str());
     276             :     }
     277             :     return 0;
     278             :   }
     279        3978 :   if(i==argc) {
     280             :     std::fprintf(out,"%s","Nothing to do. Use 'plumed help' for help\n");
     281             :     return 0;
     282             :   }
     283             : 
     284             : // this is the command to be executed:
     285        3978 :   std::string command(argv[i]);
     286             : 
     287        3978 :   if(find(availableCxx.begin(),availableCxx.end(),command)!=availableCxx.end()) {
     288        6824 :     auto cl=cltoolRegister().create(CLToolOptions(command));
     289        3412 :     plumed_assert(cl);
     290             :     // Read the command line options (returns false if we are just printing help)
     291        3412 :     if( !cl->readInput( argc-i,&argv[i],in,out ) ) {
     292             :       return 0;
     293             :     }
     294        3412 :     int ret=cl->main(in,out,pc);
     295             :     return ret;
     296        3412 :   }
     297             : 
     298         566 :   if(find(availableShell.begin(),availableShell.end(),command)!=availableShell.end()) {
     299         566 :     plumed_massert(in==stdin,"shell tools can only work on stdin");
     300         566 :     plumed_massert(out==stdout,"shell tools can only work on stdin");
     301        1132 :     std::string cmd=config::getEnvCommand()+" \""+root+"/scripts/"+command+".sh\"";
     302        2146 :     for(int j=i+1; j<argc; j++) {
     303        3160 :       cmd+=std::string(" ")+argv[j];
     304             :     }
     305         566 :     int r=std::system(cmd.c_str());
     306             : // this is necessary since system seems to return numbers which are multiple
     307             : // of 256. this would make the interpretation by the shell wrong
     308             : // I just return 1 in case of failure and 0 in case of success
     309         566 :     if(r!=0) {
     310             :       return 1;
     311             :     } else {
     312         451 :       return 0;
     313             :     }
     314             :   }
     315             : 
     316           0 :   std::string msg="ERROR: unknown command " + command + ". Use 'plumed help' for help";
     317           0 :   std::fprintf(stderr,"%s\n",msg.c_str());
     318             :   return 1;
     319             : 
     320        8625 : }
     321             : }

Generated by: LCOV version 1.16