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
+}

Reply via email to