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

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

Prototype   VALUE * cmd_move		(LONG, VALUE *);
Prototype   VOID    setautomark		(VOID);
Local	    LONG    moveautomark	(VOID);
Local	    LONG    movebookmark	(WORD);
Local	    LONG    setbookmark		(WORD);
Prototype   VOID    killmarklist	(TX *);
Local	    LONG    moveblockstart	(VOID);
Local	    LONG    moveblockend	(VOID);
Local	    LONG    movecolumn		(WORD);
Prototype   LONG    movedownpages	(LONG);
Prototype   LONG    moveuppages		(LONG);
Prototype   LONG    movedownlines	(LONG);
Prototype   LONG    moveuplines		(LONG);
Prototype   LONG    moveeof		(VOID);
Prototype   LONG    moveeol		(VOID);
Prototype   LONG    movesof		(VOID);
Prototype   LONG    movesol		(VOID);
Prototype   LONG    movelinenum		(LONG);
Prototype   LONG    moveleft		(WORD);
Prototype   LONG    moveright		(WORD);
Local	    LONG    movematchingbrac	(VOID);
Local	    LONG    movenextchar	(LONG);
Local	    LONG    moveprevchar	(LONG);
Prototype   LONG    movenexttab		(LONG);
Prototype   LONG    moveprevtab		(LONG);
Prototype   LONG    movenextword	(LONG);
Prototype   LONG    moveprevword	(LONG);
Prototype   BOOL    moveoffset		(LONG);
Prototype   VOID    positioncursorfine	(WORD, WORD);

Prototype   VALUE * cmd_find		(LONG, VALUE *);
Prototype   VALUE * cmd_replace		(LONG, VALUE *);
Prototype   BOOL    findnext		(VOID);
Prototype   BOOL    findprev		(VOID);
Prototype   BOOL    findcol0		(STRPTR);
Prototype   BOOL    replaceit		(VOID);
Local	    BOOL    mystrcmp		(STRPTR, STRPTR);
Local	    BOOL    mystricmp		(STRPTR, STRPTR);

VALUE *
cmd_move(LONG argc, VALUE *argv)
{
    STRPTR arg = ARG1.val_Value.String;
    LONG rc;
    BOOL argok = TRUE;

    if(!TPLATE1(VTF_STRING))
	return(&RES);

    switch(*arg++)
    {
	case 'a':
	    if(*arg == 'm')
		rc = moveautomark();
	    break;
	case 'b':
	    switch(*arg++)
	    {
		case 'm':
		    if(ARG2.val_Type == VTF_NUMBER)
			rc = movebookmark((WORD)ARG2.val_Value.Number);
		    else
			argok = FALSE;
		    break;
		case 's':
		    rc = moveblockstart();
		    break;
		case 'e':
		    rc = moveblockend();
		    break;
	    }
	    break;
	case 'c':
	    if(*arg++ == 'n')
	    {
		if(ARG2.val_Type == VTF_NUMBER)
		    rc = movecolumn((WORD)ARG2.val_Value.Number);
		else
		    argok = FALSE;
	    }
	    break;
	case 'd':
	    if(ARG2.val_Type == VTF_NUMBER)
	    {
		if(*arg == 'p')
		    rc = movedownpages(ARG2.val_Value.Number);
		else
		    rc = movedownlines(ARG2.val_Value.Number);
	    }
	    else
		argok = FALSE;
	    break;
	case 'e':
	    switch(*arg++)
	    {
		case 'f':
		    rc = moveeof();
		    break;
		case 'l':
		    rc = moveeol();
		    break;
	    }
	    break;
	case 'l':
	    if(ARG2.val_Type == VTF_NUMBER)
	    {
		switch(*arg++)
		{
		    case 't':
			rc = moveprevtab(ARG2.val_Value.Number);
			break;
		    case 'n':
			rc = movelinenum(ARG2.val_Value.Number);
		    default:
			rc = moveleft((WORD)ARG2.val_Value.Number);
			break;
		}
	    }
	    else
		argok = FALSE;
	    break;
	case 'm':
	    if(*arg == 'b')
		rc = movematchingbrac();
	    break;
	case 'n':
	    if(ARG2.val_Type == VTF_NUMBER)
	    {
		switch(*arg++)
		{
		    case 'c':
			rc = movenextchar(ARG2.val_Value.Number);
			break;
		    case 'w':
			rc = movenextword(ARG2.val_Value.Number);
			break;
		}
	    }
	    else
		argok = FALSE;
	    break;
	case 'o':
	    if(ARG2.val_Type == VTF_NUMBER)
		rc = moveoffset(ARG2.val_Value.Number);
	    else
		argok = FALSE;
	    break;
	case 'p':
	    if(ARG2.val_Type == VTF_NUMBER)
	    {
		switch(*arg++)
		{
		    case 'c':
			rc = moveprevchar(ARG2.val_Value.Number);
			break;
		    case 'w':
			rc = moveprevword(ARG2.val_Value.Number);
			break;
		}
	    }
	    else
		argok = FALSE;
	    break;
	case 'r':
	    if(ARG2.val_Type == VTF_NUMBER)
	    {
		switch(*arg++)
		{
		    case 't':
			rc = movenexttab(ARG2.val_Value.Number);
			break;
		    default:
			rc = moveright((WORD)ARG2.val_Value.Number);
			break;
		}
	    }
	    else
		argok = FALSE;
	    break;
	case 's':
	    switch(*arg++)
	    {
		case 'f':
		    rc = movesof();
		    break;
		case 'l':
		    rc = movesol();
		    break;
		case 'm':
		    if(ARG2.val_Type == VTF_NUMBER)
			rc = setbookmark((WORD)ARG2.val_Value.Number);
		    else
			argok = FALSE;
		    break;
	    }
	    break;
	case 'u':
	    if(ARG2.val_Type == VTF_NUMBER)
	    {
		if(*arg == 'p')
		    rc = moveuppages(ARG2.val_Value.Number);
		else
		    rc = moveuplines(ARG2.val_Value.Number);
		break;
	    }
	    else
		argok = FALSE;
	default:
	    settitle(BadArgMsg);
	    rc = FALSE;
    }
    if(!argok)
    {
	settitle("syntax error: argument 2 should be a number");
	rc = FALSE;
    }
    returnnum(rc);
}

VOID
setautomark(VOID)
{
    VW *vw = CurrVW;
    vw->vw_AutoMark = vw->vw_CursorPos;
}

Local LONG
moveautomark(VOID)
{
    VW *vw = CurrVW;
    POS temp = vw->vw_CursorPos;

    vw->vw_CursorPos = vw->vw_AutoMark;
    vw->vw_AutoMark = temp;
    resyncxy();
    return(TRUE);
}

Local LONG
movebookmark(WORD markNum)
{
    VW *vw = CurrVW;
    TX *tx = vw->vw_Tx;
    MARK *mark;

    for(mark = (MARK *)tx->tx_Marks.mlh_Head; mark->mk_Node.mln_Succ; mark = (MARK *)mark->mk_Node.mln_Succ)
    {
	if(mark->mk_Index == markNum)
	    break;
    }
    if(mark->mk_Node.mln_Succ)
    {
	setautomark();
	vw->vw_CursorPos = mark->mk_Pos;
	resyncxy();
	return(TRUE);
    }
    settitle("error: no mark of that number");
    return(FALSE);
}

Local LONG
setbookmark(WORD markNum)
{
    VW *vw = CurrVW;
    TX *tx = vw->vw_Tx;
    MARK *mark;

    for(mark = (MARK *)tx->tx_Marks.mlh_Head; mark->mk_Node.mln_Succ; mark = (MARK *)mark->mk_Node.mln_Succ)
    {
	if(mark->mk_Index == markNum)
	    break;
    }
    if(!mark->mk_Node.mln_Succ)
    {
	if(mark = AllocVec(sizeof(MARK), MEMF_CLEAR))
	{
	    AddMTail(&tx->tx_Marks, &mark->mk_Node);
	    mark->mk_Index = markNum;
	}
	else
	{
	    settitle(NoMemMsg);
	    return(FALSE);
	}
    }
    mark->mk_Pos = vw->vw_CursorPos;
    settitle("mark set");
    return(TRUE);
}

VOID
killmarklist(TX *tx)
{
    MARK *thismark, *nextmark;
    thismark = (MARK *)tx->tx_Marks.mlh_Head;
    while(nextmark = (MARK *)thismark->mk_Node.mln_Succ)
    {
	RemoveM(&thismark->mk_Node);
	FreeVec(thismark);
	thismark = nextmark;
    }
}

Local LONG
moveblockstart(VOID)
{
    VW *vw = CurrVW;
    if((!vw->vw_BlockStatus) || (vw->vw_BlockStatus == 1))
    {
	setautomark();
	vw->vw_CursorPos = vw->vw_Block[0];
	resyncxy();
	return(TRUE);
    }
    return(FALSE);
}

Local LONG
moveblockend(VOID)
{
    VW *vw = CurrVW;
    if((!vw->vw_BlockStatus) || (vw->vw_BlockStatus == 2))
    {
	setautomark();
	vw->vw_CursorPos = vw->vw_Block[1];
	resyncxy();
	return(TRUE);
    }
    return(FALSE);
}

Local LONG
movecolumn(WORD col)
{
    CurrVW->vw_CursorPos.pos_Col = col - 1;
    resyncx();
    return(TRUE);
}

LONG
movedownpages(LONG pages)
{
    VW *vw = CurrVW;
    LONG newline, rc;

    newline = vw->vw_CursorPos.pos_Line + (pages * vw->vw_MaxY);
    if(newline >= vw->vw_Tx->tx_NumLines)
    {
	newline = vw->vw_Tx->tx_NumLines - 1;
	rc = FALSE;
    }
    else
    {
	vw->vw_StartLine += (pages * vw->vw_MaxY);
	vw->vw_RefreshType |= RFF_ALL;
	rc = TRUE;
    }
    vw->vw_CursorPos.pos_Line = newline;
    resyncy();
    return(rc);
}

LONG
moveuppages(LONG pages)
{
    VW *vw = CurrVW;
    LONG newline, rc;

    newline = vw->vw_CursorPos.pos_Line - (pages * vw->vw_MaxY);
    if(newline < 0)
    {
	newline = 0;
	rc = FALSE;
    }
    else
    {
	vw->vw_StartLine -= (pages * vw->vw_MaxY);
	if(vw->vw_StartLine < 0)
	    vw->vw_StartLine = 0;
	vw->vw_RefreshType |= RFF_ALL;
	rc = TRUE;
    }
    vw->vw_CursorPos.pos_Line = newline;
    resyncy();
    return(rc);
}

LONG
movedownlines(LONG lines)
{
    VW *vw = CurrVW;
    LONG newline, rc;

    newline = vw->vw_CursorPos.pos_Line + lines;
    if(newline >= vw->vw_Tx->tx_NumLines)
    {
	newline = vw->vw_Tx->tx_NumLines - 1;
	rc = FALSE;
    }
    else
	rc = TRUE;
    vw->vw_CursorPos.pos_Line = newline;
    resyncy();
    return(rc);
}

LONG
moveuplines(LONG lines)
{
    VW *vw = CurrVW;
    LONG newline, rc;

    newline = vw->vw_CursorPos.pos_Line - lines;
    if(newline < 0)
    {
	newline = 0;
	rc = FALSE;
    }
    else
	rc = TRUE;
    vw->vw_CursorPos.pos_Line = newline;
    resyncy();
    return(rc);
}

LONG
moveeof(VOID)
{
    VW *vw = CurrVW;

    setautomark();
    vw->vw_CursorPos.pos_Line = vw->vw_Tx->tx_NumLines - 1;
    resyncy();
    return(TRUE);
}

LONG
moveeol(VOID)
{
    VW *vw = CurrVW;

    vw->vw_CursorPos.pos_Col = vw->vw_Tx->tx_Lines[vw->vw_CursorPos.pos_Line].ln_Strlen - 1;
    resyncx();
    return(TRUE);
}

LONG
movesof(VOID)
{
    setautomark();
    CurrVW->vw_CursorPos.pos_Line = 0;
    resyncy();
    return(TRUE);
}

LONG
movesol(VOID)
{
    CurrVW->vw_CursorPos.pos_Col = 0;
    resyncx();
    return(TRUE);
}

LONG
movelinenum(LONG line)
{
    VW *vw = CurrVW;
    LONG rc;

    if(--line < 0)
    {
	settitle("error: lines start at 1");
	rc = FALSE;
    }
    else
    {
	if(line >= vw->vw_Tx->tx_NumLines)
	{
	    settitlefmt("error: line %ld doesn't exist!", line + 1);
	    rc = FALSE;
	}
	else
	{
	    setautomark();
	    vw->vw_CursorPos.pos_Line = line;
	    resyncy();
	    rc = TRUE;
	}
    }
    return(rc);
}

LONG
moveleft(WORD distance)
{
    VW *vw = CurrVW;
    LONG rc;

    vw->vw_CursorPos.pos_Col -= distance;
    if(vw->vw_CursorPos.pos_Col < 0)
    {
	vw->vw_CursorPos.pos_Col = 0;
	rc = FALSE;
    }
    else
	rc = TRUE;
    resyncx();
    return(rc);
}

LONG
moveright(WORD distance)
{
    VW *vw = CurrVW;

    vw->vw_CursorPos.pos_Col += distance;
    resyncx();
    return(TRUE);
}

Local LONG
movematchingbrac(VOID)
{
#define NUM_BRAC_TYPES 10
    static UBYTE bracs[] =
    {
	'{', '}',
	'(', ')',
	'[', ']',
	'`', '\'',
	'<', '>'
    };

    VW *vw = CurrVW;
    TX *tx = vw->vw_Tx;
    LINE *line = tx->tx_Lines + vw->vw_CursorPos.pos_Line;
    if(vw->vw_CursorPos.pos_Col < line->ln_Strlen)
    {
	UBYTE startc = line->ln_Line[vw->vw_CursorPos.pos_Col];
	WORD i;
	for(i = 0; i < NUM_BRAC_TYPES; i++)
	{
	    if(startc == bracs[i])
		break;
	}
	if(i < NUM_BRAC_TYPES)
	{
	    WORD x = vw->vw_CursorPos.pos_Col;
	    LONG y = vw->vw_CursorPos.pos_Line;
	    WORD braccount = 1;
	    BOOL found = FALSE;
	    if(i & 1)
	    {
		/* search backwards
		 */
		UBYTE endc = bracs[i - 1];
		while(!found)
		{
		    UBYTE c;
		    if(--x < 0)
		    {
			if(--y < 0)
			{
			    settitle("error: can't find matching bracket");
			    return(FALSE);
			}
			line--;
			x = line->ln_Strlen - 1;
		    }
		    c = line->ln_Line[x];
		    if(c == startc)
			braccount++;
		    else if(c == endc)
		    {
			if(!(--braccount))
			    found = TRUE;
		    }
		}
	    }
	    else
	    {
		/* search forwards
		 */
		UBYTE endc = bracs[i + 1];
		while(!found)
		{
		    UBYTE c;
		    if(++x >= line->ln_Strlen)
		    {
			if(++y >= tx->tx_NumLines)
			{
			    settitle("error: can't find matching bracket");
			    return(FALSE);
			}
			line++;
			x = 0;
		    }
		    c = line->ln_Line[x];
		    if(c == startc)
			braccount++;
		    else if(c == endc)
		    {
			if(!(--braccount))
			    found = TRUE;
		    }
		}
	    }
	    vw->vw_CursorPos.pos_Col = x;
	    vw->vw_CursorPos.pos_Line = y;
	    resyncxy();
	    return(TRUE);
	}
    }
    settitle("error: cursor must be on a bracket");
    return(FALSE);
}

Local LONG
movenextchar(LONG chars)
{
    VW *vw = CurrVW;
    TX *tx = vw->vw_Tx;
    LONG rc = FALSE;

    if((vw->vw_CursorPos.pos_Col += chars) >= tx->tx_Lines[vw->vw_CursorPos.pos_Line].ln_Strlen)
    {
	if(++vw->vw_CursorPos.pos_Line >= tx->tx_NumLines)
	{
	    vw->vw_CursorPos.pos_Col -= chars;
	    vw->vw_CursorPos.pos_Line--;
	}
	else
	{
	    vw->vw_CursorPos.pos_Col = 0;
	    resyncxy();
	    rc = TRUE;
	}
    }
    else
    {
	resyncx();
	rc = TRUE;
    }
    return(rc);
}

Local LONG
moveprevchar(LONG chars)
{
    VW *vw = CurrVW;
    LONG rc = FALSE;

    if((vw->vw_CursorPos.pos_Col -= chars) < 0)
    {
	if(--vw->vw_CursorPos.pos_Line < 0)
	{
	    vw->vw_CursorPos.pos_Line = 0;
	    vw->vw_CursorPos.pos_Col += chars;
	}
	else
	{
	    vw->vw_CursorPos.pos_Col = vw->vw_Tx->tx_Lines[vw->vw_CursorPos.pos_Line].ln_Strlen - 1;
	    resyncxy();
	    rc = TRUE;
	}
    }
    else
    {
	resyncx();
	rc = TRUE;
    }
    return(rc);
}

LONG
movenexttab(LONG tabs)
{
    VW *vw = CurrVW;

    vw->vw_CursorPos.pos_Col = ((vw->vw_CursorPos.pos_Col / vw->vw_Prefs.prf_TabSize) + 1) * vw->vw_Prefs.prf_TabSize;
    resyncx();
    return(TRUE);
}

LONG
moveprevtab(LONG tabs)
{
    VW *vw = CurrVW;
    LONG rc;

    if(vw->vw_CursorPos.pos_Col)
    {
	vw->vw_CursorPos.pos_Col = (((vw->vw_CursorPos.pos_Col - 1) / vw->vw_Prefs.prf_TabSize)) * vw->vw_Prefs.prf_TabSize;
	if(vw->vw_CursorPos.pos_Col < 0)
	{
	    vw->vw_CursorPos.pos_Col = 0;
	    rc = FALSE;
	}
	else
	    rc = TRUE;
	resyncx();
	return(rc);
    }
    return(TRUE);
}

LONG
movenextword(LONG words)
{
    VW *vw = CurrVW;
    TX *tx = vw->vw_Tx;
    LONG rc = TRUE;
    POS oldcurs = vw->vw_CursorPos;

    while(words--)
    {
	LINE *line = tx->tx_Lines + vw->vw_CursorPos.pos_Line;
	STRPTR word;
	if(vw->vw_CursorPos.pos_Col >= line->ln_Strlen)
	    vw->vw_CursorPos.pos_Col = line->ln_Strlen - 1;
	word = line->ln_Line + vw->vw_CursorPos.pos_Col;
	while(isalnum(*word))
	    word++;
	while(!isalnum(*word))
	{
	    if(*word++ == 0)
	    {
		if(++vw->vw_CursorPos.pos_Line >= tx->tx_NumLines)
		{
		    vw->vw_CursorPos.pos_Line--;
		    word--;
		    rc = FALSE;
		    goto end;
		}
		line++;
		word = line->ln_Line;
	    }
	}
	vw->vw_CursorPos.pos_Col = word - line->ln_Line;
    }
end:
    if(!rc)
	vw->vw_CursorPos = oldcurs;
    else
	resyncxy();
    return(rc);
}

LONG
moveprevword(LONG words)
{
    VW *vw = CurrVW;
    TX *tx = vw->vw_Tx;
    LONG rc = TRUE;
    POS oldcurs = vw->vw_CursorPos;

    while(words--)
    {
	LINE *line = tx->tx_Lines + vw->vw_CursorPos.pos_Line;
	STRPTR word;
	if(vw->vw_CursorPos.pos_Col >= line->ln_Strlen)
	    vw->vw_CursorPos.pos_Col = line->ln_Strlen - 1;
	word = line->ln_Line + vw->vw_CursorPos.pos_Col;
	while(isalnum(*word))
	{
	    if(word-- == line->ln_Line)
	    {
		if(--vw->vw_CursorPos.pos_Line < 0)
		{
		    vw->vw_CursorPos.pos_Line++;
		    word++;
		    rc = FALSE;
		    goto end;
		}
		line--;
		word = line->ln_Line + line->ln_Strlen - 1;
	    }
	}
	while(!isalnum(*word))
	{
	    if(word-- == line->ln_Line)
	    {
		if(--vw->vw_CursorPos.pos_Line < 0)
		{
		    vw->vw_CursorPos.pos_Line++;
		    word++;
		    rc = FALSE;
		    goto end;
		}
		line--;
		word = line->ln_Line + line->ln_Strlen - 1;
	    }
	}
	while(isalnum(*word))
	{
	    if(word-- == line->ln_Line)
		break;
	}
	vw->vw_CursorPos.pos_Col = word - line->ln_Line + 1;
    }
end:
    if(!rc)
	vw->vw_CursorPos = oldcurs;
    else
	resyncxy();
    return(rc);
}

BOOL
moveoffset(LONG offset)
{
    VW *vw = CurrVW;
    TX *tx = vw->vw_Tx;
    LINE *line = tx->tx_Lines;
    LONG linenum = 0;

    while(linenum < tx->tx_NumLines)
    {
	offset -= line->ln_Strlen;
	if(offset < 0)
	{
	    offset += line->ln_Strlen;
	    setautomark();
	    vw->vw_CursorPos.pos_Col = offset;
	    vw->vw_CursorPos.pos_Line = linenum;
	    resyncxy();
	    return(TRUE);
	}
	linenum++;
	line++;
    }
    return(FALSE);
}

VOID
positioncursorfine(WORD x, WORD y)
{
    VW *vw = CurrVW;

    if(x > (WORD)vw->vw_XEndPix)
	vw->vw_CursorPos.pos_Col = vw->vw_StartCol + vw->vw_MaxX + 1;
    else if(x < (WORD)vw->vw_XStartPix)
	vw->vw_CursorPos.pos_Col = vw->vw_StartCol - 1;
    else
	vw->vw_CursorPos.pos_Col = vw->vw_StartCol + ((x - (WORD)vw->vw_XStartPix) / (WORD)vw->vw_FontX);

    if(y > (WORD)vw->vw_YEndPix)
	vw->vw_CursorPos.pos_Line = vw->vw_StartLine + vw->vw_MaxY;
    else if(y < (WORD)vw->vw_YStartPix)
	vw->vw_CursorPos.pos_Line = vw->vw_StartLine - 1;
    else
	vw->vw_CursorPos.pos_Line = vw->vw_StartLine + ((y - (WORD)vw->vw_YStartPix) / (WORD)vw->vw_FontY);

    if(vw->vw_CursorPos.pos_Col < 0)
	vw->vw_CursorPos.pos_Col = 0;
    if(vw->vw_CursorPos.pos_Line < 0)
	vw->vw_CursorPos.pos_Line = 0;
    if(vw->vw_CursorPos.pos_Line >= vw->vw_Tx->tx_NumLines)
	vw->vw_CursorPos.pos_Line = vw->vw_Tx->tx_NumLines - 1;
}

/*
 * Search and replace routines.
 */
Local UBYTE SearchBuff[100];
Local UBYTE ReplaceBuff[100];
Local BOOL  WildSearch;
Local BOOL  CaseSearch;

/*
 * (find `s' searchstring)
 * (find `w' bool)
 * (find `c' bool)
 * (find `n')
 * (find `p')
 */
VALUE *
cmd_find(LONG argc, VALUE *argv)
{
    if(TPLATE2(VTF_STRING, VTF_ANY))
    {
	switch(*(ARG1.val_Value.String))
	{
	    case 's':
		if(ARG2.val_Type == VTF_STRING)
		{
		    strncpy(SearchBuff, ARG2.val_Value.String, 99);
		    setnumres(TRUE);
		}
		else
		    settitle("syntax error: argument 2 should be a string");
		break;
	    case 'w':
		WildSearch = ARG2.val_Value.Number;
		setnumres(TRUE);
		break;
	    case 'c':
		CaseSearch = ARG2.val_Value.Number;
		setnumres(TRUE);
		break;
	    case 'n':
		setnumres(findnext());
		resyncxy();
		break;
	    case 'p':
		setnumres(findprev());
		resyncxy();
		break;
	    default:
		settitlefmt("error: incorrect find argument %s", (LONG)ARG1.val_Value.String);
		break;
	}
    }
    return(&RES);
}

/*
 * (replace `s' repstring)
 * (replace `r')
 */
VALUE *
cmd_replace(LONG argc, VALUE *argv)
{
    if(TPLATE2(VTF_STRING, VTF_ANY))
    {
	switch(*(ARG1.val_Value.String))
	{
	    case 's':
		if(ARG2.val_Type == VTF_STRING)
		{
		    strncpy(ReplaceBuff, ARG2.val_Value.String, 99);
		    setnumres(TRUE);
		}
		else
		    settitle("syntax error: argument 2 should be a string");
		break;
	    case 'r':
		setnumres(replaceit());
		resyncxy();
		break;
	}
    }
    return(&RES);
}

BOOL
findnext(VOID)
{
    UBYTE parsed[202];
    STRPTR find;
    VW *vw = CurrVW;
    LONG linenum = vw->vw_CursorPos.pos_Line;
    LINE *line = vw->vw_Tx->tx_Lines + linenum;
    LONG i = vw->vw_CursorPos.pos_Col + 1;
    LONG (*cmpfunc)(STRPTR, STRPTR);

    if(WildSearch)
    {
	LONG prc;
	if(CaseSearch)
	{
	    prc = ParsePattern(SearchBuff, parsed, 202);
	    cmpfunc = MatchPattern;
	}
	else
	{
	    prc = ParsePatternNoCase(SearchBuff, parsed, 202);
	    cmpfunc = MatchPatternNoCase;
	}
	if(prc < 0)
	{
	    settitle("error: can't parse pattern for wildcard search");
	    return(FALSE);
	}
	find = parsed;
    }
    else
    {
	find = SearchBuff;
	cmpfunc = CaseSearch ? mystrcmp : mystricmp;
    }
    while(linenum < vw->vw_Tx->tx_NumLines)
    {
	while(i < line->ln_Strlen)
	{
	    if(cmpfunc(find, line->ln_Line + i))
	    {
		setautomark();
		vw->vw_CursorPos.pos_Col = i;
		vw->vw_CursorPos.pos_Line = linenum;
		return(TRUE);
	    }
	    i++;
	}
	line++;
	linenum++;
	i = 0;
    }
    settitlefmt("can't find %s", (LONG)SearchBuff);
    return(FALSE);
}

BOOL
findprev(VOID)
{
    UBYTE parsed[202];
    STRPTR find;
    VW *vw = CurrVW;
    LONG linenum = vw->vw_CursorPos.pos_Line;
    LINE *line = vw->vw_Tx->tx_Lines + linenum;
    LONG i = vw->vw_CursorPos.pos_Col - 1;
    LONG (*cmpfunc)(STRPTR, STRPTR);

    if(WildSearch)
    {
	LONG prc;
	if(CaseSearch)
	{
	    prc = ParsePattern(SearchBuff, parsed, 202);
	    cmpfunc = MatchPattern;
	}
	else
	{
	    prc = ParsePatternNoCase(SearchBuff, parsed, 202);
	    cmpfunc = MatchPatternNoCase;
	}
	if(prc < 0)
	{
	    settitle("error: can't parse pattern for wildcard search");
	    return(FALSE);
	}
	find = parsed;
    }
    else
    {
	find = SearchBuff;
	cmpfunc = CaseSearch ? mystrcmp : mystricmp;
    }
    while(linenum >= 0)
    {
	while(i >= 0)
	{
	    if(cmpfunc(find, line->ln_Line + i))
	    {
		setautomark();
		vw->vw_CursorPos.pos_Col = i;
		vw->vw_CursorPos.pos_Line = linenum;
		return(TRUE);
	    }
	    i--;
	}
	line--;
	linenum--;
	i = line->ln_Strlen - 2;
    }
    settitlefmt("can't find %s", (LONG)SearchBuff);
    return(FALSE);
}

BOOL
findcol0(STRPTR str)
{
    VW *vw = CurrVW;
    TX *tx = vw->vw_Tx;
    LINE *line = tx->tx_Lines;
    LONG linenum = 0;

    while(linenum < tx->tx_NumLines)
    {
	if(mystrcmp(str, line->ln_Line))
	{
	    setautomark();
	    vw->vw_CursorPos.pos_Col = 0;
	    vw->vw_CursorPos.pos_Line = linenum;
	    vw->vw_StartLine = linenum;
	    vw->vw_RefreshType |= RFF_ALL;
	    return(TRUE);
	}
	linenum++;
	line++;
    }
    return(FALSE);
}

BOOL
replaceit(VOID)
{
    VW *vw = CurrVW;
    LINE *line = vw->vw_Tx->tx_Lines + vw->vw_CursorPos.pos_Line;
    WORD x = vw->vw_CursorPos.pos_Col;
    BOOL rc;

    if((x < line->ln_Strlen) && (mystricmp(SearchBuff, line->ln_Line + x)))
    {
	WORD serlen = strlen(SearchBuff);
	WORD replen = strlen(ReplaceBuff);

	setrefresh(RFF_LINEFROM, vw->vw_CursorPos.pos_Col, vw->vw_CursorPos.pos_Line);
	vw->vw_Tx->tx_Changes++;
	if(serlen < replen)
	{
	    memcpy(line->ln_Line + x, ReplaceBuff, serlen);
	    vw->vw_CursorPos.pos_Col += serlen;
	    rc = insertstrn(ReplaceBuff + serlen, replen - serlen);
	}
	else if(serlen > replen)
	{
	    memcpy(line->ln_Line + x, ReplaceBuff, replen);
	    vw->vw_CursorPos.pos_Col += replen;
	    rc = deletechars(serlen - replen);
	}
	else
	{
	    memcpy(line->ln_Line + x, ReplaceBuff, replen);
	    vw->vw_CursorPos.pos_Col += replen;
	    rc = TRUE;
	}
    }
    else
    {
	settitle("string under cursor doesn't match string in search buffer");
	rc = FALSE;
    }
    return(rc);
}

/*
 * These functions return TRUE if the strings match, if str1 ends before
 * str2 they are still considered to be matching.
 */
Local BOOL
mystrcmp(STRPTR str1, STRPTR str2)
{
    while(*str1 && (*str1 == *str2++))
	str1++;
    if(!(*str1))
	return(TRUE);
    return(FALSE);
}

Local BOOL
mystricmp(STRPTR str1, STRPTR str2)
{
    while(*str1 && ((toupper(*str1) == toupper(*str2++))))
	str1++;
    if(!(*str1))
	return(TRUE);
    return(FALSE);
}

