1 /*        $NetBSD: v_txt.c,v 1.7 2020/07/07 10:58:43 rin Exp $ */
2 /*-
3  * Copyright (c) 1993, 1994
4  *        The Regents of the University of California.  All rights reserved.
5  * Copyright (c) 1992, 1993, 1994, 1995, 1996
6  *        Keith Bostic.  All rights reserved.
7  *
8  * See the LICENSE file for redistribution information.
9  */
10 
11 #include "config.h"
12 
13 #include <sys/cdefs.h>
14 #if 0
15 #ifndef lint
16 static const char sccsid[] = "Id: v_txt.c,v 10.108 2003/07/18 21:27:42 skimo Exp  (Berkeley) Date: 2003/07/18 21:27:42 ";
17 #endif /* not lint */
18 #else
19 __RCSID("$NetBSD: v_txt.c,v 1.7 2020/07/07 10:58:43 rin Exp $");
20 #endif
21 
22 #include <sys/types.h>
23 #include <sys/queue.h>
24 #include <sys/stat.h>
25 #include <sys/time.h>
26 
27 #include <bitstring.h>
28 #include <ctype.h>
29 #include <errno.h>
30 #include <limits.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <unistd.h>
35 
36 #include "../common/common.h"
37 #include "vi.h"
38 
39 static int           txt_abbrev __P((SCR *, TEXT *, ARG_CHAR_T *, int, int *, int *));
40 static void          txt_ai_resolve __P((SCR *, TEXT *, int *));
41 static TEXT         *txt_backup __P((SCR *, TEXTH *, TEXT *, u_int32_t *));
42 static int           txt_dent __P((SCR *, TEXT *, int));
43 static int           txt_emark __P((SCR *, TEXT *, size_t));
44 static void          txt_err __P((SCR *, TEXTH *));
45 static int           txt_fc __P((SCR *, TEXT *, int *));
46 static int           txt_fc_col __P((SCR *, int, ARGS **));
47 static int           txt_hex __P((SCR *, TEXT *));
48 static int           txt_insch __P((SCR *, TEXT *, ARG_CHAR_T *, u_int));
49 static int           txt_isrch __P((SCR *, VICMD *, TEXT *, u_int8_t *));
50 static int           txt_map_end __P((SCR *));
51 static int           txt_map_init __P((SCR *));
52 static int           txt_margin __P((SCR *, TEXT *, TEXT *, int *, u_int32_t));
53 static void          txt_nomorech __P((SCR *));
54 static void          txt_Rresolve __P((SCR *, TEXTH *, TEXT *, const size_t));
55 static int           txt_resolve __P((SCR *, TEXTH *, u_int32_t));
56 static int           txt_showmatch __P((SCR *, TEXT *));
57 static void          txt_unmap __P((SCR *, TEXT *, u_int32_t *));
58 
59 /* Cursor character (space is hard to track on the screen). */
60 #if defined(DEBUG) && 0
61 #undef    CH_CURSOR
62 #define   CH_CURSOR '+'
63 #endif
64 
65 /*
66  * v_tcmd --
67  *        Fill a buffer from the terminal for vi.
68  *
69  * PUBLIC: int v_tcmd __P((SCR *, VICMD *, ARG_CHAR_T, u_int));
70  */
71 int
v_tcmd(SCR * sp,VICMD * vp,ARG_CHAR_T prompt,u_int flags)72 v_tcmd(SCR *sp, VICMD *vp, ARG_CHAR_T prompt, u_int flags)
73 {
74           /* Normally, we end up where we started. */
75           vp->m_final.lno = sp->lno;
76           vp->m_final.cno = sp->cno;
77 
78           /* Initialize the map. */
79           if (txt_map_init(sp))
80                     return (1);
81 
82           /* Move to the last line. */
83           sp->lno = TMAP[0].lno;
84           sp->cno = 0;
85 
86           /* Don't update the modeline for now. */
87           F_SET(sp, SC_TINPUT_INFO);
88 
89           /* Set the input flags. */
90           LF_SET(TXT_APPENDEOL |
91               TXT_CR | TXT_ESCAPE | TXT_INFOLINE | TXT_MAPINPUT);
92           if (O_ISSET(sp, O_ALTWERASE))
93                     LF_SET(TXT_ALTWERASE);
94           if (O_ISSET(sp, O_TTYWERASE))
95                     LF_SET(TXT_TTYWERASE);
96 
97           /* Do the input thing. */
98           if (v_txt(sp, vp, NULL, NULL, 0, prompt, 0, 1, flags))
99                     return (1);
100 
101           /* Reenable the modeline updates. */
102           F_CLR(sp, SC_TINPUT_INFO);
103 
104           /* Clean up the map. */
105           if (txt_map_end(sp))
106                     return (1);
107 
108           if (IS_ONELINE(sp))
109                     F_SET(sp, SC_SCR_REDRAW);     /* XXX */
110 
111           /* Set the cursor to the resulting position. */
112           sp->lno = vp->m_final.lno;
113           sp->cno = vp->m_final.cno;
114 
115           return (0);
116 }
117 
118 /*
119  * txt_map_init
120  *        Initialize the screen map for colon command-line input.
121  */
122 static int
txt_map_init(SCR * sp)123 txt_map_init(SCR *sp)
124 {
125           SMAP *esmp;
126           VI_PRIVATE *vip;
127 
128           vip = VIP(sp);
129           if (!IS_ONELINE(sp)) {
130                     /*
131                      * Fake like the user is doing input on the last line of the
132                      * screen.  This makes all of the scrolling work correctly,
133                      * and allows us the use of the vi text editing routines, not
134                      * to mention practically infinite length ex commands.
135                      *
136                      * Save the current location.
137                      */
138                     vip->sv_tm_lno = TMAP->lno;
139                     vip->sv_tm_soff = TMAP->soff;
140                     vip->sv_tm_coff = TMAP->coff;
141                     vip->sv_t_maxrows = sp->t_maxrows;
142                     vip->sv_t_minrows = sp->t_minrows;
143                     vip->sv_t_rows = sp->t_rows;
144 
145                     /*
146                      * If it's a small screen, TMAP may be small for the screen.
147                      * Fix it, filling in fake lines as we go.
148                      */
149                     if (IS_SMALL(sp))
150                               for (esmp =
151                                   HMAP + (sp->t_maxrows - 1); TMAP < esmp; ++TMAP) {
152                                         TMAP[1].lno = TMAP[0].lno + 1;
153                                         TMAP[1].coff = HMAP->coff;
154                                         TMAP[1].soff = 1;
155                               }
156 
157                     /* Build the fake entry. */
158                     TMAP[1].lno = TMAP[0].lno + 1;
159                     TMAP[1].soff = 1;
160                     TMAP[1].coff = 0;
161                     SMAP_FLUSH(&TMAP[1]);
162                     ++TMAP;
163 
164                     /* Reset the screen information. */
165                     sp->t_rows = sp->t_minrows = ++sp->t_maxrows;
166           }
167           return (0);
168 }
169 
170 /*
171  * txt_map_end
172  *        Reset the screen map for colon command-line input.
173  */
174 static int
txt_map_end(SCR * sp)175 txt_map_end(SCR *sp)
176 {
177           VI_PRIVATE *vip;
178           size_t cnt;
179 
180           vip = VIP(sp);
181           if (!IS_ONELINE(sp)) {
182                     /* Restore the screen information. */
183                     sp->t_rows = vip->sv_t_rows;
184                     sp->t_minrows = vip->sv_t_minrows;
185                     sp->t_maxrows = vip->sv_t_maxrows;
186 
187                     /*
188                      * If it's a small screen, TMAP may be wrong.  Clear any
189                      * lines that might have been overwritten.
190                      */
191                     if (IS_SMALL(sp)) {
192                               for (cnt = sp->t_rows; cnt <= sp->t_maxrows; ++cnt) {
193                                         (void)sp->gp->scr_move(sp, cnt, 0);
194                                         (void)sp->gp->scr_clrtoeol(sp);
195                               }
196                               TMAP = HMAP + (sp->t_rows - 1);
197                     } else
198                               --TMAP;
199 
200                     /*
201                      * The map may be wrong if the user entered more than one
202                      * (logical) line.  Fix it.  If the user entered a whole
203                      * screen, this will be slow, but we probably don't care.
204                      */
205                     if (!O_ISSET(sp, O_LEFTRIGHT))
206                               while (vip->sv_tm_lno != TMAP->lno ||
207                                   vip->sv_tm_soff != TMAP->soff)
208                                         if (vs_sm_1down(sp))
209                                                   return (1);
210           }
211 
212           /*
213            * Invalidate the cursor and the line size cache, the line never
214            * really existed.  This fixes bugs where the user searches for
215            * the last line on the screen + 1 and the refresh routine thinks
216            * that's where we just were.
217            */
218           VI_SCR_CFLUSH(vip);
219           F_SET(vip, VIP_CUR_INVALID);
220 
221           return (0);
222 }
223 
224 /*
225  * If doing input mapping on the colon command line, may need to unmap
226  * based on the command.
227  */
228 #define   UNMAP_TST                                                             \
229           FL_ISSET(ec_flags, EC_MAPINPUT) && LF_ISSET(TXT_INFOLINE)
230 
231 /*
232  * Internally, we maintain tp->lno and tp->cno, externally, everyone uses
233  * sp->lno and sp->cno.  Make them consistent as necessary.
234  */
235 #define   UPDATE_POSITION(sp, tp) {                                             \
236           (sp)->lno = (tp)->lno;                                                          \
237           (sp)->cno = (tp)->cno;                                                          \
238 }
239 
240 /*
241  * v_txt --
242  *        Vi text input.
243  *
244  * PUBLIC: int v_txt __P((SCR *, VICMD *, MARK *,
245  * PUBLIC:    const CHAR_T *, size_t, ARG_CHAR_T, db_recno_t, u_long, u_int32_t));
246  */
247 int
v_txt(SCR * sp,VICMD * vp,MARK * tm,const CHAR_T * lp,size_t len,ARG_CHAR_T prompt,db_recno_t ai_line,u_long rcount,u_int32_t flags)248 v_txt(SCR *sp, VICMD *vp, MARK *tm, const CHAR_T *lp, size_t len, ARG_CHAR_T prompt, db_recno_t ai_line, u_long rcount, u_int32_t flags)
249 
250 
251                               /* To MARK. */
252                               /* Input line. */
253                                         /* Input line length. */
254                               /* Prompt to display. */
255                               /* Line number to use for autoindent count. */
256                                         /* Replay count. */
257                               /* TXT_* flags. */
258 {
259           EVENT ev, *evp = NULL;        /* Current event. */
260           EVENT fc;           /* File name completion event. */
261           GS *gp;
262           TEXT *ntp, *tp;               /* Input text structures. */
263           TEXT ait;           /* Autoindent text structure. */
264           TEXT wmt;           /* Wrapmargin text structure. */
265           TEXTH *tiqh;
266           VI_PRIVATE *vip;
267           abb_t abb;                    /* State of abbreviation checks. */
268           carat_t carat;                /* State of the "[^0]^D" sequences. */
269           quote_t quote;                /* State of quotation. */
270           size_t owrite, insert;        /* Temporary copies of TEXT fields. */
271           size_t margin;                /* Wrapmargin value. */
272           size_t rcol;                  /* 0-N: insert offset in the replay buffer. */
273           size_t tcol;                  /* Temporary column. */
274           u_int32_t ec_flags; /* Input mapping flags. */
275 #define   IS_RESTART          0x01      /* Reset the incremental search. */
276 #define   IS_RUNNING          0x02      /* Incremental search turned on. */
277           u_int8_t is_flags;
278           int abcnt, ab_turnoff;        /* Abbreviation character count, switch. */
279           int filec_redraw;   /* Redraw after the file completion routine. */
280           int hexcnt;                   /* Hex character count. */
281           int showmatch;                /* Showmatch set on this character. */
282           int wm_set, wm_skip;          /* Wrapmargin happened, blank skip flags. */
283           size_t max;
284           int tmp;
285           int nochange;
286           CHAR_T *p;
287 
288           gp = sp->gp;
289           vip = VIP(sp);
290           memset(&wmt, 0, sizeof(wmt));
291 
292           /*
293            * Set the input flag, so tabs get displayed correctly
294            * and everyone knows that the text buffer is in use.
295            */
296           F_SET(sp, SC_TINPUT);
297 
298           /*
299            * Get one TEXT structure with some initial buffer space, reusing
300            * the last one if it's big enough.  (All TEXT bookkeeping fields
301            * default to 0 -- text_init() handles this.)  If changing a line,
302            * copy it into the TEXT buffer.
303            */
304           tiqh = &sp->tiq;
305           if (!TAILQ_EMPTY(tiqh)) {
306                     tp = TAILQ_FIRST(tiqh);
307                     if (TAILQ_NEXT(tp, q) != NULL || tp->lb_len < len + 32) {
308                               text_lfree(tiqh);
309                               goto newtp;
310                     }
311                     tp->ai = tp->insert = tp->offset = tp->owrite = 0;
312                     if (lp != NULL) {
313                               tp->len = len;
314                               BINC_RETW(sp, tp->lb, tp->lb_len, len);
315                               MEMMOVEW(tp->lb, lp, len);
316                     } else
317                               tp->len = 0;
318           } else {
319 newtp:              if ((tp = text_init(sp, lp, len, len + 32)) == NULL)
320                               return (1);
321                     TAILQ_INSERT_HEAD(tiqh, tp, q);
322           }
323 
324           /* Set default termination condition. */
325           tp->term = TERM_OK;
326 
327           /* Set the starting line, column. */
328           tp->lno = sp->lno;
329           tp->cno = sp->cno;
330 
331           /*
332            * Set the insert and overwrite counts.  If overwriting characters,
333            * do insertion afterward.  If not overwriting characters, assume
334            * doing insertion.  If change is to a mark, emphasize it with an
335            * CH_ENDMARK character.
336            */
337           if (len) {
338                     if (LF_ISSET(TXT_OVERWRITE)) {
339                               tp->owrite = (tm->cno - tp->cno) + 1;
340                               tp->insert = (len - tm->cno) - 1;
341                     } else
342                               tp->insert = len - tp->cno;
343 
344                     if (LF_ISSET(TXT_EMARK) && txt_emark(sp, tp, tm->cno))
345                               return (1);
346           }
347 
348           /*
349            * Many of the special cases in text input are to handle autoindent
350            * support.  Somebody decided that it would be a good idea if "^^D"
351            * and "0^D" deleted all of the autoindented characters.  In an editor
352            * that takes single character input from the user, this beggars the
353            * imagination.  Note also, "^^D" resets the next lines' autoindent,
354            * but "0^D" doesn't.
355            *
356            * We assume that autoindent only happens on empty lines, so insert
357            * and overwrite will be zero.  If doing autoindent, figure out how
358            * much indentation we need and fill it in.  Update input column and
359            * screen cursor as necessary.
360            */
361           if (LF_ISSET(TXT_AUTOINDENT) && ai_line != OOBLNO) {
362                     if (v_txt_auto(sp, ai_line, NULL, 0, tp))
363                               return (1);
364                     tp->cno = tp->ai;
365           } else {
366                     /*
367                      * The cc and S commands have a special feature -- leading
368                      * <blank> characters are handled as autoindent characters.
369                      * Beauty!
370                      */
371                     if (LF_ISSET(TXT_AICHARS)) {
372                               tp->offset = 0;
373                               tp->ai = tp->cno;
374                     } else
375                               tp->offset = tp->cno;
376           }
377 
378           /* If getting a command buffer from the user, there may be a prompt. */
379           if (LF_ISSET(TXT_PROMPT)) {
380                     tp->lb[tp->cno++] = prompt;
381                     ++tp->len;
382                     ++tp->offset;
383           }
384 
385           /*
386            * If appending after the end-of-line, add a space into the buffer
387            * and move the cursor right.  This space is inserted, i.e. pushed
388            * along, and then deleted when the line is resolved.  Assumes that
389            * the cursor is already positioned at the end of the line.  This
390            * avoids the nastiness of having the cursor reside on a magical
391            * column, i.e. a column that doesn't really exist.  The only down
392            * side is that we may wrap lines or scroll the screen before it's
393            * strictly necessary.  Not a big deal.
394            */
395           if (LF_ISSET(TXT_APPENDEOL)) {
396                     tp->lb[tp->cno] = CH_CURSOR;
397                     ++tp->len;
398                     ++tp->insert;
399                     (void)vs_change(sp, tp->lno, LINE_RESET);
400           }
401 
402           /*
403            * Historic practice is that the wrapmargin value was a distance
404            * from the RIGHT-HAND margin, not the left.  It's more useful to
405            * us as a distance from the left-hand margin, i.e. the same as
406            * the wraplen value.  The wrapmargin option is historic practice.
407            * Nvi added the wraplen option so that it would be possible to
408            * edit files with consistent margins without knowing the number of
409            * columns in the window.
410            *
411            * XXX
412            * Setting margin causes a significant performance hit.  Normally
413            * we don't update the screen if there are keys waiting, but we
414            * have to if margin is set, otherwise the screen routines don't
415            * know where the cursor is.
416            *
417            * !!!
418            * Abbreviated keys were affected by the wrapmargin option in the
419            * historic 4BSD vi.  Mapped keys were usually, but sometimes not.
420            * See the comment in vi/v_text():set_txt_std for more information.
421            *
422            * !!!
423            * One more special case.  If an inserted <blank> character causes
424            * wrapmargin to split the line, the next user entered character is
425            * discarded if it's a <space> character.
426            */
427           wm_set = wm_skip = 0;
428           if (LF_ISSET(TXT_WRAPMARGIN))
429                     if ((margin = O_VAL(sp, O_WRAPMARGIN)) != 0)
430                               margin = sp->cols - margin;
431                     else
432                               margin = O_VAL(sp, O_WRAPLEN);
433           else
434                     margin = 0;
435 
436           /* Initialize abbreviation checks. */
437           abcnt = ab_turnoff = 0;
438           abb = F_ISSET(gp, G_ABBREV) &&
439               LF_ISSET(TXT_MAPINPUT) ? AB_INWORD : AB_NOTSET;
440 
441           /*
442            * Set up the dot command.  Dot commands are done by saving the actual
443            * characters and then reevaluating them so that things like wrapmargin
444            * can change between the insert and the replay.
445            *
446            * !!!
447            * Historically, vi did not remap or reabbreviate replayed input.  (It
448            * did beep at you if you changed an abbreviation and then replayed the
449            * input.  We're not that compatible.)  We don't have to do anything to
450            * avoid remapping, as we're not getting characters from the terminal
451            * routines.  Turn the abbreviation check off.
452            *
453            * XXX
454            * It would be nice if we could swallow backspaces and such, but it's
455            * not all that easy to do.  What we can do is turn off the common
456            * error messages during the replay.  Otherwise, when the user enters
457            * an illegal command, e.g., "Ia<erase><erase><erase><erase>b<escape>",
458            * and then does a '.', they get a list of error messages after command
459            * completion.
460            */
461           rcol = 0;
462           if (LF_ISSET(TXT_REPLAY)) {
463                     abb = AB_NOTSET;
464                     LF_CLR(TXT_RECORD);
465           }
466 
467           /* Other text input mode setup. */
468           quote = Q_NOTSET;
469           carat = C_NOTSET;
470           nochange = 0;
471           FL_INIT(is_flags,
472               LF_ISSET(TXT_SEARCHINCR) ? IS_RESTART | IS_RUNNING : 0);
473           filec_redraw = hexcnt = showmatch = 0;
474 
475           /* Initialize input flags. */
476           ec_flags = LF_ISSET(TXT_MAPINPUT) ? EC_MAPINPUT : 0;
477 
478           /* Refresh the screen. */
479           UPDATE_POSITION(sp, tp);
480           if (vs_refresh(sp, 1))
481                     return (1);
482 
483           /* If it's dot, just do it now. */
484           if (F_ISSET(vp, VC_ISDOT))
485                     goto replay;
486 
487           /* Get an event. */
488           evp = &ev;
489 next:     if (v_event_get(sp, evp, 0, ec_flags))
490                     return (1);
491 
492           /*
493            * If file completion overwrote part of the screen and nothing else has
494            * been displayed, clean up.  We don't do this as part of the normal
495            * message resolution because we know the user is on the colon command
496            * line and there's no reason to enter explicit characters to continue.
497            */
498           if (filec_redraw && !F_ISSET(sp, SC_SCR_EXWROTE)) {
499                     filec_redraw = 0;
500 
501                     fc.e_event = E_REPAINT;
502                     fc.e_flno = vip->totalcount >=
503                         sp->rows ? 1 : sp->rows - vip->totalcount;
504                     fc.e_tlno = sp->rows;
505                     vip->linecount = vip->lcontinue = vip->totalcount = 0;
506                     (void)v_erepaint(sp, &fc);
507                     (void)vs_refresh(sp, 1);
508           }
509 
510           /* Deal with all non-character events. */
511           switch (evp->e_event) {
512           case E_CHARACTER:
513                     break;
514           case E_ERR:
515           case E_EOF:
516                     F_SET(sp, SC_EXIT_FORCE);
517                     return (1);
518           case E_INTERRUPT:
519                     /*
520                      * !!!
521                      * Historically, <interrupt> exited the user from text input
522                      * mode or cancelled a colon command, and returned to command
523                      * mode.  It also beeped the terminal, but that seems a bit
524                      * excessive.
525                      */
526                     goto k_escape;
527           case E_REPAINT:
528                     if (v_erepaint(sp, &ev))
529                               return (1);
530                     goto next;
531           case E_WRESIZE:
532                     /* <resize> interrupts the input mode. */
533 #ifdef IMCTRL
534                     sp->gp->scr_imctrl(sp, IMCTRL_OFF);
535 #endif
536                     v_emsg(sp, NULL, VIM_WRESIZE);
537                     goto k_escape;
538           default:
539                     v_event_err(sp, evp);
540                     goto k_escape;
541           }
542 
543           /*
544            * !!!
545            * If the first character of the input is a nul, replay the previous
546            * input.  (Historically, it's okay to replay non-existent input.)
547            * This was not documented as far as I know, and is a great test of vi
548            * clones.
549            */
550           if (rcol == 0 && !LF_ISSET(TXT_REPLAY) && evp->e_c == '\0') {
551                     if (vip->rep == NULL)
552                               goto done;
553 
554                     abb = AB_NOTSET;
555                     LF_CLR(TXT_RECORD);
556                     LF_SET(TXT_REPLAY);
557                     goto replay;
558           }
559 
560           /*
561            * File name completion and colon command-line editing.   We don't
562            * have enough meta characters, so we expect people to overload
563            * them.  If the two characters are the same, then we do file name
564            * completion if the cursor is past the first column, and do colon
565            * command-line editing if it's not.
566            */
567           if (quote == Q_NOTSET) {
568                     int L__cedit, L__filec;
569 
570                     L__cedit = L__filec = 0;
571                     if (LF_ISSET(TXT_CEDIT) && O_STR(sp, O_CEDIT) != NULL &&
572                         O_STR(sp, O_CEDIT)[0] == evp->e_c)
573                               L__cedit = 1;
574                     if (LF_ISSET(TXT_FILEC) && O_STR(sp, O_FILEC) != NULL &&
575                         O_STR(sp, O_FILEC)[0] == evp->e_c)
576                               L__filec = 1;
577                     if (L__cedit == 1 && (L__filec == 0 || tp->cno == tp->offset)) {
578                               tp->term = TERM_CEDIT;
579                               goto k_escape;
580                     }
581                     if (L__filec == 1) {
582                               if (txt_fc(sp, tp, &filec_redraw))
583                                         goto err;
584                               goto resolve;
585                     }
586           }
587 
588           /* Abbreviation overflow check.  See comment in txt_abbrev(). */
589 #define   MAX_ABBREVIATION_EXPANSION    256
590           if (FL_ISSET(evp->e_flags, CH_ABBREVIATED)) {
591                     if (++abcnt > MAX_ABBREVIATION_EXPANSION) {
592                               if (v_event_flush(sp, CH_ABBREVIATED))
593                                         msgq(sp, M_ERR,
594 "191|Abbreviation exceeded expansion limit: characters discarded");
595                               abcnt = 0;
596                               if (LF_ISSET(TXT_REPLAY))
597                                         goto done;
598                               goto resolve;
599                     }
600           } else
601                     abcnt = 0;
602 
603           /* Check to see if the character fits into the replay buffers. */
604           if (LF_ISSET(TXT_RECORD)) {
605                     BINC_GOTO(sp, EVENT, vip->rep,
606                         vip->rep_len, (rcol + 1) * sizeof(EVENT));
607                     vip->rep[rcol++] = *evp;
608           }
609 
610 replay:   if (LF_ISSET(TXT_REPLAY)) {
611                     if (rcol == vip->rep_cnt)
612                               goto k_escape;
613                     evp = vip->rep + rcol++;
614           }
615 
616           /* Wrapmargin check for leading space. */
617           if (wm_skip) {
618                     wm_skip = 0;
619                     if (evp->e_c == ' ')
620                               goto resolve;
621           }
622 
623           /* If quoted by someone else, simply insert the character. */
624           if (FL_ISSET(evp->e_flags, CH_QUOTED))
625                     goto insq_ch;
626 
627           /*
628            * !!!
629            * If this character was quoted by a K_VLNEXT or a backslash, replace
630            * the placeholder (a carat or a backslash) with the new character.
631            * If it was quoted by a K_VLNEXT, we've already adjusted the cursor
632            * because it has to appear on top of the placeholder character.  If
633            * it was quoted by a backslash, adjust the cursor now, the cursor
634            * doesn't appear on top of it.  Historic practice in both cases.
635            *
636            * Skip tests for abbreviations; ":ab xa XA" followed by "ixa^V<space>"
637            * doesn't perform an abbreviation.  Special case, ^V^J (not ^V^M) is
638            * the same as ^J, historically.
639            */
640           if (quote == Q_BTHIS || quote == Q_VTHIS) {
641                     FL_CLR(ec_flags, EC_QUOTED);
642                     if (LF_ISSET(TXT_MAPINPUT))
643                               FL_SET(ec_flags, EC_MAPINPUT);
644 
645                     if (quote == Q_BTHIS &&
646                         (evp->e_value == K_VERASE || evp->e_value == K_VKILL)) {
647                               quote = Q_NOTSET;
648                               --tp->cno;
649                               ++tp->owrite;
650                               goto insl_ch;
651                     }
652                     if (quote == Q_VTHIS && evp->e_value != K_NL) {
653                               quote = Q_NOTSET;
654                               goto insl_ch;
655                     }
656                     quote = Q_NOTSET;
657           }
658 
659           /*
660            * !!!
661            * Translate "<CH_HEX>[isxdigit()]*" to a character with a hex value:
662            * this test delimits the value by any non-hex character.  Offset by
663            * one, we use 0 to mean that we've found <CH_HEX>.
664            */
665           if (hexcnt > 1 && !ISXDIGIT(evp->e_c)) {
666                     hexcnt = 0;
667                     if (txt_hex(sp, tp))
668                               goto err;
669           }
670 
671           switch (evp->e_value) {
672           case K_CR:                                        /* Carriage return. */
673           case K_NL:                                        /* New line. */
674                     /* Return in script windows and the command line. */
675 k_cr:               if (LF_ISSET(TXT_CR)) {
676                               static CHAR_T cr[] = { '\r', 0 };
677                               /*
678                                * If this was a map, we may have not displayed
679                                * the line.  Display it, just in case.
680                                *
681                                * If a script window and not the colon line,
682                                * push a <cr> so it gets executed.
683                                */
684                               if (LF_ISSET(TXT_INFOLINE)) {
685                                         if (vs_change(sp, tp->lno, LINE_RESET))
686                                                   goto err;
687                               } else if (F_ISSET(sp, SC_SCRIPT))
688                                         (void)v_event_push(sp, NULL, cr, 1, CH_NOMAP);
689 
690                               /* Set term condition: if empty. */
691                               if (tp->cno <= tp->offset)
692                                         tp->term = TERM_CR;
693                               /*
694                                * Set term condition: if searching incrementally and
695                                * the user entered a pattern, return a completed
696                                * search, regardless if the entire pattern was found.
697                                */
698                               if (FL_ISSET(is_flags, IS_RUNNING) &&
699                                   tp->cno >= tp->offset + 1)
700                                         tp->term = TERM_SEARCH;
701 
702                               goto k_escape;
703                     }
704 
705 #define   LINE_RESOLVE {                                                                  \
706                     /*                                                                    \
707                      * Handle abbreviations.  If there was one, discard the     \
708                      * replay characters.                                                 \
709                      */                                                                   \
710                     if (abb == AB_INWORD &&                                               \
711                         !LF_ISSET(TXT_REPLAY) && F_ISSET(gp, G_ABBREV)) {       \
712                               if (txt_abbrev(sp, tp, &evp->e_c,                 \
713                                   LF_ISSET(TXT_INFOLINE), &tmp,                 \
714                                   &ab_turnoff))                                 \
715                                         goto err;                               \
716                               if (tmp) {                                                  \
717                                         if (LF_ISSET(TXT_RECORD))               \
718                                                   rcol -= tmp + 1;              \
719                                         goto resolve;                                     \
720                               }                                                           \
721                     }                                                                     \
722                     if (abb != AB_NOTSET)                                                 \
723                               abb = AB_NOTWORD;                                 \
724                     if (UNMAP_TST)                                                        \
725                               txt_unmap(sp, tp, &ec_flags);                     \
726                     /*                                                                    \
727                      * Delete any appended cursor.  It's possible to get in     \
728                      * situations where TXT_APPENDEOL is set but tp->insert     \
729                      * is 0 when using the R command and all the characters     \
730                      * are tp->owrite characters.                               \
731                      */                                                                   \
732                     if (LF_ISSET(TXT_APPENDEOL) && tp->insert > 0) {  \
733                               --tp->len;                                                  \
734                               --tp->insert;                                               \
735                     }                                                                     \
736 }
737                     LINE_RESOLVE;
738 
739                     /*
740                      * Save the current line information for restoration in
741                      * txt_backup(), and set the line final length.
742                      */
743                     tp->sv_len = tp->len;
744                     tp->sv_cno = tp->cno;
745                     tp->len = tp->cno;
746 
747                     /* Update the old line. */
748                     if (vs_change(sp, tp->lno, LINE_RESET))
749                               goto err;
750 
751                     /*
752                      * Historic practice, when the autoindent edit option was set,
753                      * was to delete <blank> characters following the inserted
754                      * newline.  This affected the 'R', 'c', and 's' commands; 'c'
755                      * and 's' retained the insert characters only, 'R' moved the
756                      * overwrite and insert characters into the next TEXT structure.
757                      * We keep track of the number of characters erased for the 'R'
758                      * command so that the final resolution of the line is correct.
759                      */
760                     tp->R_erase = 0;
761                     owrite = tp->owrite;
762                     insert = tp->insert;
763                     if (LF_ISSET(TXT_REPLACE) && owrite != 0) {
764                               for (p = tp->lb + tp->cno; owrite > 0 && ISBLANK((UCHAR_T)*p);
765                                   ++p, --owrite, ++tp->R_erase);
766                               if (owrite == 0)
767                                         for (; insert > 0 && ISBLANK((UCHAR_T)*p);
768                                             ++p, ++tp->R_erase, --insert);
769                     } else {
770                               p = tp->lb + tp->cno + owrite;
771                               if (O_ISSET(sp, O_AUTOINDENT))
772                                         for (; insert > 0 &&
773                                             ISBLANK((UCHAR_T)*p); ++p, --insert);
774                               owrite = 0;
775                     }
776 
777                     /*
778                      * !!!
779                      * Create a new line and insert the new TEXT into the queue.
780                      * DON'T insert until the old line has been updated, or the
781                      * inserted line count in line.c:db_get() will be wrong.
782                      */
783                     if ((ntp = text_init(sp, p,
784                         insert + owrite, insert + owrite + 32)) == NULL)
785                               goto err;
786                     TAILQ_INSERT_TAIL(&sp->tiq, ntp, q);
787 
788                     /* Set up bookkeeping for the new line. */
789                     ntp->insert = insert;
790                     ntp->owrite = owrite;
791                     ntp->lno = tp->lno + 1;
792 
793                     /*
794                      * Reset the autoindent line value.  0^D keeps the autoindent
795                      * line from changing, ^D changes the level, even if there were
796                      * no characters in the old line.  Note, if using the current
797                      * tp structure, use the cursor as the length, the autoindent
798                      * characters may have been erased.
799                      */
800                     if (LF_ISSET(TXT_AUTOINDENT)) {
801                               if (nochange) {
802                                         nochange = 0;
803                                         if (v_txt_auto(sp, OOBLNO, &ait, ait.ai, ntp))
804                                                   goto err;
805                                         FREE_SPACEW(sp, ait.lb, ait.lb_len);
806                               } else
807                                         if (v_txt_auto(sp, OOBLNO, tp, tp->cno, ntp))
808                                                   goto err;
809                               carat = C_NOTSET;
810                     }
811 
812                     /* Reset the cursor. */
813                     ntp->cno = ntp->ai;
814 
815                     /*
816                      * If we're here because wrapmargin was set and we've broken a
817                      * line, there may be additional information (i.e. the start of
818                      * a line) in the wmt structure.
819                      */
820                     if (wm_set) {
821                               if (wmt.offset != 0 ||
822                                   wmt.owrite != 0 || wmt.insert != 0) {
823 #define   WMTSPACE  wmt.offset + wmt.owrite + wmt.insert
824                                         BINC_GOTOW(sp, ntp->lb,
825                                             ntp->lb_len, ntp->len + WMTSPACE + 32);
826                                         MEMMOVEW(ntp->lb + ntp->cno, wmt.lb, WMTSPACE);
827                                         ntp->len += WMTSPACE;
828                                         ntp->cno += wmt.offset;
829                                         ntp->owrite = wmt.owrite;
830                                         ntp->insert = wmt.insert;
831                               }
832                               wm_set = 0;
833                     }
834 
835                     /* New lines are TXT_APPENDEOL. */
836                     if (ntp->owrite == 0 && ntp->insert == 0) {
837                               BINC_GOTOW(sp, ntp->lb, ntp->lb_len, ntp->len + 1);
838                               LF_SET(TXT_APPENDEOL);
839                               ntp->lb[ntp->cno] = CH_CURSOR;
840                               ++ntp->insert;
841                               ++ntp->len;
842                     }
843 
844                     /* Swap old and new TEXT's, and update the new line. */
845                     tp = ntp;
846                     if (vs_change(sp, tp->lno, LINE_INSERT))
847                               goto err;
848 
849                     goto resolve;
850           case K_ESCAPE:                                    /* Escape. */
851                     if (!LF_ISSET(TXT_ESCAPE))
852                               goto ins_ch;
853 
854                     /* If we have a count, start replaying the input. */
855                     if (rcount > 1) {
856                               --rcount;
857 
858                               vip->rep_cnt = rcol;
859                               rcol = 0;
860                               abb = AB_NOTSET;
861                               LF_CLR(TXT_RECORD);
862                               LF_SET(TXT_REPLAY);
863 
864                               /*
865                                * Some commands (e.g. 'o') need a <newline> for each
866                                * repetition.
867                                */
868                               if (LF_ISSET(TXT_ADDNEWLINE))
869                                         goto k_cr;
870 
871                               /*
872                                * The R command turns into the 'a' command after the
873                                * first repetition.
874                                */
875                               if (LF_ISSET(TXT_REPLACE)) {
876                                         tp->insert = tp->owrite;
877                                         tp->owrite = 0;
878                                         LF_CLR(TXT_REPLACE);
879                               }
880                               goto replay;
881                     }
882 
883                     /* Set term condition: if empty. */
884                     if (tp->cno <= tp->offset)
885                               tp->term = TERM_ESC;
886                     /*
887                      * Set term condition: if searching incrementally and the user
888                      * entered a pattern, return a completed search, regardless if
889                      * the entire pattern was found.
890                      */
891                     if (FL_ISSET(is_flags, IS_RUNNING) && tp->cno >= tp->offset + 1)
892                               tp->term = TERM_SEARCH;
893 
894 k_escape: LINE_RESOLVE;
895 
896                     /*
897                      * Clean up for the 'R' command, restoring overwrite
898                      * characters, and making them into insert characters.
899                      */
900                     if (LF_ISSET(TXT_REPLACE))
901                               txt_Rresolve(sp, &sp->tiq, tp, len);
902 
903                     /*
904                      * If there are any overwrite characters, copy down
905                      * any insert characters, and decrement the length.
906                      */
907                     if (tp->owrite) {
908                               if (tp->insert)
909                                         MEMMOVEW(tp->lb + tp->cno,
910                                             tp->lb + tp->cno + tp->owrite, tp->insert);
911                               tp->len -= tp->owrite;
912                     }
913 
914                     /*
915                      * Optionally resolve the lines into the file.  If not
916                      * resolving the lines into the file, end the line with
917                      * a nul.  If the line is empty, then set the length to
918                      * 0, the termination condition has already been set.
919                      *
920                      * XXX
921                      * This is wrong, should pass back a length.
922                      */
923                     if (LF_ISSET(TXT_RESOLVE)) {
924                               if (txt_resolve(sp, &sp->tiq, flags))
925                                         goto err;
926                     } else {
927                               BINC_GOTOW(sp, tp->lb, tp->lb_len, tp->len + 1);
928                               tp->lb[tp->len] = '\0';
929                     }
930 
931                     /*
932                      * Set the return cursor position to rest on the last
933                      * inserted character.
934                      */
935                     if (tp->cno != 0)
936                               --tp->cno;
937 
938                     /* Update the last line. */
939                     if (vs_change(sp, tp->lno, LINE_RESET))
940                               return (1);
941                     goto done;
942           case K_CARAT:                           /* Delete autoindent chars. */
943                     if (tp->cno <= tp->ai && LF_ISSET(TXT_AUTOINDENT))
944                               carat = C_CARATSET;
945                     goto ins_ch;
946           case K_ZERO:                            /* Delete autoindent chars. */
947                     if (tp->cno <= tp->ai && LF_ISSET(TXT_AUTOINDENT))
948                               carat = C_ZEROSET;
949                     goto ins_ch;
950           case K_CNTRLD:                          /* Delete autoindent char. */
951                     /*
952                      * If in the first column or no characters to erase, ignore
953                      * the ^D (this matches historic practice).  If not doing
954                      * autoindent or already inserted non-ai characters, it's a
955                      * literal.  The latter test is done in the switch, as the
956                      * CARAT forms are N + 1, not N.
957                      */
958                     if (!LF_ISSET(TXT_AUTOINDENT))
959                               goto ins_ch;
960                     if (tp->cno == 0)
961                               goto resolve;
962 
963                     switch (carat) {
964                     case C_CARATSET:    /* ^^D */
965                               if (tp->ai == 0 || tp->cno != tp->ai + tp->offset + 1)
966                                         goto ins_ch;
967 
968                               /* Save the ai string for later. */
969                               ait.lb = NULL;
970                               ait.lb_len = 0;
971                               BINC_GOTOW(sp, ait.lb, ait.lb_len, tp->ai);
972                               MEMMOVEW(ait.lb, tp->lb, tp->ai);
973                               ait.ai = ait.len = tp->ai;
974 
975                               carat = C_NOTSET;
976                               nochange = 1;
977                               goto leftmargin;
978                     case C_ZEROSET:               /* 0^D */
979                               if (tp->ai == 0 || tp->cno != tp->ai + tp->offset + 1)
980                                         goto ins_ch;
981 
982                               carat = C_NOTSET;
983 leftmargin:                   tp->lb[tp->cno - 1] = ' ';
984                               tp->owrite += tp->cno - tp->offset;
985                               tp->cno = tp->offset;
986                               break;
987                     case C_NOTSET:                /* ^D */
988                               if (tp->ai == 0 || tp->cno != tp->ai + tp->offset)
989                                         goto ins_ch;
990 
991                               (void)txt_dent(sp, tp, 0);
992                               break;
993                     default:
994                               abort();
995                     }
996                     break;
997           case K_VERASE:                          /* Erase the last character. */
998                     /* If can erase over the prompt, return. */
999                     if (tp->cno <= tp->offset && LF_ISSET(TXT_BS)) {
1000                               tp->term = TERM_BS;
1001                               goto done;
1002                     }
1003 
1004                     /*
1005                      * If at the beginning of the line, try and drop back to a
1006                      * previously inserted line.
1007                      */
1008                     if (tp->cno == 0) {
1009                               if ((ntp =
1010                                   txt_backup(sp, &sp->tiq, tp, &flags)) == NULL)
1011                                         goto err;
1012                               tp = ntp;
1013                               break;
1014                     }
1015 
1016                     /* If nothing to erase, bell the user. */
1017                     if (tp->cno <= tp->offset) {
1018                               if (!LF_ISSET(TXT_REPLAY))
1019                                         txt_nomorech(sp);
1020                               break;
1021                     }
1022 
1023                     /* Drop back one character. */
1024                     --tp->cno;
1025 
1026                     /*
1027                      * Historically, vi didn't replace the erased characters with
1028                      * <blank>s, presumably because it's easier to fix a minor
1029                      * typing mistake and continue on if the previous letters are
1030                      * already there.  This is a problem for incremental searching,
1031                      * because the user can no longer tell where they are in the
1032                      * colon command line because the cursor is at the last search
1033                      * point in the screen.  So, if incrementally searching, erase
1034                      * the erased characters from the screen.
1035                      */
1036                     if (FL_ISSET(is_flags, IS_RUNNING))
1037                               tp->lb[tp->cno] = ' ';
1038 
1039                     /*
1040                      * Increment overwrite, decrement ai if deleted.
1041                      *
1042                      * !!!
1043                      * Historic vi did not permit users to use erase characters
1044                      * to delete autoindent characters.  We do.  Eat hot death,
1045                      * POSIX.
1046                      */
1047                     ++tp->owrite;
1048                     if (tp->cno < tp->ai)
1049                               --tp->ai;
1050 
1051                     /* Reset if we deleted an incremental search character. */
1052                     if (FL_ISSET(is_flags, IS_RUNNING))
1053                               FL_SET(is_flags, IS_RESTART);
1054                     break;
1055           case K_VWERASE:                         /* Skip back one word. */
1056                     /*
1057                      * If at the beginning of the line, try and drop back to a
1058                      * previously inserted line.
1059                      */
1060                     if (tp->cno == 0) {
1061                               if ((ntp =
1062                                   txt_backup(sp, &sp->tiq, tp, &flags)) == NULL)
1063                                         goto err;
1064                               tp = ntp;
1065                     }
1066 
1067                     /*
1068                      * If at offset, nothing to erase so bell the user.
1069                      */
1070                     if (tp->cno <= tp->offset) {
1071                               if (!LF_ISSET(TXT_REPLAY))
1072                                         txt_nomorech(sp);
1073                               break;
1074                     }
1075 
1076                     /*
1077                      * The first werase goes back to any autoindent column and the
1078                      * second werase goes back to the offset.
1079                      *
1080                      * !!!
1081                      * Historic vi did not permit users to use erase characters to
1082                      * delete autoindent characters.
1083                      */
1084                     if (tp->ai && tp->cno > tp->ai)
1085                               max = tp->ai;
1086                     else {
1087                               tp->ai = 0;
1088                               max = tp->offset;
1089                     }
1090 
1091                     /* Skip over trailing space characters. */
1092                     while (tp->cno > max && ISBLANK((UCHAR_T)tp->lb[tp->cno - 1])) {
1093                               --tp->cno;
1094                               ++tp->owrite;
1095                     }
1096                     if (tp->cno == max)
1097                               break;
1098                     /*
1099                      * There are three types of word erase found on UNIX systems.
1100                      * They can be identified by how the string /a/b/c is treated
1101                      * -- as 1, 3, or 6 words.  Historic vi had two classes of
1102                      * characters, and strings were delimited by them and
1103                      * <blank>'s, so, 6 words.  The historic tty interface used
1104                      * <blank>'s to delimit strings, so, 1 word.  The algorithm
1105                      * offered in the 4.4BSD tty interface (as stty altwerase)
1106                      * treats it as 3 words -- there are two classes of
1107                      * characters, and strings are delimited by them and
1108                      * <blank>'s.  The difference is that the type of the first
1109                      * erased character erased is ignored, which is exactly right
1110                      * when erasing pathname components.  The edit options
1111                      * TXT_ALTWERASE and TXT_TTYWERASE specify the 4.4BSD tty
1112                      * interface and the historic tty driver behavior,
1113                      * respectively, and the default is the same as the historic
1114                      * vi behavior.
1115                      *
1116                      * Overwrite erased characters if doing incremental search;
1117                      * see comment above.
1118                      */
1119                     if (LF_ISSET(TXT_TTYWERASE))
1120                               while (tp->cno > max) {
1121                                         if (ISBLANK((UCHAR_T)tp->lb[tp->cno - 1]))
1122                                                   break;
1123                                         --tp->cno;
1124                                         ++tp->owrite;
1125                                         if (FL_ISSET(is_flags, IS_RUNNING))
1126                                                   tp->lb[tp->cno] = ' ';
1127                               }
1128                     else {
1129                               if (LF_ISSET(TXT_ALTWERASE)) {
1130                                         --tp->cno; /* No worry for out of bounds. */
1131                                         ++tp->owrite;
1132                                         if (FL_ISSET(is_flags, IS_RUNNING))
1133                                                   tp->lb[tp->cno] = ' ';
1134                               }
1135                               if (tp->cno > max)
1136                                         tmp = inword((UCHAR_T)tp->lb[tp->cno - 1]);
1137                               while (tp->cno > max) {
1138                                         if (tmp != inword((UCHAR_T)tp->lb[tp->cno - 1])
1139                                             || ISBLANK((UCHAR_T)tp->lb[tp->cno - 1]))
1140                                                   break;
1141                                         --tp->cno;
1142                                         ++tp->owrite;
1143                                         if (FL_ISSET(is_flags, IS_RUNNING))
1144                                                   tp->lb[tp->cno] = ' ';
1145                               }
1146                     }
1147 
1148                     /* Reset if we deleted an incremental search character. */
1149                     if (FL_ISSET(is_flags, IS_RUNNING))
1150                               FL_SET(is_flags, IS_RESTART);
1151                     break;
1152           case K_VKILL:                           /* Restart this line. */
1153                     /*
1154                      * !!!
1155                      * If at the beginning of the line, try and drop back to a
1156                      * previously inserted line.  Historic vi did not permit
1157                      * users to go back to previous lines.
1158                      */
1159                     if (tp->cno == 0) {
1160                               if ((ntp =
1161                                   txt_backup(sp, &sp->tiq, tp, &flags)) == NULL)
1162                                         goto err;
1163                               tp = ntp;
1164                     }
1165 
1166                     /* If at offset, nothing to erase so bell the user. */
1167                     if (tp->cno <= tp->offset) {
1168                               if (!LF_ISSET(TXT_REPLAY))
1169                                         txt_nomorech(sp);
1170                               break;
1171                     }
1172 
1173                     /*
1174                      * First kill goes back to any autoindent and second kill goes
1175                      * back to the offset.
1176                      *
1177                      * !!!
1178                      * Historic vi did not permit users to use erase characters to
1179                      * delete autoindent characters.
1180                      */
1181                     if (tp->ai && tp->cno > tp->ai)
1182                               max = tp->ai;
1183                     else {
1184                               tp->ai = 0;
1185                               max = tp->offset;
1186                     }
1187                     tp->owrite += tp->cno - max;
1188 
1189                     /*
1190                      * Overwrite erased characters if doing incremental search;
1191                      * see comment above.
1192                      */
1193                     if (FL_ISSET(is_flags, IS_RUNNING))
1194                               do {
1195                                         tp->lb[--tp->cno] = ' ';
1196                               } while (tp->cno > max);
1197                     else
1198                               tp->cno = max;
1199 
1200                     /* Reset if we deleted an incremental search character. */
1201                     if (FL_ISSET(is_flags, IS_RUNNING))
1202                               FL_SET(is_flags, IS_RESTART);
1203                     break;
1204           case K_CNTRLT:                          /* Add autoindent characters. */
1205                     if (!LF_ISSET(TXT_CNTRLT))
1206                               goto ins_ch;
1207                     if (txt_dent(sp, tp, 1))
1208                               goto err;
1209                     goto ebuf_chk;
1210           case K_RIGHTBRACE:
1211           case K_RIGHTPAREN:
1212                     if (LF_ISSET(TXT_SHOWMATCH))
1213                               showmatch = 1;
1214                     goto ins_ch;
1215           case K_BACKSLASH:             /* Quote next erase/kill. */
1216                     /*
1217                      * !!!
1218                      * Historic vi tried to make abbreviations after a backslash
1219                      * escape work.  If you did ":ab x y", and inserted "x\^H",
1220                      * (assuming the erase character was ^H) you got "x^H", and
1221                      * no abbreviation was done.  If you inserted "x\z", however,
1222                      * it tried to back up and do the abbreviation, i.e. replace
1223                      * 'x' with 'y'.  The problem was it got it wrong, and you
1224                      * ended up with "zy\".
1225                      *
1226                      * This is really hard to do (you have to remember the
1227                      * word/non-word state, for example), and doesn't make any
1228                      * sense to me.  Both backslash and the characters it
1229                      * (usually) escapes will individually trigger the
1230                      * abbreviation, so I don't see why the combination of them
1231                      * wouldn't.  I don't expect to get caught on this one,
1232                      * particularly since it never worked right, but I've been
1233                      * wrong before.
1234                      *
1235                      * Do the tests for abbreviations, so ":ab xa XA",
1236                      * "ixa\<K_VERASE>" performs the abbreviation.
1237                      */
1238                     quote = Q_BNEXT;
1239                     goto insq_ch;
1240           case K_VLNEXT:                          /* Quote next character. */
1241                     evp->e_c = '^';
1242                     quote = Q_VNEXT;
1243                     /*
1244                      * Turn on the quote flag so that the underlying routines
1245                      * quote the next character where it's possible. Turn off
1246                      * the input mapbiting flag so that we don't remap the next
1247                      * character.
1248                      */
1249                     FL_SET(ec_flags, EC_QUOTED);
1250                     FL_CLR(ec_flags, EC_MAPINPUT);
1251 
1252                     /*
1253                      * !!!
1254                      * Skip the tests for abbreviations, so ":ab xa XA",
1255                      * "ixa^V<space>" doesn't perform the abbreviation.
1256                      */
1257                     goto insl_ch;
1258           case K_HEXCHAR:
1259                     hexcnt = 1;
1260                     goto insq_ch;
1261           default:                      /* Insert the character. */
1262 ins_ch:             /*
1263                      * Historically, vi eliminated nul's out of hand.  If the
1264                      * beautify option was set, it also deleted any unknown
1265                      * ASCII value less than space (040) and the del character
1266                      * (0177), except for tabs.  Unknown is a key word here.
1267                      * Most vi documentation claims that it deleted everything
1268                      * but <tab>, <nl> and <ff>, as that's what the original
1269                      * 4BSD documentation said.  This is obviously wrong,
1270                      * however, as <esc> would be included in that list.  What
1271                      * we do is eliminate any unquoted, iscntrl() character that
1272                      * wasn't a replay and wasn't handled specially, except
1273                      * <tab> or <ff>.
1274                      */
1275                     if (LF_ISSET(TXT_BEAUTIFY) && ISCNTRL(evp->e_c) &&
1276                         evp->e_value != K_FORMFEED && evp->e_value != K_TAB) {
1277                               msgq(sp, M_BERR,
1278                                   "192|Illegal character; quote to enter");
1279                               if (LF_ISSET(TXT_REPLAY))
1280                                         goto done;
1281                               break;
1282                     }
1283 
1284 insq_ch:  /*
1285                      * If entering a non-word character after a word, check for
1286                      * abbreviations.  If there was one, discard replay characters.
1287                      * If entering a blank character, check for unmap commands,
1288                      * as well.
1289                      */
1290                     if (!inword((UCHAR_T)evp->e_c)) {
1291                               if (abb == AB_INWORD &&
1292                                   !LF_ISSET(TXT_REPLAY) && F_ISSET(gp, G_ABBREV)) {
1293                                         if (txt_abbrev(sp, tp, &evp->e_c,
1294                                             LF_ISSET(TXT_INFOLINE), &tmp, &ab_turnoff))
1295                                                   goto err;
1296                                         if (tmp) {
1297                                                   if (LF_ISSET(TXT_RECORD))
1298                                                             rcol -= tmp + 1;
1299                                                   goto resolve;
1300                                         }
1301                               }
1302                               if (ISBLANK((UCHAR_T)evp->e_c) && UNMAP_TST)
1303                                         txt_unmap(sp, tp, &ec_flags);
1304                     }
1305                     if (abb != AB_NOTSET)
1306                               abb = inword((UCHAR_T)evp->e_c) ? AB_INWORD : AB_NOTWORD;
1307 
1308 insl_ch:  if (txt_insch(sp, tp, &evp->e_c, flags))
1309                               goto err;
1310 
1311                     /*
1312                      * If we're using K_VLNEXT to quote the next character, then
1313                      * we want the cursor to position itself on the ^ placeholder
1314                      * we're displaying, to match historic practice.
1315                      */
1316                     if (quote == Q_VNEXT) {
1317                               --tp->cno;
1318                               ++tp->owrite;
1319                     }
1320 
1321                     /*
1322                      * !!!
1323                      * Translate "<CH_HEX>[isxdigit()]*" to a character with
1324                      * a hex value: this test delimits the value by the max
1325                      * number of hex bytes.  Offset by one, we use 0 to mean
1326                      * that we've found <CH_HEX>.
1327                      */
1328                     if (hexcnt != 0 && hexcnt++ == sizeof(CHAR_T) * 2 + 1) {
1329                               hexcnt = 0;
1330                               if (txt_hex(sp, tp))
1331                                         goto err;
1332                     }
1333 
1334                     /*
1335                      * Check to see if we've crossed the margin.
1336                      *
1337                      * !!!
1338                      * In the historic vi, the wrapmargin value was figured out
1339                      * using the display widths of the characters, i.e. <tab>
1340                      * characters were counted as two characters if the list edit
1341                      * option is set, but as the tabstop edit option number of
1342                      * characters otherwise.  That's what the vs_column() function
1343                      * gives us, so we use it.
1344                      */
1345                     if (margin != 0) {
1346                               if (vs_column(sp, &tcol))
1347                                         goto err;
1348                               if (tcol >= margin) {
1349                                         if (txt_margin(sp, tp, &wmt, &tmp, flags))
1350                                                   goto err;
1351                                         if (tmp) {
1352                                                   if (ISBLANK((UCHAR_T)evp->e_c))
1353                                                             wm_skip = 1;
1354                                                   wm_set = 1;
1355                                                   goto k_cr;
1356                                         }
1357                               }
1358                     }
1359 
1360                     /*
1361                      * If we've reached the end of the buffer, then we need to
1362                      * switch into insert mode.  This happens when there's a
1363                      * change to a mark and the user puts in more characters than
1364                      * the length of the motion.
1365                      */
1366 ebuf_chk: if (tp->cno >= tp->len) {
1367                               BINC_GOTOW(sp, tp->lb, tp->lb_len, tp->len + 1);
1368                               LF_SET(TXT_APPENDEOL);
1369 
1370                               tp->lb[tp->cno] = CH_CURSOR;
1371                               ++tp->insert;
1372                               ++tp->len;
1373                     }
1374 
1375                     /* Step the quote state forward. */
1376                     if (quote != Q_NOTSET) {
1377                               if (quote == Q_BNEXT)
1378                                         quote = Q_BTHIS;
1379                               if (quote == Q_VNEXT)
1380                                         quote = Q_VTHIS;
1381                     }
1382                     break;
1383           }
1384 
1385 #ifdef DEBUG
1386           if (tp->cno + tp->insert + tp->owrite != tp->len) {
1387                     msgq(sp, M_ERR,
1388                         "len %u != cno: %u ai: %u insert %u overwrite %u",
1389                         tp->len, tp->cno, tp->ai, tp->insert, tp->owrite);
1390                     if (LF_ISSET(TXT_REPLAY))
1391                               goto done;
1392                     tp->len = tp->cno + tp->insert + tp->owrite;
1393           }
1394 #endif
1395 
1396 resolve:/*
1397            * 1: If we don't need to know where the cursor really is and we're
1398            *    replaying text, keep going.
1399            */
1400           if (margin == 0 && LF_ISSET(TXT_REPLAY))
1401                     goto replay;
1402 
1403           /*
1404            * 2: Reset the line.  Don't bother unless we're about to wait on
1405            *    a character or we need to know where the cursor really is.
1406            *    We have to do this before showing matching characters so the
1407            *    user can see what they're matching.
1408            */
1409           if ((margin != 0 || !KEYS_WAITING(sp)) &&
1410               vs_change(sp, tp->lno, LINE_RESET))
1411                     return (1);
1412 
1413           /*
1414            * 3: If there aren't keys waiting, display the matching character.
1415            *    We have to do this before resolving any messages, otherwise
1416            *    the error message from a missing match won't appear correctly.
1417            */
1418           if (showmatch) {
1419                     if (!KEYS_WAITING(sp) && txt_showmatch(sp, tp))
1420                               return (1);
1421                     showmatch = 0;
1422           }
1423 
1424           /*
1425            * 4: If there have been messages and we're not editing on the colon
1426            *    command line or doing file name completion, resolve them.
1427            */
1428           if ((vip->totalcount != 0 || F_ISSET(gp, G_BELLSCHED)) &&
1429               !F_ISSET(sp, SC_TINPUT_INFO) && !filec_redraw &&
1430               vs_resolve(sp, NULL, 0))
1431                     return (1);
1432 
1433           /*
1434            * 5: Refresh the screen if we're about to wait on a character or we
1435            *    need to know where the cursor really is.
1436            */
1437           if (margin != 0 || !KEYS_WAITING(sp)) {
1438                     UPDATE_POSITION(sp, tp);
1439                     if (vs_refresh(sp, margin != 0))
1440                               return (1);
1441           }
1442 
1443           /* 6: Proceed with the incremental search. */
1444           if (FL_ISSET(is_flags, IS_RUNNING) && txt_isrch(sp, vp, tp, &is_flags))
1445                     return (1);
1446 
1447           /* 7: Next character... */
1448           if (LF_ISSET(TXT_REPLAY))
1449                     goto replay;
1450           goto next;
1451 
1452 done:     /* Leave input mode. */
1453           F_CLR(sp, SC_TINPUT);
1454 
1455           /* If recording for playback, save it. */
1456           if (LF_ISSET(TXT_RECORD))
1457                     vip->rep_cnt = rcol;
1458 
1459           /*
1460            * If not working on the colon command line, set the final cursor
1461            * position.
1462            */
1463           if (!F_ISSET(sp, SC_TINPUT_INFO)) {
1464                     vp->m_final.lno = tp->lno;
1465                     vp->m_final.cno = tp->cno;
1466           }
1467           return (0);
1468 
1469 err:
1470 alloc_err:
1471           F_CLR(sp, SC_TINPUT);
1472           txt_err(sp, &sp->tiq);
1473           return (1);
1474 }
1475 
1476 /*
1477  * txt_abbrev --
1478  *        Handle abbreviations.
1479  */
1480 static int
txt_abbrev(SCR * sp,TEXT * tp,ARG_CHAR_T * pushcp,int isinfoline,int * didsubp,int * turnoffp)1481 txt_abbrev(SCR *sp, TEXT *tp, ARG_CHAR_T *pushcp, int isinfoline, int *didsubp, int *turnoffp)
1482 {
1483           CHAR_T ch, *p;
1484           SEQ *qp;
1485           size_t len, off;
1486 
1487           /* Check to make sure we're not at the start of an append. */
1488           *didsubp = 0;
1489           if (tp->cno == tp->offset)
1490                     return (0);
1491 
1492           /*
1493            * Find the start of the "word".
1494            *
1495            * !!!
1496            * We match historic practice, which, as far as I can tell, had an
1497            * off-by-one error.  The way this worked was that when the inserted
1498            * text switched from a "word" character to a non-word character,
1499            * vi would check for possible abbreviations.  It would then take the
1500            * type (i.e. word/non-word) of the character entered TWO characters
1501            * ago, and move backward in the text until reaching a character that
1502            * was not that type, or the beginning of the insert, the line, or
1503            * the file.  For example, in the string "abc<space>", when the <space>
1504            * character triggered the abbreviation check, the type of the 'b'
1505            * character was used for moving through the string.  Maybe there's a
1506            * reason for not using the first (i.e. 'c') character, but I can't
1507            * think of one.
1508            *
1509            * Terminate at the beginning of the insert or the character after the
1510            * offset character -- both can be tested for using tp->offset.
1511            */
1512           off = tp->cno - 1;                      /* Previous character. */
1513           p = tp->lb + off;
1514           len = 1;                                /* One character test. */
1515           if (off == tp->offset || ISBLANK((UCHAR_T)p[-1]))
1516                     goto search;
1517           if (inword((UCHAR_T)p[-1]))                       /* Move backward to change. */
1518                     for (;;) {
1519                               --off; --p; ++len;
1520                               if (off == tp->offset || !inword((UCHAR_T)p[-1]))
1521                                         break;
1522                     }
1523           else
1524                     for (;;) {
1525                               --off; --p; ++len;
1526                               if (off == tp->offset ||
1527                                   inword((UCHAR_T)p[-1]) || ISBLANK((UCHAR_T)p[-1]))
1528                                         break;
1529                     }
1530 
1531           /*
1532            * !!!
1533            * Historic vi exploded abbreviations on the command line.  This has
1534            * obvious problems in that unabbreviating the string can be extremely
1535            * tricky, particularly if the string has, say, an embedded escape
1536            * character.  Personally, I think it's a stunningly bad idea.  Other
1537            * examples of problems this caused in historic vi are:
1538            *        :ab foo bar
1539            *        :ab foo baz
1540            * results in "bar" being abbreviated to "baz", which wasn't what the
1541            * user had in mind at all.  Also, the commands:
1542            *        :ab foo bar
1543            *        :unab foo<space>
1544            * resulted in an error message that "bar" wasn't mapped.  Finally,
1545            * since the string was already exploded by the time the unabbreviate
1546            * command got it, all it knew was that an abbreviation had occurred.
1547            * Cleverly, it checked the replacement string for its unabbreviation
1548            * match, which meant that the commands:
1549            *        :ab foo1 bar
1550            *        :ab foo2 bar
1551            *        :unab foo2
1552            * unabbreviate "foo1", and the commands:
1553            *        :ab foo bar
1554            *        :ab bar baz
1555            * unabbreviate "foo"!
1556            *
1557            * Anyway, people neglected to first ask my opinion before they wrote
1558            * macros that depend on this stuff, so, we make this work as follows.
1559            * When checking for an abbreviation on the command line, if we get a
1560            * string which is <blank> terminated and which starts at the beginning
1561            * of the line, we check to see it is the abbreviate or unabbreviate
1562            * commands.  If it is, turn abbreviations off and return as if no
1563            * abbreviation was found.  Note also, minor trickiness, so that if
1564            * the user erases the line and starts another command, we turn the
1565            * abbreviations back on.
1566            *
1567            * This makes the layering look like a Nachos Supreme.
1568            */
1569 search:   if (isinfoline) {
1570                     if (off == tp->ai || off == tp->offset)
1571                               if (ex_is_abbrev(sp, p, len)) {
1572                                         *turnoffp = 1;
1573                                         return (0);
1574                               } else
1575                                         *turnoffp = 0;
1576                     else
1577                               if (*turnoffp)
1578                                         return (0);
1579           }
1580 
1581           /* Check for any abbreviations. */
1582           if ((qp = seq_find(sp, NULL, NULL, p, len, SEQ_ABBREV, NULL)) == NULL)
1583                     return (0);
1584 
1585           /*
1586            * Push the abbreviation onto the tty stack.  Historically, characters
1587            * resulting from an abbreviation expansion were themselves subject to
1588            * map expansions, O_SHOWMATCH matching etc.  This means the expanded
1589            * characters will be re-tested for abbreviations.  It's difficult to
1590            * know what historic practice in this case was, since abbreviations
1591            * were applied to :colon command lines, so entering abbreviations that
1592            * looped was tricky, although possible.  In addition, obvious loops
1593            * didn't work as expected.  (The command ':ab a b|ab b c|ab c a' will
1594            * silently only implement and/or display the last abbreviation.)
1595            *
1596            * This implementation doesn't recover well from such abbreviations.
1597            * The main input loop counts abbreviated characters, and, when it
1598            * reaches a limit, discards any abbreviated characters on the queue.
1599            * It's difficult to back up to the original position, as the replay
1600            * queue would have to be adjusted, and the line state when an initial
1601            * abbreviated character was received would have to be saved.
1602            */
1603           ch = (UCHAR_T)*pushcp;
1604           if (v_event_push(sp, NULL, &ch, 1, CH_ABBREVIATED))
1605                     return (1);
1606           if (v_event_push(sp, NULL, qp->output, qp->olen, CH_ABBREVIATED))
1607                     return (1);
1608 
1609           /*
1610            * If the size of the abbreviation is larger than or equal to the size
1611            * of the original text, move to the start of the replaced characters,
1612            * and add their length to the overwrite count.
1613            *
1614            * If the abbreviation is smaller than the original text, we have to
1615            * delete the additional overwrite characters and copy down any insert
1616            * characters.
1617            */
1618           tp->cno -= len;
1619           if (qp->olen >= len)
1620                     tp->owrite += len;
1621           else {
1622                     if (tp->insert)
1623                               MEMMOVEW(tp->lb + tp->cno + qp->olen,
1624                                   tp->lb + tp->cno + tp->owrite + len, tp->insert);
1625                     tp->owrite += qp->olen;
1626                     tp->len -= len - qp->olen;
1627           }
1628 
1629           /*
1630            * We return the length of the abbreviated characters.  This is so
1631            * the calling routine can replace the replay characters with the
1632            * abbreviation.  This means that subsequent '.' commands will produce
1633            * the same text, regardless of intervening :[un]abbreviate commands.
1634            * This is historic practice.
1635            */
1636           *didsubp = len;
1637           return (0);
1638 }
1639 
1640 /*
1641  * txt_unmap --
1642  *        Handle the unmap command.
1643  */
1644 static void
txt_unmap(SCR * sp,TEXT * tp,u_int32_t * ec_flagsp)1645 txt_unmap(SCR *sp, TEXT *tp, u_int32_t *ec_flagsp)
1646 {
1647           size_t len, off;
1648           CHAR_T *p;
1649 
1650           /* Find the beginning of this "word". */
1651           for (off = tp->cno - 1, p = tp->lb + off, len = 0;; --p, --off) {
1652                     if (ISBLANK((UCHAR_T)*p)) {
1653                               ++p;
1654                               break;
1655                     }
1656                     ++len;
1657                     if (off == tp->ai || off == tp->offset)
1658                               break;
1659           }
1660 
1661           /*
1662            * !!!
1663            * Historic vi exploded input mappings on the command line.  See the
1664            * txt_abbrev() routine for an explanation of the problems inherent
1665            * in this.
1666            *
1667            * We make this work as follows.  If we get a string which is <blank>
1668            * terminated and which starts at the beginning of the line, we check
1669            * to see it is the unmap command.  If it is, we return that the input
1670            * mapping should be turned off.  Note also, minor trickiness, so that
1671            * if the user erases the line and starts another command, we go ahead
1672            * an turn mapping back on.
1673            */
1674           if ((off == tp->ai || off == tp->offset) && ex_is_unmap(sp, p, len))
1675                     FL_CLR(*ec_flagsp, EC_MAPINPUT);
1676           else
1677                     FL_SET(*ec_flagsp, EC_MAPINPUT);
1678 }
1679 
1680 /*
1681  * txt_ai_resolve --
1682  *        When a line is resolved by <esc>, review autoindent characters.
1683  */
1684 static void
txt_ai_resolve(SCR * sp,TEXT * tp,int * changedp)1685 txt_ai_resolve(SCR *sp, TEXT *tp, int *changedp)
1686 {
1687           u_long ts;
1688           int delc;
1689           size_t cno, len, new, old, scno, spaces, tab_after_sp, tabs;
1690           CHAR_T *p;
1691 
1692           *changedp = 0;
1693 
1694           /*
1695            * If the line is empty, has an offset, or no autoindent
1696            * characters, we're done.
1697            */
1698           if (!tp->len || tp->offset || !tp->ai)
1699                     return;
1700 
1701           /*
1702            * If the length is less than or equal to the autoindent
1703            * characters, delete them.
1704            */
1705           if (tp->len <= tp->ai) {
1706                     tp->ai = tp->cno = tp->len = 0;
1707                     return;
1708           }
1709 
1710           /*
1711            * The autoindent characters plus any leading <blank> characters
1712            * in the line are resolved into the minimum number of characters.
1713            * Historic practice.
1714            */
1715           ts = O_VAL(sp, O_TABSTOP);
1716 
1717           /* Figure out the last <blank> screen column. */
1718           for (p = tp->lb, scno = 0, len = tp->len,
1719               spaces = tab_after_sp = 0; len-- && ISBLANK((UCHAR_T)*p); ++p)
1720                     if (*p == '\t') {
1721                               if (spaces)
1722                                         tab_after_sp = 1;
1723                               scno += COL_OFF(scno, ts);
1724                     } else {
1725                               ++spaces;
1726                               ++scno;
1727                     }
1728 
1729           /*
1730            * If there are no spaces, or no tabs after spaces and less than
1731            * ts spaces, it's already minimal.
1732            * Keep analysing if expandtab is set.
1733            */
1734           if ((!spaces || (!tab_after_sp && spaces < ts)) &&
1735               !O_ISSET(sp, O_EXPANDTAB))
1736                     return;
1737 
1738           /* Count up spaces/tabs needed to get to the target. */
1739           cno = 0;
1740           tabs = 0;
1741           if (!O_ISSET(sp, O_EXPANDTAB)) {
1742                     for (; cno + COL_OFF(cno, ts) <= scno; ++tabs)
1743                               cno += COL_OFF(cno, ts);
1744           }
1745           spaces = scno - cno;
1746 
1747           /*
1748            * Figure out how many characters we're dropping -- if we're not
1749            * dropping any, it's already minimal, we're done.
1750            */
1751           old = p - tp->lb;
1752           new = spaces + tabs;
1753           if (old == new)
1754                     return;
1755 
1756           /* Shift the rest of the characters down, adjust the counts. */
1757           delc = old - new;
1758           MEMMOVEW(p - delc, p, tp->len - old);
1759           tp->len -= delc;
1760           tp->cno -= delc;
1761 
1762           /* Fill in space/tab characters. */
1763           for (p = tp->lb; tabs--;)
1764                     *p++ = '\t';
1765           while (spaces--)
1766                     *p++ = ' ';
1767           *changedp = 1;
1768 }
1769 
1770 /*
1771  * v_txt_auto --
1772  *        Handle autoindent.  If aitp isn't NULL, use it, otherwise,
1773  *        retrieve the line.
1774  *
1775  * PUBLIC: int v_txt_auto __P((SCR *, db_recno_t, TEXT *, size_t, TEXT *));
1776  */
1777 int
v_txt_auto(SCR * sp,db_recno_t lno,TEXT * aitp,size_t len,TEXT * tp)1778 v_txt_auto(SCR *sp, db_recno_t lno, TEXT *aitp, size_t len, TEXT *tp)
1779 {
1780           size_t nlen;
1781           CHAR_T *p, *t;
1782 
1783           if (aitp == NULL) {
1784                     /*
1785                      * If the ex append command is executed with an address of 0,
1786                      * it's possible to get here with a line number of 0.  Return
1787                      * an indent of 0.
1788                      */
1789                     if (lno == 0) {
1790                               tp->ai = 0;
1791                               return (0);
1792                     }
1793                     if (db_get(sp, lno, DBG_FATAL, &t, &len))
1794                               return (1);
1795           } else
1796                     t = aitp->lb;
1797 
1798           /* Count whitespace characters. */
1799           for (p = t; len > 0; ++p, --len)
1800                     if (!ISBLANK((UCHAR_T)*p))
1801                               break;
1802 
1803           /* Set count, check for no indentation. */
1804           if ((nlen = (p - t)) == 0)
1805                     return (0);
1806 
1807           /* Make sure the buffer's big enough. */
1808           BINC_RETW(sp, tp->lb, tp->lb_len, tp->len + nlen);
1809 
1810           /* Copy the buffer's current contents up. */
1811           if (tp->len != 0)
1812                     MEMMOVEW(tp->lb + nlen, tp->lb, tp->len);
1813           tp->len += nlen;
1814 
1815           /* Copy the indentation into the new buffer. */
1816           MEMMOVEW(tp->lb, t, nlen);
1817 
1818           /* Set the autoindent count. */
1819           tp->ai = nlen;
1820           return (0);
1821 }
1822 
1823 /*
1824  * txt_backup --
1825  *        Back up to the previously edited line.
1826  */
1827 static TEXT *
txt_backup(SCR * sp,TEXTH * tiqh,TEXT * tp,u_int32_t * flagsp)1828 txt_backup(SCR *sp, TEXTH *tiqh, TEXT *tp, u_int32_t *flagsp)
1829 {
1830           TEXT *ntp;
1831 
1832           /* Get a handle on the previous TEXT structure. */
1833           if ((ntp = TAILQ_PREV(tp, _texth, q)) == NULL) {
1834                     if (!FL_ISSET(*flagsp, TXT_REPLAY))
1835                               msgq(sp, M_BERR,
1836                                   "193|Already at the beginning of the insert");
1837                     return (tp);
1838           }
1839 
1840           /* Bookkeeping. */
1841           ntp->len = ntp->sv_len;
1842 
1843           /* Handle appending to the line. */
1844           if (ntp->owrite == 0 && ntp->insert == 0) {
1845                     ntp->lb[ntp->len] = CH_CURSOR;
1846                     ++ntp->insert;
1847                     ++ntp->len;
1848                     FL_SET(*flagsp, TXT_APPENDEOL);
1849           } else
1850                     FL_CLR(*flagsp, TXT_APPENDEOL);
1851 
1852           /* Release the current TEXT. */
1853           TAILQ_REMOVE(tiqh, tp, q);
1854           text_free(tp);
1855 
1856           /* Update the old line on the screen. */
1857           if (vs_change(sp, ntp->lno + 1, LINE_DELETE))
1858                     return (NULL);
1859 
1860           /* Return the new/current TEXT. */
1861           return (ntp);
1862 }
1863 
1864 /*
1865  * Text indentation is truly strange.  ^T and ^D do movements to the next or
1866  * previous shiftwidth value, i.e. for a 1-based numbering, with shiftwidth=3,
1867  * ^T moves a cursor on the 7th, 8th or 9th column to the 10th column, and ^D
1868  * moves it back.
1869  *
1870  * !!!
1871  * The ^T and ^D characters in historical vi had special meaning only when they
1872  * were the first characters entered after entering text input mode.  As normal
1873  * erase characters couldn't erase autoindent characters (^T in this case), it
1874  * meant that inserting text into previously existing text was strange -- ^T
1875  * only worked if it was the first keystroke(s), and then could only be erased
1876  * using ^D.  This implementation treats ^T specially anywhere it occurs in the
1877  * input, and permits the standard erase characters to erase the characters it
1878  * inserts.
1879  *
1880  * !!!
1881  * A fun test is to try:
1882  *        :se sw=4 ai list
1883  *        i<CR>^Tx<CR>^Tx<CR>^Tx<CR>^Dx<CR>^Dx<CR>^Dx<esc>
1884  * Historic vi loses some of the '$' marks on the line ends, but otherwise gets
1885  * it right.
1886  *
1887  * XXX
1888  * Technically, txt_dent should be part of the screen interface, as it requires
1889  * knowledge of character sizes, including <space>s, on the screen.  It's here
1890  * because it's a complicated little beast, and I didn't want to shove it down
1891  * into the screen.  It's probable that KEY_LEN will call into the screen once
1892  * there are screens with different character representations.
1893  *
1894  * txt_dent --
1895  *        Handle ^T indents, ^D outdents.
1896  *
1897  * If anything changes here, check the ex version to see if it needs similar
1898  * changes.
1899  */
1900 static int
txt_dent(SCR * sp,TEXT * tp,int isindent)1901 txt_dent(SCR *sp, TEXT *tp, int isindent)
1902 {
1903           ARG_CHAR_T ch;
1904           u_long sw, ts;
1905           size_t cno, current, spaces, target, tabs;
1906 
1907           ts = O_VAL(sp, O_TABSTOP);
1908           sw = O_VAL(sp, O_SHIFTWIDTH);
1909 
1910           /*
1911            * Since we don't know what precedes the character(s) being inserted
1912            * (or deleted), the preceding whitespace characters must be resolved.
1913            * An example is a <tab>, which doesn't need a full shiftwidth number
1914            * of columns because it's preceded by <space>s.  This is easy to get
1915            * if the user sets shiftwidth to a value less than tabstop (or worse,
1916            * something for which tabstop isn't a multiple) and then uses ^T to
1917            * indent, and ^D to outdent.
1918            *
1919            * Figure out the current and target screen columns.  In the historic
1920            * vi, the autoindent column was NOT determined using display widths
1921            * of characters as was the wrapmargin column.  For that reason, we
1922            * can't use the vs_column() function, but have to calculate it here.
1923            * This is slow, but it's normally only on the first few characters of
1924            * a line.
1925            */
1926           for (current = cno = 0; cno < tp->cno; ++cno)
1927                     current += tp->lb[cno] == '\t' ?
1928                         COL_OFF(current, ts) : KEY_COL(sp, tp->lb[cno]);
1929 
1930           target = current;
1931           if (isindent)
1932                     target += COL_OFF(target, sw);
1933           else {
1934                     --target;
1935                     target -= target % sw;
1936           }
1937 
1938           /*
1939            * Back up over any previous <blank> characters, changing them into
1940            * overwrite characters (including any ai characters).  Then figure
1941            * out the current screen column.
1942            */
1943           for (; tp->cno > tp->offset &&
1944               (tp->lb[tp->cno - 1] == ' ' || tp->lb[tp->cno - 1] == '\t');
1945               --tp->cno, ++tp->owrite);
1946           for (current = cno = 0; cno < tp->cno; ++cno)
1947                     current += tp->lb[cno] == '\t' ?
1948                         COL_OFF(current, ts) : KEY_COL(sp, tp->lb[cno]);
1949 
1950           /*
1951            * If we didn't move up to or past the target, it's because there
1952            * weren't enough characters to delete, e.g. the first character
1953            * of the line was a tp->offset character, and the user entered
1954            * ^D to move to the beginning of a line.  An example of this is:
1955            *
1956            *        :set ai sw=4<cr>i<space>a<esc>i^T^D
1957            *
1958            * Otherwise, count up the total spaces/tabs needed to get from the
1959            * beginning of the line (or the last non-<blank> character) to the
1960            * target.
1961            */
1962           if (current >= target)
1963                     spaces = tabs = 0;
1964           else {
1965                     cno = current;
1966                     tabs = 0;
1967                     if (!O_ISSET(sp, O_EXPANDTAB)) {
1968                               for (; cno + COL_OFF(cno, ts) <= target; ++tabs)
1969                                         cno += COL_OFF(cno, ts);
1970                     }
1971                     spaces = target - cno;
1972           }
1973 
1974           tp->ai = tabs + spaces;
1975 
1976           /*
1977            * Call txt_insch() to insert each character, so that we get the
1978            * correct effect when we add a <tab> to replace N <spaces>.
1979            */
1980           for (ch = '\t'; tabs > 0; --tabs)
1981                     (void)txt_insch(sp, tp, &ch, 0);
1982           for (ch = ' '; spaces > 0; --spaces)
1983                     (void)txt_insch(sp, tp, &ch, 0);
1984           return (0);
1985 }
1986 
1987 /*
1988  * txt_fc --
1989  *        File name completion.
1990  */
1991 static int
txt_fc(SCR * sp,TEXT * tp,int * redrawp)1992 txt_fc(SCR *sp, TEXT *tp, int *redrawp)
1993 {
1994           struct stat sb;
1995           ARGS **argv;
1996           CHAR_T s_ch;
1997           EXCMD cmd;
1998           size_t indx, len, nlen, off;
1999           int argc, trydir;
2000           CHAR_T *p, *t;
2001           const char *np;
2002           size_t nplen;
2003 
2004           trydir = 0;
2005           *redrawp = 0;
2006 
2007           /*
2008            * Find the beginning of this "word" -- if we're at the beginning
2009            * of the line, it's a special case.
2010            */
2011           if (tp->cno == 1) {
2012                     len = 0;
2013                     p = tp->lb;
2014           } else
2015 retry:              for (len = 0,
2016                         off = tp->cno - 1, p = tp->lb + off;; --off, --p) {
2017                               if (ISBLANK((UCHAR_T)*p)) {
2018                                         ++p;
2019                                         break;
2020                               }
2021                               ++len;
2022                               if (off == tp->ai || off == tp->offset)
2023                                         break;
2024                     }
2025 
2026           /*
2027            * Get enough space for a wildcard character.
2028            *
2029            * XXX
2030            * This won't work for "foo\", since the \ will escape the expansion
2031            * character.  I'm not sure if that's a bug or not...
2032            */
2033           off = p - tp->lb;
2034           BINC_RETW(sp, tp->lb, tp->lb_len, tp->len + 1);
2035           p = tp->lb + off;
2036 
2037           s_ch = p[len];
2038           p[len] = '*';
2039 
2040           /* Build an ex command, and call the ex expansion routines. */
2041           ex_cinit(sp, &cmd, 0, 0, OOBLNO, OOBLNO, 0);
2042           if (argv_exp2(sp, &cmd, p, len + 1)) {
2043                     p[len] = s_ch;
2044                     return (0);
2045           }
2046           argc = cmd.argc;
2047           argv = cmd.argv;
2048 
2049           p[len] = s_ch;
2050 
2051           switch (argc) {
2052           case 0:                                 /* No matches. */
2053                     if (!trydir)
2054                               (void)sp->gp->scr_bell(sp);
2055                     return (0);
2056           case 1:                                 /* One match. */
2057                     /* If something changed, do the exchange. */
2058                     nlen = STRLEN(cmd.argv[0]->bp);
2059                     if (len != nlen || MEMCMP(cmd.argv[0]->bp, p, len))
2060                               break;
2061 
2062                     /* If haven't done a directory test, do it now. */
2063                     INT2CHAR(sp, cmd.argv[0]->bp, cmd.argv[0]->len + 1,
2064                                np, nplen);
2065                     if (!trydir &&
2066                         !stat(np, &sb) && S_ISDIR(sb.st_mode)) {
2067                               p += len;
2068                               goto isdir;
2069                     }
2070 
2071                     /* If nothing changed, period, ring the bell. */
2072                     if (!trydir)
2073                               (void)sp->gp->scr_bell(sp);
2074                     return (0);
2075           default:                      /* Multiple matches. */
2076                     *redrawp = 1;
2077                     if (txt_fc_col(sp, argc, argv))
2078                               return (1);
2079 
2080                     /* Find the length of the shortest match. */
2081                     for (nlen = cmd.argv[0]->len; --argc > 0;) {
2082                               if (cmd.argv[argc]->len < nlen)
2083                                         nlen = cmd.argv[argc]->len;
2084                               for (indx = 0; indx < nlen &&
2085                                   cmd.argv[argc]->bp[indx] == cmd.argv[0]->bp[indx];
2086                                   ++indx);
2087                               nlen = indx;
2088                     }
2089                     break;
2090           }
2091 
2092           /* Overwrite the expanded text first. */
2093           for (t = cmd.argv[0]->bp; len > 0 && nlen > 0; --len, --nlen)
2094                     *p++ = *t++;
2095 
2096           /* If lost text, make the remaining old text overwrite characters. */
2097           if (len) {
2098                     tp->cno -= len;
2099                     tp->owrite += len;
2100           }
2101 
2102           /* Overwrite any overwrite characters next. */
2103           for (; nlen > 0 && tp->owrite > 0; --nlen, --tp->owrite, ++tp->cno)
2104                     *p++ = *t++;
2105 
2106           /* Shift remaining text up, and move the cursor to the end. */
2107           if (nlen) {
2108                     off = p - tp->lb;
2109                     BINC_RETW(sp, tp->lb, tp->lb_len, tp->len + nlen);
2110                     p = tp->lb + off;
2111 
2112                     tp->cno += nlen;
2113                     tp->len += nlen;
2114 
2115                     if (tp->insert != 0)
2116                               (void)MEMMOVEW(p + nlen, p, tp->insert);
2117                     while (nlen--)
2118                               *p++ = *t++;
2119           }
2120 
2121           /* If a single match and it's a directory, retry it. */
2122           INT2CHAR(sp, cmd.argv[0]->bp, cmd.argv[0]->len + 1, np, nplen);
2123           if (argc == 1 && !stat(np, &sb) && S_ISDIR(sb.st_mode)) {
2124 isdir:              if (tp->owrite == 0) {
2125                               off = p - tp->lb;
2126                               BINC_RETW(sp, tp->lb, tp->lb_len, tp->len + 1);
2127                               p = tp->lb + off;
2128                               if (tp->insert != 0)
2129                                         (void)MEMMOVEW(p + 1, p, tp->insert);
2130                               ++tp->len;
2131                     } else
2132                               --tp->owrite;
2133 
2134                     ++tp->cno;
2135                     *p++ = '/';
2136 
2137                     trydir = 1;
2138                     goto retry;
2139           }
2140           return (0);
2141 }
2142 
2143 /*
2144  * txt_fc_col --
2145  *        Display file names for file name completion.
2146  */
2147 static int
txt_fc_col(SCR * sp,int argc,ARGS ** argv)2148 txt_fc_col(SCR *sp, int argc, ARGS **argv)
2149 {
2150           ARGS **av;
2151           CHAR_T *p;
2152           GS *gp;
2153           size_t base, cnt, col, colwidth, numrows, numcols, prefix, row;
2154           int ac, nf, reset;
2155           const char *np;
2156           char *pp;
2157           size_t nlen;
2158 
2159           gp = sp->gp;
2160 
2161           /* Trim any directory prefix common to all of the files. */
2162           INT2CHAR(sp, argv[0]->bp, argv[0]->len + 1, np, nlen);
2163           if ((pp = strrchr(np, '/')) == NULL)
2164                     prefix = 0;
2165           else {
2166                     prefix = (pp - np) + 1;
2167                     for (ac = argc - 1, av = argv + 1; ac > 0; --ac, ++av)
2168                               if (av[0]->len < prefix ||
2169                                   MEMCMP(av[0]->bp, argv[0]->bp,
2170                                            prefix)) {
2171                                         prefix = 0;
2172                                         break;
2173                               }
2174           }
2175 
2176           /*
2177            * Figure out the column width for the longest name.  Output is done on
2178            * 6 character "tab" boundaries for no particular reason.  (Since we
2179            * don't output tab characters, we ignore the terminal's tab settings.)
2180            * Ignore the user's tab setting because we have no idea how reasonable
2181            * it is.
2182            */
2183           for (ac = argc, av = argv, colwidth = 0; ac > 0; --ac, ++av) {
2184                     for (col = 0, p = av[0]->bp + prefix; *p != '\0'; ++p)
2185                               col += KEY_LEN(sp, *p);
2186                     if (col > colwidth)
2187                               colwidth = col;
2188           }
2189           colwidth += COL_OFF(colwidth, 6);
2190 
2191           /*
2192            * Writing to the bottom line of the screen is always turned off when
2193            * SC_TINPUT_INFO is set.  Turn it back on, we know what we're doing.
2194            */
2195           if (F_ISSET(sp, SC_TINPUT_INFO)) {
2196                     reset = 1;
2197                     F_CLR(sp, SC_TINPUT_INFO);
2198           } else
2199                     reset = 0;
2200 
2201 #define   CHK_INTR                                                              \
2202           if (F_ISSET(gp, G_INTERRUPTED))                                                 \
2203                     goto intr;
2204 
2205           /* If the largest file name is too large, just print them. */
2206           if (colwidth > sp->cols) {
2207                     for (ac = argc, av = argv; ac > 0; --ac, ++av) {
2208                               INT2CHAR(sp, av[0]->bp+prefix, av[0]->len+1-prefix,
2209                                          np, nlen);
2210                               pp = msg_print(sp, np, &nf);
2211                               (void)ex_printf(sp, "%s\n", pp);
2212                               if (F_ISSET(gp, G_INTERRUPTED))
2213                                         break;
2214                     }
2215                     if (nf)
2216                               FREE_SPACE(sp, pp, 0);
2217                     CHK_INTR;
2218           } else {
2219                     /* Figure out the number of columns. */
2220                     numcols = (sp->cols - 1) / colwidth;
2221                     if ((size_t)argc > numcols) {
2222                               numrows = argc / numcols;
2223                               if (argc % numcols)
2224                                         ++numrows;
2225                     } else
2226                               numrows = 1;
2227 
2228                     /* Display the files in sorted order. */
2229                     for (row = 0; row < numrows; ++row) {
2230                               for (base = row, col = 0; col < numcols; ++col) {
2231                                         INT2CHAR(sp, argv[base]->bp+prefix,
2232                                                   argv[base]->len+1-prefix, np, nlen);
2233                                         pp = msg_print(sp, np, &nf);
2234                                         cnt = ex_printf(sp, "%s", pp);
2235                                         if (nf)
2236                                                   FREE_SPACE(sp, pp, 0);
2237                                         CHK_INTR;
2238                                         if ((base += numrows) >= (size_t)argc)
2239                                                   break;
2240                                         (void)ex_printf(sp,
2241                                             "%*s", (int)(colwidth - cnt), "");
2242                                         CHK_INTR;
2243                               }
2244                               (void)ex_puts(sp, "\n");
2245                               CHK_INTR;
2246                     }
2247                     (void)ex_puts(sp, "\n");
2248                     CHK_INTR;
2249           }
2250           (void)ex_fflush(sp);
2251 
2252           if (0) {
2253 intr:               F_CLR(gp, G_INTERRUPTED);
2254           }
2255           if (reset)
2256                     F_SET(sp, SC_TINPUT_INFO);
2257 
2258           return (0);
2259 }
2260 
2261 /*
2262  * txt_emark --
2263  *        Set the end mark on the line.
2264  */
2265 static int
txt_emark(SCR * sp,TEXT * tp,size_t cno)2266 txt_emark(SCR *sp, TEXT *tp, size_t cno)
2267 {
2268           CHAR_T ch;
2269           unsigned char *kp;
2270           size_t chlen, nlen, olen;
2271           CHAR_T *p;
2272 
2273           ch = CH_ENDMARK;
2274 
2275           /*
2276            * The end mark may not be the same size as the current character.
2277            * Don't let the line shift.
2278            */
2279           nlen = KEY_COL(sp, ch);
2280           if (tp->lb[cno] == '\t')
2281                     (void)vs_columns(sp, tp->lb, tp->lno, &cno, &olen);
2282           else
2283                     olen = KEY_COL(sp, tp->lb[cno]);
2284 
2285           /*
2286            * If the line got longer, well, it's weird, but it's easy.  If
2287            * it's the same length, it's easy.  If it got shorter, we have
2288            * to fix it up.
2289            */
2290           if (olen > nlen) {
2291                     BINC_RETW(sp, tp->lb, tp->lb_len, tp->len + olen);
2292                     chlen = olen - nlen;
2293                     if (tp->insert != 0)
2294                               MEMMOVEW(tp->lb + cno + 1 + chlen,
2295                                   tp->lb + cno + 1, tp->insert);
2296 
2297                     tp->len += chlen;
2298                     tp->owrite += chlen;
2299                     p = tp->lb + cno;
2300                     if (tp->lb[cno] == '\t')
2301                               for (cno += chlen; chlen--;)
2302                                         *p++ = ' ';
2303                     else if (INTISWIDE(tp->lb[cno])) {
2304                               /*
2305                                * Do not put the end mark on a part of a multi-width
2306                                * char, which results in screen corruption.
2307                                */
2308                               for (; chlen >= nlen; chlen -= nlen) {
2309                                         cno++;
2310                                         *p++ = ch;
2311                               }
2312                               /* Paranoia: nlen is usually 1. */
2313                               for (cno += chlen; chlen--;)
2314                                         *p++ = ' ';
2315                     } else
2316                               for (kp = KEY_NAME(sp, tp->lb[cno]),
2317                                   cno += chlen; chlen--;)
2318                                         *p++ = *kp++;
2319           }
2320           tp->lb[cno] = ch;
2321           return (vs_change(sp, tp->lno, LINE_RESET));
2322 }
2323 
2324 /*
2325  * txt_err --
2326  *        Handle an error during input processing.
2327  */
2328 static void
txt_err(SCR * sp,TEXTH * tiqh)2329 txt_err(SCR *sp, TEXTH *tiqh)
2330 {
2331           db_recno_t lno;
2332 
2333           /*
2334            * The problem with input processing is that the cursor is at an
2335            * indeterminate position since some input may have been lost due
2336            * to a malloc error.  So, try to go back to the place from which
2337            * the cursor started, knowing that it may no longer be available.
2338            *
2339            * We depend on at least one line number being set in the text
2340            * chain.
2341            */
2342           for (lno = TAILQ_FIRST(tiqh)->lno;
2343               !db_exist(sp, lno) && lno > 0; --lno);
2344 
2345           sp->lno = lno == 0 ? 1 : lno;
2346           sp->cno = 0;
2347 
2348           /* Redraw the screen, just in case. */
2349           F_SET(sp, SC_SCR_REDRAW);
2350 }
2351 
2352 /*
2353  * txt_hex --
2354  *        Let the user insert any character value they want.
2355  *
2356  * !!!
2357  * This is an extension.  The pattern "^X[0-9a-fA-F]*" is a way
2358  * for the user to specify a character value which their keyboard
2359  * may not be able to enter.
2360  */
2361 static int
txt_hex(SCR * sp,TEXT * tp)2362 txt_hex(SCR *sp, TEXT *tp)
2363 {
2364           CHAR_T savec;
2365           size_t len, off;
2366           long value;
2367           CHAR_T *p, *wp;
2368 
2369           /*
2370            * Null-terminate the string.  Since nul isn't a legal hex value,
2371            * this should be okay, and lets us use a local routine, which
2372            * presumably understands the character set, to convert the value.
2373            */
2374           savec = tp->lb[tp->cno];
2375           tp->lb[tp->cno] = 0;
2376 
2377           /* Find the previous CH_HEX character. */
2378           for (off = tp->cno - 1, p = tp->lb + off, len = 0;; --p, --off, ++len) {
2379                     if (*p == CH_HEX) {
2380                               wp = p + 1;
2381                               break;
2382                     }
2383                     /* Not on this line?  Shouldn't happen. */
2384                     if (off == tp->ai || off == tp->offset)
2385                               goto nothex;
2386           }
2387 
2388           /* If length of 0, then it wasn't a hex value. */
2389           if (len == 0)
2390                     goto nothex;
2391 
2392           /* Get the value. */
2393           errno = 0;
2394           value = STRTOL(wp, NULL, 16);
2395           if (errno || value < 0 || value > 0xff) {
2396 nothex:             tp->lb[tp->cno] = savec;
2397                     return (0);
2398           }
2399 
2400           /* Restore the original character. */
2401           tp->lb[tp->cno] = savec;
2402 
2403           /* Adjust the bookkeeping. */
2404           tp->cno -= len;
2405           tp->len -= len;
2406           tp->lb[tp->cno - 1] = value;
2407 
2408           /* Copy down any overwrite characters. */
2409           if (tp->owrite)
2410                     MEMMOVEW(tp->lb + tp->cno, tp->lb + tp->cno + len,
2411                         tp->owrite);
2412 
2413           /* Copy down any insert characters. */
2414           if (tp->insert)
2415                     MEMMOVEW(tp->lb + tp->cno + tp->owrite,
2416                         tp->lb + tp->cno + tp->owrite + len,
2417                         tp->insert);
2418 
2419           return (0);
2420 }
2421 
2422 /*
2423  * txt_insch --
2424  *
2425  * !!!
2426  * Historic vi did a special screen optimization for tab characters.  As an
2427  * example, for the keystrokes "iabcd<esc>0C<tab>", the tab overwrote the
2428  * rest of the string when it was displayed.
2429  *
2430  * Because early versions of this implementation redisplayed the entire line
2431  * on each keystroke, the "bcd" was pushed to the right as it ignored that
2432  * the user had "promised" to change the rest of the characters.  However,
2433  * the historic vi implementation had an even worse bug: given the keystrokes
2434  * "iabcd<esc>0R<tab><esc>", the "bcd" disappears, and magically reappears
2435  * on the second <esc> key.
2436  *
2437  * POSIX 1003.2 requires (will require) that this be fixed, specifying that
2438  * vi overwrite characters the user has committed to changing, on the basis
2439  * of the screen space they require, but that it not overwrite other characters.
2440  */
2441 static int
txt_insch(SCR * sp,TEXT * tp,ARG_CHAR_T * chp,u_int flags)2442 txt_insch(SCR *sp, TEXT *tp, ARG_CHAR_T *chp, u_int flags)
2443 {
2444           unsigned char *kp;
2445           CHAR_T savech;
2446           size_t chlen, cno, copydown, olen, nlen;
2447           CHAR_T *p;
2448 
2449           /*
2450            * The 'R' command does one-for-one replacement, because there's
2451            * no way to know how many characters the user intends to replace.
2452            */
2453           if (LF_ISSET(TXT_REPLACE)) {
2454                     if (tp->owrite) {
2455                               --tp->owrite;
2456                               tp->lb[tp->cno++] = (UCHAR_T)*chp;
2457                               return (0);
2458                     }
2459           } else if (tp->owrite) {                /* Overwrite a character. */
2460                     cno = tp->cno;
2461 
2462                     /*
2463                      * If the old or new characters are tabs, then the length of the
2464                      * display depends on the character position in the display.  We
2465                      * don't even try to handle this here, just ask the screen.
2466                      */
2467                     if (*chp == '\t') {
2468                               savech = tp->lb[cno];
2469                               tp->lb[cno] = '\t';
2470                               (void)vs_columns(sp, tp->lb, tp->lno, &cno, &nlen);
2471                               tp->lb[cno] = savech;
2472                     } else
2473                               nlen = KEY_COL(sp, *chp);
2474 
2475                     /*
2476                      * Eat overwrite characters until we run out of them or we've
2477                      * handled the length of the new character.  If we only eat
2478                      * part of an overwrite character, break it into its component
2479                      * elements and display the remaining components.
2480                      */
2481                     for (copydown = 0; nlen != 0 && tp->owrite != 0;) {
2482                               --tp->owrite;
2483 
2484                               if (tp->lb[cno] == '\t')
2485                                         (void)vs_columns(sp,
2486                                             tp->lb, tp->lno, &cno, &olen);
2487                               else
2488                                         olen = KEY_COL(sp, tp->lb[cno]);
2489 
2490                               if (olen == nlen) {
2491                                         nlen = 0;
2492                                         break;
2493                               }
2494                               if (olen < nlen) {
2495                                         ++copydown;
2496                                         nlen -= olen;
2497                               } else {
2498                                         BINC_RETW(sp,
2499                                             tp->lb, tp->lb_len, tp->len + olen);
2500                                         chlen = olen - nlen;
2501                                         MEMMOVEW(tp->lb + cno + 1 + chlen,
2502                                             tp->lb + cno + 1,
2503                                             tp->owrite + tp->insert);
2504 
2505                                         tp->len += chlen;
2506                                         tp->owrite += chlen;
2507                                         /*
2508                                          * Do not rewrite a part of a multi-width char,
2509                                          * which results in screen corruption.
2510                                          */
2511                                         if (tp->lb[cno] == '\t'
2512                                             || INTISWIDE(tp->lb[cno]))
2513                                                   for (p = tp->lb + cno + 1; chlen--;)
2514                                                             *p++ = ' ';
2515                                         else
2516                                                   for (kp =
2517                                                       KEY_NAME(sp, tp->lb[cno]) + nlen,
2518                                                       p = tp->lb + cno + 1; chlen--;)
2519                                                             *p++ = *kp++;
2520                                         nlen = 0;
2521                                         break;
2522                               }
2523                     }
2524 
2525                     /*
2526                      * If had to erase several characters, we adjust the total
2527                      * count, and if there are any characters left, shift them
2528                      * into position.
2529                      */
2530                     if (copydown != 0 && (tp->len -= copydown) != 0)
2531                               MEMMOVEW(tp->lb + cno, tp->lb + cno + copydown,
2532                                   tp->owrite + tp->insert + copydown);
2533 
2534                     /* If we had enough overwrite characters, we're done. */
2535                     if (nlen == 0) {
2536                               tp->lb[tp->cno++] = (UCHAR_T)*chp;
2537                               return (0);
2538                     }
2539           }
2540 
2541           /* Check to see if the character fits into the input buffer. */
2542           BINC_RETW(sp, tp->lb, tp->lb_len, tp->len + 1);
2543 
2544           ++tp->len;
2545           if (tp->insert) {                       /* Insert a character. */
2546                     if (tp->insert == 1)
2547                               tp->lb[tp->cno + 1] = tp->lb[tp->cno];
2548                     else
2549                               MEMMOVEW(tp->lb + tp->cno + 1,
2550                                   tp->lb + tp->cno, tp->owrite + tp->insert);
2551           }
2552           tp->lb[tp->cno++] = (UCHAR_T)*chp;
2553           return (0);
2554 }
2555 
2556 /*
2557  * txt_isrch --
2558  *        Do an incremental search.
2559  */
2560 static int
txt_isrch(SCR * sp,VICMD * vp,TEXT * tp,u_int8_t * is_flagsp)2561 txt_isrch(SCR *sp, VICMD *vp, TEXT *tp, u_int8_t *is_flagsp)
2562 {
2563           MARK start;
2564           db_recno_t lno;
2565           u_int sf;
2566 
2567           /* If it's a one-line screen, we don't do incrementals. */
2568           if (IS_ONELINE(sp)) {
2569                     FL_CLR(*is_flagsp, IS_RUNNING);
2570                     return (0);
2571           }
2572 
2573           /*
2574            * If the user erases back to the beginning of the buffer, there's
2575            * nothing to search for.  Reset the cursor to the starting point.
2576            */
2577           if (tp->cno <= 1) {
2578                     vp->m_final = vp->m_start;
2579                     return (0);
2580           }
2581 
2582           /*
2583            * If it's an RE quote character, and not quoted, ignore it until
2584            * we get another character.
2585            */
2586           if (tp->lb[tp->cno - 1] == '\\' &&
2587               (tp->cno == 2 || tp->lb[tp->cno - 2] != '\\'))
2588                     return (0);
2589 
2590           /*
2591            * If it's a magic shell character, and not quoted, reset the cursor
2592            * to the starting point.
2593            */
2594           if (strchr(O_STR(sp, O_SHELLMETA), tp->lb[tp->cno - 1]) != NULL &&
2595               (tp->cno == 2 || tp->lb[tp->cno - 2] != '\\'))
2596                     vp->m_final = vp->m_start;
2597 
2598           /*
2599            * If we see the search pattern termination character, then quit doing
2600            * an incremental search.  There may be more, e.g., ":/foo/;/bar/",
2601            * and we can't handle that incrementally.  Also, reset the cursor to
2602            * the original location, the ex search routines don't know anything
2603            * about incremental searches.
2604            */
2605           if (tp->lb[0] == tp->lb[tp->cno - 1] &&
2606               (tp->cno == 2 || tp->lb[tp->cno - 2] != '\\')) {
2607                     vp->m_final = vp->m_start;
2608                     FL_CLR(*is_flagsp, IS_RUNNING);
2609                     return (0);
2610           }
2611 
2612           /*
2613            * Remember the input line and discard the special input map,
2614            * but don't overwrite the input line on the screen.
2615            */
2616           lno = tp->lno;
2617           F_SET(VIP(sp), VIP_S_MODELINE);
2618           F_CLR(sp, SC_TINPUT | SC_TINPUT_INFO);
2619           if (txt_map_end(sp))
2620                     return (1);
2621 
2622           /*
2623            * Specify a starting point and search.  If we find a match, move to
2624            * it and refresh the screen.  If we didn't find the match, then we
2625            * beep the screen.  When searching from the original cursor position,
2626            * we have to move the cursor, otherwise, we don't want to move the
2627            * cursor in case the text at the current position continues to match.
2628            */
2629           if (FL_ISSET(*is_flagsp, IS_RESTART)) {
2630                     start = vp->m_start;
2631                     sf = SEARCH_SET;
2632           } else {
2633                     start = vp->m_final;
2634                     sf = SEARCH_INCR | SEARCH_SET;
2635           }
2636 
2637           if (tp->lb[0] == '/' ?
2638               !f_search(sp,
2639               &start, &vp->m_final, tp->lb + 1, tp->cno - 1, NULL, sf) :
2640               !b_search(sp,
2641               &start, &vp->m_final, tp->lb + 1, tp->cno - 1, NULL, sf)) {
2642                     sp->lno = vp->m_final.lno;
2643                     sp->cno = vp->m_final.cno;
2644                     FL_CLR(*is_flagsp, IS_RESTART);
2645 
2646                     if (!KEYS_WAITING(sp) && vs_refresh(sp, 0))
2647                               return (1);
2648           } else
2649                     FL_SET(*is_flagsp, IS_RESTART);
2650 
2651           /* Reinstantiate the special input map. */
2652           if (txt_map_init(sp))
2653                     return (1);
2654           F_CLR(VIP(sp), VIP_S_MODELINE);
2655           F_SET(sp, SC_TINPUT | SC_TINPUT_INFO);
2656 
2657           /* Reset the line number of the input line. */
2658           tp->lno = TMAP[0].lno;
2659 
2660           /*
2661            * If the colon command-line moved, i.e. the screen scrolled,
2662            * refresh the input line.
2663            *
2664            * XXX
2665            * We shouldn't be calling vs_line, here -- we need dirty bits
2666            * on entries in the SMAP array.
2667            */
2668           if (lno != TMAP[0].lno) {
2669                     if (vs_line(sp, &TMAP[0], NULL, NULL))
2670                               return (1);
2671                     (void)sp->gp->scr_refresh(sp, 0);
2672           }
2673           return (0);
2674 }
2675 
2676 /*
2677  * txt_resolve --
2678  *        Resolve the input text chain into the file.
2679  */
2680 static int
txt_resolve(SCR * sp,TEXTH * tiqh,u_int32_t flags)2681 txt_resolve(SCR *sp, TEXTH *tiqh, u_int32_t flags)
2682 {
2683           TEXT *tp;
2684           db_recno_t lno;
2685           int changed;
2686 
2687           /*
2688            * The first line replaces a current line, and all subsequent lines
2689            * are appended into the file.  Resolve autoindented characters for
2690            * each line before committing it.  If the latter causes the line to
2691            * change, we have to redisplay it, otherwise the information cached
2692            * about the line will be wrong.
2693            */
2694           tp = TAILQ_FIRST(tiqh);
2695 
2696           if (LF_ISSET(TXT_AUTOINDENT))
2697                     txt_ai_resolve(sp, tp, &changed);
2698           else
2699                     changed = 0;
2700           if (db_set(sp, tp->lno, tp->lb, tp->len) ||
2701               (changed && vs_change(sp, tp->lno, LINE_RESET)))
2702                     return (1);
2703 
2704           for (lno = tp->lno; (tp = TAILQ_NEXT(tp, q)) != NULL; ++lno) {
2705                     if (LF_ISSET(TXT_AUTOINDENT))
2706                               txt_ai_resolve(sp, tp, &changed);
2707                     else
2708                               changed = 0;
2709                     if (db_append(sp, 0, lno, tp->lb, tp->len) ||
2710                         (changed && vs_change(sp, tp->lno, LINE_RESET)))
2711                               return (1);
2712           }
2713 
2714           /*
2715            * Clear the input flag, the look-aside buffer is no longer valid.
2716            * Has to be done as part of text resolution, or upon return we'll
2717            * be looking at incorrect data.
2718            */
2719           F_CLR(sp, SC_TINPUT);
2720 
2721           return (0);
2722 }
2723 
2724 /*
2725  * txt_showmatch --
2726  *        Show a character match.
2727  *
2728  * !!!
2729  * Historic vi tried to display matches even in the :colon command line.
2730  * I think not.
2731  */
2732 static int
txt_showmatch(SCR * sp,TEXT * tp)2733 txt_showmatch(SCR *sp, TEXT *tp)
2734 {
2735           VCS cs;
2736           MARK m;
2737           int cnt, endc, startc;
2738 
2739           /*
2740            * Do a refresh first, in case we haven't done one in awhile,
2741            * so the user can see what we're complaining about.
2742            */
2743           UPDATE_POSITION(sp, tp);
2744           if (vs_refresh(sp, 1))
2745                     return (1);
2746 
2747           /*
2748            * We don't display the match if it's not on the screen.  Find
2749            * out what the first character on the screen is.
2750            */
2751           if (vs_sm_position(sp, &m, 0, P_TOP))
2752                     return (1);
2753 
2754           /* Initialize the getc() interface. */
2755           cs.cs_lno = tp->lno;
2756           cs.cs_cno = tp->cno - 1;
2757           if (cs_init(sp, &cs))
2758                     return (1);
2759           startc = (endc = cs.cs_ch)  == ')' ? '(' : '{';
2760 
2761           /* Search for the match. */
2762           for (cnt = 1;;) {
2763                     if (cs_prev(sp, &cs))
2764                               return (1);
2765                     if (cs.cs_flags != 0) {
2766                               if (cs.cs_flags == CS_EOF || cs.cs_flags == CS_SOF) {
2767                                         msgq(sp, M_BERR,
2768                                             "Unmatched %s", KEY_NAME(sp, endc));
2769                                         return (0);
2770                               }
2771                               continue;
2772                     }
2773                     if (cs.cs_ch == endc)
2774                               ++cnt;
2775                     else if (cs.cs_ch == startc && --cnt == 0)
2776                               break;
2777           }
2778 
2779           /* If the match is on the screen, move to it. */
2780           if (cs.cs_lno < m.lno || (cs.cs_lno == m.lno && cs.cs_cno < m.cno))
2781                     return (0);
2782           sp->lno = cs.cs_lno;
2783           sp->cno = cs.cs_cno;
2784           if (vs_refresh(sp, 1))
2785                     return (1);
2786 
2787           /* Wait for timeout or character arrival. */
2788           return (v_event_get(sp,
2789               NULL, O_VAL(sp, O_MATCHTIME) * 100, EC_TIMEOUT));
2790 }
2791 
2792 /*
2793  * txt_margin --
2794  *        Handle margin wrap.
2795  */
2796 static int
txt_margin(SCR * sp,TEXT * tp,TEXT * wmtp,int * didbreak,u_int32_t flags)2797 txt_margin(SCR *sp, TEXT *tp, TEXT *wmtp, int *didbreak, u_int32_t flags)
2798 {
2799           size_t len, off;
2800           CHAR_T *p;
2801 
2802           /* Find the nearest previous blank. */
2803           for (off = tp->cno - 1, p = tp->lb + off, len = 0;; --off, --p, ++len) {
2804                     if (ISBLANK((UCHAR_T)*p)) {
2805                               break;
2806                     }
2807 
2808                     /*
2809                      * If reach the start of the line, there's nowhere to break.
2810                      *
2811                      * !!!
2812                      * Historic vi belled each time a character was entered after
2813                      * crossing the margin until a space was entered which could
2814                      * be used to break the line.  I don't as it tends to wake the
2815                      * cats.
2816                      */
2817                     if (off == tp->ai || off == tp->offset) {
2818                               *didbreak = 0;
2819                               return (0);
2820                     }
2821           }
2822 
2823           /*
2824            * Store saved information about the rest of the line in the
2825            * wrapmargin TEXT structure.
2826            *
2827            * !!!
2828            * The offset field holds the length of the current characters
2829            * that the user entered, but which are getting split to the new
2830            * line -- it's going to be used to set the cursor value when we
2831            * move to the new line.
2832            */
2833           wmtp->lb = p + 1;
2834           wmtp->offset = len;
2835           wmtp->insert = LF_ISSET(TXT_APPENDEOL) ?  tp->insert - 1 : tp->insert;
2836           wmtp->owrite = tp->owrite;
2837 
2838           /* Correct current bookkeeping information. */
2839           tp->cno -= len;
2840           if (LF_ISSET(TXT_APPENDEOL)) {
2841                     tp->len -= len + tp->owrite + (tp->insert - 1);
2842                     tp->insert = 1;
2843           } else {
2844                     tp->len -= len + tp->owrite + tp->insert;
2845                     tp->insert = 0;
2846           }
2847           tp->owrite = 0;
2848 
2849           /*
2850            * !!!
2851            * Delete any trailing whitespace from the current line.
2852            */
2853           for (;; --p, --off) {
2854                     if (!ISBLANK((UCHAR_T)*p))
2855                               break;
2856                     --tp->cno;
2857                     --tp->len;
2858                     if (off == tp->ai || off == tp->offset)
2859                               break;
2860           }
2861           *didbreak = 1;
2862           return (0);
2863 }
2864 
2865 /*
2866  * txt_Rresolve --
2867  *        Resolve the input line for the 'R' command.
2868  */
2869 static void
txt_Rresolve(SCR * sp,TEXTH * tiqh,TEXT * tp,const size_t orig_len)2870 txt_Rresolve(SCR *sp, TEXTH *tiqh, TEXT *tp, const size_t orig_len)
2871 {
2872           TEXT *ttp;
2873           size_t input_len, retain;
2874           CHAR_T *p;
2875 
2876           /*
2877            * Check to make sure that the cursor hasn't moved beyond
2878            * the end of the line.
2879            */
2880           if (tp->owrite == 0)
2881                     return;
2882 
2883           /*
2884            * Calculate how many characters the user has entered,
2885            * plus the blanks erased by <carriage-return>/<newline>s.
2886            */
2887           for (ttp = TAILQ_FIRST(tiqh), input_len = 0;;) {
2888                     input_len += ttp == tp ? tp->cno : ttp->len + ttp->R_erase;
2889                     if ((ttp = TAILQ_NEXT(ttp, q)) == NULL)
2890                               break;
2891           }
2892 
2893           /*
2894            * If the user has entered less characters than the original line
2895            * was long, restore any overwriteable characters to the original
2896            * characters.  These characters are entered as "insert characters",
2897            * because they're after the cursor and we don't want to lose them.
2898            * (This is okay because the R command has no insert characters.)
2899            * We set owrite to 0 so that the insert characters don't get copied
2900            * to somewhere else, which means that the line and the length have
2901            * to be adjusted here as well.
2902            *
2903            * We have to retrieve the original line because the original pinned
2904            * page has long since been discarded.  If it doesn't exist, that's
2905            * okay, the user just extended the file.
2906            */
2907           if (input_len < orig_len) {
2908                     retain = MIN(tp->owrite, orig_len - input_len);
2909                     if (db_get(sp,
2910                         TAILQ_FIRST(tiqh)->lno, DBG_FATAL | DBG_NOCACHE, &p, NULL))
2911                               return;
2912                     MEMCPYW(tp->lb + tp->cno, p + input_len, retain);
2913                     tp->len -= tp->owrite - retain;
2914                     tp->owrite = 0;
2915                     tp->insert += retain;
2916           }
2917 }
2918 
2919 /*
2920  * txt_nomorech --
2921  *        No more characters message.
2922  */
2923 static void
txt_nomorech(SCR * sp)2924 txt_nomorech(SCR *sp)
2925 {
2926           msgq(sp, M_BERR, "194|No more characters to erase");
2927 }
2928