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