source: trunk/third/nvi/vi/v_itxt.c @ 14302

Revision 14302, 12.4 KB checked in by ghudson, 25 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r14301, which included commits to RCS files with non-trunk default branches.
Line 
1/*-
2 * Copyright (c) 1992, 1993, 1994
3 *      The Regents of the University of California.  All rights reserved.
4 * Copyright (c) 1992, 1993, 1994, 1995, 1996
5 *      Keith Bostic.  All rights reserved.
6 *
7 * See the LICENSE file for redistribution information.
8 */
9
10#include "config.h"
11
12#ifndef lint
13static const char sccsid[] = "@(#)v_itxt.c      10.16 (Berkeley) 10/23/96";
14#endif /* not lint */
15
16#include <sys/types.h>
17#include <sys/queue.h>
18#include <sys/time.h>
19
20#include <bitstring.h>
21#include <ctype.h>
22#include <errno.h>
23#include <limits.h>
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27
28#include "../common/common.h"
29#include "vi.h"
30
31/*
32 * !!!
33 * Repeated input in the historic vi is mostly wrong and this isn't very
34 * backward compatible.  For example, if the user entered "3Aab\ncd" in
35 * the historic vi, the "ab" was repeated 3 times, and the "\ncd" was then
36 * appended to the result.  There was also a hack which I don't remember
37 * right now, where "3o" would open 3 lines and then let the user fill them
38 * in, to make screen movements on 300 baud modems more tolerable.  I don't
39 * think it's going to be missed.
40 *
41 * !!!
42 * There's a problem with the way that we do logging for change commands with
43 * implied motions (e.g. A, I, O, cc, etc.).  Since the main vi loop logs the
44 * starting cursor position before the change command "moves" the cursor, the
45 * cursor position to which we return on undo will be where the user entered
46 * the change command, not the start of the change.  Several of the following
47 * routines re-log the cursor to make this work correctly.  Historic vi tried
48 * to do the same thing, and mostly got it right.  (The only spectacular way
49 * it fails is if the user entered 'o' from anywhere but the last character of
50 * the line, the undo returned the cursor to the start of the line.  If the
51 * user was on the last character of the line, the cursor returned to that
52 * position.)  We also check for mapped keys waiting, i.e. if we're in the
53 * middle of a map, don't bother logging the cursor.
54 */
55#define LOG_CORRECT {                                                   \
56        if (!MAPPED_KEYS_WAITING(sp))                                   \
57                (void)log_cursor(sp);                                   \
58}
59
60static u_int32_t set_txt_std __P((SCR *, VICMD *, u_int32_t));
61
62/*
63 * v_iA -- [count]A
64 *      Append text to the end of the line.
65 *
66 * PUBLIC: int v_iA __P((SCR *, VICMD *));
67 */
68int
69v_iA(sp, vp)
70        SCR *sp;
71        VICMD *vp;
72{
73        size_t len;
74
75        if (!db_get(sp, vp->m_start.lno, 0, NULL, &len))
76                sp->cno = len == 0 ? 0 : len - 1;
77
78        LOG_CORRECT;
79
80        return (v_ia(sp, vp));
81}
82
83/*
84 * v_ia -- [count]a
85 *         [count]A
86 *      Append text to the cursor position.
87 *
88 * PUBLIC: int v_ia __P((SCR *, VICMD *));
89 */
90int
91v_ia(sp, vp)
92        SCR *sp;
93        VICMD *vp;
94{
95        size_t len;
96        u_int32_t flags;
97        int isempty;
98        char *p;
99
100        flags = set_txt_std(sp, vp, 0);
101        sp->showmode = SM_APPEND;
102        sp->lno = vp->m_start.lno;
103
104        /* Move the cursor one column to the right and repaint the screen. */
105        if (db_eget(sp, sp->lno, &p, &len, &isempty)) {
106                if (!isempty)
107                        return (1);
108                len = 0;
109                LF_SET(TXT_APPENDEOL);
110        } else if (len) {
111                if (len == sp->cno + 1) {
112                        sp->cno = len;
113                        LF_SET(TXT_APPENDEOL);
114                } else
115                        ++sp->cno;
116        } else
117                LF_SET(TXT_APPENDEOL);
118
119        return (v_txt(sp, vp, NULL, p, len,
120            0, OOBLNO, F_ISSET(vp, VC_C1SET) ? vp->count : 1, flags));
121}
122
123/*
124 * v_iI -- [count]I
125 *      Insert text at the first nonblank.
126 *
127 * PUBLIC: int v_iI __P((SCR *, VICMD *));
128 */
129int
130v_iI(sp, vp)
131        SCR *sp;
132        VICMD *vp;
133{
134        sp->cno = 0;
135        if (nonblank(sp, vp->m_start.lno, &sp->cno))
136                return (1);
137
138        LOG_CORRECT;
139
140        return (v_ii(sp, vp));
141}
142
143/*
144 * v_ii -- [count]i
145 *         [count]I
146 *      Insert text at the cursor position.
147 *
148 * PUBLIC: int v_ii __P((SCR *, VICMD *));
149 */
150int
151v_ii(sp, vp)
152        SCR *sp;
153        VICMD *vp;
154{
155        size_t len;
156        u_int32_t flags;
157        int isempty;
158        char *p;
159
160        flags = set_txt_std(sp, vp, 0);
161        sp->showmode = SM_INSERT;
162        sp->lno = vp->m_start.lno;
163
164        if (db_eget(sp, sp->lno, &p, &len, &isempty)) {
165                if (!isempty)
166                        return (1);
167                len = 0;
168        }
169
170        if (len == 0)
171                LF_SET(TXT_APPENDEOL);
172        return (v_txt(sp, vp, NULL, p, len,
173            0, OOBLNO, F_ISSET(vp, VC_C1SET) ? vp->count : 1, flags));
174}
175
176enum which { o_cmd, O_cmd };
177static int io __P((SCR *, VICMD *, enum which));
178
179/*
180 * v_iO -- [count]O
181 *      Insert text above this line.
182 *
183 * PUBLIC: int v_iO __P((SCR *, VICMD *));
184 */
185int
186v_iO(sp, vp)
187        SCR *sp;
188        VICMD *vp;
189{
190        return (io(sp, vp, O_cmd));
191}
192
193/*
194 * v_io -- [count]o
195 *      Insert text after this line.
196 *
197 * PUBLIC: int v_io __P((SCR *, VICMD *));
198 */
199int
200v_io(sp, vp)
201        SCR *sp;
202        VICMD *vp;
203{
204        return (io(sp, vp, o_cmd));
205}
206
207static int
208io(sp, vp, cmd)
209        SCR *sp;
210        VICMD *vp;
211        enum which cmd;
212{
213        recno_t ai_line, lno;
214        size_t len;
215        u_int32_t flags;
216        char *p;
217
218        flags = set_txt_std(sp, vp, TXT_ADDNEWLINE | TXT_APPENDEOL);
219        sp->showmode = SM_INSERT;
220
221        if (sp->lno == 1) {
222                if (db_last(sp, &lno))
223                        return (1);
224                if (lno != 0)
225                        goto insert;
226                p = NULL;
227                len = 0;
228                ai_line = OOBLNO;
229        } else {
230insert:         p = "";
231                sp->cno = 0;
232                LOG_CORRECT;
233
234                if (cmd == O_cmd) {
235                        if (db_insert(sp, sp->lno, p, 0))
236                                return (1);
237                        if (db_get(sp, sp->lno, DBG_FATAL, &p, &len))
238                                return (1);
239                        ai_line = sp->lno + 1;
240                } else {
241                        if (db_append(sp, 1, sp->lno, p, 0))
242                                return (1);
243                        if (db_get(sp, ++sp->lno, DBG_FATAL, &p, &len))
244                                return (1);
245                        ai_line = sp->lno - 1;
246                }
247        }
248        return (v_txt(sp, vp, NULL, p, len,
249            0, ai_line, F_ISSET(vp, VC_C1SET) ? vp->count : 1, flags));
250}
251
252/*
253 * v_change -- [buffer][count]c[count]motion
254 *             [buffer][count]C
255 *             [buffer][count]S
256 *      Change command.
257 *
258 * PUBLIC: int v_change __P((SCR *, VICMD *));
259 */
260int
261v_change(sp, vp)
262        SCR *sp;
263        VICMD *vp;
264{
265        size_t blen, len;
266        u_int32_t flags;
267        int isempty, lmode, rval;
268        char *bp, *p;
269
270        /*
271         * 'c' can be combined with motion commands that set the resulting
272         * cursor position, i.e. "cG".  Clear the VM_RCM flags and make the
273         * resulting cursor position stick, inserting text has its own rules
274         * for cursor positioning.
275         */
276        F_CLR(vp, VM_RCM_MASK);
277        F_SET(vp, VM_RCM_SET);
278
279        /*
280         * Find out if the file is empty, it's easier to handle it as a
281         * special case.
282         */
283        if (vp->m_start.lno == vp->m_stop.lno &&
284            db_eget(sp, vp->m_start.lno, &p, &len, &isempty)) {
285                if (!isempty)
286                        return (1);
287                return (v_ia(sp, vp));
288        }
289
290        flags = set_txt_std(sp, vp, 0);
291        sp->showmode = SM_CHANGE;
292
293        /*
294         * Move the cursor to the start of the change.  Note, if autoindent
295         * is turned on, the cc command in line mode changes from the first
296         * *non-blank* character of the line, not the first character.  And,
297         * to make it just a bit more exciting, the initial space is handled
298         * as auto-indent characters.
299         */
300        lmode = F_ISSET(vp, VM_LMODE) ? CUT_LINEMODE : 0;
301        if (lmode) {
302                vp->m_start.cno = 0;
303                if (O_ISSET(sp, O_AUTOINDENT)) {
304                        if (nonblank(sp, vp->m_start.lno, &vp->m_start.cno))
305                                return (1);
306                        LF_SET(TXT_AICHARS);
307                }
308        }
309        sp->lno = vp->m_start.lno;
310        sp->cno = vp->m_start.cno;
311
312        LOG_CORRECT;
313
314        /*
315         * If not in line mode and changing within a single line, copy the
316         * text and overwrite it.
317         */
318        if (!lmode && vp->m_start.lno == vp->m_stop.lno) {
319                /*
320                 * !!!
321                 * Historic practice, c did not cut into the numeric buffers,
322                 * only the unnamed one.
323                 */
324                if (cut(sp,
325                    F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL,
326                    &vp->m_start, &vp->m_stop, lmode))
327                        return (1);
328                if (len == 0)
329                        LF_SET(TXT_APPENDEOL);
330                LF_SET(TXT_EMARK | TXT_OVERWRITE);
331                return (v_txt(sp, vp, &vp->m_stop, p, len,
332                    0, OOBLNO, F_ISSET(vp, VC_C1SET) ? vp->count : 1, flags));
333        }
334
335        /*
336         * It's trickier if in line mode or changing over multiple lines.  If
337         * we're in line mode delete all of the lines and insert a replacement
338         * line which the user edits.  If there was leading whitespace in the
339         * first line being changed, we copy it and use it as the replacement.
340         * If we're not in line mode, we delete the text and start inserting.
341         *
342         * !!!
343         * Copy the text.  Historic practice, c did not cut into the numeric
344         * buffers, only the unnamed one.
345         */
346        if (cut(sp,
347            F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL,
348            &vp->m_start, &vp->m_stop, lmode))
349                return (1);
350
351        /* If replacing entire lines and there's leading text. */
352        if (lmode && vp->m_start.cno) {
353                /*
354                 * Get a copy of the first line changed, and copy out the
355                 * leading text.
356                 */
357                if (db_get(sp, vp->m_start.lno, DBG_FATAL, &p, &len))
358                        return (1);
359                GET_SPACE_RET(sp, bp, blen, vp->m_start.cno);
360                memmove(bp, p, vp->m_start.cno);
361        } else
362                bp = NULL;
363
364        /* Delete the text. */
365        if (del(sp, &vp->m_start, &vp->m_stop, lmode))
366                return (1);
367
368        /* If replacing entire lines, insert a replacement line. */
369        if (lmode) {
370                if (db_insert(sp, vp->m_start.lno, bp, vp->m_start.cno))
371                        return (1);
372                sp->lno = vp->m_start.lno;
373                len = sp->cno = vp->m_start.cno;
374        }
375
376        /* Get the line we're editing. */
377        if (db_eget(sp, vp->m_start.lno, &p, &len, &isempty)) {
378                if (!isempty)
379                        return (1);
380                len = 0;
381        }
382
383        /* Check to see if we're appending to the line. */
384        if (vp->m_start.cno >= len)
385                LF_SET(TXT_APPENDEOL);
386
387        rval = v_txt(sp, vp, NULL, p, len,
388            0, OOBLNO, F_ISSET(vp, VC_C1SET) ? vp->count : 1, flags);
389
390        if (bp != NULL)
391                FREE_SPACE(sp, bp, blen);
392        return (rval);
393}
394
395/*
396 * v_Replace -- [count]R
397 *      Overwrite multiple characters.
398 *
399 * PUBLIC: int v_Replace __P((SCR *, VICMD *));
400 */
401int
402v_Replace(sp, vp)
403        SCR *sp;
404        VICMD *vp;
405{
406        size_t len;
407        u_int32_t flags;
408        int isempty;
409        char *p;
410
411        flags = set_txt_std(sp, vp, 0);
412        sp->showmode = SM_REPLACE;
413
414        if (db_eget(sp, vp->m_start.lno, &p, &len, &isempty)) {
415                if (!isempty)
416                        return (1);
417                len = 0;
418                LF_SET(TXT_APPENDEOL);
419        } else {
420                if (len == 0)
421                        LF_SET(TXT_APPENDEOL);
422                LF_SET(TXT_OVERWRITE | TXT_REPLACE);
423        }
424        vp->m_stop.lno = vp->m_start.lno;
425        vp->m_stop.cno = len ? len - 1 : 0;
426
427        return (v_txt(sp, vp, &vp->m_stop, p, len,
428            0, OOBLNO, F_ISSET(vp, VC_C1SET) ? vp->count : 1, flags));
429}
430
431/*
432 * v_subst -- [buffer][count]s
433 *      Substitute characters.
434 *
435 * PUBLIC: int v_subst __P((SCR *, VICMD *));
436 */
437int
438v_subst(sp, vp)
439        SCR *sp;
440        VICMD *vp;
441{
442        size_t len;
443        u_int32_t flags;
444        int isempty;
445        char *p;
446
447        flags = set_txt_std(sp, vp, 0);
448        sp->showmode = SM_CHANGE;
449
450        if (db_eget(sp, vp->m_start.lno, &p, &len, &isempty)) {
451                if (!isempty)
452                        return (1);
453                len = 0;
454                LF_SET(TXT_APPENDEOL);
455        } else {
456                if (len == 0)
457                        LF_SET(TXT_APPENDEOL);
458                LF_SET(TXT_EMARK | TXT_OVERWRITE);
459        }
460
461        vp->m_stop.lno = vp->m_start.lno;
462        vp->m_stop.cno =
463            vp->m_start.cno + (F_ISSET(vp, VC_C1SET) ? vp->count - 1 : 0);
464        if (vp->m_stop.cno > len - 1)
465                vp->m_stop.cno = len - 1;
466
467        if (p != NULL && cut(sp,
468            F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL,
469            &vp->m_start, &vp->m_stop, 0))
470                return (1);
471
472        return (v_txt(sp, vp, &vp->m_stop, p, len, 0, OOBLNO, 1, flags));
473}
474
475/*
476 * set_txt_std --
477 *      Initialize text processing flags.
478 */
479static u_int32_t
480set_txt_std(sp, vp, flags)
481        SCR *sp;
482        VICMD *vp;
483        u_int32_t flags;
484{
485        LF_SET(TXT_CNTRLT |
486            TXT_ESCAPE | TXT_MAPINPUT | TXT_RECORD | TXT_RESOLVE);
487
488        if (F_ISSET(vp, VC_ISDOT))
489                LF_SET(TXT_REPLAY);
490
491        if (O_ISSET(sp, O_ALTWERASE))
492                LF_SET(TXT_ALTWERASE);
493        if (O_ISSET(sp, O_AUTOINDENT))
494                LF_SET(TXT_AUTOINDENT);
495        if (O_ISSET(sp, O_BEAUTIFY))
496                LF_SET(TXT_BEAUTIFY);
497        if (O_ISSET(sp, O_SHOWMATCH))
498                LF_SET(TXT_SHOWMATCH);
499        if (F_ISSET(sp, SC_SCRIPT))
500                LF_SET(TXT_CR);
501        if (O_ISSET(sp, O_TTYWERASE))
502                LF_SET(TXT_TTYWERASE);
503
504        /*
505         * !!!
506         * Mapped keys were sometimes unaffected by the wrapmargin option
507         * in the historic 4BSD vi.  Consider the following commands, where
508         * each is executed on an empty line, in an 80 column screen, with
509         * the wrapmargin value set to 60.
510         *
511         *      aABC DEF <ESC>....
512         *      :map K aABC DEF ^V<ESC><CR>KKKKK
513         *      :map K 5aABC DEF ^V<ESC><CR>K
514         *
515         * The first and second commands are affected by wrapmargin.  The
516         * third is not.  (If the inserted text is itself longer than the
517         * wrapmargin value, i.e. if the "ABC DEF " string is replaced by
518         * something that's longer than 60 columns from the beginning of
519         * the line, the first two commands behave as before, but the third
520         * command gets fairly strange.)  The problem is that people wrote
521         * macros that depended on the third command NOT being affected by
522         * wrapmargin, as in this gem which centers lines:
523         *
524         *      map #c $mq81a ^V^[81^V^V|D`qld0:s/  / /g^V^M$p
525         *
526         * For compatibility reasons, we try and make it all work here.  I
527         * offer no hope that this is right, but it's probably pretty close.
528         *
529         * XXX
530         * Once I work my courage up, this is all gonna go away.  It's too
531         * evil to survive.
532         */
533        if ((O_ISSET(sp, O_WRAPLEN) || O_ISSET(sp, O_WRAPMARGIN)) &&
534            (!MAPPED_KEYS_WAITING(sp) || !F_ISSET(vp, VC_C1SET)))
535                LF_SET(TXT_WRAPMARGIN);
536        return (flags);
537}
Note: See TracBrowser for help on using the repository browser.