|
|
A STREAMS line discipline module called ldterm (see ldterm(7)) is a key part of the STREAMS-based terminal subsystem. Throughout this section, the terms ``line discipline'' and ldterm are used interchangeably and refer to the STREAMS version of the standard line discipline and not the traditional character version.
The termio and termios specifications describe four flags that are used to control the terminal: c_iflag (defines input modes), c_oflag (defines output modes), c_cflag (defines hardware control modes), and c_lflag (defines terminal functions used by ldterm). To process these flags elsewhere (for example, in the firmware or in another process), a mechanism is in place to turn on and off the processing of these flags. When ldterm is pushed, it sends an M_CTL message downstream, which asks the driver which flags the driver will process. The driver sends back that message in response if it needs to change ldterm's default processing. By default, ldterm assumes that it must process all flags except c_cflag, unless it receives a message telling otherwise.
When ldterm is pushed on the Stream, the open routine initializes the settings of the termio flags. The default settings are
c_iflag = BRKINT|ICRNL|IXON|ISTRIP c_oflag = OPOST|ONLCR|TAB3 c_cflag = 0 c_lflag = ISIG|ICANON|ECHO|ECHOKIn canonical mode (ICANON flag in c_lflag is turned on), read from the terminal file descriptor is in message nondiscard (RMSGN) mode (see streamio(7)). This implies that in canonical mode, read on the terminal file descriptor always returns at most one line regardless of how many characters have been requested. In noncanonical mode, read is in byte-stream (RNORM) mode.
The open routine of the ldterm module allocates space for holding state information.
The ldterm module establishes a controlling tty for the line when an M_SETOPTS message (so_flags is set to SO_ISTTY) is sent upstream. The Stream head allocates the controlling tty on the open, if one is not already allocated.
To maintain compatibility with existing application-programs that use the O_NDELAY flag, the open routine sets the SO_NDELON flag on in the so_flags field of the stroptions structure in the M_SETOPTS message.
The open routine fails if there is insufficient space for allocating the state structure, or when an interrupt occurs while the open is sleeping until memory becomes available.
The close routine frees all the outstanding buffers allocated by this Stream. It also sends an M_SETOPTS message to the Stream head to undo the changes made by the open routine. The ldterm module also sends M_START and M_STARTI messages downstream to undo the effect of any previous M_STOP and M_STOPI messages.
The idea of letting post-processing (the o_flags) happen off the host processor is not recommended unless the board software is prepared to deal with international (EUC) character sets properly. The reason for this is that post-processing must take the EUC information into account. ldterm knows about the screen width of characters (that is, how many columns are taken by characters from each given code set on the current physical display) and it takes this width into account when calculating tab expansions. When using multibyte characters or multicolumn characters ldterm automatically handles tab expansion (when TAB3 is set) and does not leave this handling to a lower module or driver.
By default, multibyte handling by ldterm is turned off. When ldterm receives an EUC_WSET ioctl call, it turns multibyte processing on, if it is essential to handle properly the indicated code set. Thus, if one is using single byte 8-bit codes and has no special multicolumn requirements, the special multicolumn processing is not used at all. This means that multibyte processing does not reduce the processing speed or efficiency of ldterm unless it is actually used.
The following describes how the EUC handling in ldterm works:
First, the multibyte and multicolumn character handling is only enabled when the EUC_WSET ioctl indicates that one of the following conditions is met:
Assuming that one or more of the above conditions, EUC handling is enabled. At this point, a parallel array, used for other information, is allocated. When a byte with the high bit arrives, it is checked to see if it is SS2 or SS3. If so, it belongs to code set 2 or 3. Otherwise, it is a byte that comes from code set 1. Once the extended code set flag has been set, the input processor retrieves the subsequent bytes, as they arrive, to build one multibyte character. A counter field tells the input processor how many bytes remain to be read for the current character. The parallel array holds the display width of each logical character in the canonical buffer. During erase processing, positions in the parallel array are consulted to figure out how many backspaces need to be sent to erase each logical character. (In canonical mode, one backspace of input erases one logical character, no matter how many bytes or columns that character consumes.) This greatly simplifies erase processing for EUC.
There are two relevant files for handling multibyte characters: <euc.h> and <eucioctl.h>. The <eucioctl.h> header contains the structure that is passed with EUC_WSET and EUC_WGET calls. The normal way to use this structure is to get CSWIDTH (see note below) from the locale using a mechanism such as getwidth or setlocale and then copy the values into the structure in <eucioctl.h>, and send the structure using an I_STR ioctl call. The EUC_WSET call informs the ldterm module about the number of bytes in extended characters and how many columns the extended characters from each set consume on the screen. This allows ldterm to treat multibyte characters as single entities for erase processing and to calculate correctly tab expansions for multibyte characters.
The file <euc.h> has the structure with fields for EUC width, screen width, and wide character width. The following functions are used to set and get EUC widths (these functions assume the environment where the eucwidth_t structure is needed and available):
#include <eucioctl.h> /* need some other things too, like stropts.h */struct eucioc eucw; /* for EUC_WSET/EUC_WGET to line discipline */ eucwidth_t width; /* return struct from _getwidth() */
/* * set_euc Send EUC code widths to line discipline. */
set_euc(e) set_euc(struct eucioc *e) { struct strioctl sb;
sb.ic_cmd = EUC_WSET; sb.ic_timout = 15; sb.ic_len = sizeof(struct eucioc); sb.ic_dp = (char *) e;
if (ioctl(0, I_STR, &sb) < 0) fail(); } /* * euclook Get current EUC code widths from line discipline. */
euclook(e) euclook(struct eucioc *e) { struct strioctl sb;
sb.ic_cmd = EUC_WGET; sb.ic_timout = 15; sb.ic_len = sizeof(struct eucioc); sb.ic_dp = (char *) e; if (ioctl(0, I_STR, &sb) < 0) fail(); printf("CSWIDTH=%d:%d,%d:%d,%d:%d\n", e->eucw[1], e->scrw[1], e->eucw[2], e->scrw[2], e->eucw[3], e->scrw[3]); }
The brief discussion of multiple byte character handling by the ldterm module was provided here for those interested in internationalization applications in UNIX System V.