cgma
FileOptions.cpp
Go to the documentation of this file.
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
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines