Skip to content
This repository has been archived by the owner on Oct 15, 2024. It is now read-only.

Commit

Permalink
kdb-cli: add formatted and log-level dependant output
Browse files Browse the repository at this point in the history
... and a GET_BASIC_OPTIONS macro for reading all basic basic options
at the start of a command
  • Loading branch information
hannes99 committed Jan 4, 2023
1 parent 71c7c18 commit 0c19b7c
Show file tree
Hide file tree
Showing 10 changed files with 229 additions and 85 deletions.
9 changes: 2 additions & 7 deletions src/tools/kdb/basename.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
Expand Down
18 changes: 6 additions & 12 deletions src/tools/kdb/cmerge.c
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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)
Expand All @@ -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)
{
Expand Down
29 changes: 29 additions & 0 deletions src/tools/kdb/colors.c
Original file line number Diff line number Diff line change
@@ -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 <colors.h>
#include <kdberrors.h>
#include <string.h>

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;
}
72 changes: 72 additions & 0 deletions src/tools/kdb/colors.h
Original file line number Diff line number Diff line change
@@ -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 <kdb.h>

#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
18 changes: 18 additions & 0 deletions src/tools/kdb/command.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
86 changes: 76 additions & 10 deletions src/tools/kdb/command.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@
#ifndef ELEKTRA_KDB_COMMAND_H
#define ELEKTRA_KDB_COMMAND_H

#include <colors.h>

#include <kdb.h>
#include <stdbool.h>
#include <unistd.h>

#define CLI_BASE_KEY "/sw/elektra/kdb/#0/current"

Expand All @@ -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.", \
Expand All @@ -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; \
Expand All @@ -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);

Expand All @@ -87,12 +136,29 @@ 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;
void (*addSpec) (KeySet * spec);
int (*exec) (KeySet * options, Key * errorKey);
} command;

enum LOG_LEVEL
{
CLI_LOG_NONE = 0,
CLI_LOG_VERBOSE,
CLI_LOG_DEBUG
};

#endif // ELEKTRA_KDB_COMMAND_H
9 changes: 2 additions & 7 deletions src/tools/kdb/dirname.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
Expand Down
Loading

0 comments on commit 0c19b7c

Please sign in to comment.