mirror of
https://github.com/qpdf/qpdf.git
synced 2024-12-22 10:58:58 +00:00
QPDFArgParser: support adding/printing help information
This commit is contained in:
parent
5303130cf9
commit
b4bd124be4
@ -19,7 +19,7 @@ def warn(*args, **kwargs):
|
||||
|
||||
|
||||
class Main:
|
||||
SOURCES = [whoami, 'job.yml']
|
||||
SOURCES = [whoami, 'job.yml', 'manual/cli.rst']
|
||||
DESTS = {
|
||||
'decl': 'libqpdf/qpdf/auto_job_decl.hh',
|
||||
'init': 'libqpdf/qpdf/auto_job_init.hh',
|
||||
@ -87,6 +87,88 @@ class Main:
|
||||
for k, v in hashes.items():
|
||||
print(f'{k} {v}', file=f)
|
||||
|
||||
def generate_doc(self, df, f):
|
||||
st_top = 0
|
||||
st_topic = 1
|
||||
st_option = 2
|
||||
st_option_help = 3
|
||||
state = st_top
|
||||
|
||||
indent = None
|
||||
topic = None
|
||||
option = None
|
||||
short_text = None
|
||||
long_text = None
|
||||
|
||||
print('this->ap.addHelpFooter("For detailed help, visit'
|
||||
' the qpdf manual: https://qpdf.readthedocs.io\\n");', file=f)
|
||||
|
||||
def set_indent(x):
|
||||
nonlocal indent
|
||||
indent = ' ' * len(x)
|
||||
|
||||
def append_long_text(line):
|
||||
nonlocal indent, long_text
|
||||
if line == '\n':
|
||||
long_text += '\n'
|
||||
elif line.startswith(indent):
|
||||
long_text += line[len(indent):]
|
||||
else:
|
||||
long_text = long_text.strip()
|
||||
if long_text != '':
|
||||
long_text += '\n'
|
||||
return True
|
||||
return False
|
||||
|
||||
lineno = 0
|
||||
for line in df.readlines():
|
||||
lineno += 1
|
||||
if state == st_top:
|
||||
m = re.match(r'^(\s*\.\. )help-topic (\S+): (.*)$', line)
|
||||
if m:
|
||||
set_indent(m.group(1))
|
||||
topic = m.group(2)
|
||||
short_text = m.group(3)
|
||||
long_text = ''
|
||||
state = st_topic
|
||||
continue
|
||||
m = re.match(r'^(\s*\.\. )qpdf:option:: (([^=\s]+)(=(\S+))?)$',
|
||||
line)
|
||||
if m:
|
||||
if topic is None:
|
||||
raise Exception('option seen before topic')
|
||||
set_indent(m.group(1))
|
||||
option = m.group(3)
|
||||
synopsis = m.group(2)
|
||||
if synopsis.endswith('`'):
|
||||
raise Exception(
|
||||
f'stray ` at end of option line (line {lineno})')
|
||||
if synopsis != option:
|
||||
long_text = synopsis + '\n'
|
||||
else:
|
||||
long_text = ''
|
||||
state = st_option
|
||||
continue
|
||||
elif state == st_topic:
|
||||
if append_long_text(line):
|
||||
print(f'this->ap.addHelpTopic("{topic}", "{short_text}",'
|
||||
f' R"({long_text})");', file=f)
|
||||
state = st_top
|
||||
elif state == st_option:
|
||||
if line == '\n' or line.startswith(indent):
|
||||
m = re.match(r'^(\s*\.\. )help: (.*)$', line)
|
||||
if m:
|
||||
set_indent(m.group(1))
|
||||
short_text = m.group(2)
|
||||
state = st_option_help
|
||||
else:
|
||||
state = st_top
|
||||
elif state == st_option_help:
|
||||
if append_long_text(line):
|
||||
print(f'this->ap.addOptionHelp("{option}", "{topic}",'
|
||||
f' "{short_text}", R"({long_text})");', file=f)
|
||||
state = st_top
|
||||
|
||||
def generate(self):
|
||||
warn(f'{whoami}: regenerating auto job files')
|
||||
|
||||
@ -230,6 +312,8 @@ class Main:
|
||||
for j in ft['options']:
|
||||
print('this->ap.copyFromOtherTable'
|
||||
f'("{j}", "{other_table}");', file=f)
|
||||
with open('manual/cli.rst', 'r') as df:
|
||||
self.generate_doc(df, f)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <stdexcept>
|
||||
#include <sstream>
|
||||
|
||||
// This is not a general-purpose argument parser. It is tightly
|
||||
// crafted to work with qpdf. qpdf's command-line syntax is very
|
||||
@ -38,7 +39,10 @@
|
||||
// backward compatibility to ensure we don't break what constitutes a
|
||||
// valid command. This class handles the quirks of qpdf's argument
|
||||
// parsing, bash/zsh completion, and support for @argfile to read
|
||||
// arguments from a file.
|
||||
// arguments from a file. For the qpdf CLI, setup of QPDFArgParser is
|
||||
// done mostly by automatically-generated code (one-off code for
|
||||
// qpdf), though the handlers themselves are hand-coded. See
|
||||
// generate_auto_job at the top of the source tree for details.
|
||||
|
||||
// Note about memory: there is code that expects argv to be a char*[],
|
||||
// meaning that arguments are writable. Several operations, including
|
||||
@ -119,6 +123,13 @@ class QPDFArgParser
|
||||
std::string const& arg, param_arg_handler_t,
|
||||
bool required, char const** choices);
|
||||
|
||||
// The default behavior when an invalid choice is specified with
|
||||
// an option that takes choices is to list all the choices. This
|
||||
// may not be good if there are too many choices, so you can
|
||||
// provide your own handler in this case.
|
||||
QPDF_DLL
|
||||
void addInvalidChoiceHandler(std::string const& arg, param_arg_handler_t);
|
||||
|
||||
// If an option is shared among multiple tables and uses identical
|
||||
// handlers, you can just copy it instead of repeating the
|
||||
// registration call.
|
||||
@ -131,6 +142,67 @@ class QPDFArgParser
|
||||
QPDF_DLL
|
||||
void addFinalCheck(bare_arg_handler_t);
|
||||
|
||||
// Help generation methods
|
||||
|
||||
// Help is available on topics and options. Options may be
|
||||
// associated with topics. Users can run --help, --help=topic, or
|
||||
// --help=--arg to get help. The top-level help tells the user how
|
||||
// to run help and lists available topics. Help for a topic prints
|
||||
// a short synopsis about the topic and lists any options that may
|
||||
// be associated with the topic. Help for an option provides a
|
||||
// short synopsis for that option. All help output is appended
|
||||
// with a blurb (if supplied) directing the user to the full
|
||||
// documentation. Help is not shown for options for which help has
|
||||
// not been added. This makes it possible to have undocumented
|
||||
// options for testing, backward-compatibility, etc. Also, it
|
||||
// could be quite confusing to handle appropriate help for some
|
||||
// inner options that may be repeated with different semantics
|
||||
// inside different option tables. There is also no checking for
|
||||
// whether an option that has help actually exists. In other
|
||||
// words, it's up to the caller to ensure that help actually
|
||||
// corresponds to the program's actual options. Rather than this
|
||||
// being an intentional design decision, it is because this class
|
||||
// is specifically for qpdf, qpdf generates its help and has other
|
||||
// means to ensure consistency.
|
||||
|
||||
// Note about newlines:
|
||||
//
|
||||
// short_text should fit easily after the topic/option on the same
|
||||
// line and should not end with a newline. Keep it to around 40 to
|
||||
// 60 characters.
|
||||
//
|
||||
// long_text and footer should end with a single newline. They can
|
||||
// have embedded newlines. Keep lines to under 80 columns.
|
||||
//
|
||||
// QPDFArgParser does reformat the text, but it may add blank
|
||||
// lines in some situations. Following the above conventions will
|
||||
// keep the help looking uniform.
|
||||
|
||||
// If provided, this footer is appended to all help, separated by
|
||||
// a blank line.
|
||||
QPDF_DLL
|
||||
void addHelpFooter(std::string const&);
|
||||
|
||||
// Add a help topic along with the text for that topic
|
||||
QPDF_DLL
|
||||
void addHelpTopic(std::string const& topic,
|
||||
std::string const& short_text,
|
||||
std::string const& long_text);
|
||||
|
||||
// Add help for an option, and associate it with a topic.
|
||||
QPDF_DLL
|
||||
void addOptionHelp(std::string const& option_name,
|
||||
std::string const& topic,
|
||||
std::string const& short_text,
|
||||
std::string const& long_text);
|
||||
|
||||
// Return the help text for a topic or option. Passing a null
|
||||
// pointer returns the top-level help information. Passing an
|
||||
// unknown value returns a string directing the user to run the
|
||||
// top-level --help option.
|
||||
QPDF_DLL
|
||||
std::string getHelp(char const* topic_or_option);
|
||||
|
||||
// Convenience methods for adding member functions of a class as
|
||||
// handlers.
|
||||
template <class T>
|
||||
@ -171,7 +243,8 @@ class QPDFArgParser
|
||||
OptionEntry() :
|
||||
parameter_needed(false),
|
||||
bare_arg_handler(0),
|
||||
param_arg_handler(0)
|
||||
param_arg_handler(0),
|
||||
invalid_choice_handler(0)
|
||||
{
|
||||
}
|
||||
bool parameter_needed;
|
||||
@ -179,9 +252,24 @@ class QPDFArgParser
|
||||
std::set<std::string> choices;
|
||||
bare_arg_handler_t bare_arg_handler;
|
||||
param_arg_handler_t param_arg_handler;
|
||||
param_arg_handler_t invalid_choice_handler;
|
||||
};
|
||||
typedef std::map<std::string, OptionEntry> option_table_t;
|
||||
|
||||
struct HelpTopic
|
||||
{
|
||||
HelpTopic() = default;
|
||||
HelpTopic(std::string const& short_text, std::string const& long_text) :
|
||||
short_text(short_text),
|
||||
long_text(long_text)
|
||||
{
|
||||
}
|
||||
|
||||
std::string short_text;
|
||||
std::string long_text;
|
||||
std::set<std::string> options;
|
||||
};
|
||||
|
||||
OptionEntry& registerArg(std::string const& arg);
|
||||
|
||||
void completionCommon(bool zsh);
|
||||
@ -189,6 +277,7 @@ class QPDFArgParser
|
||||
void argCompletionBash();
|
||||
void argCompletionZsh();
|
||||
void argHelp(char*);
|
||||
void invalidHelpArg(char*);
|
||||
|
||||
void checkCompletion();
|
||||
void handleArgFileArguments();
|
||||
@ -202,6 +291,11 @@ class QPDFArgParser
|
||||
option_table_t&, std::string const&, std::string const&);
|
||||
void handleCompletion();
|
||||
|
||||
void getTopHelp(std::ostringstream&);
|
||||
void getAllHelp(std::ostringstream&);
|
||||
void getTopicHelp(
|
||||
std::string const& name, HelpTopic const&, std::ostringstream&);
|
||||
|
||||
class Members
|
||||
{
|
||||
friend class QPDFArgParser;
|
||||
@ -235,6 +329,9 @@ class QPDFArgParser
|
||||
std::vector<PointerHolder<char>> bash_argv;
|
||||
PointerHolder<char*> argv_ph;
|
||||
PointerHolder<char*> bash_argv_ph;
|
||||
std::map<std::string, HelpTopic> help_topics;
|
||||
std::map<std::string, HelpTopic> option_help;
|
||||
std::string help_footer;
|
||||
};
|
||||
PointerHolder<Members> m;
|
||||
};
|
||||
|
5
job.sums
5
job.sums
@ -1,5 +1,6 @@
|
||||
# Generated by generate_auto_job
|
||||
generate_auto_job 019081046f1bc19f498134eae00344ecfc65b4e52442ee5f1bc80bff99689443
|
||||
generate_auto_job 1f42fc554778d95210d11c44e858214b4854ead907d1c9ea84fe37f993ea1a23
|
||||
job.yml 25c85cba1ae01dac9cd0f9cb7b734e7e3e531c0023ea2b892dc0d40bda1c1146
|
||||
libqpdf/qpdf/auto_job_decl.hh 97395ecbe590b23ae04d6cce2080dbd0e998917ff5eeaa5c6aafa91041d3cd6a
|
||||
libqpdf/qpdf/auto_job_init.hh 465bf46769559ceb77110d1b9d3293ba9b3595850b49848c31aeabd10aadb4ad
|
||||
libqpdf/qpdf/auto_job_init.hh 2afffb5002ff28a3909f709709f65d77bf2289dd72d5ea3d1598a36664a49c73
|
||||
manual/cli.rst f0109cca3366a9da4b0a05e3cce996ece2d776321a3f689aeaa2d6af599eee88
|
||||
|
@ -36,12 +36,15 @@ QPDFArgParser::QPDFArgParser(int argc, char* argv[], char const* progname_env) :
|
||||
{
|
||||
selectHelpOptionTable();
|
||||
char const* help_choices[] = {"all", 0};
|
||||
// More help choices are added dynamically.
|
||||
addChoices(
|
||||
"help", bindParam(&QPDFArgParser::argHelp, this), false, help_choices);
|
||||
addInvalidChoiceHandler(
|
||||
"help", bindParam(&QPDFArgParser::invalidHelpArg, this));
|
||||
addBare("completion-bash",
|
||||
std::bind(std::mem_fn(&QPDFArgParser::argCompletionBash), this));
|
||||
bindBare(&QPDFArgParser::argCompletionBash, this));
|
||||
addBare("completion-zsh",
|
||||
std::bind(std::mem_fn(&QPDFArgParser::argCompletionZsh), this));
|
||||
bindBare(&QPDFArgParser::argCompletionZsh, this));
|
||||
selectMainOptionTable();
|
||||
}
|
||||
|
||||
@ -157,6 +160,22 @@ QPDFArgParser::addChoices(
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
QPDFArgParser::addInvalidChoiceHandler(
|
||||
std::string const& arg, param_arg_handler_t handler)
|
||||
{
|
||||
auto i = this->m->option_table->find(arg);
|
||||
if (i == this->m->option_table->end())
|
||||
{
|
||||
QTC::TC("libtests", "QPDFArgParser invalid choice handler to unknown");
|
||||
throw std::logic_error(
|
||||
"QPDFArgParser: attempt to add invalid choice handler"
|
||||
" to unknown argument");
|
||||
}
|
||||
auto& oe = i->second;
|
||||
oe.invalid_choice_handler = handler;
|
||||
}
|
||||
|
||||
void
|
||||
QPDFArgParser::copyFromOtherTable(std::string const& arg,
|
||||
std::string const& other_table)
|
||||
@ -258,9 +277,17 @@ QPDFArgParser::argCompletionZsh()
|
||||
}
|
||||
|
||||
void
|
||||
QPDFArgParser::argHelp(char*)
|
||||
QPDFArgParser::argHelp(char* p)
|
||||
{
|
||||
// QXXXQ
|
||||
std::cout << getHelp(p);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
void
|
||||
QPDFArgParser::invalidHelpArg(char* p)
|
||||
{
|
||||
usage(std::string("unknown help option") +
|
||||
(p ? (std::string(" ") + p) : ""));
|
||||
}
|
||||
|
||||
void
|
||||
@ -640,7 +667,14 @@ QPDFArgParser::parseArgs()
|
||||
{
|
||||
std::string message =
|
||||
"--" + arg_s + " must be given as --" + arg_s + "=";
|
||||
if (! oe.choices.empty())
|
||||
if (oe.invalid_choice_handler)
|
||||
{
|
||||
oe.invalid_choice_handler(parameter);
|
||||
// Method should call usage() or exit. Just in case it
|
||||
// doesn't...
|
||||
message += "option";
|
||||
}
|
||||
else if (! oe.choices.empty())
|
||||
{
|
||||
QTC::TC("libtests", "QPDFArgParser required choices");
|
||||
message += "{";
|
||||
@ -844,3 +878,166 @@ QPDFArgParser::handleCompletion()
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
void
|
||||
QPDFArgParser::addHelpFooter(std::string const& text)
|
||||
{
|
||||
this->m->help_footer = "\n" + text;
|
||||
}
|
||||
|
||||
void
|
||||
QPDFArgParser::addHelpTopic(std::string const& topic,
|
||||
std::string const& short_text,
|
||||
std::string const& long_text)
|
||||
{
|
||||
if (topic == "all")
|
||||
{
|
||||
QTC::TC("libtests", "QPDFArgParser add reserved help topic");
|
||||
throw std::logic_error(
|
||||
"QPDFArgParser: can't register reserved help topic " + topic);
|
||||
}
|
||||
if (! ((topic.length() > 0) && (topic.at(0) != '-')))
|
||||
{
|
||||
QTC::TC("libtests", "QPDFArgParser bad topic for help");
|
||||
throw std::logic_error(
|
||||
"QPDFArgParser: help topics must not start with -");
|
||||
}
|
||||
if (this->m->help_topics.count(topic))
|
||||
{
|
||||
QTC::TC("libtests", "QPDFArgParser add existing topic");
|
||||
throw std::logic_error(
|
||||
"QPDFArgParser: topic " + topic + " has already been added");
|
||||
}
|
||||
|
||||
this->m->help_topics[topic] = HelpTopic(short_text, long_text);
|
||||
this->m->help_option_table["help"].choices.insert(topic);
|
||||
}
|
||||
|
||||
void
|
||||
QPDFArgParser::addOptionHelp(std::string const& option_name,
|
||||
std::string const& topic,
|
||||
std::string const& short_text,
|
||||
std::string const& long_text)
|
||||
{
|
||||
if (! ((option_name.length() > 2) &&
|
||||
(option_name.at(0) == '-') &&
|
||||
(option_name.at(1) == '-')))
|
||||
{
|
||||
QTC::TC("libtests", "QPDFArgParser bad option for help");
|
||||
throw std::logic_error(
|
||||
"QPDFArgParser: options for help must start with --");
|
||||
}
|
||||
if (this->m->option_help.count(option_name))
|
||||
{
|
||||
QTC::TC("libtests", "QPDFArgParser duplicate option help");
|
||||
throw std::logic_error(
|
||||
"QPDFArgParser: option " + option_name + " already has help");
|
||||
}
|
||||
auto ht = this->m->help_topics.find(topic);
|
||||
if (ht == this->m->help_topics.end())
|
||||
{
|
||||
QTC::TC("libtests", "QPDFArgParser add to unknown topic");
|
||||
throw std::logic_error(
|
||||
"QPDFArgParser: unable to add option " + option_name +
|
||||
" to unknown help topic " + topic);
|
||||
}
|
||||
this->m->option_help[option_name] = HelpTopic(short_text, long_text);
|
||||
ht->second.options.insert(option_name);
|
||||
this->m->help_option_table["help"].choices.insert(option_name);
|
||||
}
|
||||
|
||||
void
|
||||
QPDFArgParser::getTopHelp(std::ostringstream& msg)
|
||||
{
|
||||
msg << "Run \"" << this->m->whoami
|
||||
<< " --help=topic\" for help on a topic." << std::endl
|
||||
<< "Run \"" << this->m->whoami
|
||||
<< " --help=option\" for help on an option." << std::endl
|
||||
<< "Run \"" << this->m->whoami
|
||||
<< " --help=all\" to see all available help." << std::endl
|
||||
<< std::endl
|
||||
<< "Topics:" << std::endl;
|
||||
for (auto const& i: this->m->help_topics)
|
||||
{
|
||||
msg << " " << i.first << ": " << i.second.short_text << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
QPDFArgParser::getAllHelp(std::ostringstream& msg)
|
||||
{
|
||||
getTopHelp(msg);
|
||||
auto show = [this, &msg](std::map<std::string, HelpTopic>& topics,
|
||||
std::string const& label) {
|
||||
for (auto const& i: topics)
|
||||
{
|
||||
auto const& topic = i.first;
|
||||
msg << std::endl
|
||||
<< "== " << label << " " << topic
|
||||
<< " (" << i.second.short_text << ") =="
|
||||
<< std::endl
|
||||
<< std::endl;
|
||||
getTopicHelp(topic, i.second, msg);
|
||||
}
|
||||
};
|
||||
show(this->m->help_topics, "topic");
|
||||
show(this->m->option_help, "option");
|
||||
msg << std::endl << "====" << std::endl;
|
||||
}
|
||||
|
||||
void
|
||||
QPDFArgParser::getTopicHelp(std::string const& name,
|
||||
HelpTopic const& ht,
|
||||
std::ostringstream& msg)
|
||||
{
|
||||
if (ht.long_text.empty())
|
||||
{
|
||||
msg << ht.short_text << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
msg << ht.long_text;
|
||||
}
|
||||
if (! ht.options.empty())
|
||||
{
|
||||
msg << std::endl << "Related options:" << std::endl;
|
||||
for (auto const& i: ht.options)
|
||||
{
|
||||
msg << " " << i << ": "
|
||||
<< this->m->option_help[i].short_text << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string
|
||||
QPDFArgParser::getHelp(char const* topic_or_option)
|
||||
{
|
||||
std::ostringstream msg;
|
||||
if ((topic_or_option == nullptr) || (strlen(topic_or_option) == 0))
|
||||
{
|
||||
getTopHelp(msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string arg(topic_or_option);
|
||||
if (arg == "all")
|
||||
{
|
||||
getAllHelp(msg);
|
||||
}
|
||||
else if (this->m->option_help.count(arg))
|
||||
{
|
||||
getTopicHelp(arg, this->m->option_help[arg], msg);
|
||||
}
|
||||
else if (this->m->help_topics.count(arg))
|
||||
{
|
||||
getTopicHelp(arg, this->m->help_topics[arg], msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
// should not be possible
|
||||
getTopHelp(msg);
|
||||
}
|
||||
}
|
||||
msg << this->m->help_footer;
|
||||
return msg.str();
|
||||
}
|
||||
|
@ -162,3 +162,4 @@ this->ap.copyFromOtherTable("annotate", "128-bit encryption");
|
||||
this->ap.copyFromOtherTable("form", "128-bit encryption");
|
||||
this->ap.copyFromOtherTable("modify-other", "128-bit encryption");
|
||||
this->ap.copyFromOtherTable("modify", "128-bit encryption");
|
||||
this->ap.addHelpFooter("For detailed help, visit the qpdf manual: https://qpdf.readthedocs.io\n");
|
||||
|
@ -68,6 +68,18 @@ ArgParser::initOptions()
|
||||
ap.addBare("sheep", [this](){ this->ap.selectOptionTable("sheep"); });
|
||||
ap.registerOptionTable("sheep", nullptr);
|
||||
ap.copyFromOtherTable("ewe", "baaa");
|
||||
|
||||
ap.addHelpFooter("For more help, read the manual.\n");
|
||||
ap.addHelpTopic(
|
||||
"quack", "Quack Options",
|
||||
"Just put stuff after quack to get a count at the end.\n");
|
||||
ap.addHelpTopic(
|
||||
"baaa", "Baaa Options",
|
||||
"Ewe can do sheepish things.\n"
|
||||
"For example, ewe can add more ram to your computer.\n");
|
||||
ap.addOptionHelp("--ewe", "baaa",
|
||||
"just for ewe", "You are not a ewe.\n");
|
||||
ap.addOptionHelp("--ram", "baaa", "curly horns", "");
|
||||
}
|
||||
|
||||
void
|
||||
@ -152,62 +164,60 @@ ArgParser::finalChecks()
|
||||
void
|
||||
ArgParser::test_exceptions()
|
||||
{
|
||||
try
|
||||
{
|
||||
auto err = [](char const* msg, std::function<void()> fn) {
|
||||
try
|
||||
{
|
||||
fn();
|
||||
assert(msg == nullptr);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cout << msg << ": " << e.what() << std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
err("duplicate handler", [this]() {
|
||||
ap.selectMainOptionTable();
|
||||
ap.addBare("potato", [](){});
|
||||
assert(false);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cout << "duplicate handler: " << e.what() << std::endl;
|
||||
}
|
||||
try
|
||||
{
|
||||
});
|
||||
err("duplicate handler", [this]() {
|
||||
ap.selectOptionTable("baaa");
|
||||
ap.addBare("ram", [](){});
|
||||
assert(false);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cout << "duplicate handler: " << e.what() << std::endl;
|
||||
}
|
||||
try
|
||||
{
|
||||
});
|
||||
err("duplicate table", [this]() {
|
||||
ap.registerOptionTable("baaa", nullptr);
|
||||
assert(false);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cout << "duplicate table: " << e.what() << std::endl;
|
||||
}
|
||||
try
|
||||
{
|
||||
});
|
||||
err("unknown table", [this]() {
|
||||
ap.selectOptionTable("aardvark");
|
||||
assert(false);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cout << "unknown table: " << e.what() << std::endl;
|
||||
}
|
||||
try
|
||||
{
|
||||
});
|
||||
err("copy from unknown table", [this]() {
|
||||
ap.copyFromOtherTable("one", "two");
|
||||
assert(false);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cout << "copy from unknown table: " << e.what() << std::endl;
|
||||
}
|
||||
try
|
||||
{
|
||||
});
|
||||
err("copy unknown from other table", [this]() {
|
||||
ap.copyFromOtherTable("two", "baaa");
|
||||
assert(false);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cout << "copy unknown from other table: " << e.what() << std::endl;
|
||||
}
|
||||
});
|
||||
err("add existing help topic", [this]() {
|
||||
ap.addHelpTopic("baaa", "potato", "salad");
|
||||
});
|
||||
err("add reserved help topic", [this]() {
|
||||
ap.addHelpTopic("all", "potato", "salad");
|
||||
});
|
||||
err("add to unknown topic", [this]() {
|
||||
ap.addOptionHelp("--new", "oops", "potato", "salad");
|
||||
});
|
||||
err("bad option for help", [this]() {
|
||||
ap.addOptionHelp("nodash", "baaa", "potato", "salad");
|
||||
});
|
||||
err("bad topic for help", [this]() {
|
||||
ap.addHelpTopic("--dashes", "potato", "salad");
|
||||
});
|
||||
err("duplicate option help", [this]() {
|
||||
ap.addOptionHelp("--ewe", "baaa", "potato", "salad");
|
||||
});
|
||||
err("invalid choice handler to unknown", [this]() {
|
||||
ap.addInvalidChoiceHandler(
|
||||
"elephant", [](char*){});
|
||||
});
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
|
@ -54,3 +54,10 @@ QPDFArgParser unrecognized 0
|
||||
QPDFArgParser complete choices 0
|
||||
QPDFArgParser copy from unknown 0
|
||||
QPDFArgParser copy unknown 0
|
||||
QPDFArgParser add reserved help topic 0
|
||||
QPDFArgParser add existing topic 0
|
||||
QPDFArgParser add to unknown topic 0
|
||||
QPDFArgParser duplicate option help 0
|
||||
QPDFArgParser bad option for help 0
|
||||
QPDFArgParser bad topic for help 0
|
||||
QPDFArgParser invalid choice handler to unknown 0
|
||||
|
@ -101,4 +101,30 @@ $td->runtest("args from stdin",
|
||||
{$td->FILE => "stdin.out", $td->EXIT_STATUS => 0},
|
||||
$td->NORMALIZE_NEWLINES);
|
||||
|
||||
$td->report(2 + (2 * scalar(@completion_tests)) + scalar(@arg_tests));
|
||||
my @help_tests = (
|
||||
'',
|
||||
'=all',
|
||||
'=--ewe',
|
||||
'=quack',
|
||||
);
|
||||
foreach my $i (@help_tests)
|
||||
{
|
||||
my $out = $i;
|
||||
$out =~ s/[=-]//g;
|
||||
if ($out ne '')
|
||||
{
|
||||
$out = "-$out";
|
||||
}
|
||||
$td->runtest("--help$i",
|
||||
{$td->COMMAND => "arg_parser --help$i"},
|
||||
{$td->FILE => "help$out.out", $td->EXIT_STATUS => 0},
|
||||
$td->NORMALIZE_NEWLINES);
|
||||
}
|
||||
|
||||
$td->runtest("bad help option",
|
||||
{$td->COMMAND => 'arg_parser --help=--oops'},
|
||||
{$td->FILE => "help-bad.out", $td->EXIT_STATUS => 2},
|
||||
$td->NORMALIZE_NEWLINES);
|
||||
|
||||
$td->report(3 + (2 * scalar(@completion_tests)) +
|
||||
scalar(@arg_tests) + scalar(@help_tests));
|
||||
|
@ -2,7 +2,9 @@
|
||||
--completion-zsh
|
||||
--help
|
||||
--help=
|
||||
--help=--ewe
|
||||
--help=all
|
||||
--help=quack
|
||||
--moo
|
||||
--moo=
|
||||
--oink=
|
||||
|
@ -1,5 +1,7 @@
|
||||
--baaa
|
||||
--completion-zsh
|
||||
--help
|
||||
--help=
|
||||
--moo
|
||||
--moo=
|
||||
--oink=
|
||||
|
@ -4,3 +4,10 @@ duplicate table: QPDFArgParser: registering already registered option table baaa
|
||||
unknown table: QPDFArgParser: selecting unregistered option table aardvark
|
||||
copy from unknown table: QPDFArgParser: attempt to copy from unknown table two
|
||||
copy unknown from other table: QPDFArgParser: attempt to copy unknown argument two from table baaa
|
||||
add existing help topic: QPDFArgParser: topic baaa has already been added
|
||||
add reserved help topic: QPDFArgParser: can't register reserved help topic all
|
||||
add to unknown topic: QPDFArgParser: unable to add option --new to unknown help topic oops
|
||||
bad option for help: QPDFArgParser: options for help must start with --
|
||||
bad topic for help: QPDFArgParser: help topics must not start with -
|
||||
duplicate option help: QPDFArgParser: option --ewe already has help
|
||||
invalid choice handler to unknown: QPDFArgParser: attempt to add invalid choice handler to unknown argument
|
||||
|
32
libtests/qtest/arg_parser/help-all.out
Normal file
32
libtests/qtest/arg_parser/help-all.out
Normal file
@ -0,0 +1,32 @@
|
||||
Run "arg_parser --help=topic" for help on a topic.
|
||||
Run "arg_parser --help=option" for help on an option.
|
||||
Run "arg_parser --help=all" to see all available help.
|
||||
|
||||
Topics:
|
||||
baaa: Baaa Options
|
||||
quack: Quack Options
|
||||
|
||||
== topic baaa (Baaa Options) ==
|
||||
|
||||
Ewe can do sheepish things.
|
||||
For example, ewe can add more ram to your computer.
|
||||
|
||||
Related options:
|
||||
--ewe: just for ewe
|
||||
--ram: curly horns
|
||||
|
||||
== topic quack (Quack Options) ==
|
||||
|
||||
Just put stuff after quack to get a count at the end.
|
||||
|
||||
== option --ewe (just for ewe) ==
|
||||
|
||||
You are not a ewe.
|
||||
|
||||
== option --ram (curly horns) ==
|
||||
|
||||
curly horns
|
||||
|
||||
====
|
||||
|
||||
For more help, read the manual.
|
1
libtests/qtest/arg_parser/help-bad.out
Normal file
1
libtests/qtest/arg_parser/help-bad.out
Normal file
@ -0,0 +1 @@
|
||||
usage: unknown help option --oops
|
3
libtests/qtest/arg_parser/help-ewe.out
Normal file
3
libtests/qtest/arg_parser/help-ewe.out
Normal file
@ -0,0 +1,3 @@
|
||||
You are not a ewe.
|
||||
|
||||
For more help, read the manual.
|
3
libtests/qtest/arg_parser/help-quack.out
Normal file
3
libtests/qtest/arg_parser/help-quack.out
Normal file
@ -0,0 +1,3 @@
|
||||
Just put stuff after quack to get a count at the end.
|
||||
|
||||
For more help, read the manual.
|
9
libtests/qtest/arg_parser/help.out
Normal file
9
libtests/qtest/arg_parser/help.out
Normal file
@ -0,0 +1,9 @@
|
||||
Run "arg_parser --help=topic" for help on a topic.
|
||||
Run "arg_parser --help=option" for help on an option.
|
||||
Run "arg_parser --help=all" to see all available help.
|
||||
|
||||
Topics:
|
||||
baaa: Baaa Options
|
||||
quack: Quack Options
|
||||
|
||||
For more help, read the manual.
|
Loading…
Reference in New Issue
Block a user