commit: bfd5710de9b14712c4a621259b951bd10489d0c0
Author: Kerin Millar <kfm <AT> plushkava <DOT> net>
AuthorDate: Mon May 13 23:18:02 2024 +0000
Commit: Sam James <sam <AT> gentoo <DOT> org>
CommitDate: Mon May 13 23:18:02 2024 +0000
URL:
https://gitweb.gentoo.org/proj/gentoo-functions.git/commit/?id=bfd5710d
Simplify _eprint() and _eend() behaviour markedly
Following almost a year of usage and testing, I now consider my
experiments in smart terminal handling to be both a success and a
failure by equal measure. I deem them to be a successful in so far as
the code functions as intended, and that there have been several
positive reports from those intrepid enough to unmask
gentoo-functions-1.0. However, I also deem them to be a failure in so
far as there is a noticeable impact upon performance. In particular,
repeated calls to ebegin() and eend() are handled rather slowly. As
such, this commit greatly simplifies the code whilst retaining the most
compelling traits of the work that has been performed. The exact changes
are described herewith.
The _eprint() function shall now only check whether STDOUT refers to a
tty. In the event that it does, the message may be printed in a
colourised fashion. Otherwise, it shall be printed plainly. No longer
will the terminal be asked to remember the position of the cursor (using
DECSC), nor shall any variables be used for the purpose of keeping
state.
The _eend() function shall no longer attempt to restore the prior
position of the cursor (using DECRC), nor attempt to determine the
present position of the cursor (using ecma48-cpr). Instead, the
behaviour is reduced to that of checking whether STDOUT refers to a
smart terminal. In the event that it does, the success/failure
identifier shall always be printed on the preceding line, right-aligned.
Otherwise, it shall be printed plainly on the present line. It remains
the case that the terminal will be asked to report its dimensions, so
that _eend() can adapt to any adjustments made to the width of the
terminal emulator.
As a consequence of these changes, the _update_cursor_coords() and
_ecma48_cpr() functions are rendered obsolete and have been removed.
Additionally, the "genfun_cols", "genfun_y" and "genfun_is_pending_lf"
variables are rendered obsolete and have been removed. Finally, the
_update_winsize() function has been renamed to _update_columns().
Signed-off-by: Kerin Millar <kfm <AT> plushkava.net>
functions.sh.in | 194 +++++++-------------------------------------------------
test-functions | 34 ----------
2 files changed, 22 insertions(+), 206 deletions(-)
diff --git a/functions.sh.in b/functions.sh.in
index c97219b..e40f1ff 100644
--- a/functions.sh.in
+++ b/functions.sh.in
@@ -14,81 +14,15 @@
#
_eprint()
{
- local color msg
+ local color
+
color=$1
shift
- # Check whether STDOUT is a terminal, and how capable it is.
- _update_tty_level <&1
-
- if [ "${genfun_tty}" -eq 2 ]; then
- # If the cursor is not situated on column 1, print a LF
- # character. The assumption here is that the last call may have
- # been via ebegin, or any of the other printing functions that
- # pass a message without a trailing LF.
- if [ "${genfun_x}" -ne 1 ]; then
- printf '\n'
- fi
- elif [ "${genfun_is_pending_lf}" -eq 1 ]; then
- # We are about to print to a dumb terminal or something other
- # than a terminal. Print a LF character because the last printed
- # message did not end with one. This technique is not ideal.
- # For one thing, it can be thwarted by having called a printing
- # function from a subshell or a shell launched by a subprocess,
- # because the change to the flag variable would be lost. For
- # another, it's possible for the user of the library to be
- # directing STDOUT/STDERR to different places between calls.
- # Such weaknesses cannot be addressed without some form of IPC.
- printf '\n'
- fi
-
- msg=$*
- if [ "${genfun_tty}" -lt 2 ]; then
- if [ "${genfun_tty}" -eq 1 ]; then
- # Print but do not attempt to save the cursor position.
- printf ' %s*%s %s%s' "${color}" "${NORMAL}"
"${genfun_indent}" "${msg}"
- else
- printf ' * %s%s' "${genfun_indent}" "${msg}"
- fi
- if _ends_with_newline "${msg}"; then
- genfun_is_pending_lf=0
- else
- # Record the fact that a LF character is pending.
- genfun_is_pending_lf=1
- fi
- elif ! _ends_with_newline "${msg}"; then
- # Print the message before saving the cursor position with the
- # DECSC sequence. This is a private mode sequence that is not
- # defined by ECMA-48. However, it was introduced by DEC for the
- # VT100 and can be considered as a de-facto standard.
- printf ' %s*%s %s%s\0337' "${color}" "${NORMAL}"
"${genfun_indent}" "${msg}"
+ if [ -t 1 ]; then
+ printf ' %s*%s %s%s' "${color}" "${NORMAL}" "${genfun_indent}"
"$*"
else
- # Strip all trailing LF characters before printing the message.
- while true; do
- msg=${msg%"${genfun_newline}"}
- if ! _ends_with_newline "${msg}"; then
- break
- fi
- done
- printf ' %s*%s %s%s' "${color}" "${NORMAL}" "${genfun_indent}"
"${msg}"
-
- # Determine the current position of the cursor
- _update_cursor_coords <&1
-
- if [ "${genfun_y}" -ne "${genfun_rows}" ]; then
- # Have the terminal save the position of the cursor
- # with DECSC before printing a LF character to advance
- # to the next line.
- printf '\0337\n'
- else
- # The cursor is situated on the last row of the
- # terminal, meaning that vertical scrolling will occur.
- # Move the cursor up by one row with CUU (ECMA-48 CSI)
- # before having the terminal save the position of the
- # cursor with DECSC. Finally, print two LF characters to
- # advance to the next line.
- printf '\033[1A\0337\n\n'
- fi
+ printf ' * %s%s' "${genfun_indent}" "$*"
fi
}
@@ -254,7 +188,7 @@ ebegin()
#
_eend()
{
- local efunc indent msg offset retval
+ local efunc msg retval
efunc=$1
shift
@@ -270,19 +204,13 @@ _eend()
msg=$*
fi
- # Stash the last known terminal dimensions, if any.
- set -- "${genfun_rows}" "${genfun_cols}"
-
- # Check whether STDOUT is a terminal, and how capable it is.
- _update_tty_level <&1
-
if [ "${retval}" -ne 0 ]; then
# If a message was given, print it with the specified function.
if _is_visible "${msg}"; then
"${efunc}" "${msg}"
fi
# Generate an indicator for ebegin's unsuccessful conclusion.
- if [ "${genfun_tty}" -eq 0 ]; then
+ if _update_tty_level <&1; [ "${genfun_tty}" -eq 0 ]; then
msg="[ !! ]"
else
msg="${BRACKET}[ ${BAD}!!${BRACKET} ]${NORMAL}"
@@ -291,63 +219,21 @@ _eend()
return "${retval}"
else
# Generate an indicator for ebegin's successful conclusion.
- if [ "${genfun_tty}" -eq 0 ]; then
+ if _update_tty_level <&1; [ "${genfun_tty}" -eq 0 ]; then
msg="[ ok ]"
else
msg="${BRACKET}[ ${GOOD}ok${BRACKET} ]${NORMAL}"
fi
fi
- if [ "${genfun_tty}" -lt 2 ]; then
- printf ' %s\n' "${msg}"
- genfun_is_pending_lf=0
+ if [ "${genfun_tty}" -eq 2 ]; then
+ # Move the cursor up by one line with CUU before positioning it
+ # horizontally with CHA. Both are formal ECMA-48 CSI sequences.
+ # Print the indicator afterwards.
+ printf '\033[1A\033[%dG %s\n' "$(( genfun_cols - 6 +
genfun_offset ))" "${msg}"
else
- # Provided that the terminal has not since been resized, it may
- # be possible to write the indicator on the same row as the
- # last printed message, even if it were LF-terminated.
- if [ "${genfun_rows}" -eq "$1" ] && [ "${genfun_cols}" -eq "$2"
]; then
- # Stash the current position of the cursor.
- set -- "${genfun_y}" "${genfun_x}"
-
- # Using the DECRC sequence, restore the cursor position
- # to wherever it was just after the last message was
- # printed, but before the trailing LF character, if any.
- # This is a private mode sequence, and thus not defined
- # by ECMA-48. However, it was introduced by DEC for the
- # VT100 and can be considered as a de-facto standard.
- printf '\0338'
-
- # Determine the position of the cursor again.
- _update_cursor_coords <&1
-
- # Check whether the act of restoring the cursor position
- # moved it to a different row, excepting the immediately
- # preceding row. If it did, assume that scrolling has
- # occurred since printing the last message and move the
- # cursor back to where it was with CUP (ECMA-48 CSI).
- offset=$(( $1 - genfun_y ))
- if [ "${offset}" -lt 0 ] || [ "${offset}" -gt 1 ]; then
- printf '\033[%d;%dH' "$1" "$2"
- genfun_y=$1
- genfun_x=$2
- fi
- fi
-
- # Calculate the column at which the indicator may be printed.
- indent=$(( genfun_cols - genfun_x - 6 ))
-
- # Determine whether the cursor needs to be repositioned.
- if [ "${indent}" -gt 0 ]; then
- # Use CHA (ECMA-48 CSI) to move the cursor to the right.
- printf '\033[%dG' "$(( genfun_x + indent +
genfun_offset ))"
- elif [ "${indent}" -lt 0 ]; then
- # The indent is negative, meaning that there is not
- # enough room. Arrange for the indicator to be printed
- # on the next line instead.
- printf '\n\033[%dG' "$(( genfun_cols - 6 +
genfun_offset ))"
- fi
-
- # Finally, print the indicator.
+ # The standard output does not refer to a sufficiently capable
+ # terminal. Print only the indicator.
printf ' %s\n' "${msg}"
fi
@@ -605,68 +491,35 @@ _ends_with_newline()
&& ! case $1 in *"${genfun_newline}") false ;; esac
}
+
_update_tty_level()
{
# Grade the capability of the terminal attached to STDIN (if any) on a
# scale of 0 to 2, assigning the resulting value to genfun_tty. If no
# terminal is detected, the value shall be 0. If a dumb terminal is
# detected, the value shall be 1. If a smart terminal is detected, the
- # value shall be 2.
- #
- # In the case that a smart terminal is detected, its dimensions shall
- # be assigned to genfun_cols and genfun_rows, and the position of the
- # cursor shall be assigned to genfun_x and genfun_y. Further, it may
- # reasonably be assumed that the ECMA-48 CSI and DECSC/DECRC escape
- # sequences are supported.
+ # value shall be 2. For a terminal to be considered as smart, it must be
+ # able to successfuly reports its dimensions.
if [ ! -t 0 ]; then
genfun_tty=0
- elif _has_dumb_terminal || ! _update_winsize || !
_update_cursor_coords; then
+ elif _has_dumb_terminal || ! _update_columns; then
genfun_tty=1
else
genfun_tty=2
fi
}
-_update_winsize()
+_update_columns()
{
local ifs
- # The following use of stty(1) is portable as of POSIX Issue 8. It would
- # be beneficial to leverage the checkwinsize option in bash but the
- # implementation is buggy. Given that Chet has agreed to investigate,
- # it may eventually become possible to support it.
+ # The following use of stty(1) is portable as of POSIX Issue 8.
ifs=$IFS
IFS=' '
# shellcheck disable=2046
set -- $(stty size 2>/dev/null)
IFS=$ifs
- if [ "$#" -eq 2 ] && is_int "$1" && is_int "$2" && [ "$1" -gt 0 ] && [
"$2" -gt 0 ]; then
- genfun_rows=$1
- genfun_cols=$2
- else
- genfun_rows=
- genfun_cols=
- false
- fi
-}
-
-_update_cursor_coords()
-{
- # shellcheck disable=2046
- set -- $(_ecma48_cpr)
- if [ "$#" -eq 2 ] && is_int "$1" && is_int "$2"; then
- genfun_y=$1
- genfun_x=$2
- else
- genfun_y=
- genfun_x=
- false
- fi
-}
-
-_ecma48_cpr()
-{
- @GENTOO_LIBEXEC_DIR@/ecma48-cpr
+ [ "$#" -eq 2 ] && is_int "$2" && [ "$2" -gt 0 ] && genfun_cols=$2
}
# This is the main script, please add all functions above this point!
@@ -685,9 +538,6 @@ genfun_indent=
genfun_newline='
'
-# Whether the last printed message is pending a concluding LF character.
-genfun_is_pending_lf=0
-
# In Emacs, M-x term opens an "eterm-color" terminal, whose implementation of
# the CHA (ECMA-48 CSI) sequence suffers from an off-by-one error.
if [ "${INSIDE_EMACS}" ] && [ "${TERM}" = "eterm-color" ]; then
diff --git a/test-functions b/test-functions
index 513deb3..a2d15b4 100755
--- a/test-functions
+++ b/test-functions
@@ -323,31 +323,6 @@ test_is_visible() {
iterate_tests 2 "$@"
}
-test_update_cursor_coords() {
- skip() {
- printf 'ok %d - _update_cursor_coords # SKIP\n' "$((testnum +=
1))"
- }
- if _has_dumb_terminal; then
- skip
- elif ! ctty=$(ps -p "$$" -otty= 2>/dev/null) || [ -z "${ctty}" ]; then
- # The ps(1) implementation is defective or non-compliant
- skip
- elif [ "${ctty}" = "?" ]; then
- # No controlling terminal is available
- skip
- elif [ ! -e /dev/tty ]; then
- skip
- elif ! _update_winsize </dev/tty; then
- # The terminal isn't smart
- skip
- elif ! _update_cursor_coords <>/dev/tty; then
- printf 'not ok %d - _update_cursor_coords\n' "$((testnum += 1))"
- false
- else
- printf 'ok %d - _update_cursor_coords (x = %d, y = %d)\n'
"$((testnum += 1))" "${genfun_x}" "${genfun_y}"
- fi
-}
-
test_yesno() {
set -- \
0 yes \
@@ -453,14 +428,6 @@ if ! . ./functions.sh; then
bailout "Couldn't source ./functions.sh"
fi
-# Since the test suite is normally executed during the src_test phase, the
-# ecma48-cpr utility will not yet have been installed. Account for that by
-# redeclaring its shim function.
-if [ "${EBUILD_PHASE}" = test ]; then
- export BUILD_DIR="${PWD}"
- _ecma48_cpr() { "${BUILD_DIR}"/ecma48-cpr; }
-fi
-
assign_tmpdir
export TEST_GENFUNCS=1
@@ -477,7 +444,6 @@ test_esyslog || rc=1
test_is_identifier || rc=1
test_is_int || rc=1
test_is_visible || rc=1
-test_update_cursor_coords || rc=1
test_yesno || rc=1
cleanup_tmpdir