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.20 1993/12/09 19:43:19 bostic Exp $ (Berkeley) $Date: 1993/12/09 19:43:19 $";
12 #include <sys/types.h>
23 * Repeated input in the historic vi is mostly wrong and this isn't very
24 * backward compatible. For example, if the user entered "3Aab\ncd" in
25 * the historic vi, the "ab" was repeated 3 times, and the "\ncd" was then
26 * appended to the result. There was also a hack which I don't remember
27 * right now, where "3o" would open 3 lines and then let the user fill them
28 * in, to make screen movements on 300 baud modems more tolerable. I don't
29 * think it's going to be missed.
32 #define SET_TXT_STD(sp, f) { \
33 LF_INIT((f) | TXT_BEAUTIFY | TXT_CNTRLT | TXT_ESCAPE | \
34 TXT_MAPINPUT | TXT_RECORD | TXT_RESOLVE); \
35 if (O_ISSET(sp, O_ALTWERASE)) \
36 LF_SET(TXT_ALTWERASE); \
37 if (O_ISSET(sp, O_AUTOINDENT)) \
38 LF_SET(TXT_AUTOINDENT); \
39 if (O_ISSET(sp, O_SHOWMATCH)) \
40 LF_SET(TXT_SHOWMATCH); \
41 if (O_ISSET(sp, O_WRAPMARGIN)) \
42 LF_SET(TXT_WRAPMARGIN); \
43 if (F_ISSET(sp, S_SCRIPT)) \
45 if (O_ISSET(sp, O_TTYWERASE)) \
46 LF_SET(TXT_TTYWERASE); \
49 static int v_CS
__P((SCR
*, EXF
*, VICMDARG
*, MARK
*, MARK
*, MARK
*, u_int
));
53 * Append text to the end of the line.
56 v_iA(sp
, ep
, vp
, fm
, tm
, rp
)
68 SET_TXT_STD(sp
, TXT_APPENDEOL
);
69 if (F_ISSET(vp
, VC_ISDOT
))
72 for (cnt
= F_ISSET(vp
, VC_C1SET
) ? vp
->count
: 1; cnt
--;) {
73 /* Move the cursor to the end of the line + 1. */
74 if ((p
= file_gline(sp
, ep
, lno
, &len
)) == NULL
) {
75 if (file_lline(sp
, ep
, &lno
))
87 &sp
->tiq
, NULL
, p
, len
, rp
, 0, OOBLNO
, flags
))
90 SET_TXT_STD(sp
, TXT_APPENDEOL
| TXT_REPLAY
);
91 sp
->lno
= lno
= rp
->lno
;
99 * Append text to the cursor position.
102 v_ia(sp
, ep
, vp
, fm
, tm
, rp
)
115 if (F_ISSET(vp
, VC_ISDOT
))
118 for (cnt
= F_ISSET(vp
, VC_C1SET
) ? vp
->count
: 1; cnt
--;) {
120 * Move the cursor one column to the right and
121 * repaint the screen.
123 if ((p
= file_gline(sp
, ep
, lno
, &len
)) == NULL
) {
124 if (file_lline(sp
, ep
, &lno
))
127 GETLINE_ERR(sp
, lno
);
132 LF_SET(TXT_APPENDEOL
);
134 if (len
== sp
->cno
+ 1) {
136 LF_SET(TXT_APPENDEOL
);
140 LF_SET(TXT_APPENDEOL
);
143 &sp
->tiq
, NULL
, p
, len
, rp
, 0, OOBLNO
, flags
))
146 SET_TXT_STD(sp
, TXT_REPLAY
);
147 sp
->lno
= lno
= rp
->lno
;
155 * Insert text at the first non-blank character in the line.
158 v_iI(sp
, ep
, vp
, fm
, tm
, rp
)
171 if (F_ISSET(vp
, VC_ISDOT
))
174 for (cnt
= F_ISSET(vp
, VC_C1SET
) ? vp
->count
: 1; cnt
--;) {
176 * Move the cursor to the start of the line and repaint
179 if ((p
= file_gline(sp
, ep
, lno
, &len
)) == NULL
) {
180 if (file_lline(sp
, ep
, &lno
))
183 GETLINE_ERR(sp
, lno
);
189 for (t
= p
, wlen
= len
; wlen
-- && isblank(*t
); ++t
);
193 LF_SET(TXT_APPENDEOL
);
196 &sp
->tiq
, NULL
, p
, len
, rp
, 0, OOBLNO
, flags
))
199 SET_TXT_STD(sp
, TXT_REPLAY
);
200 sp
->lno
= lno
= rp
->lno
;
208 * Insert text at the cursor position.
211 v_ii(sp
, ep
, vp
, fm
, tm
, rp
)
224 if (F_ISSET(vp
, VC_ISDOT
))
227 for (cnt
= F_ISSET(vp
, VC_C1SET
) ? vp
->count
: 1; cnt
--;) {
228 if ((p
= file_gline(sp
, ep
, lno
, &len
)) == NULL
) {
229 if (file_lline(sp
, ep
, &lno
))
232 GETLINE_ERR(sp
, fm
->lno
);
238 /* If len == sp->cno, it's a replay caused by a count. */
239 if (len
== 0 || len
== sp
->cno
)
240 LF_SET(TXT_APPENDEOL
);
243 &sp
->tiq
, NULL
, p
, len
, rp
, 0, OOBLNO
, flags
))
247 * On replay, if the line isn't empty, advance the insert
248 * by one (make it an append).
250 SET_TXT_STD(sp
, TXT_REPLAY
);
251 sp
->lno
= lno
= rp
->lno
;
252 if ((sp
->cno
= rp
->cno
) != 0)
260 * Insert text above this line.
263 v_iO(sp
, ep
, vp
, fm
, tm
, rp
)
269 recno_t ai_line
, lno
;
275 SET_TXT_STD(sp
, TXT_APPENDEOL
);
276 if (F_ISSET(vp
, VC_ISDOT
))
278 for (cnt
= F_ISSET(vp
, VC_C1SET
) ? vp
->count
: 1; cnt
--;) {
280 if (file_lline(sp
, ep
, &lno
))
289 if (file_iline(sp
, ep
, sp
->lno
, p
, 0))
291 if ((p
= file_gline(sp
, ep
, sp
->lno
, &len
)) == NULL
) {
292 GETLINE_ERR(sp
, sp
->lno
);
296 ai_line
= sp
->lno
+ 1;
300 &sp
->tiq
, NULL
, p
, len
, rp
, 0, ai_line
, flags
))
303 SET_TXT_STD(sp
, TXT_APPENDEOL
| TXT_REPLAY
);
304 sp
->lno
= lno
= rp
->lno
;
312 * Insert text after this line.
315 v_io(sp
, ep
, vp
, fm
, tm
, rp
)
321 recno_t ai_line
, lno
;
327 SET_TXT_STD(sp
, TXT_APPENDEOL
);
328 if (F_ISSET(vp
, VC_ISDOT
))
330 for (cnt
= F_ISSET(vp
, VC_C1SET
) ? vp
->count
: 1; cnt
--;) {
332 if (file_lline(sp
, ep
, &lno
))
342 if (file_aline(sp
, ep
, 1, sp
->lno
, p
, len
))
344 if ((p
= file_gline(sp
, ep
, ++sp
->lno
, &len
)) == NULL
) {
345 GETLINE_ERR(sp
, sp
->lno
);
349 ai_line
= sp
->lno
- 1;
353 &sp
->tiq
, NULL
, p
, len
, rp
, 0, ai_line
, flags
))
356 SET_TXT_STD(sp
, TXT_APPENDEOL
| TXT_REPLAY
);
357 sp
->lno
= lno
= rp
->lno
;
364 * v_Change -- [buffer][count]C
365 * Change line command.
368 v_Change(sp
, ep
, vp
, fm
, tm
, rp
)
374 return (v_CS(sp
, ep
, vp
, fm
, tm
, rp
, 0));
378 * v_Subst -- [buffer][count]S
379 * Line substitute command.
382 v_Subst(sp
, ep
, vp
, fm
, tm
, rp
)
391 * The S command is the same as a 'C' command from the beginning
392 * of the line. This is hard to do in the parser, so do it here.
394 * If autoindent is on, the change is from the first *non-blank*
395 * character of the line, not the first character. And, to make
396 * it just a bit more exciting, the initial space is handled as
397 * auto-indent characters.
400 if (O_ISSET(sp
, O_AUTOINDENT
)) {
402 if (nonblank(sp
, ep
, fm
->lno
, &fm
->cno
))
408 return (v_CS(sp
, ep
, vp
, fm
, tm
, rp
, flags
));
416 v_CS(sp
, ep
, vp
, fm
, tm
, rp
, iflags
)
428 SET_TXT_STD(sp
, iflags
);
429 if (F_ISSET(vp
, VC_ISDOT
))
433 * There are two cases -- if a count is supplied, we do a line
434 * mode change where we delete the lines and then insert text
435 * into a new line. Otherwise, we replace the current line.
437 tm
->lno
= fm
->lno
+ (F_ISSET(vp
, VC_C1SET
) ? vp
->count
- 1 : 0);
438 if (fm
->lno
!= tm
->lno
) {
439 /* Make sure that the to line is real. */
440 if (file_gline(sp
, ep
, tm
->lno
, NULL
) == NULL
) {
447 F_ISSET(vp
, VC_BUFFER
) ? vp
->buffer
: DEFCB
, fm
, tm
, 1))
450 /* Insert a line while we still can... */
451 if (file_iline(sp
, ep
, fm
->lno
, "", 0))
456 /* Delete the lines. */
457 if (delete(sp
, ep
, fm
, tm
, 1))
460 /* Get the inserted line. */
461 if ((p
= file_gline(sp
, ep
, --fm
->lno
, &len
)) == NULL
) {
462 GETLINE_ERR(sp
, fm
->lno
);
468 LF_SET(TXT_APPENDEOL
);
470 /* The line may be empty, but that's okay. */
471 if ((p
= file_gline(sp
, ep
, fm
->lno
, &len
)) == NULL
) {
472 if (file_lline(sp
, ep
, &lno
))
475 GETLINE_ERR(sp
, tm
->lno
);
479 LF_SET(TXT_APPENDEOL
);
482 F_ISSET(vp
, VC_BUFFER
) ? vp
->buffer
: DEFCB
,
487 LF_SET(TXT_APPENDEOL
);
488 LF_SET(TXT_EMARK
| TXT_OVERWRITE
);
491 return (v_ntext(sp
, ep
,
492 &sp
->tiq
, tm
, p
, len
, rp
, 0, OOBLNO
, flags
));
496 * v_change -- [buffer][count]c[count]motion
500 v_change(sp
, ep
, vp
, fm
, tm
, rp
)
513 if (F_ISSET(vp
, VC_ISDOT
))
517 * Move the cursor to the start of the change. Note, if autoindent
518 * is turned on, the cc command in line mode changes from the first
519 * *non-blank* character of the line, not the first character. And,
520 * to make it just a bit more exciting, the initial space is handled
521 * as auto-indent characters.
523 if (lmode
= F_ISSET(vp
, VC_LMODE
)) {
525 if (O_ISSET(sp
, O_AUTOINDENT
)) {
526 if (nonblank(sp
, ep
, fm
->lno
, &fm
->cno
))
535 * If changing within a single line, the line either currently has
536 * text or it doesn't. If it doesn't, just insert text. Otherwise,
537 * copy it and overwrite it.
539 if (fm
->lno
== tm
->lno
) {
540 if ((p
= file_gline(sp
, ep
, fm
->lno
, &len
)) == NULL
) {
542 if (file_lline(sp
, ep
, &lno
))
545 GETLINE_ERR(sp
, fm
->lno
);
550 LF_SET(TXT_APPENDEOL
);
553 F_ISSET(vp
, VC_BUFFER
) ? vp
->buffer
: DEFCB
,
557 LF_SET(TXT_APPENDEOL
);
558 LF_SET(TXT_EMARK
| TXT_OVERWRITE
);
560 return (v_ntext(sp
, ep
,
561 &sp
->tiq
, tm
, p
, len
, rp
, 0, OOBLNO
, flags
));
565 * It's trickier if changing over multiple lines. If we're in
566 * line mode we delete all of the lines and insert a replacement
567 * line which the user edits. If there was leading whitespace
568 * in the first line being changed, we copy it and use it as the
569 * replacement. If we're not in line mode, we just delete the
570 * text and start inserting.
575 F_ISSET(vp
, VC_BUFFER
) ? vp
->buffer
: DEFCB
, fm
, tm
, lmode
))
578 /* If replacing entire lines and there's leading text. */
579 if (lmode
&& fm
->cno
) {
580 /* Get a copy of the first line changed. */
581 if ((p
= file_gline(sp
, ep
, fm
->lno
, &len
)) == NULL
) {
582 GETLINE_ERR(sp
, fm
->lno
);
585 /* Copy the leading text elsewhere. */
586 GET_SPACE_RET(sp
, bp
, blen
, fm
->cno
);
587 memmove(bp
, p
, fm
->cno
);
591 /* Delete the text. */
592 if (delete(sp
, ep
, fm
, tm
, lmode
))
595 /* If replacing entire lines, insert a replacement line. */
597 if (file_iline(sp
, ep
, fm
->lno
, bp
, fm
->cno
))
600 len
= sp
->cno
= fm
->cno
;
603 /* Get the line we're editing. */
604 if ((p
= file_gline(sp
, ep
, fm
->lno
, &len
)) == NULL
) {
605 if (file_lline(sp
, ep
, &lno
))
608 GETLINE_ERR(sp
, fm
->lno
);
614 /* Check to see if we're appending to the line. */
616 LF_SET(TXT_APPENDEOL
);
621 rval
= v_ntext(sp
, ep
, &sp
->tiq
, tm
, p
, len
, rp
, 0, OOBLNO
, flags
);
624 FREE_SPACE(sp
, bp
, blen
);
629 * v_Replace -- [count]R
630 * Overwrite multiple characters.
633 v_Replace(sp
, ep
, vp
, fm
, tm
, rp
)
646 if (F_ISSET(vp
, VC_ISDOT
))
649 cnt
= F_ISSET(vp
, VC_C1SET
) ? vp
->count
: 1;
650 if ((p
= file_gline(sp
, ep
, rp
->lno
, &len
)) == NULL
) {
651 if (file_lline(sp
, ep
, &lno
))
654 GETLINE_ERR(sp
, rp
->lno
);
658 LF_SET(TXT_APPENDEOL
);
661 LF_SET(TXT_APPENDEOL
);
662 LF_SET(TXT_OVERWRITE
| TXT_REPLACE
);
665 tm
->cno
= len
? len
: 0;
666 if (v_ntext(sp
, ep
, &sp
->tiq
, tm
, p
, len
, rp
, 0, OOBLNO
, flags
))
670 * Special case. The historic vi handled [count]R badly, in that R
671 * would replace some number of characters, and then the count would
672 * append count-1 copies of the replacing chars to the replaced space.
673 * This seems wrong, so this version counts R commands. There is some
674 * trickiness in moving back to where the user stopped replacing after
675 * each R command. Basically, if the user ended with a newline, we
676 * want to use rp->cno (which will be 0). Otherwise, use the column
677 * after the returned cursor, unless it would be past the end of the
678 * line, in which case we append to the line.
681 if ((p
= file_gline(sp
, ep
, rp
->lno
, &len
)) == NULL
)
682 GETLINE_ERR(sp
, rp
->lno
);
683 SET_TXT_STD(sp
, TXT_REPLAY
);
687 if (len
== 0 || rp
->cno
== len
- 1) {
689 LF_SET(TXT_APPENDEOL
);
694 LF_SET(TXT_OVERWRITE
| TXT_REPLACE
);
698 &sp
->tiq
, tm
, p
, len
, rp
, 0, OOBLNO
, flags
))
705 * v_subst -- [buffer][count]s
706 * Substitute characters.
709 v_subst(sp
, ep
, vp
, fm
, tm
, rp
)
721 if (F_ISSET(vp
, VC_ISDOT
))
723 if ((p
= file_gline(sp
, ep
, fm
->lno
, &len
)) == NULL
) {
724 if (file_lline(sp
, ep
, &lno
))
727 GETLINE_ERR(sp
, fm
->lno
);
731 LF_SET(TXT_APPENDEOL
);
734 LF_SET(TXT_APPENDEOL
);
735 LF_SET(TXT_EMARK
| TXT_OVERWRITE
);
739 tm
->cno
= fm
->cno
+ (F_ISSET(vp
, VC_C1SET
) ? vp
->count
: 1);
743 if (p
!= NULL
&& cut(sp
, ep
,
744 F_ISSET(vp
, VC_BUFFER
) ? vp
->buffer
: DEFCB
, fm
, tm
, 0))
747 return (v_ntext(sp
, ep
,
748 &sp
->tiq
, tm
, p
, len
, rp
, 0, OOBLNO
, flags
));