1 /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 Copyright (c) 2012-2021 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 "Communicator.h"
28 #include "Tools.h"
29 #include <cstdarg>
30 #include <cstring>
31 #include <cmath>
32
33 #include <iostream>
34 #include <string>
35 #ifdef __PLUMED_HAS_ZLIB
36 #include <zlib.h>
37 #endif
38
39 namespace PLMD {
40
llread(char * ptr,size_t s)41 size_t IFile::llread(char*ptr,size_t s) {
42 plumed_assert(fp);
43 size_t r;
44 if(gzfp) {
45 #ifdef __PLUMED_HAS_ZLIB
46 int rr=gzread(gzFile(gzfp),ptr,s);
47 if(rr==0) eof=true;
48 if(rr<0) err=true;
49 r=rr;
50 #else
51 plumed_merror("file " + getPath() + ": trying to use a gz file without zlib being linked");
52 #endif
53 } else {
54 r=fread(ptr,1,s,fp);
55 if(feof(fp)) eof=true;
56 if(ferror(fp)) err=true;
57 }
58 return r;
59 }
60
advanceField()61 IFile& IFile::advanceField() {
62 plumed_assert(!inMiddleOfField);
63 std::string line;
64 bool done=false;
65 while(!done) {
66 getline(line);
67 // using explicit conversion not to confuse cppcheck 1.86
68 if(!bool(*this)) {return *this;}
69 std::vector<std::string> words=Tools::getWords(line);
70 if(words.size()>=2 && words[0]=="#!" && words[1]=="FIELDS") {
71 fields.clear();
72 for(unsigned i=2; i<words.size(); i++) {
73 Field field;
74 field.name=words[i];
75 fields.push_back(field);
76 }
77 } else if(words.size()==4 && words[0]=="#!" && words[1]=="SET") {
78 Field field;
79 field.name=words[2];
80 field.value=words[3];
81 field.constant=true;
82 fields.push_back(field);
83 } else {
84 unsigned nf=0;
85 for(unsigned i=0; i<fields.size(); i++) if(!fields[i].constant) nf++;
86 Tools::trimComments(line);
87 words=Tools::getWords(line);
88 if( words.size()==nf ) {
89 unsigned j=0;
90 for(unsigned i=0; i<fields.size(); i++) {
91 if(fields[i].constant) continue;
92 fields[i].value=words[j];
93 fields[i].read=false;
94 j++;
95 }
96 done=true;
97 } else if( !words.empty() ) {
98 plumed_merror("file " + getPath() + ": mismatch between number of fields in file and expected number");
99 }
100 }
101 }
102 inMiddleOfField=true;
103 return *this;
104 }
105
open(const std::string & path)106 IFile& IFile::open(const std::string&path) {
107 plumed_massert(!cloned,"file "+path+" appears to be cloned");
108 eof=false;
109 err=false;
110 fp=NULL;
111 gzfp=NULL;
112 bool do_exist=FileExist(path);
113 plumed_massert(do_exist,"file " + path + " cannot be found");
114 fp=std::fopen(const_cast<char*>(this->path.c_str()),"r");
115 if(Tools::extension(this->path)=="gz") {
116 #ifdef __PLUMED_HAS_ZLIB
117 gzfp=(void*)gzopen(const_cast<char*>(this->path.c_str()),"r");
118 #else
119 plumed_merror("file " + getPath() + ": trying to use a gz file without zlib being linked");
120 #endif
121 }
122 if(plumed) plumed->insertFile(*this);
123 return *this;
124 }
125
scanFieldList(std::vector<std::string> & s)126 IFile& IFile::scanFieldList(std::vector<std::string>&s) {
127 if(!inMiddleOfField) advanceField();
128 // using explicit conversion not to confuse cppcheck 1.86
129 if(!bool(*this)) return *this;
130 s.clear();
131 for(unsigned i=0; i<fields.size(); i++)
132 s.push_back(fields[i].name);
133 return *this;
134 }
135
FieldExist(const std::string & s)136 bool IFile::FieldExist(const std::string& s) {
137 std::vector<std::string> slist;
138 scanFieldList(slist);
139 int mycount = (int) std::count(slist.begin(), slist.end(), s);
140 if(mycount>0) return true;
141 else return false;
142 }
143
scanField(const std::string & name,std::string & str)144 IFile& IFile::scanField(const std::string&name,std::string&str) {
145 if(!inMiddleOfField) advanceField();
146 // using explicit conversion not to confuse cppcheck 1.86
147 if(!bool(*this)) return *this;
148 unsigned i=findField(name);
149 str=fields[i].value;
150 fields[i].read=true;
151 return *this;
152 }
153
scanField(const std::string & name,double & x)154 IFile& IFile::scanField(const std::string&name,double &x) {
155 std::string str;
156 scanField(name,str);
157 if(*this) Tools::convert(str,x);
158 return *this;
159 }
160
scanField(const std::string & name,int & x)161 IFile& IFile::scanField(const std::string&name,int &x) {
162 std::string str;
163 scanField(name,str);
164 if(*this) Tools::convert(str,x);
165 return *this;
166 }
167
scanField(const std::string & name,long int & x)168 IFile& IFile::scanField(const std::string&name,long int &x) {
169 std::string str;
170 scanField(name,str);
171 if(*this) Tools::convert(str,x);
172 return *this;
173 }
174
scanField(const std::string & name,unsigned & x)175 IFile& IFile::scanField(const std::string&name,unsigned &x) {
176 std::string str;
177 scanField(name,str);
178 if(*this) Tools::convert(str,x);
179 return *this;
180 }
181
scanField(const std::string & name,long unsigned & x)182 IFile& IFile::scanField(const std::string&name,long unsigned &x) {
183 std::string str;
184 scanField(name,str);
185 if(*this) Tools::convert(str,x);
186 return *this;
187 }
188
scanField(Value * val)189 IFile& IFile::scanField(Value* val) {
190 double ff=std::numeric_limits<double>::quiet_NaN(); // this is to be sure a NaN value is replaced upon failure
191 scanField( val->getName(), ff );
192 val->set( ff );
193 if( FieldExist("min_" + val->getName() ) ) {
194 std::string min, max;
195 scanField("min_" + val->getName(), min );
196 scanField("max_" + val->getName(), max );
197 val->setDomain( min, max );
198 } else {
199 val->setNotPeriodic();
200 }
201 return *this;
202 }
203
scanField()204 IFile& IFile::scanField() {
205 if(!ignoreFields) {
206 for(unsigned i=0; i<fields.size(); i++) {
207 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" );
208 }
209 }
210 inMiddleOfField=false;
211 return *this;
212 }
213
IFile()214 IFile::IFile():
215 inMiddleOfField(false),
216 ignoreFields(false),
217 noEOL(false)
218 {
219 }
220
~IFile()221 IFile::~IFile() {
222 if(inMiddleOfField) std::cerr<<"WARNING: IFile closed in the middle of reading. seems strange!\n";
223 }
224
getline(std::string & str)225 IFile& IFile::getline(std::string &str) {
226 char tmp=0;
227 str="";
228 fpos_t pos;
229 fgetpos(fp,&pos);
230 while(llread(&tmp,1)==1 && tmp && tmp!='\n' && tmp!='\r' && !eof && !err) {
231 str+=tmp;
232 }
233 if(tmp=='\r') {
234 llread(&tmp,1);
235 plumed_massert(tmp=='\n',"plumed only accepts \\n (unix) or \\r\\n (dos) new lines");
236 }
237 if(eof && noEOL) {
238 if(str.length()>0) eof=false;
239 } else if(eof || err || tmp!='\n') {
240 eof = true;
241 str="";
242 if(!err) fsetpos(fp,&pos);
243 // there was a fsetpos here that apparently is not necessary
244 // fsetpos(fp,&pos);
245 // I think it was necessary to have rewind working correctly
246 // after end of file. Since rewind is not used now anywhere,
247 // it should be ok not to reset position.
248 // This is necessary so that eof works properly for emacs files
249 // with no endline at end of file.
250 }
251 return *this;
252 }
253
findField(const std::string & name) const254 unsigned IFile::findField(const std::string&name)const {
255 unsigned i;
256 for(i=0; i<fields.size(); i++) if(fields[i].name==name) break;
257 if(i>=fields.size()) {
258 plumed_merror("file " + getPath() + ": field " + name + " cannot be found");
259 }
260 return i;
261 }
262
reset(bool reset)263 void IFile::reset(bool reset) {
264 eof = reset;
265 err = reset;
266 if(!reset && fp) clearerr(fp);
267 #ifdef __PLUMED_HAS_ZLIB
268 if(!reset && gzfp) gzclearerr(gzFile(gzfp));
269 #endif
270 return;
271 }
272
allowIgnoredFields()273 void IFile::allowIgnoredFields() {
274 ignoreFields=true;
275 }
276
allowNoEOL()277 void IFile::allowNoEOL() {
278 noEOL=true;
279 }
280
281 }
282