00001 // -*- c++ -*- 00002 00003 // This file is part of the Collective Variables module (Colvars). 00004 // The original version of Colvars and its updates are located at: 00005 // https://github.com/Colvars/colvars 00006 // Please update all Colvars source files before making any changes. 00007 // If you wish to distribute your changes, please submit them to the 00008 // Colvars repository at GitHub. 00009 00010 // Using access() to check if a file exists (until we can assume C++14/17) 00011 #if !defined(_WIN32) || defined(__CYGWIN__) 00012 #include <unistd.h> 00013 #endif 00014 #if defined(_WIN32) 00015 #include <io.h> 00016 #endif 00017 00018 #include <cerrno> 00019 #include <cstdio> 00020 00021 #include <list> 00022 #include <map> 00023 #include <sstream> 00024 #include <fstream> 00025 00026 #include "colvarmodule.h" 00027 #include "colvarproxy_io.h" 00028 00029 00030 colvarproxy_io::colvarproxy_io() 00031 { 00032 input_buffer_ = NULL; 00033 restart_frequency_engine = 0; 00034 input_stream_error_ = new std::istringstream(); 00035 input_stream_error_->setstate(std::ios::badbit); 00036 output_stream_error_ = new std::ostringstream(); 00037 output_stream_error_->setstate(std::ios::badbit); 00038 } 00039 00040 00041 colvarproxy_io::~colvarproxy_io() 00042 { 00043 delete input_stream_error_; 00044 close_input_streams(); 00045 delete output_stream_error_; 00046 close_output_streams(); 00047 } 00048 00049 00050 bool colvarproxy_io::io_available() 00051 { 00052 return false; 00053 } 00054 00055 00056 int colvarproxy_io::get_frame(long int&) 00057 { 00058 return COLVARS_NOT_IMPLEMENTED; 00059 } 00060 00061 00062 int colvarproxy_io::set_frame(long int) 00063 { 00064 return COLVARS_NOT_IMPLEMENTED; 00065 } 00066 00067 00068 int colvarproxy_io::backup_file(char const *filename) 00069 { 00070 // Simplified version of NAMD_file_exists() 00071 int exit_code; 00072 do { 00073 #if defined(_WIN32) && !defined(__CYGWIN__) 00074 // We could use _access_s here, but it is probably too new 00075 exit_code = _access(filename, 00); 00076 #else 00077 exit_code = access(filename, F_OK); 00078 #endif 00079 } while ((exit_code != 0) && (errno == EINTR)); 00080 if (exit_code != 0) { 00081 if (errno == ENOENT) { 00082 // File does not exist 00083 return COLVARS_OK; 00084 } else { 00085 return cvm::error("Unknown error while checking if file \""+ 00086 std::string(filename)+"\" exists.\n", COLVARS_ERROR); 00087 } 00088 } 00089 00090 // The file exists, then rename it 00091 if (std::string(filename).rfind(std::string(".colvars.state")) != 00092 std::string::npos) { 00093 return rename_file(filename, (std::string(filename)+".old").c_str()); 00094 } else { 00095 return rename_file(filename, (std::string(filename)+".BAK").c_str()); 00096 } 00097 } 00098 00099 00100 int colvarproxy_io::remove_file(char const *filename) 00101 { 00102 int error_code = COLVARS_OK; 00103 #if defined(_WIN32) && !defined(__CYGWIN__) 00104 // Because the file may be open by other processes, rename it to filename.old 00105 std::string const renamed_file(std::string(filename)+".old"); 00106 // It may still be there from an interrupted run, so remove it to be safe 00107 std::remove(renamed_file.c_str()); 00108 int rename_exit_code = 0; 00109 while ((rename_exit_code = std::rename(filename, 00110 renamed_file.c_str())) != 0) { 00111 if (errno == EINTR) continue; 00112 error_code |= COLVARS_FILE_ERROR; 00113 break; 00114 } 00115 // Ask to remove filename.old, but ignore any errors raised 00116 std::remove(renamed_file.c_str()); 00117 #else 00118 if (std::remove(filename)) { 00119 if (errno != ENOENT) { 00120 error_code |= COLVARS_FILE_ERROR; 00121 } 00122 } 00123 #endif 00124 if (error_code != COLVARS_OK) { 00125 return cvm::error("Error: in removing file \""+std::string(filename)+ 00126 "\".\n.", 00127 error_code); 00128 } 00129 return COLVARS_OK; 00130 } 00131 00132 00133 int colvarproxy_io::rename_file(char const *filename, char const *newfilename) 00134 { 00135 int error_code = COLVARS_OK; 00136 #if defined(_WIN32) && !defined(__CYGWIN__) 00137 // On straight Windows, must remove the destination before renaming it 00138 error_code |= remove_file(newfilename); 00139 #endif 00140 int rename_exit_code = 0; 00141 while ((rename_exit_code = std::rename(filename, newfilename)) != 0) { 00142 if (errno == EINTR) continue; 00143 // Call log() instead of error to allow the next try 00144 cvm::log("Error: in renaming file \""+std::string(filename)+"\" to \""+ 00145 std::string(newfilename)+"\".\n."); 00146 error_code |= COLVARS_FILE_ERROR; 00147 if (errno == EXDEV) continue; 00148 break; 00149 } 00150 return rename_exit_code ? error_code : COLVARS_OK; 00151 } 00152 00153 00154 std::istream &colvarproxy_io::input_stream(std::string const &input_name, 00155 std::string const description, 00156 bool error_on_fail) 00157 { 00158 if (!io_available()) { 00159 cvm::error("Error: trying to access an input file/channel " 00160 "from the wrong thread.\n", COLVARS_BUG_ERROR); 00161 return *input_stream_error_; 00162 } 00163 00164 if (colvarproxy_io::input_stream_exists(input_name)) { 00165 return *(input_streams_[input_name]); 00166 } 00167 00168 // Using binary to work around differences in line termination conventions 00169 // See https://github.com/Colvars/colvars/commit/8236879f7de4 00170 input_streams_[input_name] = new std::ifstream(input_name.c_str(), 00171 std::ios::binary); 00172 00173 if (input_streams_[input_name]->fail() && error_on_fail) { 00174 cvm::error("Error: cannot open "+description+" \""+input_name+"\".\n", 00175 COLVARS_FILE_ERROR); 00176 } 00177 00178 return *(input_streams_[input_name]); 00179 } 00180 00181 00182 bool colvarproxy_io::input_stream_exists(std::string const &input_name) 00183 { 00184 return (input_streams_.count(input_name) > 0); 00185 } 00186 00187 00188 int colvarproxy_io::close_input_stream(std::string const &input_name) 00189 { 00190 if (colvarproxy_io::input_stream_exists(input_name)) { 00191 delete input_streams_[input_name]; 00192 input_streams_.erase(input_name); 00193 return COLVARS_OK; 00194 } 00195 return cvm::error("Error: input file/channel \""+input_name+ 00196 "\" does not exist.\n", COLVARS_FILE_ERROR); 00197 } 00198 00199 00200 int colvarproxy_io::close_input_streams() 00201 { 00202 for (std::map<std::string, std::istream *>::iterator ii = input_streams_.begin(); 00203 ii != input_streams_.end(); 00204 ii++) { 00205 delete ii->second; 00206 } 00207 input_streams_.clear(); 00208 return COLVARS_OK; 00209 } 00210 00211 00212 std::ostream & colvarproxy_io::output_stream(std::string const &output_name, 00213 std::string const description) 00214 { 00215 if (cvm::debug()) { 00216 cvm::log("Using colvarproxy_io::output_stream()\n"); 00217 } 00218 00219 if (!io_available()) { 00220 cvm::error("Error: trying to access an output file/channel " 00221 "from the wrong thread.\n", COLVARS_BUG_ERROR); 00222 return *output_stream_error_; 00223 } 00224 00225 if (colvarproxy_io::output_stream_exists(output_name)) { 00226 return *(output_streams_[output_name]); 00227 } 00228 00229 backup_file(output_name.c_str()); 00230 00231 output_streams_[output_name] = new std::ofstream(output_name.c_str()); 00232 if (!*(output_streams_[output_name])) { 00233 cvm::error("Error: cannot write to "+description+" \""+output_name+"\".\n", 00234 COLVARS_FILE_ERROR); 00235 } 00236 00237 return *(output_streams_[output_name]); 00238 } 00239 00240 00241 bool colvarproxy_io::output_stream_exists(std::string const &output_name) 00242 { 00243 return (output_streams_.count(output_name) > 0); 00244 } 00245 00246 00247 int colvarproxy_io::flush_output_stream(std::string const &output_name) 00248 { 00249 if (!io_available()) { 00250 // No-op 00251 return COLVARS_OK; 00252 } 00253 00254 if (colvarproxy_io::output_stream_exists(output_name)) { 00255 (dynamic_cast<std::ofstream *>(output_streams_[output_name]))->flush(); 00256 return COLVARS_OK; 00257 } 00258 00259 return COLVARS_OK; 00260 } 00261 00262 00263 int colvarproxy_io::flush_output_streams() 00264 { 00265 if (!io_available()) { 00266 return COLVARS_OK; 00267 } 00268 00269 for (std::map<std::string, std::ostream *>::iterator osi = output_streams_.begin(); 00270 osi != output_streams_.end(); 00271 osi++) { 00272 (dynamic_cast<std::ofstream *>(osi->second))->flush(); 00273 } 00274 00275 return COLVARS_OK; 00276 } 00277 00278 00279 int colvarproxy_io::close_output_stream(std::string const &output_name) 00280 { 00281 if (!io_available()) { 00282 return cvm::error("Error: trying to access an output file/channel " 00283 "from the wrong thread.\n", COLVARS_BUG_ERROR); 00284 } 00285 00286 if (colvarproxy_io::output_stream_exists(output_name)) { 00287 (dynamic_cast<std::ofstream *>(output_streams_[output_name]))->close(); 00288 delete output_streams_[output_name]; 00289 output_streams_.erase(output_name); 00290 } 00291 00292 return COLVARS_OK; 00293 } 00294 00295 00296 int colvarproxy_io::close_output_streams() 00297 { 00298 if (! io_available()) { 00299 return COLVARS_OK; 00300 } 00301 00302 for (std::map<std::string, std::ostream *>::iterator osi = output_streams_.begin(); 00303 osi != output_streams_.end(); 00304 osi++) { 00305 (dynamic_cast<std::ofstream *>(osi->second))->close(); 00306 } 00307 output_streams_.clear(); 00308 00309 return COLVARS_OK; 00310 }