1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31#define FOR_hexedit
32#include "toys.h"
33
34GLOBALS(
35 char *data, *search, keybuf[16], input[80];
36 long long len, base, pos;
37 int numlen, undo, undolen, mode;
38 unsigned rows, cols;
39)
40
41#define UNDO_LEN (sizeof(toybuf)/(sizeof(long long)+1))
42
43static void show_error(char *what)
44{
45 printf("\e[%dH\e[41m\e[37m\e[K\e[1m%s\e[0m", TT.rows+1, what);
46 xflush(1);
47 msleep(500);
48}
49
50
51static int prompt(char *prompt, char *initial_value)
52{
53 int yes = 0, key, len = strlen(initial_value);
54
55 strcpy(TT.input, initial_value);
56 while (1) {
57 printf("\e[%dH\e[K\e[1m%s: \e[0m%s\e[?25h", TT.rows+1, prompt, TT.input);
58 xflush(1);
59
60 key = scan_key(TT.keybuf, -1);
61 if (key < 0 || key == 27) break;
62 if (key == '\r') {
63 yes = len;
64 break;
65 }
66
67 if (key == 0x7f && (len > 0)) TT.input[--len] = 0;
68 else if (key == 'U'-'@') while (len > 0) TT.input[--len] = 0;
69 else if (key >= ' ' && key < 0x7f && len < sizeof(TT.input))
70 TT.input[len++] = key;
71 }
72 printf("\e[?25l");
73
74 return yes;
75}
76
77
78static void draw_char(int ch)
79{
80 if (ch >= ' ' && ch < 0x7f) {
81 putchar(ch);
82 return;
83 }
84
85 if (TT.mode) {
86 if (ch>127) {
87 printf("\e[2m");
88 ch &= 127;
89 }
90 if (ch<32 || ch==127) {
91 printf("\e[7m");
92 if (ch==127) ch = 32;
93 else ch += 64;
94 }
95 xputc(ch);
96 } else {
97 if (ch < ' ') printf("\e[31m%c", ch + '@');
98 else printf("\e[35m?");
99 }
100 printf("\e[0m");
101}
102
103static void draw_status(void)
104{
105 char line[80];
106
107 printf("\e[%dH\e[K", TT.rows+1);
108
109 snprintf(line, sizeof(line), "\"%s\"%s, %#llx/%#llx", *toys.optargs,
110 FLAG(r) ? " [readonly]" : "", TT.pos, TT.len);
111 draw_trim(line, -1, TT.cols);
112}
113
114static void draw_byte(int byte)
115{
116 if (byte) printf("%02x", byte);
117 else printf("\e[2m00\e[0m");
118}
119
120static void draw_line(long long yy)
121{
122 int x, xx = 16;
123
124 yy = (TT.base+yy)*16;
125 if (yy+xx>=TT.len) xx = TT.len-yy;
126
127 if (yy<TT.len) {
128 printf("\r\e[%dm%0*llx\e[0m ", 33*!TT.mode, TT.numlen, yy);
129 for (x=0; x<xx; x++) {
130 putchar(' ');
131 draw_byte(TT.data[yy+x]);
132 }
133 printf("%*s", 2+3*(16-xx), "");
134 for (x=0; x<xx; x++) draw_char(TT.data[yy+x]);
135 printf("%*s", 16-xx, "");
136 }
137 printf("\e[K");
138}
139
140static void draw_page(void)
141{
142 int y;
143
144 for (y = 0; y<TT.rows; y++) {
145 printf(y ? "\r\n" : "\e[H");
146 draw_line(y);
147 }
148 draw_status();
149}
150
151
152static void highlight(int xx, int yy, int side)
153{
154 char cc = TT.data[16*(TT.base+yy)+xx];
155 int i;
156
157
158 printf("\e[%u;%uH\e[%dm", yy+1, TT.numlen+3*(xx+1), 7*(side!=2));
159 if (side>1) draw_byte(cc);
160 else for (i=0; i<2;) {
161 if (side==i) printf("\e[32m");
162 printf("%x", (cc>>(4*(1&++i)))&15);
163 }
164
165
166 printf("\e[7m\e[%u;%uH"+4*(side==2), yy+1, 1+TT.numlen+17*3+xx);
167 draw_char(cc);
168}
169
170static void find_next(int pos)
171{
172 char *p;
173
174 p = memmem(TT.data+pos, TT.len-pos, TT.search, strlen(TT.search));
175 if (p) TT.pos = p - TT.data;
176 else show_error("No match!");
177}
178
179static void find_prev(int pos)
180{
181 size_t len = strlen(TT.search);
182
183 for (; pos >= 0; pos--) {
184 if (!memcmp(TT.data+pos, TT.search, len)) {
185 TT.pos = pos;
186 return;
187 }
188 }
189 show_error("No match!");
190}
191
192void hexedit_main(void)
193{
194 long long y;
195 int x, i, side = 0, key, fd;
196
197
198 TT.cols = 80;
199 TT.rows = 24;
200 terminal_size(&TT.cols, &TT.rows);
201 if (TT.rows) TT.rows--;
202 xsignal(SIGWINCH, generic_signal);
203 sigatexit(tty_sigreset);
204 dprintf(1, "\e[0m\e[?25l");
205 xset_terminal(1, 1, 0, 0);
206
207 if (access(*toys.optargs, W_OK)) toys.optflags |= FLAG_r;
208 fd = xopen(*toys.optargs, FLAG(r) ? O_RDONLY : O_RDWR);
209 if ((TT.len = fdlength(fd))<1) error_exit("bad length");
210 if (sizeof(long)==32 && TT.len>SIZE_MAX) TT.len = SIZE_MAX;
211
212 for (TT.pos = TT.len, TT.numlen = 0; TT.pos; TT.pos >>= 4, TT.numlen++);
213 TT.numlen += (4-TT.numlen)&3;
214
215 TT.data=xmmap(0, TT.len, PROT_READ|(PROT_WRITE*!FLAG(r)), MAP_SHARED, fd, 0);
216 close(fd);
217 draw_page();
218
219 for (;;) {
220
221 if (TT.pos<0) TT.pos = 0;
222 if (TT.pos>=TT.len) TT.pos = TT.len-1;
223 x = TT.pos&15;
224 y = TT.pos/16;
225
226
227 while (y<TT.base) {
228 if (TT.base-y>(TT.rows/2)) {
229 TT.base = y;
230 draw_page();
231 } else {
232 TT.base--;
233 printf("\e[H\e[1L");
234 draw_line(0);
235 }
236 }
237
238
239 while (y>=TT.base+TT.rows) {
240 if (y-(TT.base+TT.rows)>(TT.rows/2)) {
241 TT.base = y-TT.rows-1;
242 draw_page();
243 } else {
244 TT.base++;
245 printf("\e[H\e[1M\e[%uH", TT.rows);
246 draw_line(TT.rows-1);
247 }
248 }
249
250 draw_status();
251 y -= TT.base;
252
253
254 highlight(x, y, FLAG(r) ? 3 : side);
255 xflush(1);
256
257
258 key = scan_key(TT.keybuf, -1);
259
260
261 if (key == -3) {
262 toys.signal = 0;
263 terminal_size(&TT.cols, &TT.rows);
264 if (TT.rows) TT.rows--;
265 draw_page();
266 continue;
267 }
268
269 if (key == 'x') {
270 TT.mode = !TT.mode;
271 printf("\e[0m");
272 draw_page();
273 continue;
274 }
275
276
277 if (key==-1||key==('C'-'@')||key==('Q'-'@')||key==27||key=='q') break;
278 highlight(x, y, 2);
279
280 if (key == ('J'-'@') || key == ':' || key == '-' || key == '+') {
281
282 char initial[2] = {}, *s = 0;
283 long long val;
284
285 if (key == '-' || key == '+') *initial = key;
286 if (!prompt("Jump to", initial)) continue;
287
288 val = estrtol(TT.input, &s, 0);
289 if (!errno && s && !*s) {
290 if (*TT.input == '-' || *TT.input == '+') TT.pos += val;
291 else TT.pos = val;
292 }
293 continue;
294 } else if (key == ('F'-'@') || key == '/') {
295 if (!prompt("Find", TT.search ? TT.search : "")) continue;
296
297
298 free(TT.search);
299 TT.search = xstrdup(TT.input);
300 find_next(TT.pos);
301 } else if (TT.search && (key == ('G'-'@') || key == 'n')) {
302 if (TT.pos < TT.len) find_next(TT.pos+1);
303 } else if (TT.search && (key == ('D'-'@') || key == 'p')) {
304 if (TT.pos > 0) find_prev(TT.pos-1);
305 }
306
307
308 highlight(x, y, 2);
309
310
311 if (key>='a' && key<='f') key-=32;
312 if (!FLAG(r) && ((key>='0' && key<='9') || (key>='A' && key<='F'))) {
313 if (!side) {
314 long long *ll = (long long *)toybuf;
315
316 ll[TT.undo] = TT.pos;
317 toybuf[(sizeof(long long)*UNDO_LEN)+TT.undo++] = TT.data[TT.pos];
318 if (TT.undolen < UNDO_LEN) TT.undolen++;
319 TT.undo %= UNDO_LEN;
320 }
321
322 i = key - '0';
323 if (i>9) i -= 7;
324 TT.data[TT.pos] &= 15<<(4*side);
325 TT.data[TT.pos] |= i<<(4*!side);
326
327 if (++side==2) {
328 highlight(x, y, side);
329 side = 0;
330 ++TT.pos;
331 }
332 } else side = 0;
333 if (key=='u') {
334 if (TT.undolen) {
335 long long *ll = (long long *)toybuf;
336
337 TT.undolen--;
338 if (!TT.undo) TT.undo = UNDO_LEN;
339 TT.pos = ll[--TT.undo];
340 TT.data[TT.pos] = toybuf[sizeof(long long)*UNDO_LEN+TT.undo];
341 }
342 }
343 if (key>=256) {
344 key -= 256;
345
346 if (key==KEY_UP) TT.pos -= 16;
347 else if (key==KEY_DOWN) TT.pos += 16;
348 else if (key==KEY_RIGHT) {
349 if (TT.pos<TT.len) TT.pos++;
350 } else if (key==KEY_LEFT) {
351 if (TT.pos>0) TT.pos--;
352 } else if (key==KEY_PGUP) {
353 TT.pos -= 16*TT.rows;
354 if (TT.pos < 0) TT.pos = 0;
355 TT.base = TT.pos/16;
356 draw_page();
357 } else if (key==KEY_PGDN) {
358 TT.pos += 16*TT.rows;
359 if (TT.pos > TT.len-1) TT.pos = TT.len-1;
360 TT.base = TT.pos/16;
361 draw_page();
362 } else if (key==KEY_HOME) TT.pos &= ~0xf;
363 else if (key==KEY_END) TT.pos |= 0xf;
364 else if (key==(KEY_CTRL|KEY_HOME)) TT.pos = 0;
365 else if (key==(KEY_CTRL|KEY_END)) TT.pos = TT.len-1;
366 }
367 }
368 munmap(TT.data, TT.len);
369 tty_reset();
370}
371