mirror of
https://github.com/samba-team/samba.git
synced 2024-12-24 21:34:56 +03:00
b00f30c0ac
Guenther Signed-off-by: Guenther Deschner <gd@samba.org> Reviewed-by: Jeremy Allison <jra@samba.org>
564 lines
11 KiB
C
564 lines
11 KiB
C
/*
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#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, "%08X ", 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;
|
|
}
|