http://invisible-island.net/ncurses/
Copyright © 2011-2018,2022 by Thomas E. Dickey
Tack is a program that can be used to verify or refine a terminfo (terminal information) description of a terminal.
The program's early history is summarized in the HISTORY file in the sources:
Daniel Weaver sent me email in October 1997, suggesting that it might be incorporated into the ncurses project. We discussed this over the next few months, with an unexpected conclusion: RMS persuaded Daniel Weaver to GPL-license tack.
Based on Daniel's initial request, I agreed to make some minor changes to ncurses to allow tack to use some of the undocumented entrypoints and symbols in the ncurses library.
Given suitable disclaimers, RMS saw no problem in distributing tack along with ncurses. So I agreed to do this.
I added tack to the ncurses snapshots starting in April 1999, noting in the NEWS file that it is not part of ncurses.
I designed the ncurses configure script so that it would check for the existence of the tack subdirectory before deciding whether to build it. Thus, tack was simply bundled into the same tar ball, and since it was clearly marked as not being part of ncurses, it was not a problem for ncurses for some time.
Due to its license difference versus ncurses, I made relatively few changes to tack for some time (932 insertions, 653 deletions, less than 10% of the program). Like the Ada95 binding and terminfo database, tack resides in a separate source archive. However, there is a break in its change-log, since it was maintained for some time within the ncurses source repository.
However, early in 2007 I happened to notice more than one of my programs marked with an incorrect license, e.g., diffstat and xterm both as "GPL". That prompted me to look for more cases, and I found several. One was tack (more than one instance).
I discussed this with the package maintainers, pointing out that
During the discussion I noted that tack relied on being built within the ncurses source tree. For one packager, it was enough to provide a way to build it outside the tree (done January 2007. That did not work for openSUSE, so I removed tack from the ncurses tree a few weeks later.
After further discussion, openSUSE moved tack to its own package, as shown here.
As the maintainer for tack, I cannot simply remove it from ncurses without providing for keeping it working. I wrote a configure script. Early on, Daniel Weaver had proposed writing one, but that never happened, since we integrated it directly as a subdirectory of the ncurses tree. To make tack build outside the ncurses tree (and also address the concerns of packagers such as openSUSE who did not want to revise their packages), there were several things to consider:
Depending on the system and configuration, tack may use several private symbols from the ncurses library:
Symbol | First used | Optional library |
---|---|---|
_nc_copy_termtype | 2006/06/24 | ncurses, tinfo |
_nc_fallback | 2007/01/28 | ncurses, tinfo |
_nc_find_entry | 1.00 (1997) | ncurses, tinfo |
_nc_free_termtype | 2007/04/08 | ncurses, tinfo |
_nc_free_tic | 2007/04/08 | ncurses, tic |
cur_term or _nc_get_curterm | 1.00 (1997) | ncurses, tinfo |
_nc_get_hash_table | 1.00 (1997) | ncurses, tinfo |
_nc_get_tty_mode | 1999/02/07 | ncurses, tinfo |
_nc_init_acs | 2006/11/25 | ncurses, tinfo |
_nc_read_entry | 1.00 (1997) | ncurses, tinfo |
_nc_reset_input | 1.00 (1997) | ncurses, tic |
_nc_strstr | 2007/01/28 | ncurses, tinfo |
_nc_tic_expand | 1.00 (1997) | ncurses, tic |
_nc_trans_string | 1.00 (1997) | ncurses, tic |
That is, the list doubled in size since the first release of tack. I added a third of the entrypoints to this list after moving tack from the ncurses snapshots to a separate tarball.
Packaging does not stop with a configure script. Starting in early 2010, I began creating package scripts (RPM and Debian) for most of the programs that I maintain. Those use a more restricted build environment than the other test-builds, and help me see dependency issues from the packager's perspective. I added that to tack in September 2010. But right before that, I spent some time on code-cleanup, and reformatted it throughout with the same indentation as that which I use in other programs.
I revisited the problems raised by tack's use
of ncurses's internal interfaces while preparing
the release for ncurses 6.1 in mid-2017. In that release, I
extended the TERMINAL
data structure, adding a new
TERMTYPE2
to hold “extended numbers”
(larger than 32768). That changed the size of
TERMINAL
, which would not be a problem for
applications that used the documented API. To discourage
developers from using the details (including the size) of this
structure, I made it opaque. Tack was
using TERMINAL
directly to modify the terminal I/O
modes. There is an API for that; modifying tack
was simple.
Reconsidering it, eliminating the use of ncurses internals would be an improvement. Daniel Weaver had indicated that he used these functions mainly for convenience. In practice, that was partly true:
Initially, tack no longer used
_nc_get_tty_mode
or _nc_strstr
. I
eliminated the former when making TERMINAL
opaque, and had discarded the latter in 2013.
The call to _nc_get_curterm
went away in
1997, replaced by _nc_get_tty_mode
.
Likewise, it had no valid need for
_nc_init_acs
. According to Weaver's comment that
was done to ensure that the acs_map[]
array was
initialized (so that its use of curses definitions
for line-drawing will work). I made tack
initialize the array itself.
Weaver used _nc_read_entry
and
_nc_fallback
to attempt to force the program to
ignore ncurses' termcap support. I discarded that; termcap
support is not common, and in any case there is no reason why
tack should not be able to use a valid
terminal description from ncurses's database.
Along with this, the program used ncurses's
TERMTYPE
data structure to obtain the filename
from which the terminal description was read. I replaced that
with data from infocmp, which provides
that information in a comment.
Tack used _nc_tic_expand
and
_nc_trans_string
(along with
_nc_reset_input
) to format and decode terminfo
capability strings. In our discussion, these had seemed the
basis of his comment about convenience. Those were
easily replaced by adapting (paring down to just terminfo)
the functions from ncurses.
The calls to _nc_free_termtype
and
_nc_free_tic
would go away away easily once the
other functions were addressed. However, the remaining
functions:
_nc_copy_termtype
,_nc_find_entry
, and_nc_get_hash_table
required some effort. Those were used to allow a user of tack to modify a terminal capability in memory, test the change and write the altered terminal description to a file on exit. You can do this with ncurses, but not with other implementations:
_nc_find_entry
function (used by tic) and the arrays which
ncurses' term.h
defines for the boolean, numeric
and string capabilities. Neither is a defined
interface, but they happen to work.Actually term.h
is defined, but X/Open Curses
does not go into enough detail to ensure that different
implementations are compatible. Quoting from Issue 7,
X/Open Curses says
DESCRIPTION The following data type is defined through typedef: TERMINAL An opaque representation of the capabilities for a single terminal from the terminfo database. The <term.h> header provides a declaration for the following object: cur_term. It represents the current terminal record from the terminfo database that the application has selected by calling set_curterm(). The <term.h> header defines the variable names listed in the Variable column in the table in Section 7.1.3 (on page 342).
The table referred to is too long to quote here. It is the basis for a large part of the terminfo(5) manual page, i.e., Predefined Capabilities. The manner in which the “variable names” are related to cur_term (which happens to point to a TERMINAL object) is left for the implementor. Oddly enough, the Unix implementations that the writers had in mind all did it the same way (either directly based on the AT&T sources, or imitating it). They could have been more specific. But ncurses and NetBSD curses do it in two different ways. Both Unix and NetBSD have (different) problems:
Unix curses and ncurses variable names in
term.h
are generated using a table with the
long-name (the variables), the short-name
(the usual terminfo name) and a description.
The table used in ncurses has additional columns for the termcap names, and additional rows for capabilities observed in Unix implementations which are not mentioned in X/Open Curses.
While ncurses uses arrays for the boolean, numeric and string-values, Unix curses uses structures that cannot be indexed.
For example, Solaris' header file has definitions for SVr2, to which are added SVr4 definitions. Those are more descriptive, but not an array:
#define key_c1 CUR _Vr2_Bstrs._s143 #define key_c3 CUR _Vr2_Bstrs._s144 #define prtr_non CUR _Vr2_Bstrs._s145 #define char_padding CURS strs2._char_padding #define acs_chars CURS strs2._acs_chars #define plab_norm CURS strs2._plab_norm
The same set of definitions in ncurses uses array entries:
#define key_c1 CUR Strings[142] #define key_c3 CUR Strings[143] #define prtr_non CUR Strings[144] #define char_padding CUR Strings[145] #define acs_chars CUR Strings[146] #define plab_norm CUR Strings[147]
The definitions are useful for accessing individual capabilities. A program which manipulates lists of capabilities is far simpler if it can use arrays.
SVr4 curses provided arrays of the capability names which are used internally by the low-level terminal capability functions. While the Unix systems still have these arrays, X/Open Curses does not mention them. To comply with that, HPUX dropped the definitions from the header-files.
Complying to a standard by eliminating features is not very
productive. The feature is supported in ncurses; the array
indexes for the names match the indexes used for the variables
defined in term.h
.
Without arrays, tack cannot edit the terminfo data unless someone builds a large table to relate the structures to the capability names.
The problem with NetBSD is similar: it has arrays of
capabilities, but no arrays of names. Editing the terminfo data
as done in tack with NetBSD is not possible,
since the developer declared all of the terminfo
variables as const
(i.e., symbols rather than variables).
Working within these limitations, I
replaced the _nc_find_entry
and and
_nc_get_hash_table
functions with new functions
using a similar table generated at compile-time using the
Unix and ncurses arrays of names, and
wrote a replacement for _nc_copy_termtype
,
without using any details of ncurses except for the (visible)
TERMTYPE
structure.
NetBSD has no array of names. I constructed a workable table
at runtime using the output of infocmp
. This is
necessarily more limited than that built with Unix curses, since
tack would not be able to determine the datatype
for cancelled capabilities.
For a fully functional tack, you will need the ncurses version, since neither Unix nor NetBSD provide the ability to modify terminal capabilities readily at runtime. Likewise, the test-screens for padding rely on the ability to modify the terminal capabilities.
You should report bugs either to me. or to the ncurses mailing list bug-ncurses@gnu.org.
See the ncurses FAQ