├── .gitignore ├── README.md ├── get-asm.sh ├── meson.build ├── pics ├── 64gb.jpg ├── ascii.jpg ├── ask.png ├── help.png ├── one.jpg └── two.jpg ├── vbl.cpp └── version.sh /.gitignore: -------------------------------------------------------------------------------- 1 | /vbl/ 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | vbl - VBindiff for Linux 2 | ======================== 3 | 4 | Hex viewer, differ, dumper and editor 5 | 6 | dynamic 16/24/32 byte Hex & ASCII view in a terminal 7 | 8 | 256TB Files & Devices 9 | 10 | edit / insert / delete 11 | 12 | zero-tolerant _TurboSearch_ for SSD 13 | 14 | SIMD speedup 15 | 16 | diff mode 17 | 18 | dump mode 19 | 20 | 64-bit static + dynamic 21 | 22 | asm + disasm code 23 | 24 | Features: 25 | --------- 26 | 27 | - Ascii search `f` 28 | - Binary search 29 | - Forward search `n` 30 | - Backward search `p` 31 | - Case insensitive `i` 32 | - Search history `Up` `Dn` 33 | - Search edit `Ins` `Del` `^u` `^k` 34 | - Search highlight 35 | - Search indentation 36 | - Search interruption `Esc` 37 | - Visual feedback 38 | - Goto position decimal `g` 39 | - Goto position percent 40 | - Goto position hex (abcd 0x1234 1234x) 41 | - Goto position 2** (kmgt) 42 | - Goto position 10** (KMGT) 43 | - Goto position *512 (s) 44 | - Goto position *4096 (S) 45 | - Goto position offset (+addr -addr) 46 | - Goto position history `Up` `Dn` 47 | - Goto last address `'` `<` 48 | - Goto last offset `.` 49 | - Goto last offset neg `,` 50 | - Set last address `l` 51 | - Set last address auto `g` `f` `Home` `End` 52 | - Set jump address `j` 53 | - Get jump address `"` 54 | - Next difference `Enter` 55 | - Prev difference `#` `\` 56 | - Next different byte `PgDn` 57 | - Prev different byte `PgUp` 58 | - Sync 1. with 2. view `1` 59 | - Sync 2. with 1. view `2` 60 | - File position decimal 61 | - File position percent 62 | - File offset difference 63 | - _Smartscroll_ (single mode) `ENTER` 64 | - Skip forward 4% `+` `*` `=` 65 | - Skip backward 1% `-` 66 | - ASCII-Mode (single mode) `a` 67 | - Column raster `r` 68 | - Edit file `e` 69 | - Edit insert byte `Ins` 70 | - Edit delete byte `Del` 71 | - RW/RO detection 72 | - Use only top file `t` 73 | - Use only bottom file `b` 74 | - Help window `h` 75 | - Quit `q` 76 | - Easter egg 77 | 78 | Notes: 79 | ------ 80 | 81 | All operations take place in read-only mode. 82 | 83 | Only if you _exit_ the edit mode and there are changes and you _explicitly_ confirm this will the file be temporarily opened for read/write. 84 | 85 | With inserted or deleted bytes, the write can be huge, so it happens always **in place**. 86 | 87 | The _last address_ is auto set with initial `Find`, `Goto` w/o relative, `home`/`end` or manual with `l`. 88 | 89 | A fixed _jump address_ can be set with `j` and get with `"`; init with param + set last addr. 90 | 91 | The starting point for _seek next diff byte_ is bottom-right. 92 | 93 | The starting point for _seek prev diff byte_ is top-left; with added Page Up. 94 | 95 | A _pane offset difference_ remains during comparison. 96 | 97 | `Goto` position is now 2** kmgt and 10** KMGT(S.I.); with added sector support. 98 | 99 | `Esc` can interrupt the searches. 100 | 101 | Only `q` quit the program. 102 | 103 | Build: 104 | ------ 105 | 106 | ``` 107 | # headers + *meson* (debian) 108 | apt install libncurses-dev meson 109 | 110 | meson setup vbl 111 | 112 | ### change thousands separator from dot/default to comma: 113 | # meson configure -Dcpp_args="-DTHOU_SEP_COMMA=1" vbl 114 | 115 | meson compile -C vbl 116 | ``` 117 | 118 | Exe: 119 | ---- 120 | 121 | ``` 122 | ./vbl/vbl 64-bit executable 123 | ./vbl/vbl-strip 124 | ./vbl/vbl-stat static version 125 | ./vbl/vbl-stat-strip 126 | ``` 127 | 128 | Asm: 129 | ---- 130 | 131 | ``` 132 | vbl/vbl_asm.lst: cleaned assembly 133 | 134 | vbl/vbl_asm-diff.lst: more cleaned for diff/dwdiff 135 | 136 | vbl/vbl_dis.lst: disassembly with color codes 137 | ``` 138 | 139 | Screenshoots: 140 | ------------- 141 | 142 | ![Screenshot](pics/one.jpg) 143 | *One File* 144 | 145 | ![Screenshot](pics/ascii.jpg) 146 | *ASCII Mode* 147 | 148 | ![Screenshot](pics/64gb.jpg) 149 | *Files >64GB* 150 | 151 | ![Screenshot](pics/two.jpg) 152 | *Two Files* 153 | 154 | ![Screenshot](pics/ask.png) 155 | *You have to confirm long writes (>512MB)* 156 | 157 | ![Screenshot](pics/help.png) 158 | *Help Screen* 159 | 160 | Cmdline: 161 | -------- 162 | 163 | ``` 164 | VBinDiff for Linux 4.4 165 | 166 | vbl file [file2] [addr] [addr2] // ncurses 167 | 168 | vbl file1 file2 - // diff view 169 | 170 | vbl file1 file2 -- // diff return 171 | 172 | vbl file - [start [end]] [length{l$}] [width{w$}] // dump ascii 173 | 174 | vbl file -- [start [end]] [length{l$}] // dump binary 175 | 176 | // type 'h' for help 177 | 178 | ``` 179 | 180 | Shortcut: 181 | --------- 182 | 183 | ``` 184 | vb () { [[ $2 ]] && vbl "$1" "$2" - || echo "vbl file1 file2 [-]";} 185 | 186 | ``` 187 | 188 | -------------------------------------------------------------------------------- /get-asm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # disassemble parameter to stdout 4 | # 5 | # default: compiled dynamic exe 6 | # + 7 | # 8 | # cleaning assembler source 9 | # 10 | # optional tag 11 | 12 | if [ "$MESON_BUILD_ROOT" ]; then 13 | FILE="$MESON_BUILD_ROOT/$1" 14 | 15 | elif [ "${1#${1%.s}}" = ".s" ]; then 16 | FILS="$1" 17 | 18 | elif [ "${1#${1%.S}}" = ".S" ]; then 19 | FILS="$1" 20 | 21 | elif [ "$1" ]; then 22 | FILE="$1" 23 | fi 24 | 25 | [ -f "$FILE" ] && 26 | 27 | objdump --source-comment \ 28 | --disassembler-options intel \ 29 | --demangle \ 30 | --line-numbers \ 31 | --disassembler-color=on \ 32 | --no-show-raw-insn \ 33 | --visualize-jumps=color \ 34 | "$FILE" 35 | 36 | ################################# 37 | 38 | Xclusiv() # cut Label: block 39 | { 40 | perl -ne ' 41 | BEGIN { 42 | $r = "'$1'" 43 | } 44 | 45 | if (/^$r/) { 46 | while (<>) { 47 | if (/^\S/) { 48 | last if not (/^$r/) 49 | } 50 | } 51 | } 52 | 53 | print 54 | ' 55 | } 56 | 57 | ASM_DIR="/tmp/VBL" 58 | 59 | [ "$2" ] && TAG="-$2" 60 | 61 | if [ "$MESON_BUILD_ROOT" ]; then 62 | FILA="$MESON_BUILD_ROOT/$1.p/$1.cpp.s" 63 | LIST="$MESON_BUILD_ROOT/${1}_asm.lst" 64 | DIFF="$MESON_BUILD_ROOT/${1}_asm-diff.lst" 65 | 66 | elif [ -f "$FILS" ]; then 67 | [ -d "$ASM_DIR" ] || mkdir -pv "$ASM_DIR" || exit 68 | 69 | FILA="$FILS" 70 | FILS=`basename "${FILS%.s}" .S` 71 | LIST="$ASM_DIR/$FILS-asm$TAG.lst" 72 | DIFF="$ASM_DIR/$FILS-asm$TAG-diff.lst" 73 | fi 74 | 75 | [ -f "$FILA" ] && 76 | 77 | cat "$FILA" | 78 | c++filt | 79 | 80 | sed '/^\s*\.cfi_/d' | 81 | sed '/^\s*\.loc /d' | 82 | sed '/^\.L[BEFV]/d' | 83 | sed '/\.LVU/d' | 84 | sed '/\.LVL/d' | 85 | sed 's/_[0-9]\+/_d+/g' | 86 | sed 's/tmp[0-9]\+/tmpD+/g' | 87 | sed '/#.*\.[0-9]/ s/\.[0-9]\+/.D+/g' | 88 | 89 | Xclusiv ".Ldebug" | 90 | Xclusiv ".LLST" | 91 | Xclusiv ".LLRL" | 92 | 93 | cat > "$LIST" # test/debug expressions 94 | # exit 95 | 96 | # kick line numbers + labels for "dwdiff old new" 97 | [ -f "$LIST" ] && 98 | 99 | perl -pe ' 100 | s%( \.\./[\w.-]*?cpp):\d+:%$1:d+:%; 101 | 102 | s/\.L\d+/.Ld+/g; 103 | 104 | ' "$LIST" > "$DIFF" 105 | 106 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | project('VBindiff for Linux', 'cpp', license : 'GPL-2.0-or-later', 2 | default_options : ['cpp_std=c++11', 'buildtype=debugoptimized'], 3 | version: run_command('version.sh', check: false).stdout()) 4 | 5 | # meson compile 6 | if meson.version().version_compare('< 0.54.0') 7 | error('meson too old') 8 | endif 9 | 10 | curses_dyn = [dependency('ncursesw')] 11 | 12 | curses_sta = [dependency('ncursesw', static: true)] 13 | 14 | asm_list = ['-save-temps', '-fverbose-asm', '-masm=intel'] 15 | 16 | obj = \ 17 | executable('vbl', 'vbl.cpp', dependencies: curses_dyn, 18 | cpp_args: ['-m64', asm_list]) 19 | 20 | lnk = obj.extract_objects('vbl.cpp') 21 | 22 | executable('vbl-strip', objects: lnk, dependencies: curses_dyn, 23 | link_args: '-Wl,--strip-all') 24 | 25 | executable('vbl-stat', objects: lnk, dependencies: curses_sta, 26 | link_args: '-static') 27 | 28 | executable('vbl-stat-strip', objects: lnk, dependencies: curses_sta, 29 | link_args: ['-static', '-Wl,--strip-all']) 30 | 31 | custom_target('vbl-asm', input : obj, output : 'vbl_dis.lst', 32 | env : {'MESON_BUILD_ROOT': meson.current_build_dir()}, 33 | command : ['get-asm.sh', '@INPUT@'], 34 | capture : true, build_by_default : true) 35 | 36 | -------------------------------------------------------------------------------- /pics/64gb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linuxCowboy/vbl/6340626f5486b556b6f45c373ee60d11c9e58e20/pics/64gb.jpg -------------------------------------------------------------------------------- /pics/ascii.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linuxCowboy/vbl/6340626f5486b556b6f45c373ee60d11c9e58e20/pics/ascii.jpg -------------------------------------------------------------------------------- /pics/ask.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linuxCowboy/vbl/6340626f5486b556b6f45c373ee60d11c9e58e20/pics/ask.png -------------------------------------------------------------------------------- /pics/help.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linuxCowboy/vbl/6340626f5486b556b6f45c373ee60d11c9e58e20/pics/help.png -------------------------------------------------------------------------------- /pics/one.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linuxCowboy/vbl/6340626f5486b556b6f45c373ee60d11c9e58e20/pics/one.jpg -------------------------------------------------------------------------------- /pics/two.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linuxCowboy/vbl/6340626f5486b556b6f45c373ee60d11c9e58e20/pics/two.jpg -------------------------------------------------------------------------------- /vbl.cpp: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------------------------- 2 | // 3 | // VBinDiff for Linux 4 | // 5 | // Hex viewer, differ, dumper and editor 6 | // 7 | // Copyright 2021-2025 by linuxCowboy 8 | // 9 | // vbindiff by Christopher J. Madsen 10 | // 64GB by Bradley Grainger 11 | // dynamic width by Christophe Bucher 12 | // 13 | // Version: 14 | // 1.x classic vbindiff interface, fix 32 byte 15 | // ----------------------------------------------- 16 | // 2.0 dynamic 16/24/32 byte width 17 | // 2.1 256 terabyte files 18 | // 2.2 kick panels 19 | // 2.3 ascii mode 20 | // 2.4 speedup differ 21 | // 2.5 full help 22 | // 2.6 cursor color 23 | // 2.7 use deque 24 | // 2.8 kick map 25 | // 2.9 kick iostream 26 | // 2.10 kick sstream 27 | // 2.11 kick algorithm 28 | // 2.12 ignore case 29 | // 2.13 relative jumps 30 | // 2.14 goto back 31 | // 2.15 repeat offset 32 | // 2.16 seekNotChar ascii 33 | // ---- meanwhile almost completely rewritten ---- 34 | // 3.0 edit insert/delete 35 | // 3.1 InputManager 36 | // 3.2 progress bar 37 | // 3.3 goto prefix 38 | // 3.4 edit diff 39 | // 3.5 set last 40 | // 3.6 golf search 41 | // 3.6.1 turbo zero 42 | // 3.6.2 SIMD case 43 | // 3.7 start addr 44 | // ------------------ 45 | // 4.0 SSE2 SIMD 46 | // 4.1 search all 47 | // 4.2 jump addr 48 | // 4.3 dump mode 49 | // 4.4 diff mode 50 | // 51 | // This program is free software; you can redistribute it and/or 52 | // modify it under the terms of the GNU General Public License as 53 | // published by the Free Software Foundation; either version 2 of 54 | // the License, or (at your option) any later version. 55 | // 56 | // This program is distributed in the hope that it will be useful, 57 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 58 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 59 | // GNU General Public License for more details. 60 | // 61 | // For the GNU General Public License see . 62 | //-------------------------------------------------------------------------- 63 | 64 | #include 65 | #include 66 | #include 67 | #include 68 | #include 69 | #include 70 | #include 71 | #include 72 | 73 | #include 74 | #include 75 | 76 | using namespace std; 77 | 78 | #define VBL_VERSION "4.4" 79 | 80 | // ################### 81 | // ##### options ##### 82 | // ################### 83 | 84 | /* set Cursor Color in input window with Operating System Command */ 85 | #ifndef SET_CURSOR_COLOR 86 | #define SET_CURSOR_COLOR 0 87 | #endif 88 | 89 | /* show a summary in edit insert/delete after large writes + wait */ 90 | #ifndef SHOW_WRITE_SUMMARY 91 | #define SHOW_WRITE_SUMMARY 0 92 | #endif 93 | 94 | /* thousands separator (or '\0') */ 95 | #define THOU_SEP '.' 96 | 97 | // ################### 98 | 99 | /* const: gcc optimizes macros away */ 100 | const bool debug = 0; // ##:q 101 | 102 | /* curses debug: 103 | f=/tmp/.vbl 104 | tail -F $f 105 | vbl file 2>$f; cat $f 106 | */ 107 | #define mPI(x) if (debug) fprintf(stderr, "\r%s: 0x%lX %ld\n", #x, (long) x, (long) x); 108 | #define mPU(x) if (debug) fprintf(stderr, "\r%s: 0x%lX %lu\n", #x, (Full) x, (Full) x); 109 | #define mPF(x) if (debug) fprintf(stderr, "\r%s: %f\n", #x, (float) x); 110 | #define mPS(x) if (debug) fprintf(stderr, "\r%s: %s\n", #x, x); 111 | 112 | #define mPsec(x) if (debug) fprintf(stderr, "\r%s: %ld sec\n", #x, (long) x); 113 | #define mPms(x) if (debug) fprintf(stderr, "\r%s: %.3f msec\n", #x, (float) x / 1000000); 114 | 115 | /* profiling: sec, msec; init: {1,} */ 116 | #define mTs(x,init...) if (debug) {if (1 != 1##init) {x = time(NULL);}\ 117 | else {x = time(NULL) - x; mPsec(x)}} 118 | #define mTms(x,init...) if (debug) {if (1 != 1##init) {x = timer();}\ 119 | else {x = timer(2,x); mPms(x)}} // one-shot 120 | 121 | #define mZ(x) if (debug) {x = 0;} // reset 122 | #define mTpp(x,init...) if (debug) {if (1 != 1##init) timer(1); else {x += timer(2);}} // sum-up (t0) 123 | 124 | /* w/o redirection */ 125 | #define mPP(x) if (debug) sleep(x); 126 | #define mPK if (debug) file1.readKeyF(); 127 | 128 | /* hex dump: pointer, count */ 129 | #define mPX(x, c) if (debug) {fprintf(stderr, "\r%s, %d \n\r", #x, (int) c);\ 130 | for (Word I=0; I < c; ++I) fprintf(stderr, "%X ", (Byte) (x)[I]);\ 131 | fprintf(stderr, "\n");} 132 | 133 | #define mCeil(x, y) x / y + (x % y ? 1 : 0) 134 | 135 | #define mEdit historyPos = history.size(); 136 | #define mScale count / (scale ? scale : 1) 137 | 138 | #define KEY_CTRL_C 0x03 139 | #define KEY_TAB 0x09 140 | #define KEY_CTRL_K 0x0B 141 | #define KEY_RETURN 0x0D 142 | #define KEY_CTRL_U 0x15 143 | #define KEY_ESCAPE 0x1B 144 | #define KEY_DELETE 0x7F 145 | 146 | //==================================================================== 147 | // Color Enumerations 148 | 149 | enum ColorPair { 150 | pairWhiteBlue = 1, 151 | pairBlackWhite, 152 | pairRedWhite, 153 | pairYellowBlue, 154 | pairGreenBlue, 155 | pairBlackCyan, 156 | pairGreenBlack, 157 | pairWhiteCyan, 158 | pairWhiteRed, 159 | pairWhiteGreen, 160 | pairBlackYellow 161 | }; 162 | 163 | enum Style { 164 | cMainWin, 165 | cInputWin, 166 | cHelpWin, 167 | cName, 168 | cDiff, 169 | cEdit, 170 | cInsert, 171 | cSearch, 172 | cSeek, 173 | cMatch, 174 | cRaster, 175 | cAddress, 176 | cHotkey, 177 | cHighFile, 178 | cHighBusy, 179 | cHighBus2, 180 | cHighEdit 181 | }; 182 | 183 | static const ColorPair colorStyle[] = { 184 | pairWhiteBlue, // cMainWin 185 | pairWhiteBlue, // cInputWin 186 | pairWhiteBlue, // cHelpWin 187 | pairBlackWhite, // cName 188 | pairGreenBlack, // cDiff 189 | pairYellowBlue, // cEdit 190 | pairGreenBlue, // cInsert 191 | pairWhiteRed, // cSearch 192 | pairWhiteGreen, // cSeek 193 | pairRedWhite, // cMatch 194 | pairBlackCyan, // cRaster 195 | pairYellowBlue, // cAddress 196 | pairGreenBlue, // cHotkey 197 | pairWhiteCyan, // cHighFile 198 | pairWhiteRed, // cHighBusy 199 | pairWhiteGreen, // cHighBus2 200 | pairBlackYellow // cHighEdit 201 | }; 202 | 203 | static const attr_t attribStyle[] = { 204 | COLOR_PAIR(colorStyle[ cMainWin ]), 205 | COLOR_PAIR(colorStyle[ cInputWin ]), 206 | COLOR_PAIR(colorStyle[ cHelpWin ]), 207 | COLOR_PAIR(colorStyle[ cName ]), 208 | A_BOLD | COLOR_PAIR(colorStyle[ cDiff ]), 209 | A_BOLD | COLOR_PAIR(colorStyle[ cEdit ]), 210 | A_BOLD | COLOR_PAIR(colorStyle[ cInsert ]), 211 | A_BOLD | COLOR_PAIR(colorStyle[ cSearch ]), 212 | A_BOLD | COLOR_PAIR(colorStyle[ cSeek ]), 213 | COLOR_PAIR(colorStyle[ cMatch ]), 214 | COLOR_PAIR(colorStyle[ cRaster ]), 215 | A_BOLD | COLOR_PAIR(colorStyle[ cAddress ]), 216 | A_BOLD | COLOR_PAIR(colorStyle[ cHotkey ]), 217 | A_BOLD | COLOR_PAIR(colorStyle[ cHighFile ]), 218 | A_BOLD | COLOR_PAIR(colorStyle[ cHighBusy ]), 219 | A_BOLD | COLOR_PAIR(colorStyle[ cHighBus2 ]), 220 | COLOR_PAIR(colorStyle[ cHighEdit ]) 221 | }; 222 | 223 | //==================================================================== 224 | // Type definitions 225 | 226 | typedef unsigned char Byte; 227 | typedef unsigned short Word; 228 | typedef unsigned int Half; 229 | typedef unsigned long Full; 230 | typedef __m128i Quad; 231 | typedef Byte Command; 232 | 233 | typedef int File; 234 | typedef off_t FPos; // long int 235 | typedef ssize_t Size; // long int 236 | 237 | typedef deque StrDeq; 238 | typedef deque BytDeq; 239 | 240 | enum LockState { lockNeither, lockTop, lockBottom }; 241 | 242 | //==================================================================== 243 | // Constants ##:cmd 244 | 245 | const Command cmgGoto = 0x80; // Main cmd 246 | const Command cmgGotoTop = 0x08; // Flag 247 | const Command cmgGotoBottom = 0x04; // Flag 248 | const Command cmgGotoForw = 0x40; 249 | const Command cmgGotoBack = 0x20; 250 | const Command cmgGotoMask = 0x13; // Mask 251 | const Command cmgGotoLGet = 0x01; 252 | const Command cmgGotoLSet = 0x02; 253 | const Command cmgGotoLOff = 0x03; 254 | const Command cmgGotoNOff = 0x11; 255 | const Command cmgGotoJGet = 0x12; 256 | const Command cmgGotoJSet = 0x13; 257 | 258 | const Command cmfFind = 0x40; // Main cmd 259 | const Command cmfFindNext = 0x20; 260 | const Command cmfFindPrev = 0x10; 261 | const Command cmfNotCharDn = 0x02; 262 | const Command cmfNotCharUp = 0x01; 263 | 264 | const Command cmmMove = 0x20; // Main cmd 265 | const Command cmmMoveForward = 0x10; 266 | const Command cmmMoveMask = 0x03; // Mask 267 | const Command cmmMoveByte = 0x00; // Move 1 byte 268 | const Command cmmMoveLine = 0x01; // Move 1 line 269 | const Command cmmMovePage = 0x02; // Move 1 page 270 | const Command cmmMoveAll = 0x03; // Move to begin or end 271 | 272 | const Command cmNothing = 0; 273 | const Command cmUseTop = 1; 274 | const Command cmUseBottom = 2; 275 | const Command cmNextDiff = 3; 276 | const Command cmPrevDiff = 4; 277 | const Command cmEditTop = 5; 278 | const Command cmEditBottom = 6; 279 | const Command cmSyncUp = 7; 280 | const Command cmSyncDn = 8; 281 | const Command cmShowAscii = 9; 282 | const Command cmIgnoreCase = 10; 283 | const Command cmShowRaster = 11; 284 | const Command cmShowHelp = 12; 285 | const Command cmSmartScroll = 13; 286 | const Command cmQuit = 14; 287 | 288 | //-------------------------------------------------------------------- 289 | 290 | const Size minScreenHeight = 24, // Enforced minimum height 291 | minScreenWidth = 79, // Enforced minimum width 292 | 293 | skipForw = 4, // Percent to skip forward 294 | skipBack = 1, // Percent to skip backward 295 | 296 | staticSize = 1L << 25, // size global buffers 297 | warnResize = 1L << 29, // confirmation threshold 298 | 299 | barDelay = 6, // msec, smoothness vs. speed 300 | 301 | maxHistory = 20, // find and goto 302 | 303 | dumpDef = 16, // terminal dump: both 304 | dumpMax = 32; // full dump only 305 | 306 | const char *hexDigits = "0123456789ABCDEF", // search 307 | *hexDigitsGoto = "0123456789ABCDEFabcdef%Xx+-kmgtKMGTsS", // goto 308 | 309 | *colorInsert = "#00BBBB", // cursor color "normal" 310 | *colorDelete = "#EE0000"; // cursor color "very visible" 311 | 312 | const wchar_t barSyms[] = {L'▏', L'▎', L'▍', L'▌', L'▋', L'▊', L'▉', L'█'}; 313 | 314 | const char sPrefix[] = "skmgtSKMGT"; 315 | const Size aPrefix[] = { 512, 1024, 1048576, 1073741824, 1099511627776, 316 | 4096, 1000, 1000000, 1000000000, 1000000000000 }; 317 | 318 | //-------------------------------------------------------------------- 319 | // Help screen text - max 21 lines (minScreenHeight - 3) ##:x 320 | 321 | const char *aHelp[] = { 322 | " ", 323 | " Move: left right up down home end space backspace", 324 | " ", 325 | " Find Next Prev PgDn PgUp == next/prev diff byte", 326 | " ", 327 | " Goto [+-]{dec hex 0x x$}[%|sSkmgtKMGT] +4% + * = -1% -", 328 | " Last addr: ' < Jump Addr: \" last off: . neg off: ,", 329 | " ", 330 | " Edit file show Raster Ignore case Quit", 331 | " ", 332 | " --- One File ---", 333 | " Enter == sm4rtscroll Ascii mode", 334 | " ", 335 | " --- Two Files ---", 336 | " Enter == next diff # \\ == prev diff 1 2 == sync views", 337 | " use only Top, use only Bottom", 338 | " ", 339 | " --- Edit ---", 340 | " Enter == copy byte from other file; Insert Ctrl-U", 341 | " Tab == HEX <> ASCII, Esc == done; Delete Ctrl-K", 342 | " " 343 | }; 344 | 345 | const int longestLine = 57; // adjust! 346 | 347 | const Byte aBold[] = { // hotkeys, start y:1, x:1 348 | 4,3, 4,10, 4,15, 349 | 6,3, 6,46, 6,48, 6,50, 6,57, 350 | 7,3, 7,14, 7,16, 7,20, 7,31, 7,45, 7,57, 351 | 9,3, 9,20, 9,29, 9,54, 352 | 12,26, 353 | 15,23, 15,25, 15,41, 15,43, 354 | 16,32, 16,47, 355 | 0 356 | }; 357 | 358 | const char *helpVersion = " VBinDiff for Linux " VBL_VERSION " "; 359 | 360 | const int helpWidth = 1 + longestLine + 2 + 1, 361 | helpHeight = 1 + sizeof(aHelp) / sizeof(aHelp[0]) + 1; 362 | 363 | //==================================================================== 364 | // Global Variables ##:vars 365 | 366 | WINDOW *winInput, 367 | *winHelp; 368 | 369 | alignas(0x1000) 370 | Byte bufFile1[staticSize], 371 | bufFile2[staticSize]; 372 | 373 | Byte *buffer = bufFile1; 374 | 375 | FPos *sm4rt; 376 | 377 | char bufTimer[64]; 378 | 379 | bool singleFile, 380 | showRaster, 381 | sizeTera, 382 | modeAscii, 383 | ignoreCase, 384 | stopRead, 385 | useSSE2, 386 | haveDiff; 387 | 388 | LockState lockState; 389 | 390 | string lastSearch, 391 | lastSearchIgnCase; 392 | 393 | StrDeq hexSearchHistory, 394 | textSearchHistory, 395 | positionHistory; 396 | 397 | BytDeq editBytes, 398 | editColor; 399 | 400 | // Set dynamically for 16/24/32 byte width 401 | Size screenWidth, // Number of columns in curses 402 | linesTotal, // Number of lines in curses 403 | numLines, // Number of lines of each file to display 404 | bufSize, // Number of bytes of each file to display 405 | lineWidth, // Number of bytes displayed per line 406 | lineWidthAsc, // Number of bytes displayed per line ascii 407 | inWidth, // Number of digits in input window 408 | leftMar, // Starting column of hex display 409 | leftMar2, // Starting column of ASCII display 410 | searchIndent, // Lines of search result indentation 411 | steps[4], // Number of bytes to move for each step 412 | 413 | diffMode, // diff files to terminal 414 | dumpMode, // dump file to terminal 415 | dumpBeg, 416 | dumpEnd, 417 | dumpLen, 418 | dumpWid; 419 | 420 | // debug timer 1-9, init 0 421 | __attribute__ ((unused)) static Size t1, t2, t3, t4, t5, t6, t7, t8, t9, t0; 422 | 423 | char *program, 424 | 425 | #if THOU_SEP_COMMA 426 | thouSep = ','; 427 | #else 428 | thouSep = THOU_SEP; 429 | #endif 430 | 431 | //==================================================================== 432 | // Global Functions 433 | 434 | //-------------------------------------------------------------------- 435 | // Global timer / profiling 436 | // modes: 0-set 1-sum_up 2-nsec 3-msec 437 | 438 | Size timer(int mode=0, Size var=t0) 439 | { 440 | timespec ts; 441 | 442 | clock_gettime(CLOCK_TAI, &ts); 443 | 444 | Size ret = ts.tv_sec * 1000000000 + ts.tv_nsec; 445 | 446 | if (mode == 1) { 447 | t0 = ret; 448 | } 449 | 450 | else if (mode == 2) { 451 | ret -= var; 452 | } 453 | 454 | else if (mode == 3) { 455 | ret = (ret - var) / 1000000; 456 | } 457 | 458 | return ret; 459 | } 460 | 461 | //-------------------------------------------------------------------- 462 | // FileIO 463 | 464 | File OpenFile(const char* path, bool writable=false) 465 | { 466 | return open(path, (writable ? O_RDWR : O_RDONLY)); 467 | } 468 | 469 | bool WriteFile(File file, const Byte* buf, Size cnt) 470 | { 471 | while (cnt > 0) { 472 | Size bytesWritten = write(file, buf, cnt); 473 | 474 | if (bytesWritten < 1) { 475 | if (errno == EINTR) 476 | bytesWritten = 0; 477 | else 478 | return false; 479 | } 480 | 481 | buf += bytesWritten; 482 | cnt -= bytesWritten; 483 | } 484 | 485 | return true; 486 | } 487 | 488 | Size ReadFile(File file, Byte* buf, Size cnt) 489 | { 490 | Size ret = read(file, buf, cnt); 491 | 492 | /* interrupt the searches */ 493 | timeout(0); 494 | switch(getch()) { 495 | case KEY_ESCAPE: 496 | stopRead = true; 497 | } 498 | timeout(-1); 499 | 500 | /* mitigate read errors */ 501 | if (ret < 0) { 502 | ret = 0; 503 | stopRead = true; 504 | } 505 | 506 | return ret; 507 | } 508 | 509 | FPos SeekFile(File file, FPos position, int whence=SEEK_SET) 510 | { 511 | return lseek(file, position, whence); 512 | } 513 | 514 | //-------------------------------------------------------------------- 515 | // Dumper (no curses) 516 | 517 | void dumpFile(char* file) 518 | { 519 | File fd; 520 | 521 | if ((fd = OpenFile(file)) < 0) { 522 | err(2, file); 523 | } 524 | 525 | if (! dumpLen) { 526 | if (! dumpEnd) { 527 | dumpEnd = SeekFile(fd, 0, SEEK_END); 528 | } 529 | 530 | dumpLen = dumpEnd - dumpBeg; 531 | } 532 | 533 | if (SeekFile(fd, dumpBeg) < 0) { 534 | err(3, "seek"); 535 | } 536 | 537 | Size bytesRead, cnt, len; 538 | 539 | if (dumpMode == 2) { // binary 540 | Byte* pb = buffer; 541 | 542 | for (; ; dumpLen -= cnt) { 543 | if ((bytesRead = read(fd, pb, staticSize)) < 0) { 544 | err(4, "read"); 545 | } 546 | 547 | if (! (cnt = min(bytesRead, dumpLen))) { 548 | break; 549 | } 550 | 551 | for (Size i=0; i < cnt; ++i) { 552 | putchar(pb[i]); 553 | } 554 | } 555 | } 556 | 557 | else { 558 | Size tera = dumpBeg + dumpLen >= 68719476736 ? 3 : 0; 559 | 560 | if (dumpWid > dumpMax) { 561 | dumpWid = dumpMax; 562 | } 563 | 564 | char addr[9]; 565 | sprintf(addr, "%%0%dlX ", tera ? 12 : 9); 566 | 567 | Size lcnt = 9 + tera + 2 + (dumpWid - 1) / 8 + dumpWid * 3 + 1 + dumpWid; 568 | char line[lcnt + 1] = { 0 }; 569 | 570 | for (; ; dumpLen -= cnt, dumpBeg += cnt) { 571 | if ((bytesRead = read(fd, buffer, staticSize)) < 0) { 572 | err(4, "read"); 573 | } 574 | 575 | if ((cnt = min(bytesRead, dumpLen)) <= 0) { 576 | printf(addr, dumpBeg); 577 | 578 | putchar(10); 579 | break; 580 | } 581 | 582 | for (Size i=0; i < cnt; i += len) { 583 | if (i > cnt - dumpWid) { 584 | memset(line, ' ', lcnt); 585 | } 586 | 587 | char *pbufHex = line; 588 | 589 | pbufHex += sprintf(line, addr, dumpBeg + i); 590 | 591 | len = min(cnt - i, dumpWid); 592 | 593 | for (Size j=0; j < len; ++j) { 594 | if (! (j % 8)) { 595 | *pbufHex++ = ' '; 596 | } 597 | 598 | Byte b = buffer[i + j]; 599 | 600 | pbufHex += sprintf(pbufHex, "%02X ", b); 601 | 602 | line[lcnt - dumpWid + j] = b > '~' || b < ' ' ? '.' : b; 603 | } 604 | 605 | *pbufHex = ' '; 606 | 607 | puts(line); 608 | } 609 | } 610 | } 611 | 612 | close(fd); 613 | 614 | exit(0); 615 | } // end dumpFile 616 | 617 | //-------------------------------------------------------------------- 618 | // Cmdline differ 619 | 620 | FPos diffFile(char* file1, char* file2) 621 | { 622 | File fd1, fd2; 623 | 624 | if ((fd1 = OpenFile(file1)) < 0) { 625 | err(2, file1); 626 | } 627 | 628 | if ((fd2 = OpenFile(file2)) < 0) { 629 | close(fd1); 630 | err(2, file2); 631 | } 632 | 633 | FPos off = 0; 634 | Size size, size1, size2; 635 | 636 | do { 637 | if ((size1 = read(fd1, bufFile1, staticSize)) < 0) { 638 | err(4, file1); 639 | } 640 | 641 | if ((size2 = read(fd2, bufFile2, staticSize)) < 0) { 642 | err(4, file2); 643 | } 644 | 645 | if (size1 == 0 && size2 == 0) { 646 | off = -1; 647 | break; 648 | } 649 | 650 | size = min(size1, size2); 651 | 652 | if (size == 0) { 653 | break; 654 | } 655 | 656 | if (memcmp(bufFile1, bufFile2, size)) { 657 | break; 658 | } 659 | 660 | if (size < staticSize) { 661 | if (size1 == size2) { 662 | off = -1; 663 | } 664 | break; 665 | } 666 | 667 | off += staticSize; 668 | 669 | } while (1); 670 | 671 | close(fd1); 672 | close(fd2); 673 | 674 | return off; 675 | } // end diffFile 676 | 677 | //-------------------------------------------------------------------- 678 | // Initialize ncurses ##:i 679 | 680 | bool initialize() 681 | { 682 | setlocale(LC_ALL, ""); // for Unicode blocks 683 | 684 | if (! initscr()) { 685 | return false; 686 | } 687 | 688 | set_escdelay(10); 689 | keypad(stdscr, true); 690 | 691 | nonl(); 692 | cbreak(); 693 | noecho(); 694 | 695 | if (has_colors()) { 696 | start_color(); 697 | 698 | init_pair(pairWhiteBlue, COLOR_WHITE, COLOR_BLUE); 699 | init_pair(pairBlackWhite, COLOR_BLACK, COLOR_WHITE); 700 | init_pair(pairRedWhite, COLOR_RED, COLOR_WHITE); 701 | init_pair(pairYellowBlue, COLOR_YELLOW, COLOR_BLUE); 702 | init_pair(pairGreenBlue, COLOR_GREEN, COLOR_BLUE); 703 | init_pair(pairBlackCyan, COLOR_BLACK, COLOR_CYAN); 704 | init_pair(pairGreenBlack, COLOR_GREEN, COLOR_BLACK); 705 | init_pair(pairWhiteCyan, COLOR_WHITE, COLOR_CYAN); 706 | init_pair(pairWhiteRed, COLOR_WHITE, COLOR_RED); 707 | init_pair(pairWhiteGreen, COLOR_WHITE, COLOR_GREEN); 708 | init_pair(pairBlackYellow, COLOR_BLACK, COLOR_YELLOW); 709 | } 710 | 711 | curs_set(0); 712 | 713 | return true; 714 | } // end initialize 715 | 716 | //-------------------------------------------------------------------- 717 | // Visible difference between insert and overstrike mode 718 | 719 | void showCursor(bool over=false) 720 | { 721 | over ? curs_set(2) : curs_set(1); 722 | 723 | #if SET_CURSOR_COLOR 724 | over ? printf("\e]12;%s\a", colorDelete) : printf("\e]12;%s\a", colorInsert); 725 | 726 | fflush(stdout); 727 | #endif 728 | } 729 | 730 | void hideCursor() 731 | { 732 | curs_set(0); 733 | } 734 | 735 | //-------------------------------------------------------------------- 736 | // Shutdown ncurses 737 | 738 | void shutdown() 739 | { 740 | free(sm4rt); 741 | 742 | delwin(winInput); 743 | delwin(winHelp); 744 | 745 | showCursor(); 746 | 747 | endwin(); 748 | } 749 | 750 | //-------------------------------------------------------------------- 751 | // Error exit ncurses 752 | 753 | void exitMsg(int status, const char* message) 754 | { 755 | shutdown(); 756 | 757 | errx(status, message); 758 | } 759 | 760 | //-------------------------------------------------------------------- 761 | // Reset variables for ascii mode 762 | 763 | void setViewMode() 764 | { 765 | lineWidth = modeAscii ? lineWidthAsc : lineWidthAsc / 4; 766 | 767 | bufSize = numLines * lineWidth; 768 | 769 | searchIndent = lineWidth * 3; 770 | 771 | steps[cmmMoveByte] = 1; 772 | steps[cmmMoveLine] = lineWidth; 773 | steps[cmmMovePage] = bufSize - lineWidth; 774 | steps[cmmMoveAll] = 0; 775 | } 776 | 777 | //-------------------------------------------------------------------- 778 | // Set variables for dynamic width ##:y 779 | 780 | void calcScreenLayout() 781 | { 782 | if (COLS < minScreenWidth) { 783 | string err("The screen must be at least " + to_string(minScreenWidth) + " characters wide."); 784 | 785 | exitMsg(31, err.c_str()); 786 | } 787 | 788 | if (LINES < minScreenHeight) { 789 | string err("The screen must be at least " + to_string(minScreenHeight) + " lines high."); 790 | 791 | exitMsg(32, err.c_str()); 792 | } 793 | 794 | short tera = sizeTera ? 3 : 0; // use large addresses only if needed 795 | 796 | leftMar = 11 + tera; 797 | 798 | if (COLS >= 140 + tera) { 799 | lineWidth = 32; 800 | screenWidth = 140 + tera; 801 | leftMar2 = 108 + tera; 802 | } 803 | else if (COLS >= 108 + tera) { 804 | lineWidth = 24; 805 | screenWidth = 108 + tera; 806 | leftMar2 = 84 + tera; 807 | } 808 | else { 809 | lineWidth = 16; 810 | screenWidth = 76 + tera; 811 | leftMar2 = 60 + tera; 812 | } 813 | 814 | lineWidthAsc = lineWidth * 4; 815 | 816 | inWidth = (sizeTera ? 15 : 11) + 1; // sign 817 | 818 | linesTotal = LINES; 819 | 820 | numLines = linesTotal / (singleFile ? 1 : 2) - 1; 821 | 822 | setViewMode(); 823 | } // end calcScreenLayout 824 | 825 | //-------------------------------------------------------------------- 826 | // Convert a character to uppercase 827 | 828 | int upCase(int c) 829 | { 830 | return (c >= 'a' && c <= 'z') ? c & ~0x20 : c; 831 | } 832 | 833 | //-------------------------------------------------------------------- 834 | // Convert buffer to lowercase 835 | 836 | void lowCase(Byte* buf, Size len) 837 | { 838 | Byte* b = bufFile1; // movdqu ==> movdqa 839 | 840 | if (len == staticSize) { // SIMD 841 | for (Size i=0; i < staticSize; ++i) { 842 | b[i] = b[i] >= 'A' && b[i] <= 'Z' ? b[i] | 0x20 : b[i]; 843 | } 844 | } 845 | 846 | else { 847 | for (Size i=0; i < len; ++i) { 848 | if (buf[i] <= 'Z' && buf[i] >= 'A') { 849 | buf[i] |= 0x20; 850 | } 851 | } 852 | } 853 | } 854 | 855 | //-------------------------------------------------------------------- 856 | // Convert buffer for ascii 857 | 858 | void setAscii(Size len) 859 | { 860 | Byte* b = bufFile1; // enregister 861 | 862 | if (len == staticSize) { // SIMD 863 | for (Size i=0; i < staticSize; ++i) { 864 | b[i] = b[i] > '~' || b[i] < ' ' ? ' ' : b[i]; 865 | } 866 | } 867 | 868 | else { 869 | for (Size i=0; i < len; ++i) { 870 | if (b[i] > '~' || b[i] < ' ') { 871 | b[i] = ' '; 872 | } 873 | } 874 | } 875 | } 876 | 877 | //-------------------------------------------------------------------- 878 | // Compare lines for smartScroll: 16 / 32 / 64 / 96 / 128 Byte 879 | 880 | bool cmpLine(Byte* ln1, Byte* ln2, Full cnt) 881 | { 882 | Quad q1 = _mm_load_si128((Quad*) ln1), 883 | q2 = _mm_load_si128((Quad*) ln2); 884 | 885 | q1 = _mm_cmpeq_epi8(q1, q2); 886 | 887 | if (_mm_movemask_epi8(q1) != 0xFFFF) { 888 | return true; 889 | } 890 | 891 | if (cnt == 16) { 892 | return false; 893 | } 894 | 895 | q1 = _mm_load_si128((Quad*) (ln1 + 16)); 896 | q2 = _mm_load_si128((Quad*) (ln2 + 16)); 897 | 898 | q1 = _mm_cmpeq_epi8(q1, q2); 899 | 900 | if (_mm_movemask_epi8(q1) != 0xFFFF) { 901 | return true; 902 | } 903 | 904 | for (Full i = 32; i < cnt; i += 16) { 905 | q1 = _mm_load_si128((Quad*) (ln1 + i)); 906 | q2 = _mm_load_si128((Quad*) (ln2 + i)); 907 | 908 | q1 = _mm_cmpeq_epi8(q1, q2); 909 | 910 | if (_mm_movemask_epi8(q1) != 0xFFFF) { 911 | return true; 912 | } 913 | } 914 | 915 | return false; 916 | } // end cmpLine 917 | 918 | //-------------------------------------------------------------------- 919 | // Compare lines for smartScroll (unaligned): 24 Byte 920 | 921 | bool cmpLineU(Byte* ln1, Byte* ln2, Full cnt) 922 | { 923 | Quad q1 = _mm_loadu_si128((Quad*) ln1), 924 | q2 = _mm_loadu_si128((Quad*) ln2); 925 | 926 | q1 = _mm_cmpeq_epi8(q1, q2); 927 | 928 | if (_mm_movemask_epi8(q1) != 0xFFFF) { 929 | return true; 930 | } 931 | 932 | q1 = _mm_loadu_si128((Quad*) (ln1 + 8)); 933 | q2 = _mm_loadu_si128((Quad*) (ln2 + 8)); 934 | 935 | q1 = _mm_cmpeq_epi8(q1, q2); 936 | 937 | if (_mm_movemask_epi8(q1) != 0xFFFF) { 938 | return true; 939 | } 940 | 941 | return false; 942 | } 943 | 944 | //-------------------------------------------------------------------- 945 | // Convert hex string to bytes 946 | 947 | int packHex(char* buf) 948 | { 949 | Byte *pb = (Byte*) buf, 950 | *po = pb; 951 | 952 | for (Byte b; (b = *pb); ++pb) { 953 | if (b == ' ') { 954 | continue; 955 | } 956 | else { 957 | b = (b - (*pb++ > 64 ? 55 : 48)) << 4; 958 | 959 | b |= *pb - (*pb > 0x40 ? 0x37 : 0x30); 960 | 961 | *po++ = b; 962 | } 963 | } 964 | 965 | return po - (Byte*) buf; 966 | } 967 | 968 | //-------------------------------------------------------------------- 969 | // My pretty printer 970 | 971 | char *pretty(char *buffer, FPos *size, int sign) 972 | { 973 | char aBuf[64], 974 | *pa = aBuf, 975 | *pb = buffer; 976 | 977 | sprintf(aBuf, (sign ? "%+ld" : "%ld"), *size); 978 | 979 | int len = strlen(aBuf); 980 | 981 | while (len) { 982 | *pb++ = *pa++; 983 | 984 | if (sign) { 985 | --sign; 986 | --len; 987 | } 988 | else { 989 | if (--len && ! (len % 3)) { 990 | if (thouSep) { 991 | *pb++ = thouSep; 992 | } 993 | } 994 | } 995 | } 996 | *pb = 0; 997 | 998 | return buffer; 999 | } // end pretty 1000 | 1001 | //-------------------------------------------------------------------- 1002 | // Help window 1003 | 1004 | void displayHelp() 1005 | { 1006 | touchwin(winHelp); 1007 | wrefresh(winHelp); 1008 | wgetch(winHelp); 1009 | } 1010 | 1011 | //-------------------------------------------------------------------- 1012 | // Position the input window 1013 | 1014 | void positionInWin(Command cmd, short width, const char *title, short height=3) 1015 | { 1016 | if (wresize(winInput, height, width) != OK) { 1017 | exitMsg(41, "Failed to resize window."); 1018 | } 1019 | 1020 | wbkgd(winInput, attribStyle[cInputWin]); 1021 | werase(winInput); 1022 | 1023 | mvwin(winInput, 1024 | ((! singleFile && (cmd & cmgGotoBottom)) 1025 | ? ((cmd & cmgGotoTop) 1026 | ? numLines // Moving both 1027 | : numLines + numLines / 2) // Moving bottom 1028 | : (numLines - 1 ) / 2), // Moving top 1029 | (screenWidth - width) / 2); 1030 | 1031 | box(winInput, 0, 0); 1032 | 1033 | mvwaddstr(winInput, 0, (width - strlen(title)) / 2, title); 1034 | } 1035 | 1036 | //==================================================================== 1037 | // Class ConWindow ##:win 1038 | 1039 | class ConWindow 1040 | { 1041 | protected: 1042 | WINDOW *winW; 1043 | 1044 | public: 1045 | ConWindow() {} 1046 | ~ConWindow() { delwin(winW); winW = NULL; } 1047 | 1048 | void initW(short x, short y, short width, short height, Style style); 1049 | void updateW() { touchwin(winW); wrefresh(winW); } 1050 | int readKeyW() { return wgetch(winW); } 1051 | 1052 | void put(short x, short y, const char* s) { mvwaddstr(winW, y, x, s); } 1053 | void putAttribs(short x, short y, Style color, short count) { 1054 | mvwchgat(winW, y, x, count, attribStyle[color], colorStyle[color], NULL); } 1055 | 1056 | void setCursor(short x, short y) { wmove(winW, y, x); } 1057 | 1058 | }; // end ConWindow 1059 | 1060 | //==================================================================== 1061 | // Class ConWindow member functions 1062 | 1063 | //-------------------------------------------------------------------- 1064 | // Initialize the window 1065 | 1066 | void ConWindow::initW(short x, short y, short width, short height, Style attrib) 1067 | { 1068 | if (! (winW = newwin(height, width, y, x))) { 1069 | exitMsg(21, "Failed to create main window."); 1070 | } 1071 | 1072 | wbkgd(winW, attribStyle[attrib]); 1073 | 1074 | keypad(winW, TRUE); 1075 | } 1076 | 1077 | //==================================================================== 1078 | // Class FileDisplay ##:file 1079 | 1080 | class Difference; 1081 | 1082 | class FileDisplay 1083 | { 1084 | friend class Difference; 1085 | 1086 | ConWindow cwinF; 1087 | 1088 | const Difference *diffsF; 1089 | 1090 | File fd; 1091 | bool editable; 1092 | 1093 | Byte *dataF; 1094 | Byte *sAllF; 1095 | int dataSize; 1096 | FPos offset; 1097 | FPos prevOffset; 1098 | FPos diffOffset; 1099 | FPos lastOffset; 1100 | 1101 | int seekup; 1102 | int se4rch; 1103 | bool se4rchAll; 1104 | 1105 | public: 1106 | FPos searchOff; 1107 | FPos scrollOff; 1108 | FPos repeatOff; 1109 | FPos startAddr; 1110 | Size filesize; 1111 | char *filename; 1112 | bool two; 1113 | 1114 | public: 1115 | FileDisplay() {} 1116 | ~FileDisplay() { if (fd) close(fd); delete [] dataF; delete [] sAllF; } 1117 | 1118 | bool setFile(char* FileName); 1119 | void initF(int y, const Difference* Diff); 1120 | void resizeF(); 1121 | void updateF() { cwinF.updateW(); } 1122 | int readKeyF() { return cwinF.readKeyW(); } 1123 | 1124 | void display(); 1125 | void attr(short x, short y, Style color, short count) { cwinF.putAttribs(x, y, color, count); } 1126 | void busy(bool on, bool ic, bool np); 1127 | void highEdit(short count) { attr(0, 0, cHighEdit, count); } 1128 | 1129 | void edit(const FileDisplay* other); 1130 | void editOut(short outOffset); 1131 | bool WriteTail(FPos start); 1132 | bool assure(); 1133 | void progress1(); 1134 | void progress(wchar_t* bar, int count, int delay, int stint); 1135 | 1136 | void setLast() { lastOffset = offset; } 1137 | void setJump() { startAddr = offset; } 1138 | void getLast() { FPos tmp = offset; moveTo(lastOffset); lastOffset = tmp; } 1139 | void skip(bool upwards); 1140 | void sync(const FileDisplay* other); 1141 | void mark(Byte* searchFor, Size searchLen); 1142 | 1143 | void move(FPos step) { moveTo(offset + step); } 1144 | void moveTo(FPos newOffset); 1145 | void moveToEnd() { moveTo(filesize - steps[cmmMovePage]); } 1146 | 1147 | void moveForw(Byte* searchFor, Size searchLen); // __attribute__ ((__target__ ("sse2"))) 1148 | void moveBack(Byte* searchFor, Size searchLen); 1149 | 1150 | void seekForw(); 1151 | void seekBack(); 1152 | 1153 | void smartScroll(); 1154 | }; // end FileDisplay 1155 | 1156 | //==================================================================== 1157 | // Class Difference 1158 | 1159 | class Difference 1160 | { 1161 | friend void FileDisplay::display(); 1162 | 1163 | protected: 1164 | Byte *dataD; 1165 | FileDisplay *file1D; 1166 | FileDisplay *file2D; 1167 | 1168 | public: 1169 | Difference(FileDisplay* File1, FileDisplay* File2): 1170 | file1D(File1), file2D(File2) {} 1171 | ~Difference() { delete [] dataD; } 1172 | void resizeD() { dataD = new Byte[bufSize]; } 1173 | void compute(); 1174 | void differ(int cmd); 1175 | }; // end Difference 1176 | 1177 | //==================================================================== 1178 | // Object instantiation 1179 | 1180 | FileDisplay file1, file2; 1181 | 1182 | Difference diffs(&file1, &file2); 1183 | 1184 | //==================================================================== 1185 | // Class Difference member functions 1186 | // 1187 | // Compute differences ##:u 1188 | 1189 | void Difference::compute() 1190 | { 1191 | memset(dataD, 0, bufSize); 1192 | 1193 | int size1 = file1D->dataSize, 1194 | size2 = file2D->dataSize; 1195 | 1196 | Byte *buf1 = file1D->dataF, 1197 | *buf2 = file2D->dataF, 1198 | *data = dataD; // enregister 1199 | 1200 | int size = min(size1, size2); 1201 | 1202 | int diff = 0; 1203 | for (; diff < size; ++diff) { 1204 | if (*buf1++ != *buf2++) { 1205 | data[diff] = true; 1206 | } 1207 | } 1208 | 1209 | size = max(size1, size2); 1210 | 1211 | if (diff < size) { 1212 | memset(data + diff, true, size - diff); 1213 | } 1214 | 1215 | haveDiff = true; 1216 | } // end Difference::compute 1217 | 1218 | //-------------------------------------------------------------------- 1219 | // Search differences Next/Prev 1220 | 1221 | void Difference::differ(int cmd) 1222 | { 1223 | File fd1 = file1D->fd, 1224 | fd2 = file2D->fd; 1225 | 1226 | FPos of1 = file1D->offset, 1227 | of2 = file2D->offset; 1228 | 1229 | if (cmd == cmNextDiff) { 1230 | if (haveDiff) { // advance 1231 | Size diff = min(file1D->filesize - of1, file2D->filesize - of2); 1232 | 1233 | if (diff <= bufSize) { // indent end 1234 | file1.moveTo(of1 + diff - steps[cmmMovePage]); 1235 | file2.moveTo(of2 + diff - steps[cmmMovePage]); 1236 | 1237 | return; 1238 | } 1239 | 1240 | of1 += bufSize; 1241 | of2 += bufSize; 1242 | } 1243 | 1244 | else if (of1 == file1D->filesize && of2 == file2D->filesize) { 1245 | file1.moveToEnd(); 1246 | file2.moveToEnd(); 1247 | 1248 | return; 1249 | } 1250 | 1251 | SeekFile(fd1, of1); 1252 | SeekFile(fd2, of2); 1253 | 1254 | of1 -= of2; 1255 | 1256 | Size size; 1257 | do { 1258 | Size size1 = ReadFile(fd1, bufFile1, staticSize); 1259 | Size size2 = ReadFile(fd2, bufFile2, staticSize); 1260 | 1261 | size = min(size1, size2); 1262 | 1263 | if (size < staticSize || stopRead) { 1264 | break; 1265 | } 1266 | 1267 | if (memcmp(bufFile1, bufFile2, staticSize)) { 1268 | break; 1269 | } 1270 | 1271 | of2 += staticSize; // only one needed 1272 | 1273 | } while (1); 1274 | 1275 | Size i=0; 1276 | for (; i < size; i += 16) { 1277 | Quad q1 = _mm_load_si128((Quad*) (bufFile1 + i)), 1278 | q2 = _mm_load_si128((Quad*) (bufFile2 + i)); 1279 | 1280 | q1 = _mm_cmpeq_epi8(q1, q2); 1281 | 1282 | if ((_mm_movemask_epi8(q1)) != 0xFFFF) { 1283 | break; 1284 | } 1285 | } 1286 | 1287 | if (i > size) { 1288 | i = size; 1289 | } 1290 | 1291 | file1.moveTo(of2 + of1 + i); 1292 | file2.moveTo(of2 + i); 1293 | } 1294 | 1295 | else { // downwards 1296 | Size diff1 = 0, 1297 | diff2 = 0, 1298 | diff3 = min(of1, of2); 1299 | 1300 | if (diff3 <= bufSize) { 1301 | file1.move(-diff3); 1302 | file2.move(-diff3); 1303 | 1304 | return; 1305 | } 1306 | 1307 | do { 1308 | of1 -= staticSize; 1309 | of2 -= staticSize; 1310 | 1311 | if (of1 < 0) { 1312 | diff1 = of1; 1313 | of1 = 0; 1314 | } 1315 | 1316 | if (of2 < 0) { 1317 | diff2 = of2; 1318 | of2 = 0; 1319 | } 1320 | 1321 | SeekFile(fd1, of1); 1322 | SeekFile(fd2, of2); 1323 | 1324 | ReadFile(fd1, bufFile1, staticSize); 1325 | ReadFile(fd2, bufFile2, staticSize); 1326 | 1327 | if (of1 == 0 || of2 == 0 || stopRead) { 1328 | break; 1329 | } 1330 | 1331 | if (memcmp(bufFile1, bufFile2, staticSize)) { 1332 | break; 1333 | } 1334 | 1335 | } while (1); 1336 | 1337 | diff1 = staticSize - 1 + diff1; 1338 | diff2 = staticSize - 1 + diff2; 1339 | 1340 | diff3 = diff1 - diff2; 1341 | 1342 | if (diff3 > 0) { // maintain offset 1343 | of1 = of1 + diff3; 1344 | diff1 = (Size) bufFile1 + diff3 - 15; 1345 | diff3 = diff2; 1346 | diff2 = (Size) bufFile2 - 15; 1347 | } 1348 | else { 1349 | of2 = of2 - diff3; 1350 | diff2 = (Size) bufFile2 - diff3 - 15; 1351 | diff3 = diff1; 1352 | diff1 = (Size) bufFile1 - 15; 1353 | } 1354 | 1355 | for (; diff3 >= bufSize; diff3 -= 16) { // use only one var 1356 | Quad q1 = _mm_loadu_si128((Quad*) (diff1 + diff3)), 1357 | q2 = _mm_loadu_si128((Quad*) (diff2 + diff3)); 1358 | 1359 | q1 = _mm_cmpeq_epi8(q1, q2); 1360 | 1361 | if ((_mm_movemask_epi8(q1)) != 0xFFFF) { 1362 | file1.moveTo(of1 + diff3 - bufSize + 1); 1363 | file2.moveTo(of2 + diff3 - bufSize + 1); 1364 | 1365 | return; 1366 | } 1367 | } 1368 | 1369 | file1.moveTo(of1); 1370 | file2.moveTo(of2); 1371 | } 1372 | } // end Difference::differ 1373 | 1374 | //==================================================================== 1375 | // Class FileDisplay member functions 1376 | // 1377 | // Open a file for display 1378 | 1379 | bool FileDisplay::setFile(char* FileName) 1380 | { 1381 | filename = FileName; 1382 | 1383 | File probe = OpenFile(filename, true); 1384 | 1385 | if (probe > 0) { 1386 | editable = true; 1387 | close(probe); 1388 | } 1389 | 1390 | if ((fd = OpenFile(filename)) < 0) { 1391 | return false; 1392 | } 1393 | 1394 | if ((filesize = SeekFile(fd, 0, SEEK_END)) < 0) { 1395 | return false; 1396 | } 1397 | 1398 | if (filesize > 68719476736) { // 2**30*64 == 0x10**9 == 64GB 1399 | sizeTera = true; 1400 | } 1401 | 1402 | SeekFile(fd, 0); 1403 | 1404 | return true; 1405 | } // end FileDisplay::setFile 1406 | 1407 | void FileDisplay::resizeF() 1408 | { 1409 | delete [] dataF; 1410 | delete [] sAllF; 1411 | 1412 | dataF = new Byte[bufSize]; // max_align_t 0x10 1413 | sAllF = new Byte[bufSize]; 1414 | } 1415 | 1416 | //-------------------------------------------------------------------- 1417 | // Set member variables 1418 | 1419 | void FileDisplay::initF(int y, const Difference* Diff) 1420 | { 1421 | diffsF = Diff; 1422 | two = y ? true : false; 1423 | 1424 | cwinF.initW(0, y, screenWidth, numLines + 1, cMainWin); 1425 | 1426 | resizeF(); 1427 | 1428 | moveTo(startAddr); 1429 | } 1430 | 1431 | //-------------------------------------------------------------------- 1432 | // Display the file contents ##:disp 1433 | 1434 | void FileDisplay::display() 1435 | { 1436 | if (! fd) { 1437 | return; 1438 | } 1439 | 1440 | short first, 1441 | last, 1442 | row, 1443 | col, 1444 | idx, 1445 | lineLength; 1446 | 1447 | FPos lineOffset = offset; 1448 | 1449 | if (scrollOff) { 1450 | diffOffset = scrollOff - offset; 1451 | } 1452 | else if (offset != prevOffset) { 1453 | diffOffset = offset - prevOffset; 1454 | 1455 | prevOffset = offset; 1456 | } 1457 | 1458 | Byte pos = (scrollOff ? scrollOff + lineWidth : offset + bufSize) 1459 | * 100 1460 | / (filesize > bufSize ? filesize : bufSize); 1461 | 1462 | char bufStat[screenWidth + 1] = { 0 }; 1463 | memset(bufStat, ' ', screenWidth); 1464 | 1465 | char buf[96], 1466 | buf2[2][48]; 1467 | 1468 | sprintf(buf, " %s %s %d%% %s %s", 1469 | pretty(buf2[0], &offset, 0), 1470 | pretty(buf2[1], &diffOffset, 1), 1471 | pos > 100 ? 100 : pos, 1472 | ignoreCase ? "I" : "i", 1473 | editable ? "RW" : "RO"); 1474 | 1475 | short size_name = screenWidth - strlen(buf), 1476 | size_fname = strlen(filename); 1477 | 1478 | if (size_fname <= size_name) { 1479 | memcpy(bufStat, filename, size_fname); 1480 | } 1481 | else { 1482 | first = size_name / 4; 1483 | 1484 | memcpy(bufStat, filename, first); 1485 | memcpy(bufStat + first, " ... ", 5); 1486 | 1487 | last = size_name - first - 5; 1488 | 1489 | memcpy(bufStat + first + 5, filename + size_fname - last, last); 1490 | } 1491 | 1492 | memcpy(bufStat + size_name, buf, strlen(buf)); 1493 | 1494 | cwinF.put(0, 0, bufStat); 1495 | attr(0, 0, cName, strlen(bufStat)); 1496 | 1497 | if (lockState == lockBottom && ! two) { 1498 | attr(0, 0, cHighFile, size_name); 1499 | } 1500 | else if (lockState == lockTop && two) { 1501 | attr(0, 0, cHighFile, size_name); 1502 | } 1503 | 1504 | if (diffOffset < 0) { 1505 | char *pc = (char*) memchr(buf, '-', strlen(buf)); 1506 | 1507 | attr(size_name + (pc - buf), 0, cMatch, 1); 1508 | } 1509 | 1510 | char bufHex[screenWidth + 1] = { 0 }, 1511 | bufAsc[ lineWidth + 1] = { 0 }; 1512 | 1513 | for (row=0; row < numLines; ++row) { 1514 | memset(bufHex, ' ', screenWidth); 1515 | memset(bufAsc, ' ', lineWidth); 1516 | 1517 | if (*(sm4rt + row)) { 1518 | lineOffset += (lineWidth * (*(sm4rt + row))); 1519 | } 1520 | 1521 | char *pbufHex = bufHex; 1522 | 1523 | pbufHex += sprintf(pbufHex, "%0*lX ", sizeTera ? 12 : 9, lineOffset); 1524 | 1525 | lineLength = min(lineWidth, dataSize - row * lineWidth); 1526 | 1527 | for (col = idx = 0; col < lineLength; ++col, ++idx) { 1528 | Byte b = dataF[row * lineWidth + col]; 1529 | 1530 | if (! modeAscii) { 1531 | pbufHex += sprintf(pbufHex, "%02X ", b); 1532 | } 1533 | 1534 | if (isgraph(b)) { 1535 | bufAsc[idx] = b; 1536 | } 1537 | else if (isspace(b)) { 1538 | bufAsc[idx] = ' '; 1539 | } 1540 | else { 1541 | bufAsc[idx] = modeAscii ? ' ' : '.'; 1542 | } 1543 | } 1544 | *pbufHex = ' '; 1545 | 1546 | cwinF.put(0, row + 1, bufHex); 1547 | cwinF.put((modeAscii ? leftMar : leftMar2), row + 1, bufAsc); 1548 | 1549 | for (col=0; col < (sizeTera ? 11 : 8); ++col) { 1550 | if (*(bufHex + col) != '0') { 1551 | break; 1552 | } 1553 | } 1554 | attr(col, row + 1, cAddress, (sizeTera ? 12 : 9) - col); 1555 | 1556 | if (showRaster) { 1557 | if (sizeTera) { 1558 | attr(0, row + 1, cRaster, 1); 1559 | } 1560 | attr(sizeTera ? 4 : 1, row + 1, cRaster, 1); 1561 | attr(sizeTera ? 8 : 5, row + 1, cRaster, 1); 1562 | } 1563 | 1564 | if (! modeAscii && showRaster && bufHex[leftMar] != ' ') { 1565 | for (col=0; col <= lineWidth - 8; col += 8) { 1566 | attr(leftMar + col * 3 - 1, row + 1, cRaster, 1); 1567 | attr(leftMar2 + col , row + 1, cRaster, 1); 1568 | } 1569 | } 1570 | 1571 | if (haveDiff) { 1572 | for (col=0; col < lineWidth; ++col) { 1573 | if (diffsF->dataD[row * lineWidth + col]) { 1574 | attr(leftMar + col * 3, row + 1, cDiff, 2); 1575 | attr(leftMar2 + col , row + 1, cDiff, 1); 1576 | } 1577 | } 1578 | } 1579 | 1580 | if (se4rchAll) { 1581 | for (col=0; col < lineWidth; ++col) { 1582 | if (sAllF[row * lineWidth + col]) { 1583 | if (modeAscii) { 1584 | attr(leftMar + col , row + 1, cMatch, 1); 1585 | } 1586 | else { 1587 | attr(leftMar + col * 3, row + 1, cMatch, 2); 1588 | attr(leftMar2 + col , row + 1, cMatch, 1); 1589 | } 1590 | } 1591 | } 1592 | } 1593 | 1594 | if (se4rch && row >= (searchOff >= searchIndent ? searchIndent / lineWidth : 0)) { 1595 | for (col=0; se4rch && col < lineWidth; --se4rch, ++col) { 1596 | if (modeAscii) { 1597 | attr(leftMar + col , row + 1, cSearch, 1); 1598 | } 1599 | else { 1600 | attr(leftMar + col * 3, row + 1, cSearch, 2); 1601 | attr(leftMar2 + col , row + 1, cSearch, 1); 1602 | } 1603 | } 1604 | } 1605 | 1606 | if (seekup) { 1607 | if (seekup < 0 || row == numLines - 1) { 1608 | if (modeAscii) { 1609 | attr(leftMar , row + 1, cSeek, 1); 1610 | } 1611 | else { 1612 | attr(leftMar , row + 1, cSeek, 2); 1613 | attr(leftMar2, row + 1, cSeek, 1); 1614 | } 1615 | 1616 | seekup = 0; 1617 | } 1618 | } 1619 | 1620 | if (*(sm4rt + row)) { 1621 | for (col=0; col < lineWidth; ++col) { 1622 | if (modeAscii) { 1623 | attr(leftMar + col , row + 1, cDiff, 1); 1624 | } 1625 | else { 1626 | attr(leftMar + col * 3, row + 1, cDiff, 2); 1627 | attr(leftMar2 + col , row + 1, cDiff, 1); 1628 | } 1629 | } 1630 | } 1631 | 1632 | lineOffset += lineWidth; 1633 | } 1634 | 1635 | if (se4rchAll) { 1636 | se4rchAll = false; 1637 | } 1638 | 1639 | updateF(); 1640 | } // end FileDisplay::display 1641 | 1642 | //-------------------------------------------------------------------- 1643 | // Busy status 1644 | 1645 | void FileDisplay::busy(bool on=false, bool ic=false, bool np=false) 1646 | { 1647 | if (! fd) { 1648 | return; 1649 | } 1650 | 1651 | if (on) { 1652 | attr(screenWidth - (ic ? 4 : 2), 0, (np ? cHighBus2 : cHighBusy), ic ? 1 : 2); 1653 | updateF(); 1654 | } 1655 | else { 1656 | napms(150); 1657 | attr(screenWidth - (ic ? 4 : 2), 0, cName, ic ? 1 : 2); 1658 | 1659 | if (! singleFile && ! two) { 1660 | updateF(); 1661 | } 1662 | } 1663 | } 1664 | 1665 | //-------------------------------------------------------------------- 1666 | // Display the edit buffer ##:out 1667 | 1668 | void FileDisplay::editOut(short outOffset) 1669 | { 1670 | FPos lineOffset = offset + outOffset; 1671 | 1672 | char bufHex[screenWidth + 1] = { 0 }, 1673 | bufAsc[ lineWidth + 1] = { 0 }; 1674 | 1675 | for (int row=0; row < numLines; ++row) { 1676 | memset(bufHex, ' ', screenWidth); 1677 | memset(bufAsc, ' ', lineWidth); 1678 | 1679 | char *pbufHex = bufHex; 1680 | 1681 | pbufHex += sprintf(bufHex, "%0*lX ", sizeTera ? 12 : 9, lineOffset); 1682 | 1683 | int lineLength = min(lineWidth, (int) editBytes.size() - outOffset - row * lineWidth); 1684 | 1685 | for (int col=0; col < lineLength; ++col) { 1686 | Byte b = editBytes[outOffset + row * lineWidth + col]; 1687 | 1688 | pbufHex += sprintf(pbufHex, "%02X ", b); 1689 | 1690 | bufAsc[col] = isprint(b) ? b : '.'; 1691 | } 1692 | *pbufHex = ' '; 1693 | 1694 | cwinF.put(0, row + 1, bufHex); 1695 | cwinF.put(leftMar2, row + 1, bufAsc); 1696 | 1697 | if (showRaster) { 1698 | int col[] = { 0, 1, 4, 5, 8 }; 1699 | 1700 | for (int i = sizeTera ? 0 : 1; i < 5; i += 2) { 1701 | attr(col[i], row + 1, cRaster, 1); 1702 | } 1703 | } 1704 | 1705 | if (showRaster && bufHex[leftMar] != ' ') { 1706 | for (int col=0; col <= lineWidth - 8; col += 8) { 1707 | attr(leftMar + col * 3 - 1, row + 1, cRaster, 1); 1708 | attr(leftMar2 + col , row + 1, cRaster, 1); 1709 | } 1710 | } 1711 | 1712 | for (int c, col=0; col < lineLength; ++col) { 1713 | if ((c = editColor[outOffset + row * lineWidth + col])) { 1714 | attr(leftMar + col * 3, row + 1, (Style) c, 2); 1715 | attr(leftMar2 + col , row + 1, (Style) c, 1); 1716 | } 1717 | } 1718 | 1719 | lineOffset += lineWidth; 1720 | } 1721 | } // end FileDisplay::editOut 1722 | 1723 | //-------------------------------------------------------------------- 1724 | // Obtain confirmation for lengthy write 1725 | 1726 | bool FileDisplay::assure() 1727 | { 1728 | bool ret = true; 1729 | Size diff = filesize - offset; 1730 | 1731 | if (diff > warnResize) { 1732 | char str[96], 1733 | inp[4]; 1734 | 1735 | sprintf(str, " About to write *non-interruptable* %.1fGB!? {yes|no}: ", (double) diff / 1073741824); 1736 | 1737 | echo(); 1738 | for (;;) { 1739 | positionInWin(two ? cmgGotoBottom : cmgGotoTop, 1+ strlen(str) +5+1, " Attention! ", 5); 1740 | 1741 | mvwaddstr(winInput, 2, 1, str); 1742 | 1743 | wgetnstr(winInput, inp, 3); 1744 | 1745 | if (! strcmp(inp, "yes")) { 1746 | break; 1747 | } 1748 | 1749 | else if (! strcmp(inp, "no")) { 1750 | ret = false; 1751 | break; 1752 | } 1753 | } 1754 | noecho(); 1755 | } 1756 | updateF(); 1757 | hideCursor(); 1758 | 1759 | return ret; 1760 | } // end FileDisplay::assure 1761 | 1762 | //-------------------------------------------------------------------- 1763 | // Progress bar single 1764 | 1765 | void FileDisplay::progress1() 1766 | { 1767 | int blocks = 25, 1768 | delay = 4; 1769 | 1770 | hideCursor(); 1771 | positionInWin(two ? cmgGotoBottom : cmgGotoTop, 2+ blocks +2, ""); 1772 | 1773 | wchar_t bar[blocks + 1]; 1774 | memset(bar, 0, sizeof(bar)); 1775 | 1776 | for (int i=0; i < blocks; ++i) { 1777 | for (int j=0; j < 8; ++j) { 1778 | bar[i] = barSyms[j]; 1779 | 1780 | mvwaddwstr(winInput, 1, 2, bar); 1781 | wrefresh(winInput); 1782 | napms(delay); 1783 | } 1784 | } 1785 | napms(250); 1786 | } 1787 | 1788 | //-------------------------------------------------------------------- 1789 | // Progress bar multiple 1790 | 1791 | void FileDisplay::progress(wchar_t* bar, int count, int delay=0, int stint=1) 1792 | { 1793 | int pos = count * stint / 8, 1794 | sym = count * stint % 8; 1795 | 1796 | for (int i=0; i < stint; ++i) { 1797 | bar[pos] = barSyms[sym % 8]; 1798 | 1799 | if (! (++sym % 8)) { 1800 | ++pos; 1801 | } 1802 | 1803 | mvwaddwstr(winInput, 1, 2, bar); 1804 | wrefresh(winInput); 1805 | 1806 | if (delay) { 1807 | napms(delay); 1808 | } 1809 | } 1810 | } 1811 | 1812 | //-------------------------------------------------------------------- 1813 | // Append the remainder 1814 | 1815 | bool FileDisplay::WriteTail(FPos start) 1816 | { 1817 | bool insert = start > 0 ? true : false; 1818 | 1819 | FPos srcOff = offset + dataSize, 1820 | dstOff = offset + start * (insert ? 1 : -1); 1821 | Size remain = filesize - srcOff; 1822 | 1823 | int width = (screenWidth - 4) * 8, 1824 | level = (screenWidth / 3) * 8, 1825 | cargo = staticSize, 1826 | loops = mCeil(remain, cargo), 1827 | multi = 1, 1828 | scale = 0, 1829 | count = 0, 1830 | stage = 0, 1831 | round = 0, 1832 | final = 0; 1833 | 1834 | if (remain <= cargo) { 1835 | progress1(); 1836 | multi = 0; 1837 | } 1838 | 1839 | else if (loops > width) { 1840 | scale = mCeil(loops, width); 1841 | width = mCeil(loops, scale); 1842 | } 1843 | 1844 | else if (loops > level) { 1845 | width = loops; 1846 | } 1847 | 1848 | else { 1849 | cargo = remain / level >> 12; 1850 | cargo = cargo ? cargo : 1; 1851 | cargo *= 4096; // page align 1852 | 1853 | width = mCeil(remain, cargo); 1854 | } 1855 | 1856 | stage = width - 8; 1857 | 1858 | width = mCeil(width, 8); 1859 | 1860 | wchar_t bar[width + 1]; 1861 | memset(bar, 0, sizeof(bar)); 1862 | 1863 | #if SHOW_WRITE_SUMMARY 1864 | int term = time(NULL); 1865 | #endif 1866 | if (multi) { 1867 | Size laptime = 0, 1868 | laps = timer(); 1869 | 1870 | positionInWin(two ? cmgGotoBottom : cmgGotoTop, 2+ width +2, ""); 1871 | 1872 | if (insert) { 1873 | srcOff = filesize; // downwards 1874 | dstOff = filesize + start - dataSize; 1875 | } 1876 | 1877 | for (; remain > cargo; ++count) { // two cases - one loop 1878 | if (insert) { 1879 | srcOff -= cargo; 1880 | } 1881 | SeekFile(fd, srcOff); 1882 | ReadFile(fd, buffer, cargo); 1883 | 1884 | if (insert) { 1885 | dstOff -= cargo; 1886 | } 1887 | else { 1888 | srcOff += cargo; 1889 | } 1890 | 1891 | SeekFile(fd, dstOff); 1892 | if (! WriteFile(fd, buffer, cargo)) { 1893 | return false; 1894 | } 1895 | if (! insert) { 1896 | dstOff += cargo; 1897 | } 1898 | 1899 | remain -= cargo; 1900 | 1901 | if (scale && count % scale) { 1902 | continue; 1903 | } 1904 | 1905 | if (mScale >= stage) { 1906 | if (mScale == stage) { 1907 | laptime = timer(); 1908 | } 1909 | else { 1910 | final = timer(3, laptime) / ++round; 1911 | } 1912 | } 1913 | 1914 | progress(bar, mScale); 1915 | 1916 | laps = timer(3, laps); 1917 | 1918 | if (barDelay > laps) { 1919 | napms(barDelay - laps); 1920 | } 1921 | 1922 | laps = timer(); 1923 | } 1924 | 1925 | if (insert) { 1926 | srcOff -= remain; 1927 | dstOff -= remain; 1928 | } 1929 | } 1930 | 1931 | if (remain > 0) { 1932 | SeekFile(fd, srcOff); 1933 | ReadFile(fd, buffer, remain); 1934 | 1935 | SeekFile(fd, dstOff); 1936 | if (! WriteFile(fd, buffer, remain)) { 1937 | return false; 1938 | } 1939 | } 1940 | 1941 | if (! insert && ftruncate(fd, dstOff + remain) == ERR) { 1942 | return false; 1943 | } 1944 | 1945 | #if SHOW_WRITE_SUMMARY 1946 | if (filesize - offset > warnResize) { 1947 | term = time(NULL) - term; 1948 | 1949 | sprintf(bufTimer, " %dsec (%.1fmin) %ldMByte/s ", 1950 | term, 1951 | (float) term / 60, 1952 | (filesize - offset) / 1048576 / (term ? term : 1)); 1953 | } 1954 | #endif 1955 | if (multi) { 1956 | for (;mScale < width * 8; ++count) { 1957 | if (scale && count % scale) { 1958 | continue; 1959 | } 1960 | 1961 | progress(bar, mScale, final); // neat finish 1962 | } 1963 | 1964 | napms(400); 1965 | } 1966 | 1967 | return true; 1968 | } // end FileDisplay::WriteTail 1969 | 1970 | //-------------------------------------------------------------------- 1971 | // Edit the file ##:edit 1972 | 1973 | void FileDisplay::edit(const FileDisplay* other) 1974 | { 1975 | if (! editable) { 1976 | return; 1977 | } 1978 | 1979 | bool hiNib = true, 1980 | ascii = false, 1981 | changed = false; 1982 | 1983 | short x = 0, 1984 | y = 0, 1985 | outOffset; 1986 | 1987 | int cur, 1988 | endY, 1989 | endX, 1990 | key; 1991 | 1992 | editBytes.clear(); 1993 | editColor.clear(); 1994 | 1995 | for (int i=0; i < dataSize; ++i) { 1996 | editBytes.push_back(dataF[i]); 1997 | editColor.push_back(0); 1998 | } 1999 | 2000 | cwinF.setCursor(leftMar, 1); 2001 | showCursor(); 2002 | 2003 | for (;;) { 2004 | endY = editBytes.size() ? (editBytes.size() - 1) / lineWidth : 0; 2005 | endX = editBytes.size() ? (editBytes.size() - 1) % lineWidth : 0; 2006 | 2007 | if (y > endY) { 2008 | y = endY; 2009 | x = endX; 2010 | } 2011 | 2012 | if (y == endY && x > endX) { 2013 | x = endX; 2014 | } 2015 | 2016 | cur = y * lineWidth + x; 2017 | 2018 | outOffset = cur >= bufSize ? (cur - bufSize) / lineWidth + 1 : 0; 2019 | 2020 | editOut(outOffset * lineWidth); 2021 | 2022 | cwinF.setCursor((ascii ? leftMar2 + x : leftMar + 3 * x + ! hiNib), y - outOffset + 1); 2023 | 2024 | key = readKeyF(); 2025 | 2026 | switch (key) { 2027 | case KEY_ESCAPE: 2028 | goto done; 2029 | 2030 | case KEY_TAB: 2031 | hiNib = true; 2032 | ascii ^= true; 2033 | break; 2034 | 2035 | case KEY_IC: 2036 | changed = true; 2037 | 2038 | editBytes.insert(editBytes.begin() + cur, ascii ? ' ' : '\0'); 2039 | editColor.insert(editColor.begin() + cur, cInsert); 2040 | break; 2041 | 2042 | case KEY_DC: 2043 | if (editBytes.size()) { 2044 | changed = true; 2045 | 2046 | editBytes.erase(editBytes.begin() + cur); 2047 | editColor.erase(editColor.begin() + cur); 2048 | } 2049 | break; 2050 | 2051 | case KEY_HOME: 2052 | y = x = 0; 2053 | break; 2054 | 2055 | case KEY_END: 2056 | y = endY; 2057 | x = endX; 2058 | break; 2059 | 2060 | case KEY_LEFT: 2061 | if (! hiNib) { 2062 | hiNib = true; 2063 | break; 2064 | } 2065 | 2066 | else { 2067 | if (! ascii) { 2068 | hiNib = false; 2069 | } 2070 | 2071 | if (--x < 0) { 2072 | x = y ? lineWidth - 1 : endX; 2073 | } 2074 | else { 2075 | break; 2076 | } 2077 | } // fall thru 2078 | 2079 | case KEY_UP: 2080 | if (--y < 0) { 2081 | y = endY; 2082 | 2083 | if (x > endX) { 2084 | --y; 2085 | } 2086 | } 2087 | break; 2088 | 2089 | default: { 2090 | short newByte = -1; 2091 | 2092 | if (key == KEY_RETURN && other && other->dataSize > (cur - outOffset * lineWidth)) { 2093 | newByte = other->dataF[cur - outOffset * lineWidth]; 2094 | 2095 | hiNib = false; // advance 2096 | } 2097 | 2098 | else if (ascii && isprint(key)) { 2099 | newByte = key; 2100 | } 2101 | 2102 | else { 2103 | if (isxdigit(key)) { 2104 | newByte = upCase(key) - (isdigit(key) ? 48 : 55); 2105 | 2106 | if (hiNib) { 2107 | newByte <<= 4; 2108 | } 2109 | 2110 | newByte |= editBytes[cur] & (hiNib ? 0x0F : 0xF0); 2111 | } 2112 | } 2113 | 2114 | if (newByte < 0) { 2115 | break; 2116 | } 2117 | 2118 | changed = true; 2119 | 2120 | editBytes[cur] = newByte; 2121 | 2122 | editColor[cur] = (cur < dataSize && dataF[cur] == newByte) ? 0 : cEdit; 2123 | } // fall thru 2124 | 2125 | case KEY_RIGHT: 2126 | if (hiNib && ! ascii) { 2127 | hiNib = false; 2128 | break; 2129 | } 2130 | 2131 | hiNib = true; 2132 | 2133 | if (++x == lineWidth) { 2134 | x = 0; 2135 | } 2136 | 2137 | if (y == endY && x > endX) { 2138 | x = 0; 2139 | } 2140 | 2141 | if (x) { 2142 | break; 2143 | } // fall thru 2144 | 2145 | case KEY_DOWN: 2146 | if (++y > endY) { 2147 | y = 0; 2148 | } 2149 | 2150 | if (y == endY && x > endX) { 2151 | y = 0; 2152 | } 2153 | } 2154 | } 2155 | 2156 | done: 2157 | if (changed) { 2158 | changed = false; 2159 | 2160 | int size = editBytes.size(); 2161 | Byte buf[size]; 2162 | 2163 | for (int i=0; i < size; ++i) { 2164 | buf[i] = editBytes[i]; 2165 | } 2166 | 2167 | if (size == dataSize) { 2168 | if (! memcmp(buf, dataF, size)) { 2169 | goto done; 2170 | } 2171 | } 2172 | 2173 | if (! sizeTera && filesize + size - dataSize > 68719476736) { // very special case 2174 | hideCursor(); 2175 | positionInWin(two ? cmgGotoBottom : cmgGotoTop, 1+ 14 +1, "", 5); 2176 | 2177 | mvwaddstr(winInput, 2, 1, " File >64GB "); 2178 | wgetch(winInput); 2179 | goto done; 2180 | } 2181 | 2182 | positionInWin(two ? cmgGotoBottom : cmgGotoTop, 1+ 19 +3+1, ""); 2183 | 2184 | mvwaddstr(winInput, 1, 1, " Save changes [y]: "); 2185 | 2186 | key = wgetch(winInput); 2187 | 2188 | if (upCase(key) != 'Y') { 2189 | goto done; 2190 | } 2191 | 2192 | wechochar(winInput, key); 2193 | napms(500); 2194 | 2195 | bool ret = false; 2196 | 2197 | close(fd); 2198 | fd = OpenFile(filename, true); 2199 | 2200 | SeekFile(fd, offset); 2201 | 2202 | if (size == dataSize) { 2203 | ret = WriteFile(fd, buf, dataSize); 2204 | 2205 | progress1(); 2206 | } 2207 | 2208 | else if (size < dataSize) { 2209 | if (assure()) { 2210 | if (WriteFile(fd, buf, size)) { 2211 | ret = WriteTail(size * -1); 2212 | } 2213 | } 2214 | } 2215 | 2216 | else { // size > dataSize 2217 | if (assure()) { 2218 | SeekFile(fd, 0, SEEK_END); 2219 | 2220 | if (WriteFile(fd, buffer, size - dataSize)) { // check 2221 | if (WriteTail(size)) { 2222 | SeekFile(fd, offset); 2223 | 2224 | ret = WriteFile(fd, buf, size); 2225 | } 2226 | } 2227 | } 2228 | } 2229 | 2230 | if (ret) { 2231 | if (fsync(fd) == OK) { 2232 | if (close(fd) == ERR) { // seamless error tracking 2233 | ret = false; 2234 | } 2235 | 2236 | fd = -1; 2237 | } 2238 | else { 2239 | ret = false; 2240 | } 2241 | } 2242 | 2243 | if (fd > 0) { 2244 | close(fd); 2245 | } 2246 | 2247 | fd = OpenFile(filename); 2248 | 2249 | filesize = SeekFile(fd, 0, SEEK_END); 2250 | 2251 | move(0); 2252 | 2253 | updateF(); 2254 | 2255 | if (ret) { 2256 | positionInWin(two ? cmgGotoBottom : cmgGotoTop, 2257 | 1+ (*bufTimer ? strlen(bufTimer) : 11) +1, "", *bufTimer ? 7 : 5); 2258 | 2259 | mvwaddstr(winInput, 2, *bufTimer ? (strlen(bufTimer) - 11) / 2 + 1 : 1, " Success "); 2260 | 2261 | if (*bufTimer) { 2262 | mvwaddstr(winInput, 4, 1, bufTimer); 2263 | wgetch(winInput); 2264 | 2265 | *bufTimer = 0; 2266 | } 2267 | else { 2268 | wrefresh(winInput); 2269 | napms(900); 2270 | } 2271 | } 2272 | 2273 | else { 2274 | positionInWin(two ? cmgGotoBottom : cmgGotoTop, 1+ 11 +1, "", 5); 2275 | 2276 | mvwaddstr(winInput, 2, 1, " Failed! "); 2277 | wgetch(winInput); 2278 | } 2279 | } 2280 | 2281 | else { 2282 | hideCursor(); 2283 | } 2284 | } // end FileDisplay::edit 2285 | 2286 | //-------------------------------------------------------------------- 2287 | // Jump a specific percentage forward / backward 2288 | 2289 | void FileDisplay::skip(bool upwards=false) 2290 | { 2291 | FPos step = filesize / 100; 2292 | 2293 | if (upwards) { 2294 | move(step * -skipBack); 2295 | } 2296 | else { 2297 | move(step * skipForw); 2298 | } 2299 | } 2300 | 2301 | //-------------------------------------------------------------------- 2302 | // Synchronize the plains 2303 | // 2304 | // '1' | upper: sync file1 with file2 2305 | // '2' | lower: sync file2 with file1 2306 | 2307 | void FileDisplay::sync(const FileDisplay* other) 2308 | { 2309 | if (other->dataSize) { 2310 | moveTo(other->offset); 2311 | } 2312 | else { 2313 | moveToEnd(); 2314 | } 2315 | } 2316 | 2317 | //-------------------------------------------------------------------- 2318 | // Mark all search strings 2319 | 2320 | void FileDisplay::mark(Byte* searchFor, Size searchLen) 2321 | { 2322 | Byte ign[dataSize]; 2323 | 2324 | Byte* src = ignoreCase ? ign : dataF; 2325 | 2326 | if (ignoreCase) { 2327 | memcpy(ign, dataF, dataSize); 2328 | 2329 | lowCase(ign, dataSize); 2330 | } 2331 | 2332 | memset(sAllF, 0, bufSize); 2333 | 2334 | for (int i=0; i < dataSize; ++i) { 2335 | Byte* p; 2336 | 2337 | if ((p = (Byte*) memmem(src + i, dataSize - i, searchFor, searchLen))) { 2338 | for (int j=0; j < searchLen; ++j) { 2339 | *(sAllF + (p - src) + j) = true; 2340 | } 2341 | 2342 | i += searchLen - 1; 2343 | } 2344 | } 2345 | 2346 | se4rchAll = true; 2347 | } 2348 | 2349 | //-------------------------------------------------------------------- 2350 | // Change the file position ##:to 2351 | 2352 | void FileDisplay::moveTo(FPos newOffset) 2353 | { 2354 | if (newOffset < 0) { 2355 | offset = 0; 2356 | } 2357 | else if (newOffset > filesize) { 2358 | offset = filesize; 2359 | } 2360 | else { 2361 | offset = newOffset; 2362 | } 2363 | 2364 | SeekFile(fd, offset); 2365 | 2366 | dataSize = ReadFile(fd, dataF, bufSize); 2367 | } 2368 | 2369 | //-------------------------------------------------------------------- 2370 | // Change the file position by searching 2371 | 2372 | void FileDisplay::moveForw(Byte* searchFor, Size searchLen) 2373 | { 2374 | FPos newPos = searchOff > 0 ? searchOff + 1 : (searchOff < 0 ? 1 : offset); 2375 | 2376 | Size bias = 0, 2377 | hint = 0; 2378 | 2379 | Quad lead = _mm_setzero_si128(), 2380 | mask = _mm_set1_epi8(0xFF); 2381 | 2382 | for (Full i=0; i < (Full) searchLen % 16; ++i) { // shl128 only with immediate 2383 | mask = _mm_bslli_si128(mask, 1); 2384 | } 2385 | 2386 | while (! *(searchFor + bias) && bias < searchLen) { 2387 | ++bias; 2388 | } 2389 | 2390 | if (bias == searchLen) { 2391 | bias = 0; 2392 | } 2393 | else { 2394 | lead = _mm_set1_epi8(*(searchFor + bias)); 2395 | hint = 1; 2396 | } 2397 | 2398 | for (; ; newPos += staticSize - searchLen + 1) { 2399 | SeekFile(fd, newPos); 2400 | 2401 | Size bytesRead = ReadFile(fd, buffer, staticSize); 2402 | 2403 | if (bytesRead < searchLen || stopRead) { 2404 | break; 2405 | } 2406 | 2407 | if (ignoreCase) { 2408 | lowCase(buffer, bytesRead); 2409 | } 2410 | 2411 | for (Size i=0, todo=0, c; i <= bytesRead - searchLen; i += 16) { 2412 | Quad turbo = _mm_loadu_si128((Quad*) (buffer + i + bias)), 2413 | zero = _mm_setzero_si128(), 2414 | temp = _mm_cmpeq_epi8(turbo, zero); 2415 | 2416 | if (_mm_movemask_epi8(temp) == 0xFFFF) { 2417 | if (hint) { 2418 | continue; 2419 | } 2420 | } 2421 | 2422 | else { 2423 | temp = _mm_cmpeq_epi8(turbo, lead); 2424 | } 2425 | 2426 | if (! (todo = _mm_movemask_epi8(temp))) { 2427 | continue; 2428 | } 2429 | 2430 | do { 2431 | c = i + _bit_scan_forward(todo); 2432 | 2433 | if (searchFor[searchLen - 1] == buffer[c + searchLen - 1]) { 2434 | Size j = 0; 2435 | Quad s, 2436 | b; 2437 | 2438 | do { 2439 | s = _mm_loadu_si128((Quad*) (searchFor + j)); 2440 | b = _mm_loadu_si128((Quad*) (buffer + c + j)); 2441 | 2442 | if (searchLen < j + 16) { 2443 | break; 2444 | } 2445 | 2446 | temp = _mm_cmpeq_epi8(s, b); 2447 | 2448 | if (_mm_movemask_epi8(temp) != 0xFFFF) { 2449 | goto next; 2450 | } 2451 | 2452 | } while (j += 16); 2453 | 2454 | temp = _mm_cmpeq_epi8(s, b); 2455 | temp = _mm_or_si128(temp, mask); 2456 | 2457 | if (_mm_movemask_epi8(temp) != 0xFFFF) { 2458 | goto next; 2459 | } 2460 | 2461 | if (c <= bytesRead - searchLen) { // limit turbo 2462 | newPos = newPos + c; 2463 | searchOff = newPos ? newPos : -1; // tri-state 2464 | se4rch = searchLen; 2465 | 2466 | moveTo(newPos - (searchOff >= searchIndent ? searchIndent : 0)); 2467 | 2468 | mark(searchFor, searchLen); 2469 | return; 2470 | } 2471 | } 2472 | next: 2473 | asm ("btr{q %1, %0 | %0, %1}" : "+r" (todo) : "r" (c - i)); // use full line 2474 | 2475 | } while (todo); 2476 | } 2477 | } 2478 | 2479 | searchOff = 0; 2480 | 2481 | moveTo(stopRead ? newPos : filesize); 2482 | } // end FileDisplay::moveForw 2483 | 2484 | //-------------------------------------------------------------------- 2485 | // Change the file position by searching backwards 2486 | 2487 | void FileDisplay::moveBack(Byte* searchFor, Size searchLen) 2488 | { 2489 | FPos newPos = searchOff > 0 ? searchOff : offset; 2490 | 2491 | Size bias = 0, 2492 | hint = 0, 2493 | diff = 0; 2494 | 2495 | Quad lead = _mm_setzero_si128(), 2496 | mask = _mm_set1_epi8(0xFF); 2497 | 2498 | for (Full i=0; i < (Full) searchLen % 16; ++i) { 2499 | mask = _mm_bslli_si128(mask, 1); 2500 | } 2501 | 2502 | while (! *(searchFor + bias) && bias < searchLen) { 2503 | ++bias; 2504 | } 2505 | 2506 | if (bias == searchLen) { 2507 | bias = 0; 2508 | } 2509 | else { 2510 | lead = _mm_set1_epi8(*(searchFor + bias)); 2511 | hint = 1; 2512 | } 2513 | 2514 | if (newPos + searchLen - 1 > filesize) { 2515 | newPos = filesize - searchLen + 1; 2516 | } 2517 | 2518 | for (;;) { 2519 | newPos -= (staticSize - searchLen + 1); 2520 | 2521 | if (newPos < 0) { 2522 | diff = newPos; 2523 | newPos = 0; 2524 | } 2525 | 2526 | SeekFile(fd, newPos); 2527 | 2528 | Size bytesRead = ReadFile(fd, buffer, staticSize); 2529 | 2530 | if (ignoreCase) { 2531 | lowCase(buffer, bytesRead); 2532 | } 2533 | 2534 | for (Size i = staticSize - searchLen + diff, todo=0, c; i >= 0; i -= 16) { 2535 | Quad turbo = _mm_loadu_si128((Quad*) (buffer + i + bias - 15)), 2536 | zero = _mm_setzero_si128(), 2537 | temp = _mm_cmpeq_epi8(turbo, zero); 2538 | 2539 | if (_mm_movemask_epi8(temp) == 0xFFFF) { 2540 | if (hint) { 2541 | continue; 2542 | } 2543 | } 2544 | 2545 | else { 2546 | temp = _mm_cmpeq_epi8(turbo, lead); 2547 | } 2548 | 2549 | if (! (todo = _mm_movemask_epi8(temp))) { 2550 | continue; 2551 | } 2552 | 2553 | do { 2554 | c = i - 15 + _bit_scan_reverse(todo); 2555 | 2556 | if (searchFor[searchLen - 1] == buffer[c + searchLen - 1]) { 2557 | Size j = 0; 2558 | Quad s, 2559 | b; 2560 | 2561 | do { 2562 | s = _mm_loadu_si128((Quad*) (searchFor + j)); 2563 | b = _mm_loadu_si128((Quad*) (buffer + c + j)); 2564 | 2565 | if (searchLen < j + 16) { 2566 | break; 2567 | } 2568 | 2569 | temp = _mm_cmpeq_epi8(s, b); 2570 | 2571 | if (_mm_movemask_epi8(temp) != 0xFFFF) { 2572 | goto next; 2573 | } 2574 | 2575 | } while (j += 16); 2576 | 2577 | temp = _mm_cmpeq_epi8(s, b); 2578 | temp = _mm_or_si128(temp, mask); 2579 | 2580 | if (_mm_movemask_epi8(temp) != 0xFFFF) { 2581 | goto next; 2582 | } 2583 | 2584 | if (c >= 0) { 2585 | newPos = newPos + c; 2586 | searchOff = newPos ? newPos : -1; 2587 | se4rch = searchLen; 2588 | 2589 | moveTo(newPos - (searchOff >= searchIndent ? searchIndent : 0)); 2590 | 2591 | mark(searchFor, searchLen); 2592 | return; 2593 | } 2594 | } 2595 | next: 2596 | asm ("btr{q %1, %0 | %0, %1}" : "+r" (todo) : "r" (c - i + 15)); 2597 | 2598 | } while (todo); 2599 | } 2600 | 2601 | if (! newPos || stopRead) { 2602 | break; 2603 | } 2604 | } 2605 | 2606 | searchOff = 0; 2607 | 2608 | moveTo(newPos); 2609 | } // end FileDisplay::moveBack 2610 | 2611 | //-------------------------------------------------------------------- 2612 | // Seek forward to next byte not equal to current head + bufSize 2613 | 2614 | void FileDisplay::seekForw() 2615 | { 2616 | Byte *pTakeThat = dataF + dataSize - 1, 2617 | searchFor = modeAscii && ! isprint(*pTakeThat) ? ' ' : *pTakeThat; 2618 | 2619 | Quad lead = _mm_set1_epi8(searchFor); 2620 | 2621 | FPos newPos = offset + dataSize; 2622 | 2623 | Size here = -1; 2624 | 2625 | for (Size bytesRead; ; newPos += staticSize) { 2626 | SeekFile(fd, newPos); 2627 | 2628 | bytesRead = ReadFile(fd, buffer, staticSize); 2629 | 2630 | if (bytesRead <= 0 || stopRead) { 2631 | break; 2632 | } 2633 | 2634 | if (modeAscii) { 2635 | setAscii(bytesRead); 2636 | } 2637 | 2638 | for (Size i=0, m; i < bytesRead; i += 16) { 2639 | Quad turbo = _mm_load_si128((Quad*) (buffer + i)); // aligned 2640 | 2641 | turbo = _mm_cmpeq_epi8(turbo, lead); 2642 | 2643 | if ((m = _mm_movemask_epi8(turbo)) != 0xFFFF) { 2644 | here = i + _bit_scan_forward((Word) ~m); 2645 | 2646 | goto done; 2647 | } 2648 | } 2649 | } 2650 | done: 2651 | if (here >= 0) { 2652 | seekup = -1; 2653 | 2654 | moveTo(newPos + here); 2655 | } 2656 | 2657 | else if (stopRead) { 2658 | moveTo(newPos); 2659 | } 2660 | 2661 | else { 2662 | moveToEnd(); 2663 | } 2664 | } // end FileDisplay::seekForw 2665 | 2666 | //-------------------------------------------------------------------- 2667 | // Seek backward to next byte not equal to current head 2668 | 2669 | void FileDisplay::seekBack() 2670 | { 2671 | Byte searchFor = modeAscii && ! isprint(*dataF) ? ' ' : *dataF; 2672 | 2673 | Quad lead = _mm_set1_epi8(searchFor); 2674 | 2675 | FPos newPos = offset - staticSize; 2676 | 2677 | Size diff = 0, 2678 | here = -1; 2679 | 2680 | for (Size bytesRead; ; newPos -= staticSize) { 2681 | if (newPos < 0) { 2682 | diff = newPos; 2683 | newPos = 0; 2684 | } 2685 | 2686 | SeekFile(fd, newPos); 2687 | 2688 | if ((bytesRead = ReadFile(fd, buffer, staticSize)) <= 0) { 2689 | break; 2690 | } 2691 | 2692 | if (modeAscii) { 2693 | setAscii(bytesRead); 2694 | } 2695 | 2696 | for (Size i = staticSize - 1 + diff, m; i >= 0; i -= 16) { 2697 | Quad turbo = _mm_loadu_si128((Quad*) (buffer + i - 15)); 2698 | 2699 | turbo = _mm_cmpeq_epi8(turbo, lead); 2700 | 2701 | if ((m = _mm_movemask_epi8(turbo)) != 0xFFFF) { 2702 | here = i + _bit_scan_reverse((Word) ~m) - 15; 2703 | 2704 | goto done; 2705 | } 2706 | } 2707 | 2708 | if (! newPos || stopRead) { 2709 | break; 2710 | } 2711 | } 2712 | done: 2713 | if (here >= 0) { 2714 | newPos += here - steps[cmmMovePage]; 2715 | 2716 | if (newPos >= 0) { 2717 | seekup = 1; 2718 | } 2719 | 2720 | moveTo(newPos); 2721 | } 2722 | 2723 | else if (stopRead) { 2724 | moveTo(newPos); 2725 | } 2726 | 2727 | else { 2728 | moveTo(0); 2729 | } 2730 | } // end FileDisplay::seekBack 2731 | 2732 | //-------------------------------------------------------------------- 2733 | // Scroll forward with skipping same content lines 2734 | 2735 | void FileDisplay::smartScroll() 2736 | { 2737 | bool (*pCmp) (Byte*, Byte*, Full) = lineWidth == 24 ? cmpLineU : cmpLine; 2738 | 2739 | FPos newPos = scrollOff; 2740 | 2741 | if (! newPos) { // advance 2742 | newPos = offset & ~0xF; 2743 | 2744 | if (filesize - newPos > steps[cmmMovePage]) { 2745 | moveTo(newPos); 2746 | 2747 | newPos += steps[cmmMovePage]; 2748 | 2749 | Size end = (Size) dataF + (numLines - 1) * lineWidth; 2750 | 2751 | for (Byte* p = dataF; (Size) p < end; p += lineWidth) { // scroll if possible 2752 | if (! pCmp(p, p + lineWidth, lineWidth)) { 2753 | newPos -= steps[cmmMovePage]; 2754 | break; 2755 | } 2756 | } 2757 | } 2758 | } 2759 | 2760 | if (filesize - newPos <= steps[cmmMovePage]) { 2761 | moveToEnd(); 2762 | 2763 | scrollOff = 0; 2764 | return; 2765 | } 2766 | 2767 | offset = newPos; 2768 | 2769 | SeekFile(fd, newPos); 2770 | 2771 | Size bytesRead = ReadFile(fd, buffer, staticSize); 2772 | 2773 | if (modeAscii) { 2774 | setAscii(bytesRead); 2775 | } 2776 | 2777 | FPos repeat = 0; 2778 | 2779 | memcpy(dataF, buffer, lineWidth); 2780 | bytesRead -= lineWidth; 2781 | 2782 | Size i = 1, j = 1; 2783 | for (; bytesRead > 0;) { 2784 | if (pCmp(dataF + (i - 1) * lineWidth, buffer + j * lineWidth, lineWidth)) { 2785 | memcpy(dataF + i * lineWidth, buffer + j * lineWidth, lineWidth); 2786 | 2787 | if (repeat) { 2788 | *(sm4rt + i) = repeat; 2789 | repeat = 0; 2790 | } 2791 | 2792 | if (i == numLines - 1) { 2793 | break; 2794 | } 2795 | 2796 | ++i; 2797 | } 2798 | 2799 | else { 2800 | ++repeat; 2801 | } 2802 | 2803 | bytesRead -= lineWidth; 2804 | ++j; 2805 | 2806 | if (bytesRead < lineWidth) { 2807 | newPos += j * lineWidth; 2808 | j = 0; 2809 | 2810 | SeekFile(fd, newPos); 2811 | 2812 | bytesRead = ReadFile(fd, buffer, staticSize); 2813 | 2814 | if (modeAscii) { 2815 | setAscii(bytesRead); 2816 | } 2817 | 2818 | if (bytesRead < lineWidth || stopRead) { 2819 | if (bytesRead > 0) { 2820 | *(sm4rt + i) = repeat; 2821 | 2822 | memcpy(dataF + i * lineWidth, buffer, min(bytesRead, lineWidth)); 2823 | } 2824 | 2825 | else { 2826 | if (repeat) { 2827 | *(sm4rt + i) = --repeat; 2828 | 2829 | memcpy(dataF + i * lineWidth, dataF + (i - 1) * lineWidth, lineWidth); 2830 | ++i; // tricky! 2831 | } 2832 | } 2833 | 2834 | break; 2835 | } 2836 | } 2837 | } 2838 | 2839 | scrollOff = newPos + j * lineWidth; 2840 | 2841 | dataSize = i * lineWidth + min(bytesRead, lineWidth); 2842 | } // end FileDisplay::smartScroll 2843 | 2844 | //==================================================================== 2845 | // Class InputManager 2846 | 2847 | class InputManager 2848 | { 2849 | private: 2850 | char *buf; 2851 | const char *restrictChar; 2852 | StrDeq &history; 2853 | size_t historyPos; 2854 | string historyInp; 2855 | int maxLen; 2856 | int step; 2857 | int len = 0; 2858 | int cur = 0; 2859 | bool upcase; 2860 | bool splitHex; 2861 | bool overStrike = false; 2862 | 2863 | private: 2864 | void useHistory(int delta); 2865 | 2866 | public: 2867 | InputManager(char* Buf, int MaxLen, StrDeq& History): 2868 | buf(Buf), history(History), historyPos(History.size()), maxLen(MaxLen) {} 2869 | ~InputManager() {} 2870 | 2871 | void setCharacters(const char* RestrictChar) { restrictChar = RestrictChar; } 2872 | void setSplitHex(bool SplitHex) { splitHex = SplitHex; } 2873 | void setUpcase(bool Upcase) { upcase = Upcase; } 2874 | void setStep(int Step) { step = Step; } 2875 | 2876 | void run(); 2877 | }; // end InputManager 2878 | 2879 | //==================================================================== 2880 | // Class InputManager member functions 2881 | 2882 | //-------------------------------------------------------------------- 2883 | // Switch the current input line with one from the history 2884 | 2885 | void InputManager::useHistory(int delta) 2886 | { 2887 | if (historyPos == history.size()) { 2888 | historyInp.assign(buf, len); 2889 | } 2890 | 2891 | historyPos += delta; 2892 | 2893 | string s = historyPos == history.size() ? historyInp : history[historyPos]; 2894 | 2895 | cur = len = s.size(); 2896 | 2897 | memset(buf, ' ', maxLen); 2898 | 2899 | memcpy(buf, s.data(), len); 2900 | } 2901 | 2902 | //-------------------------------------------------------------------- 2903 | // Run the main loop to get an input string ##:run 2904 | 2905 | void InputManager::run() 2906 | { 2907 | memset(buf, ' ', maxLen); 2908 | buf[maxLen] = 0; 2909 | 2910 | showCursor(); 2911 | 2912 | for (;;) { 2913 | mvwaddstr(winInput, 1, 2, buf); 2914 | wmove(winInput, 1, 2 + cur); 2915 | 2916 | int key = wgetch(winInput); 2917 | 2918 | if (upcase) { 2919 | key = upCase(key); 2920 | } 2921 | 2922 | if (isprint(key)) { 2923 | if (restrictChar && ! strchr(restrictChar, key)) { 2924 | continue; 2925 | } 2926 | 2927 | if (overStrike) { 2928 | if (cur >= maxLen) { 2929 | continue; 2930 | } 2931 | } 2932 | 2933 | else { 2934 | if (! (cur % step)) { 2935 | if (len + step > maxLen) { 2936 | continue; 2937 | } 2938 | 2939 | if (cur != len) { // true insert 2940 | memmove(buf + cur + step, buf + cur, len - cur); 2941 | 2942 | len += step; 2943 | 2944 | if (splitHex) { 2945 | buf[cur + 1] = ' '; 2946 | } 2947 | } 2948 | } 2949 | } 2950 | 2951 | mEdit 2952 | 2953 | buf[cur++] = key; 2954 | 2955 | if (splitHex && cur % 3 == 2) { 2956 | ++cur; 2957 | } 2958 | 2959 | if (cur > len) { 2960 | len = cur; 2961 | } 2962 | } 2963 | 2964 | else { 2965 | if (key == KEY_IC) { 2966 | overStrike ^= true; 2967 | showCursor(overStrike); 2968 | continue; 2969 | } 2970 | 2971 | if (splitHex && cur) { // normalize 2972 | if (buf[cur] == ' ' && buf[cur - 1] != ' ') { 2973 | buf[cur] = buf[cur - 1]; 2974 | buf[cur - 1] = '0'; 2975 | 2976 | if (cur == len) { 2977 | len += 2; 2978 | } 2979 | } 2980 | 2981 | cur -= cur % step; 2982 | } 2983 | 2984 | switch (key) { 2985 | case KEY_ESCAPE: 2986 | case KEY_RETURN: 2987 | buf[key == KEY_RETURN ? len : 0] = 0; 2988 | goto done; 2989 | 2990 | case KEY_LEFT: 2991 | case KEY_RIGHT: 2992 | if (key == KEY_LEFT ? cur : cur < len) { 2993 | cur = cur + (key == KEY_LEFT ? -step : step); 2994 | } 2995 | break; 2996 | 2997 | case KEY_HOME: 2998 | case KEY_END: 2999 | cur = key == KEY_END ? len : 0; 3000 | break; 3001 | 3002 | case KEY_UP: 3003 | if (historyPos) { 3004 | useHistory(-1); 3005 | } 3006 | break; 3007 | 3008 | case KEY_DOWN: 3009 | if (historyPos < history.size()) { 3010 | useHistory(+1); 3011 | } 3012 | break; 3013 | 3014 | case KEY_DC: 3015 | if (cur >= len) { 3016 | continue; 3017 | } 3018 | 3019 | mEdit 3020 | 3021 | memmove(buf + cur, buf + cur + step, len - cur - step); 3022 | memset(buf + len - step, ' ', step); 3023 | 3024 | len -= step; 3025 | break; 3026 | 3027 | case KEY_BACKSPACE: 3028 | if (! cur) { 3029 | continue; 3030 | } 3031 | 3032 | mEdit 3033 | 3034 | memmove(buf + cur - step, buf + cur, len - cur); 3035 | memset(buf + len - step, ' ', step); 3036 | 3037 | cur -= step; 3038 | len -= step; 3039 | break; 3040 | 3041 | case KEY_CTRL_U: // unix-line-discard 3042 | mEdit 3043 | 3044 | memmove(buf, buf + cur, len - cur); 3045 | memset(buf + len - cur, ' ', cur); 3046 | 3047 | len -= cur; 3048 | cur = 0; 3049 | break; 3050 | 3051 | case KEY_CTRL_K: // kill-line 3052 | mEdit 3053 | 3054 | memset(buf + cur, ' ', len - cur); 3055 | len = cur; 3056 | } 3057 | } 3058 | } 3059 | 3060 | done: 3061 | hideCursor(); 3062 | 3063 | if (*buf) { 3064 | for (auto exists = history.begin(); exists != history.end(); ++exists) { 3065 | if (*exists == buf) { 3066 | history.erase(exists); 3067 | break; 3068 | } 3069 | } 3070 | 3071 | if (history.size() == maxHistory) { 3072 | history.pop_front(); 3073 | } 3074 | 3075 | history.push_back(buf); 3076 | } 3077 | 3078 | return; 3079 | } // end InputManager::run 3080 | 3081 | //==================================================================== 3082 | // Global Functions which uses Objects 3083 | 3084 | //-------------------------------------------------------------------- 3085 | // Get a string using InputManager 3086 | 3087 | void getString(char* buf, int maxlen, StrDeq& history, 3088 | const char* restrictChar=NULL, bool upcase=false, bool splitHex=false) 3089 | { 3090 | InputManager manager(buf, maxlen, history); 3091 | 3092 | manager.setCharacters(restrictChar); 3093 | manager.setSplitHex(splitHex); 3094 | manager.setUpcase(upcase); 3095 | manager.setStep(splitHex ? 3 : 1); 3096 | 3097 | manager.run(); 3098 | } 3099 | 3100 | //-------------------------------------------------------------------- 3101 | // Program setup ##:s 3102 | 3103 | void setup() 3104 | { 3105 | calcScreenLayout(); // global vars 3106 | 3107 | if (__builtin_cpu_supports("sse2")) { // sse2 is default on 3108 | useSSE2 = true; 3109 | } 3110 | 3111 | if (! (winInput = newwin(3, inWidth, 0, 0))) { 3112 | exitMsg(22, "Failed to create input window."); 3113 | } 3114 | keypad(winInput, true); 3115 | 3116 | if (! (winHelp = newwin(helpHeight, helpWidth, 3117 | 1 + (linesTotal - helpHeight) / 3, 3118 | 1 + (screenWidth - helpWidth) / 2))) { 3119 | exitMsg(23, "Failed to create help window."); 3120 | } 3121 | 3122 | wbkgd(winHelp, attribStyle[cHelpWin]); 3123 | box(winHelp, 0, 0); 3124 | 3125 | mvwaddstr(winHelp, 0, (helpWidth - 6) / 2, " Help "); 3126 | mvwaddstr(winHelp, helpHeight - 1, (helpWidth - strlen(helpVersion)) / 2, helpVersion); 3127 | mvwaddstr(winHelp, helpHeight - 1, (helpWidth - 8) , useSSE2 ? "SSE2" : ""); 3128 | 3129 | for (int i=0; i < helpHeight - 2; ++i) { // exclude border 3130 | mvwaddstr(winHelp, i + 1, 1, aHelp[i]); 3131 | } 3132 | 3133 | for (int i=0; aBold[i]; i += 2) { 3134 | mvwchgat(winHelp, aBold[i], aBold[i + 1], 1, attribStyle[cHotkey], colorStyle[cHotkey], NULL); 3135 | } 3136 | 3137 | if (! singleFile) { 3138 | diffs.resizeD(); 3139 | } 3140 | 3141 | sm4rt = (FPos*) calloc(numLines, sizeof(FPos)); 3142 | 3143 | file1.initF(0, (singleFile ? NULL : &diffs)); 3144 | 3145 | if (! singleFile) { 3146 | file2.initF(numLines + 1, &diffs); 3147 | } 3148 | } // end setup 3149 | 3150 | //-------------------------------------------------------------------- 3151 | // Test progress bar 3152 | 3153 | void ee() 3154 | { 3155 | for (int blocks = 25, naps = 4, go = 0; ;) { 3156 | for (int i=1; i;) { 3157 | char buf[32]; 3158 | sprintf(buf, " %d %d ", blocks, naps); 3159 | positionInWin(cmgGotoTop, 2+ blocks +2, buf); 3160 | 3161 | if (! go++) break; 3162 | 3163 | flushinp(); 3164 | int key = wgetch(winInput); 3165 | 3166 | switch (key) { 3167 | case KEY_UP: if (naps < 50) ++naps; break; 3168 | case KEY_DOWN: if (naps > 0) --naps; break; 3169 | 3170 | case KEY_LEFT: if (blocks > 3) --blocks; file1.updateF(); break; 3171 | case KEY_RIGHT: if (blocks < screenWidth - 4) ++blocks; break; 3172 | 3173 | case KEY_ESCAPE: file1.updateF(); return; 3174 | default: i--; 3175 | } 3176 | } 3177 | 3178 | wchar_t bar[blocks + 1]; 3179 | memset(bar, 0, sizeof(bar)); 3180 | 3181 | for (int i=0; i < blocks; ++i) { 3182 | for (int j=0; j < 8; ++j) { 3183 | bar[i] = barSyms[j]; 3184 | 3185 | mvwaddwstr(winInput, 1, 2, bar); 3186 | wrefresh(winInput); 3187 | napms(naps); 3188 | } 3189 | } 3190 | napms(200); 3191 | } 3192 | } 3193 | 3194 | //-------------------------------------------------------------------- 3195 | // Process cmdline arguments 3196 | 3197 | void processArgs(int argc, char** argv) 3198 | { 3199 | if (*argv[2] == '-') { 3200 | dumpMode = 1; 3201 | 3202 | if (*++argv[2] == '-') { 3203 | dumpMode = 2; 3204 | } 3205 | 3206 | if (argv[3]) { 3207 | Size tmp = strtol(argv[3], NULL, 0); 3208 | 3209 | if (strchr(argv[3], 'l')) { 3210 | dumpLen = tmp; 3211 | } 3212 | else if (strchr(argv[3], 'w')) { 3213 | dumpWid = tmp; 3214 | } 3215 | else { 3216 | dumpBeg = tmp; 3217 | } 3218 | 3219 | if (argv[4]) { 3220 | tmp = strtol(argv[4], NULL, 0); 3221 | 3222 | if (strchr(argv[4], 'l')) { 3223 | dumpLen = tmp; 3224 | } 3225 | else if (strchr(argv[4], 'w')) { 3226 | dumpWid = tmp; 3227 | } 3228 | else { 3229 | dumpEnd = tmp; 3230 | } 3231 | 3232 | if (argv[5]) { 3233 | dumpWid = strtol(argv[5], NULL, 0); 3234 | } 3235 | } 3236 | } 3237 | 3238 | if (dumpWid <= 0) { 3239 | dumpWid = dumpDef; 3240 | } 3241 | } 3242 | 3243 | else if (argv[3] && *argv[3] == '-') { 3244 | if (*++argv[3] == '-') { 3245 | diffMode = 2; 3246 | } 3247 | else { 3248 | diffMode = 1; 3249 | } 3250 | 3251 | singleFile = false; 3252 | } 3253 | 3254 | else { 3255 | File probe = OpenFile(argv[2]); 3256 | 3257 | if (probe > 0) { 3258 | singleFile = false; 3259 | 3260 | close(probe); 3261 | } 3262 | else { 3263 | file1.startAddr = strtol(argv[2], NULL, 0); 3264 | 3265 | if (! file1.startAddr) { 3266 | singleFile = false; // error 3267 | } 3268 | } 3269 | 3270 | if (! singleFile && argv[3]) { 3271 | file1.startAddr = strtol(argv[3], NULL, 0); 3272 | 3273 | if (argv[4]) { 3274 | file2.startAddr = strtol(argv[4], NULL, 0); 3275 | } 3276 | else { 3277 | file2.startAddr = file1.startAddr; 3278 | } 3279 | } 3280 | } 3281 | } // end processArgs 3282 | 3283 | //-------------------------------------------------------------------- 3284 | // Get a file position and move there ##:p 3285 | 3286 | void gotoPosition(Command cmd) 3287 | { 3288 | positionInWin(cmd, inWidth + 1 + 4, " Goto "); // cursor + border 3289 | 3290 | char buf[inWidth + 1]; 3291 | 3292 | getString(buf, inWidth, positionHistory, hexDigitsGoto); 3293 | 3294 | if (! buf[0]) { 3295 | return; 3296 | } 3297 | 3298 | int rel = 0; 3299 | 3300 | if (*buf == '+') { 3301 | ++rel; 3302 | } 3303 | 3304 | if (*buf == '-') { 3305 | --rel; 3306 | } 3307 | 3308 | if (rel) { 3309 | *buf = ' '; 3310 | } 3311 | 3312 | FPos pos1 = 0, 3313 | pos2 = 0; 3314 | 3315 | if (strchr(buf, '%')) { 3316 | int i = atoi(buf); 3317 | 3318 | if (i >= 1 && i <= 99) { 3319 | pos1 = file1.filesize / 100 * i; 3320 | pos2 = file2.filesize / 100 * i; 3321 | } 3322 | else if (i >= 100) { 3323 | pos1 = file1.filesize - steps[cmmMovePage]; 3324 | pos2 = file2.filesize - steps[cmmMovePage]; 3325 | } 3326 | } 3327 | 3328 | else if (strpbrk(buf, "ABCDEFXabcdefx")) { 3329 | pos1 = pos2 = strtol(buf, NULL, 16); 3330 | } 3331 | 3332 | else { 3333 | pos1 = pos2 = strtol(buf, NULL, 10); 3334 | } 3335 | 3336 | Size prefix = 1; 3337 | 3338 | const char* ptr = strpbrk(buf, sPrefix); 3339 | 3340 | if (ptr) { 3341 | ptr = strchr(sPrefix, *ptr); 3342 | 3343 | prefix = aPrefix[ptr - sPrefix]; 3344 | } 3345 | 3346 | pos1 *= prefix; 3347 | pos2 *= prefix; 3348 | 3349 | if (cmd & cmgGotoTop) { 3350 | if (rel) { 3351 | file1.repeatOff = rel > 0 ? pos1 : -pos1; 3352 | file1.move(file1.repeatOff); 3353 | } 3354 | 3355 | else { 3356 | file1.setLast(); 3357 | file1.moveTo(pos1); 3358 | } 3359 | } 3360 | 3361 | if (cmd & cmgGotoBottom) { 3362 | if (rel) { 3363 | file2.repeatOff = rel > 0 ? pos2 : -pos2; 3364 | file2.move(file2.repeatOff); 3365 | } 3366 | 3367 | else { 3368 | file2.setLast(); 3369 | file2.moveTo(pos2); 3370 | } 3371 | } 3372 | } // end gotoPosition 3373 | 3374 | //-------------------------------------------------------------------- 3375 | // Search for text or bytes in the files 3376 | 3377 | void searchFiles(Command cmd) 3378 | { 3379 | const bool havePrev = !lastSearch.empty(); 3380 | int key = 0; 3381 | 3382 | if (! ((cmd & cmfFindNext || cmd & cmfFindPrev) && havePrev)) { 3383 | positionInWin(cmd, (havePrev ? 36 : 18), " Find "); 3384 | 3385 | mvwaddstr(winInput, 1, 2, "H Hex"); 3386 | mvwaddstr(winInput, 1, 10, "T Text"); 3387 | 3388 | mvwchgat(winInput, 1, 2, 1, attribStyle[cHotkey], colorStyle[cHotkey], NULL); 3389 | mvwchgat(winInput, 1, 10, 1, attribStyle[cHotkey], colorStyle[cHotkey], NULL); 3390 | 3391 | if (havePrev) { 3392 | mvwaddstr(winInput, 1, 19, "N Next"); 3393 | mvwaddstr(winInput, 1, 28, "P Prev"); 3394 | 3395 | mvwchgat(winInput, 1, 19, 1, attribStyle[cHotkey], colorStyle[cHotkey], NULL); 3396 | mvwchgat(winInput, 1, 28, 1, attribStyle[cHotkey], colorStyle[cHotkey], NULL); 3397 | } 3398 | 3399 | key = upCase(wgetch(winInput)); 3400 | 3401 | bool hex = false; 3402 | 3403 | if (key == KEY_ESCAPE) { 3404 | return; 3405 | } 3406 | else if (key == 'H') { 3407 | hex = true; 3408 | } 3409 | 3410 | if (! ((key == 'N' || key == 'P') && havePrev)) { 3411 | positionInWin(cmd, screenWidth, (hex ? " Find Hex Bytes " : " Find Text ")); 3412 | 3413 | int maxlen = screenWidth - 4 - 1; 3414 | 3415 | if (hex) { 3416 | maxlen -= maxlen % 3; 3417 | } 3418 | 3419 | char buf[maxlen + 1]; 3420 | int searchLen; 3421 | 3422 | if (hex) { 3423 | getString(buf, maxlen, hexSearchHistory, hexDigits, true, true); 3424 | 3425 | searchLen = packHex(buf); 3426 | } 3427 | else { 3428 | getString(buf, maxlen, textSearchHistory); 3429 | 3430 | searchLen = strlen(buf); 3431 | } 3432 | 3433 | if (! searchLen) { 3434 | return; 3435 | } 3436 | 3437 | if (cmd & cmgGotoTop) { 3438 | file1.setLast(); 3439 | } 3440 | 3441 | if (cmd & cmgGotoBottom) { 3442 | file2.setLast(); 3443 | } 3444 | 3445 | lastSearch.assign(buf, searchLen); 3446 | 3447 | lowCase((Byte*)buf, searchLen); 3448 | 3449 | lastSearchIgnCase.assign(buf, searchLen); 3450 | } 3451 | 3452 | if (! singleFile) { 3453 | file2.updateF(); // kick remnants 3454 | } 3455 | } 3456 | 3457 | Byte* searchPattern = (Byte*) (ignoreCase ? lastSearchIgnCase.data() : lastSearch.data()); 3458 | 3459 | if (cmd & cmfFindPrev || key == 'P') { 3460 | if (cmd & cmgGotoTop) { 3461 | file1.busy(true, false, true); 3462 | 3463 | file1.moveBack(searchPattern, lastSearch.size()); 3464 | file1.busy(); 3465 | } 3466 | 3467 | if (cmd & cmgGotoBottom) { 3468 | file2.busy(true, false, true); 3469 | 3470 | file2.moveBack(searchPattern, lastSearch.size()); 3471 | file2.busy(); 3472 | } 3473 | } 3474 | 3475 | else { 3476 | if (cmd & cmgGotoTop) { 3477 | file1.busy(true, false, true); 3478 | 3479 | file1.moveForw(searchPattern, lastSearch.size()); 3480 | file1.busy(); 3481 | } 3482 | 3483 | if (cmd & cmgGotoBottom) { 3484 | file2.busy(true, false, true); 3485 | 3486 | file2.moveForw(searchPattern, lastSearch.size()); 3487 | file2.busy(); 3488 | } 3489 | } 3490 | } // end searchFiles 3491 | 3492 | //-------------------------------------------------------------------- 3493 | // Handle a command ##:hand 3494 | 3495 | void handleCmd(Command cmd) 3496 | { 3497 | if (cmd & cmgGoto) { 3498 | if (cmd & cmgGotoForw) { 3499 | if (cmd & cmgGotoTop) { 3500 | file1.skip(); 3501 | } 3502 | if (cmd & cmgGotoBottom) { 3503 | file2.skip(); 3504 | } 3505 | } 3506 | 3507 | else if (cmd & cmgGotoBack) { 3508 | if (cmd & cmgGotoTop) { 3509 | file1.skip(true); 3510 | } 3511 | if (cmd & cmgGotoBottom) { 3512 | file2.skip(true); 3513 | } 3514 | } 3515 | 3516 | else if ((cmd & cmgGotoMask) == cmgGotoLGet) { 3517 | if (cmd & cmgGotoTop) { 3518 | file1.getLast(); 3519 | } 3520 | if (cmd & cmgGotoBottom) { 3521 | file2.getLast(); 3522 | } 3523 | } 3524 | 3525 | else if ((cmd & cmgGotoMask) == cmgGotoLSet) { 3526 | if (cmd & cmgGotoTop) { 3527 | file1.setLast(); 3528 | } 3529 | if (cmd & cmgGotoBottom) { 3530 | file2.setLast(); 3531 | } 3532 | } 3533 | 3534 | else if ((cmd & cmgGotoMask) == cmgGotoLOff) { 3535 | if (cmd & cmgGotoTop) { 3536 | file1.move(file1.repeatOff); 3537 | } 3538 | if (cmd & cmgGotoBottom) { 3539 | file2.move(file2.repeatOff); 3540 | } 3541 | } 3542 | 3543 | else if ((cmd & cmgGotoMask) == cmgGotoNOff) { 3544 | if (cmd & cmgGotoTop) { 3545 | file1.move(-file1.repeatOff); 3546 | } 3547 | if (cmd & cmgGotoBottom) { 3548 | file2.move(-file2.repeatOff); 3549 | } 3550 | } 3551 | 3552 | else if ((cmd & cmgGotoMask) == cmgGotoJGet) { 3553 | if (cmd & cmgGotoTop) { 3554 | file1.setLast(); 3555 | file1.moveTo(file1.startAddr); 3556 | } 3557 | if (cmd & cmgGotoBottom) { 3558 | file2.setLast(); 3559 | file2.moveTo(file2.startAddr); 3560 | } 3561 | } 3562 | 3563 | else if ((cmd & cmgGotoMask) == cmgGotoJSet) { 3564 | if (cmd & cmgGotoTop) { 3565 | file1.setJump(); 3566 | } 3567 | if (cmd & cmgGotoBottom) { 3568 | file2.setJump(); 3569 | } 3570 | } 3571 | 3572 | else { 3573 | gotoPosition(cmd); 3574 | } 3575 | } 3576 | 3577 | else if (cmd & cmfFind) { 3578 | if (cmd & cmfNotCharDn) { 3579 | if (cmd & cmgGotoTop) { 3580 | file1.busy(true); 3581 | 3582 | file1.seekForw(); 3583 | file1.busy(); 3584 | } 3585 | 3586 | if (cmd & cmgGotoBottom) { 3587 | file2.busy(true); 3588 | 3589 | file2.seekForw(); 3590 | file2.busy(); 3591 | } 3592 | } 3593 | 3594 | else if (cmd & cmfNotCharUp) { 3595 | if (cmd & cmgGotoTop) { 3596 | file1.busy(true); 3597 | 3598 | file1.seekBack(); 3599 | file1.busy(); 3600 | } 3601 | 3602 | if (cmd & cmgGotoBottom) { 3603 | file2.busy(true); 3604 | 3605 | file2.seekBack(); 3606 | file2.busy(); 3607 | } 3608 | } 3609 | 3610 | else { 3611 | searchFiles(cmd); 3612 | } 3613 | } 3614 | 3615 | else if (cmd & cmmMove) { 3616 | int step = steps[cmd & cmmMoveMask]; 3617 | 3618 | if (! (cmd & cmmMoveForward)) { 3619 | step *= -1; 3620 | } 3621 | 3622 | if ((cmd & cmmMoveForward) && ! step) { // special case first 3623 | if (cmd & cmgGotoTop) { 3624 | file1.setLast(); 3625 | file1.moveToEnd(); 3626 | } 3627 | 3628 | if (cmd & cmgGotoBottom) { 3629 | file2.setLast(); 3630 | file2.moveToEnd(); 3631 | } 3632 | } 3633 | 3634 | else { 3635 | if (cmd & cmgGotoTop) { 3636 | if (step) { 3637 | file1.move(step); 3638 | 3639 | if (haveDiff) { 3640 | diffs.compute(); 3641 | } 3642 | } 3643 | else { 3644 | file1.setLast(); 3645 | file1.moveTo(0); 3646 | } 3647 | } 3648 | 3649 | if (cmd & cmgGotoBottom) { 3650 | if (step) { 3651 | file2.move(step); 3652 | 3653 | if (haveDiff) { 3654 | diffs.compute(); 3655 | } 3656 | } 3657 | else { 3658 | file2.setLast(); 3659 | file2.moveTo(0); 3660 | } 3661 | } 3662 | } 3663 | } 3664 | 3665 | else if (cmd == cmSyncUp) { 3666 | file1.sync(&file2); 3667 | } 3668 | 3669 | else if (cmd == cmSyncDn) { 3670 | file2.sync(&file1); 3671 | } 3672 | 3673 | else if (cmd == cmNextDiff || cmd == cmPrevDiff) { 3674 | if (lockState) { 3675 | lockState = lockNeither; 3676 | } 3677 | 3678 | file1.busy(true); 3679 | file2.busy(true); 3680 | 3681 | diffs.differ(cmd); 3682 | 3683 | diffs.compute(); 3684 | 3685 | file1.busy(); 3686 | file2.busy(); 3687 | } 3688 | 3689 | else if (cmd == cmUseTop) { 3690 | if (lockState == lockBottom) { 3691 | lockState = lockNeither; 3692 | } 3693 | else { 3694 | lockState = lockBottom; 3695 | } 3696 | } 3697 | 3698 | else if (cmd == cmUseBottom) { 3699 | if (lockState == lockTop) { 3700 | lockState = lockNeither; 3701 | } 3702 | else { 3703 | lockState = lockTop; 3704 | } 3705 | } 3706 | 3707 | else if (cmd == cmShowAscii) { 3708 | modeAscii ^= true; 3709 | 3710 | setViewMode(); 3711 | file1.resizeF(); 3712 | file1.move(0); 3713 | } 3714 | 3715 | else if (cmd == cmIgnoreCase) { 3716 | file1.busy(true, true); 3717 | file2.busy(true, true); 3718 | 3719 | ignoreCase ^= true; 3720 | file1.busy(false, true); 3721 | file2.busy(false, true); 3722 | } 3723 | 3724 | else if (cmd == cmShowRaster) { 3725 | showRaster ^= true; 3726 | } 3727 | 3728 | else if (cmd == cmShowHelp) { 3729 | displayHelp(); 3730 | } 3731 | 3732 | else if (cmd == cmEditTop && ! modeAscii) { 3733 | file1.display(); // reset smartscroll 3734 | file1.highEdit(screenWidth); 3735 | 3736 | file1.edit(singleFile ? NULL : &file2); 3737 | } 3738 | 3739 | else if (cmd == cmEditBottom) { 3740 | file2.highEdit(screenWidth); 3741 | 3742 | file2.edit(&file1); 3743 | } 3744 | 3745 | else if (cmd == cmSmartScroll) { 3746 | file1.busy(true); 3747 | 3748 | file1.smartScroll(); 3749 | file1.busy(); 3750 | } 3751 | 3752 | file1.display(); 3753 | file2.display(); 3754 | 3755 | if (stopRead) { 3756 | stopRead = false; 3757 | napms(500); 3758 | flushinp(); 3759 | } 3760 | } // end handleCmd 3761 | 3762 | //-------------------------------------------------------------------- 3763 | // Get a command from keyboard ##:get 3764 | 3765 | Command getCommand() 3766 | { 3767 | Command cmd = cmNothing; 3768 | 3769 | while (cmd == cmNothing) { 3770 | int key = file1.readKeyF(); 3771 | 3772 | switch (upCase(key)) { 3773 | case KEY_RIGHT: cmd = cmmMove | cmmMoveByte | cmmMoveForward; break; 3774 | case KEY_DOWN: cmd = cmmMove | cmmMoveLine | cmmMoveForward; break; 3775 | case ' ': cmd = cmmMove | cmmMovePage | cmmMoveForward; break; 3776 | case KEY_END: cmd = cmmMove | cmmMoveAll | cmmMoveForward; break; 3777 | case KEY_LEFT: cmd = cmmMove | cmmMoveByte; break; 3778 | case KEY_UP: cmd = cmmMove | cmmMoveLine; break; 3779 | case KEY_BACKSPACE: cmd = cmmMove | cmmMovePage; break; 3780 | case KEY_HOME: cmd = cmmMove | cmmMoveAll; break; 3781 | 3782 | case 'F': cmd = cmfFind; break; 3783 | case 'N': cmd = cmfFind | cmfFindNext; break; 3784 | case 'P': cmd = cmfFind | cmfFindPrev; break; 3785 | case KEY_NPAGE: cmd = cmfFind | cmfNotCharDn; break; 3786 | case KEY_PPAGE: cmd = cmfFind | cmfNotCharUp; break; 3787 | 3788 | case 'G': cmd = cmgGoto; break; 3789 | case '+': 3790 | case '*': 3791 | case '=': cmd = cmgGoto | cmgGotoForw; break; 3792 | case '-': cmd = cmgGoto | cmgGotoBack; break; 3793 | case '\'': 3794 | case '<': cmd = cmgGoto | cmgGotoLGet; break; 3795 | case 'L': cmd = cmgGoto | cmgGotoLSet; break; 3796 | case '.': cmd = cmgGoto | cmgGotoLOff; break; 3797 | case ',': cmd = cmgGoto | cmgGotoNOff; break; 3798 | case '"': cmd = cmgGoto | cmgGotoJGet; break; 3799 | case 'J': cmd = cmgGoto | cmgGotoJSet; break; 3800 | 3801 | case 'E': cmd = lockState == lockTop ? cmEditBottom : cmEditTop; break; 3802 | 3803 | case KEY_RETURN: cmd = singleFile ? cmSmartScroll : cmNextDiff; break; 3804 | 3805 | case '#': 3806 | case '\\': if (! singleFile) cmd = cmPrevDiff; break; 3807 | 3808 | case 'T': if (! singleFile) cmd = cmUseTop; break; 3809 | case 'B': if (! singleFile) cmd = cmUseBottom; break; 3810 | 3811 | case '1': if (! singleFile) cmd = cmSyncUp; break; 3812 | case '2': if (! singleFile) cmd = cmSyncDn; break; 3813 | 3814 | case 'A': if (singleFile) cmd = cmShowAscii; break; 3815 | 3816 | case 'I': cmd = cmIgnoreCase; break; 3817 | 3818 | case 'R': cmd = cmShowRaster; break; 3819 | 3820 | case 'H': cmd = cmShowHelp; break; 3821 | 3822 | case 'Z': ee(); break; 3823 | 3824 | case KEY_ESCAPE: 3825 | if (! singleFile && lockState != lockNeither) 3826 | cmd = lockState == lockTop ? cmUseBottom : cmUseTop; 3827 | break; // better off w/o Esc 3828 | 3829 | case KEY_CTRL_C: 3830 | case 'Q': cmd = cmQuit; break; 3831 | } 3832 | } 3833 | 3834 | if (cmd & (cmmMove | cmfFind | cmgGoto)) { 3835 | if (lockState != lockTop) 3836 | cmd |= cmgGotoTop; 3837 | 3838 | if (lockState != lockBottom && ! singleFile) 3839 | cmd |= cmgGotoBottom; 3840 | } 3841 | 3842 | return cmd; 3843 | } // end getCommand 3844 | 3845 | //==================================================================== 3846 | // Main Program ##:main 3847 | 3848 | int main(int argc, char* argv[]) 3849 | { 3850 | program = strrchr(*argv, '/'); 3851 | 3852 | program = program ? program + 1 : *argv; 3853 | 3854 | if (argc == 1) { 3855 | fprintf(stderr, 3856 | "%s\n" 3857 | "\n" 3858 | "\t%s file [file2] [addr] [addr2] // ncurses\n" 3859 | "\n" 3860 | "\t%s file1 file2 - // diff view\n" 3861 | "\n" 3862 | "\t%s file1 file2 -- // diff return\n" 3863 | "\n" 3864 | "\t%s file - [start [end]] [length{l$}] [width{w$}] // dump ascii\n" 3865 | "\n" 3866 | "\t%s file -- [start [end]] [length{l$}] // dump binary\n" 3867 | "\n" 3868 | "// type 'h' for help\n" 3869 | "\n", 3870 | helpVersion + 1, program, program, program, program, program); 3871 | 3872 | exit(0); 3873 | } 3874 | 3875 | singleFile = true; 3876 | 3877 | if (argc > 2) { 3878 | processArgs(argc, argv); 3879 | } 3880 | 3881 | if (dumpMode) { 3882 | dumpFile(argv[1]); // exit 3883 | } 3884 | 3885 | if (diffMode) { 3886 | FPos ret = diffFile(argv[1], argv[2]); 3887 | 3888 | if (ret == -1) { 3889 | exit(0); // equal 3890 | } 3891 | 3892 | if (diffMode == 2) { 3893 | exit(1); // diff 3894 | } 3895 | 3896 | file1.startAddr = ret; 3897 | file2.startAddr = ret; 3898 | } 3899 | 3900 | fprintf(stderr, "%s\n\n", helpVersion + 1); 3901 | 3902 | if (! initialize()) { 3903 | err(11, "Unable to initialize ncurses"); 3904 | } 3905 | 3906 | string err; 3907 | 3908 | if (! file1.setFile(argv[1])) { 3909 | err = string("Unable to open ") + argv[1] + ": " + strerror(errno); 3910 | } 3911 | else if (! singleFile && ! file2.setFile(argv[2])) { 3912 | err = string("Unable to open ") + argv[2] + ": " + strerror(errno); 3913 | } 3914 | else if (file1.filesize > 281474976710656) { // 2**40*256 == 0x10**12 == 256TB 3915 | err = string("File is too big: ") + argv[1]; 3916 | } 3917 | else if (! singleFile && file2.filesize > 281474976710656) { 3918 | err = string("File is too big: ") + argv[2]; 3919 | } 3920 | 3921 | if (err.size()) { 3922 | exitMsg(12, err.c_str()); 3923 | } 3924 | 3925 | setup(); 3926 | 3927 | if (diffMode) { 3928 | diffs.differ(cmNextDiff); 3929 | diffs.compute(); 3930 | } 3931 | 3932 | file1.display(); 3933 | file2.display(); 3934 | 3935 | for (Command cmd; (cmd = getCommand()) != cmQuit; handleCmd(cmd)) { 3936 | if (! (cmd & cmfFind && ! (cmd & (cmfNotCharDn | cmfNotCharUp | cmgGoto)))) { 3937 | file1.searchOff = file2.searchOff = 0; 3938 | } 3939 | 3940 | if (! (cmd == cmNextDiff || cmd == cmPrevDiff || cmd == cmShowRaster || 3941 | (((cmd & (cmgGoto | cmfFind | cmmMove)) == cmmMove) && steps[cmd & cmmMoveMask]))) { 3942 | haveDiff = false; 3943 | } 3944 | 3945 | if (file1.scrollOff) { 3946 | if (cmd != cmShowRaster) { 3947 | file1.move(0); 3948 | 3949 | memset(sm4rt, 0, numLines * sizeof(FPos)); 3950 | } 3951 | 3952 | if (! (cmd == cmSmartScroll || cmd == cmShowRaster)) { 3953 | file1.scrollOff = 0; 3954 | } 3955 | } 3956 | } 3957 | 3958 | shutdown(); 3959 | 3960 | return 0; 3961 | } 3962 | -------------------------------------------------------------------------------- /version.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | grep -m1 VBL_VERSION vbl.cpp |cut -d\" -f2 4 | 5 | --------------------------------------------------------------------------------