home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / vile-src.zip / vile-8.1 / line.c < prev    next >
C/C++ Source or Header  |  1998-09-22  |  35KB  |  1,619 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 register 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.  * $Header: /usr/build/vile/vile/RCS/line.c,v 1.112 1998/09/22 23:45:54 tom Exp $
  14.  *
  15.  */
  16.  
  17. /* #define POISON */
  18. #ifdef POISON
  19. #define poison(p,s) (void)memset((char *)p, 0xdf, s)
  20. #else
  21. #define poison(p,s)
  22. #endif
  23.  
  24. #include    "estruct.h"
  25. #include    "edef.h"
  26.  
  27. #define roundlenup(n) ((n+NBLOCK-1) & ~(NBLOCK-1))
  28.  
  29. static    int    doput(int f, int n, int after, REGIONSHAPE shape);
  30. static    int    ldelnewline (void);
  31. static    int    PutChar(int n, REGIONSHAPE shape);
  32.  
  33. #if OPT_SHOW_REGS && OPT_UPBUFF
  34. static    void    relist_registers (void);
  35. #else
  36. #define relist_registers()
  37. #endif
  38.  
  39. /*
  40.  * Test the 'report' threshold, returning true if the argument is above it.
  41.  */
  42. int
  43. do_report (L_NUM value)
  44. {
  45.     if (value < 0)
  46.         value = -value;
  47.     return (global_g_val(GVAL_REPORT) > 0
  48.        &&   global_g_val(GVAL_REPORT) <= value);
  49. }
  50.  
  51. /*
  52.  * This routine allocates a block of memory large enough to hold a LINE
  53.  * containing "used" characters. The block is always rounded up a bit. Return
  54.  * a pointer to the new block, or NULL if there isn't any memory left. Print a
  55.  * message in the message line if no space.
  56.  */
  57. /*ARGSUSED*/
  58. LINEPTR
  59. lalloc(register int used, BUFFER *bp)
  60. {
  61.     register LINE    *lp;
  62.     register SIZE_T    size;
  63.  
  64.     /* lalloc(-1) is used by undo for placeholders */
  65.     if (used < 0)  {
  66.         size = 0;
  67.     } else {
  68.         size = roundlenup(used);
  69.     }
  70.     /* see if the buffer LINE block has any */
  71.     if ((lp = bp->b_freeLINEs) != NULL) {
  72.         bp->b_freeLINEs = lp->l_nxtundo;
  73.     } else if ((lp = typealloc(LINE)) == NULL) {
  74.         (void)no_memory("LINE");
  75.         return NULL;
  76.     }
  77.     lp->l_text = NULL;
  78.     if (size && (lp->l_text = castalloc(char,size)) == NULL) {
  79.         (void)no_memory("LINE text");
  80.         poison(lp, sizeof(*lp));
  81.         free((char *)lp);
  82.         return NULL;
  83.     }
  84.     lp->l_size = size;
  85. #if !SMALLER
  86.     lp->l_number = 0;
  87. #endif
  88.     lp->l_used = used;
  89.     lsetclear(lp);
  90.     lp->l_nxtundo = null_ptr;
  91.     return lp;
  92. }
  93.  
  94. /*ARGSUSED*/
  95. void
  96. lfree(register LINEPTR lp, register BUFFER *bp)
  97. {
  98.     if (lisreal(lp))
  99.         ltextfree(lp,bp);
  100.  
  101.     /* if the buffer doesn't have its own block of LINEs, or this
  102.         one isn't in that range, free it */
  103.     if (!bp->b_LINEs || lp < bp->b_LINEs || lp >= bp->b_LINEs_end) {
  104.         poison(lp, sizeof(*lp));
  105.         free((char *)lp);
  106.     } else {
  107.         /* keep track of freed buffer LINEs here */
  108.         lp->l_nxtundo = bp->b_freeLINEs;
  109.         bp->b_freeLINEs = lp;
  110. #ifdef POISON
  111.         /* catch references hard */
  112.         set_lback(lp, (LINE *)1);
  113.         set_lforw(lp, (LINE *)1);
  114.         lp->l_text = (char *)1;
  115.         lp->l_size = lp->l_used = LINENOTREAL;
  116. #endif
  117.     }
  118. }
  119.  
  120. /*ARGSUSED*/
  121. void
  122. ltextfree(register LINE *lp, register BUFFER *bp)
  123. {
  124.     register UCHAR *ltextp;
  125.  
  126.     ltextp = (UCHAR *)lp->l_text;
  127.     if (ltextp) {
  128.         lp->l_text = NULL;
  129.         if (bp->b_ltext) { /* could it be in the big range? */
  130.             if (ltextp < bp->b_ltext || ltextp >= bp->b_ltext_end) {
  131.                 poison(ltextp, lp->l_size);
  132.                 free((char *)ltextp);
  133.             } /* else {
  134.             could keep track of freed big range text here;
  135.             } */
  136.         } else {
  137.             poison(ltextp, lp->l_size);
  138.             free((char *)ltextp);
  139.         }
  140.     } /* else nothing to free */
  141. }
  142.  
  143. /*
  144.  * Delete line "lp". Fix all of the links that might point at it (they are
  145.  * moved to offset 0 of the next line. Unlink the line from whatever buffer it
  146.  * might be in. The buffers are updated too; the magic
  147.  * conditions described in the above comments don't hold here.
  148.  * Memory is not released, so line can be saved in undo stacks.
  149.  */
  150. void
  151. lremove(register BUFFER *bp, register LINEPTR lp)
  152. {
  153.     register WINDOW *wp;
  154.     register LINEPTR point;
  155.  
  156.     point = lforw(lp);
  157.  
  158. #if !WINMARK
  159.     if (MK.l == lp) {
  160.         MK.l = point;
  161.         MK.o = 0;
  162.     }
  163. #endif
  164.     for_each_window(wp) {
  165.         if (wp->w_line.l == lp)
  166.             wp->w_line.l = point;
  167.         if (wp->w_dot.l == lp) {
  168.             wp->w_dot.l  = point;
  169.             wp->w_dot.o  = 0;
  170.         }
  171. #if WINMARK
  172.         if (wp->w_mark.l == lp) {
  173.             wp->w_mark.l = point;
  174.             wp->w_mark.o = 0;
  175.         }
  176. #endif
  177. #if 0
  178.         if (wp->w_lastdot.l == lp) {
  179.             wp->w_lastdot.l = point;
  180.             wp->w_lastdot.o = 0;
  181.         }
  182. #endif
  183.     }
  184.     if (bp->b_nwnd == 0) {
  185.         if (bp->b_dot.l == lp) {
  186.             bp->b_dot.l = point;
  187.             bp->b_dot.o = 0;
  188.         }
  189. #if WINMARK
  190.         if (bp->b_mark.l == lp) {
  191.             bp->b_mark.l = point;
  192.             bp->b_mark.o = 0;
  193.         }
  194. #endif
  195. #if 0
  196.         if (bp->b_lastdot.l == lp) {
  197.             bp->b_lastdot.l = point;
  198.             bp->b_lastdot.o = 0;
  199.         }
  200. #endif
  201.     }
  202. #if 0
  203.     if (bp->b_nmmarks != NULL) { /* fix the named marks */
  204.         int i;
  205.         struct MARK *mp;
  206.         for (i = 0; i < 26; i++) {
  207.             mp = &(bp->b_nmmarks[i]);
  208.             if (mp->p == lp) {
  209.                 mp->p = point;
  210.                 mp->o = 0;
  211.             }
  212.         }
  213.     }
  214. #endif
  215. #if OPT_VIDEO_ATTRS
  216.     {
  217.         AREGION *ap = bp->b_attribs;
  218.         while (ap != NULL) {
  219.         int samestart = (ap->ar_region.r_orig.l == lp);
  220.         int sameend   = (ap->ar_region.r_end.l == lp);
  221.         if (samestart && sameend) {
  222.             AREGION *tofree = ap;
  223.             ap = ap->ar_next;
  224.             free_attrib(bp, tofree);
  225.         }
  226.         else if (samestart) {
  227.             ap->ar_region.r_orig.l = point;
  228.             ap->ar_region.r_orig.o = 0;
  229.             ap = ap->ar_next;
  230.         }
  231.         else if (sameend) {
  232.             ap->ar_region.r_end.l = lback(lp);
  233.             ap->ar_region.r_end.o = llength(ap->ar_region.r_end.l);
  234.             ap = ap->ar_next;
  235.         }
  236.         else
  237.             ap = ap->ar_next;
  238.         }
  239.     }
  240. #endif /* OPT_VIDEO_ATTRS */
  241.     set_lforw(lback(lp), lforw(lp));
  242.     set_lback(lforw(lp), lback(lp));
  243. }
  244.  
  245. int
  246. insspace(int f, int n)    /* insert spaces forward into text */
  247. {
  248.     if (!linsert(n, ' '))
  249.         return FALSE;
  250.     return backchar(f, n);
  251. }
  252.  
  253. int
  254. lstrinsert(    /* insert string forward into text */
  255. const char *s,    /* if NULL, treat as "" */
  256. int len)    /* if non-zero, insert exactly this amount.  pad if needed */
  257. {
  258.     const char *p = s;
  259.     int n, b = 0;
  260.     if (len <= 0)
  261.         n = HUGE;
  262.     else
  263.         n = len;
  264.     while (p && *p && n) {
  265.         b++;
  266.         if (!linsert(1, *p++))
  267.             return FALSE;
  268.         n--;
  269.     }
  270.     if (n && len > 0) {    /* need to pad? */
  271.         if (!linsert(n, ' '))
  272.             return FALSE;
  273.         b += n;
  274.     }
  275.  
  276.     DOT.o -= b;
  277.  
  278.     return TRUE;
  279. }
  280.  
  281. /*
  282.  * Insert "n" copies of the character "c" at the current location of dot. In
  283.  * the easy case all that happens is the text is stored in the line. In the
  284.  * hard case, the line has to be reallocated. When the window list is updated,
  285.  * take special care; I screwed it up once. You always update dot in the
  286.  * current window. You update mark, and a dot in another window, if it is
  287.  * greater than the place where you did the insert. Return TRUE if all is
  288.  * well, and FALSE on errors.
  289.  */
  290. int
  291. linsert(int n, int c)
  292. {
  293.     register char    *cp1;
  294.     register char    *cp2;
  295.     register LINE    *tmp;
  296.     register LINEPTR lp1;
  297.     register LINEPTR lp2;
  298.     register LINEPTR lp3;
  299.     register int    doto;
  300.     register int    i;
  301.     register WINDOW *wp;
  302.     register char    *ntext;
  303.     SIZE_T    nsize;
  304.  
  305.     lp1 = DOT.l;                /* Current line     */
  306.     if (lp1 == buf_head(curbp)) {        /* At the end: special    */
  307.         if (DOT.o != 0) {
  308.             mlforce("BUG: linsert");
  309.             return (FALSE);
  310.         }
  311.         lp2 = lalloc(n, curbp);        /* Allocate new line    */
  312.         if (lp2 == null_ptr)
  313.             return (FALSE);
  314.  
  315.         lp3 = lback(lp1);        /* Previous line    */
  316.         set_lforw(lp3, lp2);        /* Link in        */
  317.         set_lforw(lp2, lp1);
  318.         set_lback(lp1, lp2);
  319.         set_lback(lp2, lp3);
  320.         (void)memset(lp2->l_text, c, (SIZE_T)n);
  321.  
  322.         tag_for_undo(lp2);
  323.  
  324.         /* don't move DOT until after tagging for undo */
  325.         /*  (it's important in an empty buffer) */
  326.         DOT.l = lp2;
  327.         DOT.o = n;
  328.         chg_buff(curbp, WFINS|WFEDIT);
  329.         return (TRUE);
  330.     }
  331.     doto = DOT.o;                /* Save for later.    */
  332.     tmp  = lp1;
  333.     nsize = llength(tmp) + n;
  334.     if (nsize > tmp->l_size) {        /* Hard: reallocate    */
  335.         /* first, create the new image */
  336.         nsize = roundlenup((int)nsize);
  337.         copy_for_undo(lp1);
  338.         if ((ntext=castalloc(char,nsize)) == NULL)
  339.             return (FALSE);
  340.         if (lp1->l_text) /* possibly NULL if l_size == 0 */
  341.             (void)memcpy(&ntext[0], &lp1->l_text[0], (SIZE_T)doto);
  342.         (void)memset(&ntext[doto],   c, (SIZE_T)n);
  343.         if (lp1->l_text) {
  344.             (void)memcpy(&ntext[doto+n], &lp1->l_text[doto],
  345.                     (SIZE_T)(lp1->l_used-doto ));
  346.             ltextfree(lp1,curbp);
  347.         }
  348.         lp1->l_text = ntext;
  349.         lp1->l_size = nsize;
  350.         lp1->l_used += n;
  351.     } else {        /* Easy: in place    */
  352.         copy_for_undo(lp1);
  353.         chg_buff(curbp, WFEDIT);
  354.         tmp = lp1;
  355.         /* don't use memcpy:  overlapping regions.... */
  356.         llength(tmp) += n;
  357.         if (tmp->l_used - n > doto) {
  358.             cp2 = &tmp->l_text[tmp->l_used];
  359.             cp1 = cp2-n;
  360.             while (cp1 != &tmp->l_text[doto])
  361.                 *--cp2 = *--cp1;
  362.         }
  363.         for (i=0; i<n; ++i)        /* Add the characters    */
  364.             tmp->l_text[doto+i] = (char)c;
  365.     }
  366.     chg_buff(curbp, WFEDIT);
  367. #if ! WINMARK
  368.     if (MK.l == lp1) {
  369.         if (MK.o > doto)
  370.             MK.o += n;
  371.     }
  372. #endif
  373.     for_each_window(wp) {            /* Update windows    */
  374.         if (wp->w_dot.l == lp1) {
  375.             if (wp==curwp || wp->w_dot.o>doto)
  376.                 wp->w_dot.o += n;
  377.         }
  378. #if WINMARK
  379.         if (wp->w_mark.l == lp1) {
  380.             if (wp->w_mark.o > doto)
  381.                 wp->w_mark.o += n;
  382.         }
  383. #endif
  384.         if (wp->w_lastdot.l == lp1) {
  385.             if (wp->w_lastdot.o > doto)
  386.                 wp->w_lastdot.o += n;
  387.         }
  388.     }
  389.     do_mark_iterate(mp,
  390.             if (mp->l == lp1) {
  391.                 if (mp->o > doto)
  392.                     mp->o += n;
  393.             }
  394.     );
  395.     return (TRUE);
  396. }
  397.  
  398. /*
  399.  * Insert a newline into the buffer at the current location of dot in the
  400.  * current window. The funny ass-backwards way it does things is not a botch;
  401.  * it just makes the last line in the file not a special case. Return TRUE if
  402.  * everything works out and FALSE on error (memory allocation failure). The
  403.  * update of dot and mark is a bit easier then in the above case, because the
  404.  * split forces more updating.
  405.  */
  406. int
  407. lnewline(void)
  408. {
  409.     register char    *cp1;
  410.     register char    *cp2;
  411.     register LINEPTR lp1;
  412.     register LINEPTR lp2;
  413.     register int    doto;
  414.     register WINDOW *wp;
  415.  
  416.     lp1  = DOT.l;            /* Get the address and    */
  417.     doto = DOT.o;            /* offset of "."    */
  418.  
  419.     if (lp1 == buf_head(curbp)
  420.      && lforw(lp1) == lp1) {
  421.         /* empty buffer -- just  create empty line */
  422.         lp2 = lalloc(doto, curbp);
  423.         if (lp2 == null_ptr)
  424.             return (FALSE);
  425.         /* put lp2 in below lp1 */
  426.         set_lforw(lp2, lforw(lp1));
  427.         set_lforw(lp1, lp2);
  428.         set_lback(lforw(lp2), lp2);
  429.         set_lback(lp2, lp1);
  430.  
  431.         tag_for_undo(lp2);
  432.  
  433.         for_each_window(wp) {
  434.             if (wp->w_line.l == lp1)
  435.                 wp->w_line.l = lp2;
  436.             if (wp->w_dot.l == lp1)
  437.                 wp->w_dot.l = lp2;
  438.         }
  439.  
  440.         chg_buff(curbp, WFHARD|WFINS);
  441.  
  442.         return lnewline();    /* vi really makes _2_ lines */
  443.     }
  444.  
  445.     lp2 = lalloc(doto, curbp);    /* New first half line */
  446.     if (lp2 == null_ptr)
  447.         return (FALSE);
  448.  
  449.     if (doto > 0) {
  450.         register LINE *tmp;
  451.  
  452.         copy_for_undo(lp1);
  453.         tmp = lp1;
  454.         cp1 = tmp->l_text;    /* Shuffle text around    */
  455.         cp2 = lp2->l_text;
  456.         while (cp1 != &tmp->l_text[doto])
  457.             *cp2++ = *cp1++;
  458.         cp2 = tmp->l_text;
  459.         while (cp1 != &tmp->l_text[tmp->l_used])
  460.             *cp2++ = *cp1++;
  461.         tmp->l_used -= doto;
  462.     }
  463.     /* put lp2 in above lp1 */
  464.     set_lback(lp2, lback(lp1));
  465.     set_lback(lp1, lp2);
  466.     set_lforw(lback(lp2), lp2);
  467.     set_lforw(lp2, lp1);
  468.  
  469.     tag_for_undo(lp2);
  470.     dumpuline(lp1);
  471.  
  472. #if ! WINMARK
  473.     if (MK.l == lp1) {
  474.         if (MK.o < doto)
  475.             MK.l = lp2;
  476.         else
  477.             MK.o -= doto;
  478.     }
  479. #endif
  480.     for_each_window(wp) {
  481.         if (wp->w_line.l == lp1)
  482.             wp->w_line.l = lp2;
  483.         if (wp->w_dot.l == lp1) {
  484.             if (wp->w_dot.o < doto)
  485.                 wp->w_dot.l = lp2;
  486.             else
  487.                 wp->w_dot.o -= doto;
  488.         }
  489. #if WINMARK
  490.         if (wp->w_mark.l == lp1) {
  491.             if (wp->w_mark.o < doto)
  492.                 wp->w_mark.l = lp2;
  493.             else
  494.                 wp->w_mark.o -= doto;
  495.         }
  496. #endif
  497.         if (wp->w_lastdot.l == lp1) {
  498.             if (wp->w_lastdot.o < doto)
  499.                 wp->w_lastdot.l = lp2;
  500.             else
  501.                 wp->w_lastdot.o -= doto;
  502.         }
  503.     }
  504.     do_mark_iterate(mp,
  505.             if (mp->l == lp1) {
  506.                 if (mp->o < doto)
  507.                     mp->l = lp2;
  508.                 else
  509.                     mp->o -= doto;
  510.             }
  511.     );
  512.     chg_buff(curbp, WFHARD|WFINS);
  513.     return (TRUE);
  514. }
  515.  
  516. /*
  517.  * This function deletes "n" bytes, starting at dot. It understands how to deal
  518.  * with end of lines, etc. It returns TRUE if all of the characters were
  519.  * deleted, and FALSE if they were not (because dot ran into the end of the
  520.  * buffer. The "kflag" is TRUE if the text should be put in the kill buffer.
  521.  */
  522. int
  523. ldelete(
  524. B_COUNT n,     /* # of chars to delete */
  525. int kflag)    /* put killed text in kill buffer flag */
  526. {
  527.     register char    *cp1;
  528.     register char    *cp2;
  529.     register LINEPTR dotp;
  530.     register LINEPTR nlp;
  531.     register int    doto;
  532.     register int    chunk;
  533.     register WINDOW *wp;
  534.     register int i;
  535.     register int s = TRUE;
  536.  
  537.     lines_deleted = 0;
  538.     while (n > 0) {
  539.         dotp = DOT.l;
  540.         doto = DOT.o;
  541.         if (dotp == buf_head(curbp)) { /* Hit end of buffer.*/
  542.             s = FALSE;
  543.             break;
  544.         }
  545.         chunk = dotp->l_used-doto; /* Size of chunk.    */
  546.         if (chunk > (int)n)
  547.             chunk = (int)n;
  548.         if (chunk == 0) {        /* End of line, merge.    */
  549.             /* first take out any whole lines below this one */
  550.             nlp = lforw(dotp);
  551.             while (nlp != buf_head(curbp)
  552.                &&  llength(nlp)+1 < n) {
  553.                 if (kflag) {
  554.                     s = kinsert('\n');
  555.                     for (i = 0; i < llength(nlp) &&
  556.                                 s == TRUE; i++)
  557.                         s = kinsert(lgetc(nlp,i));
  558.                 }
  559.                 if (s != TRUE)
  560.                     break;
  561.                 lremove(curbp, nlp);
  562.                 lines_deleted++;
  563.                 toss_to_undo(nlp);
  564.                 n -= llength(nlp)+1;
  565.                 nlp = lforw(dotp);
  566.             }
  567.             if (s != TRUE)
  568.                 break;
  569.             s = ldelnewline();
  570.             chg_buff(curbp, WFHARD|WFKILLS);
  571.             if (s != TRUE)
  572.                 break;
  573.             if (kflag && (s = kinsert('\n')) != TRUE)
  574.                 break;
  575.             --n;
  576.             lines_deleted++;
  577.             continue;
  578.         }
  579.         copy_for_undo(DOT.l);
  580.         chg_buff(curbp, WFEDIT);
  581.  
  582.         cp1 = dotp->l_text + doto; /* Scrunch text.    */
  583.         cp2 = cp1 + chunk;
  584.         if (kflag) {        /* Kill?        */
  585.             while (cp1 != cp2) {
  586.                 if ((s = kinsert(*cp1)) != TRUE)
  587.                     break;
  588.                 ++cp1;
  589.             }
  590.             if (s != TRUE)
  591.                 break;
  592.             cp1 = dotp->l_text + doto;
  593.         }
  594.         while (cp2 != dotp->l_text + dotp->l_used)
  595.             *cp1++ = *cp2++;
  596.         dotp->l_used -= chunk;
  597. #if ! WINMARK
  598.         if (MK.l == dotp && MK.o > doto) {
  599.             MK.o -= chunk;
  600.             if (MK.o < doto)
  601.                 MK.o = doto;
  602.         }
  603. #endif
  604.         for_each_window(wp) {        /* Fix windows        */
  605.             if (wp->w_dot.l == dotp
  606.              && wp->w_dot.o > doto) {
  607.                 wp->w_dot.o -= chunk;
  608.                 if (wp->w_dot.o < doto)
  609.                     wp->w_dot.o = doto;
  610.             }
  611. #if WINMARK
  612.             if (wp->w_mark.l == dotp
  613.              && wp->w_mark.o > doto) {
  614.                 wp->w_mark.o -= chunk;
  615.                 if (wp->w_mark.o < doto)
  616.                     wp->w_mark.o = doto;
  617.             }
  618. #endif
  619.             if (wp->w_lastdot.l == dotp
  620.              && wp->w_lastdot.o > doto) {
  621.                 wp->w_lastdot.o -= chunk;
  622.                 if (wp->w_lastdot.o < doto)
  623.                     wp->w_lastdot.o = doto;
  624.             }
  625.         }
  626.         do_mark_iterate(mp,
  627.                 if (mp->l == dotp
  628.                  && mp->o > doto) {
  629.                     mp->o -= chunk;
  630.                     if (mp->o < doto)
  631.                         mp->o = doto;
  632.                 }
  633.         );
  634.         n -= chunk;
  635.     }
  636.     return (s);
  637. }
  638.  
  639. /* getctext:    grab and return a string with text from
  640.         the current line, consisting of chars of type "type"
  641. */
  642. #if OPT_EVAL
  643. char *
  644. getctext(CHARTYPE type)
  645. {
  646.     static char rline[NSTRING];    /* line to return */
  647.  
  648.     (void)screen_string(rline, NSTRING, type);
  649.     return rline;
  650. }
  651. #endif
  652.  
  653. #if OPT_EVAL
  654. /* putctext:    replace the current line with the passed in text    */
  655.  
  656. int
  657. putctext(
  658. CHARTYPE type,
  659. const char *iline)    /* contents of new line */
  660. {
  661.     register int status = TRUE;
  662.  
  663.     TRACE(("putctext:%s%lx:%s\n", type ? "word" : "line", (ULONG) type, iline))
  664.  
  665.     if (b_val(curbp,MDVIEW))
  666.         return rdonly();
  667.  
  668.     mayneedundo();
  669.  
  670.     if (type != 0) {
  671.         regionshape = EXACT;
  672.         while (DOT.o < llength(DOT.l)
  673.           && istype(type, char_at(DOT))) {
  674.             if ((status = forwdelchar(FALSE,1)) != TRUE)
  675.                 return(status);
  676.         }
  677.     } else {
  678.         regionshape = FULLLINE;
  679.         /* delete the current line */
  680.         DOT.o = w_left_margin(curwp); /* start at the beginning of the line */
  681.         if ((status = deltoeol(TRUE, 1)) != TRUE)
  682.             return(status);
  683.     }
  684.  
  685.     /* insert the new text */
  686.     while (*iline) {
  687.         if (*iline == '\n') {
  688.             if (lnewline() != TRUE)
  689.                 return(FALSE);
  690.         } else {
  691.             if (linsert(1, *iline) != TRUE)
  692.                 return(FALSE);
  693.         }
  694.         ++iline;
  695.     }
  696.     if (type == 0) {
  697.         status = lnewline();
  698.         (void)backline(TRUE, 1);
  699.     }
  700.     return(status);
  701. }
  702. #endif
  703.  
  704. /*
  705.  * Delete a newline. Join the current line with the next line. If the next line
  706.  * is the magic header line always return TRUE; merging the last line with the
  707.  * header line can be thought of as always being a successful operation, even
  708.  * if nothing is done, and this makes the kill buffer work "right". Easy cases
  709.  * can be done by shuffling data around. Hard cases require that lines be moved
  710.  * about in memory. Return FALSE on error and TRUE if all looks ok.
  711.  */
  712. static int
  713. ldelnewline(void)
  714. {
  715.     register LINEPTR lp1;
  716.     register LINEPTR lp2;
  717.     register WINDOW *wp;
  718.     size_t    len, add;
  719.  
  720.     lp1 = DOT.l;
  721.     len = llength(lp1);
  722.     /* if the current line is empty, remove it */
  723.     if (len == 0) {            /* Blank line.        */
  724.         toss_to_undo(lp1);
  725.         lremove(curbp, lp1);
  726.         return (TRUE);
  727.     }
  728.     lp2 = lforw(lp1);
  729.     /* if the next line is empty, that's "currline\n\n", so we
  730.         remove the second \n by deleting the next line */
  731.     /* but never delete the newline on the last non-empty line */
  732.     if (lp2 == buf_head(curbp))
  733.         return (TRUE);
  734.     else if ((add = llength(lp2)) == 0) {
  735.         /* next line blank? */
  736.         toss_to_undo(lp2);
  737.         lremove(curbp, lp2);
  738.         return (TRUE);
  739.     }
  740.     copy_for_undo(DOT.l);
  741.  
  742.     /* no room in line above, make room */
  743.     if (add > lp1->l_size - len) {
  744.         char *ntext;
  745.         size_t nsize;
  746.         /* first, create the new image */
  747.         nsize = roundlenup(len + add);
  748.         if ((ntext=castalloc(char, nsize)) == NULL)
  749.             return (FALSE);
  750.         if (lp1->l_text) { /* possibly NULL if l_size == 0 */
  751.             (void)memcpy(&ntext[0], &lp1->l_text[0], len);
  752.             ltextfree(lp1,curbp);
  753.         }
  754.         lp1->l_text = ntext;
  755.         lp1->l_size = nsize;
  756.     }
  757.     (void)memcpy(lp1->l_text + len, lp2->l_text, add);
  758. #if ! WINMARK
  759.     if (MK.l == lp2) {
  760.         MK.l  = lp1;
  761.         MK.o += len;
  762.     }
  763. #endif
  764.     /* check all windows for references to the deleted line */
  765.     for_each_window(wp) {
  766.         if (wp->w_line.l == lp2)
  767.             wp->w_line.l = lp1;
  768.         if (wp->w_dot.l == lp2) {
  769.             wp->w_dot.l  = lp1;
  770.             wp->w_dot.o += len;
  771.         }
  772. #if WINMARK
  773.         if (wp->w_mark.l == lp2) {
  774.             wp->w_mark.l  = lp1;
  775.             wp->w_mark.o += len;
  776.         }
  777. #endif
  778.         if (wp->w_lastdot.l == lp2) {
  779.             wp->w_lastdot.l  = lp1;
  780.             wp->w_lastdot.o += len;
  781.         }
  782.     }
  783.     do_mark_iterate(mp,
  784.             if (mp->l == lp2) {
  785.                 mp->l  = lp1;
  786.                 mp->o += len;
  787.             }
  788.     );
  789.     llength(lp1) += add;
  790.     set_lforw(lp1, lforw(lp2));
  791.     set_lback(lforw(lp2), lp1);
  792.     dumpuline(lp1);
  793.     toss_to_undo(lp2);
  794.     return (TRUE);
  795. }
  796.  
  797.  
  798. static int kcharpending = -1;
  799.  
  800. /*
  801.  * Delete all of the text saved in the kill buffer. Called by commands when a
  802.  * new kill context is being created. The kill buffer array is released, just
  803.  * in case the buffer has grown to immense size. No errors.
  804.  */
  805. void
  806. ksetup(void)
  807. {
  808.     if ((kregflag & KAPPEND) != 0)
  809.         kregflag = KAPPEND;
  810.     else
  811.         kregflag = KNEEDCLEAN;
  812.     kchars = klines = 0;
  813.     kregwidth = 0;
  814.     kcharpending = -1;
  815.  
  816. }
  817.  
  818. /*
  819.  * clean up the old contents of a kill register.
  820.  * if called from other than kinsert, only does anything in the case where
  821.  * nothing was yanked
  822.  */
  823.  
  824. void
  825. kdone(void)
  826. {
  827.     if ((kregflag & KNEEDCLEAN) && kbs[ukb].kbufh != NULL) {
  828.         KILL *kp;    /* ptr to scan kill buffer chunk list */
  829.  
  830.         /* first, delete all the chunks */
  831.         kbs[ukb].kbufp = kbs[ukb].kbufh;
  832.         while (kbs[ukb].kbufp != NULL) {
  833.             kp = kbs[ukb].kbufp->d_next;
  834.             free((char *)(kbs[ukb].kbufp));
  835.             kbs[ukb].kbufp = kp;
  836.         }
  837.  
  838.         /* and reset all the kill buffer pointers */
  839.         kbs[ukb].kbufh = kbs[ukb].kbufp = NULL;
  840.         kbs[ukb].kused = 0;
  841.         kbs[ukb].kbwidth = kregwidth = 0;
  842.         kcharpending = -1;
  843.     }
  844.     kregflag &= ~KNEEDCLEAN;
  845.     kbs[ukb].kbflag = kregflag;
  846.     relist_registers();
  847. }
  848.  
  849. int
  850. kinsertlater(int c)
  851. {
  852.         int s = TRUE;
  853.     if (kcharpending >= 0) {
  854.         int oc = kcharpending;
  855.         kcharpending = -1;
  856.         s = kinsert(oc);
  857.     }
  858.     /* try to widen the rectangle, just in case */
  859.     if (kregwidth > kbs[ukb].kbwidth)
  860.         kbs[ukb].kbwidth = kregwidth;
  861.     kcharpending = c;
  862.     return s;
  863. }
  864.  
  865. /*
  866.  * Insert a character to the kill buffer, allocating new chunks as needed.
  867.  * Return TRUE if all is well, and FALSE on errors.
  868.  */
  869. int
  870. kinsert(
  871. int c)        /* character to insert in the kill buffer */
  872. {
  873.     KILL *nchunk;    /* ptr to newly malloced chunk */
  874.     KILLREG *kbp = &kbs[ukb];
  875.  
  876.     if (kcharpending >= 0) {
  877.         int oc = kcharpending;
  878.         kcharpending = -1;
  879.         kinsert(oc);
  880.     }
  881.  
  882.     kdone(); /* clean up the (possible) old contents */
  883.  
  884.     /* check to see if we need a new chunk */
  885.     if (kbp->kused >= KBLOCK || kbp->kbufh == NULL) {
  886.         if ((nchunk = typealloc(KILL)) == NULL)
  887.             return(FALSE);
  888.         if (kbp->kbufh == NULL)    /* set head ptr if first time */
  889.             kbp->kbufh = nchunk;
  890.         /* point the current to this new one */
  891.         if (kbp->kbufp != NULL)
  892.             kbp->kbufp->d_next = nchunk;
  893.         kbp->kbufp = nchunk;
  894.         kbp->kbufp->d_next = NULL;
  895.         kbp->kused = 0;
  896.     }
  897.  
  898.     /* and now insert the character */
  899.     kbp->kbufp->d_chunk[kbp->kused++] = (char)c;
  900.     kchars++;
  901.     if (c == '\n') {
  902.         klines++;
  903.         if (kregwidth > kbp->kbwidth)
  904.             kbp->kbwidth = kregwidth;
  905.         kregwidth = 0;
  906.     } else {
  907.         kregwidth++;
  908.     }
  909.     return(TRUE);
  910. }
  911.  
  912. /*
  913.  * Translates the index of a register in kill-buffer list to its name.
  914.  */
  915. int
  916. index2reg(int c)
  917. {
  918.     register int n;
  919.  
  920.     if (c >= 0 && c < 10)
  921.         n = (c + '0');
  922.     else if (c == KEYST_KREG)
  923.         n = '<';
  924. #if OPT_SELECTIONS
  925.     else if (c == SEL_KREG)
  926.         n = '.';
  927.     else if (c == CLIP_KREG)
  928.         n = ';';
  929. #endif
  930.     else if (c >= 10 && c < (int)TABLESIZE(kbs))
  931.         n = (c - 10 + 'a');
  932.     else
  933.         n = '?';
  934.  
  935.     return n;
  936. }
  937.  
  938. /*
  939.  * Translates the name of a register into the index in kill-buffer list.
  940.  */
  941. int
  942. reg2index(int c)
  943. {
  944.     register int n;
  945.  
  946.     if (c < 0)
  947.         n = -1;
  948.     else if (isDigit(c))
  949.         n = c - '0';
  950.     else if (isLower(c))
  951.         n = c - 'a' + 10;  /* named buffs are in 10 through 36 */
  952.     else if (isUpper(c))
  953.         n = c - 'A' + 10;
  954. #if OPT_SELECTIONS
  955.     else if (c == '.')
  956.         n = SEL_KREG;
  957.     else if (c == ';')
  958.         n = CLIP_KREG;
  959. #endif
  960.     else if (c == '<')
  961.         n = KEYST_KREG;
  962.     else if (c == '"')
  963.         n = 0;
  964.     else
  965.         n = -1;
  966.  
  967.     return n;
  968. }
  969.  
  970. /*
  971.  * Translates a kill-buffer index into the actual offset into the kill buffer,
  972.  * handling the translation of "1 .. "9
  973.  */
  974. int
  975. index2ukb(int inx)
  976. {
  977.     if (inx >= 0 && inx < 10) {
  978.         short save = ukb;
  979.         ukb = (short)inx;
  980.         kregcirculate(FALSE);
  981.         inx = ukb;
  982.         ukb = save;
  983.     }
  984.     return inx;
  985. }
  986.  
  987. /* select one of the named registers for use with the following command */
  988. /*  this could actually be handled as a command prefix, in kbd_seq(), much
  989.     the way ^X-cmd and META-cmd are done, except that we need to be
  990.     able to accept any of
  991.          3"adw    "a3dw    "ad3w
  992.     to delete 3 words into register a.  So this routine gives us an
  993.     easy way to handle the second case.  (The third case is handled in
  994.     operators(), the first in main())
  995. */
  996. int
  997. usekreg(int f, int n)
  998. {
  999.     int c, i, status;
  1000.     char tok[NSTRING];        /* command incoming */
  1001.     static    char    cbuf[2];
  1002.  
  1003.     /* take care of incrementing the buffer number, if we're replaying
  1004.         a command via 'dot' */
  1005.     incr_dot_kregnum();
  1006.  
  1007.     if ((status = mlreply_reg("Use named register: ", cbuf, &c, -1)) != TRUE)
  1008.         return status;
  1009.  
  1010.     i = reg2index(c);
  1011.     if (kbm_started(i,FALSE))
  1012.         return FALSE;
  1013.  
  1014.     /* if we're playing back dot, let its kreg override */
  1015.     if (dotcmdmode == PLAY && dotcmdkreg != 0)
  1016.         ukb = dotcmdkreg;
  1017.     else
  1018.         ukb = (short)i;
  1019.  
  1020.     if (isUpper(c))
  1021.         kregflag |= KAPPEND;
  1022.  
  1023.     if (clexec) {
  1024.         macarg(tok);    /* get the next token */
  1025.         status = execute(engl2fnc(tok), f, n);
  1026.     } else if (isnamedcmd) {
  1027.         status = namedcmd(f,n);
  1028.     } else {
  1029.         /* get the next command from the keyboard */
  1030.         c = kbd_seq();
  1031.  
  1032.         /* allow second chance for entering counts */
  1033.         do_repeats(&c,&f,&n);
  1034.  
  1035.         status = execute(kcod2fnc(c), f, n);
  1036.     }
  1037.  
  1038.     ukb = 0;
  1039.     kregflag = 0;
  1040.  
  1041.     return(status);
  1042. }
  1043.  
  1044. /* buffers 0 through 9 are circulated automatically for full-line deletes */
  1045. /* we re-use one of them until the KLINES flag is on, then we advance */
  1046. /* to the next */
  1047. void
  1048. kregcirculate(int killing)
  1049. {
  1050.     static    short    lastkb;    /* index of the real "0 */
  1051.  
  1052.     if (ukb >= 10) /* then the user specified a lettered buffer */
  1053.         return;
  1054.  
  1055.     /* we only allow killing into the real "0 */
  1056.     /* ignore any other buffer spec */
  1057.     if (killing) {
  1058.         if ((kbs[lastkb].kbflag & (KLINES|KRECT|KAPPEND)) &&
  1059.             ! (kbs[lastkb].kbflag & KYANK)) {
  1060.             if (--lastkb < 0) lastkb = 9;
  1061.             kbs[lastkb].kbflag = 0;
  1062.         }
  1063.         ukb = lastkb;
  1064.     } else {
  1065.         /* let 0 pass unmolested -- it is the default */
  1066.         if (ukb == 0) {
  1067.             ukb = lastkb;
  1068.         } else {
  1069.         /* for the others, if the current "0 has lines in it, it
  1070.             must be `"1', else "1 is `"1'.  get it? */
  1071.             if (kbs[lastkb].kbflag & (KLINES|KAPPEND))
  1072.                 ukb = (lastkb + ukb - 1) % 10;
  1073.             else
  1074.                 ukb = (lastkb + ukb) % 10;
  1075.         }
  1076.     }
  1077. }
  1078.  
  1079. int
  1080. putbefore(int f, int n)
  1081. {
  1082.     return doput(f, n, FALSE, EXACT);
  1083. }
  1084.  
  1085. int
  1086. putafter(int f, int n)
  1087. {
  1088.     return doput(f, n, TRUE, EXACT);
  1089. }
  1090.  
  1091. int
  1092. lineputbefore(int f, int n)
  1093. {
  1094.     return doput(f, n, FALSE, FULLLINE);
  1095. }
  1096.  
  1097. int
  1098. lineputafter(int f, int n)
  1099. {
  1100.     return doput(f, n, TRUE, FULLLINE);
  1101. }
  1102.  
  1103. int
  1104. rectputbefore(int f, int n)
  1105. {
  1106.     return doput(f, n, FALSE, RECTANGLE);
  1107. }
  1108.  
  1109. int
  1110. rectputafter(int f, int n)
  1111. {
  1112.     return doput(f, n, TRUE, RECTANGLE);
  1113. }
  1114.  
  1115. static int
  1116. doput(int f, int n, int after, REGIONSHAPE shape)
  1117. {
  1118.     int s, oukb;
  1119.  
  1120.     if (!f)
  1121.         n = 1;
  1122.  
  1123.     oukb = ukb;
  1124.     kregcirculate(FALSE);    /* cf: 'index2ukb()' */
  1125.     if (kbs[ukb].kbufh == NULL) {
  1126.         if (ukb != 0)
  1127.             mlwarn("[Nothing in register %c]", index2reg(oukb));
  1128.         return(FALSE);
  1129.     }
  1130.  
  1131.     if (shape == EXACT) {
  1132.         if ((kbs[ukb].kbflag & (KLINES|KAPPEND)))
  1133.             shape = FULLLINE;
  1134.         else if (kbs[ukb].kbflag & KRECT)
  1135.             shape = RECTANGLE;
  1136.         else
  1137.             shape = EXACT;
  1138.     }
  1139.  
  1140.     if (shape == FULLLINE) {
  1141.         if (after && !is_header_line(DOT, curbp))
  1142.             DOT.l = lforw(DOT.l);
  1143.         DOT.o = 0;
  1144.     } else {
  1145.         if (after && !is_at_end_of_line(DOT))
  1146.             forwchar(TRUE,1);
  1147.     }
  1148.  
  1149.     (void)setmark();
  1150.     s = PutChar(n, shape);
  1151.     if (s == TRUE)
  1152.         swapmark();
  1153.     if (is_header_line(DOT, curbp))
  1154.         DOT.l = lback(DOT.l);
  1155.     if (shape == FULLLINE)
  1156.         (void)firstnonwhite(FALSE,1);
  1157.     ukb = 0;
  1158.     return (s);
  1159. }
  1160.  
  1161. /* designed to be used with the result of "getoff()", which returns
  1162.  *    the offset of the character whose column is "close" to a goal.
  1163.  *    it may be to the left if the line is too short, or to the right
  1164.  *    if the column is spanned by a tab character.
  1165.  */
  1166. static int
  1167. force_text_at_col(C_NUM goalcol, C_NUM reached)
  1168. {
  1169.     int status = TRUE;
  1170.     if (reached < goalcol) {
  1171.         /* pad out to col */
  1172.         DOT.o = llength(DOT.l);
  1173.         status = linsert(goalcol-reached, ' ');
  1174.     } else if (reached > goalcol) {
  1175.         /* there must be a tab there. */
  1176.         /* pad to hit column we want */
  1177.         DOT.o--;
  1178.         status = linsert(goalcol%curtabval, ' ');
  1179.     }
  1180.     return status;
  1181. }
  1182.  
  1183. static int
  1184. next_line_at_col(C_NUM col, C_NUM *reachedp)
  1185. {
  1186.     int s = TRUE;
  1187.     if (is_last_line(DOT,curbp)) {
  1188.         DOT.o = llength(DOT.l);
  1189.         if (lnewline() != TRUE)
  1190.             return FALSE;
  1191.     } else {
  1192.         DOT.l = lforw(DOT.l);
  1193.     }
  1194.     DOT.o = getoff(col, reachedp);
  1195.     return s;
  1196. }
  1197.  
  1198. /*
  1199.  * Put text back from the kill register.
  1200.  */
  1201. static int
  1202. PutChar(int n, REGIONSHAPE shape)
  1203. {
  1204.     register int    c;
  1205.     register int    i;
  1206.     int status, wasnl, suppressnl;
  1207.     L_NUM before;
  1208.     C_NUM col = 0, width = 0;
  1209.     C_NUM reached = 0;
  1210.     int checkpad = FALSE;
  1211.     register char    *sp;    /* pointer into string to insert */
  1212.     KILL *kp;        /* pointer into kill register */
  1213.  
  1214.     if (n < 0)
  1215.         return FALSE;
  1216.  
  1217.     /* make sure there is something to put */
  1218.     if (kbs[ukb].kbufh == NULL)
  1219.         return TRUE;        /* not an error, just nothing */
  1220.  
  1221.     status = TRUE;
  1222.     before = line_count(curbp);
  1223.     suppressnl = FALSE;
  1224.     wasnl = FALSE;
  1225.  
  1226.  
  1227.     /* for each time.... */
  1228.     while (n--) {
  1229.         kp = kbs[ukb].kbufh;
  1230.         if (shape == RECTANGLE) {
  1231.             width = kbs[ukb].kbwidth;
  1232.             col = getcol(DOT, FALSE);
  1233.         }
  1234. #define SLOWPUT 0
  1235. #if SLOWPUT
  1236.         while (kp != NULL) {
  1237.             i = KbSize(ukb,kp);
  1238.             sp = (char *)kp->d_chunk;
  1239.             while (i--) {
  1240.                 c = *sp++;
  1241.                 if (shape == RECTANGLE) {
  1242.                     if (width == 0 || c == '\n') {
  1243.                         if (checkpad) {
  1244.                             status = force_text_at_col(
  1245.                                 col, reached);
  1246.                             if (status != TRUE)
  1247.                                 break;
  1248.                             checkpad = FALSE;
  1249.                         }
  1250.                         if (width && linsert(width, ' ')
  1251.                                 != TRUE) {
  1252.                             status = FALSE;
  1253.                             break;
  1254.                         }
  1255.                         if (next_line_at_col(col,&reached)
  1256.                                 != TRUE) {
  1257.                             status = FALSE;
  1258.                             break;
  1259.                         }
  1260.                         checkpad = TRUE;
  1261.                         width = kbs[ukb].kbwidth;
  1262.                     }
  1263.                     if (c == '\n') {
  1264.                         continue; /* did it already */
  1265.                     } else {
  1266.                         if (checkpad) {
  1267.                         status = force_text_at_col(
  1268.                                 col, reached);
  1269.                         if (status != TRUE)
  1270.                             break;
  1271.                         checkpad = FALSE;
  1272.                         }
  1273.                         width--;
  1274.  
  1275.                         if (is_header_line(DOT,curbp))
  1276.                             suppressnl = TRUE;
  1277.                         if (linsert(1, c) != TRUE) {
  1278.                             status = FALSE;
  1279.                             break;
  1280.                         }
  1281.                         wasnl = FALSE;
  1282.                     }
  1283.                 } else { /* not rectangle */
  1284.                     if (c == '\n') {
  1285.                         if (lnewline() != TRUE) {
  1286.                             status = FALSE;
  1287.                             break;
  1288.                         }
  1289.                         wasnl = TRUE;
  1290.                     } else {
  1291.                         if (is_header_line(DOT,curbp))
  1292.                             suppressnl = TRUE;
  1293.                         if (linsert(1, c) != TRUE) {
  1294.                             status = FALSE;
  1295.                             break;
  1296.                         }
  1297.                         wasnl = FALSE;
  1298.                     }
  1299.                 }
  1300.             }
  1301.             if (status != TRUE)
  1302.                 break;
  1303.             kp = kp->d_next;
  1304.         }
  1305. #else /* SLOWPUT */
  1306.         while (kp != NULL) {
  1307.             i = KbSize(ukb,kp);
  1308.             sp = (char *)kp->d_chunk;
  1309.             if (shape == RECTANGLE) {
  1310.                 while (i--) {
  1311.                     c = *sp++;
  1312.                     if (width == 0 || c == '\n') {
  1313.                         if (checkpad) {
  1314.                             status = force_text_at_col(
  1315.                                 col, reached);
  1316.                             if (status != TRUE)
  1317.                                 break;
  1318.                             checkpad = FALSE;
  1319.                         }
  1320.                         if (width && linsert(width, ' ')
  1321.                                 != TRUE) {
  1322.                             status = FALSE;
  1323.                             break;
  1324.                         }
  1325.                         if (next_line_at_col(col,&reached)
  1326.                                 != TRUE) {
  1327.                             status = FALSE;
  1328.                             break;
  1329.                         }
  1330.                         checkpad = TRUE;
  1331.                         width = kbs[ukb].kbwidth;
  1332.                     }
  1333.                     if (c == '\n') {
  1334.                         continue; /* did it already */
  1335.                     } else {
  1336.                         if (checkpad) {
  1337.                         status = force_text_at_col(
  1338.                                 col, reached);
  1339.                         if (status != TRUE)
  1340.                             break;
  1341.                         checkpad = FALSE;
  1342.                         }
  1343.                         width--;
  1344.  
  1345.                         if (is_header_line(DOT,curbp))
  1346.                             suppressnl = TRUE;
  1347.                         if (linsert(1, c) != TRUE) {
  1348.                             status = FALSE;
  1349.                             break;
  1350.                         }
  1351.                         wasnl = FALSE;
  1352.                     }
  1353.                 }
  1354.             } else { /* not rectangle */
  1355.                 while (i-- > 0) {
  1356.                 if (*sp == '\n') {
  1357.                     sp++;
  1358.                     if (lnewline() != TRUE) {
  1359.                     status = FALSE;
  1360.                     break;
  1361.                     }
  1362.                     wasnl = TRUE;
  1363.                 } else {
  1364.                     register char *dp;
  1365.                     register char *ep = sp+1;
  1366.                     if (is_header_line(DOT,curbp))
  1367.                     suppressnl = TRUE;
  1368.                     /* Find end of line or end of kill buffer */
  1369.                     while (i > 0 && *ep != '\n') {
  1370.                     i--;
  1371.                     ep++;
  1372.                     }
  1373.                     /* Open up space in current line */
  1374.                     status = linsert((int)(ep - sp), ' ');
  1375.                     if (status != TRUE)
  1376.                     break;
  1377.                     dp = DOT.l->l_text
  1378.                         + DOT.o - (int)(ep - sp);
  1379.                     /* Copy killbuf portion to the line */
  1380.                     while (sp < ep) {
  1381.                     *dp++ = *sp++;
  1382.                     }
  1383.                     wasnl = FALSE;
  1384.                 }
  1385.                 }
  1386.             }
  1387.             if (status != TRUE)
  1388.                 break;
  1389.             kp = kp->d_next;
  1390.         }
  1391. #endif /* SLOWPUT */
  1392.         if (status != TRUE)
  1393.             break;
  1394.         if (wasnl) {
  1395.             if (suppressnl) {
  1396.                 if (ldelnewline() != TRUE) {
  1397.                     status = FALSE;
  1398.                     break;
  1399.                 }
  1400.             }
  1401.         } else {
  1402.             if (shape == FULLLINE && !suppressnl) {
  1403.                 if (lnewline() != TRUE) {
  1404.                     status = FALSE;
  1405.                     break;
  1406.                 }
  1407.             }
  1408.         }
  1409.     }
  1410.     curwp->w_flag |= WFHARD;
  1411.     (void)line_report(before);
  1412.     return status;
  1413. }
  1414.  
  1415. static int    lastreg = -1;
  1416.  
  1417. /* ARGSUSED */
  1418. int
  1419. execkreg(int f, int n)
  1420. {
  1421.     int c, j, jj, status;
  1422.     KILL *kp;        /* pointer into kill register */
  1423.     static    char    cbuf[2];
  1424.     int kbcount, whichkb;
  1425.     int i;
  1426.     char *sp;
  1427.     KILL *tkp;
  1428.  
  1429.     if (!f)
  1430.         n = 1;
  1431.     else if (n <= 0)
  1432.         return TRUE;
  1433.  
  1434.     if ((status = mlreply_reg("Execute register: ", cbuf, &c, lastreg)) != TRUE)
  1435.         return status;
  1436.  
  1437.     j = reg2index(c);
  1438.     if (kbm_started(j,TRUE))
  1439.         return FALSE;
  1440.  
  1441.     lastreg = c;
  1442.     relist_registers();
  1443.  
  1444.     /* make sure there is something to execute */
  1445.     jj = index2ukb(j);
  1446.     kp = kbs[jj].kbufh;
  1447.     if (kp == NULL)
  1448.         return TRUE;        /* not an error, just nothing */
  1449.  
  1450.     /* count the kchunks */
  1451.     kbcount = 0;
  1452.     tkp = kp;
  1453.     while (tkp != NULL) {
  1454.         kbcount++;
  1455.         tkp = tkp->d_next;
  1456.     }
  1457.     /* process them in reverse order */
  1458.     while (kbcount) {
  1459.         whichkb = kbcount;
  1460.         tkp = kp;
  1461.         while (--whichkb)
  1462.             tkp = tkp->d_next;
  1463.         i = KbSize(jj,tkp);
  1464.         sp = (char *)tkp->d_chunk+i-1;
  1465.         while (i--) {
  1466.             mapungetc((*sp--)|YESREMAP);
  1467.         }
  1468.         kbcount--;
  1469.     }
  1470.     return TRUE;
  1471. }
  1472.  
  1473. /* ARGSUSED */
  1474. int
  1475. loadkreg(int f, int n GCC_UNUSED)
  1476. {
  1477.     int s;
  1478.     char respbuf[NFILEN];
  1479.  
  1480.     ksetup();
  1481.     *respbuf = EOS;
  1482.     s = mlreply_no_opts("Load register with: ",
  1483.                     respbuf, sizeof(respbuf));
  1484.     if (s != TRUE)
  1485.         return FALSE;
  1486.     if (f)
  1487.         kregflag |= KLINES;
  1488.     for (s = 0; s < NFILEN; s++) {
  1489.         if (!respbuf[s])
  1490.             break;
  1491.         if (!kinsert(respbuf[s]))
  1492.             break;
  1493.     }
  1494.     kdone();
  1495.     return TRUE;
  1496. }
  1497.  
  1498. /* Show the contents of the kill-buffers */
  1499. #if OPT_SHOW_REGS
  1500. #define    REGS_PREFIX    12    /* non-editable portion of the display */
  1501.  
  1502. #if OPT_UPBUFF
  1503. static    int    show_all_chars;
  1504. #endif
  1505.  
  1506. /*ARGSUSED*/
  1507. static void
  1508. makereglist(
  1509. int iflag,    /* list nonprinting chars flag */
  1510. void *dummy GCC_UNUSED)
  1511. {
  1512.     register KILL    *kp;
  1513.     register int    i, ii, j, c;
  1514.     register UCHAR    *p;
  1515.     int    any;
  1516.  
  1517. #if OPT_UPBUFF
  1518.     show_all_chars = iflag;
  1519. #endif
  1520.     b_set_left_margin(curbp, REGS_PREFIX);
  1521.     any = (reg2index(lastreg) >= 0);
  1522.     if (any)
  1523.         bprintf("last=%c", lastreg);
  1524.  
  1525.     for (i = 0; (size_t) i < TABLESIZE(kbs); i++) {
  1526.         short    save = ukb;
  1527.  
  1528.         ii = index2ukb(i);
  1529.         if ((kp = kbs[ii].kbufh) != 0) {
  1530.             int first = FALSE;
  1531.             if (any++) {
  1532.                 bputc('\n');
  1533.                 lsettrimmed(lback(DOT.l));
  1534.             }
  1535.             if (i > 0) {
  1536.                 bprintf("%c:%*p",
  1537.                     index2reg(i),
  1538.                     REGS_PREFIX-2, ' ');
  1539.             } else {
  1540.                 bprintf("%*S",
  1541.                     REGS_PREFIX, "(unnamed)");
  1542.             }
  1543.             do {
  1544.                 j = KbSize(ii,kp);
  1545.                 p = kp->d_chunk;
  1546.  
  1547.                 while (j-- > 0) {
  1548.                     if (first) {
  1549.                         first = FALSE;
  1550.                         bprintf("%*p", REGS_PREFIX, ' ');
  1551.                     }
  1552.                     c = *p++;
  1553.                     if (isPrint(c) || !iflag) {
  1554.                         bputc(c);
  1555.                     } else if (c != '\n') {
  1556.                         bputc('^');
  1557.                         bputc(toalpha(c));
  1558.                     }
  1559.                     if (c == '\n') {
  1560.                         first = TRUE;
  1561.                         any = 0;
  1562.                     } else
  1563.                         any = 1;
  1564.                 }
  1565.             } while ((kp = kp->d_next) != 0);
  1566.         }
  1567.         if (i < 10)
  1568.             ukb = save;
  1569.     }
  1570.     lsettrimmed(DOT.l);
  1571. }
  1572.  
  1573. static int will_relist_regs;
  1574.  
  1575. /*ARGSUSED*/
  1576. int
  1577. showkreg(int f, int n GCC_UNUSED)
  1578. {
  1579.     will_relist_regs = FALSE;
  1580.     return liststuff(REGISTERS_BufName, FALSE,
  1581.                 makereglist, f, (void *)0);
  1582. }
  1583.  
  1584. #if OPT_UPBUFF
  1585.  
  1586.  
  1587. static int
  1588. show_Registers(BUFFER *bp)
  1589. {
  1590.     b_clr_obsolete(bp);
  1591.     return showkreg(show_all_chars, 1);
  1592. }
  1593.  
  1594. static void
  1595. relist_registers(void)
  1596. {
  1597.  
  1598.     if (will_relist_regs)     /* have we already done this? */
  1599.         return;
  1600.  
  1601.     will_relist_regs = TRUE;
  1602.  
  1603.     update_scratch(REGISTERS_BufName, show_Registers);
  1604. }
  1605. #endif    /* OPT_UPBUFF */
  1606.  
  1607. #endif    /* OPT_SHOW_REGS */
  1608.  
  1609. /* For memory-leak testing (only!), releases all kill-buffer storage. */
  1610. #if NO_LEAKS
  1611. void    kbs_leaks(void)
  1612. {
  1613.     for (ukb = 0; ukb < TABLESIZE(kbs); ukb++) {
  1614.         ksetup();
  1615.         kdone();
  1616.     }
  1617. }
  1618. #endif
  1619.