"hSetBuffering stdin NoBuffering" messes up terminal

Glynn Clements glynn.clements at virgin.net
Tue Oct 14 16:47:29 EDT 2003


Alastair Reid wrote:

> > On my terminal (aterm), calling
> >
> > hSetBuffering stdin NoBuffering
> >
> > within my program messes up the terminal settings somehow, [...]
> 
> If I understand recent changes correctly, this is a deliberate decision.  The 
> idea is that if you wanted to write a program like 'stty' in Haskell, you'd 
> be very disappointed if the terminal settings got switched back the moment 
> your Haskell program terminated.

It's deliberate, but (AFAIK) not for those reasons. The idea is so
that "simple, stupid programs" do what the programmer expects (and
saving the list from lots of "my program doesn't get any input until
the user hits Return" questions).

If you were writing "stty" in Haskell, you would presumably use the
PosixTTY functions directly.

> Given this (reasonable) change, it might be worth providing an additional 
> function:
> 
>   hWithBuffering :: Handle -> BufferMode -> IO a -> IO a
> 
> which sets the buffering mode back when it finishes.

The main issue here is that this really needs to handle abnormal exits
(i.e. crashes). The user-space buffering (which is all that C's
setvbuf() etc affect) is, er, in user space, so it ceases to be an
issue as soon as the process terminates.

OTOH, the terminal settings are tied to the device, not to any
particular process.

There's also the issue that the settings should be restored
temporarily if the user suspends the program via SIGTSTP (i.e. 
Ctrl-Z).

When writing such programs in C, you have to control the tty buffering
yourself (via e.g. tcsetattr()), as well as disabling the user-space
buffering with e.g. setvbuf(). You would typically install signal
handlers to restore the settings upon abnormal exits (and atexit() for
normal exits), and also restore them on SIGTSTP and re-restore them on
SIGCONT.

Yes, it's awkward, and results in certain frequently-asked questions
from people who are use to using getch() etc from <conio.h> on DOS. 
But there are enough valid solutions that simply implementing one
particular solution "automagically" is going to bite people
occasionally.

> (There's a bunch of alternative designs possible here - this has the virtue of 
> being simple.)

Removing the TTY handling from hSetBuffering is even simpler. For
"simple, stupid programs", we could just provide e.g. hSetCooked, so
programs which want the current behaviour can use:

	hSetBuffering stdin NoBuffering
	hSetCooked stdin False

Programs which don't want this behaviour would just omit the
hSetCooked call, which is substantially easier than the current
situation (figure out if stdin is a tty, save the terminal settings,
disable buffering, restore the settings).

-- 
Glynn Clements <glynn.clements at virgin.net>


More information about the Haskell-Cafe mailing list