📄 heme.c
字号:
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <ctype.h>#include <unistd.h>#include <errno.h>#include <fcntl.h>#include <signal.h>#include <sys/stat.h>#include <pwd.h>#include <curses.h>#ifdef HAVE_MMAP# include <sys/mman.h>#endif#include "xmalloc.h"#include "pconfig.h"#include "pgetopt.h"/* this determines maximum memory that will be * available for blocks (default: 2 MB) */#define MAX_BLOCKMEM (2 * (1 << 20)) static const char heme_version[] = "0.4.2";typedef unsigned char byte;static WINDOW *w_main, *w_offset, *w_ascii;static int max_lines;static off_t cur_offset, beg_offset, end_offset, file_size;static int mode_ascii;static int fd;static int file_saved = 1;static int cur_line;static char *file_name;static char *prog_name;static const char *config_file;static int current_nibble;static int last_search = -1; /* 0 = byte search, 1 = string search */static int last_byte_search = 0;static char *last_string_search = NULL;static off_t off_len;static int bpl; /* bytes per line *//* undo struct used by put_byte */typedef struct { off_t offset1, offset2; byte b; byte *fill_bytes;}undo_t;static undo_t *undo_list;static int undo_count;typedef struct { byte *buf; off_t off_start, off_len; int modified;}blist_t;static blist_t *blist;static int blist_count;static int blist_current; /* index of current block */static int use_colors = -1;static void save_undo_info(off_t offset, byte b);static int make_backup_files = -1;static int advance_after_edit = -1;static void advance_right();/* reads file into buf, max n bytes, returns 0 on EOF, < 0 on error */static ssize_t read_file(int fds, byte *buf, size_t n){ ssize_t i; size_t sum = 0; while(sum < n) { i = read(fds, buf, n); if(i < 0) return i; else if(i == 0) return sum; sum += i; } return sum;}static void write_block(int i, int fds){ off_t len; /* error checking ??? */ lseek(fds, blist[i].off_start, SEEK_SET); /* check for eof */ len = blist[i].off_len; if(blist[i].off_start + len > file_size) len = file_size - blist[i].off_start; write(fds, blist[i].buf, len);}static void save_file(int do_munmap){ int i; if(make_backup_files) { int backup_fd; ssize_t r; byte *tmp; char *backup_name; backup_name = xmalloc(strlen(file_name) + 2); sprintf(backup_name, "%s~", file_name); backup_fd = open(backup_name, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); tmp = xmalloc(off_len); lseek(fd, 0, SEEK_SET); while((r = read_file(fd, tmp, off_len)) > 0) write(backup_fd, tmp, r); free(tmp); close(backup_fd); free(backup_name); } /* save all modified blocks */ for(i = 0; i < blist_count; i++) { if(blist[i].modified) { write_block(i, fd);#ifdef HAVE_MMAP if(do_munmap) munmap(blist[i].buf, blist[i].off_len);#endif blist[i].modified = 0; } } file_saved = 1;}static int find_block(off_t offset){ int i; for(i = 0; i < blist_count; i++) { if(blist[i].off_start <= offset && offset < blist[i].off_start + blist[i].off_len) return i; } return -1;}static void check_max_blocks(){ int i; /* check if we reached max. number of blocks */ if((blist_count + 1) * off_len >= MAX_BLOCKMEM) { /* first attempt to free unmodified blocks */ for(i = 0; i < blist_count; i++) { if(blist[i].modified) continue; if(blist[i].off_start <= cur_offset && cur_offset < blist[i].off_start + blist[i].off_len) continue; /* ckip current block */#ifdef HAVE_MMAP munmap(blist[i].buf, blist[i].off_len);#else free(blist[i].buf);#endif /* hack: move end block to this one's position in the list */ if(i != blist_count - 1) blist[i] = blist[blist_count - 1]; blist_count--; blist = xrealloc(blist, blist_count * sizeof(blist_t)); return; } /* if no blocks are unmodified (very unlikely for such large files, btw) * remove the first one */ if((blist_count + 1) * off_len >= MAX_BLOCKMEM) { write_block(0, fd);#ifdef HAVE_MMAP munmap(blist[0].buf, blist[0].off_len);#else free(blist[0].buf);#endif /* hack: move end block to this one's position in the list */ if(blist_count != 1) blist[0] = blist[blist_count - 1]; blist_count--; blist = xrealloc(blist, blist_count * sizeof(blist_t)); } }}static int load_block(off_t offset){ byte *buf; off_t off_start; /* we must allocate another block, check if we can */ check_max_blocks(); /* align on the block size */ for(off_start = offset; off_start % off_len != 0; off_start--) ;#ifndef HAVE_MMAP lseek(fd, off_start, SEEK_SET); buf = xmalloc(off_len); /* with mmap this isn't needed */ if(read_file(fd, buf, off_len) < 0) { fprintf(stderr, "%s: read_file() failed, something's gone wrong", prog_name); exit(1); }#else buf = mmap(0, off_len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, off_start); if(buf == MAP_FAILED) { fprintf(stderr, "%s: mmap() failed", prog_name); exit(1); }#endif /* add this block to the list */ blist = xrealloc(blist, sizeof(blist_t) * (blist_count + 1)); blist[blist_count].buf = buf; blist[blist_count].off_start = off_start; blist[blist_count].off_len = off_len; blist[blist_count].modified = 0; return blist_count++;}static void open_file(const char *name){ struct stat st; fd = open(name, O_RDWR); if(fd < 0) { fprintf(stderr, "%s: open: %s\n", prog_name, strerror(errno)); exit(1); } if(fstat(fd, &st) < 0) { fprintf(stderr, "%s: fstat: %s\n", prog_name, strerror(errno)); close(fd); exit(1); } file_name = xstrdup(name); file_size = st.st_size; if(file_size == 0) { close(fd); fprintf(stderr, "%s: file size of `%s' is 0\n", prog_name, name); exit(0); }#ifndef HAVE_MMAP off_len = st.st_blksize;#endif load_block(0);}static void unhilite(){ mvwchgat(w_main, cur_line, (3 * (cur_offset % bpl)), 3, A_NORMAL, use_colors, NULL); mvwchgat(w_ascii, cur_line, cur_offset % bpl, 3, A_NORMAL, use_colors, NULL); touchwin(w_main); touchwin(w_ascii);}static void hilite(){ if(use_colors) { mvwchgat(w_main, cur_line, (3 * (cur_offset % bpl)) + current_nibble, 1, A_NORMAL, 2, NULL); mvwchgat(w_ascii, cur_line, cur_offset % bpl, 1, A_NORMAL, 2, NULL); } else { mvwchgat(w_main, cur_line, 3 * (cur_offset % bpl), 2, A_STANDOUT, 0, NULL); mvwchgat(w_ascii, cur_line, cur_offset % bpl, 1, A_BLINK, 0, NULL); } touchwin(w_main); touchwin(w_ascii);}static byte get_byte(off_t offset){ int i; i = blist_current; if(blist[i].off_start > offset || offset >= blist[i].off_start + blist[i].off_len) { /* if it isn't in the current block, search already mapped area */ if((i = find_block(offset)) < 0) i = load_block(offset); blist_current = i; } return blist[i].buf[offset - blist[i].off_start];}static void put_byte(off_t offset, byte b){ int i; /* search already mapped area */ if((i = find_block(offset)) < 0) i = load_block(offset); blist[i].buf[offset - blist[i].off_start] = b; blist[i].modified = 1; file_saved = 0;}static int printable(byte *c){ if(*c > 127 || !isprint(*c)) { *c = '.'; return 0; } return 1;}/* on line number 'line' print 'bpl' bytes starting from 'offset' */static void put_line(int line, off_t offset){ int i; byte b; /* print offset */ mvwprintw(w_offset, line, 0, "%08X:", offset); mvwchgat(w_offset, line, 9, 4, A_NORMAL, 1, NULL); wmove(w_main, line, 0); wclrtoeol(w_main); wmove(w_ascii, line, 0); wclrtoeol(w_ascii); for(i = 0; i < bpl && offset < file_size; i++, offset++) { b = get_byte(offset); mvwprintw(w_main, line, i * 3, "%02X ", b); printable(&b); mvwprintw(w_ascii, line, i, "%c", b); } /* this is ugly */ { int y,x; getmaxyx(w_main,y,x); mvwchgat(w_main, line, i, x - i, A_NORMAL, 1, NULL); } while(i <= bpl) { mvwchgat(w_ascii, line, i, 1, A_NORMAL, 1, NULL); i++; }}static void put_status(){ byte b; b = get_byte(cur_offset); mvprintw(0, 0, " File: %s%c size = %lu bytes %3d%% [%c]", file_name, file_saved ? ' ' : '*', file_size, beg_offset * 100 / ( end_offset + (end_offset == 0) ), mode_ascii ? 'A' : 'H' ); mvprintw(0, COLS - 19, "Press 'h' for help"); mvprintw(LINES - 1, 0, " offset: 0x%-8X (%10d) " "char: %c\t hex = 0x%-02X dec = %-3d oct = %-8o", cur_offset, cur_offset, printable(&b) ? b : '?', b, b, b ); clrtoeol(); if(use_colors) { mvchgat(0, 0, -1, A_NORMAL, 3, NULL); mvchgat(LINES - 1, 0, -1, A_NORMAL, 3, NULL); } else { mvchgat(0, 0, -1, A_STANDOUT, 0, NULL); mvchgat(LINES - 1, 0, -1, A_STANDOUT, 0, NULL); }}/* called when in hex mode */static void set_byte_part(byte b){ byte old_byte; int shift; if(current_nibble == 1) { shift = 0; } else { shift = 4; } if(isdigit(b)) b -= '0'; else b = 10 + b - 'a'; old_byte = get_byte(cur_offset); b = (b << shift) | (old_byte & (0xF << (4 - shift))); if(b == old_byte) return; save_undo_info(cur_offset, old_byte); put_byte(cur_offset, b); mvwprintw(w_main, cur_line, 3 * (cur_offset % bpl), "%02X", b); printable(&b); mvwprintw(w_ascii, cur_line, cur_offset % bpl, "%c", b); hilite();}/* called when in ascii mode */static void set_byte(byte b){ byte old_byte; old_byte = get_byte(cur_offset); if(b == old_byte) return; save_undo_info(cur_offset, old_byte); put_byte(cur_offset, b); mvwprintw(w_main, cur_line, 3 * (cur_offset % bpl), "%02X", b); printable(&b); mvwprintw(w_ascii, cur_line, cur_offset % bpl, "%c", b); hilite();}static void save_undo_info(off_t offset, byte b){ int i; i = undo_count++; undo_list = xrealloc(undo_list, sizeof(undo_t) * undo_count); undo_list[i].offset1 = offset; undo_list[i].offset2 = 0; undo_list[i].b = b; file_saved = 0;}static void save_undo_fill_info(off_t offset1, off_t offset2){ int i; off_t t; i = undo_count++; undo_list = xrealloc(undo_list, sizeof(undo_t) * undo_count); undo_list[i].offset1 = offset1; undo_list[i].offset2 = offset2; undo_list[i].fill_bytes = xmalloc(offset2 - offset1 + 1); for(t = 0; t <= offset2 - offset1; t++) undo_list[i].fill_bytes[t] = get_byte(offset1 + t); file_saved = 0;}static void do_undo(){ byte b; off_t offset; if(undo_count == 0) return; /* the difference between "fill" undo and normal undo is * that the "fill" undo struct has offset2 != 0 */ undo_count--; if(undo_list[undo_count].offset2 == 0) { offset = undo_list[undo_count].offset1; b = undo_list[undo_count].b; put_byte(offset, b); /* if this is in current view ... */ if(offset >= beg_offset && offset < beg_offset + bpl * max_lines) { unhilite();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -