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 == ©)
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