Compile a Private terminfo Entry Safely with tic

Every terminal description on your system started life as plain text compiled by tic, and this walks through doing that safely in a private database. You will finish with a checked terminfo source file, a compiled entry, and a command that can read it back without touching anything system-wide. This guide uses the package-owned /usr/bin/tic from ncurses-bin, version 6.4+20240113-1ubuntu2.2, whose program reports ncurses 6.4.20240113.

Allow about fifteen minutes. You need a shell, a text editor or a way to create the source file, and permission to write a temporary directory. The examples do not need root and do not alter the system terminfo database. A system-wide install is a separate, security-sensitive change and is deliberately not part of the main workflow.

1. Confirm which tic you are running

Several ncurses installations can provide a command named tic. Check the path, package version and program version before relying on an option:

$ command -v tic
/home/linuxbrew/.linuxbrew/bin/tic
$ /usr/bin/tic -V
ncurses 6.4.20240113
$ dpkg-query -W -f='${Package} ${Version}\n' ncurses-bin
ncurses-bin 6.4+20240113-1ubuntu2.2

On this machine the shell finds a separate Homebrew ncurses 6.6 binary first. The examples use /usr/bin/tic so that they exercise the installed Debian package documented here. If your path is different, use the binary whose version and documentation you have checked.

Checkpoint: The version command exits successfully and the package query names ncurses-bin. If /usr/bin/tic is absent on your system, stop and identify the package that owns your preferred binary before continuing.

2. Create a small terminfo source entry

Make a file in a working directory. This entry is intentionally modest: it names a terminal, declares automatic margins, sets its dimensions, and supplies clear-screen and cursor-addressing strings.

$ mkdir -p "$HOME/terminfo-work"
$ cd "$HOME/terminfo-work"
$ editor demo-term.terminfo
demo-term|Demo terminal,\
    am, cols#80, lines#24, clear=\E[H\E[2J,
    cup=\E[%i%p1%d;%p2%dH,

The first line contains the primary name, an optional description and aliases separated by vertical bars. Subsequent lines contain capabilities. A trailing backslash continues the source entry. Numeric capabilities use #; string capabilities use =. The escape sequences are terminfo syntax, not shell syntax, so do not add shell quotes to the file.

Terminology can be confusing here. The source is text, while the compiled description is binary data read by ncurses. The term(5) format documents the compiled record as a header followed by names, Boolean flags, numbers, strings and a string table. You normally should not edit that binary directly.

3. Check syntax without producing output

Run tic -c before compiling. The -c option validates syntax and use references, but produces no compiled output:

$ /usr/bin/tic -c demo-term.terminfo
$ printf 'check status: %s\n' "$?"
check status: 0

A successful check is normally silent. If it reports a line, column or terminal name, fix the source and repeat this step. A warning about a capability that is unknown to ncurses may require either correcting the name or deciding explicitly that it is a user-defined extension.

Checkpoint: Do not move on until the check returns status 0. This stage is safe to repeat and does not need elevated privileges.

4. Compile into a private database

Choose an output directory rather than allowing tic to select a database for you:

$ db="$HOME/terminfo-work/db"
$ mkdir -p "$db"
$ /usr/bin/tic -o "$db" -s demo-term.terminfo
1 entries written to /home/USER/terminfo-work/db
$ find "$db" -type f -print
/home/USER/terminfo-work/db/d/demo-term

Replace USER in the displayed path with your login name. The two-level directory layout is normal: the first character of the terminal name selects a directory, and the entry name is the file below it. The -s option prints a short summary, which is useful for catching a source file that contained no matching entries.

The output directory is now state that you can remove and rebuild. The reversible cleanup command is:

$ rm -r -- "$db"

Only use that command when $db is the private directory you just selected. Do not substitute /usr/share/terminfo, /etc/terminfo or a directory copied from another administrator.

5. Read the compiled entry back

Tell infocmp to search the private database for this one command. This does not change the database:

$ TERMINFO="$db" infocmp demo-term
#       Reconstructed via infocmp from file: /home/USER/terminfo-work/db/./d/demo-term
demo-term|Demo terminal,\,
        am,
        cols#80, lines#24,
        clear=\E[H\E[2J,
        cup=\E[%i%p1%d;%p2%dH,

The exact wrapping and comment path can vary, but the entry name and the capabilities should be present. Setting TERMINFO for this command makes the lookup unambiguous. It does not permanently configure your shell or applications.

If another program must use the private database for a test, give that program the same environment variable, for example TERMINFO="$db" TERM=demo-term command. The value of TERM selects the entry; TERMINFO selects where the library looks first.

6. Understand the default and avoid accidental writes

Without -o, tic normally writes the system terminfo database. It can instead use the location named by TERMINFO. If that location cannot be written, ncurses may fall back to $HOME/.terminfo or $HOME/.terminfo.db, depending on whether the build uses a directory tree or hashed database. Libraries search these locations in a defined order, beginning with TERMINFO, then the home database, configured directories and system locations.

Inspect the locations known to your binary before a deliberate installation:

$ /usr/bin/tic -D
/home/USER/.terminfo
/etc/terminfo
/lib/terminfo
/usr/share/terminfo

The first location is the intended write location when the command can use it. Output differs with permissions, environment variables and how ncurses was built. Treat -D as a diagnostic, not as proof that a source file has been compiled.

Warning: Compiling into a shared system database changes what other applications can find. Do it only when you have a reviewed entry, understand the consumers and have the required administrative approval. Prefer -o with a private directory while developing or testing. If a deliberate system install needs undoing, remove only the exact compiled entry and restore any managed package files from the system's configuration process. Do not delete the whole database.

7. Use filtering and extensions deliberately

A source file can contain more than one terminal entry. The -e option limits writes or translations to a comma-separated list of names or aliases:

$ /usr/bin/tic -o "$db" -e demo-term -s demo-term.terminfo
1 entries written to /home/USER/terminfo-work/db

An unknown capability is rejected as an ordinary capability name unless you use -x. That option tells ncurses to infer whether the user-defined capability is Boolean, numeric or string, and store it as an extended capability. Check that the applications consuming it understand the extension; -x does not make the capability portable to every curses implementation.

For source translation, options such as -I, -C, -L and -N change the format or compatibility rules. Use them for a specific conversion requirement, not as generic compile switches. In particular, terminfo capabilities do not all have exact termcap equivalents. Pairing -C with -c asks for additional conversion checks.

Done means