/* * Samba Unix/Linux SMB client library * Registry Editor * Copyright (C) Christopher Davis 2012 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "includes.h" #include "regedit_hexedit.h" /* offset hex1 hex2 ascii 00000000 FF FF FF FF FF FF FF FF ........ */ #define HEX_COL1 10 #define HEX_COL1_END 21 #define HEX_COL2 23 #define HEX_COL2_END 34 #define ASCII_COL 36 #define ASCII_COL_END LINE_WIDTH #define BYTES_PER_LINE 8 struct hexedit { size_t offset; size_t len; size_t alloc_size; int cursor_y; int cursor_x; size_t cursor_offset; size_t cursor_line_offset; int nibble; uint8_t *data; WINDOW *win; }; static int max_rows(WINDOW *win) { int maxy; maxy = getmaxy(win); return maxy - 1; } struct hexedit *hexedit_new(TALLOC_CTX *ctx, WINDOW *parent, const void *data, size_t sz) { WERROR rv; struct hexedit *buf; buf = talloc_zero(ctx, struct hexedit); if (buf == NULL) { return NULL; } buf->win = parent; rv = hexedit_set_buf(buf, data, sz); if (!W_ERROR_IS_OK(rv)) { goto fail; } return buf; fail: talloc_free(buf); return NULL; } WERROR hexedit_set_buf(struct hexedit *buf, const void *data, size_t sz) { TALLOC_FREE(buf->data); buf->data = talloc_zero_array(buf, uint8_t, sz); if (buf->data == NULL) { return WERR_NOT_ENOUGH_MEMORY; } if (data != NULL) { memcpy(buf->data, data, sz); } buf->len = sz; buf->alloc_size = sz; buf->cursor_x = HEX_COL1; buf->cursor_y = 0; buf->cursor_offset = 0; buf->cursor_line_offset = 0; buf->nibble = 0; return WERR_OK; } const void *hexedit_get_buf(struct hexedit *buf) { return buf->data; } size_t hexedit_get_buf_len(struct hexedit *buf) { return buf->len; } static size_t bytes_per_screen(WINDOW *win) { return max_rows(win) * BYTES_PER_LINE; } void hexedit_set_cursor(struct hexedit *buf) { wmove(buf->win, max_rows(buf->win), 0); wattron(buf->win, A_REVERSE | A_STANDOUT); wclrtoeol(buf->win); if (buf->cursor_offset < buf->len) { wprintw(buf->win, "Len:%lu Off:%lu Val:0x%X", buf->len, buf->cursor_offset, buf->data[buf->cursor_offset]); } else { wprintw(buf->win, "Len:%lu Off:%lu", buf->len, buf->cursor_offset); } wattroff(buf->win, A_REVERSE | A_STANDOUT); wmove(buf->win, buf->cursor_y, buf->cursor_x); wcursyncup(buf->win); wsyncup(buf->win); untouchwin(buf->win); } void hexedit_refresh(struct hexedit *buf) { size_t end; size_t lineno; size_t off; werase(buf->win); if (buf->len == 0) { mvwprintw(buf->win, 0, 0, "%08X", 0); return; } end = buf->offset + bytes_per_screen(buf->win); if (end > buf->len) { end = buf->len; } for (off = buf->offset, lineno = 0; off < end; off += BYTES_PER_LINE, ++lineno) { uint8_t *line = buf->data + off; size_t i, endline; wmove(buf->win, lineno, 0); wprintw(buf->win, "%08zX ", off); endline = BYTES_PER_LINE; if (off + BYTES_PER_LINE > buf->len) { endline = buf->len - off; } for (i = 0; i < endline; ++i) { wprintw(buf->win, "%02X", line[i]); if (i + 1 < endline) { if (i == 3) { wprintw(buf->win, " "); } else { wprintw(buf->win, " "); } } } wmove(buf->win, lineno, ASCII_COL); for (i = 0; i < endline; ++i) { if (isprint(line[i])) { waddch(buf->win, line[i]); } else { waddch(buf->win, '.'); } } } } static void calc_cursor_offset(struct hexedit *buf) { buf->cursor_offset = buf->offset + buf->cursor_y * BYTES_PER_LINE + buf->cursor_line_offset; } static int offset_to_hex_col(size_t pos) { switch (pos) { case 0: return HEX_COL1; case 1: return HEX_COL1 + 3; case 2: return HEX_COL1 + 6; case 3: return HEX_COL1 + 9; case 4: return HEX_COL2; case 5: return HEX_COL2 + 3; case 6: return HEX_COL2 + 6; case 7: return HEX_COL2 + 9; } return -1; } static bool scroll_up(struct hexedit *buf) { if (buf->offset == 0) { return false; } buf->offset -= BYTES_PER_LINE; return true; } static void cursor_down(struct hexedit *buf) { size_t space; bool need_refresh = false; space = buf->offset + (buf->cursor_y + 1) * BYTES_PER_LINE; if (space > buf->len) { return; } if (buf->cursor_y + 1 == max_rows(buf->win)) { buf->offset += BYTES_PER_LINE; need_refresh = true; } else { buf->cursor_y++; } if (buf->cursor_offset + BYTES_PER_LINE > buf->len) { buf->nibble = 0; buf->cursor_offset = buf->len; buf->cursor_line_offset = buf->len - space; if (buf->cursor_x >= ASCII_COL) { buf->cursor_x = ASCII_COL + buf->cursor_line_offset; } else { buf->cursor_x = offset_to_hex_col(buf->cursor_line_offset); } } if (need_refresh) { hexedit_refresh(buf); } calc_cursor_offset(buf); } static void cursor_up(struct hexedit *buf) { if (buf->cursor_y == 0) { if (scroll_up(buf)) { hexedit_refresh(buf); } } else { buf->cursor_y--; } calc_cursor_offset(buf); } static bool is_over_gap(struct hexedit *buf) { int col; if (buf->cursor_x < ASCII_COL) { if (buf->cursor_x >= HEX_COL2) { col = buf->cursor_x - HEX_COL2; } else { col = buf->cursor_x - HEX_COL1; } switch (col) { case 2: case 5: case 8: return true; } } return false; } static void cursor_left(struct hexedit *buf) { if (buf->cursor_x == HEX_COL1) { return; } if (buf->cursor_x == HEX_COL2) { buf->cursor_x = HEX_COL1_END - 1; buf->cursor_line_offset = 3; buf->nibble = 1; } else if (buf->cursor_x == ASCII_COL) { size_t off = buf->offset + buf->cursor_y * BYTES_PER_LINE; if (off + 7 >= buf->len) { size_t lastpos = buf->len - off; buf->cursor_x = offset_to_hex_col(lastpos) + 1; buf->cursor_line_offset = lastpos; } else { buf->cursor_x = HEX_COL2_END - 1; buf->cursor_line_offset = 7; } buf->nibble = 1; } else { if (buf->cursor_x > ASCII_COL || buf->nibble == 0) { buf->cursor_line_offset--; } buf->cursor_x--; buf->nibble = !buf->nibble; } if (is_over_gap(buf)) { buf->cursor_x--; } calc_cursor_offset(buf); } static void cursor_right(struct hexedit *buf) { int new_x = buf->cursor_x + 1; if (new_x == ASCII_COL_END) { return; } if ((buf->cursor_x >= ASCII_COL || buf->nibble == 1) && buf->cursor_offset == buf->len) { if (buf->cursor_x < ASCII_COL) { new_x = ASCII_COL; buf->cursor_line_offset = 0; buf->nibble = 0; } else { return; } } if (new_x == HEX_COL1_END) { new_x = HEX_COL2; buf->cursor_line_offset = 4; buf->nibble = 0; } else if (new_x == HEX_COL2_END) { new_x = ASCII_COL; buf->cursor_line_offset = 0; buf->nibble = 0; } else { if (buf->cursor_x >= ASCII_COL || buf->nibble == 1) { buf->cursor_line_offset++; } buf->nibble = !buf->nibble; } buf->cursor_x = new_x; if (is_over_gap(buf)) { buf->cursor_x++; } calc_cursor_offset(buf); } static void do_edit(struct hexedit *buf, int c) { uint8_t *byte; if (buf->cursor_offset == buf->len) { hexedit_resize_buffer(buf, buf->len + 1); } byte = buf->data + buf->cursor_offset; if (buf->cursor_x >= ASCII_COL) { *byte = (uint8_t)c; mvwprintw(buf->win, buf->cursor_y, offset_to_hex_col(buf->cursor_line_offset), "%X", c); if (!isprint(c)) { c = '.'; } mvwaddch(buf->win, buf->cursor_y, ASCII_COL + buf->cursor_line_offset, c); if (buf->cursor_x + 1 != ASCII_COL_END) { cursor_right(buf); } else { cursor_down(buf); } } else { if (!isxdigit(c)) { return; } c = toupper(c); waddch(buf->win, c); if (isdigit(c)) { c = c - '0'; } else { c = c - 'A' + 10; } if (buf->nibble == 0) { *byte = (*byte & 0x0f) | c << 4; } else { *byte = (*byte & 0xf0) | c; } c = *byte; if (!isprint(c)) { c = '.'; } mvwaddch(buf->win, buf->cursor_y, ASCII_COL + buf->cursor_line_offset, c); if (buf->cursor_x + 1 != HEX_COL2_END) { cursor_right(buf); } else { cursor_down(buf); } } hexedit_refresh(buf); } static void erase_at(struct hexedit *buf, size_t pos) { if (pos >= buf->len) { return; } if (pos < buf->len - 1) { /* squeeze the character out of the buffer */ uint8_t *p = buf->data + pos; uint8_t *end = buf->data + buf->len; memmove(p, p + 1, end - p - 1); } buf->len--; hexedit_refresh(buf); } static void do_backspace(struct hexedit *buf) { size_t off; bool do_erase = true; if (buf->cursor_offset == 0) { return; } off = buf->cursor_offset; if (buf->cursor_x == ASCII_COL) { cursor_up(buf); buf->cursor_line_offset = 7; buf->cursor_x = ASCII_COL_END - 1; calc_cursor_offset(buf); } else if (buf->cursor_x == HEX_COL1) { cursor_up(buf); buf->cursor_line_offset = 7; buf->cursor_x = HEX_COL2_END - 1; buf->nibble = 1; calc_cursor_offset(buf); } else { if (buf->cursor_x < ASCII_COL && buf->nibble) { do_erase = false; } cursor_left(buf); } if (do_erase) { erase_at(buf, off - 1); } } static void do_delete(struct hexedit *buf) { erase_at(buf, buf->cursor_offset); } void hexedit_driver(struct hexedit *buf, int c) { switch (c) { case HE_CURSOR_UP: cursor_up(buf); break; case HE_CURSOR_DOWN: cursor_down(buf); break; case HE_CURSOR_LEFT: cursor_left(buf); break; case HE_CURSOR_RIGHT: cursor_right(buf); break; case HE_CURSOR_PGUP: break; case HE_CURSOR_PGDN: break; case HE_BACKSPACE: do_backspace(buf); break; case HE_DELETE: do_delete(buf); break; default: do_edit(buf, c & 0xff); break; } hexedit_set_cursor(buf); } WERROR hexedit_resize_buffer(struct hexedit *buf, size_t newsz) { /* reset the cursor if it'll be out of bounds after the resize */ if (buf->cursor_offset > newsz) { buf->cursor_y = 0; buf->cursor_x = HEX_COL1; buf->offset = 0; buf->cursor_offset = 0; buf->cursor_line_offset = 0; buf->nibble = 0; } if (newsz > buf->len) { if (newsz > buf->alloc_size) { uint8_t *d; buf->alloc_size *= 2; if (newsz > buf->alloc_size) { buf->alloc_size = newsz; } d = talloc_realloc(buf, buf->data, uint8_t, buf->alloc_size); if (d == NULL) { return WERR_NOT_ENOUGH_MEMORY; } buf->data = d; } memset(buf->data + buf->len, '\0', newsz - buf->len); buf->len = newsz; } else { buf->len = newsz; } return WERR_OK; }