hexedit: implement "[enter] goto offset" key

This is a must if you need to edit sector 123456789999 on your /dev/disk.

   text	   data	    bss	    dec	    hex	filename
 922745	    481	   6832	 930058	  e310a	busybox_old
 923023	    481	   6832	 930336	  e3220	busybox_unstripped

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
Denys Vlasenko 2017-09-14 10:51:12 +02:00
parent 363fb5ec40
commit d54f58d487
2 changed files with 56 additions and 27 deletions

View File

@ -17,7 +17,8 @@
#define ESC "\033" #define ESC "\033"
#define HOME ESC"[H" #define HOME ESC"[H"
#define CLEAR ESC"[H"ESC"[J" #define CLEAR ESC"[J"
#define CLEAR_TILL_EOL ESC"[K"
#define SET_ALT_SCR ESC"[?1049h" #define SET_ALT_SCR ESC"[?1049h"
#define POP_ALT_SCR ESC"[?1049l" #define POP_ALT_SCR ESC"[?1049l"
@ -30,7 +31,7 @@ struct globals {
int fd; int fd;
unsigned height; unsigned height;
unsigned row; unsigned row;
uint8_t *addr; uint8_t *baseaddr;
uint8_t *current_byte; uint8_t *current_byte;
uint8_t *eof_byte; uint8_t *eof_byte;
off_t size; off_t size;
@ -117,14 +118,15 @@ static void redraw(void)
{ {
uint8_t *data; uint8_t *data;
off_t offset; off_t offset;
unsigned i; unsigned i, pos;
data = G.addr; printf(HOME CLEAR);
offset = 0; data = G.baseaddr;
i = 0; offset = G.offset;
pos = i = 0;
while (i < G.height) { while (i < G.height) {
char buf[LINEBUF_SIZE]; char buf[LINEBUF_SIZE];
format_line(buf, data, offset); pos = format_line(buf, data, offset);
printf( printf(
"\r\n%s" + (!i)*2, /* print \r\n only on 2nd line and later */ "\r\n%s" + (!i)*2, /* print \r\n only on 2nd line and later */
buf buf
@ -133,6 +135,7 @@ static void redraw(void)
offset += 16; offset += 16;
i++; i++;
} }
printf(ESC"[1;%uH", pos + 1); /* position on 1st hex byte in first line */
} }
static void redraw_cur_line(void) static void redraw_cur_line(void)
@ -144,7 +147,7 @@ static void redraw_cur_line(void)
column = (0xf & (uintptr_t)G.current_byte); column = (0xf & (uintptr_t)G.current_byte);
data = G.current_byte - column; data = G.current_byte - column;
offset = G.offset + (data - G.addr); offset = G.offset + (data - G.baseaddr);
column = column*3 + G.half; column = column*3 + G.half;
column += format_line(buf, data, offset); column += format_line(buf, data, offset);
@ -158,28 +161,28 @@ static void redraw_cur_line(void)
static void remap(unsigned cur_pos) static void remap(unsigned cur_pos)
{ {
if (G.addr) if (G.baseaddr)
munmap(G.addr, G_mapsize); munmap(G.baseaddr, G_mapsize);
G.addr = mmap(NULL, G.baseaddr = mmap(NULL,
G_mapsize, G_mapsize,
PROT_READ | PROT_WRITE, PROT_READ | PROT_WRITE,
MAP_SHARED, MAP_SHARED,
G.fd, G.fd,
G.offset G.offset
); );
if (G.addr == MAP_FAILED) { if (G.baseaddr == MAP_FAILED) {
restore_term(); restore_term();
bb_perror_msg_and_die("mmap"); bb_perror_msg_and_die("mmap");
} }
G.current_byte = G.addr + cur_pos; G.current_byte = G.baseaddr + cur_pos;
G.eof_byte = G.addr + G_mapsize; G.eof_byte = G.baseaddr + G_mapsize;
if ((G.size - G.offset) < G_mapsize) { if ((G.size - G.offset) < G_mapsize) {
/* mapping covers tail of the file */ /* mapping covers tail of the file */
/* we do have a mapped byte which is past eof */ /* we do have a mapped byte which is past eof */
G.eof_byte = G.addr + (G.size - G.offset); G.eof_byte = G.baseaddr + (G.size - G.offset);
} }
} }
static void move_mapping_further(void) static void move_mapping_further(void)
@ -191,7 +194,7 @@ static void move_mapping_further(void)
return; /* can't move mapping even further, it's at the end already */ return; /* can't move mapping even further, it's at the end already */
pagesize = getpagesize(); /* constant on most arches */ pagesize = getpagesize(); /* constant on most arches */
pos = G.current_byte - G.addr; pos = G.current_byte - G.baseaddr;
if (pos >= pagesize) { if (pos >= pagesize) {
/* move offset up until current position is in 1st page */ /* move offset up until current position is in 1st page */
do { do {
@ -214,7 +217,7 @@ static void move_mapping_lower(void)
return; /* we are at 0 already */ return; /* we are at 0 already */
pagesize = getpagesize(); /* constant on most arches */ pagesize = getpagesize(); /* constant on most arches */
pos = G.current_byte - G.addr; pos = G.current_byte - G.baseaddr;
/* move offset down until current position is in last page */ /* move offset down until current position is in last page */
pos += pagesize; pos += pagesize;
@ -252,18 +255,15 @@ int hexedit_main(int argc UNUSED_PARAM, char **argv)
G.size = xlseek(G.fd, 0, SEEK_END); G.size = xlseek(G.fd, 0, SEEK_END);
/* TERMIOS_RAW_CRNL suppresses \n -> \r\n translation, helps with down-arrow */ /* TERMIOS_RAW_CRNL suppresses \n -> \r\n translation, helps with down-arrow */
printf(SET_ALT_SCR);
set_termios_to_raw(STDIN_FILENO, &G.orig_termios, TERMIOS_RAW_CRNL); set_termios_to_raw(STDIN_FILENO, &G.orig_termios, TERMIOS_RAW_CRNL);
bb_signals(BB_FATAL_SIGS, sig_catcher); bb_signals(BB_FATAL_SIGS, sig_catcher);
remap(0); remap(0);
printf(SET_ALT_SCR);
redraw(); redraw();
printf(ESC"[1;10H"); /* position on 1st hex byte in first line */
//TODO: //Home/End: start/end of line; '<'/'>': start/end of file //TODO: //Home/End: start/end of line; '<'/'>': start/end of file
//Backspace: undo //Backspace: undo
//Enter: goto specified position
//Ctrl-L: redraw //Ctrl-L: redraw
//Ctrl-Z: suspend //Ctrl-Z: suspend
//'/', Ctrl-S: search //'/', Ctrl-S: search
@ -367,9 +367,9 @@ int hexedit_main(int argc UNUSED_PARAM, char **argv)
} }
if ((0xf & (uintptr_t)G.current_byte) == 0) { if ((0xf & (uintptr_t)G.current_byte) == 0) {
/* leftmost pos, wrap to prev line */ /* leftmost pos, wrap to prev line */
if (G.current_byte == G.addr) { if (G.current_byte == G.baseaddr) {
move_mapping_lower(); move_mapping_lower();
if (G.current_byte == G.addr) if (G.current_byte == G.baseaddr)
break; /* first line, don't do anything */ break; /* first line, don't do anything */
} }
G.half = 1; G.half = 1;
@ -385,9 +385,9 @@ int hexedit_main(int argc UNUSED_PARAM, char **argv)
cnt = G.height; cnt = G.height;
case KEYCODE_UP: case KEYCODE_UP:
k_up: k_up:
if ((G.current_byte - G.addr) < 16) { if ((G.current_byte - G.baseaddr) < 16) {
move_mapping_lower(); move_mapping_lower();
if ((G.current_byte - G.addr) < 16) if ((G.current_byte - G.baseaddr) < 16)
break; break;
} }
G.current_byte -= 16; G.current_byte -= 16;
@ -403,6 +403,35 @@ int hexedit_main(int argc UNUSED_PARAM, char **argv)
if (--cnt) if (--cnt)
goto k_up; goto k_up;
break; break;
case '\n':
case '\r':
/* [Enter]: goto specified position */
{
char buf[sizeof(G.offset)*3 + 4];
printf(ESC"[999;1H" CLEAR_TILL_EOL); /* go to last line */
if (read_line_input(NULL, "Go to (dec,0Xhex,0oct): ", buf, sizeof(buf)) >= 0) {
off_t t;
unsigned pgmask;
t = bb_strtoull(buf, NULL, 0);
if (t >= G.size)
t = G.size - 1;
pgmask = getpagesize() - 1;
cnt = t & pgmask;
t = t & ~(off_t)pgmask;
if (t < 0)
cnt = t = 0;
G.offset = t;
remap(0);
redraw();
cnt /= 16;
if (cnt)
goto k_down;
break;
}
/* EOF/error on input: fall through to exiting */
}
case CTRL('X'): case CTRL('X'):
restore_term(); restore_term();
return EXIT_SUCCESS; return EXIT_SUCCESS;

View File

@ -281,9 +281,9 @@ static void set_tty_cooked(void)
/* Move the cursor to a position (x,y), where (0,0) is the /* Move the cursor to a position (x,y), where (0,0) is the
top-left corner of the console */ top-left corner of the console */
static void move_cursor(int line, int row) static void move_cursor(int line, int col)
{ {
printf(ESC"[%u;%uH", line, row); printf(ESC"[%u;%uH", line, col);
} }
static void clear_line(void) static void clear_line(void)