[mod.sources] v09i044: MicroEMACS, version 3.8b, Part12/14

sources-request@mirror.TMC.COM (03/18/87)

Submitted by: ihnp4!itivax!duncan!lawrence (Daniel Lawrence)
Mod.sources: Volume 9, Issue 44
Archive-name: uemacs3.8b/Part12

#! /bin/sh
# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
# If this archive is complete, you will see the message:
#		"End of archive 12 (of 14)."
# Contents:  search.c
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
echo shar: Extracting \"search.c\" \(30773 characters\)
if test -f search.c ; then 
  echo shar: Will not over-write existing file \"search.c\"
else
sed "s/^X//" >search.c <<'END_OF_search.c'
X/*
X * The functions in this file implement commands that search in the forward
X * and backward directions.  There are no special characters in the search
X * strings.  Probably should have a regular expression search, or something
X * like that.
X *
X * Aug. 1986 John M. Gamble:
X *	Made forward and reverse search use the same scan routine.
X *
X *	Added 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>
X#include	"estruct.h"
X#include        "edef.h"
X#include	"esearch.h"
X
X/*
X * Reversed pattern array.
X */
Xchar	tap[NPAT];
X
X#if	MAGIC
X/*
X * The variable magical determines if there are actual
X * metacharacters in the string - if not, then we don't
X * have to use the slower MAGIC mode search functions.
X *
X * The variable mclen holds the length of the matched
X * string - used by the replace functions.
X *
X * The arrays mcpat and tapcm hold the MC and reversed
X * MC search structures.
X */
Xshort int	magical = FALSE;
Xint		mclen = 0;
XMC		mcpat[NPAT];
XMC		tapcm[NPAT];
X#endif
X
X/*
X * forwsearch -- Search forward.  Get a search string from the user, and
X *	search, beginning at ".", for the string.  If found, reset the "."
X *	to be just after the match string, and (perhaps) repaint the display.
X */
X
Xforwsearch(f, n)
X{
X	register int status = TRUE;
X
X	/* Resolve the repeat count.
X	 */
X	if (n == 0)
X		n = 1;
X
X	/* If n is negative, search backwards.
X	 * Otherwise proceed by asking for the search string.
X	 */
X	if (n < 0)
X		status = backsearch(f, -n);
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.
X	 */
X	else if ((status = readpattern("Search", &pat[0], TRUE)) == TRUE)
X	{
X		do
X		{
X#if	MAGIC
X			if ((magical && curwp->w_bufp->b_mode & MDMAGIC) != 0)
X				status = mcscanner(&mcpat[0], FORWARD, PTEND);
X			else
X#endif
X				status = scanner(&pat[0], FORWARD, PTEND);
X		} while ((--n > 0) && status);
X
X		/* ...and complain if not there.
X		 */
X		if (status == FALSE)
X			mlwrite("Not found");
X	}
X	return(status);
X}
X
X/*
X * forwhunt -- Search forward for a previously acquired search string,
X *	beginning at ".".  If found, reset the "." to be just after
X *	the match string, and (perhaps) repaint the display.
X */
X
Xforwhunt(f, n)
X{
X	register int status = TRUE;
X
X	/* Resolve the repeat count.
X	 */
X	if (n == 0)
X		n = 1;
X	else 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#if	MAGIC
X	if ((curwp->w_bufp->b_mode & MDMAGIC) != 0 &&
X		 mcpat[0].mc_type == MCNIL)
X	{
X		if (!mcstr())
X			return FALSE;
X	}
X#endif
X
X	/* Search for the pattern...
X	 */
X	do
X	{
X#if	MAGIC
X		if ((magical && curwp->w_bufp->b_mode & MDMAGIC) != 0)
X			status = mcscanner(&mcpat[0], FORWARD, PTEND);
X		else
X#endif
X			status = scanner(&pat[0], FORWARD, PTEND);
X	} while ((--n > 0) && status);
X
X	/* ...and complain if not there.
X	 */
X	if (status == FALSE)
X		mlwrite("Not found");
X
X	return(status);
X}
X
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 */
Xbacksearch(f, n)
X{
X	register int status = TRUE;
X
X	/* Resolve null and negative arguments.
X	 */
X	if (n == 0)
X		n = 1;
X
X	/* If n is negative, search forwards.
X	 * Otherwise proceed by asking for the search string.
X	 */
X	if (n < 0)
X		status = forwsearch(f, -n);
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.
X	 */
X	else if ((status = readpattern("Reverse search", &pat[0], TRUE)) == TRUE)
X	{
X		do
X		{
X#if	MAGIC
X			if ((magical && curwp->w_bufp->b_mode & MDMAGIC) != 0)
X				status = mcscanner(&tapcm[0], REVERSE, PTBEG);
X			else
X#endif
X				status = scanner(&tap[0], REVERSE, PTBEG);
X		} while ((--n > 0) && status);
X
X		/* ...and complain if not there.
X		 */
X		if (status == FALSE)
X			mlwrite("Not found");
X	}
X	return(status);
X}
X
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 */
Xbackhunt(f, n)
X{
X	register int	status = TRUE;
X
X	/* Resolve null and negative arguments.
X	 */
X	if (n == 0)
X		n = 1;
X	else 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#if	MAGIC
X	if ((curwp->w_bufp->b_mode & MDMAGIC) != 0 &&
X		 tapcm[0].mc_type == MCNIL)
X	{
X		if (!mcstr())
X			return FALSE;
X	}
X#endif
X
X	/* Go search for it...
X	 */
X	do
X	{
X#if	MAGIC
X		if ((magical && curwp->w_bufp->b_mode & MDMAGIC) != 0)
X			status = mcscanner(&tapcm[0], REVERSE, PTBEG);
X		else
X#endif
X			status = scanner(&tap[0], REVERSE, PTBEG);
X	} while ((--n > 0) && status);
X
X	/* ...and complain if not there.
X	 */
X	if (status == FALSE)
X		mlwrite("Not found");
X
X	return(status);
X}
X
X#if	MAGIC
X/*
X * mcscanner -- Search for a meta-pattern in either direction.
X */
Xint	mcscanner(mcpatrn, direct, beg_or_end)
XMC	*mcpatrn;		/* pointer into pattern */
Xint	direct;		/* which way to go.*/
Xint	beg_or_end;	/* put point at beginning or end of pattern.*/
X{
X	register LINE *lastline;	/* last line position during scan */
X	register int lastoff;		/* position within last line */
X	LINE *curline;			/* current line during scan */
X	int curoff;			/* position within current line */
X	int c;				/* (dummy) char at current position */
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	/* Setup local scan pointers to global ".".
X	 */
X	curline = curwp->w_dotp;
X	curoff  = curwp->w_doto;
X
X	/* Scan each character until we hit the head link record.
X	 */
X	while (!boundry(curline, curoff, direct))
X	{
X		/* Save the current position in case we need to
X		 * restore it on a match, and initialize mclen to
X		 * zero in case we are doing a search for replacement.
X		 */
X		lastline = curline;
X		lastoff = curoff;
X		mclen = 0;
X
X		if (amatch(mcpatrn, direct, &curline, &curoff))
X		{
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 = lastline;
X				curwp->w_doto = lastoff;
X			}
X
X			curwp->w_flag |= WFMOVE; /* flag that we have moved */
X			return TRUE;
X		}
X
X		/* Advance the cursor.
X		 */
X		c = nextch(&curline, &curoff, direct);
X	}
X
X	return FALSE;	/* We could not find a match.*/
X}
X
X/*
X * amatch -- 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 */
Xstatic int	amatch(mcptr, direct, pcwline, pcwoff)
Xregister MC	*mcptr;	/* string to scan for */
Xint		direct;		/* which way to go.*/
XLINE		**pcwline;	/* current line during scan */
Xint		*pcwoff;	/* position within current line */
X{
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);
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 never matches a closure.
X			 */
X			nchars = 0;
X			while (c != '\n' && mceq(c, mcptr))
X			{
X				c = nextch(&curline, &curoff, direct);
X				nchars++;
X			}
X
X			/* We are now at the character that made us
X			 * fail.  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				c = nextch(&curline, &curoff, direct ^ REVERSE);
X
X				if (amatch(mcptr, direct, &curline, &curoff))
X				{
X					mclen += 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				if (curoff == llength(curline))
X				{
X					c = nextch(&curline, &curoff,
X						   direct ^ REVERSE);
X					goto success;
X				}
X				else
X					return FALSE;
X
X			if (mcptr->mc_type == EOL)
X				if (curoff == 0)
X				{
X					c = nextch(&curline, &curoff,
X						   direct ^ REVERSE);
X					goto success;
X				}
X				else
X					return FALSE;
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		mclen++;
X		mcptr++;
X	}			/* End of mcptr loop.*/
X
X	/* A SUCCESSFULL MATCH!!!
X	 * Reset the "." pointers.
X	 */
Xsuccess:
X	*pcwline = curline;
X	*pcwoff  = curoff;
X
X	return TRUE;
X}
X#endif
X
X/*
X * scanner -- Search for a pattern in either direction.
X */
Xint	scanner(patrn, direct, beg_or_end)
Xchar	*patrn;		/* string to scan for */
Xint	direct;		/* which way to go.*/
Xint	beg_or_end;	/* put point at beginning or end of pattern.*/
X{
X	register int c;			/* character at current position */
X	register char *patptr;		/* pointer into pattern */
X	register LINE *lastline;	/* last line position during scan */
X	register int lastoff;		/* position within last line */
X	LINE *curline;			/* current line during scan */
X	int curoff;			/* position within current line */
X	LINE *matchline;		/* current line during matching */
X	int matchoff;			/* position in matching line */
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	/* Setup local scan pointers to global ".".
X	 */
X	curline = curwp->w_dotp;
X	curoff = curwp->w_doto;
X
X	/* Scan each character until we hit the head link record.
X	 */
X	while (!boundry(curline, curoff, direct))
X	{
X		/* Save the current position in case we need to
X		 * restore it on a match.
X		 */
X		lastline = curline;
X		lastoff = curoff;
X
X		/* Get the character resolving newlines, and
X		 * test it against first char in pattern.
X		 */
X		c = nextch(&curline, &curoff, direct);
X
X		if (eq(c, patrn[0]))	/* if we find it..*/
X		{
X			/* Setup match pointers.
X			 */
X			matchline = curline;
X			matchoff = curoff;
X			patptr = &patrn[0];
X
X			/* Scan through the pattern for a match.
X			 */
X			while (*++patptr != '\0')
X			{
X				c = nextch(&matchline, &matchoff, direct);
X
X				if (!eq(c, *patptr))
X					goto fail;
X			}
X
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 = matchline;
X				curwp->w_doto = matchoff;
X			}
X			else		/* at beginning of string */
X			{
X				curwp->w_dotp = lastline;
X				curwp->w_doto = lastoff;
X			}
X
X			curwp->w_flag |= WFMOVE; /* Flag that we have moved.*/
X			return TRUE;
X
X		}
Xfail:;			/* continue to search */
X	}
X
X	return FALSE;	/* We could not find a match */
X}
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 */
Xint	eq(bc, pc)
Xregister int	bc;
Xregister int	pc;
X{
X	if ((curwp->w_bufp->b_mode & MDEXACT) == 0)
X	{
X		if (islower(bc))
X			bc ^= DIFCASE;
X
X		if (islower(pc))
X			pc ^= DIFCASE;
X	}
X
X	return (bc == pc);
X}
X
X/*
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.  Change to using
X *	<META> to delimit the end-of-pattern to allow <NL>s in the search
X *	string. 
X */
Xstatic int	readpattern(prompt, apat, srch)
Xchar	*prompt;
Xchar	apat[];
Xint	srch;
X{
X	int status;
X	char tpat[NPAT+20];
X
X	strcpy(tpat, prompt);	/* copy prompt to output string */
X	strcat(tpat, " [");	/* build new prompt string */
X	expandp(&apat[0], &tpat[strlen(tpat)], NPAT/2);	/* add old pattern */
X	strcat(tpat, "]<META>: ");
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 ((status = mlreplyt(tpat, tpat, NPAT, metac)) == TRUE)
X	{
X		strcpy(apat, tpat);
X		if (srch)	/* If we are doing the search string.*/
X		{
X			/* Reverse string copy.
X			 */
X			rvstrcpy(tap, apat);
X#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				mcpat[0].mc_type = tapcm[0].mc_type = MCNIL;
X			else
X				status = mcstr();
X#endif
X		}
X	}
X	else if (status == FALSE && apat[0] != 0)	/* Old one */
X		status = TRUE;
X
X	return(status);
X}
X
X/*
X * rvstrcpy -- Reverse string copy.
X */
Xrvstrcpy(rvstr, str)
Xregister char	*rvstr, *str;
X{
X	register int i;
X
X	str += (i = strlen(str));
X
X	while (i-- > 0)
X		*rvstr++ = *--str;
X
X	*rvstr = '\0';
X}
X
X/*
X * sreplace -- Search and replace.
X */
Xsreplace(f, n)
Xint f;		/* default flag */
Xint n;		/* # of repetitions wanted */
X{
X	return(replaces(FALSE, f, n));
X}
X
X/*
X * qreplace -- search and replace with query.
X */
Xqreplace(f, n)
Xint f;		/* default flag */
Xint n;		/* # of repetitions wanted */
X{
X	return(replaces(TRUE, f, n));
X}
X
X/*
X * replaces -- Search for a string and replace it with another
X *	string.  Query might be enabled (according to kind).
X */
Xstatic int	replaces(kind, f, n)
Xint	kind;	/* Query enabled flag */
Xint	f;	/* default flag */
Xint	n;	/* # of repetitions wanted */
X{
X	register int i;		/* loop index */
X	register int status;	/* success flag on pattern inputs */
X	register int slength,
X		     rlength;	/* length of search and replace strings */
X	register int numsub;	/* number of substitutions */
X	register int nummatch;	/* number of found matches */
X	int nlflag;		/* last char of search string a <NL>? */
X	int nlrepl;		/* was a replace done on the last line? */
X	char tmpc;		/* temporary character */
X	char c;			/* input char for query */
X	char tpat[NPAT];	/* temporary to hold search pattern */
X	LINE *origline;		/* original "." position */
X	int origoff;		/* and offset (for . query option) */
X	LINE *lastline;		/* position of last replace and */
X	int lastoff;		/* offset (for 'u' query option) */
X
X	if (curbp->b_mode & MDVIEW)	/* don't allow this command if	*/
X		return(rdonly());	/* we are in read only mode	*/
X
X	/* Check for negative repetitions.
X	 */
X	if (f && n < 0)
X		return(FALSE);
X
X	/* Ask the user for the text of a pattern.
X	 */
X	if ((status = readpattern(
X	    (kind == FALSE ? "Replace" : "Query replace"), &pat[0], TRUE))
X								!= TRUE)
X		return(status);
X
X	/* Ask for the replacement string.
X	 */
X	if ((status = readpattern("with", &rpat[0], FALSE)) == ABORT)
X		return(status);
X
X	/* Find the lengths of the strings.
X	 */
X	slength = strlen(&pat[0]);
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[slength - 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;
X	}
X
X	/* Save original . position, init the number of matches and
X	 * substitutions, and scan through the file.
X	 */
X	origline = curwp->w_dotp;
X	origoff = curwp->w_doto;
X	numsub = 0;
X	nummatch = 0;
X
X	while ( (f == FALSE || n > nummatch) &&
X		(nlflag == FALSE || nlrepl == FALSE) )
X	{
X		/* Search for the pattern.
X		 * If we search with a regular expression, also save
X		 * the true length of matched string.
X		 */
X#if	MAGIC
X		if ((magical && curwp->w_bufp->b_mode & MDMAGIC) != 0)
X		{
X			if (!mcscanner(&mcpat[0], FORWARD, PTBEG))
X				break;
X			slength = mclen;
X		}
X		else
X#endif
X			if (!scanner(&pat[0], FORWARD, PTBEG))
X				break;		/* all done */
X
X		++nummatch;	/* Increment # of matches */
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			 */
Xpprompt:		mlwrite(&tpat[0], &pat[0], &rpat[0]);
Xqprompt:
X			update();  /* show the proposed place to change */
X			c = tgetc();			/* and input */
X			mlwrite("");			/* and clear it */
X
X			/* And respond appropriately.
X			 */
X			switch (c)
X			{
X				case 'y':	/* yes, substitute */
X				case ' ':
X					break;
X
X				case 'n':	/* no, onword */
X					forwchar(FALSE, 1);
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 qprompt;
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					if (!ldelete(rlength, FALSE))
X					{
X						mlwrite("%%ERROR while deleting");
X						return(FALSE);
X					}
X
X					/* And put in the old one.
X					 */
X					for (i = 0; i < slength; i++)
X					{
X						tmpc = pat[i];
X						status = (tmpc == '\n'?
X							lnewline():
X							linsert(1, tmpc));
X
X						/* Insertion error?
X						 */
X						if (!status)
X						{
X							mlwrite("%%Out of memory while inserting");
X							return(FALSE);
X						}
X					}
X
X					/* Record one less substitution,
X					 * backup, and reprompt.
X					 */
X					--numsub;
X					backchar(FALSE, slength);
X					goto pprompt;
X
X				case '.':	/* abort! and return */
X					/* restore old position */
X					curwp->w_dotp = origline;
X					curwp->w_doto = origoff;
X					curwp->w_flag |= WFMOVE;
X
X				case BELL:	/* abort! and stay */
X					mlwrite("Aborted!");
X					return(FALSE);
X
X				default:	/* bitch and beep */
X					TTbeep();
X
X				case '?':	/* help me */
X					mlwrite(
X"(Y)es, (N)o, (!)Do rest, (U)ndo last, (^G)Abort, (.)Abort back, (?)Help: ");
X					goto qprompt;
X
X			}	/* end of switch */
X		}	/* end of "if kind" */
X
X		/*
X		 * Delete the sucker.
X		 */
X		if (!ldelete(slength, FALSE))
X		{
X			mlwrite("%%ERROR while deleteing");
X			return(FALSE);
X		}
X
X		/* And insert its replacement.
X		 */
X		for (i = 0; i < rlength; i++)
X		{
X			tmpc = rpat[i];
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				return(FALSE);
X			}
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
X/*
X * expandp -- Expand control key sequences for output.
X */
Xexpandp(srcstr, deststr, maxlength)
Xchar *srcstr;	/* string to expand */
Xchar *deststr;	/* destination of expanded string */
Xint maxlength;	/* maximum chars in destination */
X{
X	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 (c < 0x20 || c == 0x7f)	/* control character */
X		{
X			*deststr++ = '^';
X			*deststr++ = c ^ 0x40;
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
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 boundry restrictions
X *	in future, a' la VMS EDT.  At the moment, just return TRUE or
X *	FALSE depending on if a boundry is hit (ouch).
X */
Xint	boundry(curline, curoff, dir)
XLINE	*curline;
Xint	curoff, dir;
X{
X	register int	border;
X
X	if (dir == FORWARD)
X	{
X		border = (curoff == llength(curline)) &&
X			 (lforw(curline) == curbp->b_linep);
X	}
X	else
X	{
X		border = (curoff == 0) &&
X			 (lback(curline) == curbp->b_linep);
X	}
X	return (border);
X}
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 */
Xstatic int	nextch(pcurline, pcuroff, dir)
XLINE	**pcurline;
Xint	*pcuroff;
Xint	dir;
X{
X	register LINE	*curline;
X	register int	curoff;
X	register int	c;
X
X	curline = *pcurline;
X	curoff = *pcuroff;
X
X	if (dir == FORWARD)
X	{
X		if (curoff == llength(curline))		/* if at EOL */
X		{
X			curline = lforw(curline);	/* skip to next line */
X			curoff = 0;
X			c = '\n';			/* and return a <NL> */
X		}
X		else
X			c = lgetc(curline, curoff++);	/* get the char */
X	}
X	else			/* Reverse.*/
X	{
X		if (curoff == 0)
X		{
X			curline = lback(curline);
X			curoff = llength(curline);
X			c = '\n';
X		}
X		else
X			c = lgetc(curline, --curoff);
X
X	}
X	*pcurline = curline;
X	*pcuroff = curoff;
X
X	return (c);
X}
X
X#if	MAGIC
X/*
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 */
Xstatic int	mcstr()
X{
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		freebits();
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:
Xlitcase:			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, call freebits() to
X	 * free any other bitmaps, and set the zeroth array to MCNIL.
X	 */
X	if (status)
X	{
X		rtpcm = &tapcm[0];
X		while (--mj >= 0)
X		{
X#if	LATTICE
X			movmem(--mcptr, rtpcm++, sizeof (MC));
X#endif
X
X#if	MWC86 | AZTEC | MSC | VMS | USG | BSD | V7
X			*rtpcm++ = *--mcptr;
X#endif
X		}
X		rtpcm->mc_type = MCNIL;
X	}
X	else
X	{
X		(--mcptr)->mc_type = MCNIL;
X		freebits();
X		mcpat[0].mc_type = tapcm[0].mc_type = MCNIL;
X	}
X
X	return(status);
X}
X
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 */
Xstatic int	mceq(bc, mt)
Xint	bc;
XMC	*mt;
X{
X	register int result;
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				    (isletter(bc)))
X				{
X					result = biteq(CHCASE(bc), mt->u.cclmap);
X				}
X			}
X			break;
X
X		case NCCL:
X			result = !biteq(bc, mt->u.cclmap);
X
X			if ((curwp->w_bufp->b_mode & MDEXACT) == 0 &&
X			    (isletter(bc)))
X			{
X				result &= !biteq(CHCASE(bc), mt->u.cclmap);
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
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 */
Xstatic int	cclmake(ppatptr, mcptr)
Xchar	**ppatptr;
XMC	*mcptr;
X{
X	BITMAP		clearbits();
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("%%No characters in 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("%%Character class not ended");
X		free(bmap);
X		return FALSE;
X	}
X	return TRUE;
X}
X
X/*
X * biteq -- is the character in the bitmap?
X */
Xstatic int	biteq(bc, cclmap)
Xint	bc;
XBITMAP	cclmap;
X{
X	if (bc >= HICHAR)
X		return FALSE;
X
X	return( (*(cclmap + (bc >> 3)) & BIT(bc & 7))? TRUE: FALSE );
X}
X
X/*
X * clearbits -- Allocate and zero out a CCL bitmap.
X */
Xstatic	BITMAP clearbits()
X{
X	char		*malloc();
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
X/*
X * freebits -- Free up any CCL bitmaps.
X */
Xstatic	freebits()
X{
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}
X
X/*
X * setbit -- Set a bit (ON only) in the bitmap.
X */
Xstatic	setbit(bc, cclmap)
Xint	bc;
XBITMAP	cclmap;
X{
X	if (bc < HICHAR)
X		*(cclmap + (bc >> 3)) |= BIT(bc & 7);
X}
X#endif
END_OF_search.c
if test 30773 -ne `wc -c <search.c`; then
    echo shar: \"search.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
echo shar: End of archive 12 \(of 14\).
cp /dev/null ark12isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 14 archives.
    echo "See the readme file"
    rm -f ark[1-9]isdone ark[1-9][0-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell ve arrs;ingsings