home *** CD-ROM | disk | FTP | other *** search
/ The Fred Fish Collection 1.5 / ffcollection-1-5-1992-11.iso / ff_disks / 200-299 / ff256.lzh / Stevie / normal.c < prev    next >
C/C++ Source or Header  |  1989-10-19  |  35KB  |  1,817 lines

  1. /*
  2.  * STEVIE - Simply Try this Editor for VI Enthusiasts
  3.  *
  4.  * Code Contributions By : Tim Thompson           twitch!tjt
  5.  *                         Tony Andrews           onecom!wldrdg!tony 
  6.  *                         G. R. (Fred) Walter    watmath!grwalter 
  7.  */
  8.  
  9. /*
  10.  * This file contains the main routine for processing characters in command
  11.  * mode as well as routines for handling the operators. 
  12.  */
  13.  
  14. #include "stevie.h"
  15.  
  16. static void     doshift();
  17. static void     dodelete();
  18. static void     doput();
  19. static void     dochange();
  20. static void     startinsert();
  21. static bool_t   dojoin();
  22. static bool_t   doyank();
  23.  
  24. /*
  25.  * Macro evaluates true if char 'c' is a valid identifier character 
  26.  */
  27. #define IDCHAR(c)       (isalpha(c) || isdigit(c) || (c) == '_')
  28.  
  29. /*
  30.  * Operators 
  31.  */
  32. #define NOP     0        /* no pending operation */
  33. #define DELETE  1
  34. #define YANK    2
  35. #define CHANGE  3
  36. #define LSHIFT  4
  37. #define RSHIFT  5
  38.  
  39. #define CLEAROP (operator = NOP)/* clear any pending operator */
  40.  
  41. static int      operator = NOP;    /* current pending operator */
  42.  
  43. /*
  44.  * When a cursor motion command is made, it is marked as being a character or
  45.  * line oriented motion. Then, if an operator is in effect, the operation
  46.  * becomes character or line oriented accordingly. 
  47.  *
  48.  * Character motions are marked as being inclusive or not. Most char. motions
  49.  * are inclusive, but some (e.g. 'w') are not. 
  50.  *
  51.  * Generally speaking, every command in normal() should either clear any pending
  52.  * operator (with CLEAROP), or set the motion type variable. 
  53.  */
  54.  
  55. /*
  56.  * Motion types 
  57.  */
  58. #define MBAD    (-1)        /* 'bad' motion type marks unusable yank buf */
  59. #define MCHAR   0
  60. #define MLINE   1
  61.  
  62. static int      mtype;        /* type of the current cursor motion */
  63. static bool_t   mincl;        /* true if char motion is inclusive */
  64. static int      ybtype = MBAD;
  65. static int      ybcrossline = FALSE;
  66.  
  67. static LPtr     startop;    /* cursor pos. at start of operator */
  68.  
  69. /*
  70.  * Operators can have counts either before the operator, or between the
  71.  * operator and the following cursor motion as in: 
  72.  *
  73.  * d3w or 3dw 
  74.  *
  75.  * If a count is given before the operator, it is saved in opnum. If normal() is
  76.  * called with a pending operator, the count in opnum (if present) overrides
  77.  * any count that came later. 
  78.  */
  79. static int      opnum = 0;
  80.  
  81. #define DEFAULT1(x)     (((x) == 0) ? 1 : (x))
  82.  
  83. /*
  84.  * normal 
  85.  *
  86.  * Execute a command in normal mode. 
  87.  */
  88.  
  89. void
  90. normal(c)
  91.     char            c;
  92. {
  93.     char           *p;
  94.     int             n;
  95.     int             nn;
  96.     bool_t          flag = FALSE;
  97.     int             type = 0;    /* used in some operations to modify type */
  98.     int             dir = FORWARD;    /* search direction */
  99.     char            nchar = NUL;
  100.     bool_t          finish_op;
  101.     LPtr            temp_Curschar;
  102.  
  103.     last_command = NUL;
  104.     /*
  105.      * If there is an operator pending, then the command we take this time
  106.      * will terminate it. Finish_op tells us to finish the operation before
  107.      * returning this time (unless the operation was cancelled). 
  108.      */
  109.     finish_op = (operator != NOP);
  110.  
  111.     /*
  112.      * If we're in the middle of an operator AND we had a count before the
  113.      * operator, then that count overrides the current value of Prenum. What
  114.      * this means effectively, is that commands like "3dw" get turned into
  115.      * "d3w" which makes things fall into place pretty neatly. 
  116.      */
  117.     if (finish_op) {
  118.     if (opnum != 0)
  119.         Prenum = opnum;
  120.     } else
  121.     opnum = 0;
  122.  
  123.     switch (c) {
  124.  
  125.       case K_HELP:
  126.     CLEAROP;
  127.     if (help())
  128.         s_clear();
  129.     break;
  130.  
  131.       case CTRL('L'):
  132.     CLEAROP;
  133.     s_clear();
  134.     break;
  135.  
  136.       case CTRL('D'):
  137.     CLEAROP;
  138.     if (Prenum)
  139.         P(P_SS) = (Prenum > Rows - 1) ? Rows - 1 : Prenum;
  140.     scrollup((P(P_SS) < Rows) ? P(P_SS) : Rows - 1);
  141.     onedown((P(P_SS) < Rows) ? P(P_SS) : Rows - 1);
  142.     break;
  143.  
  144.       case CTRL('U'):
  145.     CLEAROP;
  146.     if (Prenum)
  147.         P(P_SS) = (Prenum > Rows - 1) ? Rows - 1 : Prenum;
  148.     scrolldown((P(P_SS) < Rows) ? P(P_SS) : Rows - 1);
  149.     oneup((P(P_SS) < Rows) ? P(P_SS) : Rows - 1);
  150.     break;
  151.  
  152.       case CTRL('F'):
  153.     CLEAROP;
  154.     if (nextline(Topchar) == NULL) {
  155.         beep();
  156.         break;
  157.     }
  158.     Prenum = DEFAULT1(Prenum);
  159.     while (Prenum > 0) {
  160.         *Curschar = *prevline(Botchar);
  161.         *Topchar = *Curschar;
  162.         Topchar->index = 0;
  163.         Update_Botchar();
  164.         Prenum--;
  165.     }
  166.     beginline(TRUE);
  167.     s_clear();
  168.     break;
  169.  
  170.       case CTRL('B'):
  171.     CLEAROP;
  172.     if (prevline(Topchar) == NULL) {
  173.         beep();
  174.         break;
  175.     }
  176.     Prenum = DEFAULT1(Prenum);
  177.     while (Prenum > 0) {
  178.         *Curschar = *Topchar;
  179.         n = Rows - 1;
  180.         {
  181.         LPtr           *lp = Curschar;
  182.         int             l = 0;
  183.  
  184.         while ((l < n) && (lp != NULL)) {
  185.             l += plines(lp->linep->s);
  186.             *Topchar = *lp;
  187.             lp = prevline(lp);
  188.         }
  189.         }
  190.         Topchar->index = 0;
  191.         Prenum--;
  192.     }
  193.     beginline(TRUE);
  194.     s_clear();
  195.     break;
  196.  
  197.       case CTRL('E'):
  198.     CLEAROP;
  199.     scrollup(DEFAULT1(Prenum));
  200.     if (LINEOF(Curschar) < LINEOF(Topchar))
  201.         Curschar->linep = Topchar->linep;
  202.     break;
  203.  
  204.       case CTRL('Y'):
  205.     CLEAROP;
  206.     scrolldown(DEFAULT1(Prenum));
  207.     Update_Botchar();
  208.     if (LINEOF(Curschar) >= LINEOF(Botchar)) {
  209.         LPtr           *lp;
  210.  
  211.         lp = prevline(Botchar);
  212.         if (lp == NULL)
  213.         lp = Topchar;
  214.         Curschar->linep = lp->linep;
  215.     }
  216.     break;
  217.  
  218.       case 'z':
  219.     CLEAROP;
  220.     S_CHECK_TOPCHAR_AND_BOTCHAR;
  221.     switch (vgetc()) {
  222.       case NL:        /* put Curschar at top of screen */
  223.       case CR:
  224.         *Topchar = *Curschar;
  225.         Topchar->index = 0;
  226.         break;
  227.  
  228.       case '.':        /* put Curschar in middle of screen */
  229.         n = Rows / 2;
  230.         goto dozcmd;
  231.  
  232.       case '-':        /* put Curschar at bottom of screen */
  233.         n = Rows - 1;
  234.         /* FALLTHROUGH */
  235.  
  236.     dozcmd:
  237.         {
  238.         register LPtr  *lp = Curschar;
  239.         register int    l = 0;
  240.  
  241.         while ((l < n) && (lp != NULL)) {
  242.             l += plines(lp->linep->s);
  243.             *Topchar = *lp;
  244.             lp = prevline(lp);
  245.         }
  246.         }
  247.         Topchar->index = 0;
  248.         break;
  249.  
  250.       default:
  251.         beep();
  252.     }
  253.     break;
  254.  
  255.       case CTRL('G'):
  256.     CLEAROP;
  257.     fileinfo();
  258.     break;
  259.  
  260.       case 'G':
  261.     mtype = MLINE;
  262.     *Curschar = *gotoline(Prenum);
  263.     if (!UndoInProgress) {
  264.         beginline(TRUE);
  265.         S_CHECK_TOPCHAR_AND_BOTCHAR;
  266.     }
  267.     break;
  268.  
  269.       case 'H':
  270.     mtype = MLINE;
  271.     *Curschar = *Topchar;
  272.     for (n = Prenum; n && onedown(1); n--);
  273.     beginline(TRUE);
  274.     break;
  275.  
  276.       case 'M':
  277.     mtype = MLINE;
  278.     *Curschar = *Topchar;
  279.     for (n = 0; n < Rows / 2 && onedown(1); n++);
  280.     beginline(TRUE);
  281.     break;
  282.  
  283.       case 'L':
  284.     mtype = MLINE;
  285.     *Curschar = *prevline(Botchar);
  286.     for (n = Prenum; n && oneup(1); n--);
  287.     beginline(TRUE);
  288.     break;
  289.  
  290.       case 'l':
  291.       case K_RARROW:
  292.       case ' ':
  293.     mtype = MCHAR;
  294.     mincl = FALSE;
  295.     n = DEFAULT1(Prenum);
  296.     while (n--) {
  297.         if (!oneright()) {
  298.         if (operator != DELETE && operator != CHANGE) {
  299.             beep();
  300.         } else {
  301.             if (lineempty(Curschar)) {
  302.             CLEAROP;
  303.             beep();
  304.             } else {
  305.             mincl = TRUE;
  306.             }
  307.         }
  308.         break;
  309.         }
  310.     }
  311.     set_want_col = TRUE;
  312.     break;
  313.  
  314.       case 'h':
  315.       case K_LARROW:
  316.       case CTRL('H'):
  317.     mtype = MCHAR;
  318.     mincl = FALSE;
  319.     Prenum = DEFAULT1(Prenum);
  320.     n = Prenum;
  321.     while (n--) {
  322.         if (!oneleft()) {
  323.         if (operator != DELETE && operator != CHANGE) {
  324.             beep();
  325.         } else if (Prenum == 1) {
  326.             CLEAROP;
  327.             beep();
  328.         }
  329.         break;
  330.         }
  331.     }
  332.     set_want_col = TRUE;
  333.     break;
  334.  
  335.       case '-':
  336.     flag = TRUE;
  337.     /* FALLTHROUGH */
  338.  
  339.       case 'k':
  340.       case K_UARROW:
  341.       case CTRL('P'):
  342.     mtype = MLINE;
  343.     if (!oneup(DEFAULT1(Prenum))) {
  344.         CLEAROP;
  345.         beep();
  346.     } else if (flag)
  347.         beginline(TRUE);
  348.     break;
  349.  
  350.       case '+':
  351.       case CR:
  352.       case NL:
  353.     flag = TRUE;
  354.     /* FALLTHROUGH */
  355.  
  356.       case 'j':
  357.       case K_DARROW:
  358.       case CTRL('N'):
  359.     mtype = MLINE;
  360.     if (!onedown(DEFAULT1(Prenum))) {
  361.         CLEAROP;
  362.         beep();
  363.     } else if (flag)
  364.         beginline(TRUE);
  365.     break;
  366.  
  367.     /*
  368.      * This is a strange motion command that helps make operators more
  369.      * logical. It is actually implemented, but not documented in the
  370.      * real 'vi'. This motion command actually refers to "the current
  371.      * line". Commands like "dd" and "yy" are really an alternate form of
  372.      * "d_" and "y_". It does accept a count, so "d3_" works to delete 3
  373.      * lines. 
  374.      */
  375.       case '_':
  376. lineop:
  377.     mtype = MLINE;
  378.     if (!onedown(DEFAULT1(Prenum) - 1)) {
  379.         CLEAROP;
  380.         beep();
  381.     } else
  382.         beginline(TRUE);
  383.     break;
  384.  
  385.       case '|':
  386.     mtype = MCHAR;
  387.     mincl = TRUE;
  388.     beginline(FALSE);
  389.     if (Prenum > 0)
  390.         coladvance(Curschar, Prenum - 1);
  391.     Curswant = Prenum - 1;
  392.     break;
  393.  
  394.       case CTRL(']'):        /* :ta to current identifier */
  395.     CLEAROP;
  396.     {
  397.         char            ch;
  398.         LPtr            save;
  399.  
  400.         save = *Curschar;
  401.         /*
  402.          * First back up to start of identifier. This doesn't match the
  403.          * real vi but I like it a little better and it shouldn't bother
  404.          * anyone. 
  405.          */
  406.         ch = gchar(Curschar);
  407.         while (IDCHAR(ch)) {
  408.         if (!oneleft())
  409.             break;
  410.         ch = gchar(Curschar);
  411.         }
  412.         if (!IDCHAR(ch))
  413.         oneright();
  414.  
  415.         stuffReadbuff(":ta ");
  416.         /*
  417.          * Now grab the chars in the identifier 
  418.          */
  419.         ch = gchar(Curschar);
  420.         while (IDCHAR(ch)) {
  421.         stuffReadbuff(mkstr(ch));
  422.         if (!oneright())
  423.             break;
  424.         ch = gchar(Curschar);
  425.         }
  426.         stuffReadbuff("\n");
  427.  
  428.         *Curschar = save;    /* restore, in case of error */
  429.     }
  430.     break;
  431.  
  432.       case '%':
  433.     S_CHECK_TOPCHAR_AND_BOTCHAR;
  434.     mtype = MCHAR;
  435.     mincl = TRUE;
  436.     {
  437.         LPtr           *pos;
  438.  
  439.         if ((pos = showmatch()) == NULL) {
  440.         CLEAROP;
  441.         beep();
  442.         } else {
  443.         setpcmark();
  444.         *Curschar = *pos;
  445.         set_want_col = TRUE;
  446.         }
  447.     }
  448.     break;
  449.  
  450.     /*
  451.      * Word Motions 
  452.      */
  453.  
  454.       case 'B':
  455.     type = 1;
  456.     /* FALLTHROUGH */
  457.  
  458.       case 'b':
  459.     mtype = MCHAR;
  460.     mincl = FALSE;
  461.     set_want_col = TRUE;
  462.     for (n = DEFAULT1(Prenum); n > 0; n--) {
  463.         LPtr           *pos;
  464.  
  465.         if ((Curschar->linep->prev == Filetop->linep)
  466.         && (Curschar->index == 0)) {
  467.         CLEAROP;
  468.         beep();
  469.         break;
  470.         }
  471.         pos = bck_word(Curschar, type);
  472.         if (pos == NULL) {
  473.         CLEAROP;
  474.         beep();
  475.         *Curschar = *gotoline(1);    /* goto top of file */
  476.         } else
  477.         *Curschar = *pos;
  478.     }
  479.     break;
  480.  
  481.       case 'W':
  482.     type = 1;
  483.     /* FALLTHROUGH */
  484.  
  485.       case 'w':
  486.     /*
  487.      * This is a little strange. To match what the real vi does, we
  488.      * effectively map 'cw' to 'ce', and 'cW' to 'cE'. This seems
  489.      * impolite at first, but it's really more what we mean when we say
  490.      * 'cw'. 
  491.      */
  492.     if (operator == CHANGE)
  493.         goto do_e_cmd;
  494.  
  495.     mtype = MCHAR;
  496.     mincl = FALSE;
  497.     set_want_col = TRUE;
  498.     for (n = DEFAULT1(Prenum); n > 0; n--) {
  499.         LPtr           *pos;
  500.  
  501.         if ((pos = fwd_word(Curschar, type)) == NULL) {
  502.         CLEAROP;
  503.         beep();
  504.         break;
  505.         } else
  506.         *Curschar = *pos;
  507.     }
  508.     if (operator == DELETE && DEFAULT1(Prenum) == 1) {
  509.         if (LINEOF(&startop) != LINEOF(Curschar)) {
  510.         *Curschar = startop;
  511.         while (oneright());
  512.         mincl = TRUE;
  513.         }
  514.     }
  515.     break;
  516.  
  517.       case 'E':
  518.     type = 1;
  519.     /* FALLTHROUGH */
  520.  
  521.       case 'e':
  522. do_e_cmd:
  523.     mtype = MCHAR;
  524.     mincl = TRUE;
  525.     set_want_col = TRUE;
  526.     if (c == 'e' || c == 'E') {
  527.         if (inc(Curschar) == -1) {
  528.         CLEAROP;
  529.         beep();
  530.         break;
  531.         }
  532.     }
  533.     for (n = DEFAULT1(Prenum); n > 0; n--) {
  534.         LPtr           *pos;
  535.  
  536.         if ((pos = end_word(Curschar, type)) == NULL) {
  537.         CLEAROP;
  538.         beep();
  539.         break;
  540.         } else
  541.         *Curschar = *pos;
  542.     }
  543.     break;
  544.  
  545.       case '$':
  546.     mtype = MCHAR;
  547.     mincl = TRUE;
  548.     while (oneright());
  549.     Curswant = 999;        /* so we stay at the end */
  550.     break;
  551.  
  552.       case '^':
  553.     flag = TRUE;
  554.     /* FALLTHROUGH */
  555.  
  556.       case '0':
  557.     mtype = MCHAR;
  558.     mincl = TRUE;
  559.     beginline(flag);
  560.     break;
  561.  
  562.       case 'R':
  563.     ResetBuffers();
  564.     AppendToRedobuff("R");
  565.     CLEAROP;
  566.     n = RowNumber(Curschar);
  567.     AppendPositionToUndobuff(Curschar->index, n);
  568.     AppendPositionToUndoUndobuff(Curschar->index, n);
  569.     AppendToUndoUndobuff("R");
  570.  
  571.     last_command = 'R';
  572.     startinsert(FALSE);
  573.     break;
  574.  
  575.       case 'A':
  576.     set_want_col = TRUE;
  577.     while (oneright());
  578.     ResetBuffers();
  579.     AppendToRedobuff("A");
  580.     goto doAPPENDcmd;
  581.  
  582.       case 'a':
  583.     ResetBuffers();
  584.     AppendToRedobuff("a");
  585.  
  586. doAPPENDcmd:
  587.     CLEAROP;
  588.     /* Works just like an 'i'nsert on the next character. */
  589.     n = RowNumber(Curschar);
  590.     AppendPositionToUndoUndobuff(Curschar->index, n);
  591.     AppendToUndoUndobuff("a");
  592.  
  593.     if (!lineempty(Curschar))
  594.         inc(Curschar);
  595.  
  596.     n = RowNumber(Curschar);
  597.     AppendPositionToUndobuff(Curschar->index, n);
  598.  
  599.     startinsert(FALSE);
  600.     break;
  601.  
  602.       case 'I':
  603.     beginline(TRUE);
  604.     ResetBuffers();
  605.     AppendToRedobuff("I");
  606.     goto doINSERTcmd;
  607.     /* FALLTHROUGH */
  608.  
  609.       case 'i':
  610.       case K_INSERT:
  611.     ResetBuffers();
  612.     AppendToRedobuff("i");
  613.  
  614. doINSERTcmd:
  615.     CLEAROP;
  616.  
  617.     n = RowNumber(Curschar);
  618.     AppendPositionToUndobuff(Curschar->index, n);
  619.     AppendPositionToUndoUndobuff(Curschar->index, n);
  620.     AppendToUndoUndobuff("i");
  621.  
  622.     startinsert(FALSE);
  623.     break;
  624.  
  625.       case 'o':
  626.     CLEAROP;
  627.     ResetBuffers();
  628.  
  629.     n = RowNumber(Curschar);
  630.     AppendToRedobuff("o");
  631.     AppendPositionToUndobuff(Curschar->index, n);
  632.     AppendPositionToUndoUndobuff(Curschar->index, n);
  633.     AppendToUndoUndobuff("o");
  634.  
  635.     if (OpenForward(!RedrawingDisabled))
  636.         startinsert(TRUE);
  637.  
  638.     last_command = 'o';
  639.     break;
  640.  
  641.       case 'O':
  642.     CLEAROP;
  643.     ResetBuffers();
  644.  
  645.     n = RowNumber(Curschar);
  646.     AppendToRedobuff("O");
  647.     AppendPositionToUndobuff(Curschar->index, n);
  648.     AppendPositionToUndoUndobuff(Curschar->index, n);
  649.     AppendToUndoUndobuff("O");
  650.  
  651.     if (OpenBackward(!RedrawingDisabled))
  652.         startinsert(TRUE);
  653.  
  654.     last_command = 'O';
  655.     break;
  656.  
  657.       case 'd':
  658.     if (operator == DELETE)    /* handle 'dd' */
  659.         goto lineop;
  660.     if (Prenum != 0)
  661.         opnum = Prenum;
  662.     startop = *Curschar;
  663.     operator = DELETE;
  664.     break;
  665.  
  666.     /*
  667.      * Some convenient abbreviations... 
  668.      */
  669.  
  670.       case 'x':
  671.     if (Prenum)
  672.         stuffnumReadbuff(Prenum);
  673.     stuffReadbuff("dl");
  674.     break;
  675.  
  676.       case 'X':
  677.     if (Prenum)
  678.         stuffnumReadbuff(Prenum);
  679.     stuffReadbuff("dh");
  680.     break;
  681.  
  682.       case 'D':
  683.     stuffReadbuff("d$");
  684.     break;
  685.  
  686.       case 'Y':
  687.     if (Prenum)
  688.         stuffnumReadbuff(Prenum);
  689.     stuffReadbuff("yy");
  690.     break;
  691.  
  692.       case 'C':
  693.     stuffReadbuff("c$");
  694.     break;
  695.  
  696.       case 'c':
  697.     if (operator == CHANGE) {    /* handle 'cc' */
  698.         CLEAROP;
  699.         stuffReadbuff("0c$");
  700.         break;
  701.     }
  702.     if (Prenum != 0)
  703.         opnum = Prenum;
  704.     startop = *Curschar;
  705.     operator = CHANGE;
  706.     break;
  707.  
  708.       case 'y':
  709.     if (operator == YANK)    /* handle 'yy' */
  710.         goto lineop;
  711.     if (Prenum != 0)
  712.         opnum = Prenum;
  713.     startop = *Curschar;
  714.     operator = YANK;
  715.     break;
  716.  
  717.       case ENABLE_REDRAWING:
  718.     RedrawingDisabled = FALSE;
  719.     S_NOT_VALID;
  720.     break;
  721.  
  722.       case 'p':
  723.     if (Yankbuffptr != NULL) {
  724.         doput(FORWARD);
  725.  
  726.         stuffReadbuff(ENABLE_REDRAWING_STR);
  727.         RedrawingDisabled = TRUE;
  728.     } else
  729.         beep();
  730.     break;
  731.  
  732.       case 'P':
  733.     if (Yankbuffptr != NULL) {
  734.         doput(BACKWARD);
  735.  
  736.         stuffReadbuff(ENABLE_REDRAWING_STR);
  737.         RedrawingDisabled = TRUE;
  738.     } else
  739.         beep();
  740.     break;
  741.  
  742.       case '>':
  743.     if (operator == RSHIFT)    /* handle >> */
  744.         goto lineop;
  745.     if (operator == LSHIFT) {
  746.         CLEAROP;
  747.         beep();
  748.         break;
  749.     }
  750.     if (Prenum != 0)
  751.         opnum = Prenum;
  752.     startop = *Curschar;    /* save current position */
  753.     operator = RSHIFT;
  754.     break;
  755.  
  756.       case '<':
  757.     if (operator == LSHIFT)    /* handle << */
  758.         goto lineop;
  759.     if (operator == RSHIFT) {
  760.         CLEAROP;
  761.         beep();
  762.         break;
  763.     }
  764.     if (Prenum != 0)
  765.         opnum = Prenum;
  766.     startop = *Curschar;    /* save current position */
  767.     operator = LSHIFT;
  768.     break;
  769.  
  770.       case 's':        /* substitute characters */
  771.     if (Prenum)
  772.         stuffnumReadbuff(Prenum);
  773.     stuffReadbuff("cl");
  774.     break;
  775.  
  776.       case '?':
  777.       case '/':
  778.       case ':':
  779.     CLEAROP;
  780.     readcmdline(c, (char *) NULL);
  781.     break;
  782.  
  783.       case 'n':
  784.     mtype = MCHAR;
  785.     mincl = FALSE;
  786.     set_want_col = TRUE;
  787.     if (!repsearch(0)) {
  788.         CLEAROP;
  789.         beep();
  790.     }
  791.     break;
  792.  
  793.       case 'N':
  794.     mtype = MCHAR;
  795.     mincl = FALSE;
  796.     set_want_col = TRUE;
  797.     if (!repsearch(1)) {
  798.         CLEAROP;
  799.         beep();
  800.     }
  801.     break;
  802.  
  803.     /*
  804.      * Character searches 
  805.      */
  806.       case 'T':
  807.     dir = BACKWARD;
  808.     /* FALLTHROUGH */
  809.  
  810.       case 't':
  811.     type = 1;
  812.     goto docsearch;
  813.  
  814.       case 'F':
  815.     dir = BACKWARD;
  816.     /* FALLTHROUGH */
  817.  
  818.       case 'f':
  819. docsearch:
  820.     mtype = MCHAR;
  821.     mincl = TRUE;
  822.     set_want_col = TRUE;
  823.     if ((nchar = vgetc()) == ESC)    /* search char */
  824.         break;
  825.     if (!searchc(nchar, dir, type)) {
  826.         CLEAROP;
  827.         beep();
  828.     }
  829.     break;
  830.  
  831.       case ',':
  832.     flag = 1;
  833.     /* FALLTHROUGH */
  834.  
  835.       case ';':
  836.     mtype = MCHAR;
  837.     mincl = TRUE;
  838.     set_want_col = TRUE;
  839.     if (!crepsearch(flag)) {
  840.         CLEAROP;
  841.         beep();
  842.     }
  843.     break;
  844.  
  845.     /*
  846.      * Function searches 
  847.      */
  848.  
  849.       case '[':
  850.     dir = BACKWARD;
  851.     /* FALLTHROUGH */
  852.  
  853.       case ']':
  854.     mtype = MLINE;
  855.     set_want_col = TRUE;
  856.     if (vgetc() != c) {
  857.         CLEAROP;
  858.         beep();
  859.         break;
  860.     }
  861.     if (!findfunc(dir)) {
  862.         CLEAROP;
  863.         beep();
  864.     }
  865.     break;
  866.  
  867.     /*
  868.      * Marks 
  869.      */
  870.  
  871.       case 'm':
  872.     CLEAROP;
  873.     if (!setmark(vgetc()))
  874.         beep();
  875.     break;
  876.  
  877.       case '\'':
  878.     flag = TRUE;
  879.     /* FALLTHROUGH */
  880.  
  881.       case '`':
  882.     S_CHECK_TOPCHAR_AND_BOTCHAR;
  883.     {
  884.         LPtr            mtmp;
  885.         LPtr           *mark = getmark(vgetc());
  886.  
  887.         if (mark == NULL) {
  888.         CLEAROP;
  889.         beep();
  890.         } else {
  891.         mtmp = *mark;
  892.         setpcmark();
  893.         *Curschar = mtmp;
  894.         if (flag)
  895.             beginline(TRUE);
  896.         }
  897.         mtype = flag ? MLINE : MCHAR;
  898.         mincl = TRUE;    /* ignored if not MCHAR */
  899.         set_want_col = TRUE;
  900.     }
  901.     break;
  902.  
  903.       case 'r':
  904.     CLEAROP;
  905.     if (lineempty(Curschar)) {    /* Nothing to replace */
  906.         beep();
  907.         break;
  908.     }
  909.     if ((nchar = vgetc()) == ESC)
  910.         break;
  911.  
  912.     Prenum = DEFAULT1(Prenum);
  913.     n = strlen(Curschar->linep->s) - Curschar->index;
  914.     if (n < Prenum) {
  915.         beep();
  916.         break;
  917.     }
  918.     ResetBuffers();
  919.  
  920.     nn = RowNumber(Curschar);
  921.     AppendPositionToUndobuff(Curschar->index, nn);
  922.     AppendPositionToUndoUndobuff(Curschar->index, nn);
  923.  
  924.     while (Prenum > 0) {
  925.         AppendToRedobuff("r");
  926.         AppendToRedobuff(mkstr(nchar));
  927.  
  928.         AppendToUndobuff("r");
  929.         AppendToUndobuff(mkstr(gchar(Curschar)));
  930.  
  931.         AppendToUndoUndobuff("r");
  932.         AppendToUndoUndobuff(mkstr(nchar));
  933.  
  934.         pchar(Curschar, nchar);    /* Change current character. */
  935.  
  936.         if (Prenum > 1) {
  937.         oneright();
  938.         AppendToRedobuff("l");
  939.         AppendToUndobuff("l");
  940.         AppendToUndoUndobuff("l");
  941.         }
  942.         Prenum--;
  943.     }
  944.  
  945.     CHANGED;
  946.     S_LINE_NOT_VALID;
  947.     break;
  948.  
  949.       case '~':        /* swap case */
  950.     CLEAROP;
  951.     if (lineempty(Curschar)) {
  952.         beep();
  953.         break;
  954.     }
  955.     ResetBuffers();
  956.  
  957.     n = RowNumber(Curschar);
  958.     AppendPositionToUndobuff(Curschar->index, n);
  959.     AppendPositionToUndoUndobuff(Curschar->index, n);
  960.  
  961.     Prenum = DEFAULT1(Prenum);
  962.     if (Prenum > 0) {
  963.         AppendNumberToRedobuff(Prenum);
  964.         AppendNumberToUndobuff(Prenum);
  965.         AppendNumberToUndoUndobuff(Prenum);
  966.     }
  967.     AppendToRedobuff("~");
  968.     AppendToUndobuff("~");
  969.     AppendToUndoUndobuff("~");
  970.  
  971.     while (Prenum > 0) {
  972.         c = gchar(Curschar);
  973.         if (isalpha(c)) {
  974.         if (islower(c))
  975.             pchar(Curschar, toupper(c));
  976.         else
  977.             pchar(Curschar, tolower(c));
  978.         }
  979.         if (!oneright())
  980.         break;
  981.         Prenum--;
  982.     }
  983.  
  984.     CHANGED;
  985.     S_LINE_NOT_VALID;
  986.     break;
  987.  
  988.       case UNDO_SHIFTJ:
  989.     CLEAROP;
  990.     if (UndoInProgress) {
  991.         (void) dojoin(FALSE, FALSE);
  992.         break;
  993.     }
  994.     goto doSHIFTJcommand;
  995.  
  996.       case 'J':
  997.     CLEAROP;
  998. doSHIFTJcommand:
  999.     if (nextline(Curschar) == NULL) {    /* on last line */
  1000.         beep();
  1001.         break;
  1002.     }
  1003.     ResetBuffers();
  1004.  
  1005.     temp_Curschar = *Curschar;
  1006.     nn = strlen(Curschar->linep->s);
  1007.     if (nn < 0)
  1008.         nn = 0;
  1009.     n = RowNumber(&temp_Curschar);
  1010.  
  1011.     AppendToRedobuff("J");
  1012.  
  1013.     AppendPositionToUndobuff(nn, n);
  1014.  
  1015.     AppendPositionToUndoUndobuff(0, n);
  1016.     AppendToUndoUndobuff("J");
  1017.  
  1018.     if (linewhite(nextline(Curschar))) {
  1019.         AppendToUndobuff("a\n");
  1020.         if (!dojoin(FALSE, TRUE)) {
  1021.         beep();
  1022.         break;
  1023.         }
  1024.     } else if (lineempty(Curschar)) {
  1025.         AppendToUndobuff("i\n");
  1026.         if (!dojoin(FALSE, TRUE)) {
  1027.         beep();
  1028.         break;
  1029.         }
  1030.     } else {
  1031.         AppendToUndobuff("dli\n");
  1032.         if (!dojoin(TRUE, TRUE)) {
  1033.         beep();
  1034.         break;
  1035.         }
  1036.     }
  1037.  
  1038.     AppendToUndobuff(ESC_STR);
  1039.     AppendPositionToUndobuff(nn, n);
  1040.     break;
  1041.  
  1042.       case K_CGRAVE:        /* shorthand command */
  1043.     CLEAROP;
  1044.     stuffReadbuff(":e #\n");
  1045.     break;
  1046.  
  1047.       case 'Z':        /* write, if changed, and exit */
  1048.     if (vgetc() != 'Z') {
  1049.         beep();
  1050.         break;
  1051.     }
  1052.     if (Changed) {
  1053.         if (Filename != NULL) {
  1054.         if (!writeit(Filename, (LPtr *) NULL, (LPtr *) NULL))
  1055.             return;
  1056.         } else {
  1057.         emsg("No output file");
  1058.         return;
  1059.         }
  1060.     }
  1061.     getout(0);
  1062.     break;
  1063.  
  1064.       case '.':
  1065.     CLEAROP;
  1066.     if (Redobuffptr != NULL) {
  1067.         stuffReadbuff(Redobuff);
  1068.  
  1069.         stuffReadbuff(ENABLE_REDRAWING_STR);
  1070.         RedrawingDisabled = TRUE;
  1071.     } else
  1072.         beep();
  1073.     break;
  1074.  
  1075.       case 'u':
  1076.       case K_UNDO:
  1077.     CLEAROP;
  1078.     if (UndoInProgress) {
  1079.         p = UndoUndobuff;
  1080.         UndoUndobuff = Undobuff;
  1081.         Undobuff = p;
  1082.         p = UndoUndobuffptr;
  1083.         UndoUndobuffptr = Undobuffptr;
  1084.         Undobuffptr = p;
  1085.  
  1086.         UndoInProgress = FALSE;
  1087.         RedrawingDisabled = FALSE;
  1088.         S_NOT_VALID;
  1089.     } else if (Undobuffptr != NULL) {
  1090.         stuffReadbuff(Undobuff);
  1091.         stuffReadbuff("u");
  1092.         UndoInProgress = TRUE;
  1093.         RedrawingDisabled = TRUE;
  1094.     } else {
  1095.         beep();
  1096.     }
  1097.     break;
  1098.  
  1099.       default:
  1100.     CLEAROP;
  1101.     beep();
  1102.     break;
  1103.     }
  1104.  
  1105.     /*
  1106.      * If an operation is pending, handle it... 
  1107.      */
  1108.     if (finish_op) {        /* we just finished an operator */
  1109.     if (operator == NOP)    /* ... but it was cancelled */
  1110.         return;
  1111.  
  1112.     switch (operator) {
  1113.  
  1114.       case LSHIFT:
  1115.       case RSHIFT:
  1116.         ResetBuffers();
  1117.  
  1118.         n = RowNumber(&startop);
  1119.         AppendPositionToUndobuff(startop.index, n);
  1120.         AppendPositionToUndoUndobuff(startop.index, n);
  1121.         if (Prenum != 0) {
  1122.         AppendNumberToRedobuff(Prenum);
  1123.         AppendNumberToUndobuff(Prenum);
  1124.         AppendNumberToUndoUndobuff(Prenum);
  1125.         }
  1126.         AppendToRedobuff((operator == LSHIFT) ? "<" : ">");
  1127.         AppendToUndobuff((operator == LSHIFT) ? ">" : "<");
  1128.         AppendToUndoUndobuff((operator == LSHIFT) ? "<" : ">");
  1129.         AppendToRedobuff(mkstr(c));
  1130.         if (c == '>')
  1131.         AppendToUndobuff("<");
  1132.         else if (c == '<')
  1133.         AppendToUndobuff(">");
  1134.         else
  1135.         AppendToUndobuff(mkstr(c));
  1136.         AppendToUndoUndobuff(mkstr(c));
  1137.  
  1138.         doshift(operator);
  1139.         break;
  1140.  
  1141.       case DELETE:
  1142.         ResetBuffers();
  1143.  
  1144.         n = RowNumber(&startop);
  1145.         AppendPositionToUndoUndobuff(startop.index, n);
  1146.  
  1147.         if (lt(&startop, Curschar))
  1148.         temp_Curschar = startop;
  1149.         else
  1150.             temp_Curschar = *Curschar;
  1151.         n = RowNumber(&temp_Curschar);
  1152.         if (Prenum != 0) {
  1153.         AppendNumberToRedobuff(Prenum);
  1154.         AppendNumberToUndoUndobuff(Prenum);
  1155.         }
  1156.         AppendToRedobuff("d");
  1157.         AppendToUndoUndobuff("d");
  1158.         AppendToRedobuff(mkstr(c));
  1159.         AppendToUndoUndobuff(mkstr(c));
  1160.         if (nchar != NUL) {
  1161.         AppendToRedobuff(mkstr(nchar));
  1162.         AppendToUndoUndobuff(mkstr(nchar));
  1163.         }
  1164.         AppendPositionToUndobuff(temp_Curschar.index, n);
  1165.  
  1166.         dodelete(!UndoInProgress, !UndoInProgress, !UndoInProgress);
  1167.  
  1168.         AppendPositionToUndobuff(temp_Curschar.index, n);
  1169.         break;
  1170.  
  1171.       case YANK:
  1172.         ResetBuffers();    /* no redo/undo/(undo of undo) on yank... */
  1173.         if (!doyank())
  1174.         msg("yank buffer exceeded");
  1175.         if (!ybcrossline)
  1176.         *Curschar = startop;
  1177.         else if (lt(&startop, Curschar))
  1178.         *Curschar = startop;
  1179.         break;
  1180.  
  1181.       case CHANGE:
  1182.         ResetBuffers();
  1183.  
  1184.         n = RowNumber(&startop);
  1185.         AppendPositionToUndoUndobuff(startop.index, n);
  1186.  
  1187.         if (lt(&startop, Curschar))
  1188.         temp_Curschar = startop;
  1189.         else
  1190.             temp_Curschar = *Curschar;
  1191.         n = RowNumber(&temp_Curschar);
  1192.         if (mtype == MLINE)
  1193.         AppendPositionToUndobuff(0, n);
  1194.         else
  1195.         AppendPositionToUndobuff(temp_Curschar.index, n);
  1196.  
  1197.         if (Prenum != 0) {
  1198.         AppendNumberToRedobuff(Prenum);
  1199.         AppendNumberToUndoUndobuff(Prenum);
  1200.         }
  1201.         AppendToRedobuff("c");
  1202.         AppendToUndoUndobuff("c");
  1203.         AppendToRedobuff(mkstr(c));
  1204.         AppendToUndoUndobuff(mkstr(c));
  1205.         if (nchar != NUL) {
  1206.         AppendToRedobuff(mkstr(nchar));
  1207.         AppendToUndoUndobuff(mkstr(nchar));
  1208.         }
  1209.         dochange();
  1210.  
  1211.         last_command = 'c';
  1212.         break;
  1213.  
  1214.       default:
  1215.         beep();
  1216.     }
  1217.     operator = NOP;
  1218.     }
  1219. }
  1220.  
  1221. /*
  1222.  * tabinout(shift_type, num) 
  1223.  *
  1224.  * If shift_type == RSHIFT, add a tab to the begining of the next num lines;
  1225.  * otherwise delete a tab from the beginning of the next num lines. 
  1226.  */
  1227. static void
  1228. tabinout(shift_type, num)
  1229.     int             shift_type;
  1230.     int             num;
  1231. {
  1232.     LPtr           *p;
  1233.  
  1234.     beginline(FALSE);
  1235.     while (num-- > 0) {
  1236.     beginline(FALSE);
  1237.     if (shift_type == RSHIFT)
  1238.         inschar(TAB);
  1239.     else {
  1240.         if (gchar(Curschar) == TAB)
  1241.         delchar(TRUE, FALSE);
  1242.     }
  1243.     if (num > 0) {
  1244.         if ((p = nextline(Curschar)) != NULL)
  1245.         *Curschar = *p;
  1246.         else
  1247.         break;
  1248.     }
  1249.     }
  1250. }
  1251.  
  1252. /*
  1253.  * doshift - handle a shift operation 
  1254.  */
  1255. static void
  1256. doshift(op)
  1257.     int             op;
  1258. {
  1259.     LPtr            top, bot;
  1260.     int             nlines;
  1261.  
  1262.     top = startop;
  1263.     bot = *Curschar;
  1264.  
  1265.     if (lt(&bot, &top))
  1266.     pswap(top, bot);
  1267.  
  1268.     nlines = cntllines(&top, &bot);
  1269.     *Curschar = top;
  1270.     tabinout(op, nlines);
  1271.  
  1272.     /*
  1273.      * The cursor position afterward is the prior of the two positions. 
  1274.      */
  1275.     *Curschar = top;
  1276.  
  1277.     /*
  1278.      * If we were on the last char of a line that got shifted left, then move
  1279.      * left one so we aren't beyond the end of the line 
  1280.      */
  1281.     if (gchar(Curschar) == NUL && Curschar->index > 0)
  1282.     Curschar->index--;
  1283.  
  1284.     if (op == RSHIFT)
  1285.     oneright();
  1286.     else
  1287.     oneleft();
  1288.  
  1289.     S_NOT_VALID;
  1290.  
  1291.     if (nlines > P(P_RP))
  1292.     smsg("%d lines %ced", nlines, (op == RSHIFT) ? '>' : '<');
  1293. }
  1294.  
  1295. /*
  1296.  * dodelete - handle a delete operation 
  1297.  */
  1298. static void
  1299. dodelete(redraw, setup_for_undo, try_to_yank)
  1300.     bool_t          redraw;
  1301.     bool_t          setup_for_undo;
  1302.     bool_t          try_to_yank;
  1303. {
  1304.     LPtr            top, bot;
  1305.     int             nlines;
  1306.     int             n;
  1307.  
  1308.     /*
  1309.      * Do a yank of whatever we're about to delete. If there's too much stuff
  1310.      * to fit in the yank buffer, then get a confirmation before doing the
  1311.      * delete. This is crude, but simple. And it avoids doing a delete of
  1312.      * something we can't put back if we want. 
  1313.      */
  1314.     if (try_to_yank) {
  1315.     if (!doyank()) {
  1316.         msg("yank buffer exceeded: press <y> to confirm");
  1317.         if (vgetc() != 'y') {
  1318.         emsg("delete aborted");
  1319.         *Curschar = startop;
  1320.         return;
  1321.         }
  1322.     }
  1323.     }
  1324.     top = startop;
  1325.     bot = *Curschar;
  1326.  
  1327.     if (lt(&bot, &top))
  1328.     pswap(top, bot);
  1329.  
  1330.     *Curschar = top;
  1331.     nlines = cntllines(&top, &bot);
  1332.  
  1333.     if (mtype == MLINE) {
  1334.     if (operator == CHANGE) {
  1335.         last_command_char = 'a';
  1336.         delline(nlines - 1);
  1337.         Curschar->index = 0;
  1338.         while (delchar(TRUE, FALSE));
  1339.     } else {
  1340.         if ((Filetop->linep->next == top.linep) &&
  1341.         (bot.linep->next == Fileend->linep))
  1342.         last_command_char = 'a';
  1343.         else if (bot.linep->next == Fileend->linep)
  1344.         last_command_char = 'o';
  1345.         else
  1346.         last_command_char = 'O';
  1347.         if (setup_for_undo)
  1348.         AppendToUndobuff(mkstr(last_command_char));
  1349.         delline(nlines);
  1350.     }
  1351.     } else if (top.linep == bot.linep) {    /* del. within line */
  1352.     if (!mincl)
  1353.         dec(&bot);
  1354.  
  1355.     if (endofline(&bot))
  1356.         last_command_char = 'a';
  1357.     else
  1358.         last_command_char = 'i';
  1359.     if (setup_for_undo)
  1360.         AppendToUndobuff(mkstr(last_command_char));
  1361.     n = bot.index - top.index + 1;
  1362.     while (n--)
  1363.         if (!delchar(TRUE, FALSE))
  1364.         break;
  1365.     } else {            /* del. between lines */
  1366.     if (endofline(&top)) {
  1367.         if (nextline(&top)) {
  1368.         if (lineempty(nextline(&top)))
  1369.             last_command_char = 'a';
  1370.         else
  1371.             last_command_char = 'i';
  1372.         } else {
  1373.         last_command_char = 'a';
  1374.         }
  1375.     } else {
  1376.         last_command_char = 'i';
  1377.     }
  1378.     if (setup_for_undo)
  1379.         AppendToUndobuff(mkstr(last_command_char));
  1380.  
  1381.     n = Curschar->index;
  1382.     while (Curschar->index >= n)
  1383.         if (!delchar(TRUE, FALSE))
  1384.         break;
  1385.  
  1386.     top = *Curschar;
  1387.     *Curschar = *nextline(Curschar);
  1388.     delline(nlines - 2);
  1389.     Curschar->index = 0;
  1390.     n = bot.index;
  1391.     if (!mincl)
  1392.         n--;
  1393.  
  1394.     while (n-- >= 0)
  1395.         if (!delchar(TRUE, FALSE))
  1396.         break;
  1397.     *Curschar = top;
  1398.     dojoin(FALSE, FALSE);
  1399.     }
  1400.  
  1401.     if (mtype == MCHAR && nlines == 1 && redraw && P(P_NU) == FALSE) {
  1402.     S_LINE_NOT_VALID;
  1403.     } else {
  1404.     S_NOT_VALID;
  1405.     }
  1406.  
  1407.     if (nlines > P(P_RP))
  1408.     smsg("%d fewer lines", nlines);
  1409.  
  1410.     if (setup_for_undo) {
  1411.     AppendToUndobuff(Yankbuff);
  1412.     AppendToUndobuff(ESC_STR);
  1413.     }
  1414. }
  1415.  
  1416. /*
  1417.  * dochange - handle a change operation 
  1418.  */
  1419. static void
  1420. dochange()
  1421. {
  1422.     LPtr            l;
  1423.  
  1424.     if (lt(Curschar, &startop))
  1425.     l = *Curschar;
  1426.     else
  1427.     l = startop;
  1428.  
  1429.     dodelete(FALSE, FALSE, !UndoInProgress);
  1430.  
  1431.     if ((l.index > Curschar->index) && !lineempty(Curschar))
  1432.     inc(Curschar);
  1433.  
  1434.     startinsert(FALSE);
  1435. }
  1436.  
  1437. static          bool_t
  1438. doyank()
  1439. {
  1440.     LPtr            top, bot;
  1441.     char           *ybend = &Yankbuff[YANKSIZE - 1];
  1442.     int             nlines;
  1443.  
  1444.     Yankbuffptr = Yankbuff;
  1445.  
  1446.     top = startop;
  1447.     bot = *Curschar;
  1448.  
  1449.     if (lt(&bot, &top))
  1450.     pswap(top, bot);
  1451.  
  1452.     nlines = cntllines(&top, &bot);
  1453.  
  1454.     ybtype = mtype;        /* set the yank buffer type */
  1455.     ybcrossline = FALSE;
  1456.     if (LINEOF(&top) != LINEOF(&bot))
  1457.     ybcrossline = TRUE;
  1458.  
  1459.     if (mtype == MLINE) {
  1460.     ybcrossline = TRUE;
  1461.     top.index = 0;
  1462.     bot.index = strlen(bot.linep->s);
  1463.     /*
  1464.      * The following statement checks for the special case of yanking a
  1465.      * blank line at the beginning of the file. If not handled right, we
  1466.      * yank an extra char (a newline). 
  1467.      */
  1468.     if (dec(&bot) == -1) {
  1469.         *Yankbuff = NUL;
  1470.         Yankbuffptr = NULL;
  1471.         return TRUE;
  1472.     }
  1473.     } else {
  1474.     if (!mincl)
  1475.         if (!equal(&top, &bot))
  1476.         dec(&bot);
  1477.     }
  1478.  
  1479.     for (; ltoreq(&top, &bot); inc(&top)) {
  1480.     *Yankbuffptr = (gchar(&top) != NUL) ? gchar(&top) : NL;
  1481.     Yankbuffptr++;
  1482.     if (Yankbuffptr >= ybend) {
  1483.         *Yankbuffptr = NUL;
  1484.         msg("yank too big for buffer");
  1485.         ybtype = MBAD;
  1486.         return FALSE;
  1487.     }
  1488.     }
  1489.  
  1490.     *Yankbuffptr = NUL;
  1491.  
  1492.     if (operator == YANK)
  1493.     if (nlines > P(P_RP))
  1494.         smsg("%d lines yanked", nlines);
  1495.  
  1496.     return TRUE;
  1497. }
  1498.  
  1499. static void
  1500. doput(dir)
  1501.     int             dir;
  1502. {
  1503.     bool_t          type;
  1504.  
  1505.     if (ybtype == MBAD) {
  1506.     beep();
  1507.     return;
  1508.     }
  1509.     type = (ybtype == MCHAR);
  1510.     if (dir == FORWARD)
  1511.     stuffReadbuff(type ? "a" : "o");
  1512.     else
  1513.     stuffReadbuff(type ? "i" : "O");
  1514.  
  1515.     stuffReadbuff(Yankbuff);
  1516.     stuffReadbuff(ESC_STR);
  1517.  
  1518.     if (ybtype != MCHAR)
  1519.     stuffReadbuff("^");
  1520. }
  1521.  
  1522. static void
  1523. startinsert(startln)
  1524.     int             startln;    /* if set, insert at start of line */
  1525. {
  1526.     *Insstart = *Curschar;
  1527.     if (startln) {
  1528.     Insstart->index = 0;
  1529.     }
  1530.     *Insbuff = NUL;
  1531.     Insbuffptr = NULL;
  1532.  
  1533.     State = INSERT;
  1534.     if (P(P_MO)) {
  1535.     if (last_command == 'R')
  1536.         msg("Replace Mode");
  1537.     else
  1538.         msg("Insert Mode");
  1539.     }
  1540. }
  1541.  
  1542. void
  1543. ResetBuffers()
  1544. {
  1545.     if (UndoInProgress)
  1546.     return;
  1547.  
  1548.     *Redobuff = NUL;
  1549.     Redobuffptr = NULL;
  1550.  
  1551.     *Undobuff = NUL;
  1552.     Undobuffptr = NULL;
  1553.  
  1554.     *UndoUndobuff = NUL;
  1555.     UndoUndobuffptr = NULL;
  1556. }
  1557.  
  1558. void
  1559. AppendToInsbuff(s)
  1560.     char           *s;
  1561. {
  1562.     if (UndoInProgress)
  1563.     return;
  1564.  
  1565.     if (Insbuffptr == NULL) {
  1566.     if ((strlen(s) + 1) < INSERT_SIZE) {
  1567.         strcpy(Insbuff, s);
  1568.         Insbuffptr = Insbuff;
  1569.         return;
  1570.     }
  1571.     } else if ((strlen(Insbuff) + strlen(s) + 1) < INSERT_SIZE) {
  1572.     strcat(Insbuff, s);
  1573.     return;
  1574.     }
  1575.     emsg("Couldn't AppendToInsbuff() - clearing Insbuff\n");
  1576.     *Insbuff = NUL;
  1577.     Insbuffptr = NULL;
  1578. }
  1579.  
  1580. void
  1581. AppendToRedobuff(s)
  1582.     char           *s;
  1583. {
  1584.     if (UndoInProgress)
  1585.     return;
  1586.  
  1587.     if (Redobuffptr == (char *) (-2)) {
  1588.     return;
  1589.     }
  1590.     if (Redobuffptr == (char *) (-1)) {
  1591.     Redobuffptr = (char *) (-2);
  1592.     emsg("Couldn't AppendToRedobuff() - Redobuff corrupt");
  1593.     return;
  1594.     }
  1595.     if (Redobuffptr == NULL) {
  1596.     if ((strlen(s) + 1) < REDO_UNDO_SIZE) {
  1597.         strcpy(Redobuff, s);
  1598.         Redobuffptr = Redobuff;
  1599.         return;
  1600.     }
  1601.     } else if ((strlen(Redobuff) + strlen(s) + 1) < REDO_UNDO_SIZE) {
  1602.     strcat(Redobuff, s);
  1603.     return;
  1604.     }
  1605.     emsg("Couldn't AppendToRedobuff() - clearing Redobuff");
  1606.     *Redobuff = NUL;
  1607.     Redobuffptr = (char *) (-1);
  1608. }
  1609.  
  1610. void
  1611. AppendNumberToRedobuff(n)
  1612.     int             n;
  1613. {
  1614.     char            buf[32];
  1615.  
  1616.     if (UndoInProgress)
  1617.     return;
  1618.  
  1619.     sprintf(buf, "%d", n);
  1620.     AppendToRedobuff(buf);
  1621. }
  1622.  
  1623. void
  1624. AppendToUndobuff(s)
  1625.     char           *s;
  1626. {
  1627.     if (UndoInProgress)
  1628.     return;
  1629.  
  1630.     if (Undobuffptr == (char *) (-2)) {
  1631.     return;
  1632.     }
  1633.     if (Undobuffptr == (char *) (-1)) {
  1634.     Undobuffptr = (char *) (-2);
  1635.     emsg("Couldn't AppendToUndobuff() - Undobuff corrupt");
  1636.     return;
  1637.     }
  1638.     if (Undobuffptr == NULL) {
  1639.     if ((strlen(s) + 1) < REDO_UNDO_SIZE) {
  1640.         strcpy(Undobuff, s);
  1641.         Undobuffptr = Undobuff;
  1642.         return;
  1643.     }
  1644.     } else if ((strlen(Undobuff) + strlen(s) + 1) < REDO_UNDO_SIZE) {
  1645.     strcat(Undobuff, s);
  1646.     return;
  1647.     }
  1648.     emsg("Couldn't AppendToUndobuff() - clearing Undobuff");
  1649.     *Undobuff = NUL;
  1650.     Undobuffptr = (char *) (-1);
  1651. }
  1652.  
  1653. void
  1654. AppendNumberToUndobuff(n)
  1655.     int             n;
  1656. {
  1657.     char            buf[32];
  1658.  
  1659.     if (UndoInProgress)
  1660.     return;
  1661.  
  1662.     sprintf(buf, "%d", n);
  1663.     AppendToUndobuff(buf);
  1664. }
  1665.  
  1666. void
  1667. AppendPositionToUndobuff(column, row)
  1668.     int             column;
  1669.     int             row;
  1670. {
  1671.     if (UndoInProgress)
  1672.     return;
  1673.  
  1674.     AppendNumberToUndobuff(row);
  1675.     AppendToUndobuff("G");
  1676.     AppendNumberToUndobuff(column);
  1677.     if (column)
  1678.     AppendToUndobuff("l");
  1679. }
  1680.  
  1681. void
  1682. AppendToUndoUndobuff(s)
  1683.     char           *s;
  1684. {
  1685.     if (UndoInProgress)
  1686.     return;
  1687.  
  1688.     if (UndoUndobuffptr == (char *) (-2)) {
  1689.     return;
  1690.     }
  1691.     if (UndoUndobuffptr == (char *) (-1)) {
  1692.     UndoUndobuffptr = (char *) (-2);
  1693.     emsg("Couldn't AppendToUndoUndobuff() - UndoUndobuff corrupt");
  1694.     return;
  1695.     }
  1696.     if (UndoUndobuffptr == NULL) {
  1697.     if ((strlen(s) + 1) < REDO_UNDO_SIZE) {
  1698.         strcpy(UndoUndobuff, s);
  1699.         UndoUndobuffptr = Undobuff;
  1700.         return;
  1701.     }
  1702.     } else if ((strlen(UndoUndobuff) + strlen(s) + 1) < REDO_UNDO_SIZE) {
  1703.     strcat(UndoUndobuff, s);
  1704.     return;
  1705.     }
  1706.     emsg("Couldn't AppendToUndoUndobuff() - clearing UndoUndobuff");
  1707.     *UndoUndobuff = NUL;
  1708.     UndoUndobuffptr = (char *) (-1);
  1709. }
  1710.  
  1711. void
  1712. AppendNumberToUndoUndobuff(n)
  1713.     int             n;
  1714. {
  1715.     char            buf[32];
  1716.  
  1717.     if (UndoInProgress)
  1718.     return;
  1719.  
  1720.     sprintf(buf, "%d", n);
  1721.     AppendToUndoUndobuff(buf);
  1722. }
  1723.  
  1724. void
  1725. AppendPositionToUndoUndobuff(column, row)
  1726.     int             column;
  1727.     int             row;
  1728. {
  1729.     if (UndoInProgress)
  1730.     return;
  1731.  
  1732.     AppendNumberToUndoUndobuff(row);
  1733.     AppendToUndoUndobuff("G");
  1734.     AppendNumberToUndoUndobuff(column);
  1735.     if (column)
  1736.     AppendToUndoUndobuff("l");
  1737. }
  1738.  
  1739. static          bool_t
  1740. dojoin(leading_space, strip_leading_spaces)
  1741.     bool_t          leading_space;
  1742.     bool_t          strip_leading_spaces;
  1743. {
  1744.     int             scol;    /* save cursor column */
  1745.     int             currsize;    /* size of the current line */
  1746.     int             nextsize;    /* size of the next line */
  1747.  
  1748.     if (nextline(Curschar) == NULL)    /* on last line */
  1749.     return FALSE;
  1750.  
  1751.     nextsize = strlen(Curschar->linep->next->s);
  1752.     if (!canincrease(nextsize))
  1753.     return FALSE;
  1754.  
  1755.     currsize = strlen(Curschar->linep->s);
  1756.  
  1757.     while (oneright());        /* to end of line */
  1758.  
  1759.     strcat(Curschar->linep->s, Curschar->linep->next->s);
  1760.  
  1761.     /*
  1762.      * Delete the following line. To do this we move the cursor there
  1763.      * briefly, and then move it back. Don't back up if the delete made us
  1764.      * the last line. 
  1765.      */
  1766.     Curschar->linep = Curschar->linep->next;
  1767.     scol = Curschar->index;
  1768.  
  1769.     if (nextline(Curschar) != NULL) {
  1770.     delline(1);
  1771.     Curschar->linep = Curschar->linep->prev;
  1772.     } else
  1773.     delline(1);
  1774.  
  1775.     Curschar->index = scol;
  1776.  
  1777.     if (currsize)
  1778.     oneright();        /* go to first char. of joined line */
  1779.  
  1780.     if (nextsize != 0 && strip_leading_spaces) {
  1781.     /*
  1782.      * Delete leading white space on the joined line and insert a single
  1783.      * space. 
  1784.      */
  1785.     while (gchar(Curschar) == ' ' || gchar(Curschar) == TAB) {
  1786.         delchar(TRUE, TRUE);
  1787.     }
  1788.     if (leading_space)
  1789.         inschar(' ');
  1790.     }
  1791.     CHANGED;
  1792.  
  1793.     return TRUE;
  1794. }
  1795.  
  1796. /*
  1797.  * linewhite() - returns TRUE if the line consists only of white space
  1798.  */
  1799.  
  1800. bool_t
  1801. linewhite(p)
  1802.     LPtr           *p;
  1803. {
  1804.     register int    i;
  1805.     register char   c;
  1806.  
  1807.     i = 1;
  1808.     c = p->linep->s[0];
  1809.     while (c != NUL) {
  1810.     if (c != ' ' && c != '\t')
  1811.         return (FALSE);
  1812.     c = p->linep->s[i++];
  1813.     }
  1814.  
  1815.     return (TRUE);
  1816. }
  1817.