Get it halfway working

This commit is contained in:
Přemysl Eric Janouch 2016-12-30 01:44:36 +01:00
parent 5c4be81f25
commit 742647ef80
Signed by: p
GPG Key ID: B715679E3A361BE6
2 changed files with 184 additions and 117 deletions

View File

@ -6,7 +6,7 @@ hex
Plans Plans
----- -----
In the future, it should be able to automatically interpret fields within files 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 Packages
-------- --------
@ -49,7 +49,7 @@ Create _~/.config/hex/hex.conf_ with contents like the following:
.... ....
colors = { colors = {
header = "" footer = ""
highlight = "bold" highlight = "bold"
bar = "reverse" bar = "reverse"
bar_active = "ul" bar_active = "ul"
@ -61,11 +61,9 @@ colors = {
Terminal caveats Terminal caveats
---------------- ----------------
This application aspires to be as close to a GUI as possible. It expects you Terminals are somewhat tricky to get consistent results on, so be aware of the
to use the mouse (though it's not required). Terminals are, however, somewhat following:
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 - Xterm needs `XTerm*metaSendsEscape: true` for the default bindings to work
- urxvt's 'vtwheel' plugin sabotages scrolling - urxvt's 'vtwheel' plugin sabotages scrolling

291
hex.c
View File

@ -1,5 +1,5 @@
/* /*
* hex -- a very simple hex viewer * hex -- hex viewer
* *
* Copyright (c) 2016, Přemysl Janouch <p.janouch@gmail.com> * Copyright (c) 2016, Přemysl Janouch <p.janouch@gmail.com>
* All rights reserved. * All rights reserved.
@ -22,19 +22,19 @@
// We "need" to have an enum for attributes before including liberty. // We "need" to have an enum for attributes before including liberty.
// Avoiding colours in the defaults here in order to support dumb terminals. // Avoiding colours in the defaults here in order to support dumb terminals.
#define ATTRIBUTE_TABLE(XX) \ #define ATTRIBUTE_TABLE(XX) \
XX( HEADER, "header", -1, -1, 0 ) \ XX( FOOTER, "footer", -1, -1, 0 ) \
XX( HIGHLIGHT, "highlight", -1, -1, A_BOLD ) \ XX( HIGHLIGHT, "highlight", -1, -1, A_BOLD ) \
/* Bar */ \ /* Bar */ \
XX( BAR, "bar", -1, -1, A_REVERSE ) \ XX( BAR, "bar", -1, -1, A_REVERSE ) \
XX( BAR_ACTIVE, "bar_active", -1, -1, A_UNDERLINE ) \ XX( BAR_ACTIVE, "bar_active", -1, -1, A_REVERSE | A_BOLD ) \
/* Listview */ \ /* View */ \
XX( EVEN, "even", -1, -1, 0 ) \ XX( EVEN, "even", -1, -1, 0 ) \
XX( ODD, "odd", -1, -1, 0 ) \ XX( ODD, "odd", -1, -1, 0 ) \
XX( SELECTION, "selection", -1, -1, A_REVERSE ) \ XX( SELECTION, "selection", -1, -1, A_REVERSE ) \
/* These are for debugging only */ \ /* These are for debugging only */ \
XX( WARNING, "warning", 3, -1, 0 ) \ XX( WARNING, "warning", 3, -1, 0 ) \
XX( ERROR, "error", 1, -1, 0 ) XX( ERROR, "error", 1, -1, 0 )
enum enum
{ {
@ -96,6 +96,12 @@ enum
FOOTER_SIZE = 4, ///< How many rows form the footer FOOTER_SIZE = 4, ///< How many rows form the footer
}; };
enum endianity
{
ENDIANITY_LE, ///< Little endian
ENDIANITY_BE ///< Big endian
};
static struct app_context static struct app_context
{ {
// Event loop: // Event loop:
@ -113,16 +119,16 @@ static struct app_context
char *filename; ///< Target filename char *filename; ///< Target filename
uint8_t *data; ///< Target data uint8_t *data; ///< Target data
uint64_t data_len; ///< Length of the data int64_t data_len; ///< Length of the data
uint64_t data_offset; ///< Offset of the data within the file int64_t data_offset; ///< Offset of the data within the file
uint64_t view_top; ///< Offset of the top of the screen // View:
uint64_t view_cursor; ///< Offset of the cursor
// TODO: get rid of this as it can be computed from "data*" int64_t view_top; ///< Offset of the top of the screen
size_t item_count; ///< Total item count int64_t view_cursor; ///< Offset of the cursor
int item_top; ///< Index of the topmost item bool view_skip_nibble; ///< Half-byte offset
int item_selected; ///< Index of the selected item
enum endianity endianity; ///< Endianity
// Emulated widgets: // Emulated widgets:
@ -345,10 +351,10 @@ app_visible_items (void)
static void static void
app_draw_view (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++) 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) if (row_addr > end_addr)
break; break;
@ -358,7 +364,7 @@ app_draw_view (void)
struct row_buffer buf; struct row_buffer buf;
row_buffer_init (&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); row_buffer_append (&buf, row_addr_str, row_attrs);
free (row_addr_str); free (row_addr_str);
@ -371,7 +377,7 @@ app_draw_view (void)
if (x % 8 == 0) row_buffer_append (&buf, " ", row_attrs); if (x % 8 == 0) row_buffer_append (&buf, " ", row_attrs);
if (x % 2 == 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 if (cell_addr < g_ctx.data_offset
|| cell_addr >= end_addr) || cell_addr >= end_addr)
{ {
@ -412,23 +418,39 @@ app_draw_footer (void)
row_buffer_init (&buf); row_buffer_init (&buf);
row_buffer_append (&buf, APP_TITLE, a_normal); row_buffer_append (&buf, APP_TITLE, a_normal);
row_buffer_append (&buf, " ", a_normal);
// TODO: transcode from locale encoding if (g_ctx.filename)
row_buffer_append (&buf, g_ctx.filename, a_active); {
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; struct str right;
str_init (&right); str_init (&right);
str_append_printf (&right, "%08" PRIx64 " ", g_ctx.view_cursor); str_append_printf (&right, "%08" PRIx64 " ", g_ctx.view_cursor);
// TODO: endian switch str_append_printf (&right,
str_append (&right, "?? "); "%s ", g_ctx.endianity == ENDIANITY_LE ? "LE" : "BE");
// TODO: position indication
str_append (&right, "???"); 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_align (&buf, COLS - right.len, a_normal);
row_buffer_append (&buf, right.str, a_normal); row_buffer_append (&buf, right.str, a_normal);
app_flush_buffer (&buf, COLS, 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 if (g_ctx.view_cursor < g_ctx.data_offset
|| g_ctx.view_cursor >= end_addr) || g_ctx.view_cursor >= end_addr)
return; return;
@ -440,37 +462,52 @@ app_draw_footer (void)
int64_t len = end_addr - g_ctx.view_cursor; int64_t len = end_addr - g_ctx.view_cursor;
uint8_t *cursor = g_ctx.data + (g_ctx.view_cursor - g_ctx.data_offset); 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 // TODO: make the headers bold
const char *coding = "??"; const char *coding = g_ctx.endianity == ENDIANITY_LE ? "le" : "be";
if (len >= 1) if (len >= 1)
{ {
str_append_printf (&x, "x8 %02x", cursor[0]); str_append_printf (&x, "x8 %02x", copy[0]);
str_append_printf (&u, "u8 %4u", cursor[0]); str_append_printf (&u, "u8 %4u", copy[0]);
str_append_printf (&s, "s8 %4d", cursor[0]); str_append_printf (&s, "s8 %4d", copy[0]);
} }
if (len >= 2) if (len >= 2)
{ {
str_append_printf (&x, " x16%s %04x", coding, cursor[0]); uint16_t val = copy[0] | copy[1] << 8;
str_append_printf (&u, " u16%s %6u", coding, cursor[0]); str_append_printf (&x, " x16%s %04x", coding, val);
str_append_printf (&s, " s16%s %6d", coding, cursor[0]); str_append_printf (&u, " u16%s %6u", coding, val);
str_append_printf (&s, " s16%s %6d", coding, val);
} }
if (len >= 4) if (len >= 4)
{ {
str_append_printf (&x, " x32%s %08x", coding, cursor[0]); uint32_t val = copy[0] | copy[1] << 8 | copy[2] << 16 | copy[3] << 24;
str_append_printf (&u, " u32%s %11u", coding, cursor[0]); str_append_printf (&x, " x32%s %08x", coding, val);
str_append_printf (&s, " s32%s %11d", coding, cursor[0]); str_append_printf (&u, " u32%s %11u", coding, val);
str_append_printf (&s, " s32%s %11d", coding, val);
} }
if (len >= 8) if (len >= 8)
{ {
str_append_printf (&x, " x64%s %016x", coding, cursor[0]); uint64_t val = copy[0] | copy[1] << 8 | copy[2] << 16 | copy[3] << 24
str_append_printf (&u, " u64%s %20u", coding, cursor[0]); | (uint64_t) copy[4] << 32 | (uint64_t) copy[5] << 40
str_append_printf (&s, " s64%s %20d", coding, cursor[0]); | (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 (x.str, APP_ATTR (FOOTER));
app_write_line (u.str, APP_ATTR (HEADER)); app_write_line (u.str, APP_ATTR (FOOTER));
app_write_line (s.str, APP_ATTR (HEADER)); app_write_line (s.str, APP_ATTR (FOOTER));
str_free (&x); str_free (&x);
str_free (&u); str_free (&u);
@ -490,7 +527,7 @@ app_on_refresh (void *user_data)
int64_t diff = g_ctx.view_cursor - g_ctx.view_top; int64_t diff = g_ctx.view_cursor - g_ctx.view_top;
int y = diff / ROW_SIZE; int y = diff / ROW_SIZE;
int x = 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 (); refresh ();
} }
@ -501,21 +538,22 @@ app_on_refresh (void *user_data)
static bool static bool
app_fix_view_range (void) 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 (); app_invalidate ();
return false; return false;
} }
// If the contents are at least as long as the screen, always fill it // 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 // 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 (); app_invalidate ();
return false; return false;
} }
@ -526,7 +564,7 @@ app_fix_view_range (void)
static bool static bool
app_scroll (int n) app_scroll (int n)
{ {
g_ctx.item_top += n; g_ctx.view_top += n * ROW_SIZE;
app_invalidate (); app_invalidate ();
return app_fix_view_range (); return app_fix_view_range ();
} }
@ -534,15 +572,12 @@ app_scroll (int n)
static void static void
app_ensure_selection_visible (void) app_ensure_selection_visible (void)
{ {
if (g_ctx.item_selected < 0) int too_high = g_ctx.view_top / ROW_SIZE - g_ctx.view_cursor / ROW_SIZE;
return;
int too_high = g_ctx.item_top - g_ctx.item_selected;
if (too_high > 0) if (too_high > 0)
app_scroll (-too_high); app_scroll (-too_high);
int too_low = g_ctx.item_selected int too_low = g_ctx.view_cursor / ROW_SIZE
- (g_ctx.item_top + app_visible_items () - 1); - (g_ctx.view_top / ROW_SIZE + app_visible_items () - 1);
if (too_low > 0) if (too_low > 0)
app_scroll (too_low); app_scroll (too_low);
} }
@ -550,12 +585,13 @@ app_ensure_selection_visible (void)
static bool static bool
app_move_selection (int diff) app_move_selection (int diff)
{ {
int fixed = g_ctx.item_selected += diff; // TODO: disallow partial up/down movement
fixed = MAX (fixed, 0); int64_t fixed = g_ctx.view_cursor += diff * ROW_SIZE;
fixed = MIN (fixed, (int) g_ctx.item_count - 1); 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; bool result = g_ctx.view_cursor != fixed;
g_ctx.item_selected = fixed; g_ctx.view_cursor = fixed;
app_invalidate (); app_invalidate ();
app_ensure_selection_visible (); app_ensure_selection_visible ();
@ -570,8 +606,7 @@ enum action
ACTION_QUIT, ACTION_QUIT,
ACTION_REDRAW, ACTION_REDRAW,
ACTION_CHOOSE, ACTION_TOGGLE_ENDIANITY,
ACTION_DELETE,
ACTION_SCROLL_UP, ACTION_SCROLL_UP,
ACTION_SCROLL_DOWN, ACTION_SCROLL_DOWN,
@ -603,31 +638,36 @@ app_process_action (enum action action)
app_invalidate (); app_invalidate ();
break; 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 // XXX: these should rather be parametrized
case ACTION_SCROLL_UP: case ACTION_SCROLL_UP:
app_scroll (-3); app_scroll (-1);
break; break;
case ACTION_SCROLL_DOWN: case ACTION_SCROLL_DOWN:
app_scroll (3); app_scroll (1);
break; break;
case ACTION_GOTO_TOP: case ACTION_GOTO_TOP:
if (g_ctx.item_count) g_ctx.view_cursor = g_ctx.data_offset;
{ app_ensure_selection_visible ();
g_ctx.item_selected = 0; app_invalidate ();
app_ensure_selection_visible ();
app_invalidate ();
}
break; break;
case ACTION_GOTO_BOTTOM: case ACTION_GOTO_BOTTOM:
if (g_ctx.item_count) if (!g_ctx.data_len)
{ break;
g_ctx.item_selected = (int) g_ctx.item_count - 1;
app_ensure_selection_visible (); g_ctx.view_cursor = g_ctx.data_offset + g_ctx.data_len - 1;
app_invalidate (); app_ensure_selection_visible ();
} app_invalidate ();
break; break;
case ACTION_GOTO_PAGE_PREVIOUS: case ACTION_GOTO_PAGE_PREVIOUS:
@ -646,10 +686,28 @@ app_process_action (enum action action)
app_move_selection (1); app_move_selection (1);
break; break;
case ACTION_LEFT: 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; break;
case ACTION_RIGHT: 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; break;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -670,16 +728,24 @@ app_process_left_mouse_click (int line, int column)
{ {
if (line == app_visible_items ()) 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 ()) else if (line < app_visible_items ())
{ {
int row_index = line; if (line < 0)
if (row_index < 0
|| row_index >= (int) g_ctx.item_count - g_ctx.item_top)
return false; return false;
g_ctx.item_selected = row_index + g_ctx.item_top; // TODO: convert and check "column"
int offset = 0;
if (column < 10 || column >= 50)
return false;
// 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 (); app_invalidate ();
} }
return true; return true;
@ -712,27 +778,30 @@ g_default_bindings[] =
{ "Escape", ACTION_QUIT }, { "Escape", ACTION_QUIT },
{ "q", ACTION_QUIT }, { "q", ACTION_QUIT },
{ "C-l", ACTION_REDRAW }, { "C-l", ACTION_REDRAW },
// TODO: Tab switches endianity { "Tab", ACTION_TOGGLE_ENDIANITY },
{ "Home", ACTION_GOTO_TOP }, { "Home", ACTION_GOTO_TOP },
{ "End", ACTION_GOTO_BOTTOM }, { "End", ACTION_GOTO_BOTTOM },
{ "M-<", ACTION_GOTO_TOP }, { "M-<", ACTION_GOTO_TOP },
{ "M->", ACTION_GOTO_BOTTOM }, { "M->", ACTION_GOTO_BOTTOM },
{ "Up", ACTION_UP },
{ "Down", ACTION_DOWN },
{ "k", ACTION_UP },
{ "j", ACTION_DOWN },
{ "PageUp", ACTION_GOTO_PAGE_PREVIOUS }, { "PageUp", ACTION_GOTO_PAGE_PREVIOUS },
{ "PageDown", ACTION_GOTO_PAGE_NEXT }, { "PageDown", ACTION_GOTO_PAGE_NEXT },
{ "C-p", ACTION_UP },
{ "C-n", ACTION_DOWN },
{ "C-b", ACTION_GOTO_PAGE_PREVIOUS }, { "C-b", ACTION_GOTO_PAGE_PREVIOUS },
{ "C-f", ACTION_GOTO_PAGE_NEXT }, { "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 { "Up", ACTION_UP },
{ "Enter", ACTION_CHOOSE }, { "Down", ACTION_DOWN },
{ "Delete", ACTION_DELETE }, { "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 static bool
@ -935,15 +1004,15 @@ app_init_poller_events (void)
/// Decode size arguments according to similar rules to those that dd(1) uses; /// Decode size arguments according to similar rules to those that dd(1) uses;
/// we support octal and hexadecimal numbers but they clash with suffixes /// we support octal and hexadecimal numbers but they clash with suffixes
static bool static bool
decode_size (const char *s, uint64_t *out) decode_size (const char *s, int64_t *out)
{ {
char *end; char *end;
errno = 0; errno = 0;
uint64_t n = strtoul (s, &end, 0); int64_t n = strtol (s, &end, 0);
if (errno != 0 || end == s) if (errno != 0 || end == s || n < 0)
return false; return false;
uint64_t f = 1; int64_t f = 1;
switch (*end) switch (*end)
{ {
case 'c': f = 1 << 0; end++; break; 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 'M': f = 1 << 20; if (*++end == 'B') { f = 1e6; end++; } break;
case 'G': f = 1 << 30; if (*++end == 'B') { f = 1e9; 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; return false;
*out = n * f; *out = n * f;
@ -977,7 +1046,7 @@ main (int argc, char *argv[])
struct opt_handler oh; struct opt_handler oh;
opt_handler_init (&oh, argc, argv, opts, "[FILE]", "Hex viewer."); opt_handler_init (&oh, argc, argv, opts, "[FILE]", "Hex viewer.");
uint64_t size_limit = 1 << 30; int64_t size_limit = 1 << 30;
int c; int c;
while ((c = opt_handler_get (&oh)) != -1) while ((c = opt_handler_get (&oh)) != -1)
@ -1049,7 +1118,7 @@ main (int argc, char *argv[])
struct str buf; struct str buf;
str_init (&buf); str_init (&buf);
while (buf.len < size_limit) while (buf.len < (size_t) size_limit)
{ {
str_ensure_space (&buf, 8192); str_ensure_space (&buf, 8192);
ssize_t n_read = read (input_fd, buf.str + buf.len, ssize_t n_read = read (input_fd, buf.str + buf.len,