cgma
|
00001 00006 #include "FileOptions.hpp" 00007 00008 #include <ctype.h> 00009 #include <stdlib.h> 00010 #include <string.h> 00011 00012 const char DEFAULT_SEPARATOR = ';'; 00013 00014 static inline bool strempty( const char* s ) { return !*s; } 00015 00016 FileOptions::FileOptions( const char* str ) 00017 : mData(0) 00018 { 00019 // if option string is null, just return 00020 if (!str) 00021 return; 00022 00023 // check if alternate separator is specified 00024 char separator[2] = { DEFAULT_SEPARATOR, '\0' }; 00025 if (*str == DEFAULT_SEPARATOR) { 00026 ++str; 00027 if (strempty(str)) 00028 return; 00029 separator[0] = *str; 00030 ++str; 00031 } 00032 00033 // don't bother allocating copy of input string if 00034 // input string is empty. 00035 if (!strempty(str)) 00036 { 00037 // tokenize at separator character 00038 mData = strdup( str ); 00039 for (char* i = strtok( mData, separator ); i; i = strtok( 0, separator )) 00040 if (!strempty(i)) // skip empty strings 00041 mOptions.push_back( i ); 00042 } 00043 } 00044 00045 FileOptions::FileOptions( const FileOptions& copy ) : 00046 mData(0), mOptions( copy.mOptions.size() ) 00047 { 00048 if (!copy.mOptions.empty()) { 00049 const char* last = copy.mOptions.back(); 00050 const char* endptr = last + strlen(last) + 1; 00051 size_t len = endptr - copy.mData; 00052 mData = (char*)malloc( len ); 00053 memcpy( mData, copy.mData, len ); 00054 for (size_t i = 0; i < mOptions.size(); ++i) 00055 mOptions[i] = mData + (copy.mOptions[i] - copy.mData); 00056 } 00057 } 00058 00059 FileOptions& FileOptions::operator=( const FileOptions& copy ) 00060 { 00061 free( mData ); 00062 mData = 0; 00063 mOptions.resize( copy.mOptions.size() ); 00064 00065 if (!copy.mOptions.empty()) { 00066 const char* last = copy.mOptions.back(); 00067 const char* endptr = last + strlen(last) + 1; 00068 size_t len = endptr - copy.mData; 00069 mData = (char*)malloc( len ); 00070 memcpy( mData, copy.mData, len ); 00071 for (size_t i = 0; i < mOptions.size(); ++i) 00072 mOptions[i] = mData + (copy.mOptions[i] - copy.mData); 00073 } 00074 00075 return *this; 00076 } 00077 00078 FileOptions::~FileOptions() 00079 { 00080 free( mData ); 00081 } 00082 00083 FOErrorCode FileOptions::get_null_option( const char* name ) const 00084 { 00085 const char* s; 00086 FOErrorCode rval = get_option( name, s ); 00087 if (FO_SUCCESS != rval) 00088 return rval; 00089 return strempty(s) ? FO_SUCCESS : FO_TYPE_OUT_OF_RANGE; 00090 } 00091 00092 FOErrorCode FileOptions::get_int_option( const char* name, int& value ) const 00093 { 00094 const char* s; 00095 FOErrorCode rval = get_option( name, s ); 00096 if (FO_SUCCESS != rval) 00097 return rval; 00098 00099 // empty string 00100 if (strempty(s)) 00101 return FO_TYPE_OUT_OF_RANGE; 00102 00103 // parse value 00104 char* endptr; 00105 long int pval = strtol( s, &endptr, 0 ); 00106 if (!strempty(endptr)) // syntax error 00107 return FO_TYPE_OUT_OF_RANGE; 00108 00109 // check for overflow (parsing long int, returning int) 00110 value = pval; 00111 if (pval != (long int)value) 00112 return FO_TYPE_OUT_OF_RANGE; 00113 00114 return FO_SUCCESS; 00115 } 00116 00117 FOErrorCode FileOptions::get_ints_option( const char* name, 00118 std::vector<int>& values) const 00119 { 00120 const char* s; 00121 FOErrorCode rval = get_option( name, s ); 00122 if (FO_SUCCESS != rval) 00123 return rval; 00124 00125 // empty string 00126 if (strempty(s)) 00127 return FO_TYPE_OUT_OF_RANGE; 00128 00129 // parse values 00130 while (!strempty(s)) { 00131 char* endptr; 00132 long int sval = strtol( s, &endptr, 0 ); 00133 00134 #define EATSPACE(a) while ((!strncmp(a, " ", 1) || \ 00135 !strncmp(a, ",", 1)) && !strempty(a)) a++; 00136 //EATSPACE(endptr); 00137 00138 while ((!strncmp(endptr, " ", 1) || 00139 !strncmp(endptr, ",", 1)) && !strempty(endptr)) { 00140 endptr++; 00141 } 00142 00143 long int eval = sval; 00144 if (!strcmp(endptr, "-")) { 00145 endptr++; 00146 s = endptr; 00147 eval = strtol(s, &endptr, 0); 00148 EATSPACE(endptr); 00149 } 00150 00151 // check for overflow (parsing long int, returning int) 00152 int value = sval; 00153 if (sval != (long int)value) 00154 return FO_TYPE_OUT_OF_RANGE; 00155 value = eval; 00156 if (eval != (long int)value) 00157 return FO_TYPE_OUT_OF_RANGE; 00158 00159 for (int i = sval; i <= eval; i++) 00160 values.push_back(i); 00161 00162 s = endptr; 00163 } 00164 00165 return FO_SUCCESS; 00166 } 00167 00168 FOErrorCode FileOptions::get_real_option ( const char* name, double& value ) const 00169 { 00170 const char* s; 00171 FOErrorCode rval = get_option( name, s ); 00172 if (FO_SUCCESS != rval) 00173 return rval; 00174 00175 // empty string 00176 if (strempty(s)) 00177 return FO_TYPE_OUT_OF_RANGE; 00178 00179 // parse value 00180 char* endptr; 00181 value = strtod( s, &endptr ); 00182 if (!strempty(endptr)) // syntax error 00183 return FO_TYPE_OUT_OF_RANGE; 00184 00185 return FO_SUCCESS; 00186 } 00187 00188 FOErrorCode FileOptions::get_str_option( const char* name, std::string& value ) const 00189 { 00190 const char* s; 00191 FOErrorCode rval = get_option( name, s ); 00192 if (FO_SUCCESS != rval) 00193 return rval; 00194 if (strempty(s)) 00195 return FO_TYPE_OUT_OF_RANGE; 00196 value = s; 00197 return FO_SUCCESS; 00198 } 00199 00200 FOErrorCode FileOptions::get_option( const char* name, std::string& value ) const 00201 { 00202 const char* s; 00203 FOErrorCode rval = get_option( name, s ); 00204 if (FO_SUCCESS != rval) 00205 return rval; 00206 00207 value = s; 00208 return FO_SUCCESS; 00209 } 00210 00211 FOErrorCode FileOptions::get_option( const char* name, const char*& value ) const 00212 { 00213 std::vector<const char*>::const_iterator i; 00214 for (i = mOptions.begin(); i != mOptions.end(); ++i) { 00215 const char* opt = *i; 00216 if (compare( name, opt )) { 00217 value = opt + strlen(name); 00218 // if compare returned true, next char after option 00219 // name must be either the null char or an equals symbol. 00220 if (*value == '=') 00221 ++value; 00222 00223 return FO_SUCCESS; 00224 } 00225 } 00226 00227 return FO_ENTITY_NOT_FOUND; 00228 } 00229 00230 FOErrorCode FileOptions::match_option( const char* name, 00231 const char* value ) const 00232 { 00233 int idx; 00234 const char* array[] = { value, NULL }; 00235 return match_option( name, array, idx ); 00236 } 00237 00238 FOErrorCode FileOptions::match_option( const char* name, 00239 const char* const* values, 00240 int& index ) const 00241 { 00242 const char* optval; 00243 FOErrorCode rval = get_option( name, optval ); 00244 if (FO_SUCCESS != rval) 00245 return rval; 00246 00247 for (index = 0; values[index]; ++index) 00248 if (compare( optval, values[index] )) 00249 return FO_SUCCESS; 00250 00251 index = -1; 00252 return FO_FAILURE; 00253 } 00254 00255 00256 bool FileOptions::compare( const char* name, const char* option ) 00257 { 00258 while (!strempty(name) && toupper(*name) == toupper(*option)) { 00259 ++name; 00260 ++option; 00261 } 00262 // match if name matched option for length of name, 00263 // and option either matched entirely or matches up to 00264 // and equals sign. 00265 return strempty(name) && (strempty(option) || *option == '='); 00266 } 00267 00268 void FileOptions::get_options( std::vector<std::string>& list ) const 00269 { 00270 list.clear(); 00271 list.resize( mOptions.size() ); 00272 std::copy( mOptions.begin(), mOptions.end(), list.begin() ); 00273 } 00274 00275 #ifdef TEST 00276 00277 #include <iostream> 00278 00279 #define CHECK(A) \ 00280 if (FO_SUCCESS != (A)) { \ 00281 std::cerr << "Failure at line " << __LINE__ << ": error code " << (A) << std::endl; \ 00282 return 1; \ 00283 } 00284 00285 #define EQUAL(A,B) \ 00286 if (A != B) { \ 00287 std::cerr << "Failure at line " << __LINE__ << ": expected " << (B) << " but got " << (A) << std::endl; \ 00288 return 2; \ 00289 } 00290 00291 int main() 00292 { 00293 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=;;" ); 00294 00295 std::string s; 00296 int i; 00297 double d; 00298 FOErrorCodeyy rval; 00299 00300 // test basic get_option method without deleting entry 00301 rval = tool.get_option( "STR1", s ); 00302 CHECK(rval); 00303 EQUAL( s, "ABC" ); 00304 00305 // test basic get_option method again, this time deleting the entry 00306 rval = tool.get_option( "STR1", s ); 00307 CHECK(rval); 00308 EQUAL( s, "ABC" ); 00309 00310 // test basig get_option method with a null option 00311 rval = tool.get_option( "NUL2", s ); 00312 CHECK( rval ); 00313 EQUAL( s.empty(), true ); 00314 00315 00316 // test null option 00317 rval = tool.get_null_option( "nul1" ); 00318 CHECK( rval ); 00319 00320 // try null option method on non-null value 00321 rval = tool.get_null_option( "INT1" ) ; 00322 EQUAL( rval, FO_TYPE_OUT_OF_RANGE) ; 00323 00324 00325 // test integer option 00326 rval = tool.get_int_option( "int1", i ); 00327 CHECK( rval ); 00328 EQUAL( i, 1 ); 00329 00330 rval = tool.get_int_option( "int2", i ); 00331 CHECK( rval ); 00332 EQUAL( i, 2 ); 00333 00334 // test integer option on non-integer value 00335 rval = tool.get_int_option( "dbl2", i ); 00336 EQUAL( rval, FO_TYPE_OUT_OF_RANGE ); 00337 00338 // test integer option on null value 00339 rval = tool.get_int_option( "NUL3", i); 00340 EQUAL( rval, FO_TYPE_OUT_OF_RANGE ); 00341 00342 // test double option 00343 rval = tool.get_real_option( "dbl1", d ); 00344 CHECK( rval ); 00345 EQUAL( d, 1.0 ); 00346 00347 rval = tool.get_real_option( "dbl2", d ); 00348 CHECK( rval ); 00349 EQUAL( d, 2.0 ); 00350 00351 rval = tool.get_real_option( "int3", d ); 00352 CHECK( rval ); 00353 EQUAL( d, 3.0 ); 00354 00355 // test real option on non-real value 00356 rval = tool.get_real_option( "str2", d ); 00357 EQUAL( rval, FO_TYPE_OUT_OF_RANGE ); 00358 00359 00360 // test real option on null value 00361 rval = tool.get_real_option( "NUL3", d ); 00362 EQUAL( rval, FO_TYPE_OUT_OF_RANGE ); 00363 00364 // test get a simple string option 00365 rval = tool.get_str_option( "DBL3", s ); 00366 CHECK( rval ); 00367 EQUAL( s, "3.0" ); 00368 00369 // test get a string with spaces 00370 rval = tool.get_str_option("STR2", s ); 00371 CHECK( rval ); 00372 EQUAL( s, "once upon a time" ); 00373 00374 // try to get a string value for a null option 00375 rval = tool.get_str_option( "nul3", s ); 00376 EQUAL( rval, FO_TYPE_OUT_OF_RANGE ); 00377 00378 // test options using generic get_option method 00379 00380 rval = tool.get_option( "NUL3", s ); 00381 CHECK( rval ); 00382 EQUAL( s.empty(), true ); 00383 00384 rval = tool.get_option( "STR3", s ); 00385 CHECK( rval ); 00386 EQUAL( s, "=fubar=" ); 00387 00388 // test size of options string 00389 unsigned l = tool.size(); 00390 EQUAL( l, 12u ); 00391 00392 00393 // test alternate separator 00394 00395 FileOptions tool2( ";+OPT1=ABC+OPT2=" ); 00396 l = tool2.size(); 00397 EQUAL( l, 2 ); 00398 00399 rval = tool2.get_option( "opt1", s ); 00400 CHECK( rval ); 00401 EQUAL( s, "ABC" ); 00402 00403 rval = tool2.get_option( "opt2", s ); 00404 CHECK( rval ); 00405 bool e = s.empty(); 00406 EQUAL( e, true ); 00407 00408 l = tool2.size(); 00409 EQUAL( l, 2 ); 00410 00411 00412 // test empty options string 00413 00414 FileOptions tool3( ";;;;" ); 00415 e = tool3.empty(); 00416 EQUAL( e, true ); 00417 l = tool3.size(); 00418 EQUAL( l, 0 ); 00419 00420 FileOptions tool4(NULL); 00421 e = tool4.empty(); 00422 EQUAL( e, true ); 00423 l = tool4.size(); 00424 EQUAL( l, 0 ); 00425 00426 FileOptions tool5(";+"); 00427 e = tool5.empty(); 00428 EQUAL( e, true ); 00429 l = tool5.size(); 00430 EQUAL( l, 0 ); 00431 00432 // test copy constructor 00433 00434 FileOptions tool6( tool2 ); 00435 00436 rval = tool6.get_option( "opt1", s ); 00437 CHECK( rval ); 00438 EQUAL( s, "ABC" ); 00439 00440 rval = tool6.get_option( "opt2", s ); 00441 CHECK( rval ); 00442 e = s.empty(); 00443 EQUAL( e, true ); 00444 00445 l = tool6.size(); 00446 EQUAL( l, 2 ); 00447 00448 FileOptions tool7( tool5 ); 00449 e = tool7.empty(); 00450 EQUAL( e, true ); 00451 l = tool7.size(); 00452 EQUAL( l, 0 ); 00453 00454 // test assignment operator 00455 00456 FileOptions tool8( tool2 ); 00457 tool8 = tool; 00458 EQUAL( tool8.size(), tool.size() ); 00459 00460 return 0; 00461 } 00462 00463 #endif