moab
|
00001 /* 00002 * MOAB, a Mesh-Oriented datABase, is a software component for creating, 00003 * storing and accessing finite element mesh data. 00004 * 00005 * Copyright 2004 Sandia Corporation. Under the terms of Contract 00006 * DE-AC04-94AL85000 with Sandia Coroporation, the U.S. Government 00007 * retains certain rights in this software. 00008 * 00009 * This library is free software; you can redistribute it and/or 00010 * modify it under the terms of the GNU Lesser General Public 00011 * License as published by the Free Software Foundation; either 00012 * version 2.1 of the License, or (at your option) any later version. 00013 * 00014 */ 00015 00021 #include "moab/FileOptions.hpp" 00022 00023 #include <ctype.h> 00024 #include <stdlib.h> 00025 #include <string.h> 00026 #include <algorithm> 00027 00028 namespace moab { 00029 00030 const char DEFAULT_SEPARATOR = ';'; 00031 00032 static inline bool strempty( const char* s ) { return !*s; } 00033 00034 FileOptions::FileOptions( const char* str ) 00035 : mData(0) 00036 { 00037 // if option string is null, just return 00038 if (!str) 00039 return; 00040 00041 // check if alternate separator is specified 00042 char separator[2] = { DEFAULT_SEPARATOR, '\0' }; 00043 if (*str == DEFAULT_SEPARATOR) { 00044 ++str; 00045 if (strempty(str)) 00046 return; 00047 separator[0] = *str; 00048 ++str; 00049 } 00050 00051 // don't bother allocating copy of input string if 00052 // input string is empty. 00053 if (!strempty(str)) 00054 { 00055 // tokenize at separator character 00056 mData = strdup( str ); 00057 for (char* i = strtok( mData, separator ); i; i = strtok( 0, separator )) 00058 if (!strempty(i)) // skip empty strings 00059 mOptions.push_back( i ); 00060 } 00061 00062 mSeen.resize( mOptions.size(), false ); 00063 } 00064 00065 FileOptions::FileOptions( const FileOptions& copy ) : 00066 mData(0), mOptions( copy.mOptions.size() ) 00067 { 00068 if (!copy.mOptions.empty()) { 00069 const char* last = copy.mOptions.back(); 00070 const char* endptr = last + strlen(last) + 1; 00071 size_t len = endptr - copy.mData; 00072 mData = (char*)malloc( len ); 00073 memcpy( mData, copy.mData, len ); 00074 for (size_t i = 0; i < mOptions.size(); ++i) 00075 mOptions[i] = mData + (copy.mOptions[i] - copy.mData); 00076 } 00077 mSeen = copy.mSeen; 00078 } 00079 00080 FileOptions& FileOptions::operator=( const FileOptions& copy ) 00081 { 00082 free( mData ); 00083 mData = 0; 00084 mOptions.resize( copy.mOptions.size() ); 00085 00086 if (!copy.mOptions.empty()) { 00087 const char* last = copy.mOptions.back(); 00088 const char* endptr = last + strlen(last) + 1; 00089 size_t len = endptr - copy.mData; 00090 mData = (char*)malloc( len ); 00091 memcpy( mData, copy.mData, len ); 00092 for (size_t i = 0; i < mOptions.size(); ++i) 00093 mOptions[i] = mData + (copy.mOptions[i] - copy.mData); 00094 } 00095 00096 mSeen = copy.mSeen; 00097 return *this; 00098 } 00099 00100 FileOptions::~FileOptions() 00101 { 00102 free( mData ); 00103 } 00104 00105 ErrorCode FileOptions::get_null_option( const char* name ) const 00106 { 00107 const char* s; 00108 ErrorCode rval = get_option( name, s ); 00109 if (MB_SUCCESS != rval) 00110 return rval; 00111 return strempty(s) ? MB_SUCCESS : MB_TYPE_OUT_OF_RANGE; 00112 } 00113 00114 ErrorCode FileOptions::get_int_option( const char* name, int& value ) const 00115 { 00116 const char* s; 00117 ErrorCode rval = get_option( name, s ); 00118 if (MB_SUCCESS != rval) 00119 return rval; 00120 00121 // empty string 00122 if (strempty(s)) 00123 return MB_TYPE_OUT_OF_RANGE; 00124 00125 // parse value 00126 char* endptr; 00127 long int pval = strtol( s, &endptr, 0 ); 00128 if (!strempty(endptr)) // syntax error 00129 return MB_TYPE_OUT_OF_RANGE; 00130 00131 // check for overflow (parsing long int, returning int) 00132 value = pval; 00133 if (pval != (long int)value) 00134 return MB_TYPE_OUT_OF_RANGE; 00135 00136 return MB_SUCCESS; 00137 } 00138 00139 ErrorCode FileOptions::get_int_option( const char* name, 00140 int default_val, 00141 int& value ) const 00142 { 00143 const char* s; 00144 ErrorCode rval = get_option( name, s ); 00145 if (MB_SUCCESS != rval) 00146 return rval; 00147 00148 // empty string 00149 if (strempty(s)) { 00150 value = default_val; 00151 return MB_SUCCESS; 00152 } 00153 00154 // parse value 00155 char* endptr; 00156 long int pval = strtol( s, &endptr, 0 ); 00157 if (!strempty(endptr)) // syntax error 00158 return MB_TYPE_OUT_OF_RANGE; 00159 00160 // check for overflow (parsing long int, returning int) 00161 value = pval; 00162 if (pval != (long int)value) 00163 return MB_TYPE_OUT_OF_RANGE; 00164 00165 return MB_SUCCESS; 00166 } 00167 00168 ErrorCode FileOptions::get_ints_option( const char* name, 00169 std::vector<int>& values) const 00170 { 00171 const char* s; 00172 ErrorCode rval = get_option( name, s ); 00173 if (MB_SUCCESS != rval) 00174 return rval; 00175 00176 // empty string 00177 if (strempty(s)) 00178 return MB_TYPE_OUT_OF_RANGE; 00179 00180 // parse values 00181 while (!strempty(s)) { 00182 char* endptr; 00183 long int sval = strtol( s, &endptr, 0 ); 00184 00185 #define EATSPACE(a) while ((*a == ' ' || \ 00186 *a == ',') && !strempty(a)) a++; 00187 EATSPACE(endptr); 00188 long int eval = sval; 00189 if (*endptr == '-') { 00190 endptr++; 00191 s = endptr; 00192 eval = strtol(s, &endptr, 0); 00193 EATSPACE(endptr); 00194 } 00195 00196 // check for overflow (parsing long int, returning int) 00197 int value = sval; 00198 if (sval != (long int)value) 00199 return MB_TYPE_OUT_OF_RANGE; 00200 value = eval; 00201 if (eval != (long int)value) 00202 return MB_TYPE_OUT_OF_RANGE; 00203 00204 for (int i = sval; i <= eval; i++) 00205 values.push_back(i); 00206 00207 s = endptr; 00208 } 00209 00210 return MB_SUCCESS; 00211 } 00212 00213 ErrorCode FileOptions::get_reals_option( const char* name, 00214 std::vector<double>& values) const 00215 { 00216 const char* s; 00217 ErrorCode rval = get_option( name, s ); 00218 if (MB_SUCCESS != rval) 00219 return rval; 00220 00221 // empty string 00222 if (strempty(s)) 00223 return MB_TYPE_OUT_OF_RANGE; 00224 00225 // parse values 00226 while (!strempty(s)) { 00227 char* endptr; 00228 double sval = strtod( s, &endptr); 00229 00230 EATSPACE(endptr); 00231 values.push_back(sval); 00232 00233 s = endptr; 00234 } 00235 00236 return MB_SUCCESS; 00237 } 00238 00239 ErrorCode FileOptions::get_real_option ( const char* name, double& value ) const 00240 { 00241 const char* s; 00242 ErrorCode rval = get_option( name, s ); 00243 if (MB_SUCCESS != rval) 00244 return rval; 00245 00246 // empty string 00247 if (strempty(s)) 00248 return MB_TYPE_OUT_OF_RANGE; 00249 00250 // parse value 00251 char* endptr; 00252 value = strtod( s, &endptr ); 00253 if (!strempty(endptr)) // syntax error 00254 return MB_TYPE_OUT_OF_RANGE; 00255 00256 return MB_SUCCESS; 00257 } 00258 00259 ErrorCode FileOptions::get_strs_option( const char* name, 00260 std::vector<std::string>& values) const 00261 { 00262 const char* s; 00263 ErrorCode rval = get_option( name, s ); 00264 if (MB_SUCCESS != rval) 00265 return rval; 00266 00267 // empty string 00268 if (strempty(s)) 00269 return MB_TYPE_OUT_OF_RANGE; 00270 00271 // parse values 00272 char separator[3] = { ' ', ',', '\0' }; 00273 char *tmp_str = strdup(s); 00274 for (char* i = strtok( tmp_str, separator ); i; i = strtok( 0, separator )) 00275 if (!strempty(i)) // skip empty strings 00276 values.push_back( std::string(i)); 00277 free(tmp_str); 00278 00279 return MB_SUCCESS; 00280 } 00281 00282 ErrorCode FileOptions::get_str_option( const char* name, std::string& value ) const 00283 { 00284 const char* s; 00285 ErrorCode rval = get_option( name, s ); 00286 if (MB_SUCCESS != rval) 00287 return rval; 00288 if (strempty(s)) 00289 return MB_TYPE_OUT_OF_RANGE; 00290 value = s; 00291 return MB_SUCCESS; 00292 } 00293 00294 ErrorCode FileOptions::get_option( const char* name, std::string& value ) const 00295 { 00296 const char* s; 00297 ErrorCode rval = get_option( name, s ); 00298 if (MB_SUCCESS != rval) 00299 return rval; 00300 00301 value = s; 00302 return MB_SUCCESS; 00303 } 00304 00305 ErrorCode FileOptions::get_option( const char* name, const char*& value ) const 00306 { 00307 std::vector<const char*>::const_iterator i; 00308 for (i = mOptions.begin(); i != mOptions.end(); ++i) { 00309 const char* opt = *i; 00310 if (compare( name, opt )) { 00311 value = opt + strlen(name); 00312 // if compare returned true, next char after option 00313 // name must be either the null char or an equals symbol. 00314 if (*value == '=') 00315 ++value; 00316 00317 mSeen[i - mOptions.begin()] = true; 00318 return MB_SUCCESS; 00319 } 00320 } 00321 00322 return MB_ENTITY_NOT_FOUND; 00323 } 00324 00325 ErrorCode FileOptions::match_option( const char* name, 00326 const char* value ) const 00327 { 00328 int idx; 00329 const char* array[] = { value, NULL }; 00330 return match_option( name, array, idx ); 00331 } 00332 00333 ErrorCode FileOptions::match_option( const char* name, 00334 const char* const* values, 00335 int& index ) const 00336 { 00337 const char* optval; 00338 ErrorCode rval = get_option( name, optval ); 00339 if (MB_SUCCESS != rval) 00340 return rval; 00341 00342 for (index = 0; values[index]; ++index) 00343 if (compare( optval, values[index] )) 00344 return MB_SUCCESS; 00345 00346 index = -1; 00347 return MB_FAILURE; 00348 } 00349 00350 ErrorCode FileOptions::get_toggle_option( const char* name, 00351 bool default_value, 00352 bool& value ) const 00353 { 00354 static const char* values[] = { 00355 "true", "yes", "1", "on", 00356 "false", "no", "0", "off", 00357 0 }; 00358 const int num_true = 4; 00359 00360 int index; 00361 ErrorCode result = match_option( name, values, index ); 00362 if (result == MB_SUCCESS) { 00363 value = index < num_true; 00364 } 00365 else if (result == MB_ENTITY_NOT_FOUND) { 00366 value = default_value; 00367 result = MB_SUCCESS; 00368 } 00369 else { 00370 result = MB_TYPE_OUT_OF_RANGE; 00371 } 00372 00373 return result; 00374 } 00375 00376 00377 bool FileOptions::compare( const char* name, const char* option ) 00378 { 00379 while (!strempty(name) && toupper(*name) == toupper(*option)) { 00380 ++name; 00381 ++option; 00382 } 00383 // match if name matched option for length of name, 00384 // and option either matched entirely or matches up to 00385 // and equals sign. 00386 return strempty(name) && (strempty(option) || *option == '='); 00387 } 00388 00389 void FileOptions::get_options( std::vector<std::string>& list ) const 00390 { 00391 list.clear(); 00392 list.resize( mOptions.size() ); 00393 std::copy( mOptions.begin(), mOptions.end(), list.begin() ); 00394 } 00395 00396 bool FileOptions::all_seen() const 00397 { 00398 return std::find( mSeen.begin(), mSeen.end(), false ) == mSeen.end(); 00399 } 00400 00401 void FileOptions::mark_all_seen() const 00402 { 00403 mSeen.clear(); 00404 mSeen.resize( mOptions.size(), true ); 00405 } 00406 00407 ErrorCode FileOptions::get_unseen_option( std::string& name ) const 00408 { 00409 std::vector<bool>::iterator i = std::find( mSeen.begin(), mSeen.end(), false ); 00410 if (i == mSeen.end()) { 00411 name.clear(); 00412 return MB_ENTITY_NOT_FOUND; 00413 } 00414 00415 const char* opt = mOptions[i - mSeen.begin()]; 00416 const char* end = strchr( opt, '=' ); 00417 name = end ? std::string(opt, end-opt) : std::string(opt); 00418 return MB_SUCCESS; 00419 } 00420 00421 } // namespace moab 00422 00423 #ifdef TEST 00424 00425 using namespace moab; 00426 00427 #include <iostream> 00428 00429 #define CHECK(A) \ 00430 if (MB_SUCCESS != (A)) { \ 00431 std::cerr << "Failure at line " << __LINE__ << ": error code " << (A) << std::endl; \ 00432 return 1; \ 00433 } 00434 00435 #define EQUAL(A,B) \ 00436 if (A != B) { \ 00437 std::cerr << "Failure at line " << __LINE__ << ": expected " << (B) << " but got " << (A) << std::endl; \ 00438 return 2; \ 00439 } 00440 00441 int main() 00442 { 00443 FileOptions tool( "INT1=1;NUL1;STR1=ABC;DBL1=1.0;dbl2=2.0;DBL3=3.0;INT2=2;nul2;NUL3;INT3=3;str2=once upon a time;str3==fubar=;;INTS=1-3,5,6;DBLS=1.0,2.0, 3.0;STRS=var1, var2_var2;STRS2=" ); 00444 00445 std::string s; 00446 int i; 00447 double d; 00448 ErrorCode rval; 00449 00450 // test basic get_option method without deleting entry 00451 rval = tool.get_option( "STR1", s ); 00452 CHECK(rval); 00453 EQUAL( s, "ABC" ); 00454 00455 // test basic get_option method again, this time deleting the entry 00456 rval = tool.get_option( "STR1", s ); 00457 CHECK(rval); 00458 EQUAL( s, "ABC" ); 00459 00460 // test basig get_option method with a null option 00461 rval = tool.get_option( "NUL2", s ); 00462 CHECK( rval ); 00463 EQUAL( s.empty(), true ); 00464 00465 00466 // test null option 00467 rval = tool.get_null_option( "nul1" ); 00468 CHECK( rval ); 00469 00470 // try null option method on non-null value 00471 rval = tool.get_null_option( "INT1" ) ; 00472 EQUAL( rval, MB_TYPE_OUT_OF_RANGE) ; 00473 00474 00475 // test integer option 00476 rval = tool.get_int_option( "int1", i ); 00477 CHECK( rval ); 00478 EQUAL( i, 1 ); 00479 00480 rval = tool.get_int_option( "int2", i ); 00481 CHECK( rval ); 00482 EQUAL( i, 2 ); 00483 00484 // test integer option on non-integer value 00485 rval = tool.get_int_option( "dbl2", i ); 00486 EQUAL( rval, MB_TYPE_OUT_OF_RANGE ); 00487 00488 // test integer option on null value 00489 rval = tool.get_int_option( "NUL3", i); 00490 EQUAL( rval, MB_TYPE_OUT_OF_RANGE ); 00491 00492 // test double option 00493 rval = tool.get_real_option( "dbl1", d ); 00494 CHECK( rval ); 00495 EQUAL( d, 1.0 ); 00496 00497 rval = tool.get_real_option( "dbl2", d ); 00498 CHECK( rval ); 00499 EQUAL( d, 2.0 ); 00500 00501 rval = tool.get_real_option( "int3", d ); 00502 CHECK( rval ); 00503 EQUAL( d, 3.0 ); 00504 00505 // test real option on non-real value 00506 rval = tool.get_real_option( "str2", d ); 00507 EQUAL( rval, MB_TYPE_OUT_OF_RANGE ); 00508 00509 00510 // test real option on null value 00511 rval = tool.get_real_option( "NUL3", d ); 00512 EQUAL( rval, MB_TYPE_OUT_OF_RANGE ); 00513 00514 // test get a simple string option 00515 rval = tool.get_str_option( "DBL3", s ); 00516 CHECK( rval ); 00517 EQUAL( s, "3.0" ); 00518 00519 // test get a string with spaces 00520 rval = tool.get_str_option("STR2", s ); 00521 CHECK( rval ); 00522 EQUAL( s, "once upon a time" ); 00523 00524 // try to get a string value for a null option 00525 rval = tool.get_str_option( "nul3", s ); 00526 EQUAL( rval, MB_TYPE_OUT_OF_RANGE ); 00527 00528 // We haven't looked at all of the options yet 00529 EQUAL( false, tool.all_seen() ); 00530 rval = tool.get_unseen_option( s ); 00531 CHECK( rval ); 00532 EQUAL( s, "str3" ); 00533 00534 // test options using generic get_option method 00535 00536 rval = tool.get_option( "NUL3", s ); 00537 CHECK( rval ); 00538 EQUAL( s.empty(), true ); 00539 00540 rval = tool.get_option( "STR3", s ); 00541 CHECK( rval ); 00542 EQUAL( s, "=fubar=" ); 00543 00544 // test size of options string 00545 unsigned l = tool.size(); 00546 EQUAL( l, 16u ); 00547 00548 // test ints option 00549 std::vector<int> ivals; 00550 rval = tool.get_ints_option("INTS", ivals); 00551 CHECK( rval ); 00552 EQUAL(5, ivals.size()); 00553 EQUAL(1, ivals[0]); 00554 EQUAL(2, ivals[1]); 00555 EQUAL(3, ivals[2]); 00556 EQUAL(5, ivals[3]); 00557 EQUAL(6, ivals[4]); 00558 00559 // test dbls option 00560 std::vector<double> vals; 00561 rval = tool.get_reals_option("DBLS", vals); 00562 CHECK( rval ); 00563 EQUAL(3, vals.size()); 00564 EQUAL(1.0, vals[0]); 00565 EQUAL(2.0, vals[1]); 00566 EQUAL(3.0, vals[2]); 00567 00568 // test strs option 00569 std::vector<std::string> svals; 00570 rval = tool.get_strs_option("STRS", svals); 00571 CHECK( rval ); 00572 EQUAL(2, svals.size()); 00573 EQUAL("var1", svals[0]); 00574 EQUAL("var2_var2", svals[1]); 00575 00576 svals.clear(); 00577 rval = tool.get_strs_option("STRS2", svals); 00578 EQUAL( MB_TYPE_OUT_OF_RANGE, rval ); 00579 00580 // We requested every option 00581 EQUAL( true, tool.all_seen() ); 00582 rval = tool.get_unseen_option( s ); 00583 EQUAL( MB_ENTITY_NOT_FOUND, rval ); 00584 00585 // test alternate separator 00586 00587 FileOptions tool2( ";+OPT1=ABC+OPT2=" ); 00588 l = tool2.size(); 00589 EQUAL( l, 2 ); 00590 00591 // We haven't looked at all of the options yet 00592 EQUAL( false, tool2.all_seen() ); 00593 rval = tool2.get_unseen_option( s ); 00594 CHECK( rval ); 00595 EQUAL( s, "OPT1" ); 00596 00597 rval = tool2.get_option( "opt1", s ); 00598 CHECK( rval ); 00599 EQUAL( s, "ABC" ); 00600 00601 rval = tool2.get_option( "opt2", s ); 00602 CHECK( rval ); 00603 bool e = s.empty(); 00604 EQUAL( e, true ); 00605 00606 l = tool2.size(); 00607 EQUAL( l, 2 ); 00608 00609 // We requested every option 00610 EQUAL( true, tool2.all_seen() ); 00611 rval = tool2.get_unseen_option( s ); 00612 EQUAL( MB_ENTITY_NOT_FOUND, rval ); 00613 00614 00615 // test empty options string 00616 00617 FileOptions tool3( ";;;;" ); 00618 e = tool3.empty(); 00619 EQUAL( e, true ); 00620 l = tool3.size(); 00621 EQUAL( l, 0 ); 00622 EQUAL( true, tool3.all_seen() ); 00623 00624 FileOptions tool4(NULL); 00625 e = tool4.empty(); 00626 EQUAL( e, true ); 00627 l = tool4.size(); 00628 EQUAL( l, 0 ); 00629 EQUAL( true, tool4.all_seen() ); 00630 00631 FileOptions tool5(";+"); 00632 e = tool5.empty(); 00633 EQUAL( e, true ); 00634 l = tool5.size(); 00635 EQUAL( l, 0 ); 00636 EQUAL( true, tool5.all_seen() ); 00637 00638 // test copy constructor 00639 00640 FileOptions tool6( tool2 ); 00641 00642 rval = tool6.get_option( "opt1", s ); 00643 CHECK( rval ); 00644 EQUAL( s, "ABC" ); 00645 00646 rval = tool6.get_option( "opt2", s ); 00647 CHECK( rval ); 00648 e = s.empty(); 00649 EQUAL( e, true ); 00650 00651 l = tool6.size(); 00652 EQUAL( l, 2 ); 00653 00654 FileOptions tool7( tool5 ); 00655 e = tool7.empty(); 00656 EQUAL( e, true ); 00657 l = tool7.size(); 00658 EQUAL( l, 0 ); 00659 00660 // test assignment operator 00661 00662 FileOptions tool8( tool2 ); 00663 tool8 = tool; 00664 EQUAL( tool8.size(), tool.size() ); 00665 00666 return 0; 00667 } 00668 00669 #endif