Team-FLY |
Many special files represent devices with characteristics that are platform dependent, making standardization difficult. However, since terminal control was thought to be essential on all systems, the POSIX standards committee decided to include library functions for manipulating special files representing terminals and asynchronous communication ports. This section describes these functions and the way to use them. The stty command reports or sets terminal I/O characteristics. When executed without any arguments or with the -a or -g options, the stty command outputs information about the current terminal to standard output. The -a produces a longer form of the readable information produced by stty without arguments; the -g option produces the information in a form that can be used by a program. The second form of stty allows operands to change the behavior of the terminal associated with a shell. SYNOPSIS stty [-a -g] stty operands POSIX:Shell and Utilities Exercise 6.16Execute stty , stty -a and stty -g on your system. Try to interpret the results. Answer: The stty command outputs the following under Sun Solaris 9. speed 9600 baud; -parity rows = 34; columns = 80; ypixels = 680; xpixels = 808; swtch = <undef>; brkint -inpck -istrip icrnl -ixany imaxbel onlcr tab3 echo echoe echok echoctl echoke iexten The stty -a command on the same system outputs a more complete listing of the terminal settings. speed 9600 baud; rows = 34; columns = 80; ypixels = 680; xpixels = 808; csdata ? eucw 1:0:0:0, scrw 1:0:0:0 intr = ^c; quit = ^\; erase = ^?; kill = ^u; eof = ^d; eol = <undef>; eol2 = <undef>; swtch = <undef>; start = ^q; stop = ^s; susp = ^z; dsusp = ^y; rprnt = ^r; flush = ^o; werase = ^w; lnext = ^v; -parenb -parodd cs8 -cstopb -hupcl cread -clocal -loblk -crtscts -crtsxoff -parext -ignbrk brkint ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl -iuclc ixon -ixany -ixoff imaxbel isig icanon -xcase echo echoe echok -echonl -noflsh -tostop echoctl -echoprt echoke -defecho -flusho -pendin iexten opost -olcuc onlcr -ocrnl -onocr -onlret -ofill -ofdel tab3 The stty -g command outputs the following on a single line. 2506:1805:d00bd:8a3b:3:1c:7f:15:4:0:0:0:11:13:1a:19:12:f: 17:16:0:0:1:1:0:00:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0: 0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 The interpretation of the fields closely follows the flags in the struct termios structure described below. The stty -a command displays the current terminal settings, and the second form of stty allows you to change them. One important operand of stty is sane . This operand sets all modes to reasonable values and is useful if you terminate a program that has set the modes in an inconvenient way. You can use stty sane to recover when, for example, local echo has been turned off and you cannot see what you are typing. Sometimes you will have to terminate the line containing the stty command with a Ctrl-J rather than pressing the Return key if Return has been set to send a carriage return rather than a newline. Programs access terminal characteristics through the struct termios structure, which includes at least the following members . tcflag_t c_iflag; /* input modes */ tcflag_t c_oflag; /* output modes */ tcflag_t c_cflag; /* control modes */ tcflag_t c_lflag; /* local modes */ cc_t c_cc[NCCS]; /* control characters */ The c_cc array of the struct termios structure holds the values of the characters that have special meaning to the terminal device drivers, for example, the end of input or program break characters. Table 6.1 on page 206 lists the special characters and their default settings. The c_iflag member of the struct termios structure controls the way a terminal handles input; the c_oflag controls the way a terminal handles output. The c_cflag specifies hardware control information for the terminal, and the c_lflag controls the editing functions of the terminal. Table 6.2 on page 210 lists the POSIX values that these flags can take on. You can set an action by performing a bitwise OR of the appropriate struct termios field with the corresponding flag, and you can clear it by performing a bitwise AND with the complement of the flag. Example 6.17The ECHO value of the c_lflag field of struct termios specifies that characters typed at standard input should be echoed to standard output of the terminal. The following code segment clears the ECHO flag in a struct termios structure. struct termio term; term.c_lflag &= ~ECHO; The tcgetattr function retrieves the attributes associated with the terminal referenced by the open file descriptor fildes . The attributes are returned in a struct termios structure pointed to by termios_p . The tcsetattr function sets the parameters of the terminal referenced by the open file descriptor fildes from the struct termios structure pointed to by termios_p . The optional_actions parameter controls the point at which the changes take effect: TCSANOW signifies that changes occur immediately, and TCSADRAIN signifies that changes occur after all output to fildes is transmitted. If optional_actions is TCSAFLUSH , the changes occur after all output to fildes is transmitted. In this case, all input received but not read is discarded. SYNOPSIS #include <termios.h> int tcgetattr(int fildes, struct termios *termios_p); int tcsetattr(int fildes, int optional_actions, const struct termios *termios_p); POSIX These functions return 0 if successful. If unsuccessful , these functions return 1 and set errno . The following table lists the mandatory errors for these functions.
Program 6.11 shows a ttysetchar function that sets a particular character. The ttsetchar function first calls tcgetattr to read the current settings of the terminal into a struct termios structure. After modifying the desired characters, ttysetchar calls tcsetattr to change the actual terminal settings. It is possible for tcsetattr to be interrupted by a signal while it is waiting for output to drain, so we restart it in this case. Example 6.18The following code segment calls the ttysetchar function of Program 6.11 to set the character that indicates end of terminal input to Ctrl-G. (The usual default is Ctrl-D.) if (ttysetchar(STDIN_FILENO, VEOF, 0x07) == -1) perror("Failed to change end-of-file character"); Table 6.1. The POSIX special control characters
Program 6.11 ttysetchar.cA function that sets a particular terminal control character to be a particular value . #include <errno.h> #include <termios.h> #include <unistd.h> int ttysetchar(int fd, int flagname, char c) { int error; struct termios term; if (tcgetattr(fd, &term) == -1) return -1; term.c_cc[flagname] = (cc_t)c; while (((error = tcsetattr(fd, TCSAFLUSH, &term)) == -1) && (errno == EINTR)) ; return error; } Program 6.12 shows a function that uses tcgetattr and tcsetattr to turn echoing on or off. When echoing is turned off, the characters that you type do not appear on the screen. Exercise 6.19Why did Program 6.12 use tcgetattr to read the existing struct termios structure before setting the echo flags? Answer: The code shouldn't change any of the other settings, so it reads the existing struct termios structure before modifying it. Program 6.12 setecho.cA function to turn terminal echo on or off . #include <errno.h> #include <termios.h> #include <unistd.h> #define ECHOFLAGS (ECHO ECHOE ECHOK ECHONL) int setecho(int fd, int onflag) { int error; struct termios term; if (tcgetattr(fd, &term) == -1) return -1; if (onflag) /* turn echo on */ term.c_lflag = ECHOFLAGS; else /* turn echo off */ term.c_lflag &= ~ECHOFLAGS; while (((error = tcsetattr(fd, TCSAFLUSH, &term)) == -1) && (errno == EINTR)) ; return error; } Exercise 6.20What happens when you run the following program? Under what circumstances might such behavior be useful? #include <unistd.h> int setecho(int fd, int onflag); int main(void) { setecho(STDIN_FILENO, 0); return 0; } Answer: After you run this program, you will not see anything that you type on the computer screen. You can log out or use stty sane to set the echo back on. Turning off echoing is used for entering passwords and other secrets. Program 6.13 shows the passwordnosigs function that retrieves the password entered at the controlling terminal of a process. It returns 0 if successful. On failure it returns 1 and sets errno . Notice that passwordnosigs sets the errno based on the first error that occurs. While most functions return immediately after an error, functions that must always restore state have to clean up before they return. The program calls the setecho function of Program 6.12 to turn echoing off and on. It must turn the terminal echo back on before returning or the user won't be able to see what is typed. Program 6.13 passwordnosigs.cA function that prompts for and reads a password, assuming that no signals will occur . #include <errno.h> #include <fcntl.h> #include <stdio.h> #include <string.h> #include <termios.h> #include <unistd.h> #include "restart.h" int readline(int fd, char *buf, int nbytes); int setecho(int fd, int onflag); int passwordnosigs(char *prompt, char *passbuf, int passmax) { int fd; int firsterrno = 0; int passlen; char termbuf[L_ctermid]; if (ctermid(termbuf) == NULL) { /* find the terminal name */ errno = ENODEV; return -1; } if ((fd = r_open2(termbuf, O_RDWR)) == -1) /* open the terminal */ return -1; if (setecho(fd, 0) == -1) /* turn echo off */ firsterrno = errno; else if (r_write(fd, prompt, strlen(prompt)) == -1) /* write prompt */ firsterrno = errno; else if ((passlen = readline(fd, passbuf, passmax)) == 0) firsterrno = EINVAL; else if (passlen == -1) firsterrno = errno; else passbuf[passlen-1] = '#include <errno.h> #include <fcntl.h> #include <stdio.h> #include <string.h> #include <termios.h> #include <unistd.h> #include "restart.h" int readline(int fd, char *buf, int nbytes); int setecho(int fd, int onflag); int passwordnosigs(char *prompt, char *passbuf, int passmax) { int fd; int firsterrno = 0; int passlen; char termbuf[L_ctermid]; if ( ctermid (termbuf) == NULL) { /* find the terminal name */ errno = ENODEV; return -1; } if ((fd = r_open2(termbuf, O_RDWR)) == -1) /* open the terminal */ return -1; if (setecho(fd, 0) == -1) /* turn echo off */ firsterrno = errno; else if (r_write(fd, prompt, strlen(prompt)) == -1) /* write prompt */ firsterrno = errno; else if ((passlen = readline(fd, passbuf, passmax)) == 0) firsterrno = EINVAL; else if (passlen == -1) firsterrno = errno; else passbuf[passlen-1] = '\0'; /* remove newline */ if ((setecho(fd, 1) == -1) && !firsterrno) /* always turn echo back on */ firsterrno = errno; if ((r_write(fd,"\n",1) == -1) && !firsterrno) firsterrno = errno; if ((r_close(fd) == -1) && !firsterrno) firsterrno = errno; if (firsterrno) errno = firsterrno; return firsterrno ? -1 : 0; }'; /* remove newline */ if ((setecho(fd, 1) == -1) && !firsterrno) /* always turn echo back on */ firsterrno = errno; if ((r_write(fd,"\n",1) == -1) && !firsterrno) firsterrno = errno; if ((r_close(fd) == -1) && !firsterrno) firsterrno = errno; if (firsterrno) errno = firsterrno; return firsterrno ? -1 : 0; } The passwordnosigs uses readline of Program 4.1 on page 95 to read in a line from the terminal. We were able to use it here because it was written to use a general file descriptor rather than just reading from standard input. The passwordnosigs function uses the controlling terminal as determined by the ctermid function rather than using standard input. The controlling terminal is usually something like /dev/tty and often shares the same physical devices as standard input and standard output, which are usually the keyboard and screen. One of the consequences of using a controlling terminal rather than standard input and standard output is that controlling terminals cannot be redirected from the command line. This is often used for passwords to discourage users from storing passwords in a file. Exercise 6.21What happens if a signal aborts a program that is executing passwordnosigs ? This could happen if the user enters Ctrl-C after being prompted for the password. Answer: If the signal comes in after passwordnosigs turns off echoing, the user won't be able to see subsequent typing at the terminal. If you do this, try typing stty sane followed by Return to get the terminal back to echo mode. Chapter 8 addresses this issue more carefully in Program 8.4 on page 266. Table 6.2 lists the flags for terminal control. Chapter 8 discusses some of the issues related to terminals and signals. The project of Chapter 11 explores many aspects of terminal configuration and the interaction of terminal devices with user processes. 6.5.1 Canonical and noncanonical input processingA common misconception is that somehow the keyboard and screen are connected, so everything that you type automatically appears on the screen. The keyboard and screen are, in fact, separate devices that communicate with terminal device drivers running on the computer. The device drivers receive bytes from the keyboard, buffering and editing them as specified by the settings for these devices. The usual method of handling terminal input, canonical mode , processes input one line at a time. The special characters of Table 6.1 are used for terminating input and simple editing such as erasing the last character typed. A line is a sequence of bytes delimited by a newline ( NL ), an end-of-file ( EOF ) or an end-of-line ( EOL ). In canonical mode, read requests do not return until the user enters a line delimiter (or the process receives a signal). The ERASE and KILL characters work only on the portion of a line that has not yet been delimited. A read request can return only one line, regardless of the number of bytes requested . If the system defines the POSIX constant MAX_CANON for the terminal, input lines cannot be longer than MAX_CANON . A consequence of canonical mode processing is that input from a terminal behaves differently from input from other devices such as disks. In noncanonical mode , input is not assembled into lines. The device driver does not respond to the ERASE and KILL characters. Noncanonical input processing has two controlling parameters MIN and TIME . The MIN parameter controls the smallest number of bytes that should be gathered before read returns. The TIME parameter refers to a timer with a 0.1-second granularity used for timing out bursty transmissions. Table 6.3 summarizes the settings for MIN and TIME . Table 6.2. The POSIX values of flags for terminal control.
Program 6.14 shows a function that sets the current terminal to be in noncanonical mode with single-character input. After a setnoncanonical call, the terminal device driver delivers each character as typed, treating the ERASE and KILL characters as ordinary characters. The function returns 0 on success. If an error occurs, setnoncanonical returns 1 and sets errno . Exercise 6.22How would you set the terminal back to canonical mode after a call to the function setnoncanonical ? Answer: This may be a problem on some systems. POSIX allows c_cc[MIN] and c_cc[TIME] to be used for VEOF and VEOL in canonical mode. On some systems, a call to setnoncanonical will overwrite these values. Unless these values have been saved, there is no way to restore them to their original values. If you just set the ICANON bit in the c_lflag of the struct termios structure, it may not return the terminal to the previous canonical mode state. Program 6.15 provides a method for handling this. Table 6.3. Parameters for noncanonical mode processing.
Exercise 6.23Suppose that standard input has been set to noncanonical mode. Five characters have been typed at the keyboard. You try to read 10 bytes from standard input. What happens in each of the following cases?
Answer:
Program 6.14 setnoncanonical.cA function that sets the terminal associated with the caller to perform single character input (rather than line processing) . #include <errno.h> #include <fcntl.h> #include <stdio.h> #include <termios.h> #include <unistd.h> #include "restart.h" int ttysetchar(int fd, int flagname, char c); int setnoncanonical(void) { int error; int fd; int firsterrno = 0; struct termios term; char termbuf[L_ctermid]; if (ctermid(termbuf) == NULL) { /* find the terminal name */ errno = ENODEV; return -1; } if ((fd = r_open2(termbuf, O_RDONLY)) == -1) /* open the terminal */ return -1; if (tcgetattr(fd, &term) == -1) /* get its termios */ firsterrno = errno; else { term.c_lflag &= ~ICANON; while (((error = tcsetattr(fd, TCSAFLUSH, &term)) == -1) && (errno == EINTR)) ; if (error) firsterrno = errno; } if (!firsterrno && (ttysetchar(fd, VMIN, 1) ttysetchar(fd, VTIME, 0))) firsterrno = errno; if ((r_close(fd) == -1) && !firsterrno) firsterrno = errno; if (firsterrno) errno = firsterrno; return firsterrno ? -1 : 0; } Program 6.15 shows two functions for saving and restoring the struct termios structure. Each takes a pointer to a struct termios structure as a parameter and returns 0 on success. On error these functions return 1 with errno set. The correct way to temporarily set noncanonical mode is as follows.
Program 6.15 savetermios.cFunctions for saving and restoring the terminal mode . #include <errno.h> #include <fcntl.h> #include <stdio.h> #include <termios.h> #include <unistd.h> #include "restart.h" int gettermios(struct termios *termp) { int fd; int firsterrno = 0; char termbuf[L_ctermid]; if (ctermid(termbuf) == NULL) { /* find the terminal name */ errno = ENODEV; return -1; } if ((fd = r_open2(termbuf, O_RDONLY)) == -1) /* open the terminal */ return -1; if (tcgetattr(fd, termp) == -1) /* get its termios */ firsterrno = errno; if ((r_close(fd) == -1) && !firsterrno) firsterrno = errno; if (firsterrno) { errno = firsterrno; return -1; } return 0; } int settermios(struct termios *termp) { int error; int fd; int firsterrno = 0; char termbuf[L_ctermid]; if (ctermid(termbuf) == NULL) { /* find the terminal name */ errno = ENODEV; return -1; } if ((fd = r_open2(termbuf, O_RDONLY)) == -1) /* open the terminal */ return -1; while (((error = tcsetattr(fd, TCSAFLUSH, termp)) == -1) && (errno == EINTR)) ; if (error) firsterrno = errno; if ((r_close(fd) == -1) && !firsterrno) firsterrno = errno; if (firsterrno) { errno = firsterrno; return -1; } return 0; } |
Team-FLY |