Путь: Toys/Other, команды версии: Ver.4 Ver.9 hexedit Комментарии в файле hexedit.c :
Исходный текст в файле hexedit.c #define FOR_hexedit #include "toys.h" GLOBALS( char *data, *search, keybuf[16], input[80]; long long len, base, pos; int numlen, undo, undolen, mode; unsigned rows, cols; ) #define UNDO_LEN (sizeof(toybuf)/(sizeof(long long)+1)) static void show_error(char *what) { printf("\e[%dH\e[41m\e[37m\e[K\e[1m%s\e[0m", TT.rows+1, what); xflush(1); msleep(500); } // TODO: support arrow keys, insertion, and scrolling (and reuse in vi) static int prompt(char *prompt, char *initial_value) { int yes = 0, key, len = strlen(initial_value); strcpy(TT.input, initial_value); while (1) { printf("\e[%dH\e[K\e[1m%s: \e[0m%s\e[?25h", TT.rows+1, prompt, TT.input); xflush(1); key = scan_key(TT.keybuf, -1); if (key < 0 || key == 27) break; if (key == '\r') { yes = len; // Hitting enter with no input counts as cancellation. break; } if (key == 0x7f && (len > 0)) TT.input[--len] = 0; else if (key == 'U'-'@') while (len > 0) TT.input[--len] = 0; else if (key >= ' ' && key < 0x7f && len < sizeof(TT.input)) TT.input[len++] = key; } printf("\e[?25l"); return yes; } // Render all characters printable, using color to distinguish. static void draw_char(int ch) { if (ch >= ' ' && ch < 0x7f) { putchar(ch); return; } if (TT.mode) { if (ch>127) { printf("\e[2m"); ch &= 127; } if (ch<32 || ch==127) { printf("\e[7m"); if (ch==127) ch = 32; else ch += 64; } xputc(ch); } else { if (ch < ' ') printf("\e[31m%c", ch + '@'); else printf("\e[35m?"); } printf("\e[0m"); } static void draw_status(void) { char line[80]; printf("\e[%dH\e[K", TT.rows+1); snprintf(line, sizeof(line), "\"%s\"%s, %#llx/%#llx", *toys.optargs, FLAG(r) ? " [readonly]" : "", TT.pos, TT.len); draw_trim(line, -1, TT.cols); } static void draw_byte(int byte) { if (byte) printf("%02x", byte); else printf("\e[2m00\e[0m"); } static void draw_line(long long yy) { int x, xx = 16; yy = (TT.base+yy)*16; if (yy+xx>=TT.len) xx = TT.len-yy; if (yy<TT.len) { printf("\r\e[%dm%0*llx\e[0m ", 33*!TT.mode, TT.numlen, yy); for (x=0; x<xx; x++) { putchar(' '); draw_byte(TT.data[yy+x]); } printf("%*s", 2+3*(16-xx), ""); for (x=0; x<xx; x++) draw_char(TT.data[yy+x]); printf("%*s", 16-xx, ""); } printf("\e[K"); } static void draw_page(void) { int y; for (y = 0; y<TT.rows; y++) { printf(y ? "\r\n" : "\e[H"); draw_line(y); } draw_status(); } // side: 0 = editing left, 1 = editing right, 2 = clear, 3 = read only static void highlight(int xx, int yy, int side) { char cc = TT.data[16*(TT.base+yy)+xx]; int i; // Display cursor in hex area. printf("\e[%u;%uH\e[%dm", yy+1, TT.numlen+3*(xx+1), 7*(side!=2)); if (side>1) draw_byte(cc); else for (i=0; i<2;) { if (side==i) printf("\e[32m"); printf("%x", (cc>>(4*(1&++i)))&15); } // Display cursor in text area. printf("\e[7m\e[%u;%uH"+4*(side==2), yy+1, 1+TT.numlen+17*3+xx); draw_char(cc); } static void find_next(int pos) { char *p; p = memmem(TT.data+pos, TT.len-pos, TT.search, strlen(TT.search)); if (p) TT.pos = p - TT.data; else show_error("No match!"); } static void find_prev(int pos) { size_t len = strlen(TT.search); for (; pos >= 0; pos--) { if (!smemcmp(TT.data+pos, TT.search, len)) { TT.pos = pos; return; } } show_error("No match!"); } void hexedit_main(void) { long long y; int x, i, side = 0, key, fd; // Terminal setup TT.cols = 80; TT.rows = 24; terminal_size(&TT.cols, &TT.rows); if (TT.rows) TT.rows--; xsignal(SIGWINCH, generic_signal); sigatexit(tty_sigreset); dprintf(1, "\e[0m\e[?25l"); xset_terminal(1, 1, 0, 0); if (access(*toys.optargs, W_OK)) toys.optflags |= FLAG_r; fd = xopen(*toys.optargs, FLAG(r) ? O_RDONLY : O_RDWR); if ((TT.len = fdlength(fd))<1) error_exit("bad length"); if (sizeof(long)==32 && TT.len>SIZE_MAX) TT.len = SIZE_MAX; // count file length hex in digits, rounded up to multiple of 4 for (TT.pos = TT.len, TT.numlen = 0; TT.pos; TT.pos >>= 4, TT.numlen++); TT.numlen += (4-TT.numlen)&3; TT.data=xmmap(0, TT.len, PROT_READ|(PROT_WRITE*!FLAG(r)), MAP_SHARED, fd, 0); close(fd); draw_page(); for (;;) { // Scroll display if necessary if (TT.pos<0) TT.pos = 0; if (TT.pos>=TT.len) TT.pos = TT.len-1; x = TT.pos&15; y = TT.pos/16; // scroll up while (y<TT.base) { if (TT.base-y>(TT.rows/2)) { TT.base = y; draw_page(); } else { TT.base--; printf("\e[H\e[1L"); draw_line(0); } } // scroll down while (y>=TT.base+TT.rows) { if (y-(TT.base+TT.rows)>(TT.rows/2)) { TT.base = y-TT.rows-1; draw_page(); } else { TT.base++; printf("\e[H\e[1M\e[%uH", TT.rows); draw_line(TT.rows-1); } } draw_status(); y -= TT.base; // Display cursor and flush output highlight(x, y, FLAG(r) ? 3 : side); xflush(1); // Wait for next key key = scan_key(TT.keybuf, -1); // Window resized? if (key == -3) { toys.signal = 0; terminal_size(&TT.cols, &TT.rows); if (TT.rows) TT.rows--; draw_page(); continue; } if (key == 'x') { TT.mode = !TT.mode; printf("\e[0m"); draw_page(); continue; } // Various popular ways to quit... if (key==-1||key==('C'-'@')||key==('Q'-'@')||key==27||key=='q') break; highlight(x, y, 2); if (key == ('J'-'@') || key == ':' || key == '-' || key == '+') { // Jump (relative or absolute) char initial[2] = {}, *s = 0; long long val; if (key == '-' || key == '+') *initial = key; if (!prompt("Jump to", initial)) continue; val = estrtol(TT.input, &s, 0); if (!errno && s && !*s) { if (*TT.input == '-' || *TT.input == '+') TT.pos += val; else TT.pos = val; } continue; } else if (key == ('F'-'@') || key == '/') { // Find if (!prompt("Find", TT.search ? TT.search : "")) continue; // TODO: parse hex escapes in input, and record length to support \0 free(TT.search); TT.search = xstrdup(TT.input); find_next(TT.pos); } else if (TT.search && (key == ('G'-'@') || key == 'n')) { // Find next if (TT.pos < TT.len) find_next(TT.pos+1); } else if (TT.search && (key == ('D'-'@') || key == 'p')) { // Find previous if (TT.pos > 0) find_prev(TT.pos-1); } // Remove cursor highlight(x, y, 2); // Hex digit? if (key>='a' && key<='f') key-=32; if (!FLAG(r) && ((key>='0' && key<='9') || (key>='A' && key<='F'))) { if (!side) { long long *ll = (long long *)toybuf; ll[TT.undo] = TT.pos; toybuf[(sizeof(long long)*UNDO_LEN)+TT.undo++] = TT.data[TT.pos]; if (TT.undolen < UNDO_LEN) TT.undolen++; TT.undo %= UNDO_LEN; } i = key - '0'; if (i>9) i -= 7; TT.data[TT.pos] &= 15<<(4*side); TT.data[TT.pos] |= i<<(4*!side); if (++side==2) { highlight(x, y, side); side = 0; ++TT.pos; } } else side = 0; if (key=='u') { if (TT.undolen) { long long *ll = (long long *)toybuf; TT.undolen--; if (!TT.undo) TT.undo = UNDO_LEN; TT.pos = ll[--TT.undo]; TT.data[TT.pos] = toybuf[sizeof(long long)*UNDO_LEN+TT.undo]; } } if (key>=256) { key -= 256; if (key==KEY_UP) TT.pos -= 16; else if (key==KEY_DOWN) TT.pos += 16; else if (key==KEY_RIGHT) { if (TT.pos<TT.len) TT.pos++; } else if (key==KEY_LEFT) { if (TT.pos>0) TT.pos--; } else if (key==KEY_PGUP) { TT.pos -= 16*TT.rows; if (TT.pos < 0) TT.pos = 0; TT.base = TT.pos/16; draw_page(); } else if (key==KEY_PGDN) { TT.pos += 16*TT.rows; if (TT.pos > TT.len-1) TT.pos = TT.len-1; TT.base = TT.pos/16; draw_page(); } else if (key==KEY_HOME) TT.pos &= ~0xf; else if (key==KEY_END) TT.pos |= 0xf; else if (key==(KEY_CTRL|KEY_HOME)) TT.pos = 0; else if (key==(KEY_CTRL|KEY_END)) TT.pos = TT.len-1; } } munmap(TT.data, TT.len); tty_reset(); } |