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 if (i < values.size()-1) { \
00189 cvm::fatal_error ("Error: values from string \""+ \
00190 data+"\" are less than expected ("+ \
00191 cvm::to_str (values.size())+").\n"); \
00192 } \
00193 } \
00194 \
00195 if (parse_mode != parse_silent) { \
00196 cvm::log ("# "+std::string (key)+" = "+ \
00197 cvm::to_str (values)+"\n"); \
00198 } \
00199 \
00200 } else { \
00201 \
00202 if (b_found_any) \
00203 cvm::fatal_error ("Error: improper or missing values for \""+ \
00204 std::string (key)+"\".\n"); \
00205 \
00206 for (size_t i = 0; i < values.size(); i++) \
00207 values[i] = def_values[ (i > def_values.size()) ? 0 : i ]; \
00208 \
00209 if (parse_mode != parse_silent) { \
00210 cvm::log ("# "+std::string (key)+" = "+ \
00211 cvm::to_str (def_values)+" [default]\n"); \
00212 } \
00213 } \
00214 \
00215 return b_found_any; \
00216 }
00217
00218
00219
00220
00221 _get_keyval_scalar_ (int);
00222 _get_keyval_scalar_ (size_t);
00223 _get_keyval_scalar_string_ (std::string);
00224 _get_keyval_scalar_ (cvm::real);
00225 _get_keyval_scalar_ (cvm::rvector);
00226 _get_keyval_scalar_ (cvm::quaternion);
00227 _get_keyval_scalar_ (colvarvalue);
00228
00229
00230
00231
00232 _get_keyval_vector_ (int);
00233 _get_keyval_vector_ (size_t);
00234 _get_keyval_vector_ (std::string);
00235 _get_keyval_vector_ (cvm::real);
00236 _get_keyval_vector_ (cvm::rvector);
00237 _get_keyval_vector_ (cvm::quaternion);
00238 _get_keyval_vector_ (colvarvalue);
00239
00240
00241
00242 bool colvarparse::get_keyval (std::string const &conf,
00243 char const *key,
00244 bool &value,
00245 bool const &def_value,
00246 Parse_Mode const parse_mode)
00247 {
00248 std::string data;
00249 bool b_found = false, b_found_any = false;
00250 size_t save_pos = 0, found_count = 0;
00251
00252 do {
00253 std::string data_this = "";
00254 b_found = key_lookup (conf, key, data_this, save_pos);
00255 if (b_found) {
00256 if (!b_found_any)
00257 b_found_any = true;
00258 found_count++;
00259 data = data_this;
00260 }
00261 } while (b_found);
00262
00263 if (found_count > 1)
00264 cvm::log ("Warning: found more than one instance of \""+
00265 std::string (key)+"\".\n");
00266
00267 if (data.size()) {
00268 std::istringstream is (data);
00269 if ( (data == std::string ("on")) ||
00270 (data == std::string ("yes")) ||
00271 (data == std::string ("true")) ) {
00272 value = true;
00273 } else if ( (data == std::string ("off")) ||
00274 (data == std::string ("no")) ||
00275 (data == std::string ("false")) ) {
00276 value = false;
00277 } else
00278 cvm::fatal_error ("Error: boolean values only are allowed "
00279 "for \""+std::string (key)+"\".\n");
00280 if (parse_mode != parse_silent) {
00281 cvm::log ("# "+std::string (key)+" = "+
00282 (value ? "on" : "off")+"\n");
00283 }
00284 } else {
00285
00286 if (b_found_any) {
00287 if (parse_mode != parse_silent) {
00288 cvm::log ("# "+std::string (key)+" = on\n");
00289 }
00290 value = true;
00291 } else {
00292 value = def_value;
00293 if (parse_mode != parse_silent) {
00294 cvm::log ("# "+std::string (key)+" = "+
00295 (def_value ? "on" : "off")+" [default]\n");
00296 }
00297 }
00298 }
00299
00300 return b_found_any;
00301 }
00302
00303
00304 void colvarparse::add_keyword (char const *key)
00305 {
00306 for (std::list<std::string>::iterator ki = allowed_keywords.begin();
00307 ki != allowed_keywords.end(); ki++) {
00308 if (to_lower_cppstr (std::string (key)) == *ki)
00309 return;
00310 }
00311
00312
00313
00314 allowed_keywords.push_back (to_lower_cppstr (std::string (key)));
00315 }
00316
00317
00318 void colvarparse::strip_values (std::string &conf)
00319 {
00320 size_t offset = 0;
00321 data_begin_pos.sort();
00322 data_end_pos.sort();
00323
00324 std::list<size_t>::iterator data_begin = data_begin_pos.begin();
00325 std::list<size_t>::iterator data_end = data_end_pos.begin();
00326
00327 for ( ; (data_begin != data_begin_pos.end()) &&
00328 (data_end != data_end_pos.end()) ;
00329 data_begin++, data_end++) {
00330
00331
00332
00333
00334
00335 size_t const nchars = *data_end-*data_begin;
00336
00337
00338
00339
00340
00341 conf.erase (*data_begin - offset, nchars);
00342 offset += nchars;
00343
00344
00345
00346 }
00347 }
00348
00349
00350 void colvarparse::check_keywords (std::string &conf, char const *key)
00351 {
00352
00353
00354
00355
00356 strip_values (conf);
00357
00358
00359
00360 std::string line;
00361 std::istringstream is (conf);
00362 while (std::getline (is, line)) {
00363 if (line.size() == 0)
00364 continue;
00365 if (line.find_first_not_of (white_space) ==
00366 std::string::npos)
00367 continue;
00368
00369 std::string uk;
00370 std::istringstream line_is (line);
00371 line_is >> uk;
00372 if (cvm::debug())
00373 cvm::log ("Checking the validity of \""+uk+"\".\n");
00374 uk = to_lower_cppstr (uk);
00375
00376 bool found_keyword = false;
00377 for (std::list<std::string>::iterator ki = allowed_keywords.begin();
00378 ki != allowed_keywords.end(); ki++) {
00379 if (uk == *ki) {
00380 found_keyword = true;
00381 break;
00382 }
00383 }
00384 if (!found_keyword)
00385 cvm::fatal_error ("Error: keyword \""+uk+"\" is not supported, "
00386 "or not recognized in this context.\n");
00387 }
00388 }
00389
00390
00391 std::istream & colvarparse::getline_nocomments (std::istream &is,
00392 std::string &line,
00393 char const delim)
00394 {
00395 std::getline (is, line, delim);
00396 size_t const comment = line.find ('#');
00397 if (comment != std::string::npos) {
00398 line.erase (comment);
00399 }
00400 return is;
00401 }
00402
00403
00404 bool colvarparse::key_lookup (std::string const &conf,
00405 char const *key_in,
00406 std::string &data,
00407 size_t &save_pos)
00408 {
00409
00410 add_keyword (key_in);
00411
00412
00413 std::string const key (to_lower_cppstr (key_in));
00414
00415
00416
00417 std::string const conf_lower (to_lower_cppstr (conf));
00418
00419
00420 data = "";
00421
00422
00423
00424 colvarparse::dummy_pos = 0;
00425
00426
00427 size_t pos = conf_lower.find (key, save_pos);
00428
00429
00430 while (true) {
00431
00432 if (pos == std::string::npos) {
00433
00434 return false;
00435 }
00436
00437 bool b_isolated_left = true, b_isolated_right = true;
00438
00439 if (pos > 0) {
00440 if ( std::string ("\n"+white_space+
00441 "}").find (conf[pos-1]) ==
00442 std::string::npos ) {
00443
00444 b_isolated_left = false;
00445 }
00446 }
00447
00448 if (pos < conf.size()-key.size()-1) {
00449 if ( std::string ("\n"+white_space+
00450 "{").find (conf[pos+key.size()]) ==
00451 std::string::npos ) {
00452
00453 b_isolated_right = false;
00454 }
00455 }
00456
00457
00458 bool const b_not_within_block = brace_check (conf, pos);
00459
00460 bool const b_isolated = (b_isolated_left && b_isolated_right &&
00461 b_not_within_block);
00462
00463 if (b_isolated) {
00464
00465 break;
00466 } else {
00467
00468 pos = conf_lower.find (key, pos+key.size());
00469 }
00470 }
00471
00472
00473
00474
00475
00476
00477
00478
00479
00480
00481
00482
00483
00484 save_pos = pos + key.size();
00485
00486
00487 size_t pl = conf.rfind ("\n", pos);
00488 size_t line_begin = (pl == std::string::npos) ? 0 : pos;
00489 size_t nl = conf.find ("\n", pos);
00490 size_t line_end = (nl == std::string::npos) ? conf.size() : nl;
00491 std::string line (conf, line_begin, (line_end-line_begin));
00492
00493 size_t data_begin = (to_lower_cppstr (line)).find (key) + key.size();
00494 data_begin = line.find_first_not_of (white_space, data_begin+1);
00495
00496
00497
00498
00499 if (data_begin != std::string::npos) {
00500
00501 size_t data_end = line.find_last_not_of (white_space) + 1;
00502 data_end = (data_end == std::string::npos) ? line.size() : data_end;
00503
00504
00505 if (line.find ('{', data_begin) != std::string::npos) {
00506
00507 size_t brace_count = 1;
00508 size_t brace = line.find ('{', data_begin);
00509
00510 while (brace_count > 0) {
00511
00512
00513 brace = line.find_first_of ("{}", brace+1);
00514 while (brace == std::string::npos) {
00515
00516 if (line_end >= conf.size()) {
00517 cvm::fatal_error ("Parse error: reached the end while "
00518 "looking for closing brace; until now "
00519 "the following was parsed: \"\n"+
00520 line+"\".\n");
00521 return false;
00522 }
00523 size_t const old_end = line.size();
00524
00525
00526 line_begin = line_end;
00527 nl = conf.find ('\n', line_begin+1);
00528 if (nl == std::string::npos)
00529 line_end = conf.size();
00530 else
00531 line_end = nl;
00532 line.append (conf, line_begin, (line_end-line_begin));
00533
00534 brace = line.find_first_of ("{}", old_end);
00535 }
00536
00537 if (line[brace] == '{') brace_count++;
00538 if (line[brace] == '}') brace_count--;
00539 }
00540
00541
00542 data_begin = line.find_first_of ('{', data_begin) + 1;
00543 data_begin = line.find_first_not_of (white_space,
00544 data_begin);
00545
00546 data_end = brace;
00547 data_end = line.find_last_not_of (white_space+"}",
00548 data_end) + 1;
00549
00550
00551 if (data_end > line.size())
00552 data_end = line.size();
00553 }
00554
00555 data.append (line, data_begin, (data_end-data_begin));
00556
00557 if (data.size() && save_delimiters) {
00558 data_begin_pos.push_back (conf.find (data, pos+key.size()));
00559 data_end_pos.push_back (data_begin_pos.back()+data.size());
00560
00561
00562
00563
00564 }
00565 }
00566
00567 save_pos = line_end;
00568
00569 return true;
00570 }
00571
00572
00573 std::istream & operator>> (std::istream &is, colvarparse::read_block const &rb)
00574 {
00575 size_t start_pos = is.tellg();
00576 std::string read_key, next;
00577
00578 if ( !(is >> read_key) || !(read_key == rb.key) ||
00579 !(is >> next) ) {
00580
00581
00582 is.clear();
00583 is.seekg (start_pos, std::ios::beg);
00584 is.setstate (std::ios::failbit);
00585 return is;
00586 }
00587
00588 if (next != "{") {
00589 (*rb.data) = next;
00590 return is;
00591 }
00592
00593 size_t brace_count = 1;
00594 std::string line;
00595 while (colvarparse::getline_nocomments (is, line)) {
00596 size_t br = 0, br_old;
00597 while ( (br = line.find_first_of ("{}", br)) != std::string::npos) {
00598 if (line[br] == '{') brace_count++;
00599 if (line[br] == '}') brace_count--;
00600 br_old = br;
00601 br++;
00602 }
00603 if (brace_count) (*rb.data).append (line + "\n");
00604 else {
00605 (*rb.data).append (line, 0, br_old);
00606 break;
00607 }
00608 }
00609 if (brace_count) {
00610
00611
00612 is.clear();
00613 is.seekg (start_pos, std::ios::beg);
00614 is.setstate (std::ios::failbit);
00615 }
00616 return is;
00617 }
00618
00619
00620 bool colvarparse::brace_check (std::string const &conf,
00621 size_t const start_pos)
00622 {
00623 size_t brace_count = 0;
00624 size_t brace = start_pos;
00625 while ( (brace = conf.find_first_of ("{}", brace)) != std::string::npos) {
00626 if (conf[brace] == '{') brace_count++;
00627 if (conf[brace] == '}') brace_count--;
00628 brace++;
00629 }
00630
00631 if (brace_count != 0)
00632 return false;
00633 else
00634 return true;
00635 }
00636
00637
00638
00639
00640
00641