]> git.neil.brown.name Git - edlib.git/blob - render-hex.c
TODO: clean out done items.
[edlib.git] / render-hex.c
1 /*
2  * Copyright Neil Brown ©2015-2023 <neil@brown.name>
3  * May be distributed under terms of GPLv2 - see file:COPYING
4  *
5  * hexedit renderer
6  *
7  * 16 bytes are rendered as hex, and then chars
8  * Well... currently we do chars, not bytes, because I cannot control
9  * char encoding yet.
10  *
11  */
12
13 #include <unistd.h>
14 #include <stdlib.h>
15 #include <wchar.h>
16 #include <string.h>
17 #include <stdio.h>
18
19 #define PANE_DATA_TYPE struct he_data
20 #include "core.h"
21 #include "misc.h"
22
23 struct he_data {
24         struct pane     *pane;
25         bool bytes;
26 };
27 #include "core-pane.h"
28
29 static struct map *he_map;
30 static struct pane *do_render_hex_attach(struct pane *parent safe);
31
32 DEF_LOOKUP_CMD(render_hex_handle, he_map);
33
34 DEF_CMD_CLOSED(render_hex_close)
35 {
36         struct pane *p = ci->home;
37         struct he_data *he = p->data;
38
39         he->pane = NULL;
40         return 1;
41 }
42
43 DEF_CMD(render_hex_clone)
44 {
45         struct pane *parent = ci->focus;
46
47         do_render_hex_attach(parent);
48         pane_clone_children(ci->home, parent->focus);
49         return 1;
50 }
51
52 DEF_CMD(render_hex_notify_replace)
53 {
54
55         /* If change happens only after the view port, we don't
56          * need damage.
57          * If before, we might need to update addresses.
58          * However we cannot currently access the view port, so
59          * always signal damage. FIXME.
60          */
61         call("view:changed", pane_focus(ci->home));
62         return 1;
63 }
64
65 DEF_CMD(render_hex_eol)
66 {
67         wint_t ch = 1;
68         int rpt = RPT_NUM(ci);
69         bool one_more = ci->num2 > 0;
70         int pos;
71
72         if (!ci->mark)
73                 return Enoarg;
74         call("CountLines", ci->focus, 0, ci->mark);
75
76         pos = attr_find_int(*mark_attr(ci->mark), "char") - 1;
77         while (rpt > 0 && ch != WEOF) {
78                 while ((pos & 15) != 15 &&
79                        (ch = doc_next(ci->focus, ci->mark)) != WEOF)
80                         pos += 1;
81                 rpt -= 1;
82                 if (rpt || one_more) {
83                         ch = doc_next(ci->focus, ci->mark);
84                         pos += 1;
85                 }
86         }
87         while (rpt < 0 && ch != WEOF) {
88                 while ((pos & 15) != 0 &&
89                        (ch = doc_prev(ci->focus, ci->mark)) != WEOF)
90                         pos -= 1;
91                 rpt += 1;
92                 if (rpt || one_more) {
93                         ch = doc_prev(ci->focus, ci->mark);
94                         pos -= 1;
95                 }
96         }
97         return 1;
98 }
99
100 DEF_CMD(render_line)
101 {
102         struct buf ret;
103         struct mark *m = NULL;
104         struct mark *pm = ci->mark2;
105         int pos;
106         int pm_offset = -1;
107         int i;
108         char buf[30];
109         int rv;
110
111         if (!ci->mark)
112                 return Enoarg;
113
114         call("CountLines", ci->focus, 0, ci->mark);
115         pos = attr_find_int(*mark_attr(ci->mark), "char") - 1;
116
117         buf_init(&ret);
118         if (doc_following(ci->focus, ci->mark) == WEOF)
119                 return Efail;
120         snprintf(buf, sizeof(buf), "<bold>%08x:</> ", pos);
121         buf_concat(&ret, buf);
122         m = mark_dup_view(ci->mark);
123         for (i = 0; i < 16; i++) {
124                 wint_t ch;
125                 struct mark *m2 = ci->mark;
126
127                 if (pm && mark_same(m2, pm) && pm_offset < 0)
128                         pm_offset = ret.len;
129                 if (ci->num >= 0 && ci->num <= ret.len)
130                         goto done;
131
132                 ch = doc_next(ci->focus, m2);
133                 if (ch == WEOF)
134                         strcpy(buf, "   ");
135                 else
136                         sprintf(buf, "%02x ", ch & 0xff);
137                 buf_concat(&ret, buf);
138                 if (i == 7)
139                         buf_append(&ret, ' ');
140         }
141
142         buf_concat(&ret, "  <fg:red>");
143         for (i = 0; i < 16; i++) {
144                 wint_t ch;
145
146                 ch = doc_next(ci->focus, m);
147                 if (ch == WEOF)
148                         ch = ' ';
149                 if (ch < ' ')
150                         ch = '?';
151                 buf_append(&ret, ch);
152                 if (ch == '<')
153                         /* '<<' to quote the '<' */
154                         buf_append(&ret, ch);
155                 buf_append(&ret, ' ');
156                 if (i == 7)
157                         buf_append(&ret, ' ');
158         }
159         buf_concat(&ret, "</>\n");
160 done:
161         if (m)
162                 mark_free(m);
163         rv = comm_call(ci->comm2, "callback:render", ci->focus, pm_offset, NULL,
164                        buf_final(&ret));
165         free(ret.b);
166         return rv ?: 1;
167 }
168
169 DEF_CMD(render_line_prev)
170 {
171         /* If ->num is 0, round down to multiple of 16.
172          * if it is 1, subtract a further 16.
173          */
174         int to, from;
175
176         if (!ci->mark)
177                 return Enoarg;
178         call("CountLines", ci->focus, 0, ci->mark);
179
180         from = attr_find_int(*mark_attr(ci->mark), "char") - 1;
181         to = from & ~0xF;
182         if (ci->num) {
183                 if (to >= 16)
184                         to -= 16;
185                 else
186                         return Efail;
187         }
188         while (to < from) {
189                 doc_prev(ci->focus, ci->mark);
190                 from -= 1;
191         }
192         return 1;
193 }
194
195 DEF_CMD(hex_char)
196 {
197         struct he_data *he = ci->home->data;
198
199         if (he->bytes)
200                 return home_call(ci->home->parent, "doc:byte", ci->focus,
201                                  ci->num, ci->mark, ci->str,
202                                  ci->num2, ci->mark2, ci->str2);
203         else
204                 return home_call(ci->home->parent, "doc:char", ci->focus,
205                                  ci->num, ci->mark, ci->str,
206                                  ci->num2, ci->mark2, ci->str2);
207 }
208
209 static void render_hex_register_map(void)
210 {
211         he_map = key_alloc();
212
213         key_add(he_map, "doc:EOL", &render_hex_eol);
214         key_add(he_map, "doc:char", &hex_char);
215
216         key_add(he_map, "doc:render-line-prev", &render_line_prev);
217         key_add(he_map, "doc:render-line", &render_line);
218
219         key_add(he_map, "Close", &render_hex_close);
220         key_add(he_map, "Clone", &render_hex_clone);
221         key_add(he_map, "doc:replaced", &render_hex_notify_replace);
222 }
223
224 static struct pane *do_render_hex_attach(struct pane *parent safe)
225 {
226         struct he_data *he;
227         struct pane *p;
228         char *charset = pane_attr_get(parent, "doc:charset");
229
230         if (!he_map)
231                 render_hex_register_map();
232
233         p = pane_register(parent, 0, &render_hex_handle.c);
234         if (!p)
235                 return NULL;
236         he = p->data;
237         call("doc:request:doc:replaced", p);
238         attr_set_str(&p->attrs, "render-wrap", "no");
239         attr_set_str(&p->attrs, "heading", "<bold>          00 11 22 33 44 55 66 77  88 99 aa bb cc dd ee ff   0 1 2 3 4 5 6 7  8 9 a b c d e f</>");
240         he->pane = p;
241         he->bytes = (charset && strcmp(charset, "8bit") != 0);
242         return call_ret(pane, "attach-render-lines", p);
243 }
244
245 DEF_CMD(render_hex_attach)
246 {
247         struct pane *p = do_render_hex_attach(ci->focus);
248
249         if (!p)
250                 return Efail;
251         return comm_call(ci->comm2, "callback:attach", p);
252 }
253
254 DEF_CMD(hex_appeared)
255 {
256         char *t = pane_attr_get(ci->focus, "doc-type");
257         if (t && strcmp(t, "text") == 0)
258                 attr_set_str(&ci->focus->attrs, "render-cmd-H", "hex");
259         return Efallthrough;
260 }
261
262 void edlib_init(struct pane *ed safe)
263 {
264         call_comm("global-set-command", ed, &render_hex_attach, 0, NULL, "attach-render-hex");
265         call_comm("global-set-command", ed, &hex_appeared, 0, NULL, "doc:appeared-hex");
266 }