ex_global.c revision 254225
118334Speter/*- 290075Sobrien * Copyright (c) 1992, 1993, 1994 3169689Skan * The Regents of the University of California. All rights reserved. 418334Speter * Copyright (c) 1992, 1993, 1994, 1995, 1996 590075Sobrien * Keith Bostic. All rights reserved. 618334Speter * 790075Sobrien * See the LICENSE file for redistribution information. 890075Sobrien */ 990075Sobrien 1090075Sobrien#include "config.h" 1118334Speter 1290075Sobrien#ifndef lint 1390075Sobrienstatic const char sccsid[] = "$Id: ex_global.c,v 10.32 2011/12/26 23:37:01 zy Exp $"; 1490075Sobrien#endif /* not lint */ 1590075Sobrien 1618334Speter#include <sys/types.h> 1790075Sobrien#include <sys/queue.h> 1890075Sobrien#include <sys/time.h> 19169689Skan 20169689Skan#include <bitstring.h> 2118334Speter#include <ctype.h> 2218334Speter#include <errno.h> 2390075Sobrien#include <limits.h> 2490075Sobrien#include <stdio.h> 2590075Sobrien#include <stdlib.h> 2690075Sobrien#include <string.h> 2718334Speter#include <unistd.h> 2890075Sobrien 2990075Sobrien#include "../common/common.h" 3090075Sobrien 3190075Sobrienenum which {GLOBAL, V}; 3290075Sobrien 3390075Sobrienstatic int ex_g_setup __P((SCR *, EXCMD *, enum which)); 3418334Speter 3590075Sobrien/* 3690075Sobrien * ex_global -- [line [,line]] g[lobal][!] /pattern/ [commands] 3718334Speter * Exec on lines matching a pattern. 3818334Speter * 3918334Speter * PUBLIC: int ex_global __P((SCR *, EXCMD *)); 4018334Speter */ 4190075Sobrienint 4290075Sobrienex_global(SCR *sp, EXCMD *cmdp) 4318334Speter{ 4490075Sobrien return (ex_g_setup(sp, 4590075Sobrien cmdp, FL_ISSET(cmdp->iflags, E_C_FORCE) ? V : GLOBAL)); 46117395Skan} 4718334Speter 4890075Sobrien/* 4990075Sobrien * ex_v -- [line [,line]] v /pattern/ [commands] 50117395Skan * Exec on lines not matching a pattern. 5190075Sobrien * 5290075Sobrien * PUBLIC: int ex_v __P((SCR *, EXCMD *)); 53132718Skan */ 5450397Sobrienint 55132718Skanex_v(SCR *sp, EXCMD *cmdp) 56132718Skan{ 5718334Speter return (ex_g_setup(sp, cmdp, V)); 5890075Sobrien} 5990075Sobrien 6018334Speter/* 6152284Sobrien * ex_g_setup -- 6252284Sobrien * Ex global and v commands. 6352284Sobrien */ 6490075Sobrienstatic int 65132718Skanex_g_setup(SCR *sp, EXCMD *cmdp, enum which cmd) 6690075Sobrien{ 6718334Speter CHAR_T *ptrn, *p, *t; 6890075Sobrien EXCMD *ecp; 6990075Sobrien MARK abs; 7090075Sobrien RANGE *rp; 7190075Sobrien busy_t btype; 7290075Sobrien recno_t start, end; 7318334Speter regex_t *re; 7490075Sobrien regmatch_t match[1]; 7590075Sobrien size_t len; 7690075Sobrien int cnt, delim, eval; 7718334Speter CHAR_T *dbp; 7890075Sobrien 7990075Sobrien NEEDFILE(sp, cmdp); 8090075Sobrien 8190075Sobrien if (F_ISSET(sp, SC_EX_GLOBAL)) { 8218334Speter msgq_wstr(sp, M_ERR, cmdp->cmd->name, 8390075Sobrien "124|The %s command can't be used as part of a global or v command"); 8490075Sobrien return (1); 8590075Sobrien } 86169689Skan 8790075Sobrien /* 8890075Sobrien * Skip leading white space. Historic vi allowed any non-alphanumeric 89169689Skan * to serve as the global command delimiter. 9090075Sobrien */ 9190075Sobrien if (cmdp->argc == 0) 9290075Sobrien goto usage; 9318334Speter for (p = cmdp->argv[0]->bp; cmdskip(*p); ++p); 9490075Sobrien if (!isascii(*p) || *p == '\0' || isalnum(*p) || 9590075Sobrien *p == '\\' || *p == '|' || *p == '\n') { 96169689Skanusage: ex_emsg(sp, cmdp->cmd->usage, EXM_USAGE); 9790075Sobrien return (1); 9890075Sobrien } 9990075Sobrien delim = *p++; 10090075Sobrien 10190075Sobrien /* 10290075Sobrien * Get the pattern string, toss escaped characters. 103169689Skan * 104169689Skan * QUOTING NOTE: 10590075Sobrien * Only toss an escaped character if it escapes a delimiter. 10690075Sobrien */ 10790075Sobrien for (ptrn = t = p;;) { 10890075Sobrien if (p[0] == '\0' || p[0] == delim) { 10990075Sobrien if (p[0] == delim) 11090075Sobrien ++p; 11190075Sobrien /* 11290075Sobrien * !!! 11390075Sobrien * Nul terminate the pattern string -- it's passed 11490075Sobrien * to regcomp which doesn't understand anything else. 11590075Sobrien */ 11690075Sobrien *t = '\0'; 11790075Sobrien break; 11890075Sobrien } 11990075Sobrien if (p[0] == '\\') 12090075Sobrien if (p[1] == delim) 12190075Sobrien ++p; 12290075Sobrien else if (p[1] == '\\') 12390075Sobrien *t++ = *p++; 12418334Speter *t++ = *p++; 12518334Speter } 12690075Sobrien 12790075Sobrien /* If the pattern string is empty, use the last one. */ 12890075Sobrien if (*ptrn == '\0') { 12990075Sobrien if (sp->re == NULL) { 13090075Sobrien ex_emsg(sp, NULL, EXM_NOPREVRE); 13190075Sobrien return (1); 13290075Sobrien } 13390075Sobrien 13490075Sobrien /* Re-compile the RE if necessary. */ 13590075Sobrien if (!F_ISSET(sp, SC_RE_SEARCH) && 13618334Speter re_compile(sp, sp->re, sp->re_len, 13718334Speter NULL, NULL, &sp->re_c, RE_C_SEARCH)) 13890075Sobrien return (1); 13918334Speter } else { 14018334Speter /* Compile the RE. */ 14190075Sobrien if (re_compile(sp, ptrn, t - ptrn, &sp->re, 14218334Speter &sp->re_len, &sp->re_c, RE_C_SEARCH)) 14318334Speter return (1); 14418334Speter 14590075Sobrien /* 14690075Sobrien * Set saved RE. Historic practice is that globals set 14790075Sobrien * direction as well as the RE. 14818334Speter */ 14990075Sobrien sp->searchdir = FORWARD; 15090075Sobrien } 15190075Sobrien re = &sp->re_c; 15218334Speter 15390075Sobrien /* The global commands always set the previous context mark. */ 15490075Sobrien abs.lno = sp->lno; 15518334Speter abs.cno = sp->cno; 15618334Speter if (mark_set(sp, ABSMARK1, &abs, 1)) 15718334Speter return (1); 15818334Speter 15918334Speter /* Get an EXCMD structure. */ 16018334Speter CALLOC_RET(sp, ecp, EXCMD *, 1, sizeof(EXCMD)); 16118334Speter TAILQ_INIT(ecp->rq); 16218334Speter 16318334Speter /* 16418334Speter * Get a copy of the command string; the default command is print. 16518334Speter * Don't worry about a set of <blank>s with no command, that will 16618334Speter * default to print in the ex parser. We need to have two copies 16790075Sobrien * because the ex parser may step on the command string when it's 16890075Sobrien * parsing it. 16990075Sobrien */ 17090075Sobrien if ((len = cmdp->argv[0]->len - (p - cmdp->argv[0]->bp)) == 0) { 17190075Sobrien p = L("p"); 17290075Sobrien len = 1; 17318334Speter } 174169689Skan 17518334Speter MALLOC_RET(sp, ecp->cp, CHAR_T *, (len * 2) * sizeof(CHAR_T)); 176169689Skan ecp->o_cp = ecp->cp; 177169689Skan ecp->o_clen = len; 178169689Skan MEMCPY(ecp->cp + len, p, len); 179169689Skan ecp->range_lno = OOBLNO; 180169689Skan FL_SET(ecp->agv_flags, cmd == GLOBAL ? AGV_GLOBAL : AGV_V); 181169689Skan SLIST_INSERT_HEAD(sp->gp->ecq, ecp, q); 182169689Skan 183169689Skan /* 184169689Skan * For each line... The semantics of global matching are that we first 185169689Skan * have to decide which lines are going to get passed to the command, 186169689Skan * and then pass them to the command, ignoring other changes. There's 187169689Skan * really no way to do this in a single pass, since arbitrary line 188169689Skan * creation, deletion and movement can be done in the ex command. For 189169689Skan * example, a good vi clone test is ":g/X/mo.-3", or "g/X/.,.+1d". 190169689Skan * What we do is create linked list of lines that are tracked through 191169689Skan * each ex command. There's a callback routine which the DB interface 192169689Skan * routines call when a line is created or deleted. This doesn't help 193169689Skan * the layering much. 194169689Skan */ 195169689Skan btype = BUSY_ON; 196169689Skan cnt = INTERRUPT_CHECK; 197169689Skan for (start = cmdp->addr1.lno, 198169689Skan end = cmdp->addr2.lno; start <= end; ++start) { 199169689Skan if (cnt-- == 0) { 200169689Skan if (INTERRUPTED(sp)) { 201169689Skan SLIST_REMOVE_HEAD(sp->gp->ecq, q); 202169689Skan free(ecp->cp); 203169689Skan free(ecp); 204169689Skan break; 205169689Skan } 206169689Skan search_busy(sp, btype); 207169689Skan btype = BUSY_UPDATE; 208169689Skan cnt = INTERRUPT_CHECK; 209169689Skan } 210169689Skan if (db_get(sp, start, DBG_FATAL, &dbp, &len)) 211169689Skan return (1); 212169689Skan match[0].rm_so = 0; 213169689Skan match[0].rm_eo = len; 214169689Skan switch (eval = 215169689Skan regexec(&sp->re_c, dbp, 0, match, REG_STARTEND)) { 216169689Skan case 0: 217169689Skan if (cmd == V) 218169689Skan continue; 219169689Skan break; 220169689Skan case REG_NOMATCH: 221169689Skan if (cmd == GLOBAL) 222169689Skan continue; 223169689Skan break; 224169689Skan default: 225169689Skan re_error(sp, eval, &sp->re_c); 226169689Skan break; 227169689Skan } 228169689Skan 229169689Skan /* If follows the last entry, extend the last entry's range. */ 230169689Skan if ((rp = TAILQ_LAST(ecp->rq, _rh)) != NULL && 231169689Skan rp->stop == start - 1) { 232169689Skan ++rp->stop; 233169689Skan continue; 23418334Speter } 235169689Skan 236169689Skan /* Allocate a new range, and append it to the list. */ 237169689Skan CALLOC(sp, rp, RANGE *, 1, sizeof(RANGE)); 238169689Skan if (rp == NULL) 23918334Speter return (1); 240169689Skan rp->start = rp->stop = start; 241169689Skan TAILQ_INSERT_TAIL(ecp->rq, rp, q); 242169689Skan } 243169689Skan search_busy(sp, BUSY_OFF); 244169689Skan return (0); 245169689Skan} 246169689Skan 247169689Skan/* 24818334Speter * ex_g_insdel -- 249169689Skan * Update the ranges based on an insertion or deletion. 250169689Skan * 251169689Skan * PUBLIC: int ex_g_insdel __P((SCR *, lnop_t, recno_t)); 252169689Skan */ 253169689Skanint 254169689Skanex_g_insdel(SCR *sp, lnop_t op, recno_t lno) 255169689Skan{ 256169689Skan EXCMD *ecp; 257169689Skan RANGE *nrp, *rp; 258169689Skan 259169689Skan /* All insert/append operations are done as inserts. */ 26090075Sobrien if (op == LINE_APPEND) 261169689Skan abort(); 262169689Skan 263169689Skan if (op == LINE_RESET) 264169689Skan return (0); 265169689Skan 266169689Skan SLIST_FOREACH(ecp, sp->gp->ecq, q) { 267169689Skan if (!FL_ISSET(ecp->agv_flags, AGV_AT | AGV_GLOBAL | AGV_V)) 268169689Skan continue; 269169689Skan TAILQ_FOREACH_SAFE(rp, ecp->rq, q, nrp) { 270169689Skan /* If range less than the line, ignore it. */ 27190075Sobrien if (rp->stop < lno) 272169689Skan continue; 273169689Skan 274169689Skan /* 275169689Skan * If range greater than the line, decrement or 276169689Skan * increment the range. 277169689Skan */ 278169689Skan if (rp->start > lno) { 279169689Skan if (op == LINE_DELETE) { 280169689Skan --rp->start; 281169689Skan --rp->stop; 282169689Skan } else { 283169689Skan ++rp->start; 284169689Skan ++rp->stop; 285169689Skan } 286169689Skan continue; 287169689Skan } 288169689Skan 289169689Skan /* 290169689Skan * Lno is inside the range, decrement the end point 291169689Skan * for deletion, and split the range for insertion. 292169689Skan * In the latter case, since we're inserting a new 293169689Skan * element, neither range can be exhausted. 294169689Skan */ 295169689Skan if (op == LINE_DELETE) { 296169689Skan if (rp->start > --rp->stop) { 297169689Skan TAILQ_REMOVE(ecp->rq, rp, q); 298169689Skan free(rp); 299169689Skan } 300169689Skan } else { 301169689Skan CALLOC_RET(sp, nrp, RANGE *, 1, sizeof(RANGE)); 302169689Skan nrp->start = lno + 1; 303169689Skan nrp->stop = rp->stop + 1; 304169689Skan rp->stop = lno - 1; 305169689Skan TAILQ_INSERT_AFTER(ecp->rq, rp, nrp, q); 306169689Skan } 307169689Skan } 308169689Skan 309169689Skan /* 310169689Skan * If the command deleted/inserted lines, the cursor moves to 311169689Skan * the line after the deleted/inserted line. 312169689Skan */ 313169689Skan ecp->range_lno = lno; 314169689Skan } 315169689Skan return (0); 316169689Skan} 317169689Skan