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