text: avoid invalid pointer arithmetic
[vis.git] / text-iterator.c
bloba70a5a9543d0df153ba90f2c0a75f0cec6880a77
1 #ifndef _GNU_SOURCE
2 #define _GNU_SOURCE /* memrchr(3) is non-standard */
3 #endif
4 #include <limits.h>
5 #include <stddef.h>
6 #include <errno.h>
7 #include <wchar.h>
8 #include <string.h>
9 #include "text.h"
10 #include "util.h"
12 bool text_iterator_byte_get(const Iterator *it, char *b) {
13 if (text_iterator_valid(it)) {
14 const Text *txt = text_iterator_text(it);
15 if (it->start <= it->text && it->text < it->end) {
16 *b = *it->text;
17 return true;
18 } else if (it->pos == text_size(txt)) {
19 *b = '\0';
20 return true;
23 return false;
26 bool text_iterator_byte_next(Iterator *it, char *b) {
27 if (!text_iterator_has_next(it))
28 return false;
29 bool eof = true;
30 if (it->text < it->end) {
31 it->text++;
32 it->pos++;
33 eof = false;
34 } else if (!text_iterator_has_prev(it)) {
35 eof = false;
38 while (it->text == it->end) {
39 if (!text_iterator_next(it)) {
40 if (eof)
41 return false;
42 if (b)
43 *b = '\0';
44 return text_iterator_prev(it);
48 if (b)
49 *b = *it->text;
50 return true;
53 bool text_iterator_byte_prev(Iterator *it, char *b) {
54 if (!text_iterator_has_prev(it))
55 return false;
56 bool eof = !text_iterator_has_next(it);
57 while (it->text == it->start) {
58 if (!text_iterator_prev(it)) {
59 if (!eof)
60 return false;
61 if (b)
62 *b = '\0';
63 return text_iterator_next(it);
67 --it->text;
68 --it->pos;
70 if (b)
71 *b = *it->text;
72 return true;
75 bool text_iterator_byte_find_prev(Iterator *it, char b) {
76 while (it->text) {
77 const char *match = memrchr(it->start, b, it->text - it->start);
78 if (match) {
79 it->pos -= it->text - match;
80 it->text = match;
81 return true;
83 text_iterator_prev(it);
85 text_iterator_next(it);
86 return false;
89 bool text_iterator_byte_find_next(Iterator *it, char b) {
90 while (it->text) {
91 const char *match = memchr(it->text, b, it->end - it->text);
92 if (match) {
93 it->pos += match - it->text;
94 it->text = match;
95 return true;
97 text_iterator_next(it);
99 text_iterator_prev(it);
100 return false;
103 bool text_iterator_codepoint_next(Iterator *it, char *c) {
104 while (text_iterator_byte_next(it, NULL)) {
105 if (ISUTF8(*it->text)) {
106 if (c)
107 *c = *it->text;
108 return true;
111 return false;
114 bool text_iterator_codepoint_prev(Iterator *it, char *c) {
115 while (text_iterator_byte_prev(it, NULL)) {
116 if (ISUTF8(*it->text)) {
117 if (c)
118 *c = *it->text;
119 return true;
122 return false;
125 bool text_iterator_char_next(Iterator *it, char *c) {
126 if (!text_iterator_codepoint_next(it, c))
127 return false;
128 mbstate_t ps = { 0 };
129 const Text *txt = text_iterator_text(it);
130 for (;;) {
131 char buf[MB_LEN_MAX];
132 size_t len = text_bytes_get(txt, it->pos, sizeof buf, buf);
133 wchar_t wc;
134 size_t wclen = mbrtowc(&wc, buf, len, &ps);
135 if (wclen == (size_t)-1 && errno == EILSEQ) {
136 return true;
137 } else if (wclen == (size_t)-2) {
138 return false;
139 } else if (wclen == 0) {
140 return true;
141 } else {
142 int width = wcwidth(wc);
143 if (width != 0)
144 return true;
145 if (!text_iterator_codepoint_next(it, c))
146 return false;
149 return true;
152 bool text_iterator_char_prev(Iterator *it, char *c) {
153 if (!text_iterator_codepoint_prev(it, c))
154 return false;
155 const Text *txt = text_iterator_text(it);
156 for (;;) {
157 char buf[MB_LEN_MAX];
158 size_t len = text_bytes_get(txt, it->pos, sizeof buf, buf);
159 wchar_t wc;
160 mbstate_t ps = { 0 };
161 size_t wclen = mbrtowc(&wc, buf, len, &ps);
162 if (wclen == (size_t)-1 && errno == EILSEQ) {
163 return true;
164 } else if (wclen == (size_t)-2) {
165 return false;
166 } else if (wclen == 0) {
167 return true;
168 } else {
169 int width = wcwidth(wc);
170 if (width != 0)
171 return true;
172 if (!text_iterator_codepoint_prev(it, c))
173 return false;
176 return true;