69ed622c7ea871fe4a8c65c259e325068ae4c37d
[samba.git] / source3 / utils / regedit_hexedit.c
1 #include "includes.h"
2 #include "regedit_hexedit.h"
3
4 static int max_rows(WINDOW *win)
5 {
6         int maxy, maxx;
7
8         getmaxyx(win, maxy, maxx);
9
10         return maxy - 1;
11 }
12
13 static int hexedit_free(struct hexedit *buf)
14 {
15         if (buf->status_line) {
16                 delwin(buf->status_line);
17         }
18         if (buf->win) {
19                 delwin(buf->win);
20         }
21
22         return 0;
23 }
24
25 struct hexedit *hexedit_new(TALLOC_CTX *ctx, WINDOW *parent, int nlines,
26                             int y, int x, size_t sz)
27 {
28         struct hexedit *buf;
29
30         buf = talloc_zero(ctx, struct hexedit);
31         if (buf == NULL) {
32                 return NULL;
33         }
34
35         talloc_set_destructor(buf, hexedit_free);
36
37         buf->data = talloc_zero_array(buf, uint8_t, sz);
38         if (buf->data == NULL) {
39                 goto fail;
40         }
41
42         buf->len = sz;
43         buf->alloc_size = sz;
44         buf->win = derwin(parent, nlines, LINE_WIDTH, y, x);
45         if (buf->win == NULL) {
46                 goto fail;
47         }
48         buf->cursor_x = HEX_COL1;
49
50         buf->status_line = derwin(buf->win, 1, LINE_WIDTH, max_rows(buf->win), 0);
51         if (buf->status_line == NULL) {
52                 goto fail;
53         }
54         wattron(buf->status_line, A_REVERSE | A_STANDOUT);
55
56         return buf;
57
58 fail:
59         talloc_free(buf);
60
61         return NULL;
62 }
63
64 static size_t bytes_per_screen(WINDOW *win)
65 {
66         return max_rows(win) * BYTES_PER_LINE;
67 }
68
69 void hexedit_set_cursor(struct hexedit *buf)
70 {
71         werase(buf->status_line);
72         wprintw(buf->status_line, "Len:%lu Off:%lu Val:0x%X", buf->len,
73                 buf->cursor_offset, buf->data[buf->cursor_offset]);
74         wrefresh(buf->status_line);
75         wmove(buf->win, buf->cursor_y, buf->cursor_x);
76 }
77
78 void hexedit_refresh(struct hexedit *buf)
79 {
80         size_t end;
81         size_t lineno;
82         size_t off;
83
84         werase(buf->win);
85
86         end = buf->offset + bytes_per_screen(buf->win);
87         if (end > buf->len) {
88                 end = buf->len;
89         }
90
91         for (off = buf->offset, lineno = 0; off < end; off += BYTES_PER_LINE, ++lineno) {
92                 uint8_t *line = buf->data + off;
93                 size_t i, endline;
94
95                 wmove(buf->win, lineno, 0);
96                 wprintw(buf->win, "%08X  ", off);
97
98                 endline = BYTES_PER_LINE;
99
100                 if (off + BYTES_PER_LINE > buf->len) {
101                         endline = buf->len - off;
102                 }
103
104                 for (i = 0; i < endline; ++i) {
105                         wprintw(buf->win, "%02X", line[i]);
106                         if (i + 1 < endline) {
107                                 if (i == 3) {
108                                         wprintw(buf->win, "  ");
109                                 } else {
110                                         wprintw(buf->win, " ");
111                                 }
112                         }
113                 }
114
115                 wmove(buf->win, lineno, ASCII_COL);
116                 for (i = 0; i < endline; ++i) {
117                         if (isprint(line[i])) {
118                                 waddch(buf->win, line[i]);
119                         } else {
120                                 waddch(buf->win, '.');
121                         }
122                 }
123         }
124 }
125
126 static void calc_cursor_offset(struct hexedit *buf)
127 {
128         buf->cursor_offset = buf->offset + buf->cursor_y * BYTES_PER_LINE + buf->cursor_line_offset;
129 }
130
131 static int offset_to_hex_col(size_t pos)
132 {
133         switch (pos) {
134         case 0:
135                 return HEX_COL1;
136         case 1:
137                 return HEX_COL1 + 3;
138         case 2:
139                 return HEX_COL1 + 6;
140         case 3:
141                 return HEX_COL1 + 9;
142
143         case 4:
144                 return HEX_COL2;
145         case 5:
146                 return HEX_COL2 + 3;
147         case 6:
148                 return HEX_COL2 + 6;
149         case 7:
150                 return HEX_COL2 + 9;
151         }
152
153         return -1;
154 }
155
156 static bool scroll_down(struct hexedit *buf)
157 {
158         if (buf->offset + bytes_per_screen(buf->win) >= buf->len) {
159                 return false;
160         }
161
162         buf->offset += BYTES_PER_LINE;
163
164         return true;
165 }
166
167 static bool scroll_up(struct hexedit *buf)
168 {
169         if (buf->offset == 0) {
170                 return false;
171         }
172
173         buf->offset -= BYTES_PER_LINE;
174
175         return true;
176 }
177
178 static void cursor_down(struct hexedit *buf)
179 {
180         if (buf->cursor_y + 1 == max_rows(buf->win)) {
181                 if (scroll_down(buf)) {
182                         hexedit_refresh(buf);
183                 }
184         } else {
185                 if (buf->cursor_offset + BYTES_PER_LINE >= buf->len) {
186                         return;
187                 }
188                 buf->cursor_y++;
189         }
190
191         calc_cursor_offset(buf);
192 }
193
194 static void cursor_up(struct hexedit *buf)
195 {
196         if (buf->cursor_y == 0) {
197                 if (scroll_up(buf)) {
198                         hexedit_refresh(buf);
199                 }
200         } else {
201                 buf->cursor_y--;
202         }
203
204         calc_cursor_offset(buf);
205 }
206
207 static bool is_over_gap(struct hexedit *buf)
208 {
209         int col;
210
211         if (buf->cursor_x < ASCII_COL) {
212                 if (buf->cursor_x >= HEX_COL2) {
213                         col = buf->cursor_x - HEX_COL2;
214                 } else {
215                         col = buf->cursor_x - HEX_COL1;
216                 }
217
218                 switch (col) {
219                 case 2:
220                 case 5:
221                 case 8:
222                         return true;
223                 }
224         }
225
226         return false;
227 }
228
229 static void cursor_left(struct hexedit *buf)
230 {
231         if (buf->cursor_x == HEX_COL1) {
232                 return;
233         }
234         if (buf->cursor_x == HEX_COL2) {
235                 buf->cursor_x = HEX_COL1_END - 1;
236                 buf->cursor_line_offset = 3;
237                 buf->nibble = 1;
238         } else if (buf->cursor_x == ASCII_COL) {
239                 size_t off = buf->offset + buf->cursor_y * BYTES_PER_LINE;
240                 if (off + 7 >= buf->len) {
241                         size_t lastpos = buf->len - off - 1;
242                         buf->cursor_x = offset_to_hex_col(lastpos) + 1;
243                         buf->cursor_line_offset = lastpos;
244                 } else {
245                         buf->cursor_x = HEX_COL2_END - 1;
246                         buf->cursor_line_offset = 7;
247                 }
248                 buf->nibble = 1;
249         } else {
250                 if (buf->cursor_x > ASCII_COL || buf->nibble == 0) {
251                         buf->cursor_line_offset--;
252                 }
253                 buf->cursor_x--;
254                 buf->nibble = !buf->nibble;
255         }
256
257         if (is_over_gap(buf)) {
258                 buf->cursor_x--;
259         }
260
261         calc_cursor_offset(buf);
262 }
263
264 static void cursor_right(struct hexedit *buf)
265 {
266         int new_x = buf->cursor_x + 1;
267
268         if (new_x == ASCII_COL_END) {
269                 return;
270         }
271         if ((buf->cursor_x >= ASCII_COL || buf->nibble == 1) &&
272             buf->cursor_offset + 1 == buf->len) {
273                 if (buf->cursor_x < ASCII_COL) {
274                         new_x = ASCII_COL;
275                         buf->cursor_line_offset = 0;
276                         buf->nibble = 0;
277                 } else {
278                         return;
279                 }
280         }
281         if (new_x == HEX_COL1_END) {
282                 new_x = HEX_COL2;
283                 buf->cursor_line_offset = 4;
284                 buf->nibble = 0;
285         } else if (new_x == HEX_COL2_END) {
286                 new_x = ASCII_COL;
287                 buf->cursor_line_offset = 0;
288                 buf->nibble = 0;
289         } else {
290                 if (buf->cursor_x >= ASCII_COL || buf->nibble == 1) {
291                         buf->cursor_line_offset++;
292                 }
293                 buf->nibble = !buf->nibble;
294         }
295
296         buf->cursor_x = new_x;
297
298         if (is_over_gap(buf)) {
299                 buf->cursor_x++;
300         }
301
302         calc_cursor_offset(buf);
303 }
304
305 static void do_edit(struct hexedit *buf, int c)
306 {
307         uint8_t *byte;
308
309         byte = buf->data + buf->cursor_offset;
310
311         if (buf->cursor_x >= ASCII_COL) {
312                 *byte = (uint8_t)c;
313
314                 mvwprintw(buf->win, buf->cursor_y,
315                           offset_to_hex_col(buf->cursor_line_offset), "%X", c);
316                 if (!isprint(c)) {
317                         c = '.';
318                 }
319                 mvwaddch(buf->win, buf->cursor_y, ASCII_COL + buf->cursor_line_offset, c);
320                 cursor_right(buf);
321         } else {
322                 if (!isxdigit(c)) {
323                         return;
324                 }
325                 c = toupper(c);
326                 waddch(buf->win, c);
327
328                 if (isdigit(c)) {
329                         c = c - '0';
330                 } else {
331                         c = c - 'A' + 10;
332                 }
333                 if (buf->nibble == 0) {
334                         *byte = (*byte & 0x0f) | c << 4;
335                 } else {
336                         *byte = (*byte & 0xf0) | c;
337                 }
338
339                 c = *byte;
340                 if (!isprint(c)) {
341                         c = '.';
342                 }
343                 mvwaddch(buf->win, buf->cursor_y, ASCII_COL + buf->cursor_line_offset, c);
344
345                 if (buf->cursor_x + 1 != HEX_COL2_END) {
346                         cursor_right(buf);
347                 }
348         }
349 }
350
351 void hexedit_driver(struct hexedit *buf, int c)
352 {
353         switch (c) {
354         case HE_CURSOR_UP:
355                 cursor_up(buf);
356                 break;
357         case HE_CURSOR_DOWN:
358                 cursor_down(buf);
359                 break;
360         case HE_CURSOR_LEFT:
361                 cursor_left(buf);
362                 break;
363         case HE_CURSOR_RIGHT:
364                 cursor_right(buf);
365                 break;
366         case HE_CURSOR_PGUP:
367                 break;
368         case HE_CURSOR_PGDN:
369                 break;
370         default:
371                 do_edit(buf, c & 0xff);
372                 break;
373         }
374 }
375
376 WERROR hexedit_resize_buffer(struct hexedit *buf, size_t newsz)
377 {
378         /* reset the cursor if it'll be out of bounds
379            after the resize */
380         if (buf->cursor_offset >= newsz) {
381                 buf->cursor_y = 0;
382                 buf->cursor_x = HEX_COL1;
383                 buf->offset = 0;
384                 buf->cursor_offset = 0;
385                 buf->cursor_line_offset = 0;
386                 buf->nibble = 0;
387         }
388
389         if (newsz > buf->len) {
390                 if (newsz > buf->alloc_size) {
391                         uint8_t *d;
392                         d = talloc_realloc(buf, buf->data, uint8_t, newsz);
393                         if (d == NULL) {
394                                 return WERR_NOMEM;
395                         }
396                         buf->data = d;
397                         buf->alloc_size = newsz;
398                 }
399                 memset(buf->data + buf->len, '\0', newsz - buf->len);
400                 buf->len = newsz;
401         } else {
402                 buf->len = newsz;
403         }
404
405         return WERR_OK;
406 }