advgetopt 2.0.47
Parse complex command line arguments and configuration files in C++.
advgetopt_usage.cpp
Go to the documentation of this file.
1// Copyright (c) 2006-2024 Made to Order Software Corp. All Rights Reserved
2//
3// https://snapwebsites.org/project/advgetopt
4// contact@m2osw.com
5//
6// This program is free software; you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation; either version 2 of the License, or
9// (at your option) any later version.
10//
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15//
16// You should have received a copy of the GNU General Public License along
17// with this program; if not, write to the Free Software Foundation, Inc.,
18// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
27// self
28//
29#include "advgetopt/advgetopt.h"
30
31#include "advgetopt/exception.h"
32
33
34// snapdev
35//
36#include <snapdev/join_strings.h>
37#include <snapdev/tokenize_format.h>
38
39
40// C++
41//
42#include <iomanip>
43#include <iostream>
44
45
46// last include
47//
48#include <snapdev/poison.h>
49
50
51
52
53namespace advgetopt
54{
55
56
57
64{
65 // add the --long-help if at least one option uses the GROUP1, GROUP2,
66 // or SYSTEM
67 //
68 // add the --system-help if at least one option uses SYSTEM
69 //
70 bool add_long_help(false);
71 bool add_system_help(false);
72
73 for(auto it(f_options_by_name.begin())
74 ; it != f_options_by_name.end()
75 ; ++it)
76 {
78 {
79 add_long_help = true;
80 }
81
82 if(it->second->has_flag(GETOPT_FLAG_SHOW_SYSTEM))
83 {
84 add_system_help = true;
85 }
86 }
87
88 if(add_long_help)
89 {
90 option_info::pointer_t opt(std::make_shared<option_info>("long-help"));
91 opt->add_flag(GETOPT_FLAG_COMMAND_LINE
94 opt->set_help("show all the help from all the available options.");
95 f_options_by_name["long-help"] = opt;
97 {
98 opt->set_short_name(L'?');
99 f_options_by_short_name[L'?'] = opt;
100 }
101 }
102
103 if(add_system_help)
104 {
105 option_info::pointer_t opt(std::make_shared<option_info>("system-help"));
106 opt->add_flag(GETOPT_FLAG_COMMAND_LINE
109 opt->set_help("show commands and options added by libraries.");
110 f_options_by_name["system-help"] = opt;
112 {
113 opt->set_short_name(L'S');
114 f_options_by_short_name[L'S'] = opt;
115 }
116 }
117
118 if(f_options_environment.f_groups == nullptr)
119 {
120 // no groups, ignore following loop
121 //
122 return;
123 }
124
126 ; grp->f_group != GETOPT_FLAG_GROUP_NONE
127 ; ++grp)
128 {
129 // the name is not mandatory, without it you do not get the command
130 // line option but still get the group description
131 //
132 if(grp->f_name != nullptr
133 && *grp->f_name != '\0')
134 {
135 std::string const name(grp->f_name);
136 std::string const option_name(name + "-help");
137 option_info::pointer_t opt(std::make_shared<option_info>(option_name));
138 opt->add_flag(GETOPT_FLAG_COMMAND_LINE
141 opt->set_help("show help from the \""
142 + name
143 + "\" group of options.");
144 f_options_by_name[option_name] = opt;
145 }
146 }
147}
148
149
166{
167 if(f_options_environment.f_groups == nullptr)
168 {
169 return nullptr;
170 }
171
172 if((group & ~GETOPT_FLAG_GROUP_MASK) != 0)
173 {
174 throw getopt_logic_error("group parameter must represent a valid group.");
175 }
176 if(group == GETOPT_FLAG_GROUP_NONE)
177 {
178 throw getopt_logic_error("group NONE cannot be assigned a name so you cannot search for it.");
179 }
180
182 ; grp->f_group != GETOPT_FLAG_GROUP_NONE
183 ; ++grp)
184 {
185 if(group == grp->f_group)
186 {
187 if((grp->f_name == nullptr || *grp->f_name == '\0')
188 && (grp->f_description == nullptr || *grp->f_description == '\0'))
189 {
190 throw getopt_logic_error("at least one of a group name or description must be defined (a non-empty string).");
191 }
192 return grp;
193 }
194 }
195
196 // group not defined
197 //
198 return nullptr;
199}
200
201
237std::string getopt::usage(flag_t show) const
238{
239 std::stringstream ss;
240
241 flag_t specific_group(show & GETOPT_FLAG_GROUP_MASK);
242
243 // ignore all the non-show flags
244 //
250
251 size_t const line_width(get_screen_width());
253
254 std::string save_default;
255 std::string save_help;
256
259 if(f_options_environment.f_groups == nullptr)
260 {
261 group_max = GETOPT_FLAG_GROUP_MINIMUM;
262 specific_group = GETOPT_FLAG_GROUP_NONE;
263 }
264 else if(specific_group != GETOPT_FLAG_GROUP_NONE)
265 {
266 // only display that specific group if asked to do so
267 //
268 pos = specific_group >> GETOPT_FLAG_GROUP_SHIFT;
269 group_max = pos;
270 }
271
272 std::multimap<advgetopt::option_info::pointer_t, advgetopt::option_info::pointer_t> alias_reverse_references;
273 for(auto const & opt : f_options_by_name)
274 {
275 if(!opt.second->has_flag(GETOPT_FLAG_ALIAS))
276 {
277 continue;
278 }
279 alias_reverse_references.insert(std::make_pair(opt.second->get_alias_destination(), opt.second));
280 }
281
282 for(; pos <= group_max; ++pos)
283 {
284 bool group_name_shown(false);
285 flag_t const group(pos << GETOPT_FLAG_GROUP_SHIFT);
286 for(auto const & opt : f_options_by_name)
287 {
288 if((opt.second->get_flags() & GETOPT_FLAG_GROUP_MASK) != group
289 && f_options_environment.f_groups != nullptr)
290 {
291 // this could be optimized but we'd probably not see much
292 // difference overall and it's just for the usage() call
293 //
294 continue;
295 }
296
297 std::string const help(opt.second->get_help());
298 if(help.empty())
299 {
300 // ignore entries without help
301 //
302 continue;
303 }
304
305 if(opt.second->has_flag(GETOPT_FLAG_ALIAS))
306 {
307 // ignore entries representing an alias
308 //
309 continue;
310 }
311
312 if((show & GETOPT_FLAG_SHOW_ALL) == 0)
313 {
314 if(show != 0)
315 {
316 if(!opt.second->has_flag(show))
317 {
318 // usage selected group is not present in this option, ignore
319 //
320 continue;
321 }
322 }
323 else if(opt.second->has_flag(GETOPT_FLAG_SHOW_GROUP1
326 {
327 // do not show specialized groups
328 //
329 continue;
330 }
331 }
332
333 if(!group_name_shown)
334 {
335 group_name_shown = true;
336
337 if(group != GETOPT_FLAG_GROUP_NONE)
338 {
339 group_description const * grp(find_group(group));
340 if(grp != nullptr)
341 {
342 ss << std::endl
343 << breakup_line(process_help_string(grp->f_description), 0, line_width);
344 }
345 }
346 }
347
348 std::stringstream argument;
349
350 if(opt.second->is_default_option())
351 {
352 switch(opt.second->get_flags() & (GETOPT_FLAG_REQUIRED | GETOPT_FLAG_MULTIPLE))
353 {
354 case 0:
355 argument << "[default argument]";
356 break;
357
359 argument << "<default argument>";
360 break;
361
363 argument << "[default arguments]";
364 break;
365
367 argument << "<default arguments>";
368 break;
369
370 }
371 }
372 else
373 {
374 argument << "--" << opt.second->get_name();
375 auto aliases(alias_reverse_references.lower_bound(opt.second));
376 if(aliases->first == opt.second)
377 {
378 auto end(alias_reverse_references.upper_bound(opt.second));
379 for(auto a(aliases); a != end; ++a)
380 {
381 argument << " or --" << a->second->get_name();
382 }
383 }
384 if(opt.second->get_short_name() != NO_SHORT_NAME)
385 {
386 argument << " or -" << short_name_to_string(opt.second->get_short_name());
387 }
388 if(aliases->first == opt.second)
389 {
390 auto end(alias_reverse_references.upper_bound(opt.second));
391 for(; aliases != end; ++aliases)
392 {
393 if(aliases->second->get_short_name() != NO_SHORT_NAME)
394 {
395 argument << " or -" << short_name_to_string(aliases->second->get_short_name());
396 }
397 }
398 }
399
400 switch(opt.second->get_flags() & (GETOPT_FLAG_FLAG | GETOPT_FLAG_REQUIRED | GETOPT_FLAG_MULTIPLE))
401 {
402 case 0:
403 argument << " [<arg>]";
404 break;
405
407 argument << " <arg>";
408 break;
409
411 argument << " {<arg>}";
412 break;
413
415 argument << " <arg> {<arg>}";
416 break;
417
418 }
419 }
420
421 if(opt.second->has_flag(GETOPT_FLAG_DYNAMIC_CONFIGURATION))
422 {
423 argument << "*";
424 }
425
426 if(opt.second->has_default())
427 {
428 argument << " (default is \""
429 << opt.second->get_default()
430 << "\")";
431 }
432
433 // Output argument string with help
434 //
435 if(opt.second->is_default_option())
436 {
437 save_default = argument.str();
438 save_help = help;
439 }
440 else
441 {
442 std::string variable_name;
443 if(!opt.second->get_environment_variable_name().empty())
444 {
445 variable_name += "\nEnvironment Variable Name: \"";
447 {
449 }
450 variable_name += opt.second->get_environment_variable_name();
451 variable_name += '"';
452 }
453 ss << format_usage_string(argument.str()
454 , process_help_string((help + variable_name).c_str())
455 , 30
456 , line_width);
457 }
458 }
459 }
460
461 if(!save_default.empty())
462 {
463 ss << format_usage_string(save_default
464 , process_help_string(save_help.c_str())
465 , 30
466 , line_width);
467 }
468
471 {
472 ss << std::endl;
474 }
475
476 return ss.str();
477}
478
479
481{
482public:
483 static constexpr snapdev::format_flag_t const FORMAT_FLAG_EXTENDED = 0x01; // '*'
484
485 static bool is_flag(char c, snapdev::format_item<char> & f)
486 {
487 switch(c)
488 {
489 case '*':
490 if(f.has_flags(FORMAT_FLAG_EXTENDED))
491 {
492 f.add_error(snapdev::format_error_t::FORMAT_ERROR_DUPLICATE);
493 }
494 f.add_flags(FORMAT_FLAG_EXTENDED);
495 return true;
496
497 default:
498 return false;
499
500 }
501 }
502};
503
504
506{
507public:
508 static std::string::size_type is_format(char const * s, snapdev::format_item<char> & f)
509 {
510 switch(s[0])
511 {
512 case 'a':
513 case 'b':
514 case 'c':
515 case 'd':
516 case 'e':
517 case 'E':
518 case 'f':
519 case 'g':
520 case 'i':
521 case 'l':
522 case 'm':
523 case 'o':
524 case 'p':
525 case 's':
526 case 't':
527 case 'v':
528 case 'w':
529 f.format(s[0]);
530 return 1UL;
531
532 }
533
534 f.add_error(snapdev::format_error_t::FORMAT_ERROR_UNKNOWN);
535 return 0;
536 }
537};
538
539
592std::string getopt::process_help_string(char const * help) const
593{
594 if(help == nullptr)
595 {
596 return std::string();
597 }
598
599 snapdev::format_item<char>::list_t items(snapdev::tokenize_format<
600 char
602 , usage_flag_traits>(help));
603
604 for(auto it(items.begin()); it != items.end(); ++it)
605 {
606 switch(it->format())
607 {
608 case 'a':
610 {
612 }
613 break;
614
615 case 'b':
617 {
619 }
620 break;
621
622 case 'c':
624 {
626 }
627 break;
628
629 case 'd':
632 {
634 {
635 std::string joined;
636 for(char const * const * directories(f_options_environment.f_configuration_directories);
637 *directories != nullptr;
638 ++directories)
639 {
640 if(!joined.empty())
641 {
642 joined += ", ";
643 }
644 joined += *directories;
645 }
646 it->string(joined);
647 }
648 else
649 {
651 }
652 }
653 break;
654
655 case 'e':
658 {
660 {
662 char const * env(getenv(f_options_environment.f_environment_variable_name));
663 if(env != nullptr)
664 {
665 var += '=';
666 var += env;
667 }
668 else
669 {
670 var += " (not set)";
671 }
672 it->string(var);
673 }
674 else
675 {
677 }
678 }
679 break;
680
681 case 'E':
683 {
685 }
686 break;
687
688 case 'f':
691 {
693 {
694 std::string joined;
695 for(char const * const * filenames(f_options_environment.f_configuration_files);
696 *filenames != nullptr;
697 ++filenames)
698 {
699 if(!joined.empty())
700 {
701 joined += ", ";
702 }
703 joined += *filenames;
704 }
705 it->string(joined);
706 }
707 else
708 {
710 }
711 }
712 break;
713
714 case 'g':
715 {
717 it->string(snapdev::join_strings(list, ", "));
718 }
719 break;
720
721 case 'i':
722 // in the advgetopt_options.cpp, we clearly add a final "/"
723 // so we want to add it here too, to be consistent
724 it->string(get_options_filename());
725 break;
726
727 case 'l':
728 if(f_options_environment.f_license != nullptr)
729 {
731 }
732 break;
733
734 case 'm':
736 {
738 }
739 break;
740
741 case 'o':
742 it->string(get_output_filename());
743 break;
744
745 case 'p':
747 {
748 it->string(f_program_fullname);
749 }
750 else
751 {
752 it->string(f_program_name);
753 }
754 break;
755
756 case 's':
758 {
760 }
761 break;
762
763 case 't':
765 {
767 }
768 break;
769
770 case 'v':
771 if(f_options_environment.f_version != nullptr)
772 {
774 }
775 break;
776
777 case 'w':
778 {
779 string_list_t const list(get_configuration_filenames(true, true));
780 it->string(snapdev::join_strings(list, ", "));
781 }
782 break;
783
784 }
785 }
786
787 snapdev::format_item<char> empty_item;
788 return snapdev::join_strings(items, empty_item);
789}
790
791
792
793
794} // namespace advgetopt
795// vim: ts=4 sw=4 et
Definitions of the advanced getopt class.
string_list_t get_configuration_filenames(bool exists, bool writable, int argc=0, char *argv[]=nullptr) const
Generate a list of configuration filenames.
options_environment f_options_environment
Definition advgetopt.h:224
option_info::map_by_name_t f_options_by_name
Definition advgetopt.h:225
std::string f_program_name
Definition advgetopt.h:222
std::string usage(flag_t show=GETOPT_FLAG_SHOW_MOST) const
Create a string of the command line arguments.
option_info::map_by_short_name_t f_options_by_short_name
Definition advgetopt.h:226
std::string f_program_fullname
Definition advgetopt.h:221
std::string get_options_filename() const
Get the path and filename to options.
group_description const * find_group(flag_t group) const
Search for group in the list of group names.
std::string get_output_filename() const
Determine the best suited file for updates.
void parse_options_from_group_names()
Transform group names in –<name>-help commands.
std::string process_help_string(char const *help) const
Change the % flags in help strings.
std::shared_ptr< option_info > pointer_t
Definition option_info.h:78
static constexpr snapdev::format_flag_t const FORMAT_FLAG_EXTENDED
static bool is_flag(char c, snapdev::format_item< char > &f)
static std::string::size_type is_format(char const *s, snapdev::format_item< char > &f)
Definitions of the advanced getopt exceptions.
The advgetopt environment to parse command line options.
size_t get_screen_width()
Retrieve the width of one line in your console.
Definition utils.cpp:746
static constexpr flag_t GETOPT_FLAG_GROUP_MINIMUM
Definition flags.h:67
static constexpr flag_t GETOPT_FLAG_GROUP_MASK
Definition flags.h:66
static constexpr flag_t GETOPT_FLAG_SHOW_USAGE_ON_ERROR
Definition flags.h:60
std::string breakup_line(std::string line, size_t const option_width, size_t const line_width)
Breakup a string on multiple lines.
Definition utils.cpp:791
static constexpr flag_t GETOPT_FLAG_SHOW_GROUP1
Definition flags.h:62
static constexpr flag_t GETOPT_FLAG_SHOW_SYSTEM
Definition flags.h:64
static constexpr flag_t GETOPT_FLAG_GROUP_MAXIMUM
Definition flags.h:68
static constexpr flag_t GETOPT_FLAG_DYNAMIC_CONFIGURATION
Definition flags.h:48
static constexpr flag_t GETOPT_FLAG_COMMAND_LINE
Definition flags.h:45
constexpr short_name_t NO_SHORT_NAME
Definition option_info.h:51
static constexpr flag_t GETOPT_FLAG_SHOW_GROUP2
Definition flags.h:63
std::uint32_t flag_t
Definition flags.h:41
std::string format_usage_string(std::string const &argument, std::string const &help, size_t const option_width, size_t const line_width)
Format a help string to make it fit on a given width.
Definition utils.cpp:899
static constexpr flag_t GETOPT_FLAG_GROUP_SHIFT
Definition flags.h:69
static constexpr flag_t GETOPT_FLAG_FLAG
Definition flags.h:51
static constexpr flag_t GETOPT_FLAG_GROUP_COMMANDS
Definition flags.h:71
static constexpr flag_t GETOPT_FLAG_GROUP_NONE
Definition flags.h:70
std::string short_name_to_string(short_name_t short_name)
Convert a short name to a UTF-8 string.
static constexpr flag_t GETOPT_FLAG_MULTIPLE
Definition flags.h:53
static constexpr flag_t GETOPT_FLAG_REQUIRED
Definition flags.h:52
std::vector< std::string > string_list_t
Definition utils.h:41
static constexpr flag_t GETOPT_FLAG_SHOW_ALL
Definition flags.h:61
static constexpr flag_t GETOPT_FLAG_ALIAS
Definition flags.h:50
char const *const * f_configuration_directories
Definition options.h:451
char const * f_environment_variable_intro
Definition options.h:447
char const * f_section_variables_name
Definition options.h:448
char const * f_environment_variable_name
Definition options.h:446
char const *const * f_configuration_files
Definition options.h:449
group_description const * f_groups
Definition options.h:460

This document is part of the Snap! Websites Project.

Copyright by Made to Order Software Corp.