]> git.neil.brown.name Git - edlib.git/blob - lib-utf8.c
lib-search: add a "forward" command
[edlib.git] / lib-utf8.c
1 /*
2  * Copyright Neil Brown ©2017-2023 <neil@brown.name>
3  * May be distributed under terms of GPLv2 - see file:COPYING
4  *
5  * Filter a view on a document to convert utf-8 sequences into
6  * the relevant unicode characters.
7  */
8
9 #include <unistd.h>
10 #include <stdlib.h>
11
12 #define DOC_NEXT utf8_next
13 #define DOC_PREV utf8_prev
14 #define PANE_DATA_VOID
15 #include "core.h"
16
17 static struct map *utf8_map safe;
18 DEF_LOOKUP_CMD(utf8_handle, utf8_map);
19
20 static inline wint_t utf8_next(struct pane *home safe, struct mark *mark safe,
21                                struct doc_ref *r, bool bytes)
22 {
23         int move = r == &mark->ref;
24         struct pane *p = home->parent;
25         wint_t ch;
26         struct mark *m = mark;
27         char buf[10];
28         const char *b;
29         int i;
30         wint_t ret;
31
32         if (move)
33                 ch = doc_move(p, m, 1);
34         else
35                 ch = doc_pending(p, m, 1);
36         if (ch == WEOF || (ch & 0x7f) == ch)
37                 return ch;
38         if (!move) {
39                 m = mark_dup(m);
40                 doc_move(p, m, 1);
41         }
42         i = 0;
43         buf[i++] = ch;
44         while ((ch = doc_following(p, m)) != WEOF &&
45                (ch & 0xc0) == 0x80 && i < 10) {
46                 buf[i++] = ch;
47                 doc_next(p, m);
48         }
49         b = buf;
50         ret = get_utf8(&b, b+i);
51         if (ret == WERR)
52                 ret = (unsigned char)buf[0];
53         if (!move)
54                 mark_free(m);
55         return ret;
56 }
57
58 static inline wint_t utf8_prev(struct pane *home safe, struct mark *mark safe,
59                                struct doc_ref *r, bool bytes)
60 {
61         int move = r == &mark->ref;
62         struct pane *p = home->parent;
63         wint_t ch;
64         struct mark *m = mark;
65         char buf[10];
66         const char *b;
67         int i;
68         wint_t ret;
69
70         if (move)
71                 ch = doc_move(p, m, -1);
72         else
73                 ch = doc_pending(p, m, -1);
74         if (ch == WEOF || (ch & 0x7f) == ch)
75                 return ch;
76         if (!move) {
77                 m = mark_dup(m);
78                 doc_move(p, m, -1);
79         }
80         i = 10;
81         buf[--i] = ch;
82         while (ch != WEOF && (ch & 0xc0) != 0xc0 && i > 0) {
83                 ch = doc_prev(p, m);
84                 buf[--i] = ch;
85         }
86         b = buf + i;
87         ret = get_utf8(&b, buf+10);
88         if (ret == WERR)
89                 ret = (unsigned char)buf[i];
90
91         if (!move)
92                 mark_free(m);
93         return ret;
94 }
95
96 DEF_CMD(utf8_char)
97 {
98         return do_char_byte(ci);
99 }
100
101 DEF_CMD(utf8_byte)
102 {
103         return call("doc:char", ci->home->parent, ci->num, ci->mark, ci->str,
104                     ci->num2, ci->mark2, ci->str2, ci->x, ci->y);
105 }
106
107 struct utf8cb {
108         struct command c;
109         struct command *cb safe;
110         struct pane *p safe;
111         char b[5];
112         short have, expect;
113         int size;
114 };
115
116 DEF_CMD(utf8_content_cb)
117 {
118         struct utf8cb *c = container_of(ci->comm, struct utf8cb, c);
119         wint_t wc = ci->num;
120         int ret = 1;
121
122         if (ci->x)
123                 c->size = ci->x;
124
125         if ((wc & ~0x7f) == 0) {
126                 /* 7bit char - easy.  Pass following string too,
127                  * utf8 is expected.
128                  */
129                 if (c->expect)
130                         c->expect = c->have = 0;
131                 ret = comm_call(c->cb, ci->key, c->p, wc, ci->mark, ci->str,
132                                 ci->num2, NULL, NULL, c->size, 0);
133                 c->size = 0;
134                 return ret;
135         }
136         if ((wc & 0xc0) == 0x80) {
137                 /* Continuation char */
138                 if (!c->expect)
139                         /* Ignore it */
140                         return 1;
141                 c->b[c->have++] = wc;
142                 if (c->have >= c->expect) {
143                         const char *b = c->b;
144                         wc = get_utf8(&b, b+c->have);
145                         if (wc == WERR)
146                                 wc = c->b[0];
147                         c->expect = 0;
148                         ret = comm_call(c->cb, ci->key, c->p,
149                                         wc, ci->mark, ci->str,
150                                         ci->num2, NULL, NULL, c->size, 0);
151                         c->size = 0;
152                 }
153                 return ret;
154         }
155         /* First char of multi-byte */
156         c->have = 1;
157         c->b[0] = wc;
158
159         if (wc < 0xe0)
160                 c->expect = 2;
161         else if (wc < 0xf0)
162                 c->expect = 3;
163         else if (wc < 0xf8)
164                 c->expect = 4;
165         else
166                 c->expect = 5;
167         return 1;
168 }
169
170 DEF_CMD(utf8_content)
171 {
172         struct utf8cb c;
173
174         if (!ci->comm2 || !ci->mark)
175                 return Enoarg;
176
177         c.c = utf8_content_cb;
178         c.cb = ci->comm2;
179         c.p = ci->focus;
180         c.size = 0;
181         c.expect = 0;
182         return home_call_comm(ci->home->parent, ci->key, ci->home,
183                               &c.c, 1, ci->mark, NULL, 0, ci->mark2);
184 }
185
186 DEF_CMD(utf8_attach)
187 {
188         struct pane *p;
189
190         p = pane_register(ci->focus, 0, &utf8_handle.c);
191         if (!p)
192                 return Efail;
193
194         return comm_call(ci->comm2, "callback:attach", p);
195 }
196
197 void edlib_init(struct pane *ed safe)
198 {
199
200         utf8_map = key_alloc();
201
202         key_add(utf8_map, "doc:char", &utf8_char);
203         key_add(utf8_map, "doc:byte", &utf8_byte);
204         key_add(utf8_map, "doc:content", &utf8_content);
205         /* No doc:content-bytes, that wouldn't make sense */
206
207         call_comm("global-set-command", ed, &utf8_attach, 0, NULL, "attach-charset-utf-8");
208         call_comm("global-set-command", ed, &utf8_attach, 0, NULL, "attach-utf8");
209 }