10f66f451Sopenharmony_ci/* hexedit.c - Hexadecimal file editor 20f66f451Sopenharmony_ci * 30f66f451Sopenharmony_ci * Copyright 2015 Rob Landley <rob@landley.net> 40f66f451Sopenharmony_ci * 50f66f451Sopenharmony_ci * No standard 60f66f451Sopenharmony_ci 70f66f451Sopenharmony_ciUSE_HEXEDIT(NEWTOY(hexedit, "<1>1r", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE)) 80f66f451Sopenharmony_ci 90f66f451Sopenharmony_ciconfig HEXEDIT 100f66f451Sopenharmony_ci bool "hexedit" 110f66f451Sopenharmony_ci default y 120f66f451Sopenharmony_ci help 130f66f451Sopenharmony_ci usage: hexedit FILENAME 140f66f451Sopenharmony_ci 150f66f451Sopenharmony_ci Hexadecimal file editor. All changes are written to disk immediately. 160f66f451Sopenharmony_ci 170f66f451Sopenharmony_ci -r Read only (display but don't edit) 180f66f451Sopenharmony_ci 190f66f451Sopenharmony_ci Keys: 200f66f451Sopenharmony_ci Arrows Move left/right/up/down by one line/column 210f66f451Sopenharmony_ci Pg Up/Pg Dn Move up/down by one page 220f66f451Sopenharmony_ci 0-9, a-f Change current half-byte to hexadecimal value 230f66f451Sopenharmony_ci u Undo 240f66f451Sopenharmony_ci q/^c/^d/<esc> Quit 250f66f451Sopenharmony_ci*/ 260f66f451Sopenharmony_ci 270f66f451Sopenharmony_ci#define FOR_hexedit 280f66f451Sopenharmony_ci#include "toys.h" 290f66f451Sopenharmony_ci 300f66f451Sopenharmony_ciGLOBALS( 310f66f451Sopenharmony_ci char *data; 320f66f451Sopenharmony_ci long long len, base; 330f66f451Sopenharmony_ci int numlen, undo, undolen; 340f66f451Sopenharmony_ci unsigned height; 350f66f451Sopenharmony_ci) 360f66f451Sopenharmony_ci 370f66f451Sopenharmony_ci#define UNDO_LEN (sizeof(toybuf)/(sizeof(long long)+1)) 380f66f451Sopenharmony_ci 390f66f451Sopenharmony_ci// Render all characters printable, using color to distinguish. 400f66f451Sopenharmony_cistatic int draw_char(FILE *fp, wchar_t broiled) 410f66f451Sopenharmony_ci{ 420f66f451Sopenharmony_ci if (fp) { 430f66f451Sopenharmony_ci if (broiled<32 || broiled>=127) { 440f66f451Sopenharmony_ci if (broiled>127) { 450f66f451Sopenharmony_ci tty_esc("2m"); 460f66f451Sopenharmony_ci broiled &= 127; 470f66f451Sopenharmony_ci } 480f66f451Sopenharmony_ci if (broiled<32 || broiled==127) { 490f66f451Sopenharmony_ci tty_esc("7m"); 500f66f451Sopenharmony_ci if (broiled==127) broiled = 32; 510f66f451Sopenharmony_ci else broiled += 64; 520f66f451Sopenharmony_ci } 530f66f451Sopenharmony_ci printf("%c", (int)broiled); 540f66f451Sopenharmony_ci tty_esc("0m"); 550f66f451Sopenharmony_ci } else printf("%c", (int)broiled); 560f66f451Sopenharmony_ci } 570f66f451Sopenharmony_ci 580f66f451Sopenharmony_ci return 1; 590f66f451Sopenharmony_ci} 600f66f451Sopenharmony_ci 610f66f451Sopenharmony_cistatic void draw_tail(void) 620f66f451Sopenharmony_ci{ 630f66f451Sopenharmony_ci tty_jump(0, TT.height); 640f66f451Sopenharmony_ci tty_esc("K"); 650f66f451Sopenharmony_ci 660f66f451Sopenharmony_ci draw_trim(*toys.optargs, -1, 71); 670f66f451Sopenharmony_ci} 680f66f451Sopenharmony_ci 690f66f451Sopenharmony_cistatic void draw_line(long long yy) 700f66f451Sopenharmony_ci{ 710f66f451Sopenharmony_ci int x, xx = 16; 720f66f451Sopenharmony_ci 730f66f451Sopenharmony_ci yy = (TT.base+yy)*16; 740f66f451Sopenharmony_ci if (yy+xx>=TT.len) xx = TT.len-yy; 750f66f451Sopenharmony_ci 760f66f451Sopenharmony_ci if (yy<TT.len) { 770f66f451Sopenharmony_ci printf("\r%0*llX ", TT.numlen, yy); 780f66f451Sopenharmony_ci for (x=0; x<xx; x++) printf(" %02X", TT.data[yy+x]); 790f66f451Sopenharmony_ci printf("%*s", 2+3*(16-xx), ""); 800f66f451Sopenharmony_ci for (x=0; x<xx; x++) draw_char(stdout, TT.data[yy+x]); 810f66f451Sopenharmony_ci printf("%*s", 16-xx, ""); 820f66f451Sopenharmony_ci } 830f66f451Sopenharmony_ci tty_esc("K"); 840f66f451Sopenharmony_ci} 850f66f451Sopenharmony_ci 860f66f451Sopenharmony_cistatic void draw_page(void) 870f66f451Sopenharmony_ci{ 880f66f451Sopenharmony_ci int y; 890f66f451Sopenharmony_ci 900f66f451Sopenharmony_ci tty_jump(0, 0); 910f66f451Sopenharmony_ci for (y = 0; y<TT.height; y++) { 920f66f451Sopenharmony_ci if (y) printf("\r\n"); 930f66f451Sopenharmony_ci draw_line(y); 940f66f451Sopenharmony_ci } 950f66f451Sopenharmony_ci draw_tail(); 960f66f451Sopenharmony_ci} 970f66f451Sopenharmony_ci 980f66f451Sopenharmony_ci// side: 0 = editing left, 1 = editing right, 2 = clear, 3 = read only 990f66f451Sopenharmony_cistatic void highlight(int xx, int yy, int side) 1000f66f451Sopenharmony_ci{ 1010f66f451Sopenharmony_ci char cc = TT.data[16*(TT.base+yy)+xx]; 1020f66f451Sopenharmony_ci int i; 1030f66f451Sopenharmony_ci 1040f66f451Sopenharmony_ci // Display cursor 1050f66f451Sopenharmony_ci tty_jump(2+TT.numlen+3*xx, yy); 1060f66f451Sopenharmony_ci tty_esc("0m"); 1070f66f451Sopenharmony_ci if (side!=2) tty_esc("7m"); 1080f66f451Sopenharmony_ci if (side>1) printf("%02X", cc); 1090f66f451Sopenharmony_ci else for (i=0; i<2;) { 1100f66f451Sopenharmony_ci if (side==i) tty_esc("32m"); 1110f66f451Sopenharmony_ci printf("%X", (cc>>(4*(1&++i)))&15); 1120f66f451Sopenharmony_ci } 1130f66f451Sopenharmony_ci tty_esc("0m"); 1140f66f451Sopenharmony_ci tty_jump(TT.numlen+17*3+xx, yy); 1150f66f451Sopenharmony_ci draw_char(stdout, cc); 1160f66f451Sopenharmony_ci} 1170f66f451Sopenharmony_ci 1180f66f451Sopenharmony_civoid hexedit_main(void) 1190f66f451Sopenharmony_ci{ 1200f66f451Sopenharmony_ci long long pos = 0, y; 1210f66f451Sopenharmony_ci int x, i, side = 0, key, ro = toys.optflags&FLAG_r, 1220f66f451Sopenharmony_ci fd = xopen(*toys.optargs, ro ? O_RDONLY : O_RDWR); 1230f66f451Sopenharmony_ci char keybuf[16]; 1240f66f451Sopenharmony_ci 1250f66f451Sopenharmony_ci *keybuf = 0; 1260f66f451Sopenharmony_ci 1270f66f451Sopenharmony_ci // Terminal setup 1280f66f451Sopenharmony_ci TT.height = 25; 1290f66f451Sopenharmony_ci terminal_size(0, &TT.height); 1300f66f451Sopenharmony_ci if (TT.height) TT.height--; 1310f66f451Sopenharmony_ci sigatexit(tty_sigreset); 1320f66f451Sopenharmony_ci tty_esc("0m"); 1330f66f451Sopenharmony_ci tty_esc("?25l"); 1340f66f451Sopenharmony_ci fflush(0); 1350f66f451Sopenharmony_ci xset_terminal(1, 1, 0, 0); 1360f66f451Sopenharmony_ci 1370f66f451Sopenharmony_ci if ((TT.len = fdlength(fd))<1) error_exit("bad length"); 1380f66f451Sopenharmony_ci if (sizeof(long)==32 && TT.len>SIZE_MAX) TT.len = SIZE_MAX; 1390f66f451Sopenharmony_ci // count file length hex in digits, rounded up to multiple of 4 1400f66f451Sopenharmony_ci for (pos = TT.len, TT.numlen = 0; pos; pos >>= 4, TT.numlen++); 1410f66f451Sopenharmony_ci TT.numlen += (4-TT.numlen)&3; 1420f66f451Sopenharmony_ci 1430f66f451Sopenharmony_ci TT.data = xmmap(0, TT.len, PROT_READ|(PROT_WRITE*!ro), MAP_SHARED, fd, 0); 1440f66f451Sopenharmony_ci draw_page(); 1450f66f451Sopenharmony_ci 1460f66f451Sopenharmony_ci for (;;) { 1470f66f451Sopenharmony_ci // Scroll display if necessary 1480f66f451Sopenharmony_ci if (pos<0) pos = 0; 1490f66f451Sopenharmony_ci if (pos>=TT.len) pos = TT.len-1; 1500f66f451Sopenharmony_ci x = pos&15; 1510f66f451Sopenharmony_ci y = pos/16; 1520f66f451Sopenharmony_ci 1530f66f451Sopenharmony_ci i = 0; 1540f66f451Sopenharmony_ci while (y<TT.base) { 1550f66f451Sopenharmony_ci if (TT.base-y>(TT.height/2)) { 1560f66f451Sopenharmony_ci TT.base = y; 1570f66f451Sopenharmony_ci draw_page(); 1580f66f451Sopenharmony_ci } else { 1590f66f451Sopenharmony_ci TT.base--; 1600f66f451Sopenharmony_ci i++; 1610f66f451Sopenharmony_ci tty_jump(0, 0); 1620f66f451Sopenharmony_ci tty_esc("1L"); 1630f66f451Sopenharmony_ci draw_line(0); 1640f66f451Sopenharmony_ci } 1650f66f451Sopenharmony_ci } 1660f66f451Sopenharmony_ci while (y>=TT.base+TT.height) { 1670f66f451Sopenharmony_ci if (y-(TT.base+TT.height)>(TT.height/2)) { 1680f66f451Sopenharmony_ci TT.base = y-TT.height-1; 1690f66f451Sopenharmony_ci draw_page(); 1700f66f451Sopenharmony_ci } else { 1710f66f451Sopenharmony_ci TT.base++; 1720f66f451Sopenharmony_ci i++; 1730f66f451Sopenharmony_ci tty_jump(0, 0); 1740f66f451Sopenharmony_ci tty_esc("1M"); 1750f66f451Sopenharmony_ci tty_jump(0, TT.height-1); 1760f66f451Sopenharmony_ci draw_line(TT.height-1); 1770f66f451Sopenharmony_ci } 1780f66f451Sopenharmony_ci } 1790f66f451Sopenharmony_ci if (i) draw_tail(); 1800f66f451Sopenharmony_ci y -= TT.base; 1810f66f451Sopenharmony_ci 1820f66f451Sopenharmony_ci // Display cursor and flush output 1830f66f451Sopenharmony_ci highlight(x, y, ro ? 3 : side); 1840f66f451Sopenharmony_ci xflush(1); 1850f66f451Sopenharmony_ci 1860f66f451Sopenharmony_ci // Wait for next key 1870f66f451Sopenharmony_ci key = scan_key(keybuf, -1); 1880f66f451Sopenharmony_ci // Exit for q, ctrl-c, ctrl-d, escape, or EOF 1890f66f451Sopenharmony_ci if (key==-1 || key==3 || key==4 || key==27 || key=='q') break; 1900f66f451Sopenharmony_ci highlight(x, y, 2); 1910f66f451Sopenharmony_ci 1920f66f451Sopenharmony_ci // Hex digit? 1930f66f451Sopenharmony_ci if (key>='a' && key<='f') key-=32; 1940f66f451Sopenharmony_ci if (!ro && ((key>='0' && key<='9') || (key>='A' && key<='F'))) { 1950f66f451Sopenharmony_ci if (!side) { 1960f66f451Sopenharmony_ci long long *ll = (long long *)toybuf; 1970f66f451Sopenharmony_ci 1980f66f451Sopenharmony_ci ll[TT.undo] = pos; 1990f66f451Sopenharmony_ci toybuf[(sizeof(long long)*UNDO_LEN)+TT.undo++] = TT.data[pos]; 2000f66f451Sopenharmony_ci if (TT.undolen < UNDO_LEN) TT.undolen++; 2010f66f451Sopenharmony_ci TT.undo %= UNDO_LEN; 2020f66f451Sopenharmony_ci } 2030f66f451Sopenharmony_ci 2040f66f451Sopenharmony_ci i = key - '0'; 2050f66f451Sopenharmony_ci if (i>9) i -= 7; 2060f66f451Sopenharmony_ci TT.data[pos] &= 15<<(4*side); 2070f66f451Sopenharmony_ci TT.data[pos] |= i<<(4*!side); 2080f66f451Sopenharmony_ci 2090f66f451Sopenharmony_ci if (++side==2) { 2100f66f451Sopenharmony_ci highlight(x, y, side); 2110f66f451Sopenharmony_ci side = 0; 2120f66f451Sopenharmony_ci ++pos; 2130f66f451Sopenharmony_ci } 2140f66f451Sopenharmony_ci } else side = 0; 2150f66f451Sopenharmony_ci if (key=='u') { 2160f66f451Sopenharmony_ci if (TT.undolen) { 2170f66f451Sopenharmony_ci long long *ll = (long long *)toybuf; 2180f66f451Sopenharmony_ci 2190f66f451Sopenharmony_ci TT.undolen--; 2200f66f451Sopenharmony_ci if (!TT.undo) TT.undo = UNDO_LEN; 2210f66f451Sopenharmony_ci pos = ll[--TT.undo]; 2220f66f451Sopenharmony_ci TT.data[pos] = toybuf[sizeof(long long)*UNDO_LEN+TT.undo]; 2230f66f451Sopenharmony_ci } 2240f66f451Sopenharmony_ci } 2250f66f451Sopenharmony_ci if (key>=256) { 2260f66f451Sopenharmony_ci key -= 256; 2270f66f451Sopenharmony_ci 2280f66f451Sopenharmony_ci if (key==KEY_UP) pos -= 16; 2290f66f451Sopenharmony_ci else if (key==KEY_DOWN) pos += 16; 2300f66f451Sopenharmony_ci else if (key==KEY_RIGHT) { 2310f66f451Sopenharmony_ci if (x<15) pos++; 2320f66f451Sopenharmony_ci } else if (key==KEY_LEFT) { 2330f66f451Sopenharmony_ci if (x) pos--; 2340f66f451Sopenharmony_ci } else if (key==KEY_PGUP) pos -= 16*TT.height; 2350f66f451Sopenharmony_ci else if (key==KEY_PGDN) pos += 16*TT.height; 2360f66f451Sopenharmony_ci else if (key==KEY_HOME) pos = 0; 2370f66f451Sopenharmony_ci else if (key==KEY_END) pos = TT.len-1; 2380f66f451Sopenharmony_ci } 2390f66f451Sopenharmony_ci } 2400f66f451Sopenharmony_ci munmap(TT.data, TT.len); 2410f66f451Sopenharmony_ci close(fd); 2420f66f451Sopenharmony_ci tty_reset(); 2430f66f451Sopenharmony_ci} 244