OSDN Git Service

am 7e612cb8: (-s ours) am f41c7e0b: Merge remote-tracking branch \'toybox/master...
[android-x86/external-toybox.git] / toys / other / hexedit.c
1 /* hexedit.c - Hexadecimal file editor
2  *
3  * Copyright 2015 Rob Landley <rob@landley.net>
4  *
5  * No standard
6
7 USE_HEXEDIT(NEWTOY(hexedit, "<1>1r", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE))
8
9 config HEXEDIT
10   bool "hexedit"
11   default y
12   help
13     usage: hexedit FILENAME
14
15     Hexadecimal file editor.
16
17     -r  Read only (display but don't edit)
18 */
19
20 #define FOR_hexedit
21 #include "toys.h"
22
23 GLOBALS(
24   char *data;
25   long long len, base;
26   int numlen, undo, undolen;
27   unsigned height;
28 )
29
30 #define UNDO_LEN (sizeof(toybuf)/(sizeof(long long)+1))
31
32 // Render all characters printable, using color to distinguish.
33 static void draw_char(char broiled)
34 {
35   if (broiled<32 || broiled>=127) {
36     if (broiled>127) {
37       tty_esc("2m");
38       broiled &= 127;
39     }
40     if (broiled<32 || broiled==127) {
41       tty_esc("7m");
42       if (broiled==127) broiled = 32;
43       else broiled += 64;
44     }
45     printf("%c", broiled);
46     tty_esc("0m");
47   } else printf("%c", broiled);
48 }
49
50 static void draw_tail(void)
51 {
52   int i = 0, width = 0, w, len;
53   char *start = *toys.optargs, *end;
54
55   tty_jump(0, TT.height);
56   tty_esc("K");
57
58   // First time, make sure we fit in 71 chars (advancing start as necessary).
59   // Second time, print from start to end, escaping nonprintable chars.
60   for (i=0; i<2; i++) {
61     for (end = start; *end;) {
62       wchar_t wc;
63
64       len = mbrtowc(&wc, end, 99, 0);
65       if (len<0 || wc<32 || (w = wcwidth(wc))<0) {
66         len = w = 1;
67         if (i) draw_char(*end);
68       } else if (i) fwrite(end, len, 1, stdout);
69       end += len;
70
71       if (!i) {
72         width += w;
73         while (width > 71) {
74           len = mbrtowc(&wc, start, 99, 0);
75           if (len<0 || wc<32 || (w = wcwidth(wc))<0) len = w = 1;
76           width -= w;
77           start += len;
78         }
79       }
80     }
81   }
82 }
83
84 static void draw_line(long long yy)
85 {
86   int x, xx = 16;
87
88   yy = (TT.base+yy)*16;
89   if (yy+xx>=TT.len) xx = TT.len-yy;
90
91   if (yy<TT.len) {
92     printf("\r%0*llX ", TT.numlen, yy);
93     for (x=0; x<xx; x++) printf(" %02X", TT.data[yy+x]);
94     printf("%*s", 2+3*(16-xx), "");
95     for (x=0; x<xx; x++) draw_char(TT.data[yy+x]);
96     printf("%*s", 16-xx, "");
97   }
98   tty_esc("K");
99 }
100
101 static void draw_page(void)
102 {
103   int y;
104
105   tty_jump(0, 0);
106   for (y = 0; y<TT.height; y++) {
107     if (y) printf("\r\n");
108     draw_line(y);
109   }
110   draw_tail();
111 }
112
113 // side: 0 = editing left, 1 = editing right, 2 = clear, 3 = read only
114 static void highlight(int xx, int yy, int side)
115 {
116   char cc = TT.data[16*(TT.base+yy)+xx];
117   int i;
118
119   // Display cursor
120   tty_jump(2+TT.numlen+3*xx, yy);
121   tty_esc("0m");
122   if (side!=2) tty_esc("7m");
123   if (side>1) printf("%02X", cc);
124   else for (i=0; i<2;) {
125     if (side==i) tty_esc("32m");
126     printf("%X", (cc>>(4*(1&++i)))&15);
127   }
128   tty_esc("0m");
129   tty_jump(TT.numlen+17*3+xx, yy);
130   draw_char(cc);
131 }
132
133 void hexedit_main(void)
134 {
135   long long pos = 0, y;
136   int x, i, side = 0, key, ro = toys.optflags&FLAG_r,
137       fd = xopen(*toys.optargs, ro ? O_RDONLY : O_RDWR);
138   char keybuf[16];
139
140   *keybuf = 0;
141
142   // Terminal setup
143   TT.height = 25;
144   terminal_size(0, &TT.height);
145   if (TT.height) TT.height--;
146   sigatexit(tty_sigreset);
147   tty_esc("0m");
148   tty_esc("?25l");
149   fflush(0);
150   set_terminal(1, 1, 0);
151
152   if ((TT.len = fdlength(fd))<0) error_exit("bad length");
153   if (sizeof(long)==32 && TT.len>SIZE_MAX) TT.len = SIZE_MAX;
154   // count file length hex in digits, rounded up to multiple of 4
155   for (pos = TT.len, TT.numlen = 0; pos; pos >>= 4, TT.numlen++);
156   TT.numlen += (4-TT.numlen)&3;
157
158   TT.data = mmap(0, TT.len, PROT_READ|(PROT_WRITE*!ro), MAP_SHARED, fd, 0);
159   draw_page();
160
161   for (;;) {
162     // Scroll display if necessary
163     if (pos<0) pos = 0;
164     if (pos>TT.len) pos = TT.len-1;
165     x = pos&15;
166     y = pos/16;
167
168     i = 0;
169     while (y<TT.base) {
170       if (TT.base-y>(TT.height/2)) {
171         TT.base = y;
172         draw_page();
173       } else {
174         TT.base--;
175         i++;
176         tty_esc("1T");
177         tty_jump(0, 0);
178         draw_line(0);
179       }
180     }
181     while (y>=TT.base+TT.height) {
182       if (y-(TT.base+TT.height)>(TT.height/2)) {
183         TT.base = y-TT.height-1;
184         draw_page();
185       } else {
186         TT.base++;
187         i++;
188         tty_esc("1S");
189         tty_jump(0, TT.height-1);
190         draw_line(TT.height-1);
191       }
192     }
193     if (i) draw_tail();
194     y -= TT.base;
195
196     // Display cursor and flush output
197     highlight(x, y, ro ? 3 : side);
198     xprintf("");
199
200     // Wait for next key
201     key = scan_key(keybuf, 1);
202     // Exit for q, ctrl-c, ctrl-d, escape, or EOF
203     if (key==-1 || key==3 || key==4 || key==27 || key=='q') break;
204     highlight(x, y, 2);
205
206     // Hex digit?
207     if (key>='a' && key<='f') key-=32;
208     if (!ro && ((key>='0' && key<='9') || (key>='A' && key<='F'))) {
209       if (!side) {
210         long long *ll = (long long *)toybuf;
211
212         ll[TT.undo] = pos;
213         toybuf[(sizeof(long long)*UNDO_LEN)+TT.undo++] = TT.data[pos];
214         if (TT.undolen < UNDO_LEN) TT.undolen++;
215         TT.undo %= UNDO_LEN;
216       }
217
218       i = key - '0';
219       if (i>9) i -= 7;
220       TT.data[pos] &= 15<<(4*side);
221       TT.data[pos] |= i<<(4*!side);
222
223       if (++side==2) {
224         highlight(x, y, side);
225         side = 0;
226         ++pos;
227       }
228     } else side = 0;
229     if (key=='u') {
230       if (TT.undolen) {
231         long long *ll = (long long *)toybuf;
232
233         TT.undolen--;
234         if (!TT.undo) TT.undo = UNDO_LEN;
235         pos = ll[--TT.undo];
236         TT.data[pos] = toybuf[sizeof(long long)*UNDO_LEN+TT.undo];
237       }
238     } else if (key==KEY_UP) pos -= 16;
239     else if (key==KEY_DOWN) pos += 16;
240     else if (key==KEY_RIGHT) {
241       if (x<15) pos++;
242     } else if (key==KEY_LEFT) {
243       if (x) pos--;
244     } else if (key==KEY_PGUP) pos -= 16*TT.height;
245     else if (key==KEY_PGDN) pos += 16*TT.height;
246     else if (key==KEY_HOME) pos = 0;
247     else if (key==KEY_END) pos = TT.len-1;
248   }
249   munmap(TT.data, TT.len);
250   close(fd);
251   tty_reset();
252 }