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 : }
|