1 /* *****************************************************************
2 MESQUITE -- The Mesh Quality Improvement Toolkit
3
4 Copyright 2007 Sandia National Laboratories. Developed at the
5 University of Wisconsin--Madison under SNL contract number
6 624796. The U.S. Government and the University of Wisconsin
7 retain certain rights to 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 This library is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Lesser General Public License for more details.
18
19 You should have received a copy of the GNU Lesser General Public License
20 (lgpl.txt) along with this library; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22
23 (2008) kraftche@cae.wisc.edu
24
25 ***************************************************************** */
26
27
28 /** \file CLArgs.cpp
29 * \brief
30 * \author Jason Kraftcheck
31 */
32
33 #include "Mesquite.hpp"
34 #include "CLArgs.hpp"
35 #include "CLArgFlag.hpp"
36 #include "ManPage.hpp"
37 #include <iostream>
38 #include <sstream>
39 #include <limits>
40 #include <stdlib.h>
41
42 const char HELP_FLAG = 'h';
43 const char MAN_FLAG = 'M';
44
45 class CLArgImpl
46 {
47 private:
48
49 std::vector<CLArgFlag*> mFlags;
50 std::vector< std::string > reqArgNames, optArgNames;
51 std::vector< std::string > reqArg, optArg;
52 std::string progName, shortDesc, longDesc;
53
54 public:
55
CLArgImpl(const char * progname,const char * brief,const char * desc)56 CLArgImpl( const char* progname, const char* brief, const char* desc )
57 : progName(progname), shortDesc(brief), longDesc(desc)
58 {}
59
add_req_arg(const char * name)60 void add_req_arg( const char* name )
61 { reqArgNames.push_back( name ); }
62
add_opt_arg(const char * name)63 void add_opt_arg( const char* name )
64 { optArgNames.push_back( name ); }
65
66 bool add_parsed_arg( const char* arg );
67
68 bool add_flag( CLArgFlag* flag );
69
70 CLArgFlag* find_flag( char flag );
find_flag(char flag) const71 const CLArgFlag* find_flag( char flag ) const
72 { return const_cast<CLArgImpl*>(this)->find_flag(flag); }
73
have_required_args() const74 bool have_required_args() const
75 { return reqArgNames.size() == reqArg.size(); }
76
get_args(std::vector<std::string> & result)77 void get_args( std::vector< std::string >& result )
78 { result = reqArg;
79 result.resize( reqArg.size() + optArg.size() );
80 std::copy( optArg.begin(), optArg.end(), result.begin()+reqArg.size() );
81 }
82
83 void print_help( std::ostream& stream );
84 void print_brief_help( std::ostream& stream );
85 void print_man( std::ostream& stream );
86
87 void print_arg_names( std::ostream& stream );
88 };
89
add_flag(CLArgFlag * arg)90 bool CLArgImpl::add_flag( CLArgFlag* arg )
91 {
92 if (find_flag(arg->flag()))
93 return false;
94
95 mFlags.push_back( arg );
96 return true;
97 }
98
add_parsed_arg(const char * arg)99 bool CLArgImpl::add_parsed_arg( const char* arg )
100 {
101 if (reqArg.size() < reqArgNames.size())
102 reqArg.push_back( arg );
103 else if (optArg.size() < optArgNames.size())
104 optArg.push_back( arg );
105 else
106 return false;
107
108 return true;
109 }
110
find_flag(char flag)111 CLArgFlag* CLArgImpl::find_flag( char flag )
112 {
113 for (unsigned i = 0; i < mFlags.size(); ++i)
114 if (mFlags[i]->flag() == flag)
115 return mFlags[i];
116 return 0;
117 }
118
print_help(std::ostream & stream)119 void CLArgImpl::print_help( std::ostream& stream )
120 {
121 stream << progName << " : " << shortDesc << std::endl;
122 stream << std::endl << longDesc << std::endl << std::endl;
123 print_brief_help( stream );
124 stream << '-' << HELP_FLAG << " : Print this help text." << std::endl;
125 stream << '-' << MAN_FLAG << " : Print man page text to standard output stream." << std::endl;
126 stream << "--" << " : Treat all subsequent arguments as non-flag arguments." << std::endl;
127 for (unsigned i = 0; i < mFlags.size(); ++i) {
128 stream << '-' << mFlags[i]->flag() << " : " << mFlags[i]->desc();
129 std::string extra = mFlags[i]->callback()->desc_append();
130 if (!extra.empty())
131 stream << " " << extra;
132 std::string defval = mFlags[i]->callback()->default_str();
133 if (!defval.empty())
134 stream << " (default: " << defval << ")";
135 stream << std::endl;
136 }
137 }
138
print_brief_help(std::ostream & stream)139 void CLArgImpl::print_brief_help( std::ostream& stream )
140 {
141 stream << progName;
142 for (unsigned i = 0; i < mFlags.size(); ++i) {
143 std::string str = mFlags[i]->callback()->brief();
144 if (!str.empty()) {
145 stream << "[-" << mFlags[i]->flag() << ' ' << str << "]";
146 }
147 else {
148 str = mFlags[i]->brief();
149 if (!str.empty())
150 stream << " [" << str << "]";
151 }
152 }
153 print_arg_names( stream );
154 stream << std::endl;
155 stream << progName << " -" << HELP_FLAG << std::endl;
156 stream << progName << " -" << MAN_FLAG << std::endl;
157 }
158
print_man(std::ostream & stream)159 void CLArgImpl::print_man( std::ostream& stream )
160 {
161 ManPage::begin_manpage( stream, progName, 1 );
162
163 ManPage::begin_section( stream, "NAME" );
164 ManPage::begin_paragraph( stream );
165 stream << progName << " - " << shortDesc << std::endl << std::endl;
166
167 ManPage::begin_section( stream, "SYNOPSIS" );
168 ManPage::begin_hanging_paragraph( stream );
169 ManPage::bold( stream, progName );
170 for (unsigned i = 0; i < mFlags.size(); ++i) {
171 std::string s = mFlags[i]->callback()->manstr();
172 if (!s.empty()) {
173 stream << '[';
174 ManPage::begin_bold( stream );
175 stream << '-' << mFlags[i]->flag();
176 ManPage::end_bold(stream);
177 stream << s << ']';
178 }
179 else {
180 s = mFlags[i]->manstr();
181 if (!s.empty())
182 stream << " [" << s << "]";
183 }
184 }
185 print_arg_names( stream );
186 stream << std::endl;
187 ManPage::begin_hanging_paragraph( stream );
188 ManPage::bold( stream, progName + " -h" );
189 ManPage::begin_hanging_paragraph( stream );
190 ManPage::bold( stream, progName + " -M" );
191
192 ManPage::begin_section( stream, "DESCRIPTION" );
193 ManPage::write_text( stream, false, longDesc );
194
195 ManPage::begin_section( stream, "OPTIONS" );
196 for (unsigned i = 0; i < mFlags.size(); ++i) {
197 std::string s = mFlags[i]->callback()->manstr();
198 if (!s.empty()) {
199 char tmp[] = { '-', mFlags[i]->flag(), ' ', '\0' };
200 s = std::string(tmp) + s;
201 }
202 else {
203 s = mFlags[i]->manstr();
204 if (s.empty())
205 continue;
206 }
207
208 ManPage::begin_hanging_paragraph( stream );
209 stream << s;
210 ManPage::begin_indent( stream );
211 ManPage::begin_paragraph( stream );
212 stream << mFlags[i]->desc();
213 s = mFlags[i]->callback()->desc_append();
214 if (!s.empty())
215 stream << " " << s;
216 std::string defval = mFlags[i]->callback()->default_str();
217 if (!defval.empty())
218 stream << " (default: " << defval << ")";
219 ManPage::end_indent( stream );
220 }
221
222 }
223
print_arg_names(std::ostream & stream)224 void CLArgImpl::print_arg_names( std::ostream& stream )
225 {
226 unsigned i;
227 for (i = 0; i < reqArgNames.size(); ++i)
228 stream << " <" << reqArgNames[i] << ">";
229 for (i = 0; i < optArgNames.size(); ++i)
230 stream << " [" << optArgNames[i] << "]";
231 }
232
CLArgs(const char * progname,const char * brief,const char * desc)233 CLArgs::CLArgs( const char* progname, const char* brief, const char* desc )
234 { impl = new CLArgImpl( progname, brief, desc ); }
235
~CLArgs()236 CLArgs::~CLArgs()
237 { delete impl; }
238
is_flag_available(char fl) const239 bool CLArgs::is_flag_available( char fl ) const
240 {
241 return (fl != HELP_FLAG) && (fl != MAN_FLAG) && !(impl->find_flag(fl));
242 }
243
str_flag(char fl,const char * name,const char * desc,CLArgs::StringArgI * callback)244 bool CLArgs::str_flag( char fl,
245 const char* name,
246 const char* desc,
247 CLArgs::StringArgI* callback )
248 {
249 if (!is_flag_available(fl))
250 return false;
251 return impl->add_flag( new CLArgString( fl, name, desc, callback ) );
252 }
253
int_flag(char fl,const char * name,const char * desc,CLArgs::IntArgI * callback)254 bool CLArgs::int_flag( char fl,
255 const char* name,
256 const char* desc,
257 CLArgs::IntArgI* callback )
258 {
259 if (!is_flag_available(fl))
260 return false;
261 return impl->add_flag( new CLArgInt( fl, name, desc, callback ) );
262 }
263
long_flag(char fl,const char * name,const char * desc,CLArgs::LongArgI * callback)264 bool CLArgs::long_flag( char fl,
265 const char* name,
266 const char* desc,
267 CLArgs::LongArgI* callback )
268 {
269 if (!is_flag_available(fl))
270 return false;
271 return impl->add_flag( new CLArgLong( fl, name, desc, callback ) );
272 }
273
double_flag(char fl,const char * name,const char * desc,CLArgs::DoubleArgI * callback)274 bool CLArgs::double_flag( char fl,
275 const char* name,
276 const char* desc,
277 CLArgs::DoubleArgI* callback )
278 {
279 if (!is_flag_available(fl))
280 return false;
281 return impl->add_flag( new CLArgDouble( fl, name, desc, callback ) );
282 }
283
toggle_flag(char on_flag,char off_flag,const char * desc,CLArgs::ToggleArgI * callback)284 bool CLArgs::toggle_flag( char on_flag,
285 char off_flag,
286 const char* desc,
287 CLArgs::ToggleArgI* callback )
288 {
289 if (!(is_flag_available(on_flag) && is_flag_available(off_flag)))
290 return false;
291
292 CLArgToggle* t1 = new CLArgToggle( on_flag, desc, true, callback );
293 impl->add_flag( t1 );
294 impl->add_flag( new CLArgToggle( off_flag, desc, t1 ) );
295 return true;
296 }
297
298
toggle_flag(char fl,const char * desc,CLArgs::ToggleArgI * callback)299 bool CLArgs::toggle_flag( char fl, const char* desc, CLArgs::ToggleArgI* callback )
300 {
301 if (!is_flag_available(fl))
302 return false;
303 return impl->add_flag( new CLArgToggle( fl, desc, true, callback ) );
304 }
305
id_list_flag(char fl,const char * desc,CLArgs::IntListArgI * callback)306 bool CLArgs::id_list_flag( char fl, const char* desc, CLArgs::IntListArgI* callback )
307 {
308 if (!is_flag_available(fl))
309 return false;
310 return impl->add_flag( new CLArgIDList( fl, desc, callback ) );
311 }
312
313
int_list_flag(char fl,const char * desc,CLArgs::IntListArgI * callback)314 bool CLArgs::int_list_flag( char fl,
315 const char* desc,
316 CLArgs::IntListArgI* callback )
317 {
318 if (!is_flag_available(fl))
319 return false;
320 return impl->add_flag( new CLArgIntList( fl, desc, callback ) );
321 }
322
double_list_flag(char fl,const char * desc,CLArgs::DoubleListArgI * callback)323 bool CLArgs::double_list_flag( char fl,
324 const char* desc,
325 CLArgs::DoubleListArgI* callback )
326 {
327 if (!is_flag_available(fl))
328 return false;
329 return impl->add_flag( new CLArgDoubleList( fl, desc, callback ) );
330 }
331
limit_list_flag(char fl,int num_values,const char * const * value_names)332 bool CLArgs::limit_list_flag( char fl,
333 int num_values,
334 const char* const* value_names )
335 {
336 CLArgFlag* f = impl->find_flag( fl );
337 return f ? f->add_set( num_values, value_names ) : false;
338 }
339
add_required_arg(const char * name)340 void CLArgs::add_required_arg( const char* name )
341 {
342 impl->add_req_arg( name );
343 }
344
add_optional_arg(const char * name)345 void CLArgs::add_optional_arg( const char* name )
346 {
347 impl->add_opt_arg( name );
348 }
349
parse_options(int argc,char ** argv,std::vector<std::string> & args_out,std::ostream & error_stream)350 bool CLArgs::parse_options( int argc,
351 char** argv,
352 std::vector< std::string >& args_out,
353 std::ostream& error_stream )
354 {
355 std::vector<CLArgFlag*> pending;
356 bool no_more_flags = false;
357 for (int i = 1; i < argc; ++i) {
358 if (!pending.empty()) {
359 CLArgFlag* flag = pending.front();
360 pending.erase( pending.begin() );
361 if (!flag->parse( argv[i] )) {
362 error_stream << argv[0] << ": invalid value for flag: -" << flag->flag() << " \"" << argv[i] << '"' << std::endl;
363 return false;
364 }
365 }
366 else if (!no_more_flags && argv[i][0] == '-' && argv[i][1] !='\0') {
367 for (int j = 1; argv[i][j]; ++j) {
368 if (argv[i][j] == HELP_FLAG) {
369 print_help( std::cout );
370 exit( 0 );
371 }
372 else if (argv[i][j] == MAN_FLAG) {
373 print_man_page( std::cout );
374 exit( 0 );
375 }
376
377 CLArgFlag* flag = impl->find_flag( argv[i][j] );
378 if (!flag) {
379 error_stream << argv[0] << ": invalid flag: -" << argv[i][j] << std::endl;
380 return false;
381 }
382 else if (!flag->is_toggle()) {
383 pending.push_back( flag );
384 }
385 else if (!flag->parse( NULL )) {
386 error_stream << argv[0] << ": conflicting flag: -" << argv[i][j] << std::endl;
387 return false;
388 }
389 }
390 }
391 else if (!impl->add_parsed_arg( argv[i] )) {
392 error_stream << argv[0] << ": unexpected argument: \"" <<argv[i] << '"' << std::endl;
393 return false;
394 }
395 }
396
397 impl->get_args( args_out );
398
399 if (!pending.empty()) {
400 error_stream << argv[0] << ": expected argument following flag: -" << pending.front()->flag() << std::endl;
401 return false;
402 }
403 if (!impl->have_required_args()) {
404 error_stream << argv[0] << ": insufficient arguments" << std::endl;
405 return false;
406 }
407
408 return true;
409 }
410
print_help(std::ostream & stream) const411 void CLArgs::print_help( std::ostream& stream ) const
412 {
413 impl->print_help( stream );
414 }
415
print_man_page(std::ostream & stream) const416 void CLArgs::print_man_page( std::ostream& stream ) const
417 {
418 impl->print_man( stream );
419 }
420
print_usage(std::ostream & stream) const421 void CLArgs::print_usage( std::ostream& stream ) const
422 {
423 impl->print_brief_help( stream );
424 }
425
426
427
initialize(const char * keyword_list[],int list_length)428 void CLArgs::KeyWordArg::initialize( const char* keyword_list[], int list_length )
429 {
430 mKeyWords.resize( list_length );
431 std::copy( keyword_list, keyword_list + list_length, mKeyWords.begin() );
432 }
433
value(const std::string & val)434 bool CLArgs::KeyWordArg::value( const std::string& val )
435 {
436 std::vector< std::string >::const_iterator i;
437 for (i = mKeyWords.begin(); i != mKeyWords.end(); ++i)
438 if (compare_no_case( i->c_str(), val.c_str() )) {
439 return value(*i);
440 }
441
442 return false;
443 }
444
brief() const445 std::string CLArgs::KeyWordArg::brief() const
446 {
447 std::ostringstream ss;
448 std::vector< std::string >::const_iterator i = mKeyWords.begin();
449 if (i == mKeyWords.end())
450 return std::string();
451
452 ss << '{' << *i;
453 for (++i; i != mKeyWords.end(); ++i)
454 ss << '|' << *i;
455 ss << '}';
456 return ss.str();
457 }
458
manstr() const459 std::string CLArgs::KeyWordArg::manstr() const
460 {
461 if (mKeyWords.empty())
462 return std::string();
463
464 std::ostringstream ss;
465 ManPage::bold( ss, mKeyWords[0].c_str() );
466 for (unsigned i = 1; i < mKeyWords.size(); ++i) {
467 ss << "|";
468 ManPage::bold( ss, mKeyWords[i].c_str() );
469 }
470 return ss.str();
471 }
472
compare_no_case(const char * s1,const char * s2)473 bool CLArgs::KeyWordArg::compare_no_case( const char* s1, const char* s2 )
474 {
475 for (; *s1; ++s1, ++s2)
476 if (toupper(*s1) != toupper(*s2))
477 return false;
478 return !*s2;
479 }
480
IntRange(const int * min,const int * max)481 CLArgs::IntRange::IntRange( const int* min, const int* max )
482 : mMin( min ? *min : std::numeric_limits<int>::min() ),
483 mMax( max ? *max : std::numeric_limits<int>::max() )
484 {}
485
is_valid(int val) const486 bool CLArgs::IntRange::is_valid( int val ) const
487 { return val >= mMin && val <= mMax; }
488
desc_append() const489 std::string CLArgs::IntRange::desc_append() const
490 {
491 std::ostringstream ss;
492 ss << "[" << mMin << "," << mMax << "]";
493 return ss.str();
494 }
495
value(const int & val)496 bool CLArgs::IntRangeArg::value( const int& val )
497 {
498 if (!mRange.is_valid(val))
499 return false;
500 return IntArg::value(val);
501 }
502
value(const std::vector<int> & val)503 bool CLArgs::IntListRangeArg::value( const std::vector<int>& val )
504 {
505 for (std::vector<int>::const_iterator i = val.begin(); i != val.end(); ++i)
506 if (!mRange.is_valid(*i))
507 return false;
508 return IntListArg::value(val);
509 }
510
DoubleRange(const double * min,const double * max,bool inclusive)511 CLArgs::DoubleRange::DoubleRange( const double* min, const double* max, bool inclusive )
512 : haveMin( min != NULL ),
513 haveMax( max != NULL ),
514 mInclusive( inclusive )
515 {
516 if (haveMin)
517 mMin = *min;
518 if (haveMax)
519 mMax = *max;
520 }
521
is_valid(double val) const522 bool CLArgs::DoubleRange::is_valid( double val ) const
523 {
524 if (mInclusive)
525 return (!haveMin || val >= mMin)
526 && (!haveMax || val <= mMax);
527 else
528 return (!haveMin || val > mMin)
529 && (!haveMax || val < mMax);
530 }
531
desc_append() const532 std::string CLArgs::DoubleRange::desc_append() const
533 {
534 std::ostringstream ss;
535 if (mInclusive && haveMin)
536 ss << '[';
537 else
538 ss << '(';
539 if (haveMin)
540 ss << mMin;
541 else
542 ss << "-inf";
543 ss << ",";
544 if (haveMax)
545 ss << mMax;
546 else
547 ss << "inf";
548 if (mInclusive && haveMax)
549 ss << ']';
550 else
551 ss << ')';
552 return ss.str();
553 }
554
value(const double & val)555 bool CLArgs::DoubleRangeArg::value( const double& val )
556 {
557 if (!mRange.is_valid(val))
558 return false;
559 return DoubleArg::value(val);
560 }
561
value(const std::vector<double> & val)562 bool CLArgs::DoubleListRangeArg::value( const std::vector<double>& val )
563 {
564 for (std::vector<double>::const_iterator i = val.begin(); i != val.end(); ++i)
565 if (!mRange.is_valid(*i))
566 return false;
567 return DoubleListArg::value(val);
568 }
569
570
571
572