Copyright © 2013-2023,2024 by Thomas E. Dickey

Here is the latest version of this file.

NCURSES – comments on NetBSD curses


In [comp.unix.bsd] NetBSD, FreeBSD, and OpenBSD FAQ (Part 6 of 10), Dave Burgess (or some anonymous contributor) states

5.0     Introduction

5.1     A replacement curses program/library.

        It is generally accepted that the NetBSD curses can be easily
        replaced by the ncurses package.  It is more complete and offers
        much better support for shared libraries and other advanced
        features.  The current (early 1995 version) is 1.8.5 and is
        available from

        OpenBSD comes with a different curses package, called XSI
        curses and uses the termlib library for user code.  The original
        curses code is still available in the <ocurses.h> include file
        and the -locurses library.  The code in the termlib library is
        smart enough to recognize and handle both termcap and termlib
        database parsing.

        (Ed.Note)  The X/Open curses standard is becoming the de facto
        standard (or perhaps imposed standard) for curses under Unix
        systems.  XSI implements this X/Open curses completely, and is
        being used by lots of folks.  FreeBSD and NetBSD will either
        develop their own compatible version or will use XSI like
        OpenBSD does.

That FAQ is a little stale. NetBSD developers provide little information. Here are some notes which would be helpful in updating the FAQ.

Source archives

Here are links to NetBSD source archives which are relevant to this discussion:

Manual pages

Here are links to NetBSD manual pages which are relevant to this discussion:

General discussion

In its current form (early-2017), NetBSD curses is a hybrid of BSD-curses with X/Open curses, along with some extensions found in neither. Some of the latter is due to influence by ncurses.

Development Timeline

This table shows “interesting changes”, that is, relatively large changes which show how NetBSD curses evolved from BSD 4.4 curses to incorporate features from ncurses and X/Open curses. Those which refer to ncurses extensions are more interesting than the features (such as pads, subwindows, etc.) which are provided by other implementations.

NetBSD's curses story begins in 1993, using as its starting point the BSD 4.3 curses software. There was at least one port of the BSD curses to Linux in the early 1990s, e.g., as found in Yggdrasil. In the mid-1990s (when I started working on ncurses), compatibility between BSD and SVr4 curses was important. See for example the comparison on page 112 of the 1995 Linux Programmers' Guide, which cites BSD curses on Slackware:

Interesting library changes
Date            Developer   ChangeLog Committer comments and my notes
1993-07-09 alm addbytes.c
added Andrew Chernov's patch set:
Standard curses library use eight bit for standout mode, so
8-bit characters displays like highlighted 7-bit characters.

This patch produce library which is fully compatible with all curses
programs and add 8-bit chars to all input/display functions.
I don't think, that any programs wish to use internal curses
attribute _STANDOUT directly, in expressions like:
        addch( ch | _STANDOUT );
        Normal interface use standout() and standend() functions instead.
        Many programs use 'char' type (with sign extention) for input characters
        and sign extention becomes _STANDOUT mode in this case.
        So, I refuse this future and allow 8-bit characters for programs,
        which is designed for 7-bit only ('char' type using instead of
        'unsigned char').
This small patch fix unpleasant standard curses bug:
curses can't expand TAB at all (but tries).
A man who wrote this curses misplace SYNC_IN and SYNCH_OUT,
this patch exchange macro calls.

This patch useful for standard 7-bit curses too, for this
you must delete '_' symbol before waddbytes and apply patch.

This starting point diverged from BSD 4.3 curses by changing the way video highlights were represented. BSD 4.3 curses used the eighth bit of a character for denoting standout (usually represented by reverse video).

Incidentally, "waddbytes" was not a feature of BSD 4.2 curses. It was added after 1985 (e.g., in 4.3 Reno) to do the work for waddch. It was not in SunOS (which is nominally BSD 4.3) or X/Open curses. This interface is still present (and undocumented) in NetBSD curses as of 2017.

1997-05-24 Julian Coleman curses.h
Add get{beg,max}yx and get{cur,beg,max}[yx] macros like XPG4.2 curses.

getmaxx, etc., are extensions—perhaps influenced by ncurses, but using text from one of the SVr4 platforms—added to NetBSD at this point, but not cited as extensions in NetBSD 2.0. The documentation was corrected in 2004, to note that they were extensions:

Note that getbegx, getbegy, getmaxx, and getmaxy are extensions
to X/Open Curses. From Peter Bex in PR 26352.
1999-04-13 Julian Coleman
Brett Lymn
Upgrades the standard NetBSD curses library to provide some
of the SYSV curses facilities.  The added features are the collapsing
of arrow and function keysequences (as defined by termcap for the
terminal) into symbolic code returns thus relieving the application of
recognising multi-character key sequences.  Other features are the
capability to perform a timed wait for a key (good for when you are
not sure if there is a keypress ready or not) and the capability for
turning off the inter-key timeout when assembling multi-character
function keys.

Oddly, the same commit added underscore.c, whose functions (see curses_underscore manual page) are not SYSV. These come from some other source (perhaps NetBSD extensions), but are not documented as such.

1999-04-13 Julian Coleman
Brett Lymn
Upgrades the standard NetBSD curses library to provide some
of the SYSV curses facilities.  The added features are the collapsing
of arrow and function keysequences (as defined by termcap for the
terminal) into symbolic code returns thus relieving the application of
recognising multi-character key sequences.  Other features are the
capability to perform a timed wait for a key (good for when you are
not sure if there is a keypress ready or not) and the capability for
turning off the inter-key timeout when assembling multi-character
function keys.

this work was done by Julian Coleman <>
and (Brett Lymn).  i'm just integrating it.  thanks
HEAPS guys!

I noticed this change while responding to a question on the bug-ncurses mailing list in August 2020. The mention of "SYSV curses facilities is incorrect. The NetBSD developers copied from ncurses (translating from terminfo to termcap) At the time this change was made (April 1999), SVr4 source was unavailable for comparison, but is now, e.g., illumos-gate. That does not use scrolling regions in a refresh while ncurses did.

2000-04-11 Brett Lymn curses.h
Made data structures opaque

This is a big difference between NetBSD curses and other implementations. Without adding new functions to the interface, it is not possible to directly manipulate the contents of a WINDOW struct. I implemented something like this (NCURSES_OPAQUE) in 2007. But I also added functions to compensate.

2000-04-12 Julian Coleman color.c
Colour specific routines.
2000-04-13 Brett Lymn scanw.c Committed by Julian Coleman:
Restore const after the Open Group decided it was OK.
Thanks due to Brett Lymn.

We discussed this, but since the interaction of Open Group was via private email, some of my comments are second-hand. In ncurses (see changes in April 1997), I chose to keep this as a build-time configuration option, since compilers can (and do) refuse to accept mismatched references to a const char * value –TD

2000-04-15 Brett Lymn scanw.c
Added functions to replace what were previously macros in curses.h
(this is a requirement of SUSv2) - the old macro behaviour can be
restored by defining _CURSES_USE_MACROS.
Changed function prototypes to use ANSI style.
All externally visible functions now have ANSI style declarations.

Like ncurses, ensure that except for special cases such as getyx, the library provided functions rather than relying upon macros to implement the variations of mv- and w-prefixes.

2000-04-19 Brett Lymn setterm.c
Converted all termcap library calls to the "new" interface, this fixes
a problem with curses crashing when the CM capability was larger than
64 bytes and eliminates some possible buffer overflow problems.
2000-04-21 Julian Coleman refresh.c
Work round xterm bug which displays the wrong background colour when the
screen has just been scrolled.
Only use CL if all lines have the same background colour.
Fix debug output in quickch().

Not a bug in xterm, but the developer's not taking into account the nuances of the back color erase feature of terminfo (and termcap). One may see some of the progress in the revision history of erase.c, e.g., PR 32617.

2000-05-11 Julian Coleman attributes.c getattrs and related functions are features of legacy curses (including PDCurses), not in X/Open.
2000-08-13 itojun addnstr.c
change behavior of waddnstr(w, s, 0) to be more conformant to XCURSES.
(see comment for detail)

The comment shows

         * behavior changed from traditional BSD curses, for better XCURSES
         * conformance.
         * BSD curses: if (n > 0) then "at most n", else "len = strlen(s)"
         * ncurses: if (n >= 0) then "at most n", else "len = strlen(s)"
         * XCURSES: if (n != -1) then "at most n", else "len = strlen(s)"

but the code follows ncurses rather than "XCURSES".

2000-12-31 Julian Coleman curses.h
Removal of termcap capabilities requires major bump.  Pointed out by Itojun.
Capabilities are still available if `_CURSES_TERMCAP_COMPAT' is defined.

Prior to this change, each termcap capability fetched from the database was assigned its own global symbol. Effective with this change, those variables were still globally accessible, but renamed to use a “__tc_” prefix as a strong hint that they were no longer part of the API. A year later (in revision 1.64), the compatibility feature (and renamed symbols) were removed from the header file.

2001-09-20 Brett Lymn resize.c
Add code to allow resizing of windows and the underlying terminal.

I wrote resizeterm and and wresize as early extensions to ncurses. NetBSD documents the corresponding functions in the curses_screen and curses_window manual pages –TD

2001-10-14 Brett Lymn getyx.c Like getattrs, getparx, etc., are legacy features not in X/Open.
2001-12-09 Brett Lymn setterm.c
* Major change to add support for the newterm/set_term functions.
* Added fix to getch.c suggested by Gabriel Rosenkoetter (thanks :-)
2002-08-04 Julian Coleman color.c
Set the curses default colours to white on black when using colour.
See the Single UNIX Specification, Version 2 :

Also, add the functions :

        assume_default_colors(fore, back);

(from ncurses) that allow the terminal default colours or user-specified
default colours to be used.

I wrote these ncurses functions in 1997 and 1999 respectively –TD

2002-09-11 Julian Coleman setterm.c
Add a hack for xterm-like terminals where "\E[m" will turn off
other attributes.

The issue is more than "xterm-like terminals". The NetBSD change is most closely related in ncurses to the vidattr function.

But there is an additional issue for termcap. As Michael Schroeder explained to me one of the problems in running screen (a termcap application) with terminfo is that the terminfo sgr0 assumes that it will turn off line-drawing, while termcap's "equivalent" me does not. Starting in September 2001, I made improvements to work around this in ncurses' termcap interface:

        + modify tgetent() to check if exit_attribute_mode resets the alternate
          character set, and if so, attempt to adjust the copy of the termcap
          "me" string which it will return to eliminate that part.  In
          particular, 'screen' would lose track of line-drawing characters
          (report by Frederic L W Meunier <>, analysis by
          Michael Schroeder).
2002-10-21 Brett Lymn curses_termcap.3
Added separate man pages for curses functions to provide better
documentation of the curses functions.

This manual page is odd for three reasons:

  1. it is entitled “curses termcap querying routines”, but none of the conventional termcap routines are described
  2. the one function which is described (fullname) is NetBSD-specific.
  3. the note at the end states that this implementation complies with X/Open.

The actual termcap functions are documented in a different termcap manual page.

2002-10-22 Brett Lymn getch.c
Added the ncurses extensions define_key and keyok.

These are documented in the curses_input manual page.

2003-01-27 Julian Coleman color.c
rename can_change_colors() to can_change_color().
Add can_change_colors() (that just calls can_change_color()).
Add no_color_video().
Replace __nca with _cursesi_screen->nca.

The initial implementation of color misspelled can_change_color, which caused some confusion as shown in an angband newsgroup. This was NetBSD PR #20036.

2003-04-08 Julian Coleman getch.c
Add ESCDELAY variable to control the inter-key delay in escape sequences.
Fixes PR 20031 by Thomas Klausner.
This is an ncurses extension, added in January 1996 just before lib_resize.c, cited as an “undocumented” feature of AIX curses. It is not present in AIX 5.3 curses or later. There are a few references such as this and this which date it to AIX 3.2, in the early 1990s. ncurses' manual page does not clarify this. NetBSD's manual page documents this feature, but does not associate it with either ncurses or AIX.
2004-03-22 Julian Coleman curses.h
Because we are changing the libcurses major number, remove can_change_colors()
and change the attribute definitions so that we can support more colour pairs.

Revision 1.84 increased the number of bits for A_COLOR from 6 to 8 (making it like ncurses).

2004-03-22 Julian Coleman curses.h
Change the values of ERR and OK.
This makes our getch() and related functions conform to SUSv2.
Also fixes PR lib/15920.

XXX: This causes an API incompatability, but comes less than 27 hours after
     the libcurses major number was incremented, so should have minimal impact.

Revision 1.85 corrected the value for ERR, which had been 0 (zero) rather than -1 (negative one). The problem report references ncurses.

2004-03-22 Julian Coleman curses_input.3
Add KEY_RESIZE support and a SIGWINCH handler.
Fixes PR bin/20032.
This requires a change to KEY_MAX, which affects libform and libmenu, so we
need to change libcurses major number.

This was done to provide a feature comparable to ncurses. In ncurses, KEY_RESIZE was added in 1997. However, NetBSD differs from ncurses by (notwithstanding the comments in the bug report) installing its own SIGWINCH handler whether or not the application had already installed one.

2007-01-21 Julian Coleman ctrace.c
Add debug "areas" that allow selective debugging by setting the
"CURSES_TRACE_MASK" environment variable.  Postive vales include
debug areas, negative values exclude them.

The ctrace.c module is old, but its purpose has changed. The commits in 2007 made it more like ncurses. CURSES_TRACE_MASK is equivalent to ncurses' NCURSES_TRACE, introduced in December 1995. Unlike ncurses, NetBSD checks the trace mask inside the tracing function. This means that it will run more slowly than the equivalent code in ncurses, because it must evaluate parameters and call the tracing function before deciding whether to actually do the trace.

2007-05-28 Ruibiao Qiu addbytes.c
Committed by Brett Lymn:
Merge in wide curses code done as a Summer of Code project by
Ruibiao Qiu.

In my earlier discussion with Qiu, he indicated that his implementation would differ from ncurses in at least one way: rather than storing in each cell an array of base- and combining-characters, he would store the combining characters (seen as non-spacing in curses_private.h) as a linked list.

He may have been referring to a discussion in June 2005 on the tech-userlevel mailing list, where Julian Coleman suggested that the non-spacing characters could be stored as a “list/pointer”

In order to support these functions, the curses internal storage of
characters and attributes needs to be modified.  For example, each
character position might be described by a structure containg:

        character value (32 bits)
        character attributes (32 bits)
        character width
        non-spacing character list/pointer

As of February 2017, this interface is not documented aside from the source code.
I know of no programs which use it.

2007-05-29 Brett Lymn ctrace.c
Change debug handling, now we write debug out iff CURSES_TRACE_FILE
has been set in the environment, this prevents people using MKDEBUGLIB
getting more than they bargained for.

Tidied up the debug settings in the Makefile to reflect the above change,
we no longer need to have FULL_DEBUG since nothing is written by default.

This change makes NetBSD a little more different from ncurses, by requiring a second environment variable to be set: the name of the file to use for tracing. Before, it used “__curses.out” in contrast to “trace” used by ncurses.

The tracing feature was documented later, in 2008, by Christos Zoulas:

Document curses environment variables.
2007-07-11 Julian Coleman curses.h
Add additional line drawing and characters.
Pointed out by pooka@.

Revision 1.90 added some pseudographic extensions from ncurses. NetBSD documents these in the curses_addch manual page.

2007-12-08 Julian Coleman curses_private.h
Keep pushed-back characters locally.  Fixes problems where KEY_* symbols
are pushed back.  Should fix the arrow keys part of PR pkg/37173.

While we are here, make getch() and get_wch() check for resize immediately,
instead of reading a key, checking for resize and then having to push-back
the just read key.

The bug report deals with breakage of the tin port due to introducing NetBSD curses as the default.

In modifying tin to use curses, I had used one of ncurses extensions (see Valeriy Ushakov's comment in the bug report):

The ungetch routine places ch back onto the input queue to be returned by the next call to wgetch. There is just one input queue for all windows.

X/Open curses's ungetch expects a single byte rather than accepting any value returned by getch. NetBSD's description of the ungetch function is ambiguous because it attaches no meaning to the parameter.

2008-04-14 Julian Coleman fileio.c
File IO functions getwin() and putwin().
Should fix PR lib/24928.

These are X/Open functions, which were added to build a port of ettercap using NetBSD curses rather than ncurses. Earlier (in 2004), echochar.c was added for a related problem report (PR 24927) The 4-year delay indicates that it was working well enough with ncurses, but that the developers preferred building with their own implementation.

2009-07-06 joerg curses.h
Add support for the chgat(3) family.  It is a useful extension from
ncurses, supporting it dramatically reduces the need for ncurses in

Joerg's comment for revision 1.95 was mistaken—those are documented by X/Open.

2010-02-03 Roy Marples term.h
Import my terminfo implementation.
This uses the ncurses terminal definitions.

Like the earlier termcap functions, the NetBSD terminfo functions include extensions: functions (prefixed with “ti_”) which use an additional parameter to help provide a thread-safe API.

The “ncurses terminal definitions” refers to the manner (from SVr4 curses) in which terminfo long-names are #define'd to point to structure members in term.h.

There is some ncurses-related code in the header file however:

/* Default to X/Open tparm, but allow it to be variadic also */
#  define tparm vtparm
#  define t_parm t_vtparm

The TPARM_VARARGS symbol (first mentioned in mid-2009) was likely prompted by reading NCURSES_TPARM_VARARGS (late 2006). Later, I had the opportunity to provide suggestions for X/Open Curses Issue 7, including the tiparm function. I added that to ncurses early in 2009. It was also implemented by NetBSD (late 2011). However, the declaration in NetBSD's header file is a bit askew:

/* Using tparm can be kunkly, so provide a variadic function */
/* This is not standard, but ncurses also provides this */
char *          tiparm(const char *, ...);
/* And a thread safe version */
char *          ti_tiparm(TERMINAL *, const char *, ...);

At the time Marples wrote that comment, it had been “standard” for about three years. The comment about thread-safe, as well, is inaccurate. For instance, its accesses of files are not thread-safe, because the database-reader (cdbr) does not lock its content against concurrent writes to the database. Usually that works.

2010-02-03 Roy Marples tparm.c It took a few months after the mailing list discussion before Marples' implementation of the terminfo library was added to NetBSD.
Import my terminfo implementation.
This uses the ncurses terminal definitions.

OK: core@, jdc@

The comment about “ncurses terminal definitions” refers to the data structures defined in <term.h>, but there is some additional borrowing. For instance, the tgoto and tparm functions analyze the format to determine which parameters might be strings (and require special handling). See the NetBSD code, and the original in ncurses, for comparison.

Later, Marples pointed out a related improvement for ncurses (see 20130126); the documentation in NetBSD regarding these functions is still sketchy, not mentioning this issue.

In August 2021, I noticed something about this which bears comment. In addressing a bug-report, I noticed that I had left a runtime check for stack overflow or underflow in tparm only useful in a debugging environment. To remedy that, I added another set of checks in tic to look for that problem. The checks worked, but the logs from running tic on the whole database showed an unexpected difference.

Regarding ncurses, the problem was that ncurses stored both dynamic and static terminfo variables (the data used in %P and %g) in persistent memory. Moving the dynamic variables into local (nonpersistent) storage in tparm did not solve the problem. To actually solve it, I added an internal function to reset the static variables before each call to tparm.

To see why this was necessary, I referred to the Illumos source-code. I examined the SVr4 library source, and the Solaris XPG4 source code, so that the static/dynamic variables could be properly documented. Then, as an afterthought, I took a look at NetBSD's source to put it into the proper context.

The interesting detail that I found for NetBSD is that it copied a bug from the SVr4 implementation (a flaw in the initialization of dynamic variables). Also (only obvious if one reads the <term.h> header file in each implementation), the static variables in SVr4 terminfo are stored in the runtime copy of the terminal description.

Since these details were present in the first snapshot of the source code which Marples provided in July 2009, it is apparent that in addition to using the ncurses source-code as a basis, he had studied the Solaris source-code while writing the NetBSD terminfo library, and used features from that in development.

2010-02-03 Roy Marples getch.c
Userland now builds and uses terminfo instead of termcap.
2010-02-11 Roy Marples tic.c
tic -S now outputs the specified terminal names and compiled descriptions
as C strings so we can embed them into libterminfo.

This is less complicated than the two-part process used in ncurses to provide fallback terminal descriptions, but the same idea. For comparison, see the “-e” and “-E” options of infocmp, introduced in 1996 and 1999 respectively. In particular, the NetBSD generated code provides no structural information about the terminal description.

2010-02-11 Roy Marples
Respect TERMINFO_DIRS and document using an embedded database.

These are features introduced by ncurses in 1996 and before (1.9.6 was released in October 1995). Likewise, I commented on the $HOME/.terminfo (mis)feature in the first version of the ncurses FAQ in January 1997.

2010-02-23 drocher acs.c
misc fixes and improvements:
-call setlocale(LC_CTYPE, "") before nl_langinfo(CODESET) if the
 locale settings is (still) at "C" - otherwise the CODESET doesn't work
-fix the type of the WACS_* symbols -- this needs to be cchar_t*
-add safeguards where the return value of wcwidth() is used for
 loop counters or indexing -- it can be -1
-use more common code in the widechar support case -- in particular
 let the wchar functions do the work even if chtype ones were called
-implement wcursyncup/wsyncup/wsyncdown
-somewhat experimental: allow ACS_* variables to refer to WACS_*
 table entries -- this way, programs using the old chtype using API
 can use UTF8 line drawing on terminals which support UTF8 but not
 ACS switching
-fix some logics bugs in UTF8 recognition and ALTCHARSET handling

Other curses implementations (such as ncurses) use the locale setup by the application, which is not necessarily that seen by the environment variables.

NetBSD with this change modifies the _wacs_char table at runtime. In contrast, ncurses (since 2002) detects the locale encoding and selects from the narrow or wide table. For both, the ACS_* or WACS_* symbol is taken as a hint rather than explicitly requiring narrow- or wide-characters.

2010-09-22 Roy Marples termcap_map.c The comment in the termcap_map.c file saying "NetBSD extensions" is incorrect. This commit (a year later) by Julian Coleman termcap.5 says
Add extra KEY_* definitions (from SUSv2).

which agrees with Solaris' terminfo(4) manual page (last updated July 1996). They were added to ncurses in April 1995, noted as not used in termcap as such. I pointed out in September 2009 (a year earlier than the commit to termcap_map.c) that they were documented in Solaris.

2011-10-04 Roy Marples tput.c
Ensure we only pass cap names of one or two characters to tgetstr
and friends so we don't mistakenly try and convert a terminfo code to a
termcap code.

There is no problem-report for this. Marple's rewrite of tput is intended to handle (like ncurses) both terminfo and termcap names. Incidentally, the examples in the corresponding tput manpage still use termcap names.

2013-10-16 Roy Marples setterm.c
Add TABSIZE, which is derived from terminfo init_tabs.
Use this when processing \t.
If TABSIZE is set in the environment, this takes precedence.

This is specific to NetBSD. Other implementations (including ncurses) do not honor a TABSIZE environment variable.

2014-07-13 Brett Lymn addnstr.c
Remove bogus length check - SUSV2 says add(n)str wraps and performs
special character processing so we should not be trying to limit the
length to the screen edge.  This partially fixes PR 48827, the test case
works now.

The bug report is "libcurses issue with embedded newlines", which deals with a documented feature of X/Open curses, e.g., in the addch manpage. The NetBSD manpages do not mention this.

As of October 2014, Brett Lymn appeared to be the long-term NetBSD curses library maintainer:

In early 2017, Roy Marples was making formatting and documentation fixes while continuing to adapt features from ncurses into the NetBSD curses library.

Since then, there have been sporadic updates/fixes (but keep in mind that some of is based on a faulty premise: see ncurses FAQ).

NetBSD developers discuss ncurses on mailing lists, either directly or obliquely. Not much of that is useful:

For instance, in the thread curses: create panel from stdscr? a developer stated that there was something that ncurses did contrary to the SVr4 documentation. But contacting the developer for information was unfruitful, since he had only a vague recollection of a problem without any information. Notwithstanding that, he posted a lengthy rant on the topic, which was echoed by two others (this and that) to the same effect: none.

While another developer provided a useful report regarding a difference of SVr4 curses from both ncurses and NetBSD curses (see ncurses NEWS 20181208 as well as NetBSD source), that has been less often the case.


Marples began work in mid-2009 to introduce terminfo to NetBSD, as discussed in these threads on the tech-userlevel mailing list:

Features adapted from ncurses

Marples' implemention includes the tic-x” option from ncurses (early 1999). At the time (mid-2009), I pointed out that infocmp did not provide the ability to compare the extended capabilities. Marples' followup agreed. In the current release, it does this comparison.

The documentation, however, is limited. NetBSD's terminfo manpage states

Extensions to the standard are noted in tic(1).

while the tic manpage manpage says only

Include non standard capabilities defined in the source.

Compare with the description of “-x” in ncurses' tic manpage, as well as the discussion of file formats.

Though it is the same idea, NetBSD's implementation of the extended capabilities using “-x” differs from ncurses. In ncurses:

In contrast, regarding NetBSD curses (actually libterminfo):

These mention releases here, along with dates:

Features not adapted from ncurses

Writing in May 2019, none of the improvements from ncurses 6.1 had been adapted in the NetBSD terminfo library, as seen in its source history. For instance:

Global variables versus function parameters

On the other hand, NetBSD does provide a feature not directly comparable with ncurses's extensions. That is the set of ti_xxx functions mentioned in the terminfo vs termcap thread. These functions use a pointer to a TERMINAL structure, rather than relying upon the global variable cur_term (documented in X/Open Curses, but absent from NetBSD).

Those were adapted from an earlier alternate interface to termcap introduced by Brett Lymn in 1999. However, the extension is rarely used. Checking in May 2019, it has been used outside the curses and terminfo libraries in only one place in the NetBSD source-tree. That was by a change four years later, which accounted for 8 lines versus 514 lines using the conventional terminfo interface. Aside from that, the NetBSD curses library is the main user of the feature:

lib/libcurses/tty.c:553:        ti_puts(screen->term, t_enter_ca_mode(screen->term), 0,
lib/libcurses/tty.c:555:        ti_puts(screen->term, t_cursor_normal(screen->term), 0,
lib/libcurses/tty.c:558:                ti_puts(screen->term, t_keypad_xmit(screen->term), 0,
lib/libcurses/slk.c:389:                return ti_putp(screen->term,
lib/libcurses/slk.c:453:                return ti_putp(screen->term,
lib/libcurses/slk.c:832:                return ti_putp(screen->term,
lib/libcurses/acs.c:119:                ti_puts(screen->term, t_ena_acs(screen->term), 0,
lib/libcurses/acs.c:279:                ti_puts(screen->term, t_ena_acs(screen->term), 0,
lib/libterminfo/term.h:1973:int         ti_puts(const TERMINAL *, const char *, int,
lib/libterminfo/term.h:1975:int         ti_putp(const TERMINAL *, const char *);
lib/libterminfo/tputs.c:96:_ti_puts(int dodelay, short os, char pc,
lib/libterminfo/tputs.c:131:ti_puts(const TERMINAL *term, const char *str, int affcnt,
lib/libterminfo/tputs.c:151:    return _ti_puts(dodelay, term->_ospeed, pc,
lib/libterminfo/tputs.c:156:ti_putp(const TERMINAL *term, const char *str)
lib/libterminfo/tputs.c:160:    return ti_puts(term, str, 1,
lib/libterminfo/tputs.c:169:    return _ti_puts(1, ospeed, PC, str, affcnt,

For more context, here is one from slk.c:

        if (screen->is_term_slk) {
                if (t_label_off(screen->term) == NULL)
                        return ERR;
                return ti_putp(screen->term,
                    ti_tiparm(screen->term, t_label_off(screen->term)));

Even in the one file where it is used, the curses library does not rely upon the feature: it does not attempt to manage multiple terminals concurrently. Rather, the parameter is a member of the library's _cursesi_screen variable.

Wide characters

Most (not all!) of the X/Open curses functions have been implemented in NetBSD curses. The largest part of those beyond BSD curses consists of the wide-character support added in 2007.

Here is some discussion by others:

On the other hand, while the library supports wide-characters, they are not documented. All that a developer has to work with is the header file:

To check for completeness, I compared the library public symbols. Just comparing the exported symbols, I see 486 total symbols between the two libraries.

exist in both libraries
exist only in NetBSD
exist only in ncurses.

Focusing on the 89, it turns out that none are wide-character functions. There are missing functions, however:


NetBSD 6.1.3 (2014)

In one of Marples messages, he stated that the resulting tic application was “3 times quicker” than ncurses. I pointed out that this may have been due to using a single hashed database (an option in ncurses since 2006), or some other aspect. There was no followup report.

There is another aspect of hashing which ncurses uses: the lookup of names from a terminal description in memory. The NetBSD implementation uses an analogous approach starting with this discussion of hashing. I noticed Marples (early-2010) and Sonnenberger (early-2010) saying that NetBSD's use of perfect hashing greatly improved performance, but Marples (a) gave no numbers and (b) did not say how he measured it. I said "analogous", because the hashing algorithm is different, and of course the code internals differ. Still, they should be comparable at the application level.

If everything else is equal, I would expect the two implementations to differ in the way they used hashing. I wanted to know more, in case there was some improvement which I should use in ncurses.

The NetBSD code itself is written in a non-portable manner, relying on system-specific headers and datatypes. Thus, any analysis of its performance will tie it to whatever tools are readily available on NetBSD.

I could have written a special-purpose benchmark such as tctest. However, in preparing this page, I chose instead to extend ncurses' test-programs to make it simple to perform side-by-side measurements of the time needed to retrieve terminal capabilities using the respective libraries. Specifically, I modified

In the comparison, both programs use function calls, checking their return-values, to discover the existence and types of each capability. The programs lookup the terminal description itself once.

Those tests are against all possible calls. Normal application programs would make calls referring to predefined names (or would refer to the terminfo data structure directly—but that is less interesting). Using the “-i” option here is a comparison using the predefined (standard) names from the xterm description:

My tests show that in the case where the application is using the predefined terminfo names, it will have comparable performance between ncurses and NetBSD curses. However, they are too close to see a real difference. There is some problem in the latter's termcap interface which warrants further investigation.

Both ncurses and NetBSD curses support extended (user-defined) capabilities. Here is a comparison of lookup times for the extended capabilities from the xterm terminal description. This time, only terminfo is compared because almost all of those capabilities have names longer than 2 characters (making them not usable via termcap).

This comparison shows that the lookup of extended capabilities in ncurses was less efficient than in NetBSD curses. The “-x” option shows another difference: ncurses provides a call use_extended_names which controls whether the application can see the extended capabilities. NetBSD curses does not provide this function; extended capabilities are always visible.

There are of course other things which can be measured for performance. For instance, the dots (terminfo), dots_termcap and dots_curses test-programs in ncurses show an estimated throughput in cells/second. Here are some numbers (with the usual caveat about precision versus repeatability):

program ncurses NetBSD
dots (terminfo) 4433 4249
dots_curses 3930 4051
dots_termcap 4479 4457

Testing shows that the two implementations have comparable performance. In both, the curses raw performance is lower than for the terminfo/termcap interfaces. This is because the curses interface (while it does optimization to improve performance) retains for reference a picture of what is on the screen, e.g., to support functions such as winnstr. Comparing the working and actual screens also costs something.

NetBSD 8 and 9 (2020)

The above was for NetBSD 6.1.3; subsequent data change the story somewhat. With NetBSD 8 (released mid-2018), the two demo programs ran about half as fast as ncurses 6.2:

However, with NetBSD 9 (released early 2020), they ran about ten times slower than with ncurses.

Those are test-programs, for low-level operations; higher-level programs can also be affected.

NetBSD developers have commented on other performance problems, e.g., OpenSSL, and have discussed how to adapt the extended numeric capabilities introduced by ncurses 6.1 two years before, but no mention was made of this problem on the mailing lists. It was immediately noticeable when I compiled and ran my directory editor using NetBSD curses: the cursor moved along the screen so slowly that I could watch it moving, line-by-line.

Here are the results from several runs replaying a script that starts ded and displays a few screens of data. The first rows in each set used the time built-in for tcsh; the latter rows used /usr/bin/time:


NetBSD 9.2, released about a year later, made no improvement. I ran these tests with ncurses 6.3 after installing NetBSD 9.2 to work on pkgsrc changes for vile:


NetBSD 10.0, released in March 2024, is still slower. I ran these tests with ncurses 6.5 on May 7:


As I noted, portability of the NetBSD curses and terminfo is awkward. However, if one merely ports the whole ensemble of surrounding libraries, these parts go along readily. That was done with Minix 3.2 and Haiku (both apparently lacking applications outside the kernel):

The initial port to Minix 3.2 was not complete (for instance, the "Posix" shell did not implement the test function completely). While writing this in October 2014, I noticed that the Minix developers were proceeding to revamp the initial port, as described here.

Beginning early in 2016, someone began changes to a copy of this software to alleviate some of the portability problems (seen here). As of February 2017, it is not nearly complete, although it has gotten some attention from developers of NetBSD curses, e.g., in a message from Roy Marples at the beginning of January 2017:

I'm in discussions with one of their devs about pushing some of their patches into NetBSD so it's easier for them to sync our changes back down.

As of February 2017, it does not build without change on my Debian system, due to some leftover debris (an unnecessary symbol conflict for __unused). After repairing that, it is more interesting.

Keeping in mind the __unused, that would be useful with compiler warnings, which I turn on routinely in development. Using gcc-normal, netbsd-curses produces 968 warnings, while ncurses produces 2. According to c_count, as of February 2017 (counting the utilities, libraries and headers), ncurses is roughly twice the size of NetBSD curses, making ncurses' warnings per line about one thousandth:

What Lines Statements
NetBSD curses 45693 12987
ncurses (compare) 100359 24566
ncurses-examples 35487 14400

The file bears some comment. Its anonymous author has problems getting numbers right. Here's an easy one: there are more than three implementations of curses. None of the other numbers cited are plausible, though usually they are within a factor of two. Sometimes the example given is misleading.

For example, in my initial view, that figure for 128Mb catches the eye, but what was actually measured is not readily apparent. The relevant part is this:

netbsd curses was installed without manpages (ncurses: 1.1 MB) and terminfo database
(ncurses: 6.4MB). the debug info build was created with -g3 and debuginfo stripped into
external files via objcopy.

The ncurses configure script defaults to building the test-programs, the C++ and Ada bindings. NetBSD curses has no counterparts for those. An appropriate comparison for sizes would use

--without-ada --without-tests --without-cxx-binding

Overriding CFLAGS to -g3 and compiling, a static archive holds essentially the same information (as overhead) for each of the 258 object modules in the build-tree. That adds up — 84Mb — but nowhere close to 128Mb. To get the numbers shown, the test-programs have to have been in the cited comparison. Measuring NetBSD curses the same way gives 23Mb — higher than reported. Most of the debug info comes from the system library; increasing the number of object modules has a greater effect than the macros in ncurses. Because ncurses has more than twice as many object files (counting test programs), the comparison is heavily weighted in favor of NetBSD curses. The developer used that comparison to justify the term “bloat”, which is (with the misleading numbers) still present in 2024.

Bear in mind that this is an unusual case. It has been common practice to link with shared debug libraries (not static) for more than a decade (since the early 2000s). The numbers change a lot when you use realistic assumptions.

Some of the readme deals with subjective matters such as code readability and “bloat”. Criticism should be quantifiable to be useful. Compiler warnings for instance, tell me something about the quality of the code and the competance of the developers involved with it. Library size tells me something else:

Although NetBSD curses has had support for wide-characters since 2007, it does not work properly. This can be demonstrated with the ncurses view test-program. Solaris 10 (xpg4) and ncurses produce identical screenshots. NetBSD curses is different:

sample UTF-8 file with tabs

ncurses – sample data, without curses

Solaris 10 (xpg4) curses

ncurses – sample data, using Solaris curses

NetBSD curses on NetBSD 7

ncurses – sample data, using NetBSD-curses on NetBSD 7

NetBSD curses on Debian 7

ncurses – sample data, using Netbsd-curses on Debian 7

In addition to its problems displaying wide-characters, NetBSD curses makes fewer error-checks than ncurses. A quick count from code as of February 2017 shows

Unlike a Java application where programmers may simply raise an exception (and presumably let some other part of the program handle problems), C libraries rely upon passing error codes back to the caller, who must check the return status. Error-checking in NetBSD curses is seen as a problem by its developers, particularly when imitating features of ncurses.

Roy Marples wrote to at the end of January, 2017 regarding one of the features copied from ncurses:

On 30/01/2017 04:22, coypu@SDF.ORG wrote:
> I've applied a patch to audio/moc. It works on my end now.
> It was attempting set_escdelay (25); very early, before setting
> up a window.
> On nbcurses it was:_cursesi_screen->ESCDELAY = escdelay;
> which was a null deref.
> for ncurses, it was returning an error, but it was ignoring it.

We have a lot of similar functions which operate without checking the
terminal has been setup or not.
It *IS* a programming error, so it's probably best to use an assert
rather than simply returning ERR.

Anyone have any stronger opinions on this before I add asserts liberally
to our curses?

Adding asserts guarantees that the program will dump core if it detects a problem (rather than returning an error status, as ncurses would).

Toward the end of the readme, it mentions a couple of cases where some application needed “light patching” to work with NetBSD curses.

Patching widely portable applications to make them work with a library having limited portability is not productive.


NetBSD curses has less documentation than ncurses. Just for raw numbers:

Documentation (netbsd)
man1: 5 files, 25563 bytes
man3: 87 files, 357431 bytes
Documentation (ncurses)
man1: 10 files, 104090 bytes
man3: 119 files, 714121 bytes
man5-7: 3 files, 118242 bytes

But in both cases, almost all files have a 28-line copyright notice. For ncurses, I put a box around it, adding to the byte count (making that 1681 bytes versus 2133). Revising the table:

Documentation (netbsd), no boilerplate
man1: 5 files, 17158 bytes
man3: 87 files, 211184 bytes
Documentation (ncurses), no boilerplate
man1: 10 files, 82760 bytes
man3: 119 files, 460294 bytes
man5-7: 3 files, 111843 bytes

Either way, ncurses has more documentation. At one point in our discussions, Roy Marples asked if he could just copy ncurses' documentation. I explained to him that it was not identical to the X/Open documentation and was copyrighted by the FSF.

While NetBSD's adaptation of ncurses features is generally noted in the library documentation (ncurses is found 110 times), there is no mention of this in the utility documentation. For instance, these features in NetBSD's tic are adapted from ncurses:

Likewise, the -x option in infocmp is adapted from ncurses.

Some of the changes which have incorporated ncurses extensions are not reflected in the NetBSD manual pages. These functions were overlooked as of May 2017, but partly resolved in 2022 (i.e., not mentioning their origin in ncurses):

The NetBSD tic manual page mentions one extension, but that appears to refer to the normal behavior of the use= capability versus @ cancels. However, to verify this I wrote scripts which use the infocmp program (for NetBSD, ncurses or Solaris) to dump all of the entries in a form which can be directly compared.

The NetBSD utilities manual pages omit any discussion of the TERMINFO and TERMINFO_DIRS environment variables. The latter, again, is adapted from ncurses. However, it does not behave as documented in the ncurses manual.

Besides omitting information, some of the documentation is simply incorrect. For instance, at the bottom of the tset/reset manual page it is asserted

The reset and tset commands appeared in 1bsd.

and the change comment says

Change or add history. I checked the 1bsd archives and this first appeared in 1BSD (before it was known as 1BSD). I looked at code and later sccs history to confirm. In the case of rewritten code, we are not consistent in manpages indicating that history.

But in fact, there is no SCCS archive which goes back that far (CSRG archives began a few years later), and the 1bsd archives (sic) have neither tset (whose comment header indicates it would be that old), nor reset (both of which appeared first in 2bsd). And as noted here, that reset program was discarded, in favor of changes to tset—in 4BSD (1982).

Without documentation, there is no point in making bug reports.

Problem reports