This chapter explains how to use the terminfo database and the terminfo routines to write terminal-independent screen management programs on the UNIX system. Other support tools are also described.
The purpose of this chapter is to explain how to write screen management programs as quickly as possible. Therefore, this chapter does not attempt to cover every detail. Use this chapter to get familiar with the way these routines work, then use the manual pages for more information.
This chapter has the following sections:
terminfo refers to both of the following:
Each terminal description in the database is a separate, compiled file. You use the source code that terminfo(4) describes to create these files and the command tic(1M) to compile them.
The compiled files are normally located in the directories /usr/share/lib/terminfo/?.These directories have single character names, each of which is the first character in the name of a terminal. For example, an entry for the AT&T Teletype 5425 is normally located in the file /usr/share/lib/terminfo/a/att5425.
Here's a simple shell script that uses the terminfo database.
A Shell Script Using terminfo Routines
# Clear the screen and show the 0,0 position. tput clear tput cup 0 0 # or tput home echo "<- this is 0 0" # Show line 5, column 10. tput cup 5 10 echo "<- this is 5 10"
Some programs need to use lower level routines (that is, primitives) than those offered by the curses routines. For such programs, the terminfo routines are offered. They do not manage your terminal screen, but rather give you access to strings and capabilities which you can use yourself to manipulate the terminal.
There are three circumstances when it is proper to use terminfo routines. The first is when you need only some screen management capabilities, for example, making text stand out on a screen. The second is when writing a filter. A typical filter does one transformation on an input stream without clearing the screen or addressing the cursor. If this transformation is terminal dependent and clearing the screen is inappropriate, use of the terminfo routines is worthwhile. The third is when you are writing a special purpose tool that sends a special purpose string to the terminal, such as programming a function key, setting tab stops, sending output to a printer port, or dealing with the status line. Otherwise, you are discouraged from using these routines: the higher level curses routines make your program more portable to other UNIX systems and to a wider class of terminals.
NOTE: You are discouraged from using terminfo routines except for the purposes noted, because curses routines take care of all the glitches present in physical terminals. When you use the terminfo routines, you must deal with the glitches yourself. Also, these routines may change and be incompatible with previous releases.
A terminfo program typically includes the header files and routines shown in the following example.
Typical Framework of a terminfo Program
#include <curses.h> #include <term.h> ... setupterm( (char*)0, 1, (int*)0 ); ... putp(clear_screen); ... reset_shell_mode(); exit(0);
The header files curses.h and term.h are required because they contain the definitions of the strings, numbers, and flags used by the terminfo routines. setupterm takes care of initialization. Passing this routine the values (char*)0, 1, and (int*)0 invokes reasonable defaults. If setupterm can't figure out what kind of terminal you are on, it prints an error message and exits. reset_shell_mode performs functions similar to endwin and should be called before a terminfo program exits.
A global variable like clear_screen is defined by the call to setupterm. It can be output using the terminfo routines putp or tputs,which gives a user more control. This string should not be directly output to the terminal using the C library routine printf(3S), because it contains padding information. A program that directly outputs strings will fail on terminals that require padding or that use the xon/xoff flow control protocol.
At the terminfo level, the higher level routines like addch and getch are not available. It is up to you to output whatever is needed. For a list of capabilities and a description of what they do, see terminfo(4); see curses(3X) for a list of all the terminfo routines.
The general command line for compiling and the guidelines for running a program with terminfo routines are the same as those for compiling any other curses program. See the section "Compiling and Running a terminfo Program" in this guide for more information.
The example program termhl shows a simple use of terminfo routines. It is a version of the "The highlight Program" that does not use the higher level curses routines. termhl can be used as a filter. It includes the strings to enter bold and underline mode and to turn off all attributes.
Example of terminfo Program
/* * A terminfo level version of the highlight program. */ #include <curses.h> #include <term.h> int ulmode = 0; /* Currently underlining */ main(argc, argv) int argc; char **argv; { FILE *fd; int c, c2; int outch(); if (argc > 2) { fprintf(stderr, "Usage: termhl [file]\n"); exit(1); } if (argc == 2) { fd = fopen(argv[1], "r"); if (fd == NULL) { perror(argv[1]); exit(2); } } else { fd = stdin; } setupterm((char*)0, 1, (int*)0); for (;;) { c = getc(fd); if (c == EOF) break; if (c == '\') { c2 = getc(fd); switch (c2) { case 'B': tputs(enter_bold_mode, 1, outch); continue; case 'U': tputs(enter_underline_mode, 1, outch); ulmode = 1; continue; case 'N': tputs(exit_attribute_mode, 1, outch); ulmode = 0; continue; } putch(c); putch(c2); } else putch(c); } fclose(fd); fflush(stdout); resetterm(); exit(0); } /* * This function is like putchar, but it checks for underlining. */ putch(c) int c; { outch(c); if (ulmode && underline_char) { outch('\b'); tputs(underline_char, 1, outch); } } /* * Outchar is a function version of putchar that can be passed to * tputs as a routine to call. */ outch(c) int c; { putchar(c); }
Let's discuss the use of the function tputs(cap, affcnt, outc) in this program to gain some insight into the terminfo routines. tputs applies padding information. Some terminals have the capability to delay output. Their terminal descriptions in the terminfo database probably contain strings like $<20>, which means to pad for 20 milliseconds (see the following section "Specify Capabilities" in this chapter). tputs generates enough pad characters to delay for the appropriate time.
tput has three parameters. The first parameter is the string capability to be output. The second is the number of lines affected by the capability. (Some capabilities may require padding that depends on the number of lines affected. For example, insert_line may have to copy all lines below the current line, and may require time proportional to the number of lines copied. By convention affcnt is 1 if no lines are affected. The value 1 is used, rather than 0, for safety, since affcnt is multiplied by the amount of time per item, and anything multiplied by 0 is 0.) The third parameter is a routine to be called with each character.
For many simple programs, affcnt is always 1 and outc always calls putchar. For these programs,the routine putp(cap) is a convenient abbreviation. termhl could be simplified by using putp.
Now to understand why you should use the curses level routines instead of terminfo level routines whenever possible, note the special check for the underline_char capability in this sample program. Some terminals, rather than having a code to start underlining and a code to stop underlining, have a code to underline the current character. termhl keeps track of the current mode, and if the current character is supposed to be underlined, outputs underline_char, if necessary. Low level details such as this are precisely why the curses level is recommended over the terminfo level. curses takes care of terminals with different methods of underlining and other terminal functions. Programs at the terminfo level must handle such details themselves.
termhl was written to illustrate a typical use of the terminfo routines. It is more complex than it need be in order to illustrate some properties of terminfo programs. The routine vidattr (see curses(3X)) could have been used instead of directly outputting enter_bold_mode, enter_underline_mode, and exit_attribute_mode. In fact, the program would be more robust if it did, since there are several ways to change video attribute modes.
The terminfo database describes the many terminals with which curses programs, as well as some UNIX system tools, like vi(1), can be used. Each terminal description is a compiled file containing the names that the terminal is known by and a group of comma-separated fields describing the actions and capabilities of the terminal. This section describes the terminfo database, related support tools, and their relationship to the curses library.
Descriptions of many popular terminals are already contained in the terminfo database. However, it is possible that you'll want to run a curses program on a terminal for which there is not currently a description. In that case, you'll have to build the description.
The general procedure for building a terminal description is as follows:
1. Give the known names of the terminal.
2. Learn about, list, and define the known capabilities.
3. Compile the newly created description entry.
4. Test the entry for correct operation.
5. Go back to step 2, add more capabilities, and repeat, as necessary.
Building a terminal description is sometimes easier when you build small parts of the description and test them as you go along. These tests can expose deficiencies in the ability to describe the terminal. Also, modifying an existing description of a similar terminal can make the building task easier. (Lest we forget the motto: Build on the work of others.)
In the next few pages, we follow each step required to build a terminal description for the fictitious terminal named myterm.
The name of a terminal is the first information given in a terminfo terminal description. This string of names,assuming there is more than one name, is separated by pipe symbols ( | ). The first name given should be the most common abbreviation for the terminal. The last name given should be a long name that fully identifies the terminal. The long name is usually the manufacturer's formal name for the terminal. All names between the first and last entries should be known synonyms for the terminal name. All names but the formal name should be typed in lowercase letters and contain no blanks. Naturally, the formal name is entered as closely as possible to the manufacturer's name.
Here is the name string from the description of the AT&T Teletype 5420 Buffered Display Terminal:
5420|att5420|AT&T Teletype 5420,
Notice that the first name is the most commonly used abbreviation and the last is the long name. Also notice the comma at the end of the name string.
Here's the name string for our fictitious terminal myterm:
myterm|mytm|mine|fancy|terminal|My FANCY Terminal,
Terminal names should follow common naming conventions. These conventions start with a root name, like 5425 or myterm, for example. The root name should not contain odd characters, like hyphens, that may not be recognized as a synonym for the terminal name. Possible hardware modes or user preferences should be shown by adding a hyphen and a ``mode indicator'' at the end of the name. For example, the ``wide mode'' (which is shown by a -w) version of our fictitious terminal would be described as myterm-w. term(5) describes mode indicators in greater detail.
After you complete the string of terminal names for your description, you have to learn about the terminal's capabilities so that you can properly describe them. To learn about the capabilities your terminal has, you should do the following:
stty -echo; cat -vu type in the keys you want to test;for example, see what right arrow (->) transmits. <CR> <CTRL-D> stty echo or cat >dev/null type in the escape sequences you want to test; for example, see what \E[H transmits. <CTRL-D>
Once you know the capabilities of your terminal, you have to describe them in your terminal description. You describe them with a string of comma-separated fields that contain the abbreviated terminfo name and, in some cases, the terminal's value for each capability. For example, bel is the abbreviated name for the beeping or ringing capability. On most terminals, a CTRL-G is the instruction that produces a beeping sound.Therefore, the beeping capability would be shown in the terminal description as bel=^G,.
The list of capabilities may continue onto multiple lines as long as white space (that is, tabs and spaces) begins every line but the first of the description. Comments can be included in the description by putting a # at the beginning of the line.
The terminfo(4) manual page has a complete list of the capabilities you can use in a terminal description. This list contains the name of the capability, the abbreviated name used in the database, the two-letter code that corresponds to the old termcap database name, and a short description of the capability. The abbreviated name that you will use in your database descriptions is shown in the column titled ``Capname.''
NOTE: For a curses program to run on any given terminal, its description in the terminfo database must include, at least, the capabilities to move a cursor in all four directions and to clear the screen.
A terminal's character sequence (value) for a capability can be a keyed operation (like CTRL-G), a numeric value, or a parameter string containing the sequence of operations required to achieve the particular capability. In a terminal description, certain characters are used after the capability name to show what type of character sequence is required. Explanations of these characters follow:
# |
This shows a numeric value is to follow. This character follows a capability that needs a number as a value. For example, the number of columns is defined as cols#80,. |
= |
This shows that the capability value is the character string that follows. This string instructs the terminal how to act and may actually be a sequence of commands. There are certain characters used in the instruction strings that have special meanings. These special characters follow: |
^ |
This shows a control character is to be used. For example, the beeping sound is produced by a CTRL-G. This would be shown as ^G. |
\E or \e |
These characters followed by another character show an escape instruction. An entry of \EC would transmit to the terminal as ESCAPE-C. |
\n |
These characters provide a <NL> character sequence. |
\l |
These characters provide a linefeed character sequence. |
\r |
These characters provide a return character sequence. |
\t |
These characters provide a tab character sequence. |
\b |
These characters provide a backspace character sequence. |
\f |
These characters provide a form feed character sequence. |
\s |
These characters provide a space character sequence. |
\nnn |
This is a character whose three-digit octal is nnn, where nnn can be one to three digits. |
$< > |
These symbols are used to show a delay in milliseconds. The desired length of delay is enclosed inside the ``less than/greater than'' symbols (< >). The amount of delay may be a whole number, a numeric value to one decimal place (tenths), or either form followed by an asterisk (*). The * shows that the delay will be proportional to the number of lines affected by the operation. For example, a 20- millisecond delay per line would appear as $<20*>. See the terminfo(4) manual page for more information about delays and padding. |
Sometimes, it may be necessary to comment out a capability so that the terminal ignores this particular field. This is done by placing a period ( . ) in front of the abbreviated name for the capability. For example, if you would like to comment out the beeping capability, the description entry would appear as
.bel=^G,
With this background information about specifying capabilities, let's add the capability string to our description of myterm. We'll consider basic, screen-oriented, keyboard-entered, and parameter string capabilities.
Some capabilities common to most terminals are bells, columns, lines on the screen, and over striking of characters, if necessary. Suppose our fictitious terminal has these and a few other capabilities, as listed below. Note that the list gives the abbreviated terminfo name for each capability in the parentheses following the capability description:
By combining the name string (see the section "Name the Terminal'') and the capability descriptions that we now have, we get the following general terminfo database entry:
myterm|mytm|mine|fancy|terminal|My FANCY terminal,am,bel=^G, cols#80, lines#30, xon,
Screen-oriented capabilities manipulate the contents of a screen. Our example terminal myterm has the following screen-oriented capabilities. Again, the abbreviated command associated with the given capability is shown in parentheses.
The revised terminal description for myterm including these screen-oriented capabilities follows:
myterm|mytm|mine|fancy|terminal|My FANCY Terminal, am, bel=^G, cols#80, lines#30, xon, cr=^M, cuu1=^K, cud1=^J, cub1=^H, cuf1=^L, smso=\ED, rmso=\EZ, el=\EK$<3>, ind=\n,
Keyboard-entered capabilities are sequences generated when a key is typed on a terminal keyboard. Most terminals have, at least, a few special keys on their keyboard, such as arrow keys and the backspace key. Our example terminal has several of these keys whose sequences are, as follows:
Adding this new information to our database entry for myterm produces:
myterm|mytm|mine|fancy|terminal|My FANCY Terminal, am, bel=^G, cols#80, lines#30, xon, cr=^M, cuu1=^K, cud1=^J, cub1=^H, cuf1=^L, smso=\ED, rmso=\EZ, el=\EK$<3>, ind=0 kbs=^H, kcuu1=\E[A, kcud1=\E[B, kcuf1=\E[C, kcub1=\E[D, khome=\E[H,
Parameter string capabilities are capabilities that can take parameters - for example, those used to position a cursor on a screen or turn on a combination of video modes. To address a cursor, the cup capability is used and is passed two parameters: the row and column to address. String capabilities, such as cup and set attributes (sgr) capabilities, are passed arguments in a terminfo program by the tparm routine.
The arguments to string capabilities are manipulated with special '%' sequences similar to those found in a printf(3S) statement. In addition, many of the features found on a simple stack-based RPN calculator are available. cup, as noted above, takes two arguments: the row and column. sgr, takes nine arguments, one for each of the nine video attributes. See terminfo(4) for the list and order of the attributes and further examples of sgr.
Our fancy terminal's cursor position sequence requires a row and column to be output as numbers separated by a semicolon, preceded by ESCAPE-[ and followed with H. The coordinate numbers are 1-based rather than 0-based. Thus, to move to row 5, column 18, from (0,0), the sequence 'ESCAPE-[ 6 ; 19 H' would be output.
Integer arguments are pushed onto the stack with a '%p' sequence followed by the argument number, such as '%p2' to push the second argument. A shorthand sequence to increment the first two arguments is '%i'. To output the top number on the stack as a decimal, a '%d' sequence is used, exactly as in printf. Our terminal's cup sequence is built up as follows:
\E[ |
output ESCAPE-[ |
%i |
increment the two arguments |
%p1 |
push the 1st argument (the row) onto the stack |
%d |
output the row as a decimal |
; |
output a semi-colon |
%p2 |
push the 2nd argument (the column) onto the stack |
%d |
output the column as a decimal |
H |
output the trailing letter or cup=\E[%i%p1%d;%p2%dH, |
Adding this new information to our database entry for myterm produces:
myterm|mytm|mine|fancy|terminal|My FANCY Terminal, am, bel=^G, cols#80, lines#30, xon, cr=^M, cuu1=^K, cud1=^J, cub1=^H, cuf1=^L, smso=\ED, rmso=\EZ, el=\EK$<3>, ind=0 kbs=^H, kcuu1=\E[A, kcud1=\E[B, kcuf1=\E[C, kcub1=\E[D, khome=\E[H, cup=\E[%i%p1%d;%p2%dH,
See terminfo(4) for more information about parameter string capabilities.
The terminfo database entries are compiled using the tic compiler. This compiler translates terminfo database entries from the source format into the compiled format.
The source file for the description is usually in a file suffixed with .ti. For example, the description of myterm would be in a source file named myterm.ti. The compiled description of myterm would usually be placed in /usr/share/lib/terminfo/m/myterm, since the first letter in the description entry is m. Links would also be made to synonyms of myterm, for example, to /f/fancy. If the environment variable $TERMINFO were set to a directory and exported before the entry was compiled, the compiled entry would be placed in the $TERMINFO directory. All programs using the entry would then look in the new directory for the description file if $TERMINFO were set, before looking in the default /usr/share/lib/terminfo. The general format for the tic compiler is as follows:
tic [-v] [-c] file
The -v option causes the compiler to trace its actions and output information about its progress. The -c option causes a check for errors; it may be combined with the -v option. file shows what file is to be compiled. If you want to compile more than one file at the same time, you have to first use cat(1) to join them together. The following command line shows how to compile the terminfo source file for our fictitious terminal:
tic -v myterm.ti<CR> (The trace information appears as the compilation proceeds.)
Refer to the tic(1M) manual page for more information about the compiler.
Let's consider three ways to test a terminal description. First, you can test it by setting the environment variable $TERMINFO to the path name of the directory containing the description. If programs run the same on the new terminal as they did on the older known terminals, then the new description is functional.
Second, you can test for correct insert line padding by commenting out xon in the description and then editing (using vi(1)) a large file (over 100 lines) at 9600 baud (if possible), and deleting about 15 lines from the middle of the screen. Type u (undo) several times quickly. If the terminal messes up, then more padding is usually required. A similar test can be used for inserting a character.
Third, you can use the tput(1) command. This command outputs a string or an integer according to the type of capability being described. If the capability is a Boolean expression, then tput sets the exit code (0 for TRUE, 1 for FALSE) and produces no output. The general format for the tput command is as follows:
tput[-Ttype] capname
The type of terminal you are requesting information about is identified with the -Ttype option. Usually, this option is not necessary because the default terminal name is taken from the environment variable $TERM. The capname field is used to show what capability to output from the terminfo database.
The following command line shows how to output the "clear screen'' character sequence for the terminal being used:
tput clear (The screen is cleared.)
The following command line shows how to output the number of columns for the terminal being used:
tput cols (The number of columns used by the terminal appears here.)
The tput(1) manual page contains more information on the usage and possible messages associated with this command.
Sometime you may want to compare two terminal descriptions or quickly look at a description without going to the terminfo source directory. The infocmp(1M) command was designed to help you with both of these tasks. Compare two descriptions of the same terminal; for example,
mkdir /tmp/old /tmp/new TERMINFO=/tmp/old tic old5420.ti TERMINFO=/tmp/new tic new5420.ti infocmp -A /tmp/old -B /tmp/new -d 5420 5420
compares the old and new 5420 entries.
To print out the terminfo source for the 5420, type
infocmp -I 5420
CAUTION: The terminfo database is designed to take the place of the termcap database.Because of the many programs and processes that have been written with and for the termcap database, it is not feasible to do a complete cutover at one time. Any conversion from termcap to terminfo requires some experience with both databases. All entries into the databases should be handled with extreme caution. These files are important to the operation of your terminal.
The captoinfo(1M) command converts termcap descriptions to terminfo(4) descriptions. When a file is passed to captoinfo, it looks for termcap descriptions and writes the equivalent terminfo descriptions on the standard output. For example,
captoinfo /usr/share/lib/termcap
converts the file/usr/share/lib/termcap to terminfo source,preserving comments and other extraneous information within the file. The command line
captoinfo
looks up the current terminal in the termcap database, as specified by the $TERM and $TERMCAP environment variables and converts it to terminfo.
If you must have both termcap and terminfo terminal descriptions, keep the terminfo description only and use infocmp -C to get the termcap descriptions. This is recommended because the terminfo entry will be more complete, descriptive, and accurate than the termcap entry possibly could be.
If you have been using cursor optimization programs with the -ltermcap or -ltermlib option in the cc command line, those programs will still be functional.However, these options should be replaced with the -lcurses option.