35 parseSpec( spec , sep_major , sep_minor , escape ) ;
36 std::sort( m_names.begin() , m_names.end() ) ;
39void G::Options::parseSpec(
const std::string & spec ,
char sep_major ,
char sep_minor ,
char escape )
43 spec_items.reserve( 40U ) ;
47 for(
auto & spec_item : spec_items )
50 if( spec_item.empty() ) continue ;
52 inner_parts.reserve( 7U ) ;
54 if( inner_parts.size() != 7U )
56 std::ostringstream ss ;
57 ss <<
"[" << spec_item <<
"] (" << sep_minor <<
")" ;
58 throw InvalidSpecification( ss.str() ) ;
67 if( spec_parts.size() != 7U )
68 throw InvalidSpecification(
"[" +
G::Str::join(
",",spec_parts) +
"]" ) ;
70 unsigned int level =
Str::toUInt( spec_parts[6U] ) ;
71 std::string short_form = spec_parts[0] ;
72 char c = short_form.empty() ?
'\0' : short_form.at(0U) ;
74 addImp( spec_parts[1U] , c , spec_parts[2U] ,
75 spec_parts[3U] , spec_parts[4U] , spec_parts[5U] , level ) ;
80 if( spec_parts.size() != 6U )
81 throw InvalidSpecification(
"[" +
G::Str::join(
",",spec_parts) +
"]" ) ;
84 sub_parts.reserve( 2U ) ;
86 if( sub_parts.empty() || sub_parts.size() > 2U )
87 throw InvalidSpecification(
G::Str::join(
",",spec_parts) ) ;
89 spec_parts[2U] = sub_parts[0U] ;
90 spec_parts.insert( spec_parts.begin()+3U , sub_parts.size() == 2U ? sub_parts[1U] : std::string() ) ;
95void G::Options::addImp(
const std::string & name ,
char c ,
96 const std::string & description ,
const std::string & description_extra ,
97 const std::string & value_multiplicity ,
const std::string & value_description ,
unsigned int level )
99 std::pair<Map::iterator,bool> rc = m_map.insert( std::make_pair( name ,
100 Option(c,name,description,description_extra,value_multiplicity,value_description,level) ) ) ;
102 throw InvalidSpecification(
"duplication" ) ;
103 m_names.insert( std::lower_bound(m_names.begin(),m_names.end(),name) , name ) ;
108 auto p = m_map.find( name ) ;
109 return p == m_map.end() ? false : ( (*p).second.value_multiplicity == Option::Multiplicity::zero_or_one ) ;
114 return valued( lookup(c) ) ;
119 auto p = m_map.find( name ) ;
120 return p == m_map.end() ? false : ( (*p).second.value_multiplicity != Option::Multiplicity::zero ) ;
125 return valid(name) && !valued(name) ;
130 return multivalued( lookup(c) ) ;
135 auto p = m_map.find( name ) ;
136 return p == m_map.end() ? false : ( (*p).second.value_multiplicity == Option::Multiplicity::many ) ;
141 auto p = m_map.find( name ) ;
142 if( p == m_map.end() )
return false ;
145 ( !(*p).second.hidden && (*p).second.level == level ) :
146 ( !(*p).second.hidden && (*p).second.level <= level ) ;
151 return visible( name , 99U ,
false ) ;
156 return m_map.find(name) != m_map.end() ;
161 for(
auto p = m_map.begin() ; c !=
'\0' && p != m_map.end() ; ++p )
163 if( (*p).second.c == c )
164 return (*p).second.name ;
166 return std::string() ;
178 namespace OptionsLayoutImp
180 unsigned int widthDefault() ;
184G::OptionsLayout::OptionsLayout() :
194 width = width2 = OptionsLayoutImp::widthDefault() ;
197G::OptionsLayout::OptionsLayout( std::size_t column_ ) :
207 width = width2 = OptionsLayoutImp::widthDefault() ;
210G::OptionsLayout::OptionsLayout( std::size_t column_ , std::size_t width_ ) :
220 width = width2 = OptionsLayoutImp::widthDefault() ;
223unsigned int G::OptionsLayoutImp::widthDefault()
230std::string G::Options::usageSummaryPartOne(
const Layout & layout )
const
233 std::ostringstream ss ;
235 for(
const auto & name : m_names )
237 auto spec_p = m_map.find( name ) ;
238 if( (*spec_p).second.c !=
'\0' && !valued(name) && visible(name,layout.level,layout.level_exact) )
243 ss << (*spec_p).second.c ;
247 std::string s = ss.str() ;
248 if( s.length() ) s.append(
"] " ) ;
252std::string G::Options::usageSummaryPartTwo(
const Layout & layout )
const
254 std::ostringstream ss ;
255 const char * sep =
"" ;
256 for(
const auto & name : m_names )
258 if( visible(name,layout.level,layout.level_exact) )
260 auto spec_p = m_map.find( name ) ;
262 if( (*spec_p).second.name.length() )
264 ss <<
"--" << (*spec_p).second.name ;
268 G_ASSERT( (*spec_p).second.c !=
'\0' ) ;
269 ss <<
"-" << (*spec_p).second.c ;
271 if( (*spec_p).second.value_multiplicity != Option::Multiplicity::zero )
273 std::string vd = (*spec_p).second.value_description ;
274 if( vd.empty() ) vd =
"value" ;
275 ss <<
"=<" << vd <<
">" ;
284std::string G::Options::usageHelpSyntax( Map::const_iterator spec_p )
const
287 if( (*spec_p).second.c !=
'\0' )
289 syntax.append(
"-" ) ;
290 syntax.append( 1U , (*spec_p).second.c ) ;
291 if( (*spec_p).second.name.length() )
292 syntax.append(
", " ) ;
294 if( (*spec_p).second.name.length() )
296 syntax.append(
"--" ) ;
297 syntax.append( (*spec_p).second.name ) ;
299 if( (*spec_p).second.value_multiplicity != Option::Multiplicity::zero )
301 bool defaulting = (*spec_p).second.value_multiplicity == Option::Multiplicity::zero_or_one ;
302 std::string vd = (*spec_p).second.value_description ;
303 if( vd.empty() ) vd =
"value" ;
304 if( defaulting ) syntax.append(
"[" ) ;
305 syntax.append(
"=<" ) ;
306 syntax.append( vd ) ;
307 syntax.append(
">" ) ;
308 if( defaulting ) syntax.append(
"]" ) ;
310 syntax.append( 1U ,
' ' ) ;
314std::string G::Options::usageHelpDescription( Map::const_iterator spec_p ,
const Layout & layout )
const
316 std::string description = (*spec_p).second.description ;
318 description.append( (*spec_p).second.description_extra ) ;
322std::string G::Options::usageHelpSeparator(
const Layout & layout , std::size_t syntax_length )
const
324 std::string separator ;
325 if( !layout.separator.empty() )
326 separator = layout.separator ;
327 else if( (layout.margin+syntax_length) > layout.column )
328 separator = std::string( 1U ,
' ' ) ;
330 separator = std::string( layout.column-syntax_length-layout.margin ,
' ' ) ;
334std::string G::Options::usageHelpWrap(
const Layout & layout ,
const std::string & line_in ,
335 const std::string & margin )
const
337 std::string line( line_in ) ;
338 std::size_t wrap_width = layout.width>layout.margin ? (layout.width-layout.margin) : 1U ;
339 bool separator_is_tab = layout.separator.length() == 1U && layout.separator.at(0U) ==
'\t' ;
340 if( separator_is_tab )
342 std::string prefix_other = std::string(layout.margin,
' ').append(1U,
'\t') ;
344 wrap_width , layout.width2 ,
true ) ;
346 else if( !layout.separator.empty() )
349 if( line.length() > layout.width )
351 std::string prefix_other( layout.margin+1U ,
' ' ) ;
353 wrap_width , layout.width2 ,
true ) ;
358 std::string prefix_other( layout.column ,
' ' ) ;
360 wrap_width , layout.width2 ,
true ) ;
365std::size_t G::Options::longestSubLine(
const std::string & s )
367 std::size_t result = 0U ;
387 std::string result = usageHelpImp( layout ) ;
394 std::size_t longest = longestSubLine( result ) ;
395 if( longest > layout.
width )
399 Layout new_layout( layout ) ;
401 result = usageHelpImp( new_layout ) ;
408std::string G::Options::usageHelpImp(
const Layout & layout )
const
411 for(
const auto & name : m_names )
413 if( visible(name,layout.level,layout.level_exact) )
415 auto spec_p = m_map.find( name ) ;
417 std::string margin( layout.margin ,
' ' ) ;
418 std::string syntax = usageHelpSyntax( spec_p ) ;
419 std::string description = usageHelpDescription( spec_p , layout ) ;
420 std::string separator = usageHelpSeparator( layout , syntax.length() ) ;
423 line.append(margin).append(syntax).append(separator).append(description) ;
426 line = usageHelpWrap( layout , line , margin ) ;
428 line.append( 1U ,
'\n' ) ;
429 result.append( line ) ;
436 const std::string & exe ,
const std::string & args )
const
439 << usageSummary(layout,exe,args) << std::endl
441 << usageHelp(layout) ;
445 const std::string & exe ,
const std::string & args )
const
447 const char * usage =
gettext(
"usage: " ) ;
448 const char * alt_usage =
gettext(
"abbreviated usage: " ) ;
449 std::string s = std::string()
450 .append(layout.
alt_usage?alt_usage:usage)
453 .append(usageSummaryPartOne(layout))
454 .append(usageSummaryPartTwo(layout))
455 .append(args.empty()||args.at(0U)==
' '?
"":
" ")
457 std::string indent( 2U ,
' ' ) ;
463G::Options::Option::Option(
char c_ ,
const std::string & name_ ,
const std::string & description_ ,
464 const std::string & description_extra_ ,
const std::string & value_multiplicity_ ,
465 const std::string & vd_ ,
unsigned int level_ ) :
468 description(description_) ,
469 description_extra(description_extra_) ,
470 value_multiplicity(decode(value_multiplicity_)) ,
471 hidden(description_.empty()||level_==0U) ,
472 value_description(vd_) ,
477G::Options::Option::Multiplicity G::Options::Option::decode(
const std::string & s )
480 return Multiplicity::zero ;
482 return Multiplicity::zero_or_one ;
484 return Multiplicity::one ;
486 return Multiplicity::many ;
488 throw InvalidSpecification(
"multiplicity" ) ;
static std::string get(const std::string &name, const std::string &default_)
Returns the environment variable value or the given default.
std::string lookup(char c) const
Converts from short-form option character to the corresponding long-form name.
bool unvalued(const std::string &) const
Returns true if the given option name is valid and takes no value.
const StringArray & names() const
Returns the sorted list of long-form option names.
bool visible(const std::string &name, unsigned int level, bool level_exact=false) const
Returns true if the option is visible at the given level.
bool defaulting(const std::string &) const
Returns true if the given long-form single-valued() option can optionally have no explicit value,...
bool valid(const std::string &) const
Returns true if the long-form option name is valid.
void add(StringArray)
Adds one component of the specification, broken down into its seven separate parts.
bool multivalued(char) const
Returns true if the short-form option can have multiple values.
std::string usageSummary(const Layout &, const std::string &exe, const std::string &args=std::string()) const
Returns a one-line (or line-wrapped) usage summary, as "usage: <exe> <options> <args>".
bool valued(char) const
Returns true if the given short-form option takes a value, Returns true if the short-form option char...
void showUsage(const Layout &, std::ostream &stream, const std::string &exe, const std::string &args=std::string()) const
Streams out multi-line usage text using usageSummary() and usageHelp().
Options()
Default constructor for no options.
std::string usageHelp(const Layout &) const
Returns a multi-line string giving help on each option.
static std::string join(const std::string &sep, const StringArray &strings)
Concatenates an array of strings with separators.
static void splitIntoFields(const std::string &in, StringArray &out, string_view ws, char escape='\0', bool remove_escapes=true)
Splits the string into fields.
static unsigned int toUInt(const std::string &s)
Converts string 's' to an unsigned int.
static std::string wrap(const std::string &text, const std::string &prefix_first, const std::string &prefix_other, std::size_t width_first=70U, std::size_t width_other=0U, bool preserve_spaces=false, const std::locale &=defaultLocale())
Does word-wrapping.
std::vector< std::string > StringArray
A std::vector of std::strings.
const char * gettext(const char *)
Returns the message translation in the current locale's codeset, eg.
Describes the layout for G::Options output.
std::size_t column
left hand column width if no separator (includes margin)
std::size_t width
overall width for wrapping, or zero for none
std::string separator
separator between syntax and description
bool alt_usage
use alternate "usage:" string