home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Spezial / SPEZIAL2_97.zip / SPEZIAL2_97.iso / ANWEND / EDITOR / NVI179B / NVI179B.ZIP / vi / v_txt.c < prev    next >
C/C++ Source or Header  |  1996-10-13  |  80KB  |  2,951 lines

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