From mboxrd@z Thu Jan  1 00:00:00 1970
Return-Path: <gentoo-commits+bounces-1488255-garchives=archives.gentoo.org@lists.gentoo.org>
Received: from lists.gentoo.org (pigeon.gentoo.org [208.92.234.80])
	(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
	 key-exchange X25519 server-signature RSA-PSS (2048 bits))
	(No client certificate requested)
	by finch.gentoo.org (Postfix) with ESMTPS id DE23E15800F
	for <garchives@archives.gentoo.org>; Sun, 19 Feb 2023 16:14:37 +0000 (UTC)
Received: from pigeon.gentoo.org (localhost [127.0.0.1])
	by pigeon.gentoo.org (Postfix) with SMTP id 7B3E5E08EB;
	Sun, 19 Feb 2023 16:14:36 +0000 (UTC)
Received: from smtp.gentoo.org (woodpecker.gentoo.org [140.211.166.183])
	(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
	 key-exchange X25519 server-signature RSA-PSS (4096 bits))
	(No client certificate requested)
	by pigeon.gentoo.org (Postfix) with ESMTPS id 61119E08EB
	for <gentoo-commits@lists.gentoo.org>; Sun, 19 Feb 2023 16:14:36 +0000 (UTC)
Received: from oystercatcher.gentoo.org (oystercatcher.gentoo.org [148.251.78.52])
	(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
	 key-exchange X25519 server-signature RSA-PSS (4096 bits))
	(No client certificate requested)
	by smtp.gentoo.org (Postfix) with ESMTPS id 790AB340EC9
	for <gentoo-commits@lists.gentoo.org>; Sun, 19 Feb 2023 16:14:35 +0000 (UTC)
Received: from localhost.localdomain (localhost [IPv6:::1])
	by oystercatcher.gentoo.org (Postfix) with ESMTP id 8BC7B8BC
	for <gentoo-commits@lists.gentoo.org>; Sun, 19 Feb 2023 16:14:32 +0000 (UTC)
From: "Sam James" <sam@gentoo.org>
To: gentoo-commits@lists.gentoo.org
Content-Transfer-Encoding: 8bit
Content-type: text/plain; charset=UTF-8
Reply-To: gentoo-dev@lists.gentoo.org, "Sam James" <sam@gentoo.org>
Message-ID: <1676823259.60775e89a1411c772def465ef9b901b3fe78e450.sam@gentoo>
Subject: [gentoo-commits] proj/gentoo-functions:master commit in: /
X-VCS-Repository: proj/gentoo-functions
X-VCS-Files: functions.sh
X-VCS-Directories: /
X-VCS-Committer: sam
X-VCS-Committer-Name: Sam James
X-VCS-Revision: 60775e89a1411c772def465ef9b901b3fe78e450
X-VCS-Branch: master
Date: Sun, 19 Feb 2023 16:14:32 +0000 (UTC)
Precedence: bulk
List-Post: <mailto:gentoo-commits@lists.gentoo.org>
List-Help: <mailto:gentoo-commits+help@lists.gentoo.org>
List-Unsubscribe: <mailto:gentoo-commits+unsubscribe@lists.gentoo.org>
List-Subscribe: <mailto:gentoo-commits+subscribe@lists.gentoo.org>
List-Id: Gentoo Linux mail <gentoo-commits.gentoo.org>
X-BeenThere: gentoo-commits@lists.gentoo.org
X-Auto-Response-Suppress: DR, RN, NRN, OOF, AutoReply
X-Archives-Salt: 961092be-0607-4a68-84dd-b3503279596c
X-Archives-Hash: c7f6c06187fbbea9c2ba74802235bab6

commit:     60775e89a1411c772def465ef9b901b3fe78e450
Author:     Kerin Millar <kfm <AT> plushkava <DOT> net>
AuthorDate: Sat Feb 18 16:55:17 2023 +0000
Commit:     Sam James <sam <AT> gentoo <DOT> org>
CommitDate: Sun Feb 19 16:14:19 2023 +0000
URL:        https://gitweb.gentoo.org/proj/gentoo-functions.git/commit/?id=60775e89

Simplify ECMA-48 sequence definition and dial back on the use of tput(1)

Up until very recently, functions.sh would not attempt to determine
whether a given message was being printed to a terminal. Over the years,
this has caused some deleterious effects, such as printing ECMA-48 CSI
and SGR sequences to file descriptors that are not necessarily attached
to a terminal. Another issue used to be that these sequences would be
emitted to terminals identifying themselves as "dumb".

Rather than tackle the underlying problems, the historical response has
tended towards further entrenching the use of the ncurses implementation
of tput(1). While it has its uses, the way in which it is employed is in
no way portable. POSIX only guarantees the availability of the clear,
init and reset operands. Presently, the colors operand is used as a
heuristic to determine whether the ncurses implementation is available
before proceeding to run a series of commands that will usually output
ECMA-48 SGR sequences that are standard for all but ancient and/or
obscure video terminals.

Another present use of tput(1) is in generating the CUU1 and CUF
sequences. In the overwhelming majority of cases, it will generate these
as standard ECMA-48 CSI sequences. To put this in perspective, the DEC
VT100, a video terminal released almost 45 years ago, supports these
sequences. The ECMA-48 specification, itself, is almost as old.

Unfortunately, reasoning with such matters tends towards a dichotomy.
On the one hand, there is a camp that considers it a cardinal sin to
attempt to emit an ANSI escape sequence without first having consulted a
specific implementation of tput(1). On the other, there is the camp that
points out that ECMA-48 sequences are highly portable and that video
terminals from the 1970s and 1980s are largely obsolete. Here are two
articles that present opposing points of view.

  https://mywiki.wooledge.org/BashFAQ/037
  https://xn--rpa.cc/irl/term.html

Of late, I find the arguments presented by the latter camp to be
increasingly convincing. As such, the goal of this commit is twofold.

Firstly, to implement an independent method for detecting a dumb
terminal. To that end, a _has_dumb_terminal() function has been added,
which simply checks the value of TERM. This, alone, determines whether
the CUU1 and CUF sequences should be assigned, with tput(1) no longer
being used to generate them.

Secondly, to further dial back on the use of tput(1) by not using it to
generate the SGR (colour) sequences. While I believe that nobody would
notice if we were to do away with the use of tput(1) altogether, I have
taken a nuanced approach by continuing to use it for detecting the
number of available colours. To that end, a _has_monochrome_terminal()
function has been implemented. This, alone, determines whether the SGR
sequences should be assigned. The function works by first checking
whether the terminal is dumb. If not, it tries to run "tput colors",
before checking whether -1 colours are reported, for that is how the
ncurses implementation reports the absence of colour support.

The resulting code is simpler and more pleasing to read, with tput being
run once, at most. Also, because there is no longer a requirement to
handle the CSI sequences in a raw form, they are now declared as strings
containing backslash-escape sequences, with printf %b being used to
decode them. This has the advantage of not emitting the raw codes to
STDERR while using xtrace for debugging.

In the future, I think that it may well be possible to drop the function
that detects a monochrome terminal and rely on dumb terminal detection
instead but let's see how it goes.

Signed-off-by: Kerin Millar <kfm <AT> plushkava.net>
Signed-off-by: Sam James <sam <AT> gentoo.org>

 functions.sh | 46 ++++++++++++++++++++++++++++------------------
 1 file changed, 28 insertions(+), 18 deletions(-)

diff --git a/functions.sh b/functions.sh
index c287b24..53f3205 100644
--- a/functions.sh
+++ b/functions.sh
@@ -253,7 +253,7 @@ _eend()
 	fi
 
 	if [ "${is_tty}" -eq 1 ] && [ -n "${genfun_endcol}" ]; then
-		printf '%s %s\n' "${genfun_endcol}" "${msg}"
+		printf '%b %s\n' "${genfun_endcol}" "${msg}"
 	else
 		[ "${genfun_lastcall}" = ebegin ] || genfun_lastbegun_strlen=0
 		printf "%$(( cols - genfun_lastbegun_strlen - 7 ))s %s\n" '' "${msg}"
@@ -474,6 +474,22 @@ is_identifier()
 	esac
 )
 
+_has_dumb_terminal() {
+	! case ${TERM} in *dumb*) false ;; esac
+}
+
+_has_monochrome_terminal() {
+	local colors
+
+	# 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
+		true
+	elif colors=$(tput colors 2>/dev/null) && is_int "${colors}"; then
+		test "${colors}" -eq -1
+	fi
+}
+
 # This is the main script, please add all functions above this point!
 # shellcheck disable=2034
 RC_GOT_FUNCTIONS="yes"
@@ -533,26 +549,20 @@ for _ in 1 2 3; do
 	fi
 done
 
-# Set an ECMA-48 CSI sequence, allowing for eend to line up the [ ok ] string.
-{
-	genfun_endcol="$(tput cuu1)" \
-	&& genfun_endcol="${genfun_endcol}$(tput cuf -- "$(( genfun_cols - 7 ))")" \
-	|| genfun_endcol="$(printf '\033[A\033[%dC' "$(( genfun_cols - 7 ))")"
-} 2>/dev/null
+if _has_dumb_terminal; then
+	unset -v genfun_endcol
+else
+	# Set some ECMA-48 CSI sequences (CUU1 and CUF) for cursor positioning.
+	# These are standard and, conveniently, documented by console_codes(4).
+	genfun_endcol="\\033[A\\033[$(( genfun_cols - 7 ))C"
+fi
 
-# Setup the colors so our messages all look pretty
-if yesno "${RC_NOCOLOR}"; then
+if _has_monochrome_terminal || yesno "${RC_NOCOLOR}"; then
 	unset -v BAD BRACKET GOOD HILITE NORMAL WARN
-elif { hash tput && tput colors >/dev/null; } 2>/dev/null; then
-	genfun_bold=$(tput bold) genfun_norm=$(tput sgr0)
-	BAD="${genfun_norm}${genfun_bold}$(tput setaf 1)"
-	BRACKET="${genfun_norm}${genfun_bold}$(tput setaf 4)"
-	GOOD="${genfun_norm}${genfun_bold}$(tput setaf 2)"
-	HILITE="${genfun_norm}${genfun_bold}$(tput setaf 6)"
-	NORMAL="${genfun_norm}"
-	WARN="${genfun_norm}${genfun_bold}$(tput setaf 3)"
-	unset -v genfun_bold genfun_norm
 else
+	# Define some ECMA-48 SGR sequences for color support. These variables
+	# are public, in so far as users of the library may be expanding them.
+	# The sequences are also documented by console_codes(4).
 	BAD=$(printf '\033[31;01m')
 	BRACKET=$(printf '\033[34;01m')
 	GOOD=$(printf '\033[32;01m')