⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 search.c

📁 STEVIE文本文件编缉器的C 语言源程序
💻 C
📖 第 1 页 / 共 2 页
字号:
/* $Header: /nw2/tony/src/stevie/src/RCS/search.c,v 1.16 89/08/06 09:50:51 tony Exp $
 *
 * This file contains various searching-related routines. These fall into
 * three groups: string searches (for /, ?, n, and N), character searches
 * within a single line (for f, F, t, T, etc), and "other" kinds of searches
 * like the '%' command, and 'word' searches.
 */

#include "stevie.h"
#include "regexp.h"	/* Henry Spencer's (modified) reg. exp. routines */

/*
 * String searches
 *
 * The actual searches are done using Henry Spencer's regular expression
 * library.
 */

#define	BEGWORD	"([^a-zA-Z0-9_]|^)"	/* replaces "\<" in search strings */
#define	ENDWORD	"([^a-zA-Z0-9_]|$)"	/* likewise replaces "\>" */

#define	BEGCHAR(c)	(islower(c) || isupper(c) || isdigit(c) || ((c) == '_'))

bool_t	begword;	/* does the search include a 'begin word' match */

/*
 * mapstring(s) - map special backslash sequences
 */
static char *
mapstring(s)
register char	*s;
{
	static	char	ns[80];
	register char	*p;

	begword = FALSE;

	for (p = ns; *s ;s++) {
		if (*s != '\\') {	/* not an escape */
			*p++ = *s;
			continue;
		}
		switch (*++s) {
		case '/':
			*p++ = '/';
			break;

		case '<':
			strcpy(p, BEGWORD);
			p += strlen(BEGWORD);
			begword = TRUE;
			break;

		case '>':
			strcpy(p, ENDWORD);
			p += strlen(ENDWORD);
			break;

		default:
			*p++ = '\\';
			*p++ = *s;
			break;
		}
	}
	*p++ = NUL;

	return ns;
}

static char *laststr = NULL;
static int lastsdir;

static LPTR *
ssearch(dir,str)
int	dir;	/* FORWARD or BACKWARD */
char	*str;
{
	LPTR	*bcksearch(), *fwdsearch();
	LPTR	*pos;
	char	*old_ls = laststr;

	reg_ic = P(P_IC);	/* tell the regexp routines how to search */

	laststr = strsave(str);
	lastsdir = dir;

	if (old_ls != NULL)
		free(old_ls);

	if (dir == BACKWARD) {
		smsg("?%s", laststr);
		pos = bcksearch(mapstring(laststr));
	} else {
		smsg("/%s", laststr);
		pos = fwdsearch(mapstring(laststr));
	}

	/*
	 * This is kind of a kludge, but its needed to make
	 * 'beginning of word' searches land on the right place.
	 */
	if (pos != NULL && begword) {
		if (pos->index != 0 || !BEGCHAR(pos->linep->s[0]))
			pos->index += 1;
	}
	return pos;
}

bool_t
dosearch(dir,str)
int	dir;
char	*str;
{
	LPTR	*p;

	if (str == NULL)
		str = laststr;

	got_int = FALSE;

	if ((p = ssearch(dir,str)) == NULL) {
		if (got_int)
			msg("Interrupt");
		else
			msg("Pattern not found");

		got_int = FALSE;
		return FALSE;
	} else {
		LPTR savep;

		cursupdate();
		/*
		 * if we're backing up, we make sure the line we're on
		 * is on the screen.
		 */
		setpcmark();
		*Curschar = savep = *p;
		set_want_col = TRUE;
		cursupdate();

		return TRUE;
	}
}

#define	OTHERDIR(x)	(((x) == FORWARD) ? BACKWARD : FORWARD)

bool_t
repsearch(flag)
int	flag;
{
	int	dir = lastsdir;
	bool_t	found;

	if ( laststr == NULL ) {
		beep();
		return FALSE;
	}

	found = dosearch(flag ? OTHERDIR(lastsdir) : lastsdir, laststr);

	/*
	 * We have to save and restore 'lastsdir' because it gets munged
	 * by ssearch() and winds up saving the wrong direction from here
	 * if 'flag' is true.
	 */
	lastsdir = dir;

	return found;
}

/*
 * regerror - called by regexp routines when errors are detected.
 */
void
regerror(s)
char	*s;
{
	emsg(s);
}

static LPTR *
fwdsearch(str)
register char	*str;
{
	static LPTR	infile;
	register LPTR	*p;
	regexp	*prog;

	register char	*s;
	register int	i;

	if ((prog = regcomp(str)) == NULL) {
		emsg("Invalid search string");
		return NULL;
	}

	p = Curschar;
	i = Curschar->index + 1;
	do {
		s = p->linep->s + i;

		if (regexec(prog, s, i == 0)) {		/* got a match */
			infile.linep = p->linep;
			infile.index = (int) (prog->startp[0] - p->linep->s);
			free((char *)prog);
			return (&infile);
		}
		i = 0;

		if (got_int)
			goto fwdfail;

	} while ((p = nextline(p)) != NULL);

	/*
	 * If wrapscan isn't set, then don't scan from the beginning
	 * of the file. Just return failure here.
	 */
	if (!P(P_WS))
		goto fwdfail;

	/* search from the beginning of the file to Curschar */
	for (p = Filemem; p != NULL ;p = nextline(p)) {
		s = p->linep->s;

		if (regexec(prog, s, TRUE)) {		/* got a match */
			infile.linep = p->linep;
			infile.index = (int) (prog->startp[0] - s);
			free((char *)prog);
			return (&infile);
		}

		if (p->linep == Curschar->linep)
			break;

		if (got_int)
			goto fwdfail;
	}

fwdfail:
	free((char *)prog);
	return NULL;
}

static LPTR *
bcksearch(str)
char	*str;
{
	static LPTR	infile;
	register LPTR	*p = &infile;
	register char	*s;
	register int	i;
	register char	*match;
	regexp	*prog;

	/* make sure str isn't empty */
	if (str == NULL || *str == NUL)
		return NULL;

	if ((prog = regcomp(str)) == NULL) {
		emsg("Invalid search string");
		return NULL;
	}

	*p = *Curschar;
	if (dec(p) == -1) {	/* already at start of file? */
		*p = *Fileend;
		p->index = strlen(p->linep->s) - 1;
	}

	if (begword)		/* so we don't get stuck on one match */
		dec(p);

	i = p->index;

	do {
		s = p->linep->s;

		if (regexec(prog, s, TRUE)) {	/* match somewhere on line */

			/*
			 * Now, if there are multiple matches on this line,
			 * we have to get the last one. Or the last one
			 * before the cursor, if we're on that line.
			 */
			match = prog->startp[0];

			while (regexec(prog, prog->endp[0], FALSE)) {
				if ((i >= 0) && ((prog->startp[0] - s) > i))
					break;
				match = prog->startp[0];
			}

			if ((i >= 0) && ((match - s) > i)) {
				i = -1;
				continue;
			}

			infile.linep = p->linep;
			infile.index = (int) (match - s);
			free((char *)prog);
			return (&infile);
		}
		i = -1;

		if (got_int)
			goto bckfail;

	} while ((p = prevline(p)) != NULL);

	/*
	 * If wrapscan isn't set, bag the search now
	 */
	if (!P(P_WS))
		goto bckfail;

	/* search backward from the end of the file */
	p = prevline(Fileend);
	do {
		s = p->linep->s;

		if (regexec(prog, s, TRUE)) {	/* match somewhere on line */

			/*
			 * Now, if there are multiple matches on this line,
			 * we have to get the last one.
			 */
			match = prog->startp[0];

			while (regexec(prog, prog->endp[0], FALSE))
				match = prog->startp[0];

			infile.linep = p->linep;
			infile.index = (int) (match - s);
			free((char *)prog);
			return (&infile);
		}

		if (p->linep == Curschar->linep)
			break;

		if (got_int)
			goto bckfail;

	} while ((p = prevline(p)) != NULL);

bckfail:
	free((char *)prog);
	return NULL;
}

/*
 * dosub(lp, up, cmd)
 *
 * Perform a substitution from line 'lp' to line 'up' using the
 * command pointed to by 'cmd' which should be of the form:
 *
 * /pattern/substitution/g
 *
 * The trailing 'g' is optional and, if present, indicates that multiple
 * substitutions should be performed on each line, if applicable.
 * The usual escapes are supported as described in the regexp docs.
 */
void
dosub(lp, up, cmd)
LPTR	*lp, *up;
char	*cmd;
{
	LINE	*cp;
	char	*pat, *sub;
	regexp	*prog;
	int	nsubs;
	bool_t	do_all;		/* do multiple substitutions per line */

	/*
	 * If no range was given, do the current line. If only one line
	 * was given, just do that one.
	 */
	if (lp->linep == NULL)
		*up = *lp = *Curschar;
	else {
		if (up->linep == NULL)
			*up = *lp;
	}

	pat = ++cmd;		/* skip the initial '/' */

	while (*cmd) {
		if (*cmd == '\\')	/* next char is quoted */
			cmd += 2;
		else if (*cmd == '/') {	/* delimiter */
			*cmd++ = NUL;
			break;
		} else
			cmd++;		/* regular character */
	}

	if (*pat == NUL) {
		emsg("NULL pattern specified");
		return;
	}

	sub = cmd;

	do_all = FALSE;

	while (*cmd) {
		if (*cmd == '\\')	/* next char is quoted */
			cmd += 2;
		else if (*cmd == '/') {	/* delimiter */
			do_all = (cmd[1] == 'g');
			*cmd++ = NUL;
			break;
		} else
			cmd++;		/* regular character */
	}

	reg_ic = P(P_IC);	/* set "ignore case" flag appropriately */

	if ((prog = regcomp(pat)) == NULL) {
		emsg("Invalid search string");
		return;
	}

	nsubs = 0;

	for (cp = lp->linep; cp != NULL ;cp = cp->next) {
		if (regexec(prog, cp->s, TRUE)) { /* a match on this line */
			char	*ns, *sns, *p;

			/*
			 * Get some space for a temporary buffer
			 * to do the substitution into.
			 */
			sns = ns = alloc(2048);
			*sns = NUL;

			p = cp->s;

			do {
				for (ns = sns; *ns ;ns++)
					;
				/*
				 * copy up to the part that matched
				 */
				while (p < prog->startp[0])
					*ns++ = *p++;

				regsub(prog, sub, ns);

				/*
				 * continue searching after the match
				 */
				p = prog->endp[0];

			} while (regexec(prog, p, FALSE) && do_all);

			for (ns = sns; *ns ;ns++)
				;

			/*
			 * copy the rest of the line, that didn't match
			 */
			while (*p)
				*ns++ = *p++;

			*ns = NUL;

			free(cp->s);		/* free the original line */
			cp->s = strsave(sns);	/* and save the modified str */
			cp->size = strlen(cp->s) + 1;
			free(sns);		/* free the temp buffer */
			nsubs++;
			CHANGED;
		}
		if (cp == up->linep)
			break;
	}

	if (nsubs) {
		updatescreen();
		if (nsubs >= P(P_RP))
			smsg("%d substitution%c", nsubs, (nsubs>1) ? 's' : ' ');
	} else
		msg("No match");

	free((char *)prog);
}

/*
 * doglob(cmd)
 *
 * Execute a global command of the form:
 *
 * g/pattern/X
 *
 * where 'x' is a command character, currently one of the following:
 *
 * d	Delete all matching lines
 * p	Print all matching lines
 *

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -