xref: /minix3/external/bsd/nvi/dist/ex/ex_global.c (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
1*0a6a1f1dSLionel Sambuc /*	$NetBSD: ex_global.c,v 1.5 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_global.c,v 10.30 2001/06/25 15:19:16 skimo Exp  (Berkeley) Date: 2001/06/25 15:19:16 ";
1784d9c625SLionel Sambuc #endif /* not lint */
18*0a6a1f1dSLionel Sambuc #else
19*0a6a1f1dSLionel Sambuc __RCSID("$NetBSD: ex_global.c,v 1.5 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 
2584d9c625SLionel Sambuc #include <bitstring.h>
2684d9c625SLionel Sambuc #include <ctype.h>
2784d9c625SLionel Sambuc #include <errno.h>
2884d9c625SLionel Sambuc #include <limits.h>
2984d9c625SLionel Sambuc #include <stdio.h>
3084d9c625SLionel Sambuc #include <stdlib.h>
3184d9c625SLionel Sambuc #include <string.h>
3284d9c625SLionel Sambuc #include <unistd.h>
3384d9c625SLionel Sambuc 
3484d9c625SLionel Sambuc #include "../common/common.h"
3584d9c625SLionel Sambuc 
3684d9c625SLionel Sambuc enum which {GLOBAL, V};
3784d9c625SLionel Sambuc 
3884d9c625SLionel Sambuc static int ex_g_setup __P((SCR *, EXCMD *, enum which));
3984d9c625SLionel Sambuc 
4084d9c625SLionel Sambuc /*
4184d9c625SLionel Sambuc  * ex_global -- [line [,line]] g[lobal][!] /pattern/ [commands]
4284d9c625SLionel Sambuc  *	Exec on lines matching a pattern.
4384d9c625SLionel Sambuc  *
4484d9c625SLionel Sambuc  * PUBLIC: int ex_global __P((SCR *, EXCMD *));
4584d9c625SLionel Sambuc  */
4684d9c625SLionel Sambuc int
ex_global(SCR * sp,EXCMD * cmdp)4784d9c625SLionel Sambuc ex_global(SCR *sp, EXCMD *cmdp)
4884d9c625SLionel Sambuc {
4984d9c625SLionel Sambuc 	return (ex_g_setup(sp,
5084d9c625SLionel Sambuc 	    cmdp, FL_ISSET(cmdp->iflags, E_C_FORCE) ? V : GLOBAL));
5184d9c625SLionel Sambuc }
5284d9c625SLionel Sambuc 
5384d9c625SLionel Sambuc /*
5484d9c625SLionel Sambuc  * ex_v -- [line [,line]] v /pattern/ [commands]
5584d9c625SLionel Sambuc  *	Exec on lines not matching a pattern.
5684d9c625SLionel Sambuc  *
5784d9c625SLionel Sambuc  * PUBLIC: int ex_v __P((SCR *, EXCMD *));
5884d9c625SLionel Sambuc  */
5984d9c625SLionel Sambuc int
ex_v(SCR * sp,EXCMD * cmdp)6084d9c625SLionel Sambuc ex_v(SCR *sp, EXCMD *cmdp)
6184d9c625SLionel Sambuc {
6284d9c625SLionel Sambuc 	return (ex_g_setup(sp, cmdp, V));
6384d9c625SLionel Sambuc }
6484d9c625SLionel Sambuc 
6584d9c625SLionel Sambuc /*
6684d9c625SLionel Sambuc  * ex_g_setup --
6784d9c625SLionel Sambuc  *	Ex global and v commands.
6884d9c625SLionel Sambuc  */
6984d9c625SLionel Sambuc static int
ex_g_setup(SCR * sp,EXCMD * cmdp,enum which cmd)7084d9c625SLionel Sambuc ex_g_setup(SCR *sp, EXCMD *cmdp, enum which cmd)
7184d9c625SLionel Sambuc {
7284d9c625SLionel Sambuc 	CHAR_T *ptrn, *p, *t;
7384d9c625SLionel Sambuc 	EXCMD *ecp;
7484d9c625SLionel Sambuc 	MARK myabs;
7584d9c625SLionel Sambuc 	RANGE *rp;
7684d9c625SLionel Sambuc 	busy_t btype;
7784d9c625SLionel Sambuc 	db_recno_t start, end;
7884d9c625SLionel Sambuc 	regmatch_t match[1];
7984d9c625SLionel Sambuc 	size_t len;
8084d9c625SLionel Sambuc 	int cnt, delim, eval;
8184d9c625SLionel Sambuc 	CHAR_T *dbp;
8284d9c625SLionel Sambuc 
8384d9c625SLionel Sambuc 	NEEDFILE(sp, cmdp);
8484d9c625SLionel Sambuc 
8584d9c625SLionel Sambuc 	if (F_ISSET(sp, SC_EX_GLOBAL)) {
8684d9c625SLionel Sambuc 		msgq_wstr(sp, M_ERR, cmdp->cmd->name,
8784d9c625SLionel Sambuc 	"124|The %s command can't be used as part of a global or v command");
8884d9c625SLionel Sambuc 		return (1);
8984d9c625SLionel Sambuc 	}
9084d9c625SLionel Sambuc 
9184d9c625SLionel Sambuc 	/*
9284d9c625SLionel Sambuc 	 * Skip leading white space.  Historic vi allowed any non-alphanumeric
9384d9c625SLionel Sambuc 	 * to serve as the global command delimiter.
9484d9c625SLionel Sambuc 	 */
9584d9c625SLionel Sambuc 	if (cmdp->argc == 0)
9684d9c625SLionel Sambuc 		goto usage;
9784d9c625SLionel Sambuc 	for (p = cmdp->argv[0]->bp; ISBLANK(*p); ++p);
9884d9c625SLionel Sambuc 	if (*p == '\0' || ISALNUM((UCHAR_T)*p) ||
9984d9c625SLionel Sambuc 	    *p == '\\' || *p == '|' || *p == '\n') {
10084d9c625SLionel Sambuc usage:		ex_emsg(sp, cmdp->cmd->usage, EXM_USAGE);
10184d9c625SLionel Sambuc 		return (1);
10284d9c625SLionel Sambuc 	}
10384d9c625SLionel Sambuc 	delim = *p++;
10484d9c625SLionel Sambuc 
10584d9c625SLionel Sambuc 	/*
10684d9c625SLionel Sambuc 	 * Get the pattern string, toss escaped characters.
10784d9c625SLionel Sambuc 	 *
10884d9c625SLionel Sambuc 	 * QUOTING NOTE:
10984d9c625SLionel Sambuc 	 * Only toss an escaped character if it escapes a delimiter.
11084d9c625SLionel Sambuc 	 */
11184d9c625SLionel Sambuc 	for (ptrn = t = p;;) {
11284d9c625SLionel Sambuc 		if (p[0] == '\0' || p[0] == delim) {
11384d9c625SLionel Sambuc 			if (p[0] == delim)
11484d9c625SLionel Sambuc 				++p;
11584d9c625SLionel Sambuc 			/*
11684d9c625SLionel Sambuc 			 * !!!
11784d9c625SLionel Sambuc 			 * Nul terminate the pattern string -- it's passed
11884d9c625SLionel Sambuc 			 * to regcomp which doesn't understand anything else.
11984d9c625SLionel Sambuc 			 */
12084d9c625SLionel Sambuc 			*t = L('\0');
12184d9c625SLionel Sambuc 			break;
12284d9c625SLionel Sambuc 		}
12384d9c625SLionel Sambuc 		if (p[0] == L('\\')) {
12484d9c625SLionel Sambuc 			if (p[1] == delim)
12584d9c625SLionel Sambuc 				++p;
12684d9c625SLionel Sambuc 			else if (p[1] == L('\\'))
12784d9c625SLionel Sambuc 				*t++ = *p++;
12884d9c625SLionel Sambuc 		}
12984d9c625SLionel Sambuc 		*t++ = *p++;
13084d9c625SLionel Sambuc 	}
13184d9c625SLionel Sambuc 
13284d9c625SLionel Sambuc 	/* If the pattern string is empty, use the last one. */
13384d9c625SLionel Sambuc 	if (*ptrn == L('\0')) {
13484d9c625SLionel Sambuc 		if (sp->re == NULL) {
13584d9c625SLionel Sambuc 			ex_emsg(sp, NULL, EXM_NOPREVRE);
13684d9c625SLionel Sambuc 			return (1);
13784d9c625SLionel Sambuc 		}
13884d9c625SLionel Sambuc 
13984d9c625SLionel Sambuc 		/* Re-compile the RE if necessary. */
14084d9c625SLionel Sambuc 		if (!F_ISSET(sp, SC_RE_SEARCH) &&
14184d9c625SLionel Sambuc 		    re_compile(sp, sp->re, sp->re_len,
14284d9c625SLionel Sambuc 		    NULL, NULL, &sp->re_c, SEARCH_CSEARCH | SEARCH_MSG))
14384d9c625SLionel Sambuc 			return (1);
14484d9c625SLionel Sambuc 	} else {
14584d9c625SLionel Sambuc 		/* Compile the RE. */
14684d9c625SLionel Sambuc 		if (re_compile(sp, ptrn, t - ptrn, &sp->re,
14784d9c625SLionel Sambuc 		    &sp->re_len, &sp->re_c, SEARCH_CSEARCH | SEARCH_MSG))
14884d9c625SLionel Sambuc 			return (1);
14984d9c625SLionel Sambuc 
15084d9c625SLionel Sambuc 		/*
15184d9c625SLionel Sambuc 		 * Set saved RE.  Historic practice is that globals set
15284d9c625SLionel Sambuc 		 * direction as well as the RE.
15384d9c625SLionel Sambuc 		 */
15484d9c625SLionel Sambuc 		sp->searchdir = FORWARD;
15584d9c625SLionel Sambuc 	}
15684d9c625SLionel Sambuc 
15784d9c625SLionel Sambuc 	/* The global commands always set the previous context mark. */
15884d9c625SLionel Sambuc 	myabs.lno = sp->lno;
15984d9c625SLionel Sambuc 	myabs.cno = sp->cno;
16084d9c625SLionel Sambuc 	if (mark_set(sp, ABSMARK1, &myabs, 1))
16184d9c625SLionel Sambuc 		return (1);
16284d9c625SLionel Sambuc 
16384d9c625SLionel Sambuc 	/* Get an EXCMD structure. */
16484d9c625SLionel Sambuc 	CALLOC_RET(sp, ecp, EXCMD *, 1, sizeof(EXCMD));
16584d9c625SLionel Sambuc 	TAILQ_INIT(&ecp->rq);
16684d9c625SLionel Sambuc 
16784d9c625SLionel Sambuc 	/*
16884d9c625SLionel Sambuc 	 * Get a copy of the command string; the default command is print.
16984d9c625SLionel Sambuc 	 * Don't worry about a set of <blank>s with no command, that will
17084d9c625SLionel Sambuc 	 * default to print in the ex parser.  We need to have two copies
17184d9c625SLionel Sambuc 	 * because the ex parser may step on the command string when it's
17284d9c625SLionel Sambuc 	 * parsing it.
17384d9c625SLionel Sambuc 	 */
17484d9c625SLionel Sambuc 	if ((len = cmdp->argv[0]->len - (p - cmdp->argv[0]->bp)) == 0) {
17584d9c625SLionel Sambuc 		p = __UNCONST(L("p"));
17684d9c625SLionel Sambuc 		len = 1;
17784d9c625SLionel Sambuc 	}
17884d9c625SLionel Sambuc 
17984d9c625SLionel Sambuc 	MALLOC_GOTO(sp, ecp->cp, CHAR_T *, (len * 2) * sizeof(CHAR_T));
18084d9c625SLionel Sambuc 	ecp->o_cp = ecp->cp;
18184d9c625SLionel Sambuc 	ecp->o_clen = len;
18284d9c625SLionel Sambuc 	MEMCPYW(ecp->cp + len, p, len);
18384d9c625SLionel Sambuc 	ecp->range_lno = OOBLNO;
18484d9c625SLionel Sambuc 	FL_SET(ecp->agv_flags, cmd == GLOBAL ? AGV_GLOBAL : AGV_V);
18584d9c625SLionel Sambuc 	LIST_INSERT_HEAD(&sp->wp->ecq, ecp, q);
18684d9c625SLionel Sambuc 
18784d9c625SLionel Sambuc 	/*
18884d9c625SLionel Sambuc 	 * For each line...  The semantics of global matching are that we first
18984d9c625SLionel Sambuc 	 * have to decide which lines are going to get passed to the command,
19084d9c625SLionel Sambuc 	 * and then pass them to the command, ignoring other changes.  There's
19184d9c625SLionel Sambuc 	 * really no way to do this in a single pass, since arbitrary line
19284d9c625SLionel Sambuc 	 * creation, deletion and movement can be done in the ex command.  For
19384d9c625SLionel Sambuc 	 * example, a good vi clone test is ":g/X/mo.-3", or "g/X/.,.+1d".
19484d9c625SLionel Sambuc 	 * What we do is create linked list of lines that are tracked through
19584d9c625SLionel Sambuc 	 * each ex command.  There's a callback routine which the DB interface
19684d9c625SLionel Sambuc 	 * routines call when a line is created or deleted.  This doesn't help
19784d9c625SLionel Sambuc 	 * the layering much.
19884d9c625SLionel Sambuc 	 */
19984d9c625SLionel Sambuc 	btype = BUSY_ON;
20084d9c625SLionel Sambuc 	cnt = INTERRUPT_CHECK;
20184d9c625SLionel Sambuc 	for (start = cmdp->addr1.lno,
20284d9c625SLionel Sambuc 	    end = cmdp->addr2.lno; start <= end; ++start) {
20384d9c625SLionel Sambuc 		if (cnt-- == 0) {
20484d9c625SLionel Sambuc 			if (INTERRUPTED(sp)) {
20584d9c625SLionel Sambuc 				LIST_REMOVE(ecp, q);
20684d9c625SLionel Sambuc 				free(ecp->cp);
20784d9c625SLionel Sambuc 				free(ecp);
20884d9c625SLionel Sambuc 				break;
20984d9c625SLionel Sambuc 			}
21084d9c625SLionel Sambuc 			search_busy(sp, btype);
21184d9c625SLionel Sambuc 			btype = BUSY_UPDATE;
21284d9c625SLionel Sambuc 			cnt = INTERRUPT_CHECK;
21384d9c625SLionel Sambuc 		}
21484d9c625SLionel Sambuc 		if (db_get(sp, start, DBG_FATAL, &dbp, &len))
21584d9c625SLionel Sambuc 			return (1);
21684d9c625SLionel Sambuc 		match[0].rm_so = 0;
21784d9c625SLionel Sambuc 		match[0].rm_eo = len;
21884d9c625SLionel Sambuc 		switch (eval =
21984d9c625SLionel Sambuc 		    regexec(&sp->re_c, dbp, 0, match, REG_STARTEND)) {
22084d9c625SLionel Sambuc 		case 0:
22184d9c625SLionel Sambuc 			if (cmd == V)
22284d9c625SLionel Sambuc 				continue;
22384d9c625SLionel Sambuc 			break;
22484d9c625SLionel Sambuc 		case REG_NOMATCH:
22584d9c625SLionel Sambuc 			if (cmd == GLOBAL)
22684d9c625SLionel Sambuc 				continue;
22784d9c625SLionel Sambuc 			break;
22884d9c625SLionel Sambuc 		default:
22984d9c625SLionel Sambuc 			re_error(sp, eval, &sp->re_c);
23084d9c625SLionel Sambuc 			break;
23184d9c625SLionel Sambuc 		}
23284d9c625SLionel Sambuc 
23384d9c625SLionel Sambuc 		/* If follows the last entry, extend the last entry's range. */
23484d9c625SLionel Sambuc 		if ((rp = TAILQ_LAST(&ecp->rq, _rh)) != NULL &&
23584d9c625SLionel Sambuc 		    rp->stop == start - 1) {
23684d9c625SLionel Sambuc 			++rp->stop;
23784d9c625SLionel Sambuc 			continue;
23884d9c625SLionel Sambuc 		}
23984d9c625SLionel Sambuc 
24084d9c625SLionel Sambuc 		/* Allocate a new range, and append it to the list. */
24184d9c625SLionel Sambuc 		CALLOC(sp, rp, RANGE *, 1, sizeof(RANGE));
24284d9c625SLionel Sambuc 		if (rp == NULL)
24384d9c625SLionel Sambuc 			return (1);
24484d9c625SLionel Sambuc 		rp->start = rp->stop = start;
24584d9c625SLionel Sambuc 		TAILQ_INSERT_TAIL(&ecp->rq, rp, q);
24684d9c625SLionel Sambuc 	}
24784d9c625SLionel Sambuc 	search_busy(sp, BUSY_OFF);
24884d9c625SLionel Sambuc 	return (0);
24984d9c625SLionel Sambuc alloc_err:
25084d9c625SLionel Sambuc 	free(ecp);
25184d9c625SLionel Sambuc 	return 1;
25284d9c625SLionel Sambuc }
25384d9c625SLionel Sambuc 
25484d9c625SLionel Sambuc /*
25584d9c625SLionel Sambuc  * ex_g_insdel --
25684d9c625SLionel Sambuc  *	Update the ranges based on an insertion or deletion.
25784d9c625SLionel Sambuc  *
25884d9c625SLionel Sambuc  * PUBLIC: int ex_g_insdel __P((SCR *, lnop_t, db_recno_t));
25984d9c625SLionel Sambuc  */
26084d9c625SLionel Sambuc int
ex_g_insdel(SCR * sp,lnop_t op,db_recno_t lno)26184d9c625SLionel Sambuc ex_g_insdel(SCR *sp, lnop_t op, db_recno_t lno)
26284d9c625SLionel Sambuc {
26384d9c625SLionel Sambuc 	EXCMD *ecp;
26484d9c625SLionel Sambuc 	RANGE *nrp, *rp;
26584d9c625SLionel Sambuc 
26684d9c625SLionel Sambuc 	/* All insert/append operations are done as inserts. */
26784d9c625SLionel Sambuc 	if (op == LINE_APPEND)
26884d9c625SLionel Sambuc 		abort();
26984d9c625SLionel Sambuc 
27084d9c625SLionel Sambuc 	if (op == LINE_RESET)
27184d9c625SLionel Sambuc 		return (0);
27284d9c625SLionel Sambuc 
27384d9c625SLionel Sambuc 	LIST_FOREACH(ecp, &sp->wp->ecq, q) {
27484d9c625SLionel Sambuc 		if (!FL_ISSET(ecp->agv_flags, AGV_AT | AGV_GLOBAL | AGV_V))
27584d9c625SLionel Sambuc 			continue;
27684d9c625SLionel Sambuc 		TAILQ_FOREACH_SAFE(rp, &ecp->rq, q, nrp) {
27784d9c625SLionel Sambuc 			/* If range less than the line, ignore it. */
27884d9c625SLionel Sambuc 			if (rp->stop < lno)
27984d9c625SLionel Sambuc 				continue;
28084d9c625SLionel Sambuc 
28184d9c625SLionel Sambuc 			/*
28284d9c625SLionel Sambuc 			 * If range greater than the line, decrement or
28384d9c625SLionel Sambuc 			 * increment the range.
28484d9c625SLionel Sambuc 			 */
28584d9c625SLionel Sambuc 			if (rp->start > lno) {
28684d9c625SLionel Sambuc 				if (op == LINE_DELETE) {
28784d9c625SLionel Sambuc 					--rp->start;
28884d9c625SLionel Sambuc 					--rp->stop;
28984d9c625SLionel Sambuc 				} else {
29084d9c625SLionel Sambuc 					++rp->start;
29184d9c625SLionel Sambuc 					++rp->stop;
29284d9c625SLionel Sambuc 				}
29384d9c625SLionel Sambuc 				continue;
29484d9c625SLionel Sambuc 			}
29584d9c625SLionel Sambuc 
29684d9c625SLionel Sambuc 			/*
29784d9c625SLionel Sambuc 			 * Lno is inside the range, decrement the end point
29884d9c625SLionel Sambuc 			 * for deletion, and split the range for insertion.
29984d9c625SLionel Sambuc 			 * In the latter case, since we're inserting a new
30084d9c625SLionel Sambuc 			 * element, neither range can be exhausted.
30184d9c625SLionel Sambuc 			 */
30284d9c625SLionel Sambuc 			if (op == LINE_DELETE) {
30384d9c625SLionel Sambuc 				if (rp->start > --rp->stop) {
30484d9c625SLionel Sambuc 					TAILQ_REMOVE(&ecp->rq, rp, q);
30584d9c625SLionel Sambuc 					free(rp);
30684d9c625SLionel Sambuc 				}
30784d9c625SLionel Sambuc 			} else {
30884d9c625SLionel Sambuc 				CALLOC_RET(sp, nrp, RANGE *, 1, sizeof(RANGE));
30984d9c625SLionel Sambuc 				nrp->start = lno + 1;
31084d9c625SLionel Sambuc 				nrp->stop = rp->stop + 1;
31184d9c625SLionel Sambuc 				rp->stop = lno - 1;
31284d9c625SLionel Sambuc 				TAILQ_INSERT_AFTER(&ecp->rq, rp, nrp, q);
31384d9c625SLionel Sambuc 			}
31484d9c625SLionel Sambuc 		}
31584d9c625SLionel Sambuc 
31684d9c625SLionel Sambuc 		/*
31784d9c625SLionel Sambuc 		 * If the command deleted/inserted lines, the cursor moves to
31884d9c625SLionel Sambuc 		 * the line after the deleted/inserted line.
31984d9c625SLionel Sambuc 		 */
32084d9c625SLionel Sambuc 		ecp->range_lno = lno;
32184d9c625SLionel Sambuc 	}
32284d9c625SLionel Sambuc 	return (0);
32384d9c625SLionel Sambuc }
324