From 775d0390e38ada8228bbaeb21623715169c6b587 Mon Sep 17 00:00:00 2001 From: Philippe Proulx Date: Wed, 3 Jul 2019 16:37:21 -0400 Subject: [PATCH] Add bt_common_fold() See bt_common_fold()'s comment for more details. The plan is to use this when printing error causes from the CLI. Signed-off-by: Philippe Proulx Change-Id: I57a90da90c0f65ed6909af84dc939c74addfe6eb Reviewed-on: https://review.lttng.org/c/babeltrace/+/1620 Tested-by: jenkins --- src/common/common.c | 120 ++++++++++++++++++++++++++++++++++++++++++++ src/common/common.h | 17 +++++++ 2 files changed, 137 insertions(+) diff --git a/src/common/common.c b/src/common/common.c index ed079c94..aef36b28 100644 --- a/src/common/common.c +++ b/src/common/common.c @@ -1671,3 +1671,123 @@ void bt_common_sep_digits(char *str, unsigned int digits_per_group, char sep) i++; } } + +BT_HIDDEN +GString *bt_common_fold(const char *str, unsigned int total_length, + unsigned int indent) +{ + const unsigned int content_length = total_length - indent; + GString *folded = g_string_new(NULL); + GString *tmp_line = g_string_new(NULL); + gchar **lines = NULL; + gchar **line_words = NULL; + gchar * const *line; + unsigned int i; + + BT_ASSERT(str); + BT_ASSERT(indent < total_length); + BT_ASSERT(tmp_line); + BT_ASSERT(folded); + + if (strlen(str) == 0) { + /* Empty input string: empty output string */ + goto end; + } + + /* Split lines */ + lines = g_strsplit(str, "\n", 0); + BT_ASSERT(lines); + + /* For each source line */ + for (line = lines; *line; line++) { + gchar * const *word; + + /* + * Append empty line without indenting if source line is + * empty. + */ + if (strlen(*line) == 0) { + g_string_append_c(folded, '\n'); + continue; + } + + /* Split words */ + line_words = g_strsplit(*line, " ", 0); + BT_ASSERT(line_words); + + /* + * Indent for first line (we know there's at least one + * word at this point). + */ + for (i = 0; i < indent; i++) { + g_string_append_c(folded, ' '); + } + + /* Append words, folding when necessary */ + g_string_assign(tmp_line, ""); + + for (word = line_words; *word; word++) { + /* + * `tmp_line->len > 0` is in the condition so + * that words that are larger than + * `content_length` are at least written on + * their own line. + * + * `tmp_line->len - 1` because the temporary + * line always contains a trailing space which + * won't be part of the line if we fold. + */ + if (tmp_line->len > 0 && + tmp_line->len - 1 + strlen(*word) >= content_length) { + /* Fold (without trailing space) */ + g_string_append_len(folded, + tmp_line->str, tmp_line->len - 1); + g_string_append_c(folded, '\n'); + + /* Indent new line */ + for (i = 0; i < indent; i++) { + g_string_append_c(folded, ' '); + } + + g_string_assign(tmp_line, ""); + } + + /* Append current word and space to temporary line */ + g_string_append(tmp_line, *word); + g_string_append_c(tmp_line, ' '); + } + + /* Append last line if any, without trailing space */ + if (tmp_line->len > 0) { + g_string_append_len(folded, tmp_line->str, + tmp_line->len - 1); + } + + /* Append source newline */ + g_string_append_c(folded, '\n'); + + /* Free array of this line's words */ + g_strfreev(line_words); + line_words = NULL; + } + + /* Remove trailing newline if any */ + if (folded->str[folded->len - 1] == '\n') { + g_string_truncate(folded, folded->len - 1); + } + +end: + if (lines) { + g_strfreev(lines); + } + + if (line_words) { + g_strfreev(line_words); + } + + if (tmp_line) { + g_string_free(tmp_line, TRUE); + } + + return folded; +} diff --git a/src/common/common.h b/src/common/common.h index 809c2e50..0f2736f0 100644 --- a/src/common/common.h +++ b/src/common/common.h @@ -331,6 +331,23 @@ size_t bt_common_get_page_size(int log_level); BT_HIDDEN void bt_common_sep_digits(char *str, unsigned int digits_per_group, char sep); +/* + * This is similar to what the command `fold --spaces` does: it wraps + * the input lines of `str`, breaking at spaces, and indenting each line + * with `indent` spaces so that each line fits the total length + * `total_length`. + * + * If an original line in `str` contains a word which is >= the content + * length (`total_length - indent`), then the corresponding folded line + * is also larger than the content length. In other words, breaking at + * spaces is a best effort, but it might not be possible. + * + * The returned string, on success, is owned by the caller. + */ +BT_HIDDEN +GString *bt_common_fold(const char *str, unsigned int total_length, + unsigned int indent); + /* * Wraps read() function to handle EINTR and partial reads. * On success, it returns `count` received as parameter. On error, it returns a -- 2.34.1