00001 #include <sstream>
00002 #include <iostream>
00003
00004
00005
00006 #include "colvarmodule.h"
00007 #include "colvarvalue.h"
00008 #include "colvarparse.h"
00009
00010
00011
00012
00013 std::string const colvarparse::white_space = " \t";
00014
00015 std::string colvarparse::dummy_string = "";
00016 size_t colvarparse::dummy_pos = 0;
00017
00018
00019
00020
00021 #define _get_keyval_scalar_string_(TYPE) \
00022 \
00023 bool colvarparse::get_keyval (std::string const &conf, \
00024 char const *key, \
00025 TYPE &value, \
00026 TYPE const &def_value, \
00027 Parse_Mode const parse_mode) \
00028 { \
00029 std::string data; \
00030 bool b_found = false, b_found_any = false; \
00031 size_t save_pos = 0, found_count = 0; \
00032 \
00033 do { \
00034 std::string data_this = ""; \
00035 b_found = key_lookup (conf, key, data_this, save_pos); \
00036 if (b_found) { \
00037 if (!b_found_any) \
00038 b_found_any = true; \
00039 found_count++; \
00040 data = data_this; \
00041 } \
00042 } while (b_found); \
00043 \
00044 if (found_count > 1) \
00045 cvm::log ("Warning: found more than one instance of \""+ \
00046 std::string (key)+"\".\n"); \
00047 \
00048 if (data.size()) { \
00049 std::istringstream is (data); \
00050 TYPE x (def_value); \
00051 if (is >> x) \
00052 value = x; \
00053 else \
00054 cvm::fatal_error ("Error: in parsing \""+ \
00055 std::string (key)+"\".\n"); \
00056 if (parse_mode != parse_silent) { \
00057 cvm::log ("# "+std::string (key)+" = "+ \
00058 cvm::to_str (value)+"\n"); \
00059 } \
00060 } else { \
00061 \
00062 if (b_found_any) \
00063 cvm::fatal_error ("Error: improper or missing value " \
00064 "for \""+std::string (key)+"\".\n"); \
00065 value = def_value; \
00066 if (parse_mode != parse_silent) { \
00067 cvm::log ("# "+std::string (key)+" = \""+ \
00068 cvm::to_str (def_value)+"\" [default]\n"); \
00069 } \
00070 } \
00071 \
00072 return b_found_any; \
00073 }
00074
00075
00076 #define _get_keyval_scalar_(TYPE) \
00077 \
00078 bool colvarparse::get_keyval (std::string const &conf, \
00079 char const *key, \
00080 TYPE &value, \
00081 TYPE const &def_value, \
00082 Parse_Mode const parse_mode) \
00083 { \
00084 std::string data; \
00085 bool b_found = false, b_found_any = false; \
00086 size_t save_pos = 0, found_count = 0; \
00087 \
00088 do { \
00089 std::string data_this = ""; \
00090 b_found = key_lookup (conf, key, data_this, save_pos); \
00091 if (b_found) { \
00092 if (!b_found_any) \
00093 b_found_any = true; \
00094 found_count++; \
00095 data = data_this; \
00096 } \
00097 } while (b_found); \
00098 \
00099 if (found_count > 1) \
00100 cvm::log ("Warning: found more than one instance of \""+ \
00101 std::string (key)+"\".\n"); \
00102 \
00103 if (data.size()) { \
00104 std::istringstream is (data); \
00105 TYPE x (def_value); \
00106 if (is >> x) \
00107 value = x; \
00108 else \
00109 cvm::fatal_error ("Error: in parsing \""+ \
00110 std::string (key)+"\".\n"); \
00111 if (parse_mode != parse_silent) { \
00112 cvm::log ("# "+std::string (key)+" = "+ \
00113 cvm::to_str (value)+"\n"); \
00114 } \
00115 } else { \
00116 \
00117 if (b_found_any) \
00118 cvm::fatal_error ("Error: improper or missing value " \
00119 "for \""+std::string (key)+"\".\n"); \
00120 value = def_value; \
00121 if (parse_mode != parse_silent) { \
00122 cvm::log ("# "+std::string (key)+" = "+ \
00123 cvm::to_str (def_value)+" [default]\n"); \
00124 } \
00125 } \
00126 \
00127 return b_found_any; \
00128 }
00129
00130
00131
00132
00133 #define _get_keyval_vector_(TYPE) \
00134 \
00135 bool colvarparse::get_keyval (std::string const &conf, \
00136 char const *key, \
00137 std::vector<TYPE> &values, \
00138 std::vector<TYPE> const &def_values, \
00139 Parse_Mode const parse_mode) \
00140 { \
00141 std::string data; \
00142 bool b_found = false, b_found_any = false; \
00143 size_t save_pos = 0, found_count = 0; \
00144 \
00145 do { \
00146 std::string data_this = ""; \
00147 b_found = key_lookup (conf, key, data_this, save_pos); \
00148 if (b_found) { \
00149 if (!b_found_any) \
00150 b_found_any = true; \
00151 found_count++; \
00152 data = data_this; \
00153 } \
00154 } while (b_found); \
00155 \
00156 if (found_count > 1) \
00157 cvm::log ("Warning: found more than one instance of \""+ \
00158 std::string (key)+"\".\n"); \
00159 \
00160 if (data.size()) { \
00161 std::istringstream is (data); \
00162 \
00163 if (values.size() == 0) { \
00164 \
00165 std::vector<TYPE> x; \
00166 if (def_values.size()) \
00167 x = def_values; \
00168 else \
00169 x.assign (1, TYPE()); \
00170 \
00171 for (size_t i = 0; \
00172 ( is >> x[ ((i<x.size()) ? i : x.size()-1) ] ); \
00173 i++) { \
00174 values.push_back (x[ ((i<x.size()) ? i : x.size()-1) ]); \
00175 } \
00176 \
00177 } else { \
00178 \
00179 size_t i = 0; \
00180 for ( ; i < values.size(); i++) { \
00181 TYPE x (values[i]); \
00182 if (is >> x) \
00183 values[i] = x; \
00184 else \
00185 cvm::fatal_error ("Error: in parsing \""+ \
00186 std::string (key)+"\".\n"); \
00187 } \
00188 } \
00189 \
00190 if (parse_mode != parse_silent) { \
00191 cvm::log ("# "+std::string (key)+" = "+ \
00192 cvm::to_str (values)+"\n"); \
00193 } \
00194 \
00195 } else { \
00196 \
00197 if (b_found_any) \
00198 cvm::fatal_error ("Error: improper or missing values for \""+ \
00199 std::string (key)+"\".\n"); \
00200 \
00201 for (size_t i = 0; i < values.size(); i++) \
00202 values[i] = def_values[ (i > def_values.size()) ? 0 : i ]; \
00203 \
00204 if (parse_mode != parse_silent) { \
00205 cvm::log ("# "+std::string (key)+" = "+ \
00206 cvm::to_str (def_values)+" [default]\n"); \
00207 } \
00208 } \
00209 \
00210 return b_found_any; \
00211 }
00212
00213
00214
00215
00216 _get_keyval_scalar_ (int);
00217 _get_keyval_scalar_ (size_t);
00218 _get_keyval_scalar_string_ (std::string);
00219 _get_keyval_scalar_ (cvm::real);
00220 _get_keyval_scalar_ (cvm::rvector);
00221 _get_keyval_scalar_ (cvm::quaternion);
00222 _get_keyval_scalar_ (colvarvalue);
00223
00224
00225
00226
00227 _get_keyval_vector_ (int);
00228 _get_keyval_vector_ (size_t);
00229 _get_keyval_vector_ (std::string);
00230 _get_keyval_vector_ (cvm::real);
00231 _get_keyval_vector_ (cvm::rvector);
00232 _get_keyval_vector_ (cvm::quaternion);
00233 _get_keyval_vector_ (colvarvalue);
00234
00235
00236
00237 bool colvarparse::get_keyval (std::string const &conf,
00238 char const *key,
00239 bool &value,
00240 bool const &def_value,
00241 Parse_Mode const parse_mode)
00242 {
00243 std::string data;
00244 bool b_found = false, b_found_any = false;
00245 size_t save_pos = 0, found_count = 0;
00246
00247 do {
00248 std::string data_this = "";
00249 b_found = key_lookup (conf, key, data_this, save_pos);
00250 if (b_found) {
00251 if (!b_found_any)
00252 b_found_any = true;
00253 found_count++;
00254 data = data_this;
00255 }
00256 } while (b_found);
00257
00258 if (found_count > 1)
00259 cvm::log ("Warning: found more than one instance of \""+
00260 std::string (key)+"\".\n");
00261
00262 if (data.size()) {
00263 std::istringstream is (data);
00264 if ( (data == std::string ("on")) ||
00265 (data == std::string ("yes")) ||
00266 (data == std::string ("true")) ) {
00267 value = true;
00268 } else if ( (data == std::string ("off")) ||
00269 (data == std::string ("no")) ||
00270 (data == std::string ("false")) ) {
00271 value = false;
00272 } else
00273 cvm::fatal_error ("Error: boolean values only are allowed "
00274 "for \""+std::string (key)+"\".\n");
00275 if (parse_mode != parse_silent) {
00276 cvm::log ("# "+std::string (key)+" = "+
00277 (value ? "on" : "off")+"\n");
00278 }
00279 } else {
00280
00281 if (b_found_any) {
00282 if (parse_mode != parse_silent) {
00283 cvm::log ("# "+std::string (key)+" = on\n");
00284 }
00285 value = true;
00286 } else {
00287 value = def_value;
00288 if (parse_mode != parse_silent) {
00289 cvm::log ("# "+std::string (key)+" = "+
00290 (def_value ? "on" : "off")+" [default]\n");
00291 }
00292 }
00293 }
00294
00295 return b_found_any;
00296 }
00297
00298
00299 void colvarparse::add_keyword (char const *key)
00300 {
00301 for (std::list<std::string>::iterator ki = allowed_keywords.begin();
00302 ki != allowed_keywords.end(); ki++) {
00303 if (to_lower_cppstr (std::string (key)) == *ki)
00304 return;
00305 }
00306
00307
00308
00309 allowed_keywords.push_back (to_lower_cppstr (std::string (key)));
00310 }
00311
00312
00313 void colvarparse::strip_values (std::string &conf)
00314 {
00315 size_t offset = 0;
00316 data_begin_pos.sort();
00317 data_end_pos.sort();
00318
00319 std::list<size_t>::iterator data_begin = data_begin_pos.begin();
00320 std::list<size_t>::iterator data_end = data_end_pos.begin();
00321
00322 for ( ; (data_begin != data_begin_pos.end()) &&
00323 (data_end != data_end_pos.end()) ;
00324 data_begin++, data_end++) {
00325
00326
00327
00328
00329
00330 size_t const nchars = *data_end-*data_begin;
00331
00332
00333
00334
00335
00336 conf.erase (*data_begin - offset, nchars);
00337 offset += nchars;
00338
00339
00340
00341 }
00342 }
00343
00344
00345 void colvarparse::check_keywords (std::string &conf, char const *key)
00346 {
00347 if (cvm::debug())
00348 cvm::log ("Configuration string for \""+std::string (key)+
00349 "\": \"\n"+conf+"\".\n");
00350
00351 strip_values (conf);
00352
00353
00354
00355 std::string line;
00356 std::istringstream is (conf);
00357 while (std::getline (is, line)) {
00358 if (line.size() == 0)
00359 continue;
00360 if (line.find_first_not_of (white_space) ==
00361 std::string::npos)
00362 continue;
00363
00364 std::string uk;
00365 std::istringstream line_is (line);
00366 line_is >> uk;
00367 if (cvm::debug())
00368 cvm::log ("Checking the validity of \""+uk+"\" from line:\n" + line);
00369 uk = to_lower_cppstr (uk);
00370
00371 bool found_keyword = false;
00372 for (std::list<std::string>::iterator ki = allowed_keywords.begin();
00373 ki != allowed_keywords.end(); ki++) {
00374 if (uk == *ki) {
00375 found_keyword = true;
00376 break;
00377 }
00378 }
00379 if (!found_keyword)
00380 cvm::fatal_error ("Error: keyword \""+uk+"\" is not supported, "
00381 "or not recognized in this context.\n");
00382 }
00383 }
00384
00385
00386 std::istream & colvarparse::getline_nocomments (std::istream &is,
00387 std::string &line,
00388 char const delim)
00389 {
00390 std::getline (is, line, delim);
00391 size_t const comment = line.find ('#');
00392 if (comment != std::string::npos) {
00393 line.erase (comment);
00394 }
00395 return is;
00396 }
00397
00398
00399 bool colvarparse::key_lookup (std::string const &conf,
00400 char const *key_in,
00401 std::string &data,
00402 size_t &save_pos)
00403 {
00404
00405 add_keyword (key_in);
00406
00407
00408 std::string const key (to_lower_cppstr (key_in));
00409
00410
00411
00412 std::string const conf_lower (to_lower_cppstr (conf));
00413
00414
00415 data = "";
00416
00417
00418
00419 colvarparse::dummy_pos = 0;
00420
00421
00422 size_t pos = conf_lower.find (key, save_pos);
00423
00424
00425 while (true) {
00426
00427 if (pos == std::string::npos) {
00428
00429 return false;
00430 }
00431
00432 bool b_isolated_left = true, b_isolated_right = true;
00433
00434 if (pos > 0) {
00435 if ( std::string ("\n"+white_space+
00436 "}").find (conf[pos-1]) ==
00437 std::string::npos ) {
00438
00439 b_isolated_left = false;
00440 }
00441 }
00442
00443 if (pos < conf.size()-key.size()-1) {
00444 if ( std::string ("\n"+white_space+
00445 "{").find (conf[pos+key.size()]) ==
00446 std::string::npos ) {
00447
00448 b_isolated_right = false;
00449 }
00450 }
00451
00452
00453 bool const b_not_within_block = brace_check (conf, pos);
00454
00455 bool const b_isolated = (b_isolated_left && b_isolated_right &&
00456 b_not_within_block);
00457
00458 if (b_isolated) {
00459
00460 break;
00461 } else {
00462
00463 pos = conf_lower.find (key, pos+key.size());
00464 }
00465 }
00466
00467
00468
00469
00470
00471
00472
00473
00474
00475
00476
00477
00478
00479 save_pos = pos + key.size();
00480
00481
00482 size_t pl = conf.rfind ("\n", pos);
00483 size_t line_begin = (pl == std::string::npos) ? 0 : pos;
00484 size_t nl = conf.find ("\n", pos);
00485 size_t line_end = (nl == std::string::npos) ? conf.size() : nl;
00486 std::string line (conf, line_begin, (line_end-line_begin));
00487
00488 size_t data_begin = (to_lower_cppstr (line)).find (key) + key.size();
00489 data_begin = line.find_first_not_of (white_space, data_begin+1);
00490
00491
00492
00493
00494 if (data_begin != std::string::npos) {
00495
00496 size_t data_end = line.find_last_not_of (white_space) + 1;
00497 data_end = (data_end == std::string::npos) ? line.size() : data_end;
00498
00499
00500 if (line.find ('{', data_begin) != std::string::npos) {
00501
00502 size_t brace_count = 1;
00503 size_t brace = line.find ('{', data_begin);
00504
00505 while (brace_count > 0) {
00506
00507
00508 brace = line.find_first_of ("{}", brace+1);
00509 while (brace == std::string::npos) {
00510
00511 if (line_end >= conf.size()) {
00512 cvm::fatal_error ("Parse error: reached the end while "
00513 "looking for closing brace; until now "
00514 "the following was parsed: \"\n"+
00515 line+"\".\n");
00516 return false;
00517 }
00518 size_t const old_end = line.size();
00519
00520
00521 line_begin = line_end;
00522 nl = conf.find ('\n', line_begin+1);
00523 if (nl == std::string::npos)
00524 line_end = conf.size();
00525 else
00526 line_end = nl;
00527 line.append (conf, line_begin, (line_end-line_begin));
00528
00529 brace = line.find_first_of ("{}", old_end);
00530 }
00531
00532 if (line[brace] == '{') brace_count++;
00533 if (line[brace] == '}') brace_count--;
00534 }
00535
00536
00537 data_begin = line.find_first_of ('{', data_begin) + 1;
00538 data_begin = line.find_first_not_of (white_space,
00539 data_begin);
00540
00541 data_end = brace;
00542 data_end = line.find_last_not_of (white_space+"}",
00543 data_end) + 1;
00544
00545
00546 if (data_end > line.size())
00547 data_end = line.size();
00548 }
00549
00550 data.append (line, data_begin, (data_end-data_begin));
00551
00552 if (data.size() && save_delimiters) {
00553 data_begin_pos.push_back (conf.find (data, pos+key.size()));
00554 data_end_pos.push_back (data_begin_pos.back()+data.size());
00555
00556
00557
00558
00559 }
00560 }
00561
00562 save_pos = line_end;
00563
00564 return true;
00565 }
00566
00567
00568 std::istream & operator>> (std::istream &is, colvarparse::read_block const &rb)
00569 {
00570 size_t start_pos = is.tellg();
00571 std::string read_key, next;
00572
00573 if ( !(is >> read_key) || !(read_key == rb.key) ||
00574 !(is >> next) ) {
00575
00576
00577 is.clear();
00578 is.seekg (start_pos, std::ios::beg);
00579 is.setstate (std::ios::failbit);
00580 return is;
00581 }
00582
00583 if (next != "{") {
00584 (*rb.data) = next;
00585 return is;
00586 }
00587
00588 size_t brace_count = 1;
00589 std::string line;
00590 while (colvarparse::getline_nocomments (is, line)) {
00591 size_t br = 0, br_old;
00592 while ( (br = line.find_first_of ("{}", br)) != std::string::npos) {
00593 if (line[br] == '{') brace_count++;
00594 if (line[br] == '}') brace_count--;
00595 br_old = br;
00596 br++;
00597 }
00598 if (brace_count) (*rb.data).append (line + "\n");
00599 else {
00600 (*rb.data).append (line, 0, br_old);
00601 break;
00602 }
00603 }
00604 if (brace_count) {
00605
00606
00607 is.clear();
00608 is.seekg (start_pos, std::ios::beg);
00609 is.setstate (std::ios::failbit);
00610 }
00611 return is;
00612 }
00613
00614
00615 bool colvarparse::brace_check (std::string const &conf,
00616 size_t const start_pos)
00617 {
00618 size_t brace_count = 0;
00619 size_t brace = start_pos;
00620 while ( (brace = conf.find_first_of ("{}", brace)) != std::string::npos) {
00621 if (conf[brace] == '{') brace_count++;
00622 if (conf[brace] == '}') brace_count--;
00623 brace++;
00624 }
00625
00626 if (brace_count != 0)
00627 return false;
00628 else
00629 return true;
00630 }
00631
00632
00633
00634
00635
00636