commit: 8c68f52ab3600d1b31a27c20c88ca2218c148348
Author: Kerin Millar <kfm <AT> plushkava <DOT> net>
AuthorDate: Sun Jan 25 23:42:06 2026 +0000
Commit: Sam James <sam <AT> gentoo <DOT> org>
CommitDate: Tue Jan 27 08:54:17 2026 +0000
URL:
https://gitweb.gentoo.org/proj/gentoo-functions.git/commit/?id=8c68f52a
Add the tty module
This commit introduces a new module, named "tty". It incorporates five
private functions, all of which had previously resided in the
"functions.sh" unit.
- _has_dumb_terminal()
- _should_throttle()
- _update_columns()
- _update_time()
- _update_tty_level()
Though this new module may eventually provide some public functions, the
primary intention is to reduce the size of the core.
Signed-off-by: Kerin Millar <kfm <AT> plushkava.net>
functions.sh | 151 ++-----------------------------------------
functions/experimental.sh | 1 +
functions/rc.sh | 10 +--
functions/tty.sh | 159 ++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 171 insertions(+), 150 deletions(-)
diff --git a/functions.sh b/functions.sh
index 6d9aa3b..ac4b69e 100644
--- a/functions.sh
+++ b/functions.sh
@@ -21,8 +21,6 @@
# BASH : whether bash-specific features may be employed
# BASH_VERSINFO : whether bash-specific features may be employed
# BASHPID : may be used by _update_columns() and _update_pid()
-# COLUMNS : may be used by _update_columns() to get the column count
-# EPOCHREALTIME : potentially used by _update_time() to get the time
# GENFUN_MODULES : which of the optional function collections must be sourced
# IFS : warn() operands are joined by its first character
# INVOCATION_ID : used by from_unit()
@@ -32,7 +30,6 @@
# RC_OPENRC_PID : used by from_runscript()
# SENTINEL : can define a value separating two distinct argument lists
# SYSTEMD_EXEC_PID : used by from_unit()
-# TERM : used to detect dumb terminals
# YASH_VERSION : for detecting yash before checking for incompatible
options
#------------------------------------------------------------------------------#
@@ -767,14 +764,6 @@ _collect_entropy() {
test "${#genfun_entropy}" -eq 128
}
-#
-# Determines whether the terminal is a dumb one.
-#
-_has_dumb_terminal()
-{
- ! case ${TERM} in *dumb*) false ;; esac
-}
-
#
# Potentially called by quote_args(), duly acting as a bash-optimised variant.
# It leverages the ${paramater@Q} form of expansion, which is supported as of
@@ -797,76 +786,6 @@ if [ "${BASH_VERSINFO-0}" -ge 5 ]; then
}
'
fi
-
-#
-# Considers the first parameter as a number of centiseconds and determines
-# whether fewer have elapsed since the last occasion on which the function was
-# called, or whether the last genfun_time update resulted in integer overflow.
-#
-_should_throttle()
-{
- _update_time || return
-
- # shellcheck disable=2329
- _should_throttle()
- {
- _update_time || return
- if [ "$(( (genfun_time < 0 && genfun_last_time >= 0) ||
genfun_time - genfun_last_time > $1 ))" -eq 1 ]
- then
- genfun_last_time=${genfun_time}
- false
- fi
-
- }
-
- genfun_last_time=${genfun_time}
- false
-}
-
-#
-# Determines whether the terminal on STDIN is able to report its dimensions.
-# Upon success, the number of columns shall be stored in genfun_cols.
-#
-_update_columns()
-{
- # shellcheck disable=3044
- if [ "${BASH}" ] && shopt -q checkwinsize; then
- genfun_bin_true=$(whenceforth -x true)
- fi
-
- _update_columns()
- {
- local IFS
-
- # Two optimisations are applied. Firstly, the rate at which
- # updates can be performed is throttled to intervals of half a
- # second. Secondly, if running on bash then the COLUMNS variable
- # may be gauged, albeit only in situations where doing so can be
- # expected to work reliably.
- # shellcheck disable=3028
- if from_portage; then
- # Python's pty module is broken. For now, expect for
- # portage to have exported COLUMNS to the environment.
- set -- 0 "${COLUMNS}"
- elif _should_throttle 50; then
- test "${genfun_cols}"
- return
- elif [ "${genfun_bin_true}" ] && [ "$$" = "${BASHPID}" ]; then
- # To execute the true binary is faster than stty(1).
- "${genfun_bin_true}"
- set -- 0 "${COLUMNS}"
- else
- # This use of stty(1) is portable as of POSIX-1.2024.
- IFS=' '
- # shellcheck disable=2046
- set -- $(stty size 2>/dev/null)
- fi
- [ "$#" -eq 2 ] && is_int "$2" && [ "$2" -gt 0 ] &&
genfun_cols=$2
- }
-
- _update_columns
-}
-
#
# Determines the PID of the current shell process. Upon success, the PID shall
# be assigned to genfun_pid. Otherwise, the return value shall be greater than
@@ -908,71 +827,6 @@ _update_pid()
_update_pid
}
-#
-# Determines either the number of centiseconds elapsed since the unix epoch or
-# the number of centiseconds that the operating system has been online,
-# depending on the capabilities of the shell and/or platform. Upon success, the
-# obtained value shall be assigned to genfun_time. Otherwise, the return value
-# shall be greater than 0.
-#
-_update_time()
-{
- # shellcheck disable=3028
- if [ "${BASH}" ] && [ "${EPOCHREALTIME}" != "${EPOCHREALTIME}" ]; then
- # shellcheck disable=2034,3045
- _update_time()
- {
- # Setting LC_NUMERIC as C ensures a radix character of
- # U+2E, duly affecting both EPOCHREALTIME and printf.
- local LC_ALL LC_NUMERIC=C cs s timeval
-
- timeval=${EPOCHREALTIME}
- s=${timeval%.*}
- printf -v cs '%.2f' ".${timeval#*.}"
- if [ "${cs}" = "1.00" ]; then
- cs=100
- else
- cs=${cs#0.} cs=${cs#0}
- fi
- genfun_time=$(( s * 100 + cs ))
- }
- elif [ -f /proc/uptime ] && [ ! "${YASH_VERSION}" ]; then
- # Yash is blacklisted because it dies upon integer overflow.
- _update_time()
- {
- local cs s
-
- IFS='. ' read -r s cs _ < /proc/uptime \
- && genfun_time=$(( s * 100 + ${cs#0} ))
- }
- else
- _update_time()
- {
- return 2
- }
- fi
-
- _update_time
-}
-
-#
-# Grades the capability of the terminal attached to STDIN, assigning the level
-# to genfun_tty. If no terminal is detected, the level shall be 0. If a dumb
-# terminal is detected, the level shall be 1. If a smart terminal is detected,
-# the level shall be 2. For a terminal to be considered as smart, it must be
-# able to successfully report its dimensions.
-#
-_update_tty_level()
-{
- if [ ! -t 0 ]; then
- genfun_tty=0
- elif _has_dumb_terminal || ! _update_columns; then
- genfun_tty=1
- else
- genfun_tty=2
- fi
-}
-
#
# Takes the first parameter as the path of a gentoo-functions module then
# determines whether it has been requested by attempting to match its basename
@@ -1036,6 +890,11 @@ if [ ! "${GENFUN_MODULES+set}" ]; then
fi
fi
+# Both of the rc and experimental modules depend on the tty module.
+if contains_any "${GENFUN_MODULES}" rc experimental; then
+ GENFUN_MODULES="${GENFUN_MODULES}${GENFUN_MODULES+ }tty"
+fi
+
# Source any modules that have been selected by the GENFUN_MODULES variable.
for _ in "${genfun_basedir}/functions"/*.sh; do
if ! test -e "$_"; then
diff --git a/functions/experimental.sh b/functions/experimental.sh
index 1cbebf3..6b597ce 100644
--- a/functions/experimental.sh
+++ b/functions/experimental.sh
@@ -66,6 +66,7 @@ hr()
{
local c hr i length
+ # shellcheck disable=2154
if [ "$#" -ge 2 ] && is_int "$2"; then
length=$2
elif _update_tty_level <&1; [ "${genfun_tty}" -eq 2 ]; then
diff --git a/functions/rc.sh b/functions/rc.sh
index 7297d75..aade242 100644
--- a/functions/rc.sh
+++ b/functions/rc.sh
@@ -16,7 +16,7 @@
# INSIDE_EMACS : whether to work around an emacs-specific bug in _eend()
# NO_COLOR : whether colored output should be suppressed
# RC_NOCOLOR : like NO_COLOR but deprecated
-# TERM : whether to work around an emacs-specific bug in _eend()
+# TERM : for dumb tty detection and to mitigate an emacs bug in
_eend()
# TEST_GENFUNCS : used for testing the behaviour of get_bootparam()
#------------------------------------------------------------------------------#
@@ -427,11 +427,13 @@ _has_color_terminal()
{
local colors
+ case ${TERM} in
+ *dumb*) return 1
+ esac
+
# The tput(1) invocation is not portable, though ncurses suffices. In
# this day and age, it is exceedingly unlikely that it will be needed.
- if _has_dumb_terminal; then
- false
- elif colors=$(tput colors 2>/dev/null) && is_int "${colors}"; then
+ if colors=$(tput colors 2>/dev/null) && is_int "${colors}"; then
test "${colors}" -gt 0
else
true
diff --git a/functions/tty.sh b/functions/tty.sh
new file mode 100644
index 0000000..91e4d61
--- /dev/null
+++ b/functions/tty.sh
@@ -0,0 +1,159 @@
+# Copyright 1999-2024 Gentoo Authors
+# Distributed under the terms of the GNU General Public License v2
+# shellcheck shell=sh disable=3043
+
+# This file contains several internal functions pertaining to TTY handling.
+# Please refer to ../functions.sh for coding conventions.
+
+# The following variables affect initialisation and/or function behaviour.
+
+# BASH : whether bash-specific features may be employed
+# BASHPID : may be used by _update_columns() and _update_pid()
+# COLUMNS : may be used by _update_columns() to get the column count
+# EPOCHREALTIME : potentially used by _update_time() to get the time
+# TERM : used to detect dumb terminals
+
+#------------------------------------------------------------------------------#
+
+#
+# Determines whether the terminal is a dumb one.
+#
+_has_dumb_terminal()
+{
+ ! case ${TERM} in *dumb*) false ;; esac
+}
+
+#
+# Considers the first parameter as a number of centiseconds and determines
+# whether fewer have elapsed since the last occasion on which the function was
+# called, or whether the last genfun_time update resulted in integer overflow.
+#
+_should_throttle()
+{
+ _update_time || return
+
+ # shellcheck disable=2329
+ _should_throttle()
+ {
+ _update_time || return
+ if [ "$(( (genfun_time < 0 && genfun_last_time >= 0) ||
genfun_time - genfun_last_time > $1 ))" -eq 1 ]
+ then
+ genfun_last_time=${genfun_time}
+ false
+ fi
+
+ }
+
+ genfun_last_time=${genfun_time}
+ false
+}
+
+#
+# Determines whether the terminal on STDIN is able to report its dimensions.
+# Upon success, the number of columns shall be stored in genfun_cols.
+#
+_update_columns()
+{
+ # shellcheck disable=3044
+ if [ "${BASH}" ] && shopt -q checkwinsize; then
+ genfun_bin_true=$(whenceforth -x true)
+ fi
+
+ _update_columns()
+ {
+ local IFS
+
+ # Two optimisations are applied. Firstly, the rate at which
+ # updates can be performed is throttled to intervals of half a
+ # second. Secondly, if running on bash then the COLUMNS variable
+ # may be gauged, albeit only in situations where doing so can be
+ # expected to work reliably.
+ # shellcheck disable=3028
+ if from_portage; then
+ # Python's pty module is broken. For now, expect for
+ # portage to have exported COLUMNS to the environment.
+ set -- 0 "${COLUMNS}"
+ elif _should_throttle 50; then
+ test "${genfun_cols}"
+ return
+ elif [ "${genfun_bin_true}" ] && [ "$$" = "${BASHPID}" ]; then
+ # To execute the true binary is faster than stty(1).
+ "${genfun_bin_true}"
+ set -- 0 "${COLUMNS}"
+ else
+ # This use of stty(1) is portable as of POSIX-1.2024.
+ IFS=' '
+ # shellcheck disable=2046
+ set -- $(stty size 2>/dev/null)
+ fi
+ [ "$#" -eq 2 ] && is_int "$2" && [ "$2" -gt 0 ] &&
genfun_cols=$2
+ }
+
+ _update_columns
+}
+
+#
+# Determines either the number of centiseconds elapsed since the unix epoch or
+# the number of centiseconds that the operating system has been online,
+# depending on the capabilities of the shell and/or platform. Upon success, the
+# obtained value shall be assigned to genfun_time. Otherwise, the return value
+# shall be greater than 0.
+#
+_update_time()
+{
+ # shellcheck disable=3028
+ if [ "${BASH}" ] && [ "${EPOCHREALTIME}" != "${EPOCHREALTIME}" ]; then
+ # shellcheck disable=2034,3045
+ _update_time()
+ {
+ # Setting LC_NUMERIC as C ensures a radix character of
+ # U+2E, duly affecting both EPOCHREALTIME and printf.
+ local LC_ALL LC_NUMERIC=C cs s timeval
+
+ timeval=${EPOCHREALTIME}
+ s=${timeval%.*}
+ printf -v cs '%.2f' ".${timeval#*.}"
+ if [ "${cs}" = "1.00" ]; then
+ cs=100
+ else
+ cs=${cs#0.} cs=${cs#0}
+ fi
+ genfun_time=$(( s * 100 + cs ))
+ }
+ elif [ -f /proc/uptime ] && [ ! "${YASH_VERSION}" ]; then
+ # Yash is blacklisted because it dies upon integer overflow.
+ _update_time()
+ {
+ local cs s
+
+ IFS='. ' read -r s cs _ < /proc/uptime \
+ && genfun_time=$(( s * 100 + ${cs#0} ))
+ }
+ else
+ _update_time()
+ {
+ return 2
+ }
+ fi
+
+ _update_time
+}
+
+#
+# Grades the capability of the terminal attached to STDIN, assigning the level
+# to genfun_tty. If no terminal is detected, the level shall be 0. If a dumb
+# terminal is detected, the level shall be 1. If a smart terminal is detected,
+# the level shall be 2. For a terminal to be considered as smart, it must be
+# able to successfully report its dimensions.
+#
+_update_tty_level()
+{
+ # shellcheck disable=2034
+ if [ ! -t 0 ]; then
+ genfun_tty=0
+ elif _has_dumb_terminal || ! _update_columns; then
+ genfun_tty=1
+ else
+ genfun_tty=2
+ fi
+}