home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / vol_300 / 313_01 / normal.c < prev    next >
C/C++ Source or Header  |  1990-04-22  |  18KB  |  982 lines

  1. /* $Header: /nw2/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_CGRAVE:            /* shorthand command */
  233.         CLEAROP;
  234.         stuffin(":e #\n");
  235.         break;
  236.  
  237.     case 'Z':            /* write, if changed, and exit */
  238.         if (vgetc() != 'Z') {
  239.             beep();
  240.             break;
  241.         }
  242.         doxit();
  243.         break;
  244.  
  245.     /*
  246.      * Macro evaluates true if char 'c' is a valid identifier character
  247.      */
  248. #    define    IDCHAR(c)    (isalpha(c) || isdigit(c) || (c) == '_')
  249.  
  250.     case CTRL(']'):            /* :ta to current identifier */
  251.         CLEAROP;
  252.         {
  253.             char    ch;
  254.             LPTR    save;
  255.  
  256.             save = *Curschar;
  257.             /*
  258.              * First back up to start of identifier. This
  259.              * doesn't match the real vi but I like it a
  260.              * little better and it shouldn't bother anyone.
  261.              */
  262.             ch = gchar(Curschar);
  263.             while (IDCHAR(ch)) {
  264.                 if (!oneleft())
  265.                     break;
  266.                 ch = gchar(Curschar);
  267.             }
  268.             if (!IDCHAR(ch))
  269.                 oneright();
  270.  
  271.             stuffin(":ta ");
  272.             /*
  273.              * Now grab the chars in the identifier
  274.              */
  275.             ch = gchar(Curschar);
  276.             while (IDCHAR(ch)) {
  277.                 stuffin(mkstr(ch));
  278.                 if (!oneright())
  279.                     break;
  280.                 ch = gchar(Curschar);
  281.             }
  282.             stuffin("\n");
  283.  
  284.             *Curschar = save;    /* restore, in case of error */
  285.         }
  286.         break;
  287.  
  288.     /*
  289.      * Character motion commands
  290.      */
  291.     case 'G':
  292.         mtype = MLINE;
  293.         *Curschar = *gotoline(Prenum);
  294.         beginline(TRUE);
  295.         break;
  296.  
  297.     case 'H':
  298.         mtype = MLINE;
  299.         *Curschar = *Topchar;
  300.         for (n = Prenum; n && onedown(1) ;n--)
  301.             ;
  302.         beginline(TRUE);
  303.         break;
  304.  
  305.     case 'M':
  306.         mtype = MLINE;
  307.         *Curschar = *Topchar;
  308.         for (n = 0; n < Rows/2 && onedown(1) ;n++)
  309.             ;
  310.         beginline(TRUE);
  311.         break;
  312.  
  313.     case 'L':
  314.         mtype = MLINE;
  315.         *Curschar = *prevline(Botchar);
  316.         for (n = Prenum; n && oneup(1) ;n--)
  317.             ;
  318.         beginline(TRUE);
  319.         break;
  320.  
  321.     case 'l':
  322.     case K_RARROW:
  323.     case ' ':
  324.         mtype = MCHAR;
  325.         mincl = FALSE;
  326.         n = DEFAULT1(Prenum);
  327.         while (n--) {
  328.             if ( ! oneright() )
  329.                 beep();
  330.         }
  331.         set_want_col = TRUE;
  332.         break;
  333.  
  334.     case 'h':
  335.     case K_LARROW:
  336.     case CTRL('H'):
  337.         mtype = MCHAR;
  338.         mincl = FALSE;
  339.         n = DEFAULT1(Prenum);
  340.         while (n--) {
  341.             if ( ! oneleft() )
  342.                 beep();
  343.         }
  344.         set_want_col = TRUE;
  345.         break;
  346.  
  347.     case '-':
  348.         flag = TRUE;
  349.         /* fall through */
  350.  
  351.     case 'k':
  352.     case K_UARROW:
  353.     case CTRL('P'):
  354.         mtype = MLINE;
  355.         if ( ! oneup(DEFAULT1(Prenum)) )
  356.             beep();
  357.         if (flag)
  358.             beginline(TRUE);
  359.         break;
  360.  
  361.     case '+':
  362.     case CR:
  363.     case NL:
  364.         flag = TRUE;
  365.         /* fall through */
  366.  
  367.     case 'j':
  368.     case K_DARROW:
  369.     case CTRL('N'):
  370.         mtype = MLINE;
  371.         if ( ! onedown(DEFAULT1(Prenum)) )
  372.             beep();
  373.         if (flag)
  374.             beginline(TRUE);
  375.         break;
  376.  
  377.     /*
  378.      * This is a strange motion command that helps make operators
  379.      * more logical. It is actually implemented, but not documented
  380.      * in the real 'vi'. This motion command actually refers to "the
  381.      * current line". Commands like "dd" and "yy" are really an alternate
  382.      * form of "d_" and "y_". It does accept a count, so "d3_" works to
  383.      * delete 3 lines.
  384.      */
  385.     case '_':
  386.     lineop:
  387.         mtype = MLINE;
  388.         onedown(DEFAULT1(Prenum)-1);
  389.         break;
  390.  
  391.     case '|':
  392.         mtype = MCHAR;
  393.         mincl = TRUE;
  394.         beginline(FALSE);
  395.         if (Prenum > 0)
  396.             *Curschar = *coladvance(Curschar, Prenum-1);
  397.         Curswant = Prenum - 1;
  398.         break;
  399.         
  400.     /*
  401.      * Word Motions
  402.      */
  403.  
  404.     case 'B':
  405.         type = 1;
  406.         /* fall through */
  407.  
  408.     case 'b':
  409.         mtype = MCHAR;
  410.         mincl = FALSE;
  411.         set_want_col = TRUE;
  412.         for (n = DEFAULT1(Prenum); n > 0 ;n--) {
  413.             LPTR    *pos;
  414.  
  415.             if ((pos = bck_word(Curschar, type)) == NULL) {
  416.                 beep();
  417.                 CLEAROP;
  418.                 break;
  419.             } else
  420.                 *Curschar = *pos;
  421.         }
  422.         break;
  423.  
  424.     case 'W':
  425.         type = 1;
  426.         /* fall through */
  427.  
  428.     case 'w':
  429.         /*
  430.          * This is a little strange. To match what the real vi
  431.          * does, we effectively map 'cw' to 'ce', and 'cW' to 'cE'.
  432.          * This seems impolite at first, but it's really more
  433.          * what we mean when we say 'cw'.
  434.          */
  435.         if (operator == CHANGE)
  436.             goto doecmd;
  437.  
  438.         mtype = MCHAR;
  439.         mincl = FALSE;
  440.         set_want_col = TRUE;
  441.         for (n = DEFAULT1(Prenum); n > 0 ;n--) {
  442.             LPTR    *pos;
  443.  
  444.             if ((pos = fwd_word(Curschar, type)) == NULL) {
  445.                 beep();
  446.                 CLEAROP;
  447.                 break;
  448.             } else
  449.                 *Curschar = *pos;
  450.         }
  451.         break;
  452.  
  453.     case 'E':
  454.         type = 1;
  455.         /* fall through */
  456.  
  457.     case 'e':
  458.     doecmd:
  459.         mtype = MCHAR;
  460.         mincl = TRUE;
  461.         set_want_col = TRUE;
  462.         for (n = DEFAULT1(Prenum); n > 0 ;n--) {
  463.             LPTR    *pos;
  464.  
  465.             /*
  466.              * The first motion gets special treatment if we're
  467.              * do a 'CHANGE'.
  468.              */
  469.             if (n == DEFAULT1(Prenum))
  470.                 pos = end_word(Curschar,type,operator==CHANGE);
  471.             else
  472.                 pos = end_word(Curschar, type, FALSE);
  473.  
  474.             if (pos == NULL) {
  475.                 beep();
  476.                 CLEAROP;
  477.                 break;
  478.             } else
  479.                 *Curschar = *pos;
  480.         }
  481.         break;
  482.  
  483.     case '$':
  484.         mtype = MCHAR;
  485.         mincl = TRUE;
  486.         while ( oneright() )
  487.             ;
  488.         Curswant = 999;        /* so we stay at the end */
  489.         break;
  490.  
  491.     case '^':
  492.         mtype = MCHAR;
  493.         mincl = FALSE;
  494.         beginline(TRUE);
  495.         break;
  496.  
  497.     case '0':
  498.         mtype = MCHAR;
  499.         mincl = TRUE;
  500.         beginline(FALSE);
  501.         break;
  502.  
  503.     /*
  504.      * Searches of various kinds
  505.      */
  506.     case '?':
  507.     case '/':
  508.         s = getcmdln(c);    /* get the search string */
  509.  
  510.         /*
  511.          * If they backspaced out of the search command,
  512.          * just bag everything.
  513.          */
  514.         if (s == NULL) {
  515.             CLEAROP;
  516.             break;
  517.         }
  518.  
  519.         mtype = MCHAR;
  520.         mincl = FALSE;
  521.         set_want_col = TRUE;
  522.  
  523.         /*
  524.          * If no string given, pass NULL to repeat the prior search.
  525.          * If the search fails, abort any pending operator.
  526.          */
  527.         if (!dosearch(
  528.                 (c == '/') ? FORWARD : BACKWARD,
  529.                 (*s == NUL) ? NULL : s
  530.                  ))
  531.             CLEAROP;
  532.         break;
  533.  
  534.     case 'n':
  535.         mtype = MCHAR;
  536.         mincl = FALSE;
  537.         set_want_col = TRUE;
  538.         if (!repsearch(0))
  539.             CLEAROP;
  540.         break;
  541.  
  542.     case 'N':
  543.         mtype = MCHAR;
  544.         mincl = FALSE;
  545.         set_want_col = TRUE;
  546.         if (!repsearch(1))
  547.             CLEAROP;
  548.         break;
  549.  
  550.     /*
  551.      * Character searches
  552.      */
  553.     case 'T':
  554.         dir = BACKWARD;
  555.         /* fall through */
  556.  
  557.     case 't':
  558.         type = 1;
  559.         goto docsearch;
  560.  
  561.     case 'F':
  562.         dir = BACKWARD;
  563.         /* fall through */
  564.  
  565.     case 'f':
  566.     docsearch:
  567.         mtype = MCHAR;
  568.         mincl = TRUE;
  569.         set_want_col = TRUE;
  570.         if ((nchar = vgetc()) == ESC)    /* search char */
  571.             break;
  572.  
  573.         for (n = DEFAULT1(Prenum); n > 0 ;n--) {
  574.             if (!searchc(nchar, dir, type)) {
  575.                 CLEAROP;
  576.                 beep();
  577.             }
  578.         }
  579.         break;
  580.  
  581.     case ',':
  582.         flag = 1;
  583.         /* fall through */
  584.  
  585.     case ';':
  586.         mtype = MCHAR;
  587.         mincl = TRUE;
  588.         set_want_col = TRUE;
  589.         for (n = DEFAULT1(Prenum); n > 0 ;n--) {
  590.             if (!crepsearch(flag)) {
  591.                 CLEAROP;
  592.                 beep();
  593.             }
  594.         }
  595.         break;
  596.  
  597.     case '[':            /* function searches */
  598.         dir = BACKWARD;
  599.         /* fall through */
  600.  
  601.     case ']':
  602.         mtype = MLINE;
  603.         set_want_col = TRUE;
  604.         if (vgetc() != c) {
  605.             beep();
  606.             CLEAROP;
  607.             break;
  608.         }
  609.  
  610.         if (!findfunc(dir)) {
  611.             beep();
  612.             CLEAROP;
  613.         }
  614.         break;
  615.  
  616.     case '%':
  617.         mtype = MCHAR;
  618.         mincl = TRUE;
  619.         {
  620.             LPTR    *pos;
  621.  
  622.             if ((pos = showmatch()) == NULL) {
  623.                 beep();
  624.                 CLEAROP;
  625.             } else {
  626.                 setpcmark();
  627.                 *Curschar = *pos;
  628.                 set_want_col = TRUE;
  629.             }
  630.         }
  631.         break;
  632.         
  633.     /*
  634.      * Edits
  635.      */
  636.     case '.':        /* repeat last change (usually) */
  637.         /*
  638.          * If a delete is in effect, we let '.' help out the same
  639.          * way that '_' helps for some line operations. It's like
  640.          * an 'l', but subtracts one from the count and is inclusive.
  641.          */
  642.         if (operator == DELETE || operator == CHANGE) {
  643.             if (Prenum != 0) {
  644.                 n = DEFAULT1(Prenum) - 1;
  645.                 while (n--)
  646.                     if (! oneright())
  647.                         break;
  648.             }
  649.             mtype = MCHAR;
  650.             mincl = TRUE;
  651.         } else {            /* a normal 'redo' */
  652.             CLEAROP;
  653.             stuffin(Redobuff);
  654.         }
  655.         break;
  656.  
  657.     case 'u':
  658.     case K_UNDO:
  659.         CLEAROP;
  660.         u_undo();
  661.         break;
  662.  
  663.     case 'U':
  664.         CLEAROP;
  665.         u_lundo();
  666.         break;
  667.  
  668.     case 'x':
  669.         CLEAROP;
  670.         if (lineempty())    /* can't do it on a blank line */
  671.             beep();
  672.         if (Prenum)
  673.             stuffnum(Prenum);
  674.         stuffin("d.");
  675.         break;
  676.  
  677.     case 'X':
  678.         CLEAROP;
  679.         if (!oneleft())
  680.             beep();
  681.         else {
  682.             strcpy(Redobuff, "X");
  683.             u_saveline();
  684.             delchar(TRUE);
  685.             updateline();
  686.         }
  687.         break;
  688.  
  689.     case 'r':
  690.         CLEAROP;
  691.         if (lineempty()) {    /* Nothing to replace */
  692.             beep();
  693.             break;
  694.         }
  695.         if ((nchar = vgetc()) == ESC)
  696.             break;
  697.  
  698.         if ((nchar & 0x80) || nchar == CR || nchar == NL) {
  699.             beep();
  700.             break;
  701.         }
  702.         u_saveline();
  703.  
  704.         /* Change current character. */
  705.         pchar(Curschar, nchar);
  706.  
  707.         /* Save stuff necessary to redo it */
  708.         sprintf(Redobuff, "r%c", nchar);
  709.  
  710.         CHANGED;
  711.         updateline();
  712.         break;
  713.  
  714.     case '~':        /* swap case */
  715.         if (!P(P_TO)) {
  716.             CLEAROP;
  717.             if (lineempty()) {
  718.                 beep();
  719.                 break;
  720.             }
  721.             c = gchar(Curschar);
  722.  
  723.             if (isalpha(c)) {
  724.                 if (islower(c))
  725.                     c = toupper(c);
  726.                 else
  727.                     c = tolower(c);
  728.             }
  729.             u_saveline();
  730.  
  731.             pchar(Curschar, c);    /* Change current character. */
  732.             oneright();
  733.  
  734.             strcpy(Redobuff, "~");
  735.  
  736.             CHANGED;
  737.             updateline();
  738.         }
  739. #ifdef    TILDEOP
  740.         else {
  741.             if (operator == TILDE)        /* handle '~~' */
  742.                 goto lineop;
  743.             if (Prenum != 0)
  744.                 opnum = Prenum;
  745.             startop = *Curschar;
  746.             operator = TILDE;
  747.         }
  748. #endif
  749.  
  750.         break;
  751.  
  752.     case 'J':
  753.         CLEAROP;
  754.  
  755.         u_save(Curschar->linep->prev, Curschar->linep->next->next);
  756.  
  757.         if (!dojoin(TRUE))
  758.             beep();
  759.  
  760.         strcpy(Redobuff, "J");
  761.         updatescreen();
  762.         break;
  763.  
  764.     /*
  765.      * Inserts
  766.      */
  767.     case 'A':
  768.         set_want_col = TRUE;
  769.         while (oneright())
  770.             ;
  771.         /* fall through */
  772.  
  773.     case 'a':
  774.         CLEAROP;
  775.         /* Works just like an 'i'nsert on the next character. */
  776.         if (!lineempty())
  777.             inc(Curschar);
  778.         u_saveline();
  779.         startinsert(mkstr(c), FALSE);
  780.         break;
  781.  
  782.     case 'I':
  783.         beginline(TRUE);
  784.         /* fall through */
  785.  
  786.     case 'i':
  787.     case K_INSERT:
  788.         CLEAROP;
  789.         u_saveline();
  790.         startinsert(mkstr(c), FALSE);
  791.         break;
  792.  
  793.     case 'o':
  794.         CLEAROP;
  795.         u_save(Curschar->linep, Curschar->linep->next);
  796.         opencmd(FORWARD, TRUE);
  797.         startinsert("o", TRUE);
  798.         break;
  799.  
  800.     case 'O':
  801.         CLEAROP;
  802.         u_save(Curschar->linep->prev, Curschar->linep);
  803.         opencmd(BACKWARD, TRUE);
  804.         startinsert("O", TRUE);
  805.         break;
  806.  
  807.     case 'R':
  808.         CLEAROP;
  809.         u_saveline();
  810.         startinsert("R", FALSE);
  811.         break;
  812.  
  813.     /*
  814.      * Operators
  815.      */
  816.     case 'd':
  817.         if (operator == DELETE)        /* handle 'dd' */
  818.             goto lineop;
  819.         if (Prenum != 0)
  820.             opnum = Prenum;
  821.         startop = *Curschar;
  822.         operator = DELETE;
  823.         break;
  824.  
  825.     case 'c':
  826.         if (operator == CHANGE)        /* handle 'cc' */
  827.             goto lineop;
  828.         if (Prenum != 0)
  829.             opnum = Prenum;
  830.         startop = *Curschar;
  831.         operator = CHANGE;
  832.         break;
  833.  
  834.     case 'y':
  835.         if (operator == YANK)        /* handle 'yy' */
  836.             goto lineop;
  837.         if (Prenum != 0)
  838.             opnum = Prenum;
  839.         startop = *Curschar;
  840.         operator = YANK;
  841.         break;
  842.  
  843.     case '>':
  844.         if (operator == RSHIFT)        /* handle >> */
  845.             goto lineop;
  846.         if (Prenum != 0)
  847.             opnum = Prenum;
  848.         startop = *Curschar;
  849.         operator = RSHIFT;
  850.         break;
  851.  
  852.     case '<':
  853.         if (operator == LSHIFT)        /* handle << */
  854.             goto lineop;
  855.         if (Prenum != 0)
  856.             opnum = Prenum;
  857.         startop = *Curschar;    /* save current position */
  858.         operator = LSHIFT;
  859.         break;
  860.  
  861.     case '!':
  862.         if (operator == FILTER)        /* handle '!!' */
  863.             goto lineop;
  864.         if (Prenum != 0)
  865.             opnum = Prenum;
  866.         startop = *Curschar;
  867.         operator = FILTER;
  868.         break;
  869.  
  870.     case 'p':
  871.         doput(FORWARD);
  872.         break;
  873.  
  874.     case 'P':
  875.         doput(BACKWARD);
  876.         break;
  877.  
  878.     /*
  879.      * Abbreviations
  880.      */
  881.     case 'D':
  882.         stuffin("d$");
  883.         break;
  884.  
  885.     case 'Y':
  886.         if (Prenum)
  887.             stuffnum(Prenum);
  888.         stuffin("yy");
  889.         break;
  890.  
  891.     case 'C':
  892.         stuffin("c$");
  893.         break;
  894.  
  895.     case 's':                /* substitute characters */
  896.         if (Prenum)
  897.             stuffnum(Prenum);
  898.         stuffin("c.");
  899.         break;
  900.  
  901.     /*
  902.      * Marks
  903.      */
  904.     case 'm':
  905.         CLEAROP;
  906.         if (!setmark(vgetc()))
  907.             beep();
  908.         break;
  909.  
  910.     case '\'':
  911.         flag = TRUE;
  912.         /* fall through */
  913.  
  914.     case '`':
  915.         {
  916.             LPTR    mtmp, *mark = getmark(vgetc());
  917.  
  918.             if (mark == NULL) {
  919.                 beep();
  920.                 CLEAROP;
  921.             } else {
  922.                 mtmp = *mark;
  923.                 setpcmark();
  924.                 *Curschar = mtmp;
  925.                 if (flag)
  926.                     beginline(TRUE);
  927.             }
  928.             mtype = flag ? MLINE : MCHAR;
  929.             mincl = TRUE;        /* ignored if not MCHAR */
  930.             set_want_col = TRUE;
  931.         }
  932.         break;
  933.  
  934.     default:
  935.         CLEAROP;
  936.         beep();
  937.         break;
  938.     }
  939.  
  940.     /*
  941.      * If an operation is pending, handle it...
  942.      */
  943.     if (finish_op) {        /* we just finished an operator */
  944.         if (operator == NOP)    /* ... but it was cancelled */
  945.             return;
  946.  
  947.         switch (operator) {
  948.  
  949.         case LSHIFT:
  950.         case RSHIFT:
  951.             doshift(operator, c, nchar, Prenum);
  952.             break;
  953.  
  954.         case DELETE:
  955.             dodelete(c, nchar, Prenum);
  956.             break;
  957.  
  958.         case YANK:
  959.             (void) doyank();    /* no redo on yank... */
  960.             break;
  961.  
  962.         case CHANGE:
  963.             dochange(c, nchar, Prenum);
  964.             break;
  965.  
  966.         case FILTER:
  967.             dofilter(c, nchar, Prenum);
  968.             break;
  969.  
  970. #ifdef    TILDEOP
  971.         case TILDE:
  972.             dotilde(c, nchar, Prenum);
  973.             break;
  974. #endif
  975.  
  976.         default:
  977.             beep();
  978.         }
  979.         operator = NOP;
  980.     }
  981. }
  982.