Copyright © 2021 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) stated in 1997

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 more than a little misleading:

Because ncurses is part of the base system, OpenBSD developers chose to not provide a port which could be updated frequently:

Here are some notes which would be helpful in updating the FAQ.

General discussion

OpenBSD curses (and the utilities) as of mid-2021 is ncurses 5.7 (2008), with some fixes from later versions of ncurses, as well as customization by OpenBSD developers. Not all of ncurses 5.7 is in the OpenBSD source tree. These subdirectories of ncurses are missing:

Ada95, c++, doc, test

Additionally, the makefiles are not generated by autoconf, and some of the source-files which would be generated during the build are stored directly in the OpenBSD source-tree. I wrote a simple script (200 lines of Perl) to transform the source-files to simplify comparison with the original files:

Here is a diffstat showing the amount of change for the 140 files:

 form/frm_driver.c             |    5 -
 form/fty_int.c                |    2 
 form/fty_num.c                |    2 
 include/curses.h              |   28 +++--
 include/nc_tparm.h            |    1 
 include/ncurses_cfg.h         |  203 ++++++++++++++++++++----------------------
 include/term.h                |   17 ++-
 include/term_entry.h          |    6 +
 include/termcap.h             |   17 ++-
 man/captoinfo.1m              |   23 ++--
 man/curs_add_wch.3x           |    2 
 man/curs_addch.3x             |    4 
 man/curs_extend.3x            |    2 
 man/curs_getch.3x             |    8 -
 man/curs_opaque.3x            |    6 -
 man/curs_printw.3x            |    6 -
 man/curs_scanw.3x             |    6 -
 man/curs_scr_dump.3x          |    2 
 man/curs_termcap.3x           |   10 +-
 man/curs_terminfo.3x          |    8 -
 man/form.3x                   |    2 
 man/form_cursor.3x            |    4 
 man/form_data.3x              |    4 
 man/form_field.3x             |    6 -
 man/form_field_attributes.3x  |    7 +
 man/form_field_buffer.3x      |    6 -
 man/form_field_info.3x        |    4 
 man/form_field_just.3x        |    4 
 man/form_field_new.3x         |    6 -
 man/form_field_opts.3x        |    9 +
 man/form_field_userptr.3x     |    5 -
 man/form_field_validation.3x  |    7 -
 man/form_fieldtype.3x         |    6 -
 man/form_hook.3x              |    8 +
 man/form_new.3x               |    4 
 man/form_new_page.3x          |    4 
 man/form_opts.3x              |    8 +
 man/form_page.3x              |    6 -
 man/form_post.3x              |    5 -
 man/form_requestname.3x       |    6 -
 man/form_userptr.3x           |    5 -
 man/form_win.3x               |    6 -
 man/infocmp.1m                |   43 ++++----
 man/infotocap.1m              |   23 ++--
 man/keybound.3x               |    4 
 man/legacy_coding.3x          |    2 
 man/menu.3x                   |    2 
 man/menu_attributes.3x        |    8 +
 man/menu_cursor.3x            |    4 
 man/menu_driver.3x            |    2 
 man/menu_format.3x            |    4 
 man/menu_hook.3x              |    8 +
 man/menu_items.3x             |    5 -
 man/menu_mark.3x              |    4 
 man/menu_new.3x               |    4 
 man/menu_opts.3x              |    8 +
 man/menu_pattern.3x           |    5 -
 man/menu_post.3x              |    5 -
 man/menu_requestname.3x       |    5 -
 man/menu_spacing.3x           |    5 -
 man/menu_userptr.3x           |    5 -
 man/menu_win.3x               |    7 +
 man/mitem_current.3x          |    8 +
 man/mitem_name.3x             |    7 -
 man/mitem_new.3x              |    6 -
 man/mitem_opts.3x             |    9 +
 man/mitem_userptr.3x          |    7 -
 man/mitem_value.3x            |    6 -
 man/mitem_visible.3x          |    6 -
 man/ncurses.3x                |  149 ++++++------------------------
 man/panel.3x                  |   11 +-
 man/resizeterm.3x             |    2 
 man/term.5                    |   16 +--
 man/term.7                    |   16 +--
 man/terminfo.5                |   32 +++---
 man/tic.1m                    |   21 ++--
 menu/m_item_new.c             |    2 
 misc/tabset/stdcrt            |    2 
 misc/tabset/vt100             |    1 
 misc/tabset/vt300             |    1 
 misc/terminfo.src             |   74 +++++++++++++--
 ncurses/base/MKkeyname.awk    |   11 +-
 ncurses/base/     |    1 
 ncurses/base/MKunctrl.awk     |    1 
 ncurses/base/keybound.c       |    3 
 ncurses/base/lib_mouse.c      |    4 
 ncurses/base/lib_newwin.c     |    2 
 ncurses/base/lib_redrawln.c   |    8 -
 ncurses/base/lib_set_term.c   |    1 
 ncurses/base/resizeterm.c     |    4 
 ncurses/base/safe_sprintf.c   |    6 +
 ncurses/base/vsscanf.c        |    5 -
 ncurses/curses.priv.h         |   25 ++++-
 ncurses/tinfo/   |   27 -----
 ncurses/tinfo/access.c        |   12 +-
 ncurses/tinfo/alloc_entry.c   |   18 ++-
 ncurses/tinfo/captoinfo.c     |   18 ++-
 ncurses/tinfo/comp_error.c    |    2 
 ncurses/tinfo/comp_expand.c   |   10 +-
 ncurses/tinfo/comp_hash.c     |    2 
 ncurses/tinfo/comp_parse.c    |   20 ++--
 ncurses/tinfo/comp_scan.c     |    2 
 ncurses/tinfo/doalloc.c       |    4 
 ncurses/tinfo/home_terminfo.c |    6 -
 ncurses/tinfo/lib_baudrate.c  |    4 
 ncurses/tinfo/lib_cur_term.c  |    2 
 ncurses/tinfo/lib_print.c     |    8 -
 ncurses/tinfo/lib_setup.c     |    5 -
 ncurses/tinfo/lib_termcap.c   |    9 +
 ncurses/tinfo/lib_tgoto.c     |   10 +-
 ncurses/tinfo/lib_tparm.c     |    4 
 ncurses/tinfo/lib_tputs.c     |    4 
 ncurses/tinfo/make_keys.c     |    2 
 ncurses/tinfo/parse_entry.c   |   12 +-
 ncurses/tinfo/read_entry.c    |   20 ++--
 ncurses/tinfo/read_termcap.c  |   25 +----
 ncurses/tinfo/strings.c       |    4 
 ncurses/tinfo/trim_sgr0.c     |    2 
 ncurses/tinfo/write_entry.c   |   21 ++--
 ncurses/trace/lib_trace.c     |    4 
 ncurses/trace/lib_traceatr.c  |   16 +--
 ncurses/trace/lib_tracebits.c |   39 ++++----
 ncurses/trace/lib_tracechr.c  |    9 +
 ncurses/trace/lib_tracemse.c  |   10 +-
 ncurses/trace/trace_buf.c     |   11 +-
 ncurses/trace/trace_tries.c   |    5 -
 ncurses/trace/varargs.c       |   10 +-
 ncurses/trace/visbuf.c        |   14 +-
 ncurses/tty/     |    3 
 ncurses/tty/hardscroll.c      |   10 +-
 ncurses/tty/hashmap.c         |    8 -
 ncurses/tty/lib_mvcur.c       |   10 +-
 ncurses/tty/lib_tstp.c        |   18 +++
 ncurses/tty/tty_update.c      |    6 +
 panel/panel.c                 |    8 -
 progs/dump_entry.c            |   47 ++++-----
 progs/infocmp.c               |  107 ++++++++++++----------
 progs/progs.priv.h            |    2 
 progs/tic.c                   |   52 ++++++----
 progs/tset.c                  |   87 ++++++------------
 140 files changed, 950 insertions(+), 859 deletions(-)

For a better perspective, as of mid-2021, OpenBSD curses differs from the original ncurses sources by about 1.7% (compared to the total 115140 lines).

Hashed database

Beginning in 1999, Todd Miller developed a function which could read a terminfo description from a hashed database (see source). There were pros/cons:

I kept the idea (hashed database) in mind, and in 2006 re-implemented this without the latter limitation:

+ add configure --with-hashed-db option (tested only with FreeBSD 6.0,
  e.g., the db 1.8.5 interface).

I use this feature on all of my BSD ports. It has been in use on FreeBSD since 2013.

An OpenBSD change in 2015 dropped Miller's hashed-database reader. The commit message indicates that the developers did not understand that both hashed database and directory-tree are documented, supported features of ncurses:

Instead of using our own custom BDB terminfo databases, use the ncurses
files in /usr/share/terminfo/*. This removes a large difference from
upstream ncurses and other systems.

As of mid-2021, OpenBSD uses only the directory-tree terminal database.

Signal handlers

While ncurses has provided signal handlers for other purposes, the one for SIGWINCH has gotten the most attention.

Although OpenBSD provided a more-or-less complete set of manual pages, including resizeterm (see CVS source), it took several years to actually provide a SIGWINCH handler in OpenBSD curses so that KEY_RESIZE could be used.

Initially, no reason was given. The developer disabled it by editing the generated ncurses_cfg.h, but did not mention this fact in any commit (see CVS history). These symbols were removed:

The configure option's default value changed over time:

The CVS differences show that Miller noticed this “new” feature (which had been in development over the previous two years), and disabled it in the ncurses-4.2-990301 update:

RCS file: /cvs/src/lib/libcurses/ncurses_cfg.h,v
retrieving revision 1.10
retrieving revision 1.11
diff -u -r1.10 -r1.11
--- src/lib/libcurses/ncurses_cfg.h     1999/01/31 20:25:38     1.10
+++ src/lib/libcurses/ncurses_cfg.h     1999/03/02 06:23:27     1.11
@@ -1,4 +1,4 @@
-/*     $OpenBSD: ncurses_cfg.h,v 1.10 1999/01/31 20:25:38 millert Exp $        */
+/*     $OpenBSD: ncurses_cfg.h,v 1.11 1999/03/02 06:23:27 millert Exp $        */

 /* include/ncurses_cfg.h.  Generated automatically by configure.  */
@@ -115,6 +115,7 @@
 #define USE_DATABASE 1
 #define USE_GETCAP 1
 #define USE_HASHMAP 1
+/* #define USE_SIGWINCH 1 */

        /* The C compiler may not treat these properly but C++ has to */
 #ifdef __cplusplus

I did not notice it myself, but after several years X ran well enough on OpenBSD that others noticed. There were a few mailing list threads in 2007-2008 dealing with the topic:

I posted to those threads, e.g., my reply to Ed Schouten:

List:       openbsd-tech
Subject:    Re: [ncurses] USE_SIGWINCH disabled
From:       Thomas Dickey <dickey () radix ! net>
Date:       2007-04-07 14:22:23
Message-ID: 20070407142223.GA8783 () saltmine ! radix ! net

On Sat, Apr 07, 2007 at 01:34:32PM +0000, Ed Schouten wrote:
> I took a look at the following file in OpenBSD's repository:
> The reason why terminal resizing was broken in the first reason is
> because USE_SIGWINCH is disabled (almost at the bottom). This option

The commit comment doesn't mention the change - only


> that already have their own handler, because those will overrule the
> handler supplied by ncurses. In fact, ncurses 5.6 has it enabled by
> default.

It's been enabled by default for eight years.

> Are there any plans to enable USE_SIGWINCH on OpenBSD, or even better,
> bump ncurses to version 5.6? Thanks :)

Looks like OpenBSD has ncurses 5.2 - 7 years old.

Miller responded indirectly (here):

List:       openbsd-bugs
Subject:    Re: OpenBSD's ncurses doesn't have USE_SIGWINCH
From:       "Todd C. Miller" <Todd.Miller () courtesan ! com>
Date:       2007-04-17 17:39:13
Message-ID: 200704171739.l3HHdDLq007909 () tex ! courtesan ! com
[Download RAW message or body]

In message <>
        so spake Ed Schouten (ed):

> As you can see, USE_SIGWINCH is commented, which disables the handler.
> It's quite irritating that you still have to write your own wrappers
> while almost all other operating systems that include ncurses ship with
> a working SIGWINCH handler.

It was disabled in OpenBSD because we could not convince ourselves
that it was safe (there is actually very little that is safe to do
in a signal handler).

In this case, it doesn't look like insertion and deletion of entries
to _nc_screen_chain are guaranteed atomic so a signal at the right
(wrong) time could potentially corrupt the chain.  If all the signal
handler did was set a flag (as opposed to traversing a list that
may be under construction) it would be safe.

 - todd

as did De Raadt, though neither went beyond comments (i.e., neither has ever contributed any code to improving this area). De Raadt's comment about sig_atomic_t was useful:

Please note that _nc_screen_chain is a pointer, and the C standard
specifically says that nothing but a variable of a sig_atomic_t is
safe to atomically modify.  People also end up having to use volatile
to convince the compiler to not defer source-code requested

Todd suggests that a solution to this problem will be to make the
signal handler modify one global variable (which will be volatile
sig_atommic_t, or volatile int), and then use the mainline loop to
apply this to all the windows.  Then the signal handler does not need
to walk a linked list which is potentially corrupt for small windows
of time.

though he credited it to Miller (who later echoed the same comment).

Given the hint about sig_atomic_t, I improved the SIGWINCH handler for ncurses later that month (2007-04-21):

+ move most static variables into structures _nc_globals and
  _nc_prescreen, to simplify storage.
+ add/use configure script macro CF_SIG_ATOMIC_T, use the corresponding
  type for data manipulated by signal handlers (prompted by comments
  in mailing.openbsd.bugs newsgroup).

However, after I released ncurses 5.7 the following year, I found no corresponding improvement in OpenBSD.

Another three years passed before someone revived the topic on an OpenBSD mailing list:

ncurses 5.7 update and define USE_SIGWINCH (2011-04-22)

Nicholas Marriott responded to that, and when I noticed it, pointed out that OpenBSD was the only group which disabled the feature. In his reply he indicated that he had been discussing it with others, and had concluded that it would be better to enable it:

Date: Sat, 23 Apr 2011 17:43:35 +0100
From: Nicholas Marriott <>
To: Thomas Dickey <>
Subject: Re: ncurses 5.7 update and define USE_SIGWINCH


We've been discussing it a bit since as far as we can now see the
SIGWINCH handler in ncurses is now safe (it didn't seem to be in the old
version we were using). Might be changed yet, personally I think it is
better to use the ncurses handler than have a bunch of patches of
potentially varying quality to add signal handlers to ports. But we'll

On Sat, Apr 23, 2011 at 09:33:12AM -0400, Thomas Dickey wrote:
> right - OpenBSD is the only group that I recall which disables the flag.

That said, he committed an update to enable the SIGWINCH handler later that day:

Revision 1.26, Sat Apr 23 22:08:15 2011 UTC by nicm
Branch: MAIN
Changes since 1.25: +3 -3 lines
Diff to previous 1.25 (unified)

Enable the SIGWINCH handler in ncurses.

Prompted by mail from Mikolaj Kucharski on tech@ and discussion with
deraadt@ and millert@.

A library installing signal handlers without being asked is seriously
wrong but it appears quite a few applications now depend on the ncurses
SIGWINCH and it looks to be safe. Safer than the other signal handlers
it installs (apparently without request... boke).

ok millert

That “without being asked” bears comment:

OpenBSD curses lacks all of the signal-handling improvements made since 2008.

Upgrading difficulties

The main problem to solve in upgrading OpenBSD curses is finding someone to do the work. Marriott mentioned this:

Date: Tue, 12 Feb 2019 04:47:09 -0500
From: Thomas Dickey <>
To: Nicholas Marriott <>
Cc: Thomas Dickey <>
Subject: Re: hardware tabs and xterm OTpt
> I haven't updated the database for a while because I need to look at
> what changed with ncurses 6 that won't be compatible (OpenBSD is still
> using ncurses 5.7 and upgrading is a real pain I haven't had time for).

offhand - ncurses still supports the ABI 5 configuration, so upgrading
with ABI 5 would get various improvements.  6.1 makes a small number
of changes to color- and pair-limits which you could patch for compatibility
with applications that get confused by the increased limits (ultimately
that patch should go away).


Date: Tue, 12 Feb 2019 20:16:17 -0500
From: Thomas Dickey <>
To: Nicholas Marriott <>
Cc: Thomas Dickey <>
Subject: Re: hardware tabs and xterm OTpt
> Its not so much the ABI, it's really an OpenBSD problem - the way
> ncurses was first imported (before the OpenBSD fork from NetBSD) means
> it is spread in several different places around the tree and is very
> time consuming to upgrade. But I'll get to it eventually I hope.

I see (my to-do list never gets shorter, either).

Agreeing that it is in several places in the source-tree, here is a map:

OpenBSD src
├── bin
│   └── (35 entries)
├── distrib
│   └── (148 entries)
├── etc
│   └── (24 entries)
├── games
│   └── (58 entries)
├── gnu
│   └── (3941 entries)
├── include
│   └── (4 entries)
├── lib
│   ├── (skip 239)
│   ├── libcurses
│   │   ├── base
│   │   ├── tinfo
│   │   ├── trace
│   │   ├── tty
│   │   └── widechar
│   ├── (skip 15)
│   ├── libform
│   ├── (skip 26)
│   ├── libmenu
│   ├── (skip 1)
│   ├── libpanel
│   └── (skip 19)
├── libexec
│   └── (42 entries)
├── regress
│   └── (1066 entries)
├── sbin
│   └── (85 entries)
├── share
│   ├── (skip 45)
│   ├── tabset
│   ├── termtypes
│   └── (skip 4)
├── sys
│   └── (533 entries)
├── usr.bin
│   ├── (skip 101)
│   ├── infocmp
│   ├── (skip 128)
│   ├── tic
│   ├── (skip 4)
│   ├── tput
│   ├── (skip 2)
│   ├── tset
│   └── (skip 51)
└── usr.sbin
    └── (224 entries)

6824 directories

upgrading would be simplified by scripting the patching process.


Although OpenBSD developers are slow to upgrade the bundled ncurses, this (like FreeBSD and NetBSD) is a platform that I use for testing portability.


This story starts in October 2000 (before OpenBSD's changes in March 2003):

+ modify parse_format() in lib_tparm.c to ignore precision if it is
  longer than 10000 (report by Jouko Pynnonen).
+ rewrote limit checks in lib_mvcur.c using new functions
  _nc_safe_strcat(), etc.  Made other related changes to check lengths
  used for strcat/strcpy (report by Jouko Pynnonen

The initial report was on the ncurses mailing list (October 2, 2000), with a followup on Bugtraq (October 9, 2000).

That was shortly before I released ncurses 5.2 (which Miller incorporated into OpenBSD on the following day). From my private email, I see some discussion by others suggesting using strlcpy as a followup to Pynnonen's report, but none from an OpenBSD developer. Still, that episode may have been a factor when Miller modified the OpenBSD curses in March/April 2013 to replace strcpy, strcat and sprintf by strlcpy, strlcat and snprintf.

For a while, I made no change regarding that, because I did not frequently install OpenBSD because the BSDs required a primary partition for installing, and it was not unusual for a failed install to require reinstalling everything on a multiboot machine. As for system upgrades, even in 2021 those often fail.

But starting in 2010, I began developing with virtual machines (i.e., allowing frequent installs of machines holding a single operating system). During the first year or so, those included OpenBSD 4.6 (2009), MirBSD 8 and OpenBSD 4.9 (2011).

Initially it was sufficient to update the port. Later, in 2011, I began collecting build-logs systematically. I noticed that MirBSD and OpenBSD would warn when linking programs using strcpy, strcat and sprintf. I have addressed this more generally here, but because ncurses provides libraries, it was more of a nuisance than with lynx, vile or xterm.

The point of the warning is to promote the use of analogous functions strlcpy, strlcat and snprintf. By themselves, those functions cannot ensure that a program is more robust. They are not magic bullets which can replace the need for analysis. But those linker warnings tend to promote a cargo cult style of development, and as such are counter productive.

Keeping in mind portability, although snprintf was standardized in 1997 (i.e., Issue 5 according to The Open Group), it was not universally available until a few years had passed. The OpenBSD manual page states that snprintf and vsnprintf first appeared in 4.4BSD. The CSRG SCCS credits the implementation to Chris Torek, with a 1993 copyright added by Keith Bostic. SunOS 4, Solaris 2.5 and other platforms on which I developed during the 1990s lacked this function.

Miller and De Raadt's 1999 Usenix paper outlines the use of strlcpy and strlcat. It mentions ncurses vs strncpy, referring to an improvement suggested by Miller.

Unlike snprintf, the strlcpy and strlcat functions have not yet been standardized. They are not listed in The Open Group's system interfaces.

One of the answers in Why are strlcpy and strlcat considered insecure? mentions that applications are expected to check the return value, to see if the data was truncated. Miller's conversion of ncurses added no checks of that sort. Aside from eliminating the linker warnings (added a few months later), there is no improvement.

Lacking a better (standard, portable) solution, ncurses checks lengths for string allocation, copying, and concatenation.

However, I would like to eliminate the warnings as well (since they are distracting and interfere with looking for genuine problems). In February 2012, I made this change:

+ add --enable-string-hacks option to control whether strlcat and
  strlcpy may be used.  The same issue applies to OpenBSD's warnings
  about snprintf, noting that this function is weakly standardized.
+ add configure checks for strlcat, strlcpy and snprintf, to help
  reduce bogus warnings with OpenBSD builds.

I implemented this by defining macros which would expand to strlcat, etc., or their more well-established counterparts. The macros are defined in nc_string.h, and for the most part, conversion was simple. In one case (my rewrite of the screen-dump and restore feature in 2015) that required passing the buffer sizes via function parameter. But usually the length was readily available because I was already using it in limit checks.

Here are some counts of the “.c” files in OpenBSD curses:

  255 total files
   41 files using string-hack
   70 snprintf
   33 strlcat
   53 strlcpy
    1 vsnprintf

Here are counts of my macros in current ncurses source (June 2021). This includes the test-directory and ncurses/trace, which are absent from OpenBSD:

  367 total files
   85 files using string-hack
  175 _nc_SLIMIT
  184 _nc_SPRINTF
   62 _nc_STRCAT
   98 _nc_STRCPY
    2 _nc_STRNCAT
   15 _nc_STRNCPY
    1 vsnprintf

Libtool support

OpenBSD has a libtool look-alike. Its manual page says

libtool is supposed to be a drop-in replacement for the eponymous GNU project. The following differences in behaviour with GNU Libtool are intentional:

and goes on to list a few differences which do not apply to ncurses. But it fails to build ncurses anyway. Install the port for GNU libtool, which works.

Locale support

Until OpenBSD 5, locale support was nonexistent. Beginning with OpenBSD 5, there is partial support for locales.

Heads up! OpenBSD now supports multi-byte characters! (July 2010):

On July 27th, Stefan Sperling (stsp@) added support for the multi-byte characters in the OpenBSD libc. Thanks to the work of the people involved in its development, the OpenBSD C library now supports the Unicode character encoding scheme UTF-8.

referring to this:

Replace the single-byte placeholders for the multi-byte/wide-character
conversion interfaces of libc (mbrtowc(3) and friends) with new
implementations that internally call an API based on NetBSD's citrus.
This allows us to support locales with multi-byte character encodings.

Provide two implementations of the citrus-based API: one based on the old
single-byte placeholders for use with our existing single-byte character
locales (C, ISO8859-*, KOI8, CP1251, etc.), and one that provides support
for UTF-8 encoded characters (code based on FreeBSD's implementation).

Install the en_US.UTF-8 ctype locale support file, and allow the UTF-8
ctype locale to be enabled via setlocale(3) (export LC_CTYPE='en_US.UTF-8').

A lot of programs, especially from ports, will now start using UTF-8 if the
UTF-8 locale is enabled. Use at your own risk, and please report any breakage.
Note that ncurses-based programs cannot display UTF-8 right now, this is being
worked on.

To prevent install media growth, add vfprintf(3) and mbrtowc(3) to libstubs.
The mbrtowc stub was copied unchanged from its old single-byte placeholder.
vfprintf.c doesn't need to be copied, just put in .PATH (hint by fgsch@).

Shortly after, ncurses was updated (August 2010):

This builds all the libraries with the widechar code and also links them
to "w" versions (libncursesw, libformw, etc). Some OSs only put the
widechar stuff into libncursesw and not libncurses - I don't much like
knobs in the form of library names but if opinion is that we should go
that way then I can change it.

That “Some OSs” is misleading: most use the configuration as I designed and documented it in May 2000. OpenBSD deleted the relevant documentation in their copy. The commit-message

Remove obsolete tbl suffix from ncurses man pages, no objections from

does not correspond to the actual change (which replaced the ncurses 4.1 manual page with the ncurses 5.7 manual page and deleted portions of it).

Others find this confusing, requiring special handling:

It took a while before updating xterm, as noted in

However, that is only a small part. Consider this:

[prev in list] [next in list] [prev in thread] [next in thread]

List:       openbsd-bugs
Subject:    Re: Locale not supported by C library.
From:       Daniele Bonini <my25mb () aol ! com>
Date:       2020-05-05 3:45:40
Message-ID: 20200505054540.05e8872a () me ! windows ! com

I want to thank again everyone for the time spent in reply to me.

I already adopted a new OS for my purpose that is to write chinese
characters by terminal into the file system.

Daniele Bonini
Project Owner and Author

On Fri, 1 May 2020 19:29:37 +0200
Martijn van Duren <> wrote:

> On 5/1/20 7:13 PM, Daniele Bonini wrote:
> > Hello,
> >
> > I need to work on unicode characters, expecially Chinese characters.
> >
> > So I set this in my .profile:
> >
> >    export LC_CTYPE=zh_CN.GB18030
> >
> > But launching Terminal in Xfce4 I get:
> >
> > (process:47941): xfce4-terminal-WARNING **: 18:51:15.832: Locale not
> > supported by C library.
> >
> > (process:47941): Gtk-WARNING **: 18:51:15.836: Locale not supported
> > by C library.
> >
> > I get the same kind of feedback trying to launch pkg_add.
> >
> > Please note that Xfce4 with Noto fonts on Thunar is running quite
> > smoothly on all characters.
> >
> > If it is not a bug, any help could be much appreciated.
> >
> >
> > Thnx
> >
> > Daniele Bonini
> > Project Owner and Author
> >
> >
> OpenBSD only supports UTF-8. This is intentional and unlikely to
> change. Note that UTF-8 also supports all characters in GB18030[0].
> martijn@
> [0]

By the way, OpenBSD has no iconv port. The iconv program is part of the libiconv port.

Broken linker

Through OpenBSD 6.2, compiling and linking was fairly routine, allowing for spurious linker warnings to promote the use of strlcpy and snprintf. After OpenBSD 6.2, this change was made:

commit 86f55b3d2a934eb190815c5f83a3d683acf7337d
Author: naddy <>
Date:   Fri Sep 14 13:44:18 2018 +0000
    Pass -L/usr/lib to the linker in preparation for switching to lld, which
    does not have a default search path.  ok kettenis@ jsg@
diff --git a/gnu/gcc/gcc/config/i386/openbsd64.h b/gnu/gcc/gcc/config/i386/openbsd64.h
index 24444e505e0..ddef4c13ebf 100644
--- a/gnu/gcc/gcc/config/i386/openbsd64.h
+++ b/gnu/gcc/gcc/config/i386/openbsd64.h
@@ -121,7 +121,8 @@ Boston, MA 02111-1307, USA.  */
    %{!static:-Bdynamic} \
    %{rdynamic:-export-dynamic} \
    %{assert*} \
-   %{!dynamic-linker:-dynamic-linker /usr/libexec/}"
+   %{!dynamic-linker:-dynamic-linker /usr/libexec/} \
+   %{!nostdlib:-L/usr/lib}"
diff --git a/gnu/gcc/gcc/config/i386/openbsdelf.h b/gnu/gcc/gcc/config/i386/openbsdelf.h
index 5832f64cc76..0a475954be6 100644
--- a/gnu/gcc/gcc/config/i386/openbsdelf.h
+++ b/gnu/gcc/gcc/config/i386/openbsdelf.h
@@ -126,6 +126,7 @@ Boston, MA 02110-1301, USA.  */
    %{!static:-Bdynamic} \
    %{rdynamic:-export-dynamic} \
    %{assert*} \
-   %{!dynamic-linker:-dynamic-linker /usr/libexec/}"
+   %{!dynamic-linker:-dynamic-linker /usr/libexec/} \
+   %{!nostdlib:-L/usr/lib}"

The change is faulty, adding the option in the wrong place in the command-line. A user reported that he was unable to compile an up-to-date ncurses with OpenBSD. I reported the problem here:

Compiling ncurses-6.1 on OpenBSD (10 August 2019)

and got no useful response.

That was for OpenBSD 6.3 (released in May 2019). Time passes.

While preparing for ncurses 6.3 in May 2021, I repaired my OpenBSD 6.9 machine using this change:

--- /usr/lib/gcc-lib/amd64-unknown-openbsd6.9/4.2.1/specs.orig  Mon Apr 19 12:17:02 2021
+++ /usr/lib/gcc-lib/amd64-unknown-openbsd6.9/4.2.1/specs       Sat May 29 15:08:17 2021
@@ -48,10 +48,10 @@
 %{!shared:crtend%O%s} %{shared:crtendS%O%s}
-%{!static:--eh-frame-hdr} %{!shared:%{!nostdlib:%{!r*:%{!e*:-e __start}}}}    %{shared:-shared} %{R*}    %{static:-Bstatic}    %{!static:-Bdynamic}    %{rdynamic:-export-dynamic}    %{assert*}    %{!static:%{!dynamic-linker:-dynamic-linker /usr/libexec/}}    %{!nostdlib:-L/usr/lib}
+%{!static:--eh-frame-hdr} %{!shared:%{!nostdlib:%{!r*:%{!e*:-e __start}}}}    %{shared:-shared} %{R*}    %{static:-Bstatic}    %{!static:-Bdynamic}    %{rdynamic:-export-dynamic}    %{assert*}    %{!static:%{!dynamic-linker:-dynamic-linker /usr/libexec/}}
-%{pthread:-lpthread%{!shared:%{p|pg:_p}}} %{!shared:-lc%{p|pg:_p}}
+%{!nostdlib:-L/usr/lib} %{pthread:-lpthread%{!shared:%{p|pg:_p}}} %{!shared:-lc%{p|pg:_p}}
  %{static: %{fmudflap|fmudflapth:  --wrap=malloc --wrap=free --wrap=calloc --wrap=realloc --wrap=mmap --wrap=munmap --wrap=alloca} %{fmudflapth: --wrap=pthread_create}} %{fmudflap|fmudflapth: --wrap=main}

The appropriate place to make the change would be to the LIB_SPEC macro in the gcc source-code.

I had noticed the problem first in the 64-bit machine, because the clang program is (still, as of mid-2021) unreliable for that architecture. It works more reliably on a 32-bit machine, so the packagers discarded the /usr/bin/gcc in favor of clange.

However the same linker problem exists in the gcc port for the i386 platform, but that uses built-in specs (see source), and on first inspection appears to be less readily repaired. However, gcc provides an option which makes it possible to do this without rebuilding the program:

$ sudo su -
# cd /usr/local/bin
# egcc -dumpspecs >gcc.specs
# vi gcc
# cp gcc.specs gcc.specs.orig
# vi gcc.specs
# chmod 755 gcc
# exit

As before, the necessary change moved the -L/usr/lib from the field that specifies the linker path to the field which list linker options:

--- gcc.specs.orig      Sat Jun 12 16:17:19 2021
+++ gcc.specs   Sat Jun 12 16:18:21 2021
@@ -50,10 +50,10 @@
 %{!shared:crtend%O%s} %{shared:crtendS%O%s}
-%{!static|static-pie:--eh-frame-hdr} %{!shared:%{!nostdlib:%{!r:%{!e*:-e __start}}}}    %{shared:-shared} %{R*}    %{static:-Bstatic}    %{!static:-Bdynamic}    %{rdynamic:-export-dynamic}    %{assert*}    %{!static:-dynamic-linker /usr/libexec/}    %{!nostdlib:-L/usr/lib}
+%{!static|static-pie:--eh-frame-hdr} %{!shared:%{!nostdlib:%{!r:%{!e*:-e __start}}}}    %{shared:-shared} %{R*}    %{static:-Bstatic}    %{!static:-Bdynamic}    %{rdynamic:-export-dynamic}    %{assert*}    %{!static:-dynamic-linker /usr/libexec/}
-%{pthread:-lpthread%{!shared:%{p|pg:_p}}} %{!shared:-lc%{p:_p}%{!p:%{pg:_p}}}
+ %{!nostdlib:-L/usr/lib} %{pthread:-lpthread%{!shared:%{p|pg:_p}}} %{!shared:-lc%{p:_p}%{!p:%{pg:_p}}}

and the gcc script uses that fix:

exec egcc -specs=/usr/local/bin/gcc.specs "$@"

After repairing the specs-file, the OpenBSD port of gcc still fails to build ncurses. I chose to set LD_LIBRARY_PATH during the build to work around this problem. That was also needed in one of my OpenBSD 6.7 machines to allow gmake to run (the problem with gmake appears to be fixed in OpenBSD 6.9).

Finally (for June 2021), I encountered one of the known bugs in lld:

GCC generating incorrect relocation R_386_GOTOFF in .debug-info for x86 (32)
[ELF] Allow R_386_GOTOFF from .debug_info (closed February 4, 2021)

To work around this, I disabled the no-leaks option during build-time for the 32-bit machines.

The lld program for which workarounds are necessary is provided by the LLVM project. Its manual page says:

ld.lld is a drop-in replacement for the GNU BFD and gold linkers. It accepts most of the same command line arguments and linker scripts as GNU linkers.

The LLVM project's documentation says more

LLD is a linker from the LLVM project that is a drop-in replacement for system linkers and runs much faster than them. It also provides features that are useful for toolchain developers.

Like the OpenBSD libtool, the LLVM lld is not a “drop-in replacement” (its developers should consider amending their manual page to include “supposed to be” as done by the OpenBSD developer).

According to these:

the OpenBSD developers chose to use lld to improve performance (rather than, for example, because of licensing disputes). The errata listed in the latter overlooks gcc.

Other issues

Not all of the changes are necessary. Some of them just happen.


Some of the changes made in OpenBSD are due to developer's errors, e.g., adding/changing whitespace to these files:

After removing Miller's customized terminfo interface, the prototypes for it still remain in the term_entry.h header file:

--- ncurses-5.7/include/term_entry.h    2008-08-16 12:16:03.000000000 -0400
+++ openbsd/include/term_entry.h        2021-06-17 16:06:12.414529281 -0400
@@ -161,6 +161,12 @@
 /* trace_xnames.c */
 extern NCURSES_EXPORT(void) _nc_trace_xnames (TERMTYPE *);
+#ifdef __OpenBSD__
+/* read_bsd_terminfo.c: terminfo.db reading */
+extern int _nc_read_bsd_terminfo_entry(const char * const, char * const, TERMTYPE *const);
+extern int _nc_read_bsd_terminfo_file(const char * const, TERMTYPE *const);
+#endif /* __OpenBSD__ */
 #ifdef __cplusplus

Those mechanical changes (via a one-off script) interfere with the process of importing new versions of ncurses, because they add a line with CVS keywords for OpenBSD development but fail to make existing RCS keywords inert. When I have to deal with sources in this manner, I change (using a script of course) the $ dollar-signs to @ signs. In reviewing, my script uses an option to the diff program to make it ignore lines which contain RCS/CVS keywords. That option does not work with OpenBSD:

makepatch -diff 'diff -u -I\\\$\\\(Revision\\\|Date\\\|Header\\\|Id\\\):.*\\\$' $1 $2  >$tmp

gcc attributes

In the diffstat above, the difference of ncurses_cfg.h versus ncurses 5.7 is greater than one might suppose. While that is an internal file, used when building ncurses and its associated libraries, it is a generated file. When something is amiss with generated code, one might expect a fix to the generating script.

The short explanation is that OpenBSD developers edited the generated file and during upgrade to ncurses 5.2 and ncurses 5.7 manually added lines to more or less correspond with the upgraded system. You can see this in the CVS history for ncurses_cfg.h — some is uninteresting and can be scripted as I did during review:

# $Id: openbsd-ncu57,v 1.4 2021/06/23 01:09:57 tom Exp $
# Prep ncurses 5.7 for comparison with the OpenBSD version which is transformed
# by openbsd2ncu
rm -rf /tmp/ncurses-5.7
copy /usr/build/ncurses/ncurses-5.7 /tmp/
cd /tmp/ncurses-5.7
unarchive || exit 1
copy -v /usr/build/ncurses/terminfo/RCS misc/
co -fv6_0_20170401 misc/terminfo.src || exit 1
rm -f misc/RCS
rm -rf ./Ada95
cfg-shared \
        --with-terminfo-dirs=/usr/share/terminfo:/usr/local/share/terminfo \
        --disable-hard-tabs \
        --enable-getcap \
        --enable-bsdpad \
        --enable-termcap \
        --enable-widec \
        || exit 1
make sources || exit 1
# OpenBSD developers edit the generated file and do not regen it when doing
# upgrades.  This script reflects those edits.
sed -i.bak \
        -E \
        -e '/TYPEOF_CHTYPE/s,int,long,' \
        -e '/USE_TERMCAP/d' \
        -e '/HAVE_LIB(FORM|MENU|PANEL)/s,^(.*)$,/* \1 */,' \
        -e 's/TERMPATH.*/PURE_TERMINFO 0/' \
        -e '/SYSTEM_NAME/s,[0-9.]+,,' \
diff -u include/ncurses_cfg.h.bak include/ncurses_cfg.h
rm -f include/ncurses_cfg.h.bak
rm -rf ./doc ./c++ ./test
rm -f */Makefile
rm -f */llib-*
rm -f */*.in
rm -f */Caps.*
rm -f */modules */headers */programs
rm -f */*.def */*.ref
rm -f */*.head */*.tail */*.hin
rm -f */*.cmd */*.sed
rm -f include/*.sh include/*.awk
rm -f man/*.sh
rm -f */clear* */tput.* */tset.1
find . -name 'README*' -delete
rm -f * 2>/dev/null
ls -l

Even with those deletions, the order differs. I filtered that manually for this comparison using “sort -u” (to eliminate duplicates). After factoring that out, here is what is left:

--- ncurses-5.7/include/ncurses_cfg.h   Sun Jun 20 07:04:34 2021
+++ openbsd/include/ncurses_cfg.h       Sun Jun 20 07:02:34 2021
@@ -31,7 +31,7 @@
  *  Author: Thomas E. Dickey <> 1997                        *
- * $Id: ncurses_cfg.hin,v 1.7 2005/01/02 01:26:58 tom Exp $
+ * $Id: ncurses_cfg.h,v 1.27 2011/09/21 06:26:51 nicm Exp $
  * This is a template-file used to generate the "ncurses_cfg.h" file.
@@ -50,15 +50,16 @@
 #define CC_HAS_PROTOS 1
 #define ETIP_NEEDS_MATH_H 1
-#define GCC_NORETURN __attribute__((noreturn))
+#define GCC_NORETURN __attribute__((__noreturn__))
 #define GCC_PRINTF 1
 #define GCC_SCANF 1
-#define GCC_UNUSED __attribute__((unused))
+#define GCC_UNUSED __attribute__((__unused__))
 #define HAVE_BIG_CORE 1
 #define HAVE_BTOWC 1
 #define HAVE_DIRENT_H 1
+#define HAVE_ERRNO 1
 #define HAVE_FCNTL_H 1
 #define HAVE_FORM_H 1
 #define HAVE_FSEEKO 1

The change for the parameter of __attribute__ appears unnecessary, because clang's documentation says that it accepts the gcc syntax, while gcc's documentation does not use this alternate spelling:

But the change was made well before there was a clang program to consider (December 2000):

Reading the gcc source code provides an answer in gcc/ONEWS:

Numerous enhancements were made to the __attribute__ facility including
more attributes and more places that support it.  We now support the
"packed", "nocommon", "noreturn", "volatile", "const", "unused",
"transparent_union", "constructor", "destructor", "mode", "section",
"align", "format", "weak", and "alias" attributes.  Each of these
names may also be specified with added underscores, e.g., "__packed__".
__attribute__ may now be applied to parameter definitions, function
definitions, and structure, enum, and union definitions.

Given that clue, it is possible to find this in 6.39 Attribute Syntax:

You may optionally specify attribute names with ‘__’ preceding and following the name. This allows you to use them in header files without being concerned about a possible macro of the same name. For example, you may use the attribute name __noreturn__ instead of noreturn.

The change history makes it apparent that there is no distinction between the documented and alternate spelling, i.e., changes between the forms are due to preferences by some developers rather than because there is a difference in behavior.


As part of the buffer-overflow fixes in 2000, I made this change:

+ add configure option --disable-root-environ, which tells ncurses to
  disregard $TERMINFO and similar environment variables if the current
  user is root, or running setuid/setgid (based on discussion with
  several people).

OpenBSD does not use that feature. Instead, it accepts those environment variables if the user is root but not running setuid/setgid, by

A more appropriate change would have been to the C function _nc_env_access.

 * Returns true if we allow application to use environment variables that are
 * used for searching lists of directories, etc.
    if (issetugid())
        return FALSE;
    if (getuid() != geteuid()
     || getgid() != getegid())
        return FALSE;
    return getuid() != 0;       /* ...finally, disallow root */

That already made the check for setuid/setgid, before Miller's changes.

The manual page for issetugid bears comment:

Lacking better information, it appears to have been implemented in 1996, documented initially by copy/paste from the getuid manual page. It has not been standardized; however, it is available on more than one platform (OpenBSD, FreeBSD, NetBSD, Solaris, and other BSDs).

That 1993 copyright date also appears in the Solaris manpage, where it is less plausible. Solaris 9 (2002), which is the first version of Solaris with this function, does not provide that detail.


I added ncurses_dll.h in December 2000, to simplify porting to Windows-based environments (such as Cygwin, DJGPP or MinGW).

Although <curses.h> includes other files, in this case the OpenBSD developer disagreed, using copy/paste to eliminate the file. For example:

--- ncurses-5.7/include/curses.h        2021-06-23 06:21:11.000000000 -0400
+++ openbsd/include/curses.h    2021-06-23 06:17:48.000000000 -0400
@@ -55,17 +55,25 @@
 #define NCURSES_VERSION "5.7"
+#if !defined(NCURSES_IMPEXP)
+#  define NCURSES_IMPEXP /* nothing */
+#if !defined(NCURSES_API)
+#  define NCURSES_API /* nothing */
+#if !defined(NCURSES_EXPORT)
+#if !defined(NCURSES_EXPORT_VAR)
  * Identify the mouse encoding version.
- * Definitions to facilitate DLL's.
- */
-#include <ncurses_dll.h>
  * User-definable tweak to disable the include of <stdbool.h>.

As a result, the content of <ncurses_dll.h> is inline in 4 places:

  1. curses.h
  2. term.h
  3. termcap.h
  4. curses.priv.h

No rationale was given by the developer. The check-in comment in each case was

  Update to ncurses-5.2-20010114

terminal database

According to a comment in the OpenBSD changes for terminfo.src,

# OpenBSD local changes:
#   - add rxvt-unicode and rxvt-unicode-256color
#   - xterm-r5, xterm-r6, xterm-xf86-v32, xterm+kbs change kbs=^H to kbs=\177
#   - screen change kbs=^H to kbs=\177

most of the change adds the terminal description for rxvt-unicode (see FAQ: Where is rxvt-unicode?).

While making updates to the terminal database, I use a script which compare all of the items (both canonical names and aliases) between the terminfo.src text file and the compiled/installed terminal database. That script reports

and using tic -I1x terminfo.src, there are

That is, updating the terminal database is usually beneficial, but we have to keep things in perspective. OpenBSD happened to avoid the problems with st 0.7 because no one upgraded the terminal database, but without upgrading the ncurses library and utilities, OpenBSD cannot use a current terminal database (i.e., ncurses 6.2 or later). The ncurses 6.2 release notes mention this:

Both of these issues dated from the original implementation of user-defined capabilities. Fixing them does not change the terminal database, but a older tic without the fixes will not be able to handle terminfo sources which rely upon those fixes. Starting in June 2019, the download link for the terminfo source file was capped at that date. The development sources have an up-to-date copy of the file, for people with a legitimate need for it.

The 6.2 release is long past (more than a year at this writing), so the capped download link is moot.

Changes to support rxvt-unicode and st are not new:

but since both are still under development, someone will want an up-to-date terminal description.

Besides ports, OpenBSD has its own (self-inflicted) problems with terminal descriptions. OpenBSD's console for AMD/Intel hardware can display ANSI colors (and presumably other hardware cannot), but OpenBSD sets TERM to “vt220” (which tells applications that it has no colors). Some people notice, and some propose improvements:

(At least they did not call it “xterm”).


The OpenBSD tput and its manual page are not derived from ncurses. The OpenBSD tput is derived from the 4.4BSD version began by Keith Bostic, as outlined in the ncurses tput manual.

There are differences, mainly that the OpenBSD variant is less capable:

This utility should simply be replaced (see ncurses 6.1 release notes).


OpenBSD tset is more complicated. Both it and ncurses began with the same tset.c from 4.4BSD, however most of the followup development has been in ncurses. The OpenBSD variant has been based on ncurses since November 1998, with some local changes.

While the program is derived from ncurses, the tset manual page is not.

This utility should simply be replaced (see ncurses 6.1 release notes).

Other programs

OpenBSD 6.9 lacks these utilities:

See also

Mailing lists

Source code