--- /dev/null
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Author: Andy Rushton\r
+// Copyright: (c) Southampton University 1999-2004\r
+// (c) Andy Rushton 2004-2009\r
+// License: BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "cli_parser.hpp"\r
+#include "file_system.hpp"\r
+#include "dprintf.hpp"\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus \r
+{\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // cli_definition internals\r
+\r
+ const std::string& stlplus::cli_definition::name(void) const\r
+ {\r
+ return m_name;\r
+ }\r
+\r
+ stlplus::cli_kind_t stlplus::cli_definition::kind(void) const\r
+ { \r
+ return m_kind;\r
+ }\r
+\r
+ stlplus::cli_mode_t stlplus::cli_definition::mode(void) const\r
+ {\r
+ return m_mode;\r
+ }\r
+\r
+ const std::string& stlplus::cli_definition::message(void) const\r
+ {\r
+ return m_message;\r
+ }\r
+\r
+ const std::string& stlplus::cli_definition::default_value(void) const\r
+ {\r
+ return m_default;\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // internal data structures\r
+\r
+ class cli_value\r
+ {\r
+ public:\r
+ unsigned m_definition;\r
+ std::string m_value;\r
+ unsigned m_level;\r
+ std::string m_source;\r
+\r
+ cli_value(unsigned definition, const std::string& value, unsigned level, const std::string& source) :\r
+ m_definition(definition), m_value(value), m_level(level), m_source(source) \r
+ {\r
+ }\r
+ };\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+ class cli_parser_data\r
+ {\r
+ public:\r
+ message_handler& m_messages;\r
+ std::string m_executable;\r
+ cli_parser::definitions m_definitions;\r
+ std::vector<cli_value> m_values;\r
+ unsigned m_level;\r
+ bool m_valid;\r
+ std::vector<std::string> m_ini_files;\r
+\r
+ public:\r
+\r
+ cli_parser_data(message_handler& messages) : \r
+ m_messages(messages), m_level(1), m_valid(true)\r
+ {\r
+ // ensure that the CLI's messages are in the message handler - these\r
+ // can be overridden from a file later - see message_handler\r
+ if (!m_messages.message_present("CLI_VALUE_MISSING"))\r
+ m_messages.add_message("CLI_VALUE_MISSING", "option @0 requires a value - end of line was reached instead");\r
+ if (!m_messages.message_present("CLI_UNRECOGNISED_OPTION"))\r
+ m_messages.add_message("CLI_UNRECOGNISED_OPTION", "@0 is not a recognised option for this command");\r
+ if (!m_messages.message_present("CLI_NO_VALUES"))\r
+ m_messages.add_message("CLI_NO_VALUES", "argument @0 is invalid - this program doesn't take command-line arguments");\r
+ if (!m_messages.message_present("CLI_USAGE_PROGRAM"))\r
+ m_messages.add_message("CLI_USAGE_PROGRAM", "usage:\n\t@0 { arguments }");\r
+ if (!m_messages.message_present("CLI_USAGE_DEFINITIONS"))\r
+ m_messages.add_message("CLI_USAGE_DEFINITIONS", "arguments:");\r
+ if (!m_messages.message_present("CLI_USAGE_VALUES"))\r
+ m_messages.add_message("CLI_USAGE_VALUES", "values:");\r
+ if (!m_messages.message_present("CLI_USAGE_VALUE_RESULT"))\r
+ m_messages.add_message("CLI_USAGE_VALUE_RESULT", "\t@0 : from @1");\r
+ if (!m_messages.message_present("CLI_USAGE_SWITCH_RESULT"))\r
+ m_messages.add_message("CLI_USAGE_SWITCH_RESULT", "\t-@0 : from @1");\r
+ if (!m_messages.message_present("CLI_USAGE_OPTION_RESULT"))\r
+ m_messages.add_message("CLI_USAGE_OPTION_RESULT", "\t-@0 @1 : from @2");\r
+ if (!m_messages.message_present("CLI_INI_HEADER"))\r
+ m_messages.add_message("CLI_INI_HEADER", "configuration files:");\r
+ if (!m_messages.message_present("CLI_INI_FILE_PRESENT"))\r
+ m_messages.add_message("CLI_INI_FILE_PRESENT", "\t@0");\r
+ if (!m_messages.message_present("CLI_INI_FILE_ABSENT"))\r
+ m_messages.add_message("CLI_INI_FILE_ABSENT", "\t@0 (not found)");\r
+ if (!m_messages.message_present("CLI_INI_VARIABLE"))\r
+ m_messages.add_message("CLI_INI_VARIABLE", "unknown variable \"@0\" found in Ini file");\r
+ }\r
+\r
+ unsigned add_definition(const cli_parser::definition& definition)\r
+ {\r
+ m_definitions.push_back(definition);\r
+ return m_definitions.size()-1;\r
+ }\r
+\r
+ std::string name(unsigned i) const throw(cli_index_error)\r
+ {\r
+ if (i >= m_values.size()) throw cli_index_error("Index " + dformat("%u",i) + " out of range");\r
+ return m_definitions[m_values[i].m_definition].name();\r
+ }\r
+\r
+ unsigned id(unsigned i) const throw(cli_index_error)\r
+ {\r
+ if (i >= m_values.size()) throw cli_index_error("Index " + dformat("%u",i) + " out of range");\r
+ return m_values[i].m_definition;\r
+ }\r
+\r
+ cli_parser::kind_t kind(unsigned i) const throw(cli_index_error)\r
+ {\r
+ if (i >= m_values.size()) throw cli_index_error("Index " + dformat("%u",i) + " out of range");\r
+ return m_definitions[m_values[i].m_definition].kind();\r
+ }\r
+\r
+ cli_parser::mode_t mode(unsigned i) const throw(cli_index_error)\r
+ {\r
+ if (i >= m_values.size()) throw cli_index_error("Index " + dformat("%u",i) + " out of range");\r
+ return m_definitions[m_values[i].m_definition].mode();\r
+ }\r
+\r
+// unsigned add_definition(const std::string& name,\r
+// cli_parser::kind_t kind,\r
+// cli_parser::mode_t mode,\r
+// const std::string& message)\r
+// {\r
+// return add_definition(cli_parser::definition(name, kind, mode, message));\r
+// }\r
+\r
+ unsigned find_definition(const std::string& name)\r
+ {\r
+ // this does substring matching on the definitions and returns the first\r
+ // match - however, it requires at least one character in the substring so\r
+ // that the "" convention for command line arguments doesn't match with\r
+ // anything. It returns size() if it fails\r
+ for (unsigned i = 0; i < m_definitions.size(); i++)\r
+ {\r
+ std::string candidate = m_definitions[i].name();\r
+ if ((candidate.empty() && name.empty()) ||\r
+ (!name.empty() && candidate.size() >= name.size() && candidate.substr(0,name.size()) == name))\r
+ return i;\r
+ }\r
+ return m_definitions.size();\r
+ }\r
+\r
+ void clear_definitions(void)\r
+ {\r
+ m_definitions.clear();\r
+ m_values.clear();\r
+ reset_level();\r
+ set_valid();\r
+ }\r
+\r
+ void reset_level(void)\r
+ {\r
+ // the starting level is 1 so that later functions can call clear_level with\r
+ // a value of m_level-1 without causing underflow\r
+ m_level = 1;\r
+ }\r
+\r
+ void increase_level(void)\r
+ {\r
+ m_level++;\r
+ }\r
+\r
+ void clear_level(unsigned definition, unsigned level)\r
+ {\r
+ // clears all values with this definition at the specified level or below\r
+ for (std::vector<cli_value>::iterator i = m_values.begin(); i != m_values.end(); )\r
+ {\r
+ if (i->m_definition == definition && i->m_level <= level)\r
+ i = m_values.erase(i);\r
+ else\r
+ i++;\r
+ }\r
+ }\r
+\r
+ void set_valid(void)\r
+ {\r
+ m_valid = true;\r
+ }\r
+\r
+ void set_invalid(void)\r
+ {\r
+ m_valid = false;\r
+ }\r
+\r
+ bool valid(void) const\r
+ {\r
+ return m_valid;\r
+ }\r
+\r
+ unsigned add_value(unsigned definition, const std::string& value, const std::string& source)\r
+ {\r
+ // behaviour depends on mode:\r
+ // - single: erase all previous values\r
+ // - multiple: erase values at a lower level than current\r
+ // - cumulative: erase no previous values\r
+ switch (m_definitions[definition].mode())\r
+ {\r
+ case cli_single_mode:\r
+ clear_level(definition, m_level);\r
+ break;\r
+ case cli_multiple_mode:\r
+ clear_level(definition, m_level-1);\r
+ break;\r
+ case cli_cumulative_mode:\r
+ break;\r
+ }\r
+ m_values.push_back(cli_value(definition,value,m_level,source));\r
+ return m_values.size()-1;\r
+ }\r
+\r
+ unsigned add_switch(unsigned definition, bool value, const std::string& source)\r
+ {\r
+ return add_value(definition, value ? "on" : "off", source);\r
+ }\r
+\r
+ void erase_value(unsigned definition)\r
+ {\r
+ // this simply erases all previous values\r
+ clear_level(definition, m_level);\r
+ }\r
+\r
+ void add_ini_file(const std::string& file)\r
+ {\r
+ m_ini_files.push_back(file);\r
+ }\r
+\r
+ unsigned ini_file_size(void) const\r
+ {\r
+ return m_ini_files.size();\r
+ }\r
+\r
+ const std::string& ini_file(unsigned i) const\r
+ {\r
+ return m_ini_files[i];\r
+ }\r
+\r
+ unsigned add_checked_definition(const cli_parser::definition& definition) throw(cli_mode_error)\r
+ {\r
+ // check for stupid combinations\r
+ // at this stage the only really stupid one is to declare command line arguments to be switch mode\r
+ if (definition.name().empty() && definition.kind() == cli_switch_kind) \r
+ {\r
+ set_invalid();\r
+ throw cli_mode_error("CLI arguments cannot be switch kind");\r
+ }\r
+ // add the definition to the set of all definitions\r
+ unsigned i = add_definition(definition);\r
+ // also add it to the list of values, but only if it has a default value\r
+ if (!definition.default_value().empty())\r
+ add_value(i, definition.default_value(), "builtin default");\r
+ return i;\r
+ }\r
+\r
+ bool switch_value(unsigned i) const throw(cli_mode_error,cli_index_error)\r
+ {\r
+ if (i >= m_values.size()) throw cli_index_error("Index " + dformat("%u",i) + " out of range");\r
+ if (kind(i) != cli_switch_kind) throw cli_mode_error(name(i) + " is not a switch kind");\r
+ std::string value = m_values[i].m_value;\r
+ return value == "on" || value == "true" || value == "1";\r
+ }\r
+\r
+ std::string string_value(unsigned i) const throw(cli_mode_error,cli_index_error)\r
+ {\r
+ if (i >= m_values.size()) throw cli_index_error("Index " + dformat("%u",i) + " out of range");\r
+ if (kind(i) != cli_value_kind) throw cli_mode_error(name(i) + " is not a value kind");\r
+ return m_values[i].m_value;\r
+ }\r
+\r
+ void set_defaults(const ini_manager& defaults, const std::string& ini_section) throw()\r
+ {\r
+ // import default values from the Ini Manager\r
+ increase_level();\r
+ // get the set of all names from the Ini manager so that illegal names generate meaningful error messages\r
+ std::vector<std::string> names = defaults.variable_names(ini_section);\r
+ for (unsigned i = 0; i < names.size(); i++)\r
+ {\r
+ std::string name = names[i];\r
+ unsigned definition = find_definition(name);\r
+ if (definition == (unsigned)-1)\r
+ {\r
+ // not found - give an error report\r
+ message_position position(defaults.variable_filename(ini_section,name),\r
+ defaults.variable_linenumber(ini_section,name),\r
+ 0);\r
+ m_messages.error(position,"CLI_INI_VARIABLE", name);\r
+ }\r
+ else\r
+ {\r
+ // found - so add the value\r
+ // multi-valued variables are entered as a comma-separated list and this is then turned into a vector\r
+ // the vector is empty if the value was empty\r
+ std::vector<std::string> values = defaults.variable_values(ini_section, name);\r
+ // an empty string is used to negate the value\r
+ if (values.empty())\r
+ erase_value(definition);\r
+ else\r
+ {\r
+ std::string source = filespec_to_relative_path(defaults.variable_filename(ini_section, name));\r
+ for (unsigned j = 0; j < values.size(); j++)\r
+ add_value(definition, values[j], source);\r
+ }\r
+ }\r
+ }\r
+ // add the set of ini files to the list for usage reports\r
+ for (unsigned j = 0; j < defaults.size(); j++)\r
+ add_ini_file(defaults.filename(j));\r
+ }\r
+\r
+ bool parse(char* argv[]) throw(cli_argument_error,message_handler_id_error,message_handler_format_error)\r
+ {\r
+ bool result = true;\r
+ if (!argv) throw cli_argument_error("Argument vector cannot be null");\r
+ increase_level();\r
+ if (argv[0])\r
+ m_executable = argv[0];\r
+ for (unsigned i = 1; argv[i]; i++)\r
+ {\r
+ std::string name = argv[i];\r
+ if (!name.empty() && name[0] == '-')\r
+ {\r
+ // we have a command line option\r
+ unsigned found = find_definition(name.substr(1, name.size()-1));\r
+ if (found < m_definitions.size())\r
+ {\r
+ // found it in its positive form\r
+ switch (m_definitions[found].kind())\r
+ {\r
+ case cli_switch_kind:\r
+ add_switch(found, true, "command line");\r
+ break;\r
+ case cli_value_kind:\r
+ // get the next argument in argv as the value of this option\r
+ // first check that there is a next value\r
+ if (!argv[i+1])\r
+ result &= m_messages.error("CLI_VALUE_MISSING", name);\r
+ else\r
+ add_value(found, argv[++i], "command line");\r
+ break;\r
+ }\r
+ }\r
+ else if (name.size() > 3 && name.substr(1,2) == "no")\r
+ {\r
+ found = find_definition(name.substr(3, name.size()-3));\r
+ if (found < m_definitions.size())\r
+ {\r
+ // found it in its negated form\r
+ switch (m_definitions[found].kind())\r
+ {\r
+ case cli_switch_kind:\r
+ add_switch(found, false, "command line");\r
+ break;\r
+ case cli_value_kind:\r
+ erase_value(found);\r
+ break;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ // could not find this option in either its true or negated form\r
+ result &= m_messages.error("CLI_UNRECOGNISED_OPTION", name);\r
+ }\r
+ }\r
+ else\r
+ {\r
+ // could not find this option and it could not be negated\r
+ result &= m_messages.error("CLI_UNRECOGNISED_OPTION", name);\r
+ }\r
+ }\r
+ else\r
+ {\r
+ // we have a command-line value which is represented internally as an option with an empty string as its name\r
+ // some very obscure commands do not have values - only options, so allow for that case too\r
+ unsigned found = find_definition("");\r
+ if (found < m_definitions.size())\r
+ add_value(found, name, "command line");\r
+ else\r
+ result &= m_messages.error("CLI_NO_VALUES", name);\r
+ }\r
+ }\r
+ if (!result) set_invalid();\r
+ return result;\r
+ }\r
+\r
+ void usage(void) const throw(std::runtime_error)\r
+ {\r
+ m_messages.information("CLI_USAGE_PROGRAM", m_executable);\r
+ m_messages.information("CLI_USAGE_DEFINITIONS");\r
+ for (unsigned d = 0; d < m_definitions.size(); d++)\r
+ m_messages.information(m_definitions[d].message());\r
+ if (m_values.size() != 0)\r
+ {\r
+ m_messages.information("CLI_USAGE_VALUES");\r
+ for (unsigned v = 0; v < m_values.size(); v++)\r
+ {\r
+ std::string source = m_values[v].m_source;\r
+ std::string key = name(v);\r
+ if (key.empty())\r
+ {\r
+ // command-line values\r
+ m_messages.information("CLI_USAGE_VALUE_RESULT", string_value(v), source);\r
+ }\r
+ else if (kind(v) == cli_switch_kind)\r
+ {\r
+ // a switch\r
+ m_messages.information("CLI_USAGE_SWITCH_RESULT", (switch_value(v) ? name(v) : "no" + name(v)), source);\r
+ }\r
+ else\r
+ {\r
+ // other values\r
+ std::vector<std::string> args;\r
+ args.push_back(name(v));\r
+ args.push_back(string_value(v));\r
+ args.push_back(source);\r
+ m_messages.information("CLI_USAGE_OPTION_RESULT", args);\r
+ }\r
+ }\r
+ }\r
+ if (ini_file_size() > 0)\r
+ {\r
+ m_messages.information("CLI_INI_HEADER");\r
+ for (unsigned i = 0; i < ini_file_size(); i++)\r
+ {\r
+ if (file_exists(ini_file(i)))\r
+ m_messages.information("CLI_INI_FILE_PRESENT", filespec_to_relative_path(ini_file(i)));\r
+ else\r
+ m_messages.information("CLI_INI_FILE_ABSENT", filespec_to_relative_path(ini_file(i)));\r
+ }\r
+ }\r
+ }\r
+\r
+ private:\r
+ // make this class uncopyable\r
+ cli_parser_data(const cli_parser_data&);\r
+ cli_parser_data& operator = (const cli_parser_data&);\r
+ };\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+ cli_parser::cli_parser(message_handler& messages) throw() : \r
+ m_data(new cli_parser_data(messages))\r
+ {\r
+ }\r
+\r
+ cli_parser::cli_parser(cli_parser::definitions_t definitions, message_handler& messages) throw(cli_mode_error) : \r
+ m_data(new cli_parser_data(messages))\r
+ {\r
+ add_definitions(definitions);\r
+ }\r
+\r
+ cli_parser::cli_parser(cli_parser::definitions_t definitions, const ini_manager& defaults, const std::string& ini_section, message_handler& messages) throw(cli_mode_error) : \r
+ m_data(new cli_parser_data(messages))\r
+ {\r
+ add_definitions(definitions);\r
+ set_defaults(defaults, ini_section);\r
+ }\r
+\r
+ cli_parser::cli_parser(char* argv[], cli_parser::definitions_t definitions, message_handler& messages) throw(cli_mode_error,message_handler_id_error,message_handler_format_error) : \r
+ m_data(new cli_parser_data(messages))\r
+ {\r
+ add_definitions(definitions);\r
+ parse(argv);\r
+ }\r
+\r
+ cli_parser::cli_parser(char* argv[], cli_parser::definitions_t definitions, const ini_manager& defaults, const std::string& ini_section, message_handler& messages) throw(cli_mode_error,message_handler_id_error,message_handler_format_error) : \r
+ m_data(new cli_parser_data(messages))\r
+ {\r
+ add_definitions(definitions);\r
+ set_defaults(defaults, ini_section);\r
+ parse(argv);\r
+ }\r
+\r
+ cli_parser::cli_parser(cli_parser::definitions definitions, message_handler& messages) throw(cli_mode_error) : \r
+ m_data(new cli_parser_data(messages))\r
+ {\r
+ add_definitions(definitions);\r
+ }\r
+\r
+ cli_parser::cli_parser(cli_parser::definitions definitions, const ini_manager& defaults, const std::string& ini_section, message_handler& messages) throw(cli_mode_error) : \r
+ m_data(new cli_parser_data(messages))\r
+ {\r
+ add_definitions(definitions);\r
+ set_defaults(defaults, ini_section);\r
+ }\r
+\r
+ cli_parser::cli_parser(char* argv[], cli_parser::definitions definitions, message_handler& messages) throw(cli_mode_error,message_handler_id_error,message_handler_format_error) : \r
+ m_data(new cli_parser_data(messages))\r
+ {\r
+ add_definitions(definitions);\r
+ parse(argv);\r
+ }\r
+\r
+ cli_parser::cli_parser(char* argv[], cli_parser::definitions definitions, const ini_manager& defaults, const std::string& ini_section, message_handler& messages) throw(cli_mode_error,message_handler_id_error,message_handler_format_error) : \r
+ m_data(new cli_parser_data(messages))\r
+ {\r
+ add_definitions(definitions);\r
+ set_defaults(defaults, ini_section);\r
+ parse(argv);\r
+ }\r
+\r
+ cli_parser::~cli_parser(void) throw()\r
+ {\r
+ }\r
+\r
+ void cli_parser::add_definitions(cli_parser::definitions_t definitions) throw(cli_mode_error)\r
+ {\r
+ m_data->clear_definitions();\r
+ // the definitions array is terminated by a definition with a null name pointer\r
+ for (unsigned i = 0; definitions[i].m_name; i++)\r
+ add_definition(definitions[i]);\r
+ }\r
+\r
+ unsigned cli_parser::add_definition(const cli_parser::definition_t& definition) throw(cli_mode_error,cli_argument_error)\r
+ {\r
+ std::string name = definition.m_name ? definition.m_name : "";\r
+ std::string message = definition.m_message ? definition.m_message : "";\r
+ std::string value = definition.m_default ? definition.m_default : "";\r
+ return add_definition(cli_parser::definition(name, definition.m_kind, definition.m_mode, message, value));\r
+ }\r
+\r
+ void cli_parser::add_definitions(cli_parser::definitions definitions) throw(cli_mode_error)\r
+ {\r
+ m_data->clear_definitions();\r
+ for (unsigned i = 0; i < definitions.size(); i++)\r
+ add_definition(definitions[i]);\r
+ }\r
+\r
+ unsigned cli_parser::add_definition(const cli_parser::definition& definition) throw(cli_mode_error)\r
+ {\r
+ return m_data->add_checked_definition(definition);\r
+ }\r
+\r
+ void cli_parser::set_defaults(const ini_manager& defaults, const std::string& ini_section) throw()\r
+ {\r
+ m_data->set_defaults(defaults, ini_section);\r
+ }\r
+\r
+ bool cli_parser::parse(char* argv[]) throw(cli_argument_error,message_handler_id_error,message_handler_format_error)\r
+ {\r
+ return m_data->parse(argv);\r
+ }\r
+\r
+ bool cli_parser::valid(void) throw()\r
+ {\r
+ return m_data->valid();\r
+ }\r
+\r
+ unsigned cli_parser::size(void) const throw()\r
+ {\r
+ return m_data->m_values.size();\r
+ }\r
+\r
+ std::string cli_parser::name(unsigned i) const throw(cli_index_error)\r
+ {\r
+ return m_data->name(i);\r
+ }\r
+\r
+ unsigned cli_parser::id(unsigned i) const throw(cli_index_error)\r
+ {\r
+ return m_data->id(i);\r
+ }\r
+\r
+ cli_parser::kind_t cli_parser::kind(unsigned i) const throw(cli_index_error)\r
+ {\r
+ return m_data->kind(i);\r
+ }\r
+\r
+ bool cli_parser::switch_kind(unsigned i) const throw(cli_index_error)\r
+ {\r
+ return kind(i) == cli_switch_kind;\r
+ }\r
+\r
+ bool cli_parser::value_kind(unsigned i) const throw(cli_index_error)\r
+ {\r
+ return kind(i) == cli_value_kind;\r
+ }\r
+\r
+ cli_parser::mode_t cli_parser::mode(unsigned i) const throw(cli_index_error)\r
+ {\r
+ return m_data->mode(i);\r
+ }\r
+\r
+ bool cli_parser::single_mode(unsigned i) const throw(cli_index_error)\r
+ {\r
+ return mode(i) == cli_single_mode;\r
+ }\r
+\r
+ bool cli_parser::multiple_mode(unsigned i) const throw(cli_index_error)\r
+ {\r
+ return mode(i) == cli_multiple_mode;\r
+ }\r
+\r
+ bool cli_parser::cumulative_mode(unsigned i) const throw(cli_index_error)\r
+ {\r
+ return mode(i) == cli_cumulative_mode;\r
+ }\r
+\r
+ bool cli_parser::switch_value(unsigned i) const throw(cli_mode_error,cli_index_error)\r
+ {\r
+ return m_data->switch_value(i);\r
+ }\r
+\r
+ std::string cli_parser::string_value(unsigned i) const throw(cli_mode_error,cli_index_error)\r
+ {\r
+ return m_data->string_value(i);\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+ void cli_parser::usage(void) const throw(std::runtime_error)\r
+ {\r
+ m_data->usage();\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r