From: Arvin Schnell Date: Tue, 8 Feb 2011 22:44:15 +0000 (+0100) Subject: - rewrote option parsing in snapper X-Git-Tag: v0.1.3~503 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8edadf1ac97877c53b2f2992e0670940fa0ceff7;p=thirdparty%2Fsnapper.git - rewrote option parsing in snapper --- diff --git a/snapper/AppUtil.h b/snapper/AppUtil.h index 8f079a70..dcf5a508 100644 --- a/snapper/AppUtil.h +++ b/snapper/AppUtil.h @@ -120,6 +120,9 @@ void initDefaultLogger(); string native; string text; + + friend std::ostream& operator<<(std::ostream& s, const Text& text) + { return s << text.text; } }; diff --git a/tools/snapper.cc b/tools/snapper.cc index deab0aee..11889d89 100644 --- a/tools/snapper.cc +++ b/tools/snapper.cc @@ -1,6 +1,5 @@ #include -#include #include #include @@ -12,39 +11,79 @@ #include #include -#include +#include "utils/Table.h" +#include "utils/GetOpts.h" using namespace snapper; using namespace std; -typedef void (*cmd_fnc)( const list& args ); -map cmds; -int print_number = 0; +typedef void (*cmd_fnc)(); +map cmds; + +GetOpts getopts; + +bool quiet = false; Snapper* sh = NULL; -void showHelp( const list& args ) + +Snapshots::iterator +readNum(const string& str) +{ + Snapshots& snapshots = sh->getSnapshots(); + + unsigned int num = 0; + if (str != "current") + str >> num; + + Snapshots::iterator snap = snapshots.find(num); + if (snap == snapshots.end()) + { + cerr << sformat(_("Snapshot '%u' not found"), num) << endl; + exit(EXIT_FAILURE); + } + + return snap; +} + + +void +showHelp() +{ + getopts.parse(GetOpts::no_options); + if (getopts.hasArgs()) { - cout << -"Usage: snapper [-h] [ command [ params ] ] ...\n" -"\n" -"Possible commands are:\n" -" help -- show this help\n" -" list -- list all snapshots\n" -" create single [desc] -- create single snapshot with \"descr\" as description\n" -" create pre [desc] -- create pre snapshot with \"descr\" as description\n" -" create post num -- create post snapshot that corresponds to\n" -" pre snapshot number \"num\"\n" -" diff num1 num2 -- show difference between snapnots num1 and num2.\n" -" current version of filesystem is indicated by number 0.\n" -"\n" -"It is possible to have multiple commands on one command line.\n"; + cerr << _("Command 'help' does not take arguments") << endl; + exit(EXIT_FAILURE); } + cout << + "Usage: snapper [--global-opts] [--command-opts] [command-arguments]\n" + "\n" + "Possible commands are:\n" + " help -- show this help\n" + " list -- list all snapshots\n" + " create single [desc] -- create single snapshot with \"descr\" as description\n" + " create pre [desc] -- create pre snapshot with \"descr\" as description\n" + " create post num -- create post snapshot that corresponds to\n" + " pre snapshot number \"num\"\n" + " diff num1 num2 -- show difference between snapnots num1 and num2.\n" + " current version of filesystem is indicated by number 0.\n" + "\n"; +} + + void -listSnap( const list& args ) +listSnap() { + getopts.parse(GetOpts::no_options); + if (getopts.hasArgs()) + { + cerr << _("Command 'list' does not take arguments") << endl; + exit(EXIT_FAILURE); + } + Table table; TableHeader header; @@ -71,138 +110,97 @@ listSnap( const list& args ) } -struct CompareCallbackImpl : public CompareCallback +void +createSnap() { - void start() { cout << "comparing snapshots..." << flush; } - void stop() { cout << " done" << endl; } -}; - -CompareCallbackImpl compare_callback_impl; - + const struct option options[] = { + { "type", required_argument, 0, 't' }, + { "pre-number", required_argument, 0, 0 }, + { "description", required_argument, 0, 'd' }, + { "print-number", no_argument, 0, 'p' }, + { 0, 0, 0, 0 } + }; -void createSnap( const list& args ) + GetOpts::parsed_opts opts = getopts.parse(options); + if (getopts.hasArgs()) { - unsigned int number1 = 0; - SnapshotType type; - string desc; - list::const_iterator s = args.begin(); - if( s!=args.end() ) - { - if (!toValue(*s++, type, true)) - { - cerr << "unknown type" << endl; - return; - } + cerr << _("Command 'create' does not take arguments") << endl; + exit(EXIT_FAILURE); } - if( s!=args.end() ) - { - if( type == POST ) - *s >> number1; - else - desc = *s; - ++s; - } - y2mil( "type:" << toString(type) << " desc:\"" << desc << "\" number1:" << number1 ); + SnapshotType type = SINGLE; + Snapshots::const_iterator snap1; + string description; + bool print_number = false; + + GetOpts::parsed_opts::const_iterator it; + + if ((it = opts.find("type")) != opts.end()) + toValue(it->second, type, SINGLE); + + if ((it = opts.find("pre-number")) != opts.end()) + snap1 = readNum(it->second); + + if ((it = opts.find("description")) != opts.end()) + description = it->second; + + if ((it = opts.find("print-number")) != opts.end()) + print_number = true; switch (type) { - case SINGLE: - { - Snapshots::const_iterator snap1 = sh->createSingleSnapshot(desc); + case SINGLE: { + Snapshots::const_iterator snap1 = sh->createSingleSnapshot(description); if (print_number) cout << snap1->getNum() << endl; } break; - case PRE: - { - Snapshots::const_iterator snap1 = sh->createPreSnapshot(desc); + case PRE: { + Snapshots::const_iterator snap1 = sh->createPreSnapshot(description); if (print_number) cout << snap1->getNum() << endl; } break; - case POST: - { - Snapshots::const_iterator snap1 = sh->getSnapshots().find(number1); + case POST: { Snapshots::const_iterator snap2 = sh->createPostSnapshot(snap1); if (print_number) cout << snap2->getNum() << endl; sh->startBackgroundComparsion(snap1, snap2); } break; } - } +} void -deleteSnap(const list& args) +deleteSnap() { - Snapshots& snapshots = sh->getSnapshots(); - - list::const_iterator s = args.begin(); - while (s != args.end()) + getopts.parse(GetOpts::no_options); + if (!getopts.hasArgs()) { - unsigned int number; - *s >> number; - s++; - - Snapshots::iterator snapshot = snapshots.find(number); - if (snapshot == snapshots.end()) - { - cerr << "snapshots not found" << endl; - exit(EXIT_FAILURE); - } + cerr << _("Command 'delete' needs at least one argument") << endl; + exit(EXIT_FAILURE); + } + while (getopts.hasArgs()) + { + Snapshots::iterator snapshot = readNum(getopts.popArg()); sh->deleteSnapshot(snapshot); } } void -readNums(const list& args, Snapshots::const_iterator& snap1, Snapshots::const_iterator& snap2) +showDifference() { - const Snapshots& snapshots = sh->getSnapshots(); - - list::const_iterator s = args.begin(); - if (s != args.end()) + getopts.parse(GetOpts::no_options); + if (getopts.numArgs() != 2) { - unsigned int num1 = 0; - if (*s != "current") - *s >> num1; - s++; - - snap1 = snapshots.find(num1); - if (snap1 == snapshots.end()) - { - cerr << "snapshots not found" << endl; - exit(EXIT_FAILURE); - } + cerr << _("Command 'diff' needs two arguments") << endl; + exit(EXIT_FAILURE); } - if (s != args.end()) - { - unsigned int num2 = 0; - if (*s != "current") - *s >> num2; - s++; - - snap2 = snapshots.find(num2); - if (snap2 == snapshots.end()) - { - cerr << "snapshots not found" << endl; - exit(EXIT_FAILURE); - } - } - - y2mil("num1:" << snap1->getNum() << " num2:" << snap2->getNum()); -} - - -void -showDifference( const list& args ) -{ - Snapshots::const_iterator snap1; - Snapshots::const_iterator snap2; - readNums(args, snap1, snap2); + Snapshots::const_iterator snap1 = readNum(getopts.popArg()); + Snapshots::const_iterator snap2 = readNum(getopts.popArg()); sh->setComparison(snap1, snap2); @@ -213,12 +211,17 @@ showDifference( const list& args ) void -doRollback( const list& args ) +doRollback() { - Snapshots::const_iterator snap1; - Snapshots::const_iterator snap2; + getopts.parse(GetOpts::no_options); + if (getopts.numArgs() != 2) + { + cerr << _("Command 'rollback' needs two arguments") << endl; + exit(EXIT_FAILURE); + } - readNums(args, snap1, snap2); + Snapshots::const_iterator snap1 = readNum(getopts.popArg()); + Snapshots::const_iterator snap2 = readNum(getopts.popArg()); sh->setComparison(snap1, snap2); @@ -230,64 +233,61 @@ doRollback( const list& args ) } +struct CompareCallbackImpl : public CompareCallback +{ + void start() { cout << "comparing snapshots..." << flush; } + void stop() { cout << " done" << endl; } +}; + +CompareCallbackImpl compare_callback_impl; + + int main(int argc, char** argv) - { +{ setlocale(LC_ALL, ""); initDefaultLogger(); - y2mil( "argc:" << argc ); - static struct option long_options[] = { - { "help", 0, 0, 'h' }, - { "print-number", 0, &print_number, 1 }, - { 0, 0, 0, 0 } - }; - int ch; - while( (ch=getopt_long( argc, argv, "h", long_options, NULL )) != -1 ) - { - switch( ch ) - { - case 'h': - { - list args; - showHelp(args); - exit(0); - } break; - default: - break; - } - } - - cmds["list"] = listSnap; cmds["help"] = showHelp; + cmds["list"] = listSnap; cmds["create"] = createSnap; cmds["delete"] = deleteSnap; cmds["diff"] = showDifference; cmds["rollback"] = doRollback; + const struct option options[] = { + { "quiet", no_argument, 0, 'q' }, + { 0, 0, 0, 0 } + }; + + getopts.init(argc, argv); + + GetOpts::parsed_opts opts = getopts.parse(options); + if (!getopts.hasArgs()) + { + cerr << _("No command provided") << endl; + exit(EXIT_FAILURE); + } + + const char* command = getopts.popArg(); + map::const_iterator cmd = cmds.find(command); + if (cmd == cmds.end()) + { + cerr << sformat(_("Unknown command '%s'"), command) << endl; + exit(EXIT_FAILURE); + } + + GetOpts::parsed_opts::const_iterator it; + if ((it = opts.find("quiet")) != opts.end()) + quiet = true; + sh = createSnapper(); - sh->setCompareCallback(&compare_callback_impl); - - int cnt = optind; - while( cnt::const_iterator c = cmds.find(argv[cnt]); - if( c != cmds.end() ) - { - list args; - while( ++cntsecond)(args); - } - else - { - y2war( "Unknown command: \"" << argv[cnt] << "\"" ); - cerr << "Unknown command: \"" << argv[cnt] << "\"" << endl; - ++cnt; - } - } + if (!quiet) + sh->setCompareCallback(&compare_callback_impl); + + (*cmd->second)(); deleteSnapper(sh); diff --git a/tools/utils/GetOpts.cc b/tools/utils/GetOpts.cc new file mode 100644 index 00000000..9bef660b --- /dev/null +++ b/tools/utils/GetOpts.cc @@ -0,0 +1,104 @@ + +#include +#include + +#include + +#include "GetOpts.h" + +using namespace std; +using namespace snapper; + + +// based on getopt.cc from zypper, thanks jkupec + + +const struct option GetOpts::no_options[1] = { + { 0, 0, 0, 0 } +}; + + +void +GetOpts::init(int new_argc, char** new_argv) +{ + argc = new_argc; + argv = new_argv; + + list tmp(&argv[1], &argv[argc]); + y2mil("args: " << boost::join(tmp, " ")); +} + + +GetOpts::parsed_opts +GetOpts::parse(const struct option* longopts) +{ + parsed_opts result; + opterr = 0; // we report errors on our own + + string optstring = make_optstring(longopts); + short2long_t short2long = make_short2long(longopts); + + while (true) + { + int option_index = 0; + int c = getopt_long(argc, argv, optstring.c_str(), longopts, &option_index); + + switch (c) + { + case -1: + return result; + + case '?': + cerr << sformat(_("Unknown option '%s'"), argv[optind - 1]) << endl; + exit(EXIT_FAILURE); + + case ':': + cerr << sformat(_("Missing argument for option '%s'"), argv[optind - 1]) << endl; + exit(EXIT_FAILURE); + + default: + const char* opt = c ? short2long[c] : longopts[option_index].name; + result[opt] = optarg ? optarg : ""; + break; + } + } +} + +string +GetOpts::make_optstring(const struct option* longopts) const +{ + // '+' - do not permute, stop at the 1st nonoption, which is the command or an argument + // ':' - return ':' to indicate missing arg, not '?' + string optstring = "+:"; + + for (; longopts && longopts->name; ++longopts) + { + if (!longopts->flag && longopts->val) + { + optstring += (char) longopts->val; + if (longopts->has_arg == required_argument) + optstring += ':'; + else if (longopts->has_arg == optional_argument) + optstring += "::"; + } + } + + return optstring; +} + + +GetOpts::short2long_t +GetOpts::make_short2long(const struct option* longopts) const +{ + short2long_t result; + + for (; longopts && longopts->name; ++longopts) + { + if (!longopts->flag && longopts->val) + { + result[longopts->val] = longopts->name; + } + } + + return result; +} diff --git a/tools/utils/GetOpts.h b/tools/utils/GetOpts.h new file mode 100644 index 00000000..a316ec3e --- /dev/null +++ b/tools/utils/GetOpts.h @@ -0,0 +1,38 @@ + +#include +#include +#include + + +class GetOpts +{ +public: + + static const struct option no_options[1]; + + typedef std::map parsed_opts; + + void init(int argc, char** argv); + + // longopts.flag must be NULL + parsed_opts parse(const struct option* longopts); + + bool hasArgs() const { return argc - optind > 0; } + + int numArgs() const { return argc - optind; } + + const char* popArg() { return argv[optind++]; } + +private: + + int argc; + char** argv; + + std::string make_optstring(const struct option* longopts) const; + + typedef std::map short2long_t; + + short2long_t make_short2long(const struct option* longopts) const; + +}; + diff --git a/tools/utils/Makefile.am b/tools/utils/Makefile.am index 7ed68707..96ab5075 100644 --- a/tools/utils/Makefile.am +++ b/tools/utils/Makefile.am @@ -9,5 +9,6 @@ noinst_LTLIBRARIES = libutils.la libutils_la_SOURCES = \ Table.cc Table.h \ text.cc text.h \ - console.cc console.h + console.cc console.h \ + GetOpts.cc GetOpts.h