1*cf5a6c84SAndroid Build Coastguard Worker /* hexedit.c - Hexadecimal file editor
2*cf5a6c84SAndroid Build Coastguard Worker *
3*cf5a6c84SAndroid Build Coastguard Worker * Copyright 2015 Rob Landley <[email protected]>
4*cf5a6c84SAndroid Build Coastguard Worker *
5*cf5a6c84SAndroid Build Coastguard Worker * No standard.
6*cf5a6c84SAndroid Build Coastguard Worker
7*cf5a6c84SAndroid Build Coastguard Worker USE_HEXEDIT(NEWTOY(hexedit, "<1>1r", TOYFLAG_USR|TOYFLAG_BIN))
8*cf5a6c84SAndroid Build Coastguard Worker
9*cf5a6c84SAndroid Build Coastguard Worker config HEXEDIT
10*cf5a6c84SAndroid Build Coastguard Worker bool "hexedit"
11*cf5a6c84SAndroid Build Coastguard Worker default y
12*cf5a6c84SAndroid Build Coastguard Worker help
13*cf5a6c84SAndroid Build Coastguard Worker usage: hexedit [-r] FILE
14*cf5a6c84SAndroid Build Coastguard Worker
15*cf5a6c84SAndroid Build Coastguard Worker Hexadecimal file editor/viewer. All changes are written to disk immediately.
16*cf5a6c84SAndroid Build Coastguard Worker
17*cf5a6c84SAndroid Build Coastguard Worker -r Read only (display but don't edit)
18*cf5a6c84SAndroid Build Coastguard Worker
19*cf5a6c84SAndroid Build Coastguard Worker Keys:
20*cf5a6c84SAndroid Build Coastguard Worker Arrows Move left/right/up/down by one line/column
21*cf5a6c84SAndroid Build Coastguard Worker PgUp/PgDn Move up/down by one page
22*cf5a6c84SAndroid Build Coastguard Worker Home/End Start/end of line (start/end of file with ctrl)
23*cf5a6c84SAndroid Build Coastguard Worker 0-9, a-f Change current half-byte to hexadecimal value
24*cf5a6c84SAndroid Build Coastguard Worker ^J or : Jump (+/- for relative offset, otherwise absolute address)
25*cf5a6c84SAndroid Build Coastguard Worker ^F or / Find string (^G/n: next, ^D/p: previous match)
26*cf5a6c84SAndroid Build Coastguard Worker u Undo
27*cf5a6c84SAndroid Build Coastguard Worker x Toggle bw/color display
28*cf5a6c84SAndroid Build Coastguard Worker q/^C/^Q/Esc Quit
29*cf5a6c84SAndroid Build Coastguard Worker */
30*cf5a6c84SAndroid Build Coastguard Worker
31*cf5a6c84SAndroid Build Coastguard Worker #define FOR_hexedit
32*cf5a6c84SAndroid Build Coastguard Worker #include "toys.h"
33*cf5a6c84SAndroid Build Coastguard Worker
34*cf5a6c84SAndroid Build Coastguard Worker GLOBALS(
35*cf5a6c84SAndroid Build Coastguard Worker char *data, *search, keybuf[16], input[80];
36*cf5a6c84SAndroid Build Coastguard Worker long long len, base, pos;
37*cf5a6c84SAndroid Build Coastguard Worker int numlen, undo, undolen, mode;
38*cf5a6c84SAndroid Build Coastguard Worker unsigned rows, cols;
39*cf5a6c84SAndroid Build Coastguard Worker )
40*cf5a6c84SAndroid Build Coastguard Worker
41*cf5a6c84SAndroid Build Coastguard Worker #define UNDO_LEN (sizeof(toybuf)/(sizeof(long long)+1))
42*cf5a6c84SAndroid Build Coastguard Worker
show_error(char * what)43*cf5a6c84SAndroid Build Coastguard Worker static void show_error(char *what)
44*cf5a6c84SAndroid Build Coastguard Worker {
45*cf5a6c84SAndroid Build Coastguard Worker printf("\e[%dH\e[41m\e[37m\e[K\e[1m%s\e[0m", TT.rows+1, what);
46*cf5a6c84SAndroid Build Coastguard Worker fflush(0);
47*cf5a6c84SAndroid Build Coastguard Worker msleep(500);
48*cf5a6c84SAndroid Build Coastguard Worker }
49*cf5a6c84SAndroid Build Coastguard Worker
50*cf5a6c84SAndroid Build Coastguard Worker // TODO: support arrow keys, insertion, and scrolling (and reuse in vi)
prompt(char * prompt,char * initial_value)51*cf5a6c84SAndroid Build Coastguard Worker static int prompt(char *prompt, char *initial_value)
52*cf5a6c84SAndroid Build Coastguard Worker {
53*cf5a6c84SAndroid Build Coastguard Worker int yes = 0, key, len = strlen(initial_value);
54*cf5a6c84SAndroid Build Coastguard Worker
55*cf5a6c84SAndroid Build Coastguard Worker strcpy(TT.input, initial_value);
56*cf5a6c84SAndroid Build Coastguard Worker while (1) {
57*cf5a6c84SAndroid Build Coastguard Worker printf("\e[%dH\e[K\e[1m%s: \e[0m%s\e[?25h", TT.rows+1, prompt, TT.input);
58*cf5a6c84SAndroid Build Coastguard Worker fflush(0);
59*cf5a6c84SAndroid Build Coastguard Worker
60*cf5a6c84SAndroid Build Coastguard Worker key = scan_key(TT.keybuf, -1);
61*cf5a6c84SAndroid Build Coastguard Worker if (key < 0 || key == 27) break;
62*cf5a6c84SAndroid Build Coastguard Worker if (key == '\r') {
63*cf5a6c84SAndroid Build Coastguard Worker yes = len; // Hitting enter with no input counts as cancellation.
64*cf5a6c84SAndroid Build Coastguard Worker break;
65*cf5a6c84SAndroid Build Coastguard Worker }
66*cf5a6c84SAndroid Build Coastguard Worker
67*cf5a6c84SAndroid Build Coastguard Worker if (key == 0x7f && (len > 0)) TT.input[--len] = 0;
68*cf5a6c84SAndroid Build Coastguard Worker else if (key == 'U'-'@') while (len > 0) TT.input[--len] = 0;
69*cf5a6c84SAndroid Build Coastguard Worker else if (key >= ' ' && key < 0x7f && len < sizeof(TT.input))
70*cf5a6c84SAndroid Build Coastguard Worker TT.input[len++] = key;
71*cf5a6c84SAndroid Build Coastguard Worker }
72*cf5a6c84SAndroid Build Coastguard Worker printf("\e[?25l");
73*cf5a6c84SAndroid Build Coastguard Worker
74*cf5a6c84SAndroid Build Coastguard Worker return yes;
75*cf5a6c84SAndroid Build Coastguard Worker }
76*cf5a6c84SAndroid Build Coastguard Worker
77*cf5a6c84SAndroid Build Coastguard Worker // Render all characters printable, using color to distinguish.
draw_char(int ch)78*cf5a6c84SAndroid Build Coastguard Worker static void draw_char(int ch)
79*cf5a6c84SAndroid Build Coastguard Worker {
80*cf5a6c84SAndroid Build Coastguard Worker if (ch >= ' ' && ch < 0x7f) {
81*cf5a6c84SAndroid Build Coastguard Worker putchar(ch);
82*cf5a6c84SAndroid Build Coastguard Worker return;
83*cf5a6c84SAndroid Build Coastguard Worker }
84*cf5a6c84SAndroid Build Coastguard Worker
85*cf5a6c84SAndroid Build Coastguard Worker if (TT.mode) {
86*cf5a6c84SAndroid Build Coastguard Worker if (ch>127) {
87*cf5a6c84SAndroid Build Coastguard Worker printf("\e[2m");
88*cf5a6c84SAndroid Build Coastguard Worker ch &= 127;
89*cf5a6c84SAndroid Build Coastguard Worker }
90*cf5a6c84SAndroid Build Coastguard Worker if (ch<32 || ch==127) {
91*cf5a6c84SAndroid Build Coastguard Worker printf("\e[7m");
92*cf5a6c84SAndroid Build Coastguard Worker if (ch==127) ch = 32;
93*cf5a6c84SAndroid Build Coastguard Worker else ch += 64;
94*cf5a6c84SAndroid Build Coastguard Worker }
95*cf5a6c84SAndroid Build Coastguard Worker xputc(ch);
96*cf5a6c84SAndroid Build Coastguard Worker } else {
97*cf5a6c84SAndroid Build Coastguard Worker if (ch < ' ') printf("\e[31m%c", ch + '@');
98*cf5a6c84SAndroid Build Coastguard Worker else printf("\e[35m?");
99*cf5a6c84SAndroid Build Coastguard Worker }
100*cf5a6c84SAndroid Build Coastguard Worker printf("\e[0m");
101*cf5a6c84SAndroid Build Coastguard Worker }
102*cf5a6c84SAndroid Build Coastguard Worker
draw_status(void)103*cf5a6c84SAndroid Build Coastguard Worker static void draw_status(void)
104*cf5a6c84SAndroid Build Coastguard Worker {
105*cf5a6c84SAndroid Build Coastguard Worker char line[80];
106*cf5a6c84SAndroid Build Coastguard Worker
107*cf5a6c84SAndroid Build Coastguard Worker printf("\e[%dH\e[K", TT.rows+1);
108*cf5a6c84SAndroid Build Coastguard Worker
109*cf5a6c84SAndroid Build Coastguard Worker snprintf(line, sizeof(line), "\"%s\"%s, %#llx/%#llx", *toys.optargs,
110*cf5a6c84SAndroid Build Coastguard Worker FLAG(r) ? " [readonly]" : "", TT.pos, TT.len);
111*cf5a6c84SAndroid Build Coastguard Worker draw_trim(line, -1, TT.cols);
112*cf5a6c84SAndroid Build Coastguard Worker }
113*cf5a6c84SAndroid Build Coastguard Worker
draw_byte(int byte)114*cf5a6c84SAndroid Build Coastguard Worker static void draw_byte(int byte)
115*cf5a6c84SAndroid Build Coastguard Worker {
116*cf5a6c84SAndroid Build Coastguard Worker if (byte) printf("%02x", byte);
117*cf5a6c84SAndroid Build Coastguard Worker else printf("\e[2m00\e[0m");
118*cf5a6c84SAndroid Build Coastguard Worker }
119*cf5a6c84SAndroid Build Coastguard Worker
draw_line(long long yy)120*cf5a6c84SAndroid Build Coastguard Worker static void draw_line(long long yy)
121*cf5a6c84SAndroid Build Coastguard Worker {
122*cf5a6c84SAndroid Build Coastguard Worker int x, xx = 16;
123*cf5a6c84SAndroid Build Coastguard Worker
124*cf5a6c84SAndroid Build Coastguard Worker yy = (TT.base+yy)*16;
125*cf5a6c84SAndroid Build Coastguard Worker if (yy+xx>=TT.len) xx = TT.len-yy;
126*cf5a6c84SAndroid Build Coastguard Worker
127*cf5a6c84SAndroid Build Coastguard Worker if (yy<TT.len) {
128*cf5a6c84SAndroid Build Coastguard Worker printf("\r\e[%dm%0*llx\e[0m ", 33*!TT.mode, TT.numlen, yy);
129*cf5a6c84SAndroid Build Coastguard Worker for (x=0; x<xx; x++) {
130*cf5a6c84SAndroid Build Coastguard Worker putchar(' ');
131*cf5a6c84SAndroid Build Coastguard Worker draw_byte(TT.data[yy+x]);
132*cf5a6c84SAndroid Build Coastguard Worker }
133*cf5a6c84SAndroid Build Coastguard Worker printf("%*s", 2+3*(16-xx), "");
134*cf5a6c84SAndroid Build Coastguard Worker for (x=0; x<xx; x++) draw_char(TT.data[yy+x]);
135*cf5a6c84SAndroid Build Coastguard Worker printf("%*s", 16-xx, "");
136*cf5a6c84SAndroid Build Coastguard Worker }
137*cf5a6c84SAndroid Build Coastguard Worker printf("\e[K");
138*cf5a6c84SAndroid Build Coastguard Worker }
139*cf5a6c84SAndroid Build Coastguard Worker
draw_page(void)140*cf5a6c84SAndroid Build Coastguard Worker static void draw_page(void)
141*cf5a6c84SAndroid Build Coastguard Worker {
142*cf5a6c84SAndroid Build Coastguard Worker int y;
143*cf5a6c84SAndroid Build Coastguard Worker
144*cf5a6c84SAndroid Build Coastguard Worker for (y = 0; y<TT.rows; y++) {
145*cf5a6c84SAndroid Build Coastguard Worker printf(y ? "\r\n" : "\e[H");
146*cf5a6c84SAndroid Build Coastguard Worker draw_line(y);
147*cf5a6c84SAndroid Build Coastguard Worker }
148*cf5a6c84SAndroid Build Coastguard Worker draw_status();
149*cf5a6c84SAndroid Build Coastguard Worker }
150*cf5a6c84SAndroid Build Coastguard Worker
151*cf5a6c84SAndroid Build Coastguard Worker // side: 0 = editing left, 1 = editing right, 2 = clear, 3 = read only
highlight(int xx,int yy,int side)152*cf5a6c84SAndroid Build Coastguard Worker static void highlight(int xx, int yy, int side)
153*cf5a6c84SAndroid Build Coastguard Worker {
154*cf5a6c84SAndroid Build Coastguard Worker char cc = TT.data[16*(TT.base+yy)+xx];
155*cf5a6c84SAndroid Build Coastguard Worker int i;
156*cf5a6c84SAndroid Build Coastguard Worker
157*cf5a6c84SAndroid Build Coastguard Worker // Display cursor in hex area.
158*cf5a6c84SAndroid Build Coastguard Worker printf("\e[%u;%uH\e[%dm", yy+1, TT.numlen+3*(xx+1), 7*(side!=2));
159*cf5a6c84SAndroid Build Coastguard Worker if (side>1) draw_byte(cc);
160*cf5a6c84SAndroid Build Coastguard Worker else for (i=0; i<2;) {
161*cf5a6c84SAndroid Build Coastguard Worker if (side==i) printf("\e[32m");
162*cf5a6c84SAndroid Build Coastguard Worker printf("%x", (cc>>(4*(1&++i)))&15);
163*cf5a6c84SAndroid Build Coastguard Worker }
164*cf5a6c84SAndroid Build Coastguard Worker
165*cf5a6c84SAndroid Build Coastguard Worker // Display cursor in text area.
166*cf5a6c84SAndroid Build Coastguard Worker printf("\e[7m\e[%u;%uH"+4*(side==2), yy+1, 1+TT.numlen+17*3+xx);
167*cf5a6c84SAndroid Build Coastguard Worker draw_char(cc);
168*cf5a6c84SAndroid Build Coastguard Worker }
169*cf5a6c84SAndroid Build Coastguard Worker
find_next(int pos)170*cf5a6c84SAndroid Build Coastguard Worker static void find_next(int pos)
171*cf5a6c84SAndroid Build Coastguard Worker {
172*cf5a6c84SAndroid Build Coastguard Worker char *p;
173*cf5a6c84SAndroid Build Coastguard Worker
174*cf5a6c84SAndroid Build Coastguard Worker p = memmem(TT.data+pos, TT.len-pos, TT.search, strlen(TT.search));
175*cf5a6c84SAndroid Build Coastguard Worker if (p) TT.pos = p - TT.data;
176*cf5a6c84SAndroid Build Coastguard Worker else show_error("No match!");
177*cf5a6c84SAndroid Build Coastguard Worker }
178*cf5a6c84SAndroid Build Coastguard Worker
find_prev(int pos)179*cf5a6c84SAndroid Build Coastguard Worker static void find_prev(int pos)
180*cf5a6c84SAndroid Build Coastguard Worker {
181*cf5a6c84SAndroid Build Coastguard Worker size_t len = strlen(TT.search);
182*cf5a6c84SAndroid Build Coastguard Worker
183*cf5a6c84SAndroid Build Coastguard Worker for (; pos >= 0; pos--) {
184*cf5a6c84SAndroid Build Coastguard Worker if (!smemcmp(TT.data+pos, TT.search, len)) {
185*cf5a6c84SAndroid Build Coastguard Worker TT.pos = pos;
186*cf5a6c84SAndroid Build Coastguard Worker return;
187*cf5a6c84SAndroid Build Coastguard Worker }
188*cf5a6c84SAndroid Build Coastguard Worker }
189*cf5a6c84SAndroid Build Coastguard Worker show_error("No match!");
190*cf5a6c84SAndroid Build Coastguard Worker }
191*cf5a6c84SAndroid Build Coastguard Worker
hexedit_main(void)192*cf5a6c84SAndroid Build Coastguard Worker void hexedit_main(void)
193*cf5a6c84SAndroid Build Coastguard Worker {
194*cf5a6c84SAndroid Build Coastguard Worker long long y;
195*cf5a6c84SAndroid Build Coastguard Worker int x, i, side = 0, key, fd;
196*cf5a6c84SAndroid Build Coastguard Worker
197*cf5a6c84SAndroid Build Coastguard Worker // Terminal setup
198*cf5a6c84SAndroid Build Coastguard Worker TT.cols = 80;
199*cf5a6c84SAndroid Build Coastguard Worker TT.rows = 24;
200*cf5a6c84SAndroid Build Coastguard Worker terminal_size(&TT.cols, &TT.rows);
201*cf5a6c84SAndroid Build Coastguard Worker if (TT.rows) TT.rows--;
202*cf5a6c84SAndroid Build Coastguard Worker xsignal(SIGWINCH, generic_signal);
203*cf5a6c84SAndroid Build Coastguard Worker sigatexit(tty_sigreset);
204*cf5a6c84SAndroid Build Coastguard Worker dprintf(1, "\e[0m\e[?25l");
205*cf5a6c84SAndroid Build Coastguard Worker xset_terminal(1, 1, 0, 0);
206*cf5a6c84SAndroid Build Coastguard Worker
207*cf5a6c84SAndroid Build Coastguard Worker if (access(*toys.optargs, W_OK)) toys.optflags |= FLAG_r;
208*cf5a6c84SAndroid Build Coastguard Worker fd = xopen(*toys.optargs, FLAG(r) ? O_RDONLY : O_RDWR);
209*cf5a6c84SAndroid Build Coastguard Worker if ((TT.len = fdlength(fd))<1) error_exit("bad length");
210*cf5a6c84SAndroid Build Coastguard Worker if (sizeof(long)==32 && TT.len>SIZE_MAX) TT.len = SIZE_MAX;
211*cf5a6c84SAndroid Build Coastguard Worker // count file length hex in digits, rounded up to multiple of 4
212*cf5a6c84SAndroid Build Coastguard Worker for (TT.pos = TT.len, TT.numlen = 0; TT.pos; TT.pos >>= 4, TT.numlen++);
213*cf5a6c84SAndroid Build Coastguard Worker TT.numlen += (4-TT.numlen)&3;
214*cf5a6c84SAndroid Build Coastguard Worker
215*cf5a6c84SAndroid Build Coastguard Worker TT.data=xmmap(0, TT.len, PROT_READ|(PROT_WRITE*!FLAG(r)), MAP_SHARED, fd, 0);
216*cf5a6c84SAndroid Build Coastguard Worker close(fd);
217*cf5a6c84SAndroid Build Coastguard Worker draw_page();
218*cf5a6c84SAndroid Build Coastguard Worker
219*cf5a6c84SAndroid Build Coastguard Worker for (;;) {
220*cf5a6c84SAndroid Build Coastguard Worker // Scroll display if necessary
221*cf5a6c84SAndroid Build Coastguard Worker if (TT.pos<0) TT.pos = 0;
222*cf5a6c84SAndroid Build Coastguard Worker if (TT.pos>=TT.len) TT.pos = TT.len-1;
223*cf5a6c84SAndroid Build Coastguard Worker x = TT.pos&15;
224*cf5a6c84SAndroid Build Coastguard Worker y = TT.pos/16;
225*cf5a6c84SAndroid Build Coastguard Worker
226*cf5a6c84SAndroid Build Coastguard Worker // scroll up
227*cf5a6c84SAndroid Build Coastguard Worker while (y<TT.base) {
228*cf5a6c84SAndroid Build Coastguard Worker if (TT.base-y>(TT.rows/2)) {
229*cf5a6c84SAndroid Build Coastguard Worker TT.base = y;
230*cf5a6c84SAndroid Build Coastguard Worker draw_page();
231*cf5a6c84SAndroid Build Coastguard Worker } else {
232*cf5a6c84SAndroid Build Coastguard Worker TT.base--;
233*cf5a6c84SAndroid Build Coastguard Worker printf("\e[H\e[1L");
234*cf5a6c84SAndroid Build Coastguard Worker draw_line(0);
235*cf5a6c84SAndroid Build Coastguard Worker }
236*cf5a6c84SAndroid Build Coastguard Worker }
237*cf5a6c84SAndroid Build Coastguard Worker
238*cf5a6c84SAndroid Build Coastguard Worker // scroll down
239*cf5a6c84SAndroid Build Coastguard Worker while (y>=TT.base+TT.rows) {
240*cf5a6c84SAndroid Build Coastguard Worker if (y-(TT.base+TT.rows)>(TT.rows/2)) {
241*cf5a6c84SAndroid Build Coastguard Worker TT.base = y-TT.rows-1;
242*cf5a6c84SAndroid Build Coastguard Worker draw_page();
243*cf5a6c84SAndroid Build Coastguard Worker } else {
244*cf5a6c84SAndroid Build Coastguard Worker TT.base++;
245*cf5a6c84SAndroid Build Coastguard Worker printf("\e[H\e[1M\e[%uH", TT.rows);
246*cf5a6c84SAndroid Build Coastguard Worker draw_line(TT.rows-1);
247*cf5a6c84SAndroid Build Coastguard Worker }
248*cf5a6c84SAndroid Build Coastguard Worker }
249*cf5a6c84SAndroid Build Coastguard Worker
250*cf5a6c84SAndroid Build Coastguard Worker draw_status();
251*cf5a6c84SAndroid Build Coastguard Worker y -= TT.base;
252*cf5a6c84SAndroid Build Coastguard Worker
253*cf5a6c84SAndroid Build Coastguard Worker // Display cursor and flush output
254*cf5a6c84SAndroid Build Coastguard Worker highlight(x, y, FLAG(r) ? 3 : side);
255*cf5a6c84SAndroid Build Coastguard Worker fflush(0);
256*cf5a6c84SAndroid Build Coastguard Worker
257*cf5a6c84SAndroid Build Coastguard Worker // Wait for next key
258*cf5a6c84SAndroid Build Coastguard Worker key = scan_key(TT.keybuf, -1);
259*cf5a6c84SAndroid Build Coastguard Worker
260*cf5a6c84SAndroid Build Coastguard Worker // Window resized?
261*cf5a6c84SAndroid Build Coastguard Worker if (key == -3) {
262*cf5a6c84SAndroid Build Coastguard Worker toys.signal = 0;
263*cf5a6c84SAndroid Build Coastguard Worker terminal_size(&TT.cols, &TT.rows);
264*cf5a6c84SAndroid Build Coastguard Worker if (TT.rows) TT.rows--;
265*cf5a6c84SAndroid Build Coastguard Worker draw_page();
266*cf5a6c84SAndroid Build Coastguard Worker continue;
267*cf5a6c84SAndroid Build Coastguard Worker }
268*cf5a6c84SAndroid Build Coastguard Worker
269*cf5a6c84SAndroid Build Coastguard Worker if (key == 'x') {
270*cf5a6c84SAndroid Build Coastguard Worker TT.mode = !TT.mode;
271*cf5a6c84SAndroid Build Coastguard Worker printf("\e[0m");
272*cf5a6c84SAndroid Build Coastguard Worker draw_page();
273*cf5a6c84SAndroid Build Coastguard Worker continue;
274*cf5a6c84SAndroid Build Coastguard Worker }
275*cf5a6c84SAndroid Build Coastguard Worker
276*cf5a6c84SAndroid Build Coastguard Worker // Various popular ways to quit...
277*cf5a6c84SAndroid Build Coastguard Worker if (key==-1||key==('C'-'@')||key==('Q'-'@')||key==27||key=='q') break;
278*cf5a6c84SAndroid Build Coastguard Worker highlight(x, y, 2);
279*cf5a6c84SAndroid Build Coastguard Worker
280*cf5a6c84SAndroid Build Coastguard Worker if (key == ('J'-'@') || key == ':' || key == '-' || key == '+') {
281*cf5a6c84SAndroid Build Coastguard Worker // Jump (relative or absolute)
282*cf5a6c84SAndroid Build Coastguard Worker char initial[2] = {}, *s = 0;
283*cf5a6c84SAndroid Build Coastguard Worker long long val;
284*cf5a6c84SAndroid Build Coastguard Worker
285*cf5a6c84SAndroid Build Coastguard Worker if (key == '-' || key == '+') *initial = key;
286*cf5a6c84SAndroid Build Coastguard Worker if (!prompt("Jump to", initial)) continue;
287*cf5a6c84SAndroid Build Coastguard Worker
288*cf5a6c84SAndroid Build Coastguard Worker val = estrtol(TT.input, &s, 0);
289*cf5a6c84SAndroid Build Coastguard Worker if (!errno && s && !*s) {
290*cf5a6c84SAndroid Build Coastguard Worker if (*TT.input == '-' || *TT.input == '+') TT.pos += val;
291*cf5a6c84SAndroid Build Coastguard Worker else TT.pos = val;
292*cf5a6c84SAndroid Build Coastguard Worker }
293*cf5a6c84SAndroid Build Coastguard Worker continue;
294*cf5a6c84SAndroid Build Coastguard Worker } else if (key == ('F'-'@') || key == '/') { // Find
295*cf5a6c84SAndroid Build Coastguard Worker if (!prompt("Find", TT.search ? TT.search : "")) continue;
296*cf5a6c84SAndroid Build Coastguard Worker
297*cf5a6c84SAndroid Build Coastguard Worker // TODO: parse hex escapes in input, and record length to support \0
298*cf5a6c84SAndroid Build Coastguard Worker free(TT.search);
299*cf5a6c84SAndroid Build Coastguard Worker TT.search = xstrdup(TT.input);
300*cf5a6c84SAndroid Build Coastguard Worker find_next(TT.pos);
301*cf5a6c84SAndroid Build Coastguard Worker } else if (TT.search && (key == ('G'-'@') || key == 'n')) { // Find next
302*cf5a6c84SAndroid Build Coastguard Worker if (TT.pos < TT.len) find_next(TT.pos+1);
303*cf5a6c84SAndroid Build Coastguard Worker } else if (TT.search && (key == ('D'-'@') || key == 'p')) { // Find previous
304*cf5a6c84SAndroid Build Coastguard Worker if (TT.pos > 0) find_prev(TT.pos-1);
305*cf5a6c84SAndroid Build Coastguard Worker }
306*cf5a6c84SAndroid Build Coastguard Worker
307*cf5a6c84SAndroid Build Coastguard Worker // Remove cursor
308*cf5a6c84SAndroid Build Coastguard Worker highlight(x, y, 2);
309*cf5a6c84SAndroid Build Coastguard Worker
310*cf5a6c84SAndroid Build Coastguard Worker // Hex digit?
311*cf5a6c84SAndroid Build Coastguard Worker if (key>='a' && key<='f') key-=32;
312*cf5a6c84SAndroid Build Coastguard Worker if (!FLAG(r) && ((key>='0' && key<='9') || (key>='A' && key<='F'))) {
313*cf5a6c84SAndroid Build Coastguard Worker if (!side) {
314*cf5a6c84SAndroid Build Coastguard Worker long long *ll = (long long *)toybuf;
315*cf5a6c84SAndroid Build Coastguard Worker
316*cf5a6c84SAndroid Build Coastguard Worker ll[TT.undo] = TT.pos;
317*cf5a6c84SAndroid Build Coastguard Worker toybuf[(sizeof(long long)*UNDO_LEN)+TT.undo++] = TT.data[TT.pos];
318*cf5a6c84SAndroid Build Coastguard Worker if (TT.undolen < UNDO_LEN) TT.undolen++;
319*cf5a6c84SAndroid Build Coastguard Worker TT.undo %= UNDO_LEN;
320*cf5a6c84SAndroid Build Coastguard Worker }
321*cf5a6c84SAndroid Build Coastguard Worker
322*cf5a6c84SAndroid Build Coastguard Worker i = key - '0';
323*cf5a6c84SAndroid Build Coastguard Worker if (i>9) i -= 7;
324*cf5a6c84SAndroid Build Coastguard Worker TT.data[TT.pos] &= 15<<(4*side);
325*cf5a6c84SAndroid Build Coastguard Worker TT.data[TT.pos] |= i<<(4*!side);
326*cf5a6c84SAndroid Build Coastguard Worker
327*cf5a6c84SAndroid Build Coastguard Worker if (++side==2) {
328*cf5a6c84SAndroid Build Coastguard Worker highlight(x, y, side);
329*cf5a6c84SAndroid Build Coastguard Worker side = 0;
330*cf5a6c84SAndroid Build Coastguard Worker ++TT.pos;
331*cf5a6c84SAndroid Build Coastguard Worker }
332*cf5a6c84SAndroid Build Coastguard Worker } else side = 0;
333*cf5a6c84SAndroid Build Coastguard Worker if (key=='u') {
334*cf5a6c84SAndroid Build Coastguard Worker if (TT.undolen) {
335*cf5a6c84SAndroid Build Coastguard Worker long long *ll = (long long *)toybuf;
336*cf5a6c84SAndroid Build Coastguard Worker
337*cf5a6c84SAndroid Build Coastguard Worker TT.undolen--;
338*cf5a6c84SAndroid Build Coastguard Worker if (!TT.undo) TT.undo = UNDO_LEN;
339*cf5a6c84SAndroid Build Coastguard Worker TT.pos = ll[--TT.undo];
340*cf5a6c84SAndroid Build Coastguard Worker TT.data[TT.pos] = toybuf[sizeof(long long)*UNDO_LEN+TT.undo];
341*cf5a6c84SAndroid Build Coastguard Worker }
342*cf5a6c84SAndroid Build Coastguard Worker }
343*cf5a6c84SAndroid Build Coastguard Worker if (key>=256) {
344*cf5a6c84SAndroid Build Coastguard Worker key -= 256;
345*cf5a6c84SAndroid Build Coastguard Worker
346*cf5a6c84SAndroid Build Coastguard Worker if (key==KEY_UP) TT.pos -= 16;
347*cf5a6c84SAndroid Build Coastguard Worker else if (key==KEY_DOWN) TT.pos += 16;
348*cf5a6c84SAndroid Build Coastguard Worker else if (key==KEY_RIGHT) {
349*cf5a6c84SAndroid Build Coastguard Worker if (TT.pos<TT.len) TT.pos++;
350*cf5a6c84SAndroid Build Coastguard Worker } else if (key==KEY_LEFT) {
351*cf5a6c84SAndroid Build Coastguard Worker if (TT.pos>0) TT.pos--;
352*cf5a6c84SAndroid Build Coastguard Worker } else if (key==KEY_PGUP) {
353*cf5a6c84SAndroid Build Coastguard Worker TT.pos -= 16*TT.rows;
354*cf5a6c84SAndroid Build Coastguard Worker if (TT.pos < 0) TT.pos = 0;
355*cf5a6c84SAndroid Build Coastguard Worker TT.base = TT.pos/16;
356*cf5a6c84SAndroid Build Coastguard Worker draw_page();
357*cf5a6c84SAndroid Build Coastguard Worker } else if (key==KEY_PGDN) {
358*cf5a6c84SAndroid Build Coastguard Worker TT.pos += 16*TT.rows;
359*cf5a6c84SAndroid Build Coastguard Worker if (TT.pos > TT.len-1) TT.pos = TT.len-1;
360*cf5a6c84SAndroid Build Coastguard Worker TT.base = TT.pos/16;
361*cf5a6c84SAndroid Build Coastguard Worker draw_page();
362*cf5a6c84SAndroid Build Coastguard Worker } else if (key==KEY_HOME) TT.pos &= ~0xf;
363*cf5a6c84SAndroid Build Coastguard Worker else if (key==KEY_END) TT.pos |= 0xf;
364*cf5a6c84SAndroid Build Coastguard Worker else if (key==(KEY_CTRL|KEY_HOME)) TT.pos = 0;
365*cf5a6c84SAndroid Build Coastguard Worker else if (key==(KEY_CTRL|KEY_END)) TT.pos = TT.len-1;
366*cf5a6c84SAndroid Build Coastguard Worker }
367*cf5a6c84SAndroid Build Coastguard Worker }
368*cf5a6c84SAndroid Build Coastguard Worker munmap(TT.data, TT.len);
369*cf5a6c84SAndroid Build Coastguard Worker tty_reset();
370*cf5a6c84SAndroid Build Coastguard Worker }
371