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

📄 ex_subst.c

📁 早期freebsd实现
💻 C
📖 第 1 页 / 共 2 页
字号:
/*- * Copyright (c) 1992, 1993, 1994 *	The Regents of the University of California.  All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright *    notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright *    notice, this list of conditions and the following disclaimer in the *    documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software *    must display the following acknowledgement: *	This product includes software developed by the University of *	California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors *    may be used to endorse or promote products derived from this software *    without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */#ifndef lintstatic char sccsid[] = "@(#)ex_subst.c	8.43 (Berkeley) 4/12/94";#endif /* not lint */#include <sys/types.h>#include <sys/queue.h>#include <sys/time.h>#include <bitstring.h>#include <ctype.h>#include <errno.h>#include <limits.h>#include <signal.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <termios.h>#include <unistd.h>#include "compat.h"#include <db.h>#include <regex.h>#include "vi.h"#include "excmd.h"#define	SUB_FIRST	0x01		/* The 'r' flag isn't reasonable. */#define	SUB_MUSTSETR	0x02		/* The 'r' flag is required. */static int		checkmatchsize __P((SCR *, regex_t *));static inline int	regsub __P((SCR *,			    char *, char **, size_t *, size_t *));static int		substitute __P((SCR *, EXF *,			    EXCMDARG *, char *, regex_t *, u_int));/* * ex_substitute -- *	[line [,line]] s[ubstitute] [[/;]pat[/;]/repl[/;] [cgr] [count] [#lp]] * *	Substitute on lines matching a pattern. */intex_substitute(sp, ep, cmdp)	SCR *sp;	EXF *ep;	EXCMDARG *cmdp;{	regex_t *re, lre;	size_t blen, len;	u_int flags;	int delim, eval, reflags, replaced;	char *bp, *ptrn, *rep, *p, *t;	/*	 * Skip leading white space.	 *	 * !!!	 * Historic vi allowed any non-alphanumeric to serve as the	 * substitution command delimiter.	 *	 * !!!	 * If the arguments are empty, it's the same as &, i.e. we	 * repeat the last substitution.	 */	for (p = cmdp->argv[0]->bp,	    len = cmdp->argv[0]->len; len > 0; --len, ++p) {		if (!isblank(*p))			break;	}	if (len == 0)		return (ex_subagain(sp, ep, cmdp));	delim = *p++;	if (isalnum(delim))		return (substitute(sp, ep,		    cmdp, p, &sp->subre, SUB_MUSTSETR));	/*	 * !!!	 * The full-blown substitute command reset the remembered	 * state of the 'c' and 'g' suffices.	 */	sp->c_suffix = sp->g_suffix = 0;		/*	 * Get the pattern string, toss escaped characters.	 *	 * !!!	 * Historic vi accepted any of the following forms:	 *	 *	:s/abc/def/		change "abc" to "def"	 *	:s/abc/def		change "abc" to "def"	 *	:s/abc/			delete "abc"	 *	:s/abc			delete "abc"	 *	 * QUOTING NOTE:	 *	 * Only toss an escape character if it escapes a delimiter.	 * This means that "s/A/\\\\f" replaces "A" with "\\f".  It	 * would be nice to be more regular, i.e. for each layer of	 * escaping a single escape character is removed, but that's	 * not how the historic vi worked.	 */	for (ptrn = t = p;;) {		if (p[0] == '\0' || p[0] == delim) {			if (p[0] == delim)				++p;			/*			 * !!!			 * Nul terminate the pattern string -- it's passed			 * to regcomp which doesn't understand anything else.			 */			*t = '\0';			break;		}		if (p[0] == '\\')			if (p[1] == delim)				++p;			else if (p[1] == '\\')				*t++ = *p++;		*t++ = *p++;	}	/*	 * If the pattern string is empty, use the last RE (not just the	 * last substitution RE).	 */	if (*ptrn == NULL) {		if (!F_ISSET(sp, S_SRE_SET)) {			msgq(sp, M_ERR, "No previous regular expression.");			return (1);		}		re = &sp->sre;		flags = 0;	} else {		/* Set RE flags. */		reflags = 0;		if (O_ISSET(sp, O_EXTENDED))			reflags |= REG_EXTENDED;		if (O_ISSET(sp, O_IGNORECASE))			reflags |= REG_ICASE;		/* Convert vi-style RE's to POSIX 1003.2 RE's. */		if (re_conv(sp, &ptrn, &replaced))			return (1);		/* Compile the RE. */		eval = regcomp(&lre, (char *)ptrn, reflags);		/* Free up any allocated memory. */		if (replaced)			FREE_SPACE(sp, ptrn, 0);		if (eval) {			re_error(sp, eval, &lre);			return (1);		}		/*		 * Set saved RE.		 *		 * !!!		 * Historic practice is that substitutes set the search		 * direction as well as both substitute and search RE's.		 */		sp->searchdir = FORWARD;		sp->sre = lre;		F_SET(sp, S_SRE_SET);		sp->subre = lre;		F_SET(sp, S_SUBRE_SET);		re = &lre;		flags = SUB_FIRST;	}	/*	 * Get the replacement string.	 *	 * The special character & (\& if O_MAGIC not set) matches the	 * entire RE.  No handling of & is required here, it's done by	 * regsub().	 *	 * The special character ~ (\~ if O_MAGIC not set) inserts the	 * previous replacement string into this replacement string.	 * Count ~'s to figure out how much space we need.  We could	 * special case nonexistent last patterns or whether or not	 * O_MAGIC is set, but it's probably not worth the effort.	 *	 * QUOTING NOTE:	 *	 * Only toss an escape character if it escapes a delimiter or	 * if O_MAGIC is set and it escapes a tilde.	 *	 * !!!	 * If the entire replacement pattern is "%", then use the last	 * replacement pattern.  This semantic was added to vi in System	 * V and then percolated elsewhere, presumably around the time	 * that it was added to their version of ed(1).	 */	if (p[0] == '\0' || p[0] == delim) {		if (p[0] == delim) {			if (p[1] != '\0')				return (1);			p[0] = '\0';		}		if (sp->repl != NULL)			FREE(sp->repl, sp->repl_len);		sp->repl = NULL;		sp->repl_len = 0;	} else if (p[0] == '%' && (p[1] == '\0' || p[1] == delim)) {		if (p[1] == delim && p[2] != '\0')			return (1);		p[0] = '\0';	} else {		for (rep = p, len = 0;		    p[0] != '\0' && p[0] != delim; ++p, ++len)			if (p[0] == '~')				len += sp->repl_len;		GET_SPACE_RET(sp, bp, blen, len);		for (t = bp, len = 0, p = rep;;) {			if (p[0] == '\0' || p[0] == delim) {				if (p[0] == delim)					++p;				break;			}			if (p[0] == '\\') {				if (p[1] == delim)					++p;				else if (p[1] == '\\') {					*t++ = *p++;					++len;				} else if (p[1] == '~') {					++p;					if (!O_ISSET(sp, O_MAGIC))						goto tilde;				}			} else if (p[0] == '~' && O_ISSET(sp, O_MAGIC)) {tilde:				++p;				memmove(t, sp->repl, sp->repl_len);				t += sp->repl_len;				len += sp->repl_len;				continue;			}			*t++ = *p++;			++len;		}		if (sp->repl != NULL)			FREE(sp->repl, sp->repl_len);		if ((sp->repl = malloc(len)) == NULL) {			msgq(sp, M_SYSERR, NULL);			FREE_SPACE(sp, bp, blen);			return (1);		}		memmove(sp->repl, bp, len);		sp->repl_len = len;		FREE_SPACE(sp, bp, blen);	}	if (checkmatchsize(sp, &sp->subre))		return (1);	return (substitute(sp, ep, cmdp, p, re, flags));}/* * ex_subagain -- *	[line [,line]] & [cgr] [count] [#lp]] * *	Substitute using the last substitute RE and replacement pattern. */intex_subagain(sp, ep, cmdp)	SCR *sp;	EXF *ep;	EXCMDARG *cmdp;{	if (!F_ISSET(sp, S_SUBRE_SET)) {		msgq(sp, M_ERR, "No previous regular expression.");		return (1);	}	return (substitute(sp, ep, cmdp, cmdp->argv[0]->bp, &sp->subre, 0));}/* * ex_subtilde -- *	[line [,line]] ~ [cgr] [count] [#lp]] * *	Substitute using the last RE and last substitute replacement pattern. */intex_subtilde(sp, ep, cmdp)	SCR *sp;	EXF *ep;	EXCMDARG *cmdp;{	if (!F_ISSET(sp, S_SRE_SET)) {		msgq(sp, M_ERR, "No previous regular expression.");		return (1);	}	return (substitute(sp, ep, cmdp, cmdp->argv[0]->bp, &sp->sre, 0));}/* * The nasty part of the substitution is what happens when the replacement * string contains newlines.  It's a bit tricky -- consider the information * that has to be retained for "s/f\(o\)o/^M\1^M\1/".  The solution here is * to build a set of newline offsets which we use to break the line up later, * when the replacement is done.  Don't change it unless you're pretty damned * confident. */#define	NEEDNEWLINE(sp) {						\	if (sp->newl_len == sp->newl_cnt) {				\		sp->newl_len += 25;					\		REALLOC(sp, sp->newl, size_t *,				\		    sp->newl_len * sizeof(size_t));			\		if (sp->newl == NULL) {					\			sp->newl_len = 0;				\			return (1);					\		}							\	}								\}#define	BUILD(sp, l, len) {						\	if (lbclen + (len) > lblen) {					\		lblen += MAX(lbclen + (len), 256);			\		REALLOC(sp, lb, char *, lblen);				\		if (lb == NULL) {					\			lbclen = 0;					\			return (1);					\		}							\	}								\	memmove(lb + lbclen, l, len);					\	lbclen += len;							\}#define	NEEDSP(sp, len, pnt) {						\	if (lbclen + (len) > lblen) {					\		lblen += MAX(lbclen + (len), 256);			\		REALLOC(sp, lb, char *, lblen);				\		if (lb == NULL) {					\			lbclen = 0;					\			return (1);					\		}							\		pnt = lb + lbclen;					\	}								\}/* * substitute -- *	Do the substitution.  This stuff is *really* tricky.  There are *	lots of special cases, and general nastiness.  Don't mess with it * 	unless you're pretty confident. */static intsubstitute(sp, ep, cmdp, s, re, flags)	SCR *sp;	EXF *ep;	EXCMDARG *cmdp;	char *s;	regex_t *re;	u_int flags;{	MARK from, to;	recno_t elno, lno;	size_t blen, cnt, last, lbclen, lblen, len, llen, offset, saved_offset;	int cflag, lflag, nflag, pflag, rflag;	int didsub, do_eol_match, eflags, empty_ok, eval;	int linechanged, matched, quit, rval, teardown;	char *bp, *lb;	/*	 * !!!	 * Historically, the 'g' and 'c' suffices were always toggled as flags,	 * so ":s/A/B/" was the same as ":s/A/B/ccgg".  If O_EDCOMPATIBLE was	 * not set, they were initialized to 0 for all substitute commands.  If	 * O_EDCOMPATIBLE was set, they were initialized to 0 only if the user	 * specified substitute/replacement patterns (see ex_substitute()).	 */	if (!O_ISSET(sp, O_EDCOMPATIBLE))		sp->c_suffix = sp->g_suffix = 0;	/*	 * Historic vi permitted the '#', 'l' and 'p' options in vi mode, but	 * it only displayed the last change.  I'd disallow them, but they are	 * useful in combination with the [v]global commands.  In the current	 * model the problem is combining them with the 'c' flag -- the screen	 * would have to flip back and forth between the confirm screen and the	 * ex print screen, which would be pretty awful.  We do display all	 * changes, though, for what that's worth.	 *	 * !!!	 * Historic vi was fairly strict about the order of "options", the	 * count, and "flags".  I'm somewhat fuzzy on the difference between	 * options and flags, anyway, so this is a simpler approach, and we	 * just take it them in whatever order the user gives them.  (The ex	 * usage statement doesn't reflect this.)	 */	cflag = lflag = nflag = pflag = rflag = 0;	for (lno = OOBLNO; *s != '\0'; ++s)		switch (*s) {		case ' ':		case '\t':			break;		case '0': case '1': case '2': case '3': case '4':		case '5': case '6': case '7': case '8': case '9':			if (lno != OOBLNO)				goto usage;			errno = 0;			lno = strtoul(s, &s, 10);			if (*s == '\0')		/* Loop increment correction. */				--s;			if (errno == ERANGE) {				if (lno == LONG_MAX)					msgq(sp, M_ERR, "Count overflow.");				else if (lno == LONG_MIN)					msgq(sp, M_ERR, "Count underflow.");				else					msgq(sp, M_SYSERR, NULL);				return (1);			}			/*			 * In historic vi, the count was inclusive from the			 * second address.			 */			cmdp->addr1.lno = cmdp->addr2.lno;			cmdp->addr2.lno += lno - 1;			break;		case '#':			nflag = 1;			break;		case 'c':			sp->c_suffix = !sp->c_suffix;			break;		case 'g':			sp->g_suffix = !sp->g_suffix;			break;		case 'l':			lflag = 1;			break;		case 'p':			pflag = 1;			break;		case 'r':			if (LF_ISSET(SUB_FIRST)) {				msgq(sp, M_ERR,		    "Regular expression specified; r flag meaningless.");				return (1);			}			if (!F_ISSET(sp, S_SRE_SET)) {				msgq(sp, M_ERR,				    "No previous regular expression.");				return (1);			}			rflag = 1;			re = &sp->sre;			break;		default:			goto usage;		}	if (*s != '\0' || !rflag && LF_ISSET(SUB_MUSTSETR)) {usage:		msgq(sp, M_ERR, "Usage: %s", cmdp->cmd->usage);		return (1);	}	if (IN_VI_MODE(sp) && sp->c_suffix && (lflag || nflag || pflag)) {		msgq(sp, M_ERR,	"The #, l and p flags may not be combined with the c flag in vi mode.");		return (1);	}	/* Set up interrupts. */	teardown = !intr_init(sp);	/*	 * bp:		if interactive, line cache	 * blen:	if interactive, line cache length	 * lb:		build buffer pointer.	 * lbclen:	current length of built buffer.	 * lblen;	length of build buffer.

⌨️ 快捷键说明

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