119304Speter/*-
219304Speter * Copyright (c) 1992, 1993, 1994
319304Speter *	The Regents of the University of California.  All rights reserved.
419304Speter * Copyright (c) 1992, 1993, 1994, 1995, 1996
519304Speter *	Keith Bostic.  All rights reserved.
619304Speter *
719304Speter * See the LICENSE file for redistribution information.
819304Speter */
919304Speter
1019304Speter#include "config.h"
1119304Speter
1219304Speter#ifndef lint
13254225Speterstatic const char sccsid[] = "$Id: ex_global.c,v 10.32 2011/12/26 23:37:01 zy Exp $";
1419304Speter#endif /* not lint */
1519304Speter
1619304Speter#include <sys/types.h>
1719304Speter#include <sys/queue.h>
18254225Speter#include <sys/time.h>
1919304Speter
2019304Speter#include <bitstring.h>
2119304Speter#include <ctype.h>
2219304Speter#include <errno.h>
2319304Speter#include <limits.h>
2419304Speter#include <stdio.h>
2519304Speter#include <stdlib.h>
2619304Speter#include <string.h>
2719304Speter#include <unistd.h>
2819304Speter
2919304Speter#include "../common/common.h"
3019304Speter
3119304Speterenum which {GLOBAL, V};
3219304Speter
3319304Speterstatic int ex_g_setup __P((SCR *, EXCMD *, enum which));
3419304Speter
3519304Speter/*
3619304Speter * ex_global -- [line [,line]] g[lobal][!] /pattern/ [commands]
3719304Speter *	Exec on lines matching a pattern.
3819304Speter *
3919304Speter * PUBLIC: int ex_global __P((SCR *, EXCMD *));
4019304Speter */
4119304Speterint
42254225Speterex_global(SCR *sp, EXCMD *cmdp)
4319304Speter{
4419304Speter	return (ex_g_setup(sp,
4519304Speter	    cmdp, FL_ISSET(cmdp->iflags, E_C_FORCE) ? V : GLOBAL));
4619304Speter}
4719304Speter
4819304Speter/*
4919304Speter * ex_v -- [line [,line]] v /pattern/ [commands]
5019304Speter *	Exec on lines not matching a pattern.
5119304Speter *
5219304Speter * PUBLIC: int ex_v __P((SCR *, EXCMD *));
5319304Speter */
5419304Speterint
55254225Speterex_v(SCR *sp, EXCMD *cmdp)
5619304Speter{
5719304Speter	return (ex_g_setup(sp, cmdp, V));
5819304Speter}
5919304Speter
6019304Speter/*
6119304Speter * ex_g_setup --
6219304Speter *	Ex global and v commands.
6319304Speter */
6419304Speterstatic int
65254225Speterex_g_setup(SCR *sp, EXCMD *cmdp, enum which cmd)
6619304Speter{
6719304Speter	CHAR_T *ptrn, *p, *t;
6819304Speter	EXCMD *ecp;
6919304Speter	MARK abs;
7019304Speter	RANGE *rp;
7119304Speter	busy_t btype;
7219304Speter	recno_t start, end;
7319304Speter	regex_t *re;
7419304Speter	regmatch_t match[1];
7519304Speter	size_t len;
7619304Speter	int cnt, delim, eval;
77254225Speter	CHAR_T *dbp;
7819304Speter
7919304Speter	NEEDFILE(sp, cmdp);
8019304Speter
8119304Speter	if (F_ISSET(sp, SC_EX_GLOBAL)) {
82254225Speter		msgq_wstr(sp, M_ERR, cmdp->cmd->name,
83254225Speter	"124|The %s command can't be used as part of a global or v command");
8419304Speter		return (1);
8519304Speter	}
8619304Speter
8719304Speter	/*
8819304Speter	 * Skip leading white space.  Historic vi allowed any non-alphanumeric
8919304Speter	 * to serve as the global command delimiter.
9019304Speter	 */
9119304Speter	if (cmdp->argc == 0)
9219304Speter		goto usage;
93254225Speter	for (p = cmdp->argv[0]->bp; cmdskip(*p); ++p);
94254225Speter	if (!isascii(*p) || *p == '\0' || isalnum(*p) ||
9519304Speter	    *p == '\\' || *p == '|' || *p == '\n') {
9619304Speterusage:		ex_emsg(sp, cmdp->cmd->usage, EXM_USAGE);
9719304Speter		return (1);
9819304Speter	}
9919304Speter	delim = *p++;
10019304Speter
10119304Speter	/*
10219304Speter	 * Get the pattern string, toss escaped characters.
10319304Speter	 *
10419304Speter	 * QUOTING NOTE:
10519304Speter	 * Only toss an escaped character if it escapes a delimiter.
10619304Speter	 */
10719304Speter	for (ptrn = t = p;;) {
10819304Speter		if (p[0] == '\0' || p[0] == delim) {
10919304Speter			if (p[0] == delim)
11019304Speter				++p;
11119304Speter			/*
11219304Speter			 * !!!
11319304Speter			 * Nul terminate the pattern string -- it's passed
11419304Speter			 * to regcomp which doesn't understand anything else.
11519304Speter			 */
11619304Speter			*t = '\0';
11719304Speter			break;
11819304Speter		}
11919304Speter		if (p[0] == '\\')
12019304Speter			if (p[1] == delim)
12119304Speter				++p;
12219304Speter			else if (p[1] == '\\')
12319304Speter				*t++ = *p++;
12419304Speter		*t++ = *p++;
12519304Speter	}
12619304Speter
12719304Speter	/* If the pattern string is empty, use the last one. */
12819304Speter	if (*ptrn == '\0') {
12919304Speter		if (sp->re == NULL) {
13019304Speter			ex_emsg(sp, NULL, EXM_NOPREVRE);
13119304Speter			return (1);
13219304Speter		}
13319304Speter
13419304Speter		/* Re-compile the RE if necessary. */
135254225Speter		if (!F_ISSET(sp, SC_RE_SEARCH) &&
136254225Speter		    re_compile(sp, sp->re, sp->re_len,
137254225Speter		    NULL, NULL, &sp->re_c, RE_C_SEARCH))
13819304Speter			return (1);
13919304Speter	} else {
14019304Speter		/* Compile the RE. */
141254225Speter		if (re_compile(sp, ptrn, t - ptrn, &sp->re,
142254225Speter		    &sp->re_len, &sp->re_c, RE_C_SEARCH))
14319304Speter			return (1);
14419304Speter
14519304Speter		/*
14619304Speter		 * Set saved RE.  Historic practice is that globals set
14719304Speter		 * direction as well as the RE.
14819304Speter		 */
14919304Speter		sp->searchdir = FORWARD;
15019304Speter	}
15119304Speter	re = &sp->re_c;
15219304Speter
15319304Speter	/* The global commands always set the previous context mark. */
15419304Speter	abs.lno = sp->lno;
15519304Speter	abs.cno = sp->cno;
15619304Speter	if (mark_set(sp, ABSMARK1, &abs, 1))
15719304Speter		return (1);
15819304Speter
15919304Speter	/* Get an EXCMD structure. */
16019304Speter	CALLOC_RET(sp, ecp, EXCMD *, 1, sizeof(EXCMD));
161254225Speter	TAILQ_INIT(ecp->rq);
16219304Speter
16319304Speter	/*
16419304Speter	 * Get a copy of the command string; the default command is print.
16519304Speter	 * Don't worry about a set of <blank>s with no command, that will
16619304Speter	 * default to print in the ex parser.  We need to have two copies
16719304Speter	 * because the ex parser may step on the command string when it's
16819304Speter	 * parsing it.
16919304Speter	 */
17019304Speter	if ((len = cmdp->argv[0]->len - (p - cmdp->argv[0]->bp)) == 0) {
171254225Speter		p = L("p");
17219304Speter		len = 1;
17319304Speter	}
17419304Speter
175254225Speter	MALLOC_RET(sp, ecp->cp, CHAR_T *, (len * 2) * sizeof(CHAR_T));
17619304Speter	ecp->o_cp = ecp->cp;
17719304Speter	ecp->o_clen = len;
178254225Speter	MEMCPY(ecp->cp + len, p, len);
17919304Speter	ecp->range_lno = OOBLNO;
18019304Speter	FL_SET(ecp->agv_flags, cmd == GLOBAL ? AGV_GLOBAL : AGV_V);
181254225Speter	SLIST_INSERT_HEAD(sp->gp->ecq, ecp, q);
18219304Speter
18319304Speter	/*
18419304Speter	 * For each line...  The semantics of global matching are that we first
18519304Speter	 * have to decide which lines are going to get passed to the command,
18619304Speter	 * and then pass them to the command, ignoring other changes.  There's
18719304Speter	 * really no way to do this in a single pass, since arbitrary line
18819304Speter	 * creation, deletion and movement can be done in the ex command.  For
18919304Speter	 * example, a good vi clone test is ":g/X/mo.-3", or "g/X/.,.+1d".
19019304Speter	 * What we do is create linked list of lines that are tracked through
19119304Speter	 * each ex command.  There's a callback routine which the DB interface
19219304Speter	 * routines call when a line is created or deleted.  This doesn't help
19319304Speter	 * the layering much.
19419304Speter	 */
19519304Speter	btype = BUSY_ON;
19619304Speter	cnt = INTERRUPT_CHECK;
19719304Speter	for (start = cmdp->addr1.lno,
19819304Speter	    end = cmdp->addr2.lno; start <= end; ++start) {
19919304Speter		if (cnt-- == 0) {
20019304Speter			if (INTERRUPTED(sp)) {
201254225Speter				SLIST_REMOVE_HEAD(sp->gp->ecq, q);
20219304Speter				free(ecp->cp);
20319304Speter				free(ecp);
20419304Speter				break;
20519304Speter			}
20619304Speter			search_busy(sp, btype);
20719304Speter			btype = BUSY_UPDATE;
20819304Speter			cnt = INTERRUPT_CHECK;
20919304Speter		}
21019304Speter		if (db_get(sp, start, DBG_FATAL, &dbp, &len))
21119304Speter			return (1);
21219304Speter		match[0].rm_so = 0;
21319304Speter		match[0].rm_eo = len;
21419304Speter		switch (eval =
21519304Speter		    regexec(&sp->re_c, dbp, 0, match, REG_STARTEND)) {
21619304Speter		case 0:
21719304Speter			if (cmd == V)
21819304Speter				continue;
21919304Speter			break;
22019304Speter		case REG_NOMATCH:
22119304Speter			if (cmd == GLOBAL)
22219304Speter				continue;
22319304Speter			break;
22419304Speter		default:
22519304Speter			re_error(sp, eval, &sp->re_c);
22619304Speter			break;
22719304Speter		}
22819304Speter
22919304Speter		/* If follows the last entry, extend the last entry's range. */
230254225Speter		if ((rp = TAILQ_LAST(ecp->rq, _rh)) != NULL &&
23119304Speter		    rp->stop == start - 1) {
23219304Speter			++rp->stop;
23319304Speter			continue;
23419304Speter		}
23519304Speter
23619304Speter		/* Allocate a new range, and append it to the list. */
23719304Speter		CALLOC(sp, rp, RANGE *, 1, sizeof(RANGE));
23819304Speter		if (rp == NULL)
23919304Speter			return (1);
24019304Speter		rp->start = rp->stop = start;
241254225Speter		TAILQ_INSERT_TAIL(ecp->rq, rp, q);
24219304Speter	}
24319304Speter	search_busy(sp, BUSY_OFF);
24419304Speter	return (0);
24519304Speter}
24619304Speter
24719304Speter/*
24819304Speter * ex_g_insdel --
24919304Speter *	Update the ranges based on an insertion or deletion.
25019304Speter *
25119304Speter * PUBLIC: int ex_g_insdel __P((SCR *, lnop_t, recno_t));
25219304Speter */
25319304Speterint
254254225Speterex_g_insdel(SCR *sp, lnop_t op, recno_t lno)
25519304Speter{
25619304Speter	EXCMD *ecp;
25719304Speter	RANGE *nrp, *rp;
25819304Speter
25919304Speter	/* All insert/append operations are done as inserts. */
26019304Speter	if (op == LINE_APPEND)
26119304Speter		abort();
26219304Speter
26319304Speter	if (op == LINE_RESET)
26419304Speter		return (0);
26519304Speter
266254225Speter	SLIST_FOREACH(ecp, sp->gp->ecq, q) {
26719304Speter		if (!FL_ISSET(ecp->agv_flags, AGV_AT | AGV_GLOBAL | AGV_V))
26819304Speter			continue;
269254225Speter		TAILQ_FOREACH_SAFE(rp, ecp->rq, q, nrp) {
27019304Speter			/* If range less than the line, ignore it. */
27119304Speter			if (rp->stop < lno)
27219304Speter				continue;
27319304Speter
27419304Speter			/*
27519304Speter			 * If range greater than the line, decrement or
27619304Speter			 * increment the range.
27719304Speter			 */
27819304Speter			if (rp->start > lno) {
27919304Speter				if (op == LINE_DELETE) {
28019304Speter					--rp->start;
28119304Speter					--rp->stop;
28219304Speter				} else {
28319304Speter					++rp->start;
28419304Speter					++rp->stop;
28519304Speter				}
28619304Speter				continue;
28719304Speter			}
28819304Speter
28919304Speter			/*
29019304Speter			 * Lno is inside the range, decrement the end point
29119304Speter			 * for deletion, and split the range for insertion.
29219304Speter			 * In the latter case, since we're inserting a new
29319304Speter			 * element, neither range can be exhausted.
29419304Speter			 */
29519304Speter			if (op == LINE_DELETE) {
29619304Speter				if (rp->start > --rp->stop) {
297254225Speter					TAILQ_REMOVE(ecp->rq, rp, q);
29819304Speter					free(rp);
29919304Speter				}
30019304Speter			} else {
30119304Speter				CALLOC_RET(sp, nrp, RANGE *, 1, sizeof(RANGE));
30219304Speter				nrp->start = lno + 1;
30319304Speter				nrp->stop = rp->stop + 1;
30419304Speter				rp->stop = lno - 1;
305254225Speter				TAILQ_INSERT_AFTER(ecp->rq, rp, nrp, q);
30619304Speter			}
30719304Speter		}
30819304Speter
30919304Speter		/*
31019304Speter		 * If the command deleted/inserted lines, the cursor moves to
31119304Speter		 * the line after the deleted/inserted line.
31219304Speter		 */
31319304Speter		ecp->range_lno = lno;
31419304Speter	}
31519304Speter	return (0);
31619304Speter}
317