1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16#include "libbb.h"
17
18#define ESC "\033"
19#define HOME ESC"[H"
20#define CLEAR ESC"[J"
21#define CLEAR_TILL_EOL ESC"[K"
22#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)
27
28struct globals {
29 smallint half;
30 smallint in_read_key;
31 int fd;
32 unsigned height;
33 unsigned row;
34 unsigned pagesize;
35 uint8_t *baseaddr;
36 uint8_t *current_byte;
37 uint8_t *eof_byte;
38 off_t size;
39 off_t offset;
40
41 char read_key_buffer[KEYCODE_BUFFER_SIZE];
42 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
49
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
59#define G_mapsize (64*1024)
60
61
62#define LINEBUF_SIZE (8 + 1 + 3*16 + 16 + 1 + 1 + 13)
63
64static 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
75 bb_got_signal = sig;
76 return;
77 }
78 restore_term();
79 kill_myself_with_sig(sig);
80}
81
82static int format_line(char *hex, uint8_t *data, off_t offset)
83{
84 int ofs_pos;
85 char *text;
86 uint8_t *end, *end1;
87
88#if 1
89
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
99 text = hex + 16 * 3;
100 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';
123
124 return ofs_pos;
125}
126
127static void redraw(unsigned cursor)
128{
129 uint8_t *data;
130 off_t offset;
131 unsigned i, pos;
132
133 printf(HOME CLEAR);
134
135
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;
143 pos = i = 0;
144 while (i < G.height) {
145 char buf[LINEBUF_SIZE];
146 pos = format_line(buf, data, offset);
147 printf(
148 "\r\n%s" + (!i) * 2,
149 buf
150 );
151 data += 16;
152 offset += 16;
153 i++;
154 }
155
156 G.row = cursor / 16;
157 printf(ESC"[%u;%uH", 1 + G.row, 1 + pos + (cursor & 0xf) * 3);
158}
159
160static void redraw_cur_line(void)
161{
162 char buf[LINEBUF_SIZE];
163 uint8_t *data;
164 off_t offset;
165 int column;
166
167 column = (0xf & (uintptr_t)G.current_byte);
168 data = G.current_byte - column;
169 offset = G.offset + (data - G.baseaddr);
170
171 column = column*3 + G.half;
172 column += format_line(buf, data, offset);
173 printf("%s"
174 "\r"
175 "%.*s",
176 buf + column,
177 column, buf
178 );
179}
180
181
182static int remap(unsigned cur_pos)
183{
184 if (G.baseaddr)
185 munmap(G.baseaddr, G_mapsize);
186
187 G.baseaddr = mmap(NULL,
188 G_mapsize,
189 PROT_READ | PROT_WRITE,
190 MAP_SHARED,
191 G.fd,
192 G.offset
193 );
194 if (G.baseaddr == MAP_FAILED) {
195 restore_term();
196 bb_perror_msg_and_die("mmap");
197 }
198
199 G.current_byte = G.baseaddr + cur_pos;
200
201 G.eof_byte = G.baseaddr + G_mapsize;
202 if ((G.size - G.offset) < G_mapsize) {
203
204
205 G.eof_byte = G.baseaddr + (G.size - G.offset);
206 }
207 return 1;
208}
209static int move_mapping_further(void)
210{
211 unsigned pos;
212 unsigned pagesize;
213
214 if ((G.size - G.offset) < G_mapsize)
215 return 0;
216
217 pagesize = G_pagesize;
218 pos = G.current_byte - G.baseaddr;
219 if (pos >= pagesize) {
220
221 do {
222 G.offset += pagesize;
223 if (G.offset == 0) {
224 G.offset -= pagesize;
225 break;
226 }
227 pos -= pagesize;
228 } while (pos >= pagesize);
229 return remap(pos);
230 }
231 return 0;
232}
233static int move_mapping_lower(void)
234{
235 unsigned pos;
236 unsigned pagesize;
237
238 if (G.offset == 0)
239 return 0;
240
241 pagesize = G_pagesize;
242 pos = G.current_byte - G.baseaddr;
243
244
245 pos += pagesize;
246 while (pos < G_mapsize) {
247 pos += pagesize;
248 G.offset -= pagesize;
249 if (G.offset == 0)
250 break;
251 }
252 pos -= pagesize;
253
254 return remap(pos);
255}
256
257
258
259
260
261int hexedit_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
262int hexedit_main(int argc UNUSED_PARAM, char **argv)
263{
264 INIT_G();
265 INIT_PAGESIZE();
266
267 get_terminal_width_height(-1, NULL, &G.height);
268 if (1) {
269
270 unsigned sz = (G.height | 0xf) * LINEBUF_SIZE;
271 setvbuf(stdout, xmalloc(sz), _IOFBF, sz);
272 }
273
274 getopt32(argv, "^" "" "\0" "=1");
275 argv += optind;
276
277 G.fd = xopen(*argv, O_RDWR);
278 G.size = xlseek(G.fd, 0, SEEK_END);
279
280
281 printf(SET_ALT_SCR);
282 set_termios_to_raw(STDIN_FILENO, &G.orig_termios, TERMIOS_RAW_CRNL);
283 bb_signals(BB_FATAL_SIGS, sig_catcher);
284
285 remap(0);
286 redraw(0);
287
288
289
290
291
292
293
294
295 for (;;) {
296 unsigned cnt;
297 int32_t key = key;
298 uint8_t byte;
299
300 fflush_all();
301 G.in_read_key = 1;
302 if (!bb_got_signal)
303 key = read_key(STDIN_FILENO, G.read_key_buffer, -1);
304 G.in_read_key = 0;
305 if (bb_got_signal)
306 key = CTRL('X');
307
308 cnt = 1;
309 if ((unsigned)(key - 'A') <= 'Z' - 'A')
310 key |= 0x20;
311 switch (key) {
312 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
313
314 key = key - ('a' - '0' - 10);
315
316 case '0': case '1': case '2': case '3': case '4':
317 case '5': case '6': case '7': case '8': case '9':
318 if (G.current_byte == G.eof_byte) {
319 if (!move_mapping_further()) {
320
321 if (++G.size <= 0
322 || ftruncate(G.fd, G.size) != 0
323 ) {
324 G.size--;
325 break;
326 }
327 G.eof_byte++;
328 }
329 }
330 key -= '0';
331 byte = *G.current_byte & 0xf0;
332 if (!G.half) {
333 byte = *G.current_byte & 0x0f;
334 key <<= 4;
335 }
336 *G.current_byte = byte + key;
337
338 redraw_cur_line();
339
340 case KEYCODE_RIGHT:
341 if (G.current_byte == G.eof_byte)
342 break;
343 byte = *G.current_byte;
344 if (!G.half) {
345 G.half = 1;
346 putchar(bb_hexdigits_upcase[byte >> 4]);
347 } else {
348 G.half = 0;
349 G.current_byte++;
350 if ((0xf & (uintptr_t)G.current_byte) == 0) {
351
352 if (G.current_byte == G.eof_byte)
353 move_mapping_further();
354 printf(ESC"[46D");
355 goto down;
356 }
357 putchar(bb_hexdigits_upcase[byte & 0xf]);
358 putchar(' ');
359 }
360 break;
361 case KEYCODE_PAGEDOWN:
362 cnt = G.height;
363 case KEYCODE_DOWN:
364 k_down:
365 G.current_byte += 16;
366 if (G.current_byte >= G.eof_byte) {
367 move_mapping_further();
368 if (G.current_byte > G.eof_byte) {
369
370 G.current_byte -= 16;
371 if (G.current_byte < G.baseaddr)
372 move_mapping_lower();
373 break;
374 }
375 }
376 down:
377 putchar('\n');
378 G.row++;
379 if (G.row >= G.height) {
380 G.row--;
381 redraw_cur_line();
382 }
383 if (--cnt)
384 goto k_down;
385 break;
386
387 case KEYCODE_LEFT:
388 if (G.half) {
389 G.half = 0;
390 printf(ESC"[D");
391 break;
392 }
393 if ((0xf & (uintptr_t)G.current_byte) == 0) {
394
395 if (G.current_byte == G.baseaddr) {
396 if (!move_mapping_lower())
397 break;
398 }
399 G.half = 1;
400 G.current_byte--;
401 printf(ESC"[46C");
402 goto up;
403 }
404 G.half = 1;
405 G.current_byte--;
406 printf(ESC"[2D");
407 break;
408 case KEYCODE_PAGEUP:
409 cnt = G.height;
410 case KEYCODE_UP:
411 k_up:
412 if ((G.current_byte - G.baseaddr) < 16) {
413 if (!move_mapping_lower())
414 break;
415 }
416 G.current_byte -= 16;
417 up:
418 if (G.row != 0) {
419 G.row--;
420 printf(ESC"[A");
421 } else {
422
423 printf(ESC"M");
424 redraw_cur_line();
425 }
426 if (--cnt)
427 goto k_up;
428 break;
429
430 case '\n':
431 case '\r':
432
433 {
434 char buf[sizeof(G.offset)*3 + 4];
435 printf(ESC"[999;1H" CLEAR_TILL_EOL);
436 if (read_line_input(NULL, "Go to (dec,0Xhex,0oct): ", buf, sizeof(buf)) > 0) {
437 off_t t;
438 unsigned cursor;
439
440 t = bb_strtoull(buf, NULL, 0);
441 if (t >= G.size)
442 t = G.size - 1;
443 cursor = t & (G_pagesize - 1);
444 t -= cursor;
445 if (t < 0)
446 cursor = t = 0;
447 if (t != 0 && cursor < 0x1ff) {
448
449
450 t -= G_pagesize;
451 cursor += G_pagesize;
452 }
453 G.offset = t;
454 remap(cursor);
455 redraw(cursor);
456 break;
457 }
458
459 }
460 case CTRL('X'):
461 restore_term();
462 return EXIT_SUCCESS;
463 }
464 }
465
466
467 return EXIT_SUCCESS;
468}
469