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 3859 : CLToolOptions::CLToolOptions(const std::string &name):
29 3859 : line(1,name),
30 3859 : keys(emptyKeys)
31 : {
32 3859 : }
33 :
34 3859 : CLToolOptions::CLToolOptions(const CLToolOptions& co, const Keywords& k):
35 3859 : line(co.line),
36 3859 : keys(k)
37 : {
38 3859 : }
39 :
40 90377 : void CLTool::registerKeywords( Keywords& keys ) {
41 180754 : keys.addFlag("--help/-h",false,"print this help");
42 90377 : }
43 :
44 3859 : CLTool::CLTool(const CLToolOptions& co ):
45 3859 : name(co.line[0]),
46 3859 : keywords(co.keys),
47 3859 : inputdata(unset)
48 : {
49 3859 : }
50 :
51 25970 : void CLTool::parseFlag( const std::string&key, bool&t ) {
52 25970 : plumed_massert(keywords.exists(key),"keyword " + key + " has not been registered");
53 51940 : plumed_massert(keywords.style(key,"flag"),"keyword " + key + " has not been registered as a flag");
54 0 : plumed_assert(inputData.count(key)>0);
55 51940 : if( inputData[key]=="true") t=true;
56 46278 : else if( inputData[key]=="false") t=false;
57 0 : else plumed_error();
58 25970 : }
59 :
60 3783 : bool CLTool::readInput( int argc, char**argv, FILE* in, FILE*out ) {
61 3783 : plumed_massert( inputdata!=unset,"You have not specified where your tool reads its input. "
62 : "If it is from the command line (like driver) add inputdata=commandline to the "
63 : "tools constructor. If it reads everything from an input file (like simplemd) "
64 : "add inputdata=ifile to the tools constructor");
65 3783 : if(inputdata==commandline) return readCommandLineArgs( argc, argv, out );
66 52 : if(inputdata==ifile) return readInputFile( argc, argv, in, out );
67 : return true;
68 : }
69 :
70 3731 : bool CLTool::readCommandLineArgs( int argc, char**argv, FILE*out ) {
71 3731 : plumed_assert(inputdata==commandline);
72 3731 : std::string prefix(""), a(""), thiskey;
73 :
74 : // Set all flags to default false
75 69384 : for(unsigned k=0; k<keywords.size(); ++k) {
76 65653 : thiskey=keywords.get(k);
77 161410 : if( keywords.style(thiskey,"flag") ) inputData.insert(std::pair<std::string,std::string>(thiskey,"false"));
78 : }
79 :
80 : // Read command line arguments
81 : bool printhelp=false;
82 15155 : for(int i=1; i<argc; i++) {
83 22848 : a=prefix+argv[i];
84 11424 : if(a.length()==0) continue;
85 22848 : if(a=="-h" || a=="--help") {
86 : printhelp=true;
87 : } else {
88 : bool found=false;
89 368155 : for(unsigned k=0; k<keywords.size(); ++k) {
90 356731 : thiskey=keywords.get(k);
91 713462 : if( keywords.style(thiskey,"flag") ) {
92 73070 : if( a==thiskey ) { found=true; inputData[thiskey]="true"; }
93 : } else {
94 283661 : if( a==thiskey ) {
95 3974 : prefix=thiskey+"="; found=true;
96 7948 : inputData.insert(std::pair<std::string,std::string>(thiskey,""));
97 559374 : } else if(Tools::startWith(a,thiskey+"=")) {
98 4621 : a.erase(0,a.find("=")+1); prefix=""; found=true;
99 : if(inputData.count(thiskey)==0) {
100 1296 : inputData.insert(std::pair<std::string,std::string>(thiskey,a));
101 : } else {
102 3973 : inputData[thiskey]=a;
103 : }
104 : }
105 : }
106 : }
107 11424 : if(!found) {
108 0 : std::fprintf(stderr,"ERROR in input for command line tool %s : %s option is unknown \n\n", name.c_str(), a.c_str() );
109 : std::fprintf(out,"Usage: %s < inputFile \n", name.c_str() );
110 : std::fprintf(out,"inputFile should contain one directive per line. The directives should come from amongst the following\n\n");
111 0 : keywords.print( out );
112 : printhelp=true;
113 : }
114 : }
115 11424 : if(printhelp) break;
116 : }
117 :
118 3731 : if(!printhelp) setRemainingToDefault(out);
119 :
120 3731 : if(printhelp) {
121 : std::fprintf(out,"Usage: %s [options] \n\n", name.c_str() );
122 0 : keywords.print( out );
123 : }
124 :
125 3731 : return !printhelp;
126 : }
127 :
128 3783 : void CLTool::setRemainingToDefault(FILE* out) {
129 : std::string def, thiskey;
130 70444 : for(unsigned k=0; k<keywords.size(); ++k) {
131 66661 : thiskey=keywords.get(k);
132 133322 : if( keywords.style(thiskey,"compulsory") ) {
133 : if( inputData.count(thiskey)==0 ) {
134 1993 : if( keywords.getDefaultValue(thiskey,def) ) {
135 1993 : plumed_assert( def.length()>0 );
136 3986 : inputData.insert(std::pair<std::string,std::string>(thiskey,def));
137 : } else {
138 : std::fprintf(out,"ERROR : argument %s is compulsory. Use --help option for help\n",thiskey.c_str() );
139 0 : plumed_error();
140 : }
141 : }
142 : }
143 : }
144 3783 : }
145 :
146 52 : bool CLTool::readInputFile( int argc, char**argv, FILE* in, FILE*out ) {
147 52 : plumed_assert(inputdata==ifile);
148 :
149 : // Check if use is just asking for help
150 : std::string a;
151 95 : for(int i=1; i<argc; i++) {
152 43 : a=argv[i];
153 43 : if(a.length()==0) continue;
154 86 : if(a=="-h" || a=="--help") {
155 : std::fprintf(out,"Usage: %s < inputFile \n", name.c_str() );
156 : std::fprintf(out,"inputFile should contain one directive per line. The directives should come from amongst the following\n\n");
157 0 : keywords.print( out );
158 : return false;
159 : }
160 : }
161 :
162 : FILE* mystdin=in;
163 : // call fclose when fp_deleter goes out of scope
164 43 : auto deleter=[](auto f) { std::fclose(f); };
165 : std::unique_ptr<FILE,decltype(deleter)> fp_deleter(nullptr,deleter);
166 :
167 52 : if(argc==2) {
168 43 : mystdin=std::fopen(argv[1],"r");
169 43 : if(!mystdin) {
170 0 : std::fprintf(stderr,"ERROR: cannot open file %s\n\n",argv[1]);
171 : std::fprintf(out,"Usage: %s < inputFile \n", name.c_str() );
172 : std::fprintf(out,"inputFile should contain one directive per line. The directives should come from amongst the following\n\n");
173 0 : keywords.print( out );
174 : return false;
175 : }
176 : fp_deleter.reset(mystdin);
177 : }
178 :
179 52 : plumed_assert(mystdin);
180 :
181 : char buffer[256]; std::string line; line.resize(256);
182 863 : while(fgets(buffer,256,mystdin)) {
183 : line=buffer;
184 24504 : for(unsigned i=0; i<line.length(); ++i) if(line[i]=='#' || line[i]=='\n') line.erase(i);
185 811 : Tools::stripLeadingAndTrailingBlanks( line );
186 811 : if(line.length()==0) continue;
187 774 : std::sscanf(line.c_str(),"%255s",buffer);
188 774 : std::string keyword=buffer; bool found=false;
189 16220 : for(unsigned i=0; i<keywords.size(); ++i) {
190 15446 : std::string thiskey=keywords.get(i);
191 15446 : if(thiskey==keyword) {
192 : found=true;
193 774 : std::size_t keypos=line.find_first_of(keyword)+keyword.length();
194 1548 : inputData.insert(std::pair<std::string,std::string>(thiskey,line.substr(keypos)));
195 774 : Tools::stripLeadingAndTrailingBlanks( inputData[thiskey] );
196 : }
197 : }
198 774 : if(!found) {
199 0 : std::fprintf(stderr,"ERROR in input for command line tool %s : unknown keyword %s found in input file\n\n",name.c_str(),keyword.c_str());
200 : std::fprintf(out,"Usage: %s < inputFile \n", name.c_str() );
201 : std::fprintf(out,"inputFile should contain one directive per line. The directives should come from amongst the following\n\n");
202 0 : keywords.print( out );
203 : return false;
204 : }
205 : }
206 :
207 52 : setRemainingToDefault(out);
208 : return true;
209 : }
210 :
211 0 : [[noreturn]] void CLTool::error( const std::string& msg ) {
212 0 : std::fprintf(stderr,"ERROR : in input for command line tool %s : %s\n",name.c_str(),msg.c_str());
213 0 : plumed_error();
214 : }
215 :
216 : }
|