home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Professional / OS2PRO194.ISO / os2 / editor / stevie / normal.c < prev    next >
C/C++ Source or Header  |  1994-01-31  |  19KB  |  1,025 lines

  1. /* $Header: /nw/tony/src/stevie/src/RCS/normal.c,v 1.25 89/08/06 09:50:25 tony Exp $
  2.  *
  3.  * Contains the main routine for processing characters in command mode.
  4.  * Communicates closely with the code in ops.c to handle the operators.
  5.  */
  6.  
  7. #include "stevie.h"
  8. #include "ops.h"
  9.  
  10. /*
  11.  * Generally speaking, every command in normal() should either clear any
  12.  * pending operator (with CLEAROP), or set the motion type variable.
  13.  */
  14.  
  15. #define    CLEAROP    (operator=NOP)    /* clear any pending operator */
  16.  
  17. int    operator = NOP;        /* current pending operator */
  18. int    mtype;            /* type of the current cursor motion */
  19. bool_t    mincl;            /* true if char motion is inclusive */
  20. LPTR    startop;        /* cursor pos. at start of operator */
  21.  
  22. /*
  23.  * Operators can have counts either before the operator, or between the
  24.  * operator and the following cursor motion as in:
  25.  *
  26.  *    d3w or 3dw
  27.  *
  28.  * If a count is given before the operator, it is saved in opnum. If
  29.  * normal() is called with a pending operator, the count in opnum (if
  30.  * present) overrides any count that came later.
  31.  */
  32. static    int    opnum = 0;
  33.  
  34. #define    DEFAULT1(x)    (((x) == 0) ? 1 : (x))
  35.  
  36. /*
  37.  * normal(c)
  38.  *
  39.  * Execute a command in command mode.
  40.  *
  41.  * This is basically a big switch with the cases arranged in rough categories
  42.  * in the following order:
  43.  *
  44.  *    1. File positioning commands
  45.  *    2. Control commands (e.g. ^G, Z, screen redraw, etc)
  46.  *    3. Character motions
  47.  *    4. Search commands (of various kinds)
  48.  *    5. Edit commands (e.g. J, x, X)
  49.  *    6. Insert commands (e.g. i, o, O, A)
  50.  *    7. Operators
  51.  *    8. Abbreviations (e.g. D, C)
  52.  *    9. Marks
  53.  */
  54. void
  55. normal(c)
  56. register int    c;
  57. {
  58.     register int    n;
  59.     register char    *s;    /* temporary variable for misc. strings */
  60.     bool_t    flag = FALSE;
  61.     int    type = 0;    /* used in some operations to modify type */
  62.     int    dir = FORWARD;    /* search direction */
  63.     int    nchar = NUL;
  64.     bool_t    finish_op;
  65.  
  66.     /*
  67.      * If there is an operator pending, then the command we take
  68.      * this time will terminate it. Finish_op tells us to finish
  69.      * the operation before returning this time (unless the operation
  70.      * was cancelled.
  71.      */
  72.     finish_op = (operator != NOP);
  73.  
  74.     /*
  75.      * If we're in the middle of an operator AND we had a count before
  76.      * the operator, then that count overrides the current value of
  77.      * Prenum. What this means effectively, is that commands like
  78.      * "3dw" get turned into "d3w" which makes things fall into place
  79.      * pretty neatly.
  80.      */
  81.     if (finish_op) {
  82.         if (opnum != 0)
  83.             Prenum = opnum;
  84.     } else
  85.         opnum = 0;
  86.  
  87.     u_lcheck();    /* clear the "line undo" buffer if we've moved */
  88.  
  89.     switch (c & 0xff) {
  90.  
  91.     /*
  92.      * Screen positioning commands
  93.      */
  94.     case CTRL('D'):
  95.         CLEAROP;
  96.         if (Prenum)
  97.             P(P_SS) = (Prenum > Rows-1) ? Rows-1 : Prenum;
  98.         scrollup(P(P_SS));
  99.         onedown(P(P_SS));
  100.         updatescreen();
  101.         break;
  102.  
  103.     case CTRL('U'):
  104.         CLEAROP;
  105.         if (Prenum)
  106.             P(P_SS) = (Prenum > Rows-1) ? Rows-1 : Prenum;
  107.         scrolldown(P(P_SS));
  108.         oneup(P(P_SS));
  109.         updatescreen();
  110.         break;
  111.  
  112.     /*
  113.      * This is kind of a hack. If we're moving by one page, the calls
  114.      * to stuffin() do exactly the right thing in terms of leaving
  115.      * some context, and so on. If a count was given, we don't have
  116.      * to worry about these issues.
  117.      */
  118.     case CTRL('F'):
  119.         CLEAROP;
  120.         n = DEFAULT1(Prenum);
  121.         if (n > 1) {
  122.             if ( ! onedown(Rows * n) )
  123.                 beep();
  124.             cursupdate();
  125.         } else {
  126.             /* screenclear(); */
  127.             stuffin("Lz\nM");
  128.         }
  129.         break;
  130.  
  131.     case CTRL('B'):
  132.         CLEAROP;
  133.         n = DEFAULT1(Prenum);
  134.         if (n > 1) {
  135.             if ( ! oneup(Rows * n) )
  136.                 beep();
  137.             cursupdate();
  138.         } else {
  139.             /* screenclear(); */
  140.             stuffin("Hz-M");
  141.         }
  142.         break;
  143.  
  144.     case CTRL('E'):
  145.         CLEAROP;
  146.         scrollup(DEFAULT1(Prenum));
  147.         updatescreen();
  148.         break;
  149.  
  150.     case CTRL('Y'):
  151.         CLEAROP;
  152.         scrolldown(DEFAULT1(Prenum));
  153.         updatescreen();
  154.         break;
  155.  
  156.     case 'z':
  157.         CLEAROP;
  158.         switch (vgetc()) {
  159.         case NL:        /* put Curschar at top of screen */
  160.         case CR:
  161.             *Topchar = *Curschar;
  162.             Topchar->index = 0;
  163.             updatescreen();
  164.             break;
  165.  
  166.         case '.':        /* put Curschar in middle of screen */
  167.             n = Rows/2;
  168.             goto dozcmd;
  169.  
  170.         case '-':        /* put Curschar at bottom of screen */
  171.             n = Rows-1;
  172.             /* fall through */
  173.  
  174.         dozcmd:
  175.             {
  176.                 register LPTR    *lp = Curschar;
  177.                 register int    l = 0;
  178.  
  179.                 while ((l < n) && (lp != NULL)) {
  180.                     l += plines(lp);
  181.                     *Topchar = *lp;
  182.                     lp = prevline(lp);
  183.                 }
  184.             }
  185.             Topchar->index = 0;
  186.             updatescreen();
  187.             break;
  188.  
  189.         default:
  190.             beep();
  191.         }
  192.         break;
  193.  
  194.     /*
  195.      * Control commands
  196.      */
  197.     case ':':
  198.         CLEAROP;
  199.         if ((s = getcmdln(c)) != NULL)
  200.             docmdln(s);
  201.         break;
  202.  
  203.     case K_HELP:
  204.         CLEAROP;
  205.         if (help()) {
  206.             screenclear();
  207.             updatescreen();
  208.         }
  209.         break;
  210.  
  211.     case CTRL('L'):
  212.         CLEAROP;
  213.         screenclear();
  214.         updatescreen();
  215.         break;
  216.  
  217.  
  218.     case CTRL('O'):            /* ignored */
  219.         /*
  220.          * A command that's ignored can be useful. We use it at
  221.          * times when we want to postpone redraws. By stuffing
  222.          * in a control-o, redraws get suspended until the editor
  223.          * gets back around to processing input.
  224.          */
  225.         break;
  226.  
  227.     case CTRL('G'):
  228.         CLEAROP;
  229.         fileinfo();
  230.         break;
  231.  
  232.     case K_CCIRCM:            /* shorthand command */
  233.         CLEAROP;
  234. #ifdef TAGSTACK
  235.         /* If tag stacking compiled in & enabled, this is an untag.
  236.          * Otherwise, or if tag stack empty, edit alternate file.
  237.          * "untage" is so interpreted by dountag().
  238.          */
  239.         if (P(P_TG))
  240.             stuffin(":untage\n");
  241.         else
  242. #endif
  243.             stuffin(":e #\n");
  244.         break;
  245.  
  246.     case 'Z':            /* write, if changed, and exit */
  247.         if (vgetc() != 'Z') {
  248.             beep();
  249.             break;
  250.         }
  251.         doxit();
  252.         break;
  253.  
  254.     /*
  255.      * Macro evaluates true if char 'c' is a valid identifier character
  256.      */
  257. #    define    IDCHAR(c)    (isalpha(c) || isdigit(c) || (c) == '_')
  258.  
  259.     case CTRL(']'):            /* :ta to current identifier */
  260.         CLEAROP;
  261.         {
  262.             char    ch;
  263.             LPTR    save;
  264.  
  265.             save = *Curschar;
  266.             /*
  267.              * First back up to start of identifier. This
  268.              * doesn't match the real vi but I like it a
  269.              * little better and it shouldn't bother anyone.
  270.              */
  271.             ch = gchar(Curschar);
  272.             while (IDCHAR(ch)) {
  273.                 if (!oneleft())
  274.                     break;
  275.                 ch = gchar(Curschar);
  276.             }
  277.             if (!IDCHAR(ch))
  278.                 oneright();
  279.  
  280.             stuffin(":ta ");
  281.             /*
  282.              * Now grab the chars in the identifier
  283.              */
  284.             ch = gchar(Curschar);
  285.             while (IDCHAR(ch)) {
  286.                 stuffin(mkstr(ch));
  287.                 if (!oneright())
  288.                     break;
  289.                 ch = gchar(Curschar);
  290.             }
  291.             stuffin("\n");
  292.  
  293.             *Curschar = save;    /* restore, in case of error */
  294.         }
  295.         break;
  296.  
  297.     /*
  298.      * Character motion commands
  299.      */
  300.     case 'G':
  301.         mtype = MLINE;
  302.         *Curschar = *gotoline(Prenum);
  303.         beginline(TRUE);
  304.         break;
  305.  
  306.     case 'H':
  307.         mtype = MLINE;
  308.         *Curschar = *Topchar;
  309.         for (n = Prenum; n && onedown(1) ;n--)
  310.             ;
  311.         beginline(TRUE);
  312.         break;
  313.  
  314.     case 'M':
  315.         mtype = MLINE;
  316.         *Curschar = *Topchar;
  317.         for (n = 0; n < Rows/2 && onedown(1) ;n++)
  318.             ;
  319.         beginline(TRUE);
  320.         break;
  321.  
  322.     case 'L':
  323.         mtype = MLINE;
  324.         *Curschar = *prevline(Botchar);
  325.         for (n = Prenum; n && oneup(1) ;n--)
  326.             ;
  327.         beginline(TRUE);
  328.         break;
  329.  
  330.     case 'l':
  331.     case K_RARROW:
  332.     case ' ':
  333.         mtype = MCHAR;
  334.         mincl = FALSE;
  335.         n = DEFAULT1(Prenum);
  336.         while (n--) {
  337.             if ( ! oneright() )
  338.                 beep();
  339.         }
  340.         set_want_col = TRUE;
  341.         break;
  342.  
  343.     case 'h':
  344.     case K_LARROW:
  345.     case CTRL('H'):
  346.         mtype = MCHAR;
  347.         mincl = FALSE;
  348.         n = DEFAULT1(Prenum);
  349.         while (n--) {
  350.             if ( ! oneleft() )
  351.                 beep();
  352.         }
  353.         set_want_col = TRUE;
  354.         break;
  355.  
  356.     case '-':
  357.         flag = TRUE;
  358.         /* fall through */
  359.  
  360.     case 'k':
  361.     case K_UARROW:
  362.     case CTRL('P'):
  363.         mtype = MLINE;
  364.         if ( ! oneup(DEFAULT1(Prenum)) )
  365.             beep();
  366.         if (flag)
  367.             beginline(TRUE);
  368.         break;
  369.  
  370.     case '+':
  371.     case CR:
  372.     case NL:
  373.         flag = TRUE;
  374.         /* fall through */
  375.  
  376.     case 'j':
  377.     case K_DARROW:
  378.     case CTRL('N'):
  379.         mtype = MLINE;
  380.         if ( ! onedown(DEFAULT1(Prenum)) )
  381.             beep();
  382.         if (flag)
  383.             beginline(TRUE);
  384.         break;
  385.  
  386.     /*
  387.      * This is a strange motion command that helps make operators
  388.      * more logical. It is actually implemented, but not documented
  389.      * in the real 'vi'. This motion command actually refers to "the
  390.      * current line". Commands like "dd" and "yy" are really an alternate
  391.      * form of "d_" and "y_". It does accept a count, so "d3_" works to
  392.      * delete 3 lines.
  393.      */
  394.     case '_':
  395.     lineop:
  396.         mtype = MLINE;
  397.         onedown(DEFAULT1(Prenum)-1);
  398.         break;
  399.  
  400.     case '|':
  401.         mtype = MCHAR;
  402.         mincl = TRUE;
  403.         beginline(FALSE);
  404.         if (Prenum > 0)
  405.             *Curschar = *coladvance(Curschar, Prenum-1);
  406.         Curswant = Prenum - 1;
  407.         break;
  408.         
  409.     /*
  410.      * Word Motions
  411.      */
  412.  
  413.     case 'B':
  414.         type = 1;
  415.         /* fall through */
  416.  
  417.     case 'b':
  418.         mtype = MCHAR;
  419.         mincl = FALSE;
  420.         set_want_col = TRUE;
  421.         for (n = DEFAULT1(Prenum); n > 0 ;n--) {
  422.             LPTR    *pos;
  423.  
  424.             if ((pos = bck_word(Curschar, type)) == NULL) {
  425.                 beep();
  426.                 CLEAROP;
  427.                 break;
  428.             } else
  429.                 *Curschar = *pos;
  430.         }
  431.         break;
  432.  
  433.     case 'W':
  434.         type = 1;
  435.         /* fall through */
  436.  
  437.     case 'w':
  438.         /*
  439.          * This is a little strange. To match what the real vi
  440.          * does, we effectively map 'cw' to 'ce', and 'cW' to 'cE'.
  441.          * This seems impolite at first, but it's really more
  442.          * what we mean when we say 'cw'.
  443.          */
  444.         if (operator == CHANGE)
  445.             goto doecmd;
  446.  
  447.         mtype = MCHAR;
  448.         mincl = FALSE;
  449.         set_want_col = TRUE;
  450.         for (n = DEFAULT1(Prenum); n > 0 ;n--) {
  451.             LPTR    *pos;
  452.  
  453.             if ((pos = fwd_word(Curschar, type)) == NULL) {
  454.                 beep();
  455.                 CLEAROP;
  456.                 break;
  457.             } else
  458.                 *Curschar = *pos;
  459.         }
  460.         break;
  461.  
  462.     case 'E':
  463.         type = 1;
  464.         /* fall through */
  465.  
  466.     case 'e':
  467.     doecmd:
  468.         mtype = MCHAR;
  469.         mincl = TRUE;
  470.         set_want_col = TRUE;
  471.         for (n = DEFAULT1(Prenum); n > 0 ;n--) {
  472.             LPTR    *pos;
  473.  
  474.             /*
  475.              * The first motion gets special treatment if we're
  476.              * do a 'CHANGE'.
  477.              */
  478.             if (n == DEFAULT1(Prenum))
  479.                 pos = end_word(Curschar,type,operator==CHANGE);
  480.             else
  481.                 pos = end_word(Curschar, type, FALSE);
  482.  
  483.             if (pos == NULL) {
  484.                 beep();
  485.                 CLEAROP;
  486.                 break;
  487.             } else
  488.                 *Curschar = *pos;
  489.         }
  490.         break;
  491.  
  492.     case '$':
  493.         mtype = MCHAR;
  494.         mincl = TRUE;
  495.         while ( oneright() )
  496.             ;
  497.         Curswant = 999;        /* so we stay at the end */
  498.         break;
  499.  
  500.     case '^':
  501.         mtype = MCHAR;
  502.         mincl = FALSE;
  503.         beginline(TRUE);
  504.         break;
  505.  
  506.     case '0':
  507.         mtype = MCHAR;
  508.         mincl = TRUE;
  509.         beginline(FALSE);
  510.         break;
  511.  
  512.     /*
  513.      * Searches of various kinds
  514.      */
  515.     case '?':
  516.     case '/':
  517.         s = getcmdln(c);    /* get the search string */
  518.  
  519.         /*
  520.          * If they backspaced out of the search command,
  521.          * just bag everything.
  522.          */
  523.         if (s == NULL) {
  524.             CLEAROP;
  525.             break;
  526.         }
  527.  
  528.         mtype = MCHAR;
  529.         mincl = FALSE;
  530.         set_want_col = TRUE;
  531.  
  532.         /*
  533.          * If no string given, pass NULL to repeat the prior search.
  534.          * If the search fails, abort any pending operator.
  535.          */
  536.         if (!dosearch(
  537.                 (c == '/') ? FORWARD : BACKWARD,
  538.                 (*s == NUL) ? NULL : s
  539.                  ))
  540.             CLEAROP;
  541.         break;
  542.  
  543.     case 'n':
  544.         mtype = MCHAR;
  545.         mincl = FALSE;
  546.         set_want_col = TRUE;
  547.         if (!repsearch(0))
  548.             CLEAROP;
  549.         break;
  550.  
  551.     case 'N':
  552.         mtype = MCHAR;
  553.         mincl = FALSE;
  554.         set_want_col = TRUE;
  555.         if (!repsearch(1))
  556.             CLEAROP;
  557.         break;
  558.  
  559.     /*
  560.      * Character searches
  561.      */
  562.     case 'T':
  563.         dir = BACKWARD;
  564.         /* fall through */
  565.  
  566.     case 't':
  567.         type = 1;
  568.         goto docsearch;
  569.  
  570.     case 'F':
  571.         dir = BACKWARD;
  572.         /* fall through */
  573.  
  574.     case 'f':
  575.     docsearch:
  576.         mtype = MCHAR;
  577.         mincl = TRUE;
  578.         set_want_col = TRUE;
  579.         if ((nchar = vgetc()) == ESC)    /* search char */
  580.             break;
  581.  
  582.         for (n = DEFAULT1(Prenum); n > 0 ;n--) {
  583.             if (!searchc(nchar, dir, type)) {
  584.                 CLEAROP;
  585.                 beep();
  586.             }
  587.         }
  588.         break;
  589.  
  590.     case ',':
  591.         flag = 1;
  592.         /* fall through */
  593.  
  594.     case ';':
  595.         mtype = MCHAR;
  596.         mincl = TRUE;
  597.         set_want_col = TRUE;
  598.         for (n = DEFAULT1(Prenum); n > 0 ;n--) {
  599.             if (!crepsearch(flag)) {
  600.                 CLEAROP;
  601.                 beep();
  602.             }
  603.         }
  604.         break;
  605.  
  606.     case '(':            /* sentence searches */
  607.         dir = BACKWARD;
  608.         /* fall through */
  609.  
  610.     case ')':
  611.         mtype = MCHAR;
  612.         mincl = FALSE;
  613.         set_want_col = TRUE;
  614.         if (findsent(dir) == NULL) {
  615.             beep();
  616.             CLEAROP;
  617.         }
  618.         break;
  619.  
  620.     case '{':            /* paragraph searches */
  621.         dir = BACKWARD;
  622.         /* fall through */
  623.  
  624.     case '}':
  625.         mtype = MCHAR;
  626.         mincl = FALSE;
  627.         set_want_col = TRUE;
  628.         if (!findpara(dir)) {
  629.             beep();
  630.             CLEAROP;
  631.         }
  632.         break;
  633.  
  634.     case '[':            /* function searches */
  635.         dir = BACKWARD;
  636.         /* fall through */
  637.  
  638.     case ']':
  639.         mtype = MLINE;
  640.         set_want_col = TRUE;
  641.         if (vgetc() != c) {
  642.             beep();
  643.             CLEAROP;
  644.             break;
  645.         }
  646.  
  647.         if (!findfunc(dir)) {
  648.             beep();
  649.             CLEAROP;
  650.         }
  651.         break;
  652.  
  653.     case '%':
  654.         mtype = MCHAR;
  655.         mincl = TRUE;
  656.         {
  657.             LPTR    *pos;
  658.  
  659.             if ((pos = showmatch()) == NULL) {
  660.                 beep();
  661.                 CLEAROP;
  662.             } else {
  663.                 setpcmark();
  664.                 *Curschar = *pos;
  665.                 set_want_col = TRUE;
  666.             }
  667.         }
  668.         break;
  669.         
  670.     /*
  671.      * Edits
  672.      */
  673.     case '.':        /* repeat last change (usually) */
  674.         /*
  675.          * If a delete is in effect, we let '.' help out the same
  676.          * way that '_' helps for some line operations. It's like
  677.          * an 'l', but subtracts one from the count and is inclusive.
  678.          */
  679.         if (operator == DELETE || operator == CHANGE) {
  680.             if (Prenum != 0) {
  681.                 n = DEFAULT1(Prenum) - 1;
  682.                 while (n--)
  683.                     if (! oneright())
  684.                         break;
  685.             }
  686.             mtype = MCHAR;
  687.             mincl = TRUE;
  688.         } else {            /* a normal 'redo' */
  689.             CLEAROP;
  690.             stuffin(Redobuff);
  691.         }
  692.         break;
  693.  
  694.     case 'u':
  695.     case K_UNDO:
  696.         CLEAROP;
  697.         u_undo();
  698.         break;
  699.  
  700.     case 'U':
  701.         CLEAROP;
  702.         u_lundo();
  703.         break;
  704.  
  705.     case 'x':
  706.         CLEAROP;
  707.         if (lineempty())    /* can't do it on a blank line */
  708.             beep();
  709.         if (Prenum)
  710.             stuffnum(Prenum);
  711.         stuffin("d.");
  712.         break;
  713.  
  714.     case 'X':
  715.         CLEAROP;
  716.         if (!oneleft())
  717.             beep();
  718.         else {
  719.             strcpy(Redobuff, "X");
  720.             u_saveline();
  721.             delchar(TRUE);
  722.             updateline();
  723.         }
  724.         break;
  725.  
  726.     case 'r':
  727.         CLEAROP;
  728.         if (lineempty()) {    /* Nothing to replace */
  729.             beep();
  730.             break;
  731.         }
  732.         if ((nchar = vgetc()) == ESC)
  733.             break;
  734.  
  735.         if (nchar==CR || nchar==NL) {
  736.             stuffin("R\n\033");
  737.             break;
  738.         }
  739.  
  740.         if (nchar & 0x80) {
  741.             beep();
  742.             break;
  743.         }
  744.         u_saveline();
  745.  
  746.         /* Change current character. */
  747.         pchar(Curschar, nchar);
  748.  
  749.         /* Save stuff necessary to redo it */
  750.         sprintf(Redobuff, "r%c", nchar);
  751.  
  752.         CHANGED;
  753.         updateline();
  754.         break;
  755.  
  756.     case '~':        /* swap case */
  757.         if (!P(P_TO)) {
  758.             CLEAROP;
  759.             if (lineempty()) {
  760.                 beep();
  761.                 break;
  762.             }
  763.             c = gchar(Curschar);
  764.  
  765.             if (isalpha(c)) {
  766.                 if (islower(c))
  767.                     c = toupper(c);
  768.                 else
  769.                     c = tolower(c);
  770.             }
  771.             u_saveline();
  772.  
  773.             pchar(Curschar, c);    /* Change current character. */
  774.             oneright();
  775.  
  776.             strcpy(Redobuff, "~");
  777.  
  778.             CHANGED;
  779.             updateline();
  780.         }
  781. #ifdef    TILDEOP
  782.         else {
  783.             if (operator == TILDE)        /* handle '~~' */
  784.                 goto lineop;
  785.             if (Prenum != 0)
  786.                 opnum = Prenum;
  787.             startop = *Curschar;
  788.             operator = TILDE;
  789.         }
  790. #endif
  791.  
  792.         break;
  793.  
  794.     case 'J':
  795.         CLEAROP;
  796.  
  797.         u_save(Curschar->linep->prev, Curschar->linep->next->next);
  798.  
  799.         if (!dojoin(TRUE))
  800.             beep();
  801.  
  802.         strcpy(Redobuff, "J");
  803.         updatescreen();
  804.         break;
  805.  
  806.     /*
  807.      * Inserts
  808.      */
  809.     case 'A':
  810.         set_want_col = TRUE;
  811.         while (oneright())
  812.             ;
  813.         /* fall through */
  814.  
  815.     case 'a':
  816.         CLEAROP;
  817.         /* Works just like an 'i'nsert on the next character. */
  818.         if (!lineempty())
  819.             inc(Curschar);
  820.         u_saveline();
  821.         startinsert(mkstr(c), FALSE);
  822.         break;
  823.  
  824.     case 'I':
  825.         beginline(TRUE);
  826.         /* fall through */
  827.  
  828.     case 'i':
  829.     case K_INSERT:
  830.         CLEAROP;
  831.         u_saveline();
  832.         startinsert(mkstr(c), FALSE);
  833.         break;
  834.  
  835.     case 'o':
  836.         CLEAROP;
  837.         u_save(Curschar->linep, Curschar->linep->next);
  838.         opencmd(FORWARD, TRUE);
  839.         startinsert("o", TRUE);
  840.         break;
  841.  
  842.     case 'O':
  843.         CLEAROP;
  844.         u_save(Curschar->linep->prev, Curschar->linep);
  845.         opencmd(BACKWARD, TRUE);
  846.         startinsert("O", TRUE);
  847.         break;
  848.  
  849.     case 'R':
  850.         CLEAROP;
  851.         u_saveline();
  852.         startinsert("R", FALSE);
  853.         break;
  854.  
  855.     /*
  856.      * Operators
  857.      */
  858.     case 'd':
  859.         if (operator == DELETE)        /* handle 'dd' */
  860.             goto lineop;
  861.         if (Prenum != 0)
  862.             opnum = Prenum;
  863.         startop = *Curschar;
  864.         operator = DELETE;
  865.         break;
  866.  
  867.     case 'c':
  868.         if (operator == CHANGE)        /* handle 'cc' */
  869.             goto lineop;
  870.         if (Prenum != 0)
  871.             opnum = Prenum;
  872.         startop = *Curschar;
  873.         operator = CHANGE;
  874.         break;
  875.  
  876.     case 'y':
  877.         if (operator == YANK)        /* handle 'yy' */
  878.             goto lineop;
  879.         if (Prenum != 0)
  880.             opnum = Prenum;
  881.         startop = *Curschar;
  882.         operator = YANK;
  883.         break;
  884.  
  885.     case '>':
  886.         if (operator == RSHIFT)        /* handle >> */
  887.             goto lineop;
  888.         if (Prenum != 0)
  889.             opnum = Prenum;
  890.         startop = *Curschar;
  891.         operator = RSHIFT;
  892.         break;
  893.  
  894.     case '<':
  895.         if (operator == LSHIFT)        /* handle << */
  896.             goto lineop;
  897.         if (Prenum != 0)
  898.             opnum = Prenum;
  899.         startop = *Curschar;    /* save current position */
  900.         operator = LSHIFT;
  901.         break;
  902.  
  903.     case '!':
  904.         if (operator == FILTER)        /* handle '!!' */
  905.             goto lineop;
  906.         if (Prenum != 0)
  907.             opnum = Prenum;
  908.         startop = *Curschar;
  909.         operator = FILTER;
  910.         break;
  911.  
  912.     case 'p':
  913.         doput(FORWARD);
  914.         break;
  915.  
  916.     case 'P':
  917.         doput(BACKWARD);
  918.         break;
  919.  
  920.     /*
  921.      * Abbreviations
  922.      */
  923.     case 'D':
  924.         stuffin("d$");
  925.         break;
  926.  
  927.     case 'Y':
  928.         if (Prenum)
  929.             stuffnum(Prenum);
  930.         stuffin("yy");
  931.         break;
  932.  
  933.     case 'C':
  934.         stuffin("c$");
  935.         break;
  936.  
  937.     case 's':                /* substitute characters */
  938.         if (Prenum)
  939.             stuffnum(Prenum);
  940.         stuffin("c.");
  941.         break;
  942.  
  943.     /*
  944.      * Marks
  945.      */
  946.     case 'm':
  947.         CLEAROP;
  948.         if (!setmark(vgetc()))
  949.             beep();
  950.         break;
  951.  
  952.     case '\'':
  953.         flag = TRUE;
  954.         /* fall through */
  955.  
  956.     case '`':
  957.         {
  958.             LPTR    mtmp, *mark;
  959.  
  960.             mark = getmark(vgetc());
  961.             if (mark == NULL) {
  962.                 beep();
  963.                 CLEAROP;
  964.             } else {
  965.                 mtmp = *mark;
  966.                 setpcmark();
  967.                 *Curschar = mtmp;
  968.                 if (flag)
  969.                     beginline(TRUE);
  970.             }
  971.             mtype = flag ? MLINE : MCHAR;
  972.             mincl = FALSE;        /* ignored if not MCHAR */
  973.             set_want_col = TRUE;
  974.         }
  975.         break;
  976.  
  977.     default:
  978.         CLEAROP;
  979.         beep();
  980.         break;
  981.     }
  982.  
  983.     /*
  984.      * If an operation is pending, handle it...
  985.      */
  986.     if (finish_op) {        /* we just finished an operator */
  987.         if (operator == NOP)    /* ... but it was cancelled */
  988.             return;
  989.  
  990.         switch (operator) {
  991.  
  992.         case LSHIFT:
  993.         case RSHIFT:
  994.             doshift(operator, c, nchar, Prenum);
  995.             break;
  996.  
  997.         case DELETE:
  998.             dodelete(c, nchar, Prenum);
  999.             break;
  1000.  
  1001.         case YANK:
  1002.             (void) doyank();    /* no redo on yank... */
  1003.             break;
  1004.  
  1005.         case CHANGE:
  1006.             dochange(c, nchar, Prenum);
  1007.             break;
  1008.  
  1009.         case FILTER:
  1010.             dofilter(c, nchar, Prenum);
  1011.             break;
  1012.  
  1013. #ifdef    TILDEOP
  1014.         case TILDE:
  1015.             dotilde(c, nchar, Prenum);
  1016.             break;
  1017. #endif
  1018.  
  1019.         default:
  1020.             beep();
  1021.         }
  1022.         operator = NOP;
  1023.     }
  1024. }
  1025.