home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / VILE327.ZIP / VILE327.TAR / vile3.27 / line.c < prev    next >
C/C++ Source or Header  |  1992-12-14  |  29KB  |  1,248 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.  * $Log: line.c,v $
  14.  * Revision 1.27  1992/11/19  09:10:16  foxharp
  15.  * renamed kdelete() to ksetup(), and created kdone() routine, which cleans
  16.  * up a buffer that was setup if nothing was ever put in it
  17.  *
  18.  * Revision 1.26  1992/08/20  23:40:48  foxharp
  19.  * typo fixes -- thanks, eric
  20.  *
  21.  * Revision 1.25  1992/07/10  22:01:14  foxharp
  22.  * make poison more poisonous
  23.  *
  24.  * Revision 1.24  1992/07/04  14:36:17  foxharp
  25.  * added temporary line-poisoner, to catch core dump on buffer/line reuse.
  26.  *
  27.  * Revision 1.23  1992/05/16  12:00:31  pgf
  28.  * prototypes/ansi/void-int stuff/microsoftC
  29.  *
  30.  * Revision 1.22  1992/03/24  07:37:35  pgf
  31.  * usekreg now works better as a namedcmd
  32.  *
  33.  * Revision 1.21  1992/03/07  10:28:52  pgf
  34.  * don't bother copying and marking lines that aren't really being split,
  35.  * in lnewline -- fixes problem "p"utting and undoing a blank line at the
  36.  * end of file
  37.  *
  38.  * Revision 1.20  1992/03/03  09:35:52  pgf
  39.  * added support for getting "words" out of the buffer via variables --
  40.  * needed _nonspace character type
  41.  *
  42.  * Revision 1.19  1992/02/17  09:03:22  pgf
  43.  * kill registers now hold unsigned chars
  44.  *
  45.  * Revision 1.18  1992/01/22  18:38:34  pgf
  46.  * check for empty buffer in lnewline() was insufficient.  amazing what
  47.  * you find when you go looking...
  48.  *
  49.  * Revision 1.17  1992/01/05  00:06:13  pgf
  50.  * split mlwrite into mlwrite/mlprompt/mlforce to make errors visible more
  51.  * often.  also normalized message appearance somewhat.
  52.  *
  53.  * Revision 1.16  1992/01/04  14:14:12  pgf
  54.  * attempt to keep all newly inserted lines in a fresh buffer visible, rather
  55.  * than have them "hidden" above the window, though the window might be empty
  56.  *
  57.  * Revision 1.15  1991/11/08  13:24:33  pgf
  58.  * added klines and kchars counters to kinsert()
  59.  *
  60.  * Revision 1.14  1991/11/01  14:38:00  pgf
  61.  * saber cleanup
  62.  *
  63.  * Revision 1.13  1991/10/29  03:02:04  pgf
  64.  * fixups to usekreg, and added execkreg and loadkreg
  65.  *
  66.  * Revision 1.12  1991/10/24  12:59:18  pgf
  67.  * new lgrow() routine, to add more space in a line
  68.  *
  69.  * Revision 1.11  1991/10/10  12:33:33  pgf
  70.  * changes to support "block malloc" of line text -- now for most files
  71.  * there is are two mallocs and a single read, no copies.  previously there
  72.  * were two mallocs per line, and two copies (stdio's and ours).  This change
  73.  * implies that lines and line text should not move between buffers, without
  74.  * checking that the text and line struct do not "belong" to the buffer.
  75.  *
  76.  * Revision 1.10  1991/09/24  01:04:10  pgf
  77.  * do lnewline() correctly in empty buffer
  78.  *
  79.  * Revision 1.9  1991/08/07  12:35:07  pgf
  80.  * added RCS log messages
  81.  *
  82.  * revision 1.8
  83.  * date: 1991/08/06 15:22:27;
  84.  * allow null l_text pointers for empty lines
  85.  * 
  86.  * revision 1.7
  87.  * date: 1991/07/19 17:14:49;
  88.  * fixed missing "copy_for_undo" bug introduced a while ago
  89.  * 
  90.  * revision 1.6
  91.  * date: 1991/06/25 19:52:54;
  92.  * massive data structure restructure
  93.  * 
  94.  * revision 1.5
  95.  * date: 1991/06/16 17:35:32;
  96.  * fixed bug -- wasn't assigning new size to line struct when re-allocing
  97.  * for a line merge
  98.  * 
  99.  * revision 1.4
  100.  * date: 1991/06/03 10:24:26;
  101.  * took out old #ifdef INLINE stuff, and
  102.  * added comments for usekreg
  103.  * 
  104.  * revision 1.3
  105.  * date: 1991/05/31 11:11:16;
  106.  * change args to execute()
  107.  * 
  108.  * revision 1.2
  109.  * date: 1991/04/04 09:28:37;
  110.  * line text is now separate from LINE struct
  111.  * 
  112.  * revision 1.1
  113.  * date: 1990/09/21 10:25:32;
  114.  * initial vile RCS revision
  115.  */
  116.  
  117. #define POISON
  118. #ifdef POISON
  119. #define poison(p,s) memset(p, 0xdf, s)
  120. #else
  121. #define poison(p,s)
  122. #endif
  123.  
  124. #include    <stdio.h>
  125. #include    "estruct.h"
  126. #include    "edef.h"
  127.  
  128. #define roundup(n) ((n+NBLOCK-1) & ~(NBLOCK-1))
  129. /*
  130.  * This routine allocates a block of memory large enough to hold a LINE
  131.  * containing "used" characters. The block is always rounded up a bit. Return
  132.  * a pointer to the new block, or NULL if there isn't any memory left. Print a
  133.  * message in the message line if no space.
  134.  */
  135. LINE *
  136. lalloc(used,bp)
  137. register int    used;
  138. BUFFER *bp;
  139. {
  140.     register LINE    *lp;
  141.     register int    size;
  142.  
  143.     /* lalloc(-1) is used by undo for placeholders */
  144.     if (used < 0)  {
  145.         size = 0;
  146.     } else {
  147.         size = roundup(used);
  148.     }
  149.     /* see if the buffer LINE block has any */
  150.     if ((lp = bp->b_freeLINEs) != NULL) {
  151.         bp->b_freeLINEs = lp->l_nxtundo;
  152.     } else if ((lp = (LINE *) malloc(sizeof(LINE))) == NULL) {
  153.         mlforce("[OUT OF MEMORY]");
  154.         return NULL;
  155.     }
  156.     lp->l_text = NULL;
  157.     if (size && (lp->l_text = malloc(size)) == NULL) {
  158.         mlforce("[OUT OF MEMORY]");
  159.         poison(lp, sizeof(*lp));
  160.         free((char *)lp);
  161.         return NULL;
  162.     }
  163.     lp->l_size = size;
  164.     lp->l_used = used;
  165.     lsetclear(lp);
  166.     lp->l_nxtundo = NULL;
  167.     return (lp);
  168. }
  169.  
  170. int
  171. lgrow(lp,howmuch,bp)
  172. register LINE    *lp;
  173. register int    howmuch;
  174. BUFFER *bp;
  175. {
  176.     register int    size;
  177.     char *ntext;
  178.  
  179.     size = roundup(lp->l_size + howmuch);
  180.     ntext = (char *)malloc(size);
  181.     if (ntext == NULL) {
  182.         mlforce("[OUT OF MEMORY]");
  183.         return FALSE;
  184.     }
  185.     memcpy(ntext, lp->l_text, lp->l_used);
  186.     ltextfree(lp,bp);
  187.     lp->l_text = ntext;
  188.     lp->l_size = size;
  189.     return TRUE;
  190. }
  191.  
  192.  
  193.  
  194. void
  195. lfree(lp,bp)
  196. register LINE *lp;
  197. register BUFFER *bp;
  198. {
  199.     ltextfree(lp,bp);
  200.  
  201.     /* if the buffer doesn't have its own block of LINEs, or this
  202.         one isn't in that range, free it */
  203.     if (!bp->b_LINEs || lp < bp->b_LINEs || lp >= bp->b_LINEs_end) {
  204.         poison(lp, sizeof(*lp));
  205.         free((char *)lp);
  206.     } else {
  207.         /* keep track of freed buffer LINEs here */
  208.         lp->l_nxtundo = bp->b_freeLINEs;
  209.         bp->b_freeLINEs = lp;
  210. #ifdef POISON
  211.         /* catch references hard */
  212.         lp->l_bp = lp->l_bp = (LINE *)1;
  213.         lp->l_text = (char *)1;
  214.         lp->l_size = lp->l_used = -1;
  215. #endif
  216.     }
  217. }
  218.  
  219. void
  220. ltextfree(lp,bp)
  221. register LINE *lp;
  222. register BUFFER *bp;
  223. {
  224.     register unsigned char *ltextp;
  225.  
  226.     ltextp = (unsigned char *)lp->l_text;
  227.     if (ltextp) {
  228.         if (bp->b_ltext) { /* could it be in the big range? */
  229.             if (ltextp < bp->b_ltext || ltextp >= bp->b_ltext_end) {
  230.                 poison(ltextp, lp->l_size);
  231.                 free((char *)ltextp);
  232.             } /* else {
  233.             could keep track of freed big range text here;
  234.             } */
  235.         } else {
  236.             poison(ltextp, lp->l_size);
  237.             free((char *)ltextp);
  238.         }
  239.         lp->l_text = NULL;
  240.     } /* else nothing to free */
  241. }
  242.  
  243. /*
  244.  * Delete line "lp". Fix all of the links that might point at it (they are
  245.  * moved to offset 0 of the next line. Unlink the line from whatever buffer it
  246.  * might be in. The buffers are updated too; the magic
  247.  * conditions described in the above comments don't hold here.
  248.  * Memory is not released, so line can be saved in undo stacks.
  249.  */
  250. void
  251. lremove(bp,lp)
  252. register BUFFER *bp;
  253. register LINE    *lp;
  254. {
  255.     register WINDOW *wp;
  256.  
  257. #if !WINMARK
  258.     if (MK.l == lp) {
  259.         MK.l = lp->l_fp;
  260.         MK.o = 0;
  261.     }
  262. #endif
  263.     wp = wheadp;
  264.     while (wp != NULL) {
  265.         if (wp->w_line.l == lp)
  266.             wp->w_line.l = lp->l_fp;
  267.         if (wp->w_dot.l    == lp) {
  268.             wp->w_dot.l  = lp->l_fp;
  269.             wp->w_dot.o  = 0;
  270.         }
  271. #if WINMARK
  272.         if (wp->w_mark.l == lp) {
  273.             wp->w_mark.l = lp->l_fp;
  274.             wp->w_mark.o = 0;
  275.         }
  276. #endif
  277. #if 0
  278.         if (wp->w_lastdot.l == lp) {
  279.             wp->w_lastdot.l = lp->l_fp;
  280.             wp->w_lastdot.o = 0;
  281.         }
  282. #endif
  283.         wp = wp->w_wndp;
  284.     }
  285.     if (bp->b_nwnd == 0) {
  286.         if (bp->b_dot.l    == lp) {
  287.             bp->b_dot.l = lp->l_fp;
  288.             bp->b_dot.o = 0;
  289.         }
  290. #if WINMARK
  291.         if (bp->b_mark.l == lp) {
  292.             bp->b_mark.l = lp->l_fp;
  293.             bp->b_mark.o = 0;
  294.         }
  295. #endif
  296. #if 0
  297.         if (bp->b_lastdot.l == lp) {
  298.             bp->b_lastdot.l = lp->l_fp;
  299.             bp->b_lastdot.o = 0;
  300.         }
  301. #endif
  302.     }
  303. #if 0
  304.     if (bp->b_nmmarks != NULL) { /* fix the named marks */
  305.         int i;
  306.         struct MARK *mp;
  307.         for (i = 0; i < 26; i++) {
  308.             mp = &(bp->b_nmmarks[i]);
  309.             if (mp->p == lp) {
  310.                 mp->p = lp->l_fp;
  311.                 mp->o = 0;
  312.             }
  313.         }
  314.     }
  315. #endif
  316.     lp->l_bp->l_fp = lp->l_fp;
  317.     lp->l_fp->l_bp = lp->l_bp;
  318. }
  319.  
  320. /*
  321.  * This routine gets called when a character is changed in place in the current
  322.  * buffer. It updates all of the required flags in the buffer and window
  323.  * system. The flag used is passed as an argument; if the buffer is being
  324.  * displayed in more than 1 window we change EDIT to HARD. Set MODE if the
  325.  * mode line needs to be updated (the "*" has to be set).
  326.  */
  327. void
  328. lchange(flag)
  329. register int    flag;
  330. {
  331.     register WINDOW *wp;
  332.  
  333.     if (curbp->b_nwnd != 1) {         /* Ensure hard.     */
  334.         flag |= WFHARD;
  335.     }
  336.     if ((curbp->b_flag&BFCHG) == 0) {    /* First change, so    */
  337.         flag |= WFMODE;         /* update mode lines.    */
  338.         curbp->b_flag |= BFCHG;
  339.     }
  340.     wp = wheadp;
  341.     while (wp != NULL) {
  342.         if (wp->w_bufp == curbp)
  343.             wp->w_flag |= flag;
  344.         wp = wp->w_wndp;
  345.     }
  346. }
  347.  
  348. int
  349. insspace(f, n)    /* insert spaces forward into text */
  350. int f, n;    /* default flag and numeric argument */
  351. {
  352.     linsert(n, ' ');
  353.     return backchar(f, n);
  354. }
  355.  
  356. /*
  357.  * Insert "n" copies of the character "c" at the current location of dot. In
  358.  * the easy case all that happens is the text is stored in the line. In the
  359.  * hard case, the line has to be reallocated. When the window list is updated,
  360.  * take special care; I screwed it up once. You always update dot in the
  361.  * current window. You update mark, and a dot in another window, if it is
  362.  * greater than the place where you did the insert. Return TRUE if all is
  363.  * well, and FALSE on errors.
  364.  */
  365. int
  366. linsert(n, c)
  367. int n, c;
  368. {
  369.     register char    *cp1;
  370.     register char    *cp2;
  371.     register LINE    *lp1;
  372.     register LINE    *lp2;
  373.     register LINE    *lp3;
  374.     register int    doto;
  375.     register int    i;
  376.     register WINDOW *wp;
  377.     register char    *ntext;
  378.     int nsize;
  379.  
  380.     lchange(WFEDIT);
  381.     lp1 = curwp->w_dot.l;            /* Current line     */
  382.     if (lp1 == curbp->b_line.l) {        /* At the end: special    */
  383.         if (curwp->w_dot.o != 0) {
  384.             mlforce("BUG: linsert");
  385.             return (FALSE);
  386.         }
  387.         if ((lp2=lalloc(n,curbp)) == NULL) /* Allocate new line    */
  388.             return (FALSE);
  389.         copy_for_undo(lp1->l_bp); /* don't want preundodot to point
  390.                        *    at a new line if this is the
  391.                        *    first change */
  392.         lp3 = lp1->l_bp;        /* Previous line    */
  393.         lp3->l_fp = lp2;        /* Link in        */
  394.         lp2->l_fp = lp1;
  395.         lp1->l_bp = lp2;
  396.         lp2->l_bp = lp3;
  397.         for (i=0; i<n; ++i)
  398.             lp2->l_text[i] = c;
  399.         curwp->w_dot.l = lp2;
  400.         curwp->w_dot.o = n;
  401.         tag_for_undo(lp2);
  402.         return (TRUE);
  403.     }
  404.     doto = curwp->w_dot.o;            /* Save for later.    */
  405.     if (lp1->l_used+n > lp1->l_size) {    /* Hard: reallocate    */
  406.         copy_for_undo(lp1);
  407.         /* first, create the new image */
  408.         if ((ntext=malloc(nsize = roundup(lp1->l_used+n))) == NULL)
  409.             return (FALSE);
  410.         if (lp1->l_text) /* possibly NULL if l_size == 0 */
  411.             memcpy(&ntext[0],      &lp1->l_text[0],    doto);
  412.         memset(&ntext[doto],   c, n);
  413.         if (lp1->l_text) {
  414.             memcpy(&ntext[doto+n], &lp1->l_text[doto],
  415.                             lp1->l_used-doto );
  416.             ltextfree(lp1,curbp);
  417.         }
  418.         lp1->l_text = ntext;
  419.         lp1->l_size = nsize;
  420.         lp1->l_used += n;
  421.     } else {                /* Easy: in place    */
  422.         copy_for_undo(lp1);
  423.         /* don't used memcpy:  overlapping regions.... */
  424.         lp1->l_used += n;
  425.         cp2 = &lp1->l_text[lp1->l_used];
  426.         cp1 = cp2-n;
  427.         while (cp1 != &lp1->l_text[doto])
  428.             *--cp2 = *--cp1;
  429.         for (i=0; i<n; ++i)        /* Add the characters    */
  430.             lp1->l_text[doto+i] = c;
  431.     }
  432. #if ! WINMARK
  433.     if (MK.l == lp1) {
  434.         if (MK.o > doto)
  435.             MK.o += n;
  436.     }
  437. #endif
  438.     wp = wheadp;                /* Update windows    */
  439.     while (wp != NULL) {
  440.         if (wp->w_dot.l == lp1) {
  441.             if (wp==curwp || wp->w_dot.o>doto)
  442.                 wp->w_dot.o += n;
  443.         }
  444. #if WINMARK
  445.         if (wp->w_mark.l == lp1) {
  446.             if (wp->w_mark.o > doto)
  447.                 wp->w_mark.o += n;
  448.         }
  449. #endif
  450.         if (wp->w_lastdot.l == lp1) {
  451.             if (wp->w_lastdot.o > doto)
  452.                 wp->w_lastdot.o += n;
  453.         }
  454.         wp = wp->w_wndp;
  455.     }
  456.     if (curbp->b_nmmarks != NULL) { /* fix the named marks */
  457.         struct MARK *mp;
  458.         for (i = 0; i < 26; i++) {
  459.             mp = &(curbp->b_nmmarks[i]);
  460.             if (mp->l == lp1) {
  461.                 if (mp->o > doto)
  462.                     mp->o += n;
  463.             }
  464.         }
  465.     }
  466.     return (TRUE);
  467. }
  468.  
  469. /*
  470.  * Insert a newline into the buffer at the current location of dot in the
  471.  * current window. The funny ass-backwards way it does things is not a botch;
  472.  * it just makes the last line in the file not a special case. Return TRUE if
  473.  * everything works out and FALSE on error (memory allocation failure). The
  474.  * update of dot and mark is a bit easier then in the above case, because the
  475.  * split forces more updating.
  476.  */
  477. int
  478. lnewline()
  479. {
  480.     register char    *cp1;
  481.     register char    *cp2;
  482.     register LINE    *lp1;
  483.     register LINE    *lp2;
  484.     register int    doto;
  485.     register WINDOW *wp;
  486.  
  487.     lchange(WFHARD|WFINS);
  488.     lp1  = curwp->w_dot.l;            /* Get the address and    */
  489.     doto = curwp->w_dot.o;            /* offset of "."    */
  490.     if (lp1 == curbp->b_line.l && lforw(lp1) == lp1) {
  491.         /* empty buffer -- just  create empty line */
  492.         if ((lp2=lalloc(doto,curbp)) == NULL)
  493.             return (FALSE);
  494.         /* put lp2 in below lp1 */
  495.         lp2->l_fp = lp1->l_fp;
  496.         lp1->l_fp = lp2;
  497.         lp2->l_fp->l_bp = lp2;
  498.         lp2->l_bp = lp1;
  499.         tag_for_undo(lp2);
  500.         wp = wheadp;
  501.         while (wp != NULL) {
  502.             if (wp->w_line.l == lp1)
  503.                 wp->w_line.l = lp2;
  504.             if (wp->w_dot.l == lp1)
  505.                 wp->w_dot.l = lp2;
  506.             wp = wp->w_wndp;
  507.         }
  508.         return TRUE;
  509.     }
  510.     if ((lp2=lalloc(doto,curbp)) == NULL)     /* New first half line    */
  511.         return (FALSE);
  512.     if (doto > 0) {
  513.         copy_for_undo(lp1);
  514.         cp1 = &lp1->l_text[0];        /* Shuffle text around    */
  515.         cp2 = &lp2->l_text[0];
  516.         while (cp1 != &lp1->l_text[doto])
  517.             *cp2++ = *cp1++;
  518.         cp2 = &lp1->l_text[0];
  519.         while (cp1 != &lp1->l_text[lp1->l_used])
  520.             *cp2++ = *cp1++;
  521.         lp1->l_used -= doto;
  522.     }
  523.     /* put lp2 in above lp1 */
  524.     lp2->l_bp = lp1->l_bp;
  525.     lp1->l_bp = lp2;
  526.     lp2->l_bp->l_fp = lp2;
  527.     lp2->l_fp = lp1;
  528.     tag_for_undo(lp2);
  529.     dumpuline(lp1);
  530. #if ! WINMARK
  531.     if (MK.l == lp1) {
  532.         if (MK.o < doto)
  533.             MK.l = lp2;
  534.         else
  535.             MK.o -= doto;
  536.     }
  537. #endif
  538.     wp = wheadp;                /* Windows        */
  539.     while (wp != NULL) {
  540.         if (wp->w_line.l == lp1)
  541.             wp->w_line.l = lp2;
  542.         if (wp->w_dot.l == lp1) {
  543.             if (wp->w_dot.o < doto)
  544.                 wp->w_dot.l = lp2;
  545.             else
  546.                 wp->w_dot.o -= doto;
  547.         }
  548. #if WINMARK
  549.         if (wp->w_mark.l == lp1) {
  550.             if (wp->w_mark.o < doto)
  551.                 wp->w_mark.l = lp2;
  552.             else
  553.                 wp->w_mark.o -= doto;
  554.         }
  555. #endif
  556.         if (wp->w_lastdot.l == lp1) {
  557.             if (wp->w_lastdot.o < doto)
  558.                 wp->w_lastdot.l = lp2;
  559.             else
  560.                 wp->w_lastdot.o -= doto;
  561.         }
  562.         wp = wp->w_wndp;
  563.     }
  564.     if (curbp->b_nmmarks != NULL) { /* fix the named marks */
  565.         int i;
  566.         struct MARK *mp;
  567.         for (i = 0; i < 26; i++) {
  568.             mp = &(curbp->b_nmmarks[i]);
  569.             if (mp->l == lp1) {
  570.                 if (mp->o < doto)
  571.                     mp->l = lp2;
  572.                 else
  573.                     mp->o -= doto;
  574.             }
  575.         }
  576.     }
  577.     return (TRUE);
  578. }
  579.  
  580. /*
  581.  * This function deletes "n" bytes, starting at dot. It understands how do deal
  582.  * with end of lines, etc. It returns TRUE if all of the characters were
  583.  * deleted, and FALSE if they were not (because dot ran into the end of the
  584.  * buffer. The "kflag" is TRUE if the text should be put in the kill buffer.
  585.  */
  586. int
  587. ldelete(n, kflag)
  588. long n;     /* # of chars to delete */
  589. int kflag;    /* put killed text in kill buffer flag */
  590. {
  591.     register char    *cp1;
  592.     register char    *cp2;
  593.     register LINE    *dotp;
  594.     register LINE    *nlp;
  595.     register int    doto;
  596.     register int    chunk;
  597.     register WINDOW *wp;
  598.     register int i,s;
  599.  
  600.     while (n != 0) {
  601.         dotp = DOT.l;
  602.         doto = DOT.o;
  603.         if (dotp == curbp->b_line.l)    /* Hit end of buffer.    */
  604.             return (FALSE);
  605.         chunk = dotp->l_used-doto;    /* Size of chunk.    */
  606.         if (chunk > (int)n)
  607.             chunk = (int)n;
  608.         if (chunk == 0) {        /* End of line, merge.    */
  609.             lchange(WFHARD|WFKILLS);
  610.             /* first take out any whole lines below this one */
  611.             nlp = lforw(dotp);
  612.             while (nlp != curbp->b_line.l && llength(nlp)+1 < n) {
  613.                 if (kflag) {
  614.                     s = kinsert('\n');
  615.                     for (i = 0; i < llength(nlp) && 
  616.                                 s == TRUE; i++)
  617.                         s = kinsert(lgetc(nlp,i));
  618.                     if (s != TRUE)
  619.                         return(FALSE);
  620.                 }
  621.                 lremove(curbp,nlp);
  622.                 toss_to_undo(nlp);
  623.                 n -= llength(nlp)+1;
  624.                 nlp = lforw(dotp);
  625.             }
  626.             if ((s = ldelnewline()) != TRUE)
  627.                 return (s);
  628.             if (kflag && (s = kinsert('\n')) != TRUE)
  629.                 return (s);
  630.             --n;
  631.             continue;
  632.         }
  633.         lchange(WFEDIT);
  634.         copy_for_undo(dotp);
  635.         cp1 = &dotp->l_text[doto];    /* Scrunch text.    */
  636.         cp2 = cp1 + chunk;
  637.         if (kflag) {        /* Kill?        */
  638.             while (cp1 != cp2) {
  639.                 if ((s = kinsert(*cp1)) != TRUE)
  640.                     return (s);
  641.                 ++cp1;
  642.             }
  643.             cp1 = &dotp->l_text[doto];
  644.         }
  645.         while (cp2 != &dotp->l_text[dotp->l_used])
  646.             *cp1++ = *cp2++;
  647.         dotp->l_used -= chunk;
  648. #if ! WINMARK
  649.         if (MK.l && MK.o > doto) {
  650.             MK.o -= chunk;
  651.             if (MK.o < doto)
  652.                 MK.o = doto;
  653.         }
  654. #endif
  655.         wp = wheadp;            /* Fix windows        */
  656.         while (wp != NULL) {
  657.             if (wp->w_dot.l==dotp && wp->w_dot.o > doto) {
  658.                 wp->w_dot.o -= chunk;
  659.                 if (wp->w_dot.o < doto)
  660.                     wp->w_dot.o = doto;
  661.             }
  662. #if WINMARK
  663.             if (wp->w_mark.l==dotp && wp->w_mark.o > doto) {
  664.                 wp->w_mark.o -= chunk;
  665.                 if (wp->w_mark.o < doto)
  666.                     wp->w_mark.o = doto;
  667.             }
  668. #endif
  669.             if (wp->w_lastdot.l==dotp && wp->w_lastdot.o > doto) {
  670.                 wp->w_lastdot.o -= chunk;
  671.                 if (wp->w_lastdot.o < doto)
  672.                     wp->w_lastdot.o = doto;
  673.             }
  674.             wp = wp->w_wndp;
  675.         }
  676.         if (curbp->b_nmmarks != NULL) { /* fix the named marks */
  677.             struct MARK *mp;
  678.             for (i = 0; i < 26; i++) {
  679.                 mp = &(curbp->b_nmmarks[i]);
  680.                 if (mp->l==dotp && mp->o > doto) {
  681.                     mp->o -= chunk;
  682.                     if (mp->o < doto)
  683.                         mp->o = doto;
  684.                 }
  685.             }
  686.         }
  687.         n -= chunk;
  688.     }
  689.     return (TRUE);
  690. }
  691.  
  692. /* getctext:    grab and return a string with text from
  693.         the current line, consisting of chars of type "type"
  694. */
  695.  
  696. char *getctext(type)
  697. int type;
  698. {
  699.     static char rline[NSTRING];    /* line to return */
  700.  
  701.     screen_string(rline, NSTRING, type);
  702.     return rline;
  703. }
  704.  
  705. #if ! SMALLER
  706. /* putctext:    replace the current line with the passed in text    */
  707.  
  708. int
  709. putctext(iline)
  710. char *iline;    /* contents of new line */
  711. {
  712.     register int status;
  713.  
  714.     /* delete the current line */
  715.     curwp->w_dot.o = 0;    /* starting at the beginning of the line */
  716.     if ((status = deltoeol(TRUE, 1)) != TRUE)
  717.         return(status);
  718.  
  719.     /* insert the new line */
  720.     while (*iline) {
  721.         if (*iline == '\n') {
  722.             if (lnewline() != TRUE)
  723.                 return(FALSE);
  724.         } else {
  725.             if (linsert(1, *iline) != TRUE)
  726.                 return(FALSE);
  727.         }
  728.         ++iline;
  729.     }
  730.     status = lnewline();
  731.     backline(TRUE, 1);
  732.     return(status);
  733. }
  734. #endif
  735.  
  736. /*
  737.  * Delete a newline. Join the current line with the next line. If the next line
  738.  * is the magic header line always return TRUE; merging the last line with the
  739.  * header line can be thought of as always being a successful operation, even
  740.  * if nothing is done, and this makes the kill buffer work "right". Easy cases
  741.  * can be done by shuffling data around. Hard cases require that lines be moved
  742.  * about in memory. Return FALSE on error and TRUE if all looks ok. Called by
  743.  * "ldelete" only.
  744.  */
  745. int
  746. ldelnewline()
  747. {
  748.     register char    *cp1;
  749.     register char    *cp2;
  750.     register LINE    *lp1;
  751.     register LINE    *lp2;
  752.     register WINDOW *wp;
  753.  
  754.     lp1 = curwp->w_dot.l;
  755.     /* if the current line is empty, remove it */
  756.     if (lp1->l_used == 0) {        /* Blank line.        */
  757.         lremove(curbp,lp1);
  758.         toss_to_undo(lp1);
  759.         return (TRUE);
  760.     }
  761.     lp2 = lp1->l_fp;
  762.     /* if the next line is empty, that's "currline\n\n", so we
  763.         remove the second \n by deleting the next line */
  764.     /* but never delete the newline on the last non-empty line */
  765.     if (lp2 == curbp->b_line.l)
  766.         return (TRUE);
  767.     else if (lp2->l_used == 0) {
  768.         /* next line blank? */
  769.         lremove(curbp,lp2);
  770.         toss_to_undo(lp2);
  771.         return (TRUE);
  772.     }
  773.     copy_for_undo(lp1);
  774.     /* no room in line above, make room */
  775.     if (lp2->l_used > lp1->l_size-lp1->l_used) {
  776.         char *ntext;
  777.         int nsize;
  778.         /* first, create the new image */
  779.         if ((ntext=malloc(nsize = roundup(lp1->l_used + lp2->l_used)))
  780.                                  == NULL)
  781.             return (FALSE);
  782.         if (lp1->l_text) { /* possibly NULL if l_size == 0 */
  783.             memcpy(&ntext[0], &lp1->l_text[0], lp1->l_used);
  784.             ltextfree(lp1,curbp);
  785.         }
  786.         lp1->l_text = ntext;
  787.         lp1->l_size = nsize;
  788.     }
  789.     cp1 = &lp1->l_text[lp1->l_used];
  790.     cp2 = &lp2->l_text[0];
  791.     while (cp2 != &lp2->l_text[lp2->l_used])
  792.         *cp1++ = *cp2++;
  793. #if ! WINMARK
  794.     if (MK.l == lp2) {
  795.         MK.l  = lp1;
  796.         MK.o += lp1->l_used;
  797.     }
  798. #endif
  799.     /* check all windows for references to the deleted line */
  800.     wp = wheadp;
  801.     while (wp != NULL) {
  802.         if (wp->w_line.l == lp2)
  803.             wp->w_line.l = lp1;
  804.         if (wp->w_dot.l == lp2) {
  805.             wp->w_dot.l  = lp1;
  806.             wp->w_dot.o += lp1->l_used;
  807.         }
  808. #if WINMARK
  809.         if (wp->w_mark.l == lp2) {
  810.             wp->w_mark.l  = lp1;
  811.             wp->w_mark.o += lp1->l_used;
  812.         }
  813. #endif
  814.         if (wp->w_lastdot.l == lp2) {
  815.             wp->w_lastdot.l  = lp1;
  816.             wp->w_lastdot.o += lp1->l_used;
  817.         }
  818.         wp = wp->w_wndp;
  819.     }
  820.     if (curbp->b_nmmarks != NULL) { /* fix the named marks */
  821.         int i;
  822.         struct MARK *mp;
  823.         for (i = 0; i < 26; i++) {
  824.             mp = &(curbp->b_nmmarks[i]);
  825.             if (mp->l == lp2) {
  826.                 mp->l  = lp1;
  827.                 mp->o += lp1->l_used;
  828.             }
  829.         }
  830.     }
  831.     lp1->l_used += lp2->l_used;
  832.     lp1->l_fp = lp2->l_fp;
  833.     lp2->l_fp->l_bp = lp1;
  834.     dumpuline(lp1);
  835.     toss_to_undo(lp2);
  836.     return (TRUE);
  837. }
  838.  
  839. /*
  840.  * Delete all of the text saved in the kill buffer. Called by commands when a
  841.  * new kill context is being created. The kill buffer array is released, just
  842.  * in case the buffer has grown to immense size. No errors.
  843.  */
  844. void
  845. ksetup()
  846. {
  847.  
  848.     if ((kregflag & KAPPEND) != 0)
  849.         kregflag = KAPPEND;
  850.     else
  851.         kregflag = KNEEDCLEAN;
  852.     kchars = klines = 0;
  853.  
  854. }
  855. /*
  856.  * clean up the old contents of a kill register.
  857.  * if called from other than kinsert, only does anything in the case where
  858.  * nothing was yanked
  859.  */
  860. void
  861. kdone()
  862. {
  863.     if ((kregflag & KNEEDCLEAN) && kbs[ukb].kbufh != NULL) {
  864.         KILL *kp;    /* ptr to scan kill buffer chunk list */
  865.  
  866.         /* first, delete all the chunks */
  867.         kbs[ukb].kbufp = kbs[ukb].kbufh;
  868.         while (kbs[ukb].kbufp != NULL) {
  869.             kp = kbs[ukb].kbufp->d_next;
  870.             free((char *)(kbs[ukb].kbufp));
  871.             kbs[ukb].kbufp = kp;
  872.         }
  873.  
  874.         /* and reset all the kill buffer pointers */
  875.         kbs[ukb].kbufh = kbs[ukb].kbufp = NULL;
  876.         kbs[ukb].kused = KBLOCK;             
  877.     }
  878.     kregflag &= ~KNEEDCLEAN;
  879.     kbs[ukb].kbflag = kregflag;
  880. }
  881.  
  882. /*
  883.  * Insert a character to the kill buffer, allocating new chunks as needed.
  884.  * Return TRUE if all is well, and FALSE on errors.
  885.  */
  886.  
  887. int
  888. kinsert(c)
  889. int c;        /* character to insert in the kill buffer */
  890. {
  891.     KILL *nchunk;    /* ptr to newly malloced chunk */
  892.     KILLREG *kbp = &kbs[ukb];
  893.  
  894.     kdone(); /* clean up the (possible) old contents */
  895.  
  896.     /* check to see if we need a new chunk */
  897.     if (kbp->kused >= KBLOCK || kbp->kbufh == NULL) {
  898.         if ((nchunk = (KILL *)malloc(sizeof(KILL))) == NULL)
  899.             return(FALSE);
  900.         if (kbp->kbufh == NULL)    /* set head ptr if first time */
  901.             kbp->kbufh = nchunk;
  902.         /* point the current to this new one */
  903.         if (kbp->kbufp != NULL)
  904.             kbp->kbufp->d_next = nchunk;
  905.         kbp->kbufp = nchunk;
  906.         kbp->kbufp->d_next = NULL;
  907.         kbp->kused = 0;
  908.     }
  909.  
  910.     /* and now insert the character */
  911.     kbp->kbufp->d_chunk[kbp->kused++] = c;
  912.     kchars++;
  913.     if (c == '\n')
  914.         klines++;
  915.     return(TRUE);
  916. }
  917.  
  918. /* select one of the named registers for use with the following command */
  919. /*  this could actually be handled as a command prefix, in kbdseq(), much
  920.     the way ^X-cmd and META-cmd are done, except that we need to be
  921.     able to accept any of
  922.          3"adw    "a3dw    "ad3w
  923.     to delete 3 words into register a.  So this routine gives us an
  924.     easy way to handle the second case.  (The third case is handled in
  925.     operators(), the first in main())
  926. */
  927. int
  928. usekreg(f,n)
  929. int f,n;
  930. {
  931.     int c, status;
  932.     CMDFUNC *cfp;            /* function to execute */
  933.     char tok[NSTRING];        /* command incoming */
  934.  
  935.     /* take care of incrementing the buffer number, if we're replaying
  936.         a command via 'dot' */
  937.     incr_dot_kregnum();
  938.  
  939.     if (clexec || isnamedcmd) {
  940.         int stat;
  941.         static char cbuf[2];
  942.             if ((stat=mlreply("Use named register: ", cbuf, 2)) != TRUE)
  943.                     return stat;
  944.         c = cbuf[0];
  945.         } else {
  946.         c = kbd_key();
  947.         }
  948.  
  949.     if (isdigit(c))
  950.         ukb = c - '0';
  951.     else if (islower(c))
  952.         ukb = c - 'a' + 10;  /* named buffs are in 10 through 36 */
  953.     else if (isupper(c)) {
  954.         ukb = c - 'A' + 10;
  955.     } else {
  956.         TTbeep();
  957.         return (FALSE);
  958.     }
  959.  
  960.     if (kbdmode == PLAY && kbdplayreg == ukb) {
  961.         mlforce("[Error: currently executing register %c]",c);
  962.         kbdmode = STOP;
  963.         return FALSE;
  964.     }
  965.     
  966.     if (isupper(c))
  967.         kregflag |= KAPPEND;
  968.  
  969.     if (clexec) {
  970.         macarg(tok);    /* get the next token */
  971.         cfp = engl2fnc(tok);
  972.     } else if (isnamedcmd) {
  973.         return namedcmd(f,n);
  974.     } else {
  975.         /* get the next command from the keyboard */
  976.         c = kbd_seq();
  977.  
  978.         /* allow second chance for entering counts */
  979.         if (f == FALSE) {
  980.             do_num_proc(&c,&f,&n);
  981.             do_rept_arg_proc(&c,&f,&n);
  982.         }
  983.         cfp = kcod2fnc(c);
  984.     }
  985.  
  986.     /* and execute the command */
  987.     status = execute(cfp, f, n);
  988.  
  989.     ukb = 0;
  990.     kregflag = 0;
  991.  
  992.     return(status);
  993.     
  994. }
  995.  
  996. /* buffers 0 through 9 are circulated automatically for full-line deletes */
  997. /* we re-use one of them until the KLINES flag is on, then we advance */
  998. /* to the next */
  999. void
  1000. kregcirculate(killing)
  1001. int killing;
  1002. {
  1003.     static lastkb; /* index of the real "0 */
  1004.  
  1005.     if (ukb >= 10) /* then the user specified a lettered buffer */
  1006.         return;
  1007.  
  1008.     /* we only allow killing into the real "0 */
  1009.     /* ignore any other buffer spec */
  1010.     if (killing) {
  1011.         if ((kbs[lastkb].kbflag & KLINES) && 
  1012.             ! (kbs[lastkb].kbflag & KYANK)) {
  1013.             if (--lastkb < 0) lastkb = 9;
  1014.             kbs[lastkb].kbflag = 0;
  1015.         }
  1016.         ukb = lastkb;
  1017.     } else {
  1018.         /* let 0 pass unmolested -- it is the default */
  1019.         if (ukb == 0) {
  1020.             ukb = lastkb; 
  1021.         } else {
  1022.         /* for the others, if the current "0 has lines in it, it
  1023.             must be `"1', else "1 is `"1'.  get it? */
  1024.             if (kbs[lastkb].kbflag & KLINES)
  1025.                 ukb = (lastkb + ukb - 1) % 10;
  1026.             else
  1027.                 ukb = (lastkb + ukb) % 10;
  1028.         }
  1029.     }
  1030.     
  1031. }
  1032.  
  1033. int
  1034. putbefore(f,n)
  1035. int f,n;
  1036. {
  1037.     return doput(f,n,FALSE,FALSE);
  1038. }
  1039.  
  1040. int
  1041. putafter(f,n)
  1042. int f,n;
  1043. {
  1044.     return doput(f,n,TRUE,FALSE);
  1045. }
  1046.  
  1047. int
  1048. lineputbefore(f,n)
  1049. int f,n;
  1050. {
  1051.     return doput(f,n,FALSE,TRUE);
  1052. }
  1053.  
  1054. int
  1055. lineputafter(f,n)
  1056. int f,n;
  1057. {
  1058.     return doput(f,n,TRUE,TRUE);
  1059. }
  1060.  
  1061.  
  1062. int
  1063. doput(f,n,after,putlines)
  1064. int f,n,after,putlines;
  1065. {
  1066.     int s, oukb, lining;
  1067.     
  1068.     if (!f)
  1069.         n = 1;
  1070.         
  1071.     oukb = ukb;
  1072.     kregcirculate(FALSE);
  1073.     if (kbs[ukb].kbufh == NULL) {
  1074.         if (ukb != 0)
  1075.             mlforce("[Nothing in register %c]", 
  1076.                 (oukb<10)? oukb+'0' : oukb-10+'a');
  1077.         TTbeep();
  1078.         return(FALSE);
  1079.     }
  1080.     lining = (putlines == TRUE || (kbs[ukb].kbflag & KLINES));
  1081.     if (lining) {
  1082.         if (after && !is_header_line(curwp->w_dot, curbp))
  1083.             curwp->w_dot.l = lforw(curwp->w_dot.l);
  1084.         curwp->w_dot.o = 0;
  1085.     } else {
  1086.         if (after && !is_at_end_of_line(curwp->w_dot))
  1087.             forwchar(TRUE,1);
  1088.     }
  1089.     setmark();
  1090.     s = put(n,lining);
  1091.     if (s == TRUE)
  1092.         swapmark();
  1093.     if (is_header_line(curwp->w_dot, curbp))
  1094.         curwp->w_dot.l = lback(curwp->w_dot.l);
  1095.     if (lining)
  1096.         firstnonwhite(FALSE,0);
  1097.     ukb = 0;
  1098.     return (s);
  1099. }
  1100.  
  1101. /*
  1102.  * Put text back from the kill register.
  1103.  */
  1104. int
  1105. put(n,aslines)
  1106. int n,aslines;
  1107. {
  1108.     register int    c;
  1109.     register int    i;
  1110.     int wasnl, suppressnl;
  1111.     register char    *sp;    /* pointer into string to insert */
  1112.     KILL *kp;        /* pointer into kill register */
  1113.     
  1114.     if (n < 0)
  1115.         return FALSE;
  1116.         
  1117.     /* make sure there is something to put */
  1118.     if (kbs[ukb].kbufh == NULL)
  1119.         return TRUE;        /* not an error, just nothing */
  1120.  
  1121.     suppressnl = FALSE;
  1122.     wasnl = FALSE;
  1123.  
  1124.     /* for each time.... */
  1125.     while (n--) {
  1126.         kp = kbs[ukb].kbufh;
  1127.         while (kp != NULL) {
  1128.             if (kp->d_next == NULL)
  1129.                 i = kbs[ukb].kused;
  1130.             else
  1131.                 i = KBLOCK;
  1132.             sp = (char *)kp->d_chunk;
  1133.             while (i--) {
  1134.                 if ((c = *sp++) == '\n') {
  1135.                     if (lnewline() != TRUE)
  1136.                         return FALSE;
  1137.                     wasnl = TRUE;
  1138.                 } else {
  1139.                     if (is_header_line(curwp->w_dot,curbp))
  1140.                         suppressnl = TRUE;
  1141.                     if (linsert(1, c) != TRUE)
  1142.                         return FALSE;
  1143.                     wasnl = FALSE;
  1144.                 }
  1145.             }
  1146.             kp = kp->d_next;
  1147.         }
  1148.         if (wasnl) {
  1149.             if (suppressnl) {
  1150.                 if (ldelnewline() != TRUE)
  1151.                     return FALSE;
  1152.             }
  1153.         } else {
  1154.             if (aslines && !suppressnl) {
  1155.                 if (lnewline() != TRUE)
  1156.                     return FALSE;
  1157.             }
  1158.         }
  1159.     }
  1160.         curwp->w_flag |= WFHARD;
  1161.     return (TRUE);
  1162. }
  1163.  
  1164. /* ARGSUSED */
  1165. int
  1166. execkreg(f,n)
  1167. int f,n;
  1168. {
  1169.     int c, i;
  1170.     KILL *kp;        /* pointer into kill register */
  1171.     static lastreg = -1;
  1172.  
  1173.     if (kbdmode != STOP) {
  1174.         mlforce("[Error: already executing macro]");
  1175.         kbdmode = STOP;
  1176.         return FALSE;
  1177.     }
  1178.  
  1179.     if (!f)
  1180.         n = 1;
  1181.     else if (n <= 0)
  1182.         return TRUE;
  1183.  
  1184.     if (clexec || isnamedcmd) {
  1185.         int stat;
  1186.         static char cbuf[2];
  1187.             if ((stat=mlreply("Execute register: ", cbuf, 2)) != TRUE)
  1188.                     return stat;
  1189.         c = cbuf[0];
  1190.         } else {
  1191.         c = kbd_key();
  1192.         }
  1193.  
  1194.     if (c == '@' && lastreg != -1) {
  1195.         c = lastreg;
  1196.     } else if (c < 'a' || c > 'z') {
  1197.         TTbeep();
  1198.         mlforce("[Invalid register name]");
  1199.         return FALSE;
  1200.     }
  1201.  
  1202.     lastreg = c;
  1203.  
  1204.     c = c - 'a' + 10;
  1205.  
  1206.     /* make sure there is something to execute */
  1207.     kp = kbs[c].kbufh;
  1208.     if (kp == NULL)
  1209.         return TRUE;        /* not an error, just nothing */
  1210.  
  1211.     if (kp->d_next == NULL) {
  1212.         i = kbs[c].kused;
  1213.     } else {
  1214.         mlforce("Warning:  only first %d characters will execute",
  1215.                                 KBLOCK);
  1216.         i = KBLOCK;
  1217.     }
  1218.     kbdrep = n;        /* remember how many times to execute */
  1219.     kbdmode = PLAY;     /* start us in play mode */
  1220.     kbdplayreg = c;      /* which buffer is playing */
  1221.     kbdptr = kp->d_chunk;    /*    at the beginning */
  1222.     kbdend = &kp->d_chunk[i];
  1223.     dotcmdmode = STOP;
  1224.     return TRUE;
  1225. }
  1226.  
  1227. /* ARGSUSED */
  1228. int
  1229. loadkreg(f,n)
  1230. int f,n;
  1231. {
  1232.     int s;
  1233.     char respbuf[NFILEN];
  1234.  
  1235.     ksetup();
  1236.     s = kbd_string("Load register with: ", respbuf,
  1237.                     NFILEN - 1, '\n', NO_EXPAND, FALSE);
  1238.     if (s != TRUE)
  1239.         return FALSE;
  1240.     for (s = 0; s < NFILEN; s++) {
  1241.         if (!respbuf[s])
  1242.             break;
  1243.         kinsert(respbuf[s]);
  1244.     }
  1245.     kdone();
  1246.     return TRUE;
  1247. }
  1248.