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 #include <sstream> 00011 #include <iostream> 00012 #include <algorithm> 00013 00014 #include "colvarmodule.h" 00015 #include "colvarvalue.h" 00016 #include "colvarparse.h" 00017 00018 00019 // space & tab 00020 char const * const colvarparse::white_space = " \t"; 00021 00022 00023 namespace { 00024 00025 // Avoid having to put the bool assignment in the template :-( 00026 void set_bool(void *p, bool val) 00027 { 00028 bool *v = reinterpret_cast<bool *>(p); 00029 *v = val; 00030 } 00031 00032 } 00033 00034 00035 colvarparse::colvarparse() 00036 : keyword_delimiters_left("\n"+std::string(white_space)+"}"), 00037 keyword_delimiters_right("\n"+std::string(white_space)+"{") 00038 { 00039 colvarparse::clear(); 00040 } 00041 00042 00043 void colvarparse::clear() 00044 { 00045 config_string.clear(); 00046 clear_keyword_registry(); 00047 } 00048 00049 00050 colvarparse::colvarparse(const std::string& conf) 00051 : keyword_delimiters_left("\n"+std::string(white_space)+"}"), 00052 keyword_delimiters_right("\n"+std::string(white_space)+"{") 00053 { 00054 colvarparse::set_string(conf); 00055 } 00056 00057 00058 void colvarparse::set_string(std::string const &conf) 00059 { 00060 if (! config_string.size()) { 00061 colvarparse::clear(); 00062 config_string = conf; 00063 } 00064 } 00065 00066 00067 colvarparse::~colvarparse() 00068 { 00069 colvarparse::clear(); 00070 } 00071 00072 00073 00074 bool colvarparse::get_key_string_value(std::string const &conf, 00075 char const *key, std::string &data) 00076 { 00077 bool b_found = false, b_found_any = false; 00078 size_t save_pos = 0, found_count = 0; 00079 00080 do { 00081 std::string data_this = ""; 00082 b_found = key_lookup(conf, key, &data_this, &save_pos); 00083 if (b_found) { 00084 if (!b_found_any) 00085 b_found_any = true; 00086 found_count++; 00087 data = data_this; 00088 } 00089 } while (b_found); 00090 00091 if (found_count > 1) { 00092 cvm::error("Error: found more than one instance of \""+ 00093 std::string(key)+"\".\n", COLVARS_INPUT_ERROR); 00094 } 00095 00096 return b_found_any; 00097 } 00098 00099 bool colvarparse::get_key_string_multi_value(std::string const &conf, 00100 char const *key, std::vector<std::string>& data) 00101 { 00102 bool b_found = false, b_found_any = false; 00103 size_t save_pos = 0, found_count = 0; 00104 00105 data.clear(); 00106 00107 do { 00108 std::string data_this = ""; 00109 b_found = key_lookup(conf, key, &data_this, &save_pos); 00110 if (b_found) { 00111 if (!b_found_any) 00112 b_found_any = true; 00113 found_count++; 00114 data.push_back(data_this); 00115 } 00116 } while (b_found); 00117 00118 return b_found_any; 00119 } 00120 00121 00122 template<typename TYPE> 00123 void colvarparse::mark_key_set_user(std::string const &key_str, 00124 TYPE const &value, 00125 Parse_Mode const &parse_mode) 00126 { 00127 key_set_modes[to_lower_cppstr(key_str)] = key_set_user; 00128 if (parse_mode & parse_echo) { 00129 cvm::log("# "+key_str+" = "+cvm::to_str(value)+"\n", 00130 cvm::log_user_params()); 00131 } 00132 if (parse_mode & parse_deprecation_warning) { 00133 cvm::log("Warning: keyword "+key_str+ 00134 " is deprecated. Check the documentation for the current equivalent.\n"); 00135 } 00136 } 00137 00138 00139 template<typename TYPE> 00140 void colvarparse::mark_key_set_default(std::string const &key_str, 00141 TYPE const &def_value, 00142 Parse_Mode const &parse_mode) 00143 { 00144 key_set_modes[to_lower_cppstr(key_str)] = key_set_default; 00145 if (parse_mode & parse_echo_default) { 00146 cvm::log("# "+key_str+" = "+cvm::to_str(def_value)+ 00147 " [default]\n", cvm::log_default_params()); 00148 } 00149 } 00150 00151 00152 void colvarparse::error_key_required(std::string const &key_str, 00153 Parse_Mode const &parse_mode) 00154 { 00155 if (key_already_set(key_str)) { 00156 return; 00157 } 00158 if (parse_mode & parse_restart) { 00159 cvm::error("Error: keyword \""+key_str+ 00160 "\" is missing from the restart.\n", COLVARS_INPUT_ERROR); 00161 } else { 00162 cvm::error("Error: keyword \""+key_str+ 00163 "\" is required.\n", COLVARS_INPUT_ERROR); 00164 } 00165 } 00166 00167 00168 template<typename TYPE> 00169 int colvarparse::_get_keyval_scalar_value_(std::string const &key_str, 00170 std::string const &data, 00171 TYPE &value, 00172 TYPE const &def_value) 00173 { 00174 std::istringstream is(data); 00175 size_t value_count = 0; 00176 TYPE x(def_value); 00177 00178 while (is >> x) { 00179 value = x; 00180 value_count++; 00181 } 00182 00183 if (value_count == 0) { 00184 return cvm::error("Error: in parsing \""+ 00185 key_str+"\".\n", COLVARS_INPUT_ERROR); 00186 } 00187 00188 if (value_count > 1) { 00189 return cvm::error("Error: multiple values " 00190 "are not allowed for keyword \""+ 00191 key_str+"\".\n", COLVARS_INPUT_ERROR); 00192 } 00193 00194 return COLVARS_OK; 00195 } 00196 00197 00198 template<> 00199 int colvarparse::_get_keyval_scalar_value_(std::string const &key_str, 00200 std::string const &data, 00201 bool &value, 00202 bool const & /* def_value */) 00203 { 00204 if ( (data == std::string("on")) || 00205 (data == std::string("yes")) || 00206 (data == std::string("true")) ) { 00207 set_bool(reinterpret_cast<void *>(&value), true); 00208 } else if ( (data == std::string("off")) || 00209 (data == std::string("no")) || 00210 (data == std::string("false")) ) { 00211 set_bool(reinterpret_cast<void *>(&value), false); 00212 } else { 00213 return cvm::error("Error: boolean values only are allowed " 00214 "for \""+key_str+"\".\n", COLVARS_INPUT_ERROR); 00215 } 00216 return COLVARS_OK; 00217 } 00218 00219 00220 template<typename TYPE> 00221 int colvarparse::_get_keyval_scalar_novalue_(std::string const &key_str, 00222 TYPE & /* value */, 00223 Parse_Mode const & /* parse_mode */) 00224 { 00225 return cvm::error("Error: improper or missing value " 00226 "for \""+key_str+"\".\n", COLVARS_INPUT_ERROR); 00227 } 00228 00229 template<> 00230 int colvarparse::_get_keyval_scalar_novalue_(std::string const &key_str, 00231 bool &value, 00232 Parse_Mode const &parse_mode) 00233 { 00234 set_bool(reinterpret_cast<void *>(&value), true); 00235 mark_key_set_user<bool>(key_str, value, parse_mode); 00236 return COLVARS_OK; 00237 } 00238 00239 00240 template<typename TYPE> 00241 bool colvarparse::_get_keyval_scalar_(std::string const &conf, 00242 char const *key, 00243 TYPE &value, 00244 TYPE const &def_value, 00245 Parse_Mode const &parse_mode) 00246 { 00247 std::string const key_str(key); 00248 00249 std::string data; 00250 bool const b_found_any = get_key_string_value(conf, key, data); 00251 00252 if (data.size()) { 00253 00254 _get_keyval_scalar_value_<TYPE>(key_str, data, value, def_value); 00255 00256 mark_key_set_user<TYPE>(key_str, value, parse_mode); 00257 00258 } else { // No string value 00259 00260 if (b_found_any) { 00261 00262 _get_keyval_scalar_novalue_<TYPE>(key_str, value, parse_mode); 00263 00264 } else { 00265 00266 if (parse_mode & parse_required) { 00267 if (cvm::debug()) { 00268 cvm::log("get_keyval, parse_required = "+cvm::to_str(parse_mode & parse_required)+ 00269 "\n"); 00270 } 00271 error_key_required(key_str, parse_mode); 00272 return false; 00273 } 00274 00275 if ( (parse_mode & parse_override) || !(key_already_set(key)) ) { 00276 value = def_value; 00277 mark_key_set_default<TYPE>(key_str, value, parse_mode); 00278 } 00279 } 00280 } 00281 00282 return b_found_any; 00283 } 00284 00285 00286 template<typename TYPE> 00287 bool colvarparse::_get_keyval_vector_(std::string const &conf, 00288 char const *key, 00289 std::vector<TYPE> &values, 00290 std::vector<TYPE> const &def_values, 00291 Parse_Mode const &parse_mode) 00292 { 00293 std::string const key_str(key); 00294 00295 std::string data; 00296 bool const b_found_any = get_key_string_value(conf, key, data); 00297 00298 if (data.size()) { 00299 std::istringstream is(data); 00300 00301 if (values.size() == 0) { 00302 00303 std::vector<TYPE> x; 00304 if (def_values.size()) { 00305 x = def_values; 00306 } else { 00307 x.assign(1, TYPE()); 00308 } 00309 00310 for (size_t i = 0; 00311 ( is >> x[ ((i<x.size()) ? i : x.size()-1) ] ); 00312 i++) { 00313 values.push_back(x[ ((i<x.size()) ? i : x.size()-1) ]); 00314 } 00315 00316 } else { 00317 00318 size_t i = 0; 00319 for ( ; i < values.size(); i++) { 00320 TYPE x(values[i]); 00321 if (is >> x) { 00322 values[i] = x; 00323 } else { 00324 cvm::error("Error: in parsing \""+ 00325 key_str+"\".\n", COLVARS_INPUT_ERROR); 00326 } 00327 } 00328 } 00329 00330 mark_key_set_user< std::vector<TYPE> >(key_str, values, parse_mode); 00331 00332 } else { 00333 00334 if (b_found_any) { 00335 cvm::error("Error: improper or missing values for \""+ 00336 key_str+"\".\n", COLVARS_INPUT_ERROR); 00337 } else { 00338 00339 if ((values.size() > 0) && (values.size() != def_values.size())) { 00340 cvm::error("Error: the number of default values for \""+ 00341 key_str+"\" is different from the number of " 00342 "current values.\n", COLVARS_BUG_ERROR); 00343 } 00344 00345 if (parse_mode & parse_required) { 00346 error_key_required(key_str, parse_mode); 00347 return false; 00348 } 00349 00350 if ( (parse_mode & parse_override) || !(key_already_set(key)) ) { 00351 for (size_t i = 0; i < values.size(); i++) { 00352 values[i] = def_values[i]; 00353 } 00354 mark_key_set_default< std::vector<TYPE> >(key_str, def_values, 00355 parse_mode); 00356 } 00357 00358 } 00359 } 00360 00361 return b_found_any; 00362 } 00363 00364 00365 // single-value keyword parsers 00366 00367 00368 bool colvarparse::get_keyval(std::string const &conf, 00369 char const *key, 00370 int &value, 00371 int const &def_value, 00372 Parse_Mode const parse_mode) 00373 { 00374 return _get_keyval_scalar_<int>(conf, key, value, def_value, parse_mode); 00375 } 00376 00377 bool colvarparse::get_keyval(std::string const &conf, 00378 char const *key, 00379 size_t &value, 00380 size_t const &def_value, 00381 Parse_Mode const parse_mode) 00382 { 00383 return _get_keyval_scalar_<size_t>(conf, key, value, def_value, parse_mode); 00384 } 00385 00386 bool colvarparse::get_keyval(std::string const &conf, 00387 char const *key, 00388 long &value, 00389 long const &def_value, 00390 Parse_Mode const parse_mode) 00391 { 00392 return _get_keyval_scalar_<long>(conf, key, value, def_value, parse_mode); 00393 } 00394 00395 bool colvarparse::get_keyval(std::string const &conf, 00396 char const *key, 00397 cvm::step_number &value, 00398 cvm::step_number const &def_value, 00399 Parse_Mode const parse_mode) 00400 { 00401 return _get_keyval_scalar_<cvm::step_number>(conf, key, value, def_value, parse_mode); 00402 } 00403 00404 bool colvarparse::get_keyval(std::string const &conf, 00405 char const *key, 00406 std::string &value, 00407 std::string const &def_value, 00408 Parse_Mode const parse_mode) 00409 { 00410 return _get_keyval_scalar_<std::string>(conf, key, value, def_value, parse_mode); 00411 } 00412 00413 bool colvarparse::get_keyval(std::string const &conf, 00414 char const *key, 00415 cvm::real &value, 00416 cvm::real const &def_value, 00417 Parse_Mode const parse_mode) 00418 { 00419 return _get_keyval_scalar_<cvm::real>(conf, key, value, def_value, parse_mode); 00420 } 00421 00422 bool colvarparse::get_keyval(std::string const &conf, 00423 char const *key, 00424 cvm::rvector &value, 00425 cvm::rvector const &def_value, 00426 Parse_Mode const parse_mode) 00427 { 00428 return _get_keyval_scalar_<cvm::rvector>(conf, key, value, def_value, parse_mode); 00429 } 00430 00431 bool colvarparse::get_keyval(std::string const &conf, 00432 char const *key, 00433 cvm::quaternion &value, 00434 cvm::quaternion const &def_value, 00435 Parse_Mode const parse_mode) 00436 { 00437 return _get_keyval_scalar_<cvm::quaternion>(conf, key, value, def_value, parse_mode); 00438 } 00439 00440 bool colvarparse::get_keyval(std::string const &conf, 00441 char const *key, 00442 colvarvalue &value, 00443 colvarvalue const &def_value, 00444 Parse_Mode const parse_mode) 00445 { 00446 return _get_keyval_scalar_<colvarvalue>(conf, key, value, def_value, parse_mode); 00447 } 00448 00449 bool colvarparse::get_keyval(std::string const &conf, 00450 char const *key, 00451 bool &value, 00452 bool const &def_value, 00453 Parse_Mode const parse_mode) 00454 { 00455 return _get_keyval_scalar_<bool>(conf, key, value, def_value, parse_mode); 00456 } 00457 00458 00459 // multiple-value keyword parsers 00460 00461 bool colvarparse::get_keyval(std::string const &conf, 00462 char const *key, 00463 std::vector<int> &values, 00464 std::vector<int> const &def_values, 00465 Parse_Mode const parse_mode) 00466 { 00467 return _get_keyval_vector_<int>(conf, key, values, def_values, parse_mode); 00468 } 00469 00470 bool colvarparse::get_keyval(std::string const &conf, 00471 char const *key, 00472 std::vector<size_t> &values, 00473 std::vector<size_t> const &def_values, 00474 Parse_Mode const parse_mode) 00475 { 00476 return _get_keyval_vector_<size_t>(conf, key, values, def_values, parse_mode); 00477 } 00478 00479 bool colvarparse::get_keyval(std::string const &conf, 00480 char const *key, 00481 std::vector<long> &values, 00482 std::vector<long> const &def_values, 00483 Parse_Mode const parse_mode) 00484 { 00485 return _get_keyval_vector_<long>(conf, key, values, def_values, parse_mode); 00486 } 00487 00488 bool colvarparse::get_keyval(std::string const &conf, 00489 char const *key, 00490 std::vector<std::string> &values, 00491 std::vector<std::string> const &def_values, 00492 Parse_Mode const parse_mode) 00493 { 00494 return _get_keyval_vector_<std::string>(conf, key, values, def_values, parse_mode); 00495 } 00496 00497 bool colvarparse::get_keyval(std::string const &conf, 00498 char const *key, 00499 std::vector<cvm::real> &values, 00500 std::vector<cvm::real> const &def_values, 00501 Parse_Mode const parse_mode) 00502 { 00503 return _get_keyval_vector_<cvm::real>(conf, key, values, def_values, parse_mode); 00504 } 00505 00506 bool colvarparse::get_keyval(std::string const &conf, 00507 char const *key, 00508 std::vector<cvm::rvector> &values, 00509 std::vector<cvm::rvector> const &def_values, 00510 Parse_Mode const parse_mode) 00511 { 00512 return _get_keyval_vector_<cvm::rvector>(conf, key, values, def_values, parse_mode); 00513 } 00514 00515 bool colvarparse::get_keyval(std::string const &conf, 00516 char const *key, 00517 std::vector<cvm::quaternion> &values, 00518 std::vector<cvm::quaternion> const &def_values, 00519 Parse_Mode const parse_mode) 00520 { 00521 return _get_keyval_vector_<cvm::quaternion>(conf, key, values, def_values, parse_mode); 00522 } 00523 00524 bool colvarparse::get_keyval(std::string const &conf, 00525 char const *key, 00526 std::vector<colvarvalue> &values, 00527 std::vector<colvarvalue> const &def_values, 00528 Parse_Mode const parse_mode) 00529 { 00530 return _get_keyval_vector_<colvarvalue>(conf, key, values, def_values, parse_mode); 00531 } 00532 00533 00534 void colvarparse::add_keyword(char const *key) 00535 { 00536 std::string const key_str_lower(to_lower_cppstr(std::string(key))); 00537 00538 if (key_set_modes.find(key_str_lower) != key_set_modes.end()) { 00539 return; 00540 } 00541 00542 key_set_modes[key_str_lower] = key_not_set; 00543 00544 allowed_keywords.push_back(key_str_lower); 00545 } 00546 00547 00548 bool colvarparse::key_already_set(std::string const &key_str) 00549 { 00550 std::string const key_str_lower(to_lower_cppstr(key_str)); 00551 00552 if (key_set_modes.find(key_str_lower) == key_set_modes.end()) { 00553 return false; 00554 } 00555 00556 return (key_set_modes[key_str_lower] > 0); 00557 } 00558 00559 00560 void colvarparse::strip_values(std::string &conf) 00561 { 00562 size_t offset = 0; 00563 data_begin_pos.sort(); 00564 data_end_pos.sort(); 00565 std::list<size_t>::iterator data_begin_pos_last = std::unique(data_begin_pos.begin(), data_begin_pos.end()); 00566 data_begin_pos.erase(data_begin_pos_last, data_begin_pos.end()); 00567 std::list<size_t>::iterator data_end_pos_last = std::unique(data_end_pos.begin(), data_end_pos.end()); 00568 data_end_pos.erase(data_end_pos_last, data_end_pos.end()); 00569 00570 std::list<size_t>::iterator data_begin = data_begin_pos.begin(); 00571 std::list<size_t>::iterator data_end = data_end_pos.begin(); 00572 00573 for ( ; (data_begin != data_begin_pos.end()) && 00574 (data_end != data_end_pos.end()) ; 00575 data_begin++, data_end++) { 00576 size_t const nchars = *data_end-*data_begin; 00577 conf.erase(*data_begin - offset, nchars); 00578 offset += nchars; 00579 } 00580 } 00581 00582 00583 void colvarparse::clear_keyword_registry() 00584 { 00585 key_set_modes.clear(); 00586 allowed_keywords.clear(); 00587 data_begin_pos.clear(); 00588 data_end_pos.clear(); 00589 } 00590 00591 00592 int colvarparse::check_keywords(std::string &conf, char const *key) 00593 { 00594 if (cvm::debug()) 00595 cvm::log("Configuration string for \""+std::string(key)+ 00596 "\": \"\n"+conf+"\".\n"); 00597 00598 strip_values(conf); 00599 // after stripping, the config string has either empty lines, or 00600 // lines beginning with a keyword 00601 00602 std::string line; 00603 std::istringstream is(conf); 00604 while (cvm::getline(is, line)) { 00605 if (line.size() == 0) 00606 continue; 00607 if (line.find_first_not_of(white_space) == 00608 std::string::npos) 00609 continue; 00610 00611 std::string uk; 00612 std::istringstream line_is(line); 00613 line_is >> uk; 00614 // if (cvm::debug()) 00615 // cvm::log ("Checking the validity of \""+uk+"\" from line:\n" + line); 00616 uk = to_lower_cppstr(uk); 00617 00618 bool found_keyword = false; 00619 for (std::list<std::string>::iterator ki = allowed_keywords.begin(); 00620 ki != allowed_keywords.end(); ki++) { 00621 if (uk == *ki) { 00622 found_keyword = true; 00623 break; 00624 } 00625 } 00626 if (!found_keyword) { 00627 cvm::error("Error: keyword \""+uk+"\" is not supported, " 00628 "or not recognized in this context.\n", COLVARS_INPUT_ERROR); 00629 return COLVARS_INPUT_ERROR; 00630 } 00631 } 00632 00633 clear_keyword_registry(); 00634 00635 return COLVARS_OK; 00636 } 00637 00638 00639 std::istream & colvarparse::read_config_line(std::istream &is, 00640 std::string &line) 00641 { 00642 cvm::getline(is, line); 00643 config_string += line+'\n'; 00644 size_t const comment = line.find('#'); 00645 if (comment != std::string::npos) { 00646 line.erase(comment); 00647 } 00648 return is; 00649 } 00650 00651 00652 std::istream & colvarparse::getline_nocomments(std::istream &is, 00653 std::string &line) 00654 { 00655 cvm::getline(is, line); 00656 size_t const comment = line.find('#'); 00657 if (comment != std::string::npos) { 00658 line.erase(comment); 00659 } 00660 return is; 00661 } 00662 00663 00664 bool colvarparse::key_lookup(std::string const &conf, 00665 char const *key_in, 00666 std::string *data, 00667 size_t *save_pos) 00668 { 00669 if (cvm::debug()) { 00670 cvm::log("Looking for the keyword \""+std::string(key_in)+ 00671 "\" and its value.\n"); 00672 } 00673 00674 // add this keyword to the register (in its camelCase version) 00675 add_keyword(key_in); 00676 00677 // use the lowercase version from now on 00678 std::string const key(to_lower_cppstr(key_in)); 00679 00680 // "conf_lower" is only used to lookup the keyword, but its value 00681 // will be read from "conf", in order not to mess up file names 00682 std::string const conf_lower(to_lower_cppstr(conf)); 00683 00684 // by default, there is no value, unless we found one 00685 if (data != NULL) { 00686 data->clear(); 00687 } 00688 00689 // start from the first occurrence of key 00690 size_t pos = conf_lower.find(key, (save_pos != NULL) ? *save_pos : 0); 00691 00692 // iterate over all instances of the substring until it finds it as isolated keyword 00693 while (true) { 00694 00695 if (pos == std::string::npos) { 00696 // no valid instance of the keyword has been found 00697 if (cvm::debug()) { 00698 cvm::log("Keyword \""+std::string(key_in)+"\" not found.\n"); 00699 } 00700 return false; 00701 } 00702 00703 bool b_isolated_left = true, b_isolated_right = true; 00704 00705 if (pos > 0) { 00706 if (keyword_delimiters_left.find(conf[pos-1]) == std::string::npos) { 00707 // none of the valid delimiting characters is on the left of key 00708 b_isolated_left = false; 00709 } else { 00710 size_t const pl = conf_lower.rfind("\n", pos); 00711 size_t const line_begin = (pl == std::string::npos) ? 0 : pl+1; 00712 size_t const pchar = 00713 conf_lower.find_first_not_of(keyword_delimiters_left, line_begin); 00714 size_t const first_text = (pchar == std::string::npos) ? pos : pchar; 00715 if (first_text < pos) { 00716 // There are some non-delimiting characters to the left of the 00717 // keyword on the same line 00718 b_isolated_left = false; 00719 } 00720 } 00721 } 00722 00723 if (pos < conf.size()-key.size()-1) { 00724 if (keyword_delimiters_right.find(conf[pos+key.size()]) == 00725 std::string::npos) { 00726 // none of the valid delimiting characters is on the right of key 00727 b_isolated_right = false; 00728 } 00729 } 00730 00731 // check that there are matching braces between here and the end of conf 00732 bool const b_not_within_block = (check_braces(conf, pos) == COLVARS_OK); 00733 00734 bool const b_isolated = (b_isolated_left && b_isolated_right && 00735 b_not_within_block); 00736 00737 if (b_isolated) { 00738 // found it 00739 break; 00740 } else { 00741 // try the next occurrence of key 00742 pos = conf_lower.find(key, pos+key.size()); 00743 } 00744 } 00745 00746 if (save_pos != NULL) { 00747 // save the pointer for a future call (when iterating over multiple 00748 // valid instances of the same keyword) 00749 *save_pos = pos + key.size(); 00750 } 00751 00752 // get the remainder of the line 00753 size_t pl = conf.rfind("\n", pos); 00754 size_t line_begin = (pl == std::string::npos) ? 0 : pl+1; 00755 size_t nl = conf.find("\n", pos); 00756 size_t line_end = (nl == std::string::npos) ? conf.size() : nl; 00757 std::string line(conf, line_begin, (line_end-line_begin)); 00758 00759 size_t data_begin = (to_lower_cppstr(line)).find(key) + key.size(); 00760 data_begin = line.find_first_not_of(white_space, data_begin+1); 00761 00762 if (data_begin != std::string::npos) { 00763 00764 size_t data_end = line.find_last_not_of(white_space) + 1; 00765 data_end = (data_end == std::string::npos) ? line.size() : data_end; 00766 00767 size_t brace = line.find('{', data_begin); // look for an opening brace 00768 size_t brace_last = brace; 00769 00770 if (brace != std::string::npos) { 00771 00772 // find the matching closing brace 00773 00774 // if (cvm::debug()) { 00775 // cvm::log("Multi-line value, config is now \""+line+"\".\n"); 00776 // } 00777 00778 int brace_count = 1; 00779 00780 while (brace_count > 0) { 00781 00782 brace = line.find_first_of("{}", brace_last+1); 00783 // find all braces within this line 00784 while (brace < std::string::npos) { 00785 brace_last = brace; 00786 if (line[brace] == '{') brace_count++; 00787 if (line[brace] == '}') brace_count--; 00788 if (brace_count == 0) { 00789 data_end = brace+1; 00790 break; 00791 } 00792 brace = line.find_first_of("{}", brace+1); 00793 } 00794 00795 if (brace_count == 0) { 00796 data_end = brace+1; 00797 break; 00798 } 00799 00800 if (brace == std::string::npos) { 00801 00802 // add a new line 00803 if (line_end >= conf.size()) { 00804 cvm::error("Parse error: reached the end while " 00805 "looking for closing brace; until now " 00806 "the following was parsed: \"\n"+ 00807 line+"\".\n", COLVARS_INPUT_ERROR); 00808 return false; 00809 } 00810 00811 line_begin = line_end; 00812 nl = conf.find('\n', line_begin+1); 00813 if (nl == std::string::npos) 00814 line_end = conf.size(); 00815 else 00816 line_end = nl; 00817 line.append(conf, line_begin, (line_end-line_begin)); 00818 00819 // if (cvm::debug()) { 00820 // cvm::log("Added a new line, config is now \""+line+"\".\n"); 00821 // } 00822 } 00823 00824 if (brace_count < 0) { 00825 cvm::error("Error: found closing brace without opening brace.\n", COLVARS_INPUT_ERROR); 00826 } 00827 } 00828 00829 // strip the leading and trailing braces 00830 data_begin = line.find_first_of('{') + 1; 00831 data_begin = line.find_first_not_of(white_space, 00832 data_begin); 00833 00834 data_end = line.find_last_of('}', line.size()) - 1; 00835 data_end = line.find_last_not_of(white_space, 00836 data_end) + 1; 00837 } 00838 00839 if (data != NULL) { 00840 data->append(line, data_begin, (data_end-data_begin)); 00841 00842 if (cvm::debug()) { 00843 cvm::log("Keyword value = \""+*data+"\".\n"); 00844 } 00845 00846 if (data->size()) { 00847 data_begin_pos.push_back(conf.find(*data, pos+key.size())); 00848 data_end_pos.push_back(data_begin_pos.back()+data->size()); 00849 } 00850 } 00851 } 00852 00853 if (save_pos != NULL) *save_pos = line_end; 00854 00855 return true; 00856 } 00857 00858 00859 colvarparse::read_block::read_block(std::string const &key_in, 00860 std::string *data_in) 00861 : key(key_in), data(data_in) 00862 { 00863 } 00864 00865 00866 colvarparse::read_block::~read_block() 00867 {} 00868 00869 00870 std::istream & operator>> (std::istream &is, colvarparse::read_block const &rb) 00871 { 00872 std::streampos start_pos = is.tellg(); 00873 std::string read_key, next; 00874 00875 if ( !(is >> read_key) || !(read_key == rb.key) || 00876 !(is >> next) ) { 00877 // the requested keyword has not been found, or it is not possible 00878 // to read data after it 00879 is.clear(); 00880 is.seekg(start_pos, std::ios::beg); 00881 is.setstate(std::ios::failbit); 00882 return is; 00883 } 00884 00885 if (next != "{") { 00886 if (rb.data) { 00887 *(rb.data) = next; 00888 } 00889 return is; 00890 } 00891 00892 size_t brace_count = 1; 00893 std::string line; 00894 while (colvarparse::getline_nocomments(is, line)) { 00895 size_t br = 0, br_old = 0; 00896 while ( (br = line.find_first_of("{}", br)) != std::string::npos) { 00897 if (line[br] == '{') brace_count++; 00898 if (line[br] == '}') brace_count--; 00899 br_old = br; 00900 br++; 00901 } 00902 if (brace_count) { 00903 if (rb.data) { 00904 (rb.data)->append(line + "\n"); 00905 } 00906 } 00907 else { 00908 if (rb.data) { 00909 (rb.data)->append(line, 0, br_old); 00910 } 00911 break; 00912 } 00913 } 00914 if (brace_count) { 00915 // end-of-file reached 00916 // restore initial position 00917 is.clear(); 00918 is.seekg(start_pos, std::ios::beg); 00919 is.setstate(std::ios::failbit); 00920 } 00921 return is; 00922 } 00923 00924 00925 int colvarparse::check_braces(std::string const &conf, 00926 size_t const start_pos) 00927 { 00928 int brace_count = 0; 00929 size_t brace = start_pos; 00930 while ((brace = conf.find_first_of("{}", brace)) != std::string::npos) { 00931 if (conf[brace] == '{') brace_count++; 00932 if (conf[brace] == '}') brace_count--; 00933 brace++; 00934 } 00935 return (brace_count != 0) ? COLVARS_INPUT_ERROR : COLVARS_OK; 00936 } 00937 00938 00939 int colvarparse::check_ascii(std::string const &conf) 00940 { 00941 // Check for non-ASCII characters 00942 std::string line; 00943 std::istringstream is(conf); 00944 while (cvm::getline(is, line)) { 00945 unsigned char const * const uchars = 00946 reinterpret_cast<unsigned char const *>(line.c_str()); 00947 for (size_t i = 0; i < line.size(); i++) { 00948 if (uchars[i] & 0x80U) { 00949 cvm::log("Warning: non-ASCII character detected in this line: \""+ 00950 line+"\".\n"); 00951 } 00952 } 00953 } 00954 return COLVARS_OK; 00955 } 00956 00957 00958 void colvarparse::split_string(const std::string& data, const std::string& delim, std::vector<std::string>& dest) { 00959 size_t index = 0, new_index = 0; 00960 std::string tmpstr; 00961 while (index != data.length()) { 00962 new_index = data.find(delim, index); 00963 if (new_index != std::string::npos) tmpstr = data.substr(index, new_index - index); 00964 else tmpstr = data.substr(index, data.length()); 00965 if (!tmpstr.empty()) { 00966 dest.push_back(tmpstr); 00967 } 00968 if (new_index == std::string::npos) break; 00969 index = new_index + 1; 00970 } 00971 }