#include "escape.h"
#include "extract-word.h"
#include "fileio.h"
-#include "gunicode.h"
#include "hashmap.h"
#include "log.h"
#include "memory-util.h"
bool in_prefix = true; /* still in the whitespace in the beginning of the line? */
size_t w = 0;
- for (const char *p = start; *p != 0; p = utf8_next_char(p)) {
+ for (const char *p = start; *p != 0; ) {
if (strchr(NEWLINE, *p)) {
in_prefix = true;
whitespace_begin = whitespace_end = NULL;
in_prefix = false;
}
- int cw = utf8_char_console_width(p);
- if (cw < 0) {
- log_debug_errno(cw, "Comment to line break contains invalid UTF-8, ignoring.");
- cw = 1;
- }
+ char32_t c;
+ int n = utf8_encoded_to_unichar(p, &c);
+ if (n < 0)
+ return log_debug_errno(n, "Line to break contains invalid UTF-8, refusing: %m");
+
+ int cw = unichar_console_width(c);
+ assert(cw >= 0);
w += cw;
if (r < 0)
return r;
+ /* Continue with the next line, starting at the first character after the
+ * whitespace we broke at. Note, that character has not been measured for
+ * the new line yet, hence reset the width and reprocess it. */
p = start = whitespace_end;
whitespace_begin = whitespace_end = NULL;
- w = cw;
+ w = 0;
+ continue;
}
+
+ p += n;
}
/* Process rest of the line */
assert_se(strv_equal(a, b));
}
+
+ /* Invalid UTF-8 is refused rather than measured. Previously a truncated multibyte lead byte at the
+ * end of a line was blind-skipped by its expected length, stepping over the NUL and reading past
+ * the end of the string. */
+ ASSERT_ERROR(strv_rebreak_lines(STRV_MAKE("foo\xF0"), 10, &l), EINVAL);
+ ASSERT_NULL(l);
+
+ ASSERT_ERROR(strv_rebreak_lines(STRV_MAKE("bar\xFC"), 10, &l), EINVAL);
+ ASSERT_NULL(l);
+
+ /* Same, but reached after a line break, i.e. with the scan restarted at the character following
+ * the whitespace we broke at. */
+ ASSERT_ERROR(strv_rebreak_lines(STRV_MAKE("a \xF0" "b"), 3, &l), EINVAL);
+ ASSERT_NULL(l);
}
TEST(strv_find_closest) {