basic/escape: allow truncation mode where "…" is always appended

So far we would append "…" or "..." when the string was wider than the specified
output width. But let's add a mode where the caller knows that the string being
passed is already truncated.

The condition for jumping back in utf8_escape_non_printable_full() was
off-by-one. But we only jumped to that label after doing a check with a
stronger condition, so I think it didn't matter. Now it matters because we'd
output the forced ellipsis one column too early.
This commit is contained in:
Zbigniew Jędrzejewski-Szmek
2021-05-05 13:59:23 +02:00
parent b19f211698
commit fc96e5c053
6 changed files with 55 additions and 36 deletions
+12 -4
View File
@@ -368,7 +368,8 @@ char* xescape_full(const char *s, const char *bad, size_t console_width, XEscape
* reversed with cunescape(). If XESCAPE_8_BIT is specified, characters >= 127 are let through
* unchanged. This corresponds to non-ASCII printable characters in pre-unicode encodings.
*
* If console_width is reached, output is truncated and "..." is appended. */
* If console_width is reached, or XESCAPE_FORCE_ELLIPSIS is set, output is truncated and "..." is
* appended. */
if (console_width == 0)
return strdup("");
@@ -380,10 +381,15 @@ char* xescape_full(const char *s, const char *bad, size_t console_width, XEscape
memset(ans, '_', MIN(strlen(s), console_width) * 4);
ans[MIN(strlen(s), console_width) * 4] = 0;
bool force_ellipsis = FLAGS_SET(flags, XESCAPE_FORCE_ELLIPSIS);
for (f = s, t = prev = prev2 = ans; ; f++) {
char *tmp_t = t;
if (!*f) {
if (force_ellipsis)
break;
*t = 0;
return ans;
}
@@ -391,7 +397,7 @@ char* xescape_full(const char *s, const char *bad, size_t console_width, XEscape
if ((unsigned char) *f < ' ' ||
(!FLAGS_SET(flags, XESCAPE_8_BIT) && (unsigned char) *f >= 127) ||
*f == '\\' || strchr(bad, *f)) {
if ((size_t) (t - ans) + 4 > console_width)
if ((size_t) (t - ans) + 4 + 3 * force_ellipsis > console_width)
break;
*(t++) = '\\';
@@ -399,7 +405,7 @@ char* xescape_full(const char *s, const char *bad, size_t console_width, XEscape
*(t++) = hexchar(*f >> 4);
*(t++) = hexchar(*f);
} else {
if ((size_t) (t - ans) + 1 > console_width)
if ((size_t) (t - ans) + 1 + 3 * force_ellipsis > console_width)
break;
*(t++) = *f;
@@ -432,7 +438,9 @@ char* escape_non_printable_full(const char *str, size_t console_width, XEscapeFl
if (FLAGS_SET(flags, XESCAPE_8_BIT))
return xescape_full(str, "", console_width, flags);
else
return utf8_escape_non_printable_full(str, console_width);
return utf8_escape_non_printable_full(str,
console_width,
FLAGS_SET(flags, XESCAPE_FORCE_ELLIPSIS));
}
char* octescape(const char *s, size_t len) {
+1
View File
@@ -56,6 +56,7 @@ int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit,
typedef enum XEscapeFlags {
XESCAPE_8_BIT = 1 << 0,
XESCAPE_FORCE_ELLIPSIS = 1 << 1,
} XEscapeFlags;
char* xescape_full(const char *s, const char *bad, size_t console_width, XEscapeFlags flags);
+8 -4
View File
@@ -212,7 +212,7 @@ static int utf8_char_console_width(const char *str) {
return unichar_iswide(c) ? 2 : 1;
}
char *utf8_escape_non_printable_full(const char *str, size_t console_width) {
char *utf8_escape_non_printable_full(const char *str, size_t console_width, bool force_ellipsis) {
char *p, *s, *prev_s;
size_t n = 0; /* estimated print width */
@@ -229,8 +229,12 @@ char *utf8_escape_non_printable_full(const char *str, size_t console_width) {
int len;
char *saved_s = s;
if (!*str) /* done! */
goto finish;
if (!*str) { /* done! */
if (force_ellipsis)
goto truncation;
else
goto finish;
}
len = utf8_encoded_valid_unichar(str, SIZE_MAX);
if (len > 0) {
@@ -274,7 +278,7 @@ char *utf8_escape_non_printable_full(const char *str, size_t console_width) {
truncation:
/* Try to go back one if we don't have enough space for the ellipsis */
if (n + 1 >= console_width)
if (n + 1 > console_width)
s = prev_s;
s = mempcpy(s, "…", strlen("…"));
+2 -2
View File
@@ -25,9 +25,9 @@ bool utf8_is_printable_newline(const char* str, size_t length, bool allow_newlin
#define utf8_is_printable(str, length) utf8_is_printable_newline(str, length, true)
char *utf8_escape_invalid(const char *s);
char *utf8_escape_non_printable_full(const char *str, size_t console_width);
char *utf8_escape_non_printable_full(const char *str, size_t console_width, bool force_ellipsis);
static inline char *utf8_escape_non_printable(const char *str) {
return utf8_escape_non_printable_full(str, SIZE_MAX);
return utf8_escape_non_printable_full(str, SIZE_MAX, false);
}
size_t utf8_encode_unichar(char *out_utf8, char32_t g);