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

📄 ex_subst.c

📁 早期freebsd实现
💻 C
📖 第 1 页 / 共 2 页
字号:
	 */	bp = lb = NULL;	blen = lbclen = lblen = 0;	/* For each line... */	for (matched = quit = 0, lno = cmdp->addr1.lno,	    elno = cmdp->addr2.lno; !quit && lno <= elno; ++lno) {		/* Someone's unhappy, time to stop. */		if (F_ISSET(sp, S_INTERRUPTED)) {			if (!F_ISSET(sp, S_GLOBAL))				msgq(sp, M_INFO, "Interrupted.");			break;		}		/* Get the line. */		if ((s = file_gline(sp, ep, lno, &llen)) == NULL) {			GETLINE_ERR(sp, lno);			return (1);		}		/*		 * Make a local copy if doing confirmation -- when calling		 * the confirm routine we're likely to lose the cached copy.		 */		if (sp->c_suffix) {			if (bp == NULL) {				GET_SPACE_RET(sp, bp, blen, llen);			} else				ADD_SPACE_RET(sp, bp, blen, llen);			memmove(bp, s, llen);			s = bp;		}		/* Start searching from the beginning. */		offset = 0;		len = llen;		/* Reset the build buffer offset. */		lbclen = 0;		/* Reset empty match flag. */		empty_ok = 1;		/*		 * We don't want to have to do a setline if the line didn't		 * change -- keep track of whether or not this line changed.		 * If doing confirmations, don't want to keep setting the		 * line if change is refused -- keep track of substitutions.		 */		didsub = linechanged = 0;		/* New line, do an EOL match. */		do_eol_match = 1;		/* It's not nul terminated, but we pretend it is. */		eflags = REG_STARTEND;		/*		 * The search area is from s + offset to the EOL.		 *		 * Generally, sp->match[0].rm_so is the offset of the start		 * of the match from the start of the search, and offset is		 * the offset of the start of the last search.		 */nextmatch:	sp->match[0].rm_so = 0;		sp->match[0].rm_eo = len;		/* Get the next match. */		eval = regexec(re,		    (char *)s + offset, re->re_nsub + 1, sp->match, eflags);		/*		 * There wasn't a match or if there was an error, deal with		 * it.  If there was a previous match in this line, resolve		 * the changes into the database.  Otherwise, just move on.		 */		if (eval == REG_NOMATCH)			goto endmatch;		if (eval != 0) {			re_error(sp, eval, re);			goto ret1;		}		matched = 1;		/* Only the first search can match an anchored expression. */		eflags |= REG_NOTBOL;		/*		 * !!!		 * It's possible to match 0-length strings -- for example, the		 * command s;a*;X;, when matched against the string "aabb" will		 * result in "XbXbX", i.e. the matches are "aa", the space		 * between the b's and the space between the b's and the end of		 * the string.  There is a similar space between the beginning		 * of the string and the a's.  The rule that we use (because vi		 * historically used it) is that any 0-length match, occurring		 * immediately after a match, is ignored.  Otherwise, the above		 * example would have resulted in "XXbXbX".  Another example is		 * incorrectly using " *" to replace groups of spaces with one		 * space.		 *		 * The way we do this is that if we just had a successful match,		 * the starting offset does not skip characters, and the match		 * is empty, ignore the match and move forward.  If there's no		 * more characters in the string, we were attempting to match		 * after the last character, so quit.		 */		if (!empty_ok &&		    sp->match[0].rm_so == 0 && sp->match[0].rm_eo == 0) {			empty_ok = 1;			if (len == 0)				goto endmatch;			BUILD(sp, s + offset, 1)			++offset;			--len;			goto nextmatch;		}		/* Confirm change. */		if (sp->c_suffix) {			/*			 * Set the cursor position for confirmation.  Note,			 * if we matched on a '$', the cursor may be past			 * the end of line.			 *			 * XXX			 * We may want to "fix" this in the confirm routine,			 * if the confirm routine should be able to display			 * a cursor past EOL.			 */			from.lno = to.lno = lno;			from.cno = sp->match[0].rm_so + offset;			to.cno = sp->match[0].rm_eo;			if (llen == 0)				from.cno = to.cno = 0;			else {				if (to.cno >= llen)					to.cno = llen - 1;				if (from.cno >= llen)					from.cno = llen - 1;			}			switch (sp->s_confirm(sp, ep, &from, &to)) {			case CONF_YES:				break;			case CONF_NO:				didsub = 0;				BUILD(sp, s +offset, sp->match[0].rm_eo);				goto skip;			case CONF_QUIT:				/* Set the quit flag. */				quit = 1;				/* If interruptible, pass the info back. */				if (F_ISSET(sp, S_INTERRUPTIBLE))					F_SET(sp, S_INTERRUPTED);				/*				 * If any changes, resolve them, otherwise				 * return to the main loop.				 */				goto endmatch;			}		}		/* Copy the bytes before the match into the build buffer. */		BUILD(sp, s + offset, sp->match[0].rm_so);		/* Substitute the matching bytes. */		didsub = 1;		if (regsub(sp, s + offset, &lb, &lbclen, &lblen))			goto ret1;		/* Set the change flag so we know this line was modified. */		linechanged = 1;		/* Move past the matched bytes. */skip:		offset += sp->match[0].rm_eo;		len -= sp->match[0].rm_eo;		/* A match cannot be followed by an empty pattern. */		empty_ok = 0;		/*		 * If doing a global change with confirmation, we have to		 * update the screen.  The basic idea is to store the line		 * so the screen update routines can find it, and restart.		 */		if (didsub && sp->c_suffix && sp->g_suffix) {			/*			 * The new search offset will be the end of the			 * modified line.			 */			saved_offset = lbclen;			/* Copy the rest of the line. */			if (len)				BUILD(sp, s + offset, len)			/* Set the new offset. */			offset = saved_offset;			/* Store inserted lines, adjusting the build buffer. */			last = 0;			if (sp->newl_cnt) {				for (cnt = 0;				    cnt < sp->newl_cnt; ++cnt, ++lno, ++elno) {					if (file_iline(sp, ep, lno,					    lb + last, sp->newl[cnt] - last))						goto ret1;					last = sp->newl[cnt] + 1;					++sp->rptlines[L_ADDED];				}				lbclen -= last;				offset -= last;				sp->newl_cnt = 0;			}			/* Store and retrieve the line. */			if (file_sline(sp, ep, lno, lb + last, lbclen))				goto ret1;			if ((s = file_gline(sp, ep, lno, &llen)) == NULL) {				GETLINE_ERR(sp, lno);				goto ret1;			}			ADD_SPACE_RET(sp, bp, blen, llen)			memmove(bp, s, llen);			s = bp;			len = llen - offset;			/* Restart the build. */			lbclen = 0;			BUILD(sp, s, offset);			/*			 * If we haven't already done the after-the-string			 * match, do one.  Set REG_NOTEOL so the '$' pattern			 * only matches once.			 */			if (!do_eol_match)				goto endmatch;			if (offset == len) {				do_eol_match = 0;				eflags |= REG_NOTEOL;			}			goto nextmatch;		}		/*		 * If it's a global:		 *		 * If at the end of the string, do a test for the after		 * the string match.  Set REG_NOTEOL so the '$' pattern		 * only matches once.		 */		if (sp->g_suffix && do_eol_match) {			if (len == 0) {				do_eol_match = 0;				eflags |= REG_NOTEOL;			}			goto nextmatch;		}endmatch:	if (!linechanged)			continue;		/* Copy any remaining bytes into the build buffer. */		if (len)			BUILD(sp, s + offset, len)		/* Store inserted lines, adjusting the build buffer. */		last = 0;		if (sp->newl_cnt) {			for (cnt = 0;			    cnt < sp->newl_cnt; ++cnt, ++lno, ++elno) {				if (file_iline(sp, ep,				    lno, lb + last, sp->newl[cnt] - last))					goto ret1;				last = sp->newl[cnt] + 1;				++sp->rptlines[L_ADDED];			}			lbclen -= last;			sp->newl_cnt = 0;		}		/* Store the changed line. */		if (file_sline(sp, ep, lno, lb + last, lbclen))			goto ret1;		/* Update changed line counter. */		++sp->rptlines[L_CHANGED];		/*		 * !!!		 * Display as necessary.  Historic practice is to only		 * display the last line of a line split into multiple		 * lines.		 */		if (lflag || nflag || pflag) {			from.lno = to.lno = lno;			from.cno = to.cno = 0;			if (lflag)				ex_print(sp, ep, &from, &to, E_F_LIST);			if (nflag)				ex_print(sp, ep, &from, &to, E_F_HASH);			if (pflag)				ex_print(sp, ep, &from, &to, E_F_PRINT);		}		if (!sp->c_suffix)			sp->lno = lno;		/*		 * !!!		 * Move the cursor to the last line changed.		 */		if (!sp->c_suffix)			sp->lno = lno;	}	/*	 * !!!	 * Move the cursor to the first non-blank of the last line	 * change.	 */	if (!sp->c_suffix) {		sp->cno = 0;		(void)nonblank(sp, ep, sp->lno, &sp->cno);	}	/*	 * If not in a global command, and nothing matched, say so.	 * Else, if none of the lines displayed, put something up.	 */	if (!matched) {		if (!F_ISSET(sp, S_GLOBAL))			msgq(sp, M_INFO, "No match found.");	} else if (!lflag && !nflag && !pflag)		F_SET(EXP(sp), EX_AUTOPRINT);	rval = 0;	if (0) {ret1:		rval = 1;	}	if (teardown)		intr_end(sp);	if (bp != NULL)		FREE_SPACE(sp, bp, blen);	return (rval);}/* * regsub -- * 	Do the substitution for a regular expression. */static inline intregsub(sp, ip, lbp, lbclenp, lblenp)	SCR *sp;	char *ip;			/* Input line. */	char **lbp;	size_t *lbclenp, *lblenp;{	enum { C_NOTSET, C_LOWER, C_ONELOWER, C_ONEUPPER, C_UPPER } conv;	size_t lbclen, lblen;		/* Local copies. */	size_t mlen;			/* Match length. */	size_t rpl;			/* Remaining replacement length. */	char *rp;			/* Replacement pointer. */	int ch;	int no;				/* Match replacement offset. */	char *p, *t;			/* Buffer pointers. */	char *lb;			/* Local copies. */	lb = *lbp;			/* Get local copies. */	lbclen = *lbclenp;	lblen = *lblenp;	/*	 * QUOTING NOTE:	 *	 * There are some special sequences that vi provides in the	 * replacement patterns.	 *	 & string the RE matched (\& if nomagic set)	 *	\# n-th regular subexpression	 *	\E end \U, \L conversion	 *	\e end \U, \L conversion	 *	\l convert the next character to lower-case	 *	\L convert to lower-case, until \E, \e, or end of replacement	 *	\u convert the next character to upper-case	 *	\U convert to upper-case, until \E, \e, or end of replacement	 *	 * Otherwise, since this is the lowest level of replacement, discard	 * all escape characters.  This (hopefully) follows historic practice.	 */#define	ADDCH(ch) {							\	CHAR_T __ch = (ch);						\	u_int __value = KEY_VAL(sp, __ch);				\	if (__value == K_CR || __value == K_NL) {			\		NEEDNEWLINE(sp);					\		sp->newl[sp->newl_cnt++] = lbclen;			\	} else if (conv != C_NOTSET) {					\		switch (conv) {						\		case C_ONELOWER:					\			conv = C_NOTSET;				\			/* FALLTHROUGH */				\		case C_LOWER:						\			if (isupper(__ch))				\				__ch = tolower(__ch);			\			break;						\		case C_ONEUPPER:					\			conv = C_NOTSET;				\			/* FALLTHROUGH */				\		case C_UPPER:						\			if (islower(__ch))				\				__ch = toupper(__ch);			\			break;						\		default:						\			abort();					\		}							\	}								\	NEEDSP(sp, 1, p);						\	*p++ = __ch;							\	++lbclen;							\}	conv = C_NOTSET;	for (rp = sp->repl, rpl = sp->repl_len, p = lb + lbclen; rpl--;) {		switch (ch = *rp++) {		case '&':			if (O_ISSET(sp, O_MAGIC)) {				no = 0;				goto subzero;			}			break;		case '\\':			if (rpl == 0)				break;			--rpl;			switch (ch = *rp) {			case '&':				if (!O_ISSET(sp, O_MAGIC)) {					++rp;					no = 0;					goto subzero;				}				break;			case '0': case '1': case '2': case '3': case '4':			case '5': case '6': case '7': case '8': case '9':				no = *rp++ - '0';subzero:			if (sp->match[no].rm_so == -1 ||			    	    sp->match[no].rm_eo == -1)					continue;				mlen =				    sp->match[no].rm_eo - sp->match[no].rm_so;				for (t = ip + sp->match[no].rm_so; mlen--; ++t)					ADDCH(*t);				continue;			case 'e':			case 'E':				++rp;				conv = C_NOTSET;				continue;			case 'l':				++rp;				conv = C_ONELOWER;				continue;			case 'L':				++rp;				conv = C_LOWER;				continue;			case 'u':				++rp;				conv = C_ONEUPPER;				continue;			case 'U':				++rp;				conv = C_UPPER;				continue;			default:				++rp;				break;			}		}		ADDCH(ch);	}	*lbp = lb;			/* Update caller's information. */	*lbclenp = lbclen;	*lblenp = lblen;	return (0);}static intcheckmatchsize(sp, re)	SCR *sp;	regex_t *re;{	/* Build nsub array as necessary. */	if (sp->matchsize < re->re_nsub + 1) {		sp->matchsize = re->re_nsub + 1;		REALLOC(sp, sp->match,		    regmatch_t *, sp->matchsize * sizeof(regmatch_t));		if (sp->match == NULL) {			sp->matchsize = 0;			return (1);		}	}	return (0);}

⌨️ 快捷键说明

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