diff --git a/doc/news/_preparation_next_release.md b/doc/news/_preparation_next_release.md index 5aec9924111..69b135081c5 100644 --- a/doc/news/_preparation_next_release.md +++ b/doc/news/_preparation_next_release.md @@ -195,11 +195,9 @@ This section keeps you up-to-date with the multi-language support provided by El ## Tools -### <> +### KDB -- <> -- <> -- <> +- rewrite C++ cli to C _(@hannes99)_ ### <> diff --git a/src/tools/kdb/CMakeLists.txt b/src/tools/kdb/CMakeLists.txt index b2530cffcb4..fdf60159d43 100644 --- a/src/tools/kdb/CMakeLists.txt +++ b/src/tools/kdb/CMakeLists.txt @@ -1,12 +1,12 @@ include (LibAddMacros) -file (GLOB HDR_FILES *.hpp gen/*.hpp gen/*/*.hpp) +file (GLOB HDR_FILES *.h gen/*.hpp gen/*/*.hpp) add_headers (HDR_FILES) add_cppheaders (HDR_FILES) add_toolheaders (HDR_FILES) include_directories (${CMAKE_CURRENT_SOURCE_DIR}) -file (GLOB SRC_FILES *.cpp gen/*.cpp gen/*/*.cpp) +file (GLOB SRC_FILES *.c gen/*.cpp gen/*/*.cpp) list (REMOVE_ITEM SRC_FILES "${CMAKE_CURRENT_SOURCE_DIR}/gen/templates/collect.cpp") set (SOURCES ${SRC_FILES} ${HDR_FILES}) @@ -28,6 +28,7 @@ if (BUILD_SHARED) elektra-core elektra-kdb elektratools + elektra-invoke elektra-opts elektra-merge) diff --git a/src/tools/kdb/basename.c b/src/tools/kdb/basename.c new file mode 100644 index 00000000000..a2676fb984a --- /dev/null +++ b/src/tools/kdb/basename.c @@ -0,0 +1,53 @@ +/** + * @file + * + * @brief Implementation of kdb basename command + * + * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) + */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#define COMMAND_NAME "basename" + +#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 addBasenameSpec (KeySet * spec) +{ + ksAppendKey (spec, keyNew (COMMAND_SPEC_KEY (COMMAND_NAME), KEY_META, "description", "Get the basename of a key.", KEY_META, + "command", COMMAND_NAME, KEY_END)); + ksAppendKey (spec, keyNew (COMMAND_SPEC_KEY (COMMAND_NAME) "/name", KEY_META, "description", "The name of the key", KEY_META, + "args", "indexed", KEY_META, "args/index", "0", KEY_END)); + + ADD_BASIC_OPTIONS (spec, COMMAND_SPEC_KEY (COMMAND_NAME)) +} + +int execBasename (KeySet * options, Key * errorKey) +{ + GET_BASIC_OPTIONS + + const char * name = getKeyNameFromOptions (options, GET_OPTION (options, "name"), errorKey, verbose); + if (name == NULL) return 1; + + Key * key = keyNew (name, KEY_END); + if (key == NULL) + { + ELEKTRA_SET_VALIDATION_SEMANTIC_ERRORF (errorKey, "'%s' is not a valid key name.", name); + elektraFree ((void *) name); + return 1; + } + CLI_PRINT (CLI_LOG_NONE, "%s", BOLD (keyBaseName (key))); + + elektraFree ((void *) name); + keyDel (key); + return 0; +} diff --git a/src/tools/kdb/basename.cpp b/src/tools/kdb/basename.cpp deleted file mode 100644 index d66a6015ce9..00000000000 --- a/src/tools/kdb/basename.cpp +++ /dev/null @@ -1,27 +0,0 @@ -/** - * @file - * - * @brief - * - * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) - */ - -#include -#include -#include - - -using namespace std; - -BasenameCommand::BasenameCommand () -{ -} - -int BasenameCommand::execute (Cmdline const & cl) -{ - return executeNamepartcommand (cl, keyBasename); -} - -BasenameCommand::~BasenameCommand () -{ -} diff --git a/src/tools/kdb/basename.h b/src/tools/kdb/basename.h new file mode 100644 index 00000000000..3a2262a540e --- /dev/null +++ b/src/tools/kdb/basename.h @@ -0,0 +1,33 @@ +/** + * @file + * + * @brief Header for basename command + * + * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) + */ + +#ifndef ELEKTRA_KDB_BASENAME_H +#define ELEKTRA_KDB_BASENAME_H + +#include + +/** + * Adds options specification of basename command to @spec + * + * @param spec the base spec where the commands spec should be added + */ +void addBasenameSpec (KeySet * spec); + +/** + * Executes the basename command + * + * @param options cli options and arguments as specified in @addBasenameSpec() + * @param errorKey key where errors and warnings should be saved + * + * @retval 0 ls command ran without errors + * @retval 1 errors occurred, keyGetMeta (errorKey, "error/reason") for info + * + */ +int execBasename (KeySet * options, Key * errorKey); + +#endif // ELEKTRA_KDB_BASENAME_H diff --git a/src/tools/kdb/basename.hpp b/src/tools/kdb/basename.hpp deleted file mode 100644 index db008303330..00000000000 --- a/src/tools/kdb/basename.hpp +++ /dev/null @@ -1,47 +0,0 @@ -/** - * @file - * - * @brief - * - * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) - */ - -#ifndef BASENAME_HPP -#define BASENAME_HPP - -#include "coloredkdbio.hpp" -#include -#include - -class BasenameCommand : public Command -{ - kdb::KDB kdb; - -public: - BasenameCommand (); - ~BasenameCommand (); - - virtual std::string getShortOptions () override - { - return "n"; - } - - virtual std::string getSynopsis () override - { - return ""; - } - - virtual std::string getShortHelpText () override - { - return "Get the basename of a key."; - } - - virtual std::string getLongHelpText () override - { - return "For example, \"kdb basename user:/key/subkey\" will yield \"subkey\".\n"; - } - - virtual int execute (Cmdline const & cmdline) override; -}; - -#endif diff --git a/src/tools/kdb/cmerge.c b/src/tools/kdb/cmerge.c new file mode 100644 index 00000000000..1a32d32a616 --- /dev/null +++ b/src/tools/kdb/cmerge.c @@ -0,0 +1,233 @@ +/** + * @file + * + * @brief Implementation of kdb cmerge command + * + * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define COMMAND_NAME "cmerge" + +#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 addCmergeSpec (KeySet * spec) +{ + ksAppendKey (spec, keyNew (COMMAND_SPEC_KEY (COMMAND_NAME), KEY_META, "description", "Three-way merge of Key sets.", KEY_META, + "command", COMMAND_NAME, KEY_END)); + ksAppendKey (spec, + keyNew (COMMAND_SPEC_KEY (COMMAND_NAME) "/ourpath", KEY_META, "description", "Path to the keyset to serve as our.", + KEY_META, "args", "indexed", KEY_META, "args/index", "0", KEY_END)); + ksAppendKey (spec, + keyNew (COMMAND_SPEC_KEY (COMMAND_NAME) "/theirpath", KEY_META, "description", "Path to the keyset to serve as their.", + KEY_META, "args", "indexed", KEY_META, "args/index", "1", KEY_END)); + ksAppendKey (spec, keyNew (COMMAND_SPEC_KEY (COMMAND_NAME) "/basepath", KEY_META, "description", "Path to the base keyset.", + KEY_META, "args", "indexed", KEY_META, "args/index", "2", KEY_END)); + ksAppendKey (spec, keyNew (COMMAND_SPEC_KEY (COMMAND_NAME) "/resultpath", KEY_META, "description", + "Path without keys where the merged keyset will be saved.", KEY_META, "args", "indexed", KEY_META, + "args/index", "3", KEY_END)); + ksAppendKey (spec, + keyNew (COMMAND_SPEC_KEY (COMMAND_NAME) "/strategy", KEY_META, "description", + "strategy to use in case of a conflict. Options: our,their,abort(default)", KEY_META, "opt", "s", KEY_META, + "opt/arg/help", "STRATEGY", KEY_META, "opt/long", "strategy", KEY_META, "opt/arg", "required", KEY_END)); + ksAppendKey (spec, keyNew (COMMAND_SPEC_KEY (COMMAND_NAME) "/force", KEY_META, "description", "Overwrite existing keys in result.", + KEY_META, "opt", "f", KEY_META, "opt/long", "force", KEY_META, "opt/arg", "none", KEY_END)); + + + ADD_BASIC_OPTIONS (spec, COMMAND_SPEC_KEY (COMMAND_NAME)) +} + + +void printKsNames (KeySet * ks) +{ + Key * cur = NULL; + for (elektraCursor it = 0; it < ksGetSize (ks); ++it) + { + cur = ksAtCursor (ks, it); + printf (" %s\n", keyName (cur)); + } +} + +int getKeySet (Key * where, KeySet ** ks, char * verboseName, Key * errorKey) +{ + KDB * handle = kdbOpen (NULL, errorKey); + int result = kdbGet (handle, *ks, where); + kdbClose (handle, errorKey); + *ks = ksCut (*ks, where); + ksLookup (*ks, where, 0); + if (verboseName != NULL) + { + printf ("got %s: %s with keys\n", verboseName, keyName (where)); + printKsNames (*ks); + } + return result; +} + +int execCmerge (KeySet * options, Key * errorKey) +{ + GET_BASIC_OPTIONS + + bool force = false; + tmp = GET_OPTION_KEY (options, "force"); + if (tmp != NULL) + { + elektraKeyToBoolean (GET_OPTION_KEY (options, "force"), &force); + } + + tmp = GET_OPTION_KEY (options, "strategy"); + int strategy = MERGE_STRATEGY_ABORT; + if (tmp != NULL) + { + const char * strategyName; + elektraKeyToString (tmp, &strategyName); + if (elektraStrCmp (strategyName, "our") == 0) + { + strategy = MERGE_STRATEGY_OUR; + } + else if (elektraStrCmp (strategyName, "their") == 0) + { + strategy = MERGE_STRATEGY_THEIR; + } + else if (elektraStrCmp (strategyName, "abort") != 0) + { + ELEKTRA_SET_VALIDATION_SEMANTIC_ERRORF (errorKey, "'%s' is not a valid strategy.", strategy); + return 1; + } + } + + // required args + const char * ourpath = getKeyNameFromOptions (options, GET_OPTION (options, "ourpath"), errorKey, verbose); + if (ourpath == NULL) return 1; + + const char * theirpath = getKeyNameFromOptions (options, GET_OPTION (options, "theirpath"), errorKey, verbose); + if (theirpath == NULL) + { + elektraFree ((void *) ourpath); + return 1; + } + + const char * basepath = getKeyNameFromOptions (options, GET_OPTION (options, "basepath"), errorKey, verbose); + if (basepath == NULL) + { + elektraFree ((void *) ourpath); + elektraFree ((void *) theirpath); + return 1; + } + + const char * resultpath = getKeyNameFromOptions (options, GET_OPTION (options, "resultpath"), errorKey, verbose); + if (resultpath == NULL) + { + elektraFree ((void *) ourpath); + elektraFree ((void *) theirpath); + elektraFree ((void *) basepath); + return 1; + } + + int ret = 0; + Key * oursRoot = keyNew (ourpath, KEY_END); + Key * theirsRoot = keyNew (theirpath, KEY_END); + Key * baseRoot = keyNew (basepath, KEY_END); + Key * resultRoot = keyNew (resultpath, KEY_END); + + KeySet * ours = ksNew (0, KS_END); + KeySet * theirs = ksNew (0, KS_END); + KeySet * base = ksNew (0, KS_END); + + KeySet * atResult = ksNew (0, KS_END); + KDB * handle = kdbOpen (NULL, errorKey); + if (kdbGet (handle, atResult, resultRoot) < 0) + { + ELEKTRA_SET_INTERNAL_ERRORF (errorKey, "get for \'%s\': %s", keyName (resultRoot), GET_ERR (resultRoot)); + ret = 1; + goto cleanup; + } + KeySet * discard = ksCut (atResult, resultRoot); + if (ksGetSize (discard) != 0 && !force) + { + ELEKTRA_SET_VALIDATION_SEMANTIC_ERROR (errorKey, "There are keys in the result path. Use -f to override them."); + ret = 1; + goto cleanup; + } + else if (ksGetSize (discard) != 0) + { + CLI_PRINT (CLI_LOG_VERBOSE, "will remove %ld keys, because %s was set", ksGetSize (discard), BOLD ("-f")); + } + + if (getKeySet (oursRoot, &ours, verbose ? "our" : NULL, errorKey) < 0) + { + ELEKTRA_SET_INTERNAL_ERRORF (errorKey, "get for \'%s\': %s", keyName (oursRoot), GET_ERR (resultRoot)); + ret = 1; + goto cleanup; + } + if (getKeySet (theirsRoot, &theirs, verbose ? "their" : NULL, errorKey) < 0) + { + ELEKTRA_SET_INTERNAL_ERRORF (errorKey, "get for \'%s\': %s", keyName (theirsRoot), GET_ERR (resultRoot)); + ret = 1; + goto cleanup; + } + if (getKeySet (baseRoot, &base, verbose ? "base" : NULL, errorKey) < 0) + { + ELEKTRA_SET_INTERNAL_ERRORF (errorKey, "get for \'%s\': %s", keyName (baseRoot), GET_ERR (resultRoot)); + ret = 1; + goto cleanup; + } + + Key * mergeInfo = keyNew ("/", KEY_END); + KeySet * mergeResult = elektraMerge (ours, oursRoot, theirs, theirsRoot, base, baseRoot, resultRoot, strategy, mergeInfo); + + int nrConflicts = elektraMergeGetConflicts (mergeInfo); + keyDel (mergeInfo); + if (mergeResult == NULL) + { + if (nrConflicts > 0 && strategy == MERGE_STRATEGY_ABORT) + { + ELEKTRA_SET_CONFLICTING_STATE_ERRORF (errorKey, "Aborted due to %ld conflicts.", nrConflicts); + } + else + { + ELEKTRA_SET_INTERNAL_ERROR (errorKey, "An error occurred during the merge."); + } + ret = 1; + goto cleanup; + } + + if (ksAppend (atResult, mergeResult) < 0) + { // not possible, but here for completeness + ELEKTRA_SET_VALIDATION_SEMANTIC_ERROR (errorKey, "An error occurred during the merge."); + ret = 1; + goto cleanup; + } + if (kdbSet (handle, atResult, resultRoot) < 0) + { + ELEKTRA_SET_VALIDATION_SEMANTIC_ERRORF (errorKey, "Could not set result: %s", GET_ERR (errorKey)); + ret = 1; + goto cleanup; + } + +cleanup: + elektraFree ((void *) ourpath); + elektraFree ((void *) theirpath); + elektraFree ((void *) basepath); + elektraFree ((void *) resultpath); + keyDel (oursRoot); + keyDel (theirsRoot); + keyDel (baseRoot); + keyDel (resultRoot); + ksDel (ours); + ksDel (theirs); + ksDel (base); + ksDel (atResult); + kdbClose (handle, errorKey); + return ret; +} diff --git a/src/tools/kdb/cmerge.cpp b/src/tools/kdb/cmerge.cpp deleted file mode 100644 index c3338052dda..00000000000 --- a/src/tools/kdb/cmerge.cpp +++ /dev/null @@ -1,140 +0,0 @@ -/** - * @file - * - * @brief - * - * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) - */ - -#include "cmerge.hpp" -#include "kdbmacros.h" -#include "kdbmerge.h" -#include "keyset.hpp" -#include -#include -#include -#include - -using ckdb::keyNew; - -CMergeCommand::CMergeCommand () -{ -} - -CMergeCommand::~CMergeCommand () -{ -} - -int CMergeCommand::execute (Cmdline const & cl ELEKTRA_UNUSED) -{ - if (cl.arguments.size () < 4) - { - throw invalid_argument ("Wrong number of arguments! At least 4 arguments needed"); - } - kdb::Key oursRoot = cl.createKey (0); - kdb::Key theirsRoot = cl.createKey (1); - kdb::Key baseRoot = cl.createKey (2); - kdb::Key resultRoot = cl.createKey (3); - ckdb::MergeStrategy strategy = ckdb::MERGE_STRATEGY_ABORT; - if (cl.strategy == "preserve") - { - /** This is here for compatibility. The old merge has preserve as default as defined in cmdline.cpp. - * As cmerge uses the existing functionality it is still default, even though it does not exist in cmerge. - * Default in new merge is abort. - * - * This will be obsolete as soon as cmerge supersedes the old merge. - */ - strategy = ckdb::MERGE_STRATEGY_ABORT; - } - else if (cl.strategy == "abort") - { - strategy = ckdb::MERGE_STRATEGY_ABORT; - } - else if (cl.strategy == "our") - { - strategy = ckdb::MERGE_STRATEGY_OUR; - } - else if (cl.strategy == "their") - { - strategy = ckdb::MERGE_STRATEGY_THEIR; - } - else - { - throw invalid_argument ("'" + cl.strategy + "' is not a valid strategy. Valid strategies are: abort, our, their"); - } - - kdb::KeySet ours; - kdb::KeySet theirs; - kdb::KeySet base; - - { - kdb::KDB lkdb; - lkdb.get (ours, oursRoot); - ours = ours.cut (oursRoot); - ours.lookup (oursRoot, 0); - if (cl.verbose) std::cout << "we got ours: " << oursRoot << " with keys\n" << ours << std::endl; - } - { - kdb::KDB lkdb; - lkdb.get (theirs, theirsRoot); - theirs = theirs.cut (theirsRoot); - ours.lookup (oursRoot, 0); - if (cl.verbose) std::cout << "we got theirs: " << theirsRoot << " with keys\n" << theirs << std::endl; - } - { - kdb::KDB lkdb; - lkdb.get (base, baseRoot); - base = base.cut (baseRoot); - ours.lookup (oursRoot, 0); - if (cl.verbose) std::cout << "we got base: " << baseRoot << " with keys\n" << base << std::endl; - } - kdb::KeySet keysAtResultRoot; - kdb.get (keysAtResultRoot, resultRoot); - kdb::KeySet discard = keysAtResultRoot.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 - { - throw CommandAbortException ("There are keys in the result path. Use -f to override them."); - } - } - ckdb::KeySet * c_ours = ours.getKeySet (); - ckdb::KeySet * c_theirs = theirs.getKeySet (); - ckdb::KeySet * c_base = base.getKeySet (); - ckdb::Key * informationKey = keyNew ("/", KEY_END); - ckdb::KeySet * c_merge_result = elektraMerge (c_ours, oursRoot.getKey (), c_theirs, theirsRoot.getKey (), c_base, - baseRoot.getKey (), resultRoot.getKey (), strategy, informationKey); - int numberOfConflicts = elektraMergeGetConflicts (informationKey); - ckdb::keyDel (informationKey); - if (c_merge_result != NULL) - { - kdb::KeySet merge_result = c_merge_result; - if (keysAtResultRoot.append (merge_result) < 0) - { - return 1; - } - if (kdb.set (keysAtResultRoot, resultRoot) < 0) - { - return 1; - } - return 0; - } - else - { - if (numberOfConflicts > 0 && strategy == ckdb::MERGE_STRATEGY_ABORT) - { - return 2; - } - else - { - return 1; - } - } -} diff --git a/src/tools/kdb/cmerge.h b/src/tools/kdb/cmerge.h new file mode 100644 index 00000000000..d5e462e9827 --- /dev/null +++ b/src/tools/kdb/cmerge.h @@ -0,0 +1,37 @@ +/** + * @file + * + * @brief Header for cmerge command + * + * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) + */ + +#ifndef ELEKTRA_KDB_CMERGE_H +#define ELEKTRA_KDB_CMERGE_H + +#include +#include + +/** + * Adds options specification of cmerge command to @spec + * + * @param spec the base spec where the commands spec should be added + */ +void addCmergeSpec (KeySet * spec); + +/** + * Executes the cmerge command + * + * @param options cli options and arguments as specified in @addCmergeSpec() + * @param errorKey key where errors and warnings should be saved + * + * @retval 0 ls command ran without errors + * @retval 1 errors occurred, keyGetMeta (errorKey, "error/reason") for info + * + */ +int execCmerge (KeySet * options, Key * errorKey); + +// helper functions +void printKsNames (KeySet * ks); + +#endif // ELEKTRA_KDB_CMERGE_H diff --git a/src/tools/kdb/cmerge.hpp b/src/tools/kdb/cmerge.hpp deleted file mode 100644 index 2cf3d0c2d49..00000000000 --- a/src/tools/kdb/cmerge.hpp +++ /dev/null @@ -1,50 +0,0 @@ -/** - * @file - * - * @brief - * - * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) - */ - -#ifndef CMERGE_HPP -#define CMERGE_HPP - -#include -#include - -using namespace std; - -class CMergeCommand : public Command -{ - kdb::KDB kdb; - -public: - CMergeCommand (); - ~CMergeCommand (); - - virtual int execute (Cmdline const & cmdline) override; - - virtual std::string getShortOptions () override - { - return "sf"; - } - - virtual std::string getSynopsis () override - { - return "[options] ourpath theirpath basepath resultpath"; - } - - virtual std::string getShortHelpText () override - { - return "Three-way merge of KeySets."; - } - - virtual std::string getLongHelpText () override - { - return "Performs a three-way merge between keysets.\n" - "On success the resulting keyset will be saved to resultpath.\n" - "On unresolved conflicts nothing will be changed.\n"; - } -}; - -#endif diff --git a/src/tools/kdb/colors.c b/src/tools/kdb/colors.c new file mode 100644 index 00000000000..77ec933201b --- /dev/null +++ b/src/tools/kdb/colors.c @@ -0,0 +1,30 @@ +/** + * @file + * + * @brief Implementation of ANSI escape helper functions in the kdb tool + * + * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) + */ + +#include +#include +#include + +const char * formatString (char ** buffer, int colorMode, const char * escapeCode, const char * text) +{ + if (colorMode == CLI_COLOR_NEVER) return text; + if (*buffer == NULL) + { + *buffer = elektraMalloc (4096); + } + char * tmp = elektraMalloc (elektraStrLen (escapeCode) + elektraStrLen (text) + elektraStrLen (RESET) + 1); + strcpy (tmp, escapeCode); + strcat (tmp, text); + strcat (tmp, RESET); + + **buffer = '\0'; + strcpy (*buffer, tmp); + elektraFree (tmp); + + return *buffer; +} diff --git a/src/tools/kdb/colors.h b/src/tools/kdb/colors.h new file mode 100644 index 00000000000..b736f0151ca --- /dev/null +++ b/src/tools/kdb/colors.h @@ -0,0 +1,72 @@ +/** + * @file + * + * @brief Header for colored CLI output + * + * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) + */ + +#ifndef ELEKTRA_KDB_COLOR_H +#define ELEKTRA_KDB_COLOR_H + +#include + +#define RESET "\x1b[0m" + +// colors +#define COLOR_BLACK "\x1b[30m" +#define COLOR_RED "\x1b[31m" +#define COLOR_GREEN "\x1b[32m" +#define COLOR_YELLOW "\x1b[33m" +#define COLOR_BLUE "\x1b[34m" +#define COLOR_MAGENTA "\x1b[35m" +#define COLOR_CYAN "\x1b[36m" +#define COLOR_WHITE "\x1b[37m" + +// background colors +#define BG_COLOR_BLACK "\x1b[40m" +#define BG_COLOR_RED "\x1b[41m" +#define BG_COLOR_GREEN "\x1b[42m" +#define BG_COLOR_YELLOW "\x1b[43m" +#define BG_COLOR_BLUE "\x1b[44m" +#define BG_COLOR_MAGENTA "\x1b[45m" +#define BG_COLOR_CYAN "\x1b[46m" +#define BG_COLOR_WHITE "\x1b[47m" + +// formatting +#define FORMAT_BOLD "\x1b[1m" +#define FORMAT_UNDERSCORE "\x1b[4m" + +#define FORMAT_TEXT(escapeCode, text) formatString (&fmtBuffer, colorMode, escapeCode, text) + +#define BLACK(text) FORMAT_TEXT (COLOR_BLACK, text) +#define RED(text) FORMAT_TEXT (COLOR_RED, text) +#define GREEN(text) FORMAT_TEXT (COLOR_GREEN, text) +#define YELLOW(text) FORMAT_TEXT (COLOR_YELLOW, text) +#define BLUE(text) FORMAT_TEXT (COLOR_BLUE, text) +#define MAGENTA(text) FORMAT_TEXT (COLOR_MAGENTA, text) +#define CYAN(text) FORMAT_TEXT (COLOR_CYAN, text) +#define WHITE(text) FORMAT_TEXT (COLOR_WHITE, text) + +#define BG_BLACK(text) FORMAT_TEXT (BG_COLOR_BLACK, text) +#define BG_RED(text) FORMAT_TEXT (BG_COLOR_RED, text) +#define BG_GREEN(text) FORMAT_TEXT (BG_COLOR_GREEN, text) +#define BG_YELLOW(text) FORMAT_TEXT (BG_COLOR_YELLOW, text) +#define BG_BLUE(text) FORMAT_TEXT (BG_COLOR_BLUE, text) +#define BG_MAGENTA(text) FORMAT_TEXT (BG_COLOR_MAGENTA, text) +#define BG_CYAN(text) FORMAT_TEXT (BG_COLOR_CYAN, text) +#define BG_WHITE(text) FORMAT_TEXT (BG_COLOR_WHITE, text) + +#define BOLD(text) FORMAT_TEXT (FORMAT_BOLD, text) +#define UNDERSCORE(text) FORMAT_TEXT (FORMAT_UNDERSCORE, text) + +const char * formatString (char ** buffer, int colorMode, const char * color, const char * text); + +enum COLOR_MODE +{ + CLI_COLOR_NEVER, + CLI_COLOR_AUTO, + CLI_COLOR_ALWAYS +}; + +#endif // ELEKTRA_KDB_COLOR_H diff --git a/src/tools/kdb/command.c b/src/tools/kdb/command.c new file mode 100644 index 00000000000..885f3d12a3c --- /dev/null +++ b/src/tools/kdb/command.c @@ -0,0 +1,89 @@ +/** + * @file + * + * @brief Implementation of things used everywhere in the kdb tool + * + * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) + */ + +#include + +#include +#include +#include + +const char * expandKeyName (KeySet * ks, const char * name, bool * resolved) +{ + *resolved = false; + const char * ret = NULL; + if (ks == NULL || name == NULL) + { + return ret; + } + if (name[0] != '+') + { + ret = elektraStrDup (name); + } + else + { + int bookmarkEnd = strcspn (name, "/"); + char * bookmarkName = elektraMalloc (bookmarkEnd); + strncpy (bookmarkName, name + 1, bookmarkEnd - 1); + bookmarkName[bookmarkEnd - 1] = '\0'; + Key * bookmarkLookup = keyNew (CLI_BASE_KEY "/bookmarks", KEY_END); + keyAddBaseName (bookmarkLookup, bookmarkName); + Key * bookmarkKey = ksLookup (ks, bookmarkLookup, KDB_O_DEL); + elektraFree (bookmarkName); + if (bookmarkKey != NULL) + { + Key * resolvedKey = keyNew (keyString (bookmarkKey), KEY_END); + if (resolvedKey != NULL) + { // the bookmark's value may not be valid key name + keyAddName (resolvedKey, name + bookmarkEnd); + ret = elektraStrDup (keyName (resolvedKey)); + keyDel (resolvedKey); + *resolved = true; + } + } + } + return ret; +} + + +const char * getKeyNameFromOptions (KeySet * options, const char * rawName, Key * errorKey, bool verbose) +{ + bool resolved = false; + const char * result = expandKeyName (options, rawName, &resolved); + if (result == NULL) + { + ELEKTRA_SET_VALIDATION_SEMANTIC_ERRORF (errorKey, "could not resolve bookmark in '%s'", rawName); + return NULL; + } + + if (verbose && resolved) + { + printf ("resolved bookmark: \'%s\' -> \'%s\'\n", rawName, result); + } + + Key * key = keyNew (result, KEY_END); + if (key == NULL) + { + ELEKTRA_SET_VALIDATION_SEMANTIC_ERRORF (errorKey, "'%s' is not valid key name.", result); + return NULL; + } + keyDel (key); + return result; +} + +void cliPrint (int logLevel, int minLogLevel, const char * fmt, ...) +{ + if (logLevel < minLogLevel) + { + return; + } + + va_list args; + va_start (args, fmt); + vprintf (fmt, args); + va_end (args); +} diff --git a/src/tools/kdb/command.h b/src/tools/kdb/command.h new file mode 100644 index 00000000000..bd5783a9e1c --- /dev/null +++ b/src/tools/kdb/command.h @@ -0,0 +1,175 @@ +/** + * @file + * + * @brief Header for things used everywhere in the kdb tool + * + * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) + */ + +#ifndef ELEKTRA_KDB_COMMAND_H +#define ELEKTRA_KDB_COMMAND_H + +#include + +#include +#include +#include + +#define CLI_BASE_KEY "/sw/elektra/kdb/#0/current" + +#define ADD_BASIC_OPTIONS(baseSpec, baseKeyName) \ + ksAppendKey (baseSpec, keyNew (baseKeyName "/debug", KEY_META, "description", \ + "Give debug information or ask debug questions (in interactive mode).", KEY_META, "opt", "d", \ + KEY_META, "opt/long", "debug", KEY_META, "opt/arg", "none", KEY_END)); \ + ksAppendKey (baseSpec, keyNew (baseKeyName "/verbose", KEY_META, "description", "Explain what is happening.", KEY_META, "opt", \ + "v", KEY_META, "opt/long", "verbose", KEY_META, "opt/arg", "none", KEY_END)); \ + ksAppendKey (baseSpec, keyNew (baseKeyName "/version", KEY_META, "description", "Print version info.", KEY_META, "opt", "V", \ + KEY_META, "opt/long", "version", KEY_META, "opt/arg", "none", KEY_END)); \ + ksAppendKey (baseSpec, keyNew (baseKeyName "/profile", KEY_META, "description", "Use a different profile for kdb configuration.", \ + KEY_META, "opt", "p", KEY_META, "opt/arg/help", "NAME", KEY_META, "opt/long", "profile", KEY_META, \ + "opt/arg", "required", KEY_END)); \ + ksAppendKey (baseSpec, keyNew (baseKeyName "/color", KEY_META, "description", "Print never/auto(default)/always colored output.", \ + KEY_META, "opt", "C", KEY_META, "opt/arg/help", "WHEN", KEY_META, "opt/long", "color", KEY_META, \ + "opt/arg", "required", KEY_END)); \ + ksAppendKey (baseSpec, \ + keyNew (baseKeyName "/nonewline", KEY_META, "description", "Suppress the newline at the end of the output.", \ + KEY_META, "opt", "n", KEY_META, "opt/long", "no-newline", KEY_META, "opt/arg", "none", KEY_END)); + +#define GET_OPT_KEY(options, key) ksLookupByName (options, key, 0) +#define GET_OPT(options, key) keyString (GET_OPT_KEY (options, key)) + +#define OR(value, def) \ + void * tmp = value; \ + tmp == NULL ? (def) : tmp +#define HAS_ERR(errorKey) keyGetMeta (errorKey, "error/reason") != NULL +#define GET_ERR(errorKey) keyString (keyGetMeta (errorKey, "error/reason")) +#define COMMAND_BASE_KEY(name) CLI_BASE_KEY "/" name +#define COMMAND_SPEC_KEY(name) "spec:" COMMAND_BASE_KEY (name) + +// only use in the context of a command/sub-command (options variable and GET_OPTION_KEY macro have to be in scope) +#define GET_BASIC_OPTIONS \ + bool debug = false; \ + Key * tmp = GET_OPTION_KEY (options, "debug"); \ + if (tmp != NULL) \ + { \ + elektraKeyToBoolean (GET_OPTION_KEY (options, "debug"), &debug); \ + } \ + /* debug -> verbose, so logLevel = debug+verbose */ \ + bool verbose = debug; \ + if (verbose) \ + { \ + }; /* disable unused variable warning */ \ + tmp = GET_OPTION_KEY (options, "verbose"); \ + if (tmp != NULL) \ + { \ + elektraKeyToBoolean (GET_OPTION_KEY (options, "verbose"), &verbose); \ + } \ + bool noNewLine = false; \ + tmp = GET_OPTION_KEY (options, "nonewline"); \ + if (tmp != NULL) \ + { \ + elektraKeyToBoolean (GET_OPTION_KEY (options, "nonewline"), &noNewLine); \ + } \ + int colorMode = CLI_COLOR_AUTO; \ + if (colorMode) \ + { \ + }; /* disable unused variable warning */ \ + tmp = GET_OPTION_KEY (options, "color"); \ + if (tmp != NULL) \ + { \ + if (elektraStrCmp ("never", keyString (tmp)) == 0) \ + { \ + colorMode = CLI_COLOR_NEVER; \ + } \ + if (elektraStrCmp ("always", keyString (tmp)) == 0) \ + { \ + colorMode = CLI_COLOR_ALWAYS; \ + } \ + } \ + char * fmtBuffer = NULL; \ + if (fmtBuffer) \ + { \ + }; /* disable unused variable warning */ \ + if (!isatty (STDOUT_FILENO)) \ + { \ + colorMode = CLI_COLOR_NEVER; \ + } \ + \ + int logLevel = verbose + debug; \ + if (logLevel) \ + { \ + }; /* disable unused variable warning */ \ + keyDel (tmp); + +#define EXEC_EXT(prog, argv, status) \ + pid_t extPid; \ + int timeout = 1000; \ + if (0 == (extPid = fork ())) \ + { \ + if (-1 == execve (prog, (char **) (argv), NULL)) \ + { \ + perror ("child process execve failed [%m]"); \ + return -1; \ + } \ + } \ + while (0 == waitpid (extPid, status, WNOHANG)) \ + { \ + if (--timeout < 0) \ + { \ + perror ("timeout"); \ + return -1; \ + } \ + sleep (1); \ + } + +// only print if we are at least as 'verbose' as minLogLevel +#define CLI_PRINT(minLogLevel, fmt, ...) cliPrint (logLevel, minLogLevel, fmt, __VA_ARGS__) + +/** \ + * Expands a keyname if it contains a bookmark. If @name does not contain a bookmark ref a copy of @name is returned. \ + * \ + * @param name the keyname that might contain a bookmark, and where the expanded name should be saved \ + * @param ks keyset that contains information about the bookmarks \ + * @param resolved will be set to true iff a bookmark was resolved successfully \ + * \ + * @return NULL if the bookmark could not be resolved, NULL was passed as @ks or @name \ + * @return string of the full key otherwise, has to be freed after usage \ + */ +const char * expandKeyName (KeySet * ks, const char * name, bool * resolved); + +/** + * Get a key name string from options and resolve bookmarks if present. + * + * @param options key set used to resolve bookmarks + * @param rawName the keyname as it was entered by the user, may contain a bookmark + * @param errorKey where errors should be written to, in case of: 1. can't resolve bookmark, 2. not a valid key name + * @param verbose print more info + * @return a pointer to the key name with the resolved bookmark(if present), has to freed + */ +const char * getKeyNameFromOptions (KeySet * options, const char * rawName, Key * errorKey, bool verbose); + +/** + * Helper for printing, handles log levels + * + * @param logLevel the log level set by the user + * @param minLogLevel minimum log level so the message is printed (NONE -> always print, VERBOSE -> printf if VERBOSE or DEBUG, ...) + * @param fmt format string for printing + * @param ... + */ +void cliPrint (int logLevel, int minLogLevel, const char * fmt, ...); + +typedef struct command +{ + const char * name; + void (*addSpec) (KeySet * spec); + int (*exec) (KeySet * options, Key * errorKey); +} command; + +enum LOG_LEVEL +{ + CLI_LOG_NONE = 0, + CLI_LOG_VERBOSE, + CLI_LOG_DEBUG +}; + +#endif // ELEKTRA_KDB_COMMAND_H diff --git a/src/tools/kdb/cp.c b/src/tools/kdb/cp.c new file mode 100644 index 00000000000..c670de29263 --- /dev/null +++ b/src/tools/kdb/cp.c @@ -0,0 +1,150 @@ +/** + * @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..2584b1cd3ed --- /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/dirname.c b/src/tools/kdb/dirname.c new file mode 100644 index 00000000000..f4befea9a89 --- /dev/null +++ b/src/tools/kdb/dirname.c @@ -0,0 +1,67 @@ +/** + * @file + * + * @brief Implementation of kdb dirname command + * + * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) + */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#define COMMAND_NAME "dirname" + +#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 addDirnameSpec (KeySet * spec) +{ + ksAppendKey (spec, keyNew (COMMAND_SPEC_KEY (COMMAND_NAME), KEY_META, "description", "Get the dir name of a key.", KEY_META, + "command", COMMAND_NAME, KEY_END)); + ksAppendKey (spec, keyNew (COMMAND_SPEC_KEY (COMMAND_NAME) "/name", KEY_META, "description", "The name of the key", KEY_META, + "args", "indexed", KEY_META, "args/index", "0", KEY_END)); + + ADD_BASIC_OPTIONS (spec, COMMAND_SPEC_KEY (COMMAND_NAME)) +} + +int execDirname (KeySet * options, Key * errorKey) +{ + GET_BASIC_OPTIONS + + const char * name = getKeyNameFromOptions (options, GET_OPTION (options, "name"), errorKey, verbose); + if (name == NULL) return 1; + + Key * key = keyNew (name, KEY_END); + if (key == NULL) + { + ELEKTRA_SET_VALIDATION_SEMANTIC_ERRORF (errorKey, "'%s' is not a valid key name.", name); + elektraFree ((void *) name); + return 1; + } + + name = keyName (key); + int dirnameLen = keyGetNameSize (key); + while (name[--dirnameLen] != '/') + ; + while (*name != '/') + { + name++; + dirnameLen--; + } + if (!dirnameLen) + { + dirnameLen = 1; + } + CLI_PRINT (CLI_LOG_NONE, "%*.*s", dirnameLen, dirnameLen, BOLD (name)); + + elektraFree ((void *) name); + keyDel (key); + return 0; +} diff --git a/src/tools/kdb/dirname.cpp b/src/tools/kdb/dirname.cpp deleted file mode 100644 index 826e1ad6012..00000000000 --- a/src/tools/kdb/dirname.cpp +++ /dev/null @@ -1,27 +0,0 @@ -/** - * @file - * - * @brief - * - * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) - */ - -#include -#include -#include - - -using namespace std; - -DirnameCommand::DirnameCommand () -{ -} - -int DirnameCommand::execute (Cmdline const & cl) -{ - return executeNamepartcommand (cl, keyDirname); -} - -DirnameCommand::~DirnameCommand () -{ -} diff --git a/src/tools/kdb/dirname.h b/src/tools/kdb/dirname.h new file mode 100644 index 00000000000..b8c6032f1cc --- /dev/null +++ b/src/tools/kdb/dirname.h @@ -0,0 +1,33 @@ +/** + * @file + * + * @brief Header for dirname command + * + * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) + */ + +#ifndef ELEKTRA_KDB_DIRNAME_H +#define ELEKTRA_KDB_DIRNAME_H + +#include + +/** + * Adds options specification of dirname command to @spec + * + * @param spec the base spec where the commands spec should be added + */ +void addDirnameSpec (KeySet * spec); + +/** + * Executes the dirname command + * + * @param options cli options and arguments as specified in @addDirnameSpec() + * @param errorKey key where errors and warnings should be saved + * + * @retval 0 ls command ran without errors + * @retval 1 errors occurred, keyGetMeta (errorKey, "error/reason") for info + * + */ +int execDirname (KeySet * options, Key * errorKey); + +#endif // ELEKTRA_KDB_DIRNAME_H diff --git a/src/tools/kdb/dirname.hpp b/src/tools/kdb/dirname.hpp deleted file mode 100644 index 3b808cae502..00000000000 --- a/src/tools/kdb/dirname.hpp +++ /dev/null @@ -1,47 +0,0 @@ -/** - * @file - * - * @brief - * - * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) - */ - -#ifndef DIRNAME_HPP -#define DIRNAME_HPP - -#include "coloredkdbio.hpp" -#include -#include - -class DirnameCommand : public Command -{ - kdb::KDB kdb; - -public: - DirnameCommand (); - ~DirnameCommand (); - - virtual std::string getShortOptions () override - { - return "n"; - } - - virtual std::string getSynopsis () override - { - return ""; - } - - virtual std::string getShortHelpText () override - { - return "Get the cascading directory name of a key."; - } - - virtual std::string getLongHelpText () override - { - return "For example, \"kdb dirname user:/key/subkey\" will yield \"/key\".\n"; - } - - virtual int execute (Cmdline const & cmdline) override; -}; - -#endif diff --git a/src/tools/kdb/factory.hpp b/src/tools/kdb/factory.hpp index 50b3299ed49..a8f4069e042 100644 --- a/src/tools/kdb/factory.hpp +++ b/src/tools/kdb/factory.hpp @@ -23,40 +23,26 @@ #include // TODO: to add a new command, 1.) include your header here -#include #include -#include #include #include -#include -#include #include #include #include -#include #include -#include +#include +#include #include #include -#include #include #include -#include -#include -#include -#include #include -#include -#include #include #include #include #include -#include -#include #include #include -#include #include #include #include @@ -88,23 +74,11 @@ class Factory Factory () : m_factory () { // TODO: to add a new command, 2.) add a line here -> and you are done - m_factory.insert (std::make_pair ("get", std::make_shared> ())); - m_factory.insert (std::make_pair ("set", std::make_shared> ())); - m_factory.insert (std::make_pair ("rm", std::make_shared> ())); - m_factory.insert (std::make_pair ("ls", std::make_shared> ())); 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 ("mv", 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> ())); - m_factory.insert (std::make_pair ("find", std::make_shared> ())); - m_factory.insert (std::make_pair ("meta-get", std::make_shared> ())); - m_factory.insert (std::make_pair ("meta-show", std::make_shared> ())); - m_factory.insert (std::make_pair ("meta-rm", std::make_shared> ())); - m_factory.insert (std::make_pair ("meta-set", std::make_shared> ())); - m_factory.insert (std::make_pair ("meta-ls", std::make_shared> ())); m_factory.insert (std::make_pair ("plugin-info", std::make_shared> ())); m_factory.insert (std::make_pair ("test", std::make_shared> ())); m_factory.insert (std::make_pair ("plugin-check", std::make_shared> ())); @@ -115,16 +89,12 @@ class Factory m_factory.insert (std::make_pair ("file", std::make_shared> ())); m_factory.insert (std::make_pair ("sget", std::make_shared> ())); m_factory.insert (std::make_pair ("merge", std::make_shared> ())); - m_factory.insert (std::make_pair ("cmerge", std::make_shared> ())); m_factory.insert (std::make_pair ("plugin-list", std::make_shared> ())); m_factory.insert (std::make_pair ("editor", std::make_shared> ())); m_factory.insert (std::make_pair ("spec-mount", std::make_shared> ())); m_factory.insert (std::make_pair ("smount", std::make_shared> ())); m_factory.insert (std::make_pair ("list-commands", std::make_shared> ())); m_factory.insert (std::make_pair ("gen", std::make_shared> ())); - m_factory.insert (std::make_pair ("namespace", std::make_shared> ())); - m_factory.insert (std::make_pair ("basename", std::make_shared> ())); - m_factory.insert (std::make_pair ("dirname", std::make_shared> ())); m_factory.insert (std::make_pair ("validate", std::make_shared> ())); } diff --git a/src/tools/kdb/find.c b/src/tools/kdb/find.c new file mode 100644 index 00000000000..de5aeb89f58 --- /dev/null +++ b/src/tools/kdb/find.c @@ -0,0 +1,101 @@ +/** + * @file + * + * @brief Implementation of kdb find command + * + * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define COMMAND_NAME "find" + +#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 addFindSpec (KeySet * spec) +{ + ksAppendKey (spec, keyNew (COMMAND_SPEC_KEY (COMMAND_NAME), KEY_META, "description", "Find keys that match a regex pattern.", + KEY_META, "command", COMMAND_NAME, KEY_END)); + ksAppendKey (spec, keyNew (COMMAND_SPEC_KEY (COMMAND_NAME) "/pattern", KEY_META, "description", "The patter that should be matched", + KEY_META, "args", "indexed", KEY_META, "args/index", "0", KEY_END)); + + ADD_BASIC_OPTIONS (spec, COMMAND_SPEC_KEY (COMMAND_NAME)) +} + +int execFind (KeySet * options, Key * errorKey) +{ + int ret = 0; + GET_BASIC_OPTIONS + + const char * patternString = GET_OPTION (options, "pattern"); + + regex_t regex; + if (regcomp (®ex, patternString, 0)) + { + ret = 1; + ELEKTRA_SET_VALIDATION_SYNTACTIC_ERRORF (errorKey, "could not compile regex pattern: '%s'", patternString); + return ret; + } + + Key * root = keyNew ("/", KEY_END); + + KeySet * conf = ksNew (0, KS_END); + KDB * handle = kdbOpen (NULL, errorKey); + CLI_PRINT (CLI_LOG_DEBUG, "loading %s\n", "/"); + + if (kdbGet (handle, conf, root) == -1) + { + ELEKTRA_SET_VALIDATION_SEMANTIC_ERRORF (errorKey, "could not load key database", GET_ERR (root)); + ret = 1; + goto cleanup; + } + CLI_PRINT (CLI_LOG_DEBUG, "loaded %s\n", "/"); + + Key * cur = NULL; + + int regexResult; + int count = 0; + char errorMessageBuff[100]; + + for (elektraCursor it = 0; it < ksGetSize (conf); ++it) + { + cur = ksAtCursor (conf, it); + regexResult = regexec (®ex, keyName (cur), 0, NULL, 0); + CLI_PRINT (CLI_LOG_DEBUG, "matching '%s' -> %d\n", keyName (cur), regexResult); + + if (!regexResult) + { + CLI_PRINT (CLI_LOG_NONE, "%s\n", keyName (cur)); + count++; + } + else if (regexResult != REG_NOMATCH) + { + regerror (regexResult, ®ex, errorMessageBuff, sizeof (errorMessageBuff)); + CLI_PRINT (CLI_LOG_VERBOSE, "matching '%s' with '%s' failed: %s", keyName (cur), patternString, errorMessageBuff); + } + } + + CLI_PRINT (CLI_LOG_VERBOSE, "\nfound %ld keys", count); + +cleanup: + if (!noNewLine) + { + printf ("\n"); + } + keyDel (root); + elektraFree ((void *) fmtBuffer); + ksDel (conf); + regfree (®ex); + kdbClose (handle, errorKey); + return ret; +} diff --git a/src/tools/kdb/find.cpp b/src/tools/kdb/find.cpp deleted file mode 100644 index 10194d11711..00000000000 --- a/src/tools/kdb/find.cpp +++ /dev/null @@ -1,76 +0,0 @@ -/** - * @file - * - * @brief - * - * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) - */ - -#include - -#include -#include - -#include -#include -#include - -using namespace kdb; -using namespace std; - -FindCommand::FindCommand () -{ -} - -int FindCommand::execute (Cmdline const & cl) -{ - if (cl.arguments.size () != 1) throw invalid_argument ("Need one argument"); - - Key root ("/", KEY_END); - KDB kdb (root); - KeySet ks; - - printWarnings (cerr, root, cl.verbose, cl.debug); - - kdb.get (ks, root); - - if (cl.verbose) cout << "size of all keys: " << ks.size () << endl; - - KeySet part; - std::smatch match; - - try - { - std::regex reg (cl.arguments[0]); - - for (const auto & it : ks) - { - std::string name = it.getName (); - if (std::regex_search (name, match, reg)) - { - part.append (it); - } - } - } - catch (const regex_error & error) - { - cerr << "Regex error in “" << cl.arguments[0] << "”: " << error.what () << endl; - } - - if (cl.verbose) cout << "size of found keys: " << part.size () << endl; - cout.setf (std::ios_base::unitbuf); - if (cl.null) - { - cout.unsetf (std::ios_base::skipws); - } - - std::cout << part; - - printWarnings (cerr, root, cl.verbose, cl.debug); - - return 0; -} - -FindCommand::~FindCommand () -{ -} diff --git a/src/tools/kdb/find.h b/src/tools/kdb/find.h new file mode 100644 index 00000000000..e7de3c7787a --- /dev/null +++ b/src/tools/kdb/find.h @@ -0,0 +1,36 @@ +/** + * @file + * + * @brief Header for find command + * + * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) + */ + +#ifndef ELEKTRA_KDB_FIND_H +#define ELEKTRA_KDB_FIND_H + +#include + +/** + * Adds options specification of find command to @spec + * + * @param spec the base spec where the commands spec should be added + */ +void addFindSpec (KeySet * spec); + +/** + * Executes the find command + * + * @param options cli options and arguments as specified in addFindSpec() + * @param errorKey key where errors and warnings should be saved + * + * @retval 0 find command ran without errors + * @retval 1 errors occurred, keyGetMeta (errorKey, "error/reason") for info + * + */ +int execFind (KeySet * options, Key * errorKey); + +// helper functions +int getKeyNameDepth (const char * name); + +#endif // ELEKTRA_KDB_FIND_H diff --git a/src/tools/kdb/find.hpp b/src/tools/kdb/find.hpp deleted file mode 100644 index 5250bcbfb00..00000000000 --- a/src/tools/kdb/find.hpp +++ /dev/null @@ -1,45 +0,0 @@ -/** - * @file - * - * @brief - * - * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) - */ - -#ifndef KDB_FIND_H -#define KDB_FIND_H - -#include "coloredkdbio.hpp" -#include -#include - -class FindCommand : public Command -{ -public: - FindCommand (); - ~FindCommand (); - - virtual std::string getShortOptions () override - { - return "0"; - } - - virtual std::string getSynopsis () override - { - return ""; - } - - virtual std::string getShortHelpText () override - { - return "Find keys with a given regex."; - } - - virtual std::string getLongHelpText () override - { - return ""; - } - - virtual int execute (Cmdline const & cmdline) override; -}; - -#endif diff --git a/src/tools/kdb/get.c b/src/tools/kdb/get.c new file mode 100644 index 00000000000..5b4e8433a42 --- /dev/null +++ b/src/tools/kdb/get.c @@ -0,0 +1,223 @@ +/** + * @file + * + * @brief KDB get subcommand + * + * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define COMMAND_NAME "get" + +#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 addGetSpec (KeySet * spec) +{ + ksAppendKey (spec, keyNew (COMMAND_SPEC_KEY (COMMAND_NAME), KEY_META, "description", "Get the value of an individual key.", + KEY_META, "command", COMMAND_NAME, KEY_END)); + ksAppendKey (spec, keyNew (COMMAND_SPEC_KEY (COMMAND_NAME) "/all", KEY_META, "description", "Consider all of the keys", KEY_META, + "opt", "a", KEY_META, "opt/long", "all", KEY_META, "opt/arg", "none", KEY_END)); + ksAppendKey (spec, keyNew (COMMAND_SPEC_KEY (COMMAND_NAME) "/name", KEY_META, "description", "The name of the key", KEY_META, + "args", "indexed", KEY_META, "args/index", "0", KEY_END)); + + ADD_BASIC_OPTIONS (spec, COMMAND_SPEC_KEY (COMMAND_NAME)) +} + +int execGet (KeySet * options, Key * errorKey) +{ + int ret = 0; + GET_BASIC_OPTIONS + + bool all = false; + tmp = GET_OPTION_KEY (options, "all"); + if (tmp != NULL) + { + elektraKeyToBoolean (GET_OPTION_KEY (options, "all"), &all); + } + + const char * name = getKeyNameFromOptions (options, GET_OPTION (options, "name"), errorKey, verbose); + if (name == NULL) return 1; + + KeySet * searchIn = ksNew (0, KS_END); + KDB * handle = kdbOpen (NULL, errorKey); + Key * toLookUp = keyNew (name, KEY_END); + + if (toLookUp == NULL) + { + ELEKTRA_SET_VALIDATION_SEMANTIC_ERRORF (errorKey, "'%s' is not a valid key name", name); + kdbClose (handle, errorKey); + ksDel (searchIn); + keyDel (toLookUp); + return -1; + } + + Key * parentKey = keyDup (toLookUp, 0); + if (all) + { + parentKey = keyNew ("/", KEY_END); + } + if (kdbGet (handle, searchIn, parentKey) == -1) + { + ELEKTRA_SET_VALIDATION_SEMANTIC_ERRORF (errorKey, "could not load '%s': %s", name, GET_ERR (toLookUp)); + kdbClose (handle, errorKey); + ksDel (searchIn); + keyDel (toLookUp); + keyDel (parentKey); + return 1; + } + kdbClose (handle, errorKey); + + setCallback (toLookUp, warnOnMeta); + if (verbose) + { + CLI_PRINT (CLI_LOG_VERBOSE, "got %ld keys\n", ksGetSize (searchIn)); + setCallback (toLookUp, printTrace); + } + + Key * found = ksLookup (searchIn, toLookUp, KDB_O_CALLBACK); + + if (keyCopyAllMeta (errorKey, toLookUp) == -1) + { + ELEKTRA_SET_INTERNAL_ERROR (errorKey, "could not copy key meta to errorKey"); + ksDel (searchIn); + keyDel (toLookUp); + return 1; + } + + if (found == NULL) + { + CLI_PRINT (CLI_LOG_NONE, "Did not find key '%s'", RED (name)); + ret = -1; + goto cleanup; + } + + if (keyGetNamespace (found) == KEY_NS_DEFAULT) + { + CLI_PRINT (CLI_LOG_VERBOSE, "The key was not found in any other namespace, taking the %s\n", BOLD ("default")); + } + CLI_PRINT (CLI_LOG_VERBOSE, "The resulting keyname is %s\n", keyName (found)); + CLI_PRINT (CLI_LOG_VERBOSE, "The resulting value size is %ld\n", keyGetValueSize (found)); + + if (keyIsBinary (found)) + { + ssize_t binSize = keyGetValueSize (found); + CLI_PRINT (CLI_LOG_VERBOSE, "The key is %s.\n", BOLD (binSize == 0 ? "null" : "binary")); + const char * buffer = keyValue (found); + for (ssize_t pos = 0; pos < binSize; pos++) + { + CLI_PRINT (CLI_LOG_NONE, "%x", buffer[pos]); + } + } + else + { + CLI_PRINT (CLI_LOG_NONE, "%s", keyString (found)); + } + +cleanup: + if (!noNewLine) + { + printf ("\n"); + } + keyDel (parentKey); + ksDel (searchIn); + keyDel (toLookUp); + return ret; +} + +void printOptions (elektraLookupFlags options) +{ + // :'<,'>s/\(.*\)/^Iif(options \& \1) printf("\1 "); + if (options & KDB_O_SPEC) printf ("KDB_O_SPEC "); + if (options & KDB_O_CREATE) printf ("KDB_O_CREATE "); + if (options & KDB_O_NOCASCADING) printf ("KDB_O_NOCASCADING "); + if (options & KDB_O_NOSPEC) printf ("KDB_O_NOSPEC "); + if (options & KDB_O_NODEFAULT) printf ("KDB_O_NODEFAULT "); + if (options & KDB_O_CALLBACK) printf ("KDB_O_CALLBACK"); +} + +const char * getCascadingName (const char * str) +{ + if (str == NULL) return "/"; + const char * r = strchr (str, '/'); + return r == NULL ? "/" : r; +} + +Key * warnOnMeta (ELEKTRA_UNUSED KeySet * ks, ELEKTRA_UNUSED Key * key, Key * found, elektraLookupFlags options) +{ + if (found != NULL && strncmp (keyName (found), "spec:/", 6) == 0 && options == KDB_O_CALLBACK) + { + const Key * meta = keyGetMeta (found, "context"); + if (meta != NULL) + { + ELEKTRA_ADD_RESOURCE_WARNINGF ( + key, "%s is context dependent, shown result might be wrong, -v shows you the trace to the key\n", + keyName (found)); + } + } + return found; +} + + +Key * printTrace (ELEKTRA_UNUSED KeySet * ks, Key * key, Key * found, elektraLookupFlags options) +{ + warnOnMeta (ks, key, found, options); + const char * lastKeyName = keyValue (keyGetMeta (key, "callback/print_trace/last_key_name")); + const char * name = keyName (key); + const char * rawDepth = keyValue (keyGetMeta (key, "callback/print_trace/depth")); + + int depth = rawDepth == NULL ? 0 : atoi (rawDepth); + for (int i = 0; i < depth; ++i) + { + printf (" "); + } + + printf ("searching %s%s", (name[0] == '/' ? "default of spec" : ""), name); + printf (", found: %s", (found != NULL ? keyName (found) : "")); + if (options) + { + printf (", options: "); + printOptions (options); + } + printf ("\n"); + + int newDepth = depth; + if (elektraStrNCmp (name, "spec:/", 6) == 0 && (options & KDB_O_CALLBACK)) + { + newDepth += 4; + } + else if (elektraStrCmp (getCascadingName (lastKeyName), getCascadingName (name)) != 0) + { + newDepth = depth != 0 ? depth - 2 : depth; + } + if (newDepth != depth) + { + char buff[11]; + snprintf (buff, 11, "%d", newDepth); + keySetMeta (key, "callback/print_trace/depth", buff); + } + keySetMeta (key, "callback/print_trace/last_key_name", name); + return found; +} + +void setCallback (Key * key, Key * (*f) (KeySet * ks, Key * key, Key * found, elektraLookupFlags flags)) +{ + union + { + Key * (*f) (KeySet * ks, Key * key, Key * found, elektraLookupFlags flags); + void * v; + } conversation; + + conversation.f = f; + keySetBinary (key, &conversation.v, sizeof (conversation)); + keySetMeta (key, "callback", ""); +} diff --git a/src/tools/kdb/get.cpp b/src/tools/kdb/get.cpp deleted file mode 100644 index a185b2d6e15..00000000000 --- a/src/tools/kdb/get.cpp +++ /dev/null @@ -1,197 +0,0 @@ -/** - * @file - * - * @brief - * - * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) - */ - -#include - -#include -#include - -#include -#include - -#include - -#include - -using namespace std; -using namespace kdb; - -GetCommand::GetCommand () -{ -} - -namespace -{ - -void printOptions (elektraLookupFlags options) -{ - // :'<,'>s/\(.*\)/^Iif(options \& \1) std::cout << "\1 "; - if (options & ckdb::KDB_O_SPEC) std::cout << "KDB_O_SPEC "; - if (options & ckdb::KDB_O_CREATE) std::cout << "KDB_O_CREATE "; - if (options & ckdb::KDB_O_NOCASCADING) std::cout << "KDB_O_NOCASCADING "; - if (options & ckdb::KDB_O_NOSPEC) std::cout << "KDB_O_NOSPEC "; - if (options & ckdb::KDB_O_NODEFAULT) std::cout << "KDB_O_NODEFAULT "; - if (options & ckdb::KDB_O_CALLBACK) std::cout << "KDB_O_CALLBACK"; -} - - -ckdb::Key * warnOnMeta (ELEKTRA_UNUSED ckdb::KeySet * ks, ELEKTRA_UNUSED ckdb::Key * key, ckdb::Key * found, elektraLookupFlags options) -{ - if (found && !strncmp (keyName (found), "spec:/", 5) && options == ckdb::KDB_O_CALLBACK) - { - const ckdb::Key * meta = keyGetMeta (found, "context"); - if (meta) - { - std::cout << "WARNING " << keyName (found) - << " is context dependent, shown result might be wrong, -v shows you the trace to the key" << std::endl; - } - } - return found; -} - -std::string getCascadingName (std::string name) -{ - if (name[0] == '/') return name; - if (name.find ('/') == std::string::npos) return "/"; - return name.substr (name.find ('/')); -} -} // namespace - -ckdb::Key * printTrace (ELEKTRA_UNUSED ckdb::KeySet * ks, ckdb::Key * key, ckdb::Key * found, elektraLookupFlags options) -{ - warnOnMeta (ks, key, found, options); - - Key k (key); - Key f (found); - - std::string lastKeyName = k.getMeta ("callback/print_trace/last_key_name"); - int depth = k.getMeta ("callback/print_trace/depth"); - - for (int i = 0; i < depth; ++i) - std::cout << " "; - - std::cout << "searching " << (k.getName ()[0] == '/' ? "default of spec" : "") << k.getName () - << ", found: " << (found ? f.getName () : ""); - - if (options) - { - std::cout << ", options: "; - printOptions (options); - } - std::cout << std::endl; - - if (k.getName ().substr (0, 6) == "spec:/" && (options & ckdb::KDB_O_CALLBACK)) - { - depth += 4; - k.setMeta ("callback/print_trace/depth", depth); - } - else - { - if (getCascadingName (lastKeyName) != getCascadingName (k.getName ())) - { - if (depth != 0) - { - depth -= 2; - } - k.setMeta ("callback/print_trace/depth", depth); - } - } - k.setMeta ("callback/print_trace/last_key_name", k.getName ()); - - f.release (); - k.release (); - return found; -} - - -int GetCommand::execute (Cmdline const & cl) -{ - if (cl.arguments.size () != 1) throw invalid_argument ("Need one argument"); - - KeySet conf; - - kdb::Key root = cl.createKey (0); - string parentKeyName = cl.all ? "/" : cl.getParentKey (root).getName (); - kdb::KDB kdb (root); - - std::string originalName = root.getName (); - root.setName (parentKeyName); - kdb.get (conf, root); - root.setName (originalName); - - // do a lookup without tracer to warm up default cache - conf.lookup (root); - - root.setCallback (warnOnMeta); - if (cl.verbose) - { - cout << "got " << conf.size () << " keys" << std::endl; - root.setCallback (printTrace); - } - Key k = conf.lookup (root); - - int ret = 0; - - if (k) - { - if (cl.verbose) - { - if (k.getNamespace () == ElektraNamespace::DEFAULT) - { - cout << "The key was not found in any other namespace, taking the default" << std::endl; - } - cout << "The resulting keyname is " << k.getName () << std::endl; - cout << "The resulting value size is " << k.getStringSize () << std::endl; - } - - if (k.isBinary ()) - { - if (cl.verbose) - { - if (k.getBinarySize () == 0) - { - cout << "The key is null." << std::endl; - } - else - { - cout << "The key is binary." << std::endl; - } - } - cout << std::hex; - const uint8_t * data = static_cast (k.getValue ()); - for (auto position = 0; position < k.getBinarySize (); position++) - { - cout << "\\x" << unsigned (data[position]); - } - cout << std::dec; - } - else - { - cout << k.getString (); - } - } - else - { - cerr << "Did not find key '" << root.getName () << "'"; - ret = 11; - } - - if (!cl.noNewline) - { - cout << endl; - } - - printWarnings (cerr, root, cl.verbose, cl.debug); - printError (cerr, root, cl.verbose, cl.debug); - - return ret; -} - -GetCommand::~GetCommand () -{ -} diff --git a/src/tools/kdb/get.h b/src/tools/kdb/get.h new file mode 100644 index 00000000000..1aecf0a0c2f --- /dev/null +++ b/src/tools/kdb/get.h @@ -0,0 +1,43 @@ +/** + * @file + * + * @brief KDB get subcommand header + * + * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) + */ + +#ifndef ELEKTRA_KDB_GET_H +#define ELEKTRA_KDB_GET_H + +#include + +/** + * Adds options specification of get command to keySet + * + * @param spec the base spec where the commands spec should be added + */ +void addGetSpec (KeySet * spec); + +/** + * Runs the get command + * + * @param options cli options and arguments as specified in addGetSpec() + * @param errorKey key where errors and warnings should be saved + * + * @retval 0 get command ran without errors + * @retval 1 errors occurred, keyGetMeta (errorKey, "error/reason") for info + * @retval -1 key was not found + * + */ +int execGet (KeySet * options, Key * errorKey); + + +// helper functions for the get command +const char * getCascadingName (const char * str); +Key * printTrace (KeySet * ks, Key * key, Key * found, elektraLookupFlags options); +Key * warnOnMeta (KeySet * ks, Key * key, Key * found, elektraLookupFlags options); +void printOptions (elektraLookupFlags options); +void setCallback (Key * key, Key * (*f) (KeySet * ks, Key * key, Key * found, elektraLookupFlags flags)); + + +#endif // ELEKTRA_KDB_GET_H diff --git a/src/tools/kdb/get.hpp b/src/tools/kdb/get.hpp deleted file mode 100644 index 28446339a7f..00000000000 --- a/src/tools/kdb/get.hpp +++ /dev/null @@ -1,49 +0,0 @@ -/** - * @file - * - * @brief - * - * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) - */ - -#ifndef GET_HPP -#define GET_HPP - -#include "coloredkdbio.hpp" -#include -#include - -class GetCommand : public Command -{ -public: - GetCommand (); - ~GetCommand (); - - virtual std::string getShortOptions () override - { - return "an"; - } - - virtual std::string getSynopsis () override - { - return ""; - } - - virtual std::string getShortHelpText () override - { - return "Get the value of an individual key."; - } - - virtual std::string getLongHelpText () override - { - return "When the key starts with / a cascading lookup will be done.\n" - "\n" - "Example:\n" - "\n" - " kdb get system:/elektra/version/constants/KDB_VERSION\n"; - } - - virtual int execute (Cmdline const & cmdline) override; -}; - -#endif diff --git a/src/tools/kdb/ls.c b/src/tools/kdb/ls.c new file mode 100644 index 00000000000..62758c3044b --- /dev/null +++ b/src/tools/kdb/ls.c @@ -0,0 +1,137 @@ +/** + * @file + * + * @brief Implementation of kdb ls command + * + * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) + */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#define COMMAND_NAME "ls" + +#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 addLsSpec (KeySet * spec) +{ + ksAppendKey (spec, keyNew (COMMAND_SPEC_KEY (COMMAND_NAME), KEY_META, "description", + "List keys or keynames below a given name. To also get the value use export.", KEY_META, "command", + COMMAND_NAME, KEY_END)); + ksAppendKey (spec, keyNew (COMMAND_SPEC_KEY (COMMAND_NAME) "/mindepth", KEY_META, "description", "Specify the minimum depth.", + KEY_META, "opt", "m", KEY_META, "opt/long", "min-depth", KEY_META, "opt/arg", "required", KEY_META, + "opt/arg/help", "MIN", KEY_END)); + ksAppendKey (spec, keyNew (COMMAND_SPEC_KEY (COMMAND_NAME) "/maxdepth", KEY_META, "description", "Specify the maximum depth.", + KEY_META, "opt", "M", KEY_META, "opt/long", "max-depth", KEY_META, "opt/arg", "required", KEY_META, + "opt/arg/help", "MAX", KEY_END)); + ksAppendKey (spec, keyNew (COMMAND_SPEC_KEY (COMMAND_NAME) "/nullterm", KEY_META, "description", "Use binary 0 termination.", + KEY_META, "opt", "0", KEY_META, "opt/long", "null", KEY_META, "opt/arg", "none", KEY_END)); + ksAppendKey (spec, keyNew (COMMAND_SPEC_KEY (COMMAND_NAME) "/name", KEY_META, "description", "The name of the key", KEY_META, + "args", "indexed", KEY_META, "args/index", "0", KEY_END)); + + ADD_BASIC_OPTIONS (spec, COMMAND_SPEC_KEY (COMMAND_NAME)) +} + +int execLs (KeySet * options, Key * errorKey) +{ + GET_BASIC_OPTIONS + + tmp = GET_OPTION_KEY (options, "mindepth"); + kdb_long_t minDepth = 0; + if (tmp != NULL) + { + elektraKeyToLong (tmp, &minDepth); + } + + tmp = GET_OPTION_KEY (options, "maxdepth"); + kdb_long_t maxDepth = -1; + if (tmp != NULL) + { + elektraKeyToLong (tmp, &maxDepth); + } + + if (maxDepth != -1 && maxDepth <= minDepth) + { + ELEKTRA_SET_VALIDATION_SEMANTIC_ERROR (errorKey, "the maximum depth has to be larger than the minimum depth"); + return -1; + } + if (maxDepth != -1 && maxDepth < 0) + { + ELEKTRA_SET_VALIDATION_SEMANTIC_ERROR (errorKey, "the maximum depth has to be a positive number"); + return -1; + } + if (minDepth < 0) + { + ELEKTRA_SET_VALIDATION_SEMANTIC_ERROR (errorKey, "the minimum depth has to be a positive number"); + return -1; + } + + bool nullTerm = false; + tmp = GET_OPTION_KEY (options, "nullterm"); + if (tmp != NULL) + { + elektraKeyToBoolean (tmp, &nullTerm); + } + + const char * name = getKeyNameFromOptions (options, GET_OPTION (options, "name"), errorKey, verbose); + if (name == NULL) return 1; + + Key * whereToLook = keyNew (name, KEY_END); + int rootDepth = getKeyNameDepth (name); + + KeySet * searchIn = ksNew (0, KS_END); + + KDB * handle = kdbOpen (NULL, errorKey); + if (kdbGet (handle, searchIn, whereToLook) == -1) + { + ELEKTRA_SET_VALIDATION_SEMANTIC_ERRORF (errorKey, "could not load '%s': %s", name, GET_ERR (whereToLook)); + elektraFree ((void *) name); + kdbClose (handle, errorKey); + ksDel (searchIn); + keyDel (whereToLook); + return -1; + } + kdbClose (handle, errorKey); + + CLI_PRINT (CLI_LOG_VERBOSE, "size of all keys: %zd\n", ksGetSize (searchIn)); + KeySet * part = ksCut (searchIn, whereToLook); + keyDel (whereToLook); + CLI_PRINT (CLI_LOG_VERBOSE, "size of requested keys: %zd\n", ksGetSize (part)); + + Key * cur = NULL; + for (elektraCursor it = 0; it < ksGetSize (part); ++it) + { + cur = ksAtCursor (part, it); + int currentDepth = getKeyNameDepth (keyName (cur)); + if ((maxDepth == -1 || currentDepth < rootDepth + maxDepth) && currentDepth >= rootDepth + minDepth) + { + CLI_PRINT (CLI_LOG_NONE, "%s%c", keyName (cur), nullTerm ? '\0' : '\n'); + } + } + + elektraFree ((void *) name); + ksDel (part); + ksDel (searchIn); + return 0; +} + +int getKeyNameDepth (const char * name) +{ + int nameLen = strlen (name); + int slashCount = 0; + + for (int i = 0; i < nameLen; i++) + { + slashCount += name[i] == '/' && (i == 0 || name[i - 1] != '\\'); + } + + return slashCount; +} diff --git a/src/tools/kdb/ls.cpp b/src/tools/kdb/ls.cpp deleted file mode 100644 index b8d6651b315..00000000000 --- a/src/tools/kdb/ls.cpp +++ /dev/null @@ -1,122 +0,0 @@ -/** - * @file - * - * @brief - * - * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) - */ - -#include - -#include -#include - -#include -#include -#include - -using namespace kdb; -using namespace std; - -LsCommand::LsCommand () : kdb (root) -{ -} - -int LsCommand::execute (Cmdline const & cl) -{ - checkArguments (cl); - - printWarnings (cerr, root, cl.verbose, cl.debug); - - root = cl.createKey (0); - - kdb.get (ks, root); - - if (cl.verbose) cout << "size of all keys in mount point: " << ks.size () << endl; - - KeySet part (ks.cut (root)); - - if (cl.verbose) cout << "size of requested keys: " << part.size () << endl; - cout.setf (std::ios_base::unitbuf); - if (cl.null) - { - cout.unsetf (std::ios_base::skipws); - } - - printResults (part, getDepth (root), cl); - - printWarnings (cerr, root, cl.verbose, cl.debug); - - return 0; -} - -void LsCommand::checkArguments (Cmdline const & cl) -{ - if (cl.arguments.size () != 1) - { - throw invalid_argument ("1 argument required"); - } - if (cl.maxDepth <= cl.minDepth) - { - throw invalid_argument ("the maximum depth has to be larger than the minimum depth"); - } - if (cl.maxDepth < 0) - { - throw invalid_argument ("the maximum depth has to be a positive number"); - } - if (cl.minDepth < 0) - { - throw invalid_argument ("the minimum depth has to be a positive number"); - } -} - -void LsCommand::printResults (KeySet const & part, const int rootDepth, Cmdline const & cl) -{ - const int offset = root.getBaseName ().empty () || shallShowNextLevel (cl.arguments[0]) ? 1 : 0; - const int relativeMinDepth = rootDepth + cl.minDepth + offset; - const int relativeMaxDepth = - std::max (cl.maxDepth, rootDepth > INT_MAX - cl.maxDepth - offset ? INT_MAX : rootDepth + cl.maxDepth + offset); - if (cl.debug) - { - cout << "The root depth is " << rootDepth << ", the relative minimum depth is " << relativeMinDepth - << " and the relative maximum depth is " << relativeMaxDepth << endl; - } - - for (const auto & it : part) - { - const int depth = getDepth (it); - if ((depth >= relativeMinDepth && depth < relativeMaxDepth) || cl.debug) - { - cout << it; - if (cl.debug) - { - cout << " " << depth; - } - - if (cl.null) - { - cout << '\0' << std::flush; - } - else - { - cout << endl; - } - } - } -} - -int LsCommand::getDepth (Key const & key) -{ - return std::distance (key.begin (), key.end ()); -} - -bool LsCommand::shallShowNextLevel (const string argument) -{ - auto it = argument.rbegin (); - // If the argument ends in / its an indicator to complete the next level (like done by shells), but not if its escaped - return it != argument.rend () && (*it) == '/' && ((++it) == argument.rend () || (*it) != '\\'); -} - -LsCommand::~LsCommand () -{ -} diff --git a/src/tools/kdb/ls.h b/src/tools/kdb/ls.h new file mode 100644 index 00000000000..35d56ec4d6a --- /dev/null +++ b/src/tools/kdb/ls.h @@ -0,0 +1,36 @@ +/** + * @file + * + * @brief Header for ls command + * + * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) + */ + +#ifndef ELEKTRA_KDB_LS_H +#define ELEKTRA_KDB_LS_H + +#include + +/** + * Adds options specification of ls command to @spec + * + * @param spec the base spec where the commands spec should be added + */ +void addLsSpec (KeySet * spec); + +/** + * Executes the ls command + * + * @param options cli options and arguments as specified in addLsSpec() + * @param errorKey key where errors and warnings should be saved + * + * @retval 0 ls command ran without errors + * @retval 1 errors occurred, keyGetMeta (errorKey, "error/reason") for info + * + */ +int execLs (KeySet * options, Key * errorKey); + +// helper functions +int getKeyNameDepth (const char * name); + +#endif // ELEKTRA_KDB_LS_H diff --git a/src/tools/kdb/ls.hpp b/src/tools/kdb/ls.hpp deleted file mode 100644 index c6aca5cb8d8..00000000000 --- a/src/tools/kdb/ls.hpp +++ /dev/null @@ -1,57 +0,0 @@ -/** - * @file - * - * @brief - * - * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) - */ - -#ifndef LS_H -#define LS_H - -#include "coloredkdbio.hpp" -#include -#include - -class LsCommand : public Command -{ - kdb::Key root; - kdb::KDB kdb; - kdb::KeySet ks; - -public: - LsCommand (); - ~LsCommand (); - - virtual std::string getShortOptions () override - { - return "mM0"; - } - - virtual std::string getSynopsis () override - { - return ""; - } - - virtual std::string getShortHelpText () override - { - return "List the names of keys below a given name."; - } - - virtual std::string getLongHelpText () override - { - return "List all keys below given name.\n" - "To also retrieve the value use the\n" - "export command."; - } - - virtual int execute (Cmdline const & cmdline) override; - -private: - void checkArguments (Cmdline const & cl); - void printResults (kdb::KeySet const & part, const int rootDepth, Cmdline const & cl); - int getDepth (kdb::Key const & key); - bool shallShowNextLevel (const std::string argument); -}; - -#endif diff --git a/src/tools/kdb/main.c b/src/tools/kdb/main.c new file mode 100644 index 00000000000..13313575cb9 --- /dev/null +++ b/src/tools/kdb/main.c @@ -0,0 +1,158 @@ +/** + * @file + * + * @brief The KDB cli tool + * + * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define CLI_SPEC_KEY "spec:" CLI_BASE_KEY + +extern char ** environ; + +command subcommands[] = { + { "basename", addBasenameSpec, execBasename }, + { "cmerge", addCmergeSpec, execCmerge }, + { "cp", addCpSpec, execCp }, + { "dirname", addDirnameSpec, execDirname }, + { "find", addFindSpec, execFind }, + { "get", addGetSpec, execGet }, + { "ls", addLsSpec, execLs }, + { "meta", addMetaSpec, execMeta }, + { "mountpoint", addMountpointSpec, execMountpoint }, + { "mv", addMvSpec, execMv }, + { "namespace", addNamespaceSpec, execNamespace }, + { "rm", addRmSpec, execRm }, + { "set", addSetSpec, execSet }, +}; + +void printWarnings (Key * errorKey) +{ + const Key * warningsKey = keyGetMeta (errorKey, "warnings"); + if (warningsKey == NULL) + { + return; + } + const char * warnings = keyString (warningsKey); + warnings = warnings[1] == '_' ? warnings + 2 : warnings + 1; + + int warningsCount = atoi (warnings); + char buff[8 + 1 + 1 + 11 + 1 + 6 + 1]; + for (int i = 0; i <= warningsCount; ++i) + { + snprintf (buff, sizeof buff, "warnings/#%d/reason", i); + const char * warning = keyString (keyGetMeta (errorKey, buff)); + printf ("WARNING: %s\n", warning); + } +} + +int main (int argc, char ** argv) +{ + if (argc == 2 && elektraStrCmp (argv[1], "--elektra-spec") == 0) + { + Key * parentKey = keyNew (CLI_SPEC_KEY, KEY_END); + KeySet * options = ksNew (1, + keyNew (CLI_SPEC_KEY, KEY_META, "command", "", KEY_META, "description", + "kdb is a program to manage Elektra's key database.", KEY_END), + KS_END); + + for (unsigned long i = 0; i < sizeof (subcommands) / sizeof (subcommands[0]); ++i) + { + subcommands[i].addSpec (options); + } + + KeySet * specloadConf = ksNew (1, keyNew ("system:/sendspec", KEY_END), KS_END); + ElektraInvokeHandle * specload = elektraInvokeOpen ("specload", specloadConf, parentKey); + int result = elektraInvoke2Args (specload, "sendspec", options, parentKey); + + elektraInvokeClose (specload, parentKey); + keyDel (parentKey); + ksDel (specloadConf); + return result; + } + + Key * parentKey = keyNew (CLI_BASE_KEY, KEY_END); + KeySet * options = ksNew (0, KS_END); + + KeySet * goptsConfig = ksNew (0, KS_END); + KeySet * contract = ksNew (0, KS_END); + + elektraGOptsContract (contract, argc, (const char * const *) argv, (const char * const *) environ, parentKey, goptsConfig); + + KDB * kdb = kdbOpen (contract, parentKey); + + ksDel (goptsConfig); + ksDel (contract); + + int result = kdbGet (kdb, options, parentKey); + + const char * subcommand = keyString (ksLookupByName (options, CLI_BASE_KEY, 0)); + if (elektraStrCmp (subcommand, "") == 0) + { + printf ("kdb is a program to manage Elektra's key database. For more information use --help.\n\n"); + result = EXIT_SUCCESS; + goto cleanup; + } + + Key * helpKey = ksLookupByName (options, "proc:/elektra/gopts/help", 0); + if (helpKey != NULL && elektraStrCmp (keyString (helpKey), "1") == 0) + { + const char * help = keyString (ksLookupByName (options, "proc:/elektra/gopts/help/message", 0)); + printf ("%s\n", help); + result = EXIT_SUCCESS; + goto cleanup; + } + + if (result == -1 || HAS_ERR (parentKey)) + { + fprintf (stderr, "ERROR: %s\n", GET_ERR (parentKey)); + result = EXIT_FAILURE; + goto cleanup; + } + + for (unsigned long i = 0; i < sizeof (subcommands) / sizeof (subcommands[0]); ++i) + { + if (elektraStrCmp (subcommand, subcommands[i].name) == 0) + { + keyDel (parentKey); + parentKey = keyNew (CLI_BASE_KEY, KEY_END); + result = subcommands[i].exec (options, parentKey); + if (HAS_ERR (parentKey)) + { + fprintf (stderr, "ERROR: %s\n", GET_ERR (parentKey)); + } + printWarnings (parentKey); + goto cleanup; + } + } + fprintf (stderr, "ERROR: \'help\' is not a valid subcommand, use --help for more information.\n"); + + +cleanup: + kdbClose (kdb, parentKey); + keyDel (parentKey); + ksDel (options); + return result; +} diff --git a/src/tools/kdb/main.cpp b/src/tools/kdb/main.cpp deleted file mode 100644 index 28f93bad537..00000000000 --- a/src/tools/kdb/main.cpp +++ /dev/null @@ -1,261 +0,0 @@ -/** - * @file - * - * @brief - * - * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) - */ - -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include - -#include -#include - -using namespace kdb; -using namespace std; - -int displayHelp (std::string app, Factory const & f) -{ - std::cout << "Usage: " << app << " [args]\n" << std::endl; - std::cout << app << " is a program to manage Elektra's key database.\n" - << "Please run a command with -H or --help as args to show a help text for\n" - << "a specific command.\n" - << std::endl; - std::cout << "Known commands are:" << std::endl; - std::vector commands; - try - { - commands = f.getPrettyCommands (); - } - catch (kdb::KDBException const & ce) - { - std::cerr << "Sorry, I have a severe problem, it seems like I am not installed correctly!\n" - << "kdbOpen() failed with the info:" << std::endl - << ce.what () << std::endl - << "Please report the issue at https://issues.libelektra.org/"; - return 8; - } - for (auto & command : commands) - { - std::cout << command << std::endl; - } - return 0; -} - -void displayVersion () -{ - kdb::KDB kdb; - kdb::KeySet versions; - kdb::Key k ("system:/elektra/version", KEY_END); - kdb.get (versions, k); - kdb::Key kdb_version = versions.lookup ("system:/elektra/version/constants/KDB_VERSION"); - if (!kdb_version) - { - cerr << "Could not lookup KDB_VERSION key" << endl; - } - else - { - cout << "KDB_VERSION: " << kdb_version.getString () << endl; - } - kdb::Key so_version = versions.lookup ("system:/elektra/version/constants/SO_VERSION"); - if (!so_version) - { - cerr << "Could not lookup SO_VERSION key" << endl; - } - else - { - cout << "SO_VERSION: " << so_version.getString () << endl; - } -} - -void printSignal (int signum) -{ - switch (signum) - { - case SIGILL: - cerr << "SIGILL"; - break; - case SIGABRT: - cerr << "SIGABRT"; - break; - case SIGFPE: - cerr << "SIGFPE"; - break; - case SIGSEGV: - cerr << "SIGSEGV"; - break; - } -} - - -void catchSignal (int signum) -{ - cerr << endl << "Sorry, I crashed by the signal "; - printSignal (signum); - cerr << endl << "This should not have happened!" << endl; - cerr << endl << "Please report the issue at https://issues.libelektra.org/" << std::endl; - signal (SIGABRT, SIG_DFL); - abort (); -} - -void setupSignal (int signum) -{ - if (signal (signum, catchSignal) == SIG_ERR) - { - cerr << "Sorry, I could not setup signal "; - printSignal (signum); - cerr << " because: " << strerror (errno) << std::endl; - cerr << "Please report the issue at https://issues.libelektra.org/" << std::endl; - } -} - -void setupSignals () -{ - setupSignal (SIGILL); - setupSignal (SIGABRT); - setupSignal (SIGFPE); - setupSignal (SIGSEGV); -} - -int main (int argc, char ** argv) -{ - setupSignals (); - Factory f; - - if (argc < 2) - { - return displayHelp (argv[0], f); - } - - string command = argv[1]; - if (command == "help" || command == "-H" || command == "--help") - { - if (argc >= 3) - { - runManPage (argv[2]); - } - else - { - runManPage (); - } - - return displayHelp (argv[0], f); - } - - if (command == "-V" || command == "--version") - { - displayVersion (); - return 0; - } - - bool printVerbose = false; - bool printDebug = false; - try - { - std::vector origArguments (argv + 1, argv + argc); - origArguments.push_back (0); - CommandPtr cmd = f.get (command); - Cmdline cl (argc, argv, cmd.get ()); - printVerbose = cl.verbose; - printDebug = cl.debug; - - if (cl.help) - { - runManPage (command, cl.profile); - // does not return, but may throw - } - - // version and invalidOpt might be implemented - // differently for external command - if (dynamic_cast (cmd.get ())) - { - tryExternalCommand (&origArguments[0]); - // does not return, but may throw - } - - if (cl.version) - { - displayVersion (); - return 0; - } - - if (cl.invalidOpt) - { - cerr << cl << endl; - return 1; - } - - try - { - return cmd->execute (cl); - } - catch (std::invalid_argument const & ia) - { - cerr << "Sorry, I could not process the arguments: " << ia.what () << endl << endl; - cerr << cl << endl; - return 2; - } - } - catch (CommandException const & ce) - { - std::cerr << "The command " << getErrorColor (ANSI_COLOR::BOLD) << argv[0] << " " << command - << getErrorColor (ANSI_COLOR::RESET) << " terminated " << getErrorColor (ANSI_COLOR::RED) << "unsuccessfully" - << getErrorColor (ANSI_COLOR::RESET) << " with the info:\n" - << ce.what () << std::endl; - if (ce.errorCode () != 3 && (ce.errorCode () < 11 || ce.errorCode () > 20)) - { - std::cerr << "Command used invalid return value (" << ce.errorCode () - << "), please report the issue at https://issues.libelektra.org/" << std::endl; - return 3; - } - return ce.errorCode (); - } - catch (UnknownCommand const & uc) - { - std::cerr << "The command " << getErrorColor (ANSI_COLOR::BOLD) << argv[0] << " " << command - << getErrorColor (ANSI_COLOR::RESET) << " is " << getErrorColor (ANSI_COLOR::RED) << "not known" - << getErrorColor (ANSI_COLOR::RESET) << std::endl; - displayHelp (argv[0], f); - return 4; - } - catch (kdb::KDBMountException const & ce) - { - std::cerr << ce.what () << std::endl; - return 5; - } - catch (kdb::KDBException const & ce) - { - std::cerr << ce.whatWithArguments (printVerbose, printDebug) << std::endl; - return 5; - } - catch (std::exception const & ce) - { - std::cerr << "The command " << getErrorColor (ANSI_COLOR::BOLD) << argv[0] << " " << command - << getErrorColor (ANSI_COLOR::RESET) << " terminated " << getErrorColor (ANSI_COLOR::RED) << "unsuccessfully" - << getErrorColor (ANSI_COLOR::RESET) << " with the info:" << endl - << ce.what () << endl - << "Please report the issue at https://issues.libelektra.org/" << std::endl; - return 7; - } - catch (...) - { - std::cerr << "The command " << getErrorColor (ANSI_COLOR::BOLD) << argv[0] << " " << command - << getErrorColor (ANSI_COLOR::RESET) << " terminated with an " << getErrorColor (ANSI_COLOR::RED) - << "unknown error" << getErrorColor (ANSI_COLOR::RESET) << endl - << "Please report the issue at https://issues.libelektra.org/" << std::endl; - displayHelp (argv[0], f); - return 7; - } -} diff --git a/src/tools/kdb/meta-get.c b/src/tools/kdb/meta-get.c new file mode 100644 index 00000000000..c851b8c9cde --- /dev/null +++ b/src/tools/kdb/meta-get.c @@ -0,0 +1,75 @@ +/** + * @file + * + * @brief Implementation of kdb meta-get command + * + * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) + */ + +#include + +#include +#include +#include +#include +#include +#include + +#define COMMAND_NAME "meta/get" + +#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 addMetaGetSpec (KeySet * spec) +{ + ksAppendKey (spec, keyNew (COMMAND_SPEC_KEY (COMMAND_NAME), KEY_META, "description", "Get specific metadata from a key.", KEY_META, + "command", "get", KEY_END)); + ksAppendKey (spec, keyNew (COMMAND_SPEC_KEY (COMMAND_NAME) "/keyname", KEY_META, "description", "The name of the key", KEY_META, + "args", "indexed", KEY_META, "args/index", "0", KEY_END)); + ksAppendKey (spec, keyNew (COMMAND_SPEC_KEY (COMMAND_NAME) "/metaname", KEY_META, "description", "The meta name", KEY_META, "args", + "indexed", KEY_META, "args/index", "1", KEY_END)); + + ADD_BASIC_OPTIONS (spec, COMMAND_SPEC_KEY (COMMAND_NAME)) +} + +int execMetaGet (KeySet * options, Key * errorKey) +{ + int ret = 0; + GET_BASIC_OPTIONS + + const char * name = getKeyNameFromOptions (options, GET_OPTION (options, "keyname"), errorKey, verbose); + if (name == NULL) return 1; + + const char * metaName = GET_OPTION (options, "metaname"); + + Key * toLookUp = keyNew (name, KEY_END); + KeySet * conf = ksNew (0, KS_END); + KDB * handle = kdbOpen (NULL, errorKey); + + if (kdbGet (handle, conf, toLookUp) == -1) + { + ELEKTRA_SET_VALIDATION_SEMANTIC_ERRORF (errorKey, "could not load '%s': %s", name, GET_ERR (toLookUp)); + ret = 1; + goto cleanup; + } + + Key * key = ksLookup (conf, toLookUp, KDB_O_NONE); + + const Key * metaKey = keyGetMeta (key, metaName); + if (metaKey == NULL) + { + ELEKTRA_SET_RESOURCE_ERRORF (errorKey, "key '%s' does not contain a meta-key with the name '%s'", name, metaName); + ret = 1; + } + else + { + CLI_PRINT (CLI_LOG_NONE, "%s %s", keyString (metaKey), noNewLine ? "" : "\n"); + } + +cleanup: + kdbClose (handle, errorKey); + ksDel (conf); + keyDel (toLookUp); + elektraFree ((void *) name); + return ret; +} diff --git a/src/tools/kdb/meta-get.h b/src/tools/kdb/meta-get.h new file mode 100644 index 00000000000..9048385f289 --- /dev/null +++ b/src/tools/kdb/meta-get.h @@ -0,0 +1,33 @@ +/** + * @file + * + * @brief Header for meta-get command + * + * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) + */ + +#ifndef ELEKTRA_KDB_META_GET_H +#define ELEKTRA_KDB_META_GET_H + +#include + +/** + * Adds options specification of meta-get command to @spec + * + * @param spec the base spec where the commands spec should be added + */ +void addMetaGetSpec (KeySet * spec); + +/** + * Executes the meta-get command + * + * @param options cli options and arguments as specified in @addMetaGetSpec() + * @param errorKey key where errors and warnings should be saved + * + * @retval 0 get command ran without errors + * @retval 1 errors occurred, keyGetMeta (errorKey, "error/reason") for info + * + */ +int execMetaGet (KeySet * options, Key * errorKey); + +#endif // ELEKTRA_KDB_META_GET_H diff --git a/src/tools/kdb/meta-ls.c b/src/tools/kdb/meta-ls.c new file mode 100644 index 00000000000..6b28a2a15dc --- /dev/null +++ b/src/tools/kdb/meta-ls.c @@ -0,0 +1,76 @@ +/** + * @file + * + * @brief Implementation of kdb meta-ls command + * + * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#define COMMAND_NAME "meta/ls" + +#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 addMetaLsSpec (KeySet * spec) +{ + ksAppendKey (spec, keyNew (COMMAND_SPEC_KEY (COMMAND_NAME), KEY_META, "description", "List available metadata names for a key.", + KEY_META, "command", "ls", KEY_END)); + ksAppendKey (spec, keyNew (COMMAND_SPEC_KEY (COMMAND_NAME) "/keyname", KEY_META, "description", "The name of the key", KEY_META, + "args", "indexed", KEY_META, "args/index", "0", KEY_END)); + + ADD_BASIC_OPTIONS (spec, COMMAND_SPEC_KEY (COMMAND_NAME)) +} + +int execMetaLs (KeySet * options, Key * errorKey) +{ + int ret = 0; + GET_BASIC_OPTIONS + + bool nullTerm = false; + tmp = GET_OPTION_KEY (options, "nullterm"); + if (tmp != NULL) + { + elektraKeyToBoolean (tmp, &nullTerm); + } + + const char * name = getKeyNameFromOptions (options, GET_OPTION (options, "keyname"), errorKey, verbose); + if (name == NULL) return 1; + + Key * toLookUp = keyNew (name, KEY_END); + KeySet * conf = ksNew (0, KS_END); + KDB * handle = kdbOpen (NULL, errorKey); + + if (kdbGet (handle, conf, toLookUp) == -1) + { + ELEKTRA_SET_VALIDATION_SEMANTIC_ERRORF (errorKey, "could not load '%s': %s", name, GET_ERR (toLookUp)); + ret = 1; + goto cleanup; + } + + Key * key = ksLookup (conf, toLookUp, KDB_O_NONE); + KeySet * metaKeys = keyMeta (key); + + Key * cur = NULL; + for (elektraCursor it = 0; it < ksGetSize (metaKeys); ++it) + { + cur = ksAtCursor (metaKeys, it); + CLI_PRINT (CLI_LOG_NONE, "%s%c", keyName (cur), nullTerm ? '\0' : '\n'); + } + +cleanup: + kdbClose (handle, errorKey); + ksDel (conf); + keyDel (toLookUp); + elektraFree ((void *) name); + return ret; +} diff --git a/src/tools/kdb/meta-ls.h b/src/tools/kdb/meta-ls.h new file mode 100644 index 00000000000..21600a4c423 --- /dev/null +++ b/src/tools/kdb/meta-ls.h @@ -0,0 +1,33 @@ +/** + * @file + * + * @brief Header for meta-ls command + * + * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) + */ + +#ifndef ELEKTRA_KDB_META_LS_H +#define ELEKTRA_KDB_META_LS_H + +#include + +/** + * Adds options specification of meta-ls command to @spec + * + * @param spec the base spec where the commands spec should be added + */ +void addMetaLsSpec (KeySet * spec); + +/** + * Executes the meta-ls command + * + * @param options cli options and arguments as specified in @addMetaLsSpec() + * @param errorKey key where errors and warnings should be saved + * + * @retval 0 ls command ran without errors + * @retval 1 errors occurred, keyGetMeta (errorKey, "error/reason") for info + * + */ +int execMetaLs (KeySet * options, Key * errorKey); + +#endif // ELEKTRA_KDB_META_LS_H diff --git a/src/tools/kdb/meta-rm.c b/src/tools/kdb/meta-rm.c new file mode 100644 index 00000000000..58fb3ac023e --- /dev/null +++ b/src/tools/kdb/meta-rm.c @@ -0,0 +1,85 @@ +/** + * @file + * + * @brief Implementation of kdb meta-rm command + * + * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#define COMMAND_NAME "meta/rm" + +#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 addMetaRmSpec (KeySet * spec) +{ + ksAppendKey (spec, keyNew (COMMAND_SPEC_KEY (COMMAND_NAME), KEY_META, "description", "Remove a metakey.", KEY_META, "command", "rm", + KEY_END)); + ksAppendKey (spec, keyNew (COMMAND_SPEC_KEY (COMMAND_NAME) "/keyname", KEY_META, "description", "The name of the key", KEY_META, + "args", "indexed", KEY_META, "args/index", "0", KEY_END)); + ksAppendKey (spec, keyNew (COMMAND_SPEC_KEY (COMMAND_NAME) "/metaname", KEY_META, "description", "The meta name", KEY_META, "args", + "indexed", KEY_META, "args/index", "1", KEY_END)); + + ADD_BASIC_OPTIONS (spec, COMMAND_SPEC_KEY (COMMAND_NAME)) +} + +int execMetaRm (KeySet * options, Key * errorKey) +{ + int ret = 0; + GET_BASIC_OPTIONS + + const char * keyName = getKeyNameFromOptions (options, GET_OPTION (options, "keyname"), errorKey, verbose); + if (keyName == NULL) return 1; + + const char * metaName = GET_OPTION (options, "metaname"); + + Key * parentKey = keyNew (keyName, KEY_END); + + KeySet * conf = ksNew (0, KS_END); + KDB * handle = kdbOpen (NULL, errorKey); + + if (kdbGet (handle, conf, parentKey) == -1) + { + ELEKTRA_SET_VALIDATION_SEMANTIC_ERRORF (errorKey, "could not load '%s': %s", keyName, GET_ERR (parentKey)); + ret = 1; + goto cleanup; + } + + Key * key = ksLookup (conf, parentKey, KDB_O_NONE); + if (key == NULL) + { + ELEKTRA_SET_VALIDATION_SEMANTIC_ERRORF (errorKey, "could not load '%s': %s", keyName, GET_ERR (parentKey)); + ret = 1; + goto cleanup; + } + + if (keySetMeta (key, metaName, NULL) != 0 || kdbSet (handle, conf, parentKey) == -1) + { + ret = 1; + ELEKTRA_SET_VALIDATION_SEMANTIC_ERRORF (errorKey, "could not remove meta-key '%s' for '%s': %s", metaName, keyName, + GET_ERR (parentKey)); + } + + keyDel (key); + +cleanup: + if (!noNewLine) + { + printf ("\n"); + } + elektraFree ((void *) keyName); + keyDel (parentKey); + ksDel (conf); + kdbClose (handle, errorKey); + return ret; +} diff --git a/src/tools/kdb/meta-rm.h b/src/tools/kdb/meta-rm.h new file mode 100644 index 00000000000..065a3ea2a65 --- /dev/null +++ b/src/tools/kdb/meta-rm.h @@ -0,0 +1,33 @@ +/** + * @file + * + * @brief Header for meta-rm command + * + * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) + */ + +#ifndef ELEKTRA_KDB_META_RM_H +#define ELEKTRA_KDB_META_RM_H + +#include + +/** + * Adds options specification of meta-rm command to @spec + * + * @param spec the base spec where the commands spec should be added + */ +void addMetaRmSpec (KeySet * spec); + +/** + * Executes the meta-rm command + * + * @param options cli options and arguments as specified in @addMetaRmSpec() + * @param errorKey key where errors and warnings should be saved + * + * @retval 0 rm command ran without errors + * @retval 1 errors occurred, keyGetMeta (errorKey, "error/reason") for info + * + */ +int execMetaRm (KeySet * options, Key * errorKey); + +#endif // ELEKTRA_KDB_META_RM_H diff --git a/src/tools/kdb/meta-set.c b/src/tools/kdb/meta-set.c new file mode 100644 index 00000000000..a35c7292411 --- /dev/null +++ b/src/tools/kdb/meta-set.c @@ -0,0 +1,107 @@ +/** + * @file + * + * @brief Implementation of kdb meta-set command + * + * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#define COMMAND_NAME "meta/set" + +#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 addMetaSetSpec (KeySet * spec) +{ + ksAppendKey (spec, keyNew (COMMAND_SPEC_KEY (COMMAND_NAME), KEY_META, "description", "Set a metavalue.", KEY_META, "command", "set", + KEY_END)); + + ksAppendKey (spec, keyNew (COMMAND_SPEC_KEY (COMMAND_NAME) "/keyname", KEY_META, "description", "The name of the key", KEY_META, + "args", "indexed", KEY_META, "args/index", "0", KEY_END)); + ksAppendKey (spec, keyNew (COMMAND_SPEC_KEY (COMMAND_NAME) "/metaname", KEY_META, "description", "The meta name", KEY_META, "args", + "indexed", KEY_META, "args/index", "1", KEY_END)); + ksAppendKey (spec, keyNew (COMMAND_SPEC_KEY (COMMAND_NAME) "/metavalue", KEY_META, "description", "The value that should be set", + KEY_META, "args", "indexed", KEY_META, "args/index", "2", KEY_END)); + + ADD_BASIC_OPTIONS (spec, COMMAND_SPEC_KEY (COMMAND_NAME)) +} + +int execMetaSet (KeySet * options, Key * errorKey) +{ + int ret = 0; + GET_BASIC_OPTIONS + + bool force = false; + tmp = GET_OPTION_KEY (options, "force"); + if (tmp != NULL) + { + elektraKeyToBoolean (GET_OPTION_KEY (options, "force"), &force); + } + + const char * keyName = getKeyNameFromOptions (options, GET_OPTION (options, "keyname"), errorKey, verbose); + if (keyName == NULL) return 1; + + const char * metaName = GET_OPTION (options, "metaname"); + const char * metaValue = GET_OPTION (options, "metavalue"); + + Key * parentKey = keyNew (keyName, KEY_END); + + if (keyGetNamespace (parentKey) == KEY_NS_NONE || keyGetNamespace (parentKey) == KEY_NS_CASCADING) + { + ret = 1; + ELEKTRA_SET_VALIDATION_SYNTACTIC_ERROR (errorKey, "key does not specify a namespace"); + elektraFree ((void *) keyName); + keyDel (parentKey); + return ret; + } + + keySetNamespace (parentKey, KEY_NS_CASCADING); + KeySet * conf = ksNew (0, KS_END); + KDB * handle = kdbOpen (NULL, errorKey); + + if (kdbGet (handle, conf, parentKey) == -1) + { + ELEKTRA_SET_VALIDATION_SEMANTIC_ERRORF (errorKey, "could not load '%s': %s", keyName, GET_ERR (parentKey)); + ret = 1; + goto cleanup; + } + + Key * key = ksLookup (conf, parentKey, KDB_O_NONE); + if (key == NULL) + { + ELEKTRA_SET_VALIDATION_SEMANTIC_ERRORF (errorKey, "could not load '%s': %s", keyName, GET_ERR (parentKey)); + ret = 1; + goto cleanup; + } + keySetMeta (key, metaName, metaValue); + + if (kdbSet (handle, conf, parentKey) == -1) + { + ret = 1; + ELEKTRA_SET_VALIDATION_SEMANTIC_ERRORF (errorKey, "could not set meta-value '%s' for '%s': %s", metaName, keyName, + GET_ERR (parentKey)); + } + + keyDel (key); + +cleanup: + if (!noNewLine) + { + printf ("\n"); + } + elektraFree ((void *) keyName); + keyDel (parentKey); + ksDel (conf); + kdbClose (handle, errorKey); + return ret; +} diff --git a/src/tools/kdb/meta-set.h b/src/tools/kdb/meta-set.h new file mode 100644 index 00000000000..605dd761591 --- /dev/null +++ b/src/tools/kdb/meta-set.h @@ -0,0 +1,33 @@ +/** + * @file + * + * @brief Header for meta-set command + * + * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) + */ + +#ifndef ELEKTRA_KDB_META_SET_H +#define ELEKTRA_KDB_META_SET_H + +#include + +/** + * Adds options specification of meta-set command to @spec + * + * @param spec the base spec where the commands spec should be added + */ +void addMetaSetSpec (KeySet * spec); + +/** + * Executes the meta-set command + * + * @param options cli options and arguments as specified in @addMetaSetSpec() + * @param errorKey key where errors and warnings should be saved + * + * @retval 0 set command ran without errors + * @retval 1 errors occurred, keyGetMeta (errorKey, "error/reason") for info + * + */ +int execMetaSet (KeySet * options, Key * errorKey); + +#endif // ELEKTRA_KDB_META_SET_H diff --git a/src/tools/kdb/meta-show.c b/src/tools/kdb/meta-show.c new file mode 100644 index 00000000000..f4bbdaac5d4 --- /dev/null +++ b/src/tools/kdb/meta-show.c @@ -0,0 +1,75 @@ +/** + * @file + * + * @brief Implementation of kdb meta-show command + * + * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) + */ + +#include + +#include +#include +#include +#include +#include + +#define COMMAND_NAME "meta/show" + +#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 addMetaShowSpec (KeySet * spec) +{ + ksAppendKey (spec, keyNew (COMMAND_SPEC_KEY (COMMAND_NAME), KEY_META, "description", + "Print all metakeys with their value for a key.", KEY_META, "command", "show", KEY_END)); + ksAppendKey (spec, keyNew (COMMAND_SPEC_KEY (COMMAND_NAME) "/keyname", KEY_META, "description", "The name of the key", KEY_META, + "args", "indexed", KEY_META, "args/index", "0", KEY_END)); + ADD_BASIC_OPTIONS (spec, COMMAND_SPEC_KEY (COMMAND_NAME)) +} + +int execMetaShow (KeySet * options, Key * errorKey) +{ + int ret = 0; + GET_BASIC_OPTIONS + + bool nullTerm = false; + tmp = GET_OPTION_KEY (options, "nullterm"); + if (tmp != NULL) + { + elektraKeyToBoolean (tmp, &nullTerm); + } + + const char * name = getKeyNameFromOptions (options, GET_OPTION (options, "keyname"), errorKey, verbose); + if (name == NULL) return 1; + + Key * toLookUp = keyNew (name, KEY_END); + KeySet * conf = ksNew (0, KS_END); + KDB * handle = kdbOpen (NULL, errorKey); + + if (kdbGet (handle, conf, toLookUp) == -1) + { + ELEKTRA_SET_VALIDATION_SEMANTIC_ERRORF (errorKey, "could not load '%s': %s", name, GET_ERR (toLookUp)); + ret = 1; + goto cleanup; + } + + Key * key = ksLookup (conf, toLookUp, KDB_O_NONE); + KeySet * metaKeys = keyMeta (key); + + Key * cur = NULL; + for (elektraCursor it = 0; it < ksGetSize (metaKeys); ++it) + { + cur = ksAtCursor (metaKeys, it); + CLI_PRINT (CLI_LOG_NONE, "%s -> %s%c", keyName (cur), BOLD (keyString (cur)), nullTerm ? '\0' : '\n'); + } + + +cleanup: + kdbClose (handle, errorKey); + ksDel (conf); + keyDel (toLookUp); + elektraFree (fmtBuffer); + elektraFree ((void *) name); + return ret; +} diff --git a/src/tools/kdb/meta-show.h b/src/tools/kdb/meta-show.h new file mode 100644 index 00000000000..98f11bd50f6 --- /dev/null +++ b/src/tools/kdb/meta-show.h @@ -0,0 +1,33 @@ +/** + * @file + * + * @brief Header for meta-show command + * + * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) + */ + +#ifndef ELEKTRA_KDB_META_SHOW_H +#define ELEKTRA_KDB_META_SHOW_H + +#include + +/** + * Adds options specification of meta-show command to @spec + * + * @param spec the base spec where the commands spec should be added + */ +void addMetaShowSpec (KeySet * spec); + +/** + * Executes the meta-show command + * + * @param options cli options and arguments as specified in @addMetaShowSpec() + * @param errorKey key where errors and warnings should be saved + * + * @retval 0 show command ran without errors + * @retval 1 errors occurred, keyGetMeta (errorKey, "error/reason") for info + * + */ +int execMetaShow (KeySet * options, Key * errorKey); + +#endif // ELEKTRA_KDB_META_SHOW_H diff --git a/src/tools/kdb/meta.c b/src/tools/kdb/meta.c new file mode 100644 index 00000000000..480003908ef --- /dev/null +++ b/src/tools/kdb/meta.c @@ -0,0 +1,54 @@ +/** + * @file + * + * @brief Implementation of kdb meta command + * + * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define COMMAND_NAME "meta" + +#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) + +command metaSubcommands[] = { + { "get", addMetaGetSpec, execMetaGet }, { "ls", addMetaLsSpec, execMetaLs }, { "rm", addMetaRmSpec, execMetaRm }, + { "set", addMetaSetSpec, execMetaSet }, { "show", addMetaShowSpec, execMetaShow }, +}; + +void addMetaSpec (KeySet * spec) +{ + ksAppendKey (spec, keyNew (COMMAND_SPEC_KEY (COMMAND_NAME), KEY_META, "description", "Manage meta keys.", KEY_META, "command", + COMMAND_NAME, KEY_END)); + + for (unsigned long i = 0; i < sizeof (metaSubcommands) / sizeof (metaSubcommands[0]); ++i) + { + metaSubcommands[i].addSpec (spec); + } +} + +int execMeta (KeySet * options, Key * errorKey) +{ + const char * subcommand = keyString (ksLookupByName (options, CLI_BASE_KEY "/" COMMAND_NAME, 0)); + + for (unsigned long i = 0; i < sizeof (metaSubcommands) / sizeof (metaSubcommands[0]); ++i) + { + if (elektraStrCmp (subcommand, metaSubcommands[i].name) == 0) + { + return metaSubcommands[i].exec (options, errorKey); + } + } + // this cannot happen, since not valid sub-commands are already detected when parsing + return 1; +} diff --git a/src/tools/kdb/meta.h b/src/tools/kdb/meta.h new file mode 100644 index 00000000000..672be213756 --- /dev/null +++ b/src/tools/kdb/meta.h @@ -0,0 +1,33 @@ +/** + * @file + * + * @brief Header for meta command + * + * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) + */ + +#ifndef ELEKTRA_KDB_META_H +#define ELEKTRA_KDB_META_H + +#include + +/** + * Adds options specification of meta command to @spec + * + * @param spec the base spec where the commands spec should be added + */ +void addMetaSpec (KeySet * spec); + +/** + * Executes the meta command + * + * @param options cli options and arguments as specified in @addMetaSpec() + * @param errorKey key where errors and warnings should be saved + * + * @retval 0 ls command ran without errors + * @retval 1 errors occurred, keyGetMeta (errorKey, "error/reason") for info + * + */ +int execMeta (KeySet * options, Key * errorKey); + +#endif // ELEKTRA_KDB_META_H diff --git a/src/tools/kdb/metaget.cpp b/src/tools/kdb/metaget.cpp deleted file mode 100644 index 24de7e794fd..00000000000 --- a/src/tools/kdb/metaget.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/** - * @file - * - * @brief - * - * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) - */ - -#include - -#include -#include - -#include -#include - -using namespace std; -using namespace kdb; - -MetaGetCommand::MetaGetCommand () -{ -} - -int MetaGetCommand::execute (Cmdline const & cl) -{ - if (cl.arguments.size () != 2) - { - throw invalid_argument ("Need 2 arguments"); - } - Key k = cl.createKey (0); - Key parentKey = cl.getParentKey (cl.createKey (0)); - string metaname = cl.arguments[1]; - - KeySet conf; - kdb.get (conf, parentKey); - printWarnings (cerr, parentKey, cl.verbose, cl.debug); - - k = conf.lookup (k); - - if (!k) - { - cerr << "Key not found" << endl; - return 1; - } - - if (!k.getMeta (metaname)) - { - cerr << "Metakey not found" << endl; - return 2; - } - - cout << k.getMeta (metaname); - - if (!cl.noNewline) - { - cout << endl; - } - - return 0; -} - -MetaGetCommand::~MetaGetCommand () -{ -} diff --git a/src/tools/kdb/metaget.hpp b/src/tools/kdb/metaget.hpp deleted file mode 100644 index 02a9f5f7447..00000000000 --- a/src/tools/kdb/metaget.hpp +++ /dev/null @@ -1,47 +0,0 @@ -/** - * @file - * - * @brief - * - * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) - */ - -#ifndef METAGET_HPP -#define METAGET_HPP - -#include "coloredkdbio.hpp" -#include -#include - -class MetaGetCommand : public Command -{ - kdb::KDB kdb; - -public: - MetaGetCommand (); - ~MetaGetCommand (); - - virtual std::string getShortOptions () override - { - return "n"; - } - - virtual std::string getSynopsis () override - { - return " "; - } - - virtual std::string getShortHelpText () override - { - return "Get a metavalue."; - } - - virtual std::string getLongHelpText () override - { - return "Meta key are information about keys.\n"; - } - - virtual int execute (Cmdline const & cmdline) override; -}; - -#endif diff --git a/src/tools/kdb/metals.cpp b/src/tools/kdb/metals.cpp deleted file mode 100644 index 6360994a9d8..00000000000 --- a/src/tools/kdb/metals.cpp +++ /dev/null @@ -1,73 +0,0 @@ -/** - * @file - * - * @brief - * - * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) - */ - -#include - -#include - -#include -#include - -using namespace kdb; -using namespace std; - -MetaLsCommand::MetaLsCommand () -{ -} - -int MetaLsCommand::execute (Cmdline const & cl) -{ - int ret = 0; - if (cl.arguments.size () != 1) - { - throw invalid_argument ("1 argument required"); - } - - Key root = cl.createKey (0); - - kdb.get (ks, root); - - Key k = ks.lookup (root); - - if (k) - { - if (cl.verbose) - { - std::cout << "Got key " << k.getName () << std::endl; - } - - ckdb::KeySet * metaKeys = ckdb::keyMeta (k.getKey ()); - - for (ssize_t it = 0; it < ckdb::ksGetSize (metaKeys); ++it) - { - const Key & curMeta = ckdb::ksAtCursor (metaKeys, it); - cout << curMeta.getName ().substr (sizeof ("meta:/") - 1); - if (cl.null) - { - cout << '\0' << std::flush; - } - else - { - cout << endl; - } - } - } - else - { - std::cerr << "Did not find key" << std::endl; - ret = 1; - } - - printWarnings (cerr, root, cl.verbose, cl.debug); - - return ret; -} - -MetaLsCommand::~MetaLsCommand () -{ -} diff --git a/src/tools/kdb/metals.hpp b/src/tools/kdb/metals.hpp deleted file mode 100644 index 5faf59fe8f1..00000000000 --- a/src/tools/kdb/metals.hpp +++ /dev/null @@ -1,47 +0,0 @@ -/** - * @file - * - * @brief - * - * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) - */ - -#ifndef METALS_H -#define METALS_H - -#include -#include - -class MetaLsCommand : public Command -{ - kdb::KDB kdb; - kdb::KeySet ks; - -public: - MetaLsCommand (); - ~MetaLsCommand (); - - virtual std::string getShortOptions () override - { - return "0"; - } - - virtual std::string getSynopsis () override - { - return ""; - } - - virtual std::string getShortHelpText () override - { - return "Get all meta information of an individual key."; - } - - virtual std::string getLongHelpText () override - { - return ""; - } - - virtual int execute (Cmdline const & cmdline) override; -}; - -#endif diff --git a/src/tools/kdb/metaremove.cpp b/src/tools/kdb/metaremove.cpp deleted file mode 100644 index 083e7c73af7..00000000000 --- a/src/tools/kdb/metaremove.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/** - * @file - * - * @brief - * - * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) - */ - -#include - -#include -#include - -#include -#include - -using namespace std; -using namespace kdb; - -MetaRemoveCommand::MetaRemoveCommand () -{ -} - -MetaRemoveCommand::~MetaRemoveCommand () -{ -} - -int MetaRemoveCommand::execute (Cmdline const & cl) -{ - if (cl.arguments.size () != 2) - { - throw invalid_argument ("Need 2 arguments"); - } - Key parentKey = cl.createKey (0); - string metaname = cl.arguments[1]; - - KeySet conf; - kdb.get (conf, parentKey); - printWarnings (cerr, parentKey, cl.verbose, cl.debug); - - Key k = conf.lookup (parentKey); - - if (!k) - { - cerr << "Key not found" << endl; - return 1; - } - - k.delMeta (metaname); - - kdb.set (conf, parentKey); - - return 0; -} diff --git a/src/tools/kdb/metaremove.hpp b/src/tools/kdb/metaremove.hpp deleted file mode 100644 index d3434039742..00000000000 --- a/src/tools/kdb/metaremove.hpp +++ /dev/null @@ -1,47 +0,0 @@ -/** - * @file - * - * @brief - * - * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) - */ - -#ifndef METAREMOVE_HPP -#define METAREMOVE_HPP - -#include "coloredkdbio.hpp" -#include -#include - -class MetaRemoveCommand : public Command -{ - kdb::KDB kdb; - -public: - MetaRemoveCommand (); - ~MetaRemoveCommand (); - - virtual std::string getShortOptions () override - { - return ""; - } - - virtual std::string getSynopsis () override - { - return " "; - } - - virtual std::string getShortHelpText () override - { - return "Remove a metakey."; - } - - virtual std::string getLongHelpText () override - { - return ""; - } - - virtual int execute (Cmdline const & cmdline) override; -}; - -#endif diff --git a/src/tools/kdb/metaset.cpp b/src/tools/kdb/metaset.cpp deleted file mode 100644 index 513f3cddc6f..00000000000 --- a/src/tools/kdb/metaset.cpp +++ /dev/null @@ -1,95 +0,0 @@ -/** - * @file - * - * @brief - * - * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) - */ - -#include - -#include -#include - -#include -#include - -using namespace std; -using namespace kdb; - -MetaSetCommand::MetaSetCommand () -{ -} - -int MetaSetCommand::execute (Cmdline const & cl) -{ - if (cl.arguments.size () < 2 || cl.arguments.size () > 3) - { - throw invalid_argument ("Need 2 or 3 arguments"); - } - string metaname = cl.arguments[1]; - - Key k = cl.createKey (0); - string keyname = k.getName (); - - bool cascadingWrite = keyname[0] == '/'; - - Key parentKey = cl.getParentKey (k); - - KeySet conf; - kdb.get (conf, parentKey); - k = conf.lookup (k); - - if (!k) - { - if (cascadingWrite) - { - cerr << "Aborting: A cascading write to a non-existent key is ambiguous." << endl; - return 2; - } - - k = Key (keyname, KEY_END); - // k.setBinary(0, 0); // conceptually maybe better, but would have confusing "binary" metadata - conf.append (k); - if (cl.verbose) cout << "Creating key " << keyname << endl; - } - if (!k.isValid ()) - { - cerr << "Could not create key " << keyname << endl; - return 1; - } - - if (cl.arguments.size () == 2) - { - if (!cl.quiet) cout << "Only two arguments, thus deleting metaname " << metaname << endl; - k.delMeta (metaname); - } - else - { - std::string metavalue = cl.arguments[2]; - if (metaname == "atime" || metaname == "mtime" || metaname == "ctime") - { - stringstream str (metavalue); - time_t t; - str >> t; - if (!str.good ()) throw "conversion failure"; - k.setMeta (metaname, t); - } - else - { - k.setMeta (metaname, metavalue); - } - } - - if (!cl.quiet && cascadingWrite) std::cout << "Using name " << k.getName () << std::endl; - - kdb.set (conf, parentKey); - printWarnings (cerr, parentKey, cl.verbose, cl.debug); - printError (cerr, k, cl.verbose, cl.debug); - - return 0; -} - -MetaSetCommand::~MetaSetCommand () -{ -} diff --git a/src/tools/kdb/metaset.hpp b/src/tools/kdb/metaset.hpp deleted file mode 100644 index 991c8f33754..00000000000 --- a/src/tools/kdb/metaset.hpp +++ /dev/null @@ -1,48 +0,0 @@ -/** - * @file - * - * @brief - * - * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) - */ - -#ifndef METASET_HPP -#define METASET_HPP - -#include "coloredkdbio.hpp" - -#include -#include - -class MetaSetCommand : public Command -{ - kdb::KDB kdb; - -public: - MetaSetCommand (); - ~MetaSetCommand (); - - virtual std::string getShortOptions () override - { - return "qf"; - } - - virtual std::string getSynopsis () override - { - return " "; - } - - virtual std::string getShortHelpText () override - { - return "Set a metavalue."; - } - - virtual std::string getLongHelpText () override - { - return "Meta key are information about keys.\n"; - } - - virtual int execute (Cmdline const & cmdline) override; -}; - -#endif diff --git a/src/tools/kdb/mountpoint-mount.c b/src/tools/kdb/mountpoint-mount.c new file mode 100644 index 00000000000..04eb8a60343 --- /dev/null +++ b/src/tools/kdb/mountpoint-mount.c @@ -0,0 +1,101 @@ +/** + * @file + * + * @brief Implementation of kdb mount command + * + * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define COMMAND_NAME "mountpoint/mount" + +#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 addMountSpec (KeySet * spec) +{ + ksAppendKey (spec, keyNew (COMMAND_SPEC_KEY (COMMAND_NAME), KEY_META, "description", "Mount a new backend.", KEY_META, "command", + "mount", KEY_END)); + + ksAppendKey (spec, keyNew (COMMAND_SPEC_KEY (COMMAND_NAME) "/path", KEY_META, "description", + "A filename (absolute for system, relative for cascading or user)", KEY_META, "args", "indexed", + KEY_META, "args/index", "0", KEY_END)); + ksAppendKey (spec, keyNew (COMMAND_SPEC_KEY (COMMAND_NAME) "/mountpoint", KEY_META, "description", + "where to mount the backend, start with / for cascading mp", KEY_META, "args", "indexed", KEY_META, + "args/index", "1", KEY_END)); + + ksAppendKey (spec, keyNew (COMMAND_SPEC_KEY (COMMAND_NAME) "/plugins/#", KEY_META, "description", + "Plugin and its config, [key1=val1,key2=val2,...]", KEY_META, "args", "remaining", KEY_END)); + + ksAppendKey (spec, keyNew (COMMAND_SPEC_KEY (COMMAND_NAME) "/strategy", KEY_META, "description", + "Specify the strategy to resolve conflicts.", KEY_META, "opt", "s", KEY_META, "opt/arg/help", "STRATEGY", + KEY_META, "opt/long", "strategy", KEY_META, "opt/arg", "required", KEY_END)); + ksAppendKey (spec, keyNew (COMMAND_SPEC_KEY (COMMAND_NAME) "/resolver", KEY_META, "description", + "Specify the resolver plugin to use.", KEY_META, "opt", "R", KEY_META, "opt/arg/help", "NAME", KEY_META, + "opt/long", "resolver", KEY_META, "opt/arg", "required", KEY_END)); + + ksAppendKey (spec, keyNew (COMMAND_SPEC_KEY (COMMAND_NAME) "/force", KEY_META, "description", "Force the action to be done.", + KEY_META, "opt", "f", KEY_META, "opt/long", "force", KEY_META, "opt/arg", "none", KEY_END)); + ksAppendKey (spec, keyNew (COMMAND_SPEC_KEY (COMMAND_NAME) "/addrecommended", KEY_META, "description", "Add recommended plugins.", + KEY_META, "opt", "W", KEY_META, "opt/long", "with-recommends", KEY_META, "opt/arg", "none", KEY_END)); + ksAppendKey (spec, keyNew (COMMAND_SPEC_KEY (COMMAND_NAME) "/nullterm", KEY_META, "description", "Use binary 0 termination.", + KEY_META, "opt", "0", KEY_META, "opt/long", "null", KEY_META, "opt/arg", "none", KEY_END)); + + ksAppendKey (spec, keyNew (COMMAND_SPEC_KEY (COMMAND_NAME) "/supfirst", KEY_META, "description", "Suppress the first column.", + KEY_META, "opt", "1", KEY_META, "opt/long", "first", KEY_META, "opt/arg", "none", KEY_END)); + ksAppendKey (spec, keyNew (COMMAND_SPEC_KEY (COMMAND_NAME) "/supsecond", KEY_META, "description", "Suppress the second column.", + KEY_META, "opt", "2", KEY_META, "opt/long", "second", KEY_META, "opt/arg", "none", KEY_END)); + ksAppendKey (spec, keyNew (COMMAND_SPEC_KEY (COMMAND_NAME) "/supthird", KEY_META, "description", "Suppress the third column.", + KEY_META, "opt", "3", KEY_META, "opt/long", "third", KEY_META, "opt/arg", "none", KEY_END)); + + ADD_BASIC_OPTIONS (spec, COMMAND_SPEC_KEY (COMMAND_NAME)) +} + +int execMount (KeySet * options, Key * errorKey) +{ + int ret = 0; + bool verbose = false; + Key * tmp = GET_OPTION_KEY (options, "verbose"); + if (tmp != NULL) + { + elektraKeyToBoolean (GET_OPTION_KEY (options, "verbose"), &verbose); + } + + const char * path = GET_OPTION (options, "path"); + + const char * mountpoint = getKeyNameFromOptions (options, GET_OPTION (options, "mountpoint"), errorKey, verbose); + if (mountpoint == NULL) return 1; + + Key * arrayParent = GET_OPTION_KEY (options, "plugins"); + KeySet * plugins = elektraArrayGet (arrayParent, options); + + ksRewind (plugins); + Key * cur = NULL; + while ((cur = ksNext (plugins)) != NULL) + { + printf ("PLUGIN -> %s\n", keyString (cur)); + } + printf ("\n"); + + KDB * handle = kdbOpen (NULL, errorKey); + + KeySet * currentMountConfig = getMountConfig (handle, errorKey); + + Key * mountpointKey = keyNew (mountpoint, KEY_END); + + printKsNames (currentMountConfig); + + printf ("MOUNT"); +} diff --git a/src/tools/kdb/mountpoint-mount.h b/src/tools/kdb/mountpoint-mount.h new file mode 100644 index 00000000000..423264bae86 --- /dev/null +++ b/src/tools/kdb/mountpoint-mount.h @@ -0,0 +1,33 @@ +/** + * @file + * + * @brief Header for mount command + * + * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) + */ + +#ifndef ELEKTRA_KDB_MOUNT_H +#define ELEKTRA_KDB_MOUNT_H + +#include + +/** + * Adds options specification of mount command to @spec + * + * @param spec the base spec where the commands spec should be added + */ +void addMountSpec (KeySet * spec); + +/** + * Executes the mount command + * + * @param options cli options and arguments as specified in @addMountSpec() + * @param errorKey key where errors and warnings should be saved + * + * @retval 0 ls command ran without errors + * @retval 1 errors occurred, keyGetMeta (errorKey, "error/reason") for info + * + */ +int execMount (KeySet * options, Key * errorKey); + +#endif // ELEKTRA_KDB_MOUNT_H diff --git a/src/tools/kdb/mountpoint-remount.c b/src/tools/kdb/mountpoint-remount.c new file mode 100644 index 00000000000..29b3cf47517 --- /dev/null +++ b/src/tools/kdb/mountpoint-remount.c @@ -0,0 +1,44 @@ +/** + * @file + * + * @brief Implementation of kdb remount command + * + * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define COMMAND_NAME "mountpoint/remount" + +#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 addRemountSpec (KeySet * spec) +{ + ksAppendKey (spec, keyNew (COMMAND_SPEC_KEY (COMMAND_NAME), KEY_META, "description", + "Remount an existing backend with a different filename.", KEY_META, "command", "remount", KEY_END)); + ksAppendKey (spec, keyNew (COMMAND_SPEC_KEY (COMMAND_NAME) "/filename", KEY_META, "description", "The new filename.", KEY_META, + "args", "indexed", KEY_META, "args/index", "0", KEY_END)); + ksAppendKey (spec, keyNew (COMMAND_SPEC_KEY (COMMAND_NAME) "/path", KEY_META, "description", "The new path.", KEY_META, "args", + "indexed", KEY_META, "args/index", "1", KEY_END)); + ksAppendKey (spec, + keyNew (COMMAND_SPEC_KEY (COMMAND_NAME) "/mountpoint", KEY_META, "description", + "The mountpoint that should be remounted", KEY_META, "args", "indexed", KEY_META, "args/index", "2", KEY_END)); + + + ADD_BASIC_OPTIONS (spec, COMMAND_SPEC_KEY (COMMAND_NAME)) +} + +int execRemount (KeySet * options, Key * errorKey) +{ + printf ("RE-MOUNT"); +} diff --git a/src/tools/kdb/mountpoint-remount.h b/src/tools/kdb/mountpoint-remount.h new file mode 100644 index 00000000000..ebc5f133459 --- /dev/null +++ b/src/tools/kdb/mountpoint-remount.h @@ -0,0 +1,33 @@ +/** + * @file + * + * @brief Header for remount command + * + * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) + */ + +#ifndef ELEKTRA_KDB_REMOUNT_H +#define ELEKTRA_KDB_REMOUNT_H + +#include + +/** + * Adds options specification of remount command to @spec + * + * @param spec the base spec where the commands spec should be added + */ +void addRemountSpec (KeySet * spec); + +/** + * Executes the remount command + * + * @param options cli options and arguments as specified in @addRemountSpec() + * @param errorKey key where errors and warnings should be saved + * + * @retval 0 ls command ran without errors + * @retval 1 errors occurred, keyGetMeta (errorKey, "error/reason") for info + * + */ +int execRemount (KeySet * options, Key * errorKey); + +#endif // ELEKTRA_KDB_REMOUNT_H diff --git a/src/tools/kdb/mountpoint-umount.c b/src/tools/kdb/mountpoint-umount.c new file mode 100644 index 00000000000..3d0bac94200 --- /dev/null +++ b/src/tools/kdb/mountpoint-umount.c @@ -0,0 +1,38 @@ +/** + * @file + * + * @brief Implementation of kdb umount command + * + * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define COMMAND_NAME "mountpoint/umount" + +#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 addUmountSpec (KeySet * spec) +{ + ksAppendKey (spec, keyNew (COMMAND_SPEC_KEY (COMMAND_NAME), KEY_META, "description", "Unmount backend from key database.", KEY_META, + "command", "umount", KEY_END)); + ksAppendKey (spec, keyNew (COMMAND_SPEC_KEY (COMMAND_NAME) "/name", KEY_META, "description", "The keyname to unmount.", KEY_META, + "args", "indexed", KEY_META, "args/index", "0", KEY_END)); + + ADD_BASIC_OPTIONS (spec, COMMAND_SPEC_KEY (COMMAND_NAME)) +} + +int execUmount (KeySet * options, Key * errorKey) +{ + printf ("UN-MOUNT"); +} diff --git a/src/tools/kdb/mountpoint-umount.h b/src/tools/kdb/mountpoint-umount.h new file mode 100644 index 00000000000..2efcc181085 --- /dev/null +++ b/src/tools/kdb/mountpoint-umount.h @@ -0,0 +1,33 @@ +/** + * @file + * + * @brief Header for umount command + * + * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) + */ + +#ifndef ELEKTRA_KDB_UMOUNT_H +#define ELEKTRA_KDB_UMOUNT_H + +#include + +/** + * Adds options specification of umount command to @spec + * + * @param spec the base spec where the commands spec should be added + */ +void addUmountSpec (KeySet * spec); + +/** + * Executes the umount command + * + * @param options cli options and arguments as specified in @addUmountSpec() + * @param errorKey key where errors and warnings should be saved + * + * @retval 0 ls command ran without errors + * @retval 1 errors occurred, keyGetMeta (errorKey, "error/reason") for info + * + */ +int execUmount (KeySet * options, Key * errorKey); + +#endif // ELEKTRA_KDB_UMOUNT_H diff --git a/src/tools/kdb/mountpoint.c b/src/tools/kdb/mountpoint.c new file mode 100644 index 00000000000..1a90cfe4edc --- /dev/null +++ b/src/tools/kdb/mountpoint.c @@ -0,0 +1,68 @@ +/** + * @file + * + * @brief Implementation of kdb mountpoint command + * + * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define COMMAND_NAME "mountpoint" + +#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) + +command mountSubcommands[] = { + { "mount", addMountSpec, execMount }, + { "remount", addRemountSpec, execRemount }, + { "umount", addUmountSpec, execUmount }, +}; + +void addMountpointSpec (KeySet * spec) +{ + ksAppendKey (spec, keyNew (COMMAND_SPEC_KEY (COMMAND_NAME), KEY_META, "description", "Manage kdb mounts.", KEY_META, "command", + COMMAND_NAME, KEY_END)); + + for (unsigned long i = 0; i < sizeof (mountSubcommands) / sizeof (mountSubcommands[0]); ++i) + { + mountSubcommands[i].addSpec (spec); + } +} + +int execMountpoint (KeySet * options, Key * errorKey) +{ + const char * subcommand = keyString (ksLookupByName (options, CLI_BASE_KEY "/" COMMAND_NAME, 0)); + + for (unsigned long i = 0; i < sizeof (mountSubcommands) / sizeof (mountSubcommands[0]); ++i) + { + if (elektraStrCmp (subcommand, mountSubcommands[i].name) == 0) + { + return mountSubcommands[i].exec (options, errorKey); + } + } +} + +KeySet * getMountConfig (KDB * handle, Key * errorKey) +{ + Key * parent = keyNew (MOUNTPOINTS_PATH, KEY_END); + KeySet * mountInfo = ksNew (0, KS_END); + kdbGet (handle, mountInfo, parent); + + // TODO: maybe print warnings(or add them to error key) + + keyDel (parent); + return mountInfo; +} diff --git a/src/tools/kdb/mountpoint.h b/src/tools/kdb/mountpoint.h new file mode 100644 index 00000000000..d761e44e33d --- /dev/null +++ b/src/tools/kdb/mountpoint.h @@ -0,0 +1,38 @@ +/** + * @file + * + * @brief Header for mount command + * + * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) + */ + +#ifndef ELEKTRA_KDB_MOUNTPOINT_H +#define ELEKTRA_KDB_MOUNTPOINT_H + +#include + +#define MOUNTPOINTS_PATH "system:/elektra/mountpoints" + +/** + * Adds options specification of mountpoint command to @spec + * + * @param spec the base spec where the commands spec should be added + */ +void addMountpointSpec (KeySet * spec); + +/** + * Executes the mount command + * + * @param options cli options and arguments as specified in @addMountpointSpec() + * @param errorKey key where errors and warnings should be saved + * + * @retval 0 ls command ran without errors + * @retval 1 errors occurred, keyGetMeta (errorKey, "error/reason") for info + * + */ +int execMountpoint (KeySet * options, Key * errorKey); + +// helper functions +KeySet * getMountConfig (KDB * handle, Key *); + +#endif // ELEKTRA_KDB_MOUNTPOINT_H diff --git a/src/tools/kdb/mv.c b/src/tools/kdb/mv.c new file mode 100644 index 00000000000..cb68e2e39d4 --- /dev/null +++ b/src/tools/kdb/mv.c @@ -0,0 +1,164 @@ +/** + * @file + * + * @brief Implementation of kdb mv command + * + * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) + */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#define COMMAND_NAME "mv" + +#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 addMvSpec (KeySet * spec) +{ + ksAppendKey (spec, keyNew (COMMAND_SPEC_KEY (COMMAND_NAME), KEY_META, "description", + "Move 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 execMv (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; + } + + CLI_PRINT (CLI_LOG_DEBUG, "%s", "create keys... "); + + 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; + } + + CLI_PRINT (CLI_LOG_DEBUG, "%s", "ok\n"); + + 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)); + + CLI_PRINT (CLI_LOG_DEBUG, "source: %s [%ld]\n", keyName (sourceKey), sourceNameLen); + CLI_PRINT (CLI_LOG_DEBUG, "dest: %s [%ld]\n", keyName (destKey), destNameLen); + CLI_PRINT (CLI_LOG_DEBUG, "recursive: %d\n", recursive); + + 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)); + CLI_PRINT (CLI_LOG_DEBUG, "checking if '%s' has to be moved\n", BOLD (keyName (cur))); + CLI_PRINT (CLI_LOG_DEBUG, " starts-with: %d; ", startsWithSrc); + CLI_PRINT (CLI_LOG_DEBUG, " equals: %d\n", equalsSrc); + + // 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++; + } + else + { + 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/mv.cpp b/src/tools/kdb/mv.cpp deleted file mode 100644 index 331e153af3b..00000000000 --- a/src/tools/kdb/mv.cpp +++ /dev/null @@ -1,93 +0,0 @@ -/** - * @file - * - * @brief - * - * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) - */ - -#include - -#include -#include -#include -#include - -#include - -#include - -using namespace std; -using namespace kdb; - -MvCommand::MvCommand () -{ -} - -int MvCommand::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 (); - - Key root = tools::helper::commonKeyName (sourceKey, destKey); - if (cl.verbose) std::cout << "using common basename: " << root.getName () << std::endl; - kdb.get (conf, root); - KeySet tmpConf = conf; - KeySet oldConf; - - oldConf.append (tmpConf.cut (sourceKey)); - std::string sourceName = sourceKey.getName (); - - if (!oldConf.size ()) - { - std::cerr << "No key to copy found below '" << sourceName << "'" << std::endl; - return 11; - } - - KeySet newConf; - - if (cl.recursive) - { - for (Key k : oldConf) - { - newConf.append (rename_key (k, sourceName, newDirName, cl.verbose)); - } - } - else - { - // just rename 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; - } - newConf.append (rename_key (k, sourceName, newDirName, cl.verbose)); - } - newConf.append (tmpConf); // these are unrelated keys - // drop the original configuration - - if (cl.verbose) - { - cout << "Will write out:" << endl; - cout << newConf; - } - - kdb.set (newConf, root); - printWarnings (cerr, root, cl.verbose, cl.debug); - - return 0; -} - -MvCommand::~MvCommand () -{ -} diff --git a/src/tools/kdb/mv.h b/src/tools/kdb/mv.h new file mode 100644 index 00000000000..c15fe9f5b4c --- /dev/null +++ b/src/tools/kdb/mv.h @@ -0,0 +1,36 @@ +/** + * @file + * + * @brief Header for mv command + * + * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) + */ + +#ifndef ELEKTRA_KDB_MV_H +#define ELEKTRA_KDB_MV_H + +#include + +/** + * Adds options specification of mv command to @spec + * + * @param spec the base spec where the commands spec should be added + */ +void addMvSpec (KeySet * spec); + +/** + * Executes the mv command + * + * @param options cli options and arguments as specified in addMvSpec() + * @param errorKey key where errors and warnings should be saved + * + * @retval 0 mv command ran without errors + * @retval 1 errors occurred, keyGetMeta (errorKey, "error/reason") for info + * + */ +int execMv (KeySet * options, Key * errorKey); + +// helper functions +int getKeyNameDepth (const char * name); + +#endif // ELEKTRA_KDB_MV_H diff --git a/src/tools/kdb/mv.hpp b/src/tools/kdb/mv.hpp deleted file mode 100644 index 9ae2310a702..00000000000 --- a/src/tools/kdb/mv.hpp +++ /dev/null @@ -1,47 +0,0 @@ -/** - * @file - * - * @brief - * - * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) - */ - -#ifndef MV_HPP -#define MV_HPP - -#include "coloredkdbio.hpp" -#include -#include - -class MvCommand : public Command -{ - kdb::KDB kdb; - -public: - MvCommand (); - ~MvCommand (); - - virtual std::string getShortOptions () override - { - return "r"; - } - - virtual std::string getSynopsis () override - { - return " "; - } - - virtual std::string getShortHelpText () override - { - return "Move configuration within the key database."; - } - - virtual std::string getLongHelpText () override - { - return ""; - } - - virtual int execute (Cmdline const & cmdline) override; -}; - -#endif diff --git a/src/tools/kdb/namespace.c b/src/tools/kdb/namespace.c new file mode 100644 index 00000000000..81434a771d1 --- /dev/null +++ b/src/tools/kdb/namespace.c @@ -0,0 +1,59 @@ +/** + * @file + * + * @brief Implementation of kdb namespace command + * + * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) + */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#define COMMAND_NAME "namespace" + +#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 addNamespaceSpec (KeySet * spec) +{ + ksAppendKey (spec, keyNew (COMMAND_SPEC_KEY (COMMAND_NAME), KEY_META, "description", "Get the namespace of a key.", KEY_META, + "command", COMMAND_NAME, KEY_END)); + ksAppendKey (spec, keyNew (COMMAND_SPEC_KEY (COMMAND_NAME) "/name", KEY_META, "description", "The name of the key", KEY_META, + "args", "indexed", KEY_META, "args/index", "0", KEY_END)); + + ADD_BASIC_OPTIONS (spec, COMMAND_SPEC_KEY (COMMAND_NAME)) +} + +int execNamespace (KeySet * options, Key * errorKey) +{ + GET_BASIC_OPTIONS + + const char * name = getKeyNameFromOptions (options, GET_OPTION (options, "name"), errorKey, verbose); + if (name == NULL) return 1; + + Key * key = keyNew (name, KEY_END); + if (key == NULL) + { + ELEKTRA_SET_VALIDATION_SEMANTIC_ERRORF (errorKey, "'%s' is not a valid key name.", name); + elektraFree ((void *) name); + return 1; + } + + elektraNamespace ns = keyGetNamespace (key); + if (ns != KEY_NS_NONE && ns != KEY_NS_CASCADING) + { + int pos = (int) (strchr (name, '/') - name); + CLI_PRINT (CLI_LOG_NONE, "%*.*s", pos, pos, BOLD (name)); + } + + elektraFree ((void *) name); + keyDel (key); + return 0; +} diff --git a/src/tools/kdb/namespace.cpp b/src/tools/kdb/namespace.cpp deleted file mode 100644 index 51e1b36fda0..00000000000 --- a/src/tools/kdb/namespace.cpp +++ /dev/null @@ -1,27 +0,0 @@ -/** - * @file - * - * @brief - * - * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) - */ - -#include -#include -#include - - -using namespace std; - -NamespaceCommand::NamespaceCommand () -{ -} - -int NamespaceCommand::execute (Cmdline const & cl) -{ - return executeNamepartcommand (cl, keyNamespace); -} - -NamespaceCommand::~NamespaceCommand () -{ -} diff --git a/src/tools/kdb/namespace.h b/src/tools/kdb/namespace.h new file mode 100644 index 00000000000..cb18453041e --- /dev/null +++ b/src/tools/kdb/namespace.h @@ -0,0 +1,33 @@ +/** + * @file + * + * @brief Header for namespace command + * + * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) + */ + +#ifndef ELEKTRA_KDB_NAMESPACE_H +#define ELEKTRA_KDB_NAMESPACE_H + +#include + +/** + * Adds options specification of namespace command to @spec + * + * @param spec the base spec where the commands spec should be added + */ +void addNamespaceSpec (KeySet * spec); + +/** + * Executes the namespace command + * + * @param options cli options and arguments as specified in @addNamespaceSpec() + * @param errorKey key where errors and warnings should be saved + * + * @retval 0 ls command ran without errors + * @retval 1 errors occurred, keyGetMeta (errorKey, "error/reason") for info + * + */ +int execNamespace (KeySet * options, Key * errorKey); + +#endif // ELEKTRA_KDB_NAMESPACE_H diff --git a/src/tools/kdb/namespace.hpp b/src/tools/kdb/namespace.hpp deleted file mode 100644 index 38959854463..00000000000 --- a/src/tools/kdb/namespace.hpp +++ /dev/null @@ -1,48 +0,0 @@ -/** - * @file - * - * @brief - * - * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) - */ - -#ifndef NAMESPACE_HPP -#define NAMESPACE_HPP - -#include "coloredkdbio.hpp" -#include -#include - -class NamespaceCommand : public Command -{ - kdb::KDB kdb; - -public: - NamespaceCommand (); - ~NamespaceCommand (); - - virtual std::string getShortOptions () override - { - return "n"; - } - - virtual std::string getSynopsis () override - { - return ""; - } - - virtual std::string getShortHelpText () override - { - return "Get the namespace of a key (trailing \':\' included)."; - } - - virtual std::string getLongHelpText () override - { - return "For example, \"kdb namespace user:/key\" will yield \"user:\",\n" - "and, \"kdb namespace /key\" will yield the empty string.\n"; - } - - virtual int execute (Cmdline const & cmdline) override; -}; - -#endif diff --git a/src/tools/kdb/rm.c b/src/tools/kdb/rm.c new file mode 100644 index 00000000000..7336ae64915 --- /dev/null +++ b/src/tools/kdb/rm.c @@ -0,0 +1,115 @@ +/** + * @file + * + * @brief Implementation of kdb rm command + * + * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) + */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#define COMMAND_NAME "rm" + +#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 addRmSpec (KeySet * spec) +{ + ksAppendKey (spec, keyNew (COMMAND_SPEC_KEY (COMMAND_NAME), KEY_META, "description", "Remove a key.", 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) "/name", KEY_META, "description", "The key name", KEY_META, "args", + "indexed", KEY_META, "args/index", "0", KEY_END)); + + ADD_BASIC_OPTIONS (spec, COMMAND_SPEC_KEY (COMMAND_NAME)) +} + +int execRm (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 * name = getKeyNameFromOptions (options, GET_OPTION (options, "name"), errorKey, verbose); + if (name == NULL) return 1; + + Key * key = keyNew (name, KEY_END); + + if (keyGetNamespace (key) == KEY_NS_NONE || keyGetNamespace (key) == KEY_NS_CASCADING) + { + ret = 1; + ELEKTRA_SET_VALIDATION_SYNTACTIC_ERROR (errorKey, "source key does not specify a namespace"); + elektraFree ((void *) name); + keyDel (key); + 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", name, GET_ERR (root)); + ret = 1; + goto cleanup; + } + + Key * cur = NULL; + + size_t keyNameLen = elektraStrLen (keyName (key)); + + 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 (key), keyNameLen - 1); + bool equalsSrc = !elektraStrCmp (keyName (cur), keyName (key)); + + // starts-with if recursive, or equals if !recursive + if ((recursive && startsWithSrc) || equalsSrc) + { + count++; + CLI_PRINT (CLI_LOG_VERBOSE, "removing '%s\n", BOLD (keyName (cur))); + continue; + } + 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, "\nremoved %ld keys", count); + + +cleanup: + if (!noNewLine) + { + printf ("\n"); + } + elektraFree ((void *) name); + keyDel (root); + elektraFree ((void *) fmtBuffer); + ksDel (conf); + kdbClose (handle, errorKey); + return ret; +} diff --git a/src/tools/kdb/rm.cpp b/src/tools/kdb/rm.cpp deleted file mode 100644 index 7f18c04e9c8..00000000000 --- a/src/tools/kdb/rm.cpp +++ /dev/null @@ -1,79 +0,0 @@ -/** - * @file - * - * @brief - * - * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) - */ - -#include - -#include -#include - -#include - -using namespace std; -using namespace kdb; - -RemoveCommand::RemoveCommand () -{ -} - -static int noKeyFound (bool verbose, bool force, std::string article) -{ - if (verbose || !force) - { - cerr << "Did not find " << article << " key" << endl; - } - return force ? 0 : 11; -} - -int RemoveCommand::execute (Cmdline const & cl) -{ - if (cl.arguments.size () != 1) throw invalid_argument ("1 argument required"); - - KeySet conf; - Key x = cl.createKey (0); - Key parentKey = cl.getParentKey (x); - - kdb.get (conf, parentKey); - - KeySet savedKeys; - - if (cl.withoutElektra) - { - Key systemElektra ("system:/elektra", KEY_END); - savedKeys = conf.cut (systemElektra); - } - - if (!cl.recursive) - { - Key f = conf.lookup (x, KDB_O_POP); - - if (!f) - { - return noKeyFound (cl.verbose, cl.force, "the"); - } - } - else - { - // do recursive removing - KeySet ks = conf.cut (x); - - if (ks.size () == 0) - { - return noKeyFound (cl.verbose, cl.force, "any"); - } - } - - conf.append (savedKeys); - - kdb.set (conf, parentKey); - - return 0; -} - -RemoveCommand::~RemoveCommand () -{ -} diff --git a/src/tools/kdb/rm.h b/src/tools/kdb/rm.h new file mode 100644 index 00000000000..5a2a53e22d3 --- /dev/null +++ b/src/tools/kdb/rm.h @@ -0,0 +1,36 @@ +/** + * @file + * + * @brief Header for rm command + * + * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) + */ + +#ifndef ELEKTRA_KDB_RM_H +#define ELEKTRA_KDB_RM_H + +#include + +/** + * Adds options specification of rm command to @spec + * + * @param spec the base spec where the commands spec should be added + */ +void addRmSpec (KeySet * spec); + +/** + * Executes the rm command + * + * @param options cli options and arguments as specified in addRmSpec() + * @param errorKey key where errors and warnings should be saved + * + * @retval 0 rm command ran without errors + * @retval 1 errors occurred, keyGetMeta (errorKey, "error/reason") for info + * + */ +int execRm (KeySet * options, Key * errorKey); + +// helper functions +int getKeyNameDepth (const char * name); + +#endif // ELEKTRA_KDB_RM_H diff --git a/src/tools/kdb/rm.hpp b/src/tools/kdb/rm.hpp deleted file mode 100644 index 97483c3e4a2..00000000000 --- a/src/tools/kdb/rm.hpp +++ /dev/null @@ -1,47 +0,0 @@ -/** - * @file - * - * @brief - * - * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) - */ - -#ifndef REMOVE_HPP -#define REMOVE_HPP - -#include - -#include - -class RemoveCommand : public Command -{ - kdb::KDB kdb; - -public: - RemoveCommand (); - ~RemoveCommand (); - - virtual std::string getShortOptions () override - { - return "rfE"; - } - - virtual std::string getSynopsis () override - { - return ""; - } - - virtual std::string getShortHelpText () override - { - return "Remove key(s) from key database."; - } - - virtual std::string getLongHelpText () override - { - return ""; - } - - virtual int execute (Cmdline const & cmdline) override; -}; - -#endif diff --git a/src/tools/kdb/set.c b/src/tools/kdb/set.c new file mode 100644 index 00000000000..aeb870363c9 --- /dev/null +++ b/src/tools/kdb/set.c @@ -0,0 +1,103 @@ +/** + * @file + * + * @brief KDB set subcommand + * + * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define COMMAND_NAME "set" + +#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 addSetSpec (KeySet * spec) +{ + ksAppendKey (spec, keyNew (COMMAND_SPEC_KEY (COMMAND_NAME), KEY_META, "description", "Set the value of an individual key.", + KEY_META, "command", COMMAND_NAME, KEY_END)); + ksAppendKey (spec, keyNew (COMMAND_SPEC_KEY (COMMAND_NAME) "/force", KEY_META, "description", "Force setting the value", KEY_META, + "opt", "f", KEY_META, "opt/long", "force", KEY_META, "opt/arg", "none", KEY_END)); + ksAppendKey (spec, keyNew (COMMAND_SPEC_KEY (COMMAND_NAME) "/name", KEY_META, "description", "The name of the key", KEY_META, + "args", "indexed", KEY_META, "args/index", "0", KEY_END)); + + ksAppendKey (spec, keyNew (COMMAND_SPEC_KEY (COMMAND_NAME) "/value", KEY_META, "description", "The value that should be set", + KEY_META, "args", "indexed", KEY_META, "args/index", "1", KEY_END)); + + ADD_BASIC_OPTIONS (spec, COMMAND_SPEC_KEY (COMMAND_NAME)) +} + +int execSet (KeySet * options, Key * errorKey) +{ + int ret = 0; + GET_BASIC_OPTIONS + + bool force = false; + tmp = GET_OPTION_KEY (options, "force"); + if (tmp != NULL) + { + elektraKeyToBoolean (GET_OPTION_KEY (options, "force"), &force); + } + + const char * name = getKeyNameFromOptions (options, GET_OPTION (options, "name"), errorKey, verbose); + if (name == NULL) return 1; + + const char * value = GET_OPTION (options, "value"); + + Key * parentKey = keyNew (name, KEY_END); + + if (keyGetNamespace (parentKey) == KEY_NS_NONE || keyGetNamespace (parentKey) == KEY_NS_CASCADING) + { + ret = 1; + ELEKTRA_SET_VALIDATION_SYNTACTIC_ERROR (errorKey, "key does not specify a namespace"); + elektraFree ((void *) name); + keyDel (parentKey); + return ret; + } + + keySetNamespace (parentKey, KEY_NS_CASCADING); + KeySet * conf = ksNew (0, KS_END); + KDB * handle = kdbOpen (NULL, errorKey); + + if (kdbGet (handle, conf, parentKey) == -1) + { + ELEKTRA_SET_VALIDATION_SEMANTIC_ERRORF (errorKey, "could not load '%s': %s", name, GET_ERR (parentKey)); + ret = 1; + goto cleanup; + } + + Key * key = ksLookup (conf, parentKey, KDB_O_NONE); + if (key == NULL) + { + key = keyNew (name, KEY_END); + ksAppendKey (conf, key); + } + keySetString (key, value); // can't fail, since neither value or key can be null + + if (kdbSet (handle, conf, parentKey) == -1) + { + ret = 1; + ELEKTRA_SET_VALIDATION_SEMANTIC_ERRORF (errorKey, "could not set value for '%s': %s", name, GET_ERR (parentKey)); + } + + keyDel (key); + +cleanup: + if (!noNewLine) + { + printf ("\n"); + } + elektraFree ((void *) name); + keyDel (parentKey); + ksDel (conf); + kdbClose (handle, errorKey); + return ret; +} diff --git a/src/tools/kdb/set.cpp b/src/tools/kdb/set.cpp deleted file mode 100644 index b6c69259a09..00000000000 --- a/src/tools/kdb/set.cpp +++ /dev/null @@ -1,86 +0,0 @@ -/** - * @file - * - * @brief - * - * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) - */ - -#include - -#include -#include -#include - -#include - -using namespace std; -using namespace kdb; - -SetCommand::SetCommand () -{ -} - -int SetCommand::execute (Cmdline const & cl) -{ - int argc = cl.arguments.size (); - if (argc != 2) - { - throw invalid_argument ("2 arguments needed"); - } - - std::string value = cl.arguments[1]; - - KeySet conf; - Key k = cl.createKey (0); - std::string name = k.getName (); - Key parentKey = cl.getParentKey (k); - - // do not resume on any get errors - // otherwise the user might break - // the config - kdb.get (conf, parentKey); - - bool cascadingWrite = name[0] == '/'; - - Key key = conf.lookup (name); - - std::ostringstream toprint; - - if (!key && cascadingWrite) - { - cerr << "Aborting: A cascading write to a non-existent key is ambiguous." << endl; - return 2; - } - if (!key) - { - toprint << "Create a new key " << name; - key = Key (name, KEY_END); - toprint << " with string \"" << value << '"' << endl; - key.setString (value); - - if (!key.isValid ()) - { - cerr << "no valid name supplied" << endl; - return 1; - } - conf.append (key); - } - else - { - toprint << "Set string to \"" << value << '"' << endl; - key.setString (value); - } - kdb.set (conf, parentKey); - printWarnings (cerr, parentKey, cl.verbose, cl.debug); - printError (cerr, parentKey, cl.verbose, cl.debug); - - if (cascadingWrite) toprint << "Using name " << key.getName () << std::endl; - if (!cl.quiet) cout << toprint.str (); - - return 0; -} - -SetCommand::~SetCommand () -{ -} diff --git a/src/tools/kdb/set.h b/src/tools/kdb/set.h new file mode 100644 index 00000000000..3af921614ee --- /dev/null +++ b/src/tools/kdb/set.h @@ -0,0 +1,33 @@ +/** + * @file + * + * @brief KDB set subcommand header + * + * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) + */ + +#ifndef ELEKTRA_KDB_SET_H +#define ELEKTRA_KDB_SET_H + +#include + +/** + * Adds options specification of set command to keySet + * + * @param spec the base spec where the commands spec should be added + */ +void addSetSpec (KeySet * spec); + +/** + * Runs the set command + * + * @param options cli options and arguments as specified in addSetSpec() + * @param errorKey key where errors and warnings should be saved + * + * @retval 0 set command ran without errors + * @retval 1 errors occurred, keySetMeta (errorKey, "error/reason") for info + * + */ +int execSet (KeySet * options, Key * errorKey); + +#endif // ELEKTRA_KDB_SET_H diff --git a/src/tools/kdb/set.hpp b/src/tools/kdb/set.hpp deleted file mode 100644 index c64a909e59d..00000000000 --- a/src/tools/kdb/set.hpp +++ /dev/null @@ -1,49 +0,0 @@ -/** - * @file - * - * @brief - * - * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) - */ - -#ifndef SET_HPP -#define SET_HPP - -#include "coloredkdbio.hpp" -#include -#include - -class SetCommand : public Command -{ - kdb::KDB kdb; - -public: - SetCommand (); - ~SetCommand (); - - virtual std::string getShortOptions () override - { - return "qf"; - } - - virtual std::string getSynopsis () override - { - return " "; - } - - virtual std::string getShortHelpText () override - { - return "Set the value of an individual key."; - } - - virtual std::string getLongHelpText () override - { - return "To set an empty value you need to quote like \"\" (depending on shell)\n" - "To set a negative value you need to use '--' to stop option processing.\n" - "(e.g. 'kdb set -- /tests/neg -3')\n"; - } - - virtual int execute (Cmdline const & cmdline) override; -}; - -#endif diff --git a/src/tools/kdb/showmeta.cpp b/src/tools/kdb/showmeta.cpp deleted file mode 100644 index 4705705df04..00000000000 --- a/src/tools/kdb/showmeta.cpp +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file - * - * @brief - * - * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) - */ - -#include - -#include -#include - -#include - -using namespace std; -using namespace kdb; - -ShowMetaCommand::ShowMetaCommand () -{ -} - -int ShowMetaCommand::execute (Cmdline const & cl) -{ - if (cl.arguments.size () != 1) throw invalid_argument ("Need one argument"); - - Key root = cl.createKey (0); - KeySet conf; - kdb.get (conf, root); - printWarnings (cerr, root, cl.verbose, cl.debug); - - Key k = conf.lookup (root); - - if (!k) - { - cerr << "Key not found" << endl; - return 1; - } - - ckdb::KeySet * metaKeys = ckdb::keyMeta (k.getKey ()); - - for (ssize_t it = 0; it < ckdb::ksGetSize (metaKeys); ++it) - { - const Key & curMeta = ckdb::ksAtCursor (metaKeys, it); - cout << curMeta.getName ().substr (sizeof ("meta:/") - 1) << ": " << curMeta.getString () << endl; - } - - return 0; -} - -ShowMetaCommand::~ShowMetaCommand () -{ -} diff --git a/src/tools/kdb/showmeta.hpp b/src/tools/kdb/showmeta.hpp deleted file mode 100644 index 052d0fb0732..00000000000 --- a/src/tools/kdb/showmeta.hpp +++ /dev/null @@ -1,47 +0,0 @@ -/** - * @file - * - * @brief - * - * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) - */ - -#ifndef SHOWMETA_HPP -#define SHOWMETA_HPP - -#include "coloredkdbio.hpp" -#include -#include - -class ShowMetaCommand : public Command -{ - kdb::KDB kdb; - -public: - ShowMetaCommand (); - ~ShowMetaCommand (); - - virtual std::string getShortOptions () override - { - return ""; - } - - virtual std::string getSynopsis () override - { - return ""; - } - - virtual std::string getShortHelpText () override - { - return "Print all metakeys along with their value for the given key."; - } - - virtual std::string getLongHelpText () override - { - return ""; - } - - virtual int execute (Cmdline const & cmdline) override; -}; - -#endif