table

Помоћни програм из командне линије за форматирање и приказ CSV-а
git clone https://git.sr.ht/~strahinja/table
Дневник | Датотеке | Референце | ПРОЧИТАЈМЕ | ЛИЦЕНЦА

чување d1c6ab6711445cba5ffb974c8d65b4b3c0a855b5
родитељ cb3800d5387475aa5b83c98a334b1976a43039c0
Аутор: Страхиња Радић <sr@strahinja.org>
Датум:   Sat, 18 Apr 2026 07:50:33 +0000

Handle malformed UTF-8 sequences better (replace with FFFD)

Diffstat:
Mtable.c | 75+++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
Mutf8.c | 4++++
Mutf8.h | 2++
измењених датотека: 3, додавања: 61(+), брисања: 20(-)

diff --git a/table.c b/table.c @@ -147,10 +147,8 @@ number_of_columns(const u8* input, u32 delimiter) if (u8_rune_to_u32(&uch, pinput, &ch_len)) { warning("Malformed UTF-8 at position %td: " - "uch=%02X, " - "pinput=%02X", - pinput - input, uch, *pinput); - ch_len = 1; + "from=%02X", + pinput - input, *pinput); } if (uch == delimiter) result++; @@ -198,7 +196,11 @@ print_aligned(const u8* s, size_t rune_length, size_t to_size, Alignment align, fputs(ANSI_SGR_BOLD_ON, stdout); if (u8_rune_to_u32(&uch, ps, &ch_len)) - ch_len = 1; + { + warning("Malformed UTF-8 at position %td: " + "from=%02X", + ps - s, *ps); + } output_chars = 0; while (output_chars != ch_len) @@ -218,7 +220,12 @@ print_aligned(const u8* s, size_t rune_length, size_t to_size, Alignment align, skip_while_sgr(ps, &skipped_chars, 1, 0); ps += skipped_chars; - u8_rune_to_u32(&uch, ps, &ch_len); + if (u8_rune_to_u32(&uch, ps, &ch_len)) + { + warning("Malformed UTF-8 at position %td: " + "from=%02X", + ps - s, *ps); + } ps += ch_len; skipped_runes++; } @@ -232,7 +239,12 @@ print_aligned(const u8* s, size_t rune_length, size_t to_size, Alignment align, if (!no_ansi && lineno == 0) fputs(ANSI_SGR_BOLD_ON, stdout); - u8_rune_to_u32(&uch, ps, &ch_len); + if (u8_rune_to_u32(&uch, ps, &ch_len)) + { + warning("Malformed UTF-8 at position %td: " + "from=%02X", + ps - s, *ps); + } output_chars = 0; while (output_chars != ch_len) { @@ -251,7 +263,12 @@ print_aligned(const u8* s, size_t rune_length, size_t to_size, Alignment align, skip_while_sgr(ps, &skipped_chars, 1, 0); ps += skipped_chars; - u8_rune_to_u32(&uch, ps, &ch_len); + if (u8_rune_to_u32(&uch, ps, &ch_len)) + { + warning("Malformed UTF-8 at position %td: " + "from=%02X", + ps - s, *ps); + } ps += ch_len; skipped_runes++; } @@ -264,7 +281,12 @@ print_aligned(const u8* s, size_t rune_length, size_t to_size, Alignment align, if (!no_ansi && lineno == 0) fputs(ANSI_SGR_BOLD_ON, stdout); - u8_rune_to_u32(&uch, ps, &ch_len); + if (u8_rune_to_u32(&uch, ps, &ch_len)) + { + warning("Malformed UTF-8 at position %td: " + "from=%02X", + ps - s, *ps); + } output_chars = 0; while (output_chars != ch_len) { @@ -343,8 +365,8 @@ set_delimiter(const char* arg, u32* delimiter) size_t delimiter_len = 0; u32 uch; assert((arg != NULL) && (delimiter != NULL)); - u8_rune_to_u32(&uch, (u8*)arg, &delimiter_len); - *delimiter = uch; + if (!u8_rune_to_u32(&uch, (u8*)arg, &delimiter_len)) + *delimiter = uch; return 0; } @@ -978,6 +1000,20 @@ do_input: format_value = (rune_columns - table_columns - 2) / table_columns; + if (format_value < 1) + { + warning("Impossible to fit %ld table columns into %ld rune " + "columns (%ld-%ld-2)/%ld=%d), adjusting rune columns " + "to %ld", + table_columns, rune_columns, rune_columns, + table_columns, table_columns, format_value, + 2 * table_columns + 2); + rune_columns = 2 * table_columns + 2; + format_value + = (rune_columns - table_columns - 2) / table_columns; + } + assert(format_value > 0); + current_table_column = 0; column_start = 0; @@ -1117,7 +1153,12 @@ continue_line: (ssize_t)(poutbuf - outbuf + sizeof((u8*)ANSI_SGR_BOLD_OFF)), (ssize_t)(outbuf_size + sizeof((u8*)ANSI_SGR_BOLD_OFF)), u8); poutbuf = outbuf + outbuf_len; - u8_rune_to_u32(&uch, pline, &ch_len); + if (u8_rune_to_u32(&uch, pline, &ch_len)) + { + warning("Malformed UTF-8 at position %td: " + "from=%02X", + pline - line, *pline); + } if (uch == U32('\033') && *(pline + 1) == '[') { in_sgr = 1; @@ -1206,14 +1247,6 @@ continue_line: pline += ch_len; colno += ch_len; } - else if (!ch_len) /* UTF-8 error */ - { - *poutbuf++ = *pline; - *poutbuf = 0; - outbufrunelen++; - pline++; - colno++; - } else if (uch == TAB && expand_tabs) { while ((current_rune_column + outbufrunelen) % tab_length != 0) @@ -1247,6 +1280,8 @@ done_line: size_t colwidth = format ? *(format + current_table_column) : format_value; + assert(colwidth != 0); + if (!no_ansi && lineno == 0) snprintf((char*)poutbuf, sizeof ANSI_SGR_BOLD_OFF, "%s", ANSI_SGR_BOLD_OFF); diff --git a/utf8.c b/utf8.c @@ -56,7 +56,11 @@ u8_rune_to_u32(u32* to, const u8* from, size_t* from_delta) { pfrom = &from[i]; if (!*pfrom) + { + *from_delta = 1; + *to = U32_REPL_CHAR; return 1; + } *to <<= 6; *to |= *pfrom & XMASKR; } diff --git a/utf8.h b/utf8.h @@ -37,6 +37,8 @@ typedef uint8_t u8; typedef uint32_t u32; +#define U32_REPL_CHAR ((u32)0xFFFD) + int u8_rune_to_u32(u32* to, const u8* from, size_t* from_delta); int u32_rune_to_u8(u8* to, const u32 from); /* max == 1 */ int u8_to_u32(u32* to, const u8* from, const ssize_t max, size_t* from_delta);