home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / UE311C.ZIP / LINE.C < prev    next >
C/C++ Source or Header  |  1991-10-07  |  26KB  |  1,053 lines

  1. /*
  2.  * The functions in this file are a general set of line management utilities.
  3.  * They are the only routines that touch the text. They also touch the buffer
  4.  * and window structures, to make sure that the necessary updating gets done.
  5.  * There are routines in this file that handle the kill buffer too. It isn't
  6.  * here for any good reason.
  7.  *
  8.  * Note that this code only updates the dot and mark values in the window list.
  9.  * Since all the code acts on the current window, the buffer that we are
  10.  * editing must be being displayed, which means that "b_nwnd" is non zero,
  11.  * which means that the dot and mark values in the buffer headers are nonsense.
  12.  */
  13.  
  14. #include    <stdio.h>
  15. #include    "estruct.h"
  16. #include    "eproto.h"
  17. #include    "edef.h"
  18. #include    "elang.h"
  19.  
  20. #define    BSIZE(a)    (a + NBLOCK - 1) & (~(NBLOCK - 1))
  21.  
  22. /*
  23.  * This routine allocates a block of memory large enough to hold a LINE
  24.  * containing "used" characters. Return a pointer to the new block, or
  25.  * NULL if there isn't any memory left. Print a message in the message
  26.  * line if no space.
  27.  */
  28.  
  29. LINE *PASCAL NEAR lalloc(used)
  30.  
  31. register int used;
  32.  
  33. {
  34.     register LINE    *lp;
  35.  
  36.     if ((lp = (LINE *)malloc(sizeof(LINE)+used)) == NULL) {
  37.         mlwrite(TEXT94);
  38. /*                      "%%Out of memory" */
  39.         return(NULL);
  40.     }
  41.     lp->l_size = used;
  42.     lp->l_used = used;
  43.     return(lp);
  44. }
  45.  
  46. /*
  47.  * Delete line "lp". Fix all of the links that might point at it (they are
  48.  * moved to offset 0 of the next line. Unlink the line from whatever buffer it
  49.  * might be in. Release the memory. The buffers are updated too; the magic
  50.  * conditions described in the above comments don't hold here.
  51.  */
  52. PASCAL NEAR lfree(lp)
  53. register LINE    *lp;
  54. {
  55.     register BUFFER *bp;
  56.     SCREEN *scrp;        /* screen to fix pointers in */
  57.     register WINDOW *wp;
  58.     register int cmark;        /* current mark */
  59.  
  60.     /* in all screens.... */
  61.     scrp = first_screen;
  62.     while (scrp) {
  63.         wp = scrp->s_first_window;
  64.         while (wp != NULL) {
  65.             if (wp->w_linep == lp)
  66.                 wp->w_linep = lp->l_fp;
  67.             if (wp->w_dotp    == lp) {
  68.                 wp->w_dotp  = lp->l_fp;
  69.                 wp->w_doto  = 0;
  70.             }
  71.             for (cmark = 0; cmark < NMARKS; cmark++) {
  72.                 if (wp->w_markp[cmark] == lp) {
  73.                     wp->w_markp[cmark] = lp->l_fp;
  74.                     wp->w_marko[cmark] = 0;
  75.                 }
  76.             }
  77.             wp = wp->w_wndp;
  78.         }
  79.  
  80.         /* next screen! */
  81.         scrp = scrp->s_next_screen;
  82.     }
  83.  
  84.     bp = bheadp;
  85.     while (bp != NULL) {
  86.         if (bp->b_nwnd == 0) {
  87.             if (bp->b_dotp    == lp) {
  88.                 bp->b_dotp = lp->l_fp;
  89.                 bp->b_doto = 0;
  90.             }
  91.             for (cmark = 0; cmark < NMARKS; cmark++) {
  92.                 if (bp->b_markp[cmark] == lp) {
  93.                     bp->b_markp[cmark] = lp->l_fp;
  94.                     bp->b_marko[cmark] = 0;
  95.                 }
  96.             }
  97.         }
  98.         bp = bp->b_bufp;
  99.     }
  100.     lp->l_bp->l_fp = lp->l_fp;
  101.     lp->l_fp->l_bp = lp->l_bp;
  102.     free((char *) lp);
  103. }
  104.  
  105. /*
  106.  * This routine gets called when a character is changed in place in the current
  107.  * buffer. It updates all of the required flags in the buffer and window
  108.  * system. The flag used is passed as an argument; if the buffer is being
  109.  * displayed in more than 1 window we change EDIT t HARD. Set MODE if the
  110.  * mode line needs to be updated (the "*" has to be set).
  111.  */
  112. PASCAL NEAR lchange(flag)
  113. register int    flag;
  114. {
  115.     register WINDOW *wp;
  116.     SCREEN *scrp;        /* screen to fix pointers in */
  117.  
  118.     if (curbp->b_nwnd != 1)         /* Ensure hard.     */
  119.         flag = WFHARD;
  120.     if ((curbp->b_flag&BFCHG) == 0) {    /* First change, so    */
  121.         flag |= WFMODE;         /* update mode lines.    */
  122.         curbp->b_flag |= BFCHG;
  123.     }
  124.  
  125.     /* in all screens.... */
  126.     scrp = first_screen;
  127.     while (scrp) {
  128.         /* make sure all the needed windows get this flag */
  129.         wp = scrp->s_first_window;
  130.         while (wp != NULL) {
  131.             if (wp->w_bufp == curbp)
  132.                 wp->w_flag |= flag;
  133.             wp = wp->w_wndp;
  134.         }
  135.  
  136.         /* next screen! */
  137.         scrp = scrp->s_next_screen;
  138.     }
  139. }
  140.  
  141. PASCAL NEAR insspace(f, n)    /* insert spaces forward into text */
  142.  
  143. int f, n;    /* default flag and numeric argument */
  144.  
  145. {
  146.     linsert(n, ' ');
  147.     backchar(f, n);
  148. }
  149.  
  150. /*
  151.  * linstr -- Insert a string at the current point
  152.  */
  153.  
  154. PASCAL NEAR linstr(instr)
  155. char    *instr;
  156. {
  157.     register int status;
  158.  
  159.     status = TRUE;
  160.     if (instr != NULL)
  161.         while (*instr) {
  162.             status = ((*instr == '\r') ? lnewline(): linsert(1, *instr));
  163.  
  164.             /* Insertion error? */
  165.             if (status != TRUE) {
  166.                 mlwrite(TEXT168);
  167. /*                                      "%%Can not insert string" */
  168.                 break;
  169.             }
  170.             instr++;
  171.         }
  172.     return(status);
  173. }
  174.  
  175. /*
  176.  * Insert "n" copies of the character "c" at the current location of dot. In
  177.  * the easy case all that happens is the text is stored in the line. In the
  178.  * hard case, the line has to be reallocated. When the window list is updated,
  179.  * take special care; I screwed it up once. You always update dot in the
  180.  * current window. You update mark, and a dot in another window, if it is
  181.  * greater than the place where you did the insert. Return TRUE if all is
  182.  * well, and FALSE on errors.
  183.  */
  184.  
  185. #if    PROTO
  186. PASCAL NEAR linsert(int n, char c)
  187. #else
  188. PASCAL NEAR linsert(n, c)
  189.  
  190. int    n;
  191. char    c;
  192. #endif
  193.  
  194. {
  195.     register char    *cp1;
  196.     register char    *cp2;
  197.     register LINE    *lp1;
  198.     register LINE    *lp2;
  199.     register LINE    *lp3;
  200.     register int    doto;
  201.     register int    i;
  202.     register WINDOW *wp;
  203.     SCREEN *scrp;        /* screen to fix pointers in */
  204.     int cmark;        /* current mark */
  205.  
  206.     if (curbp->b_mode&MDVIEW)    /* don't allow this command if    */
  207.         return(rdonly());    /* we are in read only mode    */
  208.  
  209.     /* a zero insert means do nothing! */
  210.     if (n == 0)
  211.         return(TRUE);
  212.  
  213.     /* Negative numbers of inserted characters are right out! */
  214.     if (n < 1)
  215.         return(FALSE);
  216.  
  217.     /* mark the current window's buffer as changed */
  218.     lchange(WFEDIT);
  219.  
  220.     lp1 = curwp->w_dotp;            /* Current line     */
  221.     if (lp1 == curbp->b_linep) {        /* At the end: special    */
  222.         if (curwp->w_doto != 0) {
  223.             mlwrite(TEXT170);
  224. /*                              "bug: linsert" */
  225.             return(FALSE);
  226.         }
  227.         if ((lp2=lalloc(BSIZE(n))) == NULL)    /* Allocate new line    */
  228.             return(FALSE);
  229.         lp2->l_used = n;
  230.         lp3 = lp1->l_bp;        /* Previous line    */
  231.         lp3->l_fp = lp2;        /* Link in        */
  232.         lp2->l_fp = lp1;
  233.         lp1->l_bp = lp2;
  234.         lp2->l_bp = lp3;
  235.         for (i=0; i<n; ++i)
  236.             lp2->l_text[i] = c;
  237.         curwp->w_dotp = lp2;
  238.         curwp->w_doto = n;
  239.         return(TRUE);
  240.     }
  241.     doto = curwp->w_doto;            /* Save for later.    */
  242.     if (lp1->l_used+n > lp1->l_size) {    /* Hard: reallocate    */
  243.         if ((lp2=lalloc(BSIZE(lp1->l_used+n))) == NULL)
  244.             return(FALSE);
  245.         lp2->l_used = lp1->l_used+n;
  246.         cp1 = &lp1->l_text[0];
  247.         cp2 = &lp2->l_text[0];
  248.         while (cp1 != &lp1->l_text[doto])
  249.             *cp2++ = *cp1++;
  250.         cp2 += n;
  251.         while (cp1 != &lp1->l_text[lp1->l_used])
  252.             *cp2++ = *cp1++;
  253.         lp1->l_bp->l_fp = lp2;
  254.         lp2->l_fp = lp1->l_fp;
  255.         lp1->l_fp->l_bp = lp2;
  256.         lp2->l_bp = lp1->l_bp;
  257.         free((char *) lp1);
  258.     } else {                /* Easy: in place    */
  259.         lp2 = lp1;            /* Pretend new line    */
  260.         lp2->l_used += n;
  261.         cp2 = &lp1->l_text[lp1->l_used];
  262.         cp1 = cp2-n;
  263.         while (cp1 != &lp1->l_text[doto])
  264.             *--cp2 = *--cp1;
  265.     }
  266.     for (i=0; i<n; ++i)            /* Add the characters    */
  267.         lp2->l_text[doto+i] = c;
  268.     /* in all screens.... */
  269.     scrp = first_screen;
  270.     while (scrp) {
  271.  
  272.         wp = scrp->s_first_window;
  273.         while (wp != NULL) {
  274.             if (wp->w_linep == lp1)
  275.                 wp->w_linep = lp2;
  276.             if (wp->w_dotp == lp1) {
  277.                 wp->w_dotp = lp2;
  278.                 if (wp==curwp || wp->w_doto>doto)
  279.                     wp->w_doto += n;
  280.             }
  281.             for (cmark = 0; cmark < NMARKS; cmark++) {
  282.                 if (wp->w_markp[cmark] == lp1) {
  283.                     wp->w_markp[cmark] = lp2;
  284.                     if (wp->w_marko[cmark] > doto)
  285.                         wp->w_marko[cmark] += n;
  286.                 }
  287.             }
  288.             wp = wp->w_wndp;
  289.         }
  290.  
  291.         /* next screen! */
  292.         scrp = scrp->s_next_screen;
  293.     }
  294.     return(TRUE);
  295. }
  296.  
  297. /*
  298.  * Overwrite a character into the current line at the current position
  299.  *
  300.  */
  301.  
  302. #if    PROTO
  303. PASCAL NEAR lowrite(char c)
  304. #else
  305. PASCAL NEAR lowrite(c)
  306.  
  307. char c;        /* character to overwrite on current position */
  308. #endif
  309.  
  310. {
  311.     if (curwp->w_doto < curwp->w_dotp->l_used &&
  312.         (lgetc(curwp->w_dotp, curwp->w_doto) != '\t' ||
  313.          (curwp->w_doto) % 8 == 7))
  314.             ldelete(1L, FALSE);
  315.     return(linsert(1, c));
  316. }
  317.  
  318. /*
  319.  * lover -- Overwrite a string at the current point
  320.  */
  321.  
  322. PASCAL NEAR lover(ostr)
  323.  
  324. char    *ostr;
  325.  
  326. {
  327.     register int status = TRUE;
  328.  
  329.     if (ostr != NULL)
  330.         while (*ostr && status == TRUE) {
  331.             status = ((*ostr == '\r') ? lnewline(): lowrite(*ostr));
  332.  
  333.             /* Insertion error? */
  334.             if (status != TRUE) {
  335.                 mlwrite(TEXT172);
  336. /*                                      "%%Out of memory while overwriting" */
  337.                 break;
  338.             }
  339.             ostr++;
  340.         }
  341.     return(status);
  342. }
  343.  
  344. /*
  345.  * Insert a newline into the buffer at the current location of dot in the
  346.  * current window. The funny ass-backwards way it does things is not a botch;
  347.  * it just makes the last line in the file not a special case. Return TRUE if
  348.  * everything works out and FALSE on error (memory allocation failure). The
  349.  * update of dot and mark is a bit easier then in the above case, because the
  350.  * split forces more updating.
  351.  */
  352. PASCAL NEAR lnewline()
  353. {
  354.     register char    *cp1;
  355.     register char    *cp2;
  356.     register LINE    *lp1;
  357.     register LINE    *lp2;
  358.     register int    doto;
  359.     register WINDOW *wp;
  360.     SCREEN *scrp;        /* screen to fix pointers in */
  361.     int cmark;        /* current mark */
  362.  
  363.     if (curbp->b_mode&MDVIEW)    /* don't allow this command if    */
  364.         return(rdonly());    /* we are in read only mode    */
  365.     lchange(WFHARD);
  366.     lp1  = curwp->w_dotp;            /* Get the address and    */
  367.     doto = curwp->w_doto;            /* offset of "."    */
  368.     if ((lp2=lalloc(doto)) == NULL)     /* New first half line    */
  369.         return(FALSE);
  370.     cp1 = &lp1->l_text[0];            /* Shuffle text around    */
  371.     cp2 = &lp2->l_text[0];
  372.     while (cp1 != &lp1->l_text[doto])
  373.         *cp2++ = *cp1++;
  374.     cp2 = &lp1->l_text[0];
  375.     while (cp1 != &lp1->l_text[lp1->l_used])
  376.         *cp2++ = *cp1++;
  377.     lp1->l_used -= doto;
  378.     lp2->l_bp = lp1->l_bp;
  379.     lp1->l_bp = lp2;
  380.     lp2->l_bp->l_fp = lp2;
  381.     lp2->l_fp = lp1;
  382.  
  383.     /* in all screens.... */
  384.     scrp = first_screen;
  385.     while (scrp) {
  386.  
  387.         wp = scrp->s_first_window;
  388.         while (wp != NULL) {
  389.             if (wp->w_linep == lp1)
  390.                 wp->w_linep = lp2;
  391.             if (wp->w_dotp == lp1) {
  392.                 if (wp->w_doto < doto)
  393.                     wp->w_dotp = lp2;
  394.                 else
  395.                     wp->w_doto -= doto;
  396.             }
  397.             for (cmark = 0; cmark < NMARKS; cmark++) {
  398.                 if (wp->w_markp[cmark] == lp1) {
  399.                     if (wp->w_marko[cmark] < doto)
  400.                         wp->w_markp[cmark] = lp2;
  401.                     else
  402.                         wp->w_marko[cmark] -= doto;
  403.                 }
  404.             }
  405.             wp = wp->w_wndp;
  406.         }
  407.  
  408.         /* next screen! */
  409.         scrp = scrp->s_next_screen;
  410.     }
  411.     return(TRUE);
  412. }
  413.  
  414. /*
  415.  
  416. LDELETE:
  417.  
  418.     This function deletes "n" bytes, starting at dot. Positive n
  419. deletes forward, negative n deletes backwords. It understands how to
  420. deal with end of lines, and with two byte characters. It returns TRUE
  421. if all of the characters were deleted, and FALSE if they were not
  422. (because dot ran into the buffer end). The "kflag" is TRUE if the text
  423. should be put in the kill buffer.
  424.  
  425. */
  426.  
  427. PASCAL NEAR ldelete(n, kflag)
  428.  
  429. long n;     /* # of chars to delete */
  430. int kflag;    /* put killed text in kill buffer flag */
  431.  
  432. {
  433.     register char    *cp1;
  434.     register char    *cp2;
  435.     register LINE    *dotp;
  436.     register int    doto;
  437.     register int    chunk;
  438.     register WINDOW *wp;
  439.     int cmark;        /* current mark */
  440.  
  441.     if (curbp->b_mode&MDVIEW)    /* don't allow this command if    */
  442.         return(rdonly());    /* we are in read only mode    */
  443.  
  444.     /* going Forward? */
  445.     if (n >= 0) {
  446.  
  447.         while (n > 0) {
  448. #if    DBCS
  449.             /* never start forward on a 2 byte char */
  450.             if (curwp->w_doto > 0 && is2byte(curwp->w_dotp->l_text,
  451.                 &curwp->w_dotp->l_text[curwp->w_doto - 1])) {
  452.                 curwp->w_doto--;
  453.                 n++;
  454.             }
  455. #endif
  456.             /* record the current point */
  457.             dotp = curwp->w_dotp;
  458.             doto = curwp->w_doto;
  459.     
  460.             /* can't delete past the end of the buffer */
  461.             if (dotp == curbp->b_linep)
  462.                 return(FALSE);
  463.     
  464.             /* find out how many chars to delete on this line */
  465.             chunk = dotp->l_used-doto;    /* Size of chunk.    */
  466.             if (chunk > n)
  467.                 chunk = n;
  468.     
  469.             /* if at the end of a line, merge with the next */
  470.             if (chunk == 0) {
  471.     
  472.                 /* flag that we are making a hard change */
  473.                 lchange(WFHARD);
  474.                 if (ldelnewline() == FALSE ||
  475.                     (kflag != FALSE &&
  476.                      kinsert(FORWARD, '\r')==FALSE))
  477.                     return(FALSE);
  478.                 --n;
  479.                 continue;
  480.             }
  481.     
  482.             /* flag the fact we are changing the current line */
  483.             lchange(WFEDIT);
  484.     
  485.             /* find the limits of the kill */
  486.             cp1 = &dotp->l_text[doto];
  487.             cp2 = cp1 + chunk;
  488. #if    DBCS
  489.             /* never leave half a character */
  490.             if (is2byte(dotp->l_text, cp2 - 1)) {
  491.                 ++chunk;
  492.                 ++cp2;
  493.                 ++n;
  494.             }
  495. #endif
  496.  
  497.             /* save the text to the kill buffer */
  498.             if (kflag != FALSE) {
  499.                 while (cp1 != cp2) {
  500.                     if (kinsert(FORWARD, *cp1) == FALSE)
  501.                         return(FALSE);
  502.                     ++cp1;
  503.                 }
  504.                 cp1 = &dotp->l_text[doto];
  505.             }
  506.     
  507.             /* copy what is left of the line upward */
  508.             while (cp2 != &dotp->l_text[dotp->l_used])
  509.                 *cp1++ = *cp2++;
  510.             dotp->l_used -= chunk;
  511.     
  512.             /* fix any other windows with the same text displayed */
  513.             wp = wheadp;
  514.             while (wp != NULL) {
  515.     
  516.                 /* reset the dot if needed */
  517.                 if (wp->w_dotp==dotp && wp->w_doto>=doto) {
  518.                     wp->w_doto -= chunk;
  519.                     if (wp->w_doto < doto)
  520.                         wp->w_doto = doto;
  521.                 }
  522.     
  523.                 /* reset any marks if needed */
  524.                 for (cmark = 0; cmark < NMARKS; cmark++) {
  525.                     if (wp->w_markp[cmark]==dotp && wp->w_marko[cmark]>=doto) {
  526.                         wp->w_marko[cmark] -= chunk;
  527.                         if (wp->w_marko[cmark] < doto)
  528.                             wp->w_marko[cmark] = doto;
  529.                     }
  530.                 }
  531.     
  532.                 /* onward to the next window */
  533.                 wp = wp->w_wndp;
  534.             }
  535.     
  536.             /* indicate we have deleted chunk characters */
  537.             n -= chunk;
  538.         }
  539.     } else {
  540.         while (n < 0) {
  541. #if    DBCS
  542.             /* never start backwards on the
  543.                1st of a 2 byte character */
  544.             if (curwp->w_doto > 1 && is2byte(curwp->w_dotp->l_text,
  545.                 &curwp->w_dotp->l_text[curwp->w_doto-1])) {
  546.                 curwp->w_doto++;
  547.                 n--;
  548.             }
  549. #endif
  550.             /* record the current point */
  551.             dotp = curwp->w_dotp;
  552.             doto = curwp->w_doto;
  553.     
  554.             /* can't delete past the beginning of the buffer */
  555.             if (dotp == lforw(curbp->b_linep) && (doto == 0))
  556.                 return(FALSE);
  557.     
  558.             /* find out how many chars to delete on this line */
  559.             chunk = doto;        /* Size of chunk.    */
  560.             if (chunk > -n)
  561.                 chunk = -n;
  562.     
  563.             /* if at the beginning of a line, merge with the last */
  564.             if (chunk == 0) {
  565.     
  566.                 /* flag that we are making a hard change */
  567.                 lchange(WFHARD);
  568.                 backchar(TRUE, 1);
  569.                 if (ldelnewline() == FALSE ||
  570.                     (kflag != FALSE &&
  571.                      kinsert(BACKWARD, '\r')==FALSE))
  572.                     return(FALSE);
  573.                 ++n;
  574.                 continue;
  575.             }
  576.     
  577.             /* flag the fact we are changing the current line */
  578.             lchange(WFEDIT);
  579.     
  580.             /* find the limits of the kill */
  581.             cp1 = &dotp->l_text[doto];
  582.             cp2 = cp1 - chunk;
  583. #if    DBCS
  584.             if (is2byte(dotp->l_text, cp2 - 1)) {
  585.                 ++chunk;
  586.                 --cp2;
  587.                 ++n;
  588.             }
  589. #endif
  590.     
  591.             /* save the text to the kill buffer */
  592.             if (kflag != FALSE) {
  593.                 while (cp1 > cp2) {
  594.                     if (kinsert(BACKWARD, *(--cp1)) == FALSE)
  595.                         return(FALSE);
  596.                 }
  597.                 cp1 = &dotp->l_text[doto];
  598.             }
  599.     
  600.             /* copy what is left of the line downward */
  601.             while (cp1 != &dotp->l_text[dotp->l_used])
  602.                 *cp2++ = *cp1++;
  603.             dotp->l_used -= chunk;
  604.             curwp->w_doto -= chunk;
  605.     
  606.             /* fix any other windows with the same text displayed */
  607.             wp = wheadp;
  608.             while (wp != NULL) {
  609.     
  610.                 /* reset the dot if needed */
  611.                 if (wp->w_dotp==dotp && wp->w_doto>=doto) {
  612.                     wp->w_doto -= chunk;
  613.                     if (wp->w_doto < doto)
  614.                         wp->w_doto = doto;
  615.                 }
  616.     
  617.                 /* reset any marks if needed */
  618.                 for (cmark = 0; cmark < NMARKS; cmark++) {
  619.                     if (wp->w_markp[cmark]==dotp && wp->w_marko[cmark]>=doto) {
  620.                         wp->w_marko[cmark] -= chunk;
  621.                         if (wp->w_marko[cmark] < doto)
  622.                             wp->w_marko[cmark] = doto;
  623.                     }
  624.                 }
  625.     
  626.                 /* onward to the next window */
  627.                 wp = wp->w_wndp;
  628.             }
  629.     
  630.             /* indicate we have deleted chunk characters */
  631.             n += chunk;
  632.         }
  633.     }
  634.     return(TRUE);
  635. }
  636.  
  637. /* getctext:    grab and return a string with the text of
  638.         the current line
  639. */
  640.  
  641. char *PASCAL NEAR getctext()
  642.  
  643. {
  644.     register LINE *lp;    /* line to copy */
  645.     register int size;    /* length of line to return */
  646.     register char *sp;    /* string pointer into line */
  647.     register char *dp;    /* string pointer into returned line */
  648.     char rline[NSTRING];    /* line to return */
  649.  
  650.     /* find the contents of the current line and its length */
  651.     lp = curwp->w_dotp;
  652.     sp = lp->l_text;
  653.     size = lp->l_used;
  654.     if (size >= NSTRING)
  655.         size = NSTRING - 1;
  656.  
  657.     /* copy it across */
  658.     dp = rline;
  659.     while (size--)
  660.         *dp++ = *sp++;
  661.     *dp = 0;
  662.     return(rline);
  663. }
  664.  
  665. /* putctext:    replace the current line with the passed in text    */
  666.  
  667. PASCAL NEAR putctext(iline)
  668.  
  669. char *iline;    /* contents of new line */
  670.  
  671. {
  672.     register int status;
  673.  
  674.     /* delete the current line */
  675.     curwp->w_doto = 0;    /* starting at the beginning of the line */
  676.     if ((status = killtext(TRUE, 1)) != TRUE)
  677.         return(status);
  678.  
  679.     /* insert the new line */
  680.     if ((status = linstr(iline)) != TRUE)
  681.         return(status);
  682.     status = lnewline();
  683.     backline(TRUE, 1);
  684.     return(status);
  685. }
  686.  
  687. /*
  688.  * Delete a newline. Join the current line with the next line. If the next line
  689.  * is the magic header line always return TRUE; merging the last line with the
  690.  * header line can be thought of as always being a successful operation, even
  691.  * if nothing is done, and this makes the kill buffer work "right". Easy cases
  692.  * can be done by shuffling data around. Hard cases require that lines be moved
  693.  * about in memory. Return FALSE on error and TRUE if all looks ok. Called by
  694.  * "ldelete" only.
  695.  */
  696. PASCAL NEAR ldelnewline()
  697. {
  698.     register char    *cp1;
  699.     register char    *cp2;
  700.     register LINE    *lp1;
  701.     register LINE    *lp2;
  702.     register LINE    *lp3;
  703.     register WINDOW *wp;
  704.     SCREEN *scrp;        /* screen to fix pointers in */
  705.     int cmark;        /* current mark */
  706.  
  707.     if (curbp->b_mode&MDVIEW)    /* don't allow this command if    */
  708.         return(rdonly());    /* we are in read only mode    */
  709.     lp1 = curwp->w_dotp;
  710.     lp2 = lp1->l_fp;
  711.     if (lp2 == curbp->b_linep) {        /* At the buffer end.    */
  712.         if (lp1->l_used == 0)        /* Blank line.        */
  713.             lfree(lp1);
  714.         return(TRUE);
  715.     }
  716.     if (lp2->l_used <= lp1->l_size-lp1->l_used) {
  717.         cp1 = &lp1->l_text[lp1->l_used];
  718.         cp2 = &lp2->l_text[0];
  719.         while (cp2 != &lp2->l_text[lp2->l_used])
  720.         *cp1++ = *cp2++;
  721.  
  722.         /* in all screens.... */
  723.         scrp = first_screen;
  724.         while (scrp) {
  725.  
  726.             wp = scrp->s_first_window;
  727.             while (wp != NULL) {
  728.                 if (wp->w_linep == lp2)
  729.                     wp->w_linep = lp1;
  730.                 if (wp->w_dotp == lp2) {
  731.                     wp->w_dotp  = lp1;
  732.                     wp->w_doto += lp1->l_used;
  733.                 }
  734.                 for (cmark = 0; cmark < NMARKS; cmark++) {
  735.                     if (wp->w_markp[cmark] == lp2) {
  736.                         wp->w_markp[cmark]  = lp1;
  737.                         wp->w_marko[cmark] += lp1->l_used;
  738.                     }
  739.                 }
  740.                 wp = wp->w_wndp;
  741.             }
  742.  
  743.             /* next screen! */
  744.             scrp = scrp->s_next_screen;
  745.         }
  746.  
  747.         lp1->l_used += lp2->l_used;
  748.         lp1->l_fp = lp2->l_fp;
  749.         lp2->l_fp->l_bp = lp1;
  750.         free((char *) lp2);
  751.         return(TRUE);
  752.     }
  753.     if ((lp3=lalloc(lp1->l_used+lp2->l_used)) == NULL)
  754.         return(FALSE);
  755.     cp1 = &lp1->l_text[0];
  756.     cp2 = &lp3->l_text[0];
  757.     while (cp1 != &lp1->l_text[lp1->l_used])
  758.         *cp2++ = *cp1++;
  759.     cp1 = &lp2->l_text[0];
  760.     while (cp1 != &lp2->l_text[lp2->l_used])
  761.         *cp2++ = *cp1++;
  762.     lp1->l_bp->l_fp = lp3;
  763.     lp3->l_fp = lp2->l_fp;
  764.     lp2->l_fp->l_bp = lp3;
  765.     lp3->l_bp = lp1->l_bp;
  766.  
  767.     /* in all screens.... */
  768.     scrp = first_screen;
  769.     while (scrp) {
  770.  
  771.         wp = scrp->s_first_window;
  772.         while (wp != NULL) {
  773.             if (wp->w_linep==lp1 || wp->w_linep==lp2)
  774.                 wp->w_linep = lp3;
  775.             if (wp->w_dotp == lp1)
  776.                 wp->w_dotp  = lp3;
  777.             else if (wp->w_dotp == lp2) {
  778.                 wp->w_dotp  = lp3;
  779.                 wp->w_doto += lp1->l_used;
  780.             }
  781.             for (cmark = 0; cmark < NMARKS; cmark++) {
  782.                 if (wp->w_markp[cmark] == lp1)
  783.                     wp->w_markp[cmark]  = lp3;
  784.                 else if (wp->w_markp[cmark] == lp2) {
  785.                     wp->w_markp[cmark]  = lp3;
  786.                     wp->w_marko[cmark] += lp1->l_used;
  787.                 }
  788.             }
  789.             wp = wp->w_wndp;
  790.         }
  791.  
  792.         /* next screen! */
  793.         scrp = scrp->s_next_screen;
  794.     }
  795.  
  796.     free((char *) lp1);
  797.     free((char *) lp2);
  798.     return(TRUE);
  799. }
  800.  
  801. /*    Add a new line to the end of the indicated buffer.
  802.     return FALSE if we run out of memory
  803.     note that this works on non-displayed buffers as well!
  804. */
  805.  
  806. #if    PROTO
  807. int PASCAL NEAR addline(BUFFER *bp, char *text)
  808. #else
  809. int PASCAL NEAR addline(bp, text)
  810.  
  811. BUFFER *bp;    /* buffer to add text to */
  812. char *text;    /* line to add */
  813. #endif
  814. {
  815.     register LINE    *lp;
  816.     register int    i;
  817.     register int    ntext;
  818.  
  819.     /* allocate the memory to hold the line */
  820.     ntext = strlen(text);
  821.     if ((lp=lalloc(ntext)) == NULL)
  822.         return(FALSE);
  823.  
  824.     /* copy the text into the new line */
  825.     for (i=0; i<ntext; ++i)
  826.         lputc(lp, i, text[i]);
  827.  
  828.     /* add the new line to the end of the buffer */
  829.     bp->b_linep->l_bp->l_fp = lp;
  830.     lp->l_bp = bp->b_linep->l_bp;
  831.     bp->b_linep->l_bp = lp;
  832.     lp->l_fp = bp->b_linep;
  833.  
  834.     /* if the point was at the end of the buffer,
  835.        move it to the beginning of the new line */
  836.     if (bp->b_dotp == bp->b_linep)
  837.         bp->b_dotp = lp;
  838.     return(TRUE);
  839. }
  840.  
  841. /*
  842.  * Delete all of the text saved in the kill buffer. Called by commands when a
  843.  * new kill context is being created. The kill buffer array is released, just
  844.  * in case the buffer has grown to immense size. No errors.
  845.  */
  846. PASCAL NEAR kdelete()
  847. {
  848.     KILL *kp;    /* ptr to scan kill buffer chunk list */
  849.  
  850.     if (kbufh != NULL) {
  851.  
  852.         /* first, delete all the chunks */
  853.         kbufp = kbufh;
  854.         while (kbufp != NULL) {
  855.             kp = kbufp->d_next;
  856.             free((char *)kbufp);
  857.             kbufp = kp;
  858.         }
  859.  
  860.         /* and reset all the kill buffer pointers */
  861.         kbufh = kbufp = NULL;
  862.         kskip = 0;
  863.         kused = KBLOCK;             
  864.     }
  865. }
  866.  
  867. /*
  868.  * Insert a character to the kill buffer, allocating new chunks as needed.
  869.  * Return TRUE if all is well, and FALSE on errors.
  870.  */
  871.  
  872. #if    PROTO
  873. PASCAL NEAR kinsert(int direct, char c)
  874. #else
  875. PASCAL NEAR kinsert(direct, c)
  876.  
  877. int direct;    /* direction (FORWARD/BACKWARD) to insert characters */
  878. char c;        /* character to insert in the kill buffer */
  879. #endif
  880.  
  881. {
  882.     KILL *nchunk;    /* ptr to newly malloced chunk */
  883.  
  884.     if (direct == FORWARD) {
  885.  
  886.         /* check to see if we need a new chunk */
  887.         if (kused >= KBLOCK) {
  888.             if ((nchunk = (KILL *)malloc(sizeof(KILL))) == NULL)
  889.                 return(FALSE);
  890.             if (kbufh == NULL)    /* set head ptr if first time */
  891.                 kbufh = nchunk;
  892.             if (kbufp != NULL)    /* point the current to this new one */
  893.                 kbufp->d_next = nchunk;
  894.             kbufp = nchunk;
  895.             kbufp->d_next = NULL;
  896.             kused = 0;
  897.         }
  898.     
  899.         /* and now insert the character */
  900.         kbufp->d_chunk[kused++] = c;
  901.     } else {
  902.         /* BACKWARDS */
  903.         /* check to see if we need a new chunk */
  904.         if (kskip == 0) {
  905.             if ((nchunk = (KILL *)malloc(sizeof(KILL))) == NULL)
  906.                 return(FALSE);
  907.             if (kbufh == NULL) {    /* set head ptr if first time */
  908.                 kbufh = nchunk;
  909.                 kbufp = nchunk;
  910.                 kskip = KBLOCK;
  911.                 kused = KBLOCK;
  912.                 nchunk->d_next = (KILL *)NULL;
  913.             } else {
  914.                 nchunk->d_next = kbufh;
  915.                 kbufh = nchunk;
  916.                 kskip = KBLOCK;
  917.             }
  918.         }
  919.     
  920.         /* and now insert the character */
  921.         kbufh->d_chunk[--kskip] = c;
  922.     }
  923.     return(TRUE);
  924. }
  925.  
  926. /*
  927.  * Yank text back from the kill buffer. This is really easy. All of the work
  928.  * is done by the standard insert routines. All you do is run the loop, and
  929.  * check for errors. Bound to "C-Y".
  930.  */
  931.  
  932. #define    Char_insert(a)    (a == '\r' ? lnewline() : linsert(1, a))
  933.  
  934. PASCAL NEAR yank(f, n)
  935.  
  936. int f,n;    /* prefix flag and argument */
  937.  
  938. {
  939.     register int counter;    /* counter into kill buffer data */
  940.     register char *sp;    /* pointer into string to insert */
  941.     short int curoff;    /* storage for line before yanking */
  942.     LINE *curline;
  943.     KILL *kptr;        /* pointer into kill buffer */
  944.  
  945.     if (curbp->b_mode&MDVIEW)    /* don't allow this command if    */
  946.         return(rdonly());    /* we are in read only mode    */
  947.     if (n < 0)
  948.         return(FALSE);
  949.     /* make sure there is something to yank */
  950.     if (kbufh == NULL)
  951.         return(TRUE);        /* not an error, just nothing */
  952.  
  953.     /*
  954.      * Save the local pointers to hold global ".".
  955.      */
  956.     if (yankflag) {
  957.         /* Find the *previous* line, since the line we are on
  958.          * may disappear due to re-allocation.  This works even
  959.          * if we are on the first line of the file.
  960.          */
  961.         curline = lback(curwp->w_dotp);
  962.         curoff = curwp->w_doto;
  963.     }
  964.  
  965.     /* for each time.... */
  966.     while (n--) {
  967.         if (kskip > 0) {
  968.             kptr = kbufh;
  969.             sp = &(kptr->d_chunk[kskip]);
  970.             counter = kskip;
  971.             while (counter++ < KBLOCK) {
  972.                 Char_insert(*sp);
  973.                 ++sp;
  974.             }
  975.             kptr = kptr->d_next;
  976.         } else {
  977.             kptr = kbufh;
  978.         }
  979.     
  980.         if (kptr != (KILL *)NULL) {
  981.             while (kptr != kbufp) {
  982.                 sp = kptr->d_chunk;
  983.                 for(counter = 0; counter < KBLOCK; counter++) {
  984.                     Char_insert(*sp);
  985.                     ++sp;
  986.                 }
  987.                 kptr = kptr->d_next;
  988.             }
  989.             counter = kused;
  990.             sp = kptr->d_chunk;
  991.             while (counter--) {
  992.                 Char_insert(*sp);
  993.                 ++sp;
  994.             }
  995.         }
  996.     }
  997.  
  998.     /* If requested, set global "." back to the
  999.      * beginning of the yanked text.
  1000.      */
  1001.     if (yankflag) {
  1002.         curwp->w_dotp = lforw(curline);
  1003.         curwp->w_doto = curoff;
  1004.     }
  1005.     return(TRUE);
  1006. }
  1007.  
  1008. #if    0
  1009. dispkill()
  1010.  
  1011. {
  1012.     KILL *kptr;
  1013.     int index;
  1014.     char *sp;
  1015.     int counter;
  1016.  
  1017.     if (kbufh == (KILL *)NULL) {
  1018.         printf("<EMPTY>\n");
  1019.         return;
  1020.     }
  1021.  
  1022.     index = 1;
  1023.     if (kskip > 0) {
  1024.         kptr = kbufh;
  1025.         printf("kskip = %d\nBLOCK %d <", kskip, index++);
  1026.         sp = &(kptr->d_chunk[kskip]);
  1027.         counter = kskip;
  1028.         while (counter++ < KBLOCK) {
  1029.             putchar(*sp++);
  1030.         }
  1031.         printf(">\n");
  1032.         kptr = kptr->d_next;
  1033.     } else {
  1034.         kptr = kbufh;
  1035.     }
  1036.  
  1037.     if (kptr != (KILL *)NULL) {
  1038.         while (kptr != kbufp) {
  1039.             printf("BLOCK %d <%255s>\n", index++, kptr->d_chunk);
  1040.             kptr = kptr->d_next;
  1041.         }
  1042.         printf("BLOCK %d <", index++);
  1043.         counter = kused;
  1044.         sp = kptr->d_chunk;
  1045.         while (counter--) {
  1046.             putchar(*sp++);
  1047.         }
  1048.         printf(">\nkused = %d\n", kused);
  1049.     }
  1050.  
  1051. }
  1052. #endif
  1053.