#!/usr/bin/env dash esc() {  case $1 in  # vt100 (IL is vt102) (DECTCEM is vt520)  CUD) printf '%s[%sB' "$esc_c" "$2" ;; # cursor down  CUP) printf '%s[%s;%sH' "$esc_c" "$2" "$3" ;; # cursor home  CUU) printf '%s[%sA' "$esc_c" "$2" ;; # cursor up  DECAWM) printf '%s[?7%s' "$esc_c" "$2" ;; # line wrap  DECRC) printf '%s8' "$esc_c" ;; # cursor restore  DECSC) printf '%s7' "$esc_c" ;; # cursor save  DECSTBM) printf '%s[%s;%sr' "$esc_c" "$2" "$3" ;; # scroll region  DECTCEM) printf '%s[?25%s' "$esc_c" "$2" ;; # cursor visible  ED[0-2]) printf '%s[%sJ' "$esc_c" "${1#ED}" ;; # clear screen  EL[0-2]) printf '%s[%sK' "$esc_c" "${1#EL}" ;; # clear line  IL) printf '%s[%sL' "$esc_c" "$2" ;; # insert line  SGR) printf '%s[%s;%sm' "$esc_c" "$2" "$3" ;; # colors  # xterm (since 1988, supported widely)  screen_alt) printf '%s[?1049%s' "$esc_c" "$2" ;; # alternate buffer  esac } term_setup() {  stty=$(stty -g)  stty -icanon -echo  esc screen_alt h  esc DECAWM l  esc DECTCEM l  esc ED2  esc DECSTBM 1 "$((LINES - 2))" } term_reset() {  esc DECAWM h >&2  esc DECTCEM h >&2  esc ED2 >&2  esc DECSTBM >&2  esc screen_alt l >&2  stty "$stty"  # needed for cd-on-exit  printf '%s\n' "$PWD" >&1 } term_resize() {  # false-positive, behavior intentional, globbing is disabled.  # shellcheck disable=2046  {  set -f  set +f -- $(stty size)  }  LINES=$1 COLUMNS=$2  # space for status_line  bottom=$((LINES - 2)) } term_scroll_down() {  case $((y - $#)) in  [0-9]*) return  esac  y=$((y + 1))  y2=$((y2 + 1 < bottom ? y2 + 1 : bottom))  line_print "$((y - 1))" "$@"  printf '\n'  line_print "$y" "$@"  status_line "$#" } term_scroll_up() {  case $y in  -*|0|1) return  esac  y=$((y - 1))  line_print "$((y + 1))" "$@"  case $y2 in  1) esc IL ;;  *) esc CUU; y2=$((y2 > 1 ? y2 - 1 : 1))  esac  line_print "$y" "$@"  status_line "$#" } cmd_run() {  stty "$stty"  esc DECTCEM h  esc DECSTBM  esc ED2  "$@" ||:  esc DECSTBM 1 "$((LINES - 2))"  esc DECTCEM l  stty -icanon -echo  hist=2 } file_escape() {  tmp=$1 safe=  # loop over string char by char  while c=${tmp%"${tmp#?}"}; do  case $c in  '') return ;;  [[:cntrl:]]) safe=$safe\? ;;  *) safe=$safe$c ;;  esac  tmp=${tmp#?}  done } hist_search() {  hist=0 j=1  for file do  case ${PWD%%/}/$file in  "$old_pwd") y=$j y2=$((j > bottom ? mid : j)) cur=$file  esac  j=$((j + 1))  done } list_print() {  esc ED2  esc CUP  i=1  end=$((bottom + 1))  mid=$((bottom / 4 < 5 ? 1 : bottom / 4))  case $# in  1) [ -e "$1" ] || set -- empty  esac  case $hist in  2) # redraw after cmd run  shift "$((y > y2 ? y - y2 : 0))"  ;;  1) # redraw after go-to-parent  hist_search "$@"  shift "$((y >= bottom ? y - mid : 0))"  ;;  *) # everything else  shift "$((y >= bottom ? y - bottom : 0))"  ;;  esac  for file do  case $i in  "$y2") esc SGR 0 7  esac  case $((i - end)) in  -*)  line_format "$file"  esc CUD  ;;  esac  i=$((i + 1))  done  esc CUP "$((y > y2 ? y2 : y))" } redraw() {  list_print "$@"  status_line "$#" } status_line() {  esc DECSC  esc CUP "$LINES"  case $USER in  root) esc SGR 31 7 ;;  *) esc SGR 34 7 ;;  esac  printf '%*s\r%s ' "$COLUMNS" "" "($y/$1)"  case $ltype in  '') printf %s "$PWD" ;;  *) printf %s "$ltype"  esac  esc SGR 0 0  esc DECRC } prompt() {  esc DECSC  esc CUP "$LINES"  printf %s "$1"  esc DECTCEM h  esc EL0  case $2 in  r)  stty icanon echo  read -r ans ||:  stty -icanon -echo  ;;  esac  esc DECRC  esc DECTCEM l  status_line "($y/$#) $PWD" } line_print() {  offset=$1  case $offset in  "$y") esc SGR 0 7  esac  shift "$offset"  case $offset in  "$y") cur=$1  esac  line_format "$1" } line_format() {  file_escape "$1"  [ -d "$1" ] && esc SGR 1 31  printf %s "$safe"  [ -d "$1" ] && printf /  esc SGR 0 0  esc EL0  printf '\r' } main() {  set -e  case $1 in  -h|--help)  printf 'shfm -[hv] \n'  exit 0  ;;  -v|--version)  printf 'shfm 0.4.2\n'  exit 0  ;;  *)  cd -- "${1:-"$PWD"}"  ;;  esac  esc_c=$(printf '\033')  bs_char=$(printf '\177')  set -- *  cur=$1  term_resize  term_setup  trap 'term_reset' EXIT INT  trap 'term_resize; term_setup; y=1 y2=1; redraw "$@"' WINCH  y=1 y2=1  redraw "$@"  while key=$(dd ibs=1 count=1 2>/dev/null); do  case $key${esc:=0} in  k?|A2)  term_scroll_up "$@"  ;;  j?|B2)  term_scroll_down "$@"  ;;  l?|C2|"$esc") # ARROW RIGHT  if [ -d "$cur" ] && cd -- "$cur" >/dev/null 2>&1; then  set -- *  y=1 y2=1 cur=$1 ltype=  redraw "$@"  elif [ -e "$cur" ]; then  cmd_run "${SHFM_OPENER:="${EDITOR:=vi}"}" "$cur"  redraw "$@"  fi  ;;  h?|D2|"$bs_char"?) # ARROW LEFT  old_pwd=$PWD  case $ltype in  '') cd .. || continue ;;  *) ltype= ;;  esac  set -- *  y=1 y2=1 cur=$1 hist=1  redraw "$@"  ;;  g?)  case $y in  1) continue  esac  y=1 y2=1 cur=$1  redraw "$@"  ;;  G?)  y=$#  y2=$(($# < bottom ? $# : bottom))  redraw "$@"  ;;  .?)  case ${hidden:=1} in  1) hidden=0; set -- .* ;;  0) hidden=1; set -- *  esac  y=1 y2=1 cur=$1  redraw "$@"  ;;  :?)  prompt "cd: " r  # false positive, behavior intentional  # shellcheck disable=2088  case $ans in  '~') ans=$HOME ;;  '~/'*) ans=$HOME/${ans#"~/"}  esac  cd -- "${ans:="$0"}" >/dev/null 2>&1|| continue  set -- *  y=1 y2=1 cur=$1  redraw "$@"  ;;  /?)  prompt / r  # word splitting and globbing intentional  # shellcheck disable=2086  set -- $ans*  case $1$# in  "$ans*1") set -- 'no results'  esac  y=1 y2=1 cur=$1 ltype="search $PWD/$ans*"  redraw "$@"  status_line "$#"  ;;  -?)  cd -- "$OLDPWD" >/dev/null 2>&1|| continue  set -- *  y=1 y2=1 cur=$1  redraw "$@"  ;;  \~?)  cd || continue  set -- *  y=1 y2=1 cur=$1  redraw "$@"  ;;  \!?)  export SHFM_LEVEL  SHFM_LEVEL=$((SHFM_LEVEL + 1))  cmd_run "${SHELL:=/bin/sh}"  redraw "$@"  ;;  \??)  set -- 'j - down' \  'k - up' \  'l - open file or directory' \  'h - go up level' \  'g - go to top' \  'G - go to bottom' \  'q - quit' \  ': - cd to ' \  '/ - search current directory *' \  '- - go to last directory' \  '~ - go home' \  '! - spawn shell' \  '. - toggle hidden files' \  '? - show keybinds'  y=1 y2=1 cur=$1 ltype=keybinds  redraw "$@"  status_line "$#"  ;;  q?) exit 0 ;;  # handle keys which emit escape sequences  "$esc_c"*) esc=1 ;;  '[1') esc=2 ;;  *) esc=0 ;;  esac  done } main "$@" >/dev/tty