From 2cd95b29055c14fe518b752e43ac6c42e6208319 Mon Sep 17 00:00:00 2001 From: Hannes Laimer Date: Tue, 20 Sep 2022 11:25:12 +0200 Subject: [PATCH 1/8] kdb-cli: add new main.c and add command.h containing helpful macros and a struct def that is needed later --- src/tools/kdb/CMakeLists.txt | 5 +- src/tools/kdb/command.h | 75 ++++++++++ src/tools/kdb/main.c | 138 ++++++++++++++++++ src/tools/kdb/main.cpp | 261 ----------------------------------- 4 files changed, 216 insertions(+), 263 deletions(-) create mode 100644 src/tools/kdb/command.h create mode 100644 src/tools/kdb/main.c delete mode 100644 src/tools/kdb/main.cpp 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/command.h b/src/tools/kdb/command.h new file mode 100644 index 00000000000..f5ef3c6af96 --- /dev/null +++ b/src/tools/kdb/command.h @@ -0,0 +1,75 @@ +/** + * @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 + +#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) + +#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); \ + } + + +typedef struct command +{ + const char * name; + void (*addSpec) (KeySet * spec); + int (*exec) (KeySet * options, Key * errorKey); +} command; + + +#endif // ELEKTRA_KDB_COMMAND_H diff --git a/src/tools/kdb/main.c b/src/tools/kdb/main.c new file mode 100644 index 00000000000..f6e4f11dd89 --- /dev/null +++ b/src/tools/kdb/main.c @@ -0,0 +1,138 @@ +/** + * @file + * + * @brief The KDB cli tool + * + * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) + */ + +#include +#include +#include +#include +#include +#include +#include + +#define CLI_SPEC_KEY "spec:" CLI_BASE_KEY + +extern char ** environ; + +command subcommands[] = { + {NULL, NULL, NULL}, +}; + +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"); + kdbClose (kdb, parentKey); + keyDel (parentKey); + ksDel (options); + return EXIT_SUCCESS; + } + + 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); + kdbClose (kdb, parentKey); + keyDel (parentKey); + ksDel (options); + return EXIT_SUCCESS; + } + + if (result == -1) + { + fprintf (stderr, "ERROR: %s\n", GET_ERR (parentKey)); + kdbClose (kdb, parentKey); + keyDel (parentKey); + ksDel (options); + return EXIT_FAILURE; + } + + keyDel (parentKey); + for (unsigned long i = 0; i < sizeof (subcommands) / sizeof (subcommands[0]); ++i) + { + if (elektraStrCmp (subcommand, subcommands[i].name) == 0) + { + 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); + kdbClose (kdb, parentKey); + keyDel (parentKey); + ksDel (options); + return result; + } + } + + kdbClose (kdb, parentKey); + keyDel (parentKey); + ksDel (options); + return 0; +} 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; - } -} From 074d135d335e506e3d43542155fed944a5d48d17 Mon Sep 17 00:00:00 2001 From: Hannes Laimer Date: Wed, 21 Sep 2022 07:03:44 +0200 Subject: [PATCH 2/8] kdb-cli: add helper functions ... to command.c for resolving bookmarks in keynames. --- src/tools/kdb/command.c | 75 +++++++++++++++++++++++++++++++++++++++++ src/tools/kdb/command.h | 23 +++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 src/tools/kdb/command.c diff --git a/src/tools/kdb/command.c b/src/tools/kdb/command.c new file mode 100644 index 00000000000..1780672d3c3 --- /dev/null +++ b/src/tools/kdb/command.c @@ -0,0 +1,75 @@ +/** + * @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; + } + return result; +} diff --git a/src/tools/kdb/command.h b/src/tools/kdb/command.h index f5ef3c6af96..9167c1345c2 100644 --- a/src/tools/kdb/command.h +++ b/src/tools/kdb/command.h @@ -10,6 +10,7 @@ #define ELEKTRA_KDB_COMMAND_H #include +#include #define CLI_BASE_KEY "/sw/elektra/kdb/#0/current" @@ -63,6 +64,28 @@ sleep (1); \ } +/** + * 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); typedef struct command { From 02f6d002da49aea30b5c0c9d88c5caf65ea7a44b Mon Sep 17 00:00:00 2001 From: Hannes Laimer Date: Wed, 21 Sep 2022 07:12:08 +0200 Subject: [PATCH 3/8] kdb-cli: rewrite get command ... and remove cpp implementation --- src/tools/kdb/factory.hpp | 4 +- src/tools/kdb/get.c | 229 ++++++++++++++++++++++++++++++++++++++ src/tools/kdb/get.cpp | 197 -------------------------------- src/tools/kdb/get.h | 43 +++++++ src/tools/kdb/get.hpp | 49 -------- src/tools/kdb/main.c | 4 +- 6 files changed, 277 insertions(+), 249 deletions(-) create mode 100644 src/tools/kdb/get.c delete mode 100644 src/tools/kdb/get.cpp create mode 100644 src/tools/kdb/get.h delete mode 100644 src/tools/kdb/get.hpp diff --git a/src/tools/kdb/factory.hpp b/src/tools/kdb/factory.hpp index 50b3299ed49..3012ba14187 100644 --- a/src/tools/kdb/factory.hpp +++ b/src/tools/kdb/factory.hpp @@ -35,7 +35,8 @@ #include #include #include -#include +#include +#include #include #include #include @@ -88,7 +89,6 @@ 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> ())); diff --git a/src/tools/kdb/get.c b/src/tools/kdb/get.c new file mode 100644 index 00000000000..41bdf6e70ca --- /dev/null +++ b/src/tools/kdb/get.c @@ -0,0 +1,229 @@ +/** + * @file + * + * @brief KDB get subcommand + * + * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) + */ + +#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; + bool verbose = false; + Key * tmp = GET_OPTION_KEY (options, "verbose"); + if (tmp != NULL) + { + elektraKeyToBoolean (GET_OPTION_KEY (options, "verbose"), &verbose); + } + + 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 = toLookUp; + 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); + keyDel (parentKey); + + setCallback (toLookUp, warnOnMeta); + if (verbose) + { + printf ("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) + { + printf ("Did not find key '%s'", name); + ret = -1; + goto cleanup; + } + + if (verbose) + { + if (keyGetNamespace (found) == KEY_NS_DEFAULT) + { + printf ("The key was not found in any other namespace, taking the default\n"); + } + printf ("The resulting keyname is %s\n", keyName (found)); + printf ("The resulting value size is %ld\n", keyGetValueSize (found)); + } + if (keyIsBinary (found)) + { + ssize_t binSize = keyGetValueSize (found); + if (verbose) + { + printf ("The key is %s.\n", binSize == 0 ? "null" : "binary"); + } + const char * buffer = keyValue (found); + for (ssize_t pos = 0; pos < binSize; pos++) + { + printf ("%x", buffer[pos]); + } + } + else + { + printf ("%s", keyString (found)); + } + +cleanup: + printf ("\n"); + 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/main.c b/src/tools/kdb/main.c index f6e4f11dd89..9950e2d08ee 100644 --- a/src/tools/kdb/main.c +++ b/src/tools/kdb/main.c @@ -6,6 +6,8 @@ * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) */ +#include + #include #include #include @@ -19,7 +21,7 @@ extern char ** environ; command subcommands[] = { - {NULL, NULL, NULL}, + { "get", addGetSpec, execGet }, }; void printWarnings (Key * errorKey) From b4510f4ab623736e42ef2694f5f4f92415591fb8 Mon Sep 17 00:00:00 2001 From: Hannes Laimer Date: Thu, 22 Sep 2022 19:48:46 +0200 Subject: [PATCH 4/8] kdb-cli: rewrite ls command ... and remove cpp implementation --- src/tools/kdb/factory.hpp | 2 - src/tools/kdb/ls.c | 150 ++++++++++++++++++++++++++++++++++++++ src/tools/kdb/ls.cpp | 122 ------------------------------- src/tools/kdb/ls.h | 36 +++++++++ src/tools/kdb/ls.hpp | 57 --------------- src/tools/kdb/main.c | 2 + 6 files changed, 188 insertions(+), 181 deletions(-) create mode 100644 src/tools/kdb/ls.c delete mode 100644 src/tools/kdb/ls.cpp create mode 100644 src/tools/kdb/ls.h delete mode 100644 src/tools/kdb/ls.hpp diff --git a/src/tools/kdb/factory.hpp b/src/tools/kdb/factory.hpp index 3012ba14187..ef538781074 100644 --- a/src/tools/kdb/factory.hpp +++ b/src/tools/kdb/factory.hpp @@ -39,7 +39,6 @@ #include #include #include -#include #include #include #include @@ -91,7 +90,6 @@ class Factory // TODO: to add a new command, 2.) add a line here -> and you are done 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> ())); diff --git a/src/tools/kdb/ls.c b/src/tools/kdb/ls.c new file mode 100644 index 00000000000..d2e0e983181 --- /dev/null +++ b/src/tools/kdb/ls.c @@ -0,0 +1,150 @@ +/** + * @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) +{ + + Key * 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 verbose = false; + tmp = GET_OPTION_KEY (options, "verbose"); + if (tmp != NULL) + { + elektraKeyToBoolean (GET_OPTION_KEY (options, "verbose"), &verbose); + } + + 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); + if (verbose) + { + printf ("size of all keys: %zd\n", ksGetSize (searchIn)); + } + KeySet * part = ksCut (searchIn, whereToLook); + keyDel (whereToLook); + if (verbose) + { + printf ("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) + { + printf ("%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 index 9950e2d08ee..8e97ad48d39 100644 --- a/src/tools/kdb/main.c +++ b/src/tools/kdb/main.c @@ -7,6 +7,7 @@ */ #include +#include #include #include @@ -22,6 +23,7 @@ extern char ** environ; command subcommands[] = { { "get", addGetSpec, execGet }, + { "ls", addLsSpec, execLs }, }; void printWarnings (Key * errorKey) From 78bc0f8d5927627a0770d6ded17bbd44f818684e Mon Sep 17 00:00:00 2001 From: Hannes Laimer Date: Fri, 23 Sep 2022 06:28:39 +0200 Subject: [PATCH 5/8] kdb-cli: rewrite basename command ... and remove cpp implementation --- src/tools/kdb/basename.c | 58 ++++++++++++++++++++++++++++++++++++++ src/tools/kdb/basename.cpp | 27 ------------------ src/tools/kdb/basename.h | 33 ++++++++++++++++++++++ src/tools/kdb/basename.hpp | 47 ------------------------------ src/tools/kdb/factory.hpp | 2 -- src/tools/kdb/main.c | 2 ++ 6 files changed, 93 insertions(+), 76 deletions(-) create mode 100644 src/tools/kdb/basename.c delete mode 100644 src/tools/kdb/basename.cpp create mode 100644 src/tools/kdb/basename.h delete mode 100644 src/tools/kdb/basename.hpp diff --git a/src/tools/kdb/basename.c b/src/tools/kdb/basename.c new file mode 100644 index 00000000000..08fba02c9b3 --- /dev/null +++ b/src/tools/kdb/basename.c @@ -0,0 +1,58 @@ +/** + * @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) +{ + bool verbose = false; + Key * tmp = GET_OPTION_KEY (options, "verbose"); + if (tmp != NULL) + { + elektraKeyToBoolean (GET_OPTION_KEY (options, "verbose"), &verbose); + } + + 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; + } + printf ("%s", 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/factory.hpp b/src/tools/kdb/factory.hpp index ef538781074..938319adfce 100644 --- a/src/tools/kdb/factory.hpp +++ b/src/tools/kdb/factory.hpp @@ -23,7 +23,6 @@ #include // TODO: to add a new command, 1.) include your header here -#include #include #include #include @@ -121,7 +120,6 @@ class Factory 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/main.c b/src/tools/kdb/main.c index 8e97ad48d39..28e52c12086 100644 --- a/src/tools/kdb/main.c +++ b/src/tools/kdb/main.c @@ -6,6 +6,7 @@ * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) */ +#include #include #include @@ -22,6 +23,7 @@ extern char ** environ; command subcommands[] = { + { "basename", addBasenameSpec, execBasename }, { "get", addGetSpec, execGet }, { "ls", addLsSpec, execLs }, }; From 682fc8f1939b83a9c5f043af598956e246a84b00 Mon Sep 17 00:00:00 2001 From: Hannes Laimer Date: Fri, 23 Sep 2022 07:02:27 +0200 Subject: [PATCH 6/8] kdb-cli: rewrite namespace command ... and remove cpp implementation --- src/tools/kdb/factory.hpp | 2 -- src/tools/kdb/main.c | 2 ++ src/tools/kdb/namespace.c | 64 +++++++++++++++++++++++++++++++++++++ src/tools/kdb/namespace.cpp | 27 ---------------- src/tools/kdb/namespace.h | 33 +++++++++++++++++++ src/tools/kdb/namespace.hpp | 48 ---------------------------- 6 files changed, 99 insertions(+), 77 deletions(-) create mode 100644 src/tools/kdb/namespace.c delete mode 100644 src/tools/kdb/namespace.cpp create mode 100644 src/tools/kdb/namespace.h delete mode 100644 src/tools/kdb/namespace.hpp diff --git a/src/tools/kdb/factory.hpp b/src/tools/kdb/factory.hpp index 938319adfce..dee88b79141 100644 --- a/src/tools/kdb/factory.hpp +++ b/src/tools/kdb/factory.hpp @@ -46,7 +46,6 @@ #include #include #include -#include #include #include #include @@ -119,7 +118,6 @@ class Factory 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 ("dirname", std::make_shared> ())); m_factory.insert (std::make_pair ("validate", std::make_shared> ())); } diff --git a/src/tools/kdb/main.c b/src/tools/kdb/main.c index 28e52c12086..d22f45d0fd4 100644 --- a/src/tools/kdb/main.c +++ b/src/tools/kdb/main.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -26,6 +27,7 @@ command subcommands[] = { { "basename", addBasenameSpec, execBasename }, { "get", addGetSpec, execGet }, { "ls", addLsSpec, execLs }, + { "namespace", addNamespaceSpec, execNamespace }, }; void printWarnings (Key * errorKey) diff --git a/src/tools/kdb/namespace.c b/src/tools/kdb/namespace.c new file mode 100644 index 00000000000..fd6b5bdd7e3 --- /dev/null +++ b/src/tools/kdb/namespace.c @@ -0,0 +1,64 @@ +/** + * @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) +{ + bool verbose = false; + Key * tmp = GET_OPTION_KEY (options, "verbose"); + if (tmp != NULL) + { + elektraKeyToBoolean (GET_OPTION_KEY (options, "verbose"), &verbose); + } + + 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); + printf ("%*.*s", pos, pos, 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 From 4e90973ff7f96b0ff346b96d3a6e848411d83c72 Mon Sep 17 00:00:00 2001 From: Hannes Laimer Date: Sun, 25 Sep 2022 16:56:04 +0200 Subject: [PATCH 7/8] kdb-cli: rewrite dirname command ... and remove cpp implementation --- src/tools/kdb/dirname.c | 72 +++++++++++++++++++++++++++++++++++++++ src/tools/kdb/dirname.cpp | 27 --------------- src/tools/kdb/dirname.h | 33 ++++++++++++++++++ src/tools/kdb/dirname.hpp | 47 ------------------------- src/tools/kdb/factory.hpp | 2 -- src/tools/kdb/main.c | 2 ++ 6 files changed, 107 insertions(+), 76 deletions(-) create mode 100644 src/tools/kdb/dirname.c delete mode 100644 src/tools/kdb/dirname.cpp create mode 100644 src/tools/kdb/dirname.h delete mode 100644 src/tools/kdb/dirname.hpp diff --git a/src/tools/kdb/dirname.c b/src/tools/kdb/dirname.c new file mode 100644 index 00000000000..3658a96d903 --- /dev/null +++ b/src/tools/kdb/dirname.c @@ -0,0 +1,72 @@ +/** + * @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) +{ + bool verbose = false; + Key * tmp = GET_OPTION_KEY (options, "verbose"); + if (tmp != NULL) + { + elektraKeyToBoolean (GET_OPTION_KEY (options, "verbose"), &verbose); + } + + 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; + } + printf ("%*.*s", dirnameLen, dirnameLen, 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 dee88b79141..22f2f3e94a6 100644 --- a/src/tools/kdb/factory.hpp +++ b/src/tools/kdb/factory.hpp @@ -28,7 +28,6 @@ #include #include #include -#include #include #include #include @@ -118,7 +117,6 @@ class Factory 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 ("dirname", std::make_shared> ())); m_factory.insert (std::make_pair ("validate", std::make_shared> ())); } diff --git a/src/tools/kdb/main.c b/src/tools/kdb/main.c index d22f45d0fd4..6427650eec0 100644 --- a/src/tools/kdb/main.c +++ b/src/tools/kdb/main.c @@ -7,6 +7,7 @@ */ #include +#include #include #include #include @@ -25,6 +26,7 @@ extern char ** environ; command subcommands[] = { { "basename", addBasenameSpec, execBasename }, + { "dirname", addDirnameSpec, execDirname }, { "get", addGetSpec, execGet }, { "ls", addLsSpec, execLs }, { "namespace", addNamespaceSpec, execNamespace }, From 14f7e0174419b87987b2ae9e4536c7c256f8be2f Mon Sep 17 00:00:00 2001 From: Hannes Laimer Date: Mon, 26 Sep 2022 19:40:33 +0200 Subject: [PATCH 8/8] kdb-cli: rewrite cmerge command .. and remove cpp implementation --- src/tools/kdb/cmerge.c | 239 ++++++++++++++++++++++++++++++++++++++ src/tools/kdb/cmerge.cpp | 140 ---------------------- src/tools/kdb/cmerge.h | 37 ++++++ src/tools/kdb/cmerge.hpp | 50 -------- src/tools/kdb/factory.hpp | 2 - src/tools/kdb/main.c | 2 + src/tools/kdb/mount.c | 5 + src/tools/kdb/mount.h | 8 ++ 8 files changed, 291 insertions(+), 192 deletions(-) create mode 100644 src/tools/kdb/cmerge.c delete mode 100644 src/tools/kdb/cmerge.cpp create mode 100644 src/tools/kdb/cmerge.h delete mode 100644 src/tools/kdb/cmerge.hpp create mode 100644 src/tools/kdb/mount.c create mode 100644 src/tools/kdb/mount.h diff --git a/src/tools/kdb/cmerge.c b/src/tools/kdb/cmerge.c new file mode 100644 index 00000000000..3d12568d899 --- /dev/null +++ b/src/tools/kdb/cmerge.c @@ -0,0 +1,239 @@ +/** + * @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) +{ + + // optional args + bool verbose = false; + Key * tmp = GET_OPTION_KEY (options, "verbose"); + if (tmp != NULL) + { + elektraKeyToBoolean (GET_OPTION_KEY (options, "verbose"), &verbose); + } + + 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 && verbose) + { + printf ("will remove %ld keys, because -f was set", ksGetSize (discard)); + } + + 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 = getConflicts (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 8dfe1687601..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); - int 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/factory.hpp b/src/tools/kdb/factory.hpp index 22f2f3e94a6..9e59c8b73ee 100644 --- a/src/tools/kdb/factory.hpp +++ b/src/tools/kdb/factory.hpp @@ -24,7 +24,6 @@ // TODO: to add a new command, 1.) include your header here #include -#include #include #include #include @@ -110,7 +109,6 @@ 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> ())); diff --git a/src/tools/kdb/main.c b/src/tools/kdb/main.c index 6427650eec0..bea9bc11ded 100644 --- a/src/tools/kdb/main.c +++ b/src/tools/kdb/main.c @@ -7,6 +7,7 @@ */ #include +#include #include #include #include @@ -26,6 +27,7 @@ extern char ** environ; command subcommands[] = { { "basename", addBasenameSpec, execBasename }, + { "cmerge", addCmergeSpec, execCmerge }, { "dirname", addDirnameSpec, execDirname }, { "get", addGetSpec, execGet }, { "ls", addLsSpec, execLs }, diff --git a/src/tools/kdb/mount.c b/src/tools/kdb/mount.c new file mode 100644 index 00000000000..3e7c0767d15 --- /dev/null +++ b/src/tools/kdb/mount.c @@ -0,0 +1,5 @@ +// +// Created by hannes on 9/26/22. +// + +#include "mount.h" diff --git a/src/tools/kdb/mount.h b/src/tools/kdb/mount.h new file mode 100644 index 00000000000..57a2ebac01e --- /dev/null +++ b/src/tools/kdb/mount.h @@ -0,0 +1,8 @@ +// +// Created by hannes on 9/26/22. +// + +#ifndef ELEKTRA_MOUNT_H +#define ELEKTRA_MOUNT_H + +#endif // ELEKTRA_MOUNT_H