Added ssfe.c
authorFrancois Fleuret <francois@fleuret.org>
Fri, 29 May 2009 15:17:01 +0000 (17:17 +0200)
committerFrancois Fleuret <francois@fleuret.org>
Fri, 29 May 2009 15:17:01 +0000 (17:17 +0200)
ssfe.c [new file with mode: 0644]

diff --git a/ssfe.c b/ssfe.c
new file mode 100644 (file)
index 0000000..b37f1dc
--- /dev/null
+++ b/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 <roger.espel.llima@ens.fr>
+   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 <sys/time.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+
+#ifdef USE_SGTTY
+#include <sgtty.h>
+#else
+#include <termios.h>
+#endif
+
+#include <sys/ioctl.h>
+
+#ifdef _AIX
+#include <sys/select.h>
+#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)<MAX_COLS ? strlen(title) : MAX_COLS);
+}
+
+void displaystatus() {
+  normal();
+  fullscroll();
+  gotoxy(0, ystatus);
+  setinv();
+  write(1, statusline, cols-1);
+  if (hold_mode) {
+    gotoxy(cols-4, ystatus);
+    write(1, "(h)", 3);
+  }
+  cursorwhere=2;
+  normal();
+  cleareol();
+}
+
+#ifdef __GNUC__
+int casecmp(unsigned char *s, unsigned char *t) {
+#else
+int casecmp(s, t)
+unsigned char *s, *t; {
+#endif
+  while (((*s>='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 (tablines<MAX_TAB_LINES) {
+    nt=(struct tabinfo *)malloc(sizeof (struct tabinfo));
+    nt->prev=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 (wherey<ystatus-1) wherey++;
+  } else if (dispmode>1) {
+    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 (wherey<ystatus-1) wherey++;
+      rc--; t=(*r++);
+    }
+    if (t=='\n') {
+      if (w!=writebuf) write(1, writebuf, w-writebuf);
+      newline();
+      write(1, "\r\n", 2);
+      normal();
+      w=writebuf;
+      lwr=r; lww=w; lwrc=rc;
+      lwbold=bold=lwinv=inv=lwunder=under=lwx=wherex=0;
+      if (wherey<ystatus-1) wherey++;
+    } else if (dispmode>1 &&
+               ((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 (histlines<MAX_HIST_LINES) {
+    nh=(struct histinfo *)malloc(sizeof (struct histinfo));
+    nh->prev=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)<inputlast+1-plen)
+      cleanupexit(1, "write error");
+  }
+  input[inputlast]='\0';
+  delempty(NULL);
+  histcurrent=makenew();
+  input=histcurrent->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<BUF_SIZE-4) {
+    modify();
+    if (inputofs+inputcursor==inputlast) {
+      write1(t, inputlast);
+      input[inputlast++]=t;
+      input[inputlast]='\0';
+      inputcursor++;
+    } else {
+      tmp=input+inputlast;
+      while (tmp>=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 (tmp<input+inputlast)
+       *(tmp-1)=(*tmp++);
+      input[--inputlast]='\0';
+      gotoxy(--inputcursor, yinput);
+      ofsredisplay(inputcursor);
+      fixpos();
+    }
+  } else if (t==4 && !quote) {  /* ^d */
+    if (inputcursor+inputofs<inputlast) {
+      modify();
+      tmp=input+inputcursor+inputofs+1;
+      while (tmp<input+inputlast)
+       *(tmp-1)=(*tmp++);
+      input[--inputlast]='\0';
+      gotoxy(inputcursor, yinput);
+      ofsredisplay(inputcursor);
+    }
+  } else if (t==11 && !quote) {  /* ^k */
+    if (inputcursor+inputofs<inputlast) {
+      modify();
+      input[inputlast=inputofs+inputcursor]='\0';
+      ofsredisplay(inputcursor);
+    }
+  } else if (t==2 && !quote) {  /* ^b */
+    if (inputcursor>0 && (inputcursor>plen || inputofs>0)) {
+      gotoxy(--inputcursor, yinput);
+      fixpos();
+    }
+  } else if (t==6 && !quote) {  /* ^f */
+    if (inputcursor+inputofs<inputlast) {
+      gotoxy(++inputcursor, yinput);
+      fixpos();
+    }
+  } else if (t==1 && !quote) { /* ^a */
+    if (inputcursor+inputofs>plen) {
+      if (inputofs==0)
+       gotoxy((inputcursor=plen), yinput);
+      else {
+       inputofs=0;
+       inputcursor=plen;
+       ofsredisplay(0);
+      }
+    }
+  } else if (t==5 && !quote) { /* ^e */
+    if (inputcursor+inputofs<inputlast) {
+      if (inputlast-inputofs<cols-3) {
+       gotoxy((inputcursor=inputlast-inputofs), yinput);
+      } else if (inputlast>cols-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 <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; i<argc; i++)
+    if (strlen(tmpstr)+strlen(argv[i])<cols-1) {
+      strcat(tmpstr, argv[i]);
+      strcat(tmpstr, " ");
+    }
+  setstatus(tmpstr);
+
+  if (dispmode==0) wherey=ystatus-1;
+  clearscreen();
+  displaystatus();
+
+  histoldest=histcurrent=(struct histinfo *)malloc(sizeof (struct histinfo));
+  input=histcurrent->string;
+  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");
+  }
+}
+