. "$BST_LIB" require hexedit hexedit/present # hexedit 1.6 is an interactive curses editor and NOTHING ELSE — no --version, no # batch mode, and the "-h | --help" its own usage string advertises is not a case # in the argument loop, so it falls through to the same DIE as any other unknown # option. Everything below the pty leg therefore asserts a DIAGNOSTIC: the five # paths that exit before initCurses() are the entire surface reachable without a # terminal, and getting the file open, the display painted and bytes written back # is the pty payload's job. # # EVERY INVOCATION HERE IS WRAPPED. `case ERR: break;` in key_to_function() falls # through to `return TRUE`, so hexedit on a pipe or an empty stdin does not exit — # getch() reports EOF as ERR and the main loop spins forever at 100% CPU. The # runner's own stdin is the guest console, so an unredirected invocation would # also take the screen away from the run that is testing it. `timeout 10` + # "$1"; } for f in view edit keep ascii goto; do seed "$work/$f.bin"; done seed "$work/pristine.bin" byte() { od -An -tx1 -j "$2" -N 1 "$1" 2>/dev/null | tr -d ' \n'; } # --- the diagnostics that need no terminal ----------------------------------- # DIE() calls exitCurses() BEFORE the message, which closes fd and runs clear() / # refresh() / endwin() against a stdscr that initscr() never allocated. On a # platform where those did not tolerate a NULL screen this would fault instead of # printing, so each case below asserts the message AND the exit status: rc 1 is # hexedit's DIE, and rc 124 would be the timeout above catching a spin or a hang. check_rc hexedit/badopt-rc 1 he -Z out=$(he -Z 2>&1) case "$out" in "usage: hexedit"*) bst_ok hexedit/badopt-usage ;; *) bst_notok hexedit/badopt-usage "$(_bst_1line "$out")" ;; esac # "-h | --help" is in the usage text but not in the parse loop: it prints the # usage and exits 1 like any unknown flag. Pinning that is how a future upstream # bump that finally implements it becomes visible instead of silent. check_rc hexedit/help-rc 1 he --help out=$(he --help 2>&1) case "$out" in *"[-s | --sector]"*) bst_ok hexedit/help-usage ;; *) bst_notok hexedit/help-usage "$(_bst_1line "$out")" ;; esac check_rc hexedit/twofiles-rc 1 he "$work/view.bin" "$work/edit.bin" # lineLength is range-checked in the argument loop, before init(). check_rc hexedit/linelen-rc 1 he -l 5000 "$work/view.bin" out=$(he -l 5000 "$work/view.bin" 2>&1) case "$out" in *"illegal line length"*) bst_ok hexedit/linelen-msg ;; *) bst_notok hexedit/linelen-msg "$(_bst_1line "$out")" ;; esac # openFile() runs before initCurses(), so both of these are decided on the file # rather than on the terminal — is_file() rejects the absent path and the # directory alike. check_rc hexedit/absent-rc 1 he "$work/no-such-file.bin" out=$(he "$work/no-such-file.bin" 2>&1) case "$out" in *"Not a file."*) bst_ok hexedit/absent-msg ;; *) bst_notok hexedit/absent-msg "$(_bst_1line "$out")" ;; esac check_rc hexedit/dir-rc 1 he "$work" # The last diagnostic before the interactive loop: an unresolvable terminal type # has to fail INSIDE initscr() and exit, not trap. This is the boundary the pty # leg is on the other side of. out=$(TERM=bst-no-such-term timeout 10 hexedit "$work/view.bin" < /dev/null 2>&1); rc=$? case "$out" in *"Error opening terminal"*) hit=1 ;; *) hit=0 ;; esac if [ "$rc" -eq 1 ] && [ "$hit" -eq 1 ]; then bst_ok hexedit/no-terminal else bst_notok hexedit/no-terminal "rc=$rc $(_bst_1line "$out")"; fi # --- the editor itself, over a real pty -------------------------------------- require expect hexedit/expect-present out=$(expect "$BST_DATA/selftest_hexedit.exp" "$work" 2>&1); rc=$? case "$out" in *ALL-HEXEDIT-OK*) all=1 ;; *) all=0 ;; esac if [ "$rc" -eq 0 ] && [ "$all" -eq 1 ]; then bst_ok hexedit/all-ok else bst_notok hexedit/all-ok "rc=$rc $(_bst_1line "$out")"; fi for tag in H1render H2quit H3render H4dirtymark H5saveexit H6render H7savequery \ H8discardexit H9render H10asciiexit H11render H12gotoprompt H13gotoexit; do case "$out" in *"H-$tag OK"*) bst_ok "hexedit/$tag" ;; *) bst_notok "hexedit/$tag" "not OK in payload output (rc=$rc)" ;; esac done # --- what actually reached the disk (the verdicts that count) ---------------- # S2: two hex digits at byte 0, written back with ^W. 'B' (0x42) is the seed's # first byte, so 0x41 is a value the file could not have had by accident. check_eq hexedit/edit-byte0 41 "$(byte "$work/edit.bin" 0)" check_eq hexedit/edit-byte1 52 "$(byte "$work/edit.bin" 1)" check_eq hexedit/edit-size 56 "$(wc -c < "$work/edit.bin" 2>/dev/null | tr -d ' ')" # save_buffer() LSEEKs to each edited page and writes only that page, so exactly # one byte in the whole file may differ from the seed and it must be byte 0. # `cmp -l` lists every differing offset (1-based), which catches the two ways a # save can be wrong without changing the length: a page written at the wrong # offset, and a whole-buffer rewrite that drags the untouched bytes along. check_eq hexedit/edit-one-byte 1 "$(cmp -l "$work/edit.bin" "$work/pristine.bin" 2>/dev/null | wc -l | tr -d ' ')" check_eq hexedit/edit-at-zero 1 "$(cmp -l "$work/edit.bin" "$work/pristine.bin" 2>/dev/null | awk 'NR==1{print $1}')" # S3: ^X then "n" — the buffer was edited (the same keystrokes as S2) and then # deliberately not written. if cmp -s "$work/keep.bin" "$work/pristine.bin"; then bst_ok hexedit/discard-unchanged else bst_notok hexedit/discard-unchanged "keep.bin differs from the seed after answering 'n'"; fi # S4: TAB put the editor in ASCII entry, so one "Z" is the byte 0x5A. check_eq hexedit/ascii-byte0 5a "$(byte "$work/ascii.bin" 0)" check_eq hexedit/ascii-size 56 "$(wc -c < "$work/ascii.bin" 2>/dev/null | tr -d ' ')" # S5: ^G moved the cursor to 0x8 and the edit landed THERE. Byte 0 staying at the # seed's 0x42 is half the assertion — 0x43 was chosen precisely because a # dismissed prompt would have written it at byte 0, where it is distinguishable # from the seed. Byte 7 pins the near side of the jump. check_eq hexedit/goto-byte8 43 "$(byte "$work/goto.bin" 8)" check_eq hexedit/goto-byte0 42 "$(byte "$work/goto.bin" 0)" check_eq hexedit/goto-byte7 32 "$(byte "$work/goto.bin" 7)" rm -rf "$work"