[alt.sources] pmlined 1.5 - the poor man's line discipline

brnstnd@kramden.acf.nyu.edu (Dan Bernstein) (11/04/90)

This is pmlined 1.5, the poor man's line discipline. Like every utility
in the poor man's series, pmlined has some feature missing from almost
all the competition: in this case, the ability to make your BSD system
feel like a good old IBM VM/CMS box.

pmlined lets you edit input lines on the status line of your terminal.
You can switch between this and the native tty mode with ^A. pmlined
should be easy to play with, unlike most line discipline implementations
running around. Feel free to add features.

New to this version of pmlined are cursor motion within the line and an
easy-to-use command history.

Send comments to me or to alt.sources.d.

---Dan

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  README pmlined.c termcap
# Wrapped by brnstnd@kramden on Sun Nov  4 09:37:56 1990
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'README' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'README'\"
else
echo shar: Extracting \"'README'\" \(2692 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
Xpmlined 1.5, 11/4/90.
XPlaced into the public domain by Daniel J. Bernstein.
XComments to him at brnstnd@nyu.edu.
X
X
XThis is pmlined, the poor man's line discipline.
X
Xpmlined provides a tty editing mode that should make former IBM users
Xfeel right at home. You edit the current line on the status line
X(through stderr). Long lines scroll horizontally. When you press return,
Xpmlined sends the line to stdout. All the usual editing characters,
Xincluding ^U, ^W, backspace, delete, and ^V, work normally. ^A switches
Xbetween this editing mode and the usual tty mode. In this version, you
Xalso get ^H and ^L to move the cursor back and forth within the line,
Xand a 20-line editable history with ^B and ^F.
X
XOf course, pmlined is also the smallest line discipline implementation
Xknown to man.
X
XOther than that, it's pretty poor. This version is just a prototype. It
Xcould be made much more efficient; there are a lot of features missing.
XFeel free to play with it, though. The code is short and (I hope) easy
Xto understand.
X
Xpmlined was originally called botwana. It was not named after a small
XAfrican country rumored to manufacture clone IBM 3270 terminals.
X
X
XRequirements:
X
X  A termcap entry with ``ts'', ``fs'', and ``ce''. Enclosed is a generic
X  VT termcap supporting the status line; you may have to rewrite your
X  termcap entry analogously. In this version, the entry must also have
X  ``us'' and ``ue'' for the cursor.
X
X  The pty program, for use as described below.
X
X
XTo use pmlined, type the following:
X
X  pmlined | pty $SHELL
X
XIt should feel just like a normal shell, except that you edit lines on
Xthe bottom of your screen before entering them. If you really are an IBM
Xprogrammer, you might feel more comfortable with a shell prompt of R;
Xfollowed by a newline. You may want to % alias ibm 'pmlined | pty'.
X
XA different use of pmlined is to make clean typescripts. If you have the
Xscript clone in the pty package,
X
X  pmlined | script
X
Xwill let you correct editing errors before they enter the script.
X  
X
XCaveats:
X
X  You have to type something after the final ^D, or pmlined will not
X  realize that the output has ended.
X
X  pmlined does not react automatically to changes in the tty mode. When
X  you run vi, for instance, you have to remember to type ^A to use
X  character-at-a-time mode. A somewhat less pure design could deal with
X  this automatically.
X
X  pmlined doesn't understand tabs.
X
X  pmlined's key bindings are hardwired.
X
X  pmlined doesn't have macros, or help, or variables, or file
X  completion, or signal characters, or anything else you'd expect out of
X  a fancy line discipline. But it's easy to extend. (The first version
X  didn't have a command history.)
X
X
XGood luck!
END_OF_FILE
if test 2692 -ne `wc -c <'README'`; then
    echo shar: \"'README'\" unpacked with wrong size!
fi
# end of 'README'
fi
if test -f 'pmlined.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'pmlined.c'\"
else
echo shar: Extracting \"'pmlined.c'\" \(6477 characters\)
sed "s/^X//" >'pmlined.c' <<'END_OF_FILE'
X#include <stdio.h>
X#include <signal.h>
X#include <curses.h>
X#include <strings.h>
X
X#define HISTLEN 20
X#define LINELEN 500
X#define PRLINELEN 1000
X
Xoutc(ch)
Xchar ch;
X{
X putc(ch,stderr);
X}
X
Xsigpipe()
X{
X close(0); /* guaranteeing that the next character we read will be an EOF */
X}
X
Xmain(argc,argv)
Xint argc;
Xchar *argv[];
X{
X char bp[1024];
X char ts[256];
X char fs[256];
X char ce[256];
X char us[256];
X char ue[256];
X char *cptr;
X char errbuf[BUFSIZ];
X char history[HISTLEN][LINELEN];
X char prhistory[HISTLEN][PRLINELEN];
X char ahistory[HISTLEN][LINELEN];
X char aprhistory[HISTLEN][PRLINELEN];
X int histpos = 0;
X int minhistpos = 0;
X int temphistpos;
X char *line;
X char *linepr;
X char *aline;
X char *alinepr;
X int ch;
X char *chpr;
X int x;
X int xpr;
X int y;
X int ypr;
X int flagcharmode = 0;
X int co;
X int i;
X
X signal(SIGPIPE,sigpipe);
X setbuf(stderr,errbuf);
X
X tgetent(bp,getenv("TERM"));
X cptr = ts;
X if (!tgetstr("ts",&cptr))
X   ; /*XXX*/
X cptr = fs;
X if (!tgetstr("fs",&cptr))
X   ; /*XXX*/
X cptr = ce;
X if (!tgetstr("ce",&cptr))
X   ; /*XXX*/
X cptr = us;
X if (!tgetstr("us",&cptr))
X   ; /*XXX*/
X cptr = ue;
X if (!tgetstr("ue",&cptr))
X   ; /*XXX*/
X co = tgetnum("co");
X#define TS tputs(ts,1,outc)
X#define CE tputs(ce,1,outc)
X#define FS tputs(fs,1,outc)
X#define US tputs(us,1,outc)
X#define UE tputs(ue,1,outc)
X
X for (;;)
X   if (flagcharmode)
X    {
X     if ((ch = getchar()) == EOF)
X       break;
X     if (ch == 0001)
X       flagcharmode = 0;
X     else
X      {
X       putchar(ch);
X       fflush(stdout);
X      }
X    }
X   else
X    {
X     line = history[histpos];
X     aline = ahistory[histpos];
X     linepr = prhistory[histpos];
X     alinepr = aprhistory[histpos];
X     temphistpos = histpos;
X     TS; CE; US; putc(' ',stderr); UE; FS; fflush(stderr);
X     x = xpr = 0;
X     line[x] = linepr[xpr] = '\0';
X     y = ypr = 0;
X     aline[y] = alinepr[ypr] = '\0';
X     while ((ch = getchar()) != '\n')
X      {
X       if ((temphistpos != histpos) && (ch != 2) && (ch != 6))
X	{
X	 strcpy(history[histpos],line); line = history[histpos];
X	 strcpy(ahistory[histpos],aline); aline = ahistory[histpos];
X	 strcpy(prhistory[histpos],linepr); linepr = prhistory[histpos];
X	 strcpy(aprhistory[histpos],alinepr); alinepr = aprhistory[histpos];
X	 temphistpos = histpos;
X	}
X       if (ch == 4)
X	 break;
X       if (ch == EOF)
X	 break;
X       if (ch == '\r')
X	{
X	 ch = '\n';
X	 break;
X	}
X       if (ch == 1)
X	{
X	 flagcharmode = 1;
X	 break;
X	}
X       switch(ch)
X        {
X	 case 6: if (temphistpos != histpos)
X		  {
X		   temphistpos++;
X		   if (temphistpos == HISTLEN)
X		     temphistpos = 0;
X		  }
X                 line = history[temphistpos];
X                 aline = ahistory[temphistpos];
X                 linepr = prhistory[temphistpos];
X                 alinepr = aprhistory[temphistpos];
X		 x = strlen(line);
X		 y = strlen(aline);
X		 xpr = strlen(linepr);
X		 ypr = strlen(alinepr);
X		 break;
X	 case 2: if (temphistpos != minhistpos)
X		  {
X		   if (temphistpos == 0)
X		     temphistpos = HISTLEN;
X		   temphistpos--;
X		  }
X                 line = history[temphistpos];
X                 aline = ahistory[temphistpos];
X                 linepr = prhistory[temphistpos];
X                 alinepr = aprhistory[temphistpos];
X		 x = strlen(line);
X		 y = strlen(aline);
X		 xpr = strlen(linepr);
X		 ypr = strlen(alinepr);
X		 break;
X	 case 21: x = xpr = 0;
X                  line[x] = linepr[xpr] = '\0';
X		  break;
X	 case 23: while (x && (line[x - 1] == ' '))
X		   {
X		    x--;
X		    xpr--;
X		   }
X	          while (x && (line[x - 1] != ' '))
X		   {
X		    x--;
X		    chpr = unctrl(line[x]);
X		    while (*(chpr++))
X		      xpr--;
X		   }
X  		  linepr[xpr] = '\0';
X  		  line[x] = '\0';
X  		  break;
X         case '\b': if (!x)
X		      break;
X		    x--;
X		    aline[y] = line[x];
X		    y++;
X		    chpr = unctrl(line[x]);
X		    while (*(chpr++))
X		     {
X		      xpr--;
X		      alinepr[ypr] = linepr[xpr];
X		      ypr++;
X		     }
X		    linepr[xpr] = '\0';
X		    line[x] = '\0';
X		    alinepr[ypr] = '\0';
X		    aline[y] = '\0';
X		    break;
X	 case 12: if (!y)
X		    break;
X		  y--;
X		  line[x] = aline[y];
X		  x++;
X		  chpr = unctrl(aline[y]);
X		  while (*(chpr++))
X		   {
X		    ypr--;
X		    linepr[xpr] = alinepr[ypr];
X		    xpr++;
X		   }
X		  linepr[xpr] = '\0';
X		  line[x] = '\0';
X		  alinepr[ypr] = '\0';
X		  aline[y] = '\0';
X		  break;
X         case 127: if (!x)
X		     break;
X		   x--;
X  		   chpr = unctrl(line[x]);
X  		   while (*(chpr++))
X  		     xpr--;
X  		   linepr[xpr] = '\0';
X  		   line[x] = '\0';
X  		   break;
X	 case 22: ch = getchar();
X	 case 0: ; /*XXX*/
X         default: if ((x == LINELEN - 1) || (xpr == PRLINELEN - 1))
X		   {
X		    putchar(7);
X		    break;
X		   }
X		  line[x++] = ch;
X  		  line[x] = '\0';
X  		  chpr = unctrl(ch);
X  		  while(*chpr)
X  		    linepr[xpr++] = *(chpr++);
X  		  linepr[xpr] = '\0';
X        }
X       TS;
X       if (ypr)
X	{
X	 if (xpr > co - 7)
X	  {
X	   fputs("<- ",stderr); fputs(linepr + xpr - co + 10,stderr);
X	   US; putc(aline[ypr - 1],stderr); UE;
X	   if (ypr != 1)
X	    {
X	     putc(' ',stderr); putc('-',stderr); putc('>',stderr);
X	    }
X          }
X	 else
X	  {
X	   fputs(linepr,stderr);
X	   US; putc(aline[ypr - 1],stderr); UE;
X           for (i = ypr - 2;(i >= 0) && (i >= xpr + ypr - co + 6);i--)
X	     putc(aline[i],stderr);
X	   if (i != -1)
X	    {
X	     putc(' ',stderr); putc('-',stderr); putc('>',stderr);
X	    }
X	  }
X	}
X       else
X	{
X         if (xpr > co - 6)
X	  { fputs("<- ",stderr); fputs(linepr + xpr - co + 9,stderr); }
X         else
X	  { fputs(linepr,stderr); } 
X	 US; putc(' ',stderr); UE;
X	}
X       CE; FS; fflush(stderr);
X      }
X     if ((ch == EOF) || (ch == 4))
X      {
X       for (i = 0;i < x;i++)
X	 putchar(line[i]);
X       for (i = y - 1;i >= 0;i--)
X	 putchar(aline[i]);
X       putchar(4);
X       fflush(stdout);
X       if (ch == EOF)
X	 break;
X       /* On a normal ^D, we won't die until after our output does. */
X      }
X     else if (ch == 1)
X      {
X       for (i = 0;i < x;i++)
X	 putchar(line[i]);
X       for (i = y - 1;i >= 0;i--)
X	 putchar(aline[i]);
X       fflush(stdout);
X       TS; CE; FS; fflush(stderr);
X      }
X     else
X      {
X       for (i = 0;i < x;i++)
X	 putchar(line[i]);
X       for (i = y - 1;i >= 0;i--)
X	 putchar(aline[i]);
X       putchar('\n');
X       fflush(stdout);
X      }
X     histpos++;
X     if (histpos == HISTLEN)
X       histpos = 0;
X     if (histpos == minhistpos)
X      {
X       minhistpos++;
X       if (minhistpos == HISTLEN)
X	 minhistpos = 0;
X      }
X    }
X TS; CE; FS; fflush(stderr);
X exit(0);
X}
END_OF_FILE
if test 6477 -ne `wc -c <'pmlined.c'`; then
    echo shar: \"'pmlined.c'\" unpacked with wrong size!
fi
# end of 'pmlined.c'
fi
if test -f 'termcap' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'termcap'\"
else
echo shar: Extracting \"'termcap'\" \(466 characters\)
sed "s/^X//" >'termcap' <<'END_OF_FILE'
Xv4|vt102-24|DEC vt102 with 24 lines:.al=\E[L:.am:bl=^G:bs:cd=50\E[J:ce=3\E[K:cl=50\E[;H\E[2J:cm=5\E[%i%2;%2H:co#80:cs=5\E[%i%d;%dr:dc=2\E[P:.dl=\E[M:do=3\E[B:ds=3\E[K:es:fs=\E8:hs:le=2\E[D:li#23:me=2\E[m:nd=2\E[C:pt:Rc=\E8:Sc=\E7:se=2\E[m:sf=2^J:sg=0:so=2\E[1m:sr=5\EM:ta=^I:ts=5\E7\E[?6h\E[24;0H:ue=2\E[m:ug=0:up=2\E[A:us=2\E[4m:is=\E7\E[1;23r\E>\E[?1;3;4;5l\E[?6;7;8h\E8:if=/usr/lib/tabset/vt100:rs=\E7\E[1;23r\E>\E[?1;3;4;5l\E[?6;7;8h\E8:ho=5\E[;H:cr=^M:ic=\E[@:
END_OF_FILE
if test 466 -ne `wc -c <'termcap'`; then
    echo shar: \"'termcap'\" unpacked with wrong size!
fi
# end of 'termcap'
fi
echo shar: End of shell archive.
exit 0