home *** CD-ROM | disk | FTP | other *** search
/ Frozen Fish 1: Amiga / FrozenFish-Apr94.iso / bbs / alib / d3xx / d352 / mg.lha / MG / src.LZH / mg / line.c < prev    next >
C/C++ Source or Header  |  1990-05-23  |  20KB  |  743 lines

  1. /*
  2.  * Text line handling. The functions in this file are a general set of line
  3.  * management utilities. They are the only routines that touch the text. They
  4.  * also touch the buffer and window structures, to make sure that the
  5.  * necessary updating gets done. There are routines in this file that handle
  6.  * the kill buffer too. It isn't 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
  12.  * nonsense. 
  13.  */
  14. #include    "def.h"
  15. #include    "line.h"
  16. #include    "buffer.h"
  17. #include    "window.h"
  18.  
  19. #ifdef    ANSI
  20. #include <stdlib.h>
  21. #include <stddef.h>
  22. #include <string.h>
  23. /* Use the ANSI offsetof if we can */
  24. #define OFFSET(t, m) offsetof(t, m)
  25. #else
  26. /* Otherwise, if we don't know yet, use the default */
  27. #ifndef OFFSET
  28. #define OFFSET(type,member) ((char *)&(((type *)0)->member)-(char *)((type *)0))
  29. #endif
  30. #endif
  31.  
  32. /*
  33.  * define/prototype static functions here. 
  34.  */
  35. static VOID     kcopy
  36. PROTO((char *, RSIZE, int));
  37. static          kgrow
  38. PROTO((int));
  39.  
  40.  
  41. #ifndef NBLOCK
  42. #define NBLOCK    16        /* Line block chunk size     */
  43. #endif
  44.  
  45. #define KBLOCK 256
  46. #define KBSHIFT 8        /* = log[2](KBLOCK) */
  47. #define KBSIZE  2048        /* maximal size of kbbuf array */
  48.  
  49. static char   **kbufp = NULL;    /* now addressed as an array of arrays */
  50. static RSIZE    kallst = 0;    /* # of first allocated byte */
  51.  
  52. static RSIZE    kused = 0;    /* # of bytes used in KB.     */
  53. static RSIZE    ksize = 0;    /* # of bytes allocated in KB.     */
  54. static RSIZE    kstart = 0;    /* # of first used byte in KB.     */
  55.  
  56. /*
  57.  * This routine allocates a block of memory large enough to hold a LINE
  58.  * containing "used" characters. The block is rounded up to whatever needs to
  59.  * be allocated. (use lallocx for lines likely to grow.) Return a pointer to
  60.  * the new block, or NULL if there isn't any memory left. Print a message in
  61.  * the message line if no space. 
  62.  */
  63. struct line    *
  64. lalloc(used)
  65.     register int    used;
  66. {
  67.     register struct line *lp;
  68.     register int    size;
  69.  
  70.     /* any padding at the end of the structure is used */
  71.     if ((size = used + OFFSET(struct line, l_text[0])) < sizeof(struct line))
  72.         size = sizeof(struct line);
  73. #ifdef MALLOCROUND
  74.     MALLOCROUND(size);    /* round up to a size optimal to malloc */
  75. #endif
  76.     if ((lp = (struct line *) malloc((unsigned) size)) == NULL) {
  77.         ewprintf("Can't get %d bytes", size);
  78.         return (struct line *) NULL;
  79.     }
  80.     lp->l_size = size - OFFSET(struct line, l_text[0]);
  81.     lp->l_used = used;
  82.     return lp;
  83. }
  84.  
  85. /*
  86.  * Like lalloc, only round amount desired up because this line will probably
  87.  * grow.  We always make room for at least one more char. (thus making 0 not
  88.  * a special case anymore.) 
  89.  */
  90. struct line    *
  91. lallocx(used)
  92.     int             used;
  93. {
  94.     register int    size;
  95.     register struct line *lp;
  96.  
  97.     size = (NBLOCK + used) & ~(NBLOCK - 1);
  98.     if ((lp = lalloc(size)) != NULL)
  99.         lp->l_used = used;
  100.     return lp;
  101. }
  102.  
  103. /*
  104.  * Delete line "lp". Fix all of the links that might point at it (they are
  105.  * moved to offset 0 of the next line. Unlink the line from whatever buffer
  106.  * it might be in. Release the memory. The buffers are updated too; the magic
  107.  * conditions described in the above comments don't hold here. 
  108.  */
  109. VOID
  110. lfree(lp)
  111.     register struct line *lp;
  112. {
  113.     register struct buffer *bp;
  114.     register struct window *wp;
  115.  
  116.     for (wp = wheadp; wp != NULL; wp = wp->w_wndp) {
  117.         if (wp->w_linep == lp)
  118.             wp->w_linep = lp->l_fp;
  119.         if (wp->w_dotp == lp) {
  120.             wp->w_dotp = lp->l_fp;
  121.             wp->w_doto = 0;
  122.         }
  123.         if (wp->w_markp == lp) {
  124.             wp->w_markp = lp->l_fp;
  125.             wp->w_marko = 0;
  126.         }
  127.     }
  128.     for (bp = bheadp; bp != NULL; bp = bp->b_bufp) {
  129.         if (bp->b_nwnd == 0) {
  130.             if (bp->b_dotp == lp) {
  131.                 bp->b_dotp = lp->l_fp;
  132.                 bp->b_doto = 0;
  133.             }
  134.             if (bp->b_markp == lp) {
  135.                 bp->b_markp = lp->l_fp;
  136.                 bp->b_marko = 0;
  137.             }
  138.         }
  139.     }
  140.     lp->l_bp->l_fp = lp->l_fp;
  141.     lp->l_fp->l_bp = lp->l_bp;
  142.     free((char *) lp);
  143. }
  144.  
  145. /*
  146.  * This routine gets called when a character is changed in place in the
  147.  * current buffer. It updates all of the required flags in the buffer and
  148.  * window system. The flag used is passed as an argument; if the buffer is
  149.  * being displayed in more than 1 window we change EDIT to HARD. Set MODE if
  150.  * the mode line needs to be updated (the "*" has to be set). 
  151.  */
  152. VOID
  153. lchange(flag)
  154.     register int    flag;
  155. {
  156.     register struct window *wp;
  157.  
  158.     if ((curbp->b_flag & BFCHG) == 0) {    /* First change, so     */
  159.         flag |= WFMODE;    /* update mode lines.     */
  160.         curbp->b_flag |= BFCHG;
  161.     }
  162.     for (wp = wheadp; wp != NULL; wp = wp->w_wndp) {
  163.         if (wp->w_bufp == curbp) {
  164.             wp->w_flag |= flag;
  165.             if (wp != curwp)
  166.                 wp->w_flag |= WFHARD;
  167.         }
  168.     }
  169. }
  170.  
  171. /*
  172.  * Insert "n" copies of the character "c" at the current location of dot. In
  173.  * the easy case all that happens is the text is stored in the line. In the
  174.  * hard case, the line has to be reallocated. When the window list is
  175.  * updated, take special care; I screwed it up once. You always update dot in
  176.  * the current window. You update mark, and a dot in another window, if it is
  177.  * greater than the place where you did the insert. Return TRUE if all is
  178.  * well, and FALSE on errors. 
  179.  */
  180. linsert(n, c)
  181.     int             n;
  182. {
  183.     register char  *cp1;
  184.     register char  *cp2;
  185.     register struct line *lp1;
  186.     struct line    *lp2;
  187.     struct line    *lp3;
  188.     register int    doto;
  189.     register RSIZE  i;
  190.     struct window  *wp;
  191.  
  192.     lchange(WFEDIT);
  193.     lp1 = curwp->w_dotp;    /* Current line         */
  194.     if (lp1 == curbp->b_linep) {    /* At the end: special     */
  195.         /* (now should only happen in empty buffer     */
  196.         if (curwp->w_doto != 0) {
  197.             ewprintf("bug: linsert");
  198.             return FALSE;
  199.         }
  200.         if ((lp2 = lallocx(n)) == NULL)    /* Allocate new line */
  201.             return FALSE;
  202.         lp3 = lp1->l_bp;/* Previous line     */
  203.         lp3->l_fp = lp2;/* Link in         */
  204.         lp2->l_fp = lp1;
  205.         lp1->l_bp = lp2;
  206.         lp2->l_bp = lp3;
  207.         for (i = 0; i < n; ++i)
  208.             lp2->l_text[i] = c;
  209.         for (wp = wheadp; wp != NULL; wp = wp->w_wndp) {
  210.             if (wp->w_linep == lp1)
  211.                 wp->w_linep = lp2;
  212.             if (wp->w_dotp == lp1)
  213.                 wp->w_dotp = lp2;
  214.             if (wp->w_markp == lp1)
  215.                 wp->w_markp = lp2;
  216.         }
  217.         /* NOSTRICT */
  218.         curwp->w_doto = n;
  219.         return TRUE;
  220.     }
  221.     doto = curwp->w_doto;    /* Save for later.     */
  222.     /* NOSTRICT (2) */
  223.     if (lp1->l_used + n > lp1->l_size) {    /* Hard: reallocate     */
  224.         if ((lp2 = lallocx(lp1->l_used + n)) == NULL)
  225.             return FALSE;
  226.         cp1 = &lp1->l_text[0];
  227.         cp2 = &lp2->l_text[0];
  228.         while (cp1 != &lp1->l_text[doto])
  229.             *cp2++ = *cp1++;
  230.         /* NOSTRICT */
  231.         cp2 += n;
  232.         while (cp1 != &lp1->l_text[lp1->l_used])
  233.             *cp2++ = *cp1++;
  234.         lp1->l_bp->l_fp = lp2;
  235.         lp2->l_fp = lp1->l_fp;
  236.         lp1->l_fp->l_bp = lp2;
  237.         lp2->l_bp = lp1->l_bp;
  238.         free((char *) lp1);
  239.     } else {        /* Easy: in place     */
  240.         lp2 = lp1;    /* Pretend new line     */
  241.         /* NOSTRICT */
  242.         lp2->l_used += n;
  243.         cp2 = &lp1->l_text[lp1->l_used];
  244.  
  245.         cp1 = cp2 - n;
  246.         while (cp1 != &lp1->l_text[doto])
  247.             *--cp2 = *--cp1;
  248.     }
  249.     for (i = 0; i < n; ++i)    /* Add the characters     */
  250.         lp2->l_text[doto + i] = c;
  251.  
  252.     for (wp = wheadp; wp != NULL; wp = wp->w_wndp) {
  253.         if (wp->w_linep == lp1)
  254.             wp->w_linep = lp2;
  255.         if (wp->w_dotp == lp1) {
  256.             wp->w_dotp = lp2;
  257.             if (wp == curwp || wp->w_doto > doto)
  258.                 /* NOSTRICT */
  259.                 wp->w_doto += n;
  260.         }
  261.         if (wp->w_markp == lp1) {
  262.             wp->w_markp = lp2;
  263.             if (wp->w_marko > doto)
  264.                 /* NOSTRICT */
  265.                 wp->w_marko += n;
  266.         }
  267.     }
  268.     return TRUE;
  269. }
  270.  
  271. /*
  272.  * Insert a newline into the buffer at the current location of dot in the
  273.  * current window.  The funny ass-backwards way is no longer used. 
  274.  */
  275. lnewline()
  276. {
  277.     register struct line *lp1;
  278.     register struct line *lp2;
  279.     register int    doto;
  280.     register int    nlen;
  281.     struct window  *wp;
  282.  
  283.     lchange(WFHARD);
  284.     lp1 = curwp->w_dotp;    /* Get the address and     */
  285.     doto = curwp->w_doto;    /* offset of "."     */
  286.     if (doto == 0) {    /* avoid unnessisary copying */
  287.         if ((lp2 = lallocx(0)) == NULL)    /* new first part     */
  288.             return FALSE;
  289.         lp2->l_bp = lp1->l_bp;
  290.         lp1->l_bp->l_fp = lp2;
  291.         lp2->l_fp = lp1;
  292.         lp1->l_bp = lp2;
  293.         if (lp1->l_bp == lp1->l_fp)    /* Screwy case */
  294.             for (wp = wheadp; wp != NULL; wp = wp->w_wndp)
  295.                 if (wp->w_linep == lp1)
  296.                     wp->w_linep = lp2;
  297.         return TRUE;
  298.     }
  299.     nlen = llength(lp1) - doto;    /* length of new part     */
  300.     if ((lp2 = lallocx(nlen)) == NULL)    /* New second half line */
  301.         return FALSE;
  302.     if (nlen != 0)
  303.         bcopy(&lp1->l_text[doto], &lp2->l_text[0], nlen);
  304.     lp1->l_used = doto;
  305.     lp2->l_bp = lp1;
  306.     lp2->l_fp = lp1->l_fp;
  307.     lp1->l_fp = lp2;
  308.     lp2->l_fp->l_bp = lp2;
  309.     for (wp = wheadp; wp != NULL; wp = wp->w_wndp) {    /* Windows     */
  310.         if (wp->w_dotp == lp1 && wp->w_doto >= doto) {
  311.             wp->w_dotp = lp2;
  312.             wp->w_doto -= doto;
  313.         }
  314.         if (wp->w_markp == lp1 && wp->w_marko >= doto) {
  315.             wp->w_markp = lp2;
  316.             wp->w_marko -= doto;
  317.         }
  318.     }
  319.     return TRUE;
  320. }
  321.  
  322. /*
  323.  * This function copies a string of length N into the kill buffer starting at
  324.  * index X. 
  325.  */
  326. static          VOID
  327. kcopy(str, x, n)
  328.     register char  *str;
  329.     register RSIZE  x;
  330.     register int    n;
  331. {
  332.     register int    i;
  333.  
  334.     for (i = 0; i < n; i++) {
  335.         kbufp[x >> KBSHIFT][x & (KBLOCK - 1)] = *str;
  336.         str++;
  337.         x++;
  338.     }
  339. }
  340.  
  341.  
  342. /*
  343.  * This function deletes "n" bytes forward from dot. It understands how do
  344.  * deal with end of lines, etc. It returns TRUE if all of the characters were
  345.  * deleted, and FALSE if they were not (because dot ran into the end of the
  346.  * buffer. Kflag is true if we instert the deleted character into the kill
  347.  * buffer. 
  348.  */
  349. fdelete(n, kflag)
  350.     RSIZE           n;
  351. {
  352.     register char  *cp1;
  353.     register char  *cp2;
  354.     register struct line *dotp;
  355.     register int    doto;
  356.     register RSIZE  chunk;
  357.     struct window  *wp;
  358.  
  359.     while (n != 0) {
  360.         dotp = curwp->w_dotp;
  361.         doto = curwp->w_doto;
  362.         if (dotp == curbp->b_linep)    /* Hit end of buffer.     */
  363.             return FALSE;
  364.         chunk = dotp->l_used - doto;    /* Size of chunk.     */
  365.         if (chunk > n)
  366.             chunk = n;
  367.         if (chunk == 0) {    /* End of line, merge.     */
  368.             if (dotp == lback(curbp->b_linep))
  369.                 return FALSE;    /* End of buffer.     */
  370.             lchange(WFHARD);
  371.             if (ldelnewline() == FALSE
  372.                 || (kflag && kinsert('\n', KFORW) == FALSE))
  373.                 return FALSE;
  374.             --n;
  375.             continue;
  376.         }
  377.         lchange(WFEDIT);
  378.         cp1 = &dotp->l_text[doto];    /* Scrunch text.     */
  379.         cp2 = cp1 + chunk;
  380.         if (kflag) {
  381.             while (ksize - kused < chunk)
  382.                 if (kgrow(FALSE) == FALSE)
  383.                     return FALSE;
  384.             kcopy(cp1, kallst + kused, (int) chunk);
  385.             kused += chunk;
  386.         }
  387.         while (cp2 != &dotp->l_text[dotp->l_used])
  388.             *cp1++ = *cp2++;
  389.         dotp->l_used -= (int) chunk;
  390.         for (wp = wheadp; wp != NULL; wp = wp->w_wndp) {
  391.             if (wp->w_dotp == dotp && wp->w_doto >= doto) {
  392.                 /* NOSTRICT */
  393.                 wp->w_doto -= chunk;
  394.                 if (wp->w_doto < doto)
  395.                     wp->w_doto = doto;
  396.             }
  397.             if (wp->w_markp == dotp && wp->w_marko >= doto) {
  398.                 /* NOSTRICT */
  399.                 wp->w_marko -= chunk;
  400.                 if (wp->w_marko < doto)
  401.                     wp->w_marko = doto;
  402.             }
  403.         }
  404.         n -= chunk;
  405.     }
  406.     return TRUE;
  407. }
  408. /*
  409.  * This function deletes "n" bytes backwards from dot. It understands how do
  410.  * deal with end of lines, etc. It returns TRUE if all of the characters were
  411.  * deleted, and FALSE if they were not (because dot ran into the end of the
  412.  * buffer. Kflag is true if we instert the deleted character into the kill
  413.  * buffer. 
  414.  */
  415. bdelete(n, kflag)
  416.     RSIZE           n;
  417. {
  418.     register char  *cp1;
  419.     register char  *cp2;
  420.     register struct line *dotp;
  421.     register int    doto;
  422.     register RSIZE  chunk;
  423.     struct window  *wp;
  424.  
  425.     while (n != 0) {
  426.         dotp = curwp->w_dotp;
  427.         doto = curwp->w_doto;
  428.         if (dotp == curbp->b_linep)    /* Hit end of buffer.     */
  429.             return FALSE;
  430.         chunk = doto;    /* Size of chunk.     */
  431.         if (chunk > n)
  432.             chunk = n;
  433.         if (chunk == 0) {    /* End of line, merge.     */
  434.             if (backchar(FFRAND, 1) == FALSE)
  435.                 return FALSE;    /* End of buffer.     */
  436.             lchange(WFHARD);
  437.             if (ldelnewline() == FALSE
  438.                 || (kflag && kinsert('\n', KBACK) == FALSE))
  439.                 return FALSE;
  440.             --n;
  441.             continue;
  442.         }
  443.         lchange(WFEDIT);
  444.         cp2 = &dotp->l_text[doto];    /* Scrunch text.     */
  445.         cp1 = cp2 - chunk;
  446.         if (kflag) {
  447.             while (kstart - kallst < chunk)
  448.                 if (kgrow(TRUE) == FALSE)
  449.                     return FALSE;
  450.             kcopy(cp1, kstart - chunk, (int) chunk);
  451.             kstart -= chunk;
  452.         }
  453.         while (cp2 != &dotp->l_text[dotp->l_used])
  454.             *cp1++ = *cp2++;
  455.         dotp->l_used -= (int) chunk;
  456.         doto -= chunk;
  457.         for (wp = wheadp; wp != NULL; wp = wp->w_wndp) {
  458.             if (wp->w_dotp == dotp && wp->w_doto >= doto) {
  459.                 /* NOSTRICT */
  460.                 wp->w_doto -= chunk;
  461.                 if (wp->w_doto < doto)
  462.                     wp->w_doto = doto;
  463.             }
  464.             if (wp->w_markp == dotp && wp->w_marko >= doto) {
  465.                 /* NOSTRICT */
  466.                 wp->w_marko -= chunk;
  467.                 if (wp->w_marko < doto)
  468.                     wp->w_marko = doto;
  469.             }
  470.         }
  471.         n -= chunk;
  472.     }
  473.     return TRUE;
  474. }
  475.  
  476. /*
  477.  * Delete a newline. Join the current line with the next line. If the next
  478.  * line is the magic header line always return TRUE; merging the last line
  479.  * with the header line can be thought of as always being a successful
  480.  * operation, even if nothing is done, and this makes the kill buffer work
  481.  * "right". Easy cases can be done by shuffling data around. Hard cases
  482.  * require that lines be moved about in memory. Return FALSE on error and
  483.  * TRUE if all looks ok. 
  484.  */
  485. ldelnewline()
  486. {
  487.     register struct line *lp1;
  488.     register struct line *lp2;
  489.     register struct window *wp;
  490.     struct line    *lp3;
  491.  
  492.     lp1 = curwp->w_dotp;
  493.     lp2 = lp1->l_fp;
  494.     if (lp2 == curbp->b_linep)    /* At the buffer end.     */
  495.         return TRUE;
  496.     if (lp2->l_used <= lp1->l_size - lp1->l_used) {
  497.         bcopy(&lp2->l_text[0], &lp1->l_text[lp1->l_used], lp2->l_used);
  498.         for (wp = wheadp; wp != NULL; wp = wp->w_wndp) {
  499.             if (wp->w_linep == lp2)
  500.                 wp->w_linep = lp1;
  501.             if (wp->w_dotp == lp2) {
  502.                 wp->w_dotp = lp1;
  503.                 wp->w_doto += lp1->l_used;
  504.             }
  505.             if (wp->w_markp == lp2) {
  506.                 wp->w_markp = lp1;
  507.                 wp->w_marko += lp1->l_used;
  508.             }
  509.         }
  510.         lp1->l_used += lp2->l_used;
  511.         lp1->l_fp = lp2->l_fp;
  512.         lp2->l_fp->l_bp = lp1;
  513.         free((char *) lp2);
  514.         return TRUE;
  515.     }
  516.     if ((lp3 = lalloc(lp1->l_used + lp2->l_used)) == NULL)
  517.         return FALSE;
  518.     bcopy(&lp1->l_text[0], &lp3->l_text[0], lp1->l_used);
  519.     bcopy(&lp2->l_text[0], &lp3->l_text[lp1->l_used], lp2->l_used);
  520.     lp1->l_bp->l_fp = lp3;
  521.     lp3->l_fp = lp2->l_fp;
  522.     lp2->l_fp->l_bp = lp3;
  523.     lp3->l_bp = lp1->l_bp;
  524.     for (wp = wheadp; wp != NULL; wp = wp->w_wndp) {
  525.         if (wp->w_linep == lp1 || wp->w_linep == lp2)
  526.             wp->w_linep = lp3;
  527.         if (wp->w_dotp == lp1)
  528.             wp->w_dotp = lp3;
  529.         else if (wp->w_dotp == lp2) {
  530.             wp->w_dotp = lp3;
  531.             wp->w_doto += lp1->l_used;
  532.         }
  533.         if (wp->w_markp == lp1)
  534.             wp->w_markp = lp3;
  535.         else if (wp->w_markp == lp2) {
  536.             wp->w_markp = lp3;
  537.             wp->w_marko += lp1->l_used;
  538.         }
  539.     }
  540.     free((char *) lp1);
  541.     free((char *) lp2);
  542.     return TRUE;
  543. }
  544.  
  545. /*
  546.  * Replace plen characters before dot with argument string. Control-J
  547.  * characters in st are interpreted as newlines. There is a casehack disable
  548.  * flag (normally it likes to match case of replacement to what was there). 
  549.  */
  550. lreplace(plen, st, f)
  551.     register RSIZE  plen;    /* length to remove         */
  552.     char           *st;    /* replacement string         */
  553.     int             f;    /* case hack disable         */
  554. {
  555.     register RSIZE  rlen;    /* replacement length         */
  556.     register int    rtype;    /* capitalization         */
  557.     register int    c;    /* used for random characters     */
  558.     register int    doto;    /* offset into line         */
  559.  
  560.     /*
  561.      * Find the capitalization of the word that was found. f says use
  562.      * exact case of replacement string (same thing that happens with
  563.      * lowercase found), so bypass check. 
  564.      */
  565.     /* NOSTRICT */
  566.     (VOID) backchar(FFARG | FFRAND, (int) plen);
  567.     rtype = _L;
  568.     c = lgetc(curwp->w_dotp, curwp->w_doto);
  569.     if (ISUPPER(c) != FALSE && f == FALSE) {
  570.         rtype = _U | _L;
  571.         if (curwp->w_doto + 1 < llength(curwp->w_dotp)) {
  572.             c = lgetc(curwp->w_dotp, curwp->w_doto + 1);
  573.             if (ISUPPER(c) != FALSE) {
  574.                 rtype = _U;
  575.             }
  576.         }
  577.     }
  578.     /*
  579.      * make the string lengths match (either pad the line so that it will
  580.      * fit, or scrunch out the excess). be careful with dot's offset. 
  581.      */
  582.     rlen = st ? strlen(st) : 0;
  583.     doto = curwp->w_doto;
  584.     if (plen > rlen)
  585.         (VOID) fdelete((RSIZE) (plen - rlen), FALSE);
  586.     else if (plen < rlen) {
  587.         if (linsert((int) (rlen - plen), ' ') == FALSE)
  588.             return FALSE;
  589.     }
  590.     curwp->w_doto = doto;
  591.  
  592.     /*
  593.      * do the replacement:    If was capital, then place first char as if
  594.      * upper, and subsequent chars as if lower. If inserting upper, check
  595.      * replacement for case. 
  596.      */
  597.     while ((c = CHARMASK(*st++)) != '\0') {
  598.         if ((rtype & _U) != 0 && ISLOWER(c) != 0)
  599.             c = TOUPPER(c);
  600.         if (rtype == (_U | _L))
  601.             rtype = _L;
  602.         if (c == CCHR('J')) {
  603.             if (curwp->w_doto == llength(curwp->w_dotp))
  604.                 (VOID) forwchar(FFRAND, 1);
  605.             else {
  606.                 if (fdelete((RSIZE) 1, FALSE) != FALSE)
  607.                     (VOID) lnewline();
  608.             }
  609.         } else if (curwp->w_dotp == curbp->b_linep) {
  610.             (VOID) linsert(1, c);
  611.         } else if (curwp->w_doto == llength(curwp->w_dotp)) {
  612.             if (fdelete((RSIZE) 1, FALSE) != FALSE)
  613.                 (VOID) linsert(1, c);
  614.         } else
  615.             lputc(curwp->w_dotp, curwp->w_doto++, c);
  616.     }
  617.     lchange(WFHARD);
  618.     return (TRUE);
  619. }
  620.  
  621. /*
  622.  * Delete all of the text saved in the kill buffer. Called by commands when a
  623.  * new kill context is being created. The kill buffer array is released, just
  624.  * in case the buffer has grown to immense size. No errors. 
  625.  */
  626. VOID
  627. kdelete()
  628. {
  629.     int             i;
  630.     if (kbufp != NULL)
  631.         for (i = 0; i < KBSIZE; i++)
  632.             if (kbufp[i] != NULL) {
  633.                 free((char *) kbufp[i]);
  634.                 kbufp[i] = NULL;
  635.             }
  636.     kstart = kused = ksize = kallst = 0;
  637. }
  638.  
  639. /*
  640.  * Insert a character to the kill buffer, enlarging the buffer if there isn't
  641.  * any room. Always grow the buffer in chunks, on the assumption that if you
  642.  * put something in the kill buffer you are going to put more stuff there too
  643.  * later. Return TRUE if all is well, and FALSE on errors. Print a message on
  644.  * errors. Dir says whether to put it at back or front. 
  645.  */
  646. kinsert(c, dir)
  647. {
  648.  
  649.     if (kused == ksize && dir == KFORW && kgrow(FALSE) == FALSE)
  650.         return FALSE;
  651.     if (kstart == kallst && dir == KBACK && kgrow(TRUE) == FALSE)
  652.         return FALSE;
  653.     if (dir == KFORW) {
  654.         kbufp[(kallst + kused) >> KBSHIFT][(kallst + kused) & (KBLOCK - 1)] = c;
  655.         kused++;
  656.     } else if (dir == KBACK) {
  657.         kstart--;
  658.         kbufp[kstart >> KBSHIFT][kstart & (KBLOCK - 1)] = c;
  659.     } else
  660.         panic("broken kinsert call");    /* Oh shit! */
  661.     return (TRUE);
  662. }
  663.  
  664. /*
  665.  * kgrow - just get more kill buffer for the callee. back is true if we are
  666.  * trying to get space at the beginning of the kill buffer. 
  667.  */
  668. static
  669. kgrow(back)
  670. {
  671.     register char  *nbufp;
  672.     register int    i;
  673.     if (kbufp == NULL)
  674.         if ((kbufp = (char **) malloc(KBSIZE * sizeof(nbufp))) == NULL) {
  675.             ewprintf("Can't get %d bytes", KBSIZE * sizeof(nbufp));
  676.             return (FALSE);
  677.         } else
  678.             for (i = 0; i < KBSIZE; i++)
  679.                 kbufp[i] = NULL;
  680.     if ((kallst == 0) && (kused == 0))
  681.         kallst = kstart = ((long) KBSIZE / 2) * KBLOCK;
  682.     if ((back && (kallst == 0)) ||
  683.         (!back && ((kallst + kused) == ((long) KBSIZE - 1) * KBLOCK))) {
  684.         ewprintf("Kill buffer overflow!");
  685.         return (FALSE);
  686.     }
  687.     if ((nbufp = (char *) malloc(KBLOCK)) == NULL) {
  688.         ewprintf("Can't get %d bytes", KBLOCK);
  689.         return (FALSE);
  690.     }
  691.     if (back) {
  692.         kallst -= KBLOCK;
  693.         kused += KBLOCK;
  694.         kbufp[kallst >> KBSHIFT] = nbufp;
  695.     } else
  696.         kbufp[(kallst + ksize) >> KBSHIFT] = nbufp;
  697.     ksize += KBLOCK;
  698.     return TRUE;
  699. }
  700.  
  701. /*
  702.  * This function gets characters from the kill buffer. If the character index
  703.  * "n" is off the end, it returns "-1". This lets the caller just scan along
  704.  * until it gets a "-1" back. 
  705.  */
  706. char
  707. kremove(n)
  708.     register RSIZE  n;
  709. {
  710.     register RSIZE  addr;
  711.     addr = (n + kstart);
  712.     if ((n < 0) || (addr >= (kused + kallst)))
  713.         return -1;
  714.     return CHARMASK(kbufp[addr >> KBSHIFT][addr & (KBLOCK - 1)]);
  715. }
  716.  
  717. /*
  718.  * This function gets entire lines from the kill buffer. If there is no line,
  719.  * a "-1" is returned, otherwise n is advanced, the characters are copied to
  720.  * str and the length is returned. 
  721.  */
  722. int
  723. klremove(str, n)
  724.     char           *str;
  725.     RSIZE          *n;
  726. {
  727.     register int    size;
  728.     register RSIZE  addr;
  729.  
  730.     size = 0;
  731.     addr = kstart + *n;
  732.     while ((addr >= kstart) && (addr < (kallst + kused)) &&
  733.      ((*str++ = kbufp[addr >> KBSHIFT][addr & (KBLOCK - 1)]) != '\n')) {
  734.         addr++;
  735.         size++;
  736.     }
  737.     if ((addr >= kstart) && (addr < (kallst + kused))) {
  738.         *n = addr - kstart;
  739.         return (size);
  740.     } else
  741.         return -1;
  742. }
  743.