blob: 15ad7837753527220de1c67cc187e2830639eb9b [file] [log] [blame]
Denys Vlasenko73af7052017-09-13 19:20:27 +02001/*
2 * Copyright (C) 2017 Denys Vlasenko <vda.linux@googlemail.com>
3 *
4 * Licensed under GPLv2, see file LICENSE in this source tree.
5 */
6//config:config HEXEDIT
Denys Vlasenkob097a842018-12-28 03:20:17 +01007//config: bool "hexedit (21 kb)"
Denys Vlasenko73af7052017-09-13 19:20:27 +02008//config: default y
9//config: help
10//config: Edit file in hexadecimal.
11
12//applet:IF_HEXEDIT(APPLET(hexedit, BB_DIR_USR_BIN, BB_SUID_DROP))
13
14//kbuild:lib-$(CONFIG_HEXEDIT) += hexedit.o
15
16#include "libbb.h"
17
Denys Vlasenko363fb5e2017-09-14 09:42:40 +020018#define ESC "\033"
19#define HOME ESC"[H"
Denys Vlasenkod54f58d2017-09-14 10:51:12 +020020#define CLEAR ESC"[J"
21#define CLEAR_TILL_EOL ESC"[K"
Denys Vlasenko363fb5e2017-09-14 09:42:40 +020022#define SET_ALT_SCR ESC"[?1049h"
23#define POP_ALT_SCR ESC"[?1049l"
24
25#undef CTRL
26#define CTRL(c) ((c) & (uint8_t)~0x60)
Denys Vlasenko73af7052017-09-13 19:20:27 +020027
28struct globals {
29 smallint half;
Denys Vlasenko363fb5e2017-09-14 09:42:40 +020030 smallint in_read_key;
Denys Vlasenko73af7052017-09-13 19:20:27 +020031 int fd;
32 unsigned height;
Denys Vlasenko363fb5e2017-09-14 09:42:40 +020033 unsigned row;
Denys Vlasenkoc7b858f2020-12-14 18:49:23 +010034 IF_VARIABLE_ARCH_PAGESIZE(unsigned pagesize;)
35#define G_pagesize cached_pagesize(G.pagesize)
Denys Vlasenkod54f58d2017-09-14 10:51:12 +020036 uint8_t *baseaddr;
Denys Vlasenko73af7052017-09-13 19:20:27 +020037 uint8_t *current_byte;
38 uint8_t *eof_byte;
39 off_t size;
40 off_t offset;
Denys Vlasenko363fb5e2017-09-14 09:42:40 +020041 /* needs to be zero-inited, thus keeping it in G: */
42 char read_key_buffer[KEYCODE_BUFFER_SIZE];
Denys Vlasenko73af7052017-09-13 19:20:27 +020043 struct termios orig_termios;
44};
45#define G (*ptr_to_globals)
46#define INIT_G() do { \
47 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
48} while (0)
49
Denys Vlasenkof3fa8652017-09-15 14:00:41 +020050/* hopefully there aren't arches with PAGE_SIZE > 64k */
51#define G_mapsize (64*1024)
Denys Vlasenko73af7052017-09-13 19:20:27 +020052
Denys Vlasenkod72e8042017-09-14 00:04:16 +020053/* "12ef5670 (xx )*16 _1_3_5_7_9abcdef\n"NUL */
54#define LINEBUF_SIZE (8 + 1 + 3*16 + 16 + 1 + 1 /*paranoia:*/ + 13)
Denys Vlasenko136946c2017-09-13 21:38:55 +020055
Denys Vlasenko363fb5e2017-09-14 09:42:40 +020056static void restore_term(void)
57{
58 tcsetattr_stdin_TCSANOW(&G.orig_termios);
59 printf(POP_ALT_SCR);
60 fflush_all();
61}
62
63static void sig_catcher(int sig)
64{
65 if (!G.in_read_key) {
66 /* now it's not safe to do I/O, just inform the main loop */
67 bb_got_signal = sig;
68 return;
69 }
70 restore_term();
71 kill_myself_with_sig(sig);
72}
73
Denys Vlasenko136946c2017-09-13 21:38:55 +020074static int format_line(char *hex, uint8_t *data, off_t offset)
Denys Vlasenko73af7052017-09-13 19:20:27 +020075{
Denys Vlasenko136946c2017-09-13 21:38:55 +020076 int ofs_pos;
77 char *text;
Denys Vlasenko73af7052017-09-13 19:20:27 +020078 uint8_t *end, *end1;
79
Denys Vlasenko136946c2017-09-13 21:38:55 +020080#if 1
81 /* Can be more than 4Gb, thus >8 chars, thus use a variable - don't assume 8! */
82 ofs_pos = sprintf(hex, "%08"OFF_FMT"x ", offset);
83#else
84 if (offset <= 0xffff)
85 ofs_pos = sprintf(hex, "%04"OFF_FMT"x ", offset);
86 else
87 ofs_pos = sprintf(hex, "%08"OFF_FMT"x ", offset);
88#endif
89 hex += ofs_pos;
90
Denys Vlasenkof3fa8652017-09-15 14:00:41 +020091 text = hex + 16 * 3;
Denys Vlasenko73af7052017-09-13 19:20:27 +020092 end1 = data + 15;
93 if ((G.size - offset) > 0) {
94 end = end1;
95 if ((G.size - offset) <= 15)
96 end = data + (G.size - offset) - 1;
97 while (data <= end) {
98 uint8_t c = *data++;
99 *hex++ = bb_hexdigits_upcase[c >> 4];
100 *hex++ = bb_hexdigits_upcase[c & 0xf];
101 *hex++ = ' ';
102 if (c < ' ' || c > 0x7e)
103 c = '.';
104 *text++ = c;
105 }
106 }
107 while (data <= end1) {
108 *hex++ = ' ';
109 *hex++ = ' ';
110 *hex++ = ' ';
111 *text++ = ' ';
112 data++;
113 }
114 *text = '\0';
Denys Vlasenko136946c2017-09-13 21:38:55 +0200115
116 return ofs_pos;
Denys Vlasenko73af7052017-09-13 19:20:27 +0200117}
118
Denys Vlasenkof3fa8652017-09-15 14:00:41 +0200119static void redraw(unsigned cursor)
Denys Vlasenko73af7052017-09-13 19:20:27 +0200120{
121 uint8_t *data;
122 off_t offset;
Denys Vlasenkod54f58d2017-09-14 10:51:12 +0200123 unsigned i, pos;
Denys Vlasenko73af7052017-09-13 19:20:27 +0200124
Denys Vlasenkod54f58d2017-09-14 10:51:12 +0200125 printf(HOME CLEAR);
Denys Vlasenkof3fa8652017-09-15 14:00:41 +0200126
127 /* if cursor is past end of screen, how many lines to move down? */
128 i = (cursor / 16) - G.height + 1;
129 if ((int)i < 0)
130 i = 0;
131
132 data = G.baseaddr + i * 16;
133 offset = G.offset + i * 16;
134 cursor -= i * 16;
Denys Vlasenkod54f58d2017-09-14 10:51:12 +0200135 pos = i = 0;
Denys Vlasenko73af7052017-09-13 19:20:27 +0200136 while (i < G.height) {
Denys Vlasenko136946c2017-09-13 21:38:55 +0200137 char buf[LINEBUF_SIZE];
Denys Vlasenkod54f58d2017-09-14 10:51:12 +0200138 pos = format_line(buf, data, offset);
Denys Vlasenko73af7052017-09-13 19:20:27 +0200139 printf(
Denys Vlasenkof3fa8652017-09-15 14:00:41 +0200140 "\r\n%s" + (!i) * 2, /* print \r\n only on 2nd line and later */
Denys Vlasenko136946c2017-09-13 21:38:55 +0200141 buf
Denys Vlasenko73af7052017-09-13 19:20:27 +0200142 );
143 data += 16;
144 offset += 16;
145 i++;
146 }
Denys Vlasenkof3fa8652017-09-15 14:00:41 +0200147
Denys Vlasenkoca2f8312018-07-23 14:21:07 +0200148 G.row = cursor / 16;
149 printf(ESC"[%u;%uH", 1 + G.row, 1 + pos + (cursor & 0xf) * 3);
Denys Vlasenko73af7052017-09-13 19:20:27 +0200150}
151
152static void redraw_cur_line(void)
153{
Denys Vlasenko136946c2017-09-13 21:38:55 +0200154 char buf[LINEBUF_SIZE];
Denys Vlasenko73af7052017-09-13 19:20:27 +0200155 uint8_t *data;
Denys Vlasenko136946c2017-09-13 21:38:55 +0200156 off_t offset;
157 int column;
Denys Vlasenko73af7052017-09-13 19:20:27 +0200158
Denys Vlasenko136946c2017-09-13 21:38:55 +0200159 column = (0xf & (uintptr_t)G.current_byte);
160 data = G.current_byte - column;
Denys Vlasenkod54f58d2017-09-14 10:51:12 +0200161 offset = G.offset + (data - G.baseaddr);
Denys Vlasenko136946c2017-09-13 21:38:55 +0200162
163 column = column*3 + G.half;
164 column += format_line(buf, data, offset);
165 printf("%s"
166 "\r"
167 "%.*s",
168 buf + column,
169 column, buf
Denys Vlasenko73af7052017-09-13 19:20:27 +0200170 );
171}
172
Denys Vlasenko8838c6d2017-09-14 11:01:37 +0200173/* if remappers return 0, no change was done */
174static int remap(unsigned cur_pos)
Denys Vlasenko73af7052017-09-13 19:20:27 +0200175{
Denys Vlasenkod54f58d2017-09-14 10:51:12 +0200176 if (G.baseaddr)
177 munmap(G.baseaddr, G_mapsize);
Denys Vlasenko73af7052017-09-13 19:20:27 +0200178
Denys Vlasenkod54f58d2017-09-14 10:51:12 +0200179 G.baseaddr = mmap(NULL,
Denys Vlasenko73af7052017-09-13 19:20:27 +0200180 G_mapsize,
181 PROT_READ | PROT_WRITE,
182 MAP_SHARED,
183 G.fd,
184 G.offset
185 );
Denys Vlasenkod54f58d2017-09-14 10:51:12 +0200186 if (G.baseaddr == MAP_FAILED) {
Denys Vlasenko363fb5e2017-09-14 09:42:40 +0200187 restore_term();
James Byrne69374872019-07-02 11:35:03 +0200188 bb_simple_perror_msg_and_die("mmap");
Denys Vlasenko363fb5e2017-09-14 09:42:40 +0200189 }
Denys Vlasenko73af7052017-09-13 19:20:27 +0200190
Denys Vlasenkod54f58d2017-09-14 10:51:12 +0200191 G.current_byte = G.baseaddr + cur_pos;
Denys Vlasenko73af7052017-09-13 19:20:27 +0200192
Denys Vlasenkod54f58d2017-09-14 10:51:12 +0200193 G.eof_byte = G.baseaddr + G_mapsize;
Denys Vlasenko73af7052017-09-13 19:20:27 +0200194 if ((G.size - G.offset) < G_mapsize) {
195 /* mapping covers tail of the file */
196 /* we do have a mapped byte which is past eof */
Denys Vlasenkod54f58d2017-09-14 10:51:12 +0200197 G.eof_byte = G.baseaddr + (G.size - G.offset);
Denys Vlasenko73af7052017-09-13 19:20:27 +0200198 }
Denys Vlasenko8838c6d2017-09-14 11:01:37 +0200199 return 1;
Denys Vlasenko73af7052017-09-13 19:20:27 +0200200}
Denys Vlasenko8838c6d2017-09-14 11:01:37 +0200201static int move_mapping_further(void)
Denys Vlasenko73af7052017-09-13 19:20:27 +0200202{
203 unsigned pos;
204 unsigned pagesize;
205
206 if ((G.size - G.offset) < G_mapsize)
Denys Vlasenko8838c6d2017-09-14 11:01:37 +0200207 return 0; /* can't move mapping even further, it's at the end already */
Denys Vlasenko73af7052017-09-13 19:20:27 +0200208
Denys Vlasenkof3fa8652017-09-15 14:00:41 +0200209 pagesize = G_pagesize; /* constant on most arches */
Denys Vlasenkod54f58d2017-09-14 10:51:12 +0200210 pos = G.current_byte - G.baseaddr;
Denys Vlasenko73af7052017-09-13 19:20:27 +0200211 if (pos >= pagesize) {
Denys Vlasenkod72e8042017-09-14 00:04:16 +0200212 /* move offset up until current position is in 1st page */
Denys Vlasenko73af7052017-09-13 19:20:27 +0200213 do {
214 G.offset += pagesize;
215 if (G.offset == 0) { /* whoops */
216 G.offset -= pagesize;
217 break;
218 }
219 pos -= pagesize;
220 } while (pos >= pagesize);
Denys Vlasenko8838c6d2017-09-14 11:01:37 +0200221 return remap(pos);
Denys Vlasenko73af7052017-09-13 19:20:27 +0200222 }
Denys Vlasenko8838c6d2017-09-14 11:01:37 +0200223 return 0;
Denys Vlasenko73af7052017-09-13 19:20:27 +0200224}
Denys Vlasenko8838c6d2017-09-14 11:01:37 +0200225static int move_mapping_lower(void)
Denys Vlasenko73af7052017-09-13 19:20:27 +0200226{
227 unsigned pos;
228 unsigned pagesize;
229
230 if (G.offset == 0)
Denys Vlasenko8838c6d2017-09-14 11:01:37 +0200231 return 0; /* we are at 0 already */
Denys Vlasenko73af7052017-09-13 19:20:27 +0200232
Denys Vlasenkof3fa8652017-09-15 14:00:41 +0200233 pagesize = G_pagesize; /* constant on most arches */
Denys Vlasenkod54f58d2017-09-14 10:51:12 +0200234 pos = G.current_byte - G.baseaddr;
Denys Vlasenko73af7052017-09-13 19:20:27 +0200235
Denys Vlasenkod72e8042017-09-14 00:04:16 +0200236 /* move offset down until current position is in last page */
Denys Vlasenko73af7052017-09-13 19:20:27 +0200237 pos += pagesize;
238 while (pos < G_mapsize) {
239 pos += pagesize;
240 G.offset -= pagesize;
241 if (G.offset == 0)
242 break;
243 }
244 pos -= pagesize;
245
Denys Vlasenko8838c6d2017-09-14 11:01:37 +0200246 return remap(pos);
Denys Vlasenko73af7052017-09-13 19:20:27 +0200247}
248
Denys Vlasenko73af7052017-09-13 19:20:27 +0200249//usage:#define hexedit_trivial_usage
250//usage: "FILE"
251//usage:#define hexedit_full_usage "\n\n"
252//usage: "Edit FILE in hexadecimal"
253int hexedit_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
254int hexedit_main(int argc UNUSED_PARAM, char **argv)
255{
Denys Vlasenko73af7052017-09-13 19:20:27 +0200256 INIT_G();
Denys Vlasenkoc7b858f2020-12-14 18:49:23 +0100257 INIT_PAGESIZE(G.pagesize);
Denys Vlasenko73af7052017-09-13 19:20:27 +0200258
Denys Vlasenko73af7052017-09-13 19:20:27 +0200259 get_terminal_width_height(-1, NULL, &G.height);
Denys Vlasenkod72e8042017-09-14 00:04:16 +0200260 if (1) {
261 /* reduce number of write() syscalls while PgUp/Down: fully buffered output */
262 unsigned sz = (G.height | 0xf) * LINEBUF_SIZE;
263 setvbuf(stdout, xmalloc(sz), _IOFBF, sz);
264 }
265
266 getopt32(argv, "^" "" "\0" "=1"/*one arg*/);
267 argv += optind;
Denys Vlasenko73af7052017-09-13 19:20:27 +0200268
269 G.fd = xopen(*argv, O_RDWR);
270 G.size = xlseek(G.fd, 0, SEEK_END);
271
272 /* TERMIOS_RAW_CRNL suppresses \n -> \r\n translation, helps with down-arrow */
Denys Vlasenkod54f58d2017-09-14 10:51:12 +0200273 printf(SET_ALT_SCR);
Denys Vlasenko73af7052017-09-13 19:20:27 +0200274 set_termios_to_raw(STDIN_FILENO, &G.orig_termios, TERMIOS_RAW_CRNL);
275 bb_signals(BB_FATAL_SIGS, sig_catcher);
276
277 remap(0);
Denys Vlasenkof3fa8652017-09-15 14:00:41 +0200278 redraw(0);
Denys Vlasenko73af7052017-09-13 19:20:27 +0200279
Denys Vlasenko62643012017-09-13 23:02:51 +0200280//TODO: //Home/End: start/end of line; '<'/'>': start/end of file
Denys Vlasenko73af7052017-09-13 19:20:27 +0200281 //Backspace: undo
Denys Vlasenko73af7052017-09-13 19:20:27 +0200282 //Ctrl-L: redraw
Denys Vlasenko73af7052017-09-13 19:20:27 +0200283 //Ctrl-Z: suspend
284 //'/', Ctrl-S: search
Denys Vlasenko73af7052017-09-13 19:20:27 +0200285//TODO: detect window resize
Denys Vlasenko73af7052017-09-13 19:20:27 +0200286
Denys Vlasenko73af7052017-09-13 19:20:27 +0200287 for (;;) {
Denys Vlasenko62643012017-09-13 23:02:51 +0200288 unsigned cnt;
Denys Vlasenko44cb1662017-09-14 11:20:36 +0200289 int32_t key = key; /* for compiler */
Denys Vlasenko73af7052017-09-13 19:20:27 +0200290 uint8_t byte;
291
292 fflush_all();
Denys Vlasenko363fb5e2017-09-14 09:42:40 +0200293 G.in_read_key = 1;
294 if (!bb_got_signal)
Denys Vlasenko12566e72022-01-17 03:02:40 +0100295 key = safe_read_key(STDIN_FILENO, G.read_key_buffer, -1);
Denys Vlasenko363fb5e2017-09-14 09:42:40 +0200296 G.in_read_key = 0;
297 if (bb_got_signal)
298 key = CTRL('X');
Denys Vlasenko73af7052017-09-13 19:20:27 +0200299
Denys Vlasenko62643012017-09-13 23:02:51 +0200300 cnt = 1;
Denys Vlasenko44cb1662017-09-14 11:20:36 +0200301 if ((unsigned)(key - 'A') <= 'Z' - 'A')
302 key |= 0x20; /* convert A-Z to a-z */
Denys Vlasenko73af7052017-09-13 19:20:27 +0200303 switch (key) {
Denys Vlasenko73af7052017-09-13 19:20:27 +0200304 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
Denys Vlasenko44cb1662017-09-14 11:20:36 +0200305 /* convert to '0'+10...15 */
306 key = key - ('a' - '0' - 10);
Denys Vlasenko73af7052017-09-13 19:20:27 +0200307 /* fall through */
308 case '0': case '1': case '2': case '3': case '4':
309 case '5': case '6': case '7': case '8': case '9':
310 if (G.current_byte == G.eof_byte) {
Denys Vlasenko8838c6d2017-09-14 11:01:37 +0200311 if (!move_mapping_further()) {
312 /* already at EOF; extend the file */
Denys Vlasenko73af7052017-09-13 19:20:27 +0200313 if (++G.size <= 0 /* overflow? */
314 || ftruncate(G.fd, G.size) != 0 /* error extending? (e.g. block dev) */
315 ) {
316 G.size--;
317 break;
318 }
319 G.eof_byte++;
320 }
321 }
322 key -= '0';
323 byte = *G.current_byte & 0xf0;
324 if (!G.half) {
325 byte = *G.current_byte & 0x0f;
326 key <<= 4;
327 }
328 *G.current_byte = byte + key;
329 /* can't just print one updated hex char: need to update right-hand ASCII too */
330 redraw_cur_line();
331 /* fall through */
332 case KEYCODE_RIGHT:
333 if (G.current_byte == G.eof_byte)
334 break; /* eof - don't allow going past it */
335 byte = *G.current_byte;
336 if (!G.half) {
337 G.half = 1;
338 putchar(bb_hexdigits_upcase[byte >> 4]);
339 } else {
340 G.half = 0;
341 G.current_byte++;
342 if ((0xf & (uintptr_t)G.current_byte) == 0) {
343 /* rightmost pos, wrap to next line */
344 if (G.current_byte == G.eof_byte)
345 move_mapping_further();
346 printf(ESC"[46D"); /* cursor left 3*15 + 1 chars */
347 goto down;
348 }
349 putchar(bb_hexdigits_upcase[byte & 0xf]);
350 putchar(' ');
351 }
352 break;
Denys Vlasenko62643012017-09-13 23:02:51 +0200353 case KEYCODE_PAGEDOWN:
354 cnt = G.height;
Denys Vlasenko73af7052017-09-13 19:20:27 +0200355 case KEYCODE_DOWN:
Denys Vlasenko62643012017-09-13 23:02:51 +0200356 k_down:
Denys Vlasenko73af7052017-09-13 19:20:27 +0200357 G.current_byte += 16;
358 if (G.current_byte >= G.eof_byte) {
359 move_mapping_further();
360 if (G.current_byte > G.eof_byte) {
Denys Vlasenko8838c6d2017-09-14 11:01:37 +0200361 /* _after_ eof - don't allow this */
Denys Vlasenko73af7052017-09-13 19:20:27 +0200362 G.current_byte -= 16;
Denys Vlasenkoca2f8312018-07-23 14:21:07 +0200363 if (G.current_byte < G.baseaddr)
364 move_mapping_lower();
Denys Vlasenko73af7052017-09-13 19:20:27 +0200365 break;
366 }
367 }
368 down:
369 putchar('\n'); /* down one line, possibly scroll screen */
Denys Vlasenko363fb5e2017-09-14 09:42:40 +0200370 G.row++;
371 if (G.row >= G.height) {
372 G.row--;
Denys Vlasenko73af7052017-09-13 19:20:27 +0200373 redraw_cur_line();
374 }
Denys Vlasenko62643012017-09-13 23:02:51 +0200375 if (--cnt)
376 goto k_down;
Denys Vlasenko73af7052017-09-13 19:20:27 +0200377 break;
378
379 case KEYCODE_LEFT:
380 if (G.half) {
381 G.half = 0;
382 printf(ESC"[D");
383 break;
384 }
385 if ((0xf & (uintptr_t)G.current_byte) == 0) {
386 /* leftmost pos, wrap to prev line */
Denys Vlasenkod54f58d2017-09-14 10:51:12 +0200387 if (G.current_byte == G.baseaddr) {
Denys Vlasenko8838c6d2017-09-14 11:01:37 +0200388 if (!move_mapping_lower())
Denys Vlasenko136946c2017-09-13 21:38:55 +0200389 break; /* first line, don't do anything */
390 }
Denys Vlasenko73af7052017-09-13 19:20:27 +0200391 G.half = 1;
392 G.current_byte--;
393 printf(ESC"[46C"); /* cursor right 3*15 + 1 chars */
394 goto up;
395 }
396 G.half = 1;
397 G.current_byte--;
398 printf(ESC"[2D");
399 break;
Denys Vlasenko62643012017-09-13 23:02:51 +0200400 case KEYCODE_PAGEUP:
401 cnt = G.height;
Denys Vlasenko73af7052017-09-13 19:20:27 +0200402 case KEYCODE_UP:
Denys Vlasenko62643012017-09-13 23:02:51 +0200403 k_up:
Denys Vlasenkod54f58d2017-09-14 10:51:12 +0200404 if ((G.current_byte - G.baseaddr) < 16) {
Denys Vlasenko8838c6d2017-09-14 11:01:37 +0200405 if (!move_mapping_lower())
406 break; /* already at 0, stop */
Denys Vlasenko73af7052017-09-13 19:20:27 +0200407 }
408 G.current_byte -= 16;
409 up:
Denys Vlasenko363fb5e2017-09-14 09:42:40 +0200410 if (G.row != 0) {
411 G.row--;
Denys Vlasenko73af7052017-09-13 19:20:27 +0200412 printf(ESC"[A"); /* up (won't scroll) */
413 } else {
414 //printf(ESC"[T"); /* scroll up */ - not implemented on Linux VT!
415 printf(ESC"M"); /* scroll up */
416 redraw_cur_line();
417 }
Denys Vlasenko62643012017-09-13 23:02:51 +0200418 if (--cnt)
419 goto k_up;
Denys Vlasenko73af7052017-09-13 19:20:27 +0200420 break;
Denys Vlasenkod54f58d2017-09-14 10:51:12 +0200421
422 case '\n':
423 case '\r':
424 /* [Enter]: goto specified position */
425 {
426 char buf[sizeof(G.offset)*3 + 4];
427 printf(ESC"[999;1H" CLEAR_TILL_EOL); /* go to last line */
Denys Vlasenkof3fa8652017-09-15 14:00:41 +0200428 if (read_line_input(NULL, "Go to (dec,0Xhex,0oct): ", buf, sizeof(buf)) > 0) {
Denys Vlasenkod54f58d2017-09-14 10:51:12 +0200429 off_t t;
Denys Vlasenkoe58b4472017-09-15 15:23:47 +0200430 unsigned cursor;
Denys Vlasenkod54f58d2017-09-14 10:51:12 +0200431
432 t = bb_strtoull(buf, NULL, 0);
433 if (t >= G.size)
434 t = G.size - 1;
Denys Vlasenkoe58b4472017-09-15 15:23:47 +0200435 cursor = t & (G_pagesize - 1);
436 t -= cursor;
Denys Vlasenkod54f58d2017-09-14 10:51:12 +0200437 if (t < 0)
Denys Vlasenkoe58b4472017-09-15 15:23:47 +0200438 cursor = t = 0;
439 if (t != 0 && cursor < 0x1ff) {
Denys Vlasenkof3fa8652017-09-15 14:00:41 +0200440 /* very close to end of page, possibly to EOF */
441 /* move one page lower */
442 t -= G_pagesize;
Denys Vlasenkoe58b4472017-09-15 15:23:47 +0200443 cursor += G_pagesize;
Denys Vlasenkof3fa8652017-09-15 14:00:41 +0200444 }
Denys Vlasenkod54f58d2017-09-14 10:51:12 +0200445 G.offset = t;
Denys Vlasenkoe58b4472017-09-15 15:23:47 +0200446 remap(cursor);
447 redraw(cursor);
Denys Vlasenkod54f58d2017-09-14 10:51:12 +0200448 break;
449 }
Denys Vlasenkoe58b4472017-09-15 15:23:47 +0200450 /* ^C/EOF/error: fall through to exiting */
Denys Vlasenkod54f58d2017-09-14 10:51:12 +0200451 }
Denys Vlasenko363fb5e2017-09-14 09:42:40 +0200452 case CTRL('X'):
453 restore_term();
454 return EXIT_SUCCESS;
455 } /* switch */
456 } /* for (;;) */
Denys Vlasenko73af7052017-09-13 19:20:27 +0200457
Denys Vlasenko363fb5e2017-09-14 09:42:40 +0200458 /* not reached */
Denys Vlasenko73af7052017-09-13 19:20:27 +0200459 return EXIT_SUCCESS;
460}