xref: /minix/external/bsd/nvi/dist/ex/ex_subst.c (revision 0a6a1f1d)
1*0a6a1f1dSLionel Sambuc /*	$NetBSD: ex_subst.c,v 1.4 2014/01/26 21:43:45 christos Exp $ */
284d9c625SLionel Sambuc /*-
384d9c625SLionel Sambuc  * Copyright (c) 1992, 1993, 1994
484d9c625SLionel Sambuc  *	The Regents of the University of California.  All rights reserved.
584d9c625SLionel Sambuc  * Copyright (c) 1992, 1993, 1994, 1995, 1996
684d9c625SLionel Sambuc  *	Keith Bostic.  All rights reserved.
784d9c625SLionel Sambuc  *
884d9c625SLionel Sambuc  * See the LICENSE file for redistribution information.
984d9c625SLionel Sambuc  */
1084d9c625SLionel Sambuc 
1184d9c625SLionel Sambuc #include "config.h"
1284d9c625SLionel Sambuc 
13*0a6a1f1dSLionel Sambuc #include <sys/cdefs.h>
14*0a6a1f1dSLionel Sambuc #if 0
1584d9c625SLionel Sambuc #ifndef lint
1684d9c625SLionel Sambuc static const char sccsid[] = "Id: ex_subst.c,v 10.50 2002/02/09 21:18:23 skimo Exp  (Berkeley) Date: 2002/02/09 21:18:23 ";
1784d9c625SLionel Sambuc #endif /* not lint */
18*0a6a1f1dSLionel Sambuc #else
19*0a6a1f1dSLionel Sambuc __RCSID("$NetBSD: ex_subst.c,v 1.4 2014/01/26 21:43:45 christos Exp $");
20*0a6a1f1dSLionel Sambuc #endif
2184d9c625SLionel Sambuc 
2284d9c625SLionel Sambuc #include <sys/types.h>
2384d9c625SLionel Sambuc #include <sys/queue.h>
2484d9c625SLionel Sambuc #include <sys/time.h>
2584d9c625SLionel Sambuc 
2684d9c625SLionel Sambuc #include <bitstring.h>
2784d9c625SLionel Sambuc #include <ctype.h>
2884d9c625SLionel Sambuc #include <errno.h>
2984d9c625SLionel Sambuc #include <limits.h>
3084d9c625SLionel Sambuc #include <stdio.h>
3184d9c625SLionel Sambuc #include <stdlib.h>
3284d9c625SLionel Sambuc #include <string.h>
3384d9c625SLionel Sambuc #include <unistd.h>
3484d9c625SLionel Sambuc 
3584d9c625SLionel Sambuc #include "../common/common.h"
3684d9c625SLionel Sambuc #include "../vi/vi.h"
3784d9c625SLionel Sambuc 
3884d9c625SLionel Sambuc #define	SUB_FIRST	0x01		/* The 'r' flag isn't reasonable. */
3984d9c625SLionel Sambuc #define	SUB_MUSTSETR	0x02		/* The 'r' flag is required. */
4084d9c625SLionel Sambuc 
4184d9c625SLionel Sambuc static int re_conv __P((SCR *, CHAR_T **, size_t *, int *));
4284d9c625SLionel Sambuc static int re_cscope_conv __P((SCR *, CHAR_T **, size_t *, int *));
4384d9c625SLionel Sambuc static int re_sub __P((SCR *,
4484d9c625SLionel Sambuc 		CHAR_T *, CHAR_T **, size_t *, size_t *, regmatch_t [10]));
4584d9c625SLionel Sambuc static int re_tag_conv __P((SCR *, CHAR_T **, size_t *, int *));
4684d9c625SLionel Sambuc static int s __P((SCR *, EXCMD *, CHAR_T *, regex_t *, u_int));
4784d9c625SLionel Sambuc 
4884d9c625SLionel Sambuc /*
4984d9c625SLionel Sambuc  * ex_s --
5084d9c625SLionel Sambuc  *	[line [,line]] s[ubstitute] [[/;]pat[/;]/repl[/;] [cgr] [count] [#lp]]
5184d9c625SLionel Sambuc  *
5284d9c625SLionel Sambuc  *	Substitute on lines matching a pattern.
5384d9c625SLionel Sambuc  *
5484d9c625SLionel Sambuc  * PUBLIC: int ex_s __P((SCR *, EXCMD *));
5584d9c625SLionel Sambuc  */
5684d9c625SLionel Sambuc int
ex_s(SCR * sp,EXCMD * cmdp)5784d9c625SLionel Sambuc ex_s(SCR *sp, EXCMD *cmdp)
5884d9c625SLionel Sambuc {
5984d9c625SLionel Sambuc 	regex_t *re;
6084d9c625SLionel Sambuc 	size_t blen, len;
6184d9c625SLionel Sambuc 	u_int flags;
6284d9c625SLionel Sambuc 	ARG_CHAR_T delim;
6384d9c625SLionel Sambuc 	CHAR_T *bp, *p, *ptrn, *rep, *t;
6484d9c625SLionel Sambuc 
6584d9c625SLionel Sambuc 	/*
6684d9c625SLionel Sambuc 	 * Skip leading white space.
6784d9c625SLionel Sambuc 	 *
6884d9c625SLionel Sambuc 	 * !!!
6984d9c625SLionel Sambuc 	 * Historic vi allowed any non-alphanumeric to serve as the
7084d9c625SLionel Sambuc 	 * substitution command delimiter.
7184d9c625SLionel Sambuc 	 *
7284d9c625SLionel Sambuc 	 * !!!
7384d9c625SLionel Sambuc 	 * If the arguments are empty, it's the same as &, i.e. we
7484d9c625SLionel Sambuc 	 * repeat the last substitution.
7584d9c625SLionel Sambuc 	 */
7684d9c625SLionel Sambuc 	if (cmdp->argc == 0)
7784d9c625SLionel Sambuc 		goto subagain;
7884d9c625SLionel Sambuc 	for (p = cmdp->argv[0]->bp,
7984d9c625SLionel Sambuc 	    len = cmdp->argv[0]->len; len > 0; --len, ++p) {
8084d9c625SLionel Sambuc 		if (!ISBLANK((UCHAR_T)*p))
8184d9c625SLionel Sambuc 			break;
8284d9c625SLionel Sambuc 	}
8384d9c625SLionel Sambuc 	if (len == 0)
8484d9c625SLionel Sambuc subagain:	return (ex_subagain(sp, cmdp));
8584d9c625SLionel Sambuc 
8684d9c625SLionel Sambuc 	delim = (UCHAR_T)*p++;
8784d9c625SLionel Sambuc 	if (ISALNUM(delim) || delim == '\\')
8884d9c625SLionel Sambuc 		return (s(sp, cmdp, p, &sp->subre_c, SUB_MUSTSETR));
8984d9c625SLionel Sambuc 
9084d9c625SLionel Sambuc 	/*
9184d9c625SLionel Sambuc 	 * !!!
9284d9c625SLionel Sambuc 	 * The full-blown substitute command reset the remembered
9384d9c625SLionel Sambuc 	 * state of the 'c' and 'g' suffices.
9484d9c625SLionel Sambuc 	 */
9584d9c625SLionel Sambuc 	sp->c_suffix = sp->g_suffix = 0;
9684d9c625SLionel Sambuc 
9784d9c625SLionel Sambuc 	/*
9884d9c625SLionel Sambuc 	 * Get the pattern string, toss escaping characters.
9984d9c625SLionel Sambuc 	 *
10084d9c625SLionel Sambuc 	 * !!!
10184d9c625SLionel Sambuc 	 * Historic vi accepted any of the following forms:
10284d9c625SLionel Sambuc 	 *
10384d9c625SLionel Sambuc 	 *	:s/abc/def/		change "abc" to "def"
10484d9c625SLionel Sambuc 	 *	:s/abc/def		change "abc" to "def"
10584d9c625SLionel Sambuc 	 *	:s/abc/			delete "abc"
10684d9c625SLionel Sambuc 	 *	:s/abc			delete "abc"
10784d9c625SLionel Sambuc 	 *
10884d9c625SLionel Sambuc 	 * QUOTING NOTE:
10984d9c625SLionel Sambuc 	 *
11084d9c625SLionel Sambuc 	 * Only toss an escaping character if it escapes a delimiter.
11184d9c625SLionel Sambuc 	 * This means that "s/A/\\\\f" replaces "A" with "\\f".  It
11284d9c625SLionel Sambuc 	 * would be nice to be more regular, i.e. for each layer of
11384d9c625SLionel Sambuc 	 * escaping a single escaping character is removed, but that's
11484d9c625SLionel Sambuc 	 * not how the historic vi worked.
11584d9c625SLionel Sambuc 	 */
11684d9c625SLionel Sambuc 	for (ptrn = t = p;;) {
11784d9c625SLionel Sambuc 		if (p[0] == '\0' || p[0] == delim) {
11884d9c625SLionel Sambuc 			if (p[0] == delim)
11984d9c625SLionel Sambuc 				++p;
12084d9c625SLionel Sambuc 			/*
12184d9c625SLionel Sambuc 			 * !!!
12284d9c625SLionel Sambuc 			 * Nul terminate the pattern string -- it's passed
12384d9c625SLionel Sambuc 			 * to regcomp which doesn't understand anything else.
12484d9c625SLionel Sambuc 			 */
12584d9c625SLionel Sambuc 			*t = '\0';
12684d9c625SLionel Sambuc 			break;
12784d9c625SLionel Sambuc 		}
12884d9c625SLionel Sambuc 		if (p[0] == '\\') {
12984d9c625SLionel Sambuc 			if (p[1] == delim)
13084d9c625SLionel Sambuc 				++p;
13184d9c625SLionel Sambuc 			else if (p[1] == '\\')
13284d9c625SLionel Sambuc 				*t++ = *p++;
13384d9c625SLionel Sambuc 		}
13484d9c625SLionel Sambuc 		*t++ = *p++;
13584d9c625SLionel Sambuc 	}
13684d9c625SLionel Sambuc 
13784d9c625SLionel Sambuc 	/*
13884d9c625SLionel Sambuc 	 * If the pattern string is empty, use the last RE (not just the
13984d9c625SLionel Sambuc 	 * last substitution RE).
14084d9c625SLionel Sambuc 	 */
14184d9c625SLionel Sambuc 	if (*ptrn == '\0') {
14284d9c625SLionel Sambuc 		if (sp->re == NULL) {
14384d9c625SLionel Sambuc 			ex_emsg(sp, NULL, EXM_NOPREVRE);
14484d9c625SLionel Sambuc 			return (1);
14584d9c625SLionel Sambuc 		}
14684d9c625SLionel Sambuc 
14784d9c625SLionel Sambuc 		/* Re-compile the RE if necessary. */
14884d9c625SLionel Sambuc 		if (!F_ISSET(sp, SC_RE_SEARCH) &&
14984d9c625SLionel Sambuc 		    re_compile(sp, sp->re, sp->re_len,
15084d9c625SLionel Sambuc 		    NULL, NULL, &sp->re_c, SEARCH_CSEARCH | SEARCH_MSG))
15184d9c625SLionel Sambuc 			return (1);
15284d9c625SLionel Sambuc 		flags = 0;
15384d9c625SLionel Sambuc 	} else {
15484d9c625SLionel Sambuc 		/*
15584d9c625SLionel Sambuc 		 * !!!
15684d9c625SLionel Sambuc 		 * Compile the RE.  Historic practice is that substitutes set
15784d9c625SLionel Sambuc 		 * the search direction as well as both substitute and search
15884d9c625SLionel Sambuc 		 * RE's.  We compile the RE twice, as we don't want to bother
15984d9c625SLionel Sambuc 		 * ref counting the pattern string and (opaque) structure.
16084d9c625SLionel Sambuc 		 */
16184d9c625SLionel Sambuc 		if (re_compile(sp, ptrn, t - ptrn, &sp->re,
16284d9c625SLionel Sambuc 		    &sp->re_len, &sp->re_c, SEARCH_CSEARCH | SEARCH_MSG))
16384d9c625SLionel Sambuc 			return (1);
16484d9c625SLionel Sambuc 		if (re_compile(sp, ptrn, t - ptrn, &sp->subre,
16584d9c625SLionel Sambuc 		    &sp->subre_len, &sp->subre_c, SEARCH_CSUBST | SEARCH_MSG))
16684d9c625SLionel Sambuc 			return (1);
16784d9c625SLionel Sambuc 
16884d9c625SLionel Sambuc 		flags = SUB_FIRST;
16984d9c625SLionel Sambuc 		sp->searchdir = FORWARD;
17084d9c625SLionel Sambuc 	}
17184d9c625SLionel Sambuc 	re = &sp->re_c;
17284d9c625SLionel Sambuc 
17384d9c625SLionel Sambuc 	/*
17484d9c625SLionel Sambuc 	 * Get the replacement string.
17584d9c625SLionel Sambuc 	 *
17684d9c625SLionel Sambuc 	 * The special character & (\& if O_MAGIC not set) matches the
17784d9c625SLionel Sambuc 	 * entire RE.  No handling of & is required here, it's done by
17884d9c625SLionel Sambuc 	 * re_sub().
17984d9c625SLionel Sambuc 	 *
18084d9c625SLionel Sambuc 	 * The special character ~ (\~ if O_MAGIC not set) inserts the
18184d9c625SLionel Sambuc 	 * previous replacement string into this replacement string.
18284d9c625SLionel Sambuc 	 * Count ~'s to figure out how much space we need.  We could
18384d9c625SLionel Sambuc 	 * special case nonexistent last patterns or whether or not
18484d9c625SLionel Sambuc 	 * O_MAGIC is set, but it's probably not worth the effort.
18584d9c625SLionel Sambuc 	 *
18684d9c625SLionel Sambuc 	 * QUOTING NOTE:
18784d9c625SLionel Sambuc 	 *
18884d9c625SLionel Sambuc 	 * Only toss an escaping character if it escapes a delimiter or
18984d9c625SLionel Sambuc 	 * if O_MAGIC is set and it escapes a tilde.
19084d9c625SLionel Sambuc 	 *
19184d9c625SLionel Sambuc 	 * !!!
19284d9c625SLionel Sambuc 	 * If the entire replacement pattern is "%", then use the last
19384d9c625SLionel Sambuc 	 * replacement pattern.  This semantic was added to vi in System
19484d9c625SLionel Sambuc 	 * V and then percolated elsewhere, presumably around the time
19584d9c625SLionel Sambuc 	 * that it was added to their version of ed(1).
19684d9c625SLionel Sambuc 	 */
19784d9c625SLionel Sambuc 	if (p[0] == L('\0') || p[0] == delim) {
19884d9c625SLionel Sambuc 		if (p[0] == delim)
19984d9c625SLionel Sambuc 			++p;
20084d9c625SLionel Sambuc 		if (sp->repl != NULL)
20184d9c625SLionel Sambuc 			free(sp->repl);
20284d9c625SLionel Sambuc 		sp->repl = NULL;
20384d9c625SLionel Sambuc 		sp->repl_len = 0;
20484d9c625SLionel Sambuc 	} else if (p[0] == L('%') && (p[1] == L('\0') || p[1] == delim))
20584d9c625SLionel Sambuc 		p += p[1] == delim ? 2 : 1;
20684d9c625SLionel Sambuc 	else {
20784d9c625SLionel Sambuc 		for (rep = p, len = 0;
20884d9c625SLionel Sambuc 		    p[0] != L('\0') && p[0] != delim; ++p, ++len)
20984d9c625SLionel Sambuc 			if (p[0] == L('~'))
21084d9c625SLionel Sambuc 				len += sp->repl_len;
21184d9c625SLionel Sambuc 		GET_SPACE_RETW(sp, bp, blen, len);
21284d9c625SLionel Sambuc 		for (t = bp, len = 0, p = rep;;) {
21384d9c625SLionel Sambuc 			if (p[0] == L('\0') || p[0] == delim) {
21484d9c625SLionel Sambuc 				if (p[0] == delim)
21584d9c625SLionel Sambuc 					++p;
21684d9c625SLionel Sambuc 				break;
21784d9c625SLionel Sambuc 			}
21884d9c625SLionel Sambuc 			if (p[0] == L('\\')) {
21984d9c625SLionel Sambuc 				if (p[1] == delim)
22084d9c625SLionel Sambuc 					++p;
22184d9c625SLionel Sambuc 				else if (p[1] == L('\\')) {
22284d9c625SLionel Sambuc 					*t++ = *p++;
22384d9c625SLionel Sambuc 					++len;
22484d9c625SLionel Sambuc 				} else if (p[1] == L('~')) {
22584d9c625SLionel Sambuc 					++p;
22684d9c625SLionel Sambuc 					if (!O_ISSET(sp, O_MAGIC))
22784d9c625SLionel Sambuc 						goto tilde;
22884d9c625SLionel Sambuc 				}
22984d9c625SLionel Sambuc 			} else if (p[0] == L('~') && O_ISSET(sp, O_MAGIC)) {
23084d9c625SLionel Sambuc tilde:				++p;
23184d9c625SLionel Sambuc 				MEMCPYW(t, sp->repl, sp->repl_len);
23284d9c625SLionel Sambuc 				t += sp->repl_len;
23384d9c625SLionel Sambuc 				len += sp->repl_len;
23484d9c625SLionel Sambuc 				continue;
23584d9c625SLionel Sambuc 			}
23684d9c625SLionel Sambuc 			*t++ = *p++;
23784d9c625SLionel Sambuc 			++len;
23884d9c625SLionel Sambuc 		}
23984d9c625SLionel Sambuc 		if ((sp->repl_len = len) != 0) {
24084d9c625SLionel Sambuc 			if (sp->repl != NULL)
24184d9c625SLionel Sambuc 				free(sp->repl);
24284d9c625SLionel Sambuc 			if ((sp->repl = malloc(len * sizeof(CHAR_T))) == NULL) {
24384d9c625SLionel Sambuc 				msgq(sp, M_SYSERR, NULL);
24484d9c625SLionel Sambuc 				FREE_SPACEW(sp, bp, blen);
24584d9c625SLionel Sambuc 				return (1);
24684d9c625SLionel Sambuc 			}
24784d9c625SLionel Sambuc 			MEMCPYW(sp->repl, bp, len);
24884d9c625SLionel Sambuc 		}
24984d9c625SLionel Sambuc 		FREE_SPACEW(sp, bp, blen);
25084d9c625SLionel Sambuc 	}
25184d9c625SLionel Sambuc 	return (s(sp, cmdp, p, re, flags));
25284d9c625SLionel Sambuc }
25384d9c625SLionel Sambuc 
25484d9c625SLionel Sambuc /*
25584d9c625SLionel Sambuc  * ex_subagain --
25684d9c625SLionel Sambuc  *	[line [,line]] & [cgr] [count] [#lp]]
25784d9c625SLionel Sambuc  *
25884d9c625SLionel Sambuc  *	Substitute using the last substitute RE and replacement pattern.
25984d9c625SLionel Sambuc  *
26084d9c625SLionel Sambuc  * PUBLIC: int ex_subagain __P((SCR *, EXCMD *));
26184d9c625SLionel Sambuc  */
26284d9c625SLionel Sambuc int
ex_subagain(SCR * sp,EXCMD * cmdp)26384d9c625SLionel Sambuc ex_subagain(SCR *sp, EXCMD *cmdp)
26484d9c625SLionel Sambuc {
26584d9c625SLionel Sambuc 	if (sp->subre == NULL) {
26684d9c625SLionel Sambuc 		ex_emsg(sp, NULL, EXM_NOPREVRE);
26784d9c625SLionel Sambuc 		return (1);
26884d9c625SLionel Sambuc 	}
26984d9c625SLionel Sambuc 	if (!F_ISSET(sp, SC_RE_SUBST) &&
27084d9c625SLionel Sambuc 	    re_compile(sp, sp->subre, sp->subre_len,
27184d9c625SLionel Sambuc 	    NULL, NULL, &sp->subre_c, SEARCH_CSUBST | SEARCH_MSG))
27284d9c625SLionel Sambuc 		return (1);
27384d9c625SLionel Sambuc 	return (s(sp,
27484d9c625SLionel Sambuc 	    cmdp, cmdp->argc ? cmdp->argv[0]->bp : NULL, &sp->subre_c, 0));
27584d9c625SLionel Sambuc }
27684d9c625SLionel Sambuc 
27784d9c625SLionel Sambuc /*
27884d9c625SLionel Sambuc  * ex_subtilde --
27984d9c625SLionel Sambuc  *	[line [,line]] ~ [cgr] [count] [#lp]]
28084d9c625SLionel Sambuc  *
28184d9c625SLionel Sambuc  *	Substitute using the last RE and last substitute replacement pattern.
28284d9c625SLionel Sambuc  *
28384d9c625SLionel Sambuc  * PUBLIC: int ex_subtilde __P((SCR *, EXCMD *));
28484d9c625SLionel Sambuc  */
28584d9c625SLionel Sambuc int
ex_subtilde(SCR * sp,EXCMD * cmdp)28684d9c625SLionel Sambuc ex_subtilde(SCR *sp, EXCMD *cmdp)
28784d9c625SLionel Sambuc {
28884d9c625SLionel Sambuc 	if (sp->re == NULL) {
28984d9c625SLionel Sambuc 		ex_emsg(sp, NULL, EXM_NOPREVRE);
29084d9c625SLionel Sambuc 		return (1);
29184d9c625SLionel Sambuc 	}
29284d9c625SLionel Sambuc 	if (!F_ISSET(sp, SC_RE_SEARCH) && re_compile(sp, sp->re,
29384d9c625SLionel Sambuc 	    sp->re_len, NULL, NULL, &sp->re_c, SEARCH_CSEARCH | SEARCH_MSG))
29484d9c625SLionel Sambuc 		return (1);
29584d9c625SLionel Sambuc 	return (s(sp,
29684d9c625SLionel Sambuc 	    cmdp, cmdp->argc ? cmdp->argv[0]->bp : NULL, &sp->re_c, 0));
29784d9c625SLionel Sambuc }
29884d9c625SLionel Sambuc 
29984d9c625SLionel Sambuc /*
30084d9c625SLionel Sambuc  * s --
30184d9c625SLionel Sambuc  * Do the substitution.  This stuff is *really* tricky.  There are lots of
30284d9c625SLionel Sambuc  * special cases, and general nastiness.  Don't mess with it unless you're
30384d9c625SLionel Sambuc  * pretty confident.
30484d9c625SLionel Sambuc  *
30584d9c625SLionel Sambuc  * The nasty part of the substitution is what happens when the replacement
30684d9c625SLionel Sambuc  * string contains newlines.  It's a bit tricky -- consider the information
30784d9c625SLionel Sambuc  * that has to be retained for "s/f\(o\)o/^M\1^M\1/".  The solution here is
30884d9c625SLionel Sambuc  * to build a set of newline offsets which we use to break the line up later,
30984d9c625SLionel Sambuc  * when the replacement is done.  Don't change it unless you're *damned*
31084d9c625SLionel Sambuc  * confident.
31184d9c625SLionel Sambuc  */
31284d9c625SLionel Sambuc #define	NEEDNEWLINE(sp) {						\
31384d9c625SLionel Sambuc 	if (sp->newl_len == sp->newl_cnt) {				\
31484d9c625SLionel Sambuc 		sp->newl_len += 25;					\
31584d9c625SLionel Sambuc 		REALLOC(sp, sp->newl, size_t *,				\
31684d9c625SLionel Sambuc 		    sp->newl_len * sizeof(size_t));			\
31784d9c625SLionel Sambuc 		if (sp->newl == NULL) {					\
31884d9c625SLionel Sambuc 			sp->newl_len = 0;				\
31984d9c625SLionel Sambuc 			return (1);					\
32084d9c625SLionel Sambuc 		}							\
32184d9c625SLionel Sambuc 	}								\
32284d9c625SLionel Sambuc }
32384d9c625SLionel Sambuc 
32484d9c625SLionel Sambuc #define	BUILD(sp, l, len) {						\
32584d9c625SLionel Sambuc 	if (lbclen + (len) > lblen) {					\
32684d9c625SLionel Sambuc 		lblen += MAX(lbclen + (len), 256);			\
32784d9c625SLionel Sambuc 		REALLOC(sp, lb, CHAR_T *, lblen * sizeof(CHAR_T));	\
32884d9c625SLionel Sambuc 		if (lb == NULL) {					\
32984d9c625SLionel Sambuc 			lbclen = 0;					\
33084d9c625SLionel Sambuc 			return (1);					\
33184d9c625SLionel Sambuc 		}							\
33284d9c625SLionel Sambuc 	}								\
33384d9c625SLionel Sambuc 	MEMCPYW(lb + lbclen, l, len);					\
33484d9c625SLionel Sambuc 	lbclen += len;							\
33584d9c625SLionel Sambuc }
33684d9c625SLionel Sambuc 
33784d9c625SLionel Sambuc #define	NEEDSP(sp, len, pnt) {						\
33884d9c625SLionel Sambuc 	if (lbclen + (len) > lblen) {					\
33984d9c625SLionel Sambuc 		lblen += MAX(lbclen + (len), 256);			\
34084d9c625SLionel Sambuc 		REALLOC(sp, lb, CHAR_T *, lblen * sizeof(CHAR_T));	\
34184d9c625SLionel Sambuc 		if (lb == NULL) {					\
34284d9c625SLionel Sambuc 			lbclen = 0;					\
34384d9c625SLionel Sambuc 			return (1);					\
34484d9c625SLionel Sambuc 		}							\
34584d9c625SLionel Sambuc 		pnt = lb + lbclen;					\
34684d9c625SLionel Sambuc 	}								\
34784d9c625SLionel Sambuc }
34884d9c625SLionel Sambuc 
34984d9c625SLionel Sambuc static int
s(SCR * sp,EXCMD * cmdp,CHAR_T * st,regex_t * re,u_int flags)35084d9c625SLionel Sambuc s(SCR *sp, EXCMD *cmdp, CHAR_T *st, regex_t *re, u_int flags)
35184d9c625SLionel Sambuc {
35284d9c625SLionel Sambuc 	EVENT ev;
35384d9c625SLionel Sambuc 	MARK from, to;
35484d9c625SLionel Sambuc 	TEXTH tiq;
35584d9c625SLionel Sambuc 	db_recno_t elno, lno, slno;
35684d9c625SLionel Sambuc 	u_long ul;
35784d9c625SLionel Sambuc 	regmatch_t match[10];
35884d9c625SLionel Sambuc 	size_t blen, cnt, last, lbclen, lblen, len, llen;
35984d9c625SLionel Sambuc 	size_t offset, saved_offset, scno;
36084d9c625SLionel Sambuc 	int lflag, nflag, pflag, rflag;
36184d9c625SLionel Sambuc 	int didsub, do_eol_match, eflags, empty_ok, eval;
36284d9c625SLionel Sambuc 	int linechanged, matched, quit, rval;
36384d9c625SLionel Sambuc 	CHAR_T *lb, *bp;
36484d9c625SLionel Sambuc 	enum nresult nret;
36584d9c625SLionel Sambuc 
36684d9c625SLionel Sambuc 	NEEDFILE(sp, cmdp);
36784d9c625SLionel Sambuc 
36884d9c625SLionel Sambuc 	slno = sp->lno;
36984d9c625SLionel Sambuc 	scno = sp->cno;
37084d9c625SLionel Sambuc 
37184d9c625SLionel Sambuc 	/*
37284d9c625SLionel Sambuc 	 * !!!
37384d9c625SLionel Sambuc 	 * Historically, the 'g' and 'c' suffices were always toggled as flags,
37484d9c625SLionel Sambuc 	 * so ":s/A/B/" was the same as ":s/A/B/ccgg".  If O_EDCOMPATIBLE was
37584d9c625SLionel Sambuc 	 * not set, they were initialized to 0 for all substitute commands.  If
37684d9c625SLionel Sambuc 	 * O_EDCOMPATIBLE was set, they were initialized to 0 only if the user
37784d9c625SLionel Sambuc 	 * specified substitute/replacement patterns (see ex_s()).
37884d9c625SLionel Sambuc 	 */
37984d9c625SLionel Sambuc 	if (!O_ISSET(sp, O_EDCOMPATIBLE))
38084d9c625SLionel Sambuc 		sp->c_suffix = sp->g_suffix = 0;
38184d9c625SLionel Sambuc 
38284d9c625SLionel Sambuc 	/*
38384d9c625SLionel Sambuc 	 * Historic vi permitted the '#', 'l' and 'p' options in vi mode, but
38484d9c625SLionel Sambuc 	 * it only displayed the last change.  I'd disallow them, but they are
38584d9c625SLionel Sambuc 	 * useful in combination with the [v]global commands.  In the current
38684d9c625SLionel Sambuc 	 * model the problem is combining them with the 'c' flag -- the screen
38784d9c625SLionel Sambuc 	 * would have to flip back and forth between the confirm screen and the
38884d9c625SLionel Sambuc 	 * ex print screen, which would be pretty awful.  We do display all
38984d9c625SLionel Sambuc 	 * changes, though, for what that's worth.
39084d9c625SLionel Sambuc 	 *
39184d9c625SLionel Sambuc 	 * !!!
39284d9c625SLionel Sambuc 	 * Historic vi was fairly strict about the order of "options", the
39384d9c625SLionel Sambuc 	 * count, and "flags".  I'm somewhat fuzzy on the difference between
39484d9c625SLionel Sambuc 	 * options and flags, anyway, so this is a simpler approach, and we
39584d9c625SLionel Sambuc 	 * just take it them in whatever order the user gives them.  (The ex
39684d9c625SLionel Sambuc 	 * usage statement doesn't reflect this.)
39784d9c625SLionel Sambuc 	 */
39884d9c625SLionel Sambuc 	lflag = nflag = pflag = rflag = 0;
39984d9c625SLionel Sambuc 	if (st == NULL)
40084d9c625SLionel Sambuc 		goto noargs;
40184d9c625SLionel Sambuc 	for (lno = OOBLNO; *st != '\0'; ++st)
40284d9c625SLionel Sambuc 		switch (*st) {
40384d9c625SLionel Sambuc 		case ' ':
40484d9c625SLionel Sambuc 		case '\t':
40584d9c625SLionel Sambuc 			continue;
40684d9c625SLionel Sambuc 		case '+':
40784d9c625SLionel Sambuc 			++cmdp->flagoff;
40884d9c625SLionel Sambuc 			break;
40984d9c625SLionel Sambuc 		case '-':
41084d9c625SLionel Sambuc 			--cmdp->flagoff;
41184d9c625SLionel Sambuc 			break;
41284d9c625SLionel Sambuc 		case '0': case '1': case '2': case '3': case '4':
41384d9c625SLionel Sambuc 		case '5': case '6': case '7': case '8': case '9':
41484d9c625SLionel Sambuc 			if (lno != OOBLNO)
41584d9c625SLionel Sambuc 				goto usage;
41684d9c625SLionel Sambuc 			errno = 0;
41784d9c625SLionel Sambuc 			nret = nget_uslong(sp, &ul, st, &st, 10);
41884d9c625SLionel Sambuc 			lno = ul;
41984d9c625SLionel Sambuc 			if (*st == '\0')		/* Loop increment correction. */
42084d9c625SLionel Sambuc 				--st;
42184d9c625SLionel Sambuc 			if (nret != NUM_OK) {
42284d9c625SLionel Sambuc 				if (nret == NUM_OVER)
42384d9c625SLionel Sambuc 					msgq(sp, M_ERR, "153|Count overflow");
42484d9c625SLionel Sambuc 				else if (nret == NUM_UNDER)
42584d9c625SLionel Sambuc 					msgq(sp, M_ERR, "154|Count underflow");
42684d9c625SLionel Sambuc 				else
42784d9c625SLionel Sambuc 					msgq(sp, M_SYSERR, NULL);
42884d9c625SLionel Sambuc 				return (1);
42984d9c625SLionel Sambuc 			}
43084d9c625SLionel Sambuc 			/*
43184d9c625SLionel Sambuc 			 * In historic vi, the count was inclusive from the
43284d9c625SLionel Sambuc 			 * second address.
43384d9c625SLionel Sambuc 			 */
43484d9c625SLionel Sambuc 			cmdp->addr1.lno = cmdp->addr2.lno;
43584d9c625SLionel Sambuc 			cmdp->addr2.lno += lno - 1;
43684d9c625SLionel Sambuc 			if (!db_exist(sp, cmdp->addr2.lno) &&
43784d9c625SLionel Sambuc 			    db_last(sp, &cmdp->addr2.lno))
43884d9c625SLionel Sambuc 				return (1);
43984d9c625SLionel Sambuc 			break;
44084d9c625SLionel Sambuc 		case '#':
44184d9c625SLionel Sambuc 			nflag = 1;
44284d9c625SLionel Sambuc 			break;
44384d9c625SLionel Sambuc 		case 'c':
44484d9c625SLionel Sambuc 			sp->c_suffix = !sp->c_suffix;
44584d9c625SLionel Sambuc 
44684d9c625SLionel Sambuc 			/* Ex text structure initialization. */
44784d9c625SLionel Sambuc 			if (F_ISSET(sp, SC_EX)) {
44884d9c625SLionel Sambuc 				memset(&tiq, 0, sizeof(TEXTH));
44984d9c625SLionel Sambuc 				TAILQ_INIT(&tiq);
45084d9c625SLionel Sambuc 			}
45184d9c625SLionel Sambuc 			break;
45284d9c625SLionel Sambuc 		case 'g':
45384d9c625SLionel Sambuc 			sp->g_suffix = !sp->g_suffix;
45484d9c625SLionel Sambuc 			break;
45584d9c625SLionel Sambuc 		case 'l':
45684d9c625SLionel Sambuc 			lflag = 1;
45784d9c625SLionel Sambuc 			break;
45884d9c625SLionel Sambuc 		case 'p':
45984d9c625SLionel Sambuc 			pflag = 1;
46084d9c625SLionel Sambuc 			break;
46184d9c625SLionel Sambuc 		case 'r':
46284d9c625SLionel Sambuc 			if (LF_ISSET(SUB_FIRST)) {
46384d9c625SLionel Sambuc 				msgq(sp, M_ERR,
46484d9c625SLionel Sambuc 		    "155|Regular expression specified; r flag meaningless");
46584d9c625SLionel Sambuc 				return (1);
46684d9c625SLionel Sambuc 			}
46784d9c625SLionel Sambuc 			if (!F_ISSET(sp, SC_RE_SEARCH)) {
46884d9c625SLionel Sambuc 				ex_emsg(sp, NULL, EXM_NOPREVRE);
46984d9c625SLionel Sambuc 				return (1);
47084d9c625SLionel Sambuc 			}
47184d9c625SLionel Sambuc 			rflag = 1;
47284d9c625SLionel Sambuc 			re = &sp->re_c;
47384d9c625SLionel Sambuc 			break;
47484d9c625SLionel Sambuc 		default:
47584d9c625SLionel Sambuc 			goto usage;
47684d9c625SLionel Sambuc 		}
47784d9c625SLionel Sambuc 
47884d9c625SLionel Sambuc 	if (*st != '\0' || (!rflag && LF_ISSET(SUB_MUSTSETR))) {
47984d9c625SLionel Sambuc usage:		ex_emsg(sp, cmdp->cmd->usage, EXM_USAGE);
48084d9c625SLionel Sambuc 		return (1);
48184d9c625SLionel Sambuc 	}
48284d9c625SLionel Sambuc 
48384d9c625SLionel Sambuc noargs:	if (F_ISSET(sp, SC_VI) && sp->c_suffix && (lflag || nflag || pflag)) {
48484d9c625SLionel Sambuc 		msgq(sp, M_ERR,
48584d9c625SLionel Sambuc "156|The #, l and p flags may not be combined with the c flag in vi mode");
48684d9c625SLionel Sambuc 		return (1);
48784d9c625SLionel Sambuc 	}
48884d9c625SLionel Sambuc 
48984d9c625SLionel Sambuc 	/*
49084d9c625SLionel Sambuc 	 * bp:		if interactive, line cache
49184d9c625SLionel Sambuc 	 * blen:	if interactive, line cache length
49284d9c625SLionel Sambuc 	 * lb:		build buffer pointer.
49384d9c625SLionel Sambuc 	 * lbclen:	current length of built buffer.
49484d9c625SLionel Sambuc 	 * lblen;	length of build buffer.
49584d9c625SLionel Sambuc 	 */
49684d9c625SLionel Sambuc 	bp = lb = NULL;
49784d9c625SLionel Sambuc 	blen = lbclen = lblen = 0;
49884d9c625SLionel Sambuc 
49984d9c625SLionel Sambuc 	/* For each line... */
50084d9c625SLionel Sambuc 	lno = cmdp->addr1.lno == 0 ? 1 : cmdp->addr1.lno;
50184d9c625SLionel Sambuc 	for (matched = quit = 0,
50284d9c625SLionel Sambuc 	    elno = cmdp->addr2.lno; !quit && lno <= elno; ++lno) {
50384d9c625SLionel Sambuc 
50484d9c625SLionel Sambuc 		/* Someone's unhappy, time to stop. */
50584d9c625SLionel Sambuc 		if (INTERRUPTED(sp))
50684d9c625SLionel Sambuc 			break;
50784d9c625SLionel Sambuc 
50884d9c625SLionel Sambuc 		/* Get the line. */
50984d9c625SLionel Sambuc 		if (db_get(sp, lno, DBG_FATAL, &st, &llen))
51084d9c625SLionel Sambuc 			goto err;
51184d9c625SLionel Sambuc 
51284d9c625SLionel Sambuc 		/*
51384d9c625SLionel Sambuc 		 * Make a local copy if doing confirmation -- when calling
51484d9c625SLionel Sambuc 		 * the confirm routine we're likely to lose the cached copy.
51584d9c625SLionel Sambuc 		 */
51684d9c625SLionel Sambuc 		if (sp->c_suffix) {
51784d9c625SLionel Sambuc 			if (bp == NULL) {
51884d9c625SLionel Sambuc 				GET_SPACE_RETW(sp, bp, blen, llen);
51984d9c625SLionel Sambuc 			} else
52084d9c625SLionel Sambuc 				ADD_SPACE_RETW(sp, bp, blen, llen);
52184d9c625SLionel Sambuc 			MEMCPYW(bp, st, llen);
52284d9c625SLionel Sambuc 			st = bp;
52384d9c625SLionel Sambuc 		}
52484d9c625SLionel Sambuc 
52584d9c625SLionel Sambuc 		/* Start searching from the beginning. */
52684d9c625SLionel Sambuc 		offset = 0;
52784d9c625SLionel Sambuc 		len = llen;
52884d9c625SLionel Sambuc 
52984d9c625SLionel Sambuc 		/* Reset the build buffer offset. */
53084d9c625SLionel Sambuc 		lbclen = 0;
53184d9c625SLionel Sambuc 
53284d9c625SLionel Sambuc 		/* Reset empty match flag. */
53384d9c625SLionel Sambuc 		empty_ok = 1;
53484d9c625SLionel Sambuc 
53584d9c625SLionel Sambuc 		/*
53684d9c625SLionel Sambuc 		 * We don't want to have to do a setline if the line didn't
53784d9c625SLionel Sambuc 		 * change -- keep track of whether or not this line changed.
53884d9c625SLionel Sambuc 		 * If doing confirmations, don't want to keep setting the
53984d9c625SLionel Sambuc 		 * line if change is refused -- keep track of substitutions.
54084d9c625SLionel Sambuc 		 */
54184d9c625SLionel Sambuc 		didsub = linechanged = 0;
54284d9c625SLionel Sambuc 
54384d9c625SLionel Sambuc 		/* New line, do an EOL match. */
54484d9c625SLionel Sambuc 		do_eol_match = 1;
54584d9c625SLionel Sambuc 
54684d9c625SLionel Sambuc 		/* It's not nul terminated, but we pretend it is. */
54784d9c625SLionel Sambuc 		eflags = REG_STARTEND;
54884d9c625SLionel Sambuc 
54984d9c625SLionel Sambuc 		/*
55084d9c625SLionel Sambuc 		 * The search area is from st + offset to the EOL.
55184d9c625SLionel Sambuc 		 *
55284d9c625SLionel Sambuc 		 * Generally, match[0].rm_so is the offset of the start
55384d9c625SLionel Sambuc 		 * of the match from the start of the search, and offset
55484d9c625SLionel Sambuc 		 * is the offset of the start of the last search.
55584d9c625SLionel Sambuc 		 */
55684d9c625SLionel Sambuc nextmatch:	match[0].rm_so = 0;
55784d9c625SLionel Sambuc 		match[0].rm_eo = len;
55884d9c625SLionel Sambuc 
55984d9c625SLionel Sambuc 		/* Get the next match. */
56084d9c625SLionel Sambuc 		eval = regexec(re, st + offset, 10, match, eflags);
56184d9c625SLionel Sambuc 
56284d9c625SLionel Sambuc 		/*
56384d9c625SLionel Sambuc 		 * There wasn't a match or if there was an error, deal with
56484d9c625SLionel Sambuc 		 * it.  If there was a previous match in this line, resolve
56584d9c625SLionel Sambuc 		 * the changes into the database.  Otherwise, just move on.
56684d9c625SLionel Sambuc 		 */
56784d9c625SLionel Sambuc 		if (eval == REG_NOMATCH)
56884d9c625SLionel Sambuc 			goto endmatch;
56984d9c625SLionel Sambuc 		if (eval != 0) {
57084d9c625SLionel Sambuc 			re_error(sp, eval, re);
57184d9c625SLionel Sambuc 			goto err;
57284d9c625SLionel Sambuc 		}
57384d9c625SLionel Sambuc 		matched = 1;
57484d9c625SLionel Sambuc 
57584d9c625SLionel Sambuc 		/* Only the first search can match an anchored expression. */
57684d9c625SLionel Sambuc 		eflags |= REG_NOTBOL;
57784d9c625SLionel Sambuc 
57884d9c625SLionel Sambuc 		/*
57984d9c625SLionel Sambuc 		 * !!!
58084d9c625SLionel Sambuc 		 * It's possible to match 0-length strings -- for example, the
58184d9c625SLionel Sambuc 		 * command s;a*;X;, when matched against the string "aabb" will
58284d9c625SLionel Sambuc 		 * result in "XbXbX", i.e. the matches are "aa", the space
58384d9c625SLionel Sambuc 		 * between the b's and the space between the b's and the end of
58484d9c625SLionel Sambuc 		 * the string.  There is a similar space between the beginning
58584d9c625SLionel Sambuc 		 * of the string and the a's.  The rule that we use (because vi
58684d9c625SLionel Sambuc 		 * historically used it) is that any 0-length match, occurring
58784d9c625SLionel Sambuc 		 * immediately after a match, is ignored.  Otherwise, the above
58884d9c625SLionel Sambuc 		 * example would have resulted in "XXbXbX".  Another example is
58984d9c625SLionel Sambuc 		 * incorrectly using " *" to replace groups of spaces with one
59084d9c625SLionel Sambuc 		 * space.
59184d9c625SLionel Sambuc 		 *
59284d9c625SLionel Sambuc 		 * The way we do this is that if we just had a successful match,
59384d9c625SLionel Sambuc 		 * the starting offset does not skip characters, and the match
59484d9c625SLionel Sambuc 		 * is empty, ignore the match and move forward.  If there's no
59584d9c625SLionel Sambuc 		 * more characters in the string, we were attempting to match
59684d9c625SLionel Sambuc 		 * after the last character, so quit.
59784d9c625SLionel Sambuc 		 */
59884d9c625SLionel Sambuc 		if (!empty_ok && match[0].rm_so == 0 && match[0].rm_eo == 0) {
59984d9c625SLionel Sambuc 			empty_ok = 1;
60084d9c625SLionel Sambuc 			if (len == 0)
60184d9c625SLionel Sambuc 				goto endmatch;
60284d9c625SLionel Sambuc 			BUILD(sp, st + offset, 1)
60384d9c625SLionel Sambuc 			++offset;
60484d9c625SLionel Sambuc 			--len;
60584d9c625SLionel Sambuc 			goto nextmatch;
60684d9c625SLionel Sambuc 		}
60784d9c625SLionel Sambuc 
60884d9c625SLionel Sambuc 		/* Confirm change. */
60984d9c625SLionel Sambuc 		if (sp->c_suffix) {
61084d9c625SLionel Sambuc 			/*
61184d9c625SLionel Sambuc 			 * Set the cursor position for confirmation.  Note,
61284d9c625SLionel Sambuc 			 * if we matched on a '$', the cursor may be past
61384d9c625SLionel Sambuc 			 * the end of line.
61484d9c625SLionel Sambuc 			 */
61584d9c625SLionel Sambuc 			from.lno = to.lno = lno;
61684d9c625SLionel Sambuc 			from.cno = match[0].rm_so + offset;
61784d9c625SLionel Sambuc 			to.cno = match[0].rm_eo + offset;
61884d9c625SLionel Sambuc 			/*
61984d9c625SLionel Sambuc 			 * Both ex and vi have to correct for a change before
62084d9c625SLionel Sambuc 			 * the first character in the line.
62184d9c625SLionel Sambuc 			 */
62284d9c625SLionel Sambuc 			if (llen == 0)
62384d9c625SLionel Sambuc 				from.cno = to.cno = 0;
62484d9c625SLionel Sambuc 			if (F_ISSET(sp, SC_VI)) {
62584d9c625SLionel Sambuc 				/*
62684d9c625SLionel Sambuc 				 * Only vi has to correct for a change after
62784d9c625SLionel Sambuc 				 * the last character in the line.
62884d9c625SLionel Sambuc 				 *
62984d9c625SLionel Sambuc 				 * XXX
63084d9c625SLionel Sambuc 				 * It would be nice to change the vi code so
63184d9c625SLionel Sambuc 				 * that we could display a cursor past EOL.
63284d9c625SLionel Sambuc 				 */
63384d9c625SLionel Sambuc 				if (to.cno >= llen)
63484d9c625SLionel Sambuc 					to.cno = llen - 1;
63584d9c625SLionel Sambuc 				if (from.cno >= llen)
63684d9c625SLionel Sambuc 					from.cno = llen - 1;
63784d9c625SLionel Sambuc 
63884d9c625SLionel Sambuc 				sp->lno = from.lno;
63984d9c625SLionel Sambuc 				sp->cno = from.cno;
64084d9c625SLionel Sambuc 				if (vs_refresh(sp, 1))
64184d9c625SLionel Sambuc 					goto err;
64284d9c625SLionel Sambuc 
64384d9c625SLionel Sambuc 				vs_update(sp, msg_cat(sp,
64484d9c625SLionel Sambuc 				    "169|Confirm change? [n]", NULL), NULL);
64584d9c625SLionel Sambuc 
64684d9c625SLionel Sambuc 				if (v_event_get(sp, &ev, 0, 0))
64784d9c625SLionel Sambuc 					goto err;
64884d9c625SLionel Sambuc 				switch (ev.e_event) {
64984d9c625SLionel Sambuc 				case E_CHARACTER:
65084d9c625SLionel Sambuc 					break;
65184d9c625SLionel Sambuc 				case E_EOF:
65284d9c625SLionel Sambuc 				case E_ERR:
65384d9c625SLionel Sambuc 				case E_INTERRUPT:
65484d9c625SLionel Sambuc 					goto lquit;
65584d9c625SLionel Sambuc 				default:
65684d9c625SLionel Sambuc 					v_event_err(sp, &ev);
65784d9c625SLionel Sambuc 					goto lquit;
65884d9c625SLionel Sambuc 				}
65984d9c625SLionel Sambuc 			} else {
66084d9c625SLionel Sambuc 				if (ex_print(sp, cmdp, &from, &to, 0) ||
66184d9c625SLionel Sambuc 				    ex_scprint(sp, &from, &to))
66284d9c625SLionel Sambuc 					goto lquit;
66384d9c625SLionel Sambuc 				if (ex_txt(sp, &tiq, 0, TXT_CR))
66484d9c625SLionel Sambuc 					goto err;
66584d9c625SLionel Sambuc 				ev.e_c = TAILQ_FIRST(&tiq)->lb[0];
66684d9c625SLionel Sambuc 			}
66784d9c625SLionel Sambuc 
66884d9c625SLionel Sambuc 			switch (ev.e_c) {
66984d9c625SLionel Sambuc 			case CH_YES:
67084d9c625SLionel Sambuc 				break;
67184d9c625SLionel Sambuc 			default:
67284d9c625SLionel Sambuc 			case CH_NO:
67384d9c625SLionel Sambuc 				didsub = 0;
67484d9c625SLionel Sambuc 				BUILD(sp, st + offset, match[0].rm_eo);
67584d9c625SLionel Sambuc 				goto skip;
67684d9c625SLionel Sambuc 			case CH_QUIT:
67784d9c625SLionel Sambuc 				/* Set the quit/interrupted flags. */
67884d9c625SLionel Sambuc lquit:				quit = 1;
67984d9c625SLionel Sambuc 				F_SET(sp->gp, G_INTERRUPTED);
68084d9c625SLionel Sambuc 
68184d9c625SLionel Sambuc 				/*
68284d9c625SLionel Sambuc 				 * Resolve any changes, then return to (and
68384d9c625SLionel Sambuc 				 * exit from) the main loop.
68484d9c625SLionel Sambuc 				 */
68584d9c625SLionel Sambuc 				goto endmatch;
68684d9c625SLionel Sambuc 			}
68784d9c625SLionel Sambuc 		}
68884d9c625SLionel Sambuc 
68984d9c625SLionel Sambuc 		/*
69084d9c625SLionel Sambuc 		 * Set the cursor to the last position changed, converting
69184d9c625SLionel Sambuc 		 * from 1-based to 0-based.
69284d9c625SLionel Sambuc 		 */
69384d9c625SLionel Sambuc 		sp->lno = lno;
69484d9c625SLionel Sambuc 		sp->cno = match[0].rm_so;
69584d9c625SLionel Sambuc 
69684d9c625SLionel Sambuc 		/* Copy the bytes before the match into the build buffer. */
69784d9c625SLionel Sambuc 		BUILD(sp, st + offset, match[0].rm_so);
69884d9c625SLionel Sambuc 
69984d9c625SLionel Sambuc 		/* Substitute the matching bytes. */
70084d9c625SLionel Sambuc 		didsub = 1;
70184d9c625SLionel Sambuc 		if (re_sub(sp, st + offset, &lb, &lbclen, &lblen, match))
70284d9c625SLionel Sambuc 			goto err;
70384d9c625SLionel Sambuc 
70484d9c625SLionel Sambuc 		/* Set the change flag so we know this line was modified. */
70584d9c625SLionel Sambuc 		linechanged = 1;
70684d9c625SLionel Sambuc 
70784d9c625SLionel Sambuc 		/* Move past the matched bytes. */
70884d9c625SLionel Sambuc skip:		offset += match[0].rm_eo;
70984d9c625SLionel Sambuc 		len -= match[0].rm_eo;
71084d9c625SLionel Sambuc 
71184d9c625SLionel Sambuc 		/* A match cannot be followed by an empty pattern. */
71284d9c625SLionel Sambuc 		empty_ok = 0;
71384d9c625SLionel Sambuc 
71484d9c625SLionel Sambuc 		/*
71584d9c625SLionel Sambuc 		 * If doing a global change with confirmation, we have to
71684d9c625SLionel Sambuc 		 * update the screen.  The basic idea is to store the line
71784d9c625SLionel Sambuc 		 * so the screen update routines can find it, and restart.
71884d9c625SLionel Sambuc 		 */
71984d9c625SLionel Sambuc 		if (didsub && sp->c_suffix && sp->g_suffix) {
72084d9c625SLionel Sambuc 			/*
72184d9c625SLionel Sambuc 			 * The new search offset will be the end of the
72284d9c625SLionel Sambuc 			 * modified line.
72384d9c625SLionel Sambuc 			 */
72484d9c625SLionel Sambuc 			saved_offset = lbclen;
72584d9c625SLionel Sambuc 
72684d9c625SLionel Sambuc 			/* Copy the rest of the line. */
72784d9c625SLionel Sambuc 			if (len)
72884d9c625SLionel Sambuc 				BUILD(sp, st + offset, len)
72984d9c625SLionel Sambuc 
73084d9c625SLionel Sambuc 			/* Set the new offset. */
73184d9c625SLionel Sambuc 			offset = saved_offset;
73284d9c625SLionel Sambuc 
73384d9c625SLionel Sambuc 			/* Store inserted lines, adjusting the build buffer. */
73484d9c625SLionel Sambuc 			last = 0;
73584d9c625SLionel Sambuc 			if (sp->newl_cnt) {
73684d9c625SLionel Sambuc 				for (cnt = 0;
73784d9c625SLionel Sambuc 				    cnt < sp->newl_cnt; ++cnt, ++lno, ++elno) {
73884d9c625SLionel Sambuc 					if (db_insert(sp, lno,
73984d9c625SLionel Sambuc 					    lb + last, sp->newl[cnt] - last))
74084d9c625SLionel Sambuc 						goto err;
74184d9c625SLionel Sambuc 					last = sp->newl[cnt] + 1;
74284d9c625SLionel Sambuc 					++sp->rptlines[L_ADDED];
74384d9c625SLionel Sambuc 				}
74484d9c625SLionel Sambuc 				lbclen -= last;
74584d9c625SLionel Sambuc 				offset -= last;
74684d9c625SLionel Sambuc 				sp->newl_cnt = 0;
74784d9c625SLionel Sambuc 			}
74884d9c625SLionel Sambuc 
74984d9c625SLionel Sambuc 			/* Store and retrieve the line. */
75084d9c625SLionel Sambuc 			if (db_set(sp, lno, lb + last, lbclen))
75184d9c625SLionel Sambuc 				goto err;
75284d9c625SLionel Sambuc 			if (db_get(sp, lno, DBG_FATAL, &st, &llen))
75384d9c625SLionel Sambuc 				goto err;
75484d9c625SLionel Sambuc 			ADD_SPACE_RETW(sp, bp, blen, llen)
75584d9c625SLionel Sambuc 			MEMCPYW(bp, st, llen);
75684d9c625SLionel Sambuc 			st = bp;
75784d9c625SLionel Sambuc 			len = llen - offset;
75884d9c625SLionel Sambuc 
75984d9c625SLionel Sambuc 			/* Restart the build. */
76084d9c625SLionel Sambuc 			lbclen = 0;
76184d9c625SLionel Sambuc 			BUILD(sp, st, offset);
76284d9c625SLionel Sambuc 
76384d9c625SLionel Sambuc 			/*
76484d9c625SLionel Sambuc 			 * If we haven't already done the after-the-string
76584d9c625SLionel Sambuc 			 * match, do one.  Set REG_NOTEOL so the '$' pattern
76684d9c625SLionel Sambuc 			 * only matches once.
76784d9c625SLionel Sambuc 			 */
76884d9c625SLionel Sambuc 			if (!do_eol_match)
76984d9c625SLionel Sambuc 				goto endmatch;
77084d9c625SLionel Sambuc 			if (offset == len) {
77184d9c625SLionel Sambuc 				do_eol_match = 0;
77284d9c625SLionel Sambuc 				eflags |= REG_NOTEOL;
77384d9c625SLionel Sambuc 			}
77484d9c625SLionel Sambuc 			goto nextmatch;
77584d9c625SLionel Sambuc 		}
77684d9c625SLionel Sambuc 
77784d9c625SLionel Sambuc 		/*
77884d9c625SLionel Sambuc 		 * If it's a global:
77984d9c625SLionel Sambuc 		 *
78084d9c625SLionel Sambuc 		 * If at the end of the string, do a test for the after
78184d9c625SLionel Sambuc 		 * the string match.  Set REG_NOTEOL so the '$' pattern
78284d9c625SLionel Sambuc 		 * only matches once.
78384d9c625SLionel Sambuc 		 */
78484d9c625SLionel Sambuc 		if (sp->g_suffix && do_eol_match) {
78584d9c625SLionel Sambuc 			if (len == 0) {
78684d9c625SLionel Sambuc 				do_eol_match = 0;
78784d9c625SLionel Sambuc 				eflags |= REG_NOTEOL;
78884d9c625SLionel Sambuc 			}
78984d9c625SLionel Sambuc 			goto nextmatch;
79084d9c625SLionel Sambuc 		}
79184d9c625SLionel Sambuc 
79284d9c625SLionel Sambuc endmatch:	if (!linechanged)
79384d9c625SLionel Sambuc 			continue;
79484d9c625SLionel Sambuc 
79584d9c625SLionel Sambuc 		/* Copy any remaining bytes into the build buffer. */
79684d9c625SLionel Sambuc 		if (len)
79784d9c625SLionel Sambuc 			BUILD(sp, st + offset, len)
79884d9c625SLionel Sambuc 
79984d9c625SLionel Sambuc 		/* Store inserted lines, adjusting the build buffer. */
80084d9c625SLionel Sambuc 		last = 0;
80184d9c625SLionel Sambuc 		if (sp->newl_cnt) {
80284d9c625SLionel Sambuc 			for (cnt = 0;
80384d9c625SLionel Sambuc 			    cnt < sp->newl_cnt; ++cnt, ++lno, ++elno) {
80484d9c625SLionel Sambuc 				if (db_insert(sp,
80584d9c625SLionel Sambuc 				    lno, lb + last, sp->newl[cnt] - last))
80684d9c625SLionel Sambuc 					goto err;
80784d9c625SLionel Sambuc 				last = sp->newl[cnt] + 1;
80884d9c625SLionel Sambuc 				++sp->rptlines[L_ADDED];
80984d9c625SLionel Sambuc 			}
81084d9c625SLionel Sambuc 			lbclen -= last;
81184d9c625SLionel Sambuc 			sp->newl_cnt = 0;
81284d9c625SLionel Sambuc 		}
81384d9c625SLionel Sambuc 
81484d9c625SLionel Sambuc 		/* Store the changed line. */
81584d9c625SLionel Sambuc 		if (db_set(sp, lno, lb + last, lbclen))
81684d9c625SLionel Sambuc 			goto err;
81784d9c625SLionel Sambuc 
81884d9c625SLionel Sambuc 		/* Update changed line counter. */
81984d9c625SLionel Sambuc 		if (sp->rptlchange != lno) {
82084d9c625SLionel Sambuc 			sp->rptlchange = lno;
82184d9c625SLionel Sambuc 			++sp->rptlines[L_CHANGED];
82284d9c625SLionel Sambuc 		}
82384d9c625SLionel Sambuc 
82484d9c625SLionel Sambuc 		/*
82584d9c625SLionel Sambuc 		 * !!!
82684d9c625SLionel Sambuc 		 * Display as necessary.  Historic practice is to only
82784d9c625SLionel Sambuc 		 * display the last line of a line split into multiple
82884d9c625SLionel Sambuc 		 * lines.
82984d9c625SLionel Sambuc 		 */
83084d9c625SLionel Sambuc 		if (lflag || nflag || pflag) {
83184d9c625SLionel Sambuc 			from.lno = to.lno = lno;
83284d9c625SLionel Sambuc 			from.cno = to.cno = 0;
83384d9c625SLionel Sambuc 			if (lflag)
83484d9c625SLionel Sambuc 				(void)ex_print(sp, cmdp, &from, &to, E_C_LIST);
83584d9c625SLionel Sambuc 			if (nflag)
83684d9c625SLionel Sambuc 				(void)ex_print(sp, cmdp, &from, &to, E_C_HASH);
83784d9c625SLionel Sambuc 			if (pflag)
83884d9c625SLionel Sambuc 				(void)ex_print(sp, cmdp, &from, &to, E_C_PRINT);
83984d9c625SLionel Sambuc 		}
84084d9c625SLionel Sambuc 	}
84184d9c625SLionel Sambuc 
84284d9c625SLionel Sambuc 	/*
84384d9c625SLionel Sambuc 	 * !!!
84484d9c625SLionel Sambuc 	 * Historically, vi attempted to leave the cursor at the same place if
84584d9c625SLionel Sambuc 	 * the substitution was done at the current cursor position.  Otherwise
84684d9c625SLionel Sambuc 	 * it moved it to the first non-blank of the last line changed.  There
84784d9c625SLionel Sambuc 	 * were some problems: for example, :s/$/foo/ with the cursor on the
84884d9c625SLionel Sambuc 	 * last character of the line left the cursor on the last character, or
84984d9c625SLionel Sambuc 	 * the & command with multiple occurrences of the matching string in the
85084d9c625SLionel Sambuc 	 * line usually left the cursor in a fairly random position.
85184d9c625SLionel Sambuc 	 *
85284d9c625SLionel Sambuc 	 * We try to do the same thing, with the exception that if the user is
85384d9c625SLionel Sambuc 	 * doing substitution with confirmation, we move to the last line about
85484d9c625SLionel Sambuc 	 * which the user was consulted, as opposed to the last line that they
85584d9c625SLionel Sambuc 	 * actually changed.  This prevents a screen flash if the user doesn't
85684d9c625SLionel Sambuc 	 * change many of the possible lines.
85784d9c625SLionel Sambuc 	 */
85884d9c625SLionel Sambuc 	if (!sp->c_suffix && (sp->lno != slno || sp->cno != scno)) {
85984d9c625SLionel Sambuc 		sp->cno = 0;
86084d9c625SLionel Sambuc 		(void)nonblank(sp, sp->lno, &sp->cno);
86184d9c625SLionel Sambuc 	}
86284d9c625SLionel Sambuc 
86384d9c625SLionel Sambuc 	/*
86484d9c625SLionel Sambuc 	 * If not in a global command, and nothing matched, say so.
86584d9c625SLionel Sambuc 	 * Else, if none of the lines displayed, put something up.
86684d9c625SLionel Sambuc 	 */
86784d9c625SLionel Sambuc 	rval = 0;
86884d9c625SLionel Sambuc 	if (!matched) {
86984d9c625SLionel Sambuc 		if (!F_ISSET(sp, SC_EX_GLOBAL)) {
87084d9c625SLionel Sambuc 			msgq(sp, M_ERR, "157|No match found");
87184d9c625SLionel Sambuc 			goto err;
87284d9c625SLionel Sambuc 		}
87384d9c625SLionel Sambuc 	} else if (!lflag && !nflag && !pflag)
87484d9c625SLionel Sambuc 		F_SET(cmdp, E_AUTOPRINT);
87584d9c625SLionel Sambuc 
87684d9c625SLionel Sambuc 	if (0) {
87784d9c625SLionel Sambuc err:		rval = 1;
87884d9c625SLionel Sambuc 	}
87984d9c625SLionel Sambuc 
88084d9c625SLionel Sambuc 	if (bp != NULL)
88184d9c625SLionel Sambuc 		FREE_SPACEW(sp, bp, blen);
88284d9c625SLionel Sambuc 	if (lb != NULL)
88384d9c625SLionel Sambuc 		free(lb);
88484d9c625SLionel Sambuc 	return (rval);
88584d9c625SLionel Sambuc }
88684d9c625SLionel Sambuc 
88784d9c625SLionel Sambuc /*
88884d9c625SLionel Sambuc  * re_compile --
88984d9c625SLionel Sambuc  *	Compile the RE.
89084d9c625SLionel Sambuc  *
89184d9c625SLionel Sambuc  * PUBLIC: int re_compile __P((SCR *,
89284d9c625SLionel Sambuc  * PUBLIC:     CHAR_T *, size_t, CHAR_T **, size_t *, regex_t *, u_int));
89384d9c625SLionel Sambuc  */
89484d9c625SLionel Sambuc int
re_compile(SCR * sp,CHAR_T * ptrn,size_t plen,CHAR_T ** ptrnp,size_t * lenp,regex_t * rep,u_int flags)89584d9c625SLionel Sambuc re_compile(SCR *sp, CHAR_T *ptrn, size_t plen, CHAR_T **ptrnp, size_t *lenp, regex_t *rep, u_int flags)
89684d9c625SLionel Sambuc {
89784d9c625SLionel Sambuc 	size_t len;
89884d9c625SLionel Sambuc 	int reflags, replaced, rval;
89984d9c625SLionel Sambuc 	CHAR_T *p;
90084d9c625SLionel Sambuc 
90184d9c625SLionel Sambuc 	/* Set RE flags. */
90284d9c625SLionel Sambuc 	reflags = 0;
90384d9c625SLionel Sambuc 	if (LF_ISSET(SEARCH_EXTEND))
90484d9c625SLionel Sambuc 		reflags |= REG_EXTENDED;
90584d9c625SLionel Sambuc 	if (LF_ISSET(SEARCH_IC))
90684d9c625SLionel Sambuc 		reflags |= REG_ICASE;
90784d9c625SLionel Sambuc 	if (LF_ISSET(SEARCH_LITERAL))
90884d9c625SLionel Sambuc 		reflags |= REG_NOSPEC;
90984d9c625SLionel Sambuc 	if (!LF_ISSET(SEARCH_NOOPT | SEARCH_CSCOPE | SEARCH_TAG)) {
91084d9c625SLionel Sambuc 		if (O_ISSET(sp, O_EXTENDED))
91184d9c625SLionel Sambuc 			reflags |= REG_EXTENDED;
91284d9c625SLionel Sambuc 		if (O_ISSET(sp, O_IGNORECASE))
91384d9c625SLionel Sambuc 			reflags |= REG_ICASE;
91484d9c625SLionel Sambuc 		if (O_ISSET(sp, O_ICLOWER))
91584d9c625SLionel Sambuc 			goto iclower;
91684d9c625SLionel Sambuc 	}
91784d9c625SLionel Sambuc 	if (LF_ISSET(SEARCH_ICL)) {
91884d9c625SLionel Sambuc iclower:	for (p = ptrn, len = plen; len > 0; ++p, --len)
91984d9c625SLionel Sambuc 			if (ISUPPER((UCHAR_T)*p))
92084d9c625SLionel Sambuc 				break;
92184d9c625SLionel Sambuc 		if (len == 0)
92284d9c625SLionel Sambuc 			reflags |= REG_ICASE;
92384d9c625SLionel Sambuc 	}
92484d9c625SLionel Sambuc 
92584d9c625SLionel Sambuc 	/* If we're replacing a saved value, clear the old one. */
92684d9c625SLionel Sambuc 	if (LF_ISSET(SEARCH_CSEARCH) && F_ISSET(sp, SC_RE_SEARCH)) {
92784d9c625SLionel Sambuc 		regfree(&sp->re_c);
92884d9c625SLionel Sambuc 		F_CLR(sp, SC_RE_SEARCH);
92984d9c625SLionel Sambuc 	}
93084d9c625SLionel Sambuc 	if (LF_ISSET(SEARCH_CSUBST) && F_ISSET(sp, SC_RE_SUBST)) {
93184d9c625SLionel Sambuc 		regfree(&sp->subre_c);
93284d9c625SLionel Sambuc 		F_CLR(sp, SC_RE_SUBST);
93384d9c625SLionel Sambuc 	}
93484d9c625SLionel Sambuc 
93584d9c625SLionel Sambuc 	/*
93684d9c625SLionel Sambuc 	 * If we're saving the string, it's a pattern we haven't seen before,
93784d9c625SLionel Sambuc 	 * so convert the vi-style RE's to POSIX 1003.2 RE's.  Save a copy for
93884d9c625SLionel Sambuc 	 * later recompilation.   Free any previously saved value.
93984d9c625SLionel Sambuc 	 */
94084d9c625SLionel Sambuc 	if (ptrnp != NULL) {
94184d9c625SLionel Sambuc 		replaced = 0;
94284d9c625SLionel Sambuc 		if (LF_ISSET(SEARCH_CSCOPE)) {
94384d9c625SLionel Sambuc 			if (re_cscope_conv(sp, &ptrn, &plen, &replaced))
94484d9c625SLionel Sambuc 				return (1);
94584d9c625SLionel Sambuc 			/*
94684d9c625SLionel Sambuc 			 * XXX
94784d9c625SLionel Sambuc 			 * Currently, the match-any-<blank> expression used in
94884d9c625SLionel Sambuc 			 * re_cscope_conv() requires extended RE's.  This may
94984d9c625SLionel Sambuc 			 * not be right or safe.
95084d9c625SLionel Sambuc 			 */
95184d9c625SLionel Sambuc 			reflags |= REG_EXTENDED;
95284d9c625SLionel Sambuc 		} else if (LF_ISSET(SEARCH_TAG)) {
95384d9c625SLionel Sambuc 			if (re_tag_conv(sp, &ptrn, &plen, &replaced))
95484d9c625SLionel Sambuc 				return (1);
95584d9c625SLionel Sambuc 		} else if (!LF_ISSET(SEARCH_LITERAL))
95684d9c625SLionel Sambuc 			if (re_conv(sp, &ptrn, &plen, &replaced))
95784d9c625SLionel Sambuc 				return (1);
95884d9c625SLionel Sambuc 
95984d9c625SLionel Sambuc 		/* Discard previous pattern. */
96084d9c625SLionel Sambuc 		if (*ptrnp != NULL) {
96184d9c625SLionel Sambuc 			free(*ptrnp);
96284d9c625SLionel Sambuc 			*ptrnp = NULL;
96384d9c625SLionel Sambuc 		}
96484d9c625SLionel Sambuc 		if (lenp != NULL)
96584d9c625SLionel Sambuc 			*lenp = plen;
96684d9c625SLionel Sambuc 
96784d9c625SLionel Sambuc 		/*
96884d9c625SLionel Sambuc 		 * Copy the string into allocated memory.
96984d9c625SLionel Sambuc 		 *
97084d9c625SLionel Sambuc 		 * XXX
97184d9c625SLionel Sambuc 		 * Regcomp isn't 8-bit clean, so the pattern is nul-terminated
97284d9c625SLionel Sambuc 		 * for now.  There's just no other solution.
97384d9c625SLionel Sambuc 		 */
97484d9c625SLionel Sambuc 		MALLOC(sp, *ptrnp, CHAR_T *, (plen + 1) * sizeof(CHAR_T));
97584d9c625SLionel Sambuc 		if (*ptrnp != NULL) {
97684d9c625SLionel Sambuc 			MEMCPYW(*ptrnp, ptrn, plen);
97784d9c625SLionel Sambuc 			(*ptrnp)[plen] = '\0';
97884d9c625SLionel Sambuc 		}
97984d9c625SLionel Sambuc 
98084d9c625SLionel Sambuc 		/* Free up conversion-routine-allocated memory. */
98184d9c625SLionel Sambuc 		if (replaced)
98284d9c625SLionel Sambuc 			FREE_SPACEW(sp, ptrn, 0);
98384d9c625SLionel Sambuc 
98484d9c625SLionel Sambuc 		if (*ptrnp == NULL)
98584d9c625SLionel Sambuc 			return (1);
98684d9c625SLionel Sambuc 
98784d9c625SLionel Sambuc 		ptrn = *ptrnp;
98884d9c625SLionel Sambuc 	}
98984d9c625SLionel Sambuc 
99084d9c625SLionel Sambuc 	/*
99184d9c625SLionel Sambuc 	 * XXX
99284d9c625SLionel Sambuc 	 * Regcomp isn't 8-bit clean, so we just lost if the pattern
99384d9c625SLionel Sambuc 	 * contained a nul.  Bummer!
99484d9c625SLionel Sambuc 	 */
99584d9c625SLionel Sambuc 	if ((rval = regcomp(rep, ptrn, /* plen, */ reflags)) != 0) {
99684d9c625SLionel Sambuc 		if (LF_ISSET(SEARCH_MSG))
99784d9c625SLionel Sambuc 			re_error(sp, rval, rep);
99884d9c625SLionel Sambuc 		return (1);
99984d9c625SLionel Sambuc 	}
100084d9c625SLionel Sambuc 
100184d9c625SLionel Sambuc 	if (LF_ISSET(SEARCH_CSEARCH))
100284d9c625SLionel Sambuc 		F_SET(sp, SC_RE_SEARCH);
100384d9c625SLionel Sambuc 	if (LF_ISSET(SEARCH_CSUBST))
100484d9c625SLionel Sambuc 		F_SET(sp, SC_RE_SUBST);
100584d9c625SLionel Sambuc 
100684d9c625SLionel Sambuc 	return (0);
100784d9c625SLionel Sambuc }
100884d9c625SLionel Sambuc 
100984d9c625SLionel Sambuc /*
101084d9c625SLionel Sambuc  * re_conv --
101184d9c625SLionel Sambuc  *	Convert vi's regular expressions into something that the
101284d9c625SLionel Sambuc  *	the POSIX 1003.2 RE functions can handle.
101384d9c625SLionel Sambuc  *
101484d9c625SLionel Sambuc  * There are three conversions we make to make vi's RE's (specifically
101584d9c625SLionel Sambuc  * the global, search, and substitute patterns) work with POSIX RE's.
101684d9c625SLionel Sambuc  *
101784d9c625SLionel Sambuc  * 1: If O_MAGIC is not set, strip backslashes from the magic character
101884d9c625SLionel Sambuc  *    set (.[*~) that have them, and add them to the ones that don't.
101984d9c625SLionel Sambuc  * 2: If O_MAGIC is not set, the string "\~" is replaced with the text
102084d9c625SLionel Sambuc  *    from the last substitute command's replacement string.  If O_MAGIC
102184d9c625SLionel Sambuc  *    is set, it's the string "~".
102284d9c625SLionel Sambuc  * 3: The pattern \<ptrn\> does "word" searches, convert it to use the
102384d9c625SLionel Sambuc  *    new RE escapes.
102484d9c625SLionel Sambuc  *
102584d9c625SLionel Sambuc  * !!!/XXX
102684d9c625SLionel Sambuc  * This doesn't exactly match the historic behavior of vi because we do
102784d9c625SLionel Sambuc  * the ~ substitution before calling the RE engine, so magic characters
102884d9c625SLionel Sambuc  * in the replacement string will be expanded by the RE engine, and they
102984d9c625SLionel Sambuc  * weren't historically.  It's a bug.
103084d9c625SLionel Sambuc  */
103184d9c625SLionel Sambuc static int
re_conv(SCR * sp,CHAR_T ** ptrnp,size_t * plenp,int * replacedp)103284d9c625SLionel Sambuc re_conv(SCR *sp, CHAR_T **ptrnp, size_t *plenp, int *replacedp)
103384d9c625SLionel Sambuc {
103484d9c625SLionel Sambuc 	size_t blen, len, needlen;
103584d9c625SLionel Sambuc 	int magic;
103684d9c625SLionel Sambuc 	CHAR_T *bp, *p, *t;
103784d9c625SLionel Sambuc 
103884d9c625SLionel Sambuc 	/*
103984d9c625SLionel Sambuc 	 * First pass through, we figure out how much space we'll need.
104084d9c625SLionel Sambuc 	 * We do it in two passes, on the grounds that most of the time
104184d9c625SLionel Sambuc 	 * the user is doing a search and won't have magic characters.
104284d9c625SLionel Sambuc 	 * That way we can skip most of the memory allocation and copies.
104384d9c625SLionel Sambuc 	 */
104484d9c625SLionel Sambuc 	magic = 0;
104584d9c625SLionel Sambuc 	for (p = *ptrnp, len = *plenp, needlen = 0; len > 0; ++p, --len)
104684d9c625SLionel Sambuc 		switch (*p) {
104784d9c625SLionel Sambuc 		case '\\':
104884d9c625SLionel Sambuc 			if (len > 1) {
104984d9c625SLionel Sambuc 				--len;
105084d9c625SLionel Sambuc 				switch (*++p) {
105184d9c625SLionel Sambuc 				case '<':
105284d9c625SLionel Sambuc 					magic = 1;
105384d9c625SLionel Sambuc 					needlen += RE_WSTART_LEN + 1;
105484d9c625SLionel Sambuc 					break;
105584d9c625SLionel Sambuc 				case '>':
105684d9c625SLionel Sambuc 					magic = 1;
105784d9c625SLionel Sambuc 					needlen += RE_WSTOP_LEN + 1;
105884d9c625SLionel Sambuc 					break;
105984d9c625SLionel Sambuc 				case '~':
106084d9c625SLionel Sambuc 					if (!O_ISSET(sp, O_MAGIC)) {
106184d9c625SLionel Sambuc 						magic = 1;
106284d9c625SLionel Sambuc 						needlen += sp->repl_len;
106384d9c625SLionel Sambuc 					}
106484d9c625SLionel Sambuc 					break;
106584d9c625SLionel Sambuc 				case '.':
106684d9c625SLionel Sambuc 				case '[':
106784d9c625SLionel Sambuc 				case '*':
106884d9c625SLionel Sambuc 					if (!O_ISSET(sp, O_MAGIC)) {
106984d9c625SLionel Sambuc 						magic = 1;
107084d9c625SLionel Sambuc 						needlen += 1;
107184d9c625SLionel Sambuc 					}
107284d9c625SLionel Sambuc 					break;
107384d9c625SLionel Sambuc 				default:
107484d9c625SLionel Sambuc 					needlen += 2;
107584d9c625SLionel Sambuc 				}
107684d9c625SLionel Sambuc 			} else
107784d9c625SLionel Sambuc 				needlen += 1;
107884d9c625SLionel Sambuc 			break;
107984d9c625SLionel Sambuc 		case '~':
108084d9c625SLionel Sambuc 			if (O_ISSET(sp, O_MAGIC)) {
108184d9c625SLionel Sambuc 				magic = 1;
108284d9c625SLionel Sambuc 				needlen += sp->repl_len;
108384d9c625SLionel Sambuc 			}
108484d9c625SLionel Sambuc 			break;
108584d9c625SLionel Sambuc 		case '.':
108684d9c625SLionel Sambuc 		case '[':
108784d9c625SLionel Sambuc 		case '*':
108884d9c625SLionel Sambuc 			if (!O_ISSET(sp, O_MAGIC)) {
108984d9c625SLionel Sambuc 				magic = 1;
109084d9c625SLionel Sambuc 				needlen += 2;
109184d9c625SLionel Sambuc 			}
109284d9c625SLionel Sambuc 			break;
109384d9c625SLionel Sambuc 		default:
109484d9c625SLionel Sambuc 			needlen += 1;
109584d9c625SLionel Sambuc 			break;
109684d9c625SLionel Sambuc 		}
109784d9c625SLionel Sambuc 
109884d9c625SLionel Sambuc 	if (!magic) {
109984d9c625SLionel Sambuc 		*replacedp = 0;
110084d9c625SLionel Sambuc 		return (0);
110184d9c625SLionel Sambuc 	}
110284d9c625SLionel Sambuc 
110384d9c625SLionel Sambuc 	/* Get enough memory to hold the final pattern. */
110484d9c625SLionel Sambuc 	*replacedp = 1;
110584d9c625SLionel Sambuc 	GET_SPACE_RETW(sp, bp, blen, needlen);
110684d9c625SLionel Sambuc 
110784d9c625SLionel Sambuc 	for (p = *ptrnp, len = *plenp, t = bp; len > 0; ++p, --len)
110884d9c625SLionel Sambuc 		switch (*p) {
110984d9c625SLionel Sambuc 		case '\\':
111084d9c625SLionel Sambuc 			if (len > 1) {
111184d9c625SLionel Sambuc 				--len;
111284d9c625SLionel Sambuc 				switch (*++p) {
111384d9c625SLionel Sambuc 				case '<':
111484d9c625SLionel Sambuc 					MEMCPY(t,
111584d9c625SLionel Sambuc 					    RE_WSTART, RE_WSTART_LEN);
111684d9c625SLionel Sambuc 					t += RE_WSTART_LEN;
111784d9c625SLionel Sambuc 					break;
111884d9c625SLionel Sambuc 				case '>':
111984d9c625SLionel Sambuc 					MEMCPY(t,
112084d9c625SLionel Sambuc 					    RE_WSTOP, RE_WSTOP_LEN);
112184d9c625SLionel Sambuc 					t += RE_WSTOP_LEN;
112284d9c625SLionel Sambuc 					break;
112384d9c625SLionel Sambuc 				case '~':
112484d9c625SLionel Sambuc 					if (O_ISSET(sp, O_MAGIC))
112584d9c625SLionel Sambuc 						*t++ = '~';
112684d9c625SLionel Sambuc 					else {
112784d9c625SLionel Sambuc 						MEMCPYW(t,
112884d9c625SLionel Sambuc 						    sp->repl, sp->repl_len);
112984d9c625SLionel Sambuc 						t += sp->repl_len;
113084d9c625SLionel Sambuc 					}
113184d9c625SLionel Sambuc 					break;
113284d9c625SLionel Sambuc 				case '.':
113384d9c625SLionel Sambuc 				case '[':
113484d9c625SLionel Sambuc 				case '*':
113584d9c625SLionel Sambuc 					if (O_ISSET(sp, O_MAGIC))
113684d9c625SLionel Sambuc 						*t++ = '\\';
113784d9c625SLionel Sambuc 					*t++ = *p;
113884d9c625SLionel Sambuc 					break;
113984d9c625SLionel Sambuc 				default:
114084d9c625SLionel Sambuc 					*t++ = '\\';
114184d9c625SLionel Sambuc 					*t++ = *p;
114284d9c625SLionel Sambuc 				}
114384d9c625SLionel Sambuc 			} else
114484d9c625SLionel Sambuc 				*t++ = '\\';
114584d9c625SLionel Sambuc 			break;
114684d9c625SLionel Sambuc 		case '~':
114784d9c625SLionel Sambuc 			if (O_ISSET(sp, O_MAGIC)) {
114884d9c625SLionel Sambuc 				MEMCPYW(t, sp->repl, sp->repl_len);
114984d9c625SLionel Sambuc 				t += sp->repl_len;
115084d9c625SLionel Sambuc 			} else
115184d9c625SLionel Sambuc 				*t++ = '~';
115284d9c625SLionel Sambuc 			break;
115384d9c625SLionel Sambuc 		case '.':
115484d9c625SLionel Sambuc 		case '[':
115584d9c625SLionel Sambuc 		case '*':
115684d9c625SLionel Sambuc 			if (!O_ISSET(sp, O_MAGIC))
115784d9c625SLionel Sambuc 				*t++ = '\\';
115884d9c625SLionel Sambuc 			*t++ = *p;
115984d9c625SLionel Sambuc 			break;
116084d9c625SLionel Sambuc 		default:
116184d9c625SLionel Sambuc 			*t++ = *p;
116284d9c625SLionel Sambuc 			break;
116384d9c625SLionel Sambuc 		}
116484d9c625SLionel Sambuc 
116584d9c625SLionel Sambuc 	*ptrnp = bp;
116684d9c625SLionel Sambuc 	*plenp = t - bp;
116784d9c625SLionel Sambuc 	return (0);
116884d9c625SLionel Sambuc }
116984d9c625SLionel Sambuc 
117084d9c625SLionel Sambuc /*
117184d9c625SLionel Sambuc  * re_tag_conv --
117284d9c625SLionel Sambuc  *	Convert a tags search path into something that the POSIX
117384d9c625SLionel Sambuc  *	1003.2 RE functions can handle.
117484d9c625SLionel Sambuc  */
117584d9c625SLionel Sambuc static int
re_tag_conv(SCR * sp,CHAR_T ** ptrnp,size_t * plenp,int * replacedp)117684d9c625SLionel Sambuc re_tag_conv(SCR *sp, CHAR_T **ptrnp, size_t *plenp, int *replacedp)
117784d9c625SLionel Sambuc {
117884d9c625SLionel Sambuc 	size_t blen, len;
117984d9c625SLionel Sambuc 	int lastdollar;
118084d9c625SLionel Sambuc 	CHAR_T *bp, *p, *t;
118184d9c625SLionel Sambuc 
118284d9c625SLionel Sambuc 	len = *plenp;
118384d9c625SLionel Sambuc 
118484d9c625SLionel Sambuc 	/* Max memory usage is 2 times the length of the string. */
118584d9c625SLionel Sambuc 	*replacedp = 1;
118684d9c625SLionel Sambuc 	GET_SPACE_RETW(sp, bp, blen, len * 2);
118784d9c625SLionel Sambuc 
118884d9c625SLionel Sambuc 	p = *ptrnp;
118984d9c625SLionel Sambuc 	t = bp;
119084d9c625SLionel Sambuc 
119184d9c625SLionel Sambuc 	/* If the last character is a '/' or '?', we just strip it. */
119284d9c625SLionel Sambuc 	if (len > 0 && (p[len - 1] == '/' || p[len - 1] == '?'))
119384d9c625SLionel Sambuc 		--len;
119484d9c625SLionel Sambuc 
119584d9c625SLionel Sambuc 	/* If the next-to-last or last character is a '$', it's magic. */
119684d9c625SLionel Sambuc 	if (len > 0 && p[len - 1] == '$') {
119784d9c625SLionel Sambuc 		--len;
119884d9c625SLionel Sambuc 		lastdollar = 1;
119984d9c625SLionel Sambuc 	} else
120084d9c625SLionel Sambuc 		lastdollar = 0;
120184d9c625SLionel Sambuc 
120284d9c625SLionel Sambuc 	/* If the first character is a '/' or '?', we just strip it. */
120384d9c625SLionel Sambuc 	if (len > 0 && (p[0] == '/' || p[0] == '?')) {
120484d9c625SLionel Sambuc 		++p;
120584d9c625SLionel Sambuc 		--len;
120684d9c625SLionel Sambuc 	}
120784d9c625SLionel Sambuc 
120884d9c625SLionel Sambuc 	/* If the first or second character is a '^', it's magic. */
120984d9c625SLionel Sambuc 	if (p[0] == '^') {
121084d9c625SLionel Sambuc 		*t++ = *p++;
121184d9c625SLionel Sambuc 		--len;
121284d9c625SLionel Sambuc 	}
121384d9c625SLionel Sambuc 
121484d9c625SLionel Sambuc 	/*
121584d9c625SLionel Sambuc 	 * Escape every other magic character we can find, meanwhile stripping
121684d9c625SLionel Sambuc 	 * the backslashes ctags inserts when escaping the search delimiter
121784d9c625SLionel Sambuc 	 * characters.
121884d9c625SLionel Sambuc 	 */
121984d9c625SLionel Sambuc 	for (; len > 0; --len) {
122084d9c625SLionel Sambuc 		if (p[0] == '\\' && (p[1] == '/' || p[1] == '?')) {
122184d9c625SLionel Sambuc 			++p;
122284d9c625SLionel Sambuc 			--len;
122384d9c625SLionel Sambuc 		} else if (strchr("^.[]$*", p[0]))
122484d9c625SLionel Sambuc 			*t++ = '\\';
122584d9c625SLionel Sambuc 		*t++ = *p++;
122684d9c625SLionel Sambuc 	}
122784d9c625SLionel Sambuc 	if (lastdollar)
122884d9c625SLionel Sambuc 		*t++ = '$';
122984d9c625SLionel Sambuc 
123084d9c625SLionel Sambuc 	*ptrnp = bp;
123184d9c625SLionel Sambuc 	*plenp = t - bp;
123284d9c625SLionel Sambuc 	return (0);
123384d9c625SLionel Sambuc }
123484d9c625SLionel Sambuc 
123584d9c625SLionel Sambuc /*
123684d9c625SLionel Sambuc  * re_cscope_conv --
123784d9c625SLionel Sambuc  *	 Convert a cscope search path into something that the POSIX
123884d9c625SLionel Sambuc  *      1003.2 RE functions can handle.
123984d9c625SLionel Sambuc  */
124084d9c625SLionel Sambuc static int
re_cscope_conv(SCR * sp,CHAR_T ** ptrnp,size_t * plenp,int * replacedp)124184d9c625SLionel Sambuc re_cscope_conv(SCR *sp, CHAR_T **ptrnp, size_t *plenp, int *replacedp)
124284d9c625SLionel Sambuc {
124384d9c625SLionel Sambuc 	size_t blen, len, nspaces;
124484d9c625SLionel Sambuc 	CHAR_T *bp, *t;
124584d9c625SLionel Sambuc 	CHAR_T *p;
124684d9c625SLionel Sambuc 	const CHAR_T *wp;
124784d9c625SLionel Sambuc 	size_t wlen;
124884d9c625SLionel Sambuc 
124984d9c625SLionel Sambuc 	/*
125084d9c625SLionel Sambuc 	 * Each space in the source line printed by cscope represents an
125184d9c625SLionel Sambuc 	 * arbitrary sequence of spaces, tabs, and comments.
125284d9c625SLionel Sambuc 	 */
125384d9c625SLionel Sambuc #define	CSCOPE_RE_SPACE		"([ \t]|/\\*([^*]|\\*/)*\\*/)*"
125484d9c625SLionel Sambuc #define CSCOPE_LEN	sizeof(CSCOPE_RE_SPACE) - 1
125584d9c625SLionel Sambuc 	CHAR2INT(sp, CSCOPE_RE_SPACE, CSCOPE_LEN, wp, wlen);
125684d9c625SLionel Sambuc 	for (nspaces = 0, p = *ptrnp, len = *plenp; len > 0; ++p, --len)
125784d9c625SLionel Sambuc 		if (*p == ' ')
125884d9c625SLionel Sambuc 			++nspaces;
125984d9c625SLionel Sambuc 
126084d9c625SLionel Sambuc 	/*
126184d9c625SLionel Sambuc 	 * Allocate plenty of space:
126284d9c625SLionel Sambuc 	 *	the string, plus potential escaping characters;
126384d9c625SLionel Sambuc 	 *	nspaces + 2 copies of CSCOPE_RE_SPACE;
126484d9c625SLionel Sambuc 	 *	^, $, nul terminator characters.
126584d9c625SLionel Sambuc 	 */
126684d9c625SLionel Sambuc 	*replacedp = 1;
126784d9c625SLionel Sambuc 	len = (p - *ptrnp) * 2 + (nspaces + 2) * sizeof(CSCOPE_RE_SPACE) + 3;
126884d9c625SLionel Sambuc 	GET_SPACE_RETW(sp, bp, blen, len);
126984d9c625SLionel Sambuc 
127084d9c625SLionel Sambuc 	p = *ptrnp;
127184d9c625SLionel Sambuc 	t = bp;
127284d9c625SLionel Sambuc 
127384d9c625SLionel Sambuc 	*t++ = '^';
127484d9c625SLionel Sambuc 	MEMCPYW(t, wp, wlen);
127584d9c625SLionel Sambuc 	t += wlen;
127684d9c625SLionel Sambuc 
127784d9c625SLionel Sambuc 	for (len = *plenp; len > 0; ++p, --len)
127884d9c625SLionel Sambuc 		if (*p == ' ') {
127984d9c625SLionel Sambuc 			MEMCPYW(t, wp, wlen);
128084d9c625SLionel Sambuc 			t += wlen;
128184d9c625SLionel Sambuc 		} else {
128284d9c625SLionel Sambuc 			if (strchr("\\^.[]$*+?()|{}", *p))
128384d9c625SLionel Sambuc 				*t++ = '\\';
128484d9c625SLionel Sambuc 			*t++ = *p;
128584d9c625SLionel Sambuc 		}
128684d9c625SLionel Sambuc 
128784d9c625SLionel Sambuc 	MEMCPYW(t, wp, wlen);
128884d9c625SLionel Sambuc 	t += wlen;
128984d9c625SLionel Sambuc 	*t++ = '$';
129084d9c625SLionel Sambuc 
129184d9c625SLionel Sambuc 	*ptrnp = bp;
129284d9c625SLionel Sambuc 	*plenp = t - bp;
129384d9c625SLionel Sambuc 	return (0);
129484d9c625SLionel Sambuc }
129584d9c625SLionel Sambuc 
129684d9c625SLionel Sambuc /*
129784d9c625SLionel Sambuc  * re_error --
129884d9c625SLionel Sambuc  *	Report a regular expression error.
129984d9c625SLionel Sambuc  *
130084d9c625SLionel Sambuc  * PUBLIC: void re_error __P((SCR *, int, regex_t *));
130184d9c625SLionel Sambuc  */
130284d9c625SLionel Sambuc void
re_error(SCR * sp,int errcode,regex_t * preg)130384d9c625SLionel Sambuc re_error(SCR *sp, int errcode, regex_t *preg)
130484d9c625SLionel Sambuc {
130584d9c625SLionel Sambuc 	size_t sz;
130684d9c625SLionel Sambuc 	char *oe;
130784d9c625SLionel Sambuc 
130884d9c625SLionel Sambuc 	sz = regerror(errcode, preg, NULL, 0);
130984d9c625SLionel Sambuc 	if ((oe = malloc(sz)) == NULL)
131084d9c625SLionel Sambuc 		msgq(sp, M_SYSERR, NULL);
131184d9c625SLionel Sambuc 	else {
131284d9c625SLionel Sambuc 		(void)regerror(errcode, preg, oe, sz);
131384d9c625SLionel Sambuc 		msgq(sp, M_ERR, "RE error: %s", oe);
131484d9c625SLionel Sambuc 		free(oe);
131584d9c625SLionel Sambuc 	}
131684d9c625SLionel Sambuc }
131784d9c625SLionel Sambuc 
131884d9c625SLionel Sambuc /*
131984d9c625SLionel Sambuc  * re_sub --
132084d9c625SLionel Sambuc  * 	Do the substitution for a regular expression.
132184d9c625SLionel Sambuc  */
132284d9c625SLionel Sambuc static int
re_sub(SCR * sp,CHAR_T * ip,CHAR_T ** lbp,size_t * lbclenp,size_t * lblenp,regmatch_t * match)132384d9c625SLionel Sambuc re_sub(SCR *sp, CHAR_T *ip, CHAR_T **lbp, size_t *lbclenp, size_t *lblenp, regmatch_t *match)
132484d9c625SLionel Sambuc 
132584d9c625SLionel Sambuc 	           			/* Input line. */
132684d9c625SLionel Sambuc 
132784d9c625SLionel Sambuc 
132884d9c625SLionel Sambuc 
132984d9c625SLionel Sambuc {
133084d9c625SLionel Sambuc 	enum { C_NOT_SET, C_LOWER, C_ONE_LOWER, C_ONE_UPPER, C_UPPER } conv;
133184d9c625SLionel Sambuc 	size_t lbclen, lblen;		/* Local copies. */
133284d9c625SLionel Sambuc 	size_t mlen;			/* Match length. */
133384d9c625SLionel Sambuc 	size_t rpl;			/* Remaining replacement length. */
133484d9c625SLionel Sambuc 	CHAR_T *rp;			/* Replacement pointer. */
133584d9c625SLionel Sambuc 	int ch;
133684d9c625SLionel Sambuc 	int no;				/* Match replacement offset. */
133784d9c625SLionel Sambuc 	CHAR_T *p, *t;			/* Buffer pointers. */
133884d9c625SLionel Sambuc 	CHAR_T *lb;			/* Local copies. */
133984d9c625SLionel Sambuc 
134084d9c625SLionel Sambuc 	lb = *lbp;			/* Get local copies. */
134184d9c625SLionel Sambuc 	lbclen = *lbclenp;
134284d9c625SLionel Sambuc 	lblen = *lblenp;
134384d9c625SLionel Sambuc 
134484d9c625SLionel Sambuc 	/*
134584d9c625SLionel Sambuc 	 * QUOTING NOTE:
134684d9c625SLionel Sambuc 	 *
134784d9c625SLionel Sambuc 	 * There are some special sequences that vi provides in the
134884d9c625SLionel Sambuc 	 * replacement patterns.
134984d9c625SLionel Sambuc 	 *	 & string the RE matched (\& if nomagic set)
135084d9c625SLionel Sambuc 	 *	\# n-th regular subexpression
135184d9c625SLionel Sambuc 	 *	\E end \U, \L conversion
135284d9c625SLionel Sambuc 	 *	\e end \U, \L conversion
135384d9c625SLionel Sambuc 	 *	\l convert the next character to lower-case
135484d9c625SLionel Sambuc 	 *	\L convert to lower-case, until \E, \e, or end of replacement
135584d9c625SLionel Sambuc 	 *	\u convert the next character to upper-case
135684d9c625SLionel Sambuc 	 *	\U convert to upper-case, until \E, \e, or end of replacement
135784d9c625SLionel Sambuc 	 *
135884d9c625SLionel Sambuc 	 * Otherwise, since this is the lowest level of replacement, discard
135984d9c625SLionel Sambuc 	 * all escaping characters.  This (hopefully) matches historic practice.
136084d9c625SLionel Sambuc 	 */
136184d9c625SLionel Sambuc #define	OUTCH(ch, nltrans) {						\
136284d9c625SLionel Sambuc 	ARG_CHAR_T __ch = (ch);						\
136384d9c625SLionel Sambuc 	e_key_t __value = KEY_VAL(sp, __ch);				\
136484d9c625SLionel Sambuc 	if (nltrans && (__value == K_CR || __value == K_NL)) {		\
136584d9c625SLionel Sambuc 		NEEDNEWLINE(sp);					\
136684d9c625SLionel Sambuc 		sp->newl[sp->newl_cnt++] = lbclen;			\
136784d9c625SLionel Sambuc 	} else if (conv != C_NOT_SET) {					\
136884d9c625SLionel Sambuc 		switch (conv) {						\
136984d9c625SLionel Sambuc 		case C_ONE_LOWER:					\
137084d9c625SLionel Sambuc 			conv = C_NOT_SET;				\
137184d9c625SLionel Sambuc 			/* FALLTHROUGH */				\
137284d9c625SLionel Sambuc 		case C_LOWER:						\
137384d9c625SLionel Sambuc 			if (ISUPPER(__ch))				\
137484d9c625SLionel Sambuc 				__ch = TOLOWER(__ch);			\
137584d9c625SLionel Sambuc 			break;						\
137684d9c625SLionel Sambuc 		case C_ONE_UPPER:					\
137784d9c625SLionel Sambuc 			conv = C_NOT_SET;				\
137884d9c625SLionel Sambuc 			/* FALLTHROUGH */				\
137984d9c625SLionel Sambuc 		case C_UPPER:						\
138084d9c625SLionel Sambuc 			if (ISLOWER(__ch))				\
138184d9c625SLionel Sambuc 				__ch = TOUPPER(__ch);			\
138284d9c625SLionel Sambuc 			break;						\
138384d9c625SLionel Sambuc 		default:						\
138484d9c625SLionel Sambuc 			abort();					\
138584d9c625SLionel Sambuc 		}							\
138684d9c625SLionel Sambuc 	}								\
138784d9c625SLionel Sambuc 	NEEDSP(sp, 1, p);						\
138884d9c625SLionel Sambuc 	*p++ = __ch;							\
138984d9c625SLionel Sambuc 	++lbclen;							\
139084d9c625SLionel Sambuc }
139184d9c625SLionel Sambuc 	conv = C_NOT_SET;
139284d9c625SLionel Sambuc 	for (rp = sp->repl, rpl = sp->repl_len, p = lb + lbclen; rpl--;) {
139384d9c625SLionel Sambuc 		switch (ch = *rp++) {
139484d9c625SLionel Sambuc 		case '&':
139584d9c625SLionel Sambuc 			if (O_ISSET(sp, O_MAGIC)) {
139684d9c625SLionel Sambuc 				no = 0;
139784d9c625SLionel Sambuc 				goto subzero;
139884d9c625SLionel Sambuc 			}
139984d9c625SLionel Sambuc 			break;
140084d9c625SLionel Sambuc 		case '\\':
140184d9c625SLionel Sambuc 			if (rpl == 0)
140284d9c625SLionel Sambuc 				break;
140384d9c625SLionel Sambuc 			--rpl;
140484d9c625SLionel Sambuc 			switch (ch = *rp) {
140584d9c625SLionel Sambuc 			case '&':
140684d9c625SLionel Sambuc 				++rp;
140784d9c625SLionel Sambuc 				if (!O_ISSET(sp, O_MAGIC)) {
140884d9c625SLionel Sambuc 					no = 0;
140984d9c625SLionel Sambuc 					goto subzero;
141084d9c625SLionel Sambuc 				}
141184d9c625SLionel Sambuc 				break;
141284d9c625SLionel Sambuc 			case '0': case '1': case '2': case '3': case '4':
141384d9c625SLionel Sambuc 			case '5': case '6': case '7': case '8': case '9':
141484d9c625SLionel Sambuc 				no = *rp++ - '0';
141584d9c625SLionel Sambuc subzero:			if (match[no].rm_so == -1 ||
141684d9c625SLionel Sambuc 			    	    match[no].rm_eo == -1)
141784d9c625SLionel Sambuc 					break;
141884d9c625SLionel Sambuc 				mlen = match[no].rm_eo - match[no].rm_so;
141984d9c625SLionel Sambuc 				for (t = ip + match[no].rm_so; mlen--; ++t)
142084d9c625SLionel Sambuc 					OUTCH((UCHAR_T)*t, 0);
142184d9c625SLionel Sambuc 				continue;
142284d9c625SLionel Sambuc 			case 'e':
142384d9c625SLionel Sambuc 			case 'E':
142484d9c625SLionel Sambuc 				++rp;
142584d9c625SLionel Sambuc 				conv = C_NOT_SET;
142684d9c625SLionel Sambuc 				continue;
142784d9c625SLionel Sambuc 			case 'l':
142884d9c625SLionel Sambuc 				++rp;
142984d9c625SLionel Sambuc 				conv = C_ONE_LOWER;
143084d9c625SLionel Sambuc 				continue;
143184d9c625SLionel Sambuc 			case 'L':
143284d9c625SLionel Sambuc 				++rp;
143384d9c625SLionel Sambuc 				conv = C_LOWER;
143484d9c625SLionel Sambuc 				continue;
143584d9c625SLionel Sambuc 			case 'u':
143684d9c625SLionel Sambuc 				++rp;
143784d9c625SLionel Sambuc 				conv = C_ONE_UPPER;
143884d9c625SLionel Sambuc 				continue;
143984d9c625SLionel Sambuc 			case 'U':
144084d9c625SLionel Sambuc 				++rp;
144184d9c625SLionel Sambuc 				conv = C_UPPER;
144284d9c625SLionel Sambuc 				continue;
144384d9c625SLionel Sambuc 			default:
144484d9c625SLionel Sambuc 				++rp;
144584d9c625SLionel Sambuc 				break;
144684d9c625SLionel Sambuc 			}
144784d9c625SLionel Sambuc 		}
144884d9c625SLionel Sambuc 		OUTCH(ch, 1);
144984d9c625SLionel Sambuc 	}
145084d9c625SLionel Sambuc 
145184d9c625SLionel Sambuc 	*lbp = lb;			/* Update caller's information. */
145284d9c625SLionel Sambuc 	*lbclenp = lbclen;
145384d9c625SLionel Sambuc 	*lblenp = lblen;
145484d9c625SLionel Sambuc 	return (0);
145584d9c625SLionel Sambuc }
1456