home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / pine / pine3.07 / pico / line.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-04-18  |  19.1 KB  |  520 lines

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