Unix Programming Frequently Asked Questions - Part III

Unix Programming Frequently Asked Questions - Part III

点击打开链接

3. Terminal I/O

3.1 How can I make my program not echo input?

How can I make my program not echo input, like login does when asking foryour password?

There is an easy way, and a slightly harder way:

The easy way, is to use getpass(), which is probably found onalmost all Unices. It takes a string to use as a prompt. It will read upto an EOF or newline and returns a pointer to a static area ofmemory holding the string typed in.

The harder way is to use tcgetattr() and tcsetattr(), bothuse a struct termios to manipulate the terminal. The followingtwo routines should allow echoing, and non-echoing mode.

#include <stdlib.h>
#include <stdio.h>

#include <termios.h>
#include <string.h>

static struct termios stored_settings;

void echo_off(void)
{
    struct termios new_settings;
    tcgetattr(0,&stored_settings);
    new_settings = stored_settings;
    new_settings.c_lflag &= (~ECHO);
    tcsetattr(0,TCSANOW,&new_settings);
    return;
}

void echo_on(void)
{
    tcsetattr(0,TCSANOW,&stored_settings);
    return;
}

Both routines used, are defined by the POSIX standard.

3.2 How can I read single characters from the terminal?

How can I read single characters from the terminal? My program is alwayswaiting for the user to press RETURN.

Terminals are usually in canonical mode, where input is read in linesafter it is edited. You may set this into non-canonical mode, where youset how many characters should be read before input is given to yourprogram. You also may set the timer in non-canonical mode terminals to0, this timer flushs your buffer at set intervals. By doing this, youcan use getc() to grab the key pressed immediately by theuser. We use tcgetattr() and tcsetattr() both of which aredefined by POSIX to manipulate the termios structure.

#include <stdlib.h>
#include <stdio.h>

#include <termios.h>
#include <string.h>

static struct termios stored_settings;

void set_keypress(void)
{
    struct termios new_settings;

    tcgetattr(0,&stored_settings);

    new_settings = stored_settings;

    /* Disable canonical mode, and set buffer size to 1 byte */
    new_settings.c_lflag &= (~ICANON);
    new_settings.c_cc[VTIME] = 0;
    new_settings.c_cc[VMIN] = 1;

    tcsetattr(0,TCSANOW,&new_settings);
    return;
}

void reset_keypress(void)
{
    tcsetattr(0,TCSANOW,&stored_settings);
    return;
}

3.3 How can I check and see if a key was pressed?

How can I check and see if a key was pressed? On DOS I use thekbhit() function, but there doesn't seem to be an equivalent?

If you set the terminal to single-character mode (see previous answer),then (on most systems) you can use select() or poll() totest for readability.

3.4 How can I move the cursor around the screen?

How can I move the cursor around the screen? I want to do full screenediting without using curses.

Seriously, you probably don't want to do this. Curses knowsabout how to handle all sorts of oddities that different terminal typesexhibit; while the termcap/terminfo data will tell you whether any giventerminal type possesses any of these oddities, you will probably findthat correctly handling all the combinations is a huge job.

However, if you insist on getting your hands dirty (so to speak), lookinto the termcap functions, particularly tputs(),tparm() and tgoto().

3.5 What are pttys?

Pseudo-teletypes (pttys, ptys, other variant abbreviations) arepseudo-devices that have two parts: the master side, which can bethought of as the `user', and the slave side, which behaves like astandard tty device.

They exist in order to provide a means to emulate the behaviour of aserial terminal under the control of a program. For example,telnet uses a pseudo-terminal on the remote system; the remotelogin shell sees the behaviour it expects from a tty device, but themaster side of the pseudo-terminal is being controlled by a daemon thatforwards all data over the network. They are also used by programs suchas xterm, expect, script, screen,emacs, and many others.

3.6 How to handle a serial port or modem?

The handling of serial devices under Unix is heavily influenced by thetraditional use of serial terminals. Historically, various combinationsof ioctls and other hacks were necessary to control the precise behaviourof a serial device, but fortunately this is one of the areas that POSIXmade some efforts to standardise.

If you're using a system that doesn't understand <termios.h>,tcsetattr() and related functions, then you'll have to goelsewhere for information (or upgrade your system to something lessarchaeological).

There are still significant differences between systems, however, mainlyin the area of device names, handling of hardware flow control, andmodem signalling. (Whenever possible, leave the device driver to do allthe handshaking work, and don't attempt to manipulate handshakingsignals directly.)

The basic steps for opening and initialising a serial device are:

  • open() the device; this may require the use of certain flags:
    O_NONBLOCK
    Opening a dial-in or modem-controlled device will block until carrier ispresent, unless this flag is used. A nonblocking open gives you theopportunity to disable the modem controls (see CLOCAL below) ifnecessary.
    O_NOCTTY
    On 4.4BSD-derived systems this is redundant, but on other systems itcontrols whether the serial device can become a control terminal for thesession. In most cases you probably don't want to acquire acontrol terminal, and should therefore specify this flag, but there areexceptions.
  • Use tcgetattr() to retrieve the current device modes. While onewill often ignore most or all of the initial settings thus obtained, it'sstill a convenient way of initialising a struct termios.
  • Set suitable values for c_iflag, c_oflag, c_cflag,c_lflag, and c_cc in the termios structure. (See below.)
  • Use cfsetispeed() and cfsetospeed() to set the desiredbaud rate. Very few systems allow you to set differing input and outputspeeds, so as a general rule you should set both to your desired speed.
  • Use tcsetattr() to set the device modes.
  • You may wish, if you used O_NONBLOCK when opening the port, touse fcntl() to ensure that O_NONBLOCK is turned off again.Systems seem to differ as to whether a nonblocking open on a tty willaffect subsequent read() calls; better to be explicit.

Once you have opened and set up the port, you can then use read()and write() normally. Note that the behaviour of read() willbe controlled by the flag settings you gave to tcsetattr().

tcflush(), tcdrain(), tcsendbreak() andtcflow() are additional useful functions that you should be awareof.

When you're done with the port, and want to close it, be aware of a verynasty little hazard on some systems; if there's any pending output waitingto be written to the device (e.g. if output flow is stopped by hardwareor software handshaking), your process can hang unkillably in theclose() call until the output drains. Calling tcflush() todiscard any pending output is probably a wise move.

(Blocked output on tty devices is by far the most common cause of"unkillable" processes in my experience.)

3.6.1 Serial device names and types

The device names used for serial port devices vary quite widely betweensystems. Some examples from different systems are:

  • `/dev/tty[0-9][a-z]' for direct access devices, and`/dev/tty[0-9][A-Z]' for modem control devices (e.g. SCO Unix)
  • `/dev/cua[0-9]p[0-9]' for direct access devices,`/dev/cul[0-9]p[0-9]' for dial-out devices and`/dev/ttyd[0-9]p[0-9]' for dial-in devices(e.g. HP-UX)
  • `/dev/cua[a-z][0-9]' for dial-out devices and`/dev/tty[a-z][0-9]' for dial-in devices (e.g. FreeBSD)

The precise interaction between the device name used, and the effect onany hardware handshake lines is system-, configuration- andhardware-dependant, but will usually follow approximately these rules(assuming that the hardware is RS-232 DTE):

  • A successful open of any device should assert DTR and RTS
  • A blocking open of a modem-control or dial-in device will wait forDCD (and possibly also DSR and/or CTS) to be raised, usually afterasserting DTR/RTS.
  • An open of a dial-out device while an open call to the correspondingdial-in device is blocked waiting for carrier may or may notcause the open of the dial-in port to complete. Some systems implement asimple sharing scheme for dial-in and dial-out ports whereby the dial-inport is effectively "put to sleep" while the dial-out port is in use;other systems do not do this, and sharing the port between dial-in anddial-out on such systems requires external cooperation (e.g. use of UUCPlockfiles) to avoid contention problems.

3.6.2 Setting up termios flags

Some hints on setting up the termios flags when using a serial devicethat you've opened yourself (as opposed to using your existing controltty):

3.6.2.1 c_iflag

You probably want to set all the bits in c_iflag to 0,unless you want to use software flow control (ick) in which case you setIXON and IXOFF.

3.6.2.2 c_oflag

Most of the bits of c_oflag are hacks of one sort or another tomake output to slow terminals work, and as such some newer systems havedropped almost all of them as obsolete (especially all the goryoutput-padding options). As with c_iflag, setting everything to 0is reasonable for most applications.

3.6.2.3 c_cflag

When setting the character size, remember to mask using CSIZEfirst; e.g. to set 8-bit characters, use:

    attr.c_cflag &= ~CSIZE;
    attr.c_cflag |= CS8;

Other important flags found in c_cflag that you probably want toturn on and CREAD and HUPCL.

If you need to generate even parity, then set PARENB and clearPARODD; if you need to generate odd parity then set bothPARENB and PARODD. If you don't want parity at all, thenmake sure PARENB is clear.

Clear CSTOPB unless you actually need to generate two stop bits.

Flags for enabling hardware flow control may also be found inc_cflag, but they aren't standardised (pity).

3.6.2.4 c_lflag

Most applications will probably want to turn off ICANON(canonical, i.e. line-based, input processing), ECHO, andISIG.

IEXTEN is a more complex issue. If you don't turn it off, theimplementation is allowed to do nonstandard things (like defineadditional control characters in c_cc) that might causeunexpected results, but you might need to leave IEXTEN enabledon some systems to get useful features like hardware flow control.

3.6.2.5 c_cc

This is an array of characters that have special meanings on input.These characters are given names like VINTR, VSTOP etc.;the names are indexes into the array.

(Two of these "characters" are not really characters at all, but controlthe behaviour of read() when ICANON is disabled; these areVMIN and VTIME.)

The indexes are often referred to as though they were actual variables,e.g. "set VMIN to 1" actually means "set c_cc[VMIN] to 1". The shorthandis useful and only occasionally confusing.

Many of the slots in c_cc are only used if some other combinationof flags is set:

Used only if ICANON is set
VEOF, VEOL, VERASE, VKILL (and also VEOL2, VSTATUS and VWERASE if defined and IEXTEN is set)
Used only if ISIG is set
VINTR, VQUIT, VSUSP (and also VDSUSP ifdefined and IEXTEN is set)
Used only if IXON or IXOFF is set
VSTOP, VSTART
Used only if ICANON is not set
VMIN, VTIME

Implementations may define additional entries in c_cc. It maybe prudent to initialise all the entries to _POSIX_VDISABLE(the constant NCCS gives the array size) before setting thespecific values you wish to use.

VMIN and VTIME (which may share slots with VEOF andVEOL respectively, depending on the implementation) have thefollowing meaning. The value of VTIME is (if not 0) alwaysinterpreted as a timer in tenths of seconds.

c_cc[VMIN] > 0, c_cc[VTIME] > 0
read() will return when either VMIN bytes of input are available,or if at least one character has been read and VTIME has expired betweencharacters, or if interrupted by a signal.
c_cc[VMIN] > 0, c_cc[VTIME] == 0
read() will return when VMIN bytes of input are available, or ifinterrupted. Otherwise it will wait indefinitely.
c_cc[VMIN] == 0, c_cc[VTIME] > 0
read() will return as soon as any input is available; if VTIMEexpires with no data arriving, it will return with no characters read.(This conflicts slightly with the end-of-file indication received inthe event of modem hangup; using 1 for VMIN and either alarm()or select() for a timeout avoids this particular problem.)
c_cc[VMIN] == 0, c_cc[VTIME] == 0
read() will always return immediately; if no data is availableit will return with no characters read (with the same problem as above).


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值