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