1331722Seadler/*- 2341477Svmaffione * Copyright (c) 1992, 1993, 1994 3341477Svmaffione * The Regents of the University of California. All rights reserved. 4341477Svmaffione * Copyright (c) 1992, 1993, 1994, 1995, 1996 5341477Svmaffione * Keith Bostic. All rights reserved. 6234228Sluigi * 7234228Sluigi * See the LICENSE file for redistribution information. 8234228Sluigi */ 9234228Sluigi 10234228Sluigi#include "config.h" 11234228Sluigi 12234228Sluigi#ifndef lint 13234228Sluigistatic const char sccsid[] = "$Id: ex_global.c,v 10.32 2011/12/26 23:37:01 zy Exp $"; 14259412Sluigi#endif /* not lint */ 15234228Sluigi 16234228Sluigi#include <sys/types.h> 17234228Sluigi#include <sys/queue.h> 18234228Sluigi#include <sys/time.h> 19234228Sluigi 20234228Sluigi#include <bitstring.h> 21234228Sluigi#include <ctype.h> 22234228Sluigi#include <errno.h> 23234228Sluigi#include <limits.h> 24234228Sluigi#include <stdio.h> 25234228Sluigi#include <stdlib.h> 26234228Sluigi#include <string.h> 27234228Sluigi#include <unistd.h> 28234228Sluigi 29257529Sluigi#include "../common/common.h" 30257529Sluigi 31257529Sluigienum which {GLOBAL, V}; 32234228Sluigi 33257529Sluigistatic int ex_g_setup __P((SCR *, EXCMD *, enum which)); 34257529Sluigi 35257529Sluigi/* 36234228Sluigi * ex_global -- [line [,line]] g[lobal][!] /pattern/ [commands] 37257529Sluigi * Exec on lines matching a pattern. 38257529Sluigi * 39257529Sluigi * PUBLIC: int ex_global __P((SCR *, EXCMD *)); 40234228Sluigi */ 41257529Sluigiint 42257529Sluigiex_global(SCR *sp, EXCMD *cmdp) 43341477Svmaffione{ 44257529Sluigi return (ex_g_setup(sp, 45257529Sluigi cmdp, FL_ISSET(cmdp->iflags, E_C_FORCE) ? V : GLOBAL)); 46257529Sluigi} 47257529Sluigi 48257529Sluigi/* 49257529Sluigi * ex_v -- [line [,line]] v /pattern/ [commands] 50257529Sluigi * Exec on lines not matching a pattern. 51257529Sluigi * 52257529Sluigi * PUBLIC: int ex_v __P((SCR *, EXCMD *)); 53257529Sluigi */ 54257529Sluigiint 55341477Svmaffioneex_v(SCR *sp, EXCMD *cmdp) 56341477Svmaffione{ 57341477Svmaffione return (ex_g_setup(sp, cmdp, V)); 58341477Svmaffione} 59257529Sluigi 60257529Sluigi/* 61341477Svmaffione * ex_g_setup -- 62341477Svmaffione * Ex global and v commands. 63341477Svmaffione */ 64341477Svmaffionestatic int 65257529Sluigiex_g_setup(SCR *sp, EXCMD *cmdp, enum which cmd) 66257529Sluigi{ 67341477Svmaffione CHAR_T *ptrn, *p, *t; 68257529Sluigi EXCMD *ecp; 69257529Sluigi MARK abs; 70341477Svmaffione RANGE *rp; 71341477Svmaffione busy_t btype; 72341477Svmaffione recno_t start, end; 73341477Svmaffione regex_t *re; 74341477Svmaffione regmatch_t match[1]; 75270063Sluigi size_t len; 76270063Sluigi int cnt, delim, eval; 77270063Sluigi CHAR_T *dbp; 78270063Sluigi 79270063Sluigi NEEDFILE(sp, cmdp); 80270063Sluigi 81270063Sluigi if (F_ISSET(sp, SC_EX_GLOBAL)) { 82270063Sluigi msgq_wstr(sp, M_ERR, cmdp->cmd->name, 83270063Sluigi "124|The %s command can't be used as part of a global or v command"); 84270063Sluigi return (1); 85270063Sluigi } 86270063Sluigi 87270063Sluigi /* 88270063Sluigi * Skip leading white space. Historic vi allowed any non-alphanumeric 89270063Sluigi * to serve as the global command delimiter. 90341477Svmaffione */ 91341477Svmaffione if (cmdp->argc == 0) 92341477Svmaffione goto usage; 93270063Sluigi for (p = cmdp->argv[0]->bp; cmdskip(*p); ++p); 94285349Sluigi if (!isascii(*p) || *p == '\0' || isalnum(*p) || 95270063Sluigi *p == '\\' || *p == '|' || *p == '\n') { 96270063Sluigiusage: ex_emsg(sp, cmdp->cmd->usage, EXM_USAGE); 97270063Sluigi return (1); 98270063Sluigi } 99270063Sluigi delim = *p++; 100270063Sluigi 101270063Sluigi /* 102270063Sluigi * Get the pattern string, toss escaped characters. 103270063Sluigi * 104270063Sluigi * QUOTING NOTE: 105270063Sluigi * Only toss an escaped character if it escapes a delimiter. 106270063Sluigi */ 107270063Sluigi for (ptrn = t = p;;) { 108270063Sluigi if (p[0] == '\0' || p[0] == delim) { 109341477Svmaffione if (p[0] == delim) 110270063Sluigi ++p; 111341477Svmaffione /* 112270063Sluigi * !!! 113270063Sluigi * Nul terminate the pattern string -- it's passed 114270063Sluigi * to regcomp which doesn't understand anything else. 115270063Sluigi */ 116270063Sluigi *t = '\0'; 117270063Sluigi break; 118270063Sluigi } 119270063Sluigi if (p[0] == '\\') 120270063Sluigi if (p[1] == delim) 121270063Sluigi ++p; 122270063Sluigi else if (p[1] == '\\') 123270063Sluigi *t++ = *p++; 124270063Sluigi *t++ = *p++; 125270063Sluigi } 126270063Sluigi 127270063Sluigi /* If the pattern string is empty, use the last one. */ 128270063Sluigi if (*ptrn == '\0') { 129270063Sluigi if (sp->re == NULL) { 130270063Sluigi ex_emsg(sp, NULL, EXM_NOPREVRE); 131270063Sluigi return (1); 132285349Sluigi } 133341477Svmaffione 134341477Svmaffione /* Re-compile the RE if necessary. */ 135341477Svmaffione if (!F_ISSET(sp, SC_RE_SEARCH) && 136341477Svmaffione re_compile(sp, sp->re, sp->re_len, 137341477Svmaffione NULL, NULL, &sp->re_c, RE_C_SEARCH)) 138270063Sluigi return (1); 139285349Sluigi } else { 140341477Svmaffione /* Compile the RE. */ 141341477Svmaffione if (re_compile(sp, ptrn, t - ptrn, &sp->re, 142285349Sluigi &sp->re_len, &sp->re_c, RE_C_SEARCH)) 143285349Sluigi return (1); 144285349Sluigi 145285349Sluigi /* 146285349Sluigi * Set saved RE. Historic practice is that globals set 147285349Sluigi * direction as well as the RE. 148285349Sluigi */ 149285349Sluigi sp->searchdir = FORWARD; 150285349Sluigi } 151341477Svmaffione re = &sp->re_c; 152341477Svmaffione 153285349Sluigi /* The global commands always set the previous context mark. */ 154285349Sluigi abs.lno = sp->lno; 155285349Sluigi abs.cno = sp->cno; 156285349Sluigi if (mark_set(sp, ABSMARK1, &abs, 1)) 157285349Sluigi return (1); 158270063Sluigi 159270063Sluigi /* Get an EXCMD structure. */ 160270063Sluigi CALLOC_RET(sp, ecp, EXCMD *, 1, sizeof(EXCMD)); 161270063Sluigi TAILQ_INIT(ecp->rq); 162270063Sluigi 163270063Sluigi /* 164341477Svmaffione * Get a copy of the command string; the default command is print. 165270063Sluigi * Don't worry about a set of <blank>s with no command, that will 166285349Sluigi * default to print in the ex parser. We need to have two copies 167285349Sluigi * because the ex parser may step on the command string when it's 168270063Sluigi * parsing it. 169270063Sluigi */ 170270063Sluigi if ((len = cmdp->argv[0]->len - (p - cmdp->argv[0]->bp)) == 0) { 171270063Sluigi p = L("p"); 172270063Sluigi len = 1; 173270063Sluigi } 174270063Sluigi 175270063Sluigi MALLOC_RET(sp, ecp->cp, CHAR_T *, (len * 2) * sizeof(CHAR_T)); 176285349Sluigi ecp->o_cp = ecp->cp; 177285349Sluigi ecp->o_clen = len; 178341477Svmaffione MEMCPY(ecp->cp + len, p, len); 179341477Svmaffione ecp->range_lno = OOBLNO; 180341477Svmaffione FL_SET(ecp->agv_flags, cmd == GLOBAL ? AGV_GLOBAL : AGV_V); 181341477Svmaffione SLIST_INSERT_HEAD(sp->gp->ecq, ecp, q); 182341477Svmaffione 183270063Sluigi /* 184270063Sluigi * For each line... The semantics of global matching are that we first 185341477Svmaffione * have to decide which lines are going to get passed to the command, 186341477Svmaffione * and then pass them to the command, ignoring other changes. There's 187341477Svmaffione * really no way to do this in a single pass, since arbitrary line 188341477Svmaffione * creation, deletion and movement can be done in the ex command. For 189341477Svmaffione * example, a good vi clone test is ":g/X/mo.-3", or "g/X/.,.+1d". 190341477Svmaffione * What we do is create linked list of lines that are tracked through 191341477Svmaffione * each ex command. There's a callback routine which the DB interface 192341477Svmaffione * routines call when a line is created or deleted. This doesn't help 193341477Svmaffione * the layering much. 194341477Svmaffione */ 195285349Sluigi btype = BUSY_ON; 196285349Sluigi cnt = INTERRUPT_CHECK; 197341477Svmaffione for (start = cmdp->addr1.lno, 198341477Svmaffione end = cmdp->addr2.lno; start <= end; ++start) { 199341477Svmaffione if (cnt-- == 0) { 200341477Svmaffione if (INTERRUPTED(sp)) { 201341477Svmaffione SLIST_REMOVE_HEAD(sp->gp->ecq, q); 202341477Svmaffione free(ecp->cp); 203341477Svmaffione free(ecp); 204341477Svmaffione break; 205341477Svmaffione } 206341477Svmaffione search_busy(sp, btype); 207341477Svmaffione btype = BUSY_UPDATE; 208285349Sluigi cnt = INTERRUPT_CHECK; 209285349Sluigi } 210341477Svmaffione if (db_get(sp, start, DBG_FATAL, &dbp, &len)) 211341477Svmaffione return (1); 212341477Svmaffione match[0].rm_so = 0; 213341477Svmaffione match[0].rm_eo = len; 214341477Svmaffione switch (eval = 215341477Svmaffione regexec(&sp->re_c, dbp, 0, match, REG_STARTEND)) { 216341477Svmaffione case 0: 217341477Svmaffione if (cmd == V) 218341477Svmaffione continue; 219341477Svmaffione break; 220341477Svmaffione case REG_NOMATCH: 221341477Svmaffione if (cmd == GLOBAL) 222341477Svmaffione continue; 223341477Svmaffione break; 224341477Svmaffione default: 225341477Svmaffione re_error(sp, eval, &sp->re_c); 226341477Svmaffione break; 227285349Sluigi } 228285349Sluigi 229341477Svmaffione /* If follows the last entry, extend the last entry's range. */ 230341477Svmaffione if ((rp = TAILQ_LAST(ecp->rq, _rh)) != NULL && 231341477Svmaffione rp->stop == start - 1) { 232341477Svmaffione ++rp->stop; 233341477Svmaffione continue; 234341477Svmaffione } 235341477Svmaffione 236341477Svmaffione /* Allocate a new range, and append it to the list. */ 237341477Svmaffione CALLOC(sp, rp, RANGE *, 1, sizeof(RANGE)); 238341477Svmaffione if (rp == NULL) 239341477Svmaffione return (1); 240285349Sluigi rp->start = rp->stop = start; 241285349Sluigi TAILQ_INSERT_TAIL(ecp->rq, rp, q); 242341477Svmaffione } 243341477Svmaffione search_busy(sp, BUSY_OFF); 244341477Svmaffione return (0); 245341477Svmaffione} 246341477Svmaffione 247341477Svmaffione/* 248341477Svmaffione * ex_g_insdel -- 249341477Svmaffione * Update the ranges based on an insertion or deletion. 250341477Svmaffione * 251341477Svmaffione * PUBLIC: int ex_g_insdel __P((SCR *, lnop_t, recno_t)); 252285349Sluigi */ 253285349Sluigiint 254341477Svmaffioneex_g_insdel(SCR *sp, lnop_t op, recno_t lno) 255341477Svmaffione{ 256341477Svmaffione EXCMD *ecp; 257341477Svmaffione RANGE *nrp, *rp; 258341477Svmaffione 259285349Sluigi /* All insert/append operations are done as inserts. */ 260341477Svmaffione if (op == LINE_APPEND) 261341477Svmaffione abort(); 262341477Svmaffione 263341477Svmaffione if (op == LINE_RESET) 264341477Svmaffione return (0); 265285349Sluigi 266341477Svmaffione SLIST_FOREACH(ecp, sp->gp->ecq, q) { 267341477Svmaffione if (!FL_ISSET(ecp->agv_flags, AGV_AT | AGV_GLOBAL | AGV_V)) 268341477Svmaffione continue; 269341477Svmaffione TAILQ_FOREACH_SAFE(rp, ecp->rq, q, nrp) { 270341477Svmaffione /* If range less than the line, ignore it. */ 271341477Svmaffione if (rp->stop < lno) 272341477Svmaffione continue; 273341477Svmaffione 274341477Svmaffione /* 275341477Svmaffione * If range greater than the line, decrement or 276341477Svmaffione * increment the range. 277341477Svmaffione */ 278341477Svmaffione if (rp->start > lno) { 279341477Svmaffione if (op == LINE_DELETE) { 280341477Svmaffione --rp->start; 281341477Svmaffione --rp->stop; 282341477Svmaffione } else { 283341477Svmaffione ++rp->start; 284341477Svmaffione ++rp->stop; 285341477Svmaffione } 286341477Svmaffione continue; 287341477Svmaffione } 288341477Svmaffione 289341477Svmaffione /* 290341477Svmaffione * Lno is inside the range, decrement the end point 291341477Svmaffione * for deletion, and split the range for insertion. 292341477Svmaffione * In the latter case, since we're inserting a new 293341477Svmaffione * element, neither range can be exhausted. 294341477Svmaffione */ 295341477Svmaffione if (op == LINE_DELETE) { 296341477Svmaffione if (rp->start > --rp->stop) { 297341477Svmaffione TAILQ_REMOVE(ecp->rq, rp, q); 298341477Svmaffione free(rp); 299341477Svmaffione } 300341477Svmaffione } else { 301341477Svmaffione CALLOC_RET(sp, nrp, RANGE *, 1, sizeof(RANGE)); 302341477Svmaffione nrp->start = lno + 1; 303341477Svmaffione nrp->stop = rp->stop + 1; 304341477Svmaffione rp->stop = lno - 1; 305341477Svmaffione TAILQ_INSERT_AFTER(ecp->rq, rp, nrp, q); 306285349Sluigi } 307285349Sluigi } 308285349Sluigi 309341477Svmaffione /* 310285349Sluigi * If the command deleted/inserted lines, the cursor moves to 311341477Svmaffione * the line after the deleted/inserted line. 312341477Svmaffione */ 313341477Svmaffione ecp->range_lno = lno; 314341477Svmaffione } 315341477Svmaffione return (0); 316285349Sluigi} 317285349Sluigi