v03i029: mg 2a part 5 of 15

Bob Larson BLARSON at ECLA.USC.EDU
Thu May 26 14:55:37 AEST 1988


comp.sources.misc: Volume 3, Issue 29
Submitted-By: "Bob Larson" <BLARSON at ECLA.USC.EDU>
Archive-Name: mg2a/Part5

#	This is a shell archive.
#	Remove everything above and including the cut line.
#	Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar:    Shell Archiver
#	Run the following text with /bin/sh to create:
#	basic.c
#	modes.c
#	paragraph.c
#	region.c
#	search.c
#	version.c
#	window.c
#	word.c
# This archive created: Tue May 17 18:06:31 1988
# By:	blarson
cat << \SHAR_EOF > basic.c
/*
 *		Basic cursor motion commands.
 *
 * The routines in this file are the basic
 * command functions for moving the cursor around on
 * the screen, setting mark, and swapping dot with
 * mark. Only moves between lines, which might make the
 * current buffer framing bad, are hard.
 */
#include	"def.h"

VOID	setgoal();

/*
 * Go to beginning of line.
 */
/*ARGSUSED*/
gotobol(f, n)
{
	curwp->w_doto  = 0;
	return (TRUE);
}

/*
 * Move cursor backwards. Do the
 * right thing if the count is less than
 * 0. Error if you try to move back from
 * the beginning of the buffer.
 */
/*ARGSUSED*/
backchar(f, n)
register int n;
{
	register LINE	*lp;

	if (n < 0) return forwchar(f, -n);
	while (n--) {
		if (curwp->w_doto == 0) {
			if ((lp=lback(curwp->w_dotp)) == curbp->b_linep) {
				if (!(f & FFRAND))
					ewprintf("Beginning of buffer");
				return (FALSE);
			}
			curwp->w_dotp  = lp;
			curwp->w_doto  = llength(lp);
			curwp->w_flag |= WFMOVE;
		} else
			curwp->w_doto--;
	}
	return TRUE;
}

/*
 * Go to end of line.
 */
/*ARGSUSED*/
gotoeol(f, n)
{
	curwp->w_doto  = llength(curwp->w_dotp);
	return (TRUE);
}

/*
 * Move cursor forwards. Do the
 * right thing if the count is less than
 * 0. Error if you try to move forward
 * from the end of the buffer.
 */
/*ARGSUSED*/
forwchar(f, n)
register int n;
{
	if (n < 0) return backchar(f, -n);
	while (n--) {
		if (curwp->w_doto == llength(curwp->w_dotp)) {
			curwp->w_dotp  = lforw(curwp->w_dotp);
			if (curwp->w_dotp == curbp->b_linep) {
				curwp->w_dotp = lback(curwp->w_dotp);
				if (!(f & FFRAND))
					ewprintf("End of buffer");
				return FALSE;
			}
			curwp->w_doto  = 0;
			curwp->w_flag |= WFMOVE;
		} else
			curwp->w_doto++;
	}
	return TRUE;
}

/*
 * Go to the beginning of the
 * buffer. Setting WFHARD is conservative,
 * but almost always the case.
 */
gotobob(f, n)
{
	(VOID) setmark(f, n) ;
	curwp->w_dotp  = lforw(curbp->b_linep);
	curwp->w_doto  = 0;
	curwp->w_flag |= WFHARD;
	return TRUE;
}

/*
 * Go to the end of the buffer.
 * Setting WFHARD is conservative, but
 * almost always the case.
 */
gotoeob(f, n)
{
	(VOID) setmark(f, n) ;
	curwp->w_dotp  = lback(curbp->b_linep);
	curwp->w_doto  = llength(curwp->w_dotp);
	curwp->w_flag |= WFHARD;
	return TRUE;
}

/*
 * Move forward by full lines.
 * If the number of lines to move is less
 * than zero, call the backward line function to
 * actually do it. The last command controls how
 * the goal column is set.
 */
/*ARGSUSED*/
forwline(f, n)
{
	register LINE	*dlp;

	if (n < 0)
		return backline(f|FFRAND, -n);
	if ((lastflag&CFCPCN) == 0)		/* Fix goal.		*/
		setgoal();
	thisflag |= CFCPCN;
	if (n == 0) return TRUE;
	dlp = curwp->w_dotp;
	while (dlp!=curbp->b_linep && n--)
		dlp = lforw(dlp);
	curwp->w_flag |= WFMOVE;
	if(dlp==curbp->b_linep) {	/* ^N at end of buffer creates lines (like gnu) */
		if(!(curbp->b_flag&BFCHG)) {	/* first change */
			curbp->b_flag |= BFCHG;
			curwp->w_flag |= WFMODE;
		}
		curwp->w_doto = 0;
		while(n-- >= 0) {
			if((dlp = lallocx(0)) == NULL) return FALSE;
			dlp->l_fp = curbp->b_linep;
			dlp->l_bp = lback(dlp->l_fp);
			dlp->l_bp->l_fp = dlp->l_fp->l_bp = dlp;
		}
		curwp->w_dotp = lback(curbp->b_linep);
	} else {
		curwp->w_dotp  = dlp;
		curwp->w_doto  = getgoal(dlp);
	}
	return TRUE;
}

/*
 * This function is like "forwline", but
 * goes backwards. The scheme is exactly the same.
 * Check for arguments that are less than zero and
 * call your alternate. Figure out the new line and
 * call "movedot" to perform the motion.
 */
/*ARGSUSED*/
backline(f, n)
{
	register LINE	*dlp;

	if (n < 0) return forwline(f|FFRAND, -n);
	if ((lastflag&CFCPCN) == 0)		/* Fix goal.		*/
		setgoal();
	thisflag |= CFCPCN;
	dlp = curwp->w_dotp;
	while (n-- && lback(dlp)!=curbp->b_linep)
		dlp = lback(dlp);
	curwp->w_dotp  = dlp;
	curwp->w_doto  = getgoal(dlp);
	curwp->w_flag |= WFMOVE;
	return TRUE;
}

/*
 * Set the current goal column,
 * which is saved in the external variable "curgoal",
 * to the current cursor column. The column is never off
 * the edge of the screen; it's more like display then
 * show position.
 */
VOID
setgoal() {

	curgoal = getcolpos() - 1;		/* Get the position.	*/
/* we can now display past end of display, don't chop! */
}

/*
 * This routine looks at a line (pointed
 * to by the LINE pointer "dlp") and the current
 * vertical motion goal column (set by the "setgoal"
 * routine above) and returns the best offset to use
 * when a vertical motion is made into the line.
 */
getgoal(dlp) register LINE *dlp; {
	register int	c;
	register int	col;
	register int	newcol;
	register int	dbo;

	col = 0;
	dbo = 0;
	while (dbo != llength(dlp)) {
		c = lgetc(dlp, dbo);
		newcol = col;
		if (c == '\t'
#ifdef	NOTAB
				&& !(curbp->b_flag & BFNOTAB)
#endif
			)
		    newcol |= 0x07;
		else if (ISCTRL(c) != FALSE)
			++newcol;
		++newcol;
		if (newcol > curgoal)
			break;
		col = newcol;
		++dbo;
	}
	return (dbo);
}

/*
 * Scroll forward by a specified number
 * of lines, or by a full page if no argument.
 * The "2" is the window overlap (this is the default
 * value from ITS EMACS). Because the top line in
 * the window is zapped, we have to do a hard
 * update and get it back.
 */
/*ARGSUSED*/
forwpage(f, n)
register int n;
{
	register LINE	*lp;

	if (!(f & FFARG)) {
		n = curwp->w_ntrows - 2;	/* Default scroll.	*/
		if (n <= 0)			/* Forget the overlap	*/
			n = 1;			/* if tiny window.	*/
	} else if (n < 0)
		return backpage(f|FFRAND, -n);
#ifdef	CVMVAS
	else					/* Convert from pages	*/
		n *= curwp->w_ntrows;		/* to lines.		*/
#endif
	lp = curwp->w_linep;
	while (n-- && lforw(lp)!=curbp->b_linep)
		lp = lforw(lp);
	curwp->w_linep = lp;
	curwp->w_flag |= WFHARD;
	/* if in current window, don't move dot */
	for(n = curwp->w_ntrows; n-- && lp!=curbp->b_linep; lp = lforw(lp))
		if(lp==curwp->w_dotp) return TRUE;
	curwp->w_dotp  = curwp->w_linep;
	curwp->w_doto  = 0;
	return TRUE;
}

/*
 * This command is like "forwpage",
 * but it goes backwards. The "2", like above,
 * is the overlap between the two windows. The
 * value is from the ITS EMACS manual. The
 * hard update is done because the top line in
 * the window is zapped.
 */
/*ARGSUSED*/
backpage(f, n)
register int n;
{
	register LINE	*lp;

	if (!(f & FFARG)) {
		n = curwp->w_ntrows - 2;	/* Default scroll.	*/
		if (n <= 0)			/* Don't blow up if the */
			n = 1;			/* window is tiny.	*/
	} else if (n < 0)
		return forwpage(f|FFRAND, -n);
#ifdef	CVMVAS
	else					/* Convert from pages	*/
		n *= curwp->w_ntrows;		/* to lines.		*/
#endif
	lp = curwp->w_linep;
	while (n-- && lback(lp)!=curbp->b_linep)
		lp = lback(lp);
	curwp->w_linep = lp;
	curwp->w_flag |= WFHARD;
	/* if in current window, don't move dot */
	for(n = curwp->w_ntrows; n-- && lp!=curbp->b_linep; lp = lforw(lp))
		if(lp==curwp->w_dotp) return TRUE;
	curwp->w_dotp = curwp->w_linep;
	curwp->w_doto = 0;
	return TRUE;
}

/* These functions are provided for compatibility with Gosling's Emacs.
 *    They are used to scroll the display up (or down) one line at a time.
 */

#ifdef GOSMACS
forw1page(f, n)
int f, n;
{
	if (!(f & FFARG))  {
        	n = 1;
		f = FFUNIV;
	}
	forwpage(f|FFRAND, n);
}

back1page(f, n)
int f, n;
{
	if (!(f & FFARG)) {
        	n = 1;
		f = FFUNIV;
	}
	backpage(f|FFRAND, n);
}
#endif

/*
 * Page the other window. Check to make sure it exists, then
 * nextwind, forwpage and restore window pointers.
 */
pagenext(f, n)
{
	register WINDOW *wp;

	if (wheadp->w_wndp == NULL) {
		ewprintf("No other window");
		return FALSE;
	}
	wp = curwp;
	(VOID) nextwind(f, n);
	(VOID) forwpage(f, n);
	curwp = wp;
	curbp = wp->w_bufp;
	return TRUE;
}

/*
 * Internal set mark routine, used by other functions (daveb).
 */
VOID
isetmark()
{
	curwp->w_markp = curwp->w_dotp;
	curwp->w_marko = curwp->w_doto;
}

/*
 * Set the mark in the current window
 * to the value of dot. A message is written to
 * the echo line.  (ewprintf knows about macros)
 */
/*ARGSUSED*/
setmark(f, n)
{
	isetmark();
	ewprintf("Mark set");
	return TRUE;
}

/*
 * Swap the values of "dot" and "mark" in
 * the current window. This is pretty easy, because
 * all of the hard work gets done by the standard routine
 * that moves the mark about. The only possible
 * error is "no mark".
 */
/*ARGSUSED*/
swapmark(f, n)
{
	register LINE	*odotp;
	register int	odoto;

	if (curwp->w_markp == NULL) {
		ewprintf("No mark in this window");
		return FALSE;
	}
	odotp = curwp->w_dotp;
	odoto = curwp->w_doto;
	curwp->w_dotp  = curwp->w_markp;
	curwp->w_doto  = curwp->w_marko;
	curwp->w_markp = odotp;
	curwp->w_marko = odoto;
	curwp->w_flag |= WFMOVE;
	return TRUE;
}

/*
 * Go to a specific line, mostly for
 * looking up errors in C programs, which give the
 * error a line number. If an argument is present, then
 * it is the line number, else prompt for a line number
 * to use.
 */
/*ARGSUSED*/
gotoline(f, n)
register int n;
{
	register LINE	*clp;
	register int	s;
	char		buf[32];

	if (!(f & FFARG)) {
		if ((s=ereply("Goto line: ", buf, sizeof(buf))) != TRUE)
			return s;
		n = atoi(buf);
	}

	if (n > 0) {
		clp = lforw(curbp->b_linep);	/* "clp" is first line	*/
		while (--n > 0) {
			if (lforw(clp) == curbp->b_linep) break;
			clp = lforw(clp);
		}
	} else {
		clp = lback(curbp->b_linep);	/* clp is last line */
		while (n < 0) {
			if (lback(clp) == curbp->b_linep) break;
			clp = lback(clp);
			n++;
		}
	}
	curwp->w_dotp = clp;
	curwp->w_doto = 0;
	curwp->w_flag |= WFMOVE;
	return TRUE;
}
SHAR_EOF
cat << \SHAR_EOF > modes.c
/*
 * Commands to toggle modes. Without an argument, toggle mode.
 * Negitive or zero argument, mode off.	 Positive argument, mode on.
 */

#include "def.h"
#include "kbd.h"

int defb_nmodes = 0;
MAPS *defb_modes[PBMODES] = {&map_table[0]};
int defb_flag = 0;

static int changemode(f, n, mode)
int f, n;
char *mode;
{
    register int i;
    MAPS *m;
    VOID upmodes();

    if((m = name_mode(mode)) == NULL) {
	ewprintf("Can't find mode %s", mode);
	return FALSE;
    }
    if(!(f & FFARG)) {
	for(i=0; i <= curbp->b_nmodes; i++)
	    if(curbp->b_modes[i] == m) {
		n = 0;			/* mode already set */
		break;
	    }
    }
    if(n > 0) {
	for(i=0; i <= curbp->b_nmodes; i++)
	    if(curbp->b_modes[i] == m) return TRUE;	/* mode already set */
	if(curbp->b_nmodes >= PBMODES-1) {
	    ewprintf("Too many modes");
	    return FALSE;
	}
	curbp->b_modes[++(curbp->b_nmodes)] = m;
    } else {
	/* fundamental is b_modes[0] and can't be unset */
	for(i=1; i <= curbp->b_nmodes && m != curbp->b_modes[i]; i++) {}
	if(i > curbp->b_nmodes) return TRUE;		/* mode wasn't set */
	for(; i < curbp->b_nmodes; i++)
	    curbp->b_modes[i] = curbp->b_modes[i+1];
	curbp->b_nmodes--;
    }
    upmodes(curbp);
    return TRUE;
}

indentmode(f, n)
{
    return changemode(f, n, "indent");
}

fillmode(f, n)
{
    return changemode(f, n, "fill");
}

/*
 * Fake the GNU "blink-matching-paren" variable.
 */
blinkparen(f, n)
{
    return changemode(f, n, "blink");
}

#ifdef	NOTAB
notabmode(f, n)
{
    if(changemode(f, n, "notab") == FALSE) return FALSE;
    if(f & FFARG) {
	if(n <= 0) curbp->b_flag &= ~BFNOTAB;
	else curbp->b_flag |= BFNOTAB;
    } else curbp->b_flag ^= BFNOTAB;
    return TRUE;
}
#endif

overwrite(f, n)
int f, n;
{
    if(changemode(f, n, "overwrite") == FALSE) return FALSE;
    if(f & FFARG) {
	if(n <= 0) curbp->b_flag &= ~BFOVERWRITE;
	else curbp->b_flag |= BFOVERWRITE;
    } else curbp->b_flag ^= BFOVERWRITE;
    return TRUE;
}

set_default_mode(f, n)
int f, n;
{
    register int i;
    register MAPS *m;
    char mode[32];

    if(eread("Set Default Mode: ", mode, 32, EFNEW) != TRUE)
    	return ABORT;
    if((m = name_mode(mode)) == NULL) {
    	ewprintf("can't find mode %s", mode);
	return FALSE;
    }
    if(!(f & FFARG)) {
	for(i=0; i <= defb_nmodes; i++)
	    if(defb_modes[i] == m) {
		n = 0;			/* mode already set */
		break;
	    }
    }
    if(n > 0) {
	for(i=0; i <= defb_nmodes; i++)
	    if(defb_modes[i] == m) return TRUE;	/* mode already set */
	if(defb_nmodes >= PBMODES-1) {
	    ewprintf("Too many modes");
	    return FALSE;
	}
	defb_modes[++defb_nmodes] = m;
    } else {
	/* fundamental is defb_modes[0] and can't be unset */
	for(i=1; i <= defb_nmodes && m != defb_modes[i]; i++) {}
	if(i > defb_nmodes) return TRUE;		/* mode wasn't set */
	for(; i < defb_nmodes; i++)
	    defb_modes[i] = defb_modes[i+1];
	defb_nmodes--;
    }
    if(strcmp(mode, "overwrite")==0)
    	if(n<=0) defb_flag &= ~BFOVERWRITE;
	else defb_flag |= BFOVERWRITE;
#ifdef NOTAB
    if(strcmp(mode, "notab")==0)
    	if(n<=0) defb_flag &= ~BFNOTAB;
	else defb_flag |= BFNOTAB;
#endif
    return TRUE;
}
SHAR_EOF
cat << \SHAR_EOF > paragraph.c
/*
 * Code for dealing with paragraphs and filling. Adapted from MicroEMACS 3.6
 * and GNU-ified by mwm at ucbvax.	 Several bug fixes by blarson at usc-oberon.
 */
#include "def.h"

static int	fillcol = 70 ;
#define MAXWORD 256

/*
 * go back to the begining of the current paragraph
 * here we look for a <NL><NL> or <NL><TAB> or <NL><SPACE>
 * combination to delimit the begining of a paragraph
 */
/*ARGSUSED*/
gotobop(f, n)
{
	if (n < 0)	/* the other way...*/
		return gotoeop(f, -n);

	while (n-- > 0) {	/* for each one asked for */

		/* first scan back until we are in a word */
		
		while (backchar(FFRAND, 1) && !inword()) {}
		curwp->w_doto = 0;	/* and go to the B-O-Line */

		/* and scan back until we hit a <NL><SP> <NL><TAB> or <NL><NL> */
		while (lback(curwp->w_dotp) != curbp->b_linep)
			if (llength(lback(curwp->w_dotp))
			    && lgetc(curwp->w_dotp,0) != ' '
			    && lgetc(curwp->w_dotp,0) != '\t')
				curwp->w_dotp = lback(curwp->w_dotp);
			else
				break;
	}
	curwp->w_flag |= WFMOVE;	/* force screen update */
	return TRUE;
}

/*
 * go forword to the end of the current paragraph
 * here we look for a <NL><NL> or <NL><TAB> or <NL><SPACE>
 * combination to delimit the begining of a paragraph
 */
/*ARGSUSED*/
gotoeop(f, n)
{
	if (n < 0)	/* the other way...*/
		return gotobop(f, -n);

	while (n-- > 0) {	/* for each one asked for */

		/* Find the first word on/after the current line */
		curwp->w_doto = 0;
		while(forwchar(FFRAND, 1) && !inword()) {}
		curwp->w_doto = 0;
		curwp->w_dotp = lforw(curwp->w_dotp);
		/* and scan forword until we hit a <NL><SP> or ... */
		while (curwp->w_dotp != curbp->b_linep) {
			if (llength(curwp->w_dotp)
			    && lgetc(curwp->w_dotp,0) != ' '
			    && lgetc(curwp->w_dotp,0) != '\t')
				curwp->w_dotp = lforw(curwp->w_dotp);
			else
				break;
		}
		if(curwp->w_dotp == curbp->b_linep) {
			/* beond end of buffer, cleanup time */
			curwp->w_dotp = lback(curwp->w_dotp);
			curwp->w_doto = llength(curwp->w_dotp);
			break;			
		}
	}
	curwp->w_flag |= WFMOVE;	/* force screen update */
	return TRUE;
}

/*
 * Fill the current paragraph according to the current
 * fill column
 */
/*ARGSUSED*/
fillpara(f, n)
{
	register int	c;		/* current char durring scan	*/
	register int	wordlen;	/* length of current word	*/
	register int	clength;	/* position on line during fill */
	register int	i;		/* index during word copy	*/
	register int	eopflag;	/* Are we at the End-Of-Paragraph? */
	int		firstflag;	/* first word? (needs no space) */
	int		newlength;	/* tentative new line length	*/
	int		eolflag;	/* was at end of line		*/
	LINE		*eopline;	/* pointer to line just past EOP */
	char wbuf[MAXWORD];		/* buffer for current word	*/

	/* record the pointer to the line just past the EOP */
	(VOID) gotoeop(FFRAND, 1);
	if(curwp->w_doto != 0)	{
		/* paragraph ends at end of buffer */
		(VOID) lnewline();
		eopline = lforw(curwp->w_dotp);
	} else	eopline = curwp->w_dotp;

	/* and back top the begining of the paragraph */
	(VOID) gotobop(FFRAND, 1);

	/* initialize various info */
	while (!inword() && forwchar(FFRAND, 1)) {}
	clength = curwp->w_doto;
	wordlen = 0;

	/* scan through lines, filling words */
	firstflag = TRUE;
	eopflag = FALSE;
	while (!eopflag) {
		/* get the next character in the paragraph */
		if (eolflag=(curwp->w_doto == llength(curwp->w_dotp))) {
			c = ' ';
			if (lforw(curwp->w_dotp) == eopline)
				eopflag = TRUE;
		} else
			c = lgetc(curwp->w_dotp, curwp->w_doto);

		/* and then delete it */
		if (ldelete((RSIZE) 1, KNONE) == FALSE && !eopflag)
			return FALSE;

		/* if not a separator, just add it in */
		if (c != ' ' && c != '\t') {
			if (wordlen < MAXWORD - 1)
				wbuf[wordlen++] = c;
			else {
				/* You loose chars beyond MAXWORD if the word
				 * is to long. I'm to lazy to fix it now; it
				 * just silently truncated the word before, so
				 * I get to feel smug.
				 */
				ewprintf("Word too long!");
			}
		} else if (wordlen) {
			/* calculate tenatitive new length with word added */
			newlength = clength + 1 + wordlen;
			/* if at end of line or at doublespace and previous
			 * character was one of '.','?','!' doublespace here.
			 */
			if((eolflag || curwp->w_doto==llength(curwp->w_dotp)
			    || (c=lgetc(curwp->w_dotp,curwp->w_doto))==' '
			    || c=='\t')
			  && ISEOSP(wbuf[wordlen-1])
			  && wordlen<MAXWORD-1)
				wbuf[wordlen++] = ' ';
			/* at a word break with a word waiting */
			if (newlength <= fillcol) {
				/* add word to current line */
				if (!firstflag) {
					(VOID) linsert(1, ' ');
					++clength;
				}
				firstflag = FALSE;
			} else {
				if(curwp->w_doto > 0 &&
				    lgetc(curwp->w_dotp,curwp->w_doto-1)==' ') {
					curwp->w_doto -= 1;
					(VOID) ldelete((RSIZE) 1, KNONE);
				}
				/* start a new line */
				(VOID) lnewline();
				clength = 0;
			}

			/* and add the word in in either case */
			for (i=0; i<wordlen; i++) {
				(VOID) linsert(1, wbuf[i]);
				++clength;
			}
			wordlen = 0;
		}
	}
	/* and add a last newline for the end of our new paragraph */
	(VOID) lnewline();
	/* we realy should wind up where we started, (which is hard to keep
	 * track of) but I think the end of the last line is better than the
	 * begining of the blank line.	 */
	(VOID) backchar(FFRAND, 1);
	return TRUE;
}

/* delete n paragraphs starting with the current one */
/*ARGSUSED*/
killpara(f, n)
{
	register int status;	/* returned status of functions */

	while (n--) {		/* for each paragraph to delete */

		/* mark out the end and begining of the para to delete */
		(VOID) gotoeop(FFRAND, 1);

		/* set the mark here */
		curwp->w_markp = curwp->w_dotp;
		curwp->w_marko = curwp->w_doto;

		/* go to the begining of the paragraph */
		(VOID) gotobop(FFRAND, 1);
		curwp->w_doto = 0;	/* force us to the begining of line */

		/* and delete it */
		if ((status = killregion(FFRAND, 1)) != TRUE)
			return status;

		/* and clean up the 2 extra lines */
		(VOID) ldelete((RSIZE) 1, KFORW);
	}
	return TRUE;
}

/*
 * check to see if we're past fillcol, and if so,
 * justify this line. As a last step, justify the line.
 */
/*ARGSUSED*/
fillword(f, n)
{
	register char	c;
	register int	col, i, nce;

	for (i = col = 0; col <= fillcol; ++i, ++col) {
		if (i == curwp->w_doto) return selfinsert(f, n) ;
		c = lgetc(curwp->w_dotp, i);
		if (c == '\t'
#ifdef	NOTAB
			&& !(curbp->b_flag & BFNOTAB)
#endif
			) col |= 0x07;
		else if (ISCTRL(c) != FALSE) ++col;
	}
	if (curwp->w_doto != llength(curwp->w_dotp)) {
		(VOID) selfinsert(f, n);
		nce = llength(curwp->w_dotp) - curwp->w_doto;
	} else nce = 0;
	curwp->w_doto = i;

	if ((c = lgetc(curwp->w_dotp, curwp->w_doto)) != ' ' && c != '\t')
		do {
			(VOID) backchar(FFRAND, 1);
		} while ((c = lgetc(curwp->w_dotp, curwp->w_doto)) != ' '
		      && c != '\t' && curwp->w_doto > 0);

	if (curwp->w_doto == 0)
		do {
			(VOID) forwchar(FFRAND, 1);
		} while ((c = lgetc(curwp->w_dotp, curwp->w_doto)) != ' '
		      && c != '\t' && curwp->w_doto < llength(curwp->w_dotp));

	(VOID) delwhite(FFRAND, 1);
	(VOID) lnewline();
	i = llength(curwp->w_dotp) - nce;
	curwp->w_doto = i>0 ? i : 0;
	curwp->w_flag |= WFMOVE;
	if (nce == 0 && curwp->w_doto != 0) return fillword(f, n);
	return TRUE;
}

/* Set fill column to n. */
setfillcol(f, n) {
	extern int	getcolpos() ;

	fillcol = ((f & FFARG) ? n : getcolpos());
	ewprintf("Fill column set to %d", fillcol);
	return TRUE;
}
SHAR_EOF
cat << \SHAR_EOF > region.c
/*
 *		Region based commands.
 * The routines in this file
 * deal with the region, that magic space
 * between "." and mark. Some functions are
 * commands. Some functions are just for
 * internal use.
 */
#include	"def.h"

/*
 * Kill the region. Ask "getregion"
 * to figure out the bounds of the region.
 * Move "." to the start, and kill the characters.
 */
/*ARGSUSED*/
killregion(f, n)
{
	register int	s;
	REGION		region;

	if ((s=getregion(&region)) != TRUE)
		return (s);
	if ((lastflag&CFKILL) == 0)		/* This is a kill type	*/
		kdelete();			/* command, so do magic */
	thisflag |= CFKILL;			/* kill buffer stuff.	*/
	curwp->w_dotp = region.r_linep;
	curwp->w_doto = region.r_offset;
	return (ldelete(region.r_size, KFORW));
}

/*
 * Copy all of the characters in the
 * region to the kill buffer. Don't move dot
 * at all. This is a bit like a kill region followed
 * by a yank.
 */
/*ARGSUSED*/
copyregion(f, n)
{
	register LINE	*linep;
	register int	loffs;
	register int	s;
	REGION		region;
	VOID		kdelete();

	if ((s=getregion(&region)) != TRUE)
		return s;
	if ((lastflag&CFKILL) == 0)		/* Kill type command.	*/
		kdelete();
	thisflag |= CFKILL;
	linep = region.r_linep;			/* Current line.	*/
	loffs = region.r_offset;		/* Current offset.	*/
	while (region.r_size--) {
		if (loffs == llength(linep)) {	/* End of line.		*/
			if ((s=kinsert('\n', KFORW)) != TRUE)
				return (s);
			linep = lforw(linep);
			loffs = 0;
		} else {			/* Middle of line.	*/
			if ((s=kinsert(lgetc(linep, loffs), KFORW)) != TRUE)
				return s;
			++loffs;
		}
	}
	return TRUE;
}

/*
 * Lower case region. Zap all of the upper
 * case characters in the region to lower case. Use
 * the region code to set the limits. Scan the buffer,
 * doing the changes. Call "lchange" to ensure that
 * redisplay is done in all buffers.
 */
/*ARGSUSED*/
lowerregion(f, n)
{
	register LINE	*linep;
	register int	loffs;
	register int	c;
	register int	s;
	REGION		region;

	if ((s=getregion(&region)) != TRUE)
		return s;
	lchange(WFHARD);
	linep = region.r_linep;
	loffs = region.r_offset;
	while (region.r_size--) {
		if (loffs == llength(linep)) {
			linep = lforw(linep);
			loffs = 0;
		} else {
			c = lgetc(linep, loffs);
			if (ISUPPER(c) != FALSE)
				lputc(linep, loffs, TOLOWER(c));
			++loffs;
		}
	}
	return TRUE;
}

/*
 * Upper case region. Zap all of the lower
 * case characters in the region to upper case. Use
 * the region code to set the limits. Scan the buffer,
 * doing the changes. Call "lchange" to ensure that
 * redisplay is done in all buffers.
 */
/*ARGSUSED*/
upperregion(f, n)
{
	register LINE	*linep;
	register int	loffs;
	register int	c;
	register int	s;
	REGION		region;
	VOID		lchange();

	if ((s=getregion(&region)) != TRUE)
		return s;
	lchange(WFHARD);
	linep = region.r_linep;
	loffs = region.r_offset;
	while (region.r_size--) {
		if (loffs == llength(linep)) {
			linep = lforw(linep);
			loffs = 0;
		} else {
			c = lgetc(linep, loffs);
			if (ISLOWER(c) != FALSE)
				lputc(linep, loffs, TOUPPER(c));
			++loffs;
		}
	}
	return TRUE;
}

/*
 * This routine figures out the bound of the region
 * in the current window, and stores the results into the fields
 * of the REGION structure. Dot and mark are usually close together,
 * but I don't know the order, so I scan outward from dot, in both
 * directions, looking for mark. The size is kept in a long. At the
 * end, after the size is figured out, it is assigned to the size
 * field of the region structure. If this assignment loses any bits,
 * then we print an error. This is "type independent" overflow
 * checking. All of the callers of this routine should be ready to
 * get an ABORT status, because I might add a "if regions is big,
 * ask before clobberring" flag.
 */
getregion(rp) register REGION *rp; {
	register LINE	*flp;
	register LINE	*blp;
	register long	fsize;			/* Long now.		*/
	register long	bsize;

	if (curwp->w_markp == NULL) {
		ewprintf("No mark set in this window");
		return (FALSE);
	}
	if (curwp->w_dotp == curwp->w_markp) {	/* "r_size" always ok.	*/
		rp->r_linep = curwp->w_dotp;
		if (curwp->w_doto < curwp->w_marko) {
			rp->r_offset = curwp->w_doto;
			rp->r_size = (RSIZE) (curwp->w_marko-curwp->w_doto);
		} else {
			rp->r_offset = curwp->w_marko;
			rp->r_size = (RSIZE) (curwp->w_doto-curwp->w_marko);
		}
		return TRUE;
	}
	flp = blp = curwp->w_dotp;		/* Get region size.	*/
	bsize = curwp->w_doto;
	fsize = llength(flp)-curwp->w_doto+1;
	while (lforw(flp)!=curbp->b_linep || lback(blp)!=curbp->b_linep) {
		if (lforw(flp) != curbp->b_linep) {
			flp = lforw(flp);
			if (flp == curwp->w_markp) {
				rp->r_linep = curwp->w_dotp;
				rp->r_offset = curwp->w_doto;
				return (setsize(rp,
					(RSIZE) (fsize+curwp->w_marko)));
			}
			fsize += llength(flp)+1;
		}
		if (lback(blp) != curbp->b_linep) {
			blp = lback(blp);
			bsize += llength(blp)+1;
			if (blp == curwp->w_markp) {
				rp->r_linep = blp;
				rp->r_offset = curwp->w_marko;
				return (setsize(rp,
					(RSIZE) (bsize-curwp->w_marko)));
			}
		}
	}
	ewprintf("Bug: lost mark");		/* Gak!			*/
	return FALSE;
}

/*
 * Set size, and check for overflow.
 */
setsize(rp, size) register REGION *rp; register RSIZE size; {

	rp->r_size = size;
	if (rp->r_size != size) {
		ewprintf("Region is too large");
		return FALSE;
	}
	return TRUE;
}

#ifdef	PREFIXREGION
/*
 * Implements one of my favorite keyboard macros; put a string at the
 * beginning of a number of lines in a buffer.	The quote string is
 * settable by using set-prefix-string.	 Great for quoting mail, which
 * is the real reason I wrote it, but also has uses for creating bar
 * comments (like the one you're reading) in C code.
 */

#define PREFIXLENGTH 40
static char prefix_string[PREFIXLENGTH] = { '>', '\0' };

/*
 * Prefix the region with whatever is in prefix_string.
 * Leaves dot at the beginning of the line after the end
 * of the region.  If an argument is given, prompts for the
 * line prefix string.
 */

/*ARGSUSED*/
prefixregion(f, n)
{
	register int	s;
	register LINE	*first, *last;
	register int	nline;
	REGION		region;
	char		*prefix = prefix_string;

	if ((f == TRUE) && ((s = setprefix(FFRAND, 1)) != TRUE))
		return s;

	/* get # of lines to affect */
	if ((s = getregion(&region)) != TRUE)
		return (s);
	first = region.r_linep;
	last = (first == curwp->w_dotp) ? curwp->w_markp : curwp->w_dotp;
	for (nline = 1; first != last; nline++)
		first = lforw(first);

	/*move to beginning of region */
	curwp->w_dotp = region.r_linep;
	curwp->w_doto = region.r_offset;

	/* for each line, go to beginning and insert the prefix string */
	while (nline--) {
		(VOID) gotobol(FFRAND, 1);
		for (prefix = prefix_string; *prefix; prefix++)
			(VOID) linsert(1, *prefix);
		(VOID) forwline(FFRAND, 1);
	}
	(VOID) gotobol(FFRAND, 1);
	return TRUE;
}

/*
 * Set prefix string.
 */

/*ARGSUSED*/
setprefix(f, n)
{
	char		buf[PREFIXLENGTH];
	register int	s;

	if (prefix_string[0] == '\0')
		s = ereply("Prefix string: ",buf,sizeof buf);
	else
		s = ereply("Prefix string (default %s): ",
				buf,sizeof buf,prefix_string);
	if (s == TRUE)
		(VOID) strcpy(prefix_string, buf);
	if ((s == FALSE) && (prefix_string[0] != '\0')) /* CR -- use old one */
		s = TRUE;
	return s;
}
#endif
SHAR_EOF
cat << \SHAR_EOF > search.c
/*
 *		Search commands.
 * The functions in this file implement the
 * search commands (both plain and incremental searches
 * are supported) and the query-replace command.
 *
 * The plain old search code is part of the original
 * MicroEMACS "distribution". The incremental search code,
 * and the query-replace code, is by Rich Ellison.
 */
#include	"def.h"
#ifndef NO_MACRO
#include	"macro.h"
#endif

#define SRCH_BEGIN	(0)			/* Search sub-codes.	*/
#define SRCH_FORW	(-1)
#define SRCH_BACK	(-2)
#define SRCH_NOPR	(-3)
#define SRCH_ACCM	(-4)
#define SRCH_MARK	(-5)

typedef struct	{
	int	s_code;
	LINE	*s_dotp;
	int	s_doto;
}	SRCHCOM;

static	SRCHCOM cmds[NSRCH];
static	int	cip;

int	srch_lastdir = SRCH_NOPR;		/* Last search flags.	*/

static VOID	is_cpush();
static VOID	is_lpush();
static VOID	is_pop();
static int	is_peek();
static VOID	is_undo();
static int	is_find();
static VOID	is_prompt();
static VOID	is_dspl();
static int	eq();

/*
 * Search forward.
 * Get a search string from the user, and search for it,
 * starting at ".". If found, "." gets moved to just after the
 * matched characters, and display does all the hard stuff.
 * If not found, it just prints a message.
 */
/*ARGSUSED*/
forwsearch(f, n)
{
	register int	s;

	if ((s=readpattern("Search")) != TRUE)
		return s;
	if (forwsrch() == FALSE) {
		ewprintf("Search failed: \"%s\"", pat);
		return FALSE;
	}
	srch_lastdir = SRCH_FORW;
	return TRUE;
}

/*
 * Reverse search.
 * Get a search string from the	 user, and search, starting at "."
 * and proceeding toward the front of the buffer. If found "." is left
 * pointing at the first character of the pattern [the last character that
 * was matched].
 */
/*ARGSUSED*/
backsearch(f, n)
{
	register int	s;

	if ((s=readpattern("Search backward")) != TRUE)
		return (s);
	if (backsrch() == FALSE) {
		ewprintf("Search failed: \"%s\"", pat);
		return FALSE;
	}
	srch_lastdir = SRCH_BACK;
	return TRUE;
}

/*
 * Search again, using the same search string
 * and direction as the last search command. The direction
 * has been saved in "srch_lastdir", so you know which way
 * to go.
 */
/*ARGSUSED*/
searchagain(f, n)
{
	if (srch_lastdir == SRCH_FORW) {
		if (forwsrch() == FALSE) {
			ewprintf("Search failed: \"%s\"", pat);
			return FALSE;
		}
		return TRUE;
	}
	if (srch_lastdir == SRCH_BACK) {
		if (backsrch() == FALSE) {
			ewprintf("Search failed: \"%s\"", pat);
			return FALSE;
		}
		return TRUE;
	}
	ewprintf("No last search");
	return FALSE;
}

/*
 * Use incremental searching, initially in the forward direction.
 * isearch ignores any explicit arguments.
 */
/*ARGSUSED*/
forwisearch(f, n)
{
	return isearch(SRCH_FORW);
}

/*
 * Use incremental searching, initially in the reverse direction.
 * isearch ignores any explicit arguments.
 */
/*ARGSUSED*/
backisearch(f, n)
{
	return isearch(SRCH_BACK);
}

/*
 * Incremental Search.
 *	dir is used as the initial direction to search.
 *	^S	switch direction to forward
 *	^R	switch direction to reverse
 *	^Q	quote next character (allows searching for ^N etc.)
 *	<ESC>	exit from Isearch
 *	<DEL>	undoes last character typed. (tricky job to do this correctly).
 *	other ^ exit search, don't set mark
 *	else	accumulate into search string
 */
isearch(dir) {
	register int	c;
	register LINE	*clp;
	register int	cbo;
	register int	success;
	int		pptr;
	char		opat[NPAT];
	VOID		ungetkey();

#ifndef NO_MACRO
	if(macrodef) {
	    ewprintf("Can't isearch in macro");
	    return FALSE;
	}
#endif
	for (cip=0; cip<NSRCH; cip++)
		cmds[cip].s_code = SRCH_NOPR;
	(VOID) strcpy(opat, pat);
	cip = 0;
	pptr = -1;
	clp = curwp->w_dotp;
	cbo = curwp->w_doto;
	is_lpush();
	is_cpush(SRCH_BEGIN);
	success = TRUE;
	is_prompt(dir, TRUE, success);
	for (;;) {
		update();
		switch (c = getkey(FALSE)) {
		case CCHR('['):
			srch_lastdir = dir;
			curwp->w_markp = clp;
			curwp->w_marko = cbo;
			ewprintf("Mark set");
			return (TRUE);

		case CCHR('G'):
			if (success != TRUE) {
				while (is_peek() == SRCH_ACCM)
					is_undo(&pptr, &dir);
				success = TRUE;
				is_prompt(dir, pptr < 0, success);
				break;
			}
			curwp->w_dotp = clp;
			curwp->w_doto = cbo;
			curwp->w_flag |= WFMOVE;
			srch_lastdir = dir;
			(VOID) ctrlg(FFRAND, 0);
			(VOID) strcpy(pat, opat);
			return ABORT;

		case CCHR(']'):
		case CCHR('S'):
			if (dir == SRCH_BACK) {
				dir = SRCH_FORW;
				is_lpush();
				is_cpush(SRCH_FORW);
				success = TRUE;
			}
			if (success==FALSE && dir==SRCH_FORW)
				break;
			is_lpush();
			pptr = strlen(pat);
			(VOID) forwchar(FFRAND, 1);
			if (is_find(SRCH_FORW) != FALSE) is_cpush(SRCH_MARK);
			else {
				(VOID) backchar(FFRAND, 1);
				ttbeep();
				success = FALSE;
			}
			is_prompt(dir, pptr < 0, success);
			break;

		case CCHR('R'):
			if (dir == SRCH_FORW) {
				dir = SRCH_BACK;
				is_lpush();
				is_cpush(SRCH_BACK);
				success = TRUE;
			}
			if (success==FALSE && dir==SRCH_BACK)
				break;
			is_lpush();
			pptr = strlen(pat);
			(VOID) backchar(FFRAND, 1);
			if (is_find(SRCH_BACK) != FALSE) is_cpush(SRCH_MARK);
			else {
				(VOID) forwchar(FFRAND, 1);
				ttbeep();
				success = FALSE;
			}
			is_prompt(dir, pptr < 0, success);
			break;

		case CCHR('H'):
		case CCHR('?'):
			is_undo(&pptr, &dir);
			if (is_peek() != SRCH_ACCM) success = TRUE;
			is_prompt(dir, pptr < 0, success);
			break;

		case CCHR('\\'):
		case CCHR('Q'):
			c = (char) getkey(FALSE);
			goto  addchar;
		case CCHR('M'):
			c = CCHR('J');
			goto  addchar;

		default:
			if (ISCTRL(c)) {
				ungetkey(c);
				curwp->w_markp = clp;
				curwp->w_marko = cbo;
				ewprintf("Mark set");
				curwp->w_flag |= WFMOVE;
				return	TRUE;
			}	/* and continue */
		case CCHR('I'):
		case CCHR('J'):
		addchar:
			if (pptr == -1)
				pptr = 0;
			if (pptr == 0)
				success = TRUE;
			pat[pptr++] = c;
			if (pptr == NPAT) {
				ewprintf("Pattern too long");
				return FALSE;
			}
			pat[pptr] = '\0';
			is_lpush();
			if (success != FALSE) {
				if (is_find(dir) != FALSE)
					is_cpush(c);
				else {
					success = FALSE;
					ttbeep();
					is_cpush(SRCH_ACCM);
				}
			} else
				is_cpush(SRCH_ACCM);
			is_prompt(dir, FALSE, success);
		}
	}
	/*NOTREACHED*/
}

static VOID
is_cpush(cmd) register int cmd; {
	if (++cip >= NSRCH)
		cip = 0;
	cmds[cip].s_code = cmd;
}

static VOID
is_lpush() {
	register int	ctp;

	ctp = cip+1;
	if (ctp >= NSRCH)
		ctp = 0;
	cmds[ctp].s_code = SRCH_NOPR;
	cmds[ctp].s_doto = curwp->w_doto;
	cmds[ctp].s_dotp = curwp->w_dotp;
}

static VOID
is_pop() {
	if (cmds[cip].s_code != SRCH_NOPR) {
		curwp->w_doto  = cmds[cip].s_doto;
		curwp->w_dotp  = cmds[cip].s_dotp;
		curwp->w_flag |= WFMOVE;
		cmds[cip].s_code = SRCH_NOPR;
	}
	if (--cip <= 0)
		cip = NSRCH-1;
}

static int
is_peek() {
	return cmds[cip].s_code;
}

/* this used to always return TRUE (the return value was checked) */
static VOID
is_undo(pptr, dir) register int *pptr; register int *dir; {
	register int	redo = FALSE ;
	switch (cmds[cip].s_code) {
	case SRCH_BEGIN:
	case SRCH_NOPR:
		*pptr = -1;
	case SRCH_MARK:
		break;

	case SRCH_FORW:
		*dir = SRCH_BACK;
		redo = TRUE;
		break;

	case SRCH_BACK:
		*dir = SRCH_FORW;
		redo = TRUE;
		break;

	case SRCH_ACCM:
	default:
		*pptr -= 1;
		if (*pptr < 0)
			*pptr = 0;
		pat[*pptr] = '\0';
		break;
	}
	is_pop();
	if (redo) is_undo(pptr, dir);
}

static int
is_find(dir) register int dir; {
	register int	plen, odoto;
	register LINE	*odotp ;

	odoto = curwp->w_doto;
	odotp = curwp->w_dotp;
	plen = strlen(pat);
	if (plen != 0) {
		if (dir==SRCH_FORW) {
			(VOID) backchar(FFARG | FFRAND, plen);
			if (forwsrch() == FALSE) {
				curwp->w_doto = odoto;
				curwp->w_dotp = odotp;
				return FALSE;
			}
			return TRUE;
		}
		if (dir==SRCH_BACK) {
			(VOID) forwchar(FFARG | FFRAND, plen);
			if (backsrch() == FALSE) {
				curwp->w_doto = odoto;
				curwp->w_dotp = odotp;
				return FALSE;
			}
			return TRUE;
		}
		ewprintf("bad call to is_find");
		return FALSE;
	}
	return FALSE;
}

/*
 * If called with "dir" not one of SRCH_FORW
 * or SRCH_BACK, this routine used to print an error
 * message. It also used to return TRUE or FALSE,
 * depending on if it liked the "dir". However, none
 * of the callers looked at the status, so I just
 * made the checking vanish.
 */
static VOID
is_prompt(dir, flag, success) {
	if (dir == SRCH_FORW) {
		if (success != FALSE)
			is_dspl("I-search", flag);
		else
			is_dspl("Failing I-search", flag);
	} else if (dir == SRCH_BACK) {
		if (success != FALSE)
			is_dspl("I-search backward", flag);
		else
			is_dspl("Failing I-search backward", flag);
	} else ewprintf("Broken call to is_prompt");
}

/*
 * Prompt writing routine for the incremental search.
 * The "prompt" is just a string. The "flag" determines
 * whether pat should be printed.
 */
static VOID
is_dspl(prompt, flag) char *prompt; {

	if (flag != FALSE)
		ewprintf("%s: ", prompt);
	else
		ewprintf("%s: %s", prompt, pat);
}

/*
 * Query Replace.
 *	Replace strings selectively.  Does a search and replace operation.
 */
/*ARGSUSED*/
queryrepl(f, n)
{
	register int	s;
	register int	rcnt = 0;	/* Replacements made so far	*/
	register int	plen;		/* length of found string	*/
	char		news[NPAT];	/* replacement string		*/

#ifndef NO_MACRO
	if(macrodef) {
	    ewprintf("Can't query replace in macro");
	    return FALSE;
	}
#endif
	if ((s=readpattern("Query replace")) != TRUE)
		return (s);
	if ((s=ereply("Query replace %s with: ",news, NPAT, pat)) == ABORT)
		return (s);
	if (s == FALSE)
		news[0] = '\0';
	ewprintf("Query replacing %s with %s:", pat, news);
	plen = strlen(pat);

	/*
	 * Search forward repeatedly, checking each time whether to insert
	 * or not.  The "!" case makes the check always true, so it gets put
	 * into a tighter loop for efficiency.
	 */

	while (forwsrch() == TRUE) {
	retry:
		update();
		switch (getkey(FALSE)) {
		case ' ':
			if (lreplace((RSIZE) plen, news, f) == FALSE)
				return (FALSE);
			rcnt++;
			break;

		case '.':
			if (lreplace((RSIZE) plen, news, f) == FALSE)
				return (FALSE);
			rcnt++;
			goto stopsearch;

		case CCHR('G'): /* ^G or ESC */
			(VOID) ctrlg(FFRAND, 0);
		case CCHR('['):
			goto stopsearch;

		case '!':
			do {
				if (lreplace((RSIZE) plen, news, f) == FALSE)
					return (FALSE);
				rcnt++;
			} while (forwsrch() == TRUE);
			goto stopsearch;

		case CCHR('H'):
		case CCHR('?'):		/* To not replace */
			break;

		default:
ewprintf("<SP> replace, [.] rep-end, <DEL> don't, [!] repl rest <ESC> quit");
			goto retry;
		}
	}
stopsearch:
	curwp->w_flag |= WFHARD;
	update();
	if (rcnt == 0)
		ewprintf("(No replacements done)");
	else if (rcnt == 1)
		ewprintf("(1 replacement done)");
	else
		ewprintf("(%d replacements done)", rcnt);
	return TRUE;
}

/*
 * This routine does the real work of a
 * forward search. The pattern is sitting in the external
 * variable "pat". If found, dot is updated, the window system
 * is notified of the change, and TRUE is returned. If the
 * string isn't found, FALSE is returned.
 */
forwsrch() {
	register LINE	*clp;
	register int	cbo;
	register LINE	*tlp;
	register int	tbo;
	char		*pp;
	register int	c;

	clp = curwp->w_dotp;
	cbo = curwp->w_doto;
	for(;;) {
		if (cbo == llength(clp)) {
			if((clp = lforw(clp)) == curbp->b_linep) break;
			cbo = 0;
			c = CCHR('J');
		} else
			c = lgetc(clp, cbo++);
		if (eq(c, pat[0]) != FALSE) {
			tlp = clp;
			tbo = cbo;
			pp  = &pat[1];
			while (*pp != 0) {
				if (tbo == llength(tlp)) {
					tlp = lforw(tlp);
					if (tlp == curbp->b_linep)
						goto fail;
					tbo = 0;
					c = CCHR('J');
				} else
					c = lgetc(tlp, tbo++);
				if (eq(c, *pp++) == FALSE)
					goto fail;
			}
			curwp->w_dotp  = tlp;
			curwp->w_doto  = tbo;
			curwp->w_flag |= WFMOVE;
			return TRUE;
		}
	fail:	;
	}
	return FALSE;
}

/*
 * This routine does the real work of a
 * backward search. The pattern is sitting in the external
 * variable "pat". If found, dot is updated, the window system
 * is notified of the change, and TRUE is returned. If the
 * string isn't found, FALSE is returned.
 */
backsrch() {
	register LINE	*clp;
	register int	cbo;
	register LINE	*tlp;
	register int	tbo;
	register int	c;
	register char	*epp;
	register char	*pp;

	for (epp = &pat[0]; epp[1] != 0; ++epp)
		;
	clp = curwp->w_dotp;
	cbo = curwp->w_doto;
	for (;;) {
		if (cbo == 0) {
			clp = lback(clp);
			if (clp == curbp->b_linep)
				return FALSE;
			cbo = llength(clp)+1;
		}
		if (--cbo == llength(clp))
			c = CCHR('J');
		else
			c = lgetc(clp,cbo);
		if (eq(c, *epp) != FALSE) {
			tlp = clp;
			tbo = cbo;
			pp  = epp;
			while (pp != &pat[0]) {
				if (tbo == 0) {
					tlp = lback(tlp);
					if (tlp == curbp->b_linep)
						goto fail;
					tbo = llength(tlp)+1;
				}
				if (--tbo == llength(tlp))
					c = CCHR('J');
				else
					c = lgetc(tlp,tbo);
				if (eq(c, *--pp) == FALSE)
					goto fail;
			}
			curwp->w_dotp  = tlp;
			curwp->w_doto  = tbo;
			curwp->w_flag |= WFMOVE;
			return TRUE;
		}
	fail:	;
	}
	/*NOTREACHED*/
}

/*
 * Compare two characters.
 * The "bc" comes from the buffer.
 * It has its case folded out. The
 * "pc" is from the pattern.
 */
static int
eq(bc, pc)
register int bc, pc;
{
	bc = CHARMASK(bc);
	pc = CHARMASK(pc);
	if (bc == pc) return TRUE;
	if (ISUPPER(bc)) return TOLOWER(bc) == pc;
	if (ISUPPER(pc)) return bc == TOLOWER(pc);
	return FALSE;
}

/*
 * Read a pattern.
 * Stash it in the external variable "pat". The "pat" is
 * not updated if the user types in an empty line. If the user typed
 * an empty line, and there is no old pattern, it is an error.
 * Display the old pattern, in the style of Jeff Lomicka. There is
 * some do-it-yourself control expansion.
 */
readpattern(prompt) char *prompt; {
	register int	s;
	char		tpat[NPAT];

	if (tpat[0] == '\0') s = ereply("%s: ", tpat, NPAT, prompt);
	else s = ereply("%s: (default %s) ", tpat, NPAT, prompt, pat);

	if (s == TRUE)				/* Specified		*/
		(VOID) strcpy(pat, tpat);
	else if (s==FALSE && pat[0]!=0)		/* CR, but old one	*/
		s = TRUE;
	return s;
}
SHAR_EOF
cat << \SHAR_EOF > version.c
/*
 * This file contains the string that get written
 * out by the emacs-version command.
 */

#define TRUE	1	/* include "def.h" when things get more complicated */

char version[] = "Mg 2a (formerly MicroGnuEmacs)";

/*
 * Display the version. All this does
 * is copy the version string onto the echo line.
 */
/*ARGSUSED*/
showversion(f, n)
int f, n;
{
	ewprintf(version);
	return TRUE;
}
SHAR_EOF
cat << \SHAR_EOF > window.c
/*
 *		Window handling.
 */
#include	"def.h"

/*
 * Reposition dot in the current
 * window to line "n". If the argument is
 * positive, it is that line. If it is negative it
 * is that line from the bottom. If it is 0 the window
 * is centered (this is what the standard redisplay code
 * does).  If GOSREC is undefined, default is 0, so it acts like GNU.
 * If GOSREC is defined, with no argument it defaults to 1
 * and works like in Gosling.
 */
/*ARGSUSED*/
reposition(f, n)
{
#ifndef GOSREC
	curwp->w_force = (f & FFARG) ? (n>=0 ? n+1 : n) : 0;
#else
	curwp->w_force = n;
#endif
	curwp->w_flag |= WFFORCE;
	sgarbf = TRUE;
	return TRUE;
}

/*
 * Refresh the display. A call is made to the
 * "ttresize" entry in the terminal handler, which tries
 * to reset "nrow" and "ncol". They will, however, never
 * be set outside of the NROW or NCOL range. If the display
 * changed size, arrange that everything is redone, then
 * call "update" to fix the display. We do this so the
 * new size can be displayed. In the normal case the
 * call to "update" in "main.c" refreshes the screen,
 * and all of the windows need not be recomputed.
 * Note that when you get to the "display unusable"
 * message, the screen will be messed up. If you make
 * the window bigger again, and send another command,
 * everything will get fixed!
 */
/*ARGSUSED*/
refresh(f, n)
{
	register WINDOW *wp;
	register int	oldnrow;
	register int	oldncol;

	oldnrow = nrow;
	oldncol = ncol;
	ttresize();
	if (nrow!=oldnrow || ncol!=oldncol) {
		wp = wheadp;			/* Find last.		*/
		while (wp->w_wndp != NULL)
			wp = wp->w_wndp;
		if (nrow < wp->w_toprow+3) {	/* Check if too small.	*/
			ewprintf("Display unusable");
			return (FALSE);
		}
		wp->w_ntrows = nrow-wp->w_toprow-2;
		sgarbf = TRUE;
		update();
		ewprintf("New size %d by %d", nrow, ncol);
	} else
		sgarbf = TRUE;
	return TRUE;
}

/*
 * The command to make the next
 * window (next => down the screen)
 * the current window. There are no real
 * errors, although the command does
 * nothing if there is only 1 window on
 * the screen.
 */
/*ARGSUSED*/
nextwind(f, n)
{
	register WINDOW *wp;

	if ((wp=curwp->w_wndp) == NULL)
		wp = wheadp;
	curwp = wp;
	curbp = wp->w_bufp;
	return TRUE;
}

#ifdef	GOSMACS
/* not in Gnu Emacs */
/*
 * This command makes the previous
 * window (previous => up the screen) the
 * current window. There arn't any errors,
 * although the command does not do a lot
 * if there is 1 window.
 */
/*ARGSUSED*/
prevwind(f, n)
{
	register WINDOW *wp1;
	register WINDOW *wp2;

	wp1 = wheadp;
	wp2 = curwp;
	if (wp1 == wp2)
		wp2 = NULL;
	while (wp1->w_wndp != wp2)
		wp1 = wp1->w_wndp;
	curwp = wp1;
	curbp = wp1->w_bufp;
	return TRUE;
}
#endif

/*
 * This command makes the current
 * window the only window on the screen.
 * Try to set the framing
 * so that "." does not have to move on
 * the display. Some care has to be taken
 * to keep the values of dot and mark
 * in the buffer structures right if the
 * distruction of a window makes a buffer
 * become undisplayed.
 */
/*ARGSUSED*/
onlywind(f, n)
{
	register WINDOW *wp;
	register LINE	*lp;
	register int	i;

	while (wheadp != curwp) {
		wp = wheadp;
		wheadp = wp->w_wndp;
		if (--wp->w_bufp->b_nwnd == 0) {
			wp->w_bufp->b_dotp  = wp->w_dotp;
			wp->w_bufp->b_doto  = wp->w_doto;
			wp->w_bufp->b_markp = wp->w_markp;
			wp->w_bufp->b_marko = wp->w_marko;
		}
		free((char *) wp);
	}
	while (curwp->w_wndp != NULL) {
		wp = curwp->w_wndp;
		curwp->w_wndp = wp->w_wndp;
		if (--wp->w_bufp->b_nwnd == 0) {
			wp->w_bufp->b_dotp  = wp->w_dotp;
			wp->w_bufp->b_doto  = wp->w_doto;
			wp->w_bufp->b_markp = wp->w_markp;
			wp->w_bufp->b_marko = wp->w_marko;
		}
		free((char *) wp);
	}
	lp = curwp->w_linep;
	i  = curwp->w_toprow;
	while (i!=0 && lback(lp)!=curbp->b_linep) {
		--i;
		lp = lback(lp);
	}
	curwp->w_toprow = 0;
	curwp->w_ntrows = nrow-2;		/* 2 = mode, echo.	*/
	curwp->w_linep	= lp;
	curwp->w_flag  |= WFMODE|WFHARD;
	return TRUE;
}

/*
 * Split the current window. A window
 * smaller than 3 lines cannot be split.
 * The only other error that is possible is
 * a "malloc" failure allocating the structure
 * for the new window.
 */
/*ARGSUSED*/
splitwind(f, n)
{
	register WINDOW *wp;
	register LINE	*lp;
	register int	ntru;
	register int	ntrd;
	int		ntrl;
	WINDOW		*wp1, *wp2;

	if (curwp->w_ntrows < 3) {
		ewprintf("Cannot split a %d line window", curwp->w_ntrows);
		return (FALSE);
	}
	if ((wp = (WINDOW *)malloc(sizeof(WINDOW))) == NULL) {
		ewprintf("Can't get %d", sizeof(WINDOW));
		return (FALSE);
	}
	++curbp->b_nwnd;			/* Displayed twice.	*/
	wp->w_bufp  = curbp;
	wp->w_dotp  = curwp->w_dotp;
	wp->w_doto  = curwp->w_doto;
	wp->w_markp = curwp->w_markp;
	wp->w_marko = curwp->w_marko;
	wp->w_flag  = 0;
	wp->w_force = 0;
	ntru = (curwp->w_ntrows-1) / 2;		/* Upper size		*/
	ntrl = (curwp->w_ntrows-1) - ntru;	/* Lower size		*/
	lp = curwp->w_linep;
	ntrd = 0;
	while (lp != curwp->w_dotp) {
		++ntrd;
		lp = lforw(lp);
	}
	lp = curwp->w_linep;
	if (ntrd <= ntru) {			/* Old is upper window. */
		if (ntrd == ntru)		/* Hit mode line.	*/
			lp = lforw(lp);
		curwp->w_ntrows = ntru;
		wp->w_wndp = curwp->w_wndp;
		curwp->w_wndp = wp;
		wp->w_toprow = curwp->w_toprow+ntru+1;
		wp->w_ntrows = ntrl;
	} else {				/* Old is lower window	*/
		wp1 = NULL;
		wp2 = wheadp;
		while (wp2 != curwp) {
			wp1 = wp2;
			wp2 = wp2->w_wndp;
		}
		if (wp1 == NULL)
			wheadp = wp;
		else
			wp1->w_wndp = wp;
		wp->w_wndp   = curwp;
		wp->w_toprow = curwp->w_toprow;
		wp->w_ntrows = ntru;
		++ntru;				/* Mode line.		*/
		curwp->w_toprow += ntru;
		curwp->w_ntrows	 = ntrl;
		while (ntru--)
			lp = lforw(lp);
	}
	curwp->w_linep = lp;			/* Adjust the top lines */
	wp->w_linep = lp;			/* if necessary.	*/
	curwp->w_flag |= WFMODE|WFHARD;
	wp->w_flag |= WFMODE|WFHARD;
	return TRUE;
}

/*
 * Enlarge the current window.
 * Find the window that loses space. Make
 * sure it is big enough. If so, hack the window
 * descriptions, and ask redisplay to do all the
 * hard work. You don't just set "force reframe"
 * because dot would move.
 */
/*ARGSUSED*/
enlargewind(f, n)
{
	register WINDOW *adjwp;
	register LINE	*lp;
	register int	i;

	if (n < 0)
		return shrinkwind(f, -n);
	if (wheadp->w_wndp == NULL) {
		ewprintf("Only one window");
		return FALSE;
	}
	if ((adjwp=curwp->w_wndp) == NULL) {
		adjwp = wheadp;
		while (adjwp->w_wndp != curwp)
			adjwp = adjwp->w_wndp;
	}
	if (adjwp->w_ntrows <= n) {
		ewprintf("Impossible change");
		return FALSE;
	}
	if (curwp->w_wndp == adjwp) {		/* Shrink below.	*/
		lp = adjwp->w_linep;
		for (i=0; i<n && lp!=adjwp->w_bufp->b_linep; ++i)
			lp = lforw(lp);
		adjwp->w_linep	= lp;
		adjwp->w_toprow += n;
	} else {				/* Shrink above.	*/
		lp = curwp->w_linep;
		for (i=0; i<n && lback(lp)!=curbp->b_linep; ++i)
			lp = lback(lp);
		curwp->w_linep	= lp;
		curwp->w_toprow -= n;
	}
	curwp->w_ntrows += n;
	adjwp->w_ntrows -= n;
	curwp->w_flag |= WFMODE|WFHARD;
	adjwp->w_flag |= WFMODE|WFHARD;
	return TRUE;
}

/*
 * Shrink the current window.
 * Find the window that gains space. Hack at
 * the window descriptions. Ask the redisplay to
 * do all the hard work.
 */
shrinkwind(f, n)
{
	register WINDOW *adjwp;
	register LINE	*lp;
	register int	i;

	if (n < 0)
		return enlargewind(f, -n);
	if (wheadp->w_wndp == NULL) {
		ewprintf("Only one window");
		return FALSE;
	}
	/*
	 * Bit of flakiness - KRANDOM means it was an internal call, and
	 * to be trusted implicitly about sizes.
	 */
	if ( !(f & FFRAND) && curwp->w_ntrows <= n) {
		ewprintf("Impossible change");
		return (FALSE);
	}
	if ((adjwp=curwp->w_wndp) == NULL) {
		adjwp = wheadp;
		while (adjwp->w_wndp != curwp)
			adjwp = adjwp->w_wndp;
	}
	if (curwp->w_wndp == adjwp) {		/* Grow below.		*/
		lp = adjwp->w_linep;
		for (i=0; i<n && lback(lp)!=adjwp->w_bufp->b_linep; ++i)
			lp = lback(lp);
		adjwp->w_linep	= lp;
		adjwp->w_toprow -= n;
	} else {				/* Grow above.		*/
		lp = curwp->w_linep;
		for (i=0; i<n && lp!=curbp->b_linep; ++i)
			lp = lforw(lp);
		curwp->w_linep	= lp;
		curwp->w_toprow += n;
	}
	curwp->w_ntrows -= n;
	adjwp->w_ntrows += n;
	curwp->w_flag |= WFMODE|WFHARD;
	adjwp->w_flag |= WFMODE|WFHARD;
	return (TRUE);
}

/*
 * Delete current window. Call shrink-window to do the screen
 * updating, then throw away the window.
 */
/*ARGSUSED*/
delwind(f, n)
{
	register WINDOW *wp, *nwp;

	wp = curwp;			/* Cheap...		*/
	/* shrinkwind returning false means only one window... */
	if (shrinkwind(FFRAND, wp->w_ntrows + 1) == FALSE)
		return FALSE;
	if (--wp->w_bufp->b_nwnd == 0) {
		wp->w_bufp->b_dotp  = wp->w_dotp;
		wp->w_bufp->b_doto  = wp->w_doto;
		wp->w_bufp->b_markp = wp->w_markp;
		wp->w_bufp->b_marko = wp->w_marko;
	}
	/* since shrinkwind did't crap out, we know we have a second window */
	if (wp == wheadp) wheadp = curwp = wp->w_wndp;
	else if ((curwp = wp->w_wndp) == NULL) curwp = wheadp;
	curbp = curwp->w_bufp;
	for (nwp = wheadp; nwp != NULL; nwp = nwp->w_wndp)
		if (nwp->w_wndp == wp) {
			nwp->w_wndp = wp->w_wndp;
			break ;
		}
	free((char *) wp);
	return TRUE;
}
/*
 * Pick a window for a pop-up.
 * Split the screen if there is only
 * one window. Pick the uppermost window that
 * isn't the current window. An LRU algorithm
 * might be better. Return a pointer, or
 * NULL on error.
 */
WINDOW	*
wpopup() {
	register WINDOW *wp;

	if (wheadp->w_wndp == NULL
	&& splitwind(FFRAND, 0) == FALSE)
		return NULL;
	wp = wheadp;				/* Find window to use	*/
	while (wp!=NULL && wp==curwp)
		wp = wp->w_wndp;
	return wp;
}
SHAR_EOF
cat << \SHAR_EOF > word.c
/*
 *		Word mode commands.
 * The routines in this file
 * implement commands that work word at
 * a time. There are all sorts of word mode
 * commands.
 */
#include	"def.h"

/*
 * Move the cursor backward by
 * "n" words. All of the details of motion
 * are performed by the "backchar" and "forwchar"
 * routines.
 */
/*ARGSUSED*/
backword(f, n)
{
	if (n < 0) return forwword(f | FFRAND, -n);
	if (backchar(FFRAND, 1) == FALSE)
		return FALSE;
	while (n--) {
		while (inword() == FALSE) {
			if (backchar(FFRAND, 1) == FALSE)
				return TRUE;
		}
		while (inword() != FALSE) {
			if (backchar(FFRAND, 1) == FALSE)
				return TRUE;
		}
	}
	return forwchar(FFRAND, 1);
}

/*
 * Move the cursor forward by
 * the specified number of words. All of the
 * motion is done by "forwchar".
 */
/*ARGSUSED*/
forwword(f, n)
{
	if (n < 0)
		return backword(f | FFRAND, -n);
	while (n--) {
		while (inword() == FALSE) {
			if (forwchar(FFRAND, 1) == FALSE)
				return TRUE;
		}
		while (inword() != FALSE) {
			if (forwchar(FFRAND, 1) == FALSE)
				return TRUE;
		}
	}
	return TRUE;
}

/*
 * Move the cursor forward by
 * the specified number of words. As you move,
 * convert any characters to upper case.
 */
/*ARGSUSED*/
upperword(f, n)
{
	register int	c;

	if (n < 0) return FALSE;
	while (n--) {
		while (inword() == FALSE) {
			if (forwchar(FFRAND, 1) == FALSE)
				return TRUE;
		}
		while (inword() != FALSE) {
			c = lgetc(curwp->w_dotp, curwp->w_doto);
			if (ISLOWER(c) != FALSE) {
				c = TOUPPER(c);
				lputc(curwp->w_dotp, curwp->w_doto, c);
				lchange(WFHARD);
			}
			if (forwchar(FFRAND, 1) == FALSE)
				return TRUE;
		}
	}
	return TRUE;
}

/*
 * Move the cursor forward by
 * the specified number of words. As you move
 * convert characters to lower case.
 */
/*ARGSUSED*/
lowerword(f, n)
{
	register int	c;

	if (n < 0) return FALSE;
	while (n--) {
		while (inword() == FALSE) {
			if (forwchar(FFRAND, 1) == FALSE)
				return TRUE;
		}
		while (inword() != FALSE) {
			c = lgetc(curwp->w_dotp, curwp->w_doto);
			if (ISUPPER(c) != FALSE) {
				c = TOLOWER(c);
				lputc(curwp->w_dotp, curwp->w_doto, c);
				lchange(WFHARD);
			}
			if (forwchar(FFRAND, 1) == FALSE)
				return TRUE;
		}
	}
	return TRUE;
}

/*
 * Move the cursor forward by
 * the specified number of words. As you move
 * convert the first character of the word to upper
 * case, and subsequent characters to lower case. Error
 * if you try and move past the end of the buffer.
 */
/*ARGSUSED*/
capword(f, n)
{
	register int	c;
	VOID		lchange();

	if (n < 0) return FALSE;
	while (n--) {
		while (inword() == FALSE) {
			if (forwchar(FFRAND, 1) == FALSE)
				return TRUE;
		}
		if (inword() != FALSE) {
			c = lgetc(curwp->w_dotp, curwp->w_doto);
			if (ISLOWER(c) != FALSE) {
				c = TOUPPER(c);
				lputc(curwp->w_dotp, curwp->w_doto, c);
				lchange(WFHARD);
			}
			if (forwchar(FFRAND, 1) == FALSE)
				return TRUE;
			while (inword() != FALSE) {
				c = lgetc(curwp->w_dotp, curwp->w_doto);
				if (ISUPPER(c) != FALSE) {
					c = TOLOWER(c);
					lputc(curwp->w_dotp, curwp->w_doto, c);
					lchange(WFHARD);
				}
				if (forwchar(FFRAND, 1) == FALSE)
					return TRUE;
			}
		}
	}
	return TRUE;
}

/*
 * Kill forward by "n" words.
 */
/*ARGSUSED*/
delfword(f, n)
{
	register RSIZE	size;
	register LINE	*dotp;
	register int	doto;

	if (n < 0)
		return FALSE;
	if ((lastflag&CFKILL) == 0)		/* Purge kill buffer.	*/
		kdelete();
	thisflag |= CFKILL;
	dotp = curwp->w_dotp;
	doto = curwp->w_doto;
	size = 0;
	while (n--) {
		while (inword() == FALSE) {
			if (forwchar(FFRAND, 1) == FALSE)
				goto out;	/* Hit end of buffer.	*/
			++size;
		}
		while (inword() != FALSE) {
			if (forwchar(FFRAND, 1) == FALSE)
				goto out;	/* Hit end of buffer.	*/
			++size;
		}
	}
out:
	curwp->w_dotp = dotp;
	curwp->w_doto = doto;
	return (ldelete(size, KFORW));
}

/*
 * Kill backwards by "n" words. The rules
 * for success and failure are now different, to prevent
 * strange behavior at the start of the buffer. The command
 * only fails if something goes wrong with the actual delete
 * of the characters. It is successful even if no characters
 * are deleted, or if you say delete 5 words, and there are
 * only 4 words left. I considered making the first call
 * to "backchar" special, but decided that that would just
 * be wierd. Normally this is bound to "M-Rubout" and
 * to "M-Backspace".
 */
/*ARGSUSED*/
delbword(f, n)
{
	register RSIZE	size;
	VOID		kdelete();

	if (n < 0) return FALSE;
	if ((lastflag&CFKILL) == 0)		/* Purge kill buffer.	*/
		kdelete();
	thisflag |= CFKILL;
	if (backchar(FFRAND, 1) == FALSE)
		return (TRUE);			/* Hit buffer start.	*/
	size = 1;				/* One deleted.		*/
	while (n--) {
		while (inword() == FALSE) {
			if (backchar(FFRAND, 1) == FALSE)
				goto out;	/* Hit buffer start.	*/
			++size;
		}
		while (inword() != FALSE) {
			if (backchar(FFRAND, 1) == FALSE)
				goto out;	/* Hit buffer start.	*/
			++size;
		}
	}
	if (forwchar(FFRAND, 1) == FALSE)
		return FALSE;
	--size;					/* Undo assumed delete. */
out:
	return ldelete(size, KBACK);
}

/*
 * Return TRUE if the character at dot
 * is a character that is considered to be
 * part of a word. The word character list is hard
 * coded. Should be setable.
 */
inword() {
/* can't use lgetc in ISWORD due to bug in OSK cpp */
	return curwp->w_doto != llength(curwp->w_dotp) && 
		ISWORD(curwp->w_dotp->l_text[curwp->w_doto]);
}

SHAR_EOF
#	End of shell archive
exit 0
-------



More information about the Comp.sources.misc mailing list