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