diff --git a/src/tools/kdb/basename.c b/src/tools/kdb/basename.c index 08fba02c9b3..a2676fb984a 100644 --- a/src/tools/kdb/basename.c +++ b/src/tools/kdb/basename.c @@ -33,12 +33,7 @@ void addBasenameSpec (KeySet * spec) 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); - } + GET_BASIC_OPTIONS const char * name = getKeyNameFromOptions (options, GET_OPTION (options, "name"), errorKey, verbose); if (name == NULL) return 1; @@ -50,7 +45,7 @@ int execBasename (KeySet * options, Key * errorKey) elektraFree ((void *) name); return 1; } - printf ("%s", keyBaseName (key)); + CLI_PRINT (CLI_LOG_NONE, "%s", BOLD (keyBaseName (key))); elektraFree ((void *) name); keyDel (key); diff --git a/src/tools/kdb/cmerge.c b/src/tools/kdb/cmerge.c index cdd3349eb85..1a32d32a616 100644 --- a/src/tools/kdb/cmerge.c +++ b/src/tools/kdb/cmerge.c @@ -76,14 +76,7 @@ int getKeySet (Key * where, KeySet ** ks, char * verboseName, Key * errorKey) 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); - } + GET_BASIC_OPTIONS bool force = false; tmp = GET_OPTION_KEY (options, "force"); @@ -118,7 +111,8 @@ int execCmerge (KeySet * options, Key * errorKey) if (ourpath == NULL) return 1; const char * theirpath = getKeyNameFromOptions (options, GET_OPTION (options, "theirpath"), errorKey, verbose); - if (theirpath == NULL) { + if (theirpath == NULL) + { elektraFree ((void *) ourpath); return 1; } @@ -165,9 +159,9 @@ int execCmerge (KeySet * options, Key * errorKey) ret = 1; goto cleanup; } - else if (ksGetSize (discard) != 0 && verbose) + else if (ksGetSize (discard) != 0) { - printf ("will remove %ld keys, because -f was set", ksGetSize (discard)); + CLI_PRINT (CLI_LOG_VERBOSE, "will remove %ld keys, because %s was set", ksGetSize (discard), BOLD ("-f")); } if (getKeySet (oursRoot, &ours, verbose ? "our" : NULL, errorKey) < 0) @@ -192,7 +186,7 @@ int execCmerge (KeySet * options, Key * errorKey) Key * mergeInfo = keyNew ("/", KEY_END); KeySet * mergeResult = elektraMerge (ours, oursRoot, theirs, theirsRoot, base, baseRoot, resultRoot, strategy, mergeInfo); - int nrConflicts = elektraMergeGetConflicts(mergeInfo); + int nrConflicts = elektraMergeGetConflicts (mergeInfo); keyDel (mergeInfo); if (mergeResult == NULL) { diff --git a/src/tools/kdb/colors.c b/src/tools/kdb/colors.c new file mode 100644 index 00000000000..869c3279e2d --- /dev/null +++ b/src/tools/kdb/colors.c @@ -0,0 +1,29 @@ +/** +* @file +* +* @brief Implementation of ANSI escape helper functions in the kdb tool +* +* @copyright BSD License (see LICENSE.md or https://www.libelektra.org) +*/ + +#include +#include +#include + +const char * formatString (char ** buffer, int colorMode, const char * escapeCode, const char * text) +{ + if (colorMode == CLI_COLOR_NEVER) return text; + if (*buffer == NULL) { + *buffer = elektraMalloc (4096); + } + char * tmp = elektraMalloc (elektraStrLen (escapeCode) + elektraStrLen (text) + elektraStrLen (RESET) + 1); + strcpy (tmp, escapeCode); + strcat (tmp, text); + strcat (tmp, RESET); + + **buffer = '\0'; + strcpy(*buffer, tmp); + elektraFree (tmp); + + return *buffer; +} diff --git a/src/tools/kdb/colors.h b/src/tools/kdb/colors.h new file mode 100644 index 00000000000..b736f0151ca --- /dev/null +++ b/src/tools/kdb/colors.h @@ -0,0 +1,72 @@ +/** + * @file + * + * @brief Header for colored CLI output + * + * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) + */ + +#ifndef ELEKTRA_KDB_COLOR_H +#define ELEKTRA_KDB_COLOR_H + +#include + +#define RESET "\x1b[0m" + +// colors +#define COLOR_BLACK "\x1b[30m" +#define COLOR_RED "\x1b[31m" +#define COLOR_GREEN "\x1b[32m" +#define COLOR_YELLOW "\x1b[33m" +#define COLOR_BLUE "\x1b[34m" +#define COLOR_MAGENTA "\x1b[35m" +#define COLOR_CYAN "\x1b[36m" +#define COLOR_WHITE "\x1b[37m" + +// background colors +#define BG_COLOR_BLACK "\x1b[40m" +#define BG_COLOR_RED "\x1b[41m" +#define BG_COLOR_GREEN "\x1b[42m" +#define BG_COLOR_YELLOW "\x1b[43m" +#define BG_COLOR_BLUE "\x1b[44m" +#define BG_COLOR_MAGENTA "\x1b[45m" +#define BG_COLOR_CYAN "\x1b[46m" +#define BG_COLOR_WHITE "\x1b[47m" + +// formatting +#define FORMAT_BOLD "\x1b[1m" +#define FORMAT_UNDERSCORE "\x1b[4m" + +#define FORMAT_TEXT(escapeCode, text) formatString (&fmtBuffer, colorMode, escapeCode, text) + +#define BLACK(text) FORMAT_TEXT (COLOR_BLACK, text) +#define RED(text) FORMAT_TEXT (COLOR_RED, text) +#define GREEN(text) FORMAT_TEXT (COLOR_GREEN, text) +#define YELLOW(text) FORMAT_TEXT (COLOR_YELLOW, text) +#define BLUE(text) FORMAT_TEXT (COLOR_BLUE, text) +#define MAGENTA(text) FORMAT_TEXT (COLOR_MAGENTA, text) +#define CYAN(text) FORMAT_TEXT (COLOR_CYAN, text) +#define WHITE(text) FORMAT_TEXT (COLOR_WHITE, text) + +#define BG_BLACK(text) FORMAT_TEXT (BG_COLOR_BLACK, text) +#define BG_RED(text) FORMAT_TEXT (BG_COLOR_RED, text) +#define BG_GREEN(text) FORMAT_TEXT (BG_COLOR_GREEN, text) +#define BG_YELLOW(text) FORMAT_TEXT (BG_COLOR_YELLOW, text) +#define BG_BLUE(text) FORMAT_TEXT (BG_COLOR_BLUE, text) +#define BG_MAGENTA(text) FORMAT_TEXT (BG_COLOR_MAGENTA, text) +#define BG_CYAN(text) FORMAT_TEXT (BG_COLOR_CYAN, text) +#define BG_WHITE(text) FORMAT_TEXT (BG_COLOR_WHITE, text) + +#define BOLD(text) FORMAT_TEXT (FORMAT_BOLD, text) +#define UNDERSCORE(text) FORMAT_TEXT (FORMAT_UNDERSCORE, text) + +const char * formatString (char ** buffer, int colorMode, const char * color, const char * text); + +enum COLOR_MODE +{ + CLI_COLOR_NEVER, + CLI_COLOR_AUTO, + CLI_COLOR_ALWAYS +}; + +#endif // ELEKTRA_KDB_COLOR_H diff --git a/src/tools/kdb/command.c b/src/tools/kdb/command.c index 6736e8f22f6..f1c1695b110 100644 --- a/src/tools/kdb/command.c +++ b/src/tools/kdb/command.c @@ -74,3 +74,21 @@ const char * getKeyNameFromOptions (KeySet * options, const char * rawName, Key keyDel (key); return result; } + +void cliPrint (char * fmtBuffer, int logLevel, int minLogLevel, const char * fmt, ...) +{ + if (logLevel < minLogLevel) + { + return; + } + + va_list args; + va_start (args, fmt); + vprintf (fmt, args); + va_end (args); + + if (fmtBuffer != NULL) + { + elektraFree (fmtBuffer); + } +} diff --git a/src/tools/kdb/command.h b/src/tools/kdb/command.h index 9167c1345c2..b551967779d 100644 --- a/src/tools/kdb/command.h +++ b/src/tools/kdb/command.h @@ -9,8 +9,11 @@ #ifndef ELEKTRA_KDB_COMMAND_H #define ELEKTRA_KDB_COMMAND_H +#include + #include #include +#include #define CLI_BASE_KEY "/sw/elektra/kdb/#0/current" @@ -26,7 +29,7 @@ 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, \ + 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.", \ @@ -43,6 +46,49 @@ #define COMMAND_BASE_KEY(name) CLI_BASE_KEY "/" name #define COMMAND_SPEC_KEY(name) "spec:" COMMAND_BASE_KEY (name) +// only use in the context of a command/sub-command (options variable and GET_OPTION_KEY macro have to be in scope) +#define GET_BASIC_OPTIONS \ + bool debug = false; \ + Key * tmp = GET_OPTION_KEY (options, "debug"); \ + if (tmp != NULL) \ + { \ + elektraKeyToBoolean (GET_OPTION_KEY (options, "debug"), &debug); \ + } \ + /* debug -> verbose, so logLevel = debug+verbose */ \ + bool verbose = debug; \ + tmp = GET_OPTION_KEY (options, "verbose"); \ + if (tmp != NULL) \ + { \ + elektraKeyToBoolean (GET_OPTION_KEY (options, "verbose"), &verbose); \ + } \ + bool noNewLine = false; \ + tmp = GET_OPTION_KEY (options, "nonewline"); \ + if (tmp != NULL) \ + { \ + elektraKeyToBoolean (GET_OPTION_KEY (options, "nonewline"), &noNewLine); \ + } \ + int colorMode = CLI_COLOR_AUTO; \ + tmp = GET_OPTION_KEY (options, "color"); \ + if (tmp != NULL) \ + { \ + if (elektraStrCmp ("never", keyString (tmp)) == 0) \ + { \ + colorMode = CLI_COLOR_NEVER; \ + } \ + if (elektraStrCmp ("always", keyString (tmp)) == 0) \ + { \ + colorMode = CLI_COLOR_ALWAYS; \ + } \ + } \ + char * fmtBuffer = NULL; \ + if (!isatty (STDOUT_FILENO)) \ + { \ + colorMode = CLI_COLOR_NEVER; \ + } \ + \ + int logLevel = verbose + debug; \ + keyDel (tmp); + #define EXEC_EXT(prog, argv, status) \ pid_t extPid; \ int timeout = 1000; \ @@ -64,15 +110,18 @@ 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 +// only print if we are at least as 'verbose' as minLogLevel +#define CLI_PRINT(minLogLevel, fmt, ...) cliPrint (fmtBuffer, logLevel, minLogLevel, fmt, __VA_ARGS__) + +/** \ + * Expands a keyname if it contains a bookmark. If @name does not contain a bookmark ref a copy of @name is returned. \ + * \ + * @param name the keyname that might contain a bookmark, and where the expanded name should be saved \ + * @param ks keyset that contains information about the bookmarks \ + * @param resolved will be set to true iff a bookmark was resolved successfully \ + * \ + * @return NULL if the bookmark could not be resolved, NULL was passed as @ks or @name \ + * @return string of the full key otherwise, has to be freed after usage \ */ const char * expandKeyName (KeySet * ks, const char * name, bool * resolved); @@ -87,6 +136,17 @@ const char * expandKeyName (KeySet * ks, const char * name, bool * resolved); */ const char * getKeyNameFromOptions (KeySet * options, const char * rawName, Key * errorKey, bool verbose); +/** + * Helper for printing, handles log levels + * + * @param fmtBuffer buffer used for format string, will be freed after printing + * @param logLevel the log level set by the user + * @param minLogLevel minimum log level so the message is printed (NONE -> always print, VERBOSE -> printf if VERBOSE or DEBUG, ...) + * @param fmt format string for printing + * @param ... + */ +void cliPrint (char * fmtBuffer, int logLevel, int minLogLevel, const char * fmt, ...); + typedef struct command { const char * name; @@ -94,5 +154,11 @@ typedef struct command int (*exec) (KeySet * options, Key * errorKey); } command; +enum LOG_LEVEL +{ + CLI_LOG_NONE = 0, + CLI_LOG_VERBOSE, + CLI_LOG_DEBUG +}; #endif // ELEKTRA_KDB_COMMAND_H diff --git a/src/tools/kdb/dirname.c b/src/tools/kdb/dirname.c index 3658a96d903..f4befea9a89 100644 --- a/src/tools/kdb/dirname.c +++ b/src/tools/kdb/dirname.c @@ -33,12 +33,7 @@ void addDirnameSpec (KeySet * spec) 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); - } + GET_BASIC_OPTIONS const char * name = getKeyNameFromOptions (options, GET_OPTION (options, "name"), errorKey, verbose); if (name == NULL) return 1; @@ -64,7 +59,7 @@ int execDirname (KeySet * options, Key * errorKey) { dirnameLen = 1; } - printf ("%*.*s", dirnameLen, dirnameLen, name); + CLI_PRINT (CLI_LOG_NONE, "%*.*s", dirnameLen, dirnameLen, BOLD (name)); elektraFree ((void *) name); keyDel (key); diff --git a/src/tools/kdb/get.c b/src/tools/kdb/get.c index b69902c1740..5b4e8433a42 100644 --- a/src/tools/kdb/get.c +++ b/src/tools/kdb/get.c @@ -6,6 +6,7 @@ * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) */ +#include #include #include #include @@ -35,12 +36,7 @@ void addGetSpec (KeySet * spec) 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); - } + GET_BASIC_OPTIONS bool all = false; tmp = GET_OPTION_KEY (options, "all"); @@ -84,7 +80,7 @@ int execGet (KeySet * options, Key * errorKey) setCallback (toLookUp, warnOnMeta); if (verbose) { - printf ("got %ld keys\n", ksGetSize (searchIn)); + CLI_PRINT (CLI_LOG_VERBOSE, "got %ld keys\n", ksGetSize (searchIn)); setCallback (toLookUp, printTrace); } @@ -100,41 +96,38 @@ int execGet (KeySet * options, Key * errorKey) if (found == NULL) { - printf ("Did not find key '%s'", name); + CLI_PRINT (CLI_LOG_NONE, "Did not find key '%s'", RED (name)); ret = -1; goto cleanup; } - if (verbose) + if (keyGetNamespace (found) == KEY_NS_DEFAULT) { - 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)); + CLI_PRINT (CLI_LOG_VERBOSE, "The key was not found in any other namespace, taking the %s\n", BOLD ("default")); } + CLI_PRINT (CLI_LOG_VERBOSE, "The resulting keyname is %s\n", keyName (found)); + CLI_PRINT (CLI_LOG_VERBOSE, "The resulting value size is %ld\n", keyGetValueSize (found)); + if (keyIsBinary (found)) { ssize_t binSize = keyGetValueSize (found); - if (verbose) - { - printf ("The key is %s.\n", binSize == 0 ? "null" : "binary"); - } + CLI_PRINT (CLI_LOG_VERBOSE, "The key is %s.\n", BOLD (binSize == 0 ? "null" : "binary")); const char * buffer = keyValue (found); for (ssize_t pos = 0; pos < binSize; pos++) { - printf ("%x", buffer[pos]); + CLI_PRINT (CLI_LOG_NONE, "%x", buffer[pos]); } } else { - printf ("%s", keyString (found)); + CLI_PRINT (CLI_LOG_NONE, "%s", keyString (found)); } cleanup: - printf ("\n"); - + if (!noNewLine) + { + printf ("\n"); + } keyDel (parentKey); ksDel (searchIn); keyDel (toLookUp); diff --git a/src/tools/kdb/ls.c b/src/tools/kdb/ls.c index d2e0e983181..62758c3044b 100644 --- a/src/tools/kdb/ls.c +++ b/src/tools/kdb/ls.c @@ -42,8 +42,9 @@ void addLsSpec (KeySet * spec) int execLs (KeySet * options, Key * errorKey) { + GET_BASIC_OPTIONS - Key * tmp = GET_OPTION_KEY (options, "mindepth"); + tmp = GET_OPTION_KEY (options, "mindepth"); kdb_long_t minDepth = 0; if (tmp != NULL) { @@ -57,7 +58,6 @@ int execLs (KeySet * options, Key * errorKey) 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"); @@ -74,14 +74,6 @@ int execLs (KeySet * options, Key * errorKey) 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) @@ -108,16 +100,11 @@ int execLs (KeySet * options, Key * errorKey) return -1; } kdbClose (handle, errorKey); - if (verbose) - { - printf ("size of all keys: %zd\n", ksGetSize (searchIn)); - } + + CLI_PRINT (CLI_LOG_VERBOSE, "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)); - } + CLI_PRINT (CLI_LOG_VERBOSE, "size of requested keys: %zd\n", ksGetSize (part)); Key * cur = NULL; for (elektraCursor it = 0; it < ksGetSize (part); ++it) @@ -126,7 +113,7 @@ int execLs (KeySet * options, Key * errorKey) int currentDepth = getKeyNameDepth (keyName (cur)); if ((maxDepth == -1 || currentDepth < rootDepth + maxDepth) && currentDepth >= rootDepth + minDepth) { - printf ("%s%c", keyName (cur), nullTerm ? '\0' : '\n'); + CLI_PRINT (CLI_LOG_NONE, "%s%c", keyName (cur), nullTerm ? '\0' : '\n'); } } diff --git a/src/tools/kdb/namespace.c b/src/tools/kdb/namespace.c index fd6b5bdd7e3..81434a771d1 100644 --- a/src/tools/kdb/namespace.c +++ b/src/tools/kdb/namespace.c @@ -33,12 +33,7 @@ void addNamespaceSpec (KeySet * spec) 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); - } + GET_BASIC_OPTIONS const char * name = getKeyNameFromOptions (options, GET_OPTION (options, "name"), errorKey, verbose); if (name == NULL) return 1; @@ -55,7 +50,7 @@ int execNamespace (KeySet * options, Key * errorKey) if (ns != KEY_NS_NONE && ns != KEY_NS_CASCADING) { int pos = (int) (strchr (name, '/') - name); - printf ("%*.*s", pos, pos, name); + CLI_PRINT (CLI_LOG_NONE, "%*.*s", pos, pos, BOLD (name)); } elektraFree ((void *) name);