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 "IFile.h"
23 : #include "Exception.h"
24 : #include "core/Action.h"
25 : #include "core/PlumedMain.h"
26 : #include "core/Value.h"
27 : #include "Tools.h"
28 : #include <cstdarg>
29 : #include <cstring>
30 : #include <cmath>
31 :
32 : #include <iostream>
33 : #include <string>
34 : #ifdef __PLUMED_HAS_ZLIB
35 : #include <zlib.h>
36 : #endif
37 :
38 : namespace PLMD {
39 :
40 1645715 : IFile& IFile::advanceField() {
41 1645715 : plumed_assert(!inMiddleOfField);
42 : std::string line;
43 : bool done=false;
44 3294762 : while(!done) {
45 1655746 : getline(line);
46 : // using explicit conversion not to confuse cppcheck 1.86
47 1655746 : if(!bool(*this)) {
48 6698 : return *this;
49 : }
50 1649048 : std::vector<std::string> words=Tools::getWords(line);
51 1649048 : if(words.size()>=2 && words[0]=="#!" && words[1]=="FIELDS") {
52 1017 : fields.clear();
53 9739 : for(unsigned i=2; i<words.size(); i++) {
54 : Field field;
55 : field.name=words[i];
56 8722 : fields.push_back(field);
57 : }
58 1648031 : } else if(words.size()==4 && words[0]=="#!" && words[1]=="SET") {
59 : Field field;
60 : field.name=words[2];
61 : field.value=words[3];
62 4305 : field.constant=true;
63 4305 : fields.push_back(field);
64 : } else {
65 : unsigned nf=0;
66 18931890 : for(unsigned i=0; i<fields.size(); i++)
67 17288164 : if(!fields[i].constant) {
68 8054692 : nf++;
69 : }
70 1643726 : Tools::trimComments(line);
71 3287452 : words=Tools::getWords(line);
72 1643726 : if( words.size()==nf ) {
73 : unsigned j=0;
74 18870964 : for(unsigned i=0; i<fields.size(); i++) {
75 17231948 : if(fields[i].constant) {
76 9198709 : continue;
77 : }
78 8033239 : fields[i].value=words[j];
79 8033239 : fields[i].read=false;
80 8033239 : j++;
81 : }
82 : done=true;
83 4710 : } else if( !words.empty() ) {
84 4 : plumed_merror("file " + getPath() + ": mismatch between number of fields in file and expected number\n this is the faulty line:\n"+line);
85 : }
86 : }
87 1649048 : }
88 1639016 : inMiddleOfField=true;
89 1639016 : return *this;
90 : }
91 :
92 2204 : IFile& IFile::open(const std::string&mypath) {
93 2204 : plumed_massert(!cloned,"file "+mypath+" appears to be cloned");
94 2204 : eof=false;
95 2204 : err=false;
96 2204 : fp=NULL;
97 2204 : gzfp=NULL;
98 2204 : if(!FileExist(mypath)) {
99 6 : plumed_error() << "file " << mypath << " cannot be found";
100 : }
101 2201 : fp=std::fopen(const_cast<char*>(path.c_str()),"r");
102 2201 : if(Tools::extension(path)=="gz") {
103 : #ifdef __PLUMED_HAS_ZLIB
104 7 : gzfp=(void*)gzopen(const_cast<char*>(path.c_str()),"r");
105 : #else
106 : plumed_merror("file " + getPath() + ": trying to use a gz file without zlib being linked");
107 : #endif
108 : }
109 2201 : if(plumed) {
110 1907 : plumed->insertFile(*this);
111 : }
112 2201 : return *this;
113 : }
114 :
115 135 : IFile& IFile::scanFieldList(std::vector<std::string>&s) {
116 135 : if(!inMiddleOfField) {
117 126 : advanceField();
118 : }
119 : // using explicit conversion not to confuse cppcheck 1.86
120 135 : if(!bool(*this)) {
121 : return *this;
122 : }
123 135 : s.clear();
124 1763 : for(unsigned i=0; i<fields.size(); i++) {
125 1628 : s.push_back(fields[i].name);
126 : }
127 : return *this;
128 : }
129 :
130 1149366 : bool IFile::FieldExist(const std::string& s) {
131 1149366 : if(!inMiddleOfField) {
132 526 : advanceField();
133 : }
134 : // using explicit conversion not to confuse cppcheck 1.86
135 1149366 : if(!bool(*this)) {
136 : return false;
137 : }
138 : // compare the names directly, without copying the list of the fields
139 9168458 : for(const auto & f : fields) {
140 8047327 : if(f.name==s) {
141 : return true;
142 : }
143 : }
144 : return false;
145 : }
146 :
147 17355820 : IFile& IFile::scanField(const std::string&name,std::string&str) {
148 17355820 : if(!inMiddleOfField) {
149 1645063 : advanceField();
150 : }
151 : // using explicit conversion not to confuse cppcheck 1.86
152 17355819 : if(!bool(*this)) {
153 : return *this;
154 : }
155 17349199 : unsigned i=findField(name);
156 17349199 : str=fields[i].value;
157 17349199 : fields[i].read=true;
158 17349199 : return *this;
159 : }
160 :
161 7892380 : IFile& IFile::scanField(const std::string&name,double &x) {
162 : std::string str;
163 7892380 : scanField(name,str);
164 7892379 : if(*this) {
165 7885829 : Tools::convert(str,x);
166 : }
167 7892379 : return *this;
168 : }
169 :
170 2577335 : IFile& IFile::scanField(const std::string&name,int &x) {
171 : std::string str;
172 2577335 : scanField(name,str);
173 2577335 : if(*this) {
174 2577284 : Tools::convert(str,x);
175 : }
176 2577335 : return *this;
177 : }
178 :
179 1 : IFile& IFile::scanField(const std::string&name,long int &x) {
180 : std::string str;
181 1 : scanField(name,str);
182 1 : if(*this) {
183 1 : Tools::convert(str,x);
184 : }
185 1 : return *this;
186 : }
187 :
188 0 : IFile& IFile::scanField(const std::string&name,long long int &x) {
189 : std::string str;
190 0 : scanField(name,str);
191 0 : if(*this) {
192 0 : Tools::convert(str,x);
193 : }
194 0 : return *this;
195 : }
196 :
197 3257 : IFile& IFile::scanField(const std::string&name,unsigned &x) {
198 : std::string str;
199 3257 : scanField(name,str);
200 3257 : if(*this) {
201 3251 : Tools::convert(str,x);
202 : }
203 3257 : return *this;
204 : }
205 :
206 1 : IFile& IFile::scanField(const std::string&name,long unsigned &x) {
207 : std::string str;
208 1 : scanField(name,str);
209 1 : if(*this) {
210 1 : Tools::convert(str,x);
211 : }
212 1 : return *this;
213 : }
214 :
215 47 : IFile& IFile::scanField(const std::string&name,long long unsigned &x) {
216 : std::string str;
217 47 : scanField(name,str);
218 47 : if(*this) {
219 47 : Tools::convert(str,x);
220 : }
221 47 : return *this;
222 : }
223 :
224 1107419 : IFile& IFile::scanField(Value* val) {
225 1107419 : double ff=std::numeric_limits<double>::quiet_NaN(); // this is to be sure a NaN value is replaced upon failure
226 1107419 : scanField( val->getName(), ff );
227 1107419 : val->set( ff );
228 2214838 : if( FieldExist("min_" + val->getName() ) ) {
229 : std::string min, max;
230 6575 : scanField("min_" + val->getName(), min );
231 6575 : scanField("max_" + val->getName(), max );
232 6575 : val->setDomain( min, max );
233 : } else {
234 1100844 : val->setNotPeriodic();
235 : }
236 1107419 : return *this;
237 : }
238 :
239 1639015 : IFile& IFile::scanField() {
240 1639015 : if(!ignoreFields) {
241 17161425 : for(unsigned i=0; i<fields.size(); i++) {
242 15737367 : plumed_massert(fields[i].read,"field "+fields[i].name+" was not read: all the fields need to be read otherwise you could miss important infos" );
243 : }
244 : }
245 1639015 : inMiddleOfField=false;
246 1639015 : return *this;
247 : }
248 :
249 7749 : IFile::IFile():
250 7749 : inMiddleOfField(false),
251 7749 : ignoreFields(false),
252 7749 : noEOL(false) {
253 7749 : }
254 :
255 8354 : IFile::~IFile() {
256 7745 : if(inMiddleOfField) {
257 25 : std::cerr<<"WARNING: IFile closed in the middle of reading. seems strange!\n";
258 : }
259 8354 : }
260 :
261 1813506 : IFile& IFile::getline(std::string &str) {
262 : str.clear();
263 : // Lines are read in chunks with fgets/gzgets: reading one character at a time
264 : // (as done until v2.10) was about 30 times slower on large files.
265 : constexpr int chunk=4096;
266 : char buffer[chunk];
267 : // number of characters taken from the file for this line,
268 : // used to go back to the beginning of an incomplete line
269 : long consumed=0;
270 : bool newline=false;
271 : while(true) {
272 : char* r=nullptr;
273 1813560 : if(gzfp) {
274 : #ifdef __PLUMED_HAS_ZLIB
275 48 : r=gzgets(gzFile(gzfp),buffer,chunk);
276 : #else
277 : plumed_merror("file " + getPath() + ": trying to use a gz file without zlib being linked");
278 : #endif
279 : } else {
280 1813512 : r=std::fgets(buffer,chunk,fp);
281 : }
282 1813560 : if(!r) {
283 : break;
284 : }
285 1805422 : const std::size_t len=std::strlen(buffer);
286 1805422 : consumed+=len;
287 1805422 : if(len>0 && buffer[len-1]=='\n') {
288 1805368 : str.append(buffer,len-1);
289 : newline=true;
290 : break;
291 : }
292 54 : str.append(buffer,len);
293 : }
294 1813506 : if(!newline) {
295 8138 : if(gzfp) {
296 : #ifdef __PLUMED_HAS_ZLIB
297 7 : int errnum=0;
298 7 : gzerror(gzFile(gzfp),&errnum);
299 7 : if(errnum!=Z_OK && errnum!=Z_BUF_ERROR) {
300 0 : err=true;
301 : } else {
302 7 : eof=true;
303 : }
304 : #endif
305 : } else {
306 8131 : if(std::ferror(fp)) {
307 0 : err=true;
308 : } else {
309 8131 : eof=true;
310 : }
311 : }
312 : }
313 1805368 : if(newline && !str.empty() && str.back()=='\r') {
314 55 : str.pop_back();
315 : }
316 1813506 : plumed_massert(str.find('\r')==std::string::npos,"plumed only accepts \\n (unix) or \\r\\n (dos) new lines");
317 1813506 : if(eof && noEOL) {
318 1378 : if(str.length()>0) {
319 4 : eof=false;
320 : }
321 1812128 : } else if(eof || err) {
322 6760 : eof = true;
323 : str.clear();
324 : // Go back to the beginning of the incomplete line, so that it can be read
325 : // again when it is complete (e.g. a file that is being written by another walker
326 : // and is read again after a reset(false)).
327 : // This is also necessary so that eof works properly for emacs files
328 : // with no endline at end of file.
329 6760 : if(!err && consumed>0) {
330 24 : if(gzfp) {
331 : #ifdef __PLUMED_HAS_ZLIB
332 0 : gzclearerr(gzFile(gzfp));
333 0 : gzseek(gzFile(gzfp),-consumed,SEEK_CUR);
334 : #endif
335 : } else {
336 24 : std::fseek(fp,-consumed,SEEK_CUR);
337 : }
338 : }
339 : }
340 1813506 : return *this;
341 : }
342 :
343 17349199 : unsigned IFile::findField(const std::string&name)const {
344 : unsigned i;
345 111499370 : for(i=0; i<fields.size(); i++)
346 111499370 : if(fields[i].name==name) {
347 : break;
348 : }
349 17349199 : if(i>=fields.size()) {
350 0 : plumed_merror("file " + getPath() + ": field " + name + " cannot be found");
351 : }
352 17349199 : return i;
353 : }
354 :
355 6410 : void IFile::reset(bool reset) {
356 6410 : eof = reset;
357 6410 : err = reset;
358 6410 : if(!reset && fp) {
359 6300 : clearerr(fp);
360 : }
361 : #ifdef __PLUMED_HAS_ZLIB
362 6410 : if(!reset && gzfp) {
363 3 : gzclearerr(gzFile(gzfp));
364 : }
365 : #endif
366 6410 : return;
367 : }
368 :
369 6381 : void IFile::allowIgnoredFields() {
370 6381 : ignoreFields=true;
371 6381 : }
372 :
373 1451 : void IFile::allowNoEOL() {
374 1451 : noEOL=true;
375 1451 : }
376 :
377 : }
|