# ================= # read_chars.bash # ================= # # Bash library for reading from tty and writing to standard out. # Can be suspended and resumed from bash (or with unix job control in # any shell). # # exporting for users: # # read_chars() <-- calls readchar in a loop # # readchar() <-- reads one character # # readchar_init() <-- must be called before readchar() in the same subshell # # The function read_chars is also available as the executable read-tty cfmakeraw() { cmd=(stty -ignbrk -brkint -parmrk -istrip -inlcr -igncr -icrnl -ixon -opost -echo -echonl -icanon -isig -iexten -parenb cs8) "${cmd[@]}" "$@" >/dev/null } i() { if [ "$DEBUG" ] then printf 'I: %s\n' "$*" >&2 fi } x() { if [ "$DEBUG" ] then printf '+ %s\n' "${*@Q}" >&2 fi "$@" } readchar_init() { declare -g SOCAT= SOCAT_PID= declare -g t declare -g -a readchar_termopts [ -t 1 ] && t=y || t= readchar_termopts=( opost onlcr isig intr undef quit undef ) trap "kill $SOCAT_PID 2>/dev/null" EXIT trap "i CONT; readchar_SIGCONT" CONT for sig in TSTP TTIN TTOU do trap "i $sig; x kill -STOP \$BASHPID \$SOCAT_PID 2>/dev/null" $sig done } readchar_SIGCONT() { if check_foreground then if [ -t 0 ] then x cfmakeraw "${readchar_termopts[@]}" fi x kill -CONT $SOCAT_PID 2>/dev/null else x kill -STOP $BASHPID $SOCAT_PID 2>/dev/null fi } check_foreground() { read _pid _comm _state _ppid pgrp _session _tty_nr tpgid _rest < /proc/self/stat [ "$tpgid" = "$pgrp" ] } readchar() { declare -n REPLY="${1:-CHARACTER}" if [ -t 0 ] then if check_foreground then if ! [ "$SOCAT_PID" ] then x cfmakeraw "${readchar_termopts[@]}" fi else sleep .25 return fi fi if ! [ "$SOCAT_PID" ] then exec {SOCAT}< <(trap 'sleep .25' SIGTSTP; socat - -) SOCAT_PID=$! fi if read -n 1 -d '' -r -s -u "$SOCAT" then if [ "$t" ] then printf -v quoted_reply "%s" "${REPLY@Q}" if (( (count += 1 + ${#quoted_reply}) < ${COLUMNS:-80} - 10 )) then seperate=' ' else seperate=$'\n' count=0 fi printf "%s" "$seperate" "$quoted_reply" else if [ "$REPLY" ] then printf "%s" "$REPLY" else printf "\0" fi fi else [ $? -gt 128 ] fi } read_chars() { readchar_init while readchar do continue done }