4.3BSD/usr/contrib/jove/re1.c

Compare this file to the similar file:
Show the results in this format:

/*************************************************************************
 * This program is copyright (C) 1985, 1986 by Jonathan Payne.  It is    *
 * provided to you without charge for use only on a licensed Unix        *
 * system.  You may copy JOVE provided that this notice is included with *
 * the copy.  You may not sell copies of this program or versions        *
 * modified for use on microcomputer systems, unless the copies are      *
 * included with a Unix system distribution and the source is provided.  *
 *************************************************************************/

#include "jove.h"
#include "io.h"
#include "re.h"

static
substitute(query, l1, char1, l2, char2)
Line	*l1,
	*l2;
{
	Line	*lp;
	int	numdone = 0,
		offset = curchar,
		stop = 0;
	disk_line	UNDO_da = 0;
	Line		*UNDO_lp = 0;

	lsave();
	REdirection = FORWARD;

	lp = l1;
	for (lp = l1; (lp != l2->l_next) && !stop; lp = lp->l_next) {
		offset = (lp == l1) ? char1 : 0;
		while (!stop && re_lindex(lp, offset, compbuf, alternates, 0)) {
			if (lp == l2 && REeom > char2)	/* nope, leave this alone */
				break;
			DotTo(lp, REeom);
			offset = curchar;
			if (query) {
				message("Replace (Type '?' for help)? ");
reswitch:			redisplay();
				switch (Upper(getchar())) {
				case '.':
					stop++;
					/* Fall into ... */

				case ' ':
				case 'Y':
					break;

				case BS:
				case RUBOUT:
				case 'N':
					if (linebuf[offset++] == '\0')
						goto nxtline;
					continue;

				case CTL(W):
					re_dosub(linebuf, YES);
					numdone++;
					offset = curchar = REbom;
					makedirty(curline);
					/* Fall into ... */

				case CTL(R):
				case 'R':
					RErecur();
					offset = curchar;
					lp = curline;
					continue;

				case CTL(U):
				case 'U':
					if (UNDO_lp == 0)
						continue;
					lp = UNDO_lp;
					lp->l_dline = UNDO_da | DIRTY;
					offset = 0;
					numdone--;
					continue;

				case 'P':
				case '!':
					query = 0;
					break;

				case CR:
				case LF:
				case 'Q':
					goto done;

				case CTL(L):
					RedrawDisplay();
					goto reswitch;

				default:
					rbell();
message("Space or Y, Period, Rubout or N, C-R or R, C-W, C-U or U, P or !, Return.");
					goto reswitch;
				}
			}
			re_dosub(linebuf, NO);
			numdone++;
			modify();
			offset = curchar = REeom;
			makedirty(curline);
			if (query) {
				message(mesgbuf);	/* No blinking. */
				redisplay();		/* Show the change. */
			}
			UNDO_da = curline->l_dline;
			UNDO_lp = curline;
			if (linebuf[offset] == 0)
nxtline:			break;
		}
	}
	SetMark();
done:	s_mess("%d substitution%n.", numdone, numdone);
}

/* Prompt for search and replacement strings and do the substitution.  The
   point is restored when we're done. */

static
replace(query, inreg)
{
	Mark	*save = MakeMark(curline, curchar, FLOATER),
		*m;
	char	*rep_ptr;
	Line	*l1 = curline,
		*l2 = curbuf->b_last;
	int	char1 = curchar,
		char2 = length(curbuf->b_last);

	if (inreg) {
		m = CurMark();
		l2 = m->m_line;
		char2 = m->m_char;
		(void) fixorder(&l1, &char1, &l2, &char2);
	}

	/* Get search string. */
	strcpy(rep_search, ask(rep_search[0] ? rep_search : (char *) 0, ProcFmt));
	REcompile(rep_search, UseRE, compbuf, alternates);
	/* Now the replacement string.  Do_ask() so the user can play with
	   the default (previous) replacement string by typing C-R in ask(),
	   OR, he can just hit Return to replace with nothing. */
	rep_ptr = do_ask("\r\n", (int (*)()) 0, rep_str, ": %f %s with ", rep_search);
	if (rep_ptr == 0)
		rep_ptr = NullStr;
	strcpy(rep_str, rep_ptr);

	substitute(query, l1, char1, l2, char2);
	ToMark(save);
	DelMark(save);
}

RegReplace()
{
	replace(0, YES);
}

QRepSearch()
{
	replace(1, NO);
}

RepSearch()
{
	replace(0, NO);
}

/* C tags package. */

static
lookup(searchbuf, filebuf, tag, file)
char	*searchbuf,
	*filebuf,
	*tag,
	*file;
{
	register int	taglen = strlen(tag);
	char	line[128],
		pattern[100];
	File	*fp;

	fp = open_file(file, iobuff, F_READ, !COMPLAIN, QUIET);
	if (fp == NIL)
		return 0;
	sprintf(pattern, "^%s[^\t]*\t\\([^\t]*\\)\t[?/]\\(.*\\)[?/]$", tag);
	while (f_gets(fp, line, sizeof line) != EOF) {
		if (line[0] != *tag || strncmp(tag, line, taglen) != 0)
			continue;
		if (!LookingAt(pattern, line, 0)) {
			complain("I thought I saw it!");
			break;
		} else {
			putmatch(2, searchbuf, 100);
			putmatch(1, filebuf, 100);
			close_file(fp);
			return 1;
		}
	}
	f_close(fp);
	s_mess("Can't find tag \"%s\".", tag);
	return 0;
}

char	TagFile[128] = "./tags";

find_tag(tag, localp)
char	*tag;
{
	char	filebuf[FILESIZE],
		sstr[100];
	register Bufpos	*bp;
	register Buffer	*b;
	char	*tagfname;

	if (!localp)
		tagfname = ask(TagFile, "With tag file (%s default): ", TagFile);
	else
		tagfname = TagFile;
	if (lookup(sstr, filebuf, tag, tagfname) == 0)
		return;
	SetMark();
	b = do_find(curwind, filebuf, 0);
	if (curbuf != b)
		SetABuf(curbuf);
	SetBuf(b);
	if ((bp = dosearch(sstr, BACKWARD, 0)) == 0 &&
	    (WrapScan || ((bp = dosearch(sstr, FORWARD, 0)) == 0)))
		message("Well, I found the file, but the tag is missing.");
	else
		SetDot(bp);
}

FindTag()
{
	int	localp = !exp_p;
	char	tag[128];

	strcpy(tag, ask((char *) 0, ProcFmt));
	find_tag(tag, localp);
}

/* Find Tag at Dot. */

FDotTag()
{
	int	c1 = curchar,
		c2 = c1;
	char	tagname[50];

	if (!ismword(linebuf[curchar]))
		complain("Not a tag!");
	while (c1 > 0 && ismword(linebuf[c1 - 1]))
		c1--;
	while (ismword(linebuf[c2]))
		c2++;

	null_ncpy(tagname, linebuf + c1, c2 - c1);
	find_tag(tagname, !exp_p);
}

/* I-search returns a code saying what to do:
   STOP:	We found the match, so unwind the stack and leave
		where it is.
   DELETE:	Rubout the last command.
   BACKUP:	Back up to where the isearch was last NOT failing.

   When a character is typed it is appended to the search string, and
   then, isearch is called recursively.  When C-S or C-R is typed, isearch
   is again called recursively. */

#define STOP	1
#define DELETE	2
#define BACKUP	3
#define TOSTART	4

static char	ISbuf[128],
		*incp = 0;
int	SExitChar = CR;

#define cmp_char(a, b)	((a) == (b) || (CaseIgnore && (Upper(a) == Upper(b))))

static Bufpos *
doisearch(dir, c, failing)
register int	c,
		dir,
		failing;
{
	static Bufpos	buf;
	Bufpos	*bp;

	if (c == CTL(S) || c == CTL(R))
		goto dosrch;

	if (failing)
		return 0;
	DOTsave(&buf);
	if (dir == FORWARD) {
		if (cmp_char(linebuf[curchar], c)) {
			buf.p_char = curchar + 1;
			return &buf;
		}
	} else {
		if (look_at(ISbuf))
			return &buf;
	}
dosrch:	if ((bp = dosearch(ISbuf, dir, 0)) == 0)
		rbell();	/* ring the first time there's no match */
	return bp;
}

IncFSearch()
{
	IncSearch(FORWARD);
}

IncRSearch()
{
	IncSearch(BACKWARD);
}

static
IncSearch(dir)
{
	Bufpos	save_env;

	DOTsave(&save_env);
	ISbuf[0] = 0;
	incp = ISbuf;
	if (isearch(dir, &save_env) == TOSTART)
		SetDot(&save_env);
	else {
		if (LineDist(curline, save_env.p_line) >= MarkThresh)
			DoSetMark(save_env.p_line, save_env.p_char);
	}
	setsearch(ISbuf);
	message(ISbuf);
}

/* Nicely recursive. */

static
isearch(dir, bp)
Bufpos	*bp;
{
	Bufpos	pushbp;
	int	c,
		ndir,
		failing;
	char	*orig_incp;

	if (bp != 0) {		/* Move to the new position. */
		pushbp.p_line = bp->p_line;
		pushbp.p_char = bp->p_char;
		SetDot(bp);
		failing = 0;
	} else {
		DOTsave(&pushbp);
		failing = 1;
	}
	orig_incp = incp;
	ndir = dir;		/* Same direction as when we got here, unless
				   we change it with C-S or C-R. */
	for (;;) {
		SetDot(&pushbp);
		message(NullStr);
		if (failing)
			add_mess("Failing ");
		if (dir == BACKWARD)
			add_mess("reverse-");
		add_mess("I-search: %s", ISbuf);
		DrawMesg(NO);
		add_mess(NullStr);	/* tell me this is disgusting ... */
		c = getch();
		if (c == SExitChar)
			return STOP;
		switch (c) {
		case RUBOUT:
		case BS:
			return DELETE;

		case CTL(G):
			/* If we're failing, we backup until we're no longer
			   failing or we've reached the beginning; else, we
			   just about the search and go back to the start. */
			if (failing)
				return BACKUP;
			return TOSTART;

		case CTL(\\):
			c = CTL(S);
		case CTL(S):
		case CTL(R):
			/* If this is the first time through and we have a
			   search string left over from last time, use that
			   one now. */
			if (incp == ISbuf) {
				strcpy(ISbuf, getsearch());
				incp = &ISbuf[strlen(ISbuf)];
			}
			ndir = (c == CTL(S)) ? FORWARD : BACKWARD;
			/* If we're failing and we're not changing our
			   direction, don't recur since there's no way
			   the search can work. */
			if (failing && ndir == dir) {
				rbell();
				continue;
			}
			break;

		case '\\':
			if (incp > &ISbuf[(sizeof ISbuf) - 1]) {
				rbell();
				continue;
			}
			*incp++ = '\\';
			add_mess("\\");
			/* Fall into ... */

		case CTL(Q):
		case CTL(^):
			add_mess("");
			c = getch() | 0400;
			/* Fall into ... */

		default:
			if (c & 0400)
				c &= 0177;
			else {
				if (c > RUBOUT || (c < ' ' && c != '\t')) {
					Ungetc(c);
					return STOP;
				}
			}
			if (incp > &ISbuf[(sizeof ISbuf) - 1]) {
				rbell();
				continue;
			}
			*incp++ = c;
			*incp = 0;
			break;
		}
		add_mess("%s", orig_incp);
		add_mess(" ...");	/* so we know what's going on */
		DrawMesg(NO);		/* do it now */
		switch (isearch(ndir, doisearch(ndir, c, failing))) {
		case TOSTART:
			return TOSTART;

		case STOP:
			return STOP;

		case BACKUP:
			/* If we're not failing, we just continue to to the
			   for loop; otherwise we keep returning to the 
			   previous levels until we find one that isn't
			   failing OR we reach the beginning. */
			if (failing)
				return BACKUP;
			/* Fall into ... */

		case DELETE:
			incp = orig_incp;
			*incp = 0;
			continue;
		}
	}
}