home *** CD-ROM | disk | FTP | other *** search
/ The Fred Fish Collection 1.5 / ffcollection-1-5-1992-11.iso / ff_disks / 001-099 / ff093.lzh / MicroEmacs / source / src.arc / line.c < prev    next >
C/C++ Source or Header  |  1987-08-16  |  19KB  |  529 lines

  1. /*
  2.  * The functions in this file are a general set of line management utilities.
  3.  * They are the only routines that touch the text. They also touch the buffer
  4.  * and window structures, to make sure that the necessary updating gets done.
  5.  * There are routines in this file that handle the kill buffer too. It isn't
  6.  * here for any good reason.
  7.  *
  8.  * Note that this code only updates the dot and mark values in the window list.
  9.  * Since all the code acts on the current window, the buffer that we are
  10.  * editing must be being displayed, which means that "b_nwnd" is non zero,
  11.  * which means that the dot and mark values in the buffer headers are nonsense.
  12.  */
  13.  
  14. #include        <stdio.h>
  15. #include    "estruct.h"
  16. #include        "edef.h"
  17.  
  18. KILL *ykbuf;    /* ptr to current kill buffer chunk being yanked */
  19. int ykboff;    /* offset into that chunk */
  20.  
  21. /*
  22.  * This routine allocates a block of memory large enough to hold a LINE
  23.  * containing "used" characters. The block is always rounded up a bit. Return
  24.  * a pointer to the new block, or NULL if there isn't any memory left. Print a
  25.  * message in the message line if no space.
  26.  */
  27. LINE    *
  28. lalloc(used)
  29. register int    used;
  30. {
  31.         register LINE   *lp;
  32.         register int    size;
  33.     char *malloc();
  34.  
  35.         size = (used+NBLOCK-1) & ~(NBLOCK-1);
  36.         if (size == 0)                          /* Assume that an empty */
  37.                 size = NBLOCK;                  /* line is for type-in. */
  38.         if ((lp = (LINE *) malloc(sizeof(LINE)+size)) == NULL) {
  39.                 mlwrite("Cannot allocate %d bytes", size);
  40.                 return (NULL);
  41.         }
  42.         lp->l_size = size;
  43.         lp->l_used = used;
  44.         return (lp);
  45. }
  46.  
  47. /*
  48.  * Delete line "lp". Fix all of the links that might point at it (they are
  49.  * moved to offset 0 of the next line. Unlink the line from whatever buffer it
  50.  * might be in. Release the memory. The buffers are updated too; the magic
  51.  * conditions described in the above comments don't hold here.
  52.  */
  53. lfree(lp)
  54. register LINE   *lp;
  55. {
  56.         register BUFFER *bp;
  57.         register WINDOW *wp;
  58.  
  59.         wp = wheadp;
  60.         while (wp != NULL) {
  61.                 if (wp->w_linep == lp)
  62.                         wp->w_linep = lp->l_fp;
  63.                 if (wp->w_dotp  == lp) {
  64.                         wp->w_dotp  = lp->l_fp;
  65.                         wp->w_doto  = 0;
  66.                 }
  67.                 if (wp->w_markp == lp) {
  68.                         wp->w_markp = lp->l_fp;
  69.                         wp->w_marko = 0;
  70.                 }
  71.                 wp = wp->w_wndp;
  72.         }
  73.         bp = bheadp;
  74.         while (bp != NULL) {
  75.                 if (bp->b_nwnd == 0) {
  76.                         if (bp->b_dotp  == lp) {
  77.                                 bp->b_dotp = lp->l_fp;
  78.                                 bp->b_doto = 0;
  79.                         }
  80.                         if (bp->b_markp == lp) {
  81.                                 bp->b_markp = lp->l_fp;
  82.                                 bp->b_marko = 0;
  83.                         }
  84.                 }
  85.                 bp = bp->b_bufp;
  86.         }
  87.         lp->l_bp->l_fp = lp->l_fp;
  88.         lp->l_fp->l_bp = lp->l_bp;
  89.         free((char *) lp);
  90. }
  91.  
  92. /*
  93.  * This routine gets called when a character is changed in place in the current
  94.  * buffer. It updates all of the required flags in the buffer and window
  95.  * system. The flag used is passed as an argument; if the buffer is being
  96.  * displayed in more than 1 window we change EDIT t HARD. Set MODE if the
  97.  * mode line needs to be updated (the "*" has to be set).
  98.  */
  99. lchange(flag)
  100. register int    flag;
  101. {
  102.         register WINDOW *wp;
  103.  
  104.         if (curbp->b_nwnd != 1)                 /* Ensure hard.         */
  105.                 flag = WFHARD;
  106.         if ((curbp->b_flag&BFCHG) == 0) {       /* First change, so     */
  107.                 flag |= WFMODE;                 /* update mode lines.   */
  108.                 curbp->b_flag |= BFCHG;
  109.         }
  110.         wp = wheadp;
  111.         while (wp != NULL) {
  112.                 if (wp->w_bufp == curbp)
  113.                         wp->w_flag |= flag;
  114.                 wp = wp->w_wndp;
  115.         }
  116. }
  117.  
  118. insspace(f, n)    /* insert spaces forward into text */
  119.  
  120. int f, n;    /* default flag and numeric argument */
  121.  
  122. {
  123.     linsert(n, ' ');
  124.     backchar(f, n);
  125. }
  126.  
  127. /*
  128.  * Insert "n" copies of the character "c" at the current location of dot. In
  129.  * the easy case all that happens is the text is stored in the line. In the
  130.  * hard case, the line has to be reallocated. When the window list is updated,
  131.  * take special care; I screwed it up once. You always update dot in the
  132.  * current window. You update mark, and a dot in another window, if it is
  133.  * greater than the place where you did the insert. Return TRUE if all is
  134.  * well, and FALSE on errors.
  135.  */
  136. linsert(n, c)
  137. {
  138.         register char   *cp1;
  139.         register char   *cp2;
  140.         register LINE   *lp1;
  141.         register LINE   *lp2;
  142.         register LINE   *lp3;
  143.         register int    doto;
  144.         register int    i;
  145.         register WINDOW *wp;
  146.  
  147.     if (curbp->b_mode&MDVIEW)    /* don't allow this command if    */
  148.         return(rdonly());    /* we are in read only mode    */
  149.         lchange(WFEDIT);
  150.         lp1 = curwp->w_dotp;                    /* Current line         */
  151.         if (lp1 == curbp->b_linep) {            /* At the end: special  */
  152.                 if (curwp->w_doto != 0) {
  153.                         mlwrite("bug: linsert");
  154.                         return (FALSE);
  155.                 }
  156.                 if ((lp2=lalloc(n)) == NULL)    /* Allocate new line    */
  157.                         return (FALSE);
  158.                 lp3 = lp1->l_bp;                /* Previous line        */
  159.                 lp3->l_fp = lp2;                /* Link in              */
  160.                 lp2->l_fp = lp1;
  161.                 lp1->l_bp = lp2;
  162.                 lp2->l_bp = lp3;
  163.                 for (i=0; i<n; ++i)
  164.                         lp2->l_text[i] = c;
  165.                 curwp->w_dotp = lp2;
  166.                 curwp->w_doto = n;
  167.                 return (TRUE);
  168.         }
  169.         doto = curwp->w_doto;                   /* Save for later.      */
  170.         if (lp1->l_used+n > lp1->l_size) {      /* Hard: reallocate     */
  171.                 if ((lp2=lalloc(lp1->l_used+n)) == NULL)
  172.                         return (FALSE);
  173.                 cp1 = &lp1->l_text[0];
  174.                 cp2 = &lp2->l_text[0];
  175.                 while (cp1 != &lp1->l_text[doto])
  176.                         *cp2++ = *cp1++;
  177.                 cp2 += n;
  178.                 while (cp1 != &lp1->l_text[lp1->l_used])
  179.                         *cp2++ = *cp1++;
  180.                 lp1->l_bp->l_fp = lp2;
  181.                 lp2->l_fp = lp1->l_fp;
  182.                 lp1->l_fp->l_bp = lp2;
  183.                 lp2->l_bp = lp1->l_bp;
  184.                 free((char *) lp1);
  185.         } else {                                /* Easy: in place       */
  186.                 lp2 = lp1;                      /* Pretend new line     */
  187.                 lp2->l_used += n;
  188.                 cp2 = &lp1->l_text[lp1->l_used];
  189.                 cp1 = cp2-n;
  190.                 while (cp1 != &lp1->l_text[doto])
  191.                         *--cp2 = *--cp1;
  192.         }
  193.         for (i=0; i<n; ++i)                     /* Add the characters   */
  194.                 lp2->l_text[doto+i] = c;
  195.         wp = wheadp;                            /* Update windows       */
  196.         while (wp != NULL) {
  197.                 if (wp->w_linep == lp1)
  198.                         wp->w_linep = lp2;
  199.                 if (wp->w_dotp == lp1) {
  200.                         wp->w_dotp = lp2;
  201.                         if (wp==curwp || wp->w_doto>doto)
  202.                                 wp->w_doto += n;
  203.                 }
  204.                 if (wp->w_markp == lp1) {
  205.                         wp->w_markp = lp2;
  206.                         if (wp->w_marko > doto)
  207.                                 wp->w_marko += n;
  208.                 }
  209.                 wp = wp->w_wndp;
  210.         }
  211.         return (TRUE);
  212. }
  213.  
  214. /*
  215.  * Insert a newline into the buffer at the current location of dot in the
  216.  * current window. The funny ass-backwards way it does things is not a botch;
  217.  * it just makes the last line in the file not a special case. Return TRUE if
  218.  * everything works out and FALSE on error (memory allocation failure). The
  219.  * update of dot and mark is a bit easier then in the above case, because the
  220.  * split forces more updating.
  221.  */
  222. lnewline()
  223. {
  224.         register char   *cp1;
  225.         register char   *cp2;
  226.         register LINE   *lp1;
  227.         register LINE   *lp2;
  228.         register int    doto;
  229.         register WINDOW *wp;
  230.  
  231.     if (curbp->b_mode&MDVIEW)    /* don't allow this command if    */
  232.         return(rdonly());    /* we are in read only mode    */
  233.         lchange(WFHARD);
  234.         lp1  = curwp->w_dotp;                   /* Get the address and  */
  235.         doto = curwp->w_doto;                   /* offset of "."        */
  236.         if ((lp2=lalloc(doto)) == NULL)         /* New first half line  */
  237.                 return (FALSE);
  238.         cp1 = &lp1->l_text[0];                  /* Shuffle text around  */
  239.         cp2 = &lp2->l_text[0];
  240.         while (cp1 != &lp1->l_text[doto])
  241.                 *cp2++ = *cp1++;
  242.         cp2 = &lp1->l_text[0];
  243.         while (cp1 != &lp1->l_text[lp1->l_used])
  244.                 *cp2++ = *cp1++;
  245.         lp1->l_used -= doto;
  246.         lp2->l_bp = lp1->l_bp;
  247.         lp1->l_bp = lp2;
  248.         lp2->l_bp->l_fp = lp2;
  249.         lp2->l_fp = lp1;
  250.         wp = wheadp;                            /* Windows              */
  251.         while (wp != NULL) {
  252.                 if (wp->w_linep == lp1)
  253.                         wp->w_linep = lp2;
  254.                 if (wp->w_dotp == lp1) {
  255.                         if (wp->w_doto < doto)
  256.                                 wp->w_dotp = lp2;
  257.                         else
  258.                                 wp->w_doto -= doto;
  259.                 }
  260.                 if (wp->w_markp == lp1) {
  261.                         if (wp->w_marko < doto)
  262.                                 wp->w_markp = lp2;
  263.                         else
  264.                                 wp->w_marko -= doto;
  265.                 }
  266.                 wp = wp->w_wndp;
  267.         }
  268.         return (TRUE);
  269. }
  270.  
  271. /*
  272.  * This function deletes "n" bytes, starting at dot. It understands how do deal
  273.  * with end of lines, etc. It returns TRUE if all of the characters were
  274.  * deleted, and FALSE if they were not (because dot ran into the end of the
  275.  * buffer. The "kflag" is TRUE if the text should be put in the kill buffer.
  276.  */
  277. ldelete(n, kflag)
  278.  
  279. long n;        /* # of chars to delete */
  280. int kflag;    /* put killed text in kill buffer flag */
  281.  
  282. {
  283.         register char   *cp1;
  284.         register char   *cp2;
  285.         register LINE   *dotp;
  286.         register int    doto;
  287.         register int    chunk;
  288.         register WINDOW *wp;
  289.  
  290.     if (curbp->b_mode&MDVIEW)    /* don't allow this command if    */
  291.         return(rdonly());    /* we are in read only mode    */
  292.         while (n != 0) {
  293.                 dotp = curwp->w_dotp;
  294.                 doto = curwp->w_doto;
  295.                 if (dotp == curbp->b_linep)     /* Hit end of buffer.   */
  296.                         return (FALSE);
  297.                 chunk = dotp->l_used-doto;      /* Size of chunk.       */
  298.                 if (chunk > n)
  299.                         chunk = n;
  300.                 if (chunk == 0) {               /* End of line, merge.  */
  301.                         lchange(WFHARD);
  302.                         if (ldelnewline() == FALSE
  303.                         || (kflag!=FALSE && kinsert('\n')==FALSE))
  304.                                 return (FALSE);
  305.                         --n;
  306.                         continue;
  307.                 }
  308.                 lchange(WFEDIT);
  309.                 cp1 = &dotp->l_text[doto];      /* Scrunch text.        */
  310.                 cp2 = cp1 + chunk;
  311.                 if (kflag != FALSE) {           /* Kill?                */
  312.                         while (cp1 != cp2) {
  313.                                 if (kinsert(*cp1) == FALSE)
  314.                                         return (FALSE);
  315.                                 ++cp1;
  316.                         }
  317.                         cp1 = &dotp->l_text[doto];
  318.                 }
  319.                 while (cp2 != &dotp->l_text[dotp->l_used])
  320.                         *cp1++ = *cp2++;
  321.                 dotp->l_used -= chunk;
  322.                 wp = wheadp;                    /* Fix windows          */
  323.                 while (wp != NULL) {
  324.                         if (wp->w_dotp==dotp && wp->w_doto>=doto) {
  325.                                 wp->w_doto -= chunk;
  326.                                 if (wp->w_doto < doto)
  327.                                         wp->w_doto = doto;
  328.                         }
  329.                         if (wp->w_markp==dotp && wp->w_marko>=doto) {
  330.                                 wp->w_marko -= chunk;
  331.                                 if (wp->w_marko < doto)
  332.                                         wp->w_marko = doto;
  333.                         }
  334.                         wp = wp->w_wndp;
  335.                 }
  336.                 n -= chunk;
  337.         }
  338.         return (TRUE);
  339. }
  340.  
  341. /*
  342.  * Delete a newline. Join the current line with the next line. If the next line
  343.  * is the magic header line always return TRUE; merging the last line with the
  344.  * header line can be thought of as always being a successful operation, even
  345.  * if nothing is done, and this makes the kill buffer work "right". Easy cases
  346.  * can be done by shuffling data around. Hard cases require that lines be moved
  347.  * about in memory. Return FALSE on error and TRUE if all looks ok. Called by
  348.  * "ldelete" only.
  349.  */
  350. ldelnewline()
  351. {
  352.         register char   *cp1;
  353.         register char   *cp2;
  354.         register LINE   *lp1;
  355.         register LINE   *lp2;
  356.         register LINE   *lp3;
  357.         register WINDOW *wp;
  358.  
  359.     if (curbp->b_mode&MDVIEW)    /* don't allow this command if    */
  360.         return(rdonly());    /* we are in read only mode    */
  361.         lp1 = curwp->w_dotp;
  362.         lp2 = lp1->l_fp;
  363.         if (lp2 == curbp->b_linep) {            /* At the buffer end.   */
  364.                 if (lp1->l_used == 0)           /* Blank line.          */
  365.                         lfree(lp1);
  366.                 return (TRUE);
  367.         }
  368.         if (lp2->l_used <= lp1->l_size-lp1->l_used) {
  369.                 cp1 = &lp1->l_text[lp1->l_used];
  370.                 cp2 = &lp2->l_text[0];
  371.                 while (cp2 != &lp2->l_text[lp2->l_used])
  372.                         *cp1++ = *cp2++;
  373.                 wp = wheadp;
  374.                 while (wp != NULL) {
  375.                         if (wp->w_linep == lp2)
  376.                                 wp->w_linep = lp1;
  377.                         if (wp->w_dotp == lp2) {
  378.                                 wp->w_dotp  = lp1;
  379.                                 wp->w_doto += lp1->l_used;
  380.                         }
  381.                         if (wp->w_markp == lp2) {
  382.                                 wp->w_markp  = lp1;
  383.                                 wp->w_marko += lp1->l_used;
  384.                         }
  385.                         wp = wp->w_wndp;
  386.                 }
  387.                 lp1->l_used += lp2->l_used;
  388.                 lp1->l_fp = lp2->l_fp;
  389.                 lp2->l_fp->l_bp = lp1;
  390.                 free((char *) lp2);
  391.                 return (TRUE);
  392.         }
  393.         if ((lp3=lalloc(lp1->l_used+lp2->l_used)) == NULL)
  394.                 return (FALSE);
  395.         cp1 = &lp1->l_text[0];
  396.         cp2 = &lp3->l_text[0];
  397.         while (cp1 != &lp1->l_text[lp1->l_used])
  398.                 *cp2++ = *cp1++;
  399.         cp1 = &lp2->l_text[0];
  400.         while (cp1 != &lp2->l_text[lp2->l_used])
  401.                 *cp2++ = *cp1++;
  402.         lp1->l_bp->l_fp = lp3;
  403.         lp3->l_fp = lp2->l_fp;
  404.         lp2->l_fp->l_bp = lp3;
  405.         lp3->l_bp = lp1->l_bp;
  406.         wp = wheadp;
  407.         while (wp != NULL) {
  408.                 if (wp->w_linep==lp1 || wp->w_linep==lp2)
  409.                         wp->w_linep = lp3;
  410.                 if (wp->w_dotp == lp1)
  411.                         wp->w_dotp  = lp3;
  412.                 else if (wp->w_dotp == lp2) {
  413.                         wp->w_dotp  = lp3;
  414.                         wp->w_doto += lp1->l_used;
  415.                 }
  416.                 if (wp->w_markp == lp1)
  417.                         wp->w_markp  = lp3;
  418.                 else if (wp->w_markp == lp2) {
  419.                         wp->w_markp  = lp3;
  420.                         wp->w_marko += lp1->l_used;
  421.                 }
  422.                 wp = wp->w_wndp;
  423.         }
  424.         free((char *) lp1);
  425.         free((char *) lp2);
  426.         return (TRUE);
  427. }
  428.  
  429. /*
  430.  * Delete all of the text saved in the kill buffer. Called by commands when a
  431.  * new kill context is being created. The kill buffer array is released, just
  432.  * in case the buffer has grown to immense size. No errors.
  433.  */
  434. kdelete()
  435. {
  436.     KILL *kp;    /* ptr to scan kill buffer chunk list */
  437.  
  438.         if (kbufh != NULL) {
  439.  
  440.         /* first, delete all the chunks */
  441.             kbufp = kbufh;
  442.             while (kbufp != NULL) {
  443.                 kp = kbufp->d_next;
  444.                 free(kbufp);
  445.                 kbufp = kp;
  446.             }
  447.  
  448.         /* and reset all the kill buffer pointers */
  449.         kbufh = kbufp = NULL;
  450.         kused = KBLOCK;                
  451.         }
  452. }
  453.  
  454. /*
  455.  * Insert a character to the kill buffer, allocating new chunks as needed.
  456.  * Return TRUE if all is well, and FALSE on errors.
  457.  */
  458.  
  459. kinsert(c)
  460.  
  461. int c;        /* character to insert in the kill buffer */
  462.  
  463. {
  464.     KILL *nchunk;    /* ptr to newly malloced chunk */
  465.  
  466.     /* check to see if we need a new chunk */
  467.     if (kused >= KBLOCK) {
  468.         if ((nchunk = (KILL *)malloc(sizeof(KILL))) == NULL)
  469.             return(FALSE);
  470.         if (kbufh == NULL)    /* set head ptr if first time */
  471.             kbufh = nchunk;
  472.         if (kbufp != NULL)    /* point the current to this new one */
  473.             kbufp->d_next = nchunk;
  474.         kbufp = nchunk;
  475.         kbufp->d_next = NULL;
  476.         kused = 0;
  477.     }
  478.  
  479.     /* and now insert the character */
  480.     kbufp->d_chunk[kused++] = c;
  481.     return(TRUE);
  482. }
  483.  
  484. /*
  485.  * Yank text back from the kill buffer. This is really easy. All of the work
  486.  * is done by the standard insert routines. All you do is run the loop, and
  487.  * check for errors. Bound to "C-Y".
  488.  */
  489. yank(f, n)
  490. {
  491.         register int    c;
  492.         register int    i;
  493.     register char    *sp;    /* pointer into string to insert */
  494.     KILL *kp;        /* pointer into kill buffer */
  495.  
  496.     if (curbp->b_mode&MDVIEW)    /* don't allow this command if    */
  497.         return(rdonly());    /* we are in read only mode    */
  498.         if (n < 0)
  499.                 return (FALSE);
  500.     /* make sure there is something to yank */
  501.     if (kbufh == NULL)
  502.         return(TRUE);        /* not an error, just nothing */
  503.  
  504.     /* for each time.... */
  505.         while (n--) {
  506.         kp = kbufh;
  507.         while (kp != NULL) {
  508.             if (kp->d_next == NULL)
  509.                 i = kused;
  510.             else
  511.                 i = KBLOCK;
  512.             sp = kp->d_chunk;
  513.             while (i--) {
  514.                             if ((c = *sp++) == '\n') {
  515.                                     if (lnewline() == FALSE)
  516.                                             return (FALSE);
  517.                             } else {
  518.                                     if (linsert(1, c) == FALSE)
  519.                                             return (FALSE);
  520.                             }
  521.                     }
  522.                     kp = kp->d_next;
  523.                 }
  524.         }
  525.         return (TRUE);
  526. }
  527.  
  528.  
  529.