libnbd/0002-info-Add-limited-colourized-output.patch
2022-08-02 15:58:29 +01:00

401 lines
12 KiB
Diff

From bf4a8911c992291e6943d5e936732c290ea2abb9 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Tue, 2 Aug 2022 10:38:56 +0100
Subject: [PATCH 2/3] info: Add limited colourized output
For terminal, non-JSON output, colourize some of the output. We can
work on making this better later. Use --no-colour (or output to a
non-terminal) to disable colours.
---
info/main.c | 25 ++++++++++++-
info/map.c | 95 ++++++++++++++++++++++++++++++++++++++----------
info/nbdinfo.pod | 11 ++++++
info/show.c | 38 ++++++++++++++-----
4 files changed, 138 insertions(+), 31 deletions(-)
diff --git a/info/main.c b/info/main.c
index 870abb4623..a4550e2294 100644
--- a/info/main.c
+++ b/info/main.c
@@ -1,5 +1,5 @@
/* NBD client library in userspace
- * Copyright (C) 2020-2021 Red Hat Inc.
+ * Copyright (C) 2020-2022 Red Hat Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -30,6 +30,7 @@
#include <libnbd.h>
+#include "ansi-colours.h"
#include "version.h"
#include "nbdinfo.h"
@@ -37,6 +38,7 @@
const char *progname;
struct nbd_handle *nbd;
FILE *fp; /* output file descriptor */
+bool colour; /* --colour / --no-colour option */
bool list_all = false; /* --list option */
bool probe_content = false; /* --content / --no-content option */
bool json_output = false; /* --json option */
@@ -93,6 +95,8 @@ main (int argc, char *argv[])
HELP_OPTION = CHAR_MAX + 1,
LONG_OPTIONS,
SHORT_OPTIONS,
+ COLOUR_OPTION,
+ NO_COLOUR_OPTION,
CONTENT_OPTION,
NO_CONTENT_OPTION,
JSON_OPTION,
@@ -105,6 +109,14 @@ main (int argc, char *argv[])
const struct option long_options[] = {
{ "help", no_argument, NULL, HELP_OPTION },
{ "can", required_argument, NULL, CAN_OPTION },
+ { "color", no_argument, NULL, COLOUR_OPTION },
+ { "colors", no_argument, NULL, COLOUR_OPTION },
+ { "colour", no_argument, NULL, COLOUR_OPTION },
+ { "colours", no_argument, NULL, COLOUR_OPTION },
+ { "no-color", no_argument, NULL, NO_COLOUR_OPTION },
+ { "no-colors", no_argument, NULL, NO_COLOUR_OPTION },
+ { "no-colour", no_argument, NULL, NO_COLOUR_OPTION },
+ { "no-colours", no_argument, NULL, NO_COLOUR_OPTION },
{ "content", no_argument, NULL, CONTENT_OPTION },
{ "no-content", no_argument, NULL, NO_CONTENT_OPTION },
{ "is", required_argument, NULL, CAN_OPTION },
@@ -127,6 +139,7 @@ main (int argc, char *argv[])
bool list_okay = true;
progname = argv[0];
+ colour = isatty (STDOUT_FILENO);
for (;;) {
c = getopt_long (argc, argv, short_options, long_options, NULL);
@@ -156,6 +169,14 @@ main (int argc, char *argv[])
json_output = true;
break;
+ case COLOUR_OPTION:
+ colour = true;
+ break;
+
+ case NO_COLOUR_OPTION:
+ colour = false;
+ break;
+
case CONTENT_OPTION:
content_flag = true;
break;
@@ -288,10 +309,12 @@ main (int argc, char *argv[])
if (!json_output) {
if (protocol) {
+ ansi_colour (ANSI_FG_MAGENTA, fp);
fprintf (fp, "protocol: %s", protocol);
if (tls_negotiated >= 0)
fprintf (fp, " %s TLS", tls_negotiated ? "with" : "without");
fprintf (fp, "\n");
+ ansi_restore (fp);
}
}
else {
diff --git a/info/map.c b/info/map.c
index 39c5933bf5..a5aad95522 100644
--- a/info/map.c
+++ b/info/map.c
@@ -30,6 +30,7 @@
#include <libnbd.h>
+#include "ansi-colours.h"
#include "minmax.h"
#include "vector.h"
@@ -99,7 +100,9 @@ do_map (void)
/* Callback handling --map. */
static void print_one_extent (uint64_t offset, uint64_t len, uint32_t type);
-static char *extent_description (const char *metacontext, uint32_t type);
+static void extent_description (const char *metacontext, uint32_t type,
+ char **descr, bool *free_descr,
+ const char **fg, const char **bg);
static int
extent_callback (void *user_data, const char *metacontext,
@@ -169,15 +172,25 @@ static void
print_one_extent (uint64_t offset, uint64_t len, uint32_t type)
{
static bool comma = false;
- char *descr = extent_description (map, type);
+ char *descr;
+ bool free_descr;
+ const char *fg, *bg;
+
+ extent_description (map, type, &descr, &free_descr, &fg, &bg);
if (!json_output) {
+ if (fg)
+ ansi_colour (fg, fp);
+ if (bg)
+ ansi_colour (bg, fp);
fprintf (fp, "%10" PRIu64 " "
"%10" PRIu64 " "
"%3" PRIu32,
offset, len, type);
if (descr)
fprintf (fp, " %s", descr);
+ if (fg || bg)
+ ansi_restore (fp);
fprintf (fp, "\n");
}
else {
@@ -196,7 +209,8 @@ print_one_extent (uint64_t offset, uint64_t len, uint32_t type)
comma = true;
}
- free (descr);
+ if (free_descr)
+ free (descr);
}
/* --map --totals suboption */
@@ -237,14 +251,24 @@ print_totals (uint32_vector *entries, int64_t size)
}
if (c > 0) {
- char *descr = extent_description (map, type);
+ char *descr;
+ bool free_descr;
+ const char *fg, *bg;
double percent = 100.0 * c / size;
+ extent_description (map, type, &descr, &free_descr, &fg, &bg);
+
if (!json_output) {
+ if (fg)
+ ansi_colour (fg, fp);
+ if (bg)
+ ansi_colour (bg, fp);
fprintf (fp, "%10" PRIu64 " %5.1f%% %3" PRIu32,
c, percent, type);
if (descr)
fprintf (fp, " %s", descr);
+ if (fg || bg)
+ ansi_restore (fp);
fprintf (fp, "\n");
}
else {
@@ -264,7 +288,8 @@ print_totals (uint32_vector *entries, int64_t size)
comma = true;
}
- free (descr);
+ if (free_descr)
+ free (descr);
}
if (next_type == (uint64_t)UINT32_MAX + 1)
@@ -275,37 +300,67 @@ print_totals (uint32_vector *entries, int64_t size)
if (json_output) fprintf (fp, "\n]\n");
}
-static char *
-extent_description (const char *metacontext, uint32_t type)
+static void
+extent_description (const char *metacontext, uint32_t type,
+ char **descr, bool *free_descr,
+ const char **fg, const char **bg)
{
- char *ret;
-
if (strcmp (metacontext, "base:allocation") == 0) {
switch (type) {
- case 0: return strdup ("data");
- case 1: return strdup ("hole");
- case 2: return strdup ("zero");
- case 3: return strdup ("hole,zero");
+ case 0:
+ *descr = "data"; *free_descr = false;
+ *fg = ANSI_FG_BOLD_BLACK; *bg = NULL;
+ return;
+ case 1:
+ *descr = "hole"; *free_descr = false;
+ *fg = *bg = NULL;
+ return;
+ case 2:
+ *descr = "zero"; *free_descr = false;
+ *fg = *bg = NULL;
+ return;
+ case 3:
+ *descr = "hole,zero"; *free_descr = false;
+ *fg = *bg = NULL;
+ return;
}
}
else if (strncmp (metacontext, "qemu:dirty-bitmap:", 18) == 0) {
switch (type) {
- case 0: return strdup ("clean");
- case 1: return strdup ("dirty");
+ case 0:
+ *descr = "clean"; *free_descr = false;
+ *fg = ANSI_FG_GREEN; *bg = NULL;
+ return;
+ case 1:
+ *descr = "dirty"; *free_descr = false;
+ *fg = ANSI_FG_RED; *bg = NULL;
+ return;
}
}
else if (strcmp (metacontext, "qemu:allocation-depth") == 0) {
switch (type) {
- case 0: return strdup ("absent");
- case 1: return strdup ("local");
+ case 0:
+ *descr = "absent"; *free_descr = false;
+ *fg = *bg = NULL;
+ return;
+ case 1:
+ *descr = "local"; *free_descr = false;
+ *fg = ANSI_FG_BRIGHT_WHITE; *bg = ANSI_BG_BLACK;
+ return;
default:
- if (asprintf (&ret, "backing depth %u", type) == -1) {
+ if (asprintf (descr, "backing depth %u", type) == -1) {
perror ("asprintf");
exit (EXIT_FAILURE);
}
- return ret;
+ *free_descr = true;
+ *fg = NULL; *bg = ANSI_BG_LIGHT_GREY;
+ return;
}
}
- return NULL; /* Don't know - description field will be omitted. */
+ /* Don't know - description field will be omitted. */
+ *descr = NULL;
+ *free_descr = false;
+ *fg = NULL;
+ *bg = NULL;
}
diff --git a/info/nbdinfo.pod b/info/nbdinfo.pod
index 4733ecd1f7..7dfb9edb60 100644
--- a/info/nbdinfo.pod
+++ b/info/nbdinfo.pod
@@ -330,6 +330,17 @@ L<nbd_can_df(3)>, L<nbd_can_fast_zero(3)>, L<nbd_can_flush(3)>,
L<nbd_can_fua(3)>, L<nbd_can_multi_conn(3)>, L<nbd_can_trim(3)>,
L<nbd_can_zero(3)>, L<nbd_is_read_only(3)>.
+=item B<--color>
+
+=item B<--colour>
+
+=item B<--no-color>
+
+=item B<--no-colour>
+
+Enable or disable ANSI colours in output. By default we use colours
+if the output seems to be a terminal, and disable them if not.
+
=item B<--content>
=item B<--no-content>
diff --git a/info/show.c b/info/show.c
index 3a436665bf..140220d8c6 100644
--- a/info/show.c
+++ b/info/show.c
@@ -28,11 +28,13 @@
#include <libnbd.h>
+#include "ansi-colours.h"
#include "human-size.h"
#include "string-vector.h"
#include "nbdinfo.h"
+static void show_boolean (const char *name, bool cond);
static int collect_context (void *opaque, const char *name);
static char *get_content (struct nbd_handle *, int64_t size);
@@ -119,6 +121,7 @@ show_one_export (struct nbd_handle *nbd, const char *desc,
content = get_content (nbd, size);
if (!json_output) {
+ ansi_colour (ANSI_FG_BRIGHT_BLUE, fp);
fprintf (fp, "export=");
/* Might as well use the JSON function to get an escaped string here ... */
print_json_string (export_name);
@@ -133,35 +136,38 @@ show_one_export (struct nbd_handle *nbd, const char *desc,
fprintf (fp, "\tcontent: %s\n", content);
if (uri)
fprintf (fp, "\turi: %s\n", uri);
+ ansi_restore (fp);
+ ansi_colour (ANSI_FG_BLUE, fp);
if (show_context) {
fprintf (fp, "\tcontexts:\n");
for (i = 0; i < contexts.len; ++i)
fprintf (fp, "\t\t%s\n", contexts.ptr[i]);
}
+ ansi_restore (fp);
+ ansi_colour (ANSI_FG_MAGENTA, fp);
if (is_rotational >= 0)
fprintf (fp, "\t%s: %s\n", "is_rotational",
is_rotational ? "true" : "false");
if (is_read_only >= 0)
fprintf (fp, "\t%s: %s\n", "is_read_only",
is_read_only ? "true" : "false");
+ ansi_restore (fp);
if (can_cache >= 0)
- fprintf (fp, "\t%s: %s\n", "can_cache", can_cache ? "true" : "false");
+ show_boolean ("can_cache", can_cache);
if (can_df >= 0)
- fprintf (fp, "\t%s: %s\n", "can_df", can_df ? "true" : "false");
+ show_boolean ("can_df", can_df);
if (can_fast_zero >= 0)
- fprintf (fp, "\t%s: %s\n", "can_fast_zero",
- can_fast_zero ? "true" : "false");
+ show_boolean ("can_fast_zero", can_fast_zero);
if (can_flush >= 0)
- fprintf (fp, "\t%s: %s\n", "can_flush", can_flush ? "true" : "false");
+ show_boolean ("can_flush", can_flush);
if (can_fua >= 0)
- fprintf (fp, "\t%s: %s\n", "can_fua", can_fua ? "true" : "false");
+ show_boolean ("can_fua", can_fua);
if (can_multi_conn >= 0)
- fprintf (fp, "\t%s: %s\n", "can_multi_conn",
- can_multi_conn ? "true" : "false");
+ show_boolean ("can_multi_conn", can_multi_conn);
if (can_trim >= 0)
- fprintf (fp, "\t%s: %s\n", "can_trim", can_trim ? "true" : "false");
+ show_boolean ("can_trim", can_trim);
if (can_zero >= 0)
- fprintf (fp, "\t%s: %s\n", "can_zero", can_zero ? "true" : "false");
+ show_boolean ("can_zero", can_zero);
if (block_minimum > 0)
fprintf (fp, "\t%s: %" PRId64 "\n", "block_size_minimum", block_minimum);
if (block_preferred > 0)
@@ -269,6 +275,18 @@ show_one_export (struct nbd_handle *nbd, const char *desc,
return true;
}
+/* Used for displaying booleans in non-JSON output. */
+static void
+show_boolean (const char *name, bool cond)
+{
+ if (cond)
+ ansi_colour (ANSI_FG_GREEN, fp);
+ else
+ ansi_colour (ANSI_FG_RED, fp);
+ fprintf (fp, "\t%s: %s\n", name, cond ? "true" : "false");
+ ansi_restore (fp);
+}
+
static int
collect_context (void *opaque, const char *name)
{
--
2.37.0.rc2