From 55693e45d376268ecc126c2d3718ad4e017cc88b Mon Sep 17 00:00:00 2001 From: Hannes Laimer Date: Wed, 21 Sep 2022 07:12:08 +0200 Subject: [PATCH] kdb-cli: rewrite get command ... and remove cpp implementation --- src/tools/kdb/factory.hpp | 2 - 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, 275 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 dae8cb2d0df..13093a7e786 100644 --- a/src/tools/kdb/factory.hpp +++ b/src/tools/kdb/factory.hpp @@ -35,7 +35,6 @@ #include #include #include -#include #include #include #include @@ -89,7 +88,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..643a34636ed --- /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; + elektraKeyToBoolean (GET_OPTION_KEY (options, "verbose"), &verbose); + + bool all = false; + elektraKeyToBoolean (GET_OPTION_KEY (options, "all"), &all); + + const char * rawName = GET_OPTION (options, "name"); + bool resolved = false; + char * name = expandKeyName (options, rawName, &resolved); + if (name == NULL) + { + ELEKTRA_SET_VALIDATION_SEMANTIC_ERRORF (errorKey, "could not resolve bookmark in '%s'", rawName); + return -1; + } + if (verbose && resolved) + { + printf ("resolved bookmark: \'%s\' -> \'%s\'\n", rawName, name); + } + + KeySet * searchIn = ksNew (0, KS_END); + KDB * handle = kdbOpen (NULL, errorKey); + Key * toLookUp = keyNew (name, KEY_END); + 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) + { + 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"); + } + char * buffer = elektraMalloc (binSize); + if (buffer == NULL) + { + ELEKTRA_SET_RESOURCE_ERROR (errorKey, "could not allocate memory for key bin value"); + } + else + { + keyGetBinary (found, buffer, sizeof buffer); + for (ssize_t pos = 0; pos < binSize; pos++) + { + printf ("%x", buffer[pos]); + } + elektraFree (buffer); + } + } + else + { + printf ("%s", keyString (found)); + } + } + else + { + printf ("Did not find key '%s'", name); + ret = 1; + } + 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..e91059c9027 --- /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)