home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Black Box 4
/
BlackBox.cdr
/
progc
/
ce331src.arj
/
LINE.C
< prev
next >
Wrap
C/C++ Source or Header
|
1991-03-10
|
19KB
|
850 lines
/*
* L I N E . C
*
* purpose:
* 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 (but aren't here for any specific 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 currently displayed, which means that "b_nwnd" is
* non-zero, which means that the dot and mark values in the buffer
* headers are nonsense.
*/
#include <stdio.h> /* Turbo C++ verison 1.0 */
#include <alloc.h>
#include <dos.h>
#include <pdefs.h> /* PforCe version 1.05 (includes jwg.h) */
#include <pf_ansi.h>
#include "struct.h"
#include "def.h"
#include "protos.h"
#define BSIZE(a) (a+NBLOCK-1)&(~(NBLOCK-1))
/*
* local function prototypes
*/
static int near ldelnewline(void);
static int near lowrite(char);
/*
* local (module global) variable declarations
*/
static KILL *ykbuf; /* ptr to current kill buffer chunk being yanked */
static int ykboff; /* offset into that chunk */
/**
* This routine allocates a block of memory large enough to hold a LINE
* containing "used" characters. Return a pointer to the new block, or
* NULL if there isn't any memory left.
*/
LINE * LAlloc(used)
int used;
{
LINE *lp;
if ((lp = (LINE *)malloc(sizeof(LINE) + used)) == NULLLPTR)
{
OutOfMem();
return(NULLLPTR);
}
lp->l_size = used;
lp->l_used = used;
return(lp);
}
/**
* Delete line "lp". Fix all of the links that might point to it (they are
* moved to offset 0 of the next line). Unlink the line from whatever buffer
* it might be in. Release the memory. Update the buffers.
*/
int LFree(lp)
LINE *lp;
{
WINDOW *wp;
BUFFER *bp;
#if BMARKS
int bmark;
#endif
wp = wheadp;
while (wp != NULLWPTR)
{
if (wp->w_linep == lp)
wp->w_linep = lp->l_fp;
if (wp->w_dotp == lp)
{
wp->w_dotp = lp->l_fp;
wp->w_doto = 0;
}
if (wp->w_markp == lp)
{
wp->w_markp = lp->l_fp;
wp->w_marko = 0;
}
if (wp->w_anchorp == lp)
{
wp->w_anchorp = lp->l_fp;
wp->w_anchoro = 0;
}
#if BMARKS
for (bmark = 0; bmark < NBMARKS; bmark++)
{
if (wp->w_bmarkp[bmark] == lp)
{
wp->w_bmarkp[bmark] = lp->l_fp;
wp->w_bmarko[bmark] = 0;
}
}
#endif
wp = wp->w_wndp;
}
bp = bheadp;
while (bp != NULLBPTR)
{
if (bp->b_nwnd == 0)
{
if (bp->b_dotp == lp)
{
bp->b_dotp = lp->l_fp;
bp->b_doto = 0;
}
if (bp->b_markp == lp)
{
bp->b_markp = lp->l_fp;
bp->b_marko = 0;
}
if (bp->b_anchorp == lp)
{
bp->b_anchorp = lp->l_fp;
bp->b_anchoro = 0;
}
#if BMARKS
for (bmark = 0; bmark < NBMARKS; bmark++)
{
if (bp->b_bmarkp[bmark] == lp)
{
bp->b_bmarkp[bmark] = lp->l_fp;
bp->b_bmarko[bmark] = 0;
}
}
#endif
}
bp = bp->b_fp;
}
lp->l_bp->l_fp = lp->l_fp;
lp->l_fp->l_bp = lp->l_bp;
free((char *)lp);
return(TRUE);
}
/**
* 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).
*/
int LChange(flag)
int flag;
{
WINDOW *wp;
if (curbp->b_nwnd != 1) /* ensure hard */
flag = WFHARD;
if ((curbp->b_flag & BFCHG) == 0) /* first change, so */
{
flag |= WFMODE; /* Update mode lines */
curbp->b_flag |= BFCHG;
}
wp = wheadp;
while (wp != NULLWPTR)
{
if (wp->w_bufp == curbp)
wp->w_flag |= flag;
wp = wp->w_wndp;
}
return(TRUE);
}
/**
* Insert spaces forward into text
*/
int InsSpace(f, n)
int f, n;
{
LInsert(n, ' ');
BackChar(f, n);
return(TRUE);
}
/**
* Insert a string at the current point
*/
int LInstr(instr)
char *instr;
{
int status = TRUE;
char tmpc;
if (instr != NULLCPTR)
{
while (((tmpc = *instr) != NULL) && status == TRUE)
{
status = (tmpc == '\n' ? LNewline() : LInsert(1, tmpc));
/* insertion error? */
if (status != TRUE)
{
OutOfMem();
break;
}
instr++;
}
}
return(status);
}
/**
* 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. You always Update dot in the current window. You Update
* mark and 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.
*/
int LInsert(n, c)
int n;
int c;
{
char *cp1;
char *cp2;
LINE *lp1;
LINE *lp2;
LINE *lp3;
short doto;
int i;
WINDOW *wp;
#if BMARKS
int bmark;
#endif
if (curbp->b_mode & MDVIEW) /* don't allow this command if */
return(ReadOnly()); /* we are in read only mode */
LChange(WFEDIT);
lp1 = curwp->w_dotp; /* current line */
if (lp1 == curbp->b_linep) /* at the end (special) */
{
if (curwp->w_doto != 0)
return(FALSE);
if ((lp2 = LAlloc(BSIZE(n))) == NULLLPTR) /* allocate new line */
return(FALSE);
lp2->l_used = n;
lp3 = lp1->l_bp; /* previous line */
lp3->l_fp = lp2; /* link in */
lp2->l_fp = lp1;
lp1->l_bp = lp2;
lp2->l_bp = lp3;
for (i = 0; i < n; ++i)
lp2->l_text[i] = (char)c;
curwp->w_dotp = lp2;
curwp->w_doto = n;
return(TRUE);
}
doto = curwp->w_doto; /* save for later */
if (lp1->l_used + n > lp1->l_size) /* hard: reallocate */
{
if ((lp2 = LAlloc(BSIZE(lp1->l_used + n))) == NULLLPTR)
return(FALSE);
lp2->l_used = lp1->l_used + n;
cp1 = &lp1->l_text[0];
cp2 = &lp2->l_text[0];
while (cp1 != &lp1->l_text[doto])
*cp2++ = *cp1++;
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 */
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 */
lp2->l_text[doto + i] = (char)c;
wp = wheadp; /* Update windows */
while (wp != NULLWPTR)
{
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)
wp->w_doto += n;
}
if (wp->w_markp == lp1)
{
wp->w_markp = lp2;
if (wp->w_marko > doto)
wp->w_marko += n;
}
if (wp->w_anchorp == lp1)
{
wp->w_anchorp = lp2;
if (wp->w_anchoro > doto)
wp->w_anchoro += n;
}
#if BMARKS
for (bmark = 0; bmark < NBMARKS; bmark++)
{
if (wp->w_bmarkp[bmark] == lp1)
{
wp->w_bmarkp[bmark] = lp2;
if (wp->w_bmarko[bmark] > doto)
wp->w_bmarko[bmark] += n;
}
}
#endif
wp = wp->w_wndp;
}
return(TRUE);
}
/**
* Overwrite a character in the current line at the current position
*/
static int near lowrite(c)
char c; /* character to overwrite */
{
if (
curwp->w_doto < curwp->w_dotp->l_used
&&
(
lgetc(curwp->w_dotp, curwp->w_doto) != '\t'
||
curwp->w_doto % tabsize == tabsize - 1
)
)
LDelete(1L, FALSE);
return(LInsert(1, c));
}
/**
* Overwrite a string at the current point
*/
int LOver(ostr)
char *ostr;
{
int status = TRUE;
char tmpc;
if (ostr != NULLCPTR)
{
while (((tmpc = *ostr) != NULL) && status == TRUE)
{
status = (tmpc == '\n' ? LNewline() : lowrite(tmpc));
/* insertion error? */
if (status != TRUE)
{
OutOfMem();
break;
}
ostr++;
}
}
return(status);
}
/**
* Insert a NewLine into the buffer at the current location of dot in the
* current window. The funny ass-backwards way it does things is not a botch;
* it just makes the last line in the file not a special case. Return TRUE if
* everything works out and FALSE on errors (memory allocation failure). The
* Update of dot and mark is a bit easier then in the above case, because the
* split forces more updating.
*/
int LNewline()
{
char *cp1;
char *cp2;
LINE *lp1;
LINE *lp2;
short doto;
WINDOW *wp;
#if BMARKS
int bmark;
#endif
if (curbp->b_mode & MDVIEW) /* don't allow this command if */
return(ReadOnly()); /* we are in read only mode */
LChange(WFHARD);
lp1 = curwp->w_dotp; /* get the address and */
doto = curwp->w_doto; /* offset of "." */
if ((lp2 = LAlloc(doto)) == NULLLPTR) /* new first half line */
return(FALSE);
cp1 = &lp1->l_text[0]; /* shuffle text around */
cp2 = &lp2->l_text[0];
while (cp1 != &lp1->l_text[doto])
*cp2++ = *cp1++;
cp2 = &lp1->l_text[0];
while (cp1 != &lp1->l_text[lp1->l_used])
*cp2++ = *cp1++;
lp1->l_used -= doto;
lp2->l_bp = lp1->l_bp;
lp1->l_bp = lp2;
lp2->l_bp->l_fp = lp2;
lp2->l_fp = lp1;
wp = wheadp; /* windows */
while (wp != NULLWPTR)
{
if (wp->w_linep == lp1)
wp->w_linep = lp2;
if (wp->w_dotp == lp1)
{
if (wp->w_doto < doto)
wp->w_dotp = lp2;
else
wp->w_doto -= doto;
}
if (wp->w_markp == lp1)
{
if (wp->w_marko < doto)
wp->w_markp = lp2;
else
wp->w_marko -= doto;
}
if (wp->w_anchorp == lp1)
{
if (wp->w_anchoro < doto)
wp->w_anchorp = lp2;
else
wp->w_anchoro -= doto;
}
#if BMARKS
for (bmark = 0; bmark < NBMARKS; bmark++)
{
if (wp->w_bmarkp[bmark] == lp1)
{
if (wp->w_bmarko[bmark] < doto)
wp->w_bmarkp[bmark] = lp2;
else
wp->w_bmarko[bmark] -= doto;
}
}
#endif
wp = wp->w_wndp;
}
return(TRUE);
}
/**
* This function deletes "n" bytes, starting at dot. It understands how to 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" is TRUE if the text should be put in the kill buffer.
*/
int LDelete(n, kflag)
long n; /* # of chars to delete */
int kflag; /* put killed text in kill buffer flag */
{
char *cp1;
char *cp2;
LINE *dotp;
short doto;
int chunk;
WINDOW *wp;
#if BMARKS
int bmark;
#endif
if (curbp->b_mode & MDVIEW) /* don't allow this command if */
return(ReadOnly()); /* we are in read only mode */
while (n != 0L)
{
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 ((long)chunk > n)
chunk = (int)n;
if (chunk == 0) /* end of line, merge */
{
LChange(WFHARD);
if (
ldelnewline() == FALSE
||
(
kflag != FALSE
&&
KInsert('\n') == FALSE
)
)
return(FALSE);
--n;
continue;
}
LChange(WFEDIT);
cp1 = &dotp->l_text[doto]; /* scrunch text */
cp2 = cp1 + chunk;
if (kflag != FALSE) /* kill? */
{
while (cp1 != cp2)
{
if (KInsert(*cp1) == FALSE)
return(FALSE);
++cp1;
}
cp1 = &dotp->l_text[doto];
}
while (cp2 != &dotp->l_text[dotp->l_used])
*cp1++ = *cp2++;
dotp->l_used -= chunk;
wp = wheadp; /* fix windows */
while (wp != NULLWPTR)
{
if (wp->w_dotp == dotp && wp->w_doto >= doto)
{
wp->w_doto -= chunk;
if (wp->w_doto < doto)
wp->w_doto = doto;
}
if (wp->w_markp == dotp && wp->w_marko >= doto)
{
wp->w_marko -= chunk;
if (wp->w_marko < doto)
wp->w_marko = doto;
}
if (wp->w_anchorp == dotp && wp->w_anchoro >= doto)
{
wp->w_anchoro -= chunk;
if (wp->w_anchoro < doto)
wp->w_anchoro = doto;
}
#if BMARKS
for (bmark = 0; bmark < NBMARKS; bmark++)
{
if (wp->w_bmarkp[bmark] == dotp && wp->w_bmarko[bmark] >= doto)
{
wp->w_bmarko[bmark] -= chunk;
if (wp->w_bmarko[bmark] < doto)
wp->w_bmarko[bmark] = doto;
}
}
#endif
wp = wp->w_wndp;
}
n -= (long)chunk;
}
return(TRUE);
}
/**
* Grab and return a string with the text of the current line
*/
char * GetCText()
{
LINE *lp; /* line to copy */
int size; /* length of line to return */
char *sp; /* string pointer into line */
char *dp; /* string pointer into returned line */
static char rline[NSTRING]; /* line to return */
/* find the contents of the current line and its length */
lp = curwp->w_dotp;
sp = lp->l_text;
size = lp->l_used;
if (size >= NSTRING)
size = NSTRING - 1;
/* copy it across */
dp = rline;
while (size--)
*dp++ = *sp++;
*dp = 0;
return(rline);
}
/**
* Replace the current line with the passed text
*/
int PutCText(iline)
char *iline; /* contents of new line */
{
int status;
/* delete the current line (from the beginning) */
curwp->w_doto = 0;
if (!KillText(TRUE, 1))
return(FALSE);
/* insert the new line */
if (!LInstr(iline))
return(FALSE);
status = LNewline();
BackLine(TRUE, 1);
return(status);
}
/**
* 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. Called by
* "LDelete" only, so we don't need to check for MDVIEW.
*/
static int near ldelnewline()
{
char *cp1;
char *cp2;
LINE *lp1;
LINE *lp2;
LINE *lp3;
WINDOW *wp;
#if BMARKS
int bmark;
#endif
lp1 = curwp->w_dotp;
lp2 = lp1->l_fp;
if (lp2 == curbp->b_linep) /* at the buffer end */
{
if (lp1->l_used == 0) /* blank line */
LFree(lp1);
return(TRUE);
}
if (lp2->l_used <= lp1->l_size - lp1->l_used)
{
cp1 = &lp1->l_text[lp1->l_used];
cp2 = &lp2->l_text[0];
while (cp2 != &lp2->l_text[lp2->l_used])
*cp1++ = *cp2++;
wp = wheadp;
while (wp != NULLWPTR)
{
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;
}
if (wp->w_anchorp == lp2)
{
wp->w_anchorp = lp1;
wp->w_anchoro += lp1->l_used;
}
#if BMARKS
for (bmark = 0; bmark < NBMARKS; bmark++)
{
if (wp->w_bmarkp[bmark] == lp2)
{
wp->w_bmarkp[bmark] = lp1;
wp->w_bmarko[bmark] += lp1->l_used;
}
}
#endif
wp = wp->w_wndp;
}
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)) == NULLLPTR)
return(FALSE);
cp1 = &lp1->l_text[0];
cp2 = &lp3->l_text[0];
while (cp1 != &lp1->l_text[lp1->l_used])
*cp2++ = *cp1++;
cp1 = &lp2->l_text[0];
while (cp1 != &lp2->l_text[lp2->l_used])
*cp2++ = *cp1++;
lp1->l_bp->l_fp = lp3;
lp3->l_fp = lp2->l_fp;
lp2->l_fp->l_bp = lp3;
lp3->l_bp = lp1->l_bp;
wp = wheadp;
while (wp != NULLWPTR)
{
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;
}
if (wp->w_anchorp == lp1)
wp->w_anchorp = lp3;
else if (wp->w_anchorp == lp2)
{
wp->w_anchorp = lp3;
wp->w_anchoro += lp1->l_used;
}
#if BMARKS
for (bmark = 0; bmark < NBMARKS; bmark++)
{
if (wp->w_bmarkp[bmark] == lp1)
wp->w_bmarkp[bmark] = lp3;
else if (wp->w_bmarkp[bmark] == lp2)
{
wp->w_bmarkp[bmark] = lp3;
wp->w_bmarko[bmark] += lp1->l_used;
}
}
#endif
wp = wp->w_wndp;
}
free((char *)lp1);
free((char *)lp2);
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.
*/
int KDelete()
{
KILL *kp; /* ptr to scan kill buffer chunk list */
if (kbufh != NULLKPTR)
{
/* first, delete all the chunks */
kbufp = kbufh;
while (kbufp != NULLKPTR)
{
kp = kbufp->d_next;
free(kbufp);
kbufp = kp;
}
/* and reset all the kill buffer pointers */
kbufh = kbufp = NULLKPTR;
kused = KBLOCK; /* force malloc of new KILL block */
}
return(TRUE);
}
/**
* Insert a character to the kill buffer, allocating new chunks as needed.
* Return TRUE if all is well, and FALSE on errors.
*/
int KInsert(c)
int c; /* character to insert in the kill buffer */
{
KILL *nchunk; /* ptr to newly malloc'd chunk */
/* check to see if we need a new chunk */
if (kused >= KBLOCK)
{
if ((nchunk = (KILL *)malloc(sizeof(KILL))) == NULLKPTR)
return(FALSE);
if (kbufh == NULLKPTR) /* set head ptr if first time */
kbufh = nchunk;
if (kbufp != NULLKPTR) /* point the current to this new one */
kbufp->d_next = nchunk;
kbufp = nchunk;
kbufp->d_next = NULLKPTR;
kused = 0;
}
/* and now insert the character */
kbufp->d_chunk[kused++] = (char)c;
return(TRUE);
}
/**
* Yank text back from the kill buffer. This is really easy. All of the work
* is done by the standard insert routines. All you do is run the loop, and
* check for errors.
*/
int Yank(f, n)
int f;
int n;
{
int c;
int i;
char *sp; /* pointer into string to insert */
KILL *kp; /* pointer into kill buffer */
if (curbp->b_mode & MDVIEW) /* don't allow this command if */
return(ReadOnly()); /* we are in read only mode */
if (n < 0)
return(FALSE);
/* make sure there is something to Yank */
if (kbufh == NULLKPTR)
return(TRUE); /* not an error, just nothing */
/* for each time */
while (n--)
{
kp = kbufh;
while (kp != NULLKPTR)
{
if (kp->d_next == NULLKPTR)
i = kused; /* last KILL block, so only 'kused' full */
else
i = KBLOCK; /* not the last, so it must be full */
sp = kp->d_chunk;
while (i--)
{
if ((c = *sp++) == '\n')
{
if (LNewline() == FALSE)
return(FALSE);
}
else
{
if (LInsert(1, c) == FALSE)
return(FALSE);
}
}
kp = kp->d_next;
}
}
return(TRUE);
}