All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 01/14] client: add generic display function for table rows
@ 2022-07-06 21:28 James Prestwood
  2022-07-06 21:28 ` [PATCH 02/14] client: remove newline/tab encoding from help description James Prestwood
                   ` (12 more replies)
  0 siblings, 13 replies; 14+ messages in thread
From: James Prestwood @ 2022-07-06 21:28 UTC (permalink / raw)
  To: iwd; +Cc: James Prestwood

There was no easy to use API for printing the contents of a table, and
was left up to the caller to handle manually. This adds display_table_row
which makes displaying tables much easier, including automatic support
for line truncation and continuation on the next line.

Lines which are too long will be truncated and displayed on the next
line while also taking into account any colored output. This works with any
number of columns.

This removes the need for the module to play games with encoding newlines
and tabs to make the output look nice.

As a start, this functionality was added to the command display.
---
 client/display.c | 174 ++++++++++++++++++++++++++++++++++++++++++++++-
 client/display.h |   1 +
 2 files changed, 172 insertions(+), 3 deletions(-)

diff --git a/client/display.c b/client/display.c
index 07cb7bda..7ca3a767 100644
--- a/client/display.c
+++ b/client/display.c
@@ -376,8 +376,176 @@ void display_table_footer(void)
 	display_refresh_check_applicability();
 }
 
-void display_command_line(const char *command_family,
-						const struct command *cmd)
+#define COLOR_END(_str) \
+({ \
+	char *_s = (_str); \
+	while (*_s != 'm' && *_s != '\0') \
+		_s++; \
+	_s - (_str) + 1; \
+})
+
+/*
+ * Finds last space in 's' before 'max' characters, terminates at that index,
+ * and returns a new string to be printed on the next line.
+ *
+ * 'max' should be set to the column width, but is also an out parameter since
+ * this width can be updated if colored escapes are detected.
+ *
+ * Any colored escapes found are set to 'color_out' so they can be re-enabled
+ * on the next line.
+ */
+static char* next_line(char *s, unsigned int *max, char **color_out)
+{
+	unsigned int i;
+	int last_space = -1;
+	int last_color = -1;
+
+	/* Find the last space before 'max', as well as any color */
+	for (i = 0; i <= *max && s[i] != '\0'; i++) {
+		if (s[i] == ' ')
+			last_space = i;
+		else if (s[i] == 0x1b) {
+			/* color escape won't count for column width */
+			*max += COLOR_END(s + i);
+			last_color = i;
+		}
+	}
+
+	/* Reached the end of the string within the column bounds */
+	if (i <= *max)
+		return NULL;
+
+	/* Not anywhere nice to split the line */
+	if (last_space == -1)
+		last_space = *max - 1;
+
+	/*
+	 * Only set the color if it occurred prior to the last space. If after,
+	 * it will get picked up on the next line.
+	 */
+	if (last_color != -1 && last_space >= last_color)
+		*color_out = l_strndup(s + last_color,
+					COLOR_END(s + last_color));
+	else
+		*color_out = NULL;
+
+	s[last_space] = '\0';
+
+	return l_strdup(s + last_space + 1);
+}
+
+struct table_entry {
+	unsigned int width;
+	char *next;
+	char *color;
+};
+
+/*
+ * Appends the next line from 'e' to 'line_buf'. 'done' is only set false when
+ * there are more lines needed for the current entry.
+ */
+static int entry_append(struct table_entry *e, char *line_buf, bool *done)
+{
+	char *value = e->next;
+	unsigned int ret = 0;
+	unsigned int width = e->width;
+
+	/* Empty line */
+	if (!value)
+		return sprintf(line_buf, "%-*s  ", e->width, "");
+
+	/* Color from previous line */
+	if (e->color) {
+		ret = sprintf(line_buf, "%s", e->color);
+		l_free(e->color);
+		e->color = NULL;
+	}
+
+	/* Advance entry to next line, and terminate current */
+	e->next = next_line(value, &width, &e->color);
+
+	if (e->next)
+		*done = false;
+
+	/* Append current line */
+	ret += sprintf(line_buf + ret, "%-*s  ", width, value);
+
+	l_free(value);
+
+	/* Un-color output for next column */
+	if (e->color)
+		ret += sprintf(line_buf + ret, "%s", COLOR_OFF);
+
+	return ret;
+}
+
+/*
+ * Expects an initial margin, number of columns in table, then row data:
+ *
+ * <row width>, <row data>, ...
+ *
+ * The data string can be of any length, and will be split into new lines of
+ * length <row width>.
+ */
+void display_table_row(const char *margin, unsigned int ncolumns, ...)
+{
+	char buf[512];
+	char *str = buf;
+	unsigned int i;
+	struct table_entry entries[ncolumns];
+	va_list va;
+	bool done = true;
+
+	memset(&entries[0], 0, sizeof(entries));
+
+	va_start(va, ncolumns);
+
+	str += sprintf(str, "%s", margin);
+
+	for (i = 0; i < ncolumns; i++) {
+		struct table_entry *e = &entries[i];
+
+		e->width = va_arg(va, unsigned int);
+		e->next = l_strdup(va_arg(va, char*));
+
+		str += entry_append(e, str, &done);
+	}
+
+	va_end(va);
+
+	display("%s\n", buf);
+	str = buf;
+
+	/*
+	 * The first column should now be indented, which effects the entry
+	 * width. Subtract this indentation only from the first column.
+	 */
+	entries[0].width -= strlen(margin) * 2;
+
+	while (!done) {
+		done = true;
+
+		for (i = 0; i < ncolumns; i++) {
+			struct table_entry *e = &entries[i];
+
+			if (i == 0)
+				str += sprintf(str, "%s%s%s", margin,
+						margin, margin);
+
+			str += entry_append(e, str, &done);
+		}
+
+		display("%s\n", buf);
+		str = buf;
+	}
+
+	for (i = 0; i < ncolumns; i++) {
+		if (entries[i].color)
+			l_free(entries[i].color);
+	}
+}
+
+void display_command_line(const char *command_family, const struct command *cmd)
 {
 	char *cmd_line = l_strdup_printf("%s%s%s%s%s%s%s",
 				command_family ? : "",
@@ -388,7 +556,7 @@ void display_command_line(const char *command_family,
 				cmd->arg ? " " : "",
 				cmd->arg ? : "");
 
-	display(MARGIN "%-*s%s\n", 50, cmd_line, cmd->desc ? : "");
+	display_table_row(MARGIN, 2, 50, cmd_line, 30, cmd->desc);
 
 	l_free(cmd_line);
 }
diff --git a/client/display.h b/client/display.h
index 7747c6a0..c34cab9c 100644
--- a/client/display.h
+++ b/client/display.h
@@ -37,6 +37,7 @@ void display(const char *format, ...)
 		__attribute__((format(printf, 1, 2)));
 void display_table_header(const char *caption, const char *fmt, ...)
 		__attribute__((format(printf, 2, 3)));
+void display_table_row(const char *margin, unsigned int ncolumns, ...);
 void display_table_footer(void);
 void display_error(const char *error);
 void display_command_line(const char *command_family,
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2022-07-06 21:31 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-07-06 21:28 [PATCH 01/14] client: add generic display function for table rows James Prestwood
2022-07-06 21:28 ` [PATCH 02/14] client: remove newline/tab encoding from help description James Prestwood
2022-07-06 21:28 ` [PATCH 03/14] client: update dbus-proxy to use display_table_row James Prestwood
2022-07-06 21:28 ` [PATCH 04/14] client: update station " James Prestwood
2022-07-06 21:28 ` [PATCH 05/14] client: dpp: display table footer and set to auto update James Prestwood
2022-07-06 21:28 ` [PATCH 06/14] client: check NULL return for DPP cmd_show James Prestwood
2022-07-06 21:28 ` [PATCH 07/14] client: update CLEAR_SCREEN to be consistent with others James Prestwood
2022-07-06 21:28 ` [PATCH 08/14] client: make COLOR_* macros take a string input James Prestwood
2022-07-06 21:28 ` [PATCH 09/14] client: update ap to use display_table_row James Prestwood
2022-07-06 21:28 ` [PATCH 10/14] client: update known-networks " James Prestwood
2022-07-06 21:28 ` [PATCH 11/14] client: update command table header James Prestwood
2022-07-06 21:28 ` [PATCH 12/14] client: update device to use display_table_row James Prestwood
2022-07-06 21:28 ` [PATCH 13/14] client: update adapter table header James Prestwood
2022-07-06 21:28 ` [PATCH 14/14] client: update ad-hoc " James Prestwood

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.