diff --git a/src/tools/kdb/cp.c b/src/tools/kdb/cp.c new file mode 100644 index 00000000000..3138edd5e33 --- /dev/null +++ b/src/tools/kdb/cp.c @@ -0,0 +1,148 @@ +/** + * @file + * + * @brief Implementation of kdb cp command + * + * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) + */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#define COMMAND_NAME "cp" + +#define GET_OPTION_KEY(options, name) GET_OPT_KEY (options, COMMAND_BASE_KEY (COMMAND_NAME) "/" name) +#define GET_OPTION(options, name) GET_OPT (options, COMMAND_BASE_KEY (COMMAND_NAME) "/" name) + +void addCpSpec (KeySet * spec) +{ + ksAppendKey (spec, keyNew (COMMAND_SPEC_KEY (COMMAND_NAME), KEY_META, "description", + "Copy a configuration within the key database.", KEY_META, "command", COMMAND_NAME, KEY_END)); + ksAppendKey (spec, keyNew (COMMAND_SPEC_KEY (COMMAND_NAME) "/recursive", KEY_META, "description", "Work in recursive mode.", + KEY_META, "opt", "r", KEY_META, "opt/long", "recursive", KEY_META, "opt/arg", "none", KEY_END)); + ksAppendKey (spec, keyNew (COMMAND_SPEC_KEY (COMMAND_NAME) "/source", KEY_META, "description", "The source key", KEY_META, "args", + "indexed", KEY_META, "args/index", "0", KEY_END)); + ksAppendKey (spec, keyNew (COMMAND_SPEC_KEY (COMMAND_NAME) "/destination", KEY_META, "description", "The destination key", KEY_META, + "args", "indexed", KEY_META, "args/index", "1", KEY_END)); + + ADD_BASIC_OPTIONS (spec, COMMAND_SPEC_KEY (COMMAND_NAME)) +} + +int execCp (KeySet * options, Key * errorKey) +{ + int ret = 0; + GET_BASIC_OPTIONS + + bool recursive = false; + tmp = GET_OPTION_KEY (options, "recursive"); + if (tmp != NULL) + { + elektraKeyToBoolean (GET_OPTION_KEY (options, "recursive"), &recursive); + } + + const char * sourceName = getKeyNameFromOptions (options, GET_OPTION (options, "source"), errorKey, verbose); + if (sourceName == NULL) return 1; + + const char * destName = getKeyNameFromOptions (options, GET_OPTION (options, "destination"), errorKey, verbose); + if (destName == NULL) + { + elektraFree ((void *) sourceName); + return 1; + } + + Key * sourceKey = keyNew (sourceName, KEY_END); + Key * destKey = keyNew (destName, KEY_END); + + if (keyGetNamespace (sourceKey) == KEY_NS_NONE || keyGetNamespace (sourceKey) == KEY_NS_CASCADING) { + ret = 1; + ELEKTRA_SET_VALIDATION_SYNTACTIC_ERROR (errorKey, "source key does not specify a namespace"); + elektraFree ((void*) sourceName); + elektraFree ((void*) destName); + keyDel (sourceKey); + keyDel (destKey); + return ret; + } + if (keyGetNamespace (destKey) == KEY_NS_NONE || keyGetNamespace (destKey) == KEY_NS_CASCADING) { + ret = 1; + ELEKTRA_SET_VALIDATION_SYNTACTIC_ERROR (errorKey, "destination key does not specify a namespace"); + elektraFree ((void*) sourceName); + elektraFree ((void*) destName); + keyDel (sourceKey); + keyDel (destKey); + return ret; + } + + Key * root = keyNew ("/", KEY_END); + + KeySet * conf = ksNew (0, KS_END); + KDB * handle = kdbOpen (NULL, errorKey); + if (kdbGet (handle, conf, root) == -1) + { + ELEKTRA_SET_VALIDATION_SEMANTIC_ERRORF (errorKey, "could not load '%s': %s", sourceName, GET_ERR (root)); + ret = 1; + goto cleanup; + } + + Key * cur = NULL; + + + size_t sourceNameLen = elektraStrLen (keyName (sourceKey)); + size_t destNameLen = elektraStrLen (keyName (destKey)); + + long count = 0; + KeySet * newConf = ksNew (ksGetSize (conf), KS_END); + for (elektraCursor it = 0; it < ksGetSize (conf); ++it) + { + cur = ksAtCursor (conf, it); + + bool startsWithSrc = !elektraStrNCmp (keyName (cur), keyName (sourceKey), sourceNameLen - 1); + bool equalsSrc = !elektraStrCmp (keyName (cur), keyName (sourceKey)); + + // starts-with if recursive, or equals if !recursive + if ((recursive && startsWithSrc) || equalsSrc) + { + size_t newNameLen = destNameLen + (equalsSrc ? 0 : 1 + elektraStrLen (keyName (cur)) - sourceNameLen); + char * newName = elektraMalloc (newNameLen); + strcpy (newName, keyName (destKey)); + if (!equalsSrc) + { + strcat (newName, "/"); + strcat (newName, &keyName (cur)[sourceNameLen]); + } + CLI_PRINT (CLI_LOG_VERBOSE, "-> moving '%s' to '%s'\n", BOLD(keyName (cur)), BOLD(newName)); + Key * tmpKey = keyDup (cur, KEY_CP_ALL); + keySetName (tmpKey, newName); + ksAppendKey (newConf, tmpKey); + elektraFree (newName); + count++; + } + ksAppendKey (newConf, cur); + } + + if (kdbSet (handle, newConf, root) == -1) + { + ret = 1; + ELEKTRA_SET_VALIDATION_SEMANTIC_ERRORF (errorKey, "could not save keyset after moving: %s", GET_ERR (root)); + } + CLI_PRINT (CLI_LOG_VERBOSE, "\nmoved %ld keys", count); + + +cleanup: + if (!noNewLine) + { + printf ("\n"); + } + elektraFree ((void *) sourceName); + keyDel (root); + elektraFree ((void *) fmtBuffer); + ksDel (conf); + kdbClose (handle, errorKey); + return ret; +} diff --git a/src/tools/kdb/cp.cpp b/src/tools/kdb/cp.cpp deleted file mode 100644 index 723f6e07a46..00000000000 --- a/src/tools/kdb/cp.cpp +++ /dev/null @@ -1,116 +0,0 @@ -/** - * @file - * - * @brief - * - * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) - */ - -#include - -#include -#include -#include -#include - -#include - -using namespace std; -using namespace kdb; - -CpCommand::CpCommand () -{ -} - -namespace -{ -void copySingleKey (Cmdline const & cl, Key const & rk, KeySet & tmpConf, KeySet & newConf) -{ - if (cl.force) - { - tmpConf.lookup (rk, KDB_O_POP); - } - else - { - Key key = tmpConf.lookup (rk); - if (key != nullptr) - { - if ((key.isString () && key.getString () != rk.getString ()) || - (key.isBinary () && key.getBinary () != rk.getBinary ())) - { - throw CommandAbortException (std::string ("Copy will not be done, because " + rk.getName () + - " already exists and has a different value" - ", use -f to force copy"), - 11); - } - } - } - newConf.append (rk); -} -} // namespace - -int CpCommand::execute (Cmdline const & cl) -{ - if (cl.arguments.size () != 2) - { - throw invalid_argument ("wrong number of arguments, 2 needed"); - } - - KeySet conf; - Key sourceKey = cl.createKey (0, false); - - Key destKey = cl.createKey (1, false); - - string newDirName = destKey.getName (); - - kdb.get (conf, sourceKey); - kdb.get (conf, destKey); - KeySet tmpConf = conf; - KeySet oldConf; - - std::string sourceName = sourceKey.getName (); - oldConf.append (tmpConf.cut (sourceKey)); - - if (!oldConf.size ()) - { - std::cerr << "No key to copy found below '" << sourceName << "'" << std::endl; - return 11; - } - - KeySet newConf; - - if (cl.verbose) cout << "common name: " << sourceName << endl; - if (cl.recursive) - { - // copy all keys with new name - for (Key k : oldConf) - { - Key rk = rename_key (k, sourceName, newDirName, cl.verbose); - copySingleKey (cl, rk, tmpConf, newConf); - } - } - else - { - // just copy one key - Key k = oldConf.at (0); - if (k != sourceKey) - { - cerr << "First key found " << k.getName () << " does not exactly match given key " << sourceKey.getName () - << ", aborting (use -r to move hierarchy)\n"; - return 11; - } - Key rk = rename_key (k, sourceName, newDirName, cl.verbose); - copySingleKey (cl, rk, tmpConf, newConf); - } - - newConf.append (tmpConf); // these are unrelated keys - newConf.append (oldConf); // these are the original keys - - kdb.set (newConf, destKey); - - return 0; -} - -CpCommand::~CpCommand () -{ -} diff --git a/src/tools/kdb/cp.h b/src/tools/kdb/cp.h new file mode 100644 index 00000000000..90fb23bb943 --- /dev/null +++ b/src/tools/kdb/cp.h @@ -0,0 +1,36 @@ +/** +* @file +* +* @brief Header for cp command +* +* @copyright BSD License (see LICENSE.md or https://www.libelektra.org) +*/ + +#ifndef ELEKTRA_KDB_CP_H +#define ELEKTRA_KDB_CP_H + +#include + +/** +* Adds options specification of cp command to @spec +* +* @param spec the base spec where the commands spec should be added +*/ +void addCpSpec (KeySet * spec); + +/** +* Executes the cp command +* +* @param options cli options and arguments as specified in addCpSpec() +* @param errorKey key where errors and warnings should be saved +* +* @retval 0 cp command ran without errors +* @retval 1 errors occurred, keyGetMeta (errorKey, "error/reason") for info +* +*/ +int execCp (KeySet * options, Key * errorKey); + +// helper functions +int getKeyNameDepth (const char * name); + +#endif // ELEKTRA_KDB_CP_H diff --git a/src/tools/kdb/cp.hpp b/src/tools/kdb/cp.hpp deleted file mode 100644 index f34ffd3f6e7..00000000000 --- a/src/tools/kdb/cp.hpp +++ /dev/null @@ -1,47 +0,0 @@ -/** - * @file - * - * @brief - * - * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) - */ - -#ifndef CP_HPP -#define CP_HPP - -#include "coloredkdbio.hpp" -#include -#include - -class CpCommand : public Command -{ - kdb::KDB kdb; - -public: - CpCommand (); - ~CpCommand (); - - virtual std::string getShortOptions () override - { - return "rf"; - } - - virtual std::string getSynopsis () override - { - return " "; - } - - virtual std::string getShortHelpText () override - { - return "Copy keys within the key database."; - } - - virtual std::string getLongHelpText () override - { - return ""; - } - - virtual int execute (Cmdline const & cmdline) override; -}; - -#endif diff --git a/src/tools/kdb/factory.hpp b/src/tools/kdb/factory.hpp index 31d2b107285..8d899aeacf8 100644 --- a/src/tools/kdb/factory.hpp +++ b/src/tools/kdb/factory.hpp @@ -26,7 +26,6 @@ #include #include #include -#include #include #include #include @@ -78,7 +77,6 @@ class Factory // TODO: to add a new command, 2.) add a line here -> and you are done m_factory.insert (std::make_pair ("cache", std::make_shared> ())); m_factory.insert (std::make_pair ("complete", std::make_shared> ())); - m_factory.insert (std::make_pair ("cp", std::make_shared> ())); m_factory.insert (std::make_pair ("mount", std::make_shared> ())); m_factory.insert (std::make_pair ("remount", std::make_shared> ())); m_factory.insert (std::make_pair ("shell", std::make_shared> ())); diff --git a/src/tools/kdb/main.c b/src/tools/kdb/main.c index 30e0109fa5f..493fc30e589 100644 --- a/src/tools/kdb/main.c +++ b/src/tools/kdb/main.c @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -33,6 +34,7 @@ extern char ** environ; command subcommands[] = { { "basename", addBasenameSpec, execBasename }, { "cmerge", addCmergeSpec, execCmerge }, + { "cp", addCpSpec, execCp }, { "dirname", addDirnameSpec, execDirname }, { "get", addGetSpec, execGet }, { "ls", addLsSpec, execLs },