#!/bin/bash -i set -m [ "${BASH_LOADABLES_PATH:=/lib/bash:/usr/lib/bash}" ] cfmakeraw() { cmd=(stty -ignbrk -brkint -parmrk -istrip -inlcr -igncr -icrnl -ixon -opost -echo -echonl -icanon -isig -iexten -parenb cs8) "${cmd[@]}" "$@" >/dev/null } readchar_reset() { : } i() { if [ "$DEBUG" ] then printf 'I: %s\n' "$*" >&2 fi } x() { if [ "$DEBUG" ] then printf '+ %s\n' "${*@Q}" >&2 fi "$@" } readchar_init() { [ $$ != $BASHPID ] || return 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 "i CONT; readchar_SIGCONT" CONT for sig in TSTP TTIN TTOU do trap "i $sig; x kill -STOP \$BASHPID \$SOCAT_PID" $sig done } DEBUG=y readchar_SIGCONT() { if check_foreground then if [ -t 0 ] then 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 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 - 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_char() { readchar_init t= c= count=0 while ! [ "$c" ] do readchar c done readchar_reset if [ -t 1 ] then printf '%s\n' "${c@Q}" else printf %s "$c" fi } read_chars() { readchar_init while readchar do continue done readchar_reset } reader_process() { enable -f sleep sleep if [ "$SHLVL" = 1 -a -t 1 ] then (read_chars) else read_chars fi } run_test() { reader_process & kill -TSTP %reader_process bg %reader_process kill -TSTP %reader_process bg %reader_process kill -TSTP %reader_process bg %reader_process kill -TSTP %reader_process bg %reader_process kill -TSTP %reader_process fg %reader_process }