diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2017-09-13 19:20:27 +0200 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2017-09-13 19:20:27 +0200 |
commit | 73af705628ddaedc4c6f7f78b9658d6c01310309 (patch) | |
tree | 9d7b4e268cb5a36e54bd2009986694faa7228708 | |
parent | b5a0396716e6ef2882ae40b2b62b4ef6e8e21da0 (diff) | |
download | busybox-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.c | 357 | ||||
-rw-r--r-- | miscutils/less.c | 2 |
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 | |||
22 | struct 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 | |||
41 | static 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 | |||
71 | static 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 | |||
93 | static 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 | |||
114 | static 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 | } | ||
139 | static 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 | } | ||
162 | static 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 | |||
186 | static 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" | ||
196 | int hexedit_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
197 | int 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 | ||