From cdc5feaa7e2220a8747c0e0bdb0f3168a04246a8 Mon Sep 17 00:00:00 2001 From: Mattias Andrée Date: Tue, 9 Jun 2026 18:07:30 +0200 Subject: Work on blkshowr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- blkshowr.c | 653 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 607 insertions(+), 46 deletions(-) (limited to 'blkshowr.c') diff --git a/blkshowr.c b/blkshowr.c index bbb0ea0..4ca0724 100644 --- a/blkshowr.c +++ b/blkshowr.c @@ -20,6 +20,12 @@ static void configure_terminal(void); static void restore_terminal(void); +enum component { + BLOCK_SELECTOR, + BLOCK_DISPLAY, + NCOMPONENTS +}; + static size_t blksize = 4ul << 10; static off_t *blocks = NULL; static size_t blocks_size = 0u; @@ -27,8 +33,8 @@ static size_t nblocks = 0u; static int max_block_len; static volatile sig_atomic_t term_resized = 0; -static struct termios term_attributes_stdin; -static int term_attributes_stdin_saved = 0; +static struct termios term_attributes; +static int term_attributes_saved = 0; static int term_entered_submode_stdout = 0; static int ttyfd = -1; @@ -37,44 +43,580 @@ static size_t term_height; static int exiting = 0; static int interrupt_deferred = 0; +static size_t selected_block = 0u; +static size_t first_visible_block = 0u; +static size_t first_visible_line = 0u; +static size_t line_count = 0u; +static size_t *line_map = NULL; +static size_t line_map_size = 0u; +static enum component active_focus = BLOCK_SELECTOR; +static int textfd; +static const char *path; +static off_t current_block_off = -1; +static unsigned char *current_block_text = NULL; +static size_t current_block_text_size = 0u; +static size_t current_block_text_len = 0u; +static size_t display_width = 0u; +static int display_as_text = 0; + +static int limited_block_drawing; +static const char *const scroll_blocks8[] = {" ", "▁", "▂", "▃", "▄", "▅", "▆", "▇"}; +static const char *const scroll_blocks2[] = {scroll_blocks8[0], scroll_blocks8[4]}; +static const char *const scroll_blocks3[] = {" ", "\xf0\x9f\xac\xad", "\xf0\x9f\xac\xb9"}; /* emacs has broken support for these, its the block equivalents of "⠤" and "⠶" */ +static const char *const scroll_blocks1_3[] = {"\xf0\x9f\xac\xad", "\xf0\x9f\xac\x8b", "\xf0\x9f\xac\x82"}; /* emacs has broken support for these, its the block equivalents of "⠤", "⠒", and "⠉" */ + + +#define S(X)\ + X"0", X"1", X"2", X"3", X"4", X"5", X"6", X"7",\ + X"8", X"9", X"A", X"B", X"C", X"D", X"E", X"F" +#if defined(__GNUC__) +__attribute__((__nonstring__)) +#endif +static const char hex[256][2] = { + S("0"), S("1"), S("2"), S("3"), S("4"), S("5"), S("6"), S("7"), + S("8"), S("9"), S("A"), S("B"), S("C"), S("D"), S("E"), S("F") +}; +#undef S + + +#define PRINT(...) dprintf(ttyfd, __VA_ARGS__) + static void do_redraw(void) { - dprintf(ttyfd, "\033[H\033[2J"); + size_t last_visible_block = first_visible_block + term_height - 2u; + size_t blocks_top, blocks_bottom; + int block_pane_width = MAX(max_block_len, (int)sizeof("Block:") - 1) + 1; + size_t i, j, end, vis_end; + size_t new_display_width; + size_t right_pad; + size_t text_top, text_bottom; + + if (current_block_off != blocks[selected_block]) { + off_t off; + ssize_t r; + if (current_block_text_size < blksize) { + current_block_text = erealloc(current_block_text, blksize); + current_block_text_size = blksize; + } + off = current_block_off = blocks[selected_block]; + off *= (off_t)blksize; + for (i = 0u; i < blksize;) { + r = pread(textfd, ¤t_block_text[i], blksize - i, off); + if (r <= 0) { + if (!r) + break; + if (errno == EINTR) { + interrupt_deferred = 1; + continue; + } + eprintf("pread %s %zu %ji:", path, blksize - i, (intmax_t)off); + } + off += (off_t)r; + i += (size_t)r; + } + current_block_text_len = i; + } + + new_display_width = term_width; + new_display_width -= MIN(new_display_width, (size_t)block_pane_width); + new_display_width -= MIN(new_display_width, 2u); /* scrollbar for block list */ + new_display_width -= MIN(new_display_width, 3u); /* scrollbar for display incl. spacing */ + right_pad = new_display_width; + new_display_width /= 4u; + if (!display_as_text) { + if (first_visible_line && new_display_width && new_display_width != display_width) { + first_visible_line *= display_width; + first_visible_line /= new_display_width; + } + if (line_count == 0u && new_display_width) { + line_count = current_block_text_len / new_display_width; + line_count += current_block_text_len % new_display_width ? 1u : 0u; + } + right_pad -= new_display_width * 4u; + } + display_width = new_display_width; + + if (display_as_text && !line_count) {; + for (i = 0u; i + 1u < current_block_text_len; i++) + if (current_block_text[i] == '\n') + line_count += 1u; + if (current_block_text_len) + line_count += 1u; + if (line_map_size < line_count) { + line_map_size = line_count; + line_map = ereallocarray(line_map, line_map_size, sizeof(*line_map)); + line_map[0u] = 0u; + } + for (i = 0u, j = 1u; j < line_count; i++) + if (current_block_text[i] == '\n') + line_map[j++] = i + 1u; + } + + /* TODO what if terminal is too small? */ + + /* TODO make size stable */ + blocks_top = first_visible_block * (term_height - 2u) / nblocks; + blocks_bottom = (last_visible_block + 1u) * (term_height - 2u) / nblocks; + if (blocks_bottom == blocks_top) + blocks_bottom += 1u; + + /* TODO make size stable */ + if (line_count) { + size_t last_visible_line = first_visible_line + term_height - 2u; + last_visible_line = MIN(last_visible_line, line_count) - 1u; + text_top = first_visible_line * (term_height - 2u) / line_count; + text_bottom = (last_visible_line + 1u) * (term_height - 2u) / line_count; + if (text_bottom == text_top) + text_bottom += 1u; + } + + PRINT("\033[H"); + + PRINT("\033[7;1m%-*s\033[m\n", (int)term_width, "Block:"); + /* TODO add caption for display area */ + + /* TODO draw breaks if display_width is too large */ + for (i = 0u; i + 2u < term_height; i++) { + j = i + first_visible_block; + if (j == selected_block && active_focus == BLOCK_SELECTOR) + PRINT("\033[7;1;34m%-*ji\033[m", block_pane_width, (intmax_t)blocks[j]); + else if (j == selected_block) + PRINT("\033[34m%-*ji\033[m", block_pane_width, (intmax_t)blocks[j]); + else if (j < nblocks) + PRINT("%-*ji", block_pane_width, (intmax_t)blocks[j]); + else + PRINT("%*s", block_pane_width, ""); + + /* TODO need to fix colour support for less capable terminals */ + /* TODO make more granular */ + if (i < blocks_top || i >= blocks_bottom) + PRINT("\033[100m%s\033[m", " "); + else + PRINT("\033[107m%s\033[m", " "); + PRINT("\033[m"); + + if (display_as_text) { + int truncated = 1; + size_t cols = 0; + if (i + first_visible_line >= line_count) { + PRINT("\033[1;30m~\033[m"); + cols += 1u; + truncated = 0; + goto end_of_line; + } + j = line_map[i + first_visible_line]; + for (; j < current_block_text_len && cols < right_pad; j++) { + unsigned char c = current_block_text[j]; + const char *ctext = NULL; + char buf[8]; + switch (c) { + case '\a': PRINT("\033[36m"); ctext = "\\a"; break; + case '\b': PRINT("\033[36m"); ctext = "\\b"; break; + case '\033': PRINT("\033[36m"); ctext = "\\e"; break; + case '\r': PRINT("\033[36m"); ctext = "\\r"; break; + case '\v': PRINT("\033[36m"); ctext = "\\v"; break; + case '\f': PRINT("\033[36m"); ctext = "^L"; break; + case 0x7Fu: PRINT("\033[36m"); ctext = "^?"; break; + case ' ': PRINT("\033[90m.\033[m"); break; + case '\t': + PRINT("\033[90m→"); + memset(buf, ' ', 7u); + buf[7u - cols % 8u] = '\0'; + cols += 1u; + ctext = buf; + break; + case '\n': + PRINT("\033[90m\\"); + cols += 1u; + if (cols < right_pad) { + PRINT("n"); + cols += 1u; + } + PRINT("\033[m"); + truncated = 0; + goto end_of_line; + default: + if (c & 0x80u) { + /* TODO add support for UTF-8 */ + PRINT("\033[31m"); + ctext = buf; + buf[0u] = '\\'; + buf[1u] = 'x'; + buf[2u] = hex[c][0]; + buf[3u] = hex[c][1]; + buf[4u] = '\0'; + } else { + if (c < (unsigned char)' ') { + PRINT("\033[36m"); + ctext = buf; + buf[0u] = '^'; + buf[1u] = (char)(c + '@'); + buf[2u] = '\0'; + } else { + PRINT("%c", (char)c); + } + } + break; + } + if (ctext) { + size_t len = strlen(ctext); + int n = (int)MIN(len, right_pad - cols); + PRINT("%.*s\033[m", n, ctext); + cols += (size_t)n; + } else { + cols += 1u; + } + } + if (j == current_block_text_len) + truncated = 0; + + end_of_line: + if (right_pad - cols) + PRINT("%*s", (int)(right_pad - cols), ""); + if (truncated) + PRINT("\033[1;33m$"); + else + PRINT(" "); + goto next; + } + + j = (i + first_visible_line) * display_width; + vis_end = j + display_width; + end = MIN(vis_end, current_block_text_len); + for (; j < end; j++) { + unsigned char c = current_block_text[j]; + switch (c) { + case '\a': PRINT("\033[35ma\033[m"); break; + case '\b': PRINT("\033[35mb\033[m"); break; + case '\033': PRINT("\033[35me\033[m"); break; + case '\f': PRINT("\033[35mf\033[m"); break; + case '\n': PRINT("\033[1;35mn\033[m"); break; + case '\r': PRINT("\033[35mr\033[m"); break; + case '\t': PRINT("\033[35mt\033[m"); break; + case '\v': PRINT("\033[35mv\033[m"); break; + case ' ': PRINT("\033[35m.\033[m"); break; + case 0x7Fu: PRINT("\033[36m?\033[m"); break; + default: + if (c & 0x80u) { + c &= 0x7Fu; + if (c == 0x7Fu) + PRINT("\033[32m?\033[m"); + else if (c == (unsigned char)' ') + PRINT("\033[32m.\033[m"); + else if (c < (unsigned char)' ') + PRINT("\033[32m%c\033[m", (char)(c + '@')); + else + PRINT("\033[33m%c\033[m", (char)c); + } else { + if (c < (unsigned char)' ') + PRINT("\033[36m%c\033[m", (char)(c + '@')); + else + PRINT("%c", (char)c); + } + break; + } + } + if (end != vis_end) + PRINT("%*s", (int)(vis_end - end), ""); + + j = (i + first_visible_line) * display_width; + for (; j < end; j++) { + unsigned char c = current_block_text[j]; + switch (c) { + case '\n': + PRINT("\033[1;35m"); + break; + case '\a': + case '\b': + case '\033': + case '\f': + case '\r': + case '\t': + case '\v': + case ' ': + PRINT("\033[35m"); + break; + case 0x7Fu: + PRINT("\033[36m"); + break; + default: + if (c & 0x80u) { + if (c == 0xFFu || c <= (unsigned char)' ' + 0x80u) + PRINT("\033[32m"); + else + PRINT("\033[33m"); + } else { + if (c < (unsigned char)' ') + PRINT("\033[36m"); + } + break; + } + PRINT(" %.2s\033[m", hex[c]); + } + if (end != vis_end || right_pad) + PRINT("%*s", (int)((vis_end - end) * 3u + right_pad), ""); + + PRINT(" "); + next: + /* TODO need to fix colour support for less capable terminals */ + /* TODO make more granular */ + if (line_count == 0u) + PRINT("\033[100m%s\033[m", " "); + else if (i < text_top || i >= text_bottom) + PRINT("\033[100m%s\033[m", " "); + else if (active_focus == BLOCK_DISPLAY) + PRINT("\033[104m%s\033[m", " "); + else + PRINT("\033[107m%s\033[m", " "); + + PRINT("\033[K\n"); + } + + PRINT("\033[7;1m%*s\033[m\033[H", (int)term_width, ""); +} + + +static int +ensure_selected_block_visible(void) +{ + if (selected_block < first_visible_block) { + first_visible_block = selected_block; + return 1; + } else if (term_height < 3u) { + first_visible_block = selected_block; + return 0; + } else if (selected_block - first_visible_block > term_height - 3u) { + first_visible_block = selected_block - (term_height - 3u); + return 1; + } else { + return 0; + } +} + + +static int +handle_keyboard_input_global(union libterminput_input *input) +{ + if (input->type != LIBTERMINPUT_KEYPRESS) + return 0; + + if (IS_CTRL_KEY(input, 'Z')) { + restore_terminal(); + raise(SIGTSTP); + configure_terminal(); + redraw: + interrupt_deferred = 1; + term_resized = 1; + + } else if (IS_CTRL_KEY(input, 'L')) { + goto redraw; + + } else if (IS_CTRL_KEY(input, 'C') || + IS_CTRL_KEY(input, 'Q')) { + exiting = 1; + + } else if (IS_KEY(input, 't')) { + /* TODO need a horizontal display offset */ + display_as_text ^= 1; + line_count = 0u; + do_redraw(); + + } else if (input->keypress.key == LIBTERMINPUT_TAB) { + if (input->keypress.mods & LIBTERMINPUT_SHIFT) + goto backtab; + active_focus += 1; + active_focus %= NCOMPONENTS; + do_redraw(); + + } else if (input->keypress.key == LIBTERMINPUT_BACKTAB) { + backtab: + active_focus -= 1; + if (active_focus < 0) + active_focus += NCOMPONENTS; + do_redraw(); + + } else { + return 0; + } + + /* TODO delete key should unlist a block */ + /* TODO should be able to add prior/next block */ + /* TODO should be able to save blocks to file */ - /* TODO draw everything */ + return 1; } static void -handle_keyboard_input(union libterminput_input *input) +handle_keyboard_input_to_block_selector(union libterminput_input *input) { - if (input->type == LIBTERMINPUT_KEYPRESS) { - if (IS_CTRL_KEY(input, 'Z')) { - restore_terminal(); - raise(SIGTSTP); - configure_terminal(); - redraw: - interrupt_deferred = 1; - term_resized = 1; - } else if (IS_CTRL_KEY(input, 'L')) { - goto redraw; - } else if (IS_CTRL_KEY(input, 'Q')) { - exiting = 1; + if (input->type != LIBTERMINPUT_KEYPRESS) + return; + + /* TODO optimise redraw to only affected areas */ + /* TODO defer redraw until all repeations have been processed */ + + if (input->keypress.key == LIBTERMINPUT_UP) { + if (selected_block > 0u) { + first_visible_line = 0u; + line_count = 0u; + selected_block -= 1u; + ensure_selected_block_visible(); + do_redraw(); + } + + } else if (input->keypress.key == LIBTERMINPUT_DOWN) { + if (selected_block < nblocks - 1u) { + first_visible_line = 0u; + line_count = 0u; + selected_block += 1u; + ensure_selected_block_visible(); + do_redraw(); + } + + } else if (input->keypress.key == LIBTERMINPUT_HOME) { + if (selected_block > 0u) { + first_visible_line = 0u; + line_count = 0u; + selected_block = 0u; + ensure_selected_block_visible(); + do_redraw(); + } + + } else if (input->keypress.key == LIBTERMINPUT_END) { + if (selected_block < nblocks - 1u) { + first_visible_line = 0u; + line_count = 0u; + selected_block = nblocks - 1u; + ensure_selected_block_visible(); + do_redraw(); + } + + } else if (input->keypress.key == LIBTERMINPUT_PRIOR) { + if (selected_block > 0u && term_height > 2u) { + first_visible_line = 0u; + line_count = 0u; + if (selected_block != first_visible_block) { + selected_block = first_visible_block; + do_redraw(); + } else { + if (selected_block > term_height - 2u) + selected_block -= term_height - 2u; + else + selected_block = 0u; + ensure_selected_block_visible(); + do_redraw(); + } + } + + } else if (input->keypress.key == LIBTERMINPUT_NEXT) { + if (selected_block < nblocks - 1u && term_height > 2u) { + size_t last = first_visible_block + (term_height - 3u); + first_visible_line = 0u; + line_count = 0u; + if (selected_block != last) { + selected_block = last; + do_redraw(); + } else { + if (term_height - 2u > nblocks - 1u - selected_block) + selected_block = nblocks - 1u; + else + selected_block += term_height - 2u; + ensure_selected_block_visible(); + do_redraw(); + } + } + } +} + + +static void +handle_keyboard_input_to_block_display(union libterminput_input *input) +{ + if (input->type != LIBTERMINPUT_KEYPRESS) + return; + + /* TODO optimise redraw to only affected areas */ + /* TODO defer redraw until all repeations have been processed */ + + if (input->keypress.key == LIBTERMINPUT_UP) { + if (first_visible_line > 0u) { + first_visible_line -= 1u; + do_redraw(); + } + + } else if (input->keypress.key == LIBTERMINPUT_DOWN) { + if (line_count > first_visible_line && term_height > 2u) { + if (term_height - 2u < line_count - first_visible_line) { + first_visible_line += 1; + do_redraw(); + } + } + + } else if (input->keypress.key == LIBTERMINPUT_HOME) { + if (first_visible_line != 0u) { + first_visible_line = 0u; + do_redraw(); + } + + } else if (input->keypress.key == LIBTERMINPUT_END) { + if (line_count && term_height > 2u && line_count > term_height - 1u) { + if (first_visible_line != line_count - (term_height - 2u)) { + first_visible_line = line_count - (term_height - 2u); + do_redraw(); + } + } + + } else if (input->keypress.key == LIBTERMINPUT_PRIOR) { + if (first_visible_line != 0u && term_height > 2u) { + if (first_visible_line < term_height - 2u) + first_visible_line = 0u; + else + first_visible_line -= term_height - 2u; + do_redraw(); + } + + } else if (input->keypress.key == LIBTERMINPUT_NEXT) { + if (term_height > 2u) { + size_t prev = first_visible_line; + first_visible_line += term_height - 2u; + if (first_visible_line + term_height - 2u > line_count) { + if (line_count > term_height - 2u) + first_visible_line = line_count - (term_height - 2u); + else + first_visible_line = 0u; + } + if (first_visible_line != prev) + do_redraw(); } } } +static void +handle_keyboard_input(union libterminput_input *input) +{ + if (handle_keyboard_input_global(input)) + return; + + if (active_focus == BLOCK_SELECTOR) + handle_keyboard_input_to_block_selector(input); + else if (active_focus == BLOCK_DISPLAY) + handle_keyboard_input_to_block_display(input); +} + + static void enter_subterminal(void) { - dprintf(ttyfd, - "\033[?1049h" /* enter subterminal */ - "\033[?25l" /* hide cursor */ - "\033[H\033[2J" /* clear terminal */ - ); + if (dprintf(ttyfd, + "\033[?1049h" /* enter subterminal */ + "\033[?25l" /* hide cursor */ + "\033[H\033[2J" /* clear terminal */ + ) < 0) + eprintf("dprintf /dev/tty:"); term_entered_submode_stdout = 1; } @@ -96,17 +638,17 @@ exit_subterminal(void) static void set_term_attributes(void) { - struct termios new_term_attributes_stdin; + struct termios new_term_attributes; - if (tcgetattr(ttyfd, &term_attributes_stdin)) + if (tcgetattr(ttyfd, &term_attributes)) eprintf("tcgetattr /dev/tty:"); - term_attributes_stdin_saved = 1; + term_attributes_saved = 1; - memcpy(&new_term_attributes_stdin, &term_attributes_stdin, sizeof(term_attributes_stdin)); - new_term_attributes_stdin.c_iflag &= (tcflag_t)~(IXON | IXANY | IXOFF); - new_term_attributes_stdin.c_lflag &= (tcflag_t)~(ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL); + memcpy(&new_term_attributes, &term_attributes, sizeof(term_attributes)); + new_term_attributes.c_iflag &= (tcflag_t)~(IXON | IXANY | IXOFF); + new_term_attributes.c_lflag &= (tcflag_t)~(ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL); - if (tcsetattr(ttyfd, TCSANOW, &new_term_attributes_stdin)) + if (tcsetattr(ttyfd, TCSANOW, &new_term_attributes)) weprintf("tcsetattr /dev/tty TCSANOW:"); } @@ -114,9 +656,9 @@ set_term_attributes(void) static void restore_term_attributes(void) { - if (term_attributes_stdin_saved) { - term_attributes_stdin_saved = 0; - if (tcsetattr(ttyfd, TCSAFLUSH, &term_attributes_stdin)) + if (term_attributes_saved) { + term_attributes_saved = 0; + if (tcsetattr(ttyfd, TCSAFLUSH, &term_attributes)) weprintf("tcsetattr /dev/tty TCSAFLUSH:"); } } @@ -142,6 +684,8 @@ static void cleanup(void) { restore_terminal(); + close(ttyfd); + ttyfd = -1; } @@ -264,8 +808,10 @@ read_block_list(int fd, const char *fname) if (r <= 0) { if (!r) break; - if (errno == EINTR) + if (errno == EINTR) { + interrupt_deferred = 1; continue; + } eprintf("read %s:", fname); } n = (size_t)r; @@ -276,6 +822,7 @@ read_block_list(int fd, const char *fname) continue; have_v = 0; add_to_block_list(v); + v = 0; } else if ('0' <= buf[i] && buf[i] <= '9') { have_v = 1; d = (off_t)(buf[i] - '0'); @@ -288,7 +835,7 @@ read_block_list(int fd, const char *fname) } } } - if (!have_v) + if (have_v) add_to_block_list(v); if (!nblocks) goto inval; @@ -307,8 +854,8 @@ main(int argc, char *argv[]) { struct libterminput_state input_ctx; union libterminput_input input; - const char *path, *listpath; - int fd, listfd; + const char *listpath, *env; + int listfd; ARGBEGIN { case 'b': @@ -332,9 +879,9 @@ main(int argc, char *argv[]) usage(); /* Open terminal and ensure process is running in the foreground */ - ttyfd = open("/dev/tty", O_RDONLY); + ttyfd = open("/dev/tty", O_RDWR); if (ttyfd < 0) - eprintf("open /dev/tty O_RDONLY:"); + eprintf("open /dev/tty O_RDWR:"); if (getpgrp() != tcgetpgrp(ttyfd)) eprintf("process not running in the foreground"); @@ -347,12 +894,12 @@ main(int argc, char *argv[]) } /* Open file to display */ - fd = open(path, O_RDONLY); - if (fd < 0) + textfd = open(path, O_RDONLY); + if (textfd < 0) eprintf("open %s O_RDONLY", path); /* Advise the kernel about the access pattern */ - posix_fadvise(fd, 0, 0, POSIX_FADV_RANDOM); + posix_fadvise(textfd, 0, 0, POSIX_FADV_RANDOM); /* Set up cleanup on exit */ atexit(&cleanup); @@ -361,15 +908,27 @@ main(int argc, char *argv[]) /* Read block list */ read_block_list(listfd, listpath); close(listfd); - max_block_len = snprintf("%ji", (uintmax_t)blocks[nblocks - 1u]); + max_block_len = snprintf(NULL, 0u, "%ji", (intmax_t)blocks[nblocks - 1u]); if (max_block_len < 1) - eprintf("snprintf %%ji:") + eprintf("snprintf %%ji:"); + + /* Configure UI */ + env = getenv("TERM"); + limited_block_drawing = (!env || !*env || !strcasecmp(env, "linux")); /* UI loop */ update_term_size(); subscribe_term_resize(); configure_terminal(); memset(&input_ctx, 0, sizeof(input_ctx)); + while (libterminput_init(&input_ctx, ttyfd)) { + if (errno == EINTR) { + interrupt_deferred = 1; + continue; + } + eprintf("libterminput_init /dev/tty:"); + } + do_redraw(); do { if (interrupt_deferred) { interrupt_deferred = 0; @@ -391,12 +950,14 @@ main(int argc, char *argv[]) if (errno == EINTR) interrupt_deferred = 1; else - eprintf("libterminput :"); + eprintf("libterminput_read /dev/tty:"); break; } } while (!exiting); + libterminput_destroy(&input_ctx); - close(fd); - close(ttyfd); + close(textfd); + free(current_block_text); + free(line_map); return 0; } -- cgit v1.3.1