From: Steve Scaffidi Date: Tue, 22 Sep 2009 22:01:14 +0000 (-0400) Subject: Cleaned up demos, various build fixes X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=3e03d89e861aadf4ce5cd842ecbaa3bd0b7f4729;p=urisagit%2FStem.git Cleaned up demos, various build fixes --- diff --git a/Build.PL b/Build.PL index 3da144c..702ee1a 100644 --- a/Build.PL +++ b/Build.PL @@ -37,33 +37,13 @@ $build->is_unixish() || die "Stem currently only installs properly on *nix-like print <<'EOT'; -Building Stem - -This script will ask you various questions in order to properly -configure, build and install Stem on your system. Whenever a question -is asked, the default answer will be shown inside [brackets]. -Pressing enter will accept the default answer. If a choice needs to be -made from a list of values, that list will be inside (parentheses). - -If you have already configured Stem in a previous build, you can put -use_defaults=1 on the Build command line and you won't be prompted for -any answers and the previous settings will be used. - -If you want to force a new build, run Build clean. - ----------------------------------------------------------------------------- - -EOT - -print <<'EOT'; - Stem comes with a utility called 'run_stem' which takes care of things -like initalizing Stem with a configuration file and controlling it's -operation via various parameters you can pass in as environment -variables or command line arguments. +like initalizing Stem with a configuration file and controlling it's +operation via various parameters you can pass in as environment +variables or command line arguments. Stem configuration files are used to create and initialize Stem Cells -(objects). run_stem can search a path list for config files, so you +(objects). run_stem can search a path list for config files, so you can set that list of directories here. Note that you can easily override this path with either a shell environment @@ -73,96 +53,52 @@ run_stem for how so do this. The last directory in the list is where the standard and demo Stem configuration files will be installed. -Please enter a list of directory paths separated by ':'. - EOT my $conf_path = $build->prompt( - "What directories do you want Stem to search for configuration files?\n", + "Please enter a list of directory paths separated by ':'\n", '.:~/.stem/conf:/usr/local/stem/conf' ); $build->config_data(conf_path => $conf_path); - - - -=begin comment - print "\n\nChecking to see if you have a good C compiler...\n\n" ; if ( $build->have_c_compiler() ) { print <<'EOT'; - - + + ssfe (Split Screen Front End) is a compiled program optionally used by the Stem demonstration scripts that provides a full screen interface with command line editing and history. It is not required to run Stem but it makes the demonstrations easier to work with and they look much nicer. To use ssfe add the '-s' option when you run any demonstration -script. You can also use ssfe for your own programs. Install ssfe in -some place in your $PATH ($conf->{'bin_path'} is where Stem executables -are being installed) so it can be used by the demo scripts. The ssfe -install script will do this for you or you can do it manually after -building it. +script. You can also use ssfe for your own programs. If you build ssfe +it will be installed in the demo/ directory. You may copy it to a place +in your $PATH is you wish. EOT - my $install_ssfe = $build->y_n("Do you want to install ssfe?\n", 'y'); - $build->config_data(install_ssfe => $install_ssfe); - if ( $install_ssfe ) { - - # Do horrible, nasty things. - # This really should be done with a proper makefile. - - } + my $build_ssfe = $build->y_n("Do you want to build ssfe?\n", 'y'); + $build->config_data(build_ssfe => $build_ssfe); } -=cut - - - - print <<'EOT'; -Stem comes with a variety of demos to show how to get started and do some -basic things. +Stem comes with a variety of demos to show how to get started and do some +basic things. If you wish, they can be configured to run locally out of the +demo/ directory. EOT -my $install_demos = $build->y_n("\nDo you want to install the demos?\n",'n'); -$build->config_data( install_demos => $install_demos ) ; -$build->config_data( build_demos => $install_demos ) ; -if ( $install_demos ) { - - my $demo_dir = $build->prompt( - "\nWhere do you want to install the demo scripts?\n", - '/usr/local/stem/demo' - ); - $build->config_data(demo_dir => $demo_dir); - $build->install_path()->{demo} ||= $demo_dir; - - - - my $demo_conf_dir = $build->prompt( - "\nWhere do you want to install the demo config files?\n", - '/usr/local/stem/conf' - ); - $build->config_data(demo_conf_dir => $demo_conf_dir); - $build->install_path()->{conf} ||= $demo_conf_dir; - $build->add_build_element('conf'); - - my $cur_conf_path = $build->config_data('conf_path') ; - my $new_conf_path = $cur_conf_path =~ /(^|:)$demo_conf_dir(:|$)/ ? - $cur_conf_path : "$cur_conf_path:$demo_conf_dir" ; - $build->config_data( conf_path => $new_conf_path ) ; - +my $build_demos = $build->y_n("\nDo you want to build the demos?\n",'y'); +$build->config_data(build_demos => $build_demos); +if ( $build_demos ) { # Check for telnet my $telnet_path = $build->find_binary( 'telnet' ) || '' ; while ( ! -x $telnet_path && ! $build->_is_unattended() ) { print <<'EOT'; - telnet was not found on this system. you can't run the demo programs without telnet. Make sure you enter a valid path to telnet or some other terminal emulator. @@ -202,7 +138,7 @@ EOT . "(or another compatible terminal emulator)", '/usr/bin/xterm' ) ; - } +} $build->config_data( xterm_path => $xterm_path ) ; } diff --git a/BuildStem.pm b/BuildStem.pm index a6aa8cb..53b52dd 100644 --- a/BuildStem.pm +++ b/BuildStem.pm @@ -13,32 +13,53 @@ use Module::Build; use vars qw(@ISA); @ISA = qw(Module::Build); +sub ACTION_build { -sub process_script_files { - my ( $self ) = @_ ; - my $files = $self->find_script_files(); - return unless keys %$files; + my ( $self ) = @_; - my $script_dir = File::Spec->catdir($self->blib, 'script'); - my $demo_dir = File::Spec->catdir($self->blib, 'demo'); - File::Path::mkpath( $script_dir ); - File::Path::mkpath( $demo_dir ); - $self->add_to_cleanup($demo_dir); + if ( $self->config_data( 'build_demos' ) ) { + $self->build_demo_scripts(); + } - foreach my $file (keys %$files) { - my $dest_dir = $file =~ /_demo$/ ? $demo_dir : $script_dir ; - my $result = $self->copy_if_modified($file, $dest_dir, 'flatten') or next; - $self->fix_shebang_line($result) if $self->is_unixish(); - $self->make_executable($result); - my $demo_run_dir = File::Spec->catdir($self->base_dir(), 'demo'); - if ( $result =~ /(?:run_stem$)|(?:_demo$)/ ) { - my $result2 = $self->copy_if_modified($result, $demo_run_dir, 'flatten') or next; - $self->add_to_cleanup($result2); - } - } - return 1; + if ( $self->config_data( 'build_ssfe' ) ) { + $self->build_ssfe(); + } + + $self->SUPER::ACTION_build(); +} + + +# yes, hard coded, will fix some other time +sub build_ssfe { + my ( $self ) = @_; + print "Compiling ssfe\n"; + system( "cd extras; tar xzf sirc-2.211.tar.gz; cp sirc-2.211/ssfe.c ../demo" ); + system( "cc -o demo/ssfe demo/ssfe.c -ltermcap 2>/dev/null" ); + $self->add_to_cleanup(qw(demo/ssfe demo/ssfe.c )); } + + +sub build_demo_scripts { + my ( $self ) = @_; + + my $demo_dir = 'demo'; + + my @files = ; + + for my $file (@files) { + my $result = $self->copy_if_modified( + $file, $demo_dir, 'flatten'); + + $self->fix_shebang_line($result) if $self->is_unixish(); + $self->make_executable($result); + $self->add_to_cleanup($result); + } + +} + +########################################################### +# Find stem config files under the build dir and notify M::B about them. sub process_conf_files { my ( $self ) = @_ ; my $files = $self->_find_file_by_type('stem','conf'); @@ -48,21 +69,31 @@ sub process_conf_files { File::Path::mkpath( $conf_dir ); foreach my $file (keys %$files) { - my $result = $self->copy_if_modified($file, $conf_dir, 'flatten') or next; - $self->fix_shebang_line($result) if $self->is_unixish(); + my $result = $self->copy_if_modified( + $file, $conf_dir, 'flatten'); + next unless $result; } return 1; } + +########################################################### +# A horrible hack to attempt to find the location of a binary program... +# It would be nice if this functionality was already part of M::B +# or there was a CPAN module for it that didn't suck. sub find_binary { my ( $self, $prog ) = @_ ; - if ( $self->do_system( "which $prog >/dev/null" ) ) { - return `which $prog` ; + # make sure the command will succeed before extracting the path. + if ( $self->do_system( "which $prog >/dev/null" ) ) { + my $path = `which $prog` ; + chomp $path; + return $path; } return; } + ########################################################### # Various convenience routines. # diff --git a/demo/chat2_demo b/bin/demo/chat2_demo similarity index 67% rename from demo/chat2_demo rename to bin/demo/chat2_demo index 1340a79..4fe3f92 100755 --- a/demo/chat2_demo +++ b/bin/demo/chat2_demo @@ -1,20 +1,17 @@ #!/usr/local/bin/perl -s +use lib qw(../blib/lib blib/lib); +use Stem::ConfigData; +$ENV{PATH} .= ':blib/script:../blib/script:demo:.'; +my $xterm = Stem::ConfigData->config('xterm_path'); +my $telnet = Stem::ConfigData->config('telnet_path'); +$conf_path='conf:../conf'; + $line_cnt = 10 ; $offset = 175 ; $base_off = 0 ; $xskip = ( $^O eq 'solaris' ) ? 600 : 500 ; -use File::Basename qw( basename ); -$ENV{PATH} .= ':' . basename( $0 ) . ':.'; -<<<<<<< HEAD:bin/chat2_demo -======= - ->>>>>>> origin/master:bin/chat2_demo -for my $prog qw( run_stem xterm telnet ) { - die "Can't find $prog in PATH\n" if - system( "which $prog >/dev/null 2>&1" ) != 0; -} my @children ; @@ -23,14 +20,14 @@ $SIG{ 'INT' } = \&cleanup ; if ( $s ) { $ssfe = 'ssfe' ; - $prompt = '-prompt Stem:' ; - $prompt2 = '-prompt Chat:' ; + $prompt = '-prompt "Stem: "' ; + $prompt2 = '-prompt "Chat: "' ; $echo = 'console_echo=1' } foreach $cmd ( split /\n/, <config('xterm_path'); +my $telnet = Stem::ConfigData->config('telnet_path'); +$conf_path='conf:../conf'; + + +$line_cnt = 10 ; +$offset = 175 ; +$base_off = 0 ; +$xskip = ( $^O eq 'solaris' ) ? 600 : 500 ; + +print "CHAT DEMO\n" ; + + +my @children ; + +$SIG{ 'INT' } = \&cleanup ; + +if ( $s ) { + + $ssfe = 'ssfe' ; + $prompt = '-prompt "Stem: "' ; + $prompt2 = '-prompt "Chat: "' ; + $echo = 'console_echo=1' +} + +foreach $cmd ( split /\n/, <config('xterm_path'); +my $telnet = Stem::ConfigData->config('telnet_path'); +my $conf_path='conf:../conf'; + $line_cnt = 10 ; $offset = 175 ; $base_off = 0 ; @@ -7,27 +14,19 @@ $xskip = ( $^O eq 'solaris' ) ? 600 : 500 ; my @children ; -print "INETD: $ENV{PATH}\n" ; - -use File::Basename qw( basename ); -$ENV{PATH} .= ':' . basename( $0 ) . ':.:../bin:./bin'; -for my $prog qw( run_stem xterm telnet ) { - die "Can't find $prog in PATH\n" if - system( "which $prog >/dev/null 2>&1" ) != 0; -} $SIG{ 'INT' } = \&cleanup ; if ( $s ) { $ssfe = 'ssfe' ; - $prompt = '-prompt Stem:' ; - $echo = 'console_echo=1' -# $prompt2 = '-prompt Chat:' ; + $prompt = '-prompt "Stem: "' ; + $echo = 'console_echo=1'; + $prompt2 = '-prompt "Chat: "' ; } foreach $cmd ( split /\n/, <config('xterm_path'); +my $telnet = Stem::ConfigData->config('telnet_path'); +$conf_path='conf:../conf'; + $line_cnt = 10 ; $offset = 175 ; $base_off = 0 ; $xskip = ( $^O eq 'solaris' ) ? 600 : 500 ; -use File::Basename qw( basename ); -$ENV{PATH} .= ':' . basename( $0 ) . ':.'; -for my $prog qw( run_stem xterm telnet ) { - die "Can't find $prog in PATH\n" if - system( "which $prog >/dev/null 2>&1" ) != 0; -} - my @children ; my $tail_dir = 'tail' ; @@ -21,7 +21,7 @@ $SIG{ 'INT' } = \&cleanup ; if ( $s ) { $ssfe = 'ssfe' ; - $prompt = '-prompt Stem:' ; + $prompt = '-prompt "Stem: "' ; $echo = 'console_echo=1' } @@ -34,20 +34,16 @@ foreach my $log ( qw( foo bar bar_status ) ) { foreach $cmd ( split /\n/, </dev/null 2>&1" ) != 0; -} - -my @children ; - -$SIG{ 'INT' } = \&cleanup ; - -if ( $s ) { - - $ssfe = 'ssfe' ; - $prompt = '-prompt Stem:' ; - $prompt2 = '-prompt Chat:' ; - $echo = 'console_echo=1' -} - -foreach $cmd ( split /\n/, <\fR" +Sets a prompt for the user input line. The default prompt is none, or +``> '' if -print is specified. + +.SH ARGUMENTS +.TP +.BR "program [options]" +Names the program that +.B ssfe +should run. + +.SH KEYS +.B ssfe +understands these keys (^ means Control): +.TP +.BR "^\e" +Interrupt ssfe and whatever program it's running, and exit back to the unix +prompt. +.TP +.BR "^a" +Go to the beginning of the line. +.TP +.BR "^b, left arrow" +Move left a letter. +.TP +.BR "^c" +Interrupt: ignored by the front-end, can be used to interrupt connecting to a +server, with sirc. +.TP +.BR "^d" +Delete the character under the cursor. +.TP +.BR "^e" +Go to the end of the line. +.TP +.BR "^f, right arrow" +Move right a letter. +.TP +.BR "^h, DEL" +Erase the previous character. +.TP +.BR "^i, TAB" +Go to next /msg in msg history. +.TP +.BR "^j, ^m, Enter" +.TP +.BR "^k" +Erase from the cursor to the end of the line. +.TP +.BR "^l" +Redisplay the status bar and the command line. +.TP +.BR "^n, down arrow" +Go to the next line in command-line history. +.TP +.BR "^o" +With sirc, type the last msg you got on the command line +.TP +.BR "^p, up arrow" +Go to the previous line in command-line history. +.TP +.BR "^t" +With sirc, switch to the next channel you're on. +.TP +.BR "^u" +Erase command-line. +.TP +.BR "^v" +Insert the next character literally, even if it's a ^something. +.TP +.BR "^x b" +Toggle beep on or off (off by default). +.TP +.BR "^x c" +Exit the front end, back to the unix prompt. +.TP +.BR "^x h" +Toggle hold mode. +.TP +.BR "^x i" +Toggle irc-mode (^b^v^_ handling) on and off. +.TP +.BR "^y" +Yank the current line in the history without sending it. +.TP +.BR "^z" +Suspend ssfe and sirc and go back to the unix prompt - you come back with 'fg'. + +.SH COPYING +.B ssfe +is free software. You can redistribute it and/or modify it under the GNU +General Public License as published by the Free Software Foundation. See +the file LICENSE for details. + +.SH SEE ALSO +.BR sirc (1) + +.SH ENVIRONMENT VARIABLES +.TP +.BR TERM +sets the terminal type. The terminal needs to be able to set a scrolling +zone for ssfe to work. + +.SH BUGS +None known, please report to the author. + +.SH AUTHOR +.B sirc +was written by Roger Espel Llima . + diff --git a/extras/ssfe.c b/extras/ssfe.c new file mode 100644 index 0000000..b37f1dc --- /dev/null +++ b/extras/ssfe.c @@ -0,0 +1,1314 @@ +/* An ircII-like split-screen front end + Copyright (C) 1995 Roger Espel Llima + + Started: 17 Feb 95 by orabidoo + Latest modification: 7 June 97 + + To compile: gcc ssfe.c -o ssfe -ltermcap + + If it doesn't work, try gcc ssfe.c -o ssfe -lcurses + or try cc, acc or c89 instead of gcc, or -lncurses. + + Use: ssfe [options] program arguments + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation. See the file LICENSE for details. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef USE_SGTTY +#include +#else +#include +#endif + +#include + +#ifdef _AIX +#include +#endif + +#define BUF_SIZE 512 +#define MAX_COLS 512 + +unsigned char *statusline; +int ystatus, yinput; /* line number of the status line, input line */ + +int ttyfd; +#ifdef TIOCGWINSZ +struct winsize wsz; +#endif + +#ifdef USE_SGTTY +struct sgttyb term, term0; +struct tchars tch, tch0; +struct ltchars lch, lch0; +#else +struct termios term, term0; +#endif + +int pid, mypid; +int i; +int cols, lines; +int readfd, writefd, errfd; + +unsigned char *t, *w; +unsigned char tmpstr[BUF_SIZE], extrainput[BUF_SIZE+20], readbuf[2*BUF_SIZE], + *input, *writebuf, o_buffer[BUF_SIZE]; +int bold=0, inv=0, under=0, wherex=0, wherey=0, donl=0; +int hold_mode=0, hold_lines=0, ctrlx=0, beep=0, flow=0; + +unsigned char defprompt[]="> ", + nullstring[]="", + *prompt; +int plen=0, specialprompt=0, modified=1, no_echo=0; + +#define MAX_TAB_LINES 20 +struct tabinfo { + unsigned char string[BUF_SIZE]; + struct tabinfo *prev, *next; +}; +int tablines=0; +struct tabinfo *curtabt=NULL, *curtabr=NULL, *oldest=NULL; + +#define MAX_HIST_LINES 50 +struct histinfo { + unsigned char string[BUF_SIZE+20]; + int len, plen; + struct histinfo *prev, *next; +}; +int histlines=0; +struct histinfo *histcurrent=NULL, *histoldest=NULL; + +char ctrl_t[128] = "/next\n"; + +unsigned char id[]="`#ssfe#", *inid=id, protcmd[BUF_SIZE], *wpc=protcmd; +int idstatus=0; /* 0 looking for/in the word, 1 in the arguments */ +#define ID_BACK "@ssfe@" + +int rc, rrc, inputcursor, inputlast, inputofs, inarrow=0, quote=0; +int cursorwhere; /* 0 = up, 1 = down, 2 = undef */ +int dispmode=1; /* 0=raw, 1=wordwrap, 2=process ^b^v^_ */ +int printmode=0; +int cutline=0; + +char *termtype, termcap[1024], *tc, capabilities[2048]; +char *t_cm, *t_cl, *t_mr, *t_md, *t_me, *t_cs, *t_ce, *t_us; +int ansi_cs = 0; + +fd_set ready, result; +extern int errno; + +#ifdef __GNUC__ +extern unsigned char *tgoto(unsigned char *cm, int col, int line); +#else +extern unsigned char *tgoto(); +#endif + +#ifdef __GNUC__ +int myputchar(int c) { +#else +int myputchar(c) { +#endif + unsigned char cc=(unsigned char)c; + return(write(1, &cc, 1)); +} + +#ifdef __GNUC__ +int addchar(int c) { +#else +int addchar(c) { +#endif + (*w++)=(unsigned char)c; +} + +#ifdef __GNUC__ +void putcap(unsigned char *s) { +#else +void putcap(s) +unsigned char *s; { +#endif + tputs(s, 0, myputchar); +} + +#ifdef __GNUC__ +int do_cs(int y1, int y2) { +#else +int do_cs(y1, y2) { +#endif + static char temp[16]; + if (ansi_cs) { + sprintf(temp, "\e[%d;%dr", y1, y2); + write(1, temp, strlen(temp)); + } else putcap((char *)tgoto(t_cs, y2-1, y1-1)); +} + +#ifdef __GNUC__ +void writecap(unsigned char *s) { +#else +void writecap(s) +unsigned char *s; { +#endif + tputs(s, 0, addchar); +} + +#ifdef __GNUC__ +void gotoxy(int x, int y) { +#else +void gotoxy(x, y) { +#endif +/* left upper = 0, 0 */ + putcap(tgoto(t_cm, x, y)); +} + +#define clearscreen() (putcap(t_cl)) +#define cleareol() (putcap(t_ce)) +#define fullscroll() (do_cs(0, 0)) +#define winscroll() (do_cs(1, lines-2)) +#define setbold() (putcap(t_md)) +#define setunder() (putcap(t_us)) +#define setinv() (putcap(t_mr)) +#define normal() (putcap(t_me)) + +#ifdef __GNUC__ +void ofsredisplay(int x); +void inschar(unsigned char t); +void dokbdchar(unsigned char t); +#else +void ofsredisplay(); +void inschar(); +void dokbdchar(); +#endif +void displaystatus(); + +#ifdef __GNUC__ +void cleanupexit(int n, unsigned char *error) { +#else +void cleanupexit(n, error) +int n; +unsigned char *error; { +#endif + normal(); + fullscroll(); + gotoxy(0, lines-1); + cleareol(); +#ifdef USE_SGTTY + ioctl(ttyfd, TIOCSETP, &term0); + ioctl(ttyfd, TIOCSETC, &tch0); + ioctl(ttyfd, TIOCSLTC, &lch0); +#else + tcsetattr(ttyfd, TCSADRAIN, &term0); +#endif + close(ttyfd); + if (error!=NULL) + fprintf(stderr, "%s\n", error); + exit(n); +} + +void allsigs(); + +void interrupted() { + cleanupexit(1, "interrupted"); +} + +void sigpipe() { + cleanupexit(1, "program died"); +} + +void sigcont() { + allsigs(); +#ifdef USE_SGTTY + ioctl(ttyfd, TIOCSETP, &term); + ioctl(ttyfd, TIOCSETC, &tch); + ioctl(ttyfd, TIOCSLTC, &lch); +#else + tcsetattr(ttyfd, TCSANOW, &term); +#endif + wherex=0; + wherey=ystatus-1; + displaystatus(); + ofsredisplay(0); +} + +void suspend() { + normal(); + fullscroll(); + gotoxy(0, ystatus); + cleareol(); +#ifdef USE_SGTTY + ioctl(ttyfd, TIOCSETP, &term0); + ioctl(ttyfd, TIOCSETC, &tch0); + ioctl(ttyfd, TIOCSLTC, &lch0); +#else + tcsetattr(ttyfd, TCSANOW, &term0); +#endif + kill(pid, SIGCONT); + signal(SIGTSTP, SIG_DFL); + signal(SIGCONT, sigcont); + kill(mypid, SIGTSTP); +} + +void sigwinch() { +#ifdef TIOCGWINSZ + signal(SIGWINCH, sigwinch); + if (ioctl(ttyfd, TIOCGWINSZ, &wsz)>=0 && wsz.ws_row>0 && wsz.ws_col>0) { + lines=wsz.ws_row; + cols=wsz.ws_col; + cursorwhere=2; + ystatus=lines-2; + yinput=lines-1; + wherex=0; + wherey=ystatus-1; + displaystatus(); + if (inputlast>cols-8) { + inputcursor=cols-9; + inputofs=inputlast-cols+9; + } else { + inputofs=0; + inputcursor=inputlast; + } + ofsredisplay(0); + } +#endif +} + +void allsigs() { + signal(SIGHUP, interrupted); + signal(SIGINT, interrupted); + signal(SIGQUIT, SIG_IGN); + signal(SIGPIPE, sigpipe); + signal(SIGTSTP, suspend); + signal(SIGCONT, sigcont); +#ifdef TIOCGWINSZ + signal(SIGWINCH, sigwinch); +#endif +} + +#ifdef __GNUC__ +void setstatus(unsigned char *title) { +#else +void setstatus(title) +unsigned char *title; { +#endif + unsigned char *t=title; + for (;*t;t++) if (*t<' ') (*t)+='@'; + memset(statusline, ' ', MAX_COLS-1); + memcpy(statusline, title, strlen(title)='a' && *s<='z')?(*s)-32:*s)== + ((*t>='a' && *t<='z')?(*t)-32:*t)) { + if (*s=='\0') return 1; + s++; t++; + } + return 0; +} + +#ifdef __GNUC__ +void addtab(unsigned char *line) { +#else +void addtab(line) +unsigned char *line; { +#endif + struct tabinfo *nt; + + nt=oldest; + if (tablines) do { + if (casecmp(nt->string, line)) { + strcpy(nt->string, line); + if (nt==oldest) oldest=nt->prev; + else { + nt->prev->next=nt->next; + nt->next->prev=nt->prev; + nt->prev=oldest; + nt->next=oldest->next; + oldest->next=nt; + nt->next->prev=nt; + } + curtabt=oldest->next; + curtabr=oldest; + return; + } + nt=nt->next; + } while (nt!=oldest); + + if (!tablines) { + nt=(struct tabinfo *)malloc(sizeof (struct tabinfo)); + nt->prev=nt->next=curtabt=curtabr=oldest=nt; + tablines++; + } else if (tablinesprev=oldest; + nt->next=oldest->next; + oldest->next=nt; + nt->next->prev=nt; + tablines++; + } else { + nt=oldest; + oldest=nt->prev; + } + strcpy(nt->string, line); + oldest=nt->prev; + curtabt=oldest->next; + curtabr=oldest; +} + +void doprotcommand() { + unsigned char *tmp; + + switch (protcmd[0]) { + case 'i' : dispmode=2; /* set irc mode, ack */ + bold=inv=under=0; + write(writefd, "@ssfe@i\n", 8); + break; + case 'c' : dispmode=1; /* set cooked mode, ack */ + write(writefd, "@ssfe@c\n", 8); + break; + case 's' : setstatus(protcmd+1); /* set status */ + displaystatus(); + break; + case 'T' : strncpy(ctrl_t, protcmd+1, 127); /* set ^t's text */ + ctrl_t[126] = '\0'; + strcat(ctrl_t, "\n"); + break; + case 't' : addtab(protcmd+1); /* add tabkey entry */ + break; + case 'l' : fullscroll(); /* clear screen */ + normal(); + clearscreen(); + bold=inv=under=wherex=wherey=donl=0; + displaystatus(); + ofsredisplay(0); + break; + + case 'P' : no_echo = 1; /* password prompt */ + case 'p' : if (strlen(protcmd+1)<=8) { /* prompt something */ + fullscroll(); + if (!specialprompt) { + histcurrent->len=inputlast; + histcurrent->plen=plen; + } + input=extrainput; + strcpy(input, protcmd+1); + plen=strlen(input); + inputofs=0; + modified=specialprompt=1; + inputlast=inputcursor=plen; + ofsredisplay(0); + } + break; + case 'n' : if (cursorwhere!=1) { /* type text */ + normal(); + fullscroll(); + gotoxy(inputcursor, yinput); + cursorwhere=1; + } + for (tmp=protcmd+1; *tmp; tmp++) { + inschar(*tmp); + } + break; + case 'o' : strcpy(o_buffer, protcmd+1); + break; + } +} + +void newline() { + unsigned char t; + hold_lines++; + if (hold_mode && hold_lines>lines-4) { + normal(); + fullscroll(); + gotoxy(cols-4, ystatus); + setinv(); + write(1, "(H)", 3); + while(1) { + read(0, &t, 1); + if (t==9) break; + dokbdchar(t); + } + normal(); + fullscroll(); + gotoxy(cols-4, ystatus); + setinv(); + write(1, "(h)", 3); + hold_lines=0; + normal(); + winscroll(); + gotoxy(cols-1, wherey); + if (bold) setbold(); + if (under) setunder(); + if (inv) setinv(); + } +} + +#ifdef __GNUC__ +void formatter(unsigned char *readbuf, int rc) { +#else +void formatter(readbuf, rc) +unsigned char *readbuf; +int rc; { +#endif + + unsigned char t, *r, *lwr, *lww; + int lwrc, lwbold, lwunder, lwinv, lwx; + + if (cursorwhere!=0) { + winscroll(); + gotoxy(wherex, wherey); + cursorwhere=0; + } + if (donl) { + newline(); + write(1, "\r\n", 2); + normal(); + wherex=0; + bold=inv=under=lwbold=lwinv=lwunder=0; + if (wherey1) { + if (bold) setbold(); + if (under) setunder(); + if (inv) setinv(); + lwbold=bold; + lwinv=inv; + lwunder=under; + } + if (rc && readbuf[rc-1]=='\n') { + rc--; + donl=1; cutline=0; + } else { + donl=0; + if (dispmode==0) cutline=1; + } + if (dispmode==0) { + if (rc) write(1, readbuf, rc); + normal(); + return; + } + lww=w=writebuf; + lwr=r=readbuf; + lwrc=rc; + lwx=wherex; + while(rc-->0) { + t=(*r++); + if (t=='\r') continue; + if (wherex>cols-2 || (t==9 && wherex>(cols-2)&0xfff8)) { + if (t==' ' || t==9) ; + else if (lww>writebuf+cols/2) { + wherex=lwx; r=lwr; w=lww; rc=lwrc; + bold=lwbold; inv=lwinv; under=lwunder; wherex=lwx; + } else { + rc++; r--; + } + write(1, writebuf, w-writebuf); + newline(); + write(1, "\r\n ", 13); + w=writebuf; + lwr=r; lww=w; lwrc=rc; + lwbold=bold; lwinv=inv; lwunder=under; + lwx=wherex=11; + if (wherey1 && + ((t==2 && bold) || (t==22 && inv) || (t==31 && under))) { + writecap(t_me); + bold=under=inv=0; + } else if (dispmode>1 && t==2) { + writecap(t_md); + bold=1; + } else if (dispmode>1 && t==22) { + writecap(t_mr); + inv=1; + } else if (dispmode>1 && t==31) { + writecap(t_us); + under=1; + } else if (dispmode>1 && t==15) { + if (bold || inv || under) writecap(t_me); + bold=under=inv=0; + } else if (t==9) { + (*w++)=t; + wherex=(wherex & 0xfff8)+8; + } else if (t<' ' && (t!=7 || !beep)) { + wherex++; + if (inv) { + writecap(t_me); + (*w++)=(t+'@'); + } else { + writecap(t_mr); + (*w++)=(t+'@'); + writecap(t_me); + } + if (bold) writecap(t_md); + if (inv) writecap(t_mr); + if (under) writecap(t_us); + } else { + if (t!=7) wherex++; + (*w++)=t; + } + if (t==' ' || t==9) { + lwr=r; lww=w; lwrc=rc; + lwbold=bold; lwinv=inv; lwunder=under; + lwx=wherex; + } + } + if (w!=writebuf) write(1, writebuf, w-writebuf); +} + +#ifdef __GNUC__ +void doprogramline(unsigned char *readbuf, int rc) { +#else +void doprogramline(readbuf, rc) +unsigned char *readbuf; +int rc; { +#endif + + unsigned char *w, *r, *r2, t; + if (dispmode==0) { + formatter(readbuf, rc); + return; + } + w=r=readbuf; + while(rc-->0) { + t=(*r++); + if (idstatus==0) + if (*inid=='\0') { + idstatus=1; + wpc=protcmd; + inid=id; + } else if (*inid==t && (inid!=id || r==(readbuf+1) || *(r-2)=='\n')) { + inid++; + (*wpc++)=t; + } else { + r2=protcmd; + while (r2!=wpc) (*w++)=(*r2++); + (*w++)=t; + wpc=protcmd; + inid=id; + } + if (idstatus==1) + if (t=='\n') { + *wpc='\0'; + doprotcommand(); + inid=id; + wpc=protcmd; + idstatus=0; + } else (*wpc++)=t; + } + if (w!=readbuf) formatter(readbuf, w-readbuf); +} + +#ifdef __GNUC__ +void write1(unsigned char t, int pos) { +#else +void write1(t, pos) +unsigned char t; +int pos; { +#endif + if (no_echo && pos>=plen) { + write(1, "*", 1); + } else if (t>=' ') + write(1, &t, 1); + else { + setinv(); + t+='@'; + write(1, &t, 1); + normal(); + } +} + +#ifdef __GNUC__ +void ofsredisplay(int x) { +#else +void ofsredisplay(x) { +#endif +/* redisplays starting at x */ + unsigned char *w; + int i; + gotoxy(x, yinput); + if (inputlast-inputofs>=x) { + i=((inputlast-inputofs>cols-1 ? cols-1-x : inputlast-inputofs-x)); + for (w=input+inputofs+x; i--; w++) write1(*w, w-input); + } + cleareol(); + gotoxy(inputcursor, yinput); + cursorwhere=1; +} + +#ifdef __GNUC__ +void delempty(struct histinfo *leavealone) { +#else +void delempty(leavealone) +struct histinfo *leavealone; { +#endif + struct histinfo *h, *h2; + int cont=0; + h=histoldest; + do { + cont=0; + if ((h->len<=h->plen) && (h!=leavealone)) { + histlines--; + h->next->prev=h->prev; + h->prev->next=h->next; + h2=h->prev; + free(h); + if (h==histoldest) { + histoldest=h2; + cont=1; + } + h=h2; + } else h=h->prev; + } while ((h!=histoldest || cont) && histlines>0); + if (!histlines) { + histoldest=NULL; + return; + } +} + +struct histinfo *makenew() { + struct histinfo *nh; + if (!histlines) { + nh=(struct histinfo *)malloc(sizeof (struct histinfo)); + nh->prev=nh->next=histoldest=nh; + histlines++; + } else if (histlinesprev=histoldest; + nh->next=histoldest->next; + histoldest->next=nh; + nh->next->prev=nh; + histlines++; + } else { + nh=histoldest; + histoldest=nh->prev; + } + return nh; +} + +#ifdef __GNUC__ +void sendline(int yank) { +#else +void sendline(yank) { +#endif + if (!specialprompt) { + histcurrent->len=inputlast; + histcurrent->plen=plen; + } + if (!yank) { + input[inputlast]='\n'; + if (printmode) formatter(input, inputlast+1); + if (write(writefd, input+plen, inputlast+1-plen)string; + strcpy(input, prompt); + plen=strlen(prompt); + inputofs=specialprompt=0; + modified=1; + inputcursor=inputlast=plen; + ofsredisplay(0); + no_echo=0; +} + +void modify() { + struct histinfo *h; + if (!modified) { + if (inputlast>plen) { + h=histcurrent; + delempty(h); + histcurrent=makenew(); + strcpy(histcurrent->string, h->string); + input=histcurrent->string; + } + modified=1; + } +} + +void fixpos() { + if (inputcursor<8 && inputofs>0) { + inputofs-=cols-16; + inputcursor+=cols-16; + if (inputofs<0) { + inputcursor+=inputofs; + inputofs=0; + } + ofsredisplay(0); + } else if (inputcursor>cols-8) { + inputofs+=cols-16; + inputcursor-=cols-16; + ofsredisplay(0); + } +} + +void reshow() { + if (inputlast>cols-8) { + inputcursor=cols-9; + inputofs=inputlast-cols+9; + } else { + inputofs=0; + inputcursor=inputlast; + } + ofsredisplay(0); +} + +#ifdef __GNUC__ +void inschar(unsigned char t) { +#else +void inschar(t) +unsigned char t; { +#endif + + unsigned char *tmp; + + if (inputlast=input+inputofs+inputcursor) + *(tmp+1)=(*tmp--); + input[inputofs+(inputcursor++)]=t; + inputlast++; + ofsredisplay(inputcursor-1); + } + fixpos(); + } +} + +#ifdef __GNUC__ +void dokbdchar(unsigned char t) { +#else +void dokbdchar(t) +unsigned char t; { +#endif + + unsigned char *tmp; + + if (inarrow==1) { + if (t=='[' || t=='O') { + inarrow++; + return; + } + inarrow=0; + } else if (inarrow==2) { + inarrow=0; + if (t=='D') t=2; + else if (t=='C') t=6; + else if (t=='A') t=16; + else if (t=='B') t=14; + else return; + } + if (ctrlx && !quote) { + ctrlx=0; + t|=0x20; + if (dispmode>0 && ((t=='h' && !hold_mode) || t=='y')) { + hold_mode=1; + hold_lines=0; + if (cursorwhere!=1) fullscroll(); + cursorwhere=2; + normal(); + gotoxy(cols-4, ystatus); + setinv(); + write(1, "(h)", 3); + normal(); + } else if (dispmode>0 && ((t=='h' && hold_mode) || t=='n')) { + hold_mode=0; + if (cursorwhere!=1) fullscroll(); + cursorwhere=2; + normal(); + gotoxy(cols-4, ystatus); + setinv(); + write(1, " ", 3); + normal(); + } else if (dispmode>0 && t=='i') { + dispmode=3-dispmode; + bold=inv=under=0; + } else if (dispmode>0 && t=='b') { + beep=!beep; + } else if (t=='c') cleanupexit(1, "exiting"); + return; + } + if (cutline) donl=1; + if (cursorwhere!=1) { + normal(); + fullscroll(); + gotoxy(inputcursor, yinput); + cursorwhere=1; + } + if (t==24 && !quote) { + ctrlx=1; + return; + } else ctrlx=0; + if (t==27 && !quote) { + inarrow=1; + } else if ((t==10 || t==13) && !quote) { /* return, newline */ + sendline(0); + if (tablines) { + curtabr=oldest; + curtabt=oldest->next; + } + } else if (t==25 && !quote) { /* ^y */ + if (!specialprompt) { + sendline(1); + if (tablines) { + curtabr=oldest; + curtabt=oldest->next; + } + } + } else if (t==21 && !quote) { /* ^u */ + modify(); + input[plen]='\0'; + inputcursor=inputlast=plen; + inputofs=0; + ofsredisplay(0); + } else if ((t==8 || t==0x7f) && !quote) { /* ^h, ^? */ + if (inputcursor>plen) { + modify(); + tmp=input+inputcursor+inputofs; + while (tmp0 && (inputcursor>plen || inputofs>0)) { + gotoxy(--inputcursor, yinput); + fixpos(); + } + } else if (t==6 && !quote) { /* ^f */ + if (inputcursor+inputofsplen) { + if (inputofs==0) + gotoxy((inputcursor=plen), yinput); + else { + inputofs=0; + inputcursor=plen; + ofsredisplay(0); + } + } + } else if (t==5 && !quote) { /* ^e */ + if (inputcursor+inputofscols-8) { + inputcursor=cols-9; + inputofs=inputlast-cols+9; + ofsredisplay(0); + } else { + inputofs=0; + inputcursor=inputlast; + ofsredisplay(0); + } + } + } else if (t==12 && !quote) { /* ^l */ + displaystatus(); + ofsredisplay(0); + } else if (t==9 && !quote) { /* TAB */ + if (tablines) { + modify(); + strcpy(input+plen, curtabt->string); + curtabr=curtabt->prev; + curtabt=curtabt->next; + inputlast=strlen(input); + reshow(); + } + } else if (t==18 && !quote) { /* ^r */ + if (tablines) { + modify(); + strcpy(input+plen, curtabr->string); + curtabt=curtabr->next; + curtabr=curtabr->prev; + inputlast=strlen(input); + reshow(); + } + } else if (t==16 && !quote) { /* ^p */ + if (histlines>1 && !specialprompt) { + histcurrent->plen=plen; + histcurrent->len=inputlast; + histcurrent=histcurrent->next; + plen=histcurrent->plen; + inputlast=histcurrent->len; + input=histcurrent->string; + modified=0; + reshow(); + } + } else if (t==14 && !quote) { /* ^n */ + if (histlines>1 && !specialprompt) { + histcurrent->plen=plen; + histcurrent->len=inputlast; + histcurrent=histcurrent->prev; + plen=histcurrent->plen; + inputlast=histcurrent->len; + input=histcurrent->string; + modified=0; + reshow(); + } + } else if (t==15 &&!quote) { /* ^o */ + if (strlen(o_buffer)) modify(); + for (tmp=o_buffer; *tmp; tmp++) inschar(*tmp); + } else if (t==20 && !quote) { /* ^t */ + write(writefd, ctrl_t, strlen(ctrl_t)); + } else if (t==22 && !quote) { /* ^v */ + quote++; + return; +#ifdef CONTROL_W + } else if (t==23 && !quote) { /* ^w */ + fullscroll(); + normal(); + clearscreen(); + bold=inv=under=wherex=wherey=donl=0; + displaystatus(); + ofsredisplay(0); +#endif + } else inschar(t); + quote=0; +} + +#ifdef __GNUC__ +void barf(unsigned char *m) { +#else +void barf(m) +unsigned char *m; { +#endif + fprintf(stderr, "%s\n", m); + exit(1); +} + +char *myname; + +void use() { + fprintf(stderr, "Use: %s [options] program [program's options]\n", myname); + fprintf(stderr, "Options are:\n"); + fprintf(stderr, " -raw, -cooked, -irc : set display mode\n"); + fprintf(stderr, " -print : print your input lines\n"); + fprintf(stderr, " -prompt : specify a command-line prompt\n"); + fprintf(stderr, " -hold : pause after each full screen (for cooked/irc mode)\n"); + fprintf(stderr, " -beep : let beeps through (for cooked/irc mode)\n"); + fprintf(stderr, " -flow : leave ^S/^Q alone for flow control\n"); + exit(1); +} + +#ifdef __GNUC__ +int main(int argc, char *argv[]) { +#else +int main(argc, argv) +int argc; +char *argv[]; { +#endif + + char *vr; + int pfds0[2], pfds1[2], pfds2[2]; + + myname=(*argv); + prompt=nullstring; + while (argc>1) { + if (strcmp(argv[1], "-raw")==0) { + dispmode=0; + argv++; argc--; + } else if (strcmp(argv[1], "-cooked")==0) { + dispmode=1; + argv++; argc--; + } else if (strcmp(argv[1], "-irc")==0) { + dispmode=2; + argv++; argc--; + } else if (strcmp(argv[1], "-hold")==0) { + hold_mode=1; + argv++; argc--; + } else if (strcmp(argv[1], "-print")==0) { + argv++; argc--; + if (prompt==nullstring) prompt=defprompt; + printmode=1; + } else if (strcmp(argv[1], "-beep")==0) { + beep=1; + argv++; argc--; + } else if (strcmp(argv[1], "-flow")==0) { + flow=1; + argv++; argc--; + } else if (strcmp(argv[1], "-prompt")==0) { + if (argc>2) prompt=(unsigned char *)argv[2]; + if (strlen(prompt)>8) barf("Prompt too long"); + argv+=2; argc-=2; + } else break; + } + if (argc<2) use(); + if (!isatty(0)) barf("I can only run on a tty, sorry"); + if ((termtype=getenv("TERM"))==NULL) barf("No terminal type set"); + if (tgetent(termcap, termtype)<1) barf("No termcap info for your terminal"); + tc=capabilities; + if ((t_cm=(char *)tgetstr("cm", &tc))==NULL) + barf("Can't find a way to move the cursor around with your terminal"); + if ((t_cl=(char *)tgetstr("cl", &tc))==NULL) + barf("Can't find a way to clear the screen with your terminal"); + if ((t_ce=(char *)tgetstr("ce", &tc))==NULL) + barf("Can't find a way to clear to end of line with your terminal"); + if ((t_cs=(char *)tgetstr("cs", &tc))==NULL) { + if (strncmp(termtype, "xterm", 5)==0 || strncmp(termtype, "vt100", 5)==0) + ansi_cs=1; + else + barf("Can't find a way to set the scrolling region with your terminal"); + } + if ((t_me=(char *)tgetstr("me", &tc))!=NULL) { + if ((t_mr=(char *)tgetstr("mr", &tc))==NULL) t_mr=t_me; + if ((t_md=(char *)tgetstr("md", &tc))==NULL) t_md=t_me; + if ((t_us=(char *)tgetstr("us", &tc))==NULL) t_us=t_me; + } else if ((t_me=(char *)tgetstr("se", &tc))!=NULL && + (t_mr=(char *)tgetstr("so", &tc))!=NULL) { + t_md=t_mr; + t_us=tc; + (*tc++)='\0'; + } else { + t_me=t_md=t_mr=t_us=tc; + (*tc++)='\0'; + } + +/* + if ((ttyfd=open("/dev/tty", O_RDWR))<0 && + (ttyfd=open("/dev/tty", O_RDONLY))<0) barf("Can't open terminal!"); + */ + ttyfd = 0; + +#ifdef TIOCGWINSZ + if (ioctl(ttyfd, TIOCGWINSZ, &wsz)<0 || wsz.ws_row<1 || wsz.ws_col<1) { +#endif + lines=((vr=getenv("LINES"))?atoi(vr):0); + cols=((vr=getenv("COLUMNS"))?atoi(vr):0); + if (lines<1 || cols<1) { + if ((lines=tgetnum("li"))<1 || (cols=tgetnum("co"))<1) { + lines=24; cols=80; + } + } +#ifdef TIOCGWINSZ + } else { + lines=wsz.ws_row; + cols=wsz.ws_col; + } +#endif + + if (pipe(pfds0)<0 || pipe(pfds1)<0 || pipe(pfds2)<0) { + perror("pipe"); + exit(1); + } + mypid=getpid(); + switch (pid=fork()) { + case -1: + perror("fork"); + exit(1); + case 0: + if (pfds0[0]!=0) dup2(pfds0[0], 0); + if (pfds1[1]!=1) dup2(pfds1[1], 1); + if (pfds2[1]!=2) dup2(pfds2[1], 2); + if (pfds0[0]>2) close(pfds0[0]); + if (pfds0[1]>2) close(pfds0[1]); + if (pfds1[0]>2) close(pfds1[0]); + if (pfds1[1]>2) close(pfds1[1]); + if (pfds2[0]>2) close(pfds2[0]); + if (pfds2[1]>2) close(pfds2[1]); + /* okay we can read from 0 and write to 1 and 2, now.. it seems */ + execvp(argv[1], argv+1); + perror("exec"); + sleep(1); + exit(1); + default: + close(pfds0[0]); + close(pfds1[1]); + close(pfds2[1]); + readfd=pfds1[0]; + writefd=pfds0[1]; + errfd=pfds2[0]; + } + +#ifdef USE_SGTTY + + if (ioctl(ttyfd, TIOCGETP, &term)<0 || ioctl(ttyfd, TIOCGETC, &tch)<0 || + ioctl(ttyfd, TIOCGLTC, &lch)<0) { + perror("sgtty get ioctl"); + exit(1); + } + term0=term; + tch0=tch; + lch0=lch; + term.sg_flags|=CBREAK; + term.sg_flags&= ~ECHO & ~CRMOD; + + memset(&tch, -1, sizeof(tch)); + memset(&lch, -1, sizeof(lch)); + tch.t_intrc=(char)28; + tch.t_quitc=(char)3; + if (flow) { + tch.t_startc=(char)17; + tch.t_stopc=(char)19; + } + lch.t_suspc=(char)26; + + if (ioctl(ttyfd, TIOCSETP, &term)<0 || ioctl(ttyfd, TIOCSETC, &tch)<0 || + ioctl(ttyfd, TIOCSLTC, &lch)<0) { + perror("sgtty set ioctl"); + exit(1); + } + +#else + if (tcgetattr(ttyfd, &term)<0) { + perror("tcgetattr"); + exit(1); + } + term0=term; + + term.c_lflag &= ~ECHO & ~ICANON; + term.c_cc[VTIME]=(char)0; + term.c_cc[VMIN]=(char)1; + if (!flow) { + term.c_cc[VSTOP]=(char)0; + term.c_cc[VSTART]=(char)0; + } + term.c_cc[VQUIT]=(char)3; + term.c_cc[VINTR]=(char)28; /* reverse ^c and ^\ */ + term.c_cc[VSUSP]=(char)26; +#ifdef VREPRINT + term.c_cc[VREPRINT]=(char)0; +#endif +#ifdef VDISCARD + term.c_cc[VDISCARD]=(char)0; +#endif +#ifdef VLNEXT + term.c_cc[VLNEXT]=(char)0; +#endif +#ifdef VDSUSP + term.c_cc[VDSUSP]=(char)0; +#endif + + if (tcsetattr(ttyfd, TCSANOW, &term)<0) { + perror("tcsetattr"); + exit(1); + } +#endif + + allsigs(); + + ystatus=lines-2; + yinput=lines-1; + + if (lines>255) barf("Screen too big"); + if (ystatus<=2 || cols<20) barf("Screen too small"); + + statusline=(unsigned char *)malloc(MAX_COLS); + writebuf=(unsigned char *)malloc(20*BUF_SIZE); + strcpy(tmpstr, " "); + for (i=1; istring; + histcurrent->prev=histcurrent->next=histcurrent; + histlines=1; + plen=strlen(prompt); + inputlast=inputcursor=plen; + strcpy(input, prompt); + ofsredisplay(0); + *protcmd='\0'; + *o_buffer='\0'; + cursorwhere=1; + + FD_ZERO(&ready); + FD_SET(ttyfd, &ready); + FD_SET(readfd, &ready); + FD_SET(errfd, &ready); + + while(1) { + result=ready; + if (select(64, &result, NULL, NULL, NULL)<=0) + if (errno==EINTR) continue; + else cleanupexit(1, "select error"); + + if (FD_ISSET(readfd, &result)) + if ((rc=read(readfd, readbuf, BUF_SIZE))>0) + doprogramline(readbuf, rc); + else + cleanupexit(1, "program terminated"); + if (FD_ISSET(errfd, &result)) + if ((rc=read(errfd, readbuf, BUF_SIZE))>0) + doprogramline(readbuf, rc); + else + cleanupexit(1, "program terminated"); + if (FD_ISSET(ttyfd, &result)) + if ((rrc=read(0, readbuf, BUF_SIZE))>0) + for (t=readbuf; rrc>0; rrc--) dokbdchar(*(t++)); + else + cleanupexit(1, "read error from keyboard"); + } +} +