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