Basic and advanced command line parsing implemented.

This commit is contained in:
Moritz Bunkus 2009-08-07 01:17:58 +02:00
parent 0862d24e99
commit 718183d30a
9 changed files with 286 additions and 34 deletions

View File

@ -13,8 +13,8 @@
#include "propedit/change.h"
change_c::change_c(change_c::change_type_e type,
const std::string name,
const std::string value)
const std::string &name,
const std::string &value)
: m_type(type)
, m_name(name)
, m_value(value)

View File

@ -15,6 +15,8 @@
#include <string>
#include "common/smart_pointers.h"
class change_c {
public:
enum change_type_e {
@ -27,9 +29,10 @@ public:
std::string m_name, m_value;
public:
change_c(change_type_e type, const std::string name, const std::string value);
change_c(change_type_e type, const std::string &name, const std::string &value);
void validate();
};
typedef counted_ptr<change_c> change_cptr;
#endif // __PROPEDIT_CHANGE_H

View File

@ -20,4 +20,48 @@ options_c::options_c()
void
options_c::validate() {
if (m_file_name.empty())
mxerror(Y("No file name given.\n"));
}
target_cptr
options_c::add_target(target_c::target_type_e type,
const std::string &spec) {
target_cptr target(new target_c);
target->m_type = type;
target->parse_target_spec(spec);
m_targets.push_back(target);
return target;
}
target_cptr
options_c::add_target(target_c::target_type_e type) {
return add_target(type, "");
}
target_cptr
options_c::add_target(const std::string &spec) {
return add_target(target_c::tt_undefined, spec);
}
void
options_c::set_file_name(const std::string &file_name) {
if (!m_file_name.empty())
mxinfo(boost::format(Y("More than one file name has been given ('%1%' and '%2%').")) % m_file_name % file_name);
m_file_name = file_name;
}
void
options_c::set_parse_mode(const std::string &parse_mode) {
if (parse_mode == "full")
m_parse_mode = kax_analyzer_c::parse_mode_full;
else if (parse_mode == "fast")
m_parse_mode = kax_analyzer_c::parse_mode_fast;
else
throw false;
}

View File

@ -24,7 +24,7 @@
class options_c {
public:
std::string m_file_name;
std::vector<target_c> m_targets;
std::vector<target_cptr> m_targets;
bool m_show_progress;
kax_analyzer_c::parse_mode_e m_parse_mode;
@ -32,6 +32,15 @@ public:
options_c();
void validate();
target_cptr add_target(target_c::target_type_e type);
target_cptr add_target(const std::string &spec);
void set_file_name(const std::string &file_name);
void set_parse_mode(const std::string &parse_mode);
protected:
target_cptr add_target(target_c::target_type_e type, const std::string &spec);
};
typedef counted_ptr<options_c> options_cptr;
#endif // __PROPEDIT_OPTIONS_H

View File

@ -13,19 +13,19 @@
#include "propedit/setup.h"
static void
run(options_c &options) {
kax_analyzzer_cptr analyzer;
run(options_cptr options) {
console_kax_analyzer_cptr analyzer;
try {
if (!kax_analyzer_c::probe(options.m_file_name))
mxerror(boost::format("The file '%1%' is not a Matroska file or it could not be found.") % options.m_file_name);
if (!kax_analyzer_c::probe(options->m_file_name))
mxerror(boost::format("The file '%1%' is not a Matroska file or it could not be found.\n") % options->m_file_name);
analyzer = kax_analyzer_c::open(options.m_file_name);
analyzer = console_kax_analyzer_cptr(new console_kax_analyzer_c(options->m_file_name));
} catch (...) {
mxerror(boost::format("The file '%1%' could not be opened for read/write access.") % options.m_file_name);
mxerror(boost::format("The file '%1%' could not be opened for read/write access.\n") % options->m_file_name);
}
analyzer->set_show_progress(options->m_show_progress);
}
/** \brief Setup and high level program control
@ -38,8 +38,8 @@ main(int argc,
char **argv) {
setup();
options_c options = parse_args(command_line_utf8(argc, argv));
options.validate();
options_cptr options = propedit_cli_parser_c(command_line_utf8(argc, argv)).run();
options->validate();
run(options);

View File

@ -14,6 +14,7 @@
#include "common/os.h"
#include <algorithm>
#include <boost/bind.hpp>
#include <boost/regex.hpp>
#include <string>
#include <typeinfo>
@ -22,6 +23,9 @@
#include "common/command_line.h"
#include "common/common.h"
#include "common/ebml.h"
#include "common/translation.h"
#include "common/unique_numbers.h"
#include "common/xml/element_mapping.h"
#include "propedit/setup.h"
#ifdef SYS_WINDOWS
@ -30,27 +34,115 @@
using namespace libmatroska;
/** \brief Outputs usage information
*/
static void
set_usage() {
usage_text = "";
usage_text += Y("mkvpropedit <file> [options]\n");
usage_text += "\n";
usage_text += Y(" Global options:\n");
usage_text += Y(" -v, --verbose verbose status\n");
usage_text += "\n";
propedit_cli_parser_c::propedit_cli_parser_c(const std::vector<std::string> &args)
: cli_parser_c(args)
, m_options(options_cptr(new options_c))
, m_target(m_options->add_target(target_c::tt_segment_info))
{
}
options_c
parse_args(std::vector<std::string> args) {
set_usage();
while (handle_common_cli_args(args, ""))
set_usage();
propedit_cli_parser_c::~propedit_cli_parser_c() {
}
options_c options;
void
propedit_cli_parser_c::set_usage() {
usage_text = "";
usage_text += Y("mkvpropedit [options] <file> [actions]\n");
usage_text += "\n";
usage_text += Y(" Options:\n");
usage_text += Y(" -v, --verbose Show verbose progress reports\n");
usage_text += Y(" -p, --parse-mode <mode> Sets the Matroska parser mode to 'fast'\n");
usage_text += Y(" (default) or 'full'\n");
usage_text += "\n";
usage_text += Y(" Actions:\n");
usage_text += Y(" -e, --edit <selector> Sets the Matroska file section that all following\n");
usage_text += Y(" add/set/delete actions operate on (see man page)\n");
usage_text += Y(" -a, --add <name=value> Add a property with a value even if such a\n");
usage_text += Y(" property already exists\n");
usage_text += Y(" -s, --set <name=value> Set a property to a value if it exists and\n");
usage_text += Y(" add it otherwise\n");
usage_text += Y(" -d, --delete <name> Delete all occurences of a property\n");
usage_text += Y(" Other options:\n");
usage_text += Y(" -l, --list-property-names\n");
usage_text += Y(" Lists all valid property names\n");
usage_text += Y(" --ui-language <code> Force the translations for 'code' to be used.\n");
usage_text += Y(" --command-line-charset <charset>\n"
" Charset for strings on the command line\n");
usage_text += Y(" --output-charset <cset> Output messages in this charset\n");
usage_text += Y(" -r, --redirect-output <file>\n"
" Redirects all messages into this file.\n");
usage_text += Y(" @optionsfile Reads additional command line options from\n"
" the specified file (see man page).\n");
usage_text += Y(" -h, --help Show this help.\n");
usage_text += Y(" -V, --version Show version information.\n");
usage_text += "\n\n";
usage_text += Y("The order of the various options is not important.\n");
return options;
version_info = "mkvpropedit v" VERSION " ('" VERSIONNAME "')";
}
void
propedit_cli_parser_c::add_option(const std::string &spec,
void (propedit_cli_parser_c::*callback)(),
const std::string &description) {
cli_parser_c::add_option(spec, boost::bind(callback, this), description);
}
void
propedit_cli_parser_c::set_parse_mode() {
try {
m_options->set_parse_mode(m_next_arg);
} catch (...) {
mxerror(boost::format(Y("Unknown parse mode in '%1% %2%'.")) % m_current_arg % m_next_arg);
}
}
void
propedit_cli_parser_c::add_target() {
try {
m_target = m_options->add_target(m_next_arg);
} catch (...) {
mxerror(boost::format(Y("Invalid selector in '%1% %2%'.\n")) % m_current_arg % m_next_arg);
}
}
void
propedit_cli_parser_c::add_change() {
try {
change_c::change_type_e type = (m_current_arg == "-a") || (m_current_arg == "--add") ? change_c::ct_add
: (m_current_arg == "-s") || (m_current_arg == "--set") ? change_c::ct_set
: change_c::ct_delete;
m_target->add_change(type, m_next_arg);
} catch (const char *message) {
mxerror(boost::format(Y("Invalid change spec (%3%) in '%1% %2%'.\n")) % m_current_arg % m_next_arg % message);
}
}
void
propedit_cli_parser_c::list_property_names() {
mxinfo(Y("All known property names and their meaning:\n"));
mxexit(0);
}
void
propedit_cli_parser_c::set_file_name() {
m_options->set_file_name(m_current_arg);
}
options_cptr
propedit_cli_parser_c::run() {
add_option("e|edit=s", &propedit_cli_parser_c::add_target, "");
add_option("a|add|s|set|d|delete=s", &propedit_cli_parser_c::add_change, "");
add_option("p|parse-mode=s", &propedit_cli_parser_c::set_parse_mode, "");
add_option("l|list-propety-names", &propedit_cli_parser_c::list_property_names, "");
set_default_callback(boost::bind(&propedit_cli_parser_c::set_file_name, this));
parse_args();
m_options->validate();
m_options->m_show_progress = 1 < verbose;
return m_options;
}
/** \brief Initialize global variables

View File

@ -16,9 +16,32 @@
#include <string>
#include <vector>
#include "common/cli_parser.h"
#include "propedit/options.h"
void setup();
options_c parse_args(std::vector<std::string> args);
class propedit_cli_parser_c: public cli_parser_c {
protected:
options_cptr m_options;
target_cptr m_target;
public:
propedit_cli_parser_c(const std::vector<std::string> &args);
virtual ~propedit_cli_parser_c();
virtual options_cptr run();
protected:
void add_option(const std::string &spec, void (propedit_cli_parser_c::*callback)(), const std::string &description);
virtual void set_usage();
void add_target();
void add_change();
void set_parse_mode();
void set_file_name();
void list_property_names();
};
#endif // __PROPEDIT_SETUP_H

View File

@ -10,13 +10,70 @@
#include "common/os.h"
#include <boost/regex.hpp>
#include "common/common.h"
#include "common/strings/editing.h"
#include "propedit/target.h"
target_c::target_c()
: m_target(NULL)
: m_type(target_c::tt_undefined)
, m_selection_mode(target_c::sm_undefined)
, m_selection_param(-1)
, m_target(NULL)
{
}
void
target_c::validate() {
}
void
target_c::add_change(change_c::change_type_e type,
const std::string &spec) {
std::string name, value;
if (change_c::ct_delete == type)
name = spec;
else {
std::vector<std::string> parts = split(spec, "=", 2);
if (2 != parts.size())
throw Y("missing value");
name = parts[0];
value = parts[1];
}
if (name.empty())
throw Y("missing property name");
m_changes.push_back(change_cptr(new change_c(type, name, value)));
}
void
target_c::parse_target_spec(std::string spec) {
if (spec.empty())
return;
spec = downcase(spec);
if ((spec == "segment_info") || (spec == "segmentinfo") || (spec == "info")) {
m_type = target_c::tt_segment_info;
return;
}
std::string prefix("track:");
if (starts_with_case(spec, prefix)) {
parse_track_spec(spec.substr(prefix.length()));
return;
}
}
void
target_c::parse_track_spec(const std::string &spec) {
boost::regex track_re("^([[:alpha:]]+)?(_[[:alpha:]]+)?(\\.[^@]+)?(@.+)?", boost::regex::perl);
boost::smatch matches;
if (!boost::regex_match(spec, matches, track_re))
throw false;
}

View File

@ -20,16 +20,40 @@
#include "propedit/change.h"
using namespace libebml;
class target_c {
public:
std::vector<change_c> m_changes;
std::string m_target_spec;
enum target_type_e {
tt_undefined,
tt_segment_info,
tt_track,
};
enum selection_mode_e {
sm_undefined,
sm_by_number,
sm_by_uid,
sm_by_position,
sm_by_type_and_position,
};
target_type_e m_type;
selection_mode_e m_selection_mode;
int64_t m_selection_param;
EbmlMaster *m_target;
std::vector<change_cptr> m_changes;
public:
target_c();
void validate();
void add_change(change_c::change_type_e type, const std::string &spec);
void parse_target_spec(std::string spec);
void parse_track_spec(const std::string &spec);
};
typedef counted_ptr<target_c> target_cptr;
#endif // __PROPEDIT_TARGET_H