aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2017-09-13 19:20:27 +0200
committerDenys Vlasenko <vda.linux@googlemail.com>2017-09-13 19:20:27 +0200
commit73af705628ddaedc4c6f7f78b9658d6c01310309 (patch)
tree9d7b4e268cb5a36e54bd2009986694faa7228708
parentb5a0396716e6ef2882ae40b2b62b4ef6e8e21da0 (diff)
downloadbusybox-w32-73af705628ddaedc4c6f7f78b9658d6c01310309.tar.gz
busybox-w32-73af705628ddaedc4c6f7f78b9658d6c01310309.tar.bz2
busybox-w32-73af705628ddaedc4c6f7f78b9658d6c01310309.zip
hexedit: new applet
function old new delta hexedit_main - 930 +930 format_line - 197 +197 remap - 168 +168 move_mapping_further - 141 +141 move_mapping_lower - 107 +107 redraw_cur_line - 104 +104 packed_usage 31802 31812 +10 applet_names 2688 2696 +8 applet_main 1552 1556 +4 applet_suid 97 98 +1 applet_install_loc 194 195 +1 ------------------------------------------------------------------------------ (add/remove: 7/0 grow/shrink: 5/0 up/down: 1671/0) Total: 1671 bytes Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r--miscutils/hexedit.c357
-rw-r--r--miscutils/less.c2
2 files changed, 358 insertions, 1 deletions
diff --git a/miscutils/hexedit.c b/miscutils/hexedit.c
new file mode 100644
index 000000000..cd4b3b1ad
--- /dev/null
+++ b/miscutils/hexedit.c
@@ -0,0 +1,357 @@
1/*
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
7//config: bool "hexedit"
8//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
18#define ESC "\033"
19#define HOME ESC"[H"
20#define CLEAR ESC"[H"ESC"[J"
21
22struct globals {
23 smallint half;
24 int fd;
25 unsigned height;
26 uint8_t *addr;
27 uint8_t *current_byte;
28 uint8_t *eof_byte;
29 off_t size;
30 off_t offset;
31 struct termios orig_termios;
32};
33#define G (*ptr_to_globals)
34#define INIT_G() do { \
35 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
36} while (0)
37
38/* Hopefully there aren't arches with PAGE_SIZE > 64k */
39#define G_mapsize (64*1024)
40
41static void format_line(char *hex, uint8_t *data, off_t offset)
42{
43 char *text = hex + 16*3;
44 uint8_t *end, *end1;
45
46 end1 = data + 15;
47 if ((G.size - offset) > 0) {
48 end = end1;
49 if ((G.size - offset) <= 15)
50 end = data + (G.size - offset) - 1;
51 while (data <= end) {
52 uint8_t c = *data++;
53 *hex++ = bb_hexdigits_upcase[c >> 4];
54 *hex++ = bb_hexdigits_upcase[c & 0xf];
55 *hex++ = ' ';
56 if (c < ' ' || c > 0x7e)
57 c = '.';
58 *text++ = c;
59 }
60 }
61 while (data <= end1) {
62 *hex++ = ' ';
63 *hex++ = ' ';
64 *hex++ = ' ';
65 *text++ = ' ';
66 data++;
67 }
68 *text = '\0';
69}
70
71static void redraw(void)
72{
73 uint8_t *data;
74 off_t offset;
75 unsigned i;
76
77 data = G.addr;
78 offset = 0;
79 i = 0;
80 while (i < G.height) {
81 char buf[16*4 + 8];
82 format_line(buf, data, offset);
83 printf(
84 "\r\n%08"OFF_FMT"x %s" + (!i)*2, /* print \r\n only on 2nd line and later */
85 offset, buf
86 );
87 data += 16;
88 offset += 16;
89 i++;
90 }
91}
92
93static void redraw_cur_line(void)
94{
95 off_t offset;
96 int len;
97 char buf[16*4 + 8];
98 uint8_t *data;
99
100 len = (0xf & (uintptr_t)G.current_byte);
101 data = G.current_byte - len;
102 offset = G.offset + (data - G.addr);
103 format_line(buf, data, offset);
104 printf(
105 "\r%08"OFF_FMT"x %s",
106 offset, buf
107 );
108 printf(
109 "\r%08"OFF_FMT"x %.*s",
110 offset, (len*3 + G.half), buf
111 );
112}
113
114static void remap(unsigned cur_pos)
115{
116 if (G.addr)
117 munmap(G.addr, G_mapsize);
118
119 G.addr = mmap(NULL,
120 G_mapsize,
121 PROT_READ | PROT_WRITE,
122 MAP_SHARED,
123 G.fd,
124 G.offset
125 );
126 if (G.addr == MAP_FAILED)
127//TODO: restore termios?
128 bb_perror_msg_and_die("mmap");
129
130 G.current_byte = G.addr + cur_pos;
131
132 G.eof_byte = G.addr + G_mapsize;
133 if ((G.size - G.offset) < G_mapsize) {
134 /* mapping covers tail of the file */
135 /* we do have a mapped byte which is past eof */
136 G.eof_byte = G.addr + (G.size - G.offset);
137 }
138}
139static void move_mapping_further(void)
140{
141 unsigned pos;
142 unsigned pagesize;
143
144 if ((G.size - G.offset) < G_mapsize)
145 return; /* can't move mapping even further, it's at the end already */
146
147 pagesize = getpagesize(); /* constant on most arches */
148 pos = G.current_byte - G.addr;
149 if (pos >= pagesize) {
150 /* Move offset up until current position is in 1st page */
151 do {
152 G.offset += pagesize;
153 if (G.offset == 0) { /* whoops */
154 G.offset -= pagesize;
155 break;
156 }
157 pos -= pagesize;
158 } while (pos >= pagesize);
159 remap(pos);
160 }
161}
162static void move_mapping_lower(void)
163{
164 unsigned pos;
165 unsigned pagesize;
166
167 if (G.offset == 0)
168 return; /* we are at 0 already */
169
170 pagesize = getpagesize(); /* constant on most arches */
171 pos = G.current_byte - G.addr;
172
173 /* Move offset down until current position is in last page */
174 pos += pagesize;
175 while (pos < G_mapsize) {
176 pos += pagesize;
177 G.offset -= pagesize;
178 if (G.offset == 0)
179 break;
180 }
181 pos -= pagesize;
182
183 remap(pos);
184}
185
186static void sig_catcher(int sig)
187{
188 tcsetattr_stdin_TCSANOW(&G.orig_termios);
189 kill_myself_with_sig(sig);
190}
191
192//usage:#define hexedit_trivial_usage
193//usage: "FILE"
194//usage:#define hexedit_full_usage "\n\n"
195//usage: "Edit FILE in hexadecimal"
196int hexedit_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
197int hexedit_main(int argc UNUSED_PARAM, char **argv)
198{
199 unsigned row = 0;
200
201 INIT_G();
202
203 getopt32(argv, "");
204 argv += optind;
205
206 get_terminal_width_height(-1, NULL, &G.height);
207
208 G.fd = xopen(*argv, O_RDWR);
209 G.size = xlseek(G.fd, 0, SEEK_END);
210
211 /* TERMIOS_RAW_CRNL suppresses \n -> \r\n translation, helps with down-arrow */
212 set_termios_to_raw(STDIN_FILENO, &G.orig_termios, TERMIOS_RAW_CRNL);
213 bb_signals(BB_FATAL_SIGS, sig_catcher);
214
215 remap(0);
216
217 printf(CLEAR);
218 redraw();
219 printf(HOME "%08x ", 0);
220
221//TODO: //PgUp/PgDown; Home/End: start/end of line; '<'/'>': start/end of file
222 //Backspace: undo
223 //Enter: goto specified position
224 //Ctrl-L: redraw
225 //Ctrl-X: save and exit (maybe also Q?)
226 //Ctrl-Z: suspend
227 //'/', Ctrl-S: search
228//TODO: go to end-of-screen on exit (for this, sighandler should interrupt read_key())
229//TODO: detect window resize
230//TODO: read-only mode if open(O_RDWR) fails? hide cursor in this case?
231
232//TODO: smarter redraw: if down-arrow is pressed on last visible line,
233//emit LF, then print the tail of next line, then CR, then beginning -
234//which makes cursor end up exactly where it should be! Same for up-arrow.
235
236 for (;;) {
237 char read_key_buffer[KEYCODE_BUFFER_SIZE];
238 int32_t key;
239 uint8_t byte;
240
241 fflush_all();
242 key = read_key(STDIN_FILENO, read_key_buffer, -1);
243
244 switch (key) {
245 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
246 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
247 /* lowercase, then convert to '0'+10...15 */
248 key = (key | 0x20) - ('a' - '0' - 10);
249 /* fall through */
250 case '0': case '1': case '2': case '3': case '4':
251 case '5': case '6': case '7': case '8': case '9':
252 if (G.current_byte == G.eof_byte) {
253 move_mapping_further();
254 if (G.current_byte == G.eof_byte) {
255 /* extend the file */
256 if (++G.size <= 0 /* overflow? */
257 || ftruncate(G.fd, G.size) != 0 /* error extending? (e.g. block dev) */
258 ) {
259 G.size--;
260 break;
261 }
262 G.eof_byte++;
263 }
264 }
265 key -= '0';
266 byte = *G.current_byte & 0xf0;
267 if (!G.half) {
268 byte = *G.current_byte & 0x0f;
269 key <<= 4;
270 }
271 *G.current_byte = byte + key;
272 /* can't just print one updated hex char: need to update right-hand ASCII too */
273 redraw_cur_line();
274 /* fall through */
275 case KEYCODE_RIGHT:
276 if (G.current_byte == G.eof_byte)
277 break; /* eof - don't allow going past it */
278 byte = *G.current_byte;
279 if (!G.half) {
280 G.half = 1;
281 putchar(bb_hexdigits_upcase[byte >> 4]);
282 } else {
283 G.half = 0;
284 G.current_byte++;
285 if ((0xf & (uintptr_t)G.current_byte) == 0) {
286 /* rightmost pos, wrap to next line */
287 if (G.current_byte == G.eof_byte)
288 move_mapping_further();
289 printf(ESC"[46D"); /* cursor left 3*15 + 1 chars */
290 goto down;
291 }
292 putchar(bb_hexdigits_upcase[byte & 0xf]);
293 putchar(' ');
294 }
295 break;
296 case KEYCODE_DOWN:
297 G.current_byte += 16;
298 if (G.current_byte >= G.eof_byte) {
299 move_mapping_further();
300 if (G.current_byte > G.eof_byte) {
301 /* eof - don't allow going past it */
302 G.current_byte -= 16;
303 break;
304 }
305 }
306 down:
307 putchar('\n'); /* down one line, possibly scroll screen */
308 row++;
309 if (row >= G.height) {
310 row--;
311 redraw_cur_line();
312 }
313 break;
314
315 case KEYCODE_LEFT:
316 if (G.half) {
317 G.half = 0;
318 printf(ESC"[D");
319 break;
320 }
321 if ((0xf & (uintptr_t)G.current_byte) == 0) {
322 /* leftmost pos, wrap to prev line */
323 if (G.current_byte == G.addr)
324 move_mapping_lower();
325 if ((G.current_byte - G.addr) < 16)
326 break; /* first line, don't do anything */
327 G.half = 1;
328 G.current_byte--;
329 printf(ESC"[46C"); /* cursor right 3*15 + 1 chars */
330 goto up;
331 }
332 G.half = 1;
333 G.current_byte--;
334 printf(ESC"[2D");
335 break;
336 case KEYCODE_UP:
337 if ((G.current_byte - G.addr) < 16) {
338 move_mapping_lower();
339 if ((G.current_byte - G.addr) < 16)
340 break;
341 }
342 G.current_byte -= 16;
343 up:
344 if (row != 0) {
345 row--;
346 printf(ESC"[A"); /* up (won't scroll) */
347 } else {
348 //printf(ESC"[T"); /* scroll up */ - not implemented on Linux VT!
349 printf(ESC"M"); /* scroll up */
350 redraw_cur_line();
351 }
352 break;
353 }
354 }
355
356 return EXIT_SUCCESS;
357}
diff --git a/miscutils/less.c b/miscutils/less.c
index c1d5e1b39..f37c80ad8 100644
--- a/miscutils/less.c
+++ b/miscutils/less.c
@@ -139,7 +139,7 @@
139#define HIGHLIGHT ESC"[7m" 139#define HIGHLIGHT ESC"[7m"
140#define NORMAL ESC"[0m" 140#define NORMAL ESC"[0m"
141/* The escape code to home and clear to the end of screen */ 141/* The escape code to home and clear to the end of screen */
142#define CLEAR ESC"[H\033[J" 142#define CLEAR ESC"[H"ESC"[J"
143/* The escape code to clear to the end of line */ 143/* The escape code to clear to the end of line */
144#define CLEAR_2_EOL ESC"[K" 144#define CLEAR_2_EOL ESC"[K"
145 145