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