[comp.sources.misc] v02i052: Unify TEXT fields from ACCELL Part 3/5

ustel@well.UUCP (Mark Hargrove) (02/11/88)

Comp.sources.misc: Volume 2, Issue 52
Submitted-By: "Mark Hargrove" <ustel@well.UUCP>
Archive-Name: accell-text/Part3

[Nice to know someone else posts Unify/Accell tools.  ++bsa]

#!/bin/sh
# to extract, remove the header and type "sh filename"
if `test ! -s ./cinfo.c`
then
echo "writing ./cinfo.c"
cat > ./cinfo.c << '\Rogue\Monster\'
/*
 *		Character class tables.
 * Do it yourself character classification
 * macros, that understand the multinational character set,
 * and let me ask some questions the standard macros (in
 * ctype.h) don't let you ask.
 */
#include	"def.h"

/*
 * This table, indexed by a character drawn
 * from the 256 member character set, is used by my
 * own character type macros to answer questions about the
 * type of a character. It handles the full multinational
 * character set, and lets me ask some questions that the
 * standard "ctype" macros cannot ask.
 */
char	cinfo[256] = {
	_C,		_C,		_C,		_C,	/* 0x0X	*/
	_C,		_C,		_C,		_C,
	_C,		_C,		_C,		_C,
	_C,		_C,		_C,		_C,
	_C,		_C,		_C,		_C,	/* 0x1X	*/
	_C,		_C,		_C,		_C,
	_C,		_C,		_C,		_C,
	_C,		_C,		_C,		_C,
	0,		_P,		0,		0,	/* 0x2X	*/
	_W,		_W,		0,		_W,
	0,		0,		0,		0,
	0,		0,		_P,		0,
	_W,		_W,		_W,		_W,	/* 0x3X	*/
	_W,		_W,		_W,		_W,
	_W,		_W,		0,		0,
	0,		0,		0,		_P,
	0,		_U|_W,		_U|_W,		_U|_W,	/* 0x4X	*/
	_U|_W,		_U|_W,		_U|_W,		_U|_W,
	_U|_W,		_U|_W,		_U|_W,		_U|_W,
	_U|_W,		_U|_W,		_U|_W,		_U|_W,
	_U|_W,		_U|_W,		_U|_W,		_U|_W,	/* 0x5X	*/
	_U|_W,		_U|_W,		_U|_W,		_U|_W,
	_U|_W,		_U|_W,		_U|_W,		0,
	0,		0,		0,		0,
	0,		_L|_W,		_L|_W,		_L|_W,	/* 0x6X	*/
	_L|_W,		_L|_W,		_L|_W,		_L|_W,
	_L|_W,		_L|_W,		_L|_W,		_L|_W,
	_L|_W,		_L|_W,		_L|_W,		_L|_W,
	_L|_W,		_L|_W,		_L|_W,		_L|_W,	/* 0x7X	*/
	_L|_W,		_L|_W,		_L|_W,		_L|_W,
	_L|_W,		_L|_W,		_L|_W,		0,
	0,		0,		0,		_C,
	0,		0,		0,		0,	/* 0x8X	*/
	0,		0,		0,		0,
	0,		0,		0,		0,
	0,		0,		0,		0,
	0,		0,		0,		0,	/* 0x9X	*/
	0,		0,		0,		0,
	0,		0,		0,		0,
	0,		0,		0,		0,
	0,		0,		0,		0,	/* 0xAX	*/
	0,		0,		0,		0,
	0,		0,		0,		0,
	0,		0,		0,		0,
	0,		0,		0,		0,	/* 0xBX	*/
	0,		0,		0,		0,
	0,		0,		0,		0,
	0,		0,		0,		0,
	_U|_W,		_U|_W,		_U|_W,		_U|_W,	/* 0xCX	*/
	_U|_W,		_U|_W,		_U|_W,		_U|_W,
	_U|_W,		_U|_W,		_U|_W,		_U|_W,
	_U|_W,		_U|_W,		_U|_W,		_U|_W,
	0,		_U|_W,		_U|_W,		_U|_W,	/* 0xDX	*/
	_U|_W,		_U|_W,		_U|_W,		_U|_W,
	_U|_W,		_U|_W,		_U|_W,		_U|_W,
	_U|_W,		_U|_W,		0,		_W,
	_L|_W,		_L|_W,		_L|_W,		_L|_W,	/* 0xEX	*/
	_L|_W,		_L|_W,		_L|_W,		_L|_W,
	_L|_W,		_L|_W,		_L|_W,		_L|_W,
	_L|_W,		_L|_W,		_L|_W,		_L|_W,
	0,		_L|_W,		_L|_W,		_L|_W,	/* 0xFX	*/
	_L|_W,		_L|_W,		_L|_W,		_L|_W,
	_L|_W,		_L|_W,		_L|_W,		_L|_W,
	_L|_W,		_L|_W,		0,		0
};

#ifdef __50SERIES
  /* Primos C blows up without this - wierd, really wierd... */
  static void dummy(){}
#endif
\Rogue\Monster\
else
  echo "will not over write ./cinfo.c"
fi
if `test ! -s ./display.c`
then
echo "writing ./display.c"
cat > ./display.c << '\Rogue\Monster\'
/*
 * The functions in this file handle redisplay. The
 * redisplay system knows almost nothing about the editing
 * process; the editing functions do, however, set some
 * hints to eliminate a lot of the grinding. There is more
 * that can be done; the "vtputc" interface is a real
 * pig. Two conditional compilation flags; the GOSLING
 * flag enables dynamic programming redisplay, using the
 * algorithm published by Jim Gosling in SIGOA. The MEMMAP
 * changes things around for memory mapped video. With
 * both off, the terminal is a VT52.
 */
#include	"def.h"

/*
 * You can change these back to the types
 * implied by the name if you get tight for space. If you
 * make both of them "int" you get better code on the VAX.
 * They do nothing if this is not Gosling redisplay, except
 * for change the size of a structure that isn't used.
 * A bit of a cheat.
 */
/* These defines really belong in sysdef.h */
#ifndef XCHAR
#  define	XCHAR	int
#  define	XSHORT	int
#endif

#ifdef	STANDOUT_GLITCH
extern int SG;				/* number of standout glitches	*/
#endif

/*
 * A video structure always holds
 * an array of characters whose length is equal to
 * the longest line possible. Only some of this is
 * used if "ncol" isn't the same as "NCOL".
 */
typedef	struct	{
	short	v_hash;			/* Hash code, for compares.	*/
	short	v_flag;			/* Flag word.			*/
	short	v_color;		/* Color of the line.		*/
	XSHORT	v_cost;			/* Cost of display.		*/
	char	v_text[NCOL];		/* The actual characters.	*/
}	VIDEO;

#define	VFCHG	0x0001			/* Changed.			*/
#define	VFHBAD	0x0002			/* Hash and cost are bad.	*/

/*
 * SCORE structures hold the optimal
 * trace trajectory, and the cost of redisplay, when
 * the dynamic programming redisplay code is used.
 * If no fancy redisplay, this isn't used. The trace index
 * fields can be "char", and the score a "short", but
 * this makes the code worse on the VAX.
 */
typedef	struct	{
	XCHAR	s_itrace;		/* "i" index for track back.	*/
	XCHAR	s_jtrace;		/* "j" index for trace back.	*/
	XSHORT	s_cost;			/* Display cost.		*/
}	SCORE;

int	sgarbf	= TRUE;			/* TRUE if screen is garbage.	*/
int	vtrow	= 0;			/* Virtual cursor row.		*/
int	vtcol	= 0;			/* Virtual cursor column.	*/
int	tthue	= CNONE;		/* Current color.		*/
int	ttrow	= HUGE;			/* Physical cursor row.		*/
int	ttcol	= HUGE;			/* Physical cursor column.	*/
int	tttop	= HUGE;			/* Top of scroll region.	*/
int	ttbot	= HUGE;			/* Bottom of scroll region.	*/

VIDEO	*vscreen[NROW-1];		/* Edge vector, virtual.	*/
VIDEO	*pscreen[NROW-1];		/* Edge vector, physical.	*/
VIDEO	video[2*(NROW-1)];		/* Actual screen data.		*/
VIDEO	blanks;				/* Blank line image.		*/

/*
 * Some predeclerations to make ANSI compilers happy
 */
VOID	vtinit();
VOID	vttidy();
VOID	vtmove();
VOID	vtputc();
VOID	vteeol();
VOID	update();
VOID	ucopy();
VOID	uline();
VOID	modeline();
VOID	hash();
VOID	setscores();
VOID	traceback();

#ifdef	GOSLING
/*
 * This matrix is written as an array because
 * we do funny things in the "setscores" routine, which
 * is very compute intensive, to make the subscripts go away.
 * It would be "SCORE	score[NROW][NROW]" in old speak.
 * Look at "setscores" to understand what is up.
 */
SCORE	score[NROW*NROW];
#endif

/*
 * Initialize the data structures used
 * by the display code. The edge vectors used
 * to access the screens are set up. The operating
 * system's terminal I/O channel is set up. Fill the
 * "blanks" array with ASCII blanks. The rest is done
 * at compile time. The original window is marked
 * as needing full update, and the physical screen
 * is marked as garbage, so all the right stuff happens
 * on the first call to redisplay.
 */
VOID
vtinit()
{
	register VIDEO	*vp;
	register int	i;

	ttopen();
	ttinit();
	vp = &video[0];
	for (i=0; i<NROW-1; ++i)
	{
		vscreen[i] = vp;
		++vp;
		pscreen[i] = vp;
		++vp;
	}
	blanks.v_color = CTEXT;
	for (i=0; i<NCOL; ++i)
		blanks.v_text[i] = ' ';
}

/*
 * Tidy up the virtual display system
 * in anticipation of a return back to the host
 * operating system. Right now all we do is position
 * the cursor to the last line, erase the line, and
 * close the terminal channel.
 */
VOID
vttidy()
{
	ttcolor(CTEXT);
	ttnowindow();				/* No scroll window.	*/
	ttmove(nrow-1, 0);			/* Echo line.		*/
	tteeol();
	tttidy();
	ttflush();
	ttclose();
}

/*
 * Move the virtual cursor to an origin
 * 0 spot on the virtual display screen. I could
 * store the column as a character pointer to the spot
 * on the line, which would make "vtputc" a little bit
 * more efficient. No checking for errors.
 */
VOID
vtmove(row, col)
{
	vtrow = row;
	vtcol = col;
}

/*
 * Write a character to the virtual display,
 * dealing with long lines and the display of unprintable
 * things like control characters. Also expand tabs every 8
 * columns. This code only puts printing characters into 
 * the virtual display image. Special care must be taken when
 * expanding tabs. On a screen whose width is not a multiple
 * of 8, it is possible for the virtual cursor to hit the
 * right margin before the next tab stop is reached. This
 * makes the tab code loop if you are not careful.
 * Three guesses how we found this.
 */
VOID
vtputc(c) register int c;
{
	register VIDEO	*vp;

	vp = vscreen[vtrow];
	if (vtcol >= ncol)
		vp->v_text[ncol-1] = '$';
        else if (
#ifdef	NOTAB
		 !(mode&MNOTAB) &&
#endif
		 c == '\t')
	{
		do {
			vtputc(' ');
		} while (vtcol<ncol && (vtcol&0x07)!=0);
	}
	else if (ISCTRL(c) != FALSE)
	{
		vtputc('^');
		vtputc(c ^ 0x40);
	} else
		vp->v_text[vtcol++] = c;		
}

/*
 * Erase from the end of the
 * software cursor to the end of the
 * line on which the software cursor is
 * located. The display routines will decide
 * if a hardware erase to end of line command
 * should be used to display this.
 */
VOID
vteeol()
{
	register VIDEO	*vp;

	vp = vscreen[vtrow];
	while (vtcol < ncol)
		vp->v_text[vtcol++] = ' ';
}

/*
 * Make sure that the display is
 * right. This is a three part process. First,
 * scan through all of the windows looking for dirty
 * ones. Check the framing, and refresh the screen.
 * Second, make sure that "currow" and "curcol" are
 * correct for the current window. Third, make the
 * virtual and physical screens the same.
 */
VOID
update()
{
	register LINE	*lp;
	register WINDOW	*wp;
	register VIDEO	*vp1;
	VIDEO		*vp2;
	register int	i;
	register int	j;
	register int	c;
	register int	hflag;
	register int	currow;
	register int	curcol;
	register int	offs;
	register int	size;
	VOID traceback ();
	VOID uline ();

	if (typeahead()) return;
	if (sgarbf)
	{				/* must update everything */
		wp = wheadp; 
		while(wp != NULL)
		{
			wp->w_flag |= WFMODE | WFHARD;
			wp = wp->w_wndp;
		}
	}
	hflag = FALSE;				/* Not hard.		*/
	wp = wheadp;
	while (wp != NULL)
	{
		if (wp->w_flag != 0)
		{		/* Need update.		*/
			if ((wp->w_flag&WFFORCE) == 0)
			{
				lp = wp->w_linep;
				for (i=0; i<wp->w_ntrows; ++i)
				{
					if (lp == wp->w_dotp)
						goto out;
					if (lp == wp->w_bufp->b_linep)
						break;
					lp = lforw(lp);
				}
			}
			i = wp->w_force;	/* Reframe this one.	*/
			if (i > 0)
			{
				--i;
				if (i >= wp->w_ntrows)
					i = wp->w_ntrows-1;
			} else if (i < 0)
			{
				i += wp->w_ntrows;
				if (i < 0)
					i = 0;
			} else
				i = wp->w_ntrows/2;
			lp = wp->w_dotp;
			while (i!=0 && lback(lp)!=wp->w_bufp->b_linep)
			{
				--i;
				lp = lback(lp);
			}
			wp->w_linep = lp;
			wp->w_flag |= WFHARD;	/* Force full.		*/
		out:
			lp = wp->w_linep;	/* Try reduced update.	*/
			i  = wp->w_toprow;
			if ((wp->w_flag&~WFMODE) == WFEDIT)
			{
				while (lp != wp->w_dotp)
				{
					++i;
					lp = lforw(lp);
				}
				vscreen[i]->v_color = CTEXT;
				vscreen[i]->v_flag |= (VFCHG|VFHBAD);
				vtmove(i, 0);
				for (j=0; j<llength(lp); ++j)
					vtputc(lgetc(lp, j));
				vteeol();
			}
			else if ((wp->w_flag&(WFEDIT|WFHARD)) != 0)
			{
				hflag = TRUE;
				while (i < wp->w_toprow+wp->w_ntrows)
				{
					vscreen[i]->v_color = CTEXT;
					vscreen[i]->v_flag |= (VFCHG|VFHBAD);
					vtmove(i, 0);
					if (lp != wp->w_bufp->b_linep)
					{
						for (j=0; j<llength(lp); ++j)
							vtputc(lgetc(lp, j));
						lp = lforw(lp);
					}
					vteeol();
					++i;
				}
			}
			if ((wp->w_flag&WFMODE) != 0)
				modeline(wp);
			wp->w_flag  = 0;
			wp->w_force = 0;
		}		
		wp = wp->w_wndp;
	}
	lp = curwp->w_linep;			/* Cursor location.	*/
	currow = curwp->w_toprow;
	while (lp != curwp->w_dotp)
	{
		++currow;
		lp = lforw(lp);
	}
	curcol = 0;
	i = 0;
	while (i < curwp->w_doto)
	{
		c = lgetc(lp, i++);
                if (
#ifdef	NOTAB
		    !(mode&MNOTAB) &&
#endif
		    c == '\t')
			curcol |= 0x07;
		else if (ISCTRL(c) != FALSE)
			++curcol;
		++curcol;
	}
	if (curcol >= ncol)			/* Long line.		*/
		curcol = ncol-1;
	if (sgarbf != FALSE)
	{					/* Screen is garbage.	*/
		sgarbf = FALSE;			/* Erase-page clears	*/
		epresf = FALSE;			/* the message area.	*/
		tttop  = HUGE;			/* Forget where you set	*/
		ttbot  = HUGE;			/* scroll region.	*/
		tthue  = CNONE;			/* Color unknown.	*/
		ttmove(0, 0);
		tteeop();
		for (i=0; i<nrow-1; ++i)
		{
			uline(i, vscreen[i], &blanks);
			ucopy(vscreen[i], pscreen[i]);
		}
		ttmove(currow, curcol);
		ttflush();
		return;
	}
#ifdef	GOSLING
	if (hflag != FALSE)
	{			/* Hard update?		*/
		for (i=0; i<nrow-1; ++i)
		{	/* Compute hash data.	*/
			hash(vscreen[i]);
			hash(pscreen[i]);
		}
		offs = 0;			/* Get top match.	*/
		while (offs != nrow-1)
		{
			vp1 = vscreen[offs];
			vp2 = pscreen[offs];
			if (vp1->v_color != vp2->v_color
			||  vp1->v_hash  != vp2->v_hash)
				break;
			uline(offs, vp1, vp2);
			ucopy(vp1, vp2);
			++offs;
		}
		if (offs == nrow-1)
		{		/* Might get it all.	*/
			ttmove(currow, curcol);
			ttflush();
			return;
		}
		size = nrow-1;			/* Get bottom match.	*/
		while (size != offs)
		{
			vp1 = vscreen[size-1];
			vp2 = pscreen[size-1];
			if (vp1->v_color != vp2->v_color
			||  vp1->v_hash  != vp2->v_hash)
				break;
			uline(size-1, vp1, vp2);
			ucopy(vp1, vp2);
			--size;
		}
		if ((size -= offs) == 0)	/* Get screen size.	*/
			panic("Illegal screen size in update");
		setscores(offs, size);		/* Do hard update.	*/
		traceback(offs, size, size, size);
		for (i=0; i<size; ++i)
			ucopy(vscreen[offs+i], pscreen[offs+i]);
		ttmove(currow, curcol);
		ttflush();
		return;			
	}
#endif
	for (i=0; i<nrow-1; ++i)
	{		/* Easy update.		*/
		vp1 = vscreen[i];
		vp2 = pscreen[i];
		if ((vp1->v_flag&VFCHG) != 0)
		{
			uline(i, vp1, vp2);
			ucopy(vp1, vp2);
		}
	}
	ttmove(currow, curcol);
	ttflush();
}

/*
 * Update a saved copy of a line,
 * kept in a VIDEO structure. The "vvp" is
 * the one in the "vscreen". The "pvp" is the one
 * in the "pscreen". This is called to make the
 * virtual and physical screens the same when
 * display has done an update.
 */
VOID
ucopy(vvp, pvp) register VIDEO *vvp; register VIDEO *pvp;
{

	vvp->v_flag &= ~VFCHG;			/* Changes done.	*/
	pvp->v_flag  = vvp->v_flag;		/* Update model.	*/
	pvp->v_hash  = vvp->v_hash;
	pvp->v_cost  = vvp->v_cost;
	pvp->v_color = vvp->v_color;
	bcopy(vvp->v_text, pvp->v_text, ncol);
}

/*
 * Update a single line. This routine only
 * uses basic functionality (no insert and delete character,
 * but erase to end of line). The "vvp" points at the VIDEO
 * structure for the line on the virtual screen, and the "pvp"
 * is the same for the physical screen. Avoid erase to end of
 * line when updating CMODE color lines, because of the way that
 * reverse video works on most terminals.
 */
VOID uline(row, vvp, pvp) VIDEO *vvp; VIDEO *pvp;
{
#ifdef	MEMMAP
	putline(row+1, 1, &vvp->v_text[0]);
#else
	register char	*cp1;
	register char	*cp2;
	register char	*cp3;
	char		*cp4;
	char		*cp5;
	register int	nbflag;

	if (vvp->v_color != pvp->v_color)
	{				/* Wrong color,			*/
		ttmove(row, 0);		/* do a full redraw.		*/
#ifdef	STANDOUT_GLITCH
		if (pvp->v_color != CTEXT && SG >= 0) tteeol();
#endif
		ttcolor(vvp->v_color);
#ifdef	STANDOUT_GLITCH
		cp1 = &vvp->v_text[SG > 0 ? SG : 0];
		/* the odd code for SG==0 is to avoid putting the invisable
		 * glitch character on the next line.  
		 * (Hazeltine executive 80 model 30)
		 */
		cp2 = &vvp->v_text[ncol - (SG >= 0 ? (SG!=0 ? SG : 1) : 0)];
#else
		cp1 = &vvp->v_text[0];
		cp2 = &vvp->v_text[ncol];
#endif
		while (cp1 != cp2)
		{
			ttputc(*cp1++);
			++ttcol;
		}
#ifndef	MOVE_STANDOUT
		ttcolor(CTEXT);
#endif
		return;
	}
	cp1 = &vvp->v_text[0];			/* Compute left match.	*/
	cp2 = &pvp->v_text[0];
	while (cp1!=&vvp->v_text[ncol] && cp1[0]==cp2[0])
	{
		++cp1;
		++cp2;
	}
	if (cp1 == &vvp->v_text[ncol])		/* All equal.		*/
		return;
	nbflag = FALSE;
	cp3 = &vvp->v_text[ncol];		/* Compute right match.	*/
	cp4 = &pvp->v_text[ncol];
	while (cp3[-1] == cp4[-1])
	{
		--cp3;
		--cp4;
		if (cp3[0] != ' ')		/* Note non-blanks in	*/
			nbflag = TRUE;		/* the right match.	*/
	}
	cp5 = cp3;				/* Is erase good?	*/
	if (nbflag==FALSE && vvp->v_color==CTEXT)
	{
		while (cp5!=cp1 && cp5[-1]==' ')
			--cp5;
		/* Alcyon hack */
		if ((int)(cp3-cp5) <= tceeol)
			cp5 = cp3;
	}
	/* Alcyon hack */
	ttmove(row, (int)(cp1-&vvp->v_text[0]));
#ifdef	STANDOUT_GLITCH
	if (vvp->v_color != CTEXT && SG > 0)
	{
		if(cp1 < &vvp->v_text[SG]) cp1 = &vvp->v_text[SG];
		if(cp5 > &vvp->v_text[ncol-SG]) cp5 = &vvp->v_text[ncol-SG];
	} else if (SG < 0)
#endif
	ttcolor(vvp->v_color);
	while (cp1 != cp5)
	{
		ttputc(*cp1++);
		++ttcol;
	}
	if (cp5 != cp3)				/* Do erase.		*/
		tteeol();
#endif
}

#define LAST_ROT 5
static int rotat_pos = 1;
/*
 * The mode line reflects the the Function-Key help
 * information, rather than the state of the current window/buffer
 * (since there is only one window in the TINY version).
 */
VOID
modeline(wp)
register WINDOW *wp;
{
	register int	row,	/* display row */
			col;	/* current column */
	register BUFFER	*bp;

	row = wp->w_toprow+wp->w_ntrows;	/* Location.		*/
	vscreen[row]->v_color = CMODE;		/* Mode line color.	*/
	vscreen[row]->v_flag |= (VFCHG|VFHBAD);	/* Recompute, display.	*/
	vtmove(row, 0);				/* Seek to right line.	*/
	bp = wp->w_bufp;
	if ((mode & MOVRSTK) == 0)
		col = vtputs(" Insert |");
	else
		col = vtputs(" Replace|");

	switch (rotat_pos)
	{
		case 1:		/* Function Keys 1-4 */
			col += vtputs("F1 -Prv Form |");
#ifdef MISLOG
			col += vtputs("F2 -Time Stmp|");
#else
			col += vtputs("             |");
#endif
			col += vtputs("F3 -Prv Line |");
			col += vtputs("F4 -Nxt Line |");
			break;
		case 2:		/* Function Keys 5-8 */
			col += vtputs("F5 -Open Line|");
			col += vtputs("F6 -Fix Para |");
#ifdef MENU_INSERT
			col += vtputs("F7 -Ins File |");
#else
			col += vtputs("             |");
#endif
			col += vtputs("F8 -Rep/Ins  |");
			break;
		case 3:		/* Function Keys 9,11-13 */
			col += vtputs("F9 -Save     |");
			col += vtputs("F11-Restart  |");
			col += vtputs("F12-Print    |");
			col += vtputs("F13-Search   |");
			break;
		case 4:		/* Function Keys 14-17 */
			col += vtputs("F14-Prv Para |");
			col += vtputs("F15-Nxt Para |");
			col += vtputs("F16-Beg Line |");
			col += vtputs("F17-End Line |");
			break;
		case 5:		/* Function Keys 18-19+ */
			col += vtputs("F18-Del Line |");
			col += vtputs("F19-Zoom Can |");
			col += vtputs("             |");
			col += vtputs("             |");
			break;
		default:	/* shouldn't get here. set to known state */
			rotat_pos = 1;
			break;
	} /* end switch */
	while (col < 60)
	{
		vtputc(' ');
		++col;
	}
	col += vtputs("F10- More Key");
	while (col < ncol)
	{			/* Pad out.		*/
		vtputc(' ');
		++col;
	}
}

/*
 * This routine does the actual rotating of the fields
 * of the mode-line help message.
 */
/*ARGSUSED*/
rotatmode( f, n, k)
{
	rotat_pos += 1;
	if (rotat_pos > LAST_ROT)
		rotat_pos = 1;
	modeline(wheadp);		/* NOTE: this only works because only */
	wheadp->w_flag &= WFHARD;	/* one window allowed in TINY version */
	return TRUE;
}

/*
 * output a string to the mode line, report how long it was.
 */
vtputs(s) register char *s;
{
	register int n = 0;

	while (*s != '\0')
	{
		vtputc(*s++);
		++n;
	}
	return n;
}
#ifdef	GOSLING
/*
 * Compute the hash code for
 * the line pointed to by the "vp". Recompute
 * it if necessary. Also set the approximate redisplay
 * cost. The validity of the hash code is marked by
 * a flag bit. The cost understand the advantages
 * of erase to end of line. Tuned for the VAX
 * by Bob McNamara; better than it used to be on
 * just about any machine.
 */
VOID
hash(vp) register VIDEO *vp;
{
	register int	i;
	register int	n;
	register char	*s;
 
	if ((vp->v_flag&VFHBAD) != 0)
	{		/* Hash bad.		*/
		s = &vp->v_text[ncol-1];
		for (i=ncol; i!=0; --i, --s)
			if (*s != ' ')
				break;
		n = ncol-i;			/* Erase cheaper?	*/
		if (n > tceeol)
			n = tceeol;
		vp->v_cost = i+n;		/* Bytes + blanks.	*/
		for (n=0; i!=0; --i, --s)
			n = (n<<5) + n + *s;
		vp->v_hash = n;			/* Hash code.		*/
		vp->v_flag &= ~VFHBAD;		/* Flag as all done.	*/
	}
}

/*
 * Compute the Insert-Delete
 * cost matrix. The dynamic programming algorithm
 * described by James Gosling is used. This code assumes
 * that the line above the echo line is the last line involved
 * in the scroll region. This is easy to arrange on the VT100
 * because of the scrolling region. The "offs" is the origin 0
 * offset of the first row in the virtual/physical screen that
 * is being updated; the "size" is the length of the chunk of
 * screen being updated. For a full screen update, use offs=0
 * and size=nrow-1.
 *
 * Older versions of this code implemented the score matrix by
 * a two dimensional array of SCORE nodes. This put all kinds of
 * multiply instructions in the code! This version is written to
 * use a linear array and pointers, and contains no multiplication
 * at all. The code has been carefully looked at on the VAX, with
 * only marginal checking on other machines for efficiency. In
 * fact, this has been tuned twice! Bob McNamara tuned it even
 * more for the VAX, which is a big issue for him because of
 * the 66 line X displays.
 *
 * On some machines, replacing the "for (i=1; i<=size; ++i)" with
 * i = 1; do { } while (++i <=size)" will make the code quite a
 * bit better; but it looks ugly.
 */
VOID
setscores(offs, size)
{
	register SCORE	*sp;
	SCORE		*sp1;
	register int	tempcost;
	register int	bestcost;
	register int	j;
	register int	i;
	register VIDEO	**vp;
	VIDEO		**pp, **vbase, **pbase;
 
	vbase = &vscreen[offs-1];		/* By hand CSE's.	*/
	pbase = &pscreen[offs-1];
	score[0].s_itrace = 0;			/* [0, 0]		*/
	score[0].s_jtrace = 0;
	score[0].s_cost   = 0;
	sp = &score[1];				/* Row 0, inserts.	*/
	tempcost = 0;
	vp = &vbase[1];
	for (j=1; j<=size; ++j)
	{
		sp->s_itrace = 0;
		sp->s_jtrace = j-1;
		tempcost += tcinsl;
		tempcost += (*vp)->v_cost;
		sp->s_cost = tempcost;
		++vp;
		++sp;
	}
	sp = &score[NROW];			/* Column 0, deletes.	*/
	tempcost = 0;
	for (i=1; i<=size; ++i)
	{
		sp->s_itrace = i-1;
		sp->s_jtrace = 0;
		tempcost  += tcdell;
		sp->s_cost = tempcost;
		sp += NROW;
	}
	sp1 = &score[NROW+1];			/* [1, 1].		*/
	pp = &pbase[1];
	for (i=1; i<=size; ++i)
	{
		sp = sp1;
		vp = &vbase[1];
		for (j=1; j<=size; ++j)
		{
			sp->s_itrace = i-1;
			sp->s_jtrace = j;
			bestcost = (sp-NROW)->s_cost;
			if (j != size)		/* Cd(A[i])=0 @ Dis.	*/
				bestcost += tcdell;
			tempcost = (sp-1)->s_cost;
			tempcost += (*vp)->v_cost;
			if (i != size)		/* Ci(B[j])=0 @ Dsj.	*/
				tempcost += tcinsl;
			if (tempcost < bestcost)
			{
				sp->s_itrace = i;
				sp->s_jtrace = j-1;
				bestcost = tempcost;
			}
			tempcost = (sp-NROW-1)->s_cost;
			if ((*pp)->v_color != (*vp)->v_color
			||  (*pp)->v_hash  != (*vp)->v_hash)
				tempcost += (*vp)->v_cost;
			if (tempcost < bestcost)
			{
				sp->s_itrace = i-1;
				sp->s_jtrace = j-1;
				bestcost = tempcost;
			}
			sp->s_cost = bestcost;
			++sp;			/* Next column.		*/
			++vp;
		}
		++pp;
		sp1 += NROW;			/* Next row.		*/
	}
}

/*
 * Trace back through the dynamic programming cost
 * matrix, and update the screen using an optimal sequence
 * of redraws, insert lines, and delete lines. The "offs" is
 * the origin 0 offset of the chunk of the screen we are about to
 * update. The "i" and "j" are always started in the lower right
 * corner of the matrix, and imply the size of the screen.
 * A full screen traceback is called with offs=0 and i=j=nrow-1.
 * There is some do-it-yourself double subscripting here,
 * which is acceptable because this routine is much less compute
 * intensive then the code that builds the score matrix!
 */
VOID traceback(offs, size, i, j)
{
	register int	itrace;
	register int	jtrace;
	register int	k;
	register int	ninsl;
	register int	ndraw;
	register int	ndell;

	if (i==0 && j==0)			/* End of update.	*/
		return;
	itrace = score[(NROW*i) + j].s_itrace;
	jtrace = score[(NROW*i) + j].s_jtrace;
	if (itrace == i)
	{			/* [i, j-1]		*/
		ninsl = 0;			/* Collect inserts.	*/
		if (i != size)
			ninsl = 1;
		ndraw = 1;
		while (itrace!=0 || jtrace!=0)
		{
			if (score[(NROW*itrace) + jtrace].s_itrace != itrace)
				break;
			jtrace = score[(NROW*itrace) + jtrace].s_jtrace;
			if (i != size)
				++ninsl;
			++ndraw;
		}
		traceback(offs, size, itrace, jtrace);
		if (ninsl != 0)
		{
			ttcolor(CTEXT);
			ttinsl(offs+j-ninsl, offs+size-1, ninsl);
		}
		do {				/* B[j], A[j] blank.	*/
			k = offs+j-ndraw;
			uline(k, vscreen[k], &blanks);
		} while (--ndraw);
		return;
	}
	if (jtrace == j)
	{			/* [i-1, j]		*/
		ndell = 0;			/* Collect deletes.	*/
		if (j != size)
			ndell = 1;
		while (itrace!=0 || jtrace!=0)
		{
			if (score[(NROW*itrace) + jtrace].s_jtrace != jtrace)
				break;
			itrace = score[(NROW*itrace) + jtrace].s_itrace;
			if (j != size)
				++ndell;
		}
		if (ndell != 0)
		{
			ttcolor(CTEXT);
			ttdell(offs+i-ndell, offs+size-1, ndell);
		}
		traceback(offs, size, itrace, jtrace);
		return;
	}
	traceback(offs, size, itrace, jtrace);
	k = offs+j-1;
	uline(k, vscreen[k], pscreen[offs+i-1]);
}
#endif
\Rogue\Monster\
else
  echo "will not over write ./display.c"
fi
if `test ! -s ./echo.c`
then
echo "writing ./echo.c"
cat > ./echo.c << '\Rogue\Monster\'
/*
 *		Echo line reading and writing.
 *
 * Common routines for reading
 * and writing characters in the echo line area
 * of the display screen. Used by the entire
 * known universe.
 */
#include	"def.h"
#ifdef	VARARGS
#  include	<varargs.h>
	static veread();
VOID	ewprintf();
#endif
static VOID	eformat();
static VOID	eputi();
static VOID	eputl();
static VOID	eputs();
static VOID	eputc();

int	epresf	= FALSE;		/* Stuff in echo line flag.	*/
/*
 * Erase the echo line.
 */
VOID
eerase()
{
	ttcolor(CTEXT);
	ttmove(nrow-1, 0);
	tteeol();
	ttflush();
	epresf = FALSE;
}

/*
 * Ask "yes" or "no" question.
 * Return ABORT if the user answers the question
 * with the abort ("^G") character. Return FALSE
 * for "no" and TRUE for "yes". No formatting
 * services are available. No newline required.
 */
eyorn(sp) char *sp;
{
	register KEY	s;

	if (kbdmop == NULL) ewprintf("%s? (y or n) ", sp);
	for (;;)
	{
		s = getkey(0);
		if (s == 'y' || s == 'Y') return (TRUE);
		if (s == 'n' || s == 'N') return (FALSE);
		if (s == (KCTRL|'G') || s == (KCTLX|KCTRL|'G')
		||  s == (KMETA|KCTRL|'G'))
		{
			(VOID) ctrlg(FALSE, 1, KRANDOM);
			return ABORT;
		}
		if (kbdmop == NULL)
			ewprintf("Please answer y or n.  %s? (y or n) ", sp);
	}
	/*NOTREACHED*/
}

/*
 * Like eyorn, but for more important question. User must type either all of
 * "yes" or "no", and the trainling newline.
 */
eyesno(sp) char *sp;
{
	register int	s;
	char		buf[64];

	s = ereply("%s? (yes or no) ", buf, sizeof(buf), sp);
	for (;;)
	{
		if (s == ABORT) return ABORT;
		if (s != FALSE)
		{
			if ((buf[0] == 'y' || buf[0] == 'Y')
			&&  (buf[1] == 'e' || buf[1] == 'E')
			&&  (buf[2] == 's' || buf[2] == 'S')) return TRUE;
			if ((buf[0] == 'n' || buf[0] == 'N')
			&&  (buf[1] == 'o' || buf[0] == 'O')) return FALSE;
		}
		s = ereply("Please answer yes or no.  %s? (yes or no) ",
			   buf, sizeof(buf), sp);
	}
	/*NOTREACHED*/
}
/*
 * Write out a prompt, and read back a
 * reply. The prompt is now written out with full "ewprintf"
 * formatting, although the arguments are in a rather strange
 * place. This is always a new message, there is no auto
 * completion, and the return is echoed as such.
 */
#ifdef	VARARGS
ereply(va_alist)
va_dcl
{
	register int i;
	va_list pvar;
	register char *fp, *buf;
	register int nbuf;
	
	va_start(pvar);
	fp = va_arg(pvar, char *);
	buf = va_arg(pvar, char *);
	nbuf = va_arg(pvar, int);
	i = veread(fp, buf, nbuf, EFNEW|EFCR, &pvar);
	va_end(pvar);
	return i;
}
#else
/* VARARGS3 */
ereply(fp, buf, nbuf, arg) char *fp, *buf; int nbuf; long arg;
{
	return (eread(fp, buf, nbuf, EFNEW|EFCR, (char *)&arg));
}
#endif

/*
 * This is the general "read input from the
 * echo line" routine. The basic idea is that the prompt
 * string "prompt" is written to the echo line, and a one
 * line reply is read back into the supplied "buf" (with
 * maximum length "len"). The "flag" contains EFNEW (a
 * new prompt), an EFFUNC (autocomplete), or EFCR (echo
 * the carriage return as CR).
 */
/* VARARGS4 */
#ifdef	VARARGS
eread(va_alist)
va_dcl
{
	va_list pvar;
	char *fp, *buf;
	int nbuf, flag, i;
	va_start(pvar);
	fp   = va_arg(pvar, char *);
	buf  = va_arg(pvar, char *);
	nbuf = va_arg(pvar, int);
	flag = va_arg(pvar, int);
	i = veread(fp, buf, nbuf, flag, &pvar);
	va_end(pvar);
	return i;
}
#endif

#ifdef	VARARGS
static veread(fp, buf, nbuf, flag, ap) char *fp; char *buf; va_list *ap;
{
#else
eread(fp, buf, nbuf, flag, ap) char *fp; char *buf; char *ap;
{
#endif
	register int	cpos;
	register int	i;
	register KEY	c;

	cpos = 0;
	if (kbdmop != NULL)
	{			/* In a macro.		*/
		while ((c = *kbdmop++) != '\0')
			buf[cpos++] = (char) c;
		buf[cpos] = '\0';
		goto done;
	}
	if ((flag&EFNEW)!=0 || ttrow!=nrow-1)
	{
		ttcolor(CTEXT);
		ttmove(nrow-1, 0);
		epresf = TRUE;
	} else
		eputc(' ');
	eformat(fp, ap);
	tteeol();
	ttflush();
	for (;;)
	{
		c = getkey(KQUOTE|KNOMAC);
		if ((flag&EFAUTO) != 0 && (c == ' ' || c == CCHR('I')))
		{
			cpos += complete(flag, c, buf, cpos);
			continue;
		}
		switch (c)
		{
		case 0x0A:
		case 0x0D:			/* Return, done.	*/
			if ((flag&EFFUNC) != 0)
			{
				if ((i = complete(flag, c, buf, cpos)) == 0)
					continue;
				if (i > 0) cpos += i;
			}
			buf[cpos] = '\0';
			if ((flag&EFCR) != 0)
			{
				ttputc(0x0D);
				ttflush();
			}
			if (kbdmip != NULL)
			{
				if (kbdmip+cpos+1 > &kbdm[NKBDM-3])
				{
					ewprintf("Keyboard macro overflow");
					ttflush();
					return FALSE;
				}
				for (i = 0; i <= cpos; ++i)
					*kbdmip++ = (KEY) buf[i];
			}
			goto done;

		case CCHR('G'):			/* Bell, abort.		*/
			eputc(CCHR('G'));
			(VOID) ctrlg(FALSE, 0, KRANDOM);
			ttflush();
			return (ABORT);

		case 0x7F:			/* Rubout, erase.	*/
			if (cpos != 0)
			{
				ttputc('\b');
				ttputc(' ');
				ttputc('\b');
				--ttcol;
				if (ISCTRL(buf[--cpos]) != FALSE)
				{
					ttputc('\b');
					ttputc(' ');
					ttputc('\b');
					--ttcol;
				}
				ttflush();
			}
			break;

		case CCHR('X'):			/* C-X			*/
		case CCHR('U'):			/* C-U, kill line.	*/
			while (cpos != 0)
			{
				ttputc('\b');
				ttputc(' ');
				ttputc('\b');
				--ttcol;
				if (ISCTRL(buf[--cpos]) != FALSE)
				{
					ttputc('\b');
					ttputc(' ');
					ttputc('\b');
					--ttcol;
				}
			}
			ttflush();
			break;

		case CCHR('Q'):			/* C-Q, quote next	*/
			c = getkey(KQUOTE|KNOMAC) ;
		default:			/* All the rest.	*/
			if (cpos < nbuf-1)
			{
				buf[cpos++] = (char) c;
				eputc((char) c);
				ttflush();
			}
		}
	}
done:
	if (buf[0] == '\0')
		return (FALSE);
	return (TRUE);
}

/*
 * do completion on a list of objects.
 */
complete(flags, c, buf, cpos) register char *buf; register int cpos;
{
	register LIST	*lh, *lh2;
	int		i, nxtra;
	int		nhits, bxtra;
	int		wflag = FALSE;
	int		msglen, nshown;
	char		*msg;

	if ((flags&EFFUNC) != 0) lh = &(symbol[0]->s_list);
	else if ((flags&EFBUF) != 0) lh = &(bheadp->b_list);
	else panic("broken complete call: flags");

	if (c == ' ') wflag = TRUE;
	else if (c != '\t' && c != 0x0D && c != 0x0A)
		panic("broken complete call: c");

	nhits = 0;
	nxtra = HUGE;

	while (lh != NULL)
	{
		for (i=0; i<cpos; ++i)
		{
			if (buf[i] != lh->l_name[i])
				break;
		}
		if (i == cpos)
		{
			if (nhits == 0)
				lh2 = lh;
			++nhits;
			if (lh->l_name[i] == '\0') nxtra = -1;
			else
			{
				bxtra = getxtra(lh, lh2, cpos, wflag);
				if (bxtra < nxtra) nxtra = bxtra;
				lh2 = lh;
			}
		}
		lh = lh->l_next;
	}
	if (nhits == 0)
		msg = " [No match]";
	else if (nhits > 1 && nxtra == 0)
		msg = " [Ambiguous]";
	else
	{		/* Got a match, do it to it */
		/*
		 * Being lazy - ought to check length, but all things
		 * autocompleted have known types/lengths.
		 */ 
		if (nxtra < 0 && nhits > 1 && c == ' ') nxtra = 1;
		for (i = 0; i < nxtra; ++i)
		{
			buf[cpos] = lh2->l_name[cpos];
			eputc(buf[cpos++]);
		}
		ttflush();
		if (nxtra < 0 && c != 0x0D && c != 0x0A) return 0;
		return nxtra;
	}
        /* Set up backspaces, etc., being mindful of echo line limit */
	msglen = strlen(msg);
	nshown = (ttcol + msglen + 2 > ncol) ? 
			ncol - ttcol - 2 : msglen;
	eputs(msg);
	ttcol -= (i = nshown);		/* update ttcol!		*/
	while (i--)			/* move back before msg		*/
		ttputc('\b');
	ttflush();			/* display to user		*/
	i = nshown;
	while (i--)			/* blank out	on next flush	*/
		eputc(' ');
	ttcol -= (i = nshown);		/* update ttcol on BS's		*/
	while (i--)
		ttputc('\b');		/* update ttcol again!		*/
	return 0;

}

/*
 * The "lp1" and "lp2" point to list structures. The
 * "cpos" is a horizontal position in the name.
 * Return the longest block of characters that can be
 * autocompleted at this point. Sometimes the two
 * symbols are the same, but this is normal.
  */
getxtra(lp1, lp2, cpos, wflag) register LIST *lp1, *lp2; register int wflag;
{
	register int	i;

	i = cpos;
	for (;;)
	{
		if (lp1->l_name[i] != lp2->l_name[i]) break;
		if (lp1->l_name[i] == '\0') break;
		++i;
		if (wflag && !ISWORD(lp1->l_name[i-1])) break;
	}
	return (i - cpos);
}

/*
 * Special "printf" for the echo line.
 * Each call to "ewprintf" starts a new line in the
 * echo area, and ends with an erase to end of the
 * echo line. The formatting is done by a call
 * to the standard formatting routine.
 */
#ifdef	VARARGS
VOID
ewprintf(va_alist)
va_dcl
{
	va_list pvar;
	register char *fp;

	va_start(pvar);
	fp = va_arg(pvar, char *);
#else
/* VARARGS1 */
ewprintf(fp, arg) char *fp;
{
#endif
	ttcolor(CTEXT);
	ttmove(nrow-1, 0);
#ifdef	VARARGS
	eformat(fp, &pvar);
	va_end(pvar);
#else
	eformat(fp, (char *)&arg);
#endif
	tteeol();
	ttflush();
	epresf = TRUE;
}

/*
 * Printf style formatting. This is
 * called by both "ewprintf" and "ereply" to provide
 * formatting services to their clients. The move to the
 * start of the echo line, and the erase to the end of
 * the echo line, is done by the caller.
 * Note: %c works, and prints the "name" of the key. However
 * the key must be cast to an int to avoid tripping over
 * various oddities in C argument passing.
 */
static VOID
eformat(fp, ap) register char *fp;
#ifdef VARARGS
register va_list *ap;
#else
register char *ap;
#endif
{
	register int	c;
	char		kname[NKNAME];

	while ((c = *fp++) != '\0')
	{
		if (c != '%')
			eputc(c);
		else
		{
			c = *fp++;
			switch (c)
			{
			case 'c':
#ifdef	VARARGS
				keyname(kname, va_arg(*ap, int));
#else
				/*NOSTRICT*/
				keyname(kname, *(int *)ap);
				ap += sizeof(int);
#endif
				eputs(kname);
				break;

			case 'd':
#ifdef	VARARGS
				eputi(va_arg(*ap, int), 10);
#else
				/*NOSTRICT*/
				eputi(*(int *)ap, 10);
				ap += sizeof(int);
#endif
				break;

			case 'o':
#ifdef	VARARGS
				eputi(va_arg(*ap, int), 8);
#else
				/*NOSTRICT*/
				eputi(*(int *)ap,  8);
				ap += sizeof(int);
#endif
				break;

			case 's':
#ifdef	VARARGS
				eputs(va_arg(*ap, char *));
#else
				/*NOSTRICT*/
				eputs(*(char **)ap);
				ap += sizeof(char *);
#endif
				break;
			case 'l':/* explicit longword */
				c = *fp++;
				switch(c)
				{
				case 'd':
#ifdef	VARARGS
					eputl((long)va_arg(*ap, long), 10L);
#else
					/*NOSTRICT*/
					eputl(*(long *)ap, 10L);
					ap += sizeof(long);
#endif
					break;
				default:
					eputc(c);
					break;
				}
				break;

			default:
				eputc(c);
			}
		}
	}
}

/*
 * Put integer, in radix "r".
 */
static VOID
eputi(i, r) register int i; register int r;
{
	register int	q;

	if ((q=i/r) != 0)
		eputi(q, r);
	eputc(i%r+'0');
}

/*
 * Put long, in radix "r".
 */
static VOID
eputl(l, r) register long l; register long r;
{
	register long	q;

	if ((q=l/r) != 0)
		eputl(q, r);
	eputc((int)(l%r)+'0');
}

/*
 * Put string.
 */
static VOID
eputs(s) register char *s;
{
	register int	c;

	while ((c = *s++) != '\0')
		eputc(c);
}

/*
 * Put character. Watch for
 * control characters, and for the line
 * getting too long.
 */
static VOID
eputc(c) register char c;
{
	if (ttcol+2 < ncol)
	{
		if (ISCTRL(c) != FALSE)
		{
			eputc('^');
			c ^= 0x40;
		}
		ttputc(c);
		++ttcol;
	}
}

\Rogue\Monster\
else
  echo "will not over write ./echo.c"
fi
if `test ! -s ./extend.c`
then
echo "writing ./extend.c"
cat > ./extend.c << '\Rogue\Monster\'
/*
 *		Extended (M-X) commands.
 */
#include	"def.h"


/*
 * Extended command. Call the message line
 * routine to read in the command name and apply autocompletion
 * to it. When it comes back, look the name up in the symbol table
 * and run the command if it is found and has the right type.
 * Print an error if there is anything wrong.
 */
/*ARGSUSED*/
extend(f, n, k)
{
	register SYMBOL	*sp;
	register int	s;
	char		xname[NXNAME];

	if (f == FALSE)
		s = eread("M-x ", xname, NXNAME, EFNEW|EFFUNC
#ifndef VARARGS
			 , (char *) NULL
#endif
			 ) ;
	else
		s = eread("%d M-x ", xname, NXNAME, EFNEW|EFFUNC, 
#ifdef	VARARGS
			 n
#else
			 (char *) &n, (char *) NULL
#endif
			 ) ;
	if (s != TRUE) return (s);
	if ((sp=symlookup(xname)) != NULL)
		return ((*sp->s_funcp)(f, n, KRANDOM));
	ewprintf("[No match]");
	return FALSE;
}

/*
 * This function will erase the current buffer, re-read the file, and
 * cancel the modification flag. It has to remember the file name.
 */
/*ARGSUSED*/
startover( f, n, k)
{
	BUFFER *bp;

	bp = wheadp->w_bufp;
	if (bclear(bp) != FALSE)	/* user did not abort it */
	{
		insertfile( bp->b_fname, (char *)NULL);
		notmodified( FALSE, 0, KRANDOM);
		sgarbf = TRUE;
	}
}

/*
 * This function will quit Emacs without saving any files,
 * or asking about saving them. Just call quit with an argument.
 */
/*ARGSUSED*/
reallyquit( f, n, k)
{
	extern int quit();

	quit( TRUE, 1, KRANDOM);
}

/*
 * This function will delete the current line. It is easy because
 * all of the work is done in "killline". The cursor may be anywhere
 * on it, not just at the beginning.
 */
/*ARGSUSED*/
deleteline( f, n, k)
{
	killline( TRUE, 0, KRANDOM);	/* kill beginning of line */
	killline( TRUE, 1, KRANDOM);	/* kill end including nl */
}

#ifdef	STARTUP
/*
 * Define the commands needed to do startup-file processing.
 * This code is mostly a kludge just so we can get startup-file processing.
 *
 * If you're serious about having this code, you should rewrite it.
 * To wit: 
 *	It has lots of funny things in it to make the startup-file look
 *	like a GNU startup file; mostly dealing with parens and semicolons.
 *	This should all vanish.
 *
 *	It uses the same buffer as keyboard macros. The fix is easy (make
 *	a new function "execmacro" that takes a pointer to char and
 *	does what ctlxe does on it. Make ctlxe and excline both call it.)
 *	but would slow down the non-micro version.
 *
 * We define eval-expression because it's easy. It's pretty useless,
 * since it duplicates the functionality of execute-extended-command.
 * All of this is just to support startup files, and should be turned
 * off for micros.
 */

/*
 * evalexpr - get one line from the user, and run it. Identical in function
 *	to extend, but easy.
 */
/*ARGSUSED*/
evalexpr(f, n, k)
{
	register int	s;
	char		exbuf[NKBDM];

	if ((s = ereply("Eval: ", exbuf, NKBDM)) != TRUE)
		return s;
	return excline(exbuf);
}
/*
 * evalbuffer - evaluate the current buffer as line commands. Useful
 *	for testing startup files.
 */
/*ARGSUSED*/
evalbuffer(f, n, k)
{
	register LINE	*lp;
	register BUFFER	*bp = curbp;
	register int	s;
	static char	excbuf[NKBDM];
	char		*strncpy();

	for (lp = lforw(bp->b_linep); lp != bp->b_linep; lp = lforw(lp))
	{
		if (llength(lp) >= NKBDM + 1) return FALSE ;
		(VOID) strncpy(excbuf, ltext(lp), NKBDM);
		if ((s = excline(excbuf)) != TRUE) return s;
	}
	return TRUE;
}
/*
 * evalfile - go get a file and evaluate it as line commands. You can
 *	go get your own startup file if need be.
 */
/*ARGSUSED*/
evalfile(f, n, k)
{
	register int	s;
	char		fname[NFILEN];

	if ((s = ereply("Load file: ", fname, NFILEN)) != TRUE)
		return s;
	return load(fname);
}

/*
 * load - go load the file name we got passed.
 */
load(fname) char *fname;
{
	register int	s;
	char		excbuf[NKBDM];

	if (((s = ffropen(fname)) == FIOERR) || (s == FIOFNF))
		return FALSE;
	while ((s = ffgetline(excbuf, NKBDM)) == FIOSUC)
		if (excline(excbuf) != TRUE) break;
	(VOID) ffclose();
	return s == FIOEOF;
}

/*
 * excline - run a line from a load file or eval-expression.
 */
excline(line) register char *line;
{
	register char	*funcp, *argp = NULL;
	char		*skipwhite(), *parsetoken(), *backquote();
	int		status;

	/* Don't know if it works; don't care - mwm */
	if (kbdmip != NULL || kbdmop != NULL)
	{
		ewprintf("Not now!") ;
		return FALSE;
	}

	funcp = skipwhite(line);
	if (*funcp == '\0') return TRUE;	/* No error on blank lines */
	line = parsetoken(funcp);
	if (*line != '\0')
	{
		*line++ = '\0';
		line = skipwhite(line);
		if ((*line >= '0' && *line <= '9') || *line == '-')
		{
			argp = line;
			line = parsetoken(line);
		}
	}

	kbdmip = &kbdm[0];
	if (argp != NULL)
	{
		*kbdmip++ = (KEY) (KCTRL|'U');
		*kbdmip++ = (KEY) atoi(argp);
	}
	*kbdmip++ = (KEY) (KMETA|'X');
	/* Pack in function */
	while (*funcp != '\0')
		if (kbdmip+1 <= &kbdm[NKBDM-3])
			*kbdmip++ = (KEY) *funcp++;
		else
		{
			ewprintf("eval-expression macro overflow");
			ttflush();
			return FALSE;
		}
	*kbdmip++ = '\0';	/* done with function */
	/* Pack away all the args now...	*/
	while (*line != '\0')
	{
		argp = skipwhite(line);
		if (*argp == '\0') break ;
		line = parsetoken(argp) ;
		/* Slightly bogus for strings. But they should be SHORT! */
		if (kbdmip+(line-argp)+1 > &kbdm[NKBDM-3])
		{
			ewprintf("eval-expression macro overflow");
			ttflush();
			return FALSE;
		}
		if (*line != '\0') *line++ = '\0';
		if (*argp != '"')
		{
			if (*argp == '\'') ++argp;
			while (*argp != '\0')
				*kbdmip++ = (KEY) *argp++;
			*kbdmip++ = '\0';
		}
		else
		{	/* Quoted strings special again */
			++argp;
			while (*argp != '"' && *argp != '\0')
				if (*argp != '\\') *kbdmip++ = (KEY) *argp++;
				else argp = backquote(++argp, TRUE);
			/* Quotes strings are gotkey'ed, so no trailing null */
		}
	}
	*kbdmip++ = (KEY) (KCTLX|')');
	*kbdmip++ = '\0';
	kbdmip = NULL;
	status = ctlxe(FALSE, 1, KRANDOM);
	kbdm[0] = (KCTLX|')');
	return status;
}
/*
 * a pair of utility functions for the above
 */
char *
skipwhite(s) register char *s;
{

	while ((*s == ' ' || *s == '\t' || *s == ')' || *s == '(')
	    && *s != '\0')
		if (*s == ';') *s = '\0' ;
		else s++;
	return s;
}

char *
parsetoken(s) register char *s;
{

	if (*s != '"')
		while (*s != ' ' && *s != '\t' && *s != '(' && *s != ')'
				&& *s != '\0')
		{
			if (*s == ';')
				*s = '\0';
			else
				s++;
		}
	else	/* Strings get special treatment */
		do {
			/* Beware: You can \ out the end of the string! */
			if (*s == '\\')
				++s;
			if (ISLOWER(*s))
				*s = TOUPPER(*s);
			} while (*++s != '"' && *s != '\0');
	return s;
}
/*
 * Put a backquoted string element into the keyboard macro. Return pointer
 * to char following backquoted stuff.
 */
/* Don't want to get the objects in isdigit.c just for this */
#define	isdigit(c)	(((c) >= '0') && ((c) <= '9'))

char *
backquote(in, flag) char *in;
{
	register KEY	keycode;

	switch (*in++)
	{
	    case 'T': *kbdmip++ = (KEY) (KCTRL|'I');
		      break;
	    case 'N': *kbdmip++ = (KEY) (KCTRL|'J');
		      break; 
	    case 'R': *kbdmip++ = (KEY) (KCTRL|'M');
		      break;
	    case '^': *kbdmip = (KEY) (KCTRL|*in++);
		if (flag != FALSE && *kbdmip == (KEY) (KCTRL|'X'))
		{
			if (*in == '\\')
				in = backquote(++in, FALSE);
			else
				*kbdmip++ = (KEY) *in++;
			kbdmip[-1] |= (KEY) KCTLX;
		}
		else ++kbdmip;
		      break;
	    case 'E':
		if (flag != TRUE)
			*kbdmip++ = (KEY) (KCTRL|'[');
		else if (*in != '\\')
			*kbdmip++ = (KEY) (KMETA|*in++);
		else
		{
			in = backquote(++in, FALSE);
			kbdmip[-1] |= (KEY) KMETA;
		}
		break;

	    /* (L. Frenkel) Convert "\Fd" and "\Fdd" to a function
	     * key code between KFIRST and KLAST. "dd" should be
	     * decimal; codes > KLAST are mapped to KLAST, for want
	     * of a better idea of what to do with them.
	     */
	    case 'F':
		keycode = 0;
		if (isdigit(*in))
			keycode += *in++ - '0';
		if (isdigit(*in))
			keycode = (10 * keycode) + *in++ - '0';
		if ( (keycode += KFIRST) > KLAST)
			keycode = KLAST;
		*kbdmip++ = (KEY) keycode;
		break;
	}
	return in;
}
#endif	STARTUP
\Rogue\Monster\
else
  echo "will not over write ./extend.c"
fi
if `test ! -s ./file.c`
then
echo "writing ./file.c"
cat > ./file.c << '\Rogue\Monster\'
/*
 * 		File commands.
 */
#include	"def.h"

BUFFER	*findbuffer();
VOID	makename();
VOID	upmodes();

/*
 * insert a file into the current buffer. Real easy - just call the
 * insertfile routine with the file name.
 */
/*ARGSUSED*/
fileinsert(f, n, k)
{
	register int	s;
	char		fname[NFILEN];

	if ((s=ereply("Insert file: ", fname, NFILEN)) != TRUE)
		return (s);
	adjustcase(fname);
	return (insertfile(fname, (char *) NULL)); /* don't set buffer name */
}

/*
 * Select a file for editing.
 * Look around to see if you can find the
 * fine in another buffer; if you can find it
 * just switch to the buffer. If you cannot find
 * the file, create a new buffer, read in the
 * text, and switch to the new buffer.
 */
/*ARGSUSED*/
filevisit(f, n, k)
{
	register BUFFER	*bp;
	int		s;
	char		fname[NFILEN];

	if ((s=ereply("Find file: ", fname, NFILEN)) != TRUE)
		return (s);
	if ((bp = findbuffer(fname, &s)) == NULL) return s;
	curbp = bp;
	if (showbuffer(bp, curwp, WFHARD) != TRUE) return FALSE;
	if (bp->b_fname[0] == 0)
		return (readin(fname));		/* Read it in.		*/
	return TRUE;
}


/*
 * given a file name, either find the buffer it uses, or create a new
 * empty buffer to put it in.
 */
BUFFER *
findbuffer(fname, s) char *fname; int *s;
{
	register BUFFER	*bp;
	char		bname[NBUFN];

	adjustcase(fname);
	for (bp=bheadp; bp!=NULL; bp=bp->b_bufp)
	{
		if (strcmp(bp->b_fname, fname) == 0)
		{
			return bp;
		}
	}
	makename(bname, fname);			/* New buffer name.	*/
	while ((bp=bfind(bname, FALSE)) != NULL)
	{
		*s = ereply("Buffer name: ", bname, NBUFN);
		if (*s == ABORT)		/* ^G to just quit	*/
			return NULL;
		if (*s == FALSE)
		{		/* CR to clobber it	*/
			bp->b_fname[0] = '\0';
			break;
		}
	}
	if (bp == NULL) bp = bfind(bname, TRUE);
	*s = FALSE;
	return bp;
}

/*
 * Read the file "fname" into the current buffer.
 * Make all of the text in the buffer go away, after checking
 * for unsaved changes. This is called by the "read" command, the
 * "visit" command, and the mainline (for "uemacs file").
 */
readin(fname) char *fname;
{
	register int		status;
	register WINDOW		*wp;

	if (bclear(curbp) != TRUE)		/* Might be old.	*/
		return TRUE;
	status = insertfile(fname, fname) ;
	curbp->b_flag &= ~BFCHG;		/* No change.		*/
	for (wp=wheadp; wp!=NULL; wp=wp->w_wndp)
	{
		if (wp->w_bufp == curbp)
		{
			wp->w_linep = lforw(curbp->b_linep);
			wp->w_dotp  = lforw(curbp->b_linep);
			wp->w_doto  = 0;
			wp->w_markp = NULL;
			wp->w_marko = 0;
		}
	}
	return status;
}
/*
 * insert a file in the current buffer, after dot. Set mark
 * at the end of the text inserted, point at the beginning.
 * Return a standard status. Print a summary (lines read,
 * error message) out as well. If the
 * BACKUP conditional is set, then this routine also does the read
 * end of backup processing. The BFBAK flag, if set in a buffer,
 * says that a backup should be taken. It is set when a file is
 * read in, but not on a new file (you don't need to make a backup
 * copy of nothing).
 *
 * Warning: Adds a trainling nl to files that don't end in one!
 * Need to fix, but later (I suspect that it will require a change
 * in the fileio files for all systems involved).
 */
insertfile(fname, newname) char fname[], newname[];
{
	register LINE	*lp1;
	register LINE	*lp2;
	LINE		*olp;			/* Line we started at */
	int		opos;			/* and offset into it */
	register WINDOW	*wp;
	register int	i;
	register int	nbytes;
	int		s, nline;
	BUFFER		*bp;
	char		line[NLINE];

	bp = curbp;				/* Cheap.		*/
	if (newname != (char *) NULL)
		(VOID) strcpy(bp->b_fname, newname);
	if ((s=ffropen(fname)) == FIOERR) 	/* Hard file open.	*/
		goto out;
	if (s == FIOFNF)
	{			/* File not found.	*/
		if (kbdmop == NULL)
			if (newname != NULL)
				ewprintf("(New file)");
			else
				ewprintf("(File not found)");
		goto out;
	}
	opos = curwp->w_doto;
	/* Open a new line, at point, and start inserting after it */
	(VOID) lnewline();
	olp = lback(curwp->w_dotp);
	nline = 0;			/* Don't count fake line at end */
	while ((s=ffgetline(line, NLINE)) == FIOSUC)
	{
		nbytes = strlen(line);
		if ((lp1=lalloc((RSIZE) nbytes)) == NULL)
		{
			s = FIOERR;		/* Keep message on the	*/
			break;			/* display.		*/
		}
		lp2 = lback(curwp->w_dotp);
		lp2->l_fp = lp1;
		lp1->l_fp = curwp->w_dotp;
		lp1->l_bp = lp2;
		curwp->w_dotp->l_bp = lp1;
		for (i=0; i<nbytes; ++i)
			lputc(lp1, i, line[i]);
		++nline;
	}
	(VOID) ffclose();			/* Ignore errors.	*/
	if (s==FIOEOF && kbdmop==NULL)
	{	/* Don't zap an error.	*/
		if (nline == 1)
			ewprintf("(Read 1 line)");
		else
			ewprintf("(Read %d lines)", nline);
	}
	/* Set mark at the end of the text */
	curwp->w_markp = curwp->w_dotp;
	curwp->w_marko = curwp->w_doto;
	/* Now, delete the results of the lnewline we started with */
	curwp->w_dotp = olp;
	curwp->w_doto = opos;
	(VOID) ldelnewline();
	curwp->w_doto = opos;			/* and dot is right	*/
#ifdef	BACKUP
	if (newname != NULL)
		bp->b_flag |= BFCHG | BFBAK;	/* Need a backup.	*/
	else	bp->b_flag |= BFCHG;
#else
	bp->b_flag |= BFCHG;
#endif
	/* if the insert was at the end of buffer, set lp1 to the end of
	 * buffer line, and lp2 to the beginning of the newly inserted
	 * text.  (Otherwise lp2 is set to NULL.)  This is 
	 * used below to set pointers in other windows correctly if they
	 * are also at the end of buffer.
	 */
	lp1 = bp->b_linep;
	if (curwp->w_markp == lp1)
		lp2 = curwp->w_dotp;
	else
	{
out:		lp2 = NULL;
	}
	for (wp=wheadp; wp!=NULL; wp=wp->w_wndp)
	{
		if (wp->w_bufp == curbp)
		{
			wp->w_flag |= WFMODE|WFEDIT;
			if (wp != curwp && lp2 != NULL)
			{
				if (wp->w_dotp == lp1)
					wp->w_dotp = lp2;
				if (wp->w_markp == lp1)
					wp->w_markp = lp2;
				if (wp->w_linep == lp1)
					wp->w_linep = lp2;
			}
		}
	}
	if (s == FIOERR)			/* False if error.	*/
		return (FALSE);
	return (TRUE);
}

/*
 * Take a file name, and from it
 * fabricate a buffer name. This routine knows
 * about the syntax of file names on the target system.
 * BDC1		left scan delimiter.
 * BDC2		optional second left scan delimiter.
 * BDC3		optional right scan delimiter.
 */
VOID
makename(bname, fname) char bname[]; char fname[];
{
	register char	*cp1;
	register char	*cp2;

	cp1 = &fname[0];
	while (*cp1 != 0)
		++cp1;
#ifdef	BDC2
	while (cp1!=&fname[0] && cp1[-1]!=BDC1 && cp1[-1]!=BDC2)
		--cp1;
#else
	while (cp1!=&fname[0] && cp1[-1]!=BDC1)
		--cp1;
#endif
	cp2 = &bname[0];
#ifdef	BDC3
	while (cp2!=&bname[NBUFN-1] && *cp1!=0 && *cp1!=BDC3)
		*cp2++ = *cp1++;
#else
	while (cp2!=&bname[NBUFN-1] && *cp1!=0)
		*cp2++ = *cp1++;
#endif
	*cp2 = 0;
}

/*
 * Ask for a file name, and write the
 * contents of the current buffer to that file.
 * Update the remembered file name and clear the
 * buffer changed flag. This handling of file names
 * is different from the earlier versions, and
 * is more compatable with Gosling EMACS than
 * with ITS EMACS.
 */
/*ARGSUSED*/
filewrite(f, n, k)
{
	register int	s;
	char		fname[NFILEN];

	if ((s=ereply("Write file: ", fname, NFILEN)) != TRUE)
		return (s);
	adjustcase(fname);
	if ((s=writeout(curbp, fname)) == TRUE)
	{
#ifdef	BACKUP
		curbp->b_flag &= ~(BFBAK | BFCHG);
#else
		curbp->b_flag &= ~BFCHG;
#endif
		upmodes(curbp);
	}
	return (s);
}

/*
 * Save the contents of the current buffer back into
 * its associated file. Do nothing if there have been no changes
 * (is this a bug, or a feature). Error if there is no remembered
 * file name. If this is the first write since the read or visit,
 * then a backup copy of the file is made.
 * Allow user to select whether or not to make backup files
 * by looking at the value of makebackup.
 */
#ifdef	BACKUP
static	int	makebackup = 0;
#endif

/*ARGSUSED*/
filesave(f, n, k)
{
	register int	s;

	if ((curbp->b_flag&BFCHG) == 0)
	{	/* Return, no changes.	*/
		if (kbdmop == NULL) ewprintf("(No changes need to be saved)");
		return (TRUE);
	}
	if (curbp->b_fname[0] == 0)
	{		/* Must have a name.	*/
		ewprintf("No file name");
		return (FALSE);
	}
#ifdef	BACKUP
	if (makebackup && ((curbp->b_flag&BFBAK) != 0))
	{
		s = fbackupfile(curbp->b_fname);
		if (s == ABORT)			/* Hard error.		*/
			return FALSE;
		if (s == FALSE			/* Softer error.	*/
		&& (s=eyesno("Backup error, save anyway")) != TRUE)
			return (s);
	}
#endif
	if ((s=writeout(curbp, curbp->b_fname)) == TRUE)
	{
#ifdef	BACKUP
		curbp->b_flag &= ~(BFCHG | BFBAK);
#else
		curbp->b_flag &= ~BFCHG;
#endif
		upmodes(curbp);
	}
	return (s);
}

#ifdef	BACKUP
/* Since we don't have variables (we probably should)
 * this is a command processor for changing the value of
 * the make backup flag.  If no argument is given,
 * sets makebackup to true, so backups are made.  If
 * an argument is given, no backup files are made when
 * saving a new version of a file. Only used when BACKUP
 * is #defined.
 */
/*ARGSUSED*/
makebkfile(f, n, k)
{
	makebackup = !f;	/* make backup if no argument given */
	ewprintf(makebackup ?	"Backup files enabled" :
				"Disabling backup files");
	return (TRUE);
}
#endif

/*
 * This function performs the details of file
 * writing; writing the file in buffer bp to
 * file fn. Uses the file management routines
 * in the "fileio.c" package. Most of the grief
 * is checking of some sort.
 */
writeout(bp, fn) register BUFFER *bp; char *fn;
{
	register int	s;
	register LINE	*lp;

	if ((s=ffwopen(fn)) != FIOSUC)		/* Open writes message.	*/
		return (FALSE);
	lp = lforw(bp->b_linep);		/* First line.		*/
	while (lp != bp->b_linep)
	{
		if ((s=ffputline(&(ltext(lp))[0], llength(lp))) != FIOSUC)
			break;
		lp = lforw(lp);
	}
	if (s == FIOSUC)
	{			/* No write error.	*/
		s = ffclose();
		if (s==FIOSUC && kbdmop==NULL)
			ewprintf("Text Saved", fn);
	} else					/* Ignore close error	*/
		(VOID) ffclose();		/* if a write error.	*/
	if (s != FIOSUC)			/* Some sort of error.	*/
		return (FALSE);
	return (TRUE);
}

/*
 * Tag all windows for bp (all windows if bp NULL) as needing their
 * mode line updated.
 */
VOID
upmodes(bp) register BUFFER *bp;
{
	register WINDOW	*wp;

	for (wp = wheadp; wp != NULL; wp = wp->w_wndp)
		if (bp == NULL || curwp->w_bufp == bp) wp->w_flag |= WFMODE;
}
\Rogue\Monster\
else
  echo "will not over write ./file.c"
fi
echo "Finished archive 3 of 5"
exit


-- 
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
Mark A. Hargrove                                             U.S. TeleCenters
Voice: 408-496-1800                                          Santa Clara, CA
uucp : {dual, hoptoad, hplabs, portal, ptsfa}!well!ustel