Changing all of the occurences of the "vi" and "ex" directory to "vi.d"
[unix-history] / usr.bin / vi / nvi / v_text.c
CommitLineData
18ae9d6b
AS
1/*-
2 * Copyright (c) 1992, 1993
3 * The Regents of the University of California. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#ifndef lint
35static char sccsid[] = "@(#)v_text.c 8.23 (Berkeley) 1/9/94";
36#endif /* not lint */
37
38#include <sys/types.h>
39
40#include <ctype.h>
41#include <errno.h>
42#include <stdlib.h>
43#include <string.h>
44
45#include "vi.h"
46#include "vcmd.h"
47
48/*
49 * !!!
50 * Repeated input in the historic vi is mostly wrong and this isn't very
51 * backward compatible. For example, if the user entered "3Aab\ncd" in
52 * the historic vi, the "ab" was repeated 3 times, and the "\ncd" was then
53 * appended to the result. There was also a hack which I don't remember
54 * right now, where "3o" would open 3 lines and then let the user fill them
55 * in, to make screen movements on 300 baud modems more tolerable. I don't
56 * think it's going to be missed.
57 */
58
59#define SET_TXT_STD(sp, f) { \
60 LF_INIT((f) | TXT_BEAUTIFY | TXT_CNTRLT | TXT_ESCAPE | \
61 TXT_MAPINPUT | TXT_RECORD | TXT_RESOLVE); \
62 if (O_ISSET(sp, O_ALTWERASE)) \
63 LF_SET(TXT_ALTWERASE); \
64 if (O_ISSET(sp, O_AUTOINDENT)) \
65 LF_SET(TXT_AUTOINDENT); \
66 if (O_ISSET(sp, O_SHOWMATCH)) \
67 LF_SET(TXT_SHOWMATCH); \
68 if (O_ISSET(sp, O_WRAPMARGIN)) \
69 LF_SET(TXT_WRAPMARGIN); \
70 if (F_ISSET(sp, S_SCRIPT)) \
71 LF_SET(TXT_CR); \
72 if (O_ISSET(sp, O_TTYWERASE)) \
73 LF_SET(TXT_TTYWERASE); \
74}
75
76/*
77 * !!!
78 * There's a problem with the way that we do logging for change commands with
79 * implied motions (e.g. A, I, O, cc, etc.). Since the main vi loop logs the
80 * starting cursor position before the change command "moves" the cursor, the
81 * cursor position to which we return on undo will be where the user entered
82 * the change command, not the start of the change. Several of the following
83 * routines re-log the cursor to make this work correctly. Historic vi tried
84 * to do the same thing, and mostly got it right. (The only spectacular way
85 * it fails is if the user entered 'o' from anywhere but the last character of
86 * the line, the undo returned the cursor to the start of the line. If the
87 * user was on the last character of the line, the cursor returned to that
88 * position.)
89 */
90
91static int v_CS __P((SCR *, EXF *, VICMDARG *, MARK *, MARK *, MARK *, u_int));
92
93/*
94 * v_iA -- [count]A
95 * Append text to the end of the line.
96 */
97int
98v_iA(sp, ep, vp, fm, tm, rp)
99 SCR *sp;
100 EXF *ep;
101 VICMDARG *vp;
102 MARK *fm, *tm, *rp;
103{
104 recno_t lno;
105 u_long cnt;
106 size_t len;
107 u_int flags;
108 int first;
109 char *p;
110
111 SET_TXT_STD(sp, TXT_APPENDEOL);
112 if (F_ISSET(vp, VC_ISDOT))
113 LF_SET(TXT_REPLAY);
114 for (first = 1, lno = fm->lno,
115 cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) {
116 /* Move the cursor to the end of the line + 1. */
117 if ((p = file_gline(sp, ep, lno, &len)) == NULL) {
118 if (file_lline(sp, ep, &lno))
119 return (1);
120 if (lno != 0) {
121 GETLINE_ERR(sp, lno);
122 return (1);
123 }
124 lno = 1;
125 len = 0;
126 } else {
127 /* Correct logging for implied cursor motion. */
128 sp->cno = len == 0 ? 0 : len - 1;
129 if (first == 1) {
130 log_cursor(sp, ep);
131 first = 0;
132 }
133 /* Start the change after the line. */
134 sp->cno = len;
135 }
136
137 if (v_ntext(sp, ep,
138 &sp->tiq, NULL, p, len, rp, 0, OOBLNO, flags))
139 return (1);
140
141 SET_TXT_STD(sp, TXT_APPENDEOL | TXT_REPLAY);
142 sp->lno = lno = rp->lno;
143 sp->cno = rp->cno;
144 }
145 return (0);
146}
147
148/*
149 * v_ia -- [count]a
150 * Append text to the cursor position.
151 */
152int
153v_ia(sp, ep, vp, fm, tm, rp)
154 SCR *sp;
155 EXF *ep;
156 VICMDARG *vp;
157 MARK *fm, *tm, *rp;
158{
159 recno_t lno;
160 u_long cnt;
161 u_int flags;
162 size_t len;
163 char *p;
164
165 SET_TXT_STD(sp, 0);
166 if (F_ISSET(vp, VC_ISDOT))
167 LF_SET(TXT_REPLAY);
168 for (lno = fm->lno,
169 cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) {
170 /*
171 * Move the cursor one column to the right and
172 * repaint the screen.
173 */
174 if ((p = file_gline(sp, ep, lno, &len)) == NULL) {
175 if (file_lline(sp, ep, &lno))
176 return (1);
177 if (lno != 0) {
178 GETLINE_ERR(sp, lno);
179 return (1);
180 }
181 lno = 1;
182 len = 0;
183 LF_SET(TXT_APPENDEOL);
184 } else if (len) {
185 if (len == sp->cno + 1) {
186 sp->cno = len;
187 LF_SET(TXT_APPENDEOL);
188 } else
189 ++sp->cno;
190 } else
191 LF_SET(TXT_APPENDEOL);
192
193 if (v_ntext(sp, ep,
194 &sp->tiq, NULL, p, len, rp, 0, OOBLNO, flags))
195 return (1);
196
197 SET_TXT_STD(sp, TXT_REPLAY);
198 sp->lno = lno = rp->lno;
199 sp->cno = rp->cno;
200 }
201 return (0);
202}
203
204/*
205 * v_iI -- [count]I
206 * Insert text at the first non-blank character in the line.
207 */
208int
209v_iI(sp, ep, vp, fm, tm, rp)
210 SCR *sp;
211 EXF *ep;
212 VICMDARG *vp;
213 MARK *fm, *tm, *rp;
214{
215 recno_t lno;
216 u_long cnt;
217 size_t len;
218 u_int flags;
219 int first;
220 char *p;
221
222 SET_TXT_STD(sp, 0);
223 if (F_ISSET(vp, VC_ISDOT))
224 LF_SET(TXT_REPLAY);
225 for (first = 1, lno = fm->lno,
226 cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) {
227 /*
228 * Move the cursor to the start of the line and repaint
229 * the screen.
230 */
231 if ((p = file_gline(sp, ep, lno, &len)) == NULL) {
232 if (file_lline(sp, ep, &lno))
233 return (1);
234 if (lno != 0) {
235 GETLINE_ERR(sp, lno);
236 return (1);
237 }
238 lno = 1;
239 len = 0;
240 } else {
241 sp->cno = 0;
242 if (nonblank(sp, ep, lno, &sp->cno))
243 return (1);
244 /* Correct logging for implied cursor motion. */
245 if (first == 1) {
246 log_cursor(sp, ep);
247 first = 0;
248 }
249 }
250 if (len == 0)
251 LF_SET(TXT_APPENDEOL);
252
253 if (v_ntext(sp, ep,
254 &sp->tiq, NULL, p, len, rp, 0, OOBLNO, flags))
255 return (1);
256
257 SET_TXT_STD(sp, TXT_REPLAY);
258 sp->lno = lno = rp->lno;
259 sp->cno = rp->cno;
260 }
261 return (0);
262}
263
264/*
265 * v_ii -- [count]i
266 * Insert text at the cursor position.
267 */
268int
269v_ii(sp, ep, vp, fm, tm, rp)
270 SCR *sp;
271 EXF *ep;
272 VICMDARG *vp;
273 MARK *fm, *tm, *rp;
274{
275 recno_t lno;
276 u_long cnt;
277 size_t len;
278 u_int flags;
279 char *p;
280
281 SET_TXT_STD(sp, 0);
282 if (F_ISSET(vp, VC_ISDOT))
283 LF_SET(TXT_REPLAY);
284 for (lno = fm->lno,
285 cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) {
286 if ((p = file_gline(sp, ep, lno, &len)) == NULL) {
287 if (file_lline(sp, ep, &lno))
288 return (1);
289 if (lno != 0) {
290 GETLINE_ERR(sp, fm->lno);
291 return (1);
292 }
293 lno = 1;
294 len = 0;
295 }
296 /* If len == sp->cno, it's a replay caused by a count. */
297 if (len == 0 || len == sp->cno)
298 LF_SET(TXT_APPENDEOL);
299
300 if (v_ntext(sp, ep,
301 &sp->tiq, NULL, p, len, rp, 0, OOBLNO, flags))
302 return (1);
303
304 /*
305 * On replay, if the line isn't empty, advance the insert
306 * by one (make it an append).
307 */
308 SET_TXT_STD(sp, TXT_REPLAY);
309 sp->lno = lno = rp->lno;
310 if ((sp->cno = rp->cno) != 0)
311 ++sp->cno;
312 }
313 return (0);
314}
315
316/*
317 * v_iO -- [count]O
318 * Insert text above this line.
319 */
320int
321v_iO(sp, ep, vp, fm, tm, rp)
322 SCR *sp;
323 EXF *ep;
324 VICMDARG *vp;
325 MARK *fm, *tm, *rp;
326{
327 recno_t ai_line, lno;
328 size_t len;
329 u_long cnt;
330 u_int flags;
331 int first;
332 char *p;
333
334 SET_TXT_STD(sp, TXT_APPENDEOL);
335 if (F_ISSET(vp, VC_ISDOT))
336 LF_SET(TXT_REPLAY);
337 for (first = 1, cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) {
338 if (sp->lno == 1) {
339 if (file_lline(sp, ep, &lno))
340 return (1);
341 if (lno != 0)
342 goto insert;
343 p = NULL;
344 len = 0;
345 ai_line = OOBLNO;
346 } else {
347insert: p = "";
348 sp->cno = 0;
349 /* Correct logging for implied cursor motion. */
350 if (first == 1) {
351 log_cursor(sp, ep);
352 first = 0;
353 }
354 if (file_iline(sp, ep, sp->lno, p, 0))
355 return (1);
356 if ((p = file_gline(sp, ep, sp->lno, &len)) == NULL) {
357 GETLINE_ERR(sp, sp->lno);
358 return (1);
359 }
360 ai_line = sp->lno + 1;
361 }
362
363 if (v_ntext(sp, ep,
364 &sp->tiq, NULL, p, len, rp, 0, ai_line, flags))
365 return (1);
366
367 SET_TXT_STD(sp, TXT_APPENDEOL | TXT_REPLAY);
368 sp->lno = lno = rp->lno;
369 sp->cno = rp->cno;
370 }
371 return (0);
372}
373
374/*
375 * v_io -- [count]o
376 * Insert text after this line.
377 */
378int
379v_io(sp, ep, vp, fm, tm, rp)
380 SCR *sp;
381 EXF *ep;
382 VICMDARG *vp;
383 MARK *fm, *tm, *rp;
384{
385 recno_t ai_line, lno;
386 size_t len;
387 u_long cnt;
388 u_int flags;
389 int first;
390 char *p;
391
392 SET_TXT_STD(sp, TXT_APPENDEOL);
393 if (F_ISSET(vp, VC_ISDOT))
394 LF_SET(TXT_REPLAY);
395 for (first = 1,
396 cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) {
397 if (sp->lno == 1) {
398 if (file_lline(sp, ep, &lno))
399 return (1);
400 if (lno != 0)
401 goto insert;
402 p = NULL;
403 len = 0;
404 ai_line = OOBLNO;
405 } else {
406insert: p = "";
407 sp->cno = 0;
408 /* Correct logging for implied cursor motion. */
409 if (first == 1) {
410 log_cursor(sp, ep);
411 first = 0;
412 }
413 len = 0;
414 if (file_aline(sp, ep, 1, sp->lno, p, len))
415 return (1);
416 if ((p = file_gline(sp, ep, ++sp->lno, &len)) == NULL) {
417 GETLINE_ERR(sp, sp->lno);
418 return (1);
419 }
420 ai_line = sp->lno - 1;
421 }
422
423 if (v_ntext(sp, ep,
424 &sp->tiq, NULL, p, len, rp, 0, ai_line, flags))
425 return (1);
426
427 SET_TXT_STD(sp, TXT_APPENDEOL | TXT_REPLAY);
428 sp->lno = lno = rp->lno;
429 sp->cno = rp->cno;
430 }
431 return (0);
432}
433
434/*
435 * v_Change -- [buffer][count]C
436 * Change line command.
437 */
438int
439v_Change(sp, ep, vp, fm, tm, rp)
440 SCR *sp;
441 EXF *ep;
442 VICMDARG *vp;
443 MARK *fm, *tm, *rp;
444{
445 return (v_CS(sp, ep, vp, fm, tm, rp, 0));
446}
447
448/*
449 * v_Subst -- [buffer][count]S
450 * Line substitute command.
451 */
452int
453v_Subst(sp, ep, vp, fm, tm, rp)
454 SCR *sp;
455 EXF *ep;
456 VICMDARG *vp;
457 MARK *fm, *tm, *rp;
458{
459 u_int flags;
460
461 /*
462 * The S command is the same as a 'C' command from the beginning
463 * of the line. This is hard to do in the parser, so do it here.
464 *
465 * If autoindent is on, the change is from the first *non-blank*
466 * character of the line, not the first character. And, to make
467 * it just a bit more exciting, the initial space is handled as
468 * auto-indent characters.
469 */
470 LF_INIT(0);
471 if (O_ISSET(sp, O_AUTOINDENT)) {
472 fm->cno = 0;
473 if (nonblank(sp, ep, fm->lno, &fm->cno))
474 return (1);
475 LF_SET(TXT_AICHARS);
476 } else
477 fm->cno = 0;
478 sp->cno = fm->cno;
479 return (v_CS(sp, ep, vp, fm, tm, rp, flags));
480}
481
482/*
483 * v_CS --
484 * C and S commands.
485 */
486static int
487v_CS(sp, ep, vp, fm, tm, rp, iflags)
488 SCR *sp;
489 EXF *ep;
490 VICMDARG *vp;
491 MARK *fm, *tm, *rp;
492 u_int iflags;
493{
494 recno_t lno;
495 size_t len;
496 char *p;
497 u_int flags;
498
499 SET_TXT_STD(sp, iflags);
500 if (F_ISSET(vp, VC_ISDOT))
501 LF_SET(TXT_REPLAY);
502
503 /*
504 * There are two cases -- if a count is supplied, we do a line
505 * mode change where we delete the lines and then insert text
506 * into a new line. Otherwise, we replace the current line.
507 */
508 tm->lno = fm->lno + (F_ISSET(vp, VC_C1SET) ? vp->count - 1 : 0);
509 if (fm->lno != tm->lno) {
510 /* Make sure that the to line is real. */
511 if (file_gline(sp, ep, tm->lno, NULL) == NULL) {
512 v_eof(sp, ep, fm);
513 return (1);
514 }
515
516 /* Cut the lines. */
517 if (cut(sp, ep,
518 NULL, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL,
519 fm, tm, CUT_LINEMODE))
520 return (1);
521
522 /* Insert a line while we still can... */
523 if (file_iline(sp, ep, fm->lno, "", 0))
524 return (1);
525 ++fm->lno;
526 ++tm->lno;
527
528 /* Delete the lines. */
529 if (delete(sp, ep, fm, tm, 1))
530 return (1);
531
532 /* Get the inserted line. */
533 if ((p = file_gline(sp, ep, --fm->lno, &len)) == NULL) {
534 GETLINE_ERR(sp, fm->lno);
535 return (1);
536 }
537 tm = NULL;
538 sp->lno = fm->lno;
539 sp->cno = 0;
540 LF_SET(TXT_APPENDEOL);
541 } else {
542 /* The line may be empty, but that's okay. */
543 if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) {
544 if (file_lline(sp, ep, &lno))
545 return (1);
546 if (lno != 0) {
547 GETLINE_ERR(sp, tm->lno);
548 return (1);
549 }
550 len = 0;
551 LF_SET(TXT_APPENDEOL);
552 } else {
553 if (cut(sp, ep,
554 NULL, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL,
555 fm, tm, CUT_LINEMODE))
556 return (1);
557 tm->cno = len;
558 if (len == 0)
559 LF_SET(TXT_APPENDEOL);
560 LF_SET(TXT_EMARK | TXT_OVERWRITE);
561 }
562 }
563 /* Correct logging for implied cursor motion. */
564 log_cursor(sp, ep);
565 return (v_ntext(sp, ep,
566 &sp->tiq, tm, p, len, rp, 0, OOBLNO, flags));
567}
568
569/*
570 * v_change -- [buffer][count]c[count]motion
571 * Change command.
572 */
573int
574v_change(sp, ep, vp, fm, tm, rp)
575 SCR *sp;
576 EXF *ep;
577 VICMDARG *vp;
578 MARK *fm, *tm, *rp;
579{
580 recno_t lno;
581 size_t blen, len;
582 u_int flags;
583 int lmode, rval;
584 char *bp, *p;
585
586 SET_TXT_STD(sp, 0);
587 if (F_ISSET(vp, VC_ISDOT))
588 LF_SET(TXT_REPLAY);
589
590 /*
591 * Move the cursor to the start of the change. Note, if autoindent
592 * is turned on, the cc command in line mode changes from the first
593 * *non-blank* character of the line, not the first character. And,
594 * to make it just a bit more exciting, the initial space is handled
595 * as auto-indent characters.
596 */
597 lmode = F_ISSET(vp, VC_LMODE) ? CUT_LINEMODE : 0;
598 if (lmode) {
599 fm->cno = 0;
600 if (O_ISSET(sp, O_AUTOINDENT)) {
601 if (nonblank(sp, ep, fm->lno, &fm->cno))
602 return (1);
603 LF_SET(TXT_AICHARS);
604 }
605 }
606 sp->lno = fm->lno;
607 sp->cno = fm->cno;
608
609 /* Correct logging for implied cursor motion. */
610 log_cursor(sp, ep);
611
612 /*
613 * If changing within a single line, the line either currently has
614 * text or it doesn't. If it doesn't, just insert text. Otherwise,
615 * copy it and overwrite it.
616 */
617 if (fm->lno == tm->lno) {
618 if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) {
619 if (p == NULL) {
620 if (file_lline(sp, ep, &lno))
621 return (1);
622 if (lno != 0) {
623 GETLINE_ERR(sp, fm->lno);
624 return (1);
625 }
626 }
627 len = 0;
628 LF_SET(TXT_APPENDEOL);
629 } else {
630 if (cut(sp, ep,
631 NULL, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL,
632 fm, tm, lmode))
633 return (1);
634 if (len == 0)
635 LF_SET(TXT_APPENDEOL);
636 LF_SET(TXT_EMARK | TXT_OVERWRITE);
637 }
638 return (v_ntext(sp, ep,
639 &sp->tiq, tm, p, len, rp, 0, OOBLNO, flags));
640 }
641
642 /*
643 * It's trickier if changing over multiple lines. If we're in
644 * line mode we delete all of the lines and insert a replacement
645 * line which the user edits. If there was leading whitespace
646 * in the first line being changed, we copy it and use it as the
647 * replacement. If we're not in line mode, we just delete the
648 * text and start inserting.
649 *
650 * Copy the text.
651 */
652 if (cut(sp, ep,
653 NULL, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL, fm, tm, lmode))
654 return (1);
655
656 /* If replacing entire lines and there's leading text. */
657 if (lmode && fm->cno) {
658 /* Get a copy of the first line changed. */
659 if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) {
660 GETLINE_ERR(sp, fm->lno);
661 return (1);
662 }
663 /* Copy the leading text elsewhere. */
664 GET_SPACE_RET(sp, bp, blen, fm->cno);
665 memmove(bp, p, fm->cno);
666 } else
667 bp = NULL;
668
669 /* Delete the text. */
670 if (delete(sp, ep, fm, tm, lmode))
671 return (1);
672
673 /* If replacing entire lines, insert a replacement line. */
674 if (lmode) {
675 if (file_iline(sp, ep, fm->lno, bp, fm->cno))
676 return (1);
677 sp->lno = fm->lno;
678 len = sp->cno = fm->cno;
679 }
680
681 /* Get the line we're editing. */
682 if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) {
683 if (file_lline(sp, ep, &lno))
684 return (1);
685 if (lno != 0) {
686 GETLINE_ERR(sp, fm->lno);
687 return (1);
688 }
689 len = 0;
690 }
691
692 /* Check to see if we're appending to the line. */
693 if (fm->cno >= len)
694 LF_SET(TXT_APPENDEOL);
695
696 /* No to mark. */
697 tm = NULL;
698
699 rval = v_ntext(sp, ep, &sp->tiq, tm, p, len, rp, 0, OOBLNO, flags);
700
701 if (bp != NULL)
702 FREE_SPACE(sp, bp, blen);
703 return (rval);
704}
705
706/*
707 * v_Replace -- [count]R
708 * Overwrite multiple characters.
709 */
710int
711v_Replace(sp, ep, vp, fm, tm, rp)
712 SCR *sp;
713 EXF *ep;
714 VICMDARG *vp;
715 MARK *fm, *tm, *rp;
716{
717 recno_t lno;
718 u_long cnt;
719 size_t len;
720 u_int flags;
721 char *p;
722
723 SET_TXT_STD(sp, 0);
724 if (F_ISSET(vp, VC_ISDOT))
725 LF_SET(TXT_REPLAY);
726
727 cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
728 if ((p = file_gline(sp, ep, rp->lno, &len)) == NULL) {
729 if (file_lline(sp, ep, &lno))
730 return (1);
731 if (lno != 0) {
732 GETLINE_ERR(sp, rp->lno);
733 return (1);
734 }
735 len = 0;
736 LF_SET(TXT_APPENDEOL);
737 } else {
738 if (len == 0)
739 LF_SET(TXT_APPENDEOL);
740 LF_SET(TXT_OVERWRITE | TXT_REPLACE);
741 }
742 tm->lno = rp->lno;
743 tm->cno = len ? len : 0;
744 if (v_ntext(sp, ep, &sp->tiq, tm, p, len, rp, 0, OOBLNO, flags))
745 return (1);
746
747 /*
748 * Special case. The historic vi handled [count]R badly, in that R
749 * would replace some number of characters, and then the count would
750 * append count-1 copies of the replacing chars to the replaced space.
751 * This seems wrong, so this version counts R commands. There is some
752 * trickiness in moving back to where the user stopped replacing after
753 * each R command. Basically, if the user ended with a newline, we
754 * want to use rp->cno (which will be 0). Otherwise, use the column
755 * after the returned cursor, unless it would be past the end of the
756 * line, in which case we append to the line.
757 */
758 while (--cnt) {
759 if ((p = file_gline(sp, ep, rp->lno, &len)) == NULL)
760 GETLINE_ERR(sp, rp->lno);
761 SET_TXT_STD(sp, TXT_REPLAY);
762
763 sp->lno = rp->lno;
764
765 if (len == 0 || rp->cno == len - 1) {
766 sp->cno = len;
767 LF_SET(TXT_APPENDEOL);
768 } else {
769 sp->cno = rp->cno;
770 if (rp->cno != 0)
771 ++sp->cno;
772 LF_SET(TXT_OVERWRITE | TXT_REPLACE);
773 }
774
775 if (v_ntext(sp, ep,
776 &sp->tiq, tm, p, len, rp, 0, OOBLNO, flags))
777 return (1);
778 }
779 return (0);
780}
781
782/*
783 * v_subst -- [buffer][count]s
784 * Substitute characters.
785 */
786int
787v_subst(sp, ep, vp, fm, tm, rp)
788 SCR *sp;
789 EXF *ep;
790 VICMDARG *vp;
791 MARK *fm, *tm, *rp;
792{
793 recno_t lno;
794 size_t len;
795 u_int flags;
796 char *p;
797
798 SET_TXT_STD(sp, 0);
799 if (F_ISSET(vp, VC_ISDOT))
800 LF_SET(TXT_REPLAY);
801 if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) {
802 if (file_lline(sp, ep, &lno))
803 return (1);
804 if (lno != 0) {
805 GETLINE_ERR(sp, fm->lno);
806 return (1);
807 }
808 len = 0;
809 LF_SET(TXT_APPENDEOL);
810 } else {
811 if (len == 0)
812 LF_SET(TXT_APPENDEOL);
813 LF_SET(TXT_EMARK | TXT_OVERWRITE);
814 }
815
816 tm->lno = fm->lno;
817 tm->cno = fm->cno + (F_ISSET(vp, VC_C1SET) ? vp->count : 1);
818 if (tm->cno > len)
819 tm->cno = len;
820
821 if (p != NULL && cut(sp, ep,
822 NULL, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL, fm, tm, 0))
823 return (1);
824
825 return (v_ntext(sp, ep,
826 &sp->tiq, tm, p, len, rp, 0, OOBLNO, flags));
827}