2 * Copyright (c) 1992, 1993
3 * The Regents of the University of California. All rights reserved.
5 * %sccs.include.redist.c%
9 static char sccsid
[] = "$Id: v_itxt.c,v 8.23 1994/01/09 23:24:44 bostic Exp $ (Berkeley) $Date: 1994/01/09 23:24:44 $";
12 #include <sys/types.h>
24 * Repeated input in the historic vi is mostly wrong and this isn't very
25 * backward compatible. For example, if the user entered "3Aab\ncd" in
26 * the historic vi, the "ab" was repeated 3 times, and the "\ncd" was then
27 * appended to the result. There was also a hack which I don't remember
28 * right now, where "3o" would open 3 lines and then let the user fill them
29 * in, to make screen movements on 300 baud modems more tolerable. I don't
30 * think it's going to be missed.
33 #define SET_TXT_STD(sp, f) { \
34 LF_INIT((f) | TXT_BEAUTIFY | TXT_CNTRLT | TXT_ESCAPE | \
35 TXT_MAPINPUT | TXT_RECORD | TXT_RESOLVE); \
36 if (O_ISSET(sp, O_ALTWERASE)) \
37 LF_SET(TXT_ALTWERASE); \
38 if (O_ISSET(sp, O_AUTOINDENT)) \
39 LF_SET(TXT_AUTOINDENT); \
40 if (O_ISSET(sp, O_SHOWMATCH)) \
41 LF_SET(TXT_SHOWMATCH); \
42 if (O_ISSET(sp, O_WRAPMARGIN)) \
43 LF_SET(TXT_WRAPMARGIN); \
44 if (F_ISSET(sp, S_SCRIPT)) \
46 if (O_ISSET(sp, O_TTYWERASE)) \
47 LF_SET(TXT_TTYWERASE); \
52 * There's a problem with the way that we do logging for change commands with
53 * implied motions (e.g. A, I, O, cc, etc.). Since the main vi loop logs the
54 * starting cursor position before the change command "moves" the cursor, the
55 * cursor position to which we return on undo will be where the user entered
56 * the change command, not the start of the change. Several of the following
57 * routines re-log the cursor to make this work correctly. Historic vi tried
58 * to do the same thing, and mostly got it right. (The only spectacular way
59 * it fails is if the user entered 'o' from anywhere but the last character of
60 * the line, the undo returned the cursor to the start of the line. If the
61 * user was on the last character of the line, the cursor returned to that
65 static int v_CS
__P((SCR
*, EXF
*, VICMDARG
*, MARK
*, MARK
*, MARK
*, u_int
));
69 * Append text to the end of the line.
72 v_iA(sp
, ep
, vp
, fm
, tm
, rp
)
85 SET_TXT_STD(sp
, TXT_APPENDEOL
);
86 if (F_ISSET(vp
, VC_ISDOT
))
88 for (first
= 1, lno
= fm
->lno
,
89 cnt
= F_ISSET(vp
, VC_C1SET
) ? vp
->count
: 1; cnt
--;) {
90 /* Move the cursor to the end of the line + 1. */
91 if ((p
= file_gline(sp
, ep
, lno
, &len
)) == NULL
) {
92 if (file_lline(sp
, ep
, &lno
))
101 /* Correct logging for implied cursor motion. */
102 sp
->cno
= len
== 0 ? 0 : len
- 1;
107 /* Start the change after the line. */
112 &sp
->tiq
, NULL
, p
, len
, rp
, 0, OOBLNO
, flags
))
115 SET_TXT_STD(sp
, TXT_APPENDEOL
| TXT_REPLAY
);
116 sp
->lno
= lno
= rp
->lno
;
124 * Append text to the cursor position.
127 v_ia(sp
, ep
, vp
, fm
, tm
, rp
)
140 if (F_ISSET(vp
, VC_ISDOT
))
143 cnt
= F_ISSET(vp
, VC_C1SET
) ? vp
->count
: 1; cnt
--;) {
145 * Move the cursor one column to the right and
146 * repaint the screen.
148 if ((p
= file_gline(sp
, ep
, lno
, &len
)) == NULL
) {
149 if (file_lline(sp
, ep
, &lno
))
152 GETLINE_ERR(sp
, lno
);
157 LF_SET(TXT_APPENDEOL
);
159 if (len
== sp
->cno
+ 1) {
161 LF_SET(TXT_APPENDEOL
);
165 LF_SET(TXT_APPENDEOL
);
168 &sp
->tiq
, NULL
, p
, len
, rp
, 0, OOBLNO
, flags
))
171 SET_TXT_STD(sp
, TXT_REPLAY
);
172 sp
->lno
= lno
= rp
->lno
;
180 * Insert text at the first non-blank character in the line.
183 v_iI(sp
, ep
, vp
, fm
, tm
, rp
)
197 if (F_ISSET(vp
, VC_ISDOT
))
199 for (first
= 1, lno
= fm
->lno
,
200 cnt
= F_ISSET(vp
, VC_C1SET
) ? vp
->count
: 1; cnt
--;) {
202 * Move the cursor to the start of the line and repaint
205 if ((p
= file_gline(sp
, ep
, lno
, &len
)) == NULL
) {
206 if (file_lline(sp
, ep
, &lno
))
209 GETLINE_ERR(sp
, lno
);
216 if (nonblank(sp
, ep
, lno
, &sp
->cno
))
218 /* Correct logging for implied cursor motion. */
225 LF_SET(TXT_APPENDEOL
);
228 &sp
->tiq
, NULL
, p
, len
, rp
, 0, OOBLNO
, flags
))
231 SET_TXT_STD(sp
, TXT_REPLAY
);
232 sp
->lno
= lno
= rp
->lno
;
240 * Insert text at the cursor position.
243 v_ii(sp
, ep
, vp
, fm
, tm
, rp
)
256 if (F_ISSET(vp
, VC_ISDOT
))
259 cnt
= F_ISSET(vp
, VC_C1SET
) ? vp
->count
: 1; cnt
--;) {
260 if ((p
= file_gline(sp
, ep
, lno
, &len
)) == NULL
) {
261 if (file_lline(sp
, ep
, &lno
))
264 GETLINE_ERR(sp
, fm
->lno
);
270 /* If len == sp->cno, it's a replay caused by a count. */
271 if (len
== 0 || len
== sp
->cno
)
272 LF_SET(TXT_APPENDEOL
);
275 &sp
->tiq
, NULL
, p
, len
, rp
, 0, OOBLNO
, flags
))
279 * On replay, if the line isn't empty, advance the insert
280 * by one (make it an append).
282 SET_TXT_STD(sp
, TXT_REPLAY
);
283 sp
->lno
= lno
= rp
->lno
;
284 if ((sp
->cno
= rp
->cno
) != 0)
292 * Insert text above this line.
295 v_iO(sp
, ep
, vp
, fm
, tm
, rp
)
301 recno_t ai_line
, lno
;
308 SET_TXT_STD(sp
, TXT_APPENDEOL
);
309 if (F_ISSET(vp
, VC_ISDOT
))
311 for (first
= 1, cnt
= F_ISSET(vp
, VC_C1SET
) ? vp
->count
: 1; cnt
--;) {
313 if (file_lline(sp
, ep
, &lno
))
323 /* Correct logging for implied cursor motion. */
328 if (file_iline(sp
, ep
, sp
->lno
, p
, 0))
330 if ((p
= file_gline(sp
, ep
, sp
->lno
, &len
)) == NULL
) {
331 GETLINE_ERR(sp
, sp
->lno
);
334 ai_line
= sp
->lno
+ 1;
338 &sp
->tiq
, NULL
, p
, len
, rp
, 0, ai_line
, flags
))
341 SET_TXT_STD(sp
, TXT_APPENDEOL
| TXT_REPLAY
);
342 sp
->lno
= lno
= rp
->lno
;
350 * Insert text after this line.
353 v_io(sp
, ep
, vp
, fm
, tm
, rp
)
359 recno_t ai_line
, lno
;
366 SET_TXT_STD(sp
, TXT_APPENDEOL
);
367 if (F_ISSET(vp
, VC_ISDOT
))
370 cnt
= F_ISSET(vp
, VC_C1SET
) ? vp
->count
: 1; cnt
--;) {
372 if (file_lline(sp
, ep
, &lno
))
382 /* Correct logging for implied cursor motion. */
388 if (file_aline(sp
, ep
, 1, sp
->lno
, p
, len
))
390 if ((p
= file_gline(sp
, ep
, ++sp
->lno
, &len
)) == NULL
) {
391 GETLINE_ERR(sp
, sp
->lno
);
394 ai_line
= sp
->lno
- 1;
398 &sp
->tiq
, NULL
, p
, len
, rp
, 0, ai_line
, flags
))
401 SET_TXT_STD(sp
, TXT_APPENDEOL
| TXT_REPLAY
);
402 sp
->lno
= lno
= rp
->lno
;
409 * v_Change -- [buffer][count]C
410 * Change line command.
413 v_Change(sp
, ep
, vp
, fm
, tm
, rp
)
419 return (v_CS(sp
, ep
, vp
, fm
, tm
, rp
, 0));
423 * v_Subst -- [buffer][count]S
424 * Line substitute command.
427 v_Subst(sp
, ep
, vp
, fm
, tm
, rp
)
436 * The S command is the same as a 'C' command from the beginning
437 * of the line. This is hard to do in the parser, so do it here.
439 * If autoindent is on, the change is from the first *non-blank*
440 * character of the line, not the first character. And, to make
441 * it just a bit more exciting, the initial space is handled as
442 * auto-indent characters.
445 if (O_ISSET(sp
, O_AUTOINDENT
)) {
447 if (nonblank(sp
, ep
, fm
->lno
, &fm
->cno
))
453 return (v_CS(sp
, ep
, vp
, fm
, tm
, rp
, flags
));
461 v_CS(sp
, ep
, vp
, fm
, tm
, rp
, iflags
)
473 SET_TXT_STD(sp
, iflags
);
474 if (F_ISSET(vp
, VC_ISDOT
))
478 * There are two cases -- if a count is supplied, we do a line
479 * mode change where we delete the lines and then insert text
480 * into a new line. Otherwise, we replace the current line.
482 tm
->lno
= fm
->lno
+ (F_ISSET(vp
, VC_C1SET
) ? vp
->count
- 1 : 0);
483 if (fm
->lno
!= tm
->lno
) {
484 /* Make sure that the to line is real. */
485 if (file_gline(sp
, ep
, tm
->lno
, NULL
) == NULL
) {
492 NULL
, F_ISSET(vp
, VC_BUFFER
) ? &vp
->buffer
: NULL
,
493 fm
, tm
, CUT_LINEMODE
))
496 /* Insert a line while we still can... */
497 if (file_iline(sp
, ep
, fm
->lno
, "", 0))
502 /* Delete the lines. */
503 if (delete(sp
, ep
, fm
, tm
, 1))
506 /* Get the inserted line. */
507 if ((p
= file_gline(sp
, ep
, --fm
->lno
, &len
)) == NULL
) {
508 GETLINE_ERR(sp
, fm
->lno
);
514 LF_SET(TXT_APPENDEOL
);
516 /* The line may be empty, but that's okay. */
517 if ((p
= file_gline(sp
, ep
, fm
->lno
, &len
)) == NULL
) {
518 if (file_lline(sp
, ep
, &lno
))
521 GETLINE_ERR(sp
, tm
->lno
);
525 LF_SET(TXT_APPENDEOL
);
528 NULL
, F_ISSET(vp
, VC_BUFFER
) ? &vp
->buffer
: NULL
,
529 fm
, tm
, CUT_LINEMODE
))
533 LF_SET(TXT_APPENDEOL
);
534 LF_SET(TXT_EMARK
| TXT_OVERWRITE
);
537 /* Correct logging for implied cursor motion. */
539 return (v_ntext(sp
, ep
,
540 &sp
->tiq
, tm
, p
, len
, rp
, 0, OOBLNO
, flags
));
544 * v_change -- [buffer][count]c[count]motion
548 v_change(sp
, ep
, vp
, fm
, tm
, rp
)
561 if (F_ISSET(vp
, VC_ISDOT
))
565 * Move the cursor to the start of the change. Note, if autoindent
566 * is turned on, the cc command in line mode changes from the first
567 * *non-blank* character of the line, not the first character. And,
568 * to make it just a bit more exciting, the initial space is handled
569 * as auto-indent characters.
571 lmode
= F_ISSET(vp
, VC_LMODE
) ? CUT_LINEMODE
: 0;
574 if (O_ISSET(sp
, O_AUTOINDENT
)) {
575 if (nonblank(sp
, ep
, fm
->lno
, &fm
->cno
))
583 /* Correct logging for implied cursor motion. */
587 * If changing within a single line, the line either currently has
588 * text or it doesn't. If it doesn't, just insert text. Otherwise,
589 * copy it and overwrite it.
591 if (fm
->lno
== tm
->lno
) {
592 if ((p
= file_gline(sp
, ep
, fm
->lno
, &len
)) == NULL
) {
594 if (file_lline(sp
, ep
, &lno
))
597 GETLINE_ERR(sp
, fm
->lno
);
602 LF_SET(TXT_APPENDEOL
);
605 NULL
, F_ISSET(vp
, VC_BUFFER
) ? &vp
->buffer
: NULL
,
609 LF_SET(TXT_APPENDEOL
);
610 LF_SET(TXT_EMARK
| TXT_OVERWRITE
);
612 return (v_ntext(sp
, ep
,
613 &sp
->tiq
, tm
, p
, len
, rp
, 0, OOBLNO
, flags
));
617 * It's trickier if changing over multiple lines. If we're in
618 * line mode we delete all of the lines and insert a replacement
619 * line which the user edits. If there was leading whitespace
620 * in the first line being changed, we copy it and use it as the
621 * replacement. If we're not in line mode, we just delete the
622 * text and start inserting.
627 NULL
, F_ISSET(vp
, VC_BUFFER
) ? &vp
->buffer
: NULL
, fm
, tm
, lmode
))
630 /* If replacing entire lines and there's leading text. */
631 if (lmode
&& fm
->cno
) {
632 /* Get a copy of the first line changed. */
633 if ((p
= file_gline(sp
, ep
, fm
->lno
, &len
)) == NULL
) {
634 GETLINE_ERR(sp
, fm
->lno
);
637 /* Copy the leading text elsewhere. */
638 GET_SPACE_RET(sp
, bp
, blen
, fm
->cno
);
639 memmove(bp
, p
, fm
->cno
);
643 /* Delete the text. */
644 if (delete(sp
, ep
, fm
, tm
, lmode
))
647 /* If replacing entire lines, insert a replacement line. */
649 if (file_iline(sp
, ep
, fm
->lno
, bp
, fm
->cno
))
652 len
= sp
->cno
= fm
->cno
;
655 /* Get the line we're editing. */
656 if ((p
= file_gline(sp
, ep
, fm
->lno
, &len
)) == NULL
) {
657 if (file_lline(sp
, ep
, &lno
))
660 GETLINE_ERR(sp
, fm
->lno
);
666 /* Check to see if we're appending to the line. */
668 LF_SET(TXT_APPENDEOL
);
673 rval
= v_ntext(sp
, ep
, &sp
->tiq
, tm
, p
, len
, rp
, 0, OOBLNO
, flags
);
676 FREE_SPACE(sp
, bp
, blen
);
681 * v_Replace -- [count]R
682 * Overwrite multiple characters.
685 v_Replace(sp
, ep
, vp
, fm
, tm
, rp
)
698 if (F_ISSET(vp
, VC_ISDOT
))
701 cnt
= F_ISSET(vp
, VC_C1SET
) ? vp
->count
: 1;
702 if ((p
= file_gline(sp
, ep
, rp
->lno
, &len
)) == NULL
) {
703 if (file_lline(sp
, ep
, &lno
))
706 GETLINE_ERR(sp
, rp
->lno
);
710 LF_SET(TXT_APPENDEOL
);
713 LF_SET(TXT_APPENDEOL
);
714 LF_SET(TXT_OVERWRITE
| TXT_REPLACE
);
717 tm
->cno
= len
? len
: 0;
718 if (v_ntext(sp
, ep
, &sp
->tiq
, tm
, p
, len
, rp
, 0, OOBLNO
, flags
))
722 * Special case. The historic vi handled [count]R badly, in that R
723 * would replace some number of characters, and then the count would
724 * append count-1 copies of the replacing chars to the replaced space.
725 * This seems wrong, so this version counts R commands. There is some
726 * trickiness in moving back to where the user stopped replacing after
727 * each R command. Basically, if the user ended with a newline, we
728 * want to use rp->cno (which will be 0). Otherwise, use the column
729 * after the returned cursor, unless it would be past the end of the
730 * line, in which case we append to the line.
733 if ((p
= file_gline(sp
, ep
, rp
->lno
, &len
)) == NULL
)
734 GETLINE_ERR(sp
, rp
->lno
);
735 SET_TXT_STD(sp
, TXT_REPLAY
);
739 if (len
== 0 || rp
->cno
== len
- 1) {
741 LF_SET(TXT_APPENDEOL
);
746 LF_SET(TXT_OVERWRITE
| TXT_REPLACE
);
750 &sp
->tiq
, tm
, p
, len
, rp
, 0, OOBLNO
, flags
))
757 * v_subst -- [buffer][count]s
758 * Substitute characters.
761 v_subst(sp
, ep
, vp
, fm
, tm
, rp
)
773 if (F_ISSET(vp
, VC_ISDOT
))
775 if ((p
= file_gline(sp
, ep
, fm
->lno
, &len
)) == NULL
) {
776 if (file_lline(sp
, ep
, &lno
))
779 GETLINE_ERR(sp
, fm
->lno
);
783 LF_SET(TXT_APPENDEOL
);
786 LF_SET(TXT_APPENDEOL
);
787 LF_SET(TXT_EMARK
| TXT_OVERWRITE
);
791 tm
->cno
= fm
->cno
+ (F_ISSET(vp
, VC_C1SET
) ? vp
->count
: 1);
795 if (p
!= NULL
&& cut(sp
, ep
,
796 NULL
, F_ISSET(vp
, VC_BUFFER
) ? &vp
->buffer
: NULL
, fm
, tm
, 0))
799 return (v_ntext(sp
, ep
,
800 &sp
->tiq
, tm
, p
, len
, rp
, 0, OOBLNO
, flags
));