home *** CD-ROM | disk | FTP | other *** search
- #include "jam.h"
- #include"stdlib.h"
- #include "def.h"
- #include "keyname.h"
- /*
- * Text line handling.
- * The functions in this file
- * are a general set of line management
- * utilities. They are the only routines that
- * touch the text. They also touch the buffer
- * and window structures, to make sure that the
- * necessary updating gets done. There are routines
- * in this file that handle the kill buffer too.
- * It isn't here for any good reason.
- *
- * Note that this code only updates the dot and
- * mark values in the window list. Since all the code
- * acts on the current window, the buffer that we
- * are editing must be being displayed, which means
- * that "b_nwnd" is non zero, which means that the
- * dot and mark values in the buffer headers are
- * nonsense.
- */
-
- #ifndef NBLOCK
- # define NBLOCK 16 /* Line block chunk size */
- #endif
-
- #ifndef KBLOCK
- # define KBLOCK 256 /* Kill buffer block size. */
- #endif
-
- static char HUGE *kbufp = NULL; /* Kill buffer data. */
- static RSIZE kused = 0; /* # of bytes used in KB. */
- static RSIZE ksize = 0; /* # of bytes allocated in KB. */
- static RSIZE kstart = 0; /* # of first used byte in KB. */
-
- RSIZE return_kused()
- {
- return(kused - kstart);
- }
-
- /*
- * This routine allocates a block of memory large enough to hold a LINE
- * containing "used" characters. The block is rounded up to whatever
- * needs to be allocated. (use lallocx for lines likely to grow.)
- * Return a pointer to the new block, or NULL if there isn't
- * any memory left. Print a message in the message line if no space.
- */
- LINE *lalloc(used)
- register int used;
- {
- register LINE *lp;
- register int size;
-
- size = used + sizeof(LINE);
-
- #ifdef MALLOCROUND
- MALLOCROUND(size); /* round up to a size optimal to malloc */
- #endif
-
- if((lp = (LINE *)calloc(1, (unsigned)size)) == NULL)
- {
- ewprintf(Nobytes, size);
- return (LINE *)NULL;
- }
-
- lp->l_size = size - sizeof(LINE);
- lp->l_used = used;
- return lp;
- }
-
- /*
- * Like lalloc, only round amount desired up because this line will
- * probably grow. We always make room for at least one more char.
- * (thus making 0 not a special case anymore.)
- */
- LINE *lallocx(used)
- int used;
- {
- register int size;
- register LINE *lp;
-
- size = (NBLOCK+used) & ~(NBLOCK-1);
- if((lp = lalloc(size)) != NULL)
- lp->l_used = used;
- return lp;
- }
-
- /*
- * Delete line "lp". Fix all of the
- * links that might point at it (they are
- * moved to offset 0 of the next line.
- * Unlink the line from whatever buffer it
- * might be in. Release the memory. The
- * buffers are updated too; the magic conditions
- * described in the above comments don't hold
- * here.
- */
- VOID lfree(lp)
- register LINE *lp;
- {
- register BUFFER *bp;
- register EWINDOW *wp;
-
- for(wp = wheadp; wp != NULL; wp = wp->w_wndp)
- {
- if (wp->w_linep == lp)
- wp->w_linep = lforw(lp);
- if (wp->w_dotp == lp)
- {
- wp->w_dotp = lforw(lp);
- wp->w_doto = 0;
- }
- if (wp->w_markp == lp)
- {
- wp->w_markp = lforw(lp);
- wp->w_marko = 0;
- }
- }
-
- for(bp = bheadp; bp != NULL; bp = bp->b_bufp)
- {
- if (bp->b_nwnd == 0)
- {
- if (bp->b_dotp == lp)
- {
- bp->b_dotp = lforw(lp);
- bp->b_doto = 0;
- }
- if (bp->b_markp == lp)
- {
- bp->b_markp = lforw(lp);
- bp->b_marko = 0;
- }
- }
- }
- lkill(lp);
- }
- void lkill(lp)
- LINE *lp;
- {
- if (!lp)
- return;
-
- if (lp->l_bp)
- lp->l_bp->l_fp = lp->l_fp;
- if (lp->l_fp)
- lp->l_fp->l_bp = lp->l_bp;
- free((char *) lp);
- }
- /*
- * This routine gets called when
- * a character is changed in place in the
- * current buffer. It updates all of the required
- * flags in the buffer and window system. The flag
- * used is passed as an argument; if the buffer is being
- * displayed in more than 1 window we change EDIT to
- * HARD. Set MODE if the mode line needs to be
- * updated (the "*" has to be set).
- *
- * NOTE special calls to changelineflag for marking the
- * actual lines changed due to edit, etc. Not easy to
- * combine the functions. (JAM)
- *
- */
- VOID lchange(flag)
- register int flag;
- {
- register EWINDOW *wp;
-
- if ((curbp->b_flag&BFCHG) == 0) /* First change, so */
- {
- curbp->b_flag |= BFCHG;
- thisflag |= CFNEWC;
- }
-
- curbp->b_flag |= BFINC; /* needs incremental */
- flag |= WFMODE; /* update mode lines. */
-
- for(wp = wheadp; wp != NULL; wp = wp->w_wndp)
- {
- if (wp->w_bufp == curbp)
- {
- wp->w_flag |= flag;
- if (wp != curwp)
- wp->w_flag |= WFHARD;
- }
- }
- }
-
- /*
- * Insert "n" copies of the character "c"
- * at the current location of dot. In the easy case
- * all that happens is the text is stored in the line.
- * In the hard case, the line has to be reallocated.
- * When the window list is updated, take special
- * care; I screwed it up once. You always update dot
- * in the current window. You update mark, and a
- * dot in another window, if it is greater than
- * the place where you did the insert. Return TRUE
- * if all is well, and FALSE on errors.
- */
- linsert(n, c)
- int n, c;
- {
- register char *cp1;
- register char *cp2;
- register LINE *lp1;
- LINE *lp2;
- LINE *lp3;
- register int doto;
- register RSIZE i;
- EWINDOW *wp;
-
- if (curbp->b_flag & BFVIEW)
- {
- ttbeep();
- return FALSE;
- }
-
- lchange(WFEDIT);
- lp1 = curwp->w_dotp; /* Current line */
- if (lp1 == curbp->b_linep) /* At the end: special */
- { /* now should only happen in empty buffer */
- if (curwp->w_doto != 0)
- {
- ewprintf("Bug!: linsert");
- return FALSE;
- }
-
- if ((lp2=lallocx(n)) == NULL) /* Allocate new line */
- return FALSE;
- changelineflag(lp2, TRUE);
-
- lp3 = lback(lp1); /* Previous line */
- lforw(lp3) = lp2; /* Link in */
- lforw(lp2) = lp1;
- lback(lp1) = lp2;
- lback(lp2) = lp3;
-
- for (i=0; i<n; ++i)
- lp2->l_text[i] = (char)c;
-
- for(wp = wheadp; wp != NULL; wp = wp->w_wndp)
- {
- if (wp->w_linep == lp1)
- wp->w_linep = lp2;
- if (wp->w_dotp == lp1)
- wp->w_dotp = lp2;
- if (wp->w_markp == lp1)
- wp->w_markp = lp2;
- }
- /*NOSTRICT*/
- curwp->w_doto = n;
- return TRUE;
- }
- doto = curwp->w_doto; /* Save for later. */
- /*NOSTRICT (2) */
-
- if (lp1->l_used+n > lp1->l_size) /* Hard: reallocate */
- {
- if ((lp2=lallocx(lp1->l_used+n)) == NULL)
- return FALSE;
- changelineflag(lp2, TRUE);
- cp1 = &lp1->l_text[0];
- cp2 = &lp2->l_text[0];
- while (cp1 != &lp1->l_text[doto])
- *cp2++ = *cp1++;
- /*NOSTRICT*/
- cp2 += n;
- while (cp1 != &lp1->l_text[lp1->l_used])
- *cp2++ = *cp1++;
- lp1->l_bp->l_fp = lp2;
- lp2->l_fp = lp1->l_fp;
- lp1->l_fp->l_bp = lp2;
- lp2->l_bp = lp1->l_bp;
- free((char *) lp1);
- }
- else
- { /* Easy: in place */
- lp2 = lp1; /* Pretend new line */
- changelineflag(lp2, TRUE);
- /*NOSTRICT*/
- lp2->l_used += n;
- cp2 = &lp1->l_text[lp1->l_used];
-
- cp1 = cp2-n;
- while (cp1 != &lp1->l_text[doto])
- *--cp2 = *--cp1;
- }
-
- for (i=0; i<n; ++i) /* Add the characters */
- lputc(lp2, doto + i, (char)c);
-
- for(wp = wheadp; wp != NULL; wp = wp->w_wndp)
- {
- if (wp->w_linep == lp1)
- wp->w_linep = lp2;
- if (wp->w_dotp == lp1)
- {
- wp->w_dotp = lp2;
- if (wp==curwp || wp->w_doto>doto)
- /*NOSTRICT*/
- wp->w_doto += n;
- }
- if (wp->w_markp == lp1)
- {
- wp->w_markp = lp2;
- if (wp->w_marko > doto)
- /*NOSTRICT*/
- wp->w_marko += n;
- }
- }
- return TRUE;
- }
-
- /*
- * Insert a newline into the buffer
- * at the current location of dot in the current
- * window. The funny ass-backwards way is no longer used.
- */
- lnewline()
- {
- register LINE *lp1;
- register LINE *lp2;
- register int doto;
- register int nlen;
- EWINDOW *wp;
-
- if (curbp->b_flag & BFVIEW)
- {
- ttbeep();
- return FALSE;
- }
-
- lchange(WFHARD);
- lp1 = curwp->w_dotp; /* Get the address and */
- doto = curwp->w_doto; /* offset of "." */
- if(doto == 0) /* avoid unneeded copying */
- {
- if((lp2 = lallocx(0)) == NULL) /* new first part */
- return FALSE;
- changelineflag(lp2, TRUE);
- lp2->l_bp = lp1->l_bp;
- lp1->l_bp->l_fp = lp2;
- lp2->l_fp = lp1;
- lp1->l_bp = lp2;
- for(wp = wheadp; wp!=NULL; wp = wp->w_wndp)
- if(wp->w_linep == lp1)
- wp->w_linep = lp2;
- return TRUE;
- }
- nlen = llength(lp1) - doto; /* length of new part */
- if((lp2=lallocx(nlen)) == NULL) /* New second half line */
- return FALSE;
- changelineflag(lp2, TRUE);
- if(nlen!=0)
- bcopy(&lp1->l_text[doto], &lp2->l_text[0], nlen);
- if (lp1->l_used != doto)
- changelineflag(lp1, TRUE);
- lp1->l_used = doto;
- lp2->l_bp = lp1;
- lp2->l_fp = lp1->l_fp;
- lp1->l_fp = lp2;
- lp2->l_fp->l_bp = lp2;
-
- for(wp = wheadp; wp != NULL; wp = wp->w_wndp) /* Windows */
- {
- if (wp->w_dotp == lp1 && wp->w_doto >= doto)
- {
- wp->w_dotp = lp2;
- wp->w_doto -= doto;
- }
- if (wp->w_markp == lp1 && wp->w_marko >= doto)
- {
- wp->w_markp = lp2;
- wp->w_marko -= doto;
- }
- }
- return TRUE;
- }
-
- /*
- * This function deletes "n" bytes,
- * starting at dot. It understands how do deal
- * with end of lines, etc. It returns TRUE if all
- * of the characters were deleted, and FALSE if
- * they were not (because dot ran into the end of
- * the buffer. The "kflag" indicates either no insertion,
- * or direction of insertion into the kill buffer.
- */
- ldelete(n, kflag)
- RSIZE n;
- int kflag;
- {
- register char *cp1;
- register char *cp2;
- register LINE *dotp;
- register int doto;
- register RSIZE chunk;
- EWINDOW *wp;
-
- if (curbp->b_flag & BFVIEW)
- {
- ttbeep();
- return FALSE;
- }
-
- /*
- * HACK - doesn't matter, and fixes back-over-nl bug for empty
- * kill buffers.
- */
- if (kused == kstart)
- kflag = KFORW;
-
- while (n != 0)
- {
- dotp = curwp->w_dotp;
- doto = curwp->w_doto;
- if (dotp == curbp->b_linep) /* Hit end of buffer. */
- return FALSE;
- chunk = dotp->l_used-doto; /* Size of chunk. */
- if (chunk > n)
- chunk = n;
- if (chunk == 0) /* End of line, merge. */
- {
- if(dotp == lback(curbp->b_linep))
- return FALSE; /* End of buffer. */
- lchange(WFHARD);
- if (ldelnewline() == FALSE
- || (kflag!=KNONE && kinsert('\n', kflag)==FALSE))
- return FALSE;
- --n;
- continue;
- }
- lchange(WFEDIT);
- cp1 = &dotp->l_text[doto]; /* Scrunch text. */
- cp2 = cp1 + chunk;
- if (kflag == KFORW)
- {
- while (ksize - kused < chunk)
- if (kgrow(FALSE) == FALSE)
- return FALSE;
- bcopy(cp1, &(kbufp[kused]), (size_t) chunk);
- kused += chunk;
- }
- else if (kflag == KBACK)
- {
- while (kstart < chunk)
- if (kgrow(TRUE) == FALSE)
- return FALSE;
- bcopy(cp1, &(kbufp[kstart-chunk]), (size_t) chunk);
- kstart -= chunk;
- }
- else if (kflag != KNONE)
- panic("broken ldelete call");
- while (cp2 != &dotp->l_text[dotp->l_used])
- *cp1++ = *cp2++;
- dotp->l_used -= (int)chunk;
- changelineflag(dotp, TRUE);
- for(wp = wheadp; wp != NULL; wp = wp->w_wndp)
- {
- if (wp->w_dotp==dotp && wp->w_doto>=doto)
- {
- /*NOSTRICT*/
- wp->w_doto -= (short)chunk;
- if (wp->w_doto < doto)
- wp->w_doto = doto;
- }
- if (wp->w_markp==dotp && wp->w_marko>=doto)
- {
- /*NOSTRICT*/
- wp->w_marko -= (short)chunk;
- if (wp->w_marko < doto)
- wp->w_marko = doto;
- }
- }
- n -= chunk;
- }
- return TRUE;
- }
-
- /*
- * Delete a newline. Join the current line
- * with the next line. If the next line is the magic
- * header line always return TRUE; merging the last line
- * with the header line can be thought of as always being a
- * successful operation, even if nothing is done, and this makes
- * the kill buffer work "right". Easy cases can be done by
- * shuffling data around. Hard cases require that lines be moved
- * about in memory. Return FALSE on error and TRUE if all
- * looks ok.
- */
- ldelnewline()
- {
- register LINE *lp1;
- register LINE *lp2;
- register EWINDOW *wp;
- LINE *lp3;
-
- if (curbp->b_flag & BFVIEW)
- {
- ttbeep();
- return FALSE;
- }
-
- lp1 = curwp->w_dotp;
- lp2 = lforw(lp1);
- if (lp2 == curbp->b_linep) /* At the buffer end. */
- return TRUE;
-
- /* Case of current line has room to just add stuff
- * to the end.
- */
- if (lp2->l_used <= lp1->l_size - lp1->l_used)
- {
- changelineflag(lp1, TRUE);
- bcopy(&lp2->l_text[0], &lp1->l_text[lp1->l_used], (size_t)lp2->l_used);
- for(wp = wheadp; wp != NULL; wp = wp->w_wndp)
- {
- if (wp->w_linep == lp2)
- wp->w_linep = lp1;
- if (wp->w_dotp == lp2)
- {
- wp->w_dotp = lp1;
- wp->w_doto += lp1->l_used;
- }
- if (wp->w_markp == lp2)
- {
- wp->w_markp = lp1;
- wp->w_marko += lp1->l_used;
- }
- }
- lp1->l_used += lp2->l_used;
- lp1->l_fp = lp2->l_fp;
- lp2->l_fp->l_bp = lp1;
- free((char *) lp2);
- return TRUE;
- }
-
- if ((lp3=lalloc(lp1->l_used + lp2->l_used)) == NULL)
- return FALSE;
-
- bcopy(&lp1->l_text[0], &lp3->l_text[0], (size_t)lp1->l_used);
- bcopy(&lp2->l_text[0], &lp3->l_text[lp1->l_used], (size_t)lp2->l_used);
-
- if ((lp1->l_used >= 1) && (lp2->l_used >= 1))
- changelineflag(lp3, TRUE); /* non-empty lines joined */
-
- lp1->l_bp->l_fp = lp3;
- lp3->l_fp = lp2->l_fp;
- lp2->l_fp->l_bp = lp3;
- lp3->l_bp = lp1->l_bp;
- for(wp = wheadp; wp != NULL; wp = wp->w_wndp)
- {
- if (wp->w_linep==lp1 || wp->w_linep==lp2)
- wp->w_linep = lp3;
- if (wp->w_dotp == lp1)
- wp->w_dotp = lp3;
- else if (wp->w_dotp == lp2)
- {
- wp->w_dotp = lp3;
- wp->w_doto += lp1->l_used;
- }
- if (wp->w_markp == lp1)
- wp->w_markp = lp3;
- else if (wp->w_markp == lp2)
- {
- wp->w_markp = lp3;
- wp->w_marko += lp1->l_used;
- }
- }
- free((char *) lp1);
- free((char *) lp2);
- return TRUE;
- }
-
- /*
- * Replace plen characters before dot with argument string.
- * Control-J characters in st are interpreted as newlines.
- * There is a casehack disable flag (normally it likes to match
- * case of replacement to what was there).
- */
- lreplace(plen, st, f)
- register RSIZE plen; /* length to remove */
- char *st; /* replacement string */
- int f; /* case hack disable */
- {
- register RSIZE rlen; /* replacement length */
- register int rtype; /* capitalization */
- register int c; /* used for random characters */
- register int doto; /* offset into line */
-
- if (curbp->b_flag & BFVIEW)
- {
- ttbeep();
- return FALSE;
- }
-
- /*
- * Find the capitalization of the word that was found.
- * f says use exact case of replacement string (same thing that
- * happens with lowercase found), so bypass check.
- */
- /*NOSTRICT*/
- (VOID) backchar(FFARG | FFRAND, (int) plen);
- rtype = _L;
- c = lgetc(curwp->w_dotp, curwp->w_doto);
- if (ISUPPER(c)!=FALSE && f==FALSE)
- {
- rtype = _U|_L;
- if (curwp->w_doto+1 < llength(curwp->w_dotp))
- {
- c = lgetc(curwp->w_dotp, curwp->w_doto+1);
- if (ISUPPER(c) != FALSE)
- {
- rtype = _U;
- }
- }
- }
-
- /*
- * make the string lengths match (either pad the line
- * so that it will fit, or scrunch out the excess).
- * be careful with dot's offset.
- */
- rlen = strlen(st);
- doto = curwp->w_doto;
- if (plen > rlen)
- (VOID) ldelete((RSIZE) (plen-rlen), KNONE);
- else if (plen < rlen)
- {
- if (linsert((int)(rlen-plen), ' ') == FALSE)
- return FALSE;
- }
- curwp->w_doto = doto;
-
- /*
- * do the replacement: If was capital, then place first
- * char as if upper, and subsequent chars as if lower.
- * If inserting upper, check replacement for case.
- */
- while ((c = CHARMASK(*st++)) != '\0')
- {
- if ((rtype&_U)!=0 && ISLOWER(c)!=0)
- c = TOUPPER(c);
- if (rtype == (_U|_L))
- rtype = _L;
- if (c == CCHR('J'))
- {
- if (curwp->w_doto == llength(curwp->w_dotp))
- (VOID) forwchar(FFRAND, 1);
- else
- {
- if (ldelete((RSIZE) 1, KNONE) != FALSE)
- (VOID) lnewline();
- }
- }
- else if (curwp->w_dotp == curbp->b_linep)
- {
- (VOID) linsert(1, c);
- }
- else if (curwp->w_doto == llength(curwp->w_dotp))
- {
- if (ldelete((RSIZE) 1, KNONE) != FALSE)
- (VOID) linsert(1, c);
- }
- else
- {
- changelineflag(curwp->w_dotp, TRUE);
- lputc(curwp->w_dotp, curwp->w_doto++, (char)c);
- }
- }
- lchange(WFHARD);
- return (TRUE);
- }
-
- /*
- * Delete all of the text
- * saved in the kill buffer. Called by commands
- * when a new kill context is being created. The kill
- * buffer array is released, just in case the buffer has
- * grown to immense size. No errors.
- */
- VOID kdelete()
- {
- if (kbufp != NULL)
- {
- ewprintf(garbage);
- free((char *) kbufp);
- kbufp = NULL;
- kstart = kused = ksize = 0;
- }
- }
-
- /*
- * Insert a character to the kill buffer,
- * enlarging the buffer if there isn't any room. Always
- * grow the buffer in chunks, on the assumption that if you
- * put something in the kill buffer you are going to put
- * more stuff there too later. Return TRUE if all is
- * well, and FALSE on errors. Print a message on
- * errors. Dir says whether to put it at back or front.
- */
- kinsert(c, dir)
- int c, dir;
- {
- if (kused == ksize && dir == KFORW && kgrow(FALSE) == FALSE)
- return FALSE;
- if (kstart == 0 && dir == KBACK && kgrow(TRUE) == FALSE)
- return FALSE;
- if (dir == KFORW)
- kbufp[kused++] = (char)c;
- else if (dir == KBACK)
- kbufp[--kstart] = (char)c;
- else
- panic("Broken kinsert call"); /* Oh shit! */
- return (TRUE);
- }
-
- /*
- * kgrow - just get more kill buffer for the callee. back is true if
- * we are trying to get space at the beginning of the kill buffer.
- */
- kgrow(back)
- int back;
- {
- register RSIZE nstart;
- register char *nbufp;
-
- if ((unsigned)(ksize+KBLOCK) <= (unsigned)ksize)
- {
- /* probably 16 bit unsigned; one of these days I
- * will fix this to be a linked-list of lines (JAM)
- */
- ewprintf("Kill buffer size at maximum.");
- return FALSE;
- }
- if ((nbufp=malloc((unsigned)(ksize+KBLOCK))) == NULL)
- {
- ewprintf(Nobytes, (long)(ksize+KBLOCK));
- return FALSE;
- }
- nstart = (back == TRUE) ? (kstart + KBLOCK) : (KBLOCK / 4) ;
-
- bcopy(&(kbufp[kstart]), &(nbufp[nstart]), (size_t)(kused-kstart));
-
- if (kbufp != NULL)
- free((char *) kbufp);
- kbufp = nbufp;
- ksize += KBLOCK;
- kused = kused - kstart + nstart;
- kstart = nstart;
- return TRUE;
- }
-
- char *killbufstart()
- {
- return(&kbufp[kstart]);
- }
- /*
- * This function gets characters from
- * the kill buffer. If the character index "n" is
- * off the end, it returns "-1". This lets the caller
- * just scan along until it gets a "-1" back.
- */
- kremove(n)
- RSIZE n;
- {
- if (n < 0 || n + kstart >= kused)
- return -1;
- return CHARMASK(kbufp[n + kstart]);
- }
- /* insert a line at dotp of curbp
- */
- int lineinsert(s, nl)
- char *s;
- BOOL nl;
- {
- while (*s)
- if (!linsert(1, *s++))
- return(FALSE);
- if (nl)
- return(lnewline());
- else
- return (TRUE);
- }
-
- #ifdef DEBUG
- void changelineflag(lp, flag)
- LINE *lp;
- BOOL flag;
- {
- if (flag)
- lp->l_flag |= LFCHANGE;
- else
- lp->l_flag &= ~LFCHANGE;
- }
- #endif
-