1 /*
2  * MOAB, a Mesh-Oriented datABase, is a software component for creating,
3  * storing and accessing finite element mesh data.
4  *
5  * Copyright 2004 Sandia Corporation.  Under the terms of Contract
6  * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government
7  * retains certain rights in this software.
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  */
15 
16 /**\file FileOptions.cpp
17  *\author Jason Kraftcheck (kraftche@cae.wisc.edu)
18  *\date 2007-08-21
19  */
20 
21 #include "moab/FileOptions.hpp"
22 
23 #include <ctype.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <algorithm>
27 
28 namespace moab {
29 
30 const char DEFAULT_SEPARATOR = ';';
31 
strempty(const char * s)32 static inline bool strempty( const char* s ) { return !*s; }
33 
FileOptions(const char * str)34 FileOptions::FileOptions( const char* str )
35   : mData(0)
36 {
37     // if option string is null, just return
38   if (!str)
39     return;
40 
41     // check if alternate separator is specified
42   char separator[2] = { DEFAULT_SEPARATOR, '\0' };
43   if (*str == DEFAULT_SEPARATOR) {
44     ++str;
45     if (strempty(str))
46       return;
47     separator[0] = *str;
48     ++str;
49   }
50 
51     // don't bother allocating copy of input string if
52     // input string is empty.
53   if (!strempty(str))
54   {
55        // tokenize at separator character
56     mData = strdup( str );
57     for (char* i = strtok( mData, separator ); i; i = strtok( 0, separator ))
58       if (!strempty(i)) // skip empty strings
59         mOptions.push_back( i );
60   }
61 
62   mSeen.resize( mOptions.size(), false );
63 }
64 
FileOptions(const FileOptions & copy)65 FileOptions::FileOptions( const FileOptions& copy ) :
66   mData(0), mOptions( copy.mOptions.size() )
67 {
68   if (!copy.mOptions.empty()) {
69     const char* last = copy.mOptions.back();
70     const char* endptr = last + strlen(last) + 1;
71     size_t len = endptr - copy.mData;
72     mData = (char*)malloc( len );
73     memcpy( mData, copy.mData, len );
74     for (size_t i = 0; i < mOptions.size(); ++i)
75       mOptions[i] = mData + (copy.mOptions[i] - copy.mData);
76   }
77   mSeen = copy.mSeen;
78 }
79 
operator =(const FileOptions & copy)80 FileOptions& FileOptions::operator=( const FileOptions& copy )
81 {
82   // Check for self-assignment
83   if (this == &copy)
84     return *this;
85 
86   free( mData );
87   mData = 0;
88   mOptions.resize( copy.mOptions.size() );
89 
90   if (!copy.mOptions.empty()) {
91     const char* last = copy.mOptions.back();
92     const char* endptr = last + strlen(last) + 1;
93     size_t len = endptr - copy.mData;
94     mData = (char*)malloc( len );
95     memcpy( mData, copy.mData, len );
96     for (size_t i = 0; i < mOptions.size(); ++i)
97       mOptions[i] = mData + (copy.mOptions[i] - copy.mData);
98   }
99 
100   mSeen = copy.mSeen;
101   return *this;
102 }
103 
~FileOptions()104 FileOptions::~FileOptions()
105 {
106   free( mData );
107 }
108 
get_null_option(const char * name) const109 ErrorCode FileOptions::get_null_option( const char* name ) const
110 {
111   const char* s;
112   ErrorCode rval = get_option( name, s );
113   if (MB_SUCCESS != rval)
114     return rval;
115   return strempty(s) ? MB_SUCCESS : MB_TYPE_OUT_OF_RANGE;
116 }
117 
get_int_option(const char * name,int & value) const118 ErrorCode FileOptions::get_int_option( const char* name, int& value ) const
119 {
120   const char* s;
121   ErrorCode rval = get_option( name, s );
122   if (MB_SUCCESS != rval)
123     return rval;
124 
125     // empty string
126   if (strempty(s))
127     return MB_TYPE_OUT_OF_RANGE;
128 
129     // parse value
130   char* endptr;
131   long int pval = strtol( s, &endptr, 0 );
132   if (!strempty(endptr)) // syntax error
133     return MB_TYPE_OUT_OF_RANGE;
134 
135     // check for overflow (parsing long int, returning int)
136   value = pval;
137   if (pval != (long int)value)
138     return MB_TYPE_OUT_OF_RANGE;
139 
140   return MB_SUCCESS;
141 }
142 
get_int_option(const char * name,int default_val,int & value) const143 ErrorCode FileOptions::get_int_option( const char* name,
144                                        int default_val,
145                                        int& value ) const
146 {
147   const char* s;
148   ErrorCode rval = get_option( name, s );
149   if (MB_SUCCESS != rval)
150     return rval;
151 
152     // empty string
153   if (strempty(s)) {
154     value = default_val;
155     return MB_SUCCESS;
156   }
157 
158     // parse value
159   char* endptr;
160   long int pval = strtol( s, &endptr, 0 );
161   if (!strempty(endptr)) // syntax error
162     return MB_TYPE_OUT_OF_RANGE;
163 
164     // check for overflow (parsing long int, returning int)
165   value = pval;
166   if (pval != (long int)value)
167     return MB_TYPE_OUT_OF_RANGE;
168 
169   return MB_SUCCESS;
170 }
171 
get_ints_option(const char * name,std::vector<int> & values) const172 ErrorCode FileOptions::get_ints_option( const char* name,
173                                           std::vector<int>& values) const
174 {
175   const char* s;
176   ErrorCode rval = get_option( name, s );
177   if (MB_SUCCESS != rval)
178     return rval;
179 
180     // empty string
181   if (strempty(s))
182     return MB_TYPE_OUT_OF_RANGE;
183 
184     // parse values
185   while (!strempty(s)) {
186     char* endptr;
187     long int sval = strtol( s, &endptr, 0 );
188 
189 #define EATSPACE(a) while ((*a == ' ' ||          \
190                             *a == ',') && !strempty(a)) a++;
191     EATSPACE(endptr);
192     long int eval = sval;
193     if (*endptr == '-') {
194       endptr++;
195       s = endptr;
196       eval = strtol(s, &endptr, 0);
197       EATSPACE(endptr);
198     }
199 
200       // check for overflow (parsing long int, returning int)
201     int value = sval;
202     if (sval != (long int)value)
203       return MB_TYPE_OUT_OF_RANGE;
204     value = eval;
205     if (eval != (long int)value)
206       return MB_TYPE_OUT_OF_RANGE;
207 
208     for (int i = sval; i <= eval; i++)
209       values.push_back(i);
210 
211     s = endptr;
212   }
213 
214   return MB_SUCCESS;
215 }
216 
get_reals_option(const char * name,std::vector<double> & values) const217 ErrorCode FileOptions::get_reals_option( const char* name,
218                                           std::vector<double>& values) const
219 {
220   const char* s;
221   ErrorCode rval = get_option( name, s );
222   if (MB_SUCCESS != rval)
223     return rval;
224 
225     // empty string
226   if (strempty(s))
227     return MB_TYPE_OUT_OF_RANGE;
228 
229     // parse values
230   while (!strempty(s)) {
231     char* endptr;
232     double sval = strtod( s, &endptr);
233 
234     EATSPACE(endptr);
235     values.push_back(sval);
236 
237     s = endptr;
238   }
239 
240   return MB_SUCCESS;
241 }
242 
get_real_option(const char * name,double & value) const243 ErrorCode FileOptions::get_real_option ( const char* name, double& value ) const
244 {
245   const char* s;
246   ErrorCode rval = get_option( name, s );
247   if (MB_SUCCESS != rval)
248     return rval;
249 
250     // empty string
251   if (strempty(s))
252     return MB_TYPE_OUT_OF_RANGE;
253 
254     // parse value
255   char* endptr;
256   value = strtod( s, &endptr );
257   if (!strempty(endptr)) // syntax error
258     return MB_TYPE_OUT_OF_RANGE;
259 
260   return MB_SUCCESS;
261 }
262 
get_strs_option(const char * name,std::vector<std::string> & values) const263 ErrorCode FileOptions::get_strs_option( const char* name,
264                                         std::vector<std::string>& values) const
265 {
266   const char* s;
267   ErrorCode rval = get_option( name, s );
268   if (MB_SUCCESS != rval)
269     return rval;
270 
271     // empty string
272   if (strempty(s))
273     return MB_TYPE_OUT_OF_RANGE;
274 
275     // parse values
276   char separator[3] = { ' ', ',', '\0' };
277   char *tmp_str = strdup(s);
278   for (char* i = strtok( tmp_str, separator ); i; i = strtok( 0, separator ))
279     if (!strempty(i)) // skip empty strings
280       values.push_back( std::string(i));
281   free(tmp_str);
282 
283   return MB_SUCCESS;
284 }
285 
get_str_option(const char * name,std::string & value) const286 ErrorCode FileOptions::get_str_option( const char* name, std::string& value ) const
287 {
288   const char* s;
289   ErrorCode rval = get_option( name, s );
290   if (MB_SUCCESS != rval)
291     return rval;
292   if (strempty(s))
293     return MB_TYPE_OUT_OF_RANGE;
294   value = s;
295   return MB_SUCCESS;
296 }
297 
get_option(const char * name,std::string & value) const298 ErrorCode FileOptions::get_option( const char* name, std::string& value ) const
299 {
300   const char* s;
301   ErrorCode rval = get_option( name, s );
302   if (MB_SUCCESS != rval)
303     return rval;
304 
305   value = s;
306   return MB_SUCCESS;
307 }
308 
get_option(const char * name,const char * & value) const309 ErrorCode FileOptions::get_option( const char* name, const char*& value ) const
310 {
311   std::vector<const char*>::const_iterator i;
312   for (i = mOptions.begin(); i != mOptions.end(); ++i) {
313     const char* opt = *i;
314     if (compare( name, opt )) {
315       value = opt + strlen(name);
316         // if compare returned true, next char after option
317         // name must be either the null char or an equals symbol.
318       if (*value == '=')
319         ++value;
320 
321       mSeen[i - mOptions.begin()] = true;
322       return MB_SUCCESS;
323     }
324   }
325 
326   return MB_ENTITY_NOT_FOUND;
327 }
328 
match_option(const char * name,const char * value) const329 ErrorCode FileOptions::match_option( const char* name,
330                                        const char* value ) const
331 {
332   int idx;
333   const char* array[] = { value, NULL };
334   return match_option( name, array, idx );
335 }
336 
match_option(const char * name,const char * const * values,int & index) const337 ErrorCode FileOptions::match_option( const char* name,
338                                        const char* const* values,
339                                        int& index ) const
340 {
341   const char* optval;
342   ErrorCode rval = get_option( name, optval );
343   if (MB_SUCCESS != rval)
344     return rval;
345 
346   for (index = 0; values[index]; ++index)
347     if (compare( optval, values[index] ))
348       return MB_SUCCESS;
349 
350   index = -1;
351   return MB_FAILURE;
352 }
353 
get_toggle_option(const char * name,bool default_value,bool & value) const354 ErrorCode FileOptions::get_toggle_option( const char* name,
355                                           bool default_value,
356                                           bool& value ) const
357 {
358   static const char* values[] = {
359     "true",  "yes", "1", "on",
360     "false", "no",  "0", "off",
361     0 };
362   const int num_true = 4;
363 
364   int index;
365   ErrorCode result = match_option( name, values, index );
366   if (result == MB_SUCCESS) {
367     value = index < num_true;
368   }
369   else if (result == MB_ENTITY_NOT_FOUND) {
370     value = default_value;
371     result = MB_SUCCESS;
372   }
373   else {
374     result = MB_TYPE_OUT_OF_RANGE;
375   }
376 
377   return result;
378 }
379 
380 
compare(const char * name,const char * option)381 bool FileOptions::compare( const char* name, const char* option )
382 {
383   while (!strempty(name) && toupper(*name) == toupper(*option)) {
384     ++name;
385     ++option;
386   }
387    // match if name matched option for length of name,
388    // and option either matched entirely or matches up to
389    // and equals sign.
390   return strempty(name) && (strempty(option) || *option == '=');
391 }
392 
get_options(std::vector<std::string> & list) const393 void FileOptions::get_options( std::vector<std::string>& list ) const
394 {
395   list.clear();
396   list.resize( mOptions.size() );
397   std::copy( mOptions.begin(), mOptions.end(), list.begin() );
398 }
399 
all_seen() const400 bool FileOptions::all_seen() const
401 {
402   return std::find( mSeen.begin(), mSeen.end(), false ) == mSeen.end();
403 }
404 
mark_all_seen() const405 void FileOptions::mark_all_seen() const
406 {
407   mSeen.clear();
408   mSeen.resize( mOptions.size(), true );
409 }
410 
get_unseen_option(std::string & name) const411 ErrorCode FileOptions::get_unseen_option( std::string& name ) const
412 {
413   std::vector<bool>::iterator i = std::find( mSeen.begin(), mSeen.end(), false );
414   if (i == mSeen.end()) {
415     name.clear();
416     return MB_ENTITY_NOT_FOUND;
417   }
418 
419   const char* opt = mOptions[i - mSeen.begin()];
420   const char* end = strchr( opt, '=' );
421   name = end ? std::string(opt, end-opt) : std::string(opt);
422   return MB_SUCCESS;
423 }
424 
425 } // namespace moab
426 
427 #ifdef TEST
428 
429 using namespace moab;
430 
431 #include <iostream>
432 
433 #define CHECK(A) \
434   if (MB_SUCCESS != (A)) { \
435     std::cerr << "Failure at line " << __LINE__ << ": error code " << (A) << std::endl; \
436     return 1; \
437   }
438 
439 #define EQUAL(A,B) \
440   if (A != B) { \
441     std::cerr << "Failure at line " << __LINE__ << ": expected " << (B) << " but got " << (A) << std::endl; \
442     return 2; \
443   }
444 
main()445 int main()
446 {
447   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=" );
448 
449   std::string s;
450   int i;
451   double d;
452   ErrorCode rval;
453 
454     // test basic get_option method without deleting entry
455   rval = tool.get_option( "STR1", s );
456   CHECK(rval);
457   EQUAL( s, "ABC" );
458 
459     // test basic get_option method again, this time deleting the entry
460   rval = tool.get_option( "STR1", s );
461   CHECK(rval);
462   EQUAL( s, "ABC" );
463 
464     // test basig get_option method with a null option
465   rval = tool.get_option( "NUL2", s );
466   CHECK( rval );
467   EQUAL( s.empty(), true );
468 
469 
470     // test null option
471   rval = tool.get_null_option( "nul1" );
472   CHECK( rval );
473 
474     // try null option method on non-null value
475   rval = tool.get_null_option( "INT1" ) ;
476   EQUAL( rval, MB_TYPE_OUT_OF_RANGE) ;
477 
478 
479     // test integer option
480   rval = tool.get_int_option( "int1", i );
481   CHECK( rval );
482   EQUAL( i, 1 );
483 
484   rval = tool.get_int_option( "int2", i );
485   CHECK( rval );
486   EQUAL( i, 2 );
487 
488     // test integer option on non-integer value
489   rval = tool.get_int_option( "dbl2", i );
490   EQUAL( rval, MB_TYPE_OUT_OF_RANGE );
491 
492     // test integer option on null value
493   rval = tool.get_int_option( "NUL3", i);
494   EQUAL( rval, MB_TYPE_OUT_OF_RANGE );
495 
496     // test double option
497   rval = tool.get_real_option( "dbl1", d );
498   CHECK( rval );
499   EQUAL( d, 1.0 );
500 
501   rval = tool.get_real_option( "dbl2", d );
502   CHECK( rval );
503   EQUAL( d, 2.0 );
504 
505   rval = tool.get_real_option( "int3", d );
506   CHECK( rval );
507   EQUAL( d, 3.0 );
508 
509     // test real option on non-real value
510   rval = tool.get_real_option( "str2", d );
511   EQUAL( rval, MB_TYPE_OUT_OF_RANGE );
512 
513 
514     // test real option on null value
515   rval = tool.get_real_option( "NUL3", d );
516   EQUAL( rval, MB_TYPE_OUT_OF_RANGE );
517 
518     // test get a simple string option
519   rval = tool.get_str_option( "DBL3", s );
520   CHECK( rval );
521   EQUAL( s, "3.0" );
522 
523     // test get a string with spaces
524   rval = tool.get_str_option("STR2", s );
525   CHECK( rval );
526   EQUAL( s, "once upon a time" );
527 
528     // try to get a string value for a null option
529   rval = tool.get_str_option( "nul3", s );
530   EQUAL( rval, MB_TYPE_OUT_OF_RANGE );
531 
532     // We haven't looked at all of the options yet
533   EQUAL( false, tool.all_seen() );
534   rval = tool.get_unseen_option( s );
535   CHECK( rval );
536   EQUAL( s, "str3" );
537 
538     // test options using generic get_option method
539 
540   rval = tool.get_option( "NUL3", s );
541   CHECK( rval );
542   EQUAL( s.empty(), true );
543 
544   rval = tool.get_option( "STR3", s );
545   CHECK( rval );
546   EQUAL( s, "=fubar=" );
547 
548     // test size of options string
549   unsigned l = tool.size();
550   EQUAL( l, 16u );
551 
552     // test ints option
553   std::vector<int> ivals;
554   rval = tool.get_ints_option("INTS", ivals);
555   CHECK( rval );
556   EQUAL(5, ivals.size());
557   EQUAL(1, ivals[0]);
558   EQUAL(2, ivals[1]);
559   EQUAL(3, ivals[2]);
560   EQUAL(5, ivals[3]);
561   EQUAL(6, ivals[4]);
562 
563     // test dbls option
564   std::vector<double> vals;
565   rval = tool.get_reals_option("DBLS", vals);
566   CHECK( rval );
567   EQUAL(3, vals.size());
568   EQUAL(1.0, vals[0]);
569   EQUAL(2.0, vals[1]);
570   EQUAL(3.0, vals[2]);
571 
572     // test strs option
573   std::vector<std::string> svals;
574   rval = tool.get_strs_option("STRS", svals);
575   CHECK( rval );
576   EQUAL(2, svals.size());
577   EQUAL("var1", svals[0]);
578   EQUAL("var2_var2", svals[1]);
579 
580   svals.clear();
581   rval = tool.get_strs_option("STRS2", svals);
582   EQUAL( MB_TYPE_OUT_OF_RANGE, rval );
583 
584     // We requested every option
585   EQUAL( true, tool.all_seen() );
586   rval = tool.get_unseen_option( s );
587   EQUAL( MB_ENTITY_NOT_FOUND, rval );
588 
589     // test alternate separator
590 
591   FileOptions tool2( ";+OPT1=ABC+OPT2=" );
592   l = tool2.size();
593   EQUAL( l, 2 );
594 
595     // We haven't looked at all of the options yet
596   EQUAL( false, tool2.all_seen() );
597   rval = tool2.get_unseen_option( s );
598   CHECK( rval );
599   EQUAL( s, "OPT1" );
600 
601   rval = tool2.get_option( "opt1", s );
602   CHECK( rval );
603   EQUAL( s, "ABC" );
604 
605   rval = tool2.get_option( "opt2", s );
606   CHECK( rval );
607   bool e = s.empty();
608   EQUAL( e, true );
609 
610   l = tool2.size();
611   EQUAL( l, 2 );
612 
613     // We requested every option
614   EQUAL( true, tool2.all_seen() );
615   rval = tool2.get_unseen_option( s );
616   EQUAL( MB_ENTITY_NOT_FOUND, rval );
617 
618 
619     // test empty options string
620 
621   FileOptions tool3( ";;;;" );
622   e = tool3.empty();
623   EQUAL( e, true );
624   l = tool3.size();
625   EQUAL( l, 0 );
626   EQUAL( true, tool3.all_seen() );
627 
628   FileOptions tool4(NULL);
629   e = tool4.empty();
630   EQUAL( e, true );
631   l = tool4.size();
632   EQUAL( l, 0 );
633   EQUAL( true, tool4.all_seen() );
634 
635   FileOptions tool5(";+");
636   e = tool5.empty();
637   EQUAL( e, true );
638   l = tool5.size();
639   EQUAL( l, 0 );
640   EQUAL( true, tool5.all_seen() );
641 
642     // test copy constructor
643 
644   FileOptions tool6( tool2 );
645 
646   rval = tool6.get_option( "opt1", s );
647   CHECK( rval );
648   EQUAL( s, "ABC" );
649 
650   rval = tool6.get_option( "opt2", s );
651   CHECK( rval );
652   e = s.empty();
653   EQUAL( e, true );
654 
655   l = tool6.size();
656   EQUAL( l, 2 );
657 
658   FileOptions tool7( tool5 );
659   e = tool7.empty();
660   EQUAL( e, true );
661   l = tool7.size();
662   EQUAL( l, 0 );
663 
664     // test assignment operator
665 
666   FileOptions tool8( tool2 );
667   tool8 = tool;
668   EQUAL( tool8.size(), tool.size() );
669 
670   return 0;
671 }
672 
673 #endif
674