LCOV - code coverage report
Current view: top level - tools - IFile.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 170 180 94.4 %
Date: 2026-11-05 10:19:04 Functions: 21 24 87.5 %

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

Generated by: LCOV version 1.16