Home | History | Annotate | Line # | Download | only in ex
ex_global.c revision 1.1
      1 /*-
      2  * Copyright (c) 1992, 1993, 1994
      3  *	The Regents of the University of California.  All rights reserved.
      4  * Copyright (c) 1992, 1993, 1994, 1995, 1996
      5  *	Keith Bostic.  All rights reserved.
      6  *
      7  * See the LICENSE file for redistribution information.
      8  */
      9 
     10 #include "config.h"
     11 
     12 #ifndef lint
     13 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 ";
     14 #endif /* not lint */
     15 
     16 #include <sys/types.h>
     17 #include <sys/queue.h>
     18 
     19 #include <bitstring.h>
     20 #include <ctype.h>
     21 #include <errno.h>
     22 #include <limits.h>
     23 #include <stdio.h>
     24 #include <stdlib.h>
     25 #include <string.h>
     26 #include <unistd.h>
     27 
     28 #include "../common/common.h"
     29 
     30 enum which {GLOBAL, V};
     31 
     32 static int ex_g_setup __P((SCR *, EXCMD *, enum which));
     33 
     34 /*
     35  * ex_global -- [line [,line]] g[lobal][!] /pattern/ [commands]
     36  *	Exec on lines matching a pattern.
     37  *
     38  * PUBLIC: int ex_global __P((SCR *, EXCMD *));
     39  */
     40 int
     41 ex_global(SCR *sp, EXCMD *cmdp)
     42 {
     43 	return (ex_g_setup(sp,
     44 	    cmdp, FL_ISSET(cmdp->iflags, E_C_FORCE) ? V : GLOBAL));
     45 }
     46 
     47 /*
     48  * ex_v -- [line [,line]] v /pattern/ [commands]
     49  *	Exec on lines not matching a pattern.
     50  *
     51  * PUBLIC: int ex_v __P((SCR *, EXCMD *));
     52  */
     53 int
     54 ex_v(SCR *sp, EXCMD *cmdp)
     55 {
     56 	return (ex_g_setup(sp, cmdp, V));
     57 }
     58 
     59 /*
     60  * ex_g_setup --
     61  *	Ex global and v commands.
     62  */
     63 static int
     64 ex_g_setup(SCR *sp, EXCMD *cmdp, enum which cmd)
     65 {
     66 	CHAR_T *ptrn, *p, *t;
     67 	EXCMD *ecp;
     68 	MARK abs;
     69 	RANGE *rp;
     70 	busy_t btype;
     71 	db_recno_t start, end;
     72 	regex_t *re;
     73 	regmatch_t match[1];
     74 	size_t len;
     75 	int cnt, delim, eval;
     76 	CHAR_T *dbp;
     77 
     78 	NEEDFILE(sp, cmdp);
     79 
     80 	if (F_ISSET(sp, SC_EX_GLOBAL)) {
     81 		msgq(sp, M_ERR,
     82 	"124|The %s command can't be used as part of a global or v command",
     83 		    cmdp->cmd->name);
     84 		return (1);
     85 	}
     86 
     87 	/*
     88 	 * Skip leading white space.  Historic vi allowed any non-alphanumeric
     89 	 * to serve as the global command delimiter.
     90 	 */
     91 	if (cmdp->argc == 0)
     92 		goto usage;
     93 	for (p = cmdp->argv[0]->bp; ISBLANK(*p); ++p);
     94 	if (*p == '\0' || ISALNUM(*p) ||
     95 	    *p == '\\' || *p == '|' || *p == '\n') {
     96 usage:		ex_emsg(sp, cmdp->cmd->usage, EXM_USAGE);
     97 		return (1);
     98 	}
     99 	delim = *p++;
    100 
    101 	/*
    102 	 * Get the pattern string, toss escaped characters.
    103 	 *
    104 	 * QUOTING NOTE:
    105 	 * Only toss an escaped character if it escapes a delimiter.
    106 	 */
    107 	for (ptrn = t = p;;) {
    108 		if (p[0] == '\0' || p[0] == delim) {
    109 			if (p[0] == delim)
    110 				++p;
    111 			/*
    112 			 * !!!
    113 			 * Nul terminate the pattern string -- it's passed
    114 			 * to regcomp which doesn't understand anything else.
    115 			 */
    116 			*t = L('\0');
    117 			break;
    118 		}
    119 		if (p[0] == L('\\'))
    120 			if (p[1] == delim)
    121 				++p;
    122 			else if (p[1] == L('\\'))
    123 				*t++ = *p++;
    124 		*t++ = *p++;
    125 	}
    126 
    127 	/* If the pattern string is empty, use the last one. */
    128 	if (*ptrn == L('\0')) {
    129 		if (sp->re == NULL) {
    130 			ex_emsg(sp, NULL, EXM_NOPREVRE);
    131 			return (1);
    132 		}
    133 
    134 		/* Re-compile the RE if necessary. */
    135 		if (!F_ISSET(sp, SC_RE_SEARCH) &&
    136 		    re_compile(sp, sp->re, sp->re_len,
    137 		    NULL, NULL, &sp->re_c, SEARCH_CSEARCH | SEARCH_MSG))
    138 			return (1);
    139 	} else {
    140 		/* Compile the RE. */
    141 		if (re_compile(sp, ptrn, t - ptrn, &sp->re,
    142 		    &sp->re_len, &sp->re_c, SEARCH_CSEARCH | SEARCH_MSG))
    143 			return (1);
    144 
    145 		/*
    146 		 * Set saved RE.  Historic practice is that globals set
    147 		 * direction as well as the RE.
    148 		 */
    149 		sp->searchdir = FORWARD;
    150 	}
    151 	re = &sp->re_c;
    152 
    153 	/* The global commands always set the previous context mark. */
    154 	abs.lno = sp->lno;
    155 	abs.cno = sp->cno;
    156 	if (mark_set(sp, ABSMARK1, &abs, 1))
    157 		return (1);
    158 
    159 	/* Get an EXCMD structure. */
    160 	CALLOC_RET(sp, ecp, EXCMD *, 1, sizeof(EXCMD));
    161 	CIRCLEQ_INIT(&ecp->rq);
    162 
    163 	/*
    164 	 * Get a copy of the command string; the default command is print.
    165 	 * Don't worry about a set of <blank>s with no command, that will
    166 	 * default to print in the ex parser.  We need to have two copies
    167 	 * because the ex parser may step on the command string when it's
    168 	 * parsing it.
    169 	 */
    170 	if ((len = cmdp->argv[0]->len - (p - cmdp->argv[0]->bp)) == 0) {
    171 		p = L("p");
    172 		len = 1;
    173 	}
    174 
    175 	MALLOC_RET(sp, ecp->cp, CHAR_T *, (len * 2) * sizeof(CHAR_T));
    176 	ecp->o_cp = ecp->cp;
    177 	ecp->o_clen = len;
    178 	MEMCPYW(ecp->cp + len, p, len);
    179 	ecp->range_lno = OOBLNO;
    180 	FL_SET(ecp->agv_flags, cmd == GLOBAL ? AGV_GLOBAL : AGV_V);
    181 	LIST_INSERT_HEAD(&sp->wp->ecq, ecp, q);
    182 
    183 	/*
    184 	 * For each line...  The semantics of global matching are that we first
    185 	 * have to decide which lines are going to get passed to the command,
    186 	 * and then pass them to the command, ignoring other changes.  There's
    187 	 * really no way to do this in a single pass, since arbitrary line
    188 	 * creation, deletion and movement can be done in the ex command.  For
    189 	 * example, a good vi clone test is ":g/X/mo.-3", or "g/X/.,.+1d".
    190 	 * What we do is create linked list of lines that are tracked through
    191 	 * each ex command.  There's a callback routine which the DB interface
    192 	 * routines call when a line is created or deleted.  This doesn't help
    193 	 * the layering much.
    194 	 */
    195 	btype = BUSY_ON;
    196 	cnt = INTERRUPT_CHECK;
    197 	for (start = cmdp->addr1.lno,
    198 	    end = cmdp->addr2.lno; start <= end; ++start) {
    199 		if (cnt-- == 0) {
    200 			if (INTERRUPTED(sp)) {
    201 				LIST_REMOVE(ecp, q);
    202 				free(ecp->cp);
    203 				free(ecp);
    204 				break;
    205 			}
    206 			search_busy(sp, btype);
    207 			btype = BUSY_UPDATE;
    208 			cnt = INTERRUPT_CHECK;
    209 		}
    210 		if (db_get(sp, start, DBG_FATAL, &dbp, &len))
    211 			return (1);
    212 		match[0].rm_so = 0;
    213 		match[0].rm_eo = len;
    214 		switch (eval =
    215 		    regexec(&sp->re_c, dbp, 0, match, REG_STARTEND)) {
    216 		case 0:
    217 			if (cmd == V)
    218 				continue;
    219 			break;
    220 		case REG_NOMATCH:
    221 			if (cmd == GLOBAL)
    222 				continue;
    223 			break;
    224 		default:
    225 			re_error(sp, eval, &sp->re_c);
    226 			break;
    227 		}
    228 
    229 		/* If follows the last entry, extend the last entry's range. */
    230 		if ((rp = ecp->rq.cqh_last) != (void *)&ecp->rq &&
    231 		    rp->stop == start - 1) {
    232 			++rp->stop;
    233 			continue;
    234 		}
    235 
    236 		/* Allocate a new range, and append it to the list. */
    237 		CALLOC(sp, rp, RANGE *, 1, sizeof(RANGE));
    238 		if (rp == NULL)
    239 			return (1);
    240 		rp->start = rp->stop = start;
    241 		CIRCLEQ_INSERT_TAIL(&ecp->rq, rp, q);
    242 	}
    243 	search_busy(sp, BUSY_OFF);
    244 	return (0);
    245 }
    246 
    247 /*
    248  * ex_g_insdel --
    249  *	Update the ranges based on an insertion or deletion.
    250  *
    251  * PUBLIC: int ex_g_insdel __P((SCR *, lnop_t, db_recno_t));
    252  */
    253 int
    254 ex_g_insdel(SCR *sp, lnop_t op, db_recno_t lno)
    255 {
    256 	EXCMD *ecp;
    257 	RANGE *nrp, *rp;
    258 
    259 	/* All insert/append operations are done as inserts. */
    260 	if (op == LINE_APPEND)
    261 		abort();
    262 
    263 	if (op == LINE_RESET)
    264 		return (0);
    265 
    266 	for (ecp = sp->wp->ecq.lh_first; ecp != NULL; ecp = ecp->q.le_next) {
    267 		if (!FL_ISSET(ecp->agv_flags, AGV_AT | AGV_GLOBAL | AGV_V))
    268 			continue;
    269 		for (rp = ecp->rq.cqh_first; rp != (void *)&ecp->rq; rp = nrp) {
    270 			nrp = rp->q.cqe_next;
    271 
    272 			/* If range less than the line, ignore it. */
    273 			if (rp->stop < lno)
    274 				continue;
    275 
    276 			/*
    277 			 * If range greater than the line, decrement or
    278 			 * increment the range.
    279 			 */
    280 			if (rp->start > lno) {
    281 				if (op == LINE_DELETE) {
    282 					--rp->start;
    283 					--rp->stop;
    284 				} else {
    285 					++rp->start;
    286 					++rp->stop;
    287 				}
    288 				continue;
    289 			}
    290 
    291 			/*
    292 			 * Lno is inside the range, decrement the end point
    293 			 * for deletion, and split the range for insertion.
    294 			 * In the latter case, since we're inserting a new
    295 			 * element, neither range can be exhausted.
    296 			 */
    297 			if (op == LINE_DELETE) {
    298 				if (rp->start > --rp->stop) {
    299 					CIRCLEQ_REMOVE(&ecp->rq, rp, q);
    300 					free(rp);
    301 				}
    302 			} else {
    303 				CALLOC_RET(sp, nrp, RANGE *, 1, sizeof(RANGE));
    304 				nrp->start = lno + 1;
    305 				nrp->stop = rp->stop + 1;
    306 				rp->stop = lno - 1;
    307 				CIRCLEQ_INSERT_AFTER(&ecp->rq, rp, nrp, q);
    308 				rp = nrp;
    309 			}
    310 		}
    311 
    312 		/*
    313 		 * If the command deleted/inserted lines, the cursor moves to
    314 		 * the line after the deleted/inserted line.
    315 		 */
    316 		ecp->range_lno = lno;
    317 	}
    318 	return (0);
    319 }
    320