Cmdline::Cmdline (int argc, char ** argv, Command * command) : synopsis (command->getSynopsis ()), helpText (), invalidOpt (false), /*XXX: Step 2: initialise your option here.*/ debug (), force (), load (), humanReadable (), help (), interactive (), minDepth (0), maxDepth (numeric_limits<int>::max ()), noNewline (), test (), recursive (), resolver (KDB_RESOLVER), strategy ("preserve"), verbose (), quiet (), version (), withoutElektra (), null (), first (true), second (true), third (true), withRecommends (false), all (), format (KDB_STORAGE), plugins ("sync"), globalPlugins ("spec"), pluginsConfig (""), color ("auto"), ns (""), editor (), bookmarks (), profile ("current"), executable (), commandName () { extern int optind; extern char * optarg; int opt; size_t optionPos; helpText += command->getShortHelpText (); helpText += "\n"; helpText += command->getLongHelpText (); helpText += "\n"; string allOptions = command->getShortOptions (); allOptions += "HVCp"; // Make sure to use the unsorted allOptions for getopt to preserve argument chars : and :: std::set<string::value_type> unique_sorted_chars (allOptions.begin (), allOptions.end ()); string acceptedOptions (unique_sorted_chars.begin (), unique_sorted_chars.end ()); vector<option> long_options; /*XXX: Step 3: give it a long name.*/ if (acceptedOptions.find ('a') != string::npos) { option o = { "all", no_argument, nullptr, 'a' }; long_options.push_back (o); helpText += "-a --all Consider all of the keys.\n"; } if (acceptedOptions.find ('d') != string::npos) { option o = { "debug", no_argument, nullptr, 'd' }; long_options.push_back (o); helpText += "-d --debug Give debug information or ask debug questions (in interactive mode).\n"; } if (acceptedOptions.find ('f') != string::npos) { option o = { "force", no_argument, nullptr, 'f' }; long_options.push_back (o); helpText += "-f --force Force the action to be done.\n"; } if (acceptedOptions.find ('l') != string::npos) { option o = { "load", no_argument, nullptr, 'f' }; long_options.push_back (o); helpText += "-l --load Load plugin even if system/elektra is available.\n"; } if (acceptedOptions.find ('h') != string::npos) { option o = { "human-readable", no_argument, nullptr, 'h' }; long_options.push_back (o); helpText += "-h --human-readable Print numbers in an human readable way.\n"; } if (acceptedOptions.find ('H') != string::npos) { option o = { "help", no_argument, nullptr, 'H' }; long_options.push_back (o); helpText += "-H --help Show the man page.\n"; } if (acceptedOptions.find ('i') != string::npos) { option o = { "interactive", no_argument, nullptr, 'i' }; long_options.push_back (o); helpText += "-i --interactive Ask the user interactively.\n"; } optionPos = acceptedOptions.find ('m'); if (optionPos != string::npos) { acceptedOptions.insert (optionPos + 1, ":"); option o = { "min-depth", required_argument, nullptr, 'm' }; long_options.push_back (o); helpText += "-m --min-depth <min> Specify the minimum depth (default 0).\n"; } optionPos = acceptedOptions.find ('M'); if (optionPos != string::npos) { acceptedOptions.insert (optionPos + 1, ":"); option o = { "max-depth", required_argument, nullptr, 'M' }; long_options.push_back (o); helpText += "-M --max-depth <max> Specify the maximum depth (unlimited by default).\n"; } if (acceptedOptions.find ('n') != string::npos) { option o = { "no-newline", no_argument, nullptr, 'n' }; long_options.push_back (o); helpText += "-n --no-newline Suppress the newline at the end of the output.\n"; } if (acceptedOptions.find ('t') != string::npos) { option o = { "test", no_argument, nullptr, 't' }; long_options.push_back (o); helpText += "-t --test Test.\n"; } if (acceptedOptions.find ('r') != string::npos) { option o = { "recursive", no_argument, nullptr, 'r' }; long_options.push_back (o); helpText += "-r --recursive Work in a recursive mode.\n"; } optionPos = acceptedOptions.find ('R'); if (optionPos != string::npos) { acceptedOptions.insert (optionPos + 1, ":"); option o = { "resolver", required_argument, nullptr, 'R' }; long_options.push_back (o); helpText += "-R --resolver <name> Specify the resolver plugin to use.\n"; } optionPos = acceptedOptions.find ('p'); if (optionPos != string::npos) { acceptedOptions.insert (optionPos + 1, ":"); option o = { "profile", required_argument, nullptr, 'p' }; long_options.push_back (o); helpText += "-p --profile <name> Use a different profile for kdb configuration.\n"; } optionPos = acceptedOptions.find ('s'); if (optionPos != string::npos) { acceptedOptions.insert (optionPos + 1, ":"); option o = { "strategy", required_argument, nullptr, 's' }; long_options.push_back (o); helpText += "-s --strategy <name> Specify the strategy to resolve conflicts.\n"; } if (acceptedOptions.find ('v') != string::npos) { option o = { "verbose", no_argument, nullptr, 'v' }; long_options.push_back (o); helpText += "-v --verbose Explain what is happening.\n"; } if (acceptedOptions.find ('q') != string::npos) { option o = { "quiet", no_argument, nullptr, 'q' }; long_options.push_back (o); helpText += "-q --quiet Only print error messages.\n"; } if (acceptedOptions.find ('V') != string::npos) { option o = { "version", no_argument, nullptr, 'V' }; long_options.push_back (o); helpText += "-V --version Print version info.\n"; } if (acceptedOptions.find ('E') != string::npos) { option o = { "without-elektra", no_argument, nullptr, 'E' }; long_options.push_back (o); helpText += "-E --without-elektra Omit the `/elektra` directory.\n"; } optionPos = acceptedOptions.find ('e'); if (optionPos != string::npos) { acceptedOptions.insert (optionPos + 1, ":"); option o = { "editor", required_argument, 0, 'e' }; long_options.push_back (o); helpText += "-e --editor <editor> Which external editor to use.\n"; } if (acceptedOptions.find ('W') != string::npos) { option o = { "with-recommends", no_argument, nullptr, 'W' }; long_options.push_back (o); helpText += "-W --with-recommends Add recommended plugins.\n"; } if (acceptedOptions.find ('0') != string::npos) { option o = { "null", no_argument, nullptr, '0' }; long_options.push_back (o); helpText += "-0 --null Use binary 0 termination.\n"; } if (acceptedOptions.find ('1') != string::npos) { option o = { "first", no_argument, nullptr, '1' }; long_options.push_back (o); helpText += "-1 --first Suppress the first column.\n"; } if (acceptedOptions.find ('2') != string::npos) { option o = { "second", no_argument, nullptr, '2' }; long_options.push_back (o); helpText += "-2 --second Suppress the second column.\n"; } if (acceptedOptions.find ('3') != string::npos) { option o = { "third", no_argument, nullptr, '3' }; long_options.push_back (o); helpText += "-3 --third Suppress the third column.\n"; } optionPos = acceptedOptions.find ('N'); if (acceptedOptions.find ('N') != string::npos) { acceptedOptions.insert (optionPos + 1, ":"); option o = { "namespace", required_argument, nullptr, 'N' }; long_options.push_back (o); helpText += "-N --namespace <ns> Specify the namespace to use for cascading keys.\n"; } optionPos = acceptedOptions.find ('c'); if (optionPos != string::npos) { acceptedOptions.insert (optionPos + 1, ":"); option o = { "plugins-config", required_argument, nullptr, 'c' }; long_options.push_back (o); helpText += "-c --plugins-config <c> Add a plugin configuration.\n"; } optionPos = acceptedOptions.find ('C'); if (optionPos != string::npos) { acceptedOptions.insert (optionPos + 1, ":"); option o = { "color", required_argument, nullptr, 'C' }; long_options.push_back (o); helpText += "-C --color <when> Print never/auto(default)/always colored output.\n"; } int index = 0; option o = { nullptr, 0, nullptr, 0 }; long_options.push_back (o); executable = argv[0]; commandName = argv[1]; opterr = 0; while ((opt = getopt_long (argc, argv, acceptedOptions.c_str (), &long_options[0], &index)) != EOF) { switch (opt) { case 'p': profile = optarg; break; default: // ignore everything else for now break; } } if (profile != "%") { try { using namespace kdb; /*XXX: Step 4: use default from KDB, if available.*/ KDB kdb; KeySet conf; for (int i = 0; i <= 2; ++i) { std::string dirname; switch (i) { // prefer later dirnames (will override) case 0: dirname = "/sw/kdb/" + profile + "/"; break; // legacy case 1: dirname = "/sw/elektra/kdb/#0/%/"; break; // no profile case 2: dirname = "/sw/elektra/kdb/#0/" + profile + "/"; break; // current profile } kdb.get (conf, dirname); Key k = conf.lookup (dirname + "resolver"); if (k) resolver = k.get<string> (); k = conf.lookup (dirname + "format"); if (k) format = k.get<string> (); k = conf.lookup (dirname + "plugins"); if (k) plugins = k.get<string> (); k = conf.lookup (dirname + "plugins/global"); if (k) globalPlugins = k.get<string> (); k = conf.lookup (dirname + "namespace"); if (k) ns = k.get<string> (); k = conf.lookup (dirname + "verbose"); if (k) verbose = k.get<bool> (); k = conf.lookup (dirname + "quiet"); if (k) quiet = k.get<bool> (); k = conf.lookup (dirname + "editor"); if (k) editor = k.get<string> (); k = conf.lookup (dirname + "recommends"); if (k) withRecommends = k.get<bool> (); map nks = conf.get<map> (dirname + "bookmarks"); bookmarks.insert (nks.begin (), nks.end ()); k = conf.lookup (dirname + "color"); if (k) color = k.get<std::string> (); } } catch (kdb::KDBException const & ce) { std::cerr << "Sorry, I could not fetch my own configuration:\n" << ce.what () << std::endl; } } // reinit index = 0; optind = 1; if (!dynamic_cast<ExternalCommand *> (command)) { // do not print to stderr for external commands, // we do not know which options they have and // otherwise maybe wrong "invalid/unrecognized option" // are reported to stderr. opterr = 1; } while ((opt = getopt_long (argc, argv, acceptedOptions.c_str (), &long_options[0], &index)) != EOF) { switch (opt) { /*XXX: Step 5: and now process the option.*/ case 'a': all = true; break; case 'C': color = optarg; if (color != "never" && color != "auto" && color != "always") { std::cerr << argv[0] << ": -C --color needs never, auto, or always as argument\n"; invalidOpt = true; } break; case 'd': debug = true; break; case 'e': editor = optarg; break; case 'f': force = true; break; case 'h': humanReadable = true; break; case 'l': load = true; break; case 'H': help = true; break; case 'i': interactive = true; break; case 'm': try { minDepth = stoi (optarg); } catch (std::invalid_argument const & ia) { std::cerr << argv[0] << ": -m --min-depth needs a valid number as argument\n"; invalidOpt = true; } break; case 'M': try { maxDepth = stoi (optarg); } catch (std::invalid_argument const & ia) { std::cerr << argv[0] << ": -M --max-depth needs a valid number as argument\n"; invalidOpt = true; } if (maxDepth == -1) { maxDepth = numeric_limits<int>::max (); } break; case 'n': noNewline = true; break; case 't': test = true; break; case 'r': recursive = true; break; case 'p': break; // already handled above case 'R': resolver = optarg; break; case 's': strategy = optarg; break; case 'v': verbose = true; break; case 'q': quiet = true; break; case 'V': version = true; break; case 'E': withoutElektra = true; break; case 'W': withRecommends = true; break; case '0': null = true; break; case '1': first = false; break; case '2': second = false; break; case '3': third = false; break; case 'N': ns = optarg; break; case 'c': pluginsConfig = optarg; break; default: invalidOpt = true; break; } } if (quiet && verbose) { std::cout << "Both quiet and verbose is active: will suppress default messages, but print verbose messages" << std::endl; } if (ns.empty ()) { #ifndef _WIN32 if (getuid () == 0 || geteuid () == 0) { ns = "system"; } else { ns = "user"; } #else ns = "user"; #endif } optind++; // skip the command name while (optind < argc) { arguments.push_back (argv[optind++]); } // init colors hasStdColor (color); hasErrorColor (color); }
int MergeCommand::execute (Cmdline const & cl) { if (cl.arguments.size () < 4) { throw invalid_argument ("wrong number of arguments, 4 needed"); } Key oursRoot = cl.createKey (0); Key theirsRoot = cl.createKey (1); Key baseRoot = cl.createKey (2); Key resultRoot = cl.createKey (3); KeySet ours; KeySet theirs; KeySet base; { KDB lkdb; lkdb.get (ours, oursRoot); ours = ours.cut (oursRoot); ours.lookup (oursRoot, KDB_O_POP); if (cl.verbose) std::cout << "we got ours: " << oursRoot << " with keys " << ours << std::endl; } { KDB lkdb; lkdb.get (theirs, theirsRoot); theirs = theirs.cut (theirsRoot); ours.lookup (oursRoot, KDB_O_POP); if (cl.verbose) std::cout << "we got theirs: " << theirsRoot << " with keys " << theirs << std::endl; } { KDB lkdb; lkdb.get (base, baseRoot); base = base.cut (baseRoot); ours.lookup (oursRoot, KDB_O_POP); if (cl.verbose) std::cout << "we got base: " << baseRoot << " with keys " << base << std::endl; } KeySet resultKeys; kdb.get (resultKeys, resultRoot); KeySet discard = resultKeys.cut (resultRoot); if (discard.size () != 0) { if (cl.force) { if (cl.verbose) { std::cout << "will remove " << discard.size () << " keys, because -f was given" << std::endl; } } else { std::cerr << discard.size () << " keys exist in merge resultroot, will quit. Use -f to override the keys there." << std::endl; } } MergeHelper helper; ThreeWayMerge merger; helper.configureMerger (cl, merger); MergeResult result = merger.mergeKeySet ( MergeTask (BaseMergeKeys (base, baseRoot), OurMergeKeys (ours, oursRoot), TheirMergeKeys (theirs, theirsRoot), resultRoot)); helper.reportResult (cl, result, cout, cerr); int ret = 0; if (!result.hasConflicts ()) { resultKeys.append (result.getMergedKeys ()); kdb.set (resultKeys, resultRoot); } else { ret = -1; } return ret; }
int EditorCommand::execute (Cmdline const & cl) { #ifdef _WIN32 throw EditorNotAvailable (); #endif int argc = cl.arguments.size (); if (argc < 1) { throw invalid_argument ("wrong number of arguments, 1 needed"); } Key root = cl.createKey (0); KeySet ours; KDB kdb; kdb.get (ours, root); KeySet oursToEdit = ours.cut (root); KeySet original = oursToEdit.dup (); if (cl.strategy == "validate") { prependNamespace (oursToEdit, cl.ns); oursToEdit.cut (prependNamespace (root, cl.ns)); } // export it to file string format = cl.format; if (argc > 1) format = cl.arguments[1]; Modules modules; PluginPtr plugin = modules.load (format); tmpFile (); if (cl.verbose) std::cout << "filename set to " << filename << std::endl; Key errorKey (root); errorKey.setString (filename); if (plugin->set (oursToEdit, errorKey) == -1) { printWarnings (cerr, errorKey); printError (cerr, errorKey); return 11; } printWarnings (cerr, errorKey); // start editor if (cl.verbose) std::cout << "running editor with " << filename << std::endl; if (!cl.editor.empty ()) { if (!runEditor (cl.editor, filename)) { std::cerr << "Could not run editor " << cl.editor << std::endl; return 12; } } else { if (!runAllEditors (filename)) { std::cerr << "Could not run any editor, please change /sw/elektra/kdb/#0/current/editor" << std::endl; return 12; } } // import from the file KeySet importedKeys; plugin->get (importedKeys, errorKey); importedKeys = importedKeys.cut (root); printWarnings (cerr, errorKey); printError (cerr, errorKey); if (cl.strategy == "validate") { applyMeta (importedKeys, original); kdb.set (importedKeys, root); printWarnings (cerr, root); return 0; } ThreeWayMerge merger; MergeHelper helper; helper.configureMerger (cl, merger); MergeResult result = merger.mergeKeySet ( MergeTask (BaseMergeKeys (oursToEdit, root), OurMergeKeys (oursToEdit, root), TheirMergeKeys (importedKeys, root), root)); helper.reportResult (cl, result, cout, cerr); int ret = 13; if (!result.hasConflicts ()) { if (cl.verbose) { cout << "The merged keyset with strategy " << cl.strategy << " is:" << endl; cout << result.getMergedKeys (); } KeySet resultKeys = result.getMergedKeys (); if (cl.verbose) std::cout << "about to write result keys " << resultKeys << std::endl; ours.append (resultKeys); try { kdb.set (ours, root); if (cl.verbose) std::cout << "successful, cleaning up " << filename << std::endl; unlink (filename.c_str ()); ret = 0; } catch (KDBException const & e) { std::cout << "Import of configuration failed with the error:\n"; std::cout << e.what (); std::cout << "\n\n"; std::cout << "Your changes are not lost." << std::endl; std::cout << "Please fix, import and remove \"" << filename << '"' << std::endl; ret = 14; } } else { std::cout << "Import not successful, please import and remove \"" << filename << '"' << std::endl; } return ret; }