telnet and $LINES

Kevin Buettner kev@primenet.com
Fri, 29 Sep 2000 07:41:50 -0700


On Sep 29,  4:35am, der.hans wrote:

> > Have you thought about creating a PTY on the near side of the
> > connection?  Once you have a PTY, you should be able to change this
> > sort of thing with the TIOCSWINSZ ioctl.  (I've even written some
> > Perl code to do this in the past - both creating PTYs as well as setting
> > the size.  Let me know if you'd like to have a look.)
> 
> Yes, I would. I think we've gotten a better solution than shoving this all
> over a telnet connection, but now that I've run into this wall I'd like to
> know how to get around it :).

Okay.  The stuff you want to look at is in a file called shell.pm.  I
can send just this file to you, but it won't be of a lot of use (i.e,
you won't be able to run it to see what it does) without the
surrounding machinery.

The surrounding machinery is a programmer's editor called vile (or
xvile, for those of us who use the X window system).  This is a
project that I've been an occasional contributor to; I contributed
the original autoconf machinery, much of the X support, the Perl
extension language support, as well as much of the support for
attributes.

Anyhow, shell.pm allows you to run a shell (in a PTY) in one of
the editor windows.  It even provides enough vt100 terminal emulation
to be able to run vi (or even vile) in one of the editor windows.
The resize bit (the stuff you're asking about) comes into play
when you resize one of these windows.  Here's the relevant
subroutine from shell.pm:

    # Make sure the size of the vile window holding the shell matches the
    # width and height saved in the terminal emulator state.
    sub check_size {
	return unless defined &TIOCSWINSZ;

	my ($w, $te_state, $pty) = @_;
	my ($height, $width) = $w->size;
	if ($height != $te_state->{HEIGHT} or $width != $te_state->{WIDTH}) {
	    $sizeparam = pack 'S4', $height, $width, 0, 0;
	    if (ioctl($pty, &TIOCSWINSZ, $sizeparam) == 0) {
		$te_state->{WIDTH} = $width;
		$te_state->{HEIGHT} = $height;
		# Just reset the scrolling region to be the entire display.
		$te_state->{SR_TOP} = 1;
		$te_state->{SR_BOT} = $height;
	    }
	}
    }

The important lines for your purposes are the ioctl() line and the
line before it which uses pack to construct an appropriate struct
winsize to pass to the TIOCSWINSZ ioctl.

The code which creates the PTY is fairly self contained.  Here it is too...

    # Do the low level work of starting a shell... Returns the pty's filehandle
    # and the pid of the shell if successful.  If it's unsuccessful, the die
    # message will tell you why.
    sub start_shell {
	my ($shell) = @_;
	$shell = $ENV{SHELL}                unless defined $shell;
	$shell = '/bin/sh'                  unless defined $shell;

	my $pty     = new IO::Pty           or die "Can't open new pty: $!";

	my $pid = fork;
	die "Can't fork"                    if $pid < 0;

	if ($pid == 0) {
	    POSIX::setsid();
	    my $tty = $pty->slave;
	    open(STDIN, "<&".fileno($tty))  or die "Can't reopen STDIN";
	    open(STDOUT, ">&STDIN")         or die "Can't reopen STDOUT";
	    open(STDERR, ">&STDIN")         or die "Can't reopen STDERR";
	    system("stty sane");
	    close $pty;
	    close $tty;
	    $ENV{TERM} = 'vt100';
	    exec $shell;
	}
	return ($pty, $pid);
    }

Doing something like this in C takes *a lot* more code.

Anyway... I really recommend taking a look at all of shell.pm and maybe
even trying it out.  To get it, fetch:

    ftp://dickey.his.com/vile/vile-9.1.tgz

The do the following (going by memory):

    tar xzvf vile-9.1.tgz
    cd vile-9.1
    ./configure --with-screen=x11 --with-perl
    make
    make install

Then, to invoke the editor, type ``xvile''.  (You'll need /usr/local/bin
on your PATH.)

Check to make sure that the perl support is working for you.  Try
something like:

    :perl print 3+4

(It should print 7 on the message line.)

To try the shell stuff out, do the following:

    :perl use shell
    :start-shell

Oops.  I nearly forgot; before the shell stuff will work, you'll need
to follow these instructions from the shell.pm documentation:

       You will need the IO::Pty module.  This is available from

           http://www.cpan.org/modules/by-module/IO/IO-Tty-0.02.tar.gz

       You may want to check CPAN for a more recent version prior
       to downloading this version.

       Resizing won't work unless you have sys/ioctl.ph
       installed.  This file is created from your system's
       <sys/ioctl.h> file.  In order to install sys/ioctl.ph,
       simply obtain whatever privileges are necessary for
       writing to the perl installation directories and then do:

           cd /usr/include
           h2ph -a -l sys/*

Okay, with all that out of the way, try running ``top'' in xvile's
shell window.  Then either resize the entire X window or just the
pane that the shell is in.  (You can resize the pane by dragging
the border between the two panes.  You'll need to grab it by it's
handle in the scrollbar region though; the mouse cursor will change
to a thick up-down arrow when you're in the right spot.)

(If you want to play around with some of the other features of
this editor, like the syntax coloring features, let me know and
I'll send you my .vilerc file which sets things up.  By default,
all the whiz-bang stuff is not enabled.... probably not a good
default, huh?)

Kevin