119304Speter/*- 219304Speter * Copyright (c) 1992, 1993, 1994 319304Speter * The Regents of the University of California. All rights reserved. 419304Speter * Copyright (c) 1992, 1993, 1994, 1995, 1996 519304Speter * Keith Bostic. All rights reserved. 619304Speter * 719304Speter * See the LICENSE file for redistribution information. 819304Speter */ 919304Speter 1019304Speter#include "config.h" 1119304Speter 1219304Speter#ifndef lint 13254225Speterstatic const char sccsid[] = "$Id: ex_global.c,v 10.32 2011/12/26 23:37:01 zy Exp $"; 1419304Speter#endif /* not lint */ 1519304Speter 1619304Speter#include <sys/types.h> 1719304Speter#include <sys/queue.h> 18254225Speter#include <sys/time.h> 1919304Speter 2019304Speter#include <bitstring.h> 2119304Speter#include <ctype.h> 2219304Speter#include <errno.h> 2319304Speter#include <limits.h> 2419304Speter#include <stdio.h> 2519304Speter#include <stdlib.h> 2619304Speter#include <string.h> 2719304Speter#include <unistd.h> 2819304Speter 2919304Speter#include "../common/common.h" 3019304Speter 3119304Speterenum which {GLOBAL, V}; 3219304Speter 3319304Speterstatic int ex_g_setup __P((SCR *, EXCMD *, enum which)); 3419304Speter 3519304Speter/* 3619304Speter * ex_global -- [line [,line]] g[lobal][!] /pattern/ [commands] 3719304Speter * Exec on lines matching a pattern. 3819304Speter * 3919304Speter * PUBLIC: int ex_global __P((SCR *, EXCMD *)); 4019304Speter */ 4119304Speterint 42254225Speterex_global(SCR *sp, EXCMD *cmdp) 4319304Speter{ 4419304Speter return (ex_g_setup(sp, 4519304Speter cmdp, FL_ISSET(cmdp->iflags, E_C_FORCE) ? V : GLOBAL)); 4619304Speter} 4719304Speter 4819304Speter/* 4919304Speter * ex_v -- [line [,line]] v /pattern/ [commands] 5019304Speter * Exec on lines not matching a pattern. 5119304Speter * 5219304Speter * PUBLIC: int ex_v __P((SCR *, EXCMD *)); 5319304Speter */ 5419304Speterint 55254225Speterex_v(SCR *sp, EXCMD *cmdp) 5619304Speter{ 5719304Speter return (ex_g_setup(sp, cmdp, V)); 5819304Speter} 5919304Speter 6019304Speter/* 6119304Speter * ex_g_setup -- 6219304Speter * Ex global and v commands. 6319304Speter */ 6419304Speterstatic int 65254225Speterex_g_setup(SCR *sp, EXCMD *cmdp, enum which cmd) 6619304Speter{ 6719304Speter CHAR_T *ptrn, *p, *t; 6819304Speter EXCMD *ecp; 6919304Speter MARK abs; 7019304Speter RANGE *rp; 7119304Speter busy_t btype; 7219304Speter recno_t start, end; 7319304Speter regex_t *re; 7419304Speter regmatch_t match[1]; 7519304Speter size_t len; 7619304Speter int cnt, delim, eval; 77254225Speter CHAR_T *dbp; 7819304Speter 7919304Speter NEEDFILE(sp, cmdp); 8019304Speter 8119304Speter if (F_ISSET(sp, SC_EX_GLOBAL)) { 82254225Speter msgq_wstr(sp, M_ERR, cmdp->cmd->name, 83254225Speter "124|The %s command can't be used as part of a global or v command"); 8419304Speter return (1); 8519304Speter } 8619304Speter 8719304Speter /* 8819304Speter * Skip leading white space. Historic vi allowed any non-alphanumeric 8919304Speter * to serve as the global command delimiter. 9019304Speter */ 9119304Speter if (cmdp->argc == 0) 9219304Speter goto usage; 93254225Speter for (p = cmdp->argv[0]->bp; cmdskip(*p); ++p); 94254225Speter if (!isascii(*p) || *p == '\0' || isalnum(*p) || 9519304Speter *p == '\\' || *p == '|' || *p == '\n') { 9619304Speterusage: ex_emsg(sp, cmdp->cmd->usage, EXM_USAGE); 9719304Speter return (1); 9819304Speter } 9919304Speter delim = *p++; 10019304Speter 10119304Speter /* 10219304Speter * Get the pattern string, toss escaped characters. 10319304Speter * 10419304Speter * QUOTING NOTE: 10519304Speter * Only toss an escaped character if it escapes a delimiter. 10619304Speter */ 10719304Speter for (ptrn = t = p;;) { 10819304Speter if (p[0] == '\0' || p[0] == delim) { 10919304Speter if (p[0] == delim) 11019304Speter ++p; 11119304Speter /* 11219304Speter * !!! 11319304Speter * Nul terminate the pattern string -- it's passed 11419304Speter * to regcomp which doesn't understand anything else. 11519304Speter */ 11619304Speter *t = '\0'; 11719304Speter break; 11819304Speter } 11919304Speter if (p[0] == '\\') 12019304Speter if (p[1] == delim) 12119304Speter ++p; 12219304Speter else if (p[1] == '\\') 12319304Speter *t++ = *p++; 12419304Speter *t++ = *p++; 12519304Speter } 12619304Speter 12719304Speter /* If the pattern string is empty, use the last one. */ 12819304Speter if (*ptrn == '\0') { 12919304Speter if (sp->re == NULL) { 13019304Speter ex_emsg(sp, NULL, EXM_NOPREVRE); 13119304Speter return (1); 13219304Speter } 13319304Speter 13419304Speter /* Re-compile the RE if necessary. */ 135254225Speter if (!F_ISSET(sp, SC_RE_SEARCH) && 136254225Speter re_compile(sp, sp->re, sp->re_len, 137254225Speter NULL, NULL, &sp->re_c, RE_C_SEARCH)) 13819304Speter return (1); 13919304Speter } else { 14019304Speter /* Compile the RE. */ 141254225Speter if (re_compile(sp, ptrn, t - ptrn, &sp->re, 142254225Speter &sp->re_len, &sp->re_c, RE_C_SEARCH)) 14319304Speter return (1); 14419304Speter 14519304Speter /* 14619304Speter * Set saved RE. Historic practice is that globals set 14719304Speter * direction as well as the RE. 14819304Speter */ 14919304Speter sp->searchdir = FORWARD; 15019304Speter } 15119304Speter re = &sp->re_c; 15219304Speter 15319304Speter /* The global commands always set the previous context mark. */ 15419304Speter abs.lno = sp->lno; 15519304Speter abs.cno = sp->cno; 15619304Speter if (mark_set(sp, ABSMARK1, &abs, 1)) 15719304Speter return (1); 15819304Speter 15919304Speter /* Get an EXCMD structure. */ 16019304Speter CALLOC_RET(sp, ecp, EXCMD *, 1, sizeof(EXCMD)); 161254225Speter TAILQ_INIT(ecp->rq); 16219304Speter 16319304Speter /* 16419304Speter * Get a copy of the command string; the default command is print. 16519304Speter * Don't worry about a set of <blank>s with no command, that will 16619304Speter * default to print in the ex parser. We need to have two copies 16719304Speter * because the ex parser may step on the command string when it's 16819304Speter * parsing it. 16919304Speter */ 17019304Speter if ((len = cmdp->argv[0]->len - (p - cmdp->argv[0]->bp)) == 0) { 171254225Speter p = L("p"); 17219304Speter len = 1; 17319304Speter } 17419304Speter 175254225Speter MALLOC_RET(sp, ecp->cp, CHAR_T *, (len * 2) * sizeof(CHAR_T)); 17619304Speter ecp->o_cp = ecp->cp; 17719304Speter ecp->o_clen = len; 178254225Speter MEMCPY(ecp->cp + len, p, len); 17919304Speter ecp->range_lno = OOBLNO; 18019304Speter FL_SET(ecp->agv_flags, cmd == GLOBAL ? AGV_GLOBAL : AGV_V); 181254225Speter SLIST_INSERT_HEAD(sp->gp->ecq, ecp, q); 18219304Speter 18319304Speter /* 18419304Speter * For each line... The semantics of global matching are that we first 18519304Speter * have to decide which lines are going to get passed to the command, 18619304Speter * and then pass them to the command, ignoring other changes. There's 18719304Speter * really no way to do this in a single pass, since arbitrary line 18819304Speter * creation, deletion and movement can be done in the ex command. For 18919304Speter * example, a good vi clone test is ":g/X/mo.-3", or "g/X/.,.+1d". 19019304Speter * What we do is create linked list of lines that are tracked through 19119304Speter * each ex command. There's a callback routine which the DB interface 19219304Speter * routines call when a line is created or deleted. This doesn't help 19319304Speter * the layering much. 19419304Speter */ 19519304Speter btype = BUSY_ON; 19619304Speter cnt = INTERRUPT_CHECK; 19719304Speter for (start = cmdp->addr1.lno, 19819304Speter end = cmdp->addr2.lno; start <= end; ++start) { 19919304Speter if (cnt-- == 0) { 20019304Speter if (INTERRUPTED(sp)) { 201254225Speter SLIST_REMOVE_HEAD(sp->gp->ecq, q); 20219304Speter free(ecp->cp); 20319304Speter free(ecp); 20419304Speter break; 20519304Speter } 20619304Speter search_busy(sp, btype); 20719304Speter btype = BUSY_UPDATE; 20819304Speter cnt = INTERRUPT_CHECK; 20919304Speter } 21019304Speter if (db_get(sp, start, DBG_FATAL, &dbp, &len)) 21119304Speter return (1); 21219304Speter match[0].rm_so = 0; 21319304Speter match[0].rm_eo = len; 21419304Speter switch (eval = 21519304Speter regexec(&sp->re_c, dbp, 0, match, REG_STARTEND)) { 21619304Speter case 0: 21719304Speter if (cmd == V) 21819304Speter continue; 21919304Speter break; 22019304Speter case REG_NOMATCH: 22119304Speter if (cmd == GLOBAL) 22219304Speter continue; 22319304Speter break; 22419304Speter default: 22519304Speter re_error(sp, eval, &sp->re_c); 22619304Speter break; 22719304Speter } 22819304Speter 22919304Speter /* If follows the last entry, extend the last entry's range. */ 230254225Speter if ((rp = TAILQ_LAST(ecp->rq, _rh)) != NULL && 23119304Speter rp->stop == start - 1) { 23219304Speter ++rp->stop; 23319304Speter continue; 23419304Speter } 23519304Speter 23619304Speter /* Allocate a new range, and append it to the list. */ 23719304Speter CALLOC(sp, rp, RANGE *, 1, sizeof(RANGE)); 23819304Speter if (rp == NULL) 23919304Speter return (1); 24019304Speter rp->start = rp->stop = start; 241254225Speter TAILQ_INSERT_TAIL(ecp->rq, rp, q); 24219304Speter } 24319304Speter search_busy(sp, BUSY_OFF); 24419304Speter return (0); 24519304Speter} 24619304Speter 24719304Speter/* 24819304Speter * ex_g_insdel -- 24919304Speter * Update the ranges based on an insertion or deletion. 25019304Speter * 25119304Speter * PUBLIC: int ex_g_insdel __P((SCR *, lnop_t, recno_t)); 25219304Speter */ 25319304Speterint 254254225Speterex_g_insdel(SCR *sp, lnop_t op, recno_t lno) 25519304Speter{ 25619304Speter EXCMD *ecp; 25719304Speter RANGE *nrp, *rp; 25819304Speter 25919304Speter /* All insert/append operations are done as inserts. */ 26019304Speter if (op == LINE_APPEND) 26119304Speter abort(); 26219304Speter 26319304Speter if (op == LINE_RESET) 26419304Speter return (0); 26519304Speter 266254225Speter SLIST_FOREACH(ecp, sp->gp->ecq, q) { 26719304Speter if (!FL_ISSET(ecp->agv_flags, AGV_AT | AGV_GLOBAL | AGV_V)) 26819304Speter continue; 269254225Speter TAILQ_FOREACH_SAFE(rp, ecp->rq, q, nrp) { 27019304Speter /* If range less than the line, ignore it. */ 27119304Speter if (rp->stop < lno) 27219304Speter continue; 27319304Speter 27419304Speter /* 27519304Speter * If range greater than the line, decrement or 27619304Speter * increment the range. 27719304Speter */ 27819304Speter if (rp->start > lno) { 27919304Speter if (op == LINE_DELETE) { 28019304Speter --rp->start; 28119304Speter --rp->stop; 28219304Speter } else { 28319304Speter ++rp->start; 28419304Speter ++rp->stop; 28519304Speter } 28619304Speter continue; 28719304Speter } 28819304Speter 28919304Speter /* 29019304Speter * Lno is inside the range, decrement the end point 29119304Speter * for deletion, and split the range for insertion. 29219304Speter * In the latter case, since we're inserting a new 29319304Speter * element, neither range can be exhausted. 29419304Speter */ 29519304Speter if (op == LINE_DELETE) { 29619304Speter if (rp->start > --rp->stop) { 297254225Speter TAILQ_REMOVE(ecp->rq, rp, q); 29819304Speter free(rp); 29919304Speter } 30019304Speter } else { 30119304Speter CALLOC_RET(sp, nrp, RANGE *, 1, sizeof(RANGE)); 30219304Speter nrp->start = lno + 1; 30319304Speter nrp->stop = rp->stop + 1; 30419304Speter rp->stop = lno - 1; 305254225Speter TAILQ_INSERT_AFTER(ecp->rq, rp, nrp, q); 30619304Speter } 30719304Speter } 30819304Speter 30919304Speter /* 31019304Speter * If the command deleted/inserted lines, the cursor moves to 31119304Speter * the line after the deleted/inserted line. 31219304Speter */ 31319304Speter ecp->range_lno = lno; 31419304Speter } 31519304Speter return (0); 31619304Speter} 317