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