E-MailRelay

- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

goptions.cpp
Go to the documentation of this file.
1 //
2 // Copyright (C) 2001-2018 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 // goptions.cpp
19 //
20
21 #include "gdef.h"
22 #include "gstrings.h"
23 #include "gstr.h"
24 #include "gassert.h"
25 #include "goptions.h"
26 #include "genvironment.h"
27 #include <algorithm>
28
29 G::Options::Options()
30 {
31 }
32
33 G::Options::Options( const std::string & spec , char sep_major , char sep_minor , char escape )
34 {
35 parseSpec( spec , sep_major , sep_minor , escape ) ;
36 std::sort( m_names.begin() , m_names.end() ) ;
37 }
38
39 void G::Options::parseSpec( const std::string & spec , char sep_major , char sep_minor , char escape )
40 {
41 // split into separate options
42 StringArray outer_part ;
43 outer_part.reserve( 40U ) ;
44 std::string ws_major( 1U , sep_major ) ;
45 G::Str::splitIntoFields( spec , outer_part , ws_major , escape , false ) ;
46
47 // for each option
48 for( StringArray::iterator p = outer_part.begin() ; p != outer_part.end() ; ++p )
49 {
50 // split into separate fields
51 if( (*p).empty() ) continue ;
52 StringArray inner_part ;
53 inner_part.reserve( 7U ) ;
54 std::string ws_minor( 1U , sep_minor ) ;
55 G::Str::splitIntoFields( *p , inner_part , ws_minor , escape ) ;
56 if( inner_part.size() != 7U )
57 {
58 std::ostringstream ss ;
59 ss << "\"" << *p << "\" (" << ws_minor << ")" ;
60 throw InvalidSpecification( ss.str() ) ;
61 }
62
63 unsigned int value_multiplicity = G::Str::toUInt( inner_part[4U] ) ;
64 unsigned int level = G::Str::toUInt( inner_part[6U] ) ;
65 std::string short_form = inner_part[0] ;
66 char c = short_form.empty() ? '\0' : short_form.at(0U) ;
67
68 addSpec( inner_part[1U] , c , inner_part[2U] ,
69 inner_part[3U] , value_multiplicity , inner_part[5U] , level ) ;
70 }
71 }
72
73 void G::Options::addSpec( const std::string & name , char c ,
74 const std::string & description , const std::string & description_extra ,
75 unsigned int value_multiplicity , const std::string & value_description , unsigned int level )
76 {
77 std::pair<Map::iterator,bool> rc = m_map.insert( std::make_pair( name ,
78 Option(c,name,description,description_extra,value_multiplicity,value_description,level) ) ) ;
79 if( ! rc.second )
80 throw InvalidSpecification("duplication") ;
81 m_names.push_back( name ) ; // defines the display order
82 }
83
84 bool G::Options::valued( char c ) const
85 {
86 return valued( lookup(c) ) ;
87 }
88
89 bool G::Options::valued( const std::string & name ) const
90 {
91 Map::const_iterator p = m_map.find( name ) ;
92 return p == m_map.end() ? false : ( (*p).second.value_multiplicity > 0U ) ;
93 }
94
95 bool G::Options::unvalued( const std::string & name ) const
96 {
97 return valid(name) && !valued(name) ;
98 }
99
100 bool G::Options::multivalued( char c ) const
101 {
102 return multivalued( lookup(c) ) ;
103 }
104
105 bool G::Options::multivalued( const std::string & name ) const
106 {
107 Map::const_iterator p = m_map.find( name ) ;
108 return p == m_map.end() ? false : ( (*p).second.value_multiplicity > 1U ) ;
109 }
110
111 bool G::Options::visible( const std::string & name , Level level , bool exact ) const
112 {
113 Map::const_iterator p = m_map.find( name ) ;
114 if( p == m_map.end() ) return false ;
115 return
116 exact ?
117 ( !(*p).second.hidden && (*p).second.level == level.level ) :
118 ( !(*p).second.hidden && (*p).second.level <= level.level ) ;
119 }
120
121 bool G::Options::valid( const std::string & name ) const
122 {
123 return m_map.find(name) != m_map.end() ;
124 }
125
126 std::string G::Options::lookup( char c ) const
127 {
128 for( Map::const_iterator p = m_map.begin() ; c != '\0' && p != m_map.end() ; ++p )
129 {
130 if( (*p).second.c == c )
131 return (*p).second.name ;
132 }
133 return std::string() ;
134 }
135
136 const G::StringArray & G::Options::names() const
137 {
138 return m_names ;
139 }
140
141 // --
142
143 size_t G::Options::widthDefault()
144 {
145 unsigned int result = 79U ;
146 std::string p = G::Environment::get("COLUMNS",std::string()) ;
147 if( !p.empty() )
148 {
149 try { result = G::Str::toUInt(p) ; } catch(std::exception&) {}
150 }
151 return result ;
152 }
153
154 size_t G::Options::widthFloor( size_t w )
155 {
156 return (w != 0U && w < 50U) ? 50U : w ;
157 }
158
159 G::Options::Layout G::Options::layoutDefault()
160 {
161 return Layout( 30U ) ;
162 }
163
164 std::string G::Options::introducerDefault()
165 {
166 return "usage: " ;
167 }
168
169 G::Options::Level G::Options::levelDefault()
170 {
171 return Level(99U) ;
172 }
173
174 std::string G::Options::usageSummaryPartOne( Level level ) const
175 {
176 // summarise the single-character switches, excluding those which take a value
177 std::ostringstream ss ;
178 bool first = true ;
179 for( StringArray::const_iterator name_p = m_names.begin() ; name_p != m_names.end() ; ++name_p )
180 {
181 Map::const_iterator spec_p = m_map.find( *name_p ) ;
182 if( (*spec_p).second.c != '\0' && !valued(*name_p) && visible(*name_p,level,false) )
183 {
184 if( first )
185 ss << "[-" ;
186 first = false ;
187 ss << (*spec_p).second.c ;
188 }
189 }
190
191 std::string s = ss.str() ;
192 if( s.length() ) s.append( "] " ) ;
193 return s ;
194 }
195
196 std::string G::Options::usageSummaryPartTwo( Level level ) const
197 {
198 std::ostringstream ss ;
199 const char * sep = "" ;
200 for( StringArray::const_iterator name_p = m_names.begin() ; name_p != m_names.end() ; ++name_p )
201 {
202 if( visible(*name_p,level,false) )
203 {
204 Map::const_iterator spec_p = m_map.find( *name_p ) ;
205 ss << sep << "[" ;
206 if( (*spec_p).second.name.length() )
207 {
208 ss << "--" << (*spec_p).second.name ;
209 }
210 else
211 {
212 G_ASSERT( (*spec_p).second.c != '\0' ) ;
213 ss << "-" << (*spec_p).second.c ;
214 }
215 if( (*spec_p).second.value_multiplicity > 0U )
216 {
217 std::string vd = (*spec_p).second.value_description ;
218 if( vd.empty() ) vd = "value" ;
219 ss << "=<" << vd << ">" ;
220 }
221 ss << "]" ;
222 sep = " " ;
223 }
224 }
225 return ss.str() ;
226 }
227
228 std::string G::Options::usageHelp( Level level , Layout layout , bool exact , bool extra ) const
229 {
230 layout.width = widthFloor( layout.width ) ;
231 return usageHelpCore( " " , level , layout , exact , extra ) ;
232 }
233
234 std::string G::Options::usageHelpCore( const std::string & prefix , Level level ,
235 Layout layout , bool exact , bool extra ) const
236 {
237 std::string result ;
238 for( StringArray::const_iterator name_p = m_names.begin() ; name_p != m_names.end() ; ++name_p )
239 {
240 if( visible(*name_p,level,exact) )
241 {
242 Map::const_iterator spec_p = m_map.find( *name_p ) ;
243 std::string line( prefix ) ;
244 if( (*spec_p).second.c != '\0' )
245 {
246 line.append( "-" ) ;
247 line.append( 1U , (*spec_p).second.c ) ;
248 if( (*spec_p).second.name.length() )
249 line.append( ", " ) ;
250 }
251 if( (*spec_p).second.name.length() )
252 {
253 line.append( "--" ) ;
254 line.append( (*spec_p).second.name ) ;
255 }
256
257 if( (*spec_p).second.value_multiplicity > 0U )
258 {
259 std::string vd = (*spec_p).second.value_description ;
260 if( vd.empty() ) vd = "value" ;
261 line.append( "=<" ) ;
262 line.append( vd ) ;
263 line.append( ">" ) ;
264 }
265 line.append( 1U , ' ' ) ;
266
267 if( !layout.separator.empty() )
268 line.append( layout.separator ) ;
269 else if( line.length() < layout.column )
270 line.append( layout.column-line.length() , ' ' ) ;
271
272 line.append( (*spec_p).second.description ) ;
273 if( extra )
274 line.append( (*spec_p).second.description_extra ) ;
275
276 if( layout.width )
277 {
278 std::string indent = layout.indent.empty() ? std::string(layout.column,' ') : layout.indent ;
279 line = G::Str::wrap( line , "" , indent , layout.width ) ;
280 }
281 else
282 {
283 line.append( 1U , '\n' ) ;
284 }
285
286 result.append( line ) ;
287 }
288 }
289 return result ;
290 }
291
292 void G::Options::showUsage( std::ostream & stream , const std::string & exe , const std::string & args ,
293 const std::string & introducer , Level level , Layout layout , bool extra ) const
294 {
295 stream
296 << usageSummary(exe,args,introducer,level,layout.width) << std::endl
297 << usageHelp(level,layout,false,extra) ;
298 }
299
300 std::string G::Options::usageSummary( const std::string & exe , const std::string & args ,
301 const std::string & introducer , Level level , size_t width ) const
302 {
303 std::string s = introducer + exe + " " + usageSummaryPartOne(level) + usageSummaryPartTwo(level) + (args.empty()||args.at(0U)==' '?"":" ") + args ;
304 std::string indent( 2U , ' ' ) ; // or from OptionsLayout ?
305 return width == 0U ? s : G::Str::wrap( s , "" , indent , widthFloor(width) ) ;
306 }
307
308 // ==
309
310 G::Options::Option::Option( char c_ , const std::string & name_ , const std::string & description_ ,
311 const std::string & description_extra_ , unsigned int value_multiplicity_ ,
312 const std::string & vd_ , unsigned int level_ ) :
313 c(c_) ,
314 name(name_) ,
315 description(description_) ,
316 description_extra(description_extra_) ,
317 value_multiplicity(value_multiplicity_) ,
318 hidden(description_.empty()||level_==0U) ,
319 value_description(vd_) ,
320 level(level_)
321 {
322 }
323
324 /// \file goptions.cpp
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
goptions.h
gstrings.h
G::Options::valid
bool valid(const std::string &) const
Returns true if the long-form option name is valid.
Definition: goptions.cpp:121
G::Options::unvalued
bool unvalued(const std::string &option_name) const
Returns true if the given option name is valid and takes no value.
Definition: goptions.cpp:95
G::Options::levelDefault
static Level levelDefault()
Returns the default level.
Definition: goptions.cpp:169
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:827
G::OptionsLayout
Describes the layout for G::Options output.
Definition: goptions.h:50
G::Options::multivalued
bool multivalued(char) const
Returns true if the short-form option can have multiple values.
Definition: goptions.cpp:100
G::StringArray
std::vector< std::string > StringArray
A std::vector of std::strings.
Definition: gstrings.h:33
G::Options::widthDefault
static size_t widthDefault()
Returns a default, non-zero word-wrapping width, reflecting the size of the standard output where pos...
Definition: goptions.cpp:143
G::OptionsLevel
Used by G::Options for extra type safety.
Definition: goptions.h:40
G::Options::usageHelp
std::string usageHelp(Level level=levelDefault(), Layout layout=layoutDefault(), bool level_exact=false, bool extra=true) const
Returns a multi-line string giving help on each option.
Definition: goptions.cpp:228
G::Options::lookup
std::string lookup(char c) const
Converts from short-form option character to the corresponding long-form name.
Definition: goptions.cpp:126
G::Str::toUInt
static unsigned int toUInt(const std::string &s)
Converts string 's' to an unsigned int.
Definition: gstr.cpp:488
gstr.h
gassert.h
G::Options::introducerDefault
static std::string introducerDefault()
Returns the string "usage: ".
Definition: goptions.cpp:164
G::Options::usageSummary
std::string usageSummary(const std::string &exe, const std::string &args, const std::string &introducer=introducerDefault(), Level level=levelDefault(), size_t wrap_width=widthDefault()) const
Returns a one-line (or line-wrapped) usage summary, as "usage: ".
Definition: goptions.cpp:300
G::Options::layoutDefault
static Layout layoutDefault()
Returns a default column layout.
Definition: goptions.cpp:159
G::OptionsLayout::width
size_t width
overall width for wrapping, or zero for no newlines
Definition: goptions.h:56
G::Options::valued
bool valued(char) const
Returns true if the short-form option character is valid.
Definition: goptions.cpp:84
G::Options::visible
bool visible(const std::string &name, Level, bool exact) const
Returns true if the option is visible at the given level.
Definition: goptions.cpp:111
G::Options::names
const StringArray & names() const
Returns the sorted list of long-form option names.
Definition: goptions.cpp:136
genvironment.h
G::Str::splitIntoFields
static void splitIntoFields(const std::string &in, StringArray &out, const std::string &separators, char escape= '\0', bool remove_escapes=true)
Splits the string into fields.
Definition: gstr.cpp:975
G::Options::Options
Options()
Default constructor for no options.
Definition: goptions.cpp:29
G::Options::showUsage
void showUsage(std::ostream &stream, const std::string &exe, const std::string &args=std::string(), const std::string &introducer=introducerDefault(), Level level=levelDefault(), Layout layout=layoutDefault(), bool extra=true) const
Streams out multi-line usage text using usageSummary() and usageHelp().
Definition: goptions.cpp:292