├── .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 | 
143 | *One File*
144 |
145 | 
146 | *ASCII Mode*
147 |
148 | 
149 | *Files >64GB*
150 |
151 | 
152 | *Two Files*
153 |
154 | 
155 | *You have to confirm long writes (>512MB)*
156 |
157 | 
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 |
--------------------------------------------------------------------------------