
/*
 * EDIT.C
 * (c) 1992 J.Harper
 */

#include "jed.h"
#include "jed_protos.h"

Prototype   BOOL    clearlinelist	(TX *);
Prototype   VOID    killlinelist	(TX *);
Prototype   VOID    killsomelines	(TX *, LONG, LONG);
Prototype   LINE *  resizelinelist	(TX *, LONG, LONG);
Prototype   BOOL    stuffline		(STRPTR, LONG);
Prototype   BOOL    insertstr		(STRPTR);
Prototype   BOOL    insertstrn		(STRPTR, WORD);
Prototype   BOOL    insertstring	(STRPTR, LONG);
Prototype   BOOL    insertsection	(STRPTR);
Prototype   BOOL    deletechars		(WORD);
Prototype   BOOL    deletelines		(LONG);
Prototype   BOOL    deletesection	(POS *);
Prototype   BOOL    splitline		(VOID);
Prototype   BOOL    joinlines		(VOID);
Prototype   BOOL    wrapline		(VOID);
Prototype   BOOL    padcursor		(VOID);
Prototype   VOID    orderpos		(POS *, POS *);
Prototype   BOOL    getsection		(STRPTR, POS *);
Prototype   LONG    sectionlength	(POS *, POS *);
Prototype   VOID    copysection		(POS *, POS *, STRPTR);
Prototype   BOOL    posinblock		(WORD, LONG);
Prototype   BOOL    cursinblock		(VOID);
Prototype   BOOL    pageinblock		(VOID);
Prototype   WORD    lineinblock		(LONG);
Prototype   VOID    orderblock		(VOID);
Prototype   VOID    setblockrefresh	(VOID);
Prototype   LONG    offset		(VOID);
Prototype   LONG    size		(VOID);

#define allocll(n)   AllocVec(sizeof(LINE) * (n), 0L)
#define freell(l)    FreeVec(l)

/*
 * This macro copies a line list (or part of one).
 * d = destination
 * s = source
 * n = number of LINE's to copy.
 */
#define movll(d,s,n) CopyMemQuick((ULONG *)(s), (ULONG *)(d), (n) << 3)

/*
 * Makes file empty (null string in first line)
 */
BOOL
clearlinelist(TX *tx)
{
    if(tx->tx_Lines)
	killlinelist(tx);
    if(tx->tx_Lines = allocll(1));
    {
	if(tx->tx_Lines[0].ln_Line = savestring(""))
	    tx->tx_Lines[0].ln_Strlen = 1;
	else
	    tx->tx_Lines[0].ln_Strlen = 0;
	tx->tx_NumLines = 1;
	return(TRUE);
    }
    return(FALSE);
}

/*
 * deallocates all lines and their list
 */
VOID
killlinelist(TX *tx)
{
    if(tx->tx_Lines)
    {
	LINE *line;
	LONG i;
	for(i = 0, line = tx->tx_Lines; i < tx->tx_NumLines; i++, line++)
	{
	    if(line->ln_Strlen)
		FreeVec(line->ln_Line);
	}
	freell(tx->tx_Lines);
	tx->tx_Lines = NULL;
	tx->tx_NumLines = 0;
    }
    killmarklist(tx);
}

/*
 * deallocates some lines (but not the list)
 */
VOID
killsomelines(TX *tx, LONG start, LONG number)
{
    LINE *line = tx->tx_Lines + start;
    LONG i;

    for(i = 0; i < number; i++, line++)
    {
	if(line->ln_Strlen)
	{
	    FreeVec(line->ln_Line);
	    line->ln_Strlen = 0;
	    line->ln_Line = NULL;
	}
    }
}

/*
 * Creates blank entries or removes existing lines starting from line 'where'
 * 'change' is the number of lines to insert, negative numbers mean delete
 * that number of lines starting at the cursor line.
 * If lines are deleted the actual text is also deleted.
 */
LINE *
resizelinelist(TX *tx, LONG change, LONG where)
{
    LINE *newlines;
    LONG newsize = tx->tx_NumLines + change;

    if((newsize >= 0) && (newlines = allocll(newsize)))
    {
	if(tx->tx_Lines)
	{
	    if(change > 0)
	    {
		movll(newlines, tx->tx_Lines, where);
		movll(newlines + where + change, tx->tx_Lines + where, tx->tx_NumLines - where);
	    }
	    else
	    {
		movll(newlines, tx->tx_Lines, where);
		movll(newlines + where, tx->tx_Lines + where - change, tx->tx_NumLines - where + change);
		killsomelines(tx, where, -change);
	    }
	    freell(tx->tx_Lines);
	}
	if(change > 0)
	    bzero(newlines + where, change * sizeof(LINE));
	tx->tx_Lines = newlines;
	tx->tx_NumLines = newsize;
	return(newlines);
    }
    return(FALSE);
}

/*
 * Pastes a line into the current view at line num.
 * a LINE should have been made if it is wanted.
 * text is not copied, it should have been AllocVec()'ed (or savestring()'ed)
 */
BOOL
stuffline(STRPTR text, LONG lineNum)
{
    LINE *line = CurrVW->vw_Tx->tx_Lines + lineNum;

    if(line->ln_Strlen)
	FreeVec(line->ln_Line);

    line->ln_Strlen = strlen(text) + 1;
    line->ln_Line = text;
    return(TRUE);
}

/*
 * Inserts a string into the current line at the cursor pos
 */
BOOL
insertstr(STRPTR text)
{
    return(insertstrn(text, strlen(text)));
}

BOOL
insertstrn(STRPTR text, WORD textLen)
{
    VW *vw = CurrVW;
    TX *tx = vw->vw_Tx;
    LINE *line = tx->tx_Lines + vw->vw_CursorPos.pos_Line;
    STRPTR newline;

    if(newline = AllocVec(textLen + line->ln_Strlen, 0L))
    {
	if(line->ln_Strlen)
	{
	    memcpy(newline, line->ln_Line, vw->vw_CursorPos.pos_Col);
	    memcpy(newline + vw->vw_CursorPos.pos_Col, text, textLen);
	    strcpy(newline + vw->vw_CursorPos.pos_Col + textLen, line->ln_Line + vw->vw_CursorPos.pos_Col);
	}
	else
	{
	    memcpy(newline, text, textLen);
	    newline[textLen] = 0;
	}
	stuffline(newline, vw->vw_CursorPos.pos_Line);
	keepposaddx(textLen);
	return(TRUE);
    }
    return(FALSE);
}

/*
 * Inserts a null teminated string, this routine acts on any '\n' or '\t'
 * characters that it finds.
 * I expect that this routine will be incredibly slow.
 */
BOOL
insertstring(STRPTR text, LONG tabSize)
{
    STRPTR buff = AllocVec(1024, 0L);
    if(buff)
    {
	VW *vw = CurrVW;
	STRPTR str = text;
	setrefresh(RFF_LINEFROM, vw->vw_CursorPos.pos_Col, vw->vw_CursorPos.pos_Line);
	while(*str)
	{
	    UBYTE c;
	    LONG i = 0;
	    WORD startx = vw->vw_CursorPos.pos_Col;

	    while((c = *str++) && (c != '\n') && (i < 1024))
	    {
		if(c == '\t')
		{
		    WORD numspc = tabSize - ((i + startx) % tabSize);
		    memcpy(buff + i, Spaces, numspc);
		    i += numspc;
		}
		else
		    buff[i++] = c;
	    }
	    buff[i] = 0;
	    if(c == '\n')
	    {
		vw->vw_RefreshType |= RFF_ALLFROM;
		if(vw->vw_CursorPos.pos_Col)
		{
		    if(!insertstrn(buff, i))
			goto abort;
		    if(!splitline())
			goto abort;
		}
		else
		{
		    STRPTR copy;
		    if(!resizelinelist(vw->vw_Tx, +1, vw->vw_CursorPos.pos_Line))
			goto abort;
		    if(!(copy = savestring(buff)))
			goto abort;
		    stuffline(copy, vw->vw_CursorPos.pos_Line);
		    keepposaddy(+1);
		}
		startx = 0;
	    }
	    else
	    {
		str--;
		if(!insertstrn(buff, i))
		{
abort:
		    settitle(NoMemMsg);
		    FreeVec(buff);
		    return(FALSE);
		}
	    }
	}
	FreeVec(buff);
	return(TRUE);
    }
    settitle(NoMemMsg);
    return(FALSE);
}

/*
 * Inserts a section from the text file into the current cursor position.
 * You can't insert into the text that is to be inserted.
 * Works out if the copied text is to be inserted before the cursor (if so
 * all line numbers have to recalculated after each line is inserted).
 */
BOOL
insertsection(STRPTR sectType)
{
    VW *vw = CurrVW;
    TX *tx = vw->vw_Tx;
    POS endpos, temp;
    POS startpos = vw->vw_CursorPos;

    if(getsection(sectType, &endpos))
    {
	BOOL copyback = FALSE;

	/* If some of the next code seems totally weird it's because the
	 * startpos and vw_CursorPos are actually swapped with each other
	 * for this part :-)
	 */
	if(!(
	    ((startpos.pos_Line < vw->vw_CursorPos.pos_Line) || (startpos.pos_Line > endpos.pos_Line)) ||
	    ((startpos.pos_Line == vw->vw_CursorPos.pos_Line) && (startpos.pos_Col < vw->vw_CursorPos.pos_Col)) ||
	    ((startpos.pos_Line == endpos.pos_Line) && (startpos.pos_Col >= endpos.pos_Col))
	)){
	    settitle("error: can't insert into self");
	    vw->vw_CursorPos = startpos;
	    return(FALSE);
	}

	/* They're switched back here.
	 */
	temp = vw->vw_CursorPos;
	vw->vw_CursorPos = startpos;
	startpos = temp;

	/* Test if we're copying to a line behind where we're copying from,
	 * this would really screw up the copy if not accounted for!
	 */
	if(vw->vw_CursorPos.pos_Line < startpos.pos_Line)
	    copyback = TRUE;

	if(startpos.pos_Line == endpos.pos_Line)
	{
	    setrefresh(RFF_LINEFROM, vw->vw_CursorPos.pos_Col, vw->vw_CursorPos.pos_Line);
	    return(insertstrn(tx->tx_Lines[startpos.pos_Line].ln_Line + startpos.pos_Col, endpos.pos_Col - startpos.pos_Col));
	}
	else
	{
	    LONG middle;
	    setrefresh(RFF_ALLFROM, vw->vw_CursorPos.pos_Col, vw->vw_CursorPos.pos_Line);
	    if(startpos.pos_Col || vw->vw_CursorPos.pos_Col)
	    {
		LINE *sline = tx->tx_Lines + startpos.pos_Line;
		if(!insertstrn(sline->ln_Line + startpos.pos_Col, sline->ln_Strlen - startpos.pos_Col - 1))
		    goto abort;
		if(!splitline())
		    goto abort;
		if(copyback)
		{
		    endpos.pos_Line++;
		    startpos.pos_Line += 2;
		}
		else
		    startpos.pos_Line++;
	    }
	    middle = endpos.pos_Line - startpos.pos_Line;
	    if(!resizelinelist(vw->vw_Tx, middle, vw->vw_CursorPos.pos_Line))
		goto abort;
	    if(copyback)
	    {
		endpos.pos_Line += middle;
		startpos.pos_Line += middle;
	    }
	    while(startpos.pos_Line < endpos.pos_Line)
	    {
		LINE *sline = tx->tx_Lines + startpos.pos_Line;
		LINE *dline = tx->tx_Lines + vw->vw_CursorPos.pos_Line;
		STRPTR copy = savestring(sline->ln_Line);
		if(!copy)
		    goto abort;
		dline->ln_Line = copy;
		dline->ln_Strlen = sline->ln_Strlen;
		keepposaddy(+1);
		startpos.pos_Line++;
	    }
	    if(endpos.pos_Col)
	    {
		if(!insertstrn(tx->tx_Lines[endpos.pos_Line].ln_Line, endpos.pos_Col))
		{
abort:
		    settitle(NoMemMsg);
		    return(FALSE);
		}
	    }
	}
	return(TRUE);
    }
    return(FALSE);
}

/*
 * Deletes some text from the cursor pos (this line only)
 */
BOOL
deletechars(WORD size)
{
    VW *vw = CurrVW;
    TX *tx = vw->vw_Tx;
    LINE *line = tx->tx_Lines + vw->vw_CursorPos.pos_Line;

    if(line->ln_Strlen)
    {
	STRPTR newline;

	if(size >= line->ln_Strlen)
	    size = line->ln_Strlen - 1;

	if(newline = AllocVec(line->ln_Strlen - size, 0L))
	{
	    strncpy(newline, line->ln_Line, vw->vw_CursorPos.pos_Col);
	    strcpy(newline + vw->vw_CursorPos.pos_Col, line->ln_Line + vw->vw_CursorPos.pos_Col + size);
	    FreeVec(line->ln_Line);
	    line->ln_Strlen -= size;
	    line->ln_Line = newline;

	    keeppossubx(size);
	    return(TRUE);
	}
	return(FALSE);
    }
    return(TRUE);
}

/*
 * deletes some lines from the cursor pos
 */
BOOL
deletelines(LONG size)
{
    VW *vw = CurrVW;
    BOOL rc;
    rc = (BOOL)resizelinelist(vw->vw_Tx, -size, vw->vw_CursorPos.pos_Line);
    keeppossuby(size);
    return(rc);
}

/*
 * Deletes from the cursor to endPos
 * sets refresh flags and resync*()'s text but doesn't call refresh().
 */
BOOL
deletesection(POS *endPos)
{
    VW *vw = CurrVW;
    TX *tx = vw->vw_Tx;
    POS oldcurs = vw->vw_CursorPos;
    if(endPos->pos_Line == vw->vw_CursorPos.pos_Line)
    {
	deletechars(endPos->pos_Col - vw->vw_CursorPos.pos_Col);
	setrefresh(RFF_LINEFROM, oldcurs.pos_Col, oldcurs.pos_Line);
	resyncx();
    }
    else
    {
	LONG middle;
	BOOL joinflag = FALSE;

	if(vw->vw_CursorPos.pos_Col)
	{
	    WORD start = tx->tx_Lines[vw->vw_CursorPos.pos_Line].ln_Strlen - vw->vw_CursorPos.pos_Col - 1;
	    if(start)
		deletechars(start);
	    vw->vw_CursorPos.pos_Col = 0;
	    vw->vw_CursorPos.pos_Line++;
	    joinflag = TRUE;
	}
	if(middle = endPos->pos_Line - vw->vw_CursorPos.pos_Line)
	{
	    deletelines(middle);
	    endPos->pos_Line -= middle;
	}
	if(endPos->pos_Col)
	    deletechars(endPos->pos_Col);
	if(joinflag && vw->vw_CursorPos.pos_Line)
	{
	    vw->vw_CursorPos.pos_Line--;
	    vw->vw_CursorPos.pos_Col = tx->tx_Lines[vw->vw_CursorPos.pos_Line].ln_Strlen - 1;
	    joinlines();
	}
	setrefresh(RFF_ALLFROM, oldcurs.pos_Col, oldcurs.pos_Line);
	resyncxy();
    }
    return(TRUE);
}

/*
 * splits the line into two at the cursor pos
 */
BOOL
splitline(VOID)
{
    VW *vw = CurrVW;
    TX *tx = vw->vw_Tx;

    if(resizelinelist(tx, +1, vw->vw_CursorPos.pos_Line + 1))
    {
	LINE *line = tx->tx_Lines + vw->vw_CursorPos.pos_Line;
	STRPTR newline1;
	if(newline1 = savestringn(line->ln_Line, vw->vw_CursorPos.pos_Col))
	{
	    STRPTR newline2;
	    if(newline2 = savestring(line->ln_Line + vw->vw_CursorPos.pos_Col))
	    {
		stuffline(newline1, vw->vw_CursorPos.pos_Line);
		stuffline(newline2, vw->vw_CursorPos.pos_Line + 1);
		keeppossplity();
		return(TRUE);
	    }
	    freestring(newline1);
	}
    }
    settitle(NoMemMsg);
    return(FALSE);
}

/*
 * joins the current line to the line below it.
 */
BOOL
joinlines(VOID)
{
    VW *vw = CurrVW;
    TX *tx = vw->vw_Tx;

    if((vw->vw_CursorPos.pos_Line + 1) < tx->tx_NumLines)
    {
	LINE *line1 = tx->tx_Lines + vw->vw_CursorPos.pos_Line;
	LINE *line2 = line1 + 1;
	STRPTR newstr;

	if(newstr = AllocVec(line1->ln_Strlen + line2->ln_Strlen - 1, 0L))
	{
	    stpcpy(stpcpy(newstr, line1->ln_Line), line2->ln_Line);
	    resizelinelist(tx, -1, vw->vw_CursorPos.pos_Line);
	    stuffline(newstr, vw->vw_CursorPos.pos_Line);
	    keepposjoiny();
	    return(TRUE);
	}
	settitle(NoMemMsg);
    }
    return(FALSE);
}

/*
 * Does a 'wordwrap'
 */
BOOL
wrapline(VOID)
{
    VW *vw = CurrVW;
    TX *tx = vw->vw_Tx;
    LINE *line = tx->tx_Lines + vw->vw_CursorPos.pos_Line;
    STRPTR txt = line->ln_Line + vw->vw_CursorPos.pos_Col;

    while(!isspace(*txt--))
    {
	if(txt == line->ln_Line - 1)
	    return(FALSE);
    }
    txt++;
    vw->vw_CursorPos.pos_Col = txt - line->ln_Line;
    setrefresh(RFF_ALLFROM, vw->vw_CursorPos.pos_Col, vw->vw_CursorPos.pos_Line);

    if(resizelinelist(tx, +1, vw->vw_CursorPos.pos_Line + 1))
    {
	LINE *line = tx->tx_Lines + vw->vw_CursorPos.pos_Line;
	STRPTR newline1;
	if(newline1 = savestringn(line->ln_Line, vw->vw_CursorPos.pos_Col))
	{
	    STRPTR newline2;
	    LONG newlen = line->ln_Strlen - vw->vw_CursorPos.pos_Col - 1;
	    LONG indlen = 0;
	    LONG movlen = vw->vw_Prefs.prf_RightMargin - vw->vw_CursorPos.pos_Col - 1;
	    if(vw->vw_Prefs.prf_AutoIndent)
	    {
		STRPTR spc = line->ln_Line;
		while(isspace(*spc++))
		    indlen++;
	    }
	    else
		indlen = vw->vw_Prefs.prf_LeftMargin;
	    movlen += indlen;
	    if(newline2 = AllocVec(newlen + indlen + 1, 0L))
	    {
		STRPTR spc = newline2;
		while(indlen--)
		    *spc++ = ' ';
		memcpy(spc, line->ln_Line + vw->vw_CursorPos.pos_Col + 1, newlen);

		stuffline(newline1, vw->vw_CursorPos.pos_Line);
		stuffline(newline2, vw->vw_CursorPos.pos_Line + 1);

		/* This next bit isn't too satisfactory.
		 */
		keeppossubx(line->ln_Strlen - vw->vw_CursorPos.pos_Col - 1);
		keeppossplity();
		if(movlen > 0)
		    keepposaddx(movlen);
		else
		    keeppossubx(-movlen);

		return(TRUE);
	    }
	    freestring(newline1);
	}
    }
    settitle(NoMemMsg);
    return(FALSE);
}

/*
 * Inserts spaces from end of line to cursor position (for free-form editing)
 */
BOOL
padcursor(VOID)
{
    VW *vw = CurrVW;
    TX *tx = vw->vw_Tx;
    LINE *line = tx->tx_Lines + vw->vw_CursorPos.pos_Line;

    if(vw->vw_LineUndo.line != vw->vw_CursorPos.pos_Line)
    {
	freestring(vw->vw_LineUndo.text);
	vw->vw_LineUndo.text = savestring(line->ln_Line);
	vw->vw_LineUndo.line = vw->vw_CursorPos.pos_Line;
	vw->vw_LineUndo.changes = tx->tx_Changes;
    }
    if(line->ln_Strlen < (vw->vw_CursorPos.pos_Col + 1))
    {
	STRPTR newline;
	if(newline = AllocVec(vw->vw_CursorPos.pos_Col + 1, 0L))
	{
	    WORD i;
	    strcpy(newline, line->ln_Line);
	    for(i = line->ln_Strlen - 1; i < vw->vw_CursorPos.pos_Col; i++)
		newline[i] = ' ';
	    newline[i] = 0;
	    stuffline(newline, vw->vw_CursorPos.pos_Line);
	    return(TRUE);
	}
	settitle(NoMemMsg);
	vw->vw_CursorPos.pos_Col = line->ln_Strlen - 1;
	return(FALSE);
    }
    return(TRUE);
}

/*
 * if end is before start then swap the two
 */
VOID
orderpos(POS *start, POS *end)
{
    if
    (
	((start->pos_Line == end->pos_Line) && (start->pos_Col > end->pos_Col)) ||
	(start->pos_Line > end->pos_Line)
    )
    {
	POS temp;
	temp = *start;
	*start = *end;
	*end = temp;
    }
}

/*
 * cursor is set to start of section, end to end (end is exclusive)
 * types,
 *  b		    - marked Block
 *  c		    - char under Cursor
 *  f		    - whole File
 *  l		    - Line
 *  p		    - Previous character
 *  n		    - Next character
 *  w		    - word
 *  el		    - to EoL
 *  sl		    - to SoL
 *  ef		    - to EoF
 *  sf		    - to SoF
 *  mX		    - to mark X
 */
BOOL
getsection(STRPTR type, POS *end)
{
    VW *vw = CurrVW;
    TX *tx = vw->vw_Tx;
    BOOL rc = TRUE;

    switch(tolower(*type++))
    {
	case 'b':
	    if(!vw->vw_BlockStatus)
	    {
		vw->vw_CursorPos = vw->vw_Block[0];
		*end = vw->vw_Block[1];
		vw->vw_BlockStatus = -1;
		setblockrefresh();
	    }
	    else
		rc = FALSE;
	    break;
	case 'c':
	    end->pos_Col = vw->vw_CursorPos.pos_Col + 1;
	    end->pos_Line = vw->vw_CursorPos.pos_Line;
	    if(end->pos_Col >= tx->tx_Lines[end->pos_Line].ln_Strlen)
	    {
		end->pos_Col = 0;
		if(++end->pos_Line >= tx->tx_NumLines)
		    rc = FALSE;
	    }
	    break;
	case 'f':
	    vw->vw_CursorPos.pos_Col = vw->vw_CursorPos.pos_Line = 0;
	    end->pos_Col = tx->tx_Lines[tx->tx_NumLines - 1].ln_Strlen - 1;
	    end->pos_Line = tx->tx_NumLines - 1;
	    break;
	case 'l':
	    vw->vw_CursorPos.pos_Col = 0;
	    end->pos_Col = 0;
	    end->pos_Line = vw->vw_CursorPos.pos_Line + 1;
	    if(end->pos_Line >= tx->tx_NumLines)
	    {
		end->pos_Line--;
		end->pos_Col = tx->tx_Lines[end->pos_Line].ln_Strlen - 1;
	    }
	    break;
	case 'p':
	    end->pos_Col = vw->vw_CursorPos.pos_Col--;
	    end->pos_Line = vw->vw_CursorPos.pos_Line;
	    if(vw->vw_CursorPos.pos_Col < 0)
	    {
		if(--vw->vw_CursorPos.pos_Line < 0)
		{
		    vw->vw_CursorPos.pos_Col++;
		    vw->vw_CursorPos.pos_Line++;
		    return(FALSE);
		}
		vw->vw_CursorPos.pos_Col = tx->tx_Lines[vw->vw_CursorPos.pos_Line].ln_Strlen - 1;
	    }
	    break;
	case 'n':
	    if(++vw->vw_CursorPos.pos_Col > tx->tx_Lines[vw->vw_CursorPos.pos_Line].ln_Strlen - 1)
	    {
		if(++vw->vw_CursorPos.pos_Line >= tx->tx_NumLines)
		{
		    vw->vw_CursorPos.pos_Col--;
		    vw->vw_CursorPos.pos_Line--;
		    return(FALSE);
		}
		vw->vw_CursorPos.pos_Col = 0;
	    }
	    end->pos_Col = vw->vw_CursorPos.pos_Col + 1;
	    end->pos_Line = vw->vw_CursorPos.pos_Line;
	    if(end->pos_Col > tx->tx_Lines[end->pos_Line].ln_Strlen - 1)
	    {
		if(++end->pos_Line >= tx->tx_NumLines)
		    return(FALSE);
		end->pos_Col = 0;
	    }
	    break;
	case 'w':
	    if(vw->vw_CursorPos.pos_Col < tx->tx_Lines[vw->vw_CursorPos.pos_Line].ln_Strlen)
	    {
		STRPTR line = tx->tx_Lines[vw->vw_CursorPos.pos_Line].ln_Line;
		STRPTR word = line + vw->vw_CursorPos.pos_Col;
		while(isalnum(*word--))
		    ;
		word += 2;
		vw->vw_CursorPos.pos_Col = word - line;
		end->pos_Col = stpalnum(word) - line;
		end->pos_Line = vw->vw_CursorPos.pos_Line;
		rc = TRUE;
	    }
	    else
		rc = FALSE;
	    break;
	case 'e':
	    switch(tolower(*type++))
	    {
		case 'f':
		    end->pos_Col = tx->tx_Lines[tx->tx_NumLines - 1].ln_Strlen - 1;
		    end->pos_Line = tx->tx_NumLines - 1;
		    break;
		case 'l':
		    end->pos_Col = tx->tx_Lines[vw->vw_CursorPos.pos_Line].ln_Strlen - 1;
		    end->pos_Line = vw->vw_CursorPos.pos_Line;
		    break;
		default:
		    rc = FALSE;
	    }
	    break;
	case 's':
	    switch(tolower(*type++))
	    {
		case 'f':
		    *end = vw->vw_CursorPos;
		    vw->vw_CursorPos.pos_Col = vw->vw_CursorPos.pos_Line = 0;
		    break;
		case 'l':
		    *end = vw->vw_CursorPos;
		    vw->vw_CursorPos.pos_Col = 0;
		    break;
		default:
		    rc = FALSE;
	    }
	    break;
	case 'm':
	    APTR dummy;
	    MARK *mk;
	    WORD mknum = strtol(type, &dummy, 0);
	    for(mk = (MARK *)tx->tx_Marks.mlh_Head; mk->mk_Node.mln_Succ; mk = (MARK *)mk->mk_Node.mln_Succ)
	    {
		if(mk->mk_Index == mknum)
		    break;
	    }
	    if(mk->mk_Node.mln_Succ)
	    {
		*end = mk->mk_Pos;
		if(end->pos_Line >= tx->tx_NumLines)
		    end->pos_Line = tx->tx_NumLines - 1;
		if(end->pos_Col >= tx->tx_Lines[end->pos_Line].ln_Strlen)
		    end->pos_Col = tx->tx_Lines[end->pos_Line].ln_Strlen - 1;
		orderpos(&vw->vw_CursorPos, end);
	    }
	    else
	    {
		settitlefmt("error: mark %ld is not set", mknum);
		rc = FALSE;
	    }
	    break;
	default:
	    settitle("error: invalid section specification");
	    rc = FALSE;
	    break;
    }
    return(rc);
}

/*
 * Returns the number of bytes needed to store a section, doesn't include
 * a zero terminator but does include all newline chars.
 */
LONG
sectionlength(POS *startPos, POS *endPos)
{
    LONG linenum = startPos->pos_Line;
    LINE *line = CurrVW->vw_Tx->tx_Lines + linenum;
    LONG length;

    if(startPos->pos_Line == endPos->pos_Line)
	length = endPos->pos_Col - startPos->pos_Col;
    else
    {
	length = line->ln_Strlen - startPos->pos_Col;
	linenum++;
	line++;
	while(linenum < endPos->pos_Line)
	{
	    length += line->ln_Strlen;
	    linenum++;
	    line++;
	}
	length += endPos->pos_Col;
    }
    return(length);
}

/*
 * Copies a section to a buffer.
 * end of copy does NOT have a zero appended to it.
 */
VOID
copysection(POS *startPos, POS *endPos, STRPTR buff)
{
    LONG linenum = startPos->pos_Line;
    LINE *line = CurrVW->vw_Tx->tx_Lines + linenum;
    LONG copylen;

    if(startPos->pos_Line == endPos->pos_Line)
    {
	copylen = endPos->pos_Col - startPos->pos_Col;
	memcpy(buff, line->ln_Line + startPos->pos_Col, copylen);
	buff[copylen] = 0;
    }
    else
    {
	copylen = line->ln_Strlen - startPos->pos_Col - 1;
	memcpy(buff, line->ln_Line + startPos->pos_Col, copylen);
	buff[copylen] = '\n';
	buff += copylen + 1;
	linenum++;
	line++;
	while(linenum < endPos->pos_Line)
	{
	    copylen = line->ln_Strlen - 1;
	    memcpy(buff, line->ln_Line, copylen);
	    buff[copylen] = '\n';
	    buff += copylen + 1;
	    linenum++;
	    line++;
	}
	memcpy(buff, line->ln_Line, endPos->pos_Col);
    }
}

/*
 * returns TRUE if the specified position is inside a block
 */
BOOL
posinblock(WORD col, LONG line)
{
    VW *vw = CurrVW;
    if
    (
	(vw->vw_BlockStatus) ||
	(line < vw->vw_Block[0].pos_Line) ||
	((line == vw->vw_Block[0].pos_Line) && (col < vw->vw_Block[0].pos_Col))
    )
	return(FALSE);
    if
    (
	(line < vw->vw_Block[1].pos_Line) ||
	((line == vw->vw_Block[1].pos_Line) && (col < vw->vw_Block[1].pos_Col))
    )
	return(TRUE);
    return(FALSE);
}

BOOL
cursinblock(VOID)
{
    VW *vw = CurrVW;
    return(posinblock(vw->vw_CursorPos.pos_Col, vw->vw_CursorPos.pos_Line));
}


BOOL
pageinblock(VOID)
{
    VW *vw = CurrVW;
    if
    (
	(vw->vw_BlockStatus) ||
	(vw->vw_Block[1].pos_Line < vw->vw_StartLine) ||
	(vw->vw_Block[0].pos_Line >= vw->vw_StartLine + vw->vw_MaxY)
    )
	return(FALSE);
    return(TRUE);
}

/*
 * these returns,
 *
 *	0   line not in block
 *	1   whole line in block
 *	2   start of line in block
 *	3   end of line in block
 *	4   middle of line in block
 *
 * note:
 *  this isn't very intelligent (but it works :-).
 */
WORD
lineinblock(LONG line)
{
    VW *vw = CurrVW;
    BOOL startin = FALSE;
    BOOL endin = FALSE;

    if
    (
	(vw->vw_BlockStatus) ||
	(line < vw->vw_Block[0].pos_Line) ||
	(line > vw->vw_Block[1].pos_Line)
    )
	return(0);

    if(line == vw->vw_Block[1].pos_Line)
	startin = TRUE;
    if(line == vw->vw_Block[0].pos_Line)
	endin = TRUE;

    if(startin)
    {
	if(endin)
	    return(4);
	return(2);
    }
    if(endin)
	return(3);
    return(1);
}

/*
 * makes sure that the marked block is valid
 */
VOID
orderblock(VOID)
{
    VW *vw = CurrVW;
    LINE *fline = vw->vw_Tx->tx_Lines;
    if(!vw->vw_BlockStatus)
    {
	if(vw->vw_Block[0].pos_Col >= fline[vw->vw_Block[0].pos_Line].ln_Strlen)
	    vw->vw_Block[0].pos_Col = fline[vw->vw_Block[0].pos_Line].ln_Strlen - 1;
	if(vw->vw_Block[1].pos_Col >= fline[vw->vw_Block[1].pos_Line].ln_Strlen)
	    vw->vw_Block[1].pos_Col = fline[vw->vw_Block[1].pos_Line].ln_Strlen - 1;

	if(vw->vw_Block[0].pos_Line > vw->vw_Block[1].pos_Line)
	{
	    POS temp = vw->vw_Block[1];
	    vw->vw_Block[1] = vw->vw_Block[0];
	    vw->vw_Block[0] = temp;
	}
	else if(vw->vw_Block[0].pos_Line == vw->vw_Block[1].pos_Line)
	{
	    if(vw->vw_Block[0].pos_Col > vw->vw_Block[1].pos_Col)
	    {
		POS temp = vw->vw_Block[1];
		vw->vw_Block[1] = vw->vw_Block[0];
		vw->vw_Block[0] = temp;
	    }
	}
    }
}

/*
 * Set up the refresh flags to refresh the block in the most efficient manner.
 * To make it better I'll have to add a refresh type which allows you to say
 * where to stop drawing.
 */
VOID
setblockrefresh(VOID)
{
    VW *vw = CurrVW;
    LONG endline = vw->vw_StartLine + vw->vw_MaxY;
    WORD refx;
    LONG refy;

    if((vw->vw_Block[0].pos_Line > endline) || (vw->vw_Block[1].pos_Line < vw->vw_StartLine))
	return;

    if(vw->vw_Block[0].pos_Line < vw->vw_StartLine)
    {
	refy= vw->vw_StartLine;
	refx = 0;
    }
    else
    {
	refy = vw->vw_Block[0].pos_Line;
	refx = vw->vw_Block[0].pos_Col;
    }
    if(vw->vw_Block[0].pos_Line == vw->vw_Block[1].pos_Line)
	setrefresh(RFF_LINEFROM, refx, refy);
    else
	setrefresh(RFF_ALLFROM, refx, refy);
}

/*
 * Returns the number of bytes from the start of the file that the cursor
 * is.
 */
LONG
offset(VOID)
{
    VW *vw = CurrVW;
    LINE *line = vw->vw_Tx->tx_Lines;
    LONG linenum = 0;
    LONG off = 0;

    while(linenum < vw->vw_CursorPos.pos_Line)
    {
	off += line->ln_Strlen;
	line++;
	linenum++;
    }
    off += vw->vw_CursorPos.pos_Col;
    return(off);
}

/*
 * Returns the number of bytes in the file
 */
LONG
size(VOID)
{
    TX *tx = CurrVW->vw_Tx;
    LINE *line = tx->tx_Lines;
    LONG linenum = 0;
    LONG size = 0;

    while(linenum < tx->tx_NumLines)
    {
	size += line->ln_Strlen;
	line++;
	linenum++;
    }
    return(size);
}

