File:  [MW Coherent from dump] / coherent / g / usr / bin / me / search.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Wed May 29 04:56:35 2019 UTC (7 years ago) by root
Branches: MarkWilliams, MAIN
CVS tags: relic, HEAD
coherent


/*
 * 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,
 * with some stylistic modifications.
 */
#include	<stdio.h>
#include	"ed.h"

#define CCHR(x)		((x)-'@')

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

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

extern	int	ctrlg();

static	SRCHCOM	cmds[NSRCH];
static	int	cip;

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

/*
 * 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.
 * This is normally bound to "M-S".
 */
forwsearch(f, n)
{
	register int	s;

	if ((s=readpattern("Search")) != TRUE)
		return (s);
	if (forwsrch() == FALSE) {
		mlwrite("Not found");
		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]. Bound to "M-R".
 */
backsearch(f, n)
{
	register int	s;

	if ((s=readpattern("Reverse search")) != TRUE)
		return (s);
	if (backsrch() == FALSE) {
		mlwrite("Not found");
		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.
 * Bound to "M-/".
 */
searchagain(f, n)
{
	if (srch_lastdir == SRCH_FORW) {
		if (forwsrch() == FALSE) {
			mlwrite("Not found");
			return (FALSE);
		}
		return (TRUE);
	}
	if (srch_lastdir == SRCH_BACK) {
		if (backsrch() == FALSE) {
			mlwrite("Not found");
			return (FALSE);
		}
		return (TRUE);
	}
	mlwrite("No last search");
	return (FALSE);
}

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

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

/*
 * Incremental Search.
 *	dir is used as the initial direction to search.
 *	^N	find next occurance  (if first thing typed reuse old string).
 *	^P	find prev occurance  (if first thing typed reuse old string).
 *	^S	switch direction to forward, find next
 *	^R	switch direction to reverse, find prev
 *	^Q	quote next character (allows searching for ^N etc.)
 *	<ESC>	exit from Isearch.
 *	<DEL>
 *	^H	undoes last character typed. (tricky job to do this correctly).
 *	else	accumulate into search string
 */
isearch(dir)
{
	int	c;
	int	pptr;
	LINE	*clp;
	int	cbo;
	int	success;

	for (cip=0; cip<NSRCH; cip++)
		cmds[cip].s_code = SRCH_NOPR;
	cip = 0;
	pptr = -1;
	clp = curwp->w_dotp;
	cbo = curwp->w_doto;
	isrch_lpush();
	isrch_cpush(SRCH_BEGIN);
	success = TRUE;
	isrch_prompt(dir,TRUE,success);
	for (;;) {
		update();
		c = ttgetc();
		bracketoff();
		switch (c) {

		case CCHR('M'):
		case METACH:
#if LK201
		case AGRAVE:
#endif
			srch_lastdir = dir;
			mlwrite("[Done]");
			return (TRUE);

		case CCHR('G'):
			curwp->w_dotp = clp;
			curwp->w_doto = cbo;
			curwp->w_flag |= WFMOVE;
			srch_lastdir = dir;
			mlwrite("Aborting");
			return (FALSE);

		case CCHR('S'):
		case CCHR('F'):
			if (dir == SRCH_BACK) {
				dir = SRCH_FORW;
				isrch_lpush();
				isrch_cpush(SRCH_FORW);
				success = TRUE;
			}
			/* drop through to find next	*/

		case CCHR('N'):
			if ((success == FALSE)&&(dir == SRCH_FORW))
				break;
			isrch_lpush();
			forwchar(FALSE, 1);
			if (isrch_find(SRCH_NEXT) != FALSE) {
				isrch_cpush(SRCH_NEXT);
				pptr = strlen(pat);
			} else {
				backchar(FALSE, 1);
				tbeep();
				success = FALSE;
			}
			isrch_prompt(dir,FALSE,success);
			break;

		case CCHR('R'):
		case CCHR('B'):
			if (dir == SRCH_FORW) {
				dir = SRCH_BACK;
				isrch_lpush();
				isrch_cpush(SRCH_BACK);
				success = TRUE;
			}
			/* drop through to find prev	*/

		case CCHR('P'):
			if ((success == FALSE)&&(dir == SRCH_BACK))
				break;
			isrch_lpush();
			backchar(FALSE, 1);
			if (isrch_find(SRCH_PREV) != FALSE) {
				isrch_cpush(SRCH_PREV);
				pptr = strlen(pat);
			} else {
				forwchar(FALSE, 1);
				tbeep();
				success = FALSE;
			}
			isrch_prompt(dir,FALSE,success);
			break;

		case CCHR('H'):
		case 0x7f:
			if (isrch_undo(&pptr,&dir) != TRUE) {
				return (ABORT);
			}
			if (isrch_peek() != SRCH_ACCM)
				success = TRUE;
			isrch_prompt(dir,FALSE,success);
			break;

		case CCHR('^'):
		case CCHR('Q'):
			c = ttgetc();
		case CCHR('U'):
		case CCHR('X'):
		case CCHR('I'):
		case CCHR('J'):
			goto  addchar;
		default:
			if (c < ' ') {		/* uninterpreted ctrl	*/
				c += '@';
				c |= CTRL;
				success = execute(c, FALSE, 1);
				curwp->w_flag |= WFMOVE;
				return (success);
			}				
addchar:
			if (pptr == -1)
				pptr=0;
			if (pptr == 0)
				success = TRUE;
			pat[pptr++] = c;
			if (pptr==NPAT) {
				mlwrite("Pattern too long");
				ctrlg(FALSE, 0);
				return (ABORT);
			}
			pat[pptr] = '\0';
			isrch_lpush();
			if (success != FALSE) {
				if (isrch_find(dir) != FALSE) {
					isrch_cpush(c);
				} else {
					success = FALSE;
					tbeep();
					isrch_cpush(SRCH_ACCM);
				}
			} else {
				isrch_cpush(SRCH_ACCM);
			}
			isrch_prompt(dir, FALSE, success);
		}
	}
}

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

isrch_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;
}

isrch_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;	/* Was "=", wrong.	*/
		cmds[cip].s_code = SRCH_NOPR;
	}
	if (--cip <= 0)				/* Is "=" right?	*/
		cip = NSRCH-1;
}

int
isrch_peek()	
{
	if (cip == 0)
		return (cmds[NSRCH-1].s_code);
	else
		return (cmds[cip-1].s_code);
}

int
isrch_undo(pptr,dir)
register int	*pptr;
register int	*dir;
{
	switch (cmds[cip].s_code) {
	case SRCH_NOPR:
	case SRCH_BEGIN:
	case SRCH_NEXT:
	case SRCH_PREV:
		break;

	case SRCH_FORW:
		if (*dir == SRCH_BACK) {
			mlwrite("ISEARCH error,  to back when already back");
			return (FALSE);
		}
		*dir = SRCH_BACK;
		break;

	case SRCH_BACK:
		if (*dir == SRCH_FORW) {
			mlwrite("ISEARCH error,  to forw when already forw");
			return (FALSE);
		}
		*dir = SRCH_FORW;
		break;

	case SRCH_ACCM:
	default:
		if (*pptr == 0) {
			mlwrite("ISEARCH error,  delete without character");
			return (FALSE);
		}
		*pptr -= 1;
		if (*pptr < 0)
			*pptr = 0;
		pat[*pptr] = '\0';
		break;
	}
	isrch_pop();
	return (TRUE);
}

isrch_find(dir)
register int	dir;
{
	register int	plen;

	plen = strlen(pat);
	if (plen != 0) {
		if (dir==SRCH_FORW || dir==SRCH_NEXT) {
			backchar(FALSE, plen);
			if (forwsrch() == FALSE) {
				forwchar(FALSE, plen);
				return (FALSE);
			}
			return (TRUE);
		}
		if (dir==SRCH_BACK || dir==SRCH_PREV) {
			forwchar(FALSE, plen);
			if (backsrch() == FALSE) {
				backchar(FALSE, plen);
				return (FALSE);
			}
			return (TRUE);
		}
		mlwrite("bad call to isrch_find");
		ctrlg(FALSE, 0);
		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.
 */
isrch_prompt(dir, flag, success)
{
	if (dir == SRCH_FORW) {
		if (success != FALSE)
			isrch_dspl("i-search forward", flag);
		else {
			isrch_dspl("failing i-search forward", flag);
			curwp->w_flag |= WFEDIT;
		}

	} else if (dir == SRCH_BACK) {
		if (success != FALSE)
			isrch_dspl("i-search backward", flag);
		else {
			isrch_dspl("failing i-search backward", flag);
			curwp->w_flag |= WFEDIT;
		}
	}
}

/*
 * Prompt writing routine for the incremental
 * search. The "prompt" is just a string. The "flag" determines
 * if a "[ ]" or ":" embelishment is used. The string is packed
 * into a big buffer and zapped out with a single call to
 * "mlwrite". The "26" in the "tpat" declaration is the length
 * of the longest prompt string. This actually isn't long
 * enough if the pattern is full of control characters
 * and other things that need an "^".
 */
isrch_dspl(prompt, flag)
uchar	*prompt;
{
	register uchar	*cp1;
	register uchar	*cp2;
	register unsigned c;
	uchar		tpat[NPAT+26+2+1+1];

	cp1 = &tpat[0];
	cp2 = prompt;
	while ((c = *cp2++) != '\0')
		*cp1++ = c;
	if (flag != FALSE) {
		*cp1++ = ' ';
		*cp1++ = '[';
	} else {
		*cp1++ = ':';
		*cp1++ = ' ';
	}
	cp2 = &pat[0];
	while ((c = *cp2++) != 0) {
		if (cp1 < &tpat[NPAT+20-4]) {	/* "??]\0"		*/
			if (dblchr(c)) {
				*cp1++ = '^';
				c ^= 0x40;
			} else if (c == '%')	/* Map "%" to		*/
				*cp1++ = c;	/* "%%".		*/
			*cp1++ = c;
		}
	}
	if (flag != FALSE)
		*cp1++ = ']';
	*cp1= '\0';
	mlwrite(tpat);
}

/*
 * Query Replace.
 *	Replace strings selectively.  Does a search and replace operation.
 *	A space or a comma replaces the string, a period replaces and quits,
 *	an n doesn't replace, a C-G quits.
 */
queryrepl(f, n)
{
	register int	s;
	uchar		news[NPAT];
	LINE		*clp;
	int		cbo;
	register int	flg;
	register int	flgc;
	int		rcnt;

	if ((s=readpattern("Old string")) != TRUE)
		return (s);
	if ((s=mlreply("New string: ",news, NPAT)) == ABORT)
		return (s);
	if (s == FALSE)				/* Null string.		*/
		news[0] = '\0';
	mlwrite("Query Replace:  [%s] -> [%s]", pat, news);
	clp = curwp->w_dotp;
	cbo = curwp->w_doto;
	rcnt = 0;
	flg = TRUE;
	flgc = FALSE;
	while (flg==TRUE && forwsrch()==TRUE) {
		if (flgc != TRUE) {
		retry:
			update();
			switch (ttgetc()) {
			case ' ':
			case ',':
				replstring(news, f);
				rcnt++;
				break;

			case '.':
				replstring(news, f);
				rcnt++;
				flg = FALSE;
				break;

			case '\007':
				flg = ABORT;
				ctrlg(FALSE, 0);
				break;

			case '!':
				replstring(news, f);
				rcnt++;
				flgc = TRUE;
				break;

			case 'n':
				break;

			default:
mlwrite("<SP>[,] replace,  [.] rep-end,  [n] dont,  [!] repl rest  <C-G> quit");
				goto retry;
			}
		} else {
			replstring(news, f);
			rcnt++;
		}
	}
	curwp->w_dotp = clp;
	curwp->w_doto = cbo;
	curwp->w_flag |= WFHARD;
	update();
	if (rcnt == 0)
		mlwrite("[No replacements done]");
	else if (rcnt == 1)
		mlwrite("[1 replacement done]");
	else
		mlwrite("[%d replacements done]", rcnt);
	return (TRUE);
}

#define	FLOWER	0				/* Found lower case.	*/
#define	FCAPTL	1				/* Found capital.	*/
#define	FUPPER	2				/* Found upper case.	*/

replstring(st, f)
uchar	*st;
int	f;
{
	register uchar	*tpt;
	register int	plen;
	register int	rtype;
	register int	c, d;

	plen = strlen(pat);
	backchar(TRUE, plen);
	c = lgetc(curwp->w_dotp, curwp->w_doto);
	rtype = FLOWER;
	if (ishi(c)) {
		rtype = FCAPTL;
		if (curwp->w_doto+1 != llength(curwp->w_dotp)) {
			c = lgetc(curwp->w_dotp, curwp->w_doto+1);
			if (ishi(c))
				rtype = FUPPER;
		}
	}
	ldelete(plen, FALSE);
	tpt = &st[0];
	if (f != FALSE) {
		while (*tpt != '\0')
			linsert(1, *tpt++);
	} else {
		switch (rtype) {
		case FLOWER:
			while (*tpt != '\0') {
				linsert(1, *tpt++);
			}
			break;

		case FCAPTL:
			if (*tpt != '\0') {
				if (d = islow(*tpt))
					linsert(1, d);
				else
					linsert(1, *tpt);
				tpt++;
			}
			while (*tpt != '\0')
				linsert(1, *tpt++);
			break;

		case FUPPER:
			while (*tpt != '\0') {
				if (d = islow(*tpt))
					linsert(1, d);
				else
					linsert(1, *tpt);
				tpt++;
			}
			break;
		}
	}
	curwp->w_flag |= WFHARD;
}

/*
 * 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;
	register uchar	*pp;
	register int	c;

	clp = curwp->w_dotp;
	cbo = curwp->w_doto;
	while (clp != curbp->b_linep) {
		if (cbo == llength(clp)) {
			clp = lforw(clp);
			cbo = 0;
			c = '\n';
		} else
			c = lgetc(clp, cbo++);
		if (eq(c, pat[0]) != FALSE) {
			tlp = clp;
			tbo = cbo;
			pp  = &pat[1];
			while (*pp != 0) {
				if (tlp == curbp->b_linep)
					goto fail;
				if (tbo == llength(tlp)) {
					tlp = lforw(tlp);
					tbo = 0;
					c = '\n';
				} 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 uchar	*epp;
	register uchar	*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 = '\n';
		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 = '\n';
				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:	;
	}
}

/*
 * Compare two characters.
 * The "bc" comes from the buffer.
 * It has it's case folded out. The
 * "pc" is from the pattern.
 */
eq(bc, pc)
register int	bc;
register int	pc;
{
	int c;

	if (bind.ffold) {
		if (c = islow(bc))
			bc = c;
		if (c = islow(pc))
			pc = c;
	}
	if (bc == pc)
		return (TRUE);
	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)
uchar	*prompt;
{
	register uchar	*cp1;
	register uchar	*cp2;
	register unsigned c;
	register int	s;
	uchar		tpat[NPAT+20];

	cp1 = &tpat[0];				/* Copy prompt		*/
	cp2 = prompt;
	while ((c = *cp2++) != '\0')
		*cp1++ = c;
	if (pat[0] != '\0') {			/* Old pattern		*/
		*cp1++ = ' ';
		*cp1++ = '[';
		cp2 = &pat[0];
		while ((c = *cp2++) != 0) {
			if (cp1 < &tpat[NPAT+20-6]) {	/* "??]: \0"	*/
				if (dblchr(c)) {
					*cp1++ = '^';
					c ^= 0x40;
				} else if (c == '%')	/* Map "%" to	*/
					*cp1++ = c;	/* "%%".	*/
				*cp1++ = c;
			}
		}
		*cp1++ = ']';
	}
	*cp1++ = ':';				/* Finish prompt	*/
	*cp1++ = ' ';
	*cp1++ = '\0';
	s = mlreply(tpat, tpat, NPAT);		/* Read pattern		*/
	if (s == TRUE)				/* Specified		*/
		strcpy(pat, tpat);
	else if (s==FALSE && pat[0]!=0)		/* CR, but old one	*/
		s = TRUE;
	return (s);
}

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.