blob: 95c930d123485e214063a0366101f3b1ec29c995 [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 Vlasenko38da4c42018-01-14 12:03:33 +01007//config: bool "hexedit (20 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 Vlasenkof3fa8652017-09-15 14:00:41 +020034 unsigned pagesize;
Denys Vlasenkod54f58d2017-09-14 10:51:12 +020035 uint8_t *baseaddr;
Denys Vlasenko73af7052017-09-13 19:20:27 +020036 uint8_t *current_byte;
37 uint8_t *eof_byte;
38 off_t size;
39 off_t offset;
Denys Vlasenko363fb5e2017-09-14 09:42:40 +020040 /* needs to be zero-inited, thus keeping it in G: */
41 char read_key_buffer[KEYCODE_BUFFER_SIZE];
Denys Vlasenko73af7052017-09-13 19:20:27 +020042 struct termios orig_termios;
43};
44#define G (*ptr_to_globals)
45#define INIT_G() do { \
46 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
47} while (0)
48
Denys Vlasenkof3fa8652017-09-15 14:00:41 +020049//TODO: move to libbb
50#if defined(__x86_64__) || defined(i386)
51# define G_pagesize 4096
52# define INIT_PAGESIZE() ((void)0)
53#else
54# define G_pagesize (G.pagesize)
55# define INIT_PAGESIZE() ((void)(G.pagesize = getpagesize()))
56#endif
57
58/* hopefully there aren't arches with PAGE_SIZE > 64k */
59#define G_mapsize (64*1024)
Denys Vlasenko73af7052017-09-13 19:20:27 +020060
Denys Vlasenkod72e8042017-09-14 00:04:16 +020061/* "12ef5670 (xx )*16 _1_3_5_7_9abcdef\n"NUL */
62#define LINEBUF_SIZE (8 + 1 + 3*16 + 16 + 1 + 1 /*paranoia:*/ + 13)
Denys Vlasenko136946c2017-09-13 21:38:55 +020063
Denys Vlasenko363fb5e2017-09-14 09:42:40 +020064static void restore_term(void)
65{
66 tcsetattr_stdin_TCSANOW(&G.orig_termios);
67 printf(POP_ALT_SCR);
68 fflush_all();
69}
70
71static void sig_catcher(int sig)
72{
73 if (!G.in_read_key) {
74 /* now it's not safe to do I/O, just inform the main loop */
75 bb_got_signal = sig;
76 return;
77 }
78 restore_term();
79 kill_myself_with_sig(sig);
80}
81
Denys Vlasenko136946c2017-09-13 21:38:55 +020082static int format_line(char *hex, uint8_t *data, off_t offset)
Denys Vlasenko73af7052017-09-13 19:20:27 +020083{
Denys Vlasenko136946c2017-09-13 21:38:55 +020084 int ofs_pos;
85 char *text;
Denys Vlasenko73af7052017-09-13 19:20:27 +020086 uint8_t *end, *end1;
87
Denys Vlasenko136946c2017-09-13 21:38:55 +020088#if 1
89 /* Can be more than 4Gb, thus >8 chars, thus use a variable - don't assume 8! */
90 ofs_pos = sprintf(hex, "%08"OFF_FMT"x ", offset);
91#else
92 if (offset <= 0xffff)
93 ofs_pos = sprintf(hex, "%04"OFF_FMT"x ", offset);
94 else
95 ofs_pos = sprintf(hex, "%08"OFF_FMT"x ", offset);
96#endif
97 hex += ofs_pos;
98
Denys Vlasenkof3fa8652017-09-15 14:00:41 +020099 text = hex + 16 * 3;
Denys Vlasenko73af7052017-09-13 19:20:27 +0200100 end1 = data + 15;
101 if ((G.size - offset) > 0) {
102 end = end1;
103 if ((G.size - offset) <= 15)
104 end = data + (G.size - offset) - 1;
105 while (data <= end) {
106 uint8_t c = *data++;
107 *hex++ = bb_hexdigits_upcase[c >> 4];
108 *hex++ = bb_hexdigits_upcase[c & 0xf];
109 *hex++ = ' ';
110 if (c < ' ' || c > 0x7e)
111 c = '.';
112 *text++ = c;
113 }
114 }
115 while (data <= end1) {
116 *hex++ = ' ';
117 *hex++ = ' ';
118 *hex++ = ' ';
119 *text++ = ' ';
120 data++;
121 }
122 *text = '\0';
Denys Vlasenko136946c2017-09-13 21:38:55 +0200123
124 return ofs_pos;
Denys Vlasenko73af7052017-09-13 19:20:27 +0200125}
126
Denys Vlasenkof3fa8652017-09-15 14:00:41 +0200127static void redraw(unsigned cursor)
Denys Vlasenko73af7052017-09-13 19:20:27 +0200128{
129 uint8_t *data;
130 off_t offset;
Denys Vlasenkod54f58d2017-09-14 10:51:12 +0200131 unsigned i, pos;
Denys Vlasenko73af7052017-09-13 19:20:27 +0200132
Denys Vlasenkod54f58d2017-09-14 10:51:12 +0200133 printf(HOME CLEAR);
Denys Vlasenkof3fa8652017-09-15 14:00:41 +0200134
135 /* if cursor is past end of screen, how many lines to move down? */
136 i = (cursor / 16) - G.height + 1;
137 if ((int)i < 0)
138 i = 0;
139
140 data = G.baseaddr + i * 16;
141 offset = G.offset + i * 16;
142 cursor -= i * 16;
Denys Vlasenkod54f58d2017-09-14 10:51:12 +0200143 pos = i = 0;
Denys Vlasenko73af7052017-09-13 19:20:27 +0200144 while (i < G.height) {
Denys Vlasenko136946c2017-09-13 21:38:55 +0200145 char buf[LINEBUF_SIZE];
Denys Vlasenkod54f58d2017-09-14 10:51:12 +0200146 pos = format_line(buf, data, offset);
Denys Vlasenko73af7052017-09-13 19:20:27 +0200147 printf(
Denys Vlasenkof3fa8652017-09-15 14:00:41 +0200148 "\r\n%s" + (!i) * 2, /* print \r\n only on 2nd line and later */
Denys Vlasenko136946c2017-09-13 21:38:55 +0200149 buf
Denys Vlasenko73af7052017-09-13 19:20:27 +0200150 );
151 data += 16;
152 offset += 16;
153 i++;
154 }
Denys Vlasenkof3fa8652017-09-15 14:00:41 +0200155
156 printf(ESC"[%u;%uH", 1 + cursor / 16, 1 + pos + (cursor & 0xf) * 3);
Denys Vlasenko73af7052017-09-13 19:20:27 +0200157}
158
159static void redraw_cur_line(void)
160{
Denys Vlasenko136946c2017-09-13 21:38:55 +0200161 char buf[LINEBUF_SIZE];
Denys Vlasenko73af7052017-09-13 19:20:27 +0200162 uint8_t *data;
Denys Vlasenko136946c2017-09-13 21:38:55 +0200163 off_t offset;
164 int column;
Denys Vlasenko73af7052017-09-13 19:20:27 +0200165
Denys Vlasenko136946c2017-09-13 21:38:55 +0200166 column = (0xf & (uintptr_t)G.current_byte);
167 data = G.current_byte - column;
Denys Vlasenkod54f58d2017-09-14 10:51:12 +0200168 offset = G.offset + (data - G.baseaddr);
Denys Vlasenko136946c2017-09-13 21:38:55 +0200169
170 column = column*3 + G.half;
171 column += format_line(buf, data, offset);
172 printf("%s"
173 "\r"
174 "%.*s",
175 buf + column,
176 column, buf
Denys Vlasenko73af7052017-09-13 19:20:27 +0200177 );
178}
179
Denys Vlasenko8838c6d2017-09-14 11:01:37 +0200180/* if remappers return 0, no change was done */
181static int remap(unsigned cur_pos)
Denys Vlasenko73af7052017-09-13 19:20:27 +0200182{
Denys Vlasenkod54f58d2017-09-14 10:51:12 +0200183 if (G.baseaddr)
184 munmap(G.baseaddr, G_mapsize);
Denys Vlasenko73af7052017-09-13 19:20:27 +0200185
Denys Vlasenkod54f58d2017-09-14 10:51:12 +0200186 G.baseaddr = mmap(NULL,
Denys Vlasenko73af7052017-09-13 19:20:27 +0200187 G_mapsize,
188 PROT_READ | PROT_WRITE,
189 MAP_SHARED,
190 G.fd,
191 G.offset
192 );
Denys Vlasenkod54f58d2017-09-14 10:51:12 +0200193 if (G.baseaddr == MAP_FAILED) {
Denys Vlasenko363fb5e2017-09-14 09:42:40 +0200194 restore_term();
Denys Vlasenko73af7052017-09-13 19:20:27 +0200195 bb_perror_msg_and_die("mmap");
Denys Vlasenko363fb5e2017-09-14 09:42:40 +0200196 }
Denys Vlasenko73af7052017-09-13 19:20:27 +0200197
Denys Vlasenkod54f58d2017-09-14 10:51:12 +0200198 G.current_byte = G.baseaddr + cur_pos;
Denys Vlasenko73af7052017-09-13 19:20:27 +0200199
Denys Vlasenkod54f58d2017-09-14 10:51:12 +0200200 G.eof_byte = G.baseaddr + G_mapsize;
Denys Vlasenko73af7052017-09-13 19:20:27 +0200201 if ((G.size - G.offset) < G_mapsize) {
202 /* mapping covers tail of the file */
203 /* we do have a mapped byte which is past eof */
Denys Vlasenkod54f58d2017-09-14 10:51:12 +0200204 G.eof_byte = G.baseaddr + (G.size - G.offset);
Denys Vlasenko73af7052017-09-13 19:20:27 +0200205 }
Denys Vlasenko8838c6d2017-09-14 11:01:37 +0200206 return 1;
Denys Vlasenko73af7052017-09-13 19:20:27 +0200207}
Denys Vlasenko8838c6d2017-09-14 11:01:37 +0200208static int move_mapping_further(void)
Denys Vlasenko73af7052017-09-13 19:20:27 +0200209{
210 unsigned pos;
211 unsigned pagesize;
212
213 if ((G.size - G.offset) < G_mapsize)
Denys Vlasenko8838c6d2017-09-14 11:01:37 +0200214 return 0; /* can't move mapping even further, it's at the end already */
Denys Vlasenko73af7052017-09-13 19:20:27 +0200215
Denys Vlasenkof3fa8652017-09-15 14:00:41 +0200216 pagesize = G_pagesize; /* constant on most arches */
Denys Vlasenkod54f58d2017-09-14 10:51:12 +0200217 pos = G.current_byte - G.baseaddr;
Denys Vlasenko73af7052017-09-13 19:20:27 +0200218 if (pos >= pagesize) {
Denys Vlasenkod72e8042017-09-14 00:04:16 +0200219 /* move offset up until current position is in 1st page */
Denys Vlasenko73af7052017-09-13 19:20:27 +0200220 do {
221 G.offset += pagesize;
222 if (G.offset == 0) { /* whoops */
223 G.offset -= pagesize;
224 break;
225 }
226 pos -= pagesize;
227 } while (pos >= pagesize);
Denys Vlasenko8838c6d2017-09-14 11:01:37 +0200228 return remap(pos);
Denys Vlasenko73af7052017-09-13 19:20:27 +0200229 }
Denys Vlasenko8838c6d2017-09-14 11:01:37 +0200230 return 0;
Denys Vlasenko73af7052017-09-13 19:20:27 +0200231}
Denys Vlasenko8838c6d2017-09-14 11:01:37 +0200232static int move_mapping_lower(void)
Denys Vlasenko73af7052017-09-13 19:20:27 +0200233{
234 unsigned pos;
235 unsigned pagesize;
236
237 if (G.offset == 0)
Denys Vlasenko8838c6d2017-09-14 11:01:37 +0200238 return 0; /* we are at 0 already */
Denys Vlasenko73af7052017-09-13 19:20:27 +0200239
Denys Vlasenkof3fa8652017-09-15 14:00:41 +0200240 pagesize = G_pagesize; /* constant on most arches */
Denys Vlasenkod54f58d2017-09-14 10:51:12 +0200241 pos = G.current_byte - G.baseaddr;
Denys Vlasenko73af7052017-09-13 19:20:27 +0200242
Denys Vlasenkod72e8042017-09-14 00:04:16 +0200243 /* move offset down until current position is in last page */
Denys Vlasenko73af7052017-09-13 19:20:27 +0200244 pos += pagesize;
245 while (pos < G_mapsize) {
246 pos += pagesize;
247 G.offset -= pagesize;
248 if (G.offset == 0)
249 break;
250 }
251 pos -= pagesize;
252
Denys Vlasenko8838c6d2017-09-14 11:01:37 +0200253 return remap(pos);
Denys Vlasenko73af7052017-09-13 19:20:27 +0200254}
255
Denys Vlasenko73af7052017-09-13 19:20:27 +0200256//usage:#define hexedit_trivial_usage
257//usage: "FILE"
258//usage:#define hexedit_full_usage "\n\n"
259//usage: "Edit FILE in hexadecimal"
260int hexedit_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
261int hexedit_main(int argc UNUSED_PARAM, char **argv)
262{
Denys Vlasenko73af7052017-09-13 19:20:27 +0200263 INIT_G();
Denys Vlasenkof3fa8652017-09-15 14:00:41 +0200264 INIT_PAGESIZE();
Denys Vlasenko73af7052017-09-13 19:20:27 +0200265
Denys Vlasenko73af7052017-09-13 19:20:27 +0200266 get_terminal_width_height(-1, NULL, &G.height);
Denys Vlasenkod72e8042017-09-14 00:04:16 +0200267 if (1) {
268 /* reduce number of write() syscalls while PgUp/Down: fully buffered output */
269 unsigned sz = (G.height | 0xf) * LINEBUF_SIZE;
270 setvbuf(stdout, xmalloc(sz), _IOFBF, sz);
271 }
272
273 getopt32(argv, "^" "" "\0" "=1"/*one arg*/);
274 argv += optind;
Denys Vlasenko73af7052017-09-13 19:20:27 +0200275
276 G.fd = xopen(*argv, O_RDWR);
277 G.size = xlseek(G.fd, 0, SEEK_END);
278
279 /* TERMIOS_RAW_CRNL suppresses \n -> \r\n translation, helps with down-arrow */
Denys Vlasenkod54f58d2017-09-14 10:51:12 +0200280 printf(SET_ALT_SCR);
Denys Vlasenko73af7052017-09-13 19:20:27 +0200281 set_termios_to_raw(STDIN_FILENO, &G.orig_termios, TERMIOS_RAW_CRNL);
282 bb_signals(BB_FATAL_SIGS, sig_catcher);
283
284 remap(0);
Denys Vlasenkof3fa8652017-09-15 14:00:41 +0200285 redraw(0);
Denys Vlasenko73af7052017-09-13 19:20:27 +0200286
Denys Vlasenko62643012017-09-13 23:02:51 +0200287//TODO: //Home/End: start/end of line; '<'/'>': start/end of file
Denys Vlasenko73af7052017-09-13 19:20:27 +0200288 //Backspace: undo
Denys Vlasenko73af7052017-09-13 19:20:27 +0200289 //Ctrl-L: redraw
Denys Vlasenko73af7052017-09-13 19:20:27 +0200290 //Ctrl-Z: suspend
291 //'/', Ctrl-S: search
Denys Vlasenko73af7052017-09-13 19:20:27 +0200292//TODO: detect window resize
Denys Vlasenko73af7052017-09-13 19:20:27 +0200293
Denys Vlasenko73af7052017-09-13 19:20:27 +0200294 for (;;) {
Denys Vlasenko62643012017-09-13 23:02:51 +0200295 unsigned cnt;
Denys Vlasenko44cb1662017-09-14 11:20:36 +0200296 int32_t key = key; /* for compiler */
Denys Vlasenko73af7052017-09-13 19:20:27 +0200297 uint8_t byte;
298
299 fflush_all();
Denys Vlasenko363fb5e2017-09-14 09:42:40 +0200300 G.in_read_key = 1;
301 if (!bb_got_signal)
302 key = read_key(STDIN_FILENO, G.read_key_buffer, -1);
303 G.in_read_key = 0;
304 if (bb_got_signal)
305 key = CTRL('X');
Denys Vlasenko73af7052017-09-13 19:20:27 +0200306
Denys Vlasenko62643012017-09-13 23:02:51 +0200307 cnt = 1;
Denys Vlasenko44cb1662017-09-14 11:20:36 +0200308 if ((unsigned)(key - 'A') <= 'Z' - 'A')
309 key |= 0x20; /* convert A-Z to a-z */
Denys Vlasenko73af7052017-09-13 19:20:27 +0200310 switch (key) {
Denys Vlasenko73af7052017-09-13 19:20:27 +0200311 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
Denys Vlasenko44cb1662017-09-14 11:20:36 +0200312 /* convert to '0'+10...15 */
313 key = key - ('a' - '0' - 10);
Denys Vlasenko73af7052017-09-13 19:20:27 +0200314 /* fall through */
315 case '0': case '1': case '2': case '3': case '4':
316 case '5': case '6': case '7': case '8': case '9':
317 if (G.current_byte == G.eof_byte) {
Denys Vlasenko8838c6d2017-09-14 11:01:37 +0200318 if (!move_mapping_further()) {
319 /* already at EOF; extend the file */
Denys Vlasenko73af7052017-09-13 19:20:27 +0200320 if (++G.size <= 0 /* overflow? */
321 || ftruncate(G.fd, G.size) != 0 /* error extending? (e.g. block dev) */
322 ) {
323 G.size--;
324 break;
325 }
326 G.eof_byte++;
327 }
328 }
329 key -= '0';
330 byte = *G.current_byte & 0xf0;
331 if (!G.half) {
332 byte = *G.current_byte & 0x0f;
333 key <<= 4;
334 }
335 *G.current_byte = byte + key;
336 /* can't just print one updated hex char: need to update right-hand ASCII too */
337 redraw_cur_line();
338 /* fall through */
339 case KEYCODE_RIGHT:
340 if (G.current_byte == G.eof_byte)
341 break; /* eof - don't allow going past it */
342 byte = *G.current_byte;
343 if (!G.half) {
344 G.half = 1;
345 putchar(bb_hexdigits_upcase[byte >> 4]);
346 } else {
347 G.half = 0;
348 G.current_byte++;
349 if ((0xf & (uintptr_t)G.current_byte) == 0) {
350 /* rightmost pos, wrap to next line */
351 if (G.current_byte == G.eof_byte)
352 move_mapping_further();
353 printf(ESC"[46D"); /* cursor left 3*15 + 1 chars */
354 goto down;
355 }
356 putchar(bb_hexdigits_upcase[byte & 0xf]);
357 putchar(' ');
358 }
359 break;
Denys Vlasenko62643012017-09-13 23:02:51 +0200360 case KEYCODE_PAGEDOWN:
361 cnt = G.height;
Denys Vlasenko73af7052017-09-13 19:20:27 +0200362 case KEYCODE_DOWN:
Denys Vlasenko62643012017-09-13 23:02:51 +0200363 k_down:
Denys Vlasenko73af7052017-09-13 19:20:27 +0200364 G.current_byte += 16;
365 if (G.current_byte >= G.eof_byte) {
366 move_mapping_further();
367 if (G.current_byte > G.eof_byte) {
Denys Vlasenko8838c6d2017-09-14 11:01:37 +0200368 /* _after_ eof - don't allow this */
Denys Vlasenko73af7052017-09-13 19:20:27 +0200369 G.current_byte -= 16;
370 break;
371 }
372 }
373 down:
374 putchar('\n'); /* down one line, possibly scroll screen */
Denys Vlasenko363fb5e2017-09-14 09:42:40 +0200375 G.row++;
376 if (G.row >= G.height) {
377 G.row--;
Denys Vlasenko73af7052017-09-13 19:20:27 +0200378 redraw_cur_line();
379 }
Denys Vlasenko62643012017-09-13 23:02:51 +0200380 if (--cnt)
381 goto k_down;
Denys Vlasenko73af7052017-09-13 19:20:27 +0200382 break;
383
384 case KEYCODE_LEFT:
385 if (G.half) {
386 G.half = 0;
387 printf(ESC"[D");
388 break;
389 }
390 if ((0xf & (uintptr_t)G.current_byte) == 0) {
391 /* leftmost pos, wrap to prev line */
Denys Vlasenkod54f58d2017-09-14 10:51:12 +0200392 if (G.current_byte == G.baseaddr) {
Denys Vlasenko8838c6d2017-09-14 11:01:37 +0200393 if (!move_mapping_lower())
Denys Vlasenko136946c2017-09-13 21:38:55 +0200394 break; /* first line, don't do anything */
395 }
Denys Vlasenko73af7052017-09-13 19:20:27 +0200396 G.half = 1;
397 G.current_byte--;
398 printf(ESC"[46C"); /* cursor right 3*15 + 1 chars */
399 goto up;
400 }
401 G.half = 1;
402 G.current_byte--;
403 printf(ESC"[2D");
404 break;
Denys Vlasenko62643012017-09-13 23:02:51 +0200405 case KEYCODE_PAGEUP:
406 cnt = G.height;
Denys Vlasenko73af7052017-09-13 19:20:27 +0200407 case KEYCODE_UP:
Denys Vlasenko62643012017-09-13 23:02:51 +0200408 k_up:
Denys Vlasenkod54f58d2017-09-14 10:51:12 +0200409 if ((G.current_byte - G.baseaddr) < 16) {
Denys Vlasenko8838c6d2017-09-14 11:01:37 +0200410 if (!move_mapping_lower())
411 break; /* already at 0, stop */
Denys Vlasenko73af7052017-09-13 19:20:27 +0200412 }
413 G.current_byte -= 16;
414 up:
Denys Vlasenko363fb5e2017-09-14 09:42:40 +0200415 if (G.row != 0) {
416 G.row--;
Denys Vlasenko73af7052017-09-13 19:20:27 +0200417 printf(ESC"[A"); /* up (won't scroll) */
418 } else {
419 //printf(ESC"[T"); /* scroll up */ - not implemented on Linux VT!
420 printf(ESC"M"); /* scroll up */
421 redraw_cur_line();
422 }
Denys Vlasenko62643012017-09-13 23:02:51 +0200423 if (--cnt)
424 goto k_up;
Denys Vlasenko73af7052017-09-13 19:20:27 +0200425 break;
Denys Vlasenkod54f58d2017-09-14 10:51:12 +0200426
427 case '\n':
428 case '\r':
429 /* [Enter]: goto specified position */
430 {
431 char buf[sizeof(G.offset)*3 + 4];
432 printf(ESC"[999;1H" CLEAR_TILL_EOL); /* go to last line */
Denys Vlasenkof3fa8652017-09-15 14:00:41 +0200433 if (read_line_input(NULL, "Go to (dec,0Xhex,0oct): ", buf, sizeof(buf)) > 0) {
Denys Vlasenkod54f58d2017-09-14 10:51:12 +0200434 off_t t;
Denys Vlasenkoe58b4472017-09-15 15:23:47 +0200435 unsigned cursor;
Denys Vlasenkod54f58d2017-09-14 10:51:12 +0200436
437 t = bb_strtoull(buf, NULL, 0);
438 if (t >= G.size)
439 t = G.size - 1;
Denys Vlasenkoe58b4472017-09-15 15:23:47 +0200440 cursor = t & (G_pagesize - 1);
441 t -= cursor;
Denys Vlasenkod54f58d2017-09-14 10:51:12 +0200442 if (t < 0)
Denys Vlasenkoe58b4472017-09-15 15:23:47 +0200443 cursor = t = 0;
444 if (t != 0 && cursor < 0x1ff) {
Denys Vlasenkof3fa8652017-09-15 14:00:41 +0200445 /* very close to end of page, possibly to EOF */
446 /* move one page lower */
447 t -= G_pagesize;
Denys Vlasenkoe58b4472017-09-15 15:23:47 +0200448 cursor += G_pagesize;
Denys Vlasenkof3fa8652017-09-15 14:00:41 +0200449 }
Denys Vlasenkod54f58d2017-09-14 10:51:12 +0200450 G.offset = t;
Denys Vlasenkoe58b4472017-09-15 15:23:47 +0200451 remap(cursor);
452 redraw(cursor);
Denys Vlasenkod54f58d2017-09-14 10:51:12 +0200453 break;
454 }
Denys Vlasenkoe58b4472017-09-15 15:23:47 +0200455 /* ^C/EOF/error: fall through to exiting */
Denys Vlasenkod54f58d2017-09-14 10:51:12 +0200456 }
Denys Vlasenko363fb5e2017-09-14 09:42:40 +0200457 case CTRL('X'):
458 restore_term();
459 return EXIT_SUCCESS;
460 } /* switch */
461 } /* for (;;) */
Denys Vlasenko73af7052017-09-13 19:20:27 +0200462
Denys Vlasenko363fb5e2017-09-14 09:42:40 +0200463 /* not reached */
Denys Vlasenko73af7052017-09-13 19:20:27 +0200464 return EXIT_SUCCESS;
465}