[alt.sources] Vile 12/17 - vi feel-alike

pgf@cayman.COM (Paul Fox) (06/08/91)

#!/bin/sh
# this is vileshar.12 (part 12 of Vile)
# do not concatenate these parts, unpack them in order with /bin/sh
# file readme.news continued
#
if test ! -r _shar_seq_.tmp; then
	echo 'Please unpack part 1 first!'
	exit 1
fi
(read Scheck
 if test "$Scheck" != 12; then
	echo Please unpack part "$Scheck" next!
	exit 1
 else
	exit 0
 fi
) < _shar_seq_.tmp || exit 1
echo 'x - continuing file readme.news'
sed 's/^X//' << 'SHAR_EOF' >> 'readme.news' &&
X	buffers,  windows, and files.
X
X	The number of lines may be selected by the user when the editor is
X	invoked (-L40 for 40 lines), and the font size and the number of
X	columns is then set by the size of the window the user chooses
X	from the NeWS interface.
X
The primary distribution mechanism used to offload i/o is selective time
buffering. Printable text is displayed locally as it is issued by the
user, being flushed to the remote machine (where the actual editor is
running) after a suitable period has elapsed during which no input is
received. For normal text this period is 2 seconds. Commands (control
characters and escape sequences) are also buffered in time, with a
period of .4 seconds. Certain prefixes are also defined (^X and escape),
so that these characters are assigned long timeouts and the following
character a short timeout. The editor controls this timing data, and has
the capability to throw the system into "immediate" mode, where every
character is treated as a command. This is done temporarily to support
the incremental search and query replace functions of emacs. The
protocol puts an absolute upper bound on the number of packets that can
be transmitted per second, which would be 1 every .4 seconds if the user
entered commands at exactly that rate. Issuing commands at a faster or
slower rate will improve the performance (reduce the packet load).
X
As local printable text is inserted, a different font is used to
indicate that the remote machine has yet to confirm the entry. After a
timeout has occurred and a packet of text and commands sent to the
remote machine, the screen update will be done with the normal font.
X
Screen updates are optimized so that an update is done only when
absolutely necessary. To help with apparent performance in low bandwidth
situations, the remote display driver will search for and optimize out
any scrolls or reverse scrolls. Essentially what has been implemented
here (in a limited fashion) is a NeWS distributed implementation of
curses, although as yet no attempt has been made to obey the syntax of
curses.
X
The Mouse
X
The mouse is used to position the cursor and for cutting and pasting
text.  It should be used in preference to the motion keys because it is
much more efficient, sending far fewer packets to the remote machine. 
It should not be used in keyboard macros however, as it's movement
commands are not relative. 
X
Text is entered at the block cursor, NOT the mouse cursor.  To move the
block cursor, move the mouse cursor to the desired position.  After the
mouse has been motionless for 1/5 second, the remote machine will adjust
the block cursor to match the mouse position.  While waiting for the
remote machine to respond the mouse cursor takes the form of an
hourglass, to indicate to the user that it is not safe to type. The
alignment of cursors is not exact, since the text cursor positioning
must take tabs into account and can't be placed past the end of a line. 
This is a feature, not a bug
X
The left mouse button is used for cutting out text.  To cut out a block
of text, move the mouse cursor to the starting location, press the left
button and move the mouse to the ending location (keeping the button
depressed).  When the button is released the text will be wiped out
(moved to the kill buffer).  The middle mouse button will yank text from
the kill buffer and insert it at the current (mouse cursor) location.
The right mouse button invokes a walking menu. This mouse behavior is
not in the standard Sunview style, and as a result it is not (yet)
possible to exchange information between the editor and other NeWS
applications.
X
Function Keys
X
The user is free to bind the function keys to various emacs commands. If
this is done, it is recommended that the definitions be kept in a file
named .emacsrc to avoid retyping them (the file ..emacsrc is
automatically read when the editor is invoked). The function keys can
also be redefined on the fly using the menu. The shift and control keys
have no effect when used with the function keys.
X
Altering the definitions is only necessary if the default definitions
are not satisfactory. The default definitions for the function keys are:
X
X	  top		  right
X
1)	find file	page up
2)	read file	top of file
3)	insert file	reverse incremental search
4)	view file	page down
5)	save file	end of file
6)	write file	incremental search
7)	rename file	beginning of line
8)	copy region	cursor up
9)	get argument	end of line
10)			cursor left
11)			set mark
12)			cursor right
13)			previous word
14)			cursor down
15)			next word
X
X
"Bugs" and Limitations
X
At present the distribution mechanism is ignorant of key rebindings,
that is, the emacs rebinding function does not alter the timing
information. Care should be taken when binding characters to keys. In
particular, be careful not to rebind the emacs prefix keys ^X or ESC.
Also, since the menu entries send "normal" emacs sequences, rebinding
these sequences should be avoided. Function keys are available for
binding.
X
If for some reason the editing process on the remote machine is killed
by methods other than emacs commands, the window may not be eliminated
(and NeWS might have to be restarted to get rid of it). Currently the
only "correct" ways of terminating are with the window menu "quit" or
"save & exit" choices, or with their equivalent emacs sequences (^X C
and ESC Z). The frame or icon menu "Zap" function will also terminate
the editor, but will not prompt the user for confirmation if changes
have not been saved to files. Do not ^C or otherwise kill the remote
editor.
SHAR_EOF
echo 'File readme.news is complete' &&
chmod 0444 readme.news ||
echo 'restore of readme.news failed'
Wc_c="`wc -c < 'readme.news'`"
test 7014 -eq "$Wc_c" ||
	echo 'readme.news: original size 7014, current size' "$Wc_c"
# ============= region.c ==============
echo 'x - extracting region.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'region.c' &&
/*
X * The routines in this file
X * deal with the region, that magic space
X * between "." and mark. Some functions are
X * commands. Some functions are just for
X * internal use.
X */
#include        <stdio.h>
#include	"estruct.h"
#include        "edef.h"
X
#if	MEGAMAX & ST520
overlay	"region"
#endif
X
/*
X * Kill the region. Ask "getregion"
X * to figure out the bounds of the region.
X * Move "." to the start, and kill the characters.
X */
killregion(f, n)
{
X        register int    s;
X        REGION          region;
X
X	if (curbp->b_mode&MDVIEW)	/* don't allow this command if	*/
X		return(rdonly());	/* we are in read only mode	*/
X        if ((s=getregion(&region)) != TRUE)
X                return (s);
X	kregcirculate(TRUE);
X        kdelete();                      /* command, so do magic */
X        curwp->w_dotp = region.r_linep;
X        curwp->w_doto = region.r_offset;
X	if (fulllineregions)
X		kregflag |= KLINES;
X	s = ldelete(region.r_size, TRUE);
X	ukb = 0;
X        return (s);
}
X
/*
X * Copy all of the characters in the
X * region to the kill buffer. Don't move dot
X * at all. This is a bit like a kill region followed
X * by a yank.
X */
yankregion(f, n)
{
X        register LINE   *linep;
X        register int    loffs;
X        register int    s;
X        REGION          region;
X
X        if ((s=getregion(&region)) != TRUE)
X                return (s);
X	kregcirculate(TRUE);
X        kdelete();
X        linep = region.r_linep;                 /* Current line.        */
X        loffs = region.r_offset;                /* Current offset.      */
X	if (fulllineregions)
X		kregflag |= KLINES|KYANK;
X        while (region.r_size--) {
X                if (loffs == llength(linep)) {  /* End of line.         */
X                        if ((s=kinsert('\n')) != TRUE) {
X				ukb = 0;
X                                return (s);
X			}
X                        linep = lforw(linep);
X                        loffs = 0;
X                } else {                        /* Middle of line.      */
X                        if ((s=kinsert(lgetc(linep, loffs))) != TRUE) {
X				ukb = 0;
X                                return (s);
X			}
X                        ++loffs;
X                }
X        }
X	mlwrite("[region yanked]");
X	ukb = 0;
X        return (TRUE);
}
X
/*
X * shift region left by a tab stop
X */
shiftrregion(f, n)
{
X        register LINE   *linep;
X        register int    loffs;
X        register int    s;
X        REGION          region;
X
X	if (curbp->b_mode&MDVIEW)	/* don't allow this command if	*/
X		return(rdonly());	/* we are in read only mode	*/
X        if ((s=getregion(&region)) != TRUE)
X                return (s);
X        linep = region.r_linep;
X        loffs = region.r_offset;
X	if (loffs != 0) {  /* possibly on first time through */
X		region.r_size -= llength(linep)-loffs+1;
X		loffs = 0;
X		linep = lforw(linep);
X	}
X	s = TRUE;
X	while (region.r_size > 0) {
X		/* adjust r_size now, while line length is right */
X		region.r_size -= llength(linep)+1;
X		if (llength(linep) != 0) {
X			curwp->w_dotp = linep;
X			curwp->w_doto = 0;
X			if ((s = linsert(1,'\t')) != TRUE)
X				return (s);
X		}
X		linep = lforw(linep);
X        }
X	firstnonwhite(f,n);
X	return (TRUE);
}
X
/*
X * shift region left by a tab stop
X */
shiftlregion(f, n)
{
X        register LINE   *linep;
X        register int    loffs;
X        register int    c;
X        register int    s;
X	register int	i;
X        REGION          region;
X
X	if (curbp->b_mode&MDVIEW)	/* don't allow this command if	*/
X		return(rdonly());	/* we are in read only mode	*/
X        if ((s=getregion(&region)) != TRUE)
X                return (s);
X        linep = region.r_linep;
X        loffs = region.r_offset;
X	if (loffs != 0) {  /* possibly on first time through */
X		region.r_size -= llength(linep)-loffs+1;
X		loffs = 0;
X		linep = lforw(linep);
X	}
X	s = TRUE;
X	while (region.r_size > 0) {
X		/* adjust r_size now, while line length is right */
X		region.r_size -= llength(linep)+1;
X		if (llength(linep) != 0) {
X			curwp->w_dotp = linep;
X			curwp->w_doto = 0;
X			if ((c = lgetc(linep,0)) == '\t') { /* delete the tab */
X				i = 1;
X			} else if (c == ' ') {
X				i = 1; 
X				/* after this, i'th char is _not_ a space, 
X					or 0-7 are spaces  */
X				while ((c = lgetc(linep,i)) == ' ' && i < TABVAL)
X					i++;
X				if (i != TABVAL && c == '\t') /* ith char is tab */
X					i++;
X			} else {
X				i = 0;
X			}
X			
X			if ( i!=0 && (s = ldelete((long)i,FALSE)) != TRUE)
X				return (s);
X		}
X		linep = lforw(linep);
X        }
X	firstnonwhite(f,n);
X        return (TRUE);
}
X
_to_lower(c)
{
X	if (isupper(c))
X		return(c ^ DIFCASE);
X	return -1;
}
X
_to_upper(c)
{
X	if (islower(c))
X		return(c ^ DIFCASE);
X	return -1;
}
X
_to_caseflip(c)
{
X	if (isalpha(c))
X		return(c ^ DIFCASE);
X	return -1;
}
X
flipregion(f, n)
{
X	return charprocreg(f,n,_to_caseflip);
}
X
lowerregion(f, n)
{
X	return charprocreg(f,n,_to_lower);
}
X
upperregion(f, n)
{
X	return charprocreg(f,n,_to_upper);
}
X
charprocreg(f, n, func)
int (*func)();
{
X        register LINE   *linep;
X        register int    loffs;
X        register int    c,nc;
X        register int    s;
X        REGION          region;
X
X	if (curbp->b_mode&MDVIEW)	/* don't allow this command if	*/
X		return(rdonly());	/* we are in read only mode	*/
X        if ((s=getregion(&region)) != TRUE)
X                return (s);
X        lchange(WFHARD);
X        linep = region.r_linep;
X        loffs = region.r_offset;
X        while (region.r_size--) {
X                if (loffs == llength(linep)) {
X                        linep = lforw(linep);
X                        loffs = 0;
X                } else {
X                        c = lgetc(linep, loffs);
X			nc = (func)(c);
X			if (nc != -1) {
X				copy_for_undo(linep);
X                                lputc(linep, loffs, nc);
X			}
X                        ++loffs;
X                }
X        }
X        return (TRUE);
}
X
/*
X * This routine figures out the
X * bounds of the region in the current window, and
X * fills in the fields of the "REGION" structure pointed
X * to by "rp". Because the dot and mark are usually very
X * close together, we scan outward from dot looking for
X * mark. This should save time. Return a standard code.
X */
getregion(rp)
register REGION *rp;
{
X        register LINE   *flp;
X        register LINE   *blp;
X        long fsize;
X        long bsize;
X
X        if (curwp->w_mkp == NULL) {
X                mlwrite("No mark set in this window");
X                return FALSE;
X        }
X        if (curwp->w_dotp == curwp->w_mkp) {
X                rp->r_linep = curwp->w_dotp;
X		if (fulllineregions) {
X                        rp->r_offset = 0;
X                        rp->r_size = (long)llength(curwp->w_dotp)+1;
X	                rp->r_endlinep = lforw(curwp->w_dotp);
X                        rp->r_endoffset = 0;
X			return TRUE;
X		}
X                rp->r_endlinep = curwp->w_dotp;
X                if (curwp->w_doto < curwp->w_mko) {
X                        rp->r_offset = curwp->w_doto;
X                        rp->r_size = (long)(curwp->w_mko-curwp->w_doto);
X                        rp->r_endoffset = curwp->w_mko;
X                } else {
X                        rp->r_offset = curwp->w_mko;
X                        rp->r_size = (long)(curwp->w_doto-curwp->w_mko);
X                        rp->r_endoffset = curwp->w_doto;
X                }
X                return TRUE;
X        }
X        blp = curwp->w_dotp;
X        flp = curwp->w_dotp;
X	if (fulllineregions) {
X		bsize = (long)(llength(blp)+1);
X		fsize = (long)(llength(flp)+1);
X	} else {
X		bsize = (long)curwp->w_doto;
X		fsize = (long)(llength(flp)-curwp->w_doto+1);
X	}
X        while (flp!=curbp->b_linep || lback(blp)!=curbp->b_linep) {
X                if (flp != curbp->b_linep) {
X                        flp = lforw(flp);
X                        if (flp == curwp->w_mkp) {
X                                rp->r_linep = curwp->w_dotp;
X				if (fulllineregions) {
X					rp->r_offset = 0;
X					rp->r_size = fsize+
X						    llength(curwp->w_mkp)+1;
X	                                rp->r_endlinep = lforw(flp);
X					rp->r_endoffset = 0;
X	                                return TRUE;
X				}
X				rp->r_offset = curwp->w_doto;
X				rp->r_size = fsize+curwp->w_mko;
X                                rp->r_endlinep = flp;
X				rp->r_endoffset = curwp->w_mko;
X                                return TRUE;
X                        }
X                        fsize += llength(flp)+1;
X                }
X                if (lback(blp) != curbp->b_linep) {
X                        blp = lback(blp);
X                        bsize += llength(blp)+1;
X                        if (blp == curwp->w_mkp) {
X                                rp->r_linep = blp;
X				if (fulllineregions) {
X					rp->r_offset = 0;
X					rp->r_size = bsize;
X	                                rp->r_endlinep = lforw(curwp->w_dotp);
X					rp->r_endoffset = 0;
X	                                return TRUE;
X				}
X                                rp->r_endlinep = curwp->w_dotp;
X				rp->r_endoffset = curwp->w_doto;
X				rp->r_offset = curwp->w_mko;
X				rp->r_size = bsize - curwp->w_mko;
X                                return TRUE;
X                        }
X                }
X        }
X        mlwrite("Bug: lost mark");
X        return FALSE;
}
X
SHAR_EOF
chmod 0444 region.c ||
echo 'restore of region.c failed'
Wc_c="`wc -c < 'region.c'`"
test 9127 -eq "$Wc_c" ||
	echo 'region.c: original size 9127, current size' "$Wc_c"
# ============= search.c ==============
echo 'x - extracting search.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'search.c' &&
/*
X * The functions in this file implement commands that search in the forward
X * and backward directions.
X *  heavily modified by Paul Fox, 1990
X *
X * Aug. 1986 John M. Gamble:
X *	... a limited number of regular expressions - 'any',
X *	'character class', 'closure', 'beginning of line', and
X *	'end of line'.
X *
X *	Replacement metacharacters will have to wait for a re-write of
X *	the replaces function, and a new variation of ldelete().
X *
X *	For those curious as to my references, i made use of
X *	Kernighan & Plauger's "Software Tools."
X *	I deliberately did not look at any published grep or editor
X *	source (aside from this one) for inspiration.  I did make use of
X *	Allen Hollub's bitmap routines as published in Doctor Dobb's Journal,
X *	June, 1985 and modified them for the limited needs of character class
X *	matching.  Any inefficiences, bugs, stupid coding examples, etc.,
X *	are therefore my own responsibility.
X *
X */
X
#include        <stdio.h>
#include	"estruct.h"
#include        "edef.h"
X
#if	LATTICE
#define	void	int
#endif
X
int    mcmatch();
int    readpattern();
#if SEARCH_AND_REPLACE
int    replaces();
#endif
int    nextch();
#if MAGIC
int    mcstr();
int    mceq();
int    cclmake();
int    biteq();
BITMAP   clearbits();
void     setbit();
#endif
X
int lastdirec;
LINE *boundline;
int boundoff;
X
scrforwsearch(f,n)
{
X	return fsearch(f,n,FALSE,TRUE);
}
X
scrbacksearch(f,n)
{
X	return bsearch(f,n,FALSE,TRUE);
}
X
char onlyonemsg[] = "Only one occurence of pattern";
char notfoundmsg[] = "Not found";
X
/*
X * forwsearch -- Search forward.  Get a search string from the user, and
X *	search for the string.  If found, reset the "." to be just after
X *	the match string, and (perhaps) repaint the display.
X */
X
forwsearch(f, n)
int f, n;
{
X	fsearch(f, n, FALSE, NULL);
}
X
/* extra args -- marking if called from globals, and should mark lines, and
X	fromscreen, if the searchpattern is on the screen, so we don't need to
X	ask for it.  */
fsearch(f, n, marking, fromscreen)
int f, n;
{
X	register int status = TRUE;
X	int wrapok;
X	int c;
X	LINE *curdotp;
X	int curoff;
X	int didmark = FALSE;
X
X	if (f && n < 0)
X		return bsearch(f, -n, NULL, NULL);
X
X	wrapok = marking || (curwp->w_bufp->b_mode & MDSWRAP) != 0;
X
X	lastdirec = 0;
X
X	/* Ask the user for the text of a pattern.  If the
X	 * response is TRUE (responses other than FALSE are
X	 * possible), search for the pattern for as long as
X	 * n is positive (n == 0 will go through once, which
X	 * is just fine).
X	 *
X	 * If "marking", then we were called to do line marking for the
X	 *  global command.
X	 */
X	if (!marking && (status = readpattern("Search: ", &pat[0],
X					TRUE, lastkey, fromscreen)) != TRUE) {
X		return status;
X	}
X		
X	curdotp = curwp->w_dotp;
X	curoff = curwp->w_doto;
X	setboundry(wrapok,curwp->w_dotp,curwp->w_doto,FORWARD);
X	do {
X		nextch(&(curwp->w_dotp),&(curwp->w_doto), FORWARD,wrapok);
X		status = thescanner(&pat[0], FORWARD, PTBEG, wrapok);
X		if (status == ABORT) {
X			TTbeep();
X			mlwrite("[Aborted]");
X			curwp->w_dotp = curdotp;
X			curwp->w_doto = curoff;
X			return status;
X		}
X		/* if found, mark the line */
X		if (status && marking) {
X			/* if we were on a match when we started, then
X				thescanner returns TRUE, even though it's
X				on a boundary. quit if we find ourselves
X				marking a line twice */
X			if (lismarked(curwp->w_dotp))
X				break;
X			lsetmarked(curwp->w_dotp);
X			/* and, so the next nextch gets to next line */
X			curwp->w_doto = llength(curwp->w_dotp);
X			didmark = TRUE;
X		}
X	} while ((marking || --n > 0) && status == TRUE);
X	
X	if (!marking && !status)
X		nextch(&(curwp->w_dotp),&(curwp->w_doto), REVERSE,wrapok);
X		
X	if (marking) {  /* restore dot and offset */
X		curwp->w_dotp = curdotp;
X		curwp->w_doto = curoff;
X	} else if (status) {
X		savematch();
X		if (curwp->w_dotp == curdotp && curwp->w_doto == curoff) {
X			mlwrite(onlyonemsg);
X			TTbeep();
X		}
X	}
X
X	/* Complain if not there.  */
X	if ((marking && didmark == FALSE) ||
X				(!marking && status == FALSE)) {
X		mlwrite(notfoundmsg);
X		TTbeep();
X		return FALSE;
X	}
X	
X	return TRUE;
}
X
/*
X * forwhunt -- Search forward for a previously acquired search string.
X *	If found, reset the "." to be just after the match string,
X *	and (perhaps) repaint the display.
X */
X
forwhunt(f, n)
int f, n;	/* default flag / numeric argument */
{
X	register int status = TRUE;
X	int wrapok;
X	LINE *curdotp;
X	int curoff;
X	
X	wrapok = (curwp->w_bufp->b_mode & MDSWRAP) != 0;
X
X	if (n < 0)		/* search backwards */
X		return(backhunt(f, -n));
X
X	/* Make sure a pattern exists, or that we didn't switch
X	 * into MAGIC mode until after we entered the pattern.
X	 */
X	if (pat[0] == '\0')
X	{
X		mlwrite("No pattern set");
X		return FALSE;
X	}
X	/* mlwrite("Searching ahead..."); */
#if	MAGIC
X	if ((curwp->w_bufp->b_mode & MDMAGIC) && (mcpat[0].mc_type == MCNIL))
X	{
X		if (!mcstr())
X			return FALSE;
X	}
#endif
X
X	/* Search for the pattern for as long as
X	 * n is positive (n == 0 will go through once, which
X	 * is just fine).
X	 */
X	curdotp = curwp->w_dotp;
X	curoff = curwp->w_doto;
X	setboundry(wrapok,curwp->w_dotp,curwp->w_doto,FORWARD);
X	do {
X		nextch(&(curwp->w_dotp),&(curwp->w_doto),FORWARD,wrapok);
X		status = thescanner(&pat[0], FORWARD, PTBEG, wrapok);
X	} while ((--n > 0) && status == TRUE);
X
X	/* Save away the match, or complain if not there.  */
X	if (status == TRUE) {
X		savematch();
X		if (curwp->w_dotp == curdotp && curwp->w_doto == curoff) {
X			mlwrite(onlyonemsg);
X			TTbeep();
X		}
X	} else if (status == FALSE) {
X		nextch(&(curwp->w_dotp),&(curwp->w_doto),REVERSE,wrapok);
X		mlwrite(notfoundmsg);
X		TTbeep();
X	} else if (status == ABORT) {
X		TTbeep();
X		mlwrite("[Aborted]");
X		curwp->w_dotp = curdotp;
X		curwp->w_doto = curoff;
X		return status;
X	}
X
X	return(status);
}
X
/*
X * backsearch -- Reverse search.  Get a search string from the user, and
X *	search, starting at "." and proceeding toward the front of the buffer.
X *	If found "." is left pointing at the first character of the pattern
X *	(the last character that was matched).
X */
backsearch(f, n)
int f, n;
{
X	return bsearch(f, n, FALSE, NULL);
}
X
bsearch(f, n, dummy, fromscreen)
int f, n;	/* default flag / numeric argument */
{
X	register int status = TRUE;
X	int wrapok;
X	LINE *curdotp;
X	int curoff;
X	
X	if (n < 0)
X		return fsearch(f, -n, NULL, fromscreen);
X
X	wrapok = (curwp->w_bufp->b_mode & MDSWRAP) != 0;
X
X	lastdirec = 1;
X
X	/* Ask the user for the text of a pattern.  If the
X	 * response is TRUE (responses other than FALSE are
X	 * possible), search for the pattern for as long as
X	 * n is positive (n == 0 will go through once, which
X	 * is just fine).
X	 */
X	if ((status = readpattern("Reverse search: ", &pat[0],
X					TRUE, lastkey, fromscreen)) == TRUE) {
X		curdotp = curwp->w_dotp;
X		curoff = curwp->w_doto;
X		setboundry(wrapok,curwp->w_dotp,curwp->w_doto,REVERSE);
X		do {
X			nextch(&(curwp->w_dotp),&(curwp->w_doto),REVERSE,
X									wrapok);
X			status = thescanner(&tap[0], REVERSE, PTBEG, wrapok);
X		} while ((--n > 0) && status == TRUE);
X
X		/* Save away the match, or complain if not there.  */
X		if (status == TRUE)
X			savematch();
X			if (curwp->w_dotp == curdotp && curwp->w_doto == curoff) {
X				mlwrite(onlyonemsg);
X				TTbeep();
X			}
X		else if (status == FALSE) {
X			nextch(&(curwp->w_dotp),&(curwp->w_doto),FORWARD,
X									wrapok);
X			mlwrite(notfoundmsg);
X			TTbeep();
X		} else if (status == ABORT) {
X			TTbeep();
X			mlwrite("[Aborted]");
X			curwp->w_dotp = curdotp;
X			curwp->w_doto = curoff;
X			return status;
X		}
X	}
X	return(status);
}
X
/*
X * backhunt -- Reverse search for a previously acquired search string,
X *	starting at "." and proceeding toward the front of the buffer.
X *	If found "." is left pointing at the first character of the pattern
X *	(the last character that was matched).
X */
backhunt(f, n)
int f, n;	/* default flag / numeric argument */
{
X	register int status = TRUE;
X	int wrapok;
X	LINE *curdotp;
X	int curoff;
X	
X	wrapok = (curwp->w_bufp->b_mode & MDSWRAP) != 0;
X
X	if (n < 0)
X		return(forwhunt(f, -n));
X
X	/* Make sure a pattern exists, or that we didn't switch
X	 * into MAGIC mode until after we entered the pattern.
X	 */
X	if (tap[0] == '\0')
X	{
X		mlwrite("No pattern set");
X		return FALSE;
X	}
X	/* mlwrite("Searching back..."); */
#if	MAGIC
X	if ((curwp->w_bufp->b_mode & MDMAGIC) && (tapcm[0].mc_type == MCNIL))
X	{
X		if (!mcstr())
X			return FALSE;
X	}
#endif
X
X	/* Go search for it for as long as
X	 * n is positive (n == 0 will go through once, which
X	 * is just fine).
X	 */
X
X	curdotp = curwp->w_dotp;
X	curoff = curwp->w_doto;
X	setboundry(wrapok,curwp->w_dotp,curwp->w_doto,REVERSE);
X	do {
X		nextch(&(curwp->w_dotp),&(curwp->w_doto),REVERSE,wrapok);
X		status = thescanner(&tap[0], REVERSE, PTBEG, wrapok);
X	} while ((--n > 0) && status == TRUE);
X
X	/* Save away the match, or complain
X	 * if not there.
X	 */
X	if (status == TRUE) {
X		savematch();
X		if (curwp->w_dotp == curdotp && curwp->w_doto == curoff) {
X			mlwrite(onlyonemsg);
X			TTbeep();
X		}
X	} else if (status == FALSE) {
X		nextch(&(curwp->w_dotp),&(curwp->w_doto), FORWARD,wrapok);
X		mlwrite(notfoundmsg);
X		TTbeep();
X	} else if (status == ABORT) {
X		TTbeep();
X		mlwrite("[Aborted]");
X		curwp->w_dotp = curdotp;
X		curwp->w_doto = curoff;
X		return status;
X	}
X
X	return(status);
}
X
consearch(f,n)
{
X	if (lastdirec == 0)
X		return(forwhunt(f,n));
X	else
X		return(backhunt(f,n));
}
X
revsearch(f,n)
{
X	if (lastdirec == 0)
X		return(backhunt(f,n));
X	else
X		return(forwhunt(f,n));
}
X
X
/*
X * thescanner -- Search for a pattern in either direction.  If found,
X *	reset the "." to be at the start or just after the match string,
X *	and (perhaps) repaint the display.
X */
int	
thescanner(patrn, direct, beg_or_end, wrapok)
char	*patrn;		/* pointer into pattern */
int	direct;		/* which way to go.*/
int	beg_or_end;	/* put point at beginning or end of pattern.*/
{
X	LINE *curline;			/* current line during scan */
X	int curoff;			/* position within current line */
X	int found;
X	int (*matcher)();
X	int mcmatch();
X	int litmatch();
X
X	/* If we are going in reverse, then the 'end' is actually
X	 * the beginning of the pattern.  Toggle it.
X	 */
X	beg_or_end ^= direct;
X
X	/*
X	 * Save the old matchlen length, in case it is
X	 * horribly different (closure) from the old length.
X	 * This is terribly important for query-replace undo
X	 * command.
X	 */
X	mlenold = matchlen;
X
X	/* Setup local scan pointers to global ".".
X	 */
X	curline = curwp->w_dotp;
X	curoff  = curwp->w_doto;
#if MAGIC
X	if (magical && (curwp->w_bufp->b_mode & MDMAGIC)) {
X		matcher = mcmatch;
X		if (direct == FORWARD)
X			patrn = (char *)mcpat;
X		else
X			patrn = (char *)tapcm;
X	} else
#endif
X	{
X		matcher = litmatch;
X		if (direct == FORWARD)
X			patrn = pat;
X		else
X			patrn = tap;
X	}
X			
X	/* Scan each character until we hit the head link record.
X	 */
X	do {
X		if (interrupted) return ABORT;
X
X		/* Save the current position in case we need to
X		 * restore it on a match, and initialize matchlen to
X		 * zero in case we are doing a search for replacement.
X		 */
X		matchline = curline;
X		matchoff = curoff;
X		matchlen = 0;
X		
X		if ((*matcher)(patrn, direct, &curline, &curoff)) {
X			/* A SUCCESSFULL MATCH!!!
X			 * reset the global "." pointers.
X			 */
X			if (beg_or_end == PTEND)	/* at end of string */
X			{
X				curwp->w_dotp = curline;
X				curwp->w_doto = curoff;
X			}
X			else		/* at beginning of string */
X			{
X				curwp->w_dotp = matchline;
X				curwp->w_doto = matchoff;
X			}
X
X			curwp->w_flag |= WFMOVE; /* flag that we have moved */
X			return TRUE;
X		}
X
X		/* Advance the cursor.
X		 */
X		nextch(&curline, &curoff, direct,wrapok);
X	} while (!boundry(curline, curoff, direct, wrapok));
X
X	return FALSE;	/* We could not find a match.*/
}
X
#if MAGIC
/*
X * mcmatch -- Search for a meta-pattern in either direction.  Based on the
X *	recursive routine amatch() (for "anchored match") in
X *	Kernighan & Plauger's "Software Tools".
X */
mcmatch(mcptr, direct, pcwline, pcwoff)
register MC	*mcptr;	/* string to scan for */
int		direct;		/* which way to go.*/
LINE		**pcwline;	/* current line during scan */
int		*pcwoff;	/* position within current line */
{
X	register int c;			/* character at current position */
X	LINE *curline;			/* current line during scan */
X	int curoff;			/* position within current line */
X	int nchars;
X
X	/* Set up local scan pointers to ".", and get
X	 * the current character.  Then loop around
X	 * the pattern pointer until success or failure.
X	 */
X	curline = *pcwline;
X	curoff = *pcwoff;
X
X	/* The beginning-of-line and end-of-line metacharacters
X	 * do not compare against characters, they compare
X	 * against positions.
X	 * BOL is guaranteed to be at the start of the pattern
X	 * for forward searches, and at the end of the pattern
X	 * for reverse searches.  The reverse is true for EOL.
X	 * So, for a start, we check for them on entry.
X	 */
X	if (mcptr->mc_type == BOL)
X	{
X		if (curoff != 0)
X			return FALSE;
X		mcptr++;
X	}
X
X	if (mcptr->mc_type == EOL)
X	{
X		if (curoff != llength(curline))
X			return FALSE;
X		mcptr++;
X	}
X
X	while (mcptr->mc_type != MCNIL)
X	{
X		c = nextch(&curline, &curoff, direct, FALSE);
X
X		if (mcptr->mc_type & CLOSURE)
X		{
X			/* Try to match as many characters as possible
X			 * against the current meta-character.  A
X			 * newline or boundary never matches a closure.
X			 */
X			nchars = 0;
X			while (c != '\n' && c != -1 && mceq(c, mcptr))
X			{
X				nchars++;
X				c = nextch(&curline, &curoff, direct, FALSE);
X			}
X
X			/* We are now at the character that made us
X			 * fail.  nchars is the number of successful
X			 * matches.  Try to match the rest of the pattern.
X			 * Shrink the closure by one for each failure.
X			 * Since closure matches *zero* or more occurences
X			 * of a pattern, a match may start even if the
X			 * previous loop matched no characters.
X			 */
X			mcptr++;
X
X			for (;;)
X			{
X				/* back up, since mcmatch goes forward first */
X				(void)nextch(&curline, &curoff, 
X					direct ^ REVERSE, FALSE);
X
X				if (mcmatch(mcptr, direct, &curline, &curoff))
X				{
X					matchlen += nchars;
X					goto success;
X				}
X
X				if (nchars-- == 0)
X					return FALSE;
X			}
X		}
X		else			/* Not closure.*/
X		{
X			/* The only way we'd get a BOL metacharacter
X			 * at this point is at the end of the reversed pattern.
X			 * The only way we'd get an EOL metacharacter
X			 * here is at the end of a regular pattern.
X			 * So if we match one or the other, and are at
X			 * the appropriate position, we are guaranteed success
X			 * (since the next pattern character has to be MCNIL).
X			 * Before we report success, however, we back up by
X			 * one character, so as to leave the cursor in the
X			 * correct position.  For example, a search for ")$"
X			 * will leave the cursor at the end of the line, while
X			 * a search for ")<NL>" will leave the cursor at the
X			 * beginning of the next line.  This follows the
X			 * notion that the meta-character '$' (and likewise
X			 * '^') match positions, not characters.
X			 */
X			if (mcptr->mc_type == BOL)
X			{
X				if (curoff == llength(curline))
X				{
X					(void)nextch(&curline, &curoff,
X						   direct ^ REVERSE, FALSE);
X					goto success;
X				}
X				else
X				{
X					return FALSE;
X				}
X			}
X			if (mcptr->mc_type == EOL)
X			{
X				if (curoff == 0)
X				{
X					(void)nextch(&curline, &curoff,
X						   direct ^ REVERSE, FALSE);
X					goto success;
X				}
X				else
X				{
X					return FALSE;
X				}
X			}
X				
X
X			/* Neither BOL nor EOL, so go through
X			 * the meta-character equal function.
X			 */
X			if (!mceq(c, mcptr))
X				return FALSE;
X		}
X
X		/* Increment the length counter and
X		 * advance the pattern pointer.
X		 */
X		matchlen++;
X		mcptr++;
X	}			/* End of mcptr loop.*/
X
X	/* A SUCCESSFULL MATCH!!!
X	 * Reset the "." pointers.
X	 */
success:
X	*pcwline = curline;
X	*pcwoff  = curoff;
X	
X
X	return TRUE;
}
#endif
X
/*
X * litmatch -- Search for a literal match in either direction.
X */
litmatch(patptr, direct, pcwline, pcwoff)
register char	*patptr;	/* string to scan for */
int		direct;		/* which way to go.*/
LINE		**pcwline;	/* current line during scan */
int		*pcwoff;	/* position within current line */
{
X	register int c;			/* character at current position */
X	LINE *curline;		/* current line during scan */
X	int curoff;		/* position within current line */
X
X	/* Set up local scan pointers to ".", and get
X	 * the current character.  Then loop around
X	 * the pattern pointer until success or failure.
X	 */
X	curline = *pcwline;
X	curoff = *pcwoff;
X
X	while (*patptr != '\0') {
X		c = nextch(&curline, &curoff, direct, FALSE);
X
X		if (!eq(c, *patptr))
X			return FALSE;
X
X		/* Increment the length counter and
X		 * advance the pattern pointer.
X		 */
X		matchlen++;
X		patptr++;
X	}
X
X	*pcwline = curline;
X	*pcwoff  = curoff;
X	
X
X	return TRUE;
}
X
X
/*
X * eq -- Compare two characters.  The "bc" comes from the buffer, "pc"
X *	from the pattern.  If we are not in EXACT mode, fold out the case.
X */
int	
eq(bc, pc)
register char	bc;
register char	pc;
{
X	if (curwp->w_bufp->b_mode & MDEXACT)
X		return (bc ^ pc) == 0;
X	/* take out the bit that makes upper and lowercase different */
X	return ((bc ^ pc) & ~DIFCASE) == 0;
}
X
scrsearchpat(f,n)
{
X	int s;
X	s =  readpattern("", pat, TRUE, 0, TRUE);
X	mlwrite("Search pattern is now %s", pat);
X	lastdirec = 0;
X	return s;
}
/*
X * readpattern -- Read a pattern.  Stash it in apat.  If it is the
X *	search string, create the reverse pattern and the magic
X *	pattern, assuming we are in MAGIC mode (and defined that way).
X *	Apat is not updated if the user types in an empty line.  If
X *	the user typed an empty line, and there is no old pattern, it is
X *	an error.  Display the old pattern, in the style of Jeff Lomicka.
X *	There is some do-it-yourself control expansion.
X *	An alternate termination character is passed in.
X */
readpattern(prompt, apat, srch, c, fromscreen)
char	*prompt;
char	*apat;
int	srch;
int	fromscreen;
{
X	int status;
X
X	/* Read a pattern.  Either we get one,
X	 * or we just get the META charater, and use the previous pattern.
X	 * Then, if it's the search string, make a reversed pattern.
X	 * *Then*, make the meta-pattern, if we are defined that way.
X	 */
X	if (fromscreen) {
X	 	status = screen_string(apat, NPAT, _ident);
X		if (status != TRUE)
X			return status;
X	} else {
X	 	status = kbd_string(prompt, apat, NPAT, c, NO_EXPAND);
X	}
X 	if (status == TRUE) {
X		if (srch) {	/* If we are doing the search string.*/
X			mlenold = matchlen = strlen(apat);
X			/* Reverse string copy.
X			 */
X			rvstrcpy(tap, apat);
#if	MAGIC
X			/* Only make the meta-pattern if in magic mode,
X			 * since the pattern in question might have an
X			 * invalid meta combination.
X			 */
X			if ((curwp->w_bufp->b_mode & MDMAGIC) == 0)
X				mcclear();
X			else
X				status = mcstr();
#endif
X		}
X	} else if (status == FALSE && *apat != 0) { /* Old one */
X		status = TRUE;
X	}
X
X	return status;
}
X
/*
X * savematch -- We found the pattern?  Let's save it away.
X */
X
savematch()
{
X	register char *ptr;	/* ptr into malloced last match string */
X	register int j;		/* index */
X	LINE *curline;		/* line of last match */
X	int curoff;		/* offset "      "    */
X
X	/* free any existing match string */
X	if (patmatch != NULL)
X		free(patmatch);
X
X	/* attempt to allocate a new one */
X	ptr = patmatch = malloc(matchlen + 1);
X	if (ptr == NULL)
X		return;
X
X	/* save the match! */
X	curoff = matchoff;
X	curline = matchline;
X
X	for (j = 0; j < matchlen; j++)
X		*ptr++ = nextch(&curline, &curoff, FORWARD, FALSE);
X
X	/* null terminate the match string */
X	*ptr = '\0';
}
X
/*
X * rvstrcpy -- Reverse string copy.
X */
rvstrcpy(rvstr, str)
register char	*rvstr, *str;
{
X	register int i;
X
X	str += (i = strlen(str));
X
X	while (i-- > 0)
X		*rvstr++ = *--str;
X
X	*rvstr = '\0';
}
X
rvstrncpy(rvstr, str, n)
register char	*rvstr, *str;
{
X	register int i;
X
X	i = strlen(str);
X	if (n < i) i = n;
X
X	str += i;
X
X	while (i-- > 0)
X		*rvstr++ = *--str;
X
X	*rvstr = '\0';
}
X
#if SEARCH_AND_REPLACE
/*
X * sreplace -- Search and replace.
X */
sreplace(f, n)
{
X	return replaces(FALSE, f, n);
}
X
/*
X * qreplace -- search and replace with query.
X */
qreplace(f, n)
{
X	register int s;
X	s = replaces(TRUE, f, n);
#if	NeWS	/* user must not buffer output */
X	newsimmediateoff() ;
#endif
X	return(s) ;
}
X
/*
X * replaces -- Search for a string and replace it with another
X *	string.  Query might be enabled (according to kind).
X * f,n unused
X */
replaces(kind, f, n)
int	kind;	/* Query enabled flag */
{
X	register int status;	/* success flag on pattern inputs */
X	register int rlength;	/* length of replacement string */
X	register int numsub;	/* number of substitutions */
X	int nlflag;		/* last char of search string a <NL>? */
X	int nlrepl;		/* was a replace done on the last line? */
X	int c;			/* input char for query */
X	char tpat[NPAT];	/* temporary to hold search pattern */
X	LINE *lastline;		/* position of last replace and */
X	int lastoff;		/* offset (for 'u' query option) */
X	REGION region;
X
X	/* we don't really care much about size, etc., but getregion does
X		a nice job of scanning for the mark, so we use it.  Then
X		we pretend that wrapping around the bottom of the file is
X		okay, and use the boundary code to keep us from going
X		past the region end. */
X	if (curwp->w_mkp == curwp->w_dotp) {
X		if (curwp->w_doto > curwp->w_mko) {
X			int tmpoff;
X			tmpoff = curwp->w_doto;
X			curwp->w_doto = curwp->w_mko;
X			curwp->w_mko = tmpoff;
X		}
X	} else {
X		getregion(&region);
X		if (region.r_linep == curwp->w_mkp)
X			swapmark();
X	}
X	if (fulllineregions) {
X		curwp->w_doto = 0;
X		curwp->w_mko = llength(curwp->w_mkp);
X	}
X	if (curwp->w_mkp == curbp->b_linep) {
X		mlwrite("BUG: mark is at b_linep");
X		return FALSE;
X	}
X
X	/* Ask the user for the text of a pattern.
X	 */
X	if ((status = readpattern(
X	    (kind == FALSE ? "Replace: " : "Query replace: "),
X	    		 &pat[0], TRUE, '\n', FALSE)) != TRUE)
X		return status;
X
X	/* Ask for the replacement string.
X	 */
X	if ((status = readpattern("with: ", &rpat[0], FALSE, '\n', FALSE))
X								== ABORT)
X		return status;
X
X	/* Find the length of the replacement string.
X	 */
X	rlength = strlen(&rpat[0]);
X
X	/* Set up flags so we can make sure not to do a recursive
X	 * replace on the last line.
X	 */
X	nlflag = (pat[matchlen - 1] == '\n');
X	nlrepl = FALSE;
X
X	if (kind)
X	{
X		/* Build query replace question string.
X		 */
X		strcpy(tpat, "Replace '");
X		expandp(&pat[0], &tpat[strlen(tpat)], NPAT/3);
X		strcat(tpat, "' with '");
X		expandp(&rpat[0], &tpat[strlen(tpat)], NPAT/3);
X		strcat(tpat, "'? ");
X
X		/* Initialize last replaced pointers.
X		 */
X		lastline = NULL;
X		lastoff = 0;
#if	NeWS
X		newsimmediateon() ;
#endif
X	}
X
X	numsub = 0;
X
X	while ( nlflag == FALSE || nlrepl == FALSE ) {
X
X		if (interrupted) return ABORT;
X
X		/* Search for the pattern.
X		 * If we search with a regular expression,
X		 * matchlen is reset to the true length of
X		 * the matched string.
X		 */
X		setboundry(TRUE,curwp->w_mkp,curwp->w_mko,FORWARD);
#if	MAGIC
X		if (magical && (curwp->w_bufp->b_mode & MDMAGIC) != 0) {
X			if (!thescanner(&mcpat[0], FORWARD, PTBEG, TRUE))
X				break;
X		} else
#endif
X			if (!thescanner(&pat[0], FORWARD, PTBEG, TRUE))
X				break;		/* all done */
X
X		/* Check if we are on the last line.
X		 */
X		nlrepl = (lforw(curwp->w_dotp) == curwp->w_bufp->b_linep);
X
X		/* Check for query.
X		 */
X		if (kind)
X		{
X			/* Get the query.
X			 */
pprompt:		mlwrite(&tpat[0], &pat[0], &rpat[0]);
qprompt:
X			update(TRUE);  /* show the proposed place to change */
nprompt:
X			c = kbd_key();			/* and input */
X			mlwrite("");			/* and clear it */
X
X			if (c == abortc) {
X				mlwrite("[Aborted]");
X				return(FALSE);
X			}
X
X			/* And respond appropriately.
X			 */
X			switch (c)
X			{
X				case tocntrl('Q'):
X				case tocntrl('S'):
X					goto nprompt;
X
X				case 'y':	/* yes, substitute */
X					savematch();
X					break;
X
X				case 'n':	/* no, onward */
X					nextch( &(curwp->w_dotp),
X						&(curwp->w_doto),FORWARD,TRUE);
X					continue;
X
X				case '!':	/* yes/stop asking */
X					kind = FALSE;
X					break;
X
X				case 'u':	/* undo last and re-prompt */
X
X					/* Restore old position.
X					 */
X					if (lastline == NULL)
X					{
X						/* There is nothing to undo.
X						 */
X						TTbeep();
X						goto pprompt;
X					}
X					curwp->w_dotp = lastline;
X					curwp->w_doto = lastoff;
X					lastline = NULL;
X					lastoff = 0;
X
X					/* Delete the new string.
X					 */
X					backchar(FALSE, rlength);
X					status = delins(rlength, patmatch);
X					if (status != TRUE) {
X						return (status);
X					}
X
X					/* Record one less substitution,
X					 * backup, and reprompt.
X					 */
X					--numsub;
X					backchar(TRUE, mlenold);
X					matchline = curwp->w_dotp;
X					matchoff  = curwp->w_doto;
X					goto pprompt;
X
X				default:	/* bitch and beep */
X					TTbeep();
X
X				case 'h':
X				case '?':	/* help me */
X					mlwrite(
X			"(Y)es, (N)o, (!)Do rest, (U)ndo last, (ESC)Abort: ");
X					goto qprompt;
X
X			}	/* end of switch */
X		}	/* end of "if kind" */
X
X		/*
X		 * Delete the sucker, and insert its
X		 * replacement.
X		 */
X		status = delins(matchlen, &rpat[0]);
X		if (status != TRUE) {
X			return (status);
X		}
X
X		/* Save where we are if we might undo this....
X		 */
X		if (kind)
X		{
X			lastline = curwp->w_dotp;
X			lastoff = curwp->w_doto;
X		}
X
X		numsub++;	/* increment # of substitutions */
X	}
X
X	/* And report the results.
X	 */
X	mlwrite("%d substitutions", numsub);
X	return(TRUE);
}
X
/*
X * delins -- Delete a specified length from the current
X *	point, then insert the string.
X */
delins(dlength, instr)
int	dlength;
char	*instr;
{
X	int	status;
X	char	tmpc;
X
X	/* Zap what we gotta,
X	 * and insert its replacement.
X	 */
X	if (!(status = ldelete((long) dlength, FALSE)))
X	{
X		mlwrite("Error while deleting");
X		return(FALSE);
X	} else {
X		while (tmpc = *instr)
X		{
X			status = (tmpc == '\n'? lnewline(): linsert(1, tmpc));
X
X			/* Insertion error?
X			 */
X			if (!status)
X			{
X				mlwrite("Out of memory while inserting");
X				break;
X			}
X			instr++;
X		}
X	}
X	return (status);
}
#endif
X
/*
X * expandp -- Expand control key sequences for output.
X */
expandp(srcstr, deststr, maxlength)
char *srcstr;	/* string to expand */
char *deststr;	/* destination of expanded string */
int maxlength;	/* maximum chars in destination */
{
X	unsigned char c;	/* current char to translate */
X
X	/* Scan through the string.
X	 */
X	while ((c = *srcstr++) != 0)
X	{
X		if (c == '\n')		/* it's a newline */
X		{
X			*deststr++ = '<';
X			*deststr++ = 'N';
X			*deststr++ = 'L';
X			*deststr++ = '>';
X			maxlength -= 4;
X		}
X		else if (!isprint(c))	/* control character */
X		{
X			*deststr++ = '^';
X			*deststr++ = toalpha(c);
X			maxlength -= 2;
X		}
X		else if (c == '%')
X		{
X			*deststr++ = '%';
X			*deststr++ = '%';
X			maxlength -= 2;
X		}
X		else			/* any other character */
X		{
X			*deststr++ = c;
X			maxlength--;
X		}
X
X		/* check for maxlength */
X		if (maxlength < 4)
X		{
X			*deststr++ = '$';
X			*deststr = '\0';
X			return(FALSE);
X		}
X	}
X	*deststr = '\0';
X	return(TRUE);
}
X
/*
X * boundry -- Return information depending on whether we may search no
X *	further.  Beginning of file and end of file are the obvious
X *	cases, but we may want to add further optional boundary restrictions
X *	in future, a' la VMS EDT.  At the moment, just return TRUE or
X *	FALSE depending on if a boundary is hit (ouch).
X */
int	
boundry(curline, curoff)
LINE	*curline;
int	curoff;
{
X	return (curline == boundline) && (curoff == boundoff);
}
X
setboundry(wrapok,lp,off,dir)
LINE *lp;
{
X	if (wrapok) {
X		(void)nextch(&lp,&off,dir,TRUE);
X		boundline = lp;
X		boundoff = off;
X	} else {
X		boundline = curbp->b_linep;
X		boundoff = 0;
X	}
}
X
/*
X * nextch -- retrieve the next/previous character in the buffer,
X *	and advance/retreat the point.
X *	The order in which this is done is significant, and depends
X *	upon the direction of the search.  Forward searches look at
X *	the current character and move, reverse searches move and
X *	look at the character.
X */
nextch(pcurline, pcuroff, dir, wrapok)
LINE	**pcurline;
int	*pcuroff;
int	dir;
{
X	register LINE	*curline;
X	register int	curoff;
X	register int	c;
X
X	/* dummy up a -1, which will never match anything, as the only
X		character on the header line */
X	
X	curline = *pcurline;
X	curoff = *pcuroff;
X	if (dir == FORWARD) {
X		if (curoff == ((curline == curbp->b_linep)?1:llength(curline)))
X		{	/* if at EOL */
X			curline = lforw(curline);	/* skip to next line */
X			curoff = 0;
X			c = '\n';			/* and return a <NL> */
X		} else {
X			c = lgetc(curline, curoff++);	/* get the char */
X			if (curline == curbp->b_linep)
X				c = -1;
X		}
X	} else	 {		/* Reverse.*/
X		if (curoff == 0) {
X			curline = lback(curline);
X			curoff = (curline == curbp->b_linep)?1:llength(curline);
X			c = '\n';
X		} else {
X			c = lgetc(curline, --curoff);
X			if (curline == curbp->b_linep)
X				c = -1;
X		}
X	}
X	*pcurline = curline;
X	*pcuroff = curoff;
X
X	return (c);
}
X
#if	MAGIC
/*
X * mcstr -- Set up the 'magic' array.  The closure symbol is taken as
X *	a literal character when (1) it is the first character in the
X *	pattern, and (2) when preceded by a symbol that does not allow
X *	closure, such as a newline, beginning of line symbol, or another
X *	closure symbol.
X *
X *	Coding comment (jmg):  yes, i know i have gotos that are, strictly
X *	speaking, unnecessary.  But right now we are so cramped for
X *	code space that i will grab what i can in order to remain
X *	within the 64K limit.  C compilers actually do very little
X *	in the way of optimizing - they expect you to do that.
X */
int 
mcstr()
{
X	MC	*mcptr, *rtpcm;
X	char	*patptr;
X 	int	mj;
X 	int	pchr;
X 	int	status = TRUE;
X 	int	does_closure = FALSE;
X
X	/* If we had metacharacters in the MC array previously,
X	 * free up any bitmaps that may have been allocated.
X	 */
X	if (magical)
X		mcclear();
X
X	magical = FALSE;
X	mj = 0;
X	mcptr = &mcpat[0];
X	patptr = &pat[0];
X
X	while ((pchr = *patptr) && status)
X	{
X		switch (pchr)
X		{
X			case MC_CCL:
X				status = cclmake(&patptr, mcptr);
X				magical = TRUE;
X				does_closure = TRUE;
X				break;
X			case MC_BOL:
X				if (mj != 0)
X					goto litcase;
X
X				mcptr->mc_type = BOL;
X				magical = TRUE;
X				does_closure = FALSE;
X				break;
X			case MC_EOL:
X				if (*(patptr + 1) != '\0')
X					goto litcase;
X
X				mcptr->mc_type = EOL;
X				magical = TRUE;
X				does_closure = FALSE;
X				break;
X			case MC_ANY:
X				mcptr->mc_type = ANY;
X				magical = TRUE;
X				does_closure = TRUE;
X				break;
X			case MC_CLOSURE:
X				/* Does the closure symbol mean closure here?
X				 * If so, back up to the previous element
X				 * and indicate it is enclosed.
X				 */
X				if (!does_closure)
X					goto litcase;
X				mj--;
X				mcptr--;
X				mcptr->mc_type |= CLOSURE;
X				magical = TRUE;
X				does_closure = FALSE;
X				break;
X
X			/* Note: no break between MC_ESC case and the default.
X			 */
X			case MC_ESC:
X				if (*(patptr + 1) != '\0')
X				{
X					pchr = *++patptr;
X					magical = TRUE;
X				}
X			default:
litcase:			mcptr->mc_type = LITCHAR;
X				mcptr->u.lchar = pchr;
X				does_closure = (pchr != '\n');
X				break;
X		}		/* End of switch.*/
X		mcptr++;
X		patptr++;
X		mj++;
X	}		/* End of while.*/
X
X	/* Close off the meta-string.
X	 */
X	mcptr->mc_type = MCNIL;
X
X	/* Set up the reverse array, if the status is good.  Please note the
X	 * structure assignment - your compiler may not like that.
X	 * If the status is not good, nil out the meta-pattern.
X	 * The only way the status would be bad is from the cclmake()
X	 * routine, and the bitmap for that member is guarenteed to be
X	 * freed.  So we stomp a MCNIL value there, and call mcclear()
X	 * to free any other bitmaps.
X	 */
X	if (status)
X	{
X		rtpcm = &tapcm[0];
X		while (--mj >= 0)
X		{
#if	LATTICE
X			movmem(--mcptr, rtpcm++, sizeof (MC));
#endif
X
#if	MWC86 | AZTEC | MSC | TURBO | VMS | USG | BSD | V7
X			*rtpcm++ = *--mcptr;
#endif
X		}
X		rtpcm->mc_type = MCNIL;
X	}
X	else
X	{
X		(--mcptr)->mc_type = MCNIL;
X		mcclear();
X	}
X
X	return(status);
}
X
/*
X * mcclear -- Free up any CCL bitmaps, and MCNIL the MC arrays.
X */
mcclear()
{
X	register MC	*mcptr;
X
X	mcptr = &mcpat[0];
X
X	while (mcptr->mc_type != MCNIL)
X	{
X		if ((mcptr->mc_type & MASKCL) == CCL ||
X		    (mcptr->mc_type & MASKCL) == NCCL)
X			free(mcptr->u.cclmap);
X		mcptr++;
X	}
X	mcpat[0].mc_type = tapcm[0].mc_type = MCNIL;
}
X
/*
X * mceq -- meta-character equality with a character.  In Kernighan & Plauger's
X *	Software Tools, this is the function omatch(), but i felt there
X *	were too many functions with the 'match' name already.
X */
mceq(bc, mt)
int	bc;
MC	*mt;
{
X	register int result;
X	
X	if (bc < 0)
X		return FALSE;
X
X	switch (mt->mc_type & MASKCL)
X	{
X		case LITCHAR:
X			result = eq(bc, mt->u.lchar);
X			break;
X
X		case ANY:
X			result = (bc != '\n');
X			break;
X
X		case CCL:
X			if (!(result = biteq(bc, mt->u.cclmap)))
X			{
X				if ((curwp->w_bufp->b_mode & MDEXACT) == 0 &&
X				    isalpha(bc))
X				{
X					result = biteq(CHCASE(bc),mt->u.cclmap);
X				}
X			}
X			break;
X
X		case NCCL:
X			
X			if (bc == '\n') {
X				result = FALSE;
X			} else {
X				result = !biteq(bc, mt->u.cclmap);
X
X				if ((curwp->w_bufp->b_mode & MDEXACT) == 0 &&
X					    isalpha(bc))
X				{
X					result &= !biteq(CHCASE(bc),
X								 mt->u.cclmap);
X				}
X			}
X			break;
X
X		default:
X			mlwrite("mceq: what is %d?", mt->mc_type);
X			result = FALSE;
X			break;
X
X	}	/* End of switch.*/
X
X	return (result);
}
X
/*
X * cclmake -- create the bitmap for the character class.
X *	ppatptr is left pointing to the end-of-character-class character,
X *	so that a loop may automatically increment with safety.
X */
cclmake(ppatptr, mcptr)
char	**ppatptr;
MC	*mcptr;
{
X	BITMAP		bmap;
X	register char	*patptr;
X	register int	pchr, ochr;
X
X	if ((bmap = clearbits()) == NULL)
X	{
X		mlwrite("Out of memory");
X		return FALSE;
X	}
X
X	mcptr->u.cclmap = bmap;
X	patptr = *ppatptr;
X
X	/*
X	 * Test the initial character(s) in ccl for
X	 * special cases - negate ccl, or an end ccl
X	 * character as a first character.  Anything
X	 * else gets set in the bitmap.
X	 */
X	if (*++patptr == MC_NCCL)
X	{
X		patptr++;
X		mcptr->mc_type = NCCL;
X	}
X	else
X		mcptr->mc_type = CCL;
X
X	if ((ochr = *patptr) == MC_ECCL)
X	{
X		mlwrite("Empty character class");
X		return (FALSE);
X	}
X	else
X	{
X		if (ochr == MC_ESC)
X			ochr = *++patptr;
X
X		setbit(ochr, bmap);
X		patptr++;
X	}
X
X	while (ochr != '\0' && (pchr = *patptr) != MC_ECCL)
X	{
X		switch (pchr)
X		{
X			/* Range character loses its meaning
X			 * if it is the last character in
X			 * the class.
X			 */
X			case MC_RCCL:
X				if (*(patptr + 1) == MC_ECCL)
X					setbit(pchr, bmap);
X				else
X				{
X					pchr = *++patptr;
X					while (++ochr <= pchr)
X						setbit(ochr, bmap);
X				}
X				break;
X
X			/* Note: no break between case MC_ESC and the default.
X			 */
X			case MC_ESC:
X				pchr = *++patptr;
X			default:
X				setbit(pchr, bmap);
X				break;
X		}
X		patptr++;
X		ochr = pchr;
X	}
X
X	*ppatptr = patptr;
X
X	if (ochr == '\0')
X	{
X		mlwrite("Missing '%c'",MC_ECCL);
X		free(bmap);
X		return FALSE;
X	}
X	return TRUE;
}
X
/*
X * biteq -- is the character in the bitmap?
X */
biteq(bc, cclmap)
int	bc;
BITMAP	cclmap;
{
X	if (bc >= HICHAR || bc < 0)
X		return FALSE;
X
X	return( (*(cclmap + (bc >> 3)) & BIT(bc & 7))? TRUE: FALSE );
}
X
/*
X * clearbits -- Allocate and zero out a CCL bitmap.
X */
BITMAP 
clearbits()
{
X
X	BITMAP		cclstart, cclmap;
X	register int	j;
X
X	if ((cclmap = cclstart = (BITMAP) malloc(HIBYTE)) != NULL)
X		for (j = 0; j < HIBYTE; j++)
X			*cclmap++ = 0;
X
X	return (cclstart);
}
X
/*
X * setbit -- Set a bit (ON only) in the bitmap.
X */
void 
setbit(bc, cclmap)
int	bc;
BITMAP	cclmap;
{
X	if (bc < HICHAR && bc >= 0)
X		*(cclmap + (bc >> 3)) |= BIT(bc & 7);
}
#endif
SHAR_EOF
chmod 0444 search.c ||
echo 'restore of search.c failed'
Wc_c="`wc -c < 'search.c'`"
test 35047 -eq "$Wc_c" ||
	echo 'search.c: original size 35047, current size' "$Wc_c"
# ============= shorten/COPYING ==============
if test ! -d 'shorten'; then
    echo 'x - creating directory shorten'
    mkdir 'shorten'
fi
echo 'x - extracting shorten/COPYING (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'shorten/COPYING' &&
The code distributed with vile in the "shortnames" subdirectory is covered
by the following license.  The vile code itself is _not_ covered by the GNU
public license.
X
X		 GNU EMACS GENERAL PUBLIC LICENSE
X		    (Clarified 11 Feb 1988)
X
X Copyright (C) 1985, 1987, 1988 Richard M. Stallman
X Everyone is permitted to copy and distribute verbatim copies
X of this license, but changing it is not allowed.  You can also
X use this wording to make the terms for other programs.
X
X  The license agreements of most software companies keep you at the
mercy of those companies.  By contrast, our general public license is
intended to give everyone the right to share GNU Emacs.  To make
sure that you get the rights we want you to have, we need to make
restrictions that forbid anyone to deny you these rights or to ask you
to surrender the rights.  Hence this license agreement.
X
X  Specifically, we want to make sure that you have the right to give
away copies of Emacs, that you receive source code or else can get it
if you want it, that you can change Emacs or use pieces of it in new
free programs, and that you know you can do these things.
X
X  To make sure that everyone has such rights, we have to forbid you to
deprive anyone else of these rights.  For example, if you distribute
copies of Emacs, you must give the recipients all the rights that you
have.  You must make sure that they, too, receive or can get the
source code.  And you must tell them their rights.
X
X  Also, for our own protection, we must make certain that everyone
finds out that there is no warranty for GNU Emacs.  If Emacs is
modified by someone else and passed on, we want its recipients to know
that what they have is not what we distributed, so that any problems
introduced by others will not reflect on our reputation.
X
X  Therefore we (Richard Stallman and the Free Software Fundation,
Inc.) make the following terms which say what you must do to be
allowed to distribute or change GNU Emacs.
X
X			COPYING POLICIES
X
X  1. You may copy and distribute verbatim copies of GNU Emacs source code
as you receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy a valid copyright notice "Copyright
(C) 1988 Free Software Foundation, Inc." (or with whatever year is
appropriate); keep intact the notices on all files that refer to this
License Agreement and to the absence of any warranty; and give any
other recipients of the GNU Emacs program a copy of this License
Agreement along with the program.  You may charge a distribution fee
SHAR_EOF
true || echo 'restore of shorten/COPYING failed'
echo 'End of Vile part 12'
echo 'File shorten/COPYING is continued in part 13'
echo 13 > _shar_seq_.tmp
exit 0
-- 
		paul fox, pgf@cayman.com, (617)494-1999
		Cayman Systems, 26 Landsdowne St., Cambridge, MA 02139