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