- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

ggetopt.cpp
Go to the documentation of this file.
1 //
2 // Copyright (C) 2001-2013 Graeme Walker <graeme_walker@users.sourceforge.net>
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
16 // ===
17 //
18 // ggetopt.cpp
19 //
20
21 #include "gdef.h"
22 #include "glog.h"
23 #include "gstrings.h"
24 #include "gstr.h"
25 #include "ggetopt.h"
26 #include "gassert.h"
27 #include "gdebug.h"
28 #include "genvironment.h"
29 #include <sstream>
30 #include <algorithm>
31 #include <functional>
32 #include <utility>
33
34 namespace
35 {
36 bool eqc( char c , std::pair<std::string,G::GetOpt::SwitchSpec> p )
37 {
38 return p.second.c == c ;
39 }
40 bool eqn( std::string n , std::pair<std::string,G::GetOpt::SwitchSpec> p )
41 {
42 return p.second.name == n ;
43 }
44 }
45
46 G::GetOpt::GetOpt( const Arg & args_in , const std::string & spec ,
47 char sep_major , char sep_minor , char escape ) :
48 m_args(args_in)
49 {
50 parseSpec( spec , sep_major , sep_minor , escape ) ;
51 size_type n = parseArgs( args_in ) ;
52 remove( n ) ;
53 }
54
55 void G::GetOpt::parseSpec( const std::string & spec , char sep_major , char sep_minor , char escape )
56 {
57 Strings outer ;
58 std::string ws_major( 1U , sep_major ) ;
59 G::Str::splitIntoFields( spec , outer , ws_major , escape , false ) ;
60
61 for( Strings::iterator p = outer.begin() ; p != outer.end() ; ++p )
62 {
63 if( (*p).empty() ) continue ;
64 StringArray inner ;
65 std::string ws_minor( 1U , sep_minor ) ;
66 G::Str::splitIntoFields( *p , inner , ws_minor , escape ) ;
67 if( inner.size() != 7U )
68 {
69 std::ostringstream ss ;
70 ss << "\"" << *p << "\" (" << ws_minor << ")" ;
71 throw InvalidSpecification( ss.str() ) ;
72 }
73 bool is_valued = G::Str::toUInt( inner[4U] ) != 0U ;
74 unsigned int level = G::Str::toUInt( inner[6U] ) ;
75 addSpec( inner[1U] , inner[0U].at(0U) , inner[1U] , inner[2U] , inner[3U] , is_valued , inner[5U] , level ) ;
76 }
77 }
78
79 void G::GetOpt::addSpec( const std::string & sort_key , char c , const std::string & name ,
80 const std::string & description , const std::string & description_extra ,
81 bool is_valued , const std::string & value_description , unsigned int level )
82 {
83 if( c == '\0' )
84 throw InvalidSpecification() ;
85
86 std::pair<SwitchSpecMap::iterator,bool> rc =
87 m_spec_map.insert( std::make_pair( sort_key ,
88 SwitchSpec(c,name,description,description_extra,is_valued,value_description,level) ) ) ;
89
90 if( ! rc.second )
91 throw InvalidSpecification("duplication") ;
92 }
93
94 bool G::GetOpt::valued( const std::string & name ) const
95 {
96 return valued(key(name)) ;
97 }
98
99 bool G::GetOpt::valued( char c ) const
100 {
101 SwitchSpecMap::const_iterator p =
102 std::find_if( m_spec_map.begin() , m_spec_map.end() , std::bind1st(std::ptr_fun(eqc),c)) ;
103 return p == m_spec_map.end() ? false : (*p).second.valued ;
104 }
105
106 char G::GetOpt::key( const std::string & name ) const
107 {
108 SwitchSpecMap::const_iterator p =
109 std::find_if( m_spec_map.begin() , m_spec_map.end() , std::bind1st(std::ptr_fun(eqn),name)) ;
110 return p == m_spec_map.end() ? '\0' : (*p).second.c ;
111 }
112
113 G::GetOpt::size_type G::GetOpt::wrapDefault()
114 {
115 unsigned int result = 79U ;
116 std::string p = G::Environment::get("COLUMNS",std::string()) ;
117 if( !p.empty() )
118 {
119 try { result = G::Str::toUInt(p) ; } catch(std::exception&) {}
120 }
121 return result ;
122 }
123
124 G::GetOpt::size_type G::GetOpt::tabDefault()
125 {
126 return 30U ;
127 }
128
129 std::string G::GetOpt::introducerDefault()
130 {
131 return "usage: " ;
132 }
133
134 G::GetOpt::Level G::GetOpt::levelDefault()
135 {
136 return Level(99U) ;
137 }
138
139 G::GetOpt::size_type G::GetOpt::widthLimit( size_type w )
140 {
141 return (w != 0U && w < 50U) ? 50U : w ;
142 }
143
144 void G::GetOpt::showUsage( std::ostream & stream , const std::string & args , bool verbose ) const
145 {
146 showUsage( stream , m_args.prefix() , args , introducerDefault() ,
147 verbose ? levelDefault() : Level(1U) , tabDefault() , wrapDefault() , verbose ) ;
148 }
149
150 void G::GetOpt::showUsage( std::ostream & stream , const std::string & exe , const std::string & args ,
151 const std::string & introducer , Level level , size_type tab_stop , size_type width , bool extra ) const
152 {
153 stream
154 << usageSummary(exe,args,introducer,level,width) << std::endl
155 << usageHelp(level,tab_stop,width,false,extra) ;
156 }
157
158 std::string G::GetOpt::usageSummary( const std::string & exe , const std::string & args ,
159 const std::string & introducer , Level level , size_type width ) const
160 {
161 std::string s = introducer + exe + " " + usageSummarySwitches(level) + args ;
162 if( width != 0U )
163 {
164 return G::Str::wrap( s , "" , " " , widthLimit(width) ) ;
165 }
166 else
167 {
168 return s ;
169 }
170 }
171
172 std::string G::GetOpt::usageSummarySwitches( Level level ) const
173 {
174 return usageSummaryPartOne(level) + usageSummaryPartTwo(level) ;
175 }
176
177 bool G::GetOpt::visible( SwitchSpecMap::const_iterator p , Level level , bool exact )
178 {
179 return
180 exact ?
181 ( !(*p).second.hidden && (*p).second.level == level.level ) :
182 ( !(*p).second.hidden && (*p).second.level <= level.level ) ;
183 }
184
185 std::string G::GetOpt::usageSummaryPartOne( Level level ) const
186 {
187 // summarise the single-character switches, excluding those which take a value
188 std::ostringstream ss ;
189 bool first = true ;
190 for( SwitchSpecMap::const_iterator p = m_spec_map.begin() ; p != m_spec_map.end() ; ++p )
191 {
192 if( !(*p).second.valued && visible(p,level,false) )
193 {
194 if( first )
195 ss << "[-" ;
196 first = false ;
197 ss << (*p).second.c ;
198 }
199 }
200
201 std::string s = ss.str() ;
202 if( s.length() ) s.append( "] " ) ;
203 return s ;
204 }
205
206 std::string G::GetOpt::usageSummaryPartTwo( Level level ) const
207 {
208 std::ostringstream ss ;
209 const char * sep = "" ;
210 for( SwitchSpecMap::const_iterator p = m_spec_map.begin() ; p != m_spec_map.end() ; ++p )
211 {
212 if( visible(p,level,false) )
213 {
214 ss << sep << "[" ;
215 if( (*p).second.name.length() )
216 {
217 ss << "--" << (*p).second.name ;
218 }
219 else
220 {
221 ss << "-" << (*p).second.c ;
222 }
223 if( (*p).second.valued )
224 {
225 std::string vd = (*p).second.value_description ;
226 if( vd.empty() ) vd = "value" ;
227 ss << "=<" << vd << ">" ;
228 }
229 ss << "]" ;
230 sep = " " ;
231 }
232 }
233 return ss.str() ;
234 }
235
236 std::string G::GetOpt::usageHelp( Level level , size_type tab_stop , size_type width ,
237 bool exact , bool extra ) const
238 {
239 return usageHelpCore( " " , level , tab_stop , widthLimit(width) , exact , extra ) ;
240 }
241
242 std::string G::GetOpt::usageHelpCore( const std::string & prefix , Level level ,
243 size_type tab_stop , size_type width , bool exact , bool extra ) const
244 {
245 std::string result ;
246 for( SwitchSpecMap::const_iterator p = m_spec_map.begin() ; p != m_spec_map.end() ; ++p )
247 {
248 if( visible(p,level,exact) )
249 {
250 std::string line( prefix ) ;
251 line.append( "-" ) ;
252 line.append( 1U , (*p).second.c ) ;
253
254 if( (*p).second.name.length() )
255 {
256 line.append( ", --" ) ;
257 line.append( (*p).second.name ) ;
258 }
259
260 if( (*p).second.valued )
261 {
262 std::string vd = (*p).second.value_description ;
263 if( vd.empty() ) vd = "value" ;
264 line.append( "=<" ) ;
265 line.append( vd ) ;
266 line.append( ">" ) ;
267 }
268 line.append( 1U , ' ' ) ;
269
270 if( line.length() < tab_stop )
271 line.append( tab_stop-line.length() , ' ' ) ;
272
273 line.append( (*p).second.description ) ;
274 if( extra )
275 line.append( (*p).second.description_extra ) ;
276
277 if( width )
278 {
279 std::string indent( tab_stop , ' ' ) ;
280 line = G::Str::wrap( line , "" , indent , width ) ;
281 }
282 else
283 {
284 line.append( 1U , '\n' ) ;
285 }
286
287 result.append( line ) ;
288 }
289 }
290 return result ;
291 }
292
293 G::GetOpt::size_type G::GetOpt::parseArgs( const Arg & args_in )
294 {
295 G::Arg::size_type i = 1U ;
296 for( ; i < args_in.c() ; i++ )
297 {
298 const std::string & arg = args_in.v(i) ;
299
300 if( arg == "--" ) // special "end-of-switches" switch
301 {
302 i++ ;
303 break ;
304 }
305
306 if( isSwitchSet(arg) ) // eg. "-lt"
307 {
308 for( size_type n = 1U ; n < arg.length() ; n++ )
309 processSwitch( arg.at(n) ) ;
310 }
311 else if( isOldSwitch(arg) ) // eg. "-v"
312 {
313 char c = arg.at(1U) ;
314 if( valued(c) && (i+1U) >= args_in.c() )
315 errorNoValue( c ) ;
316 else if( valued(c) )
317 processSwitch( c , args_in.v(++i) ) ;
318 else
319 processSwitch( c ) ;
320 }
321 else if( isNewSwitch(arg) ) // eg. "--foo"
322 {
323 std::string name = arg.substr( 2U ) ;
324 size_type pos_eq = eqPos(name) ;
325 bool has_eq = pos_eq != std::string::npos ;
326 std::string eq_value = eqValue(name,pos_eq) ;
327 if( has_eq ) name = name.substr(0U,pos_eq) ;
328
329 if( valued(name) && !has_eq && (i+1U) >= args_in.c() )
330 errorNoValue( name ) ;
331 else if( valued(name) && !has_eq )
332 processSwitch( name , args_in.v(++i) ) ;
333 else if( valued(name) )
334 processSwitch( name , eq_value ) ;
335 else
336 processSwitch( name ) ;
337 }
338 else
339 {
340 break ;
341 }
342 }
343 i-- ;
344 return i ;
345 }
346
347 G::GetOpt::size_type G::GetOpt::eqPos( const std::string & s )
348 {
349 size_type p = s.find_first_not_of("abcdefghijklmnopqrstuvwxyz0123456789-_") ;
350 return p != std::string::npos && s.at(p) == '=' ? p : std::string::npos ;
351 }
352
353 std::string G::GetOpt::eqValue( const std::string & s , size_type pos )
354 {
355 return (pos+1U) == s.length() ? std::string() : s.substr(pos+1U) ;
356 }
357
358 bool G::GetOpt::isOldSwitch( const std::string & arg ) const
359 {
360 return
361 ( arg.length() > 1U && arg.at(0U) == '-' ) &&
362 ! isNewSwitch( arg ) ;
363 }
364
365 bool G::GetOpt::isNewSwitch( const std::string & arg ) const
366 {
367 return arg.length() > 2U && arg.at(0U) == '-' && arg.at(1U) == '-' ;
368 }
369
370 bool G::GetOpt::isSwitchSet( const std::string & arg ) const
371 {
372 return isOldSwitch(arg) && arg.length() > 2U ;
373 }
374
375 void G::GetOpt::errorNoValue( char c )
376 {
377 std::string e("no value supplied for -") ;
378 e.append( 1U , c ) ;
379 m_errors.push_back( e ) ;
380 }
381
382 void G::GetOpt::errorNoValue( const std::string & name )
383 {
384 std::string e("no value supplied for --") ;
385 e.append( name ) ;
386 m_errors.push_back( e ) ;
387 }
388
389 void G::GetOpt::errorUnknownSwitch( char c )
390 {
391 std::string e("invalid option: -") ;
392 e.append( 1U , c ) ;
393 m_errors.push_back( e ) ;
394 }
395
396 void G::GetOpt::errorUnknownSwitch( const std::string & name )
397 {
398 std::string e("invalid option: --") ;
399 e.append( name ) ;
400 m_errors.push_back( e ) ;
401 }
402
403 void G::GetOpt::processSwitch( const std::string & name )
404 {
405 if( !valid(name) )
406 {
407 errorUnknownSwitch( name ) ;
408 return ;
409 }
410
411 char c = key(name) ;
412 if( valued(c) )
413 {
414 errorNoValue( name ) ;
415 return ;
416 }
417
418 m_map.insert( std::make_pair(c,std::make_pair(false,std::string())) ) ;
419 }
420
421 void G::GetOpt::processSwitch( const std::string & name , const std::string & value )
422 {
423 if( !valid(name) )
424 {
425 errorUnknownSwitch( name ) ;
426 return ;
427 }
428
429 char c = key(name) ;
430 m_map.insert( std::make_pair(c,std::make_pair(true,value)) ) ;
431 }
432
433 void G::GetOpt::processSwitch( char c )
434 {
435 if( !valid(c) )
436 {
437 errorUnknownSwitch( c ) ;
438 return ;
439 }
440
441 if( valued(c) )
442 {
443 errorNoValue( c ) ;
444 return ;
445 }
446
447 m_map.insert( std::make_pair(c,std::make_pair(false,std::string())) ) ;
448 }
449
450 void G::GetOpt::processSwitch( char c , const std::string & value )
451 {
452 if( !valid(c) )
453 errorUnknownSwitch( c ) ;
454
455 m_map.insert( std::make_pair(c,std::make_pair(true,value)) ) ;
456 }
457
458 G::Strings G::GetOpt::errorList() const
459 {
460 return m_errors ;
461 }
462
463 bool G::GetOpt::contains( char c ) const
464 {
465 SwitchMap::const_iterator p = m_map.find( c ) ;
466 return p != m_map.end() ;
467 }
468
469 bool G::GetOpt::contains( const std::string & name ) const
470 {
471 char c = key(name) ;
472 SwitchMap::const_iterator p = m_map.find( c ) ;
473 return p != m_map.end() ;
474 }
475
476 std::string G::GetOpt::value( char c ) const
477 {
478 G_ASSERT( contains(c) ) ;
479 SwitchMap::const_iterator p = m_map.find( c ) ;
480 Value value_pair = (*p).second ;
481 return value_pair.second ;
482 }
483
484 std::string G::GetOpt::value( const std::string & name ) const
485 {
486 G_ASSERT( contains(name) ) ;
487 return value( key(name) ) ;
488 }
489
490 G::Arg G::GetOpt::args() const
491 {
492 return m_args ;
493 }
494
495 void G::GetOpt::show( std::ostream & stream , std::string prefix ) const
496 {
497 for( SwitchMap::const_iterator p = m_map.begin() ; p != m_map.end() ; ++p )
498 {
499 char c = (*p).first ;
500 Value v = (*p).second ;
501 bool valued = v.first ;
502 std::string value = v.second ;
503
504 SwitchSpecMap::const_iterator q = std::find_if( m_spec_map.begin() , m_spec_map.end() ,
505 std::bind1st(std::ptr_fun(eqc),c)) ;
506 std::string name = q == m_spec_map.end() ? std::string() : (*q).second.name ;
507
508 stream << prefix << "-" << c ;
509 if( !name.empty() )
510 stream << ",--" << name ;
511 if( valued )
512 stream << " = \"" << value << "\"" ;
513 stream << std::endl ;
514 }
515 }
516
517 bool G::GetOpt::hasErrors() const
518 {
519 return m_errors.size() != 0U ;
520 }
521
522 void G::GetOpt::showErrors( std::ostream & stream ) const
523 {
524 showErrors( stream , m_args.prefix() ) ;
525 }
526
527 void G::GetOpt::showErrors( std::ostream & stream , std::string prefix_1 , std::string prefix_2 ) const
528 {
529 for( Strings::const_iterator p = m_errors.begin() ; p != m_errors.end() ; ++p )
530 {
531 stream << prefix_1 << prefix_2 << *p << std::endl ;
532 }
533 }
534
535 void G::GetOpt::remove( size_type n )
536 {
537 if( n != 0U )
538 {
539 m_args.removeAt(1U,n-1U) ;
540 }
541 }
542
543 bool G::GetOpt::valid( const std::string & name ) const
544 {
545 return valid( key(name) ) ;
546 }
547
548 bool G::GetOpt::valid( char c ) const
549 {
550 return !! std::count_if( m_spec_map.begin() , m_spec_map.end() , std::bind1st(std::ptr_fun(eqc),c)) ;
551 }
552
G::GetOpt::contains
bool contains(char switch_letter) const
Returns true if the command line contains the given switch.
Definition: ggetopt.cpp:463
G::Environment::get
static std::string get(const std::string &name, const std::string &default_)
Returns the environment variable value or the given default.
Definition: genvironment_unix.cpp:25
G::GetOpt::showErrors
void showErrors(std::ostream &stream, std::string prefix_1, std::string prefix_2=std::string(": ")) const
A convenience function which streams out each errorList() item to the given stream, prefixed with the given prefix(es).
Definition: ggetopt.cpp:527
gstrings.h
G::GetOpt::errorList
Strings errorList() const
Returns the list of errors.
Definition: ggetopt.cpp:458
glog.h
G::Arg::size_type
size_t size_type
Definition: garg.h:49
G::Str::toUInt
static unsigned int toUInt(const std::string &s, bool limited=false)
Converts string 's' to an unsigned int.
Definition: gstr.cpp:346
G::Strings
std::list< std::string > Strings
A std::list of std::strings.
Definition: gstrings.h:39
G::Str::wrap
static std::string wrap(std::string text, const std::string &prefix_first_line, const std::string &prefix_subsequent_lines, size_type width=70U)
Does word-wrapping.
Definition: gstr.cpp:633
G::Str::splitIntoFields
static void splitIntoFields(const std::string &in, Strings &out, const std::string &seperators, char escape= '\0', bool discard_bogus_escapes=true)
Splits the string into fields.
Definition: gstr.cpp:765
G::GetOpt::hasErrors
bool hasErrors() const
Returns true if there are errors.
Definition: ggetopt.cpp:517
G::StringArray
std::vector< std::string > StringArray
A std::vector of std::strings.
Definition: gstrings.h:44
md5::size_type
std::string::size_type size_type
A std::size_t type.
Definition: md5.h:43
G::GetOpt::show
void show(std::ostream &stream, std::string prefix) const
For debugging.
Definition: ggetopt.cpp:495
G::GetOpt::value
std::string value(const std::string &switch_name) const
Returns the value related to the given value-based switch.
Definition: ggetopt.cpp:484
G::GetOpt::GetOpt
GetOpt(const Arg &arg, const std::string &spec, char sep_major= '|', char sep_minor= '/', char escape= '\\')
Constructor taking a Arg reference and a specification string.
Definition: ggetopt.cpp:46
ggetopt.h
G::GetOpt::tabDefault
static size_type tabDefault()
Returns a default tab-stop.
Definition: ggetopt.cpp:124
G::GetOpt::args
Arg args() const
Returns all the non-switch command-line arguments.
Definition: ggetopt.cpp:490
G_ASSERT
#define G_ASSERT(test)
Definition: gassert.h:30
G::GetOpt::usageHelp
std::string usageHelp(Level level=levelDefault(), size_type tab_stop=tabDefault(), size_type wrap_width=wrapDefault(), bool level_exact=false, bool extra=true) const
Returns a multi-line string giving help on each switch.
Definition: ggetopt.cpp:236
gstr.h
gassert.h
G::GetOpt::wrapDefault
static size_type wrapDefault()
Returns a default word-wrapping width.
Definition: ggetopt.cpp:113
G::GetOpt::levelDefault
static Level levelDefault()
Returns the default level.
Definition: ggetopt.cpp:134
G::Arg
A class which holds a represention of the argc/argv command line array, and supports simple command-l...
Definition: garg.h:46
genvironment.h
G::GetOpt::size_type
std::string::size_type size_type
Definition: ggetopt.h:57
G::GetOpt::usageSummary
std::string usageSummary(const std::string &exe, const std::string &args, const std::string &introducer=introducerDefault(), Level level=levelDefault(), size_type wrap_width=wrapDefault()) const
Returns a one-line usage summary, as "usage: ".
Definition: ggetopt.cpp:158
G::GetOpt::introducerDefault
static std::string introducerDefault()
Returns "usage: ".
Definition: ggetopt.cpp:129
G::GetOpt::Level
Used by G::GetOpt for extra type safety.
Definition: ggetopt.h:55
G::GetOpt::usageSummarySwitches
std::string usageSummarySwitches(Level level=levelDefault()) const
Returns the one-line summary of switches.
Definition: ggetopt.cpp:172
gdebug.h
G::GetOpt::showUsage
void showUsage(std::ostream &stream, const std::string &exe, const std::string &args, const std::string &introducer=introducerDefault(), Level level=levelDefault(), size_type tab_stop=tabDefault(), size_type wrap_width=wrapDefault(), bool extra=true) const
Streams out multi-line usage text using usageSummary() and usageHelp().
Definition: ggetopt.cpp:150