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