1 /*
2 * Samba Unix/Linux SMB client library
3 * Registry Editor
4 * Copyright (C) Christopher Davis 2012
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "includes.h"
21 #include "regedit_hexedit.h"
22
23 /*
24 offset hex1 hex2 ascii
25 00000000 FF FF FF FF FF FF FF FF ........
26 */
27
28 #define HEX_COL1 10
29 #define HEX_COL1_END 21
30 #define HEX_COL2 23
31 #define HEX_COL2_END 34
32 #define ASCII_COL 36
33 #define ASCII_COL_END LINE_WIDTH
34 #define BYTES_PER_LINE 8
35
36 struct hexedit {
37 size_t offset;
38 size_t len;
39 size_t alloc_size;
40 int cursor_y;
41 int cursor_x;
42 size_t cursor_offset;
43 size_t cursor_line_offset;
44 int nibble;
45 uint8_t *data;
46 WINDOW *win;
47 };
48
max_rows(WINDOW * win)49 static int max_rows(WINDOW *win)
50 {
51 int maxy;
52
53 maxy = getmaxy(win);
54
55 return maxy - 1;
56 }
57
hexedit_new(TALLOC_CTX * ctx,WINDOW * parent,const void * data,size_t sz)58 struct hexedit *hexedit_new(TALLOC_CTX *ctx, WINDOW *parent, const void *data,
59 size_t sz)
60 {
61 WERROR rv;
62 struct hexedit *buf;
63
64 buf = talloc_zero(ctx, struct hexedit);
65 if (buf == NULL) {
66 return NULL;
67 }
68
69 buf->win = parent;
70
71 rv = hexedit_set_buf(buf, data, sz);
72 if (!W_ERROR_IS_OK(rv)) {
73 goto fail;
74 }
75
76 return buf;
77
78 fail:
79 talloc_free(buf);
80
81 return NULL;
82 }
83
hexedit_set_buf(struct hexedit * buf,const void * data,size_t sz)84 WERROR hexedit_set_buf(struct hexedit *buf, const void *data, size_t sz)
85 {
86 TALLOC_FREE(buf->data);
87
88 buf->data = talloc_zero_array(buf, uint8_t, sz);
89 if (buf->data == NULL) {
90 return WERR_NOT_ENOUGH_MEMORY;
91 }
92
93 if (data != NULL) {
94 memcpy(buf->data, data, sz);
95 }
96
97 buf->len = sz;
98 buf->alloc_size = sz;
99 buf->cursor_x = HEX_COL1;
100 buf->cursor_y = 0;
101 buf->cursor_offset = 0;
102 buf->cursor_line_offset = 0;
103 buf->nibble = 0;
104
105 return WERR_OK;
106 }
107
hexedit_get_buf(struct hexedit * buf)108 const void *hexedit_get_buf(struct hexedit *buf)
109 {
110 return buf->data;
111 }
112
hexedit_get_buf_len(struct hexedit * buf)113 size_t hexedit_get_buf_len(struct hexedit *buf)
114 {
115 return buf->len;
116 }
117
bytes_per_screen(WINDOW * win)118 static size_t bytes_per_screen(WINDOW *win)
119 {
120 return max_rows(win) * BYTES_PER_LINE;
121 }
122
hexedit_set_cursor(struct hexedit * buf)123 void hexedit_set_cursor(struct hexedit *buf)
124 {
125 wmove(buf->win, max_rows(buf->win), 0);
126 wattron(buf->win, A_REVERSE | A_STANDOUT);
127 wclrtoeol(buf->win);
128 if (buf->cursor_offset < buf->len) {
129 wprintw(buf->win, "Len:%lu Off:%lu Val:0x%X", buf->len,
130 buf->cursor_offset, buf->data[buf->cursor_offset]);
131 } else {
132 wprintw(buf->win, "Len:%lu Off:%lu", buf->len,
133 buf->cursor_offset);
134 }
135 wattroff(buf->win, A_REVERSE | A_STANDOUT);
136 wmove(buf->win, buf->cursor_y, buf->cursor_x);
137 wcursyncup(buf->win);
138 wsyncup(buf->win);
139 untouchwin(buf->win);
140 }
141
hexedit_refresh(struct hexedit * buf)142 void hexedit_refresh(struct hexedit *buf)
143 {
144 size_t end;
145 size_t lineno;
146 size_t off;
147
148 werase(buf->win);
149 if (buf->len == 0) {
150 mvwprintw(buf->win, 0, 0, "%08X", 0);
151 return;
152 }
153
154 end = buf->offset + bytes_per_screen(buf->win);
155 if (end > buf->len) {
156 end = buf->len;
157 }
158
159 for (off = buf->offset, lineno = 0;
160 off < end;
161 off += BYTES_PER_LINE, ++lineno) {
162 uint8_t *line = buf->data + off;
163 size_t i, endline;
164
165 wmove(buf->win, lineno, 0);
166 wprintw(buf->win, "%08X ", off);
167
168 endline = BYTES_PER_LINE;
169
170 if (off + BYTES_PER_LINE > buf->len) {
171 endline = buf->len - off;
172 }
173
174 for (i = 0; i < endline; ++i) {
175 wprintw(buf->win, "%02X", line[i]);
176 if (i + 1 < endline) {
177 if (i == 3) {
178 wprintw(buf->win, " ");
179 } else {
180 wprintw(buf->win, " ");
181 }
182 }
183 }
184
185 wmove(buf->win, lineno, ASCII_COL);
186 for (i = 0; i < endline; ++i) {
187 if (isprint(line[i])) {
188 waddch(buf->win, line[i]);
189 } else {
190 waddch(buf->win, '.');
191 }
192 }
193 }
194 }
195
calc_cursor_offset(struct hexedit * buf)196 static void calc_cursor_offset(struct hexedit *buf)
197 {
198 buf->cursor_offset = buf->offset + buf->cursor_y * BYTES_PER_LINE +
199 buf->cursor_line_offset;
200 }
201
offset_to_hex_col(size_t pos)202 static int offset_to_hex_col(size_t pos)
203 {
204 switch (pos) {
205 case 0:
206 return HEX_COL1;
207 case 1:
208 return HEX_COL1 + 3;
209 case 2:
210 return HEX_COL1 + 6;
211 case 3:
212 return HEX_COL1 + 9;
213
214 case 4:
215 return HEX_COL2;
216 case 5:
217 return HEX_COL2 + 3;
218 case 6:
219 return HEX_COL2 + 6;
220 case 7:
221 return HEX_COL2 + 9;
222 }
223
224 return -1;
225 }
226
scroll_up(struct hexedit * buf)227 static bool scroll_up(struct hexedit *buf)
228 {
229 if (buf->offset == 0) {
230 return false;
231 }
232
233 buf->offset -= BYTES_PER_LINE;
234
235 return true;
236 }
237
cursor_down(struct hexedit * buf)238 static void cursor_down(struct hexedit *buf)
239 {
240 size_t space;
241 bool need_refresh = false;
242
243 space = buf->offset + (buf->cursor_y + 1) * BYTES_PER_LINE;
244 if (space > buf->len) {
245 return;
246 }
247
248 if (buf->cursor_y + 1 == max_rows(buf->win)) {
249 buf->offset += BYTES_PER_LINE;
250 need_refresh = true;
251 } else {
252 buf->cursor_y++;
253 }
254
255 if (buf->cursor_offset + BYTES_PER_LINE > buf->len) {
256 buf->nibble = 0;
257 buf->cursor_offset = buf->len;
258 buf->cursor_line_offset = buf->len - space;
259 if (buf->cursor_x >= ASCII_COL) {
260 buf->cursor_x = ASCII_COL + buf->cursor_line_offset;
261 } else {
262 buf->cursor_x = offset_to_hex_col(buf->cursor_line_offset);
263 }
264 }
265 if (need_refresh) {
266 hexedit_refresh(buf);
267 }
268 calc_cursor_offset(buf);
269 }
270
cursor_up(struct hexedit * buf)271 static void cursor_up(struct hexedit *buf)
272 {
273 if (buf->cursor_y == 0) {
274 if (scroll_up(buf)) {
275 hexedit_refresh(buf);
276 }
277 } else {
278 buf->cursor_y--;
279 }
280
281 calc_cursor_offset(buf);
282 }
283
is_over_gap(struct hexedit * buf)284 static bool is_over_gap(struct hexedit *buf)
285 {
286 int col;
287
288 if (buf->cursor_x < ASCII_COL) {
289 if (buf->cursor_x >= HEX_COL2) {
290 col = buf->cursor_x - HEX_COL2;
291 } else {
292 col = buf->cursor_x - HEX_COL1;
293 }
294
295 switch (col) {
296 case 2:
297 case 5:
298 case 8:
299 return true;
300 }
301 }
302
303 return false;
304 }
305
cursor_left(struct hexedit * buf)306 static void cursor_left(struct hexedit *buf)
307 {
308 if (buf->cursor_x == HEX_COL1) {
309 return;
310 }
311 if (buf->cursor_x == HEX_COL2) {
312 buf->cursor_x = HEX_COL1_END - 1;
313 buf->cursor_line_offset = 3;
314 buf->nibble = 1;
315 } else if (buf->cursor_x == ASCII_COL) {
316 size_t off = buf->offset + buf->cursor_y * BYTES_PER_LINE;
317 if (off + 7 >= buf->len) {
318 size_t lastpos = buf->len - off;
319 buf->cursor_x = offset_to_hex_col(lastpos) + 1;
320 buf->cursor_line_offset = lastpos;
321 } else {
322 buf->cursor_x = HEX_COL2_END - 1;
323 buf->cursor_line_offset = 7;
324 }
325 buf->nibble = 1;
326 } else {
327 if (buf->cursor_x > ASCII_COL || buf->nibble == 0) {
328 buf->cursor_line_offset--;
329 }
330 buf->cursor_x--;
331 buf->nibble = !buf->nibble;
332 }
333
334 if (is_over_gap(buf)) {
335 buf->cursor_x--;
336 }
337
338 calc_cursor_offset(buf);
339 }
340
cursor_right(struct hexedit * buf)341 static void cursor_right(struct hexedit *buf)
342 {
343 int new_x = buf->cursor_x + 1;
344
345 if (new_x == ASCII_COL_END) {
346 return;
347 }
348 if ((buf->cursor_x >= ASCII_COL || buf->nibble == 1) &&
349 buf->cursor_offset == buf->len) {
350 if (buf->cursor_x < ASCII_COL) {
351 new_x = ASCII_COL;
352 buf->cursor_line_offset = 0;
353 buf->nibble = 0;
354 } else {
355 return;
356 }
357 }
358 if (new_x == HEX_COL1_END) {
359 new_x = HEX_COL2;
360 buf->cursor_line_offset = 4;
361 buf->nibble = 0;
362 } else if (new_x == HEX_COL2_END) {
363 new_x = ASCII_COL;
364 buf->cursor_line_offset = 0;
365 buf->nibble = 0;
366 } else {
367 if (buf->cursor_x >= ASCII_COL || buf->nibble == 1) {
368 buf->cursor_line_offset++;
369 }
370 buf->nibble = !buf->nibble;
371 }
372
373 buf->cursor_x = new_x;
374
375 if (is_over_gap(buf)) {
376 buf->cursor_x++;
377 }
378
379 calc_cursor_offset(buf);
380 }
381
do_edit(struct hexedit * buf,int c)382 static void do_edit(struct hexedit *buf, int c)
383 {
384 uint8_t *byte;
385
386 if (buf->cursor_offset == buf->len) {
387 hexedit_resize_buffer(buf, buf->len + 1);
388 }
389
390 byte = buf->data + buf->cursor_offset;
391
392 if (buf->cursor_x >= ASCII_COL) {
393 *byte = (uint8_t)c;
394
395 mvwprintw(buf->win, buf->cursor_y,
396 offset_to_hex_col(buf->cursor_line_offset), "%X", c);
397 if (!isprint(c)) {
398 c = '.';
399 }
400 mvwaddch(buf->win, buf->cursor_y,
401 ASCII_COL + buf->cursor_line_offset, c);
402 if (buf->cursor_x + 1 != ASCII_COL_END) {
403 cursor_right(buf);
404 } else {
405 cursor_down(buf);
406 }
407 } else {
408 if (!isxdigit(c)) {
409 return;
410 }
411 c = toupper(c);
412 waddch(buf->win, c);
413
414 if (isdigit(c)) {
415 c = c - '0';
416 } else {
417 c = c - 'A' + 10;
418 }
419 if (buf->nibble == 0) {
420 *byte = (*byte & 0x0f) | c << 4;
421 } else {
422 *byte = (*byte & 0xf0) | c;
423 }
424
425 c = *byte;
426 if (!isprint(c)) {
427 c = '.';
428 }
429 mvwaddch(buf->win, buf->cursor_y,
430 ASCII_COL + buf->cursor_line_offset, c);
431
432 if (buf->cursor_x + 1 != HEX_COL2_END) {
433 cursor_right(buf);
434 } else {
435 cursor_down(buf);
436 }
437 }
438
439 hexedit_refresh(buf);
440 }
441
erase_at(struct hexedit * buf,size_t pos)442 static void erase_at(struct hexedit *buf, size_t pos)
443 {
444 if (pos >= buf->len) {
445 return;
446 }
447
448 if (pos < buf->len - 1) {
449 /* squeeze the character out of the buffer */
450 uint8_t *p = buf->data + pos;
451 uint8_t *end = buf->data + buf->len;
452 memmove(p, p + 1, end - p - 1);
453 }
454
455 buf->len--;
456 hexedit_refresh(buf);
457 }
458
do_backspace(struct hexedit * buf)459 static void do_backspace(struct hexedit *buf)
460 {
461 size_t off;
462 bool do_erase = true;
463
464 if (buf->cursor_offset == 0) {
465 return;
466 }
467
468 off = buf->cursor_offset;
469 if (buf->cursor_x == ASCII_COL) {
470 cursor_up(buf);
471 buf->cursor_line_offset = 7;
472 buf->cursor_x = ASCII_COL_END - 1;
473 calc_cursor_offset(buf);
474 } else if (buf->cursor_x == HEX_COL1) {
475 cursor_up(buf);
476 buf->cursor_line_offset = 7;
477 buf->cursor_x = HEX_COL2_END - 1;
478 buf->nibble = 1;
479 calc_cursor_offset(buf);
480 } else {
481 if (buf->cursor_x < ASCII_COL && buf->nibble) {
482 do_erase = false;
483 }
484 cursor_left(buf);
485 }
486 if (do_erase) {
487 erase_at(buf, off - 1);
488 }
489 }
490
do_delete(struct hexedit * buf)491 static void do_delete(struct hexedit *buf)
492 {
493 erase_at(buf, buf->cursor_offset);
494 }
495
hexedit_driver(struct hexedit * buf,int c)496 void hexedit_driver(struct hexedit *buf, int c)
497 {
498 switch (c) {
499 case HE_CURSOR_UP:
500 cursor_up(buf);
501 break;
502 case HE_CURSOR_DOWN:
503 cursor_down(buf);
504 break;
505 case HE_CURSOR_LEFT:
506 cursor_left(buf);
507 break;
508 case HE_CURSOR_RIGHT:
509 cursor_right(buf);
510 break;
511 case HE_CURSOR_PGUP:
512 break;
513 case HE_CURSOR_PGDN:
514 break;
515 case HE_BACKSPACE:
516 do_backspace(buf);
517 break;
518 case HE_DELETE:
519 do_delete(buf);
520 break;
521 default:
522 do_edit(buf, c & 0xff);
523 break;
524 }
525
526 hexedit_set_cursor(buf);
527 }
528
hexedit_resize_buffer(struct hexedit * buf,size_t newsz)529 WERROR hexedit_resize_buffer(struct hexedit *buf, size_t newsz)
530 {
531 /* reset the cursor if it'll be out of bounds
532 after the resize */
533 if (buf->cursor_offset > newsz) {
534 buf->cursor_y = 0;
535 buf->cursor_x = HEX_COL1;
536 buf->offset = 0;
537 buf->cursor_offset = 0;
538 buf->cursor_line_offset = 0;
539 buf->nibble = 0;
540 }
541
542 if (newsz > buf->len) {
543 if (newsz > buf->alloc_size) {
544 uint8_t *d;
545 buf->alloc_size *= 2;
546 if (newsz > buf->alloc_size) {
547 buf->alloc_size = newsz;
548 }
549 d = talloc_realloc(buf, buf->data, uint8_t,
550 buf->alloc_size);
551 if (d == NULL) {
552 return WERR_NOT_ENOUGH_MEMORY;
553 }
554 buf->data = d;
555 }
556 memset(buf->data + buf->len, '\0', newsz - buf->len);
557 buf->len = newsz;
558 } else {
559 buf->len = newsz;
560 }
561
562 return WERR_OK;
563 }
564