home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / World_Of_Computer_Software-02-385-Vol-1of3.iso / v / vim_src.zip / NORMAL.C < prev    next >
C/C++ Source or Header  |  1993-01-12  |  30KB  |  1,612 lines

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