# hstr 3.1.0 -- shell-history browser (ncurses + readline). # # hstr's screen is a curses full-screen selector, which this runner cannot drive. # Everything below therefore exercises the NON-INTERACTIVE surface, which is not a # reduced version of the tool: `-n` runs the same dedupe, rank and filter over the # same history file that the interactive view shows, and prints the result. So the # ranking and filtering ARE covered here; what is not is the drawing and the # keyboard, which BRINTOS-737 carries. # # ONE DIAGNOSTIC SHAPES EVERY CASE. hstr probes TIOCSTI support at startup on # EVERY invocation (including --version) by opening /dev/tty. Where that fails it # prints "Error: unable to detect whether TIOCSTI is supported by the kernel" to # STDOUT WITH NO TRAILING NEWLINE, so it glues onto the first line of real output. # norm() strips it as a SUBSTRING: a filter that dropped matching LINES would eat # the first history entry instead, and report a wrong answer that looks clean. . "$BST_LIB" require hstr hstr/present tmp=${TMPDIR:-/tmp} fix=$tmp/bst-hstr-hist.$$ bl=$tmp/bst-hstr-bl.$$ mt=$tmp/bst-hstr-empty.$$ norm() { sed -e 's/Error: unable to detect whether TIOCSTI is supported by the kernel//' \ -e '/^[[:space:]]*$/d' } # Ten lines, one of them repeated: 9 unique entries, and the repeat is the one # that must rank first. cat > "$fix" <<'EOF' ls -la /tmp grep -rn pattern src/ make -j4 all ls -la /var/log tar czf backup.tgz /etc grep -rn pattern src/ ps aux | grep init make clean echo hello world df -h / EOF # ---- identity ------------------------------------------------------------- check_out hstr/version "3.1.0" env HISTFILE="$fix" hstr --version check_out hstr/help "Shell history suggest box" env HISTFILE="$fix" hstr --help check_out hstr/help-lists-n "--non-interactive" env HISTFILE="$fix" hstr --help # upstream's install-exec-hook hard-links hstr to hh; both names are this port check_out hstr/hh-link "3.1.0" env HISTFILE="$fix" hh --version # ---- the non-interactive core: dedupe, rank, filter ------------------------ n=$(env HISTFILE="$fix" hstr -n 2>/dev/null | norm | wc -l | tr -d ' ') check_eq hstr/ni-dedupe-count 9 "$n" # the twice-run command ranks above the eight singletons top=$(env HISTFILE="$fix" hstr -n 2>/dev/null | norm | head -1) check_eq hstr/ni-rank-top "grep -rn pattern src/" "$top" mk=$(env HISTFILE="$fix" hstr -n make 2>/dev/null | norm | wc -l | tr -d ' ') check_eq hstr/ni-filter-make-count 2 "$mk" check_out hstr/ni-filter-make-all "make -j4 all" env HISTFILE="$fix" hstr -n make check_out hstr/ni-filter-make-clean "make clean" env HISTFILE="$fix" hstr -n make # the pattern matches anywhere in the line, not just at its head check_out hstr/ni-filter-midline "ps aux | grep init" env HISTFILE="$fix" hstr -n grep # several arguments are assembled into one pattern one=$(env HISTFILE="$fix" hstr -n make clean 2>/dev/null | norm | wc -l | tr -d ' ') check_eq hstr/ni-multiword-count 1 "$one" none=$(env HISTFILE="$fix" hstr -n nomatchxyz 2>/dev/null | norm | wc -l | tr -d ' ') check_eq hstr/ni-nomatch-count 0 "$none" # ---- the built-in blacklist ------------------------------------------------ check_out hstr/blacklist-dump "Command blacklist" env HISTFILE="$fix" hstr --show-blacklist # bare ls/cd/pwd are blacklisted; the same words with arguments are NOT printf 'ls\ncd\npwd\nls -la /tmp\nuname -a\n' > "$bl" blc=$(env HISTFILE="$bl" hstr -n 2>/dev/null | norm | wc -l | tr -d ' ') check_eq hstr/blacklist-filters-count 2 "$blc" check_out hstr/blacklist-keeps-args "ls -la /tmp" env HISTFILE="$bl" hstr -n # ---- the configuration emitters ------------------------------------------- check_out hstr/bash-config "alias hh=hstr" env HISTFILE="$fix" hstr --show-bash-configuration check_out hstr/zsh-config "setopt histignorespace" env HISTFILE="$fix" hstr --show-zsh-configuration check_out hstr/show-config "HSTR_CONFIG" env HISTFILE="$fix" hstr --show-configuration # ---- TIOCSTI: assert the answer is DETERMINISTIC, not which answer --------- # hstr's only mechanism for putting a chosen command back on the prompt is # ioctl(TIOCSTI). Pinning y or n here would encode today's kernel rather than the # tool; what must hold is that the probe terminates and commits to one of them. t=$(env HISTFILE="$fix" hstr --is-tiocsti 2>/dev/null | norm | tr -d ' \r') case "$t" in y|n) bst_ok hstr/tiocsti-answer ;; *) bst_notok hstr/tiocsti-answer "expected y or n, got '$(_bst_1line "$t")'" ;; esac # Where TIOCSTI is refused, --insert-in-terminal falls back to writing the command # to stderr rather than failing -- that fallback is what keeps hstr usable here. # Where it is supported the command goes into the tty input queue instead and # there is nothing on stderr to read, so the case is skipped rather than failed. ins=$(env HISTFILE="$fix" hstr --insert-in-terminal="echo injected" 2>&1 >/dev/null) if [ "$t" = "n" ]; then case "$ins" in *"echo injected"*) bst_ok hstr/insert-long-fallback ;; *) bst_notok hstr/insert-long-fallback "no fallback echo: $(_bst_1line "$ins")" ;; esac else bst_skip hstr/insert-long-fallback "TIOCSTI supported ($t) -- command goes to the tty, not stderr" fi # UPSTREAM WART, pinned deliberately: long_options declares insert-in-terminal as # REQUIRED_ARGUMENT but the short optstring "fkVhnsBZbti" has no colon after i, so # `-i CMD` never receives an optarg and does nothing at all. Identical on x86-64. shortins=$(env HISTFILE="$fix" hstr -i "echo injected" 2>&1 >/dev/null) case "$shortins" in *"echo injected"*) bst_notok hstr/insert-short-noop "-i grew an optarg; upstream optstring may be fixed" ;; *) bst_ok hstr/insert-short-noop ;; esac # UPSTREAM WART: getopt's '?' shares the -h branch, so an unknown option prints # the usage text and exits 0. check_out hstr/badopt-usage "Usage: hstr" env HISTFILE="$fix" hstr --no-such-option # ---- history-file edge cases ---------------------------------------------- : > "$mt" check_out hstr/empty-history "No history - nothing to suggest" env HISTFILE="$mt" hstr -n # a missing history file is reported on stderr and is not fatal check hstr/missing-history-rc env HISTFILE="$tmp/bst-hstr-nosuch.$$" hstr -n miss=$(env HISTFILE="$tmp/bst-hstr-nosuch.$$" hstr -n 2>&1 >/dev/null) case "$miss" in *"Unable to read history file"*) bst_ok hstr/missing-history-msg ;; *) bst_notok hstr/missing-history-msg "$(_bst_1line "$miss")" ;; esac rm -f "$fix" "$bl" "$mt"