From 742647ef8070cf161bcdedc1e898a98a5f026df7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=99emysl=20Janouch?= Date: Fri, 30 Dec 2016 01:44:36 +0100 Subject: Get it halfway working --- README.adoc | 10 +-- hex.c | 291 +++++++++++++++++++++++++++++++++++++----------------------- 2 files changed, 184 insertions(+), 117 deletions(-) diff --git a/README.adoc b/README.adoc index 0c2a3b7..6f550f1 100644 --- a/README.adoc +++ b/README.adoc @@ -6,7 +6,7 @@ hex Plans ----- In the future, it should be able to automatically interpret fields within files -via a set of Lua scripts. +using a set of Lua scripts. Packages -------- @@ -49,7 +49,7 @@ Create _~/.config/hex/hex.conf_ with contents like the following: .... colors = { - header = "" + footer = "" highlight = "bold" bar = "reverse" bar_active = "ul" @@ -61,11 +61,9 @@ colors = { Terminal caveats ---------------- -This application aspires to be as close to a GUI as possible. It expects you -to use the mouse (though it's not required). Terminals are, however, somewhat -tricky to get consistent results on, so be aware of the following: +Terminals are somewhat tricky to get consistent results on, so be aware of the +following: - - use a UTF-8 locale to get finer resolution progress bars and scrollbars - Xterm needs `XTerm*metaSendsEscape: true` for the default bindings to work - urxvt's 'vtwheel' plugin sabotages scrolling diff --git a/hex.c b/hex.c index bc6398f..58b6b6b 100644 --- a/hex.c +++ b/hex.c @@ -1,5 +1,5 @@ /* - * hex -- a very simple hex viewer + * hex -- hex viewer * * Copyright (c) 2016, Přemysl Janouch * All rights reserved. @@ -22,19 +22,19 @@ // We "need" to have an enum for attributes before including liberty. // Avoiding colours in the defaults here in order to support dumb terminals. -#define ATTRIBUTE_TABLE(XX) \ - XX( HEADER, "header", -1, -1, 0 ) \ - XX( HIGHLIGHT, "highlight", -1, -1, A_BOLD ) \ - /* Bar */ \ - XX( BAR, "bar", -1, -1, A_REVERSE ) \ - XX( BAR_ACTIVE, "bar_active", -1, -1, A_UNDERLINE ) \ - /* Listview */ \ - XX( EVEN, "even", -1, -1, 0 ) \ - XX( ODD, "odd", -1, -1, 0 ) \ - XX( SELECTION, "selection", -1, -1, A_REVERSE ) \ - /* These are for debugging only */ \ - XX( WARNING, "warning", 3, -1, 0 ) \ - XX( ERROR, "error", 1, -1, 0 ) +#define ATTRIBUTE_TABLE(XX) \ + XX( FOOTER, "footer", -1, -1, 0 ) \ + XX( HIGHLIGHT, "highlight", -1, -1, A_BOLD ) \ + /* Bar */ \ + XX( BAR, "bar", -1, -1, A_REVERSE ) \ + XX( BAR_ACTIVE, "bar_active", -1, -1, A_REVERSE | A_BOLD ) \ + /* View */ \ + XX( EVEN, "even", -1, -1, 0 ) \ + XX( ODD, "odd", -1, -1, 0 ) \ + XX( SELECTION, "selection", -1, -1, A_REVERSE ) \ + /* These are for debugging only */ \ + XX( WARNING, "warning", 3, -1, 0 ) \ + XX( ERROR, "error", 1, -1, 0 ) enum { @@ -96,6 +96,12 @@ enum FOOTER_SIZE = 4, ///< How many rows form the footer }; +enum endianity +{ + ENDIANITY_LE, ///< Little endian + ENDIANITY_BE ///< Big endian +}; + static struct app_context { // Event loop: @@ -113,16 +119,16 @@ static struct app_context char *filename; ///< Target filename uint8_t *data; ///< Target data - uint64_t data_len; ///< Length of the data - uint64_t data_offset; ///< Offset of the data within the file + int64_t data_len; ///< Length of the data + int64_t data_offset; ///< Offset of the data within the file - uint64_t view_top; ///< Offset of the top of the screen - uint64_t view_cursor; ///< Offset of the cursor + // View: - // TODO: get rid of this as it can be computed from "data*" - size_t item_count; ///< Total item count - int item_top; ///< Index of the topmost item - int item_selected; ///< Index of the selected item + int64_t view_top; ///< Offset of the top of the screen + int64_t view_cursor; ///< Offset of the cursor + bool view_skip_nibble; ///< Half-byte offset + + enum endianity endianity; ///< Endianity // Emulated widgets: @@ -345,10 +351,10 @@ app_visible_items (void) static void app_draw_view (void) { - uint64_t end_addr = g_ctx.data_offset + g_ctx.data_len; + int64_t end_addr = g_ctx.data_offset + g_ctx.data_len; for (int y = 0; y < app_visible_items (); y++) { - uint64_t row_addr = g_ctx.view_top + y * ROW_SIZE; + int64_t row_addr = g_ctx.view_top + y * ROW_SIZE; if (row_addr > end_addr) break; @@ -358,7 +364,7 @@ app_draw_view (void) struct row_buffer buf; row_buffer_init (&buf); - char *row_addr_str = xstrdup_printf ("%08" PRIX64, row_addr); + char *row_addr_str = xstrdup_printf ("%08" PRIx64, row_addr); row_buffer_append (&buf, row_addr_str, row_attrs); free (row_addr_str); @@ -371,7 +377,7 @@ app_draw_view (void) if (x % 8 == 0) row_buffer_append (&buf, " ", row_attrs); if (x % 2 == 0) row_buffer_append (&buf, " ", row_attrs); - uint64_t cell_addr = row_addr + x; + int64_t cell_addr = row_addr + x; if (cell_addr < g_ctx.data_offset || cell_addr >= end_addr) { @@ -412,23 +418,39 @@ app_draw_footer (void) row_buffer_init (&buf); row_buffer_append (&buf, APP_TITLE, a_normal); - row_buffer_append (&buf, " ", a_normal); - // TODO: transcode from locale encoding - row_buffer_append (&buf, g_ctx.filename, a_active); + + if (g_ctx.filename) + { + row_buffer_append (&buf, " ", a_normal); + char *filename = (char *) u8_strconv_from_locale (g_ctx.filename); + row_buffer_append (&buf, filename, a_active); + free (filename); + } struct str right; str_init (&right); str_append_printf (&right, "%08" PRIx64 " ", g_ctx.view_cursor); - // TODO: endian switch - str_append (&right, "?? "); - // TODO: position indication - str_append (&right, "???"); + str_append_printf (&right, + "%s ", g_ctx.endianity == ENDIANITY_LE ? "LE" : "BE"); + + int64_t top = g_ctx.view_top; + int64_t bot = g_ctx.view_top + app_visible_items () * ROW_SIZE; + if (top <= g_ctx.data_offset + && bot > g_ctx.data_offset + g_ctx.data_len) + str_append (&right, "All"); + else if (top <= g_ctx.data_offset) + str_append (&right, "Top"); + else if (bot > g_ctx.data_offset + g_ctx.data_len) + str_append (&right, "Bot"); + else + // TODO: position indication in percents + str_append (&right, "??%"); row_buffer_align (&buf, COLS - right.len, a_normal); row_buffer_append (&buf, right.str, a_normal); app_flush_buffer (&buf, COLS, a_normal); - uint64_t end_addr = g_ctx.data_offset + g_ctx.data_len; + int64_t end_addr = g_ctx.data_offset + g_ctx.data_len; if (g_ctx.view_cursor < g_ctx.data_offset || g_ctx.view_cursor >= end_addr) return; @@ -440,37 +462,52 @@ app_draw_footer (void) int64_t len = end_addr - g_ctx.view_cursor; uint8_t *cursor = g_ctx.data + (g_ctx.view_cursor - g_ctx.data_offset); - // TODO: convert endianity, show full numbers + uint8_t copy[8] = {}; + memcpy (copy, cursor, MIN (len, 8)); + + if (g_ctx.endianity == ENDIANITY_BE) + for (int i = 0; i < 4; i++) + { + uint8_t tmp = copy[7 - i]; + copy[7 - i] = copy[i]; + copy[i] = tmp; + } + // TODO: make the headers bold - const char *coding = "??"; + const char *coding = g_ctx.endianity == ENDIANITY_LE ? "le" : "be"; if (len >= 1) { - str_append_printf (&x, "x8 %02x", cursor[0]); - str_append_printf (&u, "u8 %4u", cursor[0]); - str_append_printf (&s, "s8 %4d", cursor[0]); + str_append_printf (&x, "x8 %02x", copy[0]); + str_append_printf (&u, "u8 %4u", copy[0]); + str_append_printf (&s, "s8 %4d", copy[0]); } if (len >= 2) { - str_append_printf (&x, " x16%s %04x", coding, cursor[0]); - str_append_printf (&u, " u16%s %6u", coding, cursor[0]); - str_append_printf (&s, " s16%s %6d", coding, cursor[0]); + uint16_t val = copy[0] | copy[1] << 8; + str_append_printf (&x, " x16%s %04x", coding, val); + str_append_printf (&u, " u16%s %6u", coding, val); + str_append_printf (&s, " s16%s %6d", coding, val); } if (len >= 4) { - str_append_printf (&x, " x32%s %08x", coding, cursor[0]); - str_append_printf (&u, " u32%s %11u", coding, cursor[0]); - str_append_printf (&s, " s32%s %11d", coding, cursor[0]); + uint32_t val = copy[0] | copy[1] << 8 | copy[2] << 16 | copy[3] << 24; + str_append_printf (&x, " x32%s %08x", coding, val); + str_append_printf (&u, " u32%s %11u", coding, val); + str_append_printf (&s, " s32%s %11d", coding, val); } if (len >= 8) { - str_append_printf (&x, " x64%s %016x", coding, cursor[0]); - str_append_printf (&u, " u64%s %20u", coding, cursor[0]); - str_append_printf (&s, " s64%s %20d", coding, cursor[0]); + uint64_t val = copy[0] | copy[1] << 8 | copy[2] << 16 | copy[3] << 24 + | (uint64_t) copy[4] << 32 | (uint64_t) copy[5] << 40 + | (uint64_t) copy[6] << 48 | (uint64_t) copy[7] << 56; + str_append_printf (&x, " x64%s %016" PRIx64, coding, val); + str_append_printf (&u, " u64%s %20" PRIu64, coding, val); + str_append_printf (&s, " s64%s %20" PRId64, coding, val); } - app_write_line (x.str, APP_ATTR (HEADER)); - app_write_line (u.str, APP_ATTR (HEADER)); - app_write_line (s.str, APP_ATTR (HEADER)); + app_write_line (x.str, APP_ATTR (FOOTER)); + app_write_line (u.str, APP_ATTR (FOOTER)); + app_write_line (s.str, APP_ATTR (FOOTER)); str_free (&x); str_free (&u); @@ -490,7 +527,7 @@ app_on_refresh (void *user_data) int64_t diff = g_ctx.view_cursor - g_ctx.view_top; int y = diff / ROW_SIZE; int x = diff % ROW_SIZE; - move (y, x + 10 + x/8 + x/2); + move (y, 10 + x*2 + g_ctx.view_skip_nibble + x/8 + x/2); refresh (); } @@ -501,21 +538,22 @@ app_on_refresh (void *user_data) static bool app_fix_view_range (void) { - if (g_ctx.item_top < 0) + if (g_ctx.view_top < g_ctx.data_offset / ROW_SIZE * ROW_SIZE) { - g_ctx.item_top = 0; + g_ctx.view_top = g_ctx.data_offset / ROW_SIZE * ROW_SIZE; app_invalidate (); return false; } // If the contents are at least as long as the screen, always fill it - int max_item_top = (int) g_ctx.item_count - app_visible_items (); + int64_t max_view_top = (g_ctx.data_offset + g_ctx.data_len - 1) + / ROW_SIZE * ROW_SIZE - app_visible_items () * ROW_SIZE; // But don't let that suggest a negative offset - max_item_top = MAX (max_item_top, 0); + max_view_top = MAX (max_view_top, 0); - if (g_ctx.item_top > max_item_top) + if (g_ctx.view_top > max_view_top) { - g_ctx.item_top = max_item_top; + g_ctx.view_top = max_view_top; app_invalidate (); return false; } @@ -526,7 +564,7 @@ app_fix_view_range (void) static bool app_scroll (int n) { - g_ctx.item_top += n; + g_ctx.view_top += n * ROW_SIZE; app_invalidate (); return app_fix_view_range (); } @@ -534,15 +572,12 @@ app_scroll (int n) static void app_ensure_selection_visible (void) { - if (g_ctx.item_selected < 0) - return; - - int too_high = g_ctx.item_top - g_ctx.item_selected; + int too_high = g_ctx.view_top / ROW_SIZE - g_ctx.view_cursor / ROW_SIZE; if (too_high > 0) app_scroll (-too_high); - int too_low = g_ctx.item_selected - - (g_ctx.item_top + app_visible_items () - 1); + int too_low = g_ctx.view_cursor / ROW_SIZE + - (g_ctx.view_top / ROW_SIZE + app_visible_items () - 1); if (too_low > 0) app_scroll (too_low); } @@ -550,12 +585,13 @@ app_ensure_selection_visible (void) static bool app_move_selection (int diff) { - int fixed = g_ctx.item_selected += diff; - fixed = MAX (fixed, 0); - fixed = MIN (fixed, (int) g_ctx.item_count - 1); + // TODO: disallow partial up/down movement + int64_t fixed = g_ctx.view_cursor += diff * ROW_SIZE; + fixed = MAX (fixed, g_ctx.data_offset); + fixed = MIN (fixed, g_ctx.data_offset + g_ctx.data_len - 1); - bool result = g_ctx.item_selected != fixed; - g_ctx.item_selected = fixed; + bool result = g_ctx.view_cursor != fixed; + g_ctx.view_cursor = fixed; app_invalidate (); app_ensure_selection_visible (); @@ -570,8 +606,7 @@ enum action ACTION_QUIT, ACTION_REDRAW, - ACTION_CHOOSE, - ACTION_DELETE, + ACTION_TOGGLE_ENDIANITY, ACTION_SCROLL_UP, ACTION_SCROLL_DOWN, @@ -603,31 +638,36 @@ app_process_action (enum action action) app_invalidate (); break; +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + case ACTION_TOGGLE_ENDIANITY: + g_ctx.endianity = (g_ctx.endianity == ENDIANITY_LE) + ? ENDIANITY_BE : ENDIANITY_LE; + app_invalidate (); + break; + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // XXX: these should rather be parametrized case ACTION_SCROLL_UP: - app_scroll (-3); + app_scroll (-1); break; case ACTION_SCROLL_DOWN: - app_scroll (3); + app_scroll (1); break; case ACTION_GOTO_TOP: - if (g_ctx.item_count) - { - g_ctx.item_selected = 0; - app_ensure_selection_visible (); - app_invalidate (); - } + g_ctx.view_cursor = g_ctx.data_offset; + app_ensure_selection_visible (); + app_invalidate (); break; case ACTION_GOTO_BOTTOM: - if (g_ctx.item_count) - { - g_ctx.item_selected = (int) g_ctx.item_count - 1; - app_ensure_selection_visible (); - app_invalidate (); - } + if (!g_ctx.data_len) + break; + + g_ctx.view_cursor = g_ctx.data_offset + g_ctx.data_len - 1; + app_ensure_selection_visible (); + app_invalidate (); break; case ACTION_GOTO_PAGE_PREVIOUS: @@ -646,10 +686,28 @@ app_process_action (enum action action) app_move_selection (1); break; case ACTION_LEFT: - // TODO: decrement cursor, check bounds, invalidate + if (g_ctx.view_skip_nibble) + g_ctx.view_skip_nibble = false; + else + { + // TODO: check bounds + g_ctx.view_skip_nibble = true; + g_ctx.view_cursor--; + } + app_ensure_selection_visible (); + app_invalidate (); break; case ACTION_RIGHT: - // TODO: increment cursor, check bounds, invalidate + if (!g_ctx.view_skip_nibble) + g_ctx.view_skip_nibble = true; + else + { + // TODO: check bounds + g_ctx.view_skip_nibble = false; + g_ctx.view_cursor++; + } + app_ensure_selection_visible (); + app_invalidate (); break; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -670,16 +728,24 @@ app_process_left_mouse_click (int line, int column) { if (line == app_visible_items ()) { - // TODO: LE/BE switch, maybe something else + if (column < COLS - 7 + || column >= COLS - 5) + return false; + + // TODO: LE/BE switch } else if (line < app_visible_items ()) { - int row_index = line; - if (row_index < 0 - || row_index >= (int) g_ctx.item_count - g_ctx.item_top) + if (line < 0) + return false; + + // TODO: convert and check "column" + int offset = 0; + if (column < 10 || column >= 50) return false; - g_ctx.item_selected = row_index + g_ctx.item_top; + // TODO: check if the result is within bounds and return false if not + g_ctx.view_cursor = g_ctx.view_top + line * ROW_SIZE + offset; app_invalidate (); } return true; @@ -712,27 +778,30 @@ g_default_bindings[] = { "Escape", ACTION_QUIT }, { "q", ACTION_QUIT }, { "C-l", ACTION_REDRAW }, - // TODO: Tab switches endianity + { "Tab", ACTION_TOGGLE_ENDIANITY }, { "Home", ACTION_GOTO_TOP }, { "End", ACTION_GOTO_BOTTOM }, { "M-<", ACTION_GOTO_TOP }, { "M->", ACTION_GOTO_BOTTOM }, - { "Up", ACTION_UP }, - { "Down", ACTION_DOWN }, - { "k", ACTION_UP }, - { "j", ACTION_DOWN }, { "PageUp", ACTION_GOTO_PAGE_PREVIOUS }, { "PageDown", ACTION_GOTO_PAGE_NEXT }, - { "C-p", ACTION_UP }, - { "C-n", ACTION_DOWN }, { "C-b", ACTION_GOTO_PAGE_PREVIOUS }, { "C-f", ACTION_GOTO_PAGE_NEXT }, - // TODO: C-e and C-y scroll up and down - // Not sure how to set these up, they're pretty arbitrary so far - { "Enter", ACTION_CHOOSE }, - { "Delete", ACTION_DELETE }, + { "Up", ACTION_UP }, + { "Down", ACTION_DOWN }, + { "Left", ACTION_LEFT }, + { "Right", ACTION_RIGHT }, + { "k", ACTION_UP }, + { "j", ACTION_DOWN }, + { "h", ACTION_LEFT }, + { "l", ACTION_RIGHT }, + { "C-p", ACTION_UP }, + { "C-n", ACTION_DOWN }, + + { "C-y", ACTION_SCROLL_UP }, + { "C-e", ACTION_SCROLL_DOWN }, }; static bool @@ -935,15 +1004,15 @@ app_init_poller_events (void) /// Decode size arguments according to similar rules to those that dd(1) uses; /// we support octal and hexadecimal numbers but they clash with suffixes static bool -decode_size (const char *s, uint64_t *out) +decode_size (const char *s, int64_t *out) { char *end; errno = 0; - uint64_t n = strtoul (s, &end, 0); - if (errno != 0 || end == s) + int64_t n = strtol (s, &end, 0); + if (errno != 0 || end == s || n < 0) return false; - uint64_t f = 1; + int64_t f = 1; switch (*end) { case 'c': f = 1 << 0; end++; break; @@ -954,7 +1023,7 @@ decode_size (const char *s, uint64_t *out) case 'M': f = 1 << 20; if (*++end == 'B') { f = 1e6; end++; } break; case 'G': f = 1 << 30; if (*++end == 'B') { f = 1e9; end++; } break; } - if (*end || n > UINT64_MAX / f) + if (*end || n > INT64_MAX / f) return false; *out = n * f; @@ -977,7 +1046,7 @@ main (int argc, char *argv[]) struct opt_handler oh; opt_handler_init (&oh, argc, argv, opts, "[FILE]", "Hex viewer."); - uint64_t size_limit = 1 << 30; + int64_t size_limit = 1 << 30; int c; while ((c = opt_handler_get (&oh)) != -1) @@ -1049,7 +1118,7 @@ main (int argc, char *argv[]) struct str buf; str_init (&buf); - while (buf.len < size_limit) + while (buf.len < (size_t) size_limit) { str_ensure_space (&buf, 8192); ssize_t n_read = read (input_fd, buf.str + buf.len, -- cgit v1.2.3