Skip to content
Newer
Older
100644 777 lines (682 sloc) 20 KB
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
1 /* exsubst.c */
2 /* Copyright 1995 by Steve Kirkendall */
3
4
5 /* This file contains code which implements the :s command. The most
6 * challenging variation of that command is the interactive form -- specified
7 * via the 'c' flag. Because elvis is event driven, the only way to implement
8 * this is via a whole new edit mode which interprets <y> as a command to
9 * replace-and-search, and most other keystrokes as a search command (without
10 * replacing). The :s command itself will simply set this up, push the
11 * input state onto the stack, and return.
12 *
13 * On the other hand, non-interactive versions need to return a "failed"
14 * exit status if there is no match, so we can't simply run the interactive
15 * code with simulated <y> keystrokes. We need to have a separate version of
16 * the search-and-replace loop.
17 *
18 * In this file, we have a "findnext" function and a "dosubst" function.
19 * These are used by the non-interactive :s command, and the interactive
20 * version's key processor -- both of which are also in this file.
21 */
22
23 #include "elvis.h"
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
24 #ifdef FEATURE_RCSID
25 char id_exsubst[] = "$Id: exsubst.c,v 2.22 2003/10/17 17:41:23 steve Exp $";
26 #endif
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
27
28 typedef struct
29 {
30 /* THE FOLLOWING ARE RETAINED BETWEEN SEARCHES */
31 /* old text and new text */
32 regexp *re; /* text to search for */
33 CHAR *rplc; /* replacement text */
34
35 /* options */
36 PFLAG pflag; /* printing flag */
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
37 ELVBOOL global; /* boolean: substitute globally in line? */
38 ELVBOOL execute; /* boolean: execute instead of substitute? */
39 ELVBOOL confirm; /* boolean: ask for confirmation first? */
40 ELVBOOL errors; /* boolean: is a failed search an error? */
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
41 long instance;/* numeric: which instance in each line to sub */
42
43 /* THE FOLLOWING ARE RESET FOR EACH SEARCH */
44 /* range of lines to check */
45 WINDOW win; /* window */
46 MARK from; /* current line */
47 MARK to; /* end of last line to check */
48
49 /* intermediate results */
50 long thisinst;/* instance of current match within current line */
51 CHAR key; /* keystroke */
52 long printoff;/* offset of line to be printed, or -1 */
53
54 /* results */
55 long chline; /* # of lines changed */
56 long chsub; /* # of substitutions made */
57 long cursoff;/* offset of last change, so cursor can be left there */
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
58 ELVBOOL ex; /* revert to ex mode afterward? */
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
59 } subst_t;
60
61 static void print P_((subst_t *subst));
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
62 static ELVBOOL findnext P_((subst_t *subst));
63 static ELVBOOL dosubst P_((WINDOW win, subst_t *subst));
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
64 static RESULT parse P_((_CHAR_ key, void *info));
65 static RESULT perform P_((WINDOW win));
66 static ELVCURSOR shape P_((WINDOW win));
67
68
69 /* Print the line containing the most recent change. This is used when the
70 * :s command is invoked with print flags.
71 */
72 static void print(subst)
73 subst_t *subst;
74 {
75 MARK pline;
76 long fromoff;
77
78 /* If supposed to print, then do so */
79 if (subst->pflag == PF_NONE)
80 return;
81
82 /* Find the start of the line to be printed. If 'printoff' is >= 0
83 * then use it as the the starting point; else use the from field.
84 */
85 if (subst->printoff >= 0)
86 {
87 fromoff = markoffset(subst->from);
88 marksetoffset(subst->from, subst->printoff);
89 pline = (*subst->win->md->move)(subst->win,
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
90 subst->from, 0L, 0L, ElvFalse);
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
91 marksetoffset(subst->from, fromoff);
92 subst->printoff = -1L;
93 }
94 else
95 {
96 pline = (*subst->win->md->move)(subst->win,
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
97 subst->from, 0L, 0L, ElvFalse);
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
98 }
99
100 /* print the line */
101 exprintlines(subst->win, pline, 1L, subst->pflag);
102 }
103
104 /* Find the next matching instance for a given substitution. If one is found,
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
105 * return ElvTrue; else return ElvFalse. When a match is found, the contents of
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
106 * the subst argument are modified to reflect its location. In particular,
107 * subst->re->startp[0] gives its start offset, and subst->re->end[0] gives
108 * its end offset.
109 */
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
110 static ELVBOOL findnext(subst)
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
111 subst_t *subst; /* info about the substitution */
112 {
113 /* if previous line was supposed to be printed but hasn't been,
114 * then print it now.
115 */
116 if (subst->printoff >= 0)
117 print(subst);
118
119 /* for each line remaining in the range... */
120 while (markoffset(subst->from) < markoffset(subst->to))
121 {
122 /* if the user is impatient, then stop */
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
123 if (guipoll(ElvFalse))
124 return ElvFalse;
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
125
126 /* for each instance within the line... */
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
127 while (regexec(subst->re, subst->from, (ELVBOOL)(subst->thisinst == 0))
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
128 && (subst->global || subst->thisinst < subst->instance))
129 {
130 /* increment the instance counter */
131 subst->thisinst++;
132
133 /* if this is an instance we care about... */
134 if (subst->global || subst->thisinst == subst->instance)
135 {
136 /* prepare for next loop */
137 if (subst->global)
138 {
139 marksetoffset(subst->from,
140 subst->re->endp[0]);
141
142 /* For global substitutions, we need to
143 * be careful about regexps which can
144 * match zero-length text. Specifically,
145 * we need to require at least one
146 * non-matching character between
147 * matches, otherwise we'd just get in
148 * an infinite loop replacing nothing
149 * with something at the same location.
150 */
151 if (subst->re->minlen == 0)
152 {
153 if (scanchar(subst->from) == '\n')
154 subst->thisinst = 0;
155 markaddoffset(subst->from, 1L);
156 }
157 }
158 else /* single instance in each line */
159 {
160 /* remember that we'll need to print
161 * this line after the substitution.
162 */
163 subst->printoff = markoffset(subst->from);
164 /* Increment the chline counter */
165 subst->chline++;
166
167 /* move to start of the next line */
168 marksetoffset(subst->from,
169 subst->re->nextlinep);
170 subst->thisinst = 0;
171 }
172
173
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
174 /* Return ElvTrue to do substitution */
175 return ElvTrue;
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
176 }
177
178 /* Move "posn" to the end of the matched region. If
179 * the regexp could conceivably match a zero-length
180 * string, then skip one character.
181 */
182 marksetoffset(subst->from, subst->re->endp[0]);
183 if (subst->re->minlen == 0)
184 {
185 markaddoffset(subst->from, 1);/*!!!*/
186 if (scanchar(subst->from) == '\n')
187 break;
188 }
189 }
190
191 /* if any matches were found, then increment chline */
192 if (subst->global ? subst->thisinst > 0 : subst->thisinst == subst->instance)
193 {
194 subst->chline++;
195
196 /* print the changed line, if we're supposed to */
197 print(subst);
198 }
199
200 /* move forward to the start of the next line */
201 marksetoffset(subst->from, subst->re->nextlinep);
202 subst->thisinst = 0L;
203 }
204
205 /* if last line was supposed to be printed but hasn't been,
206 * then print it now.
207 */
208 if (subst->printoff >= 0)
209 print(subst);
210
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
211 return ElvFalse;
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
212 }
213
214
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
215 /* Perform a substitution, or if subst->execute is ElvTrue then execute the
216 * text which would have been substituted. Returns ElvTrue if successful, or
217 * ElvFalse if there is an error in the replacement text.
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
218 */
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
219 static ELVBOOL dosubst(win, subst)
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
220 WINDOW win;
221 subst_t *subst; /* info about the substitution */
222 {
223 CHAR *newtext;
224 MARK oldcursor;
225 MARK cursor;
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
226 ELVBOOL oldsaveregexp;
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
227
228 /* remember the offset of this change so we can move the cursor later */
229 subst->cursoff = subst->re->startp[0];
230
231 /* Either execute the replacement, or perform the substitution */
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
232 newtext = regsub(subst->re, subst->rplc, (ELVBOOL)!subst->execute);
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
233 if (!newtext)
234 {
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
235 return ElvFalse;
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
236 }
237 if (subst->execute)
238 {
239 /* move the window's cursor to the matching line */
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
240 if (win)
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
241 {
242 assert(subst->re->buffer == markbuffer(subst->from));
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
243 if (win->state->pop)
244 cursor = win->state->pop->cursor;
245 else
246 cursor = win->cursor;
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
247 oldcursor = markdup(cursor);
248 marksetoffset(cursor, subst->re->startp[0]);
249 marksetbuffer(cursor, subst->re->buffer);
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
250 bufoptions(subst->re->buffer);
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
251 }
252 else
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
253 cursor = oldcursor = NULL;
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
254
255 /* temporarily turn off the saveregexp option */
256 oldsaveregexp = o_saveregexp;
257 #if 0
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
258 o_saveregexp = ElvFalse;
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
259 #endif
260
261 /* execute the command */
3a9bb55 @mbert Import Elvis 2.1_3 (written by Steve Kirkendall)
authored
262 exstring(win, newtext, NULL);
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
263
264 /* restore the saveregexp option */
265 o_saveregexp = oldsaveregexp;
266
267 /* move the cursor back where it was before */
268 if (oldcursor)
269 {
270 marksetoffset(cursor, markoffset(oldcursor));
271 marksetbuffer(cursor, markbuffer(oldcursor));
272 markfree(oldcursor);
273 }
274 }
275 safefree(newtext);
276
277 /* increment the substitution change counter */
278 subst->chsub++;
279
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
280 return ElvTrue;
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
281 }
282
283
284 /* If key is <Esc> then cancel the change. If <n> or <N> then skip this change
285 * but continue to the next match. Any other key performs the substitution and
286 * continues to the next match.
287 */
288 static RESULT parse(key, info)
289 _CHAR_ key;
290 void *info;
291 {
292 subst_t *subst = (subst_t *)info;
293 WINDOW win;
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
294 #ifdef FEATURE_V
295 VIINFO vinf;
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
296
297 /* unhighlight the previous match */
298 vinf.command = ELVCTRL('[');
299 v_visible(subst->win, &vinf);
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
300 #endif
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
301
302 /* be lenient about keystrokes */
303 if (key == 'N' || key == 'n')
304 key = 'n';
305 else if (key == ELVCTRL('[') || key == ELVCTRL('C'))
306 key = ELVCTRL('[');
307 else
308 key = 'y';
309
310 /* do the required steps */
311 switch (key)
312 {
313 case 'y':
314 /* perform this substitution */
315 if (!dosubst(subst->win, subst))
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
316 {
317 markbuffer(subst->win->cursor)->willdo = ElvFalse;
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
318 return RESULT_ERROR;
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
319 }
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
320 /* fall through... */
321
322 case 'n':
323 /* find the next match */
324 if (findnext(subst))
325 {
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
326 #ifdef FEATURE_V
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
327 /* highlight the next match */
328 if (subst->re->endp[0] > subst->re->startp[0])
329 {
330 marksetoffset(subst->win->cursor, subst->re->endp[0] - 1);
331 vinf.command = 'v';
332 v_visible(subst->win, &vinf);
333 marksetoffset(subst->win->cursor, subst->re->startp[0]);
334 v_visible(subst->win, NULL);
335 }
336 else /* matching text is 0 characters long */
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
337 #endif /* FEATURE_V */
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
338 {
339 /* at least move the cursor there */
340 marksetoffset(subst->win->cursor, subst->re->startp[0]);
341 }
342
343 return RESULT_MORE;
344 }
345 }
346
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
347 /* Either the user canceled or there are no other matches, so we're
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
348 * done. Announce the results.
349 */
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
350 if ((subst->chline >= o_report && o_report != 0) || subst->confirm)
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
351 {
352 msg(MSG_INFO, "[dd]$1 substitutions on $2 lines", subst->chsub, subst->chline);
353 }
354
355 /* Free the marks. */
356 markfree(subst->from);
357 markfree(subst->to);
358 safefree(subst->rplc);
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
359 #ifdef FEATURE_AUTOCMD
360 markbuffer(subst->win->cursor)->eachedit = ElvFalse;
361 #endif
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
362
363 /* Either pop the confirm state (if we started it from visual mode)
364 * or replace the confirm state with an ex state (if we started from
365 * ex mode).
366 */
367 if (subst->ex)
368 {
369 /* Pop off the "confirm" state right away, and them push an
370 * "ex" stratum.
371 */
372 win = subst->win;
373 statepop(win);
374 statestratum(win, toCHAR(EX_BUF), ':', exenter);
375 win->state->flags &= ~(ELVIS_POP|ELVIS_ONCE|ELVIS_1LINE);
376 }
377 else
378 {
379 /* Pop off the "confirm" state so we return to the "vi" state */
380 subst->win->state->flags |= ELVIS_POP;
381 }
382 return RESULT_COMPLETE;
383 }
384
385 static RESULT perform(win)
386 WINDOW win;
387 {
388 return RESULT_COMPLETE;
389 }
390
391 /* Choose a cursor shape to use during confirmation */
392 static ELVCURSOR shape(win)
393 WINDOW win;
394 {
395 regexp *re = ((subst_t *)win->state->info)->re;
396 return re->startp[0] == re->endp[0] ? CURSOR_INSERT : CURSOR_REPLACE;
397 }
398
399 /* This function implements the :substitute command, and the :& and :~
400 * variations of that command. It is also used to perform the real work
401 * of the visual <&> command.
402 */
403 RESULT ex_substitute(xinf)
404 EXINFO *xinf;
405 {
406 static subst_t prev; /* previous substitution info */
407 subst_t subst; /* current substitution info */
408 CHAR *opt; /* substitution options */
409 long count; /* numeric option: which instance in each line to sub */
410 BUFFER buf;
411 STATE *state, *s;
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
412 #ifdef FEATURE_V
413 VIINFO vinf;
414 #endif
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
415
416
417 assert(xinf->command == EX_SUBSTITUTE
418 || xinf->command == EX_SUBAGAIN
419 || xinf->command == EX_SUBRECENT);
420
421 /* if invoked via visual <&> command, or not edcompatible, then reset */
422 subst = prev;
423 memset(&prev, 0, sizeof prev);
424 if (!o_edcompatible || xinf->bang)
425 {
426 subst.pflag = xinf->pflag;
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
427 subst.global = ElvFalse;
428 subst.execute = ElvFalse;
429 subst.confirm = ElvFalse;
430 subst.errors = ElvTrue;
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
431 subst.instance = 0;
432 }
433 else if (xinf->pflag != PF_NONE)
434 subst.pflag = xinf->pflag;
435
436 /* parse arguments */
437 count = 0;
438 if (xinf->command == EX_SUBAGAIN
439 || (xinf->command == EX_SUBSTITUTE && !xinf->re))
440 {
441 /* use the same regexp as the previous :s command */
442 if (!subst.re)
443 {
444 msg(MSG_ERROR, "no previous regular expression");
445 return RESULT_ERROR;
446 }
447
448 /* same replacement text as last time */
449 subst.rplc = regtilde(toCHAR(o_magic ? "~" : "\\~"));
450 }
451 else if (xinf->command == EX_SUBRECENT)
452 {
453 /* use the most recent regular expression */
454 if (subst.re)
455 safefree(subst.re);
456 prev.re = NULL;
457 subst.re = regcomp(toCHAR(""), xinf->window->state->cursor);
458 if (!subst.re)
459 {
460 /* error message already given by regcomp() */
461 return RESULT_ERROR;
462 }
463
464 /* same replacement text as last time */
465 subst.rplc = regtilde(toCHAR(o_magic ? "~" : "\\~"));
466 }
467 else /* xinf->command == CMD_SUBSTITUTE */
468 {
469 /* use the new regexp */
470 if (subst.re)
471 safefree(subst.re);
472 prev.re = NULL;
473 subst.re = xinf->re;
474 xinf->re = NULL; /* so it isn't clobbered after this cmd */
475
476 /* generate the new text */
477 subst.rplc = regtilde(xinf->lhs ? xinf->lhs : toCHAR(""));
478 }
479
480 /* analyse the option string */
481 for (opt = xinf->rhs; opt && *opt; opt++)
482 {
483 switch (*opt)
484 {
485 case 'g':
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
486 subst.global = (ELVBOOL)!subst.global;
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
487 break;
488
489 case 'x':
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
490 subst.execute = (ELVBOOL)!subst.execute;
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
491 break;
492
493 case 'c':
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
494 subst.confirm = (ELVBOOL)!subst.confirm;
495 break;
496
497 case 'e':
498 subst.errors = (ELVBOOL)!subst.errors;
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
499 break;
500
501 case 'p':
502 subst.pflag = (subst.pflag==PF_PRINT) ? PF_NONE : PF_PRINT;
503 break;
504
505 case 'l':
506 subst.pflag = (subst.pflag==PF_LIST) ? PF_NONE : PF_LIST;
507 break;
508
509 case '#':
510 subst.pflag = (subst.pflag==PF_NUMBER) ? PF_NONE : PF_NUMBER;
511 break;
512
513 case '.':
514 subst.instance = 0;
515 opt++;
516 do
517 {
518 subst.instance *= 10;
519 subst.instance += *opt++ - '0';
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
520 } while (elvdigit(*opt));
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
521 opt--;
522 break;
523
524 case '0':
525 case '1':
526 case '2':
527 case '3':
528 case '4':
529 case '5':
530 case '6':
531 case '7':
532 case '8':
533 case '9':
534 count = 0;
535 do
536 {
537 count = count * 10 + *opt++ - '0';
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
538 } while (elvdigit(*opt));
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
539 opt--;
540 break;
541
542 default:
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
543 if (!elvspace(*opt))
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
544 {
545 msg(MSG_ERROR, "[C]unsupported flag '$1'", *opt);
546 return RESULT_ERROR;
547 }
548 }
549 }
550
551 /* sanity checks */
552 if (subst.instance != 0 && subst.global)
553 {
554 msg(MSG_ERROR, "[d]can't mix instance number .$1 and g flag", subst.instance);
555 return RESULT_ERROR;
556 }
557 buf = markbuffer(xinf->fromaddr);
558 if (subst.confirm && buf != markbuffer(xinf->window->cursor))
559 {
560 msg(MSG_ERROR, "the c flag only works on the window's main edit buffer");
561 return RESULT_ERROR;
562 }
563 if (subst.confirm && !xinf->window)
564 {
565 msg(MSG_ERROR, "the c flag is only available from a window");
566 return RESULT_ERROR;
567 }
568
569 /* can't mix 'c' flag with a print flag - and don't really need to */
570 if (subst.confirm)
571 subst.pflag = PF_NONE;
572
573 /* If count>0 then adjust the "xinf->to" mark */
574 if (count > 0)
575 {
576 xinf->to = xinf->from + count - 1L;
577 if (xinf->to == o_buflines(markbuffer(&xinf->defaddr)))
578 marksetoffset(xinf->toaddr,
579 o_bufchars(markbuffer(&xinf->defaddr)));
580 else
581 marksetoffset(xinf->toaddr,
582 lowline(bufbufinfo(markbuffer(xinf->toaddr)), xinf->to + 1));
583 }
584
585 /* default behavior is to either replace only first instance,
586 * or all instances, depending on the "gdefault" option.
587 */
588 if (subst.instance == 0 && !subst.global)
589 {
590 if (o_gdefault && !o_edcompatible)
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
591 subst.global = ElvTrue;
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
592 else
593 subst.instance = 1;
594 }
595
596 /* remember the flags, unless "saveregexp" is off */
597 if (o_saveregexp)
598 {
599 prev = subst;
600 prev.re = regdup(prev.re);
601 }
602
603 /* if no replacement text, fail */
604 if (!subst.rplc)
605 {
606 return RESULT_ERROR;
607 }
608
609 /* this command does its own printing; disable auto printing */
610 xinf->pflag = PF_NONE;
611
612 /* reset the change counters */
613 subst.chline = subst.chsub = 0L;
614
615 /* make a local copy of replacement string */
616 subst.rplc = CHARdup(subst.rplc);
617
618 /* make the scanned buffer be the one used by this window */
619 bufoptions(buf);
620
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
621 #ifdef FEATURE_AUTOCMD
622 /* we want to trigger Edit events for change, even though we only save
623 * an undo version before the first change.
624 */
625 buf->eachedit = ElvTrue;
626 #endif
627
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
628 /* for each line in the range... */
629 subst.from = markdup(xinf->fromaddr);
630 subst.to = markdup(xinf->toaddr);
631 subst.thisinst = 0;
632 subst.printoff = -1;
633 subst.win = xinf->window;
634 if (subst.confirm)
635 {
636 if (findnext(&subst))
637 {
638 /* insert a state between "ex" and "vi" input
639 * states. The new state should be in the same
640 * stratum as "vi". Since there is no state
641 * function for inserting states this way, we
642 * do it by temporarily removing the "ex" state,
643 * pushing the new one, and then putting "ex"
644 * back again.
645 */
646 state = subst.win->state;
647 subst.win->state = subst.win->state->pop;
648 statepush(subst.win, subst.win->state->flags & ~ELVIS_ONCE);
649 if (state->acton == subst.win->state->pop)
650 state->acton = subst.win->state;
651 state->pop = subst.win->state;
652 subst.win->state = state;
653
654 /* Use the same cursor, top & bottom marks as
655 * higher "vi" state. Move them to the match.
656 */
657 state = subst.win->state->pop;
658 marksetoffset(state->cursor, subst.re->startp[0]);
659 marksetoffset(state->top, subst.re->startp[0]);
660 marksetoffset(state->bottom, subst.re->endp[0]);
661
662 /* If ex mode (not visual <:> command), then we need
663 * to be clever here: We need to exit ex mode while
664 * the substitution is taking place, and then return
665 * to it again afterward.
666 */
667 if ((subst.win->state->flags & (ELVIS_1LINE|ELVIS_ONCE|ELVIS_POP)) == 0)
668 {
669 /* exit "ex" mode */
670 for (s = xinf->window->state;
671 s != xinf->window->state->acton;
672 s = s->pop)
673 s->flags |= ELVIS_1LINE;
674
675 /* force main edit state use use bottom line */
676 for (; s; s = s->pop)
677 s->flags |= ELVIS_BOTTOM;
678
679 /* remember to switch back later */
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
680 subst.ex = ElvTrue;
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
681 }
682
683 /* initialize the remaining fields of the state */
684 state->enter = NULL;
685 state->perform = perform;
686 state->parse = parse;
687 state->shape = shape;
688 state->info = safealloc(1, sizeof(subst_t));
689 *((subst_t *)state->info) = subst;
690 state->modename = "Yes/No";
691
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
692 #ifdef FEATURE_V
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
693 /* highlight the first match */
694 if (subst.re->endp[0] > subst.re->startp[0])
695 {
696 marksetoffset(subst.win->cursor, subst.re->endp[0] - 1);
697 vinf.command = 'v';
698 v_visible(subst.win, &vinf);
699 marksetoffset(subst.win->cursor, subst.re->startp[0]);
700 v_visible(subst.win, NULL);
701 }
702 else /* matching text is 0 characters long */
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
703 #endif
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
704 {
705 /* at least move the cursor there */
706 marksetoffset(subst.win->cursor, subst.re->startp[0]);
707 }
708 marksetoffset(subst.win->state->top, markoffset(subst.win->cursor));
709
710 /* Well, we started successfully */
711 return RESULT_COMPLETE;
712 }
713
714 /* free the marks & stuff -- we failed */
715 markfree(subst.from);
716 markfree(subst.to);
717 safefree(subst.rplc);
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
718 #ifdef FEATURE_AUTOCMD
719 buf->eachedit = ElvFalse;
720 #endif
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
721 return RESULT_ERROR;
722 }
723 else /* non-interactive */
724 {
725 /* do all the substitutions now */
726 while (findnext(&subst))
727 if (!dosubst(xinf->window, &subst))
728 {
729 markfree(subst.from);
730 markfree(subst.to);
731 safefree(subst.rplc);
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
732 #ifdef FEATURE_AUTOCMD
733 buf->eachedit = ElvFalse;
734 #endif
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
735 return RESULT_ERROR;
736 }
737
738 /* Free the marks. */
739 markfree(subst.from);
740 markfree(subst.to);
741 safefree(subst.rplc);
742 }
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
743 #ifdef FEATURE_AUTOCMD
744 buf->eachedit = ElvFalse;
745 #endif
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
746
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
747 /* If used with "e", then finish silently and never cause an error */
748 if (!subst.errors)
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
749 return RESULT_COMPLETE;
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
750
751 /* if used with "x" flag", or if ":set report=0" then finish silently */
752 if (subst.execute || o_report == 0)
753 return subst.chsub > 0 ? RESULT_COMPLETE : RESULT_ERROR;
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
754
755 /* Reporting */
756 if (subst.chsub == 0)
757 msg(MSG_WARNING, "substitution failed");
758 else if (subst.chline >= o_report)
759 msg(MSG_INFO, "[dd]$1 substitutions on $2 lines", subst.chsub, subst.chline);
760
761 /* leave the cursor at the location of the last change */
762 if (subst.chsub > 0)
763 {
764 xinf->newcurs = markalloc(markbuffer(xinf->window->cursor), subst.cursoff);
765
766 /* try to avoid leaving cursor on a newline */
767 if (subst.cursoff > 0L && scanchar(xinf->newcurs) == '\n')
768 {
769 markaddoffset(xinf->newcurs, -1L);
770 if (scanchar(xinf->newcurs) == '\n')
771 markaddoffset(xinf->newcurs, 1L);
772 }
773 return RESULT_COMPLETE;
774 }
775 return RESULT_ERROR;
776 }
Something went wrong with that request. Please try again.