Merge branch 'pb-plugin' into master
[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, or (size_t) -2
40                  * if we've reached the end of the buffer */
41                 if (!bytes || bytes == (size_t) -2) {
42                         line_cb(arg, start, end - start);
43                         break;
44                 }
45
46                 if (wc == L'\n') {
47                         rc = line_cb(arg, start, end - start);
48                         start = sep = end += bytes;
49                         sep_bytes = 0;
50                         col = 0;
51                         continue;
52                 }
53
54                 width = wcwidth(wc);
55
56                 /* we should have caught this in the !bytes check... */
57                 if (width == 0) {
58                         line_cb(arg, start, end - start);
59                         break;
60                 }
61
62                 /* unprintable character? just add it to the current line */
63                 if (width < 0) {
64                         end += bytes;
65                         continue;
66                 }
67
68                 col += width;
69
70                 if (col > linelen) {
71                         if (sep != start) {
72                                 /* split on a previous word boundary, if
73                                  * possible */
74                                 rc = line_cb(arg, start, sep - start);
75                                 end = sep + sep_bytes;
76                         } else {
77                                 /* otherwise, break the word */
78                                 rc = line_cb(arg, start, end - start);
79                         }
80                         sep_bytes = 0;
81                         start = sep = end;
82                         col = 0;
83
84                 } else {
85                         /* record our last separator */
86                         if (wc == L' ') {
87                                 sep = end;
88                                 sep_bytes = bytes;
89                         }
90                         end += bytes;
91                 }
92         }
93 }