home *** CD-ROM | disk | FTP | other *** search
/ Fresh Fish 5 / FreshFish_July-August1994.bin / bbs / util / vim-2.0.lha / Vim-2.0 / src / normal.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-12-15  |  31.4 KB  |  1,645 lines

  1. /* vi:ts=4:sw=4
  2.  *
  3.  * VIM - Vi IMproved
  4.  *
  5.  * Code Contributions By:    Bram Moolenaar            mool@oce.nl
  6.  *                            Tim Thompson            twitch!tjt
  7.  *                            Tony Andrews            onecom!wldrdg!tony 
  8.  *                            G. R. (Fred) Walter        watmath!watcgl!grwalter 
  9.  */
  10.  
  11. /*
  12.  * Contains the main routine for processing characters in command mode.
  13.  * Communicates closely with the code in ops.c to handle the operators.
  14.  */
  15.  
  16. #include "vim.h"
  17. #include "globals.h"
  18. #include "proto.h"
  19. #include "param.h"
  20.  
  21. #undef EXTERN
  22. #undef INIT
  23. #define EXTERN
  24. #define INIT(x) x
  25. #include "ops.h"
  26.  
  27. /*
  28.  * Generally speaking, every command in normal() should either clear any
  29.  * pending operator (with CLEAROP), or set the motion type variable.
  30.  */
  31.  
  32. #define CLEAROP (operator = NOP)        /* clear any pending operator */
  33. #define CLEAROPBEEP     clearopbeep()    /* CLEAROP plus a beep() */
  34. #define CHECKCLEAROP    if (checkclearop()) break;
  35. #define CHECKCLEAROPQ    if (checkclearopq()) break;
  36.  
  37. /*
  38.  * If a count is given before the operator, it is saved in opnum.
  39.  */
  40. static linenr_t    opnum = 0;
  41. static linenr_t    Prenum;         /* The (optional) number before a command. */
  42. int                redo_Visual_busy = FALSE;    /* TRUE when redo-ing a visual */
  43.  
  44. static void        prep_redo __ARGS((long, int, int, int));
  45. static int        checkclearop __ARGS((void));
  46. static int        checkclearopq __ARGS((void));
  47. static void        clearopbeep __ARGS((void));
  48. static void        premsg __ARGS((int, int));
  49.  
  50. extern int        restart_edit;    /* this is in edit.c */
  51.  
  52. /*
  53.  * normal
  54.  *
  55.  * Execute a command in normal mode.
  56.  *
  57.  * This is basically a big switch with the cases arranged in rough categories
  58.  * in the following order:
  59.  *
  60.  *      0. Macros (q, @)
  61.  *      1. Screen positioning commands (^U, ^D, ^F, ^B, ^E, ^Y, z)
  62.  *      2. Control commands (:, <help>, ^L, ^G, ^^, ZZ, *, ^], ^T)
  63.  *      3. Cursor motions (G, H, M, L, l, K_RARROW,  , h, K_LARROW, ^H, k, K_UARROW, ^P, +, CR, LF, j, K_DARROW, ^N, _, |, B, b, W, w, E, e, $, ^, 0)
  64.  *      4. Searches (?, /, n, N, T, t, F, f, ,, ;, ], [, %, (, ), {, })
  65.  *      5. Edits (., u, K_UNDO, ^R, U, r, J, p, P, ^A, ^S)
  66.  *      6. Inserts (A, a, I, i, o, O, R)
  67.  *      7. Operators (~, d, c, y, >, <, !, =, Q)
  68.  *      8. Abbreviations (x, X, D, C, s, S, Y, &)
  69.  *      9. Marks (m, ', `, ^O, ^I)
  70.  *     10. Buffer setting (")
  71.  *     11. Visual (v, V, ^V)
  72.  *   12. Suspend (^Z)
  73.  */
  74.  
  75.     void
  76. normal()
  77. {
  78.     register u_char    c;
  79.     long             n;
  80.     int                flag = FALSE;
  81.     int             type = 0;        /* used in some operations to modify type */
  82.     int             dir = FORWARD;    /* search direction */
  83.     u_char            nchar = NUL;
  84.     int                finish_op;
  85.     linenr_t        Prenum1;
  86.     char            searchbuff[CMDBUFFSIZE];        /* buffer for search string */
  87.     FPOS            *pos;
  88.     register char    *ptr;
  89.     int                command_busy = FALSE;
  90.     static int        didwarn = FALSE;        /* warned for broken inversion */
  91.  
  92.         /* the visual area is remembered for reselection */
  93.     static linenr_t    resel_Visual_nlines;        /* number of lines */
  94.     static int        resel_Visual_type = 0;    /* type 'v', 'V' or CTRL-V */
  95.     static colnr_t    resel_Visual_col;        /* number of columns or end column */
  96.         /* the visual area is remembered for redo */
  97.     static linenr_t    redo_Visual_nlines;        /* number of lines */
  98.     static int        redo_Visual_type = 0;    /* type 'v', 'V' or CTRL-V */
  99.     static colnr_t    redo_Visual_col;            /* number of columns or end column */
  100.  
  101.     Prenum = 0;
  102.     /*
  103.      * If there is an operator pending, then the command we take this time
  104.      * will terminate it. Finish_op tells us to finish the operation before
  105.      * returning this time (unless the operation was cancelled).
  106.      */
  107.     finish_op = (operator != NOP);
  108.  
  109.     if (!finish_op && !yankbuffer)
  110.         opnum = 0;
  111.  
  112.     if (p_sc && (vpeekc() == NUL || KeyTyped == TRUE))
  113.         premsg(NUL, NUL);
  114.     State = NORMAL_BUSY;
  115.     c = vgetc();
  116.  
  117.     /* Pick up any leading digits and compute 'Prenum' */
  118.     while ((c >= '1' && c <= '9') || (Prenum != 0 && (c == DEL || c == '0')))
  119.     {
  120.         if (c == DEL)
  121.                 Prenum /= 10;
  122.         else
  123.                 Prenum = Prenum * 10 + (c - '0');
  124.         if (Prenum < 0)            /* got too large! */
  125.             Prenum = 999999999;
  126.         premsg(' ', NUL);
  127.         c = vgetc();
  128.     }
  129.  
  130.     /*
  131.      * If we're in the middle of an operator (including after entering a yank
  132.      * buffer with ") AND we had a count before the
  133.      * operator, then that count overrides the current value of Prenum. What
  134.      * this means effectively, is that commands like "3dw" get turned into
  135.      * "d3w" which makes things fall into place pretty neatly.
  136.      * If you give a count before AND after the operator, they are multiplied.
  137.      */
  138.     if (opnum != 0)
  139.     {
  140.             if (Prenum)
  141.                 Prenum *= opnum;
  142.             else
  143.                 Prenum = opnum;
  144.             opnum = 0;
  145.     }
  146.  
  147.     Prenum1 = (Prenum == 0 ? 1 : Prenum);        /* Prenum often defaults to 1 */
  148.     premsg(c, NUL);
  149.  
  150.     /*
  151.      * get an additional character if we need one
  152.      */
  153.     if (strchr("@zZtTfF[]m'`\"", c) || (c == 'q' && !Recording && !Exec_reg) ||
  154.                                         (c == 'r' && !Visual.lnum))
  155.     {
  156.         State = NOMAPPING;
  157.         nchar = vgetc();        /* no macro mapping for this char */
  158.         premsg(c, nchar);
  159.     }
  160.     if (p_sc)
  161.         flushbuf();        /* flush the premsg() characters onto the screen so we can
  162.                             see them while the command is being executed */
  163.  
  164.     if (c != 'z')    /* the 'z' command gets another character */
  165.     {
  166.         State = NORMAL;
  167.         script_winsize_pp();
  168.     }
  169.     if (nchar == ESC)
  170.     {
  171.         CLEAROP;
  172.         goto normal_end;
  173.     }
  174.     switch (c)
  175.     {
  176.  
  177. /*
  178.  * 0: Macros
  179.  */
  180.       case 'q':         /* (stop) recording into a named register */
  181.         CHECKCLEAROP;
  182.                         /* command is ignored while executing a register */
  183.         if (!Exec_reg && !dorecord(nchar))
  184.             CLEAROPBEEP;
  185.         break;
  186.  
  187.      case '@':            /* execute a named buffer */
  188.         CHECKCLEAROP;
  189.         while (Prenum1--)
  190.             if (!doexecbuf(nchar))
  191.             {
  192.                 CLEAROPBEEP;
  193.                 break;
  194.             }
  195.         break;
  196.  
  197. /*
  198.  * 1: Screen positioning commands
  199.  */
  200.       case Ctrl('D'):
  201.         flag = TRUE;
  202.  
  203.       case Ctrl('U'):
  204.         CHECKCLEAROP;
  205.         if (Prenum)
  206.             p_scroll = (Prenum > Rows - 1) ? Rows - 1 : Prenum;
  207.         n = (p_scroll < Rows) ? p_scroll : Rows - 1;
  208.         if (flag)
  209.         {
  210.                 Topline += n;
  211.                 if (Topline > line_count)
  212.                     Topline = line_count;
  213.                 comp_Botline();        /* compute Botline */
  214.                 onedown(n);
  215.         }
  216.         else
  217.         {
  218.                 if (n >= Curpos.lnum)
  219.                     n = Curpos.lnum - 1;
  220.                 Prenum1 = Curpos.lnum - n;
  221.                 scrolldown(n);
  222.                 if (Prenum1 < Curpos.lnum)
  223.                     Curpos.lnum = Prenum1;
  224.         }
  225.         beginline(TRUE);
  226.         updateScreen(VALID);
  227.         break;
  228.  
  229.       case Ctrl('B'):
  230.       case K_SUARROW:
  231.         dir = BACKWARD;
  232.  
  233.       case Ctrl('F'):
  234.       case K_SDARROW:
  235.         CHECKCLEAROP;
  236.         onepage(dir, Prenum1);
  237.         break;
  238.  
  239.       case Ctrl('E'):
  240.         CHECKCLEAROP;
  241.         scrollup(Prenum1);
  242.         updateScreen(VALID);
  243.         break;
  244.  
  245.       case Ctrl('Y'):
  246.         CHECKCLEAROP;
  247.         scrolldown(Prenum1);
  248.         updateScreen(VALID);
  249.         break;
  250.  
  251.       case 'z':
  252.         CHECKCLEAROP;
  253.         if (isdigit(nchar))
  254.         {
  255.             /*
  256.              * we misuse some variables to be able to call premsg()
  257.              */
  258.             operator = c;
  259.             opnum = Prenum;
  260.             Prenum = nchar - '0';
  261.             for (;;)
  262.             {
  263.                 premsg(' ', NUL);
  264.                 nchar = vgetc();
  265.                 State = NORMAL;
  266.                 script_winsize_pp();
  267.                 if (nchar == DEL)
  268.                     Prenum /= 10;
  269.                 else if (isdigit(nchar))
  270.                     Prenum = Prenum * 10 + (nchar - '0');
  271.                 else if (nchar == CR)
  272.                 {
  273.                     set_winheight((int)Prenum);
  274.                     break;
  275.                 }
  276.                 else
  277.                 {
  278.                     CLEAROPBEEP;
  279.                     break;
  280.                 }
  281.             }
  282.             operator = NOP;
  283.             break;
  284.         }
  285.  
  286.         if (Prenum)        /* line number given */
  287.         {
  288.             setpcmark();
  289.             if (Prenum > line_count)
  290.                 Curpos.lnum = line_count;
  291.             else
  292.                 Curpos.lnum = Prenum;
  293.         }
  294.         State = NORMAL;
  295.         script_winsize_pp();
  296.         switch (nchar)
  297.         {
  298.           case NL:                /* put Curpos at top of screen */
  299.           case CR:
  300.             Topline = Curpos.lnum;
  301.             updateScreen(VALID);
  302.             break;
  303.  
  304.           case '.':             /* put Curpos in middle of screen */
  305.             n = (Rows + plines(Curpos.lnum)) / 2;
  306.             goto dozcmd;
  307.  
  308.           case '-':             /* put Curpos at bottom of screen */
  309.             n = Rows - 1;
  310.             /* FALLTHROUGH */
  311.  
  312.     dozcmd:
  313.             {
  314.                 register linenr_t    lp = Curpos.lnum;
  315.                 register long        l = plines(lp);
  316.  
  317.                 do
  318.                 {
  319.                     Topline = lp;
  320.                     if (--lp == 0)
  321.                         break;
  322.                     l += plines(lp);
  323.                 } while (l <= n);
  324.             }
  325.             updateScreen(VALID);
  326.             beginline(TRUE);
  327.             break;
  328.  
  329.           default:
  330.             CLEAROPBEEP;
  331.         }
  332.         break;
  333.  
  334. /*
  335.  *      2: Control commands
  336.  */
  337.       case ':':
  338.         if (Visual.lnum)
  339.             goto dooperator;
  340.         CHECKCLEAROP;
  341.         docmdline(NULL);
  342.         break;
  343.  
  344.       case K_HELP:
  345.         CHECKCLEAROP;
  346.         help();
  347.         break;
  348.  
  349.       case Ctrl('L'):
  350.         CHECKCLEAROP;
  351.         updateScreen(CLEAR);
  352.         break;
  353.  
  354.       case Ctrl('G'):
  355.         CHECKCLEAROP;
  356.         fileinfo(did_cd || Prenum);    /* print full name if count given or :cd used */
  357.         break;
  358.  
  359.       case K_CCIRCM:            /* shorthand command */
  360.         CHECKCLEAROPQ;
  361.         getaltfile((int)Prenum, (linenr_t)0, TRUE);
  362.         break;
  363.  
  364.       case 'Z':         /* write, if changed, and exit */
  365.         CHECKCLEAROPQ;
  366.         if (nchar != 'Z')
  367.         {
  368.             CLEAROPBEEP;
  369.             break;
  370.         }
  371.         stuffReadbuff(":x\n");
  372.         break;
  373.  
  374.       case Ctrl(']'):            /* :ta to current identifier */
  375.         CHECKCLEAROPQ;
  376.       case '*':                 /* / to current identifier */
  377.       case '#':                 /* ? to current identifier */
  378.       case 'K':                    /* run program for current identifier */
  379.         {
  380.             register int     col;
  381.  
  382.             ptr = nr2ptr(Curpos.lnum);
  383.             col = Curpos.col;
  384.  
  385.             /*
  386.              * skip to start of identifier.
  387.              */
  388.             while (ptr[col] != NUL && !isidchar(ptr[col]))
  389.                 ++col;
  390.  
  391.             /*
  392.              * Back up to start of identifier. This doesn't match the
  393.              * real vi but I like it a little better and it shouldn't bother
  394.              * anyone.
  395.              */
  396.             while (col > 0 && isidchar(ptr[col - 1]))
  397.                 --col;
  398.  
  399.             if (!isidchar(ptr[col]))
  400.             {
  401.                 CLEAROPBEEP;
  402.                 break;
  403.             }
  404.  
  405.             if (Prenum)
  406.                 stuffnumReadbuff(Prenum);
  407.             switch (c)
  408.             {
  409.                 case '*':
  410.                     stuffReadbuff("/");
  411.                     break;
  412.                 case '#':
  413.                     stuffReadbuff("?");
  414.                     break;
  415.                 case 'K':
  416.                     stuffReadbuff(":! ");
  417.                     stuffReadbuff(p_kp);
  418.                     stuffReadbuff(" ");
  419.                     break;
  420.                 default:
  421.                     stuffReadbuff(":ta ");
  422.             }
  423.  
  424.             /*
  425.              * Now grab the chars in the identifier
  426.              */
  427.             while (isidchar(ptr[col]))
  428.             {
  429.                 stuffcharReadbuff(ptr[col]);
  430.                 ++col;
  431.             }
  432.             stuffReadbuff("\n");
  433.         }
  434.         break;
  435.  
  436.       case Ctrl('T'):        /* backwards in tag stack */
  437.             CHECKCLEAROPQ;
  438.               dotag("", 2, (int)Prenum1);
  439.             break;
  440.  
  441. /*
  442.  * Cursor motions
  443.  */
  444.       case 'G':
  445.         mtype = MLINE;
  446.         setpcmark();
  447.         if (Prenum == 0 || Prenum > line_count)
  448.                 Curpos.lnum = line_count;
  449.         else
  450.                 Curpos.lnum = Prenum;
  451.         beginline(TRUE);
  452.         break;
  453.  
  454.       case 'H':
  455.       case 'M':
  456.         if (c == 'M')
  457.                 n = (Rows - emptyrows - 1) / 2;
  458.         else
  459.                 n = Prenum;
  460.         mtype = MLINE;
  461.         setpcmark();
  462.         Curpos.lnum = Topline;
  463.         while (n && onedown((long)1))
  464.                 --n;
  465.         beginline(TRUE);
  466.         break;
  467.  
  468.       case 'L':
  469.         mtype = MLINE;
  470.         setpcmark();
  471.         Curpos.lnum = Botline - 1;
  472.         for (n = Prenum; n && oneup((long)1); n--)
  473.                 ;
  474.         beginline(TRUE);
  475.         break;
  476.  
  477.       case 'l':
  478.       case K_RARROW:
  479.       case ' ':
  480.         mtype = MCHAR;
  481.         mincl = FALSE;
  482.         n = Prenum1;
  483.         while (n--)
  484.         {
  485.             if (!oneright())
  486.             {
  487.                 if (operator == NOP)
  488.                     beep();
  489.                 else
  490.                 {
  491.                     if (lineempty(Curpos.lnum))
  492.                         CLEAROPBEEP;
  493.                     else
  494.                     {
  495.                         mincl = TRUE;
  496.                         if (n)
  497.                             beep();
  498.                     }
  499.                 }
  500.                 break;
  501.             }
  502.         }
  503.         set_want_col = TRUE;
  504.         break;
  505.  
  506.       case 'h':
  507.       case K_LARROW:
  508.       case Ctrl('H'):
  509.       case DEL:
  510.         mtype = MCHAR;
  511.         mincl = FALSE;
  512.         n = Prenum1;
  513.         while (n--)
  514.         {
  515.             if (!oneleft())
  516.             {
  517.                 if (operator != DELETE && operator != CHANGE)
  518.                     beep();
  519.                 else if (Prenum1 == 1)
  520.                     CLEAROPBEEP;
  521.                 break;
  522.             }
  523.         }
  524.         set_want_col = TRUE;
  525.         break;
  526.  
  527.       case '-':
  528.         flag = TRUE;
  529.         /* FALLTHROUGH */
  530.  
  531.       case 'k':
  532.       case K_UARROW:
  533.       case Ctrl('P'):
  534.         mtype = MLINE;
  535.         if (!oneup(Prenum1))
  536.             CLEAROPBEEP;
  537.         else if (flag)
  538.             beginline(TRUE);
  539.         break;
  540.  
  541.       case '+':
  542.       case CR:
  543.         flag = TRUE;
  544.         /* FALLTHROUGH */
  545.  
  546.       case 'j':
  547.       case K_DARROW:
  548.       case Ctrl('N'):
  549.       case NL:
  550.         mtype = MLINE;
  551.         if (!onedown(Prenum1))
  552.             CLEAROPBEEP;
  553.         else if (flag)
  554.             beginline(TRUE);
  555.         break;
  556.  
  557.         /*
  558.          * This is a strange motion command that helps make operators more
  559.          * logical. It is actually implemented, but not documented in the
  560.          * real 'vi'. This motion command actually refers to "the current
  561.          * line". Commands like "dd" and "yy" are really an alternate form of
  562.          * "d_" and "y_". It does accept a count, so "d3_" works to delete 3
  563.          * lines.
  564.          */
  565.       case '_':
  566. lineop:
  567.         if (operator == CHANGE && p_ai)        /* do not delete the indent */
  568.         {
  569.             beginline(TRUE);
  570.             startop = Curpos;
  571.             mtype = MCHAR;
  572.             mincl = TRUE;
  573.         }
  574.         else
  575.             mtype = MLINE;
  576.         if (!onedown((long)(Prenum1 - 1)))
  577.             CLEAROPBEEP;
  578.         else if (mtype == MCHAR)    /* 'c' with autoindent */
  579.         {
  580.             Curpos.col = MAXCOL;    /* put cursor on last char in line */
  581.             adjustCurpos();
  582.         }
  583.         else if (operator != YANK)    /* 'Y' does not move cursor */
  584.             beginline(TRUE);
  585.         break;
  586.  
  587.       case '|':
  588.         mtype = MCHAR;
  589.         mincl = TRUE;
  590.         beginline(FALSE);
  591.         if (Prenum > 0)
  592.             coladvance((colnr_t)(Prenum - 1));
  593.         Curswant = (colnr_t)(Prenum - 1);
  594.         break;
  595.  
  596.         /*
  597.          * Word Motions
  598.          */
  599.  
  600.       case 'B':
  601.         type = 1;
  602.         /* FALLTHROUGH */
  603.  
  604.       case 'b':
  605.       case K_SLARROW:
  606.         mtype = MCHAR;
  607.         mincl = FALSE;
  608.         set_want_col = TRUE;
  609.         if (bck_word(Prenum1, type))
  610.             CLEAROPBEEP;
  611.         break;
  612.  
  613.       case 'E':
  614.         type = 1;
  615.         /* FALLTHROUGH */
  616.  
  617.       case 'e':
  618.         mincl = TRUE;
  619.         goto dowrdcmd;
  620.  
  621.       case 'W':
  622.         type = 1;
  623.         /* FALLTHROUGH */
  624.  
  625.       case 'w':
  626.       case K_SRARROW:
  627.         mincl = FALSE;
  628.         flag = TRUE;
  629.         /*
  630.          * This is a little strange. To match what the real vi does, we
  631.          * effectively map 'cw' to 'ce', and 'cW' to 'cE', provided that we are
  632.          * not on a space or a TAB. This seems
  633.          * impolite at first, but it's really more what we mean when we say
  634.          * 'cw'.
  635.          */
  636.         if (operator == CHANGE && (n = gcharCurpos()) != ' ' && n != TAB &&
  637.                                                                 n != NUL)
  638.         {
  639.             mincl = TRUE;
  640.             flag = FALSE;
  641.         }
  642.  
  643. dowrdcmd:
  644.         mtype = MCHAR;
  645.         set_want_col = TRUE;
  646.         if (flag)
  647.             n = fwd_word(Prenum1, type, operator != NOP);
  648.         else
  649.             n = end_word(Prenum1, type, operator == CHANGE);
  650.         if (n)
  651.         {
  652.             CLEAROPBEEP;
  653.             break;
  654.         }
  655. #if 0
  656.         /*
  657.          * If we do a 'dw' for the last word in a line, we only delete the rest
  658.          * of the line, not joining the two lines, unless the current line is empty.
  659.          */
  660.         if (operator == DELETE && Prenum1 == 1 && startop.lnum != Curpos.lnum &&
  661.             !lineempty(startop.lnum))
  662.         {
  663.                 Curpos = startop;
  664.                 while (oneright())
  665.                     ;
  666.                 mincl = TRUE;
  667.         }
  668. #endif
  669.         break;
  670.  
  671.       case '$':
  672.         mtype = MCHAR;
  673.         mincl = TRUE;
  674.         Curswant = MAXCOL;                /* so we stay at the end */
  675.         if (!onedown((long)(Prenum1 - 1)))
  676.         {
  677.             CLEAROPBEEP;
  678.             break;
  679.         }
  680.         break;
  681.  
  682.       case '^':
  683.         flag = TRUE;
  684.         /* FALLTHROUGH */
  685.  
  686.       case '0':
  687.         mtype = MCHAR;
  688.         mincl = FALSE;
  689.         beginline(flag);
  690.         break;
  691.  
  692. /*
  693.  * 4: Searches
  694.  */
  695.       case '?':
  696.       case '/':
  697.         if (!getcmdline(c, (u_char *)searchbuff))
  698.         {
  699.             CLEAROP;
  700.             break;
  701.         }
  702.         mtype = MCHAR;
  703.         mincl = FALSE;
  704.         set_want_col = TRUE;
  705.  
  706.         n = dosearch(c, searchbuff, FALSE, Prenum1, TRUE);
  707.         if (n == 0)
  708.             CLEAROP;
  709.         else if (n == 2)
  710.             mtype = MLINE;
  711.         break;
  712.  
  713.       case 'N':
  714.         flag = 1;
  715.  
  716.       case 'n':
  717.         mtype = MCHAR;
  718.         mincl = FALSE;
  719.         set_want_col = TRUE;
  720.         if (!dosearch(0, NULL, flag, Prenum1, TRUE))
  721.             CLEAROP;
  722.         break;
  723.  
  724.         /*
  725.          * Character searches
  726.          */
  727.       case 'T':
  728.         dir = BACKWARD;
  729.         /* FALLTHROUGH */
  730.  
  731.       case 't':
  732.         type = 1;
  733.         goto docsearch;
  734.  
  735.       case 'F':
  736.         dir = BACKWARD;
  737.         /* FALLTHROUGH */
  738.  
  739.       case 'f':
  740. docsearch:
  741.         mtype = MCHAR;
  742.         mincl = TRUE;
  743.         set_want_col = TRUE;
  744.         if (!searchc(nchar, dir, type, Prenum1))
  745.             CLEAROPBEEP;
  746.         break;
  747.  
  748.       case ',':
  749.         flag = 1;
  750.         /* FALLTHROUGH */
  751.  
  752.       case ';':
  753.         dir = flag;
  754.         goto docsearch;        /* nchar == NUL, thus repeat previous search */
  755.  
  756.         /*
  757.          * section or C function searches
  758.          */
  759.  
  760.       case '[':
  761.         dir = BACKWARD;
  762.         /* FALLTHROUGH */
  763.  
  764.       case ']':
  765.         mtype = MLINE;
  766.         set_want_col = TRUE;
  767.         flag = '{';
  768.         if (nchar != c)
  769.         {
  770.             if (nchar == '[' || nchar == ']')
  771.                 flag = '}';
  772.             else
  773.             {
  774.                 CLEAROPBEEP;
  775.                 break;
  776.             }
  777.         }
  778.         if (dir == FORWARD && operator != NOP)    /* e.g. y]] searches for '}' */
  779.             flag = '}';
  780.         if (!findpar(dir, Prenum1, flag))
  781.         {
  782.             CLEAROPBEEP;
  783.         }
  784.         break;
  785.  
  786.       case '%':
  787.         mincl = TRUE;
  788.         if (Prenum)        /* {cnt}% : goto {cnt} percentage in file */
  789.         {
  790.             if (Prenum > 100)
  791.                 CLEAROPBEEP;
  792.             else
  793.             {
  794.                 mtype = MLINE;
  795.                 setpcmark();
  796.                         /* round up, so CTRL-G will give same value */
  797.                 Curpos.lnum = (line_count * Prenum + 99) / 100;
  798.                 Curpos.col = 0;
  799.             }
  800.         }
  801.         else            /* % : go to matching paren */
  802.         {
  803.             mtype = MCHAR;
  804.             if ((pos = showmatch()) == NULL)
  805.                 CLEAROPBEEP;
  806.             else
  807.             {
  808.                 setpcmark();
  809.                 Curpos = *pos;
  810.                 set_want_col = TRUE;
  811.             }
  812.         }
  813.         break;
  814.  
  815.       case '(':
  816.         dir = BACKWARD;
  817.         /* FALLTHROUGH */
  818.  
  819.       case ')':
  820.         mtype = MCHAR;
  821.         if (c == ')')
  822.             mincl = FALSE;
  823.         else
  824.             mincl = TRUE;
  825.         set_want_col = TRUE;
  826.  
  827.         if (!findsent(dir, Prenum1))
  828.             CLEAROPBEEP;
  829.         break;
  830.  
  831.       case '{':
  832.         dir = BACKWARD;
  833.         /* FALLTHROUGH */
  834.  
  835.       case '}':
  836.         mtype = MCHAR;
  837.         mincl = FALSE;
  838.         set_want_col = TRUE;
  839.         if (!findpar(dir, Prenum1, NUL))
  840.             CLEAROPBEEP;
  841.         break;
  842.  
  843. /*
  844.  * 5: Edits
  845.  */
  846.       case '.':
  847.         CHECKCLEAROPQ;
  848.         if (!start_redo(Prenum))
  849.             CLEAROPBEEP;
  850.         break;
  851.  
  852.       case 'u':
  853.         if (Visual.lnum)
  854.             goto dooperator;
  855.       case K_UNDO:
  856.         CHECKCLEAROPQ;
  857.         u_undo((int)Prenum1);
  858.         set_want_col = TRUE;
  859.         break;
  860.  
  861.       case Ctrl('R'):
  862.         CHECKCLEAROPQ;
  863.           u_redo((int)Prenum1);
  864.         set_want_col = TRUE;
  865.         break;
  866.  
  867.       case 'U':
  868.         if (Visual.lnum)
  869.             goto dooperator;
  870.         CHECKCLEAROPQ;
  871.         u_undoline();
  872.         set_want_col = TRUE;
  873.         break;
  874.  
  875.       case 'r':
  876.         if (Visual.lnum)
  877.         {
  878.             c = 'c';
  879.             goto dooperator;
  880.         }
  881.         CHECKCLEAROPQ;
  882.         ptr = nr2ptr(Curpos.lnum) + Curpos.col;
  883.         if (strlen(ptr) < (unsigned)Prenum1)    /* not enough characters to replace */
  884.         {
  885.             CLEAROPBEEP;
  886.             break;
  887.         }
  888.         /*
  889.          * Replacing with a line break or tab is done by edit(), because it
  890.          * is complicated.
  891.          * Other characters are done below to avoid problems with things like
  892.          * CTRL-V 048 (for edit() this would be R CTRL-V 0 ESC).
  893.          */
  894.         if (nchar == '\r' || nchar == '\n' || nchar == '\t')
  895.         {
  896.             prep_redo(Prenum1, 'r', nchar, NUL);
  897.             stuffnumReadbuff(Prenum1);
  898.             stuffcharReadbuff('R');
  899.             stuffcharReadbuff(nchar);
  900.             stuffcharReadbuff(ESC);
  901.             break;
  902.         }
  903.  
  904.         if (nchar == Ctrl('V'))                /* get another character */
  905.         {
  906.             c = Ctrl('V');
  907.             nchar = get_literal(&type);
  908.             if (type)                        /* typeahead */
  909.                 stuffcharReadbuff(type);
  910.         }
  911.         else
  912.             c = NUL;
  913.         prep_redo(Prenum1, 'r', c, nchar);
  914.         if (!u_saveCurpos())                /* save line for undo */
  915.             break;
  916.         Curpos.col += Prenum1 - 1;
  917.         while (Prenum1--)                    /* replace the characters */
  918.             *ptr++ = nchar;
  919.         set_want_col = TRUE;
  920.         CHANGED;
  921.         updateline();
  922.         break;
  923.  
  924.       case 'J':
  925.         if (Visual.lnum)        /* join the visual lines */
  926.         {
  927.             if (Curpos.lnum > Visual.lnum)
  928.             {
  929.                 Prenum = Curpos.lnum - Visual.lnum + 1;
  930.                 Curpos.lnum = Visual.lnum;
  931.             }
  932.             else
  933.                 Prenum = Visual.lnum - Curpos.lnum + 1;
  934.             Visual.lnum = 0;
  935.         }
  936.         CHECKCLEAROP;
  937.         if (Prenum <= 1)
  938.             Prenum = 2;             /* default for join is two lines! */
  939.         if (Curpos.lnum + Prenum - 1 > line_count)    /* beyond last line */
  940.         {
  941.             CLEAROPBEEP;
  942.             break;
  943.         }
  944.  
  945.         prep_redo(Prenum, 'J', NUL, NUL);
  946.         dodojoin(Prenum, TRUE, TRUE);
  947.         break;
  948.  
  949.       case 'P':
  950.         dir = BACKWARD;
  951.         /* FALLTHROUGH */
  952.  
  953.       case 'p':
  954.         CHECKCLEAROPQ;
  955.         prep_redo(Prenum, c, NUL, NUL);
  956.         doput(dir, Prenum1);
  957.         break;
  958.  
  959.       case Ctrl('A'):            /* add to number */
  960.       case Ctrl('S'):            /* subtract from number */
  961.         CHECKCLEAROPQ;
  962.         if (doaddsub((int)c, Prenum1))
  963.             prep_redo(Prenum1, c, NUL, NUL);
  964.         break;
  965.  
  966. /*
  967.  * 6: Inserts
  968.  */
  969.       case 'A':
  970.         set_want_col = TRUE;
  971.         while (oneright())
  972.                 ;
  973.         /* FALLTHROUGH */
  974.  
  975.       case 'a':
  976.         CHECKCLEAROPQ;
  977.         /* Works just like an 'i'nsert on the next character. */
  978.         if (u_saveCurpos())
  979.         {
  980.             if (!lineempty(Curpos.lnum))
  981.                 incCurpos();
  982.             startinsert(c, FALSE, Prenum1);
  983.             command_busy = TRUE;
  984.         }
  985.         break;
  986.  
  987.       case 'I':
  988.         beginline(TRUE);
  989.         /* FALLTHROUGH */
  990.  
  991.       case 'i':
  992.         CHECKCLEAROPQ;
  993.         if (u_saveCurpos())
  994.         {
  995.             startinsert(c, FALSE, Prenum1);
  996.             command_busy = TRUE;
  997.         }
  998.         break;
  999.  
  1000.       case 'o':
  1001.           if (Visual.lnum)    /* switch start and end of visual */
  1002.         {
  1003.             Prenum = Visual.lnum;
  1004.             Visual.lnum = Curpos.lnum;
  1005.             Curpos.lnum = Prenum;
  1006.             if (Visual.col != VISUALLINE)
  1007.             {
  1008.                 n = Visual.col;
  1009.                 Visual.col = Curpos.col;
  1010.                 Curpos.col = (int)n;
  1011.                 set_want_col = TRUE;
  1012.             }
  1013.             break;
  1014.         }
  1015.         CHECKCLEAROP;
  1016.         if (u_save(Curpos.lnum, (linenr_t)(Curpos.lnum + 1)) && Opencmd(FORWARD, TRUE, TRUE))
  1017.         {
  1018.             startinsert('o', TRUE, Prenum1);
  1019.             command_busy = TRUE;
  1020.         }
  1021.         break;
  1022.  
  1023.       case 'O':
  1024.         CHECKCLEAROPQ;
  1025.         if (u_save((linenr_t)(Curpos.lnum - 1), Curpos.lnum) && Opencmd(BACKWARD, TRUE, TRUE))
  1026.         {
  1027.             startinsert('O', TRUE, Prenum1);
  1028.             command_busy = TRUE;
  1029.         }
  1030.         break;
  1031.  
  1032.       case 'R':
  1033.         if (Visual.lnum)
  1034.         {
  1035.             c = 'c';
  1036.             Visual.col = VISUALLINE;
  1037.             goto dooperator;
  1038.         }
  1039.         CHECKCLEAROPQ;
  1040.         if (u_saveCurpos())
  1041.         {
  1042.             startinsert('R', FALSE, Prenum1);
  1043.             command_busy = TRUE;
  1044.         }
  1045.         break;
  1046.  
  1047. /*
  1048.  * 7: Operators
  1049.  */
  1050.       case '~':         /* swap case */
  1051.       /*
  1052.        * if tilde is not an operator and Visual is off: swap case
  1053.        * of a single character
  1054.        */
  1055.         if (!p_to && !Visual.lnum)
  1056.         {
  1057.             CHECKCLEAROPQ;
  1058.             if (lineempty(Curpos.lnum))
  1059.             {
  1060.                 CLEAROPBEEP;
  1061.                 break;
  1062.             }
  1063.             prep_redo(Prenum, '~', NUL, NUL);
  1064.  
  1065.             if (!u_saveCurpos())
  1066.                 break;
  1067.  
  1068.             for (; Prenum1 > 0; --Prenum1)
  1069.             {
  1070.                 if (gcharCurpos() == NUL)
  1071.                     break;
  1072.                 swapchar(&Curpos);
  1073.                 incCurpos();
  1074.             }
  1075.  
  1076.             set_want_col = TRUE;
  1077.             CHANGED;
  1078.             updateline();
  1079.             break;
  1080.         }
  1081.         /*FALLTHROUGH*/
  1082.  
  1083.       case 'd':
  1084.       case 'c':
  1085.       case 'y':
  1086.       case '>':
  1087.       case '<':
  1088.       case '!':
  1089.       case '=':
  1090.       case 'Q':
  1091. dooperator:
  1092.         n = strchr(opchars, c) - opchars + 1;
  1093.         if (n == operator)        /* double operator works on lines */
  1094.             goto lineop;
  1095.         CHECKCLEAROP;
  1096.         if (Prenum != 0)
  1097.             opnum = Prenum;
  1098.         startop = Curpos;
  1099.         operator = (int)n;
  1100.         break;
  1101.  
  1102. /*
  1103.  * 8: Abbreviations
  1104.  */
  1105.  
  1106.      /* when Visual the next commands are operators */
  1107.       case 'S':
  1108.       case 'Y':
  1109.       case 'D':
  1110.       case 'C':
  1111.       case 'x':
  1112.       case 'X':
  1113.       case 's':
  1114.           if (Visual.lnum)
  1115.         {
  1116.             static char trans[] = "ScYyDdCcxdXdsc";
  1117.  
  1118.             if (isupper(c) && !Visual_block)        /* uppercase means linewise */
  1119.                 Visual.col = VISUALLINE;
  1120.             c = *(strchr(trans, c) + 1);
  1121.             goto dooperator;
  1122.         }
  1123.  
  1124.       case '&':
  1125.         CHECKCLEAROPQ;
  1126.         if (Prenum)
  1127.             stuffnumReadbuff(Prenum);
  1128.  
  1129.         if (c == 'Y' && p_ye)
  1130.             c = 'Z';
  1131.         {
  1132.                 static char *(ar[9]) = {"dl", "dh", "d$", "c$", "cl", "cc", "yy", "y$", ":s\r"};
  1133.                 static char *str = "xXDCsSYZ&";
  1134.  
  1135.                 stuffReadbuff(ar[(int)(strchr(str, c) - str)]);
  1136.         }
  1137.         break;
  1138.  
  1139. /*
  1140.  * 9: Marks
  1141.  */
  1142.  
  1143.       case 'm':
  1144.         CHECKCLEAROP;
  1145.         if (!setmark(nchar))
  1146.             CLEAROPBEEP;
  1147.         break;
  1148.  
  1149.       case '\'':
  1150.         flag = TRUE;
  1151.         /* FALLTHROUGH */
  1152.  
  1153.       case '`':
  1154.         pos = getmark(nchar, (operator == NOP));
  1155.         if (pos == (FPOS *)-1)    /* jumped to other file */
  1156.         {
  1157.             if (flag)
  1158.                 beginline(TRUE);
  1159.             break;
  1160.         }
  1161.  
  1162.         if (pos != NULL)
  1163.             setpcmark();
  1164.  
  1165. cursormark:
  1166.         if (pos == NULL)
  1167.             CLEAROPBEEP;
  1168.         else
  1169.         {
  1170.             Curpos = *pos;
  1171.             if (flag)
  1172.                 beginline(TRUE);
  1173.         }
  1174.         mtype = flag ? MLINE : MCHAR;
  1175.         mincl = FALSE;        /* ignored if not MCHAR */
  1176.         set_want_col = TRUE;
  1177.         break;
  1178.  
  1179.     case Ctrl('O'):            /* goto older pcmark */
  1180.         Prenum1 = -Prenum1;
  1181.         /* FALLTHROUGH */
  1182.  
  1183.     case Ctrl('I'):            /* goto newer pcmark */
  1184.         CHECKCLEAROPQ;
  1185.         pos = movemark((int)Prenum1);
  1186.         if (pos == (FPOS *)-1)    /* jump to other file */
  1187.         {
  1188.             set_want_col = TRUE;
  1189.             break;
  1190.         }
  1191.         goto cursormark;
  1192.  
  1193. /*
  1194.  * 10. Buffer setting
  1195.  */
  1196.       case '"':
  1197.         CHECKCLEAROP;
  1198.         if (isalnum(nchar) || nchar == '.' || nchar == '%' || nchar == '"')
  1199.         {
  1200.             yankbuffer = nchar;
  1201.             opnum = Prenum;        /* remember count before '"' */
  1202.         }
  1203.         else
  1204.             CLEAROPBEEP;
  1205.         break;
  1206.  
  1207. /*
  1208.  * 11. Visual
  1209.  */
  1210.        case 'v':
  1211.       case 'V':
  1212.       case Ctrl('V'):
  1213.         CHECKCLEAROP;
  1214.         Visual_block = FALSE;
  1215.  
  1216.             /* stop Visual */
  1217.         if (Visual.lnum)
  1218.         {
  1219.             Visual.lnum = 0;
  1220.             updateScreen(NOT_VALID);        /* delete the inversion */
  1221.         }
  1222.             /* start Visual */
  1223.         else
  1224.         {
  1225.             if (!didwarn && (T_TI == NULL || *T_TI == NUL))    /* cannot invert */
  1226.             {
  1227.                 emsg("Warning: terminal cannot invert");
  1228.                 didwarn = TRUE;
  1229.             }
  1230.             if (Prenum)                        /* use previously selected part */
  1231.             {
  1232.                 if (!resel_Visual_type)        /* there is none */
  1233.                 {
  1234.                     beep();
  1235.                     break;
  1236.                 }
  1237.                 Visual = Curpos;
  1238.                 if (resel_Visual_nlines > 1)
  1239.                     Curpos.lnum += resel_Visual_nlines * Prenum - 1;
  1240.                 switch (resel_Visual_type)
  1241.                 {
  1242.                 case 'V':    Visual.col = VISUALLINE;
  1243.                             break;
  1244.  
  1245.                 case Ctrl('V'):
  1246.                             Visual_block = TRUE;
  1247.                             break;
  1248.  
  1249.                 case 'v':        
  1250.                             if (resel_Visual_nlines <= 1)
  1251.                                 Curpos.col += resel_Visual_col * Prenum - 1;
  1252.                             else
  1253.                                 Curpos.col = resel_Visual_col;
  1254.                             break;
  1255.                 }
  1256.                 if (resel_Visual_col == MAXCOL)
  1257.                 {
  1258.                     Curswant = MAXCOL;
  1259.                     coladvance(MAXCOL);
  1260.                 }
  1261.                 else if (Visual_block)
  1262.                     coladvance((colnr_t)(Cursvcol + resel_Visual_col * Prenum - 1));
  1263.                 updateScreen(NOT_VALID);    /* show the inversion */
  1264.             }
  1265.             else
  1266.             {
  1267.                 Visual = Curpos;
  1268.                 if (c == 'V')                /* linewise */
  1269.                     Visual.col = VISUALLINE;
  1270.                 else if (c == Ctrl('V'))    /* blockwise */
  1271.                     Visual_block = TRUE;
  1272.                 updateline();                /* start the inversion */
  1273.             }
  1274.         }
  1275.         break;
  1276.  
  1277. /*
  1278.  * 12. Suspend
  1279.  */
  1280.  
  1281.      case Ctrl('Z'):
  1282.         CLEAROP;
  1283.         Visual.lnum = 0;                    /* stop Visual */
  1284.         stuffReadbuff(":st\r");                /* with autowrite */
  1285.         break;
  1286.  
  1287. /*
  1288.  * The end
  1289.  */
  1290.       case ESC:
  1291.         if (Visual.lnum)
  1292.         {
  1293.             Visual.lnum = 0;            /* stop Visual */
  1294.             updateScreen(NOT_VALID);
  1295.         }
  1296.  
  1297.       default:                    /* not a known command */
  1298.         CLEAROPBEEP;
  1299.         break;
  1300.  
  1301.     }    /* end of switch on command character */
  1302.  
  1303. /*
  1304.  * if we didn't start or finish an operator, reset yankbuffer, unless we
  1305.  * need it later.
  1306.  */
  1307.     if (!finish_op && !operator && strchr("\"DCYSsXx", c) == NULL)
  1308.         yankbuffer = 0;
  1309.  
  1310.     /*
  1311.      * If an operation is pending, handle it...
  1312.      */
  1313.     if ((Visual.lnum || finish_op) && operator != NOP)
  1314.     {
  1315.         if (operator != YANK && !Visual.lnum)        /* can't redo yank */
  1316.         {
  1317.                 prep_redo(Prenum, opchars[operator - 1], c, nchar);
  1318.                 if (c == '/' || c == '?')        /* was a search */
  1319.                 {
  1320.                         AppendToRedobuff(searchbuff);
  1321.                         AppendToRedobuff(NL_STR);
  1322.                 }
  1323.         }
  1324.  
  1325.         if (redo_Visual_busy)
  1326.         {
  1327.             startop = Curpos;
  1328.             Curpos.lnum += redo_Visual_nlines - 1;
  1329.             switch (redo_Visual_type)
  1330.             {
  1331.             case 'V':    Visual.col = VISUALLINE;
  1332.                         break;
  1333.  
  1334.             case Ctrl('V'):
  1335.                         Visual_block = TRUE;
  1336.                         break;
  1337.  
  1338.             case 'v':        
  1339.                         if (redo_Visual_nlines <= 1)
  1340.                             Curpos.col += redo_Visual_col - 1;
  1341.                         else
  1342.                             Curpos.col = redo_Visual_col;
  1343.                         break;
  1344.             }
  1345.             if (redo_Visual_col == MAXCOL)
  1346.             {
  1347.                 Curswant = MAXCOL;
  1348.                 coladvance(MAXCOL);
  1349.             }
  1350.         }
  1351.         else if (Visual.lnum)
  1352.             startop = Visual;
  1353.  
  1354.         if (lt(startop, Curpos))
  1355.         {
  1356.             endop = Curpos;
  1357.             Curpos = startop;
  1358.         }
  1359.         else
  1360.         {
  1361.             endop = startop;
  1362.             startop = Curpos;
  1363.         }
  1364.         nlines = endop.lnum - startop.lnum + 1;
  1365.  
  1366.         if (Visual.lnum || redo_Visual_busy)
  1367.         {
  1368.             if (Visual_block)                /* block mode */
  1369.             {
  1370.                 startvcol = getvcol(&startop, 2);
  1371.                 n = getvcol(&endop, 2);
  1372.                 if (n < startvcol)
  1373.                     startvcol = (colnr_t)n;
  1374.  
  1375.             /* if '$' was used, get endvcol from longest line */
  1376.                 if (Curswant == MAXCOL)
  1377.                 {
  1378.                     Curpos.col = MAXCOL;
  1379.                     endvcol = 0;
  1380.                     for (Curpos.lnum = startop.lnum; Curpos.lnum <= endop.lnum; ++Curpos.lnum)
  1381.                         if ((n = getvcol(&Curpos, 3)) > endvcol)
  1382.                             endvcol = (colnr_t)n;
  1383.                     Curpos = startop;
  1384.                 }
  1385.                 else if (redo_Visual_busy)
  1386.                     endvcol = startvcol + redo_Visual_col - 1;
  1387.                 else
  1388.                 {
  1389.                     endvcol = getvcol(&startop, 3);
  1390.                     n = getvcol(&endop, 3);
  1391.                     if (n > endvcol)
  1392.                         endvcol = (colnr_t)n;
  1393.                 }
  1394.                 coladvance(startvcol);
  1395.             }
  1396.  
  1397.     /*
  1398.      * prepare to reselect and redo Visual: this is based on the size
  1399.      * of the Visual text
  1400.      */
  1401.             if (Visual_block)
  1402.                 resel_Visual_type = Ctrl('V');
  1403.             else if (Visual.col == VISUALLINE)
  1404.                 resel_Visual_type = 'V';
  1405.             else
  1406.                 resel_Visual_type = 'v';
  1407.             if (Curswant == MAXCOL)
  1408.                 resel_Visual_col = MAXCOL;
  1409.             else if (Visual_block)
  1410.                 resel_Visual_col = endvcol - startvcol + 1;
  1411.             else if (nlines > 1)
  1412.                 resel_Visual_col = endop.col;
  1413.             else
  1414.                 resel_Visual_col = endop.col - startop.col + 1;
  1415.             resel_Visual_nlines = nlines;
  1416.             if (operator != YANK && operator != COLON)    /* can't redo yank and : */
  1417.             {
  1418.                 prep_redo(0L, 'v', opchars[operator - 1], NUL);
  1419.                 redo_Visual_type = resel_Visual_type;
  1420.                 redo_Visual_col = resel_Visual_col;
  1421.                 redo_Visual_nlines = resel_Visual_nlines;
  1422.             }
  1423.  
  1424.             mincl = TRUE;
  1425.             if (Visual.col == VISUALLINE)
  1426.                 mtype = MLINE;
  1427.             else
  1428.                 mtype = MCHAR;
  1429.  
  1430.             redo_Visual_busy = FALSE;
  1431.             /*
  1432.              * Switch Visual off now, so screen updating does
  1433.              * not show inverted text when the screen is redrawn.
  1434.              * With YANK and sometimes with COLON and FILTER there is no screen
  1435.              * redraw, so it is done here to remove the inverted part.
  1436.              */
  1437.             Visual.lnum = 0;
  1438.             if (operator == YANK || operator == COLON || operator == FILTER)
  1439.                 updateScreen(NOT_VALID);
  1440.         }
  1441.  
  1442.         set_want_col = 1;
  1443.  
  1444.             /* no_op is set when start and end are the same */
  1445.         no_op = (mtype == MCHAR && !mincl && equal(startop, endop));
  1446.  
  1447.     /*
  1448.      * If the end of an operator is in column one while mtype is MCHAR and mincl
  1449.      * is FALSE, we put endop after the last character in the previous line.
  1450.      * If startop is on or before the first non-blank in the line, the operator
  1451.      * becomes linewise (strange, but that's the way vi does it).
  1452.      */
  1453.         if (mtype == MCHAR && mincl == FALSE && endop.col == 0 && nlines > 1)
  1454.         {
  1455.             --nlines;
  1456.             --endop.lnum;
  1457.             if (startinmargin())
  1458.                 mtype = MLINE;
  1459.             else
  1460.             {
  1461.                 endop.col = strlen(nr2ptr(endop.lnum));
  1462.                 if (endop.col)
  1463.                 {
  1464.                     --endop.col;
  1465.                     mincl = TRUE;
  1466.                 }
  1467.             }
  1468.         }
  1469.         switch (operator)
  1470.         {
  1471.           case LSHIFT:
  1472.           case RSHIFT:
  1473.             doshift(operator);
  1474.             break;
  1475.  
  1476.           case DELETE:
  1477.             if (!no_op)
  1478.                 dodelete();
  1479.             break;
  1480.  
  1481.           case YANK:
  1482.             if (!no_op)
  1483.                 doyank(FALSE);
  1484.             break;
  1485.  
  1486.           case CHANGE:
  1487.             dochange();
  1488.             command_busy = TRUE;
  1489.             break;
  1490.  
  1491.           case FILTER:
  1492.             bangredo = TRUE;            /* dobang() will put cmd in redo buffer */
  1493.  
  1494.           case INDENT:
  1495.           case COLON:
  1496. dofilter:
  1497.             sprintf(IObuff, ":%ld,%ld", (long)startop.lnum, (long)endop.lnum);
  1498.             stuffReadbuff(IObuff);
  1499.             if (operator != COLON)
  1500.                 stuffReadbuff("!");
  1501.             if (operator == INDENT)
  1502.             {
  1503.                 stuffReadbuff(p_ep);
  1504.                 stuffReadbuff("\n");
  1505.             }
  1506.             else if (operator == FORMAT)
  1507.             {
  1508.                 stuffReadbuff(p_fp);
  1509.                 stuffReadbuff("\n");
  1510.             }
  1511.                 /*    docmdline() does the rest */
  1512.             break;
  1513.  
  1514.           case TILDE:
  1515.           case UPPER:
  1516.           case LOWER:
  1517.             if (!no_op)
  1518.                 dotilde();
  1519.             break;
  1520.  
  1521.           case FORMAT:
  1522.             if (*p_fp != NUL)
  1523.                 goto dofilter;        /* use external command */
  1524.             doformat();                /* use internal function */
  1525.             break;
  1526.  
  1527.           default:
  1528.             CLEAROPBEEP;
  1529.         }
  1530.         operator = NOP;
  1531.         Visual_block = FALSE;
  1532.         yankbuffer = 0;
  1533.     }
  1534.  
  1535. normal_end:
  1536.     premsg(-1, NUL);
  1537.     if (restart_edit && operator == NOP && Visual.lnum == 0 && !command_busy && stuff_empty() && yankbuffer == 0)
  1538.         startinsert(restart_edit, FALSE, 1L);
  1539. }
  1540.  
  1541.     static void
  1542. prep_redo(num, cmd, c, nchar)
  1543.     long     num;
  1544.     int        cmd;
  1545.     int        c;
  1546.     int        nchar;
  1547. {
  1548.     ResetRedobuff();
  1549.     if (yankbuffer != 0)    /* yank from specified buffer */
  1550.     {
  1551.         AppendCharToRedobuff('\"');
  1552.         AppendCharToRedobuff(yankbuffer);
  1553.     }
  1554.     if (num)
  1555.         AppendNumberToRedobuff(num);
  1556.     AppendCharToRedobuff(cmd);
  1557.     if (c != NUL)
  1558.         AppendCharToRedobuff(c);
  1559.     if (nchar != NUL)
  1560.         AppendCharToRedobuff(nchar);
  1561. }
  1562.  
  1563. /*
  1564.  * check for operator active
  1565.  */
  1566.     static int
  1567. checkclearop()
  1568. {
  1569.     if (operator == NOP)
  1570.         return (FALSE);
  1571.     clearopbeep();
  1572.     return (TRUE);
  1573. }
  1574.  
  1575. /*
  1576.  * check for operator or Visual active
  1577.  */
  1578.     static int
  1579. checkclearopq()
  1580. {
  1581.     if (operator == NOP && Visual.lnum == 0)
  1582.         return (FALSE);
  1583.     clearopbeep();
  1584.     return (TRUE);
  1585. }
  1586.  
  1587.     static void
  1588. clearopbeep()
  1589. {
  1590.     CLEAROP;
  1591.     beep();
  1592. }
  1593.  
  1594. /*
  1595.  * display, on the last line of the window, the characters typed before
  1596.  * the last command character, plus 'c1' and 'c2'
  1597.  */
  1598.     static void
  1599. premsg(c1, c2)
  1600.     int c1, c2;
  1601. {
  1602.     char    buf[40];
  1603.     char    *p;
  1604.  
  1605.     if (!p_sc || !(KeyTyped || c1 == -1 || c1 == ' '))
  1606.         return;
  1607.  
  1608.     cursor_off();
  1609.     windgoto((int)Rows - 1, sc_col);
  1610.     if (c1 == -1)
  1611.         outstrn("          ");    /* look in comp_col() for the number of spaces */
  1612.     else
  1613.     {
  1614.         p = buf;
  1615.         if (opnum)
  1616.         {
  1617.             sprintf(p, "%ld", (long)opnum);
  1618.             p = p + strlen(buf);
  1619.         }
  1620.         if (yankbuffer)
  1621.         {
  1622.             *p++ = '"';
  1623.             *p++ = yankbuffer;
  1624.         }
  1625.         if (operator == 'z')
  1626.             *p++ = 'z';
  1627.         else if (operator)
  1628.             *p++ = opchars[operator - 1];
  1629.         if (Prenum)
  1630.         {
  1631.             sprintf(p, "%ld", (long)Prenum);
  1632.             p = p + strlen(p);
  1633.         }
  1634.         *p = NUL;
  1635.         if (c1)
  1636.             strcpy(p, transchar(c1));
  1637.         if (c2)
  1638.             strcat(p, transchar(c2));
  1639.         buf[10] = NUL;            /* truncate at maximal length */
  1640.         outstrn(buf);
  1641.     }
  1642.     setcursor();
  1643.     cursor_on();
  1644. }
  1645.