lib/fold: Catch error case from mbrtowc()
[petitboot] / lib / fold / fold.c
1
2 #define _GNU_SOURCE
3
4 #include <assert.h>
5 #include <string.h>
6 #include <stdio.h>
7 #include <wchar.h>
8 #include <wctype.h>
9
10 #include "fold/fold.h"
11
12 void fold_text(const char *text,
13                 int linelen,
14                 int line_cb(void *arg, const char *start, int len),
15                 void *arg)
16 {
17         const char *start, *end, *sep;
18         size_t sep_bytes, len;
19         int col, rc = 0;
20         mbstate_t ps;
21
22         /* start, end and sep are byte-positions in the string, and should always
23          * lie on the start of a multibyte sequence */
24         start = end = sep = text;
25         sep_bytes = 0;
26         col = 0;
27         len = strlen(text);
28         memset(&ps, 0, sizeof(ps));
29
30         while (!rc) {
31                 size_t bytes;
32                 wchar_t wc;
33                 int width;
34
35                 bytes = mbrtowc(&wc, end, len - (end - text), &ps);
36
37                 assert(bytes != (size_t)-1);
38
39                 /* we'll get a zero size for the nul terminator, (size_t) -2
40                  * if we've reached the end of the buffer, or (size_t) -1 on
41                  * error */
42                 if (!bytes || bytes == (size_t) -2 || bytes == (size_t) -1) {
43                         line_cb(arg, start, end - start);
44                         break;
45                 }
46
47                 if (wc == L'\n') {
48                         rc = line_cb(arg, start, end - start);
49                         start = sep = end += bytes;
50                         sep_bytes = 0;
51                         col = 0;
52                         continue;
53                 }
54
55                 width = wcwidth(wc);
56
57                 /* we should have caught this in the !bytes check... */
58                 if (width == 0) {
59                         line_cb(arg, start, end - start);
60                         break;
61                 }
62
63                 /* unprintable character? just add it to the current line */
64                 if (width < 0) {
65                         end += bytes;
66                         continue;
67                 }
68
69                 col += width;
70
71                 if (col > linelen) {
72                         if (sep != start) {
73                                 /* split on a previous word boundary, if
74                                  * possible */
75                                 rc = line_cb(arg, start, sep - start);
76                                 end = sep + sep_bytes;
77                         } else {
78                                 /* otherwise, break the word */
79                                 rc = line_cb(arg, start, end - start);
80                         }
81                         sep_bytes = 0;
82                         start = sep = end;
83                         col = 0;
84
85                 } else {
86                         /* record our last separator */
87                         if (wc == L' ') {
88                                 sep = end;
89                                 sep_bytes = bytes;
90                         }
91                         end += bytes;
92                 }
93         }
94 }